diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7337f7d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +*.aspl linguist-language=ASPL text=auto eol=lf +*.v linguist-language=V text=auto eol=lf +**/v.mod linguist-language=V text=auto eol=lf \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..5e7a35a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,35 @@ +--- +name: Bug report +about: Create a report to help us improve +title: Bug report +labels: bug, unreviewed +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Do '...' +2. Click on '...' +3. See '...' + +Or a minimal reproducible code example: +```aspl +// code here +``` + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Software (please complete the following information):** + - OS: [e.g. Windows] + - Version of ASPL [obtainable by running `aspl version`] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..8d408a6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,7 @@ +contact_links: + - name: Discussion Q&A + url: https://github.com/aspl-lang/aspl/discussions/categories/q-a + about: You can ask and answer questions here + - name: Discord + url: https://discord.gg/UUNzAFrKU2 + about: You can ask and answer questions here diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..144c9c7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: Feature request +labels: enhancement, unreviewed +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. + +**Describe the solution you would like** +A clear and concise description of what you want to be implemented. + +**Describe alternatives you have considered** +A clear and concise description of any alternative solutions or features you have considered. + +**Additional context** +Add any other context or screenshots about your feature request here. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 0000000..ef626a5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,14 @@ +--- +name: Question +about: Ask a question about ASPL +title: Question +labels: help wanted, question, unreviewed +assignees: '' + +--- + +**Describe your question** +I don't understand... + +**What kind answer do you expect?** +I expect something like... \ No newline at end of file diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml new file mode 100644 index 0000000..08d2ce7 --- /dev/null +++ b/.github/workflows/ci_cd.yml @@ -0,0 +1,303 @@ +name: CI/CD + +on: + - push + - workflow_dispatch + +jobs: + build-runtime-linux: + runs-on: ubuntu-latest + container: + image: debian:buster # use an older Debian version to support older glibc versions + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install ASPL installer dependencies + run: | + apt-get update + apt-get install --quiet -y git jq curl unzip + - name: Install ASPL + run: | + cd $GITHUB_WORKSPACE + ./install_ci.sh + - name: Install runtime dependencies + run: | + apt-get update + apt-get install --quiet -y build-essential + apt-get install --quiet -y libglfw3-dev libxi-dev libxcursor-dev # for the graphics module + apt-get install --quiet -y gcc-arm-linux-gnueabi # for ARM32 cross-compilation + apt-get install --quiet -y lib32z1 # for ARM32 cross-compilation + - name: Build ASPL runtime templates + run: | + cd $GITHUB_WORKSPACE + aspl -os linux -cc gcc build-minimal-template + mv -f Template templates/linux/x86_64/minimal + aspl -os linux -cc gcc build-full-template + mv -f Template templates/linux/x86_64/full + # aspl -os linux -arch arm32 -cc arm-linux-gnueabi-gcc build-minimal-template + # mv -f Template templates/linux/arm32/minimal + # aspl -os linux -arch arm32 -cc arm-linux-gnueabi-gcc build-full-template + # mv -f Template templates/linux/arm32/full + - name: Upload template artifact (Linux x86_64 minimal) + uses: actions/upload-artifact@v3 + with: + name: template_linux_x86_64_minimal + path: templates/linux/x86_64/minimal/Template + - name: Upload template artifact (Linux x86_64 full) + uses: actions/upload-artifact@v3 + with: + name: template_linux_x86_64_full + path: templates/linux/x86_64/full/Template + build-runtime-windows: + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install ASPL + run: | + cd $GITHUB_WORKSPACE + .\install.bat + shell: cmd + - name: Build ASPL runtime templates + run: | + cd $GITHUB_WORKSPACE + aspl -os windows -cc gcc build-minimal-template + mv -f Template.exe templates/windows/x86_64/minimal + aspl -os windows -cc gcc build-full-template + mv -f Template.exe templates/windows/x86_64/full/cli + shell: cmd + - name: Upload template artifact (Windows x86_64 minimal) + uses: actions/upload-artifact@v3 + with: + name: template_windows_x86_64_minimal + path: templates/windows/x86_64/minimal/Template.exe + - name: Upload template artifact (Windows x86_64 full) + uses: actions/upload-artifact@v3 + with: + name: template_windows_x86_64_full + path: templates/windows/x86_64/full/cli/Template.exe + build-runtime-macos: + runs-on: macos-13 + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install ASPL + run: | + cd $GITHUB_WORKSPACE + ./install_ci.sh + - name: Install runtime dependencies + run: | + brew install glfw # for the graphics module + - name: Build ASPL runtime templates + run: | + cd $GITHUB_WORKSPACE + aspl -os macos -cc gcc build-minimal-template + mv -f Template templates/macos/x86_64/minimal + aspl -os macos -cc gcc build-full-template + mv -f Template templates/macos/x86_64/full + - name: Upload template artifact (macOS x86_64 minimal) + uses: actions/upload-artifact@v3 + with: + name: template_macos_x86_64_minimal + path: templates/macos/x86_64/minimal/Template + - name: Upload template artifact (macOS x86_64 full) + uses: actions/upload-artifact@v3 + with: + name: template_macos_x86_64_full + path: templates/macos/x86_64/full/Template + build-compiler: + runs-on: ubuntu-latest + container: + image: debian:buster # use an older Debian version to support older glibc versions + needs: [build-runtime-linux, build-runtime-windows, build-runtime-macos] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install ASPL installer dependencies + run: | + apt-get update + apt-get install --quiet -y git jq curl unzip xz-utils build-essential + - name: Install ASPL + run: | + cd $GITHUB_WORKSPACE + ./install_ci.sh + # - name: Delete template (macOS x86_64) + # run: | + # rm templates/macos/x86_64/Template + - name: Download template artifact (macOS x86_64 full) + uses: actions/download-artifact@v3 + with: + name: template_macos_x86_64_full + path: templates/macos/x86_64/full + - name: Setup Zig + uses: goto-bus-stop/setup-zig@v2 + - name: Build ASPL compiler + run: | + cd $GITHUB_WORKSPACE + cd cli + ../aspl -prod -os linux -backend c -heapBased -useDynamicCTemplate -cc gcc -showcc compile . # use GCC as it tends to optimize better + mv cli ../aspl_linux_x86_64 + # ../aspl -prod -os linux -arch arm32 -backend c -heapBased -useDynamicCTemplate compile . + # mv cli ../aspl_linux_arm32 + ../aspl -prod -os windows -backend c -heapBased -useDynamicCTemplate -showcc compile . + mv cli.exe ../aspl_windows_x86_64.exe + ../aspl -prod -os macos -backend ail compile . + mv cli ../aspl_macos_x86_64 + # TODO: Build for more platforms + - name: Upload ASPL compiler artifact (Linux x86_64) + uses: actions/upload-artifact@v3 + with: + name: aspl_linux_x86_64 + path: aspl_linux_x86_64 + - name: Upload ASPL compiler artifact (Windows x86_64) + uses: actions/upload-artifact@v3 + with: + name: aspl_windows_x86_64 + path: aspl_windows_x86_64.exe + - name: Upload ASPL compiler artifact (macOS x86_64) + uses: actions/upload-artifact@v3 + with: + name: aspl_macos_x86_64 + path: aspl_macos_x86_64 + publish: + runs-on: ubuntu-latest + needs: [build-runtime-linux, build-runtime-windows, build-runtime-macos, build-compiler] + steps: + - name: Delete template (Linux x86_64 minimal) + run: | + rm -rf templates/linux/x86_64/minimal/Template + - name: Download template artifact (Linux x86_64 minimal) + uses: actions/download-artifact@v3 + with: + name: template_linux_x86_64_minimal + path: templates/linux/x86_64/minimal + - name: Delete template artifact (Linux x86_64 minimal) + uses: geekyeggo/delete-artifact@v2 + with: + name: template_linux_x86_64_minimal + - name: Delete template (Linux x86_64 full) + run: | + rm -rf templates/linux/x86_64/full/Template + - name: Download template artifact (Linux x86_64 full) + uses: actions/download-artifact@v3 + with: + name: template_linux_x86_64_full + path: templates/linux/x86_64/full + - name: Delete template artifact (Linux x86_64 full) + uses: geekyeggo/delete-artifact@v2 + with: + name: template_linux_x86_64_full + - name: Delete template (Windows x86_64 minimal) + run: | + rm -rf templates/windows/x86_64/minimal/Template.exe + - name: Download template artifact (Windows x86_64 minimal) + uses: actions/download-artifact@v3 + with: + name: template_windows_x86_64_minimal + path: templates/windows/x86_64/minimal + - name: Delete template artifact (Windows x86_64 minimal) + uses: geekyeggo/delete-artifact@v2 + with: + name: template_windows_x86_64_minimal + - name: Delete template (Windows x86_64 full) + run: | + rm -rf templates/windows/x86_64/full/cli/Template.exe + - name: Download template artifact (Windows x86_64 full) + uses: actions/download-artifact@v3 + with: + name: template_windows_x86_64_full + path: templates/windows/x86_64/full/cli + - name: Delete template artifact (Windows x86_64 full) + uses: geekyeggo/delete-artifact@v2 + with: + name: template_windows_x86_64_full + - name: Delete template (macOS x86_64 minimal) + run: | + rm -rf templates/macos/x86_64/minimal/Template + - name: Download template artifact (macOS x86_64 minimal) + uses: actions/download-artifact@v3 + with: + name: template_macos_x86_64_minimal + path: templates/macos/x86_64/minimal + - name: Delete template artifact (macOS x86_64 minimal) + uses: geekyeggo/delete-artifact@v2 + with: + name: template_macos_x86_64_minimal + - name: Delete template (macOS x86_64 full) + run: | + rm -rf templates/macos/x86_64/full/Template + - name: Download template artifact (macOS x86_64 full) + uses: actions/download-artifact@v3 + with: + name: template_macos_x86_64_full + path: templates/macos/x86_64/full + - name: Delete template artifact (macOS x86_64 full) + uses: geekyeggo/delete-artifact@v2 + with: + name: template_macos_x86_64_full + - name: Download ASPL compiler artifact (Linux x86_64) + uses: actions/download-artifact@v3 + with: + name: aspl_linux_x86_64 + path: ./ + - name: Delete ASPL compiler artifact (Linux x86_64) + uses: geekyeggo/delete-artifact@v2 + with: + name: aspl_linux_x86_64 + - name: Download ASPL compiler artifact (Windows x86_64) + uses: actions/download-artifact@v3 + with: + name: aspl_windows_x86_64 + path: ./ + - name: Delete ASPL compiler artifact (Windows x86_64) + uses: geekyeggo/delete-artifact@v2 + with: + name: aspl_windows_x86_64 + - name: Download ASPL compiler artifact (macOS x86_64) + uses: actions/download-artifact@v3 + with: + name: aspl_macos_x86_64 + path: ./ + - name: Delete ASPL compiler artifact (macOS x86_64) + uses: geekyeggo/delete-artifact@v2 + with: + name: aspl_macos_x86_64 + - name: Zip templates + run: | + cd $GITHUB_WORKSPACE + zip -r templates.zip templates -x '.gitignore' -x '*.md' + - name: Release + uses: softprops/action-gh-release@v1 + with: + repository: aspl-lang/cd + tag_name: SHA-${{ github.sha }} + token: ${{ secrets.CDKEY }} + files: | + ${{ github.workspace }}/aspl_linux_x86_64 + ${{ github.workspace }}/aspl_windows_x86_64.exe + ${{ github.workspace }}/aspl_macos_x86_64 + ${{ github.workspace }}/templates.zip + # ${{ github.workspace }}/aspl_linux_arm32 + - name: Update metadata file + run: | + curl \ + -X PUT\ + -H "Authorization: token ${{ secrets.CDKEY }}"\ + -d '{"message": "Update latest.txt", "content": "'$(echo ${{ github.sha }} | base64)'", "sha": "'$(curl -s -H "Authorization: token ${{ secrets.CDKEY }}" https://api.github.com/repos/aspl-lang/cd/contents/latest.txt | jq -r .sha)'", "committer": {"name": "github-actions[bot]", "email": "github-actions[bot]@users.noreply.github.com"}}'\ + https://api.github.com/repos/aspl-lang/cd/contents/latest.txt + ci: + runs-on: ubuntu-latest + needs: [publish] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install ASPL + run: | + $GITHUB_WORKSPACE/install_ci.sh + - name: Run tests + run: | + cd $GITHUB_WORKSPACE + echo "Testing the C backend..." + ./aspl -backend c -cc gcc -showcc test-all || exit $? + echo "Testing the AIL backend..." + ./aspl -backend ail -cc gcc -showcc test-all || exit $? \ No newline at end of file diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..ccb7503 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,46 @@ +name: Docs + +on: + - push + - workflow_dispatch + +jobs: + generate-and-publish-docs: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install ASPL + run: | + cd $GITHUB_WORKSPACE + ./install_ci.sh + - name: Generate docs + run: | + cd $GITHUB_WORKSPACE + mkdir docs + cd stdlib + FOLDERS="" + for d in * ; do + ../aspl -o ../docs/$d document $d + FOLDERS+="* [$d]($d.md)"$'\n' + done + echo "$FOLDERS" > ../docs/overview.md + - uses: actions/checkout@v3 + with: + repository: aspl-lang/docs + path: docs_repo + token: ${{ secrets.CDKEY }} + - name: Copy docs + run: | + cd $GITHUB_WORKSPACE + cp -r docs/* docs_repo/ + sed -i '/## Modules/,$ d' docs_repo/README.md && echo "## Modules" >> docs_repo/README.md && cat docs_repo/overview.md >> docs_repo/README.md + rm docs_repo/overview.md + - name: Commit and push + run: | + cd $GITHUB_WORKSPACE/docs_repo + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + git add . + git diff-index --quiet HEAD || git commit -m "Update docs" + git push \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c851a8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +* +!*.* +!*/ +!LICENSE +*.exe +*.exe.lib +*.dll +*.pdb +/*.c +*.def +*.apk +*.ail +*.log \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..cf6ac09 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,16 @@ +# Contributing to ASPL +## Creating something awesome +One of the best ways to support ASPL is by creating a project in it. You may port an already exisiting tool, develop something innovative and new or even rewrite your own applications in ASPL. + +Only through such projects, we can find out how usable ASPL really is in different fields of computer science, catch and patch bugs, and implement new features. It also just massively helps in making ASPL more know and popular. + +## Actually contributing to the implementation +You can of course also directly contribute to ASPLs sourcecode (and the stuff around it) here on Github. +
The best way to get started is probably just looking around at the files and folders; here's a quick overview on how the code is structured: + +### Compiler and standard library +The compiler and all the stuff around it is written in ASPL and part of the ASPL standard library, which is located in the `stdlib` folder. +
Please read the REAMDE of the `stdlib/aspl` folder for more information. + +### Runtime and AIL interpreter +The runtime and the AIL interpreter are written in C; they are currently located at `stdlib/aspl/compiler/backend/stringcode/c/template.c` and `runtime/ailinterpreter` respectively. \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4ccd037 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) The ASPL contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e84e388 --- /dev/null +++ b/README.md @@ -0,0 +1,83 @@ +
+The ASPL logo +

The ASPL Programming Language

+
+
+ +[![CI/CD Test Suite](https://github.com/aspl-lang/aspl/actions/workflows/ci_cd.yml/badge.svg)](https://github.com/aspl-lang/aspl/actions/workflows/ci_cd.yml) +![Commit activity](https://img.shields.io/github/commit-activity/m/aspl-lang/aspl) +[![Discord](https://img.shields.io/discord/1053681798430859264?label=Discord&logo=discord&logoColor=white)](https://discord.gg/UUNzAFrKU2) + +
+ASPL is a modern programming language featuring beautiful simplicity, seamless cross-platform support, practical OOP and so much more. + +## Why ASPL? +* Simple 😀 +
The syntax is clean and readable, error messages provide useful information, and installing ASPL is as easy as a few clicks. + +* Safe 🔒 +
High-level abstractions, automatic memory management, out-of-the-box TLS support, and a strong type system make ASPL one of the safest languages out there. + +* Cross platform 💻 +
Newcomers as well as experienced programmers can easily create powerful cross-platform applications with ASPL; seamless cross-compilation is one of ASPL's core design principles. + +* Huge standard library 🔥 +
Builtin JSON, graphics, and advanced networking support are just a few of the things that will drastically speed up your coding process. + +* Modular architecture 📚 +
ASPL has a great and simple modular library system that helps you reuse any kind of third-party code in your projects, including even the ASPL compiler itself. + +> [!IMPORTANT] +> ASPL is still in early development and some features are missing or incomplete. Even though the language is already quite stable and very pratical, it is not yet recommended for production use. Contributions are always welcome! + +# Installing +Have a look at the installation guide. + +# Introduction +An introduction into ASPL can be found here. +
It also documents most of the language and has a lot of examples. + +# Bugs and Suggestions +ASPL is currently in early development and still has some bugs and missing features. +
Please report bugs or suggest features by opening an issue or posting them in the ASPL discord server. + +# Star History +[![Star History Chart](https://api.star-history.com/svg?repos=aspl-lang/aspl&type=Date)](https://star-history.com/#aspl-lang/aspl) + +# Code Examples +-> means the program prints text to the console +
<- means the user types something into the console +## Hello World +```aspl +print("Hello World!") // print means "write into the console" +``` +**Output:** +
-> Hello World! + +## Favorite food +```aspl +var favoriteFood = input("What's your favorite food?") // prints "What's your favorite food" and waits until the user types something into the console +if(favoriteFood == "Spaghetti"){ // checks whether the users input matches a certain string, here: "Spaghetti"; if it doesn't, the code between the braces will be skipped + print("Hm, yummy, that's my favorite food too!") +}else{ // the following code is executed only if the condition in the if statement evaluated to false, here: the input was not "Spaghetti" + print("Sounds great!") +} +``` +**Example Output:** +
-> What's your favorite food? +
<- Spaghetti +
-> Hm, yummy, that's my favorite food too! + +## Random number +```aspl +import rand + +print(rand.irange(1, 100)) // prints a random number between 1 and 100 into the console +``` +**Example Output:** +
-> 42 + +> [!NOTE] +> **More examples can be found here or in the ASPL introduction.** + +

👋 Feel free to join the official ASPL discord server.

\ No newline at end of file diff --git a/cli/main.aspl b/cli/main.aspl new file mode 100644 index 0000000..9fe4892 --- /dev/null +++ b/cli/main.aspl @@ -0,0 +1,572 @@ +import os +import console +import aspl.parser +import aspl.compiler +import aspl.compiler.utils +import io +import aspl.parser.ast +import aspl.parser.ast.statements +import aspl.parser.functions +import aspl.parser.properties +import aspl.parser.methods +import aspl.parser.utils +import cli.utils + +if(os.args().length < 2){ + display_help() + exit(2) +} + +var i = 1 // starting at 1 because 0 is the executable +if(os.args()[1].startsWith("-")){ + while(i < os.args().length && os.args()[i].startsWith("-")){ + var option = os.args()[i] + if(option == "-define" || option == "-d"){ + i++ + if(i >= os.args().length){ + print(console.red("aspl: `-define` option requires an argument")) + exit(2) + } + aspl.parser.Options:customConditionalCompilationSymbols.add(os.args()[i]) + }elseif(option == "-keeptemp"){ + Options:keepTemporary = true + }elseif(option == "-os"){ + i++ + if(i >= os.args().length){ + print(console.red("aspl: `-os` option requires an argument")) + exit(2) + } + Options:targetOs = os.args()[i] + }elseif(option == "-architecture" || option == "-arch"){ + i++ + if(i >= os.args().length){ + print(console.red("aspl: `-architecture` option requires an argument")) + exit(2) + } + if(os.architecture_from_string(os.args()[i]) == null){ + print(console.red("aspl: unknown architecture: " + os.args()[i])) + exit(2) + } + Options:targetArchitecture = Architecture(os.architecture_from_string(os.args()[i])) + }elseif(option == "-output" || option == "-o"){ + i++ + if(i >= os.args().length){ + print(console.red("aspl: `-output` option requires an argument")) + exit(2) + } + Options:outputFile = os.args()[i] + }elseif(option == "-production" || option == "-prod" || option == "-release"){ + Options:production = true + }elseif(option == "-gui"){ + Options:guiApp = true + }elseif(option == "-backend"){ + i++ + if(i >= os.args().length){ + print(console.red("aspl: `-backend` option requires an argument")) + exit(2) + } + Options:backend = os.args()[i] + if(!["ail", "c"].contains(Options:backend)){ + if(Options:backend == "twail"){ + aspl.parser.utils.notice("The `twail` backend is deprecated and should only be used if it is actually needed.") + }else{ + print(console.red("aspl: unknown backend `" + Options:backend + "`")) + exit(2) + } + } + }elseif(option == "-cc"){ + i++ + if(i >= os.args().length){ + print(console.red("aspl: `-cc` option requires an argument")) + exit(2) + } + // TODO: Check if the compiler exists + Options:cCompiler = os.args()[i] + if(Options:cCompiler == "zigcc"){ + Options:cCompiler = "zig cc" + } + }elseif(option == "-useDynamicCTemplate"){ + Options:useDynamicCTemplate = true + }elseif(option == "-showcc"){ + Options:showCCommand = true + }elseif(option == "-heapBased"){ + Options:heapBased = true + }elseif(option == "-stacksize" || option == "-stack"){ + i++ + if(i >= os.args().length){ + print(console.red("aspl: `-stacksize` option requires an argument")) + exit(2) + } + Options:stackSize = int(os.args()[i]) + }elseif(option == "-ssl"){ + Options:useSsl = true + }elseif(option == "-enableErrorHandling"){ + Options:enableErrorHandling = true + }elseif(option == "-noCachedTemplate"){ + Options:noCachedTemplate = true + }else{ + print(console.red("aspl: unknown option `" + option + "`")) + exit(2) + } + i++ + } + if(Options:useSsl){ + if(Options:backend != "c"){ + print(console.red("aspl: `-ssl` option can only be used with the C backend")) + exit(2) + } + if(Options:targetOs == "windows"){ + print(console.red("aspl: `-ssl` option is not supported (and also not required) for Windows")) + exit(2) + } + if(Options:targetOs == "macos"){ + print(console.red("aspl: `-ssl` option is not yet supported for macOS")) + exit(2) + } + } +} + +if(os.args().length < i + 1){ + display_help() + exit(2) +} + +var subcommand = os.args()[i] +if(subcommand == "compile" || subcommand == "build"){ + i++ + if(os.args().length < i + 1){ + print(console.red("aspl compile: missing source file or directory")) + exit(2) + } + aspl.compiler.compile(os.args()[i]) +}elseif(subcommand == "run"){ + i++ + if(os.args().length < i + 1){ + print(console.red("aspl run: missing source file or directory")) + exit(2) + } + Options:outputFile = ".aspl_run" // TODO: Use a random name + var outputFile = Options:outputFile + if(Options:targetOs == "windows"){ + outputFile += ".exe" + } + outputFile = io.abs(outputFile) + aspl.compiler.compile(os.args()[i]) + print(os.execute(outputFile).output, false) + io.delete_file(outputFile) +}elseif(subcommand == "test-all"){ + test_all() +}elseif(subcommand == "format"){ + print(console.yellow("Coming soon...")) +}elseif(subcommand == "document" || subcommand == "doc"){ + i++ + if(os.args().length < i + 1){ + print(console.red("aspl document: missing source file or directory")) + exit(2) + } + var main = io.abs(os.args()[i]) + var string mainDirectory = "" + if(io.exists_file(main)){ + mainDirectory = io.full_directory_path(main) + }elseif(io.exists_directory(main)){ + mainDirectory = main + }else{ + aspl.parser.utils.fatal_error("Main is neither a valid file nor a valid directory: " + main) + } + Module:init(new Module(io.directory_name(mainDirectory), mainDirectory)) + var result = aspl.parser.parse() + var outputMode = "markdown" // TODO: Add option to change this + var string output = "" + if(outputMode == "markdown"){ + output += "# " + Module:mainModule.name + "\n" + } + foreach(result.nodes as node){ + if(node oftype ClassDeclareStatement){ + if(ClassDeclareStatement(node).c.type.toString().toLower().startsWith(Module:mainModule.id) && ClassDeclareStatement(node).c.isPublic){ + if(outputMode == "markdown"){ + output += "## class " + ClassDeclareStatement(node).c.type.toString() + "\n" + output += "Source: " + Location(ClassDeclareStatement(node).c.location).file + ":" + Location(ClassDeclareStatement(node).c.location).startLine + ":" + Location(ClassDeclareStatement(node).c.location).startColumn + "\n" + + var commentOutput = "" + foreach(ClassDeclareStatement(node).comments as comment){ + if(comment.value.startsWith("// " + ClassDeclareStatement(node).c.type.toString())){ + commentOutput += "> " + comment.value.after("// ".length - 1) + "\n" + } + } + if(commentOutput != ""){ + output += "\n" + output += commentOutput + output += "\n" + } + + foreach(list(ClassDeclareStatement(node).c.code) as statement){ + if(statement oftype PropertyDeclareStatement && PropertyDeclareStatement(statement).p.isPublic){ + output += "### property " + PropertyDeclareStatement(statement).p.name + "\n" + if(PropertyDeclareStatement(statement).p oftype CustomNormalProperty){ + output += "Source: " + Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).location).file + ":" + Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).location).startLine + ":" + Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).location).startColumn + "\n" + }elseif(PropertyDeclareStatement(statement).p oftype CustomReactiveProperty){ + output += "Source: " + Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).location).file + ":" + Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).location).startLine + ":" + Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).location).startColumn + "\n" + } + + var propertyCommentOutput = "" + foreach(PropertyDeclareStatement(statement).comments as comment){ + if(comment.value.startsWith("// " + PropertyDeclareStatement(statement).p.name)){ + propertyCommentOutput += "> " + comment.value.after("// ".length - 1) + "\n" + } + } + if(propertyCommentOutput != ""){ + output += "\n" + output += propertyCommentOutput + output += "\n" + } + + output += "```aspl\n" + if(PropertyDeclareStatement(statement).p oftype CustomNormalProperty){ + var lines = io.read_file(Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).location).file).replace("\r\n", "\n").replace("\r", "\n").split("\n") + var line = Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).location).startLine + var code = "" + while(line <= Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).headerEndLocation).endLine){ + var l = lines[line - 1] + if(line == Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).headerEndLocation).endLine){ + l = l.before(Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).headerEndLocation).endColumn - 2) + } + // The start cutting has to be done after the end cutting because the start cutting can change the column indices + if(line == Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).location).startLine){ + l = l.after(Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).location).startColumn - 2) + } + code += l + "\n" + line++ + } + output += code.trim() + }elseif(PropertyDeclareStatement(statement).p oftype CustomReactiveProperty){ + var lines = io.read_file(Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).location).file).replace("\r\n", "\n").replace("\r", "\n").split("\n") + var line = Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).location).startLine + var code = "" + while(line <= Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).headerEndLocation).endLine){ + var l = lines[line - 1] + if(line == Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).headerEndLocation).endLine){ + l = l.before(Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).headerEndLocation).endColumn - 2) + } + // The start cutting has to be done after the end cutting because the start cutting can change the column indices + if(line == Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).location).startLine){ + l = l.after(Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).location).startColumn - 2) + } + code += l + "\n" + line++ + } + output += code.trim() + } + output += "\n```\n" + } + } + foreach(list(ClassDeclareStatement(node).c.code) as statement){ + if(statement oftype MethodDeclareStatement && MethodDeclareStatement(statement).m.isPublic){ + if(CustomMethod(MethodDeclareStatement(statement).m).location == null){ + continue // Automatically generated constructors should not be documented + } + output += "### method " + MethodDeclareStatement(statement).m.name + "\n" + output += "Source: " + Location(CustomMethod(MethodDeclareStatement(statement).m).location).file + ":" + Location(CustomMethod(MethodDeclareStatement(statement).m).location).startLine + ":" + Location(CustomMethod(MethodDeclareStatement(statement).m).location).startColumn + "\n" + + var methodCommentOutput = "" + foreach(MethodDeclareStatement(statement).comments as comment){ + if(comment.value.startsWith("// " + MethodDeclareStatement(statement).m.name)){ + methodCommentOutput += "> " + comment.value.after("// ".length - 1) + "\n" + } + } + if(methodCommentOutput != ""){ + output += "\n" + output += methodCommentOutput + output += "\n" + } + + output += "```aspl\n" + var lines = io.read_file(Location(CustomMethod(MethodDeclareStatement(statement).m).location).file).replace("\r\n", "\n").replace("\r", "\n").split("\n") + var line = Location(CustomMethod(MethodDeclareStatement(statement).m).location).startLine + var code = "" + while(line <= Location(CustomMethod(MethodDeclareStatement(statement).m).headerEndLocation).endLine){ + var l = lines[line - 1] + if(line == Location(CustomMethod(MethodDeclareStatement(statement).m).headerEndLocation).endLine){ + l = l.before(Location(CustomMethod(MethodDeclareStatement(statement).m).headerEndLocation).endColumn - 2) + } + // The start cutting has to be done after the end cutting because the start cutting can change the column indices + if(line == Location(CustomMethod(MethodDeclareStatement(statement).m).location).startLine){ + l = l.after(Location(CustomMethod(MethodDeclareStatement(statement).m).location).startColumn - 2) + } + code += l + "\n" + line++ + } + output += code.trim() + output += "\n```\n" + } + } + + output += "\n" + } + } + } + } + foreach(result.nodes as node){ + if(node oftype EnumDeclareStatement){ + if(EnumDeclareStatement(node).e.type.toString().toLower().startsWith(Module:mainModule.id) && EnumDeclareStatement(node).e.isPublic){ + if(outputMode == "markdown"){ + output += "## enum " + EnumDeclareStatement(node).e.type.toString() + "\n" + output += "Source: " + Location(EnumDeclareStatement(node).e.location).file + ":" + Location(EnumDeclareStatement(node).e.location).startLine + ":" + Location(EnumDeclareStatement(node).e.location).startColumn + "\n" + + output += "```aspl\n" + var lines = io.read_file(Location(EnumDeclareStatement(node).e.location).file).replace("\r\n", "\n").replace("\r", "\n").split("\n") + var line = Location(EnumDeclareStatement(node).e.location).startLine + var code = "" + while(line <= Location(EnumDeclareStatement(node).e.location).endLine){ + var l = lines[line - 1] + if(line == Location(EnumDeclareStatement(node).e.location).endLine){ + l = l.before(Location(EnumDeclareStatement(node).e.location).endColumn - 1) + } + // The start cutting has to be done after the end cutting because the start cutting can change the column indices + if(line == Location(EnumDeclareStatement(node).e.location).startLine){ + l = l.after(Location(EnumDeclareStatement(node).e.location).startColumn - 2) + } + code += l + "\n" + line++ + } + + output += "\n```\n" + + var commentOutput = "" + foreach(EnumDeclareStatement(node).comments as comment){ + if(comment.value.startsWith("// " + EnumDeclareStatement(node).e.type.toString())){ + commentOutput += "> " + comment.value.after("// ".length - 1) + "\n" + } + } + if(commentOutput != ""){ + output += "\n" + output += commentOutput + output += "\n" + } + + output += "\n" + } + } + } + } + foreach(result.nodes as node){ + if(node oftype FunctionDeclareStatement){ + if(FunctionDeclareStatement(node).func.identifier.toLower().startsWith(Module:mainModule.id) && FunctionDeclareStatement(node).func.isPublic){ + if(outputMode == "markdown"){ + output += "## function " + FunctionDeclareStatement(node).func.identifier + "\n" + output += "Source: " + Location(CustomFunction(FunctionDeclareStatement(node).func).location).file + ":" + Location(CustomFunction(FunctionDeclareStatement(node).func).location).startLine + ":" + Location(CustomFunction(FunctionDeclareStatement(node).func).location).startColumn + "\n" + + output += "```aspl\n" + var lines = io.read_file(Location(CustomFunction(FunctionDeclareStatement(node).func).location).file).replace("\r\n", "\n").replace("\r", "\n").split("\n") + var line = Location(CustomFunction(FunctionDeclareStatement(node).func).location).startLine + var code = "" + while(line <= Location(CustomFunction(FunctionDeclareStatement(node).func).headerEndLocation).endLine){ + var l = lines[line - 1] + if(line == Location(CustomFunction(FunctionDeclareStatement(node).func).headerEndLocation).endLine){ + l = l.before(Location(CustomFunction(FunctionDeclareStatement(node).func).headerEndLocation).endColumn - 2) + } + // The start cutting has to be done after the end cutting because the start cutting can change the column indices + if(line == Location(CustomFunction(FunctionDeclareStatement(node).func).location).startLine){ + l = l.after(Location(CustomFunction(FunctionDeclareStatement(node).func).location).startColumn - 2) + } + code += l + "\n" + line++ + } + output += code.trim() + output += "\n```\n" + + var commentOutput = "" + foreach(FunctionDeclareStatement(node).comments as comment){ + if(comment.value.startsWith("// " + FunctionDeclareStatement(node).func.identifier.split(".", 2)[1])){ + commentOutput += "> " + comment.value.after("// ".length - 1) + "\n" + } + } + if(commentOutput != ""){ + output += "\n" + output += commentOutput + output += "\n" + } + + output += "\n" + } + } + } + } + output = output.trim() + if(outputMode == "markdown"){ + var file = Options:outputFile + if(file == null){ + file = Module:mainModule.id + } + io.write_file(file + ".md", output) + } +}elseif(subcommand == "install"){ + i++ + if(os.args().length < i + 1){ + print(console.red("aspl install: missing module URL or local path")) + exit(2) + } + var module = os.args()[i] + var moduleParts = module.replace("\\", "/").split("/") + var moduleName = moduleParts[moduleParts.length - 1] + if(io.exists_file(module) || io.exists_directory(module)){ + io.create_symlink(io.abs(module), ModuleUtils:getModulePath(moduleName)) + }else{ + os.execute("git clone " + module + " " + ModuleUtils:getModulePath(moduleName)) + } +}elseif(subcommand == "update"){ + i++ + if(os.args().length < i + 1){ + if(!io.exists_directory(io.join_path([io.full_directory_path(io.get_executable_path()), ".git"]))){ + print(console.red("aspl update: not in a git repository")) + exit(1) + } + os.execute("cmd /C cd " + io.full_directory_path(io.get_executable_path()) + " && git pull") + cli.utils.download_templates() + cli.utils.download_executable() + }else{ + var module = os.args()[i] + var moduleParts = module.split("/") + var moduleName = moduleParts[moduleParts.length - 1] + os.execute("cmd /C cd " + ModuleUtils:getModulePath(moduleName) + " && git pull") + } +}elseif(subcommand == "internal-update-1"){ + $if windows{ + io.move(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl.exe"]), io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_not_updated.exe"])) + os.execvp(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_not_updated.exe"]), ["internal-update-2"]) + }$else{ + io.move(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl"]), io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_not_updated"])) + os.execvp(io.join_path(["./", io.full_directory_path(io.get_executable_path()), "aspl_not_updated"]), ["internal-update-2"]) + } + exit(0) +}elseif(subcommand == "internal-update-2"){ + $if windows{ + io.move(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_updated.exe"]), io.join_path([io.full_directory_path(io.get_executable_path()), "aspl.exe"])) + os.execvp(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl.exe"]), ["internal-update-3"]) + }$else{ + io.move(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_updated"]), io.join_path([io.full_directory_path(io.get_executable_path()), "aspl"])) + os.execvp(io.join_path(["./", io.full_directory_path(io.get_executable_path()), "aspl"]), ["internal-update-3"]) + } + exit(0) +}elseif(subcommand == "internal-update-3"){ + $if windows{ + io.delete_file(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_not_updated.exe"])) + }$else{ + io.delete_file(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_not_updated"])) + } + print(console.green("aspl update: successfully updated ASPL to the latest version (" + cli.utils.get_current_version() + ")")) +}elseif(subcommand == "version"){ + print("aspl version: " + cli.utils.get_current_version() + " (Git SHA " + cli.utils.get_git_sha()+ ")") +}elseif(subcommand == "build-minimal-template"){ + io.create_directory("minimal_template") + io.write_file("minimal_template/main.aspl", "") + if(Options:targetOs != "windows"){ + LinkUtils:libraries.add("dl") + } + Options:backend = "ail" + Options:outputFile = "Template" + Options:production = true + Options:noCachedTemplate = true + Options:internalTemplateType = "minimal" + Options:internalDoNotBundle = true + aspl.compiler.compile("minimal_template") + io.delete_file("minimal_template/main.aspl") + io.delete_directory("minimal_template") +}elseif(subcommand == "build-full-template"){ + io.create_directory("full_template") + io.write_file("full_template/main.aspl", "") + cli.utils.gather_implementation_calls() + if(Options:targetOs != "windows"){ + LinkUtils:libraries.add("dl") + } + Options:backend = "ail" + Options:outputFile = "Template" + Options:production = true + Options:noCachedTemplate = true + Options:internalTemplateType = "full" + Options:internalDoNotBundle = true + aspl.compiler.compile("full_template") + io.delete_file("full_template/main.aspl") + io.delete_directory("full_template") +}elseif(subcommand == "help"){ + display_help() +}else{ + print(console.red("Unknown subcommand: " + subcommand)) + display_help() + exit(2) +} + +function display_help(){ + print("Usage:\n\t$ aspl [options] [arguments]") + print("Available commands:") + print("\taspl compile Compile and build a source file or directory") + print("\taspl run Compile and directly run a source file or directory") + print("\taspl test-all Compile and run all tests") + print("\taspl format Format a source file or directory") + print("\taspl document Document a source file or directory") + print("\taspl install Install a module from the internet or a local directory") + print("\taspl update [source] Update a given module or ASPL itself") + print("\taspl help Display this help message") +} + +function test_all(){ + foreach(io.glob("tests/*") as test){ + var testPath = test + if(!testPath.replace("\\", "/").startsWith("tests/")){ // TODO: glob() returns paths incosistently + testPath = io.join_path(["tests", test]) + } + test = io.directory_name(testPath) + if(!io.exists_directory(testPath)){ + print(console.red("Tests have to be directories; " + testPath + " is a file")) + exit(1) + } + if(test.endsWith("/")){ + test = test.before(test.length - 1) + } + var files = io.glob(testPath + "/*") + var found = false + foreach(files as file){ + if(file.endsWith(".aspl") || file.endsWith(".taspl")){ + found = true + } + } + if(!found){ + print(console.red("No tests found in " + testPath)) // TODO: Support nested test directories + exit(1) + } + os.chdir(testPath) + // Note: We're using separate threads here to prevent static properties from being shared between tests (they're marked as [threadlocal]) + var compilationDone = false + callback(){ + aspl.compiler.compile(".") + compilationDone = true + }.+() + while(!compilationDone){ + time.millisleep(10) + } + if(run_test(test)){ + print(console.gray("Test " + test + " passed...")) + }else{ + print(console.red("Test " + test + " failed!")) + exit(1) + } + $if windows{ + io.delete_file(test + ".exe") + if(io.exists_file(test + ".pdb")){ + io.delete_file(test + ".pdb") + } + }$else{ + io.delete_file(test) + } + os.chdir("..") + os.chdir("..") + } + print(console.green("All tests passed!")) +} + +function run_test(string test) returns bool{ + $if windows{ + return os.system(test + ".exe") == 0 + }$else{ + return os.system("./" + test) == 0 + } +} \ No newline at end of file diff --git a/cli/utils/full_template.aspl b/cli/utils/full_template.aspl new file mode 100644 index 0000000..10a4394 --- /dev/null +++ b/cli/utils/full_template.aspl @@ -0,0 +1,71 @@ +import aspl.parser +import aspl.parser.utils +import aspl.compiler.utils +import io +import strings + +function gather_implementation_calls(){ + foreach(find_files_recursively("stdlib", "implementations.c") as file){ // TODO: Also allow other file names + var lines = io.read_file(file).split("\n") + var isImplementationFile = false + foreach(lines as line){ + if(line.contains("ASPL_OBJECT_TYPE ASPL_IMPLEMENT_")){ + isImplementationFile = true + var argc = count_string_occurrences(line, "ASPL_OBJECT_TYPE* ") + var implementationCallNameMatch = regex.match_string("ASPL_OBJECT_TYPE ASPL_IMPLEMENT_", line)?! + var implementationCallName = line.after(implementationCallNameMatch.end - 1).split("(")[0] + ImplementationCallUtils:usedImplementationCalls[implementationCallName] = argc + } + } + if(isImplementationFile){ + IncludeUtils:include(file) + } + } + // The following libraries are required for the graphics module + if(Options:targetOs == "windows"){ + LinkUtils:libraries.add("gdi32") + }elseif(Options:targetOs == "linux"){ + LinkUtils:libraries.add("X11") + LinkUtils:libraries.add("Xcursor") + LinkUtils:libraries.add("Xi") + LinkUtils:libraries.add("GL") + LinkUtils:libraries.add("dl") + }elseif(Options:targetOs == "macos"){ + LinkUtils:libraries.add("framework:Cocoa") + LinkUtils:libraries.add("framework:OpenGL") + } + // The following libraries are required for the internet module + if(Options:targetOs == "windows"){ + LinkUtils:libraries.add("ws2_32") + } +} + +function find_files_recursively(string directory, string name) returns list{ + var allFiles = io.files(directory) + var list files = [] + foreach(allFiles as file){ + if(file.contains(name)){ + files.add(io.join_path([directory, file])) + } + } + var dirs = io.directories(directory) + foreach(dirs as dir){ + files.insertElements(files.length, find_files_recursively(io.join_path([directory, dir]), name)) + } + return files +} + +function count_string_occurrences(string haystack, string needle) returns int{ + var count = 0 + var index = 0 + while(index < haystack.length){ + var remainder = haystack.after(index - 1) + if(remainder.startsWith(needle)){ + count++ + index += needle.length + }else{ + index++ + } + } + return count +} \ No newline at end of file diff --git a/cli/utils/update.aspl b/cli/utils/update.aspl new file mode 100644 index 0000000..5278382 --- /dev/null +++ b/cli/utils/update.aspl @@ -0,0 +1,108 @@ +import internet +import json +import io +import encoding.base64 +import zip +import os + +function download_templates(){ + var githubToken = fetch_github_token() + var asset = fetch_asset_id("templates.zip") + var response = internet.get("https://api.github.com/repos/aspl-lang/cd/releases/assets/" + asset, "", {"Authorization" => ["token " + githubToken], "Accept" => ["application/octet-stream"]}) + io.write_file(io.join_path([io.full_directory_path(io.get_executable_path()), "templates.zip"]), response.text) + zip.unzip(io.join_path([io.full_directory_path(io.get_executable_path()), "templates.zip"]), ".") + io.delete_file(io.join_path([io.full_directory_path(io.get_executable_path()), "templates.zip"])) +} + +// download_executable downloads the latest aspl cli executable +// Warning: this function never returns and starts a new process instead +function download_executable(){ + var githubToken = fetch_github_token() + var assetName = "aspl" + $if windows{ + assetName += "_windows" + $if amd64{ + assetName += "_x86_64" + } + $if i386{ + assetName += "_x86" + } + $if arm64{ + assetName += "_arm64" + } + assetName += ".exe" + } + $if linux{ + assetName += "_linux" + $if amd64{ + assetName += "_x86_64" + } + $if i386{ + assetName += "_x86" + } + $if arm32{ + assetName += "_arm32" + } + $if arm64{ + assetName += "_arm64" + } + } + $if macos{ + assetName += "_macos" + $if amd64{ + assetName += "_x86_64" + } + $if i386{ + assetName += "_x86" + } + $if arm64{ + assetName += "_arm64" + } + } + var asset = fetch_asset_id(assetName) + var response = internet.get("https://api.github.com/repos/aspl-lang/cd/releases/assets/" + asset, "", {"Authorization" => ["token " + githubToken], "Accept" => ["application/octet-stream"]}) + $if windows{ + io.write_file(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_updated.exe"]), response.text) + os.execvp(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_updated.exe"]), ["internal-update-1"]) + }$else{ + io.write_file(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_updated"]), response.text) + os.execute("chmod +x " + io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_updated"])) + os.execvp("./" + io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_updated"]), ["internal-update-1"]) + } + exit(0) +} + +function fetch_github_token() returns string{ + if(io.exists_file("github.token")){ + return io.read_file("github.token").trim() + } + print(console.red("aspl update: github token not found, please create a file named github.token in the current directory and put your github token in it")) + exit(1) +} + +function fetch_asset_id(string name) returns string{ + var githubToken = fetch_github_token() + var sha = "" + var asset = "" + { + var response = internet.get("https://api.github.com/repos/aspl-lang/cd/contents/latest.txt", "", {"Authorization" => ["token " + githubToken]}) + var contents = map(json.decode(response.text)) + var content = string(contents["content"]).trim() + if(content.endsWith("\\n")){ + content = content.before(content.length - 2) + } + sha = encoding.base64.decode(content).trim() + } + { + var response = internet.get("https://api.github.com/repos/aspl-lang/cd/releases/tags/SHA-" + sha, "", {"Authorization" => ["token " + githubToken]}) + var contents = map(json.decode(response.text)) + var assets = list(contents["assets"]) + foreach(assets as a){ + if(map(a)["name"] == name){ + asset = string(map(a)["id"]) + break + } + } + } + return asset +} \ No newline at end of file diff --git a/cli/utils/version.aspl b/cli/utils/version.aspl new file mode 100644 index 0000000..2875d07 --- /dev/null +++ b/cli/utils/version.aspl @@ -0,0 +1,19 @@ +import os + +function get_git_sha() returns string{ + var wd = os.getwd() + os.chdir(io.full_directory_path(io.get_executable_path())) + var sha = os.execute("git rev-parse --short HEAD").output.trim() + os.chdir(wd) + return sha +} + +function get_current_version() returns string{ + var string nextRelease = "0.1" + var bool isDevelopementBuild = false + if(isDevelopementBuild){ + return "v" + nextRelease + "-" + get_git_sha() + }else{ + return "v" + nextRelease + } +} \ No newline at end of file diff --git a/examples/consonants_and_vowels/main.aspl b/examples/consonants_and_vowels/main.aspl new file mode 100644 index 0000000..3afa148 --- /dev/null +++ b/examples/consonants_and_vowels/main.aspl @@ -0,0 +1,15 @@ +var string input = input("Enter a string: ") + +var int consonants = 0 +var int vowels = 0 + +foreach(input as c){ + if(["a", "e", "i", "o", "u"].contains(c)){ + vowels++ + }else{ + consonants++ + } +} + +print("Consonants: " + consonants) +print("Vowels: " + vowels) \ No newline at end of file diff --git a/examples/double_pendulum/Pendulum.aspl b/examples/double_pendulum/Pendulum.aspl new file mode 100644 index 0000000..bf5a138 --- /dev/null +++ b/examples/double_pendulum/Pendulum.aspl @@ -0,0 +1,41 @@ +class Pendulum { + + property float g = 9.81 + property float dt = 0.05 + + [readpublic] + property double angle + [readpublic] + property double angVel = 0 + property double angAcc = 0 + property double length + property double mass + + [public] + method construct(double angle, double length, double mass){ + this.angle = angle + this.length = length + this.mass = mass + } + + [public] + method update1(){ + this.angAcc = (-g / this.length) * math.sin(this.angle) + this.angVel += this.angAcc * dt + this.angle += this.angVel * dt + } + + [public] + method update2(double angle1, double angVel1, double length1, double mass1){ + var num1 = -g * (2 * this.mass + mass1) * math.sin(this.angle) + var num2 = -this.mass * g * math.sin(this.angle - 2 * angle1) + var num3 = -2 * math.sin(this.angle - angle1) * this.mass + var num4 = angVel1 * angVel1 * length1 + this.angVel * this.angVel * this.length * math.cos(this.angle - angle1) + var den = length1 * (2 * this.mass + mass1 - this.mass * math.cos(2 * this.angle - 2 * angle1)) + + this.angAcc = (num1 + num2 + num3 * num4) / den + this.angVel += this.angAcc * dt + this.angle += this.angVel * dt + } + +} \ No newline at end of file diff --git a/examples/double_pendulum/main.aspl b/examples/double_pendulum/main.aspl new file mode 100644 index 0000000..271204d --- /dev/null +++ b/examples/double_pendulum/main.aspl @@ -0,0 +1,33 @@ +import graphics +import math +import math.geometry +import rand + +var double length1 = 100 +var double length2 = 100 +var double mass1 = 8 +var double mass2 = 6 + +var pendulum1 = new Pendulum(math.pi() / 2, length1, mass1) +var pendulum2 = new Pendulum(math.pi() * rand.drange(0, 1), length2, mass2) +var window = new Window("Double Pendulum Example", 500, 300) +window.onPaint = callback(Canvas canvas){ + length1 = canvas.height / 3d + length2 = canvas.height / 3d + + canvas.fill(Color:fromRGB(220, 220, 220), false) + pendulum1.update1() + pendulum2.update2(pendulum1.angle, pendulum1.angVel, length1, mass1) + + var x1 = int(length1 * math.sin(pendulum1.angle)) + var y1 = int(length1 * math.cos(pendulum1.angle)) + var x2 = int(x1 + length2 * math.sin(pendulum2.angle)) + var y2 = int(y1 + length2 * math.cos(pendulum2.angle)) + + canvas.drawLine(canvas.width / 2, canvas.height / 6, canvas.width / 2 + x1, canvas.height / 6 + y1, Color:fromRGB(0, 0, 0)) + canvas.drawLine(canvas.width / 2 + x1, canvas.height / 6 + y1, canvas.width / 2 + x2, canvas.height / 6 + y2, Color:fromRGB(0, 0, 0)) + canvas.fillCircle(new Ellipse(new Point(canvas.width / 2f, canvas.height / 6f), new Size(float(10), float(10))), Color:fromRGB(0, 200, 0)) + canvas.fillCircle(new Ellipse(new Point(canvas.width / 2f + x1, canvas.height / 6f + y1), new Size(float(mass1), float(mass1))), Color:fromRGB(200, 0, 0), false) + canvas.fillCircle(new Ellipse(new Point(canvas.width / 2f + x2, canvas.height / 6f + y2), new Size(float(mass2), float(mass2))), Color:fromRGB(0, 0, 200), false) +} +window.show() \ No newline at end of file diff --git a/examples/fahrenheit_to_celsius/main.aspl b/examples/fahrenheit_to_celsius/main.aspl new file mode 100644 index 0000000..84210f0 --- /dev/null +++ b/examples/fahrenheit_to_celsius/main.aspl @@ -0,0 +1,9 @@ +import math + +function fahrenheit_to_celsius(float degrees) returns float{ + return (degrees - 32) * (5f / 9) +} + +var fahrenheit = float(math.round(double(float(input("Enter a temperature in °F: "))), 2)) +var celsius = math.round(double(fahrenheit_to_celsius(fahrenheit)), 2) +print(fahrenheit + "°F ≙ " + celsius + "°C") \ No newline at end of file diff --git a/examples/fibonacci_numbers/main.aspl b/examples/fibonacci_numbers/main.aspl new file mode 100644 index 0000000..0338c45 --- /dev/null +++ b/examples/fibonacci_numbers/main.aspl @@ -0,0 +1,14 @@ +var long a = 1 +var long b = 0 +var long n = 0 + +while(true){ + n = a + b + if(n < 0){ + print("The program reached the maximum fibonacci number it can store.") + exit(0) + } + print(n) + b = a + a = n +} \ No newline at end of file diff --git a/examples/hello_world/main.aspl b/examples/hello_world/main.aspl new file mode 100644 index 0000000..1dc45ac --- /dev/null +++ b/examples/hello_world/main.aspl @@ -0,0 +1 @@ +print("Hello World!") \ No newline at end of file diff --git a/examples/json_basics/main.aspl b/examples/json_basics/main.aspl new file mode 100644 index 0000000..079dff9 --- /dev/null +++ b/examples/json_basics/main.aspl @@ -0,0 +1,7 @@ +import json + +var list l = list["Hello", "World", 1, 2, true] +print(json.encode(l)) + +var map m = map{"Hello" => "World", "x" => 1, "y" => 2, "b" => true} +print(json.encode(m, true)) // true for pretty print \ No newline at end of file diff --git a/examples/julia_set/main.aspl b/examples/julia_set/main.aspl new file mode 100644 index 0000000..4ee5295 --- /dev/null +++ b/examples/julia_set/main.aspl @@ -0,0 +1,33 @@ +// Original code taken and translated from: https://www.geeksforgeeks.org/julia-fractal-python/ + +import graphics + +var int width = 500 +var int height = 500 +var float zoom = 1f + +var Canvas canvas = new Canvas(500, 500) +canvas.fill(new Color(255b, 255b, 255b, 255b), false) + +var float cX = -0.7 +var float cY = 0.27015 +var float moveX = 0f +var float moveY = 0f +var int maxIterations = 255 + +repeat(width, x = 0){ + repeat(height, y = 0){ + var zx = 1.5 * (x - width / 2f) / (0.5 * zoom * width) + moveX + var zy = 1.5 * (y - height / 2f) / (0.5 * zoom * height) + moveY + var int i = maxIterations + while((zx * zx + zy * zy) < 4 && i > 1){ + var temp = zx * zx - zy * zy + cX + zy = 2 * zx * zy + cY + zx = temp + i-- + } + canvas.setPixel(x, y, Color:fromRGB(byte(i * 4), byte(i * 4), byte(i * 4))) + } +} + +canvas.save("julia_set.png") \ No newline at end of file diff --git a/examples/quicksort/main.aspl b/examples/quicksort/main.aspl new file mode 100644 index 0000000..5b34062 --- /dev/null +++ b/examples/quicksort/main.aspl @@ -0,0 +1,45 @@ +function quicksort(list l) returns list{ + if(l.length < 2){ + return l + } + + var float pivot = l[l.length / 2] + + var list left = [] + var list right = [] + var list equal = [] + + foreach(l as element){ + if(element < pivot){ + left.add(element) + } elseif(element == pivot){ + equal.add(element) + } else { + right.add(element) + } + } + + left = quicksort(left) + right = quicksort(right) + + var list result = [] + foreach(left as element){ + result.add(element) + } + foreach(equal as element){ + result.add(element) + } + foreach(right as element){ + result.add(element) + } + + return result +} + +var list l1 = input("Enter a list of numbers you want to sort: ").split(",") +var list l2 = [] +foreach(l1 as element){ + l2.add(float(element.trim())) +} +print("Unsorted: " + l2) +print("Sorted: " + quicksort(l2)) \ No newline at end of file diff --git a/install.bat b/install.bat new file mode 100755 index 0000000..5a8e239 --- /dev/null +++ b/install.bat @@ -0,0 +1,48 @@ +@echo off + +FOR /F "tokens=*" %%g IN ('git config --get remote.origin.url') do (SET remote=%%g) +echo %remote% | findstr /C:"github.com/aspl-lang/aspl" > nul && ( + git "pull" "origin" "main" +) || ( + git "clone" "https://github.com/aspl-lang/aspl.git" + cd "aspl" +) + +curl -L -o jq.exe https://github.com/stedolan/jq/releases/latest/download/jq-win64.exe + +(curl -s https://api.github.com/repos/aspl-lang/cd/contents/latest.txt | jq -r .content) > sha.txt +certutil -decode sha.txt sha_decoded.txt +SET /p SHA= id.txt +SET /p ASSET_ID= id.txt +SET /p ASSET_ID= **Note:** if you run the installer inside an already checked out ASPL repository, it will automatically update the repository to the latest version. + +> **Note:** you will need [`git` to be installed on your computer](https://github.com/git-guides/install-git) in order to use the ASPL installer. + +> **Note:** on Unix-like systems (Linux, macOS), you may need additionally to install `jq`, `curl` and `unzip` in order to run the installer script. On Debian-based systems, you can do this by running the following command: +```sh +sudo apt install jq curl unzip +``` + +After you have installed ASPL, you can check if everything is working by trying to compile and run the "Hello World" program: +```sh +$ aspl run "examples/hello_world" +Hello World! +``` + +Congratulations! You have successfully installed ASPL! +
Be sure to check out the [introduction](introduction.md) next to learn more about ASPL and how to use it. \ No newline at end of file diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..7f90a98 --- /dev/null +++ b/install.sh @@ -0,0 +1,40 @@ +if $(git config --get remote.origin.url | grep -q 'github.com/aspl-lang/aspl'); then + git pull origin main +else + git clone https://github.com/aspl-lang/aspl.git + cd aspl +fi +SHA=$(curl -s https://api.github.com/repos/aspl-lang/cd/contents/latest.txt | jq -r '.content' | base64 -d) +if [ "$(uname)" = "Darwin" ]; then + if [ "$(uname -m)" = "x86_64" ]; then + EXECUTABLE=aspl_macos_x86_64 + elif [ "$(uname -m)" = "armv7l" ]; then + EXECUTABLE=aspl_macos_arm32 + elif [ "$(uname -m)" = "aarch64" ]; then + EXECUTABLE=aspl_macos_arm64 + else + echo "Unsupported architecture" + exit 1 + fi +elif [ "$(uname)" = "Linux" ]; then + if [ "$(uname -m)" = "x86_64" ]; then + EXECUTABLE=aspl_linux_x86_64 + elif [ "$(uname -m)" = "armv7l" ]; then + EXECUTABLE=aspl_linux_arm32 + elif [ "$(uname -m)" = "aarch64" ]; then + EXECUTABLE=aspl_linux_arm64 + else + echo "Unsupported architecture" + exit 1 + fi +else + echo "Unsupported operating system" + exit 1 +fi +CURL="curl https://api.github.com/repos/aspl-lang/cd/releases"; ASSET_ID=$(eval "$CURL/tags/SHA-$SHA" | jq .assets | jq '.[] | select(.name == "'$EXECUTABLE'").id'); eval "$CURL/assets/$ASSET_ID -LJOH 'Accept: application/octet-stream'" +mv $EXECUTABLE aspl +chmod +x aspl +ln -s $PWD/aspl /usr/local/bin/aspl +CURL="curl https://api.github.com/repos/aspl-lang/cd/releases"; ASSET_ID=$(eval "$CURL/tags/SHA-$SHA" | jq .assets | jq '.[] | select(.name == "templates.zip").id'); eval "$CURL/assets/$ASSET_ID -LJOH 'Accept: application/octet-stream'" +unzip templates.zip +rm templates.zip \ No newline at end of file diff --git a/install_ci.sh b/install_ci.sh new file mode 100755 index 0000000..f06e1f7 --- /dev/null +++ b/install_ci.sh @@ -0,0 +1,34 @@ +SHA=$(curl -s https://api.github.com/repos/aspl-lang/cd/contents/latest.txt | jq -r '.content' | base64 -d) +if [ "$(uname)" = "Darwin" ]; then + if [ "$(uname -m)" = "x86_64" ]; then + EXECUTABLE=aspl_macos_x86_64 + elif [ "$(uname -m)" = "armv7l" ]; then + EXECUTABLE=aspl_macos_arm32 + elif [ "$(uname -m)" = "aarch64" ]; then + EXECUTABLE=aspl_macos_arm64 + else + echo "Unsupported architecture" + exit 1 + fi +elif [ "$(uname)" = "Linux" ]; then + if [ "$(uname -m)" = "x86_64" ]; then + EXECUTABLE=aspl_linux_x86_64 + elif [ "$(uname -m)" = "armv7l" ]; then + EXECUTABLE=aspl_linux_arm32 + elif [ "$(uname -m)" = "aarch64" ]; then + EXECUTABLE=aspl_linux_arm64 + else + echo "Unsupported architecture" + exit 1 + fi +else + echo "Unsupported operating system" + exit 1 +fi +CURL="curl https://api.github.com/repos/aspl-lang/cd/releases"; ASSET_ID=$(eval "$CURL/tags/SHA-$SHA" | jq .assets | jq '.[] | select(.name == "'$EXECUTABLE'").id'); eval "$CURL/assets/$ASSET_ID -LJOH 'Accept: application/octet-stream'" +mv $EXECUTABLE aspl +chmod +x aspl +ln -s $PWD/aspl /usr/local/bin/aspl +CURL="curl https://api.github.com/repos/aspl-lang/cd/releases"; ASSET_ID=$(eval "$CURL/tags/SHA-$SHA" | jq .assets | jq '.[] | select(.name == "templates.zip").id'); eval "$CURL/assets/$ASSET_ID -LJOH 'Accept: application/octet-stream'" +unzip templates.zip +rm templates.zip \ No newline at end of file diff --git a/introduction.md b/introduction.md new file mode 100644 index 0000000..6a88a59 --- /dev/null +++ b/introduction.md @@ -0,0 +1,1028 @@ +# Introduction to ASPL +ASPL is a simple yet powerful modern cross-platform programming language. +
It is especially suited for people who are just getting into programming, but can also be a powerful tool for more experienced programmers. +
ASPL has (obviously) been influenced by multiple different other languages, most notably C# and V. + +Going through this introduction will take you under an hour, but you will learn the most important skills for developing software with ASPL. +
Please keep in mind that this document is not (yet) a complete walkthrough of the language and there is still a lot of information missing. + +> [!NOTE] +> This introduction is written very beginner-friendly, but if you aren't familiar with software engineering at all yet, you might want to learn the basics of programming through other media (e.g. blog posts or videos) first. + +## Installation +ASPL supports most modern operating systems and easily cross-compiles between them. +
Please refer to the installation instructions to install ASPL on your system. + +## Table of contents + + +
+ +* [Hello World](#hello-world) +* [Comments](#comments) +* [Function basics](#function-basics) +* [Expression vs. Statement](#expression-vs-statement) +* [Variables](#variables) +* [Type system](#type-system) + * [Type aliases](#type-aliases) + * [Nullability](#nullability) + * [Oftype](#oftype) +* [If statements](#if-statements) +* [While statements](#while-statements) +* [Repeat statements](#repeat-statements) +* [Assertions](#assertions) +* [Strings](#strings) +* [Lists](#lists) +* [Maps](#maps) +* [Functions](#functions) +* [Callbacks](#callbacks) + * [Closures](#closures) + + + +* [Modules](#modules) +* [Namespaces](#namespaces) +* [Visibility modifiers](#visibility-modifiers) +* [Memory management](#memory-management) +* [Order of evaluation](#order-of-evaluation) +* [Error handling](#error-handling) + * [Catch blocks](#catch-blocks) + * [Error Propagation](#error-propagation) + * [Panics](#panics) +* [Classes](#classes) + * [Static classes](#static-classes) +* [Properties](#properties) + * [Static properties](#static-properties) +* [Methods](#methods) + * [Static methods](#static-methods) +* [Inheritance](#inheritance) +* [Parent Method Calls](#parent-method-calls) +* [Enums](#enums) + * [Bitfields/Flags](#bitfieldsflags) + + + +* [Compiler options](#compiler-options) + * [Cross-compilation](#cross-compilation) + * [Debug vs. production builds](#debug-vs-production-builds) + * [Conditional compilation](#conditional-compilation) + * [Choosing a backend](#choosing-a-backend) + * [Stack vs. heaped based builds](#stack-vs-heaped-based-builds) +* [Resource embedding](#resource-embedding) +* [Debugging](#debugging) + * [Breakpoints](#breakpoints) +* [Android & iOS](#android--ios) + +
+ +## Hello World +```aspl +print("Hello World!") +``` +Save this code to a file and run it with `aspl run file.aspl`. +
That's it. You have just written your very first ASPL program! 🎉 + +## Comments +ASPL supports two styles of comments + +### Single-line comments +If a line starts with `//`, everything after that is ignored by the ASPL parser and treated as a comment. +For example: +``` +print("Hello World!") // You can write whatever you want here +// This is a comment too +print("But this is executed again!") +``` + +### Multi-line comments +If you want to span a comment over multiple lines (very useful for commenting out pieces of code temporarily), you can use `/*` to start a comment and `*/` to end it. +
Note that multi-line comments can be nested in ASPL. +``` +print("Hello World!") +/*Start of comment +This is a comment +print("This is ignored since it's in a comment") +/* +Nested comment +*/ +End of comment*/ +print("This is executed again") +``` + +## Function basics +Functions are a computer science concept inspired by mathematical functions. +
They can take an input value, process it and return an output. +
Some functions don't return anything though, for example `print` - it only writes the text you gave it to the console. +
As already shown above, you can invoke a function like this: +```aspl +myFunction("argument 1", 42, "argument 3") // takes 3 arguments +myOtherFunction() // takes 0 arguments +``` +Functions that return something can be used as an expression and their return value can for example be passed to other functions: +```aspl +print(multiply(20 * 20)) // prints 400 +``` + +## Expression vs. Statement +A statement is an instruction to the computer. For example, `while`, `return`, function declarations, ... +
An expression, on the other hand, is also like an instruction, but it returns a value, e.g. an access to a variable, mathematical operators, the `this` keyword, ... +
Expressions can be passed to functions, assigned to variables, used with operators and so on. +
If you want to refer to any instruction, whether it's an expression or a statement, use the term `node`. + +## Variables +ASPL is statically typed. That means that all variables are associated with a certain type, e.g. a string or an integer. +
You can declare a variable with the `var` keyword, optionally followed by its types, the variable name, and its initial value. +
All variables in ASPL have an initial value. If no types are given, the type is inferred from the initial value. +```aspl +var int i = 0 +var float f = 1f +var s = "Hello" +``` +Updating a variable works like this: +```aspl +var i = 0 +print(i) +i = 1 +print(i) +``` +The output of the above program is: +```aspl +0 +1 +``` +Note that you cannot assign a value of a wrong type to a variable: +```aspl +var s = "Hello" +s = 0 // error, the variable 's' can only hold strings +``` + +## Type system +ASPL has a very advanced and powerful type system. +
Every expression and everything holding an expression (e.g. a variable) can have either a single type (e.g. literals or simple variables) or of a multitype: +```aspl +var int i = 0 // i can hold values of the type integer (since int is an alias to integer) +var int|float x = 0f // x can hold values of either integer or float, even though its initialized with a float here +``` +If something expects a certain type (e.g. a variable or function parameter), it will fail to compile if any of the types of the expression is incompatible with the expected type: +```aspl +var int|float x = 0f +var string|float y = 0f +x = y // error, y could be of type string, but x cannot hold strings +``` + +### Type aliases +Type aliases allow to refer to a certain type with another name: +```aspl +var int a = 0 +var integer b = 0 +a = b // no error, a and b are both of type integer +``` +All builtin type aliases are: +
`bool` => `boolean` +
`int` => `integer` + +It is currently not possible to declare custom type aliases. + +### Nullability +All types in ASPL are **non-nullable** by default: +```aspl +var int i = 0 +i = null // error, i cannot hold null +``` + +If you want to allow a variable (or property, etc.) to hold null, you can append a `?` to its type: +```aspl +var int? i = 0 +i = null // no error, i can hold null +``` +Note that the `?` is just a shorthand for `type|null`, so the following is also valid: +```aspl +var int|null i = 0 +i = null // no error, i can hold null +``` + +If you are sure that a value of type `type?` is not null in a certain situation (for example because you checked it before), you can use the `?!` ("null forgiving") operator to force the compiler to treat it as a value of type `type`: +```aspl +var string? s = "Hello" +if(s != null){ + print(s?!.length) // no error, s is assured to not be null here +} +``` +Note that the `?!` operator is just a shorthand for a cast to `type`, so the following is also valid: +```aspl +var string? s = "Hello" +if(s != null){ + print((string)s.length) // no error, s is assured to be not null here +} +``` + +### Oftype +The `oftype` expression checks if a certain expression is of a certain type at runtime: +```aspl +var any x = 0 +print(x oftype int) // true +print(x oftypr string) // false +``` + +## If statements +The `if` statement is an essential control flow structure in computer programming. It only executes a certain block of code if a condition is met, i.e. a given expression evaluates to true: +```aspl +if(true){ + print("Will certainly be printed") +} +if(false){ + print("Will certainly not be printed") +} +``` +Combined with for example variables or user input, this allows a programmer to dynamically react to a program's environment: +```aspl +if(input("Do you like ASPL? ") == "y"){ + print("Great!") +} +``` +For the above example, it would also be useful to execute some other code if the condition was not met. For this, there is the `else` keyword that can only follow after an `if` statement: +```aspl +if(input("Do you like ASPL? ") == "y"){ + print("Great!") +}else{ + print("Please let us know how we can improve!") +} +``` +Furthermore, if you want to check for multiple possible scenarios, you might want to use `elseif`: +```aspl +var requestedProduct = input("Which product do you want to buy? ") +if(requestedProduct == "apple"){ + print("You successfully bought an apple") +}elseif(requestedProduct == "banana"){ + print("You successfully bought a banana") +}else{ + print("I'm sorry, but we don't offer the product: " + product) +} +``` + +## While statements +Similar to the `if` statement, a `while` statement is a conditional control structure. But instead of executing the code in the body only once, it will be executed as long as the condition evaluates to true: +```aspl +// This never evaluates to true and thus directly skips the body +while(false){ + print("Will never be executed") +} + +// Prints all numbers from 0 to 9 +var i = 0 +while(i < 10){ + print(i) + i++ +} + +// This will repeat the body forever +while(true){ + print("Inside infinite loop...") +} +``` +There is no `else` for while loops. + +## Repeat statements +A `repeat` statement is like a while statement, but instead of taking a condition, it takes an integer specifying how often the code in the body should be repeated: +```aspl +repeat(100){ + print("Repeat...") +} +``` +You can also create a variable that will automatically hold the value of the current iteration: +```aspl +repeat(42, i = 0){ + print(i) +} +``` +As you can see, you can (and should) even specify the start value of the variable. + +## Assertions +Assertions are statements that are used to make sure certain conditions are met. If they are not met, the program will crash and print the location of the failed assertion. +
They are used for debugging purposes and can also help in documentation. +```aspl +assert true // ignored, no error +assert false // error, the assertion failed +``` + +## Strings +A string is simply a sequence of characters, as in most other programming languages. However, ASPL strings are special in two ways: + +1. They are immutable, i.e. you cannot change a string after it has been created +```aspl +var s = "Hello" +s[0] = "h" // error, strings are immutable +s = "h" + s.after(0) // this is the correct way to change a string +``` +This makes a lot of code much cleaner, less error-prone, and allows for better optimizations. + +2. They are natively Unicode, i.e. they can hold any character from any language: +```aspl +var s = "👋" +print(s) // prints 👋 +assert s.length == 1 +``` +> [!WARNING] +> Native Unicode strings are NOT fully implemented in ASPL yet. For example, the `length` property currently returns the number of bytes, not the number of characters. This is most likely to change in the future. + +## Lists +A list contains a variable amount of elements of the same type: +```aspl +var list l = ["Hello", "World", "!"] +print(l) // ["Hello", "World", "!"] +``` + +You can access an element in the list through indexing: +```aspl +var list l = ["Hello", "World", "!"] +print(l[0]) // Hello +print(l[2]) // ! +``` +**Note:** Indices start at **0** in ASPL (like in most other languages)! + +Adding elements to the list works using the `.add` method: +```aspl +var list l = ["Hello", "World"] +l.add("!") +print(l) // ["Hello", "World", "!"] +``` + +Removing elements can be either done by value or by index: +```aspl +var list l = ["Hello", "World", "!"] +l.removeAt(0) +l.remove("!") +print(l) // ["World"] +``` + +## Maps +A map is similar to a [list](#lists), but contains pairs of elements instead of individual elements. +
In a map, each pair contains a key and an associated value; you may know this concept from other languages under the terms `dictionary` or `associative array`. +```aspl +var map m = {"Universe" => 42} +print(m["Universe"]) // 42 +m["zero"] = 0 +print(m["zero"]) // 0 +m.removeKey("zero") +print(m) // {"Universe" => 42} +m.removeValue(42) +print(m) // {} +``` + +## Functions +As already described above, a function is a block of code with certain parameters. There are several builtin functions, e.g. `print`, `input` or `exit`. +
Moreover, you can define your own functions, as well as library developers, of course. +
This makes it possible to split a program into several smaller subroutines and reuse code. Because of this, functions are an essential concept in virtually every programming language today. + +```aspl +function hello(){ + print("Hello World!") +} +``` +The above code declares a function called `hello` that prints `Hello World!` to the console when executed. +
It can be invoked like this: +```aspl +hello() +``` + +Another example with parameters: +```aspl +function greet(string person){ + print("Hello " + person) +} + +greet("Jonas") // Hello Jonas +greet("Marie") // Hello Marie +``` + +## Callbacks +A callback is an anonymous function: +```aspl +var greet = callback(string name){ + print("Hello " + name + "!") +} +greet.invoke("John") // Hello John! +// Alternative (and preferred) syntax for invoking a callback +greet.("John") // Hello John! +``` +In this case, the callback itself does not have a name, although it is stored inside a variable called `greet`. + +### Closures +A callback can also be used as a closure. +
That means that callbacks can access the variables in their creation scope, even after it has already been popped. +
Furthermore, `this` also points to the instance in whose methods the callable was created. +```aspl +function create() returns callback{ + var num = 42 + return callback(){ + print(num) + } +} +var callback c = create() +c.invoke() // 42 +``` + +## Modules +In ASPL, the term `module` is used for code libraries, i.e. files that contain e.g. functions and classes and are intended to be used together in other applications. +
The modules of the standard library (`stdlib`) are automatically shipped with every complete ASPL installation. +
Of course, programmers can also create their own third-party modules. + +Modules are imported using the `import` statement: +```aspl +import math + +print(math.sqrt(9)) // 3 +``` + +Currently, modules do not need any special structure, but they might require a `module.json` or something similar in the future. + +You can install modules that are not in the standard library by using the `install` command, for example: `aspl install https://github.com/user/repo` +
These modules are stored in the `.aspl_modules` folder in the user's home directory. +
The modules of the standard library are located in the `stdlib` folder of the ASPL installation. + +## Namespaces +You can organize your code into folders and modules. To access functions and types defined in other folders, you can import them: + +`project/folder/hello.aspl`: +```aspl +function hello(){ + print("Hello World!") +} +``` + +`project/main.aspl`: +```aspl +import folder + +folder.hello() // Hello World! +``` + +Namespaces are always implicit in ASPL and inferred from the folder structure. + +If you import a module, you automatically import the main namespace of the module. You can import other folders in the module by prefixing the name of the folder with the name of the module: +```aspl +import encoding.ascii // imports the ascii folder of the encoding module +``` + +## Visibility modifiers +By default, all symbols, i.e. functions, methods, classes, properties and enums, are **private** in ASPL; this means that +* functions can only be called from within the same module they are defined in *(module bound)* +* classes can only be used from within the same module they are defined in *(module bound)* + * note that instantiating a class from outside that class is only possible if the constructor is marked as `[public]` +* enums can only be used from within the same module they are defined in *(module bound)* +* properties and methods can only be accessed from within the same class or children of the class they are defined in *(class bound)* + +You can export a symbol/make it public by using the `[public]` attribute: +```aspl +[public] +function hello(){ + print("Hello World!") +} +``` + +There is also the `[readpublic]` attribute, which makes a property read-only from outside the class it is defined in: +```aspl +class Person{ + + [readpublic] + property string name + + method construct(string name){ + this.name = name + } + +} +``` +Note that the property can still be modified from within the class; it is **not instance bound**, that means that all instances of the class can modify properties that are readpublic to the class, even if those properties are in a different instance. + +## Memory management +ASPL uses a garbage collector to automatically clean up memory after it has been used. +
This means that you don't have to remember manually deallocating memory or checking if it has already been freed. It allows for much faster development times and prevents leaks as well as bugs. + +## Order of evaluation +> [!WARNING] +> The C backend currently does not ensure a strict left-to-right evaluation order for function/method/callback invocations! This is at the moment considered a bug, although the specification of the argument evaluation order may still change in the future. + +Evaluation in ASPL is always left-to-right; while this may seem irrelevant in some situations, it is especially important for boolean operators like `&&` (logical and): + +```aspl +var list? l = get_list_or_null() +if(l != null && l?!.length > 0){ + // ... +} +``` + +In this example, it is crucial that the `l != null` side of the `&&` operator is evaluated before the `l?!.length > 0` side, as `l?!.length` would throw an error if `l` was `null`. + +## Error handling +> [!IMPORTANT] +> The error handling mechanisms described here are still experimental. They may contain bugs and are not fully implemented yet, which is why this feature set is currently hidden behind the `-enableErrorHandling` flag. + +ASPL uses a variation of the result type concept to handle errors with some influence from exception handling. + +Every function (or method) that can fail has to be marked with the `[throws]` attribute: +```aspl +[throws] +function divide(int a, int b) returns int{ + if(b == 0){ + throw new ArithmeticError("Division by zero") + } + return a / b +} +``` +As you can see above, the actual error is thrown using the `throw` keyword, similar to exceptions in other languages. + +The error thrown here is an instance of a class called `ArithmeticError`. A declaration of this class could look like this: +```aspl +[error] +class ArithmeticError{ + + property string message + + method construct(string message){ + this.message = message + } + + method string() returns string{ + return message + } + +} +``` +The `[error]` attribute is used to mark a class as an error, and the `string()` cast method has to be defined to allow the error to be printed by the ASPL runtime. + +### Catch blocks +You can catch errors using `catch` blocks (example 1). + +If you try to catch an error from an expression inside another expression, you either have to specify a fallback value using the `fallback` keyword (example 2) or escape from the `catch` block and simultaneously return from the function the catch block was defined in using the `escape` keyword (example 3); both work similar to `return`, but "return on a different level", which is why they have been assigned separate keywords and using the regular `return` is not allowed inside `catch blocks`. + +```aspl +divide(10, 0) catch error{ + print(error.message) +} +``` +```aspl +print(divide(10, 0) catch error{ + print(error.message) + fallback 0 +}) +``` +```aspl +function reciprocal_squared(int a) returns int{ + var r = divide(1, a) catch error{ + print(error.message) + escape 0 + } + return r * r +} +``` + +### Error Propagation +If you don't want to or cannot handle an error, you can pass it on to the caller: +```aspl +[throws] +function reciprocal(int a) returns int{ + return divide(1, a)! // ! is the error propagation operator +} +``` +As you can see, the `!` operator here is used to propagate the error. + +Note that this requires the caller to also be marked with the `[throws]` attribute. + +### Panics +If an error is so severe that the program cannot continue, you can use the `panic` keyword to stop the execution immediately: +```aspl +function divide(int a, int b) returns int{ + if(b == 0){ + panic("Division by zero") + } + return a / b +} +``` +Functions that can panic only have to be marked with the `[throws]` attribute if they can also throw an error. Recovering from panics is, at least currently, not possible. + +### Defer blocks +If you want to make sure a certain piece of code is executed once a scope is left (via break, return, error propagation or simply after reaching the end of a block), you can use the `defer` statement: +```aspl +function modifyFile(){ + var file = io.open_file("file.txt", "r") + defer { file.close() } + // Modify the file somehow +} +``` + +Note that... +* ...defer blocks will be evaluated in their reverse order of appearance in the code ("reverse order" like opening/closing tags in markdown languages) +* ...when returning a value, the defer blocks are executed after the return value has been evaluated +* ...defer blocks may not use the defer statement in their own body +* ...defer blocks may not use the return statement or throw/propagate any errors +* ...defer blocks may not use the break or continue statements with a level greater than their own loop depth +* ...defer blocks will not be called when the program exits directly using calls to `exit` or `panic` and thus some resources may stay open, but normally the OS will close most of them (e.g. file handles) automatically when the process exits anyway + +## Classes +Classes are the base of object-oriented programming in ASPL. +
They encapsulate data and make it easily accessible as well as passable. +
Classes can have properties and methods. +```aspl +class Hello{ + + property string name + property int number = 5 + + method construct(string name){ + print("An instance has been created") + this.name = name + } + + method hello(){ + print("Hello from " + name) + print("My number is: " + number) + } + +} +``` +You can instantiate a new instance of the `Hello` class using the `new` keyword: +```aspl +var Hello instance = new Hello("Daniel") +instance.hello() +``` +The above program will output: +``` +An instance has been created +Hello from Daniel +My number is: 5 +``` + +For information on inheritance in ASPL, see [inheritance](#inheritance). + +### Static classes +Static classes are classes that cannot be instantiated and can only contain static properties and methods (see [static properties](#static-properties) and [static methods](#static-methods)): +```aspl +[static] +class Example{ + + [static] + property int number = 42 + + [static] + method hello(){ + print("Hello World!") + } + +} +``` +```aspl +Example:number = 5 +Example:hello() // Hello World! +``` + +## Properties +Properties are a class's way of storing data. +
They are similar to variables, but bound to an instance of a class instead of a scope: +```aspl +class Example{ + + property int number = 42 // default value is 42 + +} +``` +```aspl +var Example a = new Example() +var Example b = new Example() +print(a.number) // 42 +print(a.number) // 42 +a.number = -1 +print(a.number) // -1 +print(b.number) // 42 +``` + +### Static properties +Static properties are properties that are not bound to an instance of a class, but to the class itself: +```aspl +class Example{ + + [static] + property int number = 42 + +} +``` +```aspl +print(Example:number) // 42 +Example:number = -1 +print(Example:number) // -1 +``` + +## Methods +Methods are class-bound functions: +```aspl +import math + +class Point{ + + property int x + property int z + + method construct(int x, int z){ + this.x = x + this.z = z + } + + method distanceTo(Point other){ + return math.sqrt(math.pow(double(x - other.x), 2d) + math.pow(double(y - other.y), 2d)) + } + +} +``` +The class `Point` has two methods: + +1. `construct`: This is a special method called the `constructor`, which allows you to initialize an instance with arguments; it is automatically called every time a class is instantiated +2. `distanceTo`: This method calculates the distance between the point it is called on and another point (which is passed to it) using the Pythagorean theorem. + +### Static methods +Similar to static properties, static methods are methods that act on a class itself instead of an instance of that class and thus can only use static properties instead of regular ones: +```aspl +class Example{ + + [static] + property string name = "Daniel" + + [static] + method hello(){ + print("Hello " + name) + } + +} +``` +```aspl +Example:hello() // Hello Daniel +``` + +## Inheritance +Classes can inherit properties and methods from other classes using the `extends` keyword: +```aspl +class Base{ + + property int number = 42 + + method construct(){ + print("Base class constructor") + } + +} +``` +```aspl +class Example extends Base{ +} +``` +``` +var Example e = new Example() // prints "Base class constructor" +print(e.number) // 42 +``` +ASPL uses multiple inheritance instead of interfaces or traits: +```aspl +class Mammal{ + +} +``` +```aspl +class Pet{ + +} +``` +```aspl +class Dog extends Mammal, Pet{ + +} +``` +```aspl +var Dog d = new Dog() +assert d oftype Mammal +assert d oftype Pet +``` +> [!IMPORTANT] +> Multiple inheritance is a tricky concept and generally not very popular amongst other programming languages. While it simplifies a lot of things and can make intuitive sense sometimes, there are also good reasons not to use it. Because of this, ASPL might switch to single inheritance + interfaces/traits in the future, although this is currently not planned. + +## Parent Method Calls +You can explicitly invoke the implementation of a method in certain parent classes even if the method is overridden in the current class using the `parent` keyword; you might also know this concept from other languages as `super`. +```aspl +class Base{ + + [public] + method hello(){ + print("Hello from Base") + } + +} +``` +```aspl +class Example extends Base{ + + [public] + method hello(){ + print("Hello from Example") + parent(Base).hello() + } + +} +``` +```aspl +var Example e = new Example() +e.hello() +``` +The above program will output: +``` +Hello from Example +Hello from Base +``` + +## Enums +Enums are a way to define a list of constants. +
They are defined using the `enum` keyword: +```aspl +enum Color{ + Red, + Green, + Blue +} +``` +The above code defines an enum called `Color` with three so-called enum fields: +```aspl +Color.Red +Color.Green +Color.Blue +``` +The values of the enum fields are assigned automatically, starting at **0**. +
You can also specify custom values though: +```aspl +enum Color{ + Red = 1, + Green = 2, + Blue = 3 +} +``` + +### Bitfields/Flags +There is a `[flags]` attribute that allows for creating bitflags. +
Multiple enum fields can be merged with the `|` operator in these enums. +
This is widely used for passing multiple options as one value. + +You can check if an enum flag contains a field like this: +```aspl +var Color c = Color.Red || Color.Green +print((c && Color.Red) == Color.Red) // true - the enum flag contains the Red field +``` + +**Limitations:** +* No field value overriding +* Only up to 32 enum fields + +## Compiler options +### Cross-compilation +One of ASPL's main goals has always been seamless cross-compilation and support for as many platforms as possible. In fact, you can simply cross-compile your ASPL code to any other platform using the `-os` and `-arch` flags: +```bash +aspl -os windows -arch amd64 hello.aspl +``` +The above command will compile `hello.aspl` to a 64-bit Windows executable. + +Such a sophisticated cross-compilation toolchain is only possible due to ASPL's unique architecture based on Zig CC (when using the C backend) and a mechanism called "templating" (when using the AIL backend; see the `templates` folder for more information). + +Additionally, ASPL uses dynamic loading of system graphics libraries (see the `thirdparty/ssl` folder for more information) to enable cross-compilation of graphical applications; in theory, this can even work on macOS! In practice, this has not been properly implemented yet and might suffer from licensing issues. +
This option is currently hidden behind the `-ssl` flag, but will probably soon be on by default. + +> [!NOTE] +> Cross-compiling to macOS using the C backend is currently not possible due to issues with the garbage collector. This will hopefully be fixed soon. + +### Debug vs. production builds +You can compile your code in production mode using the `-prod` flag: +```bash +aspl -prod hello.aspl +``` +This will disable all debug features and use optimizations to make your code run faster. Furthermore, the `production` conditional compilation symbol will be defined instead of `debug`. + +Usually, you will want to compile your code in debug mode when developing and in production mode when releasing your application. + +### Conditional compilation +Like in many other compiled languages, you can make parts of your code only compile in certain situations using the `$if` and `$else` keywords, for example: +```aspl +$if debug{ + print("Debug build") +}$else{ + print("Production build") +} +``` +The above code will print `Debug build` when compiling in debug mode and `Production build` when compiling in production mode (using the `-prod` compiler option). + +In addition to the `debug` and `production` symbols, there are also the following symbols: +* `windows`/`linux`/`macos`/`android`/`ios`/... - the target operating system +* `amd64`/`arm64`/`arm32`/`rv64`/`rv32`/`i386`/... - the target architecture +* `x32`/`x64` - the bitness of the target architecture (32-bit or 64-bit) +* `console`/`gui` - the target application type (console or GUI); this is only relevant for Windows +* `main` - whether the current file is part of the main module + +You can also define your own symbols using the `-d` compiler option: +```bash +aspl -d mySymbol hello.aspl +``` +```aspl +$if mySymbol{ + print("mySymbol is defined") +} +``` + +### Choosing a backend +ASPL currently supports two different backends: `ail` and `c`. + +The `ail` backend compiles your code to a **bytecode format called `AIL`** (formerly an acronym for "ASPL Intermediate Language") and uses [templating](templates) to bundle the AIL bytecode together with an AIL interpreter into a single executable. + +Benefits of the `ail` backend: +* Faster compilation +* Better debugging experience +* Not dependent on a C compiler +* Less bug prone +* Easier to develop for the ASPL team + +On the other hand, the `c` backend compiles your code to **C code** and then uses a C compiler to compile it to an executable. + +Benefits of the `c` backend: +* Faster execution +* Smaller executable size (sometimes) +* Even better cross-compilation +* Easier C interoperability +* Does not require prebuilt templates + +You can choose a backend using the `-backend` compiler option: +```bash +aspl -backend c hello.aspl +``` + +It's generally advised to use the `ail` backend for development/debug builds and the `c` backend for production; note that the `ail` backend is currently the default for all builds. + +The different backends are designed to be **as compatible as possible**, so you can easily switch between them, optimally without changing anything in your code (as long as you don't use C interoperability). + +### Stack vs. heaped based builds +> [!NOTE] +> This section only applies to the C backend. + +> [!WARNING] +> This is a rather sophisticated optimization technique; while it may speed up your program significantially, it's a complicated matter and might cause issues in some cases (although that's a bug and should be reported). + +ASPL allocates wrapper C-objects for all ASPL objects created in your program; i.e. if you write `"Hello World"`, it will be translated to something like `ASPL_STRING_LITERAL("Hello World")` (where `ASPL_STRING_LITERAL` is a helper function allocating the actual wrapper object). + +These objects can either be allocated on the stack or on the heap, both having their own advantages and disadvantages. Since ASPL is permanently using a GC and the C backend has been carefully designed to support both of these options, you can easily toggle between the different allocation methods. + +By default, wrapper objects are allocated on the stack. You can change this using the `-heapBased` compiler flag. + +Various performance tests have shown that neither of these methods is per se faster than the other one, so carefully profile your program first and test if switching the allocation method is really worth it. + +> [!TIP] +> Some very large programs might experience stack-overflow issues when allocating on the stack; passing `-heapBased` can potentially fix this. Alternatively, you can also try increasing the stack size using the `-stacksize` compiler option (although this is not supported on all platforms). + +## Resource embedding +You can embed resources (such as images, audio files, etc.) directly into your executable using the `$embed` compile-time function: +```aspl +var image = $embed("image.png") +``` +In the above example, the `image.png` file will be embedded into the executable and the `image` variable will contain a `list` containing the raw image data. +You can for example then load this image with the graphics module: +```aspl +import graphics + +var image = Canvas:fromFileData($embed("image.png")) +img.save("embedded.png") +``` + +## Debugging +### Breakpoints +> [!NOTE] +> This feature is currently only supported in the AIL backend. + +ASPL has a built-in debugger that allows you to step through your code and inspect variables, stack traces, etc. + +Just import the `breakpoints` module and use the `break` function to set a breakpoint: +```aspl +import breakpoints + +var x = ["a", "b", "c"] +breakpoints.break() +print(x.length) +``` + +You can also pass a message to the `break` function to make it easier to identify the breakpoint, as it will be printed to the console when the breakpoint is reached: +```aspl +import breakpoints + +var x = ["a", "b", "c"] +breakpoints.break("x is initialized") +print(x.length) +``` + +As soon as the program reaches the breakpoint, the debugger will be started, and you can use several commands such as `stack`, `scope`, `continue` or `quit` to inspect the current state of the program. +
For more commands, see the `help` command. + +## Android & iOS +Deploying apps to mobile operating systems has traditionally been a horrible experience. Not only because of bad tooling, but also because of the lack of cross-platform GUI toolkits in many programming languages. +
Since ASPL's `graphics` module is designed to be platform-agnostic and non-native, most apps written for desktop operating systems will also work on phones, tablets, etc. with little to no adjustments. + +ASPL apps can easily be deployed to Android using [ASAPP](https://github.com/aspl-lang/asapp). + +iOS support is also planned, but currently not one of the main priorities. \ No newline at end of file diff --git a/runtime/ailinterpreter/README.md b/runtime/ailinterpreter/README.md new file mode 100644 index 0000000..6814095 --- /dev/null +++ b/runtime/ailinterpreter/README.md @@ -0,0 +1,10 @@ +# AIL Bytecode Interpreter +This is a simple AIL bytecode interpreter, implemented in C. + +It partially uses the C backend template as its runtime library. + +TODO: Clean & speed up the code. + +## Dependencies +* `stdlib/backend/stringcode/c/template.c` as well as all of its dependencies +* `thirdparty/appbundle` (only when building the interpreter as a standalone application, i.e. `-DASPL_AILI_BUNDLED`) \ No newline at end of file diff --git a/runtime/ailinterpreter/builtins.c b/runtime/ailinterpreter/builtins.c new file mode 100644 index 0000000..0ffb2b7 --- /dev/null +++ b/runtime/ailinterpreter/builtins.c @@ -0,0 +1,221 @@ +#include "interpreter.h" + +void aspl_ailinterpreter_print(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, char* identifier, ASPL_AILI_ArgumentList arguments) { + ASPL_OBJECT_TYPE object = arguments.arguments[0]; + ASPL_OBJECT_TYPE newLine = arguments.arguments[1]; + aspl_function_print(C_REFERENCE(object), C_REFERENCE(newLine)); + aspl_ailinterpreter_return(context, bytes, ASPL_NULL()); +} + +void aspl_ailinterpreter_key(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, char* identifier, ASPL_AILI_ArgumentList arguments) { + ASPL_OBJECT_TYPE prompt = arguments.arguments[0]; + // TODO: Implement key when it's implemented in the C backend +} + +void aspl_ailinterpreter_input(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, char* identifier, ASPL_AILI_ArgumentList arguments) { + ASPL_OBJECT_TYPE prompt = arguments.arguments[0]; + aspl_ailinterpreter_return(context, bytes, aspl_function_input(C_REFERENCE(prompt))); +} + +void aspl_ailinterpreter_exit(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, char* identifier, ASPL_AILI_ArgumentList arguments) { + ASPL_OBJECT_TYPE code = arguments.arguments[0]; + aspl_function_exit(C_REFERENCE(code)); +} + +void aspl_ailinterpreter_range(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, char* identifier, ASPL_AILI_ArgumentList arguments) { + ASPL_OBJECT_TYPE start = arguments.arguments[0]; + ASPL_OBJECT_TYPE end = arguments.arguments[1]; + // TODO: Implement range when it's implemented in the C backend +} + +void aspl_ailinterpreter_panic(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, char* identifier, ASPL_AILI_ArgumentList arguments) { + ASPL_OBJECT_TYPE object = arguments.arguments[0]; + ASPL_OBJECT_TYPE newLine = arguments.arguments[1]; + aspl_function_panic(C_REFERENCE(object)); + aspl_ailinterpreter_return(context, bytes, ASPL_NULL()); +} + +void aspl_ailinterpreter_initialize_builtin_functions(ASPL_AILI_EnvironmentContext* context) { + hashmap_str_to_voidptr_hashmap_set(context->functions, "_print", aspl_ailinterpreter_print); + ASPL_AILI_BuiltinFunction* print = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinFunction)); + print->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 2), .size = 2 }; + print->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "object", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "any" }, .size = 1 }, .optional = 0 }; + print->parameters.parameters[1] = (ASPL_AILI_Parameter){ .name = "newLine", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "bool" }, .size = 1 }, .optional = 1, .default_value = ASPL_TRUE() }; + hashmap_str_to_voidptr_hashmap_set(context->builtin_functions, "_print", print); + + hashmap_str_to_voidptr_hashmap_set(context->functions, "_key", aspl_ailinterpreter_key); + ASPL_AILI_BuiltinFunction* key = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinFunction)); + key->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 }; + key->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "prompt", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 1, .default_value = ASPL_STRING_LITERAL("") }; + hashmap_str_to_voidptr_hashmap_set(context->builtin_functions, "_key", key); + + hashmap_str_to_voidptr_hashmap_set(context->functions, "_input", aspl_ailinterpreter_input); + ASPL_AILI_BuiltinFunction* input = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinFunction)); + input->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 }; + input->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "prompt", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 1, .default_value = ASPL_STRING_LITERAL("") }; + hashmap_str_to_voidptr_hashmap_set(context->builtin_functions, "_input", input); + + hashmap_str_to_voidptr_hashmap_set(context->functions, "_exit", aspl_ailinterpreter_exit); + ASPL_AILI_BuiltinFunction* exit = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinFunction)); + exit->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 }; + exit->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "code", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "int" }, .size = 1 }, .optional = 1, .default_value = ASPL_INT_LITERAL(0) }; + hashmap_str_to_voidptr_hashmap_set(context->builtin_functions, "_exit", exit); + + hashmap_str_to_voidptr_hashmap_set(context->functions, "_range", aspl_ailinterpreter_range); + ASPL_AILI_BuiltinFunction* range = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinFunction)); + range->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 2), .size = 2 }; + range->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "start", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "byte", "int", "long", "float", "double" }, .size = 5 }, .optional = 0 }; + range->parameters.parameters[1] = (ASPL_AILI_Parameter){ .name = "end", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "byte", "int", "long", "float", "double" }, .size = 5 }, .optional = 0 }; + hashmap_str_to_voidptr_hashmap_set(context->builtin_functions, "_range", range); + + hashmap_str_to_voidptr_hashmap_set(context->functions, "_panic", aspl_ailinterpreter_panic); + ASPL_AILI_BuiltinFunction* panic = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinFunction)); + panic->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 2), .size = 2 }; + panic->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "object", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "any" }, .size = 1 }, .optional = 0 }; + panic->parameters.parameters[1] = (ASPL_AILI_Parameter){ .name = "newLine", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "bool" }, .size = 1 }, .optional = 1, .default_value = ASPL_TRUE() }; + hashmap_str_to_voidptr_hashmap_set(context->builtin_functions, "_panic", panic); +} + +void aspl_ailinterpreter_initialize_builtin_methods(ASPL_AILI_EnvironmentContext* context) { + hashmap_str_to_voidptr_HashMap* any_methods = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 2 }); + + ASPL_AILI_BuiltinMethod* any_cloneShallow = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + any_cloneShallow->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 0), .size = 0 }; + hashmap_str_to_voidptr_hashmap_set(any_methods, "cloneShallow", any_cloneShallow); + + ASPL_AILI_BuiltinMethod* any_cloneDeep = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + any_cloneDeep->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 0), .size = 0 }; + hashmap_str_to_voidptr_hashmap_set(any_methods, "cloneDeep", any_cloneDeep); + + hashmap_str_to_voidptr_hashmap_set(context->builtin_methods, "any", any_methods); + + hashmap_str_to_voidptr_HashMap* string_methods = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 13 }); + + ASPL_AILI_BuiltinMethod* string_toLower = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + string_toLower->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 0), .size = 0 }; + hashmap_str_to_voidptr_hashmap_set(string_methods, "toLower", string_toLower); + + ASPL_AILI_BuiltinMethod* string_toUpper = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + string_toUpper->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 0), .size = 0 }; + hashmap_str_to_voidptr_hashmap_set(string_methods, "toUpper", string_toUpper); + + ASPL_AILI_BuiltinMethod* string_replace = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + string_replace->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 2), .size = 2 }; + string_replace->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "search", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 0 }; + string_replace->parameters.parameters[1] = (ASPL_AILI_Parameter){ .name = "replace", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 0 }; + hashmap_str_to_voidptr_hashmap_set(string_methods, "replace", string_replace); + + ASPL_AILI_BuiltinMethod* string_startsWith = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + string_startsWith->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 }; + string_startsWith->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "substring", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 0 }; + hashmap_str_to_voidptr_hashmap_set(string_methods, "startsWith", string_startsWith); + + ASPL_AILI_BuiltinMethod* string_endsWith = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + string_endsWith->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 }; + string_endsWith->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "substring", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 0 }; + hashmap_str_to_voidptr_hashmap_set(string_methods, "endsWith", string_endsWith); + + ASPL_AILI_BuiltinMethod* string_contains = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + string_contains->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 }; + string_contains->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "substring", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 0 }; + hashmap_str_to_voidptr_hashmap_set(string_methods, "contains", string_contains); + + ASPL_AILI_BuiltinMethod* string_after = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + string_after->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 }; + string_after->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "position", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "int" }, .size = 1 }, .optional = 0 }; + hashmap_str_to_voidptr_hashmap_set(string_methods, "after", string_after); + + ASPL_AILI_BuiltinMethod* string_before = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + string_before->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 }; + string_before->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "position", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "int" }, .size = 1 }, .optional = 0 }; + hashmap_str_to_voidptr_hashmap_set(string_methods, "before", string_before); + + ASPL_AILI_BuiltinMethod* string_trim = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + string_trim->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 }; + string_trim->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "chars", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 1, .default_value = ASPL_STRING_LITERAL(" \n\t\v\f\r") }; + hashmap_str_to_voidptr_hashmap_set(string_methods, "trim", string_trim); + + ASPL_AILI_BuiltinMethod* string_trimStart = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + string_trimStart->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 0), .size = 0 }; + hashmap_str_to_voidptr_hashmap_set(string_methods, "trimStart", string_trimStart); + + ASPL_AILI_BuiltinMethod* string_trimEnd = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + string_trimEnd->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 0), .size = 0 }; + hashmap_str_to_voidptr_hashmap_set(string_methods, "trimEnd", string_trimEnd); + + ASPL_AILI_BuiltinMethod* string_split = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + string_split->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 2), .size = 2 }; + string_split->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "delimiter", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 0 }; + string_split->parameters.parameters[1] = (ASPL_AILI_Parameter){ .name = "maxParts", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "int" }, .size = 1 }, .optional = 1, .default_value = ASPL_INT_LITERAL(-1) }; + hashmap_str_to_voidptr_hashmap_set(string_methods, "split", string_split); + + ASPL_AILI_BuiltinMethod* string_reverse = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + string_reverse->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 0), .size = 0 }; + hashmap_str_to_voidptr_hashmap_set(string_methods, "reverse", string_reverse); + hashmap_str_to_voidptr_hashmap_set(context->builtin_methods, "string", string_methods); + + hashmap_str_to_voidptr_hashmap_set(context->builtin_methods, "string", string_methods); + + hashmap_str_to_voidptr_HashMap* list_methods = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 8 }); + + ASPL_AILI_BuiltinMethod* list_contains = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + list_contains->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 }; + list_contains->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "element", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "T" }, .size = 1 }, .optional = 0 }; + hashmap_str_to_voidptr_hashmap_set(list_methods, "contains", list_contains); + + ASPL_AILI_BuiltinMethod* list_add = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + list_add->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 }; + list_add->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "element", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "T" }, .size = 1 }, .optional = 0 }; + hashmap_str_to_voidptr_hashmap_set(list_methods, "add", list_add); + + ASPL_AILI_BuiltinMethod* list_insert = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + list_insert->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 2), .size = 2 }; + list_insert->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "index", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "int" }, .size = 1 }, .optional = 0 }; + list_insert->parameters.parameters[1] = (ASPL_AILI_Parameter){ .name = "element", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "T" }, .size = 1 }, .optional = 0 }; + hashmap_str_to_voidptr_hashmap_set(list_methods, "insert", list_insert); + + ASPL_AILI_BuiltinMethod* list_insertElements = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + list_insertElements->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 2), .size = 2 }; + list_insertElements->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "index", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "int" }, .size = 1 }, .optional = 0 }; + list_insertElements->parameters.parameters[1] = (ASPL_AILI_Parameter){ .name = "elements", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "list" }, .size = 1 }, .optional = 0 }; + hashmap_str_to_voidptr_hashmap_set(list_methods, "insertElements", list_insertElements); + + ASPL_AILI_BuiltinMethod* list_remove = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + list_remove->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 }; + list_remove->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "element", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "T" }, .size = 1 }, .optional = 0 }; + hashmap_str_to_voidptr_hashmap_set(list_methods, "remove", list_remove); + + ASPL_AILI_BuiltinMethod* list_removeAt = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + list_removeAt->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 }; + list_removeAt->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "index", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "int" }, .size = 1 }, .optional = 0 }; + hashmap_str_to_voidptr_hashmap_set(list_methods, "removeAt", list_removeAt); + + ASPL_AILI_BuiltinMethod* list_clear = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + list_clear->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 0), .size = 0 }; + hashmap_str_to_voidptr_hashmap_set(list_methods, "clear", list_clear); + + ASPL_AILI_BuiltinMethod* list_join = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + list_join->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 }; + list_join->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "delimiter", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 0 }; + hashmap_str_to_voidptr_hashmap_set(list_methods, "join", list_join); + + hashmap_str_to_voidptr_hashmap_set(context->builtin_methods, "list", list_methods); + + hashmap_str_to_voidptr_HashMap* map_methods = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 3 }); + + ASPL_AILI_BuiltinMethod* map_containsKey = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + map_containsKey->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 }; + map_containsKey->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "key", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "T" }, .size = 1 }, .optional = 0 }; + hashmap_str_to_voidptr_hashmap_set(map_methods, "containsKey", map_containsKey); + + ASPL_AILI_BuiltinMethod* map_remove = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + map_remove->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 }; + map_remove->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "key", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "T" }, .size = 1 }, .optional = 0 }; + hashmap_str_to_voidptr_hashmap_set(map_methods, "remove", map_remove); + + ASPL_AILI_BuiltinMethod* map_clear = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod)); + map_clear->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 0), .size = 0 }; + hashmap_str_to_voidptr_hashmap_set(map_methods, "clear", map_clear); + + hashmap_str_to_voidptr_hashmap_set(context->builtin_methods, "map", map_methods); +} \ No newline at end of file diff --git a/runtime/ailinterpreter/byte_list.c b/runtime/ailinterpreter/byte_list.c new file mode 100644 index 0000000..d06b86e --- /dev/null +++ b/runtime/ailinterpreter/byte_list.c @@ -0,0 +1,94 @@ +#include + +#include "byte_list.h" + +ASPL_AILI_ByteList* aspl_ailinterpreter_bytelist_clone(ASPL_AILI_ByteList* old) { + ASPL_AILI_ByteList* new = ASPL_MALLOC(sizeof(ASPL_AILI_ByteList)); + *new = *old; + return new; +} + +unsigned char aspl_ailinterpreter_read_byte(ASPL_AILI_ByteList* bytes) { + return bytes->data[bytes->position++]; +} + +unsigned char aspl_ailinterpreter_peek_byte(ASPL_AILI_ByteList* bytes) { + return bytes->data[bytes->position]; +} + +ASPL_AILI_Instruction aspl_ailinterpreter_fetch_instruction(ASPL_AILI_ByteList* bytes) { + return aspl_ailinterpreter_read_byte(bytes); +} + +ASPL_AILI_Instruction aspl_ailinterpreter_peek_instruction(ASPL_AILI_ByteList* bytes) { + return aspl_ailinterpreter_peek_byte(bytes); +} + +unsigned char aspl_ailinterpreter_read_bool(ASPL_AILI_ByteList* bytes) { + return bytes->data[bytes->position++]; +} + +short aspl_ailinterpreter_read_short(ASPL_AILI_ByteList* bytes) { + short result = 0; + for (int i = 0; i < 2; i++) { + result |= (unsigned short)aspl_ailinterpreter_read_byte(bytes) << (8 * i); + } + return result; +} + +int aspl_ailinterpreter_read_int(ASPL_AILI_ByteList* bytes) { + int result = 0; + for (int i = 0; i < 4; i++) { + result |= (unsigned int)aspl_ailinterpreter_read_byte(bytes) << (8 * i); + } + return result; +} + +long long aspl_ailinterpreter_read_long(ASPL_AILI_ByteList* bytes) { + long long result = 0; + for (int i = 0; i < 8; i++) { + unsigned char byte = aspl_ailinterpreter_read_byte(bytes); + result |= ((unsigned long long)byte) << (8 * i); + } + return result; +} + +float aspl_ailinterpreter_read_float(ASPL_AILI_ByteList* bytes) { + uint32_t integerResult = 0; + for (int i = 0; i < 4; i++) { + integerResult |= ((uint32_t)aspl_ailinterpreter_read_byte(bytes)) << (8 * i); + } + float result; + memcpy(&result, &integerResult, sizeof(float)); // use memcpy to avoid endianess issues + return result; +} + +double aspl_ailinterpreter_read_double(ASPL_AILI_ByteList* bytes) { + uint64_t integerResult = 0; + for (int i = 0; i < 8; i++) { + integerResult |= ((uint64_t)aspl_ailinterpreter_read_byte(bytes)) << (8 * i); + } + double result; + memcpy(&result, &integerResult, sizeof(double)); // use memcpy to avoid endianess issues + return result; +} + +char* aspl_ailinterpreter_read_short_string(ASPL_AILI_ByteList* bytes) { + short length = aspl_ailinterpreter_read_short(bytes); + char* result = ASPL_MALLOC(sizeof(char) * (length + 1)); + for (int i = 0; i < length; i++) { + result[i] = aspl_ailinterpreter_read_byte(bytes); + } + result[length] = '\0'; + return result; +} + +char* aspl_ailinterpreter_read_long_string(ASPL_AILI_ByteList* bytes) { + long long length = aspl_ailinterpreter_read_long(bytes); + char* result = ASPL_MALLOC(sizeof(char) * (length + 1)); + for (int i = 0; i < length; i++) { + result[i] = aspl_ailinterpreter_read_byte(bytes); + } + result[length] = '\0'; + return result; +} \ No newline at end of file diff --git a/runtime/ailinterpreter/byte_list.h b/runtime/ailinterpreter/byte_list.h new file mode 100644 index 0000000..b161324 --- /dev/null +++ b/runtime/ailinterpreter/byte_list.h @@ -0,0 +1,38 @@ +#ifndef ASPL_AILI_BYTE_LIST_H_INCLUDED +#define ASPL_AILI_BYTE_LIST_H_INCLUDED + +#include "instructions.h" + +typedef struct ASPL_AILI_ByteList { + long long size; + long long position; + unsigned char* data; +} ASPL_AILI_ByteList; + +ASPL_AILI_ByteList* aspl_ailinterpreter_bytelist_clone(ASPL_AILI_ByteList* old); + +unsigned char aspl_ailinterpreter_read_byte(ASPL_AILI_ByteList* bytes); + +unsigned char aspl_ailinterpreter_peek_byte(ASPL_AILI_ByteList* bytes); + +ASPL_AILI_Instruction aspl_ailinterpreter_fetch_instruction(ASPL_AILI_ByteList* bytes); + +ASPL_AILI_Instruction aspl_ailinterpreter_peek_instruction(ASPL_AILI_ByteList* bytes); + +unsigned char aspl_ailinterpreter_read_bool(ASPL_AILI_ByteList* bytes); + +short aspl_ailinterpreter_read_short(ASPL_AILI_ByteList* bytes); + +int aspl_ailinterpreter_read_int(ASPL_AILI_ByteList* bytes); + +long long aspl_ailinterpreter_read_long(ASPL_AILI_ByteList* bytes); + +float aspl_ailinterpreter_read_float(ASPL_AILI_ByteList* bytes); + +double aspl_ailinterpreter_read_double(ASPL_AILI_ByteList* bytes); + +char* aspl_ailinterpreter_read_short_string(ASPL_AILI_ByteList* bytes); + +char* aspl_ailinterpreter_read_long_string(ASPL_AILI_ByteList* bytes); + +#endif \ No newline at end of file diff --git a/runtime/ailinterpreter/implementations.c b/runtime/ailinterpreter/implementations.c new file mode 100644 index 0000000..2e4db8f --- /dev/null +++ b/runtime/ailinterpreter/implementations.c @@ -0,0 +1,35 @@ +#ifndef ASPL_AILI_NO_STDLIB +#ifndef ASPL_AILI_BUNDLED // include all default stdlib implementations when manually invoking the interpreter +// TODO: This won't work as the implementation calls need certain wrappers +#endif +#endif + +#ifdef _WIN32 +#include +#define aspl_ailinterpreter_dl_self NULL +#define aspl_ailinterpreter_dl_sym(handle, symbol) GetProcAddress(handle, symbol) +#else +#include +#define aspl_ailinterpreter_dl_self RTLD_DEFAULT // TODO: This might not be available on all platforms +#define aspl_ailinterpreter_dl_sym(handle, symbol) dlsym(handle, symbol) +#endif + +typedef ASPL_OBJECT_TYPE(*ASPL_AILI_ImplementationPtr)(ASPL_AILI_ArgumentList arguments); + +ASPL_OBJECT_TYPE aspl_ailinterpreter_implement(ASPL_AILI_ThreadContext* thread_context, char* call, ASPL_AILI_ArgumentList args) { + char* symbol = ASPL_MALLOC(strlen("aspl_ailinterpreter_implementation_") + strlen(call) + 1); + strcat(symbol, "aspl_ailinterpreter_implementation_"); + int iBeforeCall = strlen(symbol); + strcat(symbol, call); + for (int i = iBeforeCall; i < strlen(symbol); i++) { + if (symbol[i] == '.') { + symbol[i] = '$'; + } + } + symbol[strlen("aspl_ailinterpreter_implementation_") + strlen(call)] = '\0'; + ASPL_AILI_ImplementationPtr cb = (ASPL_AILI_ImplementationPtr)aspl_ailinterpreter_dl_sym(aspl_ailinterpreter_dl_self, symbol); + if (cb == NULL) { + ASPL_PANIC("Cannot implement unknown implementation call %s", call); + } + return cb(args); +} \ No newline at end of file diff --git a/runtime/ailinterpreter/instructions.h b/runtime/ailinterpreter/instructions.h new file mode 100644 index 0000000..feeed75 --- /dev/null +++ b/runtime/ailinterpreter/instructions.h @@ -0,0 +1,72 @@ +#ifndef ASPL_AILI_INSTRUCTION_H_INCLUDED +#define ASPL_AILI_INSTRUCTION_H_INCLUDED + +typedef enum ASPL_AILI_Instruction { + ASPL_AILI_INSTRUCTION_MANIFEST, + ASPL_AILI_INSTRUCTION_CREATE_OBJECT, + ASPL_AILI_INSTRUCTION_BYTE_ARRAY_LITERAL, + ASPL_AILI_INSTRUCTION_IMPLEMENT, + ASPL_AILI_INSTRUCTION_JUMP_RELATIVE, + ASPL_AILI_INSTRUCTION_JUMP_RELATIVE_IF_FALSE, + ASPL_AILI_INSTRUCTION_DECLARE_FUNCTION, + ASPL_AILI_INSTRUCTION_CALL_FUNCTION, + ASPL_AILI_INSTRUCTION_ADD, + ASPL_AILI_INSTRUCTION_INCREMENT, + ASPL_AILI_INSTRUCTION_SUBTRACT, + ASPL_AILI_INSTRUCTION_DECREMENT, + ASPL_AILI_INSTRUCTION_MULTIPLY, + ASPL_AILI_INSTRUCTION_DIVIDE, + ASPL_AILI_INSTRUCTION_MODULO, + ASPL_AILI_INSTRUCTION_SMALLER_THAN, + ASPL_AILI_INSTRUCTION_SMALLER_EQUAL, + ASPL_AILI_INSTRUCTION_GREATER_THAN, + ASPL_AILI_INSTRUCTION_GREATER_EQUAL, + ASPL_AILI_INSTRUCTION_EQUALS, + ASPL_AILI_INSTRUCTION_NOT_EQUALS, + ASPL_AILI_INSTRUCTION_BOOLEAN_AND, + ASPL_AILI_INSTRUCTION_BITWISE_AND, + ASPL_AILI_INSTRUCTION_BOOLEAN_OR, + ASPL_AILI_INSTRUCTION_BITWISE_OR, + ASPL_AILI_INSTRUCTION_BOOLEAN_XOR, + ASPL_AILI_INSTRUCTION_BITWISE_XOR, + ASPL_AILI_INSTRUCTION_DECLARE_VARIABLE, + ASPL_AILI_INSTRUCTION_ACCESS_VARIABLE, + ASPL_AILI_INSTRUCTION_UPDATE_VARIABLE, + ASPL_AILI_INSTRUCTION_DECLARE_METHOD, + ASPL_AILI_INSTRUCTION_CALL_METHOD, + ASPL_AILI_INSTRUCTION_CALL_EXACT_METHOD, + ASPL_AILI_INSTRUCTION_BREAK, + ASPL_AILI_INSTRUCTION_CONTINUE, + ASPL_AILI_INSTRUCTION_RETURN, + ASPL_AILI_INSTRUCTION_FALLBACK, + ASPL_AILI_INSTRUCTION_ESCAPE, + ASPL_AILI_INSTRUCTION_NEGATE, + ASPL_AILI_INSTRUCTION_COUNT_COLLECTION, + ASPL_AILI_INSTRUCTION_ACCESS_COLLECTION_KEY_WITH_INDEX, + ASPL_AILI_INSTRUCTION_ACCESS_COLLECTION_VALUE_WITH_INDEX, + ASPL_AILI_INSTRUCTION_INDEX_COLLECTION, + ASPL_AILI_INSTRUCTION_UPDATE_COLLECTION, + ASPL_AILI_INSTRUCTION_DECLARE_CALLBACK, + ASPL_AILI_INSTRUCTION_DECLARE_CLASS, + ASPL_AILI_INSTRUCTION_NEW, + ASPL_AILI_INSTRUCTION_THIS, + ASPL_AILI_INSTRUCTION_DECLARE_PROPERTY, + ASPL_AILI_INSTRUCTION_ACCESS_PROPERTY, + ASPL_AILI_INSTRUCTION_UPDATE_PROPERTY, + ASPL_AILI_INSTRUCTION_DECLARE_ENUM, + ASPL_AILI_INSTRUCTION_ENUM_FIELD, + ASPL_AILI_INSTRUCTION_DECLARE_POINTER, + ASPL_AILI_INSTRUCTION_DEREFERENCE_POINTER, + ASPL_AILI_INSTRUCTION_CAST, + ASPL_AILI_INSTRUCTION_ASSERT, + ASPL_AILI_INSTRUCTION_OFTYPE, + ASPL_AILI_INSTRUCTION_SPECIFY_LOOP, + ASPL_AILI_INSTRUCTION_POP_LOOP, + ASPL_AILI_INSTRUCTION_THROW, + ASPL_AILI_INSTRUCTION_CATCH, + ASPL_AILI_INSTRUCTION_PROPAGATE_ERROR, + ASPL_AILI_INSTRUCTION_POP, + ASPL_AILI_INSTRUCTION_END, +} ASPL_AILI_Instruction; + +#endif \ No newline at end of file diff --git a/runtime/ailinterpreter/interpreter.c b/runtime/ailinterpreter/interpreter.c new file mode 100644 index 0000000..c588bf8 --- /dev/null +++ b/runtime/ailinterpreter/interpreter.c @@ -0,0 +1,1779 @@ +#include "interpreter.h" + +#include "byte_list.c" +#include "builtins.c" +#include "implementations.c" + +ASPL_AILI_EnvironmentContext* aspl_ailinterpreter_new_environment_context() { + ASPL_AILI_EnvironmentContext* environment_context = ASPL_MALLOC(sizeof(ASPL_AILI_EnvironmentContext)); + + environment_context->functions = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 64 }); + environment_context->builtin_functions = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 8 }); + aspl_ailinterpreter_initialize_builtin_functions(environment_context); + environment_context->custom_functions = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 64 }); + + environment_context->builtin_methods = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 8 }); + aspl_ailinterpreter_initialize_builtin_methods(environment_context); + environment_context->custom_methods = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 64 }); + + environment_context->classes = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 64 }); + environment_context->enums = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 64 }); + + return environment_context; +} + +ASPL_AILI_ThreadContext* aspl_ailinterpreter_new_thread_context(ASPL_AILI_EnvironmentContext* environment_context) { + ASPL_AILI_ThreadContext* thread_context = ASPL_MALLOC(sizeof(ASPL_AILI_ThreadContext)); + thread_context->environment_context = environment_context; + + thread_context->stack = ASPL_MALLOC(sizeof(ASPL_AILI_Stack)); + thread_context->stack->frames = ASPL_MALLOC(sizeof(ASPL_AILI_StackFrame) * 1024); + thread_context->stack->frames_size = 1024; + thread_context->stack->current_frame_index = -1; // push_frame will increment this to 0 + aspl_ailinterpreter_stack_push_frame(thread_context->stack); + + thread_context->callable_invocation_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_CallableInvocationMetaStack)); + thread_context->callable_invocation_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_CallableInvocationMeta) * 1024); + thread_context->callable_invocation_meta_stack->top = 0; + + thread_context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack)); + thread_context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024); + thread_context->loop_meta_stack->top = 0; + + thread_context->class_stack = ASPL_MALLOC(sizeof(ASPL_AILI_ClassStack)); + thread_context->class_stack->data = ASPL_MALLOC(sizeof(char*) * 8); + thread_context->class_stack->top = 0; + + thread_context->scopes_top = -1; // push_scope will increment this to 0 + thread_context->scopes_size = 128; + thread_context->scopes = ASPL_MALLOC(sizeof(hashmap_str_to_voidptr_HashMap*) * thread_context->scopes_size); + aspl_ailinterpreter_push_scope(thread_context); + + thread_context->current_instance = ASPL_NULL(); + + thread_context->local_static_property_values = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 64 }); + for (int i = 0; i < environment_context->classes->len; i++) { + char* class_name = environment_context->classes->pairs[i]->key; + hashmap_str_to_voidptr_hashmap_set(thread_context->local_static_property_values, class_name, hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 64 })); + ASPL_AILI_Class* class = environment_context->classes->pairs[i]->value; + for (int j = 0; j < class->default_threadlocal_static_property_values->len; j++) { + char* property_name = class->default_threadlocal_static_property_values->pairs[j]->key; + ASPL_OBJECT_TYPE* property_value = class->default_threadlocal_static_property_values->pairs[j]->value; + hashmap_str_to_voidptr_hashmap_set(hashmap_str_to_voidptr_hashmap_get_value(thread_context->local_static_property_values, class_name), property_name, property_value); + } + } + + thread_context->stack_trace = ASPL_MALLOC(sizeof(ASPL_AILI_StackTrace)); + thread_context->stack_trace->frames = ASPL_MALLOC(sizeof(char*) * 1024); + thread_context->stack_trace->size = 1024; + thread_context->stack_trace->top = 0; + + return thread_context; +} + +void aspl_ailinterpreter_stack_push_frame(ASPL_AILI_Stack* stack) { + if (stack->current_frame_index >= (stack->frames_size - 1)) { + stack->frames_size *= 2; + stack->frames = ASPL_REALLOC(stack->frames, sizeof(ASPL_AILI_StackFrame*) * stack->frames_size); + } + stack->frames[++stack->current_frame_index] = (ASPL_AILI_StackFrame){ .data = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 64), .top = 0 }; +} + +void aspl_ailinterpreter_stack_push(ASPL_AILI_Stack* stack, ASPL_OBJECT_TYPE object) { + stack->frames[stack->current_frame_index].data[stack->frames[stack->current_frame_index].top++] = object; +} + +ASPL_AILI_StackFrame aspl_ailinterpreter_stack_pop_frame(ASPL_AILI_Stack* stack) { + if (stack->current_frame_index <= 0) ASPL_PANIC("Cannot pop frame from empty stack"); + return stack->frames[--stack->current_frame_index]; +} + +ASPL_OBJECT_TYPE aspl_ailinterpreter_stack_pop(ASPL_AILI_Stack* stack) { + if (stack->frames[stack->current_frame_index].top <= 0) ASPL_PANIC("Cannot pop from empty stack frame"); + return stack->frames[stack->current_frame_index].data[--stack->frames[stack->current_frame_index].top]; +} + +ASPL_OBJECT_TYPE aspl_ailinterpreter_stack_peek(ASPL_AILI_Stack* stack) { + if (stack->frames[stack->current_frame_index].top <= 0) ASPL_PANIC("Cannot peek from empty stack frame"); + return stack->frames[stack->current_frame_index].data[stack->frames[stack->current_frame_index].top - 1]; +} + +void aspl_ailinterpreter_cims_push(ASPL_AILI_CallableInvocationMetaStack* stack, ASPL_AILI_CallableInvocationMeta value) { + stack->data[stack->top++] = value; +} + +ASPL_AILI_CallableInvocationMeta aspl_ailinterpreter_cims_pop(ASPL_AILI_CallableInvocationMetaStack* stack) { + if (stack->top <= 0) ASPL_PANIC("Cannot pop from empty callable invocation meta stack"); + return stack->data[--stack->top]; +} + +ASPL_AILI_CallableInvocationMeta aspl_ailinterpreter_cims_peek(ASPL_AILI_CallableInvocationMetaStack* stack) { + if (stack->top <= 0) ASPL_PANIC("Cannot peek from empty callable invocation meta stack"); + return stack->data[stack->top - 1]; +} + +void aspl_ailinterpreter_lms_push(ASPL_AILI_LoopMetaStack* stack, ASPL_AILI_LoopMeta value) { + stack->data[stack->top++] = value; +} + +ASPL_AILI_LoopMeta aspl_ailinterpreter_lms_pop(ASPL_AILI_LoopMetaStack* stack) { + if (stack->top <= 0) ASPL_PANIC("Cannot pop from empty loop meta stack"); + return stack->data[--stack->top]; +} + +ASPL_AILI_LoopMeta aspl_ailinterpreter_lms_peek(ASPL_AILI_LoopMetaStack* stack) { + if (stack->top <= 0) ASPL_PANIC("Cannot peek from empty loop meta stack"); + return stack->data[stack->top - 1]; +} + +void aspl_ailinterpreter_class_stack_push(ASPL_AILI_ClassStack* stack, char* object) { + stack->data[stack->top++] = object; +} + +char* aspl_ailinterpreter_class_stack_pop(ASPL_AILI_ClassStack* stack) { + if (stack->top <= 0) ASPL_PANIC("Cannot pop from empty class stack"); + return stack->data[--stack->top]; +} + +void aspl_ailinterpreter_push_scope(ASPL_AILI_ThreadContext* context) { + if (context->scopes_top >= (context->scopes_size - 1)) { + context->scopes_size *= 2; + context->scopes = ASPL_REALLOC(context->scopes, sizeof(hashmap_str_to_voidptr_HashMap*) * context->scopes_size); + } + context->scopes[++context->scopes_top] = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 64 }); +} + +void aspl_ailinterpreter_pop_scope(ASPL_AILI_ThreadContext* context) { + context->scopes_top--; +} + +void aspl_ailinterpreter_register_variable(ASPL_AILI_ThreadContext* context, char* identifier, ASPL_OBJECT_TYPE value) { + hashmap_str_to_voidptr_hashmap_set(context->scopes[context->scopes_top], identifier, C_REFERENCE(value)); +} + +ASPL_OBJECT_TYPE* aspl_ailinterpreter_access_variable_address(ASPL_AILI_ThreadContext* context, char* identifier) { + for (int i = context->scopes_top; i >= 0; i--) { + if (hashmap_str_to_voidptr_hashmap_contains_key(context->scopes[i], identifier)) { + return hashmap_str_to_voidptr_hashmap_get_value(context->scopes[i], identifier); + } + } + ASPL_PANIC("Cannot access unregistered variable '%s'", identifier); +} + +ASPL_OBJECT_TYPE aspl_ailinterpreter_access_variable(ASPL_AILI_ThreadContext* context, char* identifier) { + return *aspl_ailinterpreter_access_variable_address(context, identifier); +} + +void aspl_ailinterpreter_register_class(ASPL_AILI_EnvironmentContext* context, char* identifier, ASPL_AILI_Class* class) { + hashmap_str_to_voidptr_hashmap_set(context->classes, identifier, class); +} + +ASPL_AILI_Class* aspl_ailinterpreter_access_class(ASPL_AILI_EnvironmentContext* context, char* identifier) { + return hashmap_str_to_voidptr_hashmap_get_value(context->classes, identifier); +} + +void aspl_ailinterpreter_stack_trace_push(ASPL_AILI_ThreadContext* context, char* identifier) { + if (context->stack_trace->top >= (context->stack_trace->size - 1)) { + context->stack_trace->size *= 2; + context->stack_trace->frames = ASPL_REALLOC(context->stack_trace->frames, sizeof(char*) * context->stack_trace->size); + } + context->stack_trace->frames[context->stack_trace->top++] = identifier; +} + +char* aspl_ailinterpreter_stack_trace_pop(ASPL_AILI_ThreadContext* context) { + if (context->stack_trace->top <= 0) ASPL_PANIC("Cannot pop from empty stack trace"); + return context->stack_trace->frames[--context->stack_trace->top]; +} + +ASPL_AILI_TypeList aspl_ailinterpreter_parse_types(ASPL_AILI_ByteList* bytes) { + char** type_list = ASPL_MALLOC(sizeof(char*) * 8); + int type_list_size = 8; + int type_list_top = 0; + int type_list_length = aspl_ailinterpreter_read_short(bytes); + while (type_list_top < type_list_length) { + while (type_list_top >= type_list_size) { + type_list_size *= 2; + type_list = ASPL_REALLOC(type_list, sizeof(char*) * type_list_size); + } + type_list[type_list_top++] = aspl_ailinterpreter_read_short_string(bytes); + } + if (type_list_top == 0) { + type_list[type_list_top++] = "any"; + } + return (ASPL_AILI_TypeList) { .types = type_list, .size = type_list_top }; +} + +ASPL_AILI_ParameterList aspl_ailinterpreter_parse_parameters(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes) { + ASPL_AILI_Parameter* parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 8); + int parameters_size = 8; + int parameters_top = 0; + int parameters_length = aspl_ailinterpreter_read_short(bytes); + ASPL_OBJECT_TYPE* default_values = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 8); + int default_values_size = 8; + int default_values_top = 0; + while (parameters_top < parameters_length) { + while (parameters_top >= parameters_size) { + parameters_size *= 2; + parameters = ASPL_REALLOC(parameters, sizeof(ASPL_AILI_Parameter) * parameters_size); + } + char* name = aspl_ailinterpreter_read_short_string(bytes); + ASPL_AILI_TypeList expected_types = aspl_ailinterpreter_parse_types(bytes); + int optional = aspl_ailinterpreter_read_bool(bytes); + if (optional) { + ASPL_OBJECT_TYPE default_value = aspl_ailinterpreter_stack_pop(context->stack); + while (default_values_top >= default_values_size) { + default_values_size *= 2; + default_values = ASPL_REALLOC(default_values, sizeof(ASPL_OBJECT_TYPE) * default_values_size); + } + default_values[default_values_top++] = default_value; + parameters[parameters_top++] = (ASPL_AILI_Parameter){ .name = name, .expected_types = expected_types, .optional = optional }; + } + else { + parameters[parameters_top++] = (ASPL_AILI_Parameter){ .name = name, .expected_types = expected_types, .optional = optional }; + } + } + for (int i = 0; i < parameters_top; i++) { + if (parameters[i].optional) { + parameters[i].default_value = default_values[--default_values_top]; + } + } + return (ASPL_AILI_ParameterList) { .parameters = parameters, .size = parameters_top }; +} + +char* aspl_ailinterpreter_construct_list_from_type_list(ASPL_AILI_TypeList type_list) { + int length = 5; // "list<" + for (int i = 0; i < type_list.size; i++) { + length += strlen(type_list.types[i]); + if (i < type_list.size - 1) { + length += 1; // "|" + } + } + length += 1; // ">" + char* result = ASPL_MALLOC(sizeof(char) * (length + 1)); + result[0] = 'l'; + result[1] = 'i'; + result[2] = 's'; + result[3] = 't'; + result[4] = '<'; + int pos = 5; + for (int i = 0; i < type_list.size; i++) { + for (int j = 0; j < strlen(type_list.types[i]); j++) { + result[pos++] = type_list.types[i][j]; + } + if (i < type_list.size - 1) { + result[pos++] = '|'; + } + } + result[pos++] = '>'; + result[pos] = '\0'; + return result; +} + +char* aspl_ailinterpreter_construct_map_from_type_lists(ASPL_AILI_TypeList key_type_list, ASPL_AILI_TypeList value_type_list) { + int length = 4; // "map<" + for (int i = 0; i < key_type_list.size; i++) { + length += strlen(key_type_list.types[i]); + if (i < key_type_list.size - 1) { + length += 1; // "|" + } + } + length += 2; // ", " + for (int i = 0; i < value_type_list.size; i++) { + length += strlen(value_type_list.types[i]); + if (i < value_type_list.size - 1) { + length += 1; // "|" + } + } + length += 1; // ">" + char* result = ASPL_MALLOC(sizeof(char) * (length + 1)); + result[0] = 'm'; + result[1] = 'a'; + result[2] = 'p'; + result[3] = '<'; + int pos = 4; + for (int i = 0; i < key_type_list.size; i++) { + for (int j = 0; j < strlen(key_type_list.types[i]); j++) { + result[pos++] = key_type_list.types[i][j]; + } + if (i < key_type_list.size - 1) { + result[pos++] = '|'; + } + } + result[pos++] = ','; + result[pos++] = ' '; + for (int i = 0; i < value_type_list.size; i++) { + for (int j = 0; j < strlen(value_type_list.types[i]); j++) { + result[pos++] = value_type_list.types[i][j]; + } + if (i < value_type_list.size - 1) { + result[pos++] = '|'; + } + } + result[pos++] = '>'; + result[pos] = '\0'; + return result; +} + +void aspl_ailinterpreter_return(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, ASPL_OBJECT_TYPE value) { +#ifdef ASPL_INTERPRETER_FIND_STACK_LEAKS + if (context->stack->frames[context->stack->current_frame_index].top != 0) { + ASPL_PANIC("Leaked stack object(s) found!\n"); + } +#endif + aspl_ailinterpreter_stack_pop_frame(context->stack); + ASPL_AILI_CallableInvocationMeta meta = aspl_ailinterpreter_cims_pop(context->callable_invocation_meta_stack); + if (meta.is_reactive_property_setter) { + aspl_ailinterpreter_stack_push(context->stack, aspl_ailinterpreter_access_variable(context, "value")); + } + else if (!meta.is_constructor) { + aspl_ailinterpreter_stack_push(context->stack, value); + } + if (meta.type == ASPL_AILINTERPRETER_CALLABLE_TYPE_CALLBACK) { + context->scopes = meta.previous_scopes; + context->scopes_top = meta.previous_scopes_top; + } + else { + aspl_ailinterpreter_pop_scope(context); + } + if (meta.previous_instance != NULL) { + context->current_instance = *meta.previous_instance; + } + context->loop_meta_stack = meta.previous_loop_meta_stack; + aspl_ailinterpreter_stack_trace_pop(context); + bytes->position = meta.previous_address; +} + +ASPL_AILI_FunctionPtr aspl_ailinterpreter_get_function(ASPL_AILI_ThreadContext* context, char* identifier) { + return hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->functions, identifier); +} + +void aspl_ailinterpreter_custom_function_callback(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, char* identifier, ASPL_AILI_ArgumentList arguments) { + ASPL_AILI_CustomFunction* custom_function = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->custom_functions, identifier); + bytes->position = custom_function->address; + for (int i = 0; i < custom_function->parameters.size; i++) { + if (i < arguments.size) { + aspl_ailinterpreter_register_variable(context, custom_function->parameters.parameters[i].name, arguments.arguments[i]); + } + else if (custom_function->parameters.parameters[i].optional) { + aspl_ailinterpreter_register_variable(context, custom_function->parameters.parameters[i].name, aspl_object_clone_shallow(custom_function->parameters.parameters[i].default_value)); + } + else { + ASPL_PANIC("Missing argument for parameter '%s'", custom_function->parameters.parameters[i].name); + } + } +} + +ASPL_AILI_CustomMethod* aspl_ailinterpreter_util_find_custom_method(ASPL_AILI_ThreadContext* context, char* type, char* name) { + if (hashmap_str_to_voidptr_hashmap_contains_key(context->environment_context->custom_methods, type)) { + hashmap_str_to_voidptr_HashMap* methods = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->custom_methods, type); + if (hashmap_str_to_voidptr_hashmap_contains_key(methods, name)) { + return hashmap_str_to_voidptr_hashmap_get_value(methods, name); + } + } + ASPL_AILI_Class* class = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->classes, type); + for (int i = 0; i < class->parents_size; i++) { + ASPL_AILI_CustomMethod* method = aspl_ailinterpreter_util_find_custom_method(context, class->parents[i], name); + if (method != NULL) { + return method; + } + } + return NULL; +} + +ASPL_AILI_ReactiveProperty* aspl_ailinterpreter_util_find_reactive_property(ASPL_AILI_ThreadContext* context, char* type, char* name) { + if (hashmap_str_to_voidptr_hashmap_contains_key(aspl_ailinterpreter_access_class(context->environment_context, type)->reactive_properties, name)) { + return hashmap_str_to_voidptr_hashmap_get_value(aspl_ailinterpreter_access_class(context->environment_context, type)->reactive_properties, name); + } + ASPL_AILI_Class* class = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->classes, type); + for (int i = 0; i < class->parents_size; i++) { + ASPL_AILI_ReactiveProperty* property = aspl_ailinterpreter_util_find_reactive_property(context, class->parents[i], name); + if (property != NULL) { + return property; + } + } + return NULL; +} + +char aspl_ailinterpreter_util_is_class_child_of(ASPL_AILI_ThreadContext* context, char* child, char* parent) { + if (strcmp(child, parent) == 0) { + return 1; + } + ASPL_AILI_Class* class = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->classes, child); + for (int i = 0; i < class->parents_size; i++) { + if (aspl_ailinterpreter_util_is_class_child_of(context, class->parents[i], parent)) { + return 1; + } + } + return 0; +} + +void aspl_ailinterpreter_display_stack_trace(ASPL_AILI_ThreadContext* context) { + printf("Stack trace (most recent call first):\n"); + for (int i = context->stack_trace->top - 1; i >= 0; i--) { + if (i == context->stack_trace->top - 1) { + printf(" > %s\n", context->stack_trace->frames[i]); + } + else { + printf(" %s\n", context->stack_trace->frames[i]); + } + } +} + +_Thread_local ASPL_AILI_ThreadContext* aspl_ailinterpreter_current_thread_context; +_Thread_local ASPL_AILI_ByteList* aspl_ailinterpreter_current_byte_list; + +void aspl_ailinterpreter_invoke_callback_from_outside_of_loop_internal(ASPL_Callback* callback, ASPL_AILI_ArgumentList arguments, ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes) { + char* identifier = ASPL_MALLOC(sizeof(char) * (strlen(callback->typePtr) + strlen("invoke") + 2)); + strcpy(identifier, callback->typePtr); + strcat(identifier, "."); + strcat(identifier, "invoke"); + aspl_ailinterpreter_stack_trace_push(context, identifier); + aspl_ailinterpreter_stack_push_frame(context->stack); + ASPL_AILI_CallbackData* callback_data = (ASPL_AILI_CallbackData*)callback->function; + ASPL_AILI_CallableInvocationMetaStack* original_callable_invocation_meta_stack = context->callable_invocation_meta_stack; + ASPL_AILI_CallableInvocationMetaStack* callable_invocation_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_CallableInvocationMetaStack)); + callable_invocation_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_CallableInvocationMeta) * 1024); + callable_invocation_meta_stack->top = 0; + context->callable_invocation_meta_stack = callable_invocation_meta_stack; + hashmap_str_to_voidptr_HashMap** original_scopes = context->scopes; + int original_scopes_top = context->scopes_top; + context->scopes = callback_data->creation_scopes; + context->scopes_top = callback_data->creation_scopes_top; + int original_position = bytes->position; + bytes->position = callback_data->address; + for (int i = 0; i < callback_data->parameters.size; i++) { + if (i < arguments.size) { + aspl_ailinterpreter_register_variable(context, callback_data->parameters.parameters[i].name, arguments.arguments[i]); + } + else if (callback_data->parameters.parameters[i].optional) { + aspl_ailinterpreter_register_variable(context, callback_data->parameters.parameters[i].name, aspl_object_clone_shallow(callback_data->parameters.parameters[i].default_value)); + } + else { + ASPL_PANIC("Missing argument for parameter '%s'", callback_data->parameters.parameters[i].name); + } + } + aspl_ailinterpreter_loop(context, bytes); + bytes->position = original_position; + context->scopes = original_scopes; + context->scopes_top = original_scopes_top; + context->callable_invocation_meta_stack = original_callable_invocation_meta_stack; + aspl_ailinterpreter_stack_trace_pop(context); + aspl_ailinterpreter_stack_pop_frame(context->stack); +} + +void aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_Callback* callback, ASPL_AILI_ArgumentList arguments) { + aspl_ailinterpreter_invoke_callback_from_outside_of_loop_internal(callback, arguments, aspl_ailinterpreter_current_thread_context, aspl_ailinterpreter_current_byte_list); +} + +int aspl_ailinterpreter_new_thread_function_invocation_callback(void* arguments) { + struct GC_stack_base sb; + GC_get_stack_base(&sb); + GC_register_my_thread(&sb); + + ASPL_AILI_ThreadFunctionWrapperData* data = (ASPL_AILI_ThreadFunctionWrapperData*)arguments; + + aspl_ailinterpreter_current_thread_context = data->context; + aspl_ailinterpreter_current_byte_list = data->bytes; + + aspl_ailinterpreter_stack_trace_push(data->context, data->identifier); + aspl_ailinterpreter_stack_push_frame(data->context->stack); + data->function(data->context, data->bytes, data->identifier, data->arguments); + aspl_ailinterpreter_loop(data->context, data->bytes); + + GC_unregister_my_thread(); + return 0; +} + +int aspl_ailinterpreter_new_thread_method_invocation_callback(void* arguments) { + struct GC_stack_base sb; + GC_get_stack_base(&sb); + GC_register_my_thread(&sb); + + ASPL_AILI_ThreadMethodWrapperData* data = (ASPL_AILI_ThreadMethodWrapperData*)arguments; + + aspl_ailinterpreter_current_thread_context = data->context; + aspl_ailinterpreter_current_byte_list = data->bytes; + + aspl_ailinterpreter_stack_trace_push(data->context, data->identifier); + aspl_ailinterpreter_stack_push_frame(data->context->stack); + ASPL_AILI_CustomMethod* custom_method = data->method; + data->bytes->position = custom_method->address; + for (int i = 0; i < custom_method->parameters.size; i++) { + if (i < data->arguments.size) { + aspl_ailinterpreter_register_variable(data->context, custom_method->parameters.parameters[i].name, data->arguments.arguments[i]); + } + else if (custom_method->parameters.parameters[i].optional) { + aspl_ailinterpreter_register_variable(data->context, custom_method->parameters.parameters[i].name, aspl_object_clone_shallow(custom_method->parameters.parameters[i].default_value)); + } + else { + ASPL_PANIC("Missing argument for parameter '%s'", custom_method->parameters.parameters[i].name); + } + } + aspl_ailinterpreter_loop(data->context, data->bytes); + + GC_unregister_my_thread(); + return 0; +} + +int aspl_ailinterpreter_new_thread_callback_invocation_callback(void* arguments) { + struct GC_stack_base sb; + GC_get_stack_base(&sb); + GC_register_my_thread(&sb); + + ASPL_AILI_ThreadCallbackWrapperData* data = (ASPL_AILI_ThreadCallbackWrapperData*)arguments; + + aspl_ailinterpreter_current_thread_context = data->context; + aspl_ailinterpreter_current_byte_list = data->bytes; + + aspl_ailinterpreter_invoke_callback_from_outside_of_loop_internal(data->callback, data->arguments, data->context, data->bytes); + + GC_unregister_my_thread(); + return 0; +} + +void aspl_ailinterpreter_loop(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes) { + while (1) { + ASPL_AILI_Instruction instruction = aspl_ailinterpreter_fetch_instruction(bytes); + switch (instruction) { + case ASPL_AILI_INSTRUCTION_MANIFEST: { + int length = aspl_ailinterpreter_read_int(bytes); + // Ignore the manifest for now... + bytes->position += length; + break; + } + case ASPL_AILI_INSTRUCTION_CREATE_OBJECT: { + char* type = aspl_ailinterpreter_read_short_string(bytes); + if (strcmp(type, "null") == 0) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_NULL()); + } + else if (strcmp(type, "bool") == 0) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_BOOL_LITERAL(aspl_ailinterpreter_read_byte(bytes))); + } + else if (strcmp(type, "byte") == 0) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_BYTE_LITERAL(aspl_ailinterpreter_read_byte(bytes))); + } + else if (strcmp(type, "int") == 0) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_INT_LITERAL(aspl_ailinterpreter_read_int(bytes))); + } + else if (strcmp(type, "long") == 0) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_LONG_LITERAL(aspl_ailinterpreter_read_long(bytes))); + } + else if (strcmp(type, "float") == 0) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_FLOAT_LITERAL(aspl_ailinterpreter_read_float(bytes))); + } + else if (strcmp(type, "double") == 0) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_DOUBLE_LITERAL(aspl_ailinterpreter_read_double(bytes))); + } + else if (strcmp(type, "string") == 0) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_STRING_LITERAL(aspl_ailinterpreter_read_long_string(bytes))); + } + else if (strcmp(type, "list") == 0) { + ASPL_AILI_TypeList generic_types = aspl_ailinterpreter_parse_types(bytes); + char* type = aspl_ailinterpreter_construct_list_from_type_list(generic_types); + int length = aspl_ailinterpreter_read_int(bytes); + ASPL_OBJECT_TYPE* array = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * length); + for (int i = length - 1; i >= 0; i--) { + array[i] = aspl_ailinterpreter_stack_pop(context->stack); + } + aspl_ailinterpreter_stack_push(context->stack, ASPL_LIST_LITERAL(type, strlen(type), array, length)); + } + else if (strcmp(type, "map") == 0) { + ASPL_AILI_TypeList generic_key_types = aspl_ailinterpreter_parse_types(bytes); + ASPL_AILI_TypeList generic_value_types = aspl_ailinterpreter_parse_types(bytes); + char* type = aspl_ailinterpreter_construct_map_from_type_lists(generic_key_types, generic_value_types); + int length = aspl_ailinterpreter_read_int(bytes); + int initial_capacity = length; + if (initial_capacity < 1) initial_capacity = 1; // hashmap_new can't handle 0 as initial capacity + hashmap_aspl_object_to_aspl_object_HashMap* hashmap = hashmap_aspl_object_to_aspl_object_new_hashmap((hashmap_aspl_object_to_aspl_object_HashMapConfig) { .initial_capacity = initial_capacity }); + for (int i = 0; i < length; i++) { + ASPL_OBJECT_TYPE value = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE key = aspl_ailinterpreter_stack_pop(context->stack); + hashmap_aspl_object_to_aspl_object_hashmap_set(hashmap, ASPL_HASHMAP_WRAP(key), ASPL_HASHMAP_WRAP(value)); + } + hashmap = hashmap_aspl_object_to_aspl_object_hashmap_reverse(hashmap); + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_MAP; + ASPL_Map* m = ASPL_MALLOC(sizeof(ASPL_Map)); + m->typePtr = type; + m->typeLen = strlen(type); + m->hashmap = hashmap; + ASPL_ACCESS(obj).value.map = m; + aspl_ailinterpreter_stack_push(context->stack, obj); + } + else { + ASPL_PANIC("Unknown type '%s'", type); + } + break; + } + case ASPL_AILI_INSTRUCTION_BYTE_ARRAY_LITERAL: { + long long length = aspl_ailinterpreter_read_long(bytes); + ASPL_OBJECT_TYPE* array = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * length); + for (int i = 0; i < length; i++) { + array[i] = ASPL_BYTE_LITERAL(aspl_ailinterpreter_read_byte(bytes)); + } + aspl_ailinterpreter_stack_push(context->stack, ASPL_LIST_LITERAL("list", 10, array, length)); + break; + } + case ASPL_AILI_INSTRUCTION_IMPLEMENT: { + char* call = aspl_ailinterpreter_read_long_string(bytes); + int argc = aspl_ailinterpreter_read_int(bytes); + ASPL_AILI_ArgumentList arguments = { .arguments = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * argc), .size = argc }; + for (int i = argc - 1; i >= 0; i--) { + arguments.arguments[i] = aspl_ailinterpreter_stack_pop(context->stack); + } + ASPL_OBJECT_TYPE value = aspl_ailinterpreter_implement(context, call, arguments); + if (!ASPL_IS_UNINITIALIZED(value)) { + aspl_ailinterpreter_stack_push(context->stack, value); + } + else { + aspl_ailinterpreter_stack_push(context->stack, ASPL_NULL()); + } + break; + } + case ASPL_AILI_INSTRUCTION_DECLARE_VARIABLE: { + char* identifier = aspl_ailinterpreter_read_short_string(bytes); + ASPL_AILI_TypeList types = aspl_ailinterpreter_parse_types(bytes); + ASPL_OBJECT_TYPE value = aspl_ailinterpreter_stack_pop(context->stack); + aspl_ailinterpreter_register_variable(context, identifier, value); + aspl_ailinterpreter_stack_push(context->stack, value); + break; + } + case ASPL_AILI_INSTRUCTION_UPDATE_VARIABLE: { + char* identifier = aspl_ailinterpreter_read_short_string(bytes); + ASPL_OBJECT_TYPE value = aspl_ailinterpreter_stack_peek(context->stack); + ASPL_OBJECT_TYPE* address = aspl_ailinterpreter_access_variable_address(context, identifier); + *address = value; + break; + } + case ASPL_AILI_INSTRUCTION_ACCESS_VARIABLE: { + char* identifier = aspl_ailinterpreter_read_short_string(bytes); + ASPL_OBJECT_TYPE value = aspl_ailinterpreter_access_variable(context, identifier); + aspl_ailinterpreter_stack_push(context->stack, value); + break; + } + case ASPL_AILI_INSTRUCTION_JUMP_RELATIVE: { + long long offset = aspl_ailinterpreter_read_long(bytes); + bytes->position += offset; + break; + } + case ASPL_AILI_INSTRUCTION_JUMP_RELATIVE_IF_FALSE: { + long long offset = aspl_ailinterpreter_read_long(bytes); + if (!ASPL_IS_TRUE(aspl_ailinterpreter_stack_pop(context->stack))) { + bytes->position += offset; + } + break; + } + case ASPL_AILI_INSTRUCTION_CALL_FUNCTION: { + char* identifier = aspl_ailinterpreter_read_long_string(bytes); + int new_thread = aspl_ailinterpreter_read_bool(bytes); + int argc = aspl_ailinterpreter_read_int(bytes); + ASPL_AILI_ParameterList* function_parameters = NULL; + if (hashmap_str_to_voidptr_hashmap_contains_key(context->environment_context->builtin_functions, identifier)) { + ASPL_AILI_BuiltinFunction* builtin_function = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->builtin_functions, identifier); + function_parameters = &builtin_function->parameters; + } + else if (hashmap_str_to_voidptr_hashmap_contains_key(context->environment_context->custom_functions, identifier)) { + ASPL_AILI_CustomFunction* custom_function = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->custom_functions, identifier); + function_parameters = &custom_function->parameters; + } + int size = argc > function_parameters->size ? argc : function_parameters->size; + ASPL_AILI_ArgumentList arguments = { .arguments = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * size), .size = size }; + for (int i = argc - 1; i >= 0; i--) { + arguments.arguments[i] = aspl_ailinterpreter_stack_pop(context->stack); + } + for (int i = argc; i < function_parameters->size; i++) { + if (function_parameters->parameters[i].optional) { + arguments.arguments[i] = aspl_object_clone_shallow(function_parameters->parameters[i].default_value); + } + else { + ASPL_PANIC("Missing argument for parameter '%s'", function_parameters->parameters[i].name); + } + } + ASPL_AILI_FunctionPtr function = aspl_ailinterpreter_get_function(context, identifier); + if (new_thread) { + ASPL_AILI_ThreadFunctionWrapperData* data = ASPL_MALLOC(sizeof(ASPL_AILI_ThreadFunctionWrapperData)); + data->function = function; + data->context = aspl_ailinterpreter_new_thread_context(context->environment_context); + data->bytes = aspl_ailinterpreter_bytelist_clone(bytes); + data->identifier = identifier; + data->arguments = arguments; + thread_create(aspl_ailinterpreter_new_thread_function_invocation_callback, data, THREAD_STACK_SIZE_DEFAULT); + aspl_ailinterpreter_stack_push(context->stack, ASPL_NULL()); + } + else { + aspl_ailinterpreter_stack_trace_push(context, identifier); + aspl_ailinterpreter_stack_push_frame(context->stack); + aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, (ASPL_AILI_CallableInvocationMeta) { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_FUNCTION, .previous_address = bytes->position, .previous_loop_meta_stack = context->loop_meta_stack }); + context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack)); + context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024); + context->loop_meta_stack->top = 0; + aspl_ailinterpreter_push_scope(context); + function(context, bytes, identifier, arguments); + } + break; + } + case ASPL_AILI_INSTRUCTION_DECLARE_FUNCTION: { + char* identifier = aspl_ailinterpreter_read_short_string(bytes); + ASPL_AILI_ParameterList parameters = aspl_ailinterpreter_parse_parameters(context, bytes); + ASPL_AILI_TypeList return_types = aspl_ailinterpreter_parse_types(bytes); + ASPL_AILI_CustomFunction* custom_function = ASPL_MALLOC(sizeof(ASPL_AILI_CustomFunction)); + custom_function->parameters = parameters; + long long code_length = aspl_ailinterpreter_read_long(bytes); + custom_function->address = bytes->position; + bytes->position += code_length; + hashmap_str_to_voidptr_hashmap_set(context->environment_context->custom_functions, identifier, custom_function); + hashmap_str_to_voidptr_hashmap_set(context->environment_context->functions, identifier, aspl_ailinterpreter_custom_function_callback); + break; + } + case ASPL_AILI_INSTRUCTION_DECLARE_METHOD: { + char is_static = aspl_ailinterpreter_read_bool(bytes); + char* name = aspl_ailinterpreter_read_short_string(bytes); + ASPL_AILI_ParameterList parameters = aspl_ailinterpreter_parse_parameters(context, bytes); + ASPL_AILI_TypeList return_types = aspl_ailinterpreter_parse_types(bytes); + ASPL_AILI_CustomMethod* custom_method = ASPL_MALLOC(sizeof(ASPL_AILI_CustomMethod)); + custom_method->is_static = is_static; + custom_method->parameters = parameters; + long long code_length = aspl_ailinterpreter_read_long(bytes); + custom_method->address = bytes->position; + bytes->position += code_length; + hashmap_str_to_voidptr_HashMap* methods = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->custom_methods, context->current_class); + hashmap_str_to_voidptr_hashmap_set(methods, name, custom_method); + break; + } + case ASPL_AILI_INSTRUCTION_CALL_METHOD: { + char is_static = aspl_ailinterpreter_read_bool(bytes); + if (is_static) { + char* type = aspl_ailinterpreter_read_short_string(bytes); + char* name = aspl_ailinterpreter_read_short_string(bytes); + char new_thread = aspl_ailinterpreter_read_bool(bytes); + int argc = aspl_ailinterpreter_read_int(bytes); + ASPL_AILI_ParameterList* method_parameters = NULL; + // static builtin methods are not supported in ASPL yet + if (hashmap_str_to_voidptr_hashmap_contains_key(context->environment_context->custom_methods, type)) { + hashmap_str_to_voidptr_HashMap* methods = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->custom_methods, type); + if (hashmap_str_to_voidptr_hashmap_contains_key(methods, name)) { + ASPL_AILI_CustomMethod* method = hashmap_str_to_voidptr_hashmap_get_value(methods, name); + method_parameters = &method->parameters; + } + } + int size = argc > method_parameters->size ? argc : method_parameters->size; + ASPL_AILI_ArgumentList arguments = { .arguments = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * size), .size = size }; + for (int i = argc - 1; i >= 0; i--) { + arguments.arguments[i] = aspl_ailinterpreter_stack_pop(context->stack); + } + for (int i = argc; i < method_parameters->size; i++) { + if (method_parameters->parameters[i].optional) { + arguments.arguments[i] = aspl_object_clone_shallow(method_parameters->parameters[i].default_value); + } + else { + ASPL_PANIC("Missing argument for parameter '%s'", method_parameters->parameters[i].name); + } + } + ASPL_AILI_CustomMethod* method = hashmap_str_to_voidptr_hashmap_get_value(hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->custom_methods, type), name); + char* identifier = ASPL_MALLOC(sizeof(char) * (strlen(type) + strlen(name) + 2)); + strcpy(identifier, type); + strcat(identifier, "."); + strcat(identifier, name); + if (new_thread) { + ASPL_AILI_ThreadMethodWrapperData* data = ASPL_MALLOC(sizeof(ASPL_AILI_ThreadMethodWrapperData)); + data->method = method; + data->context = aspl_ailinterpreter_new_thread_context(context->environment_context); + data->bytes = aspl_ailinterpreter_bytelist_clone(bytes); + data->identifier = identifier; + data->arguments = arguments; + thread_create(aspl_ailinterpreter_new_thread_method_invocation_callback, data, THREAD_STACK_SIZE_DEFAULT); + aspl_ailinterpreter_stack_push(context->stack, ASPL_NULL()); + } + else { + aspl_ailinterpreter_stack_trace_push(context, identifier); + aspl_ailinterpreter_stack_push_frame(context->stack); + ASPL_AILI_CallableInvocationMeta meta = { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_METHOD, .previous_address = bytes->position, .previous_loop_meta_stack = context->loop_meta_stack }; + aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, meta); + context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack)); + context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024); + context->loop_meta_stack->top = 0; + aspl_ailinterpreter_push_scope(context); + bytes->position = method->address; + for (int i = 0; i < method->parameters.size; i++) { + if (i < arguments.size) { + aspl_ailinterpreter_register_variable(context, method->parameters.parameters[i].name, arguments.arguments[i]); + } + else if (method->parameters.parameters[i].optional) { + aspl_ailinterpreter_register_variable(context, method->parameters.parameters[i].name, aspl_object_clone_shallow(method->parameters.parameters[i].default_value)); + } + else { + ASPL_PANIC("Missing argument for parameter '%s'", method->parameters.parameters[i].name); + } + } + } + } + else { + char* name = aspl_ailinterpreter_read_short_string(bytes); + char new_thread = aspl_ailinterpreter_read_bool(bytes); + int argc = aspl_ailinterpreter_read_int(bytes); + ASPL_AILI_ArgumentList arguments = { .arguments = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * argc), .size = argc }; + for (int i = argc - 1; i >= 0; i--) { + arguments.arguments[i] = aspl_ailinterpreter_stack_pop(context->stack); + } + ASPL_OBJECT_TYPE object = aspl_ailinterpreter_stack_pop(context->stack); + char isInvokeMethod = ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_CALLBACK && strcmp(name, "invoke") == 0; + if (!isInvokeMethod) { + ASPL_AILI_ParameterList* method_parameters = NULL; + if (hashmap_str_to_voidptr_hashmap_contains_key(context->environment_context->builtin_methods, aspl_object_get_short_type_pointer(object))) { + hashmap_str_to_voidptr_HashMap* methods = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->builtin_methods, aspl_object_get_short_type_pointer(object)); + if (hashmap_str_to_voidptr_hashmap_contains_key(methods, name)) { + ASPL_AILI_BuiltinMethod* method = hashmap_str_to_voidptr_hashmap_get_value(methods, name); + method_parameters = &method->parameters; + } + } + if (method_parameters == NULL) { + if (hashmap_str_to_voidptr_hashmap_contains_key(context->environment_context->builtin_methods, "any")) { + hashmap_str_to_voidptr_HashMap* methods = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->builtin_methods, "any"); + if (hashmap_str_to_voidptr_hashmap_contains_key(methods, name)) { + ASPL_AILI_BuiltinMethod* method = hashmap_str_to_voidptr_hashmap_get_value(methods, name); + method_parameters = &method->parameters; + } + } + } + if (method_parameters == NULL) { + method_parameters = &aspl_ailinterpreter_util_find_custom_method(context, aspl_object_get_short_type_pointer(object), name)->parameters; + } + if (method_parameters->size > argc) { + arguments.arguments = ASPL_REALLOC(arguments.arguments, sizeof(ASPL_OBJECT_TYPE) * method_parameters->size); + arguments.size = method_parameters->size; + } + for (int i = argc; i < method_parameters->size; i++) { + if (method_parameters->parameters[i].optional) { + arguments.arguments[i] = aspl_object_clone_shallow(method_parameters->parameters[i].default_value); + } + else { + ASPL_PANIC("Missing argument for parameter '%s'", method_parameters->parameters[i].name); + } + } + if (ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_NULL || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_BOOLEAN || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_BYTE || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_INTEGER || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_LONG || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_FLOAT || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_DOUBLE || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_STRING || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_LIST || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_MAP) { + ASPL_OBJECT_TYPE** args = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * argc); + for (int i = 0; i < arguments.size; i++) { + args[i] = C_REFERENCE(arguments.arguments[i]); + } + aspl_ailinterpreter_stack_push(context->stack, aspl_object_method_invoke(object, name, args)); + } + else { + ASPL_AILI_CustomMethod* method = aspl_ailinterpreter_util_find_custom_method(context, aspl_object_get_short_type_pointer(object), name); + char* identifier = ASPL_MALLOC(sizeof(char) * (strlen(aspl_object_get_short_type_pointer(object)) + strlen(name) + 2)); + strcpy(identifier, aspl_object_get_short_type_pointer(object)); + strcat(identifier, "."); + strcat(identifier, name); + if (new_thread) { + ASPL_AILI_ThreadMethodWrapperData* data = ASPL_MALLOC(sizeof(ASPL_AILI_ThreadMethodWrapperData)); + data->method = method; + data->context = aspl_ailinterpreter_new_thread_context(context->environment_context); + data->bytes = aspl_ailinterpreter_bytelist_clone(bytes); + data->identifier = identifier; + data->arguments = arguments; + data->context->current_instance = object; + thread_create(aspl_ailinterpreter_new_thread_method_invocation_callback, data, THREAD_STACK_SIZE_DEFAULT); + aspl_ailinterpreter_stack_push(context->stack, ASPL_NULL()); + } + else { + aspl_ailinterpreter_stack_trace_push(context, identifier); + aspl_ailinterpreter_stack_push_frame(context->stack); + ASPL_AILI_CallableInvocationMeta meta = { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_METHOD, .previous_address = bytes->position, .previous_instance = C_REFERENCE(context->current_instance), .previous_loop_meta_stack = context->loop_meta_stack }; + aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, meta); + context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack)); + context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024); + context->loop_meta_stack->top = 0; + aspl_ailinterpreter_push_scope(context); + bytes->position = method->address; + if (ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_CLASS_INSTANCE) { // optimization + meta.previous_instance = C_REFERENCE(context->current_instance); + context->current_instance = object; + for (int i = 0; i < method->parameters.size; i++) { + if (i < arguments.size) { + aspl_ailinterpreter_register_variable(context, method->parameters.parameters[i].name, arguments.arguments[i]); + } + else if (method->parameters.parameters[i].optional) { + aspl_ailinterpreter_register_variable(context, method->parameters.parameters[i].name, aspl_object_clone_shallow(method->parameters.parameters[i].default_value)); + } + else { + ASPL_PANIC("Missing argument for parameter '%s'", method->parameters.parameters[i].name); + } + } + } + } + } + } + else { + ASPL_Callback* callback = ASPL_ACCESS(object).value.callback; + char* identifier = ASPL_MALLOC(sizeof(char) * (strlen(callback->typePtr) + strlen(name) + 2)); + strcpy(identifier, callback->typePtr); + strcat(identifier, "."); + strcat(identifier, name); + if (new_thread) { + ASPL_AILI_ThreadCallbackWrapperData* data = ASPL_MALLOC(sizeof(ASPL_AILI_ThreadCallbackWrapperData)); + data->callback = callback; + data->context = aspl_ailinterpreter_new_thread_context(context->environment_context); + data->bytes = aspl_ailinterpreter_bytelist_clone(bytes); + data->identifier = identifier; + data->arguments = arguments; + thread_create(aspl_ailinterpreter_new_thread_callback_invocation_callback, data, THREAD_STACK_SIZE_DEFAULT); + aspl_ailinterpreter_stack_push(context->stack, ASPL_NULL()); + } + else { + aspl_ailinterpreter_stack_trace_push(context, identifier); + aspl_ailinterpreter_stack_push_frame(context->stack); + ASPL_AILI_CallbackData* callback_data = (ASPL_AILI_CallbackData*)callback->function; + aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, (ASPL_AILI_CallableInvocationMeta) { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_CALLBACK, .previous_address = bytes->position, .previous_scopes = context->scopes, .previous_scopes_top = context->scopes_top, .previous_loop_meta_stack = context->loop_meta_stack }); + context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack)); + context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024); + context->loop_meta_stack->top = 0; + context->scopes = callback_data->creation_scopes; + context->scopes_top = callback_data->creation_scopes_top; + bytes->position = callback_data->address; + for (int i = 0; i < callback_data->parameters.size; i++) { + if (i < arguments.size) { + aspl_ailinterpreter_register_variable(context, callback_data->parameters.parameters[i].name, arguments.arguments[i]); + } + else if (callback_data->parameters.parameters[i].optional) { + aspl_ailinterpreter_register_variable(context, callback_data->parameters.parameters[i].name, aspl_object_clone_shallow(callback_data->parameters.parameters[i].default_value)); + } + else { + ASPL_PANIC("Missing argument for parameter '%s'", callback_data->parameters.parameters[i].name); + } + } + } + } + } + break; + } + case ASPL_AILI_INSTRUCTION_CALL_EXACT_METHOD: { + char* type = aspl_ailinterpreter_read_short_string(bytes); + char* name = aspl_ailinterpreter_read_short_string(bytes); + char new_thread = aspl_ailinterpreter_read_bool(bytes); + int argc = aspl_ailinterpreter_read_int(bytes); + hashmap_str_to_voidptr_HashMap* methods = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->custom_methods, type); + ASPL_AILI_CustomMethod* method = hashmap_str_to_voidptr_hashmap_get_value(methods, name); + ASPL_AILI_ParameterList* method_parameters = &method->parameters; + int size = argc > method_parameters->size ? argc : method_parameters->size; + ASPL_AILI_ArgumentList arguments = { .arguments = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * size), .size = size }; + for (int i = argc - 1; i >= 0; i--) { + arguments.arguments[i] = aspl_ailinterpreter_stack_pop(context->stack); + } + for (int i = argc; i < method_parameters->size; i++) { + if (method_parameters->parameters[i].optional) { + arguments.arguments[i] = aspl_object_clone_shallow(method_parameters->parameters[i].default_value); + } + else { + ASPL_PANIC("Missing argument for parameter '%s'", method_parameters->parameters[i].name); + } + } + char* identifier = ASPL_MALLOC(sizeof(char) * (strlen(type) + strlen(name) + 2)); + strcpy(identifier, type); + strcat(identifier, "."); + strcat(identifier, name); + ASPL_OBJECT_TYPE object = aspl_ailinterpreter_stack_pop(context->stack); + if (new_thread) { + ASPL_AILI_ThreadMethodWrapperData* data = ASPL_MALLOC(sizeof(ASPL_AILI_ThreadMethodWrapperData)); + data->method = method; + data->context = aspl_ailinterpreter_new_thread_context(context->environment_context); + data->bytes = aspl_ailinterpreter_bytelist_clone(bytes); + data->identifier = identifier; + data->arguments = arguments; + data->context->current_instance = object; + thread_create(aspl_ailinterpreter_new_thread_method_invocation_callback, data, THREAD_STACK_SIZE_DEFAULT); + aspl_ailinterpreter_stack_push(context->stack, ASPL_NULL()); + } + else { + aspl_ailinterpreter_stack_trace_push(context, identifier); + aspl_ailinterpreter_stack_push_frame(context->stack); + ASPL_AILI_CallableInvocationMeta meta = { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_METHOD, .previous_address = bytes->position, .previous_loop_meta_stack = context->loop_meta_stack }; + aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, meta); + context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack)); + context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024); + context->loop_meta_stack->top = 0; + aspl_ailinterpreter_push_scope(context); + bytes->position = method->address; + meta.previous_instance = C_REFERENCE(context->current_instance); + context->current_instance = object; + for (int i = 0; i < method->parameters.size; i++) { + if (i < arguments.size) { + aspl_ailinterpreter_register_variable(context, method->parameters.parameters[i].name, arguments.arguments[i]); + } + else if (method->parameters.parameters[i].optional) { + aspl_ailinterpreter_register_variable(context, method->parameters.parameters[i].name, aspl_object_clone_shallow(method->parameters.parameters[i].default_value)); + } + else { + ASPL_PANIC("Missing argument for parameter '%s'", method->parameters.parameters[i].name); + } + } + } + break; + } + case ASPL_AILI_INSTRUCTION_BREAK: { + char legacy = aspl_ailinterpreter_read_bool(bytes); // TODO: Remove this + int level = aspl_ailinterpreter_read_int(bytes); // TODO: Take this into account + bytes->position = aspl_ailinterpreter_lms_peek(context->loop_meta_stack).full_end_address; + break; + } + case ASPL_AILI_INSTRUCTION_CONTINUE: { + char legacy = aspl_ailinterpreter_read_bool(bytes); // TODO: Remove this + int level = aspl_ailinterpreter_read_int(bytes); // TODO: Take this into account + bytes->position = aspl_ailinterpreter_lms_peek(context->loop_meta_stack).body_end_address; + break; + } + case ASPL_AILI_INSTRUCTION_RETURN: { + aspl_ailinterpreter_return(context, bytes, aspl_ailinterpreter_stack_pop(context->stack)); + break; + } + case ASPL_AILI_INSTRUCTION_FALLBACK: { + aspl_ailinterpreter_return(context, bytes, aspl_ailinterpreter_stack_pop(context->stack)); + break; + } + case ASPL_AILI_INSTRUCTION_ESCAPE: { + ASPL_OBJECT_TYPE value = aspl_ailinterpreter_stack_pop(context->stack); + aspl_ailinterpreter_return(context, bytes, ASPL_UNINITIALIZED); + aspl_ailinterpreter_return(context, bytes, value); + break; + } + case ASPL_AILI_INSTRUCTION_DECLARE_ENUM: { + char* identifier = aspl_ailinterpreter_read_short_string(bytes); + int field_count = aspl_ailinterpreter_read_int(bytes); + ASPL_Enum* e = ASPL_MALLOC(sizeof(ASPL_Enum)); + e->typePtr = identifier; + e->typeLen = strlen(identifier); + e->isFlagEnum = aspl_ailinterpreter_read_bool(bytes); + e->stringValues = hashmap_int_to_str_new_hashmap((hashmap_int_to_str_HashMapConfig) { .initial_capacity = field_count }); + for (int i = 0; i < field_count; i++) { + char* field = aspl_ailinterpreter_read_short_string(bytes); + int value = aspl_ailinterpreter_read_int(bytes); + hashmap_int_to_str_hashmap_set(e->stringValues, value, field); + } + hashmap_str_to_voidptr_hashmap_set(context->environment_context->enums, identifier, e); + break; + } + case ASPL_AILI_INSTRUCTION_ENUM_FIELD: { + char* identifier = aspl_ailinterpreter_read_short_string(bytes); + char* string_field = aspl_ailinterpreter_read_short_string(bytes); + (void)string_field; // ignored in this implementation + int int_field = aspl_ailinterpreter_read_int(bytes); + ASPL_Enum* e = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->enums, identifier); + aspl_ailinterpreter_stack_push(context->stack, ASPL_ENUM_FIELD_LITERAL(e, int_field)); + break; + } + case ASPL_AILI_INSTRUCTION_DECLARE_POINTER: { + ASPL_OBJECT_TYPE* address; + switch (aspl_ailinterpreter_fetch_instruction(bytes)) { + case ASPL_AILI_INSTRUCTION_ACCESS_VARIABLE: { + address = aspl_ailinterpreter_access_variable_address(context, aspl_ailinterpreter_read_short_string(bytes)); + break; + } + default: + ASPL_PANIC("Cannot declare a pointer to an invalid address"); + } + char* to_type = aspl_object_get_type_pointer(*address); + char* type = ASPL_MALLOC(sizeof(char) * (strlen(to_type) + 1)); + strcpy(type, to_type); + type[strlen(to_type)] = '*'; + aspl_ailinterpreter_stack_push(context->stack, ASPL_REFERENCE(type, strlen(type), address)); + break; + } + case ASPL_AILI_INSTRUCTION_DEREFERENCE_POINTER: { + ASPL_OBJECT_TYPE pointer = aspl_ailinterpreter_stack_pop(context->stack); + aspl_ailinterpreter_stack_push(context->stack, ASPL_DEREFERENCE(pointer)); + break; + } + case ASPL_AILI_INSTRUCTION_DECLARE_CALLBACK: { + char* type = aspl_ailinterpreter_read_short_string(bytes); + ASPL_AILI_ParameterList parameters = aspl_ailinterpreter_parse_parameters(context, bytes); + ASPL_AILI_TypeList return_types = aspl_ailinterpreter_parse_types(bytes); + long long code_length = aspl_ailinterpreter_read_long(bytes); + ASPL_Callback* callback = ASPL_MALLOC(sizeof(ASPL_Callback)); + callback->typeLen = strlen(type); + callback->typePtr = type; + callback->function = 0; + callback->function = ASPL_MALLOC(sizeof(ASPL_AILI_CallbackData)); // TODO: This is a nasty hack to avoid having to add a new field to the ASPL_Callback struct + ((ASPL_AILI_CallbackData*)callback->function)->address = bytes->position; + ((ASPL_AILI_CallbackData*)callback->function)->creation_scopes = memcpy(ASPL_MALLOC(context->scopes_size * sizeof(hashmap_str_to_voidptr_HashMap*)), context->scopes, context->scopes_size * sizeof(hashmap_str_to_voidptr_HashMap*)); + ((ASPL_AILI_CallbackData*)callback->function)->creation_scopes_top = context->scopes_top; + ((ASPL_AILI_CallbackData*)callback->function)->parameters = parameters; + bytes->position += code_length; + ASPL_OBJECT_TYPE callback_object = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(callback_object).kind = ASPL_OBJECT_KIND_CALLBACK; + ASPL_ACCESS(callback_object).value.callback = callback; + aspl_ailinterpreter_stack_push(context->stack, callback_object); + break; + } + case ASPL_AILI_INSTRUCTION_CAST: { + char* target_type = aspl_ailinterpreter_read_short_string(bytes); + ASPL_OBJECT_TYPE object = aspl_ailinterpreter_stack_pop(context->stack); + char pushed = 0; + if (ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_INTEGER || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_ENUM_FIELD) { + if (hashmap_str_to_voidptr_hashmap_contains_key(context->environment_context->enums, target_type)) { + + pushed = 1; + switch (ASPL_ACCESS(object).kind) + { + case ASPL_OBJECT_KIND_INTEGER: + aspl_ailinterpreter_stack_push(context->stack, ASPL_ENUM_FIELD_LITERAL(hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->enums, target_type), ASPL_ACCESS(object).value.integer32)); + break; + case ASPL_OBJECT_KIND_ENUM_FIELD: + aspl_ailinterpreter_stack_push(context->stack, object); + break; + } + } + } + if (!pushed) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_CAST(object, target_type)); + } + break; + } + case ASPL_AILI_INSTRUCTION_COUNT_COLLECTION: { + ASPL_OBJECT_TYPE collection = aspl_ailinterpreter_stack_pop(context->stack); + switch (ASPL_ACCESS(collection).kind) { + case ASPL_OBJECT_KIND_LIST: + aspl_ailinterpreter_stack_push(context->stack, ASPL_LIST_LENGTH(collection)); + break; + case ASPL_OBJECT_KIND_MAP: + aspl_ailinterpreter_stack_push(context->stack, ASPL_MAP_LENGTH(collection)); + break; + case ASPL_OBJECT_KIND_STRING: + aspl_ailinterpreter_stack_push(context->stack, ASPL_STRING_LENGTH(collection)); + break; + default: + ASPL_PANIC("Cannot count a non-collection object"); + } + break; + } + case ASPL_AILI_INSTRUCTION_ACCESS_COLLECTION_KEY_WITH_INDEX: { + ASPL_OBJECT_TYPE index = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE collection = aspl_ailinterpreter_stack_pop(context->stack); + switch (ASPL_ACCESS(collection).kind) { + case ASPL_OBJECT_KIND_MAP: + aspl_ailinterpreter_stack_push(context->stack, ASPL_MAP_GET_KEY_FROM_INDEX(collection, index)); + break; + default: + ASPL_PANIC("Cannot access a key from something that is not a map"); + } + break; + } + case ASPL_AILI_INSTRUCTION_ACCESS_COLLECTION_VALUE_WITH_INDEX: { + ASPL_OBJECT_TYPE index = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE collection = aspl_ailinterpreter_stack_pop(context->stack); + switch (ASPL_ACCESS(collection).kind) { + case ASPL_OBJECT_KIND_LIST: + aspl_ailinterpreter_stack_push(context->stack, ASPL_LIST_GET(collection, index)); + break; + case ASPL_OBJECT_KIND_MAP: + aspl_ailinterpreter_stack_push(context->stack, ASPL_MAP_GET_VALUE_FROM_INDEX(collection, index)); + break; + case ASPL_OBJECT_KIND_STRING: + aspl_ailinterpreter_stack_push(context->stack, ASPL_STRING_INDEX(collection, index)); + break; + default: + ASPL_PANIC("Cannot access a value from something that is not a collection"); + } + break; + } + case ASPL_AILI_INSTRUCTION_INDEX_COLLECTION: { + ASPL_OBJECT_TYPE key = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE collection = aspl_ailinterpreter_stack_pop(context->stack); + switch (ASPL_ACCESS(collection).kind) { + case ASPL_OBJECT_KIND_LIST: + aspl_ailinterpreter_stack_push(context->stack, ASPL_LIST_GET(collection, key)); + break; + case ASPL_OBJECT_KIND_MAP: + aspl_ailinterpreter_stack_push(context->stack, ASPL_MAP_GET(collection, key)); + break; + case ASPL_OBJECT_KIND_STRING: + aspl_ailinterpreter_stack_push(context->stack, ASPL_STRING_INDEX(collection, key)); + break; + default: + ASPL_PANIC("Cannot index a non-collection object"); + } + break; + } + case ASPL_AILI_INSTRUCTION_UPDATE_COLLECTION: { + ASPL_OBJECT_TYPE value = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE key = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE collection = aspl_ailinterpreter_stack_pop(context->stack); + switch (ASPL_ACCESS(collection).kind) { + case ASPL_OBJECT_KIND_LIST: + aspl_ailinterpreter_stack_push(context->stack, ASPL_LIST_SET(collection, key, value)); + break; + case ASPL_OBJECT_KIND_MAP: + aspl_ailinterpreter_stack_push(context->stack, ASPL_MAP_SET(collection, key, value)); + break; + default: + ASPL_PANIC("Cannot update a non-collection object by an index"); + } + break; + } + case ASPL_AILI_INSTRUCTION_NEGATE: { + aspl_ailinterpreter_stack_push(context->stack, ASPL_NEGATE(aspl_ailinterpreter_stack_pop(context->stack))); + break; + } + case ASPL_AILI_INSTRUCTION_DECLARE_CLASS: { + char is_static = aspl_ailinterpreter_read_bool(bytes); + char* identifier = aspl_ailinterpreter_read_short_string(bytes); + short parents_size = aspl_ailinterpreter_read_short(bytes); + char** parents = ASPL_MALLOC(sizeof(char*) * parents_size); + for (int i = 0; i < parents_size; i++) { + parents[i] = aspl_ailinterpreter_read_short_string(bytes); + } + char is_error = aspl_ailinterpreter_read_bool(bytes); + ASPL_AILI_Class* class = ASPL_MALLOC(sizeof(ASPL_AILI_Class)); + class->is_static = is_static; + class->parents = parents; + class->parents_size = parents_size; + class->is_error = is_error; + aspl_ailinterpreter_register_class(context->environment_context, identifier, class); + aspl_ailinterpreter_class_stack_push(context->class_stack, context->current_class); + context->current_class = identifier; + hashmap_str_to_voidptr_hashmap_set(context->environment_context->custom_methods, context->current_class, hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 4 })); + aspl_ailinterpreter_access_class(context->environment_context, context->current_class)->default_threadlocal_static_property_values = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 4 }); + aspl_ailinterpreter_access_class(context->environment_context, context->current_class)->static_property_values = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 4 }); + aspl_ailinterpreter_access_class(context->environment_context, context->current_class)->reactive_properties = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 4 }); + break; + } + case ASPL_AILI_INSTRUCTION_NEW: { + char* identifier = aspl_ailinterpreter_read_short_string(bytes); + int argc = aspl_ailinterpreter_read_int(bytes); + ASPL_AILI_ArgumentList arguments = { .arguments = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * argc), .size = argc }; + for (int i = argc - 1; i >= 0; i--) { + arguments.arguments[i] = aspl_ailinterpreter_stack_pop(context->stack); + } + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_CLASS_INSTANCE; + ASPL_ClassInstance* instance = ASPL_MALLOC(sizeof(ASPL_ClassInstance)); + instance->typePtr = identifier; + instance->typeLen = strlen(identifier); + instance->properties = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 1 }); + instance->isError = aspl_ailinterpreter_access_class(context->environment_context, identifier)->is_error; + ASPL_ACCESS(obj).value.classInstance = instance; + + aspl_ailinterpreter_stack_push(context->stack, obj); + + hashmap_str_to_voidptr_HashMap* methods = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->custom_methods, identifier); + if (hashmap_str_to_voidptr_hashmap_contains_key(methods, "construct")) { + ASPL_AILI_CustomMethod* method = hashmap_str_to_voidptr_hashmap_get_value(methods, "construct"); + char* method_identifier = ASPL_MALLOC(sizeof(char) * (strlen(identifier) + strlen("construct") + 2)); + strcpy(method_identifier, identifier); + strcat(method_identifier, "."); + strcat(method_identifier, "construct"); + aspl_ailinterpreter_stack_trace_push(context, method_identifier); + aspl_ailinterpreter_stack_push_frame(context->stack); + aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, (ASPL_AILI_CallableInvocationMeta) { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_METHOD, .previous_instance = C_REFERENCE(context->current_instance), .previous_address = bytes->position, .is_constructor = 1, .previous_loop_meta_stack = context->loop_meta_stack }); + context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack)); + context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024); + context->loop_meta_stack->top = 0; + aspl_ailinterpreter_push_scope(context); + context->current_instance = obj; + bytes->position = method->address; + for (int i = 0; i < method->parameters.size; i++) { + if (i < arguments.size) { + aspl_ailinterpreter_register_variable(context, method->parameters.parameters[i].name, arguments.arguments[i]); + } + else if (method->parameters.parameters[i].optional) { + aspl_ailinterpreter_register_variable(context, method->parameters.parameters[i].name, aspl_object_clone_shallow(method->parameters.parameters[i].default_value)); + } + else { + ASPL_PANIC("Missing argument for parameter '%s'", method->parameters.parameters[i].name); + } + } + } + break; + } + case ASPL_AILI_INSTRUCTION_THIS: { + aspl_ailinterpreter_stack_push(context->stack, context->current_instance); + break; + } + case ASPL_AILI_INSTRUCTION_DECLARE_PROPERTY: { + char is_static = aspl_ailinterpreter_read_bool(bytes); + char is_thread_local = aspl_ailinterpreter_read_bool(bytes); + char* name = aspl_ailinterpreter_read_short_string(bytes); + ASPL_AILI_TypeList types = aspl_ailinterpreter_parse_types(bytes); + if (aspl_ailinterpreter_read_bool(bytes)) { + long long get_code_length = aspl_ailinterpreter_read_long(bytes); + ASPL_AILI_ReactiveProperty* reactive_property = ASPL_MALLOC(sizeof(ASPL_AILI_ReactiveProperty)); + if (get_code_length > -1) { + reactive_property->get_address = bytes->position; + bytes->position += get_code_length; + } + long long set_code_length = aspl_ailinterpreter_read_long(bytes); + if (set_code_length > -1) { + reactive_property->set_address = bytes->position; + bytes->position += set_code_length; + } + hashmap_str_to_voidptr_hashmap_set(aspl_ailinterpreter_access_class(context->environment_context, context->current_class)->reactive_properties, name, reactive_property); + } + else { + if (is_static) { + ASPL_OBJECT_TYPE default_value = aspl_ailinterpreter_stack_pop(context->stack); + if (is_thread_local) { + hashmap_str_to_voidptr_HashMap* default_threadlocal_static_property_values = aspl_ailinterpreter_access_class(context->environment_context, context->current_class)->default_threadlocal_static_property_values; + hashmap_str_to_voidptr_hashmap_set(default_threadlocal_static_property_values, name, C_REFERENCE(default_value)); + if (!hashmap_str_to_voidptr_hashmap_contains_key(context->local_static_property_values, context->current_class)) { + hashmap_str_to_voidptr_hashmap_set(context->local_static_property_values, context->current_class, hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 4 })); + } + hashmap_str_to_voidptr_hashmap_set(hashmap_str_to_voidptr_hashmap_get_value(context->local_static_property_values, context->current_class), name, C_REFERENCE(default_value)); + } + else { + hashmap_str_to_voidptr_HashMap* static_property_values = aspl_ailinterpreter_access_class(context->environment_context, context->current_class)->static_property_values; + hashmap_str_to_voidptr_hashmap_set(static_property_values, name, C_REFERENCE(default_value)); + } + } + else { + // TOOD: Do we need anything here? + } + } + break; + } + case ASPL_AILI_INSTRUCTION_ACCESS_PROPERTY: { + char is_static = aspl_ailinterpreter_read_bool(bytes); + if (is_static) { + char* type = aspl_ailinterpreter_read_short_string(bytes); + char* name = aspl_ailinterpreter_read_short_string(bytes); + if (hashmap_str_to_voidptr_hashmap_contains_key(aspl_ailinterpreter_access_class(context->environment_context, type)->reactive_properties, name)) { + ASPL_AILI_ReactiveProperty* reactive_property = hashmap_str_to_voidptr_hashmap_get_value(aspl_ailinterpreter_access_class(context->environment_context, type)->reactive_properties, name); + char* identifier = ASPL_MALLOC(sizeof(char) * (strlen(type) + strlen(name) + 2)); + strcpy(identifier, type); + strcat(identifier, "."); + strcat(identifier, name); + aspl_ailinterpreter_stack_trace_push(context, identifier); + aspl_ailinterpreter_stack_push_frame(context->stack); + aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, (ASPL_AILI_CallableInvocationMeta) { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_REACTIVE_PROPERTY, .previous_address = bytes->position, .previous_loop_meta_stack = context->loop_meta_stack }); + context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack)); + context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024); + context->loop_meta_stack->top = 0; + aspl_ailinterpreter_push_scope(context); + bytes->position = reactive_property->get_address; + } + else if (hashmap_str_to_voidptr_hashmap_contains_key(context->local_static_property_values, type) && hashmap_str_to_voidptr_hashmap_contains_key(hashmap_str_to_voidptr_hashmap_get_value(context->local_static_property_values, type), name)) { + aspl_ailinterpreter_stack_push(context->stack, *(ASPL_OBJECT_TYPE*)hashmap_str_to_voidptr_hashmap_get_value(hashmap_str_to_voidptr_hashmap_get_value(context->local_static_property_values, type), name)); + } + else { + hashmap_str_to_voidptr_HashMap* static_property_values = aspl_ailinterpreter_access_class(context->environment_context, type)->static_property_values; + aspl_ailinterpreter_stack_push(context->stack, *(ASPL_OBJECT_TYPE*)hashmap_str_to_voidptr_hashmap_get_value(static_property_values, name)); + } + } + else { + char* name = aspl_ailinterpreter_read_short_string(bytes); + ASPL_OBJECT_TYPE object = aspl_ailinterpreter_stack_pop(context->stack); + if (ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_CLASS_INSTANCE) { + if (hashmap_str_to_voidptr_hashmap_contains_key(aspl_ailinterpreter_access_class(context->environment_context, aspl_object_get_short_type_pointer(object))->reactive_properties, name)) { // TODO: Support reactive properties in parents + ASPL_AILI_ReactiveProperty* reactive_property = hashmap_str_to_voidptr_hashmap_get_value(aspl_ailinterpreter_access_class(context->environment_context, aspl_object_get_short_type_pointer(object))->reactive_properties, name); + char* identifier = ASPL_MALLOC(sizeof(char) * (strlen(aspl_object_get_short_type_pointer(object)) + strlen(name) + 2)); + strcpy(identifier, aspl_object_get_short_type_pointer(object)); + strcat(identifier, "."); + strcat(identifier, name); + aspl_ailinterpreter_stack_trace_push(context, identifier); + aspl_ailinterpreter_stack_push_frame(context->stack); + aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, (ASPL_AILI_CallableInvocationMeta) { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_REACTIVE_PROPERTY, .previous_address = bytes->position, .previous_instance = C_REFERENCE(context->current_instance), .previous_loop_meta_stack = context->loop_meta_stack }); + context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack)); + context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024); + context->loop_meta_stack->top = 0; + aspl_ailinterpreter_push_scope(context); + context->current_instance = object; + bytes->position = reactive_property->get_address; + } + else { + aspl_ailinterpreter_stack_push(context->stack, ASPL_CLASS_INSTANCE_GET_PROPERTY(ASPL_ACCESS(object).value.classInstance, name)); + } + } + else { + switch (ASPL_ACCESS(object).kind) { + case ASPL_OBJECT_KIND_STRING: + if (strcmp(name, "length") == 0) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_STRING_LENGTH(object)); + } + else { + ASPL_PANIC("Cannot access the unknown property string.%s", name); + } + break; + case ASPL_OBJECT_KIND_LIST: + if (strcmp(name, "length") == 0) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_LIST_LENGTH(object)); + } + else { + ASPL_PANIC("Cannot access the unknown property list.%s", name); + } + break; + case ASPL_OBJECT_KIND_MAP: + if (strcmp(name, "length") == 0) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_MAP_LENGTH(object)); + } + else { + ASPL_PANIC("Cannot access the unknown property map.%s", name); + } + break; + default: + ASPL_PANIC("Cannot access the unknown property %s.%s", aspl_object_get_type_pointer(object), name); + } + } + } + break; + } + case ASPL_AILI_INSTRUCTION_UPDATE_PROPERTY: { + char is_static = aspl_ailinterpreter_read_bool(bytes); + char is_reactive = aspl_ailinterpreter_read_bool(bytes); + ASPL_OBJECT_TYPE value = aspl_ailinterpreter_stack_pop(context->stack); + if (is_static) { + char* type = aspl_ailinterpreter_read_short_string(bytes); + char* name = aspl_ailinterpreter_read_short_string(bytes); + if (is_reactive) { + ASPL_AILI_ReactiveProperty* reactive_property = hashmap_str_to_voidptr_hashmap_get_value(aspl_ailinterpreter_access_class(context->environment_context, type)->reactive_properties, name); + char* identifier = ASPL_MALLOC(sizeof(char) * (strlen(type) + strlen(name) + 2)); + strcpy(identifier, type); + strcat(identifier, "."); + strcat(identifier, name); + aspl_ailinterpreter_stack_trace_push(context, identifier); + aspl_ailinterpreter_stack_push_frame(context->stack); + aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, (ASPL_AILI_CallableInvocationMeta) { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_REACTIVE_PROPERTY, .previous_address = bytes->position, .previous_loop_meta_stack = context->loop_meta_stack, .is_reactive_property_setter = 1 }); + context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack)); + context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024); + context->loop_meta_stack->top = 0; + aspl_ailinterpreter_push_scope(context); + bytes->position = reactive_property->set_address; + aspl_ailinterpreter_register_variable(context, "value", value); + } + else if (hashmap_str_to_voidptr_hashmap_contains_key(context->local_static_property_values, type) && hashmap_str_to_voidptr_hashmap_contains_key(hashmap_str_to_voidptr_hashmap_get_value(context->local_static_property_values, type), name)) { + *(ASPL_OBJECT_TYPE*)hashmap_str_to_voidptr_hashmap_get_value(hashmap_str_to_voidptr_hashmap_get_value(context->local_static_property_values, type), name) = value; + } + else { + hashmap_str_to_voidptr_HashMap* static_property_values = aspl_ailinterpreter_access_class(context->environment_context, type)->static_property_values; + hashmap_str_to_voidptr_hashmap_set(static_property_values, name, C_REFERENCE(value)); + } + } + else { + char* name = aspl_ailinterpreter_read_short_string(bytes); + ASPL_OBJECT_TYPE object = aspl_ailinterpreter_stack_pop(context->stack); + if (is_reactive) { + ASPL_AILI_ReactiveProperty* reactive_property = aspl_ailinterpreter_util_find_reactive_property(context, aspl_object_get_short_type_pointer(object), name); + char* identifier = ASPL_MALLOC(sizeof(char) * (strlen(aspl_object_get_short_type_pointer(object)) + strlen(name) + 2)); + strcpy(identifier, aspl_object_get_short_type_pointer(object)); + strcat(identifier, "."); + strcat(identifier, name); + aspl_ailinterpreter_stack_trace_push(context, identifier); + aspl_ailinterpreter_stack_push_frame(context->stack); + aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, (ASPL_AILI_CallableInvocationMeta) { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_REACTIVE_PROPERTY, .previous_address = bytes->position, .previous_instance = C_REFERENCE(context->current_instance), .previous_loop_meta_stack = context->loop_meta_stack, .is_reactive_property_setter = 1 }); + context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack)); + context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024); + context->loop_meta_stack->top = 0; + aspl_ailinterpreter_push_scope(context); + context->current_instance = object; + bytes->position = reactive_property->set_address; + aspl_ailinterpreter_register_variable(context, "value", value); + } + else { + ASPL_CLASS_INSTANCE_SET_PROPERTY(ASPL_ACCESS(object).value.classInstance, name, value); + } + } + if (!is_reactive) { + aspl_ailinterpreter_stack_push(context->stack, value); + } + break; + } + case ASPL_AILI_INSTRUCTION_ADD: { + ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack); + aspl_ailinterpreter_stack_push(context->stack, ASPL_PLUS(a, b)); + break; + } + case ASPL_AILI_INSTRUCTION_INCREMENT: { + ASPL_OBJECT_TYPE object = aspl_ailinterpreter_stack_pop(context->stack); + switch (ASPL_ACCESS(object).kind) { + case ASPL_OBJECT_KIND_BYTE: + aspl_ailinterpreter_stack_push(context->stack, ASPL_BYTE_LITERAL(ASPL_ACCESS(object).value.integer8 + 1)); + break; + case ASPL_OBJECT_KIND_INTEGER: + aspl_ailinterpreter_stack_push(context->stack, ASPL_INT_LITERAL(ASPL_ACCESS(object).value.integer32 + 1)); + break; + case ASPL_OBJECT_KIND_LONG: + aspl_ailinterpreter_stack_push(context->stack, ASPL_LONG_LITERAL(ASPL_ACCESS(object).value.integer64 + 1)); + break; + case ASPL_OBJECT_KIND_FLOAT: + aspl_ailinterpreter_stack_push(context->stack, ASPL_FLOAT_LITERAL(ASPL_ACCESS(object).value.float32 + 1)); + break; + case ASPL_OBJECT_KIND_DOUBLE: + aspl_ailinterpreter_stack_push(context->stack, ASPL_DOUBLE_LITERAL(ASPL_ACCESS(object).value.float64 + 1)); + break; + default: + ASPL_PANIC("Unknown or invalid object type for the ++ operator"); + } + break; + } + case ASPL_AILI_INSTRUCTION_SUBTRACT: { + ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack); + aspl_ailinterpreter_stack_push(context->stack, ASPL_MINUS(a, b)); + break; + } + case ASPL_AILI_INSTRUCTION_DECREMENT: { + ASPL_OBJECT_TYPE object = aspl_ailinterpreter_stack_pop(context->stack); + switch (ASPL_ACCESS(object).kind) { + case ASPL_OBJECT_KIND_BYTE: + aspl_ailinterpreter_stack_push(context->stack, ASPL_BYTE_LITERAL(ASPL_ACCESS(object).value.integer8 - 1)); + break; + case ASPL_OBJECT_KIND_INTEGER: + aspl_ailinterpreter_stack_push(context->stack, ASPL_INT_LITERAL(ASPL_ACCESS(object).value.integer32 - 1)); + break; + case ASPL_OBJECT_KIND_LONG: + aspl_ailinterpreter_stack_push(context->stack, ASPL_LONG_LITERAL(ASPL_ACCESS(object).value.integer64 - 1)); + break; + case ASPL_OBJECT_KIND_FLOAT: + aspl_ailinterpreter_stack_push(context->stack, ASPL_FLOAT_LITERAL(ASPL_ACCESS(object).value.float32 - 1)); + break; + case ASPL_OBJECT_KIND_DOUBLE: + aspl_ailinterpreter_stack_push(context->stack, ASPL_DOUBLE_LITERAL(ASPL_ACCESS(object).value.float64 - 1)); + break; + default: + ASPL_PANIC("Unknown or invalid object type for the -- operator"); + } + break; + } + case ASPL_AILI_INSTRUCTION_MULTIPLY: { + ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack); + aspl_ailinterpreter_stack_push(context->stack, ASPL_MULTIPLY(a, b)); + break; + } + case ASPL_AILI_INSTRUCTION_DIVIDE: { + ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack); + aspl_ailinterpreter_stack_push(context->stack, ASPL_DIVIDE(a, b)); + break; + } + case ASPL_AILI_INSTRUCTION_MODULO: { + ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack); + aspl_ailinterpreter_stack_push(context->stack, ASPL_MODULO(a, b)); + break; + } + case ASPL_AILI_INSTRUCTION_SMALLER_THAN: { + ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack); + aspl_ailinterpreter_stack_push(context->stack, ASPL_LESS_THAN(a, b)); + break; + } + case ASPL_AILI_INSTRUCTION_SMALLER_EQUAL: { + ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack); + aspl_ailinterpreter_stack_push(context->stack, ASPL_LESS_THAN_OR_EQUAL(a, b)); + break; + } + case ASPL_AILI_INSTRUCTION_GREATER_THAN: { + ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack); + aspl_ailinterpreter_stack_push(context->stack, ASPL_GREATER_THAN(a, b)); + break; + } + case ASPL_AILI_INSTRUCTION_GREATER_EQUAL: { + ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack); + aspl_ailinterpreter_stack_push(context->stack, ASPL_GREATER_THAN_OR_EQUAL(a, b)); + break; + } + case ASPL_AILI_INSTRUCTION_EQUALS: { + ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack); + aspl_ailinterpreter_stack_push(context->stack, ASPL_EQUALS(a, b)); + break; + } + case ASPL_AILI_INSTRUCTION_NOT_EQUALS: { + ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack); + aspl_ailinterpreter_stack_push(context->stack, ASPL_NEGATE(ASPL_EQUALS(a, b))); + break; + } + case ASPL_AILI_INSTRUCTION_BOOLEAN_AND: { + ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack); + int length = aspl_ailinterpreter_read_int(bytes); + if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_BOOLEAN) { + if (!ASPL_ACCESS(a).value.boolean) { + bytes->position += length; + aspl_ailinterpreter_stack_push(context->stack, ASPL_FALSE()); + } + // b is now just evaluated normally + } + else { + ASPL_PANIC("Cannot use the boolean AND (&&) operator on a non-boolean value"); + } + break; + } + case ASPL_AILI_INSTRUCTION_BITWISE_AND: { + ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack); + if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_BYTE && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_BYTE) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_BYTE_LITERAL(ASPL_ACCESS(a).value.integer8 & ASPL_ACCESS(b).value.integer8)); + } + else if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_INTEGER && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_INTEGER) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_INT_LITERAL(ASPL_ACCESS(a).value.integer32 & ASPL_ACCESS(b).value.integer32)); + } + else if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_LONG && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_LONG) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_LONG_LITERAL(ASPL_ACCESS(a).value.integer64 & ASPL_ACCESS(b).value.integer64)); + } + else if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_ENUM_FIELD && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_ENUM_FIELD) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_ENUM_AND(a, b)); + } + else { + ASPL_PANIC("Invalid types for the & operator"); + } + break; + } + case ASPL_AILI_INSTRUCTION_BOOLEAN_OR: { + ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack); + int length = aspl_ailinterpreter_read_int(bytes); + if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_BOOLEAN) { + if (ASPL_ACCESS(a).value.boolean) { + bytes->position += length; + aspl_ailinterpreter_stack_push(context->stack, ASPL_TRUE()); + } + // b is now just evaluated normally + } + else { + ASPL_PANIC("Cannot use the boolean OR (||) operator on a non-boolean value"); + } + break; + } + case ASPL_AILI_INSTRUCTION_BITWISE_OR: { + ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack); + if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_BYTE && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_BYTE) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_BYTE_LITERAL(ASPL_ACCESS(a).value.integer8 | ASPL_ACCESS(b).value.integer8)); + } + else if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_INTEGER && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_INTEGER) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_INT_LITERAL(ASPL_ACCESS(a).value.integer32 | ASPL_ACCESS(b).value.integer32)); + } + else if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_LONG && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_LONG) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_LONG_LITERAL(ASPL_ACCESS(a).value.integer64 | ASPL_ACCESS(b).value.integer64)); + } + else if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_ENUM_FIELD && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_ENUM_FIELD) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_ENUM_OR(a, b)); + } + else { + ASPL_PANIC("Invalid types for the | operator"); + } + break; + } + case ASPL_AILI_INSTRUCTION_BOOLEAN_XOR: { + ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack); + if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_BOOLEAN && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_BOOLEAN) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_BOOL_LITERAL(ASPL_ACCESS(a).value.boolean ^ ASPL_ACCESS(b).value.boolean)); + } + else { + ASPL_PANIC("Invalid types for the ^ operator"); + } + break; + } + case ASPL_AILI_INSTRUCTION_BITWISE_XOR: { + ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack); + if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_BYTE && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_BYTE) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_BYTE_LITERAL(ASPL_ACCESS(a).value.integer8 ^ ASPL_ACCESS(b).value.integer8)); + } + else if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_INTEGER && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_INTEGER) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_INT_LITERAL(ASPL_ACCESS(a).value.integer32 ^ ASPL_ACCESS(b).value.integer32)); + } + else if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_LONG && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_LONG) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_LONG_LITERAL(ASPL_ACCESS(a).value.integer64 ^ ASPL_ACCESS(b).value.integer64)); + } + else if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_ENUM_FIELD && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_ENUM_FIELD) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_ENUM_XOR(a, b)); + } + else { + ASPL_PANIC("Invalid types for the ^ operator"); + } + break; + } + case ASPL_AILI_INSTRUCTION_ASSERT: { + char* file = aspl_ailinterpreter_read_short_string(bytes); + int line = aspl_ailinterpreter_read_int(bytes); + int column = aspl_ailinterpreter_read_int(bytes); + ASPL_OBJECT_TYPE value = aspl_ailinterpreter_stack_pop(context->stack); + ASPL_ASSERT(value, file, line, column); + break; + } + case ASPL_AILI_INSTRUCTION_OFTYPE: { + ASPL_OBJECT_TYPE object = aspl_ailinterpreter_stack_pop(context->stack); + char* type = aspl_ailinterpreter_read_short_string(bytes); + if (ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_CLASS_INSTANCE) { + aspl_ailinterpreter_stack_push(context->stack, ASPL_BOOL_LITERAL(aspl_ailinterpreter_util_is_class_child_of(context, ASPL_ACCESS(object).value.classInstance->typePtr, type))); + } + else { + aspl_ailinterpreter_stack_push(context->stack, ASPL_OFTYPE(object, type)); + } + break; + } + case ASPL_AILI_INSTRUCTION_SPECIFY_LOOP: { + long long body_length = aspl_ailinterpreter_read_long(bytes); + long long full_length = aspl_ailinterpreter_read_long(bytes); + aspl_ailinterpreter_lms_push(context->loop_meta_stack, (ASPL_AILI_LoopMeta) { .body_end_address = bytes->position + body_length, .full_end_address = bytes->position + full_length }); + break; + } + case ASPL_AILI_INSTRUCTION_THROW: { + ASPL_OBJECT_TYPE error_instance = aspl_ailinterpreter_stack_pop(context->stack); + aspl_ailinterpreter_return(context, bytes, error_instance); + break; + } + case ASPL_AILI_INSTRUCTION_CATCH: { + char* variable = aspl_ailinterpreter_read_short_string(bytes); + long long body_length = aspl_ailinterpreter_read_long(bytes); + ASPL_OBJECT_TYPE value = aspl_ailinterpreter_stack_pop(context->stack); + if (aspl_object_is_error(value)) { + aspl_ailinterpreter_stack_trace_push(context, "catch_block"); // TODO + aspl_ailinterpreter_stack_push_frame(context->stack); + aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, (ASPL_AILI_CallableInvocationMeta) { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_CATCH_BLOCK, .previous_address = bytes->position + body_length, .previous_loop_meta_stack = context->loop_meta_stack }); + if (strlen(variable) > 0) { + aspl_ailinterpreter_register_variable(context, variable, value); + } + } + else { + aspl_ailinterpreter_stack_push(context->stack, value); + bytes->position += body_length; + } + break; + } + case ASPL_AILI_INSTRUCTION_PROPAGATE_ERROR: { + ASPL_OBJECT_TYPE value = aspl_ailinterpreter_stack_pop(context->stack); + if (aspl_object_is_error(value)) { + int n = context->stack->frames[context->stack->current_frame_index].top; + for (int i = 0; i < n; i++) { + aspl_ailinterpreter_stack_pop(context->stack); + } + if (context->callable_invocation_meta_stack->top > 0) { + aspl_ailinterpreter_return(context, bytes, value); + } + else { + ASPL_PANIC("Uncaught %s error", ASPL_ACCESS(value).value.classInstance->typePtr); + } + } + else { + aspl_ailinterpreter_stack_push(context->stack, value); + } + break; + } + case ASPL_AILI_INSTRUCTION_POP_LOOP: { + aspl_ailinterpreter_lms_pop(context->loop_meta_stack); + break; + } + case ASPL_AILI_INSTRUCTION_POP: { + aspl_ailinterpreter_stack_pop(context->stack); + break; + } + case ASPL_AILI_INSTRUCTION_END: + if (context->callable_invocation_meta_stack->top > 0) { + aspl_ailinterpreter_return(context, bytes, ASPL_NULL()); + break; + } + else if (context->class_stack->top > 0) { + context->current_class = aspl_ailinterpreter_class_stack_pop(context->class_stack); + break; + } + else { + return; + } + default: + ASPL_PANIC("Unknown instruction %d", instruction); + } + } +} \ No newline at end of file diff --git a/runtime/ailinterpreter/interpreter.h b/runtime/ailinterpreter/interpreter.h new file mode 100644 index 0000000..daa3a79 --- /dev/null +++ b/runtime/ailinterpreter/interpreter.h @@ -0,0 +1,261 @@ +#ifndef ASPL_AILI_INTERPRETER_H_INCLUDED +#define ASPL_AILI_INTERPRETER_H_INCLUDED + +#define ASPL_INTERPRETER_MODE + +#include "byte_list.h" +#include "interpreter.h" + +#ifndef ASPL_TEMPLATE_INCLUDED +#include "../../stdlib/aspl/compiler/backend/stringcode/c/template.c" +#define ASPL_TEMPLATE_INCLUDED +#endif + +typedef struct { + hashmap_str_to_voidptr_HashMap* functions; + hashmap_str_to_voidptr_HashMap* builtin_functions; + hashmap_str_to_voidptr_HashMap* custom_functions; + hashmap_str_to_voidptr_HashMap* builtin_methods; + hashmap_str_to_voidptr_HashMap* custom_methods; + hashmap_str_to_voidptr_HashMap* classes; + hashmap_str_to_voidptr_HashMap* enums; +} ASPL_AILI_EnvironmentContext; + +typedef struct ASPL_AILI_StackFrame{ + ASPL_OBJECT_TYPE* data; + int top; +} ASPL_AILI_StackFrame; + +typedef struct ASPL_AILI_Stack { + ASPL_AILI_StackFrame* frames; + int frames_size; + int current_frame_index; +} ASPL_AILI_Stack; + +typedef enum ASPL_AILI_CallableType { + ASPL_AILINTERPRETER_CALLABLE_TYPE_FUNCTION, + ASPL_AILINTERPRETER_CALLABLE_TYPE_METHOD, + ASPL_AILINTERPRETER_CALLABLE_TYPE_CALLBACK, + ASPL_AILINTERPRETER_CALLABLE_TYPE_REACTIVE_PROPERTY, + ASPL_AILINTERPRETER_CALLABLE_TYPE_CATCH_BLOCK +} ASPL_AILI_CallableType; + +typedef struct ASPL_AILI_LoopMeta { + long long body_end_address; + long long full_end_address; +} ASPL_AILI_LoopMeta; + +typedef struct ASPL_AILI_LoopMetaStack { + ASPL_AILI_LoopMeta* data; + int top; +} ASPL_AILI_LoopMetaStack; + +typedef struct ASPL_AILI_ClassStack { + char** data; + int top; +} ASPL_AILI_ClassStack; + +typedef struct ASPL_AILI_CallableInvocationMeta { + ASPL_AILI_CallableType type; + long long previous_address; + hashmap_str_to_voidptr_HashMap** previous_scopes; + int previous_scopes_top; + ASPL_OBJECT_TYPE* previous_instance; + ASPL_AILI_LoopMetaStack* previous_loop_meta_stack; + char is_constructor; + char is_reactive_property_setter; +} ASPL_AILI_CallableInvocationMeta; + +typedef struct ASPL_AILI_CallableInvocationMetaStack { + ASPL_AILI_CallableInvocationMeta* data; + int top; +} ASPL_AILI_CallableInvocationMetaStack; + +typedef struct ASPL_AILI_TypeList { + char** types; + int size; +} ASPL_AILI_TypeList; + +typedef struct ASPL_AILI_Parameter { + char* name; + ASPL_AILI_TypeList expected_types; + int optional; + ASPL_OBJECT_TYPE default_value; +} ASPL_AILI_Parameter; + +typedef struct ASPL_AILI_ParameterList { + ASPL_AILI_Parameter* parameters; + int size; +} ASPL_AILI_ParameterList; + +typedef struct ASPL_AILI_BuiltinFunction { + ASPL_AILI_ParameterList parameters; +} ASPL_AILI_BuiltinFunction; + +typedef struct ASPL_AILI_CustomFunction { + int address; + ASPL_AILI_ParameterList parameters; +} ASPL_AILI_CustomFunction; + +typedef struct ASPL_AILI_BuiltinMethod { + // static builtin methods are not supported yet in ASPL + ASPL_AILI_ParameterList parameters; +} ASPL_AILI_BuiltinMethod; + +typedef struct ASPL_AILI_CustomMethod { + int address; + char is_static; + ASPL_AILI_ParameterList parameters; +} ASPL_AILI_CustomMethod; + +typedef struct ASPL_AILI_ReactiveProperty { + int get_address; + int set_address; +} ASPL_AILI_ReactiveProperty; + +typedef struct ASPL_AILI_Class { + char is_static; + char** parents; + int parents_size; + char is_error; + hashmap_str_to_voidptr_HashMap* default_threadlocal_static_property_values; + hashmap_str_to_voidptr_HashMap* static_property_values; + hashmap_str_to_voidptr_HashMap* reactive_properties; +} ASPL_AILI_Class; + +typedef struct ASPL_AILI_MemoryAddress { + ASPL_OBJECT_TYPE object; +} ASPL_AILI_MemoryAddress; + +typedef struct ASPL_AILI_StackTrace { + char** frames; + int size; + int top; +} ASPL_AILI_StackTrace; + +typedef struct ASPL_AILI_ThreadContext { + ASPL_AILI_EnvironmentContext* environment_context; + ASPL_AILI_Stack* stack; + ASPL_AILI_CallableInvocationMetaStack* callable_invocation_meta_stack; + ASPL_AILI_LoopMetaStack* loop_meta_stack; + ASPL_AILI_ClassStack* class_stack; + hashmap_str_to_voidptr_HashMap** scopes; + int scopes_size; + int scopes_top; + char* current_class; // only used for declaring properties and methods + ASPL_OBJECT_TYPE current_instance; + hashmap_str_to_voidptr_HashMap* local_static_property_values; + ASPL_AILI_StackTrace* stack_trace; +} ASPL_AILI_ThreadContext; + +typedef struct ASPL_AILI_ArgumentList { + ASPL_OBJECT_TYPE* arguments; + int size; +} ASPL_AILI_ArgumentList; + +typedef void(*ASPL_AILI_FunctionPtr)(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, char* identifier, ASPL_AILI_ArgumentList arguments); + +typedef struct ASPL_AILI_ThreadFunctionWrapperData { + ASPL_AILI_FunctionPtr function; + ASPL_AILI_ThreadContext* context; + ASPL_AILI_ByteList* bytes; + char* identifier; + ASPL_AILI_ArgumentList arguments; +} ASPL_AILI_ThreadFunctionWrapperData; + +typedef struct ASPL_AILI_ThreadMethodWrapperData { + ASPL_AILI_CustomMethod* method; + ASPL_AILI_ThreadContext* context; + ASPL_AILI_ByteList* bytes; + char* identifier; + ASPL_AILI_ArgumentList arguments; +} ASPL_AILI_ThreadMethodWrapperData; + +typedef struct ASPL_AILI_ThreadCallbackWrapperData { + ASPL_Callback* callback; + ASPL_AILI_ThreadContext* context; + ASPL_AILI_ByteList* bytes; + char* identifier; + ASPL_AILI_ArgumentList arguments; +} ASPL_AILI_ThreadCallbackWrapperData; + +typedef struct ASPL_AILI_CallbackData { + long long address; + hashmap_str_to_voidptr_HashMap** creation_scopes; + int creation_scopes_top; + ASPL_AILI_ParameterList parameters; +} ASPL_AILI_CallbackData; + +ASPL_AILI_EnvironmentContext* aspl_ailinterpreter_new_environment_context(); + +ASPL_AILI_ThreadContext* aspl_ailinterpreter_new_thread_context(ASPL_AILI_EnvironmentContext* environment_context); + +void aspl_ailinterpreter_stack_push_frame(ASPL_AILI_Stack* stack); + +void aspl_ailinterpreter_stack_push(ASPL_AILI_Stack* stack, ASPL_OBJECT_TYPE object); + +ASPL_AILI_StackFrame aspl_ailinterpreter_stack_pop_frame(ASPL_AILI_Stack* stack); + +ASPL_OBJECT_TYPE aspl_ailinterpreter_stack_pop(ASPL_AILI_Stack* stack); + +ASPL_OBJECT_TYPE aspl_ailinterpreter_stack_peek(ASPL_AILI_Stack* stack); + +void aspl_ailinterpreter_cims_push(ASPL_AILI_CallableInvocationMetaStack* stack, ASPL_AILI_CallableInvocationMeta value); + +ASPL_AILI_CallableInvocationMeta aspl_ailinterpreter_cims_pop(ASPL_AILI_CallableInvocationMetaStack* stack); + +ASPL_AILI_CallableInvocationMeta aspl_ailinterpreter_cims_peek(ASPL_AILI_CallableInvocationMetaStack* stack); + +void aspl_ailinterpreter_lms_push(ASPL_AILI_LoopMetaStack* stack, ASPL_AILI_LoopMeta value); + +ASPL_AILI_LoopMeta aspl_ailinterpreter_lms_pop(ASPL_AILI_LoopMetaStack* stack); + +ASPL_AILI_LoopMeta aspl_ailinterpreter_lms_peek(ASPL_AILI_LoopMetaStack* stack); + +void aspl_ailinterpreter_class_stack_push(ASPL_AILI_ClassStack* stack, char* object); + +char* aspl_ailinterpreter_class_stack_pop(ASPL_AILI_ClassStack* stack); + +void aspl_ailinterpreter_push_scope(ASPL_AILI_ThreadContext* context); + +void aspl_ailinterpreter_pop_scope(ASPL_AILI_ThreadContext* context); + +void aspl_ailinterpreter_register_variable(ASPL_AILI_ThreadContext* context, char* identifier, ASPL_OBJECT_TYPE value); + +ASPL_OBJECT_TYPE* aspl_ailinterpreter_access_variable_address(ASPL_AILI_ThreadContext* context, char* identifier); + +ASPL_OBJECT_TYPE aspl_ailinterpreter_access_variable(ASPL_AILI_ThreadContext* context, char* identifier); + +void aspl_ailinterpreter_register_class(ASPL_AILI_EnvironmentContext* context, char* identifier, ASPL_AILI_Class* class); + +ASPL_AILI_Class* aspl_ailinterpreter_access_class(ASPL_AILI_EnvironmentContext* context, char* identifier); + +ASPL_AILI_TypeList aspl_ailinterpreter_parse_types(ASPL_AILI_ByteList* bytes); + +ASPL_AILI_ParameterList aspl_ailinterpreter_parse_parameters(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes); + +char* aspl_ailinterpreter_construct_list_from_type_list(ASPL_AILI_TypeList type_list); + +char* aspl_ailinterpreter_construct_map_from_type_lists(ASPL_AILI_TypeList key_type_list, ASPL_AILI_TypeList value_type_list); + +void aspl_ailinterpreter_return(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, ASPL_OBJECT_TYPE value); + +ASPL_AILI_FunctionPtr aspl_ailinterpreter_get_function(ASPL_AILI_ThreadContext* context, char* identifier); + +void aspl_ailinterpreter_custom_function_callback(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, char* identifier, ASPL_AILI_ArgumentList arguments); + +ASPL_AILI_CustomMethod* aspl_ailinterpreter_util_find_custom_method(ASPL_AILI_ThreadContext* context, char* type, char* name); + +ASPL_AILI_ReactiveProperty* aspl_ailinterpreter_util_find_reactive_property(ASPL_AILI_ThreadContext* context, char* type, char* name); + +void aspl_ailinterpreter_display_stack_trace(ASPL_AILI_ThreadContext* context); + +void aspl_ailinterpreter_invoke_callback_from_outside_of_loop_internal(ASPL_Callback* callback, ASPL_AILI_ArgumentList arguments, ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes); + +void aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_Callback* callback, ASPL_AILI_ArgumentList arguments); + +int aspl_ailinterpreter_new_thread_function_invocation_callback(void* arguments); + +void aspl_ailinterpreter_loop(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes); + +#endif \ No newline at end of file diff --git a/runtime/ailinterpreter/main.c b/runtime/ailinterpreter/main.c new file mode 100644 index 0000000..eb48aed --- /dev/null +++ b/runtime/ailinterpreter/main.c @@ -0,0 +1,64 @@ +#include "interpreter.c" + +#define APPBUNDLE_MALLOC ASPL_MALLOC +#define APPBUNDLE_REALLOC ASPL_REALLOC +#define APPBUNDLE_FREE ASPL_FREE +#include "thirdparty/appbundle/loader.c" + +ASPL_AILI_ByteList* aspl_ailinterpreter_read_file_into_byte_list(char* filename) { + FILE* file = fopen(filename, "rb"); + fseek(file, 0, SEEK_END); + long length = ftell(file); + fseek(file, 0, SEEK_SET); + unsigned char* buffer = ASPL_MALLOC(sizeof(char) * length); + fread(buffer, 1, length, file); + fclose(file); + ASPL_AILI_ByteList* result = ASPL_MALLOC(sizeof(ASPL_AILI_ByteList)); + result->size = length; + result->position = 0; + result->data = buffer; + return result; +} + +int main(int argc, char** argv) { + aspl_argc = argc; + aspl_argv = argv; + aspl_setup_gc(); + +#ifdef _WIN32 + aspl_setup_windows_console(); +#endif + + aspl_setup_builtin_method_pointers(); + class_parents_map = *hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 64 }); // TODO: Is this required? + + ASPL_AILI_EnvironmentContext* environment_context = aspl_ailinterpreter_new_environment_context(); + ASPL_AILI_ThreadContext* thread_context = aspl_ailinterpreter_new_thread_context(environment_context); + aspl_ailinterpreter_current_thread_context = thread_context; + + +#ifdef ASPL_AILI_BUNDLED + appbundle_Bundle* bundle = appbundle_Bundle_load(); + appbundle_Resource resource = appbundle_Bundle_get_resource(bundle, "AIL Code"); + ASPL_AILI_ByteList* bytes = ASPL_MALLOC(sizeof(ASPL_AILI_ByteList)); + bytes->size = resource.length; + bytes->position = 0; + bytes->data = resource.data; +#else + if (argc < 2) { + printf("ailbyteinterpreter: no file specified\n"); + return 1; + } + char* file; + file = argv[1]; + if (access(file, F_OK) == -1) { + printf("ailbyteinterpreter: file %s not found\n", file); + return 1; + } + ASPL_AILI_ByteList* bytes = aspl_ailinterpreter_read_file_into_byte_list(file); +#endif + aspl_ailinterpreter_current_byte_list = bytes; + + aspl_ailinterpreter_loop(thread_context, bytes); + return 0; +} \ No newline at end of file diff --git a/stdlib/appbundle/Workaround.aspl b/stdlib/appbundle/Workaround.aspl new file mode 100644 index 0000000..b251769 --- /dev/null +++ b/stdlib/appbundle/Workaround.aspl @@ -0,0 +1 @@ +//Needed because of a bug in the real_path function of the V standard library. \ No newline at end of file diff --git a/stdlib/appbundle/generator/Bundle.aspl b/stdlib/appbundle/generator/Bundle.aspl new file mode 100644 index 0000000..48b7d4a --- /dev/null +++ b/stdlib/appbundle/generator/Bundle.aspl @@ -0,0 +1,56 @@ +import encoding.utf8 +import io +import bitconverter + +[public] +class Bundle { + + property string executable + [readpublic] + property map resources = {} + + [public] + method construct(string executable){ + this.executable = executable + } + + [public] + method addResource(string name, any resource){ + resources[name] = resource + } + + [public] + method generate(){ + var bytes = io.read_file_bytes(executable) + var originalLength = bytes.length + foreach(resources as name => resource){ + var type = 255b + var list resourceBytes = [] + if(resource oftype list){ + type = 0b + resourceBytes = list(resource) + }elseif(resource oftype string){ + type = 2b + resourceBytes = encoding.utf8.encode(string(resource)) + } + bytes.add(type) + foreach(bitconverter.long_to_bytes(long(name.length)) as b){ + bytes.add(b) + } + foreach(encoding.utf8.encode(name) as b){ + bytes.add(b) + } + foreach(bitconverter.long_to_bytes(long(resourceBytes.length)) as b){ + bytes.add(b) + } + foreach(resourceBytes as b){ + bytes.add(b) + } + } + foreach(bitconverter.long_to_bytes(long(bytes.length - originalLength)) as b){ + bytes.add(b) + } + io.write_file_bytes(executable, bytes) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/README.md b/stdlib/aspl/README.md new file mode 100644 index 0000000..005729f --- /dev/null +++ b/stdlib/aspl/README.md @@ -0,0 +1,17 @@ +# The ASPL compiler & co. +## Overview +This module contains submodules for the `parser`, `compiler` and `formatter` of the ASPL toolchain. +
It can be imported and used like any other ASPL module and has no dependencies[^1] except for other modules of the standard library. +
Note that this module does not contain any CLI functionality, but it is instead imported and used by the `cli` folder in the root directory of the ASPL project, which implements the `aspl` command along with all its subcommands. + +[^1]: The compiler requires pre-compiled binary templates when packaging ASPL programs into an executable. Refer to the `templates` folder in the root directory of the ASPL project for more information. + +## Example usage +```aspl +import aspl.compiler + +aspl.compiler.compile("hello.aspl") +``` +That's it. You can now run the compiled program by executing `./hello` (or `hello` if you're on Windows). + +For configuration options and more information, please refer to the `parser` and `compiler` submodules. \ No newline at end of file diff --git a/stdlib/aspl/compiler/CompilationResult.aspl b/stdlib/aspl/compiler/CompilationResult.aspl new file mode 100644 index 0000000..8bc0a07 --- /dev/null +++ b/stdlib/aspl/compiler/CompilationResult.aspl @@ -0,0 +1,14 @@ +import aspl.parser.ast + +[public] +class CompilationResult { + + [readpublic] + property list output + + [public] + method construct(list output){ + this.output = output + } + +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/backend/Backend.aspl b/stdlib/aspl/compiler/backend/Backend.aspl new file mode 100644 index 0000000..6041ce2 --- /dev/null +++ b/stdlib/aspl/compiler/backend/Backend.aspl @@ -0,0 +1,12 @@ +import aspl.compiler +import aspl.parser + +[public] +[abstract] +class Backend { + + [public] + [abstract] + method compile(ParserResult result) returns CompilationResult + +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/backend/bytecode/ByteList.aspl b/stdlib/aspl/compiler/backend/bytecode/ByteList.aspl new file mode 100644 index 0000000..3bf1ad1 --- /dev/null +++ b/stdlib/aspl/compiler/backend/bytecode/ByteList.aspl @@ -0,0 +1,95 @@ +import encoding.utf8 +import bitconverter +import aspl.compiler.backend.bytecode.ail +import aspl.compiler.backend.bytecode.twail + +[public] +class ByteList { + + [public] + property list bytes = [] + [public] + property int length{ + get{ + return bytes.length + } + } + + [public] + method add(any value, IntType type = IntType.Int) returns self{ + if(value oftype byte){ + bytes.add(byte(value)) + }elseif(value oftype list){ + foreach(list(value) as b){ + bytes.add(b) + } + }elseif(value oftype self){ + bytes.insertElements(bytes.length, self(value).bytes) + }elseif(value oftype aspl.compiler.backend.bytecode.ail.Instruction){ // TODO: This class should not be specialised for any backend + bytes.add(byte(int(aspl.compiler.backend.bytecode.ail.Instruction(value)))) + }elseif(value oftype aspl.compiler.backend.bytecode.twail.Instruction){ // TODO: This class should not be specialised for any backend + bytes.add(byte(int(aspl.compiler.backend.bytecode.twail.Instruction(value)))) + }elseif(value oftype string){ + if(type == IntType.Short){ + foreach(bitconverter.short_to_bytes(string(value).length) as b){ + bytes.add(b) + } + }elseif(type == IntType.Int){ + foreach(bitconverter.int_to_bytes(string(value).length) as b){ + bytes.add(b) + } + }elseif(type == IntType.Long){ + foreach(bitconverter.long_to_bytes(long(string(value).length)) as b){ + bytes.add(b) + } + } + foreach(encoding.utf8.encode(string(value)) as c){ + bytes.add(c) + } + }elseif(value oftype bool){ + if(bool(value)){ + bytes.add(1b) + }else{ + bytes.add(0b) + } + }elseif(value oftype int){ + if(type == IntType.Short){ + foreach(bitconverter.short_to_bytes(int(value)) as b){ + bytes.add(b) + } + }elseif(type == IntType.Long){ + foreach(bitconverter.long_to_bytes(long(value)) as b){ + bytes.add(b) + } + }else{ + foreach(bitconverter.int_to_bytes(int(value)) as b){ + bytes.add(b) + } + } + } + elseif(value oftype long){ + foreach(bitconverter.long_to_bytes(long(value)) as b){ + bytes.add(b) + } + } + elseif(value oftype float){ + foreach(bitconverter.float_to_bytes(float(value)) as b){ + bytes.add(b) + } + } + elseif(value oftype double){ + foreach(bitconverter.double_to_bytes(double(value)) as b){ + bytes.add(b) + } + } + return this + } + + /* + TODO: Readd this when custom cast methods are implemented in the new compiler + [public] + method cast(string) returns string{ + return encoding.utf8.decode(bytes) + }*/ + +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/backend/bytecode/BytecodeBackend.aspl b/stdlib/aspl/compiler/backend/bytecode/BytecodeBackend.aspl new file mode 100644 index 0000000..269e60b --- /dev/null +++ b/stdlib/aspl/compiler/backend/bytecode/BytecodeBackend.aspl @@ -0,0 +1,387 @@ +import aspl.compiler.backend +import aspl.compiler.utils +import aspl.parser.ast +import aspl.parser.ast.statements +import aspl.parser.ast.expressions +import aspl.parser.ast.literals +import aspl.parser.functions +import aspl.parser.utils + +// This class is the base class for all bytecode-based backends (e.g. the AIL bytecode backend) +[public] +[abstract] +class BytecodeBackend extends Backend { + + [public] + method encode(Node node, bool standalone = false) returns ByteList{ + if(node oftype Expression && standalone){ + return encodeDiscard(Expression(node)) + } + + if(node oftype BlockStatement){ + return encodeCodeBlock(BlockStatement(node)) + }elseif(node oftype NullLiteral){ + return encodeNullLiteral(NullLiteral(node)) + }elseif(node oftype BooleanLiteral){ + return encodeBooleanLiteral(BooleanLiteral(node)) + }elseif(node oftype ByteLiteral){ + return encodeByteLiteral(ByteLiteral(node)) + }elseif(node oftype IntegerLiteral){ + return encodeIntegerLiteral(IntegerLiteral(node)) + }elseif(node oftype LongLiteral){ + return encodeLongLiteral(LongLiteral(node)) + }elseif(node oftype FloatLiteral){ + return encodeFloatLiteral(FloatLiteral(node)) + }elseif(node oftype DoubleLiteral){ + return encodeDoubleLiteral(DoubleLiteral(node)) + }elseif(node oftype StringLiteral){ + return encodeStringLiteral(StringLiteral(node)) + }elseif(node oftype ListLiteral){ + return encodeListLiteral(ListLiteral(node)) + }elseif(node oftype MapLiteral){ + return encodeMapLiteral(MapLiteral(node)) + }elseif(node oftype StringIndexExpression){ + return encodeStringIndex(StringIndexExpression(node)) + }elseif(node oftype AssertStatement){ + return encodeAssertion(AssertStatement(node)) + }elseif(node oftype CheckEqualsExpression){ + return encodeEqualsCheck(CheckEqualsExpression(node)) + }elseif(node oftype NegateExpression){ + return encodeNegation(NegateExpression(node)) + }elseif(node oftype FunctionCallExpression){ + return encodeFunctionCall(FunctionCallExpression(node)) + }elseif(node oftype VariableDeclareExpression){ + return encodeVariableDeclaration(VariableDeclareExpression(node)) + }elseif(node oftype VariableAccessExpression){ + return encodeVariableAccess(VariableAccessExpression(node)) + }elseif(node oftype VariableAssignExpression){ + return encodeVariableAssignment(VariableAssignExpression(node)) + }elseif(node oftype FunctionDeclareStatement){ + return encodeFunctionDeclaration(FunctionDeclareStatement(node)) + }elseif(node oftype AndExpression){ + return encodeAnd(AndExpression(node)) + }elseif(node oftype OrExpression){ + return encodeOr(OrExpression(node)) + }elseif(node oftype XorExpression){ + return encodeXor(XorExpression(node)) + }elseif(node oftype PlusExpression){ + return encodePlus(PlusExpression(node)) + }elseif(node oftype MinusExpression){ + return encodeMinus(MinusExpression(node)) + }elseif(node oftype MultiplyExpression){ + return encodeMultiplication(MultiplyExpression(node)) + }elseif(node oftype DivideExpression){ + return encodeDivision(DivideExpression(node)) + }elseif(node oftype ModuloExpression){ + return encodeModulo(ModuloExpression(node)) + }elseif(node oftype LessThanExpression){ + return encodeLessThan(LessThanExpression(node)) + }elseif(node oftype LessThanOrEqualExpression){ + return encodeLessThanOrEqual(LessThanOrEqualExpression(node)) + }elseif(node oftype GreaterThanExpression){ + return encodeGreaterThan(GreaterThanExpression(node)) + }elseif(node oftype GreaterThanOrEqualExpression){ + return encodeGreaterThanOrEqual(GreaterThanOrEqualExpression(node)) + }elseif(node oftype ReferenceExpression){ + return encodeReference(ReferenceExpression(node)) + }elseif(node oftype DereferenceExpression){ + return encodeDereference(DereferenceExpression(node)) + }elseif(node oftype ReturnStatement){ + return encodeReturnStatement(ReturnStatement(node)) + }elseif(node oftype FallbackStatement){ + return encodeFallbackStatement(FallbackStatement(node)) + }elseif(node oftype EscapeStatement){ + return encodeEscapeStatement(EscapeStatement(node)) + }elseif(node oftype IfStatement){ + return encodeIfStatement(IfStatement(node)) + }elseif(node oftype IfElseStatement){ + return encodeIfElseStatement(IfElseStatement(node)) + }elseif(node oftype IfElseIfStatement){ + return encodeIfElseIfStatement(IfElseIfStatement(node)) + }elseif(node oftype WhileStatement){ + return encodeWhileStatement(WhileStatement(node)) + }elseif(node oftype RepeatStatement){ + return encodeRepeatStatement(RepeatStatement(node)) + }elseif(node oftype ForeachStatement){ + return encodeForeachStatement(ForeachStatement(node)) + }elseif(node oftype NonStaticMethodCallExpression){ + return encodeNonStaticMethodCall(NonStaticMethodCallExpression(node)) + }elseif(node oftype StaticMethodCallExpression){ + return encodeStaticMethodCall(StaticMethodCallExpression(node)) + }elseif(node oftype NonStaticPropertyAccessExpression){ + return encodeNonStaticPropertyAccess(NonStaticPropertyAccessExpression(node)) + }elseif(node oftype NonStaticPropertyAssignExpression){ + return encodeNonStaticPropertyAssignment(NonStaticPropertyAssignExpression(node)) + }elseif(node oftype StaticPropertyAccessExpression){ + return encodeStaticPropertyAccess(StaticPropertyAccessExpression(node)) + }elseif(node oftype StaticPropertyAssignExpression){ + return encodeStaticPropertyAssignment(StaticPropertyAssignExpression(node)) + }elseif(node oftype ListIndexExpression){ + return encodeListIndex(ListIndexExpression(node)) + }elseif(node oftype ListAssignExpression){ + return encodeListAssignment(ListAssignExpression(node)) + }elseif(node oftype MapAccessExpression){ + return encodeMapAccess(MapAccessExpression(node)) + }elseif(node oftype MapAssignExpression){ + return encodeMapAssignment(MapAssignExpression(node)) + }elseif(node oftype ClassDeclareStatement){ + return encodeClassDeclaration(ClassDeclareStatement(node)) + }elseif(node oftype ClassInstantiateExpression){ + return encodeClassInstantiation(ClassInstantiateExpression(node)) + }elseif(node oftype MethodDeclareStatement){ + return encodeMethodDeclaration(MethodDeclareStatement(node)) + }elseif(node oftype PropertyDeclareStatement){ + return encodePropertyDeclaration(PropertyDeclareStatement(node)) + }elseif(node oftype ThisExpression){ + return encodeThis(ThisExpression(node)) + }elseif(node oftype EnumDeclareStatement){ + return encodeEnumDeclaration(EnumDeclareStatement(node)) + }elseif(node oftype EnumFieldAccessExpression){ + return encodeEnumFieldAccess(EnumFieldAccessExpression(node)) + }elseif(node oftype CallbackLiteral){ + return encodeCallbackLiteral(CallbackLiteral(node)) + }elseif(node oftype ImplementationCallExpression){ + return encodeImplementationCall(ImplementationCallExpression(node)) + }elseif(node oftype OfTypeExpression){ + return encodeOfType(OfTypeExpression(node)) + }elseif(node oftype BreakStatement){ + return encodeBreakStatement(BreakStatement(node)) + }elseif(node oftype ContinueStatement){ + return encodeContinueStatement(ContinueStatement(node)) + }elseif(node oftype CastExpression){ + return encodeCast(CastExpression(node)) + }elseif(node oftype ParentExpression){ + return encode(new ThisExpression(ParentExpression(node).c, ParentExpression(node).location)) // the parent stuff is already handled in the parser + }elseif(node oftype ThrowStatement){ + return encodeThrowStatement(ThrowStatement(node)) + }elseif(node oftype CatchExpression){ + return encodeCatchExpression(CatchExpression(node)) + }elseif(node oftype PropagateErrorExpression){ + return encodeErrorPropagation(PropagateErrorExpression(node)) + }elseif(node oftype EmbedFileExpression){ + return encodeFileEmbedding(EmbedFileExpression(node)) + }elseif(node oftype IncludeFileStatement){ + IncludeUtils:include(IncludeFileStatement(node).file) + return new ByteList() + }elseif(node oftype LinkLibraryStatement){ + LinkUtils:libraries.add(LinkLibraryStatement(node).library) + return new ByteList() + }else{ + aspl.parser.utils.fatal_error("Unsupported node type") + } + } + + [abstract] + method encodeCodeBlock(BlockStatement statement) returns ByteList + + [abstract] + method encodeNullLiteral(NullLiteral literal) returns ByteList + + [abstract] + method encodeBooleanLiteral(BooleanLiteral literal) returns ByteList + + [abstract] + method encodeByteLiteral(ByteLiteral literal) returns ByteList + + [abstract] + method encodeIntegerLiteral(IntegerLiteral literal) returns ByteList + + [abstract] + method encodeLongLiteral(LongLiteral literal) returns ByteList + + [abstract] + method encodeFloatLiteral(FloatLiteral literal) returns ByteList + + [abstract] + method encodeDoubleLiteral(DoubleLiteral literal) returns ByteList + + [abstract] + method encodeStringLiteral(StringLiteral literal) returns ByteList + + [abstract] + method encodeListLiteral(ListLiteral literal) returns ByteList + + [abstract] + method encodeMapLiteral(MapLiteral literal) returns ByteList + + [abstract] + method encodeAssertion(AssertStatement statement) returns ByteList + + [abstract] + method encodeEqualsCheck(CheckEqualsExpression expression) returns ByteList + + [abstract] + method encodeNegation(NegateExpression expression) returns ByteList + + [abstract] + method encodeFunctionCall(FunctionCallExpression expression) returns ByteList + + [abstract] + method encodeVariableDeclaration(VariableDeclareExpression expression) returns ByteList + + [abstract] + method encodeVariableAccess(VariableAccessExpression expression) returns ByteList + + [abstract] + method encodeVariableAssignment(VariableAssignExpression expression) returns ByteList + + [abstract] + method encodeFunctionDeclaration(FunctionDeclareStatement statement) returns ByteList + + [abstract] + method encodeAnd(AndExpression expression) returns ByteList + + [abstract] + method encodeOr(OrExpression expression) returns ByteList + + [abstract] + method encodeXor(XorExpression expression) returns ByteList + + [abstract] + method encodePlus(PlusExpression expression) returns ByteList + + [abstract] + method encodeMinus(MinusExpression expression) returns ByteList + + [abstract] + method encodeMultiplication(MultiplyExpression expression) returns ByteList + + [abstract] + method encodeDivision(DivideExpression expression) returns ByteList + + [abstract] + method encodeModulo(ModuloExpression expression) returns ByteList + + [abstract] + method encodeLessThan(LessThanExpression expression) returns ByteList + + [abstract] + method encodeLessThanOrEqual(LessThanOrEqualExpression expression) returns ByteList + + [abstract] + method encodeGreaterThan(GreaterThanExpression expression) returns ByteList + + [abstract] + method encodeGreaterThanOrEqual(GreaterThanOrEqualExpression expression) returns ByteList + + [abstract] + method encodeReference(ReferenceExpression expression) returns ByteList + + [abstract] + method encodeDereference(DereferenceExpression expression) returns ByteList + + [abstract] + method encodeReturnStatement(ReturnStatement statement) returns ByteList + + [abstract] + method encodeFallbackStatement(FallbackStatement statement) returns ByteList + + [abstract] + method encodeEscapeStatement(EscapeStatement statement) returns ByteList + + [abstract] + method encodeIfStatement(IfStatement statement) returns ByteList + + [abstract] + method encodeIfElseStatement(IfElseStatement statement) returns ByteList + + [abstract] + method encodeIfElseIfStatement(IfElseIfStatement statement) returns ByteList + + [abstract] + method encodeWhileStatement(WhileStatement statement) returns ByteList + + [abstract] + method encodeRepeatStatement(RepeatStatement statement) returns ByteList + + [abstract] + method encodeForeachStatement(ForeachStatement statement) returns ByteList + + [abstract] + method encodeNonStaticMethodCall(NonStaticMethodCallExpression expression) returns ByteList + + [abstract] + method encodeStaticMethodCall(StaticMethodCallExpression expression) returns ByteList + + [abstract] + method encodeNonStaticPropertyAccess(NonStaticPropertyAccessExpression expression) returns ByteList + + [abstract] + method encodeStaticPropertyAccess(StaticPropertyAccessExpression expression) returns ByteList + + [abstract] + method encodeNonStaticPropertyAssignment(NonStaticPropertyAssignExpression expression) returns ByteList + + [abstract] + method encodeStaticPropertyAssignment(StaticPropertyAssignExpression expression) returns ByteList + + [abstract] + method encodeListIndex(ListIndexExpression expression) returns ByteList + + [abstract] + method encodeListAssignment(ListAssignExpression expression) returns ByteList + + [abstract] + method encodeStringIndex(StringIndexExpression expression) returns ByteList + + [abstract] + method encodeMapAccess(MapAccessExpression expression) returns ByteList + + [abstract] + method encodeMapAssignment(MapAssignExpression expression) returns ByteList + + [abstract] + method encodeClassDeclaration(ClassDeclareStatement statement) returns ByteList + + [abstract] + method encodeClassInstantiation(ClassInstantiateExpression expression) returns ByteList + + [abstract] + method encodeMethodDeclaration(MethodDeclareStatement statement) returns ByteList + + [abstract] + method encodePropertyDeclaration(PropertyDeclareStatement statement) returns ByteList + + [abstract] + method encodeThis(ThisExpression expression) returns ByteList + + [abstract] + method encodeEnumDeclaration(EnumDeclareStatement statement) returns ByteList + + [abstract] + method encodeEnumFieldAccess(EnumFieldAccessExpression expression) returns ByteList + + [abstract] + method encodeCallbackLiteral(CallbackLiteral literal) returns ByteList + + [abstract] + method encodeImplementationCall(ImplementationCallExpression expression) returns ByteList + + [abstract] + method encodeOfType(OfTypeExpression expression) returns ByteList + + [abstract] + method encodeBreakStatement(BreakStatement statement) returns ByteList + + [abstract] + method encodeContinueStatement(ContinueStatement statement) returns ByteList + + [abstract] + method encodeCast(CastExpression expression) returns ByteList + + [abstract] + method encodeThrowStatement(ThrowStatement statement) returns ByteList + + [abstract] + method encodeCatchExpression(CatchExpression expression) returns ByteList + + [abstract] + method encodeErrorPropagation(PropagateErrorExpression expression) returns ByteList + + [abstract] + method encodeFileEmbedding(EmbedFileExpression expression) returns ByteList + + [abstract] + method encodeDiscard(Expression expression) returns ByteList + +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/backend/bytecode/IntType.aspl b/stdlib/aspl/compiler/backend/bytecode/IntType.aspl new file mode 100644 index 0000000..fe33fb5 --- /dev/null +++ b/stdlib/aspl/compiler/backend/bytecode/IntType.aspl @@ -0,0 +1,6 @@ +[public] +enum IntType { + Short, + Int, + Long +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/backend/bytecode/ail/AILBytecodeBackend.aspl b/stdlib/aspl/compiler/backend/bytecode/ail/AILBytecodeBackend.aspl new file mode 100644 index 0000000..dc0ea90 --- /dev/null +++ b/stdlib/aspl/compiler/backend/bytecode/ail/AILBytecodeBackend.aspl @@ -0,0 +1,776 @@ +import aspl.compiler +import aspl.compiler.backend +import aspl.compiler.backend.bytecode +import aspl.parser +import aspl.parser.ast +import aspl.parser.ast.statements +import aspl.parser.ast.expressions +import aspl.parser.ast.literals +import aspl.parser.functions +import aspl.parser.properties +import aspl.parser.methods +import aspl.parser.enums +import aspl.parser.attributes +import aspl.parser.utils + +[public] +class AILBytecodeBackend extends BytecodeBackend { + + property int tempVariableId = 0 + + [public] + method compile(ParserResult result) returns CompilationResult{ + var output = new ByteList() + output.add(Instruction.Manifest) + var manifestBody = new ByteList() + // TODO: Encode manifest information + output.add(manifestBody.length) + output.add(manifestBody) + foreach(result.nodes as node){ + output.add(this.encode(node, true)) + } + output.add(Instruction.End) + return new CompilationResult(output.bytes) + } + + method encodeCodeBlock(BlockStatement statement) returns ByteList{ + var bytes = new ByteList() + foreach(statement.statements as s){ + bytes.add(this.encode(s, true)) + } + return bytes + } + + method encodeNullLiteral(NullLiteral literal) returns ByteList{ + return new ByteList().add(Instruction.CreateObject).add("null", IntType.Short) + } + + method encodeBooleanLiteral(BooleanLiteral literal) returns ByteList{ + return new ByteList().add(Instruction.CreateObject).add("bool", IntType.Short).add(literal.value) // "bool" instead of "boolean" for speed and minimal size + } + + method encodeByteLiteral(ByteLiteral literal) returns ByteList{ + return new ByteList().add(Instruction.CreateObject).add("byte", IntType.Short).add(literal.value) + } + + method encodeIntegerLiteral(IntegerLiteral literal) returns ByteList{ + return new ByteList().add(Instruction.CreateObject).add("int", IntType.Short).add(literal.value) // "int" instead of "integer" for speed and minimal size + } + + method encodeLongLiteral(LongLiteral literal) returns ByteList{ + return new ByteList().add(Instruction.CreateObject).add("long", IntType.Short).add(literal.value) + } + + method encodeFloatLiteral(FloatLiteral literal) returns ByteList{ + return new ByteList().add(Instruction.CreateObject).add("float", IntType.Short).add(literal.value) + } + + method encodeDoubleLiteral(DoubleLiteral literal) returns ByteList{ + return new ByteList().add(Instruction.CreateObject).add("double", IntType.Short).add(literal.value) + } + + method encodeStringLiteral(StringLiteral literal) returns ByteList{ + return new ByteList().add(Instruction.CreateObject).add("string", IntType.Short).add(literal.value, IntType.Long) + } + + method encodeListLiteral(ListLiteral literal) returns ByteList{ + var bytes = new ByteList() + foreach(literal.value as element){ + bytes.add(this.encode(element)) + } + bytes.add(Instruction.CreateObject).add("list", IntType.Short).add(literal.type.types.length, IntType.Short) + foreach(literal.type.types as type){ + bytes.add(type.toString(), IntType.Short) + } + bytes.add(literal.value.length) + return bytes + } + + method encodeMapLiteral(MapLiteral literal) returns ByteList{ + var bytes = new ByteList() + foreach(literal.value as pair){ + bytes.add(this.encode(pair.k)) + bytes.add(this.encode(pair.v)) + } + bytes.add(Instruction.CreateObject).add("map", IntType.Short).add(literal.keyType.types.length, IntType.Short) + foreach(literal.keyType.types as type){ + bytes.add(type.toString(), IntType.Short) + } + bytes.add(literal.valueType.types.length, IntType.Short) + foreach(literal.valueType.types as type){ + bytes.add(type.toString(), IntType.Short) + } + bytes.add(literal.value.length) + return bytes + } + + method encodeAssertion(AssertStatement statement) returns ByteList{ + return new ByteList().add(this.encode(statement.expression)).add(Instruction.Assert).add(Location(statement.location).file, IntType.Short).add(Location(statement.location).startLine).add(Location(statement.location).startColumn) + } + + method encodeEqualsCheck(CheckEqualsExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.Equals) + } + + method encodeNegation(NegateExpression expression) returns ByteList{ + if(expression.expression oftype CheckEqualsExpression){ + return new ByteList().add(this.encode(CheckEqualsExpression(expression.expression).left)).add(this.encode(CheckEqualsExpression(expression.expression).right)).add(Instruction.NotEquals) + } + return new ByteList().add(this.encode(expression.expression)).add(Instruction.Negate) + } + + method encodeFunctionCall(FunctionCallExpression expression) returns ByteList{ + var bytes = new ByteList() + foreach(expression.arguments as argument){ + bytes.add(this.encode(argument)) + } + var identifier = expression.func.identifier + if(expression.func oftype InternalFunction){ + identifier = "_" + identifier + } + bytes.add(Instruction.CallFunction).add(identifier, IntType.Long).add(expression.newThread) + bytes.add(expression.arguments.length) + return bytes + } + + method encodeVariableDeclaration(VariableDeclareExpression expression) returns ByteList{ + var bytes = new ByteList() + bytes.add(this.encode(expression.value)) + bytes.add(Instruction.RegisterVariable).add(expression.variable.identifier, IntType.Short).add(expression.getType().types.length, IntType.Short) + foreach(expression.getType().types as type){ + bytes.add(TypeUtils:shortName(type.toString()), IntType.Short) + } + return bytes + } + + method encodeVariableAccess(VariableAccessExpression expression) returns ByteList{ + return new ByteList().add(Instruction.AccessVariable).add(expression.variable.identifier, IntType.Short) + } + + method encodeVariableAssignment(VariableAssignExpression expression) returns ByteList{ + var bytes = new ByteList() + bytes.add(this.encode(expression.value)) + bytes.add(Instruction.UpdateVariable).add(expression.variable.identifier, IntType.Short) + return bytes + } + + method encodeFunctionDeclaration(FunctionDeclareStatement statement) returns ByteList{ + var bytes = new ByteList() + foreach(statement.func.parameters as parameter){ + bytes.add(encodeParameterValue(parameter)) + } + bytes.add(Instruction.DeclareFunction) + bytes.add(statement.func.identifier, IntType.Short).add(statement.func.parameters.length, IntType.Short) + foreach(statement.func.parameters as parameter){ + bytes.add(encodeParameterMeta(parameter)) + } + bytes.add(statement.func.returnTypes.types.length, IntType.Short) + foreach(statement.func.returnTypes.types as returnType){ + bytes.add(TypeUtils:shortName(returnType.toString()), IntType.Short) + } + var body = new ByteList() + foreach(CustomFunction(statement.func).code as s){ + body.add(this.encode(s, true)) + } + body.add(Instruction.End) + bytes.add(body.length, IntType.Long) + bytes.add(body) + return bytes + } + + method encodeParameterValue(Parameter parameter) returns ByteList{ + if(parameter.optional){ + return this.encode(parameter.defaultValue) + }else{ + return new ByteList() + } + } + + method encodeParameterMeta(Parameter parameter) returns ByteList{ + var bytes = new ByteList().add(parameter.name, IntType.Short).add(parameter.types.types.length, IntType.Short) + foreach(parameter.types.types as type){ + bytes.add(TypeUtils:shortName(type.toString()), IntType.Short) + } + bytes.add(parameter.optional) + return bytes + } + + method encodeReturnStatement(ReturnStatement statement) returns ByteList{ + var bytes = new ByteList() + if(statement.value != null){ + bytes.add(this.encode(statement.value)) + }else{ + bytes.add(encodeNullLiteral(new NullLiteral(null))) + } + bytes.add(Instruction.ReturnStatement) + return bytes + } + + method encodeFallbackStatement(FallbackStatement statement) returns ByteList{ + var bytes = new ByteList() + bytes.add(this.encode(statement.value)) + bytes.add(Instruction.FallbackStatement) + return bytes + } + + method encodeEscapeStatement(EscapeStatement statement) returns ByteList{ + var bytes = new ByteList() + if(statement.value != null){ + bytes.add(this.encode(statement.value)) + }else{ + bytes.add(encodeNullLiteral(new NullLiteral(null))) + } + bytes.add(Instruction.EscapeStatement) + return bytes + } + + method encodeAnd(AndExpression expression) returns ByteList{ + if(Type:matches(new Types([Type:fromString("boolean")]), expression.getType())){ + var right = this.encode(expression.right) + return new ByteList().add(this.encode(expression.left)).add(Instruction.BooleanAnd).add(right.length).add(right) + }else{ + return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.BitwiseAnd) + } + } + + method encodeOr(OrExpression expression) returns ByteList{ + if(Type:matches(new Types([Type:fromString("boolean")]), expression.getType())){ + var right = this.encode(expression.right) + return new ByteList().add(this.encode(expression.left)).add(Instruction.BooleanOr).add(right.length).add(right) + }else{ + return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.BitwiseOr) + } + } + + method encodeXor(XorExpression expression) returns ByteList{ + if(Type:matches(new Types([Type:fromString("boolean")]), expression.getType())){ + return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.BooleanXor) + }else{ + return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.BitwiseXor) + } + } + + method encodePlus(PlusExpression expression) returns ByteList{ + if((expression.left oftype ByteLiteral && ByteLiteral(expression.left).value == 1b) || (expression.left oftype IntegerLiteral && IntegerLiteral(expression.left).value == 1) || (expression.left oftype LongLiteral && LongLiteral(expression.left).value == 1l) || (expression.left oftype FloatLiteral && FloatLiteral(expression.left).value == 1f) || (expression.left oftype DoubleLiteral && DoubleLiteral(expression.left).value == 1d)){ + return new ByteList().add(this.encode(expression.right)).add(Instruction.Increment) + }elseif((expression.right oftype ByteLiteral && ByteLiteral(expression.right).value == 1b) || (expression.right oftype IntegerLiteral && IntegerLiteral(expression.right).value == 1) || (expression.right oftype LongLiteral && LongLiteral(expression.right).value == 1l) || (expression.right oftype FloatLiteral && FloatLiteral(expression.right).value == 1f) || (expression.right oftype DoubleLiteral && DoubleLiteral(expression.right).value == 1d)){ + return new ByteList().add(this.encode(expression.left)).add(Instruction.Increment) + } + return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.Add) + } + + method encodeMinus(MinusExpression expression) returns ByteList{ + if((expression.right oftype ByteLiteral && ByteLiteral(expression.right).value == 1b) || (expression.right oftype IntegerLiteral && IntegerLiteral(expression.right).value == 1) || (expression.right oftype LongLiteral && LongLiteral(expression.right).value == 1l) || (expression.right oftype FloatLiteral && FloatLiteral(expression.right).value == 1f) || (expression.right oftype DoubleLiteral && DoubleLiteral(expression.right).value == 1d)){ + return new ByteList().add(this.encode(expression.left)).add(Instruction.Decrement) + } + // no left side check since minus is not commutative + return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.Subtract) + } + + method encodeMultiplication(MultiplyExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.Multiply) + } + + method encodeDivision(DivideExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.Divide) + } + + method encodeModulo(ModuloExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.Modulo) + } + + method encodeLessThan(LessThanExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.LessThan) + } + + method encodeLessThanOrEqual(LessThanOrEqualExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.LessEqual) + } + + method encodeGreaterThan(GreaterThanExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.GreaterThan) + } + + method encodeGreaterThanOrEqual(GreaterThanOrEqualExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.GreaterEqual) + } + + method encodeReference(ReferenceExpression expression) returns ByteList{ + return new ByteList().add(Instruction.DeclarePointer).add(this.encode(expression.expression)) + } + + method encodeDereference(DereferenceExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.pointer)).add(Instruction.DereferencePointer) + } + + method encodeIfStatement(IfStatement statement) returns ByteList{ + var bytes = new ByteList() + bytes.add(this.encode(statement.condition)) + bytes.add(Instruction.JumpRelativeIfFalse) + var body = new ByteList() + foreach(statement.code as s){ + body.add(this.encode(s, true)) + } + bytes.add(body.length, IntType.Long) + bytes.add(body) + return bytes + } + + method encodeIfElseStatement(IfElseStatement statement) returns ByteList{ + var ifBody = new ByteList() + foreach(statement.ifCode as s){ + ifBody.add(this.encode(s, true)) + } + var elseBody = new ByteList() + foreach(statement.elseCode as s){ + elseBody.add(this.encode(s, true)) + } + var bytes = new ByteList() + bytes.add(this.encode(statement.condition)) + bytes.add(Instruction.JumpRelativeIfFalse) + bytes.add(ifBody.length + 8 + 1, IntType.Long) // + 1 for the jump instruction and + 8 for the length of the jump (at the beginning of the else statement) + bytes.add(ifBody) + bytes.add(Instruction.JumpRelative) + bytes.add(elseBody.length, IntType.Long) + bytes.add(elseBody) + return bytes + } + + method encodeIfElseIfStatement(IfElseIfStatement statement) returns ByteList{ + var ifBody = new ByteList() + foreach(statement.ifCode as s){ + ifBody.add(this.encode(s, true)) + } + var elseBody = this.encode(statement.elseIf) + var bytes = new ByteList() + bytes.add(this.encode(statement.condition)) + bytes.add(Instruction.JumpRelativeIfFalse) + bytes.add(ifBody.length + 8 + 1, IntType.Long) // + 1 for the jump instruction and + 8 for the length of the jump (at the beginning of the else statement) + bytes.add(ifBody) + bytes.add(Instruction.JumpRelative) + bytes.add(elseBody.length, IntType.Long) + bytes.add(elseBody) + return bytes + } + + method encodeWhileStatement(WhileStatement statement) returns ByteList{ + var body = new ByteList() + foreach(statement.code as s){ + body.add(this.encode(s, true)) + } + var actualCode = new ByteList() + actualCode.add(this.encode(statement.condition)) + actualCode.add(Instruction.JumpRelativeIfFalse) + actualCode.add(body.length + 1 + 8, IntType.Long) // + 1 for the jump instruction and + 8 for the length of the jump (at the end) + actualCode.add(body) + var bytes = new ByteList() + bytes.add(Instruction.SpecifyLoop) + bytes.add(actualCode.length, IntType.Long) // loop body length + bytes.add(actualCode.length + 1 + 8, IntType.Long) // full loop length (1 for the "relative jump" instruction and 8 for the length of the jump) + bytes.add(actualCode) + bytes.add(Instruction.JumpRelative) + bytes.add(-(bytes.length + 8 - (1 + 8 + 8)), IntType.Long) // + 8 for the length of the jump + bytes.add(Instruction.PopLoop) + return bytes + } + + method encodeRepeatStatement(RepeatStatement statement) returns ByteList{ + var bytes = new ByteList() + var variable = "_temp_" + tempVariableId++ + if(statement.variable != null){ + variable = statement.variable + } + bytes.add(this.encode(new IntegerLiteral(statement.start, null))) + bytes.add(Instruction.RegisterVariable).add(variable, IntType.Short) + bytes.add(1, IntType.Short) + bytes.add("int", IntType.Short) + bytes.add(Instruction.Pop) // pop the result of the variable declaration + + var body = new ByteList() + foreach(statement.code as s){ + body.add(this.encode(s, true)) + } + var preUpdateCounterVariable = body.length + body.add(Instruction.AccessVariable).add(variable, IntType.Short) + body.add(Instruction.Increment) + body.add(Instruction.UpdateVariable).add(variable, IntType.Short) + body.add(Instruction.Pop) // pop the result of the variable update + + var actualCode = new ByteList() + actualCode.add(Instruction.AccessVariable).add(variable, IntType.Short) + actualCode.add(this.encode(statement.iterations)) + actualCode.add(this.encode(new IntegerLiteral(statement.start, null))) + actualCode.add(Instruction.Add) + actualCode.add(Instruction.LessThan) + actualCode.add(Instruction.JumpRelativeIfFalse) + actualCode.add(body.length + 1 + 8, IntType.Long) // + 1 for the jump instruction and + 8 for the length of the jump (at the end) + actualCode.add(body) + + bytes.add(Instruction.SpecifyLoop) + bytes.add(actualCode.length - (body.length - preUpdateCounterVariable), IntType.Long) // loop body length + bytes.add(actualCode.length + 1 + 8, IntType.Long) // full loop length (1 for the "relative jump" instruction and 8 for the length of the jump) + bytes.add(actualCode) + + bytes.add(Instruction.JumpRelative) + bytes.add(-(actualCode.length + 1 + 8), IntType.Long) // + 1 for the jump instruction and + 8 for the length of the jump + + bytes.add(Instruction.PopLoop) + return bytes + } + + method encodeForeachStatement(ForeachStatement statement) returns ByteList{ + var bytes = new ByteList() + var collectionVariable = "_temp_" + tempVariableId++ + var indexVariable = "_temp_" + tempVariableId++ + var string? mapKeyVariable = null + if(Type:matches(new Types([Type:fromString("map")]), statement.collection.getType(), true)){ + mapKeyVariable = statement.key + }elseif(statement.key != null){ + indexVariable = statement.key + } + var valueVariable = statement.value + bytes.add(this.encode(statement.collection)) + bytes.add(Instruction.RegisterVariable).add(collectionVariable, IntType.Short) + bytes.add(1, IntType.Short) + bytes.add("any", IntType.Short) + bytes.add(Instruction.Pop) // pop the result of the variable declaration + bytes.add(this.encode(new IntegerLiteral(0, null))) + bytes.add(Instruction.RegisterVariable).add(indexVariable, IntType.Short) + bytes.add(1, IntType.Short) + bytes.add("int", IntType.Short) + bytes.add(Instruction.Pop) // pop the result of the variable declaration + var initDoneAddress = bytes.length + var actualCode = new ByteList() + actualCode.add(Instruction.AccessVariable).add(indexVariable, IntType.Short) + actualCode.add(Instruction.AccessVariable).add(collectionVariable, IntType.Short) + actualCode.add(Instruction.CountCollection) + actualCode.add(Instruction.LessThan) + var conditionLength = actualCode.length + var body = new ByteList() + body.add(Instruction.AccessVariable).add(collectionVariable, IntType.Short) + body.add(Instruction.AccessVariable).add(indexVariable, IntType.Short) + body.add(Instruction.AccessCollectionValueWithIndex) + body.add(Instruction.RegisterVariable).add(valueVariable, IntType.Short) + body.add(1, IntType.Short) + body.add("any", IntType.Short) + body.add(Instruction.Pop) // pop the result of the variable declaration + if(mapKeyVariable != null){ + body.add(Instruction.AccessVariable).add(collectionVariable, IntType.Short) + body.add(Instruction.AccessVariable).add(indexVariable, IntType.Short) + body.add(Instruction.AccessCollectionKeyWithIndex) + body.add(Instruction.RegisterVariable).add(mapKeyVariable, IntType.Short) + body.add(1, IntType.Short) + body.add("any", IntType.Short) + body.add(Instruction.Pop) // pop the result of the variable declaration + } + foreach(statement.code as s){ + body.add(this.encode(s, true)) + } + var preUpdateIndexVariable = body.length + body.add(Instruction.AccessVariable).add(indexVariable, IntType.Short) + body.add(Instruction.Increment) + body.add(Instruction.UpdateVariable).add(indexVariable, IntType.Short) + body.add(Instruction.Pop) // pop the result of the variable update + body.add(Instruction.JumpRelative) + body.add(-(body.length + conditionLength + 1 + 8 + 8), IntType.Long) // + 8 for the length of the jump + actualCode.add(Instruction.JumpRelativeIfFalse) + actualCode.add(body.length, IntType.Long) + actualCode.add(body) + bytes.add(Instruction.SpecifyLoop) + bytes.add(actualCode.length - (body.length - preUpdateIndexVariable), IntType.Long) // - 1 for the jump instruction and - 8 for the length of the jump + bytes.add(actualCode.length, IntType.Long) + bytes.add(actualCode) + bytes.add(Instruction.PopLoop) + return bytes + } + + method encodeNonStaticMethodCall(NonStaticMethodCallExpression expression) returns ByteList{ + var bytes = new ByteList() + bytes.add(this.encode(expression.base)) + foreach(expression.arguments as argument){ + bytes.add(this.encode(argument)) + } + if(expression.exactClass == null){ + bytes.add(Instruction.CallMethod) + bytes.add(false) + }else{ + bytes.add(Instruction.CallExactMethod) + bytes.add(expression.exactClass?!.type.toString(), IntType.Short) + } + bytes.add(expression.m.name, IntType.Short) + bytes.add(expression.newThread) + bytes.add(expression.arguments.length) + return bytes + } + + method encodeStaticMethodCall(StaticMethodCallExpression expression) returns ByteList{ + var bytes = new ByteList() + foreach(expression.arguments as argument){ + bytes.add(this.encode(argument)) + } + bytes.add(Instruction.CallMethod) + bytes.add(true) + bytes.add(expression.base.toString(), IntType.Short) + bytes.add(expression.m.name, IntType.Short) + bytes.add(expression.newThread) + bytes.add(expression.arguments.length) + return bytes + } + + method encodeNonStaticPropertyAccess(NonStaticPropertyAccessExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.base)).add(Instruction.AccessProperty).add(false).add(expression.p.name, IntType.Short) + } + + method encodeStaticPropertyAccess(StaticPropertyAccessExpression expression) returns ByteList{ + return new ByteList().add(Instruction.AccessProperty).add(true).add(expression.base.toString(), IntType.Short).add(expression.p.name, IntType.Short) + } + + method encodeNonStaticPropertyAssignment(NonStaticPropertyAssignExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.base)).add(this.encode(expression.value)).add(Instruction.UpdateProperty).add(false).add(expression.p oftype CustomReactiveProperty).add(expression.p.name, IntType.Short) + } + + method encodeStaticPropertyAssignment(StaticPropertyAssignExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.value)).add(Instruction.UpdateProperty).add(true).add(expression.p oftype CustomReactiveProperty).add(expression.base.toString(), IntType.Short).add(expression.p.name, IntType.Short) + } + + method encodeListIndex(ListIndexExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.base)).add(this.encode(expression.index)).add(Instruction.IndexCollection) + } + + method encodeListAssignment(ListAssignExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.base)).add(this.encode(expression.index)).add(this.encode(expression.value)).add(Instruction.UpdateCollection) + } + + method encodeMapAccess(MapAccessExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.base)).add(this.encode(expression.key)).add(Instruction.IndexCollection) + } + + method encodeMapAssignment(MapAssignExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.base)).add(this.encode(expression.key)).add(this.encode(expression.value)).add(Instruction.UpdateCollection) + } + + method encodeStringIndex(StringIndexExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.base)).add(this.encode(expression.index)).add(Instruction.IndexCollection) + } + + method encodeClassDeclaration(ClassDeclareStatement statement) returns ByteList{ + var bytes = new ByteList() + bytes.add(Instruction.DeclareClass) + bytes.add(statement.c.isStatic) + bytes.add(statement.c.type.toString(), IntType.Short).add(list(statement.c.parents).length, IntType.Short) + foreach(list(statement.c.parents) as parent){ + bytes.add(parent.toString(), IntType.Short) + } + bytes.add(statement.c.isError) + var body = new ByteList() + foreach(statement.c.code as s){ + body.add(this.encode(s, true)) + } + body.add(Instruction.End) + bytes.add(body) + return bytes + } + + method encodeClassInstantiation(ClassInstantiateExpression expression) returns ByteList{ + var bytes = new ByteList() + foreach(expression.arguments as argument){ + bytes.add(this.encode(argument)) + } + bytes.add(Instruction.NewStatement).add(expression.c.type.toString(), IntType.Short).add(expression.arguments.length) + return bytes + } + + method encodeMethodDeclaration(MethodDeclareStatement statement) returns ByteList{ + var bytes = new ByteList() + foreach(statement.m.parameters as parameter){ + bytes.add(encodeParameterValue(parameter)) + } + bytes.add(Instruction.DeclareMethod) + bytes.add(statement.m.isStatic) + bytes.add(statement.m.name, IntType.Short) + bytes.add(statement.m.parameters.length, IntType.Short) + foreach(statement.m.parameters as parameter){ + bytes.add(encodeParameterMeta(parameter)) + } + bytes.add(statement.m.returnTypes.types.length, IntType.Short) + foreach(statement.m.returnTypes.types as returnType){ + bytes.add(TypeUtils:shortName(returnType.toString()), IntType.Short) + } + var body = new ByteList() + foreach(CustomMethod(statement.m).code as s){ + body.add(this.encode(s, true)) + } + body.add(Instruction.End) + bytes.add(body.length, IntType.Long) + bytes.add(body) + return bytes + } + + method encodePropertyDeclaration(PropertyDeclareStatement statement) returns ByteList{ + var bytes = new ByteList() + if(statement.p oftype CustomNormalProperty){ + if(CustomNormalProperty(statement.p).isStatic){ + if(CustomNormalProperty(statement.p).defaultValue != null){ + bytes.add(this.encode(CustomNormalProperty(statement.p).defaultValue)) + }else{ + bytes.add(this.encode(new NullLiteral(null))) + } + } + } + bytes.add(Instruction.DeclareProperty) + bytes.add(statement.p.isStatic) + bytes.add(statement.p.isThreadLocal) + bytes.add(statement.p.name, IntType.Short) + bytes.add(statement.p.types.types.length, IntType.Short) + foreach(statement.p.types.types as type){ + bytes.add(TypeUtils:shortName(type.toString()), IntType.Short) + } + if(statement.p oftype CustomReactiveProperty){ + bytes.add(1b) + if(CustomReactiveProperty(statement.p).getCode == null){ + bytes.add(-1, IntType.Long) + }else{ + var getCode = new ByteList() + foreach(CustomReactiveProperty(statement.p).getCode as s){ + getCode.add(this.encode(s, true)) + } + getCode.add(Instruction.End) + bytes.add(getCode.length, IntType.Long) + bytes.add(getCode) + } + if(CustomReactiveProperty(statement.p).setCode == null){ + bytes.add(-1, IntType.Long) + }else{ + var setCode = new ByteList() + foreach(CustomReactiveProperty(statement.p).setCode as s){ + setCode.add(this.encode(s, true)) + } + setCode.add(Instruction.End) + bytes.add(setCode.length, IntType.Long) + bytes.add(setCode) + } + }else{ + bytes.add(0b) + } + return bytes + } + + method encodeThis(ThisExpression expression) returns ByteList{ + return new ByteList().add(Instruction.ThisStatement) + } + + method encodeEnumDeclaration(EnumDeclareStatement statement) returns ByteList{ + var bytes = new ByteList() + var list keys = [] + foreach(map(statement.e.fields) as key => _){ + keys.add(key) + } + bytes.add(Instruction.DeclareEnum).add(statement.e.type.toString(), IntType.Short).add(map(statement.e.fields).length).add(statement.e.isFlags) + foreach(map(statement.e.fields) as field){ + bytes.add(field.name, IntType.Short) + bytes.add(field.value) + } + return bytes + } + + method encodeEnumFieldAccess(EnumFieldAccessExpression expression) returns ByteList{ + var e = Enum:enums[expression.field.e.type.toString()] // TODO: Figure out if these workarounds can be simplified + var intValue = e.fields?![expression.field.name].value + return new ByteList().add(Instruction.EnumField).add(expression.field.e.type.toString(), IntType.Short).add(expression.field.name, IntType.Short).add(intValue) + } + + method encodeCallbackLiteral(CallbackLiteral literal) returns ByteList{ + var bytes = new ByteList() + foreach(literal.value.parameters as parameter){ + bytes.add(encodeParameterValue(parameter)) + } + bytes.add(Instruction.DeclareCallback) + bytes.add(literal.value.type.toString(), IntType.Short) + bytes.add(literal.value.parameters.length, IntType.Short) + foreach(literal.value.parameters as parameter){ + bytes.add(encodeParameterMeta(parameter)) + } + bytes.add(literal.value.returnTypes.types.length, IntType.Short) + foreach(literal.value.returnTypes.types as returnType){ + bytes.add(TypeUtils:shortName(returnType.toString()), IntType.Short) + } + var body = new ByteList() + foreach(literal.value.code as s){ + body.add(this.encode(s, true)) + } + body.add(Instruction.End) + bytes.add(body.length, IntType.Long) + bytes.add(body) + return bytes + } + + method encodeImplementationCall(ImplementationCallExpression expression) returns ByteList{ + var bytes = new ByteList() + foreach(expression.arguments as argument){ + bytes.add(this.encode(argument)) + } + bytes.add(Instruction.Implement).add(expression.call, IntType.Long).add(expression.arguments.length) + return bytes + } + + method encodeOfType(OfTypeExpression expression) returns ByteList{ + var bytes = new ByteList() + bytes.add(this.encode(expression.expression)) + bytes.add(Instruction.OfType) + bytes.add(TypeUtils:shortName(expression.type.toString()), IntType.Short) + return bytes + } + + method encodeBreakStatement(BreakStatement statement) returns ByteList{ + return new ByteList().add(Instruction.BreakStatement) + .add(false) // true would be legacy mode, which interprets if as a breakable/continuable statement + .add(statement.level) + } + + method encodeContinueStatement(ContinueStatement statement) returns ByteList{ + return new ByteList().add(Instruction.ContinueStatement) + .add(false) // true would be legacy mode, which interprets if as a breakable/continuable statement + .add(statement.level) + } + + method encodeCast(CastExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.value)).add(Instruction.Cast).add(TypeUtils:shortName(expression.type.toString()), IntType.Short) + } + + method encodeThrowStatement(ThrowStatement statement) returns ByteList{ + return new ByteList().add(this.encode(statement.errorInstance)).add(Instruction.Throw) + } + + method encodeCatchExpression(CatchExpression expression) returns ByteList{ + var bytes = new ByteList() + bytes.add(this.encode(expression.expression)) + bytes.add(Instruction.Catch) + var variable = "" + if(expression.variable != null){ + variable = expression.variable?! + } + bytes.add(variable, IntType.Short) + var body = new ByteList() + foreach(expression.code as s){ + body.add(this.encode(s, true)) + } + body.add(Instruction.End) + bytes.add(body.length, IntType.Long) + bytes.add(body) + return bytes + } + + method encodeErrorPropagation(PropagateErrorExpression expression) returns ByteList{ + return new ByteList().add(this.encode(expression.expression)).add(Instruction.PropagateError) + } + + method encodeFileEmbedding(EmbedFileExpression expression) returns ByteList{ + var bytes = io.read_file_bytes(expression.file) + return new ByteList().add(Instruction.ByteArrayLiteral).add(bytes.length, IntType.Long).add(bytes) + } + + method encodeDiscard(Expression expression) returns ByteList{ + return this.encode(expression).add(Instruction.Pop) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/backend/bytecode/ail/Instruction.aspl b/stdlib/aspl/compiler/backend/bytecode/ail/Instruction.aspl new file mode 100644 index 0000000..4592bb4 --- /dev/null +++ b/stdlib/aspl/compiler/backend/bytecode/ail/Instruction.aspl @@ -0,0 +1,67 @@ +enum Instruction { + Manifest, + CreateObject, + ByteArrayLiteral, + Implement, + JumpRelative, + JumpRelativeIfFalse, + DeclareFunction, + CallFunction, + Add, + Increment, + Subtract, + Decrement, + Multiply, + Divide, + Modulo, + LessThan, + LessEqual, + GreaterThan, + GreaterEqual, + Equals, + NotEquals, + BooleanAnd, + BitwiseAnd, + BooleanOr, + BitwiseOr, + BooleanXor, + BitwiseXor, + RegisterVariable, + AccessVariable, + UpdateVariable, + DeclareMethod, + CallMethod, + CallExactMethod, + BreakStatement, + ContinueStatement, + ReturnStatement, + FallbackStatement, + EscapeStatement, + Negate, + CountCollection, + AccessCollectionKeyWithIndex, + AccessCollectionValueWithIndex, + IndexCollection, + UpdateCollection, + DeclareCallback, + DeclareClass, + NewStatement, + ThisStatement, + DeclareProperty, + AccessProperty, + UpdateProperty, + DeclareEnum, + EnumField, + DeclarePointer, + DereferencePointer, + Cast, + Assert, + OfType, + SpecifyLoop, + PopLoop, + Throw, + Catch, + PropagateError, + Pop, + End +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/backend/bytecode/ail/TypeUtils.aspl b/stdlib/aspl/compiler/backend/bytecode/ail/TypeUtils.aspl new file mode 100644 index 0000000..96dd4fa --- /dev/null +++ b/stdlib/aspl/compiler/backend/bytecode/ail/TypeUtils.aspl @@ -0,0 +1,17 @@ +[public] +[static] +class TypeUtils { + + [public] + [static] + method shortName(string s) returns string{ + if(s == "integer"){ + return "int" + }elseif(s == "boolean"){ + return "bool" + }else{ + return s + } + } + +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/backend/bytecode/twail/Instruction.aspl b/stdlib/aspl/compiler/backend/bytecode/twail/Instruction.aspl new file mode 100644 index 0000000..9fd4b41 --- /dev/null +++ b/stdlib/aspl/compiler/backend/bytecode/twail/Instruction.aspl @@ -0,0 +1,56 @@ +enum Instruction { + Manifest, + CreateObject, + Implement, + CreatePair, + DeclareFunction, + CallFunction, + IfStatement, + IfElseStatement, + WhileStatement, + RepeatStatement, + ForeachStatement, + Add, + Subtract, + Multiply, + Divide, + Modulo, + LessThan, + LessEqual, + GreaterThan, + GreaterEqual, + Equals, + And, + Or, + Xor, + RegisterVariable, + AccessVariable, + UpdateVariable, + DeclareMethod, + CallMethod, + BreakStatement, + ContinueStatement, + ReturnStatement, + FallbackStatement, + EscapeStatement, + Negate, + IndexCollection, + UpdateCollection, + DeclareCallback, + DeclareClass, + NewStatement, + ThisStatement, + DeclareProperty, + AccessProperty, + UpdateProperty, + DeclareEnum, + EnumField, + DeclarePointer, + DereferencePointer, + Cast, + Assert, + OfType, + Throw, + Catch, + PropagateError +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/backend/bytecode/twail/TreeWalkingAILBytecodeBackend.aspl b/stdlib/aspl/compiler/backend/bytecode/twail/TreeWalkingAILBytecodeBackend.aspl new file mode 100644 index 0000000..75732ff --- /dev/null +++ b/stdlib/aspl/compiler/backend/bytecode/twail/TreeWalkingAILBytecodeBackend.aspl @@ -0,0 +1,601 @@ +import aspl.compiler +import aspl.compiler.backend +import aspl.compiler.backend.bytecode +import aspl.parser +import aspl.parser.ast +import aspl.parser.ast.statements +import aspl.parser.ast.expressions +import aspl.parser.ast.literals +import aspl.parser.functions +import aspl.parser.properties +import aspl.parser.methods +import aspl.parser.enums +import aspl.parser.utils + +[public] +// NOTE: This backend is deprecated; only use it if you actually need it! +class TreeWalkingAILBytecodeBackend extends BytecodeBackend { + + [public] + method compile(ParserResult result) returns CompilationResult{ + var output = new ByteList() + output.add(Instruction.Manifest) + var manifestBody = new ByteList() + // TODO: Encode manifest information + output.add(manifestBody.length) + output.add(manifestBody) + foreach(result.nodes as node){ + output.add(this.encode(node)) + } + return new CompilationResult(output.bytes) + } + + method encodeCodeBlock(BlockStatement statement) returns ByteList{ + var bytes = new ByteList() + foreach(statement.statements as s){ + bytes.add(this.encode(s)) + } + return bytes + } + + method encodeNullLiteral(NullLiteral literal) returns ByteList{ + return new ByteList().add(Instruction.CreateObject).add("null", IntType.Short) + } + + method encodeBooleanLiteral(BooleanLiteral literal) returns ByteList{ + return new ByteList().add(Instruction.CreateObject).add("bool", IntType.Short).add(literal.value) // "bool" instead of "boolean" for speed and minimal size + } + + method encodeByteLiteral(ByteLiteral literal) returns ByteList{ + return new ByteList().add(Instruction.CreateObject).add("byte", IntType.Short).add(literal.value) + } + + method encodeIntegerLiteral(IntegerLiteral literal) returns ByteList{ + return new ByteList().add(Instruction.CreateObject).add("int", IntType.Short).add(literal.value) // "int" instead of "integer" for speed and minimal size + } + + method encodeLongLiteral(LongLiteral literal) returns ByteList{ + return new ByteList().add(Instruction.CreateObject).add("long", IntType.Short).add(literal.value) + } + + method encodeFloatLiteral(FloatLiteral literal) returns ByteList{ + return new ByteList().add(Instruction.CreateObject).add("float", IntType.Short).add(literal.value) + } + + method encodeDoubleLiteral(DoubleLiteral literal) returns ByteList{ + return new ByteList().add(Instruction.CreateObject).add("double", IntType.Short).add(literal.value) + } + + method encodeStringLiteral(StringLiteral literal) returns ByteList{ + return new ByteList().add(Instruction.CreateObject).add("string", IntType.Short).add(literal.value, IntType.Long) + } + + method encodeListLiteral(ListLiteral literal) returns ByteList{ + var bytes = new ByteList().add(Instruction.CreateObject).add("list", IntType.Short).add(literal.type.types.length, IntType.Short) + foreach(literal.type.types as type){ + bytes.add(type.toString(), IntType.Short) + } + bytes.add(literal.value.length) + foreach(literal.value as element){ + bytes.add(this.encode(element)) + } + return bytes + } + + method encodeMapLiteral(MapLiteral literal) returns ByteList{ + var bytes = new ByteList().add(Instruction.CreateObject).add("map", IntType.Short).add(literal.keyType.types.length, IntType.Short) + foreach(literal.keyType.types as type){ + bytes.add(type.toString(), IntType.Short) + } + bytes.add(literal.valueType.types.length, IntType.Short) + foreach(literal.valueType.types as type){ + bytes.add(type.toString(), IntType.Short) + } + bytes.add(literal.value.length) + foreach(literal.value as pair){ + bytes.add(Instruction.CreatePair) + bytes.add(this.encode(pair.k)) + bytes.add(this.encode(pair.v)) + } + return bytes + } + + method encodeAssertion(AssertStatement statement) returns ByteList{ + return new ByteList().add(Instruction.Assert).add(Location(statement.location).file, IntType.Short).add(Location(statement.location).startLine).add(Location(statement.location).startColumn).add(this.encode(statement.expression)) + } + + method encodeEqualsCheck(CheckEqualsExpression expression) returns ByteList{ + return new ByteList().add(Instruction.Equals).add(this.encode(expression.left)).add(this.encode(expression.right)) + } + + method encodeNegation(NegateExpression expression) returns ByteList{ + return new ByteList().add(Instruction.Negate).add(this.encode(expression.expression)) + } + + method encodeFunctionCall(FunctionCallExpression expression) returns ByteList{ + var identifier = expression.func.identifier + if(expression.func oftype InternalFunction){ + identifier = "_" + identifier + } + var bytes = new ByteList().add(Instruction.CallFunction).add(identifier, IntType.Long).add(expression.newThread).add(expression.arguments.length, IntType.Short) + foreach(expression.arguments as argument){ + bytes.add(this.encode(argument)) + } + return bytes + } + + method encodeVariableDeclaration(VariableDeclareExpression expression) returns ByteList{ + var bytes = new ByteList().add(Instruction.RegisterVariable).add(expression.variable.identifier, IntType.Short).add(expression.getType().types.length, IntType.Short) + foreach(expression.getType().types as type){ + bytes.add(TypeUtils:shortName(type.toString()), IntType.Short) + } + bytes.add(this.encode(expression.value)) + return bytes + } + + method encodeVariableAccess(VariableAccessExpression expression) returns ByteList{ + return new ByteList().add(Instruction.AccessVariable).add(expression.variable.identifier, IntType.Short) + } + + method encodeVariableAssignment(VariableAssignExpression expression) returns ByteList{ + var bytes = new ByteList().add(Instruction.UpdateVariable).add(expression.variable.identifier, IntType.Short) + bytes.add(this.encode(expression.value)) + return bytes + } + + method encodeFunctionDeclaration(FunctionDeclareStatement statement) returns ByteList{ + var bytes = new ByteList().add(Instruction.DeclareFunction) + bytes.add(statement.func.identifier, IntType.Short).add(statement.func.parameters.length, IntType.Short) + foreach(statement.func.parameters as parameter){ + bytes.add(encodeParameter(parameter)) + } + bytes.add(statement.func.returnTypes.types.length, IntType.Short) + foreach(statement.func.returnTypes.types as returnType){ + bytes.add(TypeUtils:shortName(returnType.toString()), IntType.Short) + } + var body = new ByteList() + foreach(CustomFunction(statement.func).code as s){ + body.add(this.encode(s)) + } + bytes.add(body.length, IntType.Long) + bytes.add(body) + return bytes + } + + method encodeParameter(Parameter parameter) returns ByteList{ + var bytes = new ByteList().add(parameter.name, IntType.Short).add(parameter.types.types.length, IntType.Short) + foreach(parameter.types.types as type){ + bytes.add(TypeUtils:shortName(type.toString()), IntType.Short) + } + if(parameter.optional){ + bytes.add(true) + bytes.add(this.encode(parameter.defaultValue)) + }else{ + bytes.add(false) + } + return bytes + } + + method encodeReturnStatement(ReturnStatement statement) returns ByteList{ + var bytes = new ByteList().add(Instruction.ReturnStatement) + if(statement.value != null){ + bytes.add(this.encode(statement.value)) + }else{ + bytes.add(encodeNullLiteral(new NullLiteral(null))) + } + return bytes + } + + method encodeFallbackStatement(FallbackStatement statement) returns ByteList{ + var bytes = new ByteList().add(Instruction.FallbackStatement) + bytes.add(this.encode(statement.value)) + return bytes + } + + method encodeEscapeStatement(EscapeStatement statement) returns ByteList{ + var bytes = new ByteList().add(Instruction.EscapeStatement) + bytes.add(this.encode(statement.value)) + return bytes + } + + method encodeAnd(AndExpression expression) returns ByteList{ + var right = this.encode(expression.right) + return new ByteList().add(Instruction.And).add(this.encode(expression.left)).add(right.length).add(right) + } + + method encodeOr(OrExpression expression) returns ByteList{ + var right = this.encode(expression.right) + return new ByteList().add(Instruction.Or).add(this.encode(expression.left)).add(right.length).add(right) + } + + method encodeXor(XorExpression expression) returns ByteList{ + return new ByteList().add(Instruction.Xor).add(this.encode(expression.left)).add(this.encode(expression.right)) + } + + method encodePlus(PlusExpression expression) returns ByteList{ + return new ByteList().add(Instruction.Add).add(this.encode(expression.left)).add(this.encode(expression.right)) + } + + method encodeMinus(MinusExpression expression) returns ByteList{ + return new ByteList().add(Instruction.Subtract).add(this.encode(expression.left)).add(this.encode(expression.right)) + } + + method encodeMultiplication(MultiplyExpression expression) returns ByteList{ + return new ByteList().add(Instruction.Multiply).add(this.encode(expression.left)).add(this.encode(expression.right)) + } + + method encodeDivision(DivideExpression expression) returns ByteList{ + return new ByteList().add(Instruction.Divide).add(this.encode(expression.left)).add(this.encode(expression.right)) + } + + method encodeModulo(ModuloExpression expression) returns ByteList{ + return new ByteList().add(Instruction.Modulo).add(this.encode(expression.left)).add(this.encode(expression.right)) + } + + method encodeLessThan(LessThanExpression expression) returns ByteList{ + return new ByteList().add(Instruction.LessThan).add(this.encode(expression.left)).add(this.encode(expression.right)) + } + + method encodeLessThanOrEqual(LessThanOrEqualExpression expression) returns ByteList{ + return new ByteList().add(Instruction.LessEqual).add(this.encode(expression.left)).add(this.encode(expression.right)) + } + + method encodeGreaterThan(GreaterThanExpression expression) returns ByteList{ + return new ByteList().add(Instruction.GreaterThan).add(this.encode(expression.left)).add(this.encode(expression.right)) + } + + method encodeGreaterThanOrEqual(GreaterThanOrEqualExpression expression) returns ByteList{ + return new ByteList().add(Instruction.GreaterEqual).add(this.encode(expression.left)).add(this.encode(expression.right)) + } + + method encodeReference(ReferenceExpression expression) returns ByteList{ + return new ByteList().add(Instruction.DeclarePointer).add(this.encode(expression.expression)) + } + + method encodeDereference(DereferenceExpression expression) returns ByteList{ + return new ByteList().add(Instruction.DereferencePointer).add(this.encode(expression.pointer)) + } + + method encodeIfStatement(IfStatement statement) returns ByteList{ + var bytes = new ByteList().add(Instruction.IfStatement) + var body = new ByteList() + foreach(statement.code as s){ + body.add(this.encode(s)) + } + bytes.add(body.length, IntType.Long) + bytes.add(this.encode(statement.condition)) + bytes.add(body) + return bytes + } + + method encodeIfElseStatement(IfElseStatement statement) returns ByteList{ + var bytes = new ByteList().add(Instruction.IfElseStatement) + var ifBody = new ByteList() + foreach(statement.ifCode as s){ + ifBody.add(this.encode(s)) + } + bytes.add(ifBody.length, IntType.Long) + bytes.add(this.encode(statement.condition)) + bytes.add(ifBody) + var elseBody = new ByteList() + foreach(statement.elseCode as s){ + elseBody.add(this.encode(s)) + } + bytes.add(elseBody.length, IntType.Long) + bytes.add(elseBody) + return bytes + } + + method encodeIfElseIfStatement(IfElseIfStatement statement) returns ByteList{ + var bytes = new ByteList().add(Instruction.IfElseStatement) + var ifBody = new ByteList() + foreach(statement.ifCode as s){ + ifBody.add(this.encode(s)) + } + bytes.add(ifBody.length, IntType.Long) + bytes.add(this.encode(statement.condition)) + bytes.add(ifBody) + var elseBody = this.encode(statement.elseIf) + bytes.add(elseBody.length, IntType.Long) + bytes.add(elseBody) + return bytes + } + + method encodeWhileStatement(WhileStatement statement) returns ByteList{ + var bytes = new ByteList().add(Instruction.WhileStatement) + var body = new ByteList() + foreach(statement.code as s){ + body.add(this.encode(s)) + } + bytes.add(body.length, IntType.Long) + bytes.add(this.encode(statement.condition)) + bytes.add(body) + return bytes + } + + method encodeRepeatStatement(RepeatStatement statement) returns ByteList{ + var bytes = new ByteList().add(Instruction.RepeatStatement) + var body = new ByteList() + foreach(statement.code as s){ + body.add(this.encode(s)) + } + bytes.add(body.length, IntType.Long) + bytes.add(this.encode(statement.iterations)) + if(statement.variable == null){ + bytes.add("_", IntType.Short) + }else{ + bytes.add(statement.variable, IntType.Short) + } + bytes.add(statement.start) + bytes.add(body) + return bytes + } + + method encodeForeachStatement(ForeachStatement statement) returns ByteList{ + var bytes = new ByteList().add(Instruction.ForeachStatement) + var body = new ByteList() + foreach(statement.code as s){ + body.add(this.encode(s)) + } + bytes.add(body.length, IntType.Long) + bytes.add(this.encode(statement.collection)) + if(statement.key == null){ + bytes.add("_", IntType.Long) + }else{ + bytes.add(statement.key, IntType.Long) + } + if(statement.value == null){ + bytes.add("_", IntType.Long) + }else{ + bytes.add(statement.value, IntType.Long) + } + bytes.add(body) + return bytes + } + + method encodeNonStaticMethodCall(NonStaticMethodCallExpression expression) returns ByteList{ + var bytes = new ByteList().add(Instruction.CallMethod) + .add(false) + .add(this.encode(expression.base)).add(expression.m.name, IntType.Short).add(expression.newThread).add(expression.arguments.length, IntType.Short) + foreach(expression.arguments as argument){ + bytes.add(this.encode(argument)) + } + return bytes + } + + method encodeStaticMethodCall(StaticMethodCallExpression expression) returns ByteList{ + var bytes = new ByteList().add(Instruction.CallMethod) + .add(true) + .add(expression.base.toString(), IntType.Short).add(expression.m.name, IntType.Short).add(expression.newThread).add(expression.arguments.length, IntType.Short) + foreach(expression.arguments as argument){ + bytes.add(this.encode(argument)) + } + return bytes + } + + method encodeNonStaticPropertyAccess(NonStaticPropertyAccessExpression expression) returns ByteList{ + return new ByteList().add(Instruction.AccessProperty) + .add(false) + .add(this.encode(expression.base)).add(expression.p.name, IntType.Short) + } + + method encodeStaticPropertyAccess(StaticPropertyAccessExpression expression) returns ByteList{ + return new ByteList().add(Instruction.AccessProperty) + .add(true) + .add(expression.base.toString(), IntType.Short).add(expression.p.name, IntType.Short) + } + + method encodeNonStaticPropertyAssignment(NonStaticPropertyAssignExpression expression) returns ByteList{ + return new ByteList().add(Instruction.UpdateProperty) + .add(false) + .add(this.encode(expression.base), IntType.Short).add(expression.p.name, IntType.Short).add(this.encode(expression.value)) + } + + method encodeStaticPropertyAssignment(StaticPropertyAssignExpression expression) returns ByteList{ + return new ByteList().add(Instruction.UpdateProperty) + .add(true) + .add(expression.base.toString(), IntType.Short).add(expression.p.name, IntType.Short).add(this.encode(expression.value)) + } + + method encodeListIndex(ListIndexExpression expression) returns ByteList{ + return new ByteList().add(Instruction.IndexCollection).add(this.encode(expression.base)).add(this.encode(expression.index)) + } + + method encodeListAssignment(ListAssignExpression expression) returns ByteList{ + return new ByteList().add(Instruction.UpdateCollection).add(this.encode(expression.base)).add(this.encode(expression.index)).add(this.encode(expression.value)) + } + + method encodeMapAccess(MapAccessExpression expression) returns ByteList{ + return new ByteList().add(Instruction.IndexCollection).add(this.encode(expression.base)).add(this.encode(expression.key)) + } + + method encodeMapAssignment(MapAssignExpression expression) returns ByteList{ + return new ByteList().add(Instruction.UpdateCollection).add(this.encode(expression.base)).add(this.encode(expression.key)).add(this.encode(expression.value)) + } + + method encodeStringIndex(StringIndexExpression expression) returns ByteList{ + return new ByteList().add(Instruction.IndexCollection).add(this.encode(expression.base)).add(this.encode(expression.index)) + } + + method encodeClassDeclaration(ClassDeclareStatement statement) returns ByteList{ + var bytes = new ByteList().add(Instruction.DeclareClass) + bytes.add(statement.c.isStatic) + bytes.add(statement.c.type.toString(), IntType.Short).add(list(statement.c.parents).length, IntType.Short) + foreach(list(statement.c.parents) as parent){ + bytes.add(parent.toString(), IntType.Short) + } + var body = new ByteList() + foreach(statement.c.code as s){ + body.add(this.encode(s)) + } + bytes.add(body.length, IntType.Long) + bytes.add(body) + return bytes + } + + method encodeClassInstantiation(ClassInstantiateExpression expression) returns ByteList{ + var bytes = new ByteList().add(Instruction.NewStatement).add(expression.c.type.toString(), IntType.Short).add(expression.arguments.length, IntType.Short) + foreach(expression.arguments as argument){ + bytes.add(this.encode(argument)) + } + return bytes + } + + method encodeMethodDeclaration(MethodDeclareStatement statement) returns ByteList{ + var bytes = new ByteList().add(Instruction.DeclareMethod) + bytes.add(statement.m.isStatic) + bytes.add(statement.m.name, IntType.Short) + bytes.add(statement.m.parameters.length, IntType.Short) + foreach(statement.m.parameters as parameter){ + bytes.add(encodeParameter(parameter)) + } + bytes.add(statement.m.returnTypes.types.length, IntType.Short) + foreach(statement.m.returnTypes.types as returnType){ + bytes.add(TypeUtils:shortName(returnType.toString()), IntType.Short) + } + var body = new ByteList() + foreach(CustomMethod(statement.m).code as s){ + body.add(this.encode(s)) + } + bytes.add(body.length, IntType.Long) + bytes.add(body) + return bytes + } + + method encodePropertyDeclaration(PropertyDeclareStatement statement) returns ByteList{ + var bytes = new ByteList().add(Instruction.DeclareProperty) + bytes.add(statement.p.isStatic) + bytes.add(statement.p.isThreadLocal) + bytes.add(statement.p.name, IntType.Short) + bytes.add(statement.p.types.types.length, IntType.Short) + foreach(statement.p.types.types as type){ + bytes.add(TypeUtils:shortName(type.toString()), IntType.Short) + } + if(statement.p oftype CustomReactiveProperty){ + bytes.add(1b) + if(CustomReactiveProperty(statement.p).getCode == null){ + bytes.add(-1, IntType.Long) + }else{ + var getCode = new ByteList() + foreach(CustomReactiveProperty(statement.p).getCode as s){ + getCode.add(this.encode(s)) + } + bytes.add(getCode.length, IntType.Long) + bytes.add(getCode) + } + if(CustomReactiveProperty(statement.p).setCode == null){ + bytes.add(-1, IntType.Long) + }else{ + var setCode = new ByteList() + foreach(CustomReactiveProperty(statement.p).setCode as s){ + setCode.add(this.encode(s)) + } + bytes.add(setCode.length, IntType.Long) + bytes.add(setCode) + } + }else{ + bytes.add(0b) + if(CustomNormalProperty(statement.p).isStatic){ + if(CustomNormalProperty(statement.p).defaultValue != null){ + bytes.add(this.encode(CustomNormalProperty(statement.p).defaultValue)) + } + } + } + return bytes + } + + method encodeThis(ThisExpression expression) returns ByteList{ + return new ByteList().add(Instruction.ThisStatement) + } + + method encodeEnumDeclaration(EnumDeclareStatement statement) returns ByteList{ + var bytes = new ByteList().add(Instruction.DeclareEnum) + bytes.add(statement.e.type.toString(), IntType.Short) + bytes.add(map(statement.e.fields).length) + foreach(map(statement.e.fields) as field){ + bytes.add(field.name, IntType.Short) + bytes.add(field.value) + } + return bytes + } + + method encodeEnumFieldAccess(EnumFieldAccessExpression expression) returns ByteList{ + return new ByteList().add(Instruction.EnumField).add(expression.field.e.type.toString(), IntType.Short).add(expression.field.name, IntType.Short) + } + + method encodeCallbackLiteral(CallbackLiteral literal) returns ByteList{ + var bytes = new ByteList().add(Instruction.DeclareCallback) + bytes.add(literal.value.type.toString(), IntType.Short) + bytes.add(literal.value.parameters.length, IntType.Short) + foreach(literal.value.parameters as parameter){ + bytes.add(encodeParameter(parameter)) + } + bytes.add(literal.value.returnTypes.types.length, IntType.Short) + foreach(literal.value.returnTypes.types as returnType){ + bytes.add(TypeUtils:shortName(returnType.toString()), IntType.Short) + } + var body = new ByteList() + foreach(literal.value.code as s){ + body.add(this.encode(s)) + } + bytes.add(body.length, IntType.Long) + bytes.add(body) + return bytes + } + + method encodeImplementationCall(ImplementationCallExpression expression) returns ByteList{ + var bytes = new ByteList().add(Instruction.Implement).add(expression.call, IntType.Long).add(expression.arguments.length, IntType.Short) + foreach(expression.arguments as argument){ + bytes.add(this.encode(argument)) + } + return bytes + } + + method encodeOfType(OfTypeExpression expression) returns ByteList{ + var bytes = new ByteList().add(Instruction.OfType) + bytes.add(this.encode(expression.expression)) + bytes.add(expression.type.toString(), IntType.Short) + return bytes + } + + method encodeBreakStatement(BreakStatement statement) returns ByteList{ + return new ByteList().add(Instruction.BreakStatement) + .add(false) // true would be legacy mode, which interprets if as a breakable/continuable statement + .add(statement.level) + } + + method encodeContinueStatement(ContinueStatement statement) returns ByteList{ + return new ByteList().add(Instruction.ContinueStatement) + .add(false) // true would be legacy mode, which interprets if as a breakable/continuable statement + .add(statement.level) + } + + method encodeCast(CastExpression expression) returns ByteList{ + return new ByteList().add(Instruction.Cast).add(this.encode(expression.value)).add(expression.type.toString(), IntType.Short) + } + + method encodeThrowStatement(ThrowStatement statement) returns ByteList{ + // TODO + } + + method encodeCatchExpression(CatchExpression expression) returns ByteList{ + // TODO + } + + method encodeErrorPropagation(PropagateErrorExpression expression) returns ByteList{ + // TODO + } + + method encodeFileEmbedding(EmbedFileExpression expression) returns ByteList{ + var bytes = list[] + foreach(io.read_file_bytes(expression.file) as b){ + bytes.add(new ByteLiteral(b, expression.location)) + } + return encodeListLiteral(new ListLiteral(new Types([Type:fromString("byte")]), bytes, expression.location)) + } + + method encodeDiscard(Expression expression) returns ByteList{ + return this.encode(expression) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/backend/bytecode/twail/TypeUtils.aspl b/stdlib/aspl/compiler/backend/bytecode/twail/TypeUtils.aspl new file mode 100644 index 0000000..96dd4fa --- /dev/null +++ b/stdlib/aspl/compiler/backend/bytecode/twail/TypeUtils.aspl @@ -0,0 +1,17 @@ +[public] +[static] +class TypeUtils { + + [public] + [static] + method shortName(string s) returns string{ + if(s == "integer"){ + return "int" + }elseif(s == "boolean"){ + return "bool" + }else{ + return s + } + } + +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/backend/stringcode/StringcodeBackend.aspl b/stdlib/aspl/compiler/backend/stringcode/StringcodeBackend.aspl new file mode 100644 index 0000000..2ff8ae8 --- /dev/null +++ b/stdlib/aspl/compiler/backend/stringcode/StringcodeBackend.aspl @@ -0,0 +1,402 @@ +import aspl.compiler.backend +import aspl.compiler.utils +import aspl.parser.ast +import aspl.parser.ast.statements +import aspl.parser.ast.expressions +import aspl.parser.ast.literals +import aspl.parser.functions +import aspl.parser.utils + +// This class is the base class for all stringcode-based backends (e.g. the C backend) +[public] +[abstract] +class StringcodeBackend extends Backend { + + [public] // TODO: Remove this public modifier; it is currently needed because of a bug in the compiler + property int indentLevel = 0 + + [public] + method encode(Node node, bool standalone = false) returns string{ + var indentStub = "" + var terminator = "" + if(standalone){ + repeat(indentLevel){ + indentStub += "\t" + } + terminator = getLineTerminator() + } + + if(node oftype Expression && standalone){ + return indentStub + encodeDiscard(Expression(node)) + terminator + } + + if(node oftype BlockStatement){ + return indentStub + encodeCodeBlock(BlockStatement(node)) // no line terminator + }elseif(node oftype NullLiteral){ + return indentStub + encodeNullLiteral(NullLiteral(node)) + terminator + }elseif(node oftype BooleanLiteral){ + return indentStub + encodeBooleanLiteral(BooleanLiteral(node)) + terminator + }elseif(node oftype ByteLiteral){ + return indentStub + encodeByteLiteral(ByteLiteral(node)) + terminator + }elseif(node oftype IntegerLiteral){ + return indentStub + encodeIntegerLiteral(IntegerLiteral(node)) + terminator + }elseif(node oftype LongLiteral){ + return indentStub + encodeLongLiteral(LongLiteral(node)) + terminator + }elseif(node oftype FloatLiteral){ + return indentStub + encodeFloatLiteral(FloatLiteral(node)) + terminator + }elseif(node oftype DoubleLiteral){ + return indentStub + encodeDoubleLiteral(DoubleLiteral(node)) + terminator + }elseif(node oftype StringLiteral){ + return indentStub + encodeStringLiteral(StringLiteral(node)) + terminator + }elseif(node oftype ListLiteral){ + return indentStub + encodeListLiteral(ListLiteral(node)) + terminator + }elseif(node oftype MapLiteral){ + return indentStub + encodeMapLiteral(MapLiteral(node)) + terminator + }elseif(node oftype StringIndexExpression){ + return indentStub + encodeStringIndex(StringIndexExpression(node)) + terminator + }elseif(node oftype AssertStatement){ + return indentStub + encodeAssertion(AssertStatement(node)) + terminator + }elseif(node oftype CheckEqualsExpression){ + return indentStub + encodeEqualsCheck(CheckEqualsExpression(node)) + terminator + }elseif(node oftype NegateExpression){ + return indentStub + encodeNegation(NegateExpression(node)) + terminator + }elseif(node oftype FunctionCallExpression){ + return indentStub + encodeFunctionCall(FunctionCallExpression(node)) + terminator + }elseif(node oftype VariableDeclareExpression){ + return indentStub + encodeVariableDeclaration(VariableDeclareExpression(node)) + terminator + }elseif(node oftype VariableAccessExpression){ + return indentStub + encodeVariableAccess(VariableAccessExpression(node)) + terminator + }elseif(node oftype VariableAssignExpression){ + return indentStub + encodeVariableAssignment(VariableAssignExpression(node)) + terminator + }elseif(node oftype FunctionDeclareStatement){ + return indentStub + encodeFunctionDeclaration(FunctionDeclareStatement(node)) // no line terminator + }elseif(node oftype AndExpression){ + return indentStub + encodeAnd(AndExpression(node)) + terminator + }elseif(node oftype OrExpression){ + return indentStub + encodeOr(OrExpression(node)) + terminator + }elseif(node oftype XorExpression){ + return indentStub + encodeXor(XorExpression(node)) + terminator + }elseif(node oftype PlusExpression){ + return indentStub + encodePlus(PlusExpression(node)) + terminator + }elseif(node oftype MinusExpression){ + return indentStub + encodeMinus(MinusExpression(node)) + terminator + }elseif(node oftype MultiplyExpression){ + return indentStub + encodeMultiplication(MultiplyExpression(node)) + terminator + }elseif(node oftype DivideExpression){ + return indentStub + encodeDivision(DivideExpression(node)) + terminator + }elseif(node oftype ModuloExpression){ + return indentStub + encodeModulo(ModuloExpression(node)) + terminator + }elseif(node oftype LessThanExpression){ + return indentStub + encodeLessThan(LessThanExpression(node)) + terminator + }elseif(node oftype LessThanOrEqualExpression){ + return indentStub + encodeLessThanOrEqual(LessThanOrEqualExpression(node)) + terminator + }elseif(node oftype GreaterThanExpression){ + return indentStub + encodeGreaterThan(GreaterThanExpression(node)) + terminator + }elseif(node oftype GreaterThanOrEqualExpression){ + return indentStub + encodeGreaterThanOrEqual(GreaterThanOrEqualExpression(node)) + terminator + }elseif(node oftype ReferenceExpression){ + return indentStub + encodeReference(ReferenceExpression(node)) + terminator + }elseif(node oftype DereferenceExpression){ + return indentStub + encodeDereference(DereferenceExpression(node)) + terminator + }elseif(node oftype ReturnStatement){ + return indentStub + encodeReturnStatement(ReturnStatement(node)) + terminator + }elseif(node oftype FallbackStatement){ + return indentStub + encodeFallbackStatement(FallbackStatement(node)) + terminator + }elseif(node oftype EscapeStatement){ + return indentStub + encodeEscapeStatement(EscapeStatement(node)) + terminator + }elseif(node oftype IfStatement){ + return indentStub + encodeIfStatement(IfStatement(node)) // no line terminator + }elseif(node oftype IfElseStatement){ + return indentStub + encodeIfElseStatement(IfElseStatement(node)) // no line terminator + }elseif(node oftype IfElseIfStatement){ + return indentStub + encodeIfElseIfStatement(IfElseIfStatement(node)) // no line terminator + }elseif(node oftype WhileStatement){ + return indentStub + encodeWhileStatement(WhileStatement(node)) // no line terminator + }elseif(node oftype RepeatStatement){ + return indentStub + encodeRepeatStatement(RepeatStatement(node)) // no line terminator + }elseif(node oftype ForeachStatement){ + return indentStub + encodeForeachStatement(ForeachStatement(node)) // no line terminator + }elseif(node oftype NonStaticMethodCallExpression){ + return indentStub + encodeNonStaticMethodCall(NonStaticMethodCallExpression(node)) + terminator + }elseif(node oftype StaticMethodCallExpression){ + return indentStub + encodeStaticMethodCall(StaticMethodCallExpression(node)) + terminator + }elseif(node oftype NonStaticPropertyAccessExpression){ + return indentStub + encodeNonStaticPropertyAccess(NonStaticPropertyAccessExpression(node)) + terminator + }elseif(node oftype NonStaticPropertyAssignExpression){ + return indentStub + encodeNonStaticPropertyAssignment(NonStaticPropertyAssignExpression(node)) + terminator + }elseif(node oftype StaticPropertyAccessExpression){ + return indentStub + encodeStaticPropertyAccess(StaticPropertyAccessExpression(node)) + terminator + }elseif(node oftype StaticPropertyAssignExpression){ + return indentStub + encodeStaticPropertyAssignment(StaticPropertyAssignExpression(node)) + terminator + }elseif(node oftype ListIndexExpression){ + return indentStub + encodeListIndex(ListIndexExpression(node)) + terminator + }elseif(node oftype ListAssignExpression){ + return indentStub + encodeListAssignment(ListAssignExpression(node)) + terminator + }elseif(node oftype MapAccessExpression){ + return indentStub + encodeMapAccess(MapAccessExpression(node)) + terminator + }elseif(node oftype MapAssignExpression){ + return indentStub + encodeMapAssignment(MapAssignExpression(node)) + terminator + }elseif(node oftype ClassDeclareStatement){ + return indentStub + encodeClassDeclaration(ClassDeclareStatement(node)) // no line terminator + }elseif(node oftype ClassInstantiateExpression){ + return indentStub + encodeClassInstantiation(ClassInstantiateExpression(node)) + terminator + }elseif(node oftype MethodDeclareStatement){ + return indentStub + encodeMethodDeclaration(MethodDeclareStatement(node)) // no line terminator + }elseif(node oftype PropertyDeclareStatement){ + return indentStub + encodePropertyDeclaration(PropertyDeclareStatement(node)) + terminator + }elseif(node oftype ThisExpression){ + return indentStub + encodeThis(ThisExpression(node)) + terminator + }elseif(node oftype EnumDeclareStatement){ + return indentStub + encodeEnumDeclaration(EnumDeclareStatement(node)) // no line terminator + }elseif(node oftype EnumFieldAccessExpression){ + return indentStub + encodeEnumFieldAccess(EnumFieldAccessExpression(node)) + terminator + }elseif(node oftype CallbackLiteral){ + return indentStub + encodeCallbackLiteral(CallbackLiteral(node)) + terminator + }elseif(node oftype ImplementationCallExpression){ + return indentStub + encodeImplementationCall(ImplementationCallExpression(node)) + terminator + }elseif(node oftype OfTypeExpression){ + return indentStub + encodeOfType(OfTypeExpression(node)) + terminator + }elseif(node oftype BreakStatement){ + return indentStub + encodeBreakStatement(BreakStatement(node)) + terminator + }elseif(node oftype ContinueStatement){ + return indentStub + encodeContinueStatement(ContinueStatement(node)) + terminator + }elseif(node oftype CastExpression){ + return indentStub + encodeCast(CastExpression(node)) + terminator + }elseif(node oftype ParentExpression){ + return encode(new ThisExpression(ParentExpression(node).c, ParentExpression(node).location)) // the parent stuff is already handled in the parser + }elseif(node oftype ThrowStatement){ + return indentStub + encodeThrowStatement(ThrowStatement(node)) + terminator + }elseif(node oftype CatchExpression){ + return indentStub + encodeCatchExpression(CatchExpression(node)) + terminator + }elseif(node oftype PropagateErrorExpression){ + return indentStub + encodeErrorPropagation(PropagateErrorExpression(node)) + terminator + }elseif(node oftype EmbedFileExpression){ + return indentStub + encodeFileEmbedding(EmbedFileExpression(node)) + terminator + }elseif(node oftype IncludeFileStatement){ + IncludeUtils:include(IncludeFileStatement(node).file) + return indentStub + "// Including file " + IncludeFileStatement(node).file // no line terminator + }elseif(node oftype LinkLibraryStatement){ + LinkUtils:libraries.add(LinkLibraryStatement(node).library) + return indentStub + "// Linking library " + LinkLibraryStatement(node).library // no line terminator + }else{ + aspl.parser.utils.fatal_error("Unsupported node type " + node) + } + } + + [abstract] + method getLineTerminator() returns string + + [abstract] + method encodeCodeBlock(BlockStatement statement) returns string + + [abstract] + method encodeNullLiteral(NullLiteral literal) returns string + + [abstract] + method encodeBooleanLiteral(BooleanLiteral literal) returns string + + [abstract] + method encodeByteLiteral(ByteLiteral literal) returns string + + [abstract] + method encodeIntegerLiteral(IntegerLiteral literal) returns string + + [abstract] + method encodeLongLiteral(LongLiteral literal) returns string + + [abstract] + method encodeFloatLiteral(FloatLiteral literal) returns string + + [abstract] + method encodeDoubleLiteral(DoubleLiteral literal) returns string + + [abstract] + method encodeStringLiteral(StringLiteral literal) returns string + + [abstract] + method encodeListLiteral(ListLiteral literal) returns string + + [abstract] + method encodeMapLiteral(MapLiteral literal) returns string + + [abstract] + method encodeAssertion(AssertStatement statement) returns string + + [abstract] + method encodeEqualsCheck(CheckEqualsExpression expression) returns string + + [abstract] + method encodeNegation(NegateExpression expression) returns string + + [abstract] + method encodeFunctionCall(FunctionCallExpression expression) returns string + + [abstract] + method encodeVariableDeclaration(VariableDeclareExpression expression) returns string + + [abstract] + method encodeVariableAccess(VariableAccessExpression expression) returns string + + [abstract] + method encodeVariableAssignment(VariableAssignExpression expression) returns string + + [abstract] + method encodeFunctionDeclaration(FunctionDeclareStatement statement) returns string + + [abstract] + method encodeAnd(AndExpression expression) returns string + + [abstract] + method encodeOr(OrExpression expression) returns string + + [abstract] + method encodeXor(XorExpression expression) returns string + + [abstract] + method encodePlus(PlusExpression expression) returns string + + [abstract] + method encodeMinus(MinusExpression expression) returns string + + [abstract] + method encodeMultiplication(MultiplyExpression expression) returns string + + [abstract] + method encodeDivision(DivideExpression expression) returns string + + [abstract] + method encodeModulo(ModuloExpression expression) returns string + + [abstract] + method encodeLessThan(LessThanExpression expression) returns string + + [abstract] + method encodeLessThanOrEqual(LessThanOrEqualExpression expression) returns string + + [abstract] + method encodeGreaterThan(GreaterThanExpression expression) returns string + + [abstract] + method encodeGreaterThanOrEqual(GreaterThanOrEqualExpression expression) returns string + + [abstract] + method encodeReference(ReferenceExpression expression) returns string + + [abstract] + method encodeDereference(DereferenceExpression expression) returns string + + [abstract] + method encodeReturnStatement(ReturnStatement statement) returns string + + [abstract] + method encodeFallbackStatement(FallbackStatement statement) returns string + + [abstract] + method encodeEscapeStatement(EscapeStatement statement) returns string + + [abstract] + method encodeIfStatement(IfStatement statement) returns string + + [abstract] + method encodeIfElseStatement(IfElseStatement statement) returns string + + [abstract] + method encodeIfElseIfStatement(IfElseIfStatement statement) returns string + + [abstract] + method encodeWhileStatement(WhileStatement statement) returns string + + [abstract] + method encodeRepeatStatement(RepeatStatement statement) returns string + + [abstract] + method encodeForeachStatement(ForeachStatement statement) returns string + + [abstract] + method encodeNonStaticMethodCall(NonStaticMethodCallExpression expression) returns string + + [abstract] + method encodeStaticMethodCall(StaticMethodCallExpression expression) returns string + + [abstract] + method encodeNonStaticPropertyAccess(NonStaticPropertyAccessExpression expression) returns string + + [abstract] + method encodeStaticPropertyAccess(StaticPropertyAccessExpression expression) returns string + + [abstract] + method encodeNonStaticPropertyAssignment(NonStaticPropertyAssignExpression expression) returns string + + [abstract] + method encodeStaticPropertyAssignment(StaticPropertyAssignExpression expression) returns string + + [abstract] + method encodeListIndex(ListIndexExpression expression) returns string + + [abstract] + method encodeListAssignment(ListAssignExpression expression) returns string + + [abstract] + method encodeStringIndex(StringIndexExpression expression) returns string + + [abstract] + method encodeMapAccess(MapAccessExpression expression) returns string + + [abstract] + method encodeMapAssignment(MapAssignExpression expression) returns string + + [abstract] + method encodeClassDeclaration(ClassDeclareStatement statement) returns string + + [abstract] + method encodeClassInstantiation(ClassInstantiateExpression expression) returns string + + [abstract] + method encodeMethodDeclaration(MethodDeclareStatement statement) returns string + + [abstract] + method encodePropertyDeclaration(PropertyDeclareStatement statement) returns string + + [abstract] + method encodeThis(ThisExpression expression) returns string + + [abstract] + method encodeEnumDeclaration(EnumDeclareStatement statement) returns string + + [abstract] + method encodeEnumFieldAccess(EnumFieldAccessExpression expression) returns string + + [abstract] + method encodeCallbackLiteral(CallbackLiteral literal) returns string + + [abstract] + method encodeImplementationCall(ImplementationCallExpression expression) returns string + + [abstract] + method encodeOfType(OfTypeExpression expression) returns string + + [abstract] + method encodeBreakStatement(BreakStatement statement) returns string + + [abstract] + method encodeContinueStatement(ContinueStatement statement) returns string + + [abstract] + method encodeCast(CastExpression expression) returns string + + [abstract] + method encodeThrowStatement(ThrowStatement statement) returns string + + [abstract] + method encodeCatchExpression(CatchExpression expression) returns string + + [abstract] + method encodeErrorPropagation(PropagateErrorExpression expression) returns string + + [abstract] + method encodeFileEmbedding(EmbedFileExpression expression) returns string + + [abstract] + method encodeDiscard(Expression expression) returns string + +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/backend/stringcode/c/.gitignore b/stdlib/aspl/compiler/backend/stringcode/c/.gitignore new file mode 100644 index 0000000..339efda --- /dev/null +++ b/stdlib/aspl/compiler/backend/stringcode/c/.gitignore @@ -0,0 +1,2 @@ +!.gitignore +!template.c \ No newline at end of file diff --git a/stdlib/aspl/compiler/backend/stringcode/c/CBackend.aspl b/stdlib/aspl/compiler/backend/stringcode/c/CBackend.aspl new file mode 100644 index 0000000..04fd3be --- /dev/null +++ b/stdlib/aspl/compiler/backend/stringcode/c/CBackend.aspl @@ -0,0 +1,2244 @@ +import aspl.compiler +import aspl.compiler.backend +import aspl.compiler.backend.stringcode +import aspl.compiler.utils +import aspl.parser +import aspl.parser.ast +import aspl.parser.ast.statements +import aspl.parser.ast.expressions +import aspl.parser.ast.literals +import aspl.parser.functions +import aspl.parser.properties +import aspl.parser.methods +import aspl.parser.classes +import aspl.parser.enums +import aspl.parser.utils +import encoding.utf8 +import io +import strings + +[public] +class CBackend extends StringcodeBackend { + + property int tempVariableId = 0 + property list callbackDeclarations + property list callbackInvokeFunctionHandledTypes + property list methodDeclarations + property list staticNormalPropertyDeclarations + property list reactivePropertyDeclarations + property list staticReactivePropertyDeclarations + property list enumDeclarations + property bool insideClassDeclaration + property map constructors + property map threadFunctionWrappers + property list threadCallbackWrappers + property list fileEmbeds + property list catchBlockDeclarations + + [public] + method compile(ParserResult result) returns CompilationResult{ + var functionDeclarationsHeadersOutput = new StringBuilder() + foreach(result.nodes as node){ + if(node oftype FunctionDeclareStatement){ // TODO: Also find functions nested in other nodes + functionDeclarationsHeadersOutput.append(this.encodeFunctionDeclarationHeader(FunctionDeclareStatement(node))) + functionDeclarationsHeadersOutput.append("\n") + } + } + functionDeclarationsHeadersOutput.append("\n") + foreach(Class:classes as c){ + foreach(c.code as node){ + this.encode(node, true) // register methods etc. + } + } + var generalOutput = new StringBuilder() + var mainBlockBegan = false + foreach(result.nodes as node){ + if(!(node oftype ClassDeclareStatement || node oftype EnumDeclareStatement || node oftype MethodDeclareStatement || node oftype PropertyDeclareStatement || node oftype FunctionDeclareStatement)){ + if(!mainBlockBegan){ + if(Options:targetOs == "android"){ + generalOutput.append("sapp_desc sokol_main(int argc, char* argv[]){\n") + }else{ + generalOutput.append("int main(int argc, char** argv){\n") + } + indentLevel++ + mainBlockBegan = true + + repeat(indentLevel){ + generalOutput.append("\t") + } + generalOutput.append("aspl_argc = argc;\n") + repeat(indentLevel){ + generalOutput.append("\t") + } + generalOutput.append("aspl_argv = argv;\n") + if(Options:targetOs == "windows"){ + repeat(indentLevel){ + generalOutput.append("\t") + } + generalOutput.append("aspl_setup_windows_console();\n") + } + repeat(indentLevel){ + generalOutput.append("\t") + } + generalOutput.append("aspl_setup_gc();\n") + if(Options:useSsl){ + repeat(indentLevel){ + generalOutput.append("\t") + } + generalOutput.append("aspl_setup_ssl();\n") + } + repeat(indentLevel){ + generalOutput.append("\t") + } + generalOutput.append("aspl_setup_enums();\n") + repeat(indentLevel){ + generalOutput.append("\t") + } + generalOutput.append("aspl_setup_parent_pointers();\n") + repeat(indentLevel){ + generalOutput.append("\t") + } + generalOutput.append("aspl_setup_method_pointers();\n") + repeat(indentLevel){ + generalOutput.append("\t") + } + generalOutput.append("aspl_setup_reactive_properties();\n") + repeat(indentLevel){ + generalOutput.append("\t") + } + generalOutput.append("aspl_setup_static_properties();\n") + generalOutput.append("\n") + repeat(indentLevel){ + generalOutput.append("\t") + } + generalOutput.append("aspl_setup_embeds();\n") + generalOutput.append("\n") + if(Options:enableErrorHandling){ + repeat(indentLevel){ + generalOutput.append("\t") + } + generalOutput.append("try{\n") + indentLevel++ + } + } + } + generalOutput.append(this.encode(node, true)) + generalOutput.append("\n") + } + if(mainBlockBegan){ + if(Options:enableErrorHandling){ + indentLevel-- + repeat(indentLevel){ + generalOutput.append("\t") + } + generalOutput.append("}catch{\n") + indentLevel++ + repeat(indentLevel){ + generalOutput.append("\t") + } + generalOutput.append("ASPL_PANIC(\"Uncaught %s error\", ASPL_ACCESS(aspl_current_error).value.classInstance->typePtr);\n") + indentLevel-- + repeat(indentLevel){ + generalOutput.append("\t") + } + generalOutput.append("}\n") + } + repeat(indentLevel){ + generalOutput.append("\t") + } + if(Options:targetOs == "android"){ + generalOutput.append("return aspl_global_sapp_desc;\n") + }else{ + generalOutput.append("return 0;\n") + } + indentLevel-- + repeat(indentLevel){ + generalOutput.append("\t") + } + generalOutput.append("}") + } + var includeOutput = new StringBuilder() + foreach(IncludeUtils:files as file){ + includeOutput.append("#include \"").append(file).append("\"\n") + } + if(IncludeUtils:files.length > 0){ + includeOutput.append("\n") + } + var newClassDeclarationHeadersOutput = new StringBuilder() + foreach(Class:classes as c){ + newClassDeclarationHeadersOutput.append("ASPL_OBJECT_TYPE aspl_new_" + TypeUtils:typeToCIdentifier(c.type.identifier) + "(") + if(constructors.containsKey(c.type.identifier)){ + var constructor = constructors[c.type.identifier] + if(constructor.parameters.length > 0){ + var i = 0 + foreach(constructor.parameters as parameter){ + newClassDeclarationHeadersOutput.append("ASPL_OBJECT_TYPE* " + parameter.name) + if(i < constructor.parameters.length - 1){ + newClassDeclarationHeadersOutput.append(", ") + } + i++ + } + } + } + newClassDeclarationHeadersOutput.append(");\n") + } + var enumDeclarationsOutput = new StringBuilder() + foreach(this.enumDeclarations as declaration){ + enumDeclarationsOutput.append("ASPL_Enum* aspl_enum_").append(TypeUtils:typeToCIdentifier(declaration.e.type.identifier)).append(";\n") + } + if(this.enumDeclarations.length > 0){ + enumDeclarationsOutput.append("\n") + } + var methodDeclarationsHeadersOutput = new StringBuilder() + foreach(this.methodDeclarations as declaration){ + methodDeclarationsHeadersOutput.append(this.encodeMethodDeclarationHeader(declaration)) + methodDeclarationsHeadersOutput.append("\n") + } + if(this.methodDeclarations.length > 0){ + methodDeclarationsHeadersOutput.append("\n") + } + var threadFunctionWrappersOutput = new StringBuilder() + foreach(this.threadFunctionWrappers as identifier => func){ + threadFunctionWrappersOutput.append("int aspl_function_" + identifier.replace(".", "$") + "_thread_wrapper(void* args){\n") + indentLevel++ + repeat(indentLevel){ + threadFunctionWrappersOutput.append("\t") + } + threadFunctionWrappersOutput.append("struct GC_stack_base sb;\n") + repeat(indentLevel){ + threadFunctionWrappersOutput.append("\t") + } + threadFunctionWrappersOutput.append("GC_get_stack_base(&sb);\n") + repeat(indentLevel){ + threadFunctionWrappersOutput.append("\t") + } + threadFunctionWrappersOutput.append("GC_register_my_thread(&sb);\n") + repeat(indentLevel){ + threadFunctionWrappersOutput.append("\t") + } + threadFunctionWrappersOutput.append("aspl_function_" + identifier.replace(".", "$") + "(") + var i = 0 + foreach(func.parameters as parameter){ + threadFunctionWrappersOutput.append("((ASPL_OBJECT_TYPE**)args)[" + i + "]") + if(i < func.parameters.length - 1){ + threadFunctionWrappersOutput.append(", ") + } + i++ + } + threadFunctionWrappersOutput.append(");\n") + repeat(indentLevel){ + threadFunctionWrappersOutput.append("\t") + } + threadFunctionWrappersOutput.append("GC_unregister_my_thread();\n") + repeat(indentLevel){ + threadFunctionWrappersOutput.append("\t") + } + threadFunctionWrappersOutput.append("return 0;\n") + indentLevel-- + repeat(indentLevel){ + threadFunctionWrappersOutput.append("\t") + } + threadFunctionWrappersOutput.append("}\n") + } + if(this.threadFunctionWrappers.length > 0){ + threadFunctionWrappersOutput.append("\n") + } + var threadCallbackWrappersOutput = new StringBuilder() + foreach(this.threadCallbackWrappers as identifier){ + threadCallbackWrappersOutput.append("int aspl_" + TypeUtils:typeToCIdentifier(identifier) + "_invoke_thread_wrapper(void* args){\n") + indentLevel++ + repeat(indentLevel){ + threadCallbackWrappersOutput.append("\t") + } + threadCallbackWrappersOutput.append("struct GC_stack_base sb;\n") + repeat(indentLevel){ + threadCallbackWrappersOutput.append("\t") + } + threadCallbackWrappersOutput.append("GC_get_stack_base(&sb);\n") + repeat(indentLevel){ + threadCallbackWrappersOutput.append("\t") + } + threadCallbackWrappersOutput.append("GC_register_my_thread(&sb);\n") + repeat(indentLevel){ + threadCallbackWrappersOutput.append("\t") + } + threadCallbackWrappersOutput.append("ASPL_OBJECT_TYPE closure = *(((ASPL_OBJECT_TYPE**)args)[0]);\n") + repeat(indentLevel){ + threadCallbackWrappersOutput.append("\t") + } + threadCallbackWrappersOutput.append("ASPL_ClosureMap* closure_map = ASPL_ACCESS(closure).value.callback->closure_map;\n") + repeat(indentLevel){ + threadCallbackWrappersOutput.append("\t") + } + threadCallbackWrappersOutput.append("aspl_" + TypeUtils:typeToCIdentifier(identifier) + "_invoke(closure") + var genericTypes = Type:getGenericTypesIdentifiers(identifier) + if(genericTypes.length > 0){ + threadCallbackWrappersOutput.append(", ") + } + var i = 0 + while(i < genericTypes.length){ + threadCallbackWrappersOutput.append("((ASPL_OBJECT_TYPE**)args)[").append(string(i + 1)).append("]") + if(i < genericTypes.length - 1){ + threadCallbackWrappersOutput.append(", ") + } + i++ + } + threadCallbackWrappersOutput.append(");\n") + repeat(indentLevel){ + threadCallbackWrappersOutput.append("\t") + } + threadCallbackWrappersOutput.append("GC_unregister_my_thread();\n") + repeat(indentLevel){ + threadCallbackWrappersOutput.append("\t") + } + threadCallbackWrappersOutput.append("return 0;\n") + this.indentLevel-- + repeat(indentLevel){ + threadCallbackWrappersOutput.append("\t") + } + threadCallbackWrappersOutput.append("}\n") + } + if(this.threadCallbackWrappers.length > 0){ + threadCallbackWrappersOutput.append("\n") + } + var staticNormalPropertyDeclarationsOutput = new StringBuilder() + foreach(this.staticNormalPropertyDeclarations as declaration){ + staticNormalPropertyDeclarationsOutput.append(this.encodeStaticNormalPropertyDeclaration(declaration)) + staticNormalPropertyDeclarationsOutput.append("\n") + } + if(this.staticNormalPropertyDeclarations.length > 0){ + staticNormalPropertyDeclarationsOutput.append("\n") + } + var reactivePropertyDeclarationHeadersOutput = new StringBuilder() + foreach(this.reactivePropertyDeclarations as declaration){ + reactivePropertyDeclarationHeadersOutput.append(this.encodeReactivePropertyDeclarationHeader(declaration)) + reactivePropertyDeclarationHeadersOutput.append("\n") + } + if(this.reactivePropertyDeclarations.length > 0){ + reactivePropertyDeclarationHeadersOutput.append("\n") + } + var staticReactivePropertyDeclarationHeadersOutput = new StringBuilder() + foreach(this.staticReactivePropertyDeclarations as declaration){ + staticReactivePropertyDeclarationHeadersOutput.append(this.encodeStaticReactivePropertyDeclarationHeader(declaration)) + staticReactivePropertyDeclarationHeadersOutput.append("\n") + } + if(this.staticReactivePropertyDeclarations.length > 0){ + staticReactivePropertyDeclarationHeadersOutput.append("\n") + } + var reactivePropertyDeclarationsOutput = new StringBuilder() + foreach(this.reactivePropertyDeclarations as declaration){ + reactivePropertyDeclarationsOutput.append(this.encodeReactivePropertyDeclaration(declaration)) + reactivePropertyDeclarationsOutput.append("\n") + } + if(this.reactivePropertyDeclarations.length > 0){ + reactivePropertyDeclarationsOutput.append("\n") + } + var staticReactivePropertyDeclarationsOutput = new StringBuilder() + foreach(this.staticReactivePropertyDeclarations as declaration){ + staticReactivePropertyDeclarationsOutput.append(this.encodeStaticReactivePropertyDeclaration(declaration)) + staticReactivePropertyDeclarationsOutput.append("\n") + } + if(this.staticReactivePropertyDeclarations.length > 0){ + staticReactivePropertyDeclarationsOutput.append("\n") + } + var newClassDeclarationsOutput = new StringBuilder() + foreach(Class:classes as c){ + newClassDeclarationsOutput.append("ASPL_OBJECT_TYPE aspl_new_" + TypeUtils:typeToCIdentifier(c.type.identifier) + "(") + if(constructors.containsKey(c.type.identifier)){ + var constructor = constructors[c.type.identifier] + if(constructor.parameters.length > 0){ + var i = 0 + foreach(constructor.parameters as parameter){ + newClassDeclarationsOutput.append("ASPL_OBJECT_TYPE* " + parameter.name) + if(i < constructor.parameters.length - 1){ + newClassDeclarationsOutput.append(", ") + } + i++ + } + } + } + newClassDeclarationsOutput.append("){\n") + indentLevel++ + var indentStub = "" + repeat(indentLevel){ + indentStub += "\t" + } + newClassDeclarationsOutput.append(indentStub + "ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();\n") + newClassDeclarationsOutput.append(indentStub + "ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_CLASS_INSTANCE;\n") + newClassDeclarationsOutput.append(indentStub + "ASPL_ClassInstance* instance = ASPL_MALLOC(sizeof(ASPL_ClassInstance));\n") + newClassDeclarationsOutput.append(indentStub + "instance->typePtr = \"" + c.type.identifier + "\";\n") + newClassDeclarationsOutput.append(indentStub + "instance->typeLen = " + TypeUtils:typeToCIdentifier(c.type.identifier).length + ";\n") + newClassDeclarationsOutput.append(indentStub + "instance->properties = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig){.initial_capacity = 1});\n") + if(Options:enableErrorHandling){ + var isError = "0" + if(c.isError){ + isError = "1" + } + newClassDeclarationsOutput.append(indentStub + "instance->isError = " + isError + ";\n") + } + newClassDeclarationsOutput.append(indentStub + "ASPL_ACCESS(obj).value.classInstance = instance;\n") + if(constructors.containsKey(c.type.identifier)){ + newClassDeclarationsOutput.append(indentStub + "aspl_method_" + TypeUtils:typeToCIdentifier(c.type.identifier) + "_construct(C_REFERENCE(obj)") + var constructor = constructors[c.type.identifier] + if(constructor.parameters.length > 0){ + newClassDeclarationsOutput.append(", ") + var i = 0 + foreach(constructor.parameters as parameter){ + newClassDeclarationsOutput.append(parameter.name) + if(i < constructor.parameters.length - 1){ + newClassDeclarationsOutput.append(", ") + } + i++ + } + } + newClassDeclarationsOutput.append(");\n") + } + newClassDeclarationsOutput.append(indentStub + "return obj;\n") + indentLevel-- + newClassDeclarationsOutput.append("}\n") + } + var methodDeclarationsOutput = new StringBuilder() + foreach(this.methodDeclarations as declaration){ + methodDeclarationsOutput.append(this.encodeMethodDeclarationOutsideClass(declaration)) + methodDeclarationsOutput.append("\n") + } + if(this.methodDeclarations.length > 0){ + methodDeclarationsOutput.append("\n") + } + var setupParentPointersOutput = new StringBuilder() + setupParentPointersOutput.append("void aspl_setup_parent_pointers(){\n") + indentLevel++ + repeat(indentLevel){ + setupParentPointersOutput.append("\t") + } + setupParentPointersOutput.append("class_parents_map = *hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig){.initial_capacity = 1});\n\n") + foreach(Class:classes as c){ + repeat(indentLevel){ + setupParentPointersOutput.append("\t") + } + foreach(ClassUtils:getAllParentsRecursively(c) as parent){ + repeat(indentLevel){ + setupParentPointersOutput.append("\t") + } + setupParentPointersOutput.append("aspl_class_parent_init(\"" + c.type.identifier + "\", \"" + parent.identifier + "\");\n") + } + } + indentLevel-- + setupParentPointersOutput.append("}\n") + setupParentPointersOutput.append("\n") + var setupMethodPointersOutput = new StringBuilder() + setupMethodPointersOutput.append("void aspl_setup_method_pointers(){\n") + indentLevel++ + repeat(indentLevel){ + setupMethodPointersOutput.append("\n") + } + setupMethodPointersOutput.append("aspl_setup_builtin_method_pointers();") + if(Class:classes.length > 0){ + setupMethodPointersOutput.append("\n\n") + } + foreach(Class:classes as c){ + repeat(indentLevel){ + setupMethodPointersOutput.append("\t") + } + foreach(Method:getAllFor(c.type) as m){ + if(m.createdFromAny || m.isStatic || m.isAbstract){ + continue + } + repeat(indentLevel){ + setupMethodPointersOutput.append("\t") + } + setupMethodPointersOutput.append("aspl_object_method_init(\"" + c.type.identifier + "\", \"" + m.name + "\", " + "aspl_method_" + TypeUtils:typeToCIdentifier(m.type.identifier) + "_" + m.name + "_wrapper);\n") + } + setupMethodPointersOutput.append("\n") // TODO: Do not append if last handled class + } + indentLevel-- + setupMethodPointersOutput.append("}\n") + setupMethodPointersOutput.append("\n") + var setupReactivePropertiesOutput = new StringBuilder() + setupReactivePropertiesOutput.append("void aspl_setup_reactive_properties(){\n") + indentLevel++ + repeat(indentLevel){ + setupReactivePropertiesOutput.append("\t") + } + setupReactivePropertiesOutput.append("reactive_properties_map = *hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig){.initial_capacity = 1});\n\n") + foreach(Class:classes as c){ + foreach(Property:getAllFor(c.type) as p){ + if(p.isStatic || !(p oftype CustomReactiveProperty)){ + continue + } + repeat(indentLevel){ + setupReactivePropertiesOutput.append("\t") + } + var getCallback = "NULL" + if(CustomReactiveProperty(p).getCode != null){ + getCallback = "(void*)aspl_reactive_property_" + TypeUtils:typeToCIdentifier(p.type.identifier) + "_" + p.name + "_get" + } + var setCallback = "NULL" + if(CustomReactiveProperty(p).setCode != null){ + setCallback = "(void*)aspl_reactive_property_" + TypeUtils:typeToCIdentifier(p.type.identifier) + "_" + p.name + "_set" + } + setupReactivePropertiesOutput.append("ASPL_CLASS_INIT_REACTIVE_PROPERTY(\"").append(c.type.identifier).append("\", \"").append(p.name).append("\", ").append(getCallback).append(", ").append(setCallback).append(");\n") + } + } + indentLevel-- + setupReactivePropertiesOutput.append("}\n") + setupReactivePropertiesOutput.append("\n") + var setupStaticPropertiesOutput = new StringBuilder() + setupStaticPropertiesOutput.append("void aspl_setup_static_properties(){\n") + indentLevel++ + foreach(this.staticNormalPropertyDeclarations as declaration){ + repeat(indentLevel){ + setupStaticPropertiesOutput.append("\t") + } + setupStaticPropertiesOutput.append("aspl_static_property_" + TypeUtils:typeToCIdentifier(declaration.p.type.identifier) + "_" + declaration.p.name + " = C_REFERENCE(") + if(CustomNormalProperty(declaration.p).defaultValue == null){ + setupStaticPropertiesOutput.append("ASPL_NULL()") + }else{ + setupStaticPropertiesOutput.append(this.encode(CustomNormalProperty(declaration.p).defaultValue)) + } + setupStaticPropertiesOutput.append(");\n") + } + indentLevel-- + setupStaticPropertiesOutput.append("}\n") + setupStaticPropertiesOutput.append("\n") + var setupEnumsOutput = new StringBuilder() + setupEnumsOutput.append("void aspl_setup_enums(){\n") + indentLevel++ + repeat(indentLevel){ + setupEnumsOutput.append("\t") + } + var initialCapacity = this.enumDeclarations.length + if(initialCapacity < 1){ + initialCapacity = 1 + } + setupEnumsOutput.append("enums_map = *hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig){.initial_capacity = ").append(string(initialCapacity)).append("});\n") + foreach(this.enumDeclarations as declaration){ + repeat(indentLevel){ + setupEnumsOutput.append("\t") + } + setupEnumsOutput.append("aspl_enum_").append(TypeUtils:typeToCIdentifier(declaration.e.type.identifier)).append(" = ASPL_MALLOC(sizeof(ASPL_Enum));\n") + repeat(indentLevel){ + setupEnumsOutput.append("\t") + } + setupEnumsOutput.append("aspl_enum_").append(TypeUtils:typeToCIdentifier(declaration.e.type.identifier)).append("->typePtr = \"").append(declaration.e.type.identifier).append("\";\n") + repeat(indentLevel){ + setupEnumsOutput.append("\t") + } + setupEnumsOutput.append("aspl_enum_").append(TypeUtils:typeToCIdentifier(declaration.e.type.identifier)).append("->typeLen = ").append(string(declaration.e.type.identifier.length)).append(";\n") + repeat(indentLevel){ + setupEnumsOutput.append("\t") + } + var isFlags = "0" + if(declaration.e.isFlags){ + isFlags = "1" + } + setupEnumsOutput.append("aspl_enum_").append(TypeUtils:typeToCIdentifier(declaration.e.type.identifier)).append("->isFlagEnum = ").append(isFlags).append(";\n") + repeat(indentLevel){ + setupEnumsOutput.append("\t") + } + setupEnumsOutput.append("aspl_enum_").append(TypeUtils:typeToCIdentifier(declaration.e.type.identifier)).append("->stringValues = hashmap_int_to_str_new_hashmap((hashmap_int_to_str_HashMapConfig){.initial_capacity = ").append(string(declaration.e.fields?!.length)).append("});\n") + foreach(declaration.e.fields as field){ + repeat(indentLevel){ + setupEnumsOutput.append("\t") + } + setupEnumsOutput.append("hashmap_int_to_str_hashmap_set(aspl_enum_").append(TypeUtils:typeToCIdentifier(declaration.e.type.identifier)).append("->stringValues, ").append(string(field.value)).append(", \"").append(field.name).append("\");\n") + } + repeat(indentLevel){ + setupEnumsOutput.append("\t") + } + setupEnumsOutput.append("hashmap_str_to_voidptr_hashmap_set(&enums_map, \"").append(declaration.e.type.identifier).append("\", aspl_enum_").append(TypeUtils:typeToCIdentifier(declaration.e.type.identifier)).append(");\n") + } + indentLevel-- + setupEnumsOutput.append("}\n") + setupEnumsOutput.append("\n") + var callbackDeclarationsOutput = new StringBuilder() + foreach(this.callbackDeclarations as declaration){ // TODO: This will ignore callbacks inside methods etc. as they are registered later; TODO: is this TODO still up to date? + callbackDeclarationsOutput.append(this.encodeCallbackDeclaration(declaration)) + callbackDeclarationsOutput.append("\n") + } + if(this.callbackDeclarations.length > 0){ + callbackDeclarationsOutput.append("\n") + } + this.callbackInvokeFunctionHandledTypes = [] + var callbackDeclarationHeadersOutput = new StringBuilder() + foreach(this.callbackDeclarations as declaration){ + callbackDeclarationHeadersOutput.append(this.encodeCallbackDeclarationHeader(declaration)) + callbackDeclarationHeadersOutput.append("\n") + } + if(this.callbackDeclarations.length > 0){ + callbackDeclarationHeadersOutput.append("\n") + } + var catchBlockDeclarationsOutput = new StringBuilder() + foreach(this.catchBlockDeclarations as declaration){ + catchBlockDeclarationsOutput.append(this.encodeCatchBlockDeclaration(declaration)) + catchBlockDeclarationsOutput.append("\n") + } + if(this.catchBlockDeclarations.length > 0){ + catchBlockDeclarationsOutput.append("\n") + } + var fileEmbedsOutput = new StringBuilder() + { + var int i = 0 + foreach(this.fileEmbeds as expression){ + fileEmbedsOutput.append("ASPL_OBJECT_TYPE aspl_embed_") + fileEmbedsOutput.append(string(i)) + fileEmbedsOutput.append(";\n") + i++ + } + if(this.fileEmbeds.length > 0){ + fileEmbedsOutput.append("\n") + } + fileEmbedsOutput.append("void aspl_setup_embeds(){\n") + indentLevel++ + i = 0 + foreach(this.fileEmbeds as expression){ + var bytes = io.read_file_bytes(expression.file) + repeat(indentLevel){ + fileEmbedsOutput.append("\t") + } + fileEmbedsOutput.append("ASPL_OBJECT_TYPE* aspl_embed_") + fileEmbedsOutput.append(string(i)) + fileEmbedsOutput.append("_array = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * ") + fileEmbedsOutput.append(string(bytes.length)) + fileEmbedsOutput.append(");\n") + repeat(indentLevel){ + fileEmbedsOutput.append("\t") + } + fileEmbedsOutput.append("static unsigned char const aspl_embed_") + fileEmbedsOutput.append(string(i)) + fileEmbedsOutput.append("_carray[] = {") + var j = 0 + foreach(bytes as b){ + fileEmbedsOutput.append(string(b)) + if(j < bytes.length - 1){ + fileEmbedsOutput.append(", ") + } + j++ + } + fileEmbedsOutput.append("};\n") + repeat(indentLevel){ + fileEmbedsOutput.append("\t") + } + fileEmbedsOutput.append("for(int i = 0; i < ") + fileEmbedsOutput.append(string(bytes.length)) + fileEmbedsOutput.append("; i++){\n") + indentLevel++ + repeat(indentLevel){ + fileEmbedsOutput.append("\t") + } + fileEmbedsOutput.append("aspl_embed_") + fileEmbedsOutput.append(string(i)) + fileEmbedsOutput.append("_array[i] = ASPL_BYTE_LITERAL(aspl_embed_") + fileEmbedsOutput.append(string(i)) + fileEmbedsOutput.append("_carray[i]);\n") + indentLevel-- + repeat(indentLevel){ + fileEmbedsOutput.append("\t") + } + fileEmbedsOutput.append("}\n") + repeat(indentLevel){ + fileEmbedsOutput.append("\t") + } + fileEmbedsOutput.append("aspl_embed_") + fileEmbedsOutput.append(string(i)) + fileEmbedsOutput.append(" = ") + fileEmbedsOutput.append("ASPL_LIST_LITERAL(\"list\", 10, aspl_embed_") + fileEmbedsOutput.append(string(i)) + fileEmbedsOutput.append("_array, ") + fileEmbedsOutput.append(string(bytes.length)) + fileEmbedsOutput.append(");\n") + i++ + } + indentLevel-- + } + fileEmbedsOutput.append("}\n") + + var template = encoding.utf8.decode($embed("template.c")) + if(Options:useDynamicCTemplate){ + template = io.read_file(io.join_path([io.full_directory_path(io.get_executable_path()), "stdlib", "aspl", "compiler", "backend", "stringcode", "c", "template.c"])) + } + var output = new StringBuilder(template).append("\n\n// Everything from now on is generated by the ASPL compiler:\n\n") + output.append(includeOutput) + output.append("\n") + output.append(fileEmbedsOutput) + output.append("\n") + output.append(functionDeclarationsHeadersOutput) + output.append("\n") + output.append(newClassDeclarationHeadersOutput) + output.append(enumDeclarationsOutput) + output.append(methodDeclarationsHeadersOutput) + output.append(callbackDeclarationHeadersOutput) + output.append(threadFunctionWrappersOutput) + output.append(threadCallbackWrappersOutput) + output.append(staticNormalPropertyDeclarationsOutput) + output.append(reactivePropertyDeclarationHeadersOutput) + output.append(staticReactivePropertyDeclarationHeadersOutput) + output.append(reactivePropertyDeclarationsOutput) + output.append(staticReactivePropertyDeclarationsOutput) + output.append(newClassDeclarationsOutput) + output.append("\n") + output.append(methodDeclarationsOutput) + output.append(callbackDeclarationsOutput) + output.append(catchBlockDeclarationsOutput) + output.append("\n") + output.append(setupParentPointersOutput) + output.append(setupMethodPointersOutput) + output.append(setupReactivePropertiesOutput) + output.append(setupStaticPropertiesOutput) + output.append(setupEnumsOutput) + output.append("\n") + output.append(generalOutput) + + return new CompilationResult(encoding.utf8.encode(output.toString())) + } + + method encodeCodeBlock(BlockStatement statement) returns string{ + var s = new StringBuilder("{\n") + indentLevel++ + foreach(statement.statements as node){ + s.append(this.encode(node, true)) + s.append("\n") + } + indentLevel-- + repeat(indentLevel){ + s.append("\t") + } + s.append("}") + return s.toString() + } + + method getLineTerminator() returns string{ + return ";" + } + + method encodeNullLiteral(NullLiteral literal) returns string{ + return "ASPL_NULL()" + } + + method encodeBooleanLiteral(BooleanLiteral literal) returns string{ + if(literal.value){ + return "ASPL_TRUE()" + }else{ + return "ASPL_FALSE()" + } + } + + method encodeByteLiteral(ByteLiteral literal) returns string{ + return "ASPL_BYTE_LITERAL(" + literal.value + ")" + } + + method encodeIntegerLiteral(IntegerLiteral literal) returns string{ + return "ASPL_INT_LITERAL(" + literal.value + ")" + } + + method encodeLongLiteral(LongLiteral literal) returns string{ + return "ASPL_LONG_LITERAL(" + literal.value + ")" + } + + method encodeFloatLiteral(FloatLiteral literal) returns string{ + return "ASPL_FLOAT_LITERAL(" + literal.value + ")" + } + + method encodeDoubleLiteral(DoubleLiteral literal) returns string{ + return "ASPL_DOUBLE_LITERAL(" + literal.value + ")" + } + + method encodeStringLiteral(StringLiteral literal) returns string{ + var s = literal.literalString + var s2 = "" + if(!s.contains("\\u")){ // Optimize for common case + s2 = s + }else{ + var i = 0 + while(i < s.length){ // TODO: Speed this up somehow + if(s[i] == "\\" && i < s.length - 1 && s[i + 1] == "u"){ + if(i > 0 && s[i - 1] == "\\"){ // TODO: Figure out why this is necessary + s2 += s[i] + i++ + continue + } + if(i < s.length - 5){ + var codepoint = s.after(i + 1).before(4) // Note: before(4) is correct here, as it operates on the already sliced string + var value = encoding.hex.encode(codepoint) + if(value > 159){ + s2 += "\\u" + codepoint + }else{ + s2 += "\\x" + codepoint + } + i += 5 + } + }else{ + s2 += s[i] + } + i++ + } + } + return "ASPL_STRING_LITERAL_NO_COPY(\"" + s2 + "\")" + } + + method encodeListLiteral(ListLiteral literal) returns string{ + var s = new StringBuilder("ASPL_LIST_LITERAL(\"").append(literal.getType().toString()).append("\", ").append(string(literal.getType().toString().length)).append(", (ASPL_OBJECT_TYPE[]){") + var i = 0 + foreach(literal.value as value){ + s.append(this.encode(value)) + if(i < literal.value.length - 1){ + s.append(", ") + } + i++ + } + s.append("}, " + literal.value.length + ")") + return s.toString() + } + + method encodeMapLiteral(MapLiteral literal) returns string{ + var s = new StringBuilder("ASPL_MAP_LITERAL(\"").append(literal.getType().toString()).append("\", ").append(string(literal.getType().toString().length)).append(", (ASPL_OBJECT_TYPE[]){") + var i = 0 + foreach(literal.value as pair){ + s.append(this.encode(pair.k)) + s.append(", ") + s.append(this.encode(pair.v)) + if(i < literal.value.length - 1){ + s.append(", ") + } + } + s.append("}, " + literal.value.length + ")") + return s.toString() + } + + method encodeAssertion(AssertStatement statement) returns string{ + return "ASPL_ASSERT(" + this.encode(statement.expression) + ", \"" + Location(statement.location).file.replace("\\", "\\\\") + "\", " + Location(statement.location).startLine + ", " + Location(statement.location).startColumn + ")" + } + + method encodeEqualsCheck(CheckEqualsExpression expression) returns string{ + return "ASPL_EQUALS(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")" + } + + method encodeNegation(NegateExpression expression) returns string{ + return "ASPL_NEGATE(" + this.encode(expression.expression) + ")" + } + + method encodeFunctionCall(FunctionCallExpression expression) returns string{ + var s = new StringBuilder() + if(expression.newThread){ + s.append("ASPL_LAUNCH_THREAD(") + s.append("aspl_function_").append(expression.func.identifier.replace(".", "$")).append("_thread_wrapper, (ASPL_OBJECT_TYPE*[]){") + this.threadFunctionWrappers[expression.func.identifier] = expression.func + }else{ + s.append("aspl_function_").append(expression.func.identifier.replace(".", "$")).append("(") + } + var i = 0 + foreach(expression.arguments as argument){ + s.append("C_REFERENCE(").append(this.encode(argument)).append(")") + if(i < expression.arguments.length - 1){ + s.append(", ") + } + i++ + } + if(expression.arguments.length < expression.func.parameters.length){ + if(expression.arguments.length > 0){ + s.append(", ") + } + while(i < expression.func.parameters.length){ + var parameter = expression.func.parameters[i] + if(parameter.defaultValue != null){ + s.append("C_REFERENCE(").append(this.encode(parameter.defaultValue)).append(")") + if(i < expression.func.parameters.length - 1){ + s.append(", ") + } + } + i++ + } + } + if(expression.newThread){ + s.append("}") + var length = expression.arguments.length + if(expression.arguments.length < expression.func.parameters.length){ + length = expression.func.parameters.length + } + s.append(", ").append(string(length)).append(" * sizeof(ASPL_OBJECT_TYPE*)") + } + s.append(")") + return s.toString() + } + + method encodeVariableDeclaration(VariableDeclareExpression expression) returns string{ + return "ASPL_OBJECT_TYPE* " + IdentifierEscapeUtils:escapeIdentifier(expression.variable.identifier) + " = C_REFERENCE(" + this.encode(expression.value) + ")" // TODO: Make this usable as an expression + } + + method encodeVariableAccess(VariableAccessExpression expression) returns string{ + return "*" + IdentifierEscapeUtils:escapeIdentifier(expression.variable.identifier) + } + + method encodeVariableAssignment(VariableAssignExpression expression) returns string{ + return "*" + IdentifierEscapeUtils:escapeIdentifier(expression.variable.identifier) + " = " + this.encode(expression.value) + } + + method encodeFunctionDeclaration(FunctionDeclareStatement statement) returns string{ + var s = new StringBuilder("") + if(statement.func.returnTypes.types.length > 0 || statement.func.canThrow){ + s.append("ASPL_OBJECT_TYPE ") + }else{ + s.append("void ") + } + s.append("aspl_function_").append(statement.func.identifier.replace(".", "$")).append("(") + var i = 0 + foreach(statement.func.parameters as parameter){ + s.append(this.encodeParameter(parameter)) + if(i < statement.func.parameters.length - 1){ + s.append(", ") + } + i++ + } + s.append("){\n") + this.indentLevel++ + if(Options:enableErrorHandling){ + repeat(indentLevel){ + s.append("\t") + } + s.append("try{\n") + this.indentLevel++ + } + foreach(CustomFunction(statement.func).code as node){ + s.append(this.encode(node, true)) + s.append("\n") + } + if(Options:enableErrorHandling){ + repeat(indentLevel){ + s.append("\t") + } + s.append("return_uninitialized_from_try;\n") + } + this.indentLevel-- + if(Options:enableErrorHandling){ + repeat(indentLevel){ + s.append("\t") + } + s.append("}catch{\n") + this.indentLevel++ + repeat(indentLevel){ + s.append("\t") + } + s.append("return aspl_current_error;\n") + this.indentLevel-- + repeat(indentLevel){ + s.append("\t") + } + s.append("}\n") + this.indentLevel-- + } + repeat(indentLevel){ + s.append("\t") + } + s.append("}") + s.append("\n") + return s.toString() + } + + method encodeFunctionDeclarationHeader(FunctionDeclareStatement statement) returns string{ + var s = new StringBuilder("") + if(statement.func.returnTypes.types.length > 0 || statement.func.canThrow){ + s.append("ASPL_OBJECT_TYPE ") + }else{ + s.append("void ") + } + s.append("aspl_function_").append(statement.func.identifier.replace(".", "$")).append("(") + var i = 0 + foreach(statement.func.parameters as parameter){ + s.append(this.encodeParameter(parameter)) + if(i < statement.func.parameters.length - 1){ + s.append(", ") + } + i++ + } + s.append(");") + return s.toString() + } + + method encodeParameter(Parameter parameter) returns string{ + var s = "ASPL_OBJECT_TYPE* " + parameter.name + if(parameter.optional){ + s += " /* optional */" + } + return s + } + + method encodeReturnStatement(ReturnStatement statement) returns string{ + if(statement.value != null){ + if(Options:enableErrorHandling){ + return "return_from_try(" + this.encode(statement.value) + ")" + }else{ + return "return " + this.encode(statement.value) + } + }else{ + if(Options:enableErrorHandling){ + if(ErrorUtils:canCallableThrow(statement.callable)){ + return "return_uninitialized_from_try" + }else{ + return "return_void_from_try" + } + }else{ + return "return" + } + } + } + + method encodeFallbackStatement(FallbackStatement statement) returns string{ + return "return " + this.encode(statement.value) + } + + method encodeEscapeStatement(EscapeStatement statement) returns string{ + if(statement.value != null){ + return "escape(" + this.encode(statement.value) + ")" + }else{ + return "escape(ASPL_NULL())" + } + } + + method encodeAnd(AndExpression expression) returns string{ + if(Type:matches(new Types([Type:fromString("boolean")]), expression.getType())){ + return "ASPL_AND(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")" + }else{ + return "ASPL_ENUM_AND(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")" + } + } + + method encodeOr(OrExpression expression) returns string{ + if(Type:matches(new Types([Type:fromString("boolean")]), expression.getType())){ + return "ASPL_OR(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")" + }else{ + return "ASPL_ENUM_OR(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")" + } + } + + method encodeXor(XorExpression expression) returns string{ + if(Type:matches(new Types([Type:fromString("boolean")]), expression.getType())){ + return "ASPL_XOR(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")" + }else{ + return "ASPL_ENUM_XOR(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")" + } + } + + method canAccessNumberValue(Expression expression) returns bool{ + if(expression.getType().types.length > 1){ + return false + } + if(Type:matches(new Types([Type:fromString("byte")]), expression.getType())){ + return true + }elseif(Type:matches(new Types([Type:fromString("integer")]), expression.getType())){ + return true + }elseif(Type:matches(new Types([Type:fromString("long")]), expression.getType())){ + return true + }elseif(Type:matches(new Types([Type:fromString("float")]), expression.getType())){ + return true + }elseif(Type:matches(new Types([Type:fromString("double")]), expression.getType())){ + return true + }elseif(Enum:enums.containsKey(expression.getType().types[0].identifier)){ + return true + } + return false + } + + method encodeNumberValueAccess(Expression expression) returns string{ + if(expression.getType().types.length > 1){ + aspl.parser.utils.type_error("Cannot directly access the value of a number with an ambiguous type", expression.location) + } + if(Type:matches(new Types([Type:fromString("byte")]), expression.getType())){ + return "(ASPL_ACCESS(" + this.encode(expression) + ").value.integer8)" + }elseif(Type:matches(new Types([Type:fromString("integer")]), expression.getType())){ + return "(ASPL_ACCESS(" + this.encode(expression) + ").value.integer32)" + }elseif(Type:matches(new Types([Type:fromString("long")]), expression.getType())){ + return "(ASPL_ACCESS(" + this.encode(expression) + ").value.integer64)" + }elseif(Type:matches(new Types([Type:fromString("float")]), expression.getType())){ + return "(ASPL_ACCESS(" + this.encode(expression) + ").value.float32)" + }elseif(Type:matches(new Types([Type:fromString("double")]), expression.getType())){ + return "(ASPL_ACCESS(" + this.encode(expression) + ").value.float64)" + }elseif(Enum:enums.containsKey(expression.getType().types[0].identifier)){ + return "(ASPL_ACCESS(" + this.encode(expression) + ").value.enumField->intValue)" + }else{ + aspl.parser.utils.type_error("Cannot directly access the value of a number with a non-numeric type", expression.location) + } + return "-1" + } + + method encodePlus(PlusExpression expression) returns string{ + if(expression.right oftype IntegerLiteral && IntegerLiteral(expression.right).value == 1){ + return "ASPL_PLUS_PLUS(" + this.encode(expression.left) + ")" + } + return "ASPL_PLUS(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")" + } + + method encodeMinus(MinusExpression expression) returns string{ + return "ASPL_MINUS(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")" + } + + method encodeMultiplication(MultiplyExpression expression) returns string{ + return "ASPL_MULTIPLY(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")" + } + + method encodeDivision(DivideExpression expression) returns string{ + return "ASPL_DIVIDE(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")" + } + + method encodeModulo(ModuloExpression expression) returns string{ + return "ASPL_MODULO(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")" + } + + method encodeLessThan(LessThanExpression expression) returns string{ + if(canAccessNumberValue(expression.left) && canAccessNumberValue(expression.right)){ + return "ASPL_BOOL_LITERAL(" + encodeNumberValueAccess(expression.left) + " < " + encodeNumberValueAccess(expression.right) + ")" + }else{ + return "ASPL_LESS_THAN(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")" + } + } + + method encodeLessThanOrEqual(LessThanOrEqualExpression expression) returns string{ + if(canAccessNumberValue(expression.left) && canAccessNumberValue(expression.right)){ + return "ASPL_BOOL_LITERAL(" + encodeNumberValueAccess(expression.left) + " <= " + encodeNumberValueAccess(expression.right) + ")" + }else{ + return "ASPL_LESS_THAN_OR_EQUAL(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")" + } + } + + method encodeGreaterThan(GreaterThanExpression expression) returns string{ + if(canAccessNumberValue(expression.left) && canAccessNumberValue(expression.right)){ + return "ASPL_BOOL_LITERAL(" + encodeNumberValueAccess(expression.left) + " > " + encodeNumberValueAccess(expression.right) + ")" + }else{ + return "ASPL_GREATER_THAN(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")" + } + } + + method encodeGreaterThanOrEqual(GreaterThanOrEqualExpression expression) returns string{ + if(canAccessNumberValue(expression.left) && canAccessNumberValue(expression.right)){ + return "ASPL_BOOL_LITERAL(" + encodeNumberValueAccess(expression.left) + " >= " + encodeNumberValueAccess(expression.right) + ")" + }else{ + return "ASPL_GREATER_THAN_OR_EQUAL(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")" + } + } + + method encodeReference(ReferenceExpression expression) returns string{ + if(expression.expression oftype VariableAccessExpression){ + return "ASPL_REFERENCE(\"" + TypeUtils:shortName(expression.expression.getType().getPointer().identifier) + "\", " + TypeUtils:shortName(expression.expression.getType().getPointer().identifier).length + ", " + VariableAccessExpression(expression.expression).variable.identifier + ")" + }elseif(expression.expression oftype NonStaticPropertyAccessExpression){ + if(NonStaticPropertyAccessExpression(expression.expression).p oftype CustomReactiveProperty){ + aspl.parser.utils.type_error("Cannot reference a reactive property", expression.location) + return "" + } + return "ASPL_REFERENCE(\"" + TypeUtils:shortName(expression.expression.getType().getPointer().identifier) + "\", " + TypeUtils:shortName(expression.expression.getType().getPointer().identifier).length + ", ASPL_CLASS_INSTANCE_GET_PROPERTY_ADDRESS(ASPL_ACCESS(" + this.encode(NonStaticPropertyAccessExpression(expression.expression).base) + ").value.classInstance, \"" + NonStaticPropertyAccessExpression(expression.expression).p.name + "\"))" + }elseif(expression.expression oftype StaticPropertyAccessExpression){ + if(StaticPropertyAccessExpression(expression.expression).p oftype CustomReactiveProperty){ + aspl.parser.utils.type_error("Cannot reference a reactive property", expression.location) + return "" + } + return "ASPL_REFERENCE(\"" + TypeUtils:shortName(expression.expression.getType().getPointer().identifier) + "\", " + TypeUtils:shortName(expression.expression.getType().getPointer().identifier).length + ", aspl_static_property_" + TypeUtils:typeToCIdentifier(StaticPropertyAccessExpression(expression.expression).p.type.identifier) + "_" + StaticPropertyAccessExpression(expression.expression).p.name + ")" + }else{ + aspl.parser.utils.type_error("Cannot reference something that is neither a variable nor a property", expression.location) + return "" + } + } + + method encodeDereference(DereferenceExpression expression) returns string{ + return "ASPL_DEREFERENCE(" + this.encode(expression.pointer) + ")" + } + + method encodeIfStatement(IfStatement statement) returns string{ + var conditionStub = "ASPL_IS_TRUE(" + this.encode(statement.condition) + ")" + if(statement.condition oftype CheckEqualsExpression){ + conditionStub = "ASPL_IS_EQUAL(" + this.encode(CheckEqualsExpression(statement.condition).left) + ", " + this.encode(CheckEqualsExpression(statement.condition).right) + ")" + } + var s = new StringBuilder("if(").append(conditionStub).append("){\n") + this.indentLevel++ + foreach(statement.code as node){ + s.append(this.encode(node, true)) + s.append("\n") + } + this.indentLevel-- + repeat(indentLevel){ + s.append("\t") + } + s.append("}") + return s.toString() + } + + method encodeIfElseStatement(IfElseStatement statement) returns string{ + var s = new StringBuilder("if(ASPL_IS_TRUE(").append(this.encode(statement.condition)).append(")){\n") + this.indentLevel++ + foreach(statement.ifCode as node){ + s.append(this.encode(node, true)) + s.append("\n") + } + this.indentLevel-- + repeat(indentLevel){ + s.append("\t") + } + s.append("}else{\n") + this.indentLevel++ + foreach(statement.elseCode as node){ + s.append(this.encode(node, true)) + s.append("\n") + } + this.indentLevel-- + repeat(indentLevel){ + s.append("\t") + } + s.append("}") + return s.toString() + } + + method encodeIfElseIfStatement(IfElseIfStatement statement) returns string{ + var s = new StringBuilder("if(ASPL_IS_TRUE(").append(this.encode(statement.condition)).append(")){\n") + this.indentLevel++ + foreach(statement.ifCode as node){ + s.append(this.encode(node, true)) + s.append("\n") + } + this.indentLevel-- + repeat(indentLevel){ + s.append("\t") + } + s.append("}else ") + s.append(this.encode(statement.elseIf)) + return s.toString() + } + + method encodeWhileStatement(WhileStatement statement) returns string{ + var conditionStub = new StringBuilder("ASPL_IS_TRUE(").append(this.encode(statement.condition)).append(")") + if(statement.condition oftype BooleanLiteral){ + if(BooleanLiteral(statement.condition).value == false){ + conditionStub = new StringBuilder("0") + }else{ + conditionStub = new StringBuilder("1") + } + } + var s = new StringBuilder("while(").append(conditionStub).append("){\n") + this.indentLevel++ + foreach(statement.code as node){ + s.append(this.encode(node, true)) + s.append("\n") + } + this.indentLevel-- + repeat(indentLevel){ + s.append("\t") + } + s.append("}") + return s.toString() + } + + method encodeRepeatStatement(RepeatStatement statement) returns string{ + var variable = "_temp_" + tempVariableId++ + if(statement.variable != null){ + variable = statement.variable?! + } + var s = new StringBuilder("for(ASPL_OBJECT_TYPE* ").append(variable).append(" = C_REFERENCE(ASPL_INT_LITERAL(").append(string(statement.start)).append(")); ").append("ASPL_IS_TRUE(ASPL_LESS_THAN(*").append(variable).append(", ASPL_PLUS(").append(this.encode(statement.iterations)).append(", ASPL_INT_LITERAL(").append(string(statement.start)).append(")))); ").append("*").append(variable).append(" = ASPL_PLUS_PLUS(*").append(variable).append(")){\n") + indentLevel++ + foreach(statement.code as node){ + s.append(this.encode(node, true)) + s.append("\n") + } + this.indentLevel-- + repeat(indentLevel){ + s.append("\t") + } + s.append("}") + return s.toString() + } + + method encodeForeachStatement(ForeachStatement statement) returns string{ + var collectionVariable = "_temp_" + tempVariableId++ + var indexVariable = "_temp_" + tempVariableId++ + var string? mapKeyVariable = null + if(Type:matches(new Types([Type:fromString("map")]), statement.collection.getType(), true)){ + mapKeyVariable = IdentifierEscapeUtils:escapeIdentifier(statement.key?!) + }elseif(statement.key != null){ + indexVariable = IdentifierEscapeUtils:escapeIdentifier(statement.key?!) + } + var s = new StringBuilder("ASPL_OBJECT_TYPE* ").append(collectionVariable).append(" = C_REFERENCE(").append(this.encode(statement.collection)).append(");\n") + repeat(indentLevel){ + s.append("\t") + } + s.append("for(ASPL_OBJECT_TYPE* ").append(indexVariable).append(" = C_REFERENCE(ASPL_INT_LITERAL(0)); ASPL_IS_TRUE(ASPL_LESS_THAN(*").append(indexVariable) + if(Type:matches(new Types([Type:fromString("list")]), statement.collection.getType(), true)){ + s.append(", ASPL_LIST_LENGTH(*") + }elseif(Type:matches(new Types([Type:fromString("map")]), statement.collection.getType(), true)){ + s.append(", ASPL_MAP_LENGTH(*") + }elseif(Type:matches(new Types([Type:fromString("string")]), statement.collection.getType())){ + s.append(", ASPL_STRING_LENGTH(*") + } + s.append(collectionVariable).append("))); ").append("*").append(indexVariable).append(" = ASPL_PLUS_PLUS(*").append(indexVariable).append(")){\n") + indentLevel++ + repeat(indentLevel){ + s.append("\t") + } + if(mapKeyVariable != null){ + s.append("ASPL_OBJECT_TYPE* ").append(mapKeyVariable).append(" = C_REFERENCE(ASPL_MAP_GET_KEY_FROM_INDEX(*").append(collectionVariable).append(", *").append(indexVariable).append("));\n") + repeat(indentLevel){ + s.append("\t") + } + } + if(statement.value != null){ + s.append("ASPL_OBJECT_TYPE* ").append(IdentifierEscapeUtils:escapeIdentifier(statement.value)) + if(Type:matches(new Types([Type:fromString("list")]), statement.collection.getType(), true)){ + s.append(" = C_REFERENCE(ASPL_LIST_GET(*") + }elseif(Type:matches(new Types([Type:fromString("map")]), statement.collection.getType(), true)){ + s.append(" = C_REFERENCE(ASPL_MAP_GET_VALUE_FROM_INDEX(*") + }elseif(Type:matches(new Types([Type:fromString("string")]), statement.collection.getType())){ + s.append(" = C_REFERENCE(ASPL_STRING_INDEX(*") + } + s.append(collectionVariable).append(", *").append(indexVariable).append("));\n") + } + foreach(statement.code as node){ + s.append(this.encode(node, true)) + s.append("\n") + } + this.indentLevel-- + repeat(indentLevel){ + s.append("\t") + } + s.append("}") + return s.toString() + } + + method encodeNonStaticMethodCall(NonStaticMethodCallExpression expression) returns string{ + var strippedTypeIdentifier = expression.m.type.toString() + if(strippedTypeIdentifier.startsWith("list<")){ + strippedTypeIdentifier = "list" + }elseif(strippedTypeIdentifier.startsWith("map<")){ + strippedTypeIdentifier = "map" + } + var s = new StringBuilder() + if(strippedTypeIdentifier.startsWith("callback<") || strippedTypeIdentifier == "callback"){ + if(expression.m.name == "invoke"){ + if(expression.newThread){ + s.append("ASPL_LAUNCH_THREAD(") + s.append("aspl_").append(TypeUtils:typeToCIdentifier(strippedTypeIdentifier)).append("_invoke_thread_wrapper, (ASPL_OBJECT_TYPE*[]){") + s.append("C_REFERENCE(" + this.encode(expression.base) + ")") + if(!this.threadCallbackWrappers.contains(expression.m.type.identifier)){ + this.threadCallbackWrappers.add(expression.m.type.identifier) + } + }else{ + s.append("aspl_" + TypeUtils:typeToCIdentifier(strippedTypeIdentifier) + "_invoke(") + s.append(this.encode(expression.base)) + } + if(expression.arguments.length > 0){ + s.append(", ") + } + } + }elseif(expression.exactClass != null){ + if(expression.newThread){ + s.append("aspl_method_invoke_newthread(") + s.append(this.encode(expression.base)) + s.append(", ") + s.append("aspl_method_" + TypeUtils:typeToCIdentifier(expression.exactClass?!.type.identifier) + "_" + expression.m.name + ", (ASPL_OBJECT_TYPE*[]){") + }else{ + s.append("aspl_method_" + TypeUtils:typeToCIdentifier(expression.exactClass?!.type.identifier) + "_" + expression.m.name + "(C_REFERENCE(") + s.append(this.encode(expression.base)) + s.append(")") + if(expression.arguments.length > 0 || expression.m.parameters.length > 0){ + s.append(", ") + } + } + }else{ + if(expression.newThread){ + s.append("aspl_object_method_invoke_newthread(") + }else{ + s.append("aspl_object_method_invoke(") + } + s.append(this.encode(expression.base)) + s.append(", \"") + s.append(expression.m.name) + s.append("\", (ASPL_OBJECT_TYPE*[]){") + } + var i = 0 + foreach(expression.arguments as argument){ + s.append("C_REFERENCE(" + this.encode(argument) + ")") + if(i < expression.arguments.length - 1){ + s.append(", ") + } + i++ + } + if(expression.arguments.length < expression.m.parameters.length){ + if(expression.arguments.length > 0){ + s.append(", ") + } + while(i < expression.m.parameters.length){ + var parameter = expression.m.parameters[i] + if(parameter.defaultValue != null){ + s.append("C_REFERENCE(" + this.encode(parameter.defaultValue) + ")") + if(i < expression.m.parameters.length - 1){ + s.append(", ") + } + } + i++ + } + } + if(!(expression.exactClass != null || ((strippedTypeIdentifier.startsWith("callback<") || strippedTypeIdentifier == "callback") && expression.m.name == "invoke")) || expression.newThread){ + s.append("}") + } + if(expression.newThread){ + var length = expression.arguments.length + if(expression.arguments.length < expression.m.parameters.length){ + length = expression.m.parameters.length + } + if((strippedTypeIdentifier.startsWith("callback<") || strippedTypeIdentifier == "callback") && expression.m.name == "invoke"){ + length++ // the callback itself + } + s.append(", ").append(string(length)).append(" * sizeof(ASPL_OBJECT_TYPE*)") + } + s.append(")") + return s.toString() + } + + method encodeStaticMethodCall(StaticMethodCallExpression expression) returns string{ + var s = new StringBuilder("aspl_method_").append(TypeUtils:typeToCIdentifier(expression.m.type.identifier)).append("_").append(expression.m.name).append("(") + var i = 0 + foreach(expression.arguments as argument){ + s.append("C_REFERENCE(" + this.encode(argument) + ")") + if(i < expression.arguments.length - 1){ + s.append(", ") + } + i++ + } + if(expression.arguments.length < expression.m.parameters.length){ + if(expression.arguments.length > 0){ + s.append(", ") + } + while(i < expression.m.parameters.length){ + var parameter = expression.m.parameters[i] + if(parameter.defaultValue != null){ + s.append("C_REFERENCE(" + this.encode(parameter.defaultValue) + ")") + if(i < expression.m.parameters.length - 1){ + s.append(", ") + } + } + i++ + } + } + s.append(")") + return s.toString() + } + + method encodeNonStaticPropertyAccess(NonStaticPropertyAccessExpression expression) returns string{ + if(expression.p oftype CustomReactiveProperty){ + var s = new StringBuilder() + s.append("ASPL_REACTIVE_PROPERTY_GET(C_REFERENCE(").append(this.encode(expression.base)).append("), \"").append(expression.p.name).append("\")") + return s.toString() + }else{ + if(expression.base.getType().toType().isPrimitive()){ + if(Type:matches(new Types([Type:fromString("string")]), expression.base.getType())){ + if(expression.p.name == "length"){ + return "ASPL_STRING_LENGTH(" + this.encode(expression.base) + ")" + } + }elseif(Type:matches(new Types([Type:fromString("list")]), expression.base.getType(), true)){ + if(expression.p.name == "length"){ + return "ASPL_LIST_LENGTH(" + this.encode(expression.base) + ")" + } + }elseif(Type:matches(new Types([Type:fromString("map")]), expression.base.getType(), true)){ + if(expression.p.name == "length"){ + return "ASPL_MAP_LENGTH(" + this.encode(expression.base) + ")" + } + } + } + return "ASPL_CLASS_INSTANCE_GET_PROPERTY(ASPL_ACCESS(" + this.encode(expression.base) + ").value.classInstance, \"" + expression.p.name + "\")" + } + } + + method encodeStaticPropertyAccess(StaticPropertyAccessExpression expression) returns string{ + if(expression.p oftype CustomReactiveProperty){ + return "aspl_static_reactive_property_" + TypeUtils:typeToCIdentifier(expression.base.identifier) + "_" + expression.p.name + "_get()" + }else{ + return "*aspl_static_property_" + TypeUtils:typeToCIdentifier(expression.base.identifier) + "_" + expression.p.name + } + } + + method encodeNonStaticPropertyAssignment(NonStaticPropertyAssignExpression expression) returns string{ + if(expression.p oftype CustomReactiveProperty){ + var s = new StringBuilder() + s.append("ASPL_REACTIVE_PROPERTY_SET(C_REFERENCE(").append(this.encode(expression.base)).append("), \"").append(expression.p.name).append("\", C_REFERENCE(").append(this.encode(expression.value)).append("))") + return s.toString() + }else{ + //return "((" + TypeUtils:typeToCIdentifier(expression.p.type.identifier) + "*)((" + this.encode(expression.base) + ").value.classInstance->data))->" + expression.p.name + " = C_REFERENCE(" + this.encode(expression.value) + ")" + + return "ASPL_CLASS_INSTANCE_SET_PROPERTY(ASPL_ACCESS(" + this.encode(expression.base) + ").value.classInstance, \"" + expression.p.name + "\", " + this.encode(expression.value) + ")" + } + } + + method encodeStaticPropertyAssignment(StaticPropertyAssignExpression expression) returns string{ + if(expression.p oftype CustomReactiveProperty){ + return "aspl_static_reactive_property_" + TypeUtils:typeToCIdentifier(expression.base.identifier) + "_" + expression.p.name + "_set(C_REFERENCE(" + this.encode(expression.value) + "))" + }else{ + return "*aspl_static_property_" + TypeUtils:typeToCIdentifier(expression.base.identifier) + "_" + expression.p.name + " = " + this.encode(expression.value) + } + } + + method encodeListIndex(ListIndexExpression expression) returns string{ + return "ASPL_LIST_GET(" + this.encode(expression.base) + ", " + this.encode(expression.index) + ")" + } + + method encodeListAssignment(ListAssignExpression expression) returns string{ + return "ASPL_LIST_SET(" + this.encode(expression.base) + ", " + this.encode(expression.index) + ", " + this.encode(expression.value) + ")" + } + + method encodeMapAccess(MapAccessExpression expression) returns string{ + return "ASPL_MAP_GET(" + this.encode(expression.base) + ", " + this.encode(expression.key) + ")" + } + + method encodeMapAssignment(MapAssignExpression expression) returns string{ + return "ASPL_MAP_SET(" + this.encode(expression.base) + ", " + this.encode(expression.key) + ", " + this.encode(expression.value) + ")" + } + + method encodeStringIndex(StringIndexExpression expression) returns string{ + return "ASPL_STRING_INDEX(" + this.encode(expression.base) + ", " + this.encode(expression.index) + ")" + } + + method encodeClassDeclaration(ClassDeclareStatement statement) returns string{ + return "// Declare class " + statement.c.type.identifier + } + + method encodeClassInstantiation(ClassInstantiateExpression expression) returns string{ + var s = new StringBuilder("aspl_new_") + s.append(TypeUtils:typeToCIdentifier(expression.c.type.identifier)).append("(") + var i = 0 + foreach(expression.arguments as argument){ + s.append("C_REFERENCE(").append(this.encode(argument)).append(")") + if(i < expression.arguments.length - 1){ + s.append(", ") + } + i++ + } + if(Method:exists(expression.c.type, "construct")){ + var m = Method:get(expression.c.type, "construct") + if(expression.arguments.length < m.parameters.length){ + if(expression.arguments.length > 0){ + s.append(", ") + } + while(i < m.parameters.length){ + var parameter = m.parameters[i] + if(parameter.defaultValue != null){ + s.append("C_REFERENCE(" + this.encode(parameter.defaultValue) + ")") + if(i < m.parameters.length - 1){ + s.append(", ") + } + } + i++ + } + } + } + s.append(")") + return s.toString() + } + + method encodeMethodDeclaration(MethodDeclareStatement statement) returns string{ + if(!statement.m.isAbstract){ + methodDeclarations.add(statement) + } + if(statement.m.name == "construct"){ + constructors[statement.m.type.identifier] = statement.m + } + return "// Declare method " + statement.m.name + } + + method encodeMethodDeclarationHeader(MethodDeclareStatement statement) returns string{ + var s = new StringBuilder("") + if(statement.m.returnTypes.types.length > 0 || statement.m.canThrow){ + s.append("ASPL_OBJECT_TYPE ") + }else{ + s.append("void ") + } + s.append("aspl_method_").append(TypeUtils:typeToCIdentifier(statement.m.type.identifier)).append("_").append(statement.m.name).append("(") + if(!statement.m.isStatic){ + s.append("ASPL_OBJECT_TYPE* this") + if(statement.m.parameters.length > 0){ + s.append(", ") + } + } + var i = 0 + foreach(statement.m.parameters as parameter){ + s.append("ASPL_OBJECT_TYPE* ").append(parameter.name) + if(i < statement.m.parameters.length - 1){ + s.append(", ") + } + i++ + } + s.append(");") + return s.toString() + } + + method encodeMethodDeclarationOutsideClass(MethodDeclareStatement statement) returns string{ + var s = new StringBuilder("") + if(statement.m.returnTypes.types.length > 0 || statement.m.canThrow){ + s.append("ASPL_OBJECT_TYPE ") + }else{ + s.append("void ") + } + s.append("aspl_method_").append(TypeUtils:typeToCIdentifier(statement.m.type.identifier)).append("_").append(statement.m.name).append("(") + if(!CustomMethod(statement.m).isStatic){ + s.append("ASPL_OBJECT_TYPE* this") + if(statement.m.parameters.length > 0){ + s.append(", ") + } + } + var i = 0 + foreach(statement.m.parameters as parameter){ + s.append("ASPL_OBJECT_TYPE* ").append(parameter.name) + if(i < statement.m.parameters.length - 1){ + s.append(", ") + } + i++ + } + s.append("){\n") + indentLevel++ + if(Options:enableErrorHandling){ + repeat(indentLevel){ + s.append("\t") + } + s.append("try{\n") + indentLevel++ + } + foreach(CustomMethod(statement.m).code as node){ + s.append(this.encode(node, true)) + s.append("\n") + } + if(Options:enableErrorHandling){ + repeat(indentLevel){ + s.append("\t") + } + s.append("return_uninitialized_from_try;\n") + } + indentLevel-- + if(Options:enableErrorHandling){ + repeat(indentLevel){ + s.append("\t") + } + s.append("}catch{\n") + this.indentLevel++ + repeat(indentLevel){ + s.append("\t") + } + s.append("return aspl_current_error;\n") + this.indentLevel-- + repeat(indentLevel){ + s.append("\t") + } + s.append("}\n") + this.indentLevel-- + } + repeat(indentLevel){ + s.append("\t") + } + s.append("}") + if(!CustomMethod(statement.m).isStatic){ + s.append("\n\n") + s.append("ASPL_OBJECT_TYPE aspl_method_").append(TypeUtils:typeToCIdentifier(statement.m.type.identifier)).append("_").append(statement.m.name).append("_wrapper(") + s.append("ASPL_OBJECT_TYPE* this, ") + s.append("ASPL_OBJECT_TYPE* arguments[]){\n") + indentLevel++ + repeat(indentLevel){ + s.append("\t") + } + if(statement.m.returnTypes.types.length > 0){ + s.append("return ") + } + s.append("aspl_method_").append(TypeUtils:typeToCIdentifier(statement.m.type.identifier)).append("_").append(statement.m.name).append("(") + if(!CustomMethod(statement.m).isStatic){ + s.append("this") + if(statement.m.parameters.length > 0){ + s.append(", ") + } + } + i = 0 + foreach(statement.m.parameters as parameter){ + s.append("arguments[").append(string(i)).append("]") + if(i < statement.m.parameters.length - 1){ + s.append(", ") + } + i++ + } + s.append(");\n") + if(statement.m.returnTypes.types.length == 0){ + repeat(indentLevel){ + s.append("\t") + } + s.append("return ASPL_UNINITIALIZED;\n") + } + indentLevel-- + s.append("}") + } + return s.toString() + } + + method encodePropertyDeclaration(PropertyDeclareStatement statement) returns string{ + if(statement.p oftype CustomNormalProperty){ + if(statement.p.isStatic){ + this.staticNormalPropertyDeclarations.add(statement) + return "// Declare static property " + statement.p.name + }else{ + return "ASPL_OBJECT_TYPE* " + statement.p.name + } + }else{ + if(statement.p.isStatic){ + this.staticReactivePropertyDeclarations.add(statement) + return "// Declare static reactive property " + statement.p.name + }else{ + this.reactivePropertyDeclarations.add(statement) + return "// Declare reactive property " + statement.p.name + } + } + } + + method encodeStaticNormalPropertyDeclaration(PropertyDeclareStatement statement) returns string{ + var s = "ASPL_OBJECT_TYPE* aspl_static_property_" + TypeUtils:typeToCIdentifier(statement.p.type.identifier) + "_" + statement.p.name + ";" + if(statement.p.isThreadLocal){ + // TODO: Thread local properties are not supported by GC boehm, we need to find a workaround + //return "_Thread_local " + s + return s + }else{ + return s + } + } + + method encodeReactivePropertyDeclarationHeader(PropertyDeclareStatement statement) returns string{ + var s = new StringBuilder("") + if(CustomReactiveProperty(statement.p).getCode != null){ + s.append("ASPL_OBJECT_TYPE aspl_reactive_property_").append(TypeUtils:typeToCIdentifier(statement.p.type.identifier)).append("_").append(statement.p.name).append("_get(ASPL_OBJECT_TYPE* this);") + s.append("\n\n") + } + if(CustomReactiveProperty(statement.p).setCode != null){ + s.append("ASPL_OBJECT_TYPE aspl_reactive_property_").append(TypeUtils:typeToCIdentifier(statement.p.type.identifier)).append("_").append(statement.p.name).append("_set(ASPL_OBJECT_TYPE* this, ASPL_OBJECT_TYPE* value);") + } + return s.toString() + } + + method encodeReactivePropertyDeclaration(PropertyDeclareStatement statement) returns string{ + var s = new StringBuilder() + if(CustomReactiveProperty(statement.p).getCode != null){ + s.append("ASPL_OBJECT_TYPE aspl_reactive_property_").append(TypeUtils:typeToCIdentifier(statement.p.type.identifier)).append("_").append(statement.p.name).append("_get(ASPL_OBJECT_TYPE* this){\n") + indentLevel++ + if(Options:enableErrorHandling){ + repeat(indentLevel){ + s.append("\t") + } + s.append("try{\n") + indentLevel++ + } + foreach(CustomReactiveProperty(statement.p).getCode as node){ + s.append(this.encode(node, true)) + s.append("\n") + } + indentLevel-- + if(Options:enableErrorHandling){ + repeat(indentLevel){ + s.append("\t") + } + s.append("}catch{\n") + this.indentLevel++ + repeat(indentLevel){ + s.append("\t") + } + s.append("return aspl_current_error;\n") + this.indentLevel-- + repeat(indentLevel){ + s.append("\t") + } + s.append("}\n") + this.indentLevel-- + } + repeat(indentLevel){ + s.append("\t") + } + s.append("}\n\n") + } + if(CustomReactiveProperty(statement.p).setCode != null){ + s.append("ASPL_OBJECT_TYPE aspl_reactive_property_").append(TypeUtils:typeToCIdentifier(statement.p.type.identifier)).append("_").append(statement.p.name).append("_set(ASPL_OBJECT_TYPE* this, ASPL_OBJECT_TYPE* value){\n") + indentLevel++ + if(Options:enableErrorHandling){ + repeat(indentLevel){ + s.append("\t") + } + s.append("try{\n") + indentLevel++ + } + foreach(CustomReactiveProperty(statement.p).setCode as node){ + s.append(this.encode(node, true)) + s.append("\n") + } + repeat(indentLevel){ + s.append("\t") + } + if(Options:enableErrorHandling){ + s.append("return_from_try(*value);\n") + }else{ + s.append("return *value;\n") + } + indentLevel-- + if(Options:enableErrorHandling){ + repeat(indentLevel){ + s.append("\t") + } + s.append("}catch{\n") + this.indentLevel++ + repeat(indentLevel){ + s.append("\t") + } + s.append("return aspl_current_error;\n") + this.indentLevel-- + repeat(indentLevel){ + s.append("\t") + } + s.append("}\n") + this.indentLevel-- + } + repeat(indentLevel){ + s.append("\t") + } + s.append("}") + } + return s.toString() + } + + method encodeStaticReactivePropertyDeclarationHeader(PropertyDeclareStatement statement) returns string{ + var s = new StringBuilder("") + s.append("ASPL_OBJECT_TYPE aspl_static_reactive_property_").append(TypeUtils:typeToCIdentifier(statement.p.type.identifier)).append("_").append(statement.p.name).append("_get();") + s.append("\n\n") + s.append("ASPL_OBJECT_TYPE aspl_static_reactive_property_").append(TypeUtils:typeToCIdentifier(statement.p.type.identifier)).append("_").append(statement.p.name).append("_set(ASPL_OBJECT_TYPE* value);") + return s.toString() + } + + method encodeStaticReactivePropertyDeclaration(PropertyDeclareStatement statement) returns string{ + var s = new StringBuilder() + if(CustomReactiveProperty(statement.p).getCode != null){ + s.append("ASPL_OBJECT_TYPE aspl_static_reactive_property_").append(TypeUtils:typeToCIdentifier(statement.p.type.identifier)).append("_").append(statement.p.name).append("_get(){\n") + indentLevel++ + if(Options:enableErrorHandling){ + repeat(indentLevel){ + s.append("\t") + } + s.append("try{\n") + indentLevel++ + } + foreach(CustomReactiveProperty(statement.p).getCode as node){ + s.append(this.encode(node, true)) + s.append("\n") + } + indentLevel-- + if(Options:enableErrorHandling){ + repeat(indentLevel){ + s.append("\t") + } + s.append("}catch{\n") + this.indentLevel++ + repeat(indentLevel){ + s.append("\t") + } + s.append("return aspl_current_error;\n") + this.indentLevel-- + repeat(indentLevel){ + s.append("\t") + } + s.append("}\n") + this.indentLevel-- + } + repeat(indentLevel){ + s.append("\t") + } + s.append("}\n\n") + } + if(CustomReactiveProperty(statement.p).setCode != null){ + s.append("ASPL_OBJECT_TYPE aspl_static_reactive_property_").append(TypeUtils:typeToCIdentifier(statement.p.type.identifier)).append("_").append(statement.p.name).append("_set(ASPL_OBJECT_TYPE* value){\n") + indentLevel++ + if(Options:enableErrorHandling){ + repeat(indentLevel){ + s.append("\t") + } + s.append("try{\n") + indentLevel++ + } + foreach(CustomReactiveProperty(statement.p).setCode as node){ + s.append(this.encode(node, true)) + s.append("\n") + } + repeat(indentLevel){ + s.append("\t") + } + if(Options:enableErrorHandling){ + s.append("return_from_try(*value);\n") + }else{ + s.append("return *value;\n") + } + indentLevel-- + if(Options:enableErrorHandling){ + repeat(indentLevel){ + s.append("\t") + } + s.append("}catch{\n") + this.indentLevel++ + repeat(indentLevel){ + s.append("\t") + } + s.append("return aspl_current_error;\n") + this.indentLevel-- + repeat(indentLevel){ + s.append("\t") + } + s.append("}\n") + this.indentLevel-- + } + repeat(indentLevel){ + s.append("\t") + } + s.append("}") + } + return s.toString() + } + + method encodeThis(ThisExpression expression) returns string{ + return "*this" + } + + method encodeEnumDeclaration(EnumDeclareStatement statement) returns string{ + this.enumDeclarations.add(statement) + return "// Declare enum " + statement.e.type.identifier + } + + method encodeEnumFieldAccess(EnumFieldAccessExpression expression) returns string{ + return "ASPL_ENUM_FIELD_LITERAL(aspl_enum_" + TypeUtils:typeToCIdentifier(expression.field.e.type.identifier) + ", " + expression.field.e.fields?![expression.field.name].value + ")" + } + + method encodeCallbackDeclarationHeader(CallbackDeclaration declaration) returns string{ + var s = new StringBuilder("") + if(declaration.literal.value.returnTypes.types.length > 0){ + s.append("ASPL_OBJECT_TYPE ") + }else{ + s.append("void ") + } + s.append("aspl_callback_").append(string(declaration.id)).append("(") + var i = 0 + foreach(declaration.literal.value.parameters as parameter){ + s.append(this.encodeParameter(parameter)) + if(i < declaration.literal.value.parameters.length - 1){ + s.append(", ") + } + i++ + } + if(declaration.literal.value.parameters.length > 0){ + s.append(", ") + } + s.append("ASPL_ClosureMap* closure_map") + s.append(");") + + if(callbackInvokeFunctionHandledTypes.contains(declaration.literal.value.type.identifier)){ + return s.toString() + } + callbackInvokeFunctionHandledTypes.add(declaration.literal.value.type.identifier) + + s.append("\n\n") + + var genericTypes = Type:getGenericTypesIdentifiers(declaration.literal.value.type.identifier) + var hasReturnType = false + foreach(genericTypes as genericType){ + if(genericType.startsWith("returns ")){ + hasReturnType = true + break + } + } + if(hasReturnType){ + s.append("ASPL_OBJECT_TYPE ") + }else{ + s.append("void ") + } + s.append("aspl_" + TypeUtils:typeToCIdentifier(declaration.literal.value.type.identifier) + "_invoke(") + s.append("ASPL_OBJECT_TYPE closure") + if(declaration.literal.value.parameters.length > 0){ + s.append(", ") + } + i = 0 + foreach(declaration.literal.value.parameters as parameter){ + s.append(this.encodeParameter(parameter)) + if(i < declaration.literal.value.parameters.length - 1){ + s.append(", ") + } + i++ + } + s.append(");") + return s.toString() + } + + method encodeCallbackDeclaration(CallbackDeclaration declaration) returns string{ + var s = new StringBuilder("") + if(declaration.literal.value.returnTypes.types.length > 0){ + s.append("ASPL_OBJECT_TYPE ") + }else{ + s.append("void ") + } + s.append("aspl_callback_").append(string(declaration.id)).append("(") + var i = 0 + foreach(declaration.literal.value.parameters as parameter){ + s.append(this.encodeParameter(parameter)) + if(i < declaration.literal.value.parameters.length - 1){ + s.append(", ") + } + i++ + } + if(declaration.literal.value.parameters.length > 0){ + s.append(", ") + } + s.append("ASPL_ClosureMap* closure_map") + s.append("){\n") + this.indentLevel++ + foreach(declaration.literal.capturedVariables as variable){ + repeat(indentLevel){ + s.append("\t") + } + s.append("ASPL_OBJECT_TYPE* ").append(variable).append(" = ASPL_CLOSURE_MAP_GET(closure_map, \"").append(variable).append("\");\n") + } + if(Options:enableErrorHandling){ + repeat(indentLevel){ + s.append("\t") + } + s.append("try{\n") + indentLevel++ + } + foreach(declaration.literal.value.code as node){ + s.append(this.encode(node, true)) + s.append("\n") + } + if(Options:enableErrorHandling){ + repeat(indentLevel){ + s.append("\t") + } + s.append("return_uninitialized_from_try;\n") + } + this.indentLevel-- + if(Options:enableErrorHandling){ + repeat(indentLevel){ + s.append("\t") + } + s.append("}catch{\n") + this.indentLevel++ + repeat(indentLevel){ + s.append("\t") + } + s.append("return aspl_current_error;\n") + this.indentLevel-- + repeat(indentLevel){ + s.append("\t") + } + s.append("}\n") + this.indentLevel-- + } + repeat(indentLevel){ + s.append("\t") + } + s.append("}") + + if(callbackInvokeFunctionHandledTypes.contains(declaration.literal.value.type.identifier)){ + return s.toString() + } + callbackInvokeFunctionHandledTypes.add(declaration.literal.value.type.identifier) + + s.append("\n\n") + + { + var genericTypes = Type:getGenericTypesIdentifiers(declaration.literal.value.type.identifier) + var hasReturnType = false + foreach(genericTypes as genericType){ + if(genericType.startsWith("returns ")){ + hasReturnType = true + break + } + } + if(hasReturnType){ + s.append("ASPL_OBJECT_TYPE ") + }else{ + s.append("void ") + } + } + s.append("aspl_" + TypeUtils:typeToCIdentifier(declaration.literal.value.type.identifier) + "_invoke(") + s.append("ASPL_OBJECT_TYPE closure") + if(declaration.literal.value.parameters.length > 0){ + s.append(", ") + } + i = 0 + foreach(declaration.literal.value.parameters as parameter){ + s.append(this.encodeParameter(parameter)) + if(i < declaration.literal.value.parameters.length - 1){ + s.append(", ") + } + i++ + } + s.append("){\n") + this.indentLevel++ + repeat(indentLevel){ + s.append("\t") + } + s.append("ASPL_ClosureMap* closure_map = ASPL_ACCESS(closure).value.callback->closure_map;\n") + repeat(indentLevel){ + s.append("\t") + } + var castStr = "(%returntype% (*)(" + var genericTypes = Type:getGenericTypesIdentifiers(declaration.literal.value.type.identifier) + var hasReturnType = false + foreach(genericTypes as genericType){ + if(genericType.startsWith("returns ")){ + hasReturnType = true + }else{ + castStr += "ASPL_OBJECT_TYPE*, " + } + } + castStr += "ASPL_ClosureMap*" + if(hasReturnType){ + castStr = castStr.replace("%returntype%", "ASPL_OBJECT_TYPE") + }else{ + castStr = castStr.replace("%returntype%", "void") + } + castStr += "))" + if(hasReturnType){ + s.append("return ") + } + s.append("(" + castStr + "ASPL_ACCESS(closure).value.callback->function)(") + i = 0 + foreach(declaration.literal.value.parameters as parameter){ + s.append(parameter.name) + if(i < declaration.literal.value.parameters.length - 1){ + s.append(", ") + } + i++ + } + if(declaration.literal.value.parameters.length > 0){ + s.append(", ") + } + s.append("closure_map);\n") + this.indentLevel-- + repeat(indentLevel){ + s.append("\t") + } + s.append("}") + return s.toString() + } + + method encodeCallbackLiteral(CallbackLiteral literal) returns string{ + var declaration = new CallbackDeclaration(literal) + this.callbackDeclarations.add(declaration) + var s = new StringBuilder("ASPL_CALLBACK_LITERAL(\"").append(literal.getType().toString()).append("\", ").append(string(literal.getType().toString().length)).append(", aspl_callback_").append(string(declaration.id)) + s.append(", ASPL_NEW_CLOSURE_MAP(") + s.append(string(literal.capturedVariables.length)) + if(literal.capturedVariables.length > 0){ + s.append(", ") + } + var i = 0 + foreach(literal.capturedVariables as variable){ + s.append("\"").append(variable).append("\", ").append(variable) + if(i < literal.capturedVariables.length - 1){ + s.append(", ") + } + i++ + } + s.append("))") + return s.toString() + } + + method encodeImplementationCall(ImplementationCallExpression expression) returns string{ + var s = new StringBuilder("ASPL_IMPLEMENT_").append(expression.call.replace(".", "$")).append("(") + var i = 0 + foreach(expression.arguments as argument){ + s.append("C_REFERENCE(" + this.encode(argument) + ")") + if(i < expression.arguments.length - 1){ + s.append(", ") + } + i++ + } + s.append(")") + return s.toString() + } + + method encodeOfType(OfTypeExpression expression) returns string{ + return "ASPL_OFTYPE(" + this.encode(expression.expression) + ", \"" + TypeUtils:shortName(expression.type.toString()) + "\")" + } + + method encodeBreakStatement(BreakStatement statement) returns string{ + return "break" // TODO: Add support for multiple levels + } + + method encodeContinueStatement(ContinueStatement statement) returns string{ + return "continue" // TODO: Add support for multiple levels + } + + method encodeCast(CastExpression expression) returns string{ + return "ASPL_CAST(" + this.encode(expression.value) + ", \"" + TypeUtils:shortName(expression.type.toString()) + "\")" + } + + method encodeThrowStatement(ThrowStatement statement) returns string{ + return "throw(" + this.encode(statement.errorInstance) + ")" + } + + method encodeCatchExpression(CatchExpression expression) returns string{ + var declaration = new CatchBlockDeclaration(expression) + this.catchBlockDeclarations.add(declaration) + var s = new StringBuilder("") + s.append("aspl_catch_block_" + declaration.id + "(C_REFERENCE(") + s.append(this.encode(expression.expression)) + s.append("), ASPL_NEW_CLOSURE_MAP(") + s.append(string(expression.capturedVariables.length)) + if(expression.capturedVariables.length > 0){ + s.append(", ") + } + var i = 0 + foreach(expression.capturedVariables as variable){ + s.append("\"").append(variable).append("\", ").append(variable) + if(i < expression.capturedVariables.length - 1){ + s.append(", ") + } + i++ + } + s.append("))") + return s.toString() + } + + method encodeCatchBlockDeclaration(CatchBlockDeclaration declaration) returns string{ + var s = new StringBuilder("") + if(declaration.block.standalone){ + s.append("void ") + }else{ + s.append("ASPL_OBJECT_TYPE ") + } + s.append("aspl_catch_block_").append(string(declaration.id)).append("(") + s.append("ASPL_OBJECT_TYPE* error, ") + s.append("ASPL_ClosureMap* closure_map") + s.append("){\n") + this.indentLevel++ + repeat(indentLevel){ + s.append("\t") + } + s.append("if(!aspl_object_is_error(*error)) return *error;\n") + foreach(declaration.block.capturedVariables as variable){ + repeat(indentLevel){ + s.append("\t") + } + s.append("ASPL_OBJECT_TYPE* ").append(variable).append(" = ASPL_CLOSURE_MAP_GET(closure_map, \"").append(variable).append("\");\n") + } + foreach(declaration.block.code as node){ + s.append(this.encode(node, true)) + s.append("\n") + } + this.indentLevel-- + repeat(indentLevel){ + s.append("\t") + } + s.append("}") + s.append("\n\n") + return s.toString() + } + + method encodeErrorPropagation(PropagateErrorExpression expression) returns string{ + return "ASPL_UNWRAP_RESULT(" + this.encode(expression.expression) + ")" + } + + method encodeFileEmbedding(EmbedFileExpression expression) returns string{ + var embedId = fileEmbeds.length + fileEmbeds.add(expression) + return "aspl_embed_" + embedId + } + + method encodeDiscard(Expression expression) returns string{ + return this.encode(expression) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/backend/stringcode/c/CallbackDeclaration.aspl b/stdlib/aspl/compiler/backend/stringcode/c/CallbackDeclaration.aspl new file mode 100644 index 0000000..fea9252 --- /dev/null +++ b/stdlib/aspl/compiler/backend/stringcode/c/CallbackDeclaration.aspl @@ -0,0 +1,19 @@ +import aspl.parser.ast.literals + +class CallbackDeclaration { + + [public] + [static] + property int _id + + [readpublic] + property CallbackLiteral literal + [readpublic] + property int id + + method construct(CallbackLiteral literal){ + this.literal = literal + this.id = self:_id++ + } + +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/backend/stringcode/c/CatchBlockDeclaration.aspl b/stdlib/aspl/compiler/backend/stringcode/c/CatchBlockDeclaration.aspl new file mode 100644 index 0000000..8f0d4ee --- /dev/null +++ b/stdlib/aspl/compiler/backend/stringcode/c/CatchBlockDeclaration.aspl @@ -0,0 +1,20 @@ +import aspl.parser.ast.literals +import aspl.parser.ast.expressions + +class CatchBlockDeclaration { + + [public] + [static] + property int _id + + [readpublic] + property CatchExpression block + [readpublic] + property int id + + method construct(CatchExpression block){ + this.block = block + this.id = self:_id++ + } + +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/backend/stringcode/c/IdentifierEscapeUtils.aspl b/stdlib/aspl/compiler/backend/stringcode/c/IdentifierEscapeUtils.aspl new file mode 100644 index 0000000..2d3a898 --- /dev/null +++ b/stdlib/aspl/compiler/backend/stringcode/c/IdentifierEscapeUtils.aspl @@ -0,0 +1,49 @@ +[static] +class IdentifierEscapeUtils { + + [static] + property list reservedIdentifiers = [ + "auto", + "break", + "case", + "char", + "const", + "continue", + "default", + "do", + "double", + "else", + "enum", + "extern", + "float", + "for", + "goto", + "if", + "int", + "long", + "register", + "return", + "short", + "signed", + "sizeof", + "static", + "struct", + "switch", + "typedef", + "union", + "unsigned", + "void", + "volatile", + "while" + ] + + [public] + [static] + method escapeIdentifier(string identifier) returns string{ + if(reservedIdentifiers.contains(identifier)){ + return "_escaped_" + identifier + } + return identifier + } + +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/backend/stringcode/c/TypeUtils.aspl b/stdlib/aspl/compiler/backend/stringcode/c/TypeUtils.aspl new file mode 100644 index 0000000..4007310 --- /dev/null +++ b/stdlib/aspl/compiler/backend/stringcode/c/TypeUtils.aspl @@ -0,0 +1,23 @@ +[public] +[static] +class TypeUtils { + + [public] + [static] + method shortName(string s) returns string{ + if(s == "integer"){ + return "int" + }elseif(s == "boolean"){ + return "bool" + }else{ + return s + } + } + + [public] + [static] + method typeToCIdentifier(string type) returns string{ + return type.replace(".", "$").replace("|", "_OR_").replace("<", "_").replace(">", "_").replace(", ", "_").replace(" ", "_") + } + +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/backend/stringcode/c/template.c b/stdlib/aspl/compiler/backend/stringcode/c/template.c new file mode 100644 index 0000000..24bfd4f --- /dev/null +++ b/stdlib/aspl/compiler/backend/stringcode/c/template.c @@ -0,0 +1,4370 @@ +// TODO: Clean up & refactor this whole file sometime in the (near) future + +#ifdef __linux__ +#define _GNU_SOURCE // TODO: I think this shouldn't be necessary +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +#include +#include +#include +#endif + +#ifndef ASPL_NO_MULTITHREADING +#define THREAD_IMPLEMENTATION +#define thread_id_t libthread_id_t +#ifdef __APPLE__ +#define thread_create libthread_create // thread_create is already used by macOS +#endif +#include "thirdparty/thread.h/thread.h" +#ifdef __APPLE__ +#undef thread_create +#endif +#undef thread_id_t +#undef thread_h +#define GC_THREADS +#endif + +#ifdef _WIN32 +#define GC_NOT_DLL = 1 +#else +#define GC_NO_DLOPEN +#endif +#define GC_BUILTIN_ATOMIC = 1 + +#ifdef ASPL_DEBUG +#define GC_DEBUG +#endif + +#include "thirdparty/libgc/gc.c" +#undef PROC + +#ifdef __APPLE__ +#define thread_create libthread_create +#endif + +#define ASPL_MALLOC GC_MALLOC +#define ASPL_MALLOC_ATOMIC GC_MALLOC_ATOMIC +#define ASPL_REALLOC GC_REALLOC +#define ASPL_FREE GC_FREE + +#ifdef ASPL_HEAP_BASED +#define ASPL_OBJECT_TYPE ASPL_Object_internal* +#define ASPL_ALLOC_OBJECT() ASPL_MALLOC(sizeof(ASPL_Object_internal)) +#define ASPL_ACCESS(obj) (*obj) +#define ASPL_UNINITIALIZED NULL +#define ASPL_IS_UNINITIALIZED(obj) (obj == NULL) +#define ASPL_HASHMAP_WRAP(obj) (obj) +#define ASPL_HASHMAP_UNWRAP(obj) (obj) +#else +#define ASPL_OBJECT_TYPE ASPL_Object_internal +#define ASPL_ALLOC_OBJECT() (ASPL_OBJECT_TYPE){} +#define ASPL_ACCESS(obj) (obj) +#define ASPL_UNINITIALIZED (ASPL_OBJECT_TYPE){.kind = ASPL_OBJECT_KIND_UNINITIALIZED} +#define ASPL_IS_UNINITIALIZED(obj) (obj.kind == ASPL_OBJECT_KIND_UNINITIALIZED) +#define ASPL_HASHMAP_WRAP(obj) C_REFERENCE(obj) +#define ASPL_HASHMAP_UNWRAP(obj) (*obj) +#endif + +#ifdef __ANDROID__ +#include +#define aspl_util_print(...) __android_log_print(ANDROID_LOG_INFO, ASPL_APP_NAME_STRING, __VA_ARGS__) +#define aspl_util_vprint(...) __android_log_print(ANDROID_LOG_INFO, ASPL_APP_NAME_STRING, __VA_ARGS__) +#else +#define aspl_util_print(...) printf(__VA_ARGS__); +#define aspl_util_vprint(...) vprintf(__VA_ARGS__); +#endif + +typedef enum ASPL_ObjectKind +{ + ASPL_OBJECT_KIND_UNINITIALIZED, + ASPL_OBJECT_KIND_NULL, + ASPL_OBJECT_KIND_BOOLEAN, + ASPL_OBJECT_KIND_BYTE, + ASPL_OBJECT_KIND_INTEGER, + ASPL_OBJECT_KIND_LONG, + ASPL_OBJECT_KIND_FLOAT, + ASPL_OBJECT_KIND_DOUBLE, + ASPL_OBJECT_KIND_STRING, + ASPL_OBJECT_KIND_LIST, + ASPL_OBJECT_KIND_MAP, + ASPL_OBJECT_KIND_CALLBACK, + ASPL_OBJECT_KIND_POINTER, + ASPL_OBJECT_KIND_CLASS_INSTANCE, + ASPL_OBJECT_KIND_ENUM_FIELD, + ASPL_OBJECT_KIND_HANDLE +} ASPL_ObjectKind; + +typedef struct ASPL_Object_internal +{ + ASPL_ObjectKind kind; + union + { + char boolean; + unsigned char integer8; + long integer32; + long long integer64; + float float32; + double float64; + struct ASPL_String* string; + struct ASPL_List* list; + struct ASPL_Map* map; + struct ASPL_Pointer* pointer; + struct ASPL_Callback* callback; + struct ASPL_ClassInstance* classInstance; + struct ASPL_EnumField* enumField; + void* handle; + } value; +} ASPL_Object_internal; + +typedef struct ASPL_String +{ + char* str; + size_t length; +} ASPL_String; + +typedef struct ASPL_List +{ + char* typePtr; + int typeLen; + ASPL_OBJECT_TYPE* value; + size_t length; +} ASPL_List; + +ASPL_OBJECT_TYPE* C_REFERENCE(ASPL_OBJECT_TYPE x) +{ + ASPL_OBJECT_TYPE* ptr = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE)); + *ptr = x; + return ptr; +} + +#define HASHMAP_PREFIX hashmap_aspl_object_to_aspl_object +#define HASHMAP_KEY_TYPE ASPL_Object_internal * +#define HASHMAP_KEY_NULL_VALUE NULL +#define HASHMAP_VALUE_TYPE ASPL_Object_internal * +#define HASHMAP_VALUE_NULL_VALUE NULL + +unsigned int aspl_hash(ASPL_OBJECT_TYPE obj); + +unsigned int hashmap_aspl_object_to_aspl_object_hash_key(HASHMAP_KEY_TYPE key) +{ + return aspl_hash(ASPL_HASHMAP_UNWRAP(key)); +} + +int ASPL_IS_EQUAL(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b); + +int hashmap_aspl_object_to_aspl_object_equals_key(HASHMAP_KEY_TYPE a, HASHMAP_KEY_TYPE b) +{ + return ASPL_IS_EQUAL(ASPL_HASHMAP_UNWRAP(a), ASPL_HASHMAP_UNWRAP(b)); +} + +unsigned int hashmap_aspl_object_to_aspl_object_hash_value(HASHMAP_VALUE_TYPE value) +{ + return aspl_hash(ASPL_HASHMAP_UNWRAP(value)); +} + +int hashmap_aspl_object_to_aspl_object_equals_value(HASHMAP_VALUE_TYPE a, HASHMAP_VALUE_TYPE b) +{ + return ASPL_IS_EQUAL(ASPL_HASHMAP_UNWRAP(a), ASPL_HASHMAP_UNWRAP(b)); +} + +#define HASHMAP_MALLOC ASPL_MALLOC +#define HASHMAP_REALLOC ASPL_REALLOC +#define HASHMAP_FREE ASPL_FREE +#include "thirdparty/hashmap/hashmap.h" + +typedef struct ASPL_Map +{ + char* typePtr; + int typeLen; + hashmap_aspl_object_to_aspl_object_HashMap* hashmap; +} ASPL_Map; + +typedef struct ASPL_Pointer +{ + char* typePtr; + int typeLen; + ASPL_OBJECT_TYPE* object; +} ASPL_Pointer; + +typedef struct ASPL_ClosureMapPair +{ + char* key; + ASPL_OBJECT_TYPE* value; +} ASPL_ClosureMapPair; + +typedef struct ASPL_ClosureMap +{ + ASPL_ClosureMapPair* pairs; + int length; +} ASPL_ClosureMap; + +typedef struct ASPL_Callback +{ + char* typePtr; + int typeLen; + void* function; + ASPL_ClosureMap* closure_map; +} ASPL_Callback; + +#undef HASHMAP_PREFIX +#undef HASHMAP_KEY_TYPE +#undef HASHMAP_KEY_NULL_VALUE +#undef HASHMAP_VALUE_TYPE +#undef HASHMAP_VALUE_NULL_VALUE +#define HASHMAP_PREFIX hashmap_str_to_voidptr +#define HASHMAP_KEY_TYPE char * +#define HASHMAP_KEY_NULL_VALUE NULL +#define HASHMAP_VALUE_TYPE void * +#define HASHMAP_VALUE_NULL_VALUE NULL + +unsigned int hashmap_str_to_voidptr_hash_key(char* key) +{ + unsigned int hash = 5381; + for (int i = 0; key[i] != '\0'; i++) + { + hash = ((hash << 5) + hash) + key[i]; + } + return hash; +} + +int hashmap_str_to_voidptr_equals_key(char* key1, char* key2) +{ + return strcmp(key1, key2) == 0; +} + +unsigned int hashmap_str_to_voidptr_hash_value(void* value) +{ + return (unsigned int)(size_t)value; +} + +int hashmap_str_to_voidptr_equals_value(void* value1, void* value2) +{ + return value1 == value2; +} + +#include "thirdparty/hashmap/hashmap.h" + +typedef struct ASPL_ClassInstance +{ + char* typePtr; + int typeLen; + hashmap_str_to_voidptr_HashMap* properties; + char isError; +} ASPL_ClassInstance; + +ASPL_OBJECT_TYPE* ASPL_CLASS_INSTANCE_GET_PROPERTY_ADDRESS(ASPL_ClassInstance* instance, char* property) +{ + return ASPL_HASHMAP_UNWRAP((ASPL_Object_internal**)hashmap_str_to_voidptr_hashmap_get_value(instance->properties, property)); +} + +ASPL_OBJECT_TYPE ASPL_CLASS_INSTANCE_GET_PROPERTY(ASPL_ClassInstance* instance, char* property) +{ + return *ASPL_HASHMAP_UNWRAP((ASPL_Object_internal**)hashmap_str_to_voidptr_hashmap_get_value(instance->properties, property)); +} + +ASPL_OBJECT_TYPE ASPL_CLASS_INSTANCE_SET_PROPERTY(ASPL_ClassInstance* instance, char* property, ASPL_OBJECT_TYPE value) +{ + if (hashmap_str_to_voidptr_hashmap_get_value(instance->properties, property) == NULL) + { + ASPL_Object_internal** address = ASPL_MALLOC(sizeof(ASPL_Object_internal*)); + *address = ASPL_HASHMAP_WRAP(value); + hashmap_str_to_voidptr_hashmap_set(instance->properties, property, address); + } + else { + *ASPL_HASHMAP_UNWRAP((ASPL_Object_internal**)hashmap_str_to_voidptr_hashmap_get_value(instance->properties, property)) = value; + } + return value; +} + +typedef struct ASPL_ReactiveProperty +{ + void* get_callback; + void* set_callback; +} ASPL_ReactiveProperty; + +hashmap_str_to_voidptr_HashMap reactive_properties_map; + +void ASPL_CLASS_INIT_REACTIVE_PROPERTY(char* class, char* property, void* get_callback, void* set_callback) +{ + ASPL_ReactiveProperty* reactive_property = ASPL_MALLOC(sizeof(ASPL_ReactiveProperty)); + reactive_property->get_callback = get_callback; + reactive_property->set_callback = set_callback; + hashmap_str_to_voidptr_HashMap* reactive_properties = hashmap_str_to_voidptr_hashmap_get_value(&reactive_properties_map, class); + if (reactive_properties == NULL) + { + reactive_properties = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 1 }); + hashmap_str_to_voidptr_hashmap_set(&reactive_properties_map, class, reactive_properties); + } + hashmap_str_to_voidptr_hashmap_set(reactive_properties, property, reactive_property); +} + +ASPL_OBJECT_TYPE ASPL_REACTIVE_PROPERTY_GET(ASPL_OBJECT_TYPE* this, char* property) +{ + ASPL_ReactiveProperty* reactive_property = hashmap_str_to_voidptr_hashmap_get_value(hashmap_str_to_voidptr_hashmap_get_value(&reactive_properties_map, ASPL_ACCESS(*this).value.classInstance->typePtr), property); + // TODO: Maybe add a debug check here to see if the property exists + return ((ASPL_OBJECT_TYPE(*)(ASPL_OBJECT_TYPE*))reactive_property->get_callback)(this); +} + +ASPL_OBJECT_TYPE ASPL_REACTIVE_PROPERTY_SET(ASPL_OBJECT_TYPE* this, char* property, ASPL_OBJECT_TYPE* value) +{ + ASPL_ReactiveProperty* reactive_property = hashmap_str_to_voidptr_hashmap_get_value(hashmap_str_to_voidptr_hashmap_get_value(&reactive_properties_map, ASPL_ACCESS(*this).value.classInstance->typePtr), property); + return ((ASPL_OBJECT_TYPE(*)(ASPL_OBJECT_TYPE*, ASPL_OBJECT_TYPE*))reactive_property->set_callback)(this, value); +} + +#undef HASHMAP_PREFIX +#undef HASHMAP_KEY_TYPE +#undef HASHMAP_KEY_NULL_VALUE +#undef HASHMAP_VALUE_TYPE +#undef HASHMAP_VALUE_NULL_VALUE +#define HASHMAP_PREFIX hashmap_int_to_str +#define HASHMAP_KEY_TYPE int +#define HASHMAP_KEY_NULL_VALUE 0 +#define HASHMAP_VALUE_TYPE char * +#define HASHMAP_VALUE_NULL_VALUE NULL + +unsigned int hashmap_int_to_str_hash_key(int key) +{ + return key; +} + +int hashmap_int_to_str_equals_key(int key1, int key2) +{ + return key1 == key2; +} + +unsigned int hashmap_int_to_str_hash_value(char* value) +{ + unsigned long hash = 5381; + int c; + char* str = value; + while ((c = *str++)) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + return hash; +} + +int hashmap_int_to_str_equals_value(char* value1, char* value2) +{ + return strcmp(value1, value2) == 0; +} + +#include "thirdparty/hashmap/hashmap.h" + +typedef struct ASPL_Enum +{ + char* typePtr; + int typeLen; + char isFlagEnum; + hashmap_int_to_str_HashMap* stringValues; +} ASPL_Enum; + +typedef struct ASPL_EnumField +{ + ASPL_Enum* e; + int intValue; +} ASPL_EnumField; + +hashmap_str_to_voidptr_HashMap enums_map; + +unsigned int aspl_hash(ASPL_OBJECT_TYPE obj) +{ + switch (ASPL_ACCESS(obj).kind) + { + case ASPL_OBJECT_KIND_NULL: + return 0; + case ASPL_OBJECT_KIND_BOOLEAN: + return ASPL_ACCESS(obj).value.boolean; + case ASPL_OBJECT_KIND_BYTE: + return ASPL_ACCESS(obj).value.integer8; + case ASPL_OBJECT_KIND_INTEGER: + return ASPL_ACCESS(obj).value.integer32; + case ASPL_OBJECT_KIND_LONG: + return ASPL_ACCESS(obj).value.integer64; + case ASPL_OBJECT_KIND_FLOAT: + return ASPL_ACCESS(obj).value.float32; + case ASPL_OBJECT_KIND_DOUBLE: + return ASPL_ACCESS(obj).value.float64; + case ASPL_OBJECT_KIND_STRING: + { + // hash function from https://stackoverflow.com/a/7666577 + unsigned long hash = 5381; + int c; + char* str = ASPL_ACCESS(obj).value.string->str; + while ((c = *str++)) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + return hash; + } + case ASPL_OBJECT_KIND_LIST: + { + unsigned long hash = 5381; + for (int i = 0; i < ASPL_ACCESS(obj).value.list->length; i++) + { + ASPL_OBJECT_TYPE item = ASPL_ACCESS(obj).value.list->value[i]; + hash = ((hash << 5) + hash) + aspl_hash(item); + } + return hash; + } + case ASPL_OBJECT_KIND_MAP: + { + unsigned long hash = 5381; + hashmap_aspl_object_to_aspl_object_Pair* pair = NULL; + while ((pair = hashmap_aspl_object_to_aspl_object_hashmap_next(ASPL_ACCESS(obj).value.map->hashmap)) != NULL) + { + hash = ((hash << 5) + hash) + aspl_hash(ASPL_HASHMAP_UNWRAP(pair->key)); + hash = ((hash << 5) + hash) + aspl_hash(ASPL_HASHMAP_UNWRAP(pair->value)); + } + return hash; + } + case ASPL_OBJECT_KIND_CALLBACK: + return (int)(size_t)ASPL_ACCESS(obj).value.callback; + case ASPL_OBJECT_KIND_POINTER: + return aspl_hash(*ASPL_ACCESS(obj).value.pointer->object); + case ASPL_OBJECT_KIND_CLASS_INSTANCE: + return (int)(size_t)ASPL_ACCESS(obj).value.classInstance; + case ASPL_OBJECT_KIND_ENUM_FIELD: + return ASPL_ACCESS(obj).value.enumField->intValue; + default: + return 0; + } +} + +ASPL_OBJECT_TYPE aspl_object_clone_shallow(ASPL_OBJECT_TYPE obj) +{ + ASPL_OBJECT_TYPE clone = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(clone).kind = ASPL_ACCESS(obj).kind; + switch (ASPL_ACCESS(obj).kind) + { + case ASPL_OBJECT_KIND_LIST: + ASPL_ACCESS(clone).value.list = ASPL_MALLOC(sizeof(ASPL_List)); + ASPL_ACCESS(clone).value.list->typePtr = ASPL_ACCESS(obj).value.list->typePtr; + ASPL_ACCESS(clone).value.list->typeLen = ASPL_ACCESS(obj).value.list->typeLen; + ASPL_ACCESS(clone).value.list->length = ASPL_ACCESS(obj).value.list->length; + int mallocSize = sizeof(ASPL_OBJECT_TYPE) * ASPL_ACCESS(obj).value.list->length; + if (mallocSize < 1) mallocSize = 1; // malloc can return a null pointer if size is 0, but e.g. memcpy can't handle null pointers (even if the size is 0) + ASPL_ACCESS(clone).value.list->value = ASPL_MALLOC(mallocSize); + memcpy(ASPL_ACCESS(clone).value.list->value, ASPL_ACCESS(obj).value.list->value, sizeof(ASPL_OBJECT_TYPE) * ASPL_ACCESS(obj).value.list->length); + break; + case ASPL_OBJECT_KIND_MAP: + { + ASPL_ACCESS(clone).value.map = ASPL_MALLOC(sizeof(ASPL_Map)); + ASPL_ACCESS(clone).value.map->typePtr = ASPL_ACCESS(obj).value.map->typePtr; + ASPL_ACCESS(clone).value.map->typeLen = ASPL_ACCESS(obj).value.map->typeLen; + int initial_capacity = ASPL_ACCESS(obj).value.map->hashmap->len; + if (initial_capacity < 1) initial_capacity = 1; // hashmap_new can't handle 0 as initial capacity + ASPL_ACCESS(clone).value.map->hashmap = hashmap_aspl_object_to_aspl_object_new_hashmap((hashmap_aspl_object_to_aspl_object_HashMapConfig) { .initial_capacity = initial_capacity }); + hashmap_aspl_object_to_aspl_object_Pair* pair = NULL; + while ((pair = hashmap_aspl_object_to_aspl_object_hashmap_next(ASPL_ACCESS(obj).value.map->hashmap)) != NULL) + { + hashmap_aspl_object_to_aspl_object_hashmap_set(ASPL_ACCESS(clone).value.map->hashmap, pair->key, pair->value); + } + break; + } + case ASPL_OBJECT_KIND_POINTER: + ASPL_ACCESS(clone).value.pointer = ASPL_MALLOC(sizeof(ASPL_Pointer)); + ASPL_ACCESS(clone).value.pointer->typePtr = ASPL_ACCESS(obj).value.pointer->typePtr; + ASPL_ACCESS(clone).value.pointer->typeLen = ASPL_ACCESS(obj).value.pointer->typeLen; + ASPL_ACCESS(clone).value.pointer->object = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE)); + *ASPL_ACCESS(clone).value.pointer->object = *ASPL_ACCESS(obj).value.pointer->object; + break; + case ASPL_OBJECT_KIND_CLASS_INSTANCE: + { + ASPL_ACCESS(clone).value.classInstance = ASPL_MALLOC(sizeof(ASPL_ClassInstance)); + ASPL_ACCESS(clone).value.classInstance->typePtr = ASPL_ACCESS(obj).value.classInstance->typePtr; + ASPL_ACCESS(clone).value.classInstance->typeLen = ASPL_ACCESS(obj).value.classInstance->typeLen; + int initial_capacity = ASPL_ACCESS(obj).value.classInstance->properties->len; + if (initial_capacity < 1) initial_capacity = 1; // hashmap_new can't handle 0 as initial capacity + ASPL_ACCESS(clone).value.classInstance->properties = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = initial_capacity }); + char* key = NULL; + for (int i = 0; i < ASPL_ACCESS(obj).value.classInstance->properties->len; i++) + { + key = ASPL_ACCESS(obj).value.classInstance->properties->pairs[i]->key; + if (key != NULL) + { + hashmap_str_to_voidptr_hashmap_set(ASPL_ACCESS(clone).value.classInstance->properties, key, hashmap_str_to_voidptr_hashmap_get_value(ASPL_ACCESS(obj).value.classInstance->properties, key)); + } + } + ASPL_ACCESS(clone).value.classInstance->isError = ASPL_ACCESS(obj).value.classInstance->isError; + break; + } + default: + ASPL_ACCESS(clone).value = ASPL_ACCESS(obj).value; + } + return clone; +} + +ASPL_OBJECT_TYPE aspl_object_clone_deep(ASPL_OBJECT_TYPE obj) +{ + ASPL_OBJECT_TYPE clone = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(clone).kind = ASPL_ACCESS(obj).kind; + switch (ASPL_ACCESS(obj).kind) + { + case ASPL_OBJECT_KIND_STRING: + ASPL_ACCESS(clone).value.string = ASPL_MALLOC(sizeof(ASPL_String)); + ASPL_ACCESS(clone).value.string->str = ASPL_MALLOC(ASPL_ACCESS(obj).value.string->length + 1); + memcpy(ASPL_ACCESS(clone).value.string->str, ASPL_ACCESS(obj).value.string->str, ASPL_ACCESS(obj).value.string->length); + ASPL_ACCESS(clone).value.string->str[ASPL_ACCESS(obj).value.string->length] = '\0'; + ASPL_ACCESS(clone).value.string->length = ASPL_ACCESS(obj).value.string->length; + break; + case ASPL_OBJECT_KIND_LIST: + ASPL_ACCESS(clone).value.list = ASPL_MALLOC(sizeof(ASPL_List)); + ASPL_ACCESS(clone).value.list->typePtr = ASPL_ACCESS(obj).value.list->typePtr; + ASPL_ACCESS(clone).value.list->typeLen = ASPL_ACCESS(obj).value.list->typeLen; + ASPL_ACCESS(clone).value.list->length = ASPL_ACCESS(obj).value.list->length; + int mallocSize = sizeof(ASPL_OBJECT_TYPE) * ASPL_ACCESS(obj).value.list->length; + if (mallocSize < 1) mallocSize = 1; // malloc can return a null pointer if size is 0, but e.g. memcpy can't handle null pointers (even if the size is 0 + ASPL_ACCESS(clone).value.list->value = ASPL_MALLOC(mallocSize); + for (int i = 0; i < ASPL_ACCESS(obj).value.list->length; i++) + { + ASPL_ACCESS(clone).value.list->value[i] = aspl_object_clone_deep(ASPL_ACCESS(obj).value.list->value[i]); + } + break; + case ASPL_OBJECT_KIND_MAP: + { + ASPL_ACCESS(clone).value.map = ASPL_MALLOC(sizeof(ASPL_Map)); + ASPL_ACCESS(clone).value.map->typePtr = ASPL_ACCESS(obj).value.map->typePtr; + ASPL_ACCESS(clone).value.map->typeLen = ASPL_ACCESS(obj).value.map->typeLen; + int initial_capacity = ASPL_ACCESS(obj).value.map->hashmap->len; + if (initial_capacity < 1) initial_capacity = 1; // hashmap_new can't handle 0 as initial capacity + ASPL_ACCESS(clone).value.map->hashmap = hashmap_aspl_object_to_aspl_object_new_hashmap((hashmap_aspl_object_to_aspl_object_HashMapConfig) { .initial_capacity = initial_capacity }); + hashmap_aspl_object_to_aspl_object_Pair* pair = NULL; + while ((pair = hashmap_aspl_object_to_aspl_object_hashmap_next(ASPL_ACCESS(obj).value.map->hashmap)) != NULL) + { + hashmap_aspl_object_to_aspl_object_hashmap_set(ASPL_ACCESS(clone).value.map->hashmap, ASPL_HASHMAP_WRAP(aspl_object_clone_deep(ASPL_HASHMAP_UNWRAP(pair->key))), ASPL_HASHMAP_WRAP(aspl_object_clone_deep(ASPL_HASHMAP_UNWRAP(pair->value)))); + } + break; + } + case ASPL_OBJECT_KIND_POINTER: + ASPL_ACCESS(clone).value.pointer = ASPL_MALLOC(sizeof(ASPL_Pointer)); + ASPL_ACCESS(clone).value.pointer->typePtr = ASPL_ACCESS(obj).value.pointer->typePtr; + ASPL_ACCESS(clone).value.pointer->typeLen = ASPL_ACCESS(obj).value.pointer->typeLen; + ASPL_ACCESS(clone).value.pointer->object = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE)); + *ASPL_ACCESS(clone).value.pointer->object = aspl_object_clone_deep(*ASPL_ACCESS(obj).value.pointer->object); + break; + case ASPL_OBJECT_KIND_CLASS_INSTANCE: + { + ASPL_ACCESS(clone).value.classInstance = ASPL_MALLOC(sizeof(ASPL_ClassInstance)); + ASPL_ACCESS(clone).value.classInstance->typePtr = ASPL_ACCESS(obj).value.classInstance->typePtr; + ASPL_ACCESS(clone).value.classInstance->typeLen = ASPL_ACCESS(obj).value.classInstance->typeLen; + int initial_capacity = ASPL_ACCESS(obj).value.classInstance->properties->len; + if (initial_capacity < 1) initial_capacity = 1; // hashmap_new can't handle 0 as initial capacity + ASPL_ACCESS(clone).value.classInstance->properties = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = initial_capacity }); + char* key = NULL; + for (int i = 0; i < ASPL_ACCESS(obj).value.classInstance->properties->len; i++) + { + key = ASPL_ACCESS(obj).value.classInstance->properties->pairs[i]->key; + if (key != NULL) + { + hashmap_str_to_voidptr_hashmap_set(ASPL_ACCESS(clone).value.classInstance->properties, key, ASPL_HASHMAP_WRAP(aspl_object_clone_deep(ASPL_HASHMAP_UNWRAP((ASPL_Object_internal*)hashmap_str_to_voidptr_hashmap_get_value(ASPL_ACCESS(obj).value.classInstance->properties, key))))); + } + } + ASPL_ACCESS(clone).value.classInstance->isError = ASPL_ACCESS(obj).value.classInstance->isError; + break; + } + default: + ASPL_ACCESS(clone).value = ASPL_ACCESS(obj).value; + break; + } + return clone; +} + +#ifndef ASPL_NO_MULTITHREADING +void ASPL_LAUNCH_THREAD(int (*callback)(void*), void* arguments, int arg_size) +{ + void* args = ASPL_MALLOC(arg_size); + memcpy(args, arguments, arg_size); + thread_create(callback, args, THREAD_STACK_SIZE_DEFAULT); +} +typedef struct ASPL_ThreadMethodWrapperData +{ + ASPL_OBJECT_TYPE (*callback)(ASPL_OBJECT_TYPE*, ASPL_OBJECT_TYPE* []); + ASPL_OBJECT_TYPE* this; + ASPL_OBJECT_TYPE** args; +} ASPL_ThreadMethodWrapperData; + +int aspl_method_thread_wrapper(void* arguments) +{ + struct GC_stack_base sb; + GC_get_stack_base(&sb); + GC_register_my_thread(&sb); + + ASPL_ThreadMethodWrapperData* data = arguments; + ASPL_OBJECT_TYPE (*callback)(ASPL_OBJECT_TYPE*, ASPL_OBJECT_TYPE* []) = data->callback; + ASPL_OBJECT_TYPE* this = data->this; + ASPL_OBJECT_TYPE** args = data->args; + callback(this, args); + + GC_unregister_my_thread(); + return 0; +} + +void ASPL_LAUNCH_THREAD_METHOD(ASPL_OBJECT_TYPE (*callback)(ASPL_OBJECT_TYPE*, ASPL_OBJECT_TYPE* []), ASPL_OBJECT_TYPE* this, void* arguments, int arg_size) +{ + ASPL_ThreadMethodWrapperData* args = ASPL_MALLOC(sizeof(ASPL_ThreadMethodWrapperData)); + args->callback = callback; + args->this = this; + args->args = arguments; + thread_create(aspl_method_thread_wrapper, args, THREAD_STACK_SIZE_DEFAULT); +} +#endif + +__attribute__((noreturn)) void ASPL_PANIC(const char* format, ...) +{ + va_list args; + va_start(args, format); + aspl_util_print("ASPL panic: "); + aspl_util_vprint(format, args); + aspl_util_print("\n"); + va_end(args); + +#ifdef ASPL_DEBUG + abort(); +#else + exit(1); +#endif +} + +ASPL_OBJECT_TYPE ASPL_NULL() +{ + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_NULL; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_TRUE() +{ + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(obj).value.boolean = 1; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_FALSE() +{ + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(obj).value.boolean = 0; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_BOOL_LITERAL(char b) +{ + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(obj).value.boolean = b; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_EQUALS(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b) +{ + return ASPL_BOOL_LITERAL(ASPL_IS_EQUAL(a, b)); +} + +int ASPL_IS_TRUE(ASPL_OBJECT_TYPE x) +{ + ASPL_OBJECT_TYPE obj = (ASPL_OBJECT_TYPE)x; + if (ASPL_ACCESS(obj).kind == ASPL_OBJECT_KIND_BOOLEAN) + { + return ASPL_ACCESS(obj).value.boolean; + } + return 0; +} + +int ASPL_IS_EQUAL(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b) +{ + if (ASPL_ACCESS(a).kind != ASPL_ACCESS(b).kind) + return 0; + switch (ASPL_ACCESS(a).kind) + { + case ASPL_OBJECT_KIND_NULL: + return 1; + case ASPL_OBJECT_KIND_BOOLEAN: + return ASPL_ACCESS(a).value.boolean == ASPL_ACCESS(b).value.boolean; + case ASPL_OBJECT_KIND_BYTE: + return ASPL_ACCESS(a).value.integer8 == ASPL_ACCESS(b).value.integer8; + case ASPL_OBJECT_KIND_INTEGER: + return ASPL_ACCESS(a).value.integer32 == ASPL_ACCESS(b).value.integer32; + case ASPL_OBJECT_KIND_LONG: + return ASPL_ACCESS(a).value.integer64 == ASPL_ACCESS(b).value.integer64; + case ASPL_OBJECT_KIND_FLOAT: + return ASPL_ACCESS(a).value.float32 == ASPL_ACCESS(b).value.float32; + case ASPL_OBJECT_KIND_DOUBLE: + return ASPL_ACCESS(a).value.float64 == ASPL_ACCESS(b).value.float64; + case ASPL_OBJECT_KIND_STRING: + return ASPL_ACCESS(a).value.string->length == ASPL_ACCESS(b).value.string->length && strcmp(ASPL_ACCESS(a).value.string->str, ASPL_ACCESS(b).value.string->str) == 0; + case ASPL_OBJECT_KIND_LIST: + return ASPL_ACCESS(a).value.list == ASPL_ACCESS(b).value.list; // TODO: Shouldn't this compare the elements and check if they are equal? + case ASPL_OBJECT_KIND_MAP: + // return ASPL_ACCESS(a).value.map == ASPL_ACCESS(b).value.map; + return hashmap_aspl_object_to_aspl_object_hashmap_equals(ASPL_ACCESS(a).value.map->hashmap, ASPL_ACCESS(b).value.map->hashmap); + case ASPL_OBJECT_KIND_CALLBACK: + return ASPL_ACCESS(a).value.callback == ASPL_ACCESS(b).value.callback; + case ASPL_OBJECT_KIND_POINTER: + return ASPL_ACCESS(a).value.pointer == ASPL_ACCESS(b).value.pointer; + case ASPL_OBJECT_KIND_CLASS_INSTANCE: + return ASPL_ACCESS(a).value.classInstance == ASPL_ACCESS(b).value.classInstance; + case ASPL_OBJECT_KIND_ENUM_FIELD: + return ASPL_ACCESS(a).value.enumField->intValue == ASPL_ACCESS(b).value.enumField->intValue; + default: + return 0; + } +} + +ASPL_OBJECT_TYPE ASPL_NEGATE(ASPL_OBJECT_TYPE a) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a; + if (ASPL_ACCESS(objA).kind == ASPL_OBJECT_KIND_BOOLEAN) + { + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(obj).value.boolean = !ASPL_ACCESS(objA).value.boolean; + return obj; + } + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_STRING_LITERAL(const char* str) +{ + size_t len = strlen(str); + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_STRING; + ASPL_ACCESS(obj).value.string = ASPL_MALLOC(sizeof(ASPL_String)); + ASPL_ACCESS(obj).value.string->str = ASPL_MALLOC_ATOMIC(len + 1); + memcpy(ASPL_ACCESS(obj).value.string->str, str, len); + ASPL_ACCESS(obj).value.string->str[len] = '\0'; + ASPL_ACCESS(obj).value.string->length = len; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_STRING_LITERAL_NO_COPY(char* str) +{ + size_t len = strlen(str); + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_STRING; + ASPL_ACCESS(obj).value.string = ASPL_MALLOC(sizeof(ASPL_String)); + ASPL_ACCESS(obj).value.string->str = str; + ASPL_ACCESS(obj).value.string->length = len; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_BYTE_LITERAL(unsigned char b) +{ + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_BYTE; + ASPL_ACCESS(obj).value.integer8 = b; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_INT_LITERAL(long i) +{ + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(obj).value.integer32 = i; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_LONG_LITERAL(long long l) +{ + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(obj).value.integer64 = l; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_FLOAT_LITERAL(float f) +{ + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(obj).value.float32 = f; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_DOUBLE_LITERAL(double d) +{ + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(obj).value.float64 = d; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_LIST_LITERAL(char* typePtr, int typeLen, ASPL_OBJECT_TYPE list[], size_t size) +{ + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_LIST; + ASPL_List* l = ASPL_MALLOC(sizeof(ASPL_List)); + l->typePtr = typePtr; + l->typeLen = typeLen; + l->length = size; + int mallocSize = sizeof(ASPL_OBJECT_TYPE) * size; + if (mallocSize < 1) mallocSize = 1; // malloc can return a null pointer if size is 0, but e.g. memcpy can't handle null pointers (even if the size is 0) + l->value = ASPL_MALLOC(mallocSize); + memcpy(l->value, list, sizeof(ASPL_OBJECT_TYPE) * size); + ASPL_ACCESS(obj).value.list = l; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_MAP_LITERAL(char* typePtr, int typeLen, ASPL_OBJECT_TYPE list[], size_t size) +{ + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_MAP; + ASPL_Map* m = ASPL_MALLOC(sizeof(ASPL_Map)); + m->typePtr = typePtr; + m->typeLen = typeLen; + hashmap_aspl_object_to_aspl_object_HashMap* hashmap = hashmap_aspl_object_to_aspl_object_new_hashmap((hashmap_aspl_object_to_aspl_object_HashMapConfig) { .initial_capacity = size < 1 ? 1 : size }); + for (int i = 0; i < size * 2; i += 2) + { + hashmap_aspl_object_to_aspl_object_hashmap_set(hashmap, ASPL_HASHMAP_WRAP(list[i]), ASPL_HASHMAP_WRAP(list[i + 1])); + } + m->hashmap = hashmap; + ASPL_ACCESS(obj).value.map = m; + return obj; +} + +ASPL_ClosureMap* ASPL_NEW_CLOSURE_MAP(int count, ...) +{ + va_list args; + va_start(args, count); + + ASPL_ClosureMapPair* pairs = ASPL_MALLOC(count * sizeof(ASPL_ClosureMapPair)); + for (int i = 0; i < count; i++) + { + pairs[i].key = va_arg(args, char*); + pairs[i].value = va_arg(args, ASPL_OBJECT_TYPE*); + } + + va_end(args); + ASPL_ClosureMap* map = ASPL_MALLOC(sizeof(ASPL_ClosureMap)); + map->pairs = pairs; + map->length = count; + return map; +} + +ASPL_OBJECT_TYPE* ASPL_CLOSURE_MAP_GET(ASPL_ClosureMap* map, char* key) +{ + for (int i = 0; i < map->length; i++) + { + if (strcmp(map->pairs[i].key, key) == 0) + { + return map->pairs[i].value; + } + } + return NULL; +} + +ASPL_OBJECT_TYPE ASPL_CALLBACK_LITERAL(char* typePtr, int typeLen, void* ptr, ASPL_ClosureMap* closure_map) +{ + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_CALLBACK; + ASPL_Callback* cb = ASPL_MALLOC(sizeof(ASPL_Callback)); + cb->typePtr = typePtr; + cb->typeLen = typeLen; + cb->function = ptr; + cb->closure_map = closure_map; + ASPL_ACCESS(obj).value.callback = cb; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_ENUM_FIELD_LITERAL(ASPL_Enum* e, int value) +{ + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_ENUM_FIELD; + ASPL_EnumField* field = ASPL_MALLOC(sizeof(ASPL_EnumField)); + field->e = e; + field->intValue = value; + ASPL_ACCESS(obj).value.enumField = field; + return obj; +} + +char* aspl_object_get_type_pointer(ASPL_OBJECT_TYPE obj) +{ + switch (ASPL_ACCESS(obj).kind) + { + case ASPL_OBJECT_KIND_NULL: + return "null"; + case ASPL_OBJECT_KIND_BOOLEAN: + return "boolean"; + case ASPL_OBJECT_KIND_INTEGER: + return "integer"; + case ASPL_OBJECT_KIND_LONG: + return "integer64"; + case ASPL_OBJECT_KIND_FLOAT: + return "float"; + case ASPL_OBJECT_KIND_DOUBLE: + return "double"; + case ASPL_OBJECT_KIND_STRING: + return "string"; + case ASPL_OBJECT_KIND_LIST: + return ASPL_ACCESS(obj).value.list->typePtr; + case ASPL_OBJECT_KIND_MAP: + return ASPL_ACCESS(obj).value.map->typePtr; + case ASPL_OBJECT_KIND_CALLBACK: + return ASPL_ACCESS(obj).value.callback->typePtr; + case ASPL_OBJECT_KIND_POINTER: + return ASPL_ACCESS(obj).value.pointer->typePtr; + case ASPL_OBJECT_KIND_CLASS_INSTANCE: + return ASPL_ACCESS(obj).value.classInstance->typePtr; + case ASPL_OBJECT_KIND_ENUM_FIELD: + return ASPL_ACCESS(obj).value.enumField->e->typePtr; + case ASPL_OBJECT_KIND_HANDLE: + return "handle"; + default: + return "unknown"; + } +} + +char* aspl_object_get_short_type_pointer(ASPL_OBJECT_TYPE obj) +{ + switch (ASPL_ACCESS(obj).kind) + { + case ASPL_OBJECT_KIND_LIST: + return "list"; + case ASPL_OBJECT_KIND_MAP: + return "map"; + default: + return aspl_object_get_type_pointer(obj); + } +} + +char aspl_object_is_error(ASPL_OBJECT_TYPE obj) { + if (ASPL_ACCESS(obj).kind == ASPL_OBJECT_KIND_CLASS_INSTANCE) { + return ASPL_ACCESS(obj).value.classInstance->isError; + } + return 0; +} + +typedef struct ASPL_ParentsList +{ + int amount; + char** parents; +} ASPL_ParentsList; + +hashmap_str_to_voidptr_HashMap class_parents_map; + +void aspl_class_parent_init(char* class, char* parent) +{ + unsigned int key = hashmap_str_to_voidptr_hash_key(class); + ASPL_ParentsList* parents = hashmap_str_to_voidptr_hashmap_get_value(&class_parents_map, class); + if (parents == NULL) + { + parents = ASPL_MALLOC(sizeof(ASPL_ParentsList)); + parents->amount = 0; + parents->parents = ASPL_MALLOC(sizeof(char*)); + hashmap_str_to_voidptr_hashmap_set(&class_parents_map, class, parents); + } + parents->amount++; + parents->parents = ASPL_REALLOC(parents->parents, sizeof(char*) * parents->amount); + parents->parents[parents->amount - 1] = parent; +} + +int aspl_is_class_child_of(char* class, char* parent) +{ + ASPL_ParentsList* parents = hashmap_str_to_voidptr_hashmap_get_value(&class_parents_map, class); + if (parents == NULL) + { + return 0; + } + for (int i = 0; i < parents->amount; i++) + { + if (strcmp(parents->parents[i], parent) == 0) + { + return 1; + } + } + return 0; +} + +ASPL_OBJECT_TYPE ASPL_OFTYPE(ASPL_OBJECT_TYPE a, char* type) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a; + int typeLen = strlen(type); + switch (ASPL_ACCESS(a).kind) + { + case ASPL_OBJECT_KIND_NULL: + return ASPL_BOOL_LITERAL(strcmp(type, "null") == 0); + case ASPL_OBJECT_KIND_BOOLEAN: + return ASPL_BOOL_LITERAL(strcmp(type, "bool") == 0); + case ASPL_OBJECT_KIND_BYTE: + return ASPL_BOOL_LITERAL(strcmp(type, "byte") == 0); + case ASPL_OBJECT_KIND_INTEGER: + return ASPL_BOOL_LITERAL(strcmp(type, "int") == 0); + case ASPL_OBJECT_KIND_LONG: + return ASPL_BOOL_LITERAL(strcmp(type, "long") == 0); + case ASPL_OBJECT_KIND_FLOAT: + return ASPL_BOOL_LITERAL(strcmp(type, "float") == 0); + case ASPL_OBJECT_KIND_DOUBLE: + return ASPL_BOOL_LITERAL(strcmp(type, "double") == 0); + case ASPL_OBJECT_KIND_STRING: + return ASPL_BOOL_LITERAL(strcmp(type, "string") == 0); + case ASPL_OBJECT_KIND_LIST: + return ASPL_BOOL_LITERAL(ASPL_ACCESS(objA).value.list->typeLen == typeLen && memcmp(ASPL_ACCESS(objA).value.list->typePtr, type, typeLen) == 0); + case ASPL_OBJECT_KIND_MAP: + return ASPL_BOOL_LITERAL(ASPL_ACCESS(objA).value.map->typeLen == typeLen && memcmp(ASPL_ACCESS(objA).value.map->typePtr, type, typeLen) == 0); + case ASPL_OBJECT_KIND_CALLBACK: + return ASPL_BOOL_LITERAL(ASPL_ACCESS(objA).value.callback->typeLen == typeLen && memcmp(ASPL_ACCESS(objA).value.callback->typePtr, type, typeLen) == 0); + case ASPL_OBJECT_KIND_POINTER: + return ASPL_BOOL_LITERAL(ASPL_ACCESS(objA).value.pointer->typeLen == typeLen && memcmp(ASPL_ACCESS(objA).value.pointer->typePtr, type, typeLen) == 0); + case ASPL_OBJECT_KIND_CLASS_INSTANCE: + if (ASPL_ACCESS(objA).value.classInstance->typeLen == typeLen && memcmp(ASPL_ACCESS(objA).value.classInstance->typePtr, type, typeLen) == 0) + { + return ASPL_TRUE(); + } + else + { + return ASPL_BOOL_LITERAL(aspl_is_class_child_of(ASPL_ACCESS(objA).value.classInstance->typePtr, type)); + } + case ASPL_OBJECT_KIND_ENUM_FIELD: + return ASPL_BOOL_LITERAL(ASPL_ACCESS(objA).value.enumField->e->typeLen == typeLen && memcmp(ASPL_ACCESS(objA).value.enumField->e->typePtr, type, typeLen) == 0); + default: + return ASPL_FALSE(); + } +} + +ASPL_OBJECT_TYPE ASPL_REFERENCE(char* typePtr, int typeLen, ASPL_OBJECT_TYPE* address) +{ + ASPL_OBJECT_TYPE pointerObject = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(pointerObject).kind = ASPL_OBJECT_KIND_POINTER; + ASPL_Pointer* ptr = ASPL_MALLOC(sizeof(ASPL_Pointer)); + ptr->typePtr = typePtr; + ptr->typeLen = typeLen; + ptr->object = address; + ASPL_ACCESS(pointerObject).value.pointer = ptr; + return pointerObject; +} + +#define ASPL_DEREFERENCE(obj) (*ASPL_ACCESS(obj).value.pointer->object) + +ASPL_OBJECT_TYPE ASPL_HANDLE_LITERAL(void* ptr) +{ + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_HANDLE; + ASPL_ACCESS(obj).value.handle = ptr; + return obj; +} + +void ASPL_ASSERT(ASPL_OBJECT_TYPE obj, char* file, int line, int column) +{ + if (ASPL_IS_TRUE(obj)) + { + return; + } + aspl_util_print("%s:%d:%d: Assertion failed\n", file, line, column); + exit(1); +} + +char* aspl_stringify(ASPL_OBJECT_TYPE obj) +{ + char* str = NULL; + switch (ASPL_ACCESS(obj).kind) + { + case ASPL_OBJECT_KIND_NULL: + return "null"; + break; + case ASPL_OBJECT_KIND_BOOLEAN: + return ASPL_ACCESS(obj).value.boolean ? "true" : "false"; + break; + case ASPL_OBJECT_KIND_BYTE: + str = ASPL_MALLOC(sizeof(char) * 4); + sprintf(str, "%u", ASPL_ACCESS(obj).value.integer8); + return str; + break; + case ASPL_OBJECT_KIND_INTEGER: + str = ASPL_MALLOC(sizeof(char) * 12); + sprintf(str, "%ld", ASPL_ACCESS(obj).value.integer32); + return str; + break; + case ASPL_OBJECT_KIND_LONG: + str = ASPL_MALLOC(sizeof(char) * 24); + sprintf(str, "%lld", ASPL_ACCESS(obj).value.integer64); + return str; + break; + case ASPL_OBJECT_KIND_FLOAT: + str = ASPL_MALLOC(sizeof(char) * 12); + sprintf(str, "%f", ASPL_ACCESS(obj).value.float32); + return str; + break; + case ASPL_OBJECT_KIND_DOUBLE: + str = ASPL_MALLOC(sizeof(char) * 24); + sprintf(str, "%f", ASPL_ACCESS(obj).value.float64); + return str; + break; + case ASPL_OBJECT_KIND_STRING: + return ASPL_ACCESS(obj).value.string->str; + case ASPL_OBJECT_KIND_LIST: + str = ASPL_MALLOC(sizeof(char) * 3); + strcat(str, "["); + for (int i = 0; i < ASPL_ACCESS(obj).value.list->length; i++) + { + char* str2 = aspl_stringify(ASPL_ACCESS(obj).value.list->value[i]); + if (i != ASPL_ACCESS(obj).value.list->length - 1) + { + str = ASPL_REALLOC(str, sizeof(char) * (strlen(str) + strlen(str2) + 3)); + strcat(str, str2); + strcat(str, ", "); + } + else + { + str = ASPL_REALLOC(str, sizeof(char) * (strlen(str) + strlen(str2) + 2)); + strcat(str, str2); + } + } + strcat(str, "]"); + return str; + break; + case ASPL_OBJECT_KIND_MAP: + str = ASPL_MALLOC(sizeof(char) * 2); + strcat(str, "{"); + for (int i = 0; i < ASPL_ACCESS(obj).value.map->hashmap->len; i++) + { + char* str2 = aspl_stringify(ASPL_HASHMAP_UNWRAP(ASPL_ACCESS(obj).value.map->hashmap->pairs[i]->key)); + char* str3 = aspl_stringify(ASPL_HASHMAP_UNWRAP(ASPL_ACCESS(obj).value.map->hashmap->pairs[i]->value)); + if (i != ASPL_ACCESS(obj).value.map->hashmap->len - 1) + { + str = ASPL_REALLOC(str, sizeof(char) * (strlen(str) + strlen(str2) + strlen(str3) + 5)); + strcat(str, str2); + strcat(str, ": "); + strcat(str, str3); + strcat(str, ", "); + } + else + { + str = ASPL_REALLOC(str, sizeof(char) * (strlen(str) + strlen(str2) + strlen(str3) + 4)); + strcat(str, str2); + strcat(str, ": "); + strcat(str, str3); + } + } + strcat(str, "}"); + return str; + break; + case ASPL_OBJECT_KIND_CALLBACK: + return ASPL_ACCESS(obj).value.callback->typePtr; + break; + case ASPL_OBJECT_KIND_POINTER: + { + char* s = aspl_stringify(*ASPL_ACCESS(obj).value.pointer->object); + str = ASPL_MALLOC(sizeof(char) * (strlen(s) + 2)); + strcat(str, s); + strcat(str, "*"); + return str; + } + break; + case ASPL_OBJECT_KIND_ENUM_FIELD: + if (ASPL_ACCESS(obj).value.enumField->e->isFlagEnum) + { + for (int i = 0; i < ASPL_ACCESS(obj).value.enumField->e->stringValues->len; i++) + { + if (ASPL_ACCESS(obj).value.enumField->intValue & (1 << i)) + { + char* str2 = hashmap_int_to_str_hashmap_get_value(ASPL_ACCESS(obj).value.enumField->e->stringValues, 1 << i); + if (i != ASPL_ACCESS(obj).value.enumField->e->stringValues->len - 1 && (ASPL_ACCESS(obj).value.enumField->intValue & ~((1 << (i + 1)) - 1))) + { + if (str == NULL) + { + str = ASPL_MALLOC(sizeof(char) * (strlen(str2) + 2)); + } + else + { + str = ASPL_REALLOC(str, sizeof(char) * (strlen(str) + strlen(str2) + 2)); + } + strcat(str, str2); + strcat(str, "|"); + } + else + { + if (str == NULL) + { + str = ASPL_MALLOC(sizeof(char) * (strlen(str2) + 1)); + } + else + { + str = ASPL_REALLOC(str, sizeof(char) * (strlen(str) + strlen(str2) + 1)); + } + strcat(str, str2); + } + } + } + return str; + } + else + { + return hashmap_int_to_str_hashmap_get_value(ASPL_ACCESS(obj).value.enumField->e->stringValues, ASPL_ACCESS(obj).value.enumField->intValue); + } + default: + return "ASPL: Unknown type"; + break; + } +} + +ASPL_String aspl_stringify_rso(ASPL_OBJECT_TYPE obj) +{ + ASPL_String str = { 0 }; + switch (ASPL_ACCESS(obj).kind) + { + case ASPL_OBJECT_KIND_NULL: + str.str = "null"; + str.length = 4; + return str; + break; + case ASPL_OBJECT_KIND_BOOLEAN: + str.str = ASPL_ACCESS(obj).value.boolean ? "true" : "false"; + str.length = ASPL_ACCESS(obj).value.boolean ? 4 : 5; + return str; + break; + case ASPL_OBJECT_KIND_BYTE: + str.str = ASPL_MALLOC(sizeof(char) * 4); + sprintf(str.str, "%u", ASPL_ACCESS(obj).value.integer8); + str.length = strlen(str.str); + return str; + break; + case ASPL_OBJECT_KIND_INTEGER: + str.str = ASPL_MALLOC(sizeof(char) * 12); + sprintf(str.str, "%ld", ASPL_ACCESS(obj).value.integer32); + str.length = strlen(str.str); + return str; + break; + case ASPL_OBJECT_KIND_LONG: + str.str = ASPL_MALLOC(sizeof(char) * 24); + sprintf(str.str, "%lld", ASPL_ACCESS(obj).value.integer64); + str.length = strlen(str.str); + return str; + break; + case ASPL_OBJECT_KIND_FLOAT: + str.str = ASPL_MALLOC(sizeof(char) * 12); + sprintf(str.str, "%f", ASPL_ACCESS(obj).value.float32); + str.length = strlen(str.str); + return str; + break; + case ASPL_OBJECT_KIND_DOUBLE: + str.str = ASPL_MALLOC(sizeof(char) * 24); + sprintf(str.str, "%f", ASPL_ACCESS(obj).value.float64); + str.length = strlen(str.str); + return str; + break; + case ASPL_OBJECT_KIND_STRING: + return *ASPL_ACCESS(obj).value.string; + case ASPL_OBJECT_KIND_LIST: + str.str = ASPL_MALLOC(sizeof(char) * 3); + strcat(str.str, "["); + for (int i = 0; i < ASPL_ACCESS(obj).value.list->length; i++) + { + ASPL_String str2 = aspl_stringify_rso(ASPL_ACCESS(obj).value.list->value[i]); + if (i != ASPL_ACCESS(obj).value.list->length - 1) + { + str.str = ASPL_REALLOC(str.str, sizeof(char) * (str.length + str2.length + 3)); + strcat(str.str, str2.str); + strcat(str.str, ", "); + str.length += str2.length + 2; + } + else + { + str.str = ASPL_REALLOC(str.str, sizeof(char) * (str.length + str2.length + 2)); + strcat(str.str, str2.str); + str.length += str2.length + 1; + } + } + strcat(str.str, "]"); + str.length += 1; + return str; + break; + case ASPL_OBJECT_KIND_MAP: + str.str = ASPL_MALLOC(sizeof(char) * 2); + strcat(str.str, "{"); + for (int i = 0; i < ASPL_ACCESS(obj).value.map->hashmap->len; i++) + { + ASPL_String str2 = aspl_stringify_rso(ASPL_HASHMAP_UNWRAP(ASPL_ACCESS(obj).value.map->hashmap->pairs[i]->key)); + ASPL_String str3 = aspl_stringify_rso(ASPL_HASHMAP_UNWRAP(ASPL_ACCESS(obj).value.map->hashmap->pairs[i]->value)); + if (i != ASPL_ACCESS(obj).value.map->hashmap->len - 1) + { + str.str = ASPL_REALLOC(str.str, sizeof(char) * (str.length + str2.length + str3.length + 5)); + strcat(str.str, str2.str); + strcat(str.str, ": "); + strcat(str.str, str3.str); + strcat(str.str, ", "); + str.length += str2.length + str3.length + 4; + } + else + { + str.str = ASPL_REALLOC(str.str, sizeof(char) * (str.length + str2.length + str3.length + 4)); + strcat(str.str, str2.str); + strcat(str.str, ": "); + strcat(str.str, str3.str); + str.length += str2.length + str3.length + 3; + } + } + strcat(str.str, "}"); + str.length += 1; + return str; + break; + case ASPL_OBJECT_KIND_CALLBACK: + str.str = ASPL_ACCESS(obj).value.callback->typePtr; + str.length = ASPL_ACCESS(obj).value.callback->typeLen; + return str; + break; + case ASPL_OBJECT_KIND_POINTER: + { + ASPL_String s = aspl_stringify_rso(*ASPL_ACCESS(obj).value.pointer->object); + str.str = ASPL_MALLOC(sizeof(char) * (s.length + 2)); + strcat(str.str, s.str); + strcat(str.str, "*"); + str.length = s.length + 1; + return str; + } + break; + case ASPL_OBJECT_KIND_ENUM_FIELD: + /*if (((ASPL_EnumField *)ASPL_ACCESS(objA).value.custom)->e->isFlagEnum) + { + for (int i = 0; i < ((ASPL_EnumField *)ASPL_ACCESS(objA).value.custom)->e->stringValues->len; i++) + { + if (((ASPL_EnumField *)ASPL_ACCESS(objA).value.custom)->intValue & (1 << i)) + { + printf("%s", hashmap_int_to_str_hashmap_get_value(((ASPL_EnumField *)ASPL_ACCESS(objA).value.custom)->e->stringValues, 1 << i)); + if (i != ((ASPL_EnumField *)ASPL_ACCESS(objA).value.custom)->e->stringValues->len - 1 && (((ASPL_EnumField *)ASPL_ACCESS(objA).value.custom)->intValue & ~((1 << (i + 1)) - 1))) + { + printf("|"); + } + } + } + } + else + { + printf("%s", hashmap_int_to_str_hashmap_get_value(((ASPL_EnumField *)ASPL_ACCESS(objA).value.custom)->e->stringValues, ((ASPL_EnumField *)ASPL_ACCESS(objA).value.custom)->intValue)); + } + break;*/ + if (ASPL_ACCESS(obj).value.enumField->e->isFlagEnum) + { + for (int i = 0; i < ASPL_ACCESS(obj).value.enumField->e->stringValues->len; i++) + { + if (ASPL_ACCESS(obj).value.enumField->intValue & (1 << i)) + { + ASPL_String str2; + str2.str = hashmap_int_to_str_hashmap_get_value(ASPL_ACCESS(obj).value.enumField->e->stringValues, 1 << i); + str2.length = strlen(str2.str); + if (i != ASPL_ACCESS(obj).value.enumField->e->stringValues->len - 1 && (ASPL_ACCESS(obj).value.enumField->intValue & ~((1 << (i + 1)) - 1))) + { + if (str.str == NULL) + { + str.str = ASPL_MALLOC(sizeof(char) * (str2.length + 2)); + } + else + { + str.str = ASPL_REALLOC(str.str, sizeof(char) * (str.length + str2.length + 2)); + } + strcat(str.str, str2.str); + strcat(str.str, "|"); + str.length += str2.length + 1; + } + else + { + if (str.str == NULL) + { + str.str = ASPL_MALLOC(sizeof(char) * (str2.length + 1)); + } + else + { + str.str = ASPL_REALLOC(str.str, sizeof(char) * (str.length + str2.length + 1)); + } + strcat(str.str, str2.str); + str.length += str2.length; + } + } + } + return str; + } + else + { + ASPL_String str2; + str2.str = hashmap_int_to_str_hashmap_get_value(ASPL_ACCESS(obj).value.enumField->e->stringValues, ASPL_ACCESS(obj).value.enumField->intValue); + str2.length = strlen(str2.str); + return str2; + } + default: + str.str = "ASPL: Unknown type"; + str.length = 18; + return str; + break; + } +} + +#define ASPL_AND(a, b) (ASPL_IS_TRUE(a) ? b : ASPL_FALSE()) +#define ASPL_OR(a, b) (ASPL_IS_TRUE(a) ? ASPL_TRUE() : b) +#define ASPL_XOR(a, b) (ASPL_IS_TRUE(a) ? (ASPL_IS_TRUE(b) ? ASPL_FALSE() : ASPL_TRUE()) : b) +#define ASPL_NEGATE(a) (ASPL_IS_TRUE(a) ? ASPL_FALSE() : ASPL_TRUE()) + +ASPL_OBJECT_TYPE ASPL_ENUM_AND(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b; + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_ENUM_FIELD; + ASPL_ACCESS(obj).value.enumField = ASPL_MALLOC(sizeof(ASPL_EnumField)); + ASPL_ACCESS(obj).value.enumField->e = (ASPL_ACCESS(objA).value.enumField)->e; + ASPL_ACCESS(obj).value.enumField->intValue = (ASPL_ACCESS(objA).value.enumField)->intValue & ASPL_ACCESS(objB).value.enumField->intValue; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_ENUM_OR(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b; + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_ENUM_FIELD; + ASPL_ACCESS(obj).value.enumField = ASPL_MALLOC(sizeof(ASPL_EnumField)); + ASPL_ACCESS(obj).value.enumField->e = (ASPL_ACCESS(objA).value.enumField)->e; + ASPL_ACCESS(obj).value.enumField->intValue = (ASPL_ACCESS(objA).value.enumField)->intValue | ASPL_ACCESS(objB).value.enumField->intValue; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_ENUM_XOR(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b; + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_ENUM_FIELD; + ASPL_ACCESS(obj).value.enumField = ASPL_MALLOC(sizeof(ASPL_EnumField)); + ASPL_ACCESS(obj).value.enumField->e = (ASPL_ACCESS(objA).value.enumField)->e; + ASPL_ACCESS(obj).value.enumField->intValue = (ASPL_ACCESS(objA).value.enumField)->intValue ^ ASPL_ACCESS(objB).value.enumField->intValue; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_PLUS(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b; + ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED; + + if (ASPL_ACCESS(objA).kind == ASPL_OBJECT_KIND_STRING) + { + ASPL_String s = aspl_stringify_rso(objB); + int sLen = s.length; + char* str = ASPL_MALLOC(ASPL_ACCESS(objA).value.string->length + sLen + 1); + memcpy(str, ASPL_ACCESS(objA).value.string->str, ASPL_ACCESS(objA).value.string->length); + memcpy(str + ASPL_ACCESS(objA).value.string->length, s.str, sLen); + str[ASPL_ACCESS(objA).value.string->length + sLen] = '\0'; + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_STRING; + ASPL_ACCESS(result).value.string = ASPL_MALLOC(sizeof(ASPL_String)); + ASPL_ACCESS(result).value.string->str = str; + ASPL_ACCESS(result).value.string->length = ASPL_ACCESS(objA).value.string->length + sLen; + } + else if (ASPL_ACCESS(objB).kind == ASPL_OBJECT_KIND_STRING) + { + ASPL_String s = aspl_stringify_rso(objA); + int sLen = s.length; + char* str = ASPL_MALLOC(ASPL_ACCESS(objB).value.string->length + sLen + 1); + memcpy(str, s.str, sLen); + memcpy(str + sLen, ASPL_ACCESS(objB).value.string->str, ASPL_ACCESS(objB).value.string->length); + str[ASPL_ACCESS(objB).value.string->length + sLen] = '\0'; + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_STRING; + ASPL_ACCESS(result).value.string = ASPL_MALLOC(sizeof(ASPL_String)); + ASPL_ACCESS(result).value.string->str = str; + ASPL_ACCESS(result).value.string->length = ASPL_ACCESS(objB).value.string->length + sLen; + } + else + { + switch (ASPL_ACCESS(objA).kind) + { + case ASPL_OBJECT_KIND_BYTE: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BYTE; + ASPL_ACCESS(result).value.integer8 = ASPL_ACCESS(objA).value.integer8 + ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer8 + ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer8 + ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer8 + ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer8 + ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for + operator"); + } + break; + case ASPL_OBJECT_KIND_INTEGER: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 + ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 + ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer32 + ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer32 + ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer32 + ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for + operator"); + } + break; + case ASPL_OBJECT_KIND_LONG: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 + ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 + ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 + ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer64 + ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer64 + ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for + operator"); + } + break; + case ASPL_OBJECT_KIND_FLOAT: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 + ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 + ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 + ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 + ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float32 + ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for + operator"); + } + break; + case ASPL_OBJECT_KIND_DOUBLE: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 + ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 + ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 + ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 + ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 + ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for + operator"); + } + break; + default: + ASPL_PANIC("Invalid type combination for + operator"); + } + } + + return result; +} + +ASPL_OBJECT_TYPE ASPL_PLUS_PLUS(ASPL_OBJECT_TYPE a) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a; + ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED; + + switch (ASPL_ACCESS(objA).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BYTE; + ASPL_ACCESS(result).value.integer8 = ASPL_ACCESS(objA).value.integer8 + 1; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 + 1; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 + 1; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 + 1; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 + 1; + break; + default: + ASPL_PANIC("Invalid type combination for + operator"); + } + + return result; +} + +ASPL_OBJECT_TYPE ASPL_MINUS(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b; + ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED; + + switch (ASPL_ACCESS(objA).kind) + { + case ASPL_OBJECT_KIND_BYTE: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BYTE; + ASPL_ACCESS(result).value.integer8 = ASPL_ACCESS(objA).value.integer8 - ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer8 - ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer8 - ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer8 - ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer8 - ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for - operator"); + } + break; + case ASPL_OBJECT_KIND_INTEGER: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 - ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 - ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer32 - ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer32 - ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer32 - ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for - operator"); + } + break; + case ASPL_OBJECT_KIND_LONG: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 - ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 - ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 - ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer64 - ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer64 - ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for - operator"); + } + break; + case ASPL_OBJECT_KIND_FLOAT: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 - ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 - ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 - ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 - ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float32 - ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for - operator"); + } + break; + case ASPL_OBJECT_KIND_DOUBLE: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 - ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 - ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 - ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 - ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 - ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for - operator"); + } + break; + default: + ASPL_PANIC("Invalid type combination for - operator"); + } + + return result; +} + +ASPL_OBJECT_TYPE ASPL_MULTIPLY(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b; + ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED; + + switch (ASPL_ACCESS(objA).kind) + { + case ASPL_OBJECT_KIND_BYTE: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BYTE; + ASPL_ACCESS(result).value.integer8 = ASPL_ACCESS(objA).value.integer8 * ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer8 * ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer8 * ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer8 * ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer8 * ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for * operator"); + } + break; + case ASPL_OBJECT_KIND_INTEGER: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 * ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 * ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer32 * ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer32 * ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer32 * ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for * operator"); + } + break; + case ASPL_OBJECT_KIND_LONG: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 * ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 * ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 * ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer64 * ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer64 * ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for * operator"); + } + break; + case ASPL_OBJECT_KIND_FLOAT: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 * ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 * ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 * ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 * ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float32 * ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for * operator"); + } + break; + case ASPL_OBJECT_KIND_DOUBLE: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 * ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 * ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 * ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 * ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 * ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for * operator"); + } + break; + default: + ASPL_PANIC("Invalid type combination for * operator"); + } + + return result; +} + +ASPL_OBJECT_TYPE ASPL_DIVIDE(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b; + ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED; + + switch (ASPL_ACCESS(objA).kind) + { + case ASPL_OBJECT_KIND_BYTE: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BYTE; + ASPL_ACCESS(result).value.integer8 = ASPL_ACCESS(objA).value.integer8 / ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer8 / ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer8 / ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer8 / ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer8 / ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for / operator"); + } + break; + case ASPL_OBJECT_KIND_INTEGER: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 / ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 / ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer32 / ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer32 / ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer32 / ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for / operator"); + } + break; + case ASPL_OBJECT_KIND_LONG: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 / ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 / ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 / ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer64 / ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer64 / ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for / operator"); + } + break; + case ASPL_OBJECT_KIND_FLOAT: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 / (float)ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 / (float)ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float32 / (double)ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 / ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float32 / ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for / operator"); + } + break; + case ASPL_OBJECT_KIND_DOUBLE: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 / (double)ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 / (double)ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 / (double)ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 / (double)ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 / ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for / operator"); + } + break; + default: + ASPL_PANIC("Invalid type combination for / operator"); + } + + return result; +} + +ASPL_OBJECT_TYPE ASPL_MODULO(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b; + ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED; + + switch (ASPL_ACCESS(objA).kind) + { + case ASPL_OBJECT_KIND_BYTE: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BYTE; + ASPL_ACCESS(result).value.integer8 = ASPL_ACCESS(objA).value.integer8 % ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer8 % ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer8 % ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = fmodf(ASPL_ACCESS(objA).value.integer8, ASPL_ACCESS(objB).value.float32); + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = fmod(ASPL_ACCESS(objA).value.integer8, ASPL_ACCESS(objB).value.float64); + break; + default: + ASPL_PANIC("Invalid type combination for \% operator"); + } + break; + case ASPL_OBJECT_KIND_INTEGER: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 % ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 % ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer32 % ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = fmodf(ASPL_ACCESS(objA).value.integer32, ASPL_ACCESS(objB).value.float32); + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = fmod(ASPL_ACCESS(objA).value.integer32, ASPL_ACCESS(objB).value.float64); + break; + default: + ASPL_PANIC("Invalid type combination for \% operator"); + } + break; + case ASPL_OBJECT_KIND_LONG: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 % ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 % ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 % ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = fmodf(ASPL_ACCESS(objA).value.integer64, ASPL_ACCESS(objB).value.float32); + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = fmod(ASPL_ACCESS(objA).value.integer64, ASPL_ACCESS(objB).value.float64); + break; + default: + ASPL_PANIC("Invalid type combination for \% operator"); + } + break; + case ASPL_OBJECT_KIND_FLOAT: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = fmodf(ASPL_ACCESS(objA).value.float32, (float)ASPL_ACCESS(objB).value.integer8); + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = fmodf(ASPL_ACCESS(objA).value.float32, (float)ASPL_ACCESS(objB).value.integer32); + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = fmodf(ASPL_ACCESS(objA).value.float32, (float)ASPL_ACCESS(objB).value.integer64); + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(result).value.float32 = fmodf(ASPL_ACCESS(objA).value.float32, ASPL_ACCESS(objB).value.float32); + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = fmod(ASPL_ACCESS(objA).value.float32, ASPL_ACCESS(objB).value.float64); + break; + default: + ASPL_PANIC("Invalid type combination for \% operator"); + } + break; + case ASPL_OBJECT_KIND_DOUBLE: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = fmod(ASPL_ACCESS(objA).value.float64, (double)ASPL_ACCESS(objB).value.integer8); + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = fmod(ASPL_ACCESS(objA).value.float64, (double)ASPL_ACCESS(objB).value.integer32); + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = fmod(ASPL_ACCESS(objA).value.float64, (double)ASPL_ACCESS(objB).value.integer64); + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = fmod(ASPL_ACCESS(objA).value.float64, (double)ASPL_ACCESS(objB).value.float32); + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(result).value.float64 = fmod(ASPL_ACCESS(objA).value.float64, ASPL_ACCESS(objB).value.float64); + break; + default: + ASPL_PANIC("Invalid type combination for \% operator"); + } + break; + default: + ASPL_PANIC("Invalid type combination for \% operator"); + } + + return result; +} + +ASPL_OBJECT_TYPE ASPL_LESS_THAN(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b; + ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED; + + switch (ASPL_ACCESS(objA).kind) + { + case ASPL_OBJECT_KIND_BYTE: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 < ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 < ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 < ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 < ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 < ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for < operator"); + } + break; + case ASPL_OBJECT_KIND_INTEGER: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 < ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 < ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 < ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 < ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 < ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for < operator"); + } + break; + case ASPL_OBJECT_KIND_LONG: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 < ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 < ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 < ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 < ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 < ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for < operator"); + } + break; + case ASPL_OBJECT_KIND_FLOAT: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 < ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 < ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 < ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 < ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 < ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for < operator"); + } + break; + case ASPL_OBJECT_KIND_DOUBLE: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 < ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 < ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 < ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 < ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 < ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for < operator"); + } + break; + case ASPL_OBJECT_KIND_ENUM_FIELD: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_ENUM_FIELD: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.enumField->intValue < ASPL_ACCESS(objB).value.enumField->intValue; + break; + default: + ASPL_PANIC("Invalid type combination for < operator"); + } + break; + default: + ASPL_PANIC("Invalid type combination for < operator"); + } + + return result; +} + +ASPL_OBJECT_TYPE ASPL_LESS_THAN_OR_EQUAL(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b; + ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED; + + switch (ASPL_ACCESS(objA).kind) + { + case ASPL_OBJECT_KIND_BYTE: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 <= ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 <= ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 <= ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 <= ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 <= ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for <= operator"); + } + break; + case ASPL_OBJECT_KIND_INTEGER: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 <= ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 <= ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 <= ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 <= ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 <= ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for <= operator"); + } + break; + case ASPL_OBJECT_KIND_LONG: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 <= ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 <= ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 <= ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 <= ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 <= ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for <= operator"); + } + break; + case ASPL_OBJECT_KIND_FLOAT: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 <= ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 <= ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 <= ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 <= ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 <= ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for <= operator"); + } + break; + case ASPL_OBJECT_KIND_DOUBLE: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 <= ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 <= ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 <= ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 <= ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 <= ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for <= operator"); + } + break; + case ASPL_OBJECT_KIND_ENUM_FIELD: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_ENUM_FIELD: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.enumField->intValue <= ASPL_ACCESS(objB).value.enumField->intValue; + break; + default: + ASPL_PANIC("Invalid type combination for <= operator"); + } + break; + default: + ASPL_PANIC("Invalid type combination for <= operator"); + } + + return result; +} + +ASPL_OBJECT_TYPE ASPL_GREATER_THAN(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b; + ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED; + + switch (ASPL_ACCESS(objA).kind) + { + case ASPL_OBJECT_KIND_BYTE: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 > ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 > ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 > ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 > ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 > ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for > operator"); + } + break; + case ASPL_OBJECT_KIND_INTEGER: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 > ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 > ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 > ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 > ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 > ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for > operator"); + } + break; + case ASPL_OBJECT_KIND_LONG: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 > ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 > ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 > ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 > ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 > ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for > operator"); + } + break; + case ASPL_OBJECT_KIND_FLOAT: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 > ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 > ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 > ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 > ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 > ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for > operator"); + } + break; + case ASPL_OBJECT_KIND_DOUBLE: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 > ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 > ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 > ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 > ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 > ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for > operator"); + } + break; + case ASPL_OBJECT_KIND_ENUM_FIELD: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_ENUM_FIELD: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.enumField->intValue > ASPL_ACCESS(objB).value.enumField->intValue; + break; + default: + ASPL_PANIC("Invalid type combination for > operator"); + } + break; + default: + ASPL_PANIC("Invalid type combination for > operator"); + } + + return result; +} + +ASPL_OBJECT_TYPE ASPL_GREATER_THAN_OR_EQUAL(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b; + ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED; + + switch (ASPL_ACCESS(objA).kind) + { + case ASPL_OBJECT_KIND_BYTE: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 >= ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 >= ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 >= ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 >= ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 >= ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for >= operator"); + } + break; + case ASPL_OBJECT_KIND_INTEGER: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 >= ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 >= ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 >= ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 >= ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 >= ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for >= operator"); + } + break; + case ASPL_OBJECT_KIND_LONG: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 >= ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 >= ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 >= ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 >= ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 >= ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for >= operator"); + } + break; + case ASPL_OBJECT_KIND_FLOAT: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 >= ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 >= ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 >= ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 >= ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 >= ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for >= operator"); + } + break; + case ASPL_OBJECT_KIND_DOUBLE: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_BYTE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 >= ASPL_ACCESS(objB).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 >= ASPL_ACCESS(objB).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 >= ASPL_ACCESS(objB).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 >= ASPL_ACCESS(objB).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 >= ASPL_ACCESS(objB).value.float64; + break; + default: + ASPL_PANIC("Invalid type combination for >= operator"); + } + break; + case ASPL_OBJECT_KIND_ENUM_FIELD: + switch (ASPL_ACCESS(objB).kind) + { + case ASPL_OBJECT_KIND_ENUM_FIELD: + result = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN; + ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.enumField->intValue >= ASPL_ACCESS(objB).value.enumField->intValue; + break; + default: + ASPL_PANIC("Invalid type combination for >= operator"); + } + break; + default: + ASPL_PANIC("Invalid type combination for >= operator"); + } + + return result; +} + +ASPL_OBJECT_TYPE ASPL_LIST_LENGTH(ASPL_OBJECT_TYPE list) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)list; + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(obj).value.integer32 = ASPL_ACCESS(objA).value.list->length; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_LIST_GET(ASPL_OBJECT_TYPE list, ASPL_OBJECT_TYPE index) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)list; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)index; + ASPL_List* l = ASPL_ACCESS(objA).value.list; + if (ASPL_ACCESS(objB).value.integer32 >= 0 && ASPL_ACCESS(objB).value.integer32 < l->length) + { + return l->value[ASPL_ACCESS(objB).value.integer32]; + } + else { + ASPL_PANIC("Indexing list out of range (index: %d, length: %d)", ASPL_ACCESS(objB).value.integer32, l->length); + } +} + +ASPL_OBJECT_TYPE ASPL_LIST_SET(ASPL_OBJECT_TYPE list, ASPL_OBJECT_TYPE index, ASPL_OBJECT_TYPE value) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)list; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)index; + ASPL_OBJECT_TYPE objC = (ASPL_OBJECT_TYPE)value; + ASPL_List* l = ASPL_ACCESS(objA).value.list; + if (ASPL_ACCESS(objB).value.integer32 >= 0 && ASPL_ACCESS(objB).value.integer32 < l->length) + { + l->value[ASPL_ACCESS(objB).value.integer32] = objC; + } + return list; +} + +ASPL_OBJECT_TYPE ASPL_MAP_LENGTH(ASPL_OBJECT_TYPE map) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)map; + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(obj).value.integer32 = ASPL_ACCESS(objA).value.map->hashmap->len; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_MAP_GET(ASPL_OBJECT_TYPE map, ASPL_OBJECT_TYPE key) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)map; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)key; + ASPL_Object_internal* value = hashmap_aspl_object_to_aspl_object_hashmap_get_value(ASPL_ACCESS(objA).value.map->hashmap, ASPL_HASHMAP_WRAP(objB)); + if (value == NULL) + { + return ASPL_NULL(); + } + return ASPL_HASHMAP_UNWRAP(value); +} + +ASPL_OBJECT_TYPE ASPL_MAP_GET_KEY_FROM_INDEX(ASPL_OBJECT_TYPE map, ASPL_OBJECT_TYPE index) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)map; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)index; + ASPL_Map* m = ASPL_ACCESS(objA).value.map; + if (ASPL_ACCESS(objB).value.integer32 >= 0 && ASPL_ACCESS(objB).value.integer32 < m->hashmap->len) + { + return ASPL_HASHMAP_UNWRAP(m->hashmap->pairs[ASPL_ACCESS(objB).value.integer32]->key); + } + else { + ASPL_PANIC("Indexing map out of range (index: %d, length: %d)", ASPL_ACCESS(objB).value.integer32, m->hashmap->len); + } +} + +ASPL_OBJECT_TYPE ASPL_MAP_GET_VALUE_FROM_INDEX(ASPL_OBJECT_TYPE map, ASPL_OBJECT_TYPE index) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)map; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)index; + ASPL_Map* m = ASPL_ACCESS(objA).value.map; + if (ASPL_ACCESS(objB).value.integer32 >= 0 && ASPL_ACCESS(objB).value.integer32 < m->hashmap->len) + { + return ASPL_HASHMAP_UNWRAP(m->hashmap->pairs[ASPL_ACCESS(objB).value.integer32]->value); + } + else { + ASPL_PANIC("Indexing map out of range (index: %d, length: %d)", ASPL_ACCESS(objB).value.integer32, m->hashmap->len); + } +} + +ASPL_OBJECT_TYPE ASPL_MAP_SET(ASPL_OBJECT_TYPE map, ASPL_OBJECT_TYPE key, ASPL_OBJECT_TYPE value) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)map; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)key; + ASPL_OBJECT_TYPE objC = (ASPL_OBJECT_TYPE)value; + hashmap_aspl_object_to_aspl_object_hashmap_set(ASPL_ACCESS(objA).value.map->hashmap, ASPL_HASHMAP_WRAP(objB), ASPL_HASHMAP_WRAP(objC)); + return map; +} + +ASPL_OBJECT_TYPE ASPL_STRING_LENGTH(ASPL_OBJECT_TYPE string) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)string; + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(obj).value.integer32 = ASPL_ACCESS(objA).value.string->length; + return obj; +} + +ASPL_OBJECT_TYPE ASPL_STRING_INDEX(ASPL_OBJECT_TYPE string, ASPL_OBJECT_TYPE index) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)string; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)index; + char* str = ASPL_ACCESS(objA).value.string->str; + int len = ASPL_ACCESS(objA).value.string->length; + if (ASPL_ACCESS(objB).value.integer32 >= 0 && ASPL_ACCESS(objB).value.integer32 < len) + { + ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_STRING; + ASPL_ACCESS(obj).value.string = ASPL_MALLOC(sizeof(ASPL_String)); + ASPL_ACCESS(obj).value.string->str = ASPL_MALLOC(sizeof(char) * 2); + ASPL_ACCESS(obj).value.string->str[0] = str[ASPL_ACCESS(objB).value.integer32]; + ASPL_ACCESS(obj).value.string->str[1] = '\0'; + ASPL_ACCESS(obj).value.string->length = 1; + return obj; + } + else + { + ASPL_PANIC("Indexing string out of range (index: %d, length: %d)", ASPL_ACCESS(objB).value.integer32, len); + } +} + +ASPL_OBJECT_TYPE ASPL_CAST(ASPL_OBJECT_TYPE obj, char* type) +{ + ASPL_OBJECT_TYPE newObj = ASPL_UNINITIALIZED; + if (strcmp(type, "null") == 0) + { + switch (ASPL_ACCESS(obj).kind) + { + case ASPL_OBJECT_KIND_NULL: + return obj; + default: + ASPL_PANIC("Cannot an object of type %s to type null", aspl_object_get_type_pointer(obj)); + } + } + else if (strcmp(type, "bool") == 0) + { + switch (ASPL_ACCESS(obj).kind) + { + case ASPL_OBJECT_KIND_BOOLEAN: + return obj; + default: + ASPL_PANIC("Cannot cast an object of type %s to type bool", aspl_object_get_type_pointer(obj)); + } + } + else if (strcmp(type, "byte") == 0) + { + switch (ASPL_ACCESS(obj).kind) + { + case ASPL_OBJECT_KIND_BYTE: + return obj; + case ASPL_OBJECT_KIND_INTEGER: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_BYTE; + ASPL_ACCESS(newObj).value.integer8 = ASPL_ACCESS(obj).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_BYTE; + ASPL_ACCESS(newObj).value.integer8 = ASPL_ACCESS(obj).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_BYTE; + ASPL_ACCESS(newObj).value.integer8 = ASPL_ACCESS(obj).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_BYTE; + ASPL_ACCESS(newObj).value.integer8 = ASPL_ACCESS(obj).value.float64; + break; + case ASPL_OBJECT_KIND_STRING: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_BYTE; + ASPL_ACCESS(newObj).value.integer8 = atoi(ASPL_ACCESS(obj).value.string->str); + break; + default: + ASPL_PANIC("Cannot cast an object of type %s to type byte", aspl_object_get_type_pointer(obj)); + } + } + else if (strcmp(type, "int") == 0) + { + switch (ASPL_ACCESS(obj).kind) + { + case ASPL_OBJECT_KIND_BYTE: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(newObj).value.integer32 = ASPL_ACCESS(obj).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + return obj; + case ASPL_OBJECT_KIND_LONG: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(newObj).value.integer32 = ASPL_ACCESS(obj).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(newObj).value.integer32 = ASPL_ACCESS(obj).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(newObj).value.integer32 = ASPL_ACCESS(obj).value.float64; + break; + case ASPL_OBJECT_KIND_ENUM_FIELD: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(newObj).value.integer32 = ASPL_ACCESS(obj).value.enumField->intValue; + break; + case ASPL_OBJECT_KIND_STRING: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_INTEGER; + ASPL_ACCESS(newObj).value.integer32 = atoi(ASPL_ACCESS(obj).value.string->str); + break; + default: + ASPL_PANIC("Cannot cast an object of type %s to type int", aspl_object_get_type_pointer(obj)); + } + } + else if (strcmp(type, "long") == 0) + { + switch (ASPL_ACCESS(obj).kind) + { + case ASPL_OBJECT_KIND_BYTE: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(newObj).value.integer64 = ASPL_ACCESS(obj).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(newObj).value.integer64 = ASPL_ACCESS(obj).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + return obj; + case ASPL_OBJECT_KIND_FLOAT: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(newObj).value.integer64 = ASPL_ACCESS(obj).value.float32; + break; + case ASPL_OBJECT_KIND_DOUBLE: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(newObj).value.integer64 = ASPL_ACCESS(obj).value.float64; + break; + case ASPL_OBJECT_KIND_STRING: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_LONG; + ASPL_ACCESS(newObj).value.integer64 = atoi(ASPL_ACCESS(obj).value.string->str); + break; + default: + ASPL_PANIC("Cannot cast an object of type %s to type long", aspl_object_get_type_pointer(obj)); + } + } + else if (strcmp(type, "float") == 0) + { + switch (ASPL_ACCESS(obj).kind) + { + case ASPL_OBJECT_KIND_BYTE: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(newObj).value.float32 = ASPL_ACCESS(obj).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(newObj).value.float32 = ASPL_ACCESS(obj).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(newObj).value.float32 = ASPL_ACCESS(obj).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + return obj; + case ASPL_OBJECT_KIND_DOUBLE: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(newObj).value.float32 = ASPL_ACCESS(obj).value.float64; + break; + case ASPL_OBJECT_KIND_STRING: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_FLOAT; + ASPL_ACCESS(newObj).value.float32 = atof(ASPL_ACCESS(obj).value.string->str); + break; + default: + ASPL_PANIC("Cannot cast an object of type %s to type float", aspl_object_get_type_pointer(obj)); + } + } + else if (strcmp(type, "double") == 0) + { + switch (ASPL_ACCESS(obj).kind) + { + case ASPL_OBJECT_KIND_BYTE: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(newObj).value.float64 = ASPL_ACCESS(obj).value.integer8; + break; + case ASPL_OBJECT_KIND_INTEGER: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(newObj).value.float64 = ASPL_ACCESS(obj).value.integer32; + break; + case ASPL_OBJECT_KIND_LONG: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(newObj).value.float64 = ASPL_ACCESS(obj).value.integer64; + break; + case ASPL_OBJECT_KIND_FLOAT: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(newObj).value.float64 = ASPL_ACCESS(obj).value.float32; + break; + case ASPL_OBJECT_KIND_STRING: + newObj = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_DOUBLE; + ASPL_ACCESS(newObj).value.float64 = atof(ASPL_ACCESS(obj).value.string->str); + break; + case ASPL_OBJECT_KIND_DOUBLE: + return obj; + default: + ASPL_PANIC("Cannot cast an object of type %s to type double", aspl_object_get_type_pointer(obj)); + } + } + else if (strcmp(type, "string") == 0) + { + return ASPL_STRING_LITERAL(aspl_stringify(obj)); + } + else if (strncmp(type, "list<", 5) == 0) + { + switch (ASPL_ACCESS(obj).kind) + { + case ASPL_OBJECT_KIND_LIST: + return obj; + default: + ASPL_PANIC("Cannot cast an object of type %s to type list", aspl_object_get_type_pointer(obj)); + } + } + else if (strncmp(type, "map<", 4) == 0) + { + switch (ASPL_ACCESS(obj).kind) + { + case ASPL_OBJECT_KIND_MAP: + return obj; + default: + ASPL_PANIC("Cannot cast an object of type %s to type map", aspl_object_get_type_pointer(obj)); + } + } + else if (hashmap_str_to_voidptr_hashmap_contains_key(&enums_map, type)) + { + switch (ASPL_ACCESS(obj).kind) + { + case ASPL_OBJECT_KIND_INTEGER: + return ASPL_ENUM_FIELD_LITERAL(hashmap_str_to_voidptr_hashmap_get_value(&enums_map, type), ASPL_ACCESS(obj).value.integer32); + case ASPL_OBJECT_KIND_ENUM_FIELD: + return obj; + default: + ASPL_PANIC("Cannot cast an object of type %s to an enum", aspl_object_get_type_pointer(obj)); + } + } + else + { + return obj; + } + return newObj; +} + +void aspl_function_print(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* newline) +{ + aspl_util_print("%s", aspl_stringify((ASPL_OBJECT_TYPE)*obj)); + ASPL_OBJECT_TYPE newlineObj = (ASPL_OBJECT_TYPE)*newline; + if (ASPL_IS_TRUE(newlineObj)) + { + aspl_util_print("\n"); + } +} + +ASPL_OBJECT_TYPE aspl_function_input(ASPL_OBJECT_TYPE* prompt) +{ + aspl_util_print("%s", aspl_stringify((ASPL_OBJECT_TYPE)*prompt)); + fflush(stdout); + size_t buffer_size = 1024; + char* str = ASPL_MALLOC(buffer_size); + if (fgets(str, buffer_size, stdin) == NULL) { + ASPL_FREE(str); + return ASPL_STRING_LITERAL(""); + } + size_t len = strlen(str); + if (len > 0 && str[len - 1] == '\n') { + str[len - 1] = '\0'; + } + return ASPL_STRING_LITERAL_NO_COPY(str); +} + +void aspl_function_exit(ASPL_OBJECT_TYPE* code) +{ + ASPL_OBJECT_TYPE codeObj = (ASPL_OBJECT_TYPE)*code; + exit(ASPL_ACCESS(codeObj).value.integer32); +} + +void aspl_function_panic(ASPL_OBJECT_TYPE* obj) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_PANIC(ASPL_ACCESS(objA).value.string->str); +} + +ASPL_OBJECT_TYPE aspl_method_string_toLower(ASPL_OBJECT_TYPE* obj) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + char* str = ASPL_ACCESS(objA).value.string->str; + char* str2 = ASPL_MALLOC(sizeof(char) * (ASPL_ACCESS(objA).value.string->length + 1)); + for (int i = 0; i < ASPL_ACCESS(objA).value.string->length; i++) + { + str2[i] = tolower(str[i]); + } + str2[ASPL_ACCESS(objA).value.string->length] = '\0'; + ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING; + ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String)); + ASPL_ACCESS(s).value.string->str = str2; + ASPL_ACCESS(s).value.string->length = ASPL_ACCESS(objA).value.string->length; + return s; +} + +ASPL_OBJECT_TYPE aspl_method_string_toLower_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_string_toLower(obj); +} + +ASPL_OBJECT_TYPE aspl_method_string_toUpper(ASPL_OBJECT_TYPE* obj) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + char* str = ASPL_ACCESS(objA).value.string->str; + char* str2 = ASPL_MALLOC(sizeof(char) * (ASPL_ACCESS(objA).value.string->length + 1)); + for (int i = 0; i < ASPL_ACCESS(objA).value.string->length; i++) + { + str2[i] = toupper(str[i]); + } + str2[ASPL_ACCESS(objA).value.string->length] = '\0'; + ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING; + ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String)); + ASPL_ACCESS(s).value.string->str = str2; + ASPL_ACCESS(s).value.string->length = ASPL_ACCESS(objA).value.string->length; + return s; +} + +ASPL_OBJECT_TYPE aspl_method_string_toUpper_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_string_toUpper(obj); +} + +ASPL_OBJECT_TYPE aspl_method_string_replace(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* oldValue, ASPL_OBJECT_TYPE* newValue) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*oldValue; + ASPL_OBJECT_TYPE objC = (ASPL_OBJECT_TYPE)*newValue; + char* str = ASPL_ACCESS(objA).value.string->str; + char* str2 = ASPL_ACCESS(objB).value.string->str; + char* str3 = ASPL_ACCESS(objC).value.string->str; + + int lenA = ASPL_ACCESS(objA).value.string->length; + int lenB = ASPL_ACCESS(objB).value.string->length; + int lenC = ASPL_ACCESS(objC).value.string->length; + + int newSize = lenA + 1; + for (int i = 0; i < lenA; i++) + { + if (strncmp(str + i, str2, lenB) == 0) + { + newSize += lenC - lenB; + i += lenB - 1; + } + } + + char* str4 = ASPL_MALLOC(sizeof(char) * newSize); + int i = 0; + int j = 0; + + while (i < lenA) + { + if (strncmp(str + i, str2, lenB) == 0) + { + for (int k = 0; k < lenC; k++) + { + str4[j++] = str3[k]; + } + i += lenB; + } + else + { + str4[j++] = str[i++]; + } + } + + str4[j] = '\0'; + + ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING; + ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String)); + ASPL_ACCESS(s).value.string->str = str4; + ASPL_ACCESS(s).value.string->length = newSize - 1; + return s; +} + +ASPL_OBJECT_TYPE aspl_method_string_replace_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_string_replace(obj, arguments[0], arguments[1]); +} + +ASPL_OBJECT_TYPE aspl_method_string_startsWith(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* prefix) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*prefix; + char* str = ASPL_ACCESS(objA).value.string->str; + char* str2 = ASPL_ACCESS(objB).value.string->str; + return ASPL_BOOL_LITERAL(strncmp(str, str2, ASPL_ACCESS(objB).value.string->length) == 0); +} + +ASPL_OBJECT_TYPE aspl_method_string_startsWith_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_string_startsWith(obj, arguments[0]); +} + +ASPL_OBJECT_TYPE aspl_method_string_endsWith(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* suffix) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*suffix; + char* str = ASPL_ACCESS(objA).value.string->str; + char* str2 = ASPL_ACCESS(objB).value.string->str; + return ASPL_BOOL_LITERAL(ASPL_ACCESS(objA).value.string->length >= ASPL_ACCESS(objB).value.string->length && strncmp(str + ASPL_ACCESS(objA).value.string->length - ASPL_ACCESS(objB).value.string->length, str2, ASPL_ACCESS(objB).value.string->length) == 0); +} + +ASPL_OBJECT_TYPE aspl_method_string_endsWith_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_string_endsWith(obj, arguments[0]); +} + +ASPL_OBJECT_TYPE aspl_method_string_contains(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* substring) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*substring; + char* str = ASPL_ACCESS(objA).value.string->str; + char* str2 = ASPL_ACCESS(objB).value.string->str; + return ASPL_BOOL_LITERAL(strstr(str, str2) != NULL); +} + +ASPL_OBJECT_TYPE aspl_method_string_contains_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_string_contains(obj, arguments[0]); +} + +ASPL_OBJECT_TYPE aspl_method_string_after(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* index) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*index; + char* str = ASPL_ACCESS(objA).value.string->str; + int i = ASPL_ACCESS(objB).value.integer32; + if (i >= -1 && i < (signed int)ASPL_ACCESS(objA).value.string->length) + { + char* str2 = ASPL_MALLOC(sizeof(char) * (ASPL_ACCESS(objA).value.string->length - i)); + for (int j = i + 1; j < ASPL_ACCESS(objA).value.string->length; j++) + { + str2[j - (i + 1)] = str[j]; + } + str2[ASPL_ACCESS(objA).value.string->length - (i + 1)] = '\0'; + ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING; + ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String)); + ASPL_ACCESS(s).value.string->str = str2; + ASPL_ACCESS(s).value.string->length = ASPL_ACCESS(objA).value.string->length - (i + 1); + return s; + } + ASPL_PANIC("string.after(): Indexing string out of range (index: %d, length: %d)", i, ASPL_ACCESS(objA).value.string->length); +} + +ASPL_OBJECT_TYPE aspl_method_string_after_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_string_after(obj, arguments[0]); +} + +ASPL_OBJECT_TYPE aspl_method_string_before(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* index) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*index; + char* str = ASPL_ACCESS(objA).value.string->str; + int i = ASPL_ACCESS(objB).value.integer32; + if (i >= 0 && i <= ASPL_ACCESS(objA).value.string->length) + { + char* str2 = ASPL_MALLOC(sizeof(char) * (i + 1)); + for (int j = 0; j < i; j++) + { + str2[j] = str[j]; + } + str2[i] = '\0'; + ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING; + ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String)); + ASPL_ACCESS(s).value.string->str = str2; + ASPL_ACCESS(s).value.string->length = i; + return s; + } + ASPL_PANIC("string.before(): Indexing string out of range (index: %d, length: %d)", i, ASPL_ACCESS(objA).value.string->length); +} + +ASPL_OBJECT_TYPE aspl_method_string_before_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_string_before(obj, arguments[0]); +} + +char aspl_custom_is_space(char c, ASPL_OBJECT_TYPE* chars) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*chars; + char* str = ASPL_ACCESS(objA).value.string->str; + for (int i = 0; i < ASPL_ACCESS(objA).value.string->length; i++) + { + if (c == str[i]) + { + return 1; + } + } + return 0; +} + +ASPL_OBJECT_TYPE aspl_method_string_trim(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* chars) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + char* str = ASPL_ACCESS(objA).value.string->str; + char* str2 = ASPL_MALLOC(sizeof(char) * (ASPL_ACCESS(objA).value.string->length + 1)); + int i = 0; + int j = ASPL_ACCESS(objA).value.string->length - 1; + while (i < ASPL_ACCESS(objA).value.string->length && aspl_custom_is_space(str[i], chars)) + { + i++; + } + while (j >= 0 && aspl_custom_is_space(str[j], chars)) + { + j--; + } + for (int k = i; k <= j; k++) + { + str2[k - i] = str[k]; + } + str2[j - i + 1] = '\0'; + ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING; + ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String)); + ASPL_ACCESS(s).value.string->str = str2; + ASPL_ACCESS(s).value.string->length = j - i + 1; + return s; +} + +ASPL_OBJECT_TYPE aspl_method_string_trim_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_string_trim(obj, arguments[0]); +} + +ASPL_OBJECT_TYPE aspl_method_string_trimStart(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* chars) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + char* str = ASPL_ACCESS(objA).value.string->str; + char* str2 = ASPL_MALLOC(sizeof(char) * (ASPL_ACCESS(objA).value.string->length + 1)); + int i = 0; + while (i < ASPL_ACCESS(objA).value.string->length && aspl_custom_is_space(str[i], chars)) + { + i++; + } + for (int k = i; k < ASPL_ACCESS(objA).value.string->length; k++) + { + str2[k - i] = str[k]; + } + str2[ASPL_ACCESS(objA).value.string->length - i] = '\0'; + ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING; + ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String)); + ASPL_ACCESS(s).value.string->str = str2; + ASPL_ACCESS(s).value.string->length = ASPL_ACCESS(objA).value.string->length - i; + return s; +} + +ASPL_OBJECT_TYPE aspl_method_string_trimStart_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_string_trimStart(obj, arguments[0]); +} + +ASPL_OBJECT_TYPE aspl_method_string_trimEnd(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* chars) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + char* str = ASPL_ACCESS(objA).value.string->str; + char* str2 = ASPL_MALLOC(sizeof(char) * (ASPL_ACCESS(objA).value.string->length + 1)); + int i = ASPL_ACCESS(objA).value.string->length - 1; + while (i >= 0 && aspl_custom_is_space(str[i], chars)) + { + i--; + } + for (int k = 0; k <= i; k++) + { + str2[k] = str[k]; + } + str2[i + 1] = '\0'; + ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING; + ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String)); + ASPL_ACCESS(s).value.string->str = str2; + ASPL_ACCESS(s).value.string->length = i + 1; + return s; +} + +ASPL_OBJECT_TYPE aspl_method_string_trimEnd_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_string_trimEnd(obj, arguments[0]); +} + +ASPL_OBJECT_TYPE aspl_method_string_split(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* separator, ASPL_OBJECT_TYPE* limit) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*separator; + char* str = ASPL_ACCESS(objA).value.string->str; + char* str2 = ASPL_ACCESS(objB).value.string->str; + ASPL_List* l = ASPL_MALLOC(sizeof(ASPL_List)); + l->length = 0; + l->value = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE)); + + char* str3 = strstr(str, str2); + int remaining_splits = ASPL_ACCESS(*limit).value.integer32 - 1; + + while (str3 != NULL && remaining_splits != 0) + { + l->length++; + l->value = ASPL_REALLOC(l->value, sizeof(ASPL_OBJECT_TYPE) * l->length); + + size_t token_len = str3 - str; + char* token = (char*)ASPL_MALLOC(token_len + 1); + strncpy(token, str, token_len); + token[token_len] = '\0'; + + ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING; + ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String)); + ASPL_ACCESS(s).value.string->str = token; + ASPL_ACCESS(s).value.string->length = token_len; + l->value[l->length - 1] = s; + + str = str3 + ASPL_ACCESS(objB).value.string->length; + str3 = strstr(str, str2); + + remaining_splits--; + } + + if (strlen(str) > 0) // Attention: strlen() has to be used here (because the pointer is updated in the loop above) + { + l->length++; + l->value = ASPL_REALLOC(l->value, sizeof(ASPL_OBJECT_TYPE) * l->length); + + ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING; + ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String)); + ASPL_ACCESS(s).value.string->str = ASPL_MALLOC(strlen(str) + 1); + strcpy(ASPL_ACCESS(s).value.string->str, str); + ASPL_ACCESS(s).value.string->str[strlen(str)] = '\0'; + ASPL_ACCESS(s).value.string->length = strlen(str); + l->value[l->length - 1] = s; + } + + ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_LIST; + ASPL_ACCESS(s).value.list = l; + return s; +} + +ASPL_OBJECT_TYPE aspl_method_string_split_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_string_split(obj, arguments[0], arguments[1]); +} + +ASPL_OBJECT_TYPE aspl_method_string_reverse(ASPL_OBJECT_TYPE* obj) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + char* str = ASPL_ACCESS(objA).value.string->str; + char* str2 = ASPL_MALLOC(sizeof(char) * (ASPL_ACCESS(objA).value.string->length + 1)); + for (int i = 0; i < ASPL_ACCESS(objA).value.string->length; i++) + { + str2[i] = str[ASPL_ACCESS(objA).value.string->length - i - 1]; + } + str2[ASPL_ACCESS(objA).value.string->length] = '\0'; + ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING; + ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String)); + ASPL_ACCESS(s).value.string->str = str2; + ASPL_ACCESS(s).value.string->length = ASPL_ACCESS(objA).value.string->length; + return s; +} + +ASPL_OBJECT_TYPE aspl_method_string_reverse_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_string_reverse(obj); +} + +ASPL_OBJECT_TYPE aspl_method_list_contains(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* value) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*value; + ASPL_List* l = ASPL_ACCESS(objA).value.list; + for (int i = 0; i < l->length; i++) + { + if (ASPL_IS_EQUAL(l->value[i], objB)) + { + return ASPL_TRUE(); + } + } + return ASPL_FALSE(); +} + +ASPL_OBJECT_TYPE aspl_method_list_contains_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_list_contains(obj, arguments[0]); +} + +ASPL_OBJECT_TYPE aspl_method_list_add(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* value) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*value; + ASPL_List* l = ASPL_ACCESS(objA).value.list; + l->length++; + l->value = ASPL_REALLOC(l->value, sizeof(ASPL_OBJECT_TYPE) * l->length); + l->value[l->length - 1] = objB; + return objA; +} + +ASPL_OBJECT_TYPE aspl_method_list_add_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_list_add(obj, arguments[0]); +} + +ASPL_OBJECT_TYPE aspl_method_list_insert(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* index, ASPL_OBJECT_TYPE* value) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*index; + ASPL_OBJECT_TYPE objC = (ASPL_OBJECT_TYPE)*value; + ASPL_List* l = ASPL_ACCESS(objA).value.list; + if (ASPL_ACCESS(objB).value.integer32 >= 0 && ASPL_ACCESS(objB).value.integer32 <= l->length) + { + l->length++; + l->value = ASPL_REALLOC(l->value, sizeof(ASPL_OBJECT_TYPE) * l->length); + for (int i = l->length - 1; i > ASPL_ACCESS(objB).value.integer32; i--) + { + l->value[i] = l->value[i - 1]; + } + l->value[ASPL_ACCESS(objB).value.integer32] = objC; + } + return objA; +} + +ASPL_OBJECT_TYPE aspl_method_list_insert_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_list_insert(obj, arguments[0], arguments[1]); +} + +ASPL_OBJECT_TYPE aspl_method_list_insertElements(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* index, ASPL_OBJECT_TYPE* values) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*index; + ASPL_OBJECT_TYPE objC = (ASPL_OBJECT_TYPE)*values; + ASPL_List* l = ASPL_ACCESS(objA).value.list; + if (ASPL_ACCESS(objB).value.integer32 >= 0 && ASPL_ACCESS(objB).value.integer32 <= l->length) + { + l->length += ASPL_ACCESS(objC).value.list->length; + l->value = ASPL_REALLOC(l->value, sizeof(ASPL_OBJECT_TYPE) * l->length); + for (int i = l->length - 1; i > ASPL_ACCESS(objB).value.integer32; i--) + { + l->value[i] = l->value[i - 1]; + } + for (int i = 0; i < ASPL_ACCESS(objC).value.list->length; i++) + { + l->value[ASPL_ACCESS(objB).value.integer32 + i] = ASPL_ACCESS(objC).value.list->value[i]; + } + } + return objA; +} + +ASPL_OBJECT_TYPE aspl_method_list_insertElements_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_list_insertElements(obj, arguments[0], arguments[1]); +} + +ASPL_OBJECT_TYPE aspl_method_list_remove(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* value) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*value; + ASPL_List* l = ASPL_ACCESS(objA).value.list; + for (int i = 0; i < l->length; i++) + { + if (ASPL_IS_EQUAL(l->value[i], objB)) + { + for (int j = i; j < l->length - 1; j++) + { + l->value[j] = l->value[j + 1]; + } + l->length--; + int mallocSize = sizeof(ASPL_OBJECT_TYPE) * l->length; + if (mallocSize < 1) mallocSize = 1; // realloc can return a null pointer if size is 0, but e.g. memcpy can't handle null pointers (even if the size is 0) + l->value = ASPL_REALLOC(l->value, mallocSize); + return ASPL_TRUE(); + } + } + return ASPL_FALSE(); +} + +ASPL_OBJECT_TYPE aspl_method_list_remove_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_list_remove(obj, arguments[0]); +} + +ASPL_OBJECT_TYPE aspl_method_list_removeAt(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* index) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*index; + ASPL_List* l = ASPL_ACCESS(objA).value.list; + if (ASPL_ACCESS(objB).value.integer32 >= 0 && ASPL_ACCESS(objB).value.integer32 < l->length) + { + for (int i = ASPL_ACCESS(objB).value.integer32; i < l->length - 1; i++) + { + l->value[i] = l->value[i + 1]; + } + l->length--; + int mallocSize = sizeof(ASPL_OBJECT_TYPE) * l->length; + if (mallocSize < 1) mallocSize = 1; // realloc can return a null pointer if size is 0, but e.g. memcpy can't handle null pointers (even if the size is 0) + l->value = ASPL_REALLOC(l->value, mallocSize); + } + return objA; +} + +ASPL_OBJECT_TYPE aspl_method_list_removeAt_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_list_removeAt(obj, arguments[0]); +} + +ASPL_OBJECT_TYPE aspl_method_list_clear(ASPL_OBJECT_TYPE* obj) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_List* l = ASPL_ACCESS(objA).value.list; + // don't free the objects as the GC will take care of it; freeing them here manually may free objects that are still in use + l->length = 0; + l->value = ASPL_REALLOC(l->value, 1); // realloc can return a null pointer if size is 0, but e.g. memcpy can't handle null pointers (even if the size is 0) + return objA; +} + +ASPL_OBJECT_TYPE aspl_method_list_clear_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_list_clear(obj); +} + +ASPL_OBJECT_TYPE aspl_method_list_join(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* separator) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*separator; + ASPL_List* l = ASPL_ACCESS(objA).value.list; + char* str = ASPL_MALLOC(sizeof(char)); + str[0] = '\0'; + for (int i = 0; i < l->length; i++) + { + char* str2 = aspl_stringify(l->value[i]); + str = ASPL_REALLOC(str, sizeof(char) * (strlen(str) + strlen(str2) + ASPL_ACCESS(objB).value.string->length + 1)); + strcat(str, str2); + if (i != l->length - 1) + { + strcat(str, ASPL_ACCESS(objB).value.string->str); + } + } + ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT(); + ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING; + ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String)); + ASPL_ACCESS(s).value.string->str = str; + ASPL_ACCESS(s).value.string->length = strlen(str); + return s; +} + +ASPL_OBJECT_TYPE aspl_method_list_join_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_list_join(obj, arguments[0]); +} + +ASPL_OBJECT_TYPE aspl_method_map_containsKey(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* key) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*key; + ASPL_Map* m = ASPL_ACCESS(objA).value.map; + return ASPL_BOOL_LITERAL(hashmap_aspl_object_to_aspl_object_hashmap_contains_key(m->hashmap, ASPL_HASHMAP_WRAP(objB))); +} + +ASPL_OBJECT_TYPE aspl_method_map_containsKey_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_map_containsKey(obj, arguments[0]); +} + +ASPL_OBJECT_TYPE aspl_method_map_containsValue(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* value) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*value; + ASPL_Map* m = ASPL_ACCESS(objA).value.map; + return ASPL_BOOL_LITERAL(hashmap_aspl_object_to_aspl_object_hashmap_contains_value(m->hashmap, ASPL_HASHMAP_WRAP(objB))); +} + +ASPL_OBJECT_TYPE aspl_method_map_containsValue_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_map_containsValue(obj, arguments[0]); +} + +ASPL_OBJECT_TYPE aspl_method_map_remove(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* key) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*key; + ASPL_Map* m = ASPL_ACCESS(objA).value.map; + hashmap_aspl_object_to_aspl_object_hashmap_remove(m->hashmap, ASPL_HASHMAP_WRAP(objB)); + return objA; +} + +ASPL_OBJECT_TYPE aspl_method_map_remove_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_map_remove(obj, arguments[0]); +} + +ASPL_OBJECT_TYPE aspl_method_map_clear(ASPL_OBJECT_TYPE* obj) +{ + ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj; + ASPL_Map* m = ASPL_ACCESS(objA).value.map; + hashmap_aspl_object_to_aspl_object_hashmap_clear(m->hashmap); + return objA; +} + +ASPL_OBJECT_TYPE aspl_method_map_clear_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[]) +{ + return aspl_method_map_clear(obj); +} + +hashmap_str_to_voidptr_HashMap object_methods_map; + +void aspl_object_method_init(char* class, char* method, ASPL_OBJECT_TYPE (*func)(ASPL_OBJECT_TYPE*, ASPL_OBJECT_TYPE* [])) +{ + unsigned int key = hashmap_str_to_voidptr_hash_key(class); + hashmap_str_to_voidptr_HashMap* methods = hashmap_str_to_voidptr_hashmap_get_value(&object_methods_map, class); + if (methods == NULL) + { + methods = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 1 }); + hashmap_str_to_voidptr_hashmap_set(&object_methods_map, class, methods); + } + hashmap_str_to_voidptr_hashmap_set(methods, method, func); +} + +ASPL_OBJECT_TYPE (*aspl_object_method_get(char* class, char* method))(ASPL_OBJECT_TYPE*, ASPL_OBJECT_TYPE* []) +{ + hashmap_str_to_voidptr_HashMap* methods = hashmap_str_to_voidptr_hashmap_get_value(&object_methods_map, class); + if (methods == NULL) + { + return NULL; + } + return hashmap_str_to_voidptr_hashmap_get_value(methods, method); +} + +ASPL_OBJECT_TYPE aspl_object_method_invoke(ASPL_OBJECT_TYPE base, char* method, ASPL_OBJECT_TYPE* args[]) +{ + if (strcmp(method, "cloneShallow") == 0) + { // TODO: Improve the performance of these + return aspl_object_clone_shallow(base); + } + else if (strcmp(method, "cloneDeep") == 0) + { + return aspl_object_clone_deep(base); + } + + return aspl_object_method_get(aspl_object_get_short_type_pointer(base), method)(C_REFERENCE(base), args); +} + +#ifndef ASPL_NO_MULTITHREADING +void aspl_method_invoke_newthread(ASPL_OBJECT_TYPE base, void* method, ASPL_OBJECT_TYPE* args[], int arg_size) +{ + // no cloneShallow and cloneDeep as it wouldn't make sense to run them on a new thread + ASPL_LAUNCH_THREAD_METHOD(method, C_REFERENCE(base), args, arg_size); +} + +void aspl_object_method_invoke_newthread(ASPL_OBJECT_TYPE base, char* method, ASPL_OBJECT_TYPE* args[], int arg_size) +{ + aspl_method_invoke_newthread(base, aspl_object_method_get(aspl_object_get_short_type_pointer(base), method), args, arg_size); +} +#endif + +void aspl_setup_builtin_method_pointers() +{ + object_methods_map = *hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 1 }); + + aspl_object_method_init("string", "toLower", aspl_method_string_toLower_wrapper); + aspl_object_method_init("string", "toUpper", aspl_method_string_toUpper_wrapper); + aspl_object_method_init("string", "replace", aspl_method_string_replace_wrapper); + aspl_object_method_init("string", "startsWith", aspl_method_string_startsWith_wrapper); + aspl_object_method_init("string", "endsWith", aspl_method_string_endsWith_wrapper); + aspl_object_method_init("string", "contains", aspl_method_string_contains_wrapper); + aspl_object_method_init("string", "after", aspl_method_string_after_wrapper); + aspl_object_method_init("string", "before", aspl_method_string_before_wrapper); + aspl_object_method_init("string", "trim", aspl_method_string_trim_wrapper); + aspl_object_method_init("string", "trimStart", aspl_method_string_trimStart_wrapper); + aspl_object_method_init("string", "trimEnd", aspl_method_string_trimEnd_wrapper); + aspl_object_method_init("string", "split", aspl_method_string_split_wrapper); + aspl_object_method_init("string", "reverse", aspl_method_string_reverse_wrapper); + + aspl_object_method_init("list", "contains", aspl_method_list_contains_wrapper); + aspl_object_method_init("list", "add", aspl_method_list_add_wrapper); + aspl_object_method_init("list", "insert", aspl_method_list_insert_wrapper); + aspl_object_method_init("list", "insertElements", aspl_method_list_insertElements_wrapper); + aspl_object_method_init("list", "remove", aspl_method_list_remove_wrapper); + aspl_object_method_init("list", "removeAt", aspl_method_list_removeAt_wrapper); + aspl_object_method_init("list", "clear", aspl_method_list_clear_wrapper); + aspl_object_method_init("list", "join", aspl_method_list_join_wrapper); + + aspl_object_method_init("map", "containsKey", aspl_method_map_containsKey_wrapper); + aspl_object_method_init("map", "containsValue", aspl_method_map_containsValue_wrapper); + aspl_object_method_init("map", "remove", aspl_method_map_remove_wrapper); + aspl_object_method_init("map", "clear", aspl_method_map_clear_wrapper); +} + +#include + +#define ASPL_MAX_ERROR_HANDLING_DEPTH 1028 + +_Thread_local jmp_buf aspl_error_handling_stack[ASPL_MAX_ERROR_HANDLING_DEPTH]; +_Thread_local int aspl_error_handling_stack_index = -1; +_Thread_local ASPL_OBJECT_TYPE aspl_current_error; + +int aspl_validate_error_handling_depth(int depth){ + if(depth < 0 || depth > ASPL_MAX_ERROR_HANDLING_DEPTH){ + ASPL_PANIC("Error handling depth out of bounds: %d\n", depth); + } + return 1; +} + +#define try if (aspl_validate_error_handling_depth(++aspl_error_handling_stack_index) && !setjmp(aspl_error_handling_stack[aspl_error_handling_stack_index])) + +#define catch else \ + if (aspl_validate_error_handling_depth(--aspl_error_handling_stack_index)) + +#define actually_throw(error) do { \ + if (aspl_validate_error_handling_depth(aspl_error_handling_stack_index)) { \ + aspl_current_error = error; \ + longjmp(aspl_error_handling_stack[aspl_error_handling_stack_index], 1); \ + } \ +} while (0) + +#define return_from_try(value) do { \ + aspl_validate_error_handling_depth(--aspl_error_handling_stack_index); \ + return (value); \ +} while (0) + +#define return_void_from_try do { \ + aspl_validate_error_handling_depth(--aspl_error_handling_stack_index); \ + return; \ +} while (0) + +#define return_uninitialized_from_try do { \ + aspl_validate_error_handling_depth(--aspl_error_handling_stack_index); \ + return ASPL_UNINITIALIZED; \ +} while (0) + +#define throw(value) do { \ + aspl_validate_error_handling_depth(--aspl_error_handling_stack_index); \ + return aspl_current_error = (value); \ +} while (0) + +#define escape(value) do { \ + aspl_current_error = (value); \ + longjmp(aspl_error_handling_stack[aspl_error_handling_stack_index], 1); \ +} while (0) + +ASPL_OBJECT_TYPE ASPL_UNWRAP_RESULT(ASPL_OBJECT_TYPE result) { + if (aspl_object_is_error(result)) { + actually_throw(result); + } + else { + return result; + } +} + +#ifdef _WIN32 +#if !defined(_WIN32_WINNT) || _WIN32_WINNT > 0x0600 +// TODO: Is this really how it should be done? +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 // ensure Windows Vista functions like CreateSymbolicLink are included +#endif +#include +#include + +unsigned int aspl_original_codepage = 0; + +void aspl_restore_windows_console() { + SetConsoleOutputCP(aspl_original_codepage); +} + +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#endif + +void aspl_setup_windows_console() { + aspl_original_codepage = GetConsoleOutputCP(); + atexit(aspl_restore_windows_console); + SetConsoleOutputCP(CP_UTF8); + DWORD mode = 0; + HANDLE osfh = (HANDLE)_get_osfhandle(1); + GetConsoleMode(osfh, &mode); + if (mode > 0) { + SetConsoleMode(osfh, mode | ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + setbuf(stdout, NULL); + } + // TODO: stderr +} +#endif + +void aspl_setup_gc() { + GC_set_pages_executable(0); + GC_INIT(); +#ifndef ASPL_NO_MULTITHREADING + GC_allow_register_threads(); +#endif +} + +#ifdef ASPL_USE_SSL +#ifdef __linux__ +#include "thirdparty/ssl/linux/ssl.h" +#elif __APPLE__ +#include "thirdparty/ssl/macos/ssl.h" +#else +#error SSL is not supported for this target OS +#endif + +void aspl_setup_ssl() { + ssl_init(); +} +#endif + +int aspl_argc; +char** aspl_argv; \ No newline at end of file diff --git a/stdlib/aspl/compiler/main.aspl b/stdlib/aspl/compiler/main.aspl new file mode 100644 index 0000000..34ae4fa --- /dev/null +++ b/stdlib/aspl/compiler/main.aspl @@ -0,0 +1,243 @@ +import aspl.compiler.backend +import aspl.compiler.backend.bytecode.ail +import aspl.compiler.backend.bytecode.twail +import aspl.compiler.backend.stringcode.c +import aspl.compiler.utils +import aspl.parser +import aspl.parser.utils +import io +import os +import appbundle.generator + +$if main{ + import console + + if(os.args().length < 2){ + aspl.parser.utils.syntax_error("Usage: compiler or ") + } + var main = io.abs(os.args()[1]) + compile(main) + //print("Took: " + (Timings:total / 1000) + " seconds") + var string? mainOutFile = null + var tempFile = aspl.compiler.utils.out_temp_name(main) + var tempFileParts = tempFile.replace("\\", "/").split("/") + tempFile = tempFileParts[tempFileParts.length - 1] + if(Options:targetOs == "android"){ + mainOutFile = tempFile + }else{ + var exeFile = aspl.compiler.utils.out_exe_name(main) + var exeParts = exeFile.replace("\\", "/").split("/") + exeFile = exeParts[exeParts.length - 1] + mainOutFile = exeFile + } + print(console.green("Successfully compiled: " + mainOutFile)) +} + +[public] +function compile(string main) returns CompilationResult{ + main = io.abs(main) // if the path remaind relative, below usage (e.g. executable naming) would fail + var string mainDirectory = "" + if(io.exists_file(main)){ + mainDirectory = io.full_directory_path(main) + }elseif(io.exists_directory(main)){ + mainDirectory = main + }else{ + aspl.parser.utils.fatal_error("Main is neither a valid file nor a valid directory: " + main) + } + Module:init(new Module(io.directory_name(mainDirectory), mainDirectory)) + var Backend backend = new AILBytecodeBackend() + if(Options:backend == "twail"){ + backend = new TreeWalkingAILBytecodeBackend() + }elseif(Options:backend == "c"){ + backend = new CBackend() + } + var result = backend.compile(aspl.parser.parse()) + var tempFile = aspl.compiler.utils.out_temp_name(main) + var tempFileParts = tempFile.replace("\\", "/").split("/") + tempFile = tempFileParts[tempFileParts.length - 1] + if(Options:targetOs == "android"){ + var extension = ".ail" + if(Options:backend == "c"){ + extension = ".c" + } + aspl.parser.utils.notice("Direct Android packaging is not yet supported; a " + extension + " file will be generated instead...") + aspl.parser.utils.notice("You can use ASAPP to package the application for Android") + io.write_file_bytes(tempFile, result.output) + }elseif(Options:backend == "twail"){ + aspl.parser.utils.notice("Direct packaging with the tree-walking AIL backend is currently not supported; an .ail file will be generated instead...") + io.write_file_bytes(tempFile, result.output) + }else{ + if(Options:keepTemporary || Options:backend == "c"){ + io.write_file_bytes(tempFile, result.output) + } + var exeFile = aspl.compiler.utils.out_exe_name(main) + var exeParts = exeFile.replace("\\", "/").split("/") + exeFile = exeParts[exeParts.length - 1] + if(Options:backend == "ail"){ + var templateType = "minimal" + if(Options:noCachedTemplate){ + templateType = "none" + }elseif(IncludeUtils:files.length > 0){ + templateType = "full" + } + foreach(IncludeUtils:files as file){ + if(!ModuleUtils:isFilePartOfStdlib(file)){ + templateType = "none" + break + } + } + if(templateType == "none"){ + var mainFile = "#include \"runtime/ailinterpreter/main.c\"\n\n" // TODO: Speed this up using a string builder + foreach(IncludeUtils:files as include){ + mainFile += "#include \"" + include + "\"\n" + } + if(IncludeUtils:files.length > 0){ + mainFile += "\n" + } + foreach(ImplementationCallUtils:usedImplementationCalls as call => argc){ + var callWrapper = "" + if(Options:targetOs == "windows"){ + callWrapper += "__declspec(dllexport) " + } + callWrapper += "ASPL_OBJECT_TYPE aspl_ailinterpreter_implementation_" + call.replace(".", "$") + "(ASPL_AILI_ArgumentList args){\n" + callWrapper += "\treturn ASPL_IMPLEMENT_" + call.replace(".", "$") + "(" + repeat(argc, i = 0){ + callWrapper += "C_REFERENCE(args.arguments[" + i + "])" + if(i < argc - 1){ + callWrapper += ", " + } + } + callWrapper += ");\n" + callWrapper += "}\n\n" + mainFile += callWrapper + } + io.write_file(exeFile + ".c", mainFile) + var ccmd = build_ccmd(exeFile + ".c", exeFile) + ccmd += " -DASPL_AILI_BUNDLED" + if(ImplementationCallUtils:usedImplementationCalls.length > 0){ + // TODO: Figure out a less verbose way than these flags + if(Options:targetOs == "macos"){ + ccmd += " -Wl,-export_dynamic" + }elseif(Options:targetOs != "windows"){ + ccmd += " -export-dynamic" + } + } + if(Options:showCCommand){ + print("cc: " + ccmd) + } + var ccmdResult = os.execute(ccmd) + print(ccmdResult.output, false) + if(ccmdResult.exitCode != 0){ + aspl.parser.utils.fatal_error("C compiler returned with exit code " + ccmdResult.exitCode) + } + if(!Options:keepTemporary){ + io.delete_file(exeFile + ".c") + } + }else{ + var template = aspl.compiler.utils.choose_executable_template(Options:targetOs, Options:targetArchitecture, templateType, Options:guiApp) + if(!io.exists_file(template)) { + aspl.parser.utils.fatal_error("Template file not found: " + template) + } + io.write_file_bytes(exeFile, io.read_file_bytes(template)) + } + if(!Options:internalDoNotBundle){ + os.chmod(exeFile, 509) + var Bundle bundle = new Bundle(exeFile) + bundle.addResource("AIL Code", result.output) + bundle.generate() + } + }elseif(Options:backend == "c"){ + var ccmd = build_ccmd(tempFile, exeFile) + if(Options:showCCommand){ + print("cc: " + ccmd) + } + var ccmdResult = os.execute(ccmd) + print(ccmdResult.output, false) + if(ccmdResult.exitCode != 0){ + aspl.parser.utils.fatal_error("C compiler returned with exit code " + ccmdResult.exitCode) + } + if(!Options:keepTemporary){ + io.delete_file(tempFile) + } + } + } + return result +} + +function build_ccmd(string sourceFile, string outputFile) returns string{ + var ccmd = Options:cCompiler + " " + sourceFile + " -o " + outputFile + if(Options:cCompiler == "zig cc"){ + var architecture = string(Options:targetArchitecture) + if(architecture == "amd64"){ + architecture = "x86_64" + } + ccmd += " -target " + architecture + "-" + Options:targetOs + if(Options:targetOs == "linux" || Options:targetOs == "windows"){ + ccmd += "-gnu" + } + }else{ + // TODO: Cross compilation with other compilers + } + if(Options:targetOs != "windows"){ + ccmd += " -lm" + } + if(!Options:getConditionCompilationSymbols().contains("singlethreaded")){ + if(Options:targetOs == "windows"){ + ccmd += " -lkernel32 -lwinmm" + }else{ + // TODO: The following code looks shady; check it + + // zig cc links to pthread automatically when cross-compiling, but complains when the library is manually specified + if(Options:cCompiler == "zig cc"){ + ccmd += " -lpthread" + }else{ + if(Options:targetOs == "linux"){ // TODO: Isn't this required for all Unix-like systems? + ccmd += " -lpthread" + } + } + } + }else{ + ccmd += " -DASPL_NO_MULTITHREADING" + } + var isObjectiveC = false + foreach(LinkUtils:libraries as library){ + if(library.startsWith("framework:")){ + ccmd += " -framework " + library.after("framework:".length - 1) + isObjectiveC = true + }else{ + ccmd += " -l" + library + } + } + if(isObjectiveC){ + ccmd += " -ObjC" + } + ccmd += " -I " + io.full_directory_path(io.get_executable_path()) + ccmd += " -I " + io.full_directory_path(io.get_executable_path()) + "/thirdparty/libgc/include" + if(Options:production){ + ccmd += " -O3" + }else{ + ccmd += " -g" + ccmd += " -DASPL_DEBUG" + } + if(Options:heapBased){ + ccmd += " -DASPL_HEAP_BASED" + } + if(Options:stackSize != null){ + if(Options:targetOs == "windows" && (Options:cCompiler == "gcc" || Options:cCompiler == "zig cc")){ // TODO: Figure this out for more C compilers and targets + ccmd += " -Wl,--stack," + Options:stackSize + } + } + if(Options:useSsl){ + ccmd += " -DASPL_USE_SSL" + ccmd += " -lssl" + ccmd += " -L " + io.full_directory_path(io.get_executable_path()) + "/thirdparty/ssl/" + Options:targetOs + "/bin" + } + if(Options:targetOs == "windows" && Options:guiApp){ + ccmd += " -Wl,--subsystem,windows" + } + ccmd += " -Wno-return-type" // TODO: Remove this once return statements are enforced in all control flow paths + if(Options:cCompiler == "gcc"){ + ccmd += " -Wno-stringop-overflow" // TODO: Figure out if these warnings are actually false-positives or not + } + return ccmd +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/utils/IncludeUtils.aspl b/stdlib/aspl/compiler/utils/IncludeUtils.aspl new file mode 100644 index 0000000..94bd44b --- /dev/null +++ b/stdlib/aspl/compiler/utils/IncludeUtils.aspl @@ -0,0 +1,19 @@ +[public] +[static] +class IncludeUtils { + + [public] + [static] + [threadlocal] + property list files + + [public] + [static] + method include(string file) { + if(files.contains(file)) { + return + } + files.add(file) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/utils/LinkUtils.aspl b/stdlib/aspl/compiler/utils/LinkUtils.aspl new file mode 100644 index 0000000..01c10a7 --- /dev/null +++ b/stdlib/aspl/compiler/utils/LinkUtils.aspl @@ -0,0 +1,10 @@ +[public] +[static] +class LinkUtils { + + [public] + [static] + [threadlocal] + property list libraries + +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/utils/filenames.aspl b/stdlib/aspl/compiler/utils/filenames.aspl new file mode 100644 index 0000000..8fd012d --- /dev/null +++ b/stdlib/aspl/compiler/utils/filenames.aspl @@ -0,0 +1,64 @@ +import aspl.parser +import io + +[public] +function out_exe_name(string file) returns string{ + var name = "" + if(Options:outputFile == null){ + var parts = file.split(".") // TODO: This will break if the file or directory name contains a dot + var i = 0 + foreach(parts as part){ + if(i != parts.length - 1){ + name += part + "." + } + i++ + } + if(name == ""){ // check if file was a directory + name = io.directory_name(file) + "." // the . is needed below + } + }else{ + name = Options:outputFile + "." // the . is needed below + } + + if(Options:targetOs == "windows"){ + name += "exe" // the . was already added before + }else{ + name = name.before(name.length - 1) // remove the last . + } + if(io.exists_directory(name)){ // the file system doesn't allow files with the same name as directories + var i = 1 + while(io.exists_directory(name + "(" + i + ")") || io.exists_file(name + "(" + i + ")")){ + i++ + } + return name + "(" + i + ")" + } + return name +} + +[public] +function out_temp_name(string file) returns string{ + var name = "" + if(Options:outputFile == null){ + var parts = file.split(".") // TODO: This will break if the file or directory name contains a dot + var i = 0 + foreach(parts as part){ + if(i != parts.length - 1){ + name += part + "." + } + i++ + } + if(name == ""){ // check if file was a directory + name = io.directory_name(file) + "." // the . is needed below + } + }else{ + name = Options:outputFile + "." // the . is needed below + } + if(Options:backend == "ail" || Options:backend == "twail"){ + name += "ail" // the . was already added before + }elseif(Options:backend == "c"){ + name += "c" // the . was already added before + }else{ + name += "temp" // the . was already added before + } + return name +} \ No newline at end of file diff --git a/stdlib/aspl/compiler/utils/templates.aspl b/stdlib/aspl/compiler/utils/templates.aspl new file mode 100644 index 0000000..1878a64 --- /dev/null +++ b/stdlib/aspl/compiler/utils/templates.aspl @@ -0,0 +1,30 @@ +import io +import os +import aspl.parser.utils + +[public] +function choose_executable_template(string os, Architecture arch, string internalTemplateType, bool gui) returns string{ + var baseTemplatePath = io.join_path([io.full_directory_path(io.get_executable_path()), "templates"]) + var archName = architecture_to_folder_name(arch) + if(os == "windows"){ + if(internalTemplateType == "minimal"){ + return io.join_path([baseTemplatePath, "windows", archName, internalTemplateType, "Template.exe"]) + }elseif(gui){ + return io.join_path([baseTemplatePath, "windows", archName, internalTemplateType, "gui", "Template.exe"]) + }else{ + return io.join_path([baseTemplatePath, "windows", archName, internalTemplateType, "cli", "Template.exe"]) + } + }else{ + return io.join_path([baseTemplatePath, os, archName, internalTemplateType, "Template"]) + } +} + +function architecture_to_folder_name(Architecture arch) returns string{ + if(arch == Architecture.amd64){ + return "x86_64" + }elseif(arch == Architecture.i386){ + return "x86_32" + }else{ + return string(arch) + } +} \ No newline at end of file diff --git a/stdlib/aspl/parser/Module.aspl b/stdlib/aspl/parser/Module.aspl new file mode 100644 index 0000000..9894e4b --- /dev/null +++ b/stdlib/aspl/parser/Module.aspl @@ -0,0 +1,42 @@ +[public] +class Module { + + [public] + [static] + [threadlocal] + property map modules = map{} + [public] + [static] + [threadlocal] + property Module mainModule + [readpublic] + [static] + [threadlocal] + property bool initialized = false + + [public] + [static] + method init(Module mainModule){ + self:mainModule = mainModule + self:modules = {self:mainModule.name => self:mainModule} + self:initialized = true + } + + [readpublic] + property string name + [public] + property string id{ + get{ + return name.toLower() + } + } + [readpublic] + property string directory + + [public] + method construct(string name, string directory){ + this.name = name + this.directory = directory + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/Options.aspl b/stdlib/aspl/parser/Options.aspl new file mode 100644 index 0000000..4219bc3 --- /dev/null +++ b/stdlib/aspl/parser/Options.aspl @@ -0,0 +1,166 @@ +import os + +[public] +class Options { + + [readpublic] + [static] + property list SUPPORTED_OS_LIST = [ + "windows", + "linux", + "macos", + "android" + ] + + [static] + [threadlocal] + property string? _targetOs = null + [public] + [static] + property string targetOs{ + get{ + if(self:_targetOs == null){ + self:_targetOs = os.user_os() + } + return string(self:_targetOs) + } + set{ + if(!self:SUPPORTED_OS_LIST.contains(value)){ + aspl.parser.utils.fatal_error("Unsupported OS: " + value) + } + self:_targetOs = value + } + } + [static] + [threadlocal] + property Architecture? _targetArchitecture = null + [public] + [static] + property Architecture targetArchitecture{ + get{ + if(self:_targetArchitecture == null){ + self:_targetArchitecture = os.user_architecture_generic() + } + return Architecture(self:_targetArchitecture) + } + set{ + self:_targetArchitecture = value + } + } + [public] + [static] + [threadlocal] + property string? outputFile = null + [public] + [static] + [threadlocal] + property bool production = false + [public] + [static] + [threadlocal] + property bool keepTemporary = false + [public] + [static] + [threadlocal] + property bool guiApp = false // Windows GUI subsystem + [public] + [static] + [threadlocal] + property list customConditionalCompilationSymbols = [] + [public] + [static] + [threadlocal] + property string backend = "ail" + [public] + [static] + [threadlocal] + property string? _cCompiler = null + [public] + [static] + property string cCompiler{ + get{ + if(self:_cCompiler == null){ + var result = os.execute("zig cc --version") + if (result.exitCode == 0) { + self:_cCompiler = "zig cc" + } else { + result = os.execute("gcc --version") + if (result.exitCode == 0) { + self:_cCompiler = "gcc" + } else { + self:_cCompiler = "cc" + } + } + } + return self:_cCompiler + } + set{ + self:_cCompiler = value + } + } + [public] + [static] + [threadlocal] + property bool useDynamicCTemplate = false + [public] + [static] + [threadlocal] + property bool showCCommand = false + [public] + [static] + [threadlocal] + property bool heapBased = false + [public] + [static] + [threadlocal] + property int? stackSize = null + [public] + [static] + [threadlocal] + property bool useSsl = false + [public] + [static] + [threadlocal] + property bool enableErrorHandling = false + [public] + [static] + [threadlocal] + property bool noCachedTemplate = false + [public] + [static] + [threadlocal] + property string internalTemplateType = "minimal" + [public] + [static] + [threadlocal] + property bool internalDoNotBundle = false + + [public] + [static] + method getConditionCompilationSymbols() returns list{ + var symbols = self:customConditionalCompilationSymbols.cloneShallow() + symbols.add(self:targetOs) + symbols.add(string(targetArchitecture).toLower()) + if(targetArchitecture == Architecture.amd64 || targetArchitecture == Architecture.arm64 || targetArchitecture == Architecture.rv64){ + symbols.add("x64") + }elseif(targetArchitecture == Architecture.i386 || targetArchitecture == Architecture.arm32 || targetArchitecture == Architecture.rv32){ + symbols.add("x32") + } + if(self:production){ + symbols.add("production") + }else{ + symbols.add("debug") + } + if(self:guiApp){ + symbols.add("gui") + }else{ + symbols.add("console") + } + if(self:useSsl){ + symbols.add("ssl") + } + symbols.add(self:backend + "backend") + return symbols + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ParseFileResult.aspl b/stdlib/aspl/parser/ParseFileResult.aspl new file mode 100644 index 0000000..840b0a8 --- /dev/null +++ b/stdlib/aspl/parser/ParseFileResult.aspl @@ -0,0 +1,14 @@ +import aspl.parser.ast + +[public] +class ParseFileResult { + + [readpublic] + property list nodes + + [public] + method construct(list nodes){ + this.nodes = nodes + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/Parser.aspl b/stdlib/aspl/parser/Parser.aspl new file mode 100644 index 0000000..abc2b71 --- /dev/null +++ b/stdlib/aspl/parser/Parser.aspl @@ -0,0 +1,3333 @@ +import aspl.parser.lexer +import aspl.parser.ast +import aspl.parser.ast.expressions +import aspl.parser.ast.statements +import aspl.parser.ast.literals +import aspl.parser.utils +import aspl.parser.functions +import aspl.parser.precedence +import aspl.parser.variables +import aspl.parser.methods +import aspl.parser.properties +import aspl.parser.classes +import aspl.parser.enums +import aspl.parser.attributes +import aspl.parser.callbacks +import math +import io + +[public] +class Parser{ + + [public] + property Module module + [public] + property string file + [public] + property string currentNamespace{ + get{ + var modulePath = module.directory + if(modulePath.endsWith("/") || modulePath.endsWith("\\")){ + modulePath = modulePath.before(modulePath.length - 1) + } + var relativePath = io.full_directory_path(io.abs(file)).after(modulePath.length - 1).replace("/", ".").replace("\\", ".") + if(relativePath.startsWith(".")){ + relativePath = relativePath.after(0) + } + if(relativePath.endsWith(".")){ + relativePath = relativePath.before(relativePath.length - 1) + } + var namespace = io.directory_name(module.directory) + "." + relativePath + while(namespace.endsWith(".")){ + namespace = namespace.before(namespace.length - 1) + } + return namespace.toLower() + } + } + [public] + property Class? currentClass = null + property bool inStaticContext = false + [public] + property Enum? currentEnum = null + [public] + property Method? currentMethod = null + [public] + property list attributeCache = list[] + [static] + [threadlocal] + property map importTables = map{} + [public] + property ImportTable importTable{ + get{ + if(!self:importTables.containsKey(file)){ + self:importTables[file] = new ImportTable() + } + return self:importTables[file] + } + set{ + self:importTables[file] = value + } + } + property int loopDepth = 0 + + [static] + [threadlocal] + property bool initialized = false + [readpublic] + [static] + [threadlocal] + property list importProcessedFiles = [] + + [readpublic] + property ParseMode? currentParseMode = null + + [public] + method construct(Module module, string file){ + if(!Module:initialized){ + aspl.parser.utils.fatal_error("Cannot construct a parser without initializing the modules first (parser.Module:init())") + } + this.module = module + this.file = file + if(!self:initialized){ + self:initialized = true + Attribute:init() + GenericsUtils:init() + Function:init() + Method:init() + Property:init() + } + } + + [public] + method parse(ParseMode parseMode = ParseMode.Normal) returns ParseFileResult{ + currentParseMode = parseMode + + var TokenList tokens = new TokenList(list[]) + if(Lexer:cache.containsKey(this.file)){ + tokens = Lexer:cache[this.file].clone() + }else{ + var Lexer lexer = new Lexer(this.file) + tokens = new TokenList(lexer.lex()) + Lexer:cache[this.file] = tokens + } + Scope:pushBundle(null) + Scope:push() + var list nodes = [] + if(parseMode == ParseMode.Import){ + while(true){ + if(tokens.length == 0){ + break + } + var token = tokens.next() + if(token.type == TokenType.Identifier && token.value == "import"){ + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected identifier after import", tokens.peek().location) + } + var string namespace = parseTypeIdentifier(tokens).identifier + + var string module = namespace.split(".", 2)[0] + var path = ModuleUtils:getModulePath(module) + if(io.exists_directory(path)){ + module = io.directory_name(path) + } + var moduleFound = true + if(!Module:modules.containsKey(module)){ + if(io.exists_directory(path)){ + var m = new Module(module, path) + Module:modules[module] = m + foreach(DirectoryUtils:index(m.directory) as file){ + if(!self:importProcessedFiles.contains(file)){ + self:importProcessedFiles.add(file) + var Parser parser = new Parser(m, file) + parser.parse(ParseMode.Import) + } + } + }else{ + aspl.parser.utils.generic_error("Module '" + module + "' not found", tokens.peek().location) + moduleFound = false + } + } + + if(moduleFound){ + var namespaceWithoutModule = "" + if(namespace.split(".", 2).length > 1){ + namespaceWithoutModule = namespace.split(".", 2)[1] + } + if(!io.exists_directory(Module:modules[module].directory + "/" + namespaceWithoutModule.replace(".", "/"))){ + aspl.parser.utils.generic_error("Namespace '" + namespace + "' not found", tokens.peek().location) + }else{ + importTable.importNamespace(namespace) + } + } + } + } + }elseif(parseMode == ParseMode.PreprocessTypes){ + preProcessTypes(tokens) + }elseif(parseMode == ParseMode.Preprocess){ + preProcess(tokens) + }elseif(parseMode == ParseMode.Normal){ + while(true){ + if(tokens.length == 0){ + break + } + var node = parseToken(tokens.next(), tokens, true) + if(!(node oftype NopStatement)){ + nodes.add(node) + } + } + } + Scope:pop() + Scope:popBundle() + currentParseMode = null + return new ParseFileResult(nodes) + } + + method preProcessTypes(TokenList tokens){ + while(true){ + if(tokens.length == 0){ + break + } + preProcessTypesToken(tokens.next(), tokens) + } + } + + method preProcessTypesToken(Token token, TokenList tokens){ + if(token.type == TokenType.Identifier){ + if(token.value == "class"){ + if(currentClass != null){ + aspl.parser.utils.syntax_error("Cannot declare a class inside another class", token.location) + } + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected class name after 'class'", tokens.peek().location) + } + var name = tokens.next().value + var type = Type:fromString(currentNamespace + "." + name, this, token.location) + var c = new Class(type, null, null, null, module, token.location) + Class:classes[type.toString()] = c + }elseif(token.value == "enum"){ + if(currentEnum != null){ + aspl.parser.utils.syntax_error("Cannot declare an enum inside another enum", token.location) + } + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected enum name after 'enum'", tokens.peek().location) + } + var name = tokens.next().value + if(tokens.peek().type != TokenType.BraceOpen){ + aspl.parser.utils.syntax_error("Expected '{' after enum name", tokens.peek().location) + } + tokens.shift() + var type = Type:fromString(currentNamespace + "." + name, this, token.location) + var e = new Enum(type, null, null, module, token.location) + Enum:enums[type.toString()] = e + } + } + } + + method preProcess(TokenList tokens){ + while(true){ + if(tokens.length == 0){ + break + } + preProcessToken(tokens.next(), tokens) + } + } + + method preProcessToken(Token token, TokenList tokens){ + if(token.type == TokenType.Identifier){ + if(token.value == "function"){ + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected identifier after 'function'", tokens.peek().location) + } + var identifier = tokens.next() + var list parameters = [] + tokens.shift() + while(true){ + if(tokens.peek().type == TokenType.ParenthesisClose){ + tokens.shift() + break + } + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected identifier after '('", tokens.peek().location) + } + var types = parseTypesIfAny(tokens) + if(types.types.length == 0){ + if(tokens.peek(1).type == TokenType.Identifier){ + aspl.parser.utils.fatal_error("Invalid parameter type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) // TODO: Use type_error when parseTypesIfAny for unknown types exists + }else{ + aspl.parser.utils.syntax_error("Parameters must specify a type", tokens.peek().location) + } + } + var parameter = tokens.next() + var Expression? defaultValue = null + if(tokens.peek().type == TokenType.Equals){ + tokens.shift() + defaultValue = new NullLiteral(tokens.peek().location) + TokenUtils:skipTokensTillSeparator(tokens) + } + parameters.add(new Parameter(parameter.value, types, defaultValue, token.location)) + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + }else{ + if(tokens.peek().type == TokenType.ParenthesisClose){ + tokens.shift() + break + } + aspl.parser.utils.syntax_error("Expected ',' or ')'", tokens.peek().location) + } + } + var returnTypes = new Types([]) + if(tokens.peek().value == "returns"){ + tokens.shift() + returnTypes = parseTypesIfAny(tokens) + if(returnTypes.types.length == 0){ + if(tokens.peek(1).type == TokenType.BraceOpen){ + aspl.parser.utils.syntax_error("Invalid return type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) + }else{ + aspl.parser.utils.syntax_error("Expected type identifier after 'returns'", tokens.peek().location) + } + } + } + var braceOpen = tokens.next() + if(braceOpen.type != TokenType.BraceOpen){ + aspl.parser.utils.syntax_error("Expected '{' after function parameters", braceOpen.location) + } + foreach(attributeCache as attribute){ + if((attribute.attribute.usage && AttributeUsage.Function) == 0){ + aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used on functions", attribute.location) + } + foreach(attributeCache as other){ + if(other.attribute.identifier != attribute.attribute.identifier){ + if(!attribute.attribute.canPair(other.attribute)){ + aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used together with the attribute '" + other.attribute.identifier + "'", attribute.location) + } + } + } + } + var f = new CustomFunction(IdentifierUtils:relativeToAbsoluteIdentifier(this, identifier.value), parameters, returnTypes, attributeCache.cloneShallow(), null, module, token.location, braceOpen.location) + attributeCache.clear() + while(true){ + if(tokens.peek().type == TokenType.BraceClose){ + tokens.shift() + break + } + preProcessToken(tokens.next(), tokens) + } + f.register(token.location) + }elseif(token.value == "method"){ + if(currentClass == null){ + aspl.parser.utils.syntax_error("Cannot declare a method outside of a class", token.location) + } + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected identifier after 'method'", tokens.peek().location) + } + var name = tokens.next() + if(Class(currentClass).isStatic && name.value == "construct"){ + aspl.parser.utils.type_error("Static class " + Class(currentClass).type.toString() + " cannot have a constructor", token.location) + } + var list parameters = [] + tokens.shift() + while(true){ + if(tokens.peek().type == TokenType.ParenthesisClose){ + tokens.shift() + break + } + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected identifier after '('", tokens.peek().location) + } + var types = parseTypesIfAny(tokens) + if(types.types.length == 0){ + if(tokens.peek(1).type == TokenType.Identifier){ + aspl.parser.utils.fatal_error("Invalid parameter type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) // TODO: Use type_error when parseTypesIfAny for unknown types exists + }else{ + aspl.parser.utils.syntax_error("Parameters must specify a type", tokens.peek().location) + } + } + var parameter = tokens.next() + var Expression? defaultValue = null + if(tokens.peek().type == TokenType.Equals){ + tokens.shift() + defaultValue = new NullLiteral(tokens.peek().location) + TokenUtils:skipTokensTillSeparator(tokens) + } + parameters.add(new Parameter(parameter.value, types, defaultValue, token.location)) + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + }else{ + if(tokens.peek().type == TokenType.ParenthesisClose){ + tokens.shift() + break + } + aspl.parser.utils.syntax_error("Expected ',' or ')'", tokens.peek().location) + } + } + var returnTypes = new Types([]) + if(tokens.peek().value == "returns"){ + tokens.shift() + returnTypes = parseTypesIfAny(tokens) + if(returnTypes.types.length == 0){ + if(tokens.peek(1).type == TokenType.BraceOpen){ + aspl.parser.utils.fatal_error("Invalid return type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) // TODO: Use type_error when parseTypesIfAny for unknown types exists + }else{ + aspl.parser.utils.syntax_error("Expected type identifier after 'returns'", tokens.peek().location) + } + } + } + var braceOpen = tokens.peek() + foreach(attributeCache as attribute){ + if((attribute.attribute.usage && AttributeUsage.Method) == 0){ + aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used on methods", attribute.location) + }elseif(attribute.attribute.identifier == "abstract" && !Class(currentClass).isAbstract){ + aspl.parser.utils.type_error("Only methods in abstract classes can marked with 'abstract' attribute", attribute.location) + } + foreach(attributeCache as other){ + if(other.attribute.identifier != attribute.attribute.identifier){ + if(!attribute.attribute.canPair(other.attribute)){ + aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used together with the attribute '" + other.attribute.identifier + "'", attribute.location) + } + } + } + } + var m = new CustomMethod(Class(currentClass).type, name.value, parameters, returnTypes, attributeCache.cloneShallow(), null, token.location, braceOpen.location) + if(Class(currentClass).isStatic && !m.isStatic){ + aspl.parser.utils.type_error("Cannot declare a non-static method in a static class", token.location) + } + attributeCache.clear() + if(tokens.peek().type == TokenType.BraceOpen){ + if(m.isAbstract){ + aspl.parser.utils.syntax_error("Abstract methods cannot have a body", tokens.peek().location) + } + preProcessBlock(tokens) + }elseif(!m.isAbstract){ + aspl.parser.utils.syntax_error("Non-abstract methods must have a body", tokens.peek().location) + } + m.register(token.location) + }elseif(token.value == "property"){ + if(currentClass == null){ + aspl.parser.utils.syntax_error("Cannot declare a property outside of a class", token.location) + } + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected property name after 'property'", tokens.peek().location) + } + var types = parseTypesIfAny(tokens) + var name = tokens.next() + if(types.types.length == 0){ + aspl.parser.utils.warning("Expected type after property name in 'property' statement; will use 'any' for now", name.location) + types = new Types([Type:fromString("any")]) + } + foreach(attributeCache as attribute){ + if((attribute.attribute.usage && AttributeUsage.Property) == 0){ + aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used on properties", attribute.location) + }elseif(attribute.attribute.identifier == "threadlocal" && tokens.peek().type == TokenType.BraceOpen){ + aspl.parser.utils.type_error("The attribute 'threadlocal' cannot be used on reactive properties", attribute.location) + } + foreach(attributeCache as other){ + if(other.attribute.identifier != attribute.attribute.identifier){ + if(!attribute.attribute.canPair(other.attribute)){ + aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used together with the attribute '" + other.attribute.identifier + "'", attribute.location) + } + } + } + } + var Property? p = null + var potentialBraceOpen = tokens.peek() + if(potentialBraceOpen.type == TokenType.BraceOpen){ + tokens.shift() + if(tokens.peek().type == TokenType.Identifier){ + if(tokens.peek().value == "get"){ + tokens.shift() + preProcessBlock(tokens) + } + if(tokens.peek().value == "set"){ + tokens.shift() + preProcessBlock(tokens) + } + } + tokens.shift() + p = new CustomReactiveProperty(Class(currentClass).type, name.value, types, attributeCache.cloneShallow(), null, null, token.location, potentialBraceOpen.location) + }else{ + p = new CustomNormalProperty(Class(currentClass).type, name.value, types, attributeCache.cloneShallow(), null, token.location, new Location(name.location.file, name.location.endLine, name.location.endLine, name.location.endColumn, name.location.endColumn + 1)) + } + if(Class(currentClass).isStatic && !Property(p).isStatic){ + aspl.parser.utils.type_error("Cannot declare a non-static property in a static class", token.location) + } + attributeCache.clear() + Property(p).register(token.location) + } + elseif(token.value == "class"){ + if(currentClass != null){ + aspl.parser.utils.syntax_error("Cannot declare a class inside another class", token.location) + } + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected class name after 'class'", tokens.peek().location) + } + var name = tokens.next().value + var list parents = [] + if(tokens.peek().value == "extends"){ + tokens.shift() + while(tokens.peek().type != TokenType.BraceOpen){ + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected class name after 'extends'", tokens.peek().location) + } + var t = tokens.peek() + var parent = parseTypeIdentifier(tokens).identifier + parents.add(Type:fromString(parent, this, t.location)) + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + } + } + } + if(tokens.peek().type != TokenType.BraceOpen){ + aspl.parser.utils.syntax_error("Expected '{' after class name", tokens.peek().location) + } + foreach(attributeCache as attribute){ + if((attribute.attribute.usage && AttributeUsage.Class) == 0){ + aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used on classes", attribute.location) + } + foreach(attributeCache as other){ + if(other.attribute.identifier != attribute.attribute.identifier){ + if(!attribute.attribute.canPair(other.attribute)){ + aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used together with the attribute '" + other.attribute.identifier + "'", attribute.location) + } + } + } + } + var type = Type:fromString(currentNamespace + "." + name, this, token.location) + var c = new Class(type, parents, attributeCache.cloneShallow(), null, module, token.location) + attributeCache.clear() + Class:classes[type.toString()] = c + ClassUtils:classesWithParsers[type.toString()] = this + currentClass = c + preProcessBlock(tokens) + currentClass = null + }elseif(token.value == "enum"){ + if(currentEnum != null){ + aspl.parser.utils.syntax_error("Cannot declare an enum inside another enum", token.location) + } + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected enum name after 'enum'", tokens.peek().location) + } + var name = tokens.next().value + if(tokens.peek().type != TokenType.BraceOpen){ + aspl.parser.utils.syntax_error("Expected '{' after enum name", tokens.peek().location) + } + tokens.shift() + foreach(attributeCache as attribute){ + if((attribute.attribute.usage && AttributeUsage.Enum) == 0){ + aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used on enums", attribute.location) + } + foreach(attributeCache as other){ + if(other.attribute.identifier != attribute.attribute.identifier){ + if(!attribute.attribute.canPair(other.attribute)){ + aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used together with the attribute '" + other.attribute.identifier + "'", attribute.location) + } + } + } + } + var type = Type:fromString(currentNamespace + "." + name, this, token.location) + var e = new Enum(type, attributeCache.cloneShallow(), null, module, token.location) + attributeCache.clear() + Enum:enums[type.toString()] = e + currentEnum = e + var map fields = {} + while(true){ + var field = tokens.next() + if(field.type == TokenType.BraceClose){ + e.location = new Location(token.location.file, token.location.startLine, field.location.endLine, token.location.startColumn, field.location.endColumn) + break + } + if(AttributeUtils:parseAttributesIfAny(this, field, tokens)){ + field = tokens.next() + } + if(field.type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected enum field name", field.location) + } + foreach(attributeCache as attribute){ + if((attribute.attribute.usage && AttributeUsage.EnumField) == 0){ + aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used on enum fields", attribute.location) + } + foreach(attributeCache as other){ + if(other.attribute.identifier != attribute.attribute.identifier){ + if(!attribute.attribute.canPair(other.attribute)){ + aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used together with the attribute '" + other.attribute.identifier + "'", attribute.location) + } + } + } + } + if(tokens.peek().type == TokenType.Equals){ + tokens.shift() + TokenUtils:skipTokensTillSeparator(tokens) + } + fields[field.value] = new EnumField(e, field.value, null, attributeCache, token.location) + attributeCache.clear() + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + }elseif(tokens.peek().type != TokenType.BraceClose){ + aspl.parser.utils.syntax_error("Expected ',' or '}' after enum field", tokens.peek().location) + } + } + e.fields = fields + currentEnum = null + } + }elseif(token.type == TokenType.BracketOpen){ + if(tokens.peek().type == TokenType.Identifier){ + var attributePeek = peekTypeIdentifier(tokens) + var isAttribute = true + if((tokens.peek(attributePeek.tokenCount).type == TokenType.BracketClose) || (tokens.peek(attributePeek.tokenCount).type == TokenType.ParenthesisOpen)){ + if(!Attribute:exists(attributePeek.identifier)){ + isAttribute = false + } + }else{ + isAttribute = false + } + if(isAttribute){ + var attribute = Attribute:get(IdentifierUtils:handleTypeIdentifier(parseTypeIdentifier(tokens).identifier)) + var list arguments = [] + if(tokens.peek().type == TokenType.ParenthesisOpen){ + tokens.shift() + foreach(attribute.parameters as parameter){ + if(tokens.peek().type == TokenType.ParenthesisClose){ + if(!parameter.optional){ + aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for attribute '" + attribute.identifier + "' (expected " + attribute.minimumParameterCount + ")", tokens.peek().location) + } + break + } + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens)) + if(!Type:matches(parameter.types, value.getType())){ + aspl.parser.utils.type_error("Cannot pass a value of the type '" + value.getType().toString() + "' to a parameter of the type '" + parameter.types.toString() + "'", value.location) + } + arguments.add(value) + if(arguments.length < attribute.parameters.length && tokens.peek().type == TokenType.Comma){ + tokens.shift() + } + } + if(tokens.peek().type == TokenType.ParenthesisClose){ + tokens.shift() + }else{ + aspl.parser.utils.syntax_error("Expected ')' after attribute arguments", tokens.peek().location) + } + }elseif(attribute.parameters.length > 0 && !attribute.parameters[0].optional){ + aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for attribute '" + attribute.identifier + "' (expected " + attribute.minimumParameterCount + ")", tokens.peek().location) + } + if(tokens.peek().type == TokenType.BracketClose){ + tokens.shift() + }else{ + aspl.parser.utils.syntax_error("Expected ']' after attribute name", tokens.peek().location) + } + this.attributeCache.add(new AttributeInstance(attribute, arguments, token.location, list(token.comments))) + } + } + } + } + + [public] + method parseToken(Token token, TokenList tokens, bool standalone = false, PrecedenceLevel precedenceLevel = PrecedenceLevel.None, Expression? previousExpression = null, Types? expectedTypes = null) returns Node{ + if(token.type == TokenType.Byte){ + if(token.value.endsWith("b")){ + return applyOperators(new ByteLiteral(byte(token.value.before(token.value.length - 1)), token.location), tokens, precedenceLevel) + }else{ + if(expectedTypes != null){ + if(Type:matches(Types(expectedTypes), new Types([Type:fromString("byte")]))){ + return applyOperators(new ByteLiteral(byte(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("integer")]))){ + return applyOperators(new IntegerLiteral(int(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("long")]))){ + return applyOperators(new LongLiteral(long(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("float")]))){ + return applyOperators(new FloatLiteral(float(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("double")]))){ + return applyOperators(new DoubleLiteral(double(token.value), token.location), tokens, precedenceLevel) + } + } + return applyOperators(new ByteLiteral(byte(token.value), token.location), tokens, precedenceLevel) + } + }elseif(token.type == TokenType.Integer){ + if(expectedTypes != null){ + if(Type:matches(Types(expectedTypes), new Types([Type:fromString("integer")]))){ + return applyOperators(new IntegerLiteral(int(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("byte")]))){ + return applyOperators(new ByteLiteral(byte(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("long")]))){ + return applyOperators(new LongLiteral(long(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("float")]))){ + return applyOperators(new FloatLiteral(float(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("double")]))){ + return applyOperators(new DoubleLiteral(double(token.value), token.location), tokens, precedenceLevel) + } + } + return applyOperators(new IntegerLiteral(int(token.value), token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.Long){ + if(token.value.endsWith("l")){ + return applyOperators(new LongLiteral(long(token.value.before(token.value.length - 1)), token.location), tokens, precedenceLevel) + }else{ + if(expectedTypes != null){ + if(Type:matches(Types(expectedTypes), new Types([Type:fromString("long")]))){ + return applyOperators(new LongLiteral(long(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("byte")]))){ + return applyOperators(new ByteLiteral(byte(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("integer")]))){ + return applyOperators(new IntegerLiteral(int(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("float")]))){ + return applyOperators(new FloatLiteral(float(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("double")]))){ + return applyOperators(new DoubleLiteral(double(token.value), token.location), tokens, precedenceLevel) + } + } + return applyOperators(new LongLiteral(long(token.value), token.location), tokens, precedenceLevel) + } + }elseif(token.type == TokenType.Float){ + if(token.value.endsWith("f")){ + return applyOperators(new FloatLiteral(float(token.value.before(token.value.length - 1)), token.location), tokens, precedenceLevel) + }else{ + if(expectedTypes != null){ + if(Type:matches(Types(expectedTypes), new Types([Type:fromString("float")]))){ + return applyOperators(new FloatLiteral(float(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("byte")]))){ + return applyOperators(new ByteLiteral(byte(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("integer")]))){ + return applyOperators(new IntegerLiteral(int(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("long")]))){ + return applyOperators(new LongLiteral(long(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("double")]))){ + return applyOperators(new DoubleLiteral(double(token.value), token.location), tokens, precedenceLevel) + } + } + return applyOperators(new FloatLiteral(float(token.value), token.location), tokens, precedenceLevel) + } + }elseif(token.type == TokenType.Double){ + if(token.value.endsWith("d")){ + return applyOperators(new DoubleLiteral(double(token.value.before(token.value.length - 1)), token.location), tokens, precedenceLevel) + }else{ + if(expectedTypes != null){ + if(Type:matches(Types(expectedTypes), new Types([Type:fromString("double")]))){ + return applyOperators(new DoubleLiteral(double(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("byte")]))){ + return applyOperators(new ByteLiteral(byte(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("integer")]))){ + return applyOperators(new IntegerLiteral(int(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("long")]))){ + return applyOperators(new LongLiteral(long(token.value), token.location), tokens, precedenceLevel) + }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("float")]))){ + return applyOperators(new FloatLiteral(float(token.value), token.location), tokens, precedenceLevel) + } + } + return applyOperators(new DoubleLiteral(double(token.value), token.location), tokens, precedenceLevel) + } + }elseif(token.type == TokenType.String){ + return applyOperators(new StringLiteral(token.value, StringToken(token).literalString, token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.Identifier){ + if(token.value == "import"){ + parseTypeIdentifier(tokens) // ignore the import statement as it has already been processed + return new NopStatement() + } + elseif(token.value == "null"){ + return applyOperators(new NullLiteral(token.location), tokens, precedenceLevel) + }elseif(token.value == "false"){ + return applyOperators(new BooleanLiteral(false, token.location), tokens, precedenceLevel) + }elseif(token.value == "true"){ + return applyOperators(new BooleanLiteral(true, token.location), tokens, precedenceLevel) + }elseif(token.value == "assert"){ + var expression = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("bool")]))) + if(!Type:matches(new Types([Type:fromString("bool")]), expression.getType())){ + aspl.parser.utils.type_error("Condition in assert statement must be of type boolean, but expression of type '" + expression.getType().toString() + "' given", token.location) + } + return new AssertStatement(expression, token.location) + }elseif(token.value == "var"){ + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected identifier after 'var'", tokens.peek().location) + } + var types = parseTypesIfAny(tokens) + var identifier = tokens.next() + if(tokens.peek().type != TokenType.Equals){ + if(tokens.peek().type == TokenType.Identifier){ + aspl.parser.utils.fatal_error("Unknown type '" + identifier.value + "'", identifier.location) // TODO: Use type_error when parseTypesIfAny for unknown types exists + } + aspl.parser.utils.syntax_error("Expected '=' after variable declaration", tokens.peek().location) + } + tokens.shift() + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, types)) + if(types.types.length == 0){ + types = value.getType() + }else{ + if(!Type:matches(types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a variable of type '" + types.toString() + "'", value.location) + } + } + var v = Variable:register(identifier.value, types, token.location) + return applyOperators(new VariableDeclareExpression(v, value, token.location), tokens, precedenceLevel) + }elseif(token.value == "function"){ + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected identifier after 'function'", tokens.peek().location) + } + var identifier = tokens.next() + Scope:pushBundle(null) + Scope:push() + var list parameters = [] + tokens.shift() + while(true){ + if(tokens.peek().type == TokenType.ParenthesisClose){ + tokens.shift() + break + } + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected identifier after '('", tokens.peek().location) + } + var types = parseTypesIfAny(tokens) + if(types.types.length == 0){ + if(tokens.peek(1).type == TokenType.Identifier){ + aspl.parser.utils.fatal_error("Invalid parameter type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) // TODO: Use type_error when parseTypesIfAny for unknown types exists + }else{ + aspl.parser.utils.syntax_error("Parameters must specify a type", tokens.peek().location) + } + } + var parameter = tokens.next() + var Expression? defaultValue = null + if(tokens.peek().type == TokenType.Equals){ + tokens.shift() + defaultValue = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, types)) + if(!Expression(defaultValue).isConstant()){ + aspl.parser.utils.generic_error("Default parameter values must be constant", tokens.peek().location) + } + if(!Type:matches(types, Expression(defaultValue).getType())){ + aspl.parser.utils.type_error("Default parameter value must be of the same type as the parameter", tokens.peek().location) + } + } + parameters.add(new Parameter(parameter.value, types, defaultValue, token.location)) + Variable:register(parameter.value, types, token.location) + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + }else{ + if(tokens.peek().type == TokenType.ParenthesisClose){ + tokens.shift() + break + } + aspl.parser.utils.syntax_error("Expected ',' or ')'", tokens.peek().location) + } + } + var f = CustomFunction(Function:functions[IdentifierUtils:relativeToAbsoluteIdentifier(this, identifier.value)]) + var returnTypes = new Types([]) + if(tokens.peek().value == "returns"){ + tokens.shift() + returnTypes = parseTypesIfAny(tokens) + if(returnTypes.types.length == 0){ + if(tokens.peek(1).type == TokenType.BraceOpen){ + aspl.parser.utils.generic_error("Invalid return type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) // TODO: Use type_error when parseTypesIfAny for unknown types exists + }else{ + aspl.parser.utils.syntax_error("Expected type identifier after 'returns'", tokens.peek().location) + } + }elseif(f.isPublic){ + foreach(returnTypes.types as type){ + if(!Type:isPublic(type)){ + aspl.parser.utils.warning("The type '" + type.toString() + "' is not public and thus cannot be used as a return type of a public function", token.location) // TODO: Make this a generic_error after the grace period + } + } + } + } + f.parameters = parameters + f.returnTypes = returnTypes + Scope:getCurrentBundle().func = f + var statements = parseBlock(tokens, null, false) + f.code = statements + Scope:pop() + Scope:popBundle() + f.register(token.location) + var comments = list[] + foreach(f.attributes as attribute){ + foreach(attribute.comments as comment){ + comments.add(comment) + } + } + foreach(list(token.comments) as comment){ + comments.add(comment) + } + return new FunctionDeclareStatement(f, comments, token.location) + }elseif(token.value == "method"){ + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected identifier after 'method'", tokens.peek().location) + } + var name = tokens.next() + var m = CustomMethod(Method:methods[Class(currentClass).type.toString()][name.value]) + Scope:pushBundle(null) + Scope:push() + var list parameters = [] + tokens.shift() + while(true){ + if(tokens.peek().type == TokenType.ParenthesisClose){ + tokens.shift() + break + } + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected identifier after '('", tokens.peek().location) + } + var types = parseTypesIfAny(tokens) + if(types.types.length == 0){ + if(tokens.peek(1).type == TokenType.Identifier){ + aspl.parser.utils.generic_error("Invalid parameter type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) // TODO: Use type_error when parseTypesIfAny for unknown types exists + }else{ + aspl.parser.utils.syntax_error("Parameters must specify a type", tokens.peek().location) + } + }elseif(currentClass?!.isPublic && m.isPublic){ + foreach(types.types as type){ + if(!Type:isPublic(type)){ + aspl.parser.utils.warning("The type '" + type.toString() + "' is not public and thus cannot be used as a parameter type of a public method in a public class", token.location) // TODO: Make this a generic_error after the grace period + } + } + } + var parameter = tokens.next() + var Expression? defaultValue = null + if(tokens.peek().type == TokenType.Equals){ + tokens.shift() + defaultValue = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, types)) + if(!Expression(defaultValue).isConstant()){ + aspl.parser.utils.generic_error("Default parameter values must be constant", tokens.peek().location) + } + if(!Type:matches(types, Expression(defaultValue).getType())){ + aspl.parser.utils.type_error("Default parameter value must be of the same type as the parameter", tokens.peek().location) + } + } + parameters.add(new Parameter(parameter.value, types, defaultValue, token.location)) + Variable:register(parameter.value, types, token.location) + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + }else{ + if(tokens.peek().type == TokenType.ParenthesisClose){ + tokens.shift() + break + } + aspl.parser.utils.syntax_error("Expected ',' or ')'", tokens.peek().location) + } + } + var returnTypes = new Types([]) + if(tokens.peek().value == "returns"){ + tokens.shift() + returnTypes = parseTypesIfAny(tokens) + if(returnTypes.types.length == 0){ + if(tokens.peek(1).type == TokenType.BraceOpen){ + aspl.parser.utils.generic_error("Invalid return type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) // TODO: Use type_error when parseTypesIfAny for unknown types exists + }else{ + aspl.parser.utils.syntax_error("Expected type identifier after 'returns'", tokens.peek().location) + } + }elseif(currentClass?!.isPublic && m.isPublic){ + foreach(returnTypes.types as type){ + if(!Type:isPublic(type)){ + aspl.parser.utils.warning("The type '" + type.toString() + "' is not public and thus cannot be used as a return typr of a public method in a public class", token.location) // TODO: Make this a generic_error after the grace period + } + } + } + } + m.parameters = parameters + m.returnTypes = returnTypes + Scope:getCurrentBundle().func = m + var list statements = list[] + if(tokens.peek().type == TokenType.BraceOpen){ + if(m.isAbstract){ + aspl.parser.utils.syntax_error("Abstract methods cannot have a body", tokens.peek().location) + } + currentMethod = m + var oldStaticContextState = inStaticContext + inStaticContext = m.isStatic + statements = parseBlock(tokens, null, false) + inStaticContext = oldStaticContextState + currentMethod = null + }elseif(!m.isAbstract){ + aspl.parser.utils.syntax_error("Non-abstract methods must have a body", tokens.peek().location) + }else{ + statements = list[] + } + m.code = statements + Scope:pop() + Scope:popBundle() + m.register(token.location) + var comments = list[] + foreach(m.attributes as attribute){ + foreach(attribute.comments as comment){ + comments.add(comment) + } + } + foreach(list(token.comments) as comment){ + comments.add(comment) + } + return new MethodDeclareStatement(m, comments, token.location) + }elseif(token.value == "return"){ + // TODO: Disallow if inside error callback + if(Scope:getCurrentBundle().func == null){ + aspl.parser.utils.generic_error("Cannot return when not inside a function", token.location) + } + elseif(tokens.peek() != null && tokens.peek().location.startLine == token.location.endLine){ + var returnTypes = ReturnTypeUtils:getReturnTypes(Scope:getCurrentBundle().func) + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, returnTypes)) + if(returnTypes.types.length == 0){ + aspl.parser.utils.type_error("Cannot return a value from a function with no return type", token.location) + } + elseif(!Type:matches(returnTypes, value.getType())){ + if(Scope:getCurrentBundle().func oftype Method){ + aspl.parser.utils.type_error("Cannot return a value of type '" + value.getType().toString() + "' in the method " + Method(Scope:getCurrentBundle().func).type.toString() + "." + Method(Scope:getCurrentBundle().func).name + " which has a return type of '" + returnTypes.toString() + "'", token.location) + }elseif(Scope:getCurrentBundle().func oftype ReactivePropertyCallback){ + if(ReactivePropertyCallback(Scope:getCurrentBundle().func).p.isStatic){ + aspl.parser.utils.type_error("Cannot return a value of type '" + value.getType().toString() + "' in " + ReactivePropertyCallback(Scope:getCurrentBundle().func).p.type.toString() + ":" + ReactivePropertyCallback(Scope:getCurrentBundle().func).p.name + " which has a type of '" + returnTypes.toString() + "'", token.location) + }else{ + aspl.parser.utils.type_error("Cannot return a value of type '" + value.getType().toString() + "' in " + ReactivePropertyCallback(Scope:getCurrentBundle().func).p.type.toString() + "." + ReactivePropertyCallback(Scope:getCurrentBundle().func).p.name + " which has a type of '" + returnTypes.toString() + "'", token.location) + } + }elseif(Scope:getCurrentBundle().func oftype Callback){ + aspl.parser.utils.type_error("Cannot return a value of type '" + value.getType().toString() + "' in a " + Callback(Scope:getCurrentBundle().func).type.toString() + " which has a return type of '" + returnTypes.toString() + "'", token.location) + }else{ + aspl.parser.utils.type_error("Cannot return a value of type '" + value.getType().toString() + "' in the function " + Function(Scope:getCurrentBundle().func).identifier + " which has a return type of '" + returnTypes.toString() + "'", token.location) + } + } + return new ReturnStatement(value, Scope:getCurrentBundle().func, token.location) + } + return new ReturnStatement(null, Scope:getCurrentBundle().func, token.location) + }elseif(token.value == "fallback"){ + if(!Options:enableErrorHandling){ + aspl.parser.utils.generic_error("Experimental error handling ('-enableErrorHandling') is not enabled for this build", token.location) + } + // TODO: Check if inside error callback + elseif(tokens.peek() != null && tokens.peek().location.startLine == token.location.endLine){ + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null)) + // TODO: Maybe check the type? + return new FallbackStatement(value, token.location) + }else{ + aspl.parser.utils.generic_error("No fallback value provided", token.location) + } + }elseif(token.value == "escape"){ + if(!Options:enableErrorHandling){ + aspl.parser.utils.generic_error("Experimental error handling ('-enableErrorHandling') is not enabled for this build", token.location) + } + // TODO: Check if inside error callback + if(Scope:getCurrentBundle().func == null){ + aspl.parser.utils.generic_error("Cannot escape return when not inside a function", token.location) + } + elseif(tokens.peek() != null && tokens.peek().location.startLine == token.location.endLine){ + var returnTypes = ReturnTypeUtils:getReturnTypes(Scope:getCurrentBundle().func) + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, returnTypes)) + if(returnTypes.types.length == 0){ + aspl.parser.utils.type_error("Cannot escape return a value from a function with no return type", token.location) + } + elseif(!Type:matches(returnTypes, value.getType())){ + if(Scope:getCurrentBundle().func oftype Method){ + aspl.parser.utils.type_error("Cannot escape return a value of type '" + value.getType().toString() + "' in the method " + Method(Scope:getCurrentBundle().func).type.toString() + "." + Method(Scope:getCurrentBundle().func).name + " which has a return type of '" + returnTypes.toString() + "'", token.location) + }elseif(Scope:getCurrentBundle().func oftype ReactivePropertyCallback){ + if(ReactivePropertyCallback(Scope:getCurrentBundle().func).p.isStatic){ + aspl.parser.utils.type_error("Cannot escape return a value of type '" + value.getType().toString() + "' in " + ReactivePropertyCallback(Scope:getCurrentBundle().func).p.type.toString() + ":" + ReactivePropertyCallback(Scope:getCurrentBundle().func).p.name + " which has a type of '" + returnTypes.toString() + "'", token.location) + }else{ + aspl.parser.utils.type_error("Cannot escape return a value of type '" + value.getType().toString() + "' in " + ReactivePropertyCallback(Scope:getCurrentBundle().func).p.type.toString() + "." + ReactivePropertyCallback(Scope:getCurrentBundle().func).p.name + " which has a type of '" + returnTypes.toString() + "'", token.location) + } + }elseif(Scope:getCurrentBundle().func oftype Callback){ + aspl.parser.utils.type_error("Cannot escape return a value of type '" + value.getType().toString() + "' in a " + Callback(Scope:getCurrentBundle().func).type.toString() + " which has a return type of '" + returnTypes.toString() + "'", token.location) + }else{ + aspl.parser.utils.type_error("Cannot escape return a value of type '" + value.getType().toString() + "' in the function " + Function(Scope:getCurrentBundle().func).identifier + " which has a return type of '" + returnTypes.toString() + "'", token.location) + } + } + return new EscapeStatement(value, token.location) + } + return new EscapeStatement(null, token.location) + }elseif(token.value == "callback"){ + Scope:push(true) + var list parameters = [] + if(tokens.peek().type != TokenType.ParenthesisOpen){ + aspl.parser.utils.syntax_error("Expected '(' (opening parenthesis) after 'callback'", tokens.peek().location) + } + tokens.shift() + while(true){ + if(tokens.peek().type == TokenType.ParenthesisClose){ + tokens.shift() + break + } + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected identifier after '('", tokens.peek().location) + } + var types = parseTypesIfAny(tokens) + if(types.types.length == 0){ + if(tokens.peek(1).type == TokenType.Identifier){ + aspl.parser.utils.generic_error("Invalid parameter type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) // TODO: Use type_error when parseTypesIfAny for unknown types exists + }else{ + aspl.parser.utils.syntax_error("Parameters must specify a type", tokens.peek().location) + } + } + var parameter = tokens.next() + var Expression? defaultValue = null + if(tokens.peek().type == TokenType.Equals){ + aspl.parser.utils.generic_error("Optional callback parameters are not yet supported", tokens.peek().location) // TODO + /*tokens.shift() + defaultValue = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, types)) + if(!Expression(defaultValue).isConstant()){ + aspl.parser.utils.generic_error("Default parameter values must be constant", tokens.peek().location) + } + if(!Type:matches(types, Expression(defaultValue).getType())){ + aspl.parser.utils.type_error("Default parameter value must be of the same type as the parameter", tokens.peek().location) + }*/ + } + parameters.add(new Parameter(parameter.value, types, defaultValue, token.location)) + Variable:register(parameter.value, types, token.location) + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + }else{ + if(tokens.peek().type == TokenType.ParenthesisClose){ + tokens.shift() + break + } + aspl.parser.utils.syntax_error("Expected ',' or ')'", tokens.peek().location) + } + } + var returnTypes = new Types([]) + if(tokens.peek().value == "returns"){ + tokens.shift() + returnTypes = parseTypesIfAny(tokens) + if(returnTypes.types.length == 0){ + if(tokens.peek(1).type == TokenType.BraceOpen){ + aspl.parser.utils.generic_error("Invalid return type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) // TODO: Use type_error when parseTypesIfAny for unknown types exists + }else{ + aspl.parser.utils.syntax_error("Expected type identifier after 'returns'", tokens.peek().location) + } + } + } + var type = "callback" + if(parameters.length > 0 || returnTypes.types.length > 0){ + type += "<" + } + foreach(parameters as parameter){ + type += parameter.types.toString() + ", " + } + if(returnTypes.types.length > 0){ + type += "returns " + returnTypes.toString() + ", " + } + if(parameters.length > 0 || returnTypes.types.length > 0){ + type = type.before(type.length - 2) + ">" + } + var c = new Callback(Type:fromString(type, this, token.location), parameters, returnTypes, null, Scope:getCurrentBundle(), token.location) + var oldFunction = Scope:getCurrentBundle().func + Scope:getCurrentBundle().func = c + var statements = parseBlock(tokens, null, false) + Scope:getCurrentBundle().func = oldFunction + c.code = statements + var capturedVariables = Scope:getCurrent().capturedVariables.cloneShallow() + Scope:pop() + Scope:passCapturedVariables(capturedVariables) + return applyOperators(new CallbackLiteral(c, capturedVariables, token.location), tokens, precedenceLevel) + } + elseif(token.value == "if" || token.value == "elseif"){ // see below for elseif implementation + if(tokens.peek().type != TokenType.ParenthesisOpen){ + aspl.parser.utils.syntax_error("Expected '(' after '" + token.value + "'", tokens.peek().location) + } + tokens.shift() + var condition = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("bool")]))) + if(!Type:matches(new Types([Type:fromString("bool")]), condition.getType())){ + aspl.parser.utils.type_error("Condition in " + token.value + " statement must be of type boolean, but expression of type '" + condition.getType().toString() + "' given", token.location) + } + if(tokens.peek().type != TokenType.ParenthesisClose){ + aspl.parser.utils.syntax_error("Expected ')' after condition in " + token.value + " statement", tokens.peek().location) + } + tokens.shift() + if(tokens.peek().type != TokenType.BraceOpen){ + aspl.parser.utils.syntax_error("Expected '{' after condition in " + token.value + " statement", tokens.peek().location) + } + var code = parseBlock(tokens) + if(!tokens.empty()){ + if(tokens.peek().value == "elseif"){ + var next = parseToken(tokens.next(), tokens) + if(next oftype IfStatement){ + return new IfElseIfStatement(condition, code, IfStatement(next), token.location) + }elseif(next oftype IfElseIfStatement){ + return new IfElseIfStatement(condition, code, IfElseIfStatement(next), token.location) + }elseif(next oftype IfElseStatement){ + return new IfElseIfStatement(condition, code, IfElseStatement(next), token.location) + }else{ + aspl.parser.utils.syntax_error("'elseif' didn't generate if statement (compiler bug)", token.location) + } + }elseif(tokens.peek().value == "else"){ + tokens.shift() + if(tokens.peek().value == "if"){ + aspl.parser.utils.syntax_error("Use 'elseif' instead of 'else if'", tokens.peek().location) + }elseif(tokens.peek().type != TokenType.BraceOpen){ + aspl.parser.utils.syntax_error("Expected '{' after 'else'", tokens.peek().location) + } + var elseCode = parseBlock(tokens) + return new IfElseStatement(condition, code, elseCode, token.location) + } + } + return new IfStatement(condition, code, token.location) + } + elseif(token.value == "while"){ + if(tokens.peek().type != TokenType.ParenthesisOpen){ + aspl.parser.utils.syntax_error("Expected '(' after 'while'", tokens.peek().location) + } + tokens.shift() + var condition = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("bool")]))) + if(!Type:matches(new Types([Type:fromString("bool")]), condition.getType())){ + aspl.parser.utils.type_error("Condition in while statement must be of type boolean, but expression of type '" + condition.getType().toString() + "' given", token.location) + } + if(tokens.peek().type != TokenType.ParenthesisClose){ + aspl.parser.utils.syntax_error("Expected ')' after condition in while statement", tokens.peek().location) + } + tokens.shift() + if(tokens.peek().type != TokenType.BraceOpen){ + aspl.parser.utils.syntax_error("Expected '{' after condition in while statement", tokens.peek().location) + } + loopDepth++ + var code = parseBlock(tokens) + loopDepth-- + return new WhileStatement(condition, code, token.location) + } + elseif(token.value == "repeat"){ + if(tokens.peek().type != TokenType.ParenthesisOpen){ + aspl.parser.utils.syntax_error("Expected '(' after 'repeat'", tokens.peek().location) + } + tokens.shift() + var iterations = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("integer")]))) + if(!Type:matches(new Types([Type:fromString("integer")]), iterations.getType())){ + aspl.parser.utils.type_error("Amount in repeat statement must be of type 'integer', but expression of type '" + iterations.getType().toString() + "' given", token.location) + } + var string? variable = null + var int start = 1 + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + var variableToken = tokens.next() + if(variableToken.type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected variable name after ',' in repeat statement", variableToken.location) + } + if(variableToken.value == "_"){ + aspl.parser.utils.warning("Unnecessary use of '_' as variable name in repeat statement", variableToken.location) + }else{ + variable = variableToken.value + } + if(tokens.peek().type == TokenType.Equals){ + tokens.shift() + var startExpression = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("integer")]))) + if(!Type:matches(new Types([Type:fromString("integer")]), startExpression.getType())){ + aspl.parser.utils.type_error("Start value in repeat statement must be of type 'integer', but expression of type '" + startExpression.getType().toString() + "' given", token.location) + } + if(!startExpression.isConstant()){ + aspl.parser.utils.syntax_error("Start value in repeat statement must be constant", token.location) + } + start = int(startExpression.getConstantValue()) + }else{ + aspl.parser.utils.warning("Start value in repeat statement not specified, assuming " + start, token.location) + } + } + Scope:push() + if(variable != null){ + Variable:register(string(variable), new Types([Type:fromString("integer")]), token.location) + } + if(tokens.peek().type != TokenType.ParenthesisClose){ + aspl.parser.utils.syntax_error("Expected ')' after iterations in repeat statement", tokens.peek().location) + } + tokens.shift() + if(tokens.peek().type != TokenType.BraceOpen){ + aspl.parser.utils.syntax_error("Expected '{' after iterations in repeat statement", tokens.peek().location) + } + loopDepth++ + var code = parseBlock(tokens, null, false) + loopDepth-- + Scope:pop() + return new RepeatStatement(iterations, variable, start, code, token.location) + } + elseif(token.value == "foreach"){ + if(tokens.peek().type != TokenType.ParenthesisOpen){ + aspl.parser.utils.syntax_error("Expected '(' after 'foreach'", tokens.peek().location) + } + tokens.shift() + var collection = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("list")]))) + if(!(Type:matches(new Types([Type:fromString("list")]), collection.getType(), true) || Type:matches(new Types([Type:fromString("map")]), collection.getType(), true) || Type:matches(new Types([Type:fromString("string")]), collection.getType()))){ + aspl.parser.utils.type_error("Collection in foreach statement must be of type 'list', 'map' or 'string', but expression of type '" + collection.getType().toString() + "' given", token.location) + } + if(tokens.peek().value != "as"){ + aspl.parser.utils.syntax_error("Expected 'as' after collection in foreach statement", tokens.peek().location) + } + tokens.shift() + var first = tokens.next() + if(first.type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected variable name after 'as' in foreach statement", first.location) + } + var Token? second = null + if(tokens.peek().type == TokenType.Assign){ + tokens.shift() + second = tokens.next() + if(Token(second).type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected variable name after '=>' in foreach statement", Token(second).location) + } + }elseif(tokens.peek().type == TokenType.Comma){ + aspl.parser.utils.syntax_error("Use '=>' instead of ',' when specifying key and value variables in foreach statements", tokens.peek().location) + } + Scope:push() + var string? key = null + var string? value = null + if(second != null){ + if(first.value == "_"){ + aspl.parser.utils.warning("Unnecessary use of '_' as key variable name in foreach statement", first.location) + }else{ + key = first.value + if(Type:matches(new Types([Type:fromString("list")]), collection.getType(), true)){ + Variable:register(string(key), new Types([Type:fromString("integer")]), token.location) + }elseif(Type:matches(new Types([Type:fromString("map")]), collection.getType(), true)){ + if(Type:getGenericTypes(collection.getType().toString()).length < 2){ + Variable:register(string(key), new Types([]), token.location) // map (due to a previous non-fatal error) + }else{ + Variable:register(string(key), Type:getGenericTypes(collection.getType().toString())[0], token.location) + } + }elseif(Type:matches(new Types([Type:fromString("string")]), collection.getType())){ + Variable:register(string(key), new Types([Type:fromString("integer")]), token.location) + } + } + if(Token(second).value != "_"){ + value = Token(second).value + if(Type:matches(new Types([Type:fromString("list")]), collection.getType(), true)){ + if(Type:getGenericTypes(collection.getType().toString()).length < 1){ + Variable:register(string(value), new Types([]), token.location) // list (due to a previous non-fatal error) + }else{ + Variable:register(string(value), Type:getGenericTypes(collection.getType().toString())[0], token.location) + } + }elseif(Type:matches(new Types([Type:fromString("map")]), collection.getType(), true)){ + if(Type:getGenericTypes(collection.getType().toString()).length < 2){ + Variable:register(string(value), new Types([]), token.location) // map (due to a previous non-fatal error) + }else{ + Variable:register(string(value), Type:getGenericTypes(collection.getType().toString())[1], token.location) + } + }elseif(Type:matches(new Types([Type:fromString("string")]), collection.getType())){ + Variable:register(string(value), new Types([Type:fromString("string")]), token.location) + } + } + }else{ + if(first.value == "_"){ + aspl.parser.utils.warning("Unnecessary use of '_' as value variable name in foreach statement", first.location) + }else{ + value = first.value + if(Type:matches(new Types([Type:fromString("list")]), collection.getType(), true)){ + if(Type:getGenericTypes(collection.getType().toString()).length < 1){ + Variable:register(string(value), new Types([]), token.location) + }else{ + Variable:register(string(value), Type:getGenericTypes(collection.getType().toString())[0], token.location) + } + }elseif(Type:matches(new Types([Type:fromString("map")]), collection.getType(), true)){ + if(Type:getGenericTypes(collection.getType().toString()).length < 2){ + Variable:register(string(value), new Types([]), token.location) + }else{ + Variable:register(string(value), Type:getGenericTypes(collection.getType().toString())[1], token.location) + } + }elseif(Type:matches(new Types([Type:fromString("string")]), collection.getType())){ + Variable:register(string(value), new Types([Type:fromString("string")]), token.location) + } + } + } + if(tokens.peek().type != TokenType.ParenthesisClose){ + aspl.parser.utils.syntax_error("Expected ')' after variable names in foreach statement", tokens.peek().location) + } + tokens.shift() + if(tokens.peek().type != TokenType.BraceOpen){ + aspl.parser.utils.syntax_error("Expected '{' after variable names in foreach statement", tokens.peek().location) + } + loopDepth++ + var code = parseBlock(tokens, null, false) + loopDepth-- + Scope:pop() + return new ForeachStatement(collection, key, value, code, token.location) + } + elseif(token.value == "implement"){ + if(tokens.peek().type != TokenType.ParenthesisOpen){ + aspl.parser.utils.syntax_error("Expected '(' after 'implement'", tokens.peek().location) + } + tokens.shift() + var call = tokens.next() + if(call.type != TokenType.String){ + aspl.parser.utils.syntax_error("Expected implementation call identifier (string) as first argument to 'implement'", call.location) + } + var list args = [] + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + while(true){ + var arg = tokens.peek() + if(arg.type == TokenType.ParenthesisClose){ + break + } + tokens.shift() + args.add(aspl.parser.utils.verify_expression(parseToken(arg, tokens))) + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + } + } + } + if(tokens.peek().type != TokenType.ParenthesisClose){ + aspl.parser.utils.syntax_error("Expected ')' after arguments for 'implement'", tokens.peek().location) + } + tokens.shift() + ImplementationCallUtils:usedImplementationCalls[call.value] = args.length + return applyOperators(new ImplementationCallExpression(call.value, args, token.location), tokens, precedenceLevel) + } + elseif(token.value == "oftype"){ + if(previousExpression == null){ + aspl.parser.utils.syntax_error("Expected expression before 'oftype'", token.location) + } + var typeIdentifierFirstToken = tokens.peek() + var typeIdentifier = parseTypeIdentifier(tokens).identifier + if(!Type:existsByName(this, typeIdentifier)){ + aspl.parser.utils.type_error("Type '" + typeIdentifier + "' does not exist", token.location) + } + var type = Type:fromString(typeIdentifier, this, typeIdentifierFirstToken.location) + /* + TODO: The below code is false-positive when type extends Expression(previousExpression).getType() + if(!Type:matches(new Types([type]), Expression(previousExpression).getType())){ + aspl.parser.utils.warning("Expression of type '" + Expression(previousExpression).getType().toString() + "' will never be of type '" + type.toString() + "'", token.location) + }*/ + return applyOperators(new OfTypeExpression(Expression(previousExpression), type, token.location), tokens, precedenceLevel) + } + elseif(token.value == "break" || token.value == "break_no_legacy"){ // TODO: Remove break_no_legacy when old codebases have been updated + var level = 1 + if(tokens.peek().type == TokenType.Integer){ + var levelExpression = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("integer")]))) + if(!Type:matches(new Types([Type:fromString("integer")]), levelExpression.getType())){ + aspl.parser.utils.type_error("Break level must be of type integer, but expression of type '" + levelExpression.getType().toString() + "' given", token.location) + } + if(!levelExpression.isConstant()){ + aspl.parser.utils.generic_error("Break level must be known at compile time", token.location) + } + level = int(levelExpression.getConstantValue()) + } + if(this.loopDepth < 1){ + aspl.parser.utils.generic_error("Cannot use 'break' outside of a loop", token.location) + }elseif(level < 1){ + aspl.parser.utils.generic_error("Break level must be greater than 0", token.location) + }elseif(level > this.loopDepth){ + aspl.parser.utils.generic_error("Break level must be less than or equal to " + this.loopDepth + " because it is in only that many nested loops", token.location) + } + return new BreakStatement(level, token.location) + } + elseif(token.value == "continue" || token.value == "continue_no_legacy"){ // TODO: Remove continue_no_legacy when old codebases have been updated + var level = 1 + if(tokens.peek().type == TokenType.Integer){ + var levelExpression = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("integer")]))) + if(!Type:matches(new Types([Type:fromString("integer")]), levelExpression.getType())){ + aspl.parser.utils.type_error("Continue level must be of type integer, but expression of type '" + levelExpression.getType().toString() + "' given", token.location) + } + if(!levelExpression.isConstant()){ + aspl.parser.utils.generic_error("Continue level must be known at compile time", token.location) + } + level = int(levelExpression.getConstantValue()) + } + if(this.loopDepth < 1){ + aspl.parser.utils.generic_error("Cannot use 'continue' outside of a loop", token.location) + }elseif(level < 1){ + aspl.parser.utils.generic_error("Continue level must be greater than 0", token.location) + }elseif(level > this.loopDepth){ + aspl.parser.utils.generic_error("Continue level must be less than or equal to " + this.loopDepth + " because it is in only that many nested loops", token.location) + } + return new ContinueStatement(level, token.location) + } + elseif(token.value == "class"){ + if(currentClass != null){ + aspl.parser.utils.syntax_error("Cannot declare a class inside another class", token.location) + } + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected class name after 'class'", tokens.peek().location) + } + var name = tokens.next().value + var list parents = [] + if(tokens.peek().value == "extends"){ + tokens.shift() + while(tokens.peek().type != TokenType.BraceOpen){ + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected class name after 'extends'", tokens.peek().location) + } + var t = tokens.peek() + var parent = parseTypeIdentifier(tokens).identifier + if(Type:existsByName(this, parent)){ + var type = Type:fromString(parent, this, t.location) + if(!Class:classes.containsKey(type.toString())){ + aspl.parser.utils.type_error("Cannot extend the unknown class '" + parent + "'", tokens.peek().location) + }else{ + parents.add(type) + } + }else{ + aspl.parser.utils.type_error("Cannot extend the unknown class '" + parent + "'", tokens.peek().location) + } + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + } + } + } + if(tokens.peek().type != TokenType.BraceOpen){ + aspl.parser.utils.syntax_error("Expected '{' after class name", tokens.peek().location) + } + var type = Type:fromString(currentNamespace + "." + name, this, token.location) + ClassUtils:throwOnInvalidInheritance(Class:classes[type.toString()], [], token.location) + var c = Class:classes[type.toString()] + currentClass = c + c.parents = parents + c.code = parseBlock(tokens) + currentClass = null + if(!c.isAbstract){ + var stillAbstractMethods = ClassUtils:getAllAbstractMethods(c) + if(stillAbstractMethods.length > 0){ + aspl.parser.utils.type_error("The class " + c.type.toString() + " does not implement the following abstract methods from its parents: " + stillAbstractMethods.join(", ")) + } + } + var comments = list[] + foreach(c.attributes as attribute){ + foreach(attribute.comments as comment){ + comments.add(comment) + } + } + foreach(list(token.comments) as comment){ + comments.add(comment) + } + return new ClassDeclareStatement(c, comments, token.location) + } + elseif(token.value == "enum"){ + if(currentClass != null){ + aspl.parser.utils.syntax_error("Cannot declare an enum inside another enum", token.location) + } + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected enum name after 'enum'", tokens.peek().location) + } + var name = tokens.next().value + if(tokens.peek().type != TokenType.BraceOpen){ + aspl.parser.utils.syntax_error("Expected '{' after enum name", tokens.peek().location) + } + tokens.shift() + attributeCache.clear() + var type = Type:fromString(currentNamespace + "." + name, this, token.location) + var e = Enum:enums[type.toString()] + currentEnum = e + var map fields = {} + while(true){ + if(e.isFlags && fields.length > 31){ + aspl.parser.utils.generic_error("Cannot declare more than 32 fields in the enum \"" + e.type.toString() + "\", because it has the \"flags\" attribute", tokens.peek().location) + } + var field = tokens.next() + if(field.type == TokenType.BraceClose){ + break + } + if(AttributeUtils:parseAttributesIfAny(this, field, tokens)){ + field = tokens.next() + } + if(field.type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected enum field name", field.location) + } + if(fields.containsKey(field.value)){ + aspl.parser.utils.generic_error("Enum field '" + field.value + "' already declared", field.location) + } + foreach(attributeCache as attribute){ + if((attribute.attribute.usage && AttributeUsage.EnumField) == 0){ + aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used on enum fields", attribute.location) + } + foreach(attributeCache as other){ + if(other.attribute.identifier != attribute.attribute.identifier){ + if(!attribute.attribute.canPair(other.attribute)){ + aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used together with the attribute '" + other.attribute.identifier + "'", attribute.location) + } + } + } + } + var value = fields.length + if(tokens.peek().type == TokenType.Equals){ + if(e.isFlags){ + aspl.parser.utils.generic_error("Cannot specify custom field values for enums with the 'flags' attribute", tokens.peek().location) + } + tokens.shift() + var v = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("integer")]))) + if(!v.isConstant()){ + aspl.parser.utils.generic_error("Value of enum field must be constant") + }elseif(!Type:matches(new Types([Type:fromString("integer")]), v.getType())){ + aspl.parser.utils.type_error("Value of enum field must be an integer") + }else{ + value = int(v.getConstantValue()) + } + }elseif(e.isFlags){ + value = int(math.pow(2d, double(fields.length))) + } + fields[field.value] = new EnumField(e, field.value, value, attributeCache, token.location) + attributeCache.clear() + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + }elseif(tokens.peek().type != TokenType.BraceClose){ + aspl.parser.utils.syntax_error("Expected ',' or '}' after enum field", tokens.peek().location) + } + } + e.fields = fields + currentEnum = null + var comments = list[] + foreach(list(e.attributes) as attribute){ + foreach(attribute.comments as comment){ + comments.add(comment) + } + } + foreach(list(token.comments) as comment){ + comments.add(comment) + } + return new EnumDeclareStatement(e, comments, token.location) + }elseif(token.value == "new"){ + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected class identifier after 'new'", tokens.peek().location) + } + var identifierFirstToken = tokens.peek() + var unhandledIdentifier = parseTypeIdentifier(tokens).identifier + var type = Type:fromString(unhandledIdentifier, this, identifierFirstToken.location) + if(!Class:classes.containsKey(type.toString())){ + aspl.parser.utils.fatal_error("Class '" + unhandledIdentifier + "' not found", token.location) + } + var c = Class:classes[type.toString()] + if(tokens.peek().type != TokenType.ParenthesisOpen){ + aspl.parser.utils.syntax_error("Expected '(' after class identifier in 'new' expression", tokens.peek().location) + } + tokens.shift() + if(c.isAbstract){ + aspl.parser.utils.generic_error("Cannot instantiate the abstract class " + type.toString(), token.location) + } + if(c.isStatic){ + aspl.parser.utils.generic_error("Cannot instantiate the static class " + type.toString(), token.location) + } + var list arguments = [] + if(Method:methods.containsKey(c.type.toString())){ + if(Method:exists(c.type, "construct")){ + var constructor = Method:get(c.type, "construct") + foreach(constructor.parameters as parameter){ + if(tokens.peek().type == TokenType.ParenthesisClose){ + if(!parameter.optional){ + aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for method '" + constructor.type.toString() + "." + constructor.name + "' (expected " + constructor.minimumParameterCount + ")", tokens.peek().location) + } + break + } + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, parameter.types)) + if(!Type:matches(parameter.types, value.getType())){ + aspl.parser.utils.type_error("Cannot pass a value of the type '" + value.getType().toString() + "' to a parameter of the type '" + parameter.types.toString() + "'", value.location) + } + arguments.add(value) + if(arguments.length < constructor.parameters.length && tokens.peek().type == TokenType.Comma){ + tokens.shift() + } + } + if(!constructor.isPublic && module.id != c.module.id){ + aspl.parser.utils.warning("Cannot instantiate the class '" + c.type.identifier + "' that has a private constructor here", token.location) // TODO: Make this a generic_error after the grace period + } + } + } + tokens.shift() + foreach(c.attributes as attribute){ + if(attribute.attribute.identifier == "deprecated"){ + if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){ + aspl.parser.utils.warning("Class " + c.type.toString() + " is deprecated: " + attribute.arguments[0].getConstantValue(), identifierFirstToken.location) + }else{ + aspl.parser.utils.warning("Class " + c.type.toString() + " is deprecated", identifierFirstToken.location) + } + } + } + if(!c.isPublic && module.id != c.module.id){ + aspl.parser.utils.warning("Cannot instantiate the private class '" + c.type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new ClassInstantiateExpression(c, arguments, token.location), tokens, precedenceLevel) + }elseif(token.value == "property"){ + if(currentClass == null){ + aspl.parser.utils.generic_error("Cannot declare a property outside of a class", token.location) + } + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected property name after 'property'", tokens.peek().location) + } + var types = parseTypesIfAny(tokens) + var name = tokens.next() + var Expression? defaultValue = null + attributeCache.clear() + if(tokens.peek().type == TokenType.Equals){ + tokens.shift() + defaultValue = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, types)) + } + elseif(tokens.peek().type == TokenType.BraceOpen){ + tokens.shift() + var p = CustomReactiveProperty(Property:properties[Class(currentClass).type.toString()][name.value]) + var bool isStatic = false + foreach(p.attributes as attribute){ + if(attribute.attribute.identifier == "static"){ + isStatic = true + } + } + if(currentClass?!.isPublic && p.isPublic){ + foreach(types.types as type){ + if(!Type:isPublic(type)){ + aspl.parser.utils.warning("The type '" + type.toString() + "' is not public and thus cannot be used as a type of a public property in a public class", token.location) // TODO: Make this a generic_error after the grace period + } + } + } + var list? getCode = null + var list? setCode = null + if(tokens.peek().type == TokenType.Identifier){ + if(tokens.peek().value == "get"){ + tokens.shift() + Scope:pushBundle(new ReactivePropertyCallback(p, types)) + Scope:push() + var oldStaticContextState = inStaticContext + inStaticContext = p.isStatic + getCode = parseBlock(tokens) + inStaticContext = oldStaticContextState + Scope:pop() + Scope:popBundle() + } + if(tokens.peek().value == "set"){ + tokens.shift() + Scope:pushBundle(new ReactivePropertyCallback(p, new Types([]))) + Scope:push() + Variable:register("value", types, token.location) + var oldStaticContextState = inStaticContext + inStaticContext = p.isStatic + setCode = parseBlock(tokens) + inStaticContext = oldStaticContextState + Scope:pop() + Scope:popBundle() + } + } + if(getCode == null && setCode == null){ + aspl.parser.utils.generic_error("Expected 'get', 'set' or both in reactive property declaration", tokens.peek().location) + } + tokens.shift() + p.getCode = getCode + p.setCode = setCode + p.register(token.location) + var comments = list[] + foreach(p.attributes as attribute){ + foreach(attribute.comments as comment){ + comments.add(comment) + } + } + foreach(list(token.comments) as comment){ + comments.add(comment) + } + return new PropertyDeclareStatement(p, comments, token.location) + } + if(types.types.length == 0){ + if(defaultValue == null){ + aspl.parser.utils.syntax_error("Expected type after property name in 'property' statement", name.location) + } + types = Expression(defaultValue).getType() + }else{ + if(defaultValue != null){ + if(!Type:matches(types, Expression(defaultValue).getType())){ + aspl.parser.utils.generic_error("Cannot assign a default value of type '" + Expression(defaultValue).getType().toString() + "' to a property of type '" + types.toString() + "'", Expression(defaultValue).location) + } + }else{ + if(Type:matches(types, new Types([Type:fromString("null")]))){ + defaultValue = new NullLiteral(token.location) + }else{ + if(types.types.length > 1){ + var allComplexTypes = true + foreach(types.types as type){ + if(!Class:classes.containsKey(type.toString()) && !Enum:enums.containsKey(type.toString())){ + allComplexTypes = false + } + } + if(allComplexTypes){ + defaultValue = null + }else{ + aspl.parser.utils.generic_error("Cannot infer a default value for a property with multiple types", name.location) + } + }else{ + defaultValue = types.types[0].getDefaultValue(token.location) + } + } + } + } + var p = CustomNormalProperty(Property:properties[Class(currentClass).type.toString()][name.value]) + var bool isStatic = false + var bool isThreadLocal = false + foreach(p.attributes as attribute){ + if(attribute.attribute.identifier == "static"){ + isStatic = true + }elseif(attribute.attribute.identifier == "threadlocal"){ + isThreadLocal = true + } + } + if(isThreadLocal && !isStatic){ + aspl.parser.utils.type_error("The attribute 'threadlocal' can only be used on static properties", token.location) + } + if(currentClass?!.isPublic && p.isPublic){ + foreach(types.types as type){ + if(!Type:isPublic(type)){ + aspl.parser.utils.warning("The type '" + type.toString() + "' is not public and thus cannot be used as a type of a public property in a public class", token.location) // TODO: Make this a generic_error after the grace period + } + } + } + p.defaultValue = defaultValue + p.register(token.location) + var comments = list[] + foreach(p.attributes as attribute){ + foreach(attribute.comments as comment){ + comments.add(comment) + } + } + foreach(list(token.comments) as comment){ + comments.add(comment) + } + return new PropertyDeclareStatement(p, comments, token.location) + }elseif(token.value == "this"){ + if(currentClass == null){ + aspl.parser.utils.generic_error("Cannot use the 'this' keyword outside of a class", token.location) + } + if(inStaticContext){ + aspl.parser.utils.generic_error("Cannot use the 'this' keyword in a static context", token.location) + } + Scope:passCapturedVariables(["this"]) + return applyOperators(new ThisExpression(Class(currentClass), token.location), tokens, precedenceLevel) + }elseif(token.value == "parent" && tokens.peek().type == TokenType.ParenthesisOpen){ + if(currentClass == null){ + aspl.parser.utils.generic_error("Cannot use the 'parent' keyword outside of a class", token.location) + } + if(inStaticContext){ + aspl.parser.utils.generic_error("Cannot use the 'parent' keyword in a static context", token.location) + } + if(currentClass?!.parents == null || currentClass?!.parents?!.length == 0){ // TODO: Can .parents ever be null at this point? + aspl.parser.utils.generic_error("Cannot use the 'parent' keyword in a class that does not extend any other classes", token.location) + } + tokens.shift() + var parentType = Type:fromString(parseTypeIdentifier(tokens).identifier, this, token.location) + if(!Class:classes.containsKey(parentType.toString())){ + aspl.parser.utils.type_error("Class '" + parentType.toString() + "' does not exist", token.location) + } + var parentClass = Class:classes[parentType.toString()] + if(!ClassUtils:isParent(currentClass?!, parentClass)){ + aspl.parser.utils.generic_error("Cannot use the 'parent' keyword with a class that is not a parent of the current class", token.location) + } + if(tokens.peek().type != TokenType.ParenthesisClose){ + aspl.parser.utils.syntax_error("Expected ')' after the parent class type in 'parent' expression", tokens.peek().location) + } + tokens.shift() + if(tokens.peek().type != TokenType.Dot){ + aspl.parser.utils.syntax_error("Expected '.' after 'parent' expression", tokens.peek().location) + } + return applyOperators(new ParentExpression(parentClass, token.location), tokens, precedenceLevel) + }elseif(token.value == "throw"){ + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens)) + if(!(value oftype ClassInstantiateExpression) || !ClassInstantiateExpression(value).c.isError){ + aspl.parser.utils.type_error("Only errors can be thrown", value.location) + } + if(!ErrorUtils:canCallableThrow(Scope:getCurrentBundle().func)){ + aspl.parser.utils.generic_error("Cannot throw an error in a callable without the [throws] attribute", token.location) + } + return new ThrowStatement(ClassInstantiateExpression(value), token.location) + }elseif(token.value == "catch"){ + if(!Options:enableErrorHandling){ + aspl.parser.utils.generic_error("Experimental error handling ('-enableErrorHandling') is not enabled for this build", token.location) + } + if(previousExpression == null){ + aspl.parser.utils.syntax_error("The " + token.value + " statement can only be used on expressions directly, i.e.: foo() catch { ... }", token.location) + } + if(!ErrorUtils:canExpressionThrow(previousExpression)){ + aspl.parser.utils.syntax_error("Cannot catch an error from an expression that is not error-prone", token.location) + } + var string? variable = null + if(tokens.peek().type == TokenType.Identifier){ + variable = tokens.next().value + } + if(tokens.peek().type != TokenType.BraceOpen){ + aspl.parser.utils.syntax_error("Expected '{' after variable in " + token.value + " statement", tokens.peek().location) + } + Scope:push(true) + if(variable != null){ + Variable:register(variable?!, new Types([Type:fromString("any")]), token.location) + } + var code = parseBlock(tokens) + var capturedVariables = Scope:getCurrent().capturedVariables.cloneShallow() + Scope:pop() + Scope:passCapturedVariables(capturedVariables) + return applyOperators(new CatchExpression(previousExpression?!, variable, code, capturedVariables, standalone, token.location), tokens, precedenceLevel) // TODO: The standalone variable will always be false here + }elseif(Variable:exists(token.value)){ + if(!tokens.empty()){ + if(tokens.peek().type == TokenType.Equals){ + tokens.shift() + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, Variable:get(token.value).types)) + if(!Type:matches(Variable:get(token.value).types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a variable of type '" + Variable:get(token.value).types.toString() + "'", value.location) + } + return applyOperators(new VariableAssignExpression(Variable:get(token.value), value, token.location), tokens, precedenceLevel) + } + } + return applyOperators(new VariableAccessExpression(Variable:get(token.value), token.location), tokens, precedenceLevel) + } + var typeIdentifier = peekTypeIdentifier(tokens, token) + if(tokens.length > typeIdentifier.tokenCount && (tokens.peek(typeIdentifier.tokenCount).type == TokenType.ParenthesisOpen) && Type:existsByName(this, typeIdentifier.identifier)){ + parseTypeIdentifier(tokens, token) + var target = Type:fromString(typeIdentifier.identifier, this, token.location) + tokens.shift() + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens)) + tokens.shift() + if(!value.getType().canCast(target)){ + aspl.parser.utils.type_error("Cannot cast from type '" + value.getType().toString() + "' to type '" + target.toString() + "'", value.location) + } + return applyOperators(new CastExpression(value, target, token.location), tokens, precedenceLevel) + } + if(tokens.length > typeIdentifier.tokenCount && tokens.peek(typeIdentifier.tokenCount).type == TokenType.Colon){ + if(!Type:existsByName(this, typeIdentifier.identifier)){ + aspl.parser.utils.fatal_error("Unknown type '" + typeIdentifier.identifier + "'", typeIdentifier.location) + } + var type = Type:fromString(typeIdentifier.identifier, this, typeIdentifier.location) + tokens.shift(typeIdentifier.tokenCount + 1) + var name = tokens.next() + if(name.type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected identifier after ':'", name.location) + } + if(tokens.length > 0 && tokens.peek().type == TokenType.ParenthesisOpen){ + if(!Method:exists(type, name.value, false)){ + aspl.parser.utils.fatal_error("Unknown method " + type.toString() + ":" + name.value, name.location) + } + var newThread = false + if(tokens.peek().type == TokenType.Plus){ + newThread = true + tokens.shift() + } + var m = Method:get(type, name.value) + if(!m.isStatic){ + aspl.parser.utils.generic_error("Cannot call the non-static method " + type.toString() + "." + name.value + " statically", name.location) + } + tokens.shift() + var list arguments = [] + foreach(m.parameters as parameter){ + if(tokens.peek().type == TokenType.ParenthesisClose){ + if(!parameter.optional){ + aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for method '" + m.type.toString() + ":" + m.name + "' (expected " + m.minimumParameterCount + ")", tokens.peek().location) + } + break + } + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, parameter.types)) + if(!Type:matches(parameter.types, value.getType())){ + aspl.parser.utils.type_error("Cannot pass a value of the type '" + value.getType().toString() + "' to a parameter of the type '" + parameter.types.toString() + "'", value.location) + } + arguments.add(value) + if(arguments.length < m.parameters.length && tokens.peek().type == TokenType.Comma){ + tokens.shift() + } + } + foreach(m.attributes as attribute){ + if(attribute.attribute.identifier == "deprecated"){ + if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){ + aspl.parser.utils.warning("Method " + m.type.toString() + ":" + m.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), name.location) + }else{ + aspl.parser.utils.warning("Method " + m.type.toString() + ":" + m.name + " is deprecated", name.location) + } + } + } + + if(!Class:classes[m.type.toString()].isPublic && module.id != Class:classes[m.type.toString()].module.id){ + aspl.parser.utils.warning("Cannot call a method from the private class '" + Class:classes[m.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period + } + if(m oftype CustomMethod && !m.isPublic && (currentClass == null || !Type:matches(new Types([m.type]), new Types([currentClass?!.type])))){ + aspl.parser.utils.warning("Cannot call the private method '" + m.type.identifier + ":" + m.name + "' here", token.location) // TODO: Make this a generic_error after the grace period + } + var node = new StaticMethodCallExpression(m, type, arguments, newThread, token.location) + if(tokens.peek().type != TokenType.ParenthesisClose){ + if(arguments.length == 0 || tokens.peek().type == TokenType.Comma){ + aspl.parser.utils.fatal_error("Too many arguments (" + arguments.length + ") given for method '" + m.type.toString() + ":" + m.name + "' (expected " + m.parameters.length + ")", tokens.peek().location) + }else{ + aspl.parser.utils.syntax_error("Expected ')' after arguments in method call", tokens.peek().location) + } + }else{ + tokens.shift() + } + return applyOperators(node, tokens, precedenceLevel) + }else{ + if(Enum:enums.containsKey(type.toString())){ + aspl.parser.utils.syntax_error("Enums fields are accessed with a '.' (dot) instead of a ':' (colon), so use " + typeIdentifier.identifier + "." + name.value + " instead of " + typeIdentifier.identifier + ":" + name.value, name.location) + } + if(!Property:exists(type, name.value, false)){ + aspl.parser.utils.fatal_error("Unknown property " + type.toString() + ":" + name.value, name.location) + } + var p = Property:get(type, name.value) + if(!p.isStatic){ + aspl.parser.utils.generic_error("Cannot access the non-static property " + type.toString() + "." + name.value + " statically", name.location) + } + if(!tokens.empty()){ + if(tokens.peek().type == TokenType.Equals){ + tokens.shift() + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, p.types)) + if(!Type:matches(p.types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + p.types.toString() + "'", value.location) + } + if(!Class:classes[p.type.toString()].isPublic && module.id != Class:classes[p.type.toString()].module.id){ + aspl.parser.utils.warning("Cannot assign to a property from the private class '" + Class:classes[p.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period + } + foreach(p.attributes as attribute){ + if(attribute.attribute.identifier == "deprecated"){ + if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){ + aspl.parser.utils.warning("Property " + p.type.toString() + ":" + p.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), name.location) + }else{ + aspl.parser.utils.warning("Property " + p.type.toString() + ":" + p.name + " is deprecated", name.location) + } + } + } + if(p.isReadPublic){ + if(currentClass != null && !Type:matches(new Types([p.type]), new Types([currentClass?!.type]))){ + aspl.parser.utils.warning("Cannot assign to the property " + p.type.identifier + ":" + p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period + } + }elseif(!p.isPublic && (currentClass == null || !Type:matches(new Types([p.type]), new Types([currentClass?!.type])))){ + aspl.parser.utils.warning("Cannot assign to the private property " + p.type.identifier + ":" + p.name + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new StaticPropertyAssignExpression(p, type, value, token.location), tokens, precedenceLevel) + } + } + if(!Class:classes[p.type.toString()].isPublic && module.id != Class:classes[p.type.toString()].module.id){ + aspl.parser.utils.warning("Cannot access a property from the private class '" + Class:classes[p.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period + } + foreach(p.attributes as attribute){ + if(attribute.attribute.identifier == "deprecated"){ + if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){ + aspl.parser.utils.warning("Property " + p.type.toString() + ":" + p.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), name.location) + }else{ + aspl.parser.utils.warning("Property " + p.type.toString() + ":" + p.name + " is deprecated", name.location) + } + } + } + if(!p.isPublic && !p.isReadPublic && (currentClass == null || !Type:matches(new Types([p.type]), new Types([currentClass?!.type])))){ + aspl.parser.utils.warning("Cannot access the private property " + p.type.identifier + ":" + p.name + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new StaticPropertyAccessExpression(p, type, token.location), tokens, precedenceLevel) + } + }else{ + if(typeIdentifier.identifier.contains(".")){ + var enumIdentifierParts = typeIdentifier.identifier.split(".") + var fieldToken = tokens.peek(typeIdentifier.tokenCount - 2) + var field = enumIdentifierParts[enumIdentifierParts.length - 1] + enumIdentifierParts.removeAt(enumIdentifierParts.length - 1) + var enumIdentifier = enumIdentifierParts.join(".") + var type = Type:fromString(enumIdentifier, this, typeIdentifier.location) + if(Enum:enums.containsKey(type.toString())){ + parseTypeIdentifier(tokens, token) + if(!map(Enum:enums[type.toString()].fields).containsKey(field)){ + aspl.parser.utils.syntax_error("Unknown enum field " + type.toString() + "." + field, fieldToken.location) + } + if(!Enum:enums[type.toString()].isPublic && module.id != Enum:enums[type.toString()].module.id){ + aspl.parser.utils.warning("Cannot access the private enum " + type.toString() + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new EnumFieldAccessExpression(map(Enum:enums[type.toString()].fields)[field], token.location), tokens, precedenceLevel) + } + } + } + if(token.value == "list"){ + if(tokens.peek().type != TokenType.LessThan){ + aspl.parser.utils.syntax_error("Expected '<' after 'list'", tokens.peek().location) + } + tokens.shift() + var Types type = parseTypesIfAny(tokens) + if(tokens.peek().type != TokenType.GreaterThan){ + aspl.parser.utils.syntax_error("Expected '>' after list type", tokens.peek().location) + } + tokens.shift() + if(tokens.peek().type != TokenType.BracketOpen){ + aspl.parser.utils.syntax_error("Expected '[' after list type", tokens.peek().location) + } + tokens.shift() + var list values = [] + while(tokens.peek().type != TokenType.BracketClose){ + var v = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, type)) + if(!Type:matches(type, v.getType())){ + aspl.parser.utils.type_error("Cannot add a value of type '" + v.getType().toString() + "' to a list of with the value type '" + type.toString() + "'", v.location) + } + values.add(v) + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + } + } + tokens.shift() + return applyOperators(new ListLiteral(type, values, token.location), tokens, precedenceLevel) + }elseif(token.value == "map"){ + if(tokens.peek().type != TokenType.LessThan){ + aspl.parser.utils.syntax_error("Expected '<' after 'map'", tokens.peek().location) + } + tokens.shift() + var Types keyType = parseTypesIfAny(tokens) + if(tokens.peek().type != TokenType.Comma){ + aspl.parser.utils.syntax_error("Expected ',' after key type", tokens.peek().location) + } + tokens.shift() + var Types valueType = parseTypesIfAny(tokens) + if(tokens.peek().type != TokenType.GreaterThan){ + aspl.parser.utils.syntax_error("Expected '>' after value type", tokens.peek().location) + } + tokens.shift() + if(tokens.peek().type != TokenType.BraceOpen){ + aspl.parser.utils.syntax_error("Expected '{' after map types", tokens.peek().location) + } + tokens.shift() + var list pairs = [] + while(tokens.peek().type != TokenType.BraceClose){ + var k = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, keyType)) + if(tokens.peek().type != TokenType.Assign){ + aspl.parser.utils.syntax_error("Expected '=>' after key in map declaration", tokens.peek().location) + } + tokens.shift() + var v = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, valueType)) + if(!Type:matches(keyType, k.getType())){ + aspl.parser.utils.type_error("Cannot add a key of type '" + k.getType().toString() + "' to a map of with the key type '" + keyType.toString() + "'", k.location) + } + if(!Type:matches(valueType, v.getType())){ + aspl.parser.utils.type_error("Cannot add a value of type '" + v.getType().toString() + "' to a map of with the value type '" + valueType.toString() + "'", v.location) + } + pairs.add(new Pair(k, v)) + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + } + } + tokens.shift() + return applyOperators(new MapLiteral(keyType, valueType, pairs, token.location), tokens, precedenceLevel) + } + if(Function:exists(IdentifierUtils:handleFunctionIdentifier(this, peekFunctionIdentifier(tokens, token).identifier)) && ((tokens.peek(peekFunctionIdentifier(tokens, token).tokenCount).type == TokenType.ParenthesisOpen) || ((tokens.peek(peekFunctionIdentifier(tokens, token).tokenCount).type == TokenType.Plus) && (tokens.peek(peekFunctionIdentifier(tokens, token).tokenCount + 1).type == TokenType.ParenthesisOpen)))){ + var identifierFirstToken = tokens.peek() + var identifierData = parseFunctionIdentifier(tokens, token) + var func = Function:get(IdentifierUtils:handleFunctionIdentifier(this, identifierData.identifier)) + var compileIdentifier = func.identifier + if(func oftype InternalFunction){ + compileIdentifier = "_" + compileIdentifier + } + var newThread = false + if(tokens.peek().type == TokenType.Plus){ + newThread = true + tokens.shift() + } + tokens.shift() + var list arguments = [] + foreach(func.parameters as parameter){ + if(tokens.peek().type == TokenType.ParenthesisClose){ + if(!parameter.optional){ + aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for function '" + func.identifier + "' (expected " + func.minimumParameterCount + ")", tokens.peek().location) + } + break + } + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, parameter.types)) + if(!Type:matches(parameter.types, value.getType())){ + aspl.parser.utils.type_error("Cannot pass a value of the type '" + value.getType().toString() + "' to a parameter of the type '" + parameter.types.toString() + "'", value.location) + } + arguments.add(value) + if(arguments.length < func.parameters.length && tokens.peek().type == TokenType.Comma){ + tokens.shift() + } + } + foreach(func.attributes as attribute){ + if(attribute.attribute.identifier == "deprecated"){ + if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){ + aspl.parser.utils.warning("Function " + func.identifier + " is deprecated: " + attribute.arguments[0].getConstantValue(), identifierFirstToken.location) + }else{ + aspl.parser.utils.warning("Function " + func.identifier + " is deprecated", identifierFirstToken.location) + } + } + } + if(func oftype CustomFunction && !func.isPublic && module.id != CustomFunction(func).module.id){ + aspl.parser.utils.warning("Cannot call the private function '" + func.identifier + "' here", identifierFirstToken.location) // TODO: Make this a generic_error after the grace period + } + var node = new FunctionCallExpression(compileIdentifier, func, arguments, newThread, token.location) + if(tokens.peek().type != TokenType.ParenthesisClose){ + if(arguments.length == 0 || tokens.peek().type == TokenType.Comma){ + aspl.parser.utils.fatal_error("Too many arguments given for function '" + func.identifier + "' (expected " + func.parameters.length + ")", tokens.peek().location) + }else{ + aspl.parser.utils.syntax_error("Expected ')' after arguments in function call", tokens.peek().location) + } + }else{ + tokens.shift() + } + return applyOperators(node, tokens, precedenceLevel) + }elseif(currentClass != null && Method:exists(Class(currentClass).type, token.value) && ((tokens.peek().type == TokenType.ParenthesisOpen) || (tokens.length > 1 && (tokens.peek().type == TokenType.Plus) && (tokens.peek(1).type == TokenType.ParenthesisOpen)))){ + var name = token + var newThread = false + if(tokens.peek().type == TokenType.Plus){ + newThread = true + tokens.shift() + } + var m = Method:get(Class(currentClass).type, name.value) + tokens.shift() + var list arguments = [] + foreach(m.parameters as parameter){ + if(tokens.peek().type == TokenType.ParenthesisClose){ + if(!parameter.optional){ + if(m.isStatic){ + aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for method '" + m.type.toString() + ":" + m.name + "' (expected " + m.minimumParameterCount + ")", tokens.peek().location) + }else{ + aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for method '" + m.type.toString() + "." + m.name + "' (expected " + m.minimumParameterCount + ")", tokens.peek().location) + } + } + break + } + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, parameter.types)) + if(!Type:matches(parameter.types, value.getType())){ + aspl.parser.utils.type_error("Cannot pass a value of the type '" + value.getType().toString() + "' to a parameter of the type '" + parameter.types.toString() + "'", value.location) + } + arguments.add(value) + if(arguments.length < m.parameters.length && tokens.peek().type == TokenType.Comma){ + tokens.shift() + } + } + foreach(m.attributes as attribute){ + if(attribute.attribute.identifier == "deprecated"){ + if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){ + if(m.isStatic){ + aspl.parser.utils.warning("Method " + m.type.toString() + ":" + m.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), name.location) + }else{ + aspl.parser.utils.warning("Method " + m.type.toString() + "." + m.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), name.location) + } + }else{ + if(m.isStatic){ + aspl.parser.utils.warning("Method " + m.type.toString() + ":" + m.name + " is deprecated", name.location) + }else{ + aspl.parser.utils.warning("Method " + m.type.toString() + "." + m.name + " is deprecated", name.location) + } + } + } + } + var Expression? node = null + if(m.isStatic){ + node = new StaticMethodCallExpression(m, Class(currentClass).type, arguments, newThread, token.location) + }else{ + Scope:passCapturedVariables(["this"]) + node = new NonStaticMethodCallExpression(m, new ThisExpression(Class(currentClass), token.location), null, arguments, newThread, token.location) + } + if(tokens.peek().type != TokenType.ParenthesisClose){ + if(arguments.length == 0 || tokens.peek().type == TokenType.Comma){ + if(m.isStatic){ + aspl.parser.utils.fatal_error("Too many arguments given for method '" + m.type.toString() + ":" + m.name + "' (expected " + m.parameters.length + ")", tokens.peek().location) + }else{ + aspl.parser.utils.fatal_error("Too many arguments given for method '" + m.type.toString() + "." + m.name + "' (expected " + m.parameters.length + ")", tokens.peek().location) + } + }else{ + aspl.parser.utils.syntax_error("Expected ')' after arguments in method call", tokens.peek().location) + } + }else{ + tokens.shift() + } + return applyOperators(Expression(node), tokens, precedenceLevel) + }elseif(currentClass != null && Property:exists(Class(currentClass).type, token.value)){ + var p = Property:get(Class(currentClass).type, token.value) + if(!tokens.empty()){ + if(tokens.peek().type == TokenType.Equals){ + tokens.shift() + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, p.types)) + if(!Type:matches(p.types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + p.types.toString() + "'", value.location) + } + foreach(p.attributes as attribute){ + if(attribute.attribute.identifier == "deprecated"){ + if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){ + if(p.isStatic){ + aspl.parser.utils.warning("Property " + p.type.toString() + ":" + p.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), token.location) + }else{ + aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), token.location) + } + }else{ + if(p.isStatic){ + aspl.parser.utils.warning("Property " + p.type.toString() + ":" + p.name + " is deprecated", token.location) + }else{ + aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated", token.location) + } + } + } + } + if(p.isStatic){ + return applyOperators(new StaticPropertyAssignExpression(p, p.type, value, token.location), tokens, precedenceLevel) + }else{ + Scope:passCapturedVariables(["this"]) + return applyOperators(new NonStaticPropertyAssignExpression(p, new ThisExpression(Class(currentClass), token.location), value, token.location), tokens, precedenceLevel) + } + } + } + foreach(p.attributes as attribute){ + if(attribute.attribute.identifier == "deprecated"){ + if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){ + if(p.isStatic){ + aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), token.location) + }else{ + aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), token.location) + } + }else{ + if(p.isStatic){ + aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated", token.location) + }else{ + aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated", token.location) + } + } + } + } + if(p.isStatic){ + return applyOperators(new StaticPropertyAccessExpression(p, Class(currentClass).type, token.location), tokens, precedenceLevel) + }else{ + Scope:passCapturedVariables(["this"]) + return applyOperators(new NonStaticPropertyAccessExpression(p, new ThisExpression(Class(currentClass), token.location), token.location), tokens, precedenceLevel) + } + } + }elseif(token.type == TokenType.ParenthesisOpen){ + var result = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None)) + if(tokens.peek().type == TokenType.ParenthesisClose){ + tokens.shift() + return applyOperators(result, tokens, precedenceLevel) + }else{ + aspl.parser.utils.syntax_error("Expected closing parenthesis", tokens.peek().location) + } + }elseif(token.type == TokenType.CheckEquals){ + return applyOperators(new CheckEqualsExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.CheckEquals)), token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.CheckNotEquals){ + return applyOperators(new NegateExpression(new CheckEqualsExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.CheckNotEquals)), token.location), token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.Negate){ + if(previousExpression == null){ + return applyOperators(new NegateExpression(aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Negate, null, new Types([Type:fromString("bool")]))), token.location), tokens, precedenceLevel) + } + if(!Options:enableErrorHandling){ + aspl.parser.utils.generic_error("Experimental error handling ('-enableErrorHandling') is not enabled for this build", token.location) + } + if(!ErrorUtils:canExpressionThrow(previousExpression)){ + aspl.parser.utils.syntax_error("Cannot propagate an error from an expression that is not error-prone", token.location) + } + if(!ErrorUtils:canCallableThrow(Scope:getCurrentBundle().func)){ + aspl.parser.utils.syntax_error("Cannot propagate an error in a callable that does not declare the [throws] attribute", token.location) + } + return applyOperators(new PropagateErrorExpression(previousExpression, token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.And){ + var a = Expression(previousExpression) + var b = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.And)) + if(a.getType().toString() != b.getType().toString()){ + aspl.parser.utils.type_error("Cannot use the '&&' (and) operator on values of the types " + a.getType().toString() + " and " + b.getType().toString(), token.location) + } + if(Enum:enums.containsKey(a.getType().toString())){ + if(!Enum:enums[a.getType().toString()].isFlags){ + aspl.parser.utils.type_error("The '&&' (and) operator can only be used with enums that have the 'flags' attribute", token.location) + } + } + return applyOperators(new AndExpression(a, b, token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.Or){ + var a = Expression(previousExpression) + var b = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Or)) + if(a.getType().toString() != b.getType().toString()){ + aspl.parser.utils.type_error("Cannot use the '||' (or) operator on values of the types " + a.getType().toString() + " and " + b.getType().toString(), token.location) + } + if(Enum:enums.containsKey(a.getType().toString())){ + if(!Enum:enums[a.getType().toString()].isFlags){ + aspl.parser.utils.type_error("The '||' (or) operator can only be used with enums that have the 'flags' attribute", token.location) + } + } + return applyOperators(new OrExpression(a, b, token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.Xor){ + var a = Expression(previousExpression) + var b = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Xor)) + if(a.getType().toString() != b.getType().toString()){ + aspl.parser.utils.type_error("Cannot use the '^' (xpr) operator on values of the types " + a.getType().toString() + " and " + b.getType().toString(), token.location) + } + if(Enum:enums.containsKey(a.getType().toString())){ + if(!Enum:enums[a.getType().toString()].isFlags){ + aspl.parser.utils.type_error("The '^' (xor) operator can only be used with enums that have the 'flags' attribute", token.location) + } + } + return applyOperators(new XorExpression(a, b, token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.Plus){ + if(previousExpression == null){ + return applyOperators(new PlusExpression(new IntegerLiteral(0, null), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Plus)), token.location), tokens, precedenceLevel) + } + return applyOperators(new PlusExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Plus)), token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.PlusEquals){ + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens)) + if(previousExpression oftype VariableAccessExpression){ + if(!Type:matches(VariableAccessExpression(previousExpression).variable.types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a variable of type '" + VariableAccessExpression(previousExpression).variable.types.toString() + "'", value.location) + } + return applyOperators(new VariableAssignExpression(VariableAccessExpression(previousExpression).variable, new PlusExpression(new VariableAccessExpression(VariableAccessExpression(previousExpression).variable, token.location), value, token.location), token.location), tokens, precedenceLevel) + }elseif(previousExpression oftype NonStaticPropertyAccessExpression){ + if(!Type:matches(NonStaticPropertyAccessExpression(previousExpression).p.types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + NonStaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location) + } + if(NonStaticPropertyAccessExpression(previousExpression).p.isReadPublic){ + if(currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type]))){ + aspl.parser.utils.warning("Cannot assign to the property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period + } + }elseif(!NonStaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type])))){ + aspl.parser.utils.warning("Cannot assign to the private property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new NonStaticPropertyAssignExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, new PlusExpression(new NonStaticPropertyAccessExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel) + }elseif(previousExpression oftype StaticPropertyAccessExpression){ + if(!Type:matches(StaticPropertyAccessExpression(previousExpression).p.types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + StaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location) + } + if(!Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].isPublic && module.id != Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].module.id){ + aspl.parser.utils.warning("Cannot assign to a property from the private class '" + Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period + } + if(StaticPropertyAccessExpression(previousExpression).p.isReadPublic){ + if(currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type]))){ + aspl.parser.utils.warning("Cannot assign to the property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period + } + }elseif(!StaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type])))){ + aspl.parser.utils.warning("Cannot assign to the private property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new StaticPropertyAssignExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, new PlusExpression(new StaticPropertyAccessExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel) + } + aspl.parser.utils.syntax_error("The '+=' operator must be used on something assignable, e.g. a variable", token.location) + }elseif(token.type == TokenType.PlusPlus){ + if(previousExpression oftype VariableAccessExpression){ + return applyOperators(new VariableAssignExpression(VariableAccessExpression(previousExpression).variable, new PlusExpression(new VariableAccessExpression(VariableAccessExpression(previousExpression).variable, token.location), new IntegerLiteral(1, token.location), token.location), token.location), tokens, precedenceLevel) + }elseif(previousExpression oftype NonStaticPropertyAccessExpression){ + if(NonStaticPropertyAccessExpression(previousExpression).p.isReadPublic){ + if(currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type]))){ + aspl.parser.utils.warning("Cannot assign to the property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period + } + }elseif(!NonStaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type])))){ + aspl.parser.utils.warning("Cannot assign to the private property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new NonStaticPropertyAssignExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, new PlusExpression(new NonStaticPropertyAccessExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, token.location), new IntegerLiteral(1, token.location), token.location), token.location), tokens, precedenceLevel) + }elseif(previousExpression oftype StaticPropertyAccessExpression){ + if(!Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].isPublic && module.id != Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].module.id){ + aspl.parser.utils.warning("Cannot assign to a property from the private class '" + Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period + } + if(StaticPropertyAccessExpression(previousExpression).p.isReadPublic && currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type]))){ + aspl.parser.utils.warning("Cannot assign to the property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period + } + if(!StaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type])))){ + aspl.parser.utils.warning("Cannot assign to the private property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new StaticPropertyAssignExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, new PlusExpression(new StaticPropertyAccessExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, token.location), new IntegerLiteral(1, token.location), token.location), token.location), tokens, precedenceLevel) + } + aspl.parser.utils.syntax_error("The '++' operator must be used on something assignable, e.g. a variable", token.location) + }elseif(token.type == TokenType.Minus){ + if(previousExpression == null){ + return applyOperators(new MinusExpression(new IntegerLiteral(0, null), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Minus)), token.location), tokens, precedenceLevel) + } + return applyOperators(new MinusExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Minus)), token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.MinusEquals){ + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens)) + if(previousExpression oftype VariableAccessExpression){ + if(!Type:matches(VariableAccessExpression(previousExpression).variable.types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a variable of type '" + VariableAccessExpression(previousExpression).variable.types.toString() + "'", value.location) + } + return applyOperators(new VariableAssignExpression(VariableAccessExpression(previousExpression).variable, new MinusExpression(new VariableAccessExpression(VariableAccessExpression(previousExpression).variable, token.location), value, token.location), token.location), tokens, precedenceLevel) + }elseif(previousExpression oftype NonStaticPropertyAccessExpression){ + if(!Type:matches(NonStaticPropertyAccessExpression(previousExpression).p.types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + NonStaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location) + } + if(NonStaticPropertyAccessExpression(previousExpression).p.isReadPublic){ + if(currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type]))){ + aspl.parser.utils.warning("Cannot assign to the property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period + } + }elseif(!NonStaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type])))){ + aspl.parser.utils.warning("Cannot assign to the private property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new NonStaticPropertyAssignExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, new MinusExpression(new NonStaticPropertyAccessExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel) + }elseif(previousExpression oftype StaticPropertyAccessExpression){ + if(!Type:matches(StaticPropertyAccessExpression(previousExpression).p.types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + StaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location) + } + if(!Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].isPublic && module.id != Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].module.id){ + aspl.parser.utils.warning("Cannot assign to a property from the private class '" + Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period + } + if(StaticPropertyAccessExpression(previousExpression).p.isReadPublic && currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type]))){ + aspl.parser.utils.warning("Cannot assign to the property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period + } + if(!StaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type])))){ + aspl.parser.utils.warning("Cannot assign to the private property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new StaticPropertyAssignExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, new MinusExpression(new StaticPropertyAccessExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel) + } + aspl.parser.utils.syntax_error("The '-=' operator must be used on something assignable, e.g. a variable", token.location) + }elseif(token.type == TokenType.MinusMinus){ + if(previousExpression oftype VariableAccessExpression){ + return applyOperators(new VariableAssignExpression(VariableAccessExpression(previousExpression).variable, new MinusExpression(new VariableAccessExpression(VariableAccessExpression(previousExpression).variable, token.location), new IntegerLiteral(1, token.location), token.location), token.location), tokens, precedenceLevel) + }elseif(previousExpression oftype NonStaticPropertyAccessExpression){ + if(NonStaticPropertyAccessExpression(previousExpression).p.isReadPublic){ + if(currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type]))){ + aspl.parser.utils.warning("Cannot assign to the property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period + } + }elseif(!NonStaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type])))){ + aspl.parser.utils.warning("Cannot assign to the private property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new NonStaticPropertyAssignExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, new MinusExpression(new NonStaticPropertyAccessExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, token.location), new IntegerLiteral(1, token.location), token.location), token.location), tokens, precedenceLevel) + }elseif(previousExpression oftype StaticPropertyAccessExpression){ + if(!Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].isPublic && module.id != Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].module.id){ + aspl.parser.utils.warning("Cannot assign to a property from the private class '" + Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period + } + if(StaticPropertyAccessExpression(previousExpression).p.isReadPublic && currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type]))){ + aspl.parser.utils.warning("Cannot assign to the property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period + } + if(!StaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type])))){ + aspl.parser.utils.warning("Cannot assign to the private property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new StaticPropertyAssignExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, new MinusExpression(new StaticPropertyAccessExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, token.location), new IntegerLiteral(1, token.location), token.location), token.location), tokens, precedenceLevel) + } + aspl.parser.utils.syntax_error("The '--' operator must be used on something assignable, e.g. a variable", token.location) + }elseif(token.type == TokenType.Asterisk){ + if(previousExpression == null){ + var pointer = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Reference)) + if(!pointer.getType().isPointer()){ + aspl.parser.utils.syntax_error("Cannot dereference a value of type '" + pointer.getType().toString() + "' (not a pointer)", pointer.location) + } + return applyOperators(new DereferenceExpression(pointer, token.location), tokens, precedenceLevel) + } + return applyOperators(new MultiplyExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Asterisk)), token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.MultiplyEquals){ + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens)) + if(previousExpression oftype VariableAccessExpression){ + if(!Type:matches(VariableAccessExpression(previousExpression).variable.types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a variable of type '" + VariableAccessExpression(previousExpression).variable.types.toString() + "'", value.location) + } + return applyOperators(new VariableAssignExpression(VariableAccessExpression(previousExpression).variable, new MultiplyExpression(new VariableAccessExpression(VariableAccessExpression(previousExpression).variable, token.location), value, token.location), token.location), tokens, precedenceLevel) + }elseif(previousExpression oftype NonStaticPropertyAccessExpression){ + if(!Type:matches(NonStaticPropertyAccessExpression(previousExpression).p.types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + NonStaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location) + } + if(NonStaticPropertyAccessExpression(previousExpression).p.isReadPublic){ + if(currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type]))){ + aspl.parser.utils.warning("Cannot assign to the property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period + } + }elseif(!NonStaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type])))){ + aspl.parser.utils.warning("Cannot assign to the private property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new NonStaticPropertyAssignExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, new MultiplyExpression(new NonStaticPropertyAccessExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel) + }elseif(previousExpression oftype StaticPropertyAccessExpression){ + if(!Type:matches(StaticPropertyAccessExpression(previousExpression).p.types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + StaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location) + } + if(!Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].isPublic && module.id != Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].module.id){ + aspl.parser.utils.warning("Cannot assign to a property from the private class '" + Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period + } + if(StaticPropertyAccessExpression(previousExpression).p.isReadPublic && currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type]))){ + aspl.parser.utils.warning("Cannot assign to the property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period + } + if(!StaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type])))){ + aspl.parser.utils.warning("Cannot assign to the private property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new StaticPropertyAssignExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, new MultiplyExpression(new StaticPropertyAccessExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel) + } + aspl.parser.utils.syntax_error("The '*=' operator must be used on something assignable, e.g. a variable", token.location) + }elseif(token.type == TokenType.Slash){ + return applyOperators(new DivideExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Slash)), token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.DivideEquals){ + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens)) + if(previousExpression oftype VariableAccessExpression){ + if(!Type:matches(VariableAccessExpression(previousExpression).variable.types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a variable of type '" + Variable:get(token.value).types.toString() + "'", value.location) + } + return applyOperators(new VariableAssignExpression(VariableAccessExpression(previousExpression).variable, new DivideExpression(new VariableAccessExpression(VariableAccessExpression(previousExpression).variable, token.location), value, token.location), token.location), tokens, precedenceLevel) + }elseif(previousExpression oftype NonStaticPropertyAccessExpression){ + if(!Type:matches(NonStaticPropertyAccessExpression(previousExpression).p.types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + NonStaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location) + } + if(NonStaticPropertyAccessExpression(previousExpression).p.isReadPublic){ + if(currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type]))){ + aspl.parser.utils.warning("Cannot assign to the property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period + } + }elseif(!NonStaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type])))){ + aspl.parser.utils.warning("Cannot assign to the private property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new NonStaticPropertyAssignExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, new DivideExpression(new NonStaticPropertyAccessExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel) + }elseif(previousExpression oftype StaticPropertyAccessExpression){ + if(!Type:matches(StaticPropertyAccessExpression(previousExpression).p.types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + StaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location) + } + if(!Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].isPublic && module.id != Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].module.id){ + aspl.parser.utils.warning("Cannot assign to a property from the private class '" + Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period + } + if(StaticPropertyAccessExpression(previousExpression).p.isReadPublic && currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type]))){ + aspl.parser.utils.warning("Cannot assign to the property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period + } + if(!StaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type])))){ + aspl.parser.utils.warning("Cannot assign to the private property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new StaticPropertyAssignExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, new DivideExpression(new StaticPropertyAccessExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel) + } + aspl.parser.utils.syntax_error("The '/=' operator must be used on something assignable, e.g. a variable", token.location) + }elseif(token.type == TokenType.Modulo){ + return applyOperators(new ModuloExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Modulo)), token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.ModuloEquals){ + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens)) + if(previousExpression oftype VariableAccessExpression){ + if(!Type:matches(VariableAccessExpression(previousExpression).variable.types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a variable of type '" + Variable:get(token.value).types.toString() + "'", value.location) + } + return applyOperators(new VariableAssignExpression(VariableAccessExpression(previousExpression).variable, new ModuloExpression(new VariableAccessExpression(VariableAccessExpression(previousExpression).variable, token.location), value, token.location), token.location), tokens, precedenceLevel) + }elseif(previousExpression oftype NonStaticPropertyAccessExpression){ + if(!Type:matches(NonStaticPropertyAccessExpression(previousExpression).p.types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + NonStaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location) + } + if(NonStaticPropertyAccessExpression(previousExpression).p.isReadPublic){ + if(currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type]))){ + aspl.parser.utils.warning("Cannot assign to the property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period + } + }elseif(!NonStaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type])))){ + aspl.parser.utils.warning("Cannot assign to the private property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new NonStaticPropertyAssignExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, new ModuloExpression(new NonStaticPropertyAccessExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel) + }elseif(previousExpression oftype StaticPropertyAccessExpression){ + if(!Type:matches(StaticPropertyAccessExpression(previousExpression).p.types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + StaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location) + } + if(!Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].isPublic && module.id != Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].module.id){ + aspl.parser.utils.warning("Cannot assign to a property from the private class '" + Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period + } + if(StaticPropertyAccessExpression(previousExpression).p.isReadPublic && currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type]))){ + aspl.parser.utils.warning("Cannot assign to the property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period + } + if(!StaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type])))){ + aspl.parser.utils.warning("Cannot assign to the private property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new StaticPropertyAssignExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, new ModuloExpression(new StaticPropertyAccessExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel) + } + aspl.parser.utils.syntax_error("The '%=' operator must be used on something assignable, e.g. a variable", token.location) + }elseif(token.type == TokenType.LessThan){ + return applyOperators(new LessThanExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.LessThan)), token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.LessThanOrEqual){ + return applyOperators(new LessThanOrEqualExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.LessThanOrEqual)), token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.GreaterThan){ + return applyOperators(new GreaterThanExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.GreaterThan)), token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.GreaterThanOrEqual){ + return applyOperators(new GreaterThanOrEqualExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.GreaterThanOrEqual)), token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.Ampersand){ + if(previousExpression == null){ + var expression = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Reference)) + if(!(expression oftype VariableAccessExpression || expression oftype NonStaticPropertyAccessExpression || expression oftype StaticPropertyAccessExpression)){ + aspl.parser.utils.syntax_error("The '&' operator must be used on something holding an object reference, e.g. a variable", token.location) + } + return applyOperators(new ReferenceExpression(expression, token.location), tokens, precedenceLevel) + } + aspl.parser.utils.syntax_error("Unxpected '&'", token.location) + }elseif(token.type == TokenType.Dot){ + if(previousExpression == null){ + aspl.parser.utils.syntax_error("Unexpected '" + token.value + "'", token.location) + } + var name = tokens.next() + if(name.type == TokenType.ParenthesisOpen || (tokens.length > 0 && name.type == TokenType.Plus && tokens.peek().type == TokenType.ParenthesisOpen)){ + if(!Type:matches(new Types([Type:fromString("callback")]), Expression(previousExpression).getType(), true)){ + aspl.parser.utils.syntax_error("Only callbacks can be invoked using the .(...) syntax", token.location) + } + var newThread = false + if(name.type == TokenType.Plus){ + newThread = true + tokens.shift() + } + var m = Method:get(Expression(previousExpression).getType().toType(), "invoke") + var list arguments = [] + foreach(m.parameters as parameter){ + if(tokens.peek().type == TokenType.ParenthesisClose){ + if(!parameter.optional){ + aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for method '" + m.type.toString() + ".invoke' (expected " + m.minimumParameterCount + ")", tokens.peek().location) + } + break + } + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, parameter.types)) + if(!Type:matches(parameter.types, value.getType())){ + aspl.parser.utils.type_error("Cannot pass a value of the type '" + value.getType().toString() + "' to a parameter of the type '" + parameter.types.toString() + "'", value.location) + } + arguments.add(value) + if(arguments.length < m.parameters.length && tokens.peek().type == TokenType.Comma){ + tokens.shift() + } + } + var node = new NonStaticMethodCallExpression(m, Expression(previousExpression), null, arguments, newThread, token.location) + if(tokens.peek().type != TokenType.ParenthesisClose){ + if(arguments.length == 0 || tokens.peek().type == TokenType.Comma){ + aspl.parser.utils.fatal_error("Too many arguments (" + arguments.length + ") given for method '" + m.type.toString() + ".invoke' (expected " + m.parameters.length + ")", tokens.peek().location) + }else{ + aspl.parser.utils.syntax_error("Expected ')' after arguments in callback invocation", tokens.peek().location) + } + }else{ + tokens.shift() + } + return applyOperators(node, tokens, precedenceLevel) + } + if(name.type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected identifier after '.'", name.location) + } + if(tokens.length > 0 && (tokens.peek().type == TokenType.ParenthesisOpen || (tokens.length > 1 && tokens.peek().type == TokenType.Plus && tokens.peek(1).type == TokenType.ParenthesisOpen))){ + if(!Method:exists(Expression(previousExpression).getType().toType(), name.value)){ + aspl.parser.utils.fatal_error("Unknown method " + Expression(previousExpression).getType().toType().toString() + "." + name.value, name.location) + } + var newThread = false + if(tokens.peek().type == TokenType.Plus){ + newThread = true + tokens.shift() + } + var m = Method:get(Expression(previousExpression).getType().toType(), name.value) + if(m.isStatic){ + aspl.parser.utils.type_error("Cannot call the static method " + Expression(previousExpression).getType().toType().toString() + ":" + name.value + " non-statically", name.location) + } + tokens.shift() + var list arguments = [] + foreach(m.parameters as parameter){ + if(tokens.peek().type == TokenType.ParenthesisClose){ + if(!parameter.optional){ + aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for method '" + m.type.toString() + "." + m.name + "' (expected " + m.parameters.length + ")", tokens.peek().location) + } + break + } + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, parameter.types)) + if(!Type:matches(parameter.types, value.getType())){ + aspl.parser.utils.type_error("Cannot pass a value of the type '" + value.getType().toString() + "' to a parameter of the type '" + parameter.types.toString() + "'", value.location) + } + arguments.add(value) + if(arguments.length < m.parameters.length && tokens.peek().type == TokenType.Comma){ + tokens.shift() + } + } + foreach(m.attributes as attribute){ + if(attribute.attribute.identifier == "deprecated"){ + if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){ + aspl.parser.utils.warning("Method " + m.type.toString() + "." + m.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), name.location) + }else{ + aspl.parser.utils.warning("Method " + m.type.toString() + "." + m.name + " is deprecated", name.location) + } + } + } + if(m oftype CustomMethod && !m.isPublic && (currentClass == null || !Type:matches(new Types([m.type]), new Types([currentClass?!.type])))){ + aspl.parser.utils.warning("Cannot call the private method '" + m.type.identifier + "." + m.name + "' here", token.location) // TODO: Make this a generic_error after the grace period + } + var Class? exactClass = null + if(previousExpression oftype ParentExpression){ + exactClass = ParentExpression(previousExpression).c + } + var node = new NonStaticMethodCallExpression(m, Expression(previousExpression), exactClass, arguments, newThread, token.location) + if(tokens.peek().type != TokenType.ParenthesisClose){ + if(arguments.length == 0 || tokens.peek().type == TokenType.Comma){ + aspl.parser.utils.fatal_error("Too many arguments (" + arguments.length + ") given for method '" + m.type.toString() + "." + m.name + "' (expected " + m.parameters.length + ")", tokens.peek().location) + }else{ + aspl.parser.utils.syntax_error("Expected ')' after arguments in method call", tokens.peek().location) + } + }else{ + tokens.shift() + } + return applyOperators(node, tokens, precedenceLevel) + }else{ + if(!Property:exists(Expression(previousExpression).getType().toType(), name.value)){ + aspl.parser.utils.fatal_error("Unknown property " + Expression(previousExpression).getType().toType().toString() + "." + name.value + "", name.location) + } + var p = Property:get(Expression(previousExpression).getType().toType(), name.value) + if(p.isStatic){ + aspl.parser.utils.type_error("Cannot access the static property " + p.type.toString() + ":" + p.name + " non-statically", name.location) + } + if(!tokens.empty()){ + if(tokens.peek().type == TokenType.Equals){ + tokens.shift() + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, p.types)) + if(!Type:matches(p.types, value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + p.types.toString() + "'", value.location) + } + foreach(p.attributes as attribute){ + if(attribute.attribute.identifier == "deprecated"){ + if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){ + aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), name.location) + }else{ + aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated", name.location) + } + } + } + if(p.isReadPublic){ + if(currentClass != null && !Type:matches(new Types([p.type]), new Types([currentClass?!.type]))){ + aspl.parser.utils.warning("Cannot assign to the property " + p.type.identifier + "." + p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period + } + }elseif(!p.isPublic && (currentClass == null || !Type:matches(new Types([p.type]), new Types([currentClass?!.type])))){ + aspl.parser.utils.warning("Cannot assign to the private property " + p.type.identifier + "." + p.name + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new NonStaticPropertyAssignExpression(p, Expression(previousExpression), value, token.location), tokens, precedenceLevel) + } + } + foreach(p.attributes as attribute){ + if(attribute.attribute.identifier == "deprecated"){ + if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){ + aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), name.location) + }else{ + aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated", name.location) + } + } + } + if(!p.isPublic && !p.isReadPublic && (currentClass == null || !Type:matches(new Types([p.type]), new Types([currentClass?!.type])))){ + aspl.parser.utils.warning("Cannot access the private property " + p.type.identifier + "." + p.name + " here", token.location) // TODO: Make this a generic_error after the grace period + } + return applyOperators(new NonStaticPropertyAccessExpression(p, Expression(previousExpression), token.location), tokens, precedenceLevel) + } + }elseif(token.type == TokenType.BracketOpen){ + if(AttributeUtils:parseAttributesIfAny(this, token, tokens)){ + return new NopStatement() + } + if(previousExpression != null){ + if(Type:matches(new Types([Type:fromString("list")]), Expression(previousExpression).getType(), true)){ + var index = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("integer")]))) + if(!Type:matches(new Types([Type:fromString("integer")]), index.getType())){ + aspl.parser.utils.syntax_error("Cannot index a list with a value of type '" + index.getType().toString() + "' (must be integer)", index.location) + } + if(tokens.peek().type == TokenType.BracketClose){ + tokens.shift() + }else{ + aspl.parser.utils.syntax_error("Expected ']' after index", tokens.peek().location) + } + if(tokens.peek().type == TokenType.Equals){ + tokens.shift() + var Types? types = null + foreach(Expression(previousExpression).getType().types as type){ + if(Type:getGenericTypes(type.toString()).length > 0){ + types = Type:getGenericTypes(type.toString())[0] + } + } + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, Types(types))) + foreach(Expression(previousExpression).getType().types as type){ + if(Type:getGenericTypes(type.toString()).length > 0){ + if(!Type:matches(Type:getGenericTypes(type.toString())[0], value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a list of the type '" + type.toString() + "'", value.location) + } + } + } + return applyOperators(new ListAssignExpression(Expression(previousExpression), index, value, token.location), tokens, precedenceLevel) + } + return applyOperators(new ListIndexExpression(Expression(previousExpression), index, token.location), tokens, precedenceLevel) + }elseif(Type:matches(new Types([Type:fromString("map")]), Expression(previousExpression).getType(), true)){ + var k = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens)) + foreach(Expression(previousExpression).getType().types as type){ + if(Type:getGenericTypes(type.toString()).length > 0){ + if(!Type:matches(Type:getGenericTypes(type.toString())[0], k.getType())){ + aspl.parser.utils.type_error("Cannot access a map with the key type '" + type.toString() + "' with a key of the type '" + k.getType().toString() + "'", k.location) + } + } + } + if(tokens.peek().type == TokenType.BracketClose){ + tokens.shift() + }else{ + aspl.parser.utils.syntax_error("Expected ']' after map access", tokens.peek().location) + } + if(tokens.peek().type == TokenType.Equals){ + tokens.shift() + var Types? types = null + foreach(Expression(previousExpression).getType().types as type){ + if(Type:getGenericTypes(type.toString()).length > 0){ + types = Type:getGenericTypes(type.toString())[1] + } + } + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, Types(types))) + foreach(Expression(previousExpression).getType().types as type){ + if(Type:getGenericTypes(type.toString()).length > 1){ + if(!Type:matches(Type:getGenericTypes(type.toString())[1], value.getType())){ + aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a map with the value type '" + type.toString() + "'", value.location) + } + } + } + return applyOperators(new MapAssignExpression(Expression(previousExpression), k, value, token.location), tokens, precedenceLevel) + } + return applyOperators(new MapAccessExpression(Expression(previousExpression), k, token.location), tokens, precedenceLevel) + }elseif(Type:matches(new Types([Type:fromString("string")]), Expression(previousExpression).getType())){ + var index = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("integer")]))) + if(!Type:matches(new Types([Type:fromString("integer")]), index.getType())){ + aspl.parser.utils.syntax_error("Cannot index a string with a value of type '" + index.getType().toString() + "' (must be integer)", index.location) + } + if(tokens.peek().type == TokenType.BracketClose){ + tokens.shift() + }else{ + aspl.parser.utils.syntax_error("Expected ']' after index", tokens.peek().location) + } + if(tokens.peek().type == TokenType.Equals){ + aspl.parser.utils.syntax_error("Cannot modify a string", tokens.peek().location) + } + return applyOperators(new StringIndexExpression(Expression(previousExpression), index, token.location), tokens, precedenceLevel) + }else{ + aspl.parser.utils.syntax_error("Cannot index a value of the type '" + Expression(previousExpression).getType().toString() + "'", token.location) + } + } + var Types? type = null + var list values = [] + while(tokens.peek().type != TokenType.BracketClose){ + var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, type)) + if(type == null){ + type = value.getType() + } + if(!Type:matches(type, value.getType())){ + aspl.parser.utils.type_error("Cannot add a value of type '" + value.getType().toString() + "' to a list of type '" + Types(type).toString() + "'", value.location) + } + values.add(value) + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + } + } + if(type == null){ + var found = false + if(expectedTypes != null){ + foreach(Types(expectedTypes).types as t){ + if(Type:matches(new Types([Type:fromString("list")]), new Types([t]), true)){ + if(Type:getGenericTypes(t.toString()).length > 0){ + type = Type:getGenericTypes(t.toString())[0] + }else{ + type = new Types([]) + } + found = true + break + } + } + } + if(!found){ + aspl.parser.utils.type_error("List type not specified", token.location) + type = new Types([]) + } + } + tokens.shift() + return applyOperators(new ListLiteral(Types(type), values, token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.BraceOpen){ + if(standalone){ + return new BlockStatement(parseBlock(tokens, token), false, token.location) + }else{ + var Types? keyType = null + var Types? valueType = null + var list pairs = [] + while(tokens.peek().type != TokenType.BraceClose){ + var k = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, keyType)) + if(keyType == null){ + keyType = k.getType() + } + if(tokens.peek().type != TokenType.Assign){ + aspl.parser.utils.syntax_error("Expected '=>' after key in map declaration", tokens.peek().location) + } + tokens.shift() + var v = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, valueType)) + if(valueType == null){ + valueType = v.getType() + } + if(!Type:matches(keyType, k.getType())){ + aspl.parser.utils.type_error("Cannot add a key of type '" + k.getType().toString() + "' to a map of with the key type '" + Types(keyType).toString() + "'", k.location) + } + if(!Type:matches(valueType, v.getType())){ + aspl.parser.utils.type_error("Cannot add a value of type '" + v.getType().toString() + "' to a map of with the value type '" + Types(valueType).toString() + "'", v.location) + } + pairs.add(new Pair(k, v)) + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + } + } + tokens.shift() + if(keyType == null){ + var found = false + if(expectedTypes != null){ + foreach(Types(expectedTypes).types as t){ + if(Type:matches(new Types([Type:fromString("map")]), new Types([t]), true)){ + if(Type:getGenericTypes(t.toString()).length > 0){ + keyType = Type:getGenericTypes(t.toString())[0] + }else{ + keyType = new Types([]) + valueType = new Types([]) + } + if(Type:getGenericTypes(t.toString()).length > 1){ + valueType = Type:getGenericTypes(t.toString())[1] + }else{ + valueType = new Types([]) + } + found = true + break + } + } + } + if(!found){ + aspl.parser.utils.type_error("Map key type not specified", token.location) + keyType = new Types([]) + } + } + if(valueType == null){ + aspl.parser.utils.type_error("Map value type not specified", token.location) + valueType = new Types([]) + } + return applyOperators(new MapLiteral(keyType, valueType, pairs, token.location), tokens, precedenceLevel) + } + }elseif(token.type == TokenType.QuestionAndExclamationMark){ + if(previousExpression == null){ + aspl.parser.utils.syntax_error("Unexpected ?! (can only be used after nullable expressions)", token.location) + } + if(!Type:matches(Expression(previousExpression).getType(), new Types([Type:fromString("null")]))){ + aspl.parser.utils.type_error("The ?! operator can only be used on nullable expressions (expression of type " + Expression(previousExpression).getType().toString() + " given)", token.location) + } + return applyOperators(new CastExpression(Expression(previousExpression), Expression(previousExpression).getType().withoutType(Type:fromString("null")).toType(), token.location), tokens, precedenceLevel) + }elseif(token.type == TokenType.Dollar){ + if(tokens.peek().type == TokenType.Identifier){ + if(tokens.peek().value == "if"){ + tokens.shift() + var bool negate = false + if(tokens.peek().type == TokenType.Negate){ + negate = true + tokens.shift() + } + if(tokens.peek().type != TokenType.Identifier){ + aspl.parser.utils.syntax_error("Expected identifier after compile-time if ($if)", tokens.peek().location) + } + var string condition = tokens.next().value + if(tokens.peek().type != TokenType.BraceOpen){ + aspl.parser.utils.syntax_error("Expected { after condition in compile-time if ($if)") + } + var brace = tokens.peek() + var block = parseBlock(tokens) + var list? elseBlock = null + if(!tokens.empty()){ + if(tokens.peek().type == TokenType.Dollar){ + if(tokens.length > 1 && tokens.peek(1).type == TokenType.Identifier && tokens.peek(1).value == "else"){ + tokens.shift() + tokens.shift() + if(tokens.peek().type != TokenType.BraceOpen){ + aspl.parser.utils.syntax_error("Expected { after else in compile-time if ($if)") + } + elseBlock = parseBlock(tokens) + }elseif(tokens.peek().value == "elseif"){ + aspl.parser.utils.syntax_error("Compile-time elseif is not supported yet") + } + }elseif(tokens.peek().type == TokenType.Identifier && tokens.peek().value == "else"){ + aspl.parser.utils.syntax_error("Use `$else` instead if regular `else` after compile-time if ($if)") + } + } + if((Options:getConditionCompilationSymbols().contains(condition) || (condition == "main" && module == Module:mainModule)) != negate){ + return new BlockStatement(block, true, brace.location) + } + if(elseBlock != null){ + return new BlockStatement(list(elseBlock), true, brace.location) + }else{ + return new NopStatement() + } + }elseif(tokens.peek().value == "embed"){ + tokens.shift() + if(tokens.peek().type != TokenType.ParenthesisOpen){ + aspl.parser.utils.syntax_error("Expected ( after compile-time embed ($embed)") + } + tokens.shift() + if(tokens.peek().type != TokenType.String){ + aspl.parser.utils.syntax_error("Expected string after compile-time embed ($embed)") + } + var string file = io.join_path([io.full_directory_path(io.abs(file)), tokens.next().value]) + if(tokens.peek().type != TokenType.ParenthesisClose){ + aspl.parser.utils.syntax_error("Expected ) after compile-time embed ($embed)") + } + tokens.shift() + return applyOperators(new EmbedFileExpression(file, token.location), tokens, precedenceLevel) + }elseif(tokens.peek().value == "include"){ + tokens.shift() + if(tokens.peek().type != TokenType.ParenthesisOpen){ + aspl.parser.utils.syntax_error("Expected ( after compile-time include ($include)") + } + tokens.shift() + if(tokens.peek().type != TokenType.String){ + aspl.parser.utils.syntax_error("Expected string after compile-time include ($include)") + } + var string file = io.join_path([io.full_directory_path(io.abs(file)), tokens.next().value]) + if(tokens.peek().type != TokenType.ParenthesisClose){ + aspl.parser.utils.syntax_error("Expected ) after compile-time include ($include)") + } + tokens.shift() + return new IncludeFileStatement(file, token.location) + }elseif(tokens.peek().value == "link"){ + tokens.shift() + if(tokens.peek().type != TokenType.ParenthesisOpen){ + aspl.parser.utils.syntax_error("Expected ( after compile-time link ($link)") + } + tokens.shift() + if(tokens.peek().type != TokenType.String){ + aspl.parser.utils.syntax_error("Expected string after compile-time link ($link)") + } + var string library = tokens.next().value // TODO: Also support files as libraries + if(tokens.peek().type != TokenType.ParenthesisClose){ + aspl.parser.utils.syntax_error("Expected ) after compile-time link ($link)") + } + tokens.shift() + return new LinkLibraryStatement(library, token.location) + } + } + } + aspl.parser.utils.syntax_error("Unexpected token '" + token.value + "' (" + token.type + ")", token.location) + } + + method applyOperators(Expression expression, TokenList tokens, PrecedenceLevel currentLevel) returns Expression{ + if(ErrorUtils:canExpressionThrow(expression) && (tokens.empty() || (tokens.peek().type != TokenType.Negate && tokens.peek().value != "catch"))){ // TODO: Move this somewhere else for performance & readability + aspl.parser.utils.syntax_error("Expected '!' or 'catch' after invocation of an error-prone callable", expression.location) + } + if(tokens.empty()){ + return expression + } + var nextToken = tokens.peek() + if(currentLevel < PrecedenceUtils:GetTokenPrecendenceLevel(nextToken)){ + var result = parseToken(nextToken, tokens, false, PrecedenceUtils:GetTokenPrecendenceLevel(tokens.next()), expression) + if(result oftype Expression){ + return applyOperators(Expression(result), tokens, currentLevel) + } + } + return expression + } + + method peekFunctionIdentifier(TokenList tokens, Token? first = null) returns IdentifierResult{ + var identifier = "" + var i = 0 + if(first != null){ + identifier = Token(first).value + }else{ + if(tokens.peek().type != TokenType.Identifier){ + return new IdentifierResult("", 0, null) + } + first = tokens.peek() + identifier = Token(first).value + i++ + } + while(true){ + if(tokens.length - 1 < i || tokens.peek(i).type != TokenType.Dot){ + break + } + i++ + identifier += "." + tokens.peek(i).value + i++ + } + return new IdentifierResult(identifier, i, Token(first).location) + } + + method parseFunctionIdentifier(TokenList tokens, Token? first = null) returns IdentifierResult{ + var identifier = "" + var i = 0 + if(first != null){ + identifier = Token(first).value + }else{ + if(tokens.peek().type != TokenType.Identifier){ + return new IdentifierResult("", 0, null) + } + first = tokens.next() + identifier = Token(first).value + i++ + } + while(true){ + if(tokens.length == 0 || tokens.peek().type != TokenType.Dot){ + break + } + tokens.shift() + i++ + identifier += "." + tokens.next().value + i++ + } + return new IdentifierResult(identifier, i, Token(first).location) + } + + [public] + method peekTypeIdentifier(TokenList tokens, Token? first = null) returns IdentifierResult{ + var identifier = "" + var i = 0 + if(first != null){ + identifier = Token(first).value + }else{ + if(tokens.peek().type != TokenType.Identifier){ + return new IdentifierResult("", 0, null) + } + first = tokens.peek() + identifier = Token(first).value + i++ + } + while(true){ + if(tokens.length - 1 < i){ + break + } + if(tokens.peek(i).type == TokenType.LessThan){ + var genericIdentifier = "<" + var tokenCountWithoutGeneric = i + i++ + while(true){ + if(tokens.length - 1 < i){ + i -= tokenCountWithoutGeneric + return new IdentifierResult(identifier, i, Token(first).location) + } + if(tokens.peek(i).value == "returns"){ + genericIdentifier += "returns " + i++ + } + var peeked = peekTypeIdentifier(tokens.in(i), null) + genericIdentifier += peeked.identifier + i += peeked.tokenCount + if(tokens.peek(i).type == TokenType.Pipe){ + genericIdentifier += "|" + i++ + }elseif(tokens.peek(i).type == TokenType.Comma){ + genericIdentifier += ", " + i++ + }elseif(tokens.peek(i).type == TokenType.GreaterThan){ + genericIdentifier += ">" + identifier += genericIdentifier + i++ + if(tokens.peek(i).type == TokenType.Asterisk){ + while(tokens.length > i && tokens.peek(i).type == TokenType.Asterisk){ + identifier += "*" + i++ + } + } + return new IdentifierResult(identifier, i, Token(first).location) + }else{ + i -= tokenCountWithoutGeneric + return new IdentifierResult(identifier, i, Token(first).location) + } + } + genericIdentifier += ">" + identifier += genericIdentifier + i++ + break + } + if(tokens.peek(i).type == TokenType.Asterisk){ + while(tokens.length > i && tokens.peek(i).type == TokenType.Asterisk){ + identifier += "*" + i++ + } + break + }elseif(tokens.peek(i).type != TokenType.Dot){ + break + } + i++ + identifier += "." + tokens.peek(i).value + i++ + } + return new IdentifierResult(identifier, i, Token(first).location) + } + + [public] + method parseTypeIdentifier(TokenList tokens, Token? first = null) returns IdentifierResult{ + var identifier = "" + var i = 0 + if(first != null){ + identifier = Token(first).value + }else{ + if(tokens.peek().type != TokenType.Identifier){ + return new IdentifierResult("", 0, null) + } + first = tokens.next() + identifier = Token(first).value + i++ + } + while(true){ + if(tokens.length == 0){ + break + } + if(tokens.peek().type == TokenType.LessThan){ + var genericIdentifier = "<" + var tokensWithoutGeneric = tokens.clone() + var tokenCountWithoutGeneric = i + tokens.shift() + i++ + while(true){ + if(tokens.empty()){ + tokens.set(tokensWithoutGeneric) + i -= tokenCountWithoutGeneric + return new IdentifierResult(identifier, i, Token(first).location) + } + if(tokens.peek().value == "returns"){ + genericIdentifier += "returns " + tokens.shift() + i++ + } + var parsed = parseTypeIdentifier(tokens, null) + genericIdentifier += parsed.identifier + i += parsed.tokenCount + if(tokens.peek().type == TokenType.Pipe){ + genericIdentifier += "|" + tokens.shift() + i++ + }elseif(tokens.peek().type == TokenType.Comma){ + genericIdentifier += ", " + tokens.shift() + i++ + }elseif(tokens.peek().type == TokenType.GreaterThan){ + genericIdentifier += ">" + identifier += genericIdentifier + tokens.shift() + i++ + if(tokens.peek().type == TokenType.Asterisk){ + while(tokens.length > 0 && tokens.peek().type == TokenType.Asterisk){ + identifier += "*" + tokens.shift() + i++ + } + } + return new IdentifierResult(identifier, i, Token(first).location) + }else{ + tokens.set(tokensWithoutGeneric) + i -= tokenCountWithoutGeneric + return new IdentifierResult(identifier, i, Token(first).location) + } + } + genericIdentifier += ">" + identifier += genericIdentifier + tokens.shift() + i++ + break + } + if(tokens.peek().type == TokenType.Asterisk){ + while(tokens.length > 0 && tokens.peek().type == TokenType.Asterisk){ + identifier += "*" + tokens.shift() + i++ + } + break + }elseif(tokens.peek().type != TokenType.Dot){ + break + } + tokens.shift() + i++ + identifier += "." + tokens.next().value + i++ + } + return new IdentifierResult(identifier, i, Token(first).location) + } + + method parseTypesIfAny(TokenList tokens, Token? first = null) returns Types{ + var Location location = tokens.peek().location + if(first != null){ + location = first?!.location + } + var list types = [] + if(Type:existsByName(this, peekTypeIdentifier(tokens, first).identifier)){ + types.add(Type:fromString(parseTypeIdentifier(tokens, first).identifier, this, location)) + while(true){ + if(tokens.peek().type != TokenType.Pipe){ + if(tokens.peek().type == TokenType.QuestionMark){ + types.add(Type:fromString("null")) + tokens.shift() + }else{ + break + } + }else{ + tokens.shift() + if(Type:existsByName(this, peekTypeIdentifier(tokens).identifier)){ + types.add(Type:fromString(parseTypeIdentifier(tokens).identifier, this, location)) + }else{ + aspl.parser.utils.syntax_error("Expected valid type identifier after '|'", tokens.peek().location) + } + } + } + } + return new Types(types) + } + + method parseBlock(TokenList tokens, Token? first = null, bool pushScope = true) returns list{ + if(pushScope){ + Scope:push() + } + var list statements = [] + if(first == null){ + first = tokens.next() + } + var token = Token(first) + if(token.type != TokenType.BraceOpen){ + aspl.parser.utils.syntax_error("Expected opening brace, got '" + token.value + "' (" + token.type + ")", token.location) + } + while(!tokens.empty()){ + token = tokens.next() + if(token.type == TokenType.BraceClose){ + if(pushScope){ + Scope:pop() + } + return statements + } + var statement = parseToken(token, tokens, true) + if(!(statement oftype NopStatement)){ + statements.add(statement) + } + } + aspl.parser.utils.syntax_error("Expected closing brace", token.location) + } + + method preProcessBlock(TokenList tokens, Token? first = null, bool pushScope = true){ + if(pushScope){ + Scope:push() + } + if(first == null){ + first = tokens.next() + } + var token = Token(first) + if(token.type != TokenType.BraceOpen){ + aspl.parser.utils.syntax_error("Expected opening brace, got '" + token.value + "' (" + token.type + ")", token.location) + } + var bracesIndent = 0 + while(!tokens.empty()){ + token = tokens.peek() + if(token.type == TokenType.BraceOpen){ + bracesIndent++ + }elseif(token.type == TokenType.BraceClose){ + if(bracesIndent == 0){ + tokens.shift() + if(pushScope){ + Scope:pop() + } + return + } + bracesIndent-- + } + preProcessToken(tokens.next(), tokens) + } + aspl.parser.utils.syntax_error("Expected closing brace", token.location) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ParserResult.aspl b/stdlib/aspl/parser/ParserResult.aspl new file mode 100644 index 0000000..8d9d71b --- /dev/null +++ b/stdlib/aspl/parser/ParserResult.aspl @@ -0,0 +1,14 @@ +import aspl.parser.ast + +[public] +class ParserResult { + + [readpublic] + property list nodes + + [public] + method construct(list nodes){ + this.nodes = nodes + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/Timings.aspl b/stdlib/aspl/parser/Timings.aspl new file mode 100644 index 0000000..c2512da --- /dev/null +++ b/stdlib/aspl/parser/Timings.aspl @@ -0,0 +1,91 @@ +import time + +[public] +[static] +class Timings { + + [static] + [threadlocal] + property long lexer = 0 + [static] + [threadlocal] + property long lexerStart = 0 + [static] + [threadlocal] + property long preprocessorTypes = 0 + [static] + [threadlocal] + property long preprocessorTypesStart = 0 + [static] + [threadlocal] + property long preprocessor = 0 + [static] + [threadlocal] + property long preprocessorStart = 0 + [static] + [threadlocal] + property long parser = 0 + [static] + [threadlocal] + property long parserStart = 0 + [public] + [static] + property long total{ + get{ + return self:lexer + self:lexerStart + self:preprocessorTypes + self:preprocessor + self:parser + } + } + + [public] + [static] + method startLexer(){ + self:lexerStart = time.now().milliseconds + } + + [public] + [static] + method stopLexer(){ + self:lexer = time.now().milliseconds - self:lexerStart + self:lexerStart = 0 + } + + [public] + [static] + method startPreprocessorTypes(){ + self:preprocessorTypesStart = time.now().milliseconds + } + + [public] + [static] + method stopPreprocessorTypes(){ + self:preprocessorTypes = time.now().milliseconds - self:preprocessorTypesStart + self:preprocessorTypes = 0 + } + + [public] + [static] + method startPreprocessor(){ + self:preprocessorStart = time.now().milliseconds + } + + [public] + [static] + method stopPreprocessor(){ + self:preprocessor = time.now().milliseconds - self:preprocessorStart + self:preprocessorStart = 0 + } + + [public] + [static] + method startParser(){ + self:parserStart = time.now().milliseconds + } + + [public] + [static] + method stopParser(){ + self:parser = time.now().milliseconds - self:parserStart + self:parserStart = 0 + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/Node.aspl b/stdlib/aspl/parser/ast/Node.aspl new file mode 100644 index 0000000..69ced98 --- /dev/null +++ b/stdlib/aspl/parser/ast/Node.aspl @@ -0,0 +1,16 @@ +import aspl.parser +import aspl.parser.utils + +[public] +[abstract] +class Node{ + + [readpublic] + property Location? location = null + + [public] + method construct(Location? location){ + this.location = location + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/AndExpression.aspl b/stdlib/aspl/parser/ast/expressions/AndExpression.aspl new file mode 100644 index 0000000..507deff --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/AndExpression.aspl @@ -0,0 +1,21 @@ +import aspl.parser.utils + +[public] +class AndExpression extends BinaryOperator { + + [public] + method construct(Expression left, Expression right, Location? location){ + parent(BinaryOperator).construct(left, right, location) + } + + [public] + method getType() returns Types{ + return this.left.getType() + } + + [public] + method getConstantValue() returns any{ + return this.left.getConstantValue() && this.right.getConstantValue() + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/BinaryOperator.aspl b/stdlib/aspl/parser/ast/expressions/BinaryOperator.aspl new file mode 100644 index 0000000..439a859 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/BinaryOperator.aspl @@ -0,0 +1,32 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +[abstract] +class BinaryOperator extends Expression { + + [readpublic] + property Expression left + [readpublic] + property Expression right + + [public] + method construct(Expression left, Expression right, Location? location){ + parent(Node).construct(location) + this.left = left + this.right = right + } + + [public] + method isConstant() returns bool{ + if(left.isConstant()){ + if(right.isConstant()){ + return true + } + else{ + return false + } + } + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/CastExpression.aspl b/stdlib/aspl/parser/ast/expressions/CastExpression.aspl new file mode 100644 index 0000000..106cdb8 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/CastExpression.aspl @@ -0,0 +1,24 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class CastExpression extends Expression { + + [readpublic] + property Expression value + [readpublic] + property Type type + + [public] + method construct(Expression value, Type type, Location? location){ + parent(Node).construct(location) + this.value = value + this.type = type + } + + [public] + method getType() returns Types{ + return new Types([this.type]) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/CatchExpression.aspl b/stdlib/aspl/parser/ast/expressions/CatchExpression.aspl new file mode 100644 index 0000000..b860d35 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/CatchExpression.aspl @@ -0,0 +1,34 @@ +import aspl.parser.ast +import aspl.parser.ast.expressions +import aspl.parser.utils + +[public] +class CatchExpression extends Expression { + + [readpublic] + property Expression expression + [readpublic] + property string? variable + [readpublic] + property list code + [readpublic] + property list capturedVariables + [readpublic] + property bool standalone + + [public] + method construct(Expression expression, string? variable, list code, list capturedVariables, bool standalone, Location? location){ + parent(Node).construct(location) + this.expression = expression + this.variable = variable + this.code = code + this.capturedVariables = capturedVariables + this.standalone = standalone + } + + [public] + method getType() returns Types{ + return this.expression.getType() + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/CheckEqualsExpression.aspl b/stdlib/aspl/parser/ast/expressions/CheckEqualsExpression.aspl new file mode 100644 index 0000000..a48924a --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/CheckEqualsExpression.aspl @@ -0,0 +1,22 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class CheckEqualsExpression extends BinaryOperator { + + [public] + method construct(Expression left, Expression right, Location? location){ + parent(BinaryOperator).construct(left, right, location) + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("boolean")]) + } + + [public] + method getConstantValue() returns any{ + return this.left.getConstantValue() == this.right.getConstantValue() + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/ClassInstantiateExpression.aspl b/stdlib/aspl/parser/ast/expressions/ClassInstantiateExpression.aspl new file mode 100644 index 0000000..11b5809 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/ClassInstantiateExpression.aspl @@ -0,0 +1,25 @@ +import aspl.parser.ast +import aspl.parser.utils +import aspl.parser.classes + +[public] +class ClassInstantiateExpression extends Expression { + + [readpublic] + property Class c + [readpublic] + property list arguments + + [public] + method construct(Class c, list arguments, Location? location){ + parent(Node).construct(location) + this.c = c + this.arguments = arguments + } + + [public] + method getType() returns Types{ + return new Types([this.c.type]) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/DereferenceExpression.aspl b/stdlib/aspl/parser/ast/expressions/DereferenceExpression.aspl new file mode 100644 index 0000000..9fd1daa --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/DereferenceExpression.aspl @@ -0,0 +1,21 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class DereferenceExpression extends Expression { + + [readpublic] + property Expression pointer + + [public] + method construct(Expression pointer, Location? location){ + parent(Node).construct(location) + this.pointer = pointer + } + + [public] + method getType() returns Types{ + return this.pointer.getType().getReferenced() + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/DivideExpression.aspl b/stdlib/aspl/parser/ast/expressions/DivideExpression.aspl new file mode 100644 index 0000000..52d5d1f --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/DivideExpression.aspl @@ -0,0 +1,143 @@ +import aspl.parser.ast +import aspl.parser.utils + +class DivideExpression extends BinaryOperator { + + [public] + method construct(Expression left, Expression right, Location? location){ + parent(BinaryOperator).construct(left, right, location) + } + + [public] + method getType() returns Types{ + if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("byte")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("integer")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("integer")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("integer")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + } + aspl.parser.utils.fatal_error("Incompatible types for / operator", this.location) + } + + [public] + method getConstantValue() returns any{ + if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return byte(this.left.getConstantValue()) / byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return byte(this.left.getConstantValue()) / int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return byte(this.left.getConstantValue()) / long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return byte(this.left.getConstantValue()) / float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return byte(this.left.getConstantValue()) / double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return int(this.left.getConstantValue()) / byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return int(this.left.getConstantValue()) / int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return int(this.left.getConstantValue()) / long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return int(this.left.getConstantValue()) / float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return int(this.left.getConstantValue()) / double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return long(this.left.getConstantValue()) / byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return long(this.left.getConstantValue()) / int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return long(this.left.getConstantValue()) / long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return long(this.left.getConstantValue()) / float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return long(this.left.getConstantValue()) / double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return float(this.left.getConstantValue()) / byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return float(this.left.getConstantValue()) / int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return float(this.left.getConstantValue()) / long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return float(this.left.getConstantValue()) / float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return float(this.left.getConstantValue()) / double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return double(this.left.getConstantValue()) / byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return double(this.left.getConstantValue()) / int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return double(this.left.getConstantValue()) / long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return double(this.left.getConstantValue()) / float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return double(this.left.getConstantValue()) / double(this.right.getConstantValue()) + } + } + aspl.parser.utils.fatal_error("Incompatible types for / operator", this.location) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/EmbedFileExpression.aspl b/stdlib/aspl/parser/ast/expressions/EmbedFileExpression.aspl new file mode 100644 index 0000000..7aff60f --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/EmbedFileExpression.aspl @@ -0,0 +1,30 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class EmbedFileExpression extends Expression { + + [readpublic] + property string file + + method construct(string file, Location? location){ + parent(Node).construct(location) + this.file = file + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("list")]) + } + + [public] + method isConstant() returns bool{ + return true + } + + [public] + method getConstantValue() returns any{ + return io.read_file_bytes(file) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/EnumFieldAccessExpression.aspl b/stdlib/aspl/parser/ast/expressions/EnumFieldAccessExpression.aspl new file mode 100644 index 0000000..210bddb --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/EnumFieldAccessExpression.aspl @@ -0,0 +1,31 @@ +import aspl.parser.ast +import aspl.parser.enums +import aspl.parser.utils + +[public] +class EnumFieldAccessExpression extends Expression { + + [readpublic] + property EnumField field + + method construct(EnumField field, Location? location){ + parent(Node).construct(location) + this.field = field + } + + [public] + method getType() returns Types{ + return new Types([this.field.e.type]) + } + + [public] + method isConstant() returns bool{ + return true + } + + [public] + method getConstantValue() returns any{ + return field.value + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/Expression.aspl b/stdlib/aspl/parser/ast/expressions/Expression.aspl new file mode 100644 index 0000000..ed09415 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/Expression.aspl @@ -0,0 +1,23 @@ +import aspl.parser.ast +import aspl.parser.utils + +// This class is for all nodes that may be used as expressions +[public] +[abstract] +class Expression extends Node{ + + [public] + [abstract] + method getType() returns Types + + [public] + method isConstant() returns bool{ + return false + } + + [public] + method getConstantValue() returns any{ + aspl.parser.utils.fatal_error("This expression is not constant") + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/FunctionCallExpression.aspl b/stdlib/aspl/parser/ast/expressions/FunctionCallExpression.aspl new file mode 100644 index 0000000..25d4b08 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/FunctionCallExpression.aspl @@ -0,0 +1,35 @@ +import aspl.parser.ast +import aspl.parser.functions +import aspl.parser.utils + +[public] +class FunctionCallExpression extends Expression{ + + [readpublic] + property string identifier + [readpublic] + property Function func + [readpublic] + property list arguments + [readpublic] + property bool newThread + + method construct(string identifier, Function func, list arguments, bool newThread, Location? location){ + parent(Node).construct(location) + this.identifier = identifier + this.func = func + this.arguments = arguments + this.newThread = newThread + } + + [public] + method getType() returns Types{ + return this.func.returnTypes + } + + [public] + method isConstant() returns bool{ + return false + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/GreaterThanExpression.aspl b/stdlib/aspl/parser/ast/expressions/GreaterThanExpression.aspl new file mode 100644 index 0000000..157f66e --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/GreaterThanExpression.aspl @@ -0,0 +1,22 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class GreaterThanExpression extends BinaryOperator { + + [public] + method construct(Expression left, Expression right, Location? location){ + parent(BinaryOperator).construct(left, right, location) + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("boolean")]) + } + + [public] + method getConstantValue() returns any{ + return this.left.getConstantValue() > this.right.getConstantValue() + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/GreaterThanOrEqualExpression.aspl b/stdlib/aspl/parser/ast/expressions/GreaterThanOrEqualExpression.aspl new file mode 100644 index 0000000..9785182 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/GreaterThanOrEqualExpression.aspl @@ -0,0 +1,22 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class GreaterThanOrEqualExpression extends BinaryOperator { + + [public] + method construct(Expression left, Expression right, Location? location){ + parent(BinaryOperator).construct(left, right, location) + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("boolean")]) + } + + [public] + method getConstantValue() returns any{ + return this.left.getConstantValue() >= this.right.getConstantValue() + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/ImplementationCallExpression.aspl b/stdlib/aspl/parser/ast/expressions/ImplementationCallExpression.aspl new file mode 100644 index 0000000..5c068ad --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/ImplementationCallExpression.aspl @@ -0,0 +1,29 @@ +import aspl.parser.ast +import aspl.parser.functions +import aspl.parser.utils + +[public] +class ImplementationCallExpression extends Expression{ + + [readpublic] + property string call + [readpublic] + property list arguments + + method construct(string call, list arguments, Location? location){ + parent(Node).construct(location) + this.call = call + this.arguments = arguments + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("any")]) + } + + [public] + method isConstant() returns bool{ + return false + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/LessThanExpression.aspl b/stdlib/aspl/parser/ast/expressions/LessThanExpression.aspl new file mode 100644 index 0000000..bf305eb --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/LessThanExpression.aspl @@ -0,0 +1,22 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class LessThanExpression extends BinaryOperator { + + [public] + method construct(Expression left, Expression right, Location? location){ + parent(BinaryOperator).construct(left, right, location) + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("boolean")]) + } + + [public] + method getConstantValue() returns any{ + return this.left.getConstantValue() < this.right.getConstantValue() + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/LessThanOrEqualExpression.aspl b/stdlib/aspl/parser/ast/expressions/LessThanOrEqualExpression.aspl new file mode 100644 index 0000000..bdbf4d1 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/LessThanOrEqualExpression.aspl @@ -0,0 +1,22 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class LessThanOrEqualExpression extends BinaryOperator { + + [public] + method construct(Expression left, Expression right, Location? location){ + parent(BinaryOperator).construct(left, right, location) + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("boolean")]) + } + + [public] + method getConstantValue() returns any{ + return this.left.getConstantValue() <= this.right.getConstantValue() + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/ListAssignExpression.aspl b/stdlib/aspl/parser/ast/expressions/ListAssignExpression.aspl new file mode 100644 index 0000000..d5b840c --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/ListAssignExpression.aspl @@ -0,0 +1,27 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class ListAssignExpression extends Expression { + + [readpublic] + property Expression base + [readpublic] + property Expression index + [readpublic] + property Expression value + + [public] + method construct(Expression base, Expression index, Expression value, Location? location){ + parent(Node).construct(location) + this.base = base + this.index = index + this.value = value + } + + [public] + method getType() returns Types{ + return this.base.getType() + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/ListIndexExpression.aspl b/stdlib/aspl/parser/ast/expressions/ListIndexExpression.aspl new file mode 100644 index 0000000..bf0a1b7 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/ListIndexExpression.aspl @@ -0,0 +1,42 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class ListIndexExpression extends Expression { + + [readpublic] + property Expression base + [readpublic] + property Expression index + + [public] + method construct(Expression base, Expression index, Location? location){ + parent(Node).construct(location) + this.base = base + this.index = index + } + + [public] + method getType() returns Types{ + var list types = [] + foreach(base.getType().types as type){ + if(Type:getGenericTypes(type.toString()).length > 0){ + foreach(Type:getGenericTypes(type.toString())[0].types as t){ + types.add(t) + } + } + } + return new Types(types) + } + + [public] + method isConstant() returns bool{ + return this.base.isConstant() && this.index.isConstant() + } + + [public] + method getConstantValue() returns any{ + return list(this.base.getConstantValue())[int(this.index.getConstantValue())] + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/MapAccessExpression.aspl b/stdlib/aspl/parser/ast/expressions/MapAccessExpression.aspl new file mode 100644 index 0000000..9e99585 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/MapAccessExpression.aspl @@ -0,0 +1,42 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class MapAccessExpression extends Expression { + + [readpublic] + property Expression base + [readpublic] + property Expression key + + [public] + method construct(Expression base, Expression key, Location? location){ + parent(Node).construct(location) + this.base = base + this.key = key + } + + [public] + method getType() returns Types{ + var list types = [] + foreach(base.getType().types as type){ + if(Type:getGenericTypes(type.toString()).length > 1){ + foreach(Type:getGenericTypes(type.toString())[1].types as t){ + types.add(t) + } + } + } + return new Types(types) + } + + [public] + method isConstant() returns bool{ + return this.base.isConstant() && this.key.isConstant() + } + + [public] + method getConstantValue() returns any{ + return map(this.base.getConstantValue())[this.key.getConstantValue()] + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/MapAssignExpression.aspl b/stdlib/aspl/parser/ast/expressions/MapAssignExpression.aspl new file mode 100644 index 0000000..2b712b7 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/MapAssignExpression.aspl @@ -0,0 +1,27 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class MapAssignExpression extends Expression { + + [readpublic] + property Expression base + [readpublic] + property Expression key + [readpublic] + property Expression value + + [public] + method construct(Expression base, Expression key, Expression value, Location? location){ + parent(Node).construct(location) + this.base = base + this.key = key + this.value = value + } + + [public] + method getType() returns Types{ + return this.base.getType() + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/MinusExpression.aspl b/stdlib/aspl/parser/ast/expressions/MinusExpression.aspl new file mode 100644 index 0000000..bf64399 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/MinusExpression.aspl @@ -0,0 +1,144 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class MinusExpression extends BinaryOperator { + + [public] + method construct(Expression left, Expression right, Location? location){ + parent(BinaryOperator).construct(left, right, location) + } + + [public] + method getType() returns Types{ + if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("byte")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("integer")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("integer")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("integer")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + } + aspl.parser.utils.fatal_error("Incompatible types for - operator", this.location) + } + + [public] + method getConstantValue() returns any{ + if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return byte(this.left.getConstantValue()) - byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return byte(this.left.getConstantValue()) - int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return byte(this.left.getConstantValue()) - long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return byte(this.left.getConstantValue()) - float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return byte(this.left.getConstantValue()) - double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return int(this.left.getConstantValue()) - byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return int(this.left.getConstantValue()) - int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return int(this.left.getConstantValue()) - long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return int(this.left.getConstantValue()) - float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return int(this.left.getConstantValue()) - double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return long(this.left.getConstantValue()) - byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return long(this.left.getConstantValue()) - int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return long(this.left.getConstantValue()) - long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return long(this.left.getConstantValue()) - float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return long(this.left.getConstantValue()) - double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return float(this.left.getConstantValue()) - byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return float(this.left.getConstantValue()) - int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return float(this.left.getConstantValue()) - long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return float(this.left.getConstantValue()) - float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return float(this.left.getConstantValue()) - double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return double(this.left.getConstantValue()) - byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return double(this.left.getConstantValue()) - int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return double(this.left.getConstantValue()) - long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return double(this.left.getConstantValue()) - float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return double(this.left.getConstantValue()) - double(this.right.getConstantValue()) + } + } + aspl.parser.utils.fatal_error("Incompatible types for - operator", this.location) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/ModuloExpression.aspl b/stdlib/aspl/parser/ast/expressions/ModuloExpression.aspl new file mode 100644 index 0000000..5708ccc --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/ModuloExpression.aspl @@ -0,0 +1,144 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class ModuloExpression extends BinaryOperator { + + [public] + method construct(Expression left, Expression right, Location? location){ + parent(BinaryOperator).construct(left, right, location) + } + + [public] + method getType() returns Types{ + if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("byte")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("integer")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("integer")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("integer")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + } + aspl.parser.utils.fatal_error("Incompatible types for % operator", this.location) + } + + [public] + method getConstantValue() returns any{ + if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return byte(this.left.getConstantValue()) % byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return byte(this.left.getConstantValue()) % int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return byte(this.left.getConstantValue()) % long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return byte(this.left.getConstantValue()) % float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return byte(this.left.getConstantValue()) % double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return int(this.left.getConstantValue()) % byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return int(this.left.getConstantValue()) % int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return int(this.left.getConstantValue()) % long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return int(this.left.getConstantValue()) % float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return int(this.left.getConstantValue()) % double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return long(this.left.getConstantValue()) % byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return long(this.left.getConstantValue()) % int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return long(this.left.getConstantValue()) % long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return long(this.left.getConstantValue()) % float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return long(this.left.getConstantValue()) % double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return float(this.left.getConstantValue()) % byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return float(this.left.getConstantValue()) % int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return float(this.left.getConstantValue()) % long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return float(this.left.getConstantValue()) % float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return float(this.left.getConstantValue()) % double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return double(this.left.getConstantValue()) % byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return double(this.left.getConstantValue()) % int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return double(this.left.getConstantValue()) % long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return double(this.left.getConstantValue()) % float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return double(this.left.getConstantValue()) % double(this.right.getConstantValue()) + } + } + aspl.parser.utils.fatal_error("Incompatible types for % operator", this.location) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/MultiplyExpression.aspl b/stdlib/aspl/parser/ast/expressions/MultiplyExpression.aspl new file mode 100644 index 0000000..fd6cdd2 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/MultiplyExpression.aspl @@ -0,0 +1,144 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class MultiplyExpression extends BinaryOperator { + + [public] + method construct(Expression left, Expression right, Location? location){ + parent(BinaryOperator).construct(left, right, location) + } + + [public] + method getType() returns Types{ + if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("byte")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("integer")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("integer")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("integer")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + } + aspl.parser.utils.fatal_error("Incompatible types for * operator", this.location) + } + + [public] + method getConstantValue() returns any{ + if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return byte(this.left.getConstantValue()) * byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return byte(this.left.getConstantValue()) * int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return byte(this.left.getConstantValue()) * long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return byte(this.left.getConstantValue()) * float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return byte(this.left.getConstantValue()) * double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return int(this.left.getConstantValue()) * byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return int(this.left.getConstantValue()) * int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return int(this.left.getConstantValue()) * long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return int(this.left.getConstantValue()) * float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return int(this.left.getConstantValue()) * double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return long(this.left.getConstantValue()) * byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return long(this.left.getConstantValue()) * int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return long(this.left.getConstantValue()) * long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return long(this.left.getConstantValue()) * float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return long(this.left.getConstantValue()) * double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return float(this.left.getConstantValue()) * byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return float(this.left.getConstantValue()) * int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return float(this.left.getConstantValue()) * long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return float(this.left.getConstantValue()) * float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return float(this.left.getConstantValue()) * double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return double(this.left.getConstantValue()) * byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return double(this.left.getConstantValue()) * int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return double(this.left.getConstantValue()) * long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return double(this.left.getConstantValue()) * float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return double(this.left.getConstantValue()) * double(this.right.getConstantValue()) + } + } + aspl.parser.utils.fatal_error("Incompatible types for * operator", this.location) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/NegateExpression.aspl b/stdlib/aspl/parser/ast/expressions/NegateExpression.aspl new file mode 100644 index 0000000..b47dde7 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/NegateExpression.aspl @@ -0,0 +1,22 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class NegateExpression extends UnaryOperator { + + [public] + method construct(Expression expression, Location? location){ + parent(UnaryOperator).construct(expression, location) + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("boolean")]) + } + + [public] + method getConstantValue() returns any{ + return !this.expression.getConstantValue() + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/NonStaticMethodCallExpression.aspl b/stdlib/aspl/parser/ast/expressions/NonStaticMethodCallExpression.aspl new file mode 100644 index 0000000..3c52033 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/NonStaticMethodCallExpression.aspl @@ -0,0 +1,38 @@ +import aspl.parser.ast +import aspl.parser.methods +import aspl.parser.utils +import aspl.parser.classes + +class NonStaticMethodCallExpression extends Expression{ + + [readpublic] + property Method m + [readpublic] + property Expression base + [readpublic] + property Class? exactClass + [readpublic] + property list arguments + [readpublic] + property bool newThread + + method construct(Method m, Expression base, Class? exactClass, list arguments, bool newThread, Location? location){ + parent(Node).construct(location) + this.m = m + this.base = base + this.exactClass = exactClass + this.arguments = arguments + this.newThread = newThread + } + + [public] + method getType() returns Types{ + return this.m.returnTypes + } + + [public] + method isConstant() returns bool{ + return false + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/NonStaticPropertyAccessExpression.aspl b/stdlib/aspl/parser/ast/expressions/NonStaticPropertyAccessExpression.aspl new file mode 100644 index 0000000..43f6bba --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/NonStaticPropertyAccessExpression.aspl @@ -0,0 +1,29 @@ +import aspl.parser.ast +import aspl.parser.properties +import aspl.parser.utils + +[public] +class NonStaticPropertyAccessExpression extends Expression { + + [readpublic] + property Property p + [readpublic] + property Expression base + + method construct(Property p, Expression base, Location? location){ + parent(Node).construct(location) + this.p = p + this.base = base + } + + [public] + method getType() returns Types{ + return this.p.types + } + + [public] + method isConstant() returns bool{ + return false + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/NonStaticPropertyAssignExpression.aspl b/stdlib/aspl/parser/ast/expressions/NonStaticPropertyAssignExpression.aspl new file mode 100644 index 0000000..a584a3d --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/NonStaticPropertyAssignExpression.aspl @@ -0,0 +1,33 @@ +import aspl.parser.ast +import aspl.parser.functions +import aspl.parser.utils +import aspl.parser.properties + +[public] +class NonStaticPropertyAssignExpression extends Expression{ + + [readpublic] + property Property p + [readpublic] + property Expression base + [readpublic] + property Expression value + + method construct(Property p, Expression base, Expression value, Location? location){ + parent(Node).construct(location) + this.p = p + this.base = base + this.value = value + } + + [public] + method getType() returns Types{ + return this.p.types + } + + [public] + method isConstant() returns bool{ + return false + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/OfTypeExpression.aspl b/stdlib/aspl/parser/ast/expressions/OfTypeExpression.aspl new file mode 100644 index 0000000..94a482b --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/OfTypeExpression.aspl @@ -0,0 +1,41 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class OfTypeExpression extends Expression { + + [readpublic] + property Expression expression + [readpublic] + property Type type + + [public] + method construct(Expression expression, Type type, Location? location){ + parent(Node).construct(location) + this.expression = expression + this.type = type + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("boolean")]) + } + + [public] + method isConstant() returns bool{ + if(!expression.getType().canCast(type)){ + return true + }else{ + return false + } + } + + [public] + method getConstantValue() returns any{ + if(!expression.getType().canCast(type)){ + return false + } + aspl.parser.utils.fatal_error("Cannot evaluate constant value of oftype expression") + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/OrExpression.aspl b/stdlib/aspl/parser/ast/expressions/OrExpression.aspl new file mode 100644 index 0000000..dd18fc9 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/OrExpression.aspl @@ -0,0 +1,22 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class OrExpression extends BinaryOperator { + + [public] + method construct(Expression left, Expression right, Location? location){ + parent(BinaryOperator).construct(left, right, location) + } + + [public] + method getType() returns Types{ + return this.left.getType() + } + + [public] + method getConstantValue() returns any{ + return this.left.getConstantValue() || this.right.getConstantValue() + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/ParentExpression.aspl b/stdlib/aspl/parser/ast/expressions/ParentExpression.aspl new file mode 100644 index 0000000..d2b829c --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/ParentExpression.aspl @@ -0,0 +1,27 @@ +import aspl.parser.ast +import aspl.parser.classes +import aspl.parser.utils + +[public] +class ParentExpression extends Expression { + + [readpublic] + property Class c + + [public] + method construct(Class c, Location? location){ + parent(Node).construct(location) + this.c = c + } + + [public] + method getType() returns Types{ + return new Types([this.c.type]) + } + + [public] + method isConstant() returns bool{ + return false + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/PlusExpression.aspl b/stdlib/aspl/parser/ast/expressions/PlusExpression.aspl new file mode 100644 index 0000000..6151527 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/PlusExpression.aspl @@ -0,0 +1,156 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class PlusExpression extends BinaryOperator { + + [public] + method construct(Expression left, Expression right, Location? location){ + parent(BinaryOperator).construct(left, right, location) + } + + [public] + method getType() returns Types{ + if(Type:matches(new Types([Type:fromString("string")]), this.left.getType())){ + return new Types([Type:fromString("string")]) + }elseif(Type:matches(new Types([Type:fromString("string")]), this.right.getType())){ + return new Types([Type:fromString("string")]) + }else{ + if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("byte")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("integer")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("integer")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("integer")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("long")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("float")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return new Types([Type:fromString("double")]) + } + } + aspl.parser.utils.fatal_error("Incompatible types for + operator", this.location) + } + } + + [public] + method getConstantValue() returns any{ + if(Type:matches(new Types([Type:fromString("string")]), this.left.getType())){ + return string(this.left.getConstantValue()) + string(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("string")]), this.right.getType())){ + return string(this.left.getConstantValue()) + string(this.right.getConstantValue()) + }else{ + if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return byte(this.left.getConstantValue()) + byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return byte(this.left.getConstantValue()) + int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return byte(this.left.getConstantValue()) + long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return byte(this.left.getConstantValue()) + float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return byte(this.left.getConstantValue()) + double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return int(this.left.getConstantValue()) + byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return int(this.left.getConstantValue()) + int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return int(this.left.getConstantValue()) + long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return int(this.left.getConstantValue()) + float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return int(this.left.getConstantValue()) + double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return long(this.left.getConstantValue()) + byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return long(this.left.getConstantValue()) + int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return long(this.left.getConstantValue()) + long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return long(this.left.getConstantValue()) + float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return long(this.left.getConstantValue()) + double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return float(this.left.getConstantValue()) + byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return float(this.left.getConstantValue()) + int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return float(this.left.getConstantValue()) + long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return float(this.left.getConstantValue()) + float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return float(this.left.getConstantValue()) + double(this.right.getConstantValue()) + } + }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){ + if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){ + return double(this.left.getConstantValue()) + byte(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){ + return double(this.left.getConstantValue()) + int(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){ + return double(this.left.getConstantValue()) + long(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){ + return double(this.left.getConstantValue()) + float(this.right.getConstantValue()) + }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){ + return double(this.left.getConstantValue()) + double(this.right.getConstantValue()) + } + } + aspl.parser.utils.fatal_error("Incompatible types for + operator", this.location) + } + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/PropagateErrorExpression.aspl b/stdlib/aspl/parser/ast/expressions/PropagateErrorExpression.aspl new file mode 100644 index 0000000..b5782d3 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/PropagateErrorExpression.aspl @@ -0,0 +1,21 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class PropagateErrorExpression extends Expression { + + [readpublic] + property Expression expression + + [public] + method construct(Expression expression, Location? location){ + parent(Node).construct(location) + this.expression = expression + } + + [public] + method getType() returns Types{ + return this.expression.getType() + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/ReferenceExpression.aspl b/stdlib/aspl/parser/ast/expressions/ReferenceExpression.aspl new file mode 100644 index 0000000..2d0cd7f --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/ReferenceExpression.aspl @@ -0,0 +1,21 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class ReferenceExpression extends Expression { + + [readpublic] + property Expression expression + + [public] + method construct(Expression expression, Location? location){ + parent(Node).construct(location) + this.expression = expression + } + + [public] + method getType() returns Types{ + return new Types([this.expression.getType().getPointer()]) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/StaticMethodCallExpression.aspl b/stdlib/aspl/parser/ast/expressions/StaticMethodCallExpression.aspl new file mode 100644 index 0000000..e755c46 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/StaticMethodCallExpression.aspl @@ -0,0 +1,35 @@ +import aspl.parser.ast +import aspl.parser.methods +import aspl.parser.utils + +[public] +class StaticMethodCallExpression extends Expression{ + + [readpublic] + property Method m + [readpublic] + property Type base + [readpublic] + property list arguments + [readpublic] + property bool newThread + + method construct(Method m, Type base, list arguments, bool newThread, Location? location){ + parent(Node).construct(location) + this.m = m + this.base = base + this.arguments = arguments + this.newThread = newThread + } + + [public] + method getType() returns Types{ + return this.m.returnTypes + } + + [public] + method isConstant() returns bool{ + return false + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/StaticPropertyAccessExpression.aspl b/stdlib/aspl/parser/ast/expressions/StaticPropertyAccessExpression.aspl new file mode 100644 index 0000000..dcbf428 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/StaticPropertyAccessExpression.aspl @@ -0,0 +1,29 @@ +import aspl.parser.ast +import aspl.parser.properties +import aspl.parser.utils + +[public] +class StaticPropertyAccessExpression extends Expression { + + [readpublic] + property Property p + [readpublic] + property Type base + + method construct(Property p, Type base, Location? location){ + parent(Node).construct(location) + this.p = p + this.base = base + } + + [public] + method getType() returns Types{ + return this.p.types + } + + [public] + method isConstant() returns bool{ + return false + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/StaticPropertyAssignExpression.aspl b/stdlib/aspl/parser/ast/expressions/StaticPropertyAssignExpression.aspl new file mode 100644 index 0000000..d82c99d --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/StaticPropertyAssignExpression.aspl @@ -0,0 +1,33 @@ +import aspl.parser.ast +import aspl.parser.functions +import aspl.parser.utils +import aspl.parser.properties + +[public] +class StaticPropertyAssignExpression extends Expression{ + + [readpublic] + property Property p + [readpublic] + property Type base + [readpublic] + property Expression value + + method construct(Property p, Type base, Expression value, Location? location){ + parent(Node).construct(location) + this.p = p + this.base = base + this.value = value + } + + [public] + method getType() returns Types{ + return this.p.types + } + + [public] + method isConstant() returns bool{ + return false + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/StringIndexExpression.aspl b/stdlib/aspl/parser/ast/expressions/StringIndexExpression.aspl new file mode 100644 index 0000000..b342f8d --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/StringIndexExpression.aspl @@ -0,0 +1,34 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class StringIndexExpression extends Expression { + + [readpublic] + property Expression base + [readpublic] + property Expression index + + [public] + method construct(Expression base, Expression index, Location location){ + parent(Node).construct(location) + this.base = base + this.index = index + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("string")]) + } + + [public] + method isConstant() returns bool{ + return this.base.isConstant() && this.index.isConstant() + } + + [public] + method getConstantValue() returns any{ + return string(this.base.getConstantValue())[int(this.index.getConstantValue())] + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/ThisExpression.aspl b/stdlib/aspl/parser/ast/expressions/ThisExpression.aspl new file mode 100644 index 0000000..569df37 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/ThisExpression.aspl @@ -0,0 +1,27 @@ +import aspl.parser.ast +import aspl.parser.classes +import aspl.parser.utils + +[public] +class ThisExpression extends Expression { + + [readpublic] + property Class c + + [public] + method construct(Class c, Location? location){ + parent(Node).construct(location) + this.c = c + } + + [public] + method getType() returns Types{ + return new Types([this.c.type]) + } + + [public] + method isConstant() returns bool{ + return false + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/UnaryOperator.aspl b/stdlib/aspl/parser/ast/expressions/UnaryOperator.aspl new file mode 100644 index 0000000..bee47de --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/UnaryOperator.aspl @@ -0,0 +1,22 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +[abstract] +class UnaryOperator extends Expression { + + [readpublic] + property Expression expression + + [public] + method construct(Expression expression, Location? location){ + parent(Node).construct(location) + this.expression = expression + } + + [public] + method isConstant() returns bool{ + return expression.isConstant() + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/VariableAccessExpression.aspl b/stdlib/aspl/parser/ast/expressions/VariableAccessExpression.aspl new file mode 100644 index 0000000..d16cb3a --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/VariableAccessExpression.aspl @@ -0,0 +1,27 @@ +import aspl.parser.ast +import aspl.parser.functions +import aspl.parser.utils +import aspl.parser.variables + +[public] +class VariableAccessExpression extends Expression{ + + [readpublic] + property Variable variable + + method construct(Variable variable, Location? location){ + parent(Node).construct(location) + this.variable = variable + } + + [public] + method getType() returns Types{ + return this.variable.types + } + + [public] + method isConstant() returns bool{ + return false + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/VariableAssignExpression.aspl b/stdlib/aspl/parser/ast/expressions/VariableAssignExpression.aspl new file mode 100644 index 0000000..76d35ab --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/VariableAssignExpression.aspl @@ -0,0 +1,30 @@ +import aspl.parser.ast +import aspl.parser.functions +import aspl.parser.utils +import aspl.parser.variables + +[public] +class VariableAssignExpression extends Expression{ + + [readpublic] + property Variable variable + [readpublic] + property Expression value + + method construct(Variable variable, Expression value, Location? location){ + parent(Node).construct(location) + this.variable = variable + this.value = value + } + + [public] + method getType() returns Types{ + return this.variable.types + } + + [public] + method isConstant() returns bool{ + return false + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/VariableDeclareExpression.aspl b/stdlib/aspl/parser/ast/expressions/VariableDeclareExpression.aspl new file mode 100644 index 0000000..e897cc7 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/VariableDeclareExpression.aspl @@ -0,0 +1,30 @@ +import aspl.parser.ast +import aspl.parser.functions +import aspl.parser.utils +import aspl.parser.variables + +[public] +class VariableDeclareExpression extends Expression{ + + [readpublic] + property Variable variable + [readpublic] + property Expression value + + method construct(Variable variable, Expression value, Location? location){ + parent(Node).construct(location) + this.variable = variable + this.value = value + } + + [public] + method getType() returns Types{ + return this.variable.types + } + + [public] + method isConstant() returns bool{ + return false + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/expressions/XorExpression.aspl b/stdlib/aspl/parser/ast/expressions/XorExpression.aspl new file mode 100644 index 0000000..99549e1 --- /dev/null +++ b/stdlib/aspl/parser/ast/expressions/XorExpression.aspl @@ -0,0 +1,22 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class XorExpression extends BinaryOperator { + + [public] + method construct(Expression left, Expression right, Location? location){ + parent(BinaryOperator).construct(left, right, location) + } + + [public] + method getType() returns Types{ + return this.left.getType() + } + + [public] + method getConstantValue() returns any{ + return this.left.getConstantValue()^this.right.getConstantValue() + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/literals/BooleanLiteral.aspl b/stdlib/aspl/parser/ast/literals/BooleanLiteral.aspl new file mode 100644 index 0000000..589167d --- /dev/null +++ b/stdlib/aspl/parser/ast/literals/BooleanLiteral.aspl @@ -0,0 +1,30 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class BooleanLiteral extends Literal{ + + [readpublic] + property bool value + + method construct(bool value, Location? location){ + parent(Node).construct(location) + this.value = value + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("boolean")]) + } + + [public] + method isConstant() returns bool{ + return true + } + + [public] + method getConstantValue() returns any{ + return value + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/literals/ByteLiteral.aspl b/stdlib/aspl/parser/ast/literals/ByteLiteral.aspl new file mode 100644 index 0000000..07643fc --- /dev/null +++ b/stdlib/aspl/parser/ast/literals/ByteLiteral.aspl @@ -0,0 +1,29 @@ +import aspl.parser.ast +import aspl.parser.utils + +class ByteLiteral extends Literal{ + + [readpublic] + property byte value + + method construct(byte value, Location? location){ + parent(Node).construct(location) + this.value = value + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("byte")]) + } + + [public] + method isConstant() returns bool{ + return true + } + + [public] + method getConstantValue() returns any{ + return value + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/literals/CallbackLiteral.aspl b/stdlib/aspl/parser/ast/literals/CallbackLiteral.aspl new file mode 100644 index 0000000..d979d30 --- /dev/null +++ b/stdlib/aspl/parser/ast/literals/CallbackLiteral.aspl @@ -0,0 +1,28 @@ +import aspl.parser.ast +import aspl.parser.utils +import aspl.parser.callbacks + +class CallbackLiteral extends Literal{ + + [readpublic] + property Callback value + [readpublic] + property list capturedVariables + + method construct(Callback value, list capturedVariables, Location? location){ + parent(Node).construct(location) + this.value = value + this.capturedVariables = capturedVariables + } + + [public] + method getType() returns Types{ + return new Types([this.value.type]) + } + + [public] + method isConstant() returns bool{ + return false + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/literals/DoubleLiteral.aspl b/stdlib/aspl/parser/ast/literals/DoubleLiteral.aspl new file mode 100644 index 0000000..8e747df --- /dev/null +++ b/stdlib/aspl/parser/ast/literals/DoubleLiteral.aspl @@ -0,0 +1,30 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class DoubleLiteral extends Literal{ + + [readpublic] + property double value + + method construct(double value, Location? location){ + parent(Node).construct(location) + this.value = value + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("double")]) + } + + [public] + method isConstant() returns bool{ + return true + } + + [public] + method getConstantValue() returns any{ + return value + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/literals/FloatLiteral.aspl b/stdlib/aspl/parser/ast/literals/FloatLiteral.aspl new file mode 100644 index 0000000..6b6372b --- /dev/null +++ b/stdlib/aspl/parser/ast/literals/FloatLiteral.aspl @@ -0,0 +1,30 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class FloatLiteral extends Literal{ + + [readpublic] + property float value + + method construct(float value, Location? location){ + parent(Node).construct(location) + this.value = value + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("float")]) + } + + [public] + method isConstant() returns bool{ + return true + } + + [public] + method getConstantValue() returns any{ + return value + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/literals/IntegerLiteral.aspl b/stdlib/aspl/parser/ast/literals/IntegerLiteral.aspl new file mode 100644 index 0000000..adbaa95 --- /dev/null +++ b/stdlib/aspl/parser/ast/literals/IntegerLiteral.aspl @@ -0,0 +1,30 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class IntegerLiteral extends Literal{ + + [readpublic] + property int value + + method construct(int value, Location? location){ + parent(Node).construct(location) + this.value = value + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("integer")]) + } + + [public] + method isConstant() returns bool{ + return true + } + + [public] + method getConstantValue() returns any{ + return value + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/literals/ListLiteral.aspl b/stdlib/aspl/parser/ast/literals/ListLiteral.aspl new file mode 100644 index 0000000..9c54c72 --- /dev/null +++ b/stdlib/aspl/parser/ast/literals/ListLiteral.aspl @@ -0,0 +1,43 @@ +import aspl.parser.ast +import aspl.parser.utils +import aspl.parser.ast.expressions + +[public] +class ListLiteral extends Literal{ + + [public] + property Types type + [public] + property list value + + method construct(Types type, list value, Location? location){ + parent(Node).construct(location) + this.type = type + this.value = value + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("list<" + this.type.toString() + ">")]) + } + + [public] + method isConstant() returns bool{ + foreach(this.value as element){ + if(!element.isConstant()){ + return false + } + } + return true + } + + [public] + method getConstantValue() returns any{ + var list l = list[] + foreach(this.value as element){ + l.add(element.getConstantValue()) + } + return l + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/literals/Literal.aspl b/stdlib/aspl/parser/ast/literals/Literal.aspl new file mode 100644 index 0000000..42834c4 --- /dev/null +++ b/stdlib/aspl/parser/ast/literals/Literal.aspl @@ -0,0 +1,6 @@ +import aspl.parser.ast.expressions + +[public] +[abstract] +class Literal extends Expression{ +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/literals/LongLiteral.aspl b/stdlib/aspl/parser/ast/literals/LongLiteral.aspl new file mode 100644 index 0000000..fbaaaa6 --- /dev/null +++ b/stdlib/aspl/parser/ast/literals/LongLiteral.aspl @@ -0,0 +1,30 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class LongLiteral extends Literal{ + + [readpublic] + property long value + + method construct(long value, Location? location){ + parent(Node).construct(location) + this.value = value + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("long")]) + } + + [public] + method isConstant() returns bool{ + return true + } + + [public] + method getConstantValue() returns any{ + return value + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/literals/MapLiteral.aspl b/stdlib/aspl/parser/ast/literals/MapLiteral.aspl new file mode 100644 index 0000000..a1707fc --- /dev/null +++ b/stdlib/aspl/parser/ast/literals/MapLiteral.aspl @@ -0,0 +1,48 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class MapLiteral extends Literal{ + + [readpublic] + property Types keyType + [readpublic] + property Types valueType + [readpublic] + property list value + + method construct(Types keyType, Types valueType, list value, Location? location){ + parent(Node).construct(location) + this.keyType = keyType + this.valueType = valueType + this.value = value + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("map<" + this.keyType.toString() + ", " + this.valueType.toString() + ">")]) + } + + [public] + method isConstant() returns bool{ + foreach(this.value as pair){ + if(!pair.k.isConstant()){ + return false + } + if(!pair.v.isConstant()){ + return false + } + } + return true + } + + [public] + method getConstantValue() returns any{ + var map m = map{} + foreach(this.value as pair){ + m[pair.k.getConstantValue()] = pair.v.getConstantValue() + } + return m + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/literals/NullLiteral.aspl b/stdlib/aspl/parser/ast/literals/NullLiteral.aspl new file mode 100644 index 0000000..047aedf --- /dev/null +++ b/stdlib/aspl/parser/ast/literals/NullLiteral.aspl @@ -0,0 +1,26 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class NullLiteral extends Literal{ + + method construct(Location? location){ + parent(Node).construct(location) + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("null")]) + } + + [public] + method isConstant() returns bool{ + return true + } + + [public] + method getConstantValue() returns any{ + return null + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/literals/StringLiteral.aspl b/stdlib/aspl/parser/ast/literals/StringLiteral.aspl new file mode 100644 index 0000000..668bcb3 --- /dev/null +++ b/stdlib/aspl/parser/ast/literals/StringLiteral.aspl @@ -0,0 +1,33 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class StringLiteral extends Literal{ + + [readpublic] + property string value + [readpublic] + property string literalString + + method construct(string value, string literalString, Location? location){ + parent(Node).construct(location) + this.value = value + this.literalString = literalString + } + + [public] + method getType() returns Types{ + return new Types([Type:fromString("string")]) + } + + [public] + method isConstant() returns bool{ + return true + } + + [public] + method getConstantValue() returns any{ + return value + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/AssertStatement.aspl b/stdlib/aspl/parser/ast/statements/AssertStatement.aspl new file mode 100644 index 0000000..f75d854 --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/AssertStatement.aspl @@ -0,0 +1,16 @@ +import aspl.parser.ast +import aspl.parser.ast.expressions +import aspl.parser.utils + +[public] +class AssertStatement extends Statement{ + + [readpublic] + property Expression expression + + method construct(Expression expression, Location? location){ + parent(Node).construct(location) + this.expression = expression + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/BlockStatement.aspl b/stdlib/aspl/parser/ast/statements/BlockStatement.aspl new file mode 100644 index 0000000..8123e62 --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/BlockStatement.aspl @@ -0,0 +1,20 @@ +import aspl.parser.ast +import aspl.parser.ast.expressions +import aspl.parser.utils + +[public] +class BlockStatement extends Statement{ + + [readpublic] + property list statements + + [readpublic] + property bool isCompileTime + + method construct(list statements, bool isCompileTime, Location? location){ + parent(Node).construct(location) + this.statements = statements + this.isCompileTime = isCompileTime + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/BreakStatement.aspl b/stdlib/aspl/parser/ast/statements/BreakStatement.aspl new file mode 100644 index 0000000..4ff3b1d --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/BreakStatement.aspl @@ -0,0 +1,16 @@ +import aspl.parser.ast +import aspl.parser.ast.expressions +import aspl.parser.utils + +[public] +class BreakStatement extends Statement{ + + [readpublic] + property int level + + method construct(int level, Location? location){ + parent(Node).construct(location) + this.level = level + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/ClassDeclareStatement.aspl b/stdlib/aspl/parser/ast/statements/ClassDeclareStatement.aspl new file mode 100644 index 0000000..a431e86 --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/ClassDeclareStatement.aspl @@ -0,0 +1,20 @@ +import aspl.parser.ast +import aspl.parser.classes +import aspl.parser.utils +import aspl.parser.lexer + +[public] +class ClassDeclareStatement extends Statement{ + + [readpublic] + property Class c + [readpublic] + property list comments + + method construct(Class c, list comments, Location? location){ + parent(Node).construct(location) + this.c = c + this.comments = comments + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/ContinueStatement.aspl b/stdlib/aspl/parser/ast/statements/ContinueStatement.aspl new file mode 100644 index 0000000..909e2c2 --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/ContinueStatement.aspl @@ -0,0 +1,16 @@ +import aspl.parser.ast +import aspl.parser.ast.expressions +import aspl.parser.utils + +[public] +class ContinueStatement extends Statement{ + + [readpublic] + property int level + + method construct(int level, Location? location){ + parent(Node).construct(location) + this.level = level + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/EnumDeclareStatement.aspl b/stdlib/aspl/parser/ast/statements/EnumDeclareStatement.aspl new file mode 100644 index 0000000..7e67198 --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/EnumDeclareStatement.aspl @@ -0,0 +1,20 @@ +import aspl.parser.ast +import aspl.parser.enums +import aspl.parser.utils +import aspl.parser.lexer + +[public] +class EnumDeclareStatement extends Statement{ + + [readpublic] + property Enum e + [readpublic] + property list comments + + method construct(Enum e, list comments, Location? location){ + parent(Node).construct(location) + this.e = e + this.comments = comments + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/EscapeStatement.aspl b/stdlib/aspl/parser/ast/statements/EscapeStatement.aspl new file mode 100644 index 0000000..3a634bd --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/EscapeStatement.aspl @@ -0,0 +1,16 @@ +import aspl.parser.ast +import aspl.parser.ast.expressions +import aspl.parser.utils + +[public] +class EscapeStatement extends Statement{ + + [readpublic] + property Expression? value + + method construct(Expression? value, Location? location){ + parent(Node).construct(location) + this.value = value + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/FallbackStatement.aspl b/stdlib/aspl/parser/ast/statements/FallbackStatement.aspl new file mode 100644 index 0000000..93c8602 --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/FallbackStatement.aspl @@ -0,0 +1,16 @@ +import aspl.parser.ast +import aspl.parser.ast.expressions +import aspl.parser.utils + +[public] +class FallbackStatement extends Statement{ + + [readpublic] + property Expression value + + method construct(Expression value, Location? location){ + parent(Node).construct(location) + this.value = value + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/ForeachStatement.aspl b/stdlib/aspl/parser/ast/statements/ForeachStatement.aspl new file mode 100644 index 0000000..c5e5924 --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/ForeachStatement.aspl @@ -0,0 +1,26 @@ +import aspl.parser.ast +import aspl.parser.ast.expressions +import aspl.parser.utils + +[public] +class ForeachStatement extends Statement { + + [readpublic] + property Expression collection + [readpublic] + property string? key + [readpublic] + property string? value + [readpublic] + property list code + + [public] + method construct(Expression collection, string? key, string? value, list code, Location? location){ + parent(Node).construct(location) + this.collection = collection + this.key = key + this.value = value + this.code = code + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/FunctionDeclareStatement.aspl b/stdlib/aspl/parser/ast/statements/FunctionDeclareStatement.aspl new file mode 100644 index 0000000..679decf --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/FunctionDeclareStatement.aspl @@ -0,0 +1,20 @@ +import aspl.parser.ast +import aspl.parser.functions +import aspl.parser.utils +import aspl.parser.lexer + +[public] +class FunctionDeclareStatement extends Statement{ + + [readpublic] + property Function func + [readpublic] + property list comments + + method construct(Function func, list comments, Location? location){ + parent(Node).construct(location) + this.func = func + this.comments = comments + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/IfElseIfStatement.aspl b/stdlib/aspl/parser/ast/statements/IfElseIfStatement.aspl new file mode 100644 index 0000000..283fee3 --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/IfElseIfStatement.aspl @@ -0,0 +1,23 @@ +import aspl.parser.ast +import aspl.parser.ast.expressions +import aspl.parser.utils + +[public] +class IfElseIfStatement extends Statement { + + [readpublic] + property Expression condition + [readpublic] + property list ifCode + [readpublic] + property IfStatement|IfElseIfStatement|IfElseStatement elseIf + + [public] + method construct(Expression condition, list ifCode, IfStatement|IfElseIfStatement|IfElseStatement elseIf, Location? location){ + parent(Node).construct(location) + this.condition = condition + this.ifCode = ifCode + this.elseIf = elseIf + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/IfElseStatement.aspl b/stdlib/aspl/parser/ast/statements/IfElseStatement.aspl new file mode 100644 index 0000000..af4e8de --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/IfElseStatement.aspl @@ -0,0 +1,23 @@ +import aspl.parser.ast +import aspl.parser.ast.expressions +import aspl.parser.utils + +[public] +class IfElseStatement extends Statement { + + [readpublic] + property Expression condition + [readpublic] + property list ifCode + [readpublic] + property list elseCode + + [public] + method construct(Expression condition, list ifCode, list elseCode, Location? location){ + parent(Node).construct(location) + this.condition = condition + this.ifCode = ifCode + this.elseCode = elseCode + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/IfStatement.aspl b/stdlib/aspl/parser/ast/statements/IfStatement.aspl new file mode 100644 index 0000000..cdec186 --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/IfStatement.aspl @@ -0,0 +1,20 @@ +import aspl.parser.ast +import aspl.parser.ast.expressions +import aspl.parser.utils + +[public] +class IfStatement extends Statement { + + [readpublic] + property Expression condition + [readpublic] + property list code + + [public] + method construct(Expression condition, list code, Location? location){ + parent(Node).construct(location) + this.condition = condition + this.code = code + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/IncludeFileStatement.aspl b/stdlib/aspl/parser/ast/statements/IncludeFileStatement.aspl new file mode 100644 index 0000000..6aab176 --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/IncludeFileStatement.aspl @@ -0,0 +1,15 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class IncludeFileStatement extends Statement{ + + [readpublic] + property string file + + method construct(string file, Location? location){ + parent(Node).construct(location) + this.file = file + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/LinkLibraryStatement.aspl b/stdlib/aspl/parser/ast/statements/LinkLibraryStatement.aspl new file mode 100644 index 0000000..d8b5dbd --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/LinkLibraryStatement.aspl @@ -0,0 +1,15 @@ +import aspl.parser.ast +import aspl.parser.utils + +[public] +class LinkLibraryStatement extends Statement{ + + [readpublic] + property string library + + method construct(string library, Location? location){ + parent(Node).construct(location) + this.library = library + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/MethodDeclareStatement.aspl b/stdlib/aspl/parser/ast/statements/MethodDeclareStatement.aspl new file mode 100644 index 0000000..7cfae15 --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/MethodDeclareStatement.aspl @@ -0,0 +1,20 @@ +import aspl.parser.ast +import aspl.parser.methods +import aspl.parser.utils +import aspl.parser.lexer + +[public] +class MethodDeclareStatement extends Statement{ + + [readpublic] + property Method m + [readpublic] + property list comments + + method construct(Method m, list comments, Location? location){ + parent(Node).construct(location) + this.m = m + this.comments = comments + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/NopStatement.aspl b/stdlib/aspl/parser/ast/statements/NopStatement.aspl new file mode 100644 index 0000000..bea2d2c --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/NopStatement.aspl @@ -0,0 +1,5 @@ +// This class represents a no operation instruction +// It should be automatically filtered out by the parser before being passed to e.g. the compiler +[public] +class NopStatement extends Statement { +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/PropertyDeclareStatement.aspl b/stdlib/aspl/parser/ast/statements/PropertyDeclareStatement.aspl new file mode 100644 index 0000000..0d4eecb --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/PropertyDeclareStatement.aspl @@ -0,0 +1,20 @@ +import aspl.parser.ast +import aspl.parser.properties +import aspl.parser.utils +import aspl.parser.lexer + +[public] +class PropertyDeclareStatement extends Statement{ + + [readpublic] + property Property p + [readpublic] + property list comments + + method construct(Property p, list comments, Location? location){ + parent(Node).construct(location) + this.p = p + this.comments = comments + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/RepeatStatement.aspl b/stdlib/aspl/parser/ast/statements/RepeatStatement.aspl new file mode 100644 index 0000000..ef6e9f0 --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/RepeatStatement.aspl @@ -0,0 +1,26 @@ +import aspl.parser.ast +import aspl.parser.ast.expressions +import aspl.parser.utils + +[public] +class RepeatStatement extends Statement { + + [readpublic] + property Expression iterations + [readpublic] + property string? variable + [readpublic] + property int start + [readpublic] + property list code + + [public] + method construct(Expression iterations, string? variable, int start, list code, Location? location){ + parent(Node).construct(location) + this.iterations = iterations + this.variable = variable + this.start = start + this.code = code + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/ReturnStatement.aspl b/stdlib/aspl/parser/ast/statements/ReturnStatement.aspl new file mode 100644 index 0000000..81d234c --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/ReturnStatement.aspl @@ -0,0 +1,23 @@ +import aspl.parser.ast +import aspl.parser.ast.expressions +import aspl.parser.utils +import aspl.parser.functions +import aspl.parser.methods +import aspl.parser.properties +import aspl.parser.callbacks + +[public] +class ReturnStatement extends Statement{ + + [readpublic] + property Expression? value + [readpublic] + property Function|Method|Callback|ReactivePropertyCallback callable + + method construct(Expression? value, Function|Method|Callback|ReactivePropertyCallback callable, Location? location){ + parent(Node).construct(location) + this.value = value + this.callable = callable + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/Statement.aspl b/stdlib/aspl/parser/ast/statements/Statement.aspl new file mode 100644 index 0000000..6a02982 --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/Statement.aspl @@ -0,0 +1,7 @@ +import aspl.parser.ast + +// This class is only for nodes that can never be expressions +[public] +[abstract] +class Statement extends Node{ +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/ThrowStatement.aspl b/stdlib/aspl/parser/ast/statements/ThrowStatement.aspl new file mode 100644 index 0000000..fe00549 --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/ThrowStatement.aspl @@ -0,0 +1,16 @@ +import aspl.parser.ast +import aspl.parser.ast.expressions +import aspl.parser.utils + +[public] +class ThrowStatement extends Statement{ + + [readpublic] + property ClassInstantiateExpression errorInstance + + method construct(ClassInstantiateExpression errorInstance, Location? location){ + parent(Node).construct(location) + this.errorInstance = errorInstance + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/ast/statements/WhileStatement.aspl b/stdlib/aspl/parser/ast/statements/WhileStatement.aspl new file mode 100644 index 0000000..03bd2f1 --- /dev/null +++ b/stdlib/aspl/parser/ast/statements/WhileStatement.aspl @@ -0,0 +1,20 @@ +import aspl.parser.ast +import aspl.parser.ast.expressions +import aspl.parser.utils + +[public] +class WhileStatement extends Statement { + + [readpublic] + property Expression condition + [readpublic] + property list code + + [public] + method construct(Expression condition, list code, Location? location){ + parent(Node).construct(location) + this.condition = condition + this.code = code + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/attributes/Attribute.aspl b/stdlib/aspl/parser/attributes/Attribute.aspl new file mode 100644 index 0000000..87d60f2 --- /dev/null +++ b/stdlib/aspl/parser/attributes/Attribute.aspl @@ -0,0 +1,78 @@ +import aspl.parser.utils +import aspl.parser.ast.literals + +[public] +class Attribute { + + [static] + property map attributes = map{} + + [readpublic] + property string identifier + [readpublic] + property list parameters + [public] + property int minimumParameterCount{ + get{ + var count = 0 + foreach(parameters as parameter){ + if(parameter.optional){ + return count + } + count++ + } + return count + } + } + [readpublic] + property AttributeUsage usage + [readpublic] + property list conflicting + + [public] + method construct(string identifier, list parameters, AttributeUsage usage, list conflicting){ + this.identifier = identifier + this.parameters = parameters + this.usage = usage + this.conflicting = conflicting + } + + [public] + method canPair(Attribute other) returns bool{ + return !this.conflicting.contains(other.identifier) + } + + [public] + [static] + method init(){ + self:register(new Attribute("abstract", [], AttributeUsage.Class || AttributeUsage.Method, ["static"])) + self:register(new Attribute("deprecated", [new Parameter("message", new Types([Type:fromString("string")]), new NullLiteral(null))], AttributeUsage.Function || AttributeUsage.Callback || AttributeUsage.Class || AttributeUsage.Property || AttributeUsage.Method || AttributeUsage.Enum || AttributeUsage.EnumField, [])) + self:register(new Attribute("description", [new Parameter("description", new Types([Type:fromString("string")]))], AttributeUsage.Function || AttributeUsage.Callback || AttributeUsage.Class || AttributeUsage.Property || AttributeUsage.Method || AttributeUsage.Enum || AttributeUsage.EnumField, [])) + self:register(new Attribute("flags", [], AttributeUsage.Enum, [])) + self:register(new Attribute("threadlocal", [], AttributeUsage.Property, [])) + self:register(new Attribute("static", [], AttributeUsage.Class || AttributeUsage.Property || AttributeUsage.Method, ["abstract"])) + self:register(new Attribute("public", [], AttributeUsage.Function || AttributeUsage.Class || AttributeUsage.Property || AttributeUsage.Method || AttributeUsage.Enum, ["readpublic"])) + self:register(new Attribute("readpublic", [], AttributeUsage.Property, ["public"])) + self:register(new Attribute("error", [], AttributeUsage.Class, [])) + self:register(new Attribute("throws", [], AttributeUsage.Function || AttributeUsage.Callback || AttributeUsage.Method, [])) + } + + [public] + [static] + method register(Attribute attribute){ + self:attributes[attribute.identifier] = attribute + } + + [public] + [static] + method exists(string identifier) returns bool{ + return self:attributes.containsKey(identifier) + } + + [public] + [static] + method get(string identifier) returns Attribute{ + return self:attributes[identifier] + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/attributes/AttributeInstance.aspl b/stdlib/aspl/parser/attributes/AttributeInstance.aspl new file mode 100644 index 0000000..6ebd44c --- /dev/null +++ b/stdlib/aspl/parser/attributes/AttributeInstance.aspl @@ -0,0 +1,25 @@ +import aspl.parser.utils +import aspl.parser.ast.expressions +import aspl.parser.lexer + +[public] +class AttributeInstance { + + [readpublic] + property Attribute attribute + [readpublic] + property list arguments + [readpublic] + property Location? location + [readpublic] + property list comments + + [public] + method construct(Attribute attribute, list arguments, Location? location, list comments){ + this.attribute = attribute + this.arguments = arguments + this.location = location + this.comments = comments + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/attributes/AttributeUsage.aspl b/stdlib/aspl/parser/attributes/AttributeUsage.aspl new file mode 100644 index 0000000..3971a4a --- /dev/null +++ b/stdlib/aspl/parser/attributes/AttributeUsage.aspl @@ -0,0 +1,11 @@ +[public] +[flags] +enum AttributeUsage { + Function, + Callback, + Class, + Property, + Method, + Enum, + EnumField +} \ No newline at end of file diff --git a/stdlib/aspl/parser/callbacks/Callback.aspl b/stdlib/aspl/parser/callbacks/Callback.aspl new file mode 100644 index 0000000..09af058 --- /dev/null +++ b/stdlib/aspl/parser/callbacks/Callback.aspl @@ -0,0 +1,31 @@ +import aspl.parser.utils +import aspl.parser.variables +import aspl.parser.ast + +[public] +class Callback { + + [readpublic] + property Type type + [readpublic] + property list parameters + [readpublic] + property Types returnTypes + [public] + property list? code + [readpublic] + property ScopeBundle creationScope + [readpublic] + property Location location + + [public] + method construct(Type type, list parameters, Types returnTypes, list? code, ScopeBundle creationScope, Location location){ + this.type = type + this.parameters = parameters + this.returnTypes = returnTypes + this.code = code + this.creationScope = creationScope + this.location = location + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/classes/Class.aspl b/stdlib/aspl/parser/classes/Class.aspl new file mode 100644 index 0000000..e42e5f1 --- /dev/null +++ b/stdlib/aspl/parser/classes/Class.aspl @@ -0,0 +1,59 @@ +import aspl.parser +import aspl.parser.utils +import aspl.parser.ast +import aspl.parser.attributes + +[public] +class Class { + + [public] + [static] + [threadlocal] + property map classes = map{} + + [readpublic] + property Type type + [public] + property list? parents + [readpublic] + property list? attributes + [readpublic] + property bool isAbstract = false + [readpublic] + property bool isStatic = false + [readpublic] + property bool isPublic = false + [readpublic] + property bool isError = false + [public] + property list? code + [readpublic] + property Module module + [readpublic] + property Location location + + [public] + method construct(Type type, list? parents, list? attributes, list? code, Module module, Location location) { + this.type = type + this.parents = parents + this.attributes = attributes + if(attributes != null){ + foreach(attributes as attribute){ + // Caching these for performance reasons + if(attribute.attribute.identifier == "abstract"){ + isAbstract = true + }elseif(attribute.attribute.identifier == "static"){ + isStatic = true + }elseif(attribute.attribute.identifier == "public"){ + isPublic = true + }elseif(attribute.attribute.identifier == "error"){ + isError = true + } + } + } + this.code = code + this.module = module + this.location = location + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/enums/Enum.aspl b/stdlib/aspl/parser/enums/Enum.aspl new file mode 100644 index 0000000..44d4dc0 --- /dev/null +++ b/stdlib/aspl/parser/enums/Enum.aspl @@ -0,0 +1,47 @@ +import aspl.parser +import aspl.parser.utils +import aspl.parser.attributes + +[public] +class Enum { + + [public] + [static] + [threadlocal] + property map enums = map{} + + [readpublic] + property Type type + [readpublic] + property list? attributes + [readpublic] + property bool isFlags = false + [readpublic] + property bool isPublic = false + [public] + property map? fields + [readpublic] + property Module module + [public] + property Location location + + [public] + method construct(Type type, list? attributes, map? fields, Module module, Location location) { + this.type = type + this.attributes = attributes + if(attributes != null){ + foreach(attributes as attribute){ + // Caching these for performance reasons + if(attribute.attribute.identifier == "flags"){ + isFlags = true + }elseif(attribute.attribute.identifier == "public"){ + isPublic = true + } + } + } + this.fields = fields + this.module = module + this.location = location + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/enums/EnumField.aspl b/stdlib/aspl/parser/enums/EnumField.aspl new file mode 100644 index 0000000..d0165bf --- /dev/null +++ b/stdlib/aspl/parser/enums/EnumField.aspl @@ -0,0 +1,27 @@ +import aspl.parser.utils +import aspl.parser.attributes + +[public] +class EnumField{ + + [readpublic] + property Enum e + [readpublic] + property string name + [readpublic] + property int? value + [readpublic] + property list attributes + [readpublic] + property Location location + + [public] + method construct(Enum e, string name, int? value, list attributes, Location location){ + this.e = e + this.name = name + this.attributes = attributes + this.value = value + this.location = location + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/functions/CustomFunction.aspl b/stdlib/aspl/parser/functions/CustomFunction.aspl new file mode 100644 index 0000000..2aa1399 --- /dev/null +++ b/stdlib/aspl/parser/functions/CustomFunction.aspl @@ -0,0 +1,37 @@ +import aspl.parser +import aspl.parser.utils +import aspl.parser.ast +import aspl.parser.attributes + +[public] +class CustomFunction extends Function{ + + [public] + property list? code + [readpublic] + property Module module + [readpublic] + property Location? location + [readpublic] + property Location? headerEndLocation + + [public] + method construct(string identifier, list parameters, Types returnTypes, list attributes, list? code, Module module, Location? location, Location? headerEndLocation){ + parent(Function).construct(identifier, parameters, returnTypes, attributes) + this.fullyInitialized = code != null + this.code = code + this.module = module + this.location = location + this.headerEndLocation = headerEndLocation + + foreach(attributes as attribute){ + // Caching these for performance reasons + if(attribute.attribute.identifier == "public"){ + isPublic = true + }elseif(attribute.attribute.identifier == "throws"){ + canThrow = true + } + } + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/functions/Function.aspl b/stdlib/aspl/parser/functions/Function.aspl new file mode 100644 index 0000000..06764db --- /dev/null +++ b/stdlib/aspl/parser/functions/Function.aspl @@ -0,0 +1,86 @@ +import aspl.parser +import aspl.parser.utils +import aspl.parser.ast.literals +import aspl.parser.attributes + +[public] +[abstract] +class Function { + + [public] + [static] + [threadlocal] + property map functions = map{} + + [readpublic] + property string identifier + [public] + property list parameters + [public] + property int minimumParameterCount{ + get{ + var count = 0 + foreach(parameters as parameter){ + if(parameter.optional){ + return count + } + count++ + } + return count + } + } + [public] + property Types returnTypes + [readpublic] + property list attributes + [readpublic] + property bool isPublic + [readpublic] + property bool canThrow + [readpublic] + property bool fullyInitialized = true + [readpublic] + property bool used = false + + [public] + method construct(string identifier, list parameters, Types returnTypes, list attributes){ + this.identifier = identifier + this.parameters = parameters + this.returnTypes = returnTypes + this.attributes = attributes + } + + [public] + [static] + method init(){ + new InternalFunction("print", [new Parameter("value", new Types([Type:fromString("any")])), new Parameter("newline", new Types([Type:fromString("boolean")]), new BooleanLiteral(true, null))], new Types([]), []).register(null) + new InternalFunction("panic", [new Parameter("value", new Types([Type:fromString("any")]))], new Types([]), []).register(null) + new InternalFunction("key", [new Parameter("prompt", new Types([Type:fromString("string")]), new StringLiteral("", "", null))], new Types([Type:fromString("string")]), []).register(null) + new InternalFunction("input", [new Parameter("prompt", new Types([Type:fromString("string")]))], new Types([Type:fromString("string")]), []).register(null) + new InternalFunction("exit", [new Parameter("code", new Types([Type:fromString("integer")]))], new Types([]), []).register(null) + new InternalFunction("range", [new Parameter("start", new Types([Type:fromString("byte"), Type:fromString("integer"), Type:fromString("long"), Type:fromString("float"), Type:fromString("double")])), new Parameter("end", new Types([Type:fromString("byte"), Type:fromString("integer"), Type:fromString("long"), Type:fromString("float"), Type:fromString("double")]))], new Types([Type:fromString("list"), Type:fromString("list"), Type:fromString("list"), Type:fromString("list"), Type:fromString("list")]), []).register(null) + } + + [public] + method register(Location? location){ + if(self:functions.containsKey(this.identifier)){ + if(self:functions[this.identifier].fullyInitialized){ + aspl.parser.utils.generic_error("Function '" + this.identifier + "' already defined somewhere else", location) + } + } + self:functions[this.identifier] = this + } + + [public] + [static] + method exists(string identifier) returns bool{ + return self:functions.containsKey(identifier) + } + + [public] + [static] + method get(string identifier) returns Function{ + return self:functions[identifier] + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/functions/InternalFunction.aspl b/stdlib/aspl/parser/functions/InternalFunction.aspl new file mode 100644 index 0000000..c08cabe --- /dev/null +++ b/stdlib/aspl/parser/functions/InternalFunction.aspl @@ -0,0 +1,14 @@ +import aspl.parser.utils +import aspl.parser.attributes + +[public] +class InternalFunction extends Function{ + + [public] + method construct(string identifier, list parameters, Types returnTypes, list attributes){ + parent(Function).construct(identifier, parameters, returnTypes, attributes) + this.isPublic = true + this.fullyInitialized = false + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/lexer/Lexer.aspl b/stdlib/aspl/parser/lexer/Lexer.aspl new file mode 100644 index 0000000..9a5d6ca --- /dev/null +++ b/stdlib/aspl/parser/lexer/Lexer.aspl @@ -0,0 +1,247 @@ +import aspl.parser.utils +import regex +import io +$if twail{ + import chars +}$else{ + import encoding.utf8 +} +import encoding.hex + +[public] +class Lexer{ + + [public] + [static] + [threadlocal] + property map cache = map{} + + [readpublic] + property string contents + [readpublic] + property string file + [readpublic] + property int line = 1 + [readpublic] + property int lineStartPosition = 0 + [readpublic] + property int position = 0 + [public] + property int column{ + get{ + return position - lineStartPosition + 1 + } + } + + [readpublic] + property list commentCache + + method construct(string file){ + this.file = file + contents = io.read_file(file) + } + + [public] + method lex() returns list{ + var list tokens = list[] + while(true){ + var Token? token = lexToken() + if(token == null){ + break + } + if((Token(token).type == TokenType.CommentSingleline) || (Token(token).type == TokenType.CommentMultiline)){ + commentCache.add(Token(token)) + }else{ + Token(token).comments = commentCache.cloneShallow() + commentCache.clear() + tokens.add(Token(token)) + } + } + return tokens + } + + [public] + method forward(int amount){ + contents = contents.after(amount - 1) + position += amount + } + + [public] + method newline(){ + line++ + lineStartPosition = position + } + + [public] + method lexToken() returns Token?{ + while(true){ + if(contents.length == 0){ + return null + } + if(contents[0] == " "){ + forward(1) + continue + } + if(contents.startsWith("\r\n")){ + forward(2) + newline() + continue + } + if(contents[0] == "\r"){ + forward(1) + newline() + continue + } + if(contents[0] == "\n"){ + forward(1) + newline() + continue + } + if(contents[0] == "\t"){ + forward(1) + continue + } + break + } + if(contents.startsWith("/*")){ + var beginLine = line + var beginColumn = column + var depth = 1 + var pos = 2 // for the /* + position += 2 + while(pos < contents.length){ + if(contents[pos] == "/" && (contents.length > pos + 1 && contents[pos + 1] == "*")){ + depth++ + pos += 2 + position += 2 + }elseif(contents[pos] == "*" && (contents.length > pos + 1 && contents[pos + 1] == "/")){ + depth-- + pos += 2 + position += 2 + if(depth == 0){ + var t = new Token(TokenType.CommentMultiline, contents.before(pos + 1), new Location(this.file, beginLine, line, beginColumn, column)) + contents = contents.after(pos - 1) + return t + } + }elseif(contents[pos] == "\r" && (contents.length > pos + 1 && contents[pos + 1] == "\n")){ + newline() + pos += 2 + position += 2 + }elseif(contents[pos] == "\n"){ + newline() + pos++ + position++ + }else{ + pos++ + position++ + } + } + aspl.parser.utils.syntax_error("Unterminated comment", new Location(this.file, line, line, column, column)) + } + if(contents[0] == "\""){ + forward("\"".length) + var string s = "" + var string literalS = "" + var beginLine = line + var beginColumn = column + while(true){ + if(contents[0] == "\""){ + forward(1) + return new StringToken(TokenType.String, s, literalS, new Location(this.file, beginLine, line, beginColumn, column)) + }elseif(contents[0] == "\\"){ + if(contents.length > 1){ + if(contents[1] == "\""){ + s += "\"" + literalS += "\\\"" + forward(2) + }elseif(contents[1] == "\\"){ + s += "\\" + literalS += "\\\\" + forward(2) + }elseif(contents[1] == "n"){ + s += "\n" + literalS += "\\n" + forward(2) + }elseif(contents[1] == "r"){ + if(contents.length > 2 && contents[2] == "\n"){ + s += "\r\n" + literalS += "\\r\\n" + forward(3) + }else{ + s += "\r" + literalS += "\\r" + forward(2) + } + }elseif(contents[1] == "t"){ + s += "\t" + literalS += "\\t" + forward(2) + }elseif(contents[1] == "f"){ + s += "\f" + literalS += "\\f" + forward(2) + }elseif(contents[1] == "v"){ + s += "\v" + literalS += "\\v" + forward(2) + }elseif(contents[1] == "0"){ + s += "\0" + literalS += "\\0" + forward(2) + }elseif(contents[1] == "u"){ + forward(2) + var string hex = contents.before(4) + $if twail{ // TODO: Unify this + s += chars.int_to_char(encoding.hex.encode(hex)) + }$else{ + s += encoding.utf8.decode_char(encoding.hex.encode(hex)) + } + literalS += "\\u" + hex + forward(4) + }else{ + aspl.parser.utils.syntax_error("Invalid escape sequence", new Location(this.file, line, line, column, column)) + } + }else{ + aspl.parser.utils.syntax_error("Unexpected escape sequence", new Location(this.file, line, line, column, column)) + } + }elseif(contents[0] == "\r" && contents.length > 1 && contents[1] == "\n"){ + s += "\r\n" + literalS += "\\r\\n" + forward(2) + newline() + }elseif(contents[0] == "\n"){ + s += "\n" + literalS += "\\n" + forward(1) + newline() + }else{ + s += contents[0] + literalS += contents[0] + forward(1) + } + } + } + foreach(Token:patterns as token => pattern){ + var bool success = false + var string match = "" + if(pattern oftype RegularTokenPattern){ + if(contents.startsWith(pattern.pattern)){ + success = true + match = pattern.pattern + } + }elseif(pattern oftype RegexTokenPattern){ + var m = regex.match_string(pattern.pattern, contents) + if(m != null){ + success = true + match = Match(m).value + } + } + if(success){ + forward(match.length) + return new Token(token, match, new Location(this.file, line, line, column - match.length, column)) + } + } + aspl.parser.utils.syntax_error("Lexer: invalid token", new Location(this.file, line, line, column, column)) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/lexer/RegexTokenPattern.aspl b/stdlib/aspl/parser/lexer/RegexTokenPattern.aspl new file mode 100644 index 0000000..07898a2 --- /dev/null +++ b/stdlib/aspl/parser/lexer/RegexTokenPattern.aspl @@ -0,0 +1,8 @@ +[public] +class RegexTokenPattern extends TokenPattern{ + + method construct(string pattern){ // TODO: This overwrite shouldn't be required + this.pattern = pattern + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/lexer/RegularTokenPattern.aspl b/stdlib/aspl/parser/lexer/RegularTokenPattern.aspl new file mode 100644 index 0000000..b0de11d --- /dev/null +++ b/stdlib/aspl/parser/lexer/RegularTokenPattern.aspl @@ -0,0 +1,8 @@ +[public] +class RegularTokenPattern extends TokenPattern{ + + method construct(string pattern){ // TODO: This overwrite shouldn't be required + this.pattern = pattern + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/lexer/StringToken.aspl b/stdlib/aspl/parser/lexer/StringToken.aspl new file mode 100644 index 0000000..a7f9a91 --- /dev/null +++ b/stdlib/aspl/parser/lexer/StringToken.aspl @@ -0,0 +1,14 @@ +import aspl.parser.utils + +[public] +class StringToken extends Token { + + [readpublic] + property string literalString + + method construct(TokenType type, string value, string literalString, Location location){ + parent(Token).construct(type, value, location) + this.literalString = literalString + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/lexer/Token.aspl b/stdlib/aspl/parser/lexer/Token.aspl new file mode 100644 index 0000000..59677c4 --- /dev/null +++ b/stdlib/aspl/parser/lexer/Token.aspl @@ -0,0 +1,75 @@ +import aspl.parser.utils + +[public] +class Token{ + + [readpublic] + [static] + property map patterns{ + get{ + return map{ + TokenType.CommentSingleline => new RegexTokenPattern("^//[^\n\r]*"), + TokenType.Double => new RegexTokenPattern("^-?[0-9]+((\\.[0-9]+d)|d)"), + TokenType.Float => new RegexTokenPattern("^-?[0-9]+((\\.[0-9]+(f?))|f)"), + TokenType.Byte => new RegexTokenPattern("^-?[0-9]+b"), + TokenType.Long => new RegexTokenPattern("^-?[0-9]+l"), + TokenType.Integer => new RegexTokenPattern("^-?[0-9]+"), + TokenType.ParenthesisOpen => new RegularTokenPattern("("), + TokenType.ParenthesisClose => new RegularTokenPattern(")"), + TokenType.BracketOpen => new RegularTokenPattern("["), + TokenType.BracketClose => new RegularTokenPattern("]"), + TokenType.BraceOpen => new RegularTokenPattern("{"), + TokenType.BraceClose => new RegularTokenPattern("}"), + TokenType.Comma => new RegularTokenPattern(","), + TokenType.Dot => new RegularTokenPattern("."), + TokenType.Colon => new RegularTokenPattern(":"), + TokenType.Assign => new RegularTokenPattern("=>"), + TokenType.PlusEquals => new RegularTokenPattern("+="), + TokenType.PlusPlus => new RegularTokenPattern("++"), + TokenType.Plus => new RegularTokenPattern("+"), + TokenType.MinusEquals => new RegularTokenPattern("-="), + TokenType.MinusMinus => new RegularTokenPattern("--"), + TokenType.Minus => new RegularTokenPattern("-"), + TokenType.MultiplyEquals => new RegularTokenPattern("*="), + TokenType.Asterisk => new RegularTokenPattern("*"), + TokenType.DivideEquals => new RegularTokenPattern("/="), + TokenType.Slash => new RegularTokenPattern("/"), + TokenType.ModuloEquals => new RegularTokenPattern("%="), + TokenType.Modulo => new RegularTokenPattern("%"), + TokenType.CheckEquals => new RegularTokenPattern("=="), + TokenType.CheckNotEquals => new RegularTokenPattern("!="), + TokenType.Equals => new RegularTokenPattern("="), + TokenType.And => new RegularTokenPattern("&&"), + TokenType.Ampersand => new RegularTokenPattern("&"), + TokenType.Or => new RegularTokenPattern("||"), + TokenType.Xor => new RegularTokenPattern("^"), + TokenType.Negate => new RegularTokenPattern("!"), + TokenType.LessThanOrEqual => new RegularTokenPattern("<="), + TokenType.LessThan => new RegularTokenPattern("<"), + TokenType.GreaterThanOrEqual => new RegularTokenPattern(">="), + TokenType.GreaterThan => new RegularTokenPattern(">"), + TokenType.Pipe => new RegularTokenPattern("|"), + TokenType.Dollar => new RegularTokenPattern("$"), + TokenType.QuestionAndExclamationMark => new RegularTokenPattern("?!"), + TokenType.QuestionMark => new RegularTokenPattern("?"), + TokenType.Identifier => new RegexTokenPattern("^[\\w_]+") + } + } + } + + [readpublic] + property TokenType type + [readpublic] + property string value + [readpublic] + property Location location + [public] + property list? comments + + method construct(TokenType type, string value, Location location){ + this.type = type + this.value = value + this.location = location + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/lexer/TokenPattern.aspl b/stdlib/aspl/parser/lexer/TokenPattern.aspl new file mode 100644 index 0000000..ab0f7da --- /dev/null +++ b/stdlib/aspl/parser/lexer/TokenPattern.aspl @@ -0,0 +1,12 @@ +[public] +[abstract] +class TokenPattern{ + + [readpublic] + property string pattern + + method construct(string pattern){ + this.pattern = pattern + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/lexer/TokenType.aspl b/stdlib/aspl/parser/lexer/TokenType.aspl new file mode 100644 index 0000000..04c3e80 --- /dev/null +++ b/stdlib/aspl/parser/lexer/TokenType.aspl @@ -0,0 +1,49 @@ +enum TokenType{ + CommentSingleline, + CommentMultiline, + Identifier, + Byte, + Integer, + Long, + Float, + Double, + String, + ParenthesisOpen, + ParenthesisClose, + BracketOpen, + BracketClose, + BraceOpen, + BraceClose, + Comma, + Dot, + Colon, + Assign, + Plus, + PlusPlus, + PlusEquals, + Minus, + MinusMinus, + MinusEquals, + Asterisk, + MultiplyEquals, + Slash, + DivideEquals, + Modulo, + ModuloEquals, + Equals, + CheckEquals, + CheckNotEquals, + And, + Or, + Xor, + Negate, + LessThan, + LessThanOrEqual, + GreaterThan, + GreaterThanOrEqual, + Ampersand, + Pipe, + Dollar, + QuestionMark, + QuestionAndExclamationMark +} \ No newline at end of file diff --git a/stdlib/aspl/parser/main.aspl b/stdlib/aspl/parser/main.aspl new file mode 100644 index 0000000..94db914 --- /dev/null +++ b/stdlib/aspl/parser/main.aspl @@ -0,0 +1,135 @@ +import aspl.parser.utils +import aspl.parser.ast +import aspl.parser.ast.statements +import aspl.parser.ast.expressions +import aspl.parser.ast.literals +import aspl.parser.properties +import aspl.parser.methods +import aspl.parser.classes + +$if main{ + import io + + if(os.args().length < 2){ + aspl.parser.utils.syntax_error("Usage: parser or ") + } + var string main = io.abs(os.args()[1]) + var string mainDirectory = "" + if(io.exists_file(main)){ + mainDirectory = io.full_directory_path(main) + }elseif(io.exists_directory(main)){ + mainDirectory = main + }else{ + aspl.parser.utils.fatal_error("Main is neither a valid file nor a valid directory: " + main) + } + Module:init(new Module(io.directory_name(mainDirectory), mainDirectory)) +} + +[public] +function parse() returns ParserResult +{ + foreach(DirectoryUtils:index(Module:mainModule.directory) as file){ + if(!Parser:importProcessedFiles.contains(file)){ + Parser:importProcessedFiles.add(file) + var Parser parser = new Parser(Module:mainModule, file) + parser.parse(ParseMode.Import) + } + } + foreach(Module:modules as module){ + foreach(DirectoryUtils:index(module.directory) as file){ + var Parser parser = new Parser(module, file) + parser.parse(ParseMode.PreprocessTypes) + } + } + foreach(Module:modules as module){ + foreach(DirectoryUtils:index(module.directory) as file){ + var Parser parser = new Parser(module, file) + parser.parse(ParseMode.Preprocess) + } + } + foreach(ClassUtils:classesWithParsers as c => parser){ + ClassUtils:handleParents(parser, Class:classes[c]) + } + var list nodes = list[] + foreach(Module:modules as module){ + foreach(DirectoryUtils:index(module.directory) as file){ + var Parser parser = new Parser(module, file) + foreach(parser.parse().nodes as node){ + nodes.add(node) + } + } + } + foreach(Class:classes as c){ + var properties = Property:getAllFor(c.type) + if(properties.length > 0){ + if(!Method:methods.containsKey(c.type.toString())){ + Method:methods[c.type.toString()] = {} + } + if(!Method:methods[c.type.toString()].containsKey("construct")){ + Method:methods[c.type.toString()]["construct"] = new CustomMethod(c.type, "construct", [], new Types([]), [], null, null, null) + var code = list(c.code) + code.add(new MethodDeclareStatement(Method:methods[c.type.toString()]["construct"], [], null)) + c.code = code + } + var constructor = Method:methods[c.type.toString()]["construct"] + var code = list[] + foreach(properties as p){ + if(p oftype CustomNormalProperty){ + if(!p.isStatic){ + if(CustomNormalProperty(p).defaultValue != null){ + code.add(new NonStaticPropertyAssignExpression(p, new ThisExpression(c, null), CustomNormalProperty(p).defaultValue, null)) + }else{ + if(Options:backend == "c"){ + code.add(new NonStaticPropertyAssignExpression(p, new ThisExpression(c, null), new NullLiteral(null), null)) // TODO: This is an ugly hack + } + } + } + } + } + if(CustomMethod(constructor).code != null){ + foreach(CustomMethod(constructor).code as statement){ + code.add(statement) + } + } + CustomMethod(constructor).code = code + } + } + if(ErrorUtils:hasCompilationErrors){ + exit(1) + } + return new ParserResult(sort(nodes)) +} + +[public] +function sort(list nodes) returns list +{ + var list classDeclarations = list[] + var list enumDeclarations = list[] + var list functionDeclarations = list[] + var list other = list[] + foreach(nodes as node){ + if(node oftype ClassDeclareStatement){ + classDeclarations.add(ClassDeclareStatement(node)) + }elseif(node oftype EnumDeclareStatement){ + enumDeclarations.add(EnumDeclareStatement(node)) + }elseif(node oftype FunctionDeclareStatement){ + functionDeclarations.add(FunctionDeclareStatement(node)) + }else{ + other.add(node) + } + } + var all = list[] + foreach(enumDeclarations as enumDeclaration){ // TODO: Does this order break anything? It is required for optional/default enum field values in the stack-based AIL backend + all.add(enumDeclaration) + } + foreach(classDeclarations as classDeclaration){ + all.add(classDeclaration) + } + foreach(functionDeclarations as functionDeclaration){ + all.add(functionDeclaration) + } + foreach(other as node){ + all.add(node) + } + return all +} \ No newline at end of file diff --git a/stdlib/aspl/parser/methods/CustomMethod.aspl b/stdlib/aspl/parser/methods/CustomMethod.aspl new file mode 100644 index 0000000..fcd1df8 --- /dev/null +++ b/stdlib/aspl/parser/methods/CustomMethod.aspl @@ -0,0 +1,42 @@ +import aspl.parser.utils +import aspl.parser.ast +import aspl.parser.attributes + +[public] +class CustomMethod extends Method{ + + [public] + property list? code + [readpublic] + property Location? location + [readpublic] + property Location? headerEndLocation + + [public] + method construct(Type type, string name, list parameters, Types returnTypes, list attributes, list? code, Location? location, Location? headerEndLocation){ + parent(Method).construct(type, name, parameters, returnTypes, attributes) + this.fullyInitialized = code != null + this.code = code + this.location = location + this.headerEndLocation = headerEndLocation + + foreach(attributes as attribute){ + // Caching these for performance reasons + if(attribute.attribute.identifier == "abstract"){ + this.isAbstract = true + }elseif(attribute.attribute.identifier == "static"){ + this.isStatic = true + }elseif(attribute.attribute.identifier == "public"){ + this.isPublic = true + }elseif(attribute.attribute.identifier == "throws"){ + this.canThrow = true + } + } + } + + [public] + method withType(Type type) returns Method{ + return new self(type, this.name, this.parameters, this.returnTypes, this.attributes, this.code, this.location, this.headerEndLocation) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/methods/InternalMethod.aspl b/stdlib/aspl/parser/methods/InternalMethod.aspl new file mode 100644 index 0000000..265f627 --- /dev/null +++ b/stdlib/aspl/parser/methods/InternalMethod.aspl @@ -0,0 +1,22 @@ +import aspl.parser.utils +import aspl.parser.attributes + +[public] +class InternalMethod extends Method{ + + [public] + method construct(Type type, string name, list parameters, Types returnTypes, list attributes){ + parent(Method).construct(type, name, parameters, returnTypes, attributes) + this.isAbstract = false + this.isStatic = false + this.isPublic = true + this.canThrow = false + this.fullyInitialized = false + } + + [public] + method withType(Type type) returns Method{ + return new self(type, this.name, this.parameters, this.returnTypes, this.attributes) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/methods/Method.aspl b/stdlib/aspl/parser/methods/Method.aspl new file mode 100644 index 0000000..7adee78 --- /dev/null +++ b/stdlib/aspl/parser/methods/Method.aspl @@ -0,0 +1,241 @@ +import aspl.parser +import aspl.parser.classes +import aspl.parser.utils +import aspl.parser.ast.literals +import aspl.parser.attributes + +[public] +[abstract] +class Method { + + [public] + [static] + [threadlocal] + property map> methods = {} + + [readpublic] + property Type type + [readpublic] + property string name + [public] + property list parameters + [public] + property int minimumParameterCount{ + get{ + var count = 0 + foreach(parameters as parameter){ + if(parameter.optional){ + return count + } + count++ + } + return count + } + } + [public] + property Types returnTypes + [readpublic] + property list attributes + [readpublic] + property bool isAbstract = false + [readpublic] + property bool isStatic = false + [readpublic] + property bool isPublic = false + [readpublic] + property bool canThrow = false + [readpublic] + property bool fullyInitialized = true + [readpublic] + property bool createdFromAny = false + + [public] + method construct(Type type, string name, list parameters, Types returnTypes, list attributes) { + this.type = type + this.name = name + this.parameters = parameters + this.returnTypes = returnTypes + this.attributes = attributes + } + + [public] + [static] + method init(){ + new InternalMethod(Type:fromString("any"), "cloneShallow", [], new Types([]), []).register(null) // This is a special method and it's return types are generated by the compiler + new InternalMethod(Type:fromString("any"), "cloneDeep", [], new Types([]), []).register(null) // This is a special method and it's return types are generated by the compiler + + new InternalMethod(Type:fromString("string"), "toLower", [], new Types([Type:fromString("string")]), []).register(null) + new InternalMethod(Type:fromString("string"), "toUpper", [], new Types([Type:fromString("string")]), []).register(null) + new InternalMethod(Type:fromString("string"), "replace", [new Parameter("search", new Types([Type:fromString("string")])), new Parameter("replace", new Types([Type:fromString("string")]))], new Types([Type:fromString("string")]), []).register(null) + new InternalMethod(Type:fromString("string"), "startsWith", [new Parameter("substring", new Types([Type:fromString("string")]))], new Types([Type:fromString("boolean")]), []).register(null) + new InternalMethod(Type:fromString("string"), "endsWith", [new Parameter("substring", new Types([Type:fromString("string")]))], new Types([Type:fromString("boolean")]), []).register(null) + new InternalMethod(Type:fromString("string"), "contains", [new Parameter("substring", new Types([Type:fromString("string")]))], new Types([Type:fromString("boolean")]), []).register(null) + new InternalMethod(Type:fromString("string"), "after", [new Parameter("position", new Types([Type:fromString("integer")]))], new Types([Type:fromString("string")]), []).register(null) + new InternalMethod(Type:fromString("string"), "before", [new Parameter("position", new Types([Type:fromString("integer")]))], new Types([Type:fromString("string")]), []).register(null) + new InternalMethod(Type:fromString("string"), "trim", [new Parameter("chars", new Types([Type:fromString("string")]), new StringLiteral(" \n\t\v\f\r", " \\n\\t\\v\\f\\r", null))], new Types([Type:fromString("string")]), []).register(null) + new InternalMethod(Type:fromString("string"), "trimStart", [], new Types([Type:fromString("string")]), []).register(null) + new InternalMethod(Type:fromString("string"), "trimEnd", [], new Types([Type:fromString("string")]), []).register(null) + new InternalMethod(Type:fromString("string"), "split", [new Parameter("delimiter", new Types([Type:fromString("string")])), new Parameter("maxParts", new Types([Type:fromString("integer")]), new IntegerLiteral(-1, null))], new Types([Type:fromString("list")]), []).register(null) + new InternalMethod(Type:fromString("string"), "reverse", [], new Types([Type:fromString("string")]), []).register(null) + + new InternalMethod(Type:fromString("list"), "contains", [new Parameter("element", new Types([Type:fromString("T")]))], new Types([Type:fromString("boolean")]), []).register(null) + new InternalMethod(Type:fromString("list"), "add", [new Parameter("element", new Types([Type:fromString("T")]))], new Types([Type:fromString("list")]), []).register(null) + new InternalMethod(Type:fromString("list"), "insert", [new Parameter("index", new Types([Type:fromString("integer")])), new Parameter("element", new Types([Type:fromString("T")]))], new Types([Type:fromString("list")]), []).register(null) + new InternalMethod(Type:fromString("list"), "insertElements", [new Parameter("index", new Types([Type:fromString("integer")])), new Parameter("elements", new Types([Type:fromString("list")]))], new Types([Type:fromString("list")]), []).register(null) + new InternalMethod(Type:fromString("list"), "remove", [new Parameter("element", new Types([Type:fromString("T")]))], new Types([Type:fromString("list")]), []).register(null) + new InternalMethod(Type:fromString("list"), "removeAt", [new Parameter("index", new Types([Type:fromString("integer")]))], new Types([Type:fromString("list")]), []).register(null) + new InternalMethod(Type:fromString("list"), "clear", [], new Types([Type:fromString("list")]), []).register(null) + new InternalMethod(Type:fromString("list"), "join", [new Parameter("delimiter", new Types([Type:fromString("string")]))], new Types([Type:fromString("string")]), []).register(null) + + new InternalMethod(Type:fromString("map"), "containsKey", [new Parameter("key", new Types([Type:fromString("T")]))], new Types([Type:fromString("boolean")]), []).register(null) + new InternalMethod(Type:fromString("map"), "containsKeyd", [new Parameter("key", new Types([Type:fromString("T")]))], new Types([Type:fromString("boolean")]), []).register(null) + new InternalMethod(Type:fromString("map"), "remove", [new Parameter("key", new Types([Type:fromString("T")]))], new Types([Type:fromString("map")]), []).register(null) + new InternalMethod(Type:fromString("map"), "clear", [], new Types([Type:fromString("map")]), []).register(null) + + new InternalMethod(Type:fromString("callback"), "invoke", [], new Types([]), []).register(null) // This is a special method and it's parameters and return types are generated by the compiler + } + + [public] + method register(Location? location){ + if(self:methods.containsKey(this.type.toString())){ + if(self:methods[this.type.toString()].containsKey(name)){ + if(self:methods[this.type.toString()][name].fullyInitialized){ + aspl.parser.utils.generic_error("Method '" + this.type.toString() + "." + this.name + "' already defined somewhere else", location) + } + } + } + if(!self:methods.containsKey(this.type.toString())){ + self:methods[this.type.toString()] = {} + } + self:methods[this.type.toString()][this.name] = this + } + + [public] + [static] + method exists(Type type, string name, bool checkParents = true) returns bool{ + if(self:methods.containsKey(type.toString())){ + if(self:methods[type.toString()].containsKey(name)){ + return true + } + } + if(checkParents && Class:classes.containsKey(type.toString())){ + foreach(Class:classes[type.toString()].parents as parent){ + if(self:exists(parent, name)){ + return true + } + } + } + if(self:methods.containsKey("any")){ + if(self:methods["any"].containsKey(name)){ + return true + } + } + return false + } + + [public] + [static] + method get(Type type, string name) returns Method{ + if(self:methods.containsKey(type.toString())){ + if(self:methods[type.toString()].containsKey(name)){ + return self:methods[type.toString()][name] + } + } + if(Class:classes.containsKey(type.toString())){ + foreach(Class:classes[type.toString()].parents as parent){ + if(self:exists(parent, name)){ + return self:get(parent, name) + } + } + } + if(self:methods.containsKey("any")){ + if(self:methods["any"].containsKey(name)){ + return self:createMethodFromAny(type, name) + } + } + aspl.parser.utils.fatal_error("Method '" + type.toString() + "." + name + "' not found (compiler bug)") + } + + [public] + [static] + method getAllFor(Type type) returns list{ + var map methods = map{} + if(self:methods.containsKey(type.toString())){ + foreach(self:methods[type.toString()] as m){ + methods[m.name] = m + } + } + if(Class:classes.containsKey(type.toString())){ + foreach(Class:classes[type.toString()].parents as parent){ + foreach(self:getAllFor(parent) as m){ + if(!methods.containsKey(m.name)){ + methods[m.name] = m + } + } + } + } + if(self:methods.containsKey("any")){ + foreach(self:methods["any"] as m){ + if(!methods.containsKey(m.name)){ + methods[m.name] = self:createMethodFromAny(type, m.name) + } + } + } + var list methodList = list[] + foreach(methods as m){ + methodList.add(m) + } + return methodList + } + + [public] + [abstract] + method withType(Type type) returns Method + + method setCreatedFromAny(bool createdFromAny = true){ + this.createdFromAny = createdFromAny + } + + [public] + [static] + method createMethodFromAny(Type type, string name) returns Method{ + var newMethod = self:methods["any"][name].withType(type) + newMethod.setCreatedFromAny() + if(!self:methods.containsKey(type.toString())){ + self:methods[type.toString()] = {} + } + self:methods[type.toString()][name] = newMethod + + var list parameters = [] + foreach(newMethod.parameters as parameter){ + var list paramTypes = [] + foreach(parameter.types.types as t){ + if(t.toString() == "T"){ + paramTypes.add(type) + }else{ + paramTypes.add(t) + } + } + parameters.add(new Parameter(parameter.name, new Types(paramTypes), parameter.defaultValue)) + } + newMethod.parameters = parameters + newMethod.register(null) + + var list returnTypes = [] + if(name == "cloneShallow" || name == "cloneDeep"){ + returnTypes.add(type) + }else{ + foreach(newMethod.returnTypes.types as t){ + if(t.toString() == "T"){ + returnTypes.add(type) + }else{ + returnTypes.add(t) + } + } + } + newMethod.returnTypes = new Types(returnTypes) + + return newMethod + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/precedence/PrecedenceLevel.aspl b/stdlib/aspl/parser/precedence/PrecedenceLevel.aspl new file mode 100644 index 0000000..e9e0292 --- /dev/null +++ b/stdlib/aspl/parser/precedence/PrecedenceLevel.aspl @@ -0,0 +1,32 @@ +[public] +enum PrecedenceLevel { + None = -1, + And = 0, + Or = 0, + Xor = 0, + OfType = 1, + CheckEquals = 1, + CheckNotEquals = 1, + PlusEquals = 1, + MinusEquals = 1, + MultiplyEquals = 1, + DivideEquals = 1, + ModuloEquals = 1, + LessThan = 1, + LessThanOrEqual = 1, + GreaterThan = 1, + GreaterThanOrEqual = 1, + Plus = 2, + Minus = 2, + Asterisk = 3, + Slash = 3, + Modulo = 3, + Negate = 3, + Reference = 3, + PlusPlus = 4, + MinusMinus = 4, + Dot = 4, + QuestionAndExclamationMark = 4, + BracketOpen = 5, + Catch = 5, // TODO: Is the precedence level correct? +} \ No newline at end of file diff --git a/stdlib/aspl/parser/precedence/PrecedenceUtils.aspl b/stdlib/aspl/parser/precedence/PrecedenceUtils.aspl new file mode 100644 index 0000000..b9c92d8 --- /dev/null +++ b/stdlib/aspl/parser/precedence/PrecedenceUtils.aspl @@ -0,0 +1,72 @@ +import aspl.parser.lexer + +[public] +[static] +class PrecedenceUtils { + + [public] + [static] + method GetTokenPrecendenceLevel(Token token) returns PrecedenceLevel{ // TODO: Why is this PascalCase? + if(token.type == TokenType.Identifier){ + if(token.value == "oftype"){ + return PrecedenceLevel.OfType + }elseif(token.value == "catch"){ + return PrecedenceLevel.Catch + } + }elseif(token.type == TokenType.Plus){ + return PrecedenceLevel.Plus + }elseif(token.type == TokenType.PlusEquals){ + return PrecedenceLevel.PlusEquals + }elseif(token.type == TokenType.PlusPlus){ + return PrecedenceLevel.PlusPlus + }elseif(token.type == TokenType.Minus){ + return PrecedenceLevel.Minus + }elseif(token.type == TokenType.MinusEquals){ + return PrecedenceLevel.MinusEquals + }elseif(token.type == TokenType.MinusMinus){ + return PrecedenceLevel.MinusMinus + }elseif(token.type == TokenType.Asterisk){ + return PrecedenceLevel.Asterisk + }elseif(token.type == TokenType.MultiplyEquals){ + return PrecedenceLevel.MultiplyEquals + }elseif(token.type == TokenType.Slash){ + return PrecedenceLevel.Slash + }elseif(token.type == TokenType.DivideEquals){ + return PrecedenceLevel.DivideEquals + }elseif(token.type == TokenType.Modulo){ + return PrecedenceLevel.Modulo + }elseif(token.type == TokenType.ModuloEquals){ + return PrecedenceLevel.ModuloEquals + }elseif(token.type == TokenType.And){ + return PrecedenceLevel.And + }elseif(token.type == TokenType.Or){ + return PrecedenceLevel.Or + }elseif(token.type == TokenType.Xor){ + return PrecedenceLevel.Xor + }elseif(token.type == TokenType.Negate){ + return PrecedenceLevel.Negate + }elseif(token.type == TokenType.CheckEquals){ + return PrecedenceLevel.CheckEquals + }elseif(token.type == TokenType.CheckNotEquals){ + return PrecedenceLevel.CheckNotEquals + }elseif(token.type == TokenType.LessThan){ + return PrecedenceLevel.LessThan + }elseif(token.type == TokenType.LessThanOrEqual){ + return PrecedenceLevel.LessThanOrEqual + }elseif(token.type == TokenType.GreaterThan){ + return PrecedenceLevel.GreaterThan + }elseif(token.type == TokenType.GreaterThanOrEqual){ + return PrecedenceLevel.GreaterThanOrEqual + }elseif(token.type == TokenType.Ampersand){ + return PrecedenceLevel.Reference + }elseif(token.type == TokenType.Dot){ + return PrecedenceLevel.Dot + }elseif(token.type == TokenType.QuestionAndExclamationMark){ + return PrecedenceLevel.QuestionAndExclamationMark + }elseif(token.type == TokenType.BracketOpen){ + return PrecedenceLevel.BracketOpen + } + return PrecedenceLevel.None + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/properties/CustomNormalProperty.aspl b/stdlib/aspl/parser/properties/CustomNormalProperty.aspl new file mode 100644 index 0000000..6f731c3 --- /dev/null +++ b/stdlib/aspl/parser/properties/CustomNormalProperty.aspl @@ -0,0 +1,42 @@ +import aspl.parser.ast.expressions +import aspl.parser.utils +import aspl.parser.attributes + +[public] +class CustomNormalProperty extends Property{ + + [public] + property Expression? defaultValue + [readpublic] + property Location location + [readpublic] + property Location headerEndLocation + + [public] + method construct(Type type, string name, Types types, list attributes, Expression? defaultValue, Location location, Location headerEndLocation){ + parent(Property).construct(type, name, types, attributes) + this.fullyInitialized = defaultValue != null + this.defaultValue = defaultValue + this.location = location + this.headerEndLocation = headerEndLocation + + foreach(attributes as attribute){ + // Caching these for performance reasons + if(attribute.attribute.identifier == "static"){ + this.isStatic = true + }elseif(attribute.attribute.identifier == "threadlocal"){ + this.isThreadLocal = true + }elseif(attribute.attribute.identifier == "public"){ + this.isPublic = true + }elseif(attribute.attribute.identifier == "readpublic"){ + this.isReadPublic = true + } + } + } + + [public] + method withType(Type type) returns Property{ + return new self(type, this.name, this.types, this.attributes, this.defaultValue, this.location, this.headerEndLocation) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/properties/CustomReactiveProperty.aspl b/stdlib/aspl/parser/properties/CustomReactiveProperty.aspl new file mode 100644 index 0000000..c2e9357 --- /dev/null +++ b/stdlib/aspl/parser/properties/CustomReactiveProperty.aspl @@ -0,0 +1,48 @@ +import aspl.parser.utils +import aspl.parser.ast +import aspl.parser.attributes + +[public] +class CustomReactiveProperty extends Property{ + + [public] + property list? getCode + [public] + property list? setCode + [readpublic] + property Location location + [readpublic] + property Location headerEndLocation + + [public] + method construct(Type type, string name, Types types, list attributes, list? getCode, list? setCode, Location location, Location headerEndLocation){ + parent(Property).construct(type, name, types, attributes) + this.fullyInitialized = true + if(getCode == null){ + if(setCode == null){ + this.fullyInitialized = false + } + } + this.getCode = getCode + this.setCode = setCode + this.location = location + this.headerEndLocation = headerEndLocation + + foreach(attributes as attribute){ + // Caching these for performance reasons + if(attribute.attribute.identifier == "static"){ + this.isStatic = true + }elseif(attribute.attribute.identifier == "public"){ + this.isPublic = true + }elseif(attribute.attribute.identifier == "readpublic"){ + this.isReadPublic = true + } + } + } + + [public] + method withType(Type type) returns Property{ + return new self(type, this.name, this.types, this.attributes, this.getCode, this.setCode, this.location, this.headerEndLocation) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/properties/InternalProperty.aspl b/stdlib/aspl/parser/properties/InternalProperty.aspl new file mode 100644 index 0000000..163c12c --- /dev/null +++ b/stdlib/aspl/parser/properties/InternalProperty.aspl @@ -0,0 +1,21 @@ +import aspl.parser.utils +import aspl.parser.attributes + +[public] +class InternalProperty extends Property{ + + [public] + method construct(Type type, string name, Types types, list attributes){ + parent(Property).construct(type, name, types, attributes) + this.isStatic = false + this.isPublic = false + this.isReadPublic = true + this.fullyInitialized = false + } + + [public] + method withType(Type type) returns Property{ + return new self(type, this.name, this.types, this.attributes) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/properties/Property.aspl b/stdlib/aspl/parser/properties/Property.aspl new file mode 100644 index 0000000..7b0c3d3 --- /dev/null +++ b/stdlib/aspl/parser/properties/Property.aspl @@ -0,0 +1,181 @@ +import aspl.parser +import aspl.parser.classes +import aspl.parser.utils +import aspl.parser.attributes + +[public] +[abstract] +class Property { + + [public] + [static] + [threadlocal] + property map> properties = {} + + [readpublic] + property Type type + [readpublic] + property string name + [public] + property Types types + [readpublic] + property list attributes + [readpublic] + property bool isStatic = false + [readpublic] + property bool isThreadLocal = false + [readpublic] + property bool isPublic = false + [readpublic] + property bool isReadPublic = false + [readpublic] + property bool fullyInitialized = true + [readpublic] + property bool createdFromAny = false + + [public] + method construct(Type type, string name, Types types, list attributes){ + this.type = type + this.name = name + this.types = types + this.attributes = attributes + } + + [public] + [static] + method init(){ + new InternalProperty(Type:fromString("string"), "length", new Types([Type:fromString("integer")]), [new AttributeInstance(Attribute:get("readpublic"), [], null, [])]).register(null) + + new InternalProperty(Type:fromString("list"), "length", new Types([Type:fromString("integer")]), [new AttributeInstance(Attribute:get("readpublic"), [], null, [])]).register(null) + + new InternalProperty(Type:fromString("map"), "length", new Types([Type:fromString("integer")]), [new AttributeInstance(Attribute:get("readpublic"), [], null, [])]).register(null) + } + + [public] + method register(Location? location){ + if(self:properties.containsKey(this.type.toString())){ + if(self:properties[this.type.toString()].containsKey(name)){ + if(self:properties[this.type.toString()][name].fullyInitialized){ + aspl.parser.utils.generic_error("Property '" + this.type.toString() + "." + this.name + "' already defined somewhere else", location) + } + } + } + if(!self:properties.containsKey(this.type.toString())){ + self:properties[this.type.toString()] = {} + } + self:properties[this.type.toString()][this.name] = this + } + + [public] + [static] + method exists(Type type, string name, bool checkParents = true) returns bool{ + if(self:properties.containsKey(type.toString())){ + if(self:properties[type.toString()].containsKey(name)){ + return true + } + } + if(checkParents && Class:classes.containsKey(type.toString())){ + foreach(Class:classes[type.toString()].parents as parent){ + if(self:exists(parent, name)){ + return true + } + } + } + if(self:properties.containsKey("any")){ + if(self:properties["any"].containsKey(name)){ + return true + } + } + return false + } + + [public] + [static] + method get(Type type, string name) returns Property{ + if(self:properties.containsKey(type.toString())){ + if(self:properties[type.toString()].containsKey(name)){ + return self:properties[type.toString()][name] + } + } + if(Class:classes.containsKey(type.toString())){ + foreach(Class:classes[type.toString()].parents as parent){ + if(self:exists(parent, name)){ + return self:get(parent, name) + } + } + } + if(self:properties.containsKey("any")){ + if(self:properties["any"].containsKey(name)){ + return self:createPropertyFromAny(type, name) + } + } + aspl.parser.utils.fatal_error("Property '" + type.toString() + "." + name + "' not found (compiler bug)") + } + + [public] + [static] + method getAllFor(Type type) returns list{ + var map properties = {} + if(self:properties.containsKey(type.toString())){ + foreach(self:properties[type.toString()] as p){ + properties[p.name] = p + } + } + if(Class:classes.containsKey(type.toString())){ + if(Class:classes[type.toString()].parents != null){ // TODO: This can (and will) fail when the parents are not loaded yet + foreach(Class:classes[type.toString()].parents as parent){ + foreach(self:getAllFor(parent) as p){ + if(!properties.containsKey(p.name)){ + properties[p.name] = p + } + } + } + } + } + if(self:properties.containsKey("any")){ + foreach(self:properties["any"] as p){ + if(!properties.containsKey(p.name)){ + properties[p.name] = self:createPropertyFromAny(type, p.name) + } + } + } + var list propertyList = list[] + foreach(properties as p){ + propertyList.add(p) + } + return propertyList + } + + [public] + [abstract] + method withType(Type type) returns Property + + method setCreatedFromAny(bool createdFromAny = true){ + this.createdFromAny = createdFromAny + } + + [public] + [static] + method createPropertyFromAny(Type type, string name) returns Property{ + var newProperty = self:properties["any"][name].withType(type) + newProperty.setCreatedFromAny() + if(!self:properties.containsKey(type.toString())){ + self:properties[type.toString()] = {} + } + self:properties[type.toString()][name] = newProperty + + var list propertyTypes = [] + foreach(newProperty.types.types as t){ + if(t.toString() == "T"){ + propertyTypes.add(type) + }else{ + propertyTypes.add(t) + } + } + newProperty.types = new Types(propertyTypes) + newProperty.register(null) + + return newProperty + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/properties/ReactivePropertyCallback.aspl b/stdlib/aspl/parser/properties/ReactivePropertyCallback.aspl new file mode 100644 index 0000000..b4257f6 --- /dev/null +++ b/stdlib/aspl/parser/properties/ReactivePropertyCallback.aspl @@ -0,0 +1,18 @@ +import aspl.parser.utils +import aspl.parser.properties + +[public] +class ReactivePropertyCallback { + + [readpublic] + property Property p + [readpublic] + property Types returnTypes + + [public] + method construct(Property p, Types returnTypes) { + this.p = p + this.returnTypes = returnTypes + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/AttributeUtils.aspl b/stdlib/aspl/parser/utils/AttributeUtils.aspl new file mode 100644 index 0000000..2e388f3 --- /dev/null +++ b/stdlib/aspl/parser/utils/AttributeUtils.aspl @@ -0,0 +1,95 @@ +import aspl.parser +import aspl.parser.lexer +import aspl.parser.attributes +import aspl.parser.functions +import aspl.parser.methods +import aspl.parser.variables +import aspl.parser.properties +import aspl.parser.ast.statements +import aspl.parser.ast.expressions +import aspl.parser.precedence + +[public] +[static] +class AttributeUtils { + + [public] + [static] + method parseAttributesIfAny(Parser parser, Token token, TokenList tokens) returns bool{ + var parsedAny = false + while(true){ + if(parsedAny){ + token = tokens.peek() + } + if(token.type != TokenType.BracketOpen){ + return parsedAny + } + if(parsedAny){ + tokens.shift() + } + if(tokens.peek().type == TokenType.Identifier){ + var attributePeek = parser.peekTypeIdentifier(tokens) + var isAttribute = false + if((tokens.peek(attributePeek.tokenCount).type == TokenType.BracketClose) || (tokens.peek(attributePeek.tokenCount).type == TokenType.ParenthesisOpen)){ + if(Attribute:exists(attributePeek.identifier)){ + isAttribute = true + }else{ + /* TODO: This is sometimes false-positive: + // TODO: Below variables are needed because of a weird bug in ASPL where inline and is broken; TODO: verify if this is still the case + var t = !attributePeek.identifier.startsWith("this.") + var s = !attributePeek.identifier.startsWith("self:") + var f = !Function:exists(attributePeek.identifier) + var m = (parser.currentClass == null || !Method:exists(Class(parser.currentClass).type, attributePeek.identifier)) + var v = !Variable:exists(attributePeek.identifier) + var p = (parser.currentClass == null || !Property:exists(Class(parser.currentClass).type, attributePeek.identifier)) + if(t && s && f && m && v && p){ + aspl.parser.utils.syntax_error("Unknown attribute '" + attributePeek.identifier + "'", tokens.peek().location) + }*/ + } + } + if(!isAttribute){ + return parsedAny + } + var attribute = Attribute:get(IdentifierUtils:handleTypeIdentifier(parser.parseTypeIdentifier(tokens).identifier)) + var list arguments = [] + if(tokens.peek().type == TokenType.ParenthesisOpen){ + tokens.shift() + foreach(attribute.parameters as parameter){ + if(tokens.peek().type == TokenType.ParenthesisClose){ + if(!parameter.optional){ + aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for attribute '" + attribute.identifier + "' (expected " + attribute.parameters.length + ")", tokens.peek().location) + } + break + } + var value = aspl.parser.utils.verify_expression(parser.parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, parameter.types)) + if(!Type:matches(parameter.types, value.getType())){ + aspl.parser.utils.type_error("Cannot pass a value of the type '" + value.getType().toString() + "' to a parameter of the type '" + parameter.types.toString() + "'", value.location) + } + arguments.add(value) + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + } + } + if(tokens.peek().type == TokenType.ParenthesisClose){ + tokens.shift() + }else{ + aspl.parser.utils.syntax_error("Expected ')' after attribute arguments", tokens.peek().location) + } + }elseif(attribute.parameters.length > 0 && !attribute.parameters[0].optional){ + aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for attribute '" + attribute.identifier + "' (expected " + attribute.parameters.length + ")", tokens.peek().location) + } + if(tokens.peek().type == TokenType.BracketClose){ + tokens.shift() + }else{ + aspl.parser.utils.syntax_error("Expected ']' after attribute name", tokens.peek().location) + } + parsedAny = true + parser.attributeCache.add(new AttributeInstance(attribute, arguments, token.location, list(token.comments))) + // ingore the attribute since it has already been preprocessed + }else{ + return parsedAny + } + } + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/ClassUtils.aspl b/stdlib/aspl/parser/utils/ClassUtils.aspl new file mode 100644 index 0000000..4521f42 --- /dev/null +++ b/stdlib/aspl/parser/utils/ClassUtils.aspl @@ -0,0 +1,128 @@ +import aspl.parser +import aspl.parser.classes +import aspl.parser.methods +import aspl.parser.utils + +[public] +[static] +class ClassUtils { + + [public] + [static] + [threadlocal] + property map classesWithParsers = map{} + [public] + [static] + [threadlocal] + property list parentHandledTypes = list[] + + [public] + [static] + method handleParents(Parser parser, Class c){ + self:parentHandledTypes.add(c.type.toString()) + var list parents = [] + foreach(c.parents as parent){ + var p = Type:fromString(parent.toString(), parser) + if(self:parentHandledTypes.contains(p.toString())){ + parents.add(p) + continue + } + if(!Class:classes.containsKey(p.toString())){ + aspl.parser.utils.type_error("Cannot extend the unknown class " + p.toString(), c.location) + } + var pc = Class:classes[p.toString()] + self:handleParents(self:classesWithParsers[pc.type.toString()], pc) + parents.add(p) + } + c.parents = parents + } + + [public] + [static] + method throwOnInvalidInheritance(Class c, list children, Location location){ + if(c.parents != null){ + foreach(c.parents as parentType){ + if(!Class:classes.containsKey(parentType.toString())){ + aspl.parser.utils.type_error("Cannot extend the unknown class " + parentType.toString(), location) + }elseif(parentType.toString() == c.type.toString()){ + aspl.parser.utils.type_error("Class " + c.type.toString() + " cannot extend itself", location) + }elseif(children.contains(parentType.toString())){ + aspl.parser.utils.type_error("Class " + c.type.toString() + " cannot extend " + parentType.toString() + " because " + parentType.toString() + " already extends " + c.type.toString() + " (maybe check parents of parents?)", location) + }else{ + var childrenCopy = children.cloneShallow() // shallow is enough because the elements (strings) are immutable + childrenCopy.add(c.type.toString()) + self:throwOnInvalidInheritance(Class:classes[parentType.toString()], childrenCopy, location) + } + } + var list myParents = [] + foreach(c.parents as parent){ + myParents.add(parent.toString()) + } + foreach(children as child){ + foreach(Class:classes[child].parents as parent){ + if(myParents.contains(parent.toString())){ + aspl.parser.utils.type_error("Class " + child + " extends " + parent.toString() + " multiple times (maybe check parents of parents?)", null) + } + } + } + } + } + + [public] + [static] + method getAllParentsRecursively(Class c) returns list{ + var parents = list[] + foreach(c.parents as parent){ + parents.add(parent) + foreach(self:getAllParentsRecursively(Class:classes[parent.toString()]) as p){ + parents.add(p) + } + } + return parents + } + + [public] + [static] + method getAllAbstractMethods(Class c) returns list{ + var list methods = [] + foreach(c.parents as parent){ + foreach(self:getAllAbstractMethods(Class:classes[parent.toString()]) as m){ + if(!methods.contains(m)){ + methods.add(m) + } + } + } + if(Method:methods.containsKey(c.type.toString())){ + foreach(Method:methods[c.type.toString()] as m){ + if(m.isAbstract){ + if(!methods.contains(m.name)){ + methods.add(m.name) + } + }elseif(!m.isAbstract){ + if(methods.contains(m.name)){ + methods.remove(m.name) + } + } + } + } + return methods + } + + [public] + [static] + method isParent(Class child, Class parent) returns bool{ + if(child.parents == null){ + return false + } + foreach(child.parents as parentType){ + if(parentType.toString() == parent.type.toString()){ + return true + } + if(self:isParent(Class:classes[parentType.toString()], parent)){ + return true + } + } + return false + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/DirectoryUtils.aspl b/stdlib/aspl/parser/utils/DirectoryUtils.aspl new file mode 100644 index 0000000..25e0882 --- /dev/null +++ b/stdlib/aspl/parser/utils/DirectoryUtils.aspl @@ -0,0 +1,41 @@ +import io + +[public] +[static] +class DirectoryUtils { + + [public] + [static] + method index(string dir) returns list{ + var list files = [] + foreach(io.glob(io.join_path([dir, "*"])) as file){ + if(file.endsWith("/") || file.endsWith("\\")){ + var folder = io.abs(file) + $if windows{ + folder = io.abs(io.join_path([dir, file])) // io.glob() does not include the directory on Windows (see https://github.com/vlang/v/issues/15448) + } + if(!folder.startsWith(dir)){ + continue // TODO: bug in the glob implementation on Linux; TODO: is this still up to date? + } + if(folder.endsWith(".git") || folder.endsWith(".git/") || folder.endsWith(".git\\")){ + continue + } + foreach(DirectoryUtils:index(folder) as f){ + if(!files.contains(f)){ + files.add(f) + } + } + }elseif(file.endsWith(".aspl")){ + var f = io.abs(file) + $if windows{ + f = io.abs(io.join_path([dir, file])) // io.glob() does not include the directory on Windows (see https://github.com/vlang/v/issues/15448) + } + if(!files.contains(f)){ + files.add(f) + } + } + } + return files + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/ErrorUtils.aspl b/stdlib/aspl/parser/utils/ErrorUtils.aspl new file mode 100644 index 0000000..ae42ffc --- /dev/null +++ b/stdlib/aspl/parser/utils/ErrorUtils.aspl @@ -0,0 +1,44 @@ +import aspl.parser.ast.expressions +import aspl.parser.functions +import aspl.parser.methods +import aspl.parser.properties +import aspl.parser.callbacks + +[public] +[static] +class ErrorUtils{ + + [public] + [static] + [threadlocal] + property bool hasCompilationErrors = false + + [public] + [static] + method canExpressionThrow(Expression expression) returns bool{ + if(expression oftype FunctionCallExpression){ + return FunctionCallExpression(expression).func.canThrow + }elseif(expression oftype NonStaticMethodCallExpression){ + return NonStaticMethodCallExpression(expression).m.canThrow + }elseif(expression oftype StaticMethodCallExpression){ + return StaticMethodCallExpression(expression).m.canThrow + } + // TODO: Callbacks and maybe reactive properties + return false + } + + [public] + [static] + method canCallableThrow(Function|Method|Callback|ReactivePropertyCallback? func) returns bool{ + if(func == null){ + return true + }elseif(func oftype Function){ + return Function(func).canThrow + }elseif(func oftype Method){ + return Method(func).canThrow + } + // TODO: Callbacks and maybe reactive properties + return false + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/GenericsUtils.aspl b/stdlib/aspl/parser/utils/GenericsUtils.aspl new file mode 100644 index 0000000..9004941 --- /dev/null +++ b/stdlib/aspl/parser/utils/GenericsUtils.aspl @@ -0,0 +1,23 @@ +import aspl.parser + +[public] +[static] +class GenericsUtils { + + // type.identifier => ["T", "U", ...] + [public] + [static] + property map> typePlaceholders = {} + // function.identifier => ["T", "U", ...] + [public] + [static] + property map> functionPlaceholders = {} + + [public] + [static] + method init(){ + self:typePlaceholders[Type:fromString("list").toString()] = ["T"] + self:typePlaceholders[Type:fromString("map").toString()] = ["T", "U"] + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/IdentifierResult.aspl b/stdlib/aspl/parser/utils/IdentifierResult.aspl new file mode 100644 index 0000000..31fa20f --- /dev/null +++ b/stdlib/aspl/parser/utils/IdentifierResult.aspl @@ -0,0 +1,18 @@ +[public] +class IdentifierResult { + + [readpublic] + property string identifier + [readpublic] + property int tokenCount + [readpublic] + property Location? location + + [public] + method construct(string identifier, int tokenCount, Location? location) { + this.identifier = identifier + this.tokenCount = tokenCount + this.location = location + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/IdentifierUtils.aspl b/stdlib/aspl/parser/utils/IdentifierUtils.aspl new file mode 100644 index 0000000..2619537 --- /dev/null +++ b/stdlib/aspl/parser/utils/IdentifierUtils.aspl @@ -0,0 +1,117 @@ +import aspl.parser +import aspl.parser.functions + +[public] +[static] +class IdentifierUtils { + + [static] + [threadlocal] + property map aliases = {"bool" => "boolean", "int" => "integer"} + + [public] + [static] + method handleTypeIdentifier(string identifier, Parser? parser = null) returns string{ + var handledIdentifier = self:lowerNamespaceInIdentifier(identifier) + if(parser != null){ + if(Parser(parser).importTable.canResolveAbbreviation(Parser(parser), handledIdentifier)){ + handledIdentifier = Parser(parser).importTable.resolveAbbreviation(Parser(parser), handledIdentifier).toString() + } + } + if(handledIdentifier.contains("<")){ + var genericTypeIdentifier = handledIdentifier.split("<")[0] + var genericType = Type:fromString(genericTypeIdentifier, parser) + var list types = Type:getGenericTypesIdentifiers(handledIdentifier) + handledIdentifier = self:handleTypeIdentifier(genericTypeIdentifier, parser) + if(parser != null){ + if(!handledIdentifier.contains(".")){ + if(Type:existsByName(parser, self:relativeToAbsoluteIdentifier(Parser(parser), handledIdentifier))){ + handledIdentifier = self:relativeToAbsoluteIdentifier(Parser(parser), handledIdentifier) + } + } + } + handledIdentifier += "<" + var i = 0 + foreach(types as type){ + if(type.startsWith("returns ")){ + type = type.after("returns ".length - 1) + handledIdentifier += "returns " + } + if(type.contains("|")){ + var list subTypes = Types:getPartsOfMultiType(type) + var j = 0 + foreach(subTypes as subType){ + subType = subType.trim() + handledIdentifier += self:handleTypeIdentifier(subType, parser) + if(j < subTypes.length - 1){ + handledIdentifier += "|" + } + j++ + } + }else{ + handledIdentifier += self:handleTypeIdentifier(type, parser) + } + if(i < types.length - 1){ + handledIdentifier += ", " + } + i++ + } + handledIdentifier += ">" + } + if(parser != null){ + if(!handledIdentifier.contains(".")){ + if(Type:existsByName(parser, self:relativeToAbsoluteIdentifier(Parser(parser), handledIdentifier))){ + handledIdentifier = self:relativeToAbsoluteIdentifier(Parser(parser), handledIdentifier) + } + } + } + if(self:aliases.containsKey(handledIdentifier)){ // TODO: This probably won't work on generics + return self:aliases[handledIdentifier] + } + return handledIdentifier + } + + [public] + [static] + method handleFunctionIdentifier(Parser parser, string identifier) returns string{ + var id = self:lowerNamespaceInIdentifier(identifier) + if(Function:exists(id)){ + return id + } + if(!id.contains(".")){ + return parser.currentNamespace + "." + id + } + if(!Function:exists(id)){ + if(Function:exists(parser.module.id + "." + id)){ + return parser.module.id + "." + id + } + } + return id + } + + [public] + [static] + method lowerNamespaceInIdentifier(string identifier) returns string{ + var last = 0 + var current = 0 + foreach(identifier as c){ + if(c == "<"){ + break // do not lower the namespaces inside the generic; this is handled in handleTypeIdentifier + }elseif(c == "."){ + last = current + } + current++ + } + if(last == 0){ + return identifier + } + return identifier.before(last).toLower() + "." + identifier.after(last) + } + + [public] + [static] + method relativeToAbsoluteIdentifier(Parser parser, string identifier) returns string{ + return parser.currentNamespace + "." + identifier + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/ImplementationCallUtils.aspl b/stdlib/aspl/parser/utils/ImplementationCallUtils.aspl new file mode 100644 index 0000000..ff34bd3 --- /dev/null +++ b/stdlib/aspl/parser/utils/ImplementationCallUtils.aspl @@ -0,0 +1,10 @@ +[public] +[static] +class ImplementationCallUtils { + + [public] + [static] + [threadlocal] + property map usedImplementationCalls // map of implementation call names to number of arguments + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/ImportTable.aspl b/stdlib/aspl/parser/utils/ImportTable.aspl new file mode 100644 index 0000000..7bec27d --- /dev/null +++ b/stdlib/aspl/parser/utils/ImportTable.aspl @@ -0,0 +1,32 @@ +import aspl.parser + +[public] +class ImportTable { + + property list namespaces = [] + + [public] + method importNamespace(string namespace){ + namespaces.add(namespace) + } + + [public] + method canResolveAbbreviation(Parser parser, string abbreviation) returns bool{ + foreach(namespaces as namespace){ + if(Type:existsByName(null, namespace + "." + abbreviation)){ + return true + } + } + return false + } + + [public] + method resolveAbbreviation(Parser parser, string abbreviation) returns Type{ + foreach(namespaces as namespace){ + if(Type:existsByName(null, namespace + "." + abbreviation)){ + return Type:fromString(namespace + "." + abbreviation) + } + } + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/Location.aspl b/stdlib/aspl/parser/utils/Location.aspl new file mode 100644 index 0000000..f44fdd6 --- /dev/null +++ b/stdlib/aspl/parser/utils/Location.aspl @@ -0,0 +1,23 @@ +[public] +class Location{ + + [readpublic] + property string file + [readpublic] + property int startLine + [readpublic] + property int endLine + [readpublic] + property int startColumn + [readpublic] + property int endColumn + + method construct(string file, int startLine, int endLine, int startColumn, int endColumn){ + this.file = file + this.startLine = startLine + this.endLine = endLine + this.startColumn = startColumn + this.endColumn = endColumn + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/ModuleUtils.aspl b/stdlib/aspl/parser/utils/ModuleUtils.aspl new file mode 100644 index 0000000..84c31ef --- /dev/null +++ b/stdlib/aspl/parser/utils/ModuleUtils.aspl @@ -0,0 +1,27 @@ +import io +import aspl.parser + +[public] +[static] +class ModuleUtils { + + [public] + [static] + method getModulePath(string module) returns string{ + if(Module:mainModule != null && module.toLower() == Module:mainModule.name.toLower()){ + return Module:mainModule.directory + } + // TODO: Make the below case insensitive + if(io.exists_directory(io.abs(io.join_path([io.full_directory_path(io.get_executable_path()), "stdlib", module])))){ + return io.abs(io.join_path([io.full_directory_path(io.get_executable_path()), "stdlib", module])) + } + return io.abs(io.join_path([io.get_home_directory(), ".aspl_modules", module])) + } + + [public] + [static] + method isFilePartOfStdlib(string file) returns boolean{ + return file.startsWith(io.abs(io.join_path([io.full_directory_path(io.get_executable_path()), "stdlib"]))) // TODO: This might fail with some weird paths? + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/Pair.aspl b/stdlib/aspl/parser/utils/Pair.aspl new file mode 100644 index 0000000..f0d13f8 --- /dev/null +++ b/stdlib/aspl/parser/utils/Pair.aspl @@ -0,0 +1,17 @@ +import aspl.parser.ast.expressions + +[public] +class Pair { + + [readpublic] + property Expression k + [readpublic] + property Expression v + + [public] + method construct(Expression k, Expression v) { + this.k = k + this.v = v + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/Parameter.aspl b/stdlib/aspl/parser/utils/Parameter.aspl new file mode 100644 index 0000000..ea6b38f --- /dev/null +++ b/stdlib/aspl/parser/utils/Parameter.aspl @@ -0,0 +1,30 @@ +import aspl.parser.ast.expressions +import aspl.parser.utils + +[public] +class Parameter { + + [readpublic] + property string name + [readpublic] + property Types types + [readpublic] + property Expression? defaultValue + [public] + property bool optional{ + get{ + return defaultValue != null + } + } + [readpublic] + property Location? location + + [public] + method construct(string name, Types types, Expression? defaultValue = null, Location? location = null) { + this.name = name + this.types = types + this.defaultValue = defaultValue + this.location = location + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/ParseMode.aspl b/stdlib/aspl/parser/utils/ParseMode.aspl new file mode 100644 index 0000000..dd87365 --- /dev/null +++ b/stdlib/aspl/parser/utils/ParseMode.aspl @@ -0,0 +1,9 @@ +[public] +enum ParseMode { + + Normal, + Preprocess, + PreprocessTypes, + Import + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/ReturnTypeUtils.aspl b/stdlib/aspl/parser/utils/ReturnTypeUtils.aspl new file mode 100644 index 0000000..467773a --- /dev/null +++ b/stdlib/aspl/parser/utils/ReturnTypeUtils.aspl @@ -0,0 +1,26 @@ +import aspl.parser.functions +import aspl.parser.methods +import aspl.parser.properties +import aspl.parser.callbacks + +[public] +[static] +class ReturnTypeUtils{ + + [public] + [static] + method getReturnTypes(Function|Method|Callback|ReactivePropertyCallback? func) returns Types{ + var returnTypes = new Types([]) + if(func oftype Function){ + returnTypes = Function(func).returnTypes + }elseif(func oftype Method){ + returnTypes = Method(func).returnTypes + }elseif(func oftype Callback){ + returnTypes = Callback(func).returnTypes + }elseif(func oftype ReactivePropertyCallback){ + returnTypes = ReactivePropertyCallback(func).returnTypes + } + return returnTypes + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/TokenList.aspl b/stdlib/aspl/parser/utils/TokenList.aspl new file mode 100644 index 0000000..9a42934 --- /dev/null +++ b/stdlib/aspl/parser/utils/TokenList.aspl @@ -0,0 +1,61 @@ +import aspl.parser.lexer + +[public] +class TokenList{ + + property list* tokens + [readpublic] + property int position + + [public] + property int length{ + get{ + return (*tokens).length - position + } + } + + method construct(list tokens){ + this.tokens = &tokens + } + + [public] + method peek(int offset = 0) returns Token{ + return (*tokens)[offset + position] + } + + [public] + method shift(int amount = 1){ + position += amount + } + + [public] + method next(int offset = 0) returns Token{ + var token = (*tokens)[offset + position] + shift(offset + 1) + return token + } + + [public] + method empty() returns bool{ + return length == 0 + } + + [public] + method in(int offset) returns TokenList{ + var newTokens = new TokenList((*tokens).cloneShallow()) + newTokens.shift(position + offset) + return newTokens + } + + [public] + method clone() returns TokenList{ + return new TokenList((*tokens).cloneShallow()) + } + + [public] + method set(TokenList other){ + tokens = other.tokens + position = other.position + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/TokenUtils.aspl b/stdlib/aspl/parser/utils/TokenUtils.aspl new file mode 100644 index 0000000..9d8f09b --- /dev/null +++ b/stdlib/aspl/parser/utils/TokenUtils.aspl @@ -0,0 +1,54 @@ +import aspl.parser.lexer + +[public] +[static] +class TokenUtils{ + + [public] + [static] + method skipTokensTillSeparator(TokenList tokens){ + var parenthesesIndent = 0 + var bracketsIndent = 0 + var bracesIndent = 0 + while(!tokens.empty()){ + var token = tokens.peek() + if(token.type == TokenType.ParenthesisOpen){ + if(parenthesesIndent == 0){ + return + } + parenthesesIndent++ + }elseif(token.type == TokenType.ParenthesisClose){ + if(parenthesesIndent == 0){ + return + } + parenthesesIndent-- + }elseif(token.type == TokenType.BracketOpen){ + if(bracketsIndent == 0){ + return + } + bracketsIndent++ + }elseif(token.type == TokenType.BracketClose){ + if(bracketsIndent == 0){ + return + } + bracketsIndent-- + }elseif(token.type == TokenType.BraceOpen){ + if(bracesIndent == 0){ + return + } + bracesIndent++ + }elseif(token.type == TokenType.BraceClose){ + if(bracesIndent == 0){ + return + } + bracesIndent-- + }elseif(token.type == TokenType.Comma){ + if(parenthesesIndent == 0 && bracketsIndent == 0 && bracesIndent == 0){ + return + } + } + tokens.shift() + } + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/Type.aspl b/stdlib/aspl/parser/utils/Type.aspl new file mode 100644 index 0000000..4047661 --- /dev/null +++ b/stdlib/aspl/parser/utils/Type.aspl @@ -0,0 +1,444 @@ +import aspl.parser +import aspl.parser.methods +import aspl.parser.properties +import aspl.parser.classes +import aspl.parser.enums +import aspl.parser.ast.literals + +[public] +class Type{ + + [static] + property list primitives = ["any", "null", "boolean", "byte", "integer", "long", "float", "double", "string", "list", "map", "callback"] + + [static] + [threadlocal] + property list genericsHandled = [] + + [readpublic] + property string identifier + + [public] + method construct(string identifier){ + this.identifier = identifier + } + + [public] + [static] + method existsByName(Parser? parser, string identifier) returns bool{ + if(identifier.endsWith("*")){ + return self:existsByName(parser, identifier.before(identifier.length - 1)) + }elseif(identifier == "self" && parser != null && Parser(parser).currentClass != null){ + return true + } + var handledIdentifier = IdentifierUtils:handleTypeIdentifier(identifier, parser) + if(handledIdentifier.contains("<")){ + var genericTypeIdentifier = handledIdentifier.split("<")[0] + if(!self:existsByName(parser, genericTypeIdentifier)){ + return false + } + var list types = self:getGenericTypesIdentifiers(handledIdentifier) + foreach(types as type){ + if(genericTypeIdentifier == "callback" && type.startsWith("returns ")){ + type = type.after("returns ".length - 1) + } + foreach(Types:getPartsOfMultiType(type) as t){ + if(!self:existsByName(parser, t)){ + return false + } + } + } + return true + }elseif(self:primitives.contains(handledIdentifier)){ + return true + }elseif(Class:classes.containsKey(handledIdentifier)){ + return true + }elseif(Enum:enums.containsKey(handledIdentifier)){ + return true + }else{ + return false + } + } + + [public] + [static] + method fromString(string identifier, Parser? parser = null, Location? location = null) returns Type{ + if(identifier.endsWith("*")){ + return self:fromString(identifier.before(identifier.length - 1), parser).getPointer() + } + if(identifier == "self"){ + if(parser != null && Parser(parser).currentClass != null){ + return Class(Parser(parser).currentClass).type + }else{ + aspl.parser.utils.generic_error("Cannot use the 'self' keyword outside of a class") + } + } + var Type newType = new self(IdentifierUtils:handleTypeIdentifier(identifier, parser)) + if(parser != null && parser?!.currentParseMode == ParseMode.Normal){ + if(Class:classes.containsKey(newType.toString()) && !Class:classes[newType.toString()].isPublic && parser?!.module.id != Class:classes[newType.toString()].module.id){ + aspl.parser.utils.warning("Cannot use the private class '" + newType.toString() + "' here", Location(location)) + }elseif(Enum:enums.containsKey(newType.toString()) && !Enum:enums[newType.toString()].isPublic && parser?!.module.id != Enum:enums[newType.toString()].module.id){ + aspl.parser.utils.warning("Cannot use the private enum '" + newType.toString() + "' here", Location(location)) + } + } + if(newType.toString().contains("<")){ + if(!self:genericsHandled.contains(newType.toString())){ + var genericTypeIdentifier = Type:getGenericTypeIdentifier(newType.toString()) + var genericType = Type:fromString(genericTypeIdentifier, parser) + var list types = Type:getGenericTypesIdentifiers(newType.toString()) + foreach(types as type){ + type = type.trim() + if(parser != null){ + foreach(Types:getPartsOfMultiType(type) as t){ + if(!self:existsByName(parser, t)){ + if(!(genericTypeIdentifier == "callback" && t.startsWith("returns "))){ + aspl.parser.utils.fatal_error("Type '" + t + "' is not a valid type identifier") + } + } + } + } + } + foreach(Method:getAllFor(genericType) as m){ + var newMethod = m.withType(newType) + if(!Method:exists(newType, newMethod.name)){ + var list parameters = [] + if(genericTypeIdentifier == "callback" && newMethod.name == "invoke"){ + var Types? returnTypes = null + var int i = 0 + foreach(types as type){ + if(type.startsWith("returns ")){ + if(returnTypes != null){ + aspl.parser.utils.generic_error("Tried to define more than one return type for a callback") + } + returnTypes = Types:fromString(type.after("returns ".length - 1), parser) + }else{ + parameters.add(new Parameter("arg" + i, new Types([Type:fromString(type, parser)]))) // TODO: Optional args; TODO: use the real parameter names + } + i++ + } + if(returnTypes == null){ + newMethod.returnTypes = new Types([]) + }else{ + newMethod.returnTypes = Types(returnTypes) + } + }else{ + foreach(newMethod.parameters as parameter){ + var list paramTypes = [] + foreach(parameter.types.types as type){ + if(GenericsUtils:typePlaceholders.containsKey(genericType.toString())){ + var int i = 0 + foreach(GenericsUtils:typePlaceholders[genericType.toString()] as placeholder){ + if(self:containsPlaceHolder(type, placeholder)){ + paramTypes.add(self:replacePlaceHolder(type, placeholder, Type:fromString(types[i], parser))) + continue + } + i++ + } + } + paramTypes.add(type) + } + parameters.add(new Parameter(parameter.name, new Types(paramTypes), parameter.defaultValue)) + } + var list returnTypes = [] + foreach(newMethod.returnTypes.types as type){ + if(GenericsUtils:typePlaceholders.containsKey(genericType.toString())){ + var int i = 0 + foreach(GenericsUtils:typePlaceholders[genericType.toString()] as placeholder){ + if(self:containsPlaceHolder(type, placeholder)){ + returnTypes.add(self:replacePlaceHolder(type, placeholder, Type:fromString(types[i], parser))) + continue 2 + } + i++ + } + } + returnTypes.add(type) + } + newMethod.returnTypes = new Types(returnTypes) + } + newMethod.parameters = parameters + newMethod.register(null) + } + } + foreach(Property:getAllFor(genericType) as p){ + var newProperty = p.withType(newType) + if(!Property:exists(newType, newProperty.name)){ + var list propertyTypes = [] + foreach(p.types.types as type){ + if(GenericsUtils:typePlaceholders.containsKey(genericType.toString())){ + var int i = 0 + foreach(GenericsUtils:typePlaceholders[genericType.toString()] as placeholder){ + if(self:containsPlaceHolder(type, placeholder)){ + propertyTypes.add(self:replacePlaceHolder(type, placeholder, Type:fromString(types[i], parser))) + continue + } + i++ + } + } + propertyTypes.add(type) + } + newProperty.types = new Types(propertyTypes) + newProperty.register(null) + } + } + // TODO: This does not work since generic methods & properties may have changed since this type has been handled, so it needs to be handled again: self:genericsHandled.add(newType.toString()) + } + } + return newType + } + + [public] + [static] + method matches(Types expected, Types got, bool ignoreGenerics = false) returns bool{ + if(got.types.length == 0){ + return false // void is never a match + } + foreach(got.types as t){ + foreach(expected.types as e){ + if(t.toString() == e.toString() || e.toString() == "any"){ + return true + }elseif(ignoreGenerics && t.toString().contains("<")){ + var string type = t.toString().split("<")[0] + if(e.toString() == type){ + return true + } + if(Class:classes.containsKey(type) && Class:classes.containsKey(e.toString())){ + if(ClassUtils:isParent(Class:classes[type], Class:classes[e.toString()])){ + return true + } + } + } + if(Class:classes.containsKey(t.toString()) && Class:classes.containsKey(e.toString())){ + if(ClassUtils:isParent(Class:classes[t.toString()], Class:classes[e.toString()])){ + return true + } + } + } + return false + } + return true + } + + [public] + [static] + method getGenericTypeIdentifier(string identifier) returns string{ + return identifier.split("<")[0] + } + + [public] + [static] + method getGenericTypesIdentifiers(string identifier) returns list{ + if(!identifier.contains("<")){ + return [] + } + var list genericTypes = list[] + var string currentType = "" + var int depth = 0 + foreach(identifier.split("<", 2)[1].reverse().split(">", 2)[1].reverse() as c){ + if(c == ","){ + if(depth == 0){ + genericTypes.add(currentType.trim()) + currentType = "" + }else{ + currentType += c + } + }elseif(c == "<"){ + depth++ + currentType += c + }elseif(c == ">"){ + depth-- + currentType += c + }else{ + currentType += c + } + } + if(currentType.length > 0){ + genericTypes.add(currentType.trim()) + } + return genericTypes + } + + [public] + [static] + method getGenericTypes(string identifier) returns list{ + var list genericTypes = [] + var identifiers = self:getGenericTypesIdentifiers(identifier) + foreach(identifiers as type){ + genericTypes.add(Types:fromString(type)) + } + return genericTypes + } + + [public] + method toString() returns string{ + return this.identifier + } + + [public] + method getDefaultValue(Location location) returns Literal?{ + if(toString() == "null"){ + return new NullLiteral(location) + }elseif(toString() == "boolean"){ + return new BooleanLiteral(false, location) + }elseif(toString() == "byte"){ + return new ByteLiteral(0b, location) + }elseif(toString() == "integer"){ + return new IntegerLiteral(0, location) + }elseif(toString() == "long"){ + return new LongLiteral(0, location) + }elseif(toString() == "float"){ + return new FloatLiteral(0f, location) + }elseif(toString() == "double"){ + return new DoubleLiteral(0d, location) + }elseif(toString() == "string"){ + return new StringLiteral("", "", location) + }elseif(self:getGenericTypeIdentifier(toString()) == "list"){ + if(self:getGenericTypes(toString()).length < 1){ + return new ListLiteral(new Types([]), [], location) // list (due to a previous non-fatal error) + } + return new ListLiteral(self:getGenericTypes(toString())[0], [], location) + }elseif(self:getGenericTypeIdentifier(toString()) == "map"){ + if(self:getGenericTypes(toString()).length < 2){ + return new MapLiteral(new Types([]), new Types([]), [], location) // map (due to a previous non-fatal error) + } + return new MapLiteral(self:getGenericTypes(toString())[0], self:getGenericTypes(toString())[1], [], location) + }elseif(Class:classes.containsKey(toString())){ + return null + }elseif(Enum:enums.containsKey(toString())){ + return null + }else{ + aspl.parser.utils.type_error("Type '" + toString() + "' has no default value", location) + return null + } + } + + [public] + method canCast(Type target) returns bool{ + if(toString() == target.toString()){ + return true + }elseif(toString() == "any" || target.toString() == "any"){ + return true + }elseif(target.toString() == "string"){ + return true + }elseif(target.toString() == "boolean"){ + if(toString() == "byte" || toString() == "integer" || toString() == "long" || toString() == "float" || toString() == "double"){ + return true + } + }elseif(target.toString() == "byte"){ + if(toString() == "integer" || toString() == "long" || toString() == "float" || toString() == "double" || toString() == "string"){ + return true + } + }elseif(target.toString() == "integer"){ + if(toString() == "byte" || toString() == "long" || toString() == "float" || toString() == "double" || toString() == "string"){ + return true + } + }elseif(target.toString() == "long"){ + if(toString() == "byte" || toString() == "integer" || toString() == "float" || toString() == "double" || toString() == "string"){ + return true + } + }elseif(target.toString() == "float"){ + if(toString() == "byte" || toString() == "integer" || toString() == "long" || toString() == "double" || toString() == "string"){ + return true + } + }elseif(target.toString() == "double"){ + if(toString() == "byte" || toString() == "integer" || toString() == "long" || toString() == "float" || toString() == "string"){ + return true + } + }elseif(Class:classes.containsKey(toString()) && Class:classes.containsKey(target.toString())){ + return ClassUtils:isParent(Class:classes[toString()], Class:classes[target.toString()]) || ClassUtils:isParent(Class:classes[target.toString()], Class:classes[toString()]) + } + // below line has no elseif because "integer" was handled above + if((Enum:enums.containsKey(toString()) && target.toString() == "integer") || (Enum:enums.containsKey(target.toString()) && toString() == "integer")){ + return true + } + // TODO: Check if cast method exists + return false + } + + [public] + method isPointer() returns bool{ + return identifier.endsWith("*") + } + + [public] + method getPointer() returns self{ + return new self(identifier + "*") + } + + [public] + method getReferenced() returns self{ + return new self(identifier.before(identifier.length - 1)) + } + + [public] + method isPrimitive() returns bool{ + return self:primitives.contains(identifier) || (identifier.contains("<") && self:primitives.contains(self:getGenericTypeIdentifier(identifier))) + } + + [public] + [static] + method containsPlaceHolder(Type type, string placeholder) returns bool{ + if(type.toString() == placeholder){ + return true + }elseif(type.isPointer()){ + return self:containsPlaceHolder(type.getReferenced(), placeholder) + }elseif(type.toString().contains("<")){ + var genericTypes = self:getGenericTypes(type.toString()) + foreach(genericTypes as genericType){ + foreach(genericType.types as t){ + if(self:containsPlaceHolder(t, placeholder)){ + return true + } + } + } + return false + }else{ + return false + } + } + + [public] + [static] + method replacePlaceHolder(Type type, string placeholder, Type replacement) returns Type{ + if(type.toString() == placeholder){ + return replacement + }elseif(type.isPointer()){ + return self:replacePlaceHolder(type.getReferenced(), placeholder, replacement).getPointer() + }elseif(type.toString().contains("<")){ + var genericTypes = self:getGenericTypes(type.toString()) + var list newGenericTypes = [] + foreach(genericTypes as genericType){ + var list newGenericType = [] + foreach(genericType.types as t){ + newGenericType.add(self:replacePlaceHolder(t, placeholder, replacement).toString()) + } + newGenericTypes.add(newGenericType.join("|")) + } + return new self(self:getGenericTypeIdentifier(type.toString()) + "<" + newGenericTypes.join(", ") + ">") + }else{ + return type + } + } + + [public] + [static] + method isPublic(Type type) returns bool{ + if(Class:classes.containsKey(type.identifier)){ + return Class:classes[type.identifier].isPublic + }elseif(Enum:enums.containsKey(type.identifier)){ + return Enum:enums[type.identifier].isPublic + }elseif(type.identifier.contains("<")){ + var genericTypes = self:getGenericTypes(type.identifier) + foreach(genericTypes as genericType){ + foreach(genericType.types as t){ + if(!self:isPublic(t)){ + return false + } + } + } + return self:isPublic(Type:fromString(getGenericTypeIdentifier(type.identifier))) + }else{ + return true + } + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/Types.aspl b/stdlib/aspl/parser/utils/Types.aspl new file mode 100644 index 0000000..b9a354e --- /dev/null +++ b/stdlib/aspl/parser/utils/Types.aspl @@ -0,0 +1,124 @@ +import aspl.parser + +[public] +class Types{ + + [readpublic] + property list types + + [public] + method construct(list types){ + this.types = types + } + + [public] + [static] + method existsByName(Parser parser, string identifier) returns bool{ + foreach(self:getPartsOfMultiType(identifier) as type){ + if(!Type:existsByName(parser, type)){ + return false + } + } + return true + } + + [public] + [static] + method fromString(string identifier, Parser? parser = null) returns Types{ + var list types = list[] + foreach(self:getPartsOfMultiType(identifier) as type){ + types.add(Type:fromString(type, parser)) + } + return new Types(types) + } + + [public] + method toString() returns string{ + if(types.length == 0){ + return "void" + } + var list typeArray = [] + foreach(types as type){ + typeArray.add(type.toString()) + } + return typeArray.join("|") + } + + [public] + method toType() returns Type{ + return new Type(toString()) + } + + [public] + [static] + method getPartsOfMultiType(string type) returns list{ + var list parts = [] + var int i = 0 + var string part = "" + while(i < type.length){ + var int indent = 0 // generic indent + while(i < type.length){ + var c = type[i] + if(c == "<"){ + indent++ + }elseif(c == ">"){ + indent-- + } + if(indent == 0){ + if(c == "|"){ + parts.add(part) + part = "" + }else{ + part += c + } + }else{ + part += c + } + i++ + } + } + parts.add(part) + return parts + } + + [public] + method canCast(Type target) returns bool{ + foreach(types as type){ + if(type.canCast(target)){ + return true + } + } + return false + } + + [public] + method isPointer() returns bool{ + return types.length == 1 && types[0].isPointer() + } + + [public] + method getPointer() returns Type{ + return toType().getPointer() + } + + [public] + method getReferenced() returns self{ + var list types = [] + foreach(this.types as type){ + types.add(type.getReferenced()) + } + return new self(types) + } + + [public] + method withoutType(Type type) returns self{ + var list types = [] + foreach(this.types as t){ + if(t.identifier != type.identifier){ + types.add(t) + } + } + return new self(types) + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/errors.aspl b/stdlib/aspl/parser/utils/errors.aspl new file mode 100644 index 0000000..3faac98 --- /dev/null +++ b/stdlib/aspl/parser/utils/errors.aspl @@ -0,0 +1,63 @@ +import console + +import aspl.parser.classes + +[public] +function notice(string message, Location? location = null){ + if(location == null){ + print(console.yellow("Notice: " + message)) + }else{ + print(console.yellow("Notice: " + Location(location).file + ":" + Location(location).startLine + ":" + Location(location).startColumn + ": " + message)) + } +} + +[public] +function warning(string message, Location? location = null){ + if(location == null){ + print(console.yellow("Warning: " + message)) + }else{ + print(console.yellow("Warning: " + Location(location).file + ":" + Location(location).startLine + ":" + Location(location).startColumn + ": " + message)) + } +} + +[public] +function fatal_error(string message, Location? location = null){ + if(location == null){ + print(console.red("Error: " + message)) + }else{ + print(console.red("Error: " + Location(location).file + ":" + Location(location).startLine + ":" + Location(location).startColumn + ": " + message)) + } + ErrorUtils:hasCompilationErrors = true + exit(1) +} + +[public] +function syntax_error(string message, Location? location = null){ + if(location == null){ + print(console.red("Syntax Error: " + message)) + }else{ + print(console.red("Syntax Error: " + Location(location).file + ":" + Location(location).startLine + ":" + Location(location).startColumn + ": " + message)) + } + ErrorUtils:hasCompilationErrors = true + exit(1) +} + +[public] +function generic_error(string message, Location? location = null){ + if(location == null){ + print(console.red("Error: " + message)) + }else{ + print(console.red("Error: " + Location(location).file + ":" + Location(location).startLine + ":" + Location(location).startColumn + ": " + message)) + } + ErrorUtils:hasCompilationErrors = true +} + +[public] +function type_error(string message, Location? location = null){ + if(location == null){ + print(console.red("Type Error: " + message)) + }else{ + print(console.red("Type Error: " + Location(location).file + ":" + Location(location).startLine + ":" + Location(location).startColumn + ": " + message)) + } + ErrorUtils:hasCompilationErrors = true +} \ No newline at end of file diff --git a/stdlib/aspl/parser/utils/expressions.aspl b/stdlib/aspl/parser/utils/expressions.aspl new file mode 100644 index 0000000..6a695fc --- /dev/null +++ b/stdlib/aspl/parser/utils/expressions.aspl @@ -0,0 +1,15 @@ +import aspl.parser.ast +import aspl.parser.ast.statements +import aspl.parser.ast.expressions + +[public] +function verify_expression(Node node) returns Expression{ + if(!(node oftype Expression)){ + if(node oftype Statement){ + generic_error("Expected expression, but got statement", node.location) + }else{ + generic_error("Expected expression", node.location) + } + } + return Expression(node) +} \ No newline at end of file diff --git a/stdlib/aspl/parser/variables/Scope.aspl b/stdlib/aspl/parser/variables/Scope.aspl new file mode 100644 index 0000000..c43f5d7 --- /dev/null +++ b/stdlib/aspl/parser/variables/Scope.aspl @@ -0,0 +1,82 @@ +import aspl.parser +import aspl.parser.functions +import aspl.parser.methods +import aspl.parser.properties +import aspl.parser.callbacks + +[public] +class Scope { + + [static] + [threadlocal] + property list scopes = [] + + [public] + property map variables = {} + [readpublic] + property bool closure + [public] + property list capturedVariables = [] + + method construct(bool closure = false){ + this.closure = closure + } + + [public] + [static] + method pushBundle(Function|Method|Callback|ReactivePropertyCallback? func){ + self:scopes.add(new ScopeBundle(func)) + } + + [public] + [static] + method push(bool closure = false){ + self:getCurrentBundle().scopes.add(new self(closure)) + } + + [public] + [static] + method pop(){ + self:getCurrentBundle().scopes.removeAt(self:scopes[self:scopes.length - 1].scopes.length - 1) + } + + [public] + [static] + method popBundle(){ + self:scopes.removeAt(self:scopes.length - 1) + } + + [public] + [static] + method getCurrentBundle() returns ScopeBundle{ + return self:scopes[self:scopes.length - 1] + } + + [public] + [static] + method getCurrent() returns self{ + return self:getCurrentBundle().scopes[self:getCurrentBundle().scopes.length - 1] + } + + [public] + [static] + method passCapturedVariables(list capturedVariables){ + var i = Scope:getCurrentBundle().scopes.length + while(i > 0){ + i-- + var scope = Scope:getCurrentBundle().scopes[i] + if(scope.closure){ + foreach(capturedVariables as identifier){ + if(scope.variables.containsKey(identifier)){ + continue + } + if(!scope.capturedVariables.contains(identifier)){ + scope.capturedVariables.add(identifier) + } + } + return + } + } + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/variables/ScopeBundle.aspl b/stdlib/aspl/parser/variables/ScopeBundle.aspl new file mode 100644 index 0000000..d7c62b9 --- /dev/null +++ b/stdlib/aspl/parser/variables/ScopeBundle.aspl @@ -0,0 +1,19 @@ +import aspl.parser.functions +import aspl.parser.methods +import aspl.parser.properties +import aspl.parser.callbacks + +[public] +class ScopeBundle { + + [readpublic] + property list scopes = [] + [public] + property Function|Method|Callback|ReactivePropertyCallback? func + + [public] + method construct(Function|Method|Callback|ReactivePropertyCallback? func){ + this.func = func + } + +} \ No newline at end of file diff --git a/stdlib/aspl/parser/variables/Variable.aspl b/stdlib/aspl/parser/variables/Variable.aspl new file mode 100644 index 0000000..c1fe160 --- /dev/null +++ b/stdlib/aspl/parser/variables/Variable.aspl @@ -0,0 +1,61 @@ +import aspl.parser +import aspl.parser.utils + +[public] +class Variable { + + [readpublic] + property string identifier + [readpublic] + property Types types + + [public] + method construct(string identifier, Types types) { + this.identifier = identifier + this.types = types + } + + [public] + [static] + method register(string identifier, Types types, Location? location) returns Variable{ + if(self:exists(identifier)){ + aspl.parser.utils.generic_error("Variable '" + identifier + "' already defined somewhere else", location) + } + var v = new Variable(identifier, types) + Scope:getCurrent().variables[identifier] = v + return v + } + + [public] + [static] + method exists(string identifier) returns bool{ + foreach(Scope:getCurrentBundle().scopes as scope){ // TODO: Reverse order + if(scope.variables.containsKey(identifier)){ + return true + } + } + return false + } + + [public] + [static] + method get(string identifier) returns Variable{ + var Scope? closureScope = null + var i = Scope:getCurrentBundle().scopes.length + while(i > 0){ + i-- + var scope = Scope:getCurrentBundle().scopes[i] + if(scope.variables.containsKey(identifier)){ + if(closureScope != null && !closureScope?!.capturedVariables.contains(identifier)){ + closureScope?!.capturedVariables.add(identifier) + } + return scope.variables[identifier] + } + if(scope.closure && closureScope == null){ // at the end to prevent capturing variables from the current scope + closureScope = scope + } + } + aspl.parser.utils.fatal_error("Variable " + identifier + "' not found (compiler bug)") + } + +} \ No newline at end of file diff --git a/stdlib/bitconverter/converter.aspl b/stdlib/bitconverter/converter.aspl new file mode 100644 index 0000000..592ad59 --- /dev/null +++ b/stdlib/bitconverter/converter.aspl @@ -0,0 +1,49 @@ +[public] +function short_to_bytes(int s) returns list{ + return list(implement("bitconverter.convert.short_to_bytes", s)) +} + +[public] +function bytes_to_short(list b) returns int{ + return int(implement("bitconverter.convert.bytes_to_short", b)) +} + +[public] +function int_to_bytes(int i) returns list{ + return list(implement("bitconverter.convert.int_to_bytes", i)) +} + +[public] +function bytes_to_int(list b) returns int{ + return int(implement("bitconverter.convert.bytes_to_int", b)) +} + +[public] +function long_to_bytes(long l) returns list{ + return list(implement("bitconverter.convert.long_to_bytes", l)) +} + +[public] +function bytes_to_long(list b) returns long{ + return long(implement("bitconverter.convert.bytes_to_long", b)) +} + +[public] +function float_to_bytes(float f) returns list{ + return list(implement("bitconverter.convert.float_to_bytes", f)) +} + +[public] +function bytes_to_float(list b) returns float{ + return float(implement("bitconverter.convert.bytes_to_float", b)) +} + +[public] +function double_to_bytes(double d) returns list{ + return list(implement("bitconverter.convert.double_to_bytes", d)) +} + +[public] +function bytes_to_double(list b) returns double{ + return double(implement("bitconverter.convert.bytes_to_double", b)) +} \ No newline at end of file diff --git a/stdlib/bitconverter/implementations/implementations.aspl b/stdlib/bitconverter/implementations/implementations.aspl new file mode 100644 index 0000000..74ecfe5 --- /dev/null +++ b/stdlib/bitconverter/implementations/implementations.aspl @@ -0,0 +1,3 @@ +$if !twailbackend{ + $include("implementations.c") +} \ No newline at end of file diff --git a/stdlib/bitconverter/implementations/implementations.c b/stdlib/bitconverter/implementations/implementations.c new file mode 100644 index 0000000..c22564f --- /dev/null +++ b/stdlib/bitconverter/implementations/implementations.c @@ -0,0 +1,110 @@ +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_bitconverter$convert$short_to_bytes(ASPL_OBJECT_TYPE* value) { + ASPL_OBJECT_TYPE valueObj = (ASPL_OBJECT_TYPE)*value; + char* bytes = (char*)&ASPL_ACCESS(valueObj).value.integer32; + ASPL_OBJECT_TYPE* bytesObj = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 2); + for (int i = 0; i < 2; i++) + { + bytesObj[i] = ASPL_BYTE_LITERAL(bytes[i]); + } + return ASPL_LIST_LITERAL("list", 10, bytesObj, 2); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_bitconverter$convert$bytes_to_short(ASPL_OBJECT_TYPE* bytes) { + ASPL_OBJECT_TYPE bytesObj = (ASPL_OBJECT_TYPE)*bytes; + char* cBytes = ASPL_MALLOC(sizeof(char) * 2); + cBytes[0] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[0]).value.integer8; + cBytes[1] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[1]).value.integer8; + return ASPL_INT_LITERAL(*(short*)&cBytes); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_bitconverter$convert$int_to_bytes(ASPL_OBJECT_TYPE* value) { + ASPL_OBJECT_TYPE valueObj = (ASPL_OBJECT_TYPE)*value; + char* bytes = (char*)&ASPL_ACCESS(valueObj).value.integer32; + ASPL_OBJECT_TYPE* bytesObj = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 4); + for (int i = 0; i < 4; i++) + { + bytesObj[i] = ASPL_BYTE_LITERAL(bytes[i]); + } + return ASPL_LIST_LITERAL("list", 10, bytesObj, 4); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_bitconverter$convert$bytes_to_int(ASPL_OBJECT_TYPE* bytes) { + ASPL_OBJECT_TYPE bytesObj = (ASPL_OBJECT_TYPE)*bytes; + char* cBytes = ASPL_MALLOC(sizeof(char) * 4); + cBytes[0] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[0]).value.integer8; + cBytes[1] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[1]).value.integer8; + cBytes[2] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[2]).value.integer8; + cBytes[3] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[3]).value.integer8; + return ASPL_INT_LITERAL(*(int*)&cBytes); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_bitconverter$convert$long_to_bytes(ASPL_OBJECT_TYPE* value) { + ASPL_OBJECT_TYPE valueObj = (ASPL_OBJECT_TYPE)*value; + char* bytes = (char*)&ASPL_ACCESS(valueObj).value.integer64; + ASPL_OBJECT_TYPE* bytesObj = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 8); + for (int i = 0; i < 8; i++) + { + bytesObj[i] = ASPL_BYTE_LITERAL(bytes[i]); + } + return ASPL_LIST_LITERAL("list", 10, bytesObj, 8); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_bitconverter$convert$bytes_to_long(ASPL_OBJECT_TYPE* bytes) { + ASPL_OBJECT_TYPE bytesObj = (ASPL_OBJECT_TYPE)*bytes; + char* cBytes = ASPL_MALLOC(sizeof(char) * 8); + cBytes[0] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[0]).value.integer8; + cBytes[1] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[1]).value.integer8; + cBytes[2] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[2]).value.integer8; + cBytes[3] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[3]).value.integer8; + cBytes[4] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[4]).value.integer8; + cBytes[5] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[5]).value.integer8; + cBytes[6] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[6]).value.integer8; + cBytes[7] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[7]).value.integer8; + return ASPL_LONG_LITERAL(*(long*)&cBytes); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_bitconverter$convert$float_to_bytes(ASPL_OBJECT_TYPE* value) { + ASPL_OBJECT_TYPE valueObj = (ASPL_OBJECT_TYPE)*value; + char* bytes = (char*)&ASPL_ACCESS(valueObj).value.float32; + ASPL_OBJECT_TYPE* bytesObj = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 4); + for (int i = 0; i < 4; i++) + { + bytesObj[i] = ASPL_BYTE_LITERAL(bytes[i]); + } + return ASPL_LIST_LITERAL("list", 10, bytesObj, 4); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_bitconverter$convert$bytes_to_float(ASPL_OBJECT_TYPE* bytes) { + ASPL_OBJECT_TYPE bytesObj = (ASPL_OBJECT_TYPE)*bytes; + char* cBytes = ASPL_MALLOC(sizeof(char) * 4); + cBytes[0] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[0]).value.integer8; + cBytes[1] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[1]).value.integer8; + cBytes[2] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[2]).value.integer8; + cBytes[3] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[3]).value.integer8; + return ASPL_FLOAT_LITERAL(*(float*)&cBytes); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_bitconverter$convert$double_to_bytes(ASPL_OBJECT_TYPE* value) { + ASPL_OBJECT_TYPE valueObj = (ASPL_OBJECT_TYPE)*value; + char* bytes = (char*)&ASPL_ACCESS(valueObj).value.float64; + ASPL_OBJECT_TYPE* bytesObj = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 8); + for (int i = 0; i < 8; i++) + { + bytesObj[i] = ASPL_BYTE_LITERAL(bytes[i]); + } + return ASPL_LIST_LITERAL("list", 10, bytesObj, 8); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_bitconverter$convert$bytes_to_double(ASPL_OBJECT_TYPE* bytes) { + ASPL_OBJECT_TYPE bytesObj = (ASPL_OBJECT_TYPE)*bytes; + char* cBytes = ASPL_MALLOC(sizeof(char) * 8); + cBytes[0] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[0]).value.integer8; + cBytes[1] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[1]).value.integer8; + cBytes[2] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[2]).value.integer8; + cBytes[3] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[3]).value.integer8; + cBytes[4] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[4]).value.integer8; + cBytes[5] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[5]).value.integer8; + cBytes[6] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[6]).value.integer8; + cBytes[7] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[7]).value.integer8; + return ASPL_DOUBLE_LITERAL(*(double*)&cBytes); +} \ No newline at end of file diff --git a/stdlib/breakpoints/breakpoints.aspl b/stdlib/breakpoints/breakpoints.aspl new file mode 100644 index 0000000..adc727d --- /dev/null +++ b/stdlib/breakpoints/breakpoints.aspl @@ -0,0 +1,4 @@ +[public] +function break(string? message = null){ + implement("breakpoints.break", message) +} \ No newline at end of file diff --git a/stdlib/breakpoints/implementations/implementations.aspl b/stdlib/breakpoints/implementations/implementations.aspl new file mode 100644 index 0000000..1d716d9 --- /dev/null +++ b/stdlib/breakpoints/implementations/implementations.aspl @@ -0,0 +1,3 @@ +$if ailbackend{ + $include("implementations.c") +} \ No newline at end of file diff --git a/stdlib/breakpoints/implementations/implementations.c b/stdlib/breakpoints/implementations/implementations.c new file mode 100644 index 0000000..dba9673 --- /dev/null +++ b/stdlib/breakpoints/implementations/implementations.c @@ -0,0 +1,43 @@ +// NOTE: This implementation only works with the AIL backend, not the C backend! + +unsigned int aspl_util_breakpoints$log(char* msg, ...) { + va_list args; + va_start(args, msg); + printf("\033[90m"); + vprintf(msg, args); + printf("\033[0m\n"); + va_end(args); + return strlen(msg); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_breakpoints$break(ASPL_OBJECT_TYPE* message) { + if (ASPL_ACCESS(*message).kind != ASPL_OBJECT_KIND_NULL) { + aspl_util_breakpoints$log("Breakpoint: %s", ASPL_ACCESS(*message).value.string->str); + } + int n = 0; + while (1) { + char c[1024]; + fgets(c, 1024, stdin); + c[strlen(c) - 1] = '\0'; + if (strcmp(c, "c") == 0 || strcmp(c, "continue") == 0) { + // TODO: Fill in the correct number of backspaces to clear the breakpoint messages + (void)n; // prevent unused variable warning + return ASPL_UNINITIALIZED; + } else if (strcmp(c, "st") == 0 || strcmp(c, "stack") == 0 || strcmp(c, "stacktrace") == 0 || strcmp(c, "stack-trace") == 0 || strcmp(c, "stack_trace") == 0) { + aspl_ailinterpreter_display_stack_trace(aspl_ailinterpreter_current_thread_context); + } else if (strcmp(c, "q") == 0 || strcmp(c, "quit") == 0 || strcmp(c, "exit") == 0) { + exit(0); + } else if (strcmp(c, "sc") == 0 || strcmp(c, "scope") == 0) { + // TODO + } else if (strcmp(c, "h") == 0 || strcmp(c, "help") == 0) { + n += aspl_util_breakpoints$log("Commands:"); + n += aspl_util_breakpoints$log(" c, continue: Continue execution"); + n += aspl_util_breakpoints$log(" st, stack, stacktrace, stack-trace, stack_trace: Display stack trace"); + n += aspl_util_breakpoints$log(" q, quit, exit: Quit the program"); + n += aspl_util_breakpoints$log(" sc, scope: Display the current scope"); + n += aspl_util_breakpoints$log(" h, help: Display this help message"); + } else { + n += aspl_util_breakpoints$log("Unknown command: %s", c); + } + } +} \ No newline at end of file diff --git a/stdlib/chars/chars.aspl b/stdlib/chars/chars.aspl new file mode 100644 index 0000000..35acf94 --- /dev/null +++ b/stdlib/chars/chars.aspl @@ -0,0 +1,7 @@ +import encoding.utf8 +import bitconverter + +[public] +function int_to_char(int codepoint) returns string{ + return string(implement("chars.int_to_char", codepoint)) +} \ No newline at end of file diff --git a/stdlib/chars/implementations/implementations.aspl b/stdlib/chars/implementations/implementations.aspl new file mode 100644 index 0000000..74ecfe5 --- /dev/null +++ b/stdlib/chars/implementations/implementations.aspl @@ -0,0 +1,3 @@ +$if !twailbackend{ + $include("implementations.c") +} \ No newline at end of file diff --git a/stdlib/chars/implementations/implementations.c b/stdlib/chars/implementations/implementations.c new file mode 100644 index 0000000..c9f6eb2 --- /dev/null +++ b/stdlib/chars/implementations/implementations.c @@ -0,0 +1,7 @@ +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_chars$int_to_char(ASPL_OBJECT_TYPE* integer){ + ASPL_OBJECT_TYPE integerObj = (ASPL_OBJECT_TYPE)*integer; + char *string = ASPL_MALLOC(2); + string[0] = ASPL_ACCESS(integerObj).value.integer8; + string[1] = '\0'; + return ASPL_STRING_LITERAL(string); +} \ No newline at end of file diff --git a/stdlib/console/colors.aspl b/stdlib/console/colors.aspl new file mode 100644 index 0000000..627660f --- /dev/null +++ b/stdlib/console/colors.aspl @@ -0,0 +1,224 @@ +[public] +function format(string message, string open, string close) returns string{ + return "[" + open + "m" + message + "[" + close + "m" +} + +[public] +function format_rgb(string message, int r, int g, int b, string open, string close) returns string{ + return "[" + open + ";2;" + r + ";" + g + ";" + b + "m" + message + "[" + close + "m" +} + +[public] +function rgb(string message, int r, int g, int b) returns string{ + return format_rgb(message, r, g, b, "38", "39") +} + +[public] +function rgb_background(string message, int r, int g, int b) returns string{ + return format_rgb(message, r, g, b, "48", "49") +} + +[public] +function reset(string message) returns string{ + return format(message, "0", "0") +} + +[public] +function bold(string message) returns string{ + return format(message, "1", "22") +} + +[public] +function italic(string message) returns string{ + return format(message, "3", "23") +} + +[public] +function underline(string message) returns string{ + return format(message, "4", "24") +} + +[public] +function inverse(string message) returns string{ + return format(message, "7", "27") +} + +[public] +function hidden(string message) returns string{ + return format(message, "8", "28") +} + +[public] +function strikethrough(string message) returns string{ + return format(message, "9", "29") +} + +[public] +function black(string message) returns string{ + return format(message, "30", "39") +} + +[public] +function bright_black(string message) returns string{ + return format(message, "90", "39") +} + +[public] +function gray(string message) returns string{ + return bright_black(message) +} + +[public] +function black_background(string message) returns string{ + return format(message, "40", "49") +} + +[public] +function bright_black_background(string message) returns string{ + return format(message, "100", "49") +} + +[public] +function gray_background(string message) returns string{ + return bright_black_background(message) +} + +[public] +function red(string message) returns string{ + return format(message, "31", "39") +} + +[public] +function bright_red(string message) returns string{ + return format(message, "91", "39") +} + +[public] +function red_background(string message) returns string{ + return format(message, "41", "49") +} + +[public] +function bright_red_background(string message) returns string{ + return format(message, "101", "49") +} + +[public] +function green(string message) returns string{ + return format(message, "32", "39") +} + +[public] +function bright_green(string message) returns string{ + return format(message, "92", "39") +} + +[public] +function green_background(string message) returns string{ + return format(message, "42", "49") +} + +[public] +function bright_green_background(string message) returns string{ + return format(message, "102", "49") +} + +[public] +function yellow(string message) returns string{ + return format(message, "33", "39") +} + +[public] +function bright_yellow(string message) returns string{ + return format(message, "93", "39") +} + +[public] +function yellow_background(string message) returns string{ + return format(message, "43", "49") +} + +[public] +function bright_yellow_background(string message) returns string{ + return format(message, "103", "49") +} + +[public] +function blue(string message) returns string{ + return format(message, "34", "39") +} + +[public] +function bright_blue(string message) returns string{ + return format(message, "94", "39") +} + +[public] +function blue_background(string message) returns string{ + return format(message, "44", "49") +} + +[public] +function bright_blue_background(string message) returns string{ + return format(message, "104", "49") +} + +[public] +function magenta(string message) returns string{ + return format(message, "35", "39") +} + +[public] +function bright_magenta(string message) returns string{ + return format(message, "95", "39") +} + +[public] +function magenta_background(string message) returns string{ + return format(message, "45", "49") +} + +[public] +function bright_magenta_background(string message) returns string{ + return format(message, "105", "49") +} + +[public] +function cyan(string message) returns string{ + return format(message, "36", "39") +} + +[public] +function bright_cyan(string message) returns string{ + return format(message, "96", "39") +} + +[public] +function cyan_background(string message) returns string{ + return format(message, "46", "49") +} + +[public] +function bright_cyan_background(string message) returns string{ + return format(message, "106", "49") +} + +[public] +function white(string message) returns string{ + return format(message, "37", "39") +} + +[public] +function bright_white(string message) returns string{ + return format(message, "97", "39") +} + +[public] +function white_background(string message) returns string{ + return format(message, "47", "49") +} + +[public] +function bright_white_background(string message) returns string{ + return format(message, "107", "49") +} \ No newline at end of file diff --git a/stdlib/encoding/ascii/LegacyASCIITable.aspl b/stdlib/encoding/ascii/LegacyASCIITable.aspl new file mode 100644 index 0000000..914d072 --- /dev/null +++ b/stdlib/encoding/ascii/LegacyASCIITable.aspl @@ -0,0 +1,281 @@ +[public] +class LegacyASCIITable{ + + [public] + [static] + property map idToChar{ + get{ + return { + 0b => "", + 1b => "", + 2b => "", + 3b => "", + 4b => "", + 5b => "", + 6b => "", + 7b => "", + 8b => "", + 9b => " ", + 10b => "\n", + 11b => " ", + 12b => " ", + 13b => "\r", + 14b => "", + 15b => "", + 16b => "", + 17b => "", + 18b => "", + 19b => "", + 20b => "", + 21b => "", + 22b => "", + 23b => "", + 24b => "", + 25b => "", + 26b => "", + 27b => "", + 28b => "", + 29b => "", + 30b => "", + 31b => "", + 32b => " ", + 33b => "!", + 34b => "\"", + 35b => "#", + 36b => "$", + 37b => "%", + 38b => "&", + 39b => "'", + 40b => "(", + 41b => ")", + 42b => "*", + 43b => "+", + 44b => ",", + 45b => "-", + 46b => ".", + 47b => "/", + 48b => "0", + 49b => "1", + 50b => "2", + 51b => "3", + 52b => "4", + 53b => "5", + 54b => "6", + 55b => "7", + 56b => "8", + 57b => "9", + 58b => ":", + 59b => ";", + 60b => "<", + 61b => "=", + 62b => ">", + 63b => "?", + 64b => "@", + 65b => "A", + 66b => "B", + 67b => "C", + 68b => "D", + 69b => "E", + 70b => "F", + 71b => "G", + 72b => "H", + 73b => "I", + 74b => "J", + 75b => "K", + 76b => "L", + 77b => "M", + 78b => "N", + 79b => "O", + 80b => "P", + 81b => "Q", + 82b => "R", + 83b => "S", + 84b => "T", + 85b => "U", + 86b => "V", + 87b => "W", + 88b => "X", + 89b => "Y", + 90b => "Z", + 91b => "[", + 92b => "\\", + 93b => "]", + 94b => "^", + 95b => "_", + 96b => "`", + 97b => "a", + 98b => "b", + 99b => "c", + 100b => "d", + 101b => "e", + 102b => "f", + 103b => "g", + 104b => "h", + 105b => "i", + 106b => "j", + 107b => "k", + 108b => "l", + 109b => "m", + 110b => "n", + 111b => "o", + 112b => "p", + 113b => "q", + 114b => "r", + 115b => "s", + 116b => "t", + 117b => "u", + 118b => "v", + 119b => "w", + 120b => "x", + 121b => "y", + 122b => "z", + 123b => "{", + 124b => "|", + 125b => "}", + 126b => "~", + 127b => "", + 128b => "€", + 129b => "", + 130b => "‚", + 131b => "ƒ", + 132b => "„", + 133b => "…", + 134b => "†", + 135b => "‡", + 136b => "ˆ", + 137b => "‰", + 138b => "Š", + 139b => "‹", + 140b => "Œ", + 141b => "", + 142b => "Ž", + 143b => "", + 144b => "", + 145b => "‘", + 146b => "’", + 147b => "“", + 148b => "”", + 149b => "•", + 150b => "–", + 151b => "—", + 152b => "˜", + 153b => "™", + 154b => "š", + 155b => "›", + 156b => "œ", + 157b => "", + 158b => "ž", + 159b => "Ÿ", + 160b => " ", + 161b => "¡", + 162b => "¢", + 163b => "£", + 164b => "¤", + 165b => "¥", + 166b => "¦", + 167b => "§", + 168b => "¨", + 169b => "©", + 170b => "ª", + 171b => "«", + 172b => "¬", + 173b => "", + 174b => "®", + 175b => "¯", + 176b => "°", + 177b => "±", + 178b => "²", + 179b => "³", + 180b => "´", + 181b => "µ", + 182b => "¶", + 183b => "·", + 184b => "¸", + 185b => "¹", + 186b => "º", + 187b => "»", + 188b => "¼", + 189b => "½", + 190b => "¾", + 191b => "¿", + 192b => "À", + 193b => "Á", + 194b => "Â", + 195b => "Ã", + 196b => "Ä", + 197b => "Å", + 198b => "Æ", + 199b => "Ç", + 200b => "È", + 201b => "É", + 202b => "Ê", + 203b => "Ë", + 204b => "Ì", + 205b => "Í", + 206b => "Î", + 207b => "Ï", + 208b => "Ð", + 209b => "Ñ", + 210b => "Ò", + 211b => "Ó", + 212b => "Ô", + 213b => "Õ", + 214b => "Ö", + 215b => "×", + 216b => "Ø", + 217b => "Ù", + 218b => "Ú", + 219b => "Û", + 220b => "Ü", + 221b => "Ý", + 222b => "Þ", + 223b => "ß", + 224b => "à", + 225b => "á", + 226b => "â", + 227b => "ã", + 228b => "ä", + 229b => "å", + 230b => "æ", + 231b => "ç", + 232b => "è", + 233b => "é", + 234b => "ê", + 235b => "ë", + 236b => "ì", + 237b => "í", + 238b => "î", + 239b => "ï", + 240b => "ð", + 241b => "ñ", + 242b => "ò", + 243b => "ó", + 244b => "ô", + 245b => "õ", + 246b => "ö", + 247b => "÷", + 248b => "ø", + 249b => "ù", + 250b => "ú", + 251b => "û", + 252b => "ü", + 253b => "ý", + 254b => "þ", + 255b => "ÿ" + } + } + } + + [public] + [static] + property map charToId{ + get{ + var table = map{} + foreach(self:idToChar as k => v){ + table[v] = k + } + return table + } + } + +} \ No newline at end of file diff --git a/stdlib/encoding/ascii/decoder.aspl b/stdlib/encoding/ascii/decoder.aspl new file mode 100644 index 0000000..297de86 --- /dev/null +++ b/stdlib/encoding/ascii/decoder.aspl @@ -0,0 +1,15 @@ +import encoding.utf8 + +[public] +function decode(list bytes) returns string{ + var s = "" + foreach(bytes as b){ + s += decode_char(b) + } + return s +} + +[public] +function decode_char(byte b) returns string{ + return encoding.utf8.decode([b]) +} \ No newline at end of file diff --git a/stdlib/encoding/ascii/encoder.aspl b/stdlib/encoding/ascii/encoder.aspl new file mode 100644 index 0000000..2755e68 --- /dev/null +++ b/stdlib/encoding/ascii/encoder.aspl @@ -0,0 +1,15 @@ +import encoding.utf8 + +[public] +function encode(string s) returns list{ + var list bytes = [] + foreach(s as c){ + bytes.add(encode_char(c)) + } + return bytes +} + +[public] +function encode_char(string c) returns byte{ + return encoding.utf8.encode(c)[0] +} \ No newline at end of file diff --git a/stdlib/encoding/base64/base64.aspl b/stdlib/encoding/base64/base64.aspl new file mode 100644 index 0000000..a6214da --- /dev/null +++ b/stdlib/encoding/base64/base64.aspl @@ -0,0 +1,9 @@ +[public] +function encode(string s) returns string{ + return string(implement("encoding.base64.encode", s)) +} + +[public] +function decode(string s) returns string{ + return string(implement("encoding.base64.decode", s)) +} \ No newline at end of file diff --git a/stdlib/encoding/base64/implementations/implementations.aspl b/stdlib/encoding/base64/implementations/implementations.aspl new file mode 100644 index 0000000..74ecfe5 --- /dev/null +++ b/stdlib/encoding/base64/implementations/implementations.aspl @@ -0,0 +1,3 @@ +$if !twailbackend{ + $include("implementations.c") +} \ No newline at end of file diff --git a/stdlib/encoding/base64/implementations/implementations.c b/stdlib/encoding/base64/implementations/implementations.c new file mode 100644 index 0000000..8e445c3 --- /dev/null +++ b/stdlib/encoding/base64/implementations/implementations.c @@ -0,0 +1,104 @@ +// TODO: Test if this works + +const char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +char* aspl_util_encoding$base64$encode(const char* input, size_t length) { + char* encoded_data = NULL; + size_t encoded_length = 0; + + // Calculate the size of the encoded data + encoded_length = 4 * ((length + 2) / 3); + + // Allocate memory for the encoded data + encoded_data = (char*)ASPL_MALLOC(encoded_length + 1); + if (encoded_data == NULL) { + return NULL; // Memory allocation error + } + + // Encoding loop + size_t i = 0, j = 0; + while (i < length) { + uint32_t octet_a = i < length ? input[i++] : 0; + uint32_t octet_b = i < length ? input[i++] : 0; + uint32_t octet_c = i < length ? input[i++] : 0; + + uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; + + encoded_data[j++] = base64_table[(triple >> 3 * 6) & 0x3F]; + encoded_data[j++] = base64_table[(triple >> 2 * 6) & 0x3F]; + encoded_data[j++] = base64_table[(triple >> 1 * 6) & 0x3F]; + encoded_data[j++] = base64_table[(triple >> 0 * 6) & 0x3F]; + } + + // Add padding if necessary + for (size_t padding = 0; padding < (3 - (length % 3)) % 3; padding++) { + encoded_data[encoded_length - 1 - padding] = '='; + } + + // Null-terminate the encoded data + encoded_data[encoded_length] = '\0'; + + return encoded_data; +} + +char* aspl_util_encoding$base64$decode(const char* input, size_t length) { + char* decoded_data = NULL; + size_t decoded_length = 0; + + // Calculate the size of the decoded data + decoded_length = length / 4 * 3; + if (input[length - 1] == '=') { + decoded_length--; + } + if (input[length - 2] == '=') { + decoded_length--; + } + + // Allocate memory for the decoded data + decoded_data = (char*)ASPL_MALLOC(decoded_length + 1); + if (decoded_data == NULL) { + return NULL; // Memory allocation error + } + + // Decoding loop + size_t i = 0, j = 0; + while (i < length) { + // Accumulate 4 valid characters (ignore everything else) + uint32_t sextet_a = input[i] == '=' ? 0 & i++ : base64_table[(unsigned char)input[i++]]; + uint32_t sextet_b = input[i] == '=' ? 0 & i++ : base64_table[(unsigned char)input[i++]]; + uint32_t sextet_c = input[i] == '=' ? 0 & i++ : base64_table[(unsigned char)input[i++]]; + uint32_t sextet_d = input[i] == '=' ? 0 & i++ : base64_table[(unsigned char)input[i++]]; + + uint32_t triple = (sextet_a << 3 * 6) + + (sextet_b << 2 * 6) + + (sextet_c << 1 * 6) + + (sextet_d << 0 * 6); + + if (j < decoded_length) { + decoded_data[j++] = (triple >> 2 * 8) & 0xFF; + } + if (j < decoded_length) { + decoded_data[j++] = (triple >> 1 * 8) & 0xFF; + } + if (j < decoded_length) { + decoded_data[j++] = (triple >> 0 * 8) & 0xFF; + } + } + + // Null-terminate the decoded data + decoded_data[decoded_length] = '\0'; + + return decoded_data; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_encoding$base64$encode(ASPL_OBJECT_TYPE* string) { + ASPL_OBJECT_TYPE stringObj = (ASPL_OBJECT_TYPE)*string; + char* encoded = aspl_util_encoding$base64$encode(ASPL_ACCESS(stringObj).value.string->str, ASPL_ACCESS(stringObj).value.string->length); + return ASPL_STRING_LITERAL(encoded); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_encoding$base64$decode(ASPL_OBJECT_TYPE* string) { + ASPL_OBJECT_TYPE stringObj = (ASPL_OBJECT_TYPE)*string; + char* decoded = aspl_util_encoding$base64$decode(ASPL_ACCESS(stringObj).value.string->str, ASPL_ACCESS(stringObj).value.string->length); + return ASPL_STRING_LITERAL(decoded); +} \ No newline at end of file diff --git a/stdlib/encoding/hex/hex.aspl b/stdlib/encoding/hex/hex.aspl new file mode 100644 index 0000000..fbd39a1 --- /dev/null +++ b/stdlib/encoding/hex/hex.aspl @@ -0,0 +1,99 @@ +import bitconverter +import math + +[public] +function encode(string hex) returns int{ + var n = 0 + var i = 0 + foreach(hex as d){ + n += int(encode_digit(d) * math.pow(16, double(hex.length - i - 1))) + i++ + } + return n +} + +[public] +function encode_digit(string hex) returns byte{ + if(hex == "0"){ + return 0b + }elseif(hex == "1"){ + return 1b + }elseif(hex == "2"){ + return 2b + }elseif(hex == "3"){ + return 3b + }elseif(hex == "4"){ + return 4b + }elseif(hex == "5"){ + return 5b + }elseif(hex == "6"){ + return 6b + }elseif(hex == "7"){ + return 7b + }elseif(hex == "8"){ + return 8b + }elseif(hex == "9"){ + return 9b + }elseif(hex == "a"){ + return 10b + }elseif(hex == "b"){ + return 11b + }elseif(hex == "c"){ + return 12b + }elseif(hex == "d"){ + return 13b + }elseif(hex == "e"){ + return 14b + }elseif(hex == "f"){ + return 15b + } +} + +[public] +function decode(int n) returns string{ + var hex = "" + var i = 0 + while(n > 0){ + hex += decode_digit(byte(n % 16)) + n /= 16 + i++ + } + return hex.reverse() +} + +[public] +function decode_digit(byte b) returns string{ + if(b == 0b){ + return "0" + }elseif(b == 1b){ + return "1" + }elseif(b == 2b){ + return "2" + }elseif(b == 3b){ + return "3" + }elseif(b == 4b){ + return "4" + }elseif(b == 5b){ + return "5" + }elseif(b == 6b){ + return "6" + }elseif(b == 7b){ + return "7" + }elseif(b == 8b){ + return "8" + }elseif(b == 9b){ + return "9" + }elseif(b == 10b){ + return "a" + }elseif(b == 11b){ + return "b" + }elseif(b == 12b){ + return "c" + }elseif(b == 13b){ + return "d" + }elseif(b == 14b){ + return "e" + }elseif(b == 15b){ + return "f" + } +} \ No newline at end of file diff --git a/stdlib/encoding/utf8/implementations/implementations.aspl b/stdlib/encoding/utf8/implementations/implementations.aspl new file mode 100644 index 0000000..74ecfe5 --- /dev/null +++ b/stdlib/encoding/utf8/implementations/implementations.aspl @@ -0,0 +1,3 @@ +$if !twailbackend{ + $include("implementations.c") +} \ No newline at end of file diff --git a/stdlib/encoding/utf8/implementations/implementations.c b/stdlib/encoding/utf8/implementations/implementations.c new file mode 100644 index 0000000..32f9338 --- /dev/null +++ b/stdlib/encoding/utf8/implementations/implementations.c @@ -0,0 +1,97 @@ +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_encoding$utf8$encode(ASPL_OBJECT_TYPE* string) { + ASPL_OBJECT_TYPE stringObj = (ASPL_OBJECT_TYPE)*string; + ASPL_OBJECT_TYPE* bytes = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * ASPL_ACCESS(stringObj).value.string->length); + for (int i = 0; i < ASPL_ACCESS(stringObj).value.string->length; i++) + { + bytes[i] = ASPL_BYTE_LITERAL(ASPL_ACCESS(stringObj).value.string->str[i]); + } + return ASPL_LIST_LITERAL("list", 10, bytes, ASPL_ACCESS(stringObj).value.string->length); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_encoding$utf8$encode_char_to_int(ASPL_OBJECT_TYPE* c) { + ASPL_OBJECT_TYPE charObj = (ASPL_OBJECT_TYPE)*c; + int length = 0; + int value = 0; + for (int i = 0; i < ASPL_ACCESS(charObj).value.string->length; i++) + { + if (length == 0) + { + if ((ASPL_ACCESS(charObj).value.string->str[i] & 0b10000000) == 0b00000000) + { + value = ASPL_ACCESS(charObj).value.string->str[i]; + length = 0; + } + else if ((ASPL_ACCESS(charObj).value.string->str[i] & 0b11100000) == 0b11000000) + { + value = ASPL_ACCESS(charObj).value.string->str[i] & 0b00011111; + length = 1; + } + else if ((ASPL_ACCESS(charObj).value.string->str[i] & 0b11110000) == 0b11100000) + { + value = ASPL_ACCESS(charObj).value.string->str[i] & 0b00001111; + length = 2; + } + else if ((ASPL_ACCESS(charObj).value.string->str[i] & 0b11111000) == 0b11110000) + { + value = ASPL_ACCESS(charObj).value.string->str[i] & 0b00000111; + length = 3; + } + } + else + { + value = (value << 6) | (ASPL_ACCESS(charObj).value.string->str[i] & 0b00111111); + length--; + } + } + return ASPL_INT_LITERAL(value); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_encoding$utf8$decode(ASPL_OBJECT_TYPE* bytes) { + ASPL_OBJECT_TYPE bytesObj = (ASPL_OBJECT_TYPE)*bytes; + char* string = ASPL_MALLOC(ASPL_ACCESS(bytesObj).value.list->length + 1); + for (int i = 0; i < ASPL_ACCESS(bytesObj).value.list->length; i++) + { + string[i] = ASPL_ACCESS(ASPL_ACCESS(bytesObj).value.list->value[i]).value.integer8; + } + string[ASPL_ACCESS(bytesObj).value.list->length] = '\0'; + return ASPL_STRING_LITERAL(string); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_encoding$utf8$decode_int_to_char(ASPL_OBJECT_TYPE* i) { + ASPL_OBJECT_TYPE intObj = (ASPL_OBJECT_TYPE)*i; + char* string = ASPL_MALLOC(5); + if (ASPL_ACCESS(intObj).value.integer32 < 0x80) + { + string[0] = ASPL_ACCESS(intObj).value.integer32; + string[1] = '\0'; + return ASPL_STRING_LITERAL(string); + } + else if (ASPL_ACCESS(intObj).value.integer32 < 0x800) + { + string[0] = 0b11000000 | (ASPL_ACCESS(intObj).value.integer32 >> 6); + string[1] = 0b10000000 | (ASPL_ACCESS(intObj).value.integer32 & 0b00111111); + string[2] = '\0'; + return ASPL_STRING_LITERAL(string); + } + else if (ASPL_ACCESS(intObj).value.integer32 < 0x10000) + { + string[0] = 0b11100000 | (ASPL_ACCESS(intObj).value.integer32 >> 12); + string[1] = 0b10000000 | ((ASPL_ACCESS(intObj).value.integer32 >> 6) & 0b00111111); + string[2] = 0b10000000 | (ASPL_ACCESS(intObj).value.integer32 & 0b00111111); + string[3] = '\0'; + return ASPL_STRING_LITERAL(string); + } + else if (ASPL_ACCESS(intObj).value.integer32 < 0x110000) + { + string[0] = 0b11110000 | (ASPL_ACCESS(intObj).value.integer32 >> 18); + string[1] = 0b10000000 | ((ASPL_ACCESS(intObj).value.integer32 >> 12) & 0b00111111); + string[2] = 0b10000000 | ((ASPL_ACCESS(intObj).value.integer32 >> 6) & 0b00111111); + string[3] = 0b10000000 | (ASPL_ACCESS(intObj).value.integer32 & 0b00111111); + string[4] = '\0'; + return ASPL_STRING_LITERAL(string); + } + else + { + ASPL_PANIC("Invalid UTF-8 code point %d", ASPL_ACCESS(intObj).value.integer32); + } +} \ No newline at end of file diff --git a/stdlib/encoding/utf8/utf8.aspl b/stdlib/encoding/utf8/utf8.aspl new file mode 100644 index 0000000..b3dea92 --- /dev/null +++ b/stdlib/encoding/utf8/utf8.aspl @@ -0,0 +1,19 @@ +[public] +function encode(string s) returns list{ + return list(implement("encoding.utf8.encode", s)) +} + +[public] +function encode_char(string c) returns int{ + return int(implement("encoding.utf8.encode_char_to_int", c)) +} + +[public] +function decode(list bytes) returns string{ + return string(implement("encoding.utf8.decode", bytes)) +} + +[public] +function decode_char(int code) returns string{ + return string(implement("encoding.utf8.decode_int_to_char", code)) +} \ No newline at end of file diff --git a/stdlib/graphics/Canvas.aspl b/stdlib/graphics/Canvas.aspl new file mode 100644 index 0000000..b6ba791 --- /dev/null +++ b/stdlib/graphics/Canvas.aspl @@ -0,0 +1,165 @@ +import math.geometry + +[public] +class Canvas extends ICanvas{ + + property any handle = null + [readpublic] + property int width + [readpublic] + property int height + + [public] + method construct(int width, int height, any handle = null){ + this.width = width + this.height = height + if(handle == null){ + this.handle = implement("graphics.canvas.new_from_size", width, height) + }else{ + this.handle = handle + } + } + + // getInternalHandle returns the internal handle of the canvas + // this is used by other classes in the graphics module and should not be called from outside + [public] + method getInternalHandle() returns any{ + return this.handle + } + + [public] + method getPixel(int x, int y) returns Color{ + var color = list(implement("graphics.canvas.get_pixel", handle, x, y)) + return new Color(color[0], color[1], color[2], color[3]) + } + + [public] + method setPixel(int x, int y, Color color, bool blend = true){ + implement("graphics.canvas.set_pixel", this.handle, x, y, color.a, color.r, color.g, color.b, blend) + } + + [public] + method fill(Color color, bool blend = true){ + implement("graphics.canvas.fill", this.handle, color.a, color.r, color.g, color.b, blend) + } + + [public] + method drawImage(Canvas image, int x, int y, bool blend = true){ + implement("graphics.canvas.draw_image", this.handle, image.handle, x, y, blend) + } + + [public] + method drawLine(int x1, int y1, int x2, int y2, Color color, int thickness = 1, bool blend = true, bool antialias = true){ + implement("graphics.canvas.draw_line", this.handle, x1, y1, x2, y2, color.a, color.r, color.g, color.b, thickness, blend, antialias) + } + + [public] + method drawRectangle(Rectangle rectangle, Color color, bool blend = true){ + implement("graphics.canvas.draw_rectangle", this.handle, int(Point(rectangle.position).x), int(Point(rectangle.position).y), int(Point(rectangle.position).x) + int(Size(rectangle.size).width), int(Point(rectangle.position).y) + int(Size(rectangle.size).height), color.a, color.r, color.g, color.b, blend) + } + + [public] + method fillRectangle(Rectangle rectangle, Color color, bool blend = true){ + implement("graphics.canvas.fill_rectangle", this.handle, int(Point(rectangle.position).x), int(Point(rectangle.position).y), int(Point(rectangle.position).x) + int(Size(rectangle.size).width), int(Point(rectangle.position).y) + int(Size(rectangle.size).height), color.a, color.r, color.g, color.b, blend) + } + [public] + method drawCircle(Ellipse circle, Color color, bool blend = true){ + implement("graphics.canvas.draw_circle", this.handle, int(Point(circle.position).x), int(Point(circle.position).y), int(Size(circle.size).width), color.a, color.r, color.g, color.b, blend) + } + + [public] + method fillCircle(Ellipse circle, Color color, bool blend = true){ + implement("graphics.canvas.fill_circle", this.handle, int(Point(circle.position).x), int(Point(circle.position).y), int(Size(circle.size).width), color.a, color.r, color.g, color.b, blend) + } + + [public] + method drawText(string text, int x, int y, Font font, Color color, HorizontalAlignment horizontalAlignment = HorizontalAlignment.Left, VerticalAlignment verticalAlignment = VerticalAlignment.Bottom, bool blend = true){ + implement("graphics.canvas.draw_text", this.handle, text, x, y, font.path, font.size, color.a, color.r, color.g, color.b, int(horizontalAlignment), int(verticalAlignment), blend) + } + + [public] + method replaceColor(Color from, Color to, bool blend = true){ + implement("graphics.canvas.replace_color", this.handle, from.a, from.r, from.g, from.b, to.a, to.r, to.g, to.b, blend) + } + + // replaceColorIgnoreAlpha works like replaceColor, but checks only the R, G and B components and leaves the alpha value unchanged + [public] + method replaceColorIgnoreAlpha(Color from, Color to){ + implement("graphics.canvas.replace_color_ignore_alpha", this.handle, from.r, from.g, from.b, to.r, to.g, to.b) + } + + [public] + method blur(int radius){ + implement("graphics.canvas.blur", this.handle, radius) + } + + [public] + method resize(int width, int height){ + if(width == this.width && height == this.height){ + return + } + implement("graphics.canvas.resize_with_size", this.handle, width, height) + this.width = width + this.height = height + } + + [public] + method resizeScale(float scale){ + if(scale == 1f){ + return + } + implement("graphics.canvas.resize_with_scale", this.handle, scale) + this.width = int(this.width * scale) + this.height = int(this.height * scale) + } + + [public] + method extendTo(int width, int height){ + if(width <= this.width && height <= this.height){ + return + } + implement("graphics.canvas.extend_to", this.handle, width, height) + this.width = width + this.height = height + } + + [public] + method getSubImage(int x, int y, int width, int height) returns Canvas{ + return new Canvas(width, height, implement("graphics.canvas.get_sub_image", this.handle, x, y, width, height)) + } + + [public] + method copy() returns Canvas{ + return new Canvas(width, height, implement("graphics.canvas.copy", this.handle)) + } + + [public] + method save(string path){ + implement("graphics.canvas.save_to_file", this.handle, path) + } + + [public] + [static] + method fromFile(string file) returns Canvas{ + var handle = implement("graphics.canvas.load_from_file", file) + return new Canvas(int(implement("graphics.canvas.get_width", handle)), int(implement("graphics.canvas.get_height", handle)), handle) + } + + [public] + [static] + method fromFileData(list bytes) returns Canvas{ + var handle = implement("graphics.canvas.load_from_file_data", bytes) + return new Canvas(int(implement("graphics.canvas.get_width", handle)), int(implement("graphics.canvas.get_height", handle)), handle) + } + +} + +[public] +function get_image_width_from_file(string file) returns int{ + return int(implement("graphics.canvas.get_width_from_file", file)) +} + +[public] +function get_image_height_from_file(string file) returns int{ + return int(implement("graphics.canvas.get_height_from_file", file)) +} \ No newline at end of file diff --git a/stdlib/graphics/Color.aspl b/stdlib/graphics/Color.aspl new file mode 100644 index 0000000..b67ea2b --- /dev/null +++ b/stdlib/graphics/Color.aspl @@ -0,0 +1,53 @@ +import rand + +[public] +class Color{ + + [readpublic] + property byte r + [readpublic] + property byte g + [readpublic] + property byte b + [readpublic] + property byte a + + [public] + [deprecated("Use fromARGB instead")] + method construct(byte a, byte r, byte g, byte b){ + this.r = r + this.g = g + this.b = b + this.a = a + } + + [public] + [static] + method fromRGBA(byte r, byte g, byte b, byte a) returns self{ + return new self(a, r, g, b) + } + + [public] + [static] + method fromRGB(byte r, byte g, byte b) returns self{ + return self:fromRGBA(r, g, b, 255) + } + + [public] + [static] + method fromARGB(byte a, byte r, byte g, byte b) returns self{ + return self:fromRGBA(r, g, b, a) + } + + [public] + method toString() returns string{ + return "graphics.Color(r: " + string(r) + ", g: " + string(g) + ", b: " + string(b) + ", a: " + string(a) + ")" + } + + [public] + [static] + method random() returns Color{ + return self:fromRGB(rand.byte(), rand.byte(), rand.byte()) + } + +} \ No newline at end of file diff --git a/stdlib/graphics/Font.aspl b/stdlib/graphics/Font.aspl new file mode 100644 index 0000000..373f827 --- /dev/null +++ b/stdlib/graphics/Font.aspl @@ -0,0 +1,76 @@ +import io + +[public] +class Font{ + + [static] + property Font _default + + [public] + [static] + property Font default{ + get{ + if(_default == null){ + var path = string(implement("graphics.font.get_default_font_path")) + var name = io.file_name(path) + _default = new Font(name, path, path, 12) + } + return Font(_default) + } + } + + [readpublic] + property string name + [readpublic] + property string path + [readpublic] + property string regularPath + [readpublic] + property int size + [readpublic] + property bool bold + [readpublic] + property bool italic + [readpublic] + property bool underline + [readpublic] + property bool strikeout + + [public] + method construct(string name, string path, string regularPath, int size, bool bold = false, bool italic = false, bool underline = false, bool strikeout = false) { + this.name = name + this.path = path + this.regularPath = regularPath + this.size = size + this.bold = bold + this.italic = italic + this.underline = underline + this.strikeout = strikeout + } + + [public] + method withSize(int size) returns Font { + return new Font(this.name, this.path, this.regularPath, size, this.bold, this.italic, this.underline, this.strikeout) + } + + [public] + method asBold(bool value = true) returns Font{ + return new Font(this.name, string(implement("graphics.font.get_font_variant_path", this.path, value, this.italic, this.underline, this.strikeout)), regularPath, this.size, value, this.italic, this.underline, this.strikeout) + } + + [public] + method asItalic(bool value = true) returns Font{ + return new Font(this.name, string(implement("graphics.font.get_font_variant_path", this.path, this.bold, true, this.underline, this.strikeout)), regularPath, this.size, this.bold, value, this.underline, this.strikeout) + } + + [public] + method asUnderlined(bool value = true) returns Font{ + return new Font(this.name, string(implement("graphics.font.get_font_variant_path", this.path, this.bold, this.italic, true, this.strikeout)), regularPath, this.size, this.bold, this.italic, value, this.strikeout) + } + + [public] + method asStrikedout(bool value = true) returns Font{ + return new Font(this.name, string(implement("graphics.font.get_font_variant_path", this.path, this.bold, this.italic, this.underline, value)), regularPath, this.size, this.bold, this.italic, this.underline, value) + } + +} \ No newline at end of file diff --git a/stdlib/graphics/HorizontalAlignment.aspl b/stdlib/graphics/HorizontalAlignment.aspl new file mode 100644 index 0000000..75de609 --- /dev/null +++ b/stdlib/graphics/HorizontalAlignment.aspl @@ -0,0 +1,6 @@ +[public] +enum HorizontalAlignment { + Left, + Center, + Right +} \ No newline at end of file diff --git a/stdlib/graphics/ICanvas.aspl b/stdlib/graphics/ICanvas.aspl new file mode 100644 index 0000000..ecd15ac --- /dev/null +++ b/stdlib/graphics/ICanvas.aspl @@ -0,0 +1,89 @@ +import math.geometry + +[public] +[abstract] +class ICanvas { + + [readpublic] + property int width + [readpublic] + property int height + + [public] + [abstract] + method getPixel(int x, int y) returns Color + + [public] + [abstract] + method setPixel(int x, int y, Color color, bool blend = true) + + [public] + [abstract] + method fill(Color color, bool blend = true) + + [public] + [abstract] + method drawImage(Canvas image, int x, int y, bool blend = true) + + [public] + [abstract] + method drawLine(int x1, int y1, int x2, int y2, Color color, int thickness = 1, bool blend = true, bool antialias = true) + + [public] + [abstract] + method drawRectangle(Rectangle rectangle, Color color, bool blend = true) + + [public] + [abstract] + method fillRectangle(Rectangle rectangle, Color color, bool blend = true) + + [public] + [abstract] + method drawCircle(Ellipse circle, Color color, bool blend = true) + + [public] + [abstract] + method fillCircle(Ellipse circle, Color color, bool blend = true) + + [public] + [abstract] + method drawText(string text, int x, int y, Font font, Color color, HorizontalAlignment horizontalAlignment = HorizontalAlignment.Left, VerticalAlignment verticalAlignment = VerticalAlignment.Bottom, bool blend = true) + + [public] + [abstract] + method replaceColor(Color from, Color to, bool blend = true) + + [public] + [abstract] + // replaceColorIgnoreAlpha works like replaceColor, but checks only the R, G and B components and leaves the alpha value unchanged + method replaceColorIgnoreAlpha(Color from, Color to) + + [public] + [abstract] + method blur(int radius) + + [public] + [abstract] + method resize(int width, int height) + + [public] + [abstract] + method resizeScale(float scale) + + [public] + [abstract] + method extendTo(int width, int height) + + [public] + [abstract] + method getSubImage(int x, int y, int width, int height) returns Canvas + + [public] + [abstract] + method copy() returns Canvas + + [public] + [abstract] + method save(string path) + +} \ No newline at end of file diff --git a/stdlib/graphics/KeyCode.aspl b/stdlib/graphics/KeyCode.aspl new file mode 100644 index 0000000..34f3adf --- /dev/null +++ b/stdlib/graphics/KeyCode.aspl @@ -0,0 +1,124 @@ +[public] +enum KeyCode{ + invalid = 0, + space = 32, + apostrophe = 39, //' + comma = 44, //, + minus = 45, //- + period = 46, //. + slash = 47, /// + _0 = 48, + _1 = 49, + _2 = 50, + _3 = 51, + _4 = 52, + _5 = 53, + _6 = 54, + _7 = 55, + _8 = 56, + _9 = 57, + semicolon = 59, //; + equal = 61, //= + a = 65, + b = 66, + c = 67, + d = 68, + e = 69, + f = 70, + g = 71, + h = 72, + i = 73, + j = 74, + k = 75, + l = 76, + m = 77, + n = 78, + o = 79, + p = 80, + q = 81, + r = 82, + s = 83, + t = 84, + u = 85, + v = 86, + w = 87, + x = 88, + y = 89, + z = 90, + left_bracket = 91, //[ + backslash = 92, //\ + right_bracket = 93, //] + grave_accent = 96, //` + world_1 = 161, // non-us #1 + world_2 = 162, // non-us #2 + escape = 256, + enter = 257, + tab = 258, + backspace = 259, + insert = 260, + delete = 261, + right = 262, + left = 263, + down = 264, + up = 265, + page_up = 266, + page_down = 267, + home = 268, + end = 269, + caps_lock = 280, + scroll_lock = 281, + num_lock = 282, + print_screen = 283, + pause = 284, + f1 = 290, + f2 = 291, + f3 = 292, + f4 = 293, + f5 = 294, + f6 = 295, + f7 = 296, + f8 = 297, + f9 = 298, + f10 = 299, + f11 = 300, + f12 = 301, + f13 = 302, + f14 = 303, + f15 = 304, + f16 = 305, + f17 = 306, + f18 = 307, + f19 = 308, + f20 = 309, + f21 = 310, + f22 = 311, + f23 = 312, + f24 = 313, + f25 = 314, + kp_0 = 320, + kp_1 = 321, + kp_2 = 322, + kp_3 = 323, + kp_4 = 324, + kp_5 = 325, + kp_6 = 326, + kp_7 = 327, + kp_8 = 328, + kp_9 = 329, + kp_decimal = 330, + kp_divide = 331, + kp_multiply = 332, + kp_subtract = 333, + kp_add = 334, + kp_enter = 335, + kp_equal = 336, + left_shift = 340, + left_control = 341, + left_alt = 342, + left_super = 343, + right_shift = 344, + right_control = 345, + right_alt = 346, + right_super = 347, + menu = 348, +} \ No newline at end of file diff --git a/stdlib/graphics/LazyChunkedCanvas.aspl b/stdlib/graphics/LazyChunkedCanvas.aspl new file mode 100644 index 0000000..e981ff6 --- /dev/null +++ b/stdlib/graphics/LazyChunkedCanvas.aspl @@ -0,0 +1,164 @@ +import math.geometry + +[public] +class LazyChunkedCanvas extends ICanvas{ + + property any handle = null + [readpublic] + property int width + [readpublic] + property int height + [readpublic] + property string directory + + // TODO: Use implementation calls to gather the below information + [readpublic] + [static] + property int CHUNK_WIDTH = 256 + [readpublic] + [static] + property int CHUNK_HEIGHT = 256 + + [public] + method construct(int width, int height, string directory, any handle = null){ + this.width = width + this.height = height + this.directory = directory + if(handle == null){ + this.handle = implement("graphics.canvas.lazy_chunked.new_from_size", width, height, 4, directory) + }else{ + this.handle = handle + } + } + + [public] + method getPixel(int x, int y) returns Color{ + var color = list(implement("graphics.canvas.lazy_chunked.get_pixel", handle, x, y)) + return new Color(color[0], color[1], color[2], color[3]) + } + + [public] + method setPixel(int x, int y, Color color, bool blend = true){ + implement("graphics.canvas.lazy_chunked.set_pixel", handle, x, y, color.a, color.r, color.g, color.b, blend) + } + + [public] + method fill(Color color, bool blend = true){ + implement("graphics.canvas.lazy_chunked.fill", handle, color.a, color.r, color.g, color.b, blend) + } + + [public] + method drawImage(Canvas image, int x, int y, bool blend = true){ + implement("graphics.canvas.lazy_chunked.draw_image", handle, image.getInternalHandle(), x, y, blend) + } + + [public] + method drawLine(int x1, int y1, int x2, int y2, Color color, int thickness = 1, bool blend = true, bool antialias = true){ + implement("graphics.canvas.lazy_chunked.draw_line", handle, x1, y1, x2, y2, color.a, color.r, color.g, color.b, thickness, blend, antialias) + } + + [public] + method drawRectangle(Rectangle rectangle, Color color, bool blend = true){ + implement("graphics.canvas.lazy_chunked.draw_rectangle", handle, int(Point(rectangle.position).x), int(Point(rectangle.position).y), int(Point(rectangle.position).x) + int(Size(rectangle.size).width), int(Point(rectangle.position).y) + int(Size(rectangle.size).height), color.a, color.r, color.g, color.b, blend) + } + + [public] + method fillRectangle(Rectangle rectangle, Color color, bool blend = true){ + implement("graphics.canvas.lazy_chunked.fill_rectangle", handle, int(Point(rectangle.position).x), int(Point(rectangle.position).y), int(Point(rectangle.position).x) + int(Size(rectangle.size).width), int(Point(rectangle.position).y) + int(Size(rectangle.size).height), color.a, color.r, color.g, color.b, blend) + } + + [public] + method drawCircle(Ellipse circle, Color color, bool blend = true){ + implement("graphics.canvas.lazy_chunked.draw_circle", handle, int(Point(circle.position).x), int(Point(circle.position).y), int(Size(circle.size).width), color.a, color.r, color.g, color.b, blend) + } + + [public] + method fillCircle(Ellipse circle, Color color, bool blend = true){ + implement("graphics.canvas.lazy_chunked.fill_circle", handle, int(Point(circle.position).x), int(Point(circle.position).y), int(Size(circle.size).width), color.a, color.r, color.g, color.b, blend) + } + + [public] + method drawText(string text, int x, int y, Font font, Color color, HorizontalAlignment horizontalAlignment = HorizontalAlignment.Left, VerticalAlignment verticalAlignment = VerticalAlignment.Bottom, bool blend = true){ + implement("graphics.canvas.lazy_chunked.draw_text", handle, text, x, y, font.path, font.size, color.a, color.r, color.g, color.b, int(horizontalAlignment), int(verticalAlignment), blend) + } + + [public] + method replaceColor(Color from, Color to, bool blend = true){ + implement("graphics.canvas.lazy_chunked.replace_color", handle, from.a, from.r, from.g, from.b, to.a, to.r, to.g, to.b, blend) + } + + // replaceColorIgnoreAlpha works like replaceColor, but checks only the R, G and B components and leaves the alpha value unchanged + [public] + method replaceColorIgnoreAlpha(Color from, Color to){ + implement("graphics.canvas.lazy_chunked.replace_color_ignore_alpha", handle, from.r, from.g, from.b, to.r, to.g, to.b) + } + + [public] + method blur(int radius){ + implement("graphics.canvas.lazy_chunked.blur", handle, radius) + } + + [public] + method resize(int width, int height){ + implement("graphics.canvas.lazy_chunked.resize_with_size", handle, width, height) + this.width = width + this.height = height + } + + [public] + method resizeScale(float scale){ + implement("graphics.canvas.lazy_chunked.resize_with_scale", handle, scale) + this.width = int(this.width * scale) + this.height = int(this.height * scale) + } + + [public] + method extendTo(int width, int height){ + implement("graphics.canvas.lazy_chunked.extend_to", handle, width, height) + this.width = width + this.height = height + } + + [public] + method getSubImage(int x, int y, int width, int height) returns Canvas{ + return new Canvas(width, height, implement("graphics.canvas.lazy_chunked.get_sub_image", this.handle, x, y, width, height)) + } + + [public] + method copy() returns LazyChunkedCanvas{ + return new LazyChunkedCanvas(width, height, directory, implement("graphics.canvas.lazy_chunked.copy", this.handle)) + } + + [public] + method requireArea(int x, int y, int width, int height){ + implement("graphics.canvas.lazy_chunked.require_area", this.handle, x, y, width, height) + } + + [public] + method isChunkLoaded(int x, int y) returns bool{ + return bool(implement("graphics.canvas.lazy_chunked.is_chunk_loaded", this.handle, x, y)) + } + + [public] + method loadChunk(int x, int y){ + implement("graphics.canvas.lazy_chunked.load_chunk", this.handle, x, y) + } + + [public] + method unloadChunk(int x, int y){ + implement("graphics.canvas.lazy_chunked.unload_chunk", this.handle, x, y) + } + + [public] + method save(string path){ + implement("graphics.canvas.lazy_chunked.save_to_file", this.handle, path) + } + + [public] + [static] + method fromFile(string file, string directory) returns LazyChunkedCanvas{ + var handle = implement("graphics.canvas.lazy_chunked.load_from_file", file, directory) + return new LazyChunkedCanvas(int(implement("graphics.canvas.get_width", handle)), int(implement("graphics.canvas.get_height", handle)), directory, handle) + } + +} \ No newline at end of file diff --git a/stdlib/graphics/MouseButton.aspl b/stdlib/graphics/MouseButton.aspl new file mode 100644 index 0000000..5010ce9 --- /dev/null +++ b/stdlib/graphics/MouseButton.aspl @@ -0,0 +1,7 @@ +[public] +enum MouseButton { + Left = 0, + Right = 1, + Middle = 2, + Invalid = 256 +} \ No newline at end of file diff --git a/stdlib/graphics/PrimitiveChunkedCanvas.aspl b/stdlib/graphics/PrimitiveChunkedCanvas.aspl new file mode 100644 index 0000000..e50bb48 --- /dev/null +++ b/stdlib/graphics/PrimitiveChunkedCanvas.aspl @@ -0,0 +1,153 @@ +import math.geometry + +[public] +class PrimitiveChunkedCanvas extends ICanvas{ + + property any handle = null + [public] + property int width + [public] + property int height + + // TODO: Use implementation calls to gather the below information + [readpublic] + [static] + property int CHUNK_WIDTH = 256 + [readpublic] + [static] + property int CHUNK_HEIGHT = 256 + + [public] + method construct(int width, int height, any handle = null){ + this.width = width + this.height = height + if(handle == null){ + this.handle = implement("graphics.canvas.primitive_chunked.new_from_size", width, height, 4) + }else{ + this.handle = handle + } + } + + [public] + method getPixel(int x, int y) returns Color{ + var color = list(implement("graphics.canvas.primitive_chunked.get_pixel", handle, x, y)) + return new Color(color[0], color[1], color[2], color[3]) + } + + [public] + method setPixel(int x, int y, Color color, bool blend = true){ + implement("graphics.canvas.primitive_chunked.set_pixel", this.handle, x, y, color.a, color.r, color.g, color.b, blend) + } + + [public] + method fill(Color color, bool blend = true){ + implement("graphics.canvas.primitive_chunked.fill", this.handle, color.a, color.r, color.g, color.b, blend) + } + + [public] + method drawImage(Canvas image, int x, int y, bool blend = true){ + implement("graphics.canvas.primitive_chunked.draw_image", this.handle, image.getInternalHandle(), x, y, blend) + } + + [public] + method drawLine(int x1, int y1, int x2, int y2, Color color, int thickness = 1, bool blend = true, bool antialias = true){ + implement("graphics.canvas.primitive_chunked.draw_line", this.handle, x1, y1, x2, y2, color.a, color.r, color.g, color.b, thickness, blend, antialias) + } + + [public] + method drawRectangle(Rectangle rectangle, Color color, bool blend = true){ + implement("graphics.canvas.primitive_chunked.draw_rectangle", this.handle, int(Point(rectangle.position).x), int(Point(rectangle.position).y), int(Point(rectangle.position).x) + int(Size(rectangle.size).width), int(Point(rectangle.position).y) + int(Size(rectangle.size).height), color.a, color.r, color.g, color.b, blend) + } + + [public] + method fillRectangle(Rectangle rectangle, Color color, bool blend = true){ + implement("graphics.canvas.primitive_chunked.fill_rectangle", this.handle, int(Point(rectangle.position).x), int(Point(rectangle.position).y), int(Point(rectangle.position).x) + int(Size(rectangle.size).width), int(Point(rectangle.position).y) + int(Size(rectangle.size).height), color.a, color.r, color.g, color.b, blend) + } + + [public] + method drawCircle(Ellipse circle, Color color, bool blend = true){ + implement("graphics.canvas.primitive_chunked.draw_circle", this.handle, int(Point(circle.position).x), int(Point(circle.position).y), int(Size(circle.size).width), color.a, color.r, color.g, color.b, blend) + } + + [public] + method fillCircle(Ellipse circle, Color color, bool blend = true){ + implement("graphics.canvas.primitive_chunked.fill_circle", this.handle, int(Point(circle.position).x), int(Point(circle.position).y), int(Size(circle.size).width), color.a, color.r, color.g, color.b, blend) + } + + [public] + method drawText(string text, int x, int y, Font font, Color color, HorizontalAlignment horizontalAlignment = HorizontalAlignment.Left, VerticalAlignment verticalAlignment = VerticalAlignment.Bottom, bool blend = true){ + implement("graphics.canvas.primitive_chunked.draw_text", this.handle, text, x, y, font.path, font.size, color.a, color.r, color.g, color.b, int(horizontalAlignment), int(verticalAlignment), blend) + } + + [public] + method replaceColor(Color from, Color to, bool blend = true){ + implement("graphics.canvas.primitive_chunked.replace_color", this.handle, from.a, from.r, from.g, from.b, to.a, to.r, to.g, to.b, blend) + } + + // replaceColorIgnoreAlpha works like replaceColor, but checks only the R, G and B components and leaves the alpha value unchanged + [public] + method replaceColorIgnoreAlpha(Color from, Color to){ + implement("graphics.canvas.primitive_chunked.replace_color_ignore_alpha", this.handle, from.r, from.g, from.b, to.r, to.g, to.b) + } + + [public] + method blur(int radius){ + implement("graphics.canvas.primitive_chunked.blur", this.handle, radius) + } + + [public] + method resize(int width, int height){ + implement("graphics.canvas.primitive_chunked.resize_with_size", this.handle, width, height) + this.width = width + this.height = height + } + + [public] + method resizeScale(float scale){ + implement("graphics.canvas.primitive_chunked.resize_with_scale", this.handle, scale) + this.width = int(this.width * scale) + this.height = int(this.height * scale) + } + + [public] + method extendTo(int width, int height){ + implement("graphics.canvas.primitive_chunked.extend_to", this.handle, width, height) + this.width = width + this.height = height + } + + [public] + method getSubImage(int x, int y, int width, int height) returns Canvas{ + return new Canvas(width, height, implement("graphics.canvas.primitive_chunked.get_sub_image", this.handle, x, y, width, height)) + } + + [public] + method copy() returns PrimitiveChunkedCanvas{ + return new PrimitiveChunkedCanvas(width, height, implement("graphics.canvas.primitive_chunked.copy", this.handle)) + } + + [public] + method save(string path){ + implement("graphics.canvas.primitive_chunked.save_to_file", this.handle, path) + } + + [public] + method convertToLazy(string file, string directory){ + implement("graphics.canvas.primitive_chunked.convert_to_lazy", this.handle, file, directory) + } + + [public] + [static] + method fromFile(string file) returns PrimitiveChunkedCanvas{ + var handle = implement("graphics.canvas.primitive_chunked.load_from_file", file) + return new PrimitiveChunkedCanvas(int(implement("graphics.canvas.primitive_chunked.get_width", handle)), int(implement("graphics.canvas.primitive_chunked.get_height", handle)), handle) + } + + [public] + [static] + method fromFileData(list bytes) returns PrimitiveChunkedCanvas{ + var handle = implement("graphics.canvas.primitive_chunked.load_from_file_data", bytes) + return new PrimitiveChunkedCanvas(int(implement("graphics.canvas.primitive_chunked.get_width", handle)), int(implement("graphics.canvas.primitive_chunked.get_height", handle)), handle) + } + +} \ No newline at end of file diff --git a/stdlib/graphics/TouchPoint.aspl b/stdlib/graphics/TouchPoint.aspl new file mode 100644 index 0000000..cbe4bdc --- /dev/null +++ b/stdlib/graphics/TouchPoint.aspl @@ -0,0 +1,23 @@ +import math.geometry + +[public] +class TouchPoint { + + [readpublic] + property long identifier + [readpublic] + property Point position + [readpublic] + property TouchToolType toolType + [readpublic] + property bool changed + + [public] + method construct(long identifier, Point position, TouchToolType toolType, bool changed){ + this.identifier = identifier + this.position = position + this.toolType = toolType + this.changed = changed + } + +} \ No newline at end of file diff --git a/stdlib/graphics/TouchToolType.aspl b/stdlib/graphics/TouchToolType.aspl new file mode 100644 index 0000000..62afce8 --- /dev/null +++ b/stdlib/graphics/TouchToolType.aspl @@ -0,0 +1,9 @@ +[public] +enum TouchToolType{ + Unknown, + Finger, + Stylus, + Mouse, + Eraser, + Palm +} \ No newline at end of file diff --git a/stdlib/graphics/VerticalAlignment.aspl b/stdlib/graphics/VerticalAlignment.aspl new file mode 100644 index 0000000..37820b8 --- /dev/null +++ b/stdlib/graphics/VerticalAlignment.aspl @@ -0,0 +1,6 @@ +[public] +enum VerticalAlignment { + Top, + Center, + Bottom +} \ No newline at end of file diff --git a/stdlib/graphics/Window.aspl b/stdlib/graphics/Window.aspl new file mode 100644 index 0000000..211273b --- /dev/null +++ b/stdlib/graphics/Window.aspl @@ -0,0 +1,161 @@ +import math.geometry + +[public] +class Window{ + + property any handle = implement("graphics.window.new") + property string _title + [public] + property string title{ + get{ + return _title + } + set{ + _title = value + implement("graphics.window.set_title", handle, value) + } + } + [readpublic] + property int width + [readpublic] + property int height + + [public] + property int fps{ + get{ + return int(implement("graphics.window.get_fps", handle)) + } + } + + [public] + property callback onLoad = callback(){} + [public] + property callback onPaint = callback(Canvas canvas){} + [public] + property callback onResize = callback(int width, int height){} + [public] + property callback onKeyPress = callback(KeyCode key){} + [public] + property callback onKeyDown = callback(KeyCode key){} + [public] + property callback onKeyUp = callback(KeyCode key){} + [public] + property callback onMouseClick = callback(Point position, MouseButton button){} + [public] + property callback onMouseDown = callback(Point position, MouseButton button){} + [public] + property callback onMouseUp = callback(Point position, MouseButton button){} + [public] + property callback onMouseMove = callback(Point from, float deltaX, float deltaY){} + [public] + property callback onMouseWheel = callback(Point position, float deltaX, float deltaY){} + [public] + property callback> onTouchDown = callback(list points){} + [public] + property callback> onTouchMove = callback(list points){} + [public] + property callback> onTouchUp = callback(list points){} + + property list currentlyDownKeys = list[] + + [public] + method construct(string|int title, int width, int? height = null){ + if(height == null){ + // legacy support... + height = width + width = int(title) + title = "" + } + implement("graphics.window.set_on_load", handle, callback(){ + onLoad.() + }) + this.title = title + this.width = width + this.height = height + implement("graphics.window.set_size", handle, width, height) + implement("graphics.window.set_on_resize", handle, callback(int newWidth, int newHeight){ + onResize.(newWidth, newHeight) + this.width = newWidth + this.height = newHeight + }) + implement("graphics.window.set_on_paint", handle, callback(any handle){ + var Canvas canvas = new Canvas(int(implement("graphics.canvas.get_width", handle)), int(implement("graphics.canvas.get_height", handle)), handle) + // TODO: The below two lines are needed since the window doesn't update its size correctly sometimes + this.width = canvas.width + this.height = canvas.height + onPaint.(canvas) + }) + implement("graphics.window.set_on_key_press", handle, callback(int key){ + onKeyPress.(KeyCode(key)) + }) + implement("graphics.window.set_on_key_down", handle, callback(int key){ + if(!currentlyDownKeys.contains(KeyCode(key))){ + currentlyDownKeys.add(KeyCode(key)) + } + onKeyDown.(KeyCode(key)) + }) + implement("graphics.window.set_on_key_up", handle, callback(int key){ + if(currentlyDownKeys.contains(KeyCode(key))){ + currentlyDownKeys.remove(KeyCode(key)) + } + onKeyUp.(KeyCode(key)) + }) + implement("graphics.window.set_on_mouse_click", handle, callback(float x, float y, int button){ + onMouseClick.(new Point(x, y), MouseButton(button)) + }) + implement("graphics.window.set_on_mouse_down", handle, callback(float x, float y, int button){ + onMouseDown.(new Point(x, y), MouseButton(button)) + }) + implement("graphics.window.set_on_mouse_up", handle, callback(float x, float y, int button){ + onMouseUp.(new Point(x, y), MouseButton(button)) + }) + implement("graphics.window.set_on_mouse_move", handle, callback(float fromX, float fromY, float deltaX, float deltaY){ + onMouseMove.(new Point(fromX, fromY), deltaX, deltaY) + }) + implement("graphics.window.set_on_mouse_wheel", handle, callback(float x, float y, float deltaX, float deltaY){ + onMouseWheel.(new Point(x, y), deltaX, deltaY) + }) + implement("graphics.window.set_on_touch_down", handle, callback(list> touches){ + var touchPoints = list[] + foreach(touches as touchPoint){ + touchPoints.add(new TouchPoint(long(touchPoint[0]), new Point(float(touchPoint[1]), float(touchPoint[2])), TouchToolType(int(touchPoint[3])), bool(touchPoint[4]))) + } + onTouchDown.(touchPoints) + }) + implement("graphics.window.set_on_touch_move", handle, callback(list> touches){ + var touchPoints = list[] + foreach(touches as touchPoint){ + touchPoints.add(new TouchPoint(long(touchPoint[0]), new Point(float(touchPoint[1]), float(touchPoint[2])), TouchToolType(int(touchPoint[3])), bool(touchPoint[4]))) + } + onTouchMove.(touchPoints) + }) + implement("graphics.window.set_on_touch_up", handle, callback(list> touches){ + var touchPoints = list[] + foreach(touches as touchPoint){ + touchPoints.add(new TouchPoint(long(touchPoint[0]), new Point(float(touchPoint[1]), float(touchPoint[2])), TouchToolType(int(touchPoint[3])), bool(touchPoint[4]))) + } + onTouchUp.(touchPoints) + }) + } + + [public] + method show(){ + implement("graphics.window.show", handle) + } + + [public] + method isFullscreen() returns bool{ + return bool(implement("graphics.window.is_fullscreen")) + } + + [public] + method toggleFullscreen(){ + implement("graphics.window.toggle_fullscreen", handle) + } + + [public] + method isKeyDown(KeyCode key) returns bool{ + return currentlyDownKeys.contains(key) + } + +} \ No newline at end of file diff --git a/stdlib/graphics/implementations/implementations.aspl b/stdlib/graphics/implementations/implementations.aspl new file mode 100644 index 0000000..a033c27 --- /dev/null +++ b/stdlib/graphics/implementations/implementations.aspl @@ -0,0 +1,19 @@ +$if !twailbackend{ + $include("implementations.c") + $if windows{ + $link("gdi32") + } + $if !ssl{ + $if linux{ + $link("X11") + $link("Xcursor") + $link("Xi") + $link("GL") + $link("dl") + } + $if macos{ + $link("framework:Cocoa") + $link("framework:OpenGL") + } + } +} \ No newline at end of file diff --git a/stdlib/graphics/implementations/implementations.c b/stdlib/graphics/implementations/implementations.c new file mode 100644 index 0000000..8c99540 --- /dev/null +++ b/stdlib/graphics/implementations/implementations.c @@ -0,0 +1,922 @@ +#define ICYLIB_IMPLEMENTATION +#define ICYLIB_MALLOC ASPL_MALLOC +#define ICYLIB_MALLOC_ATOMIC ASPL_MALLOC // TODO: Actually use ASPL_MALLOC_ATOMIC when it doesn't cause random crashes +#define ICYLIB_REALLOC ASPL_REALLOC +#define ICYLIB_FREE ASPL_FREE +#define ICYLIB_ERROR ASPL_PANIC +#define ICYLIB_NO_SIMD +#define STBI_NO_SIMD +#define STBIR_NO_SIMD + +#include "thirdparty/icylib/regular_image.h" +#include "thirdparty/icylib/primitive_chunked_image.h" +#include "thirdparty/icylib/lazy_chunked_image.h" + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$new_from_size(ASPL_OBJECT_TYPE* width, ASPL_OBJECT_TYPE* height) { + return ASPL_HANDLE_LITERAL(icylib_regular_create_from_size(ASPL_ACCESS(*width).value.integer32, ASPL_ACCESS(*height).value.integer32, 4)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$get_pixel(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y) { + icylib_Color color = icylib_regular_get_pixel(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32); + ASPL_OBJECT_TYPE* array = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 4); + array[0] = ASPL_BYTE_LITERAL(color.a); + array[1] = ASPL_BYTE_LITERAL(color.r); + array[2] = ASPL_BYTE_LITERAL(color.g); + array[3] = ASPL_BYTE_LITERAL(color.b); + return ASPL_LIST_LITERAL("list", 13, array, 4); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$set_pixel(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + if (ASPL_ACCESS(*blend).value.boolean) { + icylib_regular_set_pixel_blend(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8)); + } + else { + icylib_regular_set_pixel(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8)); + } + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$fill(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + icylib_regular_fill(ASPL_ACCESS(*handle).value.handle, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$draw_image(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* image, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* blend) { + icylib_regular_draw_image(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, ASPL_ACCESS(*image).value.handle, ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$draw_line(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x1, ASPL_OBJECT_TYPE* y1, ASPL_OBJECT_TYPE* x2, ASPL_OBJECT_TYPE* y2, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* thickness, ASPL_OBJECT_TYPE* blend, ASPL_OBJECT_TYPE* antialias) { + icylib_regular_draw_line_with_thickness(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x1).value.integer32, ASPL_ACCESS(*y1).value.integer32, ASPL_ACCESS(*x2).value.integer32, ASPL_ACCESS(*y2).value.integer32, ASPL_ACCESS(*thickness).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*blend).value.boolean, ASPL_ACCESS(*antialias).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$draw_rectangle(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* width, ASPL_OBJECT_TYPE* height, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + icylib_regular_draw_rectangle(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, ASPL_ACCESS(*width).value.integer32, ASPL_ACCESS(*height).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$fill_rectangle(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x1, ASPL_OBJECT_TYPE* y1, ASPL_OBJECT_TYPE* x2, ASPL_OBJECT_TYPE* y2, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + icylib_regular_fill_rectangle(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x1).value.integer32, ASPL_ACCESS(*y1).value.integer32, ASPL_ACCESS(*x2).value.integer32, ASPL_ACCESS(*y2).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$draw_triangle(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x1, ASPL_OBJECT_TYPE* y1, ASPL_OBJECT_TYPE* x2, ASPL_OBJECT_TYPE* y2, ASPL_OBJECT_TYPE* x3, ASPL_OBJECT_TYPE* y3, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + ASPL_PANIC("Unimplemented"); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$fill_triangle(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x1, ASPL_OBJECT_TYPE* y1, ASPL_OBJECT_TYPE* x2, ASPL_OBJECT_TYPE* y2, ASPL_OBJECT_TYPE* x3, ASPL_OBJECT_TYPE* y3, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + ASPL_PANIC("Unimplemented"); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$draw_circle(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* radius, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + icylib_regular_draw_circle(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, ASPL_ACCESS(*radius).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$fill_circle(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* radius, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + icylib_regular_fill_circle(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, ASPL_ACCESS(*radius).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$draw_text(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* text, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* font_path, ASPL_OBJECT_TYPE* font_size, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* horizontal_alignment, ASPL_OBJECT_TYPE* vertical_alignment, ASPL_OBJECT_TYPE* blend) { + icylib_regular_draw_text(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*text).value.string->str, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*font_path).value.string->str, ASPL_ACCESS(*font_size).value.integer32, ASPL_ACCESS(*horizontal_alignment).value.integer32, ASPL_ACCESS(*vertical_alignment).value.integer32, ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$replace_color(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* old_a, ASPL_OBJECT_TYPE* old_r, ASPL_OBJECT_TYPE* old_g, ASPL_OBJECT_TYPE* old_b, ASPL_OBJECT_TYPE* new_a, ASPL_OBJECT_TYPE* new_r, ASPL_OBJECT_TYPE* new_g, ASPL_OBJECT_TYPE* new_b, ASPL_OBJECT_TYPE* blend) { + icylib_regular_replace_color(ASPL_ACCESS(*handle).value.handle, icylib_color_from_rgba(ASPL_ACCESS(*old_r).value.integer8, ASPL_ACCESS(*old_g).value.integer8, ASPL_ACCESS(*old_b).value.integer8, ASPL_ACCESS(*old_a).value.integer8), icylib_color_from_rgba(ASPL_ACCESS(*new_r).value.integer8, ASPL_ACCESS(*new_g).value.integer8, ASPL_ACCESS(*new_b).value.integer8, ASPL_ACCESS(*new_a).value.integer8), ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$replace_color_ignore_alpha(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* old_r, ASPL_OBJECT_TYPE* old_g, ASPL_OBJECT_TYPE* old_b, ASPL_OBJECT_TYPE* new_r, ASPL_OBJECT_TYPE* new_g, ASPL_OBJECT_TYPE* new_b) { + icylib_regular_replace_color_ignore_alpha(ASPL_ACCESS(*handle).value.handle, icylib_color_from_rgb(ASPL_ACCESS(*old_r).value.integer8, ASPL_ACCESS(*old_g).value.integer8, ASPL_ACCESS(*old_b).value.integer8), icylib_color_from_rgb(ASPL_ACCESS(*new_r).value.integer8, ASPL_ACCESS(*new_g).value.integer8, ASPL_ACCESS(*new_b).value.integer8)); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$blur(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* radius) { + icylib_regular_blur(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*radius).value.integer32); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$resize_with_size(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* width, ASPL_OBJECT_TYPE* height) { + icylib_regular_resize(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*width).value.integer32, ASPL_ACCESS(*height).value.integer32); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$resize_with_scale(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* scale) { + icylib_regular_resize_scale(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*scale).value.float32); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$extend_to(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* width, ASPL_OBJECT_TYPE* height) { + icylib_regular_extend_to(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*width).value.integer32, ASPL_ACCESS(*height).value.integer32); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$get_sub_image(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* width, ASPL_OBJECT_TYPE* height) { + return ASPL_HANDLE_LITERAL(icylib_regular_get_sub_image(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, ASPL_ACCESS(*width).value.integer32, ASPL_ACCESS(*height).value.integer32)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$copy(ASPL_OBJECT_TYPE* handle) { + return ASPL_HANDLE_LITERAL(icylib_regular_copy(ASPL_ACCESS(*handle).value.handle)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$save_to_file(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* path) { + icylib_regular_save_to_file(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*path).value.string->str); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$load_from_file(ASPL_OBJECT_TYPE* path) { + return ASPL_HANDLE_LITERAL(icylib_regular_load_from_file(ASPL_ACCESS(*path).value.string->str)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$load_from_file_data(ASPL_OBJECT_TYPE* bytes) { + unsigned char* array = ASPL_MALLOC(ASPL_ACCESS(*bytes).value.list->length); + for (int i = 0; i < ASPL_ACCESS(*bytes).value.list->length; i++) { + array[i] = ASPL_ACCESS(ASPL_ACCESS(*bytes).value.list->value[i]).value.integer8; + } + return ASPL_HANDLE_LITERAL(icylib_regular_load_from_file_data(array, ASPL_ACCESS(*bytes).value.list->length)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$get_width(ASPL_OBJECT_TYPE* handle) { + return ASPL_INT_LITERAL(((icylib_RegularImage*)ASPL_ACCESS(*handle).value.handle)->width); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$get_height(ASPL_OBJECT_TYPE* handle) { + return ASPL_INT_LITERAL(((icylib_RegularImage*)ASPL_ACCESS(*handle).value.handle)->height); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$get_width_from_file(ASPL_OBJECT_TYPE* path) { + return ASPL_INT_LITERAL(icylib_regular_get_width_from_file(ASPL_ACCESS(*path).value.string->str)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$get_height_from_file(ASPL_OBJECT_TYPE* path) { + return ASPL_INT_LITERAL(icylib_regular_get_height_from_file(ASPL_ACCESS(*path).value.string->str)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$new_from_size(ASPL_OBJECT_TYPE* width, ASPL_OBJECT_TYPE* height, ASPL_OBJECT_TYPE* channels) { + return ASPL_HANDLE_LITERAL(icylib_primitive_chunked_create_from_size(ASPL_ACCESS(*width).value.integer32, ASPL_ACCESS(*height).value.integer32, ASPL_ACCESS(*channels).value.integer32)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$get_pixel(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y) { + icylib_Color color = icylib_primitive_chunked_get_pixel(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32); + ASPL_OBJECT_TYPE* array = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 4); + array[0] = ASPL_BYTE_LITERAL(color.a); + array[1] = ASPL_BYTE_LITERAL(color.r); + array[2] = ASPL_BYTE_LITERAL(color.g); + array[3] = ASPL_BYTE_LITERAL(color.b); + return ASPL_LIST_LITERAL("list", 13, array, 4); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$set_pixel(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + if (ASPL_ACCESS(*blend).value.boolean) { + icylib_primitive_chunked_set_pixel_blend(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8)); + } + else { + icylib_primitive_chunked_set_pixel(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8)); + } + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$fill(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + icylib_primitive_chunked_fill(ASPL_ACCESS(*handle).value.handle, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$draw_image(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* image, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* blend) { + icylib_primitive_chunked_draw_image(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, ASPL_ACCESS(*image).value.handle, ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$draw_line(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x1, ASPL_OBJECT_TYPE* y1, ASPL_OBJECT_TYPE* x2, ASPL_OBJECT_TYPE* y2, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* thickness, ASPL_OBJECT_TYPE* blend, ASPL_OBJECT_TYPE* antialias) { + icylib_primitive_chunked_draw_line_with_thickness(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x1).value.integer32, ASPL_ACCESS(*y1).value.integer32, ASPL_ACCESS(*x2).value.integer32, ASPL_ACCESS(*y2).value.integer32, ASPL_ACCESS(*thickness).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*blend).value.boolean, ASPL_ACCESS(*antialias).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$draw_rectangle(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* width, ASPL_OBJECT_TYPE* height, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + icylib_primitive_chunked_draw_rectangle(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, ASPL_ACCESS(*width).value.integer32, ASPL_ACCESS(*height).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$fill_rectangle(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x1, ASPL_OBJECT_TYPE* y1, ASPL_OBJECT_TYPE* x2, ASPL_OBJECT_TYPE* y2, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + icylib_primitive_chunked_fill_rectangle(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x1).value.integer32, ASPL_ACCESS(*y1).value.integer32, ASPL_ACCESS(*x2).value.integer32, ASPL_ACCESS(*y2).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$draw_triangle(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x1, ASPL_OBJECT_TYPE* y1, ASPL_OBJECT_TYPE* x2, ASPL_OBJECT_TYPE* y2, ASPL_OBJECT_TYPE* x3, ASPL_OBJECT_TYPE* y3, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + ASPL_PANIC("Unimplemented"); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$fill_triangle(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x1, ASPL_OBJECT_TYPE* y1, ASPL_OBJECT_TYPE* x2, ASPL_OBJECT_TYPE* y2, ASPL_OBJECT_TYPE* x3, ASPL_OBJECT_TYPE* y3, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + ASPL_PANIC("Unimplemented"); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$draw_circle(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* radius, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + icylib_primitive_chunked_draw_circle(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, ASPL_ACCESS(*radius).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$fill_circle(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* radius, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + icylib_primitive_chunked_fill_circle(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, ASPL_ACCESS(*radius).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$draw_text(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* text, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* font_path, ASPL_OBJECT_TYPE* font_size, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* horizontal_alignment, ASPL_OBJECT_TYPE* vertical_alignment, ASPL_OBJECT_TYPE* blend) { + icylib_primitive_chunked_draw_text(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*text).value.string->str, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*font_path).value.string->str, ASPL_ACCESS(*font_size).value.integer32, ASPL_ACCESS(*horizontal_alignment).value.integer32, ASPL_ACCESS(*vertical_alignment).value.integer32, ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$replace_color(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* old_a, ASPL_OBJECT_TYPE* old_r, ASPL_OBJECT_TYPE* old_g, ASPL_OBJECT_TYPE* old_b, ASPL_OBJECT_TYPE* new_a, ASPL_OBJECT_TYPE* new_r, ASPL_OBJECT_TYPE* new_g, ASPL_OBJECT_TYPE* new_b, ASPL_OBJECT_TYPE* blend) { + icylib_primitive_chunked_replace_color(ASPL_ACCESS(*handle).value.handle, icylib_color_from_rgba(ASPL_ACCESS(*old_r).value.integer8, ASPL_ACCESS(*old_g).value.integer8, ASPL_ACCESS(*old_b).value.integer8, ASPL_ACCESS(*old_a).value.integer8), icylib_color_from_rgba(ASPL_ACCESS(*new_r).value.integer8, ASPL_ACCESS(*new_g).value.integer8, ASPL_ACCESS(*new_b).value.integer8, ASPL_ACCESS(*new_a).value.integer8), ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$replace_color_ignore_alpha(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* old_r, ASPL_OBJECT_TYPE* old_g, ASPL_OBJECT_TYPE* old_b, ASPL_OBJECT_TYPE* new_r, ASPL_OBJECT_TYPE* new_g, ASPL_OBJECT_TYPE* new_b) { + icylib_primitive_chunked_replace_color_ignore_alpha(ASPL_ACCESS(*handle).value.handle, icylib_color_from_rgb(ASPL_ACCESS(*old_r).value.integer8, ASPL_ACCESS(*old_g).value.integer8, ASPL_ACCESS(*old_b).value.integer8), icylib_color_from_rgb(ASPL_ACCESS(*new_r).value.integer8, ASPL_ACCESS(*new_g).value.integer8, ASPL_ACCESS(*new_b).value.integer8)); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$blur(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* radius) { + icylib_primitive_chunked_blur(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*radius).value.integer32); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$resize_with_size(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* width, ASPL_OBJECT_TYPE* height) { + icylib_primitive_chunked_resize(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*width).value.integer32, ASPL_ACCESS(*height).value.integer32); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$resize_with_scale(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* scale) { + icylib_primitive_chunked_resize_scale(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*scale).value.float32); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$extend_to(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* width, ASPL_OBJECT_TYPE* height) { + icylib_primitive_chunked_extend_to(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*width).value.integer32, ASPL_ACCESS(*height).value.integer32); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$get_sub_image(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* width, ASPL_OBJECT_TYPE* height) { + return ASPL_HANDLE_LITERAL(icylib_primitive_chunked_get_sub_image(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, ASPL_ACCESS(*width).value.integer32, ASPL_ACCESS(*height).value.integer32)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$copy(ASPL_OBJECT_TYPE* handle) { + return ASPL_HANDLE_LITERAL(icylib_primitive_chunked_copy(ASPL_ACCESS(*handle).value.handle)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$save_to_file(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* path) { + icylib_primitive_chunked_save_to_file(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*path).value.string->str); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$convert_to_lazy(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* file, ASPL_OBJECT_TYPE* directory) { + ASPL_PANIC("Unimplemented"); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$load_from_file(ASPL_OBJECT_TYPE* file) { + return ASPL_HANDLE_LITERAL(icylib_primitive_chunked_load_from_file(ASPL_ACCESS(*file).value.string->str)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$load_from_file_data(ASPL_OBJECT_TYPE* bytes) { + unsigned char* array = ASPL_MALLOC(ASPL_ACCESS(*bytes).value.list->length); + for (int i = 0; i < ASPL_ACCESS(*bytes).value.list->length; i++) { + array[i] = ASPL_ACCESS(ASPL_ACCESS(*bytes).value.list->value[i]).value.integer8; + } + return ASPL_HANDLE_LITERAL(icylib_primitive_chunked_load_from_file_data(array, ASPL_ACCESS(*bytes).value.list->length)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$get_width(ASPL_OBJECT_TYPE* handle) { + return ASPL_INT_LITERAL(((icylib_LazyChunkedImage*)ASPL_ACCESS(*handle).value.handle)->width); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$primitive_chunked$get_height(ASPL_OBJECT_TYPE* handle) { + return ASPL_INT_LITERAL(((icylib_LazyChunkedImage*)ASPL_ACCESS(*handle).value.handle)->height); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$new_from_size(ASPL_OBJECT_TYPE* width, ASPL_OBJECT_TYPE* height, ASPL_OBJECT_TYPE* channels, ASPL_OBJECT_TYPE* directory) { + return ASPL_HANDLE_LITERAL(icylib_lazy_chunked_create_from_size(ASPL_ACCESS(*width).value.integer32, ASPL_ACCESS(*height).value.integer32, ASPL_ACCESS(*channels).value.integer32, ASPL_ACCESS(*directory).value.string->str)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$get_pixel(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y) { + icylib_Color color = icylib_lazy_chunked_get_pixel(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32); + ASPL_OBJECT_TYPE* array = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 4); + array[0] = ASPL_BYTE_LITERAL(color.a); + array[1] = ASPL_BYTE_LITERAL(color.r); + array[2] = ASPL_BYTE_LITERAL(color.g); + array[3] = ASPL_BYTE_LITERAL(color.b); + return ASPL_LIST_LITERAL("list", 13, array, 4); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$set_pixel(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + if (ASPL_ACCESS(*blend).value.boolean) { + icylib_lazy_chunked_set_pixel_blend(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8)); + } + else { + icylib_lazy_chunked_set_pixel(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8)); + } + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$fill(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + icylib_lazy_chunked_fill(ASPL_ACCESS(*handle).value.handle, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$draw_image(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* image, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* blend) { + icylib_lazy_chunked_draw_image(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, ASPL_ACCESS(*image).value.handle, ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$draw_line(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x1, ASPL_OBJECT_TYPE* y1, ASPL_OBJECT_TYPE* x2, ASPL_OBJECT_TYPE* y2, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* thickness, ASPL_OBJECT_TYPE* blend, ASPL_OBJECT_TYPE* antialias) { + icylib_lazy_chunked_draw_line_with_thickness(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x1).value.integer32, ASPL_ACCESS(*y1).value.integer32, ASPL_ACCESS(*x2).value.integer32, ASPL_ACCESS(*y2).value.integer32, ASPL_ACCESS(*thickness).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*blend).value.boolean, ASPL_ACCESS(*antialias).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$draw_rectangle(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* width, ASPL_OBJECT_TYPE* height, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + icylib_lazy_chunked_draw_rectangle(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, ASPL_ACCESS(*width).value.integer32, ASPL_ACCESS(*height).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$fill_rectangle(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x1, ASPL_OBJECT_TYPE* y1, ASPL_OBJECT_TYPE* x2, ASPL_OBJECT_TYPE* y2, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + icylib_lazy_chunked_fill_rectangle(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x1).value.integer32, ASPL_ACCESS(*y1).value.integer32, ASPL_ACCESS(*x2).value.integer32, ASPL_ACCESS(*y2).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$draw_triangle(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x1, ASPL_OBJECT_TYPE* y1, ASPL_OBJECT_TYPE* x2, ASPL_OBJECT_TYPE* y2, ASPL_OBJECT_TYPE* x3, ASPL_OBJECT_TYPE* y3, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + ASPL_PANIC("Unimplemented"); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$fill_triangle(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x1, ASPL_OBJECT_TYPE* y1, ASPL_OBJECT_TYPE* x2, ASPL_OBJECT_TYPE* y2, ASPL_OBJECT_TYPE* x3, ASPL_OBJECT_TYPE* y3, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + ASPL_PANIC("Unimplemented"); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$draw_circle(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* radius, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + icylib_lazy_chunked_draw_circle(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, ASPL_ACCESS(*radius).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$fill_circle(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* radius, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* blend) { + icylib_lazy_chunked_fill_circle(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, ASPL_ACCESS(*radius).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$draw_text(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* text, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* font_path, ASPL_OBJECT_TYPE* font_size, ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* r, ASPL_OBJECT_TYPE* g, ASPL_OBJECT_TYPE* b, ASPL_OBJECT_TYPE* horizontal_alignment, ASPL_OBJECT_TYPE* vertical_alignment, ASPL_OBJECT_TYPE* blend) { + icylib_lazy_chunked_draw_text(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*text).value.string->str, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, icylib_color_from_rgba(ASPL_ACCESS(*r).value.integer8, ASPL_ACCESS(*g).value.integer8, ASPL_ACCESS(*b).value.integer8, ASPL_ACCESS(*a).value.integer8), ASPL_ACCESS(*font_path).value.string->str, ASPL_ACCESS(*font_size).value.integer32, ASPL_ACCESS(*horizontal_alignment).value.integer32, ASPL_ACCESS(*vertical_alignment).value.integer32, ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$replace_color(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* old_a, ASPL_OBJECT_TYPE* old_r, ASPL_OBJECT_TYPE* old_g, ASPL_OBJECT_TYPE* old_b, ASPL_OBJECT_TYPE* new_a, ASPL_OBJECT_TYPE* new_r, ASPL_OBJECT_TYPE* new_g, ASPL_OBJECT_TYPE* new_b, ASPL_OBJECT_TYPE* blend) { + icylib_lazy_chunked_replace_color(ASPL_ACCESS(*handle).value.handle, icylib_color_from_rgba(ASPL_ACCESS(*old_r).value.integer8, ASPL_ACCESS(*old_g).value.integer8, ASPL_ACCESS(*old_b).value.integer8, ASPL_ACCESS(*old_a).value.integer8), icylib_color_from_rgba(ASPL_ACCESS(*new_r).value.integer8, ASPL_ACCESS(*new_g).value.integer8, ASPL_ACCESS(*new_b).value.integer8, ASPL_ACCESS(*new_a).value.integer8), ASPL_ACCESS(*blend).value.boolean); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$replace_color_ignore_alpha(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* old_r, ASPL_OBJECT_TYPE* old_g, ASPL_OBJECT_TYPE* old_b, ASPL_OBJECT_TYPE* new_r, ASPL_OBJECT_TYPE* new_g, ASPL_OBJECT_TYPE* new_b) { + icylib_lazy_chunked_replace_color_ignore_alpha(ASPL_ACCESS(*handle).value.handle, icylib_color_from_rgb(ASPL_ACCESS(*old_r).value.integer8, ASPL_ACCESS(*old_g).value.integer8, ASPL_ACCESS(*old_b).value.integer8), icylib_color_from_rgb(ASPL_ACCESS(*new_r).value.integer8, ASPL_ACCESS(*new_g).value.integer8, ASPL_ACCESS(*new_b).value.integer8)); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$blur(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* radius) { + icylib_lazy_chunked_blur(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*radius).value.integer32); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$resize_with_size(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* width, ASPL_OBJECT_TYPE* height) { + icylib_lazy_chunked_resize(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*width).value.integer32, ASPL_ACCESS(*height).value.integer32); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$resize_with_scale(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* scale) { + icylib_lazy_chunked_resize_scale(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*scale).value.float32); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$extend_to(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* width, ASPL_OBJECT_TYPE* height) { + icylib_lazy_chunked_extend_to(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*width).value.integer32, ASPL_ACCESS(*height).value.integer32); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$get_sub_image(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* width, ASPL_OBJECT_TYPE* height) { + return ASPL_HANDLE_LITERAL(icylib_lazy_chunked_get_sub_image(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, ASPL_ACCESS(*width).value.integer32, ASPL_ACCESS(*height).value.integer32)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$copy(ASPL_OBJECT_TYPE* handle) { + return ASPL_HANDLE_LITERAL(icylib_lazy_chunked_copy(ASPL_ACCESS(*handle).value.handle)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$save_to_file(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* path) { + int length; + icylib_lazy_chunked_save_to_file(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*path).value.string->str, &length); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$load_from_file(ASPL_OBJECT_TYPE* file, ASPL_OBJECT_TYPE* directory) { + return ASPL_HANDLE_LITERAL(icylib_lazy_chunked_load_from_file(ASPL_ACCESS(*file).value.string->str, ASPL_ACCESS(*directory).value.string->str)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$get_width(ASPL_OBJECT_TYPE* handle) { + return ASPL_INT_LITERAL(((icylib_LazyChunkedImage*)ASPL_ACCESS(*handle).value.handle)->width); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$get_height(ASPL_OBJECT_TYPE* handle) { + return ASPL_INT_LITERAL(((icylib_LazyChunkedImage*)ASPL_ACCESS(*handle).value.handle)->height); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$require_area(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* width, ASPL_OBJECT_TYPE* height) { + icylib_lazy_chunked_require_area(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32, ASPL_ACCESS(*width).value.integer32, ASPL_ACCESS(*height).value.integer32); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$is_chunk_loaded(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y) { + return ASPL_BOOL_LITERAL(icylib_lazy_chunked_is_chunk_loaded(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$load_chunk(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y) { + icylib_lazy_chunked_load_chunk(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$lazy_chunked$unload_chunk(ASPL_OBJECT_TYPE* handle, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y) { + icylib_lazy_chunked_unload_chunk(ASPL_ACCESS(*handle).value.handle, ASPL_ACCESS(*x).value.integer32, ASPL_ACCESS(*y).value.integer32); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$font$get_default_font_path() { + return ASPL_STRING_LITERAL_NO_COPY(icylib_get_default_font_path()); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$font$get_font_variant_path(ASPL_OBJECT_TYPE* font_path, ASPL_OBJECT_TYPE* bold, ASPL_OBJECT_TYPE* italic, ASPL_OBJECT_TYPE* underline, ASPL_OBJECT_TYPE* strikeout) { + char* variant = "regular"; + if (ASPL_ACCESS(*bold).value.boolean) { + variant = "bold"; + } + else if (ASPL_ACCESS(*italic).value.boolean) { + variant = "italic"; + } + else if (ASPL_ACCESS(*underline).value.boolean) { + variant = "underline"; + } + else if (ASPL_ACCESS(*strikeout).value.boolean) { + variant = "strikeout"; + } + // TODO: Allow combining variants + return ASPL_STRING_LITERAL_NO_COPY(icylib_get_font_variant_path(ASPL_ACCESS(*font_path).value.string->str, variant)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$canvas$measure_text_size(ASPL_OBJECT_TYPE* text, ASPL_OBJECT_TYPE* font_path, ASPL_OBJECT_TYPE* font_size) { + double width, height; + icylib_measure_text_size(ASPL_ACCESS(*text).value.string->str, ASPL_ACCESS(*font_path).value.string->str, ASPL_ACCESS(*font_size).value.integer32, &width, &height); + ASPL_OBJECT_TYPE* array = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 2); + array[0] = ASPL_FLOAT_LITERAL((float)width); + array[1] = ASPL_FLOAT_LITERAL((float)height); + return ASPL_LIST_LITERAL("list", 11, array, 2); +} + +#ifndef ASPL_USE_SSL +#define SOKOL_IMPL +#ifndef __ANDROID__ +#define SOKOL_GLCORE +#define SOKOL_NO_ENTRY +#endif +#endif +#include "thirdparty/sokol/sokol_app.h" +#include "thirdparty/sokol/sokol_log.h" +#include "thirdparty/sokol/sokol_gfx.h" +#include "thirdparty/sokol/sokol_glue.h" +#include "thirdparty/sokol/util/sokol_gl.h" + +void* sokol_malloc(size_t size, void* user_data) { + return ASPL_MALLOC(size); +} + +void sokol_free(void* ptr, void* user_data) { + ASPL_FREE(ptr); +} + +typedef struct StreamingImage { + sg_image image; + sg_sampler sampler; +} StreamingImage; + +typedef struct ASPL_handle_graphics$Window { + sapp_desc* desc; + StreamingImage* canvas_image; + char keys_down[512]; + char mouse_buttons_down[32]; + + ASPL_OBJECT_TYPE load_callback; + ASPL_OBJECT_TYPE paint_callback; + ASPL_OBJECT_TYPE resize_callback; + ASPL_OBJECT_TYPE key_press_callback; + ASPL_OBJECT_TYPE key_down_callback; + ASPL_OBJECT_TYPE key_up_callback; + ASPL_OBJECT_TYPE mouse_click_callback; + ASPL_OBJECT_TYPE mouse_down_callback; + ASPL_OBJECT_TYPE mouse_up_callback; + ASPL_OBJECT_TYPE mouse_move_callback; + ASPL_OBJECT_TYPE mouse_wheel_callback; + ASPL_OBJECT_TYPE touch_down_callback; + ASPL_OBJECT_TYPE touch_move_callback; + ASPL_OBJECT_TYPE touch_up_callback; +} ASPL_handle_graphics$Window; + +#ifndef ASPL_INTERPRETER_MODE +void aspl_callback_invoke(ASPL_OBJECT_TYPE closure); +void aspl_callback_any__invoke(ASPL_OBJECT_TYPE closure, ASPL_OBJECT_TYPE* canvas); +void aspl_callback_integer_integer__invoke(ASPL_OBJECT_TYPE closure, ASPL_OBJECT_TYPE* width, ASPL_OBJECT_TYPE* height); +void aspl_callback_integer__invoke(ASPL_OBJECT_TYPE closure, ASPL_OBJECT_TYPE* key); +void aspl_callback_float_float_integer__invoke(ASPL_OBJECT_TYPE closure, ASPL_OBJECT_TYPE* x, ASPL_OBJECT_TYPE* y, ASPL_OBJECT_TYPE* button); +void aspl_callback_float_float_float_float__invoke(ASPL_OBJECT_TYPE closure, ASPL_OBJECT_TYPE* fromX, ASPL_OBJECT_TYPE* fromY, ASPL_OBJECT_TYPE* deltaX, ASPL_OBJECT_TYPE* deltaY); +void aspl_callback_list_list_long_OR_float_OR_integer_OR_boolean____invoke(ASPL_OBJECT_TYPE closure, ASPL_OBJECT_TYPE* touches); +#endif + +void aspl_util_graphics$Window_load_callback(void* userdata) { + struct GC_stack_base sb; // TODO: Is this only required for Android? + GC_get_stack_base(&sb); + GC_register_my_thread(&sb); + + sg_setup(&(sg_desc) { + // .context = sapp_sgcontext(), TODO: Is this actually required? + .logger.func = slog_func, + }); + sgl_setup(&(sgl_desc_t) { + .logger.func = slog_func, + }); + + ASPL_handle_graphics$Window* handle = (ASPL_handle_graphics$Window*)userdata; +#ifdef ASPL_INTERPRETER_MODE + ASPL_AILI_ArgumentList arguments = (ASPL_AILI_ArgumentList){ .size = 0, .arguments = NULL }; + aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_ACCESS(handle->load_callback).value.callback, arguments); +#else + aspl_callback_invoke(handle->load_callback); +#endif +} + +StreamingImage new_streaming_image(int width, int height, int channels) { + sg_image_desc desc = { + .width = width, + .height = height, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .num_slices = 1, + .num_mipmaps = 1, + .usage = SG_USAGE_STREAM, + .label = "Default Canvas" + }; + sg_sampler_desc sampler_desc = { + .min_filter = SG_FILTER_LINEAR, + .mag_filter = SG_FILTER_LINEAR, + .wrap_u = SG_WRAP_CLAMP_TO_EDGE, + .wrap_v = SG_WRAP_CLAMP_TO_EDGE, + }; + return (StreamingImage) { .image = sg_make_image(&desc), .sampler = sg_make_sampler(&sampler_desc) }; +} + +void aspl_util_graphics$Window_paint_callback(void* userdata) { + ASPL_handle_graphics$Window* handle = (ASPL_handle_graphics$Window*)userdata; + ASPL_OBJECT_TYPE canvas = ASPL_IMPLEMENT_graphics$canvas$new_from_size(C_REFERENCE(ASPL_INT_LITERAL(sapp_width())), C_REFERENCE(ASPL_INT_LITERAL(sapp_height()))); +#ifdef ASPL_INTERPRETER_MODE + ASPL_AILI_ArgumentList arguments = (ASPL_AILI_ArgumentList){ .size = 1, .arguments = (ASPL_OBJECT_TYPE[]) { canvas } }; + aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_ACCESS(handle->paint_callback).value.callback, arguments); +#else + aspl_callback_any__invoke(handle->paint_callback, C_REFERENCE(canvas)); +#endif + if (handle->canvas_image == 0) { + handle->canvas_image = ASPL_MALLOC(sizeof(StreamingImage)); + *handle->canvas_image = new_streaming_image(((icylib_RegularImage*)ASPL_ACCESS(canvas).value.handle)->width, ((icylib_RegularImage*)ASPL_ACCESS(canvas).value.handle)->height, 4); + } + else if (sg_query_image_desc(handle->canvas_image->image).width != ((icylib_RegularImage*)ASPL_ACCESS(canvas).value.handle)->width || sg_query_image_desc(handle->canvas_image->image).height != ((icylib_RegularImage*)ASPL_ACCESS(canvas).value.handle)->height) { + sg_destroy_image(handle->canvas_image->image); + sg_destroy_sampler(handle->canvas_image->sampler); + *handle->canvas_image = new_streaming_image(((icylib_RegularImage*)ASPL_ACCESS(canvas).value.handle)->width, ((icylib_RegularImage*)ASPL_ACCESS(canvas).value.handle)->height, 4); + } + StreamingImage image = *handle->canvas_image; + sg_image_data data = { + .subimage[0][0] = { + .ptr = ((icylib_RegularImage*)ASPL_ACCESS(canvas).value.handle)->data, + .size = ((icylib_RegularImage*)ASPL_ACCESS(canvas).value.handle)->width * ((icylib_RegularImage*)ASPL_ACCESS(canvas).value.handle)->height * 4 + } + }; + sg_update_image(image.image, &data); + + sgl_defaults(); + sgl_ortho(0.0f, sapp_width(), sapp_height(), 0.0f, -1.0f, 1.0f); + + float scale = sapp_dpi_scale(); + if (scale < 1.0f) scale = 1.0f; + + sgl_enable_texture(); + sgl_texture(image.image, image.sampler); + sgl_begin_quads(); + sgl_c4b(255, 255, 255, 255); + sgl_v3f_t2f(0.0f, 0.0f, 0.0f, 0.0f, 0.0f); + sgl_v3f_t2f(sapp_width() * scale, 0.0f, 0.0f, 1.0f, 0.0f); + sgl_v3f_t2f(sapp_width() * scale, sapp_height() * scale, 0.0f, 1.0f, 1.0f); + sgl_v3f_t2f(0.0f, sapp_height() * scale, 0.0f, 0.0f, 1.0f); + sgl_end(); + sgl_disable_texture(); + + sg_begin_pass(&(sg_pass) { .action = (sg_pass_action){ .colors[0] = {.load_action = SG_LOADACTION_CLEAR, .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f } } }, .swapchain = sglue_swapchain() }); + sgl_draw(); + sg_end_pass(); + sg_commit(); +} + +void aspl_util_graphics$Window_event_callback(const sapp_event* event, void* userdata) { + ASPL_handle_graphics$Window* handle = (ASPL_handle_graphics$Window*)userdata; + switch (event->type) { + case SAPP_EVENTTYPE_RESIZED: + case SAPP_EVENTTYPE_RESTORED: + case SAPP_EVENTTYPE_RESUMED: { +#ifdef ASPL_INTERPRETER_MODE + ASPL_AILI_ArgumentList arguments = (ASPL_AILI_ArgumentList){ .size = 2, .arguments = (ASPL_OBJECT_TYPE[]) { ASPL_INT_LITERAL(event->framebuffer_width), ASPL_INT_LITERAL(event->framebuffer_height) } }; + aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_ACCESS(handle->resize_callback).value.callback, arguments); +#else + aspl_callback_integer_integer__invoke(handle->resize_callback, C_REFERENCE(ASPL_INT_LITERAL(event->framebuffer_width)), C_REFERENCE(ASPL_INT_LITERAL(event->framebuffer_height))); +#endif + break; + } + case SAPP_EVENTTYPE_KEY_DOWN: { +#ifdef ASPL_INTERPRETER_MODE + ASPL_AILI_ArgumentList arguments = (ASPL_AILI_ArgumentList){ .size = 1, .arguments = (ASPL_OBJECT_TYPE[]) { ASPL_INT_LITERAL(event->key_code) } }; + aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_ACCESS(handle->key_down_callback).value.callback, arguments); +#else + aspl_callback_integer__invoke(handle->key_down_callback, C_REFERENCE(ASPL_INT_LITERAL(event->key_code))); +#endif + handle->keys_down[event->key_code] = 1; + break; + } + case SAPP_EVENTTYPE_KEY_UP: { +#ifdef ASPL_INTERPRETER_MODE + ASPL_AILI_ArgumentList arguments = (ASPL_AILI_ArgumentList){ .size = 1, .arguments = (ASPL_OBJECT_TYPE[]) { ASPL_INT_LITERAL(event->key_code) } }; + aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_ACCESS(handle->key_up_callback).value.callback, arguments); +#else + aspl_callback_integer__invoke(handle->key_up_callback, C_REFERENCE(ASPL_INT_LITERAL(event->key_code))); +#endif + if (handle->keys_down[event->key_code] == 1) { +#ifdef ASPL_INTERPRETER_MODE + ASPL_AILI_ArgumentList arguments = (ASPL_AILI_ArgumentList){ .size = 1, .arguments = (ASPL_OBJECT_TYPE[]) { ASPL_INT_LITERAL(event->key_code) } }; + aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_ACCESS(handle->key_press_callback).value.callback, arguments); +#else + aspl_callback_integer__invoke(handle->key_press_callback, C_REFERENCE(ASPL_INT_LITERAL(event->key_code))); +#endif + } + handle->keys_down[event->key_code] = 0; + break; + } + case SAPP_EVENTTYPE_MOUSE_DOWN: { +#ifdef ASPL_INTERPRETER_MODE + ASPL_AILI_ArgumentList arguments = (ASPL_AILI_ArgumentList){ .size = 3, .arguments = (ASPL_OBJECT_TYPE[]) { ASPL_FLOAT_LITERAL(event->mouse_x), ASPL_FLOAT_LITERAL(event->mouse_y), ASPL_INT_LITERAL(event->mouse_button) } }; + aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_ACCESS(handle->mouse_down_callback).value.callback, arguments); +#else + aspl_callback_float_float_integer__invoke(handle->mouse_down_callback, C_REFERENCE(ASPL_FLOAT_LITERAL(event->mouse_x)), C_REFERENCE(ASPL_FLOAT_LITERAL(event->mouse_y)), C_REFERENCE(ASPL_INT_LITERAL(event->mouse_button))); +#endif + handle->mouse_buttons_down[event->mouse_button] = 1; + break; + } + case SAPP_EVENTTYPE_MOUSE_UP: { +#ifdef ASPL_INTERPRETER_MODE + ASPL_AILI_ArgumentList arguments = (ASPL_AILI_ArgumentList){ .size = 3, .arguments = (ASPL_OBJECT_TYPE[]) { ASPL_FLOAT_LITERAL(event->mouse_x), ASPL_FLOAT_LITERAL(event->mouse_y), ASPL_INT_LITERAL(event->mouse_button) } }; + aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_ACCESS(handle->mouse_up_callback).value.callback, arguments); +#else + aspl_callback_float_float_integer__invoke(handle->mouse_up_callback, C_REFERENCE(ASPL_FLOAT_LITERAL(event->mouse_x)), C_REFERENCE(ASPL_FLOAT_LITERAL(event->mouse_y)), C_REFERENCE(ASPL_INT_LITERAL(event->mouse_button))); +#endif + if (handle->mouse_buttons_down[event->mouse_button] == 1) { +#ifdef ASPL_INTERPRETER_MODE + ASPL_AILI_ArgumentList arguments = (ASPL_AILI_ArgumentList){ .size = 3, .arguments = (ASPL_OBJECT_TYPE[]) { ASPL_FLOAT_LITERAL(event->mouse_x), ASPL_FLOAT_LITERAL(event->mouse_y), ASPL_INT_LITERAL(event->mouse_button) } }; + aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_ACCESS(handle->mouse_click_callback).value.callback, arguments); +#else + aspl_callback_float_float_integer__invoke(handle->mouse_click_callback, C_REFERENCE(ASPL_FLOAT_LITERAL(event->mouse_x)), C_REFERENCE(ASPL_FLOAT_LITERAL(event->mouse_y)), C_REFERENCE(ASPL_INT_LITERAL(event->mouse_button))); +#endif + } + handle->mouse_buttons_down[event->mouse_button] = 0; + break; + } + case SAPP_EVENTTYPE_MOUSE_MOVE: { +#ifdef ASPL_INTERPRETER_MODE + ASPL_AILI_ArgumentList arguments = (ASPL_AILI_ArgumentList){ .size = 4, .arguments = (ASPL_OBJECT_TYPE[]) { ASPL_FLOAT_LITERAL(event->mouse_x), ASPL_FLOAT_LITERAL(event->mouse_y), ASPL_FLOAT_LITERAL(event->mouse_dx), ASPL_FLOAT_LITERAL(event->mouse_dy) } }; + aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_ACCESS(handle->mouse_move_callback).value.callback, arguments); +#else + aspl_callback_float_float_float_float__invoke(handle->mouse_move_callback, C_REFERENCE(ASPL_FLOAT_LITERAL(event->mouse_x)), C_REFERENCE(ASPL_FLOAT_LITERAL(event->mouse_y)), C_REFERENCE(ASPL_FLOAT_LITERAL(event->mouse_dx)), C_REFERENCE(ASPL_FLOAT_LITERAL(event->mouse_dy))); +#endif + break; + } + case SAPP_EVENTTYPE_MOUSE_SCROLL: { +#ifdef ASPL_INTERPRETER_MODE + ASPL_AILI_ArgumentList arguments = (ASPL_AILI_ArgumentList){ .size = 4, .arguments = (ASPL_OBJECT_TYPE[]) { ASPL_FLOAT_LITERAL(event->mouse_x), ASPL_FLOAT_LITERAL(event->mouse_y), ASPL_FLOAT_LITERAL(event->scroll_x), ASPL_FLOAT_LITERAL(event->scroll_y) } }; + aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_ACCESS(handle->mouse_wheel_callback).value.callback, arguments); +#else + aspl_callback_float_float_float_float__invoke(handle->mouse_wheel_callback, C_REFERENCE(ASPL_FLOAT_LITERAL(event->mouse_x)), C_REFERENCE(ASPL_FLOAT_LITERAL(event->mouse_y)), C_REFERENCE(ASPL_FLOAT_LITERAL(event->scroll_x)), C_REFERENCE(ASPL_FLOAT_LITERAL(event->scroll_y))); +#endif + break; + } + case SAPP_EVENTTYPE_TOUCHES_BEGAN: { + ASPL_OBJECT_TYPE* array = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * event->num_touches); + for (int i = 0; i < event->num_touches; i++) { + sapp_touchpoint touchpoint = event->touches[i]; + ASPL_OBJECT_TYPE* array2 = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 5); + array2[0] = ASPL_LONG_LITERAL(touchpoint.identifier); + array2[1] = ASPL_FLOAT_LITERAL(touchpoint.pos_x); + array2[2] = ASPL_FLOAT_LITERAL(touchpoint.pos_y); + array2[3] = ASPL_INT_LITERAL(touchpoint.android_tooltype); + array2[4] = ASPL_BOOL_LITERAL(touchpoint.changed); + array[i] = ASPL_LIST_LITERAL("list", 25, array2, 5); + } +#ifdef ASPL_INTERPRETER_MODE + ASPL_AILI_ArgumentList arguments = (ASPL_AILI_ArgumentList){ .size = 1, .arguments = (ASPL_OBJECT_TYPE[]) { ASPL_LIST_LITERAL("list>", 31, array, event->num_touches) } }; + aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_ACCESS(handle->touch_down_callback).value.callback, arguments); +#else + aspl_callback_list_list_long_OR_float_OR_integer_OR_boolean____invoke(handle->touch_down_callback, C_REFERENCE(ASPL_LIST_LITERAL("list>", 31, array, event->num_touches))); +#endif + break; + } + case SAPP_EVENTTYPE_TOUCHES_MOVED: { + ASPL_OBJECT_TYPE* array = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * event->num_touches); + for (int i = 0; i < event->num_touches; i++) { + sapp_touchpoint touchpoint = event->touches[i]; + ASPL_OBJECT_TYPE* array2 = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 5); + array2[0] = ASPL_LONG_LITERAL(touchpoint.identifier); + array2[1] = ASPL_FLOAT_LITERAL(touchpoint.pos_x); + array2[2] = ASPL_FLOAT_LITERAL(touchpoint.pos_y); + array2[3] = ASPL_INT_LITERAL(touchpoint.android_tooltype); + array2[4] = ASPL_BOOL_LITERAL(touchpoint.changed); + array[i] = ASPL_LIST_LITERAL("list", 25, array2, 5); + } +#ifdef ASPL_INTERPRETER_MODE + ASPL_AILI_ArgumentList arguments = (ASPL_AILI_ArgumentList){ .size = 1, .arguments = (ASPL_OBJECT_TYPE[]) { ASPL_LIST_LITERAL("list>", 31, array, event->num_touches) } }; + aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_ACCESS(handle->touch_move_callback).value.callback, arguments); +#else + aspl_callback_list_list_long_OR_float_OR_integer_OR_boolean____invoke(handle->touch_move_callback, C_REFERENCE(ASPL_LIST_LITERAL("list>", 31, array, event->num_touches))); +#endif + break; + } + case SAPP_EVENTTYPE_TOUCHES_ENDED: { + ASPL_OBJECT_TYPE* array = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * event->num_touches); + for (int i = 0; i < event->num_touches; i++) { + sapp_touchpoint touchpoint = event->touches[i]; + ASPL_OBJECT_TYPE* array2 = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 5); + array2[0] = ASPL_LONG_LITERAL(touchpoint.identifier); + array2[1] = ASPL_FLOAT_LITERAL(touchpoint.pos_x); + array2[2] = ASPL_FLOAT_LITERAL(touchpoint.pos_y); + array2[3] = ASPL_INT_LITERAL(touchpoint.android_tooltype); + array2[4] = ASPL_BOOL_LITERAL(touchpoint.changed); + array[i] = ASPL_LIST_LITERAL("list", 25, array2, 5); + } +#ifdef ASPL_INTERPRETER_MODE + ASPL_AILI_ArgumentList arguments = (ASPL_AILI_ArgumentList){ .size = 1, .arguments = (ASPL_OBJECT_TYPE[]) { ASPL_LIST_LITERAL("list>", 31, array, event->num_touches) } }; + aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_ACCESS(handle->touch_up_callback).value.callback, arguments); +#else + aspl_callback_list_list_long_OR_float_OR_integer_OR_boolean____invoke(handle->touch_up_callback, C_REFERENCE(ASPL_LIST_LITERAL("list>", 31, array, event->num_touches))); +#endif + break; + } + default: + { + // TODO: Implement all remaining events + } + } +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$new() { + ASPL_handle_graphics$Window* handle = ASPL_MALLOC(sizeof(ASPL_handle_graphics$Window)); + sapp_desc* desc = ASPL_MALLOC(sizeof(sapp_desc)); + *desc = (sapp_desc){ + .logger.func = slog_func, + .width = 640, + .height = 480, + .high_dpi = 1, + .window_title = "ASPL Graphics Window", + .allocator = { + .alloc_fn = sokol_malloc, + .free_fn = sokol_free + }, + .user_data = handle, + .init_userdata_cb = aspl_util_graphics$Window_load_callback, + .frame_userdata_cb = aspl_util_graphics$Window_paint_callback, + .event_userdata_cb = aspl_util_graphics$Window_event_callback + }; + *handle = (ASPL_handle_graphics$Window){ + .desc = desc + }; + return ASPL_HANDLE_LITERAL(handle); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$set_title(ASPL_OBJECT_TYPE* window, ASPL_OBJECT_TYPE* title) { + sapp_desc* desc = ((ASPL_handle_graphics$Window*)ASPL_ACCESS(*window).value.handle)->desc; + desc->window_title = ASPL_ACCESS(*title).value.string->str; + if (sapp_isvalid()) { + sapp_set_window_title(ASPL_ACCESS(*title).value.string->str); + } + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$set_on_load(ASPL_OBJECT_TYPE* window, ASPL_OBJECT_TYPE* callback) { + ASPL_handle_graphics$Window* handle = ASPL_ACCESS(*window).value.handle; + handle->load_callback = *callback; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$get_fps(ASPL_OBJECT_TYPE* window) { + return ASPL_INT_LITERAL((int)(0.5 + 1.0 / sapp_frame_duration())); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$set_size(ASPL_OBJECT_TYPE* window, ASPL_OBJECT_TYPE* width, ASPL_OBJECT_TYPE* height) { + sapp_desc* desc = ((ASPL_handle_graphics$Window*)ASPL_ACCESS(*window).value.handle)->desc; + desc->width = ASPL_ACCESS(*width).value.integer32; + desc->height = ASPL_ACCESS(*height).value.integer32; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$set_on_resize(ASPL_OBJECT_TYPE* window, ASPL_OBJECT_TYPE* callback) { + ASPL_handle_graphics$Window* handle = ASPL_ACCESS(*window).value.handle; + handle->resize_callback = *callback; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$set_on_paint(ASPL_OBJECT_TYPE* window, ASPL_OBJECT_TYPE* callback) { + ASPL_handle_graphics$Window* handle = ASPL_ACCESS(*window).value.handle; + handle->paint_callback = *callback; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$set_on_key_press(ASPL_OBJECT_TYPE* window, ASPL_OBJECT_TYPE* callback) { + ASPL_handle_graphics$Window* handle = ASPL_ACCESS(*window).value.handle; + handle->key_press_callback = *callback; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$set_on_key_down(ASPL_OBJECT_TYPE* window, ASPL_OBJECT_TYPE* callback) { + ASPL_handle_graphics$Window* handle = ASPL_ACCESS(*window).value.handle; + handle->key_down_callback = *callback; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$set_on_key_up(ASPL_OBJECT_TYPE* window, ASPL_OBJECT_TYPE* callback) { + ASPL_handle_graphics$Window* handle = ASPL_ACCESS(*window).value.handle; + handle->key_up_callback = *callback; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$set_on_mouse_click(ASPL_OBJECT_TYPE* window, ASPL_OBJECT_TYPE* callback) { + ASPL_handle_graphics$Window* handle = ASPL_ACCESS(*window).value.handle; + handle->mouse_click_callback = *callback; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$set_on_mouse_down(ASPL_OBJECT_TYPE* window, ASPL_OBJECT_TYPE* callback) { + ASPL_handle_graphics$Window* handle = ASPL_ACCESS(*window).value.handle; + handle->mouse_down_callback = *callback; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$set_on_mouse_up(ASPL_OBJECT_TYPE* window, ASPL_OBJECT_TYPE* callback) { + ASPL_handle_graphics$Window* handle = ASPL_ACCESS(*window).value.handle; + handle->mouse_up_callback = *callback; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$set_on_mouse_move(ASPL_OBJECT_TYPE* window, ASPL_OBJECT_TYPE* callback) { + ASPL_handle_graphics$Window* handle = ASPL_ACCESS(*window).value.handle; + handle->mouse_move_callback = *callback; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$set_on_mouse_wheel(ASPL_OBJECT_TYPE* window, ASPL_OBJECT_TYPE* callback) { + ASPL_handle_graphics$Window* handle = ASPL_ACCESS(*window).value.handle; + handle->mouse_wheel_callback = *callback; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$set_on_touch_down(ASPL_OBJECT_TYPE* window, ASPL_OBJECT_TYPE* callback) { + ASPL_handle_graphics$Window* handle = ASPL_ACCESS(*window).value.handle; + handle->touch_down_callback = *callback; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$set_on_touch_move(ASPL_OBJECT_TYPE* window, ASPL_OBJECT_TYPE* callback) { + ASPL_handle_graphics$Window* handle = ASPL_ACCESS(*window).value.handle; + handle->touch_move_callback = *callback; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$set_on_touch_up(ASPL_OBJECT_TYPE* window, ASPL_OBJECT_TYPE* callback) { + ASPL_handle_graphics$Window* handle = ASPL_ACCESS(*window).value.handle; + handle->touch_up_callback = *callback; + return ASPL_UNINITIALIZED; +} + +sapp_desc aspl_global_sapp_desc; + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$show(ASPL_OBJECT_TYPE* window) { + aspl_global_sapp_desc = *((ASPL_handle_graphics$Window*)ASPL_ACCESS(*window).value.handle)->desc; + sapp_run(((ASPL_handle_graphics$Window*)ASPL_ACCESS(*window).value.handle)->desc); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$is_fullscreen() { + return ASPL_BOOL_LITERAL(sapp_is_fullscreen()); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_graphics$window$toggle_fullscreen(ASPL_OBJECT_TYPE* window) { + sapp_toggle_fullscreen(); + return ASPL_UNINITIALIZED; +} \ No newline at end of file diff --git a/stdlib/graphics/text.aspl b/stdlib/graphics/text.aspl new file mode 100644 index 0000000..e8ff050 --- /dev/null +++ b/stdlib/graphics/text.aspl @@ -0,0 +1,7 @@ +import math.geometry + +[public] +function measure_text_size(string text, Font font) returns Size{ + var size = list(implement("graphics.canvas.measure_text_size", text, font.path, font.size)) + return new Size(float(size[0]), float(size[1])) +} \ No newline at end of file diff --git a/stdlib/gui/Control.aspl b/stdlib/gui/Control.aspl new file mode 100644 index 0000000..4be807e --- /dev/null +++ b/stdlib/gui/Control.aspl @@ -0,0 +1,87 @@ +import math.geometry +import graphics + +[public] +[abstract] +class Control { + + [public] + property Point position + property bool _hasFocus = false + [public] + property bool hasFocus{ + get{ + return _hasFocus + } + set{ + if(value != _hasFocus){ + if(value){ + onGainFocus.() + }else{ + onLoseFocus.() + } + _hasFocus = value + } + } + } + [public] + property callback onGainFocus = callback(){} + [public] + property callback onLoseFocus = callback(){} + + [public] + method onLoad(){} + [public] + method onResize(int width, int height){} + [public] + method onKeyPress(KeyCode key){} + [public] + method onKeyDown(KeyCode key, bool isShiftDown){} + [public] + method onKeyUp(KeyCode key){} + [public] + method onMouseClick(Point position, MouseButton button){} + [public] + method onMouseClickAny(Point position, MouseButton button) returns bool{ + return false + } + [public] + method onMouseDown(Point position, MouseButton button){} + [public] + method onMouseDownAny(Point position, MouseButton button) returns bool{ + return false + } + [public] + method onMouseUp(Point position, MouseButton button){} + [public] + method onMouseUpAny(Point position, MouseButton button) returns bool{ + return false + } + [public] + method onMouseMove(Point from, float deltaX, float deltaY){} + [public] + method onMouseMoveAny(Point from, float deltaX, float deltaY) returns bool{ + return false + } + [public] + method onMouseWheel(Point position, float deltaX, float deltaY){} + [public] + method onTouchDown(list points){} + [public] + method onTouchDownAny(list points) returns bool{ + return false + } + [public] + method onTouchUp(list points){} + [public] + method onTouchUpAny(list points) returns bool{ + return false + } + [public] + method onTouchMove(list points){} + [public] + method onTouchMoveAny(list points) returns bool{ + return false + } + +} \ No newline at end of file diff --git a/stdlib/gui/README.md b/stdlib/gui/README.md new file mode 100644 index 0000000..ca63562 --- /dev/null +++ b/stdlib/gui/README.md @@ -0,0 +1,6 @@ +# gui module +This module implements a simple GUI toolkit based on the `graphics` module. + +It is currently very minimal, experimental and subject to change. + +If you are looking for an image processing or window management library, check out the `graphics` module instead. \ No newline at end of file diff --git a/stdlib/gui/SingleLineTextBox.aspl b/stdlib/gui/SingleLineTextBox.aspl new file mode 100644 index 0000000..5ad9ce7 --- /dev/null +++ b/stdlib/gui/SingleLineTextBox.aspl @@ -0,0 +1,346 @@ +import graphics +import math.geometry + +// TODO: This implementation currently resizes to the required size and the padding is outside of the expected bounds (see the bounds property for the correct bounds) +[public] +class SingleLineTextBox extends Control { + + [public] + property string text + [public] + property Font font + [public] + property int caret + [public] + property Color textColor + [public] + property Color backgroundColor + [public] + property Color borderColor + [public] + property HorizontalAlignment horizontalAlignment + [public] + property VerticalAlignment verticalAlignment + [public] + property bool useCustomVirtualKeyboard + [readpublic] + property VirtualKeyboard? virtualKeyboard + + [public] + property Rectangle bounds{ + get{ + var size = graphics.measure_text_size(text, font) + var startX = position.x + if(horizontalAlignment == HorizontalAlignment.Center){ + startX -= size.width / 2f + }elseif(horizontalAlignment == HorizontalAlignment.Right){ + startX -= size.width + } + var width = size.width + var startY = position.y + if(verticalAlignment == VerticalAlignment.Center){ + startY -= size.height / 2f + }elseif(verticalAlignment == VerticalAlignment.Bottom){ + startY -= size.height + } + var height = size.height + $if relativePadding{ + var XPADDING = 0.1 + var YPADDING = 0.1 + startX -= size.width * XPADDING + width += size.width * XPADDING * 3 // TODO: Why * 3? + startY -= size.height * YPADDING + height += size.height * YPADDING * 2 + }$else{ + var XPADDING = 3f + var YPADDING = 3f + startX -= XPADDING + width += XPADDING * 2 + startY -= YPADDING + height += YPADDING * 2 + } + return new Rectangle(new Point(startX, startY), new Size(width, height)) + } + } + + [public] + property int blinkInterval = 30 // in frames + property int blinkCounter = 0 + [public] + property bool blinkState = false + + property bool lastKeyWasGraveAccent = false + + // TODO: Add support for selections + + [public] + method construct(Point position, string text, Font font, int caret = 0, Color? textColor = null, Color? backgroundColor = null, Color? borderColor = null, HorizontalAlignment? horizontalAlignment = null, VerticalAlignment? verticalAlignment = null, bool useCustomVirtualKeyboard = false) { + this.position = position + this.text = text + this.font = font + this.caret = caret + if(textColor == null) { + this.textColor = new Color(255, 0, 0, 0) + } else { + this.textColor = textColor + } + if(backgroundColor == null) { + this.backgroundColor = new Color(255, 255, 255, 255) + } else { + this.backgroundColor = backgroundColor + } + if(borderColor == null) { + this.borderColor = new Color(255, 0, 0, 0) + } else { + this.borderColor = borderColor + } + if(horizontalAlignment == null) { + this.horizontalAlignment = HorizontalAlignment.Left + } else { + this.horizontalAlignment = horizontalAlignment + } + if(verticalAlignment == null) { + this.verticalAlignment = VerticalAlignment.Top + } else { + this.verticalAlignment = verticalAlignment + } + this.useCustomVirtualKeyboard = useCustomVirtualKeyboard + if(useCustomVirtualKeyboard){ + virtualKeyboard = new VirtualKeyboard(new Point(0, 0), font) + VirtualKeyboard(virtualKeyboard).onKeyPress = callback(string key){ + if(key == "<--"){ + backspace(1) + }elseif(key == "<-"){ + if(this.caret > 0){ + this.caret-- + } + }elseif(key == "->"){ + if(this.caret < text.length){ + this.caret++ + } + }else{ + insert(key) + } + } + } + } + + [public] + method draw(Canvas canvas){ + canvas.fillRectangle(bounds, backgroundColor) + canvas.drawRectangle(bounds, borderColor) + var size = graphics.measure_text_size(text, font) + canvas.drawText(text, int(position.x), int(position.y + size.height), font, textColor, horizontalAlignment, verticalAlignment) + if(hasFocus){ + blinkCounter++ + if(blinkCounter >= blinkInterval){ + blinkCounter = 0 + blinkState = !blinkState + } + if(blinkState){ + var before = text + if(caret < text.length){ + before = text.before(caret) + } + var caretPosition = graphics.measure_text_size(before, font) + var caretPositionWidth = caretPosition.width + if(horizontalAlignment == HorizontalAlignment.Center){ + caretPositionWidth -= size.width / 2f + }elseif(horizontalAlignment == HorizontalAlignment.Right){ + caretPositionWidth -= size.width + } + var caretPositionHeight = position.y + if(verticalAlignment == VerticalAlignment.Center){ + caretPositionHeight -= size.height / 2f + }elseif(verticalAlignment == VerticalAlignment.Bottom){ + caretPositionHeight -= size.height + } + canvas.drawLine(int(position.x + caretPositionWidth), int(caretPositionHeight), int(position.x + caretPositionWidth), int(caretPositionHeight + size.height), textColor) + } + } + + if(useCustomVirtualKeyboard && hasFocus){ + var bounds = VirtualKeyboard(virtualKeyboard).bounds + VirtualKeyboard(virtualKeyboard).position = new Point(canvas.width / 2f - bounds.size.width / 2f, canvas.height * 0.75f - bounds.size.height / 2f) + VirtualKeyboard(virtualKeyboard).draw(canvas) + } + } + + [public] + method insert(string text){ + var before = this.text + if(caret < this.text.length){ + before = this.text.before(caret) + } + var after = "" + if(caret < this.text.length){ + after = this.text.after(caret - 1) + } + this.text = before + text + after + caret += text.length + } + + [public] + method backspace(int amount){ + if(caret > 0){ + this.text = this.text.before(caret - amount) + this.text.after(caret - 1) + caret -= amount + } + } + + [public] + method onMouseDownAny(Point position, MouseButton button) returns bool{ + if(useCustomVirtualKeyboard && hasFocus && VirtualKeyboard(virtualKeyboard).bounds.containsPoint(position)){ + VirtualKeyboard(virtualKeyboard).onMouseDown(position, button) + return true + } + return false + } + + [public] + method onMouseUpAny(Point position, MouseButton button) returns bool{ + if(useCustomVirtualKeyboard && hasFocus && VirtualKeyboard(virtualKeyboard).bounds.containsPoint(position)){ + VirtualKeyboard(virtualKeyboard).onMouseUp(position, button) + return true + } + return false + } + + [public] + method onMouseClick(Point clickPosition, MouseButton button){ + hasFocus = true + // TODO: Move caret to position; does this work? + var s = "" + var cachedBounds = bounds // Cache bounds to avoid multiple calculations + foreach(text as char){ + s += char + var size = graphics.measure_text_size(s, font) + if(clickPosition.x < cachedBounds.position.x + size.width){ // TODO: Already count after only half of the last character? + caret = s.length + break + } + } + } + + [public] + method onMouseClickAny(Point position, MouseButton button) returns bool{ + if(useCustomVirtualKeyboard && hasFocus && VirtualKeyboard(virtualKeyboard).bounds.containsPoint(position)){ + VirtualKeyboard(virtualKeyboard).onMouseClick(position, button) + return true + } + return false + } + + [public] + method onTouchDownAny(list points) returns bool{ + if(useCustomVirtualKeyboard && hasFocus){ + foreach(points as point){ + if(VirtualKeyboard(virtualKeyboard).bounds.containsPoint(point.position)){ + VirtualKeyboard(virtualKeyboard).onTouchDown(points) + return true + } + } + return false + } + return false + } + + [public] + method onTouchUp(list points){ + hasFocus = true + } + + [public] + method onTouchUpAny(list points) returns bool{ + if(useCustomVirtualKeyboard && hasFocus){ + foreach(points as point){ + if(VirtualKeyboard(virtualKeyboard).bounds.containsPoint(point.position)){ + VirtualKeyboard(virtualKeyboard).onTouchUp(points) + return true + } + } + return false + } + return false + } + + [public] + method onKeyDown(KeyCode key, bool isShiftDown){ + if(key == KeyCode.left){ + if(caret > 0){ + caret-- + blinkCounter = 0 + blinkState = true + } + }elseif(key == KeyCode.right){ + if(caret < text.length){ + caret++ + blinkCounter = 0 + blinkState = true + } + }elseif(key == KeyCode.backspace){ + if(caret > 0){ + backspace(1) + } + }elseif(key == KeyCode.a || key == KeyCode.b || key == KeyCode.c || key == KeyCode.d || key == KeyCode.e || key == KeyCode.f || key == KeyCode.g || key == KeyCode.h || key == KeyCode.i || key == KeyCode.j || key == KeyCode.k || key == KeyCode.l || key == KeyCode.m || key == KeyCode.n || key == KeyCode.o || key == KeyCode.p || key == KeyCode.q || key == KeyCode.r || key == KeyCode.s || key == KeyCode.t || key == KeyCode.u || key == KeyCode.v || key == KeyCode.w || key == KeyCode.x || key == KeyCode.y || key == KeyCode.z){ + if(isShiftDown){ + insert(string(key).toUpper()) + }else{ + insert(string(key)) + } + }elseif(key == KeyCode._0 || key == KeyCode._1 || key == KeyCode._2 || key == KeyCode._3 || key == KeyCode._4 || key == KeyCode._5 || key == KeyCode._6 || key == KeyCode._7 || key == KeyCode._8 || key == KeyCode._9){ + if(isShiftDown){ + if(key == KeyCode._1){ + insert("!") + }elseif(key == KeyCode._2){ + insert("@") + }elseif(key == KeyCode._3){ + insert("#") + }elseif(key == KeyCode._4){ + insert("$") + }elseif(key == KeyCode._5){ + insert("%") + }elseif(key == KeyCode._6){ + insert("&") + }elseif(key == KeyCode._7){ + insert("/") + }elseif(key == KeyCode._8){ + insert("(") + }elseif(key == KeyCode._9){ + insert(")") + }elseif(key == KeyCode._0){ + insert("=") + } + }else{ + insert(string(key).after(0)) + } + }elseif(key == KeyCode.space){ + if(lastKeyWasGraveAccent){ + insert("^") + }else{ + insert(" ") + } + }elseif(key == KeyCode.comma){ + insert(",") + }elseif(key == KeyCode.period){ + insert(".") + }elseif(key == KeyCode.right_bracket){ // TODO: Figure out why this is "right_bracket" (it's actuall the plus key) + if(isShiftDown){ + insert("*") + }else{ + insert("+") + } + }elseif(key == KeyCode.slash){ // TODO: Figure out why this is "slash" (it's actuall the minus key) + if(isShiftDown){ + insert("_") + }else{ + insert("-") + } + }elseif(key == KeyCode.enter){ + hasFocus = false + } + lastKeyWasGraveAccent = key == KeyCode.grave_accent + // TODO: Support more keys + } + +} \ No newline at end of file diff --git a/stdlib/gui/VirtualKeyboard.aspl b/stdlib/gui/VirtualKeyboard.aspl new file mode 100644 index 0000000..f2ef530 --- /dev/null +++ b/stdlib/gui/VirtualKeyboard.aspl @@ -0,0 +1,219 @@ +import graphics +import math.geometry + +[public] +class VirtualKeyboard extends Control { + + [public] + property Font font + [public] + property Color textColor + [public] + property Color backgroundColor + [public] + property Color buttonColor + [public] + property Color borderColor + [public] + property Color keyDownColor + [public] + property list> keys = [ + ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+"], + ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "-"], + ["a", "s", "d", "f", "g", "h", "j", "k", "l", "*", "/"], + ["z", "x", "c", "v", "b", "n", "m", "(", ")", ".", "%"], + ["[C]", "_", "!", "^", "?", ":", "___", "<--", "<-", "->"] + ] // TODO: Change this + + [public] + property callback onKeyPress = callback(string key){} + + [readpublic] + property map keyBounds + [readpublic] + property list keysDown + property bool capsEnabled + + [public] + property Rectangle bounds{ + get{ + var yOffset = 0 + var maxXOffset = 0 + var i = 0 + foreach(keys as row){ + var maxHeight = 0 + var xOffset = 0 + var j = 0 + foreach(row as key){ + var size = graphics.measure_text_size(key, font) + if(!(key == "<--" || key == "<-" || key == "->" || key == "___")){ + size = graphics.measure_text_size("m", font) + } + if(j == row.length - 1){ + xOffset += int(size.width * 2) + }else{ + xOffset += int(size.width * 2.5) + } + if(size.height > maxHeight){ + maxHeight = int(size.height) + } + j++ + } + if(i == keys.length - 1){ + yOffset += int(maxHeight * 2) + }else{ + yOffset += int(maxHeight * 2.5) + } + if(xOffset > maxXOffset){ + maxXOffset = xOffset + } + i++ + } + return new Rectangle(position, new Size(float(maxXOffset), float(yOffset))) + } + } + + [public] + method construct(Point position, Font font, Color? textColor = null, Color? backgroundColor = null, Color? borderColor = null, Color? keyDownColor = null) { + this.position = position + this.font = font + if(textColor == null) { + this.textColor = new Color(255, 0, 0, 0) + } else { + this.textColor = textColor + } + if(backgroundColor == null) { + this.backgroundColor = new Color(255, 255, 255, 255) + } else { + this.backgroundColor = backgroundColor + } + this.buttonColor = new Color(255, 250, 250, 250) + if(borderColor == null) { + this.borderColor = new Color(255, 0, 0, 0) + } else { + this.borderColor = borderColor + } + if(keyDownColor == null) { + this.keyDownColor = new Color(255, 230, 230, 230) + } else { + this.keyDownColor = keyDownColor + } + } + + [public] + method draw(Canvas canvas){ + canvas.fillRectangle(bounds, backgroundColor) + canvas.drawRectangle(bounds, borderColor) + var yOffset = 0 + foreach(keys as row){ + var maxHeight = 0 + var xOffset = 0 + foreach(row as key){ + var displayKey = key + if(capsEnabled){ + displayKey = key.toUpper() + } + var size = graphics.measure_text_size(displayKey, font) + if(!(key == "<--" || key == "<-" || key == "->" || key == "___")){ + size = graphics.measure_text_size("m", font) + } + var bounds = new Rectangle(new Point(position.x + xOffset, position.y + yOffset), new Size(size.width * 2, size.height * 2)) + + if(keysDown.contains(displayKey)){ + canvas.fillRectangle(bounds, keyDownColor) + }else{ + canvas.fillRectangle(bounds, buttonColor) + } + canvas.drawText(displayKey, int(position.x + xOffset + size.width / 2), int(position.y + yOffset + size.height / 2), font, textColor, HorizontalAlignment.Left, VerticalAlignment.Top) + keyBounds[key] = bounds + canvas.drawRectangle(bounds, borderColor) + + xOffset += int(size.width * 2.5) + if(size.height > maxHeight){ + maxHeight = int(size.height) + } + } + yOffset += int(maxHeight * 2.5) + } + } + + [public] + method onMouseDown(Point position, MouseButton button){ + hasFocus = true + foreach(keyBounds as key => bounds){ + if(bounds.containsPoint(position)){ + keysDown.add(key) + } + } + } + + [public] + method onMouseUp(Point position, MouseButton button){ + foreach(keyBounds as key => bounds){ + if(keysDown.contains(key)){ + keysDown.remove(key) + var string? k = key + if(k == "<--"){ + // TODO: k = "\b" + }elseif(k == "<-"){ + // TODO + }elseif(k == "->"){ + // TODO + }elseif(k == "___"){ + k = " " + }elseif(k == "[C]"){ + capsEnabled = !capsEnabled + k = null + } + if(k != null){ + if(capsEnabled){ + k = k?!.toUpper() + } + onKeyPress.(k) + } + } + } + } + + [public] + method onTouchDown(list points){ + foreach(points as point){ + if(point.changed && bounds.containsPoint(point.position)){ + foreach(keyBounds as key => bounds){ + if(bounds.containsPoint(point.position)){ + keysDown.add(key) + } + } + } + } + } + + [public] + method onTouchUp(list points){ + foreach(keyBounds as key => bounds){ + if(keysDown.contains(key)){ + keysDown.remove(key) + var string? k = key + if(k == "<--"){ + // TODO: k = "\b" + }elseif(k == "<-"){ + // TODO + }elseif(k == "->"){ + // TODO + }elseif(k == "___"){ + k = " " + }elseif(k == "[C]"){ + capsEnabled = !capsEnabled + k = null + } + if(k != null){ + if(capsEnabled){ + k = k?!.toUpper() + } + onKeyPress.(k) + } + } + } + } + +} \ No newline at end of file diff --git a/stdlib/internet/AddressFamily.aspl b/stdlib/internet/AddressFamily.aspl new file mode 100644 index 0000000..b71b038 --- /dev/null +++ b/stdlib/internet/AddressFamily.aspl @@ -0,0 +1,7 @@ +[public] +enum AddressFamily { + Unix, + Ip4, + Ip6, + Unspecified +} \ No newline at end of file diff --git a/stdlib/internet/HttpResponse.aspl b/stdlib/internet/HttpResponse.aspl new file mode 100644 index 0000000..94f55e7 --- /dev/null +++ b/stdlib/internet/HttpResponse.aspl @@ -0,0 +1,24 @@ +[public] +class HttpResponse { + + [readpublic] + property string text + [readpublic] + property map> headers + [readpublic] + property int statusCode + [readpublic] + property string statusMessage + [readpublic] + property string httpVersion + + [public] + method construct(string text, map> headers, int statusCode, string statusMessage, string httpVersion) { + this.text = text + this.headers = headers + this.statusCode = statusCode + this.statusMessage = statusMessage + this.httpVersion = httpVersion + } + +} \ No newline at end of file diff --git a/stdlib/internet/WebSocketClient.aspl b/stdlib/internet/WebSocketClient.aspl new file mode 100644 index 0000000..b2f8677 --- /dev/null +++ b/stdlib/internet/WebSocketClient.aspl @@ -0,0 +1,51 @@ +[public] +class WebSocketClient{ + + property any handle + + [public] + property callback onConnect = callback(){} + [public] + property callback onMessage = callback(string message){} + [public] + property callback onError = callback(string error){} + [public] + property callback onClose = callback(int code, string reason){} + [readpublic] + property bool connected = false + + [public] + method construct(string uri){ + handle = implement("internet.web_socket_client.new", uri) + implement("internet.web_socket_client.set_on_connect", handle, callback(){ + this.connected = true + onConnect.() + }) + implement("internet.web_socket_client.set_on_message", handle, callback(string message){ + onMessage.(message) + }) + implement("internet.web_socket_client.set_on_error", handle, callback(string error){ + onError.(error) + }) + implement("internet.web_socket_client.set_on_close", handle, callback(int code, string reason){ + onClose.(code, reason) + this.connected = false + }) + } + + [public] + method connect(){ + implement("internet.web_socket_client.connect", handle) + } + + [public] + method send(string content){ + implement("internet.web_socket_client.send", handle, content) + } + + [public] + method disconnect(int code, string reason){ + implement("internet.web_socket_client.disconnect", handle, code, reason) + } + +} diff --git a/stdlib/internet/WebSocketServer.aspl b/stdlib/internet/WebSocketServer.aspl new file mode 100644 index 0000000..d4175e7 --- /dev/null +++ b/stdlib/internet/WebSocketServer.aspl @@ -0,0 +1,46 @@ +class WebSocketServer{ + + property any handle + + [public] + property callback onConnect = callback(WebSocketServerClient client){} + [public] + property callback onMessage = callback(string message){} + [public] + property callback onClose = callback(WebSocketServerClient client, int code, string reason){} + + [public] + method construct(AddressFamily addressFamily, int port){ + var addressFamilyString = "" + if(addressFamily == AddressFamily.Unix){ + addressFamilyString = "unix" + } + elseif(addressFamily == AddressFamily.Ip4){ + addressFamilyString = "ip4" + } + elseif(addressFamily == AddressFamily.Ip6){ + addressFamilyString = "ip6" + } + elseif(addressFamily == AddressFamily.Unspecified){ + addressFamilyString = "unspecified" + } + handle = implement("internet.web_socket_server.new", addressFamilyString, port) + implement("internet.web_socket_server.set_on_connect", handle, callback(any clientHandle){ + var WebSocketServerClient client = new WebSocketServerClient(clientHandle) + onConnect.(client) + }) + implement("internet.web_socket_server.set_on_message", handle, callback(string message){ + onMessage.(message) + }) + implement("internet.web_socket_server.set_on_close", handle, callback(any clientHandle, int code, string reason){ + var WebSocketServerClient client = new WebSocketServerClient(clientHandle) + onClose.(client, code, reason) + }) + } + + [public] + method listen(){ + implement("internet.web_socket_server.listen", handle) + } + +} diff --git a/stdlib/internet/WebSocketServerClient.aspl b/stdlib/internet/WebSocketServerClient.aspl new file mode 100644 index 0000000..aed7e0d --- /dev/null +++ b/stdlib/internet/WebSocketServerClient.aspl @@ -0,0 +1,19 @@ +[public] +class WebSocketServerClient { + + property any handle + [readpublic] + property string id + + [public] + method construct(any handle){ + this.handle = handle + id = string(implement("internet.web_socket_server_client.get_id", handle)) + } + + [public] + method send(string content){ + implement("internet.web_socket_server_client.send", handle, content) + } + +} \ No newline at end of file diff --git a/stdlib/internet/http.aspl b/stdlib/internet/http.aspl new file mode 100644 index 0000000..b78682a --- /dev/null +++ b/stdlib/internet/http.aspl @@ -0,0 +1,53 @@ +[public] +function get(string uri, string data = "", map>? headers = null) returns HttpResponse{ + var response = list>|int>(implement("internet.http.get", uri, data, headers)) + return new HttpResponse(string(response[0]), map>(response[1]), int(response[2]), string(response[3]), string(response[4])) +} + +[public] +function post(string uri, string data, map>? headers = null) returns HttpResponse{ + var response = list>|int>(implement("internet.http.post", uri, data, headers)) + return new HttpResponse(string(response[0]), map>(response[1]), int(response[2]), string(response[3]), string(response[4])) +} + +[public] +function put(string uri, string data, map>? headers = null) returns HttpResponse{ + var response = list>|int>(implement("internet.http.put", uri, data, headers)) + return new HttpResponse(string(response[0]), map>(response[1]), int(response[2]), string(response[3]), string(response[4])) +} + +[public] +function head(string uri, string data, map>? headers = null) returns HttpResponse{ + var response = list>|int>(implement("internet.http.head", uri, data, headers)) + return new HttpResponse(string(response[0]), map>(response[1]), int(response[2]), string(response[3]), string(response[4])) +} + +[public] +function delete(string uri, string data, map>? headers = null) returns HttpResponse{ + var response = list>|int>(implement("internet.http.delete", uri, data, headers)) + return new HttpResponse(string(response[0]), map>(response[1]), int(response[2]), string(response[3]), string(response[4])) +} + +[public] +function options(string uri, string data, map>? headers = null) returns HttpResponse{ + var response = list>|int>(implement("internet.http.options", uri, data, headers)) + return new HttpResponse(string(response[0]), map>(response[1]), int(response[2]), string(response[3]), string(response[4])) +} + +[public] +function trace(string uri, string data, map>? headers = null) returns HttpResponse{ + var response = list>|int>(implement("internet.http.trace", uri, data, headers)) + return new HttpResponse(string(response[0]), map>(response[1]), int(response[2]), string(response[3]), string(response[4])) +} + +[public] +function connect(string uri, string data, map>? headers = null) returns HttpResponse{ + var response = list>|int>(implement("internet.http.connect", uri, data, headers)) + return new HttpResponse(string(response[0]), map>(response[1]), int(response[2]), string(response[3]), string(response[4])) +} + +[public] +function patch(string uri, string data, map>? headers = null) returns HttpResponse{ + var response = list>|int>(implement("internet.http.patch", uri, data, headers)) + return new HttpResponse(string(response[0]), map>(response[1]), int(response[2]), string(response[3]), string(response[4])) +} \ No newline at end of file diff --git a/stdlib/internet/implementations/implementations.aspl b/stdlib/internet/implementations/implementations.aspl new file mode 100644 index 0000000..6918b3e --- /dev/null +++ b/stdlib/internet/implementations/implementations.aspl @@ -0,0 +1,6 @@ +$if !twailbackend{ + $include("implementations.c") + $if windows{ + $link("ws2_32") + } +} \ No newline at end of file diff --git a/stdlib/internet/implementations/implementations.c b/stdlib/internet/implementations/implementations.c new file mode 100644 index 0000000..59ab362 --- /dev/null +++ b/stdlib/internet/implementations/implementations.c @@ -0,0 +1,312 @@ +#define ECHTTP_IMPLEMENTATION +#define ECHTTP_TLSE_IMPLEMENTATION +#define ECHTTP_MALLOC ASPL_MALLOC +#define ECHTTP_REALLOC ASPL_REALLOC +#define ECHTTP_FREE ASPL_FREE +#include "thirdparty/echttp/echttp.h" + +#define CWSC_IMPLEMENTATION +#define CWSC_MALLOC ASPL_MALLOC +#define CWSC_REALLOC ASPL_REALLOC +#define CWSC_FREE ASPL_FREE +#include "thirdparty/cwsc/cwsc.h" + +// Note: Those #undefs are required because CipherSuite.h (which is included by sokol) also uses these identifiers +// TODO: Check if there are more #undefs than required +#undef TLS_RSA_WITH_AES_128_GCM_SHA256 +#undef TLS_RSA_WITH_AES_256_GCM_SHA384 +#undef TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 +#undef TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 +#undef TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 +#undef TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 +#undef TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 +#undef TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 +#undef TLS_AES_128_GCM_SHA256 +#undef TLS_AES_256_GCM_SHA384 +#undef TLS_CHACHA20_POLY1305_SHA256 +#undef TLS_AES_128_CCM_SHA256 +#undef TLS_AES_128_CCM_8_SHA256 +#undef TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 +#undef TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 +#undef TLS_RSA_WITH_AES_128_CBC_SHA +#undef TLS_DHE_RSA_WITH_AES_128_CBC_SHA +#undef TLS_RSA_WITH_AES_256_CBC_SHA +#undef TLS_DHE_RSA_WITH_AES_256_CBC_SHA +#undef TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA +#undef TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA +#undef TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA +#undef TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA +#undef TLS_RSA_WITH_AES_128_CBC_SHA256 +#undef TLS_RSA_WITH_AES_256_CBC_SHA256 +#undef TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 +#undef TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 +#undef TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 +#undef TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 +#undef TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + +void aspl_callback_invoke(ASPL_OBJECT_TYPE closure); +void aspl_callback_string__invoke(ASPL_OBJECT_TYPE closure, ASPL_OBJECT_TYPE* message); +void aspl_callback_integer_string__invoke(ASPL_OBJECT_TYPE closure, ASPL_OBJECT_TYPE* code, ASPL_OBJECT_TYPE* message); + +ASPL_OBJECT_TYPE aspl_util_perform_http_request(const char* method, ASPL_OBJECT_TYPE* url, ASPL_OBJECT_TYPE* data, ASPL_OBJECT_TYPE* headers) { + size_t headerCount = ASPL_ACCESS(*headers).kind == ASPL_OBJECT_KIND_NULL ? 0 : ASPL_ACCESS(*headers).value.map->hashmap->len; + echttp_Header* headerList = ASPL_MALLOC(sizeof(echttp_Header) * headerCount); + for (size_t i = 0; i < headerCount; i++) + { + ASPL_OBJECT_TYPE key = ASPL_MAP_GET_KEY_FROM_INDEX(*headers, ASPL_INT_LITERAL(i)); + ASPL_OBJECT_TYPE value = ASPL_MAP_GET_VALUE_FROM_INDEX(*headers, ASPL_INT_LITERAL(i)); + headerList[i].name = ASPL_ACCESS(key).value.string->str; + headerList[i].value_count = ASPL_ACCESS(value).value.list->length; + headerList[i].values = ASPL_MALLOC(sizeof(char*) * headerList[i].value_count); + for (size_t j = 0; j < headerList[i].value_count; j++) + { + ASPL_OBJECT_TYPE headerValue = ASPL_LIST_GET(value, ASPL_INT_LITERAL(j)); + headerList[i].values[j] = ASPL_ACCESS(headerValue).value.string->str; + } + } + echttp_Response response = echttp_request(method, ASPL_ACCESS(*url).value.string->str, ASPL_ACCESS(*data).value.string->str, ASPL_ACCESS(*data).value.string->length, headerList, headerCount); + ASPL_OBJECT_TYPE* responseList = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 5); + responseList[0] = ASPL_STRING_LITERAL(response.data); + ASPL_OBJECT_TYPE* responseHeaders = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * response.header_count * 2); + for (size_t i = 0; i < response.header_count; i++) + { + responseHeaders[i * 2] = ASPL_STRING_LITERAL(response.headers[i].name); + ASPL_OBJECT_TYPE* headerValues = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * response.headers[i].value_count); + for (size_t j = 0; j < response.headers[i].value_count; j++) + { + headerValues[j] = ASPL_STRING_LITERAL(response.headers[i].values[j]); + } + responseHeaders[i * 2 + 1] = ASPL_LIST_LITERAL("list", 12, headerValues, response.headers[i].value_count); + } + responseList[1] = ASPL_MAP_LITERAL("map>", 26, responseHeaders, response.header_count); + responseList[2] = ASPL_INT_LITERAL(response.status_code); + responseList[3] = ASPL_STRING_LITERAL(response.reason_phrase); + responseList[4] = ASPL_STRING_LITERAL(response.http_version); + return ASPL_LIST_LITERAL( + "list>|integer>", + 47, + responseList, + 5 + ); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$http$get(ASPL_OBJECT_TYPE* url, ASPL_OBJECT_TYPE* data, ASPL_OBJECT_TYPE* headers) +{ + return aspl_util_perform_http_request("get", url, data, headers); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$http$post(ASPL_OBJECT_TYPE* url, ASPL_OBJECT_TYPE* data, ASPL_OBJECT_TYPE* headers) +{ + return aspl_util_perform_http_request("post", url, data, headers); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$http$put(ASPL_OBJECT_TYPE* url, ASPL_OBJECT_TYPE* data, ASPL_OBJECT_TYPE* headers) +{ + return aspl_util_perform_http_request("put", url, data, headers); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$http$head(ASPL_OBJECT_TYPE* url, ASPL_OBJECT_TYPE* data, ASPL_OBJECT_TYPE* headers) +{ + return aspl_util_perform_http_request("head", url, data, headers); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$http$delete(ASPL_OBJECT_TYPE* url, ASPL_OBJECT_TYPE* data, ASPL_OBJECT_TYPE* headers) +{ + return aspl_util_perform_http_request("delete", url, data, headers); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$http$options(ASPL_OBJECT_TYPE* url, ASPL_OBJECT_TYPE* data, ASPL_OBJECT_TYPE* headers) +{ + return aspl_util_perform_http_request("options", url, data, headers); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$http$trace(ASPL_OBJECT_TYPE* url, ASPL_OBJECT_TYPE* data, ASPL_OBJECT_TYPE* headers) +{ + return aspl_util_perform_http_request("trace", url, data, headers); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$http$connect(ASPL_OBJECT_TYPE* url, ASPL_OBJECT_TYPE* data, ASPL_OBJECT_TYPE* headers) +{ + return aspl_util_perform_http_request("connect", url, data, headers); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$http$patch(ASPL_OBJECT_TYPE* url, ASPL_OBJECT_TYPE* data, ASPL_OBJECT_TYPE* headers) +{ + return aspl_util_perform_http_request("patch", url, data, headers); +} + +typedef struct ASPL_handle_WebSocketClient +{ + char* uri; + cwsc_WebSocket* handle; + ASPL_OBJECT_TYPE on_connect; + ASPL_OBJECT_TYPE on_message; + ASPL_OBJECT_TYPE on_error; + ASPL_OBJECT_TYPE on_close; +} ASPL_handle_WebSocketClient; + +void aspl_util_internet$WebSocketClient$connect_callback(cwsc_WebSocket* ws) +{ + ASPL_handle_WebSocketClient* handle = ws->user_data; +#ifdef ASPL_INTERPRETER_MODE + ASPL_AILI_ArgumentList arguments = (ASPL_AILI_ArgumentList){ .size = 0, .arguments = NULL }; + aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_ACCESS(handle->on_connect).value.callback, arguments); +#else + aspl_callback_invoke(handle->on_connect); +#endif +} + +void aspl_util_internet$WebSocketClient$message_callback(cwsc_WebSocket* ws, cwsc_Opcode opcode, const unsigned char* message, int length) +{ + ASPL_handle_WebSocketClient* handle = ws->user_data; + ASPL_OBJECT_TYPE* messageObject = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE)); + *messageObject = ASPL_STRING_LITERAL((char*)message); +#ifdef ASPL_INTERPRETER_MODE + ASPL_AILI_ArgumentList arguments = (ASPL_AILI_ArgumentList){ .size = 1, .arguments = messageObject }; + aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_ACCESS(handle->on_message).value.callback, arguments); +#else + aspl_callback_string__invoke(handle->on_message, messageObject); +#endif +} + +void aspl_util_internet$WebSocketClient$error_callback(cwsc_WebSocket* ws, const char* message, int length) +{ + ASPL_handle_WebSocketClient* handle = ws->user_data; + ASPL_OBJECT_TYPE* messageObject = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE)); + *messageObject = ASPL_STRING_LITERAL(message); +#ifdef ASPL_INTERPRETER_MODE + ASPL_AILI_ArgumentList arguments = (ASPL_AILI_ArgumentList){ .size = 1, .arguments = messageObject }; + aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_ACCESS(handle->on_error).value.callback, arguments); +#else + aspl_callback_string__invoke(handle->on_error, messageObject); +#endif +} + +void aspl_util_internet$WebSocketClient$close_callback(cwsc_WebSocket* ws, int code, const char* message, int length) +{ + ASPL_handle_WebSocketClient* handle = ws->user_data; + ASPL_OBJECT_TYPE* codeObject = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE)); + *codeObject = ASPL_INT_LITERAL(code); + ASPL_OBJECT_TYPE* messageObject = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE)); + *messageObject = ASPL_STRING_LITERAL(message); +#ifdef ASPL_INTERPRETER_MODE + ASPL_AILI_ArgumentList arguments = (ASPL_AILI_ArgumentList){ .size = 2, .arguments = (ASPL_OBJECT_TYPE[]){ *codeObject, *messageObject } }; + aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_ACCESS(handle->on_close).value.callback, arguments); +#else + aspl_callback_integer_string__invoke(handle->on_close, codeObject, messageObject); +#endif +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$web_socket_client$new(ASPL_OBJECT_TYPE* uri) +{ + ASPL_handle_WebSocketClient* client = ASPL_MALLOC(sizeof(ASPL_handle_WebSocketClient)); + client->uri = ASPL_ACCESS(*uri).value.string->str; + client->handle = ASPL_MALLOC(sizeof(cwsc_WebSocket)); + return ASPL_HANDLE_LITERAL(client); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$web_socket_client$connect(ASPL_OBJECT_TYPE* client) +{ + ASPL_handle_WebSocketClient* handle = ASPL_ACCESS(*client).value.handle; + cwsc_WebSocket* ws = ASPL_MALLOC(sizeof(cwsc_WebSocket)); + handle->handle = ws; + ws->user_data = handle; + ws->tls_context = tls_create_context(0, TLS_V12); + ws->on_open = aspl_util_internet$WebSocketClient$connect_callback; + ws->on_message = aspl_util_internet$WebSocketClient$message_callback; + ws->on_error = aspl_util_internet$WebSocketClient$error_callback; + ws->on_close = aspl_util_internet$WebSocketClient$close_callback; + cwsc_connect(handle->handle, handle->uri, strncmp(handle->uri, "wss", strlen("wss")) == 0 ? 443 : 80); + cwsc_listen(handle->handle); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$web_socket_client$set_on_connect(ASPL_OBJECT_TYPE* client, ASPL_OBJECT_TYPE* callback) +{ + ASPL_handle_WebSocketClient* handle = ASPL_ACCESS(*client).value.handle; + handle->on_connect = *callback; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$web_socket_client$set_on_message(ASPL_OBJECT_TYPE* client, ASPL_OBJECT_TYPE* callback) +{ + ASPL_handle_WebSocketClient* handle = ASPL_ACCESS(*client).value.handle; + handle->on_message = *callback; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$web_socket_client$set_on_error(ASPL_OBJECT_TYPE* client, ASPL_OBJECT_TYPE* callback) +{ + ASPL_handle_WebSocketClient* handle = ASPL_ACCESS(*client).value.handle; + handle->on_error = *callback; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$web_socket_client$set_on_close(ASPL_OBJECT_TYPE* client, ASPL_OBJECT_TYPE* callback) +{ + ASPL_handle_WebSocketClient* handle = ASPL_ACCESS(*client).value.handle; + handle->on_close = *callback; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$web_socket_client$send(ASPL_OBJECT_TYPE* client, ASPL_OBJECT_TYPE* message) +{ + ASPL_handle_WebSocketClient* handle = ASPL_ACCESS(*client).value.handle; + cwsc_send_message(handle->handle, ASPL_ACCESS(*message).value.string->str); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$web_socket_client$disconnect(ASPL_OBJECT_TYPE* client, ASPL_OBJECT_TYPE* code, ASPL_OBJECT_TYPE* message) +{ + ASPL_handle_WebSocketClient* handle = ASPL_ACCESS(*client).value.handle; + cwsc_disconnect(handle->handle, ASPL_ACCESS(*message).value.string->str, ASPL_ACCESS(*code).value.integer32); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$web_socket_server$new(ASPL_OBJECT_TYPE* address_family, ASPL_OBJECT_TYPE* port) +{ + // TODO + ASPL_PANIC("Unimplemented"); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$web_socket_server$listen(ASPL_OBJECT_TYPE* server) +{ + // TODO + ASPL_PANIC("Unimplemented"); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$web_socket_server$set_on_connect(ASPL_OBJECT_TYPE* server, ASPL_OBJECT_TYPE* callback) +{ + // TODO + ASPL_PANIC("Unimplemented"); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$web_socket_server$set_on_message(ASPL_OBJECT_TYPE* server, ASPL_OBJECT_TYPE* callback) +{ + // TODO + ASPL_PANIC("Unimplemented"); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$web_socket_server$set_on_error(ASPL_OBJECT_TYPE* server, ASPL_OBJECT_TYPE* callback) +{ + // TODO + ASPL_PANIC("Unimplemented"); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$web_socket_server$set_on_close(ASPL_OBJECT_TYPE* server, ASPL_OBJECT_TYPE* callback) +{ + // TODO + ASPL_PANIC("Unimplemented"); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$web_socket_server_client$get_id(ASPL_OBJECT_TYPE* server_client) +{ + // TODO + ASPL_PANIC("Unimplemented"); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_internet$web_socket_server_client$send(ASPL_OBJECT_TYPE* server_client, ASPL_OBJECT_TYPE* message) +{ + // TODO + ASPL_PANIC("Unimplemented"); +} \ No newline at end of file diff --git a/stdlib/io/BinaryStream.aspl b/stdlib/io/BinaryStream.aspl new file mode 100644 index 0000000..57654cb --- /dev/null +++ b/stdlib/io/BinaryStream.aspl @@ -0,0 +1,56 @@ +[public] +class BinaryStream extends Stream{ + + property list bytes + + [public] + method construct(list bytes){ + this.bytes = bytes + } + + [public] + method getBytes() returns list{ + return bytes + } + + [public] + method peekByte() returns byte{ + return bytes[this.position] + } + + [public] + method peekBytes(int count) returns list{ + var l = readBytes(count) + this.position -= count + return l + } + + [public] + method readByte() returns byte{ + var b = peekByte() + this.position++ + return b + } + + [public] + method readBytes(int count) returns list{ + var list l = list[] + repeat(count){ + l.add(readByte()) + } + return l + } + + [public] + method writeByte(byte b){ + bytes.insert(this.position, b) + b++ + } + + [public] + method writeBytes(list bytes){ + bytes.insertElements(this.position, bytes) + this.position += bytes.length + } + +} \ No newline at end of file diff --git a/stdlib/io/Stream.aspl b/stdlib/io/Stream.aspl new file mode 100644 index 0000000..1f69fa2 --- /dev/null +++ b/stdlib/io/Stream.aspl @@ -0,0 +1,20 @@ +[public] +[abstract] +class Stream{ + + [public] + property int position + + [public] + [deprecated] + method getPosition() returns int{ + return position + } + + [public] + [deprecated] + method setPosition(int position){ + position = position + } + +} \ No newline at end of file diff --git a/stdlib/io/directories.aspl b/stdlib/io/directories.aspl new file mode 100644 index 0000000..497b78e --- /dev/null +++ b/stdlib/io/directories.aspl @@ -0,0 +1,34 @@ +[public] +function exists_directory(string path) returns bool{ + return bool(implement("io.directory.exists", path)) +} + +[public] +function create_directory(string path){ + implement("io.directory.create", path) +} + +[public] +function delete_directory(string path){ + implement("io.directory.delete", path) +} + +[public] +function full_directory_path(string path) returns string{ + return string(implement("io.path.get_directory_path", path)) +} + +[public] +function directory_name(string path) returns string{ + return string(implement("io.path.get_directory_name", path)) +} + +[public] +function directories(string path) returns list{ + return list(implement("io.directory.list", path)) +} + +[public] +function get_home_directory() returns string{ + return string(implement("io.directory.get_home_directory")) +} \ No newline at end of file diff --git a/stdlib/io/files.aspl b/stdlib/io/files.aspl new file mode 100644 index 0000000..1215166 --- /dev/null +++ b/stdlib/io/files.aspl @@ -0,0 +1,39 @@ +[public] +function exists_file(string path) returns bool{ + return bool(implement("io.file.exists", path)) +} + +[public] +function read_file(string path) returns string{ + return string(implement("io.file.read_string", path)) +} + +[public] +function read_file_bytes(string path) returns list{ + return list(implement("io.file.read_bytes", path)) +} + +[public] +function write_file(string path, string data){ + implement("io.file.write_string", path, data) +} + +[public] +function write_file_bytes(string path, list data){ + implement("io.file.write_bytes", path, data) +} + +[public] +function file_name(string path) returns string{ + return string(implement("io.path.get_file_name", path)) +} + +[public] +function delete_file(string path){ + implement("io.file.delete", path) +} + +[public] +function files(string path) returns list{ + return list(implement("io.file.list", path)) +} \ No newline at end of file diff --git a/stdlib/io/glob.aspl b/stdlib/io/glob.aspl new file mode 100644 index 0000000..f4d6073 --- /dev/null +++ b/stdlib/io/glob.aspl @@ -0,0 +1,4 @@ +[public] +function glob(string pattern) returns list{ + return list(implement("io.glob.search", pattern)) +} \ No newline at end of file diff --git a/stdlib/io/implementations/implementations.aspl b/stdlib/io/implementations/implementations.aspl new file mode 100644 index 0000000..74ecfe5 --- /dev/null +++ b/stdlib/io/implementations/implementations.aspl @@ -0,0 +1,3 @@ +$if !twailbackend{ + $include("implementations.c") +} \ No newline at end of file diff --git a/stdlib/io/implementations/implementations.c b/stdlib/io/implementations/implementations.c new file mode 100644 index 0000000..75f3af4 --- /dev/null +++ b/stdlib/io/implementations/implementations.c @@ -0,0 +1,585 @@ +#include +#include +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#include +#endif +#ifdef __APPLE__ +#include +#endif + +char* aspl_util_io$windowsify_path(ASPL_OBJECT_TYPE path) { + char* newPath = ASPL_MALLOC(ASPL_ACCESS(path).value.string->length + 1); + strcpy(newPath, ASPL_ACCESS(path).value.string->str); + for (int i = 0; i < ASPL_ACCESS(path).value.string->length; i++) + { + if (newPath[i] == '/') + { + newPath[i] = '\\'; + } + } + return newPath; +} + +char* aspl_util_io$unixify_path(ASPL_OBJECT_TYPE path) { + char* newPath = ASPL_MALLOC(ASPL_ACCESS(path).value.string->length + 1); + strcpy(newPath, ASPL_ACCESS(path).value.string->str); + for (int i = 0; i < ASPL_ACCESS(path).value.string->length; i++) + { + if (newPath[i] == '\\') + { + newPath[i] = '/'; + } + } + return newPath; +} + +char* aspl_util_io$osify_path(ASPL_OBJECT_TYPE path) { +#ifdef _WIN32 + return aspl_util_io$windowsify_path(path); +#else + return aspl_util_io$unixify_path(path); +#endif +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$directory$exists(ASPL_OBJECT_TYPE* path) +{ + char* pathStr = aspl_util_io$osify_path(*path); +#ifdef _WIN32 + DWORD dwAttrib = GetFileAttributes(pathStr); + return ASPL_BOOL_LITERAL(dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +#else + struct stat path_stat; + if (stat(ASPL_ACCESS(*path).value.string->str, &path_stat) == -1) { + return ASPL_BOOL_LITERAL(0); + } + return ASPL_BOOL_LITERAL(S_ISDIR(path_stat.st_mode)); +#endif +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$directory$create(ASPL_OBJECT_TYPE* path) +{ + char* pathStr = aspl_util_io$osify_path(*path); +#ifdef _WIN32 + int result = mkdir(pathStr); +#else + int result = mkdir(pathStr, 0777); +#endif + if (result == 0) + { + return ASPL_TRUE(); + } + else + { + return ASPL_FALSE(); + } +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$directory$delete(ASPL_OBJECT_TYPE* path) +{ + char* pathStr = aspl_util_io$osify_path(*path); + int result = rmdir(pathStr); + if (result == 0) + { + return ASPL_TRUE(); + } + else + { + return ASPL_FALSE(); + } +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$path$get_directory_path(ASPL_OBJECT_TYPE* path) +{ + char* pathStr = aspl_util_io$osify_path(*path); +#ifdef _WIN32 + char fullpath[PATH_MAX]; + DWORD len = GetFullPathNameA(pathStr, PATH_MAX, fullpath, NULL); + if (len > 0) { + if (GetFileAttributesA(fullpath) & FILE_ATTRIBUTE_DIRECTORY) { + return ASPL_STRING_LITERAL(fullpath); + } + else { + // If it's a file, remove the file name from the path + char* lastSlash = strrchr(fullpath, '\\'); + if (lastSlash != NULL) { + *lastSlash = '\0'; + } + return ASPL_STRING_LITERAL(fullpath); + } + } + else { + ASPL_PANIC("Path length <= 0"); + } +#else + char* resolved_path = realpath(pathStr, NULL); + if (resolved_path != NULL) { + struct stat path_stat; + stat(resolved_path, &path_stat); + if (S_ISDIR(path_stat.st_mode)) { + return ASPL_STRING_LITERAL(resolved_path); + } + else { + // If it's a file, remove the file name from the path + char* lastSlash = strrchr(resolved_path, '/'); + if (lastSlash != NULL) { + *lastSlash = '\0'; + } + return ASPL_STRING_LITERAL(resolved_path); + } + } + else { + ASPL_PANIC("Could not resolve path '%s'", pathStr); + } +#endif + // TODO: Resolve symlinks etc... + // see: https://github.com/vlang/v/blob/master/vlib/os/os.c.v#L875 +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$path$get_directory_name(ASPL_OBJECT_TYPE* path) +{ + char* pathStr = aspl_util_io$unixify_path(*path); // unixify here because the code only works with `/` + int pathLength = ASPL_ACCESS(*path).value.string->length; + while (pathLength > 0 && pathStr[pathLength - 1] == '/') { + pathStr[pathLength - 1] = '\0'; + pathLength--; + } + char* lastSlash = strrchr(pathStr, '/'); + if (lastSlash == NULL) + { + return ASPL_STRING_LITERAL(pathStr); + } + else + { + int lastSlashIndex = lastSlash - pathStr; + char* directoryName = ASPL_MALLOC(ASPL_ACCESS(*path).value.string->length - lastSlashIndex); + strcpy(directoryName, lastSlash + 1); + return ASPL_STRING_LITERAL(directoryName); + } +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$directory$list(ASPL_OBJECT_TYPE* path) +{ + char* pathStr = aspl_util_io$osify_path(*path); + DIR* dir = opendir(pathStr); + if (dir == NULL) + { + return ASPL_LIST_LITERAL("list", 12, 0, 0); + } + else + { + struct dirent* entry; + int pathCount = 0; + ASPL_OBJECT_TYPE* paths = NULL; + while ((entry = readdir(dir)) != NULL) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + { + continue; + } +#ifdef _WIN32 + char* fullPath = ASPL_MALLOC(strlen(pathStr) + strlen(entry->d_name) + 2); + strcpy(fullPath, pathStr); + strcat(fullPath, "/"); + strcat(fullPath, entry->d_name); + DWORD dwAttrib = GetFileAttributes(fullPath); + if (!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) { + continue; + } +#else + if (entry->d_type != DT_DIR) { + continue; + } +#endif + char* p = ASPL_MALLOC(ASPL_ACCESS(*path).value.string->length + strlen(entry->d_name) + 2); + strcat(p, entry->d_name); + paths = ASPL_REALLOC(paths, sizeof(ASPL_OBJECT_TYPE) * (pathCount + 1)); + paths[pathCount] = ASPL_STRING_LITERAL(p); + pathCount++; + } + closedir(dir); + return ASPL_LIST_LITERAL("list", 12, paths, pathCount); + } +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$directory$get_home_directory() +{ +#ifdef _WIN32 + char* home = getenv("USERPROFILE"); + if (home == NULL) + { + ASPL_PANIC("Could not get home directory"); + } + else + { + return ASPL_STRING_LITERAL(home); + } +#else + char* home = getenv("HOME"); + if (home == NULL) + { + ASPL_PANIC("Could not get home directory"); + } + else + { + return ASPL_STRING_LITERAL(home); + } +#endif +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$file$exists(ASPL_OBJECT_TYPE* path) +{ + char* pathStr = aspl_util_io$osify_path(*path); +#ifdef _WIN32 + DWORD dwAttrib = GetFileAttributes(pathStr); + return ASPL_BOOL_LITERAL(dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +#else + struct stat path_stat; + if (stat(pathStr, &path_stat) == 0 && S_ISREG(path_stat.st_mode)) { + return ASPL_TRUE(); + } + else { + return ASPL_FALSE(); + } +#endif +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$file$read_string(ASPL_OBJECT_TYPE* path) +{ + char* pathStr = aspl_util_io$osify_path(*path); + FILE* file = fopen(pathStr, "rb"); // rb because the length is in bytes (which is not necessarily the same as characters) + if (file) + { + fseek(file, 0, SEEK_END); + long length = ftell(file); + fseek(file, 0, SEEK_SET); + char* buffer = ASPL_MALLOC(length + 1); + fread(buffer, 1, length, file); + buffer[length] = '\0'; + fclose(file); + return ASPL_STRING_LITERAL(buffer); + } + else + { + ASPL_PANIC("Could not open file '%s'", ASPL_ACCESS(*path).value.string->str); + } +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$file$read_bytes(ASPL_OBJECT_TYPE* path) +{ + char* pathStr = aspl_util_io$osify_path(*path); + FILE* file = fopen(pathStr, "rb"); + if (file) + { + fseek(file, 0, SEEK_END); + long length = ftell(file); + fseek(file, 0, SEEK_SET); + char* buffer = ASPL_MALLOC(length); + fread(buffer, 1, length, file); + fclose(file); + ASPL_OBJECT_TYPE* bytes = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * length); + for (int i = 0; i < length; i++) + { + bytes[i] = ASPL_BYTE_LITERAL(buffer[i]); + } + return ASPL_LIST_LITERAL("list", 10, bytes, length); + } + else + { + ASPL_PANIC("Could not open file '%s'", ASPL_ACCESS(*path).value.string->str); + } +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$file$write_string(ASPL_OBJECT_TYPE* path, ASPL_OBJECT_TYPE* string) +{ + char* pathStr = aspl_util_io$osify_path(*path); + FILE* file = fopen(pathStr, "wb"); // wb because the length is in bytes (which is not necessarily the same as characters) if (file) + if (file) + { + fwrite(ASPL_ACCESS(*string).value.string->str, 1, ASPL_ACCESS(*string).value.string->length, file); + fclose(file); + return ASPL_TRUE(); + } + else + { + return ASPL_FALSE(); + } +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$file$write_bytes(ASPL_OBJECT_TYPE* path, ASPL_OBJECT_TYPE* bytes) +{ + char* pathStr = aspl_util_io$osify_path(*path); + ASPL_OBJECT_TYPE bytesObj = (ASPL_OBJECT_TYPE)*bytes; + FILE* file = fopen(pathStr, "wb"); + if (file) + { + for (int i = 0; i < ASPL_ACCESS(bytesObj).value.list->length; i++) + { + ASPL_OBJECT_TYPE byte = ASPL_ACCESS(bytesObj).value.list->value[i]; + fwrite(&ASPL_ACCESS(byte).value.integer8, 1, 1, file); + } + fclose(file); + return ASPL_TRUE(); + } + else + { + return ASPL_FALSE(); + } +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$path$get_file_name(ASPL_OBJECT_TYPE* path) +{ + char* pathStr = aspl_util_io$unixify_path(*path); // unixify here because the code only works with `/` + char* lastSlash = strrchr(pathStr, '/'); + if (lastSlash == NULL) + { + return ASPL_STRING_LITERAL(pathStr); + } + else + { + return ASPL_STRING_LITERAL(lastSlash + 1); + } +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$file$delete(ASPL_OBJECT_TYPE* path) +{ + char* pathStr = aspl_util_io$osify_path(*path); + int result = remove(pathStr); + if (result == 0) + { + return ASPL_TRUE(); + } + else + { + return ASPL_FALSE(); + } +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$file$list(ASPL_OBJECT_TYPE* path) +{ + char* pathStr = aspl_util_io$osify_path(*path); + DIR* dir = opendir(pathStr); + if (dir == NULL) + { + return ASPL_LIST_LITERAL("list", 12, 0, 0); + } + else + { + struct dirent* entry; + int pathCount = 0; + ASPL_OBJECT_TYPE* paths = NULL; + while ((entry = readdir(dir)) != NULL) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + { + continue; + } +#ifdef _WIN32 + char* fullPath = ASPL_MALLOC(strlen(pathStr) + strlen(entry->d_name) + 2); + strcpy(fullPath, pathStr); + strcat(fullPath, "/"); + strcat(fullPath, entry->d_name); + DWORD dwAttrib = GetFileAttributes(fullPath); + if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) { + continue; + } +#else + if (entry->d_type == DT_DIR) { + continue; + } +#endif + paths = ASPL_REALLOC(paths, sizeof(ASPL_OBJECT_TYPE) * (pathCount + 1)); + paths[pathCount] = ASPL_STRING_LITERAL(entry->d_name); + pathCount++; + } + closedir(dir); + return ASPL_LIST_LITERAL("list", 12, paths, pathCount); + } +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$glob$search(ASPL_OBJECT_TYPE* pattern) +{ + char* patternStr = aspl_util_io$osify_path(*pattern); +#ifdef _WIN32 + WIN32_FIND_DATA data; + HANDLE hFind = FindFirstFile(patternStr, &data); + if (hFind != INVALID_HANDLE_VALUE) + { + ASPL_OBJECT_TYPE* paths = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE)); + int pathCount = 0; + if (!strcmp(data.cFileName, ".") && !strcmp(data.cFileName, "..")) + { + int size = strlen(data.cFileName) + 1; + if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + size++; + } + char* path = ASPL_MALLOC(size); + strcpy(path, data.cFileName); + if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + path[strlen(data.cFileName)] = '/'; + } + path[size - 1] = '\0'; + paths[0] = ASPL_STRING_LITERAL(path); + pathCount++; + } + while (FindNextFile(hFind, &data)) + { + if (strcmp(data.cFileName, ".") == 0 || strcmp(data.cFileName, "..") == 0) + { + continue; + } + paths = ASPL_REALLOC(paths, sizeof(ASPL_OBJECT_TYPE) * (pathCount + 1)); + int size = strlen(data.cFileName) + 1; + if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + size++; + } + char* path = ASPL_MALLOC(size); + strcpy(path, data.cFileName); + if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + path[strlen(data.cFileName)] = '/'; + } + path[size - 1] = '\0'; + paths[pathCount] = ASPL_STRING_LITERAL(path); + pathCount++; + } + FindClose(hFind); + return ASPL_LIST_LITERAL("list", 12, paths, pathCount); + } + else + { + ASPL_OBJECT_TYPE* paths = ASPL_MALLOC(1); + return ASPL_LIST_LITERAL("list", 12, paths, 0); + } +#else +#ifdef __ANDROID__ + // TODO: Implement glob for Android + ASPL_OBJECT_TYPE* paths = ASPL_MALLOC(1); + return ASPL_LIST_LITERAL("list", 12, paths, 0); +#else + glob_t glob_result; + glob(patternStr, GLOB_TILDE, NULL, &glob_result); + ASPL_OBJECT_TYPE* paths = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * glob_result.gl_pathc); + for (int i = 0; i < glob_result.gl_pathc; i++) + { + char* path = glob_result.gl_pathv[i]; + struct stat path_stat; + stat(path, &path_stat); + if (S_ISDIR(path_stat.st_mode)) { + int size = strlen(path) + 2; + char* pathCopy = ASPL_MALLOC(size); + strcpy(pathCopy, path); + pathCopy[size - 2] = '/'; + pathCopy[size - 1] = '\0'; + paths[i] = ASPL_STRING_LITERAL(pathCopy); + } + else { + paths[i] = ASPL_STRING_LITERAL(path); + } + } + globfree(&glob_result); + return ASPL_LIST_LITERAL("list", 12, paths, glob_result.gl_pathc); +#endif +#endif +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$path$relative_to_absolute(ASPL_OBJECT_TYPE* path) +{ + char* pathStr = aspl_util_io$osify_path(*path); +#ifdef _WIN32 + char* absolutePath = ASPL_MALLOC(MAX_PATH); + GetFullPathName(pathStr, MAX_PATH, absolutePath, NULL); + return ASPL_STRING_LITERAL(absolutePath); +#else + char* absolutePath = realpath(pathStr, NULL); + if (absolutePath == NULL) + { + ASPL_PANIC("Could not resolve path '%s'", pathStr); + } + else + { + char* absPathCopy = ASPL_MALLOC(strlen(absolutePath) + 1); + strcpy(absPathCopy, absolutePath); + free(absolutePath); + return ASPL_STRING_LITERAL(absPathCopy); + } +#endif +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$path$get_current_executable_path() +{ +#ifdef _WIN32 + char* path = ASPL_MALLOC(MAX_PATH); + GetModuleFileName(NULL, path, MAX_PATH); + return ASPL_STRING_LITERAL(path); +#elif __APPLE__ + char result[PATH_MAX]; + pid_t pid = getpid(); + int ret = proc_pidpath(pid, result, sizeof(result)); + if (ret <= 0) { + ASPL_PANIC("os.executable() failed at calling proc_pidpath with pid %d: %d\n", pid, ret); + } + return ASPL_STRING_LITERAL(result); +#else + char path[PATH_MAX]; + ssize_t count = readlink("/proc/self/exe", path, sizeof(path)); + if (count == -1) { + ASPL_PANIC("os.executable() failed at calling readlink: %s", strerror(errno)); + } + path[count] = '\0'; + return ASPL_STRING_LITERAL(path); +#endif +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$path$copy(ASPL_OBJECT_TYPE* source, ASPL_OBJECT_TYPE* destination) { + char* sourceStr = aspl_util_io$osify_path(*source); + char* destinationStr = aspl_util_io$osify_path(*destination); +#ifdef _WIN32 + CopyFile(sourceStr, destinationStr, FALSE); +#else + FILE* sourceFile = fopen(sourceStr, "r"); + FILE* destinationFile = fopen(destinationStr, "w"); + if (sourceFile && destinationFile) + { + char buffer[4096]; + size_t bytes; + while ((bytes = fread(buffer, 1, sizeof(buffer), sourceFile)) != 0) + { + fwrite(buffer, 1, bytes, destinationFile); + } + } + fclose(sourceFile); + fclose(destinationFile); +#endif + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$path$move(ASPL_OBJECT_TYPE* source, ASPL_OBJECT_TYPE* destination) { + char* sourceStr = aspl_util_io$osify_path(*source); + char* destinationStr = aspl_util_io$osify_path(*destination); +#ifdef _WIN32 + MoveFile(sourceStr, destinationStr); +#else + rename(sourceStr, destinationStr); +#endif + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_io$create_symlink(ASPL_OBJECT_TYPE* source, ASPL_OBJECT_TYPE* destination) { + char* sourceStr = aspl_util_io$osify_path(*source); + char* destinationStr = aspl_util_io$osify_path(*destination); +#ifdef _WIN32 + CreateSymbolicLink(sourceStr, destinationStr, 0); +#else + symlink(sourceStr, destinationStr); +#endif + return ASPL_UNINITIALIZED; +} \ No newline at end of file diff --git a/stdlib/io/path.aspl b/stdlib/io/path.aspl new file mode 100644 index 0000000..940429f --- /dev/null +++ b/stdlib/io/path.aspl @@ -0,0 +1,33 @@ +[public] +function abs(string relative) returns string{ + return string(implement("io.path.relative_to_absolute", relative)) +} + +[public] +function join_path(list paths) returns string{ + var string path = "" + var int i = 0 + foreach(paths as p){ + path += p + if(i < paths.length - 1){ + path += "/" + } + i++ + } + return path +} + +[public] +function get_executable_path() returns string{ + return string(implement("io.path.get_current_executable_path")) +} + +[public] +function copy(string from, string to){ + implement("io.path.copy", from, to) +} + +[public] +function move(string from, string to){ + implement("io.path.move", from, to) +} \ No newline at end of file diff --git a/stdlib/io/symlink.aspl b/stdlib/io/symlink.aspl new file mode 100644 index 0000000..d13c93a --- /dev/null +++ b/stdlib/io/symlink.aspl @@ -0,0 +1,4 @@ +[public] +function create_symlink(string origin, string target){ + implement("io.create_symlink", origin, target) +} \ No newline at end of file diff --git a/stdlib/json/Token.aspl b/stdlib/json/Token.aspl new file mode 100644 index 0000000..2f5662b --- /dev/null +++ b/stdlib/json/Token.aspl @@ -0,0 +1,17 @@ +class Token { + + [readpublic] + property TokenType type + [readpublic] + property string value + [readpublic] + property int length + + [public] + method construct(TokenType type, string value, int length) { + this.type = type + this.value = value + this.length = length + } + +} \ No newline at end of file diff --git a/stdlib/json/TokenList.aspl b/stdlib/json/TokenList.aspl new file mode 100644 index 0000000..911f499 --- /dev/null +++ b/stdlib/json/TokenList.aspl @@ -0,0 +1,39 @@ +class TokenList{ + + property list* tokens + + [public] + method construct(list tokens){ + this.tokens = &tokens + } + + [public] + method peek(int amount = 0) returns Token{ + return (*tokens)[amount] + } + + [public] + method shift(int amount = 1){ + repeat(amount, i = 0){ + (*tokens).removeAt(i) + } + } + + [public] + method next(int offset = 0) returns Token{ + var Token token = (*tokens)[offset] + shift(offset + 1) + return token + } + + [public] + method count() returns int{ + return (*tokens).length + } + + [public] + method empty() returns bool{ + return count() == 0 + } + +} \ No newline at end of file diff --git a/stdlib/json/TokenType.aspl b/stdlib/json/TokenType.aspl new file mode 100644 index 0000000..21aa955 --- /dev/null +++ b/stdlib/json/TokenType.aspl @@ -0,0 +1,14 @@ +enum TokenType { + Null, + True, + False, + Integer, + Float, + String, + BracketOpen, + BracketClose, + BraceOpen, + BraceClose, + Colon, + Comma +} \ No newline at end of file diff --git a/stdlib/json/decoder.aspl b/stdlib/json/decoder.aspl new file mode 100644 index 0000000..dba3a7e --- /dev/null +++ b/stdlib/json/decoder.aspl @@ -0,0 +1,169 @@ +import console + +[public] +function error(string message){ + // TODO: Use proper error handling for this + print(console.red("json.decode: " + message)) + exit(1) +} + +[public] +function is_digit(string s) returns bool{ + return s[0] == "0" || s[0] == "1" || s[0] == "2" || s[0] == "3" || s[0] == "4" || s[0] == "5" || s[0] == "6" || s[0] == "7" || s[0] == "8" || s[0] == "9" +} + +function lex_token(string s) returns Token{ + var int trimLength = 0 + while(s[0] == " " || s[0] == "\n"){ + s = s.after(0) + trimLength++ + } + if(s.startsWith("null")){ + return new Token(TokenType.Null, "null", "null".length + trimLength) + }elseif(s.startsWith("true")){ + return new Token(TokenType.True, "true", "true".length + trimLength) + }elseif(s.startsWith("false")){ + return new Token(TokenType.False, "false", "false".length + trimLength) + }elseif(s.startsWith("-") || s.startsWith("+")){ + var result = s[0] + s = s.after(0) + repeat(s.length, i = 0){ + if(is_digit(s[i])){ + result += s[i] + }elseif(s[i] == "."){ + result += s[i] + }else{ + if(result.contains(".")){ + return new Token(TokenType.Float, result, result.length + trimLength) + }else{ + return new Token(TokenType.Integer, result, result.length + trimLength) + } + } + } + if(result.contains(".")){ + return new Token(TokenType.Float, result, result.length + trimLength) + }else{ + return new Token(TokenType.Integer, result, result.length + trimLength) + } + }elseif(is_digit(s[0])){ + var result = "" + repeat(s.length, i = 0){ + if(is_digit(s[i])){ + result += s[i] + }elseif(s[i] == "."){ + result += s[i] + }else{ + if(result.contains(".")){ + return new Token(TokenType.Float, result, result.length + trimLength) + }else{ + return new Token(TokenType.Integer, result, result.length + trimLength) + } + } + } + if(result.contains(".")){ + return new Token(TokenType.Float, result, result.length + trimLength) + }else{ + return new Token(TokenType.Integer, result, result.length + trimLength) + } + }elseif(s.startsWith("\"")){ + var result = "" + s = s.after(0) + var int i = 0 + while(i < s.length){ + i++ + if(s[i - 1] == "\\"){ + result += s[i - 1] + result += s[i] + i++ + }elseif(s[i - 1] == "\""){ + return new Token(TokenType.String, result, result.length + trimLength + 2) + }else{ + result += s[i - 1] + } + } + error("Unterminated string") + }elseif(s.startsWith("[")){ + return new Token(TokenType.BracketOpen, "[", "[".length + trimLength) + }elseif(s.startsWith("]")){ + return new Token(TokenType.BracketClose, "]", "]".length + trimLength) + }elseif(s.startsWith("{")){ + return new Token(TokenType.BraceOpen, "{", "{".length + trimLength) + }elseif(s.startsWith("}")){ + return new Token(TokenType.BraceClose, "}", "}".length + trimLength) + }elseif(s.startsWith(":")){ + return new Token(TokenType.Colon, ":", ":".length + trimLength) + }elseif(s.startsWith(",")){ + return new Token(TokenType.Comma, ",", ",".length + trimLength) + } + error("Unexpected character: " + s[0]) +} + +function lex(string data) returns list{ + var list tokens = [] + while(data.length > 0){ + var Token token = lex_token(data) + tokens.add(token) + data = data.after(token.length - 1) + } + return tokens +} + +[public] +function decode_token(TokenList tokens) returns any{ + while(!tokens.empty()){ + if(tokens.peek().type == TokenType.Null){ + tokens.shift() + return null + }elseif(tokens.peek().type == TokenType.True){ + tokens.shift() + return true + }elseif(tokens.peek().type == TokenType.False){ + tokens.shift() + return false + }elseif(tokens.peek().type == TokenType.Integer){ + var int value = int(tokens.peek().value) + tokens.shift() + return value + }elseif(tokens.peek().type == TokenType.Float){ + var float value = float(tokens.peek().value) + tokens.shift() + return value + }elseif(tokens.peek().type == TokenType.String){ + var string value = tokens.peek().value + tokens.shift() + return value + }elseif(tokens.peek().type == TokenType.BracketOpen){ + tokens.shift() + var list result = [] + while(tokens.peek().type != TokenType.BracketClose){ + result.add(decode_token(tokens)) + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + } + } + tokens.shift() + return result + }elseif(tokens.peek().type == TokenType.BraceOpen){ + tokens.shift() + var map result = {} + while(tokens.peek().type != TokenType.BraceClose){ + var string key = string(decode_token(tokens)) + tokens.shift() + result[key] = decode_token(tokens) + if(tokens.peek().type == TokenType.Comma){ + tokens.shift() + } + } + tokens.shift() + return result + }else{ + error("Unexpected token: " + tokens.peek().type) + } + } +} + +[public] +function decode(string data) returns any{ + var tokens = lex(data) + return decode_token(new TokenList(tokens)) +} \ No newline at end of file diff --git a/stdlib/json/encoder.aspl b/stdlib/json/encoder.aspl new file mode 100644 index 0000000..a441e9b --- /dev/null +++ b/stdlib/json/encoder.aspl @@ -0,0 +1,82 @@ +import console + +[public] +function encode(any data, bool prettyPrint = false) returns string?{ + if(data == null){ + return "null" + } + elseif(data oftype bool){ + if(bool(data)){ + return "true" + } + else{ + return "false" + } + } + elseif(data oftype byte){ + return string(data) + } + elseif(data oftype int){ + return string(data) + } + elseif(data oftype long){ + return string(data) + } + elseif(data oftype float){ + if(!string(data).contains(".")){ + return string(data) + ".0" // to mark it as a float + } + else{ + return string(data) + } + } + elseif(data oftype double){ + if(!string(data).contains(".")){ + return string(data) + ".0" // to mark it as a double + } + else{ + return string(data) + } + } + elseif(data oftype string){ + return "\"" + data + "\"" + } + elseif(data oftype list){ + var result = "[" + foreach(list(data) as element){ + result += encode(element) + "," + } + if(list(data).length > 0){ + result = result.before(result.length - 1) + } + result += "]" + return result + } + elseif(data oftype map){ + var result = "{" + if(prettyPrint){ + result + "\n" + } + foreach(map(data) as key => value){ + if(prettyPrint){ + result += "\t" + encode(key) + " :" + encode(value) + ",\n" + }else{ + result += encode(key) + ":" + encode(value) + "," + } + } + if(map(data).length > 0){ + if(prettyPrint){ + result = result.before(result.length - 2) // remove the last comma and newline + }else{ + result = result.before(result.length - 1) // remove the last comma + } + if(prettyPrint){ + result += "\n" + } + } + result += "}" + return result + } + print(console.red("json.encode: Unsupported type of expression: " + data)) + exit(1) +} \ No newline at end of file diff --git a/stdlib/json/main.aspl b/stdlib/json/main.aspl new file mode 100644 index 0000000..0e64c27 --- /dev/null +++ b/stdlib/json/main.aspl @@ -0,0 +1,9 @@ +$if main{ + assert decode(encode(null)) == null + assert decode(encode(101)) == 101 + assert decode(encode(101f)) == 101f + assert decode(encode("Hello")) == "Hello" + assert decode(encode(list[])) == list[] + assert decode(encode(list["42"])) == ["42"] + assert decode(encode(list["42", "43", 0, -12, "Hello", null])) == list["42", "43", 0, -12, "Hello", null] +} \ No newline at end of file diff --git a/stdlib/math/abs.aspl b/stdlib/math/abs.aspl new file mode 100644 index 0000000..1deef3c --- /dev/null +++ b/stdlib/math/abs.aspl @@ -0,0 +1,8 @@ +[public] +function abs(double x) returns double{ + if(x > 0){ + return x + }else{ + return double(-x) + } +} \ No newline at end of file diff --git a/stdlib/math/angular_units.aspl b/stdlib/math/angular_units.aspl new file mode 100644 index 0000000..8b8da92 --- /dev/null +++ b/stdlib/math/angular_units.aspl @@ -0,0 +1,9 @@ +[public] +function radians(double degrees) returns double{ + return double(degrees / 180 * pi()) +} + +[public] +function degrees(double radians) returns double{ + return double(radians / pi() * 180) +} \ No newline at end of file diff --git a/stdlib/math/comparison.aspl b/stdlib/math/comparison.aspl new file mode 100644 index 0000000..a399f25 --- /dev/null +++ b/stdlib/math/comparison.aspl @@ -0,0 +1,15 @@ +[public] +function min(double a, double b) returns double{ + if(a < b){ + return a + } + return b +} + +[public] +function max(double a, double b) returns double{ + if(a > b){ + return a + } + return b +} diff --git a/stdlib/math/constants.aspl b/stdlib/math/constants.aspl new file mode 100644 index 0000000..b658274 --- /dev/null +++ b/stdlib/math/constants.aspl @@ -0,0 +1,9 @@ +[public] +function pi() returns double{ + return 3.141592653589793d +} + +[public] +function e() returns double{ + return 2.718281828459045d +} \ No newline at end of file diff --git a/stdlib/math/geometry/Ellipse.aspl b/stdlib/math/geometry/Ellipse.aspl new file mode 100644 index 0000000..92a260d --- /dev/null +++ b/stdlib/math/geometry/Ellipse.aspl @@ -0,0 +1,26 @@ +import math + +[public] +class Ellipse{ + + [readpublic] + property Point position + [readpublic] + property Size size + + [public] + method construct(Point position, Size size){ + this.position = position + this.size = size + } + + [public] + method containsPoint(Point point) returns bool{ + var x = point.x - this.position.x + var y = point.y - this.position.y + var a = this.size.width / 2 + var b = this.size.height / 2 + return (x * x) / (a * a) + (y * y) / (b * b) <= 1 + } + +} \ No newline at end of file diff --git a/stdlib/math/geometry/Point.aspl b/stdlib/math/geometry/Point.aspl new file mode 100644 index 0000000..45b226f --- /dev/null +++ b/stdlib/math/geometry/Point.aspl @@ -0,0 +1,15 @@ +[public] +class Point{ + + [readpublic] + property float x + [readpublic] + property float y + + [public] + method construct(float x, float y){ + this.x = x + this.y = y + } + +} \ No newline at end of file diff --git a/stdlib/math/geometry/Rectangle.aspl b/stdlib/math/geometry/Rectangle.aspl new file mode 100644 index 0000000..e3aa938 --- /dev/null +++ b/stdlib/math/geometry/Rectangle.aspl @@ -0,0 +1,22 @@ +import math + +[public] +class Rectangle{ + + [readpublic] + property Point position + [readpublic] + property Size size + + [public] + method construct(Point position, Size size){ + this.position = position + this.size = size + } + + [public] + method containsPoint(Point point) returns bool{ + return point.x >= position.x && point.x <= position.x + size.width && point.y >= position.y && point.y <= position.y + size.height + } + +} \ No newline at end of file diff --git a/stdlib/math/geometry/Size.aspl b/stdlib/math/geometry/Size.aspl new file mode 100644 index 0000000..9dc9f8d --- /dev/null +++ b/stdlib/math/geometry/Size.aspl @@ -0,0 +1,15 @@ +[public] +class Size{ + + [readpublic] + property float width + [readpublic] + property float height + + [public] + method construct(float width, float height){ + this.width = width + this.height = height + } + +} diff --git a/stdlib/math/implementations/implementations.aspl b/stdlib/math/implementations/implementations.aspl new file mode 100644 index 0000000..74ecfe5 --- /dev/null +++ b/stdlib/math/implementations/implementations.aspl @@ -0,0 +1,3 @@ +$if !twailbackend{ + $include("implementations.c") +} \ No newline at end of file diff --git a/stdlib/math/implementations/implementations.c b/stdlib/math/implementations/implementations.c new file mode 100644 index 0000000..61d8d8e --- /dev/null +++ b/stdlib/math/implementations/implementations.c @@ -0,0 +1,48 @@ +#include + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_math$pow(ASPL_OBJECT_TYPE* base, ASPL_OBJECT_TYPE* exponent) { + ASPL_OBJECT_TYPE baseObj = (ASPL_OBJECT_TYPE)*base; + ASPL_OBJECT_TYPE exponentObj = (ASPL_OBJECT_TYPE)*exponent; + return ASPL_DOUBLE_LITERAL(pow(ASPL_ACCESS(baseObj).value.float64, ASPL_ACCESS(exponentObj).value.float64)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_math$floor(ASPL_OBJECT_TYPE* value) { + ASPL_OBJECT_TYPE valueObj = (ASPL_OBJECT_TYPE)*value; + return ASPL_DOUBLE_LITERAL(floor(ASPL_ACCESS(valueObj).value.float64)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_math$round(ASPL_OBJECT_TYPE* value, ASPL_OBJECT_TYPE* digits) { + ASPL_OBJECT_TYPE valueObj = (ASPL_OBJECT_TYPE)*value; + ASPL_OBJECT_TYPE digitsObj = (ASPL_OBJECT_TYPE)*digits; + return ASPL_DOUBLE_LITERAL(round(ASPL_ACCESS(valueObj).value.float64 * pow(10, ASPL_ACCESS(digitsObj).value.integer32)) / pow(10, ASPL_ACCESS(digitsObj).value.integer32)); // TODO: Check this +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_math$sin(ASPL_OBJECT_TYPE* value) { + ASPL_OBJECT_TYPE valueObj = (ASPL_OBJECT_TYPE)*value; + return ASPL_DOUBLE_LITERAL(sin(ASPL_ACCESS(valueObj).value.float64)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_math$asin(ASPL_OBJECT_TYPE* value) { + ASPL_OBJECT_TYPE valueObj = (ASPL_OBJECT_TYPE)*value; + return ASPL_DOUBLE_LITERAL(asin(ASPL_ACCESS(valueObj).value.float64)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_math$cos(ASPL_OBJECT_TYPE* value) { + ASPL_OBJECT_TYPE valueObj = (ASPL_OBJECT_TYPE)*value; + return ASPL_DOUBLE_LITERAL(cos(ASPL_ACCESS(valueObj).value.float64)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_math$acos(ASPL_OBJECT_TYPE* value) { + ASPL_OBJECT_TYPE valueObj = (ASPL_OBJECT_TYPE)*value; + return ASPL_DOUBLE_LITERAL(acos(ASPL_ACCESS(valueObj).value.float64)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_math$tan(ASPL_OBJECT_TYPE* value) { + ASPL_OBJECT_TYPE valueObj = (ASPL_OBJECT_TYPE)*value; + return ASPL_DOUBLE_LITERAL(tan(ASPL_ACCESS(valueObj).value.float64)); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_math$atan(ASPL_OBJECT_TYPE* value) { + ASPL_OBJECT_TYPE valueObj = (ASPL_OBJECT_TYPE)*value; + return ASPL_DOUBLE_LITERAL(atan(ASPL_ACCESS(valueObj).value.float64)); +} \ No newline at end of file diff --git a/stdlib/math/pow.aspl b/stdlib/math/pow.aspl new file mode 100644 index 0000000..ac1eabd --- /dev/null +++ b/stdlib/math/pow.aspl @@ -0,0 +1,14 @@ +[public] +function pow(double base, double exponent) returns double{ + return double(implement("math.pow", base, exponent)) +} + +[public] +function root(double x, double n) returns double{ + return pow(x, 1d / n) +} + +[public] +function sqrt(double x) returns double{ + return root(x, 2d) +} \ No newline at end of file diff --git a/stdlib/math/round.aspl b/stdlib/math/round.aspl new file mode 100644 index 0000000..213c525 --- /dev/null +++ b/stdlib/math/round.aspl @@ -0,0 +1,14 @@ +[public] +function floor(double x) returns double{ + return double(implement("math.floor", x)) +} + +[public] +function ceil(double x) returns double{ + return double(-floor(-x)) +} + +[public] +function round(double x, int d = 0) returns double{ + return double(implement("math.round", x, d)) +} \ No newline at end of file diff --git a/stdlib/math/trigonometry.aspl b/stdlib/math/trigonometry.aspl new file mode 100644 index 0000000..1c5cb06 --- /dev/null +++ b/stdlib/math/trigonometry.aspl @@ -0,0 +1,29 @@ +[public] +function sin(double x) returns double{ + return double(implement("math.sin", x)) +} + +[public] +function asin(double x) returns double{ + return double(implement("math.asin", x)) +} + +[public] +function cos(double x) returns double{ + return double(implement("math.cos", x)) +} + +[public] +function acos(double x) returns double{ + return double(implement("math.acos", x)) +} + +[public] +function tan(double x) returns double{ + return double(implement("math.tan", x)) +} + +[public] +function atan(double x) returns double{ + return double(implement("math.atan", x)) +} \ No newline at end of file diff --git a/stdlib/os/Architecture.aspl b/stdlib/os/Architecture.aspl new file mode 100644 index 0000000..326f627 --- /dev/null +++ b/stdlib/os/Architecture.aspl @@ -0,0 +1,29 @@ +[public] +enum Architecture{ + amd64, // aka x86_64 + arm64, // 64-bit arm + arm32, // 32-bit arm + rv64, // 64-bit risc-v + rv32, // 32-bit risc-v + i386 // aka x86_32 +} + +[public] +function architecture_from_string(string arch) returns Architecture? { + arch = arch.toLower() + if(arch == "amd64" || arch == "x86_64" || arch == "x64") { + return Architecture.amd64 + }elseif(arch == "arm64" || arch == "aarch64") { + return Architecture.arm64 + }elseif(arch == "arm32" || arch == "aarch32") { + return Architecture.arm32 + }elseif(arch == "rv64" || arch == "riscv64" || arch == "risc-v64") { + return Architecture.rv64 + }elseif(arch == "rv32" || arch == "riscv32" || arch == "risc-v32") { + return Architecture.rv32 + }elseif(arch == "i386" || arch == "x86_32"|| arch == "x86" || arch == "x32" || arch == "ia-32" || arch == "ia32") { + return Architecture.i386 + }else{ + return null + } +} \ No newline at end of file diff --git a/stdlib/os/CommandResult.aspl b/stdlib/os/CommandResult.aspl new file mode 100644 index 0000000..e7dc076 --- /dev/null +++ b/stdlib/os/CommandResult.aspl @@ -0,0 +1,15 @@ +[public] +class CommandResult { + + [readpublic] + property int exitCode + [readpublic] + property string output + + [public] + method construct(int exitCode, string output) { + this.exitCode = exitCode + this.output = output + } + +} \ No newline at end of file diff --git a/stdlib/os/args.aspl b/stdlib/os/args.aspl new file mode 100644 index 0000000..d9ac598 --- /dev/null +++ b/stdlib/os/args.aspl @@ -0,0 +1,6 @@ +// args returns the cli arguments passed to the currently running process +// note: the first argument is usually the path to the executable +[public] +function args() returns list{ + return list(implement("os.get_current_program_arguments")) +} \ No newline at end of file diff --git a/stdlib/os/commands.aspl b/stdlib/os/commands.aspl new file mode 100644 index 0000000..93f2043 --- /dev/null +++ b/stdlib/os/commands.aspl @@ -0,0 +1,19 @@ +// execute executes a program and returns the exit code and the output +[public] +function execute(string command) returns CommandResult{ + var result = list(implement("os.command.execute", command)) + return new CommandResult(int(result[0]), string(result[1])) +} + +// system executes a program (like os.execute()), but only returns the exit code +[public] +function system(string command) returns int{ + return int(implement("os.command.system", command)) +} + +// execvp executes a program in place of the current process +// Note: this function does not return if successful +[public] +function execvp(string command, list args){ + implement("os.command.execvp", command, args) +} \ No newline at end of file diff --git a/stdlib/os/fs.aspl b/stdlib/os/fs.aspl new file mode 100644 index 0000000..0e6750b --- /dev/null +++ b/stdlib/os/fs.aspl @@ -0,0 +1,17 @@ +// getwd returns the current working directory +[public] +function getwd() returns string{ + return string(implement("os.get_working_directory")) +} + +// chdir changes the current working directory to the specified path +[public] +function chdir(string path){ + implement("os.change_working_directory", path) +} + +// chmod changes the mode of the specified file to the specified mode +[public] +function chmod(string path, int mode){ + implement("os.change_mode", path, mode) +} \ No newline at end of file diff --git a/stdlib/os/implementations/implementations.aspl b/stdlib/os/implementations/implementations.aspl new file mode 100644 index 0000000..74ecfe5 --- /dev/null +++ b/stdlib/os/implementations/implementations.aspl @@ -0,0 +1,3 @@ +$if !twailbackend{ + $include("implementations.c") +} \ No newline at end of file diff --git a/stdlib/os/implementations/implementations.c b/stdlib/os/implementations/implementations.c new file mode 100644 index 0000000..38d5908 --- /dev/null +++ b/stdlib/os/implementations/implementations.c @@ -0,0 +1,173 @@ +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#include +#endif + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_os$get_current_program_arguments() +{ + int argc = aspl_argc; + char** argv = aspl_argv; + ASPL_OBJECT_TYPE* args = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * argc); + for (int i = 0; i < argc; i++) + { + args[i] = ASPL_STRING_LITERAL(argv[i]); + } + return ASPL_LIST_LITERAL("list", 11, args, argc); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_os$command$execute(ASPL_OBJECT_TYPE* command) +{ + ASPL_OBJECT_TYPE commandObj = (ASPL_OBJECT_TYPE)*command; +#ifdef _WIN32 + FILE* pipe = _popen(ASPL_ACCESS(commandObj).value.string->str, "r"); +#else + FILE* pipe = popen(ASPL_ACCESS(commandObj).value.string->str, "r"); +#endif + if (pipe == NULL) + { + ASPL_PANIC("Failed to execute command '%s'", ASPL_ACCESS(commandObj).value.string->str); + } + else + { + char* output = ASPL_MALLOC(1); + int outputLength = 0; + char buffer[4096]; + while (fgets(buffer, sizeof(buffer), pipe) != NULL) + { + output = ASPL_REALLOC(output, outputLength + strlen(buffer) + 1); + strcpy(output + outputLength, buffer); + outputLength += strlen(buffer); + } +#if defined(_WIN32) + int exitCode = _pclose(pipe); +#else + int exitCode = pclose(pipe); +#endif + ASPL_OBJECT_TYPE* result = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 2); + result[0] = ASPL_INT_LITERAL(exitCode); + result[1] = ASPL_STRING_LITERAL(output); + return ASPL_LIST_LITERAL("list", 10, result, 2); + } +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_os$command$system(ASPL_OBJECT_TYPE* command) +{ + ASPL_OBJECT_TYPE commandObj = (ASPL_OBJECT_TYPE)*command; + int exitCode = system(ASPL_ACCESS(commandObj).value.string->str); + return ASPL_INT_LITERAL(exitCode); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_os$command$execvp(ASPL_OBJECT_TYPE* command, ASPL_OBJECT_TYPE* args) +{ + ASPL_OBJECT_TYPE commandObj = (ASPL_OBJECT_TYPE)*command; + ASPL_OBJECT_TYPE argsObj = (ASPL_OBJECT_TYPE)*args; + const char* commandStr = ASPL_ACCESS(commandObj).value.string->str; + int numArgs = ASPL_ACCESS(argsObj).value.list->length - 1; + const char** argsv = (const char**)ASPL_MALLOC(sizeof(const char*) * (numArgs + 2)); + + argsv[0] = commandStr; + + for (int i = 1; i <= numArgs; i++) + { + argsv[i] = ASPL_ACCESS(ASPL_ACCESS(argsObj).value.list->value[i]).value.string->str; + } + + argsv[numArgs + 1] = NULL; // The last element of argsv should be NULL. + +#ifdef _WIN32 + _execvp(commandStr, argsv); +#else + execvp(commandStr, (char* const*)argsv); +#endif + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_os$get_working_directory() +{ +#ifdef _WIN32 + char* path = ASPL_MALLOC(MAX_PATH); + GetCurrentDirectory(MAX_PATH, path); + return ASPL_STRING_LITERAL(path); +#else + char* path = ASPL_MALLOC(PATH_MAX); + getcwd(path, PATH_MAX); + return ASPL_STRING_LITERAL(path); +#endif +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_os$change_working_directory(ASPL_OBJECT_TYPE* path) +{ + ASPL_OBJECT_TYPE pathObj = (ASPL_OBJECT_TYPE)*path; +#ifdef _WIN32 + SetCurrentDirectory(ASPL_ACCESS(pathObj).value.string->str); +#else + chdir(ASPL_ACCESS(pathObj).value.string->str); +#endif + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_os$change_mode(ASPL_OBJECT_TYPE* path, ASPL_OBJECT_TYPE* mode) +{ + ASPL_OBJECT_TYPE pathObj = (ASPL_OBJECT_TYPE)*path; + ASPL_OBJECT_TYPE modeObj = (ASPL_OBJECT_TYPE)*mode; +#ifdef _WIN32 + // no equivalent on Windows +#else + chmod(ASPL_ACCESS(pathObj).value.string->str, ASPL_ACCESS(modeObj).value.integer32); +#endif + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_os$get_current_runtime_os_name() +{ + // TODO +#ifdef _WIN32 + return ASPL_STRING_LITERAL("windows"); +#elif __APPLE__ + return ASPL_STRING_LITERAL("macos"); +#elif __linux__ + return ASPL_STRING_LITERAL("linux"); +#elif __unix__ + return ASPL_STRING_LITERAL("unix"); +#else + return ASPL_STRING_LITERAL("unknown"); +#endif +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_os$get_current_runtime_architecture_name() +{ +#ifdef _WIN32 + return ASPL_STRING_LITERAL(getenv("PROCESSOR_ARCHITECTURE")); +#else + struct utsname* d = ASPL_MALLOC(sizeof(struct utsname)); + uname(d); + return ASPL_STRING_LITERAL(d->machine); +#endif +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_os$get_current_runtime_architecture_enum() +{ + // TODO +#if defined(__x86_64__) || defined(_M_X64) + return ASPL_INT_LITERAL(0); +#elif defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86) + return ASPL_INT_LITERAL(5); +#elif arm64 + return ASPL_INT_LITERAL(2); +#elif arm32 + return ASPL_INT_LITERAL(3); +#elif rv64 + return ASPL_INT_LITERAL(4); +#elif rv32 + return ASPL_INT_LITERAL(5); +#elif i386 + return ASPL_INT_LITERAL(6); +#else + return ASPL_INT_LITERAL(-1); +#endif +} \ No newline at end of file diff --git a/stdlib/os/os.aspl b/stdlib/os/os.aspl new file mode 100644 index 0000000..7f8450a --- /dev/null +++ b/stdlib/os/os.aspl @@ -0,0 +1,17 @@ +// user_os returns the name of the operating system currently used to execute this program +[public] +function user_os() returns string{ + return string(implement("os.get_current_runtime_os_name")) +} + +// user_architecture returns the name of the architecture currently used to execute this program +[public] +function user_architecture() returns string{ + return string(implement("os.get_current_runtime_architecture_name")) +} + +// user_architecture_generic returns a value from the Architecture enum and is more generic than os.user_architecture() +[public] +function user_architecture_generic() returns Architecture{ + return Architecture(int(implement("os.get_current_runtime_architecture_enum"))) +} \ No newline at end of file diff --git a/stdlib/rand/implementations/implementations.aspl b/stdlib/rand/implementations/implementations.aspl new file mode 100644 index 0000000..74ecfe5 --- /dev/null +++ b/stdlib/rand/implementations/implementations.aspl @@ -0,0 +1,3 @@ +$if !twailbackend{ + $include("implementations.c") +} \ No newline at end of file diff --git a/stdlib/rand/implementations/implementations.c b/stdlib/rand/implementations/implementations.c new file mode 100644 index 0000000..d911a7e --- /dev/null +++ b/stdlib/rand/implementations/implementations.c @@ -0,0 +1,48 @@ +#include +#include + +int aspl_util_rand$generate_int() { + static char inited = 0; + if (!inited) { + srand(time(NULL)); + inited = 1; + } + return rand(); +} + +// TODO: These functions are not uniform and can only generate numbers up to the signed 32-bit integer limit + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_random$range$byte(ASPL_OBJECT_TYPE* min, ASPL_OBJECT_TYPE* max) +{ + ASPL_OBJECT_TYPE minObj = (ASPL_OBJECT_TYPE)*min; + ASPL_OBJECT_TYPE maxObj = (ASPL_OBJECT_TYPE)*max; + return ASPL_INT_LITERAL(aspl_util_rand$generate_int() % (ASPL_ACCESS(maxObj).value.integer8 - ASPL_ACCESS(minObj).value.integer8) + ASPL_ACCESS(minObj).value.integer8); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_random$range$int(ASPL_OBJECT_TYPE* min, ASPL_OBJECT_TYPE* max) +{ + ASPL_OBJECT_TYPE minObj = (ASPL_OBJECT_TYPE)*min; + ASPL_OBJECT_TYPE maxObj = (ASPL_OBJECT_TYPE)*max; + return ASPL_INT_LITERAL(aspl_util_rand$generate_int() % (ASPL_ACCESS(maxObj).value.integer32 - ASPL_ACCESS(minObj).value.integer32) + ASPL_ACCESS(minObj).value.integer32); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_random$range$long(ASPL_OBJECT_TYPE* min, ASPL_OBJECT_TYPE* max) +{ + ASPL_OBJECT_TYPE minObj = (ASPL_OBJECT_TYPE)*min; + ASPL_OBJECT_TYPE maxObj = (ASPL_OBJECT_TYPE)*max; + return ASPL_LONG_LITERAL(aspl_util_rand$generate_int() % (ASPL_ACCESS(maxObj).value.integer64 - ASPL_ACCESS(minObj).value.integer64) + ASPL_ACCESS(minObj).value.integer64); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_random$range$float(ASPL_OBJECT_TYPE* min, ASPL_OBJECT_TYPE* max) +{ + ASPL_OBJECT_TYPE minObj = (ASPL_OBJECT_TYPE)*min; + ASPL_OBJECT_TYPE maxObj = (ASPL_OBJECT_TYPE)*max; + return ASPL_FLOAT_LITERAL((float)aspl_util_rand$generate_int() / ((float)RAND_MAX / (ASPL_ACCESS(maxObj).value.float32 - ASPL_ACCESS(minObj).value.float32)) + ASPL_ACCESS(minObj).value.float32); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_random$range$double(ASPL_OBJECT_TYPE* min, ASPL_OBJECT_TYPE* max) +{ + ASPL_OBJECT_TYPE minObj = (ASPL_OBJECT_TYPE)*min; + ASPL_OBJECT_TYPE maxObj = (ASPL_OBJECT_TYPE)*max; + return ASPL_DOUBLE_LITERAL((double)aspl_util_rand$generate_int() / ((double)RAND_MAX / (ASPL_ACCESS(maxObj).value.float64 - ASPL_ACCESS(minObj).value.float64)) + ASPL_ACCESS(minObj).value.float64); +} \ No newline at end of file diff --git a/stdlib/rand/main.aspl b/stdlib/rand/main.aspl new file mode 100644 index 0000000..460bf66 --- /dev/null +++ b/stdlib/rand/main.aspl @@ -0,0 +1,34 @@ +[public] +function brange(byte min, byte max) returns byte{ + return byte(implement("random.range.byte", min, max)) +} + +[public] +function irange(int min, int max) returns int{ + return int(implement("random.range.int", min, max)) +} + +[public] +function lrange(long min, long max) returns long{ + return long(implement("random.range.long", min, max)) +} + +[public] +function frange(float min, float max) returns float{ + return float(implement("random.range.float", min, max)) +} + +[public] +function drange(double min, double max) returns double{ + return double(implement("random.range.double", min, max)) +} + +[public] +function byte() returns byte{ + return brange(0b, 255b) +} + +[public] +function float() returns float{ + return frange(0f, 1f) +} \ No newline at end of file diff --git a/stdlib/regex/Match.aspl b/stdlib/regex/Match.aspl new file mode 100644 index 0000000..0dc1753 --- /dev/null +++ b/stdlib/regex/Match.aspl @@ -0,0 +1,18 @@ +[public] +class Match{ + + [readpublic] + property int start + [readpublic] + property int end + [readpublic] + property string value + + [public] + method construct(int start, int end, string value){ + this.start = start + this.end = end + this.value = value + } + +} \ No newline at end of file diff --git a/stdlib/regex/implementations/implementations.aspl b/stdlib/regex/implementations/implementations.aspl new file mode 100644 index 0000000..74ecfe5 --- /dev/null +++ b/stdlib/regex/implementations/implementations.aspl @@ -0,0 +1,3 @@ +$if !twailbackend{ + $include("implementations.c") +} \ No newline at end of file diff --git a/stdlib/regex/implementations/implementations.c b/stdlib/regex/implementations/implementations.c new file mode 100644 index 0000000..a3d920a --- /dev/null +++ b/stdlib/regex/implementations/implementations.c @@ -0,0 +1,55 @@ +#define REGEXP_MALLOC ASPL_MALLOC +#define REGEXP_FREE ASPL_FREE +#define accept regexp_accept +#include "thirdparty/regexp/regexp.c" +#undef accept + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_regex$match(ASPL_OBJECT_TYPE* regex, ASPL_OBJECT_TYPE* string) +{ + const char* error; + Reprog* p = regcomp(ASPL_ACCESS(*regex).value.string->str, 0, &error); + // TODO: Handle errors + Resub m; + if (!regexec(p, ASPL_ACCESS(*string).value.string->str, &m, 0)) + { + for (int i = 0; i < m.nsub; ++i) + { + int n = m.sub[i].ep - m.sub[i].sp; + char* s = ASPL_MALLOC(n + 1); + memcpy(s, m.sub[i].sp, n); + s[n] = '\0'; + ASPL_OBJECT_TYPE* list = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 3); + list[0] = ASPL_INT_LITERAL(m.sub[i].sp - ASPL_ACCESS(*string).value.string->str); + list[1] = ASPL_INT_LITERAL(m.sub[i].ep - ASPL_ACCESS(*string).value.string->str); + list[2] = ASPL_STRING_LITERAL(s); + ASPL_OBJECT_TYPE result = ASPL_LIST_LITERAL("list", 20, list, 3); + ASPL_FREE(list); + return result; + } + } + return ASPL_NULL(); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_regex$find_first(ASPL_OBJECT_TYPE* regex, ASPL_OBJECT_TYPE* string) +{ + // TODO + ASPL_PANIC("Unimplemented"); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_regex$replace_first(ASPL_OBJECT_TYPE* regex, ASPL_OBJECT_TYPE* replacement, ASPL_OBJECT_TYPE* string) +{ + // TODO + ASPL_PANIC("Unimplemented"); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_regex$find_all(ASPL_OBJECT_TYPE* regex, ASPL_OBJECT_TYPE* string) +{ + // TODO + ASPL_PANIC("Unimplemented"); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_regex$replace_all(ASPL_OBJECT_TYPE* regex, ASPL_OBJECT_TYPE* replacement, ASPL_OBJECT_TYPE* string) +{ + // TODO + ASPL_PANIC("Unimplemented"); +} \ No newline at end of file diff --git a/stdlib/regex/regex.aspl b/stdlib/regex/regex.aspl new file mode 100644 index 0000000..f615193 --- /dev/null +++ b/stdlib/regex/regex.aspl @@ -0,0 +1,39 @@ +[public] +function find_first(string pattern, string haystack) returns Match?{ + var data = implement("regex.find_first", pattern, haystack) + if(data == null){ + return null + } + var list match = list(data) + return new Match(int(match[0]), int(match[1]), string(match[2])) +} + +[public] +function match_string(string pattern, string haystack) returns Match?{ + var data = implement("regex.match", pattern, haystack) + if(data == null){ + return null + } + var list match = list(data) + return new Match(int(match[0]), int(match[1]), string(match[2])) +} + +[public] +function replace_first(string pattern, string replace, string haystack) returns string{ + return string(implement("regex.replace_first", pattern, replace, haystack)) +} + +[public] +function find_all(string pattern, string haystack) returns list{ + var list> data = list>(implement("regex.find_all", pattern, haystack)) + var list matches = [] + foreach(data as match){ + matches.add(new Match(int(match[0]), int(match[1]), string(match[2]))) + } + return matches +} + +[public] +function replace_all(string pattern, string replace, string haystack) returns string{ + return string(implement("regex.replace_all", pattern, replace, haystack)) +} \ No newline at end of file diff --git a/stdlib/strings/StringBuilder.aspl b/stdlib/strings/StringBuilder.aspl new file mode 100644 index 0000000..b4ea51e --- /dev/null +++ b/stdlib/strings/StringBuilder.aspl @@ -0,0 +1,48 @@ +// StringBuilder is used to construct large strings by concatenating efficiently +[public] +class StringBuilder { + + property any handle + [public] + property int length{ + get{ + return int(implement("strings.string_builder.get_length", handle)) + } + } + + [public] + method construct(string str = ""){ + handle = implement("strings.string_builder.new") + if(str != ""){ + append(str) + } + } + + // append writes the given string or string builder to the end of this string builder + // it returns this string builder to allow chaining + [public] + method append(string|StringBuilder str) returns self{ + if(str oftype string){ + implement("strings.string_builder.append_string", handle, str) + }else{ + implement("strings.string_builder.append_string_builder", handle, StringBuilder(str).handle) + } + return this + } + + // toString returns the constructed string + // NOTE: this does not empty the string builder; see toStringFlush for that + [public] + method toString() returns string{ + var str = string(implement("strings.string_builder.to_string_flush", handle)) + append(str) + return str + } + + // toStringFlush returns the constructed string and empties the string builder + [public] + method toStringFlush() returns string{ + return string(implement("strings.string_builder.to_string_flush", handle)) + } + +} \ No newline at end of file diff --git a/stdlib/strings/implementations/implementations.aspl b/stdlib/strings/implementations/implementations.aspl new file mode 100644 index 0000000..74ecfe5 --- /dev/null +++ b/stdlib/strings/implementations/implementations.aspl @@ -0,0 +1,3 @@ +$if !twailbackend{ + $include("implementations.c") +} \ No newline at end of file diff --git a/stdlib/strings/implementations/implementations.c b/stdlib/strings/implementations/implementations.c new file mode 100644 index 0000000..947bb7d --- /dev/null +++ b/stdlib/strings/implementations/implementations.c @@ -0,0 +1,89 @@ +typedef struct ASPL_StringBuilder +{ + char* buffer; + int length; + int capacity; +} ASPL_StringBuilder; + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_strings$string_builder$new() +{ + ASPL_StringBuilder* builder = ASPL_MALLOC(sizeof(struct ASPL_StringBuilder)); + builder->buffer = ASPL_MALLOC(sizeof(char) * 16); + builder->length = 0; + builder->capacity = 16; + return ASPL_HANDLE_LITERAL(builder); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_strings$string_builder$get_length(ASPL_OBJECT_TYPE* builder) +{ + return ASPL_INT_LITERAL(((ASPL_StringBuilder*)ASPL_ACCESS(*builder).value.handle)->length); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_strings$string_builder$append_string(ASPL_OBJECT_TYPE* builder, ASPL_OBJECT_TYPE* string) +{ + ASPL_StringBuilder* builder_value = (ASPL_StringBuilder*)ASPL_ACCESS(*builder).value.handle; + int string_length = ASPL_ACCESS(*string).value.string->length; + int new_length = builder_value->length + string_length; + if (new_length > builder_value->capacity) + { + int new_capacity = builder_value->capacity * 2; + while (new_capacity < new_length) + { + new_capacity *= 2; + } + char* new_buffer = ASPL_MALLOC(sizeof(char) * new_capacity); + memcpy(new_buffer, builder_value->buffer, builder_value->length); + ASPL_FREE(builder_value->buffer); // TODO: Figure out if such calls are related to these random & weird memory corruptions that can occur in production mode + builder_value->buffer = new_buffer; + builder_value->capacity = new_capacity; + } + memcpy(builder_value->buffer + builder_value->length, ASPL_ACCESS(*string).value.string->str, string_length); + builder_value->length = new_length; + return *builder; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_strings$string_builder$append_string_builder(ASPL_OBJECT_TYPE* a, ASPL_OBJECT_TYPE* b) +{ + ASPL_StringBuilder* a_value = (ASPL_StringBuilder*)ASPL_ACCESS(*a).value.handle; + ASPL_StringBuilder* b_value = (ASPL_StringBuilder*)ASPL_ACCESS(*b).value.handle; + int new_length = a_value->length + b_value->length; + if (new_length > a_value->capacity) + { + int new_capacity = a_value->capacity * 2; + while (new_capacity < new_length) + { + new_capacity *= 2; + } + char* new_buffer = ASPL_MALLOC(sizeof(char) * new_capacity); + memcpy(new_buffer, a_value->buffer, a_value->length); + //ASPL_FREE(a_value->buffer); + a_value->buffer = new_buffer; + a_value->capacity = new_capacity; + } + memcpy(a_value->buffer + a_value->length, b_value->buffer, b_value->length); + a_value->length = new_length; + return *a; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_strings$string_builder$flush(ASPL_OBJECT_TYPE* builder) +{ + ASPL_StringBuilder* builder_value = (ASPL_StringBuilder*)ASPL_ACCESS(*builder).value.handle; + //ASPL_FREE(builder_value->buffer); + builder_value->buffer = ASPL_MALLOC(sizeof(char) * 16); + builder_value->length = 0; + builder_value->capacity = 16; + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_strings$string_builder$to_string_flush(ASPL_OBJECT_TYPE* builder) +{ + ASPL_StringBuilder* builder_value = (ASPL_StringBuilder*)ASPL_ACCESS(*builder).value.handle; + char* buffer = ASPL_MALLOC(sizeof(char) * (builder_value->length + 1)); + memcpy(buffer, builder_value->buffer, builder_value->length); + buffer[builder_value->length] = '\0'; + //ASPL_FREE(builder_value->buffer); + builder_value->buffer = ASPL_MALLOC(sizeof(char) * 16); + builder_value->length = 0; + builder_value->capacity = 16; + return ASPL_STRING_LITERAL_NO_COPY(buffer); +} \ No newline at end of file diff --git a/stdlib/time/Timestamp.aspl b/stdlib/time/Timestamp.aspl new file mode 100644 index 0000000..f2c11b6 --- /dev/null +++ b/stdlib/time/Timestamp.aspl @@ -0,0 +1,30 @@ +[public] +class Timestamp { + + [readpublic] + property long nanoseconds // since the Unix epoch + [public] + property long microseconds{ + get{ + return nanoseconds / 1000 + } + } + [public] + property long milliseconds{ + get{ + return nanoseconds / 1000000 + } + } + [public] + property long seconds{ + get{ + return nanoseconds / 1000000000 + } + } + + [public] + method construct(long nanoseconds){ + this.nanoseconds = nanoseconds + } + +} \ No newline at end of file diff --git a/stdlib/time/implementations/implementations.aspl b/stdlib/time/implementations/implementations.aspl new file mode 100644 index 0000000..74ecfe5 --- /dev/null +++ b/stdlib/time/implementations/implementations.aspl @@ -0,0 +1,3 @@ +$if !twailbackend{ + $include("implementations.c") +} \ No newline at end of file diff --git a/stdlib/time/implementations/implementations.c b/stdlib/time/implementations/implementations.c new file mode 100644 index 0000000..56da09c --- /dev/null +++ b/stdlib/time/implementations/implementations.c @@ -0,0 +1,88 @@ +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_time$nanosleep(ASPL_OBJECT_TYPE* nanoseconds) { + ASPL_OBJECT_TYPE nanosecondsObj = (ASPL_OBJECT_TYPE)*nanoseconds; +#ifdef _WIN32 + Sleep(ASPL_ACCESS(nanosecondsObj).value.integer64 / 1000000); +#else + struct timespec ts; + ts.tv_sec = ASPL_ACCESS(nanosecondsObj).value.integer64 / 1000000000; + ts.tv_nsec = ASPL_ACCESS(nanosecondsObj).value.integer64 % 1000000000; + while (nanosleep(&ts, &ts) == -1 && errno == EINTR) { + // Retry if interrupted by a signal + continue; + } +#endif + return ASPL_UNINITIALIZED; +} + +// taken from: https://github.com/vlang/v/blob/master/vlib/time/chrono.v#L7 +long aspl_util_days_from_unix_epoch(long long year, long long month, long long day) { + long long y = month <= 2 ? year - 1 : year; + long long era = y / 400; + long long year_of_the_era = y - era * 400; + long long day_of_year = (153 * (month + (month > 2 ? -3 : 9)) + 2) / 5 + day - 1; + long long day_of_the_era = year_of_the_era * 365 + year_of_the_era / 4 - year_of_the_era / 100 + day_of_year; + return era * 146097 + day_of_the_era - 719468; +} + +// taken from: https://github.com/vlang/v/blob/master/vlib/time/chrono.c.v#L5 +long long aspl_util_portable_timegm(struct tm* t) { + long long year = t->tm_year + 1900; + long long month = t->tm_mon; + if (month > 11) { + year += month / 12; + month %= 12; + } + else if (month < 0) { + long long years_diff = (11 - month) / 12; + year -= years_diff; + month += 12 * years_diff; + } + long long days_since_1970 = (long long)aspl_util_days_from_unix_epoch(year, month + 1, t->tm_mday); + return 60 * (60 * (24 * days_since_1970 + t->tm_hour) + t->tm_min) + t->tm_sec; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_time$nanotime_utc() { +#ifdef _WIN32 + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + SYSTEMTIME st; + FileTimeToSystemTime(&ft, &st); + struct tm t = { + .tm_year = st.wYear - 1900, + .tm_mon = st.wMonth - 1, + .tm_mday = st.wDay, + .tm_hour = st.wHour, + .tm_min = st.wMinute, + .tm_sec = st.wSecond + }; + return ASPL_LONG_LITERAL(aspl_util_portable_timegm(&t) * 1000000000 + st.wMilliseconds * 1000000); +#else + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return ASPL_LONG_LITERAL(ts.tv_sec * 1000000000 + ts.tv_nsec); +#endif +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_time$nanotime_local() { +#ifdef _WIN32 + SYSTEMTIME st; + GetLocalTime(&st); + FILETIME ft; + SystemTimeToFileTime(&st, &ft); + ULARGE_INTEGER uli; + uli.LowPart = ft.dwLowDateTime; + uli.HighPart = ft.dwHighDateTime; + return ASPL_LONG_LITERAL(uli.QuadPart * 100); +#else + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return ASPL_LONG_LITERAL(ts.tv_sec * 1000000000 + ts.tv_nsec); +#endif +} \ No newline at end of file diff --git a/stdlib/time/sleep.aspl b/stdlib/time/sleep.aspl new file mode 100644 index 0000000..b2618c0 --- /dev/null +++ b/stdlib/time/sleep.aspl @@ -0,0 +1,19 @@ +[public] +function sleep(long seconds){ + implement("time.nanosleep", seconds * 1000000000) +} + +[public] +function millisleep(long milliseconds){ + implement("time.nanosleep", milliseconds * 1000000) +} + +[public] +function microsleep(long microseconds){ + implement("time.nanosleep", microseconds * 1000) +} + +[public] +function nanosleep(long nanoseconds){ + implement("time.nanosleep", nanoseconds) +} \ No newline at end of file diff --git a/stdlib/time/time.aspl b/stdlib/time/time.aspl new file mode 100644 index 0000000..58de489 --- /dev/null +++ b/stdlib/time/time.aspl @@ -0,0 +1,15 @@ +/* +Returns the current time since the Unix Epoch (UTC) +*/ +[public] +function now() returns Timestamp{ + return new Timestamp(long(implement("time.nanotime_utc"))) +} + +/* +Returns the current time since the Unix Epoch (local timezone) +*/ +[public] +function local() returns Timestamp{ + return new Timestamp(long(implement("time.nanotime_local"))) +} \ No newline at end of file diff --git a/stdlib/zip/implementations/implementations.aspl b/stdlib/zip/implementations/implementations.aspl new file mode 100644 index 0000000..74ecfe5 --- /dev/null +++ b/stdlib/zip/implementations/implementations.aspl @@ -0,0 +1,3 @@ +$if !twailbackend{ + $include("implementations.c") +} \ No newline at end of file diff --git a/stdlib/zip/implementations/implementations.c b/stdlib/zip/implementations/implementations.c new file mode 100644 index 0000000..a5fd2ad --- /dev/null +++ b/stdlib/zip/implementations/implementations.c @@ -0,0 +1,288 @@ +#define MINIZ_MALLOC ASPL_MALLOC +#define MINIZ_FREE ASPL_FREE +#include "thirdparty/miniz/miniz.c" + +#ifdef _WIN32 +#include +#else +#include +#endif +// TODO: Are these includes necessary/correct? + +// TODO: Support "/" instead of "\\" on Windows + +void create_directory_recursive(const char* path) { + // find parent directory + char* parent_path = ASPL_MALLOC(strlen(path) + 1); + strcpy(parent_path, path); +#ifdef _WIN32 + char* last_slash = strrchr(parent_path, '\\'); +#else + char* last_slash = strrchr(parent_path, '/'); +#endif + if (last_slash != NULL) { + *last_slash = '\0'; + } + + // create parent directory +#ifdef _WIN32 + if (_access(parent_path, F_OK) != 0) { + create_directory_recursive(parent_path); + } +#else + if (access(parent_path, F_OK) != 0) { + create_directory_recursive(parent_path); + } +#endif + + // create directory +#ifdef _WIN32 + if (_access(path, F_OK) != 0) { + CreateDirectory(path, NULL); + } +#else + if (access(path, F_OK) != 0) { + mkdir(path, 0777); + } +#endif +} + +void unzip_file(const char* zip_path, const char* folder_path) { +#ifdef _WIN32 + if (_access(folder_path, F_OK) != 0) { + CreateDirectory(folder_path, NULL); + } +#else + if (access(folder_path, F_OK) != 0) { + mkdir(folder_path, 0777); + } +#endif + + mz_zip_archive zip_archive; + memset(&zip_archive, 0, sizeof(zip_archive)); + + if (!mz_zip_reader_init_file(&zip_archive, zip_path, 0)) { + printf("Failed to initialize zip reader\n"); + printf("Path: %s\n", zip_path); + return; + } + + int num_files = mz_zip_reader_get_num_files(&zip_archive); + for (int i = 0; i < num_files; i++) { + mz_zip_archive_file_stat file_stat; + if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) { + printf("Failed to read file stat\n"); + mz_zip_reader_end(&zip_archive); + return; + } + + char* file_path = ASPL_MALLOC(strlen(folder_path) + strlen(file_stat.m_filename) + 2); +#ifdef _WIN32 + sprintf(file_path, "%s\\%s", folder_path, file_stat.m_filename); +#else + sprintf(file_path, "%s/%s", folder_path, file_stat.m_filename); +#endif + + if (mz_zip_reader_is_file_a_directory(&zip_archive, i)) { + create_directory_recursive(file_path); + } + else { + char* parent_path = ASPL_MALLOC(strlen(file_path) + 1); + strcpy(parent_path, file_path); +#ifdef _WIN32 + char* last_slash = strrchr(parent_path, '\\'); +#else + char* last_slash = strrchr(parent_path, '/'); +#endif + if (last_slash != NULL) { + *last_slash = '\0'; + } + + create_directory_recursive(parent_path); + +#ifdef _WIN32 + if (_access(file_path, F_OK) != 0) { + FILE* fp = fopen(file_path, "wb"); + if (fp != NULL) { + fclose(fp); + } + else { + ASPL_PANIC("Failed to create file '%s'", file_path); + } + } +#else + if (access(file_path, F_OK) != 0) { + FILE* fp = fopen(file_path, "w"); + if (fp != NULL) { + fclose(fp); + } + else { + ASPL_PANIC("Failed to create file '%s'", file_path); + } + } +#endif + + + if (!mz_zip_reader_extract_to_file(&zip_archive, i, file_path, 0)) { + printf("Failed to extract file from zip archive: %s\n", file_path); + printf("Error: %s\n", mz_zip_get_error_string(mz_zip_get_last_error(&zip_archive))); + } + + ASPL_FREE(parent_path); + } + + ASPL_FREE(file_path); + } + + mz_zip_reader_end(&zip_archive); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_zip$extract_file_to_directory(ASPL_OBJECT_TYPE* filePath, ASPL_OBJECT_TYPE* directoryPath) { + unzip_file(ASPL_ACCESS(*filePath).value.string->str, ASPL_ACCESS(*directoryPath).value.string->str); + return ASPL_UNINITIALIZED; +} + +void add_directory_to_archive(mz_zip_archive* zip_archive, const char* directory) { +#ifdef _WIN32 + WIN32_FIND_DATA FindFileData; + HANDLE hFind; + char search_path[MAX_PATH]; + snprintf(search_path, sizeof(search_path), "%s\\*", directory); + if ((hFind = FindFirstFile(search_path, &FindFileData)) != INVALID_HANDLE_VALUE) { + do { + if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if (strcmp(FindFileData.cFileName, ".") != 0 && strcmp(FindFileData.cFileName, "..") != 0) { + char subfolder_path[MAX_PATH]; + snprintf(subfolder_path, sizeof(subfolder_path), "%s\\%s", directory, FindFileData.cFileName); + add_directory_to_archive(zip_archive, subfolder_path); + } + } + else { + char file_path[MAX_PATH]; + snprintf(file_path, sizeof(file_path), "%s\\%s", directory, FindFileData.cFileName); + if (mz_zip_writer_add_file(zip_archive, file_path, file_path, NULL, 0, MZ_DEFAULT_COMPRESSION) != MZ_TRUE) { + printf("Failed to add file to zip archive: %s\n", file_path); + } + } + } while (FindNextFile(hFind, &FindFileData) != 0); + FindClose(hFind); + } +#else + DIR* dir = opendir(directory); + if (dir == NULL) { + printf("Failed to open directory\n"); + return; + } + + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + char file_path[PATH_MAX]; + snprintf(file_path, sizeof(file_path), "%s/%s", directory, entry->d_name); + + if (mz_zip_writer_add_file(zip_archive, entry->d_name, file_path, NULL, 0, MZ_DEFAULT_COMPRESSION) != MZ_TRUE) { + printf("Failed to add file to zip archive: %s\n", file_path); + } + } + + closedir(dir); +#endif +} + +void zip_folder(const char* zip_path, const char* folder_path) { +#ifdef _WIN32 + if (_access(zip_path, F_OK) != 0) { + FILE* fp = fopen(zip_path, "wb"); + if (fp != NULL) { + fclose(fp); + } + else { + ASPL_PANIC("Failed to create zip archive '%s'", zip_path); + } + } +#else + if (access(zip_path, F_OK) != 0) { + FILE* fp = fopen(zip_path, "w"); + if (fp != NULL) { + fclose(fp); + } + else { + ASPL_PANIC("Failed to create zip archive '%s'", zip_path); + } + } +#endif + + mz_zip_archive zip_archive; + memset(&zip_archive, 0, sizeof(zip_archive)); + + if (!mz_zip_writer_init_file(&zip_archive, zip_path, 0)) { + ASPL_PANIC("Failed to initialize zip writer for '%s'", zip_path); + } + + add_directory_to_archive(&zip_archive, folder_path); + + if (!mz_zip_writer_finalize_archive(&zip_archive)) { + ASPL_PANIC("Failed to finalize zip archive '%s'", zip_path); + return; + } + + mz_zip_writer_end(&zip_archive); +} + +void zip_files(const char* zip_path, const char** file_paths, int amount) { +#ifdef _WIN32 + if (_access(zip_path, F_OK) != 0) { + FILE* fp = fopen(zip_path, "wb"); + if (fp != NULL) { + fclose(fp); + } + else { + ASPL_PANIC("Failed to create zip archive '%s'", zip_path); + } + } +#else + if (access(zip_path, F_OK) != 0) { + FILE* fp = fopen(zip_path, "w"); + if (fp != NULL) { + fclose(fp); + } + else { + ASPL_PANIC("Failed to create zip archive '%s'", zip_path); + } + } +#endif + + mz_zip_archive zip_archive; + memset(&zip_archive, 0, sizeof(zip_archive)); + + if (!mz_zip_writer_init_file(&zip_archive, zip_path, 0)) { + ASPL_PANIC("Failed to initialize zip writer for '%s'", zip_path); + } + + for (int i = 0; i < amount; i++) { + if (mz_zip_writer_add_file(&zip_archive, file_paths[i], file_paths[i], NULL, 0, MZ_DEFAULT_COMPRESSION) != MZ_TRUE) { + ASPL_PANIC("Failed to add file to zip archive: %s", file_paths[i]); + } + } + + if (!mz_zip_writer_finalize_archive(&zip_archive)) { + ASPL_PANIC("Failed to finalize zip archive '%s'", zip_path); + return; + } + + mz_zip_writer_end(&zip_archive); +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_zip$create_archive_from_directory(ASPL_OBJECT_TYPE* directoryPath, ASPL_OBJECT_TYPE* filePath) { + zip_folder(ASPL_ACCESS(*directoryPath).value.string->str, ASPL_ACCESS(*filePath).value.string->str); + return ASPL_UNINITIALIZED; +} + +ASPL_OBJECT_TYPE ASPL_IMPLEMENT_zip$create_archive_from_files(ASPL_OBJECT_TYPE* files, ASPL_OBJECT_TYPE* filePath) { + char** filePaths = ASPL_MALLOC(sizeof(char*) * ASPL_ACCESS(*files).value.list->length); + for (int i = 0; i < ASPL_ACCESS(*files).value.list->length; i++) { + filePaths[i] = ASPL_ACCESS(ASPL_ACCESS(*files).value.list->value[i]).value.string->str; + } + zip_files(ASPL_ACCESS(*filePath).value.string->str, (const char**)filePaths, ASPL_ACCESS(*files).value.list->length); + ASPL_FREE(filePaths); + return ASPL_UNINITIALIZED; +} \ No newline at end of file diff --git a/stdlib/zip/zip.aspl b/stdlib/zip/zip.aspl new file mode 100644 index 0000000..51e442a --- /dev/null +++ b/stdlib/zip/zip.aspl @@ -0,0 +1,14 @@ +[public] +function unzip(string file, string folder){ + implement("zip.extract_file_to_directory", file, folder) +} + +[public] +function zip_folder(string folder, string file){ + implement("zip.create_archive_from_directory", file, folder) +} + +[public] +function zip_files(list files, string file){ + implement("zip.create_archive_from_files", file, files) +} \ No newline at end of file diff --git a/templates/README.md b/templates/README.md new file mode 100644 index 0000000..a92af6c --- /dev/null +++ b/templates/README.md @@ -0,0 +1,31 @@ +# Executable Templates + +> **Note** +> This documentation only applies to the AIL backend! + +## ASPL executable and bytecode infrastructure +The AIL backend of the ASPL compiler outputs bytecode instead of native code (in contrast to the C backend, which ultimately compiles to native code using a C compiler); this has the following advantages: + +* Generating bytecode is faster than generating native code +* The bytecode is easier to read and debug +* The bytecode is platform independent +* Writing and maintaining a bytecode compiler is easier than writing and maintaining a native compiler +* ... + +On the other hand, the bytecode is obviously not an executable and requires an interpreter to run. + +Most bytecode languages, such as Java, require that all programs written and compiled in that language have an interpreter (or a virtual machine) installed on the system on which they are executed. + +ASPL takes a different approach and bundles the whole runtime, including the bytecode interpreter, into a single binary and finally adds the bytecode generated by the compiler as a resource to the executable. This executable can then be shipped, distributed, and executed on any system just like a native executable without any dependencies. + +## Templating +Since compiling the runtime can take quite some time and may require additional dependencies, the runtime is compiled only once into a so called "template" and then shipped together with the compiler in every distribution. + +That way, compiling ASPL programs into executables is as simple as copying the template and adding the bytecode as a resource, which has the following advantages: + +* Faster compilation of ASPL programs +* No additional compile-time dependencies +* Seamless cross-compilation +* ... + +The templates in this folder are automatically generated by a CD script and updated whenever the ASPL installation is updated using `aspl update`. \ No newline at end of file diff --git a/templates/linux/arm32/full/.gitignore b/templates/linux/arm32/full/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/templates/linux/arm32/full/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/templates/linux/arm32/minimal/.gitignore b/templates/linux/arm32/minimal/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/templates/linux/arm32/minimal/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/templates/linux/x86_64/full/.gitignore b/templates/linux/x86_64/full/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/templates/linux/x86_64/full/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/templates/linux/x86_64/minimal/.gitignore b/templates/linux/x86_64/minimal/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/templates/linux/x86_64/minimal/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/templates/macos/x86_64/full/.gitignore b/templates/macos/x86_64/full/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/templates/macos/x86_64/full/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/templates/macos/x86_64/minimal/.gitignore b/templates/macos/x86_64/minimal/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/templates/macos/x86_64/minimal/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/templates/windows/x86_64/full/cli/.gitignore b/templates/windows/x86_64/full/cli/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/templates/windows/x86_64/full/cli/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/templates/windows/x86_64/minimal/.gitignore b/templates/windows/x86_64/minimal/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/templates/windows/x86_64/minimal/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/tests/abstracts/Child.aspl b/tests/abstracts/Child.aspl new file mode 100644 index 0000000..bd65fe1 --- /dev/null +++ b/tests/abstracts/Child.aspl @@ -0,0 +1,23 @@ +class Child extends Parent { + + [public] + method one(){ + print("child: one") + } + + [public] + method three(){ + print("child: three") + } + + [public] + method four(){ + print("child: four") + } + + [public] + method five(){ + print("child: five") + } + +} \ No newline at end of file diff --git a/tests/abstracts/GrandParentOne.aspl b/tests/abstracts/GrandParentOne.aspl new file mode 100644 index 0000000..401e79a --- /dev/null +++ b/tests/abstracts/GrandParentOne.aspl @@ -0,0 +1,8 @@ +[abstract] +class GrandParentOne { + + [public] + [abstract] + method three() + +} \ No newline at end of file diff --git a/tests/abstracts/GrandParentTwo.aspl b/tests/abstracts/GrandParentTwo.aspl new file mode 100644 index 0000000..91ab691 --- /dev/null +++ b/tests/abstracts/GrandParentTwo.aspl @@ -0,0 +1,18 @@ +[abstract] +class GrandParentTwo { + + [public] + [abstract] + method four() + + [public] + method five(){ + print("grand parent two: five") + } + + [public] + method six(){ + print("grand parent two: six") + } + +} \ No newline at end of file diff --git a/tests/abstracts/Parent.aspl b/tests/abstracts/Parent.aspl new file mode 100644 index 0000000..e5a51fe --- /dev/null +++ b/tests/abstracts/Parent.aspl @@ -0,0 +1,13 @@ +[abstract] +class Parent extends GrandParentOne, GrandParentTwo { + + [public] + [abstract] + method one() + + [public] + method two(){ + print("parent: two") + } + +} \ No newline at end of file diff --git a/tests/abstracts/main.aspl b/tests/abstracts/main.aspl new file mode 100644 index 0000000..9761058 --- /dev/null +++ b/tests/abstracts/main.aspl @@ -0,0 +1,7 @@ +var child = new Child() +child.one() +child.two() +child.three() +child.four() +child.five() +child.six() \ No newline at end of file diff --git a/tests/arithmetic/arithmetic.aspl b/tests/arithmetic/arithmetic.aspl new file mode 100644 index 0000000..7a91916 --- /dev/null +++ b/tests/arithmetic/arithmetic.aspl @@ -0,0 +1,24 @@ +assert 2 + 3 == 5 +assert 10 - 2 == 8 +assert 10 - 11 == -1 +assert 2 * 4 == 8 +assert -2 * 4 == 2 * -4 +assert 9 / 3 == 3 +assert -9 / 3 == -3 +assert 9 / -3 == -3 +assert 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 == 45 +assert 5 * (1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9) == 5 * 45 + +assert 2 * 3 + 1 * 3 == (2 * 3) + (1 * 3) +assert 5 / 2 + 1 / 3 == (5 / 2) + (1 / 3) + +assert 2 > 1 +assert 2 >= 1 +assert 1 < 2 +assert 1 <= 2 +assert 2 >= 2 +assert 2 <= 2 +assert !(2 > 3) +assert !(2 >= 3) +assert !(-1 < -2) +assert !(-1 <= -2) \ No newline at end of file diff --git a/tests/attributes/Example.aspl b/tests/attributes/Example.aspl new file mode 100644 index 0000000..791cf99 --- /dev/null +++ b/tests/attributes/Example.aspl @@ -0,0 +1,16 @@ +[description("An example class")] +class Example { + + property int x = [0][0] // test to ensure attributes and list indexing is not mixed up + + [deprecated("Use the other property x")] + property list y = [0] // test to ensure attributes and list indexing is not mixed up + + [public] + [deprecated] + method hello(){ + print("Hello from Example!") + print(this.y) + } + +} \ No newline at end of file diff --git a/tests/attributes/main.aspl b/tests/attributes/main.aspl new file mode 100644 index 0000000..b09919b --- /dev/null +++ b/tests/attributes/main.aspl @@ -0,0 +1,2 @@ +var Example example = new Example() +example.hello() \ No newline at end of file diff --git a/tests/blocks/blocks.aspl b/tests/blocks/blocks.aspl new file mode 100644 index 0000000..7b585a3 --- /dev/null +++ b/tests/blocks/blocks.aspl @@ -0,0 +1,8 @@ +var a = 0 +{ + var b = "Hello" + print(b) + print(a) + a = 1 +} +print(a) \ No newline at end of file diff --git a/tests/boolean_operators/boolean_operators.aspl b/tests/boolean_operators/boolean_operators.aspl new file mode 100644 index 0000000..1562821 --- /dev/null +++ b/tests/boolean_operators/boolean_operators.aspl @@ -0,0 +1,17 @@ +assert !true == false +assert !false == true + +assert (true || false) == true +assert (true || true) == true +assert (false || false) == false +assert (false || true) == true + +assert (true && false) == false +assert (true && true) == true +assert (false && false) == false +assert (false && true) == false + +assert (true ^ false) == true +assert (true ^ true) == false +assert (false ^ false) == false +assert (false ^ true) == true \ No newline at end of file diff --git a/tests/break_and_continue/break_and_continue.aspl b/tests/break_and_continue/break_and_continue.aspl new file mode 100644 index 0000000..9a1e3a5 --- /dev/null +++ b/tests/break_and_continue/break_and_continue.aspl @@ -0,0 +1,23 @@ +// TODO: Enable this test again (break & continue levels are currently not supported in all backends) +/*var x = 0 +var y = 0 +var z = 0 +repeat(10){ + if(x == 5){ + while(true){ + if(z == 10){ + break 2 + } + z++ + } + } + x++ + if(x == 3){ + continue + } + y++ +} +assert x == 5 +assert y == 4 +assert z == 10*/ +assert true // required last statement in file \ No newline at end of file diff --git a/tests/callbacks/callbacks.aspl b/tests/callbacks/callbacks.aspl new file mode 100644 index 0000000..7dda524 --- /dev/null +++ b/tests/callbacks/callbacks.aspl @@ -0,0 +1,8 @@ +var x = callback(list number) returns int|float{ + return number[0] * 2 +} +var callback, returns int|float> c = x +print(c) +assert c.invoke([100]) == c.([100]) +assert c.([20]) == 40 +assert c.([-10]) == -20 \ No newline at end of file diff --git a/tests/casting/casting.aspl b/tests/casting/casting.aspl new file mode 100644 index 0000000..2325646 --- /dev/null +++ b/tests/casting/casting.aspl @@ -0,0 +1,5 @@ +assert int(1f) == 1 +assert int(1.0) == 1 +assert int(1) == 1 +assert string(1) == "1" +assert int(string(1)) == 1 \ No newline at end of file diff --git a/tests/classes/Example.aspl b/tests/classes/Example.aspl new file mode 100644 index 0000000..d533966 --- /dev/null +++ b/tests/classes/Example.aspl @@ -0,0 +1,50 @@ +class Example extends ParentOne, ParentTwo { + + [public] + property int number = 42 + [public] + property bool isImportantProperty + [public] + property bool alwaysTrue{ + get{ + return true + } + set{ + // do nothing + } + } + + [static] + property bool someStaticProperty = true + + [public] + method construct(){ + print("Creating new instance of Example...") + assert number == 42 + number = 43 + assert number == 43 + number = 42 + assert someStaticProperty == true + someStaticProperty = false + assert someStaticProperty == false + someStaticProperty = true + assert getNumber() == 42 + assert get10() == 10 + } + + [public] + method hello(){ + print("Hello from example!") + } + + [static] + method get10() returns int{ + return 10 + } + + [public] + method getNumber() returns int{ + return this.number + } + +} \ No newline at end of file diff --git a/tests/classes/GrandParent.aspl b/tests/classes/GrandParent.aspl new file mode 100644 index 0000000..ce27c22 --- /dev/null +++ b/tests/classes/GrandParent.aspl @@ -0,0 +1,11 @@ +class GrandParent { + + [public] + property int grandParentProperty = -100 + + [public] + method grandParentMethod(){ + print("Hello from grandparent!") + } + +} \ No newline at end of file diff --git a/tests/classes/ParentOne.aspl b/tests/classes/ParentOne.aspl new file mode 100644 index 0000000..f064d52 --- /dev/null +++ b/tests/classes/ParentOne.aspl @@ -0,0 +1,11 @@ +class ParentOne extends GrandParent { + + [public] + property int parentOneProperty = 100 + + [public] + method parentOneMethod(){ + print("Hello from parent 1!") + } + +} \ No newline at end of file diff --git a/tests/classes/ParentTwo.aspl b/tests/classes/ParentTwo.aspl new file mode 100644 index 0000000..7268a3c --- /dev/null +++ b/tests/classes/ParentTwo.aspl @@ -0,0 +1,11 @@ +class ParentTwo { + + [public] + property int parentTwoProperty = 200 + + [public] + method parentTwoMethod(){ + print("Hello from parent 2!") + } + +} \ No newline at end of file diff --git a/tests/classes/main.aspl b/tests/classes/main.aspl new file mode 100644 index 0000000..ae18f85 --- /dev/null +++ b/tests/classes/main.aspl @@ -0,0 +1,16 @@ +var example = new Example() +print(example) +example.hello() +example.parentOneMethod() +example.parentTwoMethod() +example.grandParentMethod() +print(example.parentOneProperty) +print(example.parentTwoProperty) +print(example.grandParentProperty) +assert example.getNumber() == 42 +example.number = 10 +assert example.getNumber() == 10 +assert !example.isImportantProperty +assert example.alwaysTrue +example.alwaysTrue = false +assert example.alwaysTrue \ No newline at end of file diff --git a/tests/closures/closures.aspl b/tests/closures/closures.aspl new file mode 100644 index 0000000..f9cf9f2 --- /dev/null +++ b/tests/closures/closures.aspl @@ -0,0 +1,13 @@ +function main() returns callback{ + var outer = 42 + var cross = "Default" + var c = callback() returns int{ + cross = "from the inner scope" + var inner = null + return outer + } + assert cross == "Default" + return c +} + +assert main().invoke() == 42 \ No newline at end of file diff --git a/tests/compile_time/compile_time.aspl b/tests/compile_time/compile_time.aspl new file mode 100644 index 0000000..961f961 --- /dev/null +++ b/tests/compile_time/compile_time.aspl @@ -0,0 +1,6 @@ +$if windows{ + print("Windows") +} +$if !windows{ + print("Not on windows") +} \ No newline at end of file diff --git a/tests/divideequals/divideequals.aspl b/tests/divideequals/divideequals.aspl new file mode 100644 index 0000000..4b8abe1 --- /dev/null +++ b/tests/divideequals/divideequals.aspl @@ -0,0 +1,3 @@ +var i = 10 +i /= 2 +assert i == 5 \ No newline at end of file diff --git a/tests/enums/Options.aspl b/tests/enums/Options.aspl new file mode 100644 index 0000000..e69de29 diff --git a/tests/enums/Persons.aspl b/tests/enums/Persons.aspl new file mode 100644 index 0000000..29c59d1 --- /dev/null +++ b/tests/enums/Persons.aspl @@ -0,0 +1,10 @@ +[description("An example enum")] +enum Persons{ + [deprecated] + [description("This is the first person")] + John, + [description("This is the second person")] + Maria, + Lewis = 4, + Leo = 5 +} \ No newline at end of file diff --git a/tests/enums/main.aspl b/tests/enums/main.aspl new file mode 100644 index 0000000..16c3490 --- /dev/null +++ b/tests/enums/main.aspl @@ -0,0 +1,12 @@ +print(Persons.John) +assert Persons.John != Persons.Maria + +print(Options.Debug || Options.TestAll) +assert ((Options.Debug || Options.TestAll) && Options.Debug) != 0 + +[flags] +enum Options { + Debug, + TestAll, + Deploy +} \ No newline at end of file diff --git a/tests/equality/equality.aspl b/tests/equality/equality.aspl new file mode 100644 index 0000000..0b76d45 --- /dev/null +++ b/tests/equality/equality.aspl @@ -0,0 +1,12 @@ +var a = 0 +var b = 0 +assert a == b +var c = 1 +assert a != c +assert b != c +assert c == 1 +var d = "hello" +assert d == "hello" +assert d != "world" +assert d != "hello world" +assert d != a \ No newline at end of file diff --git a/tests/folders/main.aspl b/tests/folders/main.aspl new file mode 100644 index 0000000..717c93f --- /dev/null +++ b/tests/folders/main.aspl @@ -0,0 +1,7 @@ +top() +middle.middle() +middle.deep.deep() + +folders.top() +folders.middle.middle() +folders.middle.deep.deep() \ No newline at end of file diff --git a/tests/folders/middle/deep/deep.aspl b/tests/folders/middle/deep/deep.aspl new file mode 100644 index 0000000..61500a5 --- /dev/null +++ b/tests/folders/middle/deep/deep.aspl @@ -0,0 +1,3 @@ +function deep(){ + print("Hello from the deep level folder") +} \ No newline at end of file diff --git a/tests/folders/middle/middle.aspl b/tests/folders/middle/middle.aspl new file mode 100644 index 0000000..b4549e2 --- /dev/null +++ b/tests/folders/middle/middle.aspl @@ -0,0 +1,3 @@ +function middle(){ + print("Hello from the middle level folder") +} \ No newline at end of file diff --git a/tests/folders/top.aspl b/tests/folders/top.aspl new file mode 100644 index 0000000..f8e36aa --- /dev/null +++ b/tests/folders/top.aspl @@ -0,0 +1,3 @@ +function top(){ + print("Hello from the top level folder") +} \ No newline at end of file diff --git a/tests/foreach/foreach.aspl b/tests/foreach/foreach.aspl new file mode 100644 index 0000000..2db30ad --- /dev/null +++ b/tests/foreach/foreach.aspl @@ -0,0 +1,13 @@ +var map m = map{} +foreach([3, 4, 5] as index => element){ + m[index] = element +} +assert m == {0 => 3, 1 => 4, 2 => 5} +var s = "" +foreach("Hello World!" as c){ + s += c +} +assert s == "Hello World!" +foreach({"Hello" => "World"} as key => value){ + print(key + ": " + value) +} \ No newline at end of file diff --git a/tests/functions/functions.aspl b/tests/functions/functions.aspl new file mode 100644 index 0000000..8393678 --- /dev/null +++ b/tests/functions/functions.aspl @@ -0,0 +1,11 @@ +function f(integer x) returns int|float{ + return x +} + +function pretty_print(any x){ + print("---") + print(x) + print("---") +} + +pretty_print(f(3)) \ No newline at end of file diff --git a/tests/hello_world/hello_world.aspl b/tests/hello_world/hello_world.aspl new file mode 100644 index 0000000..1dc45ac --- /dev/null +++ b/tests/hello_world/hello_world.aspl @@ -0,0 +1 @@ +print("Hello World!") \ No newline at end of file diff --git a/tests/if/if.aspl b/tests/if/if.aspl new file mode 100644 index 0000000..c4795a8 --- /dev/null +++ b/tests/if/if.aspl @@ -0,0 +1,24 @@ +var a = false +if(true){ + a = true +}elseif(true){ + assert false // unreachable because of previous if +}else{ + assert false // unreachable because of previous if +} +if(false){ + assert false // unreachable because condition is false +}elseif(false){ + assert false // unreachable because condition is false +}else{ + assert a +} +var b = false +if(false){ + assert false // unreachable because condition is false +}elseif(true){ + b = true +}else{ + b = false // unreachable because of previous elseif +} +assert b \ No newline at end of file diff --git a/tests/implementation_calls/implementation_calls.aspl b/tests/implementation_calls/implementation_calls.aspl new file mode 100644 index 0000000..2e0d0ce --- /dev/null +++ b/tests/implementation_calls/implementation_calls.aspl @@ -0,0 +1,6 @@ +import time // needed to include the implementations in some backends +import math + +assert implement("time.nanotime_utc") > 0 +print(implement("time.nanotime_utc")) +assert implement("math.round", 0.6d, 0) == 1d \ No newline at end of file diff --git a/tests/imports/classes/Example.aspl b/tests/imports/classes/Example.aspl new file mode 100644 index 0000000..5b8afe0 --- /dev/null +++ b/tests/imports/classes/Example.aspl @@ -0,0 +1 @@ +class Example {} \ No newline at end of file diff --git a/tests/imports/main.aspl b/tests/imports/main.aspl new file mode 100644 index 0000000..ee223ec --- /dev/null +++ b/tests/imports/main.aspl @@ -0,0 +1,4 @@ +import imports.classes + +var example = new Example() +print(example) \ No newline at end of file diff --git a/tests/lists/lists.aspl b/tests/lists/lists.aspl new file mode 100644 index 0000000..247dca4 --- /dev/null +++ b/tests/lists/lists.aspl @@ -0,0 +1,15 @@ +var l = ["Hello", "World"] +var list x = l +l.add("!") +l.add("?") +assert x.length == 4 +x.removeAt(3) +assert l.length == 3 +assert x.length == 3 +assert x[0] == l[0] +assert x[1] == l[1] +assert x[2] != l[1] +assert x[2] == "!" +x[2] = "?" +assert l[2] == "?" +print(x) \ No newline at end of file diff --git a/tests/maps/maps.aspl b/tests/maps/maps.aspl new file mode 100644 index 0000000..6785177 --- /dev/null +++ b/tests/maps/maps.aspl @@ -0,0 +1,13 @@ +var m = {"Hello" => "World", "Foo" => "Bar"} +var map x = m +assert x.length == 2 +x.remove("Hello") +assert m.length == 1 +assert x.length == 1 +assert x["Foo"] == "Bar" +m["AS"] = "PL" +assert m["AS"] == "PL" +assert m["AS"] != x["Foo"] +x["AS"] = "..." +assert m["AS"] == "..." +print(x) \ No newline at end of file diff --git a/tests/methods/methods.aspl b/tests/methods/methods.aspl new file mode 100644 index 0000000..862d095 --- /dev/null +++ b/tests/methods/methods.aspl @@ -0,0 +1 @@ +print("Hello".toLower()) \ No newline at end of file diff --git a/tests/minusequals/minusequals.aspl b/tests/minusequals/minusequals.aspl new file mode 100644 index 0000000..65b3d71 --- /dev/null +++ b/tests/minusequals/minusequals.aspl @@ -0,0 +1,3 @@ +var i = 1 +i -= 5 +assert i == -4 \ No newline at end of file diff --git a/tests/minusminus/minusminus.aspl b/tests/minusminus/minusminus.aspl new file mode 100644 index 0000000..84b75aa --- /dev/null +++ b/tests/minusminus/minusminus.aspl @@ -0,0 +1,4 @@ +var int i = 0 +i-- +assert i == -1 +assert i-- == -2 \ No newline at end of file diff --git a/tests/modules/modules.aspl b/tests/modules/modules.aspl new file mode 100644 index 0000000..11af138 --- /dev/null +++ b/tests/modules/modules.aspl @@ -0,0 +1,8 @@ +/* +This test only works with a module called "example" installed +import example + +print(example.add(1, 2)) +print(example.subfolder.stringAdd("Hello", "World")) +*/ +assert true \ No newline at end of file diff --git a/tests/moduloequals/moduloequals.aspl b/tests/moduloequals/moduloequals.aspl new file mode 100644 index 0000000..c1bf9cb --- /dev/null +++ b/tests/moduloequals/moduloequals.aspl @@ -0,0 +1,3 @@ +var i = 14 +i %= 3 +assert i == 2 \ No newline at end of file diff --git a/tests/multiplyequals/multiplyequals.aspl b/tests/multiplyequals/multiplyequals.aspl new file mode 100644 index 0000000..0fe88d3 --- /dev/null +++ b/tests/multiplyequals/multiplyequals.aspl @@ -0,0 +1,3 @@ +var i = 3 +i *= 4 +assert i == 12 \ No newline at end of file diff --git a/tests/nullable/nullable.aspl b/tests/nullable/nullable.aspl new file mode 100644 index 0000000..e79297b --- /dev/null +++ b/tests/nullable/nullable.aspl @@ -0,0 +1,6 @@ +var int? i = 0 +i = null +assert i == null +i = 10 +assert i == 10 +assert i?! == 10 \ No newline at end of file diff --git a/tests/oftype/oftype.aspl b/tests/oftype/oftype.aspl new file mode 100644 index 0000000..500d6c6 --- /dev/null +++ b/tests/oftype/oftype.aspl @@ -0,0 +1,5 @@ +assert "Hello" oftype string +assert !("Hello" oftype int) +var a = 10 +assert a oftype int +assert !(a oftype float) \ No newline at end of file diff --git a/tests/parent_method_calls/Child.aspl b/tests/parent_method_calls/Child.aspl new file mode 100644 index 0000000..7ba3a9d --- /dev/null +++ b/tests/parent_method_calls/Child.aspl @@ -0,0 +1,13 @@ +class Child extends Parent { + + [public] + method getValue() returns int{ + return parent(Parent).getValue() + 1 + } + + [public] + method getValueWithArg(int x) returns int{ + return parent(Parent).getValueWithArg(x) + 5 + } + +} \ No newline at end of file diff --git a/tests/parent_method_calls/Parent.aspl b/tests/parent_method_calls/Parent.aspl new file mode 100644 index 0000000..1a1bd1e --- /dev/null +++ b/tests/parent_method_calls/Parent.aspl @@ -0,0 +1,11 @@ +class Parent{ + + method getValue() returns int{ + return 41 + } + + method getValueWithArg(int x) returns int{ + return x * 10 + } + +} \ No newline at end of file diff --git a/tests/parent_method_calls/main.aspl b/tests/parent_method_calls/main.aspl new file mode 100644 index 0000000..667043b --- /dev/null +++ b/tests/parent_method_calls/main.aspl @@ -0,0 +1,3 @@ +var c = new Child() +assert c.getValue() == 42 +assert c.getValueWithArg(3) == 35 \ No newline at end of file diff --git a/tests/plusequals/plusequals.aspl b/tests/plusequals/plusequals.aspl new file mode 100644 index 0000000..e45ec3e --- /dev/null +++ b/tests/plusequals/plusequals.aspl @@ -0,0 +1,6 @@ +var i = 1 +i += 5 +assert i == 6 +var s = "Hello" +s += " World" +assert s == "Hello World" \ No newline at end of file diff --git a/tests/plusplus/plusplus.aspl b/tests/plusplus/plusplus.aspl new file mode 100644 index 0000000..08e23dc --- /dev/null +++ b/tests/plusplus/plusplus.aspl @@ -0,0 +1,4 @@ +var int i = 0 +i++ +assert i == 1 +assert i++ == 2 \ No newline at end of file diff --git a/tests/pointers/pointers.aspl b/tests/pointers/pointers.aspl new file mode 100644 index 0000000..5fa089e --- /dev/null +++ b/tests/pointers/pointers.aspl @@ -0,0 +1,4 @@ +var integer a = 0 +var int* p = &a +print(p) +assert *p == 0 \ No newline at end of file diff --git a/tests/properties/properties.aspl b/tests/properties/properties.aspl new file mode 100644 index 0000000..874b187 --- /dev/null +++ b/tests/properties/properties.aspl @@ -0,0 +1 @@ +print("Hello World!".length) \ No newline at end of file diff --git a/tests/properties_without_constructor/Example.aspl b/tests/properties_without_constructor/Example.aspl new file mode 100644 index 0000000..16647dc --- /dev/null +++ b/tests/properties_without_constructor/Example.aspl @@ -0,0 +1,6 @@ +class Example{ + + [public] + property map m = {"Hello" => "World"} + +} \ No newline at end of file diff --git a/tests/properties_without_constructor/main.aspl b/tests/properties_without_constructor/main.aspl new file mode 100644 index 0000000..d0c2ece --- /dev/null +++ b/tests/properties_without_constructor/main.aspl @@ -0,0 +1,2 @@ +var e = new Example() +assert e.m == {"Hello" => "World"} \ No newline at end of file diff --git a/tests/repeat/repeat.aspl b/tests/repeat/repeat.aspl new file mode 100644 index 0000000..905db6b --- /dev/null +++ b/tests/repeat/repeat.aspl @@ -0,0 +1,8 @@ +repeat(100, i = 0){ + print(i) +} + +var int amount = 10 +repeat(amount){ + print("Hello") +} \ No newline at end of file diff --git a/tests/statics/Child.aspl b/tests/statics/Child.aspl new file mode 100644 index 0000000..d153c84 --- /dev/null +++ b/tests/statics/Child.aspl @@ -0,0 +1,15 @@ +[static] +class Child extends Parent { + + [public] + [static] + property int first = 42 + + [public] + [static] + method one(){ + print("child: one") + print("self: " + self:first) + } + +} \ No newline at end of file diff --git a/tests/statics/Parent.aspl b/tests/statics/Parent.aspl new file mode 100644 index 0000000..4e916b6 --- /dev/null +++ b/tests/statics/Parent.aspl @@ -0,0 +1,20 @@ +[static] +class Parent { + + [public] + [static] + property int first = 42 + + [public] + [static] + method one(){ + print("parent: one") + } + + [public] + [static] + method two(){ + print("parent: two") + } + +} \ No newline at end of file diff --git a/tests/statics/main.aspl b/tests/statics/main.aspl new file mode 100644 index 0000000..e96bb37 --- /dev/null +++ b/tests/statics/main.aspl @@ -0,0 +1,8 @@ +Child:one() +assert Child:first == 42 +Child:first = 21 +assert Child:first == 21 +Parent:two() +assert Parent:first == 42 +Parent:first = 21 +assert Parent:first == 21 \ No newline at end of file diff --git a/tests/unicode/unicode.aspl b/tests/unicode/unicode.aspl new file mode 100644 index 0000000..9efa55a --- /dev/null +++ b/tests/unicode/unicode.aspl @@ -0,0 +1,12 @@ +print("--- Hello World in different languages ---") +print("Russian: Привет мир!") +print("Ukrainian: Привіт світ!") +print("Polish: Witaj świecie!") +print("Chinese: 你好世界!") +print("Japanese: こんにちは世界!") +print("Korean: 안녕 세상!") +print("Emoji: 👋🌍") + +assert "\u000a" == "\n" +assert "\uc548" == "안" +assert "\uc0c1" == "상" \ No newline at end of file diff --git a/tests/variables/variables.aspl b/tests/variables/variables.aspl new file mode 100644 index 0000000..c5d1b4a --- /dev/null +++ b/tests/variables/variables.aspl @@ -0,0 +1,7 @@ +var string a = "Hello" +var b = "World!" +var string|null c = "World" +c = b +print(a) +print(b) +print(c) \ No newline at end of file diff --git a/tests/while/while.aspl b/tests/while/while.aspl new file mode 100644 index 0000000..77e2b3b --- /dev/null +++ b/tests/while/while.aspl @@ -0,0 +1,8 @@ +var int i = 0 +while(i != 100){ + print(i) + i++ +} +while(false){ + assert false +} \ No newline at end of file diff --git a/thirdparty/appbundle/README.md b/thirdparty/appbundle/README.md new file mode 100644 index 0000000..43b4c93 --- /dev/null +++ b/thirdparty/appbundle/README.md @@ -0,0 +1,25 @@ +# AppBundle +This is an implementation of https://github.com/Wertzui123/AppBundle translated to C. + +## License +MIT License + +Copyright (c) 2024 Wertzui123 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/thirdparty/appbundle/loader.c b/thirdparty/appbundle/loader.c new file mode 100644 index 0000000..d4d3757 --- /dev/null +++ b/thirdparty/appbundle/loader.c @@ -0,0 +1,99 @@ +#include "thirdparty/appbundle/loader.h" + +#include + +char appbundle_Bundle_contains_resource(appbundle_Bundle* bundle, char* name) { + for (int i = 0; i < bundle->resources_length; i++) { + if (strcmp(bundle->resources[i].name, name) == 0) { + return 1; + } + } + return 0; +} + +appbundle_Resource appbundle_Bundle_get_resource(appbundle_Bundle* bundle, char* name) { + for (int i = 0; i < bundle->resources_length; i++) { + if (strcmp(bundle->resources[i].name, name) == 0) { + return bundle->resources[i]; + } + } + return (appbundle_Resource) { 0 }; +} + +#ifdef _WIN32 +#include +#elif __APPLE__ +#include +#else +#include +#endif + +char* appbundle_util_get_name_of_executable() { +#ifdef _WIN32 + char* name = APPBUNDLE_MALLOC(1024); + GetModuleFileName(NULL, name, 1024); + return name; +#elif __APPLE__ + char* name = APPBUNDLE_MALLOC(1024); + pid_t pid = getpid(); + int ret = proc_pidpath(pid, name, 1024); + if (ret <= 0) { + APPBUNDLE_ERROR("proc_pidpath failed with pid %d: %d\n", pid, ret); + } + return name; +#else + char* name = APPBUNDLE_MALLOC(1024); + ssize_t count = readlink("/proc/self/exe", name, 1024); + if (count == -1) { + APPBUNDLE_ERROR("readlink failed with: %s", strerror(errno)); + } + name[count] = '\0'; + return name; +#endif +} + +appbundle_Bundle* appbundle_Bundle_load() { + appbundle_Bundle* bundle = APPBUNDLE_MALLOC(sizeof(appbundle_Bundle)); + bundle->resources_length = 0; + bundle->resources = APPBUNDLE_MALLOC(sizeof(appbundle_Resource) * bundle->resources_length); + + FILE* file = fopen(appbundle_util_get_name_of_executable(), "rb"); + fseek(file, -8, SEEK_END); + unsigned long long bytes_length = 0; + fread(&bytes_length, 8, 1, file); + fseek(file, 0, SEEK_END); + unsigned long long start_position = ftell(file) - bytes_length - 8; // TODO: Why is this -8 required? + fseek(file, start_position, SEEK_SET); + unsigned char* bytes = APPBUNDLE_MALLOC(bytes_length); + fread(bytes, 1, bytes_length, file); + fclose(file); + + unsigned long long position = 0; + while (position < bytes_length) { + unsigned char resource_type = bytes[position++]; // 0 = binary, 2 = text; ignored in this implementation + unsigned long long resource_name_length; + memcpy(&resource_name_length, bytes + position, sizeof(unsigned long long)); + position += 8; + char* resource_name = APPBUNDLE_MALLOC(resource_name_length + 1); + for (int i = 0; i < resource_name_length; i++) { + resource_name[i] = bytes[position + i]; + } + resource_name[resource_name_length] = '\0'; + position += resource_name_length; + unsigned long long resource_data_length; + memcpy(&resource_data_length, bytes + position, sizeof(unsigned long long)); + position += 8; + unsigned char* resource_data = APPBUNDLE_MALLOC(resource_data_length); + for (int i = 0; i < resource_data_length; i++) { + resource_data[i] = bytes[position + i]; + } + position += resource_data_length; + bundle->resources_length++; + bundle->resources = APPBUNDLE_REALLOC(bundle->resources, sizeof(appbundle_Resource) * bundle->resources_length); + bundle->resources[bundle->resources_length - 1].name = resource_name; + bundle->resources[bundle->resources_length - 1].data = resource_data; + bundle->resources[bundle->resources_length - 1].length = resource_data_length; + } + + return bundle; +} \ No newline at end of file diff --git a/thirdparty/appbundle/loader.h b/thirdparty/appbundle/loader.h new file mode 100644 index 0000000..20dc69b --- /dev/null +++ b/thirdparty/appbundle/loader.h @@ -0,0 +1,34 @@ +#ifndef APPBUNDLE_MALLOC +#define APPBUNDLE_MALLOC malloc +#define STDLIB_INCLUDED +#endif +#ifndef APPBUNDLE_REALLOC +#define APPBUNDLE_REALLOC realloc +#define STDLIB_INCLUDED +#endif +#ifndef APPBUNDLE_FREE +#define APPBUNDLE_FREE free +#define STDLIB_INCLUDED +#endif +#ifdef STDLIB_INCLUDED +#include +#endif +#ifndef APPBUNDLE_ERROR +#include +#define APPBUNDLE_ERROR(...) { fprintf(stderr, __VA_ARGS__); exit(1); } +#endif + +typedef struct appbundle_Resource { + char* name; + void* data; + unsigned int length; +} appbundle_Resource; + +typedef struct appbundle_Bundle { + appbundle_Resource* resources; + unsigned int resources_length; +} appbundle_Bundle; + +char appbundle_Bundle_contains_resource(appbundle_Bundle* bundle, char* name); +appbundle_Resource appbundle_Bundle_get_resource(appbundle_Bundle* bundle, char* name); +appbundle_Bundle* appbundle_Bundle_load(); \ No newline at end of file diff --git a/thirdparty/cwsc/README.md b/thirdparty/cwsc/README.md new file mode 100644 index 0000000..901f5b4 --- /dev/null +++ b/thirdparty/cwsc/README.md @@ -0,0 +1,9 @@ +# C WebSocket Client (CWSC) +A bare but useful single-file WebSocket client library written in C. + +This implementation is very roughly based on [boomanaiden154's WebSocket library](https://github.com/boomanaiden154/websocket) as well as the [`net.websocket`](https://github.com/vlang/v/tree/master/vlib/net/websocket) module of the V standard library. + +Moreover, it uses [TLSe](https://github.com/eduardsui/tlse) for TLS (previously known as SSL) support. + +## License +This project is licensed under the [MIT License](LICENSE). \ No newline at end of file diff --git a/thirdparty/cwsc/cwsc.h b/thirdparty/cwsc/cwsc.h new file mode 100644 index 0000000..ed50ba3 --- /dev/null +++ b/thirdparty/cwsc/cwsc.h @@ -0,0 +1,355 @@ +#ifndef CWSC_H +#define CWSC_H + +#include + +#include "thirdparty/tlse.h" + +typedef struct cwsc_Url { + char protocol[10]; + char host[256]; + char path[1024]; +} cwsc_Url; + +typedef enum cwsc_Opcode { + CWSC_OPCODE_CONTINUATION = 0x0, + CWSC_OPCODE_TEXT = 0x1, + CWSC_OPCODE_BINARY = 0x2, + CWSC_OPCODE_CLOSE = 0x8, + CWSC_OPCODE_PING = 0x9, + CWSC_OPCODE_PONG = 0xa +} cwsc_Opcode; + +typedef struct cwsc_Message { + cwsc_Opcode opcode; + unsigned char* message; + int length; +} cwsc_Message; + +typedef struct cwsc_WebSocket { + void* user_data; + int sockfd; + struct TLSContext* tls_context; + cwsc_Url url; + void (*on_open)(struct cwsc_WebSocket*); + void (*on_message)(struct cwsc_WebSocket*, cwsc_Opcode, const unsigned char*, int); + void (*on_error)(struct cwsc_WebSocket*, const char*, int); + void (*on_close)(struct cwsc_WebSocket*, int, const char*, int); +} cwsc_WebSocket; + +cwsc_Url cwsc_parse_url(const char* input); +int cwsc_init_socket(const char* address, int port); +int cwsc_init_tls(int sockfd, struct TLSContext* tls_context); +int cwsc_connect(cwsc_WebSocket* ws, const char* address, int port); +cwsc_Message cwsc_internal_read_message(cwsc_WebSocket* ws); +unsigned char* cwsc_internal_create_masking_key(); +void cwsc_send_control_frame(cwsc_WebSocket* ws, cwsc_Opcode opcode, const char* message, int length); +void cwsc_send_message(cwsc_WebSocket* ws, const char* message); +void cwsc_listen(cwsc_WebSocket* ws); + +#endif + +#ifdef CWSC_IMPLEMENTATION + +#include +#include +#include + +#ifndef CWSC_MALLOC +#include +#define CWSC_MALLOC malloc +#define CWSC_REALLOC realloc +#define CWSC_FREE free +#endif + +#include "thirdparty/tlse_adapter.c" + +cwsc_Url cwsc_parse_url(const char* input) { + cwsc_Url toReturn; + memset(&toReturn, 0, sizeof(cwsc_Url)); + const char* protocolEnd = strstr(input, "://"); + if (protocolEnd == NULL) { + return toReturn; // Invalid URL + } + strncpy(toReturn.protocol, input, protocolEnd - input); + protocolEnd += 3; + + const char* hostEnd = strchr(protocolEnd, '/'); + if (hostEnd == NULL) { + strcpy(toReturn.host, protocolEnd); + } + else { + strncpy(toReturn.host, protocolEnd, hostEnd - protocolEnd); + strcpy(toReturn.path, hostEnd); + } + return toReturn; +} + +int cwsc_init_socket(const char* address, int port) { + return cwsc_tlse_wrapper_connect_socket(address, port); +} + +int cwsc_init_tls(int sockfd, struct TLSContext* tls_context) { + cwsc_tlse_wrapper_connect_tls(sockfd, tls_context); + return 0; // TODO: Don't return 0 if an error occurred +} + +int cwsc_connect(cwsc_WebSocket* ws, const char* address, int port) { + ws->url = cwsc_parse_url(address); + int sockfd; + if ((sockfd = cwsc_init_socket(ws->url.host, port)) == -1) { + return 1; + } + ws->sockfd = sockfd; + if (cwsc_init_tls(sockfd, ws->tls_context) != 0) { + return 2; + } + + char request[2048]; + snprintf(request, sizeof(request), + "GET %s HTTP/1.1\r\n" + "Host: %s\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Origin: http://localhost\r\n" + "Sec-WebSocket-Protocol: chat, superchat\r\n" + "Sec-WebSocket-Version: 13\r\n\r\n", + address, ws->url.host); + cwsc_tlse_wrapper_write_tls(sockfd, ws->tls_context, (const unsigned char*)request, strlen(request)); + + char handshakeBuffer[1024]; + int totalBytesRead = 0; + char buffer[1]; + for (totalBytesRead = 0; totalBytesRead < 1024; totalBytesRead++) { + int bytesRead = cwsc_tlse_wrapper_read_tls(sockfd, ws->tls_context, buffer, 1); + if (bytesRead == 0) { + return 3; + } + handshakeBuffer[totalBytesRead] = buffer[0]; + if (totalBytesRead > 4 && handshakeBuffer[totalBytesRead] == '\n' && handshakeBuffer[totalBytesRead - 1] == '\r' && handshakeBuffer[totalBytesRead - 2] == '\n' && handshakeBuffer[totalBytesRead - 3] == '\r') { + break; + } + } + handshakeBuffer[totalBytesRead + 1] = '\0'; + // TODO: Validate handshake + + if (ws->on_open) { + ws->on_open(ws); + } + + return 0; +} + +cwsc_Message cwsc_internal_read_message(cwsc_WebSocket* ws) { + int sockfd = ws->sockfd; + struct TLSContext* tls_context = ws->tls_context; + char socketBuffer[2]; + int bytesReceived1 = cwsc_tlse_wrapper_read_tls(sockfd, tls_context, socketBuffer, sizeof(socketBuffer)); + cwsc_Opcode opcode = (cwsc_Opcode)(socketBuffer[0] & 0b00001111); // get the four least significant bits + unsigned char hasMask = (socketBuffer[1] & 0b10000000) == 0b10000000; + uint8_t payloadLengthSimple = socketBuffer[1] & 0b01111111; // get the seven least significant bits + uint64_t payloadLength; + if (payloadLengthSimple <= 125) { + payloadLength = payloadLengthSimple; + } + else if (payloadLengthSimple == 126) { + uint8_t payloadLengthBuffer[2]; + cwsc_tlse_wrapper_read_tls(sockfd, tls_context, payloadLengthBuffer, sizeof(payloadLengthBuffer)); + payloadLength = (uint64_t)payloadLengthBuffer[0] << 8; + payloadLength += (uint64_t)payloadLengthBuffer[1]; + } + else if (payloadLengthSimple == 127) { + uint8_t payloadLengthBuffer[8]; + cwsc_tlse_wrapper_read_tls(sockfd, tls_context, payloadLengthBuffer, sizeof(payloadLengthBuffer)); + payloadLength = (uint64_t)payloadLengthBuffer[0] << 56; + payloadLength += (uint64_t)payloadLengthBuffer[1] << 48; + payloadLength += (uint64_t)payloadLengthBuffer[2] << 40; + payloadLength += (uint64_t)payloadLengthBuffer[3] << 32; + payloadLength += (uint64_t)payloadLengthBuffer[4] << 24; + payloadLength += (uint64_t)payloadLengthBuffer[5] << 16; + payloadLength += (uint64_t)payloadLengthBuffer[6] << 8; + payloadLength += (uint64_t)payloadLengthBuffer[7]; + } + char maskingKey[4]; + if (hasMask) { + cwsc_tlse_wrapper_read_tls(sockfd, tls_context, maskingKey, sizeof(maskingKey)); // TODO: Check if this is correct + } + unsigned char* textBuffer = (unsigned char*)CWSC_MALLOC(payloadLength + 1); + uint32_t bytesReceived = 0; + while (bytesReceived < payloadLength) { + if (ws->sockfd == -1) { + cwsc_Message message; + message.opcode = -1; + return message; + } + bytesReceived += cwsc_tlse_wrapper_read_tls(sockfd, tls_context, textBuffer + bytesReceived, payloadLength - bytesReceived); + } + if (hasMask) { + for (uint32_t i = 0; i < payloadLength; i++) { + textBuffer[i] ^= maskingKey[i % 4] & 0xff; + } + } + textBuffer[payloadLength] = '\0'; + cwsc_Message message; + message.opcode = opcode; + message.message = textBuffer; + message.length = payloadLength; + return message; +} + +unsigned char* cwsc_internal_create_masking_key() { + unsigned char* key = (unsigned char*)CWSC_MALLOC(4); + key[0] = (unsigned char)rand(); + key[1] = (unsigned char)rand(); + key[2] = (unsigned char)rand(); + key[3] = (unsigned char)rand(); + return key; +} + +void cwsc_send_control_frame(cwsc_WebSocket* ws, cwsc_Opcode opcode, const char* message, int length) { + int headerLength = 6; + int frameLength = headerLength + length; + unsigned char* frame = (unsigned char*)CWSC_MALLOC(frameLength); + unsigned char* maskingKey = cwsc_internal_create_masking_key(); + frame[0] = 0b10000000 + opcode; + frame[1] = (uint8_t)length; + if (opcode == CWSC_OPCODE_CLOSE) { + if (length >= 2) { + for (int i = 0; i < length; i++) { + frame[6 + i] = (message[i] ^ maskingKey[i % 4]) & 0xff; + } + } + } + else { + for (int i = 0; i < length; i++) { + frame[headerLength + i] = (message[i] ^ maskingKey[i % 4]) & 0xff; + } + } + cwsc_tlse_wrapper_write_tls(ws->sockfd, ws->tls_context, frame, frameLength); + CWSC_FREE(maskingKey); + CWSC_FREE(frame); +} + +void cwsc_send_message(cwsc_WebSocket* ws, const char* message) { + // Six bytes for framing data, four bytes for length data (if necessary), and one character for each byte + size_t messageLen = strlen(message); + char toSend[10 + messageLen]; + // Set message to not fragmented, type as text, and no special flags + toSend[0] = 0b10000001; + uint8_t offset = 0; + // Set the mask to true on each of these (first bit set to 1) + if (messageLen <= 125) { + toSend[1] = 0b10000000 + messageLen; + } + else if (messageLen >= 125 && messageLen <= SHRT_MAX) { + // Set mask to true and length to 126 + toSend[1] = 0b11111110; + toSend[2] = (uint8_t)(messageLen >> 8); + toSend[3] = (uint8_t)(messageLen); + offset = 2; + } + else { + // Set the length to 127 and the mask to true + toSend[1] = 0xff; + toSend[2] = (uint8_t)(messageLen >> 56); + toSend[3] = (uint8_t)(messageLen >> 48); + toSend[4] = (uint8_t)(messageLen >> 40); + toSend[5] = (uint8_t)(messageLen >> 32); + toSend[6] = (uint8_t)(messageLen >> 24); + toSend[7] = (uint8_t)(messageLen >> 16); + toSend[8] = (uint8_t)(messageLen >> 8); + toSend[9] = (uint8_t)(messageLen); + offset = 8; + } + // Generate the masking key + unsigned char* masking_key = cwsc_internal_create_masking_key(); + // Copy the masking key to the toSend array + for (size_t i = 0; i < 4; i++) { + toSend[2 + offset + i] = masking_key[i]; + } + CWSC_FREE(masking_key); + + // Add the data to the toSend array + for (size_t i = 0; i < messageLen; i++) { + // XOR the byte with the masking key to "mask" the message + toSend[6 + offset + i] = message[i] ^ toSend[2 + offset + (i % 4)]; + } + + cwsc_tlse_wrapper_write_tls(ws->sockfd, ws->tls_context, (const unsigned char*)toSend, offset + 6 + messageLen); +} + +void cwsc_listen(cwsc_WebSocket* ws) { + while (1) { + cwsc_Message message = cwsc_internal_read_message(ws); + switch (message.opcode) { + case CWSC_OPCODE_CONTINUATION: + // TODO: Report an error + break; + case CWSC_OPCODE_TEXT: + case CWSC_OPCODE_BINARY: + if (ws->on_message) { + ws->on_message(ws, message.opcode, message.message, message.length); + } + break; + case CWSC_OPCODE_CLOSE: + if (ws->on_close) { + if (message.length >= 2) { + uint16_t code = (uint16_t)message.message[0] << 8 | (uint16_t)message.message[1]; + ws->on_close(ws, code, (const char*)(message.message + 2), message.length - 2); + } + else { + ws->on_close(ws, 0, "", 0); + } + } + cwsc_send_control_frame(ws, CWSC_OPCODE_CLOSE, (const char*)message.message, message.length); + close(ws->sockfd); + ws->sockfd = -1; + break; + case CWSC_OPCODE_PING: + cwsc_send_control_frame(ws, CWSC_OPCODE_PONG, (const char*)message.message, message.length); + break; + case CWSC_OPCODE_PONG: + // ignore for now + break; + } + CWSC_FREE(message.message); + if (ws->sockfd == -1) { + break; + } + } + return; +} + +void cwsc_disconnect(cwsc_WebSocket* ws, const char* message, int code) { + if (ws->sockfd <= 0) { + if (ws->on_error) { + ws->on_error(ws, "Socket already closed", -1); + } + return; + } + + int message_len = (message != NULL) ? strlen(message) : 0; + int close_frame_len = 2 + message_len; + unsigned char* close_frame = (unsigned char*)CWSC_MALLOC(close_frame_len); + + close_frame[0] = (unsigned char)(code >> 8); + close_frame[1] = (unsigned char)(code & 0xFF); + + if (message) { + memcpy(close_frame + 2, message, message_len); + } + + cwsc_send_control_frame(ws, CWSC_OPCODE_CLOSE, (const char*)close_frame, close_frame_len); + + CWSC_FREE(close_frame); + + close(ws->sockfd); + ws->sockfd = -1; + + if (ws->on_close) { + ws->on_close(ws, code, message, message_len); + } +} + +#endif \ No newline at end of file diff --git a/thirdparty/cwsc/thirdparty/libtomcrypt.c b/thirdparty/cwsc/thirdparty/libtomcrypt.c new file mode 100644 index 0000000..33b9457 --- /dev/null +++ b/thirdparty/cwsc/thirdparty/libtomcrypt.c @@ -0,0 +1,34769 @@ +#define CRYPT 0x0117 +#define LTC_NO_ROLC + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://math.libtomcrypt.com + */ +#ifndef BN_H_ +#define BN_H_ + +#include +#include +#include +#include + +#if !(defined(LTM1) && defined(LTM2) && defined(LTM3)) + #if defined(LTM2) + #define LTM3 + #endif + #if defined(LTM1) + #define LTM2 + #endif + #define LTM1 + + #if defined(LTM_ALL) + #define BN_ERROR_C + #define BN_FAST_MP_INVMOD_C + #define BN_FAST_MP_MONTGOMERY_REDUCE_C + #define BN_FAST_S_MP_MUL_DIGS_C + #define BN_FAST_S_MP_MUL_HIGH_DIGS_C + #define BN_FAST_S_MP_SQR_C + #define BN_MP_2EXPT_C + #define BN_MP_ABS_C + #define BN_MP_ADD_C + #define BN_MP_ADD_D_C + #define BN_MP_ADDMOD_C + #define BN_MP_AND_C + #define BN_MP_CLAMP_C + #define BN_MP_CLEAR_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_CMP_C + #define BN_MP_CMP_D_C + #define BN_MP_CMP_MAG_C + #define BN_MP_CNT_LSB_C + #define BN_MP_COPY_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_C + #define BN_MP_DIV_2_C + #define BN_MP_DIV_2D_C + #define BN_MP_DIV_3_C + #define BN_MP_DIV_D_C + #define BN_MP_DR_IS_MODULUS_C + #define BN_MP_DR_REDUCE_C + #define BN_MP_DR_SETUP_C + #define BN_MP_EXCH_C + #define BN_MP_EXPORT_C + #define BN_MP_EXPT_D_C + #define BN_MP_EXPT_D_EX_C + #define BN_MP_EXPTMOD_C + #define BN_MP_EXPTMOD_FAST_C + #define BN_MP_EXTEUCLID_C + #define BN_MP_FREAD_C + #define BN_MP_FWRITE_C + #define BN_MP_GCD_C + #define BN_MP_GET_INT_C + #define BN_MP_GET_LONG_C + #define BN_MP_GET_LONG_LONG_C + #define BN_MP_GROW_C + #define BN_MP_IMPORT_C + #define BN_MP_INIT_C + #define BN_MP_INIT_COPY_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_INIT_SET_C + #define BN_MP_INIT_SET_INT_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_INVMOD_C + #define BN_MP_INVMOD_SLOW_C + #define BN_MP_IS_SQUARE_C + #define BN_MP_JACOBI_C + #define BN_MP_KARATSUBA_MUL_C + #define BN_MP_KARATSUBA_SQR_C + #define BN_MP_LCM_C + #define BN_MP_LSHD_C + #define BN_MP_MOD_C + #define BN_MP_MOD_2D_C + #define BN_MP_MOD_D_C + #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + #define BN_MP_MONTGOMERY_REDUCE_C + #define BN_MP_MONTGOMERY_SETUP_C + #define BN_MP_MUL_C + #define BN_MP_MUL_2_C + #define BN_MP_MUL_2D_C + #define BN_MP_MUL_D_C + #define BN_MP_MULMOD_C + #define BN_MP_N_ROOT_C + #define BN_MP_N_ROOT_EX_C + #define BN_MP_NEG_C + #define BN_MP_OR_C + #define BN_MP_PRIME_FERMAT_C + #define BN_MP_PRIME_IS_DIVISIBLE_C + #define BN_MP_PRIME_IS_PRIME_C + #define BN_MP_PRIME_MILLER_RABIN_C + #define BN_MP_PRIME_NEXT_PRIME_C + #define BN_MP_PRIME_RABIN_MILLER_TRIALS_C + #define BN_MP_PRIME_RANDOM_EX_C + #define BN_MP_RADIX_SIZE_C + #define BN_MP_RADIX_SMAP_C + #define BN_MP_RAND_C + #define BN_MP_READ_RADIX_C + #define BN_MP_READ_SIGNED_BIN_C + #define BN_MP_READ_UNSIGNED_BIN_C + #define BN_MP_REDUCE_C + #define BN_MP_REDUCE_2K_C + #define BN_MP_REDUCE_2K_L_C + #define BN_MP_REDUCE_2K_SETUP_C + #define BN_MP_REDUCE_2K_SETUP_L_C + #define BN_MP_REDUCE_IS_2K_C + #define BN_MP_REDUCE_IS_2K_L_C + #define BN_MP_REDUCE_SETUP_C + #define BN_MP_RSHD_C + #define BN_MP_SET_C + #define BN_MP_SET_INT_C + #define BN_MP_SET_LONG_C + #define BN_MP_SET_LONG_LONG_C + #define BN_MP_SHRINK_C + #define BN_MP_SIGNED_BIN_SIZE_C + #define BN_MP_SQR_C + #define BN_MP_SQRMOD_C + #define BN_MP_SQRT_C + #define BN_MP_SQRTMOD_PRIME_C + #define BN_MP_SUB_C + #define BN_MP_SUB_D_C + #define BN_MP_SUBMOD_C + #define BN_MP_TO_SIGNED_BIN_C + #define BN_MP_TO_SIGNED_BIN_N_C + #define BN_MP_TO_UNSIGNED_BIN_C + #define BN_MP_TO_UNSIGNED_BIN_N_C + #define BN_MP_TOOM_MUL_C + #define BN_MP_TOOM_SQR_C + #define BN_MP_TORADIX_C + #define BN_MP_TORADIX_N_C + #define BN_MP_UNSIGNED_BIN_SIZE_C + #define BN_MP_XOR_C + #define BN_MP_ZERO_C + #define BN_PRIME_TAB_C + #define BN_REVERSE_C + #define BN_S_MP_ADD_C + #define BN_S_MP_EXPTMOD_C + #define BN_S_MP_MUL_DIGS_C + #define BN_S_MP_MUL_HIGH_DIGS_C + #define BN_S_MP_SQR_C + #define BN_S_MP_SUB_C + #define BNCORE_C + #endif + + #if defined(BN_ERROR_C) + #define BN_MP_ERROR_TO_STRING_C + #endif + + #if defined(BN_FAST_MP_INVMOD_C) + #define BN_MP_ISEVEN_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_COPY_C + #define BN_MP_MOD_C + #define BN_MP_SET_C + #define BN_MP_DIV_2_C + #define BN_MP_ISODD_C + #define BN_MP_SUB_C + #define BN_MP_CMP_C + #define BN_MP_ISZERO_C + #define BN_MP_CMP_D_C + #define BN_MP_ADD_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_FAST_MP_MONTGOMERY_REDUCE_C) + #define BN_MP_GROW_C + #define BN_MP_RSHD_C + #define BN_MP_CLAMP_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_FAST_S_MP_MUL_DIGS_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_FAST_S_MP_SQR_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_2EXPT_C) + #define BN_MP_ZERO_C + #define BN_MP_GROW_C + #endif + + #if defined(BN_MP_ABS_C) + #define BN_MP_COPY_C + #endif + + #if defined(BN_MP_ADD_C) + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_ADD_D_C) + #define BN_MP_GROW_C + #define BN_MP_SUB_D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_ADDMOD_C) + #define BN_MP_INIT_C + #define BN_MP_ADD_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C + #endif + + #if defined(BN_MP_AND_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_CLAMP_C) + #endif + + #if defined(BN_MP_CLEAR_C) + #endif + + #if defined(BN_MP_CLEAR_MULTI_C) + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_CMP_C) + #define BN_MP_CMP_MAG_C + #endif + + #if defined(BN_MP_CMP_D_C) + #endif + + #if defined(BN_MP_CMP_MAG_C) + #endif + + #if defined(BN_MP_CNT_LSB_C) + #define BN_MP_ISZERO_C + #endif + + #if defined(BN_MP_COPY_C) + #define BN_MP_GROW_C + #endif + + #if defined(BN_MP_COUNT_BITS_C) + #endif + + #if defined(BN_MP_DIV_C) + #define BN_MP_ISZERO_C + #define BN_MP_CMP_MAG_C + #define BN_MP_COPY_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_SET_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_ABS_C + #define BN_MP_MUL_2D_C + #define BN_MP_CMP_C + #define BN_MP_SUB_C + #define BN_MP_ADD_C + #define BN_MP_DIV_2D_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_INIT_C + #define BN_MP_INIT_COPY_C + #define BN_MP_LSHD_C + #define BN_MP_RSHD_C + #define BN_MP_MUL_D_C + #define BN_MP_CLAMP_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_DIV_2_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_DIV_2D_C) + #define BN_MP_COPY_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_C + #define BN_MP_MOD_2D_C + #define BN_MP_CLEAR_C + #define BN_MP_RSHD_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #endif + + #if defined(BN_MP_DIV_3_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_DIV_D_C) + #define BN_MP_ISZERO_C + #define BN_MP_COPY_C + #define BN_MP_DIV_2D_C + #define BN_MP_DIV_3_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_DR_IS_MODULUS_C) + #endif + + #if defined(BN_MP_DR_REDUCE_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_DR_SETUP_C) + #endif + + #if defined(BN_MP_EXCH_C) + #endif + + #if defined(BN_MP_EXPORT_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_2D_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_EXPT_D_C) + #define BN_MP_EXPT_D_EX_C + #endif + + #if defined(BN_MP_EXPT_D_EX_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_SET_C + #define BN_MP_MUL_C + #define BN_MP_CLEAR_C + #define BN_MP_SQR_C + #endif + + #if defined(BN_MP_EXPTMOD_C) + #define BN_MP_INIT_C + #define BN_MP_INVMOD_C + #define BN_MP_CLEAR_C + #define BN_MP_ABS_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_REDUCE_IS_2K_L_C + #define BN_S_MP_EXPTMOD_C + #define BN_MP_DR_IS_MODULUS_C + #define BN_MP_REDUCE_IS_2K_C + #define BN_MP_ISODD_C + #define BN_MP_EXPTMOD_FAST_C + #endif + + #if defined(BN_MP_EXPTMOD_FAST_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_INIT_C + #define BN_MP_CLEAR_C + #define BN_MP_MONTGOMERY_SETUP_C + #define BN_FAST_MP_MONTGOMERY_REDUCE_C + #define BN_MP_MONTGOMERY_REDUCE_C + #define BN_MP_DR_SETUP_C + #define BN_MP_DR_REDUCE_C + #define BN_MP_REDUCE_2K_SETUP_C + #define BN_MP_REDUCE_2K_C + #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + #define BN_MP_MULMOD_C + #define BN_MP_SET_C + #define BN_MP_MOD_C + #define BN_MP_COPY_C + #define BN_MP_SQR_C + #define BN_MP_MUL_C + #define BN_MP_EXCH_C + #endif + + #if defined(BN_MP_EXTEUCLID_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_SET_C + #define BN_MP_COPY_C + #define BN_MP_ISZERO_C + #define BN_MP_DIV_C + #define BN_MP_MUL_C + #define BN_MP_SUB_C + #define BN_MP_NEG_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_FREAD_C) + #define BN_MP_ZERO_C + #define BN_MP_S_RMAP_C + #define BN_MP_MUL_D_C + #define BN_MP_ADD_D_C + #define BN_MP_CMP_D_C + #endif + + #if defined(BN_MP_FWRITE_C) + #define BN_MP_RADIX_SIZE_C + #define BN_MP_TORADIX_C + #endif + + #if defined(BN_MP_GCD_C) + #define BN_MP_ISZERO_C + #define BN_MP_ABS_C + #define BN_MP_INIT_COPY_C + #define BN_MP_CNT_LSB_C + #define BN_MP_DIV_2D_C + #define BN_MP_CMP_MAG_C + #define BN_MP_EXCH_C + #define BN_S_MP_SUB_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_GET_INT_C) + #endif + + #if defined(BN_MP_GET_LONG_C) + #endif + + #if defined(BN_MP_GET_LONG_LONG_C) + #endif + + #if defined(BN_MP_GROW_C) + #endif + + #if defined(BN_MP_IMPORT_C) + #define BN_MP_ZERO_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_INIT_C) + #endif + + #if defined(BN_MP_INIT_COPY_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_COPY_C + #endif + + #if defined(BN_MP_INIT_MULTI_C) + #define BN_MP_ERR_C + #define BN_MP_INIT_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_INIT_SET_C) + #define BN_MP_INIT_C + #define BN_MP_SET_C + #endif + + #if defined(BN_MP_INIT_SET_INT_C) + #define BN_MP_INIT_C + #define BN_MP_SET_INT_C + #endif + + #if defined(BN_MP_INIT_SIZE_C) + #define BN_MP_INIT_C + #endif + + #if defined(BN_MP_INVMOD_C) + #define BN_MP_ISZERO_C + #define BN_MP_ISODD_C + #define BN_FAST_MP_INVMOD_C + #define BN_MP_INVMOD_SLOW_C + #endif + + #if defined(BN_MP_INVMOD_SLOW_C) + #define BN_MP_ISZERO_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_C + #define BN_MP_COPY_C + #define BN_MP_ISEVEN_C + #define BN_MP_SET_C + #define BN_MP_DIV_2_C + #define BN_MP_ISODD_C + #define BN_MP_ADD_C + #define BN_MP_SUB_C + #define BN_MP_CMP_C + #define BN_MP_CMP_D_C + #define BN_MP_CMP_MAG_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_IS_SQUARE_C) + #define BN_MP_MOD_D_C + #define BN_MP_INIT_SET_INT_C + #define BN_MP_MOD_C + #define BN_MP_GET_INT_C + #define BN_MP_SQRT_C + #define BN_MP_SQR_C + #define BN_MP_CMP_MAG_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_JACOBI_C) + #define BN_MP_CMP_D_C + #define BN_MP_ISZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_CNT_LSB_C + #define BN_MP_DIV_2D_C + #define BN_MP_MOD_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_KARATSUBA_MUL_C) + #define BN_MP_MUL_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_S_MP_ADD_C + #define BN_MP_ADD_C + #define BN_S_MP_SUB_C + #define BN_MP_LSHD_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_KARATSUBA_SQR_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_SQR_C + #define BN_S_MP_ADD_C + #define BN_S_MP_SUB_C + #define BN_MP_LSHD_C + #define BN_MP_ADD_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_LCM_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_GCD_C + #define BN_MP_CMP_MAG_C + #define BN_MP_DIV_C + #define BN_MP_MUL_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_LSHD_C) + #define BN_MP_GROW_C + #define BN_MP_RSHD_C + #endif + + #if defined(BN_MP_MOD_C) + #define BN_MP_INIT_C + #define BN_MP_DIV_C + #define BN_MP_CLEAR_C + #define BN_MP_ISZERO_C + #define BN_MP_EXCH_C + #define BN_MP_ADD_C + #endif + + #if defined(BN_MP_MOD_2D_C) + #define BN_MP_ZERO_C + #define BN_MP_COPY_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_MOD_D_C) + #define BN_MP_DIV_D_C + #endif + + #if defined(BN_MP_MONTGOMERY_CALC_NORMALIZATION_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_2EXPT_C + #define BN_MP_SET_C + #define BN_MP_MUL_2_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_MONTGOMERY_REDUCE_C) + #define BN_FAST_MP_MONTGOMERY_REDUCE_C + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #define BN_MP_RSHD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_MONTGOMERY_SETUP_C) + #endif + + #if defined(BN_MP_MUL_C) + #define BN_MP_TOOM_MUL_C + #define BN_MP_KARATSUBA_MUL_C + #define BN_FAST_S_MP_MUL_DIGS_C + #define BN_S_MP_MUL_C + #define BN_S_MP_MUL_DIGS_C + #endif + + #if defined(BN_MP_MUL_2_C) + #define BN_MP_GROW_C + #endif + + #if defined(BN_MP_MUL_2D_C) + #define BN_MP_COPY_C + #define BN_MP_GROW_C + #define BN_MP_LSHD_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_MUL_D_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_MULMOD_C) + #define BN_MP_INIT_C + #define BN_MP_MUL_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C + #endif + + #if defined(BN_MP_N_ROOT_C) + #define BN_MP_N_ROOT_EX_C + #endif + + #if defined(BN_MP_N_ROOT_EX_C) + #define BN_MP_INIT_C + #define BN_MP_SET_C + #define BN_MP_COPY_C + #define BN_MP_EXPT_D_EX_C + #define BN_MP_MUL_C + #define BN_MP_SUB_C + #define BN_MP_MUL_D_C + #define BN_MP_DIV_C + #define BN_MP_CMP_C + #define BN_MP_SUB_D_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_NEG_C) + #define BN_MP_COPY_C + #define BN_MP_ISZERO_C + #endif + + #if defined(BN_MP_OR_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_FERMAT_C) + #define BN_MP_CMP_D_C + #define BN_MP_INIT_C + #define BN_MP_EXPTMOD_C + #define BN_MP_CMP_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_IS_DIVISIBLE_C) + #define BN_MP_MOD_D_C + #endif + + #if defined(BN_MP_PRIME_IS_PRIME_C) + #define BN_MP_CMP_D_C + #define BN_MP_PRIME_IS_DIVISIBLE_C + #define BN_MP_INIT_C + #define BN_MP_SET_C + #define BN_MP_PRIME_MILLER_RABIN_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_MILLER_RABIN_C) + #define BN_MP_CMP_D_C + #define BN_MP_INIT_COPY_C + #define BN_MP_SUB_D_C + #define BN_MP_CNT_LSB_C + #define BN_MP_DIV_2D_C + #define BN_MP_EXPTMOD_C + #define BN_MP_CMP_C + #define BN_MP_SQRMOD_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_NEXT_PRIME_C) + #define BN_MP_CMP_D_C + #define BN_MP_SET_C + #define BN_MP_SUB_D_C + #define BN_MP_ISEVEN_C + #define BN_MP_MOD_D_C + #define BN_MP_INIT_C + #define BN_MP_ADD_D_C + #define BN_MP_PRIME_MILLER_RABIN_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_RABIN_MILLER_TRIALS_C) + #endif + + #if defined(BN_MP_PRIME_RANDOM_EX_C) + #define BN_MP_READ_UNSIGNED_BIN_C + #define BN_MP_PRIME_IS_PRIME_C + #define BN_MP_SUB_D_C + #define BN_MP_DIV_2_C + #define BN_MP_MUL_2_C + #define BN_MP_ADD_D_C + #endif + + #if defined(BN_MP_RADIX_SIZE_C) + #define BN_MP_ISZERO_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_INIT_COPY_C + #define BN_MP_DIV_D_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_RADIX_SMAP_C) + #define BN_MP_S_RMAP_C + #endif + + #if defined(BN_MP_RAND_C) + #define BN_MP_ZERO_C + #define BN_MP_ADD_D_C + #define BN_MP_LSHD_C + #endif + + #if defined(BN_MP_READ_RADIX_C) + #define BN_MP_ZERO_C + #define BN_MP_S_RMAP_C + #define BN_MP_MUL_D_C + #define BN_MP_ADD_D_C + #define BN_MP_ISZERO_C + #endif + + #if defined(BN_MP_READ_SIGNED_BIN_C) + #define BN_MP_READ_UNSIGNED_BIN_C + #endif + + #if defined(BN_MP_READ_UNSIGNED_BIN_C) + #define BN_MP_GROW_C + #define BN_MP_ZERO_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_REDUCE_C) + #define BN_MP_REDUCE_SETUP_C + #define BN_MP_INIT_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_MUL_C + #define BN_S_MP_MUL_HIGH_DIGS_C + #define BN_FAST_S_MP_MUL_HIGH_DIGS_C + #define BN_MP_MOD_2D_C + #define BN_S_MP_MUL_DIGS_C + #define BN_MP_SUB_C + #define BN_MP_CMP_D_C + #define BN_MP_SET_C + #define BN_MP_LSHD_C + #define BN_MP_ADD_C + #define BN_MP_CMP_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_REDUCE_2K_C) + #define BN_MP_INIT_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_2D_C + #define BN_MP_MUL_D_C + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_REDUCE_2K_L_C) + #define BN_MP_INIT_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_2D_C + #define BN_MP_MUL_C + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_REDUCE_2K_SETUP_C) + #define BN_MP_INIT_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_2EXPT_C + #define BN_MP_CLEAR_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_REDUCE_2K_SETUP_L_C) + #define BN_MP_INIT_C + #define BN_MP_2EXPT_C + #define BN_MP_COUNT_BITS_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_REDUCE_IS_2K_C) + #define BN_MP_REDUCE_2K_C + #define BN_MP_COUNT_BITS_C + #endif + + #if defined(BN_MP_REDUCE_IS_2K_L_C) + #endif + + #if defined(BN_MP_REDUCE_SETUP_C) + #define BN_MP_2EXPT_C + #define BN_MP_DIV_C + #endif + + #if defined(BN_MP_RSHD_C) + #define BN_MP_ZERO_C + #endif + + #if defined(BN_MP_SET_C) + #define BN_MP_ZERO_C + #endif + + #if defined(BN_MP_SET_INT_C) + #define BN_MP_ZERO_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_SET_LONG_C) + #endif + + #if defined(BN_MP_SET_LONG_LONG_C) + #endif + + #if defined(BN_MP_SHRINK_C) + #endif + + #if defined(BN_MP_SIGNED_BIN_SIZE_C) + #define BN_MP_UNSIGNED_BIN_SIZE_C + #endif + + #if defined(BN_MP_SQR_C) + #define BN_MP_TOOM_SQR_C + #define BN_MP_KARATSUBA_SQR_C + #define BN_FAST_S_MP_SQR_C + #define BN_S_MP_SQR_C + #endif + + #if defined(BN_MP_SQRMOD_C) + #define BN_MP_INIT_C + #define BN_MP_SQR_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C + #endif + + #if defined(BN_MP_SQRT_C) + #define BN_MP_N_ROOT_C + #define BN_MP_ISZERO_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_DIV_C + #define BN_MP_ADD_C + #define BN_MP_DIV_2_C + #define BN_MP_CMP_MAG_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_SQRTMOD_PRIME_C) + #define BN_MP_CMP_D_C + #define BN_MP_ZERO_C + #define BN_MP_JACOBI_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_D_C + #define BN_MP_ADD_D_C + #define BN_MP_DIV_2_C + #define BN_MP_EXPTMOD_C + #define BN_MP_COPY_C + #define BN_MP_SUB_D_C + #define BN_MP_ISEVEN_C + #define BN_MP_SET_INT_C + #define BN_MP_SQRMOD_C + #define BN_MP_MULMOD_C + #define BN_MP_SET_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_SUB_C) + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_SUB_D_C) + #define BN_MP_GROW_C + #define BN_MP_ADD_D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_SUBMOD_C) + #define BN_MP_INIT_C + #define BN_MP_SUB_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C + #endif + + #if defined(BN_MP_TO_SIGNED_BIN_C) + #define BN_MP_TO_UNSIGNED_BIN_C + #endif + + #if defined(BN_MP_TO_SIGNED_BIN_N_C) + #define BN_MP_SIGNED_BIN_SIZE_C + #define BN_MP_TO_SIGNED_BIN_C + #endif + + #if defined(BN_MP_TO_UNSIGNED_BIN_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_ISZERO_C + #define BN_MP_DIV_2D_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_TO_UNSIGNED_BIN_N_C) + #define BN_MP_UNSIGNED_BIN_SIZE_C + #define BN_MP_TO_UNSIGNED_BIN_C + #endif + + #if defined(BN_MP_TOOM_MUL_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_2D_C + #define BN_MP_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_MUL_C + #define BN_MP_MUL_2_C + #define BN_MP_ADD_C + #define BN_MP_SUB_C + #define BN_MP_DIV_2_C + #define BN_MP_MUL_2D_C + #define BN_MP_MUL_D_C + #define BN_MP_DIV_3_C + #define BN_MP_LSHD_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_TOOM_SQR_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_2D_C + #define BN_MP_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_SQR_C + #define BN_MP_MUL_2_C + #define BN_MP_ADD_C + #define BN_MP_SUB_C + #define BN_MP_DIV_2_C + #define BN_MP_MUL_2D_C + #define BN_MP_MUL_D_C + #define BN_MP_DIV_3_C + #define BN_MP_LSHD_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_TORADIX_C) + #define BN_MP_ISZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_DIV_D_C + #define BN_MP_CLEAR_C + #define BN_MP_S_RMAP_C + #endif + + #if defined(BN_MP_TORADIX_N_C) + #define BN_MP_ISZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_DIV_D_C + #define BN_MP_CLEAR_C + #define BN_MP_S_RMAP_C + #endif + + #if defined(BN_MP_UNSIGNED_BIN_SIZE_C) + #define BN_MP_COUNT_BITS_C + #endif + + #if defined(BN_MP_XOR_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_ZERO_C) + #endif + + #if defined(BN_PRIME_TAB_C) + #endif + + #if defined(BN_REVERSE_C) + #endif + + #if defined(BN_S_MP_ADD_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_S_MP_EXPTMOD_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_INIT_C + #define BN_MP_CLEAR_C + #define BN_MP_REDUCE_SETUP_C + #define BN_MP_REDUCE_C + #define BN_MP_REDUCE_2K_SETUP_L_C + #define BN_MP_REDUCE_2K_L_C + #define BN_MP_MOD_C + #define BN_MP_COPY_C + #define BN_MP_SQR_C + #define BN_MP_MUL_C + #define BN_MP_SET_C + #define BN_MP_EXCH_C + #endif + + #if defined(BN_S_MP_MUL_DIGS_C) + #define BN_FAST_S_MP_MUL_DIGS_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_S_MP_MUL_HIGH_DIGS_C) + #define BN_FAST_S_MP_MUL_HIGH_DIGS_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_S_MP_SQR_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_S_MP_SUB_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BNCORE_C) + #endif + + #ifdef LTM3 + #define LTM_LAST + #endif +/* super class file for PK algos */ + +/* default ... include all MPI */ +#define LTM_ALL + +/* RSA only (does not support DH/DSA/ECC) */ +/* #define SC_RSA_1 */ + +/* For reference.... On an Athlon64 optimizing for speed... + + LTM's mpi.o with all functions [striped] is 142KiB in size. + + */ + +/* Works for RSA only, mpi.o is 68KiB */ +#ifdef SC_RSA_1 + #define BN_MP_SHRINK_C + #define BN_MP_LCM_C + #define BN_MP_PRIME_RANDOM_EX_C + #define BN_MP_INVMOD_C + #define BN_MP_GCD_C + #define BN_MP_MOD_C + #define BN_MP_MULMOD_C + #define BN_MP_ADDMOD_C + #define BN_MP_EXPTMOD_C + #define BN_MP_SET_INT_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_UNSIGNED_BIN_SIZE_C + #define BN_MP_TO_UNSIGNED_BIN_C + #define BN_MP_MOD_D_C + #define BN_MP_PRIME_RABIN_MILLER_TRIALS_C + #define BN_REVERSE_C + #define BN_PRIME_TAB_C + +/* other modifiers */ + #define BN_MP_DIV_SMALL /* Slower division, not critical */ + +/* here we are on the last pass so we turn things off. The functions classes are still there + * but we remove them specifically from the build. This also invokes tweaks in functions + * like removing support for even moduli, etc... + */ + #ifdef LTM_LAST + #undef BN_MP_TOOM_MUL_C + #undef BN_MP_TOOM_SQR_C + #undef BN_MP_KARATSUBA_MUL_C + #undef BN_MP_KARATSUBA_SQR_C + #undef BN_MP_REDUCE_C + #undef BN_MP_REDUCE_SETUP_C + #undef BN_MP_DR_IS_MODULUS_C + #undef BN_MP_DR_SETUP_C + #undef BN_MP_DR_REDUCE_C + #undef BN_MP_REDUCE_IS_2K_C + #undef BN_MP_REDUCE_2K_SETUP_C + #undef BN_MP_REDUCE_2K_C + #undef BN_S_MP_EXPTMOD_C + #undef BN_MP_DIV_3_C + #undef BN_S_MP_MUL_HIGH_DIGS_C + #undef BN_FAST_S_MP_MUL_HIGH_DIGS_C + #undef BN_FAST_MP_INVMOD_C + +/* To safely undefine these you have to make sure your RSA key won't exceed the Comba threshold + * which is roughly 255 digits [7140 bits for 32-bit machines, 15300 bits for 64-bit machines] + * which means roughly speaking you can handle upto 2536-bit RSA keys with these defined without + * trouble. + */ + #undef BN_S_MP_MUL_DIGS_C + #undef BN_S_MP_SQR_C + #undef BN_MP_MONTGOMERY_REDUCE_C + #endif +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ +#if !(defined(LTM1) && defined(LTM2) && defined(LTM3)) + #if defined(LTM2) + #define LTM3 + #endif + #if defined(LTM1) + #define LTM2 + #endif + #define LTM1 + + #if defined(LTM_ALL) + #define BN_ERROR_C + #define BN_FAST_MP_INVMOD_C + #define BN_FAST_MP_MONTGOMERY_REDUCE_C + #define BN_FAST_S_MP_MUL_DIGS_C + #define BN_FAST_S_MP_MUL_HIGH_DIGS_C + #define BN_FAST_S_MP_SQR_C + #define BN_MP_2EXPT_C + #define BN_MP_ABS_C + #define BN_MP_ADD_C + #define BN_MP_ADD_D_C + #define BN_MP_ADDMOD_C + #define BN_MP_AND_C + #define BN_MP_CLAMP_C + #define BN_MP_CLEAR_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_CMP_C + #define BN_MP_CMP_D_C + #define BN_MP_CMP_MAG_C + #define BN_MP_CNT_LSB_C + #define BN_MP_COPY_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_C + #define BN_MP_DIV_2_C + #define BN_MP_DIV_2D_C + #define BN_MP_DIV_3_C + #define BN_MP_DIV_D_C + #define BN_MP_DR_IS_MODULUS_C + #define BN_MP_DR_REDUCE_C + #define BN_MP_DR_SETUP_C + #define BN_MP_EXCH_C + #define BN_MP_EXPORT_C + #define BN_MP_EXPT_D_C + #define BN_MP_EXPT_D_EX_C + #define BN_MP_EXPTMOD_C + #define BN_MP_EXPTMOD_FAST_C + #define BN_MP_EXTEUCLID_C + #define BN_MP_FREAD_C + #define BN_MP_FWRITE_C + #define BN_MP_GCD_C + #define BN_MP_GET_INT_C + #define BN_MP_GET_LONG_C + #define BN_MP_GET_LONG_LONG_C + #define BN_MP_GROW_C + #define BN_MP_IMPORT_C + #define BN_MP_INIT_C + #define BN_MP_INIT_COPY_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_INIT_SET_C + #define BN_MP_INIT_SET_INT_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_INVMOD_C + #define BN_MP_INVMOD_SLOW_C + #define BN_MP_IS_SQUARE_C + #define BN_MP_JACOBI_C + #define BN_MP_KARATSUBA_MUL_C + #define BN_MP_KARATSUBA_SQR_C + #define BN_MP_LCM_C + #define BN_MP_LSHD_C + #define BN_MP_MOD_C + #define BN_MP_MOD_2D_C + #define BN_MP_MOD_D_C + #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + #define BN_MP_MONTGOMERY_REDUCE_C + #define BN_MP_MONTGOMERY_SETUP_C + #define BN_MP_MUL_C + #define BN_MP_MUL_2_C + #define BN_MP_MUL_2D_C + #define BN_MP_MUL_D_C + #define BN_MP_MULMOD_C + #define BN_MP_N_ROOT_C + #define BN_MP_N_ROOT_EX_C + #define BN_MP_NEG_C + #define BN_MP_OR_C + #define BN_MP_PRIME_FERMAT_C + #define BN_MP_PRIME_IS_DIVISIBLE_C + #define BN_MP_PRIME_IS_PRIME_C + #define BN_MP_PRIME_MILLER_RABIN_C + #define BN_MP_PRIME_NEXT_PRIME_C + #define BN_MP_PRIME_RABIN_MILLER_TRIALS_C + #define BN_MP_PRIME_RANDOM_EX_C + #define BN_MP_RADIX_SIZE_C + #define BN_MP_RADIX_SMAP_C + #define BN_MP_RAND_C + #define BN_MP_READ_RADIX_C + #define BN_MP_READ_SIGNED_BIN_C + #define BN_MP_READ_UNSIGNED_BIN_C + #define BN_MP_REDUCE_C + #define BN_MP_REDUCE_2K_C + #define BN_MP_REDUCE_2K_L_C + #define BN_MP_REDUCE_2K_SETUP_C + #define BN_MP_REDUCE_2K_SETUP_L_C + #define BN_MP_REDUCE_IS_2K_C + #define BN_MP_REDUCE_IS_2K_L_C + #define BN_MP_REDUCE_SETUP_C + #define BN_MP_RSHD_C + #define BN_MP_SET_C + #define BN_MP_SET_INT_C + #define BN_MP_SET_LONG_C + #define BN_MP_SET_LONG_LONG_C + #define BN_MP_SHRINK_C + #define BN_MP_SIGNED_BIN_SIZE_C + #define BN_MP_SQR_C + #define BN_MP_SQRMOD_C + #define BN_MP_SQRT_C + #define BN_MP_SQRTMOD_PRIME_C + #define BN_MP_SUB_C + #define BN_MP_SUB_D_C + #define BN_MP_SUBMOD_C + #define BN_MP_TO_SIGNED_BIN_C + #define BN_MP_TO_SIGNED_BIN_N_C + #define BN_MP_TO_UNSIGNED_BIN_C + #define BN_MP_TO_UNSIGNED_BIN_N_C + #define BN_MP_TOOM_MUL_C + #define BN_MP_TOOM_SQR_C + #define BN_MP_TORADIX_C + #define BN_MP_TORADIX_N_C + #define BN_MP_UNSIGNED_BIN_SIZE_C + #define BN_MP_XOR_C + #define BN_MP_ZERO_C + #define BN_PRIME_TAB_C + #define BN_REVERSE_C + #define BN_S_MP_ADD_C + #define BN_S_MP_EXPTMOD_C + #define BN_S_MP_MUL_DIGS_C + #define BN_S_MP_MUL_HIGH_DIGS_C + #define BN_S_MP_SQR_C + #define BN_S_MP_SUB_C + #define BNCORE_C + #endif + + #if defined(BN_ERROR_C) + #define BN_MP_ERROR_TO_STRING_C + #endif + + #if defined(BN_FAST_MP_INVMOD_C) + #define BN_MP_ISEVEN_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_COPY_C + #define BN_MP_MOD_C + #define BN_MP_SET_C + #define BN_MP_DIV_2_C + #define BN_MP_ISODD_C + #define BN_MP_SUB_C + #define BN_MP_CMP_C + #define BN_MP_ISZERO_C + #define BN_MP_CMP_D_C + #define BN_MP_ADD_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_FAST_MP_MONTGOMERY_REDUCE_C) + #define BN_MP_GROW_C + #define BN_MP_RSHD_C + #define BN_MP_CLAMP_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_FAST_S_MP_MUL_DIGS_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_FAST_S_MP_SQR_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_2EXPT_C) + #define BN_MP_ZERO_C + #define BN_MP_GROW_C + #endif + + #if defined(BN_MP_ABS_C) + #define BN_MP_COPY_C + #endif + + #if defined(BN_MP_ADD_C) + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_ADD_D_C) + #define BN_MP_GROW_C + #define BN_MP_SUB_D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_ADDMOD_C) + #define BN_MP_INIT_C + #define BN_MP_ADD_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C + #endif + + #if defined(BN_MP_AND_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_CLAMP_C) + #endif + + #if defined(BN_MP_CLEAR_C) + #endif + + #if defined(BN_MP_CLEAR_MULTI_C) + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_CMP_C) + #define BN_MP_CMP_MAG_C + #endif + + #if defined(BN_MP_CMP_D_C) + #endif + + #if defined(BN_MP_CMP_MAG_C) + #endif + + #if defined(BN_MP_CNT_LSB_C) + #define BN_MP_ISZERO_C + #endif + + #if defined(BN_MP_COPY_C) + #define BN_MP_GROW_C + #endif + + #if defined(BN_MP_COUNT_BITS_C) + #endif + + #if defined(BN_MP_DIV_C) + #define BN_MP_ISZERO_C + #define BN_MP_CMP_MAG_C + #define BN_MP_COPY_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_SET_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_ABS_C + #define BN_MP_MUL_2D_C + #define BN_MP_CMP_C + #define BN_MP_SUB_C + #define BN_MP_ADD_C + #define BN_MP_DIV_2D_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_INIT_C + #define BN_MP_INIT_COPY_C + #define BN_MP_LSHD_C + #define BN_MP_RSHD_C + #define BN_MP_MUL_D_C + #define BN_MP_CLAMP_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_DIV_2_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_DIV_2D_C) + #define BN_MP_COPY_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_C + #define BN_MP_MOD_2D_C + #define BN_MP_CLEAR_C + #define BN_MP_RSHD_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #endif + + #if defined(BN_MP_DIV_3_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_DIV_D_C) + #define BN_MP_ISZERO_C + #define BN_MP_COPY_C + #define BN_MP_DIV_2D_C + #define BN_MP_DIV_3_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_DR_IS_MODULUS_C) + #endif + + #if defined(BN_MP_DR_REDUCE_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_DR_SETUP_C) + #endif + + #if defined(BN_MP_EXCH_C) + #endif + + #if defined(BN_MP_EXPORT_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_2D_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_EXPT_D_C) + #define BN_MP_EXPT_D_EX_C + #endif + + #if defined(BN_MP_EXPT_D_EX_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_SET_C + #define BN_MP_MUL_C + #define BN_MP_CLEAR_C + #define BN_MP_SQR_C + #endif + + #if defined(BN_MP_EXPTMOD_C) + #define BN_MP_INIT_C + #define BN_MP_INVMOD_C + #define BN_MP_CLEAR_C + #define BN_MP_ABS_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_REDUCE_IS_2K_L_C + #define BN_S_MP_EXPTMOD_C + #define BN_MP_DR_IS_MODULUS_C + #define BN_MP_REDUCE_IS_2K_C + #define BN_MP_ISODD_C + #define BN_MP_EXPTMOD_FAST_C + #endif + + #if defined(BN_MP_EXPTMOD_FAST_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_INIT_C + #define BN_MP_CLEAR_C + #define BN_MP_MONTGOMERY_SETUP_C + #define BN_FAST_MP_MONTGOMERY_REDUCE_C + #define BN_MP_MONTGOMERY_REDUCE_C + #define BN_MP_DR_SETUP_C + #define BN_MP_DR_REDUCE_C + #define BN_MP_REDUCE_2K_SETUP_C + #define BN_MP_REDUCE_2K_C + #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + #define BN_MP_MULMOD_C + #define BN_MP_SET_C + #define BN_MP_MOD_C + #define BN_MP_COPY_C + #define BN_MP_SQR_C + #define BN_MP_MUL_C + #define BN_MP_EXCH_C + #endif + + #if defined(BN_MP_EXTEUCLID_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_SET_C + #define BN_MP_COPY_C + #define BN_MP_ISZERO_C + #define BN_MP_DIV_C + #define BN_MP_MUL_C + #define BN_MP_SUB_C + #define BN_MP_NEG_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_FREAD_C) + #define BN_MP_ZERO_C + #define BN_MP_S_RMAP_C + #define BN_MP_MUL_D_C + #define BN_MP_ADD_D_C + #define BN_MP_CMP_D_C + #endif + + #if defined(BN_MP_FWRITE_C) + #define BN_MP_RADIX_SIZE_C + #define BN_MP_TORADIX_C + #endif + + #if defined(BN_MP_GCD_C) + #define BN_MP_ISZERO_C + #define BN_MP_ABS_C + #define BN_MP_INIT_COPY_C + #define BN_MP_CNT_LSB_C + #define BN_MP_DIV_2D_C + #define BN_MP_CMP_MAG_C + #define BN_MP_EXCH_C + #define BN_S_MP_SUB_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_GET_INT_C) + #endif + + #if defined(BN_MP_GET_LONG_C) + #endif + + #if defined(BN_MP_GET_LONG_LONG_C) + #endif + + #if defined(BN_MP_GROW_C) + #endif + + #if defined(BN_MP_IMPORT_C) + #define BN_MP_ZERO_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_INIT_C) + #endif + + #if defined(BN_MP_INIT_COPY_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_COPY_C + #endif + + #if defined(BN_MP_INIT_MULTI_C) + #define BN_MP_ERR_C + #define BN_MP_INIT_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_INIT_SET_C) + #define BN_MP_INIT_C + #define BN_MP_SET_C + #endif + + #if defined(BN_MP_INIT_SET_INT_C) + #define BN_MP_INIT_C + #define BN_MP_SET_INT_C + #endif + + #if defined(BN_MP_INIT_SIZE_C) + #define BN_MP_INIT_C + #endif + + #if defined(BN_MP_INVMOD_C) + #define BN_MP_ISZERO_C + #define BN_MP_ISODD_C + #define BN_FAST_MP_INVMOD_C + #define BN_MP_INVMOD_SLOW_C + #endif + + #if defined(BN_MP_INVMOD_SLOW_C) + #define BN_MP_ISZERO_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_C + #define BN_MP_COPY_C + #define BN_MP_ISEVEN_C + #define BN_MP_SET_C + #define BN_MP_DIV_2_C + #define BN_MP_ISODD_C + #define BN_MP_ADD_C + #define BN_MP_SUB_C + #define BN_MP_CMP_C + #define BN_MP_CMP_D_C + #define BN_MP_CMP_MAG_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_IS_SQUARE_C) + #define BN_MP_MOD_D_C + #define BN_MP_INIT_SET_INT_C + #define BN_MP_MOD_C + #define BN_MP_GET_INT_C + #define BN_MP_SQRT_C + #define BN_MP_SQR_C + #define BN_MP_CMP_MAG_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_JACOBI_C) + #define BN_MP_CMP_D_C + #define BN_MP_ISZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_CNT_LSB_C + #define BN_MP_DIV_2D_C + #define BN_MP_MOD_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_KARATSUBA_MUL_C) + #define BN_MP_MUL_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_S_MP_ADD_C + #define BN_MP_ADD_C + #define BN_S_MP_SUB_C + #define BN_MP_LSHD_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_KARATSUBA_SQR_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_SQR_C + #define BN_S_MP_ADD_C + #define BN_S_MP_SUB_C + #define BN_MP_LSHD_C + #define BN_MP_ADD_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_LCM_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_GCD_C + #define BN_MP_CMP_MAG_C + #define BN_MP_DIV_C + #define BN_MP_MUL_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_LSHD_C) + #define BN_MP_GROW_C + #define BN_MP_RSHD_C + #endif + + #if defined(BN_MP_MOD_C) + #define BN_MP_INIT_C + #define BN_MP_DIV_C + #define BN_MP_CLEAR_C + #define BN_MP_ISZERO_C + #define BN_MP_EXCH_C + #define BN_MP_ADD_C + #endif + + #if defined(BN_MP_MOD_2D_C) + #define BN_MP_ZERO_C + #define BN_MP_COPY_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_MOD_D_C) + #define BN_MP_DIV_D_C + #endif + + #if defined(BN_MP_MONTGOMERY_CALC_NORMALIZATION_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_2EXPT_C + #define BN_MP_SET_C + #define BN_MP_MUL_2_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_MONTGOMERY_REDUCE_C) + #define BN_FAST_MP_MONTGOMERY_REDUCE_C + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #define BN_MP_RSHD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_MONTGOMERY_SETUP_C) + #endif + + #if defined(BN_MP_MUL_C) + #define BN_MP_TOOM_MUL_C + #define BN_MP_KARATSUBA_MUL_C + #define BN_FAST_S_MP_MUL_DIGS_C + #define BN_S_MP_MUL_C + #define BN_S_MP_MUL_DIGS_C + #endif + + #if defined(BN_MP_MUL_2_C) + #define BN_MP_GROW_C + #endif + + #if defined(BN_MP_MUL_2D_C) + #define BN_MP_COPY_C + #define BN_MP_GROW_C + #define BN_MP_LSHD_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_MUL_D_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_MULMOD_C) + #define BN_MP_INIT_C + #define BN_MP_MUL_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C + #endif + + #if defined(BN_MP_N_ROOT_C) + #define BN_MP_N_ROOT_EX_C + #endif + + #if defined(BN_MP_N_ROOT_EX_C) + #define BN_MP_INIT_C + #define BN_MP_SET_C + #define BN_MP_COPY_C + #define BN_MP_EXPT_D_EX_C + #define BN_MP_MUL_C + #define BN_MP_SUB_C + #define BN_MP_MUL_D_C + #define BN_MP_DIV_C + #define BN_MP_CMP_C + #define BN_MP_SUB_D_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_NEG_C) + #define BN_MP_COPY_C + #define BN_MP_ISZERO_C + #endif + + #if defined(BN_MP_OR_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_FERMAT_C) + #define BN_MP_CMP_D_C + #define BN_MP_INIT_C + #define BN_MP_EXPTMOD_C + #define BN_MP_CMP_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_IS_DIVISIBLE_C) + #define BN_MP_MOD_D_C + #endif + + #if defined(BN_MP_PRIME_IS_PRIME_C) + #define BN_MP_CMP_D_C + #define BN_MP_PRIME_IS_DIVISIBLE_C + #define BN_MP_INIT_C + #define BN_MP_SET_C + #define BN_MP_PRIME_MILLER_RABIN_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_MILLER_RABIN_C) + #define BN_MP_CMP_D_C + #define BN_MP_INIT_COPY_C + #define BN_MP_SUB_D_C + #define BN_MP_CNT_LSB_C + #define BN_MP_DIV_2D_C + #define BN_MP_EXPTMOD_C + #define BN_MP_CMP_C + #define BN_MP_SQRMOD_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_NEXT_PRIME_C) + #define BN_MP_CMP_D_C + #define BN_MP_SET_C + #define BN_MP_SUB_D_C + #define BN_MP_ISEVEN_C + #define BN_MP_MOD_D_C + #define BN_MP_INIT_C + #define BN_MP_ADD_D_C + #define BN_MP_PRIME_MILLER_RABIN_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_RABIN_MILLER_TRIALS_C) + #endif + + #if defined(BN_MP_PRIME_RANDOM_EX_C) + #define BN_MP_READ_UNSIGNED_BIN_C + #define BN_MP_PRIME_IS_PRIME_C + #define BN_MP_SUB_D_C + #define BN_MP_DIV_2_C + #define BN_MP_MUL_2_C + #define BN_MP_ADD_D_C + #endif + + #if defined(BN_MP_RADIX_SIZE_C) + #define BN_MP_ISZERO_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_INIT_COPY_C + #define BN_MP_DIV_D_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_RADIX_SMAP_C) + #define BN_MP_S_RMAP_C + #endif + + #if defined(BN_MP_RAND_C) + #define BN_MP_ZERO_C + #define BN_MP_ADD_D_C + #define BN_MP_LSHD_C + #endif + + #if defined(BN_MP_READ_RADIX_C) + #define BN_MP_ZERO_C + #define BN_MP_S_RMAP_C + #define BN_MP_MUL_D_C + #define BN_MP_ADD_D_C + #define BN_MP_ISZERO_C + #endif + + #if defined(BN_MP_READ_SIGNED_BIN_C) + #define BN_MP_READ_UNSIGNED_BIN_C + #endif + + #if defined(BN_MP_READ_UNSIGNED_BIN_C) + #define BN_MP_GROW_C + #define BN_MP_ZERO_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_REDUCE_C) + #define BN_MP_REDUCE_SETUP_C + #define BN_MP_INIT_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_MUL_C + #define BN_S_MP_MUL_HIGH_DIGS_C + #define BN_FAST_S_MP_MUL_HIGH_DIGS_C + #define BN_MP_MOD_2D_C + #define BN_S_MP_MUL_DIGS_C + #define BN_MP_SUB_C + #define BN_MP_CMP_D_C + #define BN_MP_SET_C + #define BN_MP_LSHD_C + #define BN_MP_ADD_C + #define BN_MP_CMP_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_REDUCE_2K_C) + #define BN_MP_INIT_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_2D_C + #define BN_MP_MUL_D_C + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_REDUCE_2K_L_C) + #define BN_MP_INIT_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_2D_C + #define BN_MP_MUL_C + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_REDUCE_2K_SETUP_C) + #define BN_MP_INIT_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_2EXPT_C + #define BN_MP_CLEAR_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_REDUCE_2K_SETUP_L_C) + #define BN_MP_INIT_C + #define BN_MP_2EXPT_C + #define BN_MP_COUNT_BITS_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_REDUCE_IS_2K_C) + #define BN_MP_REDUCE_2K_C + #define BN_MP_COUNT_BITS_C + #endif + + #if defined(BN_MP_REDUCE_IS_2K_L_C) + #endif + + #if defined(BN_MP_REDUCE_SETUP_C) + #define BN_MP_2EXPT_C + #define BN_MP_DIV_C + #endif + + #if defined(BN_MP_RSHD_C) + #define BN_MP_ZERO_C + #endif + + #if defined(BN_MP_SET_C) + #define BN_MP_ZERO_C + #endif + + #if defined(BN_MP_SET_INT_C) + #define BN_MP_ZERO_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_SET_LONG_C) + #endif + + #if defined(BN_MP_SET_LONG_LONG_C) + #endif + + #if defined(BN_MP_SHRINK_C) + #endif + + #if defined(BN_MP_SIGNED_BIN_SIZE_C) + #define BN_MP_UNSIGNED_BIN_SIZE_C + #endif + + #if defined(BN_MP_SQR_C) + #define BN_MP_TOOM_SQR_C + #define BN_MP_KARATSUBA_SQR_C + #define BN_FAST_S_MP_SQR_C + #define BN_S_MP_SQR_C + #endif + + #if defined(BN_MP_SQRMOD_C) + #define BN_MP_INIT_C + #define BN_MP_SQR_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C + #endif + + #if defined(BN_MP_SQRT_C) + #define BN_MP_N_ROOT_C + #define BN_MP_ISZERO_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_DIV_C + #define BN_MP_ADD_C + #define BN_MP_DIV_2_C + #define BN_MP_CMP_MAG_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_SQRTMOD_PRIME_C) + #define BN_MP_CMP_D_C + #define BN_MP_ZERO_C + #define BN_MP_JACOBI_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_D_C + #define BN_MP_ADD_D_C + #define BN_MP_DIV_2_C + #define BN_MP_EXPTMOD_C + #define BN_MP_COPY_C + #define BN_MP_SUB_D_C + #define BN_MP_ISEVEN_C + #define BN_MP_SET_INT_C + #define BN_MP_SQRMOD_C + #define BN_MP_MULMOD_C + #define BN_MP_SET_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_SUB_C) + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_SUB_D_C) + #define BN_MP_GROW_C + #define BN_MP_ADD_D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_SUBMOD_C) + #define BN_MP_INIT_C + #define BN_MP_SUB_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C + #endif + + #if defined(BN_MP_TO_SIGNED_BIN_C) + #define BN_MP_TO_UNSIGNED_BIN_C + #endif + + #if defined(BN_MP_TO_SIGNED_BIN_N_C) + #define BN_MP_SIGNED_BIN_SIZE_C + #define BN_MP_TO_SIGNED_BIN_C + #endif + + #if defined(BN_MP_TO_UNSIGNED_BIN_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_ISZERO_C + #define BN_MP_DIV_2D_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_TO_UNSIGNED_BIN_N_C) + #define BN_MP_UNSIGNED_BIN_SIZE_C + #define BN_MP_TO_UNSIGNED_BIN_C + #endif + + #if defined(BN_MP_TOOM_MUL_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_2D_C + #define BN_MP_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_MUL_C + #define BN_MP_MUL_2_C + #define BN_MP_ADD_C + #define BN_MP_SUB_C + #define BN_MP_DIV_2_C + #define BN_MP_MUL_2D_C + #define BN_MP_MUL_D_C + #define BN_MP_DIV_3_C + #define BN_MP_LSHD_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_TOOM_SQR_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_2D_C + #define BN_MP_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_SQR_C + #define BN_MP_MUL_2_C + #define BN_MP_ADD_C + #define BN_MP_SUB_C + #define BN_MP_DIV_2_C + #define BN_MP_MUL_2D_C + #define BN_MP_MUL_D_C + #define BN_MP_DIV_3_C + #define BN_MP_LSHD_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_TORADIX_C) + #define BN_MP_ISZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_DIV_D_C + #define BN_MP_CLEAR_C + #define BN_MP_S_RMAP_C + #endif + + #if defined(BN_MP_TORADIX_N_C) + #define BN_MP_ISZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_DIV_D_C + #define BN_MP_CLEAR_C + #define BN_MP_S_RMAP_C + #endif + + #if defined(BN_MP_UNSIGNED_BIN_SIZE_C) + #define BN_MP_COUNT_BITS_C + #endif + + #if defined(BN_MP_XOR_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_ZERO_C) + #endif + + #if defined(BN_PRIME_TAB_C) + #endif + + #if defined(BN_REVERSE_C) + #endif + + #if defined(BN_S_MP_ADD_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_S_MP_EXPTMOD_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_INIT_C + #define BN_MP_CLEAR_C + #define BN_MP_REDUCE_SETUP_C + #define BN_MP_REDUCE_C + #define BN_MP_REDUCE_2K_SETUP_L_C + #define BN_MP_REDUCE_2K_L_C + #define BN_MP_MOD_C + #define BN_MP_COPY_C + #define BN_MP_SQR_C + #define BN_MP_MUL_C + #define BN_MP_SET_C + #define BN_MP_EXCH_C + #endif + + #if defined(BN_S_MP_MUL_DIGS_C) + #define BN_FAST_S_MP_MUL_DIGS_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_S_MP_MUL_HIGH_DIGS_C) + #define BN_FAST_S_MP_MUL_HIGH_DIGS_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_S_MP_SQR_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_S_MP_SUB_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BNCORE_C) + #endif + + #ifdef LTM3 + #define LTM_LAST + #endif +#else + #define LTM_LAST +#endif +#else + #define LTM_LAST +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* detect 64-bit mode if possible */ +#if defined(__x86_64__) + #if !(defined(MP_32BIT) || defined(MP_16BIT) || defined(MP_8BIT)) + #define MP_64BIT + #endif +#endif + +/* some default configurations. + * + * A "mp_digit" must be able to hold DIGIT_BIT + 1 bits + * A "mp_word" must be able to hold 2*DIGIT_BIT + 1 bits + * + * At the very least a mp_digit must be able to hold 7 bits + * [any size beyond that is ok provided it doesn't overflow the data type] + */ +#ifdef MP_8BIT +typedef uint8_t mp_digit; +typedef uint16_t mp_word; + #define MP_SIZEOF_MP_DIGIT 1 + #ifdef DIGIT_BIT + #error You must not define DIGIT_BIT when using MP_8BIT + #endif +#elif defined(MP_16BIT) +typedef uint16_t mp_digit; +typedef uint32_t mp_word; + #define MP_SIZEOF_MP_DIGIT 2 + #ifdef DIGIT_BIT + #error You must not define DIGIT_BIT when using MP_16BIT + #endif +#elif defined(MP_64BIT) +/* for GCC only on supported platforms */ + #ifndef CRYPT +typedef unsigned long long ulong64; +typedef signed long long long64; + #endif + +typedef uint64_t mp_digit; + #if defined(_WIN32) +typedef unsigned __int128 mp_word; + #elif defined(__GNUC__) +typedef unsigned long mp_word __attribute__ ((mode(TI))); + #else + +/* it seems you have a problem + * but we assume you can somewhere define your own uint128_t */ +typedef uint128_t mp_word; + #endif + + #define DIGIT_BIT 60 +#else +/* this is the default case, 28-bit digits */ + +/* this is to make porting into LibTomCrypt easier :-) */ + #ifndef CRYPT +typedef unsigned long long ulong64; +typedef signed long long long64; + #endif + +typedef uint32_t mp_digit; +typedef uint64_t mp_word; + + #ifdef MP_31BIT +/* this is an extension that uses 31-bit digits */ + #define DIGIT_BIT 31 + #else +/* default case is 28-bit digits, defines MP_28BIT as a handy macro to test */ + #define DIGIT_BIT 28 + #define MP_28BIT + #endif +#endif + +/* otherwise the bits per digit is calculated automatically from the size of a mp_digit */ +#ifndef DIGIT_BIT + #define DIGIT_BIT (((CHAR_BIT * MP_SIZEOF_MP_DIGIT) - 1)) /* bits per digit */ +typedef uint_least32_t mp_min_u32; +#else +typedef mp_digit mp_min_u32; +#endif + +/* platforms that can use a better rand function */ +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) + #define MP_USE_ALT_RAND 1 +#endif + +/* use arc4random on platforms that support it */ +#ifdef MP_USE_ALT_RAND + #define MP_GEN_RANDOM() arc4random() +#else + #define MP_GEN_RANDOM() rand() +#endif + +#define MP_DIGIT_BIT DIGIT_BIT +#define MP_MASK ((((mp_digit)1) << ((mp_digit)DIGIT_BIT)) - ((mp_digit)1)) +#define MP_DIGIT_MAX MP_MASK + +/* equalities */ +#define MP_LT -1 /* less than */ +#define MP_EQ 0 /* equal to */ +#define MP_GT 1 /* greater than */ + +#define MP_ZPOS 0 /* positive integer */ +#define MP_NEG 1 /* negative */ + +#define MP_OKAY 0 /* ok result */ +#define MP_MEM -2 /* out of mem */ +#define MP_VAL -3 /* invalid input */ +#define MP_RANGE MP_VAL + +#define MP_YES 1 /* yes response */ +#define MP_NO 0 /* no response */ + +/* Primality generation flags */ +#define LTM_PRIME_BBS 0x0001 /* BBS style prime */ +#define LTM_PRIME_SAFE 0x0002 /* Safe prime (p-1)/2 == prime */ +#define LTM_PRIME_2MSB_ON 0x0008 /* force 2nd MSB to 1 */ + +typedef int mp_err; + +/* you'll have to tune these... */ +extern int KARATSUBA_MUL_CUTOFF, + KARATSUBA_SQR_CUTOFF, + TOOM_MUL_CUTOFF, + TOOM_SQR_CUTOFF; + +/* define this to use lower memory usage routines (exptmods mostly) */ +/* #define MP_LOW_MEM */ + +/* default precision */ +#ifndef MP_PREC + #ifndef MP_LOW_MEM + #define MP_PREC 32 /* default digits of precision */ + #else + #define MP_PREC 8 /* default digits of precision */ + #endif +#endif + +/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ +#define MP_WARRAY (1 << (((sizeof(mp_word) * CHAR_BIT) - (2 * DIGIT_BIT)) + 1)) + +/* the infamous mp_int structure */ +typedef struct { + int used, alloc, sign; + mp_digit *dp; +} mp_int; + +/* callback for mp_prime_random, should fill dst with random bytes and return how many read [upto len] */ +typedef int ltm_prime_callback (unsigned char *dst, int len, void *dat); + + +#define USED(m) ((m)->used) +#define DIGIT(m, k) ((m)->dp[(k)]) +#define SIGN(m) ((m)->sign) + +/* error code to char* string */ +const char *mp_error_to_string(int code); + +/* ---> init and deinit bignum functions <--- */ +/* init a bignum */ +int mp_init(mp_int *a); + +/* free a bignum */ +void mp_clear(mp_int *a); + +/* init a null terminated series of arguments */ +int mp_init_multi(mp_int *mp, ...); + +/* clear a null terminated series of arguments */ +void mp_clear_multi(mp_int *mp, ...); + +/* exchange two ints */ +void mp_exch(mp_int *a, mp_int *b); + +/* shrink ram required for a bignum */ +int mp_shrink(mp_int *a); + +/* grow an int to a given size */ +int mp_grow(mp_int *a, int size); + +/* init to a given number of digits */ +int mp_init_size(mp_int *a, int size); + +/* ---> Basic Manipulations <--- */ +#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO) +#define mp_iseven(a) ((((a)->used > 0) && (((a)->dp[0] & 1u) == 0u)) ? MP_YES : MP_NO) +#define mp_isodd(a) ((((a)->used > 0) && (((a)->dp[0] & 1u) == 1u)) ? MP_YES : MP_NO) +#define mp_isneg(a) (((a)->sign != MP_ZPOS) ? MP_YES : MP_NO) + +/* set to zero */ +void mp_zero(mp_int *a); + +/* set to a digit */ +void mp_set(mp_int *a, mp_digit b); + +/* set a 32-bit const */ +int mp_set_int(mp_int *a, unsigned long b); + +/* set a platform dependent unsigned long value */ +int mp_set_long(mp_int *a, unsigned long b); + +/* set a platform dependent unsigned long long value */ +int mp_set_long_long(mp_int *a, unsigned long long b); + +/* get a 32-bit value */ +unsigned long mp_get_int(mp_int *a); + +/* get a platform dependent unsigned long value */ +unsigned long mp_get_long(mp_int *a); + +/* get a platform dependent unsigned long long value */ +unsigned long long mp_get_long_long(mp_int *a); + +/* initialize and set a digit */ +int mp_init_set(mp_int *a, mp_digit b); + +/* initialize and set 32-bit value */ +int mp_init_set_int(mp_int *a, unsigned long b); + +/* copy, b = a */ +int mp_copy(mp_int *a, mp_int *b); + +/* inits and copies, a = b */ +int mp_init_copy(mp_int *a, mp_int *b); + +/* trim unused digits */ +void mp_clamp(mp_int *a); + +/* import binary data */ +int mp_import(mp_int *rop, size_t count, int order, size_t size, int endian, size_t nails, const void *op); + +/* export binary data */ +int mp_export(void *rop, size_t *countp, int order, size_t size, int endian, size_t nails, mp_int *op); + +/* ---> digit manipulation <--- */ + +/* right shift by "b" digits */ +void mp_rshd(mp_int *a, int b); + +/* left shift by "b" digits */ +int mp_lshd(mp_int *a, int b); + +/* c = a / 2**b, implemented as c = a >> b */ +int mp_div_2d(mp_int *a, int b, mp_int *c, mp_int *d); + +/* b = a/2 */ +int mp_div_2(mp_int *a, mp_int *b); + +/* c = a * 2**b, implemented as c = a << b */ +int mp_mul_2d(mp_int *a, int b, mp_int *c); + +/* b = a*2 */ +int mp_mul_2(mp_int *a, mp_int *b); + +/* c = a mod 2**b */ +int mp_mod_2d(mp_int *a, int b, mp_int *c); + +/* computes a = 2**b */ +int mp_2expt(mp_int *a, int b); + +/* Counts the number of lsbs which are zero before the first zero bit */ +int mp_cnt_lsb(mp_int *a); + +/* I Love Earth! */ + +/* makes a pseudo-random int of a given size */ +int mp_rand(mp_int *a, int digits); + +/* ---> binary operations <--- */ +/* c = a XOR b */ +int mp_xor(mp_int *a, mp_int *b, mp_int *c); + +/* c = a OR b */ +int mp_or(mp_int *a, mp_int *b, mp_int *c); + +/* c = a AND b */ +int mp_and(mp_int *a, mp_int *b, mp_int *c); + +/* ---> Basic arithmetic <--- */ + +/* b = -a */ +int mp_neg(mp_int *a, mp_int *b); + +/* b = |a| */ +int mp_abs(mp_int *a, mp_int *b); + +/* compare a to b */ +int mp_cmp(mp_int *a, mp_int *b); + +/* compare |a| to |b| */ +int mp_cmp_mag(mp_int *a, mp_int *b); + +/* c = a + b */ +int mp_add(mp_int *a, mp_int *b, mp_int *c); + +/* c = a - b */ +int mp_sub(mp_int *a, mp_int *b, mp_int *c); + +/* c = a * b */ +int mp_mul(mp_int *a, mp_int *b, mp_int *c); + +/* b = a*a */ +int mp_sqr(mp_int *a, mp_int *b); + +/* a/b => cb + d == a */ +int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* c = a mod b, 0 <= c < b */ +int mp_mod(mp_int *a, mp_int *b, mp_int *c); + +/* ---> single digit functions <--- */ + +/* compare against a single digit */ +int mp_cmp_d(mp_int *a, mp_digit b); + +/* c = a + b */ +int mp_add_d(mp_int *a, mp_digit b, mp_int *c); + +/* c = a - b */ +int mp_sub_d(mp_int *a, mp_digit b, mp_int *c); + +/* c = a * b */ +int mp_mul_d(mp_int *a, mp_digit b, mp_int *c); + +/* a/b => cb + d == a */ +int mp_div_d(mp_int *a, mp_digit b, mp_int *c, mp_digit *d); + +/* a/3 => 3c + d == a */ +int mp_div_3(mp_int *a, mp_int *c, mp_digit *d); + +/* c = a**b */ +int mp_expt_d(mp_int *a, mp_digit b, mp_int *c); +int mp_expt_d_ex(mp_int *a, mp_digit b, mp_int *c, int fast); + +/* c = a mod b, 0 <= c < b */ +int mp_mod_d(mp_int *a, mp_digit b, mp_digit *c); + +/* ---> number theory <--- */ + +/* d = a + b (mod c) */ +int mp_addmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* d = a - b (mod c) */ +int mp_submod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* d = a * b (mod c) */ +int mp_mulmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* c = a * a (mod b) */ +int mp_sqrmod(mp_int *a, mp_int *b, mp_int *c); + +/* c = 1/a (mod b) */ +int mp_invmod(mp_int *a, mp_int *b, mp_int *c); + +/* c = (a, b) */ +int mp_gcd(mp_int *a, mp_int *b, mp_int *c); + +/* produces value such that U1*a + U2*b = U3 */ +int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3); + +/* c = [a, b] or (a*b)/(a, b) */ +int mp_lcm(mp_int *a, mp_int *b, mp_int *c); + +/* finds one of the b'th root of a, such that |c|**b <= |a| + * + * returns error if a < 0 and b is even + */ +int mp_n_root(mp_int *a, mp_digit b, mp_int *c); +int mp_n_root_ex(mp_int *a, mp_digit b, mp_int *c, int fast); + +/* special sqrt algo */ +int mp_sqrt(mp_int *arg, mp_int *ret); + +/* special sqrt (mod prime) */ +int mp_sqrtmod_prime(mp_int *arg, mp_int *prime, mp_int *ret); + +/* is number a square? */ +int mp_is_square(mp_int *arg, int *ret); + +/* computes the jacobi c = (a | n) (or Legendre if b is prime) */ +int mp_jacobi(mp_int *a, mp_int *n, int *c); + +/* used to setup the Barrett reduction for a given modulus b */ +int mp_reduce_setup(mp_int *a, mp_int *b); + +/* Barrett Reduction, computes a (mod b) with a precomputed value c + * + * Assumes that 0 < a <= b*b, note if 0 > a > -(b*b) then you can merely + * compute the reduction as -1 * mp_reduce(mp_abs(a)) [pseudo code]. + */ +int mp_reduce(mp_int *a, mp_int *b, mp_int *c); + +/* setups the montgomery reduction */ +int mp_montgomery_setup(mp_int *a, mp_digit *mp); + +/* computes a = B**n mod b without division or multiplication useful for + * normalizing numbers in a Montgomery system. + */ +int mp_montgomery_calc_normalization(mp_int *a, mp_int *b); + +/* computes x/R == x (mod N) via Montgomery Reduction */ +int mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp); + +/* returns 1 if a is a valid DR modulus */ +int mp_dr_is_modulus(mp_int *a); + +/* sets the value of "d" required for mp_dr_reduce */ +void mp_dr_setup(mp_int *a, mp_digit *d); + +/* reduces a modulo b using the Diminished Radix method */ +int mp_dr_reduce(mp_int *a, mp_int *b, mp_digit mp); + +/* returns true if a can be reduced with mp_reduce_2k */ +int mp_reduce_is_2k(mp_int *a); + +/* determines k value for 2k reduction */ +int mp_reduce_2k_setup(mp_int *a, mp_digit *d); + +/* reduces a modulo b where b is of the form 2**p - k [0 <= a] */ +int mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d); + +/* returns true if a can be reduced with mp_reduce_2k_l */ +int mp_reduce_is_2k_l(mp_int *a); + +/* determines k value for 2k reduction */ +int mp_reduce_2k_setup_l(mp_int *a, mp_int *d); + +/* reduces a modulo b where b is of the form 2**p - k [0 <= a] */ +int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d); + +/* d = a**b (mod c) */ +int mp_exptmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* ---> Primes <--- */ + +/* number of primes */ +#ifdef MP_8BIT + #define PRIME_SIZE 31 +#else + #define PRIME_SIZE 256 +#endif + +/* table of first PRIME_SIZE primes */ +extern const mp_digit ltm_prime_tab[PRIME_SIZE]; + +/* result=1 if a is divisible by one of the first PRIME_SIZE primes */ +int mp_prime_is_divisible(mp_int *a, int *result); + +/* performs one Fermat test of "a" using base "b". + * Sets result to 0 if composite or 1 if probable prime + */ +int mp_prime_fermat(mp_int *a, mp_int *b, int *result); + +/* performs one Miller-Rabin test of "a" using base "b". + * Sets result to 0 if composite or 1 if probable prime + */ +int mp_prime_miller_rabin(mp_int *a, mp_int *b, int *result); + +/* This gives [for a given bit size] the number of trials required + * such that Miller-Rabin gives a prob of failure lower than 2^-96 + */ +int mp_prime_rabin_miller_trials(int size); + +/* performs t rounds of Miller-Rabin on "a" using the first + * t prime bases. Also performs an initial sieve of trial + * division. Determines if "a" is prime with probability + * of error no more than (1/4)**t. + * + * Sets result to 1 if probably prime, 0 otherwise + */ +int mp_prime_is_prime(mp_int *a, int t, int *result); + +/* finds the next prime after the number "a" using "t" trials + * of Miller-Rabin. + * + * bbs_style = 1 means the prime must be congruent to 3 mod 4 + */ +int mp_prime_next_prime(mp_int *a, int t, int bbs_style); + +/* makes a truly random prime of a given size (bytes), + * call with bbs = 1 if you want it to be congruent to 3 mod 4 + * + * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can + * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself + * so it can be NULL + * + * The prime generated will be larger than 2^(8*size). + */ +#define mp_prime_random(a, t, size, bbs, cb, dat) mp_prime_random_ex(a, t, ((size) * 8) + 1, (bbs == 1) ? LTM_PRIME_BBS : 0, cb, dat) + +/* makes a truly random prime of a given size (bits), + * + * Flags are as follows: + * + * LTM_PRIME_BBS - make prime congruent to 3 mod 4 + * LTM_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS) + * LTM_PRIME_2MSB_ON - make the 2nd highest bit one + * + * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can + * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself + * so it can be NULL + * + */ +int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat); + +/* ---> radix conversion <--- */ +int mp_count_bits(mp_int *a); + +int mp_unsigned_bin_size(mp_int *a); +int mp_read_unsigned_bin(mp_int *a, const unsigned char *b, int c); +int mp_to_unsigned_bin(mp_int *a, unsigned char *b); +int mp_to_unsigned_bin_n(mp_int *a, unsigned char *b, unsigned long *outlen); + +int mp_signed_bin_size(mp_int *a); +int mp_read_signed_bin(mp_int *a, const unsigned char *b, int c); +int mp_to_signed_bin(mp_int *a, unsigned char *b); +int mp_to_signed_bin_n(mp_int *a, unsigned char *b, unsigned long *outlen); + +int mp_read_radix(mp_int *a, const char *str, int radix); +int mp_toradix(mp_int *a, char *str, int radix); +int mp_toradix_n(mp_int *a, char *str, int radix, int maxlen); +int mp_radix_size(mp_int *a, int radix, int *size); + +#ifndef LTM_NO_FILE +int mp_fread(mp_int *a, int radix, FILE *stream); +int mp_fwrite(mp_int *a, int radix, FILE *stream); +#endif + +#define mp_read_raw(mp, str, len) mp_read_signed_bin((mp), (str), (len)) +#define mp_raw_size(mp) mp_signed_bin_size(mp) +#define mp_toraw(mp, str) mp_to_signed_bin((mp), (str)) +#define mp_read_mag(mp, str, len) mp_read_unsigned_bin((mp), (str), (len)) +#define mp_mag_size(mp) mp_unsigned_bin_size(mp) +#define mp_tomag(mp, str) mp_to_unsigned_bin((mp), (str)) + +#define mp_tobinary(M, S) mp_toradix((M), (S), 2) +#define mp_tooctal(M, S) mp_toradix((M), (S), 8) +#define mp_todecimal(M, S) mp_toradix((M), (S), 10) +#define mp_tohex(M, S) mp_toradix((M), (S), 16) + +#ifdef __cplusplus +} +#endif +#endif + + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://math.libtomcrypt.com + */ +#ifndef TOMMATH_PRIV_H_ +#define TOMMATH_PRIV_H_ + +#include + +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) + +#ifdef __cplusplus +extern "C" { +/* C++ compilers don't like assigning void * to mp_digit * */ + #define OPT_CAST(x) (x *) + +#else + +/* C on the other hand doesn't care */ + #define OPT_CAST(x) +#endif + +/* define heap macros */ +#ifndef XMALLOC +/* default to libc stuff */ + #define XMALLOC malloc + #define XFREE free + #define XREALLOC realloc + #define XCALLOC calloc +#else +/* prototypes for our heap functions */ +extern void *XMALLOC(size_t n); +extern void *XREALLOC(void *p, size_t n); +extern void *XCALLOC(size_t n, size_t s); +extern void XFREE(void *p); +#endif + +/* lowlevel functions, do not call! */ +int s_mp_add(mp_int *a, mp_int *b, mp_int *c); +int s_mp_sub(mp_int *a, mp_int *b, mp_int *c); + +#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1) +int fast_s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int fast_s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int fast_s_mp_sqr(mp_int *a, mp_int *b); +int s_mp_sqr(mp_int *a, mp_int *b); +int mp_karatsuba_mul(mp_int *a, mp_int *b, mp_int *c); +int mp_toom_mul(mp_int *a, mp_int *b, mp_int *c); +int mp_karatsuba_sqr(mp_int *a, mp_int *b); +int mp_toom_sqr(mp_int *a, mp_int *b); +int fast_mp_invmod(mp_int *a, mp_int *b, mp_int *c); +int mp_invmod_slow(mp_int *a, mp_int *b, mp_int *c); +int fast_mp_montgomery_reduce(mp_int *x, mp_int *n, mp_digit rho); +int mp_exptmod_fast(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int redmode); +int s_mp_exptmod(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int redmode); +void bn_reverse(unsigned char *s, int len); + +extern const char *mp_s_rmap; + +/* Fancy macro to set an MPI from another type. + * There are several things assumed: + * x is the counter and unsigned + * a is the pointer to the MPI + * b is the original value that should be set in the MPI. + */ +#define MP_SET_XLONG(func_name, type) \ + int func_name(mp_int * a, type b) \ + { \ + unsigned int x; \ + int res; \ + \ + mp_zero(a); \ + \ + /* set four bits at a time */ \ + for (x = 0; x < (sizeof(type) * 2u); x++) { \ + /* shift the number up four bits */ \ + if ((res = mp_mul_2d(a, 4, a)) != MP_OKAY) { \ + return res; \ + } \ + \ + /* OR in the top four bits of the source */ \ + a->dp[0] |= (b >> ((sizeof(type) * 8u) - 4u)) & 15u; \ + \ + /* shift the source up to the next four bits */ \ + b <<= 4; \ + \ + /* ensure that digits are not clamped off */ \ + a->used += 1; \ + } \ + mp_clamp(a); \ + return MP_OKAY; \ + } + +#ifdef __cplusplus +} +#endif +#endif + + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + +#define BN_FAST_MP_INVMOD_C +#ifdef BN_FAST_MP_INVMOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* computes the modular inverse via binary extended euclidean algorithm, + * that is c = 1/a mod b + * + * Based on slow invmod except this is optimized for the case where b is + * odd as per HAC Note 14.64 on pp. 610 + */ +int fast_mp_invmod(mp_int *a, mp_int *b, mp_int *c) { + mp_int x, y, u, v, B, D; + int res, neg; + + /* 2. [modified] b must be odd */ + if (mp_iseven(b) == MP_YES) { + return MP_VAL; + } + + /* init all our temps */ + if ((res = mp_init_multi(&x, &y, &u, &v, &B, &D, NULL)) != MP_OKAY) { + return res; + } + + /* x == modulus, y == value to invert */ + if ((res = mp_copy(b, &x)) != MP_OKAY) { + goto LBL_ERR; + } + + /* we need y = |a| */ + if ((res = mp_mod(a, b, &y)) != MP_OKAY) { + goto LBL_ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy(&x, &u)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy(&y, &v)) != MP_OKAY) { + goto LBL_ERR; + } + mp_set(&D, 1); + +top: + /* 4. while u is even do */ + while (mp_iseven(&u) == MP_YES) { + /* 4.1 u = u/2 */ + if ((res = mp_div_2(&u, &u)) != MP_OKAY) { + goto LBL_ERR; + } + /* 4.2 if B is odd then */ + if (mp_isodd(&B) == MP_YES) { + if ((res = mp_sub(&B, &x, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* B = B/2 */ + if ((res = mp_div_2(&B, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 5. while v is even do */ + while (mp_iseven(&v) == MP_YES) { + /* 5.1 v = v/2 */ + if ((res = mp_div_2(&v, &v)) != MP_OKAY) { + goto LBL_ERR; + } + /* 5.2 if D is odd then */ + if (mp_isodd(&D) == MP_YES) { + /* D = (D-x)/2 */ + if ((res = mp_sub(&D, &x, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* D = D/2 */ + if ((res = mp_div_2(&D, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 6. if u >= v then */ + if (mp_cmp(&u, &v) != MP_LT) { + /* u = u - v, B = B - D */ + if ((res = mp_sub(&u, &v, &u)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub(&B, &D, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } else { + /* v - v - u, D = D - B */ + if ((res = mp_sub(&v, &u, &v)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub(&D, &B, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* if not zero goto step 4 */ + if (mp_iszero(&u) == MP_NO) { + goto top; + } + + /* now a = C, b = D, gcd == g*v */ + + /* if v != 1 then there is no inverse */ + if (mp_cmp_d(&v, 1) != MP_EQ) { + res = MP_VAL; + goto LBL_ERR; + } + + /* b is now the inverse */ + neg = a->sign; + while (D.sign == MP_NEG) { + if ((res = mp_add(&D, b, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + mp_exch(&D, c); + c->sign = neg; + res = MP_OKAY; + +LBL_ERR: mp_clear_multi(&x, &y, &u, &v, &B, &D, NULL); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* computes xR**-1 == x (mod N) via Montgomery Reduction + * + * This is an optimized implementation of montgomery_reduce + * which uses the comba method to quickly calculate the columns of the + * reduction. + * + * Based on Algorithm 14.32 on pp.601 of HAC. + */ +int fast_mp_montgomery_reduce(mp_int *x, mp_int *n, mp_digit rho) { + int ix, res, olduse; + mp_word W[MP_WARRAY]; + + /* get old used count */ + olduse = x->used; + + /* grow a as required */ + if (x->alloc < (n->used + 1)) { + if ((res = mp_grow(x, n->used + 1)) != MP_OKAY) { + return res; + } + } + + /* first we have to get the digits of the input into + * an array of double precision words W[...] + */ + { + mp_word *_W; + mp_digit *tmpx; + + /* alias for the W[] array */ + _W = W; + + /* alias for the digits of x*/ + tmpx = x->dp; + + /* copy the digits of a into W[0..a->used-1] */ + for (ix = 0; ix < x->used; ix++) { + *_W++ = *tmpx++; + } + + /* zero the high words of W[a->used..m->used*2] */ + for ( ; ix < ((n->used * 2) + 1); ix++) { + *_W++ = 0; + } + } + + /* now we proceed to zero successive digits + * from the least significant upwards + */ + for (ix = 0; ix < n->used; ix++) { + /* mu = ai * m' mod b + * + * We avoid a double precision multiplication (which isn't required) + * by casting the value down to a mp_digit. Note this requires + * that W[ix-1] have the carry cleared (see after the inner loop) + */ + mp_digit mu; + mu = (mp_digit)(((W[ix] & MP_MASK) * rho) & MP_MASK); + + /* a = a + mu * m * b**i + * + * This is computed in place and on the fly. The multiplication + * by b**i is handled by offseting which columns the results + * are added to. + * + * Note the comba method normally doesn't handle carries in the + * inner loop In this case we fix the carry from the previous + * column since the Montgomery reduction requires digits of the + * result (so far) [see above] to work. This is + * handled by fixing up one carry after the inner loop. The + * carry fixups are done in order so after these loops the + * first m->used words of W[] have the carries fixed + */ + { + int iy; + mp_digit *tmpn; + mp_word *_W; + + /* alias for the digits of the modulus */ + tmpn = n->dp; + + /* Alias for the columns set by an offset of ix */ + _W = W + ix; + + /* inner loop */ + for (iy = 0; iy < n->used; iy++) { + *_W++ += ((mp_word)mu) * ((mp_word) * tmpn++); + } + } + + /* now fix carry for next digit, W[ix+1] */ + W[ix + 1] += W[ix] >> ((mp_word)DIGIT_BIT); + } + + /* now we have to propagate the carries and + * shift the words downward [all those least + * significant digits we zeroed]. + */ + { + mp_digit *tmpx; + mp_word *_W, *_W1; + + /* nox fix rest of carries */ + + /* alias for current word */ + _W1 = W + ix; + + /* alias for next word, where the carry goes */ + _W = W + ++ix; + + for ( ; ix <= ((n->used * 2) + 1); ix++) { + *_W++ += *_W1++ >> ((mp_word)DIGIT_BIT); + } + + /* copy out, A = A/b**n + * + * The result is A/b**n but instead of converting from an + * array of mp_word to mp_digit than calling mp_rshd + * we just copy them in the right order + */ + + /* alias for destination word */ + tmpx = x->dp; + + /* alias for shifted double precision result */ + _W = W + n->used; + + for (ix = 0; ix < (n->used + 1); ix++) { + *tmpx++ = (mp_digit)(*_W++ & ((mp_word)MP_MASK)); + } + + /* zero oldused digits, if the input a was larger than + * m->used+1 we'll have to clear the digits + */ + for ( ; ix < olduse; ix++) { + *tmpx++ = 0; + } + } + + /* set the max used and clamp */ + x->used = n->used + 1; + mp_clamp(x); + + /* if A >= m then A = A - m */ + if (mp_cmp_mag(x, n) != MP_LT) { + return s_mp_sub(x, n, x); + } + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_FAST_S_MP_MUL_DIGS_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* Fast (comba) multiplier + * + * This is the fast column-array [comba] multiplier. It is + * designed to compute the columns of the product first + * then handle the carries afterwards. This has the effect + * of making the nested loops that compute the columns very + * simple and schedulable on super-scalar processors. + * + * This has been modified to produce a variable number of + * digits of output so if say only a half-product is required + * you don't have to compute the upper half (a feature + * required for fast Barrett reduction). + * + * Based on Algorithm 14.12 on pp.595 of HAC. + * + */ +int fast_s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs) { + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY]; + mp_word _W; + + /* grow the destination as required */ + if (c->alloc < digs) { + if ((res = mp_grow(c, digs)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + pa = MIN(digs, a->used + b->used); + + /* clear the carry */ + _W = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty; + int iy; + mp_digit *tmpx, *tmpy; + + /* get offsets into the two bignums */ + ty = MIN(b->used - 1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = b->dp + ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used - tx, ty + 1); + + /* execute loop */ + for (iz = 0; iz < iy; ++iz) { + _W += ((mp_word) * tmpx++) * ((mp_word) * tmpy--); + } + + /* store term */ + W[ix] = ((mp_digit)_W) & MP_MASK; + + /* make next carry */ + _W = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = c->used; + c->used = pa; + + { + mp_digit *tmpc; + tmpc = c->dp; + for (ix = 0; ix < (pa + 1); ix++) { + /* now extract the previous digit [below the carry] */ + *tmpc++ = W[ix]; + } + + /* clear unused digits [that existed in the old copy of c] */ + for ( ; ix < olduse; ix++) { + *tmpc++ = 0; + } + } + mp_clamp(c); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* this is a modified version of fast_s_mul_digs that only produces + * output digits *above* digs. See the comments for fast_s_mul_digs + * to see how it works. + * + * This is used in the Barrett reduction since for one of the multiplications + * only the higher digits were needed. This essentially halves the work. + * + * Based on Algorithm 14.12 on pp.595 of HAC. + */ +int fast_s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs) { + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY]; + mp_word _W; + + /* grow the destination as required */ + pa = a->used + b->used; + if (c->alloc < pa) { + if ((res = mp_grow(c, pa)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + pa = a->used + b->used; + _W = 0; + for (ix = digs; ix < pa; ix++) { + int tx, ty, iy; + mp_digit *tmpx, *tmpy; + + /* get offsets into the two bignums */ + ty = MIN(b->used - 1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = b->dp + ty; + + /* this is the number of times the loop will iterrate, essentially its + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used - tx, ty + 1); + + /* execute loop */ + for (iz = 0; iz < iy; iz++) { + _W += ((mp_word) * tmpx++) * ((mp_word) * tmpy--); + } + + /* store term */ + W[ix] = ((mp_digit)_W) & MP_MASK; + + /* make next carry */ + _W = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = c->used; + c->used = pa; + + { + mp_digit *tmpc; + + tmpc = c->dp + digs; + for (ix = digs; ix < pa; ix++) { + /* now extract the previous digit [below the carry] */ + *tmpc++ = W[ix]; + } + + /* clear unused digits [that existed in the old copy of c] */ + for ( ; ix < olduse; ix++) { + *tmpc++ = 0; + } + } + mp_clamp(c); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_FAST_S_MP_SQR_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* the jist of squaring... + * you do like mult except the offset of the tmpx [one that + * starts closer to zero] can't equal the offset of tmpy. + * So basically you set up iy like before then you min it with + * (ty-tx) so that it never happens. You double all those + * you add in the inner loop + + After that loop you do the squares and add them in. + */ + +int fast_s_mp_sqr(mp_int *a, mp_int *b) { + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY], *tmpx; + mp_word W1; + + /* grow the destination as required */ + pa = a->used + a->used; + if (b->alloc < pa) { + if ((res = mp_grow(b, pa)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + W1 = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty, iy; + mp_word _W; + mp_digit *tmpy; + + /* clear counter */ + _W = 0; + + /* get offsets into the two bignums */ + ty = MIN(a->used - 1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = a->dp + ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used - tx, ty + 1); + + /* now for squaring tx can never equal ty + * we halve the distance since they approach at a rate of 2x + * and we have to round because odd cases need to be executed + */ + iy = MIN(iy, ((ty - tx) + 1) >> 1); + + /* execute loop */ + for (iz = 0; iz < iy; iz++) { + _W += ((mp_word) * tmpx++) * ((mp_word) * tmpy--); + } + + /* double the inner product and add carry */ + _W = _W + _W + W1; + + /* even columns have the square term in them */ + if ((ix & 1) == 0) { + _W += ((mp_word)a->dp[ix >> 1]) * ((mp_word)a->dp[ix >> 1]); + } + + /* store it */ + W[ix] = (mp_digit)(_W & MP_MASK); + + /* make next carry */ + W1 = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = b->used; + b->used = a->used + a->used; + + { + mp_digit *tmpb; + tmpb = b->dp; + for (ix = 0; ix < pa; ix++) { + *tmpb++ = W[ix] & MP_MASK; + } + + /* clear unused digits [that existed in the old copy of c] */ + for ( ; ix < olduse; ix++) { + *tmpb++ = 0; + } + } + mp_clamp(b); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_2EXPT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* computes a = 2**b + * + * Simple algorithm which zeroes the int, grows it then just sets one bit + * as required. + */ +int +mp_2expt(mp_int *a, int b) { + int res; + + /* zero a as per default */ + mp_zero(a); + + /* grow a to accomodate the single bit */ + if ((res = mp_grow(a, (b / DIGIT_BIT) + 1)) != MP_OKAY) { + return res; + } + + /* set the used count of where the bit will go */ + a->used = (b / DIGIT_BIT) + 1; + + /* put the single bit in its place */ + a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT); + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_ABS_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* b = |a| + * + * Simple function copies the input and fixes the sign to positive + */ +int +mp_abs(mp_int *a, mp_int *b) { + int res; + + /* copy a to b */ + if (a != b) { + if ((res = mp_copy(a, b)) != MP_OKAY) { + return res; + } + } + + /* force the sign of b to positive */ + b->sign = MP_ZPOS; + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_ADD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* high level addition (handles signs) */ +int mp_add(mp_int *a, mp_int *b, mp_int *c) { + int sa, sb, res; + + /* get sign of both inputs */ + sa = a->sign; + sb = b->sign; + + /* handle two cases, not four */ + if (sa == sb) { + /* both positive or both negative */ + /* add their magnitudes, copy the sign */ + c->sign = sa; + res = s_mp_add(a, b, c); + } else { + /* one positive, the other negative */ + /* subtract the one with the greater magnitude from */ + /* the one of the lesser magnitude. The result gets */ + /* the sign of the one with the greater magnitude. */ + if (mp_cmp_mag(a, b) == MP_LT) { + c->sign = sb; + res = s_mp_sub(b, a, c); + } else { + c->sign = sa; + res = s_mp_sub(a, b, c); + } + } + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_ADD_D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* single digit addition */ +int +mp_add_d(mp_int *a, mp_digit b, mp_int *c) { + int res, ix, oldused; + mp_digit *tmpa, *tmpc, mu; + + /* grow c as required */ + if (c->alloc < (a->used + 1)) { + if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* if a is negative and |a| >= b, call c = |a| - b */ + if ((a->sign == MP_NEG) && ((a->used > 1) || (a->dp[0] >= b))) { + /* temporarily fix sign of a */ + a->sign = MP_ZPOS; + + /* c = |a| - b */ + res = mp_sub_d(a, b, c); + + /* fix sign */ + a->sign = c->sign = MP_NEG; + + /* clamp */ + mp_clamp(c); + + return res; + } + + /* old number of used digits in c */ + oldused = c->used; + + /* sign always positive */ + c->sign = MP_ZPOS; + + /* source alias */ + tmpa = a->dp; + + /* destination alias */ + tmpc = c->dp; + + /* if a is positive */ + if (a->sign == MP_ZPOS) { + /* add digit, after this we're propagating + * the carry. + */ + *tmpc = *tmpa++ + b; + mu = *tmpc >> DIGIT_BIT; + *tmpc++ &= MP_MASK; + + /* now handle rest of the digits */ + for (ix = 1; ix < a->used; ix++) { + *tmpc = *tmpa++ + mu; + mu = *tmpc >> DIGIT_BIT; + *tmpc++ &= MP_MASK; + } + /* set final carry */ + ix++; + *tmpc++ = mu; + + /* setup size */ + c->used = a->used + 1; + } else { + /* a was negative and |a| < b */ + c->used = 1; + + /* the result is a single digit */ + if (a->used == 1) { + *tmpc++ = b - a->dp[0]; + } else { + *tmpc++ = b; + } + + /* setup count so the clearing of oldused + * can fall through correctly + */ + ix = 1; + } + + /* now zero to oldused */ + while (ix++ < oldused) { + *tmpc++ = 0; + } + mp_clamp(c); + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_ADDMOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* d = a + b (mod c) */ +int +mp_addmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d) { + int res; + mp_int t; + + if ((res = mp_init(&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_add(a, b, &t)) != MP_OKAY) { + mp_clear(&t); + return res; + } + res = mp_mod(&t, c, d); + mp_clear(&t); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_AND_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* AND two ints together */ +int +mp_and(mp_int *a, mp_int *b, mp_int *c) { + int res, ix, px; + mp_int t, *x; + + if (a->used > b->used) { + if ((res = mp_init_copy(&t, a)) != MP_OKAY) { + return res; + } + px = b->used; + x = b; + } else { + if ((res = mp_init_copy(&t, b)) != MP_OKAY) { + return res; + } + px = a->used; + x = a; + } + + for (ix = 0; ix < px; ix++) { + t.dp[ix] &= x->dp[ix]; + } + + /* zero digits above the last from the smallest mp_int */ + for ( ; ix < t.used; ix++) { + t.dp[ix] = 0; + } + + mp_clamp(&t); + mp_exch(c, &t); + mp_clear(&t); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_CLAMP_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* trim unused digits + * + * This is used to ensure that leading zero digits are + * trimed and the leading "used" digit will be non-zero + * Typically very fast. Also fixes the sign if there + * are no more leading digits + */ +void +mp_clamp(mp_int *a) { + /* decrease used while the most significant digit is + * zero. + */ + while ((a->used > 0) && (a->dp[a->used - 1] == 0)) { + --(a->used); + } + + /* reset the sign flag if used == 0 */ + if (a->used == 0) { + a->sign = MP_ZPOS; + } +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_CLEAR_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* clear one (frees) */ +void +mp_clear(mp_int *a) { + int i; + + /* only do anything if a hasn't been freed previously */ + if (a->dp != NULL) { + /* first zero the digits */ + for (i = 0; i < a->used; i++) { + a->dp[i] = 0; + } + + /* free ram */ + XFREE(a->dp); + + /* reset members to make debugging easier */ + a->dp = NULL; + a->alloc = a->used = 0; + a->sign = MP_ZPOS; + } +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_CLEAR_MULTI_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ +#include + +void mp_clear_multi(mp_int *mp, ...) { + mp_int *next_mp = mp; + va_list args; + + va_start(args, mp); + while (next_mp != NULL) { + mp_clear(next_mp); + next_mp = va_arg(args, mp_int *); + } + va_end(args); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_CMP_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* compare two ints (signed)*/ +int +mp_cmp(mp_int *a, mp_int *b) { + /* compare based on sign */ + if (a->sign != b->sign) { + if (a->sign == MP_NEG) { + return MP_LT; + } else { + return MP_GT; + } + } + + /* compare digits */ + if (a->sign == MP_NEG) { + /* if negative compare opposite direction */ + return mp_cmp_mag(b, a); + } else { + return mp_cmp_mag(a, b); + } +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_CMP_D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* compare a digit */ +int mp_cmp_d(mp_int *a, mp_digit b) { + /* compare based on sign */ + if (a->sign == MP_NEG) { + return MP_LT; + } + + /* compare based on magnitude */ + if (a->used > 1) { + return MP_GT; + } + + /* compare the only digit of a to b */ + if (a->dp[0] > b) { + return MP_GT; + } else if (a->dp[0] < b) { + return MP_LT; + } else { + return MP_EQ; + } +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_CMP_MAG_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* compare maginitude of two ints (unsigned) */ +int mp_cmp_mag(mp_int *a, mp_int *b) { + int n; + mp_digit *tmpa, *tmpb; + + /* compare based on # of non-zero digits */ + if (a->used > b->used) { + return MP_GT; + } + + if (a->used < b->used) { + return MP_LT; + } + + /* alias for a */ + tmpa = a->dp + (a->used - 1); + + /* alias for b */ + tmpb = b->dp + (a->used - 1); + + /* compare based on digits */ + for (n = 0; n < a->used; ++n, --tmpa, --tmpb) { + if (*tmpa > *tmpb) { + return MP_GT; + } + + if (*tmpa < *tmpb) { + return MP_LT; + } + } + return MP_EQ; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_CNT_LSB_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +static const int lnz[16] = { + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 +}; + +/* Counts the number of lsbs which are zero before the first zero bit */ +int mp_cnt_lsb(mp_int *a) { + int x; + mp_digit q, qq; + + /* easy out */ + if (mp_iszero(a) == MP_YES) { + return 0; + } + + /* scan lower digits until non-zero */ + for (x = 0; (x < a->used) && (a->dp[x] == 0); x++) { + } + q = a->dp[x]; + x *= DIGIT_BIT; + + /* now scan this digit until a 1 is found */ + if ((q & 1) == 0) { + do { + qq = q & 15; + x += lnz[qq]; + q >>= 4; + } while (qq == 0); + } + return x; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_COPY_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* copy, b = a */ +int +mp_copy(mp_int *a, mp_int *b) { + int res, n; + + /* if dst == src do nothing */ + if (a == b) { + return MP_OKAY; + } + + /* grow dest */ + if (b->alloc < a->used) { + if ((res = mp_grow(b, a->used)) != MP_OKAY) { + return res; + } + } + + /* zero b and copy the parameters over */ + { + mp_digit *tmpa, *tmpb; + + /* pointer aliases */ + + /* source */ + tmpa = a->dp; + + /* destination */ + tmpb = b->dp; + + /* copy all the digits */ + for (n = 0; n < a->used; n++) { + *tmpb++ = *tmpa++; + } + + /* clear high digits */ + for ( ; n < b->used; n++) { + *tmpb++ = 0; + } + } + + /* copy used count and sign */ + b->used = a->used; + b->sign = a->sign; + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_COUNT_BITS_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* returns the number of bits in an int */ +int +mp_count_bits(mp_int *a) { + int r; + mp_digit q; + + /* shortcut */ + if (a->used == 0) { + return 0; + } + + /* get number of digits and add that */ + r = (a->used - 1) * DIGIT_BIT; + + /* take the last digit and count the bits in it */ + q = a->dp[a->used - 1]; + while (q > ((mp_digit)0)) { + ++r; + q >>= ((mp_digit)1); + } + return r; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_DIV_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + + #ifdef BN_MP_DIV_SMALL + +/* slower bit-bang division... also smaller */ +int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d) { + mp_int ta, tb, tq, q; + int res, n, n2; + + /* is divisor zero ? */ + if (mp_iszero(b) == MP_YES) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag(a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy(a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero(c); + } + return res; + } + + /* init our temps */ + if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) { + return res; + } + + + mp_set(&tq, 1); + n = mp_count_bits(a) - mp_count_bits(b); + if (((res = mp_abs(a, &ta)) != MP_OKAY) || + ((res = mp_abs(b, &tb)) != MP_OKAY) || + ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) || + ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) { + goto LBL_ERR; + } + + while (n-- >= 0) { + if (mp_cmp(&tb, &ta) != MP_GT) { + if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) || + ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) { + goto LBL_ERR; + } + } + if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) || + ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) { + goto LBL_ERR; + } + } + + /* now q == quotient and ta == remainder */ + n = a->sign; + n2 = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + if (c != NULL) { + mp_exch(c, &q); + c->sign = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2; + } + if (d != NULL) { + mp_exch(d, &ta); + d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n; + } +LBL_ERR: + mp_clear_multi(&ta, &tb, &tq, &q, NULL); + return res; +} + + #else + +/* integer signed division. + * c*b + d == a [e.g. a/b, c=quotient, d=remainder] + * HAC pp.598 Algorithm 14.20 + * + * Note that the description in HAC is horribly + * incomplete. For example, it doesn't consider + * the case where digits are removed from 'x' in + * the inner loop. It also doesn't consider the + * case that y has fewer than three digits, etc.. + * + * The overall algorithm is as described as + * 14.20 from HAC but fixed to treat these cases. + */ +int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d) { + mp_int q, x, y, t1, t2; + int res, n, t, i, norm, neg; + + /* is divisor zero ? */ + if (mp_iszero(b) == MP_YES) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag(a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy(a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero(c); + } + return res; + } + + if ((res = mp_init_size(&q, a->used + 2)) != MP_OKAY) { + return res; + } + q.used = a->used + 2; + + if ((res = mp_init(&t1)) != MP_OKAY) { + goto LBL_Q; + } + + if ((res = mp_init(&t2)) != MP_OKAY) { + goto LBL_T1; + } + + if ((res = mp_init_copy(&x, a)) != MP_OKAY) { + goto LBL_T2; + } + + if ((res = mp_init_copy(&y, b)) != MP_OKAY) { + goto LBL_X; + } + + /* fix the sign */ + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + x.sign = y.sign = MP_ZPOS; + + /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */ + norm = mp_count_bits(&y) % DIGIT_BIT; + if (norm < (int)(DIGIT_BIT - 1)) { + norm = (DIGIT_BIT - 1) - norm; + if ((res = mp_mul_2d(&x, norm, &x)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_mul_2d(&y, norm, &y)) != MP_OKAY) { + goto LBL_Y; + } + } else { + norm = 0; + } + + /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */ + n = x.used - 1; + t = y.used - 1; + + /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */ + if ((res = mp_lshd(&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */ + goto LBL_Y; + } + + while (mp_cmp(&x, &y) != MP_LT) { + ++(q.dp[n - t]); + if ((res = mp_sub(&x, &y, &x)) != MP_OKAY) { + goto LBL_Y; + } + } + + /* reset y by shifting it back down */ + mp_rshd(&y, n - t); + + /* step 3. for i from n down to (t + 1) */ + for (i = n; i >= (t + 1); i--) { + if (i > x.used) { + continue; + } + + /* step 3.1 if xi == yt then set q{i-t-1} to b-1, + * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ + if (x.dp[i] == y.dp[t]) { + q.dp[(i - t) - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1); + } else { + mp_word tmp; + tmp = ((mp_word)x.dp[i]) << ((mp_word)DIGIT_BIT); + tmp |= ((mp_word)x.dp[i - 1]); + tmp /= ((mp_word)y.dp[t]); + if (tmp > (mp_word)MP_MASK) { + tmp = MP_MASK; + } + q.dp[(i - t) - 1] = (mp_digit)(tmp & (mp_word)(MP_MASK)); + } + + /* while (q{i-t-1} * (yt * b + y{t-1})) > + xi * b**2 + xi-1 * b + xi-2 + + do q{i-t-1} -= 1; + */ + q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] + 1) & MP_MASK; + do { + q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] - 1) & MP_MASK; + + /* find left hand */ + mp_zero(&t1); + t1.dp[0] = ((t - 1) < 0) ? 0 : y.dp[t - 1]; + t1.dp[1] = y.dp[t]; + t1.used = 2; + if ((res = mp_mul_d(&t1, q.dp[(i - t) - 1], &t1)) != MP_OKAY) { + goto LBL_Y; + } + + /* find right hand */ + t2.dp[0] = ((i - 2) < 0) ? 0 : x.dp[i - 2]; + t2.dp[1] = ((i - 1) < 0) ? 0 : x.dp[i - 1]; + t2.dp[2] = x.dp[i]; + t2.used = 3; + } while (mp_cmp_mag(&t1, &t2) == MP_GT); + + /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */ + if ((res = mp_mul_d(&y, q.dp[(i - t) - 1], &t1)) != MP_OKAY) { + goto LBL_Y; + } + + if ((res = mp_lshd(&t1, (i - t) - 1)) != MP_OKAY) { + goto LBL_Y; + } + + if ((res = mp_sub(&x, &t1, &x)) != MP_OKAY) { + goto LBL_Y; + } + + /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */ + if (x.sign == MP_NEG) { + if ((res = mp_copy(&y, &t1)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_lshd(&t1, (i - t) - 1)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_add(&x, &t1, &x)) != MP_OKAY) { + goto LBL_Y; + } + + q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] - 1UL) & MP_MASK; + } + } + + /* now q is the quotient and x is the remainder + * [which we have to normalize] + */ + + /* get sign before writing to c */ + x.sign = (x.used == 0) ? MP_ZPOS : a->sign; + + if (c != NULL) { + mp_clamp(&q); + mp_exch(&q, c); + c->sign = neg; + } + + if (d != NULL) { + if ((res = mp_div_2d(&x, norm, &x, NULL)) != MP_OKAY) { + goto LBL_Y; + } + mp_exch(&x, d); + } + + res = MP_OKAY; + +LBL_Y: mp_clear(&y); +LBL_X: mp_clear(&x); +LBL_T2: mp_clear(&t2); +LBL_T1: mp_clear(&t1); +LBL_Q: mp_clear(&q); + return res; +} + #endif +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_DIV_2_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* b = a/2 */ +int mp_div_2(mp_int *a, mp_int *b) { + int x, res, oldused; + + /* copy */ + if (b->alloc < a->used) { + if ((res = mp_grow(b, a->used)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + { + mp_digit r, rr, *tmpa, *tmpb; + + /* source alias */ + tmpa = a->dp + b->used - 1; + + /* dest alias */ + tmpb = b->dp + b->used - 1; + + /* carry */ + r = 0; + for (x = b->used - 1; x >= 0; x--) { + /* get the carry for the next iteration */ + rr = *tmpa & 1; + + /* shift the current digit, add in carry and store */ + *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1)); + + /* forward carry to next iteration */ + r = rr; + } + + /* zero excess digits */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + mp_clamp(b); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_DIV_2D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* shift right by a certain bit count (store quotient in c, optional remainder in d) */ +int mp_div_2d(mp_int *a, int b, mp_int *c, mp_int *d) { + mp_digit D, r, rr; + int x, res; + mp_int t; + + + /* if the shift count is <= 0 then we do no work */ + if (b <= 0) { + res = mp_copy(a, c); + if (d != NULL) { + mp_zero(d); + } + return res; + } + + if ((res = mp_init(&t)) != MP_OKAY) { + return res; + } + + /* get the remainder */ + if (d != NULL) { + if ((res = mp_mod_2d(a, b, &t)) != MP_OKAY) { + mp_clear(&t); + return res; + } + } + + /* copy */ + if ((res = mp_copy(a, c)) != MP_OKAY) { + mp_clear(&t); + return res; + } + + /* shift by as many digits in the bit count */ + if (b >= (int)DIGIT_BIT) { + mp_rshd(c, b / DIGIT_BIT); + } + + /* shift any bit count < DIGIT_BIT */ + D = (mp_digit)(b % DIGIT_BIT); + if (D != 0) { + mp_digit *tmpc, mask, shift; + + /* mask */ + mask = (((mp_digit)1) << D) - 1; + + /* shift for lsb */ + shift = DIGIT_BIT - D; + + /* alias */ + tmpc = c->dp + (c->used - 1); + + /* carry */ + r = 0; + for (x = c->used - 1; x >= 0; x--) { + /* get the lower bits of this word in a temp */ + rr = *tmpc & mask; + + /* shift the current word and mix in the carry bits from the previous word */ + *tmpc = (*tmpc >> D) | (r << shift); + --tmpc; + + /* set the carry to the carry bits of the current word found above */ + r = rr; + } + } + mp_clamp(c); + if (d != NULL) { + mp_exch(&t, d); + } + mp_clear(&t); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_DIV_3_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* divide by three (based on routine from MPI and the GMP manual) */ +int +mp_div_3(mp_int *a, mp_int *c, mp_digit *d) { + mp_int q; + mp_word w, t; + mp_digit b; + int res, ix; + + /* b = 2**DIGIT_BIT / 3 */ + b = (((mp_word)1) << ((mp_word)DIGIT_BIT)) / ((mp_word)3); + + if ((res = mp_init_size(&q, a->used)) != MP_OKAY) { + return res; + } + + q.used = a->used; + q.sign = a->sign; + w = 0; + for (ix = a->used - 1; ix >= 0; ix--) { + w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]); + + if (w >= 3) { + /* multiply w by [1/3] */ + t = (w * ((mp_word)b)) >> ((mp_word)DIGIT_BIT); + + /* now subtract 3 * [w/3] from w, to get the remainder */ + w -= t + t + t; + + /* fixup the remainder as required since + * the optimization is not exact. + */ + while (w >= 3) { + t += 1; + w -= 3; + } + } else { + t = 0; + } + q.dp[ix] = (mp_digit)t; + } + + /* [optional] store the remainder */ + if (d != NULL) { + *d = (mp_digit)w; + } + + /* [optional] store the quotient */ + if (c != NULL) { + mp_clamp(&q); + mp_exch(&q, c); + } + mp_clear(&q); + + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_DIV_D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +static int s_is_power_of_two(mp_digit b, int *p) { + int x; + + /* fast return if no power of two */ + if ((b == 0) || ((b & (b - 1)) != 0)) { + return 0; + } + + for (x = 0; x < DIGIT_BIT; x++) { + if (b == (((mp_digit)1) << x)) { + *p = x; + return 1; + } + } + return 0; +} + +/* single digit division (based on routine from MPI) */ +int mp_div_d(mp_int *a, mp_digit b, mp_int *c, mp_digit *d) { + mp_int q; + mp_word w; + mp_digit t; + int res, ix; + + /* cannot divide by zero */ + if (b == 0) { + return MP_VAL; + } + + /* quick outs */ + if ((b == 1) || (mp_iszero(a) == MP_YES)) { + if (d != NULL) { + *d = 0; + } + if (c != NULL) { + return mp_copy(a, c); + } + return MP_OKAY; + } + + /* power of two ? */ + if (s_is_power_of_two(b, &ix) == 1) { + if (d != NULL) { + *d = a->dp[0] & ((((mp_digit)1) << ix) - 1); + } + if (c != NULL) { + return mp_div_2d(a, ix, c, NULL); + } + return MP_OKAY; + } + + #ifdef BN_MP_DIV_3_C + /* three? */ + if (b == 3) { + return mp_div_3(a, c, d); + } + #endif + + /* no easy answer [c'est la vie]. Just division */ + if ((res = mp_init_size(&q, a->used)) != MP_OKAY) { + return res; + } + + q.used = a->used; + q.sign = a->sign; + w = 0; + for (ix = a->used - 1; ix >= 0; ix--) { + w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]); + + if (w >= b) { + t = (mp_digit)(w / b); + w -= ((mp_word)t) * ((mp_word)b); + } else { + t = 0; + } + q.dp[ix] = (mp_digit)t; + } + + if (d != NULL) { + *d = (mp_digit)w; + } + + if (c != NULL) { + mp_clamp(&q); + mp_exch(&q, c); + } + mp_clear(&q); + + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_DR_IS_MODULUS_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* determines if a number is a valid DR modulus */ +int mp_dr_is_modulus(mp_int *a) { + int ix; + + /* must be at least two digits */ + if (a->used < 2) { + return 0; + } + + /* must be of the form b**k - a [a <= b] so all + * but the first digit must be equal to -1 (mod b). + */ + for (ix = 1; ix < a->used; ix++) { + if (a->dp[ix] != MP_MASK) { + return 0; + } + } + return 1; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_DR_REDUCE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* reduce "x" in place modulo "n" using the Diminished Radix algorithm. + * + * Based on algorithm from the paper + * + * "Generating Efficient Primes for Discrete Log Cryptosystems" + * Chae Hoon Lim, Pil Joong Lee, + * POSTECH Information Research Laboratories + * + * The modulus must be of a special format [see manual] + * + * Has been modified to use algorithm 7.10 from the LTM book instead + * + * Input x must be in the range 0 <= x <= (n-1)**2 + */ +int +mp_dr_reduce(mp_int *x, mp_int *n, mp_digit k) { + int err, i, m; + mp_word r; + mp_digit mu, *tmpx1, *tmpx2; + + /* m = digits in modulus */ + m = n->used; + + /* ensure that "x" has at least 2m digits */ + if (x->alloc < (m + m)) { + if ((err = mp_grow(x, m + m)) != MP_OKAY) { + return err; + } + } + +/* top of loop, this is where the code resumes if + * another reduction pass is required. + */ +top: + /* aliases for digits */ + /* alias for lower half of x */ + tmpx1 = x->dp; + + /* alias for upper half of x, or x/B**m */ + tmpx2 = x->dp + m; + + /* set carry to zero */ + mu = 0; + + /* compute (x mod B**m) + k * [x/B**m] inline and inplace */ + for (i = 0; i < m; i++) { + r = (((mp_word) * tmpx2++) * (mp_word)k) + *tmpx1 + mu; + *tmpx1++ = (mp_digit)(r & MP_MASK); + mu = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + } + + /* set final carry */ + *tmpx1++ = mu; + + /* zero words above m */ + for (i = m + 1; i < x->used; i++) { + *tmpx1++ = 0; + } + + /* clamp, sub and return */ + mp_clamp(x); + + /* if x >= n then subtract and reduce again + * Each successive "recursion" makes the input smaller and smaller. + */ + if (mp_cmp_mag(x, n) != MP_LT) { + if ((err = s_mp_sub(x, n, x)) != MP_OKAY) { + return err; + } + goto top; + } + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_DR_SETUP_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* determines the setup value */ +void mp_dr_setup(mp_int *a, mp_digit *d) { + /* the casts are required if DIGIT_BIT is one less than + * the number of bits in a mp_digit [e.g. DIGIT_BIT==31] + */ + *d = (mp_digit)((((mp_word)1) << ((mp_word)DIGIT_BIT)) - + ((mp_word)a->dp[0])); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_EXCH_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* swap the elements of two integers, for cases where you can't simply swap the + * mp_int pointers around + */ +void +mp_exch(mp_int *a, mp_int *b) { + mp_int t; + + t = *a; + *a = *b; + *b = t; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_EXPORT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* based on gmp's mpz_export. + * see http://gmplib.org/manual/Integer-Import-and-Export.html + */ +int mp_export(void *rop, size_t *countp, int order, size_t size, + int endian, size_t nails, mp_int *op) { + int result; + size_t odd_nails, nail_bytes, i, j, bits, count; + unsigned char odd_nail_mask; + + mp_int t; + + if ((result = mp_init_copy(&t, op)) != MP_OKAY) { + return result; + } + + if (endian == 0) { + union { + unsigned int i; + char c[4]; + } lint; + lint.i = 0x01020304; + + endian = (lint.c[0] == 4) ? -1 : 1; + } + + odd_nails = (nails % 8); + odd_nail_mask = 0xff; + for (i = 0; i < odd_nails; ++i) { + odd_nail_mask ^= (1 << (7 - i)); + } + nail_bytes = nails / 8; + + bits = mp_count_bits(&t); + count = (bits / ((size * 8) - nails)) + (((bits % ((size * 8) - nails)) != 0) ? 1 : 0); + + for (i = 0; i < count; ++i) { + for (j = 0; j < size; ++j) { + unsigned char *byte = ( + (unsigned char *)rop + + (((order == -1) ? i : ((count - 1) - i)) * size) + + ((endian == -1) ? j : ((size - 1) - j)) + ); + + if (j >= (size - nail_bytes)) { + *byte = 0; + continue; + } + + *byte = (unsigned char)((j == ((size - nail_bytes) - 1)) ? (t.dp[0] & odd_nail_mask) : (t.dp[0] & 0xFF)); + + if ((result = mp_div_2d(&t, ((j == ((size - nail_bytes) - 1)) ? (8 - odd_nails) : 8), &t, NULL)) != MP_OKAY) { + mp_clear(&t); + return result; + } + } + } + + mp_clear(&t); + + if (countp != NULL) { + *countp = count; + } + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_EXPT_D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* wrapper function for mp_expt_d_ex() */ +int mp_expt_d(mp_int *a, mp_digit b, mp_int *c) { + return mp_expt_d_ex(a, b, c, 0); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_EXPT_D_EX_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* calculate c = a**b using a square-multiply algorithm */ +int mp_expt_d_ex(mp_int *a, mp_digit b, mp_int *c, int fast) { + int res; + unsigned int x; + + mp_int g; + + if ((res = mp_init_copy(&g, a)) != MP_OKAY) { + return res; + } + + /* set initial result */ + mp_set(c, 1); + + if (fast != 0) { + while (b > 0) { + /* if the bit is set multiply */ + if ((b & 1) != 0) { + if ((res = mp_mul(c, &g, c)) != MP_OKAY) { + mp_clear(&g); + return res; + } + } + + /* square */ + if (b > 1) { + if ((res = mp_sqr(&g, &g)) != MP_OKAY) { + mp_clear(&g); + return res; + } + } + + /* shift to next bit */ + b >>= 1; + } + } else { + for (x = 0; x < DIGIT_BIT; x++) { + /* square */ + if ((res = mp_sqr(c, c)) != MP_OKAY) { + mp_clear(&g); + return res; + } + + /* if the bit is set multiply */ + if ((b & (mp_digit)(((mp_digit)1) << (DIGIT_BIT - 1))) != 0) { + if ((res = mp_mul(c, &g, c)) != MP_OKAY) { + mp_clear(&g); + return res; + } + } + + /* shift to next bit */ + b <<= 1; + } + } /* if ... else */ + + mp_clear(&g); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_EXPTMOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + + +/* this is a shell function that calls either the normal or Montgomery + * exptmod functions. Originally the call to the montgomery code was + * embedded in the normal function but that wasted alot of stack space + * for nothing (since 99% of the time the Montgomery code would be called) + */ +int mp_exptmod(mp_int *G, mp_int *X, mp_int *P, mp_int *Y) { + int dr; + + /* modulus P must be positive */ + if (P->sign == MP_NEG) { + return MP_VAL; + } + + /* if exponent X is negative we have to recurse */ + if (X->sign == MP_NEG) { + #ifdef BN_MP_INVMOD_C + mp_int tmpG, tmpX; + int err; + + /* first compute 1/G mod P */ + if ((err = mp_init(&tmpG)) != MP_OKAY) { + return err; + } + if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + + /* now get |X| */ + if ((err = mp_init(&tmpX)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + if ((err = mp_abs(X, &tmpX)) != MP_OKAY) { + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; + } + + /* and now compute (1/G)**|X| instead of G**X [X < 0] */ + err = mp_exptmod(&tmpG, &tmpX, P, Y); + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; + #else + /* no invmod */ + return MP_VAL; + #endif + } + +/* modified diminished radix reduction */ + #if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C) + if (mp_reduce_is_2k_l(P) == MP_YES) { + return s_mp_exptmod(G, X, P, Y, 1); + } + #endif + + #ifdef BN_MP_DR_IS_MODULUS_C + /* is it a DR modulus? */ + dr = mp_dr_is_modulus(P); + #else + /* default to no */ + dr = 0; + #endif + + #ifdef BN_MP_REDUCE_IS_2K_C + /* if not, is it a unrestricted DR modulus? */ + if (dr == 0) { + dr = mp_reduce_is_2k(P) << 1; + } + #endif + + /* if the modulus is odd or dr != 0 use the montgomery method */ + #ifdef BN_MP_EXPTMOD_FAST_C + if ((mp_isodd(P) == MP_YES) || (dr != 0)) { + return mp_exptmod_fast(G, X, P, Y, dr); + } else { + #endif + #ifdef BN_S_MP_EXPTMOD_C + /* otherwise use the generic Barrett reduction technique */ + return s_mp_exptmod(G, X, P, Y, 0); + #else + /* no exptmod for evens */ + return MP_VAL; + #endif + #ifdef BN_MP_EXPTMOD_FAST_C +} + #endif +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_EXPTMOD_FAST_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85 + * + * Uses a left-to-right k-ary sliding window to compute the modular exponentiation. + * The value of k changes based on the size of the exponent. + * + * Uses Montgomery or Diminished Radix reduction [whichever appropriate] + */ + + #ifdef MP_LOW_MEM + #define TAB_SIZE 32 + #else + #define TAB_SIZE 256 + #endif + +int mp_exptmod_fast(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int redmode) { + mp_int M[TAB_SIZE], res; + mp_digit buf, mp; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + + /* use a pointer to the reduction algorithm. This allows us to use + * one of many reduction algorithms without modding the guts of + * the code with if statements everywhere. + */ + int (*redux)(mp_int *, mp_int *, mp_digit); + + /* find window size */ + x = mp_count_bits(X); + if (x <= 7) { + winsize = 2; + } else if (x <= 36) { + winsize = 3; + } else if (x <= 140) { + winsize = 4; + } else if (x <= 450) { + winsize = 5; + } else if (x <= 1303) { + winsize = 6; + } else if (x <= 3529) { + winsize = 7; + } else { + winsize = 8; + } + + #ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } + #endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1 << (winsize - 1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1 << (winsize - 1); y < x; y++) { + mp_clear(&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* determine and setup reduction code */ + if (redmode == 0) { + #ifdef BN_MP_MONTGOMERY_SETUP_C + /* now setup montgomery */ + if ((err = mp_montgomery_setup(P, &mp)) != MP_OKAY) { + goto LBL_M; + } + #else + err = MP_VAL; + goto LBL_M; + #endif + + /* automatically pick the comba one if available (saves quite a few calls/ifs) */ + #ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C + if ((((P->used * 2) + 1) < MP_WARRAY) && + (P->used < (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { + redux = fast_mp_montgomery_reduce; + } else + #endif + { + #ifdef BN_MP_MONTGOMERY_REDUCE_C + /* use slower baseline Montgomery method */ + redux = mp_montgomery_reduce; + #else + err = MP_VAL; + goto LBL_M; + #endif + } + } else if (redmode == 1) { + #if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C) + /* setup DR reduction for moduli of the form B**k - b */ + mp_dr_setup(P, &mp); + redux = mp_dr_reduce; + #else + err = MP_VAL; + goto LBL_M; + #endif + } else { + #if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C) + /* setup DR reduction for moduli of the form 2**k - b */ + if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) { + goto LBL_M; + } + redux = mp_reduce_2k; + #else + err = MP_VAL; + goto LBL_M; + #endif + } + + /* setup result */ + if ((err = mp_init(&res)) != MP_OKAY) { + goto LBL_M; + } + + /* create M table + * + + * + * The first half of the table is not computed though accept for M[0] and M[1] + */ + + if (redmode == 0) { + #ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + /* now we need R mod m */ + if ((err = mp_montgomery_calc_normalization(&res, P)) != MP_OKAY) { + goto LBL_RES; + } + #else + err = MP_VAL; + goto LBL_RES; + #endif + + /* now set M[1] to G * R mod m */ + if ((err = mp_mulmod(G, &res, P, &M[1])) != MP_OKAY) { + goto LBL_RES; + } + } else { + mp_set(&res, 1); + if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { + goto LBL_RES; + } + } + + /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */ + if ((err = mp_copy(&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_RES; + } + + for (x = 0; x < (winsize - 1); x++) { + if ((err = mp_sqr(&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* create upper table */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul(&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&M[x], P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for ( ; ; ) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits so break */ + if (digidx == -1) { + break; + } + /* read next digit and reset bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int)DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if ((mode == 0) && (y == 0)) { + continue; + } + + /* if the bit is zero and mode == 1 then we square */ + if ((mode == 1) && (y == 0)) { + if ((err = mp_sqr(&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr(&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* then multiply */ + if ((err = mp_mul(&res, &M[bitbuf], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + + /* empty window and reset */ + bitcpy = 0; + bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if ((mode == 2) && (bitcpy > 0)) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr(&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + + /* get next bit of the window */ + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul(&res, &M[1], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + } + } + + if (redmode == 0) { + /* fixup result if Montgomery reduction is used + * recall that any value in a Montgomery system is + * actually multiplied by R mod n. So we have + * to reduce one more time to cancel out the factor + * of R. + */ + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* swap res with Y */ + mp_exch(&res, Y); + err = MP_OKAY; +LBL_RES: mp_clear(&res); +LBL_M: + mp_clear(&M[1]); + for (x = 1 << (winsize - 1); x < (1 << winsize); x++) { + mp_clear(&M[x]); + } + return err; +} +#endif + + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_EXTEUCLID_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* Extended euclidean algorithm of (a, b) produces + a*u1 + b*u2 = u3 + */ +int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3) { + mp_int u1, u2, u3, v1, v2, v3, t1, t2, t3, q, tmp; + int err; + + if ((err = mp_init_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL)) != MP_OKAY) { + return err; + } + + /* initialize, (u1,u2,u3) = (1,0,a) */ + mp_set(&u1, 1); + if ((err = mp_copy(a, &u3)) != MP_OKAY) { + goto _ERR; + } + + /* initialize, (v1,v2,v3) = (0,1,b) */ + mp_set(&v2, 1); + if ((err = mp_copy(b, &v3)) != MP_OKAY) { + goto _ERR; + } + + /* loop while v3 != 0 */ + while (mp_iszero(&v3) == MP_NO) { + /* q = u3/v3 */ + if ((err = mp_div(&u3, &v3, &q, NULL)) != MP_OKAY) { + goto _ERR; + } + + /* (t1,t2,t3) = (u1,u2,u3) - (v1,v2,v3)q */ + if ((err = mp_mul(&v1, &q, &tmp)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_sub(&u1, &tmp, &t1)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_mul(&v2, &q, &tmp)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_sub(&u2, &tmp, &t2)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_mul(&v3, &q, &tmp)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_sub(&u3, &tmp, &t3)) != MP_OKAY) { + goto _ERR; + } + + /* (u1,u2,u3) = (v1,v2,v3) */ + if ((err = mp_copy(&v1, &u1)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_copy(&v2, &u2)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_copy(&v3, &u3)) != MP_OKAY) { + goto _ERR; + } + + /* (v1,v2,v3) = (t1,t2,t3) */ + if ((err = mp_copy(&t1, &v1)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_copy(&t2, &v2)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_copy(&t3, &v3)) != MP_OKAY) { + goto _ERR; + } + } + + /* make sure U3 >= 0 */ + if (u3.sign == MP_NEG) { + if ((err = mp_neg(&u1, &u1)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_neg(&u2, &u2)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_neg(&u3, &u3)) != MP_OKAY) { + goto _ERR; + } + } + + /* copy result out */ + if (U1 != NULL) { + mp_exch(U1, &u1); + } + if (U2 != NULL) { + mp_exch(U2, &u2); + } + if (U3 != NULL) { + mp_exch(U3, &u3); + } + + err = MP_OKAY; +_ERR: mp_clear_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL); + return err; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_FREAD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* read a bigint from a file stream in ASCII */ +int mp_fread(mp_int *a, int radix, FILE *stream) { + int err, ch, neg, y; + + /* clear a */ + mp_zero(a); + + /* if first digit is - then set negative */ + ch = fgetc(stream); + if (ch == '-') { + neg = MP_NEG; + ch = fgetc(stream); + } else { + neg = MP_ZPOS; + } + + for ( ; ; ) { + /* find y in the radix map */ + for (y = 0; y < radix; y++) { + if (mp_s_rmap[y] == ch) { + break; + } + } + if (y == radix) { + break; + } + + /* shift up and add */ + if ((err = mp_mul_d(a, radix, a)) != MP_OKAY) { + return err; + } + if ((err = mp_add_d(a, y, a)) != MP_OKAY) { + return err; + } + + ch = fgetc(stream); + } + if (mp_cmp_d(a, 0) != MP_EQ) { + a->sign = neg; + } + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_FWRITE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +int mp_fwrite(mp_int *a, int radix, FILE *stream) { + char *buf; + int err, len, x; + + if ((err = mp_radix_size(a, radix, &len)) != MP_OKAY) { + return err; + } + + buf = OPT_CAST(char) XMALLOC(len); + if (buf == NULL) { + return MP_MEM; + } + + if ((err = mp_toradix(a, buf, radix)) != MP_OKAY) { + XFREE(buf); + return err; + } + + for (x = 0; x < len; x++) { + if (fputc(buf[x], stream) == EOF) { + XFREE(buf); + return MP_VAL; + } + } + + XFREE(buf); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_GCD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* Greatest Common Divisor using the binary method */ +int mp_gcd(mp_int *a, mp_int *b, mp_int *c) { + mp_int u, v; + int k, u_lsb, v_lsb, res; + + /* either zero than gcd is the largest */ + if (mp_iszero(a) == MP_YES) { + return mp_abs(b, c); + } + if (mp_iszero(b) == MP_YES) { + return mp_abs(a, c); + } + + /* get copies of a and b we can modify */ + if ((res = mp_init_copy(&u, a)) != MP_OKAY) { + return res; + } + + if ((res = mp_init_copy(&v, b)) != MP_OKAY) { + goto LBL_U; + } + + /* must be positive for the remainder of the algorithm */ + u.sign = v.sign = MP_ZPOS; + + /* B1. Find the common power of two for u and v */ + u_lsb = mp_cnt_lsb(&u); + v_lsb = mp_cnt_lsb(&v); + k = MIN(u_lsb, v_lsb); + + if (k > 0) { + /* divide the power of two out */ + if ((res = mp_div_2d(&u, k, &u, NULL)) != MP_OKAY) { + goto LBL_V; + } + + if ((res = mp_div_2d(&v, k, &v, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + /* divide any remaining factors of two out */ + if (u_lsb != k) { + if ((res = mp_div_2d(&u, u_lsb - k, &u, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + if (v_lsb != k) { + if ((res = mp_div_2d(&v, v_lsb - k, &v, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + while (mp_iszero(&v) == MP_NO) { + /* make sure v is the largest */ + if (mp_cmp_mag(&u, &v) == MP_GT) { + /* swap u and v to make sure v is >= u */ + mp_exch(&u, &v); + } + + /* subtract smallest from largest */ + if ((res = s_mp_sub(&v, &u, &v)) != MP_OKAY) { + goto LBL_V; + } + + /* Divide out all factors of two */ + if ((res = mp_div_2d(&v, mp_cnt_lsb(&v), &v, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + /* multiply by 2**k which we divided out at the beginning */ + if ((res = mp_mul_2d(&u, k, c)) != MP_OKAY) { + goto LBL_V; + } + c->sign = MP_ZPOS; + res = MP_OKAY; +LBL_V: mp_clear(&u); +LBL_U: mp_clear(&v); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_GET_INT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* get the lower 32-bits of an mp_int */ +unsigned long mp_get_int(mp_int *a) { + int i; + mp_min_u32 res; + + if (a->used == 0) { + return 0; + } + + /* get number of digits of the lsb we have to read */ + i = MIN(a->used, (int)(((sizeof(unsigned long) * CHAR_BIT) + DIGIT_BIT - 1) / DIGIT_BIT)) - 1; + + /* get most significant digit of result */ + res = DIGIT(a, i); + + while (--i >= 0) { + res = (res << DIGIT_BIT) | DIGIT(a, i); + } + + /* force result to 32-bits always so it is consistent on non 32-bit platforms */ + return res & 0xFFFFFFFFUL; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_GET_LONG_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* get the lower unsigned long of an mp_int, platform dependent */ +unsigned long mp_get_long(mp_int *a) { + int i; + unsigned long res; + + if (a->used == 0) { + return 0; + } + + /* get number of digits of the lsb we have to read */ + i = MIN(a->used, (int)(((sizeof(unsigned long) * CHAR_BIT) + DIGIT_BIT - 1) / DIGIT_BIT)) - 1; + + /* get most significant digit of result */ + res = DIGIT(a, i); + + #if (ULONG_MAX != 0xffffffffuL) || (DIGIT_BIT < 32) + while (--i >= 0) { + res = (res << DIGIT_BIT) | DIGIT(a, i); + } + #endif + return res; +} +#endif + + + +#ifdef BN_MP_GET_LONG_LONG_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* get the lower unsigned long long of an mp_int, platform dependent */ +unsigned long long mp_get_long_long(mp_int *a) { + int i; + unsigned long long res; + + if (a->used == 0) { + return 0; + } + + /* get number of digits of the lsb we have to read */ + i = MIN(a->used, (int)(((sizeof(unsigned long long) * CHAR_BIT) + DIGIT_BIT - 1) / DIGIT_BIT)) - 1; + + /* get most significant digit of result */ + res = DIGIT(a, i); + + #if DIGIT_BIT < 64 + while (--i >= 0) { + res = (res << DIGIT_BIT) | DIGIT(a, i); + } + #endif + return res; +} +#endif + + + +#ifdef BN_MP_GROW_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* grow as required */ +int mp_grow(mp_int *a, int size) { + int i; + mp_digit *tmp; + + /* if the alloc size is smaller alloc more ram */ + if (a->alloc < size) { + /* ensure there are always at least MP_PREC digits extra on top */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* reallocate the array a->dp + * + * We store the return in a temporary variable + * in case the operation failed we don't want + * to overwrite the dp member of a. + */ + tmp = OPT_CAST(mp_digit) XREALLOC(a->dp, sizeof(mp_digit) * size); + if (tmp == NULL) { + /* reallocation failed but "a" is still valid [can be freed] */ + return MP_MEM; + } + + /* reallocation succeeded so set a->dp */ + a->dp = tmp; + + /* zero excess digits */ + i = a->alloc; + a->alloc = size; + for ( ; i < a->alloc; i++) { + a->dp[i] = 0; + } + } + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_IMPORT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* based on gmp's mpz_import. + * see http://gmplib.org/manual/Integer-Import-and-Export.html + */ +int mp_import(mp_int *rop, size_t count, int order, size_t size, + int endian, size_t nails, const void *op) { + int result; + size_t odd_nails, nail_bytes, i, j; + unsigned char odd_nail_mask; + + mp_zero(rop); + + if (endian == 0) { + union { + unsigned int i; + char c[4]; + } lint; + lint.i = 0x01020304; + + endian = (lint.c[0] == 4) ? -1 : 1; + } + + odd_nails = (nails % 8); + odd_nail_mask = 0xff; + for (i = 0; i < odd_nails; ++i) { + odd_nail_mask ^= (1 << (7 - i)); + } + nail_bytes = nails / 8; + + for (i = 0; i < count; ++i) { + for (j = 0; j < (size - nail_bytes); ++j) { + unsigned char byte = *( + (unsigned char *)op + + (((order == 1) ? i : ((count - 1) - i)) * size) + + ((endian == 1) ? (j + nail_bytes) : (((size - 1) - j) - nail_bytes)) + ); + + if ( + (result = mp_mul_2d(rop, ((j == 0) ? (8 - odd_nails) : 8), rop)) != MP_OKAY) { + return result; + } + + rop->dp[0] |= (j == 0) ? (byte & odd_nail_mask) : byte; + rop->used += 1; + } + } + + mp_clamp(rop); + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_INIT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* init a new mp_int */ +int mp_init(mp_int *a) { + int i; + + /* allocate memory required and clear it */ + a->dp = OPT_CAST(mp_digit) XMALLOC(sizeof(mp_digit) * MP_PREC); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the digits to zero */ + for (i = 0; i < MP_PREC; i++) { + a->dp[i] = 0; + } + + /* set the used to zero, allocated digits to the default precision + * and sign to positive */ + a->used = 0; + a->alloc = MP_PREC; + a->sign = MP_ZPOS; + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_INIT_COPY_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* creates "a" then copies b into it */ +int mp_init_copy(mp_int *a, mp_int *b) { + int res; + + if ((res = mp_init_size(a, b->used)) != MP_OKAY) { + return res; + } + return mp_copy(b, a); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_INIT_MULTI_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ +#include + +int mp_init_multi(mp_int *mp, ...) { + mp_err res = MP_OKAY; /* Assume ok until proven otherwise */ + int n = 0; /* Number of ok inits */ + mp_int *cur_arg = mp; + va_list args; + + va_start(args, mp); /* init args to next argument from caller */ + while (cur_arg != NULL) { + if (mp_init(cur_arg) != MP_OKAY) { + /* Oops - error! Back-track and mp_clear what we already + succeeded in init-ing, then return error. + */ + va_list clean_args; + + /* end the current list */ + va_end(args); + + /* now start cleaning up */ + cur_arg = mp; + va_start(clean_args, mp); + while (n-- != 0) { + mp_clear(cur_arg); + cur_arg = va_arg(clean_args, mp_int *); + } + va_end(clean_args); + res = MP_MEM; + break; + } + n++; + cur_arg = va_arg(args, mp_int *); + } + va_end(args); + return res; /* Assumed ok, if error flagged above. */ +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_INIT_SET_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* initialize and set a digit */ +int mp_init_set(mp_int *a, mp_digit b) { + int err; + + if ((err = mp_init(a)) != MP_OKAY) { + return err; + } + mp_set(a, b); + return err; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_INIT_SET_INT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* initialize and set a digit */ +int mp_init_set_int(mp_int *a, unsigned long b) { + int err; + + if ((err = mp_init(a)) != MP_OKAY) { + return err; + } + return mp_set_int(a, b); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_INIT_SIZE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* init an mp_init for a given size */ +int mp_init_size(mp_int *a, int size) { + int x; + + /* pad size so there are always extra digits */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* alloc mem */ + a->dp = OPT_CAST(mp_digit) XMALLOC(sizeof(mp_digit) * size); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the members */ + a->used = 0; + a->alloc = size; + a->sign = MP_ZPOS; + + /* zero the digits */ + for (x = 0; x < size; x++) { + a->dp[x] = 0; + } + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_INVMOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* hac 14.61, pp608 */ +int mp_invmod(mp_int *a, mp_int *b, mp_int *c) { + /* b cannot be negative */ + if ((b->sign == MP_NEG) || (mp_iszero(b) == MP_YES)) { + return MP_VAL; + } + + #ifdef BN_FAST_MP_INVMOD_C + /* if the modulus is odd we can use a faster routine instead */ + if (mp_isodd(b) == MP_YES) { + return fast_mp_invmod(a, b, c); + } + #endif + + #ifdef BN_MP_INVMOD_SLOW_C + return mp_invmod_slow(a, b, c); + #else + return MP_VAL; + #endif +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_INVMOD_SLOW_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* hac 14.61, pp608 */ +int mp_invmod_slow(mp_int *a, mp_int *b, mp_int *c) { + mp_int x, y, u, v, A, B, C, D; + int res; + + /* b cannot be negative */ + if ((b->sign == MP_NEG) || (mp_iszero(b) == MP_YES)) { + return MP_VAL; + } + + /* init temps */ + if ((res = mp_init_multi(&x, &y, &u, &v, + &A, &B, &C, &D, NULL)) != MP_OKAY) { + return res; + } + + /* x = a, y = b */ + if ((res = mp_mod(a, b, &x)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy(b, &y)) != MP_OKAY) { + goto LBL_ERR; + } + + /* 2. [modified] if x,y are both even then return an error! */ + if ((mp_iseven(&x) == MP_YES) && (mp_iseven(&y) == MP_YES)) { + res = MP_VAL; + goto LBL_ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy(&x, &u)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy(&y, &v)) != MP_OKAY) { + goto LBL_ERR; + } + mp_set(&A, 1); + mp_set(&D, 1); + +top: + /* 4. while u is even do */ + while (mp_iseven(&u) == MP_YES) { + /* 4.1 u = u/2 */ + if ((res = mp_div_2(&u, &u)) != MP_OKAY) { + goto LBL_ERR; + } + /* 4.2 if A or B is odd then */ + if ((mp_isodd(&A) == MP_YES) || (mp_isodd(&B) == MP_YES)) { + /* A = (A+y)/2, B = (B-x)/2 */ + if ((res = mp_add(&A, &y, &A)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_sub(&B, &x, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* A = A/2, B = B/2 */ + if ((res = mp_div_2(&A, &A)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_div_2(&B, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 5. while v is even do */ + while (mp_iseven(&v) == MP_YES) { + /* 5.1 v = v/2 */ + if ((res = mp_div_2(&v, &v)) != MP_OKAY) { + goto LBL_ERR; + } + /* 5.2 if C or D is odd then */ + if ((mp_isodd(&C) == MP_YES) || (mp_isodd(&D) == MP_YES)) { + /* C = (C+y)/2, D = (D-x)/2 */ + if ((res = mp_add(&C, &y, &C)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_sub(&D, &x, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* C = C/2, D = D/2 */ + if ((res = mp_div_2(&C, &C)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_div_2(&D, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 6. if u >= v then */ + if (mp_cmp(&u, &v) != MP_LT) { + /* u = u - v, A = A - C, B = B - D */ + if ((res = mp_sub(&u, &v, &u)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub(&A, &C, &A)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub(&B, &D, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } else { + /* v - v - u, C = C - A, D = D - B */ + if ((res = mp_sub(&v, &u, &v)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub(&C, &A, &C)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub(&D, &B, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* if not zero goto step 4 */ + if (mp_iszero(&u) == MP_NO) + goto top; + + /* now a = C, b = D, gcd == g*v */ + + /* if v != 1 then there is no inverse */ + if (mp_cmp_d(&v, 1) != MP_EQ) { + res = MP_VAL; + goto LBL_ERR; + } + + /* if its too low */ + while (mp_cmp_d(&C, 0) == MP_LT) { + if ((res = mp_add(&C, b, &C)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* too big */ + while (mp_cmp_mag(&C, b) != MP_LT) { + if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* C is now the inverse */ + mp_exch(&C, c); + res = MP_OKAY; +LBL_ERR: mp_clear_multi(&x, &y, &u, &v, &A, &B, &C, &D, NULL); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_IS_SQUARE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* Check if remainders are possible squares - fast exclude non-squares */ +static const char rem_128[128] = { + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1 +}; + +static const char rem_105[105] = { + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 +}; + +/* Store non-zero to ret if arg is square, and zero if not */ +int mp_is_square(mp_int *arg, int *ret) { + int res; + mp_digit c; + mp_int t; + unsigned long r; + + /* Default to Non-square :) */ + *ret = MP_NO; + + if (arg->sign == MP_NEG) { + return MP_VAL; + } + + /* digits used? (TSD) */ + if (arg->used == 0) { + return MP_OKAY; + } + + /* First check mod 128 (suppose that DIGIT_BIT is at least 7) */ + if (rem_128[127 & DIGIT(arg, 0)] == 1) { + return MP_OKAY; + } + + /* Next check mod 105 (3*5*7) */ + if ((res = mp_mod_d(arg, 105, &c)) != MP_OKAY) { + return res; + } + if (rem_105[c] == 1) { + return MP_OKAY; + } + + + if ((res = mp_init_set_int(&t, 11L * 13L * 17L * 19L * 23L * 29L * 31L)) != MP_OKAY) { + return res; + } + if ((res = mp_mod(arg, &t, &t)) != MP_OKAY) { + goto ERR; + } + r = mp_get_int(&t); + + /* Check for other prime modules, note it's not an ERROR but we must + * free "t" so the easiest way is to goto ERR. We know that res + * is already equal to MP_OKAY from the mp_mod call + */ + if (((1L << (r % 11)) & 0x5C4L) != 0L) goto ERR; + if (((1L << (r % 13)) & 0x9E4L) != 0L) goto ERR; + if (((1L << (r % 17)) & 0x5CE8L) != 0L) goto ERR; + if (((1L << (r % 19)) & 0x4F50CL) != 0L) goto ERR; + if (((1L << (r % 23)) & 0x7ACCA0L) != 0L) goto ERR; + if (((1L << (r % 29)) & 0xC2EDD0CL) != 0L) goto ERR; + if (((1L << (r % 31)) & 0x6DE2B848L) != 0L) goto ERR; + + /* Final check - is sqr(sqrt(arg)) == arg ? */ + if ((res = mp_sqrt(arg, &t)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sqr(&t, &t)) != MP_OKAY) { + goto ERR; + } + + *ret = (mp_cmp_mag(&t, arg) == MP_EQ) ? MP_YES : MP_NO; +ERR: mp_clear(&t); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_JACOBI_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* computes the jacobi c = (a | n) (or Legendre if n is prime) + * HAC pp. 73 Algorithm 2.149 + * HAC is wrong here, as the special case of (0 | 1) is not + * handled correctly. + */ +int mp_jacobi(mp_int *a, mp_int *n, int *c) { + mp_int a1, p1; + int k, s, r, res; + mp_digit residue; + + /* if n <= 0 return MP_VAL */ + if (mp_cmp_d(n, 0) != MP_GT) { + return MP_VAL; + } + + /* step 1. handle case of a == 0 */ + if (mp_iszero(a) == MP_YES) { + /* special case of a == 0 and n == 1 */ + if (mp_cmp_d(n, 1) == MP_EQ) { + *c = 1; + } else { + *c = 0; + } + return MP_OKAY; + } + + /* step 2. if a == 1, return 1 */ + if (mp_cmp_d(a, 1) == MP_EQ) { + *c = 1; + return MP_OKAY; + } + + /* default */ + s = 0; + + /* step 3. write a = a1 * 2**k */ + if ((res = mp_init_copy(&a1, a)) != MP_OKAY) { + return res; + } + + if ((res = mp_init(&p1)) != MP_OKAY) { + goto LBL_A1; + } + + /* divide out larger power of two */ + k = mp_cnt_lsb(&a1); + if ((res = mp_div_2d(&a1, k, &a1, NULL)) != MP_OKAY) { + goto LBL_P1; + } + + /* step 4. if e is even set s=1 */ + if ((k & 1) == 0) { + s = 1; + } else { + /* else set s=1 if p = 1/7 (mod 8) or s=-1 if p = 3/5 (mod 8) */ + residue = n->dp[0] & 7; + + if ((residue == 1) || (residue == 7)) { + s = 1; + } else if ((residue == 3) || (residue == 5)) { + s = -1; + } + } + + /* step 5. if p == 3 (mod 4) *and* a1 == 3 (mod 4) then s = -s */ + if (((n->dp[0] & 3) == 3) && ((a1.dp[0] & 3) == 3)) { + s = -s; + } + + /* if a1 == 1 we're done */ + if (mp_cmp_d(&a1, 1) == MP_EQ) { + *c = s; + } else { + /* n1 = n mod a1 */ + if ((res = mp_mod(n, &a1, &p1)) != MP_OKAY) { + goto LBL_P1; + } + if ((res = mp_jacobi(&p1, &a1, &r)) != MP_OKAY) { + goto LBL_P1; + } + *c = s * r; + } + + /* done */ + res = MP_OKAY; +LBL_P1: mp_clear(&p1); +LBL_A1: mp_clear(&a1); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_KARATSUBA_MUL_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* c = |a| * |b| using Karatsuba Multiplication using + * three half size multiplications + * + * Let B represent the radix [e.g. 2**DIGIT_BIT] and + * let n represent half of the number of digits in + * the min(a,b) + * + * a = a1 * B**n + a0 + * b = b1 * B**n + b0 + * + * Then, a * b => + a1b1 * B**2n + ((a1 + a0)(b1 + b0) - (a0b0 + a1b1)) * B + a0b0 + * + * Note that a1b1 and a0b0 are used twice and only need to be + * computed once. So in total three half size (half # of + * digit) multiplications are performed, a0b0, a1b1 and + * (a1+b1)(a0+b0) + * + * Note that a multiplication of half the digits requires + * 1/4th the number of single precision multiplications so in + * total after one call 25% of the single precision multiplications + * are saved. Note also that the call to mp_mul can end up back + * in this function if the a0, a1, b0, or b1 are above the threshold. + * This is known as divide-and-conquer and leads to the famous + * O(N**lg(3)) or O(N**1.584) work which is asymptopically lower than + * the standard O(N**2) that the baseline/comba methods use. + * Generally though the overhead of this method doesn't pay off + * until a certain size (N ~ 80) is reached. + */ +int mp_karatsuba_mul(mp_int *a, mp_int *b, mp_int *c) { + mp_int x0, x1, y0, y1, t1, x0y0, x1y1; + int B, err; + + /* default the return code to an error */ + err = MP_MEM; + + /* min # of digits */ + B = MIN(a->used, b->used); + + /* now divide in two */ + B = B >> 1; + + /* init copy all the temps */ + if (mp_init_size(&x0, B) != MP_OKAY) + goto ERR; + if (mp_init_size(&x1, a->used - B) != MP_OKAY) + goto X0; + if (mp_init_size(&y0, B) != MP_OKAY) + goto X1; + if (mp_init_size(&y1, b->used - B) != MP_OKAY) + goto Y0; + + /* init temps */ + if (mp_init_size(&t1, B * 2) != MP_OKAY) + goto Y1; + if (mp_init_size(&x0y0, B * 2) != MP_OKAY) + goto T1; + if (mp_init_size(&x1y1, B * 2) != MP_OKAY) + goto X0Y0; + + /* now shift the digits */ + x0.used = y0.used = B; + x1.used = a->used - B; + y1.used = b->used - B; + + { + int x; + mp_digit *tmpa, *tmpb, *tmpx, *tmpy; + + /* we copy the digits directly instead of using higher level functions + * since we also need to shift the digits + */ + tmpa = a->dp; + tmpb = b->dp; + + tmpx = x0.dp; + tmpy = y0.dp; + for (x = 0; x < B; x++) { + *tmpx++ = *tmpa++; + *tmpy++ = *tmpb++; + } + + tmpx = x1.dp; + for (x = B; x < a->used; x++) { + *tmpx++ = *tmpa++; + } + + tmpy = y1.dp; + for (x = B; x < b->used; x++) { + *tmpy++ = *tmpb++; + } + } + + /* only need to clamp the lower words since by definition the + * upper words x1/y1 must have a known number of digits + */ + mp_clamp(&x0); + mp_clamp(&y0); + + /* now calc the products x0y0 and x1y1 */ + /* after this x0 is no longer required, free temp [x0==t2]! */ + if (mp_mul(&x0, &y0, &x0y0) != MP_OKAY) + goto X1Y1; /* x0y0 = x0*y0 */ + if (mp_mul(&x1, &y1, &x1y1) != MP_OKAY) + goto X1Y1; /* x1y1 = x1*y1 */ + + /* now calc x1+x0 and y1+y0 */ + if (s_mp_add(&x1, &x0, &t1) != MP_OKAY) + goto X1Y1; /* t1 = x1 - x0 */ + if (s_mp_add(&y1, &y0, &x0) != MP_OKAY) + goto X1Y1; /* t2 = y1 - y0 */ + if (mp_mul(&t1, &x0, &t1) != MP_OKAY) + goto X1Y1; /* t1 = (x1 + x0) * (y1 + y0) */ + + /* add x0y0 */ + if (mp_add(&x0y0, &x1y1, &x0) != MP_OKAY) + goto X1Y1; /* t2 = x0y0 + x1y1 */ + if (s_mp_sub(&t1, &x0, &t1) != MP_OKAY) + goto X1Y1; /* t1 = (x1+x0)*(y1+y0) - (x1y1 + x0y0) */ + + /* shift by B */ + if (mp_lshd(&t1, B) != MP_OKAY) + goto X1Y1; /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<used; + + /* now divide in two */ + B = B >> 1; + + /* init copy all the temps */ + if (mp_init_size(&x0, B) != MP_OKAY) + goto ERR; + if (mp_init_size(&x1, a->used - B) != MP_OKAY) + goto X0; + + /* init temps */ + if (mp_init_size(&t1, a->used * 2) != MP_OKAY) + goto X1; + if (mp_init_size(&t2, a->used * 2) != MP_OKAY) + goto T1; + if (mp_init_size(&x0x0, B * 2) != MP_OKAY) + goto T2; + if (mp_init_size(&x1x1, (a->used - B) * 2) != MP_OKAY) + goto X0X0; + + { + int x; + mp_digit *dst, *src; + + src = a->dp; + + /* now shift the digits */ + dst = x0.dp; + for (x = 0; x < B; x++) { + *dst++ = *src++; + } + + dst = x1.dp; + for (x = B; x < a->used; x++) { + *dst++ = *src++; + } + } + + x0.used = B; + x1.used = a->used - B; + + mp_clamp(&x0); + + /* now calc the products x0*x0 and x1*x1 */ + if (mp_sqr(&x0, &x0x0) != MP_OKAY) + goto X1X1; /* x0x0 = x0*x0 */ + if (mp_sqr(&x1, &x1x1) != MP_OKAY) + goto X1X1; /* x1x1 = x1*x1 */ + + /* now calc (x1+x0)**2 */ + if (s_mp_add(&x1, &x0, &t1) != MP_OKAY) + goto X1X1; /* t1 = x1 - x0 */ + if (mp_sqr(&t1, &t1) != MP_OKAY) + goto X1X1; /* t1 = (x1 - x0) * (x1 - x0) */ + + /* add x0y0 */ + if (s_mp_add(&x0x0, &x1x1, &t2) != MP_OKAY) + goto X1X1; /* t2 = x0x0 + x1x1 */ + if (s_mp_sub(&t1, &t2, &t1) != MP_OKAY) + goto X1X1; /* t1 = (x1+x0)**2 - (x0x0 + x1x1) */ + + /* shift by B */ + if (mp_lshd(&t1, B) != MP_OKAY) + goto X1X1; /* t1 = (x0x0 + x1x1 - (x1-x0)*(x1-x0))<sign = MP_ZPOS; + +LBL_T: + mp_clear_multi(&t1, &t2, NULL); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_LSHD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* shift left a certain amount of digits */ +int mp_lshd(mp_int *a, int b) { + int x, res; + + /* if its less than zero return */ + if (b <= 0) { + return MP_OKAY; + } + + /* grow to fit the new digits */ + if (a->alloc < (a->used + b)) { + if ((res = mp_grow(a, a->used + b)) != MP_OKAY) { + return res; + } + } + + { + mp_digit *top, *bottom; + + /* increment the used by the shift amount then copy upwards */ + a->used += b; + + /* top */ + top = a->dp + a->used - 1; + + /* base */ + bottom = (a->dp + a->used - 1) - b; + + /* much like mp_rshd this is implemented using a sliding window + * except the window goes the otherway around. Copying from + * the bottom to the top. see bn_mp_rshd.c for more info. + */ + for (x = a->used - 1; x >= b; x--) { + *top-- = *bottom--; + } + + /* zero the lower digits */ + top = a->dp; + for (x = 0; x < b; x++) { + *top++ = 0; + } + } + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* c = a mod b, 0 <= c < b if b > 0, b < c <= 0 if b < 0 */ +int +mp_mod(mp_int *a, mp_int *b, mp_int *c) { + mp_int t; + int res; + + if ((res = mp_init(&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_div(a, b, NULL, &t)) != MP_OKAY) { + mp_clear(&t); + return res; + } + + if ((mp_iszero(&t) != MP_NO) || (t.sign == b->sign)) { + res = MP_OKAY; + mp_exch(&t, c); + } else { + res = mp_add(b, &t, c); + } + + mp_clear(&t); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MOD_2D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* calc a value mod 2**b */ +int +mp_mod_2d(mp_int *a, int b, mp_int *c) { + int x, res; + + /* if b is <= 0 then zero the int */ + if (b <= 0) { + mp_zero(c); + return MP_OKAY; + } + + /* if the modulus is larger than the value than return */ + if (b >= (int)(a->used * DIGIT_BIT)) { + res = mp_copy(a, c); + return res; + } + + /* copy */ + if ((res = mp_copy(a, c)) != MP_OKAY) { + return res; + } + + /* zero digits above the last digit of the modulus */ + for (x = (b / DIGIT_BIT) + (((b % DIGIT_BIT) == 0) ? 0 : 1); x < c->used; x++) { + c->dp[x] = 0; + } + /* clear the digit that is not completely outside/inside the modulus */ + c->dp[b / DIGIT_BIT] &= + (mp_digit)((((mp_digit)1) << (((mp_digit)b) % DIGIT_BIT)) - ((mp_digit)1)); + mp_clamp(c); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MOD_D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +int +mp_mod_d(mp_int *a, mp_digit b, mp_digit *c) { + return mp_div_d(a, b, NULL, c); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* + * shifts with subtractions when the result is greater than b. + * + * The method is slightly modified to shift B unconditionally upto just under + * the leading bit of b. This saves alot of multiple precision shifting. + */ +int mp_montgomery_calc_normalization(mp_int *a, mp_int *b) { + int x, bits, res; + + /* how many bits of last digit does b use */ + bits = mp_count_bits(b) % DIGIT_BIT; + + if (b->used > 1) { + if ((res = mp_2expt(a, ((b->used - 1) * DIGIT_BIT) + bits - 1)) != MP_OKAY) { + return res; + } + } else { + mp_set(a, 1); + bits = 1; + } + + + /* now compute C = A * B mod b */ + for (x = bits - 1; x < (int)DIGIT_BIT; x++) { + if ((res = mp_mul_2(a, a)) != MP_OKAY) { + return res; + } + if (mp_cmp_mag(a, b) != MP_LT) { + if ((res = s_mp_sub(a, b, a)) != MP_OKAY) { + return res; + } + } + } + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MONTGOMERY_REDUCE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* computes xR**-1 == x (mod N) via Montgomery Reduction */ +int +mp_montgomery_reduce(mp_int *x, mp_int *n, mp_digit rho) { + int ix, res, digs; + mp_digit mu; + + /* can the fast reduction [comba] method be used? + * + * Note that unlike in mul you're safely allowed *less* + * than the available columns [255 per default] since carries + * are fixed up in the inner loop. + */ + digs = (n->used * 2) + 1; + if ((digs < MP_WARRAY) && + (n->used < + (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { + return fast_mp_montgomery_reduce(x, n, rho); + } + + /* grow the input as required */ + if (x->alloc < digs) { + if ((res = mp_grow(x, digs)) != MP_OKAY) { + return res; + } + } + x->used = digs; + + for (ix = 0; ix < n->used; ix++) { + /* mu = ai * rho mod b + * + * The value of rho must be precalculated via + * montgomery_setup() such that + * it equals -1/n0 mod b this allows the + * following inner loop to reduce the + * input one digit at a time + */ + mu = (mp_digit)(((mp_word)x->dp[ix] * (mp_word)rho) & MP_MASK); + + /* a = a + mu * m * b**i */ + { + int iy; + mp_digit *tmpn, *tmpx, u; + mp_word r; + + /* alias for digits of the modulus */ + tmpn = n->dp; + + /* alias for the digits of x [the input] */ + tmpx = x->dp + ix; + + /* set the carry to zero */ + u = 0; + + /* Multiply and add in place */ + for (iy = 0; iy < n->used; iy++) { + /* compute product and sum */ + r = ((mp_word)mu * (mp_word) * tmpn++) + + (mp_word)u + (mp_word) * tmpx; + + /* get carry */ + u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + + /* fix digit */ + *tmpx++ = (mp_digit)(r & ((mp_word)MP_MASK)); + } + /* At this point the ix'th digit of x should be zero */ + + + /* propagate carries upwards as required*/ + while (u != 0) { + *tmpx += u; + u = *tmpx >> DIGIT_BIT; + *tmpx++ &= MP_MASK; + } + } + } + + /* at this point the n.used'th least + * significant digits of x are all zero + * which means we can shift x to the + * right by n.used digits and the + * residue is unchanged. + */ + + /* x = x/b**n.used */ + mp_clamp(x); + mp_rshd(x, n->used); + + /* if x >= n then x = x - n */ + if (mp_cmp_mag(x, n) != MP_LT) { + return s_mp_sub(x, n, x); + } + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MONTGOMERY_SETUP_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* setups the montgomery reduction stuff */ +int +mp_montgomery_setup(mp_int *n, mp_digit *rho) { + mp_digit x, b; + +/* fast inversion mod 2**k + * + * Based on the fact that + * + * XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n) + * => 2*X*A - X*X*A*A = 1 + * => 2*(1) - (1) = 1 + */ + b = n->dp[0]; + + if ((b & 1) == 0) { + return MP_VAL; + } + + x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */ + x *= 2 - (b * x); /* here x*a==1 mod 2**8 */ + #if !defined(MP_8BIT) + x *= 2 - (b * x); /* here x*a==1 mod 2**16 */ + #endif + #if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT)) + x *= 2 - (b * x); /* here x*a==1 mod 2**32 */ + #endif + #ifdef MP_64BIT + x *= 2 - (b * x); /* here x*a==1 mod 2**64 */ + #endif + + /* rho = -1/m mod b */ + *rho = (mp_digit)(((mp_word)1 << ((mp_word)DIGIT_BIT)) - x) & MP_MASK; + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MUL_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* high level multiplication (handles sign) */ +int mp_mul(mp_int *a, mp_int *b, mp_int *c) { + int res, neg; + + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + + /* use Toom-Cook? */ + #ifdef BN_MP_TOOM_MUL_C + if (MIN(a->used, b->used) >= TOOM_MUL_CUTOFF) { + res = mp_toom_mul(a, b, c); + } else + #endif + #ifdef BN_MP_KARATSUBA_MUL_C + /* use Karatsuba? */ + if (MIN(a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { + res = mp_karatsuba_mul(a, b, c); + } else + #endif + { + /* can we use the fast multiplier? + * + * The fast multiplier can be used if the output will + * have less than MP_WARRAY digits and the number of + * digits won't affect carry propagation + */ + int digs = a->used + b->used + 1; + + #ifdef BN_FAST_S_MP_MUL_DIGS_C + if ((digs < MP_WARRAY) && + (MIN(a->used, b->used) <= + (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { + res = fast_s_mp_mul_digs(a, b, c, digs); + } else + #endif + { + #ifdef BN_S_MP_MUL_DIGS_C + res = s_mp_mul(a, b, c); /* uses s_mp_mul_digs */ + #else + res = MP_VAL; + #endif + } + } + c->sign = (c->used > 0) ? neg : MP_ZPOS; + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MUL_2_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* b = a*2 */ +int mp_mul_2(mp_int *a, mp_int *b) { + int x, res, oldused; + + /* grow to accomodate result */ + if (b->alloc < (a->used + 1)) { + if ((res = mp_grow(b, a->used + 1)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + + { + mp_digit r, rr, *tmpa, *tmpb; + + /* alias for source */ + tmpa = a->dp; + + /* alias for dest */ + tmpb = b->dp; + + /* carry */ + r = 0; + for (x = 0; x < a->used; x++) { + /* get what will be the *next* carry bit from the + * MSB of the current digit + */ + rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); + + /* now shift up this digit, add in the carry [from the previous] */ + *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; + + /* copy the carry that would be from the source + * digit into the next iteration + */ + r = rr; + } + + /* new leading digit? */ + if (r != 0) { + /* add a MSB which is always 1 at this point */ + *tmpb = 1; + ++(b->used); + } + + /* now zero any excess digits on the destination + * that we didn't write to + */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MUL_2D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* shift left by a certain bit count */ +int mp_mul_2d(mp_int *a, int b, mp_int *c) { + mp_digit d; + int res; + + /* copy */ + if (a != c) { + if ((res = mp_copy(a, c)) != MP_OKAY) { + return res; + } + } + + if (c->alloc < (int)(c->used + (b / DIGIT_BIT) + 1)) { + if ((res = mp_grow(c, c->used + (b / DIGIT_BIT) + 1)) != MP_OKAY) { + return res; + } + } + + /* shift by as many digits in the bit count */ + if (b >= (int)DIGIT_BIT) { + if ((res = mp_lshd(c, b / DIGIT_BIT)) != MP_OKAY) { + return res; + } + } + + /* shift any bit count < DIGIT_BIT */ + d = (mp_digit)(b % DIGIT_BIT); + if (d != 0) { + mp_digit *tmpc, shift, mask, r, rr; + int x; + + /* bitmask for carries */ + mask = (((mp_digit)1) << d) - 1; + + /* shift for msbs */ + shift = DIGIT_BIT - d; + + /* alias */ + tmpc = c->dp; + + /* carry */ + r = 0; + for (x = 0; x < c->used; x++) { + /* get the higher bits of the current word */ + rr = (*tmpc >> shift) & mask; + + /* shift the current word and OR in the carry */ + *tmpc = ((*tmpc << d) | r) & MP_MASK; + ++tmpc; + + /* set the carry to the carry bits of the current word */ + r = rr; + } + + /* set final carry */ + if (r != 0) { + c->dp[(c->used)++] = r; + } + } + mp_clamp(c); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MUL_D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* multiply by a digit */ +int +mp_mul_d(mp_int *a, mp_digit b, mp_int *c) { + mp_digit u, *tmpa, *tmpc; + mp_word r; + int ix, res, olduse; + + /* make sure c is big enough to hold a*b */ + if (c->alloc < (a->used + 1)) { + if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* get the original destinations used count */ + olduse = c->used; + + /* set the sign */ + c->sign = a->sign; + + /* alias for a->dp [source] */ + tmpa = a->dp; + + /* alias for c->dp [dest] */ + tmpc = c->dp; + + /* zero carry */ + u = 0; + + /* compute columns */ + for (ix = 0; ix < a->used; ix++) { + /* compute product and carry sum for this term */ + r = (mp_word)u + ((mp_word) * tmpa++ *(mp_word)b); + + /* mask off higher bits to get a single digit */ + *tmpc++ = (mp_digit)(r & ((mp_word)MP_MASK)); + + /* send carry into next iteration */ + u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + } + + /* store final carry [if any] and increment ix offset */ + *tmpc++ = u; + ++ix; + + /* now zero digits above the top */ + while (ix++ < olduse) { + *tmpc++ = 0; + } + + /* set used count */ + c->used = a->used + 1; + mp_clamp(c); + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MULMOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* d = a * b (mod c) */ +int mp_mulmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d) { + int res; + mp_int t; + + if ((res = mp_init(&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_mul(a, b, &t)) != MP_OKAY) { + mp_clear(&t); + return res; + } + res = mp_mod(&t, c, d); + mp_clear(&t); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_N_ROOT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* wrapper function for mp_n_root_ex() + * computes c = (a)**(1/b) such that (c)**b <= a and (c+1)**b > a + */ +int mp_n_root(mp_int *a, mp_digit b, mp_int *c) { + return mp_n_root_ex(a, b, c, 0); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_N_ROOT_EX_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* find the n'th root of an integer + * + * Result found such that (c)**b <= a and (c+1)**b > a + * + * This algorithm uses Newton's approximation + * x[i+1] = x[i] - f(x[i])/f'(x[i]) + * which will find the root in log(N) time where + * each step involves a fair bit. This is not meant to + * find huge roots [square and cube, etc]. + */ +int mp_n_root_ex(mp_int *a, mp_digit b, mp_int *c, int fast) { + mp_int t1, t2, t3; + int res, neg; + + /* input must be positive if b is even */ + if (((b & 1) == 0) && (a->sign == MP_NEG)) { + return MP_VAL; + } + + if ((res = mp_init(&t1)) != MP_OKAY) { + return res; + } + + if ((res = mp_init(&t2)) != MP_OKAY) { + goto LBL_T1; + } + + if ((res = mp_init(&t3)) != MP_OKAY) { + goto LBL_T2; + } + + /* if a is negative fudge the sign but keep track */ + neg = a->sign; + a->sign = MP_ZPOS; + + /* t2 = 2 */ + mp_set(&t2, 2); + + do { + /* t1 = t2 */ + if ((res = mp_copy(&t2, &t1)) != MP_OKAY) { + goto LBL_T3; + } + + /* t2 = t1 - ((t1**b - a) / (b * t1**(b-1))) */ + + /* t3 = t1**(b-1) */ + if ((res = mp_expt_d_ex(&t1, b - 1, &t3, fast)) != MP_OKAY) { + goto LBL_T3; + } + + /* numerator */ + /* t2 = t1**b */ + if ((res = mp_mul(&t3, &t1, &t2)) != MP_OKAY) { + goto LBL_T3; + } + + /* t2 = t1**b - a */ + if ((res = mp_sub(&t2, a, &t2)) != MP_OKAY) { + goto LBL_T3; + } + + /* denominator */ + /* t3 = t1**(b-1) * b */ + if ((res = mp_mul_d(&t3, b, &t3)) != MP_OKAY) { + goto LBL_T3; + } + + /* t3 = (t1**b - a)/(b * t1**(b-1)) */ + if ((res = mp_div(&t2, &t3, &t3, NULL)) != MP_OKAY) { + goto LBL_T3; + } + + if ((res = mp_sub(&t1, &t3, &t2)) != MP_OKAY) { + goto LBL_T3; + } + } while (mp_cmp(&t1, &t2) != MP_EQ); + + /* result can be off by a few so check */ + for ( ; ; ) { + if ((res = mp_expt_d_ex(&t1, b, &t2, fast)) != MP_OKAY) { + goto LBL_T3; + } + + if (mp_cmp(&t2, a) == MP_GT) { + if ((res = mp_sub_d(&t1, 1, &t1)) != MP_OKAY) { + goto LBL_T3; + } + } else { + break; + } + } + + /* reset the sign of a first */ + a->sign = neg; + + /* set the result */ + mp_exch(&t1, c); + + /* set the sign of the result */ + c->sign = neg; + + res = MP_OKAY; + +LBL_T3: mp_clear(&t3); +LBL_T2: mp_clear(&t2); +LBL_T1: mp_clear(&t1); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_NEG_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* b = -a */ +int mp_neg(mp_int *a, mp_int *b) { + int res; + + if (a != b) { + if ((res = mp_copy(a, b)) != MP_OKAY) { + return res; + } + } + + if (mp_iszero(b) != MP_YES) { + b->sign = (a->sign == MP_ZPOS) ? MP_NEG : MP_ZPOS; + } else { + b->sign = MP_ZPOS; + } + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_OR_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* OR two ints together */ +int mp_or(mp_int *a, mp_int *b, mp_int *c) { + int res, ix, px; + mp_int t, *x; + + if (a->used > b->used) { + if ((res = mp_init_copy(&t, a)) != MP_OKAY) { + return res; + } + px = b->used; + x = b; + } else { + if ((res = mp_init_copy(&t, b)) != MP_OKAY) { + return res; + } + px = a->used; + x = a; + } + + for (ix = 0; ix < px; ix++) { + t.dp[ix] |= x->dp[ix]; + } + mp_clamp(&t); + mp_exch(c, &t); + mp_clear(&t); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_PRIME_FERMAT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* performs one Fermat test. + * + * If "a" were prime then b**a == b (mod a) since the order of + * the multiplicative sub-group would be phi(a) = a-1. That means + * it would be the same as b**(a mod (a-1)) == b**1 == b (mod a). + * + * Sets result to 1 if the congruence holds, or zero otherwise. + */ +int mp_prime_fermat(mp_int *a, mp_int *b, int *result) { + mp_int t; + int err; + + /* default to composite */ + *result = MP_NO; + + /* ensure b > 1 */ + if (mp_cmp_d(b, 1) != MP_GT) { + return MP_VAL; + } + + /* init t */ + if ((err = mp_init(&t)) != MP_OKAY) { + return err; + } + + /* compute t = b**a mod a */ + if ((err = mp_exptmod(b, a, a, &t)) != MP_OKAY) { + goto LBL_T; + } + + /* is it equal to b? */ + if (mp_cmp(&t, b) == MP_EQ) { + *result = MP_YES; + } + + err = MP_OKAY; +LBL_T: mp_clear(&t); + return err; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_PRIME_IS_DIVISIBLE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* determines if an integers is divisible by one + * of the first PRIME_SIZE primes or not + * + * sets result to 0 if not, 1 if yes + */ +int mp_prime_is_divisible(mp_int *a, int *result) { + int err, ix; + mp_digit res; + + /* default to not */ + *result = MP_NO; + + for (ix = 0; ix < PRIME_SIZE; ix++) { + /* what is a mod LBL_prime_tab[ix] */ + if ((err = mp_mod_d(a, ltm_prime_tab[ix], &res)) != MP_OKAY) { + return err; + } + + /* is the residue zero? */ + if (res == 0) { + *result = MP_YES; + return MP_OKAY; + } + } + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_PRIME_IS_PRIME_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* performs a variable number of rounds of Miller-Rabin + * + * Probability of error after t rounds is no more than + + * + * Sets result to 1 if probably prime, 0 otherwise + */ +int mp_prime_is_prime(mp_int *a, int t, int *result) { + mp_int b; + int ix, err, res; + + /* default to no */ + *result = MP_NO; + + /* valid value of t? */ + if ((t <= 0) || (t > PRIME_SIZE)) { + return MP_VAL; + } + + /* is the input equal to one of the primes in the table? */ + for (ix = 0; ix < PRIME_SIZE; ix++) { + if (mp_cmp_d(a, ltm_prime_tab[ix]) == MP_EQ) { + *result = 1; + return MP_OKAY; + } + } + + /* first perform trial division */ + if ((err = mp_prime_is_divisible(a, &res)) != MP_OKAY) { + return err; + } + + /* return if it was trivially divisible */ + if (res == MP_YES) { + return MP_OKAY; + } + + /* now perform the miller-rabin rounds */ + if ((err = mp_init(&b)) != MP_OKAY) { + return err; + } + + for (ix = 0; ix < t; ix++) { + /* set the prime */ + mp_set(&b, ltm_prime_tab[ix]); + + if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) { + goto LBL_B; + } + + if (res == MP_NO) { + goto LBL_B; + } + } + + /* passed the test */ + *result = MP_YES; +LBL_B: mp_clear(&b); + return err; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_PRIME_MILLER_RABIN_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* Miller-Rabin test of "a" to the base of "b" as described in + * HAC pp. 139 Algorithm 4.24 + * + * Sets result to 0 if definitely composite or 1 if probably prime. + * Randomly the chance of error is no more than 1/4 and often + * very much lower. + */ +int mp_prime_miller_rabin(mp_int *a, mp_int *b, int *result) { + mp_int n1, y, r; + int s, j, err; + + /* default */ + *result = MP_NO; + + /* ensure b > 1 */ + if (mp_cmp_d(b, 1) != MP_GT) { + return MP_VAL; + } + + /* get n1 = a - 1 */ + if ((err = mp_init_copy(&n1, a)) != MP_OKAY) { + return err; + } + if ((err = mp_sub_d(&n1, 1, &n1)) != MP_OKAY) { + goto LBL_N1; + } + + /* set 2**s * r = n1 */ + if ((err = mp_init_copy(&r, &n1)) != MP_OKAY) { + goto LBL_N1; + } + + /* count the number of least significant bits + * which are zero + */ + s = mp_cnt_lsb(&r); + + /* now divide n - 1 by 2**s */ + if ((err = mp_div_2d(&r, s, &r, NULL)) != MP_OKAY) { + goto LBL_R; + } + + /* compute y = b**r mod a */ + if ((err = mp_init(&y)) != MP_OKAY) { + goto LBL_R; + } + if ((err = mp_exptmod(b, &r, a, &y)) != MP_OKAY) { + goto LBL_Y; + } + + /* if y != 1 and y != n1 do */ + if ((mp_cmp_d(&y, 1) != MP_EQ) && (mp_cmp(&y, &n1) != MP_EQ)) { + j = 1; + /* while j <= s-1 and y != n1 */ + while ((j <= (s - 1)) && (mp_cmp(&y, &n1) != MP_EQ)) { + if ((err = mp_sqrmod(&y, a, &y)) != MP_OKAY) { + goto LBL_Y; + } + + /* if y == 1 then composite */ + if (mp_cmp_d(&y, 1) == MP_EQ) { + goto LBL_Y; + } + + ++j; + } + + /* if y != n1 then composite */ + if (mp_cmp(&y, &n1) != MP_EQ) { + goto LBL_Y; + } + } + + /* probably prime now */ + *result = MP_YES; +LBL_Y: mp_clear(&y); +LBL_R: mp_clear(&r); +LBL_N1: mp_clear(&n1); + return err; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_PRIME_NEXT_PRIME_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* finds the next prime after the number "a" using "t" trials + * of Miller-Rabin. + * + * bbs_style = 1 means the prime must be congruent to 3 mod 4 + */ +int mp_prime_next_prime(mp_int *a, int t, int bbs_style) { + int err, res = MP_NO, x, y; + mp_digit res_tab[PRIME_SIZE], step, kstep; + mp_int b; + + /* ensure t is valid */ + if ((t <= 0) || (t > PRIME_SIZE)) { + return MP_VAL; + } + + /* force positive */ + a->sign = MP_ZPOS; + + /* simple algo if a is less than the largest prime in the table */ + if (mp_cmp_d(a, ltm_prime_tab[PRIME_SIZE - 1]) == MP_LT) { + /* find which prime it is bigger than */ + for (x = PRIME_SIZE - 2; x >= 0; x--) { + if (mp_cmp_d(a, ltm_prime_tab[x]) != MP_LT) { + if (bbs_style == 1) { + /* ok we found a prime smaller or + * equal [so the next is larger] + * + * however, the prime must be + * congruent to 3 mod 4 + */ + if ((ltm_prime_tab[x + 1] & 3) != 3) { + /* scan upwards for a prime congruent to 3 mod 4 */ + for (y = x + 1; y < PRIME_SIZE; y++) { + if ((ltm_prime_tab[y] & 3) == 3) { + mp_set(a, ltm_prime_tab[y]); + return MP_OKAY; + } + } + } + } else { + mp_set(a, ltm_prime_tab[x + 1]); + return MP_OKAY; + } + } + } + /* at this point a maybe 1 */ + if (mp_cmp_d(a, 1) == MP_EQ) { + mp_set(a, 2); + return MP_OKAY; + } + /* fall through to the sieve */ + } + + /* generate a prime congruent to 3 mod 4 or 1/3 mod 4? */ + if (bbs_style == 1) { + kstep = 4; + } else { + kstep = 2; + } + + /* at this point we will use a combination of a sieve and Miller-Rabin */ + + if (bbs_style == 1) { + /* if a mod 4 != 3 subtract the correct value to make it so */ + if ((a->dp[0] & 3) != 3) { + if ((err = mp_sub_d(a, (a->dp[0] & 3) + 1, a)) != MP_OKAY) { + return err; + } + } + } else { + if (mp_iseven(a) == MP_YES) { + /* force odd */ + if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) { + return err; + } + } + } + + /* generate the restable */ + for (x = 1; x < PRIME_SIZE; x++) { + if ((err = mp_mod_d(a, ltm_prime_tab[x], res_tab + x)) != MP_OKAY) { + return err; + } + } + + /* init temp used for Miller-Rabin Testing */ + if ((err = mp_init(&b)) != MP_OKAY) { + return err; + } + + for ( ; ; ) { + /* skip to the next non-trivially divisible candidate */ + step = 0; + do { + /* y == 1 if any residue was zero [e.g. cannot be prime] */ + y = 0; + + /* increase step to next candidate */ + step += kstep; + + /* compute the new residue without using division */ + for (x = 1; x < PRIME_SIZE; x++) { + /* add the step to each residue */ + res_tab[x] += kstep; + + /* subtract the modulus [instead of using division] */ + if (res_tab[x] >= ltm_prime_tab[x]) { + res_tab[x] -= ltm_prime_tab[x]; + } + + /* set flag if zero */ + if (res_tab[x] == 0) { + y = 1; + } + } + } while ((y == 1) && (step < ((((mp_digit)1) << DIGIT_BIT) - kstep))); + + /* add the step */ + if ((err = mp_add_d(a, step, a)) != MP_OKAY) { + goto LBL_ERR; + } + + /* if didn't pass sieve and step == MAX then skip test */ + if ((y == 1) && (step >= ((((mp_digit)1) << DIGIT_BIT) - kstep))) { + continue; + } + + /* is this prime? */ + for (x = 0; x < t; x++) { + mp_set(&b, ltm_prime_tab[x]); + if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) { + goto LBL_ERR; + } + if (res == MP_NO) { + break; + } + } + + if (res == MP_YES) { + break; + } + } + + err = MP_OKAY; +LBL_ERR: + mp_clear(&b); + return err; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_PRIME_RABIN_MILLER_TRIALS_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + + +static const struct { + int k, t; +} sizes[] = { + { 128, 28 }, + { 256, 16 }, + { 384, 10 }, + { 512, 7 }, + { 640, 6 }, + { 768, 5 }, + { 896, 4 }, + { 1024, 4 } +}; + +/* returns # of RM trials required for a given bit size */ +int mp_prime_rabin_miller_trials(int size) { + int x; + + for (x = 0; x < (int)(sizeof(sizes) / (sizeof(sizes[0]))); x++) { + if (sizes[x].k == size) { + return sizes[x].t; + } else if (sizes[x].k > size) { + return (x == 0) ? sizes[0].t : sizes[x - 1].t; + } + } + return sizes[x - 1].t + 1; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_PRIME_RANDOM_EX_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* makes a truly random prime of a given size (bits), + * + * Flags are as follows: + * + * LTM_PRIME_BBS - make prime congruent to 3 mod 4 + * LTM_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS) + * LTM_PRIME_2MSB_ON - make the 2nd highest bit one + * + * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can + * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself + * so it can be NULL + * + */ + +/* This is possibly the mother of all prime generation functions, muahahahahaha! */ +int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat) { + unsigned char *tmp, maskAND, maskOR_msb, maskOR_lsb; + int res, err, bsize, maskOR_msb_offset; + + /* sanity check the input */ + if ((size <= 1) || (t <= 0)) { + return MP_VAL; + } + + /* LTM_PRIME_SAFE implies LTM_PRIME_BBS */ + if ((flags & LTM_PRIME_SAFE) != 0) { + flags |= LTM_PRIME_BBS; + } + + /* calc the byte size */ + bsize = (size >> 3) + ((size & 7) ? 1 : 0); + + /* we need a buffer of bsize bytes */ + tmp = OPT_CAST(unsigned char) XMALLOC(bsize); + if (tmp == NULL) { + return MP_MEM; + } + + /* calc the maskAND value for the MSbyte*/ + maskAND = ((size & 7) == 0) ? 0xFF : (0xFF >> (8 - (size & 7))); + + /* calc the maskOR_msb */ + maskOR_msb = 0; + maskOR_msb_offset = ((size & 7) == 1) ? 1 : 0; + if ((flags & LTM_PRIME_2MSB_ON) != 0) { + maskOR_msb |= 0x80 >> ((9 - size) & 7); + } + + /* get the maskOR_lsb */ + maskOR_lsb = 1; + if ((flags & LTM_PRIME_BBS) != 0) { + maskOR_lsb |= 3; + } + + do { + /* read the bytes */ + if (cb(tmp, bsize, dat) != bsize) { + err = MP_VAL; + goto error; + } + + /* work over the MSbyte */ + tmp[0] &= maskAND; + tmp[0] |= 1 << ((size - 1) & 7); + + /* mix in the maskORs */ + tmp[maskOR_msb_offset] |= maskOR_msb; + tmp[bsize - 1] |= maskOR_lsb; + + /* read it in */ + if ((err = mp_read_unsigned_bin(a, tmp, bsize)) != MP_OKAY) { + goto error; + } + + /* is it prime? */ + if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { + goto error; + } + if (res == MP_NO) { + continue; + } + + if ((flags & LTM_PRIME_SAFE) != 0) { + /* see if (a-1)/2 is prime */ + if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) { + goto error; + } + if ((err = mp_div_2(a, a)) != MP_OKAY) { + goto error; + } + + /* is it prime? */ + if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { + goto error; + } + } + } while (res == MP_NO); + + if ((flags & LTM_PRIME_SAFE) != 0) { + /* restore a to the original value */ + if ((err = mp_mul_2(a, a)) != MP_OKAY) { + goto error; + } + if ((err = mp_add_d(a, 1, a)) != MP_OKAY) { + goto error; + } + } + + err = MP_OKAY; +error: + XFREE(tmp); + return err; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_RADIX_SIZE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* returns size of ASCII reprensentation */ +int mp_radix_size(mp_int *a, int radix, int *size) { + int res, digs; + mp_int t; + mp_digit d; + + *size = 0; + + /* make sure the radix is in range */ + if ((radix < 2) || (radix > 64)) { + return MP_VAL; + } + + if (mp_iszero(a) == MP_YES) { + *size = 2; + return MP_OKAY; + } + + /* special case for binary */ + if (radix == 2) { + *size = mp_count_bits(a) + ((a->sign == MP_NEG) ? 1 : 0) + 1; + return MP_OKAY; + } + + /* digs is the digit count */ + digs = 0; + + /* if it's negative add one for the sign */ + if (a->sign == MP_NEG) { + ++digs; + } + + /* init a copy of the input */ + if ((res = mp_init_copy(&t, a)) != MP_OKAY) { + return res; + } + + /* force temp to positive */ + t.sign = MP_ZPOS; + + /* fetch out all of the digits */ + while (mp_iszero(&t) == MP_NO) { + if ((res = mp_div_d(&t, (mp_digit)radix, &t, &d)) != MP_OKAY) { + mp_clear(&t); + return res; + } + ++digs; + } + mp_clear(&t); + + /* return digs + 1, the 1 is for the NULL byte that would be required. */ + *size = digs + 1; + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_RADIX_SMAP_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* chars used in radix conversions */ +const char *mp_s_rmap = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_RAND_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* makes a pseudo-random int of a given size */ +int +mp_rand(mp_int *a, int digits) { + int res; + mp_digit d; + + mp_zero(a); + if (digits <= 0) { + return MP_OKAY; + } + + /* first place a random non-zero digit */ + do { + d = ((mp_digit)abs(MP_GEN_RANDOM())) & MP_MASK; + } while (d == 0); + + if ((res = mp_add_d(a, d, a)) != MP_OKAY) { + return res; + } + + while (--digits > 0) { + if ((res = mp_lshd(a, 1)) != MP_OKAY) { + return res; + } + + if ((res = mp_add_d(a, ((mp_digit)abs(MP_GEN_RANDOM())), a)) != MP_OKAY) { + return res; + } + } + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_READ_RADIX_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* read a string [ASCII] in a given radix */ +int mp_read_radix(mp_int *a, const char *str, int radix) { + int y, res, neg; + char ch; + + /* zero the digit bignum */ + mp_zero(a); + + /* make sure the radix is ok */ + if ((radix < 2) || (radix > 64)) { + return MP_VAL; + } + + /* if the leading digit is a + * minus set the sign to negative. + */ + if (*str == '-') { + ++str; + neg = MP_NEG; + } else { + neg = MP_ZPOS; + } + + /* set the integer to the default of zero */ + mp_zero(a); + + /* process each digit of the string */ + while (*str != '\0') { + /* if the radix <= 36 the conversion is case insensitive + * this allows numbers like 1AB and 1ab to represent the same value + * [e.g. in hex] + */ + ch = (radix <= 36) ? (char)toupper((int)*str) : *str; + for (y = 0; y < 64; y++) { + if (ch == mp_s_rmap[y]) { + break; + } + } + + /* if the char was found in the map + * and is less than the given radix add it + * to the number, otherwise exit the loop. + */ + if (y < radix) { + if ((res = mp_mul_d(a, (mp_digit)radix, a)) != MP_OKAY) { + return res; + } + if ((res = mp_add_d(a, (mp_digit)y, a)) != MP_OKAY) { + return res; + } + } else { + break; + } + ++str; + } + + /* set the sign only if a != 0 */ + if (mp_iszero(a) != MP_YES) { + a->sign = neg; + } + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_READ_SIGNED_BIN_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* read signed bin, big endian, first byte is 0==positive or 1==negative */ +int mp_read_signed_bin(mp_int *a, const unsigned char *b, int c) { + int res; + + /* read magnitude */ + if ((res = mp_read_unsigned_bin(a, b + 1, c - 1)) != MP_OKAY) { + return res; + } + + /* first byte is 0 for positive, non-zero for negative */ + if (b[0] == 0) { + a->sign = MP_ZPOS; + } else { + a->sign = MP_NEG; + } + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_READ_UNSIGNED_BIN_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* reads a unsigned char array, assumes the msb is stored first [big endian] */ +int mp_read_unsigned_bin(mp_int *a, const unsigned char *b, int c) { + int res; + + /* make sure there are at least two digits */ + if (a->alloc < 2) { + if ((res = mp_grow(a, 2)) != MP_OKAY) { + return res; + } + } + + /* zero the int */ + mp_zero(a); + + /* read the bytes in */ + while (c-- > 0) { + if ((res = mp_mul_2d(a, 8, a)) != MP_OKAY) { + return res; + } + + #ifndef MP_8BIT + a->dp[0] |= *b++; + a->used += 1; + #else + a->dp[0] = (*b & MP_MASK); + a->dp[1] |= ((*b++ >> 7U) & 1); + a->used += 2; + #endif + } + mp_clamp(a); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_REDUCE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* reduces x mod m, assumes 0 < x < m**2, mu is + * precomputed via mp_reduce_setup. + * From HAC pp.604 Algorithm 14.42 + */ +int mp_reduce(mp_int *x, mp_int *m, mp_int *mu) { + mp_int q; + int res, um = m->used; + + /* q = x */ + if ((res = mp_init_copy(&q, x)) != MP_OKAY) { + return res; + } + + /* q1 = x / b**(k-1) */ + mp_rshd(&q, um - 1); + + /* according to HAC this optimization is ok */ + if (((mp_digit)um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { + if ((res = mp_mul(&q, mu, &q)) != MP_OKAY) { + goto CLEANUP; + } + } else { + #ifdef BN_S_MP_MUL_HIGH_DIGS_C + if ((res = s_mp_mul_high_digs(&q, mu, &q, um)) != MP_OKAY) { + goto CLEANUP; + } + #elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) + if ((res = fast_s_mp_mul_high_digs(&q, mu, &q, um)) != MP_OKAY) { + goto CLEANUP; + } + #else + { + res = MP_VAL; + goto CLEANUP; + } + #endif + } + + /* q3 = q2 / b**(k+1) */ + mp_rshd(&q, um + 1); + + /* x = x mod b**(k+1), quick (no division) */ + if ((res = mp_mod_2d(x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { + goto CLEANUP; + } + + /* q = q * m mod b**(k+1), quick (no division) */ + if ((res = s_mp_mul_digs(&q, m, &q, um + 1)) != MP_OKAY) { + goto CLEANUP; + } + + /* x = x - q */ + if ((res = mp_sub(x, &q, x)) != MP_OKAY) { + goto CLEANUP; + } + + /* If x < 0, add b**(k+1) to it */ + if (mp_cmp_d(x, 0) == MP_LT) { + mp_set(&q, 1); + if ((res = mp_lshd(&q, um + 1)) != MP_OKAY) + goto CLEANUP; + if ((res = mp_add(x, &q, x)) != MP_OKAY) + goto CLEANUP; + } + + /* Back off if it's too big */ + while (mp_cmp(x, m) != MP_LT) { + if ((res = s_mp_sub(x, m, x)) != MP_OKAY) { + goto CLEANUP; + } + } + +CLEANUP: + mp_clear(&q); + + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_REDUCE_2K_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* reduces a modulo n where n is of the form 2**p - d */ +int mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d) { + mp_int q; + int p, res; + + if ((res = mp_init(&q)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(n); +top: + /* q = a/2**p, a = a mod 2**p */ + if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (d != 1) { + /* q = q * d */ + if ((res = mp_mul_d(&q, d, &q)) != MP_OKAY) { + goto ERR; + } + } + + /* a = a + q */ + if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (mp_cmp_mag(a, n) != MP_LT) { + if ((res = s_mp_sub(a, n, a)) != MP_OKAY) { + goto ERR; + } + goto top; + } + +ERR: + mp_clear(&q); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_REDUCE_2K_L_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* reduces a modulo n where n is of the form 2**p - d + This differs from reduce_2k since "d" can be larger + than a single digit. + */ +int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d) { + mp_int q; + int p, res; + + if ((res = mp_init(&q)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(n); +top: + /* q = a/2**p, a = a mod 2**p */ + if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { + goto ERR; + } + + /* q = q * d */ + if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { + goto ERR; + } + + /* a = a + q */ + if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (mp_cmp_mag(a, n) != MP_LT) { + if ((res = s_mp_sub(a, n, a)) != MP_OKAY) { + goto ERR; + } + goto top; + } + +ERR: + mp_clear(&q); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_REDUCE_2K_SETUP_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* determines the setup value */ +int mp_reduce_2k_setup(mp_int *a, mp_digit *d) { + int res, p; + mp_int tmp; + + if ((res = mp_init(&tmp)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(a); + if ((res = mp_2expt(&tmp, p)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + if ((res = s_mp_sub(&tmp, a, &tmp)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + *d = tmp.dp[0]; + mp_clear(&tmp); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_REDUCE_2K_SETUP_L_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* determines the setup value */ +int mp_reduce_2k_setup_l(mp_int *a, mp_int *d) { + int res; + mp_int tmp; + + if ((res = mp_init(&tmp)) != MP_OKAY) { + return res; + } + + if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { + goto ERR; + } + + if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear(&tmp); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_REDUCE_IS_2K_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* determines if mp_reduce_2k can be used */ +int mp_reduce_is_2k(mp_int *a) { + int ix, iy, iw; + mp_digit iz; + + if (a->used == 0) { + return MP_NO; + } else if (a->used == 1) { + return MP_YES; + } else if (a->used > 1) { + iy = mp_count_bits(a); + iz = 1; + iw = 1; + + /* Test every bit from the second digit up, must be 1 */ + for (ix = DIGIT_BIT; ix < iy; ix++) { + if ((a->dp[iw] & iz) == 0) { + return MP_NO; + } + iz <<= 1; + if (iz > (mp_digit)MP_MASK) { + ++iw; + iz = 1; + } + } + } + return MP_YES; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_REDUCE_IS_2K_L_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* determines if reduce_2k_l can be used */ +int mp_reduce_is_2k_l(mp_int *a) { + int ix, iy; + + if (a->used == 0) { + return MP_NO; + } else if (a->used == 1) { + return MP_YES; + } else if (a->used > 1) { + /* if more than half of the digits are -1 we're sold */ + for (iy = ix = 0; ix < a->used; ix++) { + if (a->dp[ix] == MP_MASK) { + ++iy; + } + } + return (iy >= (a->used / 2)) ? MP_YES : MP_NO; + } + return MP_NO; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_REDUCE_SETUP_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* pre-calculate the value required for Barrett reduction + * For a given modulus "b" it calulates the value required in "a" + */ +int mp_reduce_setup(mp_int *a, mp_int *b) { + int res; + + if ((res = mp_2expt(a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { + return res; + } + return mp_div(a, b, a, NULL); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_RSHD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* shift right a certain amount of digits */ +void mp_rshd(mp_int *a, int b) { + int x; + + /* if b <= 0 then ignore it */ + if (b <= 0) { + return; + } + + /* if b > used then simply zero it and return */ + if (a->used <= b) { + mp_zero(a); + return; + } + + { + mp_digit *bottom, *top; + + /* shift the digits down */ + + /* bottom */ + bottom = a->dp; + + /* top [offset into digits] */ + top = a->dp + b; + + /* this is implemented as a sliding window where + * the window is b-digits long and digits from + * the top of the window are copied to the bottom + * + * e.g. + + b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> + /\ | ----> + **\-------------------/ ----> + */ + for (x = 0; x < (a->used - b); x++) { + *bottom++ = *top++; + } + + /* zero the top digits */ + for ( ; x < a->used; x++) { + *bottom++ = 0; + } + } + + /* remove excess digits */ + a->used -= b; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SET_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* set to a digit */ +void mp_set(mp_int *a, mp_digit b) { + mp_zero(a); + a->dp[0] = b & MP_MASK; + a->used = (a->dp[0] != 0) ? 1 : 0; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SET_INT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* set a 32-bit const */ +int mp_set_int(mp_int *a, unsigned long b) { + int x, res; + + mp_zero(a); + + /* set four bits at a time */ + for (x = 0; x < 8; x++) { + /* shift the number up four bits */ + if ((res = mp_mul_2d(a, 4, a)) != MP_OKAY) { + return res; + } + + /* OR in the top four bits of the source */ + a->dp[0] |= (b >> 28) & 15; + + /* shift the source up to the next four bits */ + b <<= 4; + + /* ensure that digits are not clamped off */ + a->used += 1; + } + mp_clamp(a); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SET_LONG_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* set a platform dependent unsigned long int */ +MP_SET_XLONG(mp_set_long, unsigned long) +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SET_LONG_LONG_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* set a platform dependent unsigned long long int */ +MP_SET_XLONG(mp_set_long_long, unsigned long long) +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SHRINK_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* shrink a bignum */ +int mp_shrink(mp_int *a) { + mp_digit *tmp; + int used = 1; + + if (a->used > 0) { + used = a->used; + } + + if (a->alloc != used) { + if ((tmp = OPT_CAST(mp_digit) XREALLOC(a->dp, sizeof(mp_digit) * used)) == NULL) { + return MP_MEM; + } + a->dp = tmp; + a->alloc = used; + } + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SIGNED_BIN_SIZE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* get the size for an signed equivalent */ +int mp_signed_bin_size(mp_int *a) { + return 1 + mp_unsigned_bin_size(a); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SQR_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* computes b = a*a */ +int +mp_sqr(mp_int *a, mp_int *b) { + int res; + + #ifdef BN_MP_TOOM_SQR_C + /* use Toom-Cook? */ + if (a->used >= TOOM_SQR_CUTOFF) { + res = mp_toom_sqr(a, b); + /* Karatsuba? */ + } else + #endif + #ifdef BN_MP_KARATSUBA_SQR_C + if (a->used >= KARATSUBA_SQR_CUTOFF) { + res = mp_karatsuba_sqr(a, b); + } else + #endif + { + #ifdef BN_FAST_S_MP_SQR_C + /* can we use the fast comba multiplier? */ + if ((((a->used * 2) + 1) < MP_WARRAY) && + (a->used < + (1 << (((sizeof(mp_word) * CHAR_BIT) - (2 * DIGIT_BIT)) - 1)))) { + res = fast_s_mp_sqr(a, b); + } else + #endif + { + #ifdef BN_S_MP_SQR_C + res = s_mp_sqr(a, b); + #else + res = MP_VAL; + #endif + } + } + b->sign = MP_ZPOS; + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SQRMOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* c = a * a (mod b) */ +int +mp_sqrmod(mp_int *a, mp_int *b, mp_int *c) { + int res; + mp_int t; + + if ((res = mp_init(&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_sqr(a, &t)) != MP_OKAY) { + mp_clear(&t); + return res; + } + res = mp_mod(&t, b, c); + mp_clear(&t); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SQRT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* this function is less generic than mp_n_root, simpler and faster */ +int mp_sqrt(mp_int *arg, mp_int *ret) { + int res; + mp_int t1, t2; + + /* must be positive */ + if (arg->sign == MP_NEG) { + return MP_VAL; + } + + /* easy out */ + if (mp_iszero(arg) == MP_YES) { + mp_zero(ret); + return MP_OKAY; + } + + if ((res = mp_init_copy(&t1, arg)) != MP_OKAY) { + return res; + } + + if ((res = mp_init(&t2)) != MP_OKAY) { + goto E2; + } + + /* First approx. (not very bad for large arg) */ + mp_rshd(&t1, t1.used / 2); + + /* t1 > 0 */ + if ((res = mp_div(arg, &t1, &t2, NULL)) != MP_OKAY) { + goto E1; + } + if ((res = mp_add(&t1, &t2, &t1)) != MP_OKAY) { + goto E1; + } + if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) { + goto E1; + } + /* And now t1 > sqrt(arg) */ + do { + if ((res = mp_div(arg, &t1, &t2, NULL)) != MP_OKAY) { + goto E1; + } + if ((res = mp_add(&t1, &t2, &t1)) != MP_OKAY) { + goto E1; + } + if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) { + goto E1; + } + /* t1 >= sqrt(arg) >= t2 at this point */ + } while (mp_cmp_mag(&t1, &t2) == MP_GT); + + mp_exch(&t1, ret); + +E1: mp_clear(&t2); +E2: mp_clear(&t1); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SQRTMOD_PRIME_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is free for all purposes without any express + * guarantee it works. + */ + +/* Tonelli-Shanks algorithm + * https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm + * https://gmplib.org/list-archives/gmp-discuss/2013-April/005300.html + * + */ + +int mp_sqrtmod_prime(mp_int *n, mp_int *prime, mp_int *ret) { + int res, legendre; + mp_int t1, C, Q, S, Z, M, T, R, two; + mp_digit i; + + /* first handle the simple cases */ + if (mp_cmp_d(n, 0) == MP_EQ) { + mp_zero(ret); + return MP_OKAY; + } + if (mp_cmp_d(prime, 2) == MP_EQ) return MP_VAL; /* prime must be odd */ + if ((res = mp_jacobi(n, prime, &legendre)) != MP_OKAY) return res; + if (legendre == -1) return MP_VAL; /* quadratic non-residue mod prime */ + + if ((res = mp_init_multi(&t1, &C, &Q, &S, &Z, &M, &T, &R, &two, NULL)) != MP_OKAY) { + return res; + } + + /* SPECIAL CASE: if prime mod 4 == 3 + * compute directly: res = n^(prime+1)/4 mod prime + * Handbook of Applied Cryptography algorithm 3.36 + */ + if ((res = mp_mod_d(prime, 4, &i)) != MP_OKAY) goto cleanup; + if (i == 3) { + if ((res = mp_add_d(prime, 1, &t1)) != MP_OKAY) goto cleanup; + if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) goto cleanup; + if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) goto cleanup; + if ((res = mp_exptmod(n, &t1, prime, ret)) != MP_OKAY) goto cleanup; + res = MP_OKAY; + goto cleanup; + } + + /* NOW: Tonelli-Shanks algorithm */ + + /* factor out powers of 2 from prime-1, defining Q and S as: prime-1 = Q*2^S */ + if ((res = mp_copy(prime, &Q)) != MP_OKAY) goto cleanup; + if ((res = mp_sub_d(&Q, 1, &Q)) != MP_OKAY) goto cleanup; + /* Q = prime - 1 */ + mp_zero(&S); + /* S = 0 */ + while (mp_iseven(&Q) != MP_NO) { + if ((res = mp_div_2(&Q, &Q)) != MP_OKAY) goto cleanup; + /* Q = Q / 2 */ + if ((res = mp_add_d(&S, 1, &S)) != MP_OKAY) goto cleanup; + /* S = S + 1 */ + } + + /* find a Z such that the Legendre symbol (Z|prime) == -1 */ + if ((res = mp_set_int(&Z, 2)) != MP_OKAY) goto cleanup; + /* Z = 2 */ + while (1) { + if ((res = mp_jacobi(&Z, prime, &legendre)) != MP_OKAY) goto cleanup; + if (legendre == -1) break; + if ((res = mp_add_d(&Z, 1, &Z)) != MP_OKAY) goto cleanup; + /* Z = Z + 1 */ + } + + if ((res = mp_exptmod(&Z, &Q, prime, &C)) != MP_OKAY) goto cleanup; + /* C = Z ^ Q mod prime */ + if ((res = mp_add_d(&Q, 1, &t1)) != MP_OKAY) goto cleanup; + if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) goto cleanup; + /* t1 = (Q + 1) / 2 */ + if ((res = mp_exptmod(n, &t1, prime, &R)) != MP_OKAY) goto cleanup; + /* R = n ^ ((Q + 1) / 2) mod prime */ + if ((res = mp_exptmod(n, &Q, prime, &T)) != MP_OKAY) goto cleanup; + /* T = n ^ Q mod prime */ + if ((res = mp_copy(&S, &M)) != MP_OKAY) goto cleanup; + /* M = S */ + if ((res = mp_set_int(&two, 2)) != MP_OKAY) goto cleanup; + + res = MP_VAL; + while (1) { + if ((res = mp_copy(&T, &t1)) != MP_OKAY) goto cleanup; + i = 0; + while (1) { + if (mp_cmp_d(&t1, 1) == MP_EQ) break; + if ((res = mp_exptmod(&t1, &two, prime, &t1)) != MP_OKAY) goto cleanup; + i++; + } + if (i == 0) { + if ((res = mp_copy(&R, ret)) != MP_OKAY) goto cleanup; + res = MP_OKAY; + goto cleanup; + } + if ((res = mp_sub_d(&M, i, &t1)) != MP_OKAY) goto cleanup; + if ((res = mp_sub_d(&t1, 1, &t1)) != MP_OKAY) goto cleanup; + if ((res = mp_exptmod(&two, &t1, prime, &t1)) != MP_OKAY) goto cleanup; + /* t1 = 2 ^ (M - i - 1) */ + if ((res = mp_exptmod(&C, &t1, prime, &t1)) != MP_OKAY) goto cleanup; + /* t1 = C ^ (2 ^ (M - i - 1)) mod prime */ + if ((res = mp_sqrmod(&t1, prime, &C)) != MP_OKAY) goto cleanup; + /* C = (t1 * t1) mod prime */ + if ((res = mp_mulmod(&R, &t1, prime, &R)) != MP_OKAY) goto cleanup; + /* R = (R * t1) mod prime */ + if ((res = mp_mulmod(&T, &C, prime, &T)) != MP_OKAY) goto cleanup; + /* T = (T * C) mod prime */ + mp_set(&M, i); + /* M = i */ + } + +cleanup: + mp_clear_multi(&t1, &C, &Q, &S, &Z, &M, &T, &R, &two, NULL); + return res; +} +#endif + + + +#ifdef BN_MP_SUB_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* high level subtraction (handles signs) */ +int +mp_sub(mp_int *a, mp_int *b, mp_int *c) { + int sa, sb, res; + + sa = a->sign; + sb = b->sign; + + if (sa != sb) { + /* subtract a negative from a positive, OR */ + /* subtract a positive from a negative. */ + /* In either case, ADD their magnitudes, */ + /* and use the sign of the first number. */ + c->sign = sa; + res = s_mp_add(a, b, c); + } else { + /* subtract a positive from a positive, OR */ + /* subtract a negative from a negative. */ + /* First, take the difference between their */ + /* magnitudes, then... */ + if (mp_cmp_mag(a, b) != MP_LT) { + /* Copy the sign from the first */ + c->sign = sa; + /* The first has a larger or equal magnitude */ + res = s_mp_sub(a, b, c); + } else { + /* The result has the *opposite* sign from */ + /* the first number. */ + c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS; + /* The second has a larger magnitude */ + res = s_mp_sub(b, a, c); + } + } + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SUB_D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* single digit subtraction */ +int +mp_sub_d(mp_int *a, mp_digit b, mp_int *c) { + mp_digit *tmpa, *tmpc, mu; + int res, ix, oldused; + + /* grow c as required */ + if (c->alloc < (a->used + 1)) { + if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* if a is negative just do an unsigned + * addition [with fudged signs] + */ + if (a->sign == MP_NEG) { + a->sign = MP_ZPOS; + res = mp_add_d(a, b, c); + a->sign = c->sign = MP_NEG; + + /* clamp */ + mp_clamp(c); + + return res; + } + + /* setup regs */ + oldused = c->used; + tmpa = a->dp; + tmpc = c->dp; + + /* if a <= b simply fix the single digit */ + if (((a->used == 1) && (a->dp[0] <= b)) || (a->used == 0)) { + if (a->used == 1) { + *tmpc++ = b - *tmpa; + } else { + *tmpc++ = b; + } + ix = 1; + + /* negative/1digit */ + c->sign = MP_NEG; + c->used = 1; + } else { + /* positive/size */ + c->sign = MP_ZPOS; + c->used = a->used; + + /* subtract first digit */ + *tmpc = *tmpa++ - b; + mu = *tmpc >> ((sizeof(mp_digit) * CHAR_BIT) - 1); + *tmpc++ &= MP_MASK; + + /* handle rest of the digits */ + for (ix = 1; ix < a->used; ix++) { + *tmpc = *tmpa++ - mu; + mu = *tmpc >> ((sizeof(mp_digit) * CHAR_BIT) - 1); + *tmpc++ &= MP_MASK; + } + } + + /* zero excess digits */ + while (ix++ < oldused) { + *tmpc++ = 0; + } + mp_clamp(c); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SUBMOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* d = a - b (mod c) */ +int +mp_submod(mp_int *a, mp_int *b, mp_int *c, mp_int *d) { + int res; + mp_int t; + + + if ((res = mp_init(&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_sub(a, b, &t)) != MP_OKAY) { + mp_clear(&t); + return res; + } + res = mp_mod(&t, c, d); + mp_clear(&t); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_TO_SIGNED_BIN_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* store in signed [big endian] format */ +int mp_to_signed_bin(mp_int *a, unsigned char *b) { + int res; + + if ((res = mp_to_unsigned_bin(a, b + 1)) != MP_OKAY) { + return res; + } + b[0] = (a->sign == MP_ZPOS) ? (unsigned char)0 : (unsigned char)1; + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_TO_SIGNED_BIN_N_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* store in signed [big endian] format */ +int mp_to_signed_bin_n(mp_int *a, unsigned char *b, unsigned long *outlen) { + if (*outlen < (unsigned long)mp_signed_bin_size(a)) { + return MP_VAL; + } + *outlen = mp_signed_bin_size(a); + return mp_to_signed_bin(a, b); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_TO_UNSIGNED_BIN_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* store in unsigned [big endian] format */ +int mp_to_unsigned_bin(mp_int *a, unsigned char *b) { + int x, res; + mp_int t; + + if ((res = mp_init_copy(&t, a)) != MP_OKAY) { + return res; + } + + x = 0; + while (mp_iszero(&t) == MP_NO) { + #ifndef MP_8BIT + b[x++] = (unsigned char)(t.dp[0] & 255); + #else + b[x++] = (unsigned char)(t.dp[0] | ((t.dp[1] & 0x01) << 7)); + #endif + if ((res = mp_div_2d(&t, 8, &t, NULL)) != MP_OKAY) { + mp_clear(&t); + return res; + } + } + bn_reverse(b, x); + mp_clear(&t); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_TOOM_MUL_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* multiplication using the Toom-Cook 3-way algorithm + * + * Much more complicated than Karatsuba but has a lower + * asymptotic running time of O(N**1.464). This algorithm is + * only particularly useful on VERY large inputs + * (we're talking 1000s of digits here...). + */ +int mp_toom_mul(mp_int *a, mp_int *b, mp_int *c) { + mp_int w0, w1, w2, w3, w4, tmp1, tmp2, a0, a1, a2, b0, b1, b2; + int res, B; + + /* init temps */ + if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, + &a0, &a1, &a2, &b0, &b1, + &b2, &tmp1, &tmp2, NULL)) != MP_OKAY) { + return res; + } + + /* B */ + B = MIN(a->used, b->used) / 3; + + /* a = a2 * B**2 + a1 * B + a0 */ + if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(a, &a1)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a1, B); + if ((res = mp_mod_2d(&a1, DIGIT_BIT * B, &a1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(a, &a2)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a2, B * 2); + + /* b = b2 * B**2 + b1 * B + b0 */ + if ((res = mp_mod_2d(b, DIGIT_BIT * B, &b0)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(b, &b1)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&b1, B); + (void)mp_mod_2d(&b1, DIGIT_BIT * B, &b1); + + if ((res = mp_copy(b, &b2)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&b2, B * 2); + + /* w0 = a0*b0 */ + if ((res = mp_mul(&a0, &b0, &w0)) != MP_OKAY) { + goto ERR; + } + + /* w4 = a2 * b2 */ + if ((res = mp_mul(&a2, &b2, &w4)) != MP_OKAY) { + goto ERR; + } + + /* w1 = (a2 + 2(a1 + 2a0))(b2 + 2(b1 + 2b0)) */ + if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul_2(&b0, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b2, &tmp2)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul(&tmp1, &tmp2, &w1)) != MP_OKAY) { + goto ERR; + } + + /* w3 = (a0 + 2(a1 + 2a2))(b0 + 2(b1 + 2b2)) */ + if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul_2(&b2, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul(&tmp1, &tmp2, &w3)) != MP_OKAY) { + goto ERR; + } + + + /* w2 = (a2 + a1 + a0)(b2 + b1 + b0) */ + if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&b2, &b1, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul(&tmp1, &tmp2, &w2)) != MP_OKAY) { + goto ERR; + } + + /* now solve the matrix + + 0 0 0 0 1 + 1 2 4 8 16 + 1 1 1 1 1 + 16 8 4 2 1 + 1 0 0 0 0 + + using 12 subtractions, 4 shifts, + 2 small divisions and 1 small multiplication + */ + + /* r1 - r4 */ + if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r0 */ + if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/2 */ + if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3/2 */ + if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) { + goto ERR; + } + /* r2 - r0 - r4 */ + if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1 - 8r0 */ + if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - 8r4 */ + if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) { + goto ERR; + } + /* 3r2 - r1 - r3 */ + if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/3 */ + if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) { + goto ERR; + } + /* r3/3 */ + if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) { + goto ERR; + } + + /* at this point shift W[n] by B*n */ + if ((res = mp_lshd(&w1, 1 * B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w2, 2 * B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w3, 3 * B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w4, 4 * B)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_add(&w0, &w1, c)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, c, c)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear_multi(&w0, &w1, &w2, &w3, &w4, + &a0, &a1, &a2, &b0, &b1, + &b2, &tmp1, &tmp2, NULL); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_TOOM_SQR_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* squaring using Toom-Cook 3-way algorithm */ +int +mp_toom_sqr(mp_int *a, mp_int *b) { + mp_int w0, w1, w2, w3, w4, tmp1, a0, a1, a2; + int res, B; + + /* init temps */ + if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL)) != MP_OKAY) { + return res; + } + + /* B */ + B = a->used / 3; + + /* a = a2 * B**2 + a1 * B + a0 */ + if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(a, &a1)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a1, B); + if ((res = mp_mod_2d(&a1, DIGIT_BIT * B, &a1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(a, &a2)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a2, B * 2); + + /* w0 = a0*a0 */ + if ((res = mp_sqr(&a0, &w0)) != MP_OKAY) { + goto ERR; + } + + /* w4 = a2 * a2 */ + if ((res = mp_sqr(&a2, &w4)) != MP_OKAY) { + goto ERR; + } + + /* w1 = (a2 + 2(a1 + 2a0))**2 */ + if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_sqr(&tmp1, &w1)) != MP_OKAY) { + goto ERR; + } + + /* w3 = (a0 + 2(a1 + 2a2))**2 */ + if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_sqr(&tmp1, &w3)) != MP_OKAY) { + goto ERR; + } + + + /* w2 = (a2 + a1 + a0)**2 */ + if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sqr(&tmp1, &w2)) != MP_OKAY) { + goto ERR; + } + + /* now solve the matrix + + 0 0 0 0 1 + 1 2 4 8 16 + 1 1 1 1 1 + 16 8 4 2 1 + 1 0 0 0 0 + + using 12 subtractions, 4 shifts, 2 small divisions and 1 small multiplication. + */ + + /* r1 - r4 */ + if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r0 */ + if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/2 */ + if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3/2 */ + if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) { + goto ERR; + } + /* r2 - r0 - r4 */ + if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1 - 8r0 */ + if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - 8r4 */ + if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) { + goto ERR; + } + /* 3r2 - r1 - r3 */ + if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/3 */ + if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) { + goto ERR; + } + /* r3/3 */ + if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) { + goto ERR; + } + + /* at this point shift W[n] by B*n */ + if ((res = mp_lshd(&w1, 1 * B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w2, 2 * B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w3, 3 * B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w4, 4 * B)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_add(&w0, &w1, b)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, b, b)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_TORADIX_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* stores a bignum as a ASCII string in a given radix (2..64) */ +int mp_toradix(mp_int *a, char *str, int radix) { + int res, digs; + mp_int t; + mp_digit d; + char *_s = str; + + /* check range of the radix */ + if ((radix < 2) || (radix > 64)) { + return MP_VAL; + } + + /* quick out if its zero */ + if (mp_iszero(a) == MP_YES) { + *str++ = '0'; + *str = '\0'; + return MP_OKAY; + } + + if ((res = mp_init_copy(&t, a)) != MP_OKAY) { + return res; + } + + /* if it is negative output a - */ + if (t.sign == MP_NEG) { + ++_s; + *str++ = '-'; + t.sign = MP_ZPOS; + } + + digs = 0; + while (mp_iszero(&t) == MP_NO) { + if ((res = mp_div_d(&t, (mp_digit)radix, &t, &d)) != MP_OKAY) { + mp_clear(&t); + return res; + } + *str++ = mp_s_rmap[d]; + ++digs; + } + + /* reverse the digits of the string. In this case _s points + * to the first digit [exluding the sign] of the number] + */ + bn_reverse((unsigned char *)_s, digs); + + /* append a NULL so the string is properly terminated */ + *str = '\0'; + + mp_clear(&t); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_UNSIGNED_BIN_SIZE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* get the size for an unsigned equivalent */ +int mp_unsigned_bin_size(mp_int *a) { + int size = mp_count_bits(a); + + return (size / 8) + (((size & 7) != 0) ? 1 : 0); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_XOR_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* XOR two ints together */ +int +mp_xor(mp_int *a, mp_int *b, mp_int *c) { + int res, ix, px; + mp_int t, *x; + + if (a->used > b->used) { + if ((res = mp_init_copy(&t, a)) != MP_OKAY) { + return res; + } + px = b->used; + x = b; + } else { + if ((res = mp_init_copy(&t, b)) != MP_OKAY) { + return res; + } + px = a->used; + x = a; + } + + for (ix = 0; ix < px; ix++) { + t.dp[ix] ^= x->dp[ix]; + } + mp_clamp(&t); + mp_exch(c, &t); + mp_clear(&t); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_ZERO_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* set to zero */ +void mp_zero(mp_int *a) { + int n; + mp_digit *tmp; + + a->sign = MP_ZPOS; + a->used = 0; + + tmp = a->dp; + for (n = 0; n < a->alloc; n++) { + *tmp++ = 0; + } +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_PRIME_TAB_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ +const mp_digit ltm_prime_tab[] = { + 0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013, + 0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035, + 0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059, + 0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F, + #ifndef MP_8BIT + 0x0083, + 0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD, + 0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF, + 0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107, + 0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137, + + 0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167, + 0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199, + 0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9, + 0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7, + 0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239, + 0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265, + 0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293, + 0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF, + + 0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, 0x02F5, 0x02F9, 0x0301, + 0x0305, 0x0313, 0x031D, 0x0329, 0x032B, 0x0335, 0x0337, 0x033B, + 0x033D, 0x0347, 0x0355, 0x0359, 0x035B, 0x035F, 0x036D, 0x0371, + 0x0373, 0x0377, 0x038B, 0x038F, 0x0397, 0x03A1, 0x03A9, 0x03AD, + 0x03B3, 0x03B9, 0x03C7, 0x03CB, 0x03D1, 0x03D7, 0x03DF, 0x03E5, + 0x03F1, 0x03F5, 0x03FB, 0x03FD, 0x0407, 0x0409, 0x040F, 0x0419, + 0x041B, 0x0425, 0x0427, 0x042D, 0x043F, 0x0443, 0x0445, 0x0449, + 0x044F, 0x0455, 0x045D, 0x0463, 0x0469, 0x047F, 0x0481, 0x048B, + + 0x0493, 0x049D, 0x04A3, 0x04A9, 0x04B1, 0x04BD, 0x04C1, 0x04C7, + 0x04CD, 0x04CF, 0x04D5, 0x04E1, 0x04EB, 0x04FD, 0x04FF, 0x0503, + 0x0509, 0x050B, 0x0511, 0x0515, 0x0517, 0x051B, 0x0527, 0x0529, + 0x052F, 0x0551, 0x0557, 0x055D, 0x0565, 0x0577, 0x0581, 0x058F, + 0x0593, 0x0595, 0x0599, 0x059F, 0x05A7, 0x05AB, 0x05AD, 0x05B3, + 0x05BF, 0x05C9, 0x05CB, 0x05CF, 0x05D1, 0x05D5, 0x05DB, 0x05E7, + 0x05F3, 0x05FB, 0x0607, 0x060D, 0x0611, 0x0617, 0x061F, 0x0623, + 0x062B, 0x062F, 0x063D, 0x0641, 0x0647, 0x0649, 0x064D, 0x0653 + #endif +}; +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_REVERSE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* reverse an array, used for radix code */ +void +bn_reverse(unsigned char *s, int len) { + int ix, iy; + unsigned char t; + + ix = 0; + iy = len - 1; + while (ix < iy) { + t = s[ix]; + s[ix] = s[iy]; + s[iy] = t; + ++ix; + --iy; + } +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_S_MP_ADD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* low level addition, based on HAC pp.594, Algorithm 14.7 */ +int +s_mp_add(mp_int *a, mp_int *b, mp_int *c) { + mp_int *x; + int olduse, res, min, max; + + /* find sizes, we let |a| <= |b| which means we have to sort + * them. "x" will point to the input with the most digits + */ + if (a->used > b->used) { + min = b->used; + max = a->used; + x = a; + } else { + min = a->used; + max = b->used; + x = b; + } + + /* init result */ + if (c->alloc < (max + 1)) { + if ((res = mp_grow(c, max + 1)) != MP_OKAY) { + return res; + } + } + + /* get old used digit count and set new one */ + olduse = c->used; + c->used = max + 1; + + { + mp_digit u, *tmpa, *tmpb, *tmpc; + int i; + + /* alias for digit pointers */ + + /* first input */ + tmpa = a->dp; + + /* second input */ + tmpb = b->dp; + + /* destination */ + tmpc = c->dp; + + /* zero the carry */ + u = 0; + for (i = 0; i < min; i++) { + /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ + *tmpc = *tmpa++ + *tmpb++ + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, that is in A+B + * if A or B has more digits add those in + */ + if (min != max) { + for ( ; i < max; i++) { + /* T[i] = X[i] + U */ + *tmpc = x->dp[i] + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + } + + /* add carry */ + *tmpc++ = u; + + /* clear digits above oldused */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp(c); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_S_MP_EXPTMOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +int s_mp_exptmod(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int redmode) { + mp_int M[TAB_SIZE], res, mu; + mp_digit buf; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + + int (*redux)(mp_int *, mp_int *, mp_int *); + + /* find window size */ + x = mp_count_bits(X); + if (x <= 7) { + winsize = 2; + } else if (x <= 36) { + winsize = 3; + } else if (x <= 140) { + winsize = 4; + } else if (x <= 450) { + winsize = 5; + } else if (x <= 1303) { + winsize = 6; + } else if (x <= 3529) { + winsize = 7; + } else { + winsize = 8; + } + + #ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } + #endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1 << (winsize - 1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1 << (winsize - 1); y < x; y++) { + mp_clear(&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* create mu, used for Barrett reduction */ + if ((err = mp_init(&mu)) != MP_OKAY) { + goto LBL_M; + } + + if (redmode == 0) { + if ((err = mp_reduce_setup(&mu, P)) != MP_OKAY) { + goto LBL_MU; + } + redux = mp_reduce; + } else { + if ((err = mp_reduce_2k_setup_l(P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + redux = mp_reduce_2k_l; + } + + /* create M table + * + * The M table contains powers of the base, + * e.g. M[x] = G**x mod P + * + * The first half of the table is not + * computed though accept for M[0] and M[1] + */ + if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { + goto LBL_MU; + } + + /* compute the value at M[1<<(winsize-1)] by squaring + * M[1] (winsize-1) times + */ + if ((err = mp_copy(&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_MU; + } + + for (x = 0; x < (winsize - 1); x++) { + /* square it */ + if ((err = mp_sqr(&M[1 << (winsize - 1)], + &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_MU; + } + + /* reduce modulo P */ + if ((err = redux(&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + } + + /* create upper table, that is M[x] = M[x-1] * M[1] (mod P) + * for x = (2**(winsize - 1) + 1) to (2**winsize - 1) + */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul(&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto LBL_MU; + } + if ((err = redux(&M[x], P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + } + + /* setup result */ + if ((err = mp_init(&res)) != MP_OKAY) { + goto LBL_MU; + } + mp_set(&res, 1); + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for ( ; ; ) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits */ + if (digidx == -1) { + break; + } + /* read next digit and reset the bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int)DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if ((mode == 0) && (y == 0)) { + continue; + } + + /* if the bit is zero and mode == 1 then we square */ + if ((mode == 1) && (y == 0)) { + if ((err = mp_sqr(&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr(&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* then multiply */ + if ((err = mp_mul(&res, &M[bitbuf], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + + /* empty window and reset */ + bitcpy = 0; + bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if ((mode == 2) && (bitcpy > 0)) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr(&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul(&res, &M[1], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + } + } + } + + mp_exch(&res, Y); + err = MP_OKAY; +LBL_RES: mp_clear(&res); +LBL_MU: mp_clear(&mu); +LBL_M: + mp_clear(&M[1]); + for (x = 1 << (winsize - 1); x < (1 << winsize); x++) { + mp_clear(&M[x]); + } + return err; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_S_MP_MUL_DIGS_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* multiplies |a| * |b| and only computes upto digs digits of result + * HAC pp. 595, Algorithm 14.12 Modified so you can control how + * many digits of output are created. + */ +int s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs) { + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + /* can we use the fast multiplier? */ + if (((digs) < MP_WARRAY) && + (MIN(a->used, b->used) < + (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { + return fast_s_mp_mul_digs(a, b, c, digs); + } + + if ((res = mp_init_size(&t, digs)) != MP_OKAY) { + return res; + } + t.used = digs; + + /* compute the digits of the product directly */ + pa = a->used; + for (ix = 0; ix < pa; ix++) { + /* set the carry to zero */ + u = 0; + + /* limit ourselves to making digs digits of output */ + pb = MIN(b->used, digs - ix); + + /* setup some aliases */ + /* copy of the digit from a used within the nested loop */ + tmpx = a->dp[ix]; + + /* an alias for the destination shifted ix places */ + tmpt = t.dp + ix; + + /* an alias for the digits of b */ + tmpy = b->dp; + + /* compute the columns of the output and propagate the carry */ + for (iy = 0; iy < pb; iy++) { + /* compute the column as a mp_word */ + r = (mp_word) * tmpt + + ((mp_word)tmpx * (mp_word) * tmpy++) + + (mp_word)u; + + /* the new column is the lower part of the result */ + *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK)); + + /* get the carry word from the result */ + u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + } + /* set carry if it is placed below digs */ + if ((ix + iy) < digs) { + *tmpt = u; + } + } + + mp_clamp(&t); + mp_exch(&t, c); + + mp_clear(&t); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_S_MP_MUL_HIGH_DIGS_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* multiplies |a| * |b| and does not compute the lower digs digits + * [meant to get the higher part of the product] + */ +int +s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs) { + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + /* can we use the fast multiplier? */ + #ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C + if (((a->used + b->used + 1) < MP_WARRAY) && + (MIN(a->used, b->used) < (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { + return fast_s_mp_mul_high_digs(a, b, c, digs); + } + #endif + + if ((res = mp_init_size(&t, a->used + b->used + 1)) != MP_OKAY) { + return res; + } + t.used = a->used + b->used + 1; + + pa = a->used; + pb = b->used; + for (ix = 0; ix < pa; ix++) { + /* clear the carry */ + u = 0; + + /* left hand side of A[ix] * B[iy] */ + tmpx = a->dp[ix]; + + /* alias to the address of where the digits will be stored */ + tmpt = &(t.dp[digs]); + + /* alias for where to read the right hand side from */ + tmpy = b->dp + (digs - ix); + + for (iy = digs - ix; iy < pb; iy++) { + /* calculate the double precision result */ + r = (mp_word) * tmpt + + ((mp_word)tmpx * (mp_word) * tmpy++) + + (mp_word)u; + + /* get the lower part */ + *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK)); + + /* carry the carry */ + u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + } + *tmpt = u; + } + mp_clamp(&t); + mp_exch(&t, c); + mp_clear(&t); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_S_MP_SQR_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */ +int s_mp_sqr(mp_int *a, mp_int *b) { + mp_int t; + int res, ix, iy, pa; + mp_word r; + mp_digit u, tmpx, *tmpt; + + pa = a->used; + if ((res = mp_init_size(&t, (2 * pa) + 1)) != MP_OKAY) { + return res; + } + + /* default used is maximum possible size */ + t.used = (2 * pa) + 1; + + for (ix = 0; ix < pa; ix++) { + /* first calculate the digit at 2*ix */ + /* calculate double precision result */ + r = (mp_word)t.dp[2 * ix] + + ((mp_word)a->dp[ix] * (mp_word)a->dp[ix]); + + /* store lower part in result */ + t.dp[ix + ix] = (mp_digit)(r & ((mp_word)MP_MASK)); + + /* get the carry */ + u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + + /* left hand side of A[ix] * A[iy] */ + tmpx = a->dp[ix]; + + /* alias for where to store the results */ + tmpt = t.dp + ((2 * ix) + 1); + + for (iy = ix + 1; iy < pa; iy++) { + /* first calculate the product */ + r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]); + + /* now calculate the double precision result, note we use + * addition instead of *2 since it's easier to optimize + */ + r = ((mp_word) * tmpt) + r + r + ((mp_word)u); + + /* store lower part */ + *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK)); + + /* get carry */ + u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + } + /* propagate upwards */ + while (u != ((mp_digit)0)) { + r = ((mp_word) * tmpt) + ((mp_word)u); + *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK)); + u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + } + } + + mp_clamp(&t); + mp_exch(&t, b); + mp_clear(&t); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_S_MP_SUB_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */ +int +s_mp_sub(mp_int *a, mp_int *b, mp_int *c) { + int olduse, res, min, max; + + /* find sizes */ + min = b->used; + max = a->used; + + /* init result */ + if (c->alloc < max) { + if ((res = mp_grow(c, max)) != MP_OKAY) { + return res; + } + } + olduse = c->used; + c->used = max; + + { + mp_digit u, *tmpa, *tmpb, *tmpc; + int i; + + /* alias for digit pointers */ + tmpa = a->dp; + tmpb = b->dp; + tmpc = c->dp; + + /* set carry to zero */ + u = 0; + for (i = 0; i < min; i++) { + /* T[i] = A[i] - B[i] - U */ + *tmpc = (*tmpa++ - *tmpb++) - u; + + /* U = carry bit of T[i] + * Note this saves performing an AND operation since + * if a carry does occur it will propagate all the way to the + * MSB. As a result a single shift is enough to get the carry + */ + u = *tmpc >> ((mp_digit)((CHAR_BIT * sizeof(mp_digit)) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, e.g. if A has more digits than B */ + for ( ; i < max; i++) { + /* T[i] = A[i] - U */ + *tmpc = *tmpa++ - u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)((CHAR_BIT * sizeof(mp_digit)) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* clear digits above used (since we may not have grown result above) */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp(c); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BNCORE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* Known optimal configurations + + CPU /Compiler /MUL CUTOFF/SQR CUTOFF + ------------------------------------------------------------- + Intel P4 Northwood /GCC v3.4.1 / 88/ 128/LTM 0.32 ;-) + AMD Athlon64 /GCC v3.4.4 / 80/ 120/LTM 0.35 + + */ + +int KARATSUBA_MUL_CUTOFF = 80, /* Min. number of digits before Karatsuba multiplication is used. */ + KARATSUBA_SQR_CUTOFF = 120, /* Min. number of digits before Karatsuba squaring is used. */ + + TOOM_MUL_CUTOFF = 350, /* no optimal values of these are known yet so set em high */ + TOOM_SQR_CUTOFF = 400; +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file crypt.c + Build strings, Tom St Denis + */ + +const char *crypt_build_settings = + "LibTomCrypt ""1.17"" (Tom St Denis, tomstdenis@gmail.com)\n" + "LibTomCrypt is public domain software.\n" + "Built on " __DATE__ " at " __TIME__ "\n\n\n" + "Endianess: " +#if defined(ENDIAN_NEUTRAL) + "neutral\n" +#elif defined(ENDIAN_LITTLE) + "little" + #if defined(ENDIAN_32BITWORD) + " (32-bit words)\n" + #else + " (64-bit words)\n" + #endif +#elif defined(ENDIAN_BIG) + "big" + #if defined(ENDIAN_32BITWORD) + " (32-bit words)\n" + #else + " (64-bit words)\n" + #endif +#endif + "Clean stack: " +#if defined(LTC_CLEAN_STACK) + "enabled\n" +#else + "disabled\n" +#endif + "Ciphers built-in:\n" +#if defined(LTC_BLOWFISH) + " Blowfish\n" +#endif +#if defined(LTC_RC2) + " LTC_RC2\n" +#endif +#if defined(LTC_RC5) + " LTC_RC5\n" +#endif +#if defined(LTC_RC6) + " LTC_RC6\n" +#endif +#if defined(LTC_SAFERP) + " Safer+\n" +#endif +#if defined(LTC_SAFER) + " Safer\n" +#endif +#if defined(LTC_RIJNDAEL) + " Rijndael\n" +#endif +#if defined(LTC_XTEA) + " LTC_XTEA\n" +#endif +#if defined(LTC_TWOFISH) + " Twofish " + #if defined(LTC_TWOFISH_SMALL) && defined(LTC_TWOFISH_TABLES) && defined(LTC_TWOFISH_ALL_TABLES) + "(small, tables, all_tables)\n" + #elif defined(LTC_TWOFISH_SMALL) && defined(LTC_TWOFISH_TABLES) + "(small, tables)\n" + #elif defined(LTC_TWOFISH_SMALL) && defined(LTC_TWOFISH_ALL_TABLES) + "(small, all_tables)\n" + #elif defined(LTC_TWOFISH_TABLES) && defined(LTC_TWOFISH_ALL_TABLES) + "(tables, all_tables)\n" + #elif defined(LTC_TWOFISH_SMALL) + "(small)\n" + #elif defined(LTC_TWOFISH_TABLES) + "(tables)\n" + #elif defined(LTC_TWOFISH_ALL_TABLES) + "(all_tables)\n" + #else + "\n" + #endif +#endif +#if defined(LTC_DES) + " LTC_DES\n" +#endif +#if defined(LTC_CAST5) + " LTC_CAST5\n" +#endif +#if defined(LTC_NOEKEON) + " Noekeon\n" +#endif +#if defined(LTC_SKIPJACK) + " Skipjack\n" +#endif +#if defined(LTC_KHAZAD) + " Khazad\n" +#endif +#if defined(LTC_ANUBIS) + " Anubis " +#endif +#if defined(LTC_ANUBIS_TWEAK) + " (tweaked)" +#endif + "\n" +#if defined(LTC_KSEED) + " LTC_KSEED\n" +#endif +#if defined(LTC_KASUMI) + " KASUMI\n" +#endif + + "\nHashes built-in:\n" +#if defined(LTC_SHA512) + " LTC_SHA-512\n" +#endif +#if defined(LTC_SHA384) + " LTC_SHA-384\n" +#endif +#if defined(LTC_SHA256) + " LTC_SHA-256\n" +#endif +#if defined(LTC_SHA224) + " LTC_SHA-224\n" +#endif +#if defined(LTC_TIGER) + " LTC_TIGER\n" +#endif +#if defined(LTC_SHA1) + " LTC_SHA1\n" +#endif +#if defined(LTC_MD5) + " LTC_MD5\n" +#endif +#if defined(LTC_MD4) + " LTC_MD4\n" +#endif +#if defined(LTC_MD2) + " LTC_MD2\n" +#endif +#if defined(LTC_RIPEMD128) + " LTC_RIPEMD128\n" +#endif +#if defined(LTC_RIPEMD160) + " LTC_RIPEMD160\n" +#endif +#if defined(LTC_RIPEMD256) + " LTC_RIPEMD256\n" +#endif +#if defined(LTC_RIPEMD320) + " LTC_RIPEMD320\n" +#endif +#if defined(LTC_WHIRLPOOL) + " LTC_WHIRLPOOL\n" +#endif +#if defined(LTC_CHC_HASH) + " LTC_CHC_HASH \n" +#endif + + "\nBlock Chaining Modes:\n" +#if defined(LTC_CFB_MODE) + " CFB\n" +#endif +#if defined(LTC_OFB_MODE) + " OFB\n" +#endif +#if defined(LTC_ECB_MODE) + " ECB\n" +#endif +#if defined(LTC_CBC_MODE) + " CBC\n" +#endif +#if defined(LTC_CTR_MODE) + " CTR " +#endif +#if defined(LTC_CTR_OLD) + " (CTR_OLD) " +#endif + "\n" +#if defined(LRW_MODE) + " LRW_MODE" + #if defined(LRW_TABLES) + " (LRW_TABLES) " + #endif + "\n" +#endif +#if defined(LTC_F8_MODE) + " F8 MODE\n" +#endif +#if defined(LTC_XTS_MODE) + " LTC_XTS_MODE\n" +#endif + + "\nMACs:\n" +#if defined(LTC_HMAC) + " LTC_HMAC\n" +#endif +#if defined(LTC_OMAC) + " LTC_OMAC\n" +#endif +#if defined(LTC_PMAC) + " PMAC\n" +#endif +#if defined(LTC_PELICAN) + " LTC_PELICAN\n" +#endif +#if defined(LTC_XCBC) + " XCBC-MAC\n" +#endif +#if defined(LTC_F9_MODE) + " F9-MAC\n" +#endif + + "\nENC + AUTH modes:\n" +#if defined(LTC_EAX_MODE) + " LTC_EAX_MODE\n" +#endif +#if defined(LTC_OCB_MODE) + " LTC_OCB_MODE\n" +#endif +#if defined(LTC_CCM_MODE) + " LTC_CCM_MODE\n" +#endif +#if defined(LTC_GCM_MODE) + " LTC_GCM_MODE " +#endif +#if defined(LTC_GCM_TABLES) + " (LTC_GCM_TABLES) " +#endif + "\n" + + "\nPRNG:\n" +#if defined(LTC_YARROW) + " Yarrow\n" +#endif +#if defined(LTC_SPRNG) + " LTC_SPRNG\n" +#endif +#if defined(LTC_RC4) + " LTC_RC4\n" +#endif +#if defined(LTC_FORTUNA) + " Fortuna\n" +#endif +#if defined(LTC_SOBER128) + " LTC_SOBER128\n" +#endif + + "\nPK Algs:\n" +#if defined(LTC_MRSA) + " RSA \n" +#endif +#if defined(LTC_MECC) + " ECC\n" +#endif +#if defined(LTC_MDSA) + " DSA\n" +#endif +#if defined(MKAT) + " Katja\n" +#endif + + "\nCompiler:\n" +#if defined(WIN32) + " WIN32 platform detected.\n" +#endif +#if defined(__CYGWIN__) + " CYGWIN Detected.\n" +#endif +#if defined(__DJGPP__) + " DJGPP Detected.\n" +#endif +#if defined(_MSC_VER) + " MSVC compiler detected.\n" +#endif +#if defined(__GNUC__) + " GCC compiler detected.\n" +#endif +#if defined(INTEL_CC) + " Intel C Compiler detected.\n" +#endif +#if defined(__x86_64__) + " x86-64 detected.\n" +#endif +#if defined(LTC_PPC32) + " LTC_PPC32 defined \n" +#endif + + "\nVarious others: " +#if defined(LTC_BASE64) + " LTC_BASE64 " +#endif +#if defined(MPI) + " MPI " +#endif +#if defined(TRY_UNRANDOM_FIRST) + " TRY_UNRANDOM_FIRST " +#endif +#if defined(LTC_TEST) + " LTC_TEST " +#endif +#if defined(LTC_PKCS_1) + " LTC_PKCS#1 " +#endif +#if defined(LTC_PKCS_5) + " LTC_PKCS#5 " +#endif +#if defined(LTC_SMALL_CODE) + " LTC_SMALL_CODE " +#endif +#if defined(LTC_NO_FILE) + " LTC_NO_FILE " +#endif +#if defined(LTC_DER) + " LTC_DER " +#endif +#if defined(LTC_FAST) + " LTC_FAST " +#endif +#if defined(LTC_NO_FAST) + " LTC_NO_FAST " +#endif +#if defined(LTC_NO_BSWAP) + " LTC_NO_BSWAP " +#endif +#if defined(LTC_NO_ASM) + " LTC_NO_ASM " +#endif +#if defined(LTC_NO_TEST) + " LTC_NO_TEST " +#endif +#if defined(LTC_NO_TABLES) + " LTC_NO_TABLES " +#endif +#if defined(LTC_PTHREAD) + " LTC_PTHREAD " +#endif +#if defined(LTM_LTC_DESC) + " LTM_DESC " +#endif +#if defined(TFM_LTC_DESC) + " TFM_DESC " +#endif +#if defined(LTC_MECC_ACCEL) + " LTC_MECC_ACCEL " +#endif +#if defined(GMP_LTC_DESC) + " GMP_DESC " +#endif +#if defined(LTC_EASY) + " (easy) " +#endif +#if defined(LTC_MECC_FP) + " LTC_MECC_FP " +#endif +#if defined(LTC_ECC_SHAMIR) + " LTC_ECC_SHAMIR " +#endif + "\n" + "\n\n\n" +; + + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt.c,v $ */ +/* $Revision: 1.36 $ */ +/* $Date: 2007/05/12 14:46:12 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include + +/** + @file crypt_argchk.c + Perform argument checking, Tom St Denis + */ + +#if (ARGTYPE == 0) +void crypt_argchk(char *v, char *s, int d) { + fprintf(stderr, "LTC_ARGCHK '%s' failure on line %d of file %s\n", + v, d, s); + (void)raise(SIGABRT); +} +#endif + +#ifndef TOMCRYPT_H_ +#define TOMCRYPT_H_ +#define USE_LTM +#define LTM_DESC +#define LTC_SHA1 +#include +#include +#include +#include +#include +#include +#include + +/* use configuration data */ +#ifndef TOMCRYPT_CUSTOM_H_ +#define TOMCRYPT_CUSTOM_H_ + +/* macros for various libc functions you can change for embedded targets */ +#ifndef XMALLOC + #ifdef malloc + #define LTC_NO_PROTOTYPES + #endif + #define XMALLOC malloc +#endif +#ifndef XREALLOC + #ifdef realloc + #define LTC_NO_PROTOTYPES + #endif + #define XREALLOC realloc +#endif +#ifndef XCALLOC + #ifdef calloc + #define LTC_NO_PROTOTYPES + #endif + #define XCALLOC calloc +#endif +#ifndef XFREE + #ifdef free + #define LTC_NO_PROTOTYPES + #endif + #define XFREE free +#endif + +#ifndef XMEMSET + #ifdef memset + #define LTC_NO_PROTOTYPES + #endif + #define XMEMSET memset +#endif +#ifndef XMEMCPY + #ifdef memcpy + #define LTC_NO_PROTOTYPES + #endif + #define XMEMCPY memcpy +#endif +#ifndef XMEMCMP + #ifdef memcmp + #define LTC_NO_PROTOTYPES + #endif + #define XMEMCMP memcmp +#endif +#ifndef XSTRCMP + #ifdef strcmp + #define LTC_NO_PROTOTYPES + #endif + #define XSTRCMP strcmp +#endif + +#ifndef XCLOCK + #define XCLOCK clock +#endif +#ifndef XCLOCKS_PER_SEC + #define XCLOCKS_PER_SEC CLOCKS_PER_SEC +#endif + +#ifndef XQSORT + #ifdef qsort + #define LTC_NO_PROTOTYPES + #endif + #define XQSORT qsort +#endif + +/* Easy button? */ +#ifdef LTC_EASY + #define LTC_NO_CIPHERS + #define LTC_RIJNDAEL + #define LTC_BLOWFISH + #define LTC_DES + #define LTC_CAST5 + + #define LTC_NO_MODES + #define LTC_ECB_MODE + #define LTC_CBC_MODE + #define LTC_CTR_MODE + + #define LTC_NO_HASHES + #define LTC_SHA1 + #define LTC_SHA512 + #define LTC_SHA384 + #define LTC_SHA256 + #define LTC_SHA224 + + #define LTC_NO_MACS + #define LTC_HMAC + #define LTC_OMAC + #define LTC_CCM_MODE + + #define LTC_NO_PRNGS + #define LTC_SPRNG + #define LTC_YARROW + #define LTC_DEVRANDOM + #define TRY_URANDOM_FIRST + + #define LTC_NO_PK + #define LTC_MRSA + #define LTC_MECC +#endif + +/* Use small code where possible */ +/* #define LTC_SMALL_CODE */ + +/* Enable self-test test vector checking */ +#ifndef LTC_NO_TEST + #define LTC_TEST +#endif + +/* clean the stack of functions which put private information on stack */ +/* #define LTC_CLEAN_STACK */ + +/* disable all file related functions */ +/* #define LTC_NO_FILE */ + +/* disable all forms of ASM */ +/* #define LTC_NO_ASM */ + +/* disable FAST mode */ +/* #define LTC_NO_FAST */ + +/* disable BSWAP on x86 */ +/* #define LTC_NO_BSWAP */ + +/* ---> Symmetric Block Ciphers <--- */ +#ifndef LTC_NO_CIPHERS + + #define LTC_BLOWFISH + #define LTC_RC2 + #define LTC_RC5 + #define LTC_RC6 + #define LTC_SAFERP + #define LTC_RIJNDAEL + #define LTC_XTEA + +/* _TABLES tells it to use tables during setup, _SMALL means to use the smaller scheduled key format + * (saves 4KB of ram), _ALL_TABLES enables all tables during setup */ + #define LTC_TWOFISH + #ifndef LTC_NO_TABLES + #define LTC_TWOFISH_TABLES +/* #define LTC_TWOFISH_ALL_TABLES */ + #else + #define LTC_TWOFISH_SMALL + #endif +/* #define LTC_TWOFISH_SMALL */ +/* LTC_DES includes EDE triple-LTC_DES */ + #define LTC_DES + #define LTC_CAST5 + #define LTC_NOEKEON + #define LTC_SKIPJACK + #define LTC_SAFER + #define LTC_KHAZAD + #define LTC_ANUBIS + #define LTC_ANUBIS_TWEAK + #define LTC_KSEED + #define LTC_KASUMI +#endif /* LTC_NO_CIPHERS */ + + +/* ---> Block Cipher Modes of Operation <--- */ +#ifndef LTC_NO_MODES + + #define LTC_CFB_MODE + #define LTC_OFB_MODE + #define LTC_ECB_MODE + #define LTC_CBC_MODE + #define LTC_CTR_MODE + +/* F8 chaining mode */ + #define LTC_F8_MODE + +/* LRW mode */ + #define LTC_LRW_MODE + #ifndef LTC_NO_TABLES + +/* like GCM mode this will enable 16 8x128 tables [64KB] that make + * seeking very fast. + */ + #define LRW_TABLES + #endif + +/* XTS mode */ + #define LTC_XTS_MODE +#endif /* LTC_NO_MODES */ + +/* ---> One-Way Hash Functions <--- */ +#ifndef LTC_NO_HASHES + + #define LTC_CHC_HASH + #define LTC_WHIRLPOOL + #define LTC_SHA512 + #define LTC_SHA384 + #define LTC_SHA256 + #define LTC_SHA224 + #define LTC_TIGER + #define LTC_SHA1 + #define LTC_MD5 + #define LTC_MD4 + #define LTC_MD2 + #define LTC_RIPEMD128 + #define LTC_RIPEMD160 + #define LTC_RIPEMD256 + #define LTC_RIPEMD320 +#endif /* LTC_NO_HASHES */ + +/* ---> MAC functions <--- */ +#ifndef LTC_NO_MACS + + #define LTC_HMAC + #define LTC_OMAC + #define LTC_PMAC + #define LTC_XCBC + #define LTC_F9_MODE + #define LTC_PELICAN + + #if defined(LTC_PELICAN) && !defined(LTC_RIJNDAEL) + #error Pelican-MAC requires LTC_RIJNDAEL + #endif + +/* ---> Encrypt + Authenticate Modes <--- */ + + #define LTC_EAX_MODE + #if defined(LTC_EAX_MODE) && !(defined(LTC_CTR_MODE) && defined(LTC_OMAC)) + #error LTC_EAX_MODE requires CTR and LTC_OMAC mode + #endif + + #define LTC_OCB_MODE + #define LTC_CCM_MODE + #define LTC_GCM_MODE + +/* Use 64KiB tables */ + #ifndef LTC_NO_TABLES + #define LTC_GCM_TABLES + #endif + +/* USE SSE2? requires GCC works on x86_32 and x86_64*/ + #ifdef LTC_GCM_TABLES +/* #define LTC_GCM_TABLES_SSE2 */ + #endif +#endif /* LTC_NO_MACS */ + +/* Various tidbits of modern neatoness */ +#define LTC_BASE64 + +/* --> Pseudo Random Number Generators <--- */ +#ifndef LTC_NO_PRNGS + +/* Yarrow */ + #define LTC_YARROW +/* which descriptor of AES to use? */ +/* 0 = rijndael_enc 1 = aes_enc, 2 = rijndael [full], 3 = aes [full] */ + #define LTC_YARROW_AES 0 + + #if defined(LTC_YARROW) && !defined(LTC_CTR_MODE) + #error LTC_YARROW requires LTC_CTR_MODE chaining mode to be defined! + #endif + +/* a PRNG that simply reads from an available system source */ + #define LTC_SPRNG + +/* The LTC_RC4 stream cipher */ + #define LTC_RC4 + +/* Fortuna PRNG */ + #define LTC_FORTUNA +/* reseed every N calls to the read function */ + #define LTC_FORTUNA_WD 10 +/* number of pools (4..32) can save a bit of ram by lowering the count */ + #define LTC_FORTUNA_POOLS 32 + +/* Greg's LTC_SOBER128 PRNG ;-0 */ + #define LTC_SOBER128 + +/* the *nix style /dev/random device */ + #define LTC_DEVRANDOM +/* try /dev/urandom before trying /dev/random */ + #define TRY_URANDOM_FIRST +#endif /* LTC_NO_PRNGS */ + +/* ---> math provider? <--- */ +#ifndef LTC_NO_MATH + +/* LibTomMath */ +/* #define LTM_LTC_DESC */ + +/* TomsFastMath */ +/* #define TFM_LTC_DESC */ +#endif /* LTC_NO_MATH */ + +/* ---> Public Key Crypto <--- */ +#ifndef LTC_NO_PK + +/* Include RSA support */ + #define LTC_MRSA + +/* Include Katja (a Rabin variant like RSA) */ +/* #define MKAT */ + +/* Digital Signature Algorithm */ + #define LTC_MDSA + +/* ECC */ + #define LTC_MECC + +/* use Shamir's trick for point mul (speeds up signature verification) */ + #define LTC_ECC_SHAMIR + + #if defined(TFM_LTC_DESC) && defined(LTC_MECC) + #define LTC_MECC_ACCEL + #endif + +/* do we want fixed point ECC */ +/* #define LTC_MECC_FP */ + +/* Timing Resistant? */ +/* #define LTC_ECC_TIMING_RESISTANT */ +#endif /* LTC_NO_PK */ + +/* LTC_PKCS #1 (RSA) and #5 (Password Handling) stuff */ +#ifndef LTC_NO_PKCS + + #define LTC_PKCS_1 + #define LTC_PKCS_5 + +/* Include ASN.1 DER (required by DSA/RSA) */ + #define LTC_DER +#endif /* LTC_NO_PKCS */ + +/* cleanup */ + +#ifdef LTC_MECC +/* Supported ECC Key Sizes */ + #ifndef LTC_NO_CURVES + #define ECC112 + #define ECC128 + #define ECC160 + #define ECC192 + #define ECC224 + #define ECC256 + #define ECC384 + #define ECC521 + #endif +#endif + +#if defined(LTC_MECC) || defined(LTC_MRSA) || defined(LTC_MDSA) || defined(MKATJA) +/* Include the MPI functionality? (required by the PK algorithms) */ + #define MPI +#endif + +#ifdef LTC_MRSA + #define LTC_PKCS_1 +#endif + +#if defined(LTC_DER) && !defined(MPI) + #error ASN.1 DER requires MPI functionality +#endif + +#if (defined(LTC_MDSA) || defined(LTC_MRSA) || defined(LTC_MECC) || defined(MKATJA)) && !defined(LTC_DER) + #error PK requires ASN.1 DER functionality, make sure LTC_DER is enabled +#endif + +/* THREAD management */ +#ifdef LTC_PTHREAD + + #include + + #define LTC_MUTEX_GLOBAL(x) pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER; + #define LTC_MUTEX_PROTO(x) extern pthread_mutex_t x; + #define LTC_MUTEX_TYPE(x) pthread_mutex_t x; + #define LTC_MUTEX_INIT(x) pthread_mutex_init(x, NULL); + #define LTC_MUTEX_LOCK(x) pthread_mutex_lock(x); + #define LTC_MUTEX_UNLOCK(x) pthread_mutex_unlock(x); + +#else + +/* default no functions */ + #define LTC_MUTEX_GLOBAL(x) + #define LTC_MUTEX_PROTO(x) + #define LTC_MUTEX_TYPE(x) + #define LTC_MUTEX_INIT(x) + #define LTC_MUTEX_LOCK(x) + #define LTC_MUTEX_UNLOCK(x) +#endif + +/* Debuggers */ + +/* define this if you use Valgrind, note: it CHANGES the way SOBER-128 and LTC_RC4 work (see the code) */ +/* #define LTC_VALGRIND */ +#endif + + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_custom.h,v $ */ +/* $Revision: 1.73 $ */ +/* $Date: 2007/05/12 14:37:41 $ */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* version */ +#define CRYPT 0x0117 +#define SCRYPT "1.17" + +/* max size of either a cipher/hash block or symmetric key [largest of the two] */ +#define MAXBLOCKSIZE 128 + +/* descriptor table size */ + +/* error codes [will be expanded in future releases] */ +enum { + CRYPT_OK=0, /* Result OK */ + CRYPT_ERROR, /* Generic Error */ + CRYPT_NOP, /* Not a failure but no operation was performed */ + + CRYPT_INVALID_KEYSIZE, /* Invalid key size given */ + CRYPT_INVALID_ROUNDS, /* Invalid number of rounds */ + CRYPT_FAIL_TESTVECTOR, /* Algorithm failed test vectors */ + + CRYPT_BUFFER_OVERFLOW, /* Not enough space for output */ + CRYPT_INVALID_PACKET, /* Invalid input packet given */ + + CRYPT_INVALID_PRNGSIZE, /* Invalid number of bits for a PRNG */ + CRYPT_ERROR_READPRNG, /* Could not read enough from PRNG */ + + CRYPT_INVALID_CIPHER, /* Invalid cipher specified */ + CRYPT_INVALID_HASH, /* Invalid hash specified */ + CRYPT_INVALID_PRNG, /* Invalid PRNG specified */ + + CRYPT_MEM, /* Out of memory */ + + CRYPT_PK_TYPE_MISMATCH, /* Not equivalent types of PK keys */ + CRYPT_PK_NOT_PRIVATE, /* Requires a private PK key */ + + CRYPT_INVALID_ARG, /* Generic invalid argument */ + CRYPT_FILE_NOTFOUND, /* File Not Found */ + + CRYPT_PK_INVALID_TYPE, /* Invalid type of PK key */ + CRYPT_PK_INVALID_SYSTEM, /* Invalid PK system specified */ + CRYPT_PK_DUP, /* Duplicate key already in key ring */ + CRYPT_PK_NOT_FOUND, /* Key not found in keyring */ + CRYPT_PK_INVALID_SIZE, /* Invalid size input for PK parameters */ + + CRYPT_INVALID_PRIME_SIZE, /* Invalid size of prime requested */ + CRYPT_PK_INVALID_PADDING /* Invalid padding on input */ +}; + +/* This is the build config file. + * + * With this you can setup what to inlcude/exclude automatically during any build. Just comment + * out the line that #define's the word for the thing you want to remove. phew! + */ + +#ifndef TOMCRYPT_CFG_H +#define TOMCRYPT_CFG_H + +#if defined(_WIN32) || defined(_MSC_VER) + #define LTC_CALL __cdecl +#else + #ifndef LTC_CALL + #define LTC_CALL + #endif +#endif + +#ifndef LTC_EXPORT + #define LTC_EXPORT +#endif + +/* certain platforms use macros for these, making the prototypes broken */ +#ifndef LTC_NO_PROTOTYPES + +/* you can change how memory allocation works ... */ +LTC_EXPORT void *LTC_CALL XMALLOC(size_t n); +LTC_EXPORT void *LTC_CALL XREALLOC(void *p, size_t n); +LTC_EXPORT void *LTC_CALL XCALLOC(size_t n, size_t s); +LTC_EXPORT void LTC_CALL XFREE(void *p); + +LTC_EXPORT void LTC_CALL XQSORT(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); + + +/* change the clock function too */ +LTC_EXPORT clock_t LTC_CALL XCLOCK(void); + +/* various other functions */ +LTC_EXPORT void *LTC_CALL XMEMCPY(void *dest, const void *src, size_t n); +LTC_EXPORT int LTC_CALL XMEMCMP(const void *s1, const void *s2, size_t n); +LTC_EXPORT void *LTC_CALL XMEMSET(void *s, int c, size_t n); + +LTC_EXPORT int LTC_CALL XSTRCMP(const char *s1, const char *s2); +#endif + +/* type of argument checking, 0=default, 1=fatal and 2=error+continue, 3=nothing */ +#ifndef ARGTYPE + #define ARGTYPE 0 +#endif + +/* Controls endianess and size of registers. Leave uncommented to get platform neutral [slower] code + * + * Note: in order to use the optimized macros your platform must support unaligned 32 and 64 bit read/writes. + * The x86 platforms allow this but some others [ARM for instance] do not. On those platforms you **MUST** + * use the portable [slower] macros. + */ + +/* detect x86-32 machines somewhat */ +#if !defined(__STRICT_ANSI__) && (defined(INTEL_CC) || (defined(_MSC_VER) && defined(WIN32)) || (defined(__GNUC__) && (defined(__DJGPP__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__i386__)))) + #define ENDIAN_LITTLE + #define ENDIAN_32BITWORD + #define LTC_FAST + #define LTC_FAST_TYPE unsigned long +#endif + +/* detects MIPS R5900 processors (PS2) */ +#if (defined(__R5900) || defined(R5900) || defined(__R5900__)) && (defined(_mips) || defined(__mips__) || defined(mips)) + #define ENDIAN_LITTLE + #define ENDIAN_64BITWORD +#endif + +/* detect amd64 */ +#if !defined(__STRICT_ANSI__) && defined(__x86_64__) + #define ENDIAN_LITTLE + #define ENDIAN_64BITWORD + #define LTC_FAST + #define LTC_FAST_TYPE unsigned long +#endif + +/* detect PPC32 */ +#if !defined(__STRICT_ANSI__) && defined(LTC_PPC32) + #define ENDIAN_BIG + #define ENDIAN_32BITWORD + #define LTC_FAST + #define LTC_FAST_TYPE unsigned long +#endif + +/* detect sparc and sparc64 */ +#if defined(__sparc__) + #define ENDIAN_BIG + #if defined(__arch64__) + #define ENDIAN_64BITWORD + #else + #define ENDIAN_32BITWORD + #endif +#endif + + +#ifdef LTC_NO_FAST + #ifdef LTC_FAST + #undef LTC_FAST + #endif +#endif + +/* No asm is a quick way to disable anything "not portable" */ +#ifdef LTC_NO_ASM + #undef ENDIAN_LITTLE + #undef ENDIAN_BIG + #undef ENDIAN_32BITWORD + #undef ENDIAN_64BITWORD + #undef LTC_FAST + #undef LTC_FAST_TYPE + #define LTC_NO_ROLC + #define LTC_NO_BSWAP +#endif + +/* #define ENDIAN_LITTLE */ +/* #define ENDIAN_BIG */ + +/* #define ENDIAN_32BITWORD */ +/* #define ENDIAN_64BITWORD */ + +#if (defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE)) && !(defined(ENDIAN_32BITWORD) || defined(ENDIAN_64BITWORD)) + #error You must specify a word size as well as endianess in tomcrypt_cfg.h +#endif + +#if !(defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE)) + #define ENDIAN_NEUTRAL +#endif +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_cfg.h,v $ */ +/* $Revision: 1.19 $ */ +/* $Date: 2006/12/04 02:19:48 $ */ + +/* fix for MSVC ...evil! */ +#ifdef _MSC_VER + #define CONST64(n) n ## ui64 +typedef unsigned __int64 ulong64; +#else + #define CONST64(n) n ## ULL +typedef unsigned long long ulong64; +#endif + +/* this is the "32-bit at least" data type + * Re-define it to suit your platform but it must be at least 32-bits + */ +#if defined(__x86_64__) || (defined(__sparc__) && defined(__arch64__)) +typedef unsigned ulong32; +#else +typedef unsigned long ulong32; +#endif + +/* ---- HELPER MACROS ---- */ +#ifdef ENDIAN_NEUTRAL + + #define STORE32L(x, y) \ + { (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \ + (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); } + + #define LOAD32L(x, y) \ + { x = ((unsigned long)((y)[3] & 255) << 24) | \ + ((unsigned long)((y)[2] & 255) << 16) | \ + ((unsigned long)((y)[1] & 255) << 8) | \ + ((unsigned long)((y)[0] & 255)); } + + #define STORE64L(x, y) \ + { (y)[7] = (unsigned char)(((x) >> 56) & 255); (y)[6] = (unsigned char)(((x) >> 48) & 255); \ + (y)[5] = (unsigned char)(((x) >> 40) & 255); (y)[4] = (unsigned char)(((x) >> 32) & 255); \ + (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \ + (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); } + + #define LOAD64L(x, y) \ + { x = (((ulong64)((y)[7] & 255)) << 56) | (((ulong64)((y)[6] & 255)) << 48) | \ + (((ulong64)((y)[5] & 255)) << 40) | (((ulong64)((y)[4] & 255)) << 32) | \ + (((ulong64)((y)[3] & 255)) << 24) | (((ulong64)((y)[2] & 255)) << 16) | \ + (((ulong64)((y)[1] & 255)) << 8) | (((ulong64)((y)[0] & 255))); } + + #define STORE32H(x, y) \ + { (y)[0] = (unsigned char)(((x) >> 24) & 255); (y)[1] = (unsigned char)(((x) >> 16) & 255); \ + (y)[2] = (unsigned char)(((x) >> 8) & 255); (y)[3] = (unsigned char)((x) & 255); } + + #define LOAD32H(x, y) \ + { x = ((unsigned long)((y)[0] & 255) << 24) | \ + ((unsigned long)((y)[1] & 255) << 16) | \ + ((unsigned long)((y)[2] & 255) << 8) | \ + ((unsigned long)((y)[3] & 255)); } + + #define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x) >> 56) & 255); (y)[1] = (unsigned char)(((x) >> 48) & 255); \ + (y)[2] = (unsigned char)(((x) >> 40) & 255); (y)[3] = (unsigned char)(((x) >> 32) & 255); \ + (y)[4] = (unsigned char)(((x) >> 24) & 255); (y)[5] = (unsigned char)(((x) >> 16) & 255); \ + (y)[6] = (unsigned char)(((x) >> 8) & 255); (y)[7] = (unsigned char)((x) & 255); } + + #define LOAD64H(x, y) \ + { x = (((ulong64)((y)[0] & 255)) << 56) | (((ulong64)((y)[1] & 255)) << 48) | \ + (((ulong64)((y)[2] & 255)) << 40) | (((ulong64)((y)[3] & 255)) << 32) | \ + (((ulong64)((y)[4] & 255)) << 24) | (((ulong64)((y)[5] & 255)) << 16) | \ + (((ulong64)((y)[6] & 255)) << 8) | (((ulong64)((y)[7] & 255))); } +#endif /* ENDIAN_NEUTRAL */ + +#ifdef ENDIAN_LITTLE + + #if !defined(LTC_NO_BSWAP) && (defined(INTEL_CC) || (defined(__GNUC__) && (defined(__DJGPP__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__i386__) || defined(__x86_64__)))) + + #define STORE32H(x, y) \ + asm __volatile__ ( \ + "bswapl %0 \n\t" \ + "movl %0,(%1)\n\t" \ + "bswapl %0 \n\t" \ + ::"r" (x), "r" (y)); + + #define LOAD32H(x, y) \ + asm __volatile__ ( \ + "movl (%1),%0\n\t" \ + "bswapl %0\n\t" \ + : "=r" (x) : "r" (y)); + + #else + + #define STORE32H(x, y) \ + { (y)[0] = (unsigned char)(((x) >> 24) & 255); (y)[1] = (unsigned char)(((x) >> 16) & 255); \ + (y)[2] = (unsigned char)(((x) >> 8) & 255); (y)[3] = (unsigned char)((x) & 255); } + + #define LOAD32H(x, y) \ + { x = ((unsigned long)((y)[0] & 255) << 24) | \ + ((unsigned long)((y)[1] & 255) << 16) | \ + ((unsigned long)((y)[2] & 255) << 8) | \ + ((unsigned long)((y)[3] & 255)); } + #endif + + +/* x86_64 processor */ + #if !defined(LTC_NO_BSWAP) && (defined(__GNUC__) && defined(__x86_64__)) + + #define STORE64H(x, y) \ + asm __volatile__ ( \ + "bswapq %0 \n\t" \ + "movq %0,(%1)\n\t" \ + "bswapq %0 \n\t" \ + ::"r" (x), "r" (y)); + + #define LOAD64H(x, y) \ + asm __volatile__ ( \ + "movq (%1),%0\n\t" \ + "bswapq %0\n\t" \ + : "=r" (x) : "r" (y)); + + #else + + #define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x) >> 56) & 255); (y)[1] = (unsigned char)(((x) >> 48) & 255); \ + (y)[2] = (unsigned char)(((x) >> 40) & 255); (y)[3] = (unsigned char)(((x) >> 32) & 255); \ + (y)[4] = (unsigned char)(((x) >> 24) & 255); (y)[5] = (unsigned char)(((x) >> 16) & 255); \ + (y)[6] = (unsigned char)(((x) >> 8) & 255); (y)[7] = (unsigned char)((x) & 255); } + + #define LOAD64H(x, y) \ + { x = (((ulong64)((y)[0] & 255)) << 56) | (((ulong64)((y)[1] & 255)) << 48) | \ + (((ulong64)((y)[2] & 255)) << 40) | (((ulong64)((y)[3] & 255)) << 32) | \ + (((ulong64)((y)[4] & 255)) << 24) | (((ulong64)((y)[5] & 255)) << 16) | \ + (((ulong64)((y)[6] & 255)) << 8) | (((ulong64)((y)[7] & 255))); } + #endif + + #ifdef ENDIAN_32BITWORD + + #define STORE32L(x, y) \ + { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + + #define LOAD32L(x, y) \ + XMEMCPY(&(x), y, 4); + + #define STORE64L(x, y) \ + { (y)[7] = (unsigned char)(((x) >> 56) & 255); (y)[6] = (unsigned char)(((x) >> 48) & 255); \ + (y)[5] = (unsigned char)(((x) >> 40) & 255); (y)[4] = (unsigned char)(((x) >> 32) & 255); \ + (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \ + (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); } + + #define LOAD64L(x, y) \ + { x = (((ulong64)((y)[7] & 255)) << 56) | (((ulong64)((y)[6] & 255)) << 48) | \ + (((ulong64)((y)[5] & 255)) << 40) | (((ulong64)((y)[4] & 255)) << 32) | \ + (((ulong64)((y)[3] & 255)) << 24) | (((ulong64)((y)[2] & 255)) << 16) | \ + (((ulong64)((y)[1] & 255)) << 8) | (((ulong64)((y)[0] & 255))); } + + #else /* 64-bit words then */ + + #define STORE32L(x, y) \ + { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + + #define LOAD32L(x, y) \ + { XMEMCPY(&(x), y, 4); x &= 0xFFFFFFFF; } + + #define STORE64L(x, y) \ + { ulong64 __t = (x); XMEMCPY(y, &__t, 8); } + + #define LOAD64L(x, y) \ + { XMEMCPY(&(x), y, 8); } + #endif /* ENDIAN_64BITWORD */ +#endif /* ENDIAN_LITTLE */ + +#ifdef ENDIAN_BIG + #define STORE32L(x, y) \ + { (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \ + (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); } + + #define LOAD32L(x, y) \ + { x = ((unsigned long)((y)[3] & 255) << 24) | \ + ((unsigned long)((y)[2] & 255) << 16) | \ + ((unsigned long)((y)[1] & 255) << 8) | \ + ((unsigned long)((y)[0] & 255)); } + + #define STORE64L(x, y) \ + { (y)[7] = (unsigned char)(((x) >> 56) & 255); (y)[6] = (unsigned char)(((x) >> 48) & 255); \ + (y)[5] = (unsigned char)(((x) >> 40) & 255); (y)[4] = (unsigned char)(((x) >> 32) & 255); \ + (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \ + (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); } + + #define LOAD64L(x, y) \ + { x = (((ulong64)((y)[7] & 255)) << 56) | (((ulong64)((y)[6] & 255)) << 48) | \ + (((ulong64)((y)[5] & 255)) << 40) | (((ulong64)((y)[4] & 255)) << 32) | \ + (((ulong64)((y)[3] & 255)) << 24) | (((ulong64)((y)[2] & 255)) << 16) | \ + (((ulong64)((y)[1] & 255)) << 8) | (((ulong64)((y)[0] & 255))); } + + #ifdef ENDIAN_32BITWORD + + #define STORE32H(x, y) \ + { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + + #define LOAD32H(x, y) \ + XMEMCPY(&(x), y, 4); + + #define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x) >> 56) & 255); (y)[1] = (unsigned char)(((x) >> 48) & 255); \ + (y)[2] = (unsigned char)(((x) >> 40) & 255); (y)[3] = (unsigned char)(((x) >> 32) & 255); \ + (y)[4] = (unsigned char)(((x) >> 24) & 255); (y)[5] = (unsigned char)(((x) >> 16) & 255); \ + (y)[6] = (unsigned char)(((x) >> 8) & 255); (y)[7] = (unsigned char)((x) & 255); } + + #define LOAD64H(x, y) \ + { x = (((ulong64)((y)[0] & 255)) << 56) | (((ulong64)((y)[1] & 255)) << 48) | \ + (((ulong64)((y)[2] & 255)) << 40) | (((ulong64)((y)[3] & 255)) << 32) | \ + (((ulong64)((y)[4] & 255)) << 24) | (((ulong64)((y)[5] & 255)) << 16) | \ + (((ulong64)((y)[6] & 255)) << 8) | (((ulong64)((y)[7] & 255))); } + + #else /* 64-bit words then */ + + #define STORE32H(x, y) \ + { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + + #define LOAD32H(x, y) \ + { XMEMCPY(&(x), y, 4); x &= 0xFFFFFFFF; } + + #define STORE64H(x, y) \ + { ulong64 __t = (x); XMEMCPY(y, &__t, 8); } + + #define LOAD64H(x, y) \ + { XMEMCPY(&(x), y, 8); } + #endif /* ENDIAN_64BITWORD */ +#endif /* ENDIAN_BIG */ + +#define BSWAP(x) \ + (((x >> 24) & 0x000000FFUL) | ((x << 24) & 0xFF000000UL) | \ + ((x >> 8) & 0x0000FF00UL) | ((x << 8) & 0x00FF0000UL)) + + +/* 32-bit Rotates */ +#if defined(_MSC_VER) + +/* instrinsic rotate */ + #include + #pragma intrinsic(_lrotr,_lrotl) + #define ROR(x, n) _lrotr(x, n) + #define ROL(x, n) _lrotl(x, n) + #define RORc(x, n) _lrotr(x, n) + #define ROLc(x, n) _lrotl(x, n) + +#elif !defined(__STRICT_ANSI__) && defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) && !defined(INTEL_CC) && !defined(LTC_NO_ASM) + +static inline unsigned ROL(unsigned word, int i) { + asm ("roll %%cl,%0" + : "=r" (word) + : "0" (word), "c" (i)); + return word; +} + +static inline unsigned ROR(unsigned word, int i) { + asm ("rorl %%cl,%0" + : "=r" (word) + : "0" (word), "c" (i)); + return word; +} + + #ifndef LTC_NO_ROLC + +static inline unsigned ROLc(unsigned word, const int i) { + asm ("roll %2,%0" + : "=r" (word) + : "0" (word), "I" (i)); + return word; +} + +static inline unsigned RORc(unsigned word, const int i) { + asm ("rorl %2,%0" + : "=r" (word) + : "0" (word), "I" (i)); + return word; +} + + #else + + #define ROLc ROL + #define RORc ROR + #endif + +#elif !defined(__STRICT_ANSI__) && defined(LTC_PPC32) + +static inline unsigned ROL(unsigned word, int i) { + asm ("rotlw %0,%0,%2" + : "=r" (word) + : "0" (word), "r" (i)); + return word; +} + +static inline unsigned ROR(unsigned word, int i) { + asm ("rotlw %0,%0,%2" + : "=r" (word) + : "0" (word), "r" (32 - i)); + return word; +} + + #ifndef LTC_NO_ROLC + +static inline unsigned ROLc(unsigned word, const int i) { + asm ("rotlwi %0,%0,%2" + : "=r" (word) + : "0" (word), "I" (i)); + return word; +} + +static inline unsigned RORc(unsigned word, const int i) { + asm ("rotrwi %0,%0,%2" + : "=r" (word) + : "0" (word), "I" (i)); + return word; +} + + #else + + #define ROLc ROL + #define RORc ROR + #endif + + +#else + +/* rotates the hard way */ + #define ROL(x, y) ((((unsigned long)(x) << (unsigned long)((y) & 31)) | (((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) + #define ROR(x, y) (((((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)((y) & 31)) | ((unsigned long)(x) << (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) + #define ROLc(x, y) ((((unsigned long)(x) << (unsigned long)((y) & 31)) | (((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) + #define RORc(x, y) (((((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)((y) & 31)) | ((unsigned long)(x) << (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) +#endif + + +/* 64-bit Rotates */ +#if !defined(__STRICT_ANSI__) && defined(__GNUC__) && defined(__x86_64__) && !defined(LTC_NO_ASM) + +static inline unsigned long ROL64(unsigned long word, int i) { + asm ("rolq %%cl,%0" + : "=r" (word) + : "0" (word), "c" (i)); + return word; +} + +static inline unsigned long ROR64(unsigned long word, int i) { + asm ("rorq %%cl,%0" + : "=r" (word) + : "0" (word), "c" (i)); + return word; +} + + #ifndef LTC_NO_ROLC + +static inline unsigned long ROL64c(unsigned long word, const int i) { + asm ("rolq %2,%0" + : "=r" (word) + : "0" (word), "J" (i)); + return word; +} + +static inline unsigned long ROR64c(unsigned long word, const int i) { + asm ("rorq %2,%0" + : "=r" (word) + : "0" (word), "J" (i)); + return word; +} + + #else /* LTC_NO_ROLC */ + + #define ROL64c ROL64 + #define ROR64c ROR64 + #endif + +#else /* Not x86_64 */ + + #define ROL64(x, y) \ + ((((x) << ((ulong64)(y) & 63)) | \ + (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((ulong64)64 - ((y) & 63)))) & CONST64(0xFFFFFFFFFFFFFFFF)) + + #define ROR64(x, y) \ + (((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((ulong64)(y) & CONST64(63))) | \ + ((x) << ((ulong64)(64 - ((y) & CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF)) + + #define ROL64c(x, y) \ + ((((x) << ((ulong64)(y) & 63)) | \ + (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((ulong64)64 - ((y) & 63)))) & CONST64(0xFFFFFFFFFFFFFFFF)) + + #define ROR64c(x, y) \ + (((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((ulong64)(y) & CONST64(63))) | \ + ((x) << ((ulong64)(64 - ((y) & CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF)) +#endif + +#ifndef MAX + #define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif + +#ifndef MIN + #define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +/* extract a byte portably */ +#ifdef _MSC_VER + #define extract_byte(x, n) ((unsigned char)((x) >> (8 * (n)))) +#else + #define extract_byte(x, n) (((x) >> (8 * (n))) & 255) +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_macros.h,v $ */ +/* $Revision: 1.15 $ */ +/* $Date: 2006/11/29 23:43:57 $ */ + +/* ---- SYMMETRIC KEY STUFF ----- + * + * We put each of the ciphers scheduled keys in their own structs then we put all of + * the key formats in one union. This makes the function prototypes easier to use. + */ +#ifdef LTC_BLOWFISH +struct blowfish_key { + ulong32 S[4][256]; + ulong32 K[18]; +}; +#endif + +#ifdef LTC_RC5 +struct rc5_key { + int rounds; + ulong32 K[50]; +}; +#endif + +#ifdef LTC_RC6 +struct rc6_key { + ulong32 K[44]; +}; +#endif + +#ifdef LTC_SAFERP +struct saferp_key { + unsigned char K[33][16]; + long rounds; +}; +#endif + +#ifdef LTC_RIJNDAEL +struct rijndael_key { + ulong32 eK[60], dK[60]; + int Nr; +}; +#endif + +#ifdef LTC_KSEED +struct kseed_key { + ulong32 K[32], dK[32]; +}; +#endif + +#ifdef LTC_KASUMI +struct kasumi_key { + ulong32 KLi1[8], KLi2[8], + KOi1[8], KOi2[8], KOi3[8], + KIi1[8], KIi2[8], KIi3[8]; +}; +#endif + +#ifdef LTC_XTEA +struct xtea_key { + unsigned long A[32], B[32]; +}; +#endif + +#ifdef LTC_TWOFISH + #ifndef LTC_TWOFISH_SMALL +struct twofish_key { + ulong32 S[4][256], K[40]; +}; + #else +struct twofish_key { + ulong32 K[40]; + unsigned char S[32], start; +}; + #endif +#endif + +#ifdef LTC_SAFER + #define LTC_SAFER_K64_DEFAULT_NOF_ROUNDS 6 + #define LTC_SAFER_K128_DEFAULT_NOF_ROUNDS 10 + #define LTC_SAFER_SK64_DEFAULT_NOF_ROUNDS 8 + #define LTC_SAFER_SK128_DEFAULT_NOF_ROUNDS 10 + #define LTC_SAFER_MAX_NOF_ROUNDS 13 + #define LTC_SAFER_BLOCK_LEN 8 + #define LTC_SAFER_KEY_LEN (1 + LTC_SAFER_BLOCK_LEN * (1 + 2 * LTC_SAFER_MAX_NOF_ROUNDS)) +typedef unsigned char safer_block_t[LTC_SAFER_BLOCK_LEN]; +typedef unsigned char safer_key_t[LTC_SAFER_KEY_LEN]; +struct safer_key { + safer_key_t key; +}; +#endif + +#ifdef LTC_RC2 +struct rc2_key { + unsigned xkey[64]; +}; +#endif + +#ifdef LTC_DES +struct des_key { + ulong32 ek[32], dk[32]; +}; + +struct des3_key { + ulong32 ek[3][32], dk[3][32]; +}; +#endif + +#ifdef LTC_CAST5 +struct cast5_key { + ulong32 K[32], keylen; +}; +#endif + +#ifdef LTC_NOEKEON +struct noekeon_key { + ulong32 K[4], dK[4]; +}; +#endif + +#ifdef LTC_SKIPJACK +struct skipjack_key { + unsigned char key[10]; +}; +#endif + +#ifdef LTC_KHAZAD +struct khazad_key { + ulong64 roundKeyEnc[8 + 1]; + ulong64 roundKeyDec[8 + 1]; +}; +#endif + +#ifdef LTC_ANUBIS +struct anubis_key { + int keyBits; + int R; + ulong32 roundKeyEnc[18 + 1][4]; + ulong32 roundKeyDec[18 + 1][4]; +}; +#endif + +#ifdef LTC_MULTI2 +struct multi2_key { + int N; + ulong32 uk[8]; +}; +#endif + +typedef union Symmetric_key { +#ifdef LTC_DES + struct des_key des; + struct des3_key des3; +#endif +#ifdef LTC_RC2 + struct rc2_key rc2; +#endif +#ifdef LTC_SAFER + struct safer_key safer; +#endif +#ifdef LTC_TWOFISH + struct twofish_key twofish; +#endif +#ifdef LTC_BLOWFISH + struct blowfish_key blowfish; +#endif +#ifdef LTC_RC5 + struct rc5_key rc5; +#endif +#ifdef LTC_RC6 + struct rc6_key rc6; +#endif +#ifdef LTC_SAFERP + struct saferp_key saferp; +#endif +#ifdef LTC_RIJNDAEL + struct rijndael_key rijndael; +#endif +#ifdef LTC_XTEA + struct xtea_key xtea; +#endif +#ifdef LTC_CAST5 + struct cast5_key cast5; +#endif +#ifdef LTC_NOEKEON + struct noekeon_key noekeon; +#endif +#ifdef LTC_SKIPJACK + struct skipjack_key skipjack; +#endif +#ifdef LTC_KHAZAD + struct khazad_key khazad; +#endif +#ifdef LTC_ANUBIS + struct anubis_key anubis; +#endif +#ifdef LTC_KSEED + struct kseed_key kseed; +#endif +#ifdef LTC_KASUMI + struct kasumi_key kasumi; +#endif +#ifdef LTC_MULTI2 + struct multi2_key multi2; +#endif + void *data; +} symmetric_key; + +#ifdef LTC_ECB_MODE +/** A block cipher ECB structure */ +typedef struct { + /** The index of the cipher chosen */ + int cipher, + /** The block size of the given cipher */ + blocklen; + /** The scheduled key */ + symmetric_key key; +} symmetric_ECB; +#endif + +#ifdef LTC_CFB_MODE +/** A block cipher CFB structure */ +typedef struct { + /** The index of the cipher chosen */ + int cipher, + /** The block size of the given cipher */ + blocklen, + /** The padding offset */ + padlen; + /** The current IV */ + unsigned char IV[MAXBLOCKSIZE], + /** The pad used to encrypt/decrypt */ + pad[MAXBLOCKSIZE]; + /** The scheduled key */ + symmetric_key key; +} symmetric_CFB; +#endif + +#ifdef LTC_OFB_MODE +/** A block cipher OFB structure */ +typedef struct { + /** The index of the cipher chosen */ + int cipher, + /** The block size of the given cipher */ + blocklen, + /** The padding offset */ + padlen; + /** The current IV */ + unsigned char IV[MAXBLOCKSIZE]; + /** The scheduled key */ + symmetric_key key; +} symmetric_OFB; +#endif + +#ifdef LTC_CBC_MODE +/** A block cipher CBC structure */ +typedef struct { + /** The index of the cipher chosen */ + int cipher, + /** The block size of the given cipher */ + blocklen; + /** The current IV */ + unsigned char IV[MAXBLOCKSIZE]; + /** The scheduled key */ + symmetric_key key; +} symmetric_CBC; +#endif + + +#ifdef LTC_CTR_MODE +/** A block cipher CTR structure */ +typedef struct { + /** The index of the cipher chosen */ + int cipher, + /** The block size of the given cipher */ + blocklen, + /** The padding offset */ + padlen, + /** The mode (endianess) of the CTR, 0==little, 1==big */ + mode, + /** counter width */ + ctrlen; + + /** The counter */ + unsigned char ctr[MAXBLOCKSIZE], + /** The pad used to encrypt/decrypt */ + pad[MAXBLOCKSIZE]; + /** The scheduled key */ + symmetric_key key; +} symmetric_CTR; +#endif + + +#ifdef LTC_LRW_MODE +/** A LRW structure */ +typedef struct { + /** The index of the cipher chosen (must be a 128-bit block cipher) */ + int cipher; + + /** The current IV */ + unsigned char IV[16], + + /** the tweak key */ + tweak[16], + + /** The current pad, it's the product of the first 15 bytes against the tweak key */ + pad[16]; + + /** The scheduled symmetric key */ + symmetric_key key; + + #ifdef LRW_TABLES + /** The pre-computed multiplication table */ + unsigned char PC[16][256][16]; + #endif +} symmetric_LRW; +#endif + +#ifdef LTC_F8_MODE +/** A block cipher F8 structure */ +typedef struct { + /** The index of the cipher chosen */ + int cipher, + /** The block size of the given cipher */ + blocklen, + /** The padding offset */ + padlen; + /** The current IV */ + unsigned char IV[MAXBLOCKSIZE], + MIV[MAXBLOCKSIZE]; + /** Current block count */ + ulong32 blockcnt; + /** The scheduled key */ + symmetric_key key; +} symmetric_F8; +#endif + + +/** cipher descriptor table, last entry has "name == NULL" to mark the end of table */ +extern struct ltc_cipher_descriptor { + /** name of cipher */ + char *name; + /** internal ID */ + unsigned char ID; + /** min keysize (octets) */ + int min_key_length, + /** max keysize (octets) */ + max_key_length, + /** block size (octets) */ + block_length, + /** default number of rounds */ + default_rounds; + + /** Setup the cipher + @param key The input symmetric key + @param keylen The length of the input key (octets) + @param num_rounds The requested number of rounds (0==default) + @param skey [out] The destination of the scheduled key + @return CRYPT_OK if successful + */ + int (*setup)(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); + + /** Encrypt a block + @param pt The plaintext + @param ct [out] The ciphertext + @param skey The scheduled key + @return CRYPT_OK if successful + */ + int (*ecb_encrypt)(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); + + /** Decrypt a block + @param ct The ciphertext + @param pt [out] The plaintext + @param skey The scheduled key + @return CRYPT_OK if successful + */ + int (*ecb_decrypt)(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); + + /** Test the block cipher + @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled + */ + int (*test)(void); + + /** Terminate the context + @param skey The scheduled key + */ + void (*done)(symmetric_key *skey); + + /** Determine a key size + @param keysize [in/out] The size of the key desired and the suggested size + @return CRYPT_OK if successful + */ + int (*keysize)(int *keysize); + +/** Accelerators **/ + + /** Accelerated ECB encryption + @param pt Plaintext + @param ct Ciphertext + @param blocks The number of complete blocks to process + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_ecb_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, symmetric_key *skey); + + /** Accelerated ECB decryption + @param pt Plaintext + @param ct Ciphertext + @param blocks The number of complete blocks to process + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_ecb_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, symmetric_key *skey); + + /** Accelerated CBC encryption + @param pt Plaintext + @param ct Ciphertext + @param blocks The number of complete blocks to process + @param IV The initial value (input/output) + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_cbc_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, symmetric_key *skey); + + /** Accelerated CBC decryption + @param pt Plaintext + @param ct Ciphertext + @param blocks The number of complete blocks to process + @param IV The initial value (input/output) + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_cbc_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, unsigned char *IV, symmetric_key *skey); + + /** Accelerated CTR encryption + @param pt Plaintext + @param ct Ciphertext + @param blocks The number of complete blocks to process + @param IV The initial value (input/output) + @param mode little or big endian counter (mode=0 or mode=1) + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_ctr_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, int mode, symmetric_key *skey); + + /** Accelerated LRW + @param pt Plaintext + @param ct Ciphertext + @param blocks The number of complete blocks to process + @param IV The initial value (input/output) + @param tweak The LRW tweak + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_lrw_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, const unsigned char *tweak, symmetric_key *skey); + + /** Accelerated LRW + @param ct Ciphertext + @param pt Plaintext + @param blocks The number of complete blocks to process + @param IV The initial value (input/output) + @param tweak The LRW tweak + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_lrw_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, unsigned char *IV, const unsigned char *tweak, symmetric_key *skey); + + /** Accelerated CCM packet (one-shot) + @param key The secret key to use + @param keylen The length of the secret key (octets) + @param uskey A previously scheduled key [optional can be NULL] + @param nonce The session nonce [use once] + @param noncelen The length of the nonce + @param header The header for the session + @param headerlen The length of the header (octets) + @param pt [out] The plaintext + @param ptlen The length of the plaintext (octets) + @param ct [out] The ciphertext + @param tag [out] The destination tag + @param taglen [in/out] The max size and resulting size of the authentication tag + @param direction Encrypt or Decrypt direction (0 or 1) + @return CRYPT_OK if successful + */ + int (*accel_ccm_memory)( + const unsigned char *key, unsigned long keylen, + symmetric_key *uskey, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *header, unsigned long headerlen, + unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen, + int direction); + + /** Accelerated GCM packet (one shot) + @param key The secret key + @param keylen The length of the secret key + @param IV The initial vector + @param IVlen The length of the initial vector + @param adata The additional authentication data (header) + @param adatalen The length of the adata + @param pt The plaintext + @param ptlen The length of the plaintext (ciphertext length is the same) + @param ct The ciphertext + @param tag [out] The MAC tag + @param taglen [in/out] The MAC tag length + @param direction Encrypt or Decrypt mode (GCM_ENCRYPT or GCM_DECRYPT) + @return CRYPT_OK on success + */ + int (*accel_gcm_memory)( + const unsigned char *key, unsigned long keylen, + const unsigned char *IV, unsigned long IVlen, + const unsigned char *adata, unsigned long adatalen, + unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen, + int direction); + + /** Accelerated one shot LTC_OMAC + @param key The secret key + @param keylen The key length (octets) + @param in The message + @param inlen Length of message (octets) + @param out [out] Destination for tag + @param outlen [in/out] Initial and final size of out + @return CRYPT_OK on success + */ + int (*omac_memory)( + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); + + /** Accelerated one shot XCBC + @param key The secret key + @param keylen The key length (octets) + @param in The message + @param inlen Length of message (octets) + @param out [out] Destination for tag + @param outlen [in/out] Initial and final size of out + @return CRYPT_OK on success + */ + int (*xcbc_memory)( + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); + + /** Accelerated one shot F9 + @param key The secret key + @param keylen The key length (octets) + @param in The message + @param inlen Length of message (octets) + @param out [out] Destination for tag + @param outlen [in/out] Initial and final size of out + @return CRYPT_OK on success + @remark Requires manual padding + */ + int (*f9_memory)( + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +} cipher_descriptor[]; + +#ifdef LTC_BLOWFISH +int blowfish_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int blowfish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int blowfish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int blowfish_test(void); +void blowfish_done(symmetric_key *skey); +int blowfish_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor blowfish_desc; +#endif + +#ifdef LTC_RC5 +int rc5_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rc5_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int rc5_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int rc5_test(void); +void rc5_done(symmetric_key *skey); +int rc5_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor rc5_desc; +#endif + +#ifdef LTC_RC6 +int rc6_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rc6_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int rc6_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int rc6_test(void); +void rc6_done(symmetric_key *skey); +int rc6_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor rc6_desc; +#endif + +#ifdef LTC_RC2 +int rc2_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rc2_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int rc2_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int rc2_test(void); +void rc2_done(symmetric_key *skey); +int rc2_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor rc2_desc; +#endif + +#ifdef LTC_SAFERP +int saferp_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int saferp_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int saferp_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int saferp_test(void); +void saferp_done(symmetric_key *skey); +int saferp_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor saferp_desc; +#endif + +#ifdef LTC_SAFER +int safer_k64_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int safer_sk64_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int safer_k128_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int safer_sk128_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int safer_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *key); +int safer_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *key); +int safer_k64_test(void); +int safer_sk64_test(void); +int safer_sk128_test(void); +void safer_done(symmetric_key *skey); +int safer_64_keysize(int *keysize); +int safer_128_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor safer_k64_desc, safer_k128_desc, safer_sk64_desc, safer_sk128_desc; +#endif + +#ifdef LTC_RIJNDAEL + +/* make aes an alias */ + #define aes_setup rijndael_setup + #define aes_ecb_encrypt rijndael_ecb_encrypt + #define aes_ecb_decrypt rijndael_ecb_decrypt + #define aes_test rijndael_test + #define aes_done rijndael_done + #define aes_keysize rijndael_keysize + + #define aes_enc_setup rijndael_enc_setup + #define aes_enc_ecb_encrypt rijndael_enc_ecb_encrypt + #define aes_enc_keysize rijndael_enc_keysize + +int rijndael_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rijndael_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int rijndael_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int rijndael_test(void); +void rijndael_done(symmetric_key *skey); +int rijndael_keysize(int *keysize); +int rijndael_enc_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rijndael_enc_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +void rijndael_enc_done(symmetric_key *skey); +int rijndael_enc_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor rijndael_desc, aes_desc; +extern const struct ltc_cipher_descriptor rijndael_enc_desc, aes_enc_desc; +#endif + +#ifdef LTC_XTEA +int xtea_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int xtea_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int xtea_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int xtea_test(void); +void xtea_done(symmetric_key *skey); +int xtea_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor xtea_desc; +#endif + +#ifdef LTC_TWOFISH +int twofish_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int twofish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int twofish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int twofish_test(void); +void twofish_done(symmetric_key *skey); +int twofish_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor twofish_desc; +#endif + +#ifdef LTC_DES +int des_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int des_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int des_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int des_test(void); +void des_done(symmetric_key *skey); +int des_keysize(int *keysize); +int des3_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int des3_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int des3_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int des3_test(void); +void des3_done(symmetric_key *skey); +int des3_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor des_desc, des3_desc; +#endif + +#ifdef LTC_CAST5 +int cast5_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int cast5_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int cast5_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int cast5_test(void); +void cast5_done(symmetric_key *skey); +int cast5_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor cast5_desc; +#endif + +#ifdef LTC_NOEKEON +int noekeon_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int noekeon_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int noekeon_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int noekeon_test(void); +void noekeon_done(symmetric_key *skey); +int noekeon_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor noekeon_desc; +#endif + +#ifdef LTC_SKIPJACK +int skipjack_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int skipjack_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int skipjack_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int skipjack_test(void); +void skipjack_done(symmetric_key *skey); +int skipjack_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor skipjack_desc; +#endif + +#ifdef LTC_KHAZAD +int khazad_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int khazad_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int khazad_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int khazad_test(void); +void khazad_done(symmetric_key *skey); +int khazad_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor khazad_desc; +#endif + +#ifdef LTC_ANUBIS +int anubis_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int anubis_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int anubis_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int anubis_test(void); +void anubis_done(symmetric_key *skey); +int anubis_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor anubis_desc; +#endif + +#ifdef LTC_KSEED +int kseed_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int kseed_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int kseed_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int kseed_test(void); +void kseed_done(symmetric_key *skey); +int kseed_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor kseed_desc; +#endif + +#ifdef LTC_KASUMI +int kasumi_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int kasumi_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int kasumi_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int kasumi_test(void); +void kasumi_done(symmetric_key *skey); +int kasumi_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor kasumi_desc; +#endif + + +#ifdef LTC_MULTI2 +int multi2_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int multi2_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int multi2_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int multi2_test(void); +void multi2_done(symmetric_key *skey); +int multi2_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor multi2_desc; +#endif + +#ifdef LTC_ECB_MODE +int ecb_start(int cipher, const unsigned char *key, + int keylen, int num_rounds, symmetric_ECB *ecb); +int ecb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_ECB *ecb); +int ecb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_ECB *ecb); +int ecb_done(symmetric_ECB *ecb); +#endif + +#ifdef LTC_CFB_MODE +int cfb_start(int cipher, const unsigned char *IV, const unsigned char *key, + int keylen, int num_rounds, symmetric_CFB *cfb); +int cfb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CFB *cfb); +int cfb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CFB *cfb); +int cfb_getiv(unsigned char *IV, unsigned long *len, symmetric_CFB *cfb); +int cfb_setiv(const unsigned char *IV, unsigned long len, symmetric_CFB *cfb); +int cfb_done(symmetric_CFB *cfb); +#endif + +#ifdef LTC_OFB_MODE +int ofb_start(int cipher, const unsigned char *IV, const unsigned char *key, + int keylen, int num_rounds, symmetric_OFB *ofb); +int ofb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_OFB *ofb); +int ofb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_OFB *ofb); +int ofb_getiv(unsigned char *IV, unsigned long *len, symmetric_OFB *ofb); +int ofb_setiv(const unsigned char *IV, unsigned long len, symmetric_OFB *ofb); +int ofb_done(symmetric_OFB *ofb); +#endif + +#ifdef LTC_CBC_MODE +int cbc_start(int cipher, const unsigned char *IV, const unsigned char *key, + int keylen, int num_rounds, symmetric_CBC *cbc); +int cbc_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CBC *cbc); +int cbc_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CBC *cbc); +int cbc_getiv(unsigned char *IV, unsigned long *len, symmetric_CBC *cbc); +int cbc_setiv(const unsigned char *IV, unsigned long len, symmetric_CBC *cbc); +int cbc_done(symmetric_CBC *cbc); +#endif + +#ifdef LTC_CTR_MODE + + #define CTR_COUNTER_LITTLE_ENDIAN 0x0000 + #define CTR_COUNTER_BIG_ENDIAN 0x1000 + #define LTC_CTR_RFC3686 0x2000 + +int ctr_start(int cipher, + const unsigned char *IV, + const unsigned char *key, int keylen, + int num_rounds, int ctr_mode, + symmetric_CTR *ctr); +int ctr_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CTR *ctr); +int ctr_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CTR *ctr); +int ctr_getiv(unsigned char *IV, unsigned long *len, symmetric_CTR *ctr); +int ctr_setiv(const unsigned char *IV, unsigned long len, symmetric_CTR *ctr); +int ctr_done(symmetric_CTR *ctr); +int ctr_test(void); +#endif + +#ifdef LTC_LRW_MODE + + #define LRW_ENCRYPT 0 + #define LRW_DECRYPT 1 + +int lrw_start(int cipher, + const unsigned char *IV, + const unsigned char *key, int keylen, + const unsigned char *tweak, + int num_rounds, + symmetric_LRW *lrw); +int lrw_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_LRW *lrw); +int lrw_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_LRW *lrw); +int lrw_getiv(unsigned char *IV, unsigned long *len, symmetric_LRW *lrw); +int lrw_setiv(const unsigned char *IV, unsigned long len, symmetric_LRW *lrw); +int lrw_done(symmetric_LRW *lrw); +int lrw_test(void); + +/* don't call */ +int lrw_process(const unsigned char *pt, unsigned char *ct, unsigned long len, int mode, symmetric_LRW *lrw); +#endif + +#ifdef LTC_F8_MODE +int f8_start(int cipher, const unsigned char *IV, + const unsigned char *key, int keylen, + const unsigned char *salt_key, int skeylen, + int num_rounds, symmetric_F8 *f8); +int f8_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_F8 *f8); +int f8_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_F8 *f8); +int f8_getiv(unsigned char *IV, unsigned long *len, symmetric_F8 *f8); +int f8_setiv(const unsigned char *IV, unsigned long len, symmetric_F8 *f8); +int f8_done(symmetric_F8 *f8); +int f8_test_mode(void); +#endif + +#ifdef LTC_XTS_MODE +typedef struct { + symmetric_key key1, key2; + int cipher; +} symmetric_xts; + +int xts_start(int cipher, + const unsigned char *key1, + const unsigned char *key2, + unsigned long keylen, + int num_rounds, + symmetric_xts *xts); + +int xts_encrypt( + const unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + const unsigned char *tweak, + symmetric_xts *xts); +int xts_decrypt( + const unsigned char *ct, unsigned long ptlen, + unsigned char *pt, + const unsigned char *tweak, + symmetric_xts *xts); + +void xts_done(symmetric_xts *xts); +int xts_test(void); +void xts_mult_x(unsigned char *I); +#endif + +int find_cipher(const char *name); +int find_cipher_any(const char *name, int blocklen, int keylen); +int find_cipher_id(unsigned char ID); +int register_cipher(const struct ltc_cipher_descriptor *cipher); +int unregister_cipher(const struct ltc_cipher_descriptor *cipher); +int cipher_is_valid(int idx); + +LTC_MUTEX_PROTO(ltc_cipher_mutex) + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_cipher.h,v $ */ +/* $Revision: 1.54 $ */ +/* $Date: 2007/05/12 14:37:41 $ */ + +#define LTC_SHA1 +/* ---- HASH FUNCTIONS ---- */ +#ifdef LTC_SHA512 +struct sha512_state { + ulong64 length, state[8]; + unsigned long curlen; + unsigned char buf[128]; +}; +#endif + +#ifdef LTC_SHA256 +struct sha256_state { + ulong64 length; + ulong32 state[8], curlen; + unsigned char buf[64]; +}; +#endif + +#ifdef LTC_SHA1 +struct sha1_state { + ulong64 length; + ulong32 state[5], curlen; + unsigned char buf[64]; +}; +#endif + +#ifdef LTC_MD5 +struct md5_state { + ulong64 length; + ulong32 state[4], curlen; + unsigned char buf[64]; +}; +#endif + +#ifdef LTC_MD4 +struct md4_state { + ulong64 length; + ulong32 state[4], curlen; + unsigned char buf[64]; +}; +#endif + +#ifdef LTC_TIGER +struct tiger_state { + ulong64 state[3], length; + unsigned long curlen; + unsigned char buf[64]; +}; +#endif + +#ifdef LTC_MD2 +struct md2_state { + unsigned char chksum[16], X[48], buf[16]; + unsigned long curlen; +}; +#endif + +#ifdef LTC_RIPEMD128 +struct rmd128_state { + ulong64 length; + unsigned char buf[64]; + ulong32 curlen, state[4]; +}; +#endif + +#ifdef LTC_RIPEMD160 +struct rmd160_state { + ulong64 length; + unsigned char buf[64]; + ulong32 curlen, state[5]; +}; +#endif + +#ifdef LTC_RIPEMD256 +struct rmd256_state { + ulong64 length; + unsigned char buf[64]; + ulong32 curlen, state[8]; +}; +#endif + +#ifdef LTC_RIPEMD320 +struct rmd320_state { + ulong64 length; + unsigned char buf[64]; + ulong32 curlen, state[10]; +}; +#endif + +#ifdef LTC_WHIRLPOOL +struct whirlpool_state { + ulong64 length, state[8]; + unsigned char buf[64]; + ulong32 curlen; +}; +#endif + +#ifdef LTC_CHC_HASH +struct chc_state { + ulong64 length; + unsigned char state[MAXBLOCKSIZE], buf[MAXBLOCKSIZE]; + ulong32 curlen; +}; +#endif + +typedef union Hash_state { + char dummy[1]; +#ifdef LTC_CHC_HASH + struct chc_state chc; +#endif +#ifdef LTC_WHIRLPOOL + struct whirlpool_state whirlpool; +#endif +#ifdef LTC_SHA512 + struct sha512_state sha512; +#endif +#ifdef LTC_SHA256 + struct sha256_state sha256; +#endif +#ifdef LTC_SHA1 + struct sha1_state sha1; +#endif +#ifdef LTC_MD5 + struct md5_state md5; +#endif +#ifdef LTC_MD4 + struct md4_state md4; +#endif +#ifdef LTC_MD2 + struct md2_state md2; +#endif +#ifdef LTC_TIGER + struct tiger_state tiger; +#endif +#ifdef LTC_RIPEMD128 + struct rmd128_state rmd128; +#endif +#ifdef LTC_RIPEMD160 + struct rmd160_state rmd160; +#endif +#ifdef LTC_RIPEMD256 + struct rmd256_state rmd256; +#endif +#ifdef LTC_RIPEMD320 + struct rmd320_state rmd320; +#endif + void *data; +} hash_state; + +/** hash descriptor */ +extern struct ltc_hash_descriptor { + /** name of hash */ + char *name; + /** internal ID */ + unsigned char ID; + /** Size of digest in octets */ + unsigned long hashsize; + /** Input block size in octets */ + unsigned long blocksize; + /** ASN.1 OID */ + unsigned long OID[16]; + /** Length of DER encoding */ + unsigned long OIDlen; + + /** Init a hash state + @param hash The hash to initialize + @return CRYPT_OK if successful + */ + int (*init)(hash_state *hash); + + /** Process a block of data + @param hash The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful + */ + int (*process)(hash_state *hash, const unsigned char *in, unsigned long inlen); + + /** Produce the digest and store it + @param hash The hash state + @param out [out] The destination of the digest + @return CRYPT_OK if successful + */ + int (*done)(hash_state *hash, unsigned char *out); + + /** Self-test + @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled + */ + int (*test)(void); + + /* accelerated hmac callback: if you need to-do multiple packets just use the generic hmac_memory and provide a hash callback */ + int (*hmac_block)(const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +} hash_descriptor[]; + +#ifdef LTC_CHC_HASH +int chc_register(int cipher); +int chc_init(hash_state *md); +int chc_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int chc_done(hash_state *md, unsigned char *hash); +int chc_test(void); + +extern const struct ltc_hash_descriptor chc_desc; +#endif + +#ifdef LTC_WHIRLPOOL +int whirlpool_init(hash_state *md); +int whirlpool_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int whirlpool_done(hash_state *md, unsigned char *hash); +int whirlpool_test(void); + +extern const struct ltc_hash_descriptor whirlpool_desc; +#endif + +#ifdef LTC_SHA512 +int sha512_init(hash_state *md); +int sha512_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int sha512_done(hash_state *md, unsigned char *hash); +int sha512_test(void); + +extern const struct ltc_hash_descriptor sha512_desc; +#endif + +#ifdef LTC_SHA384 + #ifndef LTC_SHA512 + #error LTC_SHA512 is required for LTC_SHA384 + #endif +int sha384_init(hash_state *md); + + #define sha384_process sha512_process +int sha384_done(hash_state *md, unsigned char *hash); +int sha384_test(void); + +extern const struct ltc_hash_descriptor sha384_desc; +#endif + +#ifdef LTC_SHA256 +int sha256_init(hash_state *md); +int sha256_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int sha256_done(hash_state *md, unsigned char *hash); +int sha256_test(void); + +extern const struct ltc_hash_descriptor sha256_desc; + + #ifdef LTC_SHA224 + #ifndef LTC_SHA256 + #error LTC_SHA256 is required for LTC_SHA224 + #endif +int sha224_init(hash_state *md); + + #define sha224_process sha256_process +int sha224_done(hash_state *md, unsigned char *hash); +int sha224_test(void); + +extern const struct ltc_hash_descriptor sha224_desc; + #endif +#endif + +#ifdef LTC_SHA1 +int sha1_init(hash_state *md); +int sha1_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int sha1_done(hash_state *md, unsigned char *hash); +int sha1_test(void); + +extern const struct ltc_hash_descriptor sha1_desc; +#endif + +#ifdef LTC_MD5 +int md5_init(hash_state *md); +int md5_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int md5_done(hash_state *md, unsigned char *hash); +int md5_test(void); + +extern const struct ltc_hash_descriptor md5_desc; +#endif + +#ifdef LTC_MD4 +int md4_init(hash_state *md); +int md4_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int md4_done(hash_state *md, unsigned char *hash); +int md4_test(void); + +extern const struct ltc_hash_descriptor md4_desc; +#endif + +#ifdef LTC_MD2 +int md2_init(hash_state *md); +int md2_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int md2_done(hash_state *md, unsigned char *hash); +int md2_test(void); + +extern const struct ltc_hash_descriptor md2_desc; +#endif + +#ifdef LTC_TIGER +int tiger_init(hash_state *md); +int tiger_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int tiger_done(hash_state *md, unsigned char *hash); +int tiger_test(void); + +extern const struct ltc_hash_descriptor tiger_desc; +#endif + +#ifdef LTC_RIPEMD128 +int rmd128_init(hash_state *md); +int rmd128_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int rmd128_done(hash_state *md, unsigned char *hash); +int rmd128_test(void); + +extern const struct ltc_hash_descriptor rmd128_desc; +#endif + +#ifdef LTC_RIPEMD160 +int rmd160_init(hash_state *md); +int rmd160_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int rmd160_done(hash_state *md, unsigned char *hash); +int rmd160_test(void); + +extern const struct ltc_hash_descriptor rmd160_desc; +#endif + +#ifdef LTC_RIPEMD256 +int rmd256_init(hash_state *md); +int rmd256_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int rmd256_done(hash_state *md, unsigned char *hash); +int rmd256_test(void); + +extern const struct ltc_hash_descriptor rmd256_desc; +#endif + +#ifdef LTC_RIPEMD320 +int rmd320_init(hash_state *md); +int rmd320_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int rmd320_done(hash_state *md, unsigned char *hash); +int rmd320_test(void); + +extern const struct ltc_hash_descriptor rmd320_desc; +#endif + + +int find_hash(const char *name); +int find_hash_id(unsigned char ID); +int find_hash_oid(const unsigned long *ID, unsigned long IDlen); +int find_hash_any(const char *name, int digestlen); +int register_hash(const struct ltc_hash_descriptor *hash); +int unregister_hash(const struct ltc_hash_descriptor *hash); +int hash_is_valid(int idx); + +LTC_MUTEX_PROTO(ltc_hash_mutex) + +int hash_memory(int hash, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int hash_memory_multi(int hash, unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...); +int hash_filehandle(int hash, FILE *in, unsigned char *out, unsigned long *outlen); +int hash_file(int hash, const char *fname, unsigned char *out, unsigned long *outlen); + +/* a simple macro for making hash "process" functions */ +#define HASH_PROCESS(func_name, compress_name, state_var, block_size) \ + int func_name(hash_state * md, const unsigned char *in, unsigned long inlen) \ + { \ + unsigned long n; \ + int err; \ + LTC_ARGCHK(md != NULL); \ + LTC_ARGCHK(in != NULL); \ + if (md->state_var.curlen > sizeof(md->state_var.buf)) { \ + return CRYPT_INVALID_ARG; \ + } \ + while (inlen > 0) { \ + if (md->state_var.curlen == 0 && inlen >= block_size) { \ + if ((err = compress_name(md, (unsigned char *)in)) != CRYPT_OK) { \ + return err; \ + } \ + md->state_var.length += block_size * 8; \ + in += block_size; \ + inlen -= block_size; \ + } else { \ + n = MIN(inlen, (block_size - md->state_var.curlen)); \ + memcpy(md->state_var.buf + md->state_var.curlen, in, (size_t)n); \ + md->state_var.curlen += n; \ + in += n; \ + inlen -= n; \ + if (md->state_var.curlen == block_size) { \ + if ((err = compress_name(md, md->state_var.buf)) != CRYPT_OK) { \ + return err; \ + } \ + md->state_var.length += 8 * block_size; \ + md->state_var.curlen = 0; \ + } \ + } \ + } \ + return CRYPT_OK; \ + } + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_hash.h,v $ */ +/* $Revision: 1.22 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + +#ifdef LTC_HMAC +typedef struct Hmac_state { + hash_state md; + int hash; + hash_state hashstate; + unsigned char *key; +} hmac_state; + +int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, unsigned long keylen); +int hmac_process(hmac_state *hmac, const unsigned char *in, unsigned long inlen); +int hmac_done(hmac_state *hmac, unsigned char *out, unsigned long *outlen); +int hmac_test(void); +int hmac_memory(int hash, + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int hmac_memory_multi(int hash, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...); +int hmac_file(int hash, const char *fname, const unsigned char *key, + unsigned long keylen, + unsigned char *dst, unsigned long *dstlen); +#endif + +#ifdef LTC_OMAC + +typedef struct { + int cipher_idx, + buflen, + blklen; + unsigned char block[MAXBLOCKSIZE], + prev[MAXBLOCKSIZE], + Lu[2][MAXBLOCKSIZE]; + symmetric_key key; +} omac_state; + +int omac_init(omac_state *omac, int cipher, const unsigned char *key, unsigned long keylen); +int omac_process(omac_state *omac, const unsigned char *in, unsigned long inlen); +int omac_done(omac_state *omac, unsigned char *out, unsigned long *outlen); +int omac_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int omac_memory_multi(int cipher, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...); +int omac_file(int cipher, + const unsigned char *key, unsigned long keylen, + const char *filename, + unsigned char *out, unsigned long *outlen); +int omac_test(void); +#endif /* LTC_OMAC */ + +#ifdef LTC_PMAC + +typedef struct { + unsigned char Ls[32][MAXBLOCKSIZE], /* L shifted by i bits to the left */ + Li[MAXBLOCKSIZE], /* value of Li [current value, we calc from previous recall] */ + Lr[MAXBLOCKSIZE], /* L * x^-1 */ + block[MAXBLOCKSIZE], /* currently accumulated block */ + checksum[MAXBLOCKSIZE]; /* current checksum */ + + symmetric_key key; /* scheduled key for cipher */ + unsigned long block_index; /* index # for current block */ + int cipher_idx, /* cipher idx */ + block_len, /* length of block */ + buflen; /* number of bytes in the buffer */ +} pmac_state; + +int pmac_init(pmac_state *pmac, int cipher, const unsigned char *key, unsigned long keylen); +int pmac_process(pmac_state *pmac, const unsigned char *in, unsigned long inlen); +int pmac_done(pmac_state *pmac, unsigned char *out, unsigned long *outlen); + +int pmac_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *msg, unsigned long msglen, + unsigned char *out, unsigned long *outlen); + +int pmac_memory_multi(int cipher, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...); + +int pmac_file(int cipher, + const unsigned char *key, unsigned long keylen, + const char *filename, + unsigned char *out, unsigned long *outlen); + +int pmac_test(void); + +/* internal functions */ +int pmac_ntz(unsigned long x); +void pmac_shift_xor(pmac_state *pmac); +#endif /* PMAC */ + +#ifdef LTC_EAX_MODE + + #if !(defined(LTC_OMAC) && defined(LTC_CTR_MODE)) + #error LTC_EAX_MODE requires LTC_OMAC and CTR + #endif + +typedef struct { + unsigned char N[MAXBLOCKSIZE]; + symmetric_CTR ctr; + omac_state headeromac, ctomac; +} eax_state; + +int eax_init(eax_state *eax, int cipher, const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *header, unsigned long headerlen); + +int eax_encrypt(eax_state *eax, const unsigned char *pt, unsigned char *ct, unsigned long length); +int eax_decrypt(eax_state *eax, const unsigned char *ct, unsigned char *pt, unsigned long length); +int eax_addheader(eax_state *eax, const unsigned char *header, unsigned long length); +int eax_done(eax_state *eax, unsigned char *tag, unsigned long *taglen); + +int eax_encrypt_authenticate_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *header, unsigned long headerlen, + const unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen); + +int eax_decrypt_verify_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *header, unsigned long headerlen, + const unsigned char *ct, unsigned long ctlen, + unsigned char *pt, + unsigned char *tag, unsigned long taglen, + int *stat); + +int eax_test(void); +#endif /* EAX MODE */ + +#ifdef LTC_OCB_MODE +typedef struct { + unsigned char L[MAXBLOCKSIZE], /* L value */ + Ls[32][MAXBLOCKSIZE], /* L shifted by i bits to the left */ + Li[MAXBLOCKSIZE], /* value of Li [current value, we calc from previous recall] */ + Lr[MAXBLOCKSIZE], /* L * x^-1 */ + R[MAXBLOCKSIZE], /* R value */ + checksum[MAXBLOCKSIZE]; /* current checksum */ + + symmetric_key key; /* scheduled key for cipher */ + unsigned long block_index; /* index # for current block */ + int cipher, /* cipher idx */ + block_len; /* length of block */ +} ocb_state; + +int ocb_init(ocb_state *ocb, int cipher, + const unsigned char *key, unsigned long keylen, const unsigned char *nonce); + +int ocb_encrypt(ocb_state *ocb, const unsigned char *pt, unsigned char *ct); +int ocb_decrypt(ocb_state *ocb, const unsigned char *ct, unsigned char *pt); + +int ocb_done_encrypt(ocb_state *ocb, + const unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen); + +int ocb_done_decrypt(ocb_state *ocb, + const unsigned char *ct, unsigned long ctlen, + unsigned char *pt, + const unsigned char *tag, unsigned long taglen, int *stat); + +int ocb_encrypt_authenticate_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, + const unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen); + +int ocb_decrypt_verify_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, + const unsigned char *ct, unsigned long ctlen, + unsigned char *pt, + const unsigned char *tag, unsigned long taglen, + int *stat); + +int ocb_test(void); + +/* internal functions */ +void ocb_shift_xor(ocb_state *ocb, unsigned char *Z); +int ocb_ntz(unsigned long x); +int s_ocb_done(ocb_state *ocb, const unsigned char *pt, unsigned long ptlen, + unsigned char *ct, unsigned char *tag, unsigned long *taglen, int mode); +#endif /* LTC_OCB_MODE */ + +#ifdef LTC_CCM_MODE + + #define CCM_ENCRYPT 0 + #define CCM_DECRYPT 1 + +int ccm_memory(int cipher, + const unsigned char *key, unsigned long keylen, + symmetric_key *uskey, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *header, unsigned long headerlen, + unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen, + int direction); + +int ccm_test(void); +#endif /* LTC_CCM_MODE */ + +#if defined(LRW_MODE) || defined(LTC_GCM_MODE) +void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c); +#endif + + +/* table shared between GCM and LRW */ +#if defined(LTC_GCM_TABLES) || defined(LRW_TABLES) || ((defined(LTC_GCM_MODE) || defined(LTC_GCM_MODE)) && defined(LTC_FAST)) +extern const unsigned char gcm_shift_table[]; +#endif + +#ifdef LTC_GCM_MODE + + #define GCM_ENCRYPT 0 + #define GCM_DECRYPT 1 + + #define LTC_GCM_MODE_IV 0 + #define LTC_GCM_MODE_AAD 1 + #define LTC_GCM_MODE_TEXT 2 + +typedef struct { + symmetric_key K; + unsigned char H[16], /* multiplier */ + X[16], /* accumulator */ + Y[16], /* counter */ + Y_0[16], /* initial counter */ + buf[16]; /* buffer for stuff */ + + int cipher, /* which cipher */ + ivmode, /* Which mode is the IV in? */ + mode, /* mode the GCM code is in */ + buflen; /* length of data in buf */ + + ulong64 totlen, /* 64-bit counter used for IV and AAD */ + pttotlen; /* 64-bit counter for the PT */ + + #ifdef LTC_GCM_TABLES + unsigned char PC[16][256][16] /* 16 tables of 8x128 */ + #ifdef LTC_GCM_TABLES_SSE2 + __attribute__ ((aligned(16))) + #endif + ; + #endif +} gcm_state; + +void gcm_mult_h(gcm_state *gcm, unsigned char *I); + +int gcm_init(gcm_state *gcm, int cipher, + const unsigned char *key, int keylen); + +int gcm_reset(gcm_state *gcm); + +int gcm_add_iv(gcm_state *gcm, + const unsigned char *IV, unsigned long IVlen); + +int gcm_add_aad(gcm_state *gcm, + const unsigned char *adata, unsigned long adatalen); + +int gcm_process(gcm_state *gcm, + unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + int direction); + +int gcm_done(gcm_state *gcm, + unsigned char *tag, unsigned long *taglen); + +int gcm_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *IV, unsigned long IVlen, + const unsigned char *adata, unsigned long adatalen, + unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen, + int direction); +int gcm_test(void); +#endif /* LTC_GCM_MODE */ + +#ifdef LTC_PELICAN + +typedef struct pelican_state { + symmetric_key K; + unsigned char state[16]; + int buflen; +} pelican_state; + +int pelican_init(pelican_state *pelmac, const unsigned char *key, unsigned long keylen); +int pelican_process(pelican_state *pelmac, const unsigned char *in, unsigned long inlen); +int pelican_done(pelican_state *pelmac, unsigned char *out); +int pelican_test(void); + +int pelican_memory(const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out); +#endif + +#ifdef LTC_XCBC + +/* add this to "keylen" to xcbc_init to use a pure three-key XCBC MAC */ + #define LTC_XCBC_PURE 0x8000UL + +typedef struct { + unsigned char K[3][MAXBLOCKSIZE], + IV[MAXBLOCKSIZE]; + + symmetric_key key; + + int cipher, + buflen, + blocksize; +} xcbc_state; + +int xcbc_init(xcbc_state *xcbc, int cipher, const unsigned char *key, unsigned long keylen); +int xcbc_process(xcbc_state *xcbc, const unsigned char *in, unsigned long inlen); +int xcbc_done(xcbc_state *xcbc, unsigned char *out, unsigned long *outlen); +int xcbc_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int xcbc_memory_multi(int cipher, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...); +int xcbc_file(int cipher, + const unsigned char *key, unsigned long keylen, + const char *filename, + unsigned char *out, unsigned long *outlen); +int xcbc_test(void); +#endif + +#ifdef LTC_F9_MODE + +typedef struct { + unsigned char akey[MAXBLOCKSIZE], + ACC[MAXBLOCKSIZE], + IV[MAXBLOCKSIZE]; + + symmetric_key key; + + int cipher, + buflen, + keylen, + blocksize; +} f9_state; + +int f9_init(f9_state *f9, int cipher, const unsigned char *key, unsigned long keylen); +int f9_process(f9_state *f9, const unsigned char *in, unsigned long inlen); +int f9_done(f9_state *f9, unsigned char *out, unsigned long *outlen); +int f9_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int f9_memory_multi(int cipher, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...); +int f9_file(int cipher, + const unsigned char *key, unsigned long keylen, + const char *filename, + unsigned char *out, unsigned long *outlen); +int f9_test(void); +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_mac.h,v $ */ +/* $Revision: 1.23 $ */ +/* $Date: 2007/05/12 14:37:41 $ */ + +/* ---- PRNG Stuff ---- */ +#ifdef LTC_YARROW +struct yarrow_prng { + int cipher, hash; + unsigned char pool[MAXBLOCKSIZE]; + symmetric_CTR ctr; + LTC_MUTEX_TYPE(prng_lock) +}; +#endif + +#ifdef LTC_RC4 +struct rc4_prng { + int x, y; + unsigned char buf[256]; +}; +#endif + +#ifdef LTC_FORTUNA +struct fortuna_prng { + hash_state pool[LTC_FORTUNA_POOLS]; /* the pools */ + + symmetric_key skey; + + unsigned char K[32], /* the current key */ + IV[16]; /* IV for CTR mode */ + + unsigned long pool_idx, /* current pool we will add to */ + pool0_len, /* length of 0'th pool */ + wd; + + ulong64 reset_cnt; /* number of times we have reset */ + LTC_MUTEX_TYPE(prng_lock) +}; +#endif + +#ifdef LTC_SOBER128 +struct sober128_prng { + ulong32 R[17], /* Working storage for the shift register */ + initR[17], /* saved register contents */ + konst, /* key dependent constant */ + sbuf; /* partial word encryption buffer */ + + int nbuf, /* number of part-word stream bits buffered */ + flag, /* first add_entropy call or not? */ + set; /* did we call add_entropy to set key? */ +}; +#endif + +typedef union Prng_state { + char dummy[1]; +#ifdef LTC_YARROW + struct yarrow_prng yarrow; +#endif +#ifdef LTC_RC4 + struct rc4_prng rc4; +#endif +#ifdef LTC_FORTUNA + struct fortuna_prng fortuna; +#endif +#ifdef LTC_SOBER128 + struct sober128_prng sober128; +#endif +} prng_state; + +/** PRNG descriptor */ +extern struct ltc_prng_descriptor { + /** Name of the PRNG */ + char *name; + /** size in bytes of exported state */ + int export_size; + + /** Start a PRNG state + @param prng [out] The state to initialize + @return CRYPT_OK if successful + */ + int (*start)(prng_state *prng); + + /** Add entropy to the PRNG + @param in The entropy + @param inlen Length of the entropy (octets)\ + @param prng The PRNG state + @return CRYPT_OK if successful + */ + int (*add_entropy)(const unsigned char *in, unsigned long inlen, prng_state *prng); + + /** Ready a PRNG state to read from + @param prng The PRNG state to ready + @return CRYPT_OK if successful + */ + int (*ready)(prng_state *prng); + + /** Read from the PRNG + @param out [out] Where to store the data + @param outlen Length of data desired (octets) + @param prng The PRNG state to read from + @return Number of octets read + */ + unsigned long (*read)(unsigned char *out, unsigned long outlen, prng_state *prng); + + /** Terminate a PRNG state + @param prng The PRNG state to terminate + @return CRYPT_OK if successful + */ + int (*done)(prng_state *prng); + + /** Export a PRNG state + @param out [out] The destination for the state + @param outlen [in/out] The max size and resulting size of the PRNG state + @param prng The PRNG to export + @return CRYPT_OK if successful + */ + int (*pexport)(unsigned char *out, unsigned long *outlen, prng_state *prng); + + /** Import a PRNG state + @param in The data to import + @param inlen The length of the data to import (octets) + @param prng The PRNG to initialize/import + @return CRYPT_OK if successful + */ + int (*pimport)(const unsigned char *in, unsigned long inlen, prng_state *prng); + + /** Self-test the PRNG + @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled + */ + int (*test)(void); +} prng_descriptor[]; + +#ifdef LTC_YARROW +int yarrow_start(prng_state *prng); +int yarrow_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int yarrow_ready(prng_state *prng); +unsigned long yarrow_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int yarrow_done(prng_state *prng); +int yarrow_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int yarrow_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int yarrow_test(void); + +extern const struct ltc_prng_descriptor yarrow_desc; +#endif + +#ifdef LTC_FORTUNA +int fortuna_start(prng_state *prng); +int fortuna_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int fortuna_ready(prng_state *prng); +unsigned long fortuna_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int fortuna_done(prng_state *prng); +int fortuna_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int fortuna_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int fortuna_test(void); + +extern const struct ltc_prng_descriptor fortuna_desc; +#endif + +#ifdef LTC_RC4 +int rc4_start(prng_state *prng); +int rc4_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int rc4_ready(prng_state *prng); +unsigned long rc4_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int rc4_done(prng_state *prng); +int rc4_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int rc4_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int rc4_test(void); + +extern const struct ltc_prng_descriptor rc4_desc; +#endif + +#ifdef LTC_SPRNG +int sprng_start(prng_state *prng); +int sprng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int sprng_ready(prng_state *prng); +unsigned long sprng_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int sprng_done(prng_state *prng); +int sprng_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int sprng_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int sprng_test(void); + +extern const struct ltc_prng_descriptor sprng_desc; +#endif + +#ifdef LTC_SOBER128 +int sober128_start(prng_state *prng); +int sober128_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int sober128_ready(prng_state *prng); +unsigned long sober128_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int sober128_done(prng_state *prng); +int sober128_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int sober128_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int sober128_test(void); + +extern const struct ltc_prng_descriptor sober128_desc; +#endif + +int find_prng(const char *name); +int register_prng(const struct ltc_prng_descriptor *prng); +int unregister_prng(const struct ltc_prng_descriptor *prng); +int prng_is_valid(int idx); + +LTC_MUTEX_PROTO(ltc_prng_mutex) + +/* Slow RNG you **might** be able to use to seed a PRNG with. Be careful as this + * might not work on all platforms as planned + */ +unsigned long rng_get_bytes(unsigned char *out, + unsigned long outlen, + void ( *callback)(void)); + +int rng_make_prng(int bits, int wprng, prng_state *prng, void (*callback)(void)); + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_prng.h,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + +/* ---- NUMBER THEORY ---- */ + +enum { + PK_PUBLIC =0, + PK_PRIVATE=1 +}; + +int rand_prime(void *N, long len, prng_state *prng, int wprng); + +/* ---- RSA ---- */ +#ifdef LTC_MRSA + +/* Min and Max RSA key sizes (in bits) */ + #define MIN_RSA_SIZE 1024 + #define MAX_RSA_SIZE 4096 + +/** RSA LTC_PKCS style key */ +typedef struct Rsa_key { + /** Type of key, PK_PRIVATE or PK_PUBLIC */ + int type; + /** The public exponent */ + void *e; + /** The private exponent */ + void *d; + /** The modulus */ + void *N; + /** The p factor of N */ + void *p; + /** The q factor of N */ + void *q; + /** The 1/q mod p CRT param */ + void *qP; + /** The d mod (p - 1) CRT param */ + void *dP; + /** The d mod (q - 1) CRT param */ + void *dQ; +} rsa_key; + +int rsa_make_key(prng_state *prng, int wprng, int size, long e, rsa_key *key); + +int rsa_exptmod(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, int which, + rsa_key *key); + +void rsa_free(rsa_key *key); + +/* These use LTC_PKCS #1 v2.0 padding */ + #define rsa_encrypt_key(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _prng, _prng_idx, _hash_idx, _key) \ + rsa_encrypt_key_ex(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _prng, _prng_idx, _hash_idx, LTC_LTC_PKCS_1_OAEP, _key) + + #define rsa_decrypt_key(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _hash_idx, _stat, _key) \ + rsa_decrypt_key_ex(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _hash_idx, LTC_LTC_PKCS_1_OAEP, _stat, _key) + + #define rsa_sign_hash(_in, _inlen, _out, _outlen, _prng, _prng_idx, _hash_idx, _saltlen, _key) \ + rsa_sign_hash_ex(_in, _inlen, _out, _outlen, LTC_LTC_PKCS_1_PSS, _prng, _prng_idx, _hash_idx, _saltlen, _key) + + #define rsa_verify_hash(_sig, _siglen, _hash, _hashlen, _hash_idx, _saltlen, _stat, _key) \ + rsa_verify_hash_ex(_sig, _siglen, _hash, _hashlen, LTC_LTC_PKCS_1_PSS, _hash_idx, _saltlen, _stat, _key) + +/* These can be switched between LTC_PKCS #1 v2.x and LTC_PKCS #1 v1.5 paddings */ +int rsa_encrypt_key_ex(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + const unsigned char *lparam, unsigned long lparamlen, + prng_state *prng, int prng_idx, int hash_idx, int padding, rsa_key *key); + +int rsa_decrypt_key_ex(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + const unsigned char *lparam, unsigned long lparamlen, + int hash_idx, int padding, + int *stat, rsa_key *key); + +int rsa_sign_hash_ex(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + int padding, + prng_state *prng, int prng_idx, + int hash_idx, unsigned long saltlen, + rsa_key *key); + +int rsa_verify_hash_ex(const unsigned char *sig, unsigned long siglen, + const unsigned char *hash, unsigned long hashlen, + int padding, + int hash_idx, unsigned long saltlen, + int *stat, rsa_key *key); + +/* LTC_PKCS #1 import/export */ +int rsa_export(unsigned char *out, unsigned long *outlen, int type, rsa_key *key); +int rsa_import(const unsigned char *in, unsigned long inlen, rsa_key *key); +#endif + +/* ---- Katja ---- */ +#ifdef MKAT + +/* Min and Max KAT key sizes (in bits) */ + #define MIN_KAT_SIZE 1024 + #define MAX_KAT_SIZE 4096 + +/** Katja LTC_PKCS style key */ +typedef struct KAT_key { + /** Type of key, PK_PRIVATE or PK_PUBLIC */ + int type; + /** The private exponent */ + void *d; + /** The modulus */ + void *N; + /** The p factor of N */ + void *p; + /** The q factor of N */ + void *q; + /** The 1/q mod p CRT param */ + void *qP; + /** The d mod (p - 1) CRT param */ + void *dP; + /** The d mod (q - 1) CRT param */ + void *dQ; + /** The pq param */ + void *pq; +} katja_key; + +int katja_make_key(prng_state *prng, int wprng, int size, katja_key *key); + +int katja_exptmod(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, int which, + katja_key *key); + +void katja_free(katja_key *key); + +/* These use LTC_PKCS #1 v2.0 padding */ +int katja_encrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + const unsigned char *lparam, unsigned long lparamlen, + prng_state *prng, int prng_idx, int hash_idx, katja_key *key); + +int katja_decrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + const unsigned char *lparam, unsigned long lparamlen, + int hash_idx, int *stat, + katja_key *key); + +/* LTC_PKCS #1 import/export */ +int katja_export(unsigned char *out, unsigned long *outlen, int type, katja_key *key); +int katja_import(const unsigned char *in, unsigned long inlen, katja_key *key); +#endif + +/* ---- ECC Routines ---- */ +#ifdef LTC_MECC + +/* size of our temp buffers for exported keys */ + #define ECC_BUF_SIZE 256 + +/* max private key size */ + #define ECC_MAXSIZE 66 + +/** Structure defines a NIST GF(p) curve */ +typedef struct { + /** The size of the curve in octets */ + int size; + + /** name of curve */ + char *name; + + /** The prime that defines the field the curve is in (encoded in hex) */ + char *prime; + + /** The fields B param (hex) */ + char *B; + + /** The order of the curve (hex) */ + char *order; + + /** The x co-ordinate of the base point on the curve (hex) */ + char *Gx; + + /** The y co-ordinate of the base point on the curve (hex) */ + char *Gy; +} ltc_ecc_set_type; + +/** A point on a ECC curve, stored in Jacbobian format such that (x,y,z) => (x/z^2, y/z^3, 1) when interpretted as affine */ +typedef struct { + /** The x co-ordinate */ + void *x; + + /** The y co-ordinate */ + void *y; + + /** The z co-ordinate */ + void *z; +} ecc_point; + +/** An ECC key */ +typedef struct { + /** Type of key, PK_PRIVATE or PK_PUBLIC */ + int type; + + /** Index into the ltc_ecc_sets[] for the parameters of this curve; if -1, then this key is using user supplied curve in dp */ + int idx; + + /** pointer to domain parameters; either points to NIST curves (identified by idx >= 0) or user supplied curve */ + const ltc_ecc_set_type *dp; + + /** The public key */ + ecc_point pubkey; + + /** The private key */ + void *k; +} ecc_key; + +/** the ECC params provided */ +extern const ltc_ecc_set_type ltc_ecc_sets[]; + +int ecc_test(void); +void ecc_sizes(int *low, int *high); +int ecc_get_size(ecc_key *key); + +int ecc_make_key(prng_state *prng, int wprng, int keysize, ecc_key *key); +int ecc_make_key_ex(prng_state *prng, int wprng, ecc_key *key, const ltc_ecc_set_type *dp); +void ecc_free(ecc_key *key); + +int ecc_export(unsigned char *out, unsigned long *outlen, int type, ecc_key *key); +int ecc_import(const unsigned char *in, unsigned long inlen, ecc_key *key); +int ecc_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, const ltc_ecc_set_type *dp); + +int ecc_ansi_x963_export(ecc_key *key, unsigned char *out, unsigned long *outlen); +int ecc_ansi_x963_import(const unsigned char *in, unsigned long inlen, ecc_key *key); +int ecc_ansi_x963_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, ltc_ecc_set_type *dp); + +int ecc_shared_secret(ecc_key *private_key, ecc_key *public_key, + unsigned char *out, unsigned long *outlen); + +int ecc_encrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, int hash, + ecc_key *key); + +int ecc_decrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + ecc_key *key); + +int ecc_sign_hash(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, ecc_key *key); + +int ecc_verify_hash(const unsigned char *sig, unsigned long siglen, + const unsigned char *hash, unsigned long hashlen, + int *stat, ecc_key *key); + +/* low level functions */ +ecc_point *ltc_ecc_new_point(void); +void ltc_ecc_del_point(ecc_point *p); +int ltc_ecc_is_valid_idx(int n); + +/* point ops (mp == montgomery digit) */ + #if !defined(LTC_MECC_ACCEL) || defined(LTM_LTC_DESC) || defined(GMP_LTC_DESC) +/* R = 2P */ +int ltc_ecc_projective_dbl_point(ecc_point *P, ecc_point *R, void *modulus, void *mp); + +/* R = P + Q */ +int ltc_ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp); + #endif + + #if defined(LTC_MECC_FP) +/* optimized point multiplication using fixed point cache (HAC algorithm 14.117) */ +int ltc_ecc_fp_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map); + +/* functions for saving/loading/freeing/adding to fixed point cache */ +int ltc_ecc_fp_save_state(unsigned char **out, unsigned long *outlen); +int ltc_ecc_fp_restore_state(unsigned char *in, unsigned long inlen); +void ltc_ecc_fp_free(void); +int ltc_ecc_fp_add_point(ecc_point *g, void *modulus, int lock); + +/* lock/unlock all points currently in fixed point cache */ +void ltc_ecc_fp_tablelock(int lock); + #endif + +/* R = kG */ +int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map); + + #ifdef LTC_ECC_SHAMIR +/* kA*A + kB*B = C */ +int ltc_ecc_mul2add(ecc_point *A, void *kA, + ecc_point *B, void *kB, + ecc_point *C, + void *modulus); + + #ifdef LTC_MECC_FP +/* Shamir's trick with optimized point multiplication using fixed point cache */ +int ltc_ecc_fp_mul2add(ecc_point *A, void *kA, + ecc_point *B, void *kB, + ecc_point *C, void *modulus); + #endif + #endif + + +/* map P to affine from projective */ +int ltc_ecc_map(ecc_point *P, void *modulus, void *mp); +#endif + +#ifdef LTC_MDSA + +/* Max diff between group and modulus size in bytes */ + #define LTC_MDSA_DELTA 512 + +/* Max DSA group size in bytes (default allows 4k-bit groups) */ + #define LTC_MDSA_MAX_GROUP 512 + +/** DSA key structure */ +typedef struct { + /** The key type, PK_PRIVATE or PK_PUBLIC */ + int type; + + /** The order of the sub-group used in octets */ + int qord; + + /** The generator */ + void *g; + + /** The prime used to generate the sub-group */ + void *q; + + /** The large prime that generats the field the contains the sub-group */ + void *p; + + /** The private key */ + void *x; + + /** The public key */ + void *y; +} dsa_key; + +int dsa_make_key(prng_state *prng, int wprng, int group_size, int modulus_size, dsa_key *key); +void dsa_free(dsa_key *key); + +int dsa_sign_hash_raw(const unsigned char *in, unsigned long inlen, + void *r, void *s, + prng_state *prng, int wprng, dsa_key *key); + +int dsa_sign_hash(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, dsa_key *key); + +int dsa_verify_hash_raw(void *r, void *s, + const unsigned char *hash, unsigned long hashlen, + int *stat, dsa_key *key); + +int dsa_verify_hash(const unsigned char *sig, unsigned long siglen, + const unsigned char *hash, unsigned long hashlen, + int *stat, dsa_key *key); + +int dsa_encrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, int hash, + dsa_key *key); + +int dsa_decrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + dsa_key *key); + +int dsa_import(const unsigned char *in, unsigned long inlen, dsa_key *key); +int dsa_export(unsigned char *out, unsigned long *outlen, int type, dsa_key *key); +int dsa_verify_key(dsa_key *key, int *stat); + +int dsa_shared_secret(void *private_key, void *base, + dsa_key *public_key, + unsigned char *out, unsigned long *outlen); +#endif + +#ifdef LTC_DER +/* DER handling */ + +enum { + LTC_ASN1_EOL, + LTC_ASN1_BOOLEAN, + LTC_ASN1_INTEGER, + LTC_ASN1_SHORT_INTEGER, + LTC_ASN1_BIT_STRING, + LTC_ASN1_OCTET_STRING, + LTC_ASN1_NULL, + LTC_ASN1_OBJECT_IDENTIFIER, + LTC_ASN1_IA5_STRING, + LTC_ASN1_PRINTABLE_STRING, + LTC_ASN1_UTF8_STRING, + LTC_ASN1_UTCTIME, + LTC_ASN1_CHOICE, + LTC_ASN1_SEQUENCE, + LTC_ASN1_SET, + LTC_ASN1_SETOF +}; + +/** A LTC ASN.1 list type */ +typedef struct ltc_asn1_list_ { + /** The LTC ASN.1 enumerated type identifier */ + int type; + /** The data to encode or place for decoding */ + void *data; + /** The size of the input or resulting output */ + unsigned long size; + /** The used flag, this is used by the CHOICE ASN.1 type to indicate which choice was made */ + int used; + /** prev/next entry in the list */ + struct ltc_asn1_list_ *prev, *next, *child, *parent; +} ltc_asn1_list; + + #define LTC_SET_ASN1(list, index, Type, Data, Size) \ + do { \ + int LTC_MACRO_temp = (index); \ + ltc_asn1_list *LTC_MACRO_list = (list); \ + LTC_MACRO_list[LTC_MACRO_temp].type = (Type); \ + LTC_MACRO_list[LTC_MACRO_temp].data = (void *)(Data); \ + LTC_MACRO_list[LTC_MACRO_temp].size = (Size); \ + LTC_MACRO_list[LTC_MACRO_temp].used = 0; \ + } while (0); + +/* SEQUENCE */ +int der_encode_sequence_ex(ltc_asn1_list *list, unsigned long inlen, + unsigned char *out, unsigned long *outlen, int type_of); + + #define der_encode_sequence(list, inlen, out, outlen) der_encode_sequence_ex(list, inlen, out, outlen, LTC_ASN1_SEQUENCE) + +int der_decode_sequence_ex(const unsigned char *in, unsigned long inlen, + ltc_asn1_list *list, unsigned long outlen, int ordered); + + #define der_decode_sequence(in, inlen, list, outlen) der_decode_sequence_ex(in, inlen, list, outlen, 1) + +int der_length_sequence(ltc_asn1_list *list, unsigned long inlen, + unsigned long *outlen); + +/* SET */ + #define der_decode_set(in, inlen, list, outlen) der_decode_sequence_ex(in, inlen, list, outlen, 0) + #define der_length_set der_length_sequence +int der_encode_set(ltc_asn1_list *list, unsigned long inlen, + unsigned char *out, unsigned long *outlen); + +int der_encode_setof(ltc_asn1_list *list, unsigned long inlen, + unsigned char *out, unsigned long *outlen); + +/* VA list handy helpers with triplets of */ +int der_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...); +int der_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...); + +/* FLEXI DECODER handle unknown list decoder */ +int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out); +void der_free_sequence_flexi(ltc_asn1_list *list); +void der_sequence_free(ltc_asn1_list *in); + +/* BOOLEAN */ +int der_length_boolean(unsigned long *outlen); +int der_encode_boolean(int in, + unsigned char *out, unsigned long *outlen); +int der_decode_boolean(const unsigned char *in, unsigned long inlen, + int *out); + +/* INTEGER */ +int der_encode_integer(void *num, unsigned char *out, unsigned long *outlen); +int der_decode_integer(const unsigned char *in, unsigned long inlen, void *num); +int der_length_integer(void *num, unsigned long *len); + +/* INTEGER -- handy for 0..2^32-1 values */ +int der_decode_short_integer(const unsigned char *in, unsigned long inlen, unsigned long *num); +int der_encode_short_integer(unsigned long num, unsigned char *out, unsigned long *outlen); +int der_length_short_integer(unsigned long num, unsigned long *outlen); + +/* BIT STRING */ +int der_encode_bit_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_decode_bit_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_length_bit_string(unsigned long nbits, unsigned long *outlen); + +/* OCTET STRING */ +int der_encode_octet_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_decode_octet_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_length_octet_string(unsigned long noctets, unsigned long *outlen); + +/* OBJECT IDENTIFIER */ +int der_encode_object_identifier(unsigned long *words, unsigned long nwords, + unsigned char *out, unsigned long *outlen); +int der_decode_object_identifier(const unsigned char *in, unsigned long inlen, + unsigned long *words, unsigned long *outlen); +int der_length_object_identifier(unsigned long *words, unsigned long nwords, unsigned long *outlen); +unsigned long der_object_identifier_bits(unsigned long x); + +/* IA5 STRING */ +int der_encode_ia5_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_decode_ia5_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_length_ia5_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen); + +int der_ia5_char_encode(int c); +int der_ia5_value_decode(int v); + +/* Printable STRING */ +int der_encode_printable_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_decode_printable_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_length_printable_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen); + +int der_printable_char_encode(int c); +int der_printable_value_decode(int v); + +/* UTF-8 */ + #if (defined(SIZE_MAX) || __STDC_VERSION__ >= 199901L || defined(WCHAR_MAX) || defined(_WCHAR_T) || defined(_WCHAR_T_DEFINED) || defined (__WCHAR_TYPE__)) && !defined(LTC_NO_WCHAR) + #include + #else +typedef ulong32 wchar_t; + #endif + +int der_encode_utf8_string(const wchar_t *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); + +int der_decode_utf8_string(const unsigned char *in, unsigned long inlen, + wchar_t *out, unsigned long *outlen); +unsigned long der_utf8_charsize(const wchar_t c); +int der_length_utf8_string(const wchar_t *in, unsigned long noctets, unsigned long *outlen); + + +/* CHOICE */ +int der_decode_choice(const unsigned char *in, unsigned long *inlen, + ltc_asn1_list *list, unsigned long outlen); + +/* UTCTime */ +typedef struct { + unsigned YY, /* year */ + MM, /* month */ + DD, /* day */ + hh, /* hour */ + mm, /* minute */ + ss, /* second */ + off_dir, /* timezone offset direction 0 == +, 1 == - */ + off_hh, /* timezone offset hours */ + off_mm; /* timezone offset minutes */ +} ltc_utctime; + +int der_encode_utctime(ltc_utctime *utctime, + unsigned char *out, unsigned long *outlen); + +int der_decode_utctime(const unsigned char *in, unsigned long *inlen, + ltc_utctime *out); + +int der_length_utctime(ltc_utctime *utctime, unsigned long *outlen); +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_pk.h,v $ */ +/* $Revision: 1.81 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + +/** math functions **/ +#define LTC_SOURCE +#define LTC_MP_LT -1 +#define LTC_MP_EQ 0 +#define LTC_MP_GT 1 + +#define LTC_MP_NO 0 +#define LTC_MP_YES 1 + +#ifndef LTC_MECC +typedef void ecc_point; +#endif + +#ifndef LTC_MRSA +typedef void rsa_key; +#endif + +/** math descriptor */ +typedef struct { + /** Name of the math provider */ + char *name; + + /** Bits per digit, amount of bits must fit in an unsigned long */ + int bits_per_digit; + +/* ---- init/deinit functions ---- */ + + /** initialize a bignum + @param a The number to initialize + @return CRYPT_OK on success + */ + int (*init)(void **a); + + /** init copy + @param dst The number to initialize and write to + @param src The number to copy from + @return CRYPT_OK on success + */ + int (*init_copy)(void **dst, void *src); + + /** deinit + @param a The number to free + @return CRYPT_OK on success + */ + void (*deinit)(void *a); + +/* ---- data movement ---- */ + + /** negate + @param src The number to negate + @param dst The destination + @return CRYPT_OK on success + */ + int (*neg)(void *src, void *dst); + + /** copy + @param src The number to copy from + @param dst The number to write to + @return CRYPT_OK on success + */ + int (*copy)(void *src, void *dst); + +/* ---- trivial low level functions ---- */ + + /** set small constant + @param a Number to write to + @param n Source upto bits_per_digit (actually meant for very small constants) + @return CRYPT_OK on succcess + */ + int (*set_int)(void *a, unsigned long n); + + /** get small constant + @param a Number to read, only fetches upto bits_per_digit from the number + @return The lower bits_per_digit of the integer (unsigned) + */ + unsigned long (*get_int)(void *a); + + /** get digit n + @param a The number to read from + @param n The number of the digit to fetch + @return The bits_per_digit sized n'th digit of a + */ + unsigned long (*get_digit)(void *a, int n); + + /** Get the number of digits that represent the number + @param a The number to count + @return The number of digits used to represent the number + */ + int (*get_digit_count)(void *a); + + /** compare two integers + @param a The left side integer + @param b The right side integer + @return LTC_MP_LT if a < b, LTC_MP_GT if a > b and LTC_MP_EQ otherwise. (signed comparison) + */ + int (*compare)(void *a, void *b); + + /** compare against int + @param a The left side integer + @param b The right side integer (upto bits_per_digit) + @return LTC_MP_LT if a < b, LTC_MP_GT if a > b and LTC_MP_EQ otherwise. (signed comparison) + */ + int (*compare_d)(void *a, unsigned long n); + + /** Count the number of bits used to represent the integer + @param a The integer to count + @return The number of bits required to represent the integer + */ + int (*count_bits)(void *a); + + /** Count the number of LSB bits which are zero + @param a The integer to count + @return The number of contiguous zero LSB bits + */ + int (*count_lsb_bits)(void *a); + + /** Compute a power of two + @param a The integer to store the power in + @param n The power of two you want to store (a = 2^n) + @return CRYPT_OK on success + */ + int (*twoexpt)(void *a, int n); + +/* ---- radix conversions ---- */ + + /** read ascii string + @param a The integer to store into + @param str The string to read + @param radix The radix the integer has been represented in (2-64) + @return CRYPT_OK on success + */ + int (*read_radix)(void *a, const char *str, int radix); + + /** write number to string + @param a The integer to store + @param str The destination for the string + @param radix The radix the integer is to be represented in (2-64) + @return CRYPT_OK on success + */ + int (*write_radix)(void *a, char *str, int radix); + + /** get size as unsigned char string + @param a The integer to get the size (when stored in array of octets) + @return The length of the integer + */ + unsigned long (*unsigned_size)(void *a); + + /** store an integer as an array of octets + @param src The integer to store + @param dst The buffer to store the integer in + @return CRYPT_OK on success + */ + int (*unsigned_write)(void *src, unsigned char *dst); + + /** read an array of octets and store as integer + @param dst The integer to load + @param src The array of octets + @param len The number of octets + @return CRYPT_OK on success + */ + int (*unsigned_read)(void *dst, unsigned char *src, unsigned long len); + +/* ---- basic math ---- */ + + /** add two integers + @param a The first source integer + @param b The second source integer + @param c The destination of "a + b" + @return CRYPT_OK on success + */ + int (*add)(void *a, void *b, void *c); + + + /** add two integers + @param a The first source integer + @param b The second source integer (single digit of upto bits_per_digit in length) + @param c The destination of "a + b" + @return CRYPT_OK on success + */ + int (*addi)(void *a, unsigned long b, void *c); + + /** subtract two integers + @param a The first source integer + @param b The second source integer + @param c The destination of "a - b" + @return CRYPT_OK on success + */ + int (*sub)(void *a, void *b, void *c); + + /** subtract two integers + @param a The first source integer + @param b The second source integer (single digit of upto bits_per_digit in length) + @param c The destination of "a - b" + @return CRYPT_OK on success + */ + int (*subi)(void *a, unsigned long b, void *c); + + /** multiply two integers + @param a The first source integer + @param b The second source integer (single digit of upto bits_per_digit in length) + @param c The destination of "a * b" + @return CRYPT_OK on success + */ + int (*mul)(void *a, void *b, void *c); + + /** multiply two integers + @param a The first source integer + @param b The second source integer (single digit of upto bits_per_digit in length) + @param c The destination of "a * b" + @return CRYPT_OK on success + */ + int (*muli)(void *a, unsigned long b, void *c); + + /** Square an integer + @param a The integer to square + @param b The destination + @return CRYPT_OK on success + */ + int (*sqr)(void *a, void *b); + + /** Divide an integer + @param a The dividend + @param b The divisor + @param c The quotient (can be NULL to signify don't care) + @param d The remainder (can be NULL to signify don't care) + @return CRYPT_OK on success + */ + int (*mpdiv)(void *a, void *b, void *c, void *d); + + /** divide by two + @param a The integer to divide (shift right) + @param b The destination + @return CRYPT_OK on success + */ + int (*div_2)(void *a, void *b); + + /** Get remainder (small value) + @param a The integer to reduce + @param b The modulus (upto bits_per_digit in length) + @param c The destination for the residue + @return CRYPT_OK on success + */ + int (*modi)(void *a, unsigned long b, unsigned long *c); + + /** gcd + @param a The first integer + @param b The second integer + @param c The destination for (a, b) + @return CRYPT_OK on success + */ + int (*gcd)(void *a, void *b, void *c); + + /** lcm + @param a The first integer + @param b The second integer + @param c The destination for [a, b] + @return CRYPT_OK on success + */ + int (*lcm)(void *a, void *b, void *c); + + /** Modular multiplication + @param a The first source + @param b The second source + @param c The modulus + @param d The destination (a*b mod c) + @return CRYPT_OK on success + */ + int (*mulmod)(void *a, void *b, void *c, void *d); + + /** Modular squaring + @param a The first source + @param b The modulus + @param c The destination (a*a mod b) + @return CRYPT_OK on success + */ + int (*sqrmod)(void *a, void *b, void *c); + + /** Modular inversion + @param a The value to invert + @param b The modulus + @param c The destination (1/a mod b) + @return CRYPT_OK on success + */ + int (*invmod)(void *, void *, void *); + +/* ---- reduction ---- */ + + /** setup montgomery + @param a The modulus + @param b The destination for the reduction digit + @return CRYPT_OK on success + */ + int (*montgomery_setup)(void *a, void **b); + + /** get normalization value + @param a The destination for the normalization value + @param b The modulus + @return CRYPT_OK on success + */ + int (*montgomery_normalization)(void *a, void *b); + + /** reduce a number + @param a The number [and dest] to reduce + @param b The modulus + @param c The value "b" from montgomery_setup() + @return CRYPT_OK on success + */ + int (*montgomery_reduce)(void *a, void *b, void *c); + + /** clean up (frees memory) + @param a The value "b" from montgomery_setup() + @return CRYPT_OK on success + */ + void (*montgomery_deinit)(void *a); + +/* ---- exponentiation ---- */ + + /** Modular exponentiation + @param a The base integer + @param b The power (can be negative) integer + @param c The modulus integer + @param d The destination + @return CRYPT_OK on success + */ + int (*exptmod)(void *a, void *b, void *c, void *d); + + /** Primality testing + @param a The integer to test + @param b The destination of the result (FP_YES if prime) + @return CRYPT_OK on success + */ + int (*isprime)(void *a, int *b); + +/* ---- (optional) ecc point math ---- */ + + /** ECC GF(p) point multiplication (from the NIST curves) + @param k The integer to multiply the point by + @param G The point to multiply + @param R The destination for kG + @param modulus The modulus for the field + @param map Boolean indicated whether to map back to affine or not (can be ignored if you work in affine only) + @return CRYPT_OK on success + */ + int (*ecc_ptmul)(void *k, ecc_point *G, ecc_point *R, void *modulus, int map); + + /** ECC GF(p) point addition + @param P The first point + @param Q The second point + @param R The destination of P + Q + @param modulus The modulus + @param mp The "b" value from montgomery_setup() + @return CRYPT_OK on success + */ + int (*ecc_ptadd)(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp); + + /** ECC GF(p) point double + @param P The first point + @param R The destination of 2P + @param modulus The modulus + @param mp The "b" value from montgomery_setup() + @return CRYPT_OK on success + */ + int (*ecc_ptdbl)(ecc_point *P, ecc_point *R, void *modulus, void *mp); + + /** ECC mapping from projective to affine, currently uses (x,y,z) => (x/z^2, y/z^3, 1) + @param P The point to map + @param modulus The modulus + @param mp The "b" value from montgomery_setup() + @return CRYPT_OK on success + @remark The mapping can be different but keep in mind a ecc_point only has three + integers (x,y,z) so if you use a different mapping you have to make it fit. + */ + int (*ecc_map)(ecc_point *P, void *modulus, void *mp); + + /** Computes kA*A + kB*B = C using Shamir's Trick + @param A First point to multiply + @param kA What to multiple A by + @param B Second point to multiply + @param kB What to multiple B by + @param C [out] Destination point (can overlap with A or B + @param modulus Modulus for curve + @return CRYPT_OK on success + */ + int (*ecc_mul2add)(ecc_point *A, void *kA, + ecc_point *B, void *kB, + ecc_point *C, + void *modulus); + +/* ---- (optional) rsa optimized math (for internal CRT) ---- */ + + /** RSA Key Generation + @param prng An active PRNG state + @param wprng The index of the PRNG desired + @param size The size of the modulus (key size) desired (octets) + @param e The "e" value (public key). e==65537 is a good choice + @param key [out] Destination of a newly created private key pair + @return CRYPT_OK if successful, upon error all allocated ram is freed + */ + int (*rsa_keygen)(prng_state *prng, int wprng, int size, long e, rsa_key *key); + + + /** RSA exponentiation + @param in The octet array representing the base + @param inlen The length of the input + @param out The destination (to be stored in an octet array format) + @param outlen The length of the output buffer and the resulting size (zero padded to the size of the modulus) + @param which PK_PUBLIC for public RSA and PK_PRIVATE for private RSA + @param key The RSA key to use + @return CRYPT_OK on success + */ + int (*rsa_me)(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, int which, + rsa_key *key); +} ltc_math_descriptor; + +extern ltc_math_descriptor ltc_mp; + +int ltc_init_multi(void **a, ...); +void ltc_deinit_multi(void *a, ...); + +#ifdef LTM_DESC +extern const ltc_math_descriptor ltm_desc; +#endif + +#ifdef TFM_DESC +extern const ltc_math_descriptor tfm_desc; +#endif + +#ifdef GMP_DESC +extern const ltc_math_descriptor gmp_desc; +#endif + +#if !defined(DESC_DEF_ONLY) && defined(LTC_SOURCE) + #undef MP_DIGIT_BIT + #undef mp_iszero + #undef mp_isodd + #undef mp_tohex + + #define MP_DIGIT_BIT ltc_mp.bits_per_digit + +/* some handy macros */ + #define mp_init(a) ltc_mp.init(a) + #define mp_init_multi ltc_init_multi + #define mp_clear(a) ltc_mp.deinit(a) + #define mp_clear_multi ltc_deinit_multi + #define mp_init_copy(a, b) ltc_mp.init_copy(a, b) + + #define mp_neg(a, b) ltc_mp.neg(a, b) + #define mp_copy(a, b) ltc_mp.copy(a, b) + + #define mp_set(a, b) ltc_mp.set_int(a, b) + #define mp_set_int(a, b) ltc_mp.set_int(a, b) + #define mp_get_int(a) ltc_mp.get_int(a) + #define mp_get_digit(a, n) ltc_mp.get_digit(a, n) + #define mp_get_digit_count(a) ltc_mp.get_digit_count(a) + #define mp_cmp(a, b) ltc_mp.compare(a, b) + #define mp_cmp_d(a, b) ltc_mp.compare_d(a, b) + #define mp_count_bits(a) ltc_mp.count_bits(a) + #define mp_cnt_lsb(a) ltc_mp.count_lsb_bits(a) + #define mp_2expt(a, b) ltc_mp.twoexpt(a, b) + + #define mp_read_radix(a, b, c) ltc_mp.read_radix(a, b, c) + #define mp_toradix(a, b, c) ltc_mp.write_radix(a, b, c) + #define mp_unsigned_bin_size(a) ltc_mp.unsigned_size(a) + #define mp_to_unsigned_bin(a, b) ltc_mp.unsigned_write(a, b) + #define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c) + + #define mp_add(a, b, c) ltc_mp.add(a, b, c) + #define mp_add_d(a, b, c) ltc_mp.addi(a, b, c) + #define mp_sub(a, b, c) ltc_mp.sub(a, b, c) + #define mp_sub_d(a, b, c) ltc_mp.subi(a, b, c) + #define mp_mul(a, b, c) ltc_mp.mul(a, b, c) + #define mp_mul_d(a, b, c) ltc_mp.muli(a, b, c) + #define mp_sqr(a, b) ltc_mp.sqr(a, b) + #define mp_div(a, b, c, d) ltc_mp.mpdiv(a, b, c, d) + #define mp_div_2(a, b) ltc_mp.div_2(a, b) + #define mp_mod(a, b, c) ltc_mp.mpdiv(a, b, NULL, c) + #define mp_mod_d(a, b, c) ltc_mp.modi(a, b, c) + #define mp_gcd(a, b, c) ltc_mp.gcd(a, b, c) + #define mp_lcm(a, b, c) ltc_mp.lcm(a, b, c) + + #define mp_mulmod(a, b, c, d) ltc_mp.mulmod(a, b, c, d) + #define mp_sqrmod(a, b, c) ltc_mp.sqrmod(a, b, c) + #define mp_invmod(a, b, c) ltc_mp.invmod(a, b, c) + + #define mp_montgomery_setup(a, b) ltc_mp.montgomery_setup(a, b) + #define mp_montgomery_normalization(a, b) ltc_mp.montgomery_normalization(a, b) + #define mp_montgomery_reduce(a, b, c) ltc_mp.montgomery_reduce(a, b, c) + #define mp_montgomery_free(a) ltc_mp.montgomery_deinit(a) + + #define mp_exptmod(a, b, c, d) ltc_mp.exptmod(a, b, c, d) + #define mp_prime_is_prime(a, b, c) ltc_mp.isprime(a, c) + + #define mp_iszero(a) (mp_cmp_d(a, 0) == LTC_MP_EQ ? LTC_MP_YES : LTC_MP_NO) + #define mp_isodd(a) (mp_get_digit_count(a) > 0 ? (mp_get_digit(a, 0) & 1 ? LTC_MP_YES : LTC_MP_NO) : LTC_MP_NO) + #define mp_exch(a, b) do { void *ABC__tmp = a; a = b; b = ABC__tmp; } while (0); + + #define mp_tohex(a, b) mp_toradix(a, b, 16) +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_math.h,v $ */ +/* $Revision: 1.44 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + +/* ---- LTC_BASE64 Routines ---- */ +#ifdef LTC_BASE64 +int base64_encode(const unsigned char *in, unsigned long len, + unsigned char *out, unsigned long *outlen); + +int base64_decode(const unsigned char *in, unsigned long len, + unsigned char *out, unsigned long *outlen); +#endif + +/* ---- MEM routines ---- */ +void zeromem(void *dst, size_t len); +void burn_stack(unsigned long len); + +const char *error_to_string(int err); + +extern const char *crypt_build_settings; + +/* ---- HMM ---- */ +int crypt_fsa(void *mp, ...); + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_misc.h,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + +/* Defines the LTC_ARGCHK macro used within the library */ +/* ARGTYPE is defined in mycrypt_cfg.h */ +#if ARGTYPE == 0 + + #include + +/* this is the default LibTomCrypt macro */ +void crypt_argchk(char *v, char *s, int d); + + #define LTC_ARGCHK(x) if (!(x)) { crypt_argchk(#x, __FILE__, __LINE__); } + #define LTC_ARGCHKVD(x) LTC_ARGCHK(x) + +#elif ARGTYPE == 1 + +/* fatal type of error */ + #define LTC_ARGCHK(x) assert((x)) + #define LTC_ARGCHKVD(x) LTC_ARGCHK(x) + +#elif ARGTYPE == 2 + + #define LTC_ARGCHK(x) if (!(x)) { fprintf(stderr, "\nwarning: ARGCHK failed at %s:%d\n", __FILE__, __LINE__); } + #define LTC_ARGCHKVD(x) LTC_ARGCHK(x) + +#elif ARGTYPE == 3 + + #define LTC_ARGCHK(x) + #define LTC_ARGCHKVD(x) LTC_ARGCHK(x) + +#elif ARGTYPE == 4 + + #define LTC_ARGCHK(x) if (!(x)) return CRYPT_INVALID_ARG; + #define LTC_ARGCHKVD(x) if (!(x)) return; +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_argchk.h,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/08/27 20:50:21 $ */ + +/* LTC_PKCS Header Info */ + +/* ===> LTC_PKCS #1 -- RSA Cryptography <=== */ +#ifdef LTC_PKCS_1 + +enum ltc_pkcs_1_v1_5_blocks { + LTC_LTC_PKCS_1_EMSA = 1, /* Block type 1 (LTC_PKCS #1 v1.5 signature padding) */ + LTC_LTC_PKCS_1_EME = 2 /* Block type 2 (LTC_PKCS #1 v1.5 encryption padding) */ +}; + +enum ltc_pkcs_1_paddings { + LTC_LTC_PKCS_1_V1_5 = 1, /* LTC_PKCS #1 v1.5 padding (\sa ltc_pkcs_1_v1_5_blocks) */ + LTC_LTC_PKCS_1_OAEP = 2, /* LTC_PKCS #1 v2.0 encryption padding */ + LTC_LTC_PKCS_1_PSS = 3 /* LTC_PKCS #1 v2.1 signature padding */ +}; + +int pkcs_1_mgf1(int hash_idx, + const unsigned char *seed, unsigned long seedlen, + unsigned char *mask, unsigned long masklen); + +int pkcs_1_i2osp(void *n, unsigned long modulus_len, unsigned char *out); +int pkcs_1_os2ip(void *n, unsigned char *in, unsigned long inlen); + +/* *** v1.5 padding */ +int pkcs_1_v1_5_encode(const unsigned char *msg, + unsigned long msglen, + int block_type, + unsigned long modulus_bitlen, + prng_state *prng, + int prng_idx, + unsigned char *out, + unsigned long *outlen); + +int pkcs_1_v1_5_decode(const unsigned char *msg, + unsigned long msglen, + int block_type, + unsigned long modulus_bitlen, + unsigned char *out, + unsigned long *outlen, + int *is_valid); + +/* *** v2.1 padding */ +int pkcs_1_oaep_encode(const unsigned char *msg, unsigned long msglen, + const unsigned char *lparam, unsigned long lparamlen, + unsigned long modulus_bitlen, prng_state *prng, + int prng_idx, int hash_idx, + unsigned char *out, unsigned long *outlen); + +int pkcs_1_oaep_decode(const unsigned char *msg, unsigned long msglen, + const unsigned char *lparam, unsigned long lparamlen, + unsigned long modulus_bitlen, int hash_idx, + unsigned char *out, unsigned long *outlen, + int *res); + +int pkcs_1_pss_encode(const unsigned char *msghash, unsigned long msghashlen, + unsigned long saltlen, prng_state *prng, + int prng_idx, int hash_idx, + unsigned long modulus_bitlen, + unsigned char *out, unsigned long *outlen); + +int pkcs_1_pss_decode(const unsigned char *msghash, unsigned long msghashlen, + const unsigned char *sig, unsigned long siglen, + unsigned long saltlen, int hash_idx, + unsigned long modulus_bitlen, int *res); +#endif /* LTC_PKCS_1 */ + +/* ===> LTC_PKCS #5 -- Password Based Cryptography <=== */ +#ifdef LTC_PKCS_5 + +/* Algorithm #1 (old) */ +int pkcs_5_alg1(const unsigned char *password, unsigned long password_len, + const unsigned char *salt, + int iteration_count, int hash_idx, + unsigned char *out, unsigned long *outlen); + +/* Algorithm #2 (new) */ +int pkcs_5_alg2(const unsigned char *password, unsigned long password_len, + const unsigned char *salt, unsigned long salt_len, + int iteration_count, int hash_idx, + unsigned char *out, unsigned long *outlen); +#endif /* LTC_PKCS_5 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_pkcs.h,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + +#ifdef __cplusplus +} +#endif +#endif /* TOMCRYPT_H_ */ + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt.h,v $ */ +/* $Revision: 1.21 $ */ +/* $Date: 2006/12/16 19:34:05 $ */ + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_argchk.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_cipher_descriptor.c + Stores the cipher descriptor table, Tom St Denis + */ + +struct ltc_cipher_descriptor cipher_descriptor[TAB_SIZE] = { + { NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +}; + +LTC_MUTEX_GLOBAL(ltc_cipher_mutex) + + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_cipher_descriptor.c,v $ */ +/* $Revision: 1.13 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_cipher_is_valid.c + Determine if cipher is valid, Tom St Denis + */ + +/* + Test if a cipher index is valid + @param idx The index of the cipher to search for + @return CRYPT_OK if valid + */ +int cipher_is_valid(int idx) { + LTC_MUTEX_LOCK(<c_cipher_mutex); + if ((idx < 0) || (idx >= TAB_SIZE) || (cipher_descriptor[idx].name == NULL)) { + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return CRYPT_INVALID_CIPHER; + } + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return CRYPT_OK; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_cipher_is_valid.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_find_cipher.c + Find a cipher in the descriptor tables, Tom St Denis + */ + +/** + Find a registered cipher by name + @param name The name of the cipher to look for + @return >= 0 if found, -1 if not present + */ +int find_cipher(const char *name) { + int x; + + LTC_ARGCHK(name != NULL); + LTC_MUTEX_LOCK(<c_cipher_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if ((cipher_descriptor[x].name != NULL) && !XSTRCMP(cipher_descriptor[x].name, name)) { + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return x; + } + } + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_cipher.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_find_cipher_any.c + Find a cipher in the descriptor tables, Tom St Denis + */ + +/** + Find a cipher flexibly. First by name then if not present by block and key size + @param name The name of the cipher desired + @param blocklen The minimum length of the block cipher desired (octets) + @param keylen The minimum length of the key size desired (octets) + @return >= 0 if found, -1 if not present + */ +int find_cipher_any(const char *name, int blocklen, int keylen) { + int x; + + LTC_ARGCHK(name != NULL); + + x = find_cipher(name); + if (x != -1) return x; + + LTC_MUTEX_LOCK(<c_cipher_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if (cipher_descriptor[x].name == NULL) { + continue; + } + if ((blocklen <= (int)cipher_descriptor[x].block_length) && (keylen <= (int)cipher_descriptor[x].max_key_length)) { + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return x; + } + } + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_cipher_any.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_find_cipher_id.c + Find cipher by ID, Tom St Denis + */ + +/** + Find a cipher by ID number + @param ID The ID (not same as index) of the cipher to find + @return >= 0 if found, -1 if not present + */ +int find_cipher_id(unsigned char ID) { + int x; + + LTC_MUTEX_LOCK(<c_cipher_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if (cipher_descriptor[x].ID == ID) { + x = (cipher_descriptor[x].name == NULL) ? -1 : x; + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return x; + } + } + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_cipher_id.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_find_hash.c + Find a hash, Tom St Denis + */ + +/** + Find a registered hash by name + @param name The name of the hash to look for + @return >= 0 if found, -1 if not present + */ +int find_hash(const char *name) { + int x; + + LTC_ARGCHK(name != NULL); + LTC_MUTEX_LOCK(<c_hash_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if ((hash_descriptor[x].name != NULL) && (XSTRCMP(hash_descriptor[x].name, name) == 0)) { + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return x; + } + } + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_hash.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_find_hash_any.c + Find a hash, Tom St Denis + */ + +/** + Find a hash flexibly. First by name then if not present by digest size + @param name The name of the hash desired + @param digestlen The minimum length of the digest size (octets) + @return >= 0 if found, -1 if not present + */int find_hash_any(const char *name, int digestlen) { + int x, y, z; + + LTC_ARGCHK(name != NULL); + + x = find_hash(name); + if (x != -1) return x; + + LTC_MUTEX_LOCK(<c_hash_mutex); + y = MAXBLOCKSIZE + 1; + z = -1; + for (x = 0; x < TAB_SIZE; x++) { + if (hash_descriptor[x].name == NULL) { + continue; + } + if (((int)hash_descriptor[x].hashsize >= digestlen) && ((int)hash_descriptor[x].hashsize < y)) { + z = x; + y = hash_descriptor[x].hashsize; + } + } + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return z; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_hash_any.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_find_hash_id.c + Find hash by ID, Tom St Denis + */ + +/** + Find a hash by ID number + @param ID The ID (not same as index) of the hash to find + @return >= 0 if found, -1 if not present + */ +int find_hash_id(unsigned char ID) { + int x; + + LTC_MUTEX_LOCK(<c_hash_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if (hash_descriptor[x].ID == ID) { + x = (hash_descriptor[x].name == NULL) ? -1 : x; + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return x; + } + } + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_hash_id.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_find_hash_oid.c + Find a hash, Tom St Denis + */ + +int find_hash_oid(const unsigned long *ID, unsigned long IDlen) { + int x; + + LTC_ARGCHK(ID != NULL); + LTC_MUTEX_LOCK(<c_hash_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if ((hash_descriptor[x].name != NULL) && (hash_descriptor[x].OIDlen == IDlen) && !XMEMCMP(hash_descriptor[x].OID, ID, sizeof(unsigned long) * IDlen)) { + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return x; + } + } + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_hash_oid.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_find_prng.c + Find a PRNG, Tom St Denis + */ + +/** + Find a registered PRNG by name + @param name The name of the PRNG to look for + @return >= 0 if found, -1 if not present + */ +int find_prng(const char *name) { + int x; + + LTC_ARGCHK(name != NULL); + LTC_MUTEX_LOCK(<c_prng_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if ((prng_descriptor[x].name != NULL) && (XSTRCMP(prng_descriptor[x].name, name) == 0)) { + LTC_MUTEX_UNLOCK(<c_prng_mutex); + return x; + } + } + LTC_MUTEX_UNLOCK(<c_prng_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_prng.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#include + +/** + @file crypt_fsa.c + LibTomCrypt FULL SPEED AHEAD!, Tom St Denis + */ + +/* format is ltc_mp, cipher_desc, [cipher_desc], NULL, hash_desc, [hash_desc], NULL, prng_desc, [prng_desc], NULL */ +int crypt_fsa(void *mp, ...) { + int err; + va_list args; + void *p; + + va_start(args, mp); + if (mp != NULL) { + XMEMCPY(<c_mp, mp, sizeof(ltc_mp)); + } + + while ((p = va_arg(args, void *)) != NULL) { + if ((err = register_cipher(p)) != CRYPT_OK) { + va_end(args); + return err; + } + } + + while ((p = va_arg(args, void *)) != NULL) { + if ((err = register_hash(p)) != CRYPT_OK) { + va_end(args); + return err; + } + } + + while ((p = va_arg(args, void *)) != NULL) { + if ((err = register_prng(p)) != CRYPT_OK) { + va_end(args); + return err; + } + } + + va_end(args); + return CRYPT_OK; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_fsa.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_hash_descriptor.c + Stores the hash descriptor table, Tom St Denis + */ + +struct ltc_hash_descriptor hash_descriptor[TAB_SIZE] = { + { NULL, 0, 0, 0, { 0 }, 0, NULL, NULL, NULL, NULL, NULL } +}; + +LTC_MUTEX_GLOBAL(ltc_hash_mutex) + + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_hash_descriptor.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_hash_is_valid.c + Determine if hash is valid, Tom St Denis + */ + +/* + Test if a hash index is valid + @param idx The index of the hash to search for + @return CRYPT_OK if valid + */ +int hash_is_valid(int idx) { + LTC_MUTEX_LOCK(<c_hash_mutex); + if ((idx < 0) || (idx >= TAB_SIZE) || (hash_descriptor[idx].name == NULL)) { + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return CRYPT_INVALID_HASH; + } + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return CRYPT_OK; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_hash_is_valid.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +ltc_math_descriptor ltc_mp; + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_prng_descriptor.c + Stores the PRNG descriptors, Tom St Denis + */ +struct ltc_prng_descriptor prng_descriptor[TAB_SIZE] = { + { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +}; + +LTC_MUTEX_GLOBAL(ltc_prng_mutex) + + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_prng_descriptor.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_prng_is_valid.c + Determine if PRNG is valid, Tom St Denis + */ + +/* + Test if a PRNG index is valid + @param idx The index of the PRNG to search for + @return CRYPT_OK if valid + */ +int prng_is_valid(int idx) { + LTC_MUTEX_LOCK(<c_prng_mutex); + if ((idx < 0) || (idx >= TAB_SIZE) || (prng_descriptor[idx].name == NULL)) { + LTC_MUTEX_UNLOCK(<c_prng_mutex); + return CRYPT_INVALID_PRNG; + } + LTC_MUTEX_UNLOCK(<c_prng_mutex); + return CRYPT_OK; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_prng_is_valid.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_register_cipher.c + Register a cipher, Tom St Denis + */ + +/** + Register a cipher with the descriptor table + @param cipher The cipher you wish to register + @return value >= 0 if successfully added (or already present), -1 if unsuccessful + */ +int register_cipher(const struct ltc_cipher_descriptor *cipher) { + int x; + + LTC_ARGCHK(cipher != NULL); + + /* is it already registered? */ + LTC_MUTEX_LOCK(<c_cipher_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if ((cipher_descriptor[x].name != NULL) && (cipher_descriptor[x].ID == cipher->ID)) { + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return x; + } + } + + /* find a blank spot */ + for (x = 0; x < TAB_SIZE; x++) { + if (cipher_descriptor[x].name == NULL) { + XMEMCPY(&cipher_descriptor[x], cipher, sizeof(struct ltc_cipher_descriptor)); + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return x; + } + } + + /* no spot */ + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_register_cipher.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_register_hash.c + Register a HASH, Tom St Denis + */ + +/** + Register a hash with the descriptor table + @param hash The hash you wish to register + @return value >= 0 if successfully added (or already present), -1 if unsuccessful + */ +int register_hash(const struct ltc_hash_descriptor *hash) { + int x; + + LTC_ARGCHK(hash != NULL); + + /* is it already registered? */ + LTC_MUTEX_LOCK(<c_hash_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if (XMEMCMP(&hash_descriptor[x], hash, sizeof(struct ltc_hash_descriptor)) == 0) { + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return x; + } + } + + /* find a blank spot */ + for (x = 0; x < TAB_SIZE; x++) { + if (hash_descriptor[x].name == NULL) { + XMEMCPY(&hash_descriptor[x], hash, sizeof(struct ltc_hash_descriptor)); + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return x; + } + } + + /* no spot */ + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_register_hash.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_register_prng.c + Register a PRNG, Tom St Denis + */ + +/** + Register a PRNG with the descriptor table + @param prng The PRNG you wish to register + @return value >= 0 if successfully added (or already present), -1 if unsuccessful + */ +int register_prng(const struct ltc_prng_descriptor *prng) { + int x; + + LTC_ARGCHK(prng != NULL); + + /* is it already registered? */ + LTC_MUTEX_LOCK(<c_prng_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if (XMEMCMP(&prng_descriptor[x], prng, sizeof(struct ltc_prng_descriptor)) == 0) { + LTC_MUTEX_UNLOCK(<c_prng_mutex); + return x; + } + } + + /* find a blank spot */ + for (x = 0; x < TAB_SIZE; x++) { + if (prng_descriptor[x].name == NULL) { + XMEMCPY(&prng_descriptor[x], prng, sizeof(struct ltc_prng_descriptor)); + LTC_MUTEX_UNLOCK(<c_prng_mutex); + return x; + } + } + + /* no spot */ + LTC_MUTEX_UNLOCK(<c_prng_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_register_prng.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_unregister_cipher.c + Unregister a cipher, Tom St Denis + */ + +/** + Unregister a cipher from the descriptor table + @param cipher The cipher descriptor to remove + @return CRYPT_OK on success + */ +int unregister_cipher(const struct ltc_cipher_descriptor *cipher) { + int x; + + LTC_ARGCHK(cipher != NULL); + + /* is it already registered? */ + LTC_MUTEX_LOCK(<c_cipher_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if (XMEMCMP(&cipher_descriptor[x], cipher, sizeof(struct ltc_cipher_descriptor)) == 0) { + cipher_descriptor[x].name = NULL; + cipher_descriptor[x].ID = 255; + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return CRYPT_OK; + } + } + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return CRYPT_ERROR; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_unregister_cipher.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_unregister_hash.c + Unregister a hash, Tom St Denis + */ + +/** + Unregister a hash from the descriptor table + @param hash The hash descriptor to remove + @return CRYPT_OK on success + */ +int unregister_hash(const struct ltc_hash_descriptor *hash) { + int x; + + LTC_ARGCHK(hash != NULL); + + /* is it already registered? */ + LTC_MUTEX_LOCK(<c_hash_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if (XMEMCMP(&hash_descriptor[x], hash, sizeof(struct ltc_hash_descriptor)) == 0) { + hash_descriptor[x].name = NULL; + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return CRYPT_OK; + } + } + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return CRYPT_ERROR; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_unregister_hash.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_unregister_prng.c + Unregister a PRNG, Tom St Denis + */ + +/** + Unregister a PRNG from the descriptor table + @param prng The PRNG descriptor to remove + @return CRYPT_OK on success + */ +int unregister_prng(const struct ltc_prng_descriptor *prng) { + int x; + + LTC_ARGCHK(prng != NULL); + + /* is it already registered? */ + LTC_MUTEX_LOCK(<c_prng_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if (XMEMCMP(&prng_descriptor[x], prng, sizeof(struct ltc_prng_descriptor)) != 0) { + prng_descriptor[x].name = NULL; + LTC_MUTEX_UNLOCK(<c_prng_mutex); + return CRYPT_OK; + } + } + LTC_MUTEX_UNLOCK(<c_prng_mutex); + return CRYPT_ERROR; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_unregister_prng.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_bit_string.c + ASN.1 DER, encode a BIT STRING, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store a BIT STRING + @param in The DER encoded BIT STRING + @param inlen The size of the DER BIT STRING + @param out [out] The array of bits stored (one per char) + @param outlen [in/out] The number of bits stored + @return CRYPT_OK if successful + */ +int der_decode_bit_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long dlen, blen, x, y; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* packet must be at least 4 bytes */ + if (inlen < 4) { + return CRYPT_INVALID_ARG; + } + + /* check for 0x03 */ + if ((in[0] & 0x1F) != 0x03) { + return CRYPT_INVALID_PACKET; + } + + /* offset in the data */ + x = 1; + + /* get the length of the data */ + if (in[x] & 0x80) { + /* long format get number of length bytes */ + y = in[x++] & 0x7F; + + /* invalid if 0 or > 2 */ + if ((y == 0) || (y > 2)) { + return CRYPT_INVALID_PACKET; + } + + /* read the data len */ + dlen = 0; + while (y--) { + dlen = (dlen << 8) | (unsigned long)in[x++]; + } + } else { + /* short format */ + dlen = in[x++] & 0x7F; + } + + /* is the data len too long or too short? */ + if ((dlen == 0) || (dlen + x > inlen)) { + return CRYPT_INVALID_PACKET; + } + + /* get padding count */ + blen = ((dlen - 1) << 3) - (in[x++] & 7); + + /* too many bits? */ + if (blen > *outlen) { + *outlen = blen; + return CRYPT_BUFFER_OVERFLOW; + } + + /* decode/store the bits */ + for (y = 0; y < blen; y++) { + out[y] = (in[x] & (1 << (7 - (y & 7)))) ? 1 : 0; + if ((y & 7) == 7) { + ++x; + } + } + + /* we done */ + *outlen = blen; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/bit/der_decode_bit_string.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_boolean.c + ASN.1 DER, decode a BOOLEAN, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Read a BOOLEAN + @param in The destination for the DER encoded BOOLEAN + @param inlen The size of the DER BOOLEAN + @param out [out] The boolean to decode + @return CRYPT_OK if successful + */ +int der_decode_boolean(const unsigned char *in, unsigned long inlen, + int *out) { + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + + if ((inlen != 3) || (in[0] != 0x01) || (in[1] != 0x01) || ((in[2] != 0x00) && (in[2] != 0xFF))) { + return CRYPT_INVALID_ARG; + } + + *out = (in[2] == 0xFF) ? 1 : 0; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/boolean/der_decode_boolean.c,v $ */ +/* $Revision: 1.2 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_choice.c + ASN.1 DER, decode a CHOICE, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Decode a CHOICE + @param in The DER encoded input + @param inlen [in/out] The size of the input and resulting size of read type + @param list The list of items to decode + @param outlen The number of items in the list + @return CRYPT_OK on success + */ +int der_decode_choice(const unsigned char *in, unsigned long *inlen, + ltc_asn1_list *list, unsigned long outlen) { + unsigned long size, x, z; + void *data; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(inlen != NULL); + LTC_ARGCHK(list != NULL); + + /* get blk size */ + if (*inlen < 2) { + return CRYPT_INVALID_PACKET; + } + + /* set all of the "used" flags to zero */ + for (x = 0; x < outlen; x++) { + list[x].used = 0; + } + + /* now scan until we have a winner */ + for (x = 0; x < outlen; x++) { + size = list[x].size; + data = list[x].data; + + switch (list[x].type) { + case LTC_ASN1_INTEGER: + if (der_decode_integer(in, *inlen, data) == CRYPT_OK) { + if (der_length_integer(data, &z) == CRYPT_OK) { + list[x].used = 1; + *inlen = z; + return CRYPT_OK; + } + } + break; + + case LTC_ASN1_SHORT_INTEGER: + if (der_decode_short_integer(in, *inlen, data) == CRYPT_OK) { + if (der_length_short_integer(size, &z) == CRYPT_OK) { + list[x].used = 1; + *inlen = z; + return CRYPT_OK; + } + } + break; + + case LTC_ASN1_BIT_STRING: + if (der_decode_bit_string(in, *inlen, data, &size) == CRYPT_OK) { + if (der_length_bit_string(size, &z) == CRYPT_OK) { + list[x].used = 1; + list[x].size = size; + *inlen = z; + return CRYPT_OK; + } + } + break; + + case LTC_ASN1_OCTET_STRING: + if (der_decode_octet_string(in, *inlen, data, &size) == CRYPT_OK) { + if (der_length_octet_string(size, &z) == CRYPT_OK) { + list[x].used = 1; + list[x].size = size; + *inlen = z; + return CRYPT_OK; + } + } + break; + + case LTC_ASN1_NULL: + if ((*inlen == 2) && (in[x] == 0x05) && (in[x + 1] == 0x00)) { + *inlen = 2; + list[x].used = 1; + return CRYPT_OK; + } + break; + + case LTC_ASN1_OBJECT_IDENTIFIER: + if (der_decode_object_identifier(in, *inlen, data, &size) == CRYPT_OK) { + if (der_length_object_identifier(data, size, &z) == CRYPT_OK) { + list[x].used = 1; + list[x].size = size; + *inlen = z; + return CRYPT_OK; + } + } + break; + + case LTC_ASN1_IA5_STRING: + if (der_decode_ia5_string(in, *inlen, data, &size) == CRYPT_OK) { + if (der_length_ia5_string(data, size, &z) == CRYPT_OK) { + list[x].used = 1; + list[x].size = size; + *inlen = z; + return CRYPT_OK; + } + } + break; + + + case LTC_ASN1_PRINTABLE_STRING: + if (der_decode_printable_string(in, *inlen, data, &size) == CRYPT_OK) { + if (der_length_printable_string(data, size, &z) == CRYPT_OK) { + list[x].used = 1; + list[x].size = size; + *inlen = z; + return CRYPT_OK; + } + } + break; + + case LTC_ASN1_UTF8_STRING: + if (der_decode_utf8_string(in, *inlen, data, &size) == CRYPT_OK) { + if (der_length_utf8_string(data, size, &z) == CRYPT_OK) { + list[x].used = 1; + list[x].size = size; + *inlen = z; + return CRYPT_OK; + } + } + break; + + case LTC_ASN1_UTCTIME: + z = *inlen; + if (der_decode_utctime(in, &z, data) == CRYPT_OK) { + list[x].used = 1; + *inlen = z; + return CRYPT_OK; + } + break; + + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + case LTC_ASN1_SEQUENCE: + if (der_decode_sequence(in, *inlen, data, size) == CRYPT_OK) { + if (der_length_sequence(data, size, &z) == CRYPT_OK) { + list[x].used = 1; + *inlen = z; + return CRYPT_OK; + } + } + break; + + default: + return CRYPT_INVALID_ARG; + } + } + + return CRYPT_INVALID_PACKET; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/choice/der_decode_choice.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_ia5_string.c + ASN.1 DER, encode a IA5 STRING, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store a IA5 STRING + @param in The DER encoded IA5 STRING + @param inlen The size of the DER IA5 STRING + @param out [out] The array of octets stored (one per char) + @param outlen [in/out] The number of octets stored + @return CRYPT_OK if successful + */ +int der_decode_ia5_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long x, y, len; + int t; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* must have header at least */ + if (inlen < 2) { + return CRYPT_INVALID_PACKET; + } + + /* check for 0x16 */ + if ((in[0] & 0x1F) != 0x16) { + return CRYPT_INVALID_PACKET; + } + x = 1; + + /* decode the length */ + if (in[x] & 0x80) { + /* valid # of bytes in length are 1,2,3 */ + y = in[x] & 0x7F; + if ((y == 0) || (y > 3) || ((x + y) > inlen)) { + return CRYPT_INVALID_PACKET; + } + + /* read the length in */ + len = 0; + ++x; + while (y--) { + len = (len << 8) | in[x++]; + } + } else { + len = in[x++] & 0x7F; + } + + /* is it too long? */ + if (len > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + if (len + x > inlen) { + return CRYPT_INVALID_PACKET; + } + + /* read the data */ + for (y = 0; y < len; y++) { + t = der_ia5_value_decode(in[x++]); + if (t == -1) { + return CRYPT_INVALID_ARG; + } + out[y] = t; + } + + *outlen = y; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/ia5/der_decode_ia5_string.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_integer.c + ASN.1 DER, decode an integer, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Read a mp_int integer + @param in The DER encoded data + @param inlen Size of DER encoded data + @param num The first mp_int to decode + @return CRYPT_OK if successful + */ +int der_decode_integer(const unsigned char *in, unsigned long inlen, void *num) { + unsigned long x, y, z; + int err; + + LTC_ARGCHK(num != NULL); + LTC_ARGCHK(in != NULL); + + /* min DER INTEGER is 0x02 01 00 == 0 */ + if (inlen < (1 + 1 + 1)) { + return CRYPT_INVALID_PACKET; + } + + /* ok expect 0x02 when we AND with 0001 1111 [1F] */ + x = 0; + if ((in[x++] & 0x1F) != 0x02) { + return CRYPT_INVALID_PACKET; + } + + /* now decode the len stuff */ + z = in[x++]; + + if ((z & 0x80) == 0x00) { + /* short form */ + + /* will it overflow? */ + if (x + z > inlen) { + return CRYPT_INVALID_PACKET; + } + + /* no so read it */ + if ((err = mp_read_unsigned_bin(num, (unsigned char *)in + x, z)) != CRYPT_OK) { + return err; + } + } else { + /* long form */ + z &= 0x7F; + + /* will number of length bytes overflow? (or > 4) */ + if (((x + z) > inlen) || (z > 4) || (z == 0)) { + return CRYPT_INVALID_PACKET; + } + + /* now read it in */ + y = 0; + while (z--) { + y = ((unsigned long)(in[x++])) | (y << 8); + } + + /* now will reading y bytes overrun? */ + if ((x + y) > inlen) { + return CRYPT_INVALID_PACKET; + } + + /* no so read it */ + if ((err = mp_read_unsigned_bin(num, (unsigned char *)in + x, y)) != CRYPT_OK) { + return err; + } + } + + /* see if it's negative */ + if (in[x] & 0x80) { + void *tmp; + if (mp_init(&tmp) != CRYPT_OK) { + return CRYPT_MEM; + } + + if ((mp_2expt(tmp, mp_count_bits(num)) != CRYPT_OK) || (mp_sub(num, tmp, num) != CRYPT_OK)) { + mp_clear(tmp); + return CRYPT_MEM; + } + mp_clear(tmp); + } + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/integer/der_decode_integer.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_object_identifier.c + ASN.1 DER, Decode Object Identifier, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Decode OID data and store the array of integers in words + @param in The OID DER encoded data + @param inlen The length of the OID data + @param words [out] The destination of the OID words + @param outlen [in/out] The number of OID words + @return CRYPT_OK if successful + */ +int der_decode_object_identifier(const unsigned char *in, unsigned long inlen, + unsigned long *words, unsigned long *outlen) { + unsigned long x, y, t, len; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(words != NULL); + LTC_ARGCHK(outlen != NULL); + + /* header is at least 3 bytes */ + if (inlen < 3) { + return CRYPT_INVALID_PACKET; + } + + /* must be room for at least two words */ + if (*outlen < 2) { + return CRYPT_BUFFER_OVERFLOW; + } + + /* decode the packet header */ + x = 0; + if ((in[x++] & 0x1F) != 0x06) { + return CRYPT_INVALID_PACKET; + } + + /* get the length */ + if (in[x] < 128) { + len = in[x++]; + } else { + if ((in[x] < 0x81) || (in[x] > 0x82)) { + return CRYPT_INVALID_PACKET; + } + y = in[x++] & 0x7F; + len = 0; + while (y--) { + len = (len << 8) | (unsigned long)in[x++]; + } + } + + if ((len < 1) || ((len + x) > inlen)) { + return CRYPT_INVALID_PACKET; + } + + /* decode words */ + y = 0; + t = 0; + while (len--) { + t = (t << 7) | (in[x] & 0x7F); + if (!(in[x++] & 0x80)) { + /* store t */ + if (y >= *outlen) { + return CRYPT_BUFFER_OVERFLOW; + } + if (y == 0) { + words[0] = t / 40; + words[1] = t % 40; + y = 2; + } else { + words[y++] = t; + } + t = 0; + } + } + + *outlen = y; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/object_identifier/der_decode_object_identifier.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_octet_string.c + ASN.1 DER, encode a OCTET STRING, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store a OCTET STRING + @param in The DER encoded OCTET STRING + @param inlen The size of the DER OCTET STRING + @param out [out] The array of octets stored (one per char) + @param outlen [in/out] The number of octets stored + @return CRYPT_OK if successful + */ +int der_decode_octet_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long x, y, len; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* must have header at least */ + if (inlen < 2) { + return CRYPT_INVALID_PACKET; + } + + /* check for 0x04 */ + if ((in[0] & 0x1F) != 0x04) { + return CRYPT_INVALID_PACKET; + } + x = 1; + + /* decode the length */ + if (in[x] & 0x80) { + /* valid # of bytes in length are 1,2,3 */ + y = in[x] & 0x7F; + if ((y == 0) || (y > 3) || ((x + y) > inlen)) { + return CRYPT_INVALID_PACKET; + } + + /* read the length in */ + len = 0; + ++x; + while (y--) { + len = (len << 8) | in[x++]; + } + } else { + len = in[x++] & 0x7F; + } + + /* is it too long? */ + if (len > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + if (len + x > inlen) { + return CRYPT_INVALID_PACKET; + } + + /* read the data */ + for (y = 0; y < len; y++) { + out[y] = in[x++]; + } + + *outlen = y; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/octet/der_decode_octet_string.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_printable_string.c + ASN.1 DER, encode a printable STRING, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store a printable STRING + @param in The DER encoded printable STRING + @param inlen The size of the DER printable STRING + @param out [out] The array of octets stored (one per char) + @param outlen [in/out] The number of octets stored + @return CRYPT_OK if successful + */ +int der_decode_printable_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long x, y, len; + int t; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* must have header at least */ + if (inlen < 2) { + return CRYPT_INVALID_PACKET; + } + + /* check for 0x13 */ + if ((in[0] & 0x1F) != 0x13) { + return CRYPT_INVALID_PACKET; + } + x = 1; + + /* decode the length */ + if (in[x] & 0x80) { + /* valid # of bytes in length are 1,2,3 */ + y = in[x] & 0x7F; + if ((y == 0) || (y > 3) || ((x + y) > inlen)) { + return CRYPT_INVALID_PACKET; + } + + /* read the length in */ + len = 0; + ++x; + while (y--) { + len = (len << 8) | in[x++]; + } + } else { + len = in[x++] & 0x7F; + } + + /* is it too long? */ + if (len > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + if (len + x > inlen) { + return CRYPT_INVALID_PACKET; + } + + /* read the data */ + for (y = 0; y < len; y++) { + t = der_printable_value_decode(in[x++]); + if (t == -1) { + return CRYPT_INVALID_ARG; + } + out[y] = t; + } + + *outlen = y; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/printable_string/der_decode_printable_string.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#include + + +/** + @file der_decode_sequence_ex.c + ASN.1 DER, decode a SEQUENCE, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Decode a SEQUENCE + @param in The DER encoded input + @param inlen The size of the input + @param list The list of items to decode + @param outlen The number of items in the list + @param ordered Search an unordered or ordered list + @return CRYPT_OK on success + */ +int der_decode_sequence_ex(const unsigned char *in, unsigned long inlen, + ltc_asn1_list *list, unsigned long outlen, int ordered) { + int err, type; + unsigned long size, x, y, z, i, blksize; + void *data; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(list != NULL); + + /* get blk size */ + if (inlen < 2) { + return CRYPT_INVALID_PACKET; + } + + /* sequence type? We allow 0x30 SEQUENCE and 0x31 SET since fundamentally they're the same structure */ + x = 0; + if ((in[x] != 0x30) && (in[x] != 0x31)) { + return CRYPT_INVALID_PACKET; + } + ++x; + + if (in[x] < 128) { + blksize = in[x++]; + } else if (in[x] & 0x80) { + if ((in[x] < 0x81) || (in[x] > 0x83)) { + return CRYPT_INVALID_PACKET; + } + y = in[x++] & 0x7F; + + /* would reading the len bytes overrun? */ + if (x + y > inlen) { + return CRYPT_INVALID_PACKET; + } + + /* read len */ + blksize = 0; + while (y--) { + blksize = (blksize << 8) | (unsigned long)in[x++]; + } + } + + /* would this blksize overflow? */ + if (x + blksize > inlen) { + return CRYPT_INVALID_PACKET; + } + + /* mark all as unused */ + for (i = 0; i < outlen; i++) { + list[i].used = 0; + } + + /* ok read data */ + inlen = blksize; + for (i = 0; i < outlen; i++) { + z = 0; + type = list[i].type; + size = list[i].size; + data = list[i].data; + if (!ordered && (list[i].used == 1)) { + continue; + } + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + z = inlen; + if ((err = der_decode_boolean(in + x, z, ((int *)data))) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = der_length_boolean(&z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + case LTC_ASN1_INTEGER: + z = inlen; + if ((err = der_decode_integer(in + x, z, data)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + if ((err = der_length_integer(data, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + case LTC_ASN1_SHORT_INTEGER: + z = inlen; + if ((err = der_decode_short_integer(in + x, z, data)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + if ((err = der_length_short_integer(((unsigned long *)data)[0], &z)) != CRYPT_OK) { + goto LBL_ERR; + } + + break; + + case LTC_ASN1_BIT_STRING: + z = inlen; + if ((err = der_decode_bit_string(in + x, z, data, &size)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + list[i].size = size; + if ((err = der_length_bit_string(size, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + case LTC_ASN1_OCTET_STRING: + z = inlen; + if ((err = der_decode_octet_string(in + x, z, data, &size)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + list[i].size = size; + if ((err = der_length_octet_string(size, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + case LTC_ASN1_NULL: + if ((inlen < 2) || (in[x] != 0x05) || (in[x + 1] != 0x00)) { + if (!ordered) { + continue; + } + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } + z = 2; + break; + + case LTC_ASN1_OBJECT_IDENTIFIER: + z = inlen; + if ((err = der_decode_object_identifier(in + x, z, data, &size)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + list[i].size = size; + if ((err = der_length_object_identifier(data, size, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + case LTC_ASN1_IA5_STRING: + z = inlen; + if ((err = der_decode_ia5_string(in + x, z, data, &size)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + list[i].size = size; + if ((err = der_length_ia5_string(data, size, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + + case LTC_ASN1_PRINTABLE_STRING: + z = inlen; + if ((err = der_decode_printable_string(in + x, z, data, &size)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + list[i].size = size; + if ((err = der_length_printable_string(data, size, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + case LTC_ASN1_UTF8_STRING: + z = inlen; + if ((err = der_decode_utf8_string(in + x, z, data, &size)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + list[i].size = size; + if ((err = der_length_utf8_string(data, size, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + case LTC_ASN1_UTCTIME: + z = inlen; + if ((err = der_decode_utctime(in + x, &z, data)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + break; + + case LTC_ASN1_SET: + z = inlen; + if ((err = der_decode_set(in + x, z, data, size)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + if ((err = der_length_sequence(data, size, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + case LTC_ASN1_SETOF: + case LTC_ASN1_SEQUENCE: + /* detect if we have the right type */ + if (((type == LTC_ASN1_SETOF) && ((in[x] & 0x3F) != 0x31)) || ((type == LTC_ASN1_SEQUENCE) && ((in[x] & 0x3F) != 0x30))) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } + + z = inlen; + if ((err = der_decode_sequence(in + x, z, data, size)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + if ((err = der_length_sequence(data, size, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + + case LTC_ASN1_CHOICE: + z = inlen; + if ((err = der_decode_choice(in + x, &z, data, size)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + break; + + default: + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + x += z; + inlen -= z; + list[i].used = 1; + if (!ordered) { + /* restart the decoder */ + i = -1; + } + } + + for (i = 0; i < outlen; i++) { + if (list[i].used == 0) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } + } + err = CRYPT_OK; + +LBL_ERR: + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_ex.c,v $ */ +/* $Revision: 1.16 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_sequence_flexi.c + ASN.1 DER, decode an array of ASN.1 types with a flexi parser, Tom St Denis + */ + +#ifdef LTC_DER + +static unsigned long fetch_length(const unsigned char *in, unsigned long inlen) { + unsigned long x, y, z; + + y = 0; + + /* skip type and read len */ + if (inlen < 2) { + return 0xFFFFFFFF; + } + ++in; + ++y; + + /* read len */ + x = *in++; + ++y; + + /* <128 means literal */ + if (x < 128) { + return x + y; + } + x &= 0x7F; /* the lower 7 bits are the length of the length */ + inlen -= 2; + + /* len means len of len! */ + if ((x == 0) || (x > 4) || (x > inlen)) { + return 0xFFFFFFFF; + } + + y += x; + z = 0; + while (x--) { + z = (z << 8) | ((unsigned long)*in); + ++in; + } + return z + y; +} + +/** + ASN.1 DER Flexi(ble) decoder will decode arbitrary DER packets and create a linked list of the decoded elements. + @param in The input buffer + @param inlen [in/out] The length of the input buffer and on output the amount of decoded data + @param out [out] A pointer to the linked list + @return CRYPT_OK on success. + */ +int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out) { + ltc_asn1_list *l; + unsigned long err, type, len, totlen, x, y; + void *realloc_tmp; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(inlen != NULL); + LTC_ARGCHK(out != NULL); + + l = NULL; + totlen = 0; + + /* scan the input and and get lengths and what not */ + while (*inlen) { + /* read the type byte */ + type = *in; + + /* fetch length */ + len = fetch_length(in, *inlen); + if (len > *inlen) { + err = CRYPT_INVALID_PACKET; + goto error; + } + + /* alloc new link */ + if (l == NULL) { + l = XCALLOC(1, sizeof(*l)); + if (l == NULL) { + err = CRYPT_MEM; + goto error; + } + } else { + l->next = XCALLOC(1, sizeof(*l)); + if (l->next == NULL) { + err = CRYPT_MEM; + goto error; + } + l->next->prev = l; + l = l->next; + } + + /* now switch on type */ + switch (type) { + case 0x01: /* BOOLEAN */ + l->type = LTC_ASN1_BOOLEAN; + l->size = 1; + l->data = XCALLOC(1, sizeof(int)); + + if ((err = der_decode_boolean(in, *inlen, l->data)) != CRYPT_OK) { + goto error; + } + + if ((err = der_length_boolean(&len)) != CRYPT_OK) { + goto error; + } + break; + + case 0x02: /* INTEGER */ + /* init field */ + l->type = LTC_ASN1_INTEGER; + l->size = 1; + if ((err = mp_init(&l->data)) != CRYPT_OK) { + goto error; + } + + /* decode field */ + if ((err = der_decode_integer(in, *inlen, l->data)) != CRYPT_OK) { + goto error; + } + + /* calc length of object */ + if ((err = der_length_integer(l->data, &len)) != CRYPT_OK) { + goto error; + } + break; + + case 0x03: /* BIT */ + /* init field */ + l->type = LTC_ASN1_BIT_STRING; + l->size = len * 8; /* *8 because we store decoded bits one per char and they are encoded 8 per char. */ + + if ((l->data = XCALLOC(1, l->size)) == NULL) { + err = CRYPT_MEM; + goto error; + } + + if ((err = der_decode_bit_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { + goto error; + } + + if ((err = der_length_bit_string(l->size, &len)) != CRYPT_OK) { + goto error; + } + break; + + case 0x04: /* OCTET */ + + /* init field */ + l->type = LTC_ASN1_OCTET_STRING; + l->size = len; + + if ((l->data = XCALLOC(1, l->size)) == NULL) { + err = CRYPT_MEM; + goto error; + } + + if ((err = der_decode_octet_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { + goto error; + } + + if ((err = der_length_octet_string(l->size, &len)) != CRYPT_OK) { + goto error; + } + break; + + case 0x05: /* NULL */ + + /* valid NULL is 0x05 0x00 */ + if ((in[0] != 0x05) || (in[1] != 0x00)) { + err = CRYPT_INVALID_PACKET; + goto error; + } + + /* simple to store ;-) */ + l->type = LTC_ASN1_NULL; + l->data = NULL; + l->size = 0; + len = 2; + + break; + + case 0x06: /* OID */ + + /* init field */ + l->type = LTC_ASN1_OBJECT_IDENTIFIER; + l->size = len; + + if ((l->data = XCALLOC(len, sizeof(unsigned long))) == NULL) { + err = CRYPT_MEM; + goto error; + } + + if ((err = der_decode_object_identifier(in, *inlen, l->data, &l->size)) != CRYPT_OK) { + goto error; + } + + if ((err = der_length_object_identifier(l->data, l->size, &len)) != CRYPT_OK) { + goto error; + } + + /* resize it to save a bunch of mem */ + if ((realloc_tmp = XREALLOC(l->data, l->size * sizeof(unsigned long))) == NULL) { + /* out of heap but this is not an error */ + break; + } + l->data = realloc_tmp; + break; + + case 0x0C: /* UTF8 */ + + /* init field */ + l->type = LTC_ASN1_UTF8_STRING; + l->size = len; + + if ((l->data = XCALLOC(sizeof(wchar_t), l->size)) == NULL) { + err = CRYPT_MEM; + goto error; + } + + if ((err = der_decode_utf8_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { + goto error; + } + + if ((err = der_length_utf8_string(l->data, l->size, &len)) != CRYPT_OK) { + goto error; + } + break; + + case 0x13: /* PRINTABLE */ + + /* init field */ + l->type = LTC_ASN1_PRINTABLE_STRING; + l->size = len; + + if ((l->data = XCALLOC(1, l->size)) == NULL) { + err = CRYPT_MEM; + goto error; + } + + if ((err = der_decode_printable_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { + goto error; + } + + if ((err = der_length_printable_string(l->data, l->size, &len)) != CRYPT_OK) { + goto error; + } + break; + + case 0x16: /* IA5 */ + + /* init field */ + l->type = LTC_ASN1_IA5_STRING; + l->size = len; + + if ((l->data = XCALLOC(1, l->size)) == NULL) { + err = CRYPT_MEM; + goto error; + } + + if ((err = der_decode_ia5_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { + goto error; + } + + if ((err = der_length_ia5_string(l->data, l->size, &len)) != CRYPT_OK) { + goto error; + } + break; + + case 0x17: /* UTC TIME */ + + /* init field */ + l->type = LTC_ASN1_UTCTIME; + l->size = 1; + + if ((l->data = XCALLOC(1, sizeof(ltc_utctime))) == NULL) { + err = CRYPT_MEM; + goto error; + } + + len = *inlen; + if ((err = der_decode_utctime(in, &len, l->data)) != CRYPT_OK) { + goto error; + } + + if ((err = der_length_utctime(l->data, &len)) != CRYPT_OK) { + goto error; + } + break; + + case 0x30: /* SEQUENCE */ + case 0x31: /* SET */ + + /* init field */ + l->type = (type == 0x30) ? LTC_ASN1_SEQUENCE : LTC_ASN1_SET; + + /* we have to decode the SEQUENCE header and get it's length */ + + /* move past type */ + ++in; + --(*inlen); + + /* read length byte */ + x = *in++; + --(*inlen); + + /* smallest SEQUENCE/SET header */ + y = 2; + + /* now if it's > 127 the next bytes are the length of the length */ + if (x > 128) { + x &= 0x7F; + in += x; + *inlen -= x; + + /* update sequence header len */ + y += x; + } + + /* Sequence elements go as child */ + len = len - y; + if ((err = der_decode_sequence_flexi(in, &len, &(l->child))) != CRYPT_OK) { + goto error; + } + + /* len update */ + totlen += y; + + /* link them up y0 */ + l->child->parent = l; + + break; + + default: + /* invalid byte ... this is a soft error */ + /* remove link */ + l = l->prev; + XFREE(l->next); + l->next = NULL; + goto outside; + } + + /* advance pointers */ + totlen += len; + in += len; + *inlen -= len; + } + +outside: + + /* rewind l please */ + while (l->prev != NULL || l->parent != NULL) { + if (l->parent != NULL) { + l = l->parent; + } else { + l = l->prev; + } + } + + /* return */ + *out = l; + *inlen = totlen; + return CRYPT_OK; + +error: + /* free list */ + der_sequence_free(l); + + return err; +} +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c,v $ */ +/* $Revision: 1.26 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#include + + +/** + @file der_decode_sequence_multi.c + ASN.1 DER, decode a SEQUENCE, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Decode a SEQUENCE type using a VA list + @param in Input buffer + @param inlen Length of input in octets + @remark <...> is of the form (int, unsigned long, void*) + @return CRYPT_OK on success + */ +int der_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...) { + int err, type; + unsigned long size, x; + void *data; + va_list args; + ltc_asn1_list *list; + + LTC_ARGCHK(in != NULL); + + /* get size of output that will be required */ + va_start(args, inlen); + x = 0; + for ( ; ; ) { + type = va_arg(args, int); + size = va_arg(args, unsigned long); + data = va_arg(args, void *); + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + case LTC_ASN1_INTEGER: + case LTC_ASN1_SHORT_INTEGER: + case LTC_ASN1_BIT_STRING: + case LTC_ASN1_OCTET_STRING: + case LTC_ASN1_NULL: + case LTC_ASN1_OBJECT_IDENTIFIER: + case LTC_ASN1_IA5_STRING: + case LTC_ASN1_PRINTABLE_STRING: + case LTC_ASN1_UTF8_STRING: + case LTC_ASN1_UTCTIME: + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + case LTC_ASN1_SEQUENCE: + case LTC_ASN1_CHOICE: + ++x; + break; + + default: + va_end(args); + return CRYPT_INVALID_ARG; + } + } + va_end(args); + + /* allocate structure for x elements */ + if (x == 0) { + return CRYPT_NOP; + } + + list = XCALLOC(sizeof(*list), x); + if (list == NULL) { + return CRYPT_MEM; + } + + /* fill in the structure */ + va_start(args, inlen); + x = 0; + for ( ; ; ) { + type = va_arg(args, int); + size = va_arg(args, unsigned long); + data = va_arg(args, void *); + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + case LTC_ASN1_INTEGER: + case LTC_ASN1_SHORT_INTEGER: + case LTC_ASN1_BIT_STRING: + case LTC_ASN1_OCTET_STRING: + case LTC_ASN1_NULL: + case LTC_ASN1_OBJECT_IDENTIFIER: + case LTC_ASN1_IA5_STRING: + case LTC_ASN1_PRINTABLE_STRING: + case LTC_ASN1_UTF8_STRING: + case LTC_ASN1_UTCTIME: + case LTC_ASN1_SEQUENCE: + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + case LTC_ASN1_CHOICE: + list[x].type = type; + list[x].size = size; + list[x++].data = data; + break; + + default: + va_end(args); + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + } + va_end(args); + + err = der_decode_sequence(in, inlen, list, x); +LBL_ERR: + XFREE(list); + return err; +} +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_multi.c,v $ */ +/* $Revision: 1.13 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_short_integer.c + ASN.1 DER, decode an integer, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Read a short integer + @param in The DER encoded data + @param inlen Size of data + @param num [out] The integer to decode + @return CRYPT_OK if successful + */ +int der_decode_short_integer(const unsigned char *in, unsigned long inlen, unsigned long *num) { + unsigned long len, x, y; + + LTC_ARGCHK(num != NULL); + LTC_ARGCHK(in != NULL); + + /* check length */ + if (inlen < 2) { + return CRYPT_INVALID_PACKET; + } + + /* check header */ + x = 0; + if ((in[x++] & 0x1F) != 0x02) { + return CRYPT_INVALID_PACKET; + } + + /* get the packet len */ + len = in[x++]; + + if (x + len > inlen) { + return CRYPT_INVALID_PACKET; + } + + /* read number */ + y = 0; + while (len--) { + y = (y << 8) | (unsigned long)in[x++]; + } + *num = y; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/short_integer/der_decode_short_integer.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_utctime.c + ASN.1 DER, decode a UTCTIME, Tom St Denis + */ + +#ifdef LTC_DER + +static int char_to_int(unsigned char x) { + switch (x) { + case '0': + return 0; + + case '1': + return 1; + + case '2': + return 2; + + case '3': + return 3; + + case '4': + return 4; + + case '5': + return 5; + + case '6': + return 6; + + case '7': + return 7; + + case '8': + return 8; + + case '9': + return 9; + } + return 100; +} + + #define DECODE_V(y, max) \ + y = char_to_int(buf[x]) * 10 + char_to_int(buf[x + 1]); \ + if (y >= max) return CRYPT_INVALID_PACKET; \ + x += 2; + +/** + Decodes a UTC time structure in DER format (reads all 6 valid encoding formats) + @param in Input buffer + @param inlen Length of input buffer in octets + @param out [out] Destination of UTC time structure + @return CRYPT_OK if successful + */ +int der_decode_utctime(const unsigned char *in, unsigned long *inlen, + ltc_utctime *out) { + unsigned char buf[32]; + unsigned long x; + int y; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(inlen != NULL); + LTC_ARGCHK(out != NULL); + + /* check header */ + if ((*inlen < 2UL) || (in[1] >= sizeof(buf)) || ((in[1] + 2UL) > *inlen)) { + return CRYPT_INVALID_PACKET; + } + + /* decode the string */ + for (x = 0; x < in[1]; x++) { + y = der_ia5_value_decode(in[x + 2]); + if (y == -1) { + return CRYPT_INVALID_PACKET; + } + buf[x] = y; + } + *inlen = 2 + x; + + + /* possible encodings are + YYMMDDhhmmZ + YYMMDDhhmm+hh'mm' + YYMMDDhhmm-hh'mm' + YYMMDDhhmmssZ + YYMMDDhhmmss+hh'mm' + YYMMDDhhmmss-hh'mm' + + So let's do a trivial decode upto [including] mm + */ + + x = 0; + DECODE_V(out->YY, 100); + DECODE_V(out->MM, 13); + DECODE_V(out->DD, 32); + DECODE_V(out->hh, 24); + DECODE_V(out->mm, 60); + + /* clear timezone and seconds info */ + out->off_dir = out->off_hh = out->off_mm = out->ss = 0; + + /* now is it Z, +, - or 0-9 */ + if (buf[x] == 'Z') { + return CRYPT_OK; + } else if ((buf[x] == '+') || (buf[x] == '-')) { + out->off_dir = (buf[x++] == '+') ? 0 : 1; + DECODE_V(out->off_hh, 24); + DECODE_V(out->off_mm, 60); + return CRYPT_OK; + } + + /* decode seconds */ + DECODE_V(out->ss, 60); + + /* now is it Z, +, - */ + if (buf[x] == 'Z') { + return CRYPT_OK; + } else if ((buf[x] == '+') || (buf[x] == '-')) { + out->off_dir = (buf[x++] == '+') ? 0 : 1; + DECODE_V(out->off_hh, 24); + DECODE_V(out->off_mm, 60); + return CRYPT_OK; + } else { + return CRYPT_INVALID_PACKET; + } +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utctime/der_decode_utctime.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_utf8_string.c + ASN.1 DER, encode a UTF8 STRING, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store a UTF8 STRING + @param in The DER encoded UTF8 STRING + @param inlen The size of the DER UTF8 STRING + @param out [out] The array of utf8s stored (one per char) + @param outlen [in/out] The number of utf8s stored + @return CRYPT_OK if successful + */ +int der_decode_utf8_string(const unsigned char *in, unsigned long inlen, + wchar_t *out, unsigned long *outlen) { + wchar_t tmp; + unsigned long x, y, z, len; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* must have header at least */ + if (inlen < 2) { + return CRYPT_INVALID_PACKET; + } + + /* check for 0x0C */ + if ((in[0] & 0x1F) != 0x0C) { + return CRYPT_INVALID_PACKET; + } + x = 1; + + /* decode the length */ + if (in[x] & 0x80) { + /* valid # of bytes in length are 1,2,3 */ + y = in[x] & 0x7F; + if ((y == 0) || (y > 3) || ((x + y) > inlen)) { + return CRYPT_INVALID_PACKET; + } + + /* read the length in */ + len = 0; + ++x; + while (y--) { + len = (len << 8) | in[x++]; + } + } else { + len = in[x++] & 0x7F; + } + + if (len + x > inlen) { + return CRYPT_INVALID_PACKET; + } + + /* proceed to decode */ + for (y = 0; x < inlen; ) { + /* get first byte */ + tmp = in[x++]; + + /* count number of bytes */ + for (z = 0; (tmp & 0x80) && (z <= 4); z++, tmp = (tmp << 1) & 0xFF); + + if ((z > 4) || (x + (z - 1) > inlen)) { + return CRYPT_INVALID_PACKET; + } + + /* decode, grab upper bits */ + tmp >>= z; + + /* grab remaining bytes */ + if (z > 1) { + --z; + } + while (z-- != 0) { + if ((in[x] & 0xC0) != 0x80) { + return CRYPT_INVALID_PACKET; + } + tmp = (tmp << 6) | ((wchar_t)in[x++] & 0x3F); + } + + if (y > *outlen) { + *outlen = y; + return CRYPT_BUFFER_OVERFLOW; + } + out[y++] = tmp; + } + *outlen = y; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utf8/der_decode_utf8_string.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_bit_string.c + ASN.1 DER, encode a BIT STRING, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store a BIT STRING + @param in The array of bits to store (one per char) + @param inlen The number of bits tostore + @param out [out] The destination for the DER encoded BIT STRING + @param outlen [in/out] The max size and resulting size of the DER BIT STRING + @return CRYPT_OK if successful + */ +int der_encode_bit_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long len, x, y; + unsigned char buf; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* avoid overflows */ + if ((err = der_length_bit_string(inlen, &len)) != CRYPT_OK) { + return err; + } + + if (len > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + /* store header (include bit padding count in length) */ + x = 0; + y = (inlen >> 3) + ((inlen & 7) ? 1 : 0) + 1; + + out[x++] = 0x03; + if (y < 128) { + out[x++] = (unsigned char)y; + } else if (y < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)y; + } else if (y < 65536) { + out[x++] = 0x82; + out[x++] = (unsigned char)((y >> 8) & 255); + out[x++] = (unsigned char)(y & 255); + } + + /* store number of zero padding bits */ + out[x++] = (unsigned char)((8 - inlen) & 7); + + /* store the bits in big endian format */ + for (y = buf = 0; y < inlen; y++) { + buf |= (in[y] ? 1 : 0) << (7 - (y & 7)); + if ((y & 7) == 7) { + out[x++] = buf; + buf = 0; + } + } + /* store last byte */ + if (inlen & 7) { + out[x++] = buf; + } + *outlen = x; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/bit/der_encode_bit_string.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_boolean.c + ASN.1 DER, encode a BOOLEAN, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store a BOOLEAN + @param in The boolean to encode + @param out [out] The destination for the DER encoded BOOLEAN + @param outlen [in/out] The max size and resulting size of the DER BOOLEAN + @return CRYPT_OK if successful + */ +int der_encode_boolean(int in, + unsigned char *out, unsigned long *outlen) { + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(out != NULL); + + if (*outlen < 3) { + *outlen = 3; + return CRYPT_BUFFER_OVERFLOW; + } + + *outlen = 3; + out[0] = 0x01; + out[1] = 0x01; + out[2] = in ? 0xFF : 0x00; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/boolean/der_encode_boolean.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_ia5_string.c + ASN.1 DER, encode a IA5 STRING, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Store an IA5 STRING + @param in The array of IA5 to store (one per char) + @param inlen The number of IA5 to store + @param out [out] The destination for the DER encoded IA5 STRING + @param outlen [in/out] The max size and resulting size of the DER IA5 STRING + @return CRYPT_OK if successful + */ +int der_encode_ia5_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long x, y, len; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get the size */ + if ((err = der_length_ia5_string(in, inlen, &len)) != CRYPT_OK) { + return err; + } + + /* too big? */ + if (len > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + /* encode the header+len */ + x = 0; + out[x++] = 0x16; + if (inlen < 128) { + out[x++] = (unsigned char)inlen; + } else if (inlen < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)inlen; + } else if (inlen < 65536UL) { + out[x++] = 0x82; + out[x++] = (unsigned char)((inlen >> 8) & 255); + out[x++] = (unsigned char)(inlen & 255); + } else if (inlen < 16777216UL) { + out[x++] = 0x83; + out[x++] = (unsigned char)((inlen >> 16) & 255); + out[x++] = (unsigned char)((inlen >> 8) & 255); + out[x++] = (unsigned char)(inlen & 255); + } else { + return CRYPT_INVALID_ARG; + } + + /* store octets */ + for (y = 0; y < inlen; y++) { + out[x++] = der_ia5_char_encode(in[y]); + } + + /* retun length */ + *outlen = x; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/ia5/der_encode_ia5_string.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_integer.c + ASN.1 DER, encode an integer, Tom St Denis + */ + + +#ifdef LTC_DER + +/* Exports a positive bignum as DER format (upto 2^32 bytes in size) */ + +/** + Store a mp_int integer + @param num The first mp_int to encode + @param out [out] The destination for the DER encoded integers + @param outlen [in/out] The max size and resulting size of the DER encoded integers + @return CRYPT_OK if successful + */ +int der_encode_integer(void *num, unsigned char *out, unsigned long *outlen) { + unsigned long tmplen, y; + int err, leading_zero; + + LTC_ARGCHK(num != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* find out how big this will be */ + if ((err = der_length_integer(num, &tmplen)) != CRYPT_OK) { + return err; + } + + if (*outlen < tmplen) { + *outlen = tmplen; + return CRYPT_BUFFER_OVERFLOW; + } + + if (mp_cmp_d(num, 0) != LTC_MP_LT) { + /* we only need a leading zero if the msb of the first byte is one */ + if (((mp_count_bits(num) & 7) == 0) || (mp_iszero(num) == LTC_MP_YES)) { + leading_zero = 1; + } else { + leading_zero = 0; + } + + /* get length of num in bytes (plus 1 since we force the msbyte to zero) */ + y = mp_unsigned_bin_size(num) + leading_zero; + } else { + leading_zero = 0; + y = mp_count_bits(num); + y = y + (8 - (y & 7)); + y = y >> 3; + if (((mp_cnt_lsb(num) + 1) == mp_count_bits(num)) && ((mp_count_bits(num) & 7) == 0)) --y; + } + + /* now store initial data */ + *out++ = 0x02; + if (y < 128) { + /* short form */ + *out++ = (unsigned char)y; + } else if (y < 256) { + *out++ = 0x81; + *out++ = (unsigned char)y; + } else if (y < 65536UL) { + *out++ = 0x82; + *out++ = (unsigned char)((y >> 8) & 255); + *out++ = (unsigned char)y; + } else if (y < 16777216UL) { + *out++ = 0x83; + *out++ = (unsigned char)((y >> 16) & 255); + *out++ = (unsigned char)((y >> 8) & 255); + *out++ = (unsigned char)y; + } else { + return CRYPT_INVALID_ARG; + } + + /* now store msbyte of zero if num is non-zero */ + if (leading_zero) { + *out++ = 0x00; + } + + /* if it's not zero store it as big endian */ + if (mp_cmp_d(num, 0) == LTC_MP_GT) { + /* now store the mpint */ + if ((err = mp_to_unsigned_bin(num, out)) != CRYPT_OK) { + return err; + } + } else if (mp_iszero(num) != LTC_MP_YES) { + void *tmp; + + /* negative */ + if (mp_init(&tmp) != CRYPT_OK) { + return CRYPT_MEM; + } + + /* 2^roundup and subtract */ + y = mp_count_bits(num); + y = y + (8 - (y & 7)); + if (((mp_cnt_lsb(num) + 1) == mp_count_bits(num)) && ((mp_count_bits(num) & 7) == 0)) y -= 8; + if ((mp_2expt(tmp, y) != CRYPT_OK) || (mp_add(tmp, num, tmp) != CRYPT_OK)) { + mp_clear(tmp); + return CRYPT_MEM; + } + if ((err = mp_to_unsigned_bin(tmp, out)) != CRYPT_OK) { + mp_clear(tmp); + return err; + } + mp_clear(tmp); + } + + /* we good */ + *outlen = tmplen; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/integer/der_encode_integer.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_object_identifier.c + ASN.1 DER, Encode Object Identifier, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Encode an OID + @param words The words to encode (upto 32-bits each) + @param nwords The number of words in the OID + @param out [out] Destination of OID data + @param outlen [in/out] The max and resulting size of the OID + @return CRYPT_OK if successful + */ +int der_encode_object_identifier(unsigned long *words, unsigned long nwords, + unsigned char *out, unsigned long *outlen) { + unsigned long i, x, y, z, t, mask, wordbuf; + int err; + + LTC_ARGCHK(words != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* check length */ + if ((err = der_length_object_identifier(words, nwords, &x)) != CRYPT_OK) { + return err; + } + if (x > *outlen) { + *outlen = x; + return CRYPT_BUFFER_OVERFLOW; + } + + /* compute length to store OID data */ + z = 0; + wordbuf = words[0] * 40 + words[1]; + for (y = 1; y < nwords; y++) { + t = der_object_identifier_bits(wordbuf); + z += t / 7 + ((t % 7) ? 1 : 0) + (wordbuf == 0 ? 1 : 0); + if (y < nwords - 1) { + wordbuf = words[y + 1]; + } + } + + /* store header + length */ + x = 0; + out[x++] = 0x06; + if (z < 128) { + out[x++] = (unsigned char)z; + } else if (z < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)z; + } else if (z < 65536UL) { + out[x++] = 0x82; + out[x++] = (unsigned char)((z >> 8) & 255); + out[x++] = (unsigned char)(z & 255); + } else { + return CRYPT_INVALID_ARG; + } + + /* store first byte */ + wordbuf = words[0] * 40 + words[1]; + for (i = 1; i < nwords; i++) { + /* store 7 bit words in little endian */ + t = wordbuf & 0xFFFFFFFF; + if (t) { + y = x; + mask = 0; + while (t) { + out[x++] = (unsigned char)((t & 0x7F) | mask); + t >>= 7; + mask |= 0x80; /* upper bit is set on all but the last byte */ + } + /* now swap bytes y...x-1 */ + z = x - 1; + while (y < z) { + t = out[y]; + out[y] = out[z]; + out[z] = (unsigned char)t; + ++y; + --z; + } + } else { + /* zero word */ + out[x++] = 0x00; + } + + if (i < nwords - 1) { + wordbuf = words[i + 1]; + } + } + + *outlen = x; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/object_identifier/der_encode_object_identifier.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_octet_string.c + ASN.1 DER, encode a OCTET STRING, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store an OCTET STRING + @param in The array of OCTETS to store (one per char) + @param inlen The number of OCTETS to store + @param out [out] The destination for the DER encoded OCTET STRING + @param outlen [in/out] The max size and resulting size of the DER OCTET STRING + @return CRYPT_OK if successful + */ +int der_encode_octet_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long x, y, len; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get the size */ + if ((err = der_length_octet_string(inlen, &len)) != CRYPT_OK) { + return err; + } + + /* too big? */ + if (len > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + /* encode the header+len */ + x = 0; + out[x++] = 0x04; + if (inlen < 128) { + out[x++] = (unsigned char)inlen; + } else if (inlen < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)inlen; + } else if (inlen < 65536UL) { + out[x++] = 0x82; + out[x++] = (unsigned char)((inlen >> 8) & 255); + out[x++] = (unsigned char)(inlen & 255); + } else if (inlen < 16777216UL) { + out[x++] = 0x83; + out[x++] = (unsigned char)((inlen >> 16) & 255); + out[x++] = (unsigned char)((inlen >> 8) & 255); + out[x++] = (unsigned char)(inlen & 255); + } else { + return CRYPT_INVALID_ARG; + } + + /* store octets */ + for (y = 0; y < inlen; y++) { + out[x++] = in[y]; + } + + /* retun length */ + *outlen = x; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/octet/der_encode_octet_string.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_printable_string.c + ASN.1 DER, encode a printable STRING, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Store an printable STRING + @param in The array of printable to store (one per char) + @param inlen The number of printable to store + @param out [out] The destination for the DER encoded printable STRING + @param outlen [in/out] The max size and resulting size of the DER printable STRING + @return CRYPT_OK if successful + */ +int der_encode_printable_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long x, y, len; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get the size */ + if ((err = der_length_printable_string(in, inlen, &len)) != CRYPT_OK) { + return err; + } + + /* too big? */ + if (len > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + /* encode the header+len */ + x = 0; + out[x++] = 0x13; + if (inlen < 128) { + out[x++] = (unsigned char)inlen; + } else if (inlen < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)inlen; + } else if (inlen < 65536UL) { + out[x++] = 0x82; + out[x++] = (unsigned char)((inlen >> 8) & 255); + out[x++] = (unsigned char)(inlen & 255); + } else if (inlen < 16777216UL) { + out[x++] = 0x83; + out[x++] = (unsigned char)((inlen >> 16) & 255); + out[x++] = (unsigned char)((inlen >> 8) & 255); + out[x++] = (unsigned char)(inlen & 255); + } else { + return CRYPT_INVALID_ARG; + } + + /* store octets */ + for (y = 0; y < inlen; y++) { + out[x++] = der_printable_char_encode(in[y]); + } + + /* retun length */ + *outlen = x; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/printable_string/der_encode_printable_string.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#include + + +/** + @file der_encode_sequence_ex.c + ASN.1 DER, encode a SEQUENCE, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Encode a SEQUENCE + @param list The list of items to encode + @param inlen The number of items in the list + @param out [out] The destination + @param outlen [in/out] The size of the output + @param type_of LTC_ASN1_SEQUENCE or LTC_ASN1_SET/LTC_ASN1_SETOF + @return CRYPT_OK on success + */ +int der_encode_sequence_ex(ltc_asn1_list *list, unsigned long inlen, + unsigned char *out, unsigned long *outlen, int type_of) { + int err, type; + unsigned long size, x, y, z, i; + void *data; + + LTC_ARGCHK(list != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get size of output that will be required */ + y = 0; + for (i = 0; i < inlen; i++) { + type = list[i].type; + size = list[i].size; + data = list[i].data; + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + if ((err = der_length_boolean(&x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_INTEGER: + if ((err = der_length_integer(data, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_SHORT_INTEGER: + if ((err = der_length_short_integer(*((unsigned long *)data), &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_BIT_STRING: + if ((err = der_length_bit_string(size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_OCTET_STRING: + if ((err = der_length_octet_string(size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_NULL: + y += 2; + break; + + case LTC_ASN1_OBJECT_IDENTIFIER: + if ((err = der_length_object_identifier(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_IA5_STRING: + if ((err = der_length_ia5_string(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_PRINTABLE_STRING: + if ((err = der_length_printable_string(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_UTF8_STRING: + if ((err = der_length_utf8_string(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_UTCTIME: + if ((err = der_length_utctime(data, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + case LTC_ASN1_SEQUENCE: + if ((err = der_length_sequence(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + default: + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + } + + /* calc header size */ + z = y; + if (y < 128) { + y += 2; + } else if (y < 256) { + /* 0x30 0x81 LL */ + y += 3; + } else if (y < 65536UL) { + /* 0x30 0x82 LL LL */ + y += 4; + } else if (y < 16777216UL) { + /* 0x30 0x83 LL LL LL */ + y += 5; + } else { + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + + /* too big ? */ + if (*outlen < y) { + *outlen = y; + err = CRYPT_BUFFER_OVERFLOW; + goto LBL_ERR; + } + + /* store header */ + x = 0; + out[x++] = (type_of == LTC_ASN1_SEQUENCE) ? 0x30 : 0x31; + + if (z < 128) { + out[x++] = (unsigned char)z; + } else if (z < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)z; + } else if (z < 65536UL) { + out[x++] = 0x82; + out[x++] = (unsigned char)((z >> 8UL) & 255); + out[x++] = (unsigned char)(z & 255); + } else if (z < 16777216UL) { + out[x++] = 0x83; + out[x++] = (unsigned char)((z >> 16UL) & 255); + out[x++] = (unsigned char)((z >> 8UL) & 255); + out[x++] = (unsigned char)(z & 255); + } + + /* store data */ + *outlen -= x; + for (i = 0; i < inlen; i++) { + type = list[i].type; + size = list[i].size; + data = list[i].data; + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + z = *outlen; + if ((err = der_encode_boolean(*((int *)data), out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_INTEGER: + z = *outlen; + if ((err = der_encode_integer(data, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_SHORT_INTEGER: + z = *outlen; + if ((err = der_encode_short_integer(*((unsigned long *)data), out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_BIT_STRING: + z = *outlen; + if ((err = der_encode_bit_string(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_OCTET_STRING: + z = *outlen; + if ((err = der_encode_octet_string(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_NULL: + out[x++] = 0x05; + out[x++] = 0x00; + *outlen -= 2; + break; + + case LTC_ASN1_OBJECT_IDENTIFIER: + z = *outlen; + if ((err = der_encode_object_identifier(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_IA5_STRING: + z = *outlen; + if ((err = der_encode_ia5_string(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_PRINTABLE_STRING: + z = *outlen; + if ((err = der_encode_printable_string(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_UTF8_STRING: + z = *outlen; + if ((err = der_encode_utf8_string(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_UTCTIME: + z = *outlen; + if ((err = der_encode_utctime(data, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_SET: + z = *outlen; + if ((err = der_encode_set(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_SETOF: + z = *outlen; + if ((err = der_encode_setof(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_SEQUENCE: + z = *outlen; + if ((err = der_encode_sequence_ex(data, size, out + x, &z, type)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + default: + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + } + *outlen = x; + err = CRYPT_OK; + +LBL_ERR: + return err; +} +#endif + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#include + + +/** + @file der_encode_sequence_multi.c + ASN.1 DER, encode a SEQUENCE, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Encode a SEQUENCE type using a VA list + @param out [out] Destination for data + @param outlen [in/out] Length of buffer and resulting length of output + @remark <...> is of the form (int, unsigned long, void*) + @return CRYPT_OK on success + */ +int der_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...) { + int err, type; + unsigned long size, x; + void *data; + va_list args; + ltc_asn1_list *list; + + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get size of output that will be required */ + va_start(args, outlen); + x = 0; + for ( ; ; ) { + type = va_arg(args, int); + size = va_arg(args, unsigned long); + data = va_arg(args, void *); + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + case LTC_ASN1_INTEGER: + case LTC_ASN1_SHORT_INTEGER: + case LTC_ASN1_BIT_STRING: + case LTC_ASN1_OCTET_STRING: + case LTC_ASN1_NULL: + case LTC_ASN1_OBJECT_IDENTIFIER: + case LTC_ASN1_IA5_STRING: + case LTC_ASN1_PRINTABLE_STRING: + case LTC_ASN1_UTF8_STRING: + case LTC_ASN1_UTCTIME: + case LTC_ASN1_SEQUENCE: + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + ++x; + break; + + default: + va_end(args); + return CRYPT_INVALID_ARG; + } + } + va_end(args); + + /* allocate structure for x elements */ + if (x == 0) { + return CRYPT_NOP; + } + + list = XCALLOC(sizeof(*list), x); + if (list == NULL) { + return CRYPT_MEM; + } + + /* fill in the structure */ + va_start(args, outlen); + x = 0; + for ( ; ; ) { + type = va_arg(args, int); + size = va_arg(args, unsigned long); + data = va_arg(args, void *); + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + case LTC_ASN1_INTEGER: + case LTC_ASN1_SHORT_INTEGER: + case LTC_ASN1_BIT_STRING: + case LTC_ASN1_OCTET_STRING: + case LTC_ASN1_NULL: + case LTC_ASN1_OBJECT_IDENTIFIER: + case LTC_ASN1_IA5_STRING: + case LTC_ASN1_PRINTABLE_STRING: + case LTC_ASN1_UTF8_STRING: + case LTC_ASN1_UTCTIME: + case LTC_ASN1_SEQUENCE: + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + list[x].type = type; + list[x].size = size; + list[x++].data = data; + break; + + default: + va_end(args); + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + } + va_end(args); + + err = der_encode_sequence(list, x, out, outlen); +LBL_ERR: + XFREE(list); + return err; +} +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_encode_sequence_multi.c,v $ */ +/* $Revision: 1.12 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_set.c + ASN.1 DER, Encode a SET, Tom St Denis + */ + +#ifdef LTC_DER + +/* LTC define to ASN.1 TAG */ +static int ltc_to_asn1(int v) { + switch (v) { + case LTC_ASN1_BOOLEAN: + return 0x01; + + case LTC_ASN1_INTEGER: + case LTC_ASN1_SHORT_INTEGER: + return 0x02; + + case LTC_ASN1_BIT_STRING: + return 0x03; + + case LTC_ASN1_OCTET_STRING: + return 0x04; + + case LTC_ASN1_NULL: + return 0x05; + + case LTC_ASN1_OBJECT_IDENTIFIER: + return 0x06; + + case LTC_ASN1_UTF8_STRING: + return 0x0C; + + case LTC_ASN1_PRINTABLE_STRING: + return 0x13; + + case LTC_ASN1_IA5_STRING: + return 0x16; + + case LTC_ASN1_UTCTIME: + return 0x17; + + case LTC_ASN1_SEQUENCE: + return 0x30; + + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + return 0x31; + + default: + return -1; + } +} + +static int qsort_helper_set(const void *a, const void *b) { + ltc_asn1_list *A = (ltc_asn1_list *)a, *B = (ltc_asn1_list *)b; + int r; + + r = ltc_to_asn1(A->type) - ltc_to_asn1(B->type); + + /* for QSORT the order is UNDEFINED if they are "equal" which means it is NOT DETERMINISTIC. So we force it to be :-) */ + if (r == 0) { + /* their order in the original list now determines the position */ + return A->used - B->used; + } else { + return r; + } +} + +/* + Encode a SET type + @param list The list of items to encode + @param inlen The number of items in the list + @param out [out] The destination + @param outlen [in/out] The size of the output + @return CRYPT_OK on success + */ +int der_encode_set(ltc_asn1_list *list, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + ltc_asn1_list *copy; + unsigned long x; + int err; + + /* make copy of list */ + copy = XCALLOC(inlen, sizeof(*copy)); + if (copy == NULL) { + return CRYPT_MEM; + } + + /* fill in used member with index so we can fully sort it */ + for (x = 0; x < inlen; x++) { + copy[x] = list[x]; + copy[x].used = x; + } + + /* sort it by the "type" field */ + XQSORT(copy, inlen, sizeof(*copy), &qsort_helper_set); + + /* call der_encode_sequence_ex() */ + err = der_encode_sequence_ex(copy, inlen, out, outlen, LTC_ASN1_SET); + + /* free list */ + XFREE(copy); + + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/set/der_encode_set.c,v $ */ +/* $Revision: 1.12 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_setof.c + ASN.1 DER, Encode SET OF, Tom St Denis + */ + +#ifdef LTC_DER + +struct edge { + unsigned char *start; + unsigned long size; +}; + +static int qsort_helper(const void *a, const void *b) { + struct edge *A = (struct edge *)a, *B = (struct edge *)b; + int r; + unsigned long x; + + /* compare min length */ + r = XMEMCMP(A->start, B->start, MIN(A->size, B->size)); + + if ((r == 0) && (A->size != B->size)) { + if (A->size > B->size) { + for (x = B->size; x < A->size; x++) { + if (A->start[x]) { + return 1; + } + } + } else { + for (x = A->size; x < B->size; x++) { + if (B->start[x]) { + return -1; + } + } + } + } + + return r; +} + +/** + Encode a SETOF stucture + @param list The list of items to encode + @param inlen The number of items in the list + @param out [out] The destination + @param outlen [in/out] The size of the output + @return CRYPT_OK on success + */ +int der_encode_setof(ltc_asn1_list *list, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long x, y, z, hdrlen; + int err; + struct edge *edges; + unsigned char *ptr, *buf; + + /* check that they're all the same type */ + for (x = 1; x < inlen; x++) { + if (list[x].type != list[x - 1].type) { + return CRYPT_INVALID_ARG; + } + } + + /* alloc buffer to store copy of output */ + buf = XCALLOC(1, *outlen); + if (buf == NULL) { + return CRYPT_MEM; + } + + /* encode list */ + if ((err = der_encode_sequence_ex(list, inlen, buf, outlen, LTC_ASN1_SETOF)) != CRYPT_OK) { + XFREE(buf); + return err; + } + + /* allocate edges */ + edges = XCALLOC(inlen, sizeof(*edges)); + if (edges == NULL) { + XFREE(buf); + return CRYPT_MEM; + } + + /* skip header */ + ptr = buf + 1; + + /* now skip length data */ + x = *ptr++; + if (x >= 0x80) { + ptr += (x & 0x7F); + } + + /* get the size of the static header */ + hdrlen = ((unsigned long)ptr) - ((unsigned long)buf); + + + /* scan for edges */ + x = 0; + while (ptr < (buf + *outlen)) { + /* store start */ + edges[x].start = ptr; + + /* skip type */ + z = 1; + + /* parse length */ + y = ptr[z++]; + if (y < 128) { + edges[x].size = y; + } else { + y &= 0x7F; + edges[x].size = 0; + while (y--) { + edges[x].size = (edges[x].size << 8) | ((unsigned long)ptr[z++]); + } + } + + /* skip content */ + edges[x].size += z; + ptr += edges[x].size; + ++x; + } + + /* sort based on contents (using edges) */ + XQSORT(edges, inlen, sizeof(*edges), &qsort_helper); + + /* copy static header */ + XMEMCPY(out, buf, hdrlen); + + /* copy+sort using edges+indecies to output from buffer */ + for (y = hdrlen, x = 0; x < inlen; x++) { + XMEMCPY(out + y, edges[x].start, edges[x].size); + y += edges[x].size; + } + + #ifdef LTC_CLEAN_STACK + zeromem(buf, *outlen); + #endif + + /* free buffers */ + XFREE(edges); + XFREE(buf); + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/set/der_encode_setof.c,v $ */ +/* $Revision: 1.12 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_short_integer.c + ASN.1 DER, encode an integer, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store a short integer in the range (0,2^32-1) + @param num The integer to encode + @param out [out] The destination for the DER encoded integers + @param outlen [in/out] The max size and resulting size of the DER encoded integers + @return CRYPT_OK if successful + */ +int der_encode_short_integer(unsigned long num, unsigned char *out, unsigned long *outlen) { + unsigned long len, x, y, z; + int err; + + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* force to 32 bits */ + num &= 0xFFFFFFFFUL; + + /* find out how big this will be */ + if ((err = der_length_short_integer(num, &len)) != CRYPT_OK) { + return err; + } + + if (*outlen < len) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + /* get len of output */ + z = 0; + y = num; + while (y) { + ++z; + y >>= 8; + } + + /* handle zero */ + if (z == 0) { + z = 1; + } + + /* see if msb is set */ + z += (num & (1UL << ((z << 3) - 1))) ? 1 : 0; + + /* adjust the number so the msB is non-zero */ + for (x = 0; (z <= 4) && (x < (4 - z)); x++) { + num <<= 8; + } + + /* store header */ + x = 0; + out[x++] = 0x02; + out[x++] = (unsigned char)z; + + /* if 31st bit is set output a leading zero and decrement count */ + if (z == 5) { + out[x++] = 0; + --z; + } + + /* store values */ + for (y = 0; y < z; y++) { + out[x++] = (unsigned char)((num >> 24) & 0xFF); + num <<= 8; + } + + /* we good */ + *outlen = x; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/short_integer/der_encode_short_integer.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_utctime.c + ASN.1 DER, encode a UTCTIME, Tom St Denis + */ + +#ifdef LTC_DER + +static const char baseten[] = "0123456789"; + + #define STORE_V(y) \ + out[x++] = der_ia5_char_encode(baseten[(y / 10) % 10]); \ + out[x++] = der_ia5_char_encode(baseten[y % 10]); + +/** + Encodes a UTC time structure in DER format + @param utctime The UTC time structure to encode + @param out The destination of the DER encoding of the UTC time structure + @param outlen [in/out] The length of the DER encoding + @return CRYPT_OK if successful + */ +int der_encode_utctime(ltc_utctime *utctime, + unsigned char *out, unsigned long *outlen) { + unsigned long x, tmplen; + int err; + + LTC_ARGCHK(utctime != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + if ((err = der_length_utctime(utctime, &tmplen)) != CRYPT_OK) { + return err; + } + if (tmplen > *outlen) { + *outlen = tmplen; + return CRYPT_BUFFER_OVERFLOW; + } + + /* store header */ + out[0] = 0x17; + + /* store values */ + x = 2; + STORE_V(utctime->YY); + STORE_V(utctime->MM); + STORE_V(utctime->DD); + STORE_V(utctime->hh); + STORE_V(utctime->mm); + STORE_V(utctime->ss); + + if (utctime->off_mm || utctime->off_hh) { + out[x++] = der_ia5_char_encode(utctime->off_dir ? '-' : '+'); + STORE_V(utctime->off_hh); + STORE_V(utctime->off_mm); + } else { + out[x++] = der_ia5_char_encode('Z'); + } + + /* store length */ + out[1] = (unsigned char)(x - 2); + + /* all good let's return */ + *outlen = x; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utctime/der_encode_utctime.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_utf8_string.c + ASN.1 DER, encode a UTF8 STRING, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store an UTF8 STRING + @param in The array of UTF8 to store (one per wchar_t) + @param inlen The number of UTF8 to store + @param out [out] The destination for the DER encoded UTF8 STRING + @param outlen [in/out] The max size and resulting size of the DER UTF8 STRING + @return CRYPT_OK if successful + */ +int der_encode_utf8_string(const wchar_t *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long x, y, len; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get the size */ + for (x = len = 0; x < inlen; x++) { + if ((in[x] < 0) || (in[x] > 0x1FFFF)) { + return CRYPT_INVALID_ARG; + } + len += der_utf8_charsize(in[x]); + } + + if (len < 128) { + y = 2 + len; + } else if (len < 256) { + y = 3 + len; + } else if (len < 65536UL) { + y = 4 + len; + } else if (len < 16777216UL) { + y = 5 + len; + } else { + return CRYPT_INVALID_ARG; + } + + /* too big? */ + if (y > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + /* encode the header+len */ + x = 0; + out[x++] = 0x0C; + if (len < 128) { + out[x++] = (unsigned char)len; + } else if (len < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)len; + } else if (len < 65536UL) { + out[x++] = 0x82; + out[x++] = (unsigned char)((len >> 8) & 255); + out[x++] = (unsigned char)(len & 255); + } else if (len < 16777216UL) { + out[x++] = 0x83; + out[x++] = (unsigned char)((len >> 16) & 255); + out[x++] = (unsigned char)((len >> 8) & 255); + out[x++] = (unsigned char)(len & 255); + } else { + return CRYPT_INVALID_ARG; + } + + /* store UTF8 */ + for (y = 0; y < inlen; y++) { + switch (der_utf8_charsize(in[y])) { + case 1: + out[x++] = (unsigned char)in[y]; + break; + + case 2: + out[x++] = 0xC0 | ((in[y] >> 6) & 0x1F); + out[x++] = 0x80 | (in[y] & 0x3F); + break; + + case 3: + out[x++] = 0xE0 | ((in[y] >> 12) & 0x0F); + out[x++] = 0x80 | ((in[y] >> 6) & 0x3F); + out[x++] = 0x80 | (in[y] & 0x3F); + break; + + case 4: + out[x++] = 0xF0 | ((in[y] >> 18) & 0x07); + out[x++] = 0x80 | ((in[y] >> 12) & 0x3F); + out[x++] = 0x80 | ((in[y] >> 6) & 0x3F); + out[x++] = 0x80 | (in[y] & 0x3F); + break; + } + } + + /* retun length */ + *outlen = x; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utf8/der_encode_utf8_string.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_bit_string.c + ASN.1 DER, get length of BIT STRING, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Gets length of DER encoding of BIT STRING + @param nbits The number of bits in the string to encode + @param outlen [out] The length of the DER encoding for the given string + @return CRYPT_OK if successful + */ +int der_length_bit_string(unsigned long nbits, unsigned long *outlen) { + unsigned long nbytes; + + LTC_ARGCHK(outlen != NULL); + + /* get the number of the bytes */ + nbytes = (nbits >> 3) + ((nbits & 7) ? 1 : 0) + 1; + + if (nbytes < 128) { + /* 03 LL PP DD DD DD ... */ + *outlen = 2 + nbytes; + } else if (nbytes < 256) { + /* 03 81 LL PP DD DD DD ... */ + *outlen = 3 + nbytes; + } else if (nbytes < 65536) { + /* 03 82 LL LL PP DD DD DD ... */ + *outlen = 4 + nbytes; + } else { + return CRYPT_INVALID_ARG; + } + + return CRYPT_OK; +} +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/bit/der_length_bit_string.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_boolean.c + ASN.1 DER, get length of a BOOLEAN, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Gets length of DER encoding of a BOOLEAN + @param outlen [out] The length of the DER encoding + @return CRYPT_OK if successful + */ +int der_length_boolean(unsigned long *outlen) { + LTC_ARGCHK(outlen != NULL); + *outlen = 3; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/boolean/der_length_boolean.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_ia5_string.c + ASN.1 DER, get length of IA5 STRING, Tom St Denis + */ + +#ifdef LTC_DER + +static const struct { + int code, value; +} ia5_table[] = { + { '\0', 0 }, + { '\a', 7 }, + { '\b', 8 }, + { '\t', 9 }, + { '\n', 10 }, + { '\f', 12 }, + { '\r', 13 }, + { ' ', 32 }, + { '!', 33 }, + { '"', 34 }, + { '#', 35 }, + { '$', 36 }, + { '%', 37 }, + { '&', 38 }, + { '\'', 39 }, + { '(', 40 }, + { ')', 41 }, + { '*', 42 }, + { '+', 43 }, + { ',', 44 }, + { '-', 45 }, + { '.', 46 }, + { '/', 47 }, + { '0', 48 }, + { '1', 49 }, + { '2', 50 }, + { '3', 51 }, + { '4', 52 }, + { '5', 53 }, + { '6', 54 }, + { '7', 55 }, + { '8', 56 }, + { '9', 57 }, + { ':', 58 }, + { ';', 59 }, + { '<', 60 }, + { '=', 61 }, + { '>', 62 }, + { '?', 63 }, + { '@', 64 }, + { 'A', 65 }, + { 'B', 66 }, + { 'C', 67 }, + { 'D', 68 }, + { 'E', 69 }, + { 'F', 70 }, + { 'G', 71 }, + { 'H', 72 }, + { 'I', 73 }, + { 'J', 74 }, + { 'K', 75 }, + { 'L', 76 }, + { 'M', 77 }, + { 'N', 78 }, + { 'O', 79 }, + { 'P', 80 }, + { 'Q', 81 }, + { 'R', 82 }, + { 'S', 83 }, + { 'T', 84 }, + { 'U', 85 }, + { 'V', 86 }, + { 'W', 87 }, + { 'X', 88 }, + { 'Y', 89 }, + { 'Z', 90 }, + { '[', 91 }, + { '\\', 92 }, + { ']', 93 }, + { '^', 94 }, + { '_', 95 }, + { '`', 96 }, + { 'a', 97 }, + { 'b', 98 }, + { 'c', 99 }, + { 'd', 100 }, + { 'e', 101 }, + { 'f', 102 }, + { 'g', 103 }, + { 'h', 104 }, + { 'i', 105 }, + { 'j', 106 }, + { 'k', 107 }, + { 'l', 108 }, + { 'm', 109 }, + { 'n', 110 }, + { 'o', 111 }, + { 'p', 112 }, + { 'q', 113 }, + { 'r', 114 }, + { 's', 115 }, + { 't', 116 }, + { 'u', 117 }, + { 'v', 118 }, + { 'w', 119 }, + { 'x', 120 }, + { 'y', 121 }, + { 'z', 122 }, + { '{', 123 }, + { '|', 124 }, + { '}', 125 }, + { '~', 126 } +}; + +int der_ia5_char_encode(int c) { + int x; + + for (x = 0; x < (int)(sizeof(ia5_table) / sizeof(ia5_table[0])); x++) { + if (ia5_table[x].code == c) { + return ia5_table[x].value; + } + } + return -1; +} + +int der_ia5_value_decode(int v) { + int x; + + for (x = 0; x < (int)(sizeof(ia5_table) / sizeof(ia5_table[0])); x++) { + if (ia5_table[x].value == v) { + return ia5_table[x].code; + } + } + return -1; +} + +/** + Gets length of DER encoding of IA5 STRING + @param octets The values you want to encode + @param noctets The number of octets in the string to encode + @param outlen [out] The length of the DER encoding for the given string + @return CRYPT_OK if successful + */ +int der_length_ia5_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen) { + unsigned long x; + + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(octets != NULL); + + /* scan string for validity */ + for (x = 0; x < noctets; x++) { + if (der_ia5_char_encode(octets[x]) == -1) { + return CRYPT_INVALID_ARG; + } + } + + if (noctets < 128) { + /* 16 LL DD DD DD ... */ + *outlen = 2 + noctets; + } else if (noctets < 256) { + /* 16 81 LL DD DD DD ... */ + *outlen = 3 + noctets; + } else if (noctets < 65536UL) { + /* 16 82 LL LL DD DD DD ... */ + *outlen = 4 + noctets; + } else if (noctets < 16777216UL) { + /* 16 83 LL LL LL DD DD DD ... */ + *outlen = 5 + noctets; + } else { + return CRYPT_INVALID_ARG; + } + + return CRYPT_OK; +} +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/ia5/der_length_ia5_string.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_integer.c + ASN.1 DER, get length of encoding, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Gets length of DER encoding of num + @param num The int to get the size of + @param outlen [out] The length of the DER encoding for the given integer + @return CRYPT_OK if successful + */ +int der_length_integer(void *num, unsigned long *outlen) { + unsigned long z, len; + int leading_zero; + + LTC_ARGCHK(num != NULL); + LTC_ARGCHK(outlen != NULL); + + if (mp_cmp_d(num, 0) != LTC_MP_LT) { + /* positive */ + + /* we only need a leading zero if the msb of the first byte is one */ + if (((mp_count_bits(num) & 7) == 0) || (mp_iszero(num) == LTC_MP_YES)) { + leading_zero = 1; + } else { + leading_zero = 0; + } + + /* size for bignum */ + z = len = leading_zero + mp_unsigned_bin_size(num); + } else { + /* it's negative */ + /* find power of 2 that is a multiple of eight and greater than count bits */ + leading_zero = 0; + z = mp_count_bits(num); + z = z + (8 - (z & 7)); + if (((mp_cnt_lsb(num) + 1) == mp_count_bits(num)) && ((mp_count_bits(num) & 7) == 0)) --z; + len = z = z >> 3; + } + + /* now we need a length */ + if (z < 128) { + /* short form */ + ++len; + } else { + /* long form (relies on z != 0), assumes length bytes < 128 */ + ++len; + + while (z) { + ++len; + z >>= 8; + } + } + + /* we need a 0x02 to indicate it's INTEGER */ + ++len; + + /* return length */ + *outlen = len; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/integer/der_length_integer.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_object_identifier.c + ASN.1 DER, get length of Object Identifier, Tom St Denis + */ + +#ifdef LTC_DER + +unsigned long der_object_identifier_bits(unsigned long x) { + unsigned long c; + + x &= 0xFFFFFFFF; + c = 0; + while (x) { + ++c; + x >>= 1; + } + return c; +} + +/** + Gets length of DER encoding of Object Identifier + @param nwords The number of OID words + @param words The actual OID words to get the size of + @param outlen [out] The length of the DER encoding for the given string + @return CRYPT_OK if successful + */ +int der_length_object_identifier(unsigned long *words, unsigned long nwords, unsigned long *outlen) { + unsigned long y, z, t, wordbuf; + + LTC_ARGCHK(words != NULL); + LTC_ARGCHK(outlen != NULL); + + + /* must be >= 2 words */ + if (nwords < 2) { + return CRYPT_INVALID_ARG; + } + + /* word1 = 0,1,2,3 and word2 0..39 */ + if ((words[0] > 3) || ((words[0] < 2) && (words[1] > 39))) { + return CRYPT_INVALID_ARG; + } + + /* leading word is the first two */ + z = 0; + wordbuf = words[0] * 40 + words[1]; + for (y = 1; y < nwords; y++) { + t = der_object_identifier_bits(wordbuf); + z += t / 7 + ((t % 7) ? 1 : 0) + (wordbuf == 0 ? 1 : 0); + if (y < nwords - 1) { + /* grab next word */ + wordbuf = words[y + 1]; + } + } + + /* now depending on the length our length encoding changes */ + if (z < 128) { + z += 2; + } else if (z < 256) { + z += 3; + } else if (z < 65536UL) { + z += 4; + } else { + return CRYPT_INVALID_ARG; + } + + *outlen = z; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/object_identifier/der_length_object_identifier.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_octet_string.c + ASN.1 DER, get length of OCTET STRING, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Gets length of DER encoding of OCTET STRING + @param noctets The number of octets in the string to encode + @param outlen [out] The length of the DER encoding for the given string + @return CRYPT_OK if successful + */ +int der_length_octet_string(unsigned long noctets, unsigned long *outlen) { + LTC_ARGCHK(outlen != NULL); + + if (noctets < 128) { + /* 04 LL DD DD DD ... */ + *outlen = 2 + noctets; + } else if (noctets < 256) { + /* 04 81 LL DD DD DD ... */ + *outlen = 3 + noctets; + } else if (noctets < 65536UL) { + /* 04 82 LL LL DD DD DD ... */ + *outlen = 4 + noctets; + } else if (noctets < 16777216UL) { + /* 04 83 LL LL LL DD DD DD ... */ + *outlen = 5 + noctets; + } else { + return CRYPT_INVALID_ARG; + } + + return CRYPT_OK; +} +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/octet/der_length_octet_string.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_printable_string.c + ASN.1 DER, get length of Printable STRING, Tom St Denis + */ + +#ifdef LTC_DER + +static const struct { + int code, value; +} printable_table[] = { + { ' ', 32 }, + { '\'', 39 }, + { '(', 40 }, + { ')', 41 }, + { '+', 43 }, + { ',', 44 }, + { '-', 45 }, + { '.', 46 }, + { '/', 47 }, + { '0', 48 }, + { '1', 49 }, + { '2', 50 }, + { '3', 51 }, + { '4', 52 }, + { '5', 53 }, + { '6', 54 }, + { '7', 55 }, + { '8', 56 }, + { '9', 57 }, + { ':', 58 }, + { '=', 61 }, + { '?', 63 }, + { 'A', 65 }, + { 'B', 66 }, + { 'C', 67 }, + { 'D', 68 }, + { 'E', 69 }, + { 'F', 70 }, + { 'G', 71 }, + { 'H', 72 }, + { 'I', 73 }, + { 'J', 74 }, + { 'K', 75 }, + { 'L', 76 }, + { 'M', 77 }, + { 'N', 78 }, + { 'O', 79 }, + { 'P', 80 }, + { 'Q', 81 }, + { 'R', 82 }, + { 'S', 83 }, + { 'T', 84 }, + { 'U', 85 }, + { 'V', 86 }, + { 'W', 87 }, + { 'X', 88 }, + { 'Y', 89 }, + { 'Z', 90 }, + { 'a', 97 }, + { 'b', 98 }, + { 'c', 99 }, + { 'd', 100 }, + { 'e', 101 }, + { 'f', 102 }, + { 'g', 103 }, + { 'h', 104 }, + { 'i', 105 }, + { 'j', 106 }, + { 'k', 107 }, + { 'l', 108 }, + { 'm', 109 }, + { 'n', 110 }, + { 'o', 111 }, + { 'p', 112 }, + { 'q', 113 }, + { 'r', 114 }, + { 's', 115 }, + { 't', 116 }, + { 'u', 117 }, + { 'v', 118 }, + { 'w', 119 }, + { 'x', 120 }, + { 'y', 121 }, + { 'z', 122 }, +}; + +int der_printable_char_encode(int c) { + int x; + + for (x = 0; x < (int)(sizeof(printable_table) / sizeof(printable_table[0])); x++) { + if (printable_table[x].code == c) { + return printable_table[x].value; + } + } + return -1; +} + +int der_printable_value_decode(int v) { + int x; + + for (x = 0; x < (int)(sizeof(printable_table) / sizeof(printable_table[0])); x++) { + if (printable_table[x].value == v) { + return printable_table[x].code; + } + } + return -1; +} + +/** + Gets length of DER encoding of Printable STRING + @param octets The values you want to encode + @param noctets The number of octets in the string to encode + @param outlen [out] The length of the DER encoding for the given string + @return CRYPT_OK if successful + */ +int der_length_printable_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen) { + unsigned long x; + + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(octets != NULL); + + /* scan string for validity */ + for (x = 0; x < noctets; x++) { + if (der_printable_char_encode(octets[x]) == -1) { + return CRYPT_INVALID_ARG; + } + } + + if (noctets < 128) { + /* 16 LL DD DD DD ... */ + *outlen = 2 + noctets; + } else if (noctets < 256) { + /* 16 81 LL DD DD DD ... */ + *outlen = 3 + noctets; + } else if (noctets < 65536UL) { + /* 16 82 LL LL DD DD DD ... */ + *outlen = 4 + noctets; + } else if (noctets < 16777216UL) { + /* 16 83 LL LL LL DD DD DD ... */ + *outlen = 5 + noctets; + } else { + return CRYPT_INVALID_ARG; + } + + return CRYPT_OK; +} +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/printable_string/der_length_printable_string.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_sequence.c + ASN.1 DER, length a SEQUENCE, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Get the length of a DER sequence + @param list The sequences of items in the SEQUENCE + @param inlen The number of items + @param outlen [out] The length required in octets to store it + @return CRYPT_OK on success + */ +int der_length_sequence(ltc_asn1_list *list, unsigned long inlen, + unsigned long *outlen) { + int err, type; + unsigned long size, x, y, z, i; + void *data; + + LTC_ARGCHK(list != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get size of output that will be required */ + y = 0; + for (i = 0; i < inlen; i++) { + type = list[i].type; + size = list[i].size; + data = list[i].data; + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + if ((err = der_length_boolean(&x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_INTEGER: + if ((err = der_length_integer(data, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_SHORT_INTEGER: + if ((err = der_length_short_integer(*((unsigned long *)data), &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_BIT_STRING: + if ((err = der_length_bit_string(size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_OCTET_STRING: + if ((err = der_length_octet_string(size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_NULL: + y += 2; + break; + + case LTC_ASN1_OBJECT_IDENTIFIER: + if ((err = der_length_object_identifier(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_IA5_STRING: + if ((err = der_length_ia5_string(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_PRINTABLE_STRING: + if ((err = der_length_printable_string(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_UTCTIME: + if ((err = der_length_utctime(data, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_UTF8_STRING: + if ((err = der_length_utf8_string(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + case LTC_ASN1_SEQUENCE: + if ((err = der_length_sequence(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + + default: + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + } + + /* calc header size */ + z = y; + if (y < 128) { + y += 2; + } else if (y < 256) { + /* 0x30 0x81 LL */ + y += 3; + } else if (y < 65536UL) { + /* 0x30 0x82 LL LL */ + y += 4; + } else if (y < 16777216UL) { + /* 0x30 0x83 LL LL LL */ + y += 5; + } else { + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + + /* store size */ + *outlen = y; + err = CRYPT_OK; + +LBL_ERR: + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_length_sequence.c,v $ */ +/* $Revision: 1.14 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_short_integer.c + ASN.1 DER, get length of encoding, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Gets length of DER encoding of num + @param num The integer to get the size of + @param outlen [out] The length of the DER encoding for the given integer + @return CRYPT_OK if successful + */ +int der_length_short_integer(unsigned long num, unsigned long *outlen) { + unsigned long z, y, len; + + LTC_ARGCHK(outlen != NULL); + + /* force to 32 bits */ + num &= 0xFFFFFFFFUL; + + /* get the number of bytes */ + z = 0; + y = num; + while (y) { + ++z; + y >>= 8; + } + + /* handle zero */ + if (z == 0) { + z = 1; + } + + /* we need a 0x02 to indicate it's INTEGER */ + len = 1; + + /* length byte */ + ++len; + + /* bytes in value */ + len += z; + + /* see if msb is set */ + len += (num & (1UL << ((z << 3) - 1))) ? 1 : 0; + + /* return length */ + *outlen = len; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/short_integer/der_length_short_integer.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_utctime.c + ASN.1 DER, get length of UTCTIME, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Gets length of DER encoding of UTCTIME + @param utctime The UTC time structure to get the size of + @param outlen [out] The length of the DER encoding + @return CRYPT_OK if successful + */ +int der_length_utctime(ltc_utctime *utctime, unsigned long *outlen) { + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(utctime != NULL); + + if ((utctime->off_hh == 0) && (utctime->off_mm == 0)) { + /* we encode as YYMMDDhhmmssZ */ + *outlen = 2 + 13; + } else { + /* we encode as YYMMDDhhmmss{+|-}hh'mm' */ + *outlen = 2 + 17; + } + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utctime/der_length_utctime.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_utf8_string.c + ASN.1 DER, get length of UTF8 STRING, Tom St Denis + */ + +#ifdef LTC_DER + +/** Return the size in bytes of a UTF-8 character + @param c The UTF-8 character to measure + @return The size in bytes + */ +unsigned long der_utf8_charsize(const wchar_t c) { + if (c <= 0x7F) { + return 1; + } else if (c <= 0x7FF) { + return 2; + } else if (c <= 0xFFFF) { + return 3; + } else { + return 4; + } +} + +/** + Gets length of DER encoding of UTF8 STRING + @param in The characters to measure the length of + @param noctets The number of octets in the string to encode + @param outlen [out] The length of the DER encoding for the given string + @return CRYPT_OK if successful + */ +int der_length_utf8_string(const wchar_t *in, unsigned long noctets, unsigned long *outlen) { + unsigned long x, len; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(outlen != NULL); + + len = 0; + for (x = 0; x < noctets; x++) { + if ((in[x] < 0) || (in[x] > 0x10FFFF)) { + return CRYPT_INVALID_ARG; + } + len += der_utf8_charsize(in[x]); + } + + if (len < 128) { + /* 0C LL DD DD DD ... */ + *outlen = 2 + len; + } else if (len < 256) { + /* 0C 81 LL DD DD DD ... */ + *outlen = 3 + len; + } else if (len < 65536UL) { + /* 0C 82 LL LL DD DD DD ... */ + *outlen = 4 + len; + } else if (len < 16777216UL) { + /* 0C 83 LL LL LL DD DD DD ... */ + *outlen = 5 + len; + } else { + return CRYPT_INVALID_ARG; + } + + return CRYPT_OK; +} +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utf8/der_length_utf8_string.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_sequence_free.c + ASN.1 DER, free's a structure allocated by der_decode_sequence_flexi(), Tom St Denis + */ + +#ifdef LTC_DER + +/** + Free memory allocated by der_decode_sequence_flexi() + @param in The list to free + */ +void der_sequence_free(ltc_asn1_list *in) { + ltc_asn1_list *l; + + /* walk to the start of the chain */ + while (in->prev != NULL || in->parent != NULL) { + if (in->parent != NULL) { + in = in->parent; + } else { + in = in->prev; + } + } + + /* now walk the list and free stuff */ + while (in != NULL) { + /* is there a child? */ + if (in->child) { + /* disconnect */ + in->child->parent = NULL; + der_sequence_free(in->child); + } + + switch (in->type) { + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + case LTC_ASN1_SEQUENCE: + break; + + case LTC_ASN1_INTEGER: + if (in->data != NULL) { + mp_clear(in->data); + } + break; + + default: + if (in->data != NULL) { + XFREE(in->data); + } + } + + /* move to next and free current */ + l = in->next; + XFREE(in); + in = l; + } +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_sequence_free.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/* This holds the key settings. ***MUST*** be organized by size from smallest to largest. */ +const ltc_ecc_set_type ltc_ecc_sets[] = { + #ifdef ECC112 + { + 14, + "SECP112R1", + "DB7C2ABF62E35E668076BEAD208B", + "659EF8BA043916EEDE8911702B22", + "DB7C2ABF62E35E7628DFAC6561C5", + "09487239995A5EE76B55F9C2F098", + "A89CE5AF8724C0A23E0E0FF77500" + }, + #endif + #ifdef ECC128 + { + 16, + "SECP128R1", + "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF", + "E87579C11079F43DD824993C2CEE5ED3", + "FFFFFFFE0000000075A30D1B9038A115", + "161FF7528B899B2D0C28607CA52C5B86", + "CF5AC8395BAFEB13C02DA292DDED7A83", + }, + #endif + #ifdef ECC160 + { + 20, + "SECP160R1", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF", + "1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45", + "0100000000000000000001F4C8F927AED3CA752257", + "4A96B5688EF573284664698968C38BB913CBFC82", + "23A628553168947D59DCC912042351377AC5FB32", + }, + #endif + #ifdef ECC192 + { + 24, + "ECC-192", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", + "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", + "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831", + "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012", + "7192B95FFC8DA78631011ED6B24CDD573F977A11E794811", + }, + #endif + #ifdef ECC224 + { + 28, + "ECC-224", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001", + "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D", + "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21", + "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34", + }, + #endif + #ifdef ECC256 + { + 32, + "ECC-256", + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", + "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", + "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", + }, + #endif + #ifdef ECC384 + { + 48, + "ECC-384", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", + "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", + }, + #endif + #ifdef ECC521 + { + 66, + "ECC-521", + "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "51953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", + "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", + "C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", + "11839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", + }, + #endif + { + 0, + NULL, NULL, NULL, NULL, NULL, NULL + } +}; +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc.c,v $ */ +/* $Revision: 1.40 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_ansi_x963_export.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** ECC X9.63 (Sec. 4.3.6) uncompressed export + @param key Key to export + @param out [out] destination of export + @param outlen [in/out] Length of destination and final output size + Return CRYPT_OK on success + */ +int ecc_ansi_x963_export(ecc_key *key, unsigned char *out, unsigned long *outlen) { + unsigned char buf[ECC_BUF_SIZE]; + unsigned long numlen; + + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + if (ltc_ecc_is_valid_idx(key->idx) == 0) { + return CRYPT_INVALID_ARG; + } + numlen = key->dp->size; + + if (*outlen < (1 + 2 * numlen)) { + *outlen = 1 + 2 * numlen; + return CRYPT_BUFFER_OVERFLOW; + } + + /* store byte 0x04 */ + out[0] = 0x04; + + /* pad and store x */ + zeromem(buf, sizeof(buf)); + mp_to_unsigned_bin(key->pubkey.x, buf + (numlen - mp_unsigned_bin_size(key->pubkey.x))); + XMEMCPY(out + 1, buf, numlen); + + /* pad and store y */ + zeromem(buf, sizeof(buf)); + mp_to_unsigned_bin(key->pubkey.y, buf + (numlen - mp_unsigned_bin_size(key->pubkey.y))); + XMEMCPY(out + 1 + numlen, buf, numlen); + + *outlen = 1 + 2 * numlen; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_ansi_x963_export.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_ansi_x963_import.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** Import an ANSI X9.63 format public key + @param in The input data to read + @param inlen The length of the input data + @param key [out] destination to store imported key \ + */ +int ecc_ansi_x963_import(const unsigned char *in, unsigned long inlen, ecc_key *key) { + return ecc_ansi_x963_import_ex(in, inlen, key, NULL); +} + +int ecc_ansi_x963_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, ltc_ecc_set_type *dp) { + int x, err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(key != NULL); + + /* must be odd */ + if ((inlen & 1) == 0) { + return CRYPT_INVALID_ARG; + } + + /* init key */ + if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, NULL) != CRYPT_OK) { + return CRYPT_MEM; + } + + /* check for 4, 6 or 7 */ + if ((in[0] != 4) && (in[0] != 6) && (in[0] != 7)) { + err = CRYPT_INVALID_PACKET; + goto error; + } + + /* read data */ + if ((err = mp_read_unsigned_bin(key->pubkey.x, (unsigned char *)in + 1, (inlen - 1) >> 1)) != CRYPT_OK) { + goto error; + } + + if ((err = mp_read_unsigned_bin(key->pubkey.y, (unsigned char *)in + 1 + ((inlen - 1) >> 1), (inlen - 1) >> 1)) != CRYPT_OK) { + goto error; + } + if ((err = mp_set(key->pubkey.z, 1)) != CRYPT_OK) { + goto error; + } + + if (dp == NULL) { + /* determine the idx */ + for (x = 0; ltc_ecc_sets[x].size != 0; x++) { + if ((unsigned)ltc_ecc_sets[x].size >= ((inlen - 1) >> 1)) { + break; + } + } + if (ltc_ecc_sets[x].size == 0) { + err = CRYPT_INVALID_PACKET; + goto error; + } + /* set the idx */ + key->idx = x; + key->dp = <c_ecc_sets[x]; + } else { + if (((inlen - 1) >> 1) != (unsigned long)dp->size) { + err = CRYPT_INVALID_PACKET; + goto error; + } + key->idx = -1; + key->dp = dp; + } + key->type = PK_PUBLIC; + + /* we're done */ + return CRYPT_OK; +error: + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_ansi_x963_import.c,v $ */ +/* $Revision: 1.11 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_decrypt_key.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Decrypt an ECC encrypted key + @param in The ciphertext + @param inlen The length of the ciphertext (octets) + @param out [out] The plaintext + @param outlen [in/out] The max size and resulting size of the plaintext + @param key The corresponding private ECC key + @return CRYPT_OK if successful + */ +int ecc_decrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + ecc_key *key) { + unsigned char *ecc_shared, *skey, *pub_expt; + unsigned long x, y, hashOID[32]; + int hash, err; + ecc_key pubkey; + ltc_asn1_list decode[3]; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* right key type? */ + if (key->type != PK_PRIVATE) { + return CRYPT_PK_NOT_PRIVATE; + } + + /* decode to find out hash */ + LTC_SET_ASN1(decode, 0, LTC_ASN1_OBJECT_IDENTIFIER, hashOID, sizeof(hashOID) / sizeof(hashOID[0])); + + if ((err = der_decode_sequence(in, inlen, decode, 1)) != CRYPT_OK) { + return err; + } + + hash = find_hash_oid(hashOID, decode[0].size); + if (hash_is_valid(hash) != CRYPT_OK) { + return CRYPT_INVALID_PACKET; + } + + /* we now have the hash! */ + + /* allocate memory */ + pub_expt = XMALLOC(ECC_BUF_SIZE); + ecc_shared = XMALLOC(ECC_BUF_SIZE); + skey = XMALLOC(MAXBLOCKSIZE); + if ((pub_expt == NULL) || (ecc_shared == NULL) || (skey == NULL)) { + if (pub_expt != NULL) { + XFREE(pub_expt); + } + if (ecc_shared != NULL) { + XFREE(ecc_shared); + } + if (skey != NULL) { + XFREE(skey); + } + return CRYPT_MEM; + } + LTC_SET_ASN1(decode, 1, LTC_ASN1_OCTET_STRING, pub_expt, ECC_BUF_SIZE); + LTC_SET_ASN1(decode, 2, LTC_ASN1_OCTET_STRING, skey, MAXBLOCKSIZE); + + /* read the structure in now */ + if ((err = der_decode_sequence(in, inlen, decode, 3)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* import ECC key from packet */ + if ((err = ecc_import(decode[1].data, decode[1].size, &pubkey)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* make shared key */ + x = ECC_BUF_SIZE; + if ((err = ecc_shared_secret(key, &pubkey, ecc_shared, &x)) != CRYPT_OK) { + ecc_free(&pubkey); + goto LBL_ERR; + } + ecc_free(&pubkey); + + y = MIN(ECC_BUF_SIZE, MAXBLOCKSIZE); + if ((err = hash_memory(hash, ecc_shared, x, ecc_shared, &y)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* ensure the hash of the shared secret is at least as big as the encrypt itself */ + if (decode[2].size > y) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } + + /* avoid buffer overflow */ + if (*outlen < decode[2].size) { + *outlen = decode[2].size; + err = CRYPT_BUFFER_OVERFLOW; + goto LBL_ERR; + } + + /* Decrypt the key */ + for (x = 0; x < decode[2].size; x++) { + out[x] = skey[x] ^ ecc_shared[x]; + } + *outlen = x; + + err = CRYPT_OK; +LBL_ERR: + #ifdef LTC_CLEAN_STACK + zeromem(pub_expt, ECC_BUF_SIZE); + zeromem(ecc_shared, ECC_BUF_SIZE); + zeromem(skey, MAXBLOCKSIZE); + #endif + + XFREE(pub_expt); + XFREE(ecc_shared); + XFREE(skey); + + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_decrypt_key.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_encrypt_key.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Encrypt a symmetric key with ECC + @param in The symmetric key you want to encrypt + @param inlen The length of the key to encrypt (octets) + @param out [out] The destination for the ciphertext + @param outlen [in/out] The max size and resulting size of the ciphertext + @param prng An active PRNG state + @param wprng The index of the PRNG you wish to use + @param hash The index of the hash you want to use + @param key The ECC key you want to encrypt to + @return CRYPT_OK if successful + */ +int ecc_encrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, int hash, + ecc_key *key) { + unsigned char *pub_expt, *ecc_shared, *skey; + ecc_key pubkey; + unsigned long x, y, pubkeysize; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* check that wprng/cipher/hash are not invalid */ + if ((err = prng_is_valid(wprng)) != CRYPT_OK) { + return err; + } + + if ((err = hash_is_valid(hash)) != CRYPT_OK) { + return err; + } + + if (inlen > hash_descriptor[hash].hashsize) { + return CRYPT_INVALID_HASH; + } + + /* make a random key and export the public copy */ + if ((err = ecc_make_key_ex(prng, wprng, &pubkey, key->dp)) != CRYPT_OK) { + return err; + } + + pub_expt = XMALLOC(ECC_BUF_SIZE); + ecc_shared = XMALLOC(ECC_BUF_SIZE); + skey = XMALLOC(MAXBLOCKSIZE); + if ((pub_expt == NULL) || (ecc_shared == NULL) || (skey == NULL)) { + if (pub_expt != NULL) { + XFREE(pub_expt); + } + if (ecc_shared != NULL) { + XFREE(ecc_shared); + } + if (skey != NULL) { + XFREE(skey); + } + ecc_free(&pubkey); + return CRYPT_MEM; + } + + pubkeysize = ECC_BUF_SIZE; + if ((err = ecc_export(pub_expt, &pubkeysize, PK_PUBLIC, &pubkey)) != CRYPT_OK) { + ecc_free(&pubkey); + goto LBL_ERR; + } + + /* make random key */ + x = ECC_BUF_SIZE; + if ((err = ecc_shared_secret(&pubkey, key, ecc_shared, &x)) != CRYPT_OK) { + ecc_free(&pubkey); + goto LBL_ERR; + } + ecc_free(&pubkey); + y = MAXBLOCKSIZE; + if ((err = hash_memory(hash, ecc_shared, x, skey, &y)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* Encrypt key */ + for (x = 0; x < inlen; x++) { + skey[x] ^= in[x]; + } + + err = der_encode_sequence_multi(out, outlen, + LTC_ASN1_OBJECT_IDENTIFIER, hash_descriptor[hash].OIDlen, hash_descriptor[hash].OID, + LTC_ASN1_OCTET_STRING, pubkeysize, pub_expt, + LTC_ASN1_OCTET_STRING, inlen, skey, + LTC_ASN1_EOL, 0UL, NULL); + +LBL_ERR: + #ifdef LTC_CLEAN_STACK + /* clean up */ + zeromem(pub_expt, ECC_BUF_SIZE); + zeromem(ecc_shared, ECC_BUF_SIZE); + zeromem(skey, MAXBLOCKSIZE); + #endif + + XFREE(skey); + XFREE(ecc_shared); + XFREE(pub_expt); + + return err; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_encrypt_key.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_export.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Export an ECC key as a binary packet + @param out [out] Destination for the key + @param outlen [in/out] Max size and resulting size of the exported key + @param type The type of key you want to export (PK_PRIVATE or PK_PUBLIC) + @param key The key to export + @return CRYPT_OK if successful + */ +int ecc_export(unsigned char *out, unsigned long *outlen, int type, ecc_key *key) { + int err; + unsigned char flags[1]; + unsigned long key_size; + + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* type valid? */ + if ((key->type != PK_PRIVATE) && (type == PK_PRIVATE)) { + return CRYPT_PK_TYPE_MISMATCH; + } + + if (ltc_ecc_is_valid_idx(key->idx) == 0) { + return CRYPT_INVALID_ARG; + } + + /* we store the NIST byte size */ + key_size = key->dp->size; + + if (type == PK_PRIVATE) { + flags[0] = 1; + err = der_encode_sequence_multi(out, outlen, + LTC_ASN1_BIT_STRING, 1UL, flags, + LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, + LTC_ASN1_INTEGER, 1UL, key->pubkey.x, + LTC_ASN1_INTEGER, 1UL, key->pubkey.y, + LTC_ASN1_INTEGER, 1UL, key->k, + LTC_ASN1_EOL, 0UL, NULL); + } else { + flags[0] = 0; + err = der_encode_sequence_multi(out, outlen, + LTC_ASN1_BIT_STRING, 1UL, flags, + LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, + LTC_ASN1_INTEGER, 1UL, key->pubkey.x, + LTC_ASN1_INTEGER, 1UL, key->pubkey.y, + LTC_ASN1_EOL, 0UL, NULL); + } + + return err; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_export.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_free.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Free an ECC key from memory + @param key The key you wish to free + */ +void ecc_free(ecc_key *key) { + LTC_ARGCHKVD(key != NULL); + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_free.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_get_size.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Get the size of an ECC key + @param key The key to get the size of + @return The size (octets) of the key or INT_MAX on error + */ +int ecc_get_size(ecc_key *key) { + LTC_ARGCHK(key != NULL); + if (ltc_ecc_is_valid_idx(key->idx)) + return key->dp->size; + else + return INT_MAX; /* large value known to cause it to fail when passed to ecc_make_key() */ +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_get_size.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_import.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +static int is_point(ecc_key *key) { + void *prime, *b, *t1, *t2; + int err; + + if ((err = mp_init_multi(&prime, &b, &t1, &t2, NULL)) != CRYPT_OK) { + return err; + } + + /* load prime and b */ + if ((err = mp_read_radix(prime, key->dp->prime, 16)) != CRYPT_OK) { + goto error; + } + if ((err = mp_read_radix(b, key->dp->B, 16)) != CRYPT_OK) { + goto error; + } + + /* compute y^2 */ + if ((err = mp_sqr(key->pubkey.y, t1)) != CRYPT_OK) { + goto error; + } + + /* compute x^3 */ + if ((err = mp_sqr(key->pubkey.x, t2)) != CRYPT_OK) { + goto error; + } + if ((err = mp_mod(t2, prime, t2)) != CRYPT_OK) { + goto error; + } + if ((err = mp_mul(key->pubkey.x, t2, t2)) != CRYPT_OK) { + goto error; + } + + /* compute y^2 - x^3 */ + if ((err = mp_sub(t1, t2, t1)) != CRYPT_OK) { + goto error; + } + + /* compute y^2 - x^3 + 3x */ + if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { + goto error; + } + if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { + goto error; + } + if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { + goto error; + } + if ((err = mp_mod(t1, prime, t1)) != CRYPT_OK) { + goto error; + } + while (mp_cmp_d(t1, 0) == LTC_MP_LT) { + if ((err = mp_add(t1, prime, t1)) != CRYPT_OK) { + goto error; + } + } + while (mp_cmp(t1, prime) != LTC_MP_LT) { + if ((err = mp_sub(t1, prime, t1)) != CRYPT_OK) { + goto error; + } + } + + /* compare to b */ + if (mp_cmp(t1, b) != LTC_MP_EQ) { + err = CRYPT_INVALID_PACKET; + } else { + err = CRYPT_OK; + } + +error: + mp_clear_multi(prime, b, t1, t2, NULL); + return err; +} + +/** + Import an ECC key from a binary packet + @param in The packet to import + @param inlen The length of the packet + @param key [out] The destination of the import + @return CRYPT_OK if successful, upon error all allocated memory will be freed + */ +int ecc_import(const unsigned char *in, unsigned long inlen, ecc_key *key) { + return ecc_import_ex(in, inlen, key, NULL); +} + +/** + Import an ECC key from a binary packet, using user supplied domain params rather than one of the NIST ones + @param in The packet to import + @param inlen The length of the packet + @param key [out] The destination of the import + @param dp pointer to user supplied params; must be the same as the params used when exporting + @return CRYPT_OK if successful, upon error all allocated memory will be freed + */ +int ecc_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, const ltc_ecc_set_type *dp) { + unsigned long key_size; + unsigned char flags[1]; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(ltc_mp.name != NULL); + + /* init key */ + if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, NULL) != CRYPT_OK) { + return CRYPT_MEM; + } + + /* find out what type of key it is */ + if ((err = der_decode_sequence_multi(in, inlen, + LTC_ASN1_BIT_STRING, 1UL, &flags, + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { + goto done; + } + + + if (flags[0] == 1) { + /* private key */ + key->type = PK_PRIVATE; + if ((err = der_decode_sequence_multi(in, inlen, + LTC_ASN1_BIT_STRING, 1UL, flags, + LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, + LTC_ASN1_INTEGER, 1UL, key->pubkey.x, + LTC_ASN1_INTEGER, 1UL, key->pubkey.y, + LTC_ASN1_INTEGER, 1UL, key->k, + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { + goto done; + } + } else { + /* public key */ + key->type = PK_PUBLIC; + if ((err = der_decode_sequence_multi(in, inlen, + LTC_ASN1_BIT_STRING, 1UL, flags, + LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, + LTC_ASN1_INTEGER, 1UL, key->pubkey.x, + LTC_ASN1_INTEGER, 1UL, key->pubkey.y, + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { + goto done; + } + } + + if (dp == NULL) { + /* find the idx */ + for (key->idx = 0; ltc_ecc_sets[key->idx].size && (unsigned long)ltc_ecc_sets[key->idx].size != key_size; ++key->idx); + if (ltc_ecc_sets[key->idx].size == 0) { + err = CRYPT_INVALID_PACKET; + goto done; + } + key->dp = <c_ecc_sets[key->idx]; + } else { + key->idx = -1; + key->dp = dp; + } + /* set z */ + if ((err = mp_set(key->pubkey.z, 1)) != CRYPT_OK) { + goto done; + } + + /* is it a point on the curve? */ + if ((err = is_point(key)) != CRYPT_OK) { + goto done; + } + + /* we're good */ + return CRYPT_OK; +done: + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_import.c,v $ */ +/* $Revision: 1.13 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_make_key.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Make a new ECC key + @param prng An active PRNG state + @param wprng The index of the PRNG you wish to use + @param keysize The keysize for the new key (in octets from 20 to 65 bytes) + @param key [out] Destination of the newly created key + @return CRYPT_OK if successful, upon error all allocated memory will be freed + */ +int ecc_make_key(prng_state *prng, int wprng, int keysize, ecc_key *key) { + int x, err; + + /* find key size */ + for (x = 0; (keysize > ltc_ecc_sets[x].size) && (ltc_ecc_sets[x].size != 0); x++); + keysize = ltc_ecc_sets[x].size; + + if ((keysize > ECC_MAXSIZE) || (ltc_ecc_sets[x].size == 0)) { + return CRYPT_INVALID_KEYSIZE; + } + err = ecc_make_key_ex(prng, wprng, key, <c_ecc_sets[x]); + key->idx = x; + return err; +} + +int ecc_make_key_ex(prng_state *prng, int wprng, ecc_key *key, const ltc_ecc_set_type *dp) { + int err; + ecc_point *base; + void *prime, *order; + unsigned char *buf; + int keysize; + + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(ltc_mp.name != NULL); + LTC_ARGCHK(dp != NULL); + + /* good prng? */ + if ((err = prng_is_valid(wprng)) != CRYPT_OK) { + return err; + } + + key->idx = -1; + key->dp = dp; + keysize = dp->size; + + /* allocate ram */ + base = NULL; + buf = XMALLOC(ECC_MAXSIZE); + if (buf == NULL) { + return CRYPT_MEM; + } + + /* make up random string */ + if (prng_descriptor[wprng].read(buf, (unsigned long)keysize, prng) != (unsigned long)keysize) { + err = CRYPT_ERROR_READPRNG; + goto ERR_BUF; + } + + /* setup the key variables */ + if ((err = mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, &prime, &order, NULL)) != CRYPT_OK) { + goto ERR_BUF; + } + base = ltc_ecc_new_point(); + if (base == NULL) { + err = CRYPT_MEM; + goto errkey; + } + + /* read in the specs for this key */ + if ((err = mp_read_radix(prime, (char *)key->dp->prime, 16)) != CRYPT_OK) { + goto errkey; + } + if ((err = mp_read_radix(order, (char *)key->dp->order, 16)) != CRYPT_OK) { + goto errkey; + } + if ((err = mp_read_radix(base->x, (char *)key->dp->Gx, 16)) != CRYPT_OK) { + goto errkey; + } + if ((err = mp_read_radix(base->y, (char *)key->dp->Gy, 16)) != CRYPT_OK) { + goto errkey; + } + if ((err = mp_set(base->z, 1)) != CRYPT_OK) { + goto errkey; + } + if ((err = mp_read_unsigned_bin(key->k, (unsigned char *)buf, keysize)) != CRYPT_OK) { + goto errkey; + } + + /* the key should be smaller than the order of base point */ + if (mp_cmp(key->k, order) != LTC_MP_LT) { + if ((err = mp_mod(key->k, order, key->k)) != CRYPT_OK) { + goto errkey; + } + } + /* make the public key */ + if ((err = ltc_mp.ecc_ptmul(key->k, base, &key->pubkey, prime, 1)) != CRYPT_OK) { + goto errkey; + } + key->type = PK_PRIVATE; + + /* free up ram */ + err = CRYPT_OK; + goto cleanup; +errkey: + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); +cleanup: + ltc_ecc_del_point(base); + mp_clear_multi(prime, order, NULL); +ERR_BUF: + #ifdef LTC_CLEAN_STACK + zeromem(buf, ECC_MAXSIZE); + #endif + XFREE(buf); + return err; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_make_key.c,v $ */ +/* $Revision: 1.13 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_shared_secret.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Create an ECC shared secret between two keys + @param private_key The private ECC key + @param public_key The public key + @param out [out] Destination of the shared secret (Conforms to EC-DH from ANSI X9.63) + @param outlen [in/out] The max size and resulting size of the shared secret + @return CRYPT_OK if successful + */ +int ecc_shared_secret(ecc_key *private_key, ecc_key *public_key, + unsigned char *out, unsigned long *outlen) { + unsigned long x; + ecc_point *result; + void *prime; + int err; + + LTC_ARGCHK(private_key != NULL); + LTC_ARGCHK(public_key != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* type valid? */ + if (private_key->type != PK_PRIVATE) { + return CRYPT_PK_NOT_PRIVATE; + } + + if ((ltc_ecc_is_valid_idx(private_key->idx) == 0) || (ltc_ecc_is_valid_idx(public_key->idx) == 0)) { + return CRYPT_INVALID_ARG; + } + + if (XSTRCMP(private_key->dp->name, public_key->dp->name) != 0) { + return CRYPT_PK_TYPE_MISMATCH; + } + + /* make new point */ + result = ltc_ecc_new_point(); + if (result == NULL) { + return CRYPT_MEM; + } + + if ((err = mp_init(&prime)) != CRYPT_OK) { + ltc_ecc_del_point(result); + return err; + } + + if ((err = mp_read_radix(prime, (char *)private_key->dp->prime, 16)) != CRYPT_OK) { + goto done; + } + if ((err = ltc_mp.ecc_ptmul(private_key->k, &public_key->pubkey, result, prime, 1)) != CRYPT_OK) { + goto done; + } + + x = (unsigned long)mp_unsigned_bin_size(prime); + if (*outlen < x) { + *outlen = x; + err = CRYPT_BUFFER_OVERFLOW; + goto done; + } + zeromem(out, x); + if ((err = mp_to_unsigned_bin(result->x, out + (x - mp_unsigned_bin_size(result->x)))) != CRYPT_OK) { + goto done; + } + + err = CRYPT_OK; + *outlen = x; +done: + mp_clear(prime); + ltc_ecc_del_point(result); + return err; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_shared_secret.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_sign_hash.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Sign a message digest + @param in The message digest to sign + @param inlen The length of the digest + @param out [out] The destination for the signature + @param outlen [in/out] The max size and resulting size of the signature + @param prng An active PRNG state + @param wprng The index of the PRNG you wish to use + @param key A private ECC key + @return CRYPT_OK if successful + */ +int ecc_sign_hash(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, ecc_key *key) { + ecc_key pubkey; + void *r, *s, *e, *p; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* is this a private key? */ + if (key->type != PK_PRIVATE) { + return CRYPT_PK_NOT_PRIVATE; + } + + /* is the IDX valid ? */ + if (ltc_ecc_is_valid_idx(key->idx) != 1) { + return CRYPT_PK_INVALID_TYPE; + } + + if ((err = prng_is_valid(wprng)) != CRYPT_OK) { + return err; + } + + /* get the hash and load it as a bignum into 'e' */ + /* init the bignums */ + if ((err = mp_init_multi(&r, &s, &p, &e, NULL)) != CRYPT_OK) { + return err; + } + if ((err = mp_read_radix(p, (char *)key->dp->order, 16)) != CRYPT_OK) { + goto errnokey; + } + if ((err = mp_read_unsigned_bin(e, (unsigned char *)in, (int)inlen)) != CRYPT_OK) { + goto errnokey; + } + + /* make up a key and export the public copy */ + for ( ; ; ) { + if ((err = ecc_make_key_ex(prng, wprng, &pubkey, key->dp)) != CRYPT_OK) { + goto errnokey; + } + + /* find r = x1 mod n */ + if ((err = mp_mod(pubkey.pubkey.x, p, r)) != CRYPT_OK) { + goto error; + } + + if (mp_iszero(r) == LTC_MP_YES) { + ecc_free(&pubkey); + } else { + /* find s = (e + xr)/k */ + if ((err = mp_invmod(pubkey.k, p, pubkey.k)) != CRYPT_OK) { + goto error; + } /* k = 1/k */ + if ((err = mp_mulmod(key->k, r, p, s)) != CRYPT_OK) { + goto error; + } /* s = xr */ + if ((err = mp_add(e, s, s)) != CRYPT_OK) { + goto error; + } /* s = e + xr */ + if ((err = mp_mod(s, p, s)) != CRYPT_OK) { + goto error; + } /* s = e + xr */ + if ((err = mp_mulmod(s, pubkey.k, p, s)) != CRYPT_OK) { + goto error; + } /* s = (e + xr)/k */ + ecc_free(&pubkey); + if (mp_iszero(s) == LTC_MP_NO) { + break; + } + } + } + + /* store as SEQUENCE { r, s -- integer } */ + err = der_encode_sequence_multi(out, outlen, + LTC_ASN1_INTEGER, 1UL, r, + LTC_ASN1_INTEGER, 1UL, s, + LTC_ASN1_EOL, 0UL, NULL); + goto errnokey; +error: + ecc_free(&pubkey); +errnokey: + mp_clear_multi(r, s, p, e, NULL); + return err; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_sign_hash.c,v $ */ +/* $Revision: 1.11 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_sizes.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +void ecc_sizes(int *low, int *high) { + int i; + + LTC_ARGCHKVD(low != NULL); + LTC_ARGCHKVD(high != NULL); + + *low = INT_MAX; + *high = 0; + for (i = 0; ltc_ecc_sets[i].size != 0; i++) { + if (ltc_ecc_sets[i].size < *low) { + *low = ltc_ecc_sets[i].size; + } + if (ltc_ecc_sets[i].size > *high) { + *high = ltc_ecc_sets[i].size; + } + } +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_sizes.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_test.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Perform on the ECC system + @return CRYPT_OK if successful + */ +int ecc_test(void) { + void *modulus, *order; + ecc_point *G, *GG; + int i, err, primality; + + if ((err = mp_init_multi(&modulus, &order, NULL)) != CRYPT_OK) { + return err; + } + + G = ltc_ecc_new_point(); + GG = ltc_ecc_new_point(); + if ((G == NULL) || (GG == NULL)) { + mp_clear_multi(modulus, order, NULL); + ltc_ecc_del_point(G); + ltc_ecc_del_point(GG); + return CRYPT_MEM; + } + + for (i = 0; ltc_ecc_sets[i].size; i++) { + #if 0 + printf("Testing %d\n", ltc_ecc_sets[i].size); + #endif + if ((err = mp_read_radix(modulus, (char *)ltc_ecc_sets[i].prime, 16)) != CRYPT_OK) { + goto done; + } + if ((err = mp_read_radix(order, (char *)ltc_ecc_sets[i].order, 16)) != CRYPT_OK) { + goto done; + } + + /* is prime actually prime? */ + if ((err = mp_prime_is_prime(modulus, 8, &primality)) != CRYPT_OK) { + goto done; + } + if (primality == 0) { + err = CRYPT_FAIL_TESTVECTOR; + goto done; + } + + /* is order prime ? */ + if ((err = mp_prime_is_prime(order, 8, &primality)) != CRYPT_OK) { + goto done; + } + if (primality == 0) { + err = CRYPT_FAIL_TESTVECTOR; + goto done; + } + + if ((err = mp_read_radix(G->x, (char *)ltc_ecc_sets[i].Gx, 16)) != CRYPT_OK) { + goto done; + } + if ((err = mp_read_radix(G->y, (char *)ltc_ecc_sets[i].Gy, 16)) != CRYPT_OK) { + goto done; + } + mp_set(G->z, 1); + + /* then we should have G == (order + 1)G */ + if ((err = mp_add_d(order, 1, order)) != CRYPT_OK) { + goto done; + } + if ((err = ltc_mp.ecc_ptmul(order, G, GG, modulus, 1)) != CRYPT_OK) { + goto done; + } + if ((mp_cmp(G->x, GG->x) != LTC_MP_EQ) || (mp_cmp(G->y, GG->y) != LTC_MP_EQ)) { + err = CRYPT_FAIL_TESTVECTOR; + goto done; + } + } + err = CRYPT_OK; +done: + ltc_ecc_del_point(GG); + ltc_ecc_del_point(G); + mp_clear_multi(order, modulus, NULL); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_test.c,v $ */ +/* $Revision: 1.12 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_verify_hash.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/* verify + * + * w = s^-1 mod n + * u1 = xw + * u2 = rw + * X = u1*G + u2*Q + * v = X_x1 mod n + * accept if v == r + */ + +/** + Verify an ECC signature + @param sig The signature to verify + @param siglen The length of the signature (octets) + @param hash The hash (message digest) that was signed + @param hashlen The length of the hash (octets) + @param stat Result of signature, 1==valid, 0==invalid + @param key The corresponding public ECC key + @return CRYPT_OK if successful (even if the signature is not valid) + */ +int ecc_verify_hash(const unsigned char *sig, unsigned long siglen, + const unsigned char *hash, unsigned long hashlen, + int *stat, ecc_key *key) { + ecc_point *mG, *mQ; + void *r, *s, *v, *w, *u1, *u2, *e, *p, *m; + void *mp; + int err; + + LTC_ARGCHK(sig != NULL); + LTC_ARGCHK(hash != NULL); + LTC_ARGCHK(stat != NULL); + LTC_ARGCHK(key != NULL); + + /* default to invalid signature */ + *stat = 0; + mp = NULL; + + /* is the IDX valid ? */ + if (ltc_ecc_is_valid_idx(key->idx) != 1) { + return CRYPT_PK_INVALID_TYPE; + } + + /* allocate ints */ + if ((err = mp_init_multi(&r, &s, &v, &w, &u1, &u2, &p, &e, &m, NULL)) != CRYPT_OK) { + return CRYPT_MEM; + } + + /* allocate points */ + mG = ltc_ecc_new_point(); + mQ = ltc_ecc_new_point(); + if ((mQ == NULL) || (mG == NULL)) { + err = CRYPT_MEM; + goto error; + } + + /* parse header */ + if ((err = der_decode_sequence_multi(sig, siglen, + LTC_ASN1_INTEGER, 1UL, r, + LTC_ASN1_INTEGER, 1UL, s, + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { + goto error; + } + + /* get the order */ + if ((err = mp_read_radix(p, (char *)key->dp->order, 16)) != CRYPT_OK) { + goto error; + } + + /* get the modulus */ + if ((err = mp_read_radix(m, (char *)key->dp->prime, 16)) != CRYPT_OK) { + goto error; + } + + /* check for zero */ + if (mp_iszero(r) || mp_iszero(s) || (mp_cmp(r, p) != LTC_MP_LT) || (mp_cmp(s, p) != LTC_MP_LT)) { + err = CRYPT_INVALID_PACKET; + goto error; + } + + /* read hash */ + if ((err = mp_read_unsigned_bin(e, (unsigned char *)hash, (int)hashlen)) != CRYPT_OK) { + goto error; + } + + /* w = s^-1 mod n */ + if ((err = mp_invmod(s, p, w)) != CRYPT_OK) { + goto error; + } + + /* u1 = ew */ + if ((err = mp_mulmod(e, w, p, u1)) != CRYPT_OK) { + goto error; + } + + /* u2 = rw */ + if ((err = mp_mulmod(r, w, p, u2)) != CRYPT_OK) { + goto error; + } + + /* find mG and mQ */ + if ((err = mp_read_radix(mG->x, (char *)key->dp->Gx, 16)) != CRYPT_OK) { + goto error; + } + if ((err = mp_read_radix(mG->y, (char *)key->dp->Gy, 16)) != CRYPT_OK) { + goto error; + } + if ((err = mp_set(mG->z, 1)) != CRYPT_OK) { + goto error; + } + + if ((err = mp_copy(key->pubkey.x, mQ->x)) != CRYPT_OK) { + goto error; + } + if ((err = mp_copy(key->pubkey.y, mQ->y)) != CRYPT_OK) { + goto error; + } + if ((err = mp_copy(key->pubkey.z, mQ->z)) != CRYPT_OK) { + goto error; + } + + /* compute u1*mG + u2*mQ = mG */ + if (ltc_mp.ecc_mul2add == NULL) { + if ((err = ltc_mp.ecc_ptmul(u1, mG, mG, m, 0)) != CRYPT_OK) { + goto error; + } + if ((err = ltc_mp.ecc_ptmul(u2, mQ, mQ, m, 0)) != CRYPT_OK) { + goto error; + } + + /* find the montgomery mp */ + if ((err = mp_montgomery_setup(m, &mp)) != CRYPT_OK) { + goto error; + } + + /* add them */ + if ((err = ltc_mp.ecc_ptadd(mQ, mG, mG, m, mp)) != CRYPT_OK) { + goto error; + } + + /* reduce */ + if ((err = ltc_mp.ecc_map(mG, m, mp)) != CRYPT_OK) { + goto error; + } + } else { + /* use Shamir's trick to compute u1*mG + u2*mQ using half of the doubles */ + if ((err = ltc_mp.ecc_mul2add(mG, u1, mQ, u2, mG, m)) != CRYPT_OK) { + goto error; + } + } + + /* v = X_x1 mod n */ + if ((err = mp_mod(mG->x, p, v)) != CRYPT_OK) { + goto error; + } + + /* does v == r */ + if (mp_cmp(v, r) == LTC_MP_EQ) { + *stat = 1; + } + + /* clear up and return */ + err = CRYPT_OK; +error: + ltc_ecc_del_point(mG); + ltc_ecc_del_point(mQ); + mp_clear_multi(r, s, v, w, u1, u2, p, e, m, NULL); + if (mp != NULL) { + mp_montgomery_free(mp); + } + return err; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_verify_hash.c,v $ */ +/* $Revision: 1.14 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + + +/** + @file error_to_string.c + Convert error codes to ASCII strings, Tom St Denis + */ + +static const char * const err_2_str[] = +{ + "CRYPT_OK", + "CRYPT_ERROR", + "Non-fatal 'no-operation' requested.", + + "Invalid keysize for block cipher.", + "Invalid number of rounds for block cipher.", + "Algorithm failed test vectors.", + + "Buffer overflow.", + "Invalid input packet.", + + "Invalid number of bits for a PRNG.", + "Error reading the PRNG.", + + "Invalid cipher specified.", + "Invalid hash specified.", + "Invalid PRNG specified.", + + "Out of memory.", + + "Invalid PK key or key type specified for function.", + "A private PK key is required.", + + "Invalid argument provided.", + "File Not Found", + + "Invalid PK type.", + "Invalid PK system.", + "Duplicate PK key found on keyring.", + "Key not found in keyring.", + "Invalid sized parameter.", + + "Invalid size for prime.", +}; + +/** + Convert an LTC error code to ASCII + @param err The error code + @return A pointer to the ASCII NUL terminated string for the error or "Invalid error code." if the err code was not valid. + */ +const char *error_to_string(int err) { + if ((err < 0) || (err >= (int)(sizeof(err_2_str) / sizeof(err_2_str[0])))) { + return "Invalid error code."; + } else { + return err_2_str[err]; + } +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/error_to_string.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#define DESC_DEF_ONLY + + + +/* $Source: /cvs/libtom/libtomcrypt/src/math/gmp_desc.c,v $ */ +/* $Revision: 1.16 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file hash_file.c + Hash a file, Tom St Denis + */ + +/** + @param hash The index of the hash desired + @param fname The name of the file you wish to hash + @param out [out] The destination of the digest + @param outlen [in/out] The max size and resulting size of the message digest + @result CRYPT_OK if successful + */ +int hash_file(int hash, const char *fname, unsigned char *out, unsigned long *outlen) { +#ifdef LTC_NO_FILE + return CRYPT_NOP; +#else + FILE *in; + int err; + LTC_ARGCHK(fname != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + if ((err = hash_is_valid(hash)) != CRYPT_OK) { + return err; + } + + in = fopen(fname, "rb"); + if (in == NULL) { + return CRYPT_FILE_NOTFOUND; + } + + err = hash_filehandle(hash, in, out, outlen); + if (fclose(in) != 0) { + return CRYPT_ERROR; + } + + return err; +#endif +} + +/* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_file.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:23 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file hash_filehandle.c + Hash open files, Tom St Denis + */ + +/** + Hash data from an open file handle. + @param hash The index of the hash you want to use + @param in The FILE* handle of the file you want to hash + @param out [out] The destination of the digest + @param outlen [in/out] The max size and resulting size of the digest + @result CRYPT_OK if successful + */ +int hash_filehandle(int hash, FILE *in, unsigned char *out, unsigned long *outlen) { +#ifdef LTC_NO_FILE + return CRYPT_NOP; +#else + hash_state md; + unsigned char buf[512]; + size_t x; + int err; + + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(in != NULL); + + if ((err = hash_is_valid(hash)) != CRYPT_OK) { + return err; + } + + if (*outlen < hash_descriptor[hash].hashsize) { + *outlen = hash_descriptor[hash].hashsize; + return CRYPT_BUFFER_OVERFLOW; + } + if ((err = hash_descriptor[hash].init(&md)) != CRYPT_OK) { + return err; + } + + *outlen = hash_descriptor[hash].hashsize; + do { + x = fread(buf, 1, sizeof(buf), in); + if ((err = hash_descriptor[hash].process(&md, buf, x)) != CRYPT_OK) { + return err; + } + } while (x == sizeof(buf)); + err = hash_descriptor[hash].done(&md, out); + + #ifdef LTC_CLEAN_STACK + zeromem(buf, sizeof(buf)); + #endif + return err; +#endif +} + +/* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_filehandle.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:23 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file hash_memory.c + Hash memory helper, Tom St Denis + */ + +/** + Hash a block of memory and store the digest. + @param hash The index of the hash you wish to use + @param in The data you wish to hash + @param inlen The length of the data to hash (octets) + @param out [out] Where to store the digest + @param outlen [in/out] Max size and resulting size of the digest + @return CRYPT_OK if successful + */ +int hash_memory(int hash, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) { + hash_state *md; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + if ((err = hash_is_valid(hash)) != CRYPT_OK) { + return err; + } + + if (*outlen < hash_descriptor[hash].hashsize) { + *outlen = hash_descriptor[hash].hashsize; + return CRYPT_BUFFER_OVERFLOW; + } + + md = XMALLOC(sizeof(hash_state)); + if (md == NULL) { + return CRYPT_MEM; + } + + if ((err = hash_descriptor[hash].init(md)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash].process(md, in, inlen)) != CRYPT_OK) { + goto LBL_ERR; + } + err = hash_descriptor[hash].done(md, out); + *outlen = hash_descriptor[hash].hashsize; +LBL_ERR: +#ifdef LTC_CLEAN_STACK + zeromem(md, sizeof(hash_state)); +#endif + XFREE(md); + + return err; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_memory.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:23 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#include + +/** + @file hash_memory_multi.c + Hash (multiple buffers) memory helper, Tom St Denis + */ + +/** + Hash multiple (non-adjacent) blocks of memory at once. + @param hash The index of the hash you wish to use + @param out [out] Where to store the digest + @param outlen [in/out] Max size and resulting size of the digest + @param in The data you wish to hash + @param inlen The length of the data to hash (octets) + @param ... tuples of (data,len) pairs to hash, terminated with a (NULL,x) (x=don't care) + @return CRYPT_OK if successful + */ +int hash_memory_multi(int hash, unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...) { + hash_state *md; + int err; + va_list args; + const unsigned char *curptr; + unsigned long curlen; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + if ((err = hash_is_valid(hash)) != CRYPT_OK) { + return err; + } + + if (*outlen < hash_descriptor[hash].hashsize) { + *outlen = hash_descriptor[hash].hashsize; + return CRYPT_BUFFER_OVERFLOW; + } + + md = XMALLOC(sizeof(hash_state)); + if (md == NULL) { + return CRYPT_MEM; + } + + if ((err = hash_descriptor[hash].init(md)) != CRYPT_OK) { + goto LBL_ERR; + } + + va_start(args, inlen); + curptr = in; + curlen = inlen; + for ( ; ; ) { + /* process buf */ + if ((err = hash_descriptor[hash].process(md, curptr, curlen)) != CRYPT_OK) { + goto LBL_ERR; + } + /* step to next */ + curptr = va_arg(args, const unsigned char *); + if (curptr == NULL) { + break; + } + curlen = va_arg(args, unsigned long); + } + err = hash_descriptor[hash].done(md, out); + *outlen = hash_descriptor[hash].hashsize; +LBL_ERR: +#ifdef LTC_CLEAN_STACK + zeromem(md, sizeof(hash_state)); +#endif + XFREE(md); + va_end(args); + return err; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_memory_multi.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:23 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ltc_ecc_is_valid_idx.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** Returns whether an ECC idx is valid or not + @param n The idx number to check + @return 1 if valid, 0 if not + */ +int ltc_ecc_is_valid_idx(int n) { + int x; + + for (x = 0; ltc_ecc_sets[x].size != 0; x++); + /* -1 is a valid index --- indicating that the domain params were supplied by the user */ + if ((n >= -1) && (n < x)) { + return 1; + } + return 0; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_is_valid_idx.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ltc_ecc_map.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Map a projective jacbobian point back to affine space + @param P [in/out] The point to map + @param modulus The modulus of the field the ECC curve is in + @param mp The "b" value from montgomery_setup() + @return CRYPT_OK on success + */ +int ltc_ecc_map(ecc_point *P, void *modulus, void *mp) { + void *t1, *t2; + int err; + + LTC_ARGCHK(P != NULL); + LTC_ARGCHK(modulus != NULL); + LTC_ARGCHK(mp != NULL); + + if ((err = mp_init_multi(&t1, &t2, NULL)) != CRYPT_OK) { + return CRYPT_MEM; + } + + /* first map z back to normal */ + if ((err = mp_montgomery_reduce(P->z, modulus, mp)) != CRYPT_OK) { + goto done; + } + + /* get 1/z */ + if ((err = mp_invmod(P->z, modulus, t1)) != CRYPT_OK) { + goto done; + } + + /* get 1/z^2 and 1/z^3 */ + if ((err = mp_sqr(t1, t2)) != CRYPT_OK) { + goto done; + } + if ((err = mp_mod(t2, modulus, t2)) != CRYPT_OK) { + goto done; + } + if ((err = mp_mul(t1, t2, t1)) != CRYPT_OK) { + goto done; + } + if ((err = mp_mod(t1, modulus, t1)) != CRYPT_OK) { + goto done; + } + + /* multiply against x/y */ + if ((err = mp_mul(P->x, t2, P->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(P->x, modulus, mp)) != CRYPT_OK) { + goto done; + } + if ((err = mp_mul(P->y, t1, P->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(P->y, modulus, mp)) != CRYPT_OK) { + goto done; + } + if ((err = mp_set(P->z, 1)) != CRYPT_OK) { + goto done; + } + + err = CRYPT_OK; +done: + mp_clear_multi(t1, t2, NULL); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_map.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ltc_ecc_mul2add.c + ECC Crypto, Shamir's Trick, Tom St Denis + */ + +#ifdef LTC_MECC + + #ifdef LTC_ECC_SHAMIR + +/** Computes kA*A + kB*B = C using Shamir's Trick + @param A First point to multiply + @param kA What to multiple A by + @param B Second point to multiply + @param kB What to multiple B by + @param C [out] Destination point (can overlap with A or B + @param modulus Modulus for curve + @return CRYPT_OK on success + */ +int ltc_ecc_mul2add(ecc_point *A, void *kA, + ecc_point *B, void *kB, + ecc_point *C, + void *modulus) { + ecc_point *precomp[16]; + unsigned bitbufA, bitbufB, lenA, lenB, len, x, y, nA, nB, nibble; + unsigned char *tA, *tB; + int err, first; + void *mp, *mu; + + /* argchks */ + LTC_ARGCHK(A != NULL); + LTC_ARGCHK(B != NULL); + LTC_ARGCHK(C != NULL); + LTC_ARGCHK(kA != NULL); + LTC_ARGCHK(kB != NULL); + LTC_ARGCHK(modulus != NULL); + + /* allocate memory */ + tA = XCALLOC(1, ECC_BUF_SIZE); + if (tA == NULL) { + return CRYPT_MEM; + } + tB = XCALLOC(1, ECC_BUF_SIZE); + if (tB == NULL) { + XFREE(tA); + return CRYPT_MEM; + } + + /* get sizes */ + lenA = mp_unsigned_bin_size(kA); + lenB = mp_unsigned_bin_size(kB); + len = MAX(lenA, lenB); + + /* sanity check */ + if ((lenA > ECC_BUF_SIZE) || (lenB > ECC_BUF_SIZE)) { + err = CRYPT_INVALID_ARG; + goto ERR_T; + } + + /* extract and justify kA */ + mp_to_unsigned_bin(kA, (len - lenA) + tA); + + /* extract and justify kB */ + mp_to_unsigned_bin(kB, (len - lenB) + tB); + + /* allocate the table */ + for (x = 0; x < 16; x++) { + precomp[x] = ltc_ecc_new_point(); + if (precomp[x] == NULL) { + for (y = 0; y < x; ++y) { + ltc_ecc_del_point(precomp[y]); + } + err = CRYPT_MEM; + goto ERR_T; + } + } + + /* init montgomery reduction */ + if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { + goto ERR_P; + } + if ((err = mp_init(&mu)) != CRYPT_OK) { + goto ERR_MP; + } + if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) { + goto ERR_MU; + } + + /* copy ones ... */ + if ((err = mp_mulmod(A->x, mu, modulus, precomp[1]->x)) != CRYPT_OK) { + goto ERR_MU; + } + if ((err = mp_mulmod(A->y, mu, modulus, precomp[1]->y)) != CRYPT_OK) { + goto ERR_MU; + } + if ((err = mp_mulmod(A->z, mu, modulus, precomp[1]->z)) != CRYPT_OK) { + goto ERR_MU; + } + + if ((err = mp_mulmod(B->x, mu, modulus, precomp[1 << 2]->x)) != CRYPT_OK) { + goto ERR_MU; + } + if ((err = mp_mulmod(B->y, mu, modulus, precomp[1 << 2]->y)) != CRYPT_OK) { + goto ERR_MU; + } + if ((err = mp_mulmod(B->z, mu, modulus, precomp[1 << 2]->z)) != CRYPT_OK) { + goto ERR_MU; + } + + /* precomp [i,0](A + B) table */ + if ((err = ltc_mp.ecc_ptdbl(precomp[1], precomp[2], modulus, mp)) != CRYPT_OK) { + goto ERR_MU; + } + if ((err = ltc_mp.ecc_ptadd(precomp[1], precomp[2], precomp[3], modulus, mp)) != CRYPT_OK) { + goto ERR_MU; + } + + /* precomp [0,i](A + B) table */ + if ((err = ltc_mp.ecc_ptdbl(precomp[1 << 2], precomp[2 << 2], modulus, mp)) != CRYPT_OK) { + goto ERR_MU; + } + if ((err = ltc_mp.ecc_ptadd(precomp[1 << 2], precomp[2 << 2], precomp[3 << 2], modulus, mp)) != CRYPT_OK) { + goto ERR_MU; + } + + /* precomp [i,j](A + B) table (i != 0, j != 0) */ + for (x = 1; x < 4; x++) { + for (y = 1; y < 4; y++) { + if ((err = ltc_mp.ecc_ptadd(precomp[x], precomp[(y << 2)], precomp[x + (y << 2)], modulus, mp)) != CRYPT_OK) { + goto ERR_MU; + } + } + } + + nibble = 3; + first = 1; + bitbufA = tA[0]; + bitbufB = tB[0]; + + /* for every byte of the multiplicands */ + for (x = -1; ; ) { + /* grab a nibble */ + if (++nibble == 4) { + ++x; + if (x == len) break; + bitbufA = tA[x]; + bitbufB = tB[x]; + nibble = 0; + } + + /* extract two bits from both, shift/update */ + nA = (bitbufA >> 6) & 0x03; + nB = (bitbufB >> 6) & 0x03; + bitbufA = (bitbufA << 2) & 0xFF; + bitbufB = (bitbufB << 2) & 0xFF; + + /* if both zero, if first, continue */ + if ((nA == 0) && (nB == 0) && (first == 1)) { + continue; + } + + /* double twice, only if this isn't the first */ + if (first == 0) { + /* double twice */ + if ((err = ltc_mp.ecc_ptdbl(C, C, modulus, mp)) != CRYPT_OK) { + goto ERR_MU; + } + if ((err = ltc_mp.ecc_ptdbl(C, C, modulus, mp)) != CRYPT_OK) { + goto ERR_MU; + } + } + + /* if not both zero */ + if ((nA != 0) || (nB != 0)) { + if (first == 1) { + /* if first, copy from table */ + first = 0; + if ((err = mp_copy(precomp[nA + (nB << 2)]->x, C->x)) != CRYPT_OK) { + goto ERR_MU; + } + if ((err = mp_copy(precomp[nA + (nB << 2)]->y, C->y)) != CRYPT_OK) { + goto ERR_MU; + } + if ((err = mp_copy(precomp[nA + (nB << 2)]->z, C->z)) != CRYPT_OK) { + goto ERR_MU; + } + } else { + /* if not first, add from table */ + if ((err = ltc_mp.ecc_ptadd(C, precomp[nA + (nB << 2)], C, modulus, mp)) != CRYPT_OK) { + goto ERR_MU; + } + } + } + } + + /* reduce to affine */ + err = ltc_ecc_map(C, modulus, mp); + + /* clean up */ +ERR_MU: + mp_clear(mu); +ERR_MP: + mp_montgomery_free(mp); +ERR_P: + for (x = 0; x < 16; x++) { + ltc_ecc_del_point(precomp[x]); + } +ERR_T: + #ifdef LTC_CLEAN_STACK + zeromem(tA, ECC_BUF_SIZE); + zeromem(tB, ECC_BUF_SIZE); + #endif + XFREE(tA); + XFREE(tB); + + return err; +} + #endif +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_mul2add.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ltc_ecc_mulmod.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + #ifndef LTC_ECC_TIMING_RESISTANT + +/* size of sliding window, don't change this! */ + #define WINSIZE 4 + +/** + Perform a point multiplication + @param k The scalar to multiply by + @param G The base point + @param R [out] Destination for kG + @param modulus The modulus of the field the ECC curve is in + @param map Boolean whether to map back to affine or not (1==map, 0 == leave in projective) + @return CRYPT_OK on success + */ +int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map) { + ecc_point *tG, *M[8]; + int i, j, err; + void *mu, *mp; + unsigned long buf; + int first, bitbuf, bitcpy, bitcnt, mode, digidx; + + LTC_ARGCHK(k != NULL); + LTC_ARGCHK(G != NULL); + LTC_ARGCHK(R != NULL); + LTC_ARGCHK(modulus != NULL); + + /* init montgomery reduction */ + if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { + return err; + } + if ((err = mp_init(&mu)) != CRYPT_OK) { + mp_montgomery_free(mp); + return err; + } + if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) { + mp_montgomery_free(mp); + mp_clear(mu); + return err; + } + + /* alloc ram for window temps */ + for (i = 0; i < 8; i++) { + M[i] = ltc_ecc_new_point(); + if (M[i] == NULL) { + for (j = 0; j < i; j++) { + ltc_ecc_del_point(M[j]); + } + mp_montgomery_free(mp); + mp_clear(mu); + return CRYPT_MEM; + } + } + + /* make a copy of G incase R==G */ + tG = ltc_ecc_new_point(); + if (tG == NULL) { + err = CRYPT_MEM; + goto done; + } + + /* tG = G and convert to montgomery */ + if (mp_cmp_d(mu, 1) == LTC_MP_EQ) { + if ((err = mp_copy(G->x, tG->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(G->y, tG->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(G->z, tG->z)) != CRYPT_OK) { + goto done; + } + } else { + if ((err = mp_mulmod(G->x, mu, modulus, tG->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_mulmod(G->y, mu, modulus, tG->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_mulmod(G->z, mu, modulus, tG->z)) != CRYPT_OK) { + goto done; + } + } + mp_clear(mu); + mu = NULL; + + /* calc the M tab, which holds kG for k==8..15 */ + /* M[0] == 8G */ + if ((err = ltc_mp.ecc_ptdbl(tG, M[0], modulus, mp)) != CRYPT_OK) { + goto done; + } + if ((err = ltc_mp.ecc_ptdbl(M[0], M[0], modulus, mp)) != CRYPT_OK) { + goto done; + } + if ((err = ltc_mp.ecc_ptdbl(M[0], M[0], modulus, mp)) != CRYPT_OK) { + goto done; + } + + /* now find (8+k)G for k=1..7 */ + for (j = 9; j < 16; j++) { + if ((err = ltc_mp.ecc_ptadd(M[j - 9], tG, M[j - 8], modulus, mp)) != CRYPT_OK) { + goto done; + } + } + + /* setup sliding window */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = mp_get_digit_count(k) - 1; + bitcpy = bitbuf = 0; + first = 1; + + /* perform ops */ + for ( ; ; ) { + /* grab next digit as required */ + if (--bitcnt == 0) { + if (digidx == -1) { + break; + } + buf = mp_get_digit(k, digidx); + bitcnt = (int)ltc_mp.bits_per_digit; + --digidx; + } + + /* grab the next msb from the ltiplicand */ + i = (buf >> (ltc_mp.bits_per_digit - 1)) & 1; + buf <<= 1; + + /* skip leading zero bits */ + if ((mode == 0) && (i == 0)) { + continue; + } + + /* if the bit is zero and mode == 1 then we double */ + if ((mode == 1) && (i == 0)) { + if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK) { + goto done; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (i << (WINSIZE - ++bitcpy)); + mode = 2; + + if (bitcpy == WINSIZE) { + /* if this is the first window we do a simple copy */ + if (first == 1) { + /* R = kG [k = first window] */ + if ((err = mp_copy(M[bitbuf - 8]->x, R->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(M[bitbuf - 8]->y, R->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(M[bitbuf - 8]->z, R->z)) != CRYPT_OK) { + goto done; + } + first = 0; + } else { + /* normal window */ + /* ok window is filled so double as required and add */ + /* double first */ + for (j = 0; j < WINSIZE; j++) { + if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK) { + goto done; + } + } + + /* then add, bitbuf will be 8..15 [8..2^WINSIZE] guaranteed */ + if ((err = ltc_mp.ecc_ptadd(R, M[bitbuf - 8], R, modulus, mp)) != CRYPT_OK) { + goto done; + } + } + /* empty window and reset */ + bitcpy = bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then double/add */ + if ((mode == 2) && (bitcpy > 0)) { + /* double then add */ + for (j = 0; j < bitcpy; j++) { + /* only double if we have had at least one add first */ + if (first == 0) { + if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK) { + goto done; + } + } + + bitbuf <<= 1; + if ((bitbuf & (1 << WINSIZE)) != 0) { + if (first == 1) { + /* first add, so copy */ + if ((err = mp_copy(tG->x, R->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(tG->y, R->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(tG->z, R->z)) != CRYPT_OK) { + goto done; + } + first = 0; + } else { + /* then add */ + if ((err = ltc_mp.ecc_ptadd(R, tG, R, modulus, mp)) != CRYPT_OK) { + goto done; + } + } + } + } + } + + /* map R back from projective space */ + if (map) { + err = ltc_ecc_map(R, modulus, mp); + } else { + err = CRYPT_OK; + } +done: + if (mu != NULL) { + mp_clear(mu); + } + mp_montgomery_free(mp); + ltc_ecc_del_point(tG); + for (i = 0; i < 8; i++) { + ltc_ecc_del_point(M[i]); + } + return err; +} + #endif + + #undef WINSIZE +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_mulmod.c,v $ */ +/* $Revision: 1.26 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ltc_ecc_mulmod_timing.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + + #ifdef LTC_ECC_TIMING_RESISTANT + +/** + Perform a point multiplication (timing resistant) + @param k The scalar to multiply by + @param G The base point + @param R [out] Destination for kG + @param modulus The modulus of the field the ECC curve is in + @param map Boolean whether to map back to affine or not (1==map, 0 == leave in projective) + @return CRYPT_OK on success + */ +int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map) { + ecc_point *tG, *M[3]; + int i, j, err; + void *mu, *mp; + unsigned long buf; + int first, bitbuf, bitcpy, bitcnt, mode, digidx; + + LTC_ARGCHK(k != NULL); + LTC_ARGCHK(G != NULL); + LTC_ARGCHK(R != NULL); + LTC_ARGCHK(modulus != NULL); + + /* init montgomery reduction */ + if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { + return err; + } + if ((err = mp_init(&mu)) != CRYPT_OK) { + mp_montgomery_free(mp); + return err; + } + if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) { + mp_clear(mu); + mp_montgomery_free(mp); + return err; + } + + /* alloc ram for window temps */ + for (i = 0; i < 3; i++) { + M[i] = ltc_ecc_new_point(); + if (M[i] == NULL) { + for (j = 0; j < i; j++) { + ltc_ecc_del_point(M[j]); + } + mp_clear(mu); + mp_montgomery_free(mp); + return CRYPT_MEM; + } + } + + /* make a copy of G incase R==G */ + tG = ltc_ecc_new_point(); + if (tG == NULL) { + err = CRYPT_MEM; + goto done; + } + + /* tG = G and convert to montgomery */ + if ((err = mp_mulmod(G->x, mu, modulus, tG->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_mulmod(G->y, mu, modulus, tG->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_mulmod(G->z, mu, modulus, tG->z)) != CRYPT_OK) { + goto done; + } + mp_clear(mu); + mu = NULL; + + /* calc the M tab */ + /* M[0] == G */ + if ((err = mp_copy(tG->x, M[0]->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(tG->y, M[0]->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(tG->z, M[0]->z)) != CRYPT_OK) { + goto done; + } + /* M[1] == 2G */ + if ((err = ltc_mp.ecc_ptdbl(tG, M[1], modulus, mp)) != CRYPT_OK) { + goto done; + } + + /* setup sliding window */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = mp_get_digit_count(k) - 1; + bitcpy = bitbuf = 0; + first = 1; + + /* perform ops */ + for ( ; ; ) { + /* grab next digit as required */ + if (--bitcnt == 0) { + if (digidx == -1) { + break; + } + buf = mp_get_digit(k, digidx); + bitcnt = (int)MP_DIGIT_BIT; + --digidx; + } + + /* grab the next msb from the ltiplicand */ + i = (buf >> (MP_DIGIT_BIT - 1)) & 1; + buf <<= 1; + + if ((mode == 0) && (i == 0)) { + /* dummy operations */ + if ((err = ltc_mp.ecc_ptadd(M[0], M[1], M[2], modulus, mp)) != CRYPT_OK) { + goto done; + } + if ((err = ltc_mp.ecc_ptdbl(M[1], M[2], modulus, mp)) != CRYPT_OK) { + goto done; + } + continue; + } + + if ((mode == 0) && (i == 1)) { + mode = 1; + /* dummy operations */ + if ((err = ltc_mp.ecc_ptadd(M[0], M[1], M[2], modulus, mp)) != CRYPT_OK) { + goto done; + } + if ((err = ltc_mp.ecc_ptdbl(M[1], M[2], modulus, mp)) != CRYPT_OK) { + goto done; + } + continue; + } + + if ((err = ltc_mp.ecc_ptadd(M[0], M[1], M[i ^ 1], modulus, mp)) != CRYPT_OK) { + goto done; + } + if ((err = ltc_mp.ecc_ptdbl(M[i], M[i], modulus, mp)) != CRYPT_OK) { + goto done; + } + } + + /* copy result out */ + if ((err = mp_copy(M[0]->x, R->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(M[0]->y, R->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(M[0]->z, R->z)) != CRYPT_OK) { + goto done; + } + + /* map R back from projective space */ + if (map) { + err = ltc_ecc_map(R, modulus, mp); + } else { + err = CRYPT_OK; + } +done: + if (mu != NULL) { + mp_clear(mu); + } + mp_montgomery_free(mp); + ltc_ecc_del_point(tG); + for (i = 0; i < 3; i++) { + ltc_ecc_del_point(M[i]); + } + return err; +} + #endif +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_mulmod_timing.c,v $ */ +/* $Revision: 1.13 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ltc_ecc_points.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Allocate a new ECC point + @return A newly allocated point or NULL on error + */ +ecc_point *ltc_ecc_new_point(void) { + ecc_point *p; + + p = XCALLOC(1, sizeof(*p)); + if (p == NULL) { + return NULL; + } + if (mp_init_multi(&p->x, &p->y, &p->z, NULL) != CRYPT_OK) { + XFREE(p); + return NULL; + } + return p; +} + +/** Free an ECC point from memory + @param p The point to free + */ +void ltc_ecc_del_point(ecc_point *p) { + /* prevents free'ing null arguments */ + if (p != NULL) { + mp_clear_multi(p->x, p->y, p->z, NULL); /* note: p->z may be NULL but that's ok with this function anyways */ + XFREE(p); + } +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_points.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ltc_ecc_projective_add_point.c + ECC Crypto, Tom St Denis + */ + +#if defined(LTC_MECC) && (!defined(LTC_MECC_ACCEL) || defined(LTM_LTC_DESC)) + +/** + Add two ECC points + @param P The point to add + @param Q The point to add + @param R [out] The destination of the double + @param modulus The modulus of the field the ECC curve is in + @param mp The "b" value from montgomery_setup() + @return CRYPT_OK on success + */ +int ltc_ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp) { + void *t1, *t2, *x, *y, *z; + int err; + + LTC_ARGCHK(P != NULL); + LTC_ARGCHK(Q != NULL); + LTC_ARGCHK(R != NULL); + LTC_ARGCHK(modulus != NULL); + LTC_ARGCHK(mp != NULL); + + if ((err = mp_init_multi(&t1, &t2, &x, &y, &z, NULL)) != CRYPT_OK) { + return err; + } + + /* should we dbl instead? */ + if ((err = mp_sub(modulus, Q->y, t1)) != CRYPT_OK) { + goto done; + } + + if ((mp_cmp(P->x, Q->x) == LTC_MP_EQ) && + ((Q->z != NULL) && (mp_cmp(P->z, Q->z) == LTC_MP_EQ)) && + ((mp_cmp(P->y, Q->y) == LTC_MP_EQ) || (mp_cmp(P->y, t1) == LTC_MP_EQ))) { + mp_clear_multi(t1, t2, x, y, z, NULL); + return ltc_ecc_projective_dbl_point(P, R, modulus, mp); + } + + if ((err = mp_copy(P->x, x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(P->y, y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(P->z, z)) != CRYPT_OK) { + goto done; + } + + /* if Z is one then these are no-operations */ + if (Q->z != NULL) { + /* T1 = Z' * Z' */ + if ((err = mp_sqr(Q->z, t1)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* X = X * T1 */ + if ((err = mp_mul(t1, x, x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(x, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* T1 = Z' * T1 */ + if ((err = mp_mul(Q->z, t1, t1)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* Y = Y * T1 */ + if ((err = mp_mul(t1, y, y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(y, modulus, mp)) != CRYPT_OK) { + goto done; + } + } + + /* T1 = Z*Z */ + if ((err = mp_sqr(z, t1)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* T2 = X' * T1 */ + if ((err = mp_mul(Q->x, t1, t2)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* T1 = Z * T1 */ + if ((err = mp_mul(z, t1, t1)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* T1 = Y' * T1 */ + if ((err = mp_mul(Q->y, t1, t1)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { + goto done; + } + + /* Y = Y - T1 */ + if ((err = mp_sub(y, t1, y)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(y, 0) == LTC_MP_LT) { + if ((err = mp_add(y, modulus, y)) != CRYPT_OK) { + goto done; + } + } + /* T1 = 2T1 */ + if ((err = mp_add(t1, t1, t1)) != CRYPT_OK) { + goto done; + } + if (mp_cmp(t1, modulus) != LTC_MP_LT) { + if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) { + goto done; + } + } + /* T1 = Y + T1 */ + if ((err = mp_add(t1, y, t1)) != CRYPT_OK) { + goto done; + } + if (mp_cmp(t1, modulus) != LTC_MP_LT) { + if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) { + goto done; + } + } + /* X = X - T2 */ + if ((err = mp_sub(x, t2, x)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(x, 0) == LTC_MP_LT) { + if ((err = mp_add(x, modulus, x)) != CRYPT_OK) { + goto done; + } + } + /* T2 = 2T2 */ + if ((err = mp_add(t2, t2, t2)) != CRYPT_OK) { + goto done; + } + if (mp_cmp(t2, modulus) != LTC_MP_LT) { + if ((err = mp_sub(t2, modulus, t2)) != CRYPT_OK) { + goto done; + } + } + /* T2 = X + T2 */ + if ((err = mp_add(t2, x, t2)) != CRYPT_OK) { + goto done; + } + if (mp_cmp(t2, modulus) != LTC_MP_LT) { + if ((err = mp_sub(t2, modulus, t2)) != CRYPT_OK) { + goto done; + } + } + + /* if Z' != 1 */ + if (Q->z != NULL) { + /* Z = Z * Z' */ + if ((err = mp_mul(z, Q->z, z)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(z, modulus, mp)) != CRYPT_OK) { + goto done; + } + } + + /* Z = Z * X */ + if ((err = mp_mul(z, x, z)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(z, modulus, mp)) != CRYPT_OK) { + goto done; + } + + /* T1 = T1 * X */ + if ((err = mp_mul(t1, x, t1)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* X = X * X */ + if ((err = mp_sqr(x, x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(x, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* T2 = T2 * x */ + if ((err = mp_mul(t2, x, t2)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* T1 = T1 * X */ + if ((err = mp_mul(t1, x, t1)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { + goto done; + } + + /* X = Y*Y */ + if ((err = mp_sqr(y, x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(x, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* X = X - T2 */ + if ((err = mp_sub(x, t2, x)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(x, 0) == LTC_MP_LT) { + if ((err = mp_add(x, modulus, x)) != CRYPT_OK) { + goto done; + } + } + + /* T2 = T2 - X */ + if ((err = mp_sub(t2, x, t2)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(t2, 0) == LTC_MP_LT) { + if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK) { + goto done; + } + } + /* T2 = T2 - X */ + if ((err = mp_sub(t2, x, t2)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(t2, 0) == LTC_MP_LT) { + if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK) { + goto done; + } + } + /* T2 = T2 * Y */ + if ((err = mp_mul(t2, y, t2)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* Y = T2 - T1 */ + if ((err = mp_sub(t2, t1, y)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(y, 0) == LTC_MP_LT) { + if ((err = mp_add(y, modulus, y)) != CRYPT_OK) { + goto done; + } + } + /* Y = Y/2 */ + if (mp_isodd(y)) { + if ((err = mp_add(y, modulus, y)) != CRYPT_OK) { + goto done; + } + } + if ((err = mp_div_2(y, y)) != CRYPT_OK) { + goto done; + } + + if ((err = mp_copy(x, R->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(y, R->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(z, R->z)) != CRYPT_OK) { + goto done; + } + + err = CRYPT_OK; +done: + mp_clear_multi(t1, t2, x, y, z, NULL); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_projective_add_point.c,v $ */ +/* $Revision: 1.16 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ltc_ecc_projective_dbl_point.c + ECC Crypto, Tom St Denis + */ + +#if defined(LTC_MECC) && (!defined(LTC_MECC_ACCEL) || defined(LTM_LTC_DESC)) + +/** + Double an ECC point + @param P The point to double + @param R [out] The destination of the double + @param modulus The modulus of the field the ECC curve is in + @param mp The "b" value from montgomery_setup() + @return CRYPT_OK on success + */ +int ltc_ecc_projective_dbl_point(ecc_point *P, ecc_point *R, void *modulus, void *mp) { + void *t1, *t2; + int err; + + LTC_ARGCHK(P != NULL); + LTC_ARGCHK(R != NULL); + LTC_ARGCHK(modulus != NULL); + LTC_ARGCHK(mp != NULL); + + if ((err = mp_init_multi(&t1, &t2, NULL)) != CRYPT_OK) { + return err; + } + + if (P != R) { + if ((err = mp_copy(P->x, R->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(P->y, R->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(P->z, R->z)) != CRYPT_OK) { + goto done; + } + } + + /* t1 = Z * Z */ + if ((err = mp_sqr(R->z, t1)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* Z = Y * Z */ + if ((err = mp_mul(R->z, R->y, R->z)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(R->z, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* Z = 2Z */ + if ((err = mp_add(R->z, R->z, R->z)) != CRYPT_OK) { + goto done; + } + if (mp_cmp(R->z, modulus) != LTC_MP_LT) { + if ((err = mp_sub(R->z, modulus, R->z)) != CRYPT_OK) { + goto done; + } + } + + /* T2 = X - T1 */ + if ((err = mp_sub(R->x, t1, t2)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(t2, 0) == LTC_MP_LT) { + if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK) { + goto done; + } + } + /* T1 = X + T1 */ + if ((err = mp_add(t1, R->x, t1)) != CRYPT_OK) { + goto done; + } + if (mp_cmp(t1, modulus) != LTC_MP_LT) { + if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) { + goto done; + } + } + /* T2 = T1 * T2 */ + if ((err = mp_mul(t1, t2, t2)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* T1 = 2T2 */ + if ((err = mp_add(t2, t2, t1)) != CRYPT_OK) { + goto done; + } + if (mp_cmp(t1, modulus) != LTC_MP_LT) { + if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) { + goto done; + } + } + /* T1 = T1 + T2 */ + if ((err = mp_add(t1, t2, t1)) != CRYPT_OK) { + goto done; + } + if (mp_cmp(t1, modulus) != LTC_MP_LT) { + if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) { + goto done; + } + } + + /* Y = 2Y */ + if ((err = mp_add(R->y, R->y, R->y)) != CRYPT_OK) { + goto done; + } + if (mp_cmp(R->y, modulus) != LTC_MP_LT) { + if ((err = mp_sub(R->y, modulus, R->y)) != CRYPT_OK) { + goto done; + } + } + /* Y = Y * Y */ + if ((err = mp_sqr(R->y, R->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(R->y, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* T2 = Y * Y */ + if ((err = mp_sqr(R->y, t2)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* T2 = T2/2 */ + if (mp_isodd(t2)) { + if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK) { + goto done; + } + } + if ((err = mp_div_2(t2, t2)) != CRYPT_OK) { + goto done; + } + /* Y = Y * X */ + if ((err = mp_mul(R->y, R->x, R->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(R->y, modulus, mp)) != CRYPT_OK) { + goto done; + } + + /* X = T1 * T1 */ + if ((err = mp_sqr(t1, R->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(R->x, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* X = X - Y */ + if ((err = mp_sub(R->x, R->y, R->x)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(R->x, 0) == LTC_MP_LT) { + if ((err = mp_add(R->x, modulus, R->x)) != CRYPT_OK) { + goto done; + } + } + /* X = X - Y */ + if ((err = mp_sub(R->x, R->y, R->x)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(R->x, 0) == LTC_MP_LT) { + if ((err = mp_add(R->x, modulus, R->x)) != CRYPT_OK) { + goto done; + } + } + + /* Y = Y - X */ + if ((err = mp_sub(R->y, R->x, R->y)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(R->y, 0) == LTC_MP_LT) { + if ((err = mp_add(R->y, modulus, R->y)) != CRYPT_OK) { + goto done; + } + } + /* Y = Y * T1 */ + if ((err = mp_mul(R->y, t1, R->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(R->y, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* Y = Y - T2 */ + if ((err = mp_sub(R->y, t2, R->y)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(R->y, 0) == LTC_MP_LT) { + if ((err = mp_add(R->y, modulus, R->y)) != CRYPT_OK) { + goto done; + } + } + + err = CRYPT_OK; +done: + mp_clear_multi(t1, t2, NULL); + return err; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_projective_dbl_point.c,v $ */ +/* $Revision: 1.11 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#define DESC_DEF_ONLY + +#ifdef LTM_DESC + +#undef mp_init +#undef mp_init_multi +#undef mp_clear +#undef mp_clear_multi +#undef mp_init_copy +#undef mp_neg +#undef mp_copy +#undef mp_set +#undef mp_set_int +#undef mp_get_int +#undef mp_get_digit +#undef mp_get_digit_count +#undef mp_cmp +#undef mp_cmp_d +#undef mp_count_bits +#undef mp_cnt_lsb +#undef mp_2expt +#undef mp_read_radix +#undef mp_toradix +#undef mp_unsigned_bin_size +#undef mp_to_unsigned_bin +#undef mp_read_unsigned_bin +#undef mp_add +#undef mp_add_d +#undef mp_sub +#undef mp_sub_d +#undef mp_mul +#undef mp_mul_d +#undef mp_sqr +#undef mp_div +#undef mp_div_2 +#undef mp_mod +#undef mp_mod_d +#undef mp_gcd +#undef mp_lcm +#undef mp_mulmod +#undef mp_sqrmod +#undef mp_invmod +#undef mp_montgomery_setup +#undef mp_montgomery_normalization +#undef mp_montgomery_reduce +#undef mp_montgomery_free +#undef mp_exptmod +#undef mp_prime_is_prime +#undef mp_iszero +#undef mp_isodd +#undef mp_exch +#undef mp_tohex + +static const struct { + int mpi_code, ltc_code; +} mpi_to_ltc_codes[] = { + { MP_OKAY, CRYPT_OK }, + { MP_MEM, CRYPT_MEM }, + { MP_VAL, CRYPT_INVALID_ARG }, +}; + +/** + Convert a MPI error to a LTC error (Possibly the most powerful function ever! Oh wait... no) + @param err The error to convert + @return The equivalent LTC error code or CRYPT_ERROR if none found + */ +static int mpi_to_ltc_error(int err) { + int x; + + for (x = 0; x < (int)(sizeof(mpi_to_ltc_codes) / sizeof(mpi_to_ltc_codes[0])); x++) { + if (err == mpi_to_ltc_codes[x].mpi_code) { + return mpi_to_ltc_codes[x].ltc_code; + } + } + return CRYPT_ERROR; +} + +static int init(void **a) { + int err; + + LTC_ARGCHK(a != NULL); + + *a = XCALLOC(1, sizeof(mp_int)); + if (*a == NULL) { + return CRYPT_MEM; + } + if ((err = mpi_to_ltc_error(mp_init(*a))) != CRYPT_OK) { + XFREE(*a); + } + return err; +} + +static void deinit(void *a) { + LTC_ARGCHKVD(a != NULL); + mp_clear(a); + XFREE(a); +} + +static int neg(void *a, void *b) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_neg(a, b)); +} + +static int _copy(void *a, void *b) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_copy(a, b)); +} + +static int init_copy(void **a, void *b) { + if (init(a) != CRYPT_OK) { + return CRYPT_MEM; + } + return _copy(b, *a); +} + +/* ---- trivial ---- */ +static int set_int(void *a, unsigned long b) { + LTC_ARGCHK(a != NULL); + return mpi_to_ltc_error(mp_set_int(a, b)); +} + +static unsigned long get_int(void *a) { + LTC_ARGCHK(a != NULL); + return mp_get_int(a); +} + +static unsigned long get_digit(void *a, int n) { + mp_int *A; + + LTC_ARGCHK(a != NULL); + A = a; + return (n >= A->used || n < 0) ? 0 : A->dp[n]; +} + +static int get_digit_count(void *a) { + mp_int *A; + + LTC_ARGCHK(a != NULL); + A = a; + return A->used; +} + +static int compare(void *a, void *b) { + int ret; + + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + ret = mp_cmp(a, b); + switch (ret) { + case MP_LT: + return LTC_MP_LT; + + case MP_EQ: + return LTC_MP_EQ; + + case MP_GT: + return LTC_MP_GT; + } + return 0; +} + +static int compare_d(void *a, unsigned long b) { + int ret; + + LTC_ARGCHK(a != NULL); + ret = mp_cmp_d(a, b); + switch (ret) { + case MP_LT: + return LTC_MP_LT; + + case MP_EQ: + return LTC_MP_EQ; + + case MP_GT: + return LTC_MP_GT; + } + return 0; +} + +static int count_bits(void *a) { + LTC_ARGCHK(a != NULL); + return mp_count_bits(a); +} + +static int count_lsb_bits(void *a) { + LTC_ARGCHK(a != NULL); + return mp_cnt_lsb(a); +} + +static int twoexpt(void *a, int n) { + LTC_ARGCHK(a != NULL); + return mpi_to_ltc_error(mp_2expt(a, n)); +} + +/* ---- conversions ---- */ + +/* read ascii string */ +static int read_radix(void *a, const char *b, int radix) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_read_radix(a, b, radix)); +} + +/* write one */ +static int write_radix(void *a, char *b, int radix) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_toradix(a, b, radix)); +} + +/* get size as unsigned char string */ +static unsigned long unsigned_size(void *a) { + LTC_ARGCHK(a != NULL); + return mp_unsigned_bin_size(a); +} + +/* store */ +static int unsigned_write(void *a, unsigned char *b) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_to_unsigned_bin(a, b)); +} + +/* read */ +static int unsigned_read(void *a, unsigned char *b, unsigned long len) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_read_unsigned_bin(a, b, len)); +} + +/* add */ +static int add(void *a, void *b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_add(a, b, c)); +} + +static int addi(void *a, unsigned long b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_add_d(a, b, c)); +} + +/* sub */ +static int sub(void *a, void *b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_sub(a, b, c)); +} + +static int subi(void *a, unsigned long b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_sub_d(a, b, c)); +} + +/* mul */ +static int mul(void *a, void *b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_mul(a, b, c)); +} + +static int muli(void *a, unsigned long b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_mul_d(a, b, c)); +} + +/* sqr */ +static int sqr(void *a, void *b) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_sqr(a, b)); +} + +/* div */ +static int divide(void *a, void *b, void *c, void *d) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_div(a, b, c, d)); +} + +static int div_2(void *a, void *b) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_div_2(a, b)); +} + +/* modi */ +static int modi(void *a, unsigned long b, unsigned long *c) { + mp_digit tmp; + int err; + + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(c != NULL); + + if ((err = mpi_to_ltc_error(mp_mod_d(a, b, &tmp))) != CRYPT_OK) { + return err; + } + *c = tmp; + return CRYPT_OK; +} + +/* gcd */ +static int gcd(void *a, void *b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_gcd(a, b, c)); +} + +/* lcm */ +static int lcm(void *a, void *b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_lcm(a, b, c)); +} + +static int mulmod(void *a, void *b, void *c, void *d) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + LTC_ARGCHK(d != NULL); + return mpi_to_ltc_error(mp_mulmod(a, b, c, d)); +} + +static int sqrmod(void *a, void *b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_sqrmod(a, b, c)); +} + +/* invmod */ +static int invmod(void *a, void *b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_invmod(a, b, c)); +} + +/* setup */ +static int montgomery_setup(void *a, void **b) { + int err; + + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + *b = XCALLOC(1, sizeof(mp_digit)); + if (*b == NULL) { + return CRYPT_MEM; + } + if ((err = mpi_to_ltc_error(mp_montgomery_setup(a, (mp_digit *)*b))) != CRYPT_OK) { + XFREE(*b); + } + return err; +} + +/* get normalization value */ +static int montgomery_normalization(void *a, void *b) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_montgomery_calc_normalization(a, b)); +} + +/* reduce */ +static int montgomery_reduce(void *a, void *b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_montgomery_reduce(a, b, *((mp_digit *)c))); +} + +/* clean up */ +static void montgomery_deinit(void *a) { + XFREE(a); +} + +static int exptmod(void *a, void *b, void *c, void *d) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + LTC_ARGCHK(d != NULL); + return mpi_to_ltc_error(mp_exptmod(a, b, c, d)); +} + +static int isprime(void *a, int *b) { + int err; + + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + err = mpi_to_ltc_error(mp_prime_is_prime(a, 8, b)); + *b = (*b == MP_YES) ? LTC_MP_YES : LTC_MP_NO; + return err; +} + +const ltc_math_descriptor ltm_desc = { + "LibTomMath", + (int)DIGIT_BIT, + + &init, + &init_copy, + &deinit, + + &neg, + &_copy, + + &set_int, + &get_int, + &get_digit, + &get_digit_count, + &compare, + &compare_d, + &count_bits, + &count_lsb_bits, + &twoexpt, + + &read_radix, + &write_radix, + &unsigned_size, + &unsigned_write, + &unsigned_read, + + &add, + &addi, + &sub, + &subi, + &mul, + &muli, + &sqr, + ÷, + &div_2, + &modi, + &gcd, + &lcm, + + &mulmod, + &sqrmod, + &invmod, + + &montgomery_setup, + &montgomery_normalization, + &montgomery_reduce, + &montgomery_deinit, + + &exptmod, + &isprime, + + #ifdef LTC_MECC + #ifdef LTC_MECC_FP + <c_ecc_fp_mulmod, + #else + <c_ecc_mulmod, + #endif + <c_ecc_projective_add_point, + <c_ecc_projective_dbl_point, + <c_ecc_map, + #ifdef LTC_ECC_SHAMIR + #ifdef LTC_MECC_FP + <c_ecc_fp_mul2add, + #else + <c_ecc_mul2add, + #endif /* LTC_MECC_FP */ + #else + NULL, + #endif /* LTC_ECC_SHAMIR */ + #else + NULL, NULL,NULL, NULL, NULL, + #endif /* LTC_MECC */ + + #ifdef LTC_MRSA + &rsa_make_key, + &rsa_exptmod, + #else + NULL, NULL + #endif +}; + + #define mp_init(a) ltc_mp.init(a) + #define mp_init_multi ltc_init_multi + #define mp_clear(a) ltc_mp.deinit(a) + #define mp_clear_multi ltc_deinit_multi + #define mp_init_copy(a, b) ltc_mp.init_copy(a, b) + + #define mp_neg(a, b) ltc_mp.neg(a, b) + #define mp_copy(a, b) ltc_mp.copy(a, b) + + #define mp_set(a, b) ltc_mp.set_int(a, b) + #define mp_set_int(a, b) ltc_mp.set_int(a, b) + #define mp_get_int(a) ltc_mp.get_int(a) + #define mp_get_digit(a, n) ltc_mp.get_digit(a, n) + #define mp_get_digit_count(a) ltc_mp.get_digit_count(a) + #define mp_cmp(a, b) ltc_mp.compare(a, b) + #define mp_cmp_d(a, b) ltc_mp.compare_d(a, b) + #define mp_count_bits(a) ltc_mp.count_bits(a) + #define mp_cnt_lsb(a) ltc_mp.count_lsb_bits(a) + #define mp_2expt(a, b) ltc_mp.twoexpt(a, b) + + #define mp_read_radix(a, b, c) ltc_mp.read_radix(a, b, c) + #define mp_toradix(a, b, c) ltc_mp.write_radix(a, b, c) + #define mp_unsigned_bin_size(a) ltc_mp.unsigned_size(a) + #define mp_to_unsigned_bin(a, b) ltc_mp.unsigned_write(a, b) + #define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c) + + #define mp_add(a, b, c) ltc_mp.add(a, b, c) + #define mp_add_d(a, b, c) ltc_mp.addi(a, b, c) + #define mp_sub(a, b, c) ltc_mp.sub(a, b, c) + #define mp_sub_d(a, b, c) ltc_mp.subi(a, b, c) + #define mp_mul(a, b, c) ltc_mp.mul(a, b, c) + #define mp_mul_d(a, b, c) ltc_mp.muli(a, b, c) + #define mp_sqr(a, b) ltc_mp.sqr(a, b) + #define mp_div(a, b, c, d) ltc_mp.mpdiv(a, b, c, d) + #define mp_div_2(a, b) ltc_mp.div_2(a, b) + #define mp_mod(a, b, c) ltc_mp.mpdiv(a, b, NULL, c) + #define mp_mod_d(a, b, c) ltc_mp.modi(a, b, c) + #define mp_gcd(a, b, c) ltc_mp.gcd(a, b, c) + #define mp_lcm(a, b, c) ltc_mp.lcm(a, b, c) + + #define mp_mulmod(a, b, c, d) ltc_mp.mulmod(a, b, c, d) + #define mp_sqrmod(a, b, c) ltc_mp.sqrmod(a, b, c) + #define mp_invmod(a, b, c) ltc_mp.invmod(a, b, c) + + #define mp_montgomery_setup(a, b) ltc_mp.montgomery_setup(a, b) + #define mp_montgomery_normalization(a, b) ltc_mp.montgomery_normalization(a, b) + #define mp_montgomery_reduce(a, b, c) ltc_mp.montgomery_reduce(a, b, c) + #define mp_montgomery_free(a) ltc_mp.montgomery_deinit(a) + + #define mp_exptmod(a, b, c, d) ltc_mp.exptmod(a, b, c, d) + #define mp_prime_is_prime(a, b, c) ltc_mp.isprime(a, c) + + #define mp_iszero(a) (mp_cmp_d(a, 0) == LTC_MP_EQ ? LTC_MP_YES : LTC_MP_NO) + #define mp_isodd(a) (mp_get_digit_count(a) > 0 ? (mp_get_digit(a, 0) & 1 ? LTC_MP_YES : LTC_MP_NO) : LTC_MP_NO) + #define mp_exch(a, b) do { void *ABC__tmp = a; a = b; b = ABC__tmp; } while (0); + + #define mp_tohex(a, b) mp_toradix(a, b, 16) + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/math/ltm_desc.c,v $ */ +/* $Revision: 1.31 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +#ifdef MPI +#include + +int ltc_init_multi(void **a, ...) { + void **cur = a; + int np = 0; + va_list args; + + va_start(args, a); + while (cur != NULL) { + if (mp_init(cur) != CRYPT_OK) { + /* failed */ + va_list clean_list; + + va_start(clean_list, a); + cur = a; + while (np--) { + mp_clear(*cur); + cur = va_arg(clean_list, void **); + } + va_end(clean_list); + return CRYPT_MEM; + } + ++np; + cur = va_arg(args, void **); + } + va_end(args); + return CRYPT_OK; +} + +void ltc_deinit_multi(void *a, ...) { + void *cur = a; + va_list args; + + va_start(args, a); + while (cur != NULL) { + mp_clear(cur); + cur = va_arg(args, void *); + } + va_end(args); +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/math/multi.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:23 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file pkcs_1_i2osp.c + Integer to Octet I2OSP, Tom St Denis + */ + +#ifdef LTC_PKCS_1 + +/* always stores the same # of bytes, pads with leading zero bytes + as required + */ + +/** + LTC_PKCS #1 Integer to binary + @param n The integer to store + @param modulus_len The length of the RSA modulus + @param out [out] The destination for the integer + @return CRYPT_OK if successful + */ +int pkcs_1_i2osp(void *n, unsigned long modulus_len, unsigned char *out) { + unsigned long size; + + size = mp_unsigned_bin_size(n); + + if (size > modulus_len) { + return CRYPT_BUFFER_OVERFLOW; + } + + /* store it */ + zeromem(out, modulus_len); + return mp_to_unsigned_bin(n, out + (modulus_len - size)); +} +#endif /* LTC_PKCS_1 */ + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_i2osp.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file pkcs_1_mgf1.c + The Mask Generation Function (MGF1) for LTC_PKCS #1, Tom St Denis + */ + +#ifdef LTC_PKCS_1 + +/** + Perform LTC_PKCS #1 MGF1 (internal) + @param seed The seed for MGF1 + @param seedlen The length of the seed + @param hash_idx The index of the hash desired + @param mask [out] The destination + @param masklen The length of the mask desired + @return CRYPT_OK if successful + */ +int pkcs_1_mgf1(int hash_idx, + const unsigned char *seed, unsigned long seedlen, + unsigned char *mask, unsigned long masklen) { + unsigned long hLen, x; + ulong32 counter; + int err; + hash_state *md; + unsigned char *buf; + + LTC_ARGCHK(seed != NULL); + LTC_ARGCHK(mask != NULL); + + /* ensure valid hash */ + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + + /* get hash output size */ + hLen = hash_descriptor[hash_idx].hashsize; + + /* allocate memory */ + md = XMALLOC(sizeof(hash_state)); + buf = XMALLOC(hLen); + if ((md == NULL) || (buf == NULL)) { + if (md != NULL) { + XFREE(md); + } + if (buf != NULL) { + XFREE(buf); + } + return CRYPT_MEM; + } + + /* start counter */ + counter = 0; + + while (masklen > 0) { + /* handle counter */ + STORE32H(counter, buf); + ++counter; + + /* get hash of seed || counter */ + if ((err = hash_descriptor[hash_idx].init(md)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].process(md, seed, seedlen)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].process(md, buf, 4)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].done(md, buf)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* store it */ + for (x = 0; x < hLen && masklen > 0; x++, masklen--) { + *mask++ = buf[x]; + } + } + + err = CRYPT_OK; +LBL_ERR: + #ifdef LTC_CLEAN_STACK + zeromem(buf, hLen); + zeromem(md, sizeof(hash_state)); + #endif + + XFREE(buf); + XFREE(md); + + return err; +} +#endif /* LTC_PKCS_1 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_mgf1.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file pkcs_1_oaep_decode.c + OAEP Padding for LTC_PKCS #1, Tom St Denis + */ + +#ifdef LTC_PKCS_1 + +/** + LTC_PKCS #1 v2.00 OAEP decode + @param msg The encoded data to decode + @param msglen The length of the encoded data (octets) + @param lparam The session or system data (can be NULL) + @param lparamlen The length of the lparam + @param modulus_bitlen The bit length of the RSA modulus + @param hash_idx The index of the hash desired + @param out [out] Destination of decoding + @param outlen [in/out] The max size and resulting size of the decoding + @param res [out] Result of decoding, 1==valid, 0==invalid + @return CRYPT_OK if successful (even if invalid) + */ +int pkcs_1_oaep_decode(const unsigned char *msg, unsigned long msglen, + const unsigned char *lparam, unsigned long lparamlen, + unsigned long modulus_bitlen, int hash_idx, + unsigned char *out, unsigned long *outlen, + int *res) { + unsigned char *DB, *seed, *mask; + unsigned long hLen, x, y, modulus_len; + int err; + + LTC_ARGCHK(msg != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(res != NULL); + + /* default to invalid packet */ + *res = 0; + + /* test valid hash */ + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + hLen = hash_descriptor[hash_idx].hashsize; + modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); + + /* test hash/message size */ + if ((2 * hLen >= (modulus_len - 2)) || (msglen != modulus_len)) { + return CRYPT_PK_INVALID_SIZE; + } + + /* allocate ram for DB/mask/salt of size modulus_len */ + DB = XMALLOC(modulus_len); + mask = XMALLOC(modulus_len); + seed = XMALLOC(hLen); + if ((DB == NULL) || (mask == NULL) || (seed == NULL)) { + if (DB != NULL) { + XFREE(DB); + } + if (mask != NULL) { + XFREE(mask); + } + if (seed != NULL) { + XFREE(seed); + } + return CRYPT_MEM; + } + + /* ok so it's now in the form + + 0x00 || maskedseed || maskedDB + + 1 || hLen || modulus_len - hLen - 1 + + */ + + /* must have leading 0x00 byte */ + if (msg[0] != 0x00) { + err = CRYPT_OK; + goto LBL_ERR; + } + + /* now read the masked seed */ + x = 1; + XMEMCPY(seed, msg + x, hLen); + x += hLen; + + /* now read the masked DB */ + XMEMCPY(DB, msg + x, modulus_len - hLen - 1); + x += modulus_len - hLen - 1; + + /* compute MGF1 of maskedDB (hLen) */ + if ((err = pkcs_1_mgf1(hash_idx, DB, modulus_len - hLen - 1, mask, hLen)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* XOR against seed */ + for (y = 0; y < hLen; y++) { + seed[y] ^= mask[y]; + } + + /* compute MGF1 of seed (k - hlen - 1) */ + if ((err = pkcs_1_mgf1(hash_idx, seed, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* xor against DB */ + for (y = 0; y < (modulus_len - hLen - 1); y++) { + DB[y] ^= mask[y]; + } + + /* now DB == lhash || PS || 0x01 || M, PS == k - mlen - 2hlen - 2 zeroes */ + + /* compute lhash and store it in seed [reuse temps!] */ + x = modulus_len; + if (lparam != NULL) { + if ((err = hash_memory(hash_idx, lparam, lparamlen, seed, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + } else { + /* can't pass hash_memory a NULL so use DB with zero length */ + if ((err = hash_memory(hash_idx, DB, 0, seed, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + } + + /* compare the lhash'es */ + if (XMEMCMP(seed, DB, hLen) != 0) { + err = CRYPT_OK; + goto LBL_ERR; + } + + /* now zeroes before a 0x01 */ + for (x = hLen; x < (modulus_len - hLen - 1) && DB[x] == 0x00; x++) { + /* step... */ + } + + /* error out if wasn't 0x01 */ + if ((x == (modulus_len - hLen - 1)) || (DB[x] != 0x01)) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } + + /* rest is the message (and skip 0x01) */ + if ((modulus_len - hLen - 1 - ++x) > *outlen) { + *outlen = modulus_len - hLen - 1 - x; + err = CRYPT_BUFFER_OVERFLOW; + goto LBL_ERR; + } + + /* copy message */ + *outlen = modulus_len - hLen - 1 - x; + XMEMCPY(out, DB + x, modulus_len - hLen - 1 - x); + x += modulus_len - hLen - 1; + + /* valid packet */ + *res = 1; + + err = CRYPT_OK; +LBL_ERR: + #ifdef LTC_CLEAN_STACK + zeromem(DB, modulus_len); + zeromem(seed, hLen); + zeromem(mask, modulus_len); + #endif + + XFREE(seed); + XFREE(mask); + XFREE(DB); + + return err; +} +#endif /* LTC_PKCS_1 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_oaep_decode.c,v $ */ +/* $Revision: 1.13 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file pkcs_1_oaep_encode.c + OAEP Padding for LTC_PKCS #1, Tom St Denis + */ + +#ifdef LTC_PKCS_1 + +/** + LTC_PKCS #1 v2.00 OAEP encode + @param msg The data to encode + @param msglen The length of the data to encode (octets) + @param lparam A session or system parameter (can be NULL) + @param lparamlen The length of the lparam data + @param modulus_bitlen The bit length of the RSA modulus + @param prng An active PRNG state + @param prng_idx The index of the PRNG desired + @param hash_idx The index of the hash desired + @param out [out] The destination for the encoded data + @param outlen [in/out] The max size and resulting size of the encoded data + @return CRYPT_OK if successful + */ +int pkcs_1_oaep_encode(const unsigned char *msg, unsigned long msglen, + const unsigned char *lparam, unsigned long lparamlen, + unsigned long modulus_bitlen, prng_state *prng, + int prng_idx, int hash_idx, + unsigned char *out, unsigned long *outlen) { + unsigned char *DB, *seed, *mask; + unsigned long hLen, x, y, modulus_len; + int err; + + LTC_ARGCHK(msg != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* test valid hash */ + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + + /* valid prng */ + if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) { + return err; + } + + hLen = hash_descriptor[hash_idx].hashsize; + modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); + + /* test message size */ + if ((2 * hLen >= (modulus_len - 2)) || (msglen > (modulus_len - 2 * hLen - 2))) { + return CRYPT_PK_INVALID_SIZE; + } + + /* allocate ram for DB/mask/salt of size modulus_len */ + DB = XMALLOC(modulus_len); + mask = XMALLOC(modulus_len); + seed = XMALLOC(hLen); + if ((DB == NULL) || (mask == NULL) || (seed == NULL)) { + if (DB != NULL) { + XFREE(DB); + } + if (mask != NULL) { + XFREE(mask); + } + if (seed != NULL) { + XFREE(seed); + } + return CRYPT_MEM; + } + + /* get lhash */ + /* DB == lhash || PS || 0x01 || M, PS == k - mlen - 2hlen - 2 zeroes */ + x = modulus_len; + if (lparam != NULL) { + if ((err = hash_memory(hash_idx, lparam, lparamlen, DB, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + } else { + /* can't pass hash_memory a NULL so use DB with zero length */ + if ((err = hash_memory(hash_idx, DB, 0, DB, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + } + + /* append PS then 0x01 (to lhash) */ + x = hLen; + y = modulus_len - msglen - 2 * hLen - 2; + XMEMSET(DB + x, 0, y); + x += y; + + /* 0x01 byte */ + DB[x++] = 0x01; + + /* message (length = msglen) */ + XMEMCPY(DB + x, msg, msglen); + x += msglen; + + /* now choose a random seed */ + if (prng_descriptor[prng_idx].read(seed, hLen, prng) != hLen) { + err = CRYPT_ERROR_READPRNG; + goto LBL_ERR; + } + + /* compute MGF1 of seed (k - hlen - 1) */ + if ((err = pkcs_1_mgf1(hash_idx, seed, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* xor against DB */ + for (y = 0; y < (modulus_len - hLen - 1); y++) { + DB[y] ^= mask[y]; + } + + /* compute MGF1 of maskedDB (hLen) */ + if ((err = pkcs_1_mgf1(hash_idx, DB, modulus_len - hLen - 1, mask, hLen)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* XOR against seed */ + for (y = 0; y < hLen; y++) { + seed[y] ^= mask[y]; + } + + /* create string of length modulus_len */ + if (*outlen < modulus_len) { + *outlen = modulus_len; + err = CRYPT_BUFFER_OVERFLOW; + goto LBL_ERR; + } + + /* start output which is 0x00 || maskedSeed || maskedDB */ + x = 0; + out[x++] = 0x00; + XMEMCPY(out + x, seed, hLen); + x += hLen; + XMEMCPY(out + x, DB, modulus_len - hLen - 1); + x += modulus_len - hLen - 1; + + *outlen = x; + + err = CRYPT_OK; +LBL_ERR: + #ifdef LTC_CLEAN_STACK + zeromem(DB, modulus_len); + zeromem(seed, hLen); + zeromem(mask, modulus_len); + #endif + + XFREE(seed); + XFREE(mask); + XFREE(DB); + + return err; +} +#endif /* LTC_PKCS_1 */ + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_oaep_encode.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file pkcs_1_os2ip.c + Octet to Integer OS2IP, Tom St Denis + */ +#ifdef LTC_PKCS_1 + +/** + Read a binary string into an mp_int + @param n [out] The mp_int destination + @param in The binary string to read + @param inlen The length of the binary string + @return CRYPT_OK if successful + */ +int pkcs_1_os2ip(void *n, unsigned char *in, unsigned long inlen) { + return mp_read_unsigned_bin(n, in, inlen); +} +#endif /* LTC_PKCS_1 */ + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_os2ip.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file pkcs_1_pss_decode.c + LTC_PKCS #1 PSS Signature Padding, Tom St Denis + */ + +#ifdef LTC_PKCS_1 + +/** + LTC_PKCS #1 v2.00 PSS decode + @param msghash The hash to verify + @param msghashlen The length of the hash (octets) + @param sig The signature data (encoded data) + @param siglen The length of the signature data (octets) + @param saltlen The length of the salt used (octets) + @param hash_idx The index of the hash desired + @param modulus_bitlen The bit length of the RSA modulus + @param res [out] The result of the comparison, 1==valid, 0==invalid + @return CRYPT_OK if successful (even if the comparison failed) + */ +int pkcs_1_pss_decode(const unsigned char *msghash, unsigned long msghashlen, + const unsigned char *sig, unsigned long siglen, + unsigned long saltlen, int hash_idx, + unsigned long modulus_bitlen, int *res) { + unsigned char *DB, *mask, *salt, *hash; + unsigned long x, y, hLen, modulus_len; + int err; + hash_state md; + + LTC_ARGCHK(msghash != NULL); + LTC_ARGCHK(res != NULL); + + /* default to invalid */ + *res = 0; + + /* ensure hash is valid */ + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + + hLen = hash_descriptor[hash_idx].hashsize; + modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); + + /* check sizes */ + if ((saltlen > modulus_len) || + (modulus_len < hLen + saltlen + 2) || (siglen != modulus_len)) { + return CRYPT_PK_INVALID_SIZE; + } + + /* allocate ram for DB/mask/salt/hash of size modulus_len */ + DB = XMALLOC(modulus_len); + mask = XMALLOC(modulus_len); + salt = XMALLOC(modulus_len); + hash = XMALLOC(modulus_len); + if ((DB == NULL) || (mask == NULL) || (salt == NULL) || (hash == NULL)) { + if (DB != NULL) { + XFREE(DB); + } + if (mask != NULL) { + XFREE(mask); + } + if (salt != NULL) { + XFREE(salt); + } + if (hash != NULL) { + XFREE(hash); + } + return CRYPT_MEM; + } + + /* ensure the 0xBC byte */ + if (sig[siglen - 1] != 0xBC) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } + + /* copy out the DB */ + x = 0; + XMEMCPY(DB, sig + x, modulus_len - hLen - 1); + x += modulus_len - hLen - 1; + + /* copy out the hash */ + XMEMCPY(hash, sig + x, hLen); + x += hLen; + + /* check the MSB */ + if ((sig[0] & ~(0xFF >> ((modulus_len << 3) - (modulus_bitlen - 1)))) != 0) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } + + /* generate mask of length modulus_len - hLen - 1 from hash */ + if ((err = pkcs_1_mgf1(hash_idx, hash, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* xor against DB */ + for (y = 0; y < (modulus_len - hLen - 1); y++) { + DB[y] ^= mask[y]; + } + + /* now clear the first byte [make sure smaller than modulus] */ + DB[0] &= 0xFF >> ((modulus_len << 3) - (modulus_bitlen - 1)); + + /* DB = PS || 0x01 || salt, PS == modulus_len - saltlen - hLen - 2 zero bytes */ + + /* check for zeroes and 0x01 */ + for (x = 0; x < modulus_len - saltlen - hLen - 2; x++) { + if (DB[x] != 0x00) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } + } + + /* check for the 0x01 */ + if (DB[x++] != 0x01) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } + + /* M = (eight) 0x00 || msghash || salt, mask = H(M) */ + if ((err = hash_descriptor[hash_idx].init(&md)) != CRYPT_OK) { + goto LBL_ERR; + } + zeromem(mask, 8); + if ((err = hash_descriptor[hash_idx].process(&md, mask, 8)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].process(&md, msghash, msghashlen)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].process(&md, DB + x, saltlen)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].done(&md, mask)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* mask == hash means valid signature */ + if (XMEMCMP(mask, hash, hLen) == 0) { + *res = 1; + } + + err = CRYPT_OK; +LBL_ERR: + #ifdef LTC_CLEAN_STACK + zeromem(DB, modulus_len); + zeromem(mask, modulus_len); + zeromem(salt, modulus_len); + zeromem(hash, modulus_len); + #endif + + XFREE(hash); + XFREE(salt); + XFREE(mask); + XFREE(DB); + + return err; +} +#endif /* LTC_PKCS_1 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_decode.c,v $ */ +/* $Revision: 1.11 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file pkcs_1_pss_encode.c + LTC_PKCS #1 PSS Signature Padding, Tom St Denis + */ + +#ifdef LTC_PKCS_1 + +/** + LTC_PKCS #1 v2.00 Signature Encoding + @param msghash The hash to encode + @param msghashlen The length of the hash (octets) + @param saltlen The length of the salt desired (octets) + @param prng An active PRNG context + @param prng_idx The index of the PRNG desired + @param hash_idx The index of the hash desired + @param modulus_bitlen The bit length of the RSA modulus + @param out [out] The destination of the encoding + @param outlen [in/out] The max size and resulting size of the encoded data + @return CRYPT_OK if successful + */ +int pkcs_1_pss_encode(const unsigned char *msghash, unsigned long msghashlen, + unsigned long saltlen, prng_state *prng, + int prng_idx, int hash_idx, + unsigned long modulus_bitlen, + unsigned char *out, unsigned long *outlen) { + unsigned char *DB, *mask, *salt, *hash; + unsigned long x, y, hLen, modulus_len; + int err; + hash_state md; + + LTC_ARGCHK(msghash != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* ensure hash and PRNG are valid */ + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) { + return err; + } + + hLen = hash_descriptor[hash_idx].hashsize; + modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); + + /* check sizes */ + if ((saltlen > modulus_len) || (modulus_len < hLen + saltlen + 2)) { + return CRYPT_PK_INVALID_SIZE; + } + + /* allocate ram for DB/mask/salt/hash of size modulus_len */ + DB = XMALLOC(modulus_len); + mask = XMALLOC(modulus_len); + salt = XMALLOC(modulus_len); + hash = XMALLOC(modulus_len); + if ((DB == NULL) || (mask == NULL) || (salt == NULL) || (hash == NULL)) { + if (DB != NULL) { + XFREE(DB); + } + if (mask != NULL) { + XFREE(mask); + } + if (salt != NULL) { + XFREE(salt); + } + if (hash != NULL) { + XFREE(hash); + } + return CRYPT_MEM; + } + + + /* generate random salt */ + if (saltlen > 0) { + if (prng_descriptor[prng_idx].read(salt, saltlen, prng) != saltlen) { + err = CRYPT_ERROR_READPRNG; + goto LBL_ERR; + } + } + + /* M = (eight) 0x00 || msghash || salt, hash = H(M) */ + if ((err = hash_descriptor[hash_idx].init(&md)) != CRYPT_OK) { + goto LBL_ERR; + } + zeromem(DB, 8); + if ((err = hash_descriptor[hash_idx].process(&md, DB, 8)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].process(&md, msghash, msghashlen)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].process(&md, salt, saltlen)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].done(&md, hash)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* generate DB = PS || 0x01 || salt, PS == modulus_len - saltlen - hLen - 2 zero bytes */ + x = 0; + XMEMSET(DB + x, 0, modulus_len - saltlen - hLen - 2); + x += modulus_len - saltlen - hLen - 2; + DB[x++] = 0x01; + XMEMCPY(DB + x, salt, saltlen); + x += saltlen; + + /* generate mask of length modulus_len - hLen - 1 from hash */ + if ((err = pkcs_1_mgf1(hash_idx, hash, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* xor against DB */ + for (y = 0; y < (modulus_len - hLen - 1); y++) { + DB[y] ^= mask[y]; + } + + /* output is DB || hash || 0xBC */ + if (*outlen < modulus_len) { + *outlen = modulus_len; + err = CRYPT_BUFFER_OVERFLOW; + goto LBL_ERR; + } + + /* DB len = modulus_len - hLen - 1 */ + y = 0; + XMEMCPY(out + y, DB, modulus_len - hLen - 1); + y += modulus_len - hLen - 1; + + /* hash */ + XMEMCPY(out + y, hash, hLen); + y += hLen; + + /* 0xBC */ + out[y] = 0xBC; + + /* now clear the 8*modulus_len - modulus_bitlen most significant bits */ + out[0] &= 0xFF >> ((modulus_len << 3) - (modulus_bitlen - 1)); + + /* store output size */ + *outlen = modulus_len; + err = CRYPT_OK; +LBL_ERR: + #ifdef LTC_CLEAN_STACK + zeromem(DB, modulus_len); + zeromem(mask, modulus_len); + zeromem(salt, modulus_len); + zeromem(hash, modulus_len); + #endif + + XFREE(hash); + XFREE(salt); + XFREE(mask); + XFREE(DB); + + return err; +} +#endif /* LTC_PKCS_1 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_encode.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** @file pkcs_1_v1_5_decode.c + * + * LTC_PKCS #1 v1.5 Padding. (Andreas Lange) + */ + +#ifdef LTC_PKCS_1 + +/** @brief LTC_PKCS #1 v1.5 decode. + * + * @param msg The encoded data to decode + * @param msglen The length of the encoded data (octets) + * @param block_type Block type to use in padding (\sa ltc_pkcs_1_v1_5_blocks) + * @param modulus_bitlen The bit length of the RSA modulus + * @param out [out] Destination of decoding + * @param outlen [in/out] The max size and resulting size of the decoding + * @param is_valid [out] Boolean whether the padding was valid + * + * @return CRYPT_OK if successful (even if invalid) + */ +int pkcs_1_v1_5_decode(const unsigned char *msg, + unsigned long msglen, + int block_type, + unsigned long modulus_bitlen, + unsigned char *out, + unsigned long *outlen, + int *is_valid) { + unsigned long modulus_len, ps_len, i; + int result; + + /* default to invalid packet */ + *is_valid = 0; + + modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); + + /* test message size */ + + if ((msglen > modulus_len) || (modulus_len < 11)) { + return CRYPT_PK_INVALID_SIZE; + } + + /* separate encoded message */ + + if ((msg[0] != 0x00) || (msg[1] != (unsigned char)block_type)) { + result = CRYPT_INVALID_PACKET; + goto bail; + } + + if (block_type == LTC_LTC_PKCS_1_EME) { + for (i = 2; i < modulus_len; i++) { + /* separator */ + if (msg[i] == 0x00) { + break; + } + } + ps_len = i++ - 2; + + if ((i >= modulus_len) || (ps_len < 8)) { + /* There was no octet with hexadecimal value 0x00 to separate ps from m, + * or the length of ps is less than 8 octets. + */ + result = CRYPT_INVALID_PACKET; + goto bail; + } + } else { + for (i = 2; i < modulus_len - 1; i++) { + if (msg[i] != 0xFF) { + break; + } + } + + /* separator check */ + if (msg[i] != 0) { + /* There was no octet with hexadecimal value 0x00 to separate ps from m. */ + result = CRYPT_INVALID_PACKET; + goto bail; + } + + ps_len = i - 2; + } + + if (*outlen < (msglen - (2 + ps_len + 1))) { + *outlen = msglen - (2 + ps_len + 1); + result = CRYPT_BUFFER_OVERFLOW; + goto bail; + } + + *outlen = (msglen - (2 + ps_len + 1)); + XMEMCPY(out, &msg[2 + ps_len + 1], *outlen); + + /* valid packet */ + *is_valid = 1; + result = CRYPT_OK; +bail: + return result; +} /* pkcs_1_v1_5_decode */ +#endif /* #ifdef LTC_PKCS_1 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_decode.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/*! \file pkcs_1_v1_5_encode.c + * + * LTC_PKCS #1 v1.5 Padding (Andreas Lange) + */ + +#ifdef LTC_PKCS_1 + +/*! \brief LTC_PKCS #1 v1.5 encode. + * + * \param msg The data to encode + * \param msglen The length of the data to encode (octets) + * \param block_type Block type to use in padding (\sa ltc_pkcs_1_v1_5_blocks) + * \param modulus_bitlen The bit length of the RSA modulus + * \param prng An active PRNG state (only for LTC_LTC_PKCS_1_EME) + * \param prng_idx The index of the PRNG desired (only for LTC_LTC_PKCS_1_EME) + * \param out [out] The destination for the encoded data + * \param outlen [in/out] The max size and resulting size of the encoded data + * + * \return CRYPT_OK if successful + */ +int pkcs_1_v1_5_encode(const unsigned char *msg, + unsigned long msglen, + int block_type, + unsigned long modulus_bitlen, + prng_state *prng, + int prng_idx, + unsigned char *out, + unsigned long *outlen) { + unsigned long modulus_len, ps_len, i; + unsigned char *ps; + int result; + + /* valid block_type? */ + if ((block_type != LTC_LTC_PKCS_1_EMSA) && + (block_type != LTC_LTC_PKCS_1_EME)) { + return CRYPT_PK_INVALID_PADDING; + } + + if (block_type == LTC_LTC_PKCS_1_EME) { /* encryption padding, we need a valid PRNG */ + if ((result = prng_is_valid(prng_idx)) != CRYPT_OK) { + return result; + } + } + + modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); + + /* test message size */ + if ((msglen + 11) > modulus_len) { + return CRYPT_PK_INVALID_SIZE; + } + + if (*outlen < modulus_len) { + *outlen = modulus_len; + result = CRYPT_BUFFER_OVERFLOW; + goto bail; + } + + /* generate an octets string PS */ + ps = &out[2]; + ps_len = modulus_len - msglen - 3; + + if (block_type == LTC_LTC_PKCS_1_EME) { + /* now choose a random ps */ + if (prng_descriptor[prng_idx].read(ps, ps_len, prng) != ps_len) { + result = CRYPT_ERROR_READPRNG; + goto bail; + } + + /* transform zero bytes (if any) to non-zero random bytes */ + for (i = 0; i < ps_len; i++) { + while (ps[i] == 0) { + if (prng_descriptor[prng_idx].read(&ps[i], 1, prng) != 1) { + result = CRYPT_ERROR_READPRNG; + goto bail; + } + } + } + } else { + XMEMSET(ps, 0xFF, ps_len); + } + + /* create string of length modulus_len */ + out[0] = 0x00; + out[1] = (unsigned char)block_type;/* block_type 1 or 2 */ + out[2 + ps_len] = 0x00; + XMEMCPY(&out[2 + ps_len + 1], msg, msglen); + *outlen = modulus_len; + + result = CRYPT_OK; +bail: + return result; +} /* pkcs_1_v1_5_encode */ +#endif /* #ifdef LTC_PKCS_1 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_encode.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rand_prime.c + Generate a random prime, Tom St Denis + */ + +#define USE_BBS 1 + +int rand_prime(void *N, long len, prng_state *prng, int wprng) { + int err, res, type; + unsigned char *buf; + + LTC_ARGCHK(N != NULL); + + /* get type */ + if (len < 0) { + type = USE_BBS; + len = -len; + } else { + type = 0; + } + + /* allow sizes between 2 and 512 bytes for a prime size */ + if ((len < 2) || (len > 512)) { + return CRYPT_INVALID_PRIME_SIZE; + } + + /* valid PRNG? Better be! */ + if ((err = prng_is_valid(wprng)) != CRYPT_OK) { + return err; + } + + /* allocate buffer to work with */ + buf = XCALLOC(1, len); + if (buf == NULL) { + return CRYPT_MEM; + } + + do { + /* generate value */ + if (prng_descriptor[wprng].read(buf, len, prng) != (unsigned long)len) { + XFREE(buf); + return CRYPT_ERROR_READPRNG; + } + + /* munge bits */ + buf[0] |= 0x80 | 0x40; + buf[len - 1] |= 0x01 | ((type & USE_BBS) ? 0x02 : 0x00); + + /* load value */ + if ((err = mp_read_unsigned_bin(N, buf, len)) != CRYPT_OK) { + XFREE(buf); + return err; + } + + /* test */ + if ((err = mp_prime_is_prime(N, 8, &res)) != CRYPT_OK) { + XFREE(buf); + return err; + } + } while (res == LTC_MP_NO); + +#ifdef LTC_CLEAN_STACK + zeromem(buf, len); +#endif + + XFREE(buf); + return CRYPT_OK; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/math/rand_prime.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:23 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rng_get_bytes.c + portable way to get secure random bits to feed a PRNG (Tom St Denis) + */ + +#ifdef LTC_DEVRANDOM +/* on *NIX read /dev/random */ +static unsigned long rng_nix(unsigned char *buf, unsigned long len, + void (*callback)(void)) { + #ifdef LTC_NO_FILE + return 0; + #else + FILE *f; + unsigned long x; + #ifdef TRY_URANDOM_FIRST + f = fopen("/dev/urandom", "rb"); + if (f == NULL) + #endif /* TRY_URANDOM_FIRST */ + f = fopen("/dev/random", "rb"); + + if (f == NULL) { + return 0; + } + + /* disable buffering */ + if (setvbuf(f, NULL, _IONBF, 0) != 0) { + fclose(f); + return 0; + } + + x = (unsigned long)fread(buf, 1, (size_t)len, f); + fclose(f); + return x; + #endif /* LTC_NO_FILE */ +} +#endif /* LTC_DEVRANDOM */ + +/* on ANSI C platforms with 100 < CLOCKS_PER_SEC < 10000 */ +#if defined(CLOCKS_PER_SEC) && !defined(WINCE) + + #define ANSI_RNG + +static unsigned long rng_ansic(unsigned char *buf, unsigned long len, + void (*callback)(void)) { + clock_t t1; + int l, acc, bits, a, b; + + if ((XCLOCKS_PER_SEC < 100) || (XCLOCKS_PER_SEC > 10000)) { + return 0; + } + + l = len; + bits = 8; + acc = a = b = 0; + while (len--) { + if (callback != NULL) callback(); + while (bits--) { + do { + t1 = XCLOCK(); + while (t1 == XCLOCK()) a ^= 1; + t1 = XCLOCK(); + while (t1 == XCLOCK()) b ^= 1; + } while (a == b); + acc = (acc << 1) | a; + } + *buf++ = acc; + acc = 0; + bits = 8; + } + acc = bits = a = b = 0; + return l; +} +#endif + +/* Try the Microsoft CSP */ +#if defined(WIN32) || defined(WINCE) + #define _WIN32_WINNT 0x0400 + #ifdef WINCE + #define UNDER_CE + #define ARM + #endif +#include +#include + +static unsigned long rng_win32(unsigned char *buf, unsigned long len, + void (*callback)(void)) { + HCRYPTPROV hProv = 0; + + if (!CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, + (CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET)) && + !CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET)) + return 0; + + if (CryptGenRandom(hProv, len, buf) == TRUE) { + CryptReleaseContext(hProv, 0); + return len; + } else { + CryptReleaseContext(hProv, 0); + return 0; + } +} +#endif /* WIN32 */ + +/** + Read the system RNG + @param out Destination + @param outlen Length desired (octets) + @param callback Pointer to void function to act as "callback" when RNG is slow. This can be NULL + @return Number of octets read + */ +unsigned long rng_get_bytes(unsigned char *out, unsigned long outlen, + void (*callback)(void)) { + unsigned long x; + + LTC_ARGCHK(out != NULL); + +#if defined(LTC_DEVRANDOM) + x = rng_nix(out, outlen, callback); + if (x != 0) { + return x; + } +#endif +#ifdef WIN32 + x = rng_win32(out, outlen, callback); + if (x != 0) { + return x; + } +#endif +#ifdef ANSI_RNG + x = rng_ansic(out, outlen, callback); + if (x != 0) { + return x; + } +#endif + return 0; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/prngs/rng_get_bytes.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rng_make_prng.c + portable way to get secure random bits to feed a PRNG (Tom St Denis) + */ + +/** + Create a PRNG from a RNG + @param bits Number of bits of entropy desired (64 ... 1024) + @param wprng Index of which PRNG to setup + @param prng [out] PRNG state to initialize + @param callback A pointer to a void function for when the RNG is slow, this can be NULL + @return CRYPT_OK if successful + */ +int rng_make_prng(int bits, int wprng, prng_state *prng, + void (*callback)(void)) { + unsigned char buf[256]; + int err; + + LTC_ARGCHK(prng != NULL); + + /* check parameter */ + if ((err = prng_is_valid(wprng)) != CRYPT_OK) { + return err; + } + + if ((bits < 64) || (bits > 1024)) { + return CRYPT_INVALID_PRNGSIZE; + } + + if ((err = prng_descriptor[wprng].start(prng)) != CRYPT_OK) { + return err; + } + + bits = ((bits / 8) + ((bits & 7) != 0 ? 1 : 0)) * 2; + if (rng_get_bytes(buf, (unsigned long)bits, callback) != (unsigned long)bits) { + return CRYPT_ERROR_READPRNG; + } + + if ((err = prng_descriptor[wprng].add_entropy(buf, (unsigned long)bits, prng)) != CRYPT_OK) { + return err; + } + + if ((err = prng_descriptor[wprng].ready(prng)) != CRYPT_OK) { + return err; + } + +#ifdef LTC_CLEAN_STACK + zeromem(buf, sizeof(buf)); +#endif + return CRYPT_OK; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/prngs/rng_make_prng.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rsa_decrypt_key.c + RSA LTC_PKCS #1 Decryption, Tom St Denis and Andreas Lange + */ + +#ifdef LTC_MRSA + +/** + LTC_PKCS #1 decrypt then v1.5 or OAEP depad + @param in The ciphertext + @param inlen The length of the ciphertext (octets) + @param out [out] The plaintext + @param outlen [in/out] The max size and resulting size of the plaintext (octets) + @param lparam The system "lparam" value + @param lparamlen The length of the lparam value (octets) + @param hash_idx The index of the hash desired + @param padding Type of padding (LTC_LTC_PKCS_1_OAEP or LTC_LTC_PKCS_1_V1_5) + @param stat [out] Result of the decryption, 1==valid, 0==invalid + @param key The corresponding private RSA key + @return CRYPT_OK if succcessul (even if invalid) + */ +int rsa_decrypt_key_ex(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + const unsigned char *lparam, unsigned long lparamlen, + int hash_idx, int padding, + int *stat, rsa_key *key) { + unsigned long modulus_bitlen, modulus_bytelen, x; + int err; + unsigned char *tmp; + + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(stat != NULL); + + /* default to invalid */ + *stat = 0; + + /* valid padding? */ + + if ((padding != LTC_LTC_PKCS_1_V1_5) && + (padding != LTC_LTC_PKCS_1_OAEP)) { + return CRYPT_PK_INVALID_PADDING; + } + + if (padding == LTC_LTC_PKCS_1_OAEP) { + /* valid hash ? */ + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + } + + /* get modulus len in bits */ + modulus_bitlen = mp_count_bits((key->N)); + + /* outlen must be at least the size of the modulus */ + modulus_bytelen = mp_unsigned_bin_size((key->N)); + if (modulus_bytelen != inlen) { + return CRYPT_INVALID_PACKET; + } + + /* allocate ram */ + tmp = XMALLOC(inlen); + if (tmp == NULL) { + return CRYPT_MEM; + } + + /* rsa decode the packet */ + x = inlen; + if ((err = ltc_mp.rsa_me(in, inlen, tmp, &x, PK_PRIVATE, key)) != CRYPT_OK) { + XFREE(tmp); + return err; + } + + if (padding == LTC_LTC_PKCS_1_OAEP) { + /* now OAEP decode the packet */ + err = pkcs_1_oaep_decode(tmp, x, lparam, lparamlen, modulus_bitlen, hash_idx, + out, outlen, stat); + } else { + /* now LTC_PKCS #1 v1.5 depad the packet */ + err = pkcs_1_v1_5_decode(tmp, x, LTC_LTC_PKCS_1_EME, modulus_bitlen, out, outlen, stat); + } + + XFREE(tmp); + return err; +} +#endif /* LTC_MRSA */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_decrypt_key.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rsa_encrypt_key.c + RSA LTC_PKCS #1 encryption, Tom St Denis and Andreas Lange + */ + +#ifdef LTC_MRSA + +/** + (LTC_PKCS #1 v2.0) OAEP pad then encrypt + @param in The plaintext + @param inlen The length of the plaintext (octets) + @param out [out] The ciphertext + @param outlen [in/out] The max size and resulting size of the ciphertext + @param lparam The system "lparam" for the encryption + @param lparamlen The length of lparam (octets) + @param prng An active PRNG + @param prng_idx The index of the desired prng + @param hash_idx The index of the desired hash + @param padding Type of padding (LTC_LTC_PKCS_1_OAEP or LTC_LTC_PKCS_1_V1_5) + @param key The RSA key to encrypt to + @return CRYPT_OK if successful + */ +int rsa_encrypt_key_ex(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + const unsigned char *lparam, unsigned long lparamlen, + prng_state *prng, int prng_idx, int hash_idx, int padding, rsa_key *key) { + unsigned long modulus_bitlen, modulus_bytelen, x; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* valid padding? */ + if ((padding != LTC_LTC_PKCS_1_V1_5) && + (padding != LTC_LTC_PKCS_1_OAEP)) { + return CRYPT_PK_INVALID_PADDING; + } + + /* valid prng? */ + if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) { + return err; + } + + if (padding == LTC_LTC_PKCS_1_OAEP) { + /* valid hash? */ + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + } + + /* get modulus len in bits */ + modulus_bitlen = mp_count_bits((key->N)); + + /* outlen must be at least the size of the modulus */ + modulus_bytelen = mp_unsigned_bin_size((key->N)); + if (modulus_bytelen > *outlen) { + *outlen = modulus_bytelen; + return CRYPT_BUFFER_OVERFLOW; + } + + if (padding == LTC_LTC_PKCS_1_OAEP) { + /* OAEP pad the key */ + x = *outlen; + if ((err = pkcs_1_oaep_encode(in, inlen, lparam, + lparamlen, modulus_bitlen, prng, prng_idx, hash_idx, + out, &x)) != CRYPT_OK) { + return err; + } + } else { + /* LTC_PKCS #1 v1.5 pad the key */ + x = *outlen; + if ((err = pkcs_1_v1_5_encode(in, inlen, LTC_LTC_PKCS_1_EME, + modulus_bitlen, prng, prng_idx, + out, &x)) != CRYPT_OK) { + return err; + } + } + + /* rsa exptmod the OAEP or LTC_PKCS #1 v1.5 pad */ + return ltc_mp.rsa_me(out, x, out, outlen, PK_PUBLIC, key); +} +#endif /* LTC_MRSA */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_encrypt_key.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rsa_exptmod.c + RSA LTC_PKCS exptmod, Tom St Denis + */ + +#ifdef LTC_MRSA + +/** + Compute an RSA modular exponentiation + @param in The input data to send into RSA + @param inlen The length of the input (octets) + @param out [out] The destination + @param outlen [in/out] The max size and resulting size of the output + @param which Which exponent to use, e.g. PK_PRIVATE or PK_PUBLIC + @param key The RSA key to use + @return CRYPT_OK if successful + */ +int rsa_exptmod(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, int which, + rsa_key *key) { + void *tmp, *tmpa, *tmpb; + unsigned long x; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* is the key of the right type for the operation? */ + if ((which == PK_PRIVATE) && (key->type != PK_PRIVATE)) { + return CRYPT_PK_NOT_PRIVATE; + } + + /* must be a private or public operation */ + if ((which != PK_PRIVATE) && (which != PK_PUBLIC)) { + return CRYPT_PK_INVALID_TYPE; + } + + /* init and copy into tmp */ + if ((err = mp_init_multi(&tmp, &tmpa, &tmpb, NULL)) != CRYPT_OK) { + return err; + } + if ((err = mp_read_unsigned_bin(tmp, (unsigned char *)in, (int)inlen)) != CRYPT_OK) { + goto error; + } + + /* sanity check on the input */ + if (mp_cmp(key->N, tmp) == LTC_MP_LT) { + err = CRYPT_PK_INVALID_SIZE; + goto error; + } + + /* are we using the private exponent and is the key optimized? */ + if (which == PK_PRIVATE) { + /* tmpa = tmp^dP mod p */ + if ((err = mp_exptmod(tmp, key->dP, key->p, tmpa)) != CRYPT_OK) { + goto error; + } + + /* tmpb = tmp^dQ mod q */ + if ((err = mp_exptmod(tmp, key->dQ, key->q, tmpb)) != CRYPT_OK) { + goto error; + } + + /* tmp = (tmpa - tmpb) * qInv (mod p) */ + if ((err = mp_sub(tmpa, tmpb, tmp)) != CRYPT_OK) { + goto error; + } + if ((err = mp_mulmod(tmp, key->qP, key->p, tmp)) != CRYPT_OK) { + goto error; + } + + /* tmp = tmpb + q * tmp */ + if ((err = mp_mul(tmp, key->q, tmp)) != CRYPT_OK) { + goto error; + } + if ((err = mp_add(tmp, tmpb, tmp)) != CRYPT_OK) { + goto error; + } + } else { + /* exptmod it */ + if ((err = mp_exptmod(tmp, key->e, key->N, tmp)) != CRYPT_OK) { + goto error; + } + } + + /* read it back */ + x = (unsigned long)mp_unsigned_bin_size(key->N); + if (x > *outlen) { + *outlen = x; + err = CRYPT_BUFFER_OVERFLOW; + goto error; + } + + /* this should never happen ... */ + if (mp_unsigned_bin_size(tmp) > mp_unsigned_bin_size(key->N)) { + err = CRYPT_ERROR; + goto error; + } + *outlen = x; + + /* convert it */ + zeromem(out, x); + if ((err = mp_to_unsigned_bin(tmp, out + (x - mp_unsigned_bin_size(tmp)))) != CRYPT_OK) { + goto error; + } + + /* clean up and return */ + err = CRYPT_OK; +error: + mp_clear_multi(tmp, tmpa, tmpb, NULL); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_exptmod.c,v $ */ +/* $Revision: 1.18 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rsa_free.c + Free an RSA key, Tom St Denis + */ + +#ifdef LTC_MRSA + +/** + Free an RSA key from memory + @param key The RSA key to free + */ +void rsa_free(rsa_key *key) { + LTC_ARGCHKVD(key != NULL); + mp_clear_multi(key->e, key->d, key->N, key->dQ, key->dP, key->qP, key->p, key->q, NULL); +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_free.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rsa_import.c + Import a LTC_PKCS RSA key, Tom St Denis + */ + +#ifdef LTC_MRSA + +/** + Import an RSAPublicKey or RSAPrivateKey [two-prime only, only support >= 1024-bit keys, defined in LTC_PKCS #1 v2.1] + @param in The packet to import from + @param inlen It's length (octets) + @param key [out] Destination for newly imported key + @return CRYPT_OK if successful, upon error allocated memory is freed + */ +int rsa_import(const unsigned char *in, unsigned long inlen, rsa_key *key) { + int err; + void *zero; + unsigned char *tmpbuf; + unsigned long t, x, y, z, tmpoid[16]; + ltc_asn1_list ssl_pubkey_hashoid[2]; + ltc_asn1_list ssl_pubkey[2]; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(ltc_mp.name != NULL); + + /* init key */ + if ((err = mp_init_multi(&key->e, &key->d, &key->N, &key->dQ, + &key->dP, &key->qP, &key->p, &key->q, NULL)) != CRYPT_OK) { + return err; + } + + /* see if the OpenSSL DER format RSA public key will work */ + tmpbuf = XCALLOC(1, MAX_RSA_SIZE * 8); + if (tmpbuf == NULL) { + err = CRYPT_MEM; + goto LBL_ERR; + } + + /* this includes the internal hash ID and optional params (NULL in this case) */ + LTC_SET_ASN1(ssl_pubkey_hashoid, 0, LTC_ASN1_OBJECT_IDENTIFIER, tmpoid, sizeof(tmpoid) / sizeof(tmpoid[0])); + LTC_SET_ASN1(ssl_pubkey_hashoid, 1, LTC_ASN1_NULL, NULL, 0); + + /* the actual format of the SSL DER key is odd, it stores a RSAPublicKey in a **BIT** string ... so we have to extract it + then proceed to convert bit to octet + */ + LTC_SET_ASN1(ssl_pubkey, 0, LTC_ASN1_SEQUENCE, &ssl_pubkey_hashoid, 2); + LTC_SET_ASN1(ssl_pubkey, 1, LTC_ASN1_BIT_STRING, tmpbuf, MAX_RSA_SIZE * 8); + + if (der_decode_sequence(in, inlen, + ssl_pubkey, 2UL) == CRYPT_OK) { + /* ok now we have to reassemble the BIT STRING to an OCTET STRING. Thanks OpenSSL... */ + for (t = y = z = x = 0; x < ssl_pubkey[1].size; x++) { + y = (y << 1) | tmpbuf[x]; + if (++z == 8) { + tmpbuf[t++] = (unsigned char)y; + y = 0; + z = 0; + } + } + + /* now it should be SEQUENCE { INTEGER, INTEGER } */ + if ((err = der_decode_sequence_multi(tmpbuf, t, + LTC_ASN1_INTEGER, 1UL, key->N, + LTC_ASN1_INTEGER, 1UL, key->e, + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { + XFREE(tmpbuf); + goto LBL_ERR; + } + XFREE(tmpbuf); + key->type = PK_PUBLIC; + return CRYPT_OK; + } + XFREE(tmpbuf); + + /* not SSL public key, try to match against LTC_PKCS #1 standards */ + if ((err = der_decode_sequence_multi(in, inlen, + LTC_ASN1_INTEGER, 1UL, key->N, + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { + goto LBL_ERR; + } + + if (mp_cmp_d(key->N, 0) == LTC_MP_EQ) { + if ((err = mp_init(&zero)) != CRYPT_OK) { + goto LBL_ERR; + } + /* it's a private key */ + if ((err = der_decode_sequence_multi(in, inlen, + LTC_ASN1_INTEGER, 1UL, zero, + LTC_ASN1_INTEGER, 1UL, key->N, + LTC_ASN1_INTEGER, 1UL, key->e, + LTC_ASN1_INTEGER, 1UL, key->d, + LTC_ASN1_INTEGER, 1UL, key->p, + LTC_ASN1_INTEGER, 1UL, key->q, + LTC_ASN1_INTEGER, 1UL, key->dP, + LTC_ASN1_INTEGER, 1UL, key->dQ, + LTC_ASN1_INTEGER, 1UL, key->qP, + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { + mp_clear(zero); + goto LBL_ERR; + } + mp_clear(zero); + key->type = PK_PRIVATE; + } else if (mp_cmp_d(key->N, 1) == LTC_MP_EQ) { + /* we don't support multi-prime RSA */ + err = CRYPT_PK_INVALID_TYPE; + goto LBL_ERR; + } else { + /* it's a public key and we lack e */ + if ((err = der_decode_sequence_multi(in, inlen, + LTC_ASN1_INTEGER, 1UL, key->N, + LTC_ASN1_INTEGER, 1UL, key->e, + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { + goto LBL_ERR; + } + key->type = PK_PUBLIC; + } + return CRYPT_OK; +LBL_ERR: + mp_clear_multi(key->d, key->e, key->N, key->dQ, key->dP, key->qP, key->p, key->q, NULL); + return err; +} +#endif /* LTC_MRSA */ + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_import.c,v $ */ +/* $Revision: 1.23 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rsa_make_key.c + RSA key generation, Tom St Denis + */ + +#ifdef LTC_MRSA + +/** + Create an RSA key + @param prng An active PRNG state + @param wprng The index of the PRNG desired + @param size The size of the modulus (key size) desired (octets) + @param e The "e" value (public key). e==65537 is a good choice + @param key [out] Destination of a newly created private key pair + @return CRYPT_OK if successful, upon error all allocated ram is freed + */ +int rsa_make_key(prng_state *prng, int wprng, int size, long e, rsa_key *key) { + void *p, *q, *tmp1, *tmp2, *tmp3; + int err; + + LTC_ARGCHK(ltc_mp.name != NULL); + LTC_ARGCHK(key != NULL); + + if ((size < (MIN_RSA_SIZE / 8)) || (size > (MAX_RSA_SIZE / 8))) { + return CRYPT_INVALID_KEYSIZE; + } + + if ((e < 3) || ((e & 1) == 0)) { + return CRYPT_INVALID_ARG; + } + + if ((err = prng_is_valid(wprng)) != CRYPT_OK) { + return err; + } + + if ((err = mp_init_multi(&p, &q, &tmp1, &tmp2, &tmp3, NULL)) != CRYPT_OK) { + return err; + } + + /* make primes p and q (optimization provided by Wayne Scott) */ + if ((err = mp_set_int(tmp3, e)) != CRYPT_OK) { + goto errkey; + } /* tmp3 = e */ + + /* make prime "p" */ + do { + if ((err = rand_prime(p, size / 2, prng, wprng)) != CRYPT_OK) { + goto errkey; + } + if ((err = mp_sub_d(p, 1, tmp1)) != CRYPT_OK) { + goto errkey; + } /* tmp1 = p-1 */ + if ((err = mp_gcd(tmp1, tmp3, tmp2)) != CRYPT_OK) { + goto errkey; + } /* tmp2 = gcd(p-1, e) */ + } while (mp_cmp_d(tmp2, 1) != 0); /* while e divides p-1 */ + + /* make prime "q" */ + do { + if ((err = rand_prime(q, size / 2, prng, wprng)) != CRYPT_OK) { + goto errkey; + } + if ((err = mp_sub_d(q, 1, tmp1)) != CRYPT_OK) { + goto errkey; + } /* tmp1 = q-1 */ + if ((err = mp_gcd(tmp1, tmp3, tmp2)) != CRYPT_OK) { + goto errkey; + } /* tmp2 = gcd(q-1, e) */ + } while (mp_cmp_d(tmp2, 1) != 0); /* while e divides q-1 */ + + /* tmp1 = lcm(p-1, q-1) */ + if ((err = mp_sub_d(p, 1, tmp2)) != CRYPT_OK) { + goto errkey; + } /* tmp2 = p-1 */ + /* tmp1 = q-1 (previous do/while loop) */ + if ((err = mp_lcm(tmp1, tmp2, tmp1)) != CRYPT_OK) { + goto errkey; + } /* tmp1 = lcm(p-1, q-1) */ + + /* make key */ + if ((err = mp_init_multi(&key->e, &key->d, &key->N, &key->dQ, &key->dP, &key->qP, &key->p, &key->q, NULL)) != CRYPT_OK) { + goto errkey; + } + + if ((err = mp_set_int(key->e, e)) != CRYPT_OK) { + goto errkey; + } /* key->e = e */ + if ((err = mp_invmod(key->e, tmp1, key->d)) != CRYPT_OK) { + goto errkey; + } /* key->d = 1/e mod lcm(p-1,q-1) */ + if ((err = mp_mul(p, q, key->N)) != CRYPT_OK) { + goto errkey; + } /* key->N = pq */ + + /* optimize for CRT now */ + /* find d mod q-1 and d mod p-1 */ + if ((err = mp_sub_d(p, 1, tmp1)) != CRYPT_OK) { + goto errkey; + } /* tmp1 = q-1 */ + if ((err = mp_sub_d(q, 1, tmp2)) != CRYPT_OK) { + goto errkey; + } /* tmp2 = p-1 */ + if ((err = mp_mod(key->d, tmp1, key->dP)) != CRYPT_OK) { + goto errkey; + } /* dP = d mod p-1 */ + if ((err = mp_mod(key->d, tmp2, key->dQ)) != CRYPT_OK) { + goto errkey; + } /* dQ = d mod q-1 */ + if ((err = mp_invmod(q, p, key->qP)) != CRYPT_OK) { + goto errkey; + } /* qP = 1/q mod p */ + + if ((err = mp_copy(p, key->p)) != CRYPT_OK) { + goto errkey; + } + if ((err = mp_copy(q, key->q)) != CRYPT_OK) { + goto errkey; + } + + /* set key type (in this case it's CRT optimized) */ + key->type = PK_PRIVATE; + + /* return ok and free temps */ + err = CRYPT_OK; + goto cleanup; +errkey: + mp_clear_multi(key->d, key->e, key->N, key->dQ, key->dP, key->qP, key->p, key->q, NULL); +cleanup: + mp_clear_multi(tmp3, tmp2, tmp1, p, q, NULL); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_make_key.c,v $ */ +/* $Revision: 1.16 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rsa_sign_hash.c + RSA LTC_PKCS #1 v1.5 and v2 PSS sign hash, Tom St Denis and Andreas Lange + */ + +#ifdef LTC_MRSA + +/** + LTC_PKCS #1 pad then sign + @param in The hash to sign + @param inlen The length of the hash to sign (octets) + @param out [out] The signature + @param outlen [in/out] The max size and resulting size of the signature + @param padding Type of padding (LTC_LTC_PKCS_1_PSS or LTC_LTC_PKCS_1_V1_5) + @param prng An active PRNG state + @param prng_idx The index of the PRNG desired + @param hash_idx The index of the hash desired + @param saltlen The length of the salt desired (octets) + @param key The private RSA key to use + @return CRYPT_OK if successful + */ +int rsa_sign_hash_ex(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + int padding, + prng_state *prng, int prng_idx, + int hash_idx, unsigned long saltlen, + rsa_key *key) { + unsigned long modulus_bitlen, modulus_bytelen, x, y; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* valid padding? */ + if ((padding != LTC_LTC_PKCS_1_V1_5) && (padding != LTC_LTC_PKCS_1_PSS)) { + return CRYPT_PK_INVALID_PADDING; + } + + if (padding == LTC_LTC_PKCS_1_PSS) { + /* valid prng and hash ? */ + if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) { + return err; + } + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + } + + /* get modulus len in bits */ + modulus_bitlen = mp_count_bits((key->N)); + + /* outlen must be at least the size of the modulus */ + modulus_bytelen = mp_unsigned_bin_size((key->N)); + if (modulus_bytelen > *outlen) { + *outlen = modulus_bytelen; + return CRYPT_BUFFER_OVERFLOW; + } + + if (padding == LTC_LTC_PKCS_1_PSS) { + /* PSS pad the key */ + x = *outlen; + if ((err = pkcs_1_pss_encode(in, inlen, saltlen, prng, prng_idx, + hash_idx, modulus_bitlen, out, &x)) != CRYPT_OK) { + return err; + } + } else { + /* LTC_PKCS #1 v1.5 pad the hash */ + unsigned char *tmpin; + ltc_asn1_list digestinfo[2], siginfo[2]; + + /* not all hashes have OIDs... so sad */ + if (hash_descriptor[hash_idx].OIDlen == 0) { + return CRYPT_INVALID_ARG; + } + + /* construct the SEQUENCE + SEQUENCE { + SEQUENCE {hashoid OID + blah NULL + } + hash OCTET STRING + } + */ + LTC_SET_ASN1(digestinfo, 0, LTC_ASN1_OBJECT_IDENTIFIER, hash_descriptor[hash_idx].OID, hash_descriptor[hash_idx].OIDlen); + LTC_SET_ASN1(digestinfo, 1, LTC_ASN1_NULL, NULL, 0); + LTC_SET_ASN1(siginfo, 0, LTC_ASN1_SEQUENCE, digestinfo, 2); + LTC_SET_ASN1(siginfo, 1, LTC_ASN1_OCTET_STRING, in, inlen); + + /* allocate memory for the encoding */ + y = mp_unsigned_bin_size(key->N); + tmpin = XMALLOC(y); + if (tmpin == NULL) { + return CRYPT_MEM; + } + + if ((err = der_encode_sequence(siginfo, 2, tmpin, &y)) != CRYPT_OK) { + XFREE(tmpin); + return err; + } + + x = *outlen; + if ((err = pkcs_1_v1_5_encode(tmpin, y, LTC_LTC_PKCS_1_EMSA, + modulus_bitlen, NULL, 0, + out, &x)) != CRYPT_OK) { + XFREE(tmpin); + return err; + } + XFREE(tmpin); + } + + /* RSA encode it */ + return ltc_mp.rsa_me(out, x, out, outlen, PK_PRIVATE, key); +} +#endif /* LTC_MRSA */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_sign_hash.c,v $ */ +/* $Revision: 1.11 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rsa_verify_hash.c + RSA LTC_PKCS #1 v1.5 or v2 PSS signature verification, Tom St Denis and Andreas Lange + */ + +#ifdef LTC_MRSA + +/** + LTC_PKCS #1 de-sign then v1.5 or PSS depad + @param sig The signature data + @param siglen The length of the signature data (octets) + @param hash The hash of the message that was signed + @param hashlen The length of the hash of the message that was signed (octets) + @param padding Type of padding (LTC_LTC_PKCS_1_PSS or LTC_LTC_PKCS_1_V1_5) + @param hash_idx The index of the desired hash + @param saltlen The length of the salt used during signature + @param stat [out] The result of the signature comparison, 1==valid, 0==invalid + @param key The public RSA key corresponding to the key that performed the signature + @return CRYPT_OK on success (even if the signature is invalid) + */ +int rsa_verify_hash_ex(const unsigned char *sig, unsigned long siglen, + const unsigned char *hash, unsigned long hashlen, + int padding, + int hash_idx, unsigned long saltlen, + int *stat, rsa_key *key) { + unsigned long modulus_bitlen, modulus_bytelen, x; + int err; + unsigned char *tmpbuf; + + LTC_ARGCHK(hash != NULL); + LTC_ARGCHK(sig != NULL); + LTC_ARGCHK(stat != NULL); + LTC_ARGCHK(key != NULL); + + /* default to invalid */ + *stat = 0; + + /* valid padding? */ + + if ((padding != LTC_LTC_PKCS_1_V1_5) && + (padding != LTC_LTC_PKCS_1_PSS)) { + return CRYPT_PK_INVALID_PADDING; + } + + if (padding == LTC_LTC_PKCS_1_PSS) { + /* valid hash ? */ + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + } + + /* get modulus len in bits */ + modulus_bitlen = mp_count_bits((key->N)); + + /* outlen must be at least the size of the modulus */ + modulus_bytelen = mp_unsigned_bin_size((key->N)); + if (modulus_bytelen != siglen) { + return CRYPT_INVALID_PACKET; + } + + /* allocate temp buffer for decoded sig */ + tmpbuf = XMALLOC(siglen); + if (tmpbuf == NULL) { + return CRYPT_MEM; + } + + /* RSA decode it */ + x = siglen; + if ((err = ltc_mp.rsa_me(sig, siglen, tmpbuf, &x, PK_PUBLIC, key)) != CRYPT_OK) { + XFREE(tmpbuf); + return err; + } + + /* make sure the output is the right size */ + if (x != siglen) { + XFREE(tmpbuf); + return CRYPT_INVALID_PACKET; + } + + if (padding == LTC_LTC_PKCS_1_PSS) { + /* PSS decode and verify it */ + err = pkcs_1_pss_decode(hash, hashlen, tmpbuf, x, saltlen, hash_idx, modulus_bitlen, stat); + } else { + /* LTC_PKCS #1 v1.5 decode it */ + unsigned char *out; + unsigned long outlen, loid[16]; + int decoded; + ltc_asn1_list digestinfo[2], siginfo[2]; + + /* not all hashes have OIDs... so sad */ + if (hash_descriptor[hash_idx].OIDlen == 0) { + err = CRYPT_INVALID_ARG; + goto bail_2; + } + + /* allocate temp buffer for decoded hash */ + outlen = ((modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0)) - 3; + out = XMALLOC(outlen); + if (out == NULL) { + err = CRYPT_MEM; + goto bail_2; + } + + if ((err = pkcs_1_v1_5_decode(tmpbuf, x, LTC_LTC_PKCS_1_EMSA, modulus_bitlen, out, &outlen, &decoded)) != CRYPT_OK) { + XFREE(out); + goto bail_2; + } + + /* now we must decode out[0...outlen-1] using ASN.1, test the OID and then test the hash */ + + /* construct the SEQUENCE + SEQUENCE { + SEQUENCE {hashoid OID + blah NULL + } + hash OCTET STRING + } + */ + LTC_SET_ASN1(digestinfo, 0, LTC_ASN1_OBJECT_IDENTIFIER, loid, sizeof(loid) / sizeof(loid[0])); + LTC_SET_ASN1(digestinfo, 1, LTC_ASN1_NULL, NULL, 0); + LTC_SET_ASN1(siginfo, 0, LTC_ASN1_SEQUENCE, digestinfo, 2); + LTC_SET_ASN1(siginfo, 1, LTC_ASN1_OCTET_STRING, tmpbuf, siglen); + + if ((err = der_decode_sequence(out, outlen, siginfo, 2)) != CRYPT_OK) { + XFREE(out); + goto bail_2; + } + + /* test OID */ + if ((digestinfo[0].size == hash_descriptor[hash_idx].OIDlen) && + (XMEMCMP(digestinfo[0].data, hash_descriptor[hash_idx].OID, sizeof(unsigned long) * hash_descriptor[hash_idx].OIDlen) == 0) && + (siginfo[1].size == hashlen) && + (XMEMCMP(siginfo[1].data, hash, hashlen) == 0)) { + *stat = 1; + } + + #ifdef LTC_CLEAN_STACK + zeromem(out, outlen); + #endif + XFREE(out); + } + +bail_2: + #ifdef LTC_CLEAN_STACK + zeromem(tmpbuf, siglen); + #endif + XFREE(tmpbuf); + return err; +} +#endif /* LTC_MRSA */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_verify_hash.c,v $ */ +/* $Revision: 1.13 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file sprng.c + Secure PRNG, Tom St Denis + */ + +/* A secure PRNG using the RNG functions. Basically this is a + * wrapper that allows you to use a secure RNG as a PRNG + * in the various other functions. + */ + +#ifdef LTC_SPRNG + +const struct ltc_prng_descriptor sprng_desc = +{ + "sprng", 0, + &sprng_start, + &sprng_add_entropy, + &sprng_ready, + &sprng_read, + &sprng_done, + &sprng_export, + &sprng_import, + &sprng_test +}; + +/** + Start the PRNG + @param prng [out] The PRNG state to initialize + @return CRYPT_OK if successful + */ +int sprng_start(prng_state *prng) { + return CRYPT_OK; +} + +/** + Add entropy to the PRNG state + @param in The data to add + @param inlen Length of the data to add + @param prng PRNG state to update + @return CRYPT_OK if successful + */ +int sprng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng) { + return CRYPT_OK; +} + +/** + Make the PRNG ready to read from + @param prng The PRNG to make active + @return CRYPT_OK if successful + */ +int sprng_ready(prng_state *prng) { + return CRYPT_OK; +} + +/** + Read from the PRNG + @param out Destination + @param outlen Length of output + @param prng The active PRNG to read from + @return Number of octets read + */ +unsigned long sprng_read(unsigned char *out, unsigned long outlen, prng_state *prng) { + LTC_ARGCHK(out != NULL); + return rng_get_bytes(out, outlen, NULL); +} + +/** + Terminate the PRNG + @param prng The PRNG to terminate + @return CRYPT_OK if successful + */ +int sprng_done(prng_state *prng) { + return CRYPT_OK; +} + +/** + Export the PRNG state + @param out [out] Destination + @param outlen [in/out] Max size and resulting size of the state + @param prng The PRNG to export + @return CRYPT_OK if successful + */ +int sprng_export(unsigned char *out, unsigned long *outlen, prng_state *prng) { + LTC_ARGCHK(outlen != NULL); + + *outlen = 0; + return CRYPT_OK; +} + +/** + Import a PRNG state + @param in The PRNG state + @param inlen Size of the state + @param prng The PRNG to import + @return CRYPT_OK if successful + */ +int sprng_import(const unsigned char *in, unsigned long inlen, prng_state *prng) { + return CRYPT_OK; +} + +/** + PRNG self-test + @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled + */ +int sprng_test(void) { + return CRYPT_OK; +} +#endif + + + +/* $Source: /cvs/libtom/libtomcrypt/src/prngs/sprng.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file zeromem.c + Zero a block of memory, Tom St Denis + */ + +/** + Zero a block of memory + @param out The destination of the area to zero + @param outlen The length of the area to zero (octets) + */ +void zeromem(void *out, size_t outlen) { + unsigned char *mem = out; + + LTC_ARGCHKVD(out != NULL); + while (outlen-- > 0) { + *mem++ = 0; + } +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/zeromem.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file sha1.c + LTC_SHA1 code by Tom St Denis + */ + + +#ifdef LTC_SHA1 + +const struct ltc_hash_descriptor sha1_desc = +{ + "sha1", + 2, + 20, + 64, + + /* OID */ + { 1, 3, 14, 3, 2, 26, }, + 6, + + &sha1_init, + &sha1_process, + &sha1_done, + &sha1_test, + NULL +}; + + #define F0(x, y, z) (z ^ (x & (y ^ z))) + #define F1(x, y, z) (x ^ y ^ z) + #define F2(x, y, z) ((x & y) | (z & (x | y))) + #define F3(x, y, z) (x ^ y ^ z) + + #ifdef LTC_CLEAN_STACK +static int _sha1_compress(hash_state *md, unsigned char *buf) + #else +static int sha1_compress(hash_state *md, unsigned char *buf) + #endif +{ + ulong32 a, b, c, d, e, W[80], i; + + #ifdef LTC_SMALL_CODE + ulong32 t; + #endif + + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD32H(W[i], buf + (4 * i)); + } + + /* copy state */ + a = md->sha1.state[0]; + b = md->sha1.state[1]; + c = md->sha1.state[2]; + d = md->sha1.state[3]; + e = md->sha1.state[4]; + + /* expand it */ + for (i = 16; i < 80; i++) { + W[i] = ROL(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1); + } + + /* compress */ + /* round one */ + #define FF0(a, b, c, d, e, i) e = (ROLc(a, 5) + F0(b, c, d) + e + W[i] + 0x5a827999UL); b = ROLc(b, 30); + #define FF1(a, b, c, d, e, i) e = (ROLc(a, 5) + F1(b, c, d) + e + W[i] + 0x6ed9eba1UL); b = ROLc(b, 30); + #define FF2(a, b, c, d, e, i) e = (ROLc(a, 5) + F2(b, c, d) + e + W[i] + 0x8f1bbcdcUL); b = ROLc(b, 30); + #define FF3(a, b, c, d, e, i) e = (ROLc(a, 5) + F3(b, c, d) + e + W[i] + 0xca62c1d6UL); b = ROLc(b, 30); + + #ifdef LTC_SMALL_CODE + for (i = 0; i < 20; ) { + FF0(a, b, c, d, e, i++); + t = e; + e = d; + d = c; + c = b; + b = a; + a = t; + } + + for ( ; i < 40; ) { + FF1(a, b, c, d, e, i++); + t = e; + e = d; + d = c; + c = b; + b = a; + a = t; + } + + for ( ; i < 60; ) { + FF2(a, b, c, d, e, i++); + t = e; + e = d; + d = c; + c = b; + b = a; + a = t; + } + + for ( ; i < 80; ) { + FF3(a, b, c, d, e, i++); + t = e; + e = d; + d = c; + c = b; + b = a; + a = t; + } + + #else + for (i = 0; i < 20; ) { + FF0(a, b, c, d, e, i++); + FF0(e, a, b, c, d, i++); + FF0(d, e, a, b, c, i++); + FF0(c, d, e, a, b, i++); + FF0(b, c, d, e, a, i++); + } + + /* round two */ + for ( ; i < 40; ) { + FF1(a, b, c, d, e, i++); + FF1(e, a, b, c, d, i++); + FF1(d, e, a, b, c, i++); + FF1(c, d, e, a, b, i++); + FF1(b, c, d, e, a, i++); + } + + /* round three */ + for ( ; i < 60; ) { + FF2(a, b, c, d, e, i++); + FF2(e, a, b, c, d, i++); + FF2(d, e, a, b, c, i++); + FF2(c, d, e, a, b, i++); + FF2(b, c, d, e, a, i++); + } + + /* round four */ + for ( ; i < 80; ) { + FF3(a, b, c, d, e, i++); + FF3(e, a, b, c, d, i++); + FF3(d, e, a, b, c, i++); + FF3(c, d, e, a, b, i++); + FF3(b, c, d, e, a, i++); + } + #endif + + #undef FF0 + #undef FF1 + #undef FF2 + #undef FF3 + + /* store */ + md->sha1.state[0] = md->sha1.state[0] + a; + md->sha1.state[1] = md->sha1.state[1] + b; + md->sha1.state[2] = md->sha1.state[2] + c; + md->sha1.state[3] = md->sha1.state[3] + d; + md->sha1.state[4] = md->sha1.state[4] + e; + + return CRYPT_OK; +} + + #ifdef LTC_CLEAN_STACK +static int sha1_compress(hash_state *md, unsigned char *buf) { + int err; + + err = _sha1_compress(md, buf); + burn_stack(sizeof(ulong32) * 87); + return err; +} + #endif + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful + */ +int sha1_init(hash_state *md) { + LTC_ARGCHK(md != NULL); + md->sha1.state[0] = 0x67452301UL; + md->sha1.state[1] = 0xefcdab89UL; + md->sha1.state[2] = 0x98badcfeUL; + md->sha1.state[3] = 0x10325476UL; + md->sha1.state[4] = 0xc3d2e1f0UL; + md->sha1.curlen = 0; + md->sha1.length = 0; + return CRYPT_OK; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful + */ +HASH_PROCESS(sha1_process, sha1_compress, sha1, 64) + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (20 bytes) + @return CRYPT_OK if successful + */ +int sha1_done(hash_state *md, unsigned char *out) { + int i; + + LTC_ARGCHK(md != NULL); + LTC_ARGCHK(out != NULL); + + if (md->sha1.curlen >= sizeof(md->sha1.buf)) { + return CRYPT_INVALID_ARG; + } + + /* increase the length of the message */ + md->sha1.length += md->sha1.curlen * 8; + + /* append the '1' bit */ + md->sha1.buf[md->sha1.curlen++] = (unsigned char)0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->sha1.curlen > 56) { + while (md->sha1.curlen < 64) { + md->sha1.buf[md->sha1.curlen++] = (unsigned char)0; + } + sha1_compress(md, md->sha1.buf); + md->sha1.curlen = 0; + } + + /* pad upto 56 bytes of zeroes */ + while (md->sha1.curlen < 56) { + md->sha1.buf[md->sha1.curlen++] = (unsigned char)0; + } + + /* store length */ + STORE64H(md->sha1.length, md->sha1.buf + 56); + sha1_compress(md, md->sha1.buf); + + /* copy output */ + for (i = 0; i < 5; i++) { + STORE32H(md->sha1.state[i], out + (4 * i)); + } + #ifdef LTC_CLEAN_STACK + zeromem(md, sizeof(hash_state)); + #endif + return CRYPT_OK; +} + +/** + Self-test the hash + @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled + */ +int sha1_test(void) { + #ifndef LTC_TEST + return CRYPT_NOP; + #else + static const struct { + char *msg; + unsigned char hash[20]; + } tests[] = { + { "abc", + { 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, + 0xba, 0x3e, 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, + 0x9c, 0xd0, 0xd8, 0x9d } }, + { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, + 0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, + 0xE5, 0x46, 0x70, 0xF1 } } + }; + + int i; + unsigned char tmp[20]; + hash_state md; + + for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) { + sha1_init(&md); + sha1_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg)); + sha1_done(&md, tmp); + if (XMEMCMP(tmp, tests[i].hash, 20) != 0) { + return CRYPT_FAIL_TESTVECTOR; + } + } + return CRYPT_OK; + #endif +} +#endif + + + +/* $Source: /cvs/libtom/libtomcrypt/src/hashes/sha1.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2007/05/12 14:25:28 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file sha256.c + LTC_SHA256 by Tom St Denis +*/ + +#ifdef LTC_SHA256 + +const struct ltc_hash_descriptor sha256_desc = +{ + "sha256", + 0, + 32, + 64, + + /* OID */ + { 2, 16, 840, 1, 101, 3, 4, 2, 1, }, + 9, + + &sha256_init, + &sha256_process, + &sha256_done, + &sha256_test, + NULL +}; + +#ifdef LTC_SMALL_CODE +/* the K array */ +static const ulong32 K[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, + 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, + 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, + 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, + 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, + 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, + 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, + 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, + 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; +#endif + +/* Various logical functions */ +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) RORc((x),(n)) +#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) + +/* compress 512-bits */ +#ifdef LTC_CLEAN_STACK +static int _sha256_compress(hash_state * md, unsigned char *buf) +#else +static int sha256_compress(hash_state * md, unsigned char *buf) +#endif +{ + ulong32 S[8], W[64], t0, t1; +#ifdef LTC_SMALL_CODE + ulong32 t; +#endif + int i; + + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->sha256.state[i]; + } + + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD32H(W[i], buf + (4*i)); + } + + /* fill W[16..63] */ + for (i = 16; i < 64; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + } + + /* Compress */ +#ifdef LTC_SMALL_CODE +#define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + for (i = 0; i < 64; ++i) { + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i); + t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; + S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; + } +#else +#define RND(a,b,c,d,e,f,g,h,i,ki) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2); + +#undef RND + +#endif + + /* feedback */ + for (i = 0; i < 8; i++) { + md->sha256.state[i] = md->sha256.state[i] + S[i]; + } + return CRYPT_OK; +} + +#ifdef LTC_CLEAN_STACK +static int sha256_compress(hash_state * md, unsigned char *buf) +{ + int err; + err = _sha256_compress(md, buf); + burn_stack(sizeof(ulong32) * 74); + return err; +} +#endif + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful +*/ +int sha256_init(hash_state * md) +{ + LTC_ARGCHK(md != NULL); + + md->sha256.curlen = 0; + md->sha256.length = 0; + md->sha256.state[0] = 0x6A09E667UL; + md->sha256.state[1] = 0xBB67AE85UL; + md->sha256.state[2] = 0x3C6EF372UL; + md->sha256.state[3] = 0xA54FF53AUL; + md->sha256.state[4] = 0x510E527FUL; + md->sha256.state[5] = 0x9B05688CUL; + md->sha256.state[6] = 0x1F83D9ABUL; + md->sha256.state[7] = 0x5BE0CD19UL; + return CRYPT_OK; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful +*/ +HASH_PROCESS(sha256_process, sha256_compress, sha256, 64) + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (32 bytes) + @return CRYPT_OK if successful +*/ +int sha256_done(hash_state * md, unsigned char *out) +{ + int i; + + LTC_ARGCHK(md != NULL); + LTC_ARGCHK(out != NULL); + + if (md->sha256.curlen >= sizeof(md->sha256.buf)) { + return CRYPT_INVALID_ARG; + } + + + /* increase the length of the message */ + md->sha256.length += md->sha256.curlen * 8; + + /* append the '1' bit */ + md->sha256.buf[md->sha256.curlen++] = (unsigned char)0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->sha256.curlen > 56) { + while (md->sha256.curlen < 64) { + md->sha256.buf[md->sha256.curlen++] = (unsigned char)0; + } + sha256_compress(md, md->sha256.buf); + md->sha256.curlen = 0; + } + + /* pad upto 56 bytes of zeroes */ + while (md->sha256.curlen < 56) { + md->sha256.buf[md->sha256.curlen++] = (unsigned char)0; + } + + /* store length */ + STORE64H(md->sha256.length, md->sha256.buf+56); + sha256_compress(md, md->sha256.buf); + + /* copy output */ + for (i = 0; i < 8; i++) { + STORE32H(md->sha256.state[i], out+(4*i)); + } +#ifdef LTC_CLEAN_STACK + zeromem(md, sizeof(hash_state)); +#endif + return CRYPT_OK; +} + +/** + Self-test the hash + @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled +*/ +int sha256_test(void) +{ + #ifndef LTC_TEST + return CRYPT_NOP; + #else + static const struct { + char *msg; + unsigned char hash[32]; + } tests[] = { + { "abc", + { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad } + }, + { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + { 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, + 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39, + 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, + 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1 } + }, + }; + + int i; + unsigned char tmp[32]; + hash_state md; + + for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) { + sha256_init(&md); + sha256_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg)); + sha256_done(&md, tmp); + if (XMEMCMP(tmp, tests[i].hash, 32) != 0) { + return CRYPT_FAIL_TESTVECTOR; + } + } + return CRYPT_OK; + #endif +} + +#endif + + + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +/** + @param sha384.c + LTC_SHA384 hash included in sha512.c, Tom St Denis +*/ + + + +#if defined(LTC_SHA384) && defined(LTC_SHA512) + +const struct ltc_hash_descriptor sha384_desc = +{ + "sha384", + 4, + 48, + 128, + + /* OID */ + { 2, 16, 840, 1, 101, 3, 4, 2, 2, }, + 9, + + &sha384_init, + &sha512_process, + &sha384_done, + &sha384_test, + NULL +}; + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful +*/ +int sha384_init(hash_state * md) +{ + LTC_ARGCHK(md != NULL); + + md->sha512.curlen = 0; + md->sha512.length = 0; + md->sha512.state[0] = CONST64(0xcbbb9d5dc1059ed8); + md->sha512.state[1] = CONST64(0x629a292a367cd507); + md->sha512.state[2] = CONST64(0x9159015a3070dd17); + md->sha512.state[3] = CONST64(0x152fecd8f70e5939); + md->sha512.state[4] = CONST64(0x67332667ffc00b31); + md->sha512.state[5] = CONST64(0x8eb44a8768581511); + md->sha512.state[6] = CONST64(0xdb0c2e0d64f98fa7); + md->sha512.state[7] = CONST64(0x47b5481dbefa4fa4); + return CRYPT_OK; +} + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (48 bytes) + @return CRYPT_OK if successful +*/ +int sha384_done(hash_state * md, unsigned char *out) +{ + unsigned char buf[64]; + + LTC_ARGCHK(md != NULL); + LTC_ARGCHK(out != NULL); + + if (md->sha512.curlen >= sizeof(md->sha512.buf)) { + return CRYPT_INVALID_ARG; + } + + sha512_done(md, buf); + XMEMCPY(out, buf, 48); +#ifdef LTC_CLEAN_STACK + zeromem(buf, sizeof(buf)); +#endif + return CRYPT_OK; +} + +/** + Self-test the hash + @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled +*/ +int sha384_test(void) +{ + #ifndef LTC_TEST + return CRYPT_NOP; + #else + static const struct { + char *msg; + unsigned char hash[48]; + } tests[] = { + { "abc", + { 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b, + 0xb5, 0xa0, 0x3d, 0x69, 0x9a, 0xc6, 0x50, 0x07, + 0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63, + 0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed, + 0x80, 0x86, 0x07, 0x2b, 0xa1, 0xe7, 0xcc, 0x23, + 0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7 } + }, + { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + { 0x09, 0x33, 0x0c, 0x33, 0xf7, 0x11, 0x47, 0xe8, + 0x3d, 0x19, 0x2f, 0xc7, 0x82, 0xcd, 0x1b, 0x47, + 0x53, 0x11, 0x1b, 0x17, 0x3b, 0x3b, 0x05, 0xd2, + 0x2f, 0xa0, 0x80, 0x86, 0xe3, 0xb0, 0xf7, 0x12, + 0xfc, 0xc7, 0xc7, 0x1a, 0x55, 0x7e, 0x2d, 0xb9, + 0x66, 0xc3, 0xe9, 0xfa, 0x91, 0x74, 0x60, 0x39 } + }, + }; + + int i; + unsigned char tmp[48]; + hash_state md; + + for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) { + sha384_init(&md); + sha384_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg)); + sha384_done(&md, tmp); + if (XMEMCMP(tmp, tests[i].hash, 48) != 0) { + return CRYPT_FAIL_TESTVECTOR; + } + } + return CRYPT_OK; + #endif +} + +#endif /* defined(LTC_SHA384) && defined(LTC_SHA512) */ + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @param sha512.c + LTC_SHA512 by Tom St Denis +*/ + +#ifdef LTC_SHA512 + +const struct ltc_hash_descriptor sha512_desc = +{ + "sha512", + 5, + 64, + 128, + + /* OID */ + { 2, 16, 840, 1, 101, 3, 4, 2, 3, }, + 9, + + &sha512_init, + &sha512_process, + &sha512_done, + &sha512_test, + NULL +}; + +/* the K array */ +static const ulong64 K[80] = { +CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd), +CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc), +CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019), +CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118), +CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe), +CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2), +CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1), +CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694), +CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3), +CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65), +CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483), +CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5), +CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210), +CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4), +CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725), +CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70), +CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926), +CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df), +CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8), +CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b), +CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001), +CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30), +CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910), +CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8), +CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53), +CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8), +CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb), +CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3), +CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60), +CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec), +CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9), +CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b), +CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207), +CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178), +CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6), +CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b), +CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493), +CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c), +CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a), +CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817) +}; + +/* Various logical functions */ +#undef S +#undef R +#undef Sigma0 +#undef Sigma1 +#undef Gamma0 +#undef Gamma1 + +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) ROR64c(x, n) +#define R(x, n) (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)n)) +#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) +#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) +#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) +#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) + +/* compress 1024-bits */ +#ifdef LTC_CLEAN_STACK +static int _sha512_compress(hash_state * md, unsigned char *buf) +#else +static int sha512_compress(hash_state * md, unsigned char *buf) +#endif +{ + ulong64 S[8], W[80], t0, t1; + int i; + + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->sha512.state[i]; + } + + /* copy the state into 1024-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD64H(W[i], buf + (8*i)); + } + + /* fill W[16..79] */ + for (i = 16; i < 80; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + } + + /* Compress */ +#ifdef LTC_SMALL_CODE + for (i = 0; i < 80; i++) { + t0 = S[7] + Sigma1(S[4]) + Ch(S[4], S[5], S[6]) + K[i] + W[i]; + t1 = Sigma0(S[0]) + Maj(S[0], S[1], S[2]); + S[7] = S[6]; + S[6] = S[5]; + S[5] = S[4]; + S[4] = S[3] + t0; + S[3] = S[2]; + S[2] = S[1]; + S[1] = S[0]; + S[0] = t0 + t1; + } +#else +#define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + for (i = 0; i < 80; i += 8) { + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7); + } +#endif + + + /* feedback */ + for (i = 0; i < 8; i++) { + md->sha512.state[i] = md->sha512.state[i] + S[i]; + } + + return CRYPT_OK; +} + +/* compress 1024-bits */ +#ifdef LTC_CLEAN_STACK +static int sha512_compress(hash_state * md, unsigned char *buf) +{ + int err; + err = _sha512_compress(md, buf); + burn_stack(sizeof(ulong64) * 90 + sizeof(int)); + return err; +} +#endif + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful +*/ +int sha512_init(hash_state * md) +{ + LTC_ARGCHK(md != NULL); + md->sha512.curlen = 0; + md->sha512.length = 0; + md->sha512.state[0] = CONST64(0x6a09e667f3bcc908); + md->sha512.state[1] = CONST64(0xbb67ae8584caa73b); + md->sha512.state[2] = CONST64(0x3c6ef372fe94f82b); + md->sha512.state[3] = CONST64(0xa54ff53a5f1d36f1); + md->sha512.state[4] = CONST64(0x510e527fade682d1); + md->sha512.state[5] = CONST64(0x9b05688c2b3e6c1f); + md->sha512.state[6] = CONST64(0x1f83d9abfb41bd6b); + md->sha512.state[7] = CONST64(0x5be0cd19137e2179); + return CRYPT_OK; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful +*/ +HASH_PROCESS(sha512_process, sha512_compress, sha512, 128) + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (64 bytes) + @return CRYPT_OK if successful +*/ +int sha512_done(hash_state * md, unsigned char *out) +{ + int i; + + LTC_ARGCHK(md != NULL); + LTC_ARGCHK(out != NULL); + + if (md->sha512.curlen >= sizeof(md->sha512.buf)) { + return CRYPT_INVALID_ARG; + } + + /* increase the length of the message */ + md->sha512.length += md->sha512.curlen * CONST64(8); + + /* append the '1' bit */ + md->sha512.buf[md->sha512.curlen++] = (unsigned char)0x80; + + /* if the length is currently above 112 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->sha512.curlen > 112) { + while (md->sha512.curlen < 128) { + md->sha512.buf[md->sha512.curlen++] = (unsigned char)0; + } + sha512_compress(md, md->sha512.buf); + md->sha512.curlen = 0; + } + + /* pad upto 120 bytes of zeroes + * note: that from 112 to 120 is the 64 MSB of the length. We assume that you won't hash + * > 2^64 bits of data... :-) + */ + while (md->sha512.curlen < 120) { + md->sha512.buf[md->sha512.curlen++] = (unsigned char)0; + } + + /* store length */ + STORE64H(md->sha512.length, md->sha512.buf+120); + sha512_compress(md, md->sha512.buf); + + /* copy output */ + for (i = 0; i < 8; i++) { + STORE64H(md->sha512.state[i], out+(8*i)); + } +#ifdef LTC_CLEAN_STACK + zeromem(md, sizeof(hash_state)); +#endif + return CRYPT_OK; +} + +/** + Self-test the hash + @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled +*/ +int sha512_test(void) +{ + #ifndef LTC_TEST + return CRYPT_NOP; + #else + static const struct { + char *msg; + unsigned char hash[64]; + } tests[] = { + { "abc", + { 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, + 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31, + 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, + 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, + 0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8, + 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, + 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, + 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f } + }, + { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + { 0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda, + 0x8c, 0xf4, 0xf7, 0x28, 0x14, 0xfc, 0x14, 0x3f, + 0x8f, 0x77, 0x79, 0xc6, 0xeb, 0x9f, 0x7f, 0xa1, + 0x72, 0x99, 0xae, 0xad, 0xb6, 0x88, 0x90, 0x18, + 0x50, 0x1d, 0x28, 0x9e, 0x49, 0x00, 0xf7, 0xe4, + 0x33, 0x1b, 0x99, 0xde, 0xc4, 0xb5, 0x43, 0x3a, + 0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, 0x26, 0x54, + 0x5e, 0x96, 0xe5, 0x5b, 0x87, 0x4b, 0xe9, 0x09 } + }, + }; + + int i; + unsigned char tmp[64]; + hash_state md; + + for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) { + sha512_init(&md); + sha512_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg)); + sha512_done(&md, tmp); + if (XMEMCMP(tmp, tests[i].hash, 64) != 0) { + return CRYPT_FAIL_TESTVECTOR; + } + } + return CRYPT_OK; + #endif +} + +#endif + + + + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file hmac_init.c + HMAC support, initialize state, Tom St Denis/Dobes Vandermeer +*/ + +#ifdef LTC_HMAC + +#define LTC_HMAC_BLOCKSIZE hash_descriptor[hash].blocksize + +/** + Initialize an HMAC context. + @param hmac The HMAC state + @param hash The index of the hash you want to use + @param key The secret key + @param keylen The length of the secret key (octets) + @return CRYPT_OK if successful +*/ +int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, unsigned long keylen) +{ + unsigned char *buf; + unsigned long hashsize; + unsigned long i, z; + int err; + + LTC_ARGCHK(hmac != NULL); + LTC_ARGCHK(key != NULL); + + /* valid hash? */ + if ((err = hash_is_valid(hash)) != CRYPT_OK) { + return err; + } + hmac->hash = hash; + hashsize = hash_descriptor[hash].hashsize; + + /* valid key length? */ + if (keylen == 0) { + return CRYPT_INVALID_KEYSIZE; + } + + /* allocate ram for buf */ + buf = XMALLOC(LTC_HMAC_BLOCKSIZE); + if (buf == NULL) { + return CRYPT_MEM; + } + + /* allocate memory for key */ + hmac->key = XMALLOC(LTC_HMAC_BLOCKSIZE); + if (hmac->key == NULL) { + XFREE(buf); + return CRYPT_MEM; + } + + /* (1) make sure we have a large enough key */ + if(keylen > LTC_HMAC_BLOCKSIZE) { + z = LTC_HMAC_BLOCKSIZE; + if ((err = hash_memory(hash, key, keylen, hmac->key, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + keylen = hashsize; + } else { + XMEMCPY(hmac->key, key, (size_t)keylen); + } + + if(keylen < LTC_HMAC_BLOCKSIZE) { + zeromem((hmac->key) + keylen, (size_t)(LTC_HMAC_BLOCKSIZE - keylen)); + } + + /* Create the initial vector for step (3) */ + for(i=0; i < LTC_HMAC_BLOCKSIZE; i++) { + buf[i] = hmac->key[i] ^ 0x36; + } + + /* Pre-pend that to the hash data */ + if ((err = hash_descriptor[hash].init(&hmac->md)) != CRYPT_OK) { + goto LBL_ERR; + } + + if ((err = hash_descriptor[hash].process(&hmac->md, buf, LTC_HMAC_BLOCKSIZE)) != CRYPT_OK) { + goto LBL_ERR; + } + goto done; +LBL_ERR: + /* free the key since we failed */ + XFREE(hmac->key); +done: +#ifdef LTC_CLEAN_STACK + zeromem(buf, LTC_HMAC_BLOCKSIZE); +#endif + + XFREE(buf); + return err; +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file hmac_process.c + HMAC support, process data, Tom St Denis/Dobes Vandermeer +*/ + +#ifdef LTC_HMAC + +/** + Process data through HMAC + @param hmac The hmac state + @param in The data to send through HMAC + @param inlen The length of the data to HMAC (octets) + @return CRYPT_OK if successful +*/ +int hmac_process(hmac_state *hmac, const unsigned char *in, unsigned long inlen) +{ + int err; + LTC_ARGCHK(hmac != NULL); + LTC_ARGCHK(in != NULL); + if ((err = hash_is_valid(hmac->hash)) != CRYPT_OK) { + return err; + } + return hash_descriptor[hmac->hash].process(&hmac->md, in, inlen); +} + +#endif + + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file hmac_done.c + HMAC support, terminate stream, Tom St Denis/Dobes Vandermeer +*/ + +#ifdef LTC_HMAC + +#define LTC_HMAC_BLOCKSIZE hash_descriptor[hash].blocksize + +/** + Terminate an HMAC session + @param hmac The HMAC state + @param out [out] The destination of the HMAC authentication tag + @param outlen [in/out] The max size and resulting size of the HMAC authentication tag + @return CRYPT_OK if successful +*/ +int hmac_done(hmac_state *hmac, unsigned char *out, unsigned long *outlen) +{ + unsigned char *buf, *isha; + unsigned long hashsize, i; + int hash, err; + + LTC_ARGCHK(hmac != NULL); + LTC_ARGCHK(out != NULL); + + /* test hash */ + hash = hmac->hash; + if((err = hash_is_valid(hash)) != CRYPT_OK) { + return err; + } + + /* get the hash message digest size */ + hashsize = hash_descriptor[hash].hashsize; + + /* allocate buffers */ + buf = XMALLOC(LTC_HMAC_BLOCKSIZE); + isha = XMALLOC(hashsize); + if (buf == NULL || isha == NULL) { + if (buf != NULL) { + XFREE(buf); + } + if (isha != NULL) { + XFREE(isha); + } + return CRYPT_MEM; + } + + /* Get the hash of the first HMAC vector plus the data */ + if ((err = hash_descriptor[hash].done(&hmac->md, isha)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* Create the second HMAC vector vector for step (3) */ + for(i=0; i < LTC_HMAC_BLOCKSIZE; i++) { + buf[i] = hmac->key[i] ^ 0x5C; + } + + /* Now calculate the "outer" hash for step (5), (6), and (7) */ + if ((err = hash_descriptor[hash].init(&hmac->md)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash].process(&hmac->md, buf, LTC_HMAC_BLOCKSIZE)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash].process(&hmac->md, isha, hashsize)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash].done(&hmac->md, buf)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* copy to output */ + for (i = 0; i < hashsize && i < *outlen; i++) { + out[i] = buf[i]; + } + *outlen = i; + + err = CRYPT_OK; +LBL_ERR: + XFREE(hmac->key); +#ifdef LTC_CLEAN_STACK + zeromem(isha, hashsize); + zeromem(buf, hashsize); + zeromem(hmac, sizeof(*hmac)); +#endif + + XFREE(isha); + XFREE(buf); + + return err; +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +#define __LTC_AES_TAB_C__ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +/* The precomputed tables for AES */ +/* +Te0[x] = S [x].[02, 01, 01, 03]; +Te1[x] = S [x].[03, 02, 01, 01]; +Te2[x] = S [x].[01, 03, 02, 01]; +Te3[x] = S [x].[01, 01, 03, 02]; +Te4[x] = S [x].[01, 01, 01, 01]; + +Td0[x] = Si[x].[0e, 09, 0d, 0b]; +Td1[x] = Si[x].[0b, 0e, 09, 0d]; +Td2[x] = Si[x].[0d, 0b, 0e, 09]; +Td3[x] = Si[x].[09, 0d, 0b, 0e]; +Td4[x] = Si[x].[01, 01, 01, 01]; +*/ + +#ifdef __LTC_AES_TAB_C__ + +/** + @file aes_tab.c + AES tables +*/ +static const ulong32 TE0[256] = { + 0xc66363a5UL, 0xf87c7c84UL, 0xee777799UL, 0xf67b7b8dUL, + 0xfff2f20dUL, 0xd66b6bbdUL, 0xde6f6fb1UL, 0x91c5c554UL, + 0x60303050UL, 0x02010103UL, 0xce6767a9UL, 0x562b2b7dUL, + 0xe7fefe19UL, 0xb5d7d762UL, 0x4dababe6UL, 0xec76769aUL, + 0x8fcaca45UL, 0x1f82829dUL, 0x89c9c940UL, 0xfa7d7d87UL, + 0xeffafa15UL, 0xb25959ebUL, 0x8e4747c9UL, 0xfbf0f00bUL, + 0x41adadecUL, 0xb3d4d467UL, 0x5fa2a2fdUL, 0x45afafeaUL, + 0x239c9cbfUL, 0x53a4a4f7UL, 0xe4727296UL, 0x9bc0c05bUL, + 0x75b7b7c2UL, 0xe1fdfd1cUL, 0x3d9393aeUL, 0x4c26266aUL, + 0x6c36365aUL, 0x7e3f3f41UL, 0xf5f7f702UL, 0x83cccc4fUL, + 0x6834345cUL, 0x51a5a5f4UL, 0xd1e5e534UL, 0xf9f1f108UL, + 0xe2717193UL, 0xabd8d873UL, 0x62313153UL, 0x2a15153fUL, + 0x0804040cUL, 0x95c7c752UL, 0x46232365UL, 0x9dc3c35eUL, + 0x30181828UL, 0x379696a1UL, 0x0a05050fUL, 0x2f9a9ab5UL, + 0x0e070709UL, 0x24121236UL, 0x1b80809bUL, 0xdfe2e23dUL, + 0xcdebeb26UL, 0x4e272769UL, 0x7fb2b2cdUL, 0xea75759fUL, + 0x1209091bUL, 0x1d83839eUL, 0x582c2c74UL, 0x341a1a2eUL, + 0x361b1b2dUL, 0xdc6e6eb2UL, 0xb45a5aeeUL, 0x5ba0a0fbUL, + 0xa45252f6UL, 0x763b3b4dUL, 0xb7d6d661UL, 0x7db3b3ceUL, + 0x5229297bUL, 0xdde3e33eUL, 0x5e2f2f71UL, 0x13848497UL, + 0xa65353f5UL, 0xb9d1d168UL, 0x00000000UL, 0xc1eded2cUL, + 0x40202060UL, 0xe3fcfc1fUL, 0x79b1b1c8UL, 0xb65b5bedUL, + 0xd46a6abeUL, 0x8dcbcb46UL, 0x67bebed9UL, 0x7239394bUL, + 0x944a4adeUL, 0x984c4cd4UL, 0xb05858e8UL, 0x85cfcf4aUL, + 0xbbd0d06bUL, 0xc5efef2aUL, 0x4faaaae5UL, 0xedfbfb16UL, + 0x864343c5UL, 0x9a4d4dd7UL, 0x66333355UL, 0x11858594UL, + 0x8a4545cfUL, 0xe9f9f910UL, 0x04020206UL, 0xfe7f7f81UL, + 0xa05050f0UL, 0x783c3c44UL, 0x259f9fbaUL, 0x4ba8a8e3UL, + 0xa25151f3UL, 0x5da3a3feUL, 0x804040c0UL, 0x058f8f8aUL, + 0x3f9292adUL, 0x219d9dbcUL, 0x70383848UL, 0xf1f5f504UL, + 0x63bcbcdfUL, 0x77b6b6c1UL, 0xafdada75UL, 0x42212163UL, + 0x20101030UL, 0xe5ffff1aUL, 0xfdf3f30eUL, 0xbfd2d26dUL, + 0x81cdcd4cUL, 0x180c0c14UL, 0x26131335UL, 0xc3ecec2fUL, + 0xbe5f5fe1UL, 0x359797a2UL, 0x884444ccUL, 0x2e171739UL, + 0x93c4c457UL, 0x55a7a7f2UL, 0xfc7e7e82UL, 0x7a3d3d47UL, + 0xc86464acUL, 0xba5d5de7UL, 0x3219192bUL, 0xe6737395UL, + 0xc06060a0UL, 0x19818198UL, 0x9e4f4fd1UL, 0xa3dcdc7fUL, + 0x44222266UL, 0x542a2a7eUL, 0x3b9090abUL, 0x0b888883UL, + 0x8c4646caUL, 0xc7eeee29UL, 0x6bb8b8d3UL, 0x2814143cUL, + 0xa7dede79UL, 0xbc5e5ee2UL, 0x160b0b1dUL, 0xaddbdb76UL, + 0xdbe0e03bUL, 0x64323256UL, 0x743a3a4eUL, 0x140a0a1eUL, + 0x924949dbUL, 0x0c06060aUL, 0x4824246cUL, 0xb85c5ce4UL, + 0x9fc2c25dUL, 0xbdd3d36eUL, 0x43acacefUL, 0xc46262a6UL, + 0x399191a8UL, 0x319595a4UL, 0xd3e4e437UL, 0xf279798bUL, + 0xd5e7e732UL, 0x8bc8c843UL, 0x6e373759UL, 0xda6d6db7UL, + 0x018d8d8cUL, 0xb1d5d564UL, 0x9c4e4ed2UL, 0x49a9a9e0UL, + 0xd86c6cb4UL, 0xac5656faUL, 0xf3f4f407UL, 0xcfeaea25UL, + 0xca6565afUL, 0xf47a7a8eUL, 0x47aeaee9UL, 0x10080818UL, + 0x6fbabad5UL, 0xf0787888UL, 0x4a25256fUL, 0x5c2e2e72UL, + 0x381c1c24UL, 0x57a6a6f1UL, 0x73b4b4c7UL, 0x97c6c651UL, + 0xcbe8e823UL, 0xa1dddd7cUL, 0xe874749cUL, 0x3e1f1f21UL, + 0x964b4bddUL, 0x61bdbddcUL, 0x0d8b8b86UL, 0x0f8a8a85UL, + 0xe0707090UL, 0x7c3e3e42UL, 0x71b5b5c4UL, 0xcc6666aaUL, + 0x904848d8UL, 0x06030305UL, 0xf7f6f601UL, 0x1c0e0e12UL, + 0xc26161a3UL, 0x6a35355fUL, 0xae5757f9UL, 0x69b9b9d0UL, + 0x17868691UL, 0x99c1c158UL, 0x3a1d1d27UL, 0x279e9eb9UL, + 0xd9e1e138UL, 0xebf8f813UL, 0x2b9898b3UL, 0x22111133UL, + 0xd26969bbUL, 0xa9d9d970UL, 0x078e8e89UL, 0x339494a7UL, + 0x2d9b9bb6UL, 0x3c1e1e22UL, 0x15878792UL, 0xc9e9e920UL, + 0x87cece49UL, 0xaa5555ffUL, 0x50282878UL, 0xa5dfdf7aUL, + 0x038c8c8fUL, 0x59a1a1f8UL, 0x09898980UL, 0x1a0d0d17UL, + 0x65bfbfdaUL, 0xd7e6e631UL, 0x844242c6UL, 0xd06868b8UL, + 0x824141c3UL, 0x299999b0UL, 0x5a2d2d77UL, 0x1e0f0f11UL, + 0x7bb0b0cbUL, 0xa85454fcUL, 0x6dbbbbd6UL, 0x2c16163aUL, +}; + +#ifndef PELI_TAB +static const ulong32 Te4[256] = { + 0x63636363UL, 0x7c7c7c7cUL, 0x77777777UL, 0x7b7b7b7bUL, + 0xf2f2f2f2UL, 0x6b6b6b6bUL, 0x6f6f6f6fUL, 0xc5c5c5c5UL, + 0x30303030UL, 0x01010101UL, 0x67676767UL, 0x2b2b2b2bUL, + 0xfefefefeUL, 0xd7d7d7d7UL, 0xababababUL, 0x76767676UL, + 0xcacacacaUL, 0x82828282UL, 0xc9c9c9c9UL, 0x7d7d7d7dUL, + 0xfafafafaUL, 0x59595959UL, 0x47474747UL, 0xf0f0f0f0UL, + 0xadadadadUL, 0xd4d4d4d4UL, 0xa2a2a2a2UL, 0xafafafafUL, + 0x9c9c9c9cUL, 0xa4a4a4a4UL, 0x72727272UL, 0xc0c0c0c0UL, + 0xb7b7b7b7UL, 0xfdfdfdfdUL, 0x93939393UL, 0x26262626UL, + 0x36363636UL, 0x3f3f3f3fUL, 0xf7f7f7f7UL, 0xccccccccUL, + 0x34343434UL, 0xa5a5a5a5UL, 0xe5e5e5e5UL, 0xf1f1f1f1UL, + 0x71717171UL, 0xd8d8d8d8UL, 0x31313131UL, 0x15151515UL, + 0x04040404UL, 0xc7c7c7c7UL, 0x23232323UL, 0xc3c3c3c3UL, + 0x18181818UL, 0x96969696UL, 0x05050505UL, 0x9a9a9a9aUL, + 0x07070707UL, 0x12121212UL, 0x80808080UL, 0xe2e2e2e2UL, + 0xebebebebUL, 0x27272727UL, 0xb2b2b2b2UL, 0x75757575UL, + 0x09090909UL, 0x83838383UL, 0x2c2c2c2cUL, 0x1a1a1a1aUL, + 0x1b1b1b1bUL, 0x6e6e6e6eUL, 0x5a5a5a5aUL, 0xa0a0a0a0UL, + 0x52525252UL, 0x3b3b3b3bUL, 0xd6d6d6d6UL, 0xb3b3b3b3UL, + 0x29292929UL, 0xe3e3e3e3UL, 0x2f2f2f2fUL, 0x84848484UL, + 0x53535353UL, 0xd1d1d1d1UL, 0x00000000UL, 0xededededUL, + 0x20202020UL, 0xfcfcfcfcUL, 0xb1b1b1b1UL, 0x5b5b5b5bUL, + 0x6a6a6a6aUL, 0xcbcbcbcbUL, 0xbebebebeUL, 0x39393939UL, + 0x4a4a4a4aUL, 0x4c4c4c4cUL, 0x58585858UL, 0xcfcfcfcfUL, + 0xd0d0d0d0UL, 0xefefefefUL, 0xaaaaaaaaUL, 0xfbfbfbfbUL, + 0x43434343UL, 0x4d4d4d4dUL, 0x33333333UL, 0x85858585UL, + 0x45454545UL, 0xf9f9f9f9UL, 0x02020202UL, 0x7f7f7f7fUL, + 0x50505050UL, 0x3c3c3c3cUL, 0x9f9f9f9fUL, 0xa8a8a8a8UL, + 0x51515151UL, 0xa3a3a3a3UL, 0x40404040UL, 0x8f8f8f8fUL, + 0x92929292UL, 0x9d9d9d9dUL, 0x38383838UL, 0xf5f5f5f5UL, + 0xbcbcbcbcUL, 0xb6b6b6b6UL, 0xdadadadaUL, 0x21212121UL, + 0x10101010UL, 0xffffffffUL, 0xf3f3f3f3UL, 0xd2d2d2d2UL, + 0xcdcdcdcdUL, 0x0c0c0c0cUL, 0x13131313UL, 0xececececUL, + 0x5f5f5f5fUL, 0x97979797UL, 0x44444444UL, 0x17171717UL, + 0xc4c4c4c4UL, 0xa7a7a7a7UL, 0x7e7e7e7eUL, 0x3d3d3d3dUL, + 0x64646464UL, 0x5d5d5d5dUL, 0x19191919UL, 0x73737373UL, + 0x60606060UL, 0x81818181UL, 0x4f4f4f4fUL, 0xdcdcdcdcUL, + 0x22222222UL, 0x2a2a2a2aUL, 0x90909090UL, 0x88888888UL, + 0x46464646UL, 0xeeeeeeeeUL, 0xb8b8b8b8UL, 0x14141414UL, + 0xdedededeUL, 0x5e5e5e5eUL, 0x0b0b0b0bUL, 0xdbdbdbdbUL, + 0xe0e0e0e0UL, 0x32323232UL, 0x3a3a3a3aUL, 0x0a0a0a0aUL, + 0x49494949UL, 0x06060606UL, 0x24242424UL, 0x5c5c5c5cUL, + 0xc2c2c2c2UL, 0xd3d3d3d3UL, 0xacacacacUL, 0x62626262UL, + 0x91919191UL, 0x95959595UL, 0xe4e4e4e4UL, 0x79797979UL, + 0xe7e7e7e7UL, 0xc8c8c8c8UL, 0x37373737UL, 0x6d6d6d6dUL, + 0x8d8d8d8dUL, 0xd5d5d5d5UL, 0x4e4e4e4eUL, 0xa9a9a9a9UL, + 0x6c6c6c6cUL, 0x56565656UL, 0xf4f4f4f4UL, 0xeaeaeaeaUL, + 0x65656565UL, 0x7a7a7a7aUL, 0xaeaeaeaeUL, 0x08080808UL, + 0xbabababaUL, 0x78787878UL, 0x25252525UL, 0x2e2e2e2eUL, + 0x1c1c1c1cUL, 0xa6a6a6a6UL, 0xb4b4b4b4UL, 0xc6c6c6c6UL, + 0xe8e8e8e8UL, 0xddddddddUL, 0x74747474UL, 0x1f1f1f1fUL, + 0x4b4b4b4bUL, 0xbdbdbdbdUL, 0x8b8b8b8bUL, 0x8a8a8a8aUL, + 0x70707070UL, 0x3e3e3e3eUL, 0xb5b5b5b5UL, 0x66666666UL, + 0x48484848UL, 0x03030303UL, 0xf6f6f6f6UL, 0x0e0e0e0eUL, + 0x61616161UL, 0x35353535UL, 0x57575757UL, 0xb9b9b9b9UL, + 0x86868686UL, 0xc1c1c1c1UL, 0x1d1d1d1dUL, 0x9e9e9e9eUL, + 0xe1e1e1e1UL, 0xf8f8f8f8UL, 0x98989898UL, 0x11111111UL, + 0x69696969UL, 0xd9d9d9d9UL, 0x8e8e8e8eUL, 0x94949494UL, + 0x9b9b9b9bUL, 0x1e1e1e1eUL, 0x87878787UL, 0xe9e9e9e9UL, + 0xcecececeUL, 0x55555555UL, 0x28282828UL, 0xdfdfdfdfUL, + 0x8c8c8c8cUL, 0xa1a1a1a1UL, 0x89898989UL, 0x0d0d0d0dUL, + 0xbfbfbfbfUL, 0xe6e6e6e6UL, 0x42424242UL, 0x68686868UL, + 0x41414141UL, 0x99999999UL, 0x2d2d2d2dUL, 0x0f0f0f0fUL, + 0xb0b0b0b0UL, 0x54545454UL, 0xbbbbbbbbUL, 0x16161616UL, +}; +#endif + +#ifndef ENCRYPT_ONLY + +static const ulong32 TD0[256] = { + 0x51f4a750UL, 0x7e416553UL, 0x1a17a4c3UL, 0x3a275e96UL, + 0x3bab6bcbUL, 0x1f9d45f1UL, 0xacfa58abUL, 0x4be30393UL, + 0x2030fa55UL, 0xad766df6UL, 0x88cc7691UL, 0xf5024c25UL, + 0x4fe5d7fcUL, 0xc52acbd7UL, 0x26354480UL, 0xb562a38fUL, + 0xdeb15a49UL, 0x25ba1b67UL, 0x45ea0e98UL, 0x5dfec0e1UL, + 0xc32f7502UL, 0x814cf012UL, 0x8d4697a3UL, 0x6bd3f9c6UL, + 0x038f5fe7UL, 0x15929c95UL, 0xbf6d7aebUL, 0x955259daUL, + 0xd4be832dUL, 0x587421d3UL, 0x49e06929UL, 0x8ec9c844UL, + 0x75c2896aUL, 0xf48e7978UL, 0x99583e6bUL, 0x27b971ddUL, + 0xbee14fb6UL, 0xf088ad17UL, 0xc920ac66UL, 0x7dce3ab4UL, + 0x63df4a18UL, 0xe51a3182UL, 0x97513360UL, 0x62537f45UL, + 0xb16477e0UL, 0xbb6bae84UL, 0xfe81a01cUL, 0xf9082b94UL, + 0x70486858UL, 0x8f45fd19UL, 0x94de6c87UL, 0x527bf8b7UL, + 0xab73d323UL, 0x724b02e2UL, 0xe31f8f57UL, 0x6655ab2aUL, + 0xb2eb2807UL, 0x2fb5c203UL, 0x86c57b9aUL, 0xd33708a5UL, + 0x302887f2UL, 0x23bfa5b2UL, 0x02036abaUL, 0xed16825cUL, + 0x8acf1c2bUL, 0xa779b492UL, 0xf307f2f0UL, 0x4e69e2a1UL, + 0x65daf4cdUL, 0x0605bed5UL, 0xd134621fUL, 0xc4a6fe8aUL, + 0x342e539dUL, 0xa2f355a0UL, 0x058ae132UL, 0xa4f6eb75UL, + 0x0b83ec39UL, 0x4060efaaUL, 0x5e719f06UL, 0xbd6e1051UL, + 0x3e218af9UL, 0x96dd063dUL, 0xdd3e05aeUL, 0x4de6bd46UL, + 0x91548db5UL, 0x71c45d05UL, 0x0406d46fUL, 0x605015ffUL, + 0x1998fb24UL, 0xd6bde997UL, 0x894043ccUL, 0x67d99e77UL, + 0xb0e842bdUL, 0x07898b88UL, 0xe7195b38UL, 0x79c8eedbUL, + 0xa17c0a47UL, 0x7c420fe9UL, 0xf8841ec9UL, 0x00000000UL, + 0x09808683UL, 0x322bed48UL, 0x1e1170acUL, 0x6c5a724eUL, + 0xfd0efffbUL, 0x0f853856UL, 0x3daed51eUL, 0x362d3927UL, + 0x0a0fd964UL, 0x685ca621UL, 0x9b5b54d1UL, 0x24362e3aUL, + 0x0c0a67b1UL, 0x9357e70fUL, 0xb4ee96d2UL, 0x1b9b919eUL, + 0x80c0c54fUL, 0x61dc20a2UL, 0x5a774b69UL, 0x1c121a16UL, + 0xe293ba0aUL, 0xc0a02ae5UL, 0x3c22e043UL, 0x121b171dUL, + 0x0e090d0bUL, 0xf28bc7adUL, 0x2db6a8b9UL, 0x141ea9c8UL, + 0x57f11985UL, 0xaf75074cUL, 0xee99ddbbUL, 0xa37f60fdUL, + 0xf701269fUL, 0x5c72f5bcUL, 0x44663bc5UL, 0x5bfb7e34UL, + 0x8b432976UL, 0xcb23c6dcUL, 0xb6edfc68UL, 0xb8e4f163UL, + 0xd731dccaUL, 0x42638510UL, 0x13972240UL, 0x84c61120UL, + 0x854a247dUL, 0xd2bb3df8UL, 0xaef93211UL, 0xc729a16dUL, + 0x1d9e2f4bUL, 0xdcb230f3UL, 0x0d8652ecUL, 0x77c1e3d0UL, + 0x2bb3166cUL, 0xa970b999UL, 0x119448faUL, 0x47e96422UL, + 0xa8fc8cc4UL, 0xa0f03f1aUL, 0x567d2cd8UL, 0x223390efUL, + 0x87494ec7UL, 0xd938d1c1UL, 0x8ccaa2feUL, 0x98d40b36UL, + 0xa6f581cfUL, 0xa57ade28UL, 0xdab78e26UL, 0x3fadbfa4UL, + 0x2c3a9de4UL, 0x5078920dUL, 0x6a5fcc9bUL, 0x547e4662UL, + 0xf68d13c2UL, 0x90d8b8e8UL, 0x2e39f75eUL, 0x82c3aff5UL, + 0x9f5d80beUL, 0x69d0937cUL, 0x6fd52da9UL, 0xcf2512b3UL, + 0xc8ac993bUL, 0x10187da7UL, 0xe89c636eUL, 0xdb3bbb7bUL, + 0xcd267809UL, 0x6e5918f4UL, 0xec9ab701UL, 0x834f9aa8UL, + 0xe6956e65UL, 0xaaffe67eUL, 0x21bccf08UL, 0xef15e8e6UL, + 0xbae79bd9UL, 0x4a6f36ceUL, 0xea9f09d4UL, 0x29b07cd6UL, + 0x31a4b2afUL, 0x2a3f2331UL, 0xc6a59430UL, 0x35a266c0UL, + 0x744ebc37UL, 0xfc82caa6UL, 0xe090d0b0UL, 0x33a7d815UL, + 0xf104984aUL, 0x41ecdaf7UL, 0x7fcd500eUL, 0x1791f62fUL, + 0x764dd68dUL, 0x43efb04dUL, 0xccaa4d54UL, 0xe49604dfUL, + 0x9ed1b5e3UL, 0x4c6a881bUL, 0xc12c1fb8UL, 0x4665517fUL, + 0x9d5eea04UL, 0x018c355dUL, 0xfa877473UL, 0xfb0b412eUL, + 0xb3671d5aUL, 0x92dbd252UL, 0xe9105633UL, 0x6dd64713UL, + 0x9ad7618cUL, 0x37a10c7aUL, 0x59f8148eUL, 0xeb133c89UL, + 0xcea927eeUL, 0xb761c935UL, 0xe11ce5edUL, 0x7a47b13cUL, + 0x9cd2df59UL, 0x55f2733fUL, 0x1814ce79UL, 0x73c737bfUL, + 0x53f7cdeaUL, 0x5ffdaa5bUL, 0xdf3d6f14UL, 0x7844db86UL, + 0xcaaff381UL, 0xb968c43eUL, 0x3824342cUL, 0xc2a3405fUL, + 0x161dc372UL, 0xbce2250cUL, 0x283c498bUL, 0xff0d9541UL, + 0x39a80171UL, 0x080cb3deUL, 0xd8b4e49cUL, 0x6456c190UL, + 0x7bcb8461UL, 0xd532b670UL, 0x486c5c74UL, 0xd0b85742UL, +}; + +static const ulong32 Td4[256] = { + 0x52525252UL, 0x09090909UL, 0x6a6a6a6aUL, 0xd5d5d5d5UL, + 0x30303030UL, 0x36363636UL, 0xa5a5a5a5UL, 0x38383838UL, + 0xbfbfbfbfUL, 0x40404040UL, 0xa3a3a3a3UL, 0x9e9e9e9eUL, + 0x81818181UL, 0xf3f3f3f3UL, 0xd7d7d7d7UL, 0xfbfbfbfbUL, + 0x7c7c7c7cUL, 0xe3e3e3e3UL, 0x39393939UL, 0x82828282UL, + 0x9b9b9b9bUL, 0x2f2f2f2fUL, 0xffffffffUL, 0x87878787UL, + 0x34343434UL, 0x8e8e8e8eUL, 0x43434343UL, 0x44444444UL, + 0xc4c4c4c4UL, 0xdedededeUL, 0xe9e9e9e9UL, 0xcbcbcbcbUL, + 0x54545454UL, 0x7b7b7b7bUL, 0x94949494UL, 0x32323232UL, + 0xa6a6a6a6UL, 0xc2c2c2c2UL, 0x23232323UL, 0x3d3d3d3dUL, + 0xeeeeeeeeUL, 0x4c4c4c4cUL, 0x95959595UL, 0x0b0b0b0bUL, + 0x42424242UL, 0xfafafafaUL, 0xc3c3c3c3UL, 0x4e4e4e4eUL, + 0x08080808UL, 0x2e2e2e2eUL, 0xa1a1a1a1UL, 0x66666666UL, + 0x28282828UL, 0xd9d9d9d9UL, 0x24242424UL, 0xb2b2b2b2UL, + 0x76767676UL, 0x5b5b5b5bUL, 0xa2a2a2a2UL, 0x49494949UL, + 0x6d6d6d6dUL, 0x8b8b8b8bUL, 0xd1d1d1d1UL, 0x25252525UL, + 0x72727272UL, 0xf8f8f8f8UL, 0xf6f6f6f6UL, 0x64646464UL, + 0x86868686UL, 0x68686868UL, 0x98989898UL, 0x16161616UL, + 0xd4d4d4d4UL, 0xa4a4a4a4UL, 0x5c5c5c5cUL, 0xccccccccUL, + 0x5d5d5d5dUL, 0x65656565UL, 0xb6b6b6b6UL, 0x92929292UL, + 0x6c6c6c6cUL, 0x70707070UL, 0x48484848UL, 0x50505050UL, + 0xfdfdfdfdUL, 0xededededUL, 0xb9b9b9b9UL, 0xdadadadaUL, + 0x5e5e5e5eUL, 0x15151515UL, 0x46464646UL, 0x57575757UL, + 0xa7a7a7a7UL, 0x8d8d8d8dUL, 0x9d9d9d9dUL, 0x84848484UL, + 0x90909090UL, 0xd8d8d8d8UL, 0xababababUL, 0x00000000UL, + 0x8c8c8c8cUL, 0xbcbcbcbcUL, 0xd3d3d3d3UL, 0x0a0a0a0aUL, + 0xf7f7f7f7UL, 0xe4e4e4e4UL, 0x58585858UL, 0x05050505UL, + 0xb8b8b8b8UL, 0xb3b3b3b3UL, 0x45454545UL, 0x06060606UL, + 0xd0d0d0d0UL, 0x2c2c2c2cUL, 0x1e1e1e1eUL, 0x8f8f8f8fUL, + 0xcacacacaUL, 0x3f3f3f3fUL, 0x0f0f0f0fUL, 0x02020202UL, + 0xc1c1c1c1UL, 0xafafafafUL, 0xbdbdbdbdUL, 0x03030303UL, + 0x01010101UL, 0x13131313UL, 0x8a8a8a8aUL, 0x6b6b6b6bUL, + 0x3a3a3a3aUL, 0x91919191UL, 0x11111111UL, 0x41414141UL, + 0x4f4f4f4fUL, 0x67676767UL, 0xdcdcdcdcUL, 0xeaeaeaeaUL, + 0x97979797UL, 0xf2f2f2f2UL, 0xcfcfcfcfUL, 0xcecececeUL, + 0xf0f0f0f0UL, 0xb4b4b4b4UL, 0xe6e6e6e6UL, 0x73737373UL, + 0x96969696UL, 0xacacacacUL, 0x74747474UL, 0x22222222UL, + 0xe7e7e7e7UL, 0xadadadadUL, 0x35353535UL, 0x85858585UL, + 0xe2e2e2e2UL, 0xf9f9f9f9UL, 0x37373737UL, 0xe8e8e8e8UL, + 0x1c1c1c1cUL, 0x75757575UL, 0xdfdfdfdfUL, 0x6e6e6e6eUL, + 0x47474747UL, 0xf1f1f1f1UL, 0x1a1a1a1aUL, 0x71717171UL, + 0x1d1d1d1dUL, 0x29292929UL, 0xc5c5c5c5UL, 0x89898989UL, + 0x6f6f6f6fUL, 0xb7b7b7b7UL, 0x62626262UL, 0x0e0e0e0eUL, + 0xaaaaaaaaUL, 0x18181818UL, 0xbebebebeUL, 0x1b1b1b1bUL, + 0xfcfcfcfcUL, 0x56565656UL, 0x3e3e3e3eUL, 0x4b4b4b4bUL, + 0xc6c6c6c6UL, 0xd2d2d2d2UL, 0x79797979UL, 0x20202020UL, + 0x9a9a9a9aUL, 0xdbdbdbdbUL, 0xc0c0c0c0UL, 0xfefefefeUL, + 0x78787878UL, 0xcdcdcdcdUL, 0x5a5a5a5aUL, 0xf4f4f4f4UL, + 0x1f1f1f1fUL, 0xddddddddUL, 0xa8a8a8a8UL, 0x33333333UL, + 0x88888888UL, 0x07070707UL, 0xc7c7c7c7UL, 0x31313131UL, + 0xb1b1b1b1UL, 0x12121212UL, 0x10101010UL, 0x59595959UL, + 0x27272727UL, 0x80808080UL, 0xececececUL, 0x5f5f5f5fUL, + 0x60606060UL, 0x51515151UL, 0x7f7f7f7fUL, 0xa9a9a9a9UL, + 0x19191919UL, 0xb5b5b5b5UL, 0x4a4a4a4aUL, 0x0d0d0d0dUL, + 0x2d2d2d2dUL, 0xe5e5e5e5UL, 0x7a7a7a7aUL, 0x9f9f9f9fUL, + 0x93939393UL, 0xc9c9c9c9UL, 0x9c9c9c9cUL, 0xefefefefUL, + 0xa0a0a0a0UL, 0xe0e0e0e0UL, 0x3b3b3b3bUL, 0x4d4d4d4dUL, + 0xaeaeaeaeUL, 0x2a2a2a2aUL, 0xf5f5f5f5UL, 0xb0b0b0b0UL, + 0xc8c8c8c8UL, 0xebebebebUL, 0xbbbbbbbbUL, 0x3c3c3c3cUL, + 0x83838383UL, 0x53535353UL, 0x99999999UL, 0x61616161UL, + 0x17171717UL, 0x2b2b2b2bUL, 0x04040404UL, 0x7e7e7e7eUL, + 0xbabababaUL, 0x77777777UL, 0xd6d6d6d6UL, 0x26262626UL, + 0xe1e1e1e1UL, 0x69696969UL, 0x14141414UL, 0x63636363UL, + 0x55555555UL, 0x21212121UL, 0x0c0c0c0cUL, 0x7d7d7d7dUL, +}; + +#endif /* ENCRYPT_ONLY */ + +#ifdef LTC_SMALL_CODE + +#define Te0(x) TE0[x] +#define Te1(x) RORc(TE0[x], 8) +#define Te2(x) RORc(TE0[x], 16) +#define Te3(x) RORc(TE0[x], 24) + +#define Td0(x) TD0[x] +#define Td1(x) RORc(TD0[x], 8) +#define Td2(x) RORc(TD0[x], 16) +#define Td3(x) RORc(TD0[x], 24) + +#define Te4_0 0x000000FF & Te4 +#define Te4_1 0x0000FF00 & Te4 +#define Te4_2 0x00FF0000 & Te4 +#define Te4_3 0xFF000000 & Te4 + +#else + +#define Te0(x) TE0[x] +#define Te1(x) TE1[x] +#define Te2(x) TE2[x] +#define Te3(x) TE3[x] + +#define Td0(x) TD0[x] +#define Td1(x) TD1[x] +#define Td2(x) TD2[x] +#define Td3(x) TD3[x] + +static const ulong32 TE1[256] = { + 0xa5c66363UL, 0x84f87c7cUL, 0x99ee7777UL, 0x8df67b7bUL, + 0x0dfff2f2UL, 0xbdd66b6bUL, 0xb1de6f6fUL, 0x5491c5c5UL, + 0x50603030UL, 0x03020101UL, 0xa9ce6767UL, 0x7d562b2bUL, + 0x19e7fefeUL, 0x62b5d7d7UL, 0xe64dababUL, 0x9aec7676UL, + 0x458fcacaUL, 0x9d1f8282UL, 0x4089c9c9UL, 0x87fa7d7dUL, + 0x15effafaUL, 0xebb25959UL, 0xc98e4747UL, 0x0bfbf0f0UL, + 0xec41adadUL, 0x67b3d4d4UL, 0xfd5fa2a2UL, 0xea45afafUL, + 0xbf239c9cUL, 0xf753a4a4UL, 0x96e47272UL, 0x5b9bc0c0UL, + 0xc275b7b7UL, 0x1ce1fdfdUL, 0xae3d9393UL, 0x6a4c2626UL, + 0x5a6c3636UL, 0x417e3f3fUL, 0x02f5f7f7UL, 0x4f83ccccUL, + 0x5c683434UL, 0xf451a5a5UL, 0x34d1e5e5UL, 0x08f9f1f1UL, + 0x93e27171UL, 0x73abd8d8UL, 0x53623131UL, 0x3f2a1515UL, + 0x0c080404UL, 0x5295c7c7UL, 0x65462323UL, 0x5e9dc3c3UL, + 0x28301818UL, 0xa1379696UL, 0x0f0a0505UL, 0xb52f9a9aUL, + 0x090e0707UL, 0x36241212UL, 0x9b1b8080UL, 0x3ddfe2e2UL, + 0x26cdebebUL, 0x694e2727UL, 0xcd7fb2b2UL, 0x9fea7575UL, + 0x1b120909UL, 0x9e1d8383UL, 0x74582c2cUL, 0x2e341a1aUL, + 0x2d361b1bUL, 0xb2dc6e6eUL, 0xeeb45a5aUL, 0xfb5ba0a0UL, + 0xf6a45252UL, 0x4d763b3bUL, 0x61b7d6d6UL, 0xce7db3b3UL, + 0x7b522929UL, 0x3edde3e3UL, 0x715e2f2fUL, 0x97138484UL, + 0xf5a65353UL, 0x68b9d1d1UL, 0x00000000UL, 0x2cc1ededUL, + 0x60402020UL, 0x1fe3fcfcUL, 0xc879b1b1UL, 0xedb65b5bUL, + 0xbed46a6aUL, 0x468dcbcbUL, 0xd967bebeUL, 0x4b723939UL, + 0xde944a4aUL, 0xd4984c4cUL, 0xe8b05858UL, 0x4a85cfcfUL, + 0x6bbbd0d0UL, 0x2ac5efefUL, 0xe54faaaaUL, 0x16edfbfbUL, + 0xc5864343UL, 0xd79a4d4dUL, 0x55663333UL, 0x94118585UL, + 0xcf8a4545UL, 0x10e9f9f9UL, 0x06040202UL, 0x81fe7f7fUL, + 0xf0a05050UL, 0x44783c3cUL, 0xba259f9fUL, 0xe34ba8a8UL, + 0xf3a25151UL, 0xfe5da3a3UL, 0xc0804040UL, 0x8a058f8fUL, + 0xad3f9292UL, 0xbc219d9dUL, 0x48703838UL, 0x04f1f5f5UL, + 0xdf63bcbcUL, 0xc177b6b6UL, 0x75afdadaUL, 0x63422121UL, + 0x30201010UL, 0x1ae5ffffUL, 0x0efdf3f3UL, 0x6dbfd2d2UL, + 0x4c81cdcdUL, 0x14180c0cUL, 0x35261313UL, 0x2fc3ececUL, + 0xe1be5f5fUL, 0xa2359797UL, 0xcc884444UL, 0x392e1717UL, + 0x5793c4c4UL, 0xf255a7a7UL, 0x82fc7e7eUL, 0x477a3d3dUL, + 0xacc86464UL, 0xe7ba5d5dUL, 0x2b321919UL, 0x95e67373UL, + 0xa0c06060UL, 0x98198181UL, 0xd19e4f4fUL, 0x7fa3dcdcUL, + 0x66442222UL, 0x7e542a2aUL, 0xab3b9090UL, 0x830b8888UL, + 0xca8c4646UL, 0x29c7eeeeUL, 0xd36bb8b8UL, 0x3c281414UL, + 0x79a7dedeUL, 0xe2bc5e5eUL, 0x1d160b0bUL, 0x76addbdbUL, + 0x3bdbe0e0UL, 0x56643232UL, 0x4e743a3aUL, 0x1e140a0aUL, + 0xdb924949UL, 0x0a0c0606UL, 0x6c482424UL, 0xe4b85c5cUL, + 0x5d9fc2c2UL, 0x6ebdd3d3UL, 0xef43acacUL, 0xa6c46262UL, + 0xa8399191UL, 0xa4319595UL, 0x37d3e4e4UL, 0x8bf27979UL, + 0x32d5e7e7UL, 0x438bc8c8UL, 0x596e3737UL, 0xb7da6d6dUL, + 0x8c018d8dUL, 0x64b1d5d5UL, 0xd29c4e4eUL, 0xe049a9a9UL, + 0xb4d86c6cUL, 0xfaac5656UL, 0x07f3f4f4UL, 0x25cfeaeaUL, + 0xafca6565UL, 0x8ef47a7aUL, 0xe947aeaeUL, 0x18100808UL, + 0xd56fbabaUL, 0x88f07878UL, 0x6f4a2525UL, 0x725c2e2eUL, + 0x24381c1cUL, 0xf157a6a6UL, 0xc773b4b4UL, 0x5197c6c6UL, + 0x23cbe8e8UL, 0x7ca1ddddUL, 0x9ce87474UL, 0x213e1f1fUL, + 0xdd964b4bUL, 0xdc61bdbdUL, 0x860d8b8bUL, 0x850f8a8aUL, + 0x90e07070UL, 0x427c3e3eUL, 0xc471b5b5UL, 0xaacc6666UL, + 0xd8904848UL, 0x05060303UL, 0x01f7f6f6UL, 0x121c0e0eUL, + 0xa3c26161UL, 0x5f6a3535UL, 0xf9ae5757UL, 0xd069b9b9UL, + 0x91178686UL, 0x5899c1c1UL, 0x273a1d1dUL, 0xb9279e9eUL, + 0x38d9e1e1UL, 0x13ebf8f8UL, 0xb32b9898UL, 0x33221111UL, + 0xbbd26969UL, 0x70a9d9d9UL, 0x89078e8eUL, 0xa7339494UL, + 0xb62d9b9bUL, 0x223c1e1eUL, 0x92158787UL, 0x20c9e9e9UL, + 0x4987ceceUL, 0xffaa5555UL, 0x78502828UL, 0x7aa5dfdfUL, + 0x8f038c8cUL, 0xf859a1a1UL, 0x80098989UL, 0x171a0d0dUL, + 0xda65bfbfUL, 0x31d7e6e6UL, 0xc6844242UL, 0xb8d06868UL, + 0xc3824141UL, 0xb0299999UL, 0x775a2d2dUL, 0x111e0f0fUL, + 0xcb7bb0b0UL, 0xfca85454UL, 0xd66dbbbbUL, 0x3a2c1616UL, +}; +static const ulong32 TE2[256] = { + 0x63a5c663UL, 0x7c84f87cUL, 0x7799ee77UL, 0x7b8df67bUL, + 0xf20dfff2UL, 0x6bbdd66bUL, 0x6fb1de6fUL, 0xc55491c5UL, + 0x30506030UL, 0x01030201UL, 0x67a9ce67UL, 0x2b7d562bUL, + 0xfe19e7feUL, 0xd762b5d7UL, 0xabe64dabUL, 0x769aec76UL, + 0xca458fcaUL, 0x829d1f82UL, 0xc94089c9UL, 0x7d87fa7dUL, + 0xfa15effaUL, 0x59ebb259UL, 0x47c98e47UL, 0xf00bfbf0UL, + 0xadec41adUL, 0xd467b3d4UL, 0xa2fd5fa2UL, 0xafea45afUL, + 0x9cbf239cUL, 0xa4f753a4UL, 0x7296e472UL, 0xc05b9bc0UL, + 0xb7c275b7UL, 0xfd1ce1fdUL, 0x93ae3d93UL, 0x266a4c26UL, + 0x365a6c36UL, 0x3f417e3fUL, 0xf702f5f7UL, 0xcc4f83ccUL, + 0x345c6834UL, 0xa5f451a5UL, 0xe534d1e5UL, 0xf108f9f1UL, + 0x7193e271UL, 0xd873abd8UL, 0x31536231UL, 0x153f2a15UL, + 0x040c0804UL, 0xc75295c7UL, 0x23654623UL, 0xc35e9dc3UL, + 0x18283018UL, 0x96a13796UL, 0x050f0a05UL, 0x9ab52f9aUL, + 0x07090e07UL, 0x12362412UL, 0x809b1b80UL, 0xe23ddfe2UL, + 0xeb26cdebUL, 0x27694e27UL, 0xb2cd7fb2UL, 0x759fea75UL, + 0x091b1209UL, 0x839e1d83UL, 0x2c74582cUL, 0x1a2e341aUL, + 0x1b2d361bUL, 0x6eb2dc6eUL, 0x5aeeb45aUL, 0xa0fb5ba0UL, + 0x52f6a452UL, 0x3b4d763bUL, 0xd661b7d6UL, 0xb3ce7db3UL, + 0x297b5229UL, 0xe33edde3UL, 0x2f715e2fUL, 0x84971384UL, + 0x53f5a653UL, 0xd168b9d1UL, 0x00000000UL, 0xed2cc1edUL, + 0x20604020UL, 0xfc1fe3fcUL, 0xb1c879b1UL, 0x5bedb65bUL, + 0x6abed46aUL, 0xcb468dcbUL, 0xbed967beUL, 0x394b7239UL, + 0x4ade944aUL, 0x4cd4984cUL, 0x58e8b058UL, 0xcf4a85cfUL, + 0xd06bbbd0UL, 0xef2ac5efUL, 0xaae54faaUL, 0xfb16edfbUL, + 0x43c58643UL, 0x4dd79a4dUL, 0x33556633UL, 0x85941185UL, + 0x45cf8a45UL, 0xf910e9f9UL, 0x02060402UL, 0x7f81fe7fUL, + 0x50f0a050UL, 0x3c44783cUL, 0x9fba259fUL, 0xa8e34ba8UL, + 0x51f3a251UL, 0xa3fe5da3UL, 0x40c08040UL, 0x8f8a058fUL, + 0x92ad3f92UL, 0x9dbc219dUL, 0x38487038UL, 0xf504f1f5UL, + 0xbcdf63bcUL, 0xb6c177b6UL, 0xda75afdaUL, 0x21634221UL, + 0x10302010UL, 0xff1ae5ffUL, 0xf30efdf3UL, 0xd26dbfd2UL, + 0xcd4c81cdUL, 0x0c14180cUL, 0x13352613UL, 0xec2fc3ecUL, + 0x5fe1be5fUL, 0x97a23597UL, 0x44cc8844UL, 0x17392e17UL, + 0xc45793c4UL, 0xa7f255a7UL, 0x7e82fc7eUL, 0x3d477a3dUL, + 0x64acc864UL, 0x5de7ba5dUL, 0x192b3219UL, 0x7395e673UL, + 0x60a0c060UL, 0x81981981UL, 0x4fd19e4fUL, 0xdc7fa3dcUL, + 0x22664422UL, 0x2a7e542aUL, 0x90ab3b90UL, 0x88830b88UL, + 0x46ca8c46UL, 0xee29c7eeUL, 0xb8d36bb8UL, 0x143c2814UL, + 0xde79a7deUL, 0x5ee2bc5eUL, 0x0b1d160bUL, 0xdb76addbUL, + 0xe03bdbe0UL, 0x32566432UL, 0x3a4e743aUL, 0x0a1e140aUL, + 0x49db9249UL, 0x060a0c06UL, 0x246c4824UL, 0x5ce4b85cUL, + 0xc25d9fc2UL, 0xd36ebdd3UL, 0xacef43acUL, 0x62a6c462UL, + 0x91a83991UL, 0x95a43195UL, 0xe437d3e4UL, 0x798bf279UL, + 0xe732d5e7UL, 0xc8438bc8UL, 0x37596e37UL, 0x6db7da6dUL, + 0x8d8c018dUL, 0xd564b1d5UL, 0x4ed29c4eUL, 0xa9e049a9UL, + 0x6cb4d86cUL, 0x56faac56UL, 0xf407f3f4UL, 0xea25cfeaUL, + 0x65afca65UL, 0x7a8ef47aUL, 0xaee947aeUL, 0x08181008UL, + 0xbad56fbaUL, 0x7888f078UL, 0x256f4a25UL, 0x2e725c2eUL, + 0x1c24381cUL, 0xa6f157a6UL, 0xb4c773b4UL, 0xc65197c6UL, + 0xe823cbe8UL, 0xdd7ca1ddUL, 0x749ce874UL, 0x1f213e1fUL, + 0x4bdd964bUL, 0xbddc61bdUL, 0x8b860d8bUL, 0x8a850f8aUL, + 0x7090e070UL, 0x3e427c3eUL, 0xb5c471b5UL, 0x66aacc66UL, + 0x48d89048UL, 0x03050603UL, 0xf601f7f6UL, 0x0e121c0eUL, + 0x61a3c261UL, 0x355f6a35UL, 0x57f9ae57UL, 0xb9d069b9UL, + 0x86911786UL, 0xc15899c1UL, 0x1d273a1dUL, 0x9eb9279eUL, + 0xe138d9e1UL, 0xf813ebf8UL, 0x98b32b98UL, 0x11332211UL, + 0x69bbd269UL, 0xd970a9d9UL, 0x8e89078eUL, 0x94a73394UL, + 0x9bb62d9bUL, 0x1e223c1eUL, 0x87921587UL, 0xe920c9e9UL, + 0xce4987ceUL, 0x55ffaa55UL, 0x28785028UL, 0xdf7aa5dfUL, + 0x8c8f038cUL, 0xa1f859a1UL, 0x89800989UL, 0x0d171a0dUL, + 0xbfda65bfUL, 0xe631d7e6UL, 0x42c68442UL, 0x68b8d068UL, + 0x41c38241UL, 0x99b02999UL, 0x2d775a2dUL, 0x0f111e0fUL, + 0xb0cb7bb0UL, 0x54fca854UL, 0xbbd66dbbUL, 0x163a2c16UL, +}; +static const ulong32 TE3[256] = { + + 0x6363a5c6UL, 0x7c7c84f8UL, 0x777799eeUL, 0x7b7b8df6UL, + 0xf2f20dffUL, 0x6b6bbdd6UL, 0x6f6fb1deUL, 0xc5c55491UL, + 0x30305060UL, 0x01010302UL, 0x6767a9ceUL, 0x2b2b7d56UL, + 0xfefe19e7UL, 0xd7d762b5UL, 0xababe64dUL, 0x76769aecUL, + 0xcaca458fUL, 0x82829d1fUL, 0xc9c94089UL, 0x7d7d87faUL, + 0xfafa15efUL, 0x5959ebb2UL, 0x4747c98eUL, 0xf0f00bfbUL, + 0xadadec41UL, 0xd4d467b3UL, 0xa2a2fd5fUL, 0xafafea45UL, + 0x9c9cbf23UL, 0xa4a4f753UL, 0x727296e4UL, 0xc0c05b9bUL, + 0xb7b7c275UL, 0xfdfd1ce1UL, 0x9393ae3dUL, 0x26266a4cUL, + 0x36365a6cUL, 0x3f3f417eUL, 0xf7f702f5UL, 0xcccc4f83UL, + 0x34345c68UL, 0xa5a5f451UL, 0xe5e534d1UL, 0xf1f108f9UL, + 0x717193e2UL, 0xd8d873abUL, 0x31315362UL, 0x15153f2aUL, + 0x04040c08UL, 0xc7c75295UL, 0x23236546UL, 0xc3c35e9dUL, + 0x18182830UL, 0x9696a137UL, 0x05050f0aUL, 0x9a9ab52fUL, + 0x0707090eUL, 0x12123624UL, 0x80809b1bUL, 0xe2e23ddfUL, + 0xebeb26cdUL, 0x2727694eUL, 0xb2b2cd7fUL, 0x75759feaUL, + 0x09091b12UL, 0x83839e1dUL, 0x2c2c7458UL, 0x1a1a2e34UL, + 0x1b1b2d36UL, 0x6e6eb2dcUL, 0x5a5aeeb4UL, 0xa0a0fb5bUL, + 0x5252f6a4UL, 0x3b3b4d76UL, 0xd6d661b7UL, 0xb3b3ce7dUL, + 0x29297b52UL, 0xe3e33eddUL, 0x2f2f715eUL, 0x84849713UL, + 0x5353f5a6UL, 0xd1d168b9UL, 0x00000000UL, 0xeded2cc1UL, + 0x20206040UL, 0xfcfc1fe3UL, 0xb1b1c879UL, 0x5b5bedb6UL, + 0x6a6abed4UL, 0xcbcb468dUL, 0xbebed967UL, 0x39394b72UL, + 0x4a4ade94UL, 0x4c4cd498UL, 0x5858e8b0UL, 0xcfcf4a85UL, + 0xd0d06bbbUL, 0xefef2ac5UL, 0xaaaae54fUL, 0xfbfb16edUL, + 0x4343c586UL, 0x4d4dd79aUL, 0x33335566UL, 0x85859411UL, + 0x4545cf8aUL, 0xf9f910e9UL, 0x02020604UL, 0x7f7f81feUL, + 0x5050f0a0UL, 0x3c3c4478UL, 0x9f9fba25UL, 0xa8a8e34bUL, + 0x5151f3a2UL, 0xa3a3fe5dUL, 0x4040c080UL, 0x8f8f8a05UL, + 0x9292ad3fUL, 0x9d9dbc21UL, 0x38384870UL, 0xf5f504f1UL, + 0xbcbcdf63UL, 0xb6b6c177UL, 0xdada75afUL, 0x21216342UL, + 0x10103020UL, 0xffff1ae5UL, 0xf3f30efdUL, 0xd2d26dbfUL, + 0xcdcd4c81UL, 0x0c0c1418UL, 0x13133526UL, 0xecec2fc3UL, + 0x5f5fe1beUL, 0x9797a235UL, 0x4444cc88UL, 0x1717392eUL, + 0xc4c45793UL, 0xa7a7f255UL, 0x7e7e82fcUL, 0x3d3d477aUL, + 0x6464acc8UL, 0x5d5de7baUL, 0x19192b32UL, 0x737395e6UL, + 0x6060a0c0UL, 0x81819819UL, 0x4f4fd19eUL, 0xdcdc7fa3UL, + 0x22226644UL, 0x2a2a7e54UL, 0x9090ab3bUL, 0x8888830bUL, + 0x4646ca8cUL, 0xeeee29c7UL, 0xb8b8d36bUL, 0x14143c28UL, + 0xdede79a7UL, 0x5e5ee2bcUL, 0x0b0b1d16UL, 0xdbdb76adUL, + 0xe0e03bdbUL, 0x32325664UL, 0x3a3a4e74UL, 0x0a0a1e14UL, + 0x4949db92UL, 0x06060a0cUL, 0x24246c48UL, 0x5c5ce4b8UL, + 0xc2c25d9fUL, 0xd3d36ebdUL, 0xacacef43UL, 0x6262a6c4UL, + 0x9191a839UL, 0x9595a431UL, 0xe4e437d3UL, 0x79798bf2UL, + 0xe7e732d5UL, 0xc8c8438bUL, 0x3737596eUL, 0x6d6db7daUL, + 0x8d8d8c01UL, 0xd5d564b1UL, 0x4e4ed29cUL, 0xa9a9e049UL, + 0x6c6cb4d8UL, 0x5656faacUL, 0xf4f407f3UL, 0xeaea25cfUL, + 0x6565afcaUL, 0x7a7a8ef4UL, 0xaeaee947UL, 0x08081810UL, + 0xbabad56fUL, 0x787888f0UL, 0x25256f4aUL, 0x2e2e725cUL, + 0x1c1c2438UL, 0xa6a6f157UL, 0xb4b4c773UL, 0xc6c65197UL, + 0xe8e823cbUL, 0xdddd7ca1UL, 0x74749ce8UL, 0x1f1f213eUL, + 0x4b4bdd96UL, 0xbdbddc61UL, 0x8b8b860dUL, 0x8a8a850fUL, + 0x707090e0UL, 0x3e3e427cUL, 0xb5b5c471UL, 0x6666aaccUL, + 0x4848d890UL, 0x03030506UL, 0xf6f601f7UL, 0x0e0e121cUL, + 0x6161a3c2UL, 0x35355f6aUL, 0x5757f9aeUL, 0xb9b9d069UL, + 0x86869117UL, 0xc1c15899UL, 0x1d1d273aUL, 0x9e9eb927UL, + 0xe1e138d9UL, 0xf8f813ebUL, 0x9898b32bUL, 0x11113322UL, + 0x6969bbd2UL, 0xd9d970a9UL, 0x8e8e8907UL, 0x9494a733UL, + 0x9b9bb62dUL, 0x1e1e223cUL, 0x87879215UL, 0xe9e920c9UL, + 0xcece4987UL, 0x5555ffaaUL, 0x28287850UL, 0xdfdf7aa5UL, + 0x8c8c8f03UL, 0xa1a1f859UL, 0x89898009UL, 0x0d0d171aUL, + 0xbfbfda65UL, 0xe6e631d7UL, 0x4242c684UL, 0x6868b8d0UL, + 0x4141c382UL, 0x9999b029UL, 0x2d2d775aUL, 0x0f0f111eUL, + 0xb0b0cb7bUL, 0x5454fca8UL, 0xbbbbd66dUL, 0x16163a2cUL, +}; + +#ifndef PELI_TAB +static const ulong32 Te4_0[] = { +0x00000063UL, 0x0000007cUL, 0x00000077UL, 0x0000007bUL, 0x000000f2UL, 0x0000006bUL, 0x0000006fUL, 0x000000c5UL, +0x00000030UL, 0x00000001UL, 0x00000067UL, 0x0000002bUL, 0x000000feUL, 0x000000d7UL, 0x000000abUL, 0x00000076UL, +0x000000caUL, 0x00000082UL, 0x000000c9UL, 0x0000007dUL, 0x000000faUL, 0x00000059UL, 0x00000047UL, 0x000000f0UL, +0x000000adUL, 0x000000d4UL, 0x000000a2UL, 0x000000afUL, 0x0000009cUL, 0x000000a4UL, 0x00000072UL, 0x000000c0UL, +0x000000b7UL, 0x000000fdUL, 0x00000093UL, 0x00000026UL, 0x00000036UL, 0x0000003fUL, 0x000000f7UL, 0x000000ccUL, +0x00000034UL, 0x000000a5UL, 0x000000e5UL, 0x000000f1UL, 0x00000071UL, 0x000000d8UL, 0x00000031UL, 0x00000015UL, +0x00000004UL, 0x000000c7UL, 0x00000023UL, 0x000000c3UL, 0x00000018UL, 0x00000096UL, 0x00000005UL, 0x0000009aUL, +0x00000007UL, 0x00000012UL, 0x00000080UL, 0x000000e2UL, 0x000000ebUL, 0x00000027UL, 0x000000b2UL, 0x00000075UL, +0x00000009UL, 0x00000083UL, 0x0000002cUL, 0x0000001aUL, 0x0000001bUL, 0x0000006eUL, 0x0000005aUL, 0x000000a0UL, +0x00000052UL, 0x0000003bUL, 0x000000d6UL, 0x000000b3UL, 0x00000029UL, 0x000000e3UL, 0x0000002fUL, 0x00000084UL, +0x00000053UL, 0x000000d1UL, 0x00000000UL, 0x000000edUL, 0x00000020UL, 0x000000fcUL, 0x000000b1UL, 0x0000005bUL, +0x0000006aUL, 0x000000cbUL, 0x000000beUL, 0x00000039UL, 0x0000004aUL, 0x0000004cUL, 0x00000058UL, 0x000000cfUL, +0x000000d0UL, 0x000000efUL, 0x000000aaUL, 0x000000fbUL, 0x00000043UL, 0x0000004dUL, 0x00000033UL, 0x00000085UL, +0x00000045UL, 0x000000f9UL, 0x00000002UL, 0x0000007fUL, 0x00000050UL, 0x0000003cUL, 0x0000009fUL, 0x000000a8UL, +0x00000051UL, 0x000000a3UL, 0x00000040UL, 0x0000008fUL, 0x00000092UL, 0x0000009dUL, 0x00000038UL, 0x000000f5UL, +0x000000bcUL, 0x000000b6UL, 0x000000daUL, 0x00000021UL, 0x00000010UL, 0x000000ffUL, 0x000000f3UL, 0x000000d2UL, +0x000000cdUL, 0x0000000cUL, 0x00000013UL, 0x000000ecUL, 0x0000005fUL, 0x00000097UL, 0x00000044UL, 0x00000017UL, +0x000000c4UL, 0x000000a7UL, 0x0000007eUL, 0x0000003dUL, 0x00000064UL, 0x0000005dUL, 0x00000019UL, 0x00000073UL, +0x00000060UL, 0x00000081UL, 0x0000004fUL, 0x000000dcUL, 0x00000022UL, 0x0000002aUL, 0x00000090UL, 0x00000088UL, +0x00000046UL, 0x000000eeUL, 0x000000b8UL, 0x00000014UL, 0x000000deUL, 0x0000005eUL, 0x0000000bUL, 0x000000dbUL, +0x000000e0UL, 0x00000032UL, 0x0000003aUL, 0x0000000aUL, 0x00000049UL, 0x00000006UL, 0x00000024UL, 0x0000005cUL, +0x000000c2UL, 0x000000d3UL, 0x000000acUL, 0x00000062UL, 0x00000091UL, 0x00000095UL, 0x000000e4UL, 0x00000079UL, +0x000000e7UL, 0x000000c8UL, 0x00000037UL, 0x0000006dUL, 0x0000008dUL, 0x000000d5UL, 0x0000004eUL, 0x000000a9UL, +0x0000006cUL, 0x00000056UL, 0x000000f4UL, 0x000000eaUL, 0x00000065UL, 0x0000007aUL, 0x000000aeUL, 0x00000008UL, +0x000000baUL, 0x00000078UL, 0x00000025UL, 0x0000002eUL, 0x0000001cUL, 0x000000a6UL, 0x000000b4UL, 0x000000c6UL, +0x000000e8UL, 0x000000ddUL, 0x00000074UL, 0x0000001fUL, 0x0000004bUL, 0x000000bdUL, 0x0000008bUL, 0x0000008aUL, +0x00000070UL, 0x0000003eUL, 0x000000b5UL, 0x00000066UL, 0x00000048UL, 0x00000003UL, 0x000000f6UL, 0x0000000eUL, +0x00000061UL, 0x00000035UL, 0x00000057UL, 0x000000b9UL, 0x00000086UL, 0x000000c1UL, 0x0000001dUL, 0x0000009eUL, +0x000000e1UL, 0x000000f8UL, 0x00000098UL, 0x00000011UL, 0x00000069UL, 0x000000d9UL, 0x0000008eUL, 0x00000094UL, +0x0000009bUL, 0x0000001eUL, 0x00000087UL, 0x000000e9UL, 0x000000ceUL, 0x00000055UL, 0x00000028UL, 0x000000dfUL, +0x0000008cUL, 0x000000a1UL, 0x00000089UL, 0x0000000dUL, 0x000000bfUL, 0x000000e6UL, 0x00000042UL, 0x00000068UL, +0x00000041UL, 0x00000099UL, 0x0000002dUL, 0x0000000fUL, 0x000000b0UL, 0x00000054UL, 0x000000bbUL, 0x00000016UL +}; + +static const ulong32 Te4_1[] = { +0x00006300UL, 0x00007c00UL, 0x00007700UL, 0x00007b00UL, 0x0000f200UL, 0x00006b00UL, 0x00006f00UL, 0x0000c500UL, +0x00003000UL, 0x00000100UL, 0x00006700UL, 0x00002b00UL, 0x0000fe00UL, 0x0000d700UL, 0x0000ab00UL, 0x00007600UL, +0x0000ca00UL, 0x00008200UL, 0x0000c900UL, 0x00007d00UL, 0x0000fa00UL, 0x00005900UL, 0x00004700UL, 0x0000f000UL, +0x0000ad00UL, 0x0000d400UL, 0x0000a200UL, 0x0000af00UL, 0x00009c00UL, 0x0000a400UL, 0x00007200UL, 0x0000c000UL, +0x0000b700UL, 0x0000fd00UL, 0x00009300UL, 0x00002600UL, 0x00003600UL, 0x00003f00UL, 0x0000f700UL, 0x0000cc00UL, +0x00003400UL, 0x0000a500UL, 0x0000e500UL, 0x0000f100UL, 0x00007100UL, 0x0000d800UL, 0x00003100UL, 0x00001500UL, +0x00000400UL, 0x0000c700UL, 0x00002300UL, 0x0000c300UL, 0x00001800UL, 0x00009600UL, 0x00000500UL, 0x00009a00UL, +0x00000700UL, 0x00001200UL, 0x00008000UL, 0x0000e200UL, 0x0000eb00UL, 0x00002700UL, 0x0000b200UL, 0x00007500UL, +0x00000900UL, 0x00008300UL, 0x00002c00UL, 0x00001a00UL, 0x00001b00UL, 0x00006e00UL, 0x00005a00UL, 0x0000a000UL, +0x00005200UL, 0x00003b00UL, 0x0000d600UL, 0x0000b300UL, 0x00002900UL, 0x0000e300UL, 0x00002f00UL, 0x00008400UL, +0x00005300UL, 0x0000d100UL, 0x00000000UL, 0x0000ed00UL, 0x00002000UL, 0x0000fc00UL, 0x0000b100UL, 0x00005b00UL, +0x00006a00UL, 0x0000cb00UL, 0x0000be00UL, 0x00003900UL, 0x00004a00UL, 0x00004c00UL, 0x00005800UL, 0x0000cf00UL, +0x0000d000UL, 0x0000ef00UL, 0x0000aa00UL, 0x0000fb00UL, 0x00004300UL, 0x00004d00UL, 0x00003300UL, 0x00008500UL, +0x00004500UL, 0x0000f900UL, 0x00000200UL, 0x00007f00UL, 0x00005000UL, 0x00003c00UL, 0x00009f00UL, 0x0000a800UL, +0x00005100UL, 0x0000a300UL, 0x00004000UL, 0x00008f00UL, 0x00009200UL, 0x00009d00UL, 0x00003800UL, 0x0000f500UL, +0x0000bc00UL, 0x0000b600UL, 0x0000da00UL, 0x00002100UL, 0x00001000UL, 0x0000ff00UL, 0x0000f300UL, 0x0000d200UL, +0x0000cd00UL, 0x00000c00UL, 0x00001300UL, 0x0000ec00UL, 0x00005f00UL, 0x00009700UL, 0x00004400UL, 0x00001700UL, +0x0000c400UL, 0x0000a700UL, 0x00007e00UL, 0x00003d00UL, 0x00006400UL, 0x00005d00UL, 0x00001900UL, 0x00007300UL, +0x00006000UL, 0x00008100UL, 0x00004f00UL, 0x0000dc00UL, 0x00002200UL, 0x00002a00UL, 0x00009000UL, 0x00008800UL, +0x00004600UL, 0x0000ee00UL, 0x0000b800UL, 0x00001400UL, 0x0000de00UL, 0x00005e00UL, 0x00000b00UL, 0x0000db00UL, +0x0000e000UL, 0x00003200UL, 0x00003a00UL, 0x00000a00UL, 0x00004900UL, 0x00000600UL, 0x00002400UL, 0x00005c00UL, +0x0000c200UL, 0x0000d300UL, 0x0000ac00UL, 0x00006200UL, 0x00009100UL, 0x00009500UL, 0x0000e400UL, 0x00007900UL, +0x0000e700UL, 0x0000c800UL, 0x00003700UL, 0x00006d00UL, 0x00008d00UL, 0x0000d500UL, 0x00004e00UL, 0x0000a900UL, +0x00006c00UL, 0x00005600UL, 0x0000f400UL, 0x0000ea00UL, 0x00006500UL, 0x00007a00UL, 0x0000ae00UL, 0x00000800UL, +0x0000ba00UL, 0x00007800UL, 0x00002500UL, 0x00002e00UL, 0x00001c00UL, 0x0000a600UL, 0x0000b400UL, 0x0000c600UL, +0x0000e800UL, 0x0000dd00UL, 0x00007400UL, 0x00001f00UL, 0x00004b00UL, 0x0000bd00UL, 0x00008b00UL, 0x00008a00UL, +0x00007000UL, 0x00003e00UL, 0x0000b500UL, 0x00006600UL, 0x00004800UL, 0x00000300UL, 0x0000f600UL, 0x00000e00UL, +0x00006100UL, 0x00003500UL, 0x00005700UL, 0x0000b900UL, 0x00008600UL, 0x0000c100UL, 0x00001d00UL, 0x00009e00UL, +0x0000e100UL, 0x0000f800UL, 0x00009800UL, 0x00001100UL, 0x00006900UL, 0x0000d900UL, 0x00008e00UL, 0x00009400UL, +0x00009b00UL, 0x00001e00UL, 0x00008700UL, 0x0000e900UL, 0x0000ce00UL, 0x00005500UL, 0x00002800UL, 0x0000df00UL, +0x00008c00UL, 0x0000a100UL, 0x00008900UL, 0x00000d00UL, 0x0000bf00UL, 0x0000e600UL, 0x00004200UL, 0x00006800UL, +0x00004100UL, 0x00009900UL, 0x00002d00UL, 0x00000f00UL, 0x0000b000UL, 0x00005400UL, 0x0000bb00UL, 0x00001600UL +}; + +static const ulong32 Te4_2[] = { +0x00630000UL, 0x007c0000UL, 0x00770000UL, 0x007b0000UL, 0x00f20000UL, 0x006b0000UL, 0x006f0000UL, 0x00c50000UL, +0x00300000UL, 0x00010000UL, 0x00670000UL, 0x002b0000UL, 0x00fe0000UL, 0x00d70000UL, 0x00ab0000UL, 0x00760000UL, +0x00ca0000UL, 0x00820000UL, 0x00c90000UL, 0x007d0000UL, 0x00fa0000UL, 0x00590000UL, 0x00470000UL, 0x00f00000UL, +0x00ad0000UL, 0x00d40000UL, 0x00a20000UL, 0x00af0000UL, 0x009c0000UL, 0x00a40000UL, 0x00720000UL, 0x00c00000UL, +0x00b70000UL, 0x00fd0000UL, 0x00930000UL, 0x00260000UL, 0x00360000UL, 0x003f0000UL, 0x00f70000UL, 0x00cc0000UL, +0x00340000UL, 0x00a50000UL, 0x00e50000UL, 0x00f10000UL, 0x00710000UL, 0x00d80000UL, 0x00310000UL, 0x00150000UL, +0x00040000UL, 0x00c70000UL, 0x00230000UL, 0x00c30000UL, 0x00180000UL, 0x00960000UL, 0x00050000UL, 0x009a0000UL, +0x00070000UL, 0x00120000UL, 0x00800000UL, 0x00e20000UL, 0x00eb0000UL, 0x00270000UL, 0x00b20000UL, 0x00750000UL, +0x00090000UL, 0x00830000UL, 0x002c0000UL, 0x001a0000UL, 0x001b0000UL, 0x006e0000UL, 0x005a0000UL, 0x00a00000UL, +0x00520000UL, 0x003b0000UL, 0x00d60000UL, 0x00b30000UL, 0x00290000UL, 0x00e30000UL, 0x002f0000UL, 0x00840000UL, +0x00530000UL, 0x00d10000UL, 0x00000000UL, 0x00ed0000UL, 0x00200000UL, 0x00fc0000UL, 0x00b10000UL, 0x005b0000UL, +0x006a0000UL, 0x00cb0000UL, 0x00be0000UL, 0x00390000UL, 0x004a0000UL, 0x004c0000UL, 0x00580000UL, 0x00cf0000UL, +0x00d00000UL, 0x00ef0000UL, 0x00aa0000UL, 0x00fb0000UL, 0x00430000UL, 0x004d0000UL, 0x00330000UL, 0x00850000UL, +0x00450000UL, 0x00f90000UL, 0x00020000UL, 0x007f0000UL, 0x00500000UL, 0x003c0000UL, 0x009f0000UL, 0x00a80000UL, +0x00510000UL, 0x00a30000UL, 0x00400000UL, 0x008f0000UL, 0x00920000UL, 0x009d0000UL, 0x00380000UL, 0x00f50000UL, +0x00bc0000UL, 0x00b60000UL, 0x00da0000UL, 0x00210000UL, 0x00100000UL, 0x00ff0000UL, 0x00f30000UL, 0x00d20000UL, +0x00cd0000UL, 0x000c0000UL, 0x00130000UL, 0x00ec0000UL, 0x005f0000UL, 0x00970000UL, 0x00440000UL, 0x00170000UL, +0x00c40000UL, 0x00a70000UL, 0x007e0000UL, 0x003d0000UL, 0x00640000UL, 0x005d0000UL, 0x00190000UL, 0x00730000UL, +0x00600000UL, 0x00810000UL, 0x004f0000UL, 0x00dc0000UL, 0x00220000UL, 0x002a0000UL, 0x00900000UL, 0x00880000UL, +0x00460000UL, 0x00ee0000UL, 0x00b80000UL, 0x00140000UL, 0x00de0000UL, 0x005e0000UL, 0x000b0000UL, 0x00db0000UL, +0x00e00000UL, 0x00320000UL, 0x003a0000UL, 0x000a0000UL, 0x00490000UL, 0x00060000UL, 0x00240000UL, 0x005c0000UL, +0x00c20000UL, 0x00d30000UL, 0x00ac0000UL, 0x00620000UL, 0x00910000UL, 0x00950000UL, 0x00e40000UL, 0x00790000UL, +0x00e70000UL, 0x00c80000UL, 0x00370000UL, 0x006d0000UL, 0x008d0000UL, 0x00d50000UL, 0x004e0000UL, 0x00a90000UL, +0x006c0000UL, 0x00560000UL, 0x00f40000UL, 0x00ea0000UL, 0x00650000UL, 0x007a0000UL, 0x00ae0000UL, 0x00080000UL, +0x00ba0000UL, 0x00780000UL, 0x00250000UL, 0x002e0000UL, 0x001c0000UL, 0x00a60000UL, 0x00b40000UL, 0x00c60000UL, +0x00e80000UL, 0x00dd0000UL, 0x00740000UL, 0x001f0000UL, 0x004b0000UL, 0x00bd0000UL, 0x008b0000UL, 0x008a0000UL, +0x00700000UL, 0x003e0000UL, 0x00b50000UL, 0x00660000UL, 0x00480000UL, 0x00030000UL, 0x00f60000UL, 0x000e0000UL, +0x00610000UL, 0x00350000UL, 0x00570000UL, 0x00b90000UL, 0x00860000UL, 0x00c10000UL, 0x001d0000UL, 0x009e0000UL, +0x00e10000UL, 0x00f80000UL, 0x00980000UL, 0x00110000UL, 0x00690000UL, 0x00d90000UL, 0x008e0000UL, 0x00940000UL, +0x009b0000UL, 0x001e0000UL, 0x00870000UL, 0x00e90000UL, 0x00ce0000UL, 0x00550000UL, 0x00280000UL, 0x00df0000UL, +0x008c0000UL, 0x00a10000UL, 0x00890000UL, 0x000d0000UL, 0x00bf0000UL, 0x00e60000UL, 0x00420000UL, 0x00680000UL, +0x00410000UL, 0x00990000UL, 0x002d0000UL, 0x000f0000UL, 0x00b00000UL, 0x00540000UL, 0x00bb0000UL, 0x00160000UL +}; + +static const ulong32 Te4_3[] = { +0x63000000UL, 0x7c000000UL, 0x77000000UL, 0x7b000000UL, 0xf2000000UL, 0x6b000000UL, 0x6f000000UL, 0xc5000000UL, +0x30000000UL, 0x01000000UL, 0x67000000UL, 0x2b000000UL, 0xfe000000UL, 0xd7000000UL, 0xab000000UL, 0x76000000UL, +0xca000000UL, 0x82000000UL, 0xc9000000UL, 0x7d000000UL, 0xfa000000UL, 0x59000000UL, 0x47000000UL, 0xf0000000UL, +0xad000000UL, 0xd4000000UL, 0xa2000000UL, 0xaf000000UL, 0x9c000000UL, 0xa4000000UL, 0x72000000UL, 0xc0000000UL, +0xb7000000UL, 0xfd000000UL, 0x93000000UL, 0x26000000UL, 0x36000000UL, 0x3f000000UL, 0xf7000000UL, 0xcc000000UL, +0x34000000UL, 0xa5000000UL, 0xe5000000UL, 0xf1000000UL, 0x71000000UL, 0xd8000000UL, 0x31000000UL, 0x15000000UL, +0x04000000UL, 0xc7000000UL, 0x23000000UL, 0xc3000000UL, 0x18000000UL, 0x96000000UL, 0x05000000UL, 0x9a000000UL, +0x07000000UL, 0x12000000UL, 0x80000000UL, 0xe2000000UL, 0xeb000000UL, 0x27000000UL, 0xb2000000UL, 0x75000000UL, +0x09000000UL, 0x83000000UL, 0x2c000000UL, 0x1a000000UL, 0x1b000000UL, 0x6e000000UL, 0x5a000000UL, 0xa0000000UL, +0x52000000UL, 0x3b000000UL, 0xd6000000UL, 0xb3000000UL, 0x29000000UL, 0xe3000000UL, 0x2f000000UL, 0x84000000UL, +0x53000000UL, 0xd1000000UL, 0x00000000UL, 0xed000000UL, 0x20000000UL, 0xfc000000UL, 0xb1000000UL, 0x5b000000UL, +0x6a000000UL, 0xcb000000UL, 0xbe000000UL, 0x39000000UL, 0x4a000000UL, 0x4c000000UL, 0x58000000UL, 0xcf000000UL, +0xd0000000UL, 0xef000000UL, 0xaa000000UL, 0xfb000000UL, 0x43000000UL, 0x4d000000UL, 0x33000000UL, 0x85000000UL, +0x45000000UL, 0xf9000000UL, 0x02000000UL, 0x7f000000UL, 0x50000000UL, 0x3c000000UL, 0x9f000000UL, 0xa8000000UL, +0x51000000UL, 0xa3000000UL, 0x40000000UL, 0x8f000000UL, 0x92000000UL, 0x9d000000UL, 0x38000000UL, 0xf5000000UL, +0xbc000000UL, 0xb6000000UL, 0xda000000UL, 0x21000000UL, 0x10000000UL, 0xff000000UL, 0xf3000000UL, 0xd2000000UL, +0xcd000000UL, 0x0c000000UL, 0x13000000UL, 0xec000000UL, 0x5f000000UL, 0x97000000UL, 0x44000000UL, 0x17000000UL, +0xc4000000UL, 0xa7000000UL, 0x7e000000UL, 0x3d000000UL, 0x64000000UL, 0x5d000000UL, 0x19000000UL, 0x73000000UL, +0x60000000UL, 0x81000000UL, 0x4f000000UL, 0xdc000000UL, 0x22000000UL, 0x2a000000UL, 0x90000000UL, 0x88000000UL, +0x46000000UL, 0xee000000UL, 0xb8000000UL, 0x14000000UL, 0xde000000UL, 0x5e000000UL, 0x0b000000UL, 0xdb000000UL, +0xe0000000UL, 0x32000000UL, 0x3a000000UL, 0x0a000000UL, 0x49000000UL, 0x06000000UL, 0x24000000UL, 0x5c000000UL, +0xc2000000UL, 0xd3000000UL, 0xac000000UL, 0x62000000UL, 0x91000000UL, 0x95000000UL, 0xe4000000UL, 0x79000000UL, +0xe7000000UL, 0xc8000000UL, 0x37000000UL, 0x6d000000UL, 0x8d000000UL, 0xd5000000UL, 0x4e000000UL, 0xa9000000UL, +0x6c000000UL, 0x56000000UL, 0xf4000000UL, 0xea000000UL, 0x65000000UL, 0x7a000000UL, 0xae000000UL, 0x08000000UL, +0xba000000UL, 0x78000000UL, 0x25000000UL, 0x2e000000UL, 0x1c000000UL, 0xa6000000UL, 0xb4000000UL, 0xc6000000UL, +0xe8000000UL, 0xdd000000UL, 0x74000000UL, 0x1f000000UL, 0x4b000000UL, 0xbd000000UL, 0x8b000000UL, 0x8a000000UL, +0x70000000UL, 0x3e000000UL, 0xb5000000UL, 0x66000000UL, 0x48000000UL, 0x03000000UL, 0xf6000000UL, 0x0e000000UL, +0x61000000UL, 0x35000000UL, 0x57000000UL, 0xb9000000UL, 0x86000000UL, 0xc1000000UL, 0x1d000000UL, 0x9e000000UL, +0xe1000000UL, 0xf8000000UL, 0x98000000UL, 0x11000000UL, 0x69000000UL, 0xd9000000UL, 0x8e000000UL, 0x94000000UL, +0x9b000000UL, 0x1e000000UL, 0x87000000UL, 0xe9000000UL, 0xce000000UL, 0x55000000UL, 0x28000000UL, 0xdf000000UL, +0x8c000000UL, 0xa1000000UL, 0x89000000UL, 0x0d000000UL, 0xbf000000UL, 0xe6000000UL, 0x42000000UL, 0x68000000UL, +0x41000000UL, 0x99000000UL, 0x2d000000UL, 0x0f000000UL, 0xb0000000UL, 0x54000000UL, 0xbb000000UL, 0x16000000UL +}; +#endif /* pelimac */ + +#ifndef ENCRYPT_ONLY + +static const ulong32 TD1[256] = { + 0x5051f4a7UL, 0x537e4165UL, 0xc31a17a4UL, 0x963a275eUL, + 0xcb3bab6bUL, 0xf11f9d45UL, 0xabacfa58UL, 0x934be303UL, + 0x552030faUL, 0xf6ad766dUL, 0x9188cc76UL, 0x25f5024cUL, + 0xfc4fe5d7UL, 0xd7c52acbUL, 0x80263544UL, 0x8fb562a3UL, + 0x49deb15aUL, 0x6725ba1bUL, 0x9845ea0eUL, 0xe15dfec0UL, + 0x02c32f75UL, 0x12814cf0UL, 0xa38d4697UL, 0xc66bd3f9UL, + 0xe7038f5fUL, 0x9515929cUL, 0xebbf6d7aUL, 0xda955259UL, + 0x2dd4be83UL, 0xd3587421UL, 0x2949e069UL, 0x448ec9c8UL, + 0x6a75c289UL, 0x78f48e79UL, 0x6b99583eUL, 0xdd27b971UL, + 0xb6bee14fUL, 0x17f088adUL, 0x66c920acUL, 0xb47dce3aUL, + 0x1863df4aUL, 0x82e51a31UL, 0x60975133UL, 0x4562537fUL, + 0xe0b16477UL, 0x84bb6baeUL, 0x1cfe81a0UL, 0x94f9082bUL, + 0x58704868UL, 0x198f45fdUL, 0x8794de6cUL, 0xb7527bf8UL, + 0x23ab73d3UL, 0xe2724b02UL, 0x57e31f8fUL, 0x2a6655abUL, + 0x07b2eb28UL, 0x032fb5c2UL, 0x9a86c57bUL, 0xa5d33708UL, + 0xf2302887UL, 0xb223bfa5UL, 0xba02036aUL, 0x5ced1682UL, + 0x2b8acf1cUL, 0x92a779b4UL, 0xf0f307f2UL, 0xa14e69e2UL, + 0xcd65daf4UL, 0xd50605beUL, 0x1fd13462UL, 0x8ac4a6feUL, + 0x9d342e53UL, 0xa0a2f355UL, 0x32058ae1UL, 0x75a4f6ebUL, + 0x390b83ecUL, 0xaa4060efUL, 0x065e719fUL, 0x51bd6e10UL, + 0xf93e218aUL, 0x3d96dd06UL, 0xaedd3e05UL, 0x464de6bdUL, + 0xb591548dUL, 0x0571c45dUL, 0x6f0406d4UL, 0xff605015UL, + 0x241998fbUL, 0x97d6bde9UL, 0xcc894043UL, 0x7767d99eUL, + 0xbdb0e842UL, 0x8807898bUL, 0x38e7195bUL, 0xdb79c8eeUL, + 0x47a17c0aUL, 0xe97c420fUL, 0xc9f8841eUL, 0x00000000UL, + 0x83098086UL, 0x48322bedUL, 0xac1e1170UL, 0x4e6c5a72UL, + 0xfbfd0effUL, 0x560f8538UL, 0x1e3daed5UL, 0x27362d39UL, + 0x640a0fd9UL, 0x21685ca6UL, 0xd19b5b54UL, 0x3a24362eUL, + 0xb10c0a67UL, 0x0f9357e7UL, 0xd2b4ee96UL, 0x9e1b9b91UL, + 0x4f80c0c5UL, 0xa261dc20UL, 0x695a774bUL, 0x161c121aUL, + 0x0ae293baUL, 0xe5c0a02aUL, 0x433c22e0UL, 0x1d121b17UL, + 0x0b0e090dUL, 0xadf28bc7UL, 0xb92db6a8UL, 0xc8141ea9UL, + 0x8557f119UL, 0x4caf7507UL, 0xbbee99ddUL, 0xfda37f60UL, + 0x9ff70126UL, 0xbc5c72f5UL, 0xc544663bUL, 0x345bfb7eUL, + 0x768b4329UL, 0xdccb23c6UL, 0x68b6edfcUL, 0x63b8e4f1UL, + 0xcad731dcUL, 0x10426385UL, 0x40139722UL, 0x2084c611UL, + 0x7d854a24UL, 0xf8d2bb3dUL, 0x11aef932UL, 0x6dc729a1UL, + 0x4b1d9e2fUL, 0xf3dcb230UL, 0xec0d8652UL, 0xd077c1e3UL, + 0x6c2bb316UL, 0x99a970b9UL, 0xfa119448UL, 0x2247e964UL, + 0xc4a8fc8cUL, 0x1aa0f03fUL, 0xd8567d2cUL, 0xef223390UL, + 0xc787494eUL, 0xc1d938d1UL, 0xfe8ccaa2UL, 0x3698d40bUL, + 0xcfa6f581UL, 0x28a57adeUL, 0x26dab78eUL, 0xa43fadbfUL, + 0xe42c3a9dUL, 0x0d507892UL, 0x9b6a5fccUL, 0x62547e46UL, + 0xc2f68d13UL, 0xe890d8b8UL, 0x5e2e39f7UL, 0xf582c3afUL, + 0xbe9f5d80UL, 0x7c69d093UL, 0xa96fd52dUL, 0xb3cf2512UL, + 0x3bc8ac99UL, 0xa710187dUL, 0x6ee89c63UL, 0x7bdb3bbbUL, + 0x09cd2678UL, 0xf46e5918UL, 0x01ec9ab7UL, 0xa8834f9aUL, + 0x65e6956eUL, 0x7eaaffe6UL, 0x0821bccfUL, 0xe6ef15e8UL, + 0xd9bae79bUL, 0xce4a6f36UL, 0xd4ea9f09UL, 0xd629b07cUL, + 0xaf31a4b2UL, 0x312a3f23UL, 0x30c6a594UL, 0xc035a266UL, + 0x37744ebcUL, 0xa6fc82caUL, 0xb0e090d0UL, 0x1533a7d8UL, + 0x4af10498UL, 0xf741ecdaUL, 0x0e7fcd50UL, 0x2f1791f6UL, + 0x8d764dd6UL, 0x4d43efb0UL, 0x54ccaa4dUL, 0xdfe49604UL, + 0xe39ed1b5UL, 0x1b4c6a88UL, 0xb8c12c1fUL, 0x7f466551UL, + 0x049d5eeaUL, 0x5d018c35UL, 0x73fa8774UL, 0x2efb0b41UL, + 0x5ab3671dUL, 0x5292dbd2UL, 0x33e91056UL, 0x136dd647UL, + 0x8c9ad761UL, 0x7a37a10cUL, 0x8e59f814UL, 0x89eb133cUL, + 0xeecea927UL, 0x35b761c9UL, 0xede11ce5UL, 0x3c7a47b1UL, + 0x599cd2dfUL, 0x3f55f273UL, 0x791814ceUL, 0xbf73c737UL, + 0xea53f7cdUL, 0x5b5ffdaaUL, 0x14df3d6fUL, 0x867844dbUL, + 0x81caaff3UL, 0x3eb968c4UL, 0x2c382434UL, 0x5fc2a340UL, + 0x72161dc3UL, 0x0cbce225UL, 0x8b283c49UL, 0x41ff0d95UL, + 0x7139a801UL, 0xde080cb3UL, 0x9cd8b4e4UL, 0x906456c1UL, + 0x617bcb84UL, 0x70d532b6UL, 0x74486c5cUL, 0x42d0b857UL, +}; +static const ulong32 TD2[256] = { + 0xa75051f4UL, 0x65537e41UL, 0xa4c31a17UL, 0x5e963a27UL, + 0x6bcb3babUL, 0x45f11f9dUL, 0x58abacfaUL, 0x03934be3UL, + 0xfa552030UL, 0x6df6ad76UL, 0x769188ccUL, 0x4c25f502UL, + 0xd7fc4fe5UL, 0xcbd7c52aUL, 0x44802635UL, 0xa38fb562UL, + 0x5a49deb1UL, 0x1b6725baUL, 0x0e9845eaUL, 0xc0e15dfeUL, + 0x7502c32fUL, 0xf012814cUL, 0x97a38d46UL, 0xf9c66bd3UL, + 0x5fe7038fUL, 0x9c951592UL, 0x7aebbf6dUL, 0x59da9552UL, + 0x832dd4beUL, 0x21d35874UL, 0x692949e0UL, 0xc8448ec9UL, + 0x896a75c2UL, 0x7978f48eUL, 0x3e6b9958UL, 0x71dd27b9UL, + 0x4fb6bee1UL, 0xad17f088UL, 0xac66c920UL, 0x3ab47dceUL, + 0x4a1863dfUL, 0x3182e51aUL, 0x33609751UL, 0x7f456253UL, + 0x77e0b164UL, 0xae84bb6bUL, 0xa01cfe81UL, 0x2b94f908UL, + 0x68587048UL, 0xfd198f45UL, 0x6c8794deUL, 0xf8b7527bUL, + 0xd323ab73UL, 0x02e2724bUL, 0x8f57e31fUL, 0xab2a6655UL, + 0x2807b2ebUL, 0xc2032fb5UL, 0x7b9a86c5UL, 0x08a5d337UL, + 0x87f23028UL, 0xa5b223bfUL, 0x6aba0203UL, 0x825ced16UL, + 0x1c2b8acfUL, 0xb492a779UL, 0xf2f0f307UL, 0xe2a14e69UL, + 0xf4cd65daUL, 0xbed50605UL, 0x621fd134UL, 0xfe8ac4a6UL, + 0x539d342eUL, 0x55a0a2f3UL, 0xe132058aUL, 0xeb75a4f6UL, + 0xec390b83UL, 0xefaa4060UL, 0x9f065e71UL, 0x1051bd6eUL, + 0x8af93e21UL, 0x063d96ddUL, 0x05aedd3eUL, 0xbd464de6UL, + 0x8db59154UL, 0x5d0571c4UL, 0xd46f0406UL, 0x15ff6050UL, + 0xfb241998UL, 0xe997d6bdUL, 0x43cc8940UL, 0x9e7767d9UL, + 0x42bdb0e8UL, 0x8b880789UL, 0x5b38e719UL, 0xeedb79c8UL, + 0x0a47a17cUL, 0x0fe97c42UL, 0x1ec9f884UL, 0x00000000UL, + 0x86830980UL, 0xed48322bUL, 0x70ac1e11UL, 0x724e6c5aUL, + 0xfffbfd0eUL, 0x38560f85UL, 0xd51e3daeUL, 0x3927362dUL, + 0xd9640a0fUL, 0xa621685cUL, 0x54d19b5bUL, 0x2e3a2436UL, + 0x67b10c0aUL, 0xe70f9357UL, 0x96d2b4eeUL, 0x919e1b9bUL, + 0xc54f80c0UL, 0x20a261dcUL, 0x4b695a77UL, 0x1a161c12UL, + 0xba0ae293UL, 0x2ae5c0a0UL, 0xe0433c22UL, 0x171d121bUL, + 0x0d0b0e09UL, 0xc7adf28bUL, 0xa8b92db6UL, 0xa9c8141eUL, + 0x198557f1UL, 0x074caf75UL, 0xddbbee99UL, 0x60fda37fUL, + 0x269ff701UL, 0xf5bc5c72UL, 0x3bc54466UL, 0x7e345bfbUL, + 0x29768b43UL, 0xc6dccb23UL, 0xfc68b6edUL, 0xf163b8e4UL, + 0xdccad731UL, 0x85104263UL, 0x22401397UL, 0x112084c6UL, + 0x247d854aUL, 0x3df8d2bbUL, 0x3211aef9UL, 0xa16dc729UL, + 0x2f4b1d9eUL, 0x30f3dcb2UL, 0x52ec0d86UL, 0xe3d077c1UL, + 0x166c2bb3UL, 0xb999a970UL, 0x48fa1194UL, 0x642247e9UL, + 0x8cc4a8fcUL, 0x3f1aa0f0UL, 0x2cd8567dUL, 0x90ef2233UL, + 0x4ec78749UL, 0xd1c1d938UL, 0xa2fe8ccaUL, 0x0b3698d4UL, + 0x81cfa6f5UL, 0xde28a57aUL, 0x8e26dab7UL, 0xbfa43fadUL, + 0x9de42c3aUL, 0x920d5078UL, 0xcc9b6a5fUL, 0x4662547eUL, + 0x13c2f68dUL, 0xb8e890d8UL, 0xf75e2e39UL, 0xaff582c3UL, + 0x80be9f5dUL, 0x937c69d0UL, 0x2da96fd5UL, 0x12b3cf25UL, + 0x993bc8acUL, 0x7da71018UL, 0x636ee89cUL, 0xbb7bdb3bUL, + 0x7809cd26UL, 0x18f46e59UL, 0xb701ec9aUL, 0x9aa8834fUL, + 0x6e65e695UL, 0xe67eaaffUL, 0xcf0821bcUL, 0xe8e6ef15UL, + 0x9bd9bae7UL, 0x36ce4a6fUL, 0x09d4ea9fUL, 0x7cd629b0UL, + 0xb2af31a4UL, 0x23312a3fUL, 0x9430c6a5UL, 0x66c035a2UL, + 0xbc37744eUL, 0xcaa6fc82UL, 0xd0b0e090UL, 0xd81533a7UL, + 0x984af104UL, 0xdaf741ecUL, 0x500e7fcdUL, 0xf62f1791UL, + 0xd68d764dUL, 0xb04d43efUL, 0x4d54ccaaUL, 0x04dfe496UL, + 0xb5e39ed1UL, 0x881b4c6aUL, 0x1fb8c12cUL, 0x517f4665UL, + 0xea049d5eUL, 0x355d018cUL, 0x7473fa87UL, 0x412efb0bUL, + 0x1d5ab367UL, 0xd25292dbUL, 0x5633e910UL, 0x47136dd6UL, + 0x618c9ad7UL, 0x0c7a37a1UL, 0x148e59f8UL, 0x3c89eb13UL, + 0x27eecea9UL, 0xc935b761UL, 0xe5ede11cUL, 0xb13c7a47UL, + 0xdf599cd2UL, 0x733f55f2UL, 0xce791814UL, 0x37bf73c7UL, + 0xcdea53f7UL, 0xaa5b5ffdUL, 0x6f14df3dUL, 0xdb867844UL, + 0xf381caafUL, 0xc43eb968UL, 0x342c3824UL, 0x405fc2a3UL, + 0xc372161dUL, 0x250cbce2UL, 0x498b283cUL, 0x9541ff0dUL, + 0x017139a8UL, 0xb3de080cUL, 0xe49cd8b4UL, 0xc1906456UL, + 0x84617bcbUL, 0xb670d532UL, 0x5c74486cUL, 0x5742d0b8UL, +}; +static const ulong32 TD3[256] = { + 0xf4a75051UL, 0x4165537eUL, 0x17a4c31aUL, 0x275e963aUL, + 0xab6bcb3bUL, 0x9d45f11fUL, 0xfa58abacUL, 0xe303934bUL, + 0x30fa5520UL, 0x766df6adUL, 0xcc769188UL, 0x024c25f5UL, + 0xe5d7fc4fUL, 0x2acbd7c5UL, 0x35448026UL, 0x62a38fb5UL, + 0xb15a49deUL, 0xba1b6725UL, 0xea0e9845UL, 0xfec0e15dUL, + 0x2f7502c3UL, 0x4cf01281UL, 0x4697a38dUL, 0xd3f9c66bUL, + 0x8f5fe703UL, 0x929c9515UL, 0x6d7aebbfUL, 0x5259da95UL, + 0xbe832dd4UL, 0x7421d358UL, 0xe0692949UL, 0xc9c8448eUL, + 0xc2896a75UL, 0x8e7978f4UL, 0x583e6b99UL, 0xb971dd27UL, + 0xe14fb6beUL, 0x88ad17f0UL, 0x20ac66c9UL, 0xce3ab47dUL, + 0xdf4a1863UL, 0x1a3182e5UL, 0x51336097UL, 0x537f4562UL, + 0x6477e0b1UL, 0x6bae84bbUL, 0x81a01cfeUL, 0x082b94f9UL, + 0x48685870UL, 0x45fd198fUL, 0xde6c8794UL, 0x7bf8b752UL, + 0x73d323abUL, 0x4b02e272UL, 0x1f8f57e3UL, 0x55ab2a66UL, + 0xeb2807b2UL, 0xb5c2032fUL, 0xc57b9a86UL, 0x3708a5d3UL, + 0x2887f230UL, 0xbfa5b223UL, 0x036aba02UL, 0x16825cedUL, + 0xcf1c2b8aUL, 0x79b492a7UL, 0x07f2f0f3UL, 0x69e2a14eUL, + 0xdaf4cd65UL, 0x05bed506UL, 0x34621fd1UL, 0xa6fe8ac4UL, + 0x2e539d34UL, 0xf355a0a2UL, 0x8ae13205UL, 0xf6eb75a4UL, + 0x83ec390bUL, 0x60efaa40UL, 0x719f065eUL, 0x6e1051bdUL, + 0x218af93eUL, 0xdd063d96UL, 0x3e05aeddUL, 0xe6bd464dUL, + 0x548db591UL, 0xc45d0571UL, 0x06d46f04UL, 0x5015ff60UL, + 0x98fb2419UL, 0xbde997d6UL, 0x4043cc89UL, 0xd99e7767UL, + 0xe842bdb0UL, 0x898b8807UL, 0x195b38e7UL, 0xc8eedb79UL, + 0x7c0a47a1UL, 0x420fe97cUL, 0x841ec9f8UL, 0x00000000UL, + 0x80868309UL, 0x2bed4832UL, 0x1170ac1eUL, 0x5a724e6cUL, + 0x0efffbfdUL, 0x8538560fUL, 0xaed51e3dUL, 0x2d392736UL, + 0x0fd9640aUL, 0x5ca62168UL, 0x5b54d19bUL, 0x362e3a24UL, + 0x0a67b10cUL, 0x57e70f93UL, 0xee96d2b4UL, 0x9b919e1bUL, + 0xc0c54f80UL, 0xdc20a261UL, 0x774b695aUL, 0x121a161cUL, + 0x93ba0ae2UL, 0xa02ae5c0UL, 0x22e0433cUL, 0x1b171d12UL, + 0x090d0b0eUL, 0x8bc7adf2UL, 0xb6a8b92dUL, 0x1ea9c814UL, + 0xf1198557UL, 0x75074cafUL, 0x99ddbbeeUL, 0x7f60fda3UL, + 0x01269ff7UL, 0x72f5bc5cUL, 0x663bc544UL, 0xfb7e345bUL, + 0x4329768bUL, 0x23c6dccbUL, 0xedfc68b6UL, 0xe4f163b8UL, + 0x31dccad7UL, 0x63851042UL, 0x97224013UL, 0xc6112084UL, + 0x4a247d85UL, 0xbb3df8d2UL, 0xf93211aeUL, 0x29a16dc7UL, + 0x9e2f4b1dUL, 0xb230f3dcUL, 0x8652ec0dUL, 0xc1e3d077UL, + 0xb3166c2bUL, 0x70b999a9UL, 0x9448fa11UL, 0xe9642247UL, + 0xfc8cc4a8UL, 0xf03f1aa0UL, 0x7d2cd856UL, 0x3390ef22UL, + 0x494ec787UL, 0x38d1c1d9UL, 0xcaa2fe8cUL, 0xd40b3698UL, + 0xf581cfa6UL, 0x7ade28a5UL, 0xb78e26daUL, 0xadbfa43fUL, + 0x3a9de42cUL, 0x78920d50UL, 0x5fcc9b6aUL, 0x7e466254UL, + 0x8d13c2f6UL, 0xd8b8e890UL, 0x39f75e2eUL, 0xc3aff582UL, + 0x5d80be9fUL, 0xd0937c69UL, 0xd52da96fUL, 0x2512b3cfUL, + 0xac993bc8UL, 0x187da710UL, 0x9c636ee8UL, 0x3bbb7bdbUL, + 0x267809cdUL, 0x5918f46eUL, 0x9ab701ecUL, 0x4f9aa883UL, + 0x956e65e6UL, 0xffe67eaaUL, 0xbccf0821UL, 0x15e8e6efUL, + 0xe79bd9baUL, 0x6f36ce4aUL, 0x9f09d4eaUL, 0xb07cd629UL, + 0xa4b2af31UL, 0x3f23312aUL, 0xa59430c6UL, 0xa266c035UL, + 0x4ebc3774UL, 0x82caa6fcUL, 0x90d0b0e0UL, 0xa7d81533UL, + 0x04984af1UL, 0xecdaf741UL, 0xcd500e7fUL, 0x91f62f17UL, + 0x4dd68d76UL, 0xefb04d43UL, 0xaa4d54ccUL, 0x9604dfe4UL, + 0xd1b5e39eUL, 0x6a881b4cUL, 0x2c1fb8c1UL, 0x65517f46UL, + 0x5eea049dUL, 0x8c355d01UL, 0x877473faUL, 0x0b412efbUL, + 0x671d5ab3UL, 0xdbd25292UL, 0x105633e9UL, 0xd647136dUL, + 0xd7618c9aUL, 0xa10c7a37UL, 0xf8148e59UL, 0x133c89ebUL, + 0xa927eeceUL, 0x61c935b7UL, 0x1ce5ede1UL, 0x47b13c7aUL, + 0xd2df599cUL, 0xf2733f55UL, 0x14ce7918UL, 0xc737bf73UL, + 0xf7cdea53UL, 0xfdaa5b5fUL, 0x3d6f14dfUL, 0x44db8678UL, + 0xaff381caUL, 0x68c43eb9UL, 0x24342c38UL, 0xa3405fc2UL, + 0x1dc37216UL, 0xe2250cbcUL, 0x3c498b28UL, 0x0d9541ffUL, + 0xa8017139UL, 0x0cb3de08UL, 0xb4e49cd8UL, 0x56c19064UL, + 0xcb84617bUL, 0x32b670d5UL, 0x6c5c7448UL, 0xb85742d0UL, +}; + +static const ulong32 Tks0[] = { +0x00000000UL, 0x0e090d0bUL, 0x1c121a16UL, 0x121b171dUL, 0x3824342cUL, 0x362d3927UL, 0x24362e3aUL, 0x2a3f2331UL, +0x70486858UL, 0x7e416553UL, 0x6c5a724eUL, 0x62537f45UL, 0x486c5c74UL, 0x4665517fUL, 0x547e4662UL, 0x5a774b69UL, +0xe090d0b0UL, 0xee99ddbbUL, 0xfc82caa6UL, 0xf28bc7adUL, 0xd8b4e49cUL, 0xd6bde997UL, 0xc4a6fe8aUL, 0xcaaff381UL, +0x90d8b8e8UL, 0x9ed1b5e3UL, 0x8ccaa2feUL, 0x82c3aff5UL, 0xa8fc8cc4UL, 0xa6f581cfUL, 0xb4ee96d2UL, 0xbae79bd9UL, +0xdb3bbb7bUL, 0xd532b670UL, 0xc729a16dUL, 0xc920ac66UL, 0xe31f8f57UL, 0xed16825cUL, 0xff0d9541UL, 0xf104984aUL, +0xab73d323UL, 0xa57ade28UL, 0xb761c935UL, 0xb968c43eUL, 0x9357e70fUL, 0x9d5eea04UL, 0x8f45fd19UL, 0x814cf012UL, +0x3bab6bcbUL, 0x35a266c0UL, 0x27b971ddUL, 0x29b07cd6UL, 0x038f5fe7UL, 0x0d8652ecUL, 0x1f9d45f1UL, 0x119448faUL, +0x4be30393UL, 0x45ea0e98UL, 0x57f11985UL, 0x59f8148eUL, 0x73c737bfUL, 0x7dce3ab4UL, 0x6fd52da9UL, 0x61dc20a2UL, +0xad766df6UL, 0xa37f60fdUL, 0xb16477e0UL, 0xbf6d7aebUL, 0x955259daUL, 0x9b5b54d1UL, 0x894043ccUL, 0x87494ec7UL, +0xdd3e05aeUL, 0xd33708a5UL, 0xc12c1fb8UL, 0xcf2512b3UL, 0xe51a3182UL, 0xeb133c89UL, 0xf9082b94UL, 0xf701269fUL, +0x4de6bd46UL, 0x43efb04dUL, 0x51f4a750UL, 0x5ffdaa5bUL, 0x75c2896aUL, 0x7bcb8461UL, 0x69d0937cUL, 0x67d99e77UL, +0x3daed51eUL, 0x33a7d815UL, 0x21bccf08UL, 0x2fb5c203UL, 0x058ae132UL, 0x0b83ec39UL, 0x1998fb24UL, 0x1791f62fUL, +0x764dd68dUL, 0x7844db86UL, 0x6a5fcc9bUL, 0x6456c190UL, 0x4e69e2a1UL, 0x4060efaaUL, 0x527bf8b7UL, 0x5c72f5bcUL, +0x0605bed5UL, 0x080cb3deUL, 0x1a17a4c3UL, 0x141ea9c8UL, 0x3e218af9UL, 0x302887f2UL, 0x223390efUL, 0x2c3a9de4UL, +0x96dd063dUL, 0x98d40b36UL, 0x8acf1c2bUL, 0x84c61120UL, 0xaef93211UL, 0xa0f03f1aUL, 0xb2eb2807UL, 0xbce2250cUL, +0xe6956e65UL, 0xe89c636eUL, 0xfa877473UL, 0xf48e7978UL, 0xdeb15a49UL, 0xd0b85742UL, 0xc2a3405fUL, 0xccaa4d54UL, +0x41ecdaf7UL, 0x4fe5d7fcUL, 0x5dfec0e1UL, 0x53f7cdeaUL, 0x79c8eedbUL, 0x77c1e3d0UL, 0x65daf4cdUL, 0x6bd3f9c6UL, +0x31a4b2afUL, 0x3fadbfa4UL, 0x2db6a8b9UL, 0x23bfa5b2UL, 0x09808683UL, 0x07898b88UL, 0x15929c95UL, 0x1b9b919eUL, +0xa17c0a47UL, 0xaf75074cUL, 0xbd6e1051UL, 0xb3671d5aUL, 0x99583e6bUL, 0x97513360UL, 0x854a247dUL, 0x8b432976UL, +0xd134621fUL, 0xdf3d6f14UL, 0xcd267809UL, 0xc32f7502UL, 0xe9105633UL, 0xe7195b38UL, 0xf5024c25UL, 0xfb0b412eUL, +0x9ad7618cUL, 0x94de6c87UL, 0x86c57b9aUL, 0x88cc7691UL, 0xa2f355a0UL, 0xacfa58abUL, 0xbee14fb6UL, 0xb0e842bdUL, +0xea9f09d4UL, 0xe49604dfUL, 0xf68d13c2UL, 0xf8841ec9UL, 0xd2bb3df8UL, 0xdcb230f3UL, 0xcea927eeUL, 0xc0a02ae5UL, +0x7a47b13cUL, 0x744ebc37UL, 0x6655ab2aUL, 0x685ca621UL, 0x42638510UL, 0x4c6a881bUL, 0x5e719f06UL, 0x5078920dUL, +0x0a0fd964UL, 0x0406d46fUL, 0x161dc372UL, 0x1814ce79UL, 0x322bed48UL, 0x3c22e043UL, 0x2e39f75eUL, 0x2030fa55UL, +0xec9ab701UL, 0xe293ba0aUL, 0xf088ad17UL, 0xfe81a01cUL, 0xd4be832dUL, 0xdab78e26UL, 0xc8ac993bUL, 0xc6a59430UL, +0x9cd2df59UL, 0x92dbd252UL, 0x80c0c54fUL, 0x8ec9c844UL, 0xa4f6eb75UL, 0xaaffe67eUL, 0xb8e4f163UL, 0xb6edfc68UL, +0x0c0a67b1UL, 0x02036abaUL, 0x10187da7UL, 0x1e1170acUL, 0x342e539dUL, 0x3a275e96UL, 0x283c498bUL, 0x26354480UL, +0x7c420fe9UL, 0x724b02e2UL, 0x605015ffUL, 0x6e5918f4UL, 0x44663bc5UL, 0x4a6f36ceUL, 0x587421d3UL, 0x567d2cd8UL, +0x37a10c7aUL, 0x39a80171UL, 0x2bb3166cUL, 0x25ba1b67UL, 0x0f853856UL, 0x018c355dUL, 0x13972240UL, 0x1d9e2f4bUL, +0x47e96422UL, 0x49e06929UL, 0x5bfb7e34UL, 0x55f2733fUL, 0x7fcd500eUL, 0x71c45d05UL, 0x63df4a18UL, 0x6dd64713UL, +0xd731dccaUL, 0xd938d1c1UL, 0xcb23c6dcUL, 0xc52acbd7UL, 0xef15e8e6UL, 0xe11ce5edUL, 0xf307f2f0UL, 0xfd0efffbUL, +0xa779b492UL, 0xa970b999UL, 0xbb6bae84UL, 0xb562a38fUL, 0x9f5d80beUL, 0x91548db5UL, 0x834f9aa8UL, 0x8d4697a3UL +}; + +static const ulong32 Tks1[] = { +0x00000000UL, 0x0b0e090dUL, 0x161c121aUL, 0x1d121b17UL, 0x2c382434UL, 0x27362d39UL, 0x3a24362eUL, 0x312a3f23UL, +0x58704868UL, 0x537e4165UL, 0x4e6c5a72UL, 0x4562537fUL, 0x74486c5cUL, 0x7f466551UL, 0x62547e46UL, 0x695a774bUL, +0xb0e090d0UL, 0xbbee99ddUL, 0xa6fc82caUL, 0xadf28bc7UL, 0x9cd8b4e4UL, 0x97d6bde9UL, 0x8ac4a6feUL, 0x81caaff3UL, +0xe890d8b8UL, 0xe39ed1b5UL, 0xfe8ccaa2UL, 0xf582c3afUL, 0xc4a8fc8cUL, 0xcfa6f581UL, 0xd2b4ee96UL, 0xd9bae79bUL, +0x7bdb3bbbUL, 0x70d532b6UL, 0x6dc729a1UL, 0x66c920acUL, 0x57e31f8fUL, 0x5ced1682UL, 0x41ff0d95UL, 0x4af10498UL, +0x23ab73d3UL, 0x28a57adeUL, 0x35b761c9UL, 0x3eb968c4UL, 0x0f9357e7UL, 0x049d5eeaUL, 0x198f45fdUL, 0x12814cf0UL, +0xcb3bab6bUL, 0xc035a266UL, 0xdd27b971UL, 0xd629b07cUL, 0xe7038f5fUL, 0xec0d8652UL, 0xf11f9d45UL, 0xfa119448UL, +0x934be303UL, 0x9845ea0eUL, 0x8557f119UL, 0x8e59f814UL, 0xbf73c737UL, 0xb47dce3aUL, 0xa96fd52dUL, 0xa261dc20UL, +0xf6ad766dUL, 0xfda37f60UL, 0xe0b16477UL, 0xebbf6d7aUL, 0xda955259UL, 0xd19b5b54UL, 0xcc894043UL, 0xc787494eUL, +0xaedd3e05UL, 0xa5d33708UL, 0xb8c12c1fUL, 0xb3cf2512UL, 0x82e51a31UL, 0x89eb133cUL, 0x94f9082bUL, 0x9ff70126UL, +0x464de6bdUL, 0x4d43efb0UL, 0x5051f4a7UL, 0x5b5ffdaaUL, 0x6a75c289UL, 0x617bcb84UL, 0x7c69d093UL, 0x7767d99eUL, +0x1e3daed5UL, 0x1533a7d8UL, 0x0821bccfUL, 0x032fb5c2UL, 0x32058ae1UL, 0x390b83ecUL, 0x241998fbUL, 0x2f1791f6UL, +0x8d764dd6UL, 0x867844dbUL, 0x9b6a5fccUL, 0x906456c1UL, 0xa14e69e2UL, 0xaa4060efUL, 0xb7527bf8UL, 0xbc5c72f5UL, +0xd50605beUL, 0xde080cb3UL, 0xc31a17a4UL, 0xc8141ea9UL, 0xf93e218aUL, 0xf2302887UL, 0xef223390UL, 0xe42c3a9dUL, +0x3d96dd06UL, 0x3698d40bUL, 0x2b8acf1cUL, 0x2084c611UL, 0x11aef932UL, 0x1aa0f03fUL, 0x07b2eb28UL, 0x0cbce225UL, +0x65e6956eUL, 0x6ee89c63UL, 0x73fa8774UL, 0x78f48e79UL, 0x49deb15aUL, 0x42d0b857UL, 0x5fc2a340UL, 0x54ccaa4dUL, +0xf741ecdaUL, 0xfc4fe5d7UL, 0xe15dfec0UL, 0xea53f7cdUL, 0xdb79c8eeUL, 0xd077c1e3UL, 0xcd65daf4UL, 0xc66bd3f9UL, +0xaf31a4b2UL, 0xa43fadbfUL, 0xb92db6a8UL, 0xb223bfa5UL, 0x83098086UL, 0x8807898bUL, 0x9515929cUL, 0x9e1b9b91UL, +0x47a17c0aUL, 0x4caf7507UL, 0x51bd6e10UL, 0x5ab3671dUL, 0x6b99583eUL, 0x60975133UL, 0x7d854a24UL, 0x768b4329UL, +0x1fd13462UL, 0x14df3d6fUL, 0x09cd2678UL, 0x02c32f75UL, 0x33e91056UL, 0x38e7195bUL, 0x25f5024cUL, 0x2efb0b41UL, +0x8c9ad761UL, 0x8794de6cUL, 0x9a86c57bUL, 0x9188cc76UL, 0xa0a2f355UL, 0xabacfa58UL, 0xb6bee14fUL, 0xbdb0e842UL, +0xd4ea9f09UL, 0xdfe49604UL, 0xc2f68d13UL, 0xc9f8841eUL, 0xf8d2bb3dUL, 0xf3dcb230UL, 0xeecea927UL, 0xe5c0a02aUL, +0x3c7a47b1UL, 0x37744ebcUL, 0x2a6655abUL, 0x21685ca6UL, 0x10426385UL, 0x1b4c6a88UL, 0x065e719fUL, 0x0d507892UL, +0x640a0fd9UL, 0x6f0406d4UL, 0x72161dc3UL, 0x791814ceUL, 0x48322bedUL, 0x433c22e0UL, 0x5e2e39f7UL, 0x552030faUL, +0x01ec9ab7UL, 0x0ae293baUL, 0x17f088adUL, 0x1cfe81a0UL, 0x2dd4be83UL, 0x26dab78eUL, 0x3bc8ac99UL, 0x30c6a594UL, +0x599cd2dfUL, 0x5292dbd2UL, 0x4f80c0c5UL, 0x448ec9c8UL, 0x75a4f6ebUL, 0x7eaaffe6UL, 0x63b8e4f1UL, 0x68b6edfcUL, +0xb10c0a67UL, 0xba02036aUL, 0xa710187dUL, 0xac1e1170UL, 0x9d342e53UL, 0x963a275eUL, 0x8b283c49UL, 0x80263544UL, +0xe97c420fUL, 0xe2724b02UL, 0xff605015UL, 0xf46e5918UL, 0xc544663bUL, 0xce4a6f36UL, 0xd3587421UL, 0xd8567d2cUL, +0x7a37a10cUL, 0x7139a801UL, 0x6c2bb316UL, 0x6725ba1bUL, 0x560f8538UL, 0x5d018c35UL, 0x40139722UL, 0x4b1d9e2fUL, +0x2247e964UL, 0x2949e069UL, 0x345bfb7eUL, 0x3f55f273UL, 0x0e7fcd50UL, 0x0571c45dUL, 0x1863df4aUL, 0x136dd647UL, +0xcad731dcUL, 0xc1d938d1UL, 0xdccb23c6UL, 0xd7c52acbUL, 0xe6ef15e8UL, 0xede11ce5UL, 0xf0f307f2UL, 0xfbfd0effUL, +0x92a779b4UL, 0x99a970b9UL, 0x84bb6baeUL, 0x8fb562a3UL, 0xbe9f5d80UL, 0xb591548dUL, 0xa8834f9aUL, 0xa38d4697UL +}; + +static const ulong32 Tks2[] = { +0x00000000UL, 0x0d0b0e09UL, 0x1a161c12UL, 0x171d121bUL, 0x342c3824UL, 0x3927362dUL, 0x2e3a2436UL, 0x23312a3fUL, +0x68587048UL, 0x65537e41UL, 0x724e6c5aUL, 0x7f456253UL, 0x5c74486cUL, 0x517f4665UL, 0x4662547eUL, 0x4b695a77UL, +0xd0b0e090UL, 0xddbbee99UL, 0xcaa6fc82UL, 0xc7adf28bUL, 0xe49cd8b4UL, 0xe997d6bdUL, 0xfe8ac4a6UL, 0xf381caafUL, +0xb8e890d8UL, 0xb5e39ed1UL, 0xa2fe8ccaUL, 0xaff582c3UL, 0x8cc4a8fcUL, 0x81cfa6f5UL, 0x96d2b4eeUL, 0x9bd9bae7UL, +0xbb7bdb3bUL, 0xb670d532UL, 0xa16dc729UL, 0xac66c920UL, 0x8f57e31fUL, 0x825ced16UL, 0x9541ff0dUL, 0x984af104UL, +0xd323ab73UL, 0xde28a57aUL, 0xc935b761UL, 0xc43eb968UL, 0xe70f9357UL, 0xea049d5eUL, 0xfd198f45UL, 0xf012814cUL, +0x6bcb3babUL, 0x66c035a2UL, 0x71dd27b9UL, 0x7cd629b0UL, 0x5fe7038fUL, 0x52ec0d86UL, 0x45f11f9dUL, 0x48fa1194UL, +0x03934be3UL, 0x0e9845eaUL, 0x198557f1UL, 0x148e59f8UL, 0x37bf73c7UL, 0x3ab47dceUL, 0x2da96fd5UL, 0x20a261dcUL, +0x6df6ad76UL, 0x60fda37fUL, 0x77e0b164UL, 0x7aebbf6dUL, 0x59da9552UL, 0x54d19b5bUL, 0x43cc8940UL, 0x4ec78749UL, +0x05aedd3eUL, 0x08a5d337UL, 0x1fb8c12cUL, 0x12b3cf25UL, 0x3182e51aUL, 0x3c89eb13UL, 0x2b94f908UL, 0x269ff701UL, +0xbd464de6UL, 0xb04d43efUL, 0xa75051f4UL, 0xaa5b5ffdUL, 0x896a75c2UL, 0x84617bcbUL, 0x937c69d0UL, 0x9e7767d9UL, +0xd51e3daeUL, 0xd81533a7UL, 0xcf0821bcUL, 0xc2032fb5UL, 0xe132058aUL, 0xec390b83UL, 0xfb241998UL, 0xf62f1791UL, +0xd68d764dUL, 0xdb867844UL, 0xcc9b6a5fUL, 0xc1906456UL, 0xe2a14e69UL, 0xefaa4060UL, 0xf8b7527bUL, 0xf5bc5c72UL, +0xbed50605UL, 0xb3de080cUL, 0xa4c31a17UL, 0xa9c8141eUL, 0x8af93e21UL, 0x87f23028UL, 0x90ef2233UL, 0x9de42c3aUL, +0x063d96ddUL, 0x0b3698d4UL, 0x1c2b8acfUL, 0x112084c6UL, 0x3211aef9UL, 0x3f1aa0f0UL, 0x2807b2ebUL, 0x250cbce2UL, +0x6e65e695UL, 0x636ee89cUL, 0x7473fa87UL, 0x7978f48eUL, 0x5a49deb1UL, 0x5742d0b8UL, 0x405fc2a3UL, 0x4d54ccaaUL, +0xdaf741ecUL, 0xd7fc4fe5UL, 0xc0e15dfeUL, 0xcdea53f7UL, 0xeedb79c8UL, 0xe3d077c1UL, 0xf4cd65daUL, 0xf9c66bd3UL, +0xb2af31a4UL, 0xbfa43fadUL, 0xa8b92db6UL, 0xa5b223bfUL, 0x86830980UL, 0x8b880789UL, 0x9c951592UL, 0x919e1b9bUL, +0x0a47a17cUL, 0x074caf75UL, 0x1051bd6eUL, 0x1d5ab367UL, 0x3e6b9958UL, 0x33609751UL, 0x247d854aUL, 0x29768b43UL, +0x621fd134UL, 0x6f14df3dUL, 0x7809cd26UL, 0x7502c32fUL, 0x5633e910UL, 0x5b38e719UL, 0x4c25f502UL, 0x412efb0bUL, +0x618c9ad7UL, 0x6c8794deUL, 0x7b9a86c5UL, 0x769188ccUL, 0x55a0a2f3UL, 0x58abacfaUL, 0x4fb6bee1UL, 0x42bdb0e8UL, +0x09d4ea9fUL, 0x04dfe496UL, 0x13c2f68dUL, 0x1ec9f884UL, 0x3df8d2bbUL, 0x30f3dcb2UL, 0x27eecea9UL, 0x2ae5c0a0UL, +0xb13c7a47UL, 0xbc37744eUL, 0xab2a6655UL, 0xa621685cUL, 0x85104263UL, 0x881b4c6aUL, 0x9f065e71UL, 0x920d5078UL, +0xd9640a0fUL, 0xd46f0406UL, 0xc372161dUL, 0xce791814UL, 0xed48322bUL, 0xe0433c22UL, 0xf75e2e39UL, 0xfa552030UL, +0xb701ec9aUL, 0xba0ae293UL, 0xad17f088UL, 0xa01cfe81UL, 0x832dd4beUL, 0x8e26dab7UL, 0x993bc8acUL, 0x9430c6a5UL, +0xdf599cd2UL, 0xd25292dbUL, 0xc54f80c0UL, 0xc8448ec9UL, 0xeb75a4f6UL, 0xe67eaaffUL, 0xf163b8e4UL, 0xfc68b6edUL, +0x67b10c0aUL, 0x6aba0203UL, 0x7da71018UL, 0x70ac1e11UL, 0x539d342eUL, 0x5e963a27UL, 0x498b283cUL, 0x44802635UL, +0x0fe97c42UL, 0x02e2724bUL, 0x15ff6050UL, 0x18f46e59UL, 0x3bc54466UL, 0x36ce4a6fUL, 0x21d35874UL, 0x2cd8567dUL, +0x0c7a37a1UL, 0x017139a8UL, 0x166c2bb3UL, 0x1b6725baUL, 0x38560f85UL, 0x355d018cUL, 0x22401397UL, 0x2f4b1d9eUL, +0x642247e9UL, 0x692949e0UL, 0x7e345bfbUL, 0x733f55f2UL, 0x500e7fcdUL, 0x5d0571c4UL, 0x4a1863dfUL, 0x47136dd6UL, +0xdccad731UL, 0xd1c1d938UL, 0xc6dccb23UL, 0xcbd7c52aUL, 0xe8e6ef15UL, 0xe5ede11cUL, 0xf2f0f307UL, 0xfffbfd0eUL, +0xb492a779UL, 0xb999a970UL, 0xae84bb6bUL, 0xa38fb562UL, 0x80be9f5dUL, 0x8db59154UL, 0x9aa8834fUL, 0x97a38d46UL +}; + +static const ulong32 Tks3[] = { +0x00000000UL, 0x090d0b0eUL, 0x121a161cUL, 0x1b171d12UL, 0x24342c38UL, 0x2d392736UL, 0x362e3a24UL, 0x3f23312aUL, +0x48685870UL, 0x4165537eUL, 0x5a724e6cUL, 0x537f4562UL, 0x6c5c7448UL, 0x65517f46UL, 0x7e466254UL, 0x774b695aUL, +0x90d0b0e0UL, 0x99ddbbeeUL, 0x82caa6fcUL, 0x8bc7adf2UL, 0xb4e49cd8UL, 0xbde997d6UL, 0xa6fe8ac4UL, 0xaff381caUL, +0xd8b8e890UL, 0xd1b5e39eUL, 0xcaa2fe8cUL, 0xc3aff582UL, 0xfc8cc4a8UL, 0xf581cfa6UL, 0xee96d2b4UL, 0xe79bd9baUL, +0x3bbb7bdbUL, 0x32b670d5UL, 0x29a16dc7UL, 0x20ac66c9UL, 0x1f8f57e3UL, 0x16825cedUL, 0x0d9541ffUL, 0x04984af1UL, +0x73d323abUL, 0x7ade28a5UL, 0x61c935b7UL, 0x68c43eb9UL, 0x57e70f93UL, 0x5eea049dUL, 0x45fd198fUL, 0x4cf01281UL, +0xab6bcb3bUL, 0xa266c035UL, 0xb971dd27UL, 0xb07cd629UL, 0x8f5fe703UL, 0x8652ec0dUL, 0x9d45f11fUL, 0x9448fa11UL, +0xe303934bUL, 0xea0e9845UL, 0xf1198557UL, 0xf8148e59UL, 0xc737bf73UL, 0xce3ab47dUL, 0xd52da96fUL, 0xdc20a261UL, +0x766df6adUL, 0x7f60fda3UL, 0x6477e0b1UL, 0x6d7aebbfUL, 0x5259da95UL, 0x5b54d19bUL, 0x4043cc89UL, 0x494ec787UL, +0x3e05aeddUL, 0x3708a5d3UL, 0x2c1fb8c1UL, 0x2512b3cfUL, 0x1a3182e5UL, 0x133c89ebUL, 0x082b94f9UL, 0x01269ff7UL, +0xe6bd464dUL, 0xefb04d43UL, 0xf4a75051UL, 0xfdaa5b5fUL, 0xc2896a75UL, 0xcb84617bUL, 0xd0937c69UL, 0xd99e7767UL, +0xaed51e3dUL, 0xa7d81533UL, 0xbccf0821UL, 0xb5c2032fUL, 0x8ae13205UL, 0x83ec390bUL, 0x98fb2419UL, 0x91f62f17UL, +0x4dd68d76UL, 0x44db8678UL, 0x5fcc9b6aUL, 0x56c19064UL, 0x69e2a14eUL, 0x60efaa40UL, 0x7bf8b752UL, 0x72f5bc5cUL, +0x05bed506UL, 0x0cb3de08UL, 0x17a4c31aUL, 0x1ea9c814UL, 0x218af93eUL, 0x2887f230UL, 0x3390ef22UL, 0x3a9de42cUL, +0xdd063d96UL, 0xd40b3698UL, 0xcf1c2b8aUL, 0xc6112084UL, 0xf93211aeUL, 0xf03f1aa0UL, 0xeb2807b2UL, 0xe2250cbcUL, +0x956e65e6UL, 0x9c636ee8UL, 0x877473faUL, 0x8e7978f4UL, 0xb15a49deUL, 0xb85742d0UL, 0xa3405fc2UL, 0xaa4d54ccUL, +0xecdaf741UL, 0xe5d7fc4fUL, 0xfec0e15dUL, 0xf7cdea53UL, 0xc8eedb79UL, 0xc1e3d077UL, 0xdaf4cd65UL, 0xd3f9c66bUL, +0xa4b2af31UL, 0xadbfa43fUL, 0xb6a8b92dUL, 0xbfa5b223UL, 0x80868309UL, 0x898b8807UL, 0x929c9515UL, 0x9b919e1bUL, +0x7c0a47a1UL, 0x75074cafUL, 0x6e1051bdUL, 0x671d5ab3UL, 0x583e6b99UL, 0x51336097UL, 0x4a247d85UL, 0x4329768bUL, +0x34621fd1UL, 0x3d6f14dfUL, 0x267809cdUL, 0x2f7502c3UL, 0x105633e9UL, 0x195b38e7UL, 0x024c25f5UL, 0x0b412efbUL, +0xd7618c9aUL, 0xde6c8794UL, 0xc57b9a86UL, 0xcc769188UL, 0xf355a0a2UL, 0xfa58abacUL, 0xe14fb6beUL, 0xe842bdb0UL, +0x9f09d4eaUL, 0x9604dfe4UL, 0x8d13c2f6UL, 0x841ec9f8UL, 0xbb3df8d2UL, 0xb230f3dcUL, 0xa927eeceUL, 0xa02ae5c0UL, +0x47b13c7aUL, 0x4ebc3774UL, 0x55ab2a66UL, 0x5ca62168UL, 0x63851042UL, 0x6a881b4cUL, 0x719f065eUL, 0x78920d50UL, +0x0fd9640aUL, 0x06d46f04UL, 0x1dc37216UL, 0x14ce7918UL, 0x2bed4832UL, 0x22e0433cUL, 0x39f75e2eUL, 0x30fa5520UL, +0x9ab701ecUL, 0x93ba0ae2UL, 0x88ad17f0UL, 0x81a01cfeUL, 0xbe832dd4UL, 0xb78e26daUL, 0xac993bc8UL, 0xa59430c6UL, +0xd2df599cUL, 0xdbd25292UL, 0xc0c54f80UL, 0xc9c8448eUL, 0xf6eb75a4UL, 0xffe67eaaUL, 0xe4f163b8UL, 0xedfc68b6UL, +0x0a67b10cUL, 0x036aba02UL, 0x187da710UL, 0x1170ac1eUL, 0x2e539d34UL, 0x275e963aUL, 0x3c498b28UL, 0x35448026UL, +0x420fe97cUL, 0x4b02e272UL, 0x5015ff60UL, 0x5918f46eUL, 0x663bc544UL, 0x6f36ce4aUL, 0x7421d358UL, 0x7d2cd856UL, +0xa10c7a37UL, 0xa8017139UL, 0xb3166c2bUL, 0xba1b6725UL, 0x8538560fUL, 0x8c355d01UL, 0x97224013UL, 0x9e2f4b1dUL, +0xe9642247UL, 0xe0692949UL, 0xfb7e345bUL, 0xf2733f55UL, 0xcd500e7fUL, 0xc45d0571UL, 0xdf4a1863UL, 0xd647136dUL, +0x31dccad7UL, 0x38d1c1d9UL, 0x23c6dccbUL, 0x2acbd7c5UL, 0x15e8e6efUL, 0x1ce5ede1UL, 0x07f2f0f3UL, 0x0efffbfdUL, +0x79b492a7UL, 0x70b999a9UL, 0x6bae84bbUL, 0x62a38fb5UL, 0x5d80be9fUL, 0x548db591UL, 0x4f9aa883UL, 0x4697a38dUL +}; + +#endif /* ENCRYPT_ONLY */ + +#endif /* SMALL CODE */ + +static const ulong32 rcon[] = { + 0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL, + 0x10000000UL, 0x20000000UL, 0x40000000UL, 0x80000000UL, + 0x1B000000UL, 0x36000000UL, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; + +#endif /* __LTC_AES_TAB_C__ */ + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* AES implementation by Tom St Denis + * + * Derived from the Public Domain source code by + +--- + * rijndael-alg-fst.c + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto +--- + */ +/** + @file aes.c + Implementation of AES +*/ + + + +#ifdef LTC_RIJNDAEL + +#ifndef ENCRYPT_ONLY + +#define SETUP rijndael_setup +#define ECB_ENC rijndael_ecb_encrypt +#define ECB_DEC rijndael_ecb_decrypt +#define ECB_DONE rijndael_done +#define ECB_TEST rijndael_test +#define ECB_KS rijndael_keysize + +const struct ltc_cipher_descriptor rijndael_desc = +{ + "rijndael", + 6, + 16, 32, 16, 10, + SETUP, ECB_ENC, ECB_DEC, ECB_TEST, ECB_DONE, ECB_KS, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +const struct ltc_cipher_descriptor aes_desc = +{ + "aes", + 6, + 16, 32, 16, 10, + SETUP, ECB_ENC, ECB_DEC, ECB_TEST, ECB_DONE, ECB_KS, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +#else + +#define SETUP rijndael_enc_setup +#define ECB_ENC rijndael_enc_ecb_encrypt +#define ECB_KS rijndael_enc_keysize +#define ECB_DONE rijndael_enc_done + +const struct ltc_cipher_descriptor rijndael_enc_desc = +{ + "rijndael", + 6, + 16, 32, 16, 10, + SETUP, ECB_ENC, NULL, NULL, ECB_DONE, ECB_KS, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +const struct ltc_cipher_descriptor aes_enc_desc = +{ + "aes", + 6, + 16, 32, 16, 10, + SETUP, ECB_ENC, NULL, NULL, ECB_DONE, ECB_KS, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +#endif + +#define __LTC_AES_TAB_C__ + + +static ulong32 setup_mix(ulong32 temp) +{ + return (Te4_3[extract_byte(temp, 2)]) ^ + (Te4_2[extract_byte(temp, 1)]) ^ + (Te4_1[extract_byte(temp, 0)]) ^ + (Te4_0[extract_byte(temp, 3)]); +} + +#ifndef ENCRYPT_ONLY +#ifdef LTC_SMALL_CODE +static ulong32 setup_mix2(ulong32 temp) +{ + return Td0(255 & Te4[extract_byte(temp, 3)]) ^ + Td1(255 & Te4[extract_byte(temp, 2)]) ^ + Td2(255 & Te4[extract_byte(temp, 1)]) ^ + Td3(255 & Te4[extract_byte(temp, 0)]); +} +#endif +#endif + + /** + Initialize the AES (Rijndael) block cipher + @param key The symmetric key you wish to pass + @param keylen The key length in bytes + @param num_rounds The number of rounds desired (0 for default) + @param skey The key in as scheduled by this function. + @return CRYPT_OK if successful + */ +int SETUP(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey) +{ + int i; + ulong32 temp, *rk; +#ifndef ENCRYPT_ONLY + ulong32 *rrk; +#endif + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(skey != NULL); + + if (keylen != 16 && keylen != 24 && keylen != 32) { + return CRYPT_INVALID_KEYSIZE; + } + + if (num_rounds != 0 && num_rounds != (10 + ((keylen/8)-2)*2)) { + return CRYPT_INVALID_ROUNDS; + } + + skey->rijndael.Nr = 10 + ((keylen/8)-2)*2; + + /* setup the forward key */ + i = 0; + rk = skey->rijndael.eK; + LOAD32H(rk[0], key ); + LOAD32H(rk[1], key + 4); + LOAD32H(rk[2], key + 8); + LOAD32H(rk[3], key + 12); + if (keylen == 16) { + for (;;) { + temp = rk[3]; + rk[4] = rk[0] ^ setup_mix(temp) ^ rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if (++i == 10) { + break; + } + rk += 4; + } + } else if (keylen == 24) { + LOAD32H(rk[4], key + 16); + LOAD32H(rk[5], key + 20); + for (;;) { + #ifdef _MSC_VER + temp = skey->rijndael.eK[rk - skey->rijndael.eK + 5]; + #else + temp = rk[5]; + #endif + rk[ 6] = rk[ 0] ^ setup_mix(temp) ^ rcon[i]; + rk[ 7] = rk[ 1] ^ rk[ 6]; + rk[ 8] = rk[ 2] ^ rk[ 7]; + rk[ 9] = rk[ 3] ^ rk[ 8]; + if (++i == 8) { + break; + } + rk[10] = rk[ 4] ^ rk[ 9]; + rk[11] = rk[ 5] ^ rk[10]; + rk += 6; + } + } else if (keylen == 32) { + LOAD32H(rk[4], key + 16); + LOAD32H(rk[5], key + 20); + LOAD32H(rk[6], key + 24); + LOAD32H(rk[7], key + 28); + for (;;) { + #ifdef _MSC_VER + temp = skey->rijndael.eK[rk - skey->rijndael.eK + 7]; + #else + temp = rk[7]; + #endif + rk[ 8] = rk[ 0] ^ setup_mix(temp) ^ rcon[i]; + rk[ 9] = rk[ 1] ^ rk[ 8]; + rk[10] = rk[ 2] ^ rk[ 9]; + rk[11] = rk[ 3] ^ rk[10]; + if (++i == 7) { + break; + } + temp = rk[11]; + rk[12] = rk[ 4] ^ setup_mix(RORc(temp, 8)); + rk[13] = rk[ 5] ^ rk[12]; + rk[14] = rk[ 6] ^ rk[13]; + rk[15] = rk[ 7] ^ rk[14]; + rk += 8; + } + } else { + /* this can't happen */ + /* coverity[dead_error_line] */ + return CRYPT_ERROR; + } + +#ifndef ENCRYPT_ONLY + /* setup the inverse key now */ + rk = skey->rijndael.dK; + rrk = skey->rijndael.eK + (28 + keylen) - 4; + + /* apply the inverse MixColumn transform to all round keys but the first and the last: */ + /* copy first */ + *rk++ = *rrk++; + *rk++ = *rrk++; + *rk++ = *rrk++; + *rk = *rrk; + rk -= 3; rrk -= 3; + + for (i = 1; i < skey->rijndael.Nr; i++) { + rrk -= 4; + rk += 4; + #ifdef LTC_SMALL_CODE + temp = rrk[0]; + rk[0] = setup_mix2(temp); + temp = rrk[1]; + rk[1] = setup_mix2(temp); + temp = rrk[2]; + rk[2] = setup_mix2(temp); + temp = rrk[3]; + rk[3] = setup_mix2(temp); + #else + temp = rrk[0]; + rk[0] = + Tks0[extract_byte(temp, 3)] ^ + Tks1[extract_byte(temp, 2)] ^ + Tks2[extract_byte(temp, 1)] ^ + Tks3[extract_byte(temp, 0)]; + temp = rrk[1]; + rk[1] = + Tks0[extract_byte(temp, 3)] ^ + Tks1[extract_byte(temp, 2)] ^ + Tks2[extract_byte(temp, 1)] ^ + Tks3[extract_byte(temp, 0)]; + temp = rrk[2]; + rk[2] = + Tks0[extract_byte(temp, 3)] ^ + Tks1[extract_byte(temp, 2)] ^ + Tks2[extract_byte(temp, 1)] ^ + Tks3[extract_byte(temp, 0)]; + temp = rrk[3]; + rk[3] = + Tks0[extract_byte(temp, 3)] ^ + Tks1[extract_byte(temp, 2)] ^ + Tks2[extract_byte(temp, 1)] ^ + Tks3[extract_byte(temp, 0)]; + #endif + + } + + /* copy last */ + rrk -= 4; + rk += 4; + *rk++ = *rrk++; + *rk++ = *rrk++; + *rk++ = *rrk++; + *rk = *rrk; +#endif /* ENCRYPT_ONLY */ + + return CRYPT_OK; +} + +/** + Encrypts a block of text with AES + @param pt The input plaintext (16 bytes) + @param ct The output ciphertext (16 bytes) + @param skey The key as scheduled + @return CRYPT_OK if successful +*/ +#ifdef LTC_CLEAN_STACK +static int _rijndael_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey) +#else +int ECB_ENC(const unsigned char *pt, unsigned char *ct, symmetric_key *skey) +#endif +{ + ulong32 s0, s1, s2, s3, t0, t1, t2, t3, *rk; + int Nr, r; + + LTC_ARGCHK(pt != NULL); + LTC_ARGCHK(ct != NULL); + LTC_ARGCHK(skey != NULL); + + Nr = skey->rijndael.Nr; + rk = skey->rijndael.eK; + + /* + * map byte array block to cipher state + * and add initial round key: + */ + LOAD32H(s0, pt ); s0 ^= rk[0]; + LOAD32H(s1, pt + 4); s1 ^= rk[1]; + LOAD32H(s2, pt + 8); s2 ^= rk[2]; + LOAD32H(s3, pt + 12); s3 ^= rk[3]; + +#ifdef LTC_SMALL_CODE + + for (r = 0; ; r++) { + rk += 4; + t0 = + Te0(extract_byte(s0, 3)) ^ + Te1(extract_byte(s1, 2)) ^ + Te2(extract_byte(s2, 1)) ^ + Te3(extract_byte(s3, 0)) ^ + rk[0]; + t1 = + Te0(extract_byte(s1, 3)) ^ + Te1(extract_byte(s2, 2)) ^ + Te2(extract_byte(s3, 1)) ^ + Te3(extract_byte(s0, 0)) ^ + rk[1]; + t2 = + Te0(extract_byte(s2, 3)) ^ + Te1(extract_byte(s3, 2)) ^ + Te2(extract_byte(s0, 1)) ^ + Te3(extract_byte(s1, 0)) ^ + rk[2]; + t3 = + Te0(extract_byte(s3, 3)) ^ + Te1(extract_byte(s0, 2)) ^ + Te2(extract_byte(s1, 1)) ^ + Te3(extract_byte(s2, 0)) ^ + rk[3]; + if (r == Nr-2) { + break; + } + s0 = t0; s1 = t1; s2 = t2; s3 = t3; + } + rk += 4; + +#else + + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = + Te0(extract_byte(s0, 3)) ^ + Te1(extract_byte(s1, 2)) ^ + Te2(extract_byte(s2, 1)) ^ + Te3(extract_byte(s3, 0)) ^ + rk[4]; + t1 = + Te0(extract_byte(s1, 3)) ^ + Te1(extract_byte(s2, 2)) ^ + Te2(extract_byte(s3, 1)) ^ + Te3(extract_byte(s0, 0)) ^ + rk[5]; + t2 = + Te0(extract_byte(s2, 3)) ^ + Te1(extract_byte(s3, 2)) ^ + Te2(extract_byte(s0, 1)) ^ + Te3(extract_byte(s1, 0)) ^ + rk[6]; + t3 = + Te0(extract_byte(s3, 3)) ^ + Te1(extract_byte(s0, 2)) ^ + Te2(extract_byte(s1, 1)) ^ + Te3(extract_byte(s2, 0)) ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Te0(extract_byte(t0, 3)) ^ + Te1(extract_byte(t1, 2)) ^ + Te2(extract_byte(t2, 1)) ^ + Te3(extract_byte(t3, 0)) ^ + rk[0]; + s1 = + Te0(extract_byte(t1, 3)) ^ + Te1(extract_byte(t2, 2)) ^ + Te2(extract_byte(t3, 1)) ^ + Te3(extract_byte(t0, 0)) ^ + rk[1]; + s2 = + Te0(extract_byte(t2, 3)) ^ + Te1(extract_byte(t3, 2)) ^ + Te2(extract_byte(t0, 1)) ^ + Te3(extract_byte(t1, 0)) ^ + rk[2]; + s3 = + Te0(extract_byte(t3, 3)) ^ + Te1(extract_byte(t0, 2)) ^ + Te2(extract_byte(t1, 1)) ^ + Te3(extract_byte(t2, 0)) ^ + rk[3]; + } + +#endif + + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te4_3[extract_byte(t0, 3)]) ^ + (Te4_2[extract_byte(t1, 2)]) ^ + (Te4_1[extract_byte(t2, 1)]) ^ + (Te4_0[extract_byte(t3, 0)]) ^ + rk[0]; + STORE32H(s0, ct); + s1 = + (Te4_3[extract_byte(t1, 3)]) ^ + (Te4_2[extract_byte(t2, 2)]) ^ + (Te4_1[extract_byte(t3, 1)]) ^ + (Te4_0[extract_byte(t0, 0)]) ^ + rk[1]; + STORE32H(s1, ct+4); + s2 = + (Te4_3[extract_byte(t2, 3)]) ^ + (Te4_2[extract_byte(t3, 2)]) ^ + (Te4_1[extract_byte(t0, 1)]) ^ + (Te4_0[extract_byte(t1, 0)]) ^ + rk[2]; + STORE32H(s2, ct+8); + s3 = + (Te4_3[extract_byte(t3, 3)]) ^ + (Te4_2[extract_byte(t0, 2)]) ^ + (Te4_1[extract_byte(t1, 1)]) ^ + (Te4_0[extract_byte(t2, 0)]) ^ + rk[3]; + STORE32H(s3, ct+12); + + return CRYPT_OK; +} + +#ifdef LTC_CLEAN_STACK +int ECB_ENC(const unsigned char *pt, unsigned char *ct, symmetric_key *skey) +{ + int err = _rijndael_ecb_encrypt(pt, ct, skey); + burn_stack(sizeof(unsigned long)*8 + sizeof(unsigned long*) + sizeof(int)*2); + return err; +} +#endif + +#ifndef ENCRYPT_ONLY + +/** + Decrypts a block of text with AES + @param ct The input ciphertext (16 bytes) + @param pt The output plaintext (16 bytes) + @param skey The key as scheduled + @return CRYPT_OK if successful +*/ +#ifdef LTC_CLEAN_STACK +static int _rijndael_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey) +#else +int ECB_DEC(const unsigned char *ct, unsigned char *pt, symmetric_key *skey) +#endif +{ + ulong32 s0, s1, s2, s3, t0, t1, t2, t3, *rk; + int Nr, r; + + LTC_ARGCHK(pt != NULL); + LTC_ARGCHK(ct != NULL); + LTC_ARGCHK(skey != NULL); + + Nr = skey->rijndael.Nr; + rk = skey->rijndael.dK; + + /* + * map byte array block to cipher state + * and add initial round key: + */ + LOAD32H(s0, ct ); s0 ^= rk[0]; + LOAD32H(s1, ct + 4); s1 ^= rk[1]; + LOAD32H(s2, ct + 8); s2 ^= rk[2]; + LOAD32H(s3, ct + 12); s3 ^= rk[3]; + +#ifdef LTC_SMALL_CODE + for (r = 0; ; r++) { + rk += 4; + t0 = + Td0(extract_byte(s0, 3)) ^ + Td1(extract_byte(s3, 2)) ^ + Td2(extract_byte(s2, 1)) ^ + Td3(extract_byte(s1, 0)) ^ + rk[0]; + t1 = + Td0(extract_byte(s1, 3)) ^ + Td1(extract_byte(s0, 2)) ^ + Td2(extract_byte(s3, 1)) ^ + Td3(extract_byte(s2, 0)) ^ + rk[1]; + t2 = + Td0(extract_byte(s2, 3)) ^ + Td1(extract_byte(s1, 2)) ^ + Td2(extract_byte(s0, 1)) ^ + Td3(extract_byte(s3, 0)) ^ + rk[2]; + t3 = + Td0(extract_byte(s3, 3)) ^ + Td1(extract_byte(s2, 2)) ^ + Td2(extract_byte(s1, 1)) ^ + Td3(extract_byte(s0, 0)) ^ + rk[3]; + if (r == Nr-2) { + break; + } + s0 = t0; s1 = t1; s2 = t2; s3 = t3; + } + rk += 4; + +#else + + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + + t0 = + Td0(extract_byte(s0, 3)) ^ + Td1(extract_byte(s3, 2)) ^ + Td2(extract_byte(s2, 1)) ^ + Td3(extract_byte(s1, 0)) ^ + rk[4]; + t1 = + Td0(extract_byte(s1, 3)) ^ + Td1(extract_byte(s0, 2)) ^ + Td2(extract_byte(s3, 1)) ^ + Td3(extract_byte(s2, 0)) ^ + rk[5]; + t2 = + Td0(extract_byte(s2, 3)) ^ + Td1(extract_byte(s1, 2)) ^ + Td2(extract_byte(s0, 1)) ^ + Td3(extract_byte(s3, 0)) ^ + rk[6]; + t3 = + Td0(extract_byte(s3, 3)) ^ + Td1(extract_byte(s2, 2)) ^ + Td2(extract_byte(s1, 1)) ^ + Td3(extract_byte(s0, 0)) ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + + s0 = + Td0(extract_byte(t0, 3)) ^ + Td1(extract_byte(t3, 2)) ^ + Td2(extract_byte(t2, 1)) ^ + Td3(extract_byte(t1, 0)) ^ + rk[0]; + s1 = + Td0(extract_byte(t1, 3)) ^ + Td1(extract_byte(t0, 2)) ^ + Td2(extract_byte(t3, 1)) ^ + Td3(extract_byte(t2, 0)) ^ + rk[1]; + s2 = + Td0(extract_byte(t2, 3)) ^ + Td1(extract_byte(t1, 2)) ^ + Td2(extract_byte(t0, 1)) ^ + Td3(extract_byte(t3, 0)) ^ + rk[2]; + s3 = + Td0(extract_byte(t3, 3)) ^ + Td1(extract_byte(t2, 2)) ^ + Td2(extract_byte(t1, 1)) ^ + Td3(extract_byte(t0, 0)) ^ + rk[3]; + } +#endif + + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Td4[extract_byte(t0, 3)] & 0xff000000) ^ + (Td4[extract_byte(t3, 2)] & 0x00ff0000) ^ + (Td4[extract_byte(t2, 1)] & 0x0000ff00) ^ + (Td4[extract_byte(t1, 0)] & 0x000000ff) ^ + rk[0]; + STORE32H(s0, pt); + s1 = + (Td4[extract_byte(t1, 3)] & 0xff000000) ^ + (Td4[extract_byte(t0, 2)] & 0x00ff0000) ^ + (Td4[extract_byte(t3, 1)] & 0x0000ff00) ^ + (Td4[extract_byte(t2, 0)] & 0x000000ff) ^ + rk[1]; + STORE32H(s1, pt+4); + s2 = + (Td4[extract_byte(t2, 3)] & 0xff000000) ^ + (Td4[extract_byte(t1, 2)] & 0x00ff0000) ^ + (Td4[extract_byte(t0, 1)] & 0x0000ff00) ^ + (Td4[extract_byte(t3, 0)] & 0x000000ff) ^ + rk[2]; + STORE32H(s2, pt+8); + s3 = + (Td4[extract_byte(t3, 3)] & 0xff000000) ^ + (Td4[extract_byte(t2, 2)] & 0x00ff0000) ^ + (Td4[extract_byte(t1, 1)] & 0x0000ff00) ^ + (Td4[extract_byte(t0, 0)] & 0x000000ff) ^ + rk[3]; + STORE32H(s3, pt+12); + + return CRYPT_OK; +} + + +#ifdef LTC_CLEAN_STACK +int ECB_DEC(const unsigned char *ct, unsigned char *pt, symmetric_key *skey) +{ + int err = _rijndael_ecb_decrypt(ct, pt, skey); + burn_stack(sizeof(unsigned long)*8 + sizeof(unsigned long*) + sizeof(int)*2); + return err; +} +#endif + +/** + Performs a self-test of the AES block cipher + @return CRYPT_OK if functional, CRYPT_NOP if self-test has been disabled +*/ +int ECB_TEST(void) +{ + #ifndef LTC_TEST + return CRYPT_NOP; + #else + int err; + static const struct { + int keylen; + unsigned char key[32], pt[16], ct[16]; + } tests[] = { + { 16, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, + { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }, + { 0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30, + 0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a } + }, { + 24, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }, + { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }, + { 0xdd, 0xa9, 0x7c, 0xa4, 0x86, 0x4c, 0xdf, 0xe0, + 0x6e, 0xaf, 0x70, 0xa0, 0xec, 0x0d, 0x71, 0x91 } + }, { + 32, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f }, + { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }, + { 0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf, + 0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89 } + } + }; + + symmetric_key key; + unsigned char tmp[2][16]; + int i, y; + + for (i = 0; i < (int)(sizeof(tests)/sizeof(tests[0])); i++) { + zeromem(&key, sizeof(key)); + if ((err = rijndael_setup(tests[i].key, tests[i].keylen, 0, &key)) != CRYPT_OK) { + return err; + } + + rijndael_ecb_encrypt(tests[i].pt, tmp[0], &key); + rijndael_ecb_decrypt(tmp[0], tmp[1], &key); + if (XMEMCMP(tmp[0], tests[i].ct, 16) || XMEMCMP(tmp[1], tests[i].pt, 16)) { +#if 0 + printf("\n\nTest %d failed\n", i); + if (XMEMCMP(tmp[0], tests[i].ct, 16)) { + printf("CT: "); + for (i = 0; i < 16; i++) { + printf("%02x ", tmp[0][i]); + } + printf("\n"); + } else { + printf("PT: "); + for (i = 0; i < 16; i++) { + printf("%02x ", tmp[1][i]); + } + printf("\n"); + } +#endif + return CRYPT_FAIL_TESTVECTOR; + } + + /* now see if we can encrypt all zero bytes 1000 times, decrypt and come back where we started */ + for (y = 0; y < 16; y++) tmp[0][y] = 0; + for (y = 0; y < 1000; y++) rijndael_ecb_encrypt(tmp[0], tmp[0], &key); + for (y = 0; y < 1000; y++) rijndael_ecb_decrypt(tmp[0], tmp[0], &key); + for (y = 0; y < 16; y++) if (tmp[0][y] != 0) return CRYPT_FAIL_TESTVECTOR; + } + return CRYPT_OK; + #endif +} + +#endif /* ENCRYPT_ONLY */ + + +/** Terminate the context + @param skey The scheduled key +*/ +void ECB_DONE(symmetric_key *skey) +{ + //LTC_UNUSED_PARAM(skey); +} + + +/** + Gets suitable key size + @param keysize [in/out] The length of the recommended key (in bytes). This function will store the suitable size back in this variable. + @return CRYPT_OK if the input key size is acceptable. +*/ +int ECB_KS(int *keysize) +{ + LTC_ARGCHK(keysize != NULL); + + if (*keysize < 16) + return CRYPT_INVALID_KEYSIZE; + if (*keysize < 24) { + *keysize = 16; + return CRYPT_OK; + } else if (*keysize < 32) { + *keysize = 24; + return CRYPT_OK; + } else { + *keysize = 32; + return CRYPT_OK; + } +} + +#endif + + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file cbc_decrypt.c + CBC implementation, encrypt block, Tom St Denis +*/ + + +#ifdef LTC_CBC_MODE + +/** + CBC decrypt + @param ct Ciphertext + @param pt [out] Plaintext + @param len The number of bytes to process (must be multiple of block length) + @param cbc CBC state + @return CRYPT_OK if successful +*/ +int cbc_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CBC *cbc) +{ + int x, err; + unsigned char tmp[16]; +#ifdef LTC_FAST + LTC_FAST_TYPE tmpy; +#else + unsigned char tmpy; +#endif + + LTC_ARGCHK(pt != NULL); + LTC_ARGCHK(ct != NULL); + LTC_ARGCHK(cbc != NULL); + + if ((err = cipher_is_valid(cbc->cipher)) != CRYPT_OK) { + return err; + } + + /* is blocklen valid? */ + if (cbc->blocklen < 1 || cbc->blocklen > (int)sizeof(cbc->IV)) { + return CRYPT_INVALID_ARG; + } + + if (len % cbc->blocklen) { + return CRYPT_INVALID_ARG; + } +#ifdef LTC_FAST + if (cbc->blocklen % sizeof(LTC_FAST_TYPE)) { + return CRYPT_INVALID_ARG; + } +#endif + + if (cipher_descriptor[cbc->cipher].accel_cbc_decrypt != NULL) { + return cipher_descriptor[cbc->cipher].accel_cbc_decrypt(ct, pt, len / cbc->blocklen, cbc->IV, &cbc->key); + } else { + while (len) { + /* decrypt */ + if ((err = cipher_descriptor[cbc->cipher].ecb_decrypt(ct, tmp, &cbc->key)) != CRYPT_OK) { + return err; + } + + /* xor IV against plaintext */ + #if defined(LTC_FAST) + for (x = 0; x < cbc->blocklen; x += sizeof(LTC_FAST_TYPE)) { + tmpy = *((LTC_FAST_TYPE*)((unsigned char *)cbc->IV + x)) ^ *((LTC_FAST_TYPE*)((unsigned char *)tmp + x)); + *((LTC_FAST_TYPE*)((unsigned char *)cbc->IV + x)) = *((LTC_FAST_TYPE*)((unsigned char *)ct + x)); + *((LTC_FAST_TYPE*)((unsigned char *)pt + x)) = tmpy; + } + #else + for (x = 0; x < cbc->blocklen; x++) { + tmpy = tmp[x] ^ cbc->IV[x]; + cbc->IV[x] = ct[x]; + pt[x] = tmpy; + } + #endif + + ct += cbc->blocklen; + pt += cbc->blocklen; + len -= cbc->blocklen; + } + } + return CRYPT_OK; +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file cbc_done.c + CBC implementation, finish chain, Tom St Denis +*/ + +#ifdef LTC_CBC_MODE + +/** Terminate the chain + @param cbc The CBC chain to terminate + @return CRYPT_OK on success +*/ +int cbc_done(symmetric_CBC *cbc) +{ + int err; + LTC_ARGCHK(cbc != NULL); + + if ((err = cipher_is_valid(cbc->cipher)) != CRYPT_OK) { + return err; + } + cipher_descriptor[cbc->cipher].done(&cbc->key); + return CRYPT_OK; +} + + + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file cbc_encrypt.c + CBC implementation, encrypt block, Tom St Denis +*/ + + +#ifdef LTC_CBC_MODE + +/** + CBC encrypt + @param pt Plaintext + @param ct [out] Ciphertext + @param len The number of bytes to process (must be multiple of block length) + @param cbc CBC state + @return CRYPT_OK if successful +*/ +int cbc_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CBC *cbc) +{ + int x, err; + + LTC_ARGCHK(pt != NULL); + LTC_ARGCHK(ct != NULL); + LTC_ARGCHK(cbc != NULL); + + if ((err = cipher_is_valid(cbc->cipher)) != CRYPT_OK) { + return err; + } + + /* is blocklen valid? */ + if (cbc->blocklen < 1 || cbc->blocklen > (int)sizeof(cbc->IV)) { + return CRYPT_INVALID_ARG; + } + + if (len % cbc->blocklen) { + return CRYPT_INVALID_ARG; + } +#ifdef LTC_FAST + if (cbc->blocklen % sizeof(LTC_FAST_TYPE)) { + return CRYPT_INVALID_ARG; + } +#endif + + if (cipher_descriptor[cbc->cipher].accel_cbc_encrypt != NULL) { + return cipher_descriptor[cbc->cipher].accel_cbc_encrypt(pt, ct, len / cbc->blocklen, cbc->IV, &cbc->key); + } else { + while (len) { + /* xor IV against plaintext */ + #if defined(LTC_FAST) + for (x = 0; x < cbc->blocklen; x += sizeof(LTC_FAST_TYPE)) { + *((LTC_FAST_TYPE*)((unsigned char *)cbc->IV + x)) ^= *((LTC_FAST_TYPE*)((unsigned char *)pt + x)); + } + #else + for (x = 0; x < cbc->blocklen; x++) { + cbc->IV[x] ^= pt[x]; + } + #endif + + /* encrypt */ + if ((err = cipher_descriptor[cbc->cipher].ecb_encrypt(cbc->IV, ct, &cbc->key)) != CRYPT_OK) { + return err; + } + + /* store IV [ciphertext] for a future block */ + #if defined(LTC_FAST) + for (x = 0; x < cbc->blocklen; x += sizeof(LTC_FAST_TYPE)) { + *((LTC_FAST_TYPE*)((unsigned char *)cbc->IV + x)) = *((LTC_FAST_TYPE*)((unsigned char *)ct + x)); + } + #else + for (x = 0; x < cbc->blocklen; x++) { + cbc->IV[x] = ct[x]; + } + #endif + + ct += cbc->blocklen; + pt += cbc->blocklen; + len -= cbc->blocklen; + } + } + return CRYPT_OK; +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file cbc_getiv.c + CBC implementation, get IV, Tom St Denis +*/ + +#ifdef LTC_CBC_MODE + +/** + Get the current initial vector + @param IV [out] The destination of the initial vector + @param len [in/out] The max size and resulting size of the initial vector + @param cbc The CBC state + @return CRYPT_OK if successful +*/ +int cbc_getiv(unsigned char *IV, unsigned long *len, symmetric_CBC *cbc) +{ + LTC_ARGCHK(IV != NULL); + LTC_ARGCHK(len != NULL); + LTC_ARGCHK(cbc != NULL); + if ((unsigned long)cbc->blocklen > *len) { + *len = cbc->blocklen; + return CRYPT_BUFFER_OVERFLOW; + } + XMEMCPY(IV, cbc->IV, cbc->blocklen); + *len = cbc->blocklen; + + return CRYPT_OK; +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file cbc_setiv.c + CBC implementation, set IV, Tom St Denis +*/ + + +#ifdef LTC_CBC_MODE + +/** + Set an initial vector + @param IV The initial vector + @param len The length of the vector (in octets) + @param cbc The CBC state + @return CRYPT_OK if successful +*/ +int cbc_setiv(const unsigned char *IV, unsigned long len, symmetric_CBC *cbc) +{ + LTC_ARGCHK(IV != NULL); + LTC_ARGCHK(cbc != NULL); + if (len != (unsigned long)cbc->blocklen) { + return CRYPT_INVALID_ARG; + } + XMEMCPY(cbc->IV, IV, len); + return CRYPT_OK; +} + +#endif + + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file cbc_start.c + CBC implementation, start chain, Tom St Denis +*/ + +#ifdef LTC_CBC_MODE + +/** + Initialize a CBC context + @param cipher The index of the cipher desired + @param IV The initial vector + @param key The secret key + @param keylen The length of the secret key (octets) + @param num_rounds Number of rounds in the cipher desired (0 for default) + @param cbc The CBC state to initialize + @return CRYPT_OK if successful +*/ +int cbc_start(int cipher, const unsigned char *IV, const unsigned char *key, + int keylen, int num_rounds, symmetric_CBC *cbc) +{ + int x, err; + + LTC_ARGCHK(IV != NULL); + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(cbc != NULL); + + /* bad param? */ + if ((err = cipher_is_valid(cipher)) != CRYPT_OK) { + return err; + } + + /* setup cipher */ + if ((err = cipher_descriptor[cipher].setup(key, keylen, num_rounds, &cbc->key)) != CRYPT_OK) { + return err; + } + + /* copy IV */ + cbc->blocklen = cipher_descriptor[cipher].block_length; + cbc->cipher = cipher; + for (x = 0; x < cbc->blocklen; x++) { + cbc->IV[x] = IV[x]; + } + return CRYPT_OK; +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file gcm_add_iv.c + GCM implementation, add IV data to the state, by Tom St Denis +*/ + + +#ifdef LTC_GCM_MODE + +/** + Add IV data to the GCM state + @param gcm The GCM state + @param IV The initial value data to add + @param IVlen The length of the IV + @return CRYPT_OK on success + */ +int gcm_add_iv(gcm_state *gcm, + const unsigned char *IV, unsigned long IVlen) +{ + unsigned long x, y; + int err; + + LTC_ARGCHK(gcm != NULL); + if (IVlen > 0) { + LTC_ARGCHK(IV != NULL); + } + + /* must be in IV mode */ + if (gcm->mode != LTC_GCM_MODE_IV) { + return CRYPT_INVALID_ARG; + } + + if (gcm->buflen >= 16 || gcm->buflen < 0) { + return CRYPT_INVALID_ARG; + } + + if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) { + return err; + } + + + /* trip the ivmode flag */ + if (IVlen + gcm->buflen > 12) { + gcm->ivmode |= 1; + } + + x = 0; +#ifdef LTC_FAST + if (gcm->buflen == 0) { + for (x = 0; x < (IVlen & ~15); x += 16) { + for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) { + *((LTC_FAST_TYPE*)(&gcm->X[y])) ^= *((LTC_FAST_TYPE*)(&IV[x + y])); + } + gcm_mult_h(gcm, gcm->X); + gcm->totlen += 128; + } + IV += x; + } +#endif + + /* start adding IV data to the state */ + for (; x < IVlen; x++) { + gcm->buf[gcm->buflen++] = *IV++; + + if (gcm->buflen == 16) { + /* GF mult it */ + for (y = 0; y < 16; y++) { + gcm->X[y] ^= gcm->buf[y]; + } + gcm_mult_h(gcm, gcm->X); + gcm->buflen = 0; + gcm->totlen += 128; + } + } + + return CRYPT_OK; +} + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_add_iv.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file gcm_done.c + GCM implementation, Terminate the stream, by Tom St Denis +*/ + + +#ifdef LTC_GCM_MODE + +/** + Terminate a GCM stream + @param gcm The GCM state + @param tag [out] The destination for the MAC tag + @param taglen [in/out] The length of the MAC tag + @return CRYPT_OK on success + */ +int gcm_done(gcm_state *gcm, + unsigned char *tag, unsigned long *taglen) +{ + unsigned long x; + int err; + + LTC_ARGCHK(gcm != NULL); + LTC_ARGCHK(tag != NULL); + LTC_ARGCHK(taglen != NULL); + + if (gcm->buflen > 16 || gcm->buflen < 0) { + return CRYPT_INVALID_ARG; + } + + if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) { + return err; + } + + + if (gcm->mode != LTC_GCM_MODE_TEXT) { + return CRYPT_INVALID_ARG; + } + + /* handle remaining ciphertext */ + if (gcm->buflen) { + gcm->pttotlen += gcm->buflen * CONST64(8); + gcm_mult_h(gcm, gcm->X); + } + + /* length */ + STORE64H(gcm->totlen, gcm->buf); + STORE64H(gcm->pttotlen, gcm->buf+8); + for (x = 0; x < 16; x++) { + gcm->X[x] ^= gcm->buf[x]; + } + gcm_mult_h(gcm, gcm->X); + + /* encrypt original counter */ + if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y_0, gcm->buf, &gcm->K)) != CRYPT_OK) { + return err; + } + for (x = 0; x < 16 && x < *taglen; x++) { + tag[x] = gcm->buf[x] ^ gcm->X[x]; + } + *taglen = x; + + cipher_descriptor[gcm->cipher].done(&gcm->K); + + return CRYPT_OK; +} + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_done.c,v $ */ +/* $Revision: 1.11 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file gcm_init.c + GCM implementation, initialize state, by Tom St Denis +*/ + + +#ifdef LTC_GCM_MODE + +/** + Initialize a GCM state + @param gcm The GCM state to initialize + @param cipher The index of the cipher to use + @param key The secret key + @param keylen The length of the secret key + @return CRYPT_OK on success + */ +int gcm_init(gcm_state *gcm, int cipher, + const unsigned char *key, int keylen) +{ + int err; + unsigned char B[16]; +#ifdef LTC_GCM_TABLES + int x, y, z, t; +#endif + + LTC_ARGCHK(gcm != NULL); + LTC_ARGCHK(key != NULL); + +#ifdef LTC_FAST + if (16 % sizeof(LTC_FAST_TYPE)) { + return CRYPT_INVALID_ARG; + } +#endif + + /* is cipher valid? */ + if ((err = cipher_is_valid(cipher)) != CRYPT_OK) { + return err; + } + if (cipher_descriptor[cipher].block_length != 16) { + return CRYPT_INVALID_CIPHER; + } + + /* schedule key */ + if ((err = cipher_descriptor[cipher].setup(key, keylen, 0, &gcm->K)) != CRYPT_OK) { + return err; + } + + /* H = E(0) */ + zeromem(B, 16); + if ((err = cipher_descriptor[cipher].ecb_encrypt(B, gcm->H, &gcm->K)) != CRYPT_OK) { + return err; + } + + /* setup state */ + zeromem(gcm->buf, sizeof(gcm->buf)); + zeromem(gcm->X, sizeof(gcm->X)); + gcm->cipher = cipher; + gcm->mode = LTC_GCM_MODE_IV; + gcm->ivmode = 0; + gcm->buflen = 0; + gcm->totlen = 0; + gcm->pttotlen = 0; + +#ifdef LTC_GCM_TABLES + /* setup tables */ + + /* generate the first table as it has no shifting (from which we make the other tables) */ + zeromem(B, 16); + for (y = 0; y < 256; y++) { + B[0] = y; + gcm_gf_mult(gcm->H, B, &gcm->PC[0][y][0]); + } + + /* now generate the rest of the tables based the previous table */ + for (x = 1; x < 16; x++) { + for (y = 0; y < 256; y++) { + /* now shift it right by 8 bits */ + t = gcm->PC[x-1][y][15]; + for (z = 15; z > 0; z--) { + gcm->PC[x][y][z] = gcm->PC[x-1][y][z-1]; + } + gcm->PC[x][y][0] = gcm_shift_table[t<<1]; + gcm->PC[x][y][1] ^= gcm_shift_table[(t<<1)+1]; + } + } + +#endif + + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_init.c,v $ */ +/* $Revision: 1.20 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file gcm_process.c + GCM implementation, process message data, by Tom St Denis +*/ + + +#ifdef LTC_GCM_MODE + +/** + Process plaintext/ciphertext through GCM + @param gcm The GCM state + @param pt The plaintext + @param ptlen The plaintext length (ciphertext length is the same) + @param ct The ciphertext + @param direction Encrypt or Decrypt mode (GCM_ENCRYPT or GCM_DECRYPT) + @return CRYPT_OK on success + */ +int gcm_process(gcm_state *gcm, + unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + int direction) +{ + unsigned long x; + int y, err; + unsigned char b; + + LTC_ARGCHK(gcm != NULL); + if (ptlen > 0) { + LTC_ARGCHK(pt != NULL); + LTC_ARGCHK(ct != NULL); + } + + if (gcm->buflen > 16 || gcm->buflen < 0) { + return CRYPT_INVALID_ARG; + } + + if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) { + return err; + } + + /* in AAD mode? */ + if (gcm->mode == LTC_GCM_MODE_AAD) { + /* let's process the AAD */ + if (gcm->buflen) { + gcm->totlen += gcm->buflen * CONST64(8); + gcm_mult_h(gcm, gcm->X); + } + + /* increment counter */ + for (y = 15; y >= 12; y--) { + if (++gcm->Y[y] & 255) { break; } + } + /* encrypt the counter */ + if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) { + return err; + } + + gcm->buflen = 0; + gcm->mode = LTC_GCM_MODE_TEXT; + } + + if (gcm->mode != LTC_GCM_MODE_TEXT) { + return CRYPT_INVALID_ARG; + } + + x = 0; +#ifdef LTC_FAST + if (gcm->buflen == 0) { + if (direction == GCM_ENCRYPT) { + for (x = 0; x < (ptlen & ~15); x += 16) { + /* ctr encrypt */ + for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) { + *((LTC_FAST_TYPE*)(&ct[x + y])) = *((LTC_FAST_TYPE*)(&pt[x+y])) ^ *((LTC_FAST_TYPE*)(&gcm->buf[y])); + *((LTC_FAST_TYPE*)(&gcm->X[y])) ^= *((LTC_FAST_TYPE*)(&ct[x+y])); + } + /* GMAC it */ + gcm->pttotlen += 128; + gcm_mult_h(gcm, gcm->X); + /* increment counter */ + for (y = 15; y >= 12; y--) { + if (++gcm->Y[y] & 255) { break; } + } + if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) { + return err; + } + } + } else { + for (x = 0; x < (ptlen & ~15); x += 16) { + /* ctr encrypt */ + for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) { + *((LTC_FAST_TYPE*)(&gcm->X[y])) ^= *((LTC_FAST_TYPE*)(&ct[x+y])); + *((LTC_FAST_TYPE*)(&pt[x + y])) = *((LTC_FAST_TYPE*)(&ct[x+y])) ^ *((LTC_FAST_TYPE*)(&gcm->buf[y])); + } + /* GMAC it */ + gcm->pttotlen += 128; + gcm_mult_h(gcm, gcm->X); + /* increment counter */ + for (y = 15; y >= 12; y--) { + if (++gcm->Y[y] & 255) { break; } + } + if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) { + return err; + } + } + } + } +#endif + + /* process text */ + for (; x < ptlen; x++) { + if (gcm->buflen == 16) { + gcm->pttotlen += 128; + gcm_mult_h(gcm, gcm->X); + + /* increment counter */ + for (y = 15; y >= 12; y--) { + if (++gcm->Y[y] & 255) { break; } + } + if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) { + return err; + } + gcm->buflen = 0; + } + + if (direction == GCM_ENCRYPT) { + b = ct[x] = pt[x] ^ gcm->buf[gcm->buflen]; + } else { + b = ct[x]; + pt[x] = ct[x] ^ gcm->buf[gcm->buflen]; + } + gcm->X[gcm->buflen++] ^= b; + } + + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_process.c,v $ */ +/* $Revision: 1.16 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file gcm_mult_h.c + GCM implementation, do the GF mult, by Tom St Denis +*/ + + +#if defined(LTC_GCM_MODE) +/** + GCM multiply by H + @param gcm The GCM state which holds the H value + @param I The value to multiply H by + */ +void gcm_mult_h(gcm_state *gcm, unsigned char *I) +{ + unsigned char T[16]; +#ifdef LTC_GCM_TABLES + int x, y; +#ifdef LTC_GCM_TABLES_SSE2 + asm("movdqa (%0),%%xmm0"::"r"(&gcm->PC[0][I[0]][0])); + for (x = 1; x < 16; x++) { + asm("pxor (%0),%%xmm0"::"r"(&gcm->PC[x][I[x]][0])); + } + asm("movdqa %%xmm0,(%0)"::"r"(&T)); +#else + XMEMCPY(T, &gcm->PC[0][I[0]][0], 16); + for (x = 1; x < 16; x++) { +#ifdef LTC_FAST + for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) { + *((LTC_FAST_TYPE *)(T + y)) ^= *((LTC_FAST_TYPE *)(&gcm->PC[x][I[x]][y])); + } +#else + for (y = 0; y < 16; y++) { + T[y] ^= gcm->PC[x][I[x]][y]; + } +#endif /* LTC_FAST */ + } +#endif /* LTC_GCM_TABLES_SSE2 */ +#else + gcm_gf_mult(gcm->H, I, T); +#endif + XMEMCPY(I, T, 16); +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_mult_h.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file gcm_gf_mult.c + GCM implementation, do the GF mult, by Tom St Denis +*/ + + +#if defined(LTC_GCM_TABLES) || defined(LRW_TABLES) || ((defined(LTC_GCM_MODE) || defined(LTC_GCM_MODE)) && defined(LTC_FAST)) + +/* this is x*2^128 mod p(x) ... the results are 16 bytes each stored in a packed format. Since only the + * lower 16 bits are not zero'ed I removed the upper 14 bytes */ +const unsigned char gcm_shift_table[256*2] = { +0x00, 0x00, 0x01, 0xc2, 0x03, 0x84, 0x02, 0x46, 0x07, 0x08, 0x06, 0xca, 0x04, 0x8c, 0x05, 0x4e, +0x0e, 0x10, 0x0f, 0xd2, 0x0d, 0x94, 0x0c, 0x56, 0x09, 0x18, 0x08, 0xda, 0x0a, 0x9c, 0x0b, 0x5e, +0x1c, 0x20, 0x1d, 0xe2, 0x1f, 0xa4, 0x1e, 0x66, 0x1b, 0x28, 0x1a, 0xea, 0x18, 0xac, 0x19, 0x6e, +0x12, 0x30, 0x13, 0xf2, 0x11, 0xb4, 0x10, 0x76, 0x15, 0x38, 0x14, 0xfa, 0x16, 0xbc, 0x17, 0x7e, +0x38, 0x40, 0x39, 0x82, 0x3b, 0xc4, 0x3a, 0x06, 0x3f, 0x48, 0x3e, 0x8a, 0x3c, 0xcc, 0x3d, 0x0e, +0x36, 0x50, 0x37, 0x92, 0x35, 0xd4, 0x34, 0x16, 0x31, 0x58, 0x30, 0x9a, 0x32, 0xdc, 0x33, 0x1e, +0x24, 0x60, 0x25, 0xa2, 0x27, 0xe4, 0x26, 0x26, 0x23, 0x68, 0x22, 0xaa, 0x20, 0xec, 0x21, 0x2e, +0x2a, 0x70, 0x2b, 0xb2, 0x29, 0xf4, 0x28, 0x36, 0x2d, 0x78, 0x2c, 0xba, 0x2e, 0xfc, 0x2f, 0x3e, +0x70, 0x80, 0x71, 0x42, 0x73, 0x04, 0x72, 0xc6, 0x77, 0x88, 0x76, 0x4a, 0x74, 0x0c, 0x75, 0xce, +0x7e, 0x90, 0x7f, 0x52, 0x7d, 0x14, 0x7c, 0xd6, 0x79, 0x98, 0x78, 0x5a, 0x7a, 0x1c, 0x7b, 0xde, +0x6c, 0xa0, 0x6d, 0x62, 0x6f, 0x24, 0x6e, 0xe6, 0x6b, 0xa8, 0x6a, 0x6a, 0x68, 0x2c, 0x69, 0xee, +0x62, 0xb0, 0x63, 0x72, 0x61, 0x34, 0x60, 0xf6, 0x65, 0xb8, 0x64, 0x7a, 0x66, 0x3c, 0x67, 0xfe, +0x48, 0xc0, 0x49, 0x02, 0x4b, 0x44, 0x4a, 0x86, 0x4f, 0xc8, 0x4e, 0x0a, 0x4c, 0x4c, 0x4d, 0x8e, +0x46, 0xd0, 0x47, 0x12, 0x45, 0x54, 0x44, 0x96, 0x41, 0xd8, 0x40, 0x1a, 0x42, 0x5c, 0x43, 0x9e, +0x54, 0xe0, 0x55, 0x22, 0x57, 0x64, 0x56, 0xa6, 0x53, 0xe8, 0x52, 0x2a, 0x50, 0x6c, 0x51, 0xae, +0x5a, 0xf0, 0x5b, 0x32, 0x59, 0x74, 0x58, 0xb6, 0x5d, 0xf8, 0x5c, 0x3a, 0x5e, 0x7c, 0x5f, 0xbe, +0xe1, 0x00, 0xe0, 0xc2, 0xe2, 0x84, 0xe3, 0x46, 0xe6, 0x08, 0xe7, 0xca, 0xe5, 0x8c, 0xe4, 0x4e, +0xef, 0x10, 0xee, 0xd2, 0xec, 0x94, 0xed, 0x56, 0xe8, 0x18, 0xe9, 0xda, 0xeb, 0x9c, 0xea, 0x5e, +0xfd, 0x20, 0xfc, 0xe2, 0xfe, 0xa4, 0xff, 0x66, 0xfa, 0x28, 0xfb, 0xea, 0xf9, 0xac, 0xf8, 0x6e, +0xf3, 0x30, 0xf2, 0xf2, 0xf0, 0xb4, 0xf1, 0x76, 0xf4, 0x38, 0xf5, 0xfa, 0xf7, 0xbc, 0xf6, 0x7e, +0xd9, 0x40, 0xd8, 0x82, 0xda, 0xc4, 0xdb, 0x06, 0xde, 0x48, 0xdf, 0x8a, 0xdd, 0xcc, 0xdc, 0x0e, +0xd7, 0x50, 0xd6, 0x92, 0xd4, 0xd4, 0xd5, 0x16, 0xd0, 0x58, 0xd1, 0x9a, 0xd3, 0xdc, 0xd2, 0x1e, +0xc5, 0x60, 0xc4, 0xa2, 0xc6, 0xe4, 0xc7, 0x26, 0xc2, 0x68, 0xc3, 0xaa, 0xc1, 0xec, 0xc0, 0x2e, +0xcb, 0x70, 0xca, 0xb2, 0xc8, 0xf4, 0xc9, 0x36, 0xcc, 0x78, 0xcd, 0xba, 0xcf, 0xfc, 0xce, 0x3e, +0x91, 0x80, 0x90, 0x42, 0x92, 0x04, 0x93, 0xc6, 0x96, 0x88, 0x97, 0x4a, 0x95, 0x0c, 0x94, 0xce, +0x9f, 0x90, 0x9e, 0x52, 0x9c, 0x14, 0x9d, 0xd6, 0x98, 0x98, 0x99, 0x5a, 0x9b, 0x1c, 0x9a, 0xde, +0x8d, 0xa0, 0x8c, 0x62, 0x8e, 0x24, 0x8f, 0xe6, 0x8a, 0xa8, 0x8b, 0x6a, 0x89, 0x2c, 0x88, 0xee, +0x83, 0xb0, 0x82, 0x72, 0x80, 0x34, 0x81, 0xf6, 0x84, 0xb8, 0x85, 0x7a, 0x87, 0x3c, 0x86, 0xfe, +0xa9, 0xc0, 0xa8, 0x02, 0xaa, 0x44, 0xab, 0x86, 0xae, 0xc8, 0xaf, 0x0a, 0xad, 0x4c, 0xac, 0x8e, +0xa7, 0xd0, 0xa6, 0x12, 0xa4, 0x54, 0xa5, 0x96, 0xa0, 0xd8, 0xa1, 0x1a, 0xa3, 0x5c, 0xa2, 0x9e, +0xb5, 0xe0, 0xb4, 0x22, 0xb6, 0x64, 0xb7, 0xa6, 0xb2, 0xe8, 0xb3, 0x2a, 0xb1, 0x6c, 0xb0, 0xae, +0xbb, 0xf0, 0xba, 0x32, 0xb8, 0x74, 0xb9, 0xb6, 0xbc, 0xf8, 0xbd, 0x3a, 0xbf, 0x7c, 0xbe, 0xbe }; + +#endif + + +#if defined(LTC_GCM_MODE) || defined(LRW_MODE) + +#ifndef LTC_FAST +/* right shift */ +static void gcm_rightshift(unsigned char *a) +{ + int x; + for (x = 15; x > 0; x--) { + a[x] = (a[x]>>1) | ((a[x-1]<<7)&0x80); + } + a[0] >>= 1; +} + +/* c = b*a */ +static const unsigned char mask[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; +static const unsigned char poly[] = { 0x00, 0xE1 }; + + +/** + GCM GF multiplier (internal use only) bitserial + @param a First value + @param b Second value + @param c Destination for a * b + */ +void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c) +{ + unsigned char Z[16], V[16]; + unsigned char x, y, z; + + zeromem(Z, 16); + XMEMCPY(V, a, 16); + for (x = 0; x < 128; x++) { + if (b[x>>3] & mask[x&7]) { + for (y = 0; y < 16; y++) { + Z[y] ^= V[y]; + } + } + z = V[15] & 0x01; + gcm_rightshift(V); + V[0] ^= poly[z]; + } + XMEMCPY(c, Z, 16); +} + +#else + +/* map normal numbers to "ieee" way ... e.g. bit reversed */ +#define M(x) ( ((x&8)>>3) | ((x&4)>>1) | ((x&2)<<1) | ((x&1)<<3) ) + +#define BPD (sizeof(LTC_FAST_TYPE) * 8) +#define WPV (1 + (16 / sizeof(LTC_FAST_TYPE))) + +/** + GCM GF multiplier (internal use only) word oriented + @param a First value + @param b Second value + @param c Destination for a * b + */ +void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c) +{ + int i, j, k, u; + LTC_FAST_TYPE B[16][WPV], tmp[32 / sizeof(LTC_FAST_TYPE)], pB[16 / sizeof(LTC_FAST_TYPE)], zz, z; + unsigned char pTmp[32]; + + /* create simple tables */ + zeromem(B[0], sizeof(B[0])); + zeromem(B[M(1)], sizeof(B[M(1)])); + +#ifdef ENDIAN_32BITWORD + for (i = 0; i < 4; i++) { + LOAD32H(B[M(1)][i], a + (i<<2)); + LOAD32L(pB[i], b + (i<<2)); + } +#else + for (i = 0; i < 2; i++) { + LOAD64H(B[M(1)][i], a + (i<<3)); + LOAD64L(pB[i], b + (i<<3)); + } +#endif + + /* now create 2, 4 and 8 */ + B[M(2)][0] = B[M(1)][0] >> 1; + B[M(4)][0] = B[M(1)][0] >> 2; + B[M(8)][0] = B[M(1)][0] >> 3; + for (i = 1; i < (int)WPV; i++) { + B[M(2)][i] = (B[M(1)][i-1] << (BPD-1)) | (B[M(1)][i] >> 1); + B[M(4)][i] = (B[M(1)][i-1] << (BPD-2)) | (B[M(1)][i] >> 2); + B[M(8)][i] = (B[M(1)][i-1] << (BPD-3)) | (B[M(1)][i] >> 3); + } + + /* now all values with two bits which are 3, 5, 6, 9, 10, 12 */ + for (i = 0; i < (int)WPV; i++) { + B[M(3)][i] = B[M(1)][i] ^ B[M(2)][i]; + B[M(5)][i] = B[M(1)][i] ^ B[M(4)][i]; + B[M(6)][i] = B[M(2)][i] ^ B[M(4)][i]; + B[M(9)][i] = B[M(1)][i] ^ B[M(8)][i]; + B[M(10)][i] = B[M(2)][i] ^ B[M(8)][i]; + B[M(12)][i] = B[M(8)][i] ^ B[M(4)][i]; + + /* now all 3 bit values and the only 4 bit value: 7, 11, 13, 14, 15 */ + B[M(7)][i] = B[M(3)][i] ^ B[M(4)][i]; + B[M(11)][i] = B[M(3)][i] ^ B[M(8)][i]; + B[M(13)][i] = B[M(1)][i] ^ B[M(12)][i]; + B[M(14)][i] = B[M(6)][i] ^ B[M(8)][i]; + B[M(15)][i] = B[M(7)][i] ^ B[M(8)][i]; + } + + zeromem(tmp, sizeof(tmp)); + + /* compute product four bits of each word at a time */ + /* for each nibble */ + for (i = (BPD/4)-1; i >= 0; i--) { + /* for each word */ + for (j = 0; j < (int)(WPV-1); j++) { + /* grab the 4 bits recall the nibbles are backwards so it's a shift by (i^1)*4 */ + u = (pB[j] >> ((i^1)<<2)) & 15; + + /* add offset by the word count the table looked up value to the result */ + for (k = 0; k < (int)WPV; k++) { + tmp[k+j] ^= B[u][k]; + } + } + /* shift result up by 4 bits */ + if (i != 0) { + for (z = j = 0; j < (int)(32 / sizeof(LTC_FAST_TYPE)); j++) { + zz = tmp[j] << (BPD-4); + tmp[j] = (tmp[j] >> 4) | z; + z = zz; + } + } + } + + /* store product */ +#ifdef ENDIAN_32BITWORD + for (i = 0; i < 8; i++) { + STORE32H(tmp[i], pTmp + (i<<2)); + } +#else + for (i = 0; i < 4; i++) { + STORE64H(tmp[i], pTmp + (i<<3)); + } +#endif + + /* reduce by taking most significant byte and adding the appropriate two byte sequence 16 bytes down */ + for (i = 31; i >= 16; i--) { + pTmp[i-16] ^= gcm_shift_table[((unsigned)pTmp[i]<<1)]; + pTmp[i-15] ^= gcm_shift_table[((unsigned)pTmp[i]<<1)+1]; + } + + for (i = 0; i < 16; i++) { + c[i] = pTmp[i]; + } + +} + +#endif + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_gf_mult.c,v $ */ +/* $Revision: 1.25 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file gcm_add_aad.c + GCM implementation, Add AAD data to the stream, by Tom St Denis +*/ + + +#ifdef LTC_GCM_MODE + +/** + Add AAD to the GCM state + @param gcm The GCM state + @param adata The additional authentication data to add to the GCM state + @param adatalen The length of the AAD data. + @return CRYPT_OK on success + */ +int gcm_add_aad(gcm_state *gcm, + const unsigned char *adata, unsigned long adatalen) +{ + unsigned long x; + int err; +#ifdef LTC_FAST + unsigned long y; +#endif + + LTC_ARGCHK(gcm != NULL); + if (adatalen > 0) { + LTC_ARGCHK(adata != NULL); + } + + if (gcm->buflen > 16 || gcm->buflen < 0) { + return CRYPT_INVALID_ARG; + } + + if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) { + return err; + } + + /* in IV mode? */ + if (gcm->mode == LTC_GCM_MODE_IV) { + /* let's process the IV */ + if (gcm->ivmode || gcm->buflen != 12) { + for (x = 0; x < (unsigned long)gcm->buflen; x++) { + gcm->X[x] ^= gcm->buf[x]; + } + if (gcm->buflen) { + gcm->totlen += gcm->buflen * CONST64(8); + gcm_mult_h(gcm, gcm->X); + } + + /* mix in the length */ + zeromem(gcm->buf, 8); + STORE64H(gcm->totlen, gcm->buf+8); + for (x = 0; x < 16; x++) { + gcm->X[x] ^= gcm->buf[x]; + } + gcm_mult_h(gcm, gcm->X); + + /* copy counter out */ + XMEMCPY(gcm->Y, gcm->X, 16); + zeromem(gcm->X, 16); + } else { + XMEMCPY(gcm->Y, gcm->buf, 12); + gcm->Y[12] = 0; + gcm->Y[13] = 0; + gcm->Y[14] = 0; + gcm->Y[15] = 1; + } + XMEMCPY(gcm->Y_0, gcm->Y, 16); + zeromem(gcm->buf, 16); + gcm->buflen = 0; + gcm->totlen = 0; + gcm->mode = LTC_GCM_MODE_AAD; + } + + if (gcm->mode != LTC_GCM_MODE_AAD || gcm->buflen >= 16) { + return CRYPT_INVALID_ARG; + } + + x = 0; +#ifdef LTC_FAST + if (gcm->buflen == 0) { + for (x = 0; x < (adatalen & ~15); x += 16) { + for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) { + *((LTC_FAST_TYPE*)(&gcm->X[y])) ^= *((LTC_FAST_TYPE*)(&adata[x + y])); + } + gcm_mult_h(gcm, gcm->X); + gcm->totlen += 128; + } + adata += x; + } +#endif + + + /* start adding AAD data to the state */ + for (; x < adatalen; x++) { + gcm->X[gcm->buflen++] ^= *adata++; + + if (gcm->buflen == 16) { + /* GF mult it */ + gcm_mult_h(gcm, gcm->X); + gcm->buflen = 0; + gcm->totlen += 128; + } + } + + return CRYPT_OK; +} +#endif + + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file gcm_reset.c + GCM implementation, reset a used state so it can accept IV data, by Tom St Denis +*/ + + +#ifdef LTC_GCM_MODE + +/** + Reset a GCM state to as if you just called gcm_init(). This saves the initialization time. + @param gcm The GCM state to reset + @return CRYPT_OK on success +*/ +int gcm_reset(gcm_state *gcm) +{ + LTC_ARGCHK(gcm != NULL); + + zeromem(gcm->buf, sizeof(gcm->buf)); + zeromem(gcm->X, sizeof(gcm->X)); + gcm->mode = LTC_GCM_MODE_IV; + gcm->ivmode = 0; + gcm->buflen = 0; + gcm->totlen = 0; + gcm->pttotlen = 0; + + return CRYPT_OK; +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + + +/** + @file md5.c + LTC_MD5 hash function by Tom St Denis +*/ + +#ifdef LTC_MD5 + +const struct ltc_hash_descriptor md5_desc = +{ + "md5", + 3, + 16, + 64, + + /* OID */ + { 1, 2, 840, 113549, 2, 5, }, + 6, + + &md5_init, + &md5_process, + &md5_done, + &md5_test, + NULL +}; + +#define F(x,y,z) (z ^ (x & (y ^ z))) +#define G(x,y,z) (y ^ (z & (y ^ x))) +#define H(x,y,z) (x^y^z) +#define I(x,y,z) (y^(x|(~z))) + +#ifdef LTC_SMALL_CODE + +#define FF(a,b,c,d,M,s,t) \ + a = (a + F(b,c,d) + M + t); a = ROL(a, s) + b; + +#define GG(a,b,c,d,M,s,t) \ + a = (a + G(b,c,d) + M + t); a = ROL(a, s) + b; + +#define HH(a,b,c,d,M,s,t) \ + a = (a + H(b,c,d) + M + t); a = ROL(a, s) + b; + +#define II(a,b,c,d,M,s,t) \ + a = (a + I(b,c,d) + M + t); a = ROL(a, s) + b; + +static const unsigned char Worder[64] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, + 1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12, + 5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2, + 0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9 +}; + +static const unsigned char Rorder[64] = { + 7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22, + 5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20, + 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23, + 6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21 +}; + +static const ulong32 Korder[64] = { +0xd76aa478UL, 0xe8c7b756UL, 0x242070dbUL, 0xc1bdceeeUL, 0xf57c0fafUL, 0x4787c62aUL, 0xa8304613UL, 0xfd469501UL, +0x698098d8UL, 0x8b44f7afUL, 0xffff5bb1UL, 0x895cd7beUL, 0x6b901122UL, 0xfd987193UL, 0xa679438eUL, 0x49b40821UL, +0xf61e2562UL, 0xc040b340UL, 0x265e5a51UL, 0xe9b6c7aaUL, 0xd62f105dUL, 0x02441453UL, 0xd8a1e681UL, 0xe7d3fbc8UL, +0x21e1cde6UL, 0xc33707d6UL, 0xf4d50d87UL, 0x455a14edUL, 0xa9e3e905UL, 0xfcefa3f8UL, 0x676f02d9UL, 0x8d2a4c8aUL, +0xfffa3942UL, 0x8771f681UL, 0x6d9d6122UL, 0xfde5380cUL, 0xa4beea44UL, 0x4bdecfa9UL, 0xf6bb4b60UL, 0xbebfbc70UL, +0x289b7ec6UL, 0xeaa127faUL, 0xd4ef3085UL, 0x04881d05UL, 0xd9d4d039UL, 0xe6db99e5UL, 0x1fa27cf8UL, 0xc4ac5665UL, +0xf4292244UL, 0x432aff97UL, 0xab9423a7UL, 0xfc93a039UL, 0x655b59c3UL, 0x8f0ccc92UL, 0xffeff47dUL, 0x85845dd1UL, +0x6fa87e4fUL, 0xfe2ce6e0UL, 0xa3014314UL, 0x4e0811a1UL, 0xf7537e82UL, 0xbd3af235UL, 0x2ad7d2bbUL, 0xeb86d391UL +}; + +#else + +#define FF(a,b,c,d,M,s,t) \ + a = (a + F(b,c,d) + M + t); a = ROLc(a, s) + b; + +#define GG(a,b,c,d,M,s,t) \ + a = (a + G(b,c,d) + M + t); a = ROLc(a, s) + b; + +#define HH(a,b,c,d,M,s,t) \ + a = (a + H(b,c,d) + M + t); a = ROLc(a, s) + b; + +#define II(a,b,c,d,M,s,t) \ + a = (a + I(b,c,d) + M + t); a = ROLc(a, s) + b; + + +#endif + +#ifdef LTC_CLEAN_STACK +static int _md5_compress(hash_state *md, unsigned char *buf) +#else +static int md5_compress(hash_state *md, unsigned char *buf) +#endif +{ + ulong32 i, W[16], a, b, c, d; +#ifdef LTC_SMALL_CODE + ulong32 t; +#endif + + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD32L(W[i], buf + (4*i)); + } + + /* copy state */ + a = md->md5.state[0]; + b = md->md5.state[1]; + c = md->md5.state[2]; + d = md->md5.state[3]; + +#ifdef LTC_SMALL_CODE + for (i = 0; i < 16; ++i) { + FF(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); + t = d; d = c; c = b; b = a; a = t; + } + + for (; i < 32; ++i) { + GG(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); + t = d; d = c; c = b; b = a; a = t; + } + + for (; i < 48; ++i) { + HH(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); + t = d; d = c; c = b; b = a; a = t; + } + + for (; i < 64; ++i) { + II(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); + t = d; d = c; c = b; b = a; a = t; + } + +#else + FF(a,b,c,d,W[0],7,0xd76aa478UL) + FF(d,a,b,c,W[1],12,0xe8c7b756UL) + FF(c,d,a,b,W[2],17,0x242070dbUL) + FF(b,c,d,a,W[3],22,0xc1bdceeeUL) + FF(a,b,c,d,W[4],7,0xf57c0fafUL) + FF(d,a,b,c,W[5],12,0x4787c62aUL) + FF(c,d,a,b,W[6],17,0xa8304613UL) + FF(b,c,d,a,W[7],22,0xfd469501UL) + FF(a,b,c,d,W[8],7,0x698098d8UL) + FF(d,a,b,c,W[9],12,0x8b44f7afUL) + FF(c,d,a,b,W[10],17,0xffff5bb1UL) + FF(b,c,d,a,W[11],22,0x895cd7beUL) + FF(a,b,c,d,W[12],7,0x6b901122UL) + FF(d,a,b,c,W[13],12,0xfd987193UL) + FF(c,d,a,b,W[14],17,0xa679438eUL) + FF(b,c,d,a,W[15],22,0x49b40821UL) + GG(a,b,c,d,W[1],5,0xf61e2562UL) + GG(d,a,b,c,W[6],9,0xc040b340UL) + GG(c,d,a,b,W[11],14,0x265e5a51UL) + GG(b,c,d,a,W[0],20,0xe9b6c7aaUL) + GG(a,b,c,d,W[5],5,0xd62f105dUL) + GG(d,a,b,c,W[10],9,0x02441453UL) + GG(c,d,a,b,W[15],14,0xd8a1e681UL) + GG(b,c,d,a,W[4],20,0xe7d3fbc8UL) + GG(a,b,c,d,W[9],5,0x21e1cde6UL) + GG(d,a,b,c,W[14],9,0xc33707d6UL) + GG(c,d,a,b,W[3],14,0xf4d50d87UL) + GG(b,c,d,a,W[8],20,0x455a14edUL) + GG(a,b,c,d,W[13],5,0xa9e3e905UL) + GG(d,a,b,c,W[2],9,0xfcefa3f8UL) + GG(c,d,a,b,W[7],14,0x676f02d9UL) + GG(b,c,d,a,W[12],20,0x8d2a4c8aUL) + HH(a,b,c,d,W[5],4,0xfffa3942UL) + HH(d,a,b,c,W[8],11,0x8771f681UL) + HH(c,d,a,b,W[11],16,0x6d9d6122UL) + HH(b,c,d,a,W[14],23,0xfde5380cUL) + HH(a,b,c,d,W[1],4,0xa4beea44UL) + HH(d,a,b,c,W[4],11,0x4bdecfa9UL) + HH(c,d,a,b,W[7],16,0xf6bb4b60UL) + HH(b,c,d,a,W[10],23,0xbebfbc70UL) + HH(a,b,c,d,W[13],4,0x289b7ec6UL) + HH(d,a,b,c,W[0],11,0xeaa127faUL) + HH(c,d,a,b,W[3],16,0xd4ef3085UL) + HH(b,c,d,a,W[6],23,0x04881d05UL) + HH(a,b,c,d,W[9],4,0xd9d4d039UL) + HH(d,a,b,c,W[12],11,0xe6db99e5UL) + HH(c,d,a,b,W[15],16,0x1fa27cf8UL) + HH(b,c,d,a,W[2],23,0xc4ac5665UL) + II(a,b,c,d,W[0],6,0xf4292244UL) + II(d,a,b,c,W[7],10,0x432aff97UL) + II(c,d,a,b,W[14],15,0xab9423a7UL) + II(b,c,d,a,W[5],21,0xfc93a039UL) + II(a,b,c,d,W[12],6,0x655b59c3UL) + II(d,a,b,c,W[3],10,0x8f0ccc92UL) + II(c,d,a,b,W[10],15,0xffeff47dUL) + II(b,c,d,a,W[1],21,0x85845dd1UL) + II(a,b,c,d,W[8],6,0x6fa87e4fUL) + II(d,a,b,c,W[15],10,0xfe2ce6e0UL) + II(c,d,a,b,W[6],15,0xa3014314UL) + II(b,c,d,a,W[13],21,0x4e0811a1UL) + II(a,b,c,d,W[4],6,0xf7537e82UL) + II(d,a,b,c,W[11],10,0xbd3af235UL) + II(c,d,a,b,W[2],15,0x2ad7d2bbUL) + II(b,c,d,a,W[9],21,0xeb86d391UL) +#endif + + md->md5.state[0] = md->md5.state[0] + a; + md->md5.state[1] = md->md5.state[1] + b; + md->md5.state[2] = md->md5.state[2] + c; + md->md5.state[3] = md->md5.state[3] + d; + + return CRYPT_OK; +} + +#ifdef LTC_CLEAN_STACK +static int md5_compress(hash_state *md, unsigned char *buf) +{ + int err; + err = _md5_compress(md, buf); + burn_stack(sizeof(ulong32) * 21); + return err; +} +#endif + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful +*/ +int md5_init(hash_state * md) +{ + LTC_ARGCHK(md != NULL); + md->md5.state[0] = 0x67452301UL; + md->md5.state[1] = 0xefcdab89UL; + md->md5.state[2] = 0x98badcfeUL; + md->md5.state[3] = 0x10325476UL; + md->md5.curlen = 0; + md->md5.length = 0; + return CRYPT_OK; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful +*/ +HASH_PROCESS(md5_process, md5_compress, md5, 64) + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (16 bytes) + @return CRYPT_OK if successful +*/ +int md5_done(hash_state * md, unsigned char *out) +{ + int i; + + LTC_ARGCHK(md != NULL); + LTC_ARGCHK(out != NULL); + + if (md->md5.curlen >= sizeof(md->md5.buf)) { + return CRYPT_INVALID_ARG; + } + + + /* increase the length of the message */ + md->md5.length += md->md5.curlen * 8; + + /* append the '1' bit */ + md->md5.buf[md->md5.curlen++] = (unsigned char)0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->md5.curlen > 56) { + while (md->md5.curlen < 64) { + md->md5.buf[md->md5.curlen++] = (unsigned char)0; + } + md5_compress(md, md->md5.buf); + md->md5.curlen = 0; + } + + /* pad upto 56 bytes of zeroes */ + while (md->md5.curlen < 56) { + md->md5.buf[md->md5.curlen++] = (unsigned char)0; + } + + /* store length */ + STORE64L(md->md5.length, md->md5.buf+56); + md5_compress(md, md->md5.buf); + + /* copy output */ + for (i = 0; i < 4; i++) { + STORE32L(md->md5.state[i], out+(4*i)); + } +#ifdef LTC_CLEAN_STACK + zeromem(md, sizeof(hash_state)); +#endif + return CRYPT_OK; +} + +/** + Self-test the hash + @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled +*/ +int md5_test(void) +{ + #ifndef LTC_TEST + return CRYPT_NOP; + #else + static const struct { + char *msg; + unsigned char hash[16]; + } tests[] = { + { "", + { 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, + 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e } }, + { "a", + {0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, + 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61 } }, + { "abc", + { 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, + 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 } }, + { "message digest", + { 0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d, + 0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0 } }, + { "abcdefghijklmnopqrstuvwxyz", + { 0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00, + 0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b } }, + { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + { 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5, + 0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f } }, + { "12345678901234567890123456789012345678901234567890123456789012345678901234567890", + { 0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55, + 0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a } }, + { NULL, { 0 } } + }; + + int i; + unsigned char tmp[16]; + hash_state md; + + for (i = 0; tests[i].msg != NULL; i++) { + md5_init(&md); + md5_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg)); + md5_done(&md, tmp); + if (XMEMCMP(tmp, tests[i].hash, 16) != 0) { + return CRYPT_FAIL_TESTVECTOR; + } + } + return CRYPT_OK; + #endif +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file ctr_encrypt.c + CTR implementation, encrypt data, Tom St Denis +*/ + + +#ifdef LTC_CTR_MODE + +/** + CTR encrypt + @param pt Plaintext + @param ct [out] Ciphertext + @param len Length of plaintext (octets) + @param ctr CTR state + @return CRYPT_OK if successful +*/ +int ctr_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CTR *ctr) +{ + int x, err; + + LTC_ARGCHK(pt != NULL); + LTC_ARGCHK(ct != NULL); + LTC_ARGCHK(ctr != NULL); + + if ((err = cipher_is_valid(ctr->cipher)) != CRYPT_OK) { + return err; + } + + /* is blocklen/padlen valid? */ + if (ctr->blocklen < 1 || ctr->blocklen > (int)sizeof(ctr->ctr) || + ctr->padlen < 0 || ctr->padlen > (int)sizeof(ctr->pad)) { + return CRYPT_INVALID_ARG; + } + +#ifdef LTC_FAST + if (ctr->blocklen % sizeof(LTC_FAST_TYPE)) { + return CRYPT_INVALID_ARG; + } +#endif + + /* handle acceleration only if pad is empty, accelerator is present and length is >= a block size */ + if ((ctr->padlen == ctr->blocklen) && cipher_descriptor[ctr->cipher].accel_ctr_encrypt != NULL && (len >= (unsigned long)ctr->blocklen)) { + if ((err = cipher_descriptor[ctr->cipher].accel_ctr_encrypt(pt, ct, len/ctr->blocklen, ctr->ctr, ctr->mode, &ctr->key)) != CRYPT_OK) { + return err; + } + len %= ctr->blocklen; + } + + while (len) { + /* is the pad empty? */ + if (ctr->padlen == ctr->blocklen) { + /* increment counter */ + if (ctr->mode == CTR_COUNTER_LITTLE_ENDIAN) { + /* little-endian */ + for (x = 0; x < ctr->ctrlen; x++) { + ctr->ctr[x] = (ctr->ctr[x] + (unsigned char)1) & (unsigned char)255; + if (ctr->ctr[x] != (unsigned char)0) { + break; + } + } + } else { + /* big-endian */ + for (x = ctr->blocklen-1; x >= ctr->ctrlen; x--) { + ctr->ctr[x] = (ctr->ctr[x] + (unsigned char)1) & (unsigned char)255; + if (ctr->ctr[x] != (unsigned char)0) { + break; + } + } + } + + /* encrypt it */ + if ((err = cipher_descriptor[ctr->cipher].ecb_encrypt(ctr->ctr, ctr->pad, &ctr->key)) != CRYPT_OK) { + return err; + } + ctr->padlen = 0; + } +#ifdef LTC_FAST + if (ctr->padlen == 0 && len >= (unsigned long)ctr->blocklen) { + for (x = 0; x < ctr->blocklen; x += sizeof(LTC_FAST_TYPE)) { + *((LTC_FAST_TYPE*)((unsigned char *)ct + x)) = *((LTC_FAST_TYPE*)((unsigned char *)pt + x)) ^ + *((LTC_FAST_TYPE*)((unsigned char *)ctr->pad + x)); + } + pt += ctr->blocklen; + ct += ctr->blocklen; + len -= ctr->blocklen; + ctr->padlen = ctr->blocklen; + continue; + } +#endif + *ct++ = *pt++ ^ ctr->pad[ctr->padlen++]; + --len; + } + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/modes/ctr/ctr_encrypt.c,v $ */ +/* $Revision: 1.22 $ */ +/* $Date: 2007/02/22 20:26:05 $ */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file ctr_done.c + CTR implementation, finish chain, Tom St Denis +*/ + +#ifdef LTC_CTR_MODE + +/** Terminate the chain + @param ctr The CTR chain to terminate + @return CRYPT_OK on success +*/ +int ctr_done(symmetric_CTR *ctr) +{ + int err; + LTC_ARGCHK(ctr != NULL); + + if ((err = cipher_is_valid(ctr->cipher)) != CRYPT_OK) { + return err; + } + cipher_descriptor[ctr->cipher].done(&ctr->key); + return CRYPT_OK; +} + + + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/modes/ctr/ctr_done.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file ctr_decrypt.c + CTR implementation, decrypt data, Tom St Denis +*/ + +#ifdef LTC_CTR_MODE + +/** + CTR decrypt + @param ct Ciphertext + @param pt [out] Plaintext + @param len Length of ciphertext (octets) + @param ctr CTR state + @return CRYPT_OK if successful +*/ +int ctr_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CTR *ctr) +{ + LTC_ARGCHK(pt != NULL); + LTC_ARGCHK(ct != NULL); + LTC_ARGCHK(ctr != NULL); + + return ctr_encrypt(ct, pt, len, ctr); +} + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/modes/ctr/ctr_decrypt.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file ctr_start.c + CTR implementation, start chain, Tom St Denis +*/ + + +#ifdef LTC_CTR_MODE + +/** + Initialize a CTR context + @param cipher The index of the cipher desired + @param IV The initial vector + @param key The secret key + @param keylen The length of the secret key (octets) + @param num_rounds Number of rounds in the cipher desired (0 for default) + @param ctr_mode The counter mode (CTR_COUNTER_LITTLE_ENDIAN or CTR_COUNTER_BIG_ENDIAN) + @param ctr The CTR state to initialize + @return CRYPT_OK if successful +*/ +int ctr_start( int cipher, + const unsigned char *IV, + const unsigned char *key, int keylen, + int num_rounds, int ctr_mode, + symmetric_CTR *ctr) +{ + int x, err; + + LTC_ARGCHK(IV != NULL); + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(ctr != NULL); + + /* bad param? */ + if ((err = cipher_is_valid(cipher)) != CRYPT_OK) { + return err; + } + + /* ctrlen == counter width */ + ctr->ctrlen = (ctr_mode & 255) ? (ctr_mode & 255) : cipher_descriptor[cipher].block_length; + if (ctr->ctrlen > cipher_descriptor[cipher].block_length) { + return CRYPT_INVALID_ARG; + } + + if ((ctr_mode & 0x1000) == CTR_COUNTER_BIG_ENDIAN) { + ctr->ctrlen = cipher_descriptor[cipher].block_length - ctr->ctrlen; + } + + /* setup cipher */ + if ((err = cipher_descriptor[cipher].setup(key, keylen, num_rounds, &ctr->key)) != CRYPT_OK) { + return err; + } + + /* copy ctr */ + ctr->blocklen = cipher_descriptor[cipher].block_length; + ctr->cipher = cipher; + ctr->padlen = 0; + ctr->mode = ctr_mode & 0x1000; + for (x = 0; x < ctr->blocklen; x++) { + ctr->ctr[x] = IV[x]; + } + + if (ctr_mode & LTC_CTR_RFC3686) { + /* increment the IV as per RFC 3686 */ + if (ctr->mode == CTR_COUNTER_LITTLE_ENDIAN) { + /* little-endian */ + for (x = 0; x < ctr->ctrlen; x++) { + ctr->ctr[x] = (ctr->ctr[x] + (unsigned char)1) & (unsigned char)255; + if (ctr->ctr[x] != (unsigned char)0) { + break; + } + } + } else { + /* big-endian */ + for (x = ctr->blocklen-1; x >= ctr->ctrlen; x--) { + ctr->ctr[x] = (ctr->ctr[x] + (unsigned char)1) & (unsigned char)255; + if (ctr->ctr[x] != (unsigned char)0) { + break; + } + } + } + } + + return cipher_descriptor[ctr->cipher].ecb_encrypt(ctr->ctr, ctr->pad, &ctr->key); +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/modes/ctr/ctr_start.c,v $ */ +/* $Revision: 1.15 $ */ +/* $Date: 2007/02/23 14:18:37 $ */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file ctr_setiv.c + CTR implementation, set IV, Tom St Denis +*/ + +#ifdef LTC_CTR_MODE + +/** + Set an initial vector + @param IV The initial vector + @param len The length of the vector (in octets) + @param ctr The CTR state + @return CRYPT_OK if successful +*/ +int ctr_setiv(const unsigned char *IV, unsigned long len, symmetric_CTR *ctr) +{ + int err; + + LTC_ARGCHK(IV != NULL); + LTC_ARGCHK(ctr != NULL); + + /* bad param? */ + if ((err = cipher_is_valid(ctr->cipher)) != CRYPT_OK) { + return err; + } + + if (len != (unsigned long)ctr->blocklen) { + return CRYPT_INVALID_ARG; + } + + /* set IV */ + XMEMCPY(ctr->ctr, IV, len); + + /* force next block */ + ctr->padlen = 0; + return cipher_descriptor[ctr->cipher].ecb_encrypt(IV, ctr->pad, &ctr->key); +} + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/modes/ctr/ctr_setiv.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file ctr_getiv.c + CTR implementation, get IV, Tom St Denis +*/ + +#ifdef LTC_CTR_MODE + +/** + Get the current initial vector + @param IV [out] The destination of the initial vector + @param len [in/out] The max size and resulting size of the initial vector + @param ctr The CTR state + @return CRYPT_OK if successful +*/ +int ctr_getiv(unsigned char *IV, unsigned long *len, symmetric_CTR *ctr) +{ + LTC_ARGCHK(IV != NULL); + LTC_ARGCHK(len != NULL); + LTC_ARGCHK(ctr != NULL); + if ((unsigned long)ctr->blocklen > *len) { + *len = ctr->blocklen; + return CRYPT_BUFFER_OVERFLOW; + } + XMEMCPY(IV, ctr->ctr, ctr->blocklen); + *len = ctr->blocklen; + + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/modes/ctr/ctr_getiv.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ \ No newline at end of file diff --git a/thirdparty/cwsc/thirdparty/tlse.c b/thirdparty/cwsc/thirdparty/tlse.c new file mode 100644 index 0000000..5fd0dc3 --- /dev/null +++ b/thirdparty/cwsc/thirdparty/tlse.c @@ -0,0 +1,10750 @@ +/******************************************************************************** + Copyright (c) 2016-2021, Eduard Suica + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + ********************************************************************************/ +#ifndef TLSE_C +#define TLSE_C + +#include +#include +#include +#include +#include +#ifdef _WIN32 +#ifdef SSL_COMPATIBLE_INTERFACE +#include +#endif +#include +#include +#ifndef strcasecmp + #define strcasecmp stricmp +#endif +#else +// hton* and ntoh* functions +#include +#include +#include +#endif + +#ifdef TLS_AMALGAMATION +#ifdef I +#pragma push_macro("I") +#define TLS_I_MACRO +#undef I +#endif +#include "libtomcrypt.c" +#ifdef TLS_I_MACRO +#pragma pop_macro("I") +#undef TLS_I_MACRO +#endif +#else +#include +#endif + +#if (CRYPT <= 0x0117) + #define LTC_PKCS_1_EMSA LTC_LTC_PKCS_1_EMSA + #define LTC_PKCS_1_V1_5 LTC_LTC_PKCS_1_V1_5 + #define LTC_PKCS_1_PSS LTC_LTC_PKCS_1_PSS +#endif + +#ifdef WITH_KTLS + #include + #include + #include + // should get uapi/linux/tls.h (linux headers) + // rename it to ktls.h and add it to your project + // or just include tls.h instead of ktls.h + #include "ktls.h" +#endif + +#include "tlse.h" +#ifdef TLS_CURVE25519 + #include "curve25519.c" +#endif +// using ChaCha20 implementation by D. J. Bernstein + +#ifndef TLS_FORWARD_SECRECY +#undef TLS_ECDSA_SUPPORTED +#endif + +#ifndef TLS_ECDSA_SUPPORTED +// disable client ECDSA if not supported +#undef TLS_CLIENT_ECDSA +#endif + +#define TLS_DH_DEFAULT_P "87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597" +#define TLS_DH_DEFAULT_G "3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659" +#define TLS_DHE_KEY_SIZE 2048 + +// you should never use weak DH groups (1024 bits) +// but if you have old devices (like grandstream ip phones) +// that can't handle 2048bit DHE, uncomment next lines +// and define TLS_WEAK_DH_LEGACY_DEVICES +// #ifdef TLS_WEAK_DH_LEGACY_DEVICES +// #define TLS_DH_DEFAULT_P "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C69A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C013ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD7098488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708DF1FB2BC2E4A4371" +// #define TLS_DH_DEFAULT_G "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507FD6406CFF14266D31266FEA1E5C41564B777E690F5504F213160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28AD662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24855E6EEB22B3B2E5" +// #define TLS_DHE_KEY_SIZE 1024 +// #endif + +#ifndef TLS_MALLOC + #define TLS_MALLOC(size) malloc(size) +#endif +#ifndef TLS_REALLOC + #define TLS_REALLOC(ptr, size) realloc(ptr, size) +#endif +#ifndef TLS_FREE + #define TLS_FREE(ptr) if (ptr) free(ptr) +#endif + +#define TLS_ERROR(err, statement) if (err) statement; + +#ifdef DEBUG +#define DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__) +#define DEBUG_DUMP_HEX(buf, len) {if (buf) { int _i_; for (_i_ = 0; _i_ < len; _i_++) { DEBUG_PRINT("%02X ", (unsigned int)(buf)[_i_]); } } else { fprintf(stderr, "(null)"); } } +#define DEBUG_INDEX(fields) print_index(fields) +#define DEBUG_DUMP(buf, length) fwrite(buf, 1, length, stderr); +#define DEBUG_DUMP_HEX_LABEL(title, buf, len) {fprintf(stderr, "%s (%i): ", title, (int)len); DEBUG_DUMP_HEX(buf, len); fprintf(stderr, "\n");} +#else +#define DEBUG_PRINT(...) { } +#define DEBUG_DUMP_HEX(buf, len) { } +#define DEBUG_INDEX(fields) { } +#define DEBUG_DUMP(buf, length) { } +#define DEBUG_DUMP_HEX_LABEL(title, buf, len) { } +#endif + +#ifndef htonll +#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) +#endif + +#ifndef ntohll +#define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32)) +#endif + +#define TLS_CHANGE_CIPHER 0x14 +#define TLS_ALERT 0x15 +#define TLS_HANDSHAKE 0x16 +#define TLS_APPLICATION_DATA 0x17 + +#define TLS_SERIALIZED_OBJECT 0xFE + +#define TLS_CLIENT_HELLO_MINSIZE 41 +#define TLS_CLIENT_RANDOM_SIZE 32 +#define TLS_SERVER_RANDOM_SIZE 32 +#define TLS_MAX_SESSION_ID 32 +#define TLS_SHA256_MAC_SIZE 32 +#define TLS_SHA1_MAC_SIZE 20 +#define TLS_SHA384_MAC_SIZE 48 +#define TLS_MAX_MAC_SIZE TLS_SHA384_MAC_SIZE + // 160 +#define TLS_MAX_KEY_EXPANSION_SIZE 192 +// 512bits (sha256) = 64 bytes +#define TLS_MAX_HASH_LEN 64 +#define TLS_AES_IV_LENGTH 16 +#define TLS_AES_BLOCK_SIZE 16 +#define TLS_AES_GCM_IV_LENGTH 4 +#define TLS_13_AES_GCM_IV_LENGTH 12 +#define TLS_GCM_TAG_LEN 16 +#define TLS_MAX_TAG_LEN 16 +#define TLS_MIN_FINISHED_OPAQUE_LEN 12 + +#define TLS_BLOB_INCREMENT 0xFFF +#define TLS_ASN1_MAXLEVEL 0xFF + +#define DTLS_COOKIE_SIZE 32 + +#define TLS_MAX_SHA_SIZE 48 +// 16(md5) + 20(sha1) +#define TLS_V11_HASH_SIZE 36 +#define TLS_MAX_HASH_SIZE TLS_MAX_SHA_SIZE +// 16(md5) + 20(sha1) +#define TLS_MAX_RSA_KEY 2048 + +#define TLS_MAXTLS_APP_SIZE 0x4000 +// max 1 second sleep +#define TLS_MAX_ERROR_SLEEP_uS 1000000 +// max 5 seconds context sleep +#define TLS_MAX_ERROR_IDLE_S 5 + +#define TLS_V13_MAX_KEY_SIZE 32 +#define TLS_V13_MAX_IV_SIZE 12 + +#define VERSION_SUPPORTED(version, err) if ((version != TLS_V13) && (version != TLS_V12) && (version != TLS_V11) && (version != TLS_V10) && (version != DTLS_V13) && (version != DTLS_V12) && (version != DTLS_V10)) { if ((version == SSL_V30) && (context->connection_status == 0)) { version = TLS_V12; } else { DEBUG_PRINT("UNSUPPORTED TLS VERSION %x\n", (int)version); return err;} } +#define CHECK_SIZE(size, buf_size, err) if (((int)(size) > (int)(buf_size)) || ((int)(buf_size) < 0)) return err; +#define TLS_IMPORT_CHECK_SIZE(buf_pos, size, buf_size) if (((int)size > (int)buf_size - buf_pos) || ((int)buf_pos > (int)buf_size)) { DEBUG_PRINT("IMPORT ELEMENT SIZE ERROR\n"); tls_destroy_context(context); return NULL; } +#define CHECK_HANDSHAKE_STATE(context, n, limit) { if (context->hs_messages[n] >= limit) { DEBUG_PRINT("* UNEXPECTED MESSAGE (%i)\n", (int)n); payload_res = TLS_UNEXPECTED_MESSAGE; break; } context->hs_messages[n]++; } + +#if CRYPT > 0x0118 + #define TLS_TOMCRYPT_PRIVATE_DP(key) ((key).dp) + #define TLS_TOMCRYPT_PRIVATE_SET_INDEX(key, k_idx) +#else + #define TLS_TOMCRYPT_PRIVATE_DP(key) ((key)->dp) + #define TLS_TOMCRYPT_PRIVATE_SET_INDEX(key, k_idx) key->idx = k_idx +#endif + +#ifdef TLS_WITH_CHACHA20_POLY1305 +#define TLS_CHACHA20_IV_LENGTH 12 + +// ChaCha20 implementation by D. J. Bernstein +// Public domain. + +#define CHACHA_MINKEYLEN 16 +#define CHACHA_NONCELEN 8 +#define CHACHA_NONCELEN_96 12 +#define CHACHA_CTRLEN 8 +#define CHACHA_CTRLEN_96 4 +#define CHACHA_STATELEN (CHACHA_NONCELEN+CHACHA_CTRLEN) +#define CHACHA_BLOCKLEN 64 + +#define POLY1305_MAX_AAD 32 +#define POLY1305_KEYLEN 32 +#define POLY1305_TAGLEN 16 + +#define u_int unsigned int +#define uint8_t unsigned char +#define u_char unsigned char +#ifndef NULL +#define NULL (void *)0 +#endif + +#if (CRYPT >= 0x0117) && (0) + // to do: use ltc chacha/poly1305 implementation (working on big-endian machines) + #define chacha_ctx chacha20poly1305_state + #define poly1305_context poly1305_state + + #define _private_tls_poly1305_init(ctx, key, len) poly1305_init(ctx, key, len) + #define _private_tls_poly1305_update(ctx, in, len) poly1305_process(ctx, in, len) + #define _private_tls_poly1305_finish(ctx, mac) poly1305_done(ctx, mac, 16) +#else +struct chacha_ctx { + u_int input[16]; + uint8_t ks[CHACHA_BLOCKLEN]; + uint8_t unused; +}; + +static inline void chacha_keysetup(struct chacha_ctx *x, const u_char *k, u_int kbits); +static inline void chacha_ivsetup(struct chacha_ctx *x, const u_char *iv, const u_char *ctr); +static inline void chacha_ivsetup_96bitnonce(struct chacha_ctx *x, const u_char *iv, const u_char *ctr); +static inline void chacha_encrypt_bytes(struct chacha_ctx *x, const u_char *m, u_char *c, u_int bytes); +static inline int poly1305_generate_key(unsigned char *key256, unsigned char *nonce, unsigned int noncelen, unsigned char *poly_key, unsigned int counter); + +#define poly1305_block_size 16 +#define poly1305_context poly1305_state_internal_t + +//========== ChaCha20 from D. J. Bernstein ========= // +// Source available at https://cr.yp.to/chacha.html // + +typedef unsigned char u8; +typedef unsigned int u32; + +typedef struct chacha_ctx chacha_ctx; + +#define U8C(v) (v##U) +#define U32C(v) (v##U) + +#define U8V(v) ((u8)(v) & U8C(0xFF)) +#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF)) + +#define ROTL32(v, n) \ + (U32V((v) << (n)) | ((v) >> (32 - (n)))) + +#define _private_tls_U8TO32_LITTLE(p) \ + (((u32)((p)[0])) | \ + ((u32)((p)[1]) << 8) | \ + ((u32)((p)[2]) << 16) | \ + ((u32)((p)[3]) << 24)) + +#define _private_tls_U32TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v)); \ + (p)[1] = U8V((v) >> 8); \ + (p)[2] = U8V((v) >> 16); \ + (p)[3] = U8V((v) >> 24); \ + } while (0) + +#define ROTATE(v,c) (ROTL32(v,c)) +#define XOR(v,w) ((v) ^ (w)) +#define PLUS(v,w) (U32V((v) + (w))) +#define PLUSONE(v) (PLUS((v),1)) + +#define QUARTERROUND(a,b,c,d) \ + a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ + a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); + +static const char sigma[] = "expand 32-byte k"; +static const char tau[] = "expand 16-byte k"; + +static inline void chacha_keysetup(chacha_ctx *x, const u8 *k, u32 kbits) { + const char *constants; + + x->input[4] = _private_tls_U8TO32_LITTLE(k + 0); + x->input[5] = _private_tls_U8TO32_LITTLE(k + 4); + x->input[6] = _private_tls_U8TO32_LITTLE(k + 8); + x->input[7] = _private_tls_U8TO32_LITTLE(k + 12); + if (kbits == 256) { /* recommended */ + k += 16; + constants = sigma; + } else { /* kbits == 128 */ + constants = tau; + } + x->input[8] = _private_tls_U8TO32_LITTLE(k + 0); + x->input[9] = _private_tls_U8TO32_LITTLE(k + 4); + x->input[10] = _private_tls_U8TO32_LITTLE(k + 8); + x->input[11] = _private_tls_U8TO32_LITTLE(k + 12); + x->input[0] = _private_tls_U8TO32_LITTLE(constants + 0); + x->input[1] = _private_tls_U8TO32_LITTLE(constants + 4); + x->input[2] = _private_tls_U8TO32_LITTLE(constants + 8); + x->input[3] = _private_tls_U8TO32_LITTLE(constants + 12); +} + +static inline void chacha_key(chacha_ctx *x, u8 *k) { + _private_tls_U32TO8_LITTLE(k, x->input[4]); + _private_tls_U32TO8_LITTLE(k + 4, x->input[5]); + _private_tls_U32TO8_LITTLE(k + 8, x->input[6]); + _private_tls_U32TO8_LITTLE(k + 12, x->input[7]); + + _private_tls_U32TO8_LITTLE(k + 16, x->input[8]); + _private_tls_U32TO8_LITTLE(k + 20, x->input[9]); + _private_tls_U32TO8_LITTLE(k + 24, x->input[10]); + _private_tls_U32TO8_LITTLE(k + 28, x->input[11]); +} + +static inline void chacha_nonce(chacha_ctx *x, u8 *nonce) { + _private_tls_U32TO8_LITTLE(nonce + 0, x->input[13]); + _private_tls_U32TO8_LITTLE(nonce + 4, x->input[14]); + _private_tls_U32TO8_LITTLE(nonce + 8, x->input[15]); +} + +static inline void chacha_ivsetup(chacha_ctx *x, const u8 *iv, const u8 *counter) { + x->input[12] = counter == NULL ? 0 : _private_tls_U8TO32_LITTLE(counter + 0); + x->input[13] = counter == NULL ? 0 : _private_tls_U8TO32_LITTLE(counter + 4); + if (iv) { + x->input[14] = _private_tls_U8TO32_LITTLE(iv + 0); + x->input[15] = _private_tls_U8TO32_LITTLE(iv + 4); + } +} + +static inline void chacha_ivsetup_96bitnonce(chacha_ctx *x, const u8 *iv, const u8 *counter) { + x->input[12] = counter == NULL ? 0 : _private_tls_U8TO32_LITTLE(counter + 0); + if (iv) { + x->input[13] = _private_tls_U8TO32_LITTLE(iv + 0); + x->input[14] = _private_tls_U8TO32_LITTLE(iv + 4); + x->input[15] = _private_tls_U8TO32_LITTLE(iv + 8); + } +} + +static inline void chacha_ivupdate(chacha_ctx *x, const u8 *iv, const u8 *aad, const u8 *counter) { + x->input[12] = counter == NULL ? 0 : _private_tls_U8TO32_LITTLE(counter + 0); + x->input[13] = _private_tls_U8TO32_LITTLE(iv + 0); + x->input[14] = _private_tls_U8TO32_LITTLE(iv + 4) ^ _private_tls_U8TO32_LITTLE(aad); + x->input[15] = _private_tls_U8TO32_LITTLE(iv + 8) ^ _private_tls_U8TO32_LITTLE(aad + 4); +} + +static inline void chacha_encrypt_bytes(chacha_ctx *x, const u8 *m, u8 *c, u32 bytes) { + u32 x0, x1, x2, x3, x4, x5, x6, x7; + u32 x8, x9, x10, x11, x12, x13, x14, x15; + u32 j0, j1, j2, j3, j4, j5, j6, j7; + u32 j8, j9, j10, j11, j12, j13, j14, j15; + u8 *ctarget = NULL; + u8 tmp[64]; + u_int i; + + if (!bytes) + return; + + j0 = x->input[0]; + j1 = x->input[1]; + j2 = x->input[2]; + j3 = x->input[3]; + j4 = x->input[4]; + j5 = x->input[5]; + j6 = x->input[6]; + j7 = x->input[7]; + j8 = x->input[8]; + j9 = x->input[9]; + j10 = x->input[10]; + j11 = x->input[11]; + j12 = x->input[12]; + j13 = x->input[13]; + j14 = x->input[14]; + j15 = x->input[15]; + + for (;;) { + if (bytes < 64) { + for (i = 0; i < bytes; ++i) + tmp[i] = m[i]; + m = tmp; + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 20; i > 0; i -= 2) { + QUARTERROUND(x0, x4, x8, x12) + QUARTERROUND(x1, x5, x9, x13) + QUARTERROUND(x2, x6, x10, x14) + QUARTERROUND(x3, x7, x11, x15) + QUARTERROUND(x0, x5, x10, x15) + QUARTERROUND(x1, x6, x11, x12) + QUARTERROUND(x2, x7, x8, x13) + QUARTERROUND(x3, x4, x9, x14) + } + x0 = PLUS(x0, j0); + x1 = PLUS(x1, j1); + x2 = PLUS(x2, j2); + x3 = PLUS(x3, j3); + x4 = PLUS(x4, j4); + x5 = PLUS(x5, j5); + x6 = PLUS(x6, j6); + x7 = PLUS(x7, j7); + x8 = PLUS(x8, j8); + x9 = PLUS(x9, j9); + x10 = PLUS(x10, j10); + x11 = PLUS(x11, j11); + x12 = PLUS(x12, j12); + x13 = PLUS(x13, j13); + x14 = PLUS(x14, j14); + x15 = PLUS(x15, j15); + + if (bytes < 64) { + _private_tls_U32TO8_LITTLE(x->ks + 0, x0); + _private_tls_U32TO8_LITTLE(x->ks + 4, x1); + _private_tls_U32TO8_LITTLE(x->ks + 8, x2); + _private_tls_U32TO8_LITTLE(x->ks + 12, x3); + _private_tls_U32TO8_LITTLE(x->ks + 16, x4); + _private_tls_U32TO8_LITTLE(x->ks + 20, x5); + _private_tls_U32TO8_LITTLE(x->ks + 24, x6); + _private_tls_U32TO8_LITTLE(x->ks + 28, x7); + _private_tls_U32TO8_LITTLE(x->ks + 32, x8); + _private_tls_U32TO8_LITTLE(x->ks + 36, x9); + _private_tls_U32TO8_LITTLE(x->ks + 40, x10); + _private_tls_U32TO8_LITTLE(x->ks + 44, x11); + _private_tls_U32TO8_LITTLE(x->ks + 48, x12); + _private_tls_U32TO8_LITTLE(x->ks + 52, x13); + _private_tls_U32TO8_LITTLE(x->ks + 56, x14); + _private_tls_U32TO8_LITTLE(x->ks + 60, x15); + } + + x0 = XOR(x0, _private_tls_U8TO32_LITTLE(m + 0)); + x1 = XOR(x1, _private_tls_U8TO32_LITTLE(m + 4)); + x2 = XOR(x2, _private_tls_U8TO32_LITTLE(m + 8)); + x3 = XOR(x3, _private_tls_U8TO32_LITTLE(m + 12)); + x4 = XOR(x4, _private_tls_U8TO32_LITTLE(m + 16)); + x5 = XOR(x5, _private_tls_U8TO32_LITTLE(m + 20)); + x6 = XOR(x6, _private_tls_U8TO32_LITTLE(m + 24)); + x7 = XOR(x7, _private_tls_U8TO32_LITTLE(m + 28)); + x8 = XOR(x8, _private_tls_U8TO32_LITTLE(m + 32)); + x9 = XOR(x9, _private_tls_U8TO32_LITTLE(m + 36)); + x10 = XOR(x10, _private_tls_U8TO32_LITTLE(m + 40)); + x11 = XOR(x11, _private_tls_U8TO32_LITTLE(m + 44)); + x12 = XOR(x12, _private_tls_U8TO32_LITTLE(m + 48)); + x13 = XOR(x13, _private_tls_U8TO32_LITTLE(m + 52)); + x14 = XOR(x14, _private_tls_U8TO32_LITTLE(m + 56)); + x15 = XOR(x15, _private_tls_U8TO32_LITTLE(m + 60)); + + j12 = PLUSONE(j12); + if (!j12) { + j13 = PLUSONE(j13); + /* + * Stopping at 2^70 bytes per nonce is the user's + * responsibility. + */ + } + + _private_tls_U32TO8_LITTLE(c + 0, x0); + _private_tls_U32TO8_LITTLE(c + 4, x1); + _private_tls_U32TO8_LITTLE(c + 8, x2); + _private_tls_U32TO8_LITTLE(c + 12, x3); + _private_tls_U32TO8_LITTLE(c + 16, x4); + _private_tls_U32TO8_LITTLE(c + 20, x5); + _private_tls_U32TO8_LITTLE(c + 24, x6); + _private_tls_U32TO8_LITTLE(c + 28, x7); + _private_tls_U32TO8_LITTLE(c + 32, x8); + _private_tls_U32TO8_LITTLE(c + 36, x9); + _private_tls_U32TO8_LITTLE(c + 40, x10); + _private_tls_U32TO8_LITTLE(c + 44, x11); + _private_tls_U32TO8_LITTLE(c + 48, x12); + _private_tls_U32TO8_LITTLE(c + 52, x13); + _private_tls_U32TO8_LITTLE(c + 56, x14); + _private_tls_U32TO8_LITTLE(c + 60, x15); + + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0; i < bytes; ++i) + ctarget[i] = c[i]; + } + x->input[12] = j12; + x->input[13] = j13; + x->unused = 64 - bytes; + return; + } + bytes -= 64; + c += 64; + m += 64; + } +} + +static inline void chacha20_block(chacha_ctx *x, unsigned char *c, u_int len) { + u_int i; + + unsigned int state[16]; + for (i = 0; i < 16; i++) + state[i] = x->input[i]; + for (i = 20; i > 0; i -= 2) { + QUARTERROUND(state[0], state[4], state[8], state[12]) + QUARTERROUND(state[1], state[5], state[9], state[13]) + QUARTERROUND(state[2], state[6], state[10], state[14]) + QUARTERROUND(state[3], state[7], state[11], state[15]) + QUARTERROUND(state[0], state[5], state[10], state[15]) + QUARTERROUND(state[1], state[6], state[11], state[12]) + QUARTERROUND(state[2], state[7], state[8], state[13]) + QUARTERROUND(state[3], state[4], state[9], state[14]) + } + + for (i = 0; i < 16; i++) + x->input[i] = PLUS(x->input[i], state[i]); + + for (i = 0; i < len; i += 4) { + _private_tls_U32TO8_LITTLE(c + i, x->input[i/4]); + } +} + +static inline int poly1305_generate_key(unsigned char *key256, unsigned char *nonce, unsigned int noncelen, unsigned char *poly_key, unsigned int counter) { + struct chacha_ctx ctx; + uint64_t ctr; + memset(&ctx, 0, sizeof(ctx)); + chacha_keysetup(&ctx, key256, 256); + switch (noncelen) { + case 8: + ctr = counter; + chacha_ivsetup(&ctx, nonce, (unsigned char *)&ctr); + break; + case 12: + chacha_ivsetup_96bitnonce(&ctx, nonce, (unsigned char *)&counter); + break; + default: + return -1; + } + chacha20_block(&ctx, poly_key, POLY1305_KEYLEN); + return 0; +} + +/* 17 + sizeof(size_t) + 14*sizeof(unsigned long) */ +typedef struct poly1305_state_internal_t { + unsigned long r[5]; + unsigned long h[5]; + unsigned long pad[4]; + size_t leftover; + unsigned char buffer[poly1305_block_size]; + unsigned char final; +} poly1305_state_internal_t; + +/* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little endian */ +static unsigned long _private_tls_U8TO32(const unsigned char *p) { + return + (((unsigned long)(p[0] & 0xff) ) | + ((unsigned long)(p[1] & 0xff) << 8) | + ((unsigned long)(p[2] & 0xff) << 16) | + ((unsigned long)(p[3] & 0xff) << 24)); +} + +/* store a 32 bit unsigned integer as four 8 bit unsigned integers in little endian */ +static void _private_tls_U32TO8(unsigned char *p, unsigned long v) { + p[0] = (v ) & 0xff; + p[1] = (v >> 8) & 0xff; + p[2] = (v >> 16) & 0xff; + p[3] = (v >> 24) & 0xff; +} + +void _private_tls_poly1305_init(poly1305_context *ctx, const unsigned char key[32]) { + poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; + + /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */ + st->r[0] = (_private_tls_U8TO32(&key[ 0]) ) & 0x3ffffff; + st->r[1] = (_private_tls_U8TO32(&key[ 3]) >> 2) & 0x3ffff03; + st->r[2] = (_private_tls_U8TO32(&key[ 6]) >> 4) & 0x3ffc0ff; + st->r[3] = (_private_tls_U8TO32(&key[ 9]) >> 6) & 0x3f03fff; + st->r[4] = (_private_tls_U8TO32(&key[12]) >> 8) & 0x00fffff; + + /* h = 0 */ + st->h[0] = 0; + st->h[1] = 0; + st->h[2] = 0; + st->h[3] = 0; + st->h[4] = 0; + + /* save pad for later */ + st->pad[0] = _private_tls_U8TO32(&key[16]); + st->pad[1] = _private_tls_U8TO32(&key[20]); + st->pad[2] = _private_tls_U8TO32(&key[24]); + st->pad[3] = _private_tls_U8TO32(&key[28]); + + st->leftover = 0; + st->final = 0; +} + +static void _private_tls_poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) { + const unsigned long hibit = (st->final) ? 0 : (1UL << 24); /* 1 << 128 */ + unsigned long r0,r1,r2,r3,r4; + unsigned long s1,s2,s3,s4; + unsigned long h0,h1,h2,h3,h4; + unsigned long long d0,d1,d2,d3,d4; + unsigned long c; + + r0 = st->r[0]; + r1 = st->r[1]; + r2 = st->r[2]; + r3 = st->r[3]; + r4 = st->r[4]; + + s1 = r1 * 5; + s2 = r2 * 5; + s3 = r3 * 5; + s4 = r4 * 5; + + h0 = st->h[0]; + h1 = st->h[1]; + h2 = st->h[2]; + h3 = st->h[3]; + h4 = st->h[4]; + + while (bytes >= poly1305_block_size) { + /* h += m[i] */ + h0 += (_private_tls_U8TO32(m+ 0) ) & 0x3ffffff; + h1 += (_private_tls_U8TO32(m+ 3) >> 2) & 0x3ffffff; + h2 += (_private_tls_U8TO32(m+ 6) >> 4) & 0x3ffffff; + h3 += (_private_tls_U8TO32(m+ 9) >> 6) & 0x3ffffff; + h4 += (_private_tls_U8TO32(m+12) >> 8) | hibit; + + /* h *= r */ + d0 = ((unsigned long long)h0 * r0) + ((unsigned long long)h1 * s4) + ((unsigned long long)h2 * s3) + ((unsigned long long)h3 * s2) + ((unsigned long long)h4 * s1); + d1 = ((unsigned long long)h0 * r1) + ((unsigned long long)h1 * r0) + ((unsigned long long)h2 * s4) + ((unsigned long long)h3 * s3) + ((unsigned long long)h4 * s2); + d2 = ((unsigned long long)h0 * r2) + ((unsigned long long)h1 * r1) + ((unsigned long long)h2 * r0) + ((unsigned long long)h3 * s4) + ((unsigned long long)h4 * s3); + d3 = ((unsigned long long)h0 * r3) + ((unsigned long long)h1 * r2) + ((unsigned long long)h2 * r1) + ((unsigned long long)h3 * r0) + ((unsigned long long)h4 * s4); + d4 = ((unsigned long long)h0 * r4) + ((unsigned long long)h1 * r3) + ((unsigned long long)h2 * r2) + ((unsigned long long)h3 * r1) + ((unsigned long long)h4 * r0); + + /* (partial) h %= p */ + c = (unsigned long)(d0 >> 26); h0 = (unsigned long)d0 & 0x3ffffff; + d1 += c; c = (unsigned long)(d1 >> 26); h1 = (unsigned long)d1 & 0x3ffffff; + d2 += c; c = (unsigned long)(d2 >> 26); h2 = (unsigned long)d2 & 0x3ffffff; + d3 += c; c = (unsigned long)(d3 >> 26); h3 = (unsigned long)d3 & 0x3ffffff; + d4 += c; c = (unsigned long)(d4 >> 26); h4 = (unsigned long)d4 & 0x3ffffff; + h0 += c * 5; c = (h0 >> 26); h0 = h0 & 0x3ffffff; + h1 += c; + + m += poly1305_block_size; + bytes -= poly1305_block_size; + } + + st->h[0] = h0; + st->h[1] = h1; + st->h[2] = h2; + st->h[3] = h3; + st->h[4] = h4; +} + +void _private_tls_poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) { + poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; + unsigned long h0,h1,h2,h3,h4,c; + unsigned long g0,g1,g2,g3,g4; + unsigned long long f; + unsigned long mask; + + /* process the remaining block */ + if (st->leftover) { + size_t i = st->leftover; + st->buffer[i++] = 1; + for (; i < poly1305_block_size; i++) + st->buffer[i] = 0; + st->final = 1; + _private_tls_poly1305_blocks(st, st->buffer, poly1305_block_size); + } + + /* fully carry h */ + h0 = st->h[0]; + h1 = st->h[1]; + h2 = st->h[2]; + h3 = st->h[3]; + h4 = st->h[4]; + + c = h1 >> 26; h1 = h1 & 0x3ffffff; + h2 += c; c = h2 >> 26; h2 = h2 & 0x3ffffff; + h3 += c; c = h3 >> 26; h3 = h3 & 0x3ffffff; + h4 += c; c = h4 >> 26; h4 = h4 & 0x3ffffff; + h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff; + h1 += c; + + /* compute h + -p */ + g0 = h0 + 5; c = g0 >> 26; g0 &= 0x3ffffff; + g1 = h1 + c; c = g1 >> 26; g1 &= 0x3ffffff; + g2 = h2 + c; c = g2 >> 26; g2 &= 0x3ffffff; + g3 = h3 + c; c = g3 >> 26; g3 &= 0x3ffffff; + g4 = h4 + c - (1UL << 26); + + /* select h if h < p, or h + -p if h >= p */ + mask = (g4 >> ((sizeof(unsigned long) * 8) - 1)) - 1; + g0 &= mask; + g1 &= mask; + g2 &= mask; + g3 &= mask; + g4 &= mask; + mask = ~mask; + h0 = (h0 & mask) | g0; + h1 = (h1 & mask) | g1; + h2 = (h2 & mask) | g2; + h3 = (h3 & mask) | g3; + h4 = (h4 & mask) | g4; + + /* h = h % (2^128) */ + h0 = ((h0 ) | (h1 << 26)) & 0xffffffff; + h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff; + h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff; + h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff; + + /* mac = (h + pad) % (2^128) */ + f = (unsigned long long)h0 + st->pad[0] ; h0 = (unsigned long)f; + f = (unsigned long long)h1 + st->pad[1] + (f >> 32); h1 = (unsigned long)f; + f = (unsigned long long)h2 + st->pad[2] + (f >> 32); h2 = (unsigned long)f; + f = (unsigned long long)h3 + st->pad[3] + (f >> 32); h3 = (unsigned long)f; + + _private_tls_U32TO8(mac + 0, h0); + _private_tls_U32TO8(mac + 4, h1); + _private_tls_U32TO8(mac + 8, h2); + _private_tls_U32TO8(mac + 12, h3); + + /* zero out the state */ + st->h[0] = 0; + st->h[1] = 0; + st->h[2] = 0; + st->h[3] = 0; + st->h[4] = 0; + st->r[0] = 0; + st->r[1] = 0; + st->r[2] = 0; + st->r[3] = 0; + st->r[4] = 0; + st->pad[0] = 0; + st->pad[1] = 0; + st->pad[2] = 0; + st->pad[3] = 0; +} + +void _private_tls_poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) { + poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; + size_t i; + /* handle leftover */ + if (st->leftover) { + size_t want = (poly1305_block_size - st->leftover); + if (want > bytes) + want = bytes; + for (i = 0; i < want; i++) + st->buffer[st->leftover + i] = m[i]; + bytes -= want; + m += want; + st->leftover += want; + if (st->leftover < poly1305_block_size) + return; + _private_tls_poly1305_blocks(st, st->buffer, poly1305_block_size); + st->leftover = 0; + } + + /* process full blocks */ + if (bytes >= poly1305_block_size) { + size_t want = (bytes & ~(poly1305_block_size - 1)); + _private_tls_poly1305_blocks(st, m, want); + m += want; + bytes -= want; + } + + /* store leftover */ + if (bytes) { + for (i = 0; i < bytes; i++) + st->buffer[st->leftover + i] = m[i]; + st->leftover += bytes; + } +} + +int poly1305_verify(const unsigned char mac1[16], const unsigned char mac2[16]) { + size_t i; + unsigned int dif = 0; + for (i = 0; i < 16; i++) + dif |= (mac1[i] ^ mac2[i]); + dif = (dif - 1) >> ((sizeof(unsigned int) * 8) - 1); + return (dif & 1); +} + +void chacha20_poly1305_key(struct chacha_ctx *ctx, unsigned char *poly1305_key) { + unsigned char key[32]; + unsigned char nonce[12]; + chacha_key(ctx, key); + chacha_nonce(ctx, nonce); + poly1305_generate_key(key, nonce, sizeof(nonce), poly1305_key, 0); +} + +int chacha20_poly1305_aead(struct chacha_ctx *ctx, unsigned char *pt, unsigned int len, unsigned char *aad, unsigned int aad_len, unsigned char *poly_key, unsigned char *out) { + static unsigned char zeropad[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + if (aad_len > POLY1305_MAX_AAD) + return -1; + + unsigned int counter = 1; + chacha_ivsetup_96bitnonce(ctx, NULL, (unsigned char *)&counter); + chacha_encrypt_bytes(ctx, pt, out, len); + + poly1305_context aead_ctx; + _private_tls_poly1305_init(&aead_ctx, poly_key); + _private_tls_poly1305_update(&aead_ctx, aad, aad_len); + int rem = aad_len % 16; + if (rem) + _private_tls_poly1305_update(&aead_ctx, zeropad, 16 - rem); + _private_tls_poly1305_update(&aead_ctx, out, len); + rem = len % 16; + if (rem) + _private_tls_poly1305_update(&aead_ctx, zeropad, 16 - rem); + + unsigned char trail[16]; + _private_tls_U32TO8(trail, aad_len); + *(int *)(trail + 4) = 0; + _private_tls_U32TO8(trail + 8, len); + *(int *)(trail + 12) = 0; + + _private_tls_poly1305_update(&aead_ctx, trail, 16); + _private_tls_poly1305_finish(&aead_ctx, out + len); + + return len + POLY1305_TAGLEN; +} +#endif +#endif + +typedef enum { + KEA_dhe_dss, + KEA_dhe_rsa, + KEA_dh_anon, + KEA_rsa, + KEA_dh_dss, + KEA_dh_rsa, + KEA_ec_diffie_hellman +} KeyExchangeAlgorithm; + +typedef enum { + rsa_sign = 1, + dss_sign = 2, + rsa_fixed_dh = 3, + dss_fixed_dh = 4, + rsa_ephemeral_dh_RESERVED = 5, + dss_ephemeral_dh_RESERVED = 6, + fortezza_dms_RESERVED = 20, + ecdsa_sign = 64, + rsa_fixed_ecdh = 65, + ecdsa_fixed_ecdh = 66 +} TLSClientCertificateType; + +typedef enum { + _none = 0, + md5 = 1, + sha1 = 2, + sha224 = 3, + sha256 = 4, + sha384 = 5, + sha512 = 6, + _md5_sha1 = 255 +} TLSHashAlgorithm; + +typedef enum { + anonymous = 0, + rsa = 1, + dsa = 2, + ecdsa = 3 +} TLSSignatureAlgorithm; + +struct _private_OID_chain { + void *top; + unsigned char *oid; +}; + +struct TLSCertificate { + unsigned short version; + unsigned int algorithm; + unsigned int key_algorithm; + unsigned int ec_algorithm; + unsigned char *exponent; + unsigned int exponent_len; + unsigned char *pk; + unsigned int pk_len; + unsigned char *priv; + unsigned int priv_len; + unsigned char *issuer_country; + unsigned char *issuer_state; + unsigned char *issuer_location; + unsigned char *issuer_entity; + unsigned char *issuer_subject; + unsigned char *not_before; + unsigned char *not_after; + unsigned char *country; + unsigned char *state; + unsigned char *location; + unsigned char *entity; + unsigned char *subject; + unsigned char **san; + unsigned short san_length; + unsigned char *ocsp; + unsigned char *serial_number; + unsigned int serial_len; + unsigned char *sign_key; + unsigned int sign_len; + unsigned char *fingerprint; + unsigned char *der_bytes; + unsigned int der_len; + unsigned char *bytes; + unsigned int len; +}; + +typedef struct { + union { + symmetric_CBC aes_local; + gcm_state aes_gcm_local; +#ifdef TLS_WITH_CHACHA20_POLY1305 + chacha_ctx chacha_local; +#endif + } ctx_local; + union { + symmetric_CBC aes_remote; + gcm_state aes_gcm_remote; +#ifdef TLS_WITH_CHACHA20_POLY1305 + chacha_ctx chacha_remote; +#endif + } ctx_remote; + union { + unsigned char local_mac[TLS_MAX_MAC_SIZE]; + unsigned char local_aead_iv[TLS_AES_GCM_IV_LENGTH]; +#ifdef WITH_TLS_13 + unsigned char local_iv[TLS_13_AES_GCM_IV_LENGTH]; +#endif +#ifdef TLS_WITH_CHACHA20_POLY1305 + unsigned char local_nonce[TLS_CHACHA20_IV_LENGTH]; +#endif + } ctx_local_mac; + union { + unsigned char remote_aead_iv[TLS_AES_GCM_IV_LENGTH]; + unsigned char remote_mac[TLS_MAX_MAC_SIZE]; +#ifdef WITH_TLS_13 + unsigned char remote_iv[TLS_13_AES_GCM_IV_LENGTH]; +#endif +#ifdef TLS_WITH_CHACHA20_POLY1305 + unsigned char remote_nonce[TLS_CHACHA20_IV_LENGTH]; +#endif + } ctx_remote_mac; + unsigned char created; +} TLSCipher; + +typedef struct { + hash_state hash32; + hash_state hash48; +#ifdef TLS_LEGACY_SUPPORT + hash_state hash2; +#endif + unsigned char created; +} TLSHash; + +#ifdef TLS_FORWARD_SECRECY +#define mp_init(a) ltc_mp.init(a) +#define mp_init_multi ltc_init_multi +#define mp_clear(a) ltc_mp.deinit(a) +#define mp_clear_multi ltc_deinit_multi +#define mp_count_bits(a) ltc_mp.count_bits(a) +#define mp_read_radix(a, b, c) ltc_mp.read_radix(a, b, c) +#define mp_unsigned_bin_size(a) ltc_mp.unsigned_size(a) +#define mp_to_unsigned_bin(a, b) ltc_mp.unsigned_write(a, b) +#define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c) +#define mp_exptmod(a, b, c, d) ltc_mp.exptmod(a, b, c, d) +#define mp_add(a, b, c) ltc_mp.add(a, b, c) +#define mp_mul(a, b, c) ltc_mp.mul(a, b, c) +#define mp_cmp(a, b) ltc_mp.compare(a, b) +#define mp_cmp_d(a, b) ltc_mp.compare_d(a, b) +#define mp_sqr(a, b) ltc_mp.sqr(a, b) +#define mp_mod(a, b, c) ltc_mp.mpdiv(a, b, NULL, c) +#define mp_sub(a, b, c) ltc_mp.sub(a, b, c) +#define mp_set(a, b) ltc_mp.set_int(a, b) + +typedef struct { + int iana; + void *x; + void *y; + void *p; + void *g; +} DHKey; + +#ifdef WITH_TLS_13 +static DHKey ffdhe2048 = { + 0x0100, + NULL, + NULL, + (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B423861285C97FFFFFFFFFFFFFFFF", + (void *)"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002" +}; + +static DHKey ffdhe3072 = { + 0x0101, + NULL, + NULL, + (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B66C62E37FFFFFFFFFFFFFFFF", + (void *)"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002" +}; + +static DHKey ffdhe4096 = { + 0x0102, + NULL, + NULL, + (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E655F6AFFFFFFFFFFFFFFFF", + (void *)"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002" +}; + +static DHKey ffdhe6144 = { + 0x0103, + NULL, + NULL, + (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD9020BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA63BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3ACDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477A52471F7A9A96910B855322EDB6340D8A00EF092350511E30ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538CD72B03746AE77F5E62292C311562A846505DC82DB854338AE49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B045B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1A41D570D7938DAD4A40E329CD0E40E65FFFFFFFFFFFFFFFF", + (void *)"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002" +}; + +static DHKey ffdhe8192 = { + 0x0104, + NULL, + NULL, + (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD9020BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA63BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3ACDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477A52471F7A9A96910B855322EDB6340D8A00EF092350511E30ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538CD72B03746AE77F5E62292C311562A846505DC82DB854338AE49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B045B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1A41D570D7938DAD4A40E329CCFF46AAA36AD004CF600C8381E425A31D951AE64FDB23FCEC9509D43687FEB69EDD1CC5E0B8CC3BDF64B10EF86B63142A3AB8829555B2F747C932665CB2C0F1CC01BD70229388839D2AF05E454504AC78B7582822846C0BA35C35F5C59160CC046FD8251541FC68C9C86B022BB7099876A460E7451A8A93109703FEE1C217E6C3826E52C51AA691E0E423CFC99E9E31650C1217B624816CDAD9A95F9D5B8019488D9C0A0A1FE3075A577E23183F81D4A3F2FA4571EFC8CE0BA8A4FE8B6855DFE72B0A66EDED2FBABFBE58A30FAFABE1C5D71A87E2F741EF8C1FE86FEA6BBFDE530677F0D97D11D49F7A8443D0822E506A9F4614E011E2A94838FF88CD68C8BB7C5C6424CFFFFFFFFFFFFFFFF", + (void *)"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002" +}; +#endif + +struct ECCCurveParameters { + int size; + int iana; + const char *name; + const char *P; + const char *A; + const char *B; + const char *Gx; + const char *Gy; + const char *order; + ltc_ecc_set_type dp; +}; + +static struct ECCCurveParameters secp192r1 = { + 24, + 19, + "secp192r1", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", // P + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", // A + "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", // B + "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012", // Gx + "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811", // Gy + "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831" // order (n) +}; + + +static struct ECCCurveParameters secp224r1 = { + 28, + 21, + "secp224r1", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001", // P + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE", // A + "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4", // B + "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21", // Gx + "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34", // Gy + "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D" // order (n) +}; + +static struct ECCCurveParameters secp224k1 = { + 28, + 20, + "secp224k1", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D", // P + "00000000000000000000000000000000000000000000000000000000", // A + "00000000000000000000000000000000000000000000000000000005", // B + "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C", // Gx + "7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5", // Gy + "0000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7" // order (n) +}; + +static struct ECCCurveParameters secp256r1 = { + 32, + 23, + "secp256r1", + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", // P + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", // A + "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", // B + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", // Gx + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", // Gy + "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551" // order (n) +}; + +static struct ECCCurveParameters secp256k1 = { + 32, + 22, + "secp256k1", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", // P + "0000000000000000000000000000000000000000000000000000000000000000", // A + "0000000000000000000000000000000000000000000000000000000000000007", // B + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", // Gx + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", // Gy + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141" // order (n) +}; + +static struct ECCCurveParameters secp384r1 = { + 48, + 24, + "secp384r1", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", // P + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", // A + "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", // B + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", // Gx + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", // Gy + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973" // order (n) +}; + +static struct ECCCurveParameters secp521r1 = { + 66, + 25, + "secp521r1", + "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", // P + "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", // A + "0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", // B + "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", // Gx + "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", // Gy + "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409" // order (n) +}; + +#ifdef TLS_CURVE25519 +// dummy +static struct ECCCurveParameters x25519 = { + 32, + 29, + "x25519", + "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED", // P + "0000000000000000000000000000000000000000000000000000000000076D06", // A + "0000000000000000000000000000000000000000000000000000000000000000", // B + "0000000000000000000000000000000000000000000000000000000000000009", // Gx + "20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9", // Gy + "1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED" // order (n) +}; +#endif + +static struct ECCCurveParameters * const default_curve = &secp256r1; + +void init_curve(struct ECCCurveParameters *curve) { + curve->dp.size = curve->size; + curve->dp.name = (char *)curve->name; + curve->dp.B = (char *)curve->B; + curve->dp.prime = (char *)curve->P; + curve->dp.Gx = (char *)curve->Gx; + curve->dp.Gy = (char *)curve->Gy; + curve->dp.order = (char *)curve->order; +} + +void init_curves() { + init_curve(&secp192r1); + init_curve(&secp224r1); + init_curve(&secp224k1); + init_curve(&secp256r1); + init_curve(&secp256k1); + init_curve(&secp384r1); + init_curve(&secp521r1); +} +#endif + +struct TLSContext { + unsigned char remote_random[TLS_CLIENT_RANDOM_SIZE]; + unsigned char local_random[TLS_SERVER_RANDOM_SIZE]; + unsigned char session[TLS_MAX_SESSION_ID]; + unsigned char session_size; + unsigned short cipher; + unsigned short version; + unsigned char is_server; + struct TLSCertificate **certificates; + struct TLSCertificate *private_key; +#ifdef TLS_ECDSA_SUPPORTED + struct TLSCertificate *ec_private_key; +#endif +#ifdef TLS_FORWARD_SECRECY + DHKey *dhe; + ecc_key *ecc_dhe; + char *default_dhe_p; + char *default_dhe_g; + const struct ECCCurveParameters *curve; +#endif + struct TLSCertificate **client_certificates; + unsigned int certificates_count; + unsigned int client_certificates_count; + unsigned char *master_key; + unsigned int master_key_len; + unsigned char *premaster_key; + unsigned int premaster_key_len; + unsigned char cipher_spec_set; + TLSCipher crypto; + TLSHash *handshake_hash; + + unsigned char *message_buffer; + unsigned int message_buffer_len; + uint64_t remote_sequence_number; + uint64_t local_sequence_number; + + unsigned char connection_status; + unsigned char critical_error; + unsigned char error_code; + + unsigned char *tls_buffer; + unsigned int tls_buffer_len; + + unsigned char *application_buffer; + unsigned int application_buffer_len; + unsigned char is_child; + unsigned char exportable; + unsigned char *exportable_keys; + unsigned char exportable_size; + char *sni; + unsigned char request_client_certificate; + unsigned char dtls; + unsigned short dtls_epoch_local; + unsigned short dtls_epoch_remote; + unsigned char *dtls_cookie; + unsigned char dtls_cookie_len; + unsigned char dtls_seq; + unsigned char *cached_handshake; + unsigned int cached_handshake_len; + unsigned char client_verified; + // handshake messages flags + unsigned char hs_messages[11]; + void *user_data; + struct TLSCertificate **root_certificates; + unsigned int root_count; +#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION + unsigned char *verify_data; + unsigned char verify_len; +#endif +#ifdef WITH_TLS_13 + unsigned char *finished_key; + unsigned char *remote_finished_key; + unsigned char *server_finished_hash; +#endif +#ifdef TLS_CURVE25519 + unsigned char *client_secret; +#endif + char **alpn; + unsigned char alpn_count; + char *negotiated_alpn; + unsigned int sleep_until; + unsigned short tls13_version; +#ifdef TLS_12_FALSE_START + unsigned char false_start; +#endif +}; + +struct TLSPacket { + unsigned char *buf; + unsigned int len; + unsigned int size; + unsigned char broken; + struct TLSContext *context; +}; + +#ifdef SSL_COMPATIBLE_INTERFACE + +typedef int (*SOCKET_RECV_CALLBACK)(int socket, void *buffer, size_t length, int flags); +typedef int (*SOCKET_SEND_CALLBACK)(int socket, const void *buffer, size_t length, int flags); + +#ifndef _WIN32 +#include +#endif +#endif + +static const unsigned int version_id[] = {1, 1, 1, 0}; +static const unsigned int pk_id[] = {1, 1, 7, 0}; +static const unsigned int serial_id[] = {1, 1, 2, 1, 0}; +static const unsigned int issurer_id[] = {1, 1, 4, 0}; +static const unsigned int owner_id[] = {1, 1, 6, 0}; +static const unsigned int validity_id[] = {1, 1, 5, 0}; +static const unsigned int algorithm_id[] = {1, 1, 3, 0}; +static const unsigned int sign_id[] = {1, 3, 2, 1, 0}; +static const unsigned int priv_id[] = {1, 4, 0}; +static const unsigned int priv_der_id[] = {1, 3, 1, 0}; +static const unsigned int ecc_priv_id[] = {1, 2, 0}; + +static const unsigned char country_oid[] = {0x55, 0x04, 0x06, 0x00}; +static const unsigned char state_oid[] = {0x55, 0x04, 0x08, 0x00}; +static const unsigned char location_oid[] = {0x55, 0x04, 0x07, 0x00}; +static const unsigned char entity_oid[] = {0x55, 0x04, 0x0A, 0x00}; +static const unsigned char subject_oid[] = {0x55, 0x04, 0x03, 0x00}; +static const unsigned char san_oid[] = {0x55, 0x1D, 0x11, 0x00}; +static const unsigned char ocsp_oid[] = {0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x00}; + +static const unsigned char TLS_RSA_SIGN_RSA_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x00}; +static const unsigned char TLS_RSA_SIGN_MD5_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04, 0x00}; +static const unsigned char TLS_RSA_SIGN_SHA1_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x00}; +static const unsigned char TLS_RSA_SIGN_SHA256_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x00}; +static const unsigned char TLS_RSA_SIGN_SHA384_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C, 0x00}; +static const unsigned char TLS_RSA_SIGN_SHA512_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D, 0x00}; + +// static const unsigned char TLS_ECDSA_SIGN_SHA1_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x01, 0x05, 0x00, 0x00}; +// static const unsigned char TLS_ECDSA_SIGN_SHA224_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01, 0x05, 0x00, 0x00}; +static const unsigned char TLS_ECDSA_SIGN_SHA256_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x05, 0x00, 0x00}; +// static const unsigned char TLS_ECDSA_SIGN_SHA384_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03, 0x05, 0x00, 0x00}; +// static const unsigned char TLS_ECDSA_SIGN_SHA512_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04, 0x05, 0x00, 0x00}; + +static const unsigned char TLS_EC_PUBLIC_KEY_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x00}; + +static const unsigned char TLS_EC_prime192v1_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01, 0x00}; +static const unsigned char TLS_EC_prime192v2_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x02, 0x00}; +static const unsigned char TLS_EC_prime192v3_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x03, 0x00}; +static const unsigned char TLS_EC_prime239v1_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x04, 0x00}; +static const unsigned char TLS_EC_prime239v2_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x05, 0x00}; +static const unsigned char TLS_EC_prime239v3_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x06, 0x00}; +static const unsigned char TLS_EC_prime256v1_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x00}; + +#define TLS_EC_secp256r1_OID TLS_EC_prime256v1_OID +static const unsigned char TLS_EC_secp224r1_OID[] = {0x2B, 0x81, 0x04, 0x00, 0x21, 0x00}; +static const unsigned char TLS_EC_secp384r1_OID[] = {0x2B, 0x81, 0x04, 0x00, 0x22, 0x00}; +static const unsigned char TLS_EC_secp521r1_OID[] = {0x2B, 0x81, 0x04, 0x00, 0x23, 0x00}; + +struct TLSCertificate *asn1_parse(struct TLSContext *context, const unsigned char *buffer, unsigned int size, int client_cert); +int _private_tls_update_hash(struct TLSContext *context, const unsigned char *in, unsigned int len); +struct TLSPacket *tls_build_finished(struct TLSContext *context); +unsigned int _private_tls_hmac_message(unsigned char local, struct TLSContext *context, const unsigned char *buf, int buf_len, const unsigned char *buf2, int buf_len2, unsigned char *out, unsigned int outlen, uint64_t remote_sequence_number); +int tls_random(unsigned char *key, int len); +void tls_destroy_packet(struct TLSPacket *packet); +struct TLSPacket *tls_build_hello(struct TLSContext *context, int tls13_downgrade); +struct TLSPacket *tls_build_certificate(struct TLSContext *context); +struct TLSPacket *tls_build_done(struct TLSContext *context); +struct TLSPacket *tls_build_alert(struct TLSContext *context, char critical, unsigned char code); +struct TLSPacket *tls_build_change_cipher_spec(struct TLSContext *context); +struct TLSPacket *tls_build_verify_request(struct TLSContext *context); +int _private_tls_crypto_create(struct TLSContext *context, int key_length, unsigned char *localkey, unsigned char *localiv, unsigned char *remotekey, unsigned char *remoteiv); +int _private_tls_get_hash(struct TLSContext *context, unsigned char *hout); +int _private_tls_done_hash(struct TLSContext *context, unsigned char *hout); +int _private_tls_get_hash_idx(struct TLSContext *context); +int _private_tls_build_random(struct TLSPacket *packet); +unsigned int _private_tls_mac_length(struct TLSContext *context); +void _private_dtls_handshake_data(struct TLSContext *context, struct TLSPacket *packet, unsigned int dataframe); +#ifdef TLS_FORWARD_SECRECY +void _private_tls_dhe_free(struct TLSContext *context); +void _private_tls_ecc_dhe_free(struct TLSContext *context); +void _private_tls_dh_clear_key(DHKey *key); +#endif + +#ifdef WITH_TLS_13 +struct TLSPacket *tls_build_encrypted_extensions(struct TLSContext *context); +struct TLSPacket *tls_build_certificate_verify(struct TLSContext *context); +#endif + +// dtls base secret +static unsigned char dtls_secret[32]; + +static unsigned char dependecies_loaded = 0; +// not supported +// static unsigned char TLS_DSA_SIGN_SHA1_OID[] = {0x2A, 0x86, 0x52, 0xCE, 0x38, 0x04, 0x03, 0x00}; + +// base64 stuff +static const char cd64[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq"; + +void _private_b64_decodeblock(unsigned char in[4], unsigned char out[3]) { + out[0] = (unsigned char )(in[0] << 2 | in[1] >> 4); + out[1] = (unsigned char )(in[1] << 4 | in[2] >> 2); + out[2] = (unsigned char )(((in[2] << 6) & 0xc0) | in[3]); +} + +int _private_b64_decode(const char *in_buffer, int in_buffer_size, unsigned char *out_buffer) { + unsigned char in[4], out[3], v; + int i, len; + + const char *ptr = in_buffer; + char *out_ptr = (char *)out_buffer; + + while (ptr <= in_buffer + in_buffer_size) { + for (len = 0, i = 0; i < 4 && (ptr <= in_buffer + in_buffer_size); i++) { + v = 0; + while ((ptr <= in_buffer + in_buffer_size) && v == 0) { + v = (unsigned char)ptr[0]; + ptr++; + v = (unsigned char)((v < 43 || v > 122) ? 0 : cd64[v - 43]); + if (v) + v = (unsigned char)((v == '$') ? 0 : v - 61); + } + if (ptr <= in_buffer + in_buffer_size) { + len++; + if (v) + in[i] = (unsigned char)(v - 1); + } else { + in[i] = 0; + } + } + if (len) { + _private_b64_decodeblock(in, out); + for (i = 0; i < len - 1; i++) { + out_ptr[0] = out[i]; + out_ptr++; + } + } + } + return (int)((intptr_t)out_ptr - (intptr_t)out_buffer); +} + +void dtls_reset_cookie_secret() { + tls_random(dtls_secret, sizeof(dtls_secret)); +} + +void tls_init() { + if (dependecies_loaded) + return; + DEBUG_PRINT("Initializing dependencies\n"); + dependecies_loaded = 1; +#ifdef LTM_DESC + ltc_mp = ltm_desc; +#else +#ifdef TFM_DESC + ltc_mp = tfm_desc; +#else +#ifdef GMP_DESC + ltc_mp = gmp_desc; +#endif +#endif +#endif + register_prng(&sprng_desc); + register_hash(&sha256_desc); + register_hash(&sha1_desc); + register_hash(&sha384_desc); + register_hash(&sha512_desc); + register_hash(&md5_desc); + register_cipher(&aes_desc); +#ifdef TLS_FORWARD_SECRECY + init_curves(); +#endif + dtls_reset_cookie_secret(); +} + +#ifdef TLS_FORWARD_SECRECY +int _private_tls_dh_shared_secret(DHKey *private_key, DHKey *public_key, unsigned char *out, unsigned long *outlen) { + void *tmp; + unsigned long x; + int err; + + if ((!private_key) || (!public_key) || (!out) || (!outlen)) + return TLS_GENERIC_ERROR; + + /* compute y^x mod p */ + if ((err = mp_init(&tmp)) != CRYPT_OK) + return err; + + if ((err = mp_exptmod(public_key->y, private_key->x, private_key->p, tmp)) != CRYPT_OK) { + mp_clear(tmp); + return err; + } + + x = (unsigned long)mp_unsigned_bin_size(tmp); + if (*outlen < x) { + err = CRYPT_BUFFER_OVERFLOW; + mp_clear(tmp); + return err; + } + + if ((err = mp_to_unsigned_bin(tmp, out)) != CRYPT_OK) { + mp_clear(tmp); + return err; + } + *outlen = x; + mp_clear(tmp); + return 0; +} + +unsigned char *_private_tls_decrypt_dhe(struct TLSContext *context, const unsigned char *buffer, unsigned int len, unsigned int *size, int clear_key) { + *size = 0; + if ((!len) || (!context) || (!context->dhe)) { + DEBUG_PRINT("No private DHE key set\n"); + return NULL; + } + + unsigned long out_size = len; + void *Yc = NULL; + + if (mp_init(&Yc)) { + DEBUG_PRINT("ERROR CREATING Yc\n"); + return NULL; + } + if (mp_read_unsigned_bin(Yc, (unsigned char *)buffer, len)) { + DEBUG_PRINT("ERROR LOADING DHE Yc\n"); + mp_clear(Yc); + return NULL; + } + + unsigned char *out = (unsigned char *)TLS_MALLOC(len); + DHKey client_key; + memset(&client_key, 0, sizeof(DHKey)); + + client_key.p = context->dhe->p; + client_key.g = context->dhe->g; + client_key.y = Yc; + int err = _private_tls_dh_shared_secret(context->dhe, &client_key, out, &out_size); + // don't delete p and g + client_key.p = NULL; + client_key.g = NULL; + _private_tls_dh_clear_key(&client_key); + // not needing the dhe key anymore + if (clear_key) + _private_tls_dhe_free(context); + if (err) { + DEBUG_PRINT("DHE DECRYPT ERROR %i\n", err); + TLS_FREE(out); + return NULL; + } + DEBUG_PRINT("OUT_SIZE: %lu\n", out_size); + DEBUG_DUMP_HEX_LABEL("DHE", out, out_size); + *size = (unsigned int)out_size; + return out; +} + +unsigned char *_private_tls_decrypt_ecc_dhe(struct TLSContext *context, const unsigned char *buffer, unsigned int len, unsigned int *size, int clear_key) { + *size = 0; + if ((!len) || (!context) || (!context->ecc_dhe)) { + DEBUG_PRINT("No private ECC DHE key set\n"); + return NULL; + } + + const struct ECCCurveParameters *curve; + if (context->curve) + curve = context->curve; + else + curve = default_curve; + + ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&curve->dp; + + ecc_key client_key; + memset(&client_key, 0, sizeof(client_key)); + if (ecc_ansi_x963_import_ex(buffer, len, &client_key, dp)) { + DEBUG_PRINT("Error importing ECC DHE key\n"); + return NULL; + } + unsigned char *out = (unsigned char *)TLS_MALLOC(len); + unsigned long out_size = len; + + int err = ecc_shared_secret(context->ecc_dhe, &client_key, out, &out_size); + ecc_free(&client_key); + if (clear_key) + _private_tls_ecc_dhe_free(context); + if (err) { + DEBUG_PRINT("ECC DHE DECRYPT ERROR %i\n", err); + TLS_FREE(out); + return NULL; + } + DEBUG_PRINT("OUT_SIZE: %lu\n", out_size); + DEBUG_DUMP_HEX_LABEL("ECC DHE", out, out_size); + *size = (unsigned int)out_size; + return out; +} +#endif + +unsigned char *_private_tls_decrypt_rsa(struct TLSContext *context, const unsigned char *buffer, unsigned int len, unsigned int *size) { + *size = 0; + if ((!len) || (!context) || (!context->private_key) || (!context->private_key->der_bytes) || (!context->private_key->der_len)) { + DEBUG_PRINT("No private key set\n"); + return NULL; + } + tls_init(); + rsa_key key; + int err; + err = rsa_import(context->private_key->der_bytes, context->private_key->der_len, &key); + + if (err) { + DEBUG_PRINT("Error importing RSA key (code: %i)\n", err); + return NULL; + } + unsigned char *out = (unsigned char *)TLS_MALLOC(len); + unsigned long out_size = len; + int res = 0; + + err = rsa_decrypt_key_ex(buffer, len, out, &out_size, NULL, 0, -1, LTC_PKCS_1_V1_5, &res, &key); + rsa_free(&key); + + if ((err) || (out_size != 48) || (ntohs(*(unsigned short *)out) != context->version)) { + // generate a random secret and continue (ROBOT fix) + // silently ignore and generate a random secret + out_size = 48; + tls_random(out, out_size); + *(unsigned short *)out = htons(context->version); + } + *size = (unsigned int)out_size; + return out; +} + +unsigned char *_private_tls_encrypt_rsa(struct TLSContext *context, const unsigned char *buffer, unsigned int len, unsigned int *size) { + *size = 0; + if ((!len) || (!context) || (!context->certificates) || (!context->certificates_count) || (!context->certificates[0]) || + (!context->certificates[0]->der_bytes) || (!context->certificates[0]->der_len)) { + DEBUG_PRINT("No certificate set\n"); + return NULL; + } + tls_init(); + rsa_key key; + int err; + err = rsa_import(context->certificates[0]->der_bytes, context->certificates[0]->der_len, &key); + + if (err) { + DEBUG_PRINT("Error importing RSA certificate (code: %i)\n", err); + return NULL; + } + unsigned long out_size = TLS_MAX_RSA_KEY; + unsigned char *out = (unsigned char *)TLS_MALLOC(out_size); + int hash_idx = find_hash("sha256"); + int prng_idx = find_prng("sprng"); + err = rsa_encrypt_key_ex(buffer, len, out, &out_size, (unsigned char *)"Concept", 7, NULL, prng_idx, hash_idx, LTC_PKCS_1_V1_5, &key); + rsa_free(&key); + if ((err) || (!out_size)) { + TLS_FREE(out); + return NULL; + } + *size = (unsigned int)out_size; + return out; +} + +#ifdef TLS_LEGACY_SUPPORT +int _private_rsa_verify_hash_md5sha1(const unsigned char *sig, unsigned long siglen, unsigned char *hash, unsigned long hashlen, int *stat, rsa_key *key) { + unsigned long modulus_bitlen, modulus_bytelen, x; + int err; + unsigned char *tmpbuf = NULL; + + if ((hash == NULL) || (sig == NULL) || (stat == NULL) || (key == NULL) || (!siglen) || (!hashlen)) + return TLS_GENERIC_ERROR; + + *stat = 0; + + modulus_bitlen = mp_count_bits((key->N)); + + modulus_bytelen = mp_unsigned_bin_size((key->N)); + if (modulus_bytelen != siglen) + return TLS_GENERIC_ERROR; + + tmpbuf = (unsigned char *)TLS_MALLOC(siglen); + if (!tmpbuf) + return TLS_GENERIC_ERROR; + + x = siglen; + if ((err = ltc_mp.rsa_me(sig, siglen, tmpbuf, &x, PK_PUBLIC, key)) != CRYPT_OK) { + TLS_FREE(tmpbuf); + return err; + } + + if (x != siglen) { + TLS_FREE(tmpbuf); + return CRYPT_INVALID_PACKET; + } + unsigned long out_len = siglen; + unsigned char *out = (unsigned char *)TLS_MALLOC(siglen); + if (!out) { + TLS_FREE(tmpbuf); + return TLS_GENERIC_ERROR; + } + + int decoded = 0; + err = pkcs_1_v1_5_decode(tmpbuf, x, LTC_PKCS_1_EMSA, modulus_bitlen, out, &out_len, &decoded); + if (decoded) { + if (out_len == hashlen) { + if (!memcmp(out, hash, hashlen)) + *stat = 1; + } + } + + TLS_FREE(tmpbuf); + TLS_FREE(out); + return err; +} +#endif + +int _private_tls_verify_rsa(struct TLSContext *context, unsigned int hash_type, const unsigned char *buffer, unsigned int len, const unsigned char *message, unsigned int message_len) { + tls_init(); + rsa_key key; + int err; + + if (context->is_server) { + if ((!len) || (!context->client_certificates) || (!context->client_certificates_count) || (!context->client_certificates[0]) || + (!context->client_certificates[0]->der_bytes) || (!context->client_certificates[0]->der_len)) { + DEBUG_PRINT("No client certificate set\n"); + return TLS_GENERIC_ERROR; + } + err = rsa_import(context->client_certificates[0]->der_bytes, context->client_certificates[0]->der_len, &key); + } else { + if ((!len) || (!context->certificates) || (!context->certificates_count) || (!context->certificates[0]) || + (!context->certificates[0]->der_bytes) || (!context->certificates[0]->der_len)) { + DEBUG_PRINT("No server certificate set\n"); + return TLS_GENERIC_ERROR; + } + err = rsa_import(context->certificates[0]->der_bytes, context->certificates[0]->der_len, &key); + } + if (err) { + DEBUG_PRINT("Error importing RSA certificate (code: %i)\n", err); + return TLS_GENERIC_ERROR; + } + int hash_idx = -1; + unsigned char hash[TLS_MAX_HASH_LEN]; + unsigned int hash_len = 0; + hash_state state; + switch (hash_type) { + case md5: + hash_idx = find_hash("md5"); + err = md5_init(&state); + TLS_ERROR(err, break); + err = md5_process(&state, message, message_len); + TLS_ERROR(err, break); + err = md5_done(&state, hash); + TLS_ERROR(err, break); + hash_len = 16; + break; + case sha1: + hash_idx = find_hash("sha1"); + err = sha1_init(&state); + TLS_ERROR(err, break) + err = sha1_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha1_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 20; + break; + case sha256: + hash_idx = find_hash("sha256"); + err = sha256_init(&state); + TLS_ERROR(err, break) + err = sha256_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha256_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 32; + break; + case sha384: + hash_idx = find_hash("sha384"); + err = sha384_init(&state); + TLS_ERROR(err, break) + err = sha384_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha384_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 48; + break; + case sha512: + hash_idx = find_hash("sha512"); + err = sha512_init(&state); + TLS_ERROR(err, break) + err = sha512_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha512_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 64; + break; +#ifdef TLS_LEGACY_SUPPORT + case _md5_sha1: + hash_idx = find_hash("md5"); + err = md5_init(&state); + TLS_ERROR(err, break) + err = md5_process(&state, message, message_len); + TLS_ERROR(err, break) + err = md5_done(&state, hash); + TLS_ERROR(err, break) + hash_idx = find_hash("sha1"); + err = sha1_init(&state); + TLS_ERROR(err, break) + err = sha1_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha1_done(&state, hash + 16); + TLS_ERROR(err, break) + hash_len = 36; + err = sha1_init(&state); + TLS_ERROR(err, break) + err = sha1_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha1_done(&state, hash + 16); + TLS_ERROR(err, break) + hash_len = 36; + break; +#endif + } + if ((hash_idx < 0) || (err)) { + DEBUG_PRINT("Unsupported hash type: %i\n", hash_type); + return TLS_GENERIC_ERROR; + } + int rsa_stat = 0; +#ifdef TLS_LEGACY_SUPPORT + if (hash_type == _md5_sha1) + err = _private_rsa_verify_hash_md5sha1(buffer, len, hash, hash_len, &rsa_stat, &key); + else +#endif +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + err = rsa_verify_hash_ex(buffer, len, hash, hash_len, LTC_PKCS_1_PSS, hash_idx, 0, &rsa_stat, &key); + else +#endif + err = rsa_verify_hash_ex(buffer, len, hash, hash_len, LTC_PKCS_1_V1_5, hash_idx, 0, &rsa_stat, &key); + rsa_free(&key); + if (err) + return 0; + return rsa_stat; +} + +#ifdef TLS_LEGACY_SUPPORT +int _private_rsa_sign_hash_md5sha1(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, rsa_key *key) { + unsigned long modulus_bitlen, modulus_bytelen, x; + int err; + + if ((in == NULL) || (out == NULL) || (outlen == NULL) || (key == NULL)) + return TLS_GENERIC_ERROR; + + modulus_bitlen = mp_count_bits((key->N)); + + modulus_bytelen = mp_unsigned_bin_size((key->N)); + if (modulus_bytelen > *outlen) { + *outlen = modulus_bytelen; + return CRYPT_BUFFER_OVERFLOW; + } + x = modulus_bytelen; + err = pkcs_1_v1_5_encode(in, inlen, LTC_PKCS_1_EMSA, modulus_bitlen, NULL, 0, out, &x); + if (err != CRYPT_OK) + return err; + + return ltc_mp.rsa_me(out, x, out, outlen, PK_PRIVATE, key); +} +#endif + +int _private_tls_sign_rsa(struct TLSContext *context, unsigned int hash_type, const unsigned char *message, unsigned int message_len, unsigned char *out, unsigned long *outlen) { + if ((!outlen) || (!context) || (!out) || (!outlen) || (!context->private_key) || (!context->private_key->der_bytes) || (!context->private_key->der_len)) { + DEBUG_PRINT("No private key set\n"); + return TLS_GENERIC_ERROR; + } + tls_init(); + rsa_key key; + int err; + err = rsa_import(context->private_key->der_bytes, context->private_key->der_len, &key); + + if (err) { + DEBUG_PRINT("Error importing RSA certificate (code: %i)\n", err); + return TLS_GENERIC_ERROR; + } + int hash_idx = -1; + unsigned char hash[TLS_MAX_HASH_LEN]; + unsigned int hash_len = 0; + hash_state state; + switch (hash_type) { + case md5: + hash_idx = find_hash("md5"); + err = md5_init(&state); + TLS_ERROR(err, break) + err = md5_process(&state, message, message_len); + TLS_ERROR(err, break) + err = md5_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 16; + break; + case sha1: + hash_idx = find_hash("sha1"); + err = sha1_init(&state); + TLS_ERROR(err, break) + err = sha1_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha1_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 20; + break; + case sha256: + hash_idx = find_hash("sha256"); + err = sha256_init(&state); + TLS_ERROR(err, break) + err = sha256_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha256_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 32; + break; + case sha384: + hash_idx = find_hash("sha384"); + err = sha384_init(&state); + TLS_ERROR(err, break) + err = sha384_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha384_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 48; + break; + case sha512: + hash_idx = find_hash("sha512"); + err = sha512_init(&state); + TLS_ERROR(err, break) + err = sha512_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha512_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 64; + break; + case _md5_sha1: + hash_idx = find_hash("md5"); + err = md5_init(&state); + TLS_ERROR(err, break) + err = md5_process(&state, message, message_len); + TLS_ERROR(err, break) + err = md5_done(&state, hash); + TLS_ERROR(err, break) + hash_idx = find_hash("sha1"); + err = sha1_init(&state); + TLS_ERROR(err, break) + err = sha1_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha1_done(&state, hash + 16); + TLS_ERROR(err, break) + hash_len = 36; + err = sha1_init(&state); + TLS_ERROR(err, break) + err = sha1_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha1_done(&state, hash + 16); + TLS_ERROR(err, break) + hash_len = 36; + break; + } +#ifdef TLS_LEGACY_SUPPORT + if (hash_type == _md5_sha1) { + if (err) { + DEBUG_PRINT("Unsupported hash type: %i\n", hash_type); + return TLS_GENERIC_ERROR; + } + err = _private_rsa_sign_hash_md5sha1(hash, hash_len, out, outlen, &key); + } else +#endif + { + if ((hash_idx < 0) || (err)) { + DEBUG_PRINT("Unsupported hash type: %i\n", hash_type); + return TLS_GENERIC_ERROR; + } +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + err = rsa_sign_hash_ex(hash, hash_len, out, outlen, LTC_PKCS_1_PSS, NULL, find_prng("sprng"), hash_idx, hash_type == sha256 ? 32 : 48, &key); + else +#endif + err = rsa_sign_hash_ex(hash, hash_len, out, outlen, LTC_PKCS_1_V1_5, NULL, find_prng("sprng"), hash_idx, 0, &key); + } + rsa_free(&key); + if (err) + return 0; + + return 1; +} + +#ifdef TLS_ECDSA_SUPPORTED +static int _private_tls_is_point(ecc_key *key) { + void *prime, *b, *t1, *t2; + int err; + + if ((err = mp_init_multi(&prime, &b, &t1, &t2, NULL)) != CRYPT_OK) { + return err; + } + + /* load prime and b */ + if ((err = mp_read_radix(prime, TLS_TOMCRYPT_PRIVATE_DP(key)->prime, 16)) != CRYPT_OK) { + goto error; + } + if ((err = mp_read_radix(b, TLS_TOMCRYPT_PRIVATE_DP(key)->B, 16)) != CRYPT_OK) { + goto error; + } + + /* compute y^2 */ + if ((err = mp_sqr(key->pubkey.y, t1)) != CRYPT_OK) { + goto error; + } + + /* compute x^3 */ + if ((err = mp_sqr(key->pubkey.x, t2)) != CRYPT_OK) { + goto error; + } + if ((err = mp_mod(t2, prime, t2)) != CRYPT_OK) { + goto error; + } + if ((err = mp_mul(key->pubkey.x, t2, t2)) != CRYPT_OK) { + goto error; + } + + /* compute y^2 - x^3 */ + if ((err = mp_sub(t1, t2, t1)) != CRYPT_OK) { + goto error; + } + + /* compute y^2 - x^3 + 3x */ + if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { + goto error; + } + if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { + goto error; + } + if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { + goto error; + } + if ((err = mp_mod(t1, prime, t1)) != CRYPT_OK) { + goto error; + } + while (mp_cmp_d(t1, 0) == LTC_MP_LT) { + if ((err = mp_add(t1, prime, t1)) != CRYPT_OK) { + goto error; + } + } + while (mp_cmp(t1, prime) != LTC_MP_LT) { + if ((err = mp_sub(t1, prime, t1)) != CRYPT_OK) { + goto error; + } + } + + /* compare to b */ + if (mp_cmp(t1, b) != LTC_MP_EQ) { + err = CRYPT_INVALID_PACKET; + } else { + err = CRYPT_OK; + } + +error: + mp_clear_multi(prime, b, t1, t2, NULL); + return err; +} + +int _private_tls_ecc_import_key(const unsigned char *private_key, int private_len, const unsigned char *public_key, int public_len, ecc_key *key, const ltc_ecc_set_type *dp) { + int err; + + if ((!key) || (!ltc_mp.name)) + return CRYPT_MEM; + + key->type = PK_PRIVATE; + + if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, NULL) != CRYPT_OK) + return CRYPT_MEM; + + if ((public_len) && (!public_key[0])) { + public_key++; + public_len--; + } + if ((err = mp_read_unsigned_bin(key->pubkey.x, (unsigned char *)public_key + 1, (public_len - 1) >> 1)) != CRYPT_OK) { + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; + } + + if ((err = mp_read_unsigned_bin(key->pubkey.y, (unsigned char *)public_key + 1 + ((public_len - 1) >> 1), (public_len - 1) >> 1)) != CRYPT_OK) { + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; + } + + if ((err = mp_read_unsigned_bin(key->k, (unsigned char *)private_key, private_len)) != CRYPT_OK) { + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; + } + + TLS_TOMCRYPT_PRIVATE_SET_INDEX(key, -1); + TLS_TOMCRYPT_PRIVATE_DP(key) = dp; + + /* set z */ + if ((err = mp_set(key->pubkey.z, 1)) != CRYPT_OK) { + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; + } + + /* is it a point on the curve? */ + if ((err = _private_tls_is_point(key)) != CRYPT_OK) { + DEBUG_PRINT("KEY IS NOT ON CURVE\n"); + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; + } + + /* we're good */ + return CRYPT_OK; +} + +int _private_tls_sign_ecdsa(struct TLSContext *context, unsigned int hash_type, const unsigned char *message, unsigned int message_len, unsigned char *out, unsigned long *outlen) { + if ((!outlen) || (!context) || (!out) || (!outlen) || (!context->ec_private_key) || + (!context->ec_private_key->priv) || (!context->ec_private_key->priv_len) || (!context->ec_private_key->pk) || (!context->ec_private_key->pk_len)) { + DEBUG_PRINT("No private ECDSA key set\n"); + return TLS_GENERIC_ERROR; + } + + const struct ECCCurveParameters *curve = NULL; + + switch (context->ec_private_key->ec_algorithm) { + case 19: + curve = &secp192r1; + break; + case 20: + curve = &secp224k1; + break; + case 21: + curve = &secp224r1; + break; + case 22: + curve = &secp256k1; + break; + case 23: + curve = &secp256r1; + break; + case 24: + curve = &secp384r1; + break; + case 25: + curve = &secp521r1; + break; + default: + DEBUG_PRINT("UNSUPPORTED CURVE\n"); + } + + if (!curve) + return TLS_GENERIC_ERROR; + + tls_init(); + ecc_key key; + int err; + + ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&curve->dp; + + // broken ... fix this + err = _private_tls_ecc_import_key(context->ec_private_key->priv, context->ec_private_key->priv_len, context->ec_private_key->pk, context->ec_private_key->pk_len, &key, dp); + if (err) { + DEBUG_PRINT("Error importing ECC certificate (code: %i)\n", (int)err); + return TLS_GENERIC_ERROR; + } + unsigned char hash[TLS_MAX_HASH_LEN]; + unsigned int hash_len = 0; + hash_state state; + switch (hash_type) { + case md5: + err = md5_init(&state); + TLS_ERROR(err, break) + err = md5_process(&state, message, message_len); + TLS_ERROR(err, break) + err = md5_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 16; + break; + case sha1: + err = sha1_init(&state); + TLS_ERROR(err, break) + err = sha1_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha1_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 20; + break; + case sha256: + err = sha256_init(&state); + TLS_ERROR(err, break) + err = sha256_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha256_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 32; + break; + case sha384: + err = sha384_init(&state); + TLS_ERROR(err, break) + err = sha384_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha384_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 48; + break; + case sha512: + err = sha512_init(&state); + TLS_ERROR(err, break) + err = sha512_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha512_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 64; + break; + case _md5_sha1: + err = md5_init(&state); + TLS_ERROR(err, break) + err = md5_process(&state, message, message_len); + TLS_ERROR(err, break) + err = md5_done(&state, hash); + TLS_ERROR(err, break) + err = sha1_init(&state); + TLS_ERROR(err, break) + err = sha1_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha1_done(&state, hash + 16); + TLS_ERROR(err, break) + hash_len = 36; + err = sha1_init(&state); + TLS_ERROR(err, break) + err = sha1_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha1_done(&state, hash + 16); + TLS_ERROR(err, break) + hash_len = 36; + break; + } + + if (err) { + DEBUG_PRINT("Unsupported hash type: %i\n", hash_type); + return TLS_GENERIC_ERROR; + } + // "Let z be the Ln leftmost bits of e, where Ln is the bit length of the group order n." + if (hash_len > (unsigned int)curve->size) + hash_len = (unsigned int)curve->size; + err = ecc_sign_hash(hash, hash_len, out, outlen, NULL, find_prng("sprng"), &key); + DEBUG_DUMP_HEX_LABEL("ECC SIGNATURE", out, *outlen); + ecc_free(&key); + if (err) + return 0; + + return 1; +} + +#if defined(TLS_CLIENT_ECDSA) || defined(WITH_TLS_13) +int _private_tls_ecc_import_pk(const unsigned char *public_key, int public_len, ecc_key *key, const ltc_ecc_set_type *dp) { + int err; + + if ((!key) || (!ltc_mp.name)) + return CRYPT_MEM; + + key->type = PK_PUBLIC; + + if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, NULL) != CRYPT_OK) + return CRYPT_MEM; + + if ((public_len) && (!public_key[0])) { + public_key++; + public_len--; + } + if ((err = mp_read_unsigned_bin(key->pubkey.x, (unsigned char *)public_key + 1, (public_len - 1) >> 1)) != CRYPT_OK) { + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; + } + + if ((err = mp_read_unsigned_bin(key->pubkey.y, (unsigned char *)public_key + 1 + ((public_len - 1) >> 1), (public_len - 1) >> 1)) != CRYPT_OK) { + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; + } + + + TLS_TOMCRYPT_PRIVATE_SET_INDEX(key, -1); + TLS_TOMCRYPT_PRIVATE_DP(key) = dp; + + /* set z */ + if ((err = mp_set(key->pubkey.z, 1)) != CRYPT_OK) { + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; + } + + /* is it a point on the curve? */ + if ((err = _private_tls_is_point(key)) != CRYPT_OK) { + DEBUG_PRINT("KEY IS NOT ON CURVE\n"); + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; + } + + /* we're good */ + return CRYPT_OK; +} + +int _private_tls_verify_ecdsa(struct TLSContext *context, unsigned int hash_type, const unsigned char *buffer, unsigned int len, const unsigned char *message, unsigned int message_len, const struct ECCCurveParameters *curve_hint) { + tls_init(); + ecc_key key; + int err; + + if (!curve_hint) + curve_hint = context->curve; + + if (context->is_server) { + if ((!len) || (!context->client_certificates) || (!context->client_certificates_count) || (!context->client_certificates[0]) || + (!context->client_certificates[0]->pk) || (!context->client_certificates[0]->pk_len) || (!curve_hint)) { + DEBUG_PRINT("No client certificate set\n"); + return TLS_GENERIC_ERROR; + } + err = _private_tls_ecc_import_pk(context->client_certificates[0]->pk, context->client_certificates[0]->pk_len, &key, (ltc_ecc_set_type *)&curve_hint->dp); + } else { + if ((!len) || (!context->certificates) || (!context->certificates_count) || (!context->certificates[0]) || + (!context->certificates[0]->pk) || (!context->certificates[0]->pk_len) || (!curve_hint)) { + DEBUG_PRINT("No server certificate set\n"); + return TLS_GENERIC_ERROR; + } + err = _private_tls_ecc_import_pk(context->certificates[0]->pk, context->certificates[0]->pk_len, &key, (ltc_ecc_set_type *)&curve_hint->dp); + } + if (err) { + DEBUG_PRINT("Error importing ECC certificate (code: %i)", err); + return TLS_GENERIC_ERROR; + } + int hash_idx = -1; + unsigned char hash[TLS_MAX_HASH_LEN]; + unsigned int hash_len = 0; + hash_state state; + switch (hash_type) { + case md5: + hash_idx = find_hash("md5"); + err = md5_init(&state); + if (!err) { + err = md5_process(&state, message, message_len); + if (!err) + err = md5_done(&state, hash); + } + hash_len = 16; + break; + case sha1: + hash_idx = find_hash("sha1"); + err = sha1_init(&state); + if (!err) { + err = sha1_process(&state, message, message_len); + if (!err) + err = sha1_done(&state, hash); + } + hash_len = 20; + break; + case sha256: + hash_idx = find_hash("sha256"); + err = sha256_init(&state); + if (!err) { + err = sha256_process(&state, message, message_len); + if (!err) + err = sha256_done(&state, hash); + } + hash_len = 32; + break; + case sha384: + hash_idx = find_hash("sha384"); + err = sha384_init(&state); + if (!err) { + err = sha384_process(&state, message, message_len); + if (!err) + err = sha384_done(&state, hash); + } + hash_len = 48; + break; + case sha512: + hash_idx = find_hash("sha512"); + err = sha512_init(&state); + if (!err) { + err = sha512_process(&state, message, message_len); + if (!err) + err = sha512_done(&state, hash); + } + hash_len = 64; + break; +#ifdef TLS_LEGACY_SUPPORT + case _md5_sha1: + hash_idx = find_hash("md5"); + err = md5_init(&state); + if (!err) { + err = md5_process(&state, message, message_len); + if (!err) + err = md5_done(&state, hash); + } + hash_idx = find_hash("sha1"); + err = sha1_init(&state); + if (!err) { + err = sha1_process(&state, message, message_len); + if (!err) + err = sha1_done(&state, hash + 16); + } + hash_len = 36; + err = sha1_init(&state); + if (!err) { + err = sha1_process(&state, message, message_len); + if (!err) + err = sha1_done(&state, hash + 16); + } + hash_len = 36; + break; +#endif + } + if ((hash_idx < 0) || (err)) { + DEBUG_PRINT("Unsupported hash type: %i\n", hash_type); + return TLS_GENERIC_ERROR; + } + int ecc_stat = 0; + err = ecc_verify_hash(buffer, len, hash, hash_len, &ecc_stat, &key); + ecc_free(&key); + if (err) + return 0; + return ecc_stat; +} +#endif + +#endif + +unsigned int _private_tls_random_int(int limit) { + unsigned int res = 0; + tls_random((unsigned char *)&res, sizeof(int)); + if (limit) + res %= limit; + return res; +} + +void _private_tls_sleep(unsigned int microseconds) { +#ifdef _WIN32 + Sleep(microseconds/1000); +#else + struct timespec ts; + + ts.tv_sec = (unsigned int) (microseconds / 1000000); + ts.tv_nsec = (unsigned int) (microseconds % 1000000) * 1000ul; + + nanosleep(&ts, NULL); +#endif +} + +void _private_random_sleep(struct TLSContext *context, int max_microseconds) { + if (context) + context->sleep_until = (unsigned int)time(NULL) + _private_tls_random_int(max_microseconds/1000000 * TLS_MAX_ERROR_IDLE_S); + else + _private_tls_sleep(_private_tls_random_int(max_microseconds)); +} + +void _private_tls_prf_helper(int hash_idx, unsigned long dlen, unsigned char *output, unsigned int outlen, const unsigned char *secret, const unsigned int secret_len, + const unsigned char *label, unsigned int label_len, unsigned char *seed, unsigned int seed_len, + unsigned char *seed_b, unsigned int seed_b_len) { + unsigned char digest_out0[TLS_MAX_HASH_LEN]; + unsigned char digest_out1[TLS_MAX_HASH_LEN]; + unsigned int i; + hmac_state hmac; + + hmac_init(&hmac, hash_idx, secret, secret_len); + hmac_process(&hmac, label, label_len); + + hmac_process(&hmac, seed, seed_len); + if ((seed_b) && (seed_b_len)) + hmac_process(&hmac, seed_b, seed_b_len); + hmac_done(&hmac, digest_out0, &dlen); + int idx = 0; + while (outlen) { + hmac_init(&hmac, hash_idx, secret, secret_len); + hmac_process(&hmac, digest_out0, dlen); + hmac_process(&hmac, label, label_len); + hmac_process(&hmac, seed, seed_len); + if ((seed_b) && (seed_b_len)) + hmac_process(&hmac, seed_b, seed_b_len); + hmac_done(&hmac, digest_out1, &dlen); + + unsigned int copylen = outlen; + if (copylen > dlen) + copylen = dlen; + + for (i = 0; i < copylen; i++) { + output[idx++] ^= digest_out1[i]; + outlen--; + } + + if (!outlen) + break; + + hmac_init(&hmac, hash_idx, secret, secret_len); + hmac_process(&hmac, digest_out0, dlen); + hmac_done(&hmac, digest_out0, &dlen); + } +} + +#ifdef WITH_TLS_13 +int _private_tls_hkdf_label(const char *label, unsigned char label_len, const unsigned char *data, unsigned char data_len, unsigned char *hkdflabel, unsigned short length, const char *prefix) { + *(unsigned short *)hkdflabel = htons(length); + int prefix_len; + if (prefix) { + prefix_len = (int)strlen(prefix); + memcpy(&hkdflabel[3], prefix, prefix_len); + } else { + memcpy(&hkdflabel[3], "tls13 ", 6); + prefix_len = 6; + } + hkdflabel[2] = (unsigned char)prefix_len + label_len; + memcpy(&hkdflabel[3 + prefix_len], label, label_len); + hkdflabel[3 + prefix_len + label_len] = (unsigned char)data_len; + if (data_len) + memcpy(&hkdflabel[4 + prefix_len + label_len], data, data_len); + return 4 + prefix_len + label_len + data_len; +} + +int _private_tls_hkdf_extract(unsigned int mac_length, unsigned char *output, unsigned int outlen, const unsigned char *salt, unsigned int salt_len, const unsigned char *ikm, unsigned char ikm_len) { + unsigned long dlen = outlen; + static unsigned char dummy_label[1] = { 0 }; + if ((!salt) || (salt_len == 0)) { + salt_len = 1; + salt = dummy_label; + } + int hash_idx; + if (mac_length == TLS_SHA384_MAC_SIZE) { + hash_idx = find_hash("sha384"); + dlen = mac_length; + } else + hash_idx = find_hash("sha256"); + + hmac_state hmac; + hmac_init(&hmac, hash_idx, salt, salt_len); + hmac_process(&hmac, ikm, ikm_len); + hmac_done(&hmac, output, &dlen); + DEBUG_DUMP_HEX_LABEL("EXTRACT", output, dlen); + return dlen; +} + +void _private_tls_hkdf_expand(unsigned int mac_length, unsigned char *output, unsigned int outlen, const unsigned char *secret, unsigned int secret_len, const unsigned char *info, unsigned char info_len) { + unsigned char digest_out[TLS_MAX_HASH_LEN]; + unsigned long dlen = 32; + int hash_idx; + if (mac_length == TLS_SHA384_MAC_SIZE) { + hash_idx = find_hash("sha384"); + dlen = mac_length; + } else + hash_idx = find_hash("sha256"); + unsigned int i; + unsigned int idx = 0; + hmac_state hmac; + unsigned char i2 = 0; + while (outlen) { + hmac_init(&hmac, hash_idx, secret, secret_len); + if (i2) + hmac_process(&hmac, digest_out, dlen); + if ((info) && (info_len)) + hmac_process(&hmac, info, info_len); + i2++; + hmac_process(&hmac, &i2, 1); + hmac_done(&hmac, digest_out, &dlen); + + unsigned int copylen = outlen; + if (copylen > dlen) + copylen = (unsigned int)dlen; + + for (i = 0; i < copylen; i++) { + output[idx++] = digest_out[i]; + outlen--; + } + + if (!outlen) + break; + } +} + +void _private_tls_hkdf_expand_label(unsigned int mac_length, unsigned char *output, unsigned int outlen, const unsigned char *secret, unsigned int secret_len, const char *label, unsigned char label_len, const unsigned char *data, unsigned char data_len) { + unsigned char hkdf_label[512]; + int len = _private_tls_hkdf_label(label, label_len, data, data_len, hkdf_label, outlen, NULL); + DEBUG_DUMP_HEX_LABEL("INFO", hkdf_label, len); + _private_tls_hkdf_expand(mac_length, output, outlen, secret, secret_len, hkdf_label, len); +} +#endif + +void _private_tls_prf(struct TLSContext *context, + unsigned char *output, unsigned int outlen, const unsigned char *secret, const unsigned int secret_len, + const unsigned char *label, unsigned int label_len, unsigned char *seed, unsigned int seed_len, + unsigned char *seed_b, unsigned int seed_b_len) { + if ((!secret) || (!secret_len)) { + DEBUG_PRINT("NULL SECRET\n"); + return; + } + if ((context->version != TLS_V12) && (context->version != DTLS_V12)) { + int md5_hash_idx = find_hash("md5"); + int sha1_hash_idx = find_hash("sha1"); + int half_secret = (secret_len + 1) / 2; + + memset(output, 0, outlen); + _private_tls_prf_helper(md5_hash_idx, 16, output, outlen, secret, half_secret, label, label_len, seed, seed_len, seed_b, seed_b_len); + _private_tls_prf_helper(sha1_hash_idx, 20, output, outlen, secret + (secret_len - half_secret), secret_len - half_secret, label, label_len, seed, seed_len, seed_b, seed_b_len); + } else { + // sha256_hmac + unsigned char digest_out0[TLS_MAX_HASH_LEN]; + unsigned char digest_out1[TLS_MAX_HASH_LEN]; + unsigned long dlen = 32; + int hash_idx; + unsigned int mac_length = _private_tls_mac_length(context); + if (mac_length == TLS_SHA384_MAC_SIZE) { + hash_idx = find_hash("sha384"); + dlen = mac_length; + } else + hash_idx = find_hash("sha256"); + unsigned int i; + hmac_state hmac; + + hmac_init(&hmac, hash_idx, secret, secret_len); + hmac_process(&hmac, label, label_len); + + hmac_process(&hmac, seed, seed_len); + if ((seed_b) && (seed_b_len)) + hmac_process(&hmac, seed_b, seed_b_len); + hmac_done(&hmac, digest_out0, &dlen); + int idx = 0; + while (outlen) { + hmac_init(&hmac, hash_idx, secret, secret_len); + hmac_process(&hmac, digest_out0, dlen); + hmac_process(&hmac, label, label_len); + hmac_process(&hmac, seed, seed_len); + if ((seed_b) && (seed_b_len)) + hmac_process(&hmac, seed_b, seed_b_len); + hmac_done(&hmac, digest_out1, &dlen); + + unsigned int copylen = outlen; + if (copylen > dlen) + copylen = (unsigned int)dlen; + + for (i = 0; i < copylen; i++) { + output[idx++] = digest_out1[i]; + outlen--; + } + + if (!outlen) + break; + + hmac_init(&hmac, hash_idx, secret, secret_len); + hmac_process(&hmac, digest_out0, dlen); + hmac_done(&hmac, digest_out0, &dlen); + } + } +} + +int _private_tls_key_length(struct TLSContext *context) { + switch (context->cipher) { + case TLS_RSA_WITH_AES_128_CBC_SHA: + case TLS_RSA_WITH_AES_128_CBC_SHA256: + case TLS_RSA_WITH_AES_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case TLS_AES_128_GCM_SHA256: + return 16; + case TLS_RSA_WITH_AES_256_CBC_SHA: + case TLS_RSA_WITH_AES_256_CBC_SHA256: + case TLS_RSA_WITH_AES_256_GCM_SHA384: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_AES_256_GCM_SHA384: + case TLS_CHACHA20_POLY1305_SHA256: + return 32; + } + return 0; +} + +int _private_tls_is_aead(struct TLSContext *context) { + switch (context->cipher) { + case TLS_RSA_WITH_AES_128_GCM_SHA256: + case TLS_RSA_WITH_AES_256_GCM_SHA384: + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case TLS_AES_128_GCM_SHA256: + case TLS_AES_256_GCM_SHA384: + return 1; + case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_CHACHA20_POLY1305_SHA256: + return 2; + } + return 0; +} + + + +unsigned int _private_tls_mac_length(struct TLSContext *context) { + switch (context->cipher) { + case TLS_RSA_WITH_AES_128_CBC_SHA: + case TLS_RSA_WITH_AES_256_CBC_SHA: + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + return TLS_SHA1_MAC_SIZE; + case TLS_RSA_WITH_AES_128_CBC_SHA256: + case TLS_RSA_WITH_AES_256_CBC_SHA256: + case TLS_RSA_WITH_AES_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: +#ifdef WITH_TLS_13 + case TLS_AES_128_GCM_SHA256: + case TLS_CHACHA20_POLY1305_SHA256: + case TLS_AES_128_CCM_SHA256: + case TLS_AES_128_CCM_8_SHA256: +#endif + return TLS_SHA256_MAC_SIZE; + case TLS_RSA_WITH_AES_256_GCM_SHA384: + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: +#ifdef WITH_TLS_13 + case TLS_AES_256_GCM_SHA384: +#endif + return TLS_SHA384_MAC_SIZE; + } + return 0; +} + +#ifdef WITH_TLS_13 +int _private_tls13_key(struct TLSContext *context, int handshake) { + tls_init(); + + int key_length = _private_tls_key_length(context); + unsigned int mac_length = _private_tls_mac_length(context); + + if ((!context->premaster_key) || (!context->premaster_key_len)) + return 0; + + if ((!key_length) || (!mac_length)) { + DEBUG_PRINT("KEY EXPANSION FAILED, KEY LENGTH: %i, MAC LENGTH: %i\n", key_length, mac_length); + return 0; + } + + unsigned char *clientkey = NULL; + unsigned char *serverkey = NULL; + unsigned char *clientiv = NULL; + unsigned char *serveriv = NULL; + int is_aead = _private_tls_is_aead(context); + + unsigned char local_keybuffer[TLS_V13_MAX_KEY_SIZE]; + unsigned char local_ivbuffer[TLS_V13_MAX_IV_SIZE]; + unsigned char remote_keybuffer[TLS_V13_MAX_KEY_SIZE]; + unsigned char remote_ivbuffer[TLS_V13_MAX_IV_SIZE]; + + unsigned char prk[TLS_MAX_HASH_SIZE]; + unsigned char hash[TLS_MAX_HASH_SIZE]; + static unsigned char earlysecret[TLS_MAX_HASH_SIZE]; + + const char *server_key = "s ap traffic"; + const char *client_key = "c ap traffic"; + if (handshake) { + server_key = "s hs traffic"; + client_key = "c hs traffic"; + } + + unsigned char salt[TLS_MAX_HASH_SIZE]; + + hash_state md; + if (mac_length == TLS_SHA384_MAC_SIZE) { + sha384_init(&md); + sha384_done(&md, hash); + } else { + sha256_init(&md); + sha256_done(&md, hash); + } + // extract secret "early" + if ((context->master_key) && (context->master_key_len) && (!handshake)) { + DEBUG_DUMP_HEX_LABEL("USING PREVIOUS SECRET", context->master_key, context->master_key_len); + _private_tls_hkdf_expand_label(mac_length, salt, mac_length, context->master_key, context->master_key_len, "derived", 7, hash, mac_length); + DEBUG_DUMP_HEX_LABEL("salt", salt, mac_length); + _private_tls_hkdf_extract(mac_length, prk, mac_length, salt, mac_length, earlysecret, mac_length); + } else { + _private_tls_hkdf_extract(mac_length, prk, mac_length, NULL, 0, earlysecret, mac_length); + // derive secret for handshake "tls13 derived": + DEBUG_DUMP_HEX_LABEL("null hash", hash, mac_length); + _private_tls_hkdf_expand_label(mac_length, salt, mac_length, prk, mac_length, "derived", 7, hash, mac_length); + // extract secret "handshake": + DEBUG_DUMP_HEX_LABEL("salt", salt, mac_length); + _private_tls_hkdf_extract(mac_length, prk, mac_length, salt, mac_length, context->premaster_key, context->premaster_key_len); + } + + if (!is_aead) { + DEBUG_PRINT("KEY EXPANSION FAILED, NON AEAD CIPHER\n"); + return 0; + } + + unsigned char secret[TLS_MAX_MAC_SIZE]; + unsigned char hs_secret[TLS_MAX_HASH_SIZE]; + + int hash_size; + if (handshake) + hash_size = _private_tls_get_hash(context, hash); + else + hash_size = _private_tls_done_hash(context, hash); + DEBUG_DUMP_HEX_LABEL("messages hash", hash, hash_size); + + if (context->is_server) { + _private_tls_hkdf_expand_label(mac_length, hs_secret, mac_length, prk, mac_length, server_key, 12, context->server_finished_hash ? context->server_finished_hash : hash, hash_size); + DEBUG_DUMP_HEX_LABEL(server_key, hs_secret, mac_length); + serverkey = local_keybuffer; + serveriv = local_ivbuffer; + clientkey = remote_keybuffer; + clientiv = remote_ivbuffer; + } else { + _private_tls_hkdf_expand_label(mac_length, hs_secret, mac_length, prk, mac_length, client_key, 12, context->server_finished_hash ? context->server_finished_hash : hash, hash_size); + DEBUG_DUMP_HEX_LABEL(client_key, hs_secret, mac_length); + serverkey = remote_keybuffer; + serveriv = remote_ivbuffer; + clientkey = local_keybuffer; + clientiv = local_ivbuffer; + } + + int iv_length = TLS_13_AES_GCM_IV_LENGTH; +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (is_aead == 2) + iv_length = TLS_CHACHA20_IV_LENGTH; +#endif + + _private_tls_hkdf_expand_label(mac_length, local_keybuffer, key_length, hs_secret, mac_length, "key", 3, NULL, 0); + _private_tls_hkdf_expand_label(mac_length, local_ivbuffer, iv_length, hs_secret, mac_length, "iv", 2, NULL, 0); + if (context->is_server) + _private_tls_hkdf_expand_label(mac_length, secret, mac_length, prk, mac_length, client_key, 12, context->server_finished_hash ? context->server_finished_hash : hash, hash_size); + else + _private_tls_hkdf_expand_label(mac_length, secret, mac_length, prk, mac_length, server_key, 12, context->server_finished_hash ? context->server_finished_hash : hash, hash_size); + + _private_tls_hkdf_expand_label(mac_length, remote_keybuffer, key_length, secret, mac_length, "key", 3, NULL, 0); + _private_tls_hkdf_expand_label(mac_length, remote_ivbuffer, iv_length, secret, mac_length, "iv", 2, NULL, 0); + + DEBUG_DUMP_HEX_LABEL("CLIENT KEY", clientkey, key_length) + DEBUG_DUMP_HEX_LABEL("CLIENT IV", clientiv, iv_length) + DEBUG_DUMP_HEX_LABEL("SERVER KEY", serverkey, key_length) + DEBUG_DUMP_HEX_LABEL("SERVER IV", serveriv, iv_length) + + TLS_FREE(context->finished_key); + TLS_FREE(context->remote_finished_key); + if (handshake) { + context->finished_key = (unsigned char *)TLS_MALLOC(mac_length); + context->remote_finished_key = (unsigned char *)TLS_MALLOC(mac_length); + + if (context->finished_key) { + _private_tls_hkdf_expand_label(mac_length, context->finished_key, mac_length, hs_secret, mac_length, "finished", 8, NULL, 0); + DEBUG_DUMP_HEX_LABEL("FINISHED", context->finished_key, mac_length) + } + + if (context->remote_finished_key) { + _private_tls_hkdf_expand_label(mac_length, context->remote_finished_key, mac_length, secret, mac_length, "finished", 8, NULL, 0); + DEBUG_DUMP_HEX_LABEL("REMOTE FINISHED", context->remote_finished_key, mac_length) + } + } else { + context->finished_key = NULL; + context->remote_finished_key = NULL; + TLS_FREE(context->server_finished_hash); + context->server_finished_hash = NULL; + } + + if (context->is_server) { +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (is_aead == 2) { + memcpy(context->crypto.ctx_remote_mac.remote_nonce, clientiv, iv_length); + memcpy(context->crypto.ctx_local_mac.local_nonce, serveriv, iv_length); + } else +#endif + if (is_aead) { + memcpy(context->crypto.ctx_remote_mac.remote_iv, clientiv, iv_length); + memcpy(context->crypto.ctx_local_mac.local_iv, serveriv, iv_length); + } + if (_private_tls_crypto_create(context, key_length, serverkey, serveriv, clientkey, clientiv)) + return 0; + } else { +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (is_aead == 2) { + memcpy(context->crypto.ctx_local_mac.local_nonce, clientiv, iv_length); + memcpy(context->crypto.ctx_remote_mac.remote_nonce, serveriv, iv_length); + } else +#endif + if (is_aead) { + memcpy(context->crypto.ctx_local_mac.local_iv, clientiv, iv_length); + memcpy(context->crypto.ctx_remote_mac.remote_iv, serveriv, iv_length); + } + if (_private_tls_crypto_create(context, key_length, clientkey, clientiv, serverkey, serveriv)) + return 0; + } + context->crypto.created = 1 + is_aead; + if (context->exportable) { + TLS_FREE(context->exportable_keys); + context->exportable_keys = (unsigned char *)TLS_MALLOC(key_length * 2); + if (context->exportable_keys) { + if (context->is_server) { + memcpy(context->exportable_keys, serverkey, key_length); + memcpy(context->exportable_keys + key_length, clientkey, key_length); + } else { + memcpy(context->exportable_keys, clientkey, key_length); + memcpy(context->exportable_keys + key_length, serverkey, key_length); + } + context->exportable_size = key_length * 2; + } + } + TLS_FREE(context->master_key); + context->master_key = (unsigned char *)TLS_MALLOC(mac_length); + if (context->master_key) { + memcpy(context->master_key, prk, mac_length); + context->master_key_len = mac_length; + } + context->local_sequence_number = 0; + context->remote_sequence_number = 0; + + // extract client_mac_key(mac_key_length) + // extract server_mac_key(mac_key_length) + // extract client_key(enc_key_length) + // extract server_key(enc_key_length) + // extract client_iv(fixed_iv_lengh) + // extract server_iv(fixed_iv_length) + return 1; +} +#endif + +int _private_tls_expand_key(struct TLSContext *context) { + unsigned char key[TLS_MAX_KEY_EXPANSION_SIZE]; +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + return 0; +#endif + + if ((!context->master_key) || (!context->master_key_len)) + return 0; + + int key_length = _private_tls_key_length(context); + int mac_length = _private_tls_mac_length(context); + + if ((!key_length) || (!mac_length)) { + DEBUG_PRINT("KEY EXPANSION FAILED, KEY LENGTH: %i, MAC LENGTH: %i\n", key_length, mac_length); + return 0; + } + unsigned char *clientkey = NULL; + unsigned char *serverkey = NULL; + unsigned char *clientiv = NULL; + unsigned char *serveriv = NULL; + int iv_length = TLS_AES_IV_LENGTH; + int is_aead = _private_tls_is_aead(context); + if (context->is_server) + _private_tls_prf(context, key, sizeof(key), context->master_key, context->master_key_len, (unsigned char *)"key expansion", 13, context->local_random, TLS_SERVER_RANDOM_SIZE, context->remote_random, TLS_CLIENT_RANDOM_SIZE); + else + _private_tls_prf(context, key, sizeof(key), context->master_key, context->master_key_len, (unsigned char *)"key expansion", 13, context->remote_random, TLS_SERVER_RANDOM_SIZE, context->local_random, TLS_CLIENT_RANDOM_SIZE); + + DEBUG_DUMP_HEX_LABEL("LOCAL RANDOM ", context->local_random, TLS_SERVER_RANDOM_SIZE); + DEBUG_DUMP_HEX_LABEL("REMOTE RANDOM", context->remote_random, TLS_CLIENT_RANDOM_SIZE); + DEBUG_PRINT("\n=========== EXPANSION ===========\n"); + DEBUG_DUMP_HEX(key, TLS_MAX_KEY_EXPANSION_SIZE); + DEBUG_PRINT("\n"); + + int pos = 0; +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (is_aead == 2) { + iv_length = TLS_CHACHA20_IV_LENGTH; + } else +#endif + if (is_aead) { + iv_length = TLS_AES_GCM_IV_LENGTH; + } else { + if (context->is_server) { + memcpy(context->crypto.ctx_remote_mac.remote_mac, &key[pos], mac_length); + pos += mac_length; + memcpy(context->crypto.ctx_local_mac.local_mac, &key[pos], mac_length); + pos += mac_length; + } else { + memcpy(context->crypto.ctx_local_mac.local_mac, &key[pos], mac_length); + pos += mac_length; + memcpy(context->crypto.ctx_remote_mac.remote_mac, &key[pos], mac_length); + pos += mac_length; + } + } + + clientkey = &key[pos]; + pos += key_length; + serverkey = &key[pos]; + pos += key_length; + clientiv = &key[pos]; + pos += iv_length; + serveriv = &key[pos]; + pos += iv_length; + DEBUG_PRINT("EXPANSION %i/%i\n", (int)pos, (int)TLS_MAX_KEY_EXPANSION_SIZE); + DEBUG_DUMP_HEX_LABEL("CLIENT KEY", clientkey, key_length) + DEBUG_DUMP_HEX_LABEL("CLIENT IV", clientiv, iv_length) + DEBUG_DUMP_HEX_LABEL("CLIENT MAC KEY", context->is_server ? context->crypto.ctx_remote_mac.remote_mac : context->crypto.ctx_local_mac.local_mac, mac_length) + DEBUG_DUMP_HEX_LABEL("SERVER KEY", serverkey, key_length) + DEBUG_DUMP_HEX_LABEL("SERVER IV", serveriv, iv_length) + DEBUG_DUMP_HEX_LABEL("SERVER MAC KEY", context->is_server ? context->crypto.ctx_local_mac.local_mac : context->crypto.ctx_remote_mac.remote_mac, mac_length) + + if (context->is_server) { +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (is_aead == 2) { + memcpy(context->crypto.ctx_remote_mac.remote_nonce, clientiv, iv_length); + memcpy(context->crypto.ctx_local_mac.local_nonce, serveriv, iv_length); + } else +#endif + if (is_aead) { + memcpy(context->crypto.ctx_remote_mac.remote_aead_iv, clientiv, iv_length); + memcpy(context->crypto.ctx_local_mac.local_aead_iv, serveriv, iv_length); + } + if (_private_tls_crypto_create(context, key_length, serverkey, serveriv, clientkey, clientiv)) + return 0; + } else { +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (is_aead == 2) { + memcpy(context->crypto.ctx_local_mac.local_nonce, clientiv, iv_length); + memcpy(context->crypto.ctx_remote_mac.remote_nonce, serveriv, iv_length); + } else +#endif + if (is_aead) { + memcpy(context->crypto.ctx_local_mac.local_aead_iv, clientiv, iv_length); + memcpy(context->crypto.ctx_remote_mac.remote_aead_iv, serveriv, iv_length); + } + if (_private_tls_crypto_create(context, key_length, clientkey, clientiv, serverkey, serveriv)) + return 0; + } + + if (context->exportable) { + TLS_FREE(context->exportable_keys); + context->exportable_keys = (unsigned char *)TLS_MALLOC(key_length * 2); + if (context->exportable_keys) { + if (context->is_server) { + memcpy(context->exportable_keys, serverkey, key_length); + memcpy(context->exportable_keys + key_length, clientkey, key_length); + } else { + memcpy(context->exportable_keys, clientkey, key_length); + memcpy(context->exportable_keys + key_length, serverkey, key_length); + } + context->exportable_size = key_length * 2; + } + } + + // extract client_mac_key(mac_key_length) + // extract server_mac_key(mac_key_length) + // extract client_key(enc_key_length) + // extract server_key(enc_key_length) + // extract client_iv(fixed_iv_lengh) + // extract server_iv(fixed_iv_length) + return 1; +} + +int _private_tls_compute_key(struct TLSContext *context, unsigned int key_len) { +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + return 0; +#endif + if ((!context->premaster_key) || (!context->premaster_key_len) || (key_len < 48)) { + DEBUG_PRINT("CANNOT COMPUTE MASTER SECRET\n"); + return 0; + } + unsigned char master_secret_label[] = "master secret"; +#ifdef TLS_CHECK_PREMASTER_KEY + if (!tls_cipher_is_ephemeral(context)) { + unsigned short version = ntohs(*(unsigned short *)context->premaster_key); + // this check is not true for DHE/ECDHE ciphers + if (context->version > version) { + DEBUG_PRINT("Mismatch protocol version 0x(%x)\n", version); + return 0; + } + } +#endif + TLS_FREE(context->master_key); + context->master_key_len = 0; + context->master_key = NULL; + if ((context->version == TLS_V13) || (context->version == TLS_V12) || (context->version == TLS_V11) || (context->version == TLS_V10) || (context->version == DTLS_V13) || (context->version == DTLS_V12) || (context->version == DTLS_V10)) { + context->master_key = (unsigned char *)TLS_MALLOC(key_len); + if (!context->master_key) + return 0; + context->master_key_len = key_len; + if (context->is_server) { + _private_tls_prf(context, + context->master_key, context->master_key_len, + context->premaster_key, context->premaster_key_len, + master_secret_label, 13, + context->remote_random, TLS_CLIENT_RANDOM_SIZE, + context->local_random, TLS_SERVER_RANDOM_SIZE + ); + } else { + _private_tls_prf(context, + context->master_key, context->master_key_len, + context->premaster_key, context->premaster_key_len, + master_secret_label, 13, + context->local_random, TLS_CLIENT_RANDOM_SIZE, + context->remote_random, TLS_SERVER_RANDOM_SIZE + ); + } + TLS_FREE(context->premaster_key); + context->premaster_key = NULL; + context->premaster_key_len = 0; + DEBUG_PRINT("\n=========== Master key ===========\n"); + DEBUG_DUMP_HEX(context->master_key, context->master_key_len); + DEBUG_PRINT("\n"); + _private_tls_expand_key(context); + return 1; + } + return 0; +} + +unsigned char *tls_pem_decode(const unsigned char *data_in, unsigned int input_length, int cert_index, unsigned int *output_len) { + unsigned int i; + *output_len = 0; + int alloc_len = input_length / 4 * 3; + unsigned char *output = (unsigned char *)TLS_MALLOC(alloc_len); + if (!output) + return NULL; + unsigned int start_at = 0; + unsigned int idx = 0; + for (i = 0; i < input_length; i++) { + if ((data_in[i] == '\n') || (data_in[i] == '\r')) + continue; + + if (data_in[i] != '-') { + // read entire line + while ((i < input_length) && (data_in[i] != '\n')) + i++; + continue; + } + + if (data_in[i] == '-') { + unsigned int end_idx = i; + //read until end of line + while ((i < input_length) && (data_in[i] != '\n')) + i++; + if (start_at) { + if (cert_index > 0) { + cert_index--; + start_at = 0; + } else { + idx = _private_b64_decode((const char *)&data_in[start_at], end_idx - start_at, output); + break; + } + } else + start_at = i + 1; + } + } + *output_len = idx; + if (!idx) { + TLS_FREE(output); + return NULL; + } + return output; +} + +int _is_oid(const unsigned char *oid, const unsigned char *compare_to, int compare_to_len) { + int i = 0; + while ((oid[i]) && (i < compare_to_len)) { + if (oid[i] != compare_to[i]) + return 0; + + i++; + } + return 1; +} + +int _is_oid2(const unsigned char *oid, const unsigned char *compare_to, int compare_to_len, int oid_len) { + int i = 0; + if (oid_len < compare_to_len) + compare_to_len = oid_len; + while (i < compare_to_len) { + if (oid[i] != compare_to[i]) + return 0; + + i++; + } + return 1; +} + +struct TLSCertificate *tls_create_certificate() { + struct TLSCertificate *cert = (struct TLSCertificate *)TLS_MALLOC(sizeof(struct TLSCertificate)); + if (cert) + memset(cert, 0, sizeof(struct TLSCertificate)); + return cert; +} + +int tls_certificate_valid_subject_name(const unsigned char *cert_subject, const char *subject) { + // no subjects ... + if (((!cert_subject) || (!cert_subject[0])) && ((!subject) || (!subject[0]))) + return 0; + + if ((!subject) || (!subject[0])) + return bad_certificate; + + if ((!cert_subject) || (!cert_subject[0])) + return bad_certificate; + + // exact match + if (!strcmp((const char *)cert_subject, subject)) + return 0; + + const char *wildcard = strchr((const char *)cert_subject, '*'); + if (wildcard) { + // 6.4.3 (1) The client SHOULD NOT attempt to match a presented identifier in + // which the wildcard character comprises a label other than the left-most label + if (!wildcard[1]) { + // subject is [*] + // or + // subject is [something*] .. invalid + return bad_certificate; + } + wildcard++; + const char *match = strstr(subject, wildcard); + if ((!match) && (wildcard[0] == '.')) { + // check *.domain.com agains domain.com + wildcard++; + if (!strcasecmp(subject, wildcard)) + return 0; + } + if (match) { + uintptr_t offset = (uintptr_t)match - (uintptr_t)subject; + if (offset) { + // check for foo.*.domain.com against *.domain.com (invalid) + if (memchr(subject, '.', offset)) + return bad_certificate; + } + // check if exact match + if (!strcasecmp(match, wildcard)) + return 0; + } + } + + return bad_certificate; +} + +int tls_certificate_valid_subject(struct TLSCertificate *cert, const char *subject) { + int i; + if (!cert) + return certificate_unknown; + int err = tls_certificate_valid_subject_name(cert->subject, subject); + if ((err) && (cert->san)) { + for (i = 0; i < cert->san_length; i++) { + err = tls_certificate_valid_subject_name(cert->san[i], subject); + if (!err) + return err; + } + } + return err; +} + +int tls_certificate_is_valid(struct TLSCertificate *cert) { + if (!cert) + return certificate_unknown; + if (!cert->not_before) + return certificate_unknown; + if (!cert->not_after) + return certificate_unknown; + //20160224182300Z// + char current_time[16]; + time_t t = time(NULL); + struct tm *utct = gmtime(&t); + if (utct) { + current_time[0] = 0; + snprintf(current_time, sizeof(current_time), "%04d%02d%02d%02d%02d%02dZ", 1900 + utct->tm_year, utct->tm_mon + 1, utct->tm_mday, utct->tm_hour, utct->tm_min, utct->tm_sec); + if (strcasecmp((char *)cert->not_before, current_time) > 0) { + DEBUG_PRINT("Certificate is not yer valid, now: %s (validity: %s - %s)\n", current_time, cert->not_before, cert->not_after); + return certificate_expired; + } + if (strcasecmp((char *)cert->not_after, current_time) < 0) { + DEBUG_PRINT("Expired certificate, now: %s (validity: %s - %s)\n", current_time, cert->not_before, cert->not_after); + return certificate_expired; + } + DEBUG_PRINT("Valid certificate, now: %s (validity: %s - %s)\n", current_time, cert->not_before, cert->not_after); + } + return 0; +} + +void tls_certificate_set_copy(unsigned char **member, const unsigned char *val, int len) { + if (!member) + return; + TLS_FREE(*member); + if (len) { + *member = (unsigned char *)TLS_MALLOC(len + 1); + if (*member) { + memcpy(*member, val, len); + (*member)[len] = 0; + } + } else + *member = NULL; +} + +void tls_certificate_set_copy_date(unsigned char **member, const unsigned char *val, int len) { + if (!member) + return; + TLS_FREE(*member); + if (len > 4) { + *member = (unsigned char *)TLS_MALLOC(len + 3); + if (*member) { + if (val[0] == '9') { + (*member)[0]='1'; + (*member)[1]='9'; + } else { + (*member)[0]='2'; + (*member)[1]='0'; + } + memcpy(*member + 2, val, len); + (*member)[len] = 0; + } + } else + *member = NULL; +} + +void tls_certificate_set_key(struct TLSCertificate *cert, const unsigned char *val, int len) { + if ((!val[0]) && (len % 2)) { + val++; + len--; + } + tls_certificate_set_copy(&cert->pk, val, len); + if (cert->pk) + cert->pk_len = len; +} + +void tls_certificate_set_priv(struct TLSCertificate *cert, const unsigned char *val, int len) { + tls_certificate_set_copy(&cert->priv, val, len); + if (cert->priv) + cert->priv_len = len; +} + +void tls_certificate_set_sign_key(struct TLSCertificate *cert, const unsigned char *val, int len) { + if ((!val[0]) && (len % 2)) { + val++; + len--; + } + tls_certificate_set_copy(&cert->sign_key, val, len); + if (cert->sign_key) + cert->sign_len = len; +} + +char *tls_certificate_to_string(struct TLSCertificate *cert, char *buffer, int len) { + unsigned int i; + if (!buffer) + return NULL; + buffer[0] = 0; + if (cert->version) { + int res = snprintf(buffer, len, "X.509v%i certificate\n Issued by: [%s]%s (%s)\n Issued to: [%s]%s (%s, %s)\n Subject: %s\n Validity: %s - %s\n OCSP: %s\n Serial number: ", + (int)cert->version, + cert->issuer_country, cert->issuer_entity, cert->issuer_subject, + cert->country, cert->entity, cert->state, cert->location, + cert->subject, + cert->not_before, cert->not_after, + cert->ocsp + ); + if (res > 0) { + for (i = 0; i < cert->serial_len; i++) + res += snprintf(buffer + res, len - res, "%02x", (int)cert->serial_number[i]); + } + if ((cert->san) && (cert->san_length)) { + res += snprintf(buffer + res, len - res, "\n Alternative subjects: "); + for (i = 0; i < cert->san_length; i++) { + if (i) + res += snprintf(buffer + res, len - res, ", %s", cert->san[i]); + else + res += snprintf(buffer + res, len - res, "%s", cert->san[i]); + } + } + res += snprintf(buffer + res, len - res, "\n Key (%i bits, ", cert->pk_len * 8); + if (res > 0) { + switch (cert->key_algorithm) { + case TLS_RSA_SIGN_RSA: + res += snprintf(buffer + res, len - res, "RSA_SIGN_RSA"); + break; + case TLS_RSA_SIGN_MD5: + res += snprintf(buffer + res, len - res, "RSA_SIGN_MD5"); + break; + case TLS_RSA_SIGN_SHA1: + res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA1"); + break; + case TLS_RSA_SIGN_SHA256: + res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA256"); + break; + case TLS_RSA_SIGN_SHA384: + res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA384"); + break; + case TLS_RSA_SIGN_SHA512: + res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA512"); + break; + case TLS_ECDSA_SIGN_SHA256: + res += snprintf(buffer + res, len - res, "ECDSA_SIGN_SHA512"); + break; + case TLS_EC_PUBLIC_KEY: + res += snprintf(buffer + res, len - res, "EC_PUBLIC_KEY"); + break; + default: + res += snprintf(buffer + res, len - res, "not supported (%i)", (int)cert->key_algorithm); + } + } + if ((res > 0) && (cert->ec_algorithm)) { + switch (cert->ec_algorithm) { + case TLS_EC_prime192v1: + res += snprintf(buffer + res, len - res, " prime192v1"); + break; + case TLS_EC_prime192v2: + res += snprintf(buffer + res, len - res, " prime192v2"); + break; + case TLS_EC_prime192v3: + res += snprintf(buffer + res, len - res, " prime192v3"); + break; + case TLS_EC_prime239v2: + res += snprintf(buffer + res, len - res, " prime239v2"); + break; + case TLS_EC_secp256r1: + res += snprintf(buffer + res, len - res, " EC_secp256r1"); + break; + case TLS_EC_secp224r1: + res += snprintf(buffer + res, len - res, " EC_secp224r1"); + break; + case TLS_EC_secp384r1: + res += snprintf(buffer + res, len - res, " EC_secp384r1"); + break; + case TLS_EC_secp521r1: + res += snprintf(buffer + res, len - res, " EC_secp521r1"); + break; + default: + res += snprintf(buffer + res, len - res, " unknown(%i)", (int)cert->ec_algorithm); + } + } + res += snprintf(buffer + res, len - res, "):\n"); + if (res > 0) { + for (i = 0; i < cert->pk_len; i++) + res += snprintf(buffer + res, len - res, "%02x", (int)cert->pk[i]); + res += snprintf(buffer + res, len - res, "\n Signature (%i bits, ", cert->sign_len * 8); + switch (cert->algorithm) { + case TLS_RSA_SIGN_RSA: + res += snprintf(buffer + res, len - res, "RSA_SIGN_RSA):\n"); + break; + case TLS_RSA_SIGN_MD5: + res += snprintf(buffer + res, len - res, "RSA_SIGN_MD5):\n"); + break; + case TLS_RSA_SIGN_SHA1: + res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA1):\n"); + break; + case TLS_RSA_SIGN_SHA256: + res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA256):\n"); + break; + case TLS_RSA_SIGN_SHA384: + res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA384):\n"); + break; + case TLS_RSA_SIGN_SHA512: + res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA512):\n"); + break; + case TLS_EC_PUBLIC_KEY: + res += snprintf(buffer + res, len - res, "EC_PUBLIC_KEY):\n"); + break; + default: + res += snprintf(buffer + res, len - res, "not supported):\n"); + } + + for (i = 0; i < cert->sign_len; i++) + res += snprintf(buffer + res, len - res, "%02x", (int)cert->sign_key[i]); + } + } else + if ((cert->priv) && (cert->priv_len)) { + int res = snprintf(buffer, len, "X.509 private key\n"); + res += snprintf(buffer + res, len - res, " Private Key: "); + if (res > 0) { + for (i = 0; i < cert->priv_len; i++) + res += snprintf(buffer + res, len - res, "%02x", (int)cert->priv[i]); + } + } else + snprintf(buffer, len, "Empty ASN1 file"); + return buffer; +} + +void tls_certificate_set_exponent(struct TLSCertificate *cert, const unsigned char *val, int len) { + tls_certificate_set_copy(&cert->exponent, val, len); + if (cert->exponent) + cert->exponent_len = len; +} + +void tls_certificate_set_serial(struct TLSCertificate *cert, const unsigned char *val, int len) { + tls_certificate_set_copy(&cert->serial_number, val, len); + if (cert->serial_number) + cert->serial_len = len; +} + +void tls_certificate_set_algorithm(struct TLSContext *context, unsigned int *algorithm, const unsigned char *val, int len) { + if ((len == 7) && (_is_oid(val, TLS_EC_PUBLIC_KEY_OID, 7))) { + *algorithm = TLS_EC_PUBLIC_KEY; + return; + } + if (len == 8) { + if (_is_oid(val, TLS_EC_prime192v1_OID, len)) { + *algorithm = TLS_EC_prime192v1; + return; + } + if (_is_oid(val, TLS_EC_prime192v2_OID, len)) { + *algorithm = TLS_EC_prime192v2; + return; + } + if (_is_oid(val, TLS_EC_prime192v3_OID, len)) { + *algorithm = TLS_EC_prime192v3; + return; + } + if (_is_oid(val, TLS_EC_prime239v1_OID, len)) { + *algorithm = TLS_EC_prime239v1; + return; + } + if (_is_oid(val, TLS_EC_prime239v2_OID, len)) { + *algorithm = TLS_EC_prime239v2; + return; + } + if (_is_oid(val, TLS_EC_prime239v3_OID, len)) { + *algorithm = TLS_EC_prime239v3; + return; + } + if (_is_oid(val, TLS_EC_prime256v1_OID, len)) { + *algorithm = TLS_EC_prime256v1; + return; + } + } + if (len == 5) { + if (_is_oid2(val, TLS_EC_secp224r1_OID, len, sizeof(TLS_EC_secp224r1_OID) - 1)) { + *algorithm = TLS_EC_secp224r1; + return; + } + if (_is_oid2(val, TLS_EC_secp384r1_OID, len, sizeof(TLS_EC_secp384r1_OID) - 1)) { + *algorithm = TLS_EC_secp384r1; + return; + } + if (_is_oid2(val, TLS_EC_secp521r1_OID, len, sizeof(TLS_EC_secp521r1_OID) - 1)) { + *algorithm = TLS_EC_secp521r1; + return; + } + } + if (len != 9) + return; + + if (_is_oid(val, TLS_RSA_SIGN_SHA256_OID, 9)) { + *algorithm = TLS_RSA_SIGN_SHA256; + return; + } + + if (_is_oid(val, TLS_RSA_SIGN_RSA_OID, 9)) { + *algorithm = TLS_RSA_SIGN_RSA; + return; + } + + if (_is_oid(val, TLS_RSA_SIGN_SHA1_OID, 9)) { + *algorithm = TLS_RSA_SIGN_SHA1; + return; + } + + if (_is_oid(val, TLS_RSA_SIGN_SHA512_OID, 9)) { + *algorithm = TLS_RSA_SIGN_SHA512; + return; + } + + if (_is_oid(val, TLS_RSA_SIGN_SHA384_OID, 9)) { + *algorithm = TLS_RSA_SIGN_SHA384; + return; + } + + if (_is_oid(val, TLS_RSA_SIGN_MD5_OID, 9)) { + *algorithm = TLS_RSA_SIGN_MD5; + return; + } + + if (_is_oid(val, TLS_ECDSA_SIGN_SHA256_OID, 9)) { + *algorithm = TLS_ECDSA_SIGN_SHA256; + return; + } + // client should fail on unsupported signature + if (!context->is_server) { + DEBUG_PRINT("UNSUPPORTED SIGNATURE ALGORITHM\n"); + context->critical_error = 1; + } +} + +void tls_destroy_certificate(struct TLSCertificate *cert) { + if (cert) { + int i; + TLS_FREE(cert->exponent); + TLS_FREE(cert->pk); + TLS_FREE(cert->issuer_country); + TLS_FREE(cert->issuer_state); + TLS_FREE(cert->issuer_location); + TLS_FREE(cert->issuer_entity); + TLS_FREE(cert->issuer_subject); + TLS_FREE(cert->country); + TLS_FREE(cert->state); + TLS_FREE(cert->location); + TLS_FREE(cert->subject); + for (i = 0; i < cert->san_length; i++) { + TLS_FREE(cert->san[i]); + } + TLS_FREE(cert->san); + TLS_FREE(cert->ocsp); + TLS_FREE(cert->serial_number); + TLS_FREE(cert->entity); + TLS_FREE(cert->not_before); + TLS_FREE(cert->not_after); + TLS_FREE(cert->sign_key); + TLS_FREE(cert->priv); + TLS_FREE(cert->der_bytes); + TLS_FREE(cert->bytes); + TLS_FREE(cert->fingerprint); + TLS_FREE(cert); + } +} + +struct TLSPacket *tls_create_packet(struct TLSContext *context, unsigned char type, unsigned short version, int payload_size_hint) { + struct TLSPacket *packet = (struct TLSPacket *)TLS_MALLOC(sizeof(struct TLSPacket)); + if (!packet) + return NULL; + packet->broken = 0; + if (payload_size_hint > 0) + packet->size = payload_size_hint + 10; + else + packet->size = TLS_BLOB_INCREMENT; + packet->buf = (unsigned char *)TLS_MALLOC(packet->size); + packet->context = context; + if (!packet->buf) { + TLS_FREE(packet); + return NULL; + } + if ((context) && (context->dtls)) + packet->len = 13; + else + packet->len = 5; + packet->buf[0] = type; +#ifdef WITH_TLS_13 + switch (version) { + case TLS_V13: + // check if context is not null. If null, is a tls_export_context call + if (context) + *(unsigned short *)(packet->buf + 1) = 0x0303; // no need to reorder (same bytes) + else + *(unsigned short *)(packet->buf + 1) = htons(version); + break; + case DTLS_V13: + *(unsigned short *)(packet->buf + 1) = htons(DTLS_V13); + break; + default: + *(unsigned short *)(packet->buf + 1) = htons(version); + } +#else + *(unsigned short *)(packet->buf + 1) = htons(version); +#endif + return packet; +} + +void tls_destroy_packet(struct TLSPacket *packet) { + if (packet) { + if (packet->buf) + TLS_FREE(packet->buf); + TLS_FREE(packet); + } +} + +int _private_tls_crypto_create(struct TLSContext *context, int key_length, unsigned char *localkey, unsigned char *localiv, unsigned char *remotekey, unsigned char *remoteiv) { + if (context->crypto.created) { + if (context->crypto.created == 1) { + cbc_done(&context->crypto.ctx_remote.aes_remote); + cbc_done(&context->crypto.ctx_local.aes_local); + } else { +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (context->crypto.created == 2) { +#endif + unsigned char dummy_buffer[32]; + unsigned long tag_len = 0; + gcm_done(&context->crypto.ctx_remote.aes_gcm_remote, dummy_buffer, &tag_len); + gcm_done(&context->crypto.ctx_local.aes_gcm_local, dummy_buffer, &tag_len); +#ifdef TLS_WITH_CHACHA20_POLY1305 + } +#endif + } + context->crypto.created = 0; + } + tls_init(); + int is_aead = _private_tls_is_aead(context); + int cipherID = find_cipher("aes"); + DEBUG_PRINT("Using cipher ID: %x\n", (int)context->cipher); +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (is_aead == 2) { + unsigned int counter = 1; + + chacha_keysetup(&context->crypto.ctx_local.chacha_local, localkey, key_length * 8); + chacha_ivsetup_96bitnonce(&context->crypto.ctx_local.chacha_local, localiv, (unsigned char *)&counter); + + chacha_keysetup(&context->crypto.ctx_remote.chacha_remote, remotekey, key_length * 8); + chacha_ivsetup_96bitnonce(&context->crypto.ctx_remote.chacha_remote, remoteiv, (unsigned char *)&counter); + + context->crypto.created = 3; + } else +#endif + if (is_aead) { + int res1 = gcm_init(&context->crypto.ctx_local.aes_gcm_local, cipherID, localkey, key_length); + int res2 = gcm_init(&context->crypto.ctx_remote.aes_gcm_remote, cipherID, remotekey, key_length); + + if ((res1) || (res2)) + return TLS_GENERIC_ERROR; + context->crypto.created = 2; + } else { + int res1 = cbc_start(cipherID, localiv, localkey, key_length, 0, &context->crypto.ctx_local.aes_local); + int res2 = cbc_start(cipherID, remoteiv, remotekey, key_length, 0, &context->crypto.ctx_remote.aes_remote); + + if ((res1) || (res2)) + return TLS_GENERIC_ERROR; + context->crypto.created = 1; + } + return 0; +} + +int _private_tls_crypto_encrypt(struct TLSContext *context, unsigned char *buf, unsigned char *ct, unsigned int len) { + if (context->crypto.created == 1) + return cbc_encrypt(buf, ct, len, &context->crypto.ctx_local.aes_local); + + memset(ct, 0, len); + return TLS_GENERIC_ERROR; +} + +int _private_tls_crypto_decrypt(struct TLSContext *context, unsigned char *buf, unsigned char *pt, unsigned int len) { + if (context->crypto.created == 1) + return cbc_decrypt(buf, pt, len, &context->crypto.ctx_remote.aes_remote); + + memset(pt, 0, len); + return TLS_GENERIC_ERROR; +} + +void _private_tls_crypto_done(struct TLSContext *context) { + unsigned char dummy_buffer[32]; + unsigned long tag_len = 0; + switch (context->crypto.created) { + case 1: + cbc_done(&context->crypto.ctx_remote.aes_remote); + cbc_done(&context->crypto.ctx_local.aes_local); + break; + case 2: + gcm_done(&context->crypto.ctx_remote.aes_gcm_remote, dummy_buffer, &tag_len); + gcm_done(&context->crypto.ctx_local.aes_gcm_local, dummy_buffer, &tag_len); + break; + } + context->crypto.created = 0; +} + +void tls_packet_update(struct TLSPacket *packet) { + if ((packet) && (!packet->broken)) { + int footer_size = 0; +#ifdef WITH_TLS_13 + if ((packet->context) && ((packet->context->version == TLS_V13) || (packet->context->version == DTLS_V13)) && (packet->context->cipher_spec_set) && (packet->context->crypto.created)) { + // type + tls_packet_uint8(packet, packet->buf[0]); + // no padding + // tls_packet_uint8(packet, 0); + footer_size = 1; + } +#endif + unsigned int header_size = 5; + if ((packet->context) && (packet->context->dtls)) { + header_size = 13; + *(unsigned short *)(packet->buf + 3) = htons(packet->context->dtls_epoch_local); + uint64_t sequence_number = packet->context->local_sequence_number; + packet->buf[5] = (unsigned char)(sequence_number / 0x10000000000LL); + sequence_number %= 0x10000000000LL; + packet->buf[6] = (unsigned char)(sequence_number / 0x100000000LL); + sequence_number %= 0x100000000LL; + packet->buf[7] = (unsigned char)(sequence_number / 0x1000000); + sequence_number %= 0x1000000; + packet->buf[8] = (unsigned char)(sequence_number / 0x10000); + sequence_number %= 0x10000; + packet->buf[9] = (unsigned char)(sequence_number / 0x100); + sequence_number %= 0x100; + packet->buf[10] = (unsigned char)sequence_number; + + *(unsigned short *)(packet->buf + 11) = htons(packet->len - header_size); + } else + *(unsigned short *)(packet->buf + 3) = htons(packet->len - header_size); + if (packet->context) { + if (packet->buf[0] != TLS_CHANGE_CIPHER) { + if ((packet->buf[0] == TLS_HANDSHAKE) && (packet->len > header_size)) { + unsigned char handshake_type = packet->buf[header_size]; + if ((handshake_type != 0x00) && (handshake_type != 0x03)) + _private_tls_update_hash(packet->context, packet->buf + header_size, packet->len - header_size - footer_size); + } +#ifdef TLS_12_FALSE_START + if (((packet->context->cipher_spec_set) || (packet->context->false_start)) && (packet->context->crypto.created)) { +#else + if ((packet->context->cipher_spec_set) && (packet->context->crypto.created)) { +#endif + int block_size = TLS_AES_BLOCK_SIZE; + int mac_size = 0; + unsigned int length = 0; + unsigned char padding = 0; + unsigned int pt_length = packet->len - header_size; + + if (packet->context->crypto.created == 1) { + mac_size = _private_tls_mac_length(packet->context); +#ifdef TLS_LEGACY_SUPPORT + if (packet->context->version == TLS_V10) + length = packet->len - header_size + mac_size; + else +#endif + length = packet->len - header_size + TLS_AES_IV_LENGTH + mac_size; + padding = block_size - length % block_size; + length += padding; +#ifdef TLS_WITH_CHACHA20_POLY1305 + } else + if (packet->context->crypto.created == 3) { + mac_size = POLY1305_TAGLEN; + length = packet->len - header_size + mac_size; +#endif + } else { + mac_size = TLS_GCM_TAG_LEN; + length = packet->len - header_size + 8 + mac_size; + } + if (packet->context->crypto.created == 1) { + unsigned char *buf = (unsigned char *)TLS_MALLOC(length); + if (buf) { + unsigned char *ct = (unsigned char *)TLS_MALLOC(length + header_size); + if (ct) { + unsigned int buf_pos = 0; + memcpy(ct, packet->buf, header_size - 2); + *(unsigned short *)&ct[header_size - 2] = htons(length); +#ifdef TLS_LEGACY_SUPPORT + if (packet->context->version != TLS_V10) +#endif + { + tls_random(buf, TLS_AES_IV_LENGTH); + buf_pos += TLS_AES_IV_LENGTH; + } + // copy payload + memcpy(buf + buf_pos, packet->buf + header_size, packet->len - header_size); + buf_pos += packet->len - header_size; + if (packet->context->dtls) { + unsigned char temp_buf[5]; + memcpy(temp_buf, packet->buf, 3); + *(unsigned short *)(temp_buf + 3) = *(unsigned short *)&packet->buf[header_size - 2]; + uint64_t dtls_sequence_number = ntohll(*(uint64_t *)&packet->buf[3]); + _private_tls_hmac_message(1, packet->context, temp_buf, 5, packet->buf + header_size, packet->len - header_size, buf + buf_pos, mac_size, dtls_sequence_number); + } else + _private_tls_hmac_message(1, packet->context, packet->buf, packet->len, NULL, 0, buf + buf_pos, mac_size, 0); + buf_pos += mac_size; + + memset(buf + buf_pos, padding - 1, padding); + buf_pos += padding; + + //DEBUG_DUMP_HEX_LABEL("PT BUFFER", buf, length); + _private_tls_crypto_encrypt(packet->context, buf, ct + header_size, length); + TLS_FREE(packet->buf); + packet->buf = ct; + packet->len = length + header_size; + packet->size = packet->len; + } else { + // invalidate packet + memset(packet->buf, 0, packet->len); + } + TLS_FREE(buf); + } else { + // invalidate packet + memset(packet->buf, 0, packet->len); + } + } else +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (packet->context->crypto.created >= 2) { +#else + if (packet->context->crypto.created == 2) { +#endif + // + 1 = type + int ct_size = length + header_size + 12 + TLS_MAX_TAG_LEN + 1; + unsigned char *ct = (unsigned char *)TLS_MALLOC(ct_size); + if (ct) { + memset(ct, 0, ct_size); + // AEAD + // sequence number (8 bytes) + // content type (1 byte) + // version (2 bytes) + // length (2 bytes) + unsigned char aad[13]; + int aad_size = sizeof(aad); + unsigned char *sequence = aad; +#ifdef WITH_TLS_13 + if ((packet->context->version == TLS_V13) || (packet->context->version == DTLS_V13)) { + aad[0] = TLS_APPLICATION_DATA; + aad[1] = packet->buf[1]; + aad[2] = packet->buf[2]; +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (packet->context->crypto.created == 3) + *((unsigned short *)(aad + 3)) = htons(packet->len + POLY1305_TAGLEN - header_size); + else +#endif + *((unsigned short *)(aad + 3)) = htons(packet->len + TLS_GCM_TAG_LEN - header_size); + aad_size = 5; + sequence = aad + 5; + if (packet->context->dtls) + *((uint64_t *)sequence) = *(uint64_t *)&packet->buf[3]; + else + *((uint64_t *)sequence) = htonll(packet->context->local_sequence_number); + } else { +#endif + if (packet->context->dtls) + *((uint64_t *)aad) = *(uint64_t *)&packet->buf[3]; + else + *((uint64_t *)aad) = htonll(packet->context->local_sequence_number); + aad[8] = packet->buf[0]; + aad[9] = packet->buf[1]; + aad[10] = packet->buf[2]; + *((unsigned short *)(aad + 11)) = htons(packet->len - header_size); +#ifdef WITH_TLS_13 + } +#endif + int ct_pos = header_size; +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (packet->context->crypto.created == 3) { + unsigned int counter = 1; + unsigned char poly1305_key[POLY1305_KEYLEN]; + chacha_ivupdate(&packet->context->crypto.ctx_local.chacha_local, packet->context->crypto.ctx_local_mac.local_aead_iv, sequence, (u8 *)&counter); + chacha20_poly1305_key(&packet->context->crypto.ctx_local.chacha_local, poly1305_key); + ct_pos += chacha20_poly1305_aead(&packet->context->crypto.ctx_local.chacha_local, packet->buf + header_size, pt_length, aad, aad_size, poly1305_key, ct + ct_pos); + } else { +#endif + unsigned char iv[TLS_13_AES_GCM_IV_LENGTH]; +#ifdef WITH_TLS_13 + if ((packet->context->version == TLS_V13) || (packet->context->version == DTLS_V13)) { + memcpy(iv, packet->context->crypto.ctx_local_mac.local_iv, TLS_13_AES_GCM_IV_LENGTH); + int i; + int offset = TLS_13_AES_GCM_IV_LENGTH - 8; + for (i = 0; i < 8; i++) + iv[offset + i] = packet->context->crypto.ctx_local_mac.local_iv[offset + i] ^ sequence[i]; + } else { +#endif + memcpy(iv, packet->context->crypto.ctx_local_mac.local_aead_iv, TLS_AES_GCM_IV_LENGTH); + tls_random(iv + TLS_AES_GCM_IV_LENGTH, 8); + memcpy(ct + ct_pos, iv + TLS_AES_GCM_IV_LENGTH, 8); + ct_pos += 8; +#ifdef WITH_TLS_13 + } +#endif + + gcm_reset(&packet->context->crypto.ctx_local.aes_gcm_local); + gcm_add_iv(&packet->context->crypto.ctx_local.aes_gcm_local, iv, 12); + gcm_add_aad(&packet->context->crypto.ctx_local.aes_gcm_local, aad, aad_size); + gcm_process(&packet->context->crypto.ctx_local.aes_gcm_local, packet->buf + header_size, pt_length, ct + ct_pos, GCM_ENCRYPT); + ct_pos += pt_length; + + unsigned long taglen = TLS_GCM_TAG_LEN; + gcm_done(&packet->context->crypto.ctx_local.aes_gcm_local, ct + ct_pos, &taglen); + ct_pos += taglen; +#ifdef TLS_WITH_CHACHA20_POLY1305 + } +#endif +#ifdef WITH_TLS_13 + if ((packet->context->version == TLS_V13) || (packet->context->version == DTLS_V13)) { + ct[0] = TLS_APPLICATION_DATA; + *(unsigned short *)&ct[1] = htons(packet->context->version == TLS_V13 ? TLS_V12 : DTLS_V12); + // is dtls ? + if (header_size != 5) + memcpy(ct, packet->buf + 3, header_size - 2); + } else +#endif + memcpy(ct, packet->buf, header_size - 2); + *(unsigned short *)&ct[header_size - 2] = htons(ct_pos - header_size); + TLS_FREE(packet->buf); + packet->buf = ct; + packet->len = ct_pos; + packet->size = ct_pos; + } else { + // invalidate packet + memset(packet->buf, 0, packet->len); + } + } else { + // invalidate packet (never reached) + memset(packet->buf, 0, packet->len); + } + } + } else + packet->context->dtls_epoch_local++; + packet->context->local_sequence_number++; + } + } +} + +int tls_packet_append(struct TLSPacket *packet, const unsigned char *buf, unsigned int len) { + if ((!packet) || (packet->broken)) + return -1; + + if (!len) + return 0; + + unsigned int new_len = packet->len + len; + + if (new_len > packet->size) { + packet->size = (new_len / TLS_BLOB_INCREMENT + 1) * TLS_BLOB_INCREMENT; + packet->buf = (unsigned char *)TLS_REALLOC(packet->buf, packet->size); + if (!packet->buf) { + packet->size = 0; + packet->len = 0; + packet->broken = 1; + return -1; + } + } + memcpy(packet->buf + packet->len, buf, len); + packet->len = new_len; + return new_len; +} + +int tls_packet_uint8(struct TLSPacket *packet, unsigned char i) { + return tls_packet_append(packet, &i, 1); +} + +int tls_packet_uint16(struct TLSPacket *packet, unsigned short i) { + unsigned short ni = htons(i); + return tls_packet_append(packet, (unsigned char *)&ni, 2); +} + +int tls_packet_uint32(struct TLSPacket *packet, unsigned int i) { + unsigned int ni = htonl(i); + return tls_packet_append(packet, (unsigned char *)&ni, 4); +} + +int tls_packet_uint24(struct TLSPacket *packet, unsigned int i) { + unsigned char buf[3]; + buf[0] = i / 0x10000; + i %= 0x10000; + buf[1] = i / 0x100; + i %= 0x100; + buf[2] = i; + + return tls_packet_append(packet, buf, 3); +} + +int tls_random(unsigned char *key, int len) { +#ifdef TLS_USE_RANDOM_SOURCE + TLS_USE_RANDOM_SOURCE(key, len); +#else +#ifdef __APPLE__ + for (int i = 0; i < len; i++) { + unsigned int v = arc4random() % 0x100; + key[i] = (char)v; + } + return 1; +#else +#ifdef _WIN32 + HCRYPTPROV hProvider = 0; + if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { + if (CryptGenRandom(hProvider, len, (BYTE *)key)) { + CryptReleaseContext(hProvider, 0); + return 1; + } + CryptReleaseContext(hProvider, 0); + } +#else + FILE *fp = fopen("/dev/urandom", "r"); + if (fp) { + int key_len = fread(key, 1, len, fp); + fclose(fp); + if (key_len == len) + return 1; + } +#endif +#endif +#endif + return 0; +} + +TLSHash *_private_tls_ensure_hash(struct TLSContext *context) { + TLSHash *hash = context->handshake_hash; + if (!hash) { + hash = (TLSHash *)TLS_MALLOC(sizeof(TLSHash)); + if (hash) + memset(hash, 0, sizeof(TLSHash)); + context->handshake_hash = hash; + } + return hash; +} + +void _private_tls_destroy_hash(struct TLSContext *context) { + if (context) { + TLS_FREE(context->handshake_hash); + context->handshake_hash = NULL; + } +} + +void _private_tls_create_hash(struct TLSContext *context) { + if (!context) + return; + + TLSHash *hash = _private_tls_ensure_hash(context); + if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { + int hash_size = _private_tls_mac_length(context); + if (hash->created) { + unsigned char temp[TLS_MAX_SHA_SIZE]; + sha384_done(&hash->hash32, temp); + sha256_done(&hash->hash48, temp); + } + sha384_init(&hash->hash48); + sha256_init(&hash->hash32); + hash->created = 1; + } else { +#ifdef TLS_LEGACY_SUPPORT + // TLS_V11 + if (hash->created) { + unsigned char temp[TLS_V11_HASH_SIZE]; + md5_done(&hash->hash32, temp); + sha1_done(&hash->hash2, temp); + } + md5_init(&hash->hash32); + sha1_init(&hash->hash2); + hash->created = 1; +#endif + } +} + +int _private_tls_update_hash(struct TLSContext *context, const unsigned char *in, unsigned int len) { + if (!context) + return 0; + TLSHash *hash = _private_tls_ensure_hash(context); + if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { + if (!hash->created) { + _private_tls_create_hash(context); +#ifdef TLS_LEGACY_SUPPORT + // cache first hello in case of protocol downgrade + if ((!context->is_server) && (!context->cached_handshake) && (!context->request_client_certificate) && (len)) { + context->cached_handshake = (unsigned char *)TLS_MALLOC(len); + if (context->cached_handshake) { + memcpy(context->cached_handshake, in, len); + context->cached_handshake_len = len; + } + } +#endif + } + int hash_size = _private_tls_mac_length(context); + sha256_process(&hash->hash32, in, len); + sha384_process(&hash->hash48, in, len); + if (!hash_size) + hash_size = TLS_SHA256_MAC_SIZE; + } else { +#ifdef TLS_LEGACY_SUPPORT + if (!hash->created) + _private_tls_create_hash(context); + md5_process(&hash->hash32, in, len); + sha1_process(&hash->hash2, in, len); +#endif + } + if ((context->request_client_certificate) && (len)) { + // cache all messages for verification + int new_len = context->cached_handshake_len + len; + context->cached_handshake = (unsigned char *)TLS_REALLOC(context->cached_handshake, new_len); + if (context->cached_handshake) { + memcpy(context->cached_handshake + context->cached_handshake_len, in, len); + context->cached_handshake_len = new_len; + } else + context->cached_handshake_len = 0; + } + return 0; +} + +#ifdef TLS_LEGACY_SUPPORT +int _private_tls_change_hash_type(struct TLSContext *context) { + if (!context) + return 0; + TLSHash *hash = _private_tls_ensure_hash(context); + if ((hash) && (hash->created) && (context->cached_handshake) && (context->cached_handshake_len)) { + _private_tls_destroy_hash(context); + int res = _private_tls_update_hash(context, context->cached_handshake, context->cached_handshake_len); + TLS_FREE(context->cached_handshake); + context->cached_handshake = NULL; + context->cached_handshake_len = 0; + return res; + } + return 0; +} +#endif + +int _private_tls_done_hash(struct TLSContext *context, unsigned char *hout) { + if (!context) + return 0; + + TLSHash *hash = _private_tls_ensure_hash(context); + if (!hash->created) + return 0; + + int hash_size = 0; + if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { + unsigned char temp[TLS_MAX_SHA_SIZE]; + if (!hout) + hout = temp; + //TLS_HASH_DONE(&hash->hash, hout); + hash_size = _private_tls_mac_length(context); + if (hash_size == TLS_SHA384_MAC_SIZE) { + sha256_done(&hash->hash32, temp); + sha384_done(&hash->hash48, hout); + } else { + sha256_done(&hash->hash32, hout); + sha384_done(&hash->hash48, temp); + hash_size = TLS_SHA256_MAC_SIZE; + } + } else { +#ifdef TLS_LEGACY_SUPPORT + // TLS_V11 + unsigned char temp[TLS_V11_HASH_SIZE]; + if (!hout) + hout = temp; + md5_done(&hash->hash32, hout); + sha1_done(&hash->hash2, hout + 16); + hash_size = TLS_V11_HASH_SIZE; +#endif + } + hash->created = 0; + if (context->cached_handshake) { + // not needed anymore + TLS_FREE(context->cached_handshake); + context->cached_handshake = NULL; + context->cached_handshake_len = 0; + } + return hash_size; +} + +int _private_tls_get_hash_idx(struct TLSContext *context) { + if (!context) + return -1; + switch (_private_tls_mac_length(context)) { + case TLS_SHA256_MAC_SIZE: + return find_hash("sha256"); + case TLS_SHA384_MAC_SIZE: + return find_hash("sha384"); + case TLS_SHA1_MAC_SIZE: + return find_hash("sha1"); + } + return -1; +} + +int _private_tls_get_hash(struct TLSContext *context, unsigned char *hout) { + if (!context) + return 0; + + TLSHash *hash = _private_tls_ensure_hash(context); + if (!hash->created) + return 0; + + int hash_size = 0; + if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { + hash_size = _private_tls_mac_length(context); + hash_state prec; + if (hash_size == TLS_SHA384_MAC_SIZE) { + memcpy(&prec, &hash->hash48, sizeof(hash_state)); + sha384_done(&hash->hash48, hout); + memcpy(&hash->hash48, &prec, sizeof(hash_state)); + } else { + memcpy(&prec, &hash->hash32, sizeof(hash_state)); + hash_size = TLS_SHA256_MAC_SIZE; + sha256_done(&hash->hash32, hout); + memcpy(&hash->hash32, &prec, sizeof(hash_state)); + } + } else { +#ifdef TLS_LEGACY_SUPPORT + // TLS_V11 + hash_state prec; + + memcpy(&prec, &hash->hash32, sizeof(hash_state)); + md5_done(&hash->hash32, hout); + memcpy(&hash->hash32, &prec, sizeof(hash_state)); + + memcpy(&prec, &hash->hash2, sizeof(hash_state)); + sha1_done(&hash->hash2, hout + 16); + memcpy(&hash->hash2, &prec, sizeof(hash_state)); + + hash_size = TLS_V11_HASH_SIZE; +#endif + } + return hash_size; +} + +int _private_tls_write_packet(struct TLSPacket *packet) { + if (!packet) + return -1; + struct TLSContext *context = packet->context; + if (!context) + return -1; + + if (context->tls_buffer) { + int len = context->tls_buffer_len + packet->len; + context->tls_buffer = (unsigned char *)TLS_REALLOC(context->tls_buffer, len); + if (!context->tls_buffer) { + context->tls_buffer_len = 0; + return -1; + } + memcpy(context->tls_buffer + context->tls_buffer_len, packet->buf, packet->len); + context->tls_buffer_len = len; + int written = packet->len; + tls_destroy_packet(packet); + return written; + } + context->tls_buffer_len = packet->len; + context->tls_buffer = packet->buf; + packet->buf = NULL; + packet->len = 0; + packet->size = 0; + tls_destroy_packet(packet); + return context->tls_buffer_len; +} + +int _private_tls_write_app_data(struct TLSContext *context, const unsigned char *buf, unsigned int buf_len) { + if (!context) + return -1; + if ((!buf) || (!buf_len)) + return 0; + + int len = context->application_buffer_len + buf_len; + context->application_buffer = (unsigned char *)TLS_REALLOC(context->application_buffer, len); + if (!context->application_buffer) { + context->application_buffer_len = 0; + return -1; + } + memcpy(context->application_buffer + context->application_buffer_len, buf, buf_len); + context->application_buffer_len = len; + return buf_len; +} + +const unsigned char *tls_get_write_buffer(struct TLSContext *context, unsigned int *outlen) { + if (!outlen) + return NULL; + if (!context) { + *outlen = 0; + return NULL; + } + // check if any error + if (context->sleep_until) { + if (context->sleep_until < time(NULL)) { + *outlen = 0; + return NULL; + } + context->sleep_until = 0; + } + *outlen = context->tls_buffer_len; + return context->tls_buffer; +} + +const unsigned char *tls_get_message(struct TLSContext *context, unsigned int *outlen, unsigned int offset) { + if (!outlen) + return NULL; + if ((!context) || (!context->tls_buffer)) { + *outlen = 0; + return NULL; + } + + if (offset >= context->tls_buffer_len) { + *outlen = 0; + return NULL; + } + // check if any error + if (context->sleep_until) { + if (context->sleep_until < time(NULL)) { + *outlen = 0; + return NULL; + } + context->sleep_until = 0; + } + unsigned char *tls_buffer = &context->tls_buffer[offset]; + unsigned int tls_buffer_len = context->tls_buffer_len - offset; + unsigned int len = 0; + if (context->dtls) { + if (tls_buffer_len < 13) { + *outlen = 0; + return NULL; + } + + len = ntohs(*(unsigned short *)&tls_buffer[11]) + 13; + } else { + if (tls_buffer_len < 5) { + *outlen = 0; + return NULL; + } + len = ntohs(*(unsigned short *)&tls_buffer[3]) + 5; + } + if (len > tls_buffer_len) { + *outlen = 0; + return NULL; + } + + *outlen = len; + return tls_buffer; +} + +void tls_buffer_clear(struct TLSContext *context) { + if ((context) && (context->tls_buffer)) { + TLS_FREE(context->tls_buffer); + context->tls_buffer = NULL; + context->tls_buffer_len = 0; + } +} + +int tls_established(struct TLSContext *context) { + if (context) { + if (context->critical_error) + return -1; + + if (context->connection_status == 0xFF) + return 1; + +#ifdef TLS_12_FALSE_START + // allow false start + if ((!context->is_server) && (context->version == TLS_V12) && (context->false_start)) + return 1; +#endif + } + return 0; +} + +void tls_read_clear(struct TLSContext *context) { + if ((context) && (context->application_buffer)) { + TLS_FREE(context->application_buffer); + context->application_buffer = NULL; + context->application_buffer_len = 0; + } +} + +int tls_read(struct TLSContext *context, unsigned char *buf, unsigned int size) { + if (!context) + return -1; + if ((context->application_buffer) && (context->application_buffer_len)) { + if (context->application_buffer_len < size) + size = context->application_buffer_len; + + memcpy(buf, context->application_buffer, size); + if (context->application_buffer_len == size) { + TLS_FREE(context->application_buffer); + context->application_buffer = NULL; + context->application_buffer_len = 0; + return size; + } + context->application_buffer_len -= size; + memmove(context->application_buffer, context->application_buffer + size, context->application_buffer_len); + return size; + } + return 0; +} + +struct TLSContext *tls_create_context(unsigned char is_server, unsigned short version) { + struct TLSContext *context = (struct TLSContext *)TLS_MALLOC(sizeof(struct TLSContext)); + if (context) { + memset(context, 0, sizeof(struct TLSContext)); + context->is_server = is_server; + if ((version == DTLS_V13) || (version == DTLS_V12) || (version == DTLS_V10)) + context->dtls = 1; + context->version = version; + } + return context; +} + +#ifdef TLS_FORWARD_SECRECY +const struct ECCCurveParameters *tls_set_curve(struct TLSContext *context, const struct ECCCurveParameters *curve) { + if (!context->is_server) + return NULL; + const struct ECCCurveParameters *old_curve = context->curve; + context->curve = curve; + return old_curve; +} +#endif + +struct TLSContext *tls_accept(struct TLSContext *context) { + if ((!context) || (!context->is_server)) + return NULL; + + struct TLSContext *child = (struct TLSContext *)TLS_MALLOC(sizeof(struct TLSContext)); + if (child) { + memset(child, 0, sizeof(struct TLSContext)); + child->is_server = 1; + child->is_child = 1; + child->dtls = context->dtls; + child->version = context->version; + child->certificates = context->certificates; + child->certificates_count = context->certificates_count; + child->private_key = context->private_key; +#ifdef TLS_ECDSA_SUPPORTED + child->ec_private_key = context->ec_private_key; +#endif + child->exportable = context->exportable; + child->root_certificates = context->root_certificates; + child->root_count = context->root_count; +#ifdef TLS_FORWARD_SECRECY + child->default_dhe_p = context->default_dhe_p; + child->default_dhe_g = context->default_dhe_g; + child->curve = context->curve; +#endif + child->alpn = context->alpn; + child->alpn_count = context->alpn_count; + } + return child; +} + +#ifdef TLS_FORWARD_SECRECY +void _private_tls_dhe_free(struct TLSContext *context) { + if (context->dhe) { + _private_tls_dh_clear_key(context->dhe); + TLS_FREE(context->dhe); + context->dhe = NULL; + } +} + +void _private_tls_dhe_create(struct TLSContext *context) { + _private_tls_dhe_free(context); + context->dhe = (DHKey *)TLS_MALLOC(sizeof(DHKey)); + if (context->dhe) + memset(context->dhe, 0, sizeof(DHKey)); +} + +void _private_tls_ecc_dhe_free(struct TLSContext *context) { + if (context->ecc_dhe) { + ecc_free(context->ecc_dhe); + TLS_FREE(context->ecc_dhe); + context->ecc_dhe = NULL; + } +} + +void _private_tls_ecc_dhe_create(struct TLSContext *context) { + _private_tls_ecc_dhe_free(context); + context->ecc_dhe = (ecc_key *)TLS_MALLOC(sizeof(ecc_key)); + memset(context->ecc_dhe, 0, sizeof(ecc_key)); +} + +int tls_set_default_dhe_pg(struct TLSContext *context, const char *p_hex_str, const char *g_hex_str) { + if ((!context) || (context->is_child) || (!context->is_server) || (!p_hex_str) || (!g_hex_str)) + return 0; + + TLS_FREE(context->default_dhe_p); + TLS_FREE(context->default_dhe_g); + + context->default_dhe_p = NULL; + context->default_dhe_g = NULL; + + size_t p_len = strlen(p_hex_str); + size_t g_len = strlen(g_hex_str); + if ((p_len <= 0) || (g_len <= 0)) + return 0; + context->default_dhe_p = (char *)TLS_MALLOC(p_len + 1); + if (!context->default_dhe_p) + return 0; + context->default_dhe_g = (char *)TLS_MALLOC(g_len + 1); + if (!context->default_dhe_g) + return 0; + + memcpy(context->default_dhe_p, p_hex_str, p_len); + context->default_dhe_p[p_len] = 0; + + memcpy(context->default_dhe_g, g_hex_str, g_len); + context->default_dhe_g[g_len] = 0; + return 1; +} +#endif + +const char *tls_alpn(struct TLSContext *context) { + if (!context) + return NULL; + return context->negotiated_alpn; +} + +int tls_add_alpn(struct TLSContext *context, const char *alpn) { + if ((!context) || (!alpn) || (!alpn[0]) || ((context->is_server) && (context->is_child))) + return TLS_GENERIC_ERROR; + int len = strlen(alpn); + if (tls_alpn_contains(context, alpn, len)) + return 0; + context->alpn = (char **)TLS_REALLOC(context->alpn, (context->alpn_count + 1) * sizeof(char *)); + if (!context->alpn) { + context->alpn_count = 0; + return TLS_NO_MEMORY; + } + char *alpn_ref = (char *)TLS_MALLOC(len+1); + context->alpn[context->alpn_count] = alpn_ref; + if (alpn_ref) { + memcpy(alpn_ref, alpn, len); + alpn_ref[len] = 0; + context->alpn_count++; + } else + return TLS_NO_MEMORY; + return 0; +} + +int tls_alpn_contains(struct TLSContext *context, const char *alpn, unsigned char alpn_size) { + if ((!context) || (!alpn) || (!alpn_size)) + return 0; + + if (context->alpn) { + int i; + for (i = 0; i < context->alpn_count; i++) { + const char *alpn_local = context->alpn[i]; + if (alpn_local) { + int len = strlen(alpn_local); + if (alpn_size == len) { + if (!memcmp(alpn_local, alpn, alpn_size)) + return 1; + } + } + } + } + return 0; +} + +void tls_destroy_context(struct TLSContext *context) { + unsigned int i; + if (!context) + return; + if (!context->is_child) { + if (context->certificates) { + for (i = 0; i < context->certificates_count; i++) + tls_destroy_certificate(context->certificates[i]); + } + if (context->root_certificates) { + for (i = 0; i < context->root_count; i++) + tls_destroy_certificate(context->root_certificates[i]); + TLS_FREE(context->root_certificates); + context->root_certificates = NULL; + } + if (context->private_key) + tls_destroy_certificate(context->private_key); +#ifdef TLS_ECDSA_SUPPORTED + if (context->ec_private_key) + tls_destroy_certificate(context->ec_private_key); +#endif + TLS_FREE(context->certificates); +#ifdef TLS_FORWARD_SECRECY + TLS_FREE(context->default_dhe_p); + TLS_FREE(context->default_dhe_g); +#endif + if (context->alpn) { + for (i = 0; i < context->alpn_count; i++) + TLS_FREE(context->alpn[i]); + TLS_FREE(context->alpn); + } + } + if (context->client_certificates) { + for (i = 0; i < context->client_certificates_count; i++) + tls_destroy_certificate(context->client_certificates[i]); + TLS_FREE(context->client_certificates); + } + context->client_certificates = NULL; + TLS_FREE(context->master_key); + TLS_FREE(context->premaster_key); + if (context->crypto.created) + _private_tls_crypto_done(context); + TLS_FREE(context->message_buffer); + _private_tls_done_hash(context, NULL); + _private_tls_destroy_hash(context); + TLS_FREE(context->tls_buffer); + TLS_FREE(context->application_buffer); + // zero out the keys before free + if ((context->exportable_keys) && (context->exportable_size)) + memset(context->exportable_keys, 0, context->exportable_size); + TLS_FREE(context->exportable_keys); + TLS_FREE(context->sni); + TLS_FREE(context->dtls_cookie); + TLS_FREE(context->cached_handshake); +#ifdef TLS_FORWARD_SECRECY + _private_tls_dhe_free(context); + _private_tls_ecc_dhe_free(context); +#endif +#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION + TLS_FREE(context->verify_data); +#endif + TLS_FREE(context->negotiated_alpn); +#ifdef WITH_TLS_13 + TLS_FREE(context->finished_key); + TLS_FREE(context->remote_finished_key); + TLS_FREE(context->server_finished_hash); +#endif +#ifdef TLS_CURVE25519 + TLS_FREE(context->client_secret); +#endif + TLS_FREE(context); +} + +#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION +void _private_tls_reset_context(struct TLSContext *context) { + unsigned int i; + if (!context) + return; + if (!context->is_child) { + if (context->certificates) { + for (i = 0; i < context->certificates_count; i++) + tls_destroy_certificate(context->certificates[i]); + } + context->certificates = NULL; + if (context->private_key) { + tls_destroy_certificate(context->private_key); + context->private_key = NULL; + } +#ifdef TLS_ECDSA_SUPPORTED + if (context->ec_private_key) { + tls_destroy_certificate(context->ec_private_key); + context->ec_private_key = NULL; + } +#endif + TLS_FREE(context->certificates); + context->certificates = NULL; +#ifdef TLS_FORWARD_SECRECY + TLS_FREE(context->default_dhe_p); + TLS_FREE(context->default_dhe_g); + context->default_dhe_p = NULL; + context->default_dhe_g = NULL; +#endif + } + if (context->client_certificates) { + for (i = 0; i < context->client_certificates_count; i++) + tls_destroy_certificate(context->client_certificates[i]); + TLS_FREE(context->client_certificates); + } + context->client_certificates = NULL; + TLS_FREE(context->master_key); + context->master_key = NULL; + TLS_FREE(context->premaster_key); + context->premaster_key = NULL; + if (context->crypto.created) + _private_tls_crypto_done(context); + _private_tls_done_hash(context, NULL); + _private_tls_destroy_hash(context); + TLS_FREE(context->application_buffer); + context->application_buffer = NULL; + // zero out the keys before free + if ((context->exportable_keys) && (context->exportable_size)) + memset(context->exportable_keys, 0, context->exportable_size); + TLS_FREE(context->exportable_keys); + context->exportable_keys = NULL; + TLS_FREE(context->sni); + context->sni = NULL; + TLS_FREE(context->dtls_cookie); + context->dtls_cookie = NULL; + TLS_FREE(context->cached_handshake); + context->cached_handshake = NULL; + context->connection_status = 0; +#ifdef TLS_FORWARD_SECRECY + _private_tls_dhe_free(context); + _private_tls_ecc_dhe_free(context); +#endif +} +#endif + +int tls_cipher_supported(struct TLSContext *context, unsigned short cipher) { + if (!context) + return 0; + + switch (cipher) { +#ifdef WITH_TLS_13 + case TLS_AES_128_GCM_SHA256: + case TLS_AES_256_GCM_SHA384: + case TLS_CHACHA20_POLY1305_SHA256: + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + return 1; + return 0; +#endif +#ifdef TLS_FORWARD_SECRECY +#ifdef TLS_ECDSA_SUPPORTED + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: +#ifdef TLS_CLIENT_ECDSA + if ((context) && (((context->certificates) && (context->certificates_count) && (context->ec_private_key)) || (!context->is_server))) +#else + if ((context) && (context->certificates) && (context->certificates_count) && (context->ec_private_key)) +#endif + return 1; + return 0; + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: +#ifdef TLS_WITH_CHACHA20_POLY1305 + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: +#endif + if ((context->version == TLS_V12) || (context->version == DTLS_V12)) { +#ifdef TLS_CLIENT_ECDSA + if ((context) && (((context->certificates) && (context->certificates_count) && (context->ec_private_key)) || (!context->is_server))) +#else + if ((context) && (context->certificates) && (context->certificates_count) && (context->ec_private_key)) +#endif + return 1; + } + return 0; +#endif + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: +#endif + case TLS_RSA_WITH_AES_128_CBC_SHA: + case TLS_RSA_WITH_AES_256_CBC_SHA: + return 1; +#ifdef TLS_FORWARD_SECRECY + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: +#ifdef TLS_WITH_CHACHA20_POLY1305 + case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: +#endif +#endif + case TLS_RSA_WITH_AES_128_GCM_SHA256: + case TLS_RSA_WITH_AES_128_CBC_SHA256: + case TLS_RSA_WITH_AES_256_CBC_SHA256: + case TLS_RSA_WITH_AES_256_GCM_SHA384: + if ((context->version == TLS_V12) || (context->version == DTLS_V12)) + return 1; + return 0; + } + return 0; +} + +int tls_cipher_is_fs(struct TLSContext *context, unsigned short cipher) { + if (!context) + return 0; +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + switch (cipher) { + case TLS_AES_128_GCM_SHA256: + case TLS_AES_256_GCM_SHA384: + case TLS_CHACHA20_POLY1305_SHA256: + return 1; + } + return 0; + } +#endif + switch (cipher) { +#ifdef TLS_ECDSA_SUPPORTED + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: +#ifdef TLS_WITH_CHACHA20_POLY1305 + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: +#endif + if ((context) && (context->certificates) && (context->certificates_count) && (context->ec_private_key)) + return 1; + return 0; + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + if ((context->version == TLS_V12) || (context->version == DTLS_V12)) { + if ((context) && (context->certificates) && (context->certificates_count) && (context->ec_private_key)) + return 1; + } + return 0; +#endif + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + return 1; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: +#ifdef TLS_WITH_CHACHA20_POLY1305 + case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: +#endif + if ((context->version == TLS_V12) || (context->version == DTLS_V12)) + return 1; + break; + } + return 0; +} + +#ifdef WITH_KTLS +int _private_tls_prefer_ktls(struct TLSContext *context, unsigned short cipher) { + if ((context->version == TLS_V13) || (context->version == DTLS_V13) || ((context->version != TLS_V12) && (context->version != DTLS_V12))) + return 0; + + switch (cipher) { + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + if ((context->version == TLS_V13) || (context->version == DTLS_V13) || (context->version == TLS_V12) || (context->version == DTLS_V12)) { + if ((context->certificates) && (context->certificates_count) && (context->ec_private_key)) + return 1; + } + break; + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + return 1; + } + return 0; +} +#endif + +int tls_choose_cipher(struct TLSContext *context, const unsigned char *buf, int buf_len, int *scsv_set) { + int i; + if (scsv_set) + *scsv_set = 0; + if (!context) + return 0; + int selected_cipher = TLS_NO_COMMON_CIPHER; +#ifdef TLS_FORWARD_SECRECY +#ifdef WITH_KTLS + for (i = 0; i < buf_len; i+=2) { + unsigned short cipher = ntohs(*(unsigned short *)&buf[i]); + if (_private_tls_prefer_ktls(context, cipher)) { + selected_cipher = cipher; + break; + } + } +#endif + if (selected_cipher == TLS_NO_COMMON_CIPHER) { + for (i = 0; i < buf_len; i+=2) { + unsigned short cipher = ntohs(*(unsigned short *)&buf[i]); + if (tls_cipher_is_fs(context, cipher)) { + selected_cipher = cipher; + break; + } + } + } +#endif + for (i = 0; i < buf_len; i+=2) { + unsigned short cipher = ntohs(*(unsigned short *)&buf[i]); + if (cipher == TLS_FALLBACK_SCSV) { + if (scsv_set) + *scsv_set = 1; + if (selected_cipher != TLS_NO_COMMON_CIPHER) + break; + } +#ifndef TLS_ROBOT_MITIGATION + else + if ((selected_cipher == TLS_NO_COMMON_CIPHER) && (tls_cipher_supported(context, cipher))) + selected_cipher = cipher; +#endif + } + return selected_cipher; +} + +int tls_cipher_is_ephemeral(struct TLSContext *context) { + if (context) { + switch (context->cipher) { + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + return 1; + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + return 2; +#ifdef WITH_TLS_13 + case TLS_AES_128_GCM_SHA256: + case TLS_CHACHA20_POLY1305_SHA256: + case TLS_AES_128_CCM_SHA256: + case TLS_AES_128_CCM_8_SHA256: + case TLS_AES_256_GCM_SHA384: + if (context->dhe) + return 1; + return 2; +#endif + } + } + return 0; +} + +const char *tls_cipher_name(struct TLSContext *context) { + if (context) { + switch (context->cipher) { + case TLS_RSA_WITH_AES_128_CBC_SHA: + return "RSA-AES128CBC-SHA"; + case TLS_RSA_WITH_AES_256_CBC_SHA: + return "RSA-AES256CBC-SHA"; + case TLS_RSA_WITH_AES_128_CBC_SHA256: + return "RSA-AES128CBC-SHA256"; + case TLS_RSA_WITH_AES_256_CBC_SHA256: + return "RSA-AES256CBC-SHA256"; + case TLS_RSA_WITH_AES_128_GCM_SHA256: + return "RSA-AES128GCM-SHA256"; + case TLS_RSA_WITH_AES_256_GCM_SHA384: + return "RSA-AES256GCM-SHA384"; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + return "DHE-RSA-AES128CBC-SHA"; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + return "DHE-RSA-AES256CBC-SHA"; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + return "DHE-RSA-AES128CBC-SHA256"; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + return "DHE-RSA-AES256CBC-SHA256"; + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + return "DHE-RSA-AES128GCM-SHA256"; + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + return "DHE-RSA-AES256GCM-SHA384"; + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + return "ECDHE-RSA-AES128CBC-SHA"; + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + return "ECDHE-RSA-AES256CBC-SHA"; + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + return "ECDHE-RSA-AES128CBC-SHA256"; + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + return "ECDHE-RSA-AES128GCM-SHA256"; + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + return "ECDHE-RSA-AES256GCM-SHA384"; + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + return "ECDHE-ECDSA-AES128CBC-SHA"; + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + return "ECDHE-ECDSA-AES256CBC-SHA"; + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + return "ECDHE-ECDSA-AES128CBC-SHA256"; + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + return "ECDHE-ECDSA-AES256CBC-SHA384"; + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + return "ECDHE-ECDSA-AES128GCM-SHA256"; + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + return "ECDHE-ECDSA-AES256GCM-SHA384"; + case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + return "ECDHE-RSA-CHACHA20-POLY1305-SHA256"; + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + return "ECDHE-ECDSA-CHACHA20-POLY1305-SHA256"; + case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + return "ECDHE-DHE-CHACHA20-POLY1305-SHA256"; + case TLS_AES_128_GCM_SHA256: + return "TLS-AES-128-GCM-SHA256"; + case TLS_AES_256_GCM_SHA384: + return "TLS-AES-256-GCM-SHA384"; + case TLS_CHACHA20_POLY1305_SHA256: + return "TLS-CHACHA20-POLY1305-SHA256"; + case TLS_AES_128_CCM_SHA256: + return "TLS-AES-128-CCM-SHA256"; + case TLS_AES_128_CCM_8_SHA256: + return "TLS-AES-128-CCM-8-SHA256"; + } + } + return "UNKNOWN"; +} + +#ifdef TLS_FORWARD_SECRECY +int _private_tls_dh_export_Y(unsigned char *Ybuf, unsigned long *Ylen, DHKey *key) { + unsigned long len; + + if ((Ybuf == NULL) || (Ylen == NULL) || (key == NULL)) + return TLS_GENERIC_ERROR; + + len = mp_unsigned_bin_size(key->y); + if (len > *Ylen) + return TLS_GENERIC_ERROR; + + *Ylen = len; + return 0; + } + +int _private_tls_dh_export_pqY(unsigned char *pbuf, unsigned long *plen, unsigned char *gbuf, unsigned long *glen, unsigned char *Ybuf, unsigned long *Ylen, DHKey *key) { + unsigned long len; + int err; + + if ((pbuf == NULL) || (plen == NULL) || (gbuf == NULL) || (glen == NULL) || (Ybuf == NULL) || (Ylen == NULL) || (key == NULL)) + return TLS_GENERIC_ERROR; + + len = mp_unsigned_bin_size(key->y); + if (len > *Ylen) + return TLS_GENERIC_ERROR; + + if ((err = mp_to_unsigned_bin(key->y, Ybuf)) != CRYPT_OK) + return err; + + *Ylen = len; + + len = mp_unsigned_bin_size(key->p); + if (len > *plen) + return TLS_GENERIC_ERROR; + + if ((err = mp_to_unsigned_bin(key->p, pbuf)) != CRYPT_OK) + return err; + + *plen = len; + + len = mp_unsigned_bin_size(key->g); + if (len > *glen) + return TLS_GENERIC_ERROR; + + if ((err = mp_to_unsigned_bin(key->g, gbuf)) != CRYPT_OK) + return err; + + *glen = len; + + return 0; +} + +void _private_tls_dh_clear_key(DHKey *key) { + mp_clear_multi(key->g, key->p, key->x, key->y, NULL); + key->g = NULL; + key->p = NULL; + key->x = NULL; + key->y = NULL; +} + +int _private_tls_dh_make_key(int keysize, DHKey *key, const char *pbuf, const char *gbuf, int pbuf_len, int gbuf_len) { + unsigned char *buf; + int err; + if (!key) + return TLS_GENERIC_ERROR; + + static prng_state prng; + int wprng = find_prng("sprng"); + if ((err = prng_is_valid(wprng)) != CRYPT_OK) + return err; + + buf = (unsigned char *)TLS_MALLOC(keysize); + if (!buf) + return TLS_NO_MEMORY; + + if (rng_make_prng(keysize, wprng, &prng, NULL) != CRYPT_OK) { + TLS_FREE(buf); + return TLS_GENERIC_ERROR; + } + + if (prng_descriptor[wprng].read(buf, keysize, &prng) != (unsigned long)keysize) { + TLS_FREE(buf); + return TLS_GENERIC_ERROR; + } + + if ((err = mp_init_multi(&key->g, &key->p, &key->x, &key->y, NULL)) != CRYPT_OK) { + TLS_FREE(buf); + + return TLS_GENERIC_ERROR; + } + + if (gbuf_len <= 0) { + if ((err = mp_read_radix(key->g, gbuf, 16)) != CRYPT_OK) { + TLS_FREE(buf); + _private_tls_dh_clear_key(key); + return TLS_GENERIC_ERROR; + } + } else { + if ((err = mp_read_unsigned_bin(key->g, (unsigned char *)gbuf, gbuf_len)) != CRYPT_OK) { + TLS_FREE(buf); + _private_tls_dh_clear_key(key); + return TLS_GENERIC_ERROR; + } + } + + if (pbuf_len <= 0) { + if ((err = mp_read_radix(key->p, pbuf, 16)) != CRYPT_OK) { + TLS_FREE(buf); + _private_tls_dh_clear_key(key); + return TLS_GENERIC_ERROR; + } + } else { + if ((err = mp_read_unsigned_bin(key->p, (unsigned char *)pbuf, pbuf_len)) != CRYPT_OK) { + TLS_FREE(buf); + _private_tls_dh_clear_key(key); + return TLS_GENERIC_ERROR; + } + } + + if ((err = mp_read_unsigned_bin(key->x, buf, keysize)) != CRYPT_OK) { + TLS_FREE(buf); + _private_tls_dh_clear_key(key); + return TLS_GENERIC_ERROR; + } + + if ((err = mp_exptmod(key->g, key->x, key->p, key->y)) != CRYPT_OK) { + TLS_FREE(buf); + _private_tls_dh_clear_key(key); + return TLS_GENERIC_ERROR; + } + + TLS_FREE(buf); + return 0; +} +#endif + +int tls_is_ecdsa(struct TLSContext *context) { + if (!context) + return 0; + switch (context->cipher) { + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: +#ifdef TLS_WITH_CHACHA20_POLY1305 + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: +#endif + return 1; + } +#ifdef WITH_TLS_13 + if (context->ec_private_key) + return 1; +#endif + return 0; +} + +struct TLSPacket *tls_build_client_key_exchange(struct TLSContext *context) { + if (context->is_server) { + DEBUG_PRINT("CANNOT BUILD CLIENT KEY EXCHANGE MESSAGE FOR SERVERS\n"); + return NULL; + } + + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0); + tls_packet_uint8(packet, 0x10); +#ifdef TLS_FORWARD_SECRECY + int ephemeral = tls_cipher_is_ephemeral(context); + if ((ephemeral) && (context->premaster_key) && (context->premaster_key_len)) { + if (ephemeral == 1) { + unsigned char dh_Ys[0xFFF]; + unsigned char dh_p[0xFFF]; + unsigned char dh_g[0xFFF]; + unsigned long dh_p_len = sizeof(dh_p); + unsigned long dh_g_len = sizeof(dh_g); + unsigned long dh_Ys_len = sizeof(dh_Ys); + + if (_private_tls_dh_export_pqY(dh_p, &dh_p_len, dh_g, &dh_g_len, dh_Ys, &dh_Ys_len, context->dhe)) { + DEBUG_PRINT("ERROR EXPORTING DHE KEY %p\n", context->dhe); + TLS_FREE(packet); + _private_tls_dhe_free(context); + return NULL; + } + _private_tls_dhe_free(context); + DEBUG_DUMP_HEX_LABEL("Yc", dh_Ys, dh_Ys_len); + tls_packet_uint24(packet, dh_Ys_len + 2); + if (context->dtls) + _private_dtls_handshake_data(context, packet, dh_Ys_len + 2); + tls_packet_uint16(packet, dh_Ys_len); + tls_packet_append(packet, dh_Ys, dh_Ys_len); + } else + if (context->ecc_dhe) { + unsigned char out[TLS_MAX_RSA_KEY]; + unsigned long out_len = TLS_MAX_RSA_KEY; + + if (ecc_ansi_x963_export(context->ecc_dhe, out, &out_len)) { + DEBUG_PRINT("Error exporting ECC key\n"); + TLS_FREE(packet); + return NULL; + } + _private_tls_ecc_dhe_free(context); + tls_packet_uint24(packet, out_len + 1); + if (context->dtls) { + _private_dtls_handshake_data(context, packet, out_len + 1); + context->dtls_seq++; + } + tls_packet_uint8(packet, out_len); + tls_packet_append(packet, out, out_len); + } +#ifdef TLS_CURVE25519 + else + if ((context->curve == &x25519) && (context->client_secret)) { + static const unsigned char basepoint[32] = {9}; + unsigned char shared_key[32]; + curve25519(shared_key, context->client_secret, basepoint); + tls_packet_uint24(packet, 32 + 1); + tls_packet_uint8(packet, 32); + tls_packet_append(packet, shared_key, 32); + TLS_FREE(context->client_secret); + context->client_secret = NULL; + } +#endif + _private_tls_compute_key(context, 48); + } else +#endif + _private_tls_build_random(packet); + context->connection_status = 2; + tls_packet_update(packet); + return packet; +} + +void _private_dtls_handshake_data(struct TLSContext *context, struct TLSPacket *packet, unsigned int framelength) { + // message seq + tls_packet_uint16(packet, context->dtls_seq); + // fragment offset + tls_packet_uint24(packet, 0); + // fragment length + tls_packet_uint24(packet, framelength); +} + +void _private_dtls_handshake_copyframesize(struct TLSPacket *packet) { + packet->buf[22] = packet->buf[14]; + packet->buf[23] = packet->buf[15]; + packet->buf[24] = packet->buf[16]; +} + +struct TLSPacket *tls_build_server_key_exchange(struct TLSContext *context, int method) { + if (!context->is_server) { + DEBUG_PRINT("CANNOT BUILD SERVER KEY EXCHANGE MESSAGE FOR CLIENTS\n"); + return NULL; + } + + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0); + tls_packet_uint8(packet, 0x0C); + unsigned char dummy[3]; + tls_packet_append(packet, dummy, 3); + if (context->dtls) + _private_dtls_handshake_data(context, packet, 0); + int start_len = packet->len; +#ifdef TLS_FORWARD_SECRECY + if (method == KEA_dhe_rsa) { + tls_init(); + _private_tls_dhe_create(context); + + const char *default_dhe_p = context->default_dhe_p; + const char *default_dhe_g = context->default_dhe_g; + int key_size; + if ((!default_dhe_p) || (!default_dhe_g)) { + default_dhe_p = TLS_DH_DEFAULT_P; + default_dhe_g = TLS_DH_DEFAULT_G; + key_size = TLS_DHE_KEY_SIZE / 8; + } else { + key_size = strlen(default_dhe_p); + } + if (_private_tls_dh_make_key(key_size, context->dhe, default_dhe_p, default_dhe_g, 0, 0)) { + DEBUG_PRINT("ERROR CREATING DHE KEY\n"); + TLS_FREE(packet); + TLS_FREE(context->dhe); + context->dhe = NULL; + return NULL; + } + + unsigned char dh_Ys[0xFFF]; + unsigned char dh_p[0xFFF]; + unsigned char dh_g[0xFFF]; + unsigned long dh_p_len = sizeof(dh_p); + unsigned long dh_g_len = sizeof(dh_g); + unsigned long dh_Ys_len = sizeof(dh_Ys); + + if (_private_tls_dh_export_pqY(dh_p, &dh_p_len, dh_g, &dh_g_len, dh_Ys, &dh_Ys_len, context->dhe)) { + DEBUG_PRINT("ERROR EXPORTING DHE KEY\n"); + TLS_FREE(packet); + return NULL; + } + + DEBUG_PRINT("LEN: %lu (%lu, %lu)\n", dh_Ys_len, dh_p_len, dh_g_len); + DEBUG_DUMP_HEX_LABEL("DHE PK", dh_Ys, dh_Ys_len); + DEBUG_DUMP_HEX_LABEL("DHE P", dh_p, dh_p_len); + DEBUG_DUMP_HEX_LABEL("DHE G", dh_g, dh_g_len); + + tls_packet_uint16(packet, dh_p_len); + tls_packet_append(packet, dh_p, dh_p_len); + + tls_packet_uint16(packet, dh_g_len); + tls_packet_append(packet, dh_g, dh_g_len); + + tls_packet_uint16(packet, dh_Ys_len); + tls_packet_append(packet, dh_Ys, dh_Ys_len); + //dh_p + //dh_g + //dh_Ys + } else + if (method == KEA_ec_diffie_hellman) { + // 3 = named curve + if (!context->curve) + context->curve = default_curve; + tls_packet_uint8(packet, 3); + tls_packet_uint16(packet, context->curve->iana); + tls_init(); + _private_tls_ecc_dhe_create(context); + + ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&context->curve->dp; + + if (ecc_make_key_ex(NULL, find_prng("sprng"), context->ecc_dhe, dp)) { + TLS_FREE(context->ecc_dhe); + context->ecc_dhe = NULL; + DEBUG_PRINT("Error generating ECC key\n"); + TLS_FREE(packet); + return NULL; + } + unsigned char out[TLS_MAX_RSA_KEY]; + unsigned long out_len = TLS_MAX_RSA_KEY; + if (ecc_ansi_x963_export(context->ecc_dhe, out, &out_len)) { + DEBUG_PRINT("Error exporting ECC key\n"); + TLS_FREE(packet); + return NULL; + } + tls_packet_uint8(packet, out_len); + tls_packet_append(packet, out, out_len); + } else +#endif + { + TLS_FREE(packet); + DEBUG_PRINT("Unsupported ephemeral method: %i\n", method); + return NULL; + } + + // signature + unsigned int params_len = packet->len - start_len; + unsigned int message_len = params_len + TLS_CLIENT_RANDOM_SIZE + TLS_SERVER_RANDOM_SIZE; + unsigned char *message = (unsigned char *)TLS_MALLOC(message_len); + if (message) { + unsigned char out[TLS_MAX_RSA_KEY]; + unsigned long out_len = TLS_MAX_RSA_KEY; + + int hash_algorithm; + if ((context->version != TLS_V13) && (context->version != DTLS_V13) && (context->version != TLS_V12) && (context->version != DTLS_V12)) { + hash_algorithm = _md5_sha1; + } else { + if ((context->version == TLS_V13) || (context->version == DTLS_V13) || (context->version == TLS_V12) || (context->version == DTLS_V12)) + hash_algorithm = sha256; + else + hash_algorithm = sha1; + +#ifdef TLS_ECDSA_SUPPORTED + if (tls_is_ecdsa(context)) { + if ((context->version == TLS_V13) || (context->version == DTLS_V13) || (context->version == TLS_V12) || (context->version == DTLS_V12)) + hash_algorithm = sha512; + tls_packet_uint8(packet, hash_algorithm); + tls_packet_uint8(packet, ecdsa); + } else +#endif + { + tls_packet_uint8(packet, hash_algorithm); + tls_packet_uint8(packet, rsa_sign); + } + } + + memcpy(message, context->remote_random, TLS_CLIENT_RANDOM_SIZE); + memcpy(message + TLS_CLIENT_RANDOM_SIZE, context->local_random, TLS_SERVER_RANDOM_SIZE); + memcpy(message + TLS_CLIENT_RANDOM_SIZE + TLS_SERVER_RANDOM_SIZE, packet->buf + start_len, params_len); +#ifdef TLS_ECDSA_SUPPORTED + if (tls_is_ecdsa(context)) { + if (_private_tls_sign_ecdsa(context, hash_algorithm, message, message_len, out, &out_len) == 1) { + DEBUG_PRINT("Signing OK! (ECDSA, length %lu)\n", out_len); + tls_packet_uint16(packet, out_len); + tls_packet_append(packet, out, out_len); + } + } else +#endif + if (_private_tls_sign_rsa(context, hash_algorithm, message, message_len, out, &out_len) == 1) { + DEBUG_PRINT("Signing OK! (length %lu)\n", out_len); + tls_packet_uint16(packet, out_len); + tls_packet_append(packet, out, out_len); + } + TLS_FREE(message); + } + if ((!packet->broken) && (packet->buf)) { + int remaining = packet->len - start_len; + int payload_pos = 6; + if (context->dtls) + payload_pos = 14; + packet->buf[payload_pos] = remaining / 0x10000; + remaining %= 0x10000; + packet->buf[payload_pos + 1] = remaining / 0x100; + remaining %= 0x100; + packet->buf[payload_pos + 2] = remaining; + if (context->dtls) { + _private_dtls_handshake_copyframesize(packet); + context->dtls_seq++; + } + } + tls_packet_update(packet); + return packet; +} + +void _private_tls_set_session_id(struct TLSContext *context) { + if (((context->version == TLS_V13) || (context->version == DTLS_V13)) && (context->session_size == TLS_MAX_SESSION_ID)) + return; + if (tls_random(context->session, TLS_MAX_SESSION_ID)) + context->session_size = TLS_MAX_SESSION_ID; + else + context->session_size = 0; +} + +struct TLSPacket *tls_build_hello(struct TLSContext *context, int tls13_downgrade) { + tls_init(); +#ifdef WITH_TLS_13 + if (context->connection_status == 4) { + static unsigned char sha256_helloretryrequest[] = {0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C}; + memcpy(context->local_random, sha256_helloretryrequest, 32); + unsigned char header[4] = {0xFE, 0, 0, 0}; + unsigned char hash[TLS_MAX_SHA_SIZE ]; + int hash_len = _private_tls_done_hash(context, hash); + header[3] = (unsigned char)hash_len; + _private_tls_update_hash(context, header, sizeof(header)); + _private_tls_update_hash(context, hash, hash_len); + } else + if ((!context->is_server) || ((context->version != TLS_V13) && (context->version != DTLS_V13))) +#endif + if (!tls_random(context->local_random, context->is_server ? TLS_SERVER_RANDOM_SIZE : TLS_CLIENT_RANDOM_SIZE)) + return NULL; + if (!context->is_server) + *(unsigned int *)context->local_random = htonl((unsigned int)time(NULL)); + + if ((context->is_server) && (tls13_downgrade)) { + if ((tls13_downgrade == TLS_V12) || (tls13_downgrade == DTLS_V12)) + memcpy(context->local_random + TLS_SERVER_RANDOM_SIZE - 8, "DOWNGRD\x01", 8); + else + memcpy(context->local_random + TLS_SERVER_RANDOM_SIZE - 8, "DOWNGRD\x00", 8); + } + unsigned short packet_version = context->version; + unsigned short version = context->version; +#ifdef WITH_TLS_13 + if (context->version == TLS_V13) + version = TLS_V12; + else + if (context->version == DTLS_V13) + version = DTLS_V12; +#endif + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, version, 0); + if (packet) { + // hello + if (context->is_server) + tls_packet_uint8(packet, 0x02); + else + tls_packet_uint8(packet, 0x01); + unsigned char dummy[3]; + tls_packet_append(packet, dummy, 3); + + if (context->dtls) + _private_dtls_handshake_data(context, packet, 0); + + int start_len = packet->len; + tls_packet_uint16(packet, version); + if (context->is_server) + tls_packet_append(packet, context->local_random, TLS_SERVER_RANDOM_SIZE); + else + tls_packet_append(packet, context->local_random, TLS_CLIENT_RANDOM_SIZE); + +#ifdef IGNORE_SESSION_ID + // session size + tls_packet_uint8(packet, 0); +#else + _private_tls_set_session_id(context); + // session size + tls_packet_uint8(packet, context->session_size); + if (context->session_size) + tls_packet_append(packet, context->session, context->session_size); +#endif + + int extension_len = 0; + int alpn_len = 0; + int alpn_negotiated_len = 0; + int i; +#ifdef WITH_TLS_13 + unsigned char shared_key[TLS_MAX_RSA_KEY]; + unsigned long shared_key_len = TLS_MAX_RSA_KEY; + unsigned short shared_key_short = 0; + int selected_group = 0; + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + if (context->connection_status == 4) { + // connection_status == 4 => hello retry request + extension_len += 6; + } else + if (context->is_server) { +#ifdef TLS_CURVE25519 + if (context->curve == &x25519) { + extension_len += 8 + 32; + shared_key_short = (unsigned short)32; + if (context->finished_key) { + memcpy(shared_key, context->finished_key, 32); + TLS_FREE(context->finished_key); + context->finished_key = NULL; + } + selected_group = context->curve->iana; + // make context->curve NULL (x25519 is a different implementation) + context->curve = NULL; + } else +#endif + if (context->ecc_dhe) { + if (ecc_ansi_x963_export(context->ecc_dhe, shared_key, &shared_key_len)) { + DEBUG_PRINT("Error exporting ECC DHE key\n"); + tls_destroy_packet(packet); + return tls_build_alert(context, 1, internal_error); + } + _private_tls_ecc_dhe_free(context); + extension_len += 8 + shared_key_len; + shared_key_short = (unsigned short)shared_key_len; + if (context->curve) + selected_group = context->curve->iana; + } else + if (context->dhe) { + selected_group = context->dhe->iana; + _private_tls_dh_export_Y(shared_key, &shared_key_len, context->dhe); + _private_tls_dhe_free(context); + extension_len += 8 + shared_key_len; + shared_key_short = (unsigned short)shared_key_len; + } + } + // supported versions + if (context->is_server) + extension_len += 6; + else + extension_len += 9; + } + if ((context->is_server) && (context->negotiated_alpn) && (context->version != TLS_V13) && (context->version != DTLS_V13)) { +#else + if ((context->is_server) && (context->negotiated_alpn)) { +#endif + alpn_negotiated_len = strlen(context->negotiated_alpn); + alpn_len = alpn_negotiated_len + 1; + extension_len += alpn_len + 6; + } else + if ((!context->is_server) && (context->alpn_count)) { + for (i = 0; i < context->alpn_count;i++) { + if (context->alpn[i]) { + int len = strlen(context->alpn[i]); + if (len) + alpn_len += len + 1; + } + } + if (alpn_len) + extension_len += alpn_len + 6; + } + + // ciphers + if (context->is_server) { + // fallback ... this should never happen + if (!context->cipher) + context->cipher = TLS_DHE_RSA_WITH_AES_128_CBC_SHA; + + tls_packet_uint16(packet, context->cipher); + // no compression + tls_packet_uint8(packet, 0); +#ifndef STRICT_TLS + if ((context->version == TLS_V13) || (context->version == DTLS_V13) || (context->version == TLS_V12) || (context->version == DTLS_V12)) { + // extensions size +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + tls_packet_uint16(packet, extension_len); + } else +#endif + { + tls_packet_uint16(packet, 5 + extension_len); + // secure renegotation + // advertise it, but refuse renegotiation + tls_packet_uint16(packet, 0xff01); +#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION + // a little defensive + if ((context->verify_len) && (!context->verify_data)) + context->verify_len = 0; + tls_packet_uint16(packet, context->verify_len + 1); + tls_packet_uint8(packet, context->verify_len); + if (context->verify_len) + tls_packet_append(packet, (unsigned char *)context->verify_data, context->verify_len); +#else + tls_packet_uint16(packet, 1); + tls_packet_uint8(packet, 0); +#endif + } + if (alpn_len) { + tls_packet_uint16(packet, 0x10); + tls_packet_uint16(packet, alpn_len + 2); + tls_packet_uint16(packet, alpn_len); + + tls_packet_uint8(packet, alpn_negotiated_len); + tls_packet_append(packet, (unsigned char *)context->negotiated_alpn, alpn_negotiated_len); + } + } +#endif + } else { + if (context->dtls) { + tls_packet_uint8(packet, context->dtls_cookie_len); + if (context->dtls_cookie_len) + tls_packet_append(packet, context->dtls_cookie, context->dtls_cookie_len); + } + +#ifndef STRICT_TLS +#ifdef WITH_TLS_13 +#ifdef TLS_FORWARD_SECRECY + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + #ifdef TLS_WITH_CHACHA20_POLY1305 + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(9, 0)); + tls_packet_uint16(packet, TLS_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_AES_256_GCM_SHA384); + tls_packet_uint16(packet, TLS_CHACHA20_POLY1305_SHA256); + #else + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(8, 0)); + tls_packet_uint16(packet, TLS_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_AES_256_GCM_SHA384); + #endif + #ifdef TLS_PREFER_CHACHA20 + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256); + #else + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256); + #endif + } else +#endif +#endif + if ((context->version == TLS_V12) || (context->version == DTLS_V12)) { +#endif +#ifdef TLS_FORWARD_SECRECY +#ifdef TLS_CLIENT_ECDHE +#ifdef TLS_WITH_CHACHA20_POLY1305 + #ifdef TLS_CLIENT_ECDSA + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(16, 5)); + #ifdef TLS_PREFER_CHACHA20 + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256); + #endif + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256); + #ifndef TLS_PREFER_CHACHA20 + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256); + #endif + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA); + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA); + #else + // sizeof ciphers (16 ciphers * 2 bytes) + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(11, 5)); + #endif +#else + #ifdef TLS_CLIENT_ECDSA + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(13, 5)); + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA); + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA); + #else + // sizeof ciphers (14 ciphers * 2 bytes) + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(9, 5)); + #endif +#endif +#ifdef TLS_WITH_CHACHA20_POLY1305 + #ifdef TLS_PREFER_CHACHA20 + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256); + #endif +#endif + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA); + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA); + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256); +#ifdef TLS_WITH_CHACHA20_POLY1305 + #ifndef TLS_PREFER_CHACHA20 + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256); + #endif +#endif +#else +#ifdef TLS_WITH_CHACHA20_POLY1305 + // sizeof ciphers (11 ciphers * 2 bytes) + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(6, 5)); +#else + // sizeof ciphers (10 ciphers * 2 bytes) + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(5, 5)); +#endif +#endif + // not yet supported, because the first message sent (this one) + // is already hashed by the client with sha256 (sha384 not yet supported client-side) + // but is fully suported server-side + // tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_CBC_SHA); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_CBC_SHA); +#ifdef TLS_WITH_CHACHA20_POLY1305 + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256); +#endif +#else + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(0, 5)); +#endif + // tls_packet_uint16(packet, TLS_RSA_WITH_AES_256_GCM_SHA384); +#ifndef TLS_ROBOT_MITIGATION + tls_packet_uint16(packet, TLS_RSA_WITH_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_RSA_WITH_AES_256_CBC_SHA256); + tls_packet_uint16(packet, TLS_RSA_WITH_AES_128_CBC_SHA256); + tls_packet_uint16(packet, TLS_RSA_WITH_AES_256_CBC_SHA); + tls_packet_uint16(packet, TLS_RSA_WITH_AES_128_CBC_SHA); +#endif +#ifndef STRICT_TLS + } else { +#ifdef TLS_FORWARD_SECRECY +#ifdef TLS_CLIENT_ECDHE + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(5, 2)); + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA); + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA); +#else + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(3, 2)); +#endif + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_CBC_SHA); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_CBC_SHA); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_CBC_SHA); +#else + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(0, 2)); +#endif +#ifndef TLS_ROBOT_MITIGATION + tls_packet_uint16(packet, TLS_RSA_WITH_AES_256_CBC_SHA); + tls_packet_uint16(packet, TLS_RSA_WITH_AES_128_CBC_SHA); +#endif + } +#endif + // compression + tls_packet_uint8(packet, 1); + // no compression + tls_packet_uint8(packet, 0); + if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { + int sni_len = 0; + if (context->sni) + sni_len = strlen(context->sni); + +#ifdef TLS_CLIENT_ECDHE + extension_len += 12; +#endif + if (sni_len) + extension_len += sni_len + 9; +#ifdef WITH_TLS_13 + if ((!context->is_server) && ((context->version == TLS_V13) || (context->version == DTLS_V13))) { +#ifdef TLS_CURVE25519 + extension_len += 70; +#else + // secp256r1 produces 65 bytes export + extension_len += 103; +#endif + } +#endif + tls_packet_uint16(packet, extension_len); + + if (sni_len) { + // sni extension + tls_packet_uint16(packet, 0x00); + // sni extension len + tls_packet_uint16(packet, sni_len + 5); + // sni len + tls_packet_uint16(packet, sni_len + 3); + // sni type + tls_packet_uint8(packet, 0); + // sni host len + tls_packet_uint16(packet, sni_len); + tls_packet_append(packet, (unsigned char *)context->sni, sni_len); + } +#ifdef TLS_FORWARD_SECRECY +#ifdef TLS_CLIENT_ECDHE + // supported groups + tls_packet_uint16(packet, 0x0A); + tls_packet_uint16(packet, 8); + // 3 curves x 2 bytes + tls_packet_uint16(packet, 6); + tls_packet_uint16(packet, secp256r1.iana); + tls_packet_uint16(packet, secp384r1.iana); +#ifdef TLS_CURVE25519 + tls_packet_uint16(packet, x25519.iana); +#else + tls_packet_uint16(packet, secp224r1.iana); +#endif +#endif +#endif + if (alpn_len) { + tls_packet_uint16(packet, 0x10); + tls_packet_uint16(packet, alpn_len + 2); + tls_packet_uint16(packet, alpn_len); + + for (i = 0; i < context->alpn_count;i++) { + if (context->alpn[i]) { + int len = strlen(context->alpn[i]); + if (len) { + tls_packet_uint8(packet, len); + tls_packet_append(packet, (unsigned char *)context->alpn[i], len); + } + } + } + } + } + } +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + // supported versions + tls_packet_uint16(packet, 0x2B); + if (context->is_server) { + tls_packet_uint16(packet, 2); + if (context->version == TLS_V13) + tls_packet_uint16(packet, context->tls13_version ? context->tls13_version : TLS_V13); + else + tls_packet_uint16(packet, context->version); + } else { + tls_packet_uint16(packet, 5); + tls_packet_uint8(packet, 4); + tls_packet_uint16(packet, TLS_V13); + tls_packet_uint16(packet, 0x7F1C); + } + if (context->connection_status == 4) { + // fallback to the mandatory secp256r1 + tls_packet_uint16(packet, 0x33); + tls_packet_uint16(packet, 2); + tls_packet_uint16(packet, (unsigned short)secp256r1.iana); + } + if (((shared_key_short) && (selected_group)) || (!context->is_server)) { + // key share + tls_packet_uint16(packet, 0x33); + if (context->is_server) { + tls_packet_uint16(packet, shared_key_short + 4); + tls_packet_uint16(packet, (unsigned short)selected_group); + tls_packet_uint16(packet, shared_key_short); + tls_packet_append(packet, (unsigned char *)shared_key, shared_key_short); + } else { +#ifdef TLS_CURVE25519 + // make key + shared_key_short = 32; + tls_packet_uint16(packet, shared_key_short + 6); + tls_packet_uint16(packet, shared_key_short + 4); + + TLS_FREE(context->client_secret); + context->client_secret = (unsigned char *)TLS_MALLOC(32); + if (!context->client_secret) { + DEBUG_PRINT("ERROR IN TLS_MALLOC"); + TLS_FREE(packet); + return NULL; + + } + + static const unsigned char basepoint[32] = {9}; + + tls_random(context->client_secret, 32); + + context->client_secret[0] &= 248; + context->client_secret[31] &= 127; + context->client_secret[31] |= 64; + + curve25519(shared_key, context->client_secret, basepoint); + + tls_packet_uint16(packet, (unsigned short)x25519.iana); + tls_packet_uint16(packet, shared_key_short); + tls_packet_append(packet, (unsigned char *)shared_key, shared_key_short); +#else + // make key + shared_key_short = 65; + tls_packet_uint16(packet, shared_key_short + 6); + tls_packet_uint16(packet, shared_key_short + 4); + + _private_tls_ecc_dhe_create(context); + ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&secp256r1.dp; + + if (ecc_make_key_ex(NULL, find_prng("sprng"), context->ecc_dhe, dp)) { + TLS_FREE(context->ecc_dhe); + context->ecc_dhe = NULL; + DEBUG_PRINT("Error generating ECC key\n"); + TLS_FREE(packet); + return NULL; + } + unsigned char out[TLS_MAX_RSA_KEY]; + unsigned long out_len = shared_key_short; + if (ecc_ansi_x963_export(context->ecc_dhe, out, &out_len)) { + DEBUG_PRINT("Error exporting ECC key\n"); + TLS_FREE(packet); + return NULL; + } + + tls_packet_uint16(packet, (unsigned short)secp256r1.iana); + tls_packet_uint16(packet, out_len); + tls_packet_append(packet, (unsigned char *)out, shared_key_short); +#endif + } + } + if (!context->is_server) { + // signature algorithms + tls_packet_uint16(packet, 0x0D); + tls_packet_uint16(packet, 24); + tls_packet_uint16(packet, 22); + tls_packet_uint16(packet, 0x0403); + tls_packet_uint16(packet, 0x0503); + tls_packet_uint16(packet, 0x0603); + tls_packet_uint16(packet, 0x0804); + tls_packet_uint16(packet, 0x0805); + tls_packet_uint16(packet, 0x0806); + tls_packet_uint16(packet, 0x0401); + tls_packet_uint16(packet, 0x0501); + tls_packet_uint16(packet, 0x0601); + tls_packet_uint16(packet, 0x0203); + tls_packet_uint16(packet, 0x0201); + } + } +#endif + + if ((!packet->broken) && (packet->buf)) { + int remaining = packet->len - start_len; + int payload_pos = 6; + if (context->dtls) + payload_pos = 14; + packet->buf[payload_pos] = remaining / 0x10000; + remaining %= 0x10000; + packet->buf[payload_pos + 1] = remaining / 0x100; + remaining %= 0x100; + packet->buf[payload_pos + 2] = remaining; + if (context->dtls) { + _private_dtls_handshake_copyframesize(packet); + context->dtls_seq++; + } + } + tls_packet_update(packet); + } + return packet; +} + +struct TLSPacket *tls_certificate_request(struct TLSContext *context) { + if ((!context) || (!context->is_server)) + return NULL; + + unsigned short packet_version = context->version; + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, packet_version, 0); + if (packet) { + // certificate request + tls_packet_uint8(packet, 0x0D); + unsigned char dummy[3]; + tls_packet_append(packet, dummy, 3); + if (context->dtls) + _private_dtls_handshake_data(context, packet, 0); + int start_len = packet->len; +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + // certificate request context + tls_packet_uint8(packet, 0); + // extensions + tls_packet_uint16(packet, 18); + // signature algorithms + tls_packet_uint16(packet, 0x0D); + tls_packet_uint16(packet, 14); + tls_packet_uint16(packet, 12); + // rsa_pkcs1_sha256 + // tls_packet_uint16(packet, 0x0401); + // rsa_pkcs1_sha384 + // tls_packet_uint16(packet, 0x0501); + // rsa_pkcs1_sha512 + // tls_packet_uint16(packet, 0x0601); + + // ecdsa_secp256r1_sha256 + tls_packet_uint16(packet, 0x0403); + // ecdsa_secp384r1_sha384 + tls_packet_uint16(packet, 0x0503); + // ecdsa_secp521r1_sha512 + tls_packet_uint16(packet, 0x0604); + // rsa_pss_rsae_sha256 + tls_packet_uint16(packet, 0x0804); + // rsa_pss_rsae_sha384 + tls_packet_uint16(packet, 0x0805); + // rsa_pss_rsae_sha512 + tls_packet_uint16(packet, 0x0806); + } else +#endif + { + tls_packet_uint8(packet, 1); + tls_packet_uint8(packet, rsa_sign); + if ((context->version == TLS_V12) || (context->version == DTLS_V12)) { + // 10 pairs or 2 bytes + tls_packet_uint16(packet, 10); + tls_packet_uint8(packet, sha256); + tls_packet_uint8(packet, rsa); + tls_packet_uint8(packet, sha1); + tls_packet_uint8(packet, rsa); + tls_packet_uint8(packet, sha384); + tls_packet_uint8(packet, rsa); + tls_packet_uint8(packet, sha512); + tls_packet_uint8(packet, rsa); + tls_packet_uint8(packet, md5); + tls_packet_uint8(packet, rsa); + } + // no DistinguishedName yet + tls_packet_uint16(packet, 0); + } + if (!packet->broken) { + int remaining = packet->len - start_len; + int payload_pos = 6; + if (context->dtls) + payload_pos = 14; + packet->buf[payload_pos] = remaining / 0x10000; + remaining %= 0x10000; + packet->buf[payload_pos + 1] = remaining / 0x100; + remaining %= 0x100; + packet->buf[payload_pos + 2] = remaining; + + if (context->dtls) { + _private_dtls_handshake_copyframesize(packet); + context->dtls_seq++; + } + } + tls_packet_update(packet); + } + return packet; +} + +int _private_dtls_build_cookie(struct TLSContext *context) { + if ((!context->dtls_cookie) || (!context->dtls_cookie_len)) { + context->dtls_cookie = (unsigned char *)TLS_MALLOC(DTLS_COOKIE_SIZE); + if (!context->dtls_cookie) + return 0; + +#ifdef WITH_RANDOM_DLTS_COOKIE + if (!tls_random(context->dtls_cookie, DTLS_COOKIE_SIZE)) { + TLS_FREE(context->dtls_cookie); + context->dtls_cookie = NULL; + return 0; + } + context->dtls_cookie_len = DTLS_COOKIE_SIZE; +#else + hmac_state hmac; + hmac_init(&hmac, find_hash("sha256"), dtls_secret, sizeof(dtls_secret)); + hmac_process(&hmac, context->remote_random, TLS_CLIENT_RANDOM_SIZE); + + unsigned long out_size = DTLS_COOKIE_SIZE; + hmac_done(&hmac, context->dtls_cookie, &out_size); +#endif + } + return 1; +} + +struct TLSPacket *tls_build_verify_request(struct TLSContext *context) { + if ((!context->is_server) || (!context->dtls)) + return NULL; + + if ((!context->dtls_cookie) || (!context->dtls_cookie_len)) { + if (!_private_dtls_build_cookie(context)) + return NULL; + } + + unsigned short packet_version = context->version; + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, packet_version, 0); + if (packet) { + // verify request + tls_packet_uint8(packet, 0x03); + // 24-bit length + tls_packet_uint24(packet, context->dtls_cookie_len + 3); + // 16-bit message_sequence + tls_packet_uint16(packet, 0); + // 24-bit fragment_offset + tls_packet_uint24(packet, 0); + // 24-bit fragment_offset + tls_packet_uint24(packet, context->dtls_cookie_len + 3); + // server_version + tls_packet_uint16(packet, context->version); + tls_packet_uint8(packet, context->dtls_cookie_len); + tls_packet_append(packet, context->dtls_cookie, context->dtls_cookie_len); + tls_packet_update(packet); + } + return packet; +} + +int _private_dtls_check_packet(const unsigned char *buf, int buf_len) { + CHECK_SIZE(11, buf_len, TLS_NEED_MORE_DATA) + + unsigned int bytes_to_follow = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; + // not used: unsigned short message_seq = ntohs(*(unsigned short *)&buf[3]); + unsigned int fragment_offset = buf[5] * 0x10000 + buf[6] * 0x100 + buf[7]; + unsigned int fragment_length = buf[8] * 0x10000 + buf[9] * 0x100 + buf[10]; + + if ((fragment_offset) || (fragment_length != bytes_to_follow)) { + DEBUG_PRINT("FRAGMENTED PACKETS NOT SUPPORTED\n"); + return TLS_FEATURE_NOT_SUPPORTED; + } + return bytes_to_follow; +} + +void _private_dtls_reset(struct TLSContext *context) { + context->dtls_epoch_local = 0; + context->dtls_epoch_remote = 0; + context->dtls_seq = 0; + _private_tls_destroy_hash(context); + context->connection_status = 0; +} + +int tls_parse_verify_request(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets) { + *write_packets = 0; + if ((context->connection_status != 0) || (!context->dtls)) { + DEBUG_PRINT("UNEXPECTED VERIFY REQUEST MESSAGE\n"); + return TLS_UNEXPECTED_MESSAGE; + } + int res = 11; + int bytes_to_follow = _private_dtls_check_packet(buf, buf_len); + if (bytes_to_follow < 0) + return bytes_to_follow; + + CHECK_SIZE(bytes_to_follow, buf_len - res, TLS_NEED_MORE_DATA) + // not used: unsigned short version = ntohs(*(unsigned short *)&buf[res]); + res += 2; + unsigned char len = buf[res]; + res++; + TLS_FREE(context->dtls_cookie); + context->dtls_cookie_len = 0; + if (len) { + CHECK_SIZE(len, buf_len - res, TLS_NEED_MORE_DATA) + context->dtls_cookie = (unsigned char *)TLS_MALLOC(len); + if (!context->dtls_cookie) + return TLS_NO_MEMORY; + context->dtls_cookie_len = len; + memcpy(context->dtls_cookie, &buf[res], len); + res += len; + *write_packets = 4; + } + + // reset context + _private_dtls_reset(context); + return res; +} + +void _private_dtls_reset_cookie(struct TLSContext *context) { + TLS_FREE(context->dtls_cookie); + context->dtls_cookie = NULL; + context->dtls_cookie_len = 0; +} + +#ifdef WITH_TLS_13 +int _private_tls_parse_key_share(struct TLSContext *context, const unsigned char *buf, int buf_len) { + int i = 0; + struct ECCCurveParameters *curve = 0; + DHKey *dhkey = 0; + int dhe_key_size = 0; + const unsigned char *buffer = NULL; + unsigned char *out2; + unsigned long out_size; + unsigned short key_size = 0; + while (buf_len >= 4) { + unsigned short named_group = ntohs(*(unsigned short *)&buf[i]); + i += 2; + buf_len -= 2; + + key_size = ntohs(*(unsigned short *)&buf[i]); + i += 2; + buf_len -= 2; + + if (key_size > buf_len) + return TLS_BROKEN_PACKET; + + switch (named_group) { + case 0x0017: + curve = &secp256r1; + buffer = &buf[i]; + DEBUG_PRINT("KEY SHARE => secp256r1\n"); + buf_len = 0; + continue; + case 0x0018: + // secp384r1 + curve = &secp384r1; + buffer = &buf[i]; + DEBUG_PRINT("KEY SHARE => secp384r1\n"); + buf_len = 0; + continue; + case 0x0019: + // secp521r1 + break; + case 0x001D: + // x25519 +#ifdef TLS_CURVE25519 + if (key_size != 32) { + DEBUG_PRINT("INVALID x25519 KEY SIZE (%i)\n", key_size); + continue; + } + curve = &x25519; + buffer = &buf[i]; + DEBUG_PRINT("KEY SHARE => x25519\n"); + buf_len = 0; + continue; +#endif + break; + + case 0x001E: + // x448 + break; + case 0x0100: + dhkey = &ffdhe2048; + dhe_key_size = 2048; + break; + case 0x0101: + dhkey = &ffdhe3072; + dhe_key_size = 3072; + break; + case 0x0102: + dhkey = &ffdhe4096; + dhe_key_size = 4096; + break; + case 0x0103: + dhkey = &ffdhe6144; + dhe_key_size = 6144; + break; + case 0x0104: + dhkey = &ffdhe8192; + dhe_key_size = 8192; + break; + } + i += key_size; + buf_len -= key_size; + + if (!context->is_server) + break; + } + tls_init(); + if (curve) { + context->curve = curve; +#ifdef TLS_CURVE25519 + if (curve == &x25519) { + if ((context->is_server) && (!tls_random(context->local_random, TLS_SERVER_RANDOM_SIZE))) + return TLS_GENERIC_ERROR; + unsigned char secret[32]; + static const unsigned char basepoint[32] = {9}; + + if ((context->is_server) || (!context->client_secret)) { + tls_random(secret, 32); + + secret[0] &= 248; + secret[31] &= 127; + secret[31] |= 64; + + // use finished key to store public key + TLS_FREE(context->finished_key); + context->finished_key = (unsigned char *)TLS_MALLOC(32); + if (!context->finished_key) + return TLS_GENERIC_ERROR; + + curve25519(context->finished_key, secret, basepoint); + + TLS_FREE(context->premaster_key); + context->premaster_key = (unsigned char *)TLS_MALLOC(32); + if (!context->premaster_key) + return TLS_GENERIC_ERROR; + + curve25519(context->premaster_key, secret, buffer); + context->premaster_key_len = 32; + } else { + TLS_FREE(context->premaster_key); + context->premaster_key = (unsigned char *)TLS_MALLOC(32); + if (!context->premaster_key) + return TLS_GENERIC_ERROR; + + curve25519(context->premaster_key, context->client_secret, buffer); + context->premaster_key_len = 32; + + TLS_FREE(context->client_secret); + context->client_secret = NULL; + } + DEBUG_DUMP_HEX_LABEL("x25519 KEY", context->premaster_key, context->premaster_key_len); + + return 0; + } +#endif + if (context->is_server) { + _private_tls_ecc_dhe_create(context); + if (ecc_make_key_ex(NULL, find_prng("sprng"), context->ecc_dhe, (ltc_ecc_set_type *)&context->curve->dp)) { + TLS_FREE(context->ecc_dhe); + context->ecc_dhe = NULL; + DEBUG_PRINT("Error generating ECC DHE key\n"); + return TLS_GENERIC_ERROR; + } + } + + ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&context->curve->dp; + + if ((context->is_server) && (!tls_random(context->local_random, TLS_SERVER_RANDOM_SIZE))) + return TLS_GENERIC_ERROR; + + ecc_key client_key; + memset(&client_key, 0, sizeof(client_key)); + if (ecc_ansi_x963_import_ex(buffer, key_size, &client_key, dp)) { + DEBUG_PRINT("Error importing ECC DHE key\n"); + return TLS_GENERIC_ERROR; + } + out2 = (unsigned char *)TLS_MALLOC(key_size); + out_size = key_size; + + int err = ecc_shared_secret(context->ecc_dhe, &client_key, out2, &out_size); + ecc_free(&client_key); + + if (err) { + DEBUG_PRINT("ECC DHE DECRYPT ERROR %i\n", err); + TLS_FREE(out2); + return TLS_GENERIC_ERROR; + } + DEBUG_PRINT("OUT_SIZE: %lu\n", out_size); + DEBUG_DUMP_HEX_LABEL("ECC DHE", out2, out_size); + + TLS_FREE(context->premaster_key); + context->premaster_key = out2; + context->premaster_key_len = out_size; + return 0; + } else + if (dhkey) { + _private_tls_dhe_create(context); + if (!tls_random(context->local_random, TLS_SERVER_RANDOM_SIZE)) + return TLS_GENERIC_ERROR; + if (_private_tls_dh_make_key(dhe_key_size / 8, context->dhe, (const char *)dhkey->p, (const char *)dhkey->g, 0, 0)) { + TLS_FREE(context->dhe); + context->dhe = NULL; + DEBUG_PRINT("Error generating DHE key\n"); + return TLS_GENERIC_ERROR; + } + + unsigned int dhe_out_size; + out2 = _private_tls_decrypt_dhe(context, buffer, key_size, &dhe_out_size, 0); + if (!out2) { + DEBUG_PRINT("Error generating DHE shared key\n"); + return TLS_GENERIC_ERROR; + } + + TLS_FREE(context->premaster_key); + context->premaster_key = out2; + context->premaster_key_len = dhe_out_size; + if (context->dhe) + context->dhe->iana = dhkey->iana; + return 0; + } + DEBUG_PRINT("NO COMMON KEY SHARE SUPPORTED\n"); + return TLS_NO_COMMON_CIPHER; +} +#endif + +int tls_parse_hello(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets, unsigned int *dtls_verified) { + *write_packets = 0; + *dtls_verified = 0; + if ((context->connection_status != 0) && (context->connection_status != 4)) { + // ignore multiple hello on dtls + if (context->dtls) { + DEBUG_PRINT("RETRANSMITTED HELLO MESSAGE RECEIVED\n"); + return 1; + } + DEBUG_PRINT("UNEXPECTED HELLO MESSAGE\n"); + return TLS_UNEXPECTED_MESSAGE; + } + + int res = 0; + int downgraded = 0; + int hello_min_size = context->dtls ? TLS_CLIENT_HELLO_MINSIZE + 8 : TLS_CLIENT_HELLO_MINSIZE; + CHECK_SIZE(hello_min_size, buf_len, TLS_NEED_MORE_DATA) + // big endian + unsigned int bytes_to_follow = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; + res += 3; + if (context->dtls) { + int dtls_check = _private_dtls_check_packet(buf, buf_len); + if (dtls_check < 0) + return dtls_check; + // 16 bit message seq + 24 bit fragment offset + 24 bit fragment length + res += 8; + } + CHECK_SIZE(bytes_to_follow, buf_len - res, TLS_NEED_MORE_DATA) + + CHECK_SIZE(2, buf_len - res, TLS_NEED_MORE_DATA) + unsigned short version = ntohs(*(unsigned short *)&buf[res]); + unsigned short cipher = 0; + + res += 2; + VERSION_SUPPORTED(version, TLS_NOT_SAFE) + DEBUG_PRINT("VERSION REQUIRED BY REMOTE %x, VERSION NOW %x\n", (int)version, (int)context->version); +#ifdef TLS_LEGACY_SUPPORT + // when no legacy support, don't downgrade +#ifndef TLS_FORCE_LOCAL_VERSION + // downgrade ? + if (context->dtls) { + // for dlts, newer version has lower id (1.0 = FEFF, 1.2 = FEFD) + if (context->version < version) + downgraded = 1; + } else { + if (context->version > version) + downgraded = 1; + } + if (downgraded) { + context->version = version; + if (!context->is_server) + _private_tls_change_hash_type(context); + } +#endif +#endif + memcpy(context->remote_random, &buf[res], TLS_CLIENT_RANDOM_SIZE); + res += TLS_CLIENT_RANDOM_SIZE; + + unsigned char session_len = buf[res++]; + CHECK_SIZE(session_len, buf_len - res, TLS_NEED_MORE_DATA) + if ((session_len) && (session_len <= TLS_MAX_SESSION_ID)) { + memcpy(context->session, &buf[res], session_len); + context->session_size = session_len; + DEBUG_DUMP_HEX_LABEL("REMOTE SESSION ID: ", context->session, context->session_size); + } else + context->session_size = 0; + res += session_len; + + const unsigned char *cipher_buffer = NULL; + unsigned short cipher_len = 0; + int scsv_set = 0; + if (context->is_server) { + if (context->dtls) { + CHECK_SIZE(1, buf_len - res, TLS_NEED_MORE_DATA) + unsigned char tls_cookie_len = buf[res++]; + if (tls_cookie_len) { + CHECK_SIZE(tls_cookie_len, buf_len - res, TLS_NEED_MORE_DATA) + if ((!context->dtls_cookie_len) || (!context->dtls_cookie)) + _private_dtls_build_cookie(context); + + if ((context->dtls_cookie_len != tls_cookie_len) || (!context->dtls_cookie)) { + *dtls_verified = 2; + _private_dtls_reset_cookie(context); + DEBUG_PRINT("INVALID DTLS COOKIE\n"); + return TLS_BROKEN_PACKET; + } + if (memcmp(context->dtls_cookie, &buf[res], tls_cookie_len)) { + *dtls_verified = 3; + _private_dtls_reset_cookie(context); + DEBUG_PRINT("MISMATCH DTLS COOKIE\n"); + return TLS_BROKEN_PACKET; + } + _private_dtls_reset_cookie(context); + context->dtls_seq++; + *dtls_verified = 1; + res += tls_cookie_len; + } else { + *write_packets = 2; + return buf_len; + } + } + CHECK_SIZE(2, buf_len - res, TLS_NEED_MORE_DATA) + cipher_len = ntohs(*(unsigned short *)&buf[res]); + res += 2; + CHECK_SIZE(cipher_len, buf_len - res, TLS_NEED_MORE_DATA) + // faster than cipher_len % 2 + if (cipher_len & 1) + return TLS_BROKEN_PACKET; + + cipher_buffer = &buf[res]; + res += cipher_len; + + CHECK_SIZE(1, buf_len - res, TLS_NEED_MORE_DATA) + unsigned char compression_list_size = buf[res++]; + CHECK_SIZE(compression_list_size, buf_len - res, TLS_NEED_MORE_DATA) + + // no compression support + res += compression_list_size; + } else { + CHECK_SIZE(2, buf_len - res, TLS_NEED_MORE_DATA) + cipher = ntohs(*(unsigned short *)&buf[res]); + res += 2; + context->cipher = cipher; +#ifndef WITH_TLS_13 + if (!tls_cipher_supported(context, cipher)) { + context->cipher = 0; + DEBUG_PRINT("NO CIPHER SUPPORTED\n"); + return TLS_NO_COMMON_CIPHER; + } + DEBUG_PRINT("CIPHER: %s\n", tls_cipher_name(context)); +#endif + CHECK_SIZE(1, buf_len - res, TLS_NEED_MORE_DATA) + unsigned char compression = buf[res++]; + if (compression != 0) { + DEBUG_PRINT("COMPRESSION NOT SUPPORTED\n"); + return TLS_COMPRESSION_NOT_SUPPORTED; + } + } + + if (res > 0) { + if (context->is_server) + *write_packets = 2; + if (context->connection_status != 4) + context->connection_status = 1; + } + + + if (res > 2) + res += 2; +#ifdef WITH_TLS_13 + const unsigned char *key_share = NULL; + unsigned short key_size = 0; +#endif + while (buf_len - res >= 4) { + // have extensions + unsigned short extension_type = ntohs(*(unsigned short *)&buf[res]); + res += 2; + unsigned short extension_len = ntohs(*(unsigned short *)&buf[res]); + res += 2; + DEBUG_PRINT("Extension: 0x0%x (%i), len: %i\n", (int)extension_type, (int)extension_type, (int)extension_len); + if (extension_len) { + // SNI extension + CHECK_SIZE(extension_len, buf_len - res, TLS_NEED_MORE_DATA) + if (extension_type == 0x00) { + // unsigned short sni_len = ntohs(*(unsigned short *)&buf[res]); + // unsigned char sni_type = buf[res + 2]; + unsigned short sni_host_len = ntohs(*(unsigned short *)&buf[res + 3]); + CHECK_SIZE(sni_host_len, buf_len - res - 5, TLS_NEED_MORE_DATA) + if (sni_host_len) { + TLS_FREE(context->sni); + context->sni = (char *)TLS_MALLOC(sni_host_len + 1); + if (context->sni) { + memcpy(context->sni, &buf[res + 5], sni_host_len); + context->sni[sni_host_len] = 0; + DEBUG_PRINT("SNI HOST INDICATOR: [%s]\n", context->sni); + } + } + } else +#ifdef TLS_FORWARD_SECRECY + if (extension_type == 0x0A) { + // supported groups + if (buf_len - res > 2) { + unsigned short group_len = ntohs(*(unsigned short *)&buf[res]); + if (buf_len - res >= group_len + 2) { + DEBUG_DUMP_HEX_LABEL("SUPPORTED GROUPS", &buf[res + 2], group_len); + int i; + int selected = 0; + for (i = 0; i < group_len; i += 2) { + unsigned short iana_n = ntohs(*(unsigned short *)&buf[res + 2 + i]); + switch (iana_n) { + case 23: + context->curve = &secp256r1; + selected = 1; + break; + case 24: + context->curve = &secp384r1; + selected = 1; + break; +#ifdef WITH_TLS_13 + // needs different implementation + // case 29: + // context->curve = &x25519; + // selected = 1; + // break; +#endif + // do not use it anymore + // case 25: + // context->curve = &secp521r1; + // selected = 1; + // break; + } + if (selected) { + DEBUG_PRINT("SELECTED CURVE %s\n", context->curve->name); + break; + } + } + } + } + } else +#endif + if ((extension_type == 0x10) && (context->alpn) && (context->alpn_count)) { + if (buf_len - res > 2) { + unsigned short alpn_len = ntohs(*(unsigned short *)&buf[res]); + if ((alpn_len) && (alpn_len <= extension_len - 2)) { + unsigned char *alpn = (unsigned char *)&buf[res + 2]; + int alpn_pos = 0; + while (alpn_pos < alpn_len) { + unsigned char alpn_size = alpn[alpn_pos++]; + if (alpn_size + alpn_pos >= extension_len) + break; + if ((alpn_size) && (tls_alpn_contains(context, (char *)&alpn[alpn_pos], alpn_size))) { + TLS_FREE(context->negotiated_alpn); + context->negotiated_alpn = (char *)TLS_MALLOC(alpn_size + 1); + if (context->negotiated_alpn) { + memcpy(context->negotiated_alpn, &alpn[alpn_pos], alpn_size); + context->negotiated_alpn[alpn_size] = 0; + DEBUG_PRINT("NEGOTIATED ALPN: %s\n", context->negotiated_alpn); + } + break; + } + alpn_pos += alpn_size; + // ServerHello contains just one alpn + if (!context->is_server) + break; + } + } + } + } else + if (extension_type == 0x0D) { + // supported signatures + DEBUG_DUMP_HEX_LABEL("SUPPORTED SIGNATURES", &buf[res], extension_len); + } else + if (extension_type == 0x0B) { + // supported point formats + DEBUG_DUMP_HEX_LABEL("SUPPORTED POINT FORMATS", &buf[res], extension_len); + } +#ifdef WITH_TLS_13 + else + if (extension_type == 0x2B) { + // supported versions + if ((context->is_server) && (buf[res] == extension_len - 1)) { + if (extension_len > 2) { + DEBUG_DUMP_HEX_LABEL("SUPPORTED VERSIONS", &buf[res], extension_len); + int i; + int limit = (int)buf[res]; + if (limit == extension_len - 1) { + for (i = 1; i < limit; i += 2) { + if ((ntohs(*(unsigned short *)&buf[res + i]) == TLS_V13) || (ntohs(*(unsigned short *)&buf[res + i]) == 0x7F1C)) { + context->version = TLS_V13; + context->tls13_version = ntohs(*(unsigned short *)&buf[res + i]); + DEBUG_PRINT("TLS 1.3 SUPPORTED\n"); + break; + } + } + } + } + } else + if ((!context->is_server) && (extension_len == 2)) { + if ((ntohs(*(unsigned short *)&buf[res]) == TLS_V13) || (ntohs(*(unsigned short *)&buf[res]) == 0x7F1C)) { + context->version = TLS_V13; + context->tls13_version = ntohs(*(unsigned short *)&buf[res]); + DEBUG_PRINT("TLS 1.3 SUPPORTED\n"); + } + } + } else + if (extension_type == 0x2A) { + // early data + DEBUG_DUMP_HEX_LABEL("EXTENSION, EARLY DATA", &buf[res], extension_len); + } else + if (extension_type == 0x29) { + // pre shared key + DEBUG_DUMP_HEX_LABEL("EXTENSION, PRE SHARED KEY", &buf[res], extension_len); + } else + if (extension_type == 0x33) { + // key share + if (context->is_server) { + key_size = ntohs(*(unsigned short *)&buf[res]); + if ((context->is_server) && (key_size > extension_len - 2)) { + DEBUG_PRINT("BROKEN KEY SHARE\n"); + return TLS_BROKEN_PACKET; + } + } else { + key_size = extension_len; + } + DEBUG_DUMP_HEX_LABEL("EXTENSION, KEY SHARE", &buf[res], extension_len); + if (context->is_server) + key_share = &buf[res + 2]; + else + key_share = &buf[res]; + } else + if (extension_type == 0x0D) { + // signature algorithms + DEBUG_DUMP_HEX_LABEL("EXTENSION, SIGNATURE ALGORITHMS", &buf[res], extension_len); + } else + if (extension_type == 0x2D) { + // psk key exchange modes + DEBUG_DUMP_HEX_LABEL("EXTENSION, PSK KEY EXCHANGE MODES", &buf[res], extension_len); + } +#endif + res += extension_len; + } + } + if (buf_len != res) + return TLS_NEED_MORE_DATA; + if ((context->is_server) && (cipher_buffer) && (cipher_len)) { + int cipher = tls_choose_cipher(context, cipher_buffer, cipher_len, &scsv_set); + if (cipher < 0) { + DEBUG_PRINT("NO COMMON CIPHERS\n"); + return cipher; + } + if ((downgraded) && (scsv_set)) { + DEBUG_PRINT("NO DOWNGRADE (SCSV SET)\n"); + _private_tls_write_packet(tls_build_alert(context, 1, inappropriate_fallback)); + context->critical_error = 1; + return TLS_NOT_SAFE; + } + context->cipher = cipher; + } +#ifdef WITH_TLS_13 + if (!context->is_server) { + if (!tls_cipher_supported(context, cipher)) { + context->cipher = 0; + DEBUG_PRINT("NO CIPHER SUPPORTED\n"); + return TLS_NO_COMMON_CIPHER; + } + DEBUG_PRINT("CIPHER: %s\n", tls_cipher_name(context)); + } + + if ((key_share) && (key_size) && ((context->version == TLS_V13) || (context->version == DTLS_V13))) { + int key_share_err = _private_tls_parse_key_share(context, key_share, key_size); + if (key_share_err) { + // request hello retry + if (context->connection_status != 4) { + *write_packets = 5; + context->hs_messages[1] = 0; + context->connection_status = 4; + return res; + } else + return key_share_err; + } + // we have key share + if (context->is_server) + context->connection_status = 3; + else + context->connection_status = 2; + } +#endif + return res; +} + +int tls_parse_certificate(struct TLSContext *context, const unsigned char *buf, int buf_len, int is_client) { + int res = 0; + CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA) + unsigned int size_of_all_certificates = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; + + if (size_of_all_certificates <= 4) + return 3 + size_of_all_certificates; + res += 3; + if (context->dtls) { + int dtls_check = _private_dtls_check_packet(buf, buf_len); + if (dtls_check < 0) + return dtls_check; + res += 8; + } +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + int context_size = buf[res]; + res++; + // must be 0 + if (context_size) + res += context_size; + } +#endif + + CHECK_SIZE(size_of_all_certificates, buf_len - res, TLS_NEED_MORE_DATA); + int size = size_of_all_certificates; + + int idx = 0; + int valid_certificate = 0; + while (size > 0) { + idx++; + CHECK_SIZE(3, buf_len - res, TLS_NEED_MORE_DATA); + unsigned int certificate_size = buf[res] * 0x10000 + buf[res + 1] * 0x100 + buf[res + 2]; + res += 3; + CHECK_SIZE(certificate_size, buf_len - res, TLS_NEED_MORE_DATA) + // load chain + int certificates_in_chain = 0; + int res2 = res; + unsigned int remaining = certificate_size; + do { + if (remaining <= 3) + break; + certificates_in_chain++; + unsigned int certificate_size2 = buf[res2] * 0x10000 + buf[res2 + 1] * 0x100 + buf[res2 + 2]; + res2 += 3; + remaining -= 3; + if (certificate_size2 > remaining) { + DEBUG_PRINT("Invalid certificate size (%i from %i bytes remaining)\n", certificate_size2, remaining); + break; + } + remaining -= certificate_size2; + + struct TLSCertificate *cert = asn1_parse(context, &buf[res2], certificate_size2, is_client); + if (cert) { + if (certificate_size2) { + cert->bytes = (unsigned char *)TLS_MALLOC(certificate_size2); + if (cert->bytes) { + cert->len = certificate_size2; + memcpy(cert->bytes, &buf[res2], certificate_size2); + } + } + // valid certificate + if (is_client) { + valid_certificate = 1; + context->client_certificates = (struct TLSCertificate **)TLS_REALLOC(context->client_certificates, (context->client_certificates_count + 1) * sizeof(struct TLSCertificate *)); + context->client_certificates[context->client_certificates_count] = cert; + context->client_certificates_count++; + } else { + context->certificates = (struct TLSCertificate **)TLS_REALLOC(context->certificates, (context->certificates_count + 1) * sizeof(struct TLSCertificate *)); + context->certificates[context->certificates_count] = cert; + context->certificates_count++; + if ((cert->pk) || (cert->priv)) + valid_certificate = 1; + else + if (!context->is_server) + valid_certificate = 1; + } + } + res2 += certificate_size2; +#ifdef WITH_TLS_13 + // extension + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + if (remaining >= 2) { + // ignore extensions + remaining -= 2; + unsigned short size = ntohs(*(unsigned short *)&buf[res2]); + if ((size) && (size >= remaining)) { + res2 += size; + remaining -= size; + } + } + } +#endif + } while (remaining > 0); + if (remaining) { + DEBUG_PRINT("Extra %i bytes after certificate\n", remaining); + } + size -= certificate_size + 3; + res += certificate_size; + } + if (!valid_certificate) + return TLS_UNSUPPORTED_CERTIFICATE; + if (res != buf_len) { + DEBUG_PRINT("Warning: %i bytes read from %i byte buffer\n", (int)res, (int)buf_len); + } + return res; +} + +int _private_tls_parse_dh(const unsigned char *buf, int buf_len, const unsigned char **out, int *out_size) { + int res = 0; + *out = NULL; + *out_size = 0; + CHECK_SIZE(2, buf_len, TLS_NEED_MORE_DATA) + unsigned short size = ntohs(*(unsigned short *)buf); + res += 2; + CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA) + DEBUG_DUMP_HEX(&buf[res], size); + *out = &buf[res]; + *out_size = size; + res += size; + return res; +} + +int _private_tls_parse_random(struct TLSContext *context, const unsigned char *buf, int buf_len) { + int res = 0; + int ephemeral = tls_cipher_is_ephemeral(context); + unsigned short size; + if (ephemeral == 2) { + CHECK_SIZE(1, buf_len, TLS_NEED_MORE_DATA) + size = buf[0]; + res += 1; + } else { + CHECK_SIZE(2, buf_len, TLS_NEED_MORE_DATA) + size = ntohs(*(unsigned short *)buf); + res += 2; + } + + CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA) + unsigned int out_len = 0; + unsigned char *random = NULL; + switch (ephemeral) { +#ifdef TLS_FORWARD_SECRECY + case 1: + random = _private_tls_decrypt_dhe(context, &buf[res], size, &out_len, 1); + break; + case 2: + random = _private_tls_decrypt_ecc_dhe(context, &buf[res], size, &out_len, 1); + break; +#endif + default: + random = _private_tls_decrypt_rsa(context, &buf[res], size, &out_len); + } + + if ((random) && (out_len > 2)) { + DEBUG_DUMP_HEX_LABEL("PRE MASTER KEY", random, out_len); + TLS_FREE(context->premaster_key); + context->premaster_key = random; + context->premaster_key_len = out_len; + _private_tls_compute_key(context, 48); + } else { + TLS_FREE(random); + return 0; + } + res += size; + return res; +} + +int _private_tls_build_random(struct TLSPacket *packet) { + int res = 0; + unsigned char rand_bytes[48]; + int bytes = 48; + if (!tls_random(rand_bytes, bytes)) + return TLS_GENERIC_ERROR; + + // max supported version + if (packet->context->is_server) + *(unsigned short *)rand_bytes = htons(packet->context->version); + else + if (packet->context->dtls) + *(unsigned short *)rand_bytes = htons(DTLS_V12); + else + *(unsigned short *)rand_bytes = htons(TLS_V12); + //DEBUG_DUMP_HEX_LABEL("PREMASTER KEY", rand_bytes, bytes); + + TLS_FREE(packet->context->premaster_key); + packet->context->premaster_key = (unsigned char *)TLS_MALLOC(bytes); + if (!packet->context->premaster_key) + return TLS_NO_MEMORY; + + packet->context->premaster_key_len = bytes; + memcpy(packet->context->premaster_key, rand_bytes, packet->context->premaster_key_len); + + unsigned int out_len; + unsigned char *random = _private_tls_encrypt_rsa(packet->context, packet->context->premaster_key, packet->context->premaster_key_len, &out_len); + + _private_tls_compute_key(packet->context, bytes); + if ((random) && (out_len > 2)) { + tls_packet_uint24(packet, out_len + 2); + if (packet->context->dtls) + _private_dtls_handshake_data(packet->context, packet, out_len + 2); + tls_packet_uint16(packet, out_len); + tls_packet_append(packet, random, out_len); + } else + res = TLS_GENERIC_ERROR; + TLS_FREE(random); + if (res) + return res; + + return out_len + 2; +} + +const unsigned char *_private_tls_parse_signature(struct TLSContext *context, const unsigned char *buf, int buf_len, int *hash_algorithm, int *sign_algorithm, int *sig_size, int *offset) { + int res = 0; + CHECK_SIZE(2, buf_len, NULL) + *hash_algorithm = _md5_sha1; + *sign_algorithm = rsa_sign; + *sig_size = 0; + if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { + *hash_algorithm = buf[res]; + res++; + *sign_algorithm = buf[res]; + res++; + } + unsigned short size = ntohs(*(unsigned short *)&buf[res]); + res += 2; + CHECK_SIZE(size, buf_len - res, NULL) + DEBUG_DUMP_HEX(&buf[res], size); + *sig_size = size; + *offset = res + size; + return &buf[res]; +} + +int tls_parse_server_key_exchange(struct TLSContext *context, const unsigned char *buf, int buf_len) { + int res = 0; + int dh_res = 0; + CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA) + unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; + res += 3; + if (context->dtls) { + int dtls_check = _private_dtls_check_packet(buf, buf_len); + if (dtls_check < 0) + return dtls_check; + res += 8; + } + const unsigned char *packet_ref = buf + res; + CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA); + + if (!size) + return res; + + unsigned char has_ds_params = 0; + unsigned int key_size = 0; +#ifdef TLS_FORWARD_SECRECY + const struct ECCCurveParameters *curve = NULL; + const unsigned char *pk_key = NULL; + int ephemeral = tls_cipher_is_ephemeral(context); + if (ephemeral) { + if (ephemeral == 1) { + has_ds_params = 1; + } else { + if (buf[res++] != 3) { + // named curve + // any other method is not supported + return 0; + } + CHECK_SIZE(3, buf_len - res, TLS_NEED_MORE_DATA); + int iana_n = ntohs(*(unsigned short *)&buf[res]); + res += 2; + key_size = buf[res]; + res++; + CHECK_SIZE(key_size, buf_len - res, TLS_NEED_MORE_DATA); + DEBUG_PRINT("IANA CURVE NUMBER: %i\n", iana_n); + switch (iana_n) { + case 19: + curve = &secp192r1; + break; + case 20: + curve = &secp224k1; + break; + case 21: + curve = &secp224r1; + break; + case 22: + curve = &secp256k1; + break; + case 23: + curve = &secp256r1; + break; + case 24: + curve = &secp384r1; + break; + case 25: + curve = &secp521r1; + break; +#ifdef TLS_CURVE25519 + case 29: + curve = &x25519; + break; +#endif + default: + DEBUG_PRINT("UNSUPPORTED CURVE\n"); + return TLS_GENERIC_ERROR; + } + pk_key = &buf[res]; + res += key_size; + context->curve = curve; + } + } +#endif + const unsigned char *dh_p = NULL; + int dh_p_len = 0; + const unsigned char *dh_g = NULL; + int dh_g_len = 0; + const unsigned char *dh_Ys = NULL; + int dh_Ys_len = 0; + if (has_ds_params) { + DEBUG_PRINT(" dh_p: "); + dh_res = _private_tls_parse_dh(&buf[res], buf_len - res, &dh_p, &dh_p_len); + if (dh_res <= 0) + return TLS_BROKEN_PACKET; + res += dh_res; + DEBUG_PRINT("\n"); + + DEBUG_PRINT(" dh_q: "); + dh_res = _private_tls_parse_dh(&buf[res], buf_len - res, &dh_g, &dh_g_len); + if (dh_res <= 0) + return TLS_BROKEN_PACKET; + res += dh_res; + DEBUG_PRINT("\n"); + + DEBUG_PRINT(" dh_Ys: "); + dh_res = _private_tls_parse_dh(&buf[res], buf_len - res, &dh_Ys, &dh_Ys_len); + if (dh_res <= 0) + return TLS_BROKEN_PACKET; + res += dh_res; + DEBUG_PRINT("\n"); + } + int sign_size; + int hash_algorithm; + int sign_algorithm; + int packet_size = res - 3; + if (context->dtls) + packet_size -= 8; + int offset = 0; + DEBUG_PRINT(" SIGNATURE (%i/%i/%i): ", packet_size, dh_res, key_size); + const unsigned char *signature = _private_tls_parse_signature(context, &buf[res], buf_len - res, &hash_algorithm, &sign_algorithm, &sign_size, &offset); + DEBUG_PRINT("\n"); + if ((sign_size <= 0) || (!signature)) + return TLS_BROKEN_PACKET; + res += offset; + // check signature + unsigned int message_len = packet_size + TLS_CLIENT_RANDOM_SIZE + TLS_SERVER_RANDOM_SIZE; + unsigned char *message = (unsigned char *)TLS_MALLOC(message_len); + if (message) { + memcpy(message, context->local_random, TLS_CLIENT_RANDOM_SIZE); + memcpy(message + TLS_CLIENT_RANDOM_SIZE, context->remote_random, TLS_SERVER_RANDOM_SIZE); + memcpy(message + TLS_CLIENT_RANDOM_SIZE + TLS_SERVER_RANDOM_SIZE, packet_ref, packet_size); +#ifdef TLS_CLIENT_ECDSA + if (tls_is_ecdsa(context)) { + if (_private_tls_verify_ecdsa(context, hash_algorithm, signature, sign_size, message, message_len, NULL) != 1) { + DEBUG_PRINT("ECC Server signature FAILED!\n"); + TLS_FREE(message); + return TLS_BROKEN_PACKET; + } + } else +#endif + { + if (_private_tls_verify_rsa(context, hash_algorithm, signature, sign_size, message, message_len) != 1) { + DEBUG_PRINT("Server signature FAILED!\n"); + TLS_FREE(message); + return TLS_BROKEN_PACKET; + } + } + TLS_FREE(message); + } + + if (buf_len - res) { + DEBUG_PRINT("EXTRA %i BYTES AT THE END OF MESSAGE\n", buf_len - res); + DEBUG_DUMP_HEX(&buf[res], buf_len - res); + DEBUG_PRINT("\n"); + } +#ifdef TLS_FORWARD_SECRECY + if (ephemeral == 1) { + _private_tls_dhe_create(context); + DEBUG_DUMP_HEX_LABEL("DHP", dh_p, dh_p_len); + DEBUG_DUMP_HEX_LABEL("DHG", dh_g, dh_g_len); + int dhe_key_size = dh_p_len; + if (dh_g_len > dh_p_len) + dhe_key_size = dh_g_len; + if (_private_tls_dh_make_key(dhe_key_size, context->dhe, (const char *)dh_p, (const char *)dh_g, dh_p_len, dh_g_len)) { + DEBUG_PRINT("ERROR CREATING DHE KEY\n"); + TLS_FREE(context->dhe); + context->dhe = NULL; + return TLS_GENERIC_ERROR; + } + + unsigned int dh_key_size = 0; + unsigned char *key = _private_tls_decrypt_dhe(context, dh_Ys, dh_Ys_len, &dh_key_size, 0); + DEBUG_DUMP_HEX_LABEL("DH COMMON SECRET", key, dh_key_size); + if ((key) && (dh_key_size)) { + TLS_FREE(context->premaster_key); + context->premaster_key = key; + context->premaster_key_len = dh_key_size; + } + } else + if ((ephemeral == 2) && (curve) && (pk_key) && (key_size)) { +#ifdef TLS_CURVE25519 + if (curve == &x25519) { + if (key_size != 32) { + DEBUG_PRINT("INVALID X25519 PUBLIC SIZE"); + return TLS_GENERIC_ERROR; + } + + TLS_FREE(context->client_secret); + context->client_secret = (unsigned char *)TLS_MALLOC(32); + if (!context->client_secret) { + DEBUG_PRINT("ERROR IN TLS_MALLOC"); + return TLS_GENERIC_ERROR; + } + + tls_random(context->client_secret, 32); + + context->client_secret[0] &= 248; + context->client_secret[31] &= 127; + context->client_secret[31] |= 64; + + TLS_FREE(context->premaster_key); + context->premaster_key = (unsigned char *)TLS_MALLOC(32); + if (!context->premaster_key) + return TLS_GENERIC_ERROR; + + curve25519(context->premaster_key, context->client_secret, pk_key); + context->premaster_key_len = 32; + } else +#endif + { + tls_init(); + _private_tls_ecc_dhe_create(context); + + ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&curve->dp; + if (ecc_make_key_ex(NULL, find_prng("sprng"), context->ecc_dhe, dp)) { + TLS_FREE(context->ecc_dhe); + context->ecc_dhe = NULL; + DEBUG_PRINT("Error generating ECC key\n"); + return TLS_GENERIC_ERROR; + } + + TLS_FREE(context->premaster_key); + context->premaster_key_len = 0; + + unsigned int out_len = 0; + context->premaster_key = _private_tls_decrypt_ecc_dhe(context, pk_key, key_size, &out_len, 0); + if (context->premaster_key) + context->premaster_key_len = out_len; + } + } +#endif + return res; +} + +int tls_parse_client_key_exchange(struct TLSContext *context, const unsigned char *buf, int buf_len) { + if (context->connection_status != 1) { + DEBUG_PRINT("UNEXPECTED CLIENT KEY EXCHANGE MESSAGE (connections status: %i)\n", (int)context->connection_status); + return TLS_UNEXPECTED_MESSAGE; + } + + int res = 0; + int dh_res = 0; + CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA) + + unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; + res += 3; + if (context->dtls) { + int dtls_check = _private_dtls_check_packet(buf, buf_len); + if (dtls_check < 0) + return dtls_check; + res += 8; + } + + CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA); + + if (!size) + return res; + + dh_res = _private_tls_parse_random(context, &buf[res], size); + if (dh_res <= 0) { + DEBUG_PRINT("broken key\n"); + return TLS_BROKEN_PACKET; + } + DEBUG_PRINT("\n"); + + res += size; + context->connection_status = 2; + return res; +} + +int tls_parse_server_hello_done(struct TLSContext *context, const unsigned char *buf, int buf_len) { + int res = 0; + CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA) + + unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; + res += 3; + if (context->dtls) { + int dtls_check = _private_dtls_check_packet(buf, buf_len); + if (dtls_check < 0) + return dtls_check; + res += 8; + } + + CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA); + + res += size; + return res; +} + +int tls_parse_finished(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets) { + if ((context->connection_status < 2) || (context->connection_status == 0xFF)) { + DEBUG_PRINT("UNEXPECTED FINISHED MESSAGE\n"); + return TLS_UNEXPECTED_MESSAGE; + } + + int res = 0; + *write_packets = 0; + CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA) + + unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; + res += 3; + if (context->dtls) { + int dtls_check = _private_dtls_check_packet(buf, buf_len); + if (dtls_check < 0) + return dtls_check; + res += 8; + } + + if (size < TLS_MIN_FINISHED_OPAQUE_LEN) { + DEBUG_PRINT("Invalid finished pachet size: %i\n", size); + return TLS_BROKEN_PACKET; + } + + CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA); + + unsigned char hash[TLS_MAX_SHA_SIZE]; + unsigned int hash_len = _private_tls_get_hash(context, hash); + +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + unsigned char hash_out[TLS_MAX_SHA_SIZE]; + unsigned long out_size = TLS_MAX_SHA_SIZE; + if ((!context->remote_finished_key) || (!hash_len)) { + DEBUG_PRINT("NO FINISHED KEY COMPUTED OR NO HANDSHAKE HASH\n"); + return TLS_NOT_VERIFIED; + } + + DEBUG_DUMP_HEX_LABEL("HS HASH", hash, hash_len); + DEBUG_DUMP_HEX_LABEL("HS FINISH", context->finished_key, hash_len); + DEBUG_DUMP_HEX_LABEL("HS REMOTE FINISH", context->remote_finished_key, hash_len); + + out_size = hash_len; + hmac_state hmac; + hmac_init(&hmac, _private_tls_get_hash_idx(context), context->remote_finished_key, hash_len); + hmac_process(&hmac, hash, hash_len); + hmac_done(&hmac, hash_out, &out_size); + + if ((size != out_size) || (memcmp(hash_out, &buf[res], size))) { + DEBUG_PRINT("Finished validation error (sequence number, local: %i, remote: %i)\n", (int)context->local_sequence_number, (int)context->remote_sequence_number); + DEBUG_DUMP_HEX_LABEL("FINISHED OPAQUE", &buf[res], size); + DEBUG_DUMP_HEX_LABEL("VERIFY", hash_out, out_size); + return TLS_NOT_VERIFIED; + } + if (context->is_server) { + context->connection_status = 0xFF; + res += size; + _private_tls13_key(context, 0); + context->local_sequence_number = 0; + context->remote_sequence_number = 0; + return res; + } + } else +#endif + { + // verify + unsigned char *out = (unsigned char *)TLS_MALLOC(size); + if (!out) { + DEBUG_PRINT("Error in TLS_MALLOC (%i bytes)\n", (int)size); + return TLS_NO_MEMORY; + } + + // server verifies client's message + if (context->is_server) + _private_tls_prf(context, out, size, context->master_key, context->master_key_len, (unsigned char *)"client finished", 15, hash, hash_len, NULL, 0); + else + _private_tls_prf(context, out, size, context->master_key, context->master_key_len, (unsigned char *)"server finished", 15, hash, hash_len, NULL, 0); + + if (memcmp(out, &buf[res], size)) { + TLS_FREE(out); + DEBUG_PRINT("Finished validation error (sequence number, local: %i, remote: %i)\n", (int)context->local_sequence_number, (int)context->remote_sequence_number); + DEBUG_DUMP_HEX_LABEL("FINISHED OPAQUE", &buf[res], size); + DEBUG_DUMP_HEX_LABEL("VERIFY", out, size); + return TLS_NOT_VERIFIED; + } +#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION + if (size) { + if (context->is_server) { + TLS_FREE(context->verify_data); + context->verify_data = (unsigned char *)TLS_MALLOC(size); + if (context->verify_data) { + memcpy(context->verify_data, out, size); + context->verify_len = size; + } + } else { + // concatenate client verify and server verify + context->verify_data = (unsigned char *)TLS_REALLOC(context->verify_data, size); + if (context->verify_data) { + memcpy(context->verify_data + context->verify_len, out, size); + context->verify_len += size; + } else + context->verify_len = 0; + } + } +#endif + TLS_FREE(out); + } + if (context->is_server) + *write_packets = 3; + else + context->connection_status = 0xFF; + res += size; + return res; +} + +#ifdef WITH_TLS_13 +int tls_parse_verify_tls13(struct TLSContext *context, const unsigned char *buf, int buf_len) { + CHECK_SIZE(7, buf_len, TLS_NEED_MORE_DATA) + unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; + + if (size < 2) + return buf_len; + + unsigned char signing_data[TLS_MAX_HASH_SIZE + 98]; + int signing_data_len; + + // first 64 bytes to 0x20 (32) + memset(signing_data, 0x20, 64); + // context string 33 bytes + if (context->is_server) + memcpy(signing_data + 64, "TLS 1.3, server CertificateVerify", 33); + else + memcpy(signing_data + 64, "TLS 1.3, client CertificateVerify", 33); + // a single 0 byte separator + signing_data[97] = 0; + signing_data_len = 98; + + signing_data_len += _private_tls_get_hash(context, signing_data + 98); + DEBUG_DUMP_HEX_LABEL("signature data", signing_data, signing_data_len); + unsigned short signature = ntohs(*(unsigned short *)&buf[3]); + unsigned short signature_size = ntohs(*(unsigned short *)&buf[5]); + int valid = 0; + CHECK_SIZE(7 + size, buf_len, TLS_NEED_MORE_DATA) + switch (signature) { +#ifdef TLS_ECDSA_SUPPORTED + case 0x0403: + // secp256r1 + sha256 + valid = _private_tls_verify_ecdsa(context, sha256, buf + 7, signature_size, signing_data, signing_data_len, &secp256r1); + break; + case 0x0503: + // secp384r1 + sha384 + valid = _private_tls_verify_ecdsa(context, sha384, buf + 7, signature_size, signing_data, signing_data_len, &secp384r1); + break; + case 0x0603: + // secp521r1 + sha512 + valid = _private_tls_verify_ecdsa(context, sha512, buf + 7, signature_size, signing_data, signing_data_len, &secp521r1); + break; +#endif + case 0x0804: + valid = _private_tls_verify_rsa(context, sha256, buf + 7, signature_size, signing_data, signing_data_len); + break; + default: + DEBUG_PRINT("Unsupported signature: %x\n", (int)signature); + return TLS_UNSUPPORTED_CERTIFICATE; + } + if (valid != 1) { + DEBUG_PRINT("Signature FAILED!\n"); + return TLS_DECRYPTION_FAILED; + } + return buf_len; +} +#endif + +int tls_parse_verify(struct TLSContext *context, const unsigned char *buf, int buf_len) { +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + return tls_parse_verify_tls13(context, buf, buf_len); +#endif + CHECK_SIZE(7, buf_len, TLS_BAD_CERTIFICATE) + unsigned int bytes_to_follow = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; + CHECK_SIZE(bytes_to_follow, buf_len - 3, TLS_BAD_CERTIFICATE) + int res = -1; + + if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { + unsigned int hash = buf[3]; + unsigned int algorithm = buf[4]; + if (algorithm != rsa) + return TLS_UNSUPPORTED_CERTIFICATE; + unsigned short size = ntohs(*(unsigned short *)&buf[5]); + CHECK_SIZE(size, bytes_to_follow - 4, TLS_BAD_CERTIFICATE) + DEBUG_PRINT("ALGORITHM %i/%i (%i)\n", hash, algorithm, (int)size); + DEBUG_DUMP_HEX_LABEL("VERIFY", &buf[7], bytes_to_follow - 7); + + res = _private_tls_verify_rsa(context, hash, &buf[7], size, context->cached_handshake, context->cached_handshake_len); + } else { +#ifdef TLS_LEGACY_SUPPORT + unsigned short size = ntohs(*(unsigned short *)&buf[3]); + CHECK_SIZE(size, bytes_to_follow - 2, TLS_BAD_CERTIFICATE) + res = _private_tls_verify_rsa(context, md5, &buf[5], size, context->cached_handshake, context->cached_handshake_len); +#endif + } + if (context->cached_handshake) { + // not needed anymore + TLS_FREE(context->cached_handshake); + context->cached_handshake = NULL; + context->cached_handshake_len = 0; + } + if (res == 1) { + DEBUG_PRINT("Signature OK\n"); + context->client_verified = 1; + } else { + DEBUG_PRINT("Signature FAILED\n"); + context->client_verified = 0; + } + return 1; +} + +int tls_parse_payload(struct TLSContext *context, const unsigned char *buf, int buf_len, tls_validation_function certificate_verify) { + int orig_len = buf_len; + if (context->connection_status == 0xFF) { +#ifndef TLS_ACCEPT_SECURE_RENEGOTIATION + // renegotiation disabled (emit warning alert) + _private_tls_write_packet(tls_build_alert(context, 0, no_renegotiation)); + return 1; +#endif + } + + while ((buf_len >= 4) && (!context->critical_error)) { + int payload_res = 0; + unsigned char update_hash = 1; + CHECK_SIZE(1, buf_len, TLS_NEED_MORE_DATA) + unsigned char type = buf[0]; + unsigned int write_packets = 0; + unsigned int dtls_cookie_verified = 0; + int certificate_verify_alert = no_error; + unsigned int payload_size = buf[1] * 0x10000 + buf[2] * 0x100 + buf[3] + 3; + if (context->dtls) + payload_size += 8; + CHECK_SIZE(payload_size + 1, buf_len, TLS_NEED_MORE_DATA) + switch (type) { + // hello request + case 0x00: + CHECK_HANDSHAKE_STATE(context, 0, 1); + DEBUG_PRINT(" => HELLO REQUEST (RENEGOTIATION?)\n"); + if (context->dtls) + context->dtls_seq = 0; + if (context->is_server) + payload_res = TLS_UNEXPECTED_MESSAGE; + else { + if (context->connection_status == 0xFF) { + // renegotiation +#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION + if (context->critical_error) + payload_res = TLS_UNEXPECTED_MESSAGE; + else { + _private_tls_reset_context(context); + _private_tls_write_packet(tls_build_hello(context, 0)); + return 1; + } +#else + payload_res = TLS_NO_RENEGOTIATION; +#endif + } else + payload_res = TLS_UNEXPECTED_MESSAGE; + } + // no payload + break; + // client hello + case 0x01: + CHECK_HANDSHAKE_STATE(context, 1, (context->dtls ? 2 : 1)); + DEBUG_PRINT(" => CLIENT HELLO\n"); + if (context->is_server) { + payload_res = tls_parse_hello(context, buf + 1, payload_size, &write_packets, &dtls_cookie_verified); + DEBUG_PRINT(" => DTLS COOKIE VERIFIED: %i (%i)\n", dtls_cookie_verified, payload_res); + if ((context->dtls) && (payload_res > 0) && (!dtls_cookie_verified) && (context->connection_status == 1)) { + // wait client hello + context->connection_status = 3; + update_hash = 0; + } + } else + payload_res = TLS_UNEXPECTED_MESSAGE; + break; + // server hello + case 0x02: + CHECK_HANDSHAKE_STATE(context, 2, 1); + DEBUG_PRINT(" => SERVER HELLO\n"); + if (context->is_server) + payload_res = TLS_UNEXPECTED_MESSAGE; + else + payload_res = tls_parse_hello(context, buf + 1, payload_size, &write_packets, &dtls_cookie_verified); + break; + // hello verify request + case 0x03: + DEBUG_PRINT(" => VERIFY REQUEST\n"); + CHECK_HANDSHAKE_STATE(context, 3, 1); + if ((context->dtls) && (!context->is_server)) { + payload_res = tls_parse_verify_request(context, buf + 1, payload_size, &write_packets); + update_hash = 0; + } else + payload_res = TLS_UNEXPECTED_MESSAGE; + break; + // certificate + case 0x0B: + CHECK_HANDSHAKE_STATE(context, 4, 1); + DEBUG_PRINT(" => CERTIFICATE\n"); +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + if (context->connection_status == 2) { + payload_res = tls_parse_certificate(context, buf + 1, payload_size, context->is_server); + if (context->is_server) { + if ((certificate_verify) && (context->client_certificates_count)) + certificate_verify_alert = certificate_verify(context, context->client_certificates, context->client_certificates_count); + // empty certificates are permitted for client + if (payload_res <= 0) + payload_res = 1; + } else { + if ((certificate_verify) && (context->certificates_count)) + certificate_verify_alert = certificate_verify(context, context->certificates, context->certificates_count); + } + } else + payload_res = TLS_UNEXPECTED_MESSAGE; + } else +#endif + if (context->connection_status == 1) { + if (context->is_server) { + // client certificate + payload_res = tls_parse_certificate(context, buf + 1, payload_size, 1); + if ((certificate_verify) && (context->client_certificates_count)) + certificate_verify_alert = certificate_verify(context, context->client_certificates, context->client_certificates_count); + // empty certificates are permitted for client + if (payload_res <= 0) + payload_res = 1; + } else { + payload_res = tls_parse_certificate(context, buf + 1, payload_size, 0); + if ((certificate_verify) && (context->certificates_count)) + certificate_verify_alert = certificate_verify(context, context->certificates, context->certificates_count); + } + } else + payload_res = TLS_UNEXPECTED_MESSAGE; + break; + // server key exchange + case 0x0C: + CHECK_HANDSHAKE_STATE(context, 5, 1); + DEBUG_PRINT(" => SERVER KEY EXCHANGE\n"); + if (context->is_server) + payload_res = TLS_UNEXPECTED_MESSAGE; + else + payload_res = tls_parse_server_key_exchange(context, buf + 1, payload_size); + break; + // certificate request + case 0x0D: + CHECK_HANDSHAKE_STATE(context, 6, 1); + // server to client + if (context->is_server) + payload_res = TLS_UNEXPECTED_MESSAGE; + else + context->client_verified = 2; + DEBUG_PRINT(" => CERTIFICATE REQUEST\n"); + break; + // server hello done + case 0x0E: + CHECK_HANDSHAKE_STATE(context, 7, 1); + DEBUG_PRINT(" => SERVER HELLO DONE\n"); + if (context->is_server) { + payload_res = TLS_UNEXPECTED_MESSAGE; + } else { + payload_res = tls_parse_server_hello_done(context, buf + 1, payload_size); + if (payload_res > 0) + write_packets = 1; + } + break; + // certificate verify + case 0x0F: + CHECK_HANDSHAKE_STATE(context, 8, 1); + DEBUG_PRINT(" => CERTIFICATE VERIFY\n"); + if (context->connection_status == 2) + payload_res = tls_parse_verify(context, buf + 1, payload_size); + else + payload_res = TLS_UNEXPECTED_MESSAGE; + break; + // client key exchange + case 0x10: + CHECK_HANDSHAKE_STATE(context, 9, 1); + DEBUG_PRINT(" => CLIENT KEY EXCHANGE\n"); + if (context->is_server) + payload_res = tls_parse_client_key_exchange(context, buf + 1, payload_size); + else + payload_res = TLS_UNEXPECTED_MESSAGE; + break; + // finished + case 0x14: + if (context->cached_handshake) { + TLS_FREE(context->cached_handshake); + context->cached_handshake = NULL; + context->cached_handshake_len = 0; + } + CHECK_HANDSHAKE_STATE(context, 10, 1); + DEBUG_PRINT(" => FINISHED\n"); + payload_res = tls_parse_finished(context, buf + 1, payload_size, &write_packets); + if (payload_res > 0) + memset(context->hs_messages, 0, sizeof(context->hs_messages)); + #ifdef WITH_TLS_13 + if ((!context->is_server) && ((context->version == TLS_V13) || (context->version == DTLS_V13))) { + update_hash = 0; + DEBUG_PRINT("<= SENDING FINISHED\n"); + _private_tls_update_hash(context, buf, payload_size + 1); + _private_tls_write_packet(tls_build_finished(context)); + _private_tls13_key(context, 0); + context->connection_status = 0xFF; + context->local_sequence_number = 0; + context->remote_sequence_number = 0; + } +#endif + break; +#ifdef WITH_TLS_13 + case 0x08: + // encrypted extensions ... ignore it for now + break; +#endif + default: + DEBUG_PRINT(" => NOT UNDERSTOOD PAYLOAD TYPE: %x\n", (int)type); + return TLS_NOT_UNDERSTOOD; + } + if ((type != 0x00) && (update_hash)) + _private_tls_update_hash(context, buf, payload_size + 1); + + if (certificate_verify_alert != no_error) { + _private_tls_write_packet(tls_build_alert(context, 1, certificate_verify_alert)); + context->critical_error = 1; + } + + if (payload_res < 0) { + switch (payload_res) { + case TLS_UNEXPECTED_MESSAGE: + _private_tls_write_packet(tls_build_alert(context, 1, unexpected_message)); + break; + case TLS_COMPRESSION_NOT_SUPPORTED: + _private_tls_write_packet(tls_build_alert(context, 1, decompression_failure)); + break; + case TLS_BROKEN_PACKET: + _private_tls_write_packet(tls_build_alert(context, 1, decode_error)); + break; + case TLS_NO_MEMORY: + _private_tls_write_packet(tls_build_alert(context, 1, internal_error)); + break; + case TLS_NOT_VERIFIED: + _private_tls_write_packet(tls_build_alert(context, 1, bad_record_mac)); + break; + case TLS_BAD_CERTIFICATE: + if (context->is_server) { + // bad client certificate, continue + _private_tls_write_packet(tls_build_alert(context, 0, bad_certificate)); + payload_res = 0; + } else + _private_tls_write_packet(tls_build_alert(context, 1, bad_certificate)); + break; + case TLS_UNSUPPORTED_CERTIFICATE: + _private_tls_write_packet(tls_build_alert(context, 1, unsupported_certificate)); + break; + case TLS_NO_COMMON_CIPHER: + _private_tls_write_packet(tls_build_alert(context, 1, insufficient_security)); + break; + case TLS_NOT_UNDERSTOOD: + _private_tls_write_packet(tls_build_alert(context, 1, internal_error)); + break; + case TLS_NO_RENEGOTIATION: + _private_tls_write_packet(tls_build_alert(context, 0, no_renegotiation)); + payload_res = 0; + break; + case TLS_DECRYPTION_FAILED: + _private_tls_write_packet(tls_build_alert(context, 1, decryption_failed_RESERVED)); + break; + } + if (payload_res < 0) + return payload_res; + } + if (certificate_verify_alert != no_error) + payload_res = TLS_BAD_CERTIFICATE; + + // except renegotiation + switch (write_packets) { + case 1: + if (context->client_verified == 2) { + DEBUG_PRINT("<= Building CERTIFICATE \n"); + _private_tls_write_packet(tls_build_certificate(context)); + context->client_verified = 0; + } + // client handshake + DEBUG_PRINT("<= Building KEY EXCHANGE\n"); + _private_tls_write_packet(tls_build_client_key_exchange(context)); + DEBUG_PRINT("<= Building CHANGE CIPHER SPEC\n"); + _private_tls_write_packet(tls_build_change_cipher_spec(context)); + context->cipher_spec_set = 1; + context->local_sequence_number = 0; + DEBUG_PRINT("<= Building CLIENT FINISHED\n"); + _private_tls_write_packet(tls_build_finished(context)); + context->cipher_spec_set = 0; +#ifdef TLS_12_FALSE_START + if ((!context->is_server) && (context->version == TLS_V12)) { + // https://tools.ietf.org/html/rfc7918 + // 5.1. Symmetric Cipher + // Clients MUST NOT use the False Start protocol modification in a + // handshake unless the cipher suite uses a symmetric cipher that is + // considered cryptographically strong. + switch (context->cipher) { + case TLS_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + context->false_start = 1; + break; + } + } +#endif + break; + case 2: + // server handshake + if ((context->dtls) && (dtls_cookie_verified == 0)) { + _private_tls_write_packet(tls_build_verify_request(context)); + _private_dtls_reset(context); + } else { + DEBUG_PRINT("<= SENDING SERVER HELLO\n"); +#ifdef WITH_TLS_13 + if (context->connection_status == 3) { + context->connection_status = 2; + _private_tls_write_packet(tls_build_hello(context, 0)); + _private_tls_write_packet(tls_build_change_cipher_spec(context)); + _private_tls13_key(context, 1); + context->cipher_spec_set = 1; + DEBUG_PRINT("<= SENDING ENCRYPTED EXTENSIONS\n"); + _private_tls_write_packet(tls_build_encrypted_extensions(context)); + if (context->request_client_certificate) { + DEBUG_PRINT("<= SENDING CERTIFICATE REQUEST\n"); + _private_tls_write_packet(tls_certificate_request(context)); + } + DEBUG_PRINT("<= SENDING CERTIFICATE\n"); + _private_tls_write_packet(tls_build_certificate(context)); + DEBUG_PRINT("<= SENDING CERTIFICATE VERIFY\n"); + _private_tls_write_packet(tls_build_certificate_verify(context)); + DEBUG_PRINT("<= SENDING FINISHED\n"); + _private_tls_write_packet(tls_build_finished(context)); + // new key + TLS_FREE(context->server_finished_hash); + context->server_finished_hash = (unsigned char *)TLS_MALLOC(_private_tls_mac_length(context)); + if (context->server_finished_hash) + _private_tls_get_hash(context, context->server_finished_hash); + break; + } +#endif + _private_tls_write_packet(tls_build_hello(context, 0)); + DEBUG_PRINT("<= SENDING CERTIFICATE\n"); + _private_tls_write_packet(tls_build_certificate(context)); + int ephemeral_cipher = tls_cipher_is_ephemeral(context); + if (ephemeral_cipher) { + DEBUG_PRINT("<= SENDING EPHEMERAL DH KEY\n"); + _private_tls_write_packet(tls_build_server_key_exchange(context, ephemeral_cipher == 1 ? KEA_dhe_rsa : KEA_ec_diffie_hellman)); + } + if (context->request_client_certificate) { + DEBUG_PRINT("<= SENDING CERTIFICATE REQUEST\n"); + _private_tls_write_packet(tls_certificate_request(context)); + } + DEBUG_PRINT("<= SENDING DONE\n"); + _private_tls_write_packet(tls_build_done(context)); + } + break; + case 3: + // finished + _private_tls_write_packet(tls_build_change_cipher_spec(context)); + _private_tls_write_packet(tls_build_finished(context)); + context->connection_status = 0xFF; + break; + case 4: + // dtls only + context->dtls_seq = 1; + _private_tls_write_packet(tls_build_hello(context, 0)); + break; +#ifdef WITH_TLS_13 + case 5: + // hello retry request + DEBUG_PRINT("<= SENDING HELLO RETRY REQUEST\n"); + _private_tls_write_packet(tls_build_hello(context, 0)); + break; +#endif + } + payload_size++; + buf += payload_size; + buf_len -= payload_size; + } + return orig_len; +} + +unsigned int _private_tls_hmac_message(unsigned char local, struct TLSContext *context, const unsigned char *buf, int buf_len, const unsigned char *buf2, int buf_len2, unsigned char *out, unsigned int outlen, uint64_t remote_sequence_number) { + hmac_state hash; + int mac_size = outlen; + int hash_idx; + if (mac_size == TLS_SHA1_MAC_SIZE) + hash_idx = find_hash("sha1"); + else + if (mac_size == TLS_SHA384_MAC_SIZE) + hash_idx = find_hash("sha384"); + else + hash_idx = find_hash("sha256"); + + if (hmac_init(&hash, hash_idx, local ? context->crypto.ctx_local_mac.local_mac : context->crypto.ctx_remote_mac.remote_mac, mac_size)) + return 0; + + uint64_t squence_number; + if (context->dtls) + squence_number = htonll(remote_sequence_number); + else + if (local) + squence_number = htonll(context->local_sequence_number); + else + squence_number = htonll(context->remote_sequence_number); + + if (hmac_process(&hash, (unsigned char *)&squence_number, sizeof(uint64_t))) + return 0; + + if (hmac_process(&hash, buf, buf_len)) + return 0; + if ((buf2) && (buf_len2)) { + if (hmac_process(&hash, buf2, buf_len2)) + return 0; + } + unsigned long ref_outlen = outlen; + if (hmac_done(&hash, out, &ref_outlen)) + return 0; + + return (unsigned int)ref_outlen; +} + +int tls_parse_message(struct TLSContext *context, unsigned char *buf, int buf_len, tls_validation_function certificate_verify) { + int res = 5; + if (context->dtls) + res = 13; + int header_size = res; + int payload_res = 0; + + CHECK_SIZE(res, buf_len, TLS_NEED_MORE_DATA) + + unsigned char type = *buf; + + int buf_pos = 1; + + unsigned short version = ntohs(*(unsigned short *)&buf[buf_pos]); + buf_pos += 2; + + uint64_t dtls_sequence_number = 0; + if (context->dtls) { + CHECK_SIZE(buf_pos + 8, buf_len, TLS_NEED_MORE_DATA) + dtls_sequence_number = ntohll(*(uint64_t *)&buf[buf_pos]); + buf_pos += 8; + } + + VERSION_SUPPORTED(version, TLS_NOT_SAFE) + unsigned short length; + length = ntohs(*(unsigned short *)&buf[buf_pos]); + buf_pos += 2; + + unsigned char *pt = NULL; + const unsigned char *ptr = buf + buf_pos; + + CHECK_SIZE(buf_pos + length, buf_len, TLS_NEED_MORE_DATA) + DEBUG_PRINT("Message type: %0x, length: %i\n", (int)type, (int)length); + if ((context->cipher_spec_set) && (type != TLS_CHANGE_CIPHER)) { + DEBUG_DUMP_HEX_LABEL("encrypted", &buf[header_size], length); + if (!context->crypto.created) { + DEBUG_PRINT("Encryption context not created\n"); + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + return TLS_BROKEN_PACKET; + } + pt = (unsigned char *)TLS_MALLOC(length); + if (!pt) { + DEBUG_PRINT("Error in TLS_MALLOC (%i bytes)\n", (int)length); + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + return TLS_NO_MEMORY; + } + + unsigned char aad[16]; + int aad_size = sizeof(aad); + unsigned char *sequence = aad; + + if (context->crypto.created == 2) { + int delta = 8; + int pt_length; + unsigned char iv[TLS_13_AES_GCM_IV_LENGTH]; + gcm_reset(&context->crypto.ctx_remote.aes_gcm_remote); + +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + aad[0] = TLS_APPLICATION_DATA; + aad[1] = 0x03; + aad[2] = 0x03; + *((unsigned short *)(aad + 3)) = htons(buf_len - header_size); + aad_size = 5; + sequence = aad + 5; + if (context->dtls) + *((uint64_t *)sequence) = *(uint64_t *)(buf + 3); + else + *((uint64_t *)sequence) = htonll(context->remote_sequence_number); + memcpy(iv, context->crypto.ctx_remote_mac.remote_iv, TLS_13_AES_GCM_IV_LENGTH); + int i; + int offset = TLS_13_AES_GCM_IV_LENGTH - 8; + for (i = 0; i < 8; i++) + iv[offset + i] = context->crypto.ctx_remote_mac.remote_iv[offset + i] ^ sequence[i]; + pt_length = buf_len - header_size - TLS_GCM_TAG_LEN; + delta = 0; + } else { +#endif + aad_size = 13; + pt_length = length - 8 - TLS_GCM_TAG_LEN; + // build aad and iv + if (context->dtls) + *((uint64_t *)aad) = htonll(dtls_sequence_number); + else + *((uint64_t *)aad) = htonll(context->remote_sequence_number); + aad[8] = buf[0]; + aad[9] = buf[1]; + aad[10] = buf[2]; + + memcpy(iv, context->crypto.ctx_remote_mac.remote_aead_iv, 4); + memcpy(iv + 4, buf + header_size, 8); + *((unsigned short *)(aad + 11)) = htons((unsigned short)pt_length); +#ifdef WITH_TLS_13 + } +#endif + if (pt_length < 0) { + DEBUG_PRINT("Invalid packet length"); + TLS_FREE(pt); + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + return TLS_BROKEN_PACKET; + } + DEBUG_DUMP_HEX_LABEL("aad", aad, aad_size); + DEBUG_DUMP_HEX_LABEL("aad iv", iv, 12); + + int res0 = gcm_add_iv(&context->crypto.ctx_remote.aes_gcm_remote, iv, 12); + int res1 = gcm_add_aad(&context->crypto.ctx_remote.aes_gcm_remote, aad, aad_size); + memset(pt, 0, length); + DEBUG_PRINT("PT SIZE: %i\n", pt_length); + int res2 = gcm_process(&context->crypto.ctx_remote.aes_gcm_remote, pt, pt_length, buf + header_size + delta, GCM_DECRYPT); + unsigned char tag[32]; + unsigned long taglen = 32; + int res3 = gcm_done(&context->crypto.ctx_remote.aes_gcm_remote, tag, &taglen); + if ((res0) || (res1) || (res2) || (res3) || (taglen != TLS_GCM_TAG_LEN)) { + DEBUG_PRINT("ERROR: gcm_add_iv: %i, gcm_add_aad: %i, gcm_process: %i, gcm_done: %i\n", res0, res1, res2, res3); + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + return TLS_BROKEN_PACKET; + } + DEBUG_DUMP_HEX_LABEL("decrypted", pt, pt_length); + DEBUG_DUMP_HEX_LABEL("tag", tag, taglen); + // check tag + if (memcmp(buf + header_size + delta + pt_length, tag, taglen)) { + DEBUG_PRINT("INTEGRITY CHECK FAILED (msg length %i)\n", pt_length); + DEBUG_DUMP_HEX_LABEL("TAG RECEIVED", buf + header_size + delta + pt_length, taglen); + DEBUG_DUMP_HEX_LABEL("TAG COMPUTED", tag, taglen); + TLS_FREE(pt); + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + _private_tls_write_packet(tls_build_alert(context, 1, bad_record_mac)); + return TLS_INTEGRITY_FAILED; + } + ptr = pt; + length = (unsigned short)pt_length; +#ifdef TLS_WITH_CHACHA20_POLY1305 + } else + if (context->crypto.created == 3) { + int pt_length = length - POLY1305_TAGLEN; + unsigned int counter = 1; + unsigned char poly1305_key[POLY1305_KEYLEN]; + unsigned char trail[16]; + unsigned char mac_tag[POLY1305_TAGLEN]; + aad_size = 16; + if (pt_length < 0) { + DEBUG_PRINT("Invalid packet length"); + TLS_FREE(pt); + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + return TLS_BROKEN_PACKET; + } +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + aad[0] = TLS_APPLICATION_DATA; + aad[1] = 0x03; + aad[2] = 0x03; + *((unsigned short *)(aad + 3)) = htons(buf_len - header_size); + aad_size = 5; + sequence = aad + 5; + if (context->dtls) + *((uint64_t *)sequence) = *(uint64_t *)(buf + 3); + else + *((uint64_t *)sequence) = htonll(context->remote_sequence_number); + } else { +#endif + if (context->dtls) + *((uint64_t *)aad) = htonll(dtls_sequence_number); + else + *((uint64_t *)aad) = htonll(context->remote_sequence_number); + aad[8] = buf[0]; + aad[9] = buf[1]; + aad[10] = buf[2]; + *((unsigned short *)(aad + 11)) = htons((unsigned short)pt_length); + aad[13] = 0; + aad[14] = 0; + aad[15] = 0; +#ifdef WITH_TLS_13 + } +#endif + + chacha_ivupdate(&context->crypto.ctx_remote.chacha_remote, context->crypto.ctx_remote_mac.remote_aead_iv, sequence, (unsigned char *)&counter); + + chacha_encrypt_bytes(&context->crypto.ctx_remote.chacha_remote, buf + header_size, pt, pt_length); + DEBUG_DUMP_HEX_LABEL("decrypted", pt, pt_length); + ptr = pt; + length = (unsigned short)pt_length; + + chacha20_poly1305_key(&context->crypto.ctx_remote.chacha_remote, poly1305_key); + poly1305_context ctx; + _private_tls_poly1305_init(&ctx, poly1305_key); + _private_tls_poly1305_update(&ctx, aad, aad_size); + static unsigned char zeropad[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int rem = aad_size % 16; + if (rem) + _private_tls_poly1305_update(&ctx, zeropad, 16 - rem); + _private_tls_poly1305_update(&ctx, buf + header_size, pt_length); + rem = pt_length % 16; + if (rem) + _private_tls_poly1305_update(&ctx, zeropad, 16 - rem); + + _private_tls_U32TO8(&trail[0], aad_size == 5 ? 5 : 13); + *(int *)&trail[4] = 0; + _private_tls_U32TO8(&trail[8], pt_length); + *(int *)&trail[12] = 0; + + _private_tls_poly1305_update(&ctx, trail, 16); + _private_tls_poly1305_finish(&ctx, mac_tag); + if (memcmp(mac_tag, buf + header_size + pt_length, POLY1305_TAGLEN)) { + DEBUG_PRINT("INTEGRITY CHECK FAILED (msg length %i)\n", length); + DEBUG_DUMP_HEX_LABEL("POLY1305 TAG RECEIVED", buf + header_size + pt_length, POLY1305_TAGLEN); + DEBUG_DUMP_HEX_LABEL("POLY1305 TAG COMPUTED", mac_tag, POLY1305_TAGLEN); + TLS_FREE(pt); + + // silently ignore packet for DTLS + if (context->dtls) + return header_size + length; + + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + _private_tls_write_packet(tls_build_alert(context, 1, bad_record_mac)); + return TLS_INTEGRITY_FAILED; + } +#endif + } else { + int err = _private_tls_crypto_decrypt(context, buf + header_size, pt, length); + if (err) { + TLS_FREE(pt); + DEBUG_PRINT("Decryption error %i\n", (int)err); + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + return TLS_BROKEN_PACKET; + } + unsigned char padding_byte = pt[length - 1]; + unsigned char padding = padding_byte + 1; + + // poodle check + int padding_index = length - padding; + if (padding_index > 0) { + int i; + int limit = length - 1; + for (i = length - padding; i < limit; i++) { + if (pt[i] != padding_byte) { + TLS_FREE(pt); + DEBUG_PRINT("BROKEN PACKET (POODLE ?)\n"); + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + _private_tls_write_packet(tls_build_alert(context, 1, decrypt_error)); + return TLS_BROKEN_PACKET; + } + } + } + + unsigned int decrypted_length = length; + if (padding < decrypted_length) + decrypted_length -= padding; + + DEBUG_DUMP_HEX_LABEL("decrypted", pt, decrypted_length); + ptr = pt; +#ifdef TLS_LEGACY_SUPPORT + if ((context->version != TLS_V10) && (decrypted_length > TLS_AES_IV_LENGTH)) { + decrypted_length -= TLS_AES_IV_LENGTH; + ptr += TLS_AES_IV_LENGTH; + } +#else + if (decrypted_length > TLS_AES_IV_LENGTH) { + decrypted_length -= TLS_AES_IV_LENGTH; + ptr += TLS_AES_IV_LENGTH; + } +#endif + length = decrypted_length; + + unsigned int mac_size = _private_tls_mac_length(context); + if ((length < mac_size) || (!mac_size)) { + TLS_FREE(pt); + DEBUG_PRINT("BROKEN PACKET\n"); + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + _private_tls_write_packet(tls_build_alert(context, 1, decrypt_error)); + return TLS_BROKEN_PACKET; + } + + length -= mac_size; + + const unsigned char *message_hmac = &ptr[length]; + unsigned char hmac_out[TLS_MAX_MAC_SIZE]; + unsigned char temp_buf[5]; + memcpy(temp_buf, buf, 3); + *(unsigned short *)(temp_buf + 3) = htons(length); + unsigned int hmac_out_len = _private_tls_hmac_message(0, context, temp_buf, 5, ptr, length, hmac_out, mac_size, dtls_sequence_number); + if ((hmac_out_len != mac_size) || (memcmp(message_hmac, hmac_out, mac_size))) { + DEBUG_PRINT("INTEGRITY CHECK FAILED (msg length %i)\n", length); + DEBUG_DUMP_HEX_LABEL("HMAC RECEIVED", message_hmac, mac_size); + DEBUG_DUMP_HEX_LABEL("HMAC COMPUTED", hmac_out, hmac_out_len); + TLS_FREE(pt); + + // silently ignore packet for DTLS + if (context->dtls) + return header_size + length; + + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + _private_tls_write_packet(tls_build_alert(context, 1, bad_record_mac)); + + return TLS_INTEGRITY_FAILED; + } + } + } + context->remote_sequence_number++; +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + if (/*(context->connection_status == 2) && */(type == TLS_APPLICATION_DATA) && (context->crypto.created)) { + do { + length--; + type = ptr[length]; + } while (!type); + } + } +#endif + switch (type) { + // application data + case TLS_APPLICATION_DATA: + if (context->connection_status != 0xFF) { + DEBUG_PRINT("UNEXPECTED APPLICATION DATA MESSAGE\n"); + payload_res = TLS_UNEXPECTED_MESSAGE; + _private_tls_write_packet(tls_build_alert(context, 1, unexpected_message)); + } else { + DEBUG_PRINT("APPLICATION DATA MESSAGE (TLS VERSION: %x):\n", (int)context->version); + DEBUG_DUMP(ptr, length); + DEBUG_PRINT("\n"); + _private_tls_write_app_data(context, ptr, length); + } + break; + // handshake + case TLS_HANDSHAKE: + DEBUG_PRINT("HANDSHAKE MESSAGE\n"); + payload_res = tls_parse_payload(context, ptr, length, certificate_verify); + break; + // change cipher spec + case TLS_CHANGE_CIPHER: + context->dtls_epoch_remote++; + if (context->connection_status != 2) { +#ifdef WITH_TLS_13 + if (context->connection_status == 4) { + DEBUG_PRINT("IGNORING CHANGE CIPHER SPEC MESSAGE (HELLO RETRY REQUEST)\n"); + break; + } +#endif + DEBUG_PRINT("UNEXPECTED CHANGE CIPHER SPEC MESSAGE (%i)\n", context->connection_status); + _private_tls_write_packet(tls_build_alert(context, 1, unexpected_message)); + payload_res = TLS_UNEXPECTED_MESSAGE; + } else { + DEBUG_PRINT("CHANGE CIPHER SPEC MESSAGE\n"); + context->cipher_spec_set = 1; + // reset sequence numbers + context->remote_sequence_number = 0; + } +#ifdef WITH_TLS_13 + if (!context->is_server) + _private_tls13_key(context, 1); +#endif + break; + // alert + case TLS_ALERT: + DEBUG_PRINT("ALERT MESSAGE\n"); + if (length >= 2) { + DEBUG_DUMP_HEX(ptr, length); + int level = ptr[0]; + int code = ptr[1]; + if (level == TLS_ALERT_CRITICAL) { + context->critical_error = 1; + res = TLS_ERROR_ALERT; + } + context->error_code = code; + } + break; + default: + DEBUG_PRINT("NOT UNDERSTOOD MESSAGE TYPE: %x\n", (int)type); + TLS_FREE(pt); + return TLS_NOT_UNDERSTOOD; + } + TLS_FREE(pt); + + if (payload_res < 0) + return payload_res; + + if (res > 0) + return header_size + length; + + return res; +} + +unsigned int asn1_get_len(const unsigned char *buffer, int buf_len, unsigned int *octets) { + *octets = 0; + + if (buf_len < 1) + return 0; + + unsigned char size = buffer[0]; + int i; + if (size & 0x80) { + *octets = size & 0x7F; + if ((int)*octets > buf_len - 1) + return 0; + // max 32 bits + unsigned int ref_octets = *octets; + if (*octets > 4) + ref_octets = 4; + if ((int)*octets > buf_len -1) + return 0; + unsigned int long_size = 0; + unsigned int coef = 1; + + for (i = ref_octets; i > 0; i--) { + long_size += buffer[i] * coef; + coef *= 0x100; + } + ++*octets; + return long_size; + } + ++*octets; + return size; +} + +void print_index(const unsigned int *fields) { + int i = 0; + while (fields[i]) { + if (i) + DEBUG_PRINT("."); + DEBUG_PRINT("%i", fields[i]); + i++; + } + while (i < 6) { + DEBUG_PRINT(" "); + i++; + } +} + +int _is_field(const unsigned int *fields, const unsigned int *prefix) { + int i = 0; + while (prefix[i]) { + if (fields[i] != prefix[i]) + return 0; + i++; + } + return 1; +} + +int _private_tls_hash_len(int algorithm) { + switch (algorithm) { + case TLS_RSA_SIGN_MD5: + return 16; + case TLS_RSA_SIGN_SHA1: + return 20; + case TLS_RSA_SIGN_SHA256: + case TLS_ECDSA_SIGN_SHA256: + return 32; + case TLS_RSA_SIGN_SHA384: + return 48; + case TLS_RSA_SIGN_SHA512: + return 64; + } + return 0; +} + +unsigned char *_private_tls_compute_hash(int algorithm, const unsigned char *message, unsigned int message_len) { + unsigned char *hash = NULL; + if ((!message) || (!message_len)) + return hash; + int err; + hash_state state; + switch (algorithm) { + case TLS_RSA_SIGN_MD5: + DEBUG_PRINT("SIGN MD5\n"); + hash = (unsigned char *)TLS_MALLOC(16); + if (!hash) + return NULL; + + err = md5_init(&state); + if (!err) { + err = md5_process(&state, message, message_len); + if (!err) + err = md5_done(&state, hash); + } + break; + case TLS_RSA_SIGN_SHA1: + DEBUG_PRINT("SIGN SHA1\n"); + hash = (unsigned char *)TLS_MALLOC(20); + if (!hash) + return NULL; + + err = sha1_init(&state); + if (!err) { + err = sha1_process(&state, message, message_len); + if (!err) + err = sha1_done(&state, hash); + } + break; + case TLS_RSA_SIGN_SHA256: + case TLS_ECDSA_SIGN_SHA256: + DEBUG_PRINT("SIGN SHA256\n"); + hash = (unsigned char *)TLS_MALLOC(32); + if (!hash) + return NULL; + + err = sha256_init(&state); + if (!err) { + err = sha256_process(&state, message, message_len); + if (!err) + err = sha256_done(&state, hash); + } + break; + case TLS_RSA_SIGN_SHA384: + DEBUG_PRINT("SIGN SHA384\n"); + hash = (unsigned char *)TLS_MALLOC(48); + if (!hash) + return NULL; + + err = sha384_init(&state); + if (!err) { + err = sha384_process(&state, message, message_len); + if (!err) + err = sha384_done(&state, hash); + } + break; + case TLS_RSA_SIGN_SHA512: + DEBUG_PRINT("SIGN SHA512\n"); + hash = (unsigned char *)TLS_MALLOC(64); + if (!hash) + return NULL; + + err = sha512_init(&state); + if (!err) { + err = sha512_process(&state, message, message_len); + if (!err) + err = sha512_done(&state, hash); + } + break; + default: + DEBUG_PRINT("UNKNOWN SIGNATURE ALGORITHM\n"); + } + return hash; +} + +int tls_certificate_verify_signature(struct TLSCertificate *cert, struct TLSCertificate *parent) { + if ((!cert) || (!parent) || (!cert->sign_key) || (!cert->fingerprint) || (!cert->sign_len) || (!parent->der_bytes) || (!parent->der_len)) { + DEBUG_PRINT("CANNOT VERIFY SIGNATURE"); + return 0; + } + tls_init(); + int hash_len = _private_tls_hash_len(cert->algorithm); + if (hash_len <= 0) + return 0; + + int hash_index = -1; + switch (cert->algorithm) { + case TLS_RSA_SIGN_MD5: + hash_index = find_hash("md5"); + break; + case TLS_RSA_SIGN_SHA1: + hash_index = find_hash("sha1"); + break; + case TLS_RSA_SIGN_SHA256: + case TLS_ECDSA_SIGN_SHA256: + hash_index = find_hash("sha256"); + break; + case TLS_RSA_SIGN_SHA384: + hash_index = find_hash("sha384"); + break; + case TLS_RSA_SIGN_SHA512: + hash_index = find_hash("sha512"); + break; + default: + DEBUG_PRINT("UNKNOWN SIGNATURE ALGORITHM\n"); + return 0; + } +#ifdef TLS_ECDSA_SUPPORTED + if (cert->algorithm == TLS_ECDSA_SIGN_SHA256) { + ecc_key key; + int err = ecc_import(parent->der_bytes, parent->der_len, &key); + if (err) { + DEBUG_PRINT("Error importing ECC certificate (code: %i)\n", err); + DEBUG_DUMP_HEX_LABEL("CERTIFICATE", parent->der_bytes, parent->der_len); + return 0; + } + int ecc_stat = 0; + unsigned char *signature = cert->sign_key; + int signature_len = cert->sign_len; + if (!signature[0]) { + signature++; + signature_len--; + } + err = ecc_verify_hash(signature, signature_len, cert->fingerprint, hash_len, &ecc_stat, &key); + ecc_free(&key); + if (err) { + DEBUG_PRINT("ECC HASH VERIFY ERROR %i\n", err); + return 0; + } + DEBUG_PRINT("ECC CERTIFICATE VALIDATION: %i\n", ecc_stat); + return ecc_stat; + } +#endif + + rsa_key key; + int err = rsa_import(parent->der_bytes, parent->der_len, &key); + if (err) { + DEBUG_PRINT("Error importing RSA certificate (code: %i)\n", err); + DEBUG_DUMP_HEX_LABEL("CERTIFICATE", parent->der_bytes, parent->der_len); + return 0; + } + int rsa_stat = 0; + unsigned char *signature = cert->sign_key; + int signature_len = cert->sign_len; + if (!signature[0]) { + signature++; + signature_len--; + } + err = rsa_verify_hash_ex(signature, signature_len, cert->fingerprint, hash_len, LTC_PKCS_1_V1_5, hash_index, 0, &rsa_stat, &key); + rsa_free(&key); + if (err) { + DEBUG_PRINT("HASH VERIFY ERROR %i\n", err); + return 0; + } + DEBUG_PRINT("CERTIFICATE VALIDATION: %i\n", rsa_stat); + return rsa_stat; +} + +int tls_certificate_chain_is_valid(struct TLSCertificate **certificates, int len) { + if ((!certificates) || (!len)) + return bad_certificate; + + int i; + len--; + + // expired certificate or not yet valid ? + if (tls_certificate_is_valid(certificates[0])) + return bad_certificate; + + // check + for (i = 0; i < len; i++) { + // certificate in chain is expired ? + if (tls_certificate_is_valid(certificates[i+1])) + return bad_certificate; + if (!tls_certificate_verify_signature(certificates[i], certificates[i+1])) + return bad_certificate; + } + return 0; +} + +int tls_certificate_chain_is_valid_root(struct TLSContext *context, struct TLSCertificate **certificates, int len) { + if ((!certificates) || (!len) || (!context->root_certificates) || (!context->root_count)) + return bad_certificate; + int i; + unsigned int j; + for (i = 0; i < len; i++) { + for (j = 0; j < context->root_count; j++) { + // check if root certificate expired + if (tls_certificate_is_valid(context->root_certificates[j])) + continue; + // if any root validates any certificate in the chain, then is root validated + if (tls_certificate_verify_signature(certificates[i], context->root_certificates[j])) + return 0; + } + } + return bad_certificate; +} + +int _private_is_oid(struct _private_OID_chain *ref_chain, const unsigned char *looked_oid, int looked_oid_len) { + while (ref_chain) { + if (ref_chain->oid) { + if (_is_oid2(ref_chain->oid, looked_oid, 16, looked_oid_len)) + return 1; + } + ref_chain = (struct _private_OID_chain *)ref_chain->top; + } + return 0; +} + +int _private_asn1_parse(struct TLSContext *context, struct TLSCertificate *cert, const unsigned char *buffer, unsigned int size, int level, unsigned int *fields, unsigned char *has_key, int client_cert, unsigned char *top_oid, struct _private_OID_chain *chain) { + struct _private_OID_chain local_chain; + local_chain.top = chain; + unsigned int pos = 0; + // X.690 + int idx = 0; + unsigned char oid[16]; + memset(oid, 0, 16); + local_chain.oid = oid; + if (has_key) + *has_key = 0; + unsigned char local_has_key = 0; + const unsigned char *cert_data = NULL; + unsigned int cert_len = 0; + while (pos < size) { + unsigned int start_pos = pos; + CHECK_SIZE(2, size - pos, TLS_NEED_MORE_DATA) + unsigned char first = buffer[pos++]; + unsigned char type = first & 0x1F; + unsigned char constructed = first & 0x20; + unsigned char element_class = first >> 6; + unsigned int octets = 0; + unsigned int temp; + idx++; + if (level <= TLS_ASN1_MAXLEVEL) + fields[level - 1] = idx; + unsigned int length = asn1_get_len((unsigned char *)&buffer[pos], size - pos, &octets); + if ((octets > 4) || (octets > size - pos)) { + DEBUG_PRINT("CANNOT READ CERTIFICATE\n"); + return pos; + } + pos += octets; + CHECK_SIZE(length, size - pos, TLS_NEED_MORE_DATA) + //DEBUG_PRINT("FIRST: %x => %x (%i)\n", (int)first, (int)type, length); + // sequence + //DEBUG_PRINT("%2i: ", level); +#ifdef DEBUG + DEBUG_INDEX(fields); + int i1; + for (i1 = 1; i1 < level; i1++) + DEBUG_PRINT(" "); +#endif + + if ((length) && (constructed)) { + switch (type) { + case 0x03: + DEBUG_PRINT("CONSTRUCTED BITSTREAM\n"); + break; + case 0x10: + DEBUG_PRINT("SEQUENCE\n"); + if ((level == 2) && (idx == 1)) { + cert_len = length + (pos - start_pos); + cert_data = &buffer[start_pos]; + } + // private key on server or public key on client + if ((!cert->version) && (_is_field(fields, priv_der_id))) { + TLS_FREE(cert->der_bytes); + temp = length + (pos - start_pos); + cert->der_bytes = (unsigned char *)TLS_MALLOC(temp); + if (cert->der_bytes) { + memcpy(cert->der_bytes, &buffer[start_pos], temp); + cert->der_len = temp; + } else + cert->der_len = 0; + } + break; + case 0x11: + DEBUG_PRINT("EMBEDDED PDV\n"); + break; + case 0x00: + if (element_class == 0x02) { + DEBUG_PRINT("CONTEXT-SPECIFIC\n"); + break; + } + default: + DEBUG_PRINT("CONSTRUCT TYPE %02X\n", (int)type); + } + local_has_key = 0; + _private_asn1_parse(context, cert, &buffer[pos], length, level + 1, fields, &local_has_key, client_cert, top_oid, &local_chain); + if ((((local_has_key) && (context) && ((!context->is_server) || (client_cert))) || (!context)) && (_is_field(fields, pk_id))) { + TLS_FREE(cert->der_bytes); + temp = length + (pos - start_pos); + cert->der_bytes = (unsigned char *)TLS_MALLOC(temp); + if (cert->der_bytes) { + memcpy(cert->der_bytes, &buffer[start_pos], temp); + cert->der_len = temp; + } else + cert->der_len = 0; + } + } else { + switch (type) { + case 0x00: + // end of content + DEBUG_PRINT("END OF CONTENT\n"); + return pos; + break; + case 0x01: + // boolean + temp = buffer[pos]; + DEBUG_PRINT("BOOLEAN: %i\n", temp); + break; + case 0x02: + // integer + if (_is_field(fields, pk_id)) { + if (has_key) + *has_key = 1; + + if (idx == 1) + tls_certificate_set_key(cert, &buffer[pos], length); + else + if (idx == 2) + tls_certificate_set_exponent(cert, &buffer[pos], length); + } else + if (_is_field(fields, serial_id)) + tls_certificate_set_serial(cert, &buffer[pos], length); + if (_is_field(fields, version_id)) { + if (length == 1) + cert->version = buffer[pos]; +#ifdef TLS_X509_V1_SUPPORT + else + cert->version = 0; + idx++; +#endif + } + if (level >= 2) { + unsigned int fields_temp[3]; + fields_temp[0] = fields[level - 2]; + fields_temp[1] = fields[level - 1]; + fields_temp[2] = 0; + if (_is_field(fields_temp, priv_id)) + tls_certificate_set_priv(cert, &buffer[pos], length); + } + DEBUG_PRINT("INTEGER(%i): ", length); + DEBUG_DUMP_HEX(&buffer[pos], length); + if ((chain) && (length > 2)) { + if (_private_is_oid(chain, san_oid, sizeof(san_oid) - 1)) { + cert->san = (unsigned char **)TLS_REALLOC(cert->san, sizeof(unsigned char *) * (cert->san_length + 1)); + if (cert->san) { + cert->san[cert->san_length] = NULL; + tls_certificate_set_copy(&cert->san[cert->san_length], &buffer[pos], length); + DEBUG_PRINT(" => SUBJECT ALTERNATIVE NAME: %s", cert->san[cert->san_length ]); + cert->san_length++; + } else + cert->san_length = 0; + } + } + DEBUG_PRINT("\n"); + break; + case 0x03: + if (_is_field(fields, pk_id)) { + if (has_key) + *has_key = 1; + } + // bitstream + DEBUG_PRINT("BITSTREAM(%i): ", length); + DEBUG_DUMP_HEX(&buffer[pos], length); + DEBUG_PRINT("\n"); + if (_is_field(fields, sign_id)) { + tls_certificate_set_sign_key(cert, &buffer[pos], length); + } else + if ((cert->ec_algorithm) && (_is_field(fields, pk_id))) { + tls_certificate_set_key(cert, &buffer[pos], length); + } else { + if ((buffer[pos] == 0x00) && (length > 256)) + _private_asn1_parse(context, cert, &buffer[pos]+1, length - 1, level + 1, fields, &local_has_key, client_cert, top_oid, &local_chain); + else + _private_asn1_parse(context, cert, &buffer[pos], length, level + 1, fields, &local_has_key, client_cert, top_oid, &local_chain); +#ifdef TLS_FORWARD_SECRECY + #ifdef TLS_ECDSA_SUPPORTED + if (top_oid) { + if (_is_oid2(top_oid, TLS_EC_prime256v1_OID, sizeof(oid), sizeof(TLS_EC_prime256v1) - 1)) { + cert->ec_algorithm = secp256r1.iana; + } else + if (_is_oid2(top_oid, TLS_EC_secp224r1_OID, sizeof(oid), sizeof(TLS_EC_secp224r1_OID) - 1)) { + cert->ec_algorithm = secp224r1.iana; + } else + if (_is_oid2(top_oid, TLS_EC_secp384r1_OID, sizeof(oid), sizeof(TLS_EC_secp384r1_OID) - 1)) { + cert->ec_algorithm = secp384r1.iana; + } else + if (_is_oid2(top_oid, TLS_EC_secp521r1_OID, sizeof(oid), sizeof(TLS_EC_secp521r1_OID) - 1)) { + cert->ec_algorithm = secp521r1.iana; + } + if ((cert->ec_algorithm) && (!cert->pk)) + tls_certificate_set_key(cert, &buffer[pos], length); + } + #endif +#endif + } + break; + case 0x04: + if ((top_oid) && (_is_field(fields, ecc_priv_id)) && (!cert->priv)) { + DEBUG_PRINT("BINARY STRING(%i): ", length); + DEBUG_DUMP_HEX(&buffer[pos], length); + DEBUG_PRINT("\n"); + tls_certificate_set_priv(cert, &buffer[pos], length); + } else + _private_asn1_parse(context, cert, &buffer[pos], length, level + 1, fields, &local_has_key, client_cert, top_oid, &local_chain); + break; + case 0x05: + DEBUG_PRINT("NULL\n"); + break; + case 0x06: + // object identifier + if (_is_field(fields, pk_id)) { +#ifdef TLS_ECDSA_SUPPORTED + if ((length == 8) || (length == 5)) + tls_certificate_set_algorithm(context, &cert->ec_algorithm, &buffer[pos], length); + else +#endif + tls_certificate_set_algorithm(context, &cert->key_algorithm, &buffer[pos], length); + } + if (_is_field(fields, algorithm_id)) + tls_certificate_set_algorithm(context, &cert->algorithm, &buffer[pos], length); + + DEBUG_PRINT("OBJECT IDENTIFIER(%i): ", length); + DEBUG_DUMP_HEX(&buffer[pos], length); + DEBUG_PRINT("\n"); + // check previous oid + if (_is_oid2(oid, ocsp_oid, 16, sizeof(ocsp_oid) - 1)) + tls_certificate_set_copy(&cert->ocsp, &buffer[pos], length); + + if (length < 16) + memcpy(oid, &buffer[pos], length); + else + memcpy(oid, &buffer[pos], 16); + if (top_oid) + memcpy(top_oid, oid, 16); + break; + case 0x09: + DEBUG_PRINT("REAL NUMBER(%i): ", length); + DEBUG_DUMP_HEX(&buffer[pos], length); + DEBUG_PRINT("\n"); + break; + case 0x17: + // utc time + DEBUG_PRINT("UTC TIME: ["); + DEBUG_DUMP(&buffer[pos], length); + DEBUG_PRINT("]\n"); + + if (_is_field(fields, validity_id)) { + if (idx == 1) + tls_certificate_set_copy_date(&cert->not_before, &buffer[pos], length); + else + tls_certificate_set_copy_date(&cert->not_after, &buffer[pos], length); + } + break; + case 0x18: + // generalized time + DEBUG_PRINT("GENERALIZED TIME: ["); + DEBUG_DUMP(&buffer[pos], length); + DEBUG_PRINT("]\n"); + break; + case 0x13: + // printable string + case 0x0C: + case 0x14: + case 0x15: + case 0x16: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + if (_is_field(fields, issurer_id)) { + if (_is_oid(oid, country_oid, 3)) + tls_certificate_set_copy(&cert->issuer_country, &buffer[pos], length); + else + if (_is_oid(oid, state_oid, 3)) + tls_certificate_set_copy(&cert->issuer_state, &buffer[pos], length); + else + if (_is_oid(oid, location_oid, 3)) + tls_certificate_set_copy(&cert->issuer_location, &buffer[pos], length); + else + if (_is_oid(oid, entity_oid, 3)) + tls_certificate_set_copy(&cert->issuer_entity, &buffer[pos], length); + else + if (_is_oid(oid, subject_oid, 3)) + tls_certificate_set_copy(&cert->issuer_subject, &buffer[pos], length); + } else + if (_is_field(fields, owner_id)) { + if (_is_oid(oid, country_oid, 3)) + tls_certificate_set_copy(&cert->country, &buffer[pos], length); + else + if (_is_oid(oid, state_oid, 3)) + tls_certificate_set_copy(&cert->state, &buffer[pos], length); + else + if (_is_oid(oid, location_oid, 3)) + tls_certificate_set_copy(&cert->location, &buffer[pos], length); + else + if (_is_oid(oid, entity_oid, 3)) + tls_certificate_set_copy(&cert->entity, &buffer[pos], length); + else + if (_is_oid(oid, subject_oid, 3)) + tls_certificate_set_copy(&cert->subject, &buffer[pos], length); + } + DEBUG_PRINT("STR: ["); + DEBUG_DUMP(&buffer[pos], length); + DEBUG_PRINT("]\n"); + break; + case 0x10: + DEBUG_PRINT("EMPTY SEQUENCE\n"); + break; + case 0xA: + DEBUG_PRINT("ENUMERATED(%i): ", length); + DEBUG_DUMP_HEX(&buffer[pos], length); + DEBUG_PRINT("\n"); + break; + default: + DEBUG_PRINT("========> NOT SUPPORTED %x\n", (int)type); + // not supported / needed + break; + } + } + pos += length; + } + if ((level == 2) && (cert->sign_key) && (cert->sign_len) && (cert_len) && (cert_data)) { + TLS_FREE(cert->fingerprint); + cert->fingerprint = _private_tls_compute_hash(cert->algorithm, cert_data, cert_len); +#ifdef DEBUG + if (cert->fingerprint) { + DEBUG_DUMP_HEX_LABEL("FINGERPRINT", cert->fingerprint, _private_tls_hash_len(cert->algorithm)); + } +#endif + } + return pos; +} + +struct TLSCertificate *asn1_parse(struct TLSContext *context, const unsigned char *buffer, unsigned int size, int client_cert) { + unsigned int fields[TLS_ASN1_MAXLEVEL]; + memset(fields, 0, sizeof(int) * TLS_ASN1_MAXLEVEL); + struct TLSCertificate *cert = tls_create_certificate(); + if (cert) { + if (client_cert < 0) { + client_cert = 0; + // private key + unsigned char top_oid[16]; + memset(top_oid, 0, sizeof(top_oid)); + _private_asn1_parse(context, cert, buffer, size, 1, fields, NULL, client_cert, top_oid, NULL); + } else + _private_asn1_parse(context, cert, buffer, size, 1, fields, NULL, client_cert, NULL, NULL); + } + return cert; +} + +int tls_load_certificates(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size) { + if (!context) + return TLS_GENERIC_ERROR; + + unsigned int len; + int idx = 0; + do { + unsigned char *data = tls_pem_decode(pem_buffer, pem_size, idx++, &len); + if ((!data) || (!len)) + break; + struct TLSCertificate *cert = asn1_parse(context, data, len, 0); + if (cert) { + if ((cert->version == 2) +#ifdef TLS_X509_V1_SUPPORT + || (cert->version == 0) +#endif + ) { + TLS_FREE(cert->der_bytes); + cert->der_bytes = data; + cert->der_len = len; + data = NULL; + if (cert->priv) { + DEBUG_PRINT("WARNING - parse error (private key encountered in certificate)\n"); + TLS_FREE(cert->priv); + cert->priv = NULL; + cert->priv_len = 0; + } + if (context->is_server) { + context->certificates = (struct TLSCertificate **)TLS_REALLOC(context->certificates, (context->certificates_count + 1) * sizeof(struct TLSCertificate *)); + context->certificates[context->certificates_count] = cert; + context->certificates_count++; + DEBUG_PRINT("Loaded certificate: %i\n", (int)context->certificates_count); + } else { + context->client_certificates = (struct TLSCertificate **)TLS_REALLOC(context->client_certificates, (context->client_certificates_count + 1) * sizeof(struct TLSCertificate *)); + context->client_certificates[context->client_certificates_count] = cert; + context->client_certificates_count++; + DEBUG_PRINT("Loaded client certificate: %i\n", (int)context->client_certificates_count); + } + } else { + DEBUG_PRINT("WARNING - certificate version error (v%i)\n", (int)cert->version); + tls_destroy_certificate(cert); + } + } + TLS_FREE(data); + } while (1); + return context->certificates_count; +} + +int tls_load_private_key(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size) { + if (!context) + return TLS_GENERIC_ERROR; + + unsigned int len; + int idx = 0; + do { + unsigned char *data = tls_pem_decode(pem_buffer, pem_size, idx++, &len); + if ((!data) || (!len)) + break; + struct TLSCertificate *cert = asn1_parse(context, data, len, -1); + if (cert) { + if (!cert->der_len) { + TLS_FREE(cert->der_bytes); + cert->der_bytes = data; + cert->der_len = len; + } else + TLS_FREE(data); + if ((cert) && (cert->priv) && (cert->priv_len)) { +#ifdef TLS_ECDSA_SUPPORTED + if (cert->ec_algorithm) { + DEBUG_PRINT("Loaded ECC private key\n"); + if (context->ec_private_key) + tls_destroy_certificate(context->ec_private_key); + context->ec_private_key = cert; + return 1; + } else +#endif + { + DEBUG_PRINT("Loaded private key\n"); + if (context->private_key) + tls_destroy_certificate(context->private_key); + context->private_key = cert; + return 1; + } + } + tls_destroy_certificate(cert); + } else + TLS_FREE(data); + } while (1); + return 0; +} + +int tls_clear_certificates(struct TLSContext *context) { + unsigned int i; + if ((!context) || (!context->is_server) || (context->is_child)) + return TLS_GENERIC_ERROR; + + if (context->root_certificates) { + for (i = 0; i < context->root_count; i++) + tls_destroy_certificate(context->root_certificates[i]); + } + context->root_certificates = NULL; + context->root_count = 0; + if (context->private_key) + tls_destroy_certificate(context->private_key); + context->private_key = NULL; +#ifdef TLS_ECDSA_SUPPORTED + if (context->ec_private_key) + tls_destroy_certificate(context->ec_private_key); + context->ec_private_key = NULL; +#endif + TLS_FREE(context->certificates); + context->certificates = NULL; + context->certificates_count = 0; + return 0; +} + +#ifdef WITH_TLS_13 +struct TLSPacket *tls_build_certificate_verify(struct TLSContext *context) { + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0); + //certificate verify + tls_packet_uint8(packet, 0x0F); + unsigned int size_offset = packet->len; + tls_packet_uint24(packet, 0); + + unsigned char out[TLS_MAX_RSA_KEY]; +#ifdef TLS_ECDSA_SUPPORTED + unsigned long out_len = TLS_MAX_RSA_KEY; +#endif + + unsigned char signing_data[TLS_MAX_HASH_SIZE + 98]; + int signing_data_len; + + // first 64 bytes to 0x20 (32) + memset(signing_data, 0x20, 64); + // context string 33 bytes + if (context->is_server) + memcpy(signing_data + 64, "TLS 1.3, server CertificateVerify", 33); + else + memcpy(signing_data + 64, "TLS 1.3, client CertificateVerify", 33); + // a single 0 byte separator + signing_data[97] = 0; + signing_data_len = 98; + + signing_data_len += _private_tls_get_hash(context, signing_data + 98); + DEBUG_DUMP_HEX_LABEL("verify data", signing_data, signing_data_len); + int hash_algorithm = sha256; +#ifdef TLS_ECDSA_SUPPORTED + if (tls_is_ecdsa(context)) { + switch (context->ec_private_key->ec_algorithm) { + case 23: + // secp256r1 + sha256 + tls_packet_uint16(packet, 0x0403); + break; + case 24: + // secp384r1 + sha384 + tls_packet_uint16(packet, 0x0503); + hash_algorithm = sha384; + break; + case 25: + // secp521r1 + sha512 + tls_packet_uint16(packet, 0x0603); + hash_algorithm = sha512; + break; + default: + DEBUG_PRINT("UNSUPPORTED CURVE (SIGNING)\n"); + packet->broken = 1; + return packet; + } + } else +#endif + { + tls_packet_uint16(packet, 0x0804); + } + + int packet_size = 2; +#ifdef TLS_ECDSA_SUPPORTED + if (tls_is_ecdsa(context)) { + if (_private_tls_sign_ecdsa(context, hash_algorithm, signing_data, signing_data_len, out, &out_len) == 1) { + DEBUG_PRINT("ECDSA signing OK! (ECDSA, length %lu)\n", out_len); + tls_packet_uint16(packet, out_len); + tls_packet_append(packet, out, out_len); + packet_size += out_len + 2; + } + } else +#endif + if (_private_tls_sign_rsa(context, hash_algorithm, signing_data, signing_data_len, out, &out_len) == 1) { + DEBUG_PRINT("RSA signing OK! (length %lu)\n", out_len); + tls_packet_uint16(packet, out_len); + tls_packet_append(packet, out, out_len); + packet_size += out_len + 2; + } + packet->buf[size_offset] = packet_size / 0x10000; + packet_size %= 0x10000; + packet->buf[size_offset + 1] = packet_size / 0x100; + packet_size %= 0x100; + packet->buf[size_offset + 2] = packet_size; + + tls_packet_update(packet); + return packet; +} +#endif + +struct TLSPacket *tls_build_certificate(struct TLSContext *context) { + int i; + unsigned int all_certificate_size = 0; + int certificates_count; + struct TLSCertificate **certificates; + if (context->is_server) { + certificates_count = context->certificates_count; + certificates = context->certificates; + } else { + certificates_count = context->client_certificates_count; + certificates = context->client_certificates; + } + int delta = 3; +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + delta = 5; +#endif +#ifdef TLS_ECDSA_SUPPORTED + int is_ecdsa = tls_is_ecdsa(context); + if (is_ecdsa) { + for (i = 0; i < certificates_count; i++) { + struct TLSCertificate *cert = certificates[i]; + if ((cert) && (cert->der_len) && (cert->ec_algorithm)) + all_certificate_size += cert->der_len + delta; + } + } else { + for (i = 0; i < certificates_count; i++) { + struct TLSCertificate *cert = certificates[i]; + if ((cert) && (cert->der_len) && (!cert->ec_algorithm)) + all_certificate_size += cert->der_len + delta; + } + } +#else + for (i = 0; i < certificates_count; i++) { + struct TLSCertificate *cert = certificates[i]; + if ((cert) && (cert->der_len)) + all_certificate_size += cert->der_len + delta; + } +#endif + if (!all_certificate_size) { + DEBUG_PRINT("NO CERTIFICATE SET\n"); + } + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0); + tls_packet_uint8(packet, 0x0B); + if (all_certificate_size) { +#ifdef WITH_TLS_13 + // context + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + tls_packet_uint24(packet, all_certificate_size + 4); + tls_packet_uint8(packet, 0); + } else +#endif + tls_packet_uint24(packet, all_certificate_size + 3); + + if (context->dtls) + _private_dtls_handshake_data(context, packet, all_certificate_size + 3); + + tls_packet_uint24(packet, all_certificate_size); + for (i = 0; i < certificates_count; i++) { + struct TLSCertificate *cert = certificates[i]; + if ((cert) && (cert->der_len)) { +#ifdef TLS_ECDSA_SUPPORTED + // is RSA certificate ? + if ((is_ecdsa) && (!cert->ec_algorithm)) + continue; + // is ECC certificate ? + if ((!is_ecdsa) && (cert->ec_algorithm)) + continue; +#endif + // 2 times -> one certificate + tls_packet_uint24(packet, cert->der_len); + tls_packet_append(packet, cert->der_bytes, cert->der_len); +#ifdef WITH_TLS_13 + // extension + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + tls_packet_uint16(packet, 0); +#endif + } + } + } else { + tls_packet_uint24(packet, all_certificate_size); +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + tls_packet_uint8(packet, 0); +#endif + + if (context->dtls) + _private_dtls_handshake_data(context, packet, all_certificate_size); + } + tls_packet_update(packet); + if (context->dtls) + context->dtls_seq++; + return packet; +} + +#ifdef WITH_TLS_13 +struct TLSPacket *tls_build_encrypted_extensions(struct TLSContext *context) { + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 3); + tls_packet_uint8(packet, 0x08); + if (context->negotiated_alpn) { + int alpn_negotiated_len = strlen(context->negotiated_alpn); + int alpn_len = alpn_negotiated_len + 1; + + tls_packet_uint24(packet, alpn_len + 8); + tls_packet_uint16(packet, alpn_len + 6); + tls_packet_uint16(packet, 0x10); + tls_packet_uint16(packet, alpn_len + 2); + tls_packet_uint16(packet, alpn_len); + + tls_packet_uint8(packet, alpn_negotiated_len); + tls_packet_append(packet, (unsigned char *)context->negotiated_alpn, alpn_negotiated_len); + } else { + tls_packet_uint24(packet, 2); + tls_packet_uint16(packet, 0); + } + tls_packet_update(packet); + return packet; +} +#endif + +struct TLSPacket *tls_build_finished(struct TLSContext *context) { + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, TLS_MIN_FINISHED_OPAQUE_LEN + 64); + tls_packet_uint8(packet, 0x14); +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + tls_packet_uint24(packet, _private_tls_mac_length(context)); + else +#endif + tls_packet_uint24(packet, TLS_MIN_FINISHED_OPAQUE_LEN); + if (context->dtls) + _private_dtls_handshake_data(context, packet, TLS_MIN_FINISHED_OPAQUE_LEN); + // verify + unsigned char hash[TLS_MAX_HASH_SIZE]; + unsigned long out_size = TLS_MIN_FINISHED_OPAQUE_LEN; +#ifdef WITH_TLS_13 + unsigned char out[TLS_MAX_HASH_SIZE]; +#else + unsigned char out[TLS_MIN_FINISHED_OPAQUE_LEN]; +#endif + unsigned int hash_len; + + // server verifies client's message + if (context->is_server) { +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + hash_len = _private_tls_get_hash(context, hash); + if ((!context->finished_key) || (!hash_len)) { + DEBUG_PRINT("NO FINISHED KEY COMPUTED OR NO HANDSHAKE HASH\n"); + packet->broken = 1; + return packet; + } + + DEBUG_DUMP_HEX_LABEL("HS HASH", hash, hash_len); + DEBUG_DUMP_HEX_LABEL("HS FINISH", context->finished_key, hash_len); + DEBUG_DUMP_HEX_LABEL("HS REMOTE FINISH", context->remote_finished_key, hash_len); + + out_size = hash_len; + hmac_state hmac; + hmac_init(&hmac, _private_tls_get_hash_idx(context), context->finished_key, hash_len); + hmac_process(&hmac, hash, hash_len); + hmac_done(&hmac, out, &out_size); + } else +#endif + { + hash_len = _private_tls_done_hash(context, hash); + _private_tls_prf(context, out, TLS_MIN_FINISHED_OPAQUE_LEN, context->master_key, context->master_key_len, (unsigned char *)"server finished", 15, hash, hash_len, NULL, 0); + _private_tls_destroy_hash(context); + } + } else { +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + hash_len = _private_tls_get_hash(context, hash); + if ((!context->finished_key) || (!hash_len)) { + DEBUG_PRINT("NO FINISHED KEY COMPUTED OR NO HANDSHAKE HASH\n"); + packet->broken = 1; + return packet; + } + DEBUG_DUMP_HEX_LABEL("HS HASH", hash, hash_len); + DEBUG_DUMP_HEX_LABEL("HS FINISH", context->finished_key, hash_len); + DEBUG_DUMP_HEX_LABEL("HS REMOTE FINISH", context->remote_finished_key, hash_len); + + TLS_FREE(context->server_finished_hash); + context->server_finished_hash = (unsigned char *)TLS_MALLOC(hash_len); + if (context->server_finished_hash) + memcpy(context->server_finished_hash, hash, hash_len); + + out_size = hash_len; + hmac_state hmac; + hmac_init(&hmac, _private_tls_get_hash_idx(context), context->finished_key, hash_len); + hmac_process(&hmac, hash, hash_len); + hmac_done(&hmac, out, &out_size); + } else { +#endif + hash_len = _private_tls_get_hash(context, hash); + _private_tls_prf(context, out, TLS_MIN_FINISHED_OPAQUE_LEN, context->master_key, context->master_key_len, (unsigned char *)"client finished", 15, hash, hash_len, NULL, 0); +#ifdef WITH_TLS_13 + } +#endif + } + tls_packet_append(packet, out, out_size); + tls_packet_update(packet); + DEBUG_DUMP_HEX_LABEL("VERIFY DATA", out, out_size); +#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION + if (context->is_server) { + // concatenate client verify and server verify + context->verify_data = (unsigned char *)TLS_REALLOC(context->verify_data, out_size); + if (context->verify_data) { + memcpy(context->verify_data + context->verify_len, out, out_size); + context->verify_len += out_size; + } else + context->verify_len = 0; + } else { + TLS_FREE(context->verify_data); + context->verify_data = (unsigned char *)TLS_MALLOC(out_size); + if (context->verify_data) { + memcpy(context->verify_data, out, out_size); + context->verify_len = out_size; + } + } +#endif + return packet; +} + +struct TLSPacket *tls_build_change_cipher_spec(struct TLSContext *context) { + struct TLSPacket *packet = tls_create_packet(context, TLS_CHANGE_CIPHER, context->version, 64); + tls_packet_uint8(packet, 1); + tls_packet_update(packet); + context->local_sequence_number = 0; + return packet; +} + +struct TLSPacket *tls_build_done(struct TLSContext *context) { + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0); + tls_packet_uint8(packet, 0x0E); + tls_packet_uint24(packet, 0); + if (context->dtls) { + _private_dtls_handshake_data(context, packet, 0); + context->dtls_seq++; + } + tls_packet_update(packet); + return packet; +} + +struct TLSPacket *tls_build_message(struct TLSContext *context, const unsigned char *data, unsigned int len) { + if ((!data) || (!len)) + return 0; + struct TLSPacket *packet = tls_create_packet(context, TLS_APPLICATION_DATA, context->version, len); + tls_packet_append(packet, data, len); + tls_packet_update(packet); + return packet; +} + +int tls_client_connect(struct TLSContext *context) { + if ((context->is_server) || (context->critical_error)) + return TLS_UNEXPECTED_MESSAGE; + + return _private_tls_write_packet(tls_build_hello(context, 0)); +} + +int tls_write(struct TLSContext *context, const unsigned char *data, unsigned int len) { + if (!context) + return TLS_GENERIC_ERROR; +#ifdef TLS_12_FALSE_START + if ((context->connection_status != 0xFF) && ((context->is_server) || (context->version != TLS_V12) || (context->critical_error) || (!context->false_start))) + return TLS_UNEXPECTED_MESSAGE; +#else + if (context->connection_status != 0xFF) + return TLS_UNEXPECTED_MESSAGE; +#endif + if (len > TLS_MAXTLS_APP_SIZE) + len = TLS_MAXTLS_APP_SIZE; + int actually_written = _private_tls_write_packet(tls_build_message(context, data, len)); + if (actually_written <= 0) + return actually_written; + return len; +} + +struct TLSPacket *tls_build_alert(struct TLSContext *context, char critical, unsigned char code) { + struct TLSPacket *packet = tls_create_packet(context, TLS_ALERT, context->version, 0); + tls_packet_uint8(packet, critical ? TLS_ALERT_CRITICAL : TLS_ALERT_WARNING); + if (critical) + context->critical_error = 1; + tls_packet_uint8(packet, code); + tls_packet_update(packet); + return packet; +} + +int _private_tls_read_from_file(const char *fname, void *buf, int max_len) { + FILE *f = fopen(fname, "rb"); + if (f) { + int size = (int)fread(buf, 1, max_len, f); + fclose(f); + return size; + } + return 0; +} + +int tls_consume_stream(struct TLSContext *context, const unsigned char *buf, int buf_len, tls_validation_function certificate_verify) { + if (!context) + return TLS_GENERIC_ERROR; + + if (context->critical_error) + return TLS_BROKEN_CONNECTION; + + if (buf_len <= 0) { + DEBUG_PRINT("tls_consume_stream called with buf_len %i\n", buf_len); + return 0; + } + + if (!buf) { + DEBUG_PRINT("tls_consume_stream called NULL buffer\n"); + context->critical_error = 1; + return TLS_NO_MEMORY; + } + + unsigned int orig_len = context->message_buffer_len; + context->message_buffer_len += buf_len; + context->message_buffer = (unsigned char *)TLS_REALLOC(context->message_buffer, context->message_buffer_len); + if (!context->message_buffer) { + context->message_buffer_len = 0; + return TLS_NO_MEMORY; + } + memcpy(context->message_buffer + orig_len, buf, buf_len); + unsigned int index = 0; + unsigned int tls_buffer_len = context->message_buffer_len; + int err_flag = 0; + + int tls_header_size; + int tls_size_offset; + + if (context->dtls) { + tls_size_offset = 11; + tls_header_size = 13; + } else { + tls_size_offset = 3; + tls_header_size = 5; + } + while (tls_buffer_len >= 5) { + unsigned int length = ntohs(*(unsigned short *)&context->message_buffer[index + tls_size_offset]) + tls_header_size; + if (length > tls_buffer_len) { + DEBUG_PRINT("NEED DATA: %i/%i\n", length, tls_buffer_len); + break; + } + int consumed = tls_parse_message(context, &context->message_buffer[index], length, certificate_verify); + DEBUG_PRINT("Consumed %i bytes\n", consumed); + if (consumed < 0) { + if (!context->critical_error) + context->critical_error = 1; + err_flag = consumed; + break; + } + index += length; + tls_buffer_len -= length; + if (context->critical_error) { + err_flag = TLS_BROKEN_CONNECTION; + break; + } + } + if (err_flag) { + DEBUG_PRINT("ERROR IN CONSUME: %i\n", err_flag); + context->message_buffer_len = 0; + TLS_FREE(context->message_buffer); + context->message_buffer = NULL; + return err_flag; + } + if (index) { + context->message_buffer_len -= index; + if (context->message_buffer_len) { + // no realloc here + memmove(context->message_buffer, context->message_buffer + index, context->message_buffer_len); + } else { + TLS_FREE(context->message_buffer); + context->message_buffer = NULL; + } + } + return index; +} + +void tls_close_notify(struct TLSContext *context) { + if ((!context) || (context->critical_error)) + return; + context->critical_error = 1; + DEBUG_PRINT("CLOSE\n"); + _private_tls_write_packet(tls_build_alert(context, 0, close_notify)); +} + +void tls_alert(struct TLSContext *context, unsigned char critical, int code) { + if (!context) + return; + if ((!context->critical_error) && (critical)) + context->critical_error = 1; + DEBUG_PRINT("ALERT\n"); + _private_tls_write_packet(tls_build_alert(context, critical, code)); +} + +int tls_pending(struct TLSContext *context) { + if (!context->message_buffer) + return 0; + return context->message_buffer_len; +} + +void tls_make_exportable(struct TLSContext *context, unsigned char exportable_flag) { + context->exportable = exportable_flag; + if (!exportable_flag) { + // zero the memory + if ((context->exportable_keys) && (context->exportable_size)) + memset(context->exportable_keys, 0, context->exportable_size); + // free the memory, if alocated + TLS_FREE(context->exportable_keys); + context->exportable_size = 0; + } +} + +int tls_export_context(struct TLSContext *context, unsigned char *buffer, unsigned int buf_len, unsigned char small_version) { + // only negotiated AND exportable connections may be exported + if ((!context) || (context->critical_error) || (context->connection_status != 0xFF) || (!context->exportable) || (!context->exportable_keys) || (!context->exportable_size) || (!context->crypto.created)) { + DEBUG_PRINT("CANNOT EXPORT CONTEXT %i\n", (int)context->connection_status); + return 0; + } + + struct TLSPacket *packet = tls_create_packet(NULL, TLS_SERIALIZED_OBJECT, context->version, 0); + // export buffer version + tls_packet_uint8(packet, 0x01); + tls_packet_uint8(packet, context->connection_status); + tls_packet_uint16(packet, context->cipher); + if (context->is_child) + tls_packet_uint8(packet, 2); + else + tls_packet_uint8(packet, context->is_server); + + if (context->crypto.created == 2) { + // aead +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + tls_packet_uint8(packet, TLS_13_AES_GCM_IV_LENGTH); + tls_packet_append(packet, context->crypto.ctx_local_mac.local_iv, TLS_13_AES_GCM_IV_LENGTH); + tls_packet_append(packet, context->crypto.ctx_remote_mac.remote_iv, TLS_13_AES_GCM_IV_LENGTH); + } else { +#endif + tls_packet_uint8(packet, TLS_AES_GCM_IV_LENGTH); + tls_packet_append(packet, context->crypto.ctx_local_mac.local_aead_iv, TLS_AES_GCM_IV_LENGTH); + tls_packet_append(packet, context->crypto.ctx_remote_mac.remote_aead_iv, TLS_AES_GCM_IV_LENGTH); +#ifdef WITH_TLS_13 + } +#endif +#ifdef TLS_WITH_CHACHA20_POLY1305 + } else + if (context->crypto.created == 3) { + // ChaCha20 + tls_packet_uint8(packet, TLS_CHACHA20_IV_LENGTH); + tls_packet_append(packet, context->crypto.ctx_local_mac.local_nonce, TLS_CHACHA20_IV_LENGTH); + tls_packet_append(packet, context->crypto.ctx_remote_mac.remote_nonce, TLS_CHACHA20_IV_LENGTH); +#endif + } else { + unsigned char iv[TLS_AES_IV_LENGTH]; + unsigned long len = TLS_AES_IV_LENGTH; + + memset(iv, 0, TLS_AES_IV_LENGTH); + cbc_getiv(iv, &len, &context->crypto.ctx_local.aes_local); + tls_packet_uint8(packet, TLS_AES_IV_LENGTH); + tls_packet_append(packet, iv, len); + + memset(iv, 0, TLS_AES_IV_LENGTH); + cbc_getiv(iv, &len, &context->crypto.ctx_remote.aes_remote); + tls_packet_append(packet, iv, TLS_AES_IV_LENGTH); + } + + tls_packet_uint8(packet, context->exportable_size); + tls_packet_append(packet, context->exportable_keys, context->exportable_size); + + if (context->crypto.created == 2) { + tls_packet_uint8(packet, 0); +#ifdef TLS_WITH_CHACHA20_POLY1305 + } else + if (context->crypto.created == 3) { + // ChaCha20 + tls_packet_uint8(packet, 0); + unsigned int i; + for (i = 0; i < 16; i++) + tls_packet_uint32(packet, context->crypto.ctx_local.chacha_local.input[i]); + for (i = 0; i < 16; i++) + tls_packet_uint32(packet, context->crypto.ctx_remote.chacha_remote.input[i]); + tls_packet_append(packet, context->crypto.ctx_local.chacha_local.ks, CHACHA_BLOCKLEN); + tls_packet_append(packet, context->crypto.ctx_remote.chacha_remote.ks, CHACHA_BLOCKLEN); +#endif + } else { + unsigned char mac_length = (unsigned char)_private_tls_mac_length(context); + tls_packet_uint8(packet, mac_length); + tls_packet_append(packet, context->crypto.ctx_local_mac.local_mac, mac_length); + tls_packet_append(packet, context->crypto.ctx_remote_mac.remote_mac, mac_length); + } + + if (small_version) { + tls_packet_uint16(packet, 0); + } else { + tls_packet_uint16(packet, context->master_key_len); + tls_packet_append(packet, context->master_key, context->master_key_len); + } + + uint64_t sequence_number = htonll(context->local_sequence_number); + tls_packet_append(packet, (unsigned char *)&sequence_number, sizeof(uint64_t)); + sequence_number = htonll(context->remote_sequence_number); + tls_packet_append(packet, (unsigned char *)&sequence_number, sizeof(uint64_t)); + + tls_packet_uint32(packet, context->tls_buffer_len); + tls_packet_append(packet, context->tls_buffer, context->tls_buffer_len); + + tls_packet_uint32(packet, context->message_buffer_len); + tls_packet_append(packet, context->message_buffer, context->message_buffer_len); + + tls_packet_uint32(packet, context->application_buffer_len); + tls_packet_append(packet, context->application_buffer, context->application_buffer_len); + tls_packet_uint8(packet, context->dtls); + if (context->dtls) { + tls_packet_uint16(packet, context->dtls_epoch_local); + tls_packet_uint16(packet, context->dtls_epoch_remote); + } + tls_packet_update(packet); + unsigned int size = packet->len; + if ((buffer) && (buf_len)) { + if (size > buf_len) { + tls_destroy_packet(packet); + DEBUG_PRINT("EXPORT BUFFER TO SMALL\n"); + return (int)buf_len - (int)size; + } + memcpy(buffer, packet->buf, size); + } + tls_destroy_packet(packet); + return size; +} + +struct TLSContext *tls_import_context(const unsigned char *buffer, unsigned int buf_len) { + if ((!buffer) || (buf_len < 64) || (buffer[0] != TLS_SERIALIZED_OBJECT) || (buffer[5] != 0x01)) { + DEBUG_PRINT("CANNOT IMPORT CONTEXT BUFFER\n"); + return NULL; + } + // create a context object + struct TLSContext *context = tls_create_context(0, TLS_V12); + if (context) { + unsigned char temp[0xFF]; + context->version = ntohs(*(unsigned short *)&buffer[1]); + unsigned short length = ntohs(*(unsigned short *)&buffer[3]); + if (length != buf_len - 5) { + DEBUG_PRINT("INVALID IMPORT BUFFER SIZE\n"); + tls_destroy_context(context); + return NULL; + } + context->connection_status = buffer[6]; + context->cipher = ntohs(*(unsigned short *)&buffer[7]); + unsigned char server = buffer[9]; + if (server == 2) { + context->is_server = 1; + context->is_child = 1; + } else + context->is_server = server; + + unsigned char local_iv[TLS_AES_IV_LENGTH]; + unsigned char remote_iv[TLS_AES_IV_LENGTH]; + unsigned char iv_len = buffer[10]; + if (iv_len > TLS_AES_IV_LENGTH) { + DEBUG_PRINT("INVALID IV LENGTH\n"); + tls_destroy_context(context); + return NULL; + } + + // get the initialization vectors + int buf_pos = 11; + memcpy(local_iv, &buffer[buf_pos], iv_len); + buf_pos += iv_len; + memcpy(remote_iv, &buffer[buf_pos], iv_len); + buf_pos += iv_len; + + unsigned char key_lengths = buffer[buf_pos++]; + TLS_IMPORT_CHECK_SIZE(buf_pos, key_lengths, buf_len) + memcpy(temp, &buffer[buf_pos], key_lengths); + buf_pos += key_lengths; +#ifdef TLS_REEXPORTABLE + context->exportable = 1; + context->exportable_keys = (unsigned char *)TLS_MALLOC(key_lengths); + memcpy(context->exportable_keys, temp, key_lengths); + context->exportable_size = key_lengths; +#else + context->exportable = 0; +#endif + int is_aead = _private_tls_is_aead(context); +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (is_aead == 2) { + // ChaCha20 + if (iv_len > TLS_CHACHA20_IV_LENGTH) + iv_len = TLS_CHACHA20_IV_LENGTH; + memcpy(context->crypto.ctx_local_mac.local_nonce, local_iv, iv_len); + memcpy(context->crypto.ctx_remote_mac.remote_nonce, remote_iv, iv_len); + } else +#endif + if (is_aead) { +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + if (iv_len > TLS_13_AES_GCM_IV_LENGTH) + iv_len = TLS_13_AES_GCM_IV_LENGTH; + memcpy(context->crypto.ctx_local_mac.local_iv, local_iv, iv_len); + memcpy(context->crypto.ctx_remote_mac.remote_iv, remote_iv, iv_len); + } else { +#endif + if (iv_len > TLS_AES_GCM_IV_LENGTH) + iv_len = TLS_AES_GCM_IV_LENGTH; + memcpy(context->crypto.ctx_local_mac.local_aead_iv, local_iv, iv_len); + memcpy(context->crypto.ctx_remote_mac.remote_aead_iv, remote_iv, iv_len); +#ifdef WITH_TLS_13 + } +#endif + } + if (context->is_server) { + if (_private_tls_crypto_create(context, key_lengths / 2, temp, local_iv, temp + key_lengths / 2, remote_iv)) { + DEBUG_PRINT("ERROR CREATING KEY CONTEXT\n"); + tls_destroy_context(context); + return NULL; + } + } else { + if (_private_tls_crypto_create(context, key_lengths / 2, temp + key_lengths / 2, remote_iv, temp, local_iv)) { + DEBUG_PRINT("ERROR CREATING KEY CONTEXT (CLIENT)\n"); + tls_destroy_context(context); + return NULL; + } + } + memset(temp, 0, sizeof(temp)); + + unsigned char mac_length = buffer[buf_pos++]; + if (mac_length > TLS_MAX_MAC_SIZE) { + DEBUG_PRINT("INVALID MAC SIZE\n"); + tls_destroy_context(context); + return NULL; + } + + if (mac_length) { + TLS_IMPORT_CHECK_SIZE(buf_pos, mac_length, buf_len) + memcpy(context->crypto.ctx_local_mac.local_mac, &buffer[buf_pos], mac_length); + buf_pos += mac_length; + + TLS_IMPORT_CHECK_SIZE(buf_pos, mac_length, buf_len) + memcpy(context->crypto.ctx_remote_mac.remote_mac, &buffer[buf_pos], mac_length); + buf_pos += mac_length; + } else +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (is_aead == 2) { + // ChaCha20 + unsigned int i; + TLS_IMPORT_CHECK_SIZE(buf_pos, 128 + CHACHA_BLOCKLEN * 2, buf_len) + for (i = 0; i < 16; i++) { + context->crypto.ctx_local.chacha_local.input[i] = ntohl(*(unsigned int *)(buffer + buf_pos)); + buf_pos += sizeof(unsigned int); + } + for (i = 0; i < 16; i++) { + context->crypto.ctx_remote.chacha_remote.input[i] = ntohl(*(unsigned int *)(buffer + buf_pos)); + buf_pos += sizeof(unsigned int); + } + memcpy(context->crypto.ctx_local.chacha_local.ks, buffer + buf_pos, CHACHA_BLOCKLEN); + buf_pos += CHACHA_BLOCKLEN; + memcpy(context->crypto.ctx_remote.chacha_remote.ks, buffer + buf_pos, CHACHA_BLOCKLEN); + buf_pos += CHACHA_BLOCKLEN; + } +#endif + + TLS_IMPORT_CHECK_SIZE(buf_pos, 2, buf_len) + unsigned short master_key_len = ntohs(*(unsigned short *)(buffer + buf_pos)); + buf_pos += 2; + if (master_key_len) { + TLS_IMPORT_CHECK_SIZE(buf_pos, master_key_len, buf_len) + context->master_key = (unsigned char *)TLS_MALLOC(master_key_len); + if (context->master_key) { + memcpy(context->master_key, &buffer[buf_pos], master_key_len); + context->master_key_len = master_key_len; + } + buf_pos += master_key_len; + } + + TLS_IMPORT_CHECK_SIZE(buf_pos, 16, buf_len) + + context->local_sequence_number = ntohll(*(uint64_t *)&buffer[buf_pos]); + buf_pos += 8; + context->remote_sequence_number = ntohll(*(uint64_t *)&buffer[buf_pos]); + buf_pos += 8; + + TLS_IMPORT_CHECK_SIZE(buf_pos, 4, buf_len) + unsigned int tls_buffer_len = ntohl(*(unsigned int *)&buffer[buf_pos]); + buf_pos += 4; + TLS_IMPORT_CHECK_SIZE(buf_pos, tls_buffer_len, buf_len) + if (tls_buffer_len) { + context->tls_buffer = (unsigned char *)TLS_MALLOC(tls_buffer_len); + if (context->tls_buffer) { + memcpy(context->tls_buffer, &buffer[buf_pos], tls_buffer_len); + context->tls_buffer_len = tls_buffer_len; + } + buf_pos += tls_buffer_len; + } + + TLS_IMPORT_CHECK_SIZE(buf_pos, 4, buf_len) + unsigned int message_buffer_len = ntohl(*(unsigned int *)&buffer[buf_pos]); + buf_pos += 4; + TLS_IMPORT_CHECK_SIZE(buf_pos, message_buffer_len, buf_len) + if (message_buffer_len) { + context->message_buffer = (unsigned char *)TLS_MALLOC(message_buffer_len); + if (context->message_buffer) { + memcpy(context->message_buffer, &buffer[buf_pos], message_buffer_len); + context->message_buffer_len = message_buffer_len; + } + buf_pos += message_buffer_len; + } + + TLS_IMPORT_CHECK_SIZE(buf_pos, 4, buf_len) + unsigned int application_buffer_len = ntohl(*(unsigned int *)&buffer[buf_pos]); + buf_pos += 4; + context->cipher_spec_set = 1; + TLS_IMPORT_CHECK_SIZE(buf_pos, application_buffer_len, buf_len) + if (application_buffer_len) { + context->application_buffer = (unsigned char *)TLS_MALLOC(application_buffer_len); + if (context->application_buffer) { + memcpy(context->application_buffer, &buffer[buf_pos], application_buffer_len); + context->application_buffer_len = application_buffer_len; + } + buf_pos += application_buffer_len; + } + TLS_IMPORT_CHECK_SIZE(buf_pos, 1, buf_len) + context->dtls = buffer[buf_pos]; + buf_pos++; + if (context->dtls) { + TLS_IMPORT_CHECK_SIZE(buf_pos, 4, buf_len) + context->dtls_epoch_local = ntohs(*(unsigned short *)&buffer[buf_pos]); + buf_pos += 2; + context->dtls_epoch_remote = ntohs(*(unsigned short *)&buffer[buf_pos]); + } + } + return context; +} + +int tls_is_broken(struct TLSContext *context) { + if ((!context) || (context->critical_error)) + return 1; + return 0; +} + +int tls_request_client_certificate(struct TLSContext *context) { + if ((!context) || (!context->is_server)) + return 0; + + context->request_client_certificate = 1; + return 1; +} + +int tls_client_verified(struct TLSContext *context) { + if ((!context) || (context->critical_error)) + return 0; + + return (context->client_verified == 1); +} + +const char *tls_sni(struct TLSContext *context) { + if (!context) + return NULL; + return context->sni; +} + +int tls_sni_set(struct TLSContext *context, const char *sni) { + if ((!context) || (context->is_server) || (context->critical_error) || (context->connection_status != 0)) + return 0; + TLS_FREE(context->sni); + context->sni = NULL; + if (sni) { + size_t len = strlen(sni); + if (len > 0) { + context->sni = (char *)TLS_MALLOC(len + 1); + if (context->sni) { + context->sni[len] = 0; + memcpy(context->sni, sni, len); + return 1; + } + } + } + return 0; +} + +int tls_load_root_certificates(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size) { + if (!context) + return TLS_GENERIC_ERROR; + + unsigned int len; + int idx = 0; + + do { + unsigned char *data = tls_pem_decode(pem_buffer, pem_size, idx++, &len); + if ((!data) || (!len)) + break; + struct TLSCertificate *cert = asn1_parse(NULL, data, len, 0); + if (cert) { + if ((cert->version == 2) +#ifdef TLS_X509_V1_SUPPORT + || (cert->version == 0) +#endif + ) { + if (cert->priv) { + DEBUG_PRINT("WARNING - parse error (private key encountered in certificate)\n"); + TLS_FREE(cert->priv); + cert->priv = NULL; + cert->priv_len = 0; + } + context->root_certificates = (struct TLSCertificate **)TLS_REALLOC(context->root_certificates, (context->root_count + 1) * sizeof(struct TLSCertificate *)); + if (!context->root_certificates) { + context->root_count = 0; + return TLS_GENERIC_ERROR; + } + context->root_certificates[context->root_count] = cert; + context->root_count++; + DEBUG_PRINT("Loaded certificate: %i\n", (int)context->root_count); + } else { + DEBUG_PRINT("WARNING - certificate version error (v%i)\n", (int)cert->version); + tls_destroy_certificate(cert); + } + } + TLS_FREE(data); + } while (1); + return context->root_count; +} + +int tls_default_verify(struct TLSContext *context, struct TLSCertificate **certificate_chain, int len) { + int i; + int err; + + if (certificate_chain) { + for (i = 0; i < len; i++) { + struct TLSCertificate *certificate = certificate_chain[i]; + // check validity date + err = tls_certificate_is_valid(certificate); + if (err) + return err; + } + } + // check if chain is valid + err = tls_certificate_chain_is_valid(certificate_chain, len); + if (err) + return err; + + // check certificate subject + if ((!context->is_server) && (context->sni) && (len > 0) && (certificate_chain)) { + err = tls_certificate_valid_subject(certificate_chain[0], context->sni); + if (err) + return err; + } + + err = tls_certificate_chain_is_valid_root(context, certificate_chain, len); + if (err) + return err; + + DEBUG_PRINT("Certificate OK\n"); + return no_error; +} + +int tls_unmake_ktls(struct TLSContext *context, int socket) { +#ifdef WITH_KTLS + struct tls12_crypto_info_aes_gcm_128 crypto_info; + socklen_t crypt_info_size = sizeof(crypto_info); + if (getsockopt(socket, SOL_TLS, TLS_TX, &crypto_info, &crypt_info_size)) { + DEBUG_PRINT("ERROR IN getsockopt\n"); + return TLS_GENERIC_ERROR; + } + memcpy(crypto_info.rec_seq, &context->local_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE); + context->local_sequence_number = ntohll(context->local_sequence_number); +#ifdef TLS_RX + crypt_info_size = sizeof(crypto_info); + if (getsockopt(socket, SOL_TLS, TLS_RX, &crypto_info, &crypt_info_size)) { + DEBUG_PRINT("ERROR IN getsockopt\n"); + return TLS_GENERIC_ERROR; + } + memcpy(crypto_info.rec_seq, &context->remote_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE); + context->remote_sequence_number = ntohll(context->remote_sequence_number); +#endif + return 0; +#endif + DEBUG_PRINT("TLSe COMPILED WITHOUT kTLS SUPPORT\n"); + return TLS_FEATURE_NOT_SUPPORTED; +} + +int tls_make_ktls(struct TLSContext *context, int socket) { + if ((!context) || (context->critical_error) || (context->connection_status != 0xFF) || (!context->crypto.created)) { + DEBUG_PRINT("CANNOT SWITCH TO kTLS\n"); + return TLS_GENERIC_ERROR; + } + if ((!context->exportable) || (!context->exportable_keys)) { + DEBUG_PRINT("KEY MUST BE EXPORTABLE TO BE ABLE TO USE kTLS\n"); + return TLS_GENERIC_ERROR; + } + if ((context->version != TLS_V12) && (context->version != DTLS_V12) && (context->version != TLS_V13) && (context->version != DTLS_V13)) { + DEBUG_PRINT("kTLS IS SUPPORTED ONLY FOR TLS >= 1.2 AND DTLS >= 1.2\n"); + return TLS_FEATURE_NOT_SUPPORTED; + } + switch (context->cipher) { + case TLS_RSA_WITH_AES_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + break; + default: + DEBUG_PRINT("CIPHER UNSUPPORTED: kTLS SUPPORTS ONLY AES 128 GCM CIPHERS\n"); + return TLS_FEATURE_NOT_SUPPORTED; + } +#ifdef WITH_KTLS + if (context->exportable_size < TLS_CIPHER_AES_GCM_128_KEY_SIZE * 2) { + DEBUG_PRINT("INVALID KEY SIZE\n"); + return TLS_GENERIC_ERROR; + } + int err; + struct tls12_crypto_info_aes_gcm_128 crypto_info; + + crypto_info.info.version = TLS_1_2_VERSION; + crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_128; + + uint64_t local_sequence_number = htonll(context->local_sequence_number); + memcpy(crypto_info.iv, &local_sequence_number, TLS_CIPHER_AES_GCM_128_IV_SIZE); + memcpy(crypto_info.rec_seq, &local_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE); + memcpy(crypto_info.key, context->exportable_keys, TLS_CIPHER_AES_GCM_128_KEY_SIZE); + memcpy(crypto_info.salt, context->crypto.ctx_local_mac.local_aead_iv, TLS_CIPHER_AES_GCM_128_SALT_SIZE); + + err = setsockopt(socket, SOL_TCP, TCP_ULP, "tls", sizeof("tls")); + if (err) + return err; + +#ifdef TLS_RX + // kernel 4.17 adds TLS_RX support + struct tls12_crypto_info_aes_gcm_128 crypto_info_read; + + crypto_info_read.info.version = TLS_1_2_VERSION; + crypto_info_read.info.cipher_type = TLS_CIPHER_AES_GCM_128; + + uint64_t remote_sequence_number = htonll(context->remote_sequence_number); + memcpy(crypto_info_read.iv, &remote_sequence_number, TLS_CIPHER_AES_GCM_128_IV_SIZE); + memcpy(crypto_info_read.rec_seq, &remote_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE); + memcpy(crypto_info_read.key, context->exportable_keys + TLS_CIPHER_AES_GCM_128_KEY_SIZE, TLS_CIPHER_AES_GCM_128_KEY_SIZE); + memcpy(crypto_info_read.salt, context->crypto.ctx_remote_mac.remote_aead_iv, TLS_CIPHER_AES_GCM_128_SALT_SIZE); + + err = setsockopt(socket, SOL_TLS, TLS_RX, &crypto_info_read, sizeof(crypto_info_read)); + if (err) + return err; +#endif + return setsockopt(socket, SOL_TLS, TLS_TX, &crypto_info, sizeof(crypto_info)); +#else + DEBUG_PRINT("TLSe COMPILED WITHOUT kTLS SUPPORT\n"); + return TLS_FEATURE_NOT_SUPPORTED; +#endif +} + +#ifdef DEBUG +void tls_print_certificate(const char *fname) { + unsigned char buf[0xFFFF]; + char out_buf[0xFFFF]; + int size = _private_tls_read_from_file(fname, buf, 0xFFFF); + if (size > 0) { + int idx = 0; + unsigned int len; + do { + unsigned char *data; + if (buf[0] == '-') { + data = tls_pem_decode(buf, size, idx++, &len); + } else { + data = buf; + len = size; + } + if ((!data) || (!len)) + return; + struct TLSCertificate *cert = asn1_parse(NULL, data, len, -1); + if (data != buf) + TLS_FREE(data); + if (cert) { + fprintf(stderr, "%s", tls_certificate_to_string(cert, out_buf, 0xFFFF)); + tls_destroy_certificate(cert); + } + if (data == buf) + break; + } while (1); + } +} +#endif + +int tls_remote_error(struct TLSContext *context) { + if (!context) + return TLS_GENERIC_ERROR; + + return context->error_code; +} + +#ifdef SSL_COMPATIBLE_INTERFACE + +int SSL_library_init() { + // dummy function + return 1; +} + +void SSL_load_error_strings() { + // dummy function +} + +void OpenSSL_add_all_algorithms() { + // dummy function +} + +void OpenSSL_add_all_ciphers() { + // dummy function +} + +void OpenSSL_add_all_digests() { + // dummy function +} + +void EVP_cleanup() { + // dummy function +} + +int _tls_ssl_private_send_pending(int client_sock, struct TLSContext *context) { + unsigned int out_buffer_len = 0; + const unsigned char *out_buffer = tls_get_write_buffer(context, &out_buffer_len); + unsigned int out_buffer_index = 0; + int send_res = 0; + SOCKET_SEND_CALLBACK write_cb = NULL; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if (ssl_data) + write_cb = (SOCKET_SEND_CALLBACK)ssl_data->send; + while ((out_buffer) && (out_buffer_len > 0)) { + int res; + if (write_cb) + res = write_cb(client_sock, (char *)&out_buffer[out_buffer_index], out_buffer_len, 0); + else + res = send(client_sock, (char *)&out_buffer[out_buffer_index], out_buffer_len, 0); + if (res <= 0) { + if ((!write_cb) && (res < 0)) { +#ifdef _WIN32 + if (WSAGetLastError() == WSAEWOULDBLOCK) { + context->tls_buffer_len = out_buffer_len; + memmove(context->tls_buffer, out_buffer + out_buffer_index, out_buffer_len); + return res; + } +#else + if ((errno == EAGAIN) || (errno == EINTR)) { + context->tls_buffer_len = out_buffer_len; + memmove(context->tls_buffer, out_buffer + out_buffer_index, out_buffer_len); + return res; + } +#endif + } + send_res = res; + break; + } + out_buffer_len -= res; + out_buffer_index += res; + send_res += res; + } + tls_buffer_clear(context); + return send_res; +} + +struct TLSContext *SSL_new(struct TLSContext *context) { + return tls_accept(context); +} + +int SSLv3_server_method() { + return 1; +} + +int SSLv3_client_method() { + return 0; +} + +int SSL_CTX_use_certificate_file(struct TLSContext *context, const char *filename, int dummy) { + // max 64k buffer + unsigned char buf[0xFFFF]; + int size = _private_tls_read_from_file(filename, buf, sizeof(buf)); + if (size > 0) + return tls_load_certificates(context, buf, size); + return size; +} + +int SSL_CTX_use_PrivateKey_file(struct TLSContext *context, const char *filename, int dummy) { + unsigned char buf[0xFFFF]; + int size = _private_tls_read_from_file(filename, buf, sizeof(buf)); + if (size > 0) + return tls_load_private_key(context, buf, size); + + return size; +} + +int SSL_CTX_check_private_key(struct TLSContext *context) { + if ((!context) || (((!context->private_key) || (!context->private_key->der_bytes) || (!context->private_key->der_len)) +#ifdef TLS_ECDSA_SUPPORTED + && ((!context->ec_private_key) || (!context->ec_private_key->der_bytes) || (!context->ec_private_key->der_len)) +#endif + )) + return 0; + return 1; +} + +struct TLSContext *SSL_CTX_new(int method) { +#ifdef WITH_TLS_13 + return tls_create_context(method, TLS_V13); +#else + return tls_create_context(method, TLS_V12); +#endif +} + +void SSL_free(struct TLSContext *context) { + if (context) { + TLS_FREE(context->user_data); + tls_destroy_context(context); + } +} + +void SSL_CTX_free(struct TLSContext *context) { + SSL_free(context); +} + +int SSL_get_error(struct TLSContext *context, int ret) { + if (!context) + return TLS_GENERIC_ERROR; + return context->critical_error; +} + +int SSL_set_fd(struct TLSContext *context, int socket) { + if (!context) + return 0; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if (!ssl_data) { + ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData)); + if (!ssl_data) + return TLS_NO_MEMORY; + memset(ssl_data, 0, sizeof(SSLUserData)); + context->user_data = ssl_data; + } + ssl_data->fd = socket; + return 1; +} + +void *SSL_set_userdata(struct TLSContext *context, void *data) { + if (!context) + return NULL; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if (!ssl_data) { + ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData)); + if (!ssl_data) + return NULL; + memset(ssl_data, 0, sizeof(SSLUserData)); + context->user_data = ssl_data; + } + void *old_data = ssl_data->user_data; + ssl_data->user_data = data; + return old_data; +} + +void *SSL_userdata(struct TLSContext *context) { + if (!context) + return NULL; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if (!ssl_data) + return NULL; + + return ssl_data->user_data; +} + +int SSL_CTX_root_ca(struct TLSContext *context, const char *pem_filename) { + if (!context) + return TLS_GENERIC_ERROR; + + int count = TLS_GENERIC_ERROR; + FILE *f = fopen(pem_filename, "rb"); + if (f) { + fseek(f, 0, SEEK_END); + size_t size = (size_t)ftell(f); + fseek(f, 0, SEEK_SET); + if (size) { + unsigned char *buf = (unsigned char *)TLS_MALLOC(size + 1); + if (buf) { + buf[size] = 1; + if (fread(buf, 1, size, f) == size) { + count = tls_load_root_certificates(context, buf, size); + if (count > 0) { + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if (!ssl_data) { + ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData)); + if (!ssl_data) { + fclose(f); + return TLS_NO_MEMORY; + } + memset(ssl_data, 0, sizeof(SSLUserData)); + context->user_data = ssl_data; + } + if (!ssl_data->certificate_verify) + ssl_data->certificate_verify = tls_default_verify; + } + } + TLS_FREE(buf); + } + } + fclose(f); + } + return count; +} + +void SSL_CTX_set_verify(struct TLSContext *context, int mode, tls_validation_function verify_callback) { + if (!context) + return; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if (!ssl_data) { + ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData)); + if (!ssl_data) + return; + memset(ssl_data, 0, sizeof(SSLUserData)); + context->user_data = ssl_data; + } + if (mode == SSL_VERIFY_NONE) + ssl_data->certificate_verify = NULL; + else + ssl_data->certificate_verify = verify_callback; +} + +int _private_tls_safe_read(struct TLSContext *context, void *buffer, int buf_size) { + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if ((!ssl_data) || (ssl_data->fd < 0)) + return TLS_GENERIC_ERROR; + + SOCKET_RECV_CALLBACK read_cb = (SOCKET_RECV_CALLBACK)ssl_data->recv; + if (read_cb) + return read_cb(ssl_data->fd, (char *)buffer, buf_size, 0); + return recv(ssl_data->fd, (char *)buffer, buf_size, 0); +} + +int SSL_accept(struct TLSContext *context) { + if (!context) + return TLS_GENERIC_ERROR; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if ((!ssl_data) || (ssl_data->fd < 0)) + return TLS_GENERIC_ERROR; + if (tls_established(context)) + return 1; + unsigned char client_message[0xFFFF]; + // accept + int read_size = 0; + while ((read_size = _private_tls_safe_read(context, (char *)client_message, sizeof(client_message))) > 0) { + if (tls_consume_stream(context, client_message, read_size, ssl_data->certificate_verify) >= 0) { + int res = _tls_ssl_private_send_pending(ssl_data->fd, context); + if (res < 0) + return res; + } + if (tls_established(context)) + return 1; + } + if (read_size <= 0) + return TLS_BROKEN_CONNECTION; + return 0; +} + +int SSL_connect(struct TLSContext *context) { + if (!context) + return TLS_GENERIC_ERROR; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if ((!ssl_data) || (ssl_data->fd < 0) || (context->critical_error)) + return TLS_GENERIC_ERROR; + int res = tls_client_connect(context); + if (res < 0) + return res; + res = _tls_ssl_private_send_pending(ssl_data->fd, context); + if (res < 0) + return res; + + int read_size; + unsigned char client_message[0xFFFF]; + + while ((read_size = _private_tls_safe_read(context, (char *)client_message, sizeof(client_message))) > 0) { + if (tls_consume_stream(context, client_message, read_size, ssl_data->certificate_verify) >= 0) { + res = _tls_ssl_private_send_pending(ssl_data->fd, context); + if (res < 0) + return res; + } + if (tls_established(context)) + return 1; + if (context->critical_error) + return TLS_GENERIC_ERROR; + } + return read_size; +} + +int SSL_shutdown(struct TLSContext *context) { + if (!context) + return TLS_GENERIC_ERROR; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if ((!ssl_data) || (ssl_data->fd < 0)) + return TLS_GENERIC_ERROR; + + tls_close_notify(context); + return 0; +} + +int SSL_write(struct TLSContext *context, const void *buf, unsigned int len) { + if (!context) + return TLS_GENERIC_ERROR; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if ((!ssl_data) || (ssl_data->fd < 0)) + return TLS_GENERIC_ERROR; + + int written_size = tls_write(context, (const unsigned char *)buf, len); + if (written_size > 0) { + int res = _tls_ssl_private_send_pending(ssl_data->fd, context); + if (res <= 0) + return res; + } + return written_size; +} + +int SSL_read(struct TLSContext *context, void *buf, unsigned int len) { + if (!context) + return TLS_GENERIC_ERROR; + + if (context->application_buffer_len) + return tls_read(context, (unsigned char *)buf, len); + + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if ((!ssl_data) || (ssl_data->fd < 0) || (context->critical_error)) + return TLS_GENERIC_ERROR; + if (tls_established(context) != 1) + return TLS_GENERIC_ERROR; + + unsigned char client_message[0xFFFF]; + // accept + int read_size; + while ((!context->application_buffer_len) && ((read_size = _private_tls_safe_read(context, (char *)client_message, sizeof(client_message))) > 0)) { + if (tls_consume_stream(context, client_message, read_size, ssl_data->certificate_verify) > 0) + _tls_ssl_private_send_pending(ssl_data->fd, context); + + if ((context->critical_error) && (!context->application_buffer_len)) + return TLS_GENERIC_ERROR; + } + if ((read_size <= 0) && (!context->application_buffer_len)) + return read_size; + + return tls_read(context, (unsigned char *)buf, len); +} + +int SSL_pending(struct TLSContext *context) { + if (!context) + return TLS_GENERIC_ERROR; + return context->application_buffer_len; +} + +int SSL_set_io(struct TLSContext *context, void *recv_cb, void *send_cb) { + if (!context) + return TLS_GENERIC_ERROR; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if (!ssl_data) { + ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData)); + if (!ssl_data) + return TLS_NO_MEMORY; + memset(ssl_data, 0, sizeof(SSLUserData)); + context->user_data = ssl_data; + } + ssl_data->recv = recv_cb; + ssl_data->send = send_cb; + return 0; +} +#endif // SSL_COMPATIBLE_INTERFACE + + +#ifdef TLS_SRTP + +struct SRTPContext { + symmetric_CTR aes; + unsigned int salt[4]; + unsigned char mac[TLS_SHA1_MAC_SIZE]; + unsigned int tag_size; + unsigned int roc; + unsigned short seq; + + unsigned char mode; + unsigned char auth_mode; +}; + +struct SRTPContext *srtp_init(unsigned char mode, unsigned char auth_mode) { + struct SRTPContext *context = NULL; + tls_init(); + switch (mode) { + case SRTP_NULL: + break; + case SRTP_AES_CM: + break; + default: + return NULL; + } + + switch (auth_mode) { + case SRTP_AUTH_NULL: + break; + case SRTP_AUTH_HMAC_SHA1: + break; + default: + return NULL; + } + context = (struct SRTPContext *)TLS_MALLOC(sizeof(struct SRTPContext)); + if (context) { + memset(context, 0, sizeof(struct SRTPContext)); + context->mode = mode; + context->auth_mode = auth_mode; + } + return context; +} + +static int _private_tls_srtp_key_derive(const void *key, int keylen, const void *salt, unsigned char label, void *out, int outlen) { + unsigned char iv[16]; + memcpy(iv, salt, 14); + iv[14] = iv[15] = 0; + void *in = TLS_MALLOC(outlen); + if (!in) + return TLS_GENERIC_ERROR; + memset(in, 0, outlen); + + iv[7] ^= label; + + symmetric_CTR aes; + + if (ctr_start(find_cipher("aes"), iv, (const unsigned char *)key, keylen, 0, CTR_COUNTER_BIG_ENDIAN, &aes)) + return TLS_GENERIC_ERROR; + + ctr_encrypt((unsigned char *)in, (unsigned char *)out, outlen, &aes); + TLS_FREE(in); + ctr_done(&aes); + return 0; +} + +int srtp_key(struct SRTPContext *context, const void *key, int keylen, const void *salt, int saltlen, int tag_bits) { + if (!context) + return TLS_GENERIC_ERROR; + if (context->mode == SRTP_AES_CM) { + if ((saltlen < 14) || (keylen < 16)) + return TLS_GENERIC_ERROR; + // key + unsigned char key_buf[16]; + unsigned char iv[16]; + + memset(iv, 0, sizeof(iv)); + + if (_private_tls_srtp_key_derive(key, keylen, salt, 0, key_buf, sizeof(key_buf))) + return TLS_GENERIC_ERROR; + + DEBUG_DUMP_HEX_LABEL("KEY", key_buf, 16) + + if (_private_tls_srtp_key_derive(key, keylen, salt, 1, context->mac, 20)) + return TLS_GENERIC_ERROR; + + DEBUG_DUMP_HEX_LABEL("AUTH", context->mac, 20) + + memset(context->salt, 0, sizeof(context->salt)); + if (_private_tls_srtp_key_derive(key, keylen, salt, 2, context->salt, 14)) + return TLS_GENERIC_ERROR; + + DEBUG_DUMP_HEX_LABEL("SALT", ((unsigned char *)context->salt), 14) + + if (ctr_start(find_cipher("aes"), iv, key_buf, sizeof(key_buf), 0, CTR_COUNTER_BIG_ENDIAN, &context->aes)) + return TLS_GENERIC_ERROR; + } + if (context->auth_mode) + context->tag_size = tag_bits / 8; + return 0; +} + +int srtp_inline(struct SRTPContext *context, const char *b64, int tag_bits) { + char out_buffer[1024]; + + if (!b64) + return TLS_GENERIC_ERROR; + + int len = strlen(b64); + if (len >= sizeof(out_buffer)) + len = sizeof(out_buffer); + int size = _private_b64_decode(b64, len, (unsigned char *)out_buffer); + if (size <= 0) + return TLS_GENERIC_ERROR; + switch (context->mode) { + case SRTP_AES_CM: + if (size < 30) + return TLS_BROKEN_PACKET; + return srtp_key(context, out_buffer, 16, out_buffer + 16, 14, tag_bits); + } + return TLS_GENERIC_ERROR; +} + +int srtp_encrypt(struct SRTPContext *context, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len) { + if ((!context) || (!out) || (!out_buffer_len) || (*out_buffer_len < payload_len)) + return TLS_GENERIC_ERROR; + + int out_len = payload_len; + + unsigned short seq = 0; + unsigned int roc = context->roc; + unsigned int ssrc = 0; + + if ((pt_header) && (pt_len >= 12)) { + seq = ntohs(*((unsigned short *)&pt_header[2])); + ssrc = ntohl(*((unsigned long *)&pt_header[8])); + } + + if (seq < context->seq) + roc++; + + unsigned int roc_be = htonl(roc); + if (context->mode) { + if (*out_buffer_len < out_len) + return TLS_NO_MEMORY; + + unsigned int counter[4]; + counter[0] = context->salt[0]; + counter[1] = context->salt[1] ^ htonl (ssrc); + counter[2] = context->salt[2] ^ roc_be; + counter[3] = context->salt[3] ^ htonl (seq << 16); + ctr_setiv((unsigned char *)&counter, 16, &context->aes); + if (ctr_encrypt(payload, out, payload_len, &context->aes)) + return TLS_GENERIC_ERROR; + } else { + memcpy(out, payload, payload_len); + } + + *out_buffer_len = out_len; + + if (context->auth_mode == SRTP_AUTH_HMAC_SHA1) { + unsigned char digest_out[TLS_SHA1_MAC_SIZE]; + unsigned long dlen = TLS_SHA1_MAC_SIZE; + hmac_state hmac; + int err = hmac_init(&hmac, find_hash("sha1"), context->mac, 20); + if (!err) { + if (pt_len) + err = hmac_process(&hmac, pt_header, pt_len); + if (out_len) + err = hmac_process(&hmac, out, payload_len); + err = hmac_process(&hmac, (unsigned char *)&roc_be, 4); + if (!err) + err = hmac_done(&hmac, digest_out, &dlen); + } + if (err) + return TLS_GENERIC_ERROR; + if (dlen > context->tag_size) + dlen = context->tag_size; + + *out_buffer_len += dlen; + memcpy(out + out_len, digest_out, dlen); + } + context->roc = roc; + context->seq = seq; + return 0; +} + +int srtp_decrypt(struct SRTPContext *context, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len) { + if ((!context) || (!out) || (!out_buffer_len) || (*out_buffer_len < payload_len) || (payload_len < context->tag_size) || (!pt_header) || (pt_len < 12)) + return TLS_GENERIC_ERROR; + + int out_len = payload_len; + + unsigned short seq = ntohs(*((unsigned short *)&pt_header[2])); + unsigned int roc = context->roc; + unsigned int ssrc = ntohl(*((unsigned long *)&pt_header[8])); + + if (seq < context->seq) + roc++; + + unsigned int roc_be = htonl(roc); + if (context->mode) { + unsigned int counter[4]; + counter[0] = context->salt[0]; + counter[1] = context->salt[1] ^ htonl (ssrc); + counter[2] = context->salt[2] ^ roc_be; + counter[3] = context->salt[3] ^ htonl (seq << 16); + ctr_setiv((unsigned char *)&counter, 16, &context->aes); + + if (ctr_decrypt(payload, out, payload_len - context->tag_size, &context->aes)) + return TLS_GENERIC_ERROR; + + if (context->auth_mode == SRTP_AUTH_HMAC_SHA1) { + unsigned char digest_out[TLS_SHA1_MAC_SIZE]; + unsigned long dlen = TLS_SHA1_MAC_SIZE; + hmac_state hmac; + int err = hmac_init(&hmac, find_hash("sha1"), context->mac, 20); + if (!err) { + if (pt_len) + err = hmac_process(&hmac, pt_header, pt_len); + if (out_len) + err = hmac_process(&hmac, payload, payload_len - context->tag_size); + err = hmac_process(&hmac, (unsigned char *)&roc_be, 4); + if (!err) + err = hmac_done(&hmac, digest_out, &dlen); + } + if (err) + return TLS_GENERIC_ERROR; + if (dlen > context->tag_size) + dlen = context->tag_size; + + if (memcmp(digest_out, payload + payload_len - context->tag_size, dlen)) + return TLS_INTEGRITY_FAILED; + } + } else { + memcpy(out, payload, payload_len - context->tag_size); + } + context->seq = seq; + context->roc = roc; + *out_buffer_len = payload_len - context->tag_size; + return 0; +} + +void srtp_destroy(struct SRTPContext *context) { + if (context) { + if (context->mode) + ctr_done(&context->aes); + TLS_FREE(context); + } +} + +#endif // TLS_SRTP + +#endif // TLSE_C \ No newline at end of file diff --git a/thirdparty/cwsc/thirdparty/tlse.h b/thirdparty/cwsc/thirdparty/tlse.h new file mode 100644 index 0000000..86387b2 --- /dev/null +++ b/thirdparty/cwsc/thirdparty/tlse.h @@ -0,0 +1,430 @@ +#ifndef TLSE_H +#define TLSE_H + +// #define DEBUG + +// define TLS_LEGACY_SUPPORT to support TLS 1.1/1.0 (legacy) +// legacy support it will use an additional 272 bytes / context +#ifndef NO_TLS_LEGACY_SUPPORT +#define TLS_LEGACY_SUPPORT +#endif +// SSL_* style blocking APIs +#ifndef NO_SSL_COMPATIBLE_INTERFACE +#define SSL_COMPATIBLE_INTERFACE +#endif +// support ChaCha20/Poly1305 +#if !defined(__BIG_ENDIAN__) && ((!defined(__BYTE_ORDER)) || (__BYTE_ORDER == __LITTLE_ENDIAN)) + // not working on big endian machines + #ifndef NO_TLS_WITH_CHACHA20_POLY1305 + #define TLS_WITH_CHACHA20_POLY1305 + #endif +#endif +#ifndef NO_TLS_13 +#define WITH_TLS_13 +#endif +// support forward secrecy (Diffie-Hellman ephemeral) +#ifndef NO_TLS_FORWARD_SECRECY +#define TLS_FORWARD_SECRECY +#endif +// support client-side ECDHE +#ifndef NO_TLS_CLIENT_ECDHE +#define TLS_CLIENT_ECDHE +#endif +// suport ecdsa +#ifndef NO_TLS_ECDSA_SUPPORTED +#define TLS_ECDSA_SUPPORTED +#endif +// suport ecdsa client-side +// #define TLS_CLIENT_ECDSA +// TLS renegotiation is disabled by default (secured or not) +// do not uncomment next line! +// #define TLS_ACCEPT_SECURE_RENEGOTIATION +// basic superficial X509v1 certificate support +#ifndef NO_TLS_X509_V1_SUPPORT +#define TLS_X509_V1_SUPPORT +#endif + +// disable TLS_RSA_WITH_* ciphers +#ifndef NO_TLS_ROBOT_MITIGATION +#define TLS_ROBOT_MITIGATION +#endif + +#define SSL_V30 0x0300 +#define TLS_V10 0x0301 +#define TLS_V11 0x0302 +#define TLS_V12 0x0303 +#define TLS_V13 0x0304 +#define DTLS_V10 0xFEFF +#define DTLS_V12 0xFEFD +#define DTLS_V13 0xFEFC + +#define TLS_NEED_MORE_DATA 0 +#define TLS_GENERIC_ERROR -1 +#define TLS_BROKEN_PACKET -2 +#define TLS_NOT_UNDERSTOOD -3 +#define TLS_NOT_SAFE -4 +#define TLS_NO_COMMON_CIPHER -5 +#define TLS_UNEXPECTED_MESSAGE -6 +#define TLS_CLOSE_CONNECTION -7 +#define TLS_COMPRESSION_NOT_SUPPORTED -8 +#define TLS_NO_MEMORY -9 +#define TLS_NOT_VERIFIED -10 +#define TLS_INTEGRITY_FAILED -11 +#define TLS_ERROR_ALERT -12 +#define TLS_BROKEN_CONNECTION -13 +#define TLS_BAD_CERTIFICATE -14 +#define TLS_UNSUPPORTED_CERTIFICATE -15 +#define TLS_NO_RENEGOTIATION -16 +#define TLS_FEATURE_NOT_SUPPORTED -17 +#define TLS_DECRYPTION_FAILED -20 + +#define TLS_AES_128_GCM_SHA256 0x1301 +#define TLS_AES_256_GCM_SHA384 0x1302 +#define TLS_CHACHA20_POLY1305_SHA256 0x1303 +#define TLS_AES_128_CCM_SHA256 0x1304 +#define TLS_AES_128_CCM_8_SHA256 0x1305 + +#define TLS_RSA_WITH_AES_128_CBC_SHA 0x002F +#define TLS_RSA_WITH_AES_256_CBC_SHA 0x0035 +#define TLS_RSA_WITH_AES_128_CBC_SHA256 0x003C +#define TLS_RSA_WITH_AES_256_CBC_SHA256 0x003D +#define TLS_RSA_WITH_AES_128_GCM_SHA256 0x009C +#define TLS_RSA_WITH_AES_256_GCM_SHA384 0x009D + +// forward secrecy +#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0x0033 +#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x0039 +#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x0067 +#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x006B +#define TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 0x009E +#define TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 0x009F + +#define TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA 0xC013 +#define TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA 0xC014 +#define TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 0xC027 +#define TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 0xC02F +#define TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 0xC030 + +#define TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA 0xC009 +#define TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA 0xC00A +#define TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 0xC023 +#define TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 0xC024 +#define TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 0xC02B +#define TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 0xC02C + +#define TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA8 +#define TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA9 +#define TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCAA + +#define TLS_FALLBACK_SCSV 0x5600 + +#define TLS_UNSUPPORTED_ALGORITHM 0x00 +#define TLS_RSA_SIGN_RSA 0x01 +#define TLS_RSA_SIGN_MD5 0x04 +#define TLS_RSA_SIGN_SHA1 0x05 +#define TLS_RSA_SIGN_SHA256 0x0B +#define TLS_RSA_SIGN_SHA384 0x0C +#define TLS_RSA_SIGN_SHA512 0x0D +#define TLS_ECDSA_SIGN_SHA256 0x0E + +#define TLS_EC_PUBLIC_KEY 0x11 +#define TLS_EC_prime192v1 0x12 +#define TLS_EC_prime192v2 0x13 +#define TLS_EC_prime192v3 0x14 +#define TLS_EC_prime239v1 0x15 +#define TLS_EC_prime239v2 0x16 +#define TLS_EC_prime239v3 0x17 +#define TLS_EC_prime256v1 0x18 +#define TLS_EC_secp224r1 21 +#define TLS_EC_secp256r1 23 +#define TLS_EC_secp384r1 24 +#define TLS_EC_secp521r1 25 + +#define TLS_ALERT_WARNING 0x01 +#define TLS_ALERT_CRITICAL 0x02 + +#ifdef TLS_ROBOT_MITIGATION + #define TLS_CIPHERS_SIZE(n, mitigated) n * 2 +#else + #define TLS_CIPHERS_SIZE(n, mitigated) (n + mitigated) * 2 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + close_notify = 0, + unexpected_message = 10, + bad_record_mac = 20, + decryption_failed_RESERVED = 21, + record_overflow = 22, + decompression_failure = 30, + handshake_failure = 40, + no_certificate_RESERVED = 41, + bad_certificate = 42, + unsupported_certificate = 43, + certificate_revoked = 44, + certificate_expired = 45, + certificate_unknown = 46, + illegal_parameter = 47, + unknown_ca = 48, + access_denied = 49, + decode_error = 50, + decrypt_error = 51, + export_restriction_RESERVED = 60, + protocol_version = 70, + insufficient_security = 71, + internal_error = 80, + inappropriate_fallback = 86, + user_canceled = 90, + no_renegotiation = 100, + unsupported_extension = 110, + no_error = 255 +} TLSAlertDescription; + +// forward declarations +struct TLSPacket; +struct TLSCertificate; +struct TLSContext; +struct ECCCurveParameters; +typedef struct TLSContext TLS; +typedef struct TLSCertificate Certificate; + +typedef int (*tls_validation_function)(struct TLSContext *context, struct TLSCertificate **certificate_chain, int len); + +/* + Global initialization. Optional, as it will be called automatically; + however, the initialization is not thread-safe, so if you intend to use TLSe + from multiple threads, you'll need to call tls_init() once, from a single thread, + before using the library. + */ +void tls_init(); +unsigned char *tls_pem_decode(const unsigned char *data_in, unsigned int input_length, int cert_index, unsigned int *output_len); +struct TLSCertificate *tls_create_certificate(); +int tls_certificate_valid_subject(struct TLSCertificate *cert, const char *subject); +int tls_certificate_valid_subject_name(const unsigned char *cert_subject, const char *subject); +int tls_certificate_is_valid(struct TLSCertificate *cert); +void tls_certificate_set_copy(unsigned char **member, const unsigned char *val, int len); +void tls_certificate_set_copy_date(unsigned char **member, const unsigned char *val, int len); +void tls_certificate_set_key(struct TLSCertificate *cert, const unsigned char *val, int len); +void tls_certificate_set_priv(struct TLSCertificate *cert, const unsigned char *val, int len); +void tls_certificate_set_sign_key(struct TLSCertificate *cert, const unsigned char *val, int len); +char *tls_certificate_to_string(struct TLSCertificate *cert, char *buffer, int len); +void tls_certificate_set_exponent(struct TLSCertificate *cert, const unsigned char *val, int len); +void tls_certificate_set_serial(struct TLSCertificate *cert, const unsigned char *val, int len); +void tls_certificate_set_algorithm(struct TLSContext *context, unsigned int *algorithm, const unsigned char *val, int len); +void tls_destroy_certificate(struct TLSCertificate *cert); +struct TLSPacket *tls_create_packet(struct TLSContext *context, unsigned char type, unsigned short version, int payload_size_hint); +void tls_destroy_packet(struct TLSPacket *packet); +void tls_packet_update(struct TLSPacket *packet); +int tls_packet_append(struct TLSPacket *packet, const unsigned char *buf, unsigned int len); +int tls_packet_uint8(struct TLSPacket *packet, unsigned char i); +int tls_packet_uint16(struct TLSPacket *packet, unsigned short i); +int tls_packet_uint32(struct TLSPacket *packet, unsigned int i); +int tls_packet_uint24(struct TLSPacket *packet, unsigned int i); +int tls_random(unsigned char *key, int len); + +/* + Get encrypted data to write, if any. Once you've sent all of it, call + tls_buffer_clear(). + */ +const unsigned char *tls_get_write_buffer(struct TLSContext *context, unsigned int *outlen); + +void tls_buffer_clear(struct TLSContext *context); + +/* Returns 1 for established, 0 for not established yet, and -1 for a critical error. */ +int tls_established(struct TLSContext *context); + +/* Discards any unread decrypted data not consumed by tls_read(). */ +void tls_read_clear(struct TLSContext *context); + +/* + Reads any unread decrypted data (see tls_consume_stream). If you don't read all of it, + the remainder will be left in the internal buffers for next tls_read(). Returns -1 for + fatal error, 0 for no more data, or otherwise the number of bytes copied into the buffer + (up to a maximum of the given size). + */ +int tls_read(struct TLSContext *context, unsigned char *buf, unsigned int size); + +struct TLSContext *tls_create_context(unsigned char is_server, unsigned short version); +const struct ECCCurveParameters *tls_set_curve(struct TLSContext *context, const struct ECCCurveParameters *curve); + +/* Create a context for a given client, from a server context. Returns NULL on error. */ +struct TLSContext *tls_accept(struct TLSContext *context); + +int tls_set_default_dhe_pg(struct TLSContext *context, const char *p_hex_str, const char *g_hex_str); +void tls_destroy_context(struct TLSContext *context); +int tls_cipher_supported(struct TLSContext *context, unsigned short cipher); +int tls_cipher_is_fs(struct TLSContext *context, unsigned short cipher); +int tls_choose_cipher(struct TLSContext *context, const unsigned char *buf, int buf_len, int *scsv_set); +int tls_cipher_is_ephemeral(struct TLSContext *context); +const char *tls_cipher_name(struct TLSContext *context); +int tls_is_ecdsa(struct TLSContext *context); +struct TLSPacket *tls_build_client_key_exchange(struct TLSContext *context); +struct TLSPacket *tls_build_server_key_exchange(struct TLSContext *context, int method); +struct TLSPacket *tls_build_hello(struct TLSContext *context, int tls13_downgrade); +struct TLSPacket *tls_certificate_request(struct TLSContext *context); +struct TLSPacket *tls_build_verify_request(struct TLSContext *context); +int tls_parse_hello(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets, unsigned int *dtls_verified); +int tls_parse_certificate(struct TLSContext *context, const unsigned char *buf, int buf_len, int is_client); +int tls_parse_server_key_exchange(struct TLSContext *context, const unsigned char *buf, int buf_len); +int tls_parse_client_key_exchange(struct TLSContext *context, const unsigned char *buf, int buf_len); +int tls_parse_server_hello_done(struct TLSContext *context, const unsigned char *buf, int buf_len); +int tls_parse_finished(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets); +int tls_parse_verify(struct TLSContext *context, const unsigned char *buf, int buf_len); +int tls_parse_payload(struct TLSContext *context, const unsigned char *buf, int buf_len, tls_validation_function certificate_verify); +int tls_parse_message(struct TLSContext *context, unsigned char *buf, int buf_len, tls_validation_function certificate_verify); +int tls_certificate_verify_signature(struct TLSCertificate *cert, struct TLSCertificate *parent); +int tls_certificate_chain_is_valid(struct TLSCertificate **certificates, int len); +int tls_certificate_chain_is_valid_root(struct TLSContext *context, struct TLSCertificate **certificates, int len); + +/* + Add a certificate or a certificate chain to the given context, in PEM form. + Returns a negative value (TLS_GENERIC_ERROR etc.) on error, 0 if there were no + certificates in the buffer, or the number of loaded certificates on success. + */ +int tls_load_certificates(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size); + +/* + Add a private key to the given context, in PEM form. Returns a negative value + (TLS_GENERIC_ERROR etc.) on error, 0 if there was no private key in the + buffer, or 1 on success. + */ +int tls_load_private_key(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size); +struct TLSPacket *tls_build_certificate(struct TLSContext *context); +struct TLSPacket *tls_build_finished(struct TLSContext *context); +struct TLSPacket *tls_build_change_cipher_spec(struct TLSContext *context); +struct TLSPacket *tls_build_done(struct TLSContext *context); +struct TLSPacket *tls_build_message(struct TLSContext *context, const unsigned char *data, unsigned int len); +int tls_client_connect(struct TLSContext *context); +int tls_write(struct TLSContext *context, const unsigned char *data, unsigned int len); +struct TLSPacket *tls_build_alert(struct TLSContext *context, char critical, unsigned char code); + +/* + Process a given number of input bytes from a socket. If the other side just + presented a certificate and certificate_verify is not NULL, it will be called. + + Returns 0 if there's no data ready yet, a negative value (see + TLS_GENERIC_ERROR etc.) for an error, or a positive value (the number of bytes + used from buf) if one or more complete TLS messages were received. The data + is copied into an internal buffer even if not all of it was consumed, + so you should not re-send it the next time. + + Decrypted data, if any, should be read back with tls_read(). Can change the + status of tls_established(). If the library has anything to send back on the + socket (e.g. as part of the handshake), tls_get_write_buffer() will return + non-NULL. + */ +int tls_consume_stream(struct TLSContext *context, const unsigned char *buf, int buf_len, tls_validation_function certificate_verify); +void tls_close_notify(struct TLSContext *context); +void tls_alert(struct TLSContext *context, unsigned char critical, int code); + +/* Whether tls_consume_stream() has data in its buffer that is not processed yet. */ +int tls_pending(struct TLSContext *context); + +/* + Set the context as serializable or not. Must be called before negotiation. + Exportable contexts use a bit more memory, to be able to hold the keys. + + Note that imported keys are not reexportable unless TLS_REEXPORTABLE is set. + */ +void tls_make_exportable(struct TLSContext *context, unsigned char exportable_flag); + +int tls_export_context(struct TLSContext *context, unsigned char *buffer, unsigned int buf_len, unsigned char small_version); +struct TLSContext *tls_import_context(const unsigned char *buffer, unsigned int buf_len); +int tls_is_broken(struct TLSContext *context); +int tls_request_client_certificate(struct TLSContext *context); +int tls_client_verified(struct TLSContext *context); +const char *tls_sni(struct TLSContext *context); +int tls_sni_set(struct TLSContext *context, const char *sni); +int tls_load_root_certificates(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size); +int tls_default_verify(struct TLSContext *context, struct TLSCertificate **certificate_chain, int len); +void tls_print_certificate(const char *fname); +int tls_add_alpn(struct TLSContext *context, const char *alpn); +int tls_alpn_contains(struct TLSContext *context, const char *alpn, unsigned char alpn_size); +const char *tls_alpn(struct TLSContext *context); +// useful when renewing certificates for servers, without the need to restart the server +int tls_clear_certificates(struct TLSContext *context); +int tls_make_ktls(struct TLSContext *context, int socket); +int tls_unmake_ktls(struct TLSContext *context, int socket); +/* + Creates a new DTLS random cookie secret to be used in HelloVerifyRequest (server-side). + It is recommended to call this function from time to time, to protect against some + DoS attacks. +*/ +void dtls_reset_cookie_secret(); + +int tls_remote_error(struct TLSContext *context); + +#ifdef SSL_COMPATIBLE_INTERFACE + #define SSL_SERVER_RSA_CERT 1 + #define SSL_SERVER_RSA_KEY 2 + typedef struct TLSContext SSL_CTX; + typedef struct TLSContext SSL; + + #define SSL_FILETYPE_PEM 1 + #define SSL_VERIFY_NONE 0 + #define SSL_VERIFY_PEER 1 + #define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 2 + #define SSL_VERIFY_CLIENT_ONCE 3 + + typedef struct { + int fd; + tls_validation_function certificate_verify; + void *recv; + void *send; + void *user_data; + } SSLUserData; + + int SSL_library_init(); + void SSL_load_error_strings(); + void OpenSSL_add_all_algorithms(); + void OpenSSL_add_all_ciphers(); + void OpenSSL_add_all_digests(); + void EVP_cleanup(); + + int SSLv3_server_method(); + int SSLv3_client_method(); + struct TLSContext *SSL_new(struct TLSContext *context); + int SSL_CTX_use_certificate_file(struct TLSContext *context, const char *filename, int dummy); + int SSL_CTX_use_PrivateKey_file(struct TLSContext *context, const char *filename, int dummy); + int SSL_CTX_check_private_key(struct TLSContext *context); + struct TLSContext *SSL_CTX_new(int method); + void SSL_free(struct TLSContext *context); + void SSL_CTX_free(struct TLSContext *context); + int SSL_get_error(struct TLSContext *context, int ret); + int SSL_set_fd(struct TLSContext *context, int socket); + void *SSL_set_userdata(struct TLSContext *context, void *data); + void *SSL_userdata(struct TLSContext *context); + int SSL_CTX_root_ca(struct TLSContext *context, const char *pem_filename); + void SSL_CTX_set_verify(struct TLSContext *context, int mode, tls_validation_function verify_callback); + int SSL_accept(struct TLSContext *context); + int SSL_connect(struct TLSContext *context); + int SSL_shutdown(struct TLSContext *context); + int SSL_write(struct TLSContext *context, const void *buf, unsigned int len); + int SSL_read(struct TLSContext *context, void *buf, unsigned int len); + int SSL_pending(struct TLSContext *context); + int SSL_set_io(struct TLSContext *context, void *recv, void *send); +#endif + +#ifdef TLS_SRTP + struct SRTPContext; + #define SRTP_NULL 0 + #define SRTP_AES_CM 1 + #define SRTP_AUTH_NULL 0 + #define SRTP_AUTH_HMAC_SHA1 1 + + struct SRTPContext *srtp_init(unsigned char mode, unsigned char auth_mode); + int srtp_key(struct SRTPContext *context, const void *key, int keylen, const void *salt, int saltlen, int tag_bits); + int srtp_inline(struct SRTPContext *context, const char *b64, int tag_bits); + int srtp_encrypt(struct SRTPContext *context, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len); + int srtp_decrypt(struct SRTPContext *context, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len); + void srtp_destroy(struct SRTPContext *context); +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif \ No newline at end of file diff --git a/thirdparty/cwsc/thirdparty/tlse_adapter.c b/thirdparty/cwsc/thirdparty/tlse_adapter.c new file mode 100644 index 0000000..f7e4998 --- /dev/null +++ b/thirdparty/cwsc/thirdparty/tlse_adapter.c @@ -0,0 +1,151 @@ +#ifdef _WIN32 +#include +#else +#include +#endif + +#ifdef CWSC_TLSE_IMPLEMENTATION +#define TLS_AMALGAMATION +#define LTC_NO_ASM +#define NO_SSL_COMPATIBLE_INTERFACE +#define TLS_MALLOC CWSC_MALLOC +#define TLS_REALLOC CWSC_REALLOC +#define TLS_FREE CWSC_FREE +#include "tlse.c" +#else +#include "tlse.h" +#endif + +void cwsc_tlse_wrapper_error(char* msg) +{ + perror(msg); + exit(0); +} + +int cwsc_tlse_wrapper_send_pending(int client_sock, struct TLSContext* context) +{ + unsigned int out_buffer_len = 0; + const unsigned char* out_buffer = tls_get_write_buffer(context, &out_buffer_len); + unsigned int out_buffer_index = 0; + int send_res = 0; + while ((out_buffer) && (out_buffer_len > 0)) + { + int res = send(client_sock, (char*)&out_buffer[out_buffer_index], out_buffer_len, 0); + if (res <= 0) + { + send_res = res; + break; + } + out_buffer_len -= res; + out_buffer_index += res; + } + tls_buffer_clear(context); + return send_res; +} + +int cwsc_tlse_wrapper_validate_certificate(struct TLSContext* context, struct TLSCertificate** certificate_chain, int len) +{ + int i; + if (certificate_chain) + { + for (i = 0; i < len; i++) + { + struct TLSCertificate* certificate = certificate_chain[i]; + // TODO: Validate certificate + } + } + // return certificate_expired; + // return certificate_revoked; + // return certificate_unknown; + return no_error; +} + +int cwsc_tlse_wrapper_connect_socket(const char* host, int port) +{ + int sockfd; + struct sockaddr_in serv_addr; + struct hostent* server; +#ifdef _WIN32 + WSADATA wsaData; + WSAStartup(MAKEWORD(2, 2), &wsaData); +#else + signal(SIGPIPE, SIG_IGN); +#endif + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) + cwsc_tlse_wrapper_error("ERROR opening socket"); + server = gethostbyname(host); + if (server == NULL) + { + fprintf(stderr, "ERROR, no such host\n"); + exit(0); + } + memset((char*)&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + memcpy((char*)&serv_addr.sin_addr.s_addr, (char*)server->h_addr, server->h_length); + serv_addr.sin_port = htons(port); + if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) + cwsc_tlse_wrapper_error("ERROR connecting"); + return sockfd; +} + +int cwsc_tlse_wrapper_connect_tls(int sockfd, struct TLSContext* context) +{ + int res = tls_client_connect(context); + cwsc_tlse_wrapper_send_pending(sockfd, context); + unsigned char client_message[0xFFFF]; + for (;;) + { + int read_size = recv(sockfd, (char*)client_message, sizeof(client_message), 0); + tls_consume_stream(context, (const unsigned char*)client_message, read_size, cwsc_tlse_wrapper_validate_certificate); + cwsc_tlse_wrapper_send_pending(sockfd, context); + if (tls_established(context)) + { + break; + } + } + return res; +} + +int cwsc_tlse_wrapper_read_tls(int sockfd, struct TLSContext* context, void* buffer, int len) +{ + unsigned char client_message[0xFFFF]; + int read_res; + int read_size; + int read = 0; + for (;;) + { + if (tls_established(context)) + { + unsigned char read_buffer[len]; + read_res = tls_read(context, read_buffer, sizeof(read_buffer) - read); + if (read_res > 0) + { + memcpy(buffer + read, read_buffer, read_res); + read += read_res; + } + } + if (read >= len) + { + break; + } + read_size = recv(sockfd, (char*)client_message, sizeof(client_message), 0); + if (read_size <= 0) + { + break; + } + tls_consume_stream(context, (const unsigned char*)client_message, read_size, cwsc_tlse_wrapper_validate_certificate); + cwsc_tlse_wrapper_send_pending(sockfd, context); + } + return read; +} + +int cwsc_tlse_wrapper_write_tls(int sockfd, struct TLSContext* context, const unsigned char* data, int len) +{ + int write_res = tls_write(context, data, len); + if (write_res > 0) + { + cwsc_tlse_wrapper_send_pending(sockfd, context); + } + return write_res; +} \ No newline at end of file diff --git a/thirdparty/echttp/README.md b/thirdparty/echttp/README.md new file mode 100644 index 0000000..5a46c26 --- /dev/null +++ b/thirdparty/echttp/README.md @@ -0,0 +1,9 @@ +# ECHTTP +ECHTTP (pronounced somewhat like "easy HTTP" I'd say) is a simple single-file HTTP/HTTPS client library written in C. + +This implementation is roughly based on [http.h by Mattias Gustavsson](https://github.com/mattiasgustavsson/libs/blob/main/http.h), but has been refactored and improved, including **TLS support**, **more than just GET and POST requests**, and an **easier API**. + +It uses [TLSe](https://github.com/eduardsui/tlse) for TLS (previously known as SSL) support. + +## License +This project is licensed under the [MIT License](LICENSE). \ No newline at end of file diff --git a/thirdparty/echttp/echttp.h b/thirdparty/echttp/echttp.h new file mode 100644 index 0000000..342d56f --- /dev/null +++ b/thirdparty/echttp/echttp.h @@ -0,0 +1,606 @@ +#ifndef ECHTTP_H +#define ECHTTP_H + +#include + +typedef struct echttp_Header +{ + char const* name; + size_t value_count; + char const** values; +} echttp_Header; + +typedef struct echttp_Response +{ + void* _handle; // TODO: Type annotate this + int status_code; + char const* http_version; + char const* reason_phrase; + size_t header_count; + echttp_Header* headers; + size_t response_size; + char const* data; +} echttp_Response; + +echttp_Response echttp_request(char const* method, char const* url, char const* data, size_t data_size, echttp_Header* headers, size_t header_count); + +echttp_Response echttp_get(char const* url, echttp_Header* headers, size_t header_count); + +echttp_Response echttp_post(char const* url, char const* data, size_t data_size, echttp_Header* headers, size_t header_count); + +echttp_Response echttp_put(char const* url, char const* data, size_t data_size, echttp_Header* headers, size_t header_count); + +echttp_Response echttp_delete(char const* url, char const* data, size_t data_size, echttp_Header* headers, size_t header_count); + +echttp_Response echttp_patch(char const* url, char const* data, size_t data_size, echttp_Header* headers, size_t header_count); + +echttp_Response echttp_head(char const* url, char const* data, size_t data_size, echttp_Header* headers, size_t header_count); + +echttp_Response echttp_options(char const* url, char const* data, size_t data_size, echttp_Header* headers, size_t header_count); + +echttp_Response echttp_trace(char const* url, char const* data, size_t data_size, echttp_Header* headers, size_t header_count); + +echttp_Response echttp_connect(char const* url, char const* data, size_t data_size, echttp_Header* headers, size_t header_count); + +void echttp_release(echttp_Response response); + +#endif + +#ifdef ECHTTP_IMPLEMENTATION + +#ifdef _WIN32 +#define _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_SECURE_NO_WARNINGS +#pragma warning( push ) +#pragma warning( disable: 4127 ) // conditional expression is constant +#pragma warning( disable: 4255 ) // 'function' : no function prototype given: converting '()' to '(void)' +#pragma warning( disable: 4365 ) // 'action' : conversion from 'type_1' to 'type_2', signed/unsigned mismatch +#pragma warning( disable: 4574 ) // 'Identifier' is defined to be '0': did you mean to use '#if identifier'? +#pragma warning( disable: 4668 ) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directive' +#pragma warning( disable: 4706 ) // assignment within conditional expression +#include +#include +#pragma warning( pop ) +#pragma comment (lib, "Ws2_32.lib") +#include +#include +#define HTTP_SOCKET SOCKET +#define HTTP_INVALID_SOCKET INVALID_SOCKET +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define HTTP_SOCKET int +#define HTTP_INVALID_SOCKET -1 +#endif + +#ifndef ECHTTP_MALLOC +#define _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_SECURE_NO_WARNINGS +#include +#define ECHTTP_MALLOC malloc +#define ECHTTP_REALLOC realloc +#define ECHTTP_FREE free +#endif + +#include "thirdparty/tlse_adapter.c" + +typedef enum echttp_internal_Status +{ + HTTP_STATUS_PENDING, + HTTP_STATUS_COMPLETED, + HTTP_STATUS_FAILED, +} echttp_internal_Status; + +typedef struct echttp_internal_Request +{ + HTTP_SOCKET socket; + struct TLSContext* tls_context; + echttp_internal_Status status; + int status_code; + int connect_pending; + int request_sent; + char address[256]; + char request_header[256]; + char* request_header_large; + void* request_data; + size_t request_data_size; + char response_http_version[9]; + char response_reason_phrase[1024]; + size_t response_header_count; + echttp_Header* response_headers; + size_t response_data_size; + size_t response_data_capacity; + void* response_data; +} echttp_internal_Request; + +static int echttp_internal_parse_url(char const* url, char* address, size_t address_capacity, char* port, size_t port_capacity, char const** resource, char* is_https) +{ + // make sure url starts with http:// or https:// + if (strncmp(url, "http://", 7) != 0 && strncmp(url, "https://", 8) != 0) return 0; + *is_https = strncmp(url, "https://", 8) == 0; + url += *is_https ? 8 : 7; + + size_t url_len = strlen(url); + + // find end of address part of url + char const* address_end = strchr(url, ':'); + if (!address_end) address_end = strchr(url, '/'); + if (!address_end) address_end = url + url_len; + + // extract address + size_t address_len = (size_t)(address_end - url); + if (address_len >= address_capacity) return 0; + memcpy(address, url, address_len); + address[address_len] = 0; + + // check if there's a port defined + char const* port_end = address_end; + if (*address_end == ':') + { + ++address_end; + port_end = strchr(address_end, '/'); + if (!port_end) port_end = address_end + strlen(address_end); + size_t port_len = (size_t)(port_end - address_end); + if (port_len >= port_capacity) return 0; + memcpy(port, address_end, port_len); + port[port_len] = 0; + } + else + { + // use default port number 80 + if (port_capacity <= 2) return 0; + strcpy(port, *is_https ? "443" : "80"); + } + + *resource = port_end; + + if (strlen(*resource) == 0) + { + *resource = "/"; + } + + return 1; +} + +static echttp_internal_Request* echttp_internal_create_handle(size_t request_data_size) +{ + echttp_internal_Request* request = (echttp_internal_Request*)ECHTTP_MALLOC(sizeof(echttp_internal_Request) + request_data_size); + + request->status = HTTP_STATUS_PENDING; + request->status_code = 0; + + request->connect_pending = 1; + request->request_sent = 0; + + request->request_data = NULL; + request->request_data_size = 0; + + strcpy(request->response_http_version, ""); + + strcpy(request->response_reason_phrase, ""); + + request->response_header_count = 0; + request->response_headers = NULL; + + request->response_data_size = 0; + request->response_data_capacity = 64 * 1024; + request->response_data = ECHTTP_MALLOC(request->response_data_capacity); + + return request; +} + +echttp_internal_Request* echttp_build_request(char const* method, char const* url, void const* data, size_t size, echttp_Header* headers, size_t header_count) +{ +#ifdef _WIN32 + WSADATA wsa_data; + if (WSAStartup(MAKEWORD(1, 0), &wsa_data) != 0) return 0; +#endif + + char address[256]; + char port[16]; + char const* resource; + char is_https; + + if (echttp_internal_parse_url(url, address, sizeof(address), port, sizeof(port), &resource, &is_https) == 0) + return NULL; + + HTTP_SOCKET socket = echttp_tlse_wrapper_connect_socket(address, atoi(port)); + if (socket == HTTP_INVALID_SOCKET) return NULL; + + struct TLSContext* tls_context = NULL; + if (is_https) { + tls_context = tls_create_context(0, TLS_V12); + echttp_tlse_wrapper_connect_tls(socket, tls_context); + } + + echttp_internal_Request* request = echttp_internal_create_handle(size); + request->socket = socket; + request->tls_context = tls_context; + + char* request_header; + size_t request_header_len = 64 + strlen(resource) + strlen(address) + strlen(port); + for (size_t i = 0; i < header_count; i++) + { + request_header_len += strlen(headers[i].name) + 2; + for (size_t j = 0; j < headers[i].value_count; j++) + { + request_header_len += strlen(headers[i].values[j]) + 2; + } + } + if (request_header_len < sizeof(request->request_header)) + { + request->request_header_large = NULL; + request_header = request->request_header; + } + else + { + request->request_header_large = (char*)ECHTTP_MALLOC(request_header_len + 1); + request_header = request->request_header_large; + } + int default_http_port = (strcmp(port, "80") == 0); + char* uppercase_method = (char*)ECHTTP_MALLOC(strlen(method) + 1); + for (size_t i = 0; i < strlen(method); i++) + { + uppercase_method[i] = toupper(method[i]); + } + uppercase_method[strlen(method)] = '\0'; + sprintf(request_header, "%s %s HTTP/1.0\r\nHost: %s%s%s\r\nContent-Length: %d\r\n", uppercase_method, resource, address, default_http_port ? "" : ":", default_http_port ? "" : port, (int)size); + for (size_t i = 0; i < header_count; i++) + { + sprintf(request_header + strlen(request_header), "%s: ", headers[i].name); + for (size_t j = 0; j < headers[i].value_count; j++) + { + sprintf(request_header + strlen(request_header), "%s", headers[i].values[j]); + if (j + 1 < headers[i].value_count) + { + sprintf(request_header + strlen(request_header), ", "); + } + } + sprintf(request_header + strlen(request_header), "\r\n"); + } + sprintf(request_header + strlen(request_header), "\r\n"); + + request->request_data_size = size; + request->request_data = (request + 1); + memcpy(request->request_data, data, size); + + return request; +} + +echttp_internal_Status echttp_process_request(echttp_internal_Request* request) +{ + if (request->status == HTTP_STATUS_FAILED) return request->status; + + if (request->connect_pending) + { + fd_set sockets_to_check; + FD_ZERO(&sockets_to_check); +#pragma warning( push ) +#pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect + FD_SET(request->socket, &sockets_to_check); +#pragma warning( pop ) + struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; + // check if socket is ready for send + if (select((int)(request->socket + 1), NULL, &sockets_to_check, NULL, &timeout) == 1) + { + int opt = -1; + socklen_t len = sizeof(opt); + if (getsockopt(request->socket, SOL_SOCKET, SO_ERROR, (char*)(&opt), &len) >= 0 && opt == 0) + request->connect_pending = 0; // if it is, we're connected + } + } + + if (request->connect_pending) return request->status; + + if (!request->request_sent) + { + char* request_header = request->request_header_large ? request->request_header_large : request->request_header; + if ((request->tls_context == NULL ? send(request->socket, (const char*)request_header, (int)strlen(request_header), 0) : echttp_tlse_wrapper_write_tls(request->socket, request->tls_context, (const unsigned char*)request_header, (int)strlen(request_header))) == -1) + { + request->status = HTTP_STATUS_FAILED; + return request->status; + } + if (request->request_data_size) + { + int res = request->tls_context == NULL ? send(request->socket, (char const*)request->request_data, (int)request->request_data_size, 0) : echttp_tlse_wrapper_write_tls(request->socket, request->tls_context, (unsigned char*)request->request_data, (int)request->request_data_size); + if (res == -1) + { + request->status = HTTP_STATUS_FAILED; + return request->status; + } + } + request->request_sent = 1; + return request->status; + } + + // check if socket is ready for recv + fd_set sockets_to_check; + FD_ZERO(&sockets_to_check); +#pragma warning( push ) +#pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect + FD_SET(request->socket, &sockets_to_check); +#pragma warning( pop ) + struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; + while (select((int)(request->socket + 1), &sockets_to_check, NULL, NULL, &timeout) == 1) + { + unsigned char buffer[4096]; + int size = request->tls_context == NULL ? recv(request->socket, (char*)buffer, sizeof(buffer), 0) : echttp_tlse_wrapper_read_tls(request->socket, request->tls_context, buffer, sizeof(buffer)); + if (size == -1) + { + request->status = HTTP_STATUS_FAILED; + return request->status; + } + else if (size > 0) + { + size_t min_size = request->response_data_size + size + 1; + if (request->response_data_capacity < min_size) + { + request->response_data_capacity *= 2; + if (request->response_data_capacity < min_size) request->response_data_capacity = min_size; + void* new_data = ECHTTP_MALLOC(request->response_data_capacity); + memcpy(new_data, request->response_data, request->response_data_size); + ECHTTP_FREE(request->response_data); + request->response_data = new_data; + } + memcpy((void*)(((uintptr_t)request->response_data) + request->response_data_size), buffer, (size_t)size); + request->response_data_size += size; + } + else if (size == 0) + { + char const* status_line = (char const*)request->response_data; + + int header_size = 0; + char const* header_end = strstr(status_line, "\r\n\r\n"); + if (header_end) + { + header_end += 4; + header_size = (int)(header_end - status_line); + } + else + { + request->status = HTTP_STATUS_FAILED; + return request->status; + } + + memcpy(request->response_http_version, status_line, 8); + request->response_http_version[8] = 0; + status_line = strchr(status_line, ' '); + if (!status_line) + { + request->status = HTTP_STATUS_FAILED; + return request->status; + } + ++status_line; + + // extract status code + char status_code[16]; + char const* status_code_end = strchr(status_line, ' '); + if (!status_code_end) + { + request->status = HTTP_STATUS_FAILED; + return request->status; + } + memcpy(status_code, status_line, (size_t)(status_code_end - status_line)); + status_code[status_code_end - status_line] = 0; + status_line = status_code_end + 1; + request->status_code = atoi(status_code); + + // extract reason phrase + char const* reason_phrase_end = strstr(status_line, "\r\n"); + if (!reason_phrase_end) + { + request->status = HTTP_STATUS_FAILED; + return request->status; + } + size_t reason_phrase_len = (size_t)(reason_phrase_end - status_line); + if (reason_phrase_len >= sizeof(request->response_reason_phrase)) + reason_phrase_len = sizeof(request->response_reason_phrase) - 1; + memcpy(request->response_reason_phrase, status_line, reason_phrase_len); + request->response_reason_phrase[reason_phrase_len] = 0; + status_line = reason_phrase_end + 1; + + if ((status_line)-(char const*)request->response_data < header_size) + { + if (status_line[0] == '\n') status_line++; + + // extract headers + while (status_line[0] != '\r' && status_line[1] != '\n') + { + char const* header_end = strstr(status_line, "\r\n"); + if (!header_end) + { + request->status = HTTP_STATUS_FAILED; + return request->status; + } + size_t header_len = (size_t)(header_end - status_line); + if (header_len > 0) + { + if (request->response_header_count == 0) + { + request->response_headers = (echttp_Header*)ECHTTP_MALLOC(sizeof(echttp_Header)); + } + else + { + request->response_headers = (echttp_Header*)ECHTTP_REALLOC(request->response_headers, (request->response_header_count + 1) * sizeof(echttp_Header)); + } + echttp_Header header = *(request->response_headers + request->response_header_count); + size_t name_len = 0; + while (name_len < header_len && status_line[name_len] != ':') ++name_len; + header.name = (char const*)ECHTTP_MALLOC(name_len + 1); + memcpy((void*)header.name, status_line, name_len); + ((char*)header.name)[name_len] = 0; + status_line += name_len + 2; + size_t value_len = header_len - name_len - 2; + + header.values = (char const**)ECHTTP_MALLOC(sizeof(char const*)); + header.value_count = 0; + + char const* value_start = status_line; + long remaining_len = value_len; // long instead of size_t because it may be negative + while (remaining_len > 0) { + char const* semicolon_pos = strchr(value_start, ';'); + size_t single_value_len; + if (semicolon_pos && semicolon_pos < value_start + remaining_len) { + single_value_len = (size_t)(semicolon_pos - value_start); + } + else { + single_value_len = remaining_len; + } + + header.values = (char const**)ECHTTP_REALLOC(header.values, sizeof(char const*) * (header.value_count + 1)); + header.values[header.value_count] = (char const*)ECHTTP_MALLOC(single_value_len + 1); + memcpy((void*)header.values[header.value_count], value_start, single_value_len); + ((char*)header.values[header.value_count])[single_value_len] = 0; + header.value_count++; + + value_start += single_value_len + 1; + remaining_len -= single_value_len + 1; + } + + status_line += value_len; + request->response_headers[request->response_header_count] = header; + request->response_header_count++; + if ((status_line - (char const*)request->response_data) <= header_size - 2 && status_line[0] == '\r' && status_line[1] == '\n') { + status_line += 2; + } + } + else + { + status_line += 2; + } + } + } + + request->status = request->status_code < 300 ? HTTP_STATUS_COMPLETED : HTTP_STATUS_FAILED; + request->response_data = (void*)(((uintptr_t)request->response_data) + header_size); + request->response_data_size = request->response_data_size - header_size; + + // add an extra zero after the received data, but don't modify the size, so ascii results can be used as + // a zero terminated string. the size returned will be the string without this extra zero terminator. + ((char*)request->response_data)[request->response_data_size] = 0; + return request->status; + } + } + + return request->status; +} + +echttp_Response echttp_request(char const* method, char const* url, char const* data, size_t data_size, echttp_Header* headers, size_t header_count) +{ + echttp_Response response = { 0 }; + + echttp_internal_Request* request = echttp_build_request(method, url, data, data_size, headers, header_count); + response._handle = request; + if (!request) + { + response.status_code = -1; + response.reason_phrase = "Invalid request."; + return response; + } + + echttp_internal_Status status = HTTP_STATUS_PENDING; + while (status == HTTP_STATUS_PENDING) + { + status = echttp_process_request(request); + } + + if (request->status_code == 301 || request->status_code == 302 || request->status_code == 303 || request->status_code == 307 || request->status_code == 308) + { + if (request->response_header_count > 0) + { + for (size_t i = 0; i < request->response_header_count; i++) + { + if (strcmp(request->response_headers[i].name, "Location") == 0) + { + return echttp_request(method, request->response_headers[i].values[0], data, data_size, headers, header_count); // TODO: Limit the number of redirects + } + } + } + response.status_code = -1; + response.reason_phrase = "Invalid request."; + return response; + } + + response.status_code = request->status_code; + response.http_version = request->response_http_version; + response.reason_phrase = request->response_reason_phrase; + response.header_count = request->response_header_count; + response.headers = request->response_headers; + response.response_size = request->response_data_size; + response.data = (char*)request->response_data; + return response; +} + +echttp_Response echttp_get(char const* url, echttp_Header* headers, size_t header_count) +{ + return echttp_request("GET", url, NULL, 0, headers, header_count); +} + +echttp_Response echttp_post(char const* url, char const* data, size_t data_size, echttp_Header* headers, size_t header_count) +{ + return echttp_request("POST", url, data, data_size, headers, header_count); +} + +echttp_Response echttp_put(char const* url, char const* data, size_t data_size, echttp_Header* headers, size_t header_count) +{ + return echttp_request("PUT", url, data, data_size, headers, header_count); +} + +echttp_Response echttp_delete(char const* url, char const* data, size_t data_size, echttp_Header* headers, size_t header_count) +{ + return echttp_request("DELETE", url, data, data_size, headers, header_count); +} + +echttp_Response echttp_patch(char const* url, char const* data, size_t data_size, echttp_Header* headers, size_t header_count) +{ + return echttp_request("PATCH", url, data, data_size, headers, header_count); +} + +echttp_Response echttp_head(char const* url, char const* data, size_t data_size, echttp_Header* headers, size_t header_count) +{ + return echttp_request("HEAD", url, data, data_size, headers, header_count); +} + +echttp_Response echttp_options(char const* url, char const* data, size_t data_size, echttp_Header* headers, size_t header_count) +{ + return echttp_request("OPTIONS", url, data, data_size, headers, header_count); +} + +echttp_Response echttp_trace(char const* url, char const* data, size_t data_size, echttp_Header* headers, size_t header_count) +{ + return echttp_request("TRACE", url, data, data_size, headers, header_count); +} + +echttp_Response echttp_connect(char const* url, char const* data, size_t data_size, echttp_Header* headers, size_t header_count) +{ + return echttp_request("CONNECT", url, data, data_size, headers, header_count); +} + +void echttp_release(echttp_Response response) +{ + if (response.status_code != -1) + { + echttp_internal_Request* request = (echttp_internal_Request*)response._handle; +#ifdef _WIN32 + closesocket(request->socket); +#else + close(request->socket); +#endif + + if (request->request_header_large) ECHTTP_FREE(request->request_header_large); + ECHTTP_FREE(request->response_data); + ECHTTP_FREE(request); +#ifdef _WIN32 + WSACleanup(); +#endif + } +} + +#endif \ No newline at end of file diff --git a/thirdparty/echttp/thirdparty/libtomcrypt.c b/thirdparty/echttp/thirdparty/libtomcrypt.c new file mode 100644 index 0000000..20b931d --- /dev/null +++ b/thirdparty/echttp/thirdparty/libtomcrypt.c @@ -0,0 +1,34771 @@ +#define CRYPT 0x0117 +#define LTC_NO_ROLC + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://math.libtomcrypt.com + */ +#ifndef BN_H_ +#define BN_H_ + +#include +#include +#include +#include + +#if !(defined(LTM1) && defined(LTM2) && defined(LTM3)) + #if defined(LTM2) + #define LTM3 + #endif + #if defined(LTM1) + #define LTM2 + #endif + #define LTM1 + + #if defined(LTM_ALL) + #define BN_ERROR_C + #define BN_FAST_MP_INVMOD_C + #define BN_FAST_MP_MONTGOMERY_REDUCE_C + #define BN_FAST_S_MP_MUL_DIGS_C + #define BN_FAST_S_MP_MUL_HIGH_DIGS_C + #define BN_FAST_S_MP_SQR_C + #define BN_MP_2EXPT_C + #define BN_MP_ABS_C + #define BN_MP_ADD_C + #define BN_MP_ADD_D_C + #define BN_MP_ADDMOD_C + #define BN_MP_AND_C + #define BN_MP_CLAMP_C + #define BN_MP_CLEAR_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_CMP_C + #define BN_MP_CMP_D_C + #define BN_MP_CMP_MAG_C + #define BN_MP_CNT_LSB_C + #define BN_MP_COPY_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_C + #define BN_MP_DIV_2_C + #define BN_MP_DIV_2D_C + #define BN_MP_DIV_3_C + #define BN_MP_DIV_D_C + #define BN_MP_DR_IS_MODULUS_C + #define BN_MP_DR_REDUCE_C + #define BN_MP_DR_SETUP_C + #define BN_MP_EXCH_C + #define BN_MP_EXPORT_C + #define BN_MP_EXPT_D_C + #define BN_MP_EXPT_D_EX_C + #define BN_MP_EXPTMOD_C + #define BN_MP_EXPTMOD_FAST_C + #define BN_MP_EXTEUCLID_C + #define BN_MP_FREAD_C + #define BN_MP_FWRITE_C + #define BN_MP_GCD_C + #define BN_MP_GET_INT_C + #define BN_MP_GET_LONG_C + #define BN_MP_GET_LONG_LONG_C + #define BN_MP_GROW_C + #define BN_MP_IMPORT_C + #define BN_MP_INIT_C + #define BN_MP_INIT_COPY_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_INIT_SET_C + #define BN_MP_INIT_SET_INT_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_INVMOD_C + #define BN_MP_INVMOD_SLOW_C + #define BN_MP_IS_SQUARE_C + #define BN_MP_JACOBI_C + #define BN_MP_KARATSUBA_MUL_C + #define BN_MP_KARATSUBA_SQR_C + #define BN_MP_LCM_C + #define BN_MP_LSHD_C + #define BN_MP_MOD_C + #define BN_MP_MOD_2D_C + #define BN_MP_MOD_D_C + #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + #define BN_MP_MONTGOMERY_REDUCE_C + #define BN_MP_MONTGOMERY_SETUP_C + #define BN_MP_MUL_C + #define BN_MP_MUL_2_C + #define BN_MP_MUL_2D_C + #define BN_MP_MUL_D_C + #define BN_MP_MULMOD_C + #define BN_MP_N_ROOT_C + #define BN_MP_N_ROOT_EX_C + #define BN_MP_NEG_C + #define BN_MP_OR_C + #define BN_MP_PRIME_FERMAT_C + #define BN_MP_PRIME_IS_DIVISIBLE_C + #define BN_MP_PRIME_IS_PRIME_C + #define BN_MP_PRIME_MILLER_RABIN_C + #define BN_MP_PRIME_NEXT_PRIME_C + #define BN_MP_PRIME_RABIN_MILLER_TRIALS_C + #define BN_MP_PRIME_RANDOM_EX_C + #define BN_MP_RADIX_SIZE_C + #define BN_MP_RADIX_SMAP_C + #define BN_MP_RAND_C + #define BN_MP_READ_RADIX_C + #define BN_MP_READ_SIGNED_BIN_C + #define BN_MP_READ_UNSIGNED_BIN_C + #define BN_MP_REDUCE_C + #define BN_MP_REDUCE_2K_C + #define BN_MP_REDUCE_2K_L_C + #define BN_MP_REDUCE_2K_SETUP_C + #define BN_MP_REDUCE_2K_SETUP_L_C + #define BN_MP_REDUCE_IS_2K_C + #define BN_MP_REDUCE_IS_2K_L_C + #define BN_MP_REDUCE_SETUP_C + #define BN_MP_RSHD_C + #define BN_MP_SET_C + #define BN_MP_SET_INT_C + #define BN_MP_SET_LONG_C + #define BN_MP_SET_LONG_LONG_C + #define BN_MP_SHRINK_C + #define BN_MP_SIGNED_BIN_SIZE_C + #define BN_MP_SQR_C + #define BN_MP_SQRMOD_C + #define BN_MP_SQRT_C + #define BN_MP_SQRTMOD_PRIME_C + #define BN_MP_SUB_C + #define BN_MP_SUB_D_C + #define BN_MP_SUBMOD_C + #define BN_MP_TO_SIGNED_BIN_C + #define BN_MP_TO_SIGNED_BIN_N_C + #define BN_MP_TO_UNSIGNED_BIN_C + #define BN_MP_TO_UNSIGNED_BIN_N_C + #define BN_MP_TOOM_MUL_C + #define BN_MP_TOOM_SQR_C + #define BN_MP_TORADIX_C + #define BN_MP_TORADIX_N_C + #define BN_MP_UNSIGNED_BIN_SIZE_C + #define BN_MP_XOR_C + #define BN_MP_ZERO_C + #define BN_PRIME_TAB_C + #define BN_REVERSE_C + #define BN_S_MP_ADD_C + #define BN_S_MP_EXPTMOD_C + #define BN_S_MP_MUL_DIGS_C + #define BN_S_MP_MUL_HIGH_DIGS_C + #define BN_S_MP_SQR_C + #define BN_S_MP_SUB_C + #define BNCORE_C + #endif + + #if defined(BN_ERROR_C) + #define BN_MP_ERROR_TO_STRING_C + #endif + + #if defined(BN_FAST_MP_INVMOD_C) + #define BN_MP_ISEVEN_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_COPY_C + #define BN_MP_MOD_C + #define BN_MP_SET_C + #define BN_MP_DIV_2_C + #define BN_MP_ISODD_C + #define BN_MP_SUB_C + #define BN_MP_CMP_C + #define BN_MP_ISZERO_C + #define BN_MP_CMP_D_C + #define BN_MP_ADD_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_FAST_MP_MONTGOMERY_REDUCE_C) + #define BN_MP_GROW_C + #define BN_MP_RSHD_C + #define BN_MP_CLAMP_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_FAST_S_MP_MUL_DIGS_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_FAST_S_MP_SQR_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_2EXPT_C) + #define BN_MP_ZERO_C + #define BN_MP_GROW_C + #endif + + #if defined(BN_MP_ABS_C) + #define BN_MP_COPY_C + #endif + + #if defined(BN_MP_ADD_C) + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_ADD_D_C) + #define BN_MP_GROW_C + #define BN_MP_SUB_D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_ADDMOD_C) + #define BN_MP_INIT_C + #define BN_MP_ADD_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C + #endif + + #if defined(BN_MP_AND_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_CLAMP_C) + #endif + + #if defined(BN_MP_CLEAR_C) + #endif + + #if defined(BN_MP_CLEAR_MULTI_C) + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_CMP_C) + #define BN_MP_CMP_MAG_C + #endif + + #if defined(BN_MP_CMP_D_C) + #endif + + #if defined(BN_MP_CMP_MAG_C) + #endif + + #if defined(BN_MP_CNT_LSB_C) + #define BN_MP_ISZERO_C + #endif + + #if defined(BN_MP_COPY_C) + #define BN_MP_GROW_C + #endif + + #if defined(BN_MP_COUNT_BITS_C) + #endif + + #if defined(BN_MP_DIV_C) + #define BN_MP_ISZERO_C + #define BN_MP_CMP_MAG_C + #define BN_MP_COPY_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_SET_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_ABS_C + #define BN_MP_MUL_2D_C + #define BN_MP_CMP_C + #define BN_MP_SUB_C + #define BN_MP_ADD_C + #define BN_MP_DIV_2D_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_INIT_C + #define BN_MP_INIT_COPY_C + #define BN_MP_LSHD_C + #define BN_MP_RSHD_C + #define BN_MP_MUL_D_C + #define BN_MP_CLAMP_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_DIV_2_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_DIV_2D_C) + #define BN_MP_COPY_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_C + #define BN_MP_MOD_2D_C + #define BN_MP_CLEAR_C + #define BN_MP_RSHD_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #endif + + #if defined(BN_MP_DIV_3_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_DIV_D_C) + #define BN_MP_ISZERO_C + #define BN_MP_COPY_C + #define BN_MP_DIV_2D_C + #define BN_MP_DIV_3_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_DR_IS_MODULUS_C) + #endif + + #if defined(BN_MP_DR_REDUCE_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_DR_SETUP_C) + #endif + + #if defined(BN_MP_EXCH_C) + #endif + + #if defined(BN_MP_EXPORT_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_2D_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_EXPT_D_C) + #define BN_MP_EXPT_D_EX_C + #endif + + #if defined(BN_MP_EXPT_D_EX_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_SET_C + #define BN_MP_MUL_C + #define BN_MP_CLEAR_C + #define BN_MP_SQR_C + #endif + + #if defined(BN_MP_EXPTMOD_C) + #define BN_MP_INIT_C + #define BN_MP_INVMOD_C + #define BN_MP_CLEAR_C + #define BN_MP_ABS_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_REDUCE_IS_2K_L_C + #define BN_S_MP_EXPTMOD_C + #define BN_MP_DR_IS_MODULUS_C + #define BN_MP_REDUCE_IS_2K_C + #define BN_MP_ISODD_C + #define BN_MP_EXPTMOD_FAST_C + #endif + + #if defined(BN_MP_EXPTMOD_FAST_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_INIT_C + #define BN_MP_CLEAR_C + #define BN_MP_MONTGOMERY_SETUP_C + #define BN_FAST_MP_MONTGOMERY_REDUCE_C + #define BN_MP_MONTGOMERY_REDUCE_C + #define BN_MP_DR_SETUP_C + #define BN_MP_DR_REDUCE_C + #define BN_MP_REDUCE_2K_SETUP_C + #define BN_MP_REDUCE_2K_C + #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + #define BN_MP_MULMOD_C + #define BN_MP_SET_C + #define BN_MP_MOD_C + #define BN_MP_COPY_C + #define BN_MP_SQR_C + #define BN_MP_MUL_C + #define BN_MP_EXCH_C + #endif + + #if defined(BN_MP_EXTEUCLID_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_SET_C + #define BN_MP_COPY_C + #define BN_MP_ISZERO_C + #define BN_MP_DIV_C + #define BN_MP_MUL_C + #define BN_MP_SUB_C + #define BN_MP_NEG_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_FREAD_C) + #define BN_MP_ZERO_C + #define BN_MP_S_RMAP_C + #define BN_MP_MUL_D_C + #define BN_MP_ADD_D_C + #define BN_MP_CMP_D_C + #endif + + #if defined(BN_MP_FWRITE_C) + #define BN_MP_RADIX_SIZE_C + #define BN_MP_TORADIX_C + #endif + + #if defined(BN_MP_GCD_C) + #define BN_MP_ISZERO_C + #define BN_MP_ABS_C + #define BN_MP_INIT_COPY_C + #define BN_MP_CNT_LSB_C + #define BN_MP_DIV_2D_C + #define BN_MP_CMP_MAG_C + #define BN_MP_EXCH_C + #define BN_S_MP_SUB_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_GET_INT_C) + #endif + + #if defined(BN_MP_GET_LONG_C) + #endif + + #if defined(BN_MP_GET_LONG_LONG_C) + #endif + + #if defined(BN_MP_GROW_C) + #endif + + #if defined(BN_MP_IMPORT_C) + #define BN_MP_ZERO_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_INIT_C) + #endif + + #if defined(BN_MP_INIT_COPY_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_COPY_C + #endif + + #if defined(BN_MP_INIT_MULTI_C) + #define BN_MP_ERR_C + #define BN_MP_INIT_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_INIT_SET_C) + #define BN_MP_INIT_C + #define BN_MP_SET_C + #endif + + #if defined(BN_MP_INIT_SET_INT_C) + #define BN_MP_INIT_C + #define BN_MP_SET_INT_C + #endif + + #if defined(BN_MP_INIT_SIZE_C) + #define BN_MP_INIT_C + #endif + + #if defined(BN_MP_INVMOD_C) + #define BN_MP_ISZERO_C + #define BN_MP_ISODD_C + #define BN_FAST_MP_INVMOD_C + #define BN_MP_INVMOD_SLOW_C + #endif + + #if defined(BN_MP_INVMOD_SLOW_C) + #define BN_MP_ISZERO_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_C + #define BN_MP_COPY_C + #define BN_MP_ISEVEN_C + #define BN_MP_SET_C + #define BN_MP_DIV_2_C + #define BN_MP_ISODD_C + #define BN_MP_ADD_C + #define BN_MP_SUB_C + #define BN_MP_CMP_C + #define BN_MP_CMP_D_C + #define BN_MP_CMP_MAG_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_IS_SQUARE_C) + #define BN_MP_MOD_D_C + #define BN_MP_INIT_SET_INT_C + #define BN_MP_MOD_C + #define BN_MP_GET_INT_C + #define BN_MP_SQRT_C + #define BN_MP_SQR_C + #define BN_MP_CMP_MAG_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_JACOBI_C) + #define BN_MP_CMP_D_C + #define BN_MP_ISZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_CNT_LSB_C + #define BN_MP_DIV_2D_C + #define BN_MP_MOD_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_KARATSUBA_MUL_C) + #define BN_MP_MUL_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_S_MP_ADD_C + #define BN_MP_ADD_C + #define BN_S_MP_SUB_C + #define BN_MP_LSHD_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_KARATSUBA_SQR_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_SQR_C + #define BN_S_MP_ADD_C + #define BN_S_MP_SUB_C + #define BN_MP_LSHD_C + #define BN_MP_ADD_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_LCM_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_GCD_C + #define BN_MP_CMP_MAG_C + #define BN_MP_DIV_C + #define BN_MP_MUL_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_LSHD_C) + #define BN_MP_GROW_C + #define BN_MP_RSHD_C + #endif + + #if defined(BN_MP_MOD_C) + #define BN_MP_INIT_C + #define BN_MP_DIV_C + #define BN_MP_CLEAR_C + #define BN_MP_ISZERO_C + #define BN_MP_EXCH_C + #define BN_MP_ADD_C + #endif + + #if defined(BN_MP_MOD_2D_C) + #define BN_MP_ZERO_C + #define BN_MP_COPY_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_MOD_D_C) + #define BN_MP_DIV_D_C + #endif + + #if defined(BN_MP_MONTGOMERY_CALC_NORMALIZATION_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_2EXPT_C + #define BN_MP_SET_C + #define BN_MP_MUL_2_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_MONTGOMERY_REDUCE_C) + #define BN_FAST_MP_MONTGOMERY_REDUCE_C + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #define BN_MP_RSHD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_MONTGOMERY_SETUP_C) + #endif + + #if defined(BN_MP_MUL_C) + #define BN_MP_TOOM_MUL_C + #define BN_MP_KARATSUBA_MUL_C + #define BN_FAST_S_MP_MUL_DIGS_C + #define BN_S_MP_MUL_C + #define BN_S_MP_MUL_DIGS_C + #endif + + #if defined(BN_MP_MUL_2_C) + #define BN_MP_GROW_C + #endif + + #if defined(BN_MP_MUL_2D_C) + #define BN_MP_COPY_C + #define BN_MP_GROW_C + #define BN_MP_LSHD_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_MUL_D_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_MULMOD_C) + #define BN_MP_INIT_C + #define BN_MP_MUL_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C + #endif + + #if defined(BN_MP_N_ROOT_C) + #define BN_MP_N_ROOT_EX_C + #endif + + #if defined(BN_MP_N_ROOT_EX_C) + #define BN_MP_INIT_C + #define BN_MP_SET_C + #define BN_MP_COPY_C + #define BN_MP_EXPT_D_EX_C + #define BN_MP_MUL_C + #define BN_MP_SUB_C + #define BN_MP_MUL_D_C + #define BN_MP_DIV_C + #define BN_MP_CMP_C + #define BN_MP_SUB_D_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_NEG_C) + #define BN_MP_COPY_C + #define BN_MP_ISZERO_C + #endif + + #if defined(BN_MP_OR_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_FERMAT_C) + #define BN_MP_CMP_D_C + #define BN_MP_INIT_C + #define BN_MP_EXPTMOD_C + #define BN_MP_CMP_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_IS_DIVISIBLE_C) + #define BN_MP_MOD_D_C + #endif + + #if defined(BN_MP_PRIME_IS_PRIME_C) + #define BN_MP_CMP_D_C + #define BN_MP_PRIME_IS_DIVISIBLE_C + #define BN_MP_INIT_C + #define BN_MP_SET_C + #define BN_MP_PRIME_MILLER_RABIN_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_MILLER_RABIN_C) + #define BN_MP_CMP_D_C + #define BN_MP_INIT_COPY_C + #define BN_MP_SUB_D_C + #define BN_MP_CNT_LSB_C + #define BN_MP_DIV_2D_C + #define BN_MP_EXPTMOD_C + #define BN_MP_CMP_C + #define BN_MP_SQRMOD_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_NEXT_PRIME_C) + #define BN_MP_CMP_D_C + #define BN_MP_SET_C + #define BN_MP_SUB_D_C + #define BN_MP_ISEVEN_C + #define BN_MP_MOD_D_C + #define BN_MP_INIT_C + #define BN_MP_ADD_D_C + #define BN_MP_PRIME_MILLER_RABIN_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_RABIN_MILLER_TRIALS_C) + #endif + + #if defined(BN_MP_PRIME_RANDOM_EX_C) + #define BN_MP_READ_UNSIGNED_BIN_C + #define BN_MP_PRIME_IS_PRIME_C + #define BN_MP_SUB_D_C + #define BN_MP_DIV_2_C + #define BN_MP_MUL_2_C + #define BN_MP_ADD_D_C + #endif + + #if defined(BN_MP_RADIX_SIZE_C) + #define BN_MP_ISZERO_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_INIT_COPY_C + #define BN_MP_DIV_D_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_RADIX_SMAP_C) + #define BN_MP_S_RMAP_C + #endif + + #if defined(BN_MP_RAND_C) + #define BN_MP_ZERO_C + #define BN_MP_ADD_D_C + #define BN_MP_LSHD_C + #endif + + #if defined(BN_MP_READ_RADIX_C) + #define BN_MP_ZERO_C + #define BN_MP_S_RMAP_C + #define BN_MP_MUL_D_C + #define BN_MP_ADD_D_C + #define BN_MP_ISZERO_C + #endif + + #if defined(BN_MP_READ_SIGNED_BIN_C) + #define BN_MP_READ_UNSIGNED_BIN_C + #endif + + #if defined(BN_MP_READ_UNSIGNED_BIN_C) + #define BN_MP_GROW_C + #define BN_MP_ZERO_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_REDUCE_C) + #define BN_MP_REDUCE_SETUP_C + #define BN_MP_INIT_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_MUL_C + #define BN_S_MP_MUL_HIGH_DIGS_C + #define BN_FAST_S_MP_MUL_HIGH_DIGS_C + #define BN_MP_MOD_2D_C + #define BN_S_MP_MUL_DIGS_C + #define BN_MP_SUB_C + #define BN_MP_CMP_D_C + #define BN_MP_SET_C + #define BN_MP_LSHD_C + #define BN_MP_ADD_C + #define BN_MP_CMP_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_REDUCE_2K_C) + #define BN_MP_INIT_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_2D_C + #define BN_MP_MUL_D_C + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_REDUCE_2K_L_C) + #define BN_MP_INIT_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_2D_C + #define BN_MP_MUL_C + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_REDUCE_2K_SETUP_C) + #define BN_MP_INIT_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_2EXPT_C + #define BN_MP_CLEAR_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_REDUCE_2K_SETUP_L_C) + #define BN_MP_INIT_C + #define BN_MP_2EXPT_C + #define BN_MP_COUNT_BITS_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_REDUCE_IS_2K_C) + #define BN_MP_REDUCE_2K_C + #define BN_MP_COUNT_BITS_C + #endif + + #if defined(BN_MP_REDUCE_IS_2K_L_C) + #endif + + #if defined(BN_MP_REDUCE_SETUP_C) + #define BN_MP_2EXPT_C + #define BN_MP_DIV_C + #endif + + #if defined(BN_MP_RSHD_C) + #define BN_MP_ZERO_C + #endif + + #if defined(BN_MP_SET_C) + #define BN_MP_ZERO_C + #endif + + #if defined(BN_MP_SET_INT_C) + #define BN_MP_ZERO_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_SET_LONG_C) + #endif + + #if defined(BN_MP_SET_LONG_LONG_C) + #endif + + #if defined(BN_MP_SHRINK_C) + #endif + + #if defined(BN_MP_SIGNED_BIN_SIZE_C) + #define BN_MP_UNSIGNED_BIN_SIZE_C + #endif + + #if defined(BN_MP_SQR_C) + #define BN_MP_TOOM_SQR_C + #define BN_MP_KARATSUBA_SQR_C + #define BN_FAST_S_MP_SQR_C + #define BN_S_MP_SQR_C + #endif + + #if defined(BN_MP_SQRMOD_C) + #define BN_MP_INIT_C + #define BN_MP_SQR_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C + #endif + + #if defined(BN_MP_SQRT_C) + #define BN_MP_N_ROOT_C + #define BN_MP_ISZERO_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_DIV_C + #define BN_MP_ADD_C + #define BN_MP_DIV_2_C + #define BN_MP_CMP_MAG_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_SQRTMOD_PRIME_C) + #define BN_MP_CMP_D_C + #define BN_MP_ZERO_C + #define BN_MP_JACOBI_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_D_C + #define BN_MP_ADD_D_C + #define BN_MP_DIV_2_C + #define BN_MP_EXPTMOD_C + #define BN_MP_COPY_C + #define BN_MP_SUB_D_C + #define BN_MP_ISEVEN_C + #define BN_MP_SET_INT_C + #define BN_MP_SQRMOD_C + #define BN_MP_MULMOD_C + #define BN_MP_SET_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_SUB_C) + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_SUB_D_C) + #define BN_MP_GROW_C + #define BN_MP_ADD_D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_SUBMOD_C) + #define BN_MP_INIT_C + #define BN_MP_SUB_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C + #endif + + #if defined(BN_MP_TO_SIGNED_BIN_C) + #define BN_MP_TO_UNSIGNED_BIN_C + #endif + + #if defined(BN_MP_TO_SIGNED_BIN_N_C) + #define BN_MP_SIGNED_BIN_SIZE_C + #define BN_MP_TO_SIGNED_BIN_C + #endif + + #if defined(BN_MP_TO_UNSIGNED_BIN_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_ISZERO_C + #define BN_MP_DIV_2D_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_TO_UNSIGNED_BIN_N_C) + #define BN_MP_UNSIGNED_BIN_SIZE_C + #define BN_MP_TO_UNSIGNED_BIN_C + #endif + + #if defined(BN_MP_TOOM_MUL_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_2D_C + #define BN_MP_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_MUL_C + #define BN_MP_MUL_2_C + #define BN_MP_ADD_C + #define BN_MP_SUB_C + #define BN_MP_DIV_2_C + #define BN_MP_MUL_2D_C + #define BN_MP_MUL_D_C + #define BN_MP_DIV_3_C + #define BN_MP_LSHD_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_TOOM_SQR_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_2D_C + #define BN_MP_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_SQR_C + #define BN_MP_MUL_2_C + #define BN_MP_ADD_C + #define BN_MP_SUB_C + #define BN_MP_DIV_2_C + #define BN_MP_MUL_2D_C + #define BN_MP_MUL_D_C + #define BN_MP_DIV_3_C + #define BN_MP_LSHD_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_TORADIX_C) + #define BN_MP_ISZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_DIV_D_C + #define BN_MP_CLEAR_C + #define BN_MP_S_RMAP_C + #endif + + #if defined(BN_MP_TORADIX_N_C) + #define BN_MP_ISZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_DIV_D_C + #define BN_MP_CLEAR_C + #define BN_MP_S_RMAP_C + #endif + + #if defined(BN_MP_UNSIGNED_BIN_SIZE_C) + #define BN_MP_COUNT_BITS_C + #endif + + #if defined(BN_MP_XOR_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_ZERO_C) + #endif + + #if defined(BN_PRIME_TAB_C) + #endif + + #if defined(BN_REVERSE_C) + #endif + + #if defined(BN_S_MP_ADD_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_S_MP_EXPTMOD_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_INIT_C + #define BN_MP_CLEAR_C + #define BN_MP_REDUCE_SETUP_C + #define BN_MP_REDUCE_C + #define BN_MP_REDUCE_2K_SETUP_L_C + #define BN_MP_REDUCE_2K_L_C + #define BN_MP_MOD_C + #define BN_MP_COPY_C + #define BN_MP_SQR_C + #define BN_MP_MUL_C + #define BN_MP_SET_C + #define BN_MP_EXCH_C + #endif + + #if defined(BN_S_MP_MUL_DIGS_C) + #define BN_FAST_S_MP_MUL_DIGS_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_S_MP_MUL_HIGH_DIGS_C) + #define BN_FAST_S_MP_MUL_HIGH_DIGS_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_S_MP_SQR_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_S_MP_SUB_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BNCORE_C) + #endif + + #ifdef LTM3 + #define LTM_LAST + #endif +/* super class file for PK algos */ + +/* default ... include all MPI */ +#define LTM_ALL + +/* RSA only (does not support DH/DSA/ECC) */ +/* #define SC_RSA_1 */ + +/* For reference.... On an Athlon64 optimizing for speed... + + LTM's mpi.o with all functions [striped] is 142KiB in size. + + */ + +/* Works for RSA only, mpi.o is 68KiB */ +#ifdef SC_RSA_1 + #define BN_MP_SHRINK_C + #define BN_MP_LCM_C + #define BN_MP_PRIME_RANDOM_EX_C + #define BN_MP_INVMOD_C + #define BN_MP_GCD_C + #define BN_MP_MOD_C + #define BN_MP_MULMOD_C + #define BN_MP_ADDMOD_C + #define BN_MP_EXPTMOD_C + #define BN_MP_SET_INT_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_UNSIGNED_BIN_SIZE_C + #define BN_MP_TO_UNSIGNED_BIN_C + #define BN_MP_MOD_D_C + #define BN_MP_PRIME_RABIN_MILLER_TRIALS_C + #define BN_REVERSE_C + #define BN_PRIME_TAB_C + +/* other modifiers */ + #define BN_MP_DIV_SMALL /* Slower division, not critical */ + +/* here we are on the last pass so we turn things off. The functions classes are still there + * but we remove them specifically from the build. This also invokes tweaks in functions + * like removing support for even moduli, etc... + */ + #ifdef LTM_LAST + #undef BN_MP_TOOM_MUL_C + #undef BN_MP_TOOM_SQR_C + #undef BN_MP_KARATSUBA_MUL_C + #undef BN_MP_KARATSUBA_SQR_C + #undef BN_MP_REDUCE_C + #undef BN_MP_REDUCE_SETUP_C + #undef BN_MP_DR_IS_MODULUS_C + #undef BN_MP_DR_SETUP_C + #undef BN_MP_DR_REDUCE_C + #undef BN_MP_REDUCE_IS_2K_C + #undef BN_MP_REDUCE_2K_SETUP_C + #undef BN_MP_REDUCE_2K_C + #undef BN_S_MP_EXPTMOD_C + #undef BN_MP_DIV_3_C + #undef BN_S_MP_MUL_HIGH_DIGS_C + #undef BN_FAST_S_MP_MUL_HIGH_DIGS_C + #undef BN_FAST_MP_INVMOD_C + +/* To safely undefine these you have to make sure your RSA key won't exceed the Comba threshold + * which is roughly 255 digits [7140 bits for 32-bit machines, 15300 bits for 64-bit machines] + * which means roughly speaking you can handle upto 2536-bit RSA keys with these defined without + * trouble. + */ + #undef BN_S_MP_MUL_DIGS_C + #undef BN_S_MP_SQR_C + #undef BN_MP_MONTGOMERY_REDUCE_C + #endif +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ +#if !(defined(LTM1) && defined(LTM2) && defined(LTM3)) + #if defined(LTM2) + #define LTM3 + #endif + #if defined(LTM1) + #define LTM2 + #endif + #define LTM1 + + #if defined(LTM_ALL) + #define BN_ERROR_C + #define BN_FAST_MP_INVMOD_C + #define BN_FAST_MP_MONTGOMERY_REDUCE_C + #define BN_FAST_S_MP_MUL_DIGS_C + #define BN_FAST_S_MP_MUL_HIGH_DIGS_C + #define BN_FAST_S_MP_SQR_C + #define BN_MP_2EXPT_C + #define BN_MP_ABS_C + #define BN_MP_ADD_C + #define BN_MP_ADD_D_C + #define BN_MP_ADDMOD_C + #define BN_MP_AND_C + #define BN_MP_CLAMP_C + #define BN_MP_CLEAR_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_CMP_C + #define BN_MP_CMP_D_C + #define BN_MP_CMP_MAG_C + #define BN_MP_CNT_LSB_C + #define BN_MP_COPY_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_C + #define BN_MP_DIV_2_C + #define BN_MP_DIV_2D_C + #define BN_MP_DIV_3_C + #define BN_MP_DIV_D_C + #define BN_MP_DR_IS_MODULUS_C + #define BN_MP_DR_REDUCE_C + #define BN_MP_DR_SETUP_C + #define BN_MP_EXCH_C + #define BN_MP_EXPORT_C + #define BN_MP_EXPT_D_C + #define BN_MP_EXPT_D_EX_C + #define BN_MP_EXPTMOD_C + #define BN_MP_EXPTMOD_FAST_C + #define BN_MP_EXTEUCLID_C + #define BN_MP_FREAD_C + #define BN_MP_FWRITE_C + #define BN_MP_GCD_C + #define BN_MP_GET_INT_C + #define BN_MP_GET_LONG_C + #define BN_MP_GET_LONG_LONG_C + #define BN_MP_GROW_C + #define BN_MP_IMPORT_C + #define BN_MP_INIT_C + #define BN_MP_INIT_COPY_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_INIT_SET_C + #define BN_MP_INIT_SET_INT_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_INVMOD_C + #define BN_MP_INVMOD_SLOW_C + #define BN_MP_IS_SQUARE_C + #define BN_MP_JACOBI_C + #define BN_MP_KARATSUBA_MUL_C + #define BN_MP_KARATSUBA_SQR_C + #define BN_MP_LCM_C + #define BN_MP_LSHD_C + #define BN_MP_MOD_C + #define BN_MP_MOD_2D_C + #define BN_MP_MOD_D_C + #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + #define BN_MP_MONTGOMERY_REDUCE_C + #define BN_MP_MONTGOMERY_SETUP_C + #define BN_MP_MUL_C + #define BN_MP_MUL_2_C + #define BN_MP_MUL_2D_C + #define BN_MP_MUL_D_C + #define BN_MP_MULMOD_C + #define BN_MP_N_ROOT_C + #define BN_MP_N_ROOT_EX_C + #define BN_MP_NEG_C + #define BN_MP_OR_C + #define BN_MP_PRIME_FERMAT_C + #define BN_MP_PRIME_IS_DIVISIBLE_C + #define BN_MP_PRIME_IS_PRIME_C + #define BN_MP_PRIME_MILLER_RABIN_C + #define BN_MP_PRIME_NEXT_PRIME_C + #define BN_MP_PRIME_RABIN_MILLER_TRIALS_C + #define BN_MP_PRIME_RANDOM_EX_C + #define BN_MP_RADIX_SIZE_C + #define BN_MP_RADIX_SMAP_C + #define BN_MP_RAND_C + #define BN_MP_READ_RADIX_C + #define BN_MP_READ_SIGNED_BIN_C + #define BN_MP_READ_UNSIGNED_BIN_C + #define BN_MP_REDUCE_C + #define BN_MP_REDUCE_2K_C + #define BN_MP_REDUCE_2K_L_C + #define BN_MP_REDUCE_2K_SETUP_C + #define BN_MP_REDUCE_2K_SETUP_L_C + #define BN_MP_REDUCE_IS_2K_C + #define BN_MP_REDUCE_IS_2K_L_C + #define BN_MP_REDUCE_SETUP_C + #define BN_MP_RSHD_C + #define BN_MP_SET_C + #define BN_MP_SET_INT_C + #define BN_MP_SET_LONG_C + #define BN_MP_SET_LONG_LONG_C + #define BN_MP_SHRINK_C + #define BN_MP_SIGNED_BIN_SIZE_C + #define BN_MP_SQR_C + #define BN_MP_SQRMOD_C + #define BN_MP_SQRT_C + #define BN_MP_SQRTMOD_PRIME_C + #define BN_MP_SUB_C + #define BN_MP_SUB_D_C + #define BN_MP_SUBMOD_C + #define BN_MP_TO_SIGNED_BIN_C + #define BN_MP_TO_SIGNED_BIN_N_C + #define BN_MP_TO_UNSIGNED_BIN_C + #define BN_MP_TO_UNSIGNED_BIN_N_C + #define BN_MP_TOOM_MUL_C + #define BN_MP_TOOM_SQR_C + #define BN_MP_TORADIX_C + #define BN_MP_TORADIX_N_C + #define BN_MP_UNSIGNED_BIN_SIZE_C + #define BN_MP_XOR_C + #define BN_MP_ZERO_C + #define BN_PRIME_TAB_C + #define BN_REVERSE_C + #define BN_S_MP_ADD_C + #define BN_S_MP_EXPTMOD_C + #define BN_S_MP_MUL_DIGS_C + #define BN_S_MP_MUL_HIGH_DIGS_C + #define BN_S_MP_SQR_C + #define BN_S_MP_SUB_C + #define BNCORE_C + #endif + + #if defined(BN_ERROR_C) + #define BN_MP_ERROR_TO_STRING_C + #endif + + #if defined(BN_FAST_MP_INVMOD_C) + #define BN_MP_ISEVEN_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_COPY_C + #define BN_MP_MOD_C + #define BN_MP_SET_C + #define BN_MP_DIV_2_C + #define BN_MP_ISODD_C + #define BN_MP_SUB_C + #define BN_MP_CMP_C + #define BN_MP_ISZERO_C + #define BN_MP_CMP_D_C + #define BN_MP_ADD_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_FAST_MP_MONTGOMERY_REDUCE_C) + #define BN_MP_GROW_C + #define BN_MP_RSHD_C + #define BN_MP_CLAMP_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_FAST_S_MP_MUL_DIGS_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_FAST_S_MP_SQR_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_2EXPT_C) + #define BN_MP_ZERO_C + #define BN_MP_GROW_C + #endif + + #if defined(BN_MP_ABS_C) + #define BN_MP_COPY_C + #endif + + #if defined(BN_MP_ADD_C) + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_ADD_D_C) + #define BN_MP_GROW_C + #define BN_MP_SUB_D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_ADDMOD_C) + #define BN_MP_INIT_C + #define BN_MP_ADD_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C + #endif + + #if defined(BN_MP_AND_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_CLAMP_C) + #endif + + #if defined(BN_MP_CLEAR_C) + #endif + + #if defined(BN_MP_CLEAR_MULTI_C) + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_CMP_C) + #define BN_MP_CMP_MAG_C + #endif + + #if defined(BN_MP_CMP_D_C) + #endif + + #if defined(BN_MP_CMP_MAG_C) + #endif + + #if defined(BN_MP_CNT_LSB_C) + #define BN_MP_ISZERO_C + #endif + + #if defined(BN_MP_COPY_C) + #define BN_MP_GROW_C + #endif + + #if defined(BN_MP_COUNT_BITS_C) + #endif + + #if defined(BN_MP_DIV_C) + #define BN_MP_ISZERO_C + #define BN_MP_CMP_MAG_C + #define BN_MP_COPY_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_SET_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_ABS_C + #define BN_MP_MUL_2D_C + #define BN_MP_CMP_C + #define BN_MP_SUB_C + #define BN_MP_ADD_C + #define BN_MP_DIV_2D_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_INIT_C + #define BN_MP_INIT_COPY_C + #define BN_MP_LSHD_C + #define BN_MP_RSHD_C + #define BN_MP_MUL_D_C + #define BN_MP_CLAMP_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_DIV_2_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_DIV_2D_C) + #define BN_MP_COPY_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_C + #define BN_MP_MOD_2D_C + #define BN_MP_CLEAR_C + #define BN_MP_RSHD_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #endif + + #if defined(BN_MP_DIV_3_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_DIV_D_C) + #define BN_MP_ISZERO_C + #define BN_MP_COPY_C + #define BN_MP_DIV_2D_C + #define BN_MP_DIV_3_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_DR_IS_MODULUS_C) + #endif + + #if defined(BN_MP_DR_REDUCE_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_DR_SETUP_C) + #endif + + #if defined(BN_MP_EXCH_C) + #endif + + #if defined(BN_MP_EXPORT_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_2D_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_EXPT_D_C) + #define BN_MP_EXPT_D_EX_C + #endif + + #if defined(BN_MP_EXPT_D_EX_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_SET_C + #define BN_MP_MUL_C + #define BN_MP_CLEAR_C + #define BN_MP_SQR_C + #endif + + #if defined(BN_MP_EXPTMOD_C) + #define BN_MP_INIT_C + #define BN_MP_INVMOD_C + #define BN_MP_CLEAR_C + #define BN_MP_ABS_C + #define BN_MP_CLEAR_MULTI_C + #define BN_MP_REDUCE_IS_2K_L_C + #define BN_S_MP_EXPTMOD_C + #define BN_MP_DR_IS_MODULUS_C + #define BN_MP_REDUCE_IS_2K_C + #define BN_MP_ISODD_C + #define BN_MP_EXPTMOD_FAST_C + #endif + + #if defined(BN_MP_EXPTMOD_FAST_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_INIT_C + #define BN_MP_CLEAR_C + #define BN_MP_MONTGOMERY_SETUP_C + #define BN_FAST_MP_MONTGOMERY_REDUCE_C + #define BN_MP_MONTGOMERY_REDUCE_C + #define BN_MP_DR_SETUP_C + #define BN_MP_DR_REDUCE_C + #define BN_MP_REDUCE_2K_SETUP_C + #define BN_MP_REDUCE_2K_C + #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + #define BN_MP_MULMOD_C + #define BN_MP_SET_C + #define BN_MP_MOD_C + #define BN_MP_COPY_C + #define BN_MP_SQR_C + #define BN_MP_MUL_C + #define BN_MP_EXCH_C + #endif + + #if defined(BN_MP_EXTEUCLID_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_SET_C + #define BN_MP_COPY_C + #define BN_MP_ISZERO_C + #define BN_MP_DIV_C + #define BN_MP_MUL_C + #define BN_MP_SUB_C + #define BN_MP_NEG_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_FREAD_C) + #define BN_MP_ZERO_C + #define BN_MP_S_RMAP_C + #define BN_MP_MUL_D_C + #define BN_MP_ADD_D_C + #define BN_MP_CMP_D_C + #endif + + #if defined(BN_MP_FWRITE_C) + #define BN_MP_RADIX_SIZE_C + #define BN_MP_TORADIX_C + #endif + + #if defined(BN_MP_GCD_C) + #define BN_MP_ISZERO_C + #define BN_MP_ABS_C + #define BN_MP_INIT_COPY_C + #define BN_MP_CNT_LSB_C + #define BN_MP_DIV_2D_C + #define BN_MP_CMP_MAG_C + #define BN_MP_EXCH_C + #define BN_S_MP_SUB_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_GET_INT_C) + #endif + + #if defined(BN_MP_GET_LONG_C) + #endif + + #if defined(BN_MP_GET_LONG_LONG_C) + #endif + + #if defined(BN_MP_GROW_C) + #endif + + #if defined(BN_MP_IMPORT_C) + #define BN_MP_ZERO_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_INIT_C) + #endif + + #if defined(BN_MP_INIT_COPY_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_COPY_C + #endif + + #if defined(BN_MP_INIT_MULTI_C) + #define BN_MP_ERR_C + #define BN_MP_INIT_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_INIT_SET_C) + #define BN_MP_INIT_C + #define BN_MP_SET_C + #endif + + #if defined(BN_MP_INIT_SET_INT_C) + #define BN_MP_INIT_C + #define BN_MP_SET_INT_C + #endif + + #if defined(BN_MP_INIT_SIZE_C) + #define BN_MP_INIT_C + #endif + + #if defined(BN_MP_INVMOD_C) + #define BN_MP_ISZERO_C + #define BN_MP_ISODD_C + #define BN_FAST_MP_INVMOD_C + #define BN_MP_INVMOD_SLOW_C + #endif + + #if defined(BN_MP_INVMOD_SLOW_C) + #define BN_MP_ISZERO_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_C + #define BN_MP_COPY_C + #define BN_MP_ISEVEN_C + #define BN_MP_SET_C + #define BN_MP_DIV_2_C + #define BN_MP_ISODD_C + #define BN_MP_ADD_C + #define BN_MP_SUB_C + #define BN_MP_CMP_C + #define BN_MP_CMP_D_C + #define BN_MP_CMP_MAG_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_IS_SQUARE_C) + #define BN_MP_MOD_D_C + #define BN_MP_INIT_SET_INT_C + #define BN_MP_MOD_C + #define BN_MP_GET_INT_C + #define BN_MP_SQRT_C + #define BN_MP_SQR_C + #define BN_MP_CMP_MAG_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_JACOBI_C) + #define BN_MP_CMP_D_C + #define BN_MP_ISZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_CNT_LSB_C + #define BN_MP_DIV_2D_C + #define BN_MP_MOD_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_KARATSUBA_MUL_C) + #define BN_MP_MUL_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_S_MP_ADD_C + #define BN_MP_ADD_C + #define BN_S_MP_SUB_C + #define BN_MP_LSHD_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_KARATSUBA_SQR_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_SQR_C + #define BN_S_MP_ADD_C + #define BN_S_MP_SUB_C + #define BN_MP_LSHD_C + #define BN_MP_ADD_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_LCM_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_GCD_C + #define BN_MP_CMP_MAG_C + #define BN_MP_DIV_C + #define BN_MP_MUL_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_LSHD_C) + #define BN_MP_GROW_C + #define BN_MP_RSHD_C + #endif + + #if defined(BN_MP_MOD_C) + #define BN_MP_INIT_C + #define BN_MP_DIV_C + #define BN_MP_CLEAR_C + #define BN_MP_ISZERO_C + #define BN_MP_EXCH_C + #define BN_MP_ADD_C + #endif + + #if defined(BN_MP_MOD_2D_C) + #define BN_MP_ZERO_C + #define BN_MP_COPY_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_MOD_D_C) + #define BN_MP_DIV_D_C + #endif + + #if defined(BN_MP_MONTGOMERY_CALC_NORMALIZATION_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_2EXPT_C + #define BN_MP_SET_C + #define BN_MP_MUL_2_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_MONTGOMERY_REDUCE_C) + #define BN_FAST_MP_MONTGOMERY_REDUCE_C + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #define BN_MP_RSHD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_MONTGOMERY_SETUP_C) + #endif + + #if defined(BN_MP_MUL_C) + #define BN_MP_TOOM_MUL_C + #define BN_MP_KARATSUBA_MUL_C + #define BN_FAST_S_MP_MUL_DIGS_C + #define BN_S_MP_MUL_C + #define BN_S_MP_MUL_DIGS_C + #endif + + #if defined(BN_MP_MUL_2_C) + #define BN_MP_GROW_C + #endif + + #if defined(BN_MP_MUL_2D_C) + #define BN_MP_COPY_C + #define BN_MP_GROW_C + #define BN_MP_LSHD_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_MUL_D_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_MULMOD_C) + #define BN_MP_INIT_C + #define BN_MP_MUL_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C + #endif + + #if defined(BN_MP_N_ROOT_C) + #define BN_MP_N_ROOT_EX_C + #endif + + #if defined(BN_MP_N_ROOT_EX_C) + #define BN_MP_INIT_C + #define BN_MP_SET_C + #define BN_MP_COPY_C + #define BN_MP_EXPT_D_EX_C + #define BN_MP_MUL_C + #define BN_MP_SUB_C + #define BN_MP_MUL_D_C + #define BN_MP_DIV_C + #define BN_MP_CMP_C + #define BN_MP_SUB_D_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_NEG_C) + #define BN_MP_COPY_C + #define BN_MP_ISZERO_C + #endif + + #if defined(BN_MP_OR_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_FERMAT_C) + #define BN_MP_CMP_D_C + #define BN_MP_INIT_C + #define BN_MP_EXPTMOD_C + #define BN_MP_CMP_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_IS_DIVISIBLE_C) + #define BN_MP_MOD_D_C + #endif + + #if defined(BN_MP_PRIME_IS_PRIME_C) + #define BN_MP_CMP_D_C + #define BN_MP_PRIME_IS_DIVISIBLE_C + #define BN_MP_INIT_C + #define BN_MP_SET_C + #define BN_MP_PRIME_MILLER_RABIN_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_MILLER_RABIN_C) + #define BN_MP_CMP_D_C + #define BN_MP_INIT_COPY_C + #define BN_MP_SUB_D_C + #define BN_MP_CNT_LSB_C + #define BN_MP_DIV_2D_C + #define BN_MP_EXPTMOD_C + #define BN_MP_CMP_C + #define BN_MP_SQRMOD_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_NEXT_PRIME_C) + #define BN_MP_CMP_D_C + #define BN_MP_SET_C + #define BN_MP_SUB_D_C + #define BN_MP_ISEVEN_C + #define BN_MP_MOD_D_C + #define BN_MP_INIT_C + #define BN_MP_ADD_D_C + #define BN_MP_PRIME_MILLER_RABIN_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_PRIME_RABIN_MILLER_TRIALS_C) + #endif + + #if defined(BN_MP_PRIME_RANDOM_EX_C) + #define BN_MP_READ_UNSIGNED_BIN_C + #define BN_MP_PRIME_IS_PRIME_C + #define BN_MP_SUB_D_C + #define BN_MP_DIV_2_C + #define BN_MP_MUL_2_C + #define BN_MP_ADD_D_C + #endif + + #if defined(BN_MP_RADIX_SIZE_C) + #define BN_MP_ISZERO_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_INIT_COPY_C + #define BN_MP_DIV_D_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_RADIX_SMAP_C) + #define BN_MP_S_RMAP_C + #endif + + #if defined(BN_MP_RAND_C) + #define BN_MP_ZERO_C + #define BN_MP_ADD_D_C + #define BN_MP_LSHD_C + #endif + + #if defined(BN_MP_READ_RADIX_C) + #define BN_MP_ZERO_C + #define BN_MP_S_RMAP_C + #define BN_MP_MUL_D_C + #define BN_MP_ADD_D_C + #define BN_MP_ISZERO_C + #endif + + #if defined(BN_MP_READ_SIGNED_BIN_C) + #define BN_MP_READ_UNSIGNED_BIN_C + #endif + + #if defined(BN_MP_READ_UNSIGNED_BIN_C) + #define BN_MP_GROW_C + #define BN_MP_ZERO_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_REDUCE_C) + #define BN_MP_REDUCE_SETUP_C + #define BN_MP_INIT_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_MUL_C + #define BN_S_MP_MUL_HIGH_DIGS_C + #define BN_FAST_S_MP_MUL_HIGH_DIGS_C + #define BN_MP_MOD_2D_C + #define BN_S_MP_MUL_DIGS_C + #define BN_MP_SUB_C + #define BN_MP_CMP_D_C + #define BN_MP_SET_C + #define BN_MP_LSHD_C + #define BN_MP_ADD_C + #define BN_MP_CMP_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_REDUCE_2K_C) + #define BN_MP_INIT_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_2D_C + #define BN_MP_MUL_D_C + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_REDUCE_2K_L_C) + #define BN_MP_INIT_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_DIV_2D_C + #define BN_MP_MUL_C + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_REDUCE_2K_SETUP_C) + #define BN_MP_INIT_C + #define BN_MP_COUNT_BITS_C + #define BN_MP_2EXPT_C + #define BN_MP_CLEAR_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_REDUCE_2K_SETUP_L_C) + #define BN_MP_INIT_C + #define BN_MP_2EXPT_C + #define BN_MP_COUNT_BITS_C + #define BN_S_MP_SUB_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_REDUCE_IS_2K_C) + #define BN_MP_REDUCE_2K_C + #define BN_MP_COUNT_BITS_C + #endif + + #if defined(BN_MP_REDUCE_IS_2K_L_C) + #endif + + #if defined(BN_MP_REDUCE_SETUP_C) + #define BN_MP_2EXPT_C + #define BN_MP_DIV_C + #endif + + #if defined(BN_MP_RSHD_C) + #define BN_MP_ZERO_C + #endif + + #if defined(BN_MP_SET_C) + #define BN_MP_ZERO_C + #endif + + #if defined(BN_MP_SET_INT_C) + #define BN_MP_ZERO_C + #define BN_MP_MUL_2D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_SET_LONG_C) + #endif + + #if defined(BN_MP_SET_LONG_LONG_C) + #endif + + #if defined(BN_MP_SHRINK_C) + #endif + + #if defined(BN_MP_SIGNED_BIN_SIZE_C) + #define BN_MP_UNSIGNED_BIN_SIZE_C + #endif + + #if defined(BN_MP_SQR_C) + #define BN_MP_TOOM_SQR_C + #define BN_MP_KARATSUBA_SQR_C + #define BN_FAST_S_MP_SQR_C + #define BN_S_MP_SQR_C + #endif + + #if defined(BN_MP_SQRMOD_C) + #define BN_MP_INIT_C + #define BN_MP_SQR_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C + #endif + + #if defined(BN_MP_SQRT_C) + #define BN_MP_N_ROOT_C + #define BN_MP_ISZERO_C + #define BN_MP_ZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_DIV_C + #define BN_MP_ADD_C + #define BN_MP_DIV_2_C + #define BN_MP_CMP_MAG_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_SQRTMOD_PRIME_C) + #define BN_MP_CMP_D_C + #define BN_MP_ZERO_C + #define BN_MP_JACOBI_C + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_D_C + #define BN_MP_ADD_D_C + #define BN_MP_DIV_2_C + #define BN_MP_EXPTMOD_C + #define BN_MP_COPY_C + #define BN_MP_SUB_D_C + #define BN_MP_ISEVEN_C + #define BN_MP_SET_INT_C + #define BN_MP_SQRMOD_C + #define BN_MP_MULMOD_C + #define BN_MP_SET_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_SUB_C) + #define BN_S_MP_ADD_C + #define BN_MP_CMP_MAG_C + #define BN_S_MP_SUB_C + #endif + + #if defined(BN_MP_SUB_D_C) + #define BN_MP_GROW_C + #define BN_MP_ADD_D_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_MP_SUBMOD_C) + #define BN_MP_INIT_C + #define BN_MP_SUB_C + #define BN_MP_CLEAR_C + #define BN_MP_MOD_C + #endif + + #if defined(BN_MP_TO_SIGNED_BIN_C) + #define BN_MP_TO_UNSIGNED_BIN_C + #endif + + #if defined(BN_MP_TO_SIGNED_BIN_N_C) + #define BN_MP_SIGNED_BIN_SIZE_C + #define BN_MP_TO_SIGNED_BIN_C + #endif + + #if defined(BN_MP_TO_UNSIGNED_BIN_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_ISZERO_C + #define BN_MP_DIV_2D_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_TO_UNSIGNED_BIN_N_C) + #define BN_MP_UNSIGNED_BIN_SIZE_C + #define BN_MP_TO_UNSIGNED_BIN_C + #endif + + #if defined(BN_MP_TOOM_MUL_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_2D_C + #define BN_MP_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_MUL_C + #define BN_MP_MUL_2_C + #define BN_MP_ADD_C + #define BN_MP_SUB_C + #define BN_MP_DIV_2_C + #define BN_MP_MUL_2D_C + #define BN_MP_MUL_D_C + #define BN_MP_DIV_3_C + #define BN_MP_LSHD_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_TOOM_SQR_C) + #define BN_MP_INIT_MULTI_C + #define BN_MP_MOD_2D_C + #define BN_MP_COPY_C + #define BN_MP_RSHD_C + #define BN_MP_SQR_C + #define BN_MP_MUL_2_C + #define BN_MP_ADD_C + #define BN_MP_SUB_C + #define BN_MP_DIV_2_C + #define BN_MP_MUL_2D_C + #define BN_MP_MUL_D_C + #define BN_MP_DIV_3_C + #define BN_MP_LSHD_C + #define BN_MP_CLEAR_MULTI_C + #endif + + #if defined(BN_MP_TORADIX_C) + #define BN_MP_ISZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_DIV_D_C + #define BN_MP_CLEAR_C + #define BN_MP_S_RMAP_C + #endif + + #if defined(BN_MP_TORADIX_N_C) + #define BN_MP_ISZERO_C + #define BN_MP_INIT_COPY_C + #define BN_MP_DIV_D_C + #define BN_MP_CLEAR_C + #define BN_MP_S_RMAP_C + #endif + + #if defined(BN_MP_UNSIGNED_BIN_SIZE_C) + #define BN_MP_COUNT_BITS_C + #endif + + #if defined(BN_MP_XOR_C) + #define BN_MP_INIT_COPY_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_MP_ZERO_C) + #endif + + #if defined(BN_PRIME_TAB_C) + #endif + + #if defined(BN_REVERSE_C) + #endif + + #if defined(BN_S_MP_ADD_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BN_S_MP_EXPTMOD_C) + #define BN_MP_COUNT_BITS_C + #define BN_MP_INIT_C + #define BN_MP_CLEAR_C + #define BN_MP_REDUCE_SETUP_C + #define BN_MP_REDUCE_C + #define BN_MP_REDUCE_2K_SETUP_L_C + #define BN_MP_REDUCE_2K_L_C + #define BN_MP_MOD_C + #define BN_MP_COPY_C + #define BN_MP_SQR_C + #define BN_MP_MUL_C + #define BN_MP_SET_C + #define BN_MP_EXCH_C + #endif + + #if defined(BN_S_MP_MUL_DIGS_C) + #define BN_FAST_S_MP_MUL_DIGS_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_S_MP_MUL_HIGH_DIGS_C) + #define BN_FAST_S_MP_MUL_HIGH_DIGS_C + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_S_MP_SQR_C) + #define BN_MP_INIT_SIZE_C + #define BN_MP_CLAMP_C + #define BN_MP_EXCH_C + #define BN_MP_CLEAR_C + #endif + + #if defined(BN_S_MP_SUB_C) + #define BN_MP_GROW_C + #define BN_MP_CLAMP_C + #endif + + #if defined(BNCORE_C) + #endif + + #ifdef LTM3 + #define LTM_LAST + #endif +#else + #define LTM_LAST +#endif +#else + #define LTM_LAST +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* detect 64-bit mode if possible */ +#if defined(__x86_64__) + #if !(defined(MP_32BIT) || defined(MP_16BIT) || defined(MP_8BIT)) + #define MP_64BIT + #endif +#endif + +/* some default configurations. + * + * A "mp_digit" must be able to hold DIGIT_BIT + 1 bits + * A "mp_word" must be able to hold 2*DIGIT_BIT + 1 bits + * + * At the very least a mp_digit must be able to hold 7 bits + * [any size beyond that is ok provided it doesn't overflow the data type] + */ +#ifdef MP_8BIT +typedef uint8_t mp_digit; +typedef uint16_t mp_word; + #define MP_SIZEOF_MP_DIGIT 1 + #ifdef DIGIT_BIT + #error You must not define DIGIT_BIT when using MP_8BIT + #endif +#elif defined(MP_16BIT) +typedef uint16_t mp_digit; +typedef uint32_t mp_word; + #define MP_SIZEOF_MP_DIGIT 2 + #ifdef DIGIT_BIT + #error You must not define DIGIT_BIT when using MP_16BIT + #endif +#elif defined(MP_64BIT) +/* for GCC only on supported platforms */ + #ifndef CRYPT +typedef unsigned long long ulong64; +typedef signed long long long64; + #endif + +typedef uint64_t mp_digit; + #if defined(_WIN32) +typedef unsigned __int128 mp_word; + #elif defined(__GNUC__) +typedef unsigned long mp_word __attribute__ ((mode(TI))); + #else + +/* it seems you have a problem + * but we assume you can somewhere define your own uint128_t */ +typedef uint128_t mp_word; + #endif + + #define DIGIT_BIT 60 +#else +/* this is the default case, 28-bit digits */ + +/* this is to make porting into LibTomCrypt easier :-) */ + #ifndef CRYPT +typedef unsigned long long ulong64; +typedef signed long long long64; + #endif + +typedef uint32_t mp_digit; +typedef uint64_t mp_word; + + #ifdef MP_31BIT +/* this is an extension that uses 31-bit digits */ + #define DIGIT_BIT 31 + #else +/* default case is 28-bit digits, defines MP_28BIT as a handy macro to test */ + #define DIGIT_BIT 28 + #define MP_28BIT + #endif +#endif + +/* otherwise the bits per digit is calculated automatically from the size of a mp_digit */ +#ifndef DIGIT_BIT + #define DIGIT_BIT (((CHAR_BIT * MP_SIZEOF_MP_DIGIT) - 1)) /* bits per digit */ +typedef uint_least32_t mp_min_u32; +#else +typedef mp_digit mp_min_u32; +#endif + +/* platforms that can use a better rand function */ +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) + #define MP_USE_ALT_RAND 1 +#endif + +/* use arc4random on platforms that support it */ +#ifdef MP_USE_ALT_RAND + #define MP_GEN_RANDOM() arc4random() +#else + #define MP_GEN_RANDOM() rand() +#endif + +#define MP_DIGIT_BIT DIGIT_BIT +#define MP_MASK ((((mp_digit)1) << ((mp_digit)DIGIT_BIT)) - ((mp_digit)1)) +#define MP_DIGIT_MAX MP_MASK + +/* equalities */ +#define MP_LT -1 /* less than */ +#define MP_EQ 0 /* equal to */ +#define MP_GT 1 /* greater than */ + +#define MP_ZPOS 0 /* positive integer */ +#define MP_NEG 1 /* negative */ + +#define MP_OKAY 0 /* ok result */ +#define MP_MEM -2 /* out of mem */ +#define MP_VAL -3 /* invalid input */ +#define MP_RANGE MP_VAL + +#define MP_YES 1 /* yes response */ +#define MP_NO 0 /* no response */ + +/* Primality generation flags */ +#define LTM_PRIME_BBS 0x0001 /* BBS style prime */ +#define LTM_PRIME_SAFE 0x0002 /* Safe prime (p-1)/2 == prime */ +#define LTM_PRIME_2MSB_ON 0x0008 /* force 2nd MSB to 1 */ + +typedef int mp_err; + +/* you'll have to tune these... */ +extern int KARATSUBA_MUL_CUTOFF, + KARATSUBA_SQR_CUTOFF, + TOOM_MUL_CUTOFF, + TOOM_SQR_CUTOFF; + +/* define this to use lower memory usage routines (exptmods mostly) */ +/* #define MP_LOW_MEM */ + +/* default precision */ +#ifndef MP_PREC + #ifndef MP_LOW_MEM + #define MP_PREC 32 /* default digits of precision */ + #else + #define MP_PREC 8 /* default digits of precision */ + #endif +#endif + +/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ +#define MP_WARRAY (1 << (((sizeof(mp_word) * CHAR_BIT) - (2 * DIGIT_BIT)) + 1)) + +/* the infamous mp_int structure */ +typedef struct { + int used, alloc, sign; + mp_digit *dp; +} mp_int; + +/* callback for mp_prime_random, should fill dst with random bytes and return how many read [upto len] */ +typedef int ltm_prime_callback (unsigned char *dst, int len, void *dat); + + +#define USED(m) ((m)->used) +#define DIGIT(m, k) ((m)->dp[(k)]) +#define SIGN(m) ((m)->sign) + +/* error code to char* string */ +const char *mp_error_to_string(int code); + +/* ---> init and deinit bignum functions <--- */ +/* init a bignum */ +int mp_init(mp_int *a); + +/* free a bignum */ +void mp_clear(mp_int *a); + +/* init a null terminated series of arguments */ +int mp_init_multi(mp_int *mp, ...); + +/* clear a null terminated series of arguments */ +void mp_clear_multi(mp_int *mp, ...); + +/* exchange two ints */ +void mp_exch(mp_int *a, mp_int *b); + +/* shrink ram required for a bignum */ +int mp_shrink(mp_int *a); + +/* grow an int to a given size */ +int mp_grow(mp_int *a, int size); + +/* init to a given number of digits */ +int mp_init_size(mp_int *a, int size); + +/* ---> Basic Manipulations <--- */ +#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO) +#define mp_iseven(a) ((((a)->used > 0) && (((a)->dp[0] & 1u) == 0u)) ? MP_YES : MP_NO) +#define mp_isodd(a) ((((a)->used > 0) && (((a)->dp[0] & 1u) == 1u)) ? MP_YES : MP_NO) +#define mp_isneg(a) (((a)->sign != MP_ZPOS) ? MP_YES : MP_NO) + +/* set to zero */ +void mp_zero(mp_int *a); + +/* set to a digit */ +void mp_set(mp_int *a, mp_digit b); + +/* set a 32-bit const */ +int mp_set_int(mp_int *a, unsigned long b); + +/* set a platform dependent unsigned long value */ +int mp_set_long(mp_int *a, unsigned long b); + +/* set a platform dependent unsigned long long value */ +int mp_set_long_long(mp_int *a, unsigned long long b); + +/* get a 32-bit value */ +unsigned long mp_get_int(mp_int *a); + +/* get a platform dependent unsigned long value */ +unsigned long mp_get_long(mp_int *a); + +/* get a platform dependent unsigned long long value */ +unsigned long long mp_get_long_long(mp_int *a); + +/* initialize and set a digit */ +int mp_init_set(mp_int *a, mp_digit b); + +/* initialize and set 32-bit value */ +int mp_init_set_int(mp_int *a, unsigned long b); + +/* copy, b = a */ +int mp_copy(mp_int *a, mp_int *b); + +/* inits and copies, a = b */ +int mp_init_copy(mp_int *a, mp_int *b); + +/* trim unused digits */ +void mp_clamp(mp_int *a); + +/* import binary data */ +int mp_import(mp_int *rop, size_t count, int order, size_t size, int endian, size_t nails, const void *op); + +/* export binary data */ +int mp_export(void *rop, size_t *countp, int order, size_t size, int endian, size_t nails, mp_int *op); + +/* ---> digit manipulation <--- */ + +/* right shift by "b" digits */ +void mp_rshd(mp_int *a, int b); + +/* left shift by "b" digits */ +int mp_lshd(mp_int *a, int b); + +/* c = a / 2**b, implemented as c = a >> b */ +int mp_div_2d(mp_int *a, int b, mp_int *c, mp_int *d); + +/* b = a/2 */ +int mp_div_2(mp_int *a, mp_int *b); + +/* c = a * 2**b, implemented as c = a << b */ +int mp_mul_2d(mp_int *a, int b, mp_int *c); + +/* b = a*2 */ +int mp_mul_2(mp_int *a, mp_int *b); + +/* c = a mod 2**b */ +int mp_mod_2d(mp_int *a, int b, mp_int *c); + +/* computes a = 2**b */ +int mp_2expt(mp_int *a, int b); + +/* Counts the number of lsbs which are zero before the first zero bit */ +int mp_cnt_lsb(mp_int *a); + +/* I Love Earth! */ + +/* makes a pseudo-random int of a given size */ +int mp_rand(mp_int *a, int digits); + +/* ---> binary operations <--- */ +/* c = a XOR b */ +int mp_xor(mp_int *a, mp_int *b, mp_int *c); + +/* c = a OR b */ +int mp_or(mp_int *a, mp_int *b, mp_int *c); + +/* c = a AND b */ +int mp_and(mp_int *a, mp_int *b, mp_int *c); + +/* ---> Basic arithmetic <--- */ + +/* b = -a */ +int mp_neg(mp_int *a, mp_int *b); + +/* b = |a| */ +int mp_abs(mp_int *a, mp_int *b); + +/* compare a to b */ +int mp_cmp(mp_int *a, mp_int *b); + +/* compare |a| to |b| */ +int mp_cmp_mag(mp_int *a, mp_int *b); + +/* c = a + b */ +int mp_add(mp_int *a, mp_int *b, mp_int *c); + +/* c = a - b */ +int mp_sub(mp_int *a, mp_int *b, mp_int *c); + +/* c = a * b */ +int mp_mul(mp_int *a, mp_int *b, mp_int *c); + +/* b = a*a */ +int mp_sqr(mp_int *a, mp_int *b); + +/* a/b => cb + d == a */ +int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* c = a mod b, 0 <= c < b */ +int mp_mod(mp_int *a, mp_int *b, mp_int *c); + +/* ---> single digit functions <--- */ + +/* compare against a single digit */ +int mp_cmp_d(mp_int *a, mp_digit b); + +/* c = a + b */ +int mp_add_d(mp_int *a, mp_digit b, mp_int *c); + +/* c = a - b */ +int mp_sub_d(mp_int *a, mp_digit b, mp_int *c); + +/* c = a * b */ +int mp_mul_d(mp_int *a, mp_digit b, mp_int *c); + +/* a/b => cb + d == a */ +int mp_div_d(mp_int *a, mp_digit b, mp_int *c, mp_digit *d); + +/* a/3 => 3c + d == a */ +int mp_div_3(mp_int *a, mp_int *c, mp_digit *d); + +/* c = a**b */ +int mp_expt_d(mp_int *a, mp_digit b, mp_int *c); +int mp_expt_d_ex(mp_int *a, mp_digit b, mp_int *c, int fast); + +/* c = a mod b, 0 <= c < b */ +int mp_mod_d(mp_int *a, mp_digit b, mp_digit *c); + +/* ---> number theory <--- */ + +/* d = a + b (mod c) */ +int mp_addmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* d = a - b (mod c) */ +int mp_submod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* d = a * b (mod c) */ +int mp_mulmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* c = a * a (mod b) */ +int mp_sqrmod(mp_int *a, mp_int *b, mp_int *c); + +/* c = 1/a (mod b) */ +int mp_invmod(mp_int *a, mp_int *b, mp_int *c); + +/* c = (a, b) */ +int mp_gcd(mp_int *a, mp_int *b, mp_int *c); + +/* produces value such that U1*a + U2*b = U3 */ +int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3); + +/* c = [a, b] or (a*b)/(a, b) */ +int mp_lcm(mp_int *a, mp_int *b, mp_int *c); + +/* finds one of the b'th root of a, such that |c|**b <= |a| + * + * returns error if a < 0 and b is even + */ +int mp_n_root(mp_int *a, mp_digit b, mp_int *c); +int mp_n_root_ex(mp_int *a, mp_digit b, mp_int *c, int fast); + +/* special sqrt algo */ +int mp_sqrt(mp_int *arg, mp_int *ret); + +/* special sqrt (mod prime) */ +int mp_sqrtmod_prime(mp_int *arg, mp_int *prime, mp_int *ret); + +/* is number a square? */ +int mp_is_square(mp_int *arg, int *ret); + +/* computes the jacobi c = (a | n) (or Legendre if b is prime) */ +int mp_jacobi(mp_int *a, mp_int *n, int *c); + +/* used to setup the Barrett reduction for a given modulus b */ +int mp_reduce_setup(mp_int *a, mp_int *b); + +/* Barrett Reduction, computes a (mod b) with a precomputed value c + * + * Assumes that 0 < a <= b*b, note if 0 > a > -(b*b) then you can merely + * compute the reduction as -1 * mp_reduce(mp_abs(a)) [pseudo code]. + */ +int mp_reduce(mp_int *a, mp_int *b, mp_int *c); + +/* setups the montgomery reduction */ +int mp_montgomery_setup(mp_int *a, mp_digit *mp); + +/* computes a = B**n mod b without division or multiplication useful for + * normalizing numbers in a Montgomery system. + */ +int mp_montgomery_calc_normalization(mp_int *a, mp_int *b); + +/* computes x/R == x (mod N) via Montgomery Reduction */ +int mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp); + +/* returns 1 if a is a valid DR modulus */ +int mp_dr_is_modulus(mp_int *a); + +/* sets the value of "d" required for mp_dr_reduce */ +void mp_dr_setup(mp_int *a, mp_digit *d); + +/* reduces a modulo b using the Diminished Radix method */ +int mp_dr_reduce(mp_int *a, mp_int *b, mp_digit mp); + +/* returns true if a can be reduced with mp_reduce_2k */ +int mp_reduce_is_2k(mp_int *a); + +/* determines k value for 2k reduction */ +int mp_reduce_2k_setup(mp_int *a, mp_digit *d); + +/* reduces a modulo b where b is of the form 2**p - k [0 <= a] */ +int mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d); + +/* returns true if a can be reduced with mp_reduce_2k_l */ +int mp_reduce_is_2k_l(mp_int *a); + +/* determines k value for 2k reduction */ +int mp_reduce_2k_setup_l(mp_int *a, mp_int *d); + +/* reduces a modulo b where b is of the form 2**p - k [0 <= a] */ +int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d); + +/* d = a**b (mod c) */ +int mp_exptmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* ---> Primes <--- */ + +/* number of primes */ +#ifdef MP_8BIT + #define PRIME_SIZE 31 +#else + #define PRIME_SIZE 256 +#endif + +/* table of first PRIME_SIZE primes */ +extern const mp_digit ltm_prime_tab[PRIME_SIZE]; + +/* result=1 if a is divisible by one of the first PRIME_SIZE primes */ +int mp_prime_is_divisible(mp_int *a, int *result); + +/* performs one Fermat test of "a" using base "b". + * Sets result to 0 if composite or 1 if probable prime + */ +int mp_prime_fermat(mp_int *a, mp_int *b, int *result); + +/* performs one Miller-Rabin test of "a" using base "b". + * Sets result to 0 if composite or 1 if probable prime + */ +int mp_prime_miller_rabin(mp_int *a, mp_int *b, int *result); + +/* This gives [for a given bit size] the number of trials required + * such that Miller-Rabin gives a prob of failure lower than 2^-96 + */ +int mp_prime_rabin_miller_trials(int size); + +/* performs t rounds of Miller-Rabin on "a" using the first + * t prime bases. Also performs an initial sieve of trial + * division. Determines if "a" is prime with probability + * of error no more than (1/4)**t. + * + * Sets result to 1 if probably prime, 0 otherwise + */ +int mp_prime_is_prime(mp_int *a, int t, int *result); + +/* finds the next prime after the number "a" using "t" trials + * of Miller-Rabin. + * + * bbs_style = 1 means the prime must be congruent to 3 mod 4 + */ +int mp_prime_next_prime(mp_int *a, int t, int bbs_style); + +/* makes a truly random prime of a given size (bytes), + * call with bbs = 1 if you want it to be congruent to 3 mod 4 + * + * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can + * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself + * so it can be NULL + * + * The prime generated will be larger than 2^(8*size). + */ +#define mp_prime_random(a, t, size, bbs, cb, dat) mp_prime_random_ex(a, t, ((size) * 8) + 1, (bbs == 1) ? LTM_PRIME_BBS : 0, cb, dat) + +/* makes a truly random prime of a given size (bits), + * + * Flags are as follows: + * + * LTM_PRIME_BBS - make prime congruent to 3 mod 4 + * LTM_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS) + * LTM_PRIME_2MSB_ON - make the 2nd highest bit one + * + * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can + * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself + * so it can be NULL + * + */ +int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat); + +/* ---> radix conversion <--- */ +int mp_count_bits(mp_int *a); + +int mp_unsigned_bin_size(mp_int *a); +int mp_read_unsigned_bin(mp_int *a, const unsigned char *b, int c); +int mp_to_unsigned_bin(mp_int *a, unsigned char *b); +int mp_to_unsigned_bin_n(mp_int *a, unsigned char *b, unsigned long *outlen); + +int mp_signed_bin_size(mp_int *a); +int mp_read_signed_bin(mp_int *a, const unsigned char *b, int c); +int mp_to_signed_bin(mp_int *a, unsigned char *b); +int mp_to_signed_bin_n(mp_int *a, unsigned char *b, unsigned long *outlen); + +int mp_read_radix(mp_int *a, const char *str, int radix); +int mp_toradix(mp_int *a, char *str, int radix); +int mp_toradix_n(mp_int *a, char *str, int radix, int maxlen); +int mp_radix_size(mp_int *a, int radix, int *size); + +#ifndef LTM_NO_FILE +int mp_fread(mp_int *a, int radix, FILE *stream); +int mp_fwrite(mp_int *a, int radix, FILE *stream); +#endif + +#define mp_read_raw(mp, str, len) mp_read_signed_bin((mp), (str), (len)) +#define mp_raw_size(mp) mp_signed_bin_size(mp) +#define mp_toraw(mp, str) mp_to_signed_bin((mp), (str)) +#define mp_read_mag(mp, str, len) mp_read_unsigned_bin((mp), (str), (len)) +#define mp_mag_size(mp) mp_unsigned_bin_size(mp) +#define mp_tomag(mp, str) mp_to_unsigned_bin((mp), (str)) + +#define mp_tobinary(M, S) mp_toradix((M), (S), 2) +#define mp_tooctal(M, S) mp_toradix((M), (S), 8) +#define mp_todecimal(M, S) mp_toradix((M), (S), 10) +#define mp_tohex(M, S) mp_toradix((M), (S), 16) + +#ifdef __cplusplus +} +#endif +#endif + + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://math.libtomcrypt.com + */ +#ifndef TOMMATH_PRIV_H_ +#define TOMMATH_PRIV_H_ + +#include + +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) + +#ifdef __cplusplus +extern "C" { +/* C++ compilers don't like assigning void * to mp_digit * */ + #define OPT_CAST(x) (x *) + +#else + +/* C on the other hand doesn't care */ + #define OPT_CAST(x) +#endif + +/* define heap macros */ +#ifndef XMALLOC +/* default to libc stuff */ + #define XMALLOC malloc + #define XFREE free + #define XREALLOC realloc + #define XCALLOC calloc +#else +/* prototypes for our heap functions */ +extern void *XMALLOC(size_t n); +extern void *XREALLOC(void *p, size_t n); +extern void *XCALLOC(size_t n, size_t s); +extern void XFREE(void *p); +#endif + +/* lowlevel functions, do not call! */ +int s_mp_add(mp_int *a, mp_int *b, mp_int *c); +int s_mp_sub(mp_int *a, mp_int *b, mp_int *c); + +#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1) +int fast_s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int fast_s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int fast_s_mp_sqr(mp_int *a, mp_int *b); +int s_mp_sqr(mp_int *a, mp_int *b); +int mp_karatsuba_mul(mp_int *a, mp_int *b, mp_int *c); +int mp_toom_mul(mp_int *a, mp_int *b, mp_int *c); +int mp_karatsuba_sqr(mp_int *a, mp_int *b); +int mp_toom_sqr(mp_int *a, mp_int *b); +int fast_mp_invmod(mp_int *a, mp_int *b, mp_int *c); +int mp_invmod_slow(mp_int *a, mp_int *b, mp_int *c); +int fast_mp_montgomery_reduce(mp_int *x, mp_int *n, mp_digit rho); +int mp_exptmod_fast(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int redmode); +int s_mp_exptmod(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int redmode); +void bn_reverse(unsigned char *s, int len); + +extern const char *mp_s_rmap; + +/* Fancy macro to set an MPI from another type. + * There are several things assumed: + * x is the counter and unsigned + * a is the pointer to the MPI + * b is the original value that should be set in the MPI. + */ +#define MP_SET_XLONG(func_name, type) \ + int func_name(mp_int * a, type b) \ + { \ + unsigned int x; \ + int res; \ + \ + mp_zero(a); \ + \ + /* set four bits at a time */ \ + for (x = 0; x < (sizeof(type) * 2u); x++) { \ + /* shift the number up four bits */ \ + if ((res = mp_mul_2d(a, 4, a)) != MP_OKAY) { \ + return res; \ + } \ + \ + /* OR in the top four bits of the source */ \ + a->dp[0] |= (b >> ((sizeof(type) * 8u) - 4u)) & 15u; \ + \ + /* shift the source up to the next four bits */ \ + b <<= 4; \ + \ + /* ensure that digits are not clamped off */ \ + a->used += 1; \ + } \ + mp_clamp(a); \ + return MP_OKAY; \ + } + +#ifdef __cplusplus +} +#endif +#endif + + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + +#define BN_FAST_MP_INVMOD_C +#ifdef BN_FAST_MP_INVMOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* computes the modular inverse via binary extended euclidean algorithm, + * that is c = 1/a mod b + * + * Based on slow invmod except this is optimized for the case where b is + * odd as per HAC Note 14.64 on pp. 610 + */ +int fast_mp_invmod(mp_int *a, mp_int *b, mp_int *c) { + mp_int x, y, u, v, B, D; + int res, neg; + + /* 2. [modified] b must be odd */ + if (mp_iseven(b) == MP_YES) { + return MP_VAL; + } + + /* init all our temps */ + if ((res = mp_init_multi(&x, &y, &u, &v, &B, &D, NULL)) != MP_OKAY) { + return res; + } + + /* x == modulus, y == value to invert */ + if ((res = mp_copy(b, &x)) != MP_OKAY) { + goto LBL_ERR; + } + + /* we need y = |a| */ + if ((res = mp_mod(a, b, &y)) != MP_OKAY) { + goto LBL_ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy(&x, &u)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy(&y, &v)) != MP_OKAY) { + goto LBL_ERR; + } + mp_set(&D, 1); + +top: + /* 4. while u is even do */ + while (mp_iseven(&u) == MP_YES) { + /* 4.1 u = u/2 */ + if ((res = mp_div_2(&u, &u)) != MP_OKAY) { + goto LBL_ERR; + } + /* 4.2 if B is odd then */ + if (mp_isodd(&B) == MP_YES) { + if ((res = mp_sub(&B, &x, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* B = B/2 */ + if ((res = mp_div_2(&B, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 5. while v is even do */ + while (mp_iseven(&v) == MP_YES) { + /* 5.1 v = v/2 */ + if ((res = mp_div_2(&v, &v)) != MP_OKAY) { + goto LBL_ERR; + } + /* 5.2 if D is odd then */ + if (mp_isodd(&D) == MP_YES) { + /* D = (D-x)/2 */ + if ((res = mp_sub(&D, &x, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* D = D/2 */ + if ((res = mp_div_2(&D, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 6. if u >= v then */ + if (mp_cmp(&u, &v) != MP_LT) { + /* u = u - v, B = B - D */ + if ((res = mp_sub(&u, &v, &u)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub(&B, &D, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } else { + /* v - v - u, D = D - B */ + if ((res = mp_sub(&v, &u, &v)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub(&D, &B, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* if not zero goto step 4 */ + if (mp_iszero(&u) == MP_NO) { + goto top; + } + + /* now a = C, b = D, gcd == g*v */ + + /* if v != 1 then there is no inverse */ + if (mp_cmp_d(&v, 1) != MP_EQ) { + res = MP_VAL; + goto LBL_ERR; + } + + /* b is now the inverse */ + neg = a->sign; + while (D.sign == MP_NEG) { + if ((res = mp_add(&D, b, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + mp_exch(&D, c); + c->sign = neg; + res = MP_OKAY; + +LBL_ERR: mp_clear_multi(&x, &y, &u, &v, &B, &D, NULL); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* computes xR**-1 == x (mod N) via Montgomery Reduction + * + * This is an optimized implementation of montgomery_reduce + * which uses the comba method to quickly calculate the columns of the + * reduction. + * + * Based on Algorithm 14.32 on pp.601 of HAC. + */ +int fast_mp_montgomery_reduce(mp_int *x, mp_int *n, mp_digit rho) { + int ix, res, olduse; + mp_word W[MP_WARRAY]; + + /* get old used count */ + olduse = x->used; + + /* grow a as required */ + if (x->alloc < (n->used + 1)) { + if ((res = mp_grow(x, n->used + 1)) != MP_OKAY) { + return res; + } + } + + /* first we have to get the digits of the input into + * an array of double precision words W[...] + */ + { + mp_word *_W; + mp_digit *tmpx; + + /* alias for the W[] array */ + _W = W; + + /* alias for the digits of x*/ + tmpx = x->dp; + + /* copy the digits of a into W[0..a->used-1] */ + for (ix = 0; ix < x->used; ix++) { + *_W++ = *tmpx++; + } + + /* zero the high words of W[a->used..m->used*2] */ + for ( ; ix < ((n->used * 2) + 1); ix++) { + *_W++ = 0; + } + } + + /* now we proceed to zero successive digits + * from the least significant upwards + */ + for (ix = 0; ix < n->used; ix++) { + /* mu = ai * m' mod b + * + * We avoid a double precision multiplication (which isn't required) + * by casting the value down to a mp_digit. Note this requires + * that W[ix-1] have the carry cleared (see after the inner loop) + */ + mp_digit mu; + mu = (mp_digit)(((W[ix] & MP_MASK) * rho) & MP_MASK); + + /* a = a + mu * m * b**i + * + * This is computed in place and on the fly. The multiplication + * by b**i is handled by offseting which columns the results + * are added to. + * + * Note the comba method normally doesn't handle carries in the + * inner loop In this case we fix the carry from the previous + * column since the Montgomery reduction requires digits of the + * result (so far) [see above] to work. This is + * handled by fixing up one carry after the inner loop. The + * carry fixups are done in order so after these loops the + * first m->used words of W[] have the carries fixed + */ + { + int iy; + mp_digit *tmpn; + mp_word *_W; + + /* alias for the digits of the modulus */ + tmpn = n->dp; + + /* Alias for the columns set by an offset of ix */ + _W = W + ix; + + /* inner loop */ + for (iy = 0; iy < n->used; iy++) { + *_W++ += ((mp_word)mu) * ((mp_word) * tmpn++); + } + } + + /* now fix carry for next digit, W[ix+1] */ + W[ix + 1] += W[ix] >> ((mp_word)DIGIT_BIT); + } + + /* now we have to propagate the carries and + * shift the words downward [all those least + * significant digits we zeroed]. + */ + { + mp_digit *tmpx; + mp_word *_W, *_W1; + + /* nox fix rest of carries */ + + /* alias for current word */ + _W1 = W + ix; + + /* alias for next word, where the carry goes */ + _W = W + ++ix; + + for ( ; ix <= ((n->used * 2) + 1); ix++) { + *_W++ += *_W1++ >> ((mp_word)DIGIT_BIT); + } + + /* copy out, A = A/b**n + * + * The result is A/b**n but instead of converting from an + * array of mp_word to mp_digit than calling mp_rshd + * we just copy them in the right order + */ + + /* alias for destination word */ + tmpx = x->dp; + + /* alias for shifted double precision result */ + _W = W + n->used; + + for (ix = 0; ix < (n->used + 1); ix++) { + *tmpx++ = (mp_digit)(*_W++ & ((mp_word)MP_MASK)); + } + + /* zero oldused digits, if the input a was larger than + * m->used+1 we'll have to clear the digits + */ + for ( ; ix < olduse; ix++) { + *tmpx++ = 0; + } + } + + /* set the max used and clamp */ + x->used = n->used + 1; + mp_clamp(x); + + /* if A >= m then A = A - m */ + if (mp_cmp_mag(x, n) != MP_LT) { + return s_mp_sub(x, n, x); + } + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_FAST_S_MP_MUL_DIGS_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* Fast (comba) multiplier + * + * This is the fast column-array [comba] multiplier. It is + * designed to compute the columns of the product first + * then handle the carries afterwards. This has the effect + * of making the nested loops that compute the columns very + * simple and schedulable on super-scalar processors. + * + * This has been modified to produce a variable number of + * digits of output so if say only a half-product is required + * you don't have to compute the upper half (a feature + * required for fast Barrett reduction). + * + * Based on Algorithm 14.12 on pp.595 of HAC. + * + */ +int fast_s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs) { + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY]; + mp_word _W; + + /* grow the destination as required */ + if (c->alloc < digs) { + if ((res = mp_grow(c, digs)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + pa = MIN(digs, a->used + b->used); + + /* clear the carry */ + _W = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty; + int iy; + mp_digit *tmpx, *tmpy; + + /* get offsets into the two bignums */ + ty = MIN(b->used - 1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = b->dp + ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used - tx, ty + 1); + + /* execute loop */ + for (iz = 0; iz < iy; ++iz) { + _W += ((mp_word) * tmpx++) * ((mp_word) * tmpy--); + } + + /* store term */ + W[ix] = ((mp_digit)_W) & MP_MASK; + + /* make next carry */ + _W = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = c->used; + c->used = pa; + + { + mp_digit *tmpc; + tmpc = c->dp; + for (ix = 0; ix < (pa + 1); ix++) { + /* now extract the previous digit [below the carry] */ + *tmpc++ = W[ix]; + } + + /* clear unused digits [that existed in the old copy of c] */ + for ( ; ix < olduse; ix++) { + *tmpc++ = 0; + } + } + mp_clamp(c); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* this is a modified version of fast_s_mul_digs that only produces + * output digits *above* digs. See the comments for fast_s_mul_digs + * to see how it works. + * + * This is used in the Barrett reduction since for one of the multiplications + * only the higher digits were needed. This essentially halves the work. + * + * Based on Algorithm 14.12 on pp.595 of HAC. + */ +int fast_s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs) { + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY]; + mp_word _W; + + /* grow the destination as required */ + pa = a->used + b->used; + if (c->alloc < pa) { + if ((res = mp_grow(c, pa)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + pa = a->used + b->used; + _W = 0; + for (ix = digs; ix < pa; ix++) { + int tx, ty, iy; + mp_digit *tmpx, *tmpy; + + /* get offsets into the two bignums */ + ty = MIN(b->used - 1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = b->dp + ty; + + /* this is the number of times the loop will iterrate, essentially its + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used - tx, ty + 1); + + /* execute loop */ + for (iz = 0; iz < iy; iz++) { + _W += ((mp_word) * tmpx++) * ((mp_word) * tmpy--); + } + + /* store term */ + W[ix] = ((mp_digit)_W) & MP_MASK; + + /* make next carry */ + _W = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = c->used; + c->used = pa; + + { + mp_digit *tmpc; + + tmpc = c->dp + digs; + for (ix = digs; ix < pa; ix++) { + /* now extract the previous digit [below the carry] */ + *tmpc++ = W[ix]; + } + + /* clear unused digits [that existed in the old copy of c] */ + for ( ; ix < olduse; ix++) { + *tmpc++ = 0; + } + } + mp_clamp(c); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_FAST_S_MP_SQR_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* the jist of squaring... + * you do like mult except the offset of the tmpx [one that + * starts closer to zero] can't equal the offset of tmpy. + * So basically you set up iy like before then you min it with + * (ty-tx) so that it never happens. You double all those + * you add in the inner loop + + After that loop you do the squares and add them in. + */ + +int fast_s_mp_sqr(mp_int *a, mp_int *b) { + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY], *tmpx; + mp_word W1; + + /* grow the destination as required */ + pa = a->used + a->used; + if (b->alloc < pa) { + if ((res = mp_grow(b, pa)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + W1 = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty, iy; + mp_word _W; + mp_digit *tmpy; + + /* clear counter */ + _W = 0; + + /* get offsets into the two bignums */ + ty = MIN(a->used - 1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = a->dp + ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used - tx, ty + 1); + + /* now for squaring tx can never equal ty + * we halve the distance since they approach at a rate of 2x + * and we have to round because odd cases need to be executed + */ + iy = MIN(iy, ((ty - tx) + 1) >> 1); + + /* execute loop */ + for (iz = 0; iz < iy; iz++) { + _W += ((mp_word) * tmpx++) * ((mp_word) * tmpy--); + } + + /* double the inner product and add carry */ + _W = _W + _W + W1; + + /* even columns have the square term in them */ + if ((ix & 1) == 0) { + _W += ((mp_word)a->dp[ix >> 1]) * ((mp_word)a->dp[ix >> 1]); + } + + /* store it */ + W[ix] = (mp_digit)(_W & MP_MASK); + + /* make next carry */ + W1 = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = b->used; + b->used = a->used + a->used; + + { + mp_digit *tmpb; + tmpb = b->dp; + for (ix = 0; ix < pa; ix++) { + *tmpb++ = W[ix] & MP_MASK; + } + + /* clear unused digits [that existed in the old copy of c] */ + for ( ; ix < olduse; ix++) { + *tmpb++ = 0; + } + } + mp_clamp(b); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_2EXPT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* computes a = 2**b + * + * Simple algorithm which zeroes the int, grows it then just sets one bit + * as required. + */ +int +mp_2expt(mp_int *a, int b) { + int res; + + /* zero a as per default */ + mp_zero(a); + + /* grow a to accomodate the single bit */ + if ((res = mp_grow(a, (b / DIGIT_BIT) + 1)) != MP_OKAY) { + return res; + } + + /* set the used count of where the bit will go */ + a->used = (b / DIGIT_BIT) + 1; + + /* put the single bit in its place */ + a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT); + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_ABS_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* b = |a| + * + * Simple function copies the input and fixes the sign to positive + */ +int +mp_abs(mp_int *a, mp_int *b) { + int res; + + /* copy a to b */ + if (a != b) { + if ((res = mp_copy(a, b)) != MP_OKAY) { + return res; + } + } + + /* force the sign of b to positive */ + b->sign = MP_ZPOS; + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_ADD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* high level addition (handles signs) */ +int mp_add(mp_int *a, mp_int *b, mp_int *c) { + int sa, sb, res; + + /* get sign of both inputs */ + sa = a->sign; + sb = b->sign; + + /* handle two cases, not four */ + if (sa == sb) { + /* both positive or both negative */ + /* add their magnitudes, copy the sign */ + c->sign = sa; + res = s_mp_add(a, b, c); + } else { + /* one positive, the other negative */ + /* subtract the one with the greater magnitude from */ + /* the one of the lesser magnitude. The result gets */ + /* the sign of the one with the greater magnitude. */ + if (mp_cmp_mag(a, b) == MP_LT) { + c->sign = sb; + res = s_mp_sub(b, a, c); + } else { + c->sign = sa; + res = s_mp_sub(a, b, c); + } + } + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_ADD_D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* single digit addition */ +int +mp_add_d(mp_int *a, mp_digit b, mp_int *c) { + int res, ix, oldused; + mp_digit *tmpa, *tmpc, mu; + + /* grow c as required */ + if (c->alloc < (a->used + 1)) { + if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* if a is negative and |a| >= b, call c = |a| - b */ + if ((a->sign == MP_NEG) && ((a->used > 1) || (a->dp[0] >= b))) { + /* temporarily fix sign of a */ + a->sign = MP_ZPOS; + + /* c = |a| - b */ + res = mp_sub_d(a, b, c); + + /* fix sign */ + a->sign = c->sign = MP_NEG; + + /* clamp */ + mp_clamp(c); + + return res; + } + + /* old number of used digits in c */ + oldused = c->used; + + /* sign always positive */ + c->sign = MP_ZPOS; + + /* source alias */ + tmpa = a->dp; + + /* destination alias */ + tmpc = c->dp; + + /* if a is positive */ + if (a->sign == MP_ZPOS) { + /* add digit, after this we're propagating + * the carry. + */ + *tmpc = *tmpa++ + b; + mu = *tmpc >> DIGIT_BIT; + *tmpc++ &= MP_MASK; + + /* now handle rest of the digits */ + for (ix = 1; ix < a->used; ix++) { + *tmpc = *tmpa++ + mu; + mu = *tmpc >> DIGIT_BIT; + *tmpc++ &= MP_MASK; + } + /* set final carry */ + ix++; + *tmpc++ = mu; + + /* setup size */ + c->used = a->used + 1; + } else { + /* a was negative and |a| < b */ + c->used = 1; + + /* the result is a single digit */ + if (a->used == 1) { + *tmpc++ = b - a->dp[0]; + } else { + *tmpc++ = b; + } + + /* setup count so the clearing of oldused + * can fall through correctly + */ + ix = 1; + } + + /* now zero to oldused */ + while (ix++ < oldused) { + *tmpc++ = 0; + } + mp_clamp(c); + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_ADDMOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* d = a + b (mod c) */ +int +mp_addmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d) { + int res; + mp_int t; + + if ((res = mp_init(&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_add(a, b, &t)) != MP_OKAY) { + mp_clear(&t); + return res; + } + res = mp_mod(&t, c, d); + mp_clear(&t); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_AND_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* AND two ints together */ +int +mp_and(mp_int *a, mp_int *b, mp_int *c) { + int res, ix, px; + mp_int t, *x; + + if (a->used > b->used) { + if ((res = mp_init_copy(&t, a)) != MP_OKAY) { + return res; + } + px = b->used; + x = b; + } else { + if ((res = mp_init_copy(&t, b)) != MP_OKAY) { + return res; + } + px = a->used; + x = a; + } + + for (ix = 0; ix < px; ix++) { + t.dp[ix] &= x->dp[ix]; + } + + /* zero digits above the last from the smallest mp_int */ + for ( ; ix < t.used; ix++) { + t.dp[ix] = 0; + } + + mp_clamp(&t); + mp_exch(c, &t); + mp_clear(&t); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_CLAMP_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* trim unused digits + * + * This is used to ensure that leading zero digits are + * trimed and the leading "used" digit will be non-zero + * Typically very fast. Also fixes the sign if there + * are no more leading digits + */ +void +mp_clamp(mp_int *a) { + /* decrease used while the most significant digit is + * zero. + */ + while ((a->used > 0) && (a->dp[a->used - 1] == 0)) { + --(a->used); + } + + /* reset the sign flag if used == 0 */ + if (a->used == 0) { + a->sign = MP_ZPOS; + } +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_CLEAR_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* clear one (frees) */ +void +mp_clear(mp_int *a) { + int i; + + /* only do anything if a hasn't been freed previously */ + if (a->dp != NULL) { + /* first zero the digits */ + for (i = 0; i < a->used; i++) { + a->dp[i] = 0; + } + + /* free ram */ + XFREE(a->dp); + + /* reset members to make debugging easier */ + a->dp = NULL; + a->alloc = a->used = 0; + a->sign = MP_ZPOS; + } +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_CLEAR_MULTI_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ +#include + +void mp_clear_multi(mp_int *mp, ...) { + mp_int *next_mp = mp; + va_list args; + + va_start(args, mp); + while (next_mp != NULL) { + mp_clear(next_mp); + next_mp = va_arg(args, mp_int *); + } + va_end(args); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_CMP_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* compare two ints (signed)*/ +int +mp_cmp(mp_int *a, mp_int *b) { + /* compare based on sign */ + if (a->sign != b->sign) { + if (a->sign == MP_NEG) { + return MP_LT; + } else { + return MP_GT; + } + } + + /* compare digits */ + if (a->sign == MP_NEG) { + /* if negative compare opposite direction */ + return mp_cmp_mag(b, a); + } else { + return mp_cmp_mag(a, b); + } +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_CMP_D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* compare a digit */ +int mp_cmp_d(mp_int *a, mp_digit b) { + /* compare based on sign */ + if (a->sign == MP_NEG) { + return MP_LT; + } + + /* compare based on magnitude */ + if (a->used > 1) { + return MP_GT; + } + + /* compare the only digit of a to b */ + if (a->dp[0] > b) { + return MP_GT; + } else if (a->dp[0] < b) { + return MP_LT; + } else { + return MP_EQ; + } +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_CMP_MAG_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* compare maginitude of two ints (unsigned) */ +int mp_cmp_mag(mp_int *a, mp_int *b) { + int n; + mp_digit *tmpa, *tmpb; + + /* compare based on # of non-zero digits */ + if (a->used > b->used) { + return MP_GT; + } + + if (a->used < b->used) { + return MP_LT; + } + + /* alias for a */ + tmpa = a->dp + (a->used - 1); + + /* alias for b */ + tmpb = b->dp + (a->used - 1); + + /* compare based on digits */ + for (n = 0; n < a->used; ++n, --tmpa, --tmpb) { + if (*tmpa > *tmpb) { + return MP_GT; + } + + if (*tmpa < *tmpb) { + return MP_LT; + } + } + return MP_EQ; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_CNT_LSB_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +static const int lnz[16] = { + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 +}; + +/* Counts the number of lsbs which are zero before the first zero bit */ +int mp_cnt_lsb(mp_int *a) { + int x; + mp_digit q, qq; + + /* easy out */ + if (mp_iszero(a) == MP_YES) { + return 0; + } + + /* scan lower digits until non-zero */ + for (x = 0; (x < a->used) && (a->dp[x] == 0); x++) { + } + q = a->dp[x]; + x *= DIGIT_BIT; + + /* now scan this digit until a 1 is found */ + if ((q & 1) == 0) { + do { + qq = q & 15; + x += lnz[qq]; + q >>= 4; + } while (qq == 0); + } + return x; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_COPY_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* copy, b = a */ +int +mp_copy(mp_int *a, mp_int *b) { + int res, n; + + /* if dst == src do nothing */ + if (a == b) { + return MP_OKAY; + } + + /* grow dest */ + if (b->alloc < a->used) { + if ((res = mp_grow(b, a->used)) != MP_OKAY) { + return res; + } + } + + /* zero b and copy the parameters over */ + { + mp_digit *tmpa, *tmpb; + + /* pointer aliases */ + + /* source */ + tmpa = a->dp; + + /* destination */ + tmpb = b->dp; + + /* copy all the digits */ + for (n = 0; n < a->used; n++) { + *tmpb++ = *tmpa++; + } + + /* clear high digits */ + for ( ; n < b->used; n++) { + *tmpb++ = 0; + } + } + + /* copy used count and sign */ + b->used = a->used; + b->sign = a->sign; + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_COUNT_BITS_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* returns the number of bits in an int */ +int +mp_count_bits(mp_int *a) { + int r; + mp_digit q; + + /* shortcut */ + if (a->used == 0) { + return 0; + } + + /* get number of digits and add that */ + r = (a->used - 1) * DIGIT_BIT; + + /* take the last digit and count the bits in it */ + q = a->dp[a->used - 1]; + while (q > ((mp_digit)0)) { + ++r; + q >>= ((mp_digit)1); + } + return r; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_DIV_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + + #ifdef BN_MP_DIV_SMALL + +/* slower bit-bang division... also smaller */ +int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d) { + mp_int ta, tb, tq, q; + int res, n, n2; + + /* is divisor zero ? */ + if (mp_iszero(b) == MP_YES) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag(a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy(a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero(c); + } + return res; + } + + /* init our temps */ + if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) { + return res; + } + + + mp_set(&tq, 1); + n = mp_count_bits(a) - mp_count_bits(b); + if (((res = mp_abs(a, &ta)) != MP_OKAY) || + ((res = mp_abs(b, &tb)) != MP_OKAY) || + ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) || + ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) { + goto LBL_ERR; + } + + while (n-- >= 0) { + if (mp_cmp(&tb, &ta) != MP_GT) { + if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) || + ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) { + goto LBL_ERR; + } + } + if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) || + ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) { + goto LBL_ERR; + } + } + + /* now q == quotient and ta == remainder */ + n = a->sign; + n2 = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + if (c != NULL) { + mp_exch(c, &q); + c->sign = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2; + } + if (d != NULL) { + mp_exch(d, &ta); + d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n; + } +LBL_ERR: + mp_clear_multi(&ta, &tb, &tq, &q, NULL); + return res; +} + + #else + +/* integer signed division. + * c*b + d == a [e.g. a/b, c=quotient, d=remainder] + * HAC pp.598 Algorithm 14.20 + * + * Note that the description in HAC is horribly + * incomplete. For example, it doesn't consider + * the case where digits are removed from 'x' in + * the inner loop. It also doesn't consider the + * case that y has fewer than three digits, etc.. + * + * The overall algorithm is as described as + * 14.20 from HAC but fixed to treat these cases. + */ +int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d) { + mp_int q, x, y, t1, t2; + int res, n, t, i, norm, neg; + + /* is divisor zero ? */ + if (mp_iszero(b) == MP_YES) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag(a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy(a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero(c); + } + return res; + } + + if ((res = mp_init_size(&q, a->used + 2)) != MP_OKAY) { + return res; + } + q.used = a->used + 2; + + if ((res = mp_init(&t1)) != MP_OKAY) { + goto LBL_Q; + } + + if ((res = mp_init(&t2)) != MP_OKAY) { + goto LBL_T1; + } + + if ((res = mp_init_copy(&x, a)) != MP_OKAY) { + goto LBL_T2; + } + + if ((res = mp_init_copy(&y, b)) != MP_OKAY) { + goto LBL_X; + } + + /* fix the sign */ + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + x.sign = y.sign = MP_ZPOS; + + /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */ + norm = mp_count_bits(&y) % DIGIT_BIT; + if (norm < (int)(DIGIT_BIT - 1)) { + norm = (DIGIT_BIT - 1) - norm; + if ((res = mp_mul_2d(&x, norm, &x)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_mul_2d(&y, norm, &y)) != MP_OKAY) { + goto LBL_Y; + } + } else { + norm = 0; + } + + /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */ + n = x.used - 1; + t = y.used - 1; + + /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */ + if ((res = mp_lshd(&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */ + goto LBL_Y; + } + + while (mp_cmp(&x, &y) != MP_LT) { + ++(q.dp[n - t]); + if ((res = mp_sub(&x, &y, &x)) != MP_OKAY) { + goto LBL_Y; + } + } + + /* reset y by shifting it back down */ + mp_rshd(&y, n - t); + + /* step 3. for i from n down to (t + 1) */ + for (i = n; i >= (t + 1); i--) { + if (i > x.used) { + continue; + } + + /* step 3.1 if xi == yt then set q{i-t-1} to b-1, + * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ + if (x.dp[i] == y.dp[t]) { + q.dp[(i - t) - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1); + } else { + mp_word tmp; + tmp = ((mp_word)x.dp[i]) << ((mp_word)DIGIT_BIT); + tmp |= ((mp_word)x.dp[i - 1]); + tmp /= ((mp_word)y.dp[t]); + if (tmp > (mp_word)MP_MASK) { + tmp = MP_MASK; + } + q.dp[(i - t) - 1] = (mp_digit)(tmp & (mp_word)(MP_MASK)); + } + + /* while (q{i-t-1} * (yt * b + y{t-1})) > + xi * b**2 + xi-1 * b + xi-2 + + do q{i-t-1} -= 1; + */ + q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] + 1) & MP_MASK; + do { + q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] - 1) & MP_MASK; + + /* find left hand */ + mp_zero(&t1); + t1.dp[0] = ((t - 1) < 0) ? 0 : y.dp[t - 1]; + t1.dp[1] = y.dp[t]; + t1.used = 2; + if ((res = mp_mul_d(&t1, q.dp[(i - t) - 1], &t1)) != MP_OKAY) { + goto LBL_Y; + } + + /* find right hand */ + t2.dp[0] = ((i - 2) < 0) ? 0 : x.dp[i - 2]; + t2.dp[1] = ((i - 1) < 0) ? 0 : x.dp[i - 1]; + t2.dp[2] = x.dp[i]; + t2.used = 3; + } while (mp_cmp_mag(&t1, &t2) == MP_GT); + + /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */ + if ((res = mp_mul_d(&y, q.dp[(i - t) - 1], &t1)) != MP_OKAY) { + goto LBL_Y; + } + + if ((res = mp_lshd(&t1, (i - t) - 1)) != MP_OKAY) { + goto LBL_Y; + } + + if ((res = mp_sub(&x, &t1, &x)) != MP_OKAY) { + goto LBL_Y; + } + + /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */ + if (x.sign == MP_NEG) { + if ((res = mp_copy(&y, &t1)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_lshd(&t1, (i - t) - 1)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_add(&x, &t1, &x)) != MP_OKAY) { + goto LBL_Y; + } + + q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] - 1UL) & MP_MASK; + } + } + + /* now q is the quotient and x is the remainder + * [which we have to normalize] + */ + + /* get sign before writing to c */ + x.sign = (x.used == 0) ? MP_ZPOS : a->sign; + + if (c != NULL) { + mp_clamp(&q); + mp_exch(&q, c); + c->sign = neg; + } + + if (d != NULL) { + if ((res = mp_div_2d(&x, norm, &x, NULL)) != MP_OKAY) { + goto LBL_Y; + } + mp_exch(&x, d); + } + + res = MP_OKAY; + +LBL_Y: mp_clear(&y); +LBL_X: mp_clear(&x); +LBL_T2: mp_clear(&t2); +LBL_T1: mp_clear(&t1); +LBL_Q: mp_clear(&q); + return res; +} + #endif +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_DIV_2_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* b = a/2 */ +int mp_div_2(mp_int *a, mp_int *b) { + int x, res, oldused; + + /* copy */ + if (b->alloc < a->used) { + if ((res = mp_grow(b, a->used)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + { + mp_digit r, rr, *tmpa, *tmpb; + + /* source alias */ + tmpa = a->dp + b->used - 1; + + /* dest alias */ + tmpb = b->dp + b->used - 1; + + /* carry */ + r = 0; + for (x = b->used - 1; x >= 0; x--) { + /* get the carry for the next iteration */ + rr = *tmpa & 1; + + /* shift the current digit, add in carry and store */ + *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1)); + + /* forward carry to next iteration */ + r = rr; + } + + /* zero excess digits */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + mp_clamp(b); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_DIV_2D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* shift right by a certain bit count (store quotient in c, optional remainder in d) */ +int mp_div_2d(mp_int *a, int b, mp_int *c, mp_int *d) { + mp_digit D, r, rr; + int x, res; + mp_int t; + + + /* if the shift count is <= 0 then we do no work */ + if (b <= 0) { + res = mp_copy(a, c); + if (d != NULL) { + mp_zero(d); + } + return res; + } + + if ((res = mp_init(&t)) != MP_OKAY) { + return res; + } + + /* get the remainder */ + if (d != NULL) { + if ((res = mp_mod_2d(a, b, &t)) != MP_OKAY) { + mp_clear(&t); + return res; + } + } + + /* copy */ + if ((res = mp_copy(a, c)) != MP_OKAY) { + mp_clear(&t); + return res; + } + + /* shift by as many digits in the bit count */ + if (b >= (int)DIGIT_BIT) { + mp_rshd(c, b / DIGIT_BIT); + } + + /* shift any bit count < DIGIT_BIT */ + D = (mp_digit)(b % DIGIT_BIT); + if (D != 0) { + mp_digit *tmpc, mask, shift; + + /* mask */ + mask = (((mp_digit)1) << D) - 1; + + /* shift for lsb */ + shift = DIGIT_BIT - D; + + /* alias */ + tmpc = c->dp + (c->used - 1); + + /* carry */ + r = 0; + for (x = c->used - 1; x >= 0; x--) { + /* get the lower bits of this word in a temp */ + rr = *tmpc & mask; + + /* shift the current word and mix in the carry bits from the previous word */ + *tmpc = (*tmpc >> D) | (r << shift); + --tmpc; + + /* set the carry to the carry bits of the current word found above */ + r = rr; + } + } + mp_clamp(c); + if (d != NULL) { + mp_exch(&t, d); + } + mp_clear(&t); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_DIV_3_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* divide by three (based on routine from MPI and the GMP manual) */ +int +mp_div_3(mp_int *a, mp_int *c, mp_digit *d) { + mp_int q; + mp_word w, t; + mp_digit b; + int res, ix; + + /* b = 2**DIGIT_BIT / 3 */ + b = (((mp_word)1) << ((mp_word)DIGIT_BIT)) / ((mp_word)3); + + if ((res = mp_init_size(&q, a->used)) != MP_OKAY) { + return res; + } + + q.used = a->used; + q.sign = a->sign; + w = 0; + for (ix = a->used - 1; ix >= 0; ix--) { + w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]); + + if (w >= 3) { + /* multiply w by [1/3] */ + t = (w * ((mp_word)b)) >> ((mp_word)DIGIT_BIT); + + /* now subtract 3 * [w/3] from w, to get the remainder */ + w -= t + t + t; + + /* fixup the remainder as required since + * the optimization is not exact. + */ + while (w >= 3) { + t += 1; + w -= 3; + } + } else { + t = 0; + } + q.dp[ix] = (mp_digit)t; + } + + /* [optional] store the remainder */ + if (d != NULL) { + *d = (mp_digit)w; + } + + /* [optional] store the quotient */ + if (c != NULL) { + mp_clamp(&q); + mp_exch(&q, c); + } + mp_clear(&q); + + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_DIV_D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +static int s_is_power_of_two(mp_digit b, int *p) { + int x; + + /* fast return if no power of two */ + if ((b == 0) || ((b & (b - 1)) != 0)) { + return 0; + } + + for (x = 0; x < DIGIT_BIT; x++) { + if (b == (((mp_digit)1) << x)) { + *p = x; + return 1; + } + } + return 0; +} + +/* single digit division (based on routine from MPI) */ +int mp_div_d(mp_int *a, mp_digit b, mp_int *c, mp_digit *d) { + mp_int q; + mp_word w; + mp_digit t; + int res, ix; + + /* cannot divide by zero */ + if (b == 0) { + return MP_VAL; + } + + /* quick outs */ + if ((b == 1) || (mp_iszero(a) == MP_YES)) { + if (d != NULL) { + *d = 0; + } + if (c != NULL) { + return mp_copy(a, c); + } + return MP_OKAY; + } + + /* power of two ? */ + if (s_is_power_of_two(b, &ix) == 1) { + if (d != NULL) { + *d = a->dp[0] & ((((mp_digit)1) << ix) - 1); + } + if (c != NULL) { + return mp_div_2d(a, ix, c, NULL); + } + return MP_OKAY; + } + + #ifdef BN_MP_DIV_3_C + /* three? */ + if (b == 3) { + return mp_div_3(a, c, d); + } + #endif + + /* no easy answer [c'est la vie]. Just division */ + if ((res = mp_init_size(&q, a->used)) != MP_OKAY) { + return res; + } + + q.used = a->used; + q.sign = a->sign; + w = 0; + for (ix = a->used - 1; ix >= 0; ix--) { + w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]); + + if (w >= b) { + t = (mp_digit)(w / b); + w -= ((mp_word)t) * ((mp_word)b); + } else { + t = 0; + } + q.dp[ix] = (mp_digit)t; + } + + if (d != NULL) { + *d = (mp_digit)w; + } + + if (c != NULL) { + mp_clamp(&q); + mp_exch(&q, c); + } + mp_clear(&q); + + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_DR_IS_MODULUS_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* determines if a number is a valid DR modulus */ +int mp_dr_is_modulus(mp_int *a) { + int ix; + + /* must be at least two digits */ + if (a->used < 2) { + return 0; + } + + /* must be of the form b**k - a [a <= b] so all + * but the first digit must be equal to -1 (mod b). + */ + for (ix = 1; ix < a->used; ix++) { + if (a->dp[ix] != MP_MASK) { + return 0; + } + } + return 1; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_DR_REDUCE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* reduce "x" in place modulo "n" using the Diminished Radix algorithm. + * + * Based on algorithm from the paper + * + * "Generating Efficient Primes for Discrete Log Cryptosystems" + * Chae Hoon Lim, Pil Joong Lee, + * POSTECH Information Research Laboratories + * + * The modulus must be of a special format [see manual] + * + * Has been modified to use algorithm 7.10 from the LTM book instead + * + * Input x must be in the range 0 <= x <= (n-1)**2 + */ +int +mp_dr_reduce(mp_int *x, mp_int *n, mp_digit k) { + int err, i, m; + mp_word r; + mp_digit mu, *tmpx1, *tmpx2; + + /* m = digits in modulus */ + m = n->used; + + /* ensure that "x" has at least 2m digits */ + if (x->alloc < (m + m)) { + if ((err = mp_grow(x, m + m)) != MP_OKAY) { + return err; + } + } + +/* top of loop, this is where the code resumes if + * another reduction pass is required. + */ +top: + /* aliases for digits */ + /* alias for lower half of x */ + tmpx1 = x->dp; + + /* alias for upper half of x, or x/B**m */ + tmpx2 = x->dp + m; + + /* set carry to zero */ + mu = 0; + + /* compute (x mod B**m) + k * [x/B**m] inline and inplace */ + for (i = 0; i < m; i++) { + r = (((mp_word) * tmpx2++) * (mp_word)k) + *tmpx1 + mu; + *tmpx1++ = (mp_digit)(r & MP_MASK); + mu = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + } + + /* set final carry */ + *tmpx1++ = mu; + + /* zero words above m */ + for (i = m + 1; i < x->used; i++) { + *tmpx1++ = 0; + } + + /* clamp, sub and return */ + mp_clamp(x); + + /* if x >= n then subtract and reduce again + * Each successive "recursion" makes the input smaller and smaller. + */ + if (mp_cmp_mag(x, n) != MP_LT) { + if ((err = s_mp_sub(x, n, x)) != MP_OKAY) { + return err; + } + goto top; + } + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_DR_SETUP_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* determines the setup value */ +void mp_dr_setup(mp_int *a, mp_digit *d) { + /* the casts are required if DIGIT_BIT is one less than + * the number of bits in a mp_digit [e.g. DIGIT_BIT==31] + */ + *d = (mp_digit)((((mp_word)1) << ((mp_word)DIGIT_BIT)) - + ((mp_word)a->dp[0])); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_EXCH_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* swap the elements of two integers, for cases where you can't simply swap the + * mp_int pointers around + */ +void +mp_exch(mp_int *a, mp_int *b) { + mp_int t; + + t = *a; + *a = *b; + *b = t; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_EXPORT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* based on gmp's mpz_export. + * see http://gmplib.org/manual/Integer-Import-and-Export.html + */ +int mp_export(void *rop, size_t *countp, int order, size_t size, + int endian, size_t nails, mp_int *op) { + int result; + size_t odd_nails, nail_bytes, i, j, bits, count; + unsigned char odd_nail_mask; + + mp_int t; + + if ((result = mp_init_copy(&t, op)) != MP_OKAY) { + return result; + } + + if (endian == 0) { + union { + unsigned int i; + char c[4]; + } lint; + lint.i = 0x01020304; + + endian = (lint.c[0] == 4) ? -1 : 1; + } + + odd_nails = (nails % 8); + odd_nail_mask = 0xff; + for (i = 0; i < odd_nails; ++i) { + odd_nail_mask ^= (1 << (7 - i)); + } + nail_bytes = nails / 8; + + bits = mp_count_bits(&t); + count = (bits / ((size * 8) - nails)) + (((bits % ((size * 8) - nails)) != 0) ? 1 : 0); + + for (i = 0; i < count; ++i) { + for (j = 0; j < size; ++j) { + unsigned char *byte = ( + (unsigned char *)rop + + (((order == -1) ? i : ((count - 1) - i)) * size) + + ((endian == -1) ? j : ((size - 1) - j)) + ); + + if (j >= (size - nail_bytes)) { + *byte = 0; + continue; + } + + *byte = (unsigned char)((j == ((size - nail_bytes) - 1)) ? (t.dp[0] & odd_nail_mask) : (t.dp[0] & 0xFF)); + + if ((result = mp_div_2d(&t, ((j == ((size - nail_bytes) - 1)) ? (8 - odd_nails) : 8), &t, NULL)) != MP_OKAY) { + mp_clear(&t); + return result; + } + } + } + + mp_clear(&t); + + if (countp != NULL) { + *countp = count; + } + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_EXPT_D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* wrapper function for mp_expt_d_ex() */ +int mp_expt_d(mp_int *a, mp_digit b, mp_int *c) { + return mp_expt_d_ex(a, b, c, 0); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_EXPT_D_EX_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* calculate c = a**b using a square-multiply algorithm */ +int mp_expt_d_ex(mp_int *a, mp_digit b, mp_int *c, int fast) { + int res; + unsigned int x; + + mp_int g; + + if ((res = mp_init_copy(&g, a)) != MP_OKAY) { + return res; + } + + /* set initial result */ + mp_set(c, 1); + + if (fast != 0) { + while (b > 0) { + /* if the bit is set multiply */ + if ((b & 1) != 0) { + if ((res = mp_mul(c, &g, c)) != MP_OKAY) { + mp_clear(&g); + return res; + } + } + + /* square */ + if (b > 1) { + if ((res = mp_sqr(&g, &g)) != MP_OKAY) { + mp_clear(&g); + return res; + } + } + + /* shift to next bit */ + b >>= 1; + } + } else { + for (x = 0; x < DIGIT_BIT; x++) { + /* square */ + if ((res = mp_sqr(c, c)) != MP_OKAY) { + mp_clear(&g); + return res; + } + + /* if the bit is set multiply */ + if ((b & (mp_digit)(((mp_digit)1) << (DIGIT_BIT - 1))) != 0) { + if ((res = mp_mul(c, &g, c)) != MP_OKAY) { + mp_clear(&g); + return res; + } + } + + /* shift to next bit */ + b <<= 1; + } + } /* if ... else */ + + mp_clear(&g); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_EXPTMOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + + +/* this is a shell function that calls either the normal or Montgomery + * exptmod functions. Originally the call to the montgomery code was + * embedded in the normal function but that wasted alot of stack space + * for nothing (since 99% of the time the Montgomery code would be called) + */ +int mp_exptmod(mp_int *G, mp_int *X, mp_int *P, mp_int *Y) { + int dr; + + /* modulus P must be positive */ + if (P->sign == MP_NEG) { + return MP_VAL; + } + + /* if exponent X is negative we have to recurse */ + if (X->sign == MP_NEG) { + #ifdef BN_MP_INVMOD_C + mp_int tmpG, tmpX; + int err; + + /* first compute 1/G mod P */ + if ((err = mp_init(&tmpG)) != MP_OKAY) { + return err; + } + if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + + /* now get |X| */ + if ((err = mp_init(&tmpX)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + if ((err = mp_abs(X, &tmpX)) != MP_OKAY) { + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; + } + + /* and now compute (1/G)**|X| instead of G**X [X < 0] */ + err = mp_exptmod(&tmpG, &tmpX, P, Y); + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; + #else + /* no invmod */ + return MP_VAL; + #endif + } + +/* modified diminished radix reduction */ + #if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C) + if (mp_reduce_is_2k_l(P) == MP_YES) { + return s_mp_exptmod(G, X, P, Y, 1); + } + #endif + + #ifdef BN_MP_DR_IS_MODULUS_C + /* is it a DR modulus? */ + dr = mp_dr_is_modulus(P); + #else + /* default to no */ + dr = 0; + #endif + + #ifdef BN_MP_REDUCE_IS_2K_C + /* if not, is it a unrestricted DR modulus? */ + if (dr == 0) { + dr = mp_reduce_is_2k(P) << 1; + } + #endif + + /* if the modulus is odd or dr != 0 use the montgomery method */ + #ifdef BN_MP_EXPTMOD_FAST_C + if ((mp_isodd(P) == MP_YES) || (dr != 0)) { + return mp_exptmod_fast(G, X, P, Y, dr); + } else { + #endif + #ifdef BN_S_MP_EXPTMOD_C + /* otherwise use the generic Barrett reduction technique */ + return s_mp_exptmod(G, X, P, Y, 0); + #else + /* no exptmod for evens */ + return MP_VAL; + #endif + #ifdef BN_MP_EXPTMOD_FAST_C +} + #endif +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_EXPTMOD_FAST_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85 + * + * Uses a left-to-right k-ary sliding window to compute the modular exponentiation. + * The value of k changes based on the size of the exponent. + * + * Uses Montgomery or Diminished Radix reduction [whichever appropriate] + */ + + #ifdef MP_LOW_MEM + #define TAB_SIZE 32 + #else + #define TAB_SIZE 256 + #endif + +int mp_exptmod_fast(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int redmode) { + mp_int M[TAB_SIZE], res; + mp_digit buf, mp; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + + /* use a pointer to the reduction algorithm. This allows us to use + * one of many reduction algorithms without modding the guts of + * the code with if statements everywhere. + */ + int (*redux)(mp_int *, mp_int *, mp_digit); + + /* find window size */ + x = mp_count_bits(X); + if (x <= 7) { + winsize = 2; + } else if (x <= 36) { + winsize = 3; + } else if (x <= 140) { + winsize = 4; + } else if (x <= 450) { + winsize = 5; + } else if (x <= 1303) { + winsize = 6; + } else if (x <= 3529) { + winsize = 7; + } else { + winsize = 8; + } + + #ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } + #endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1 << (winsize - 1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1 << (winsize - 1); y < x; y++) { + mp_clear(&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* determine and setup reduction code */ + if (redmode == 0) { + #ifdef BN_MP_MONTGOMERY_SETUP_C + /* now setup montgomery */ + if ((err = mp_montgomery_setup(P, &mp)) != MP_OKAY) { + goto LBL_M; + } + #else + err = MP_VAL; + goto LBL_M; + #endif + + /* automatically pick the comba one if available (saves quite a few calls/ifs) */ + #ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C + if ((((P->used * 2) + 1) < MP_WARRAY) && + (P->used < (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { + redux = fast_mp_montgomery_reduce; + } else + #endif + { + #ifdef BN_MP_MONTGOMERY_REDUCE_C + /* use slower baseline Montgomery method */ + redux = mp_montgomery_reduce; + #else + err = MP_VAL; + goto LBL_M; + #endif + } + } else if (redmode == 1) { + #if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C) + /* setup DR reduction for moduli of the form B**k - b */ + mp_dr_setup(P, &mp); + redux = mp_dr_reduce; + #else + err = MP_VAL; + goto LBL_M; + #endif + } else { + #if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C) + /* setup DR reduction for moduli of the form 2**k - b */ + if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) { + goto LBL_M; + } + redux = mp_reduce_2k; + #else + err = MP_VAL; + goto LBL_M; + #endif + } + + /* setup result */ + if ((err = mp_init(&res)) != MP_OKAY) { + goto LBL_M; + } + + /* create M table + * + + * + * The first half of the table is not computed though accept for M[0] and M[1] + */ + + if (redmode == 0) { + #ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + /* now we need R mod m */ + if ((err = mp_montgomery_calc_normalization(&res, P)) != MP_OKAY) { + goto LBL_RES; + } + #else + err = MP_VAL; + goto LBL_RES; + #endif + + /* now set M[1] to G * R mod m */ + if ((err = mp_mulmod(G, &res, P, &M[1])) != MP_OKAY) { + goto LBL_RES; + } + } else { + mp_set(&res, 1); + if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { + goto LBL_RES; + } + } + + /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */ + if ((err = mp_copy(&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_RES; + } + + for (x = 0; x < (winsize - 1); x++) { + if ((err = mp_sqr(&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* create upper table */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul(&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&M[x], P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for ( ; ; ) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits so break */ + if (digidx == -1) { + break; + } + /* read next digit and reset bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int)DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if ((mode == 0) && (y == 0)) { + continue; + } + + /* if the bit is zero and mode == 1 then we square */ + if ((mode == 1) && (y == 0)) { + if ((err = mp_sqr(&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr(&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* then multiply */ + if ((err = mp_mul(&res, &M[bitbuf], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + + /* empty window and reset */ + bitcpy = 0; + bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if ((mode == 2) && (bitcpy > 0)) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr(&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + + /* get next bit of the window */ + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul(&res, &M[1], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + } + } + + if (redmode == 0) { + /* fixup result if Montgomery reduction is used + * recall that any value in a Montgomery system is + * actually multiplied by R mod n. So we have + * to reduce one more time to cancel out the factor + * of R. + */ + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* swap res with Y */ + mp_exch(&res, Y); + err = MP_OKAY; +LBL_RES: mp_clear(&res); +LBL_M: + mp_clear(&M[1]); + for (x = 1 << (winsize - 1); x < (1 << winsize); x++) { + mp_clear(&M[x]); + } + return err; +} +#endif + + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_EXTEUCLID_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* Extended euclidean algorithm of (a, b) produces + a*u1 + b*u2 = u3 + */ +int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3) { + mp_int u1, u2, u3, v1, v2, v3, t1, t2, t3, q, tmp; + int err; + + if ((err = mp_init_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL)) != MP_OKAY) { + return err; + } + + /* initialize, (u1,u2,u3) = (1,0,a) */ + mp_set(&u1, 1); + if ((err = mp_copy(a, &u3)) != MP_OKAY) { + goto _ERR; + } + + /* initialize, (v1,v2,v3) = (0,1,b) */ + mp_set(&v2, 1); + if ((err = mp_copy(b, &v3)) != MP_OKAY) { + goto _ERR; + } + + /* loop while v3 != 0 */ + while (mp_iszero(&v3) == MP_NO) { + /* q = u3/v3 */ + if ((err = mp_div(&u3, &v3, &q, NULL)) != MP_OKAY) { + goto _ERR; + } + + /* (t1,t2,t3) = (u1,u2,u3) - (v1,v2,v3)q */ + if ((err = mp_mul(&v1, &q, &tmp)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_sub(&u1, &tmp, &t1)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_mul(&v2, &q, &tmp)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_sub(&u2, &tmp, &t2)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_mul(&v3, &q, &tmp)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_sub(&u3, &tmp, &t3)) != MP_OKAY) { + goto _ERR; + } + + /* (u1,u2,u3) = (v1,v2,v3) */ + if ((err = mp_copy(&v1, &u1)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_copy(&v2, &u2)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_copy(&v3, &u3)) != MP_OKAY) { + goto _ERR; + } + + /* (v1,v2,v3) = (t1,t2,t3) */ + if ((err = mp_copy(&t1, &v1)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_copy(&t2, &v2)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_copy(&t3, &v3)) != MP_OKAY) { + goto _ERR; + } + } + + /* make sure U3 >= 0 */ + if (u3.sign == MP_NEG) { + if ((err = mp_neg(&u1, &u1)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_neg(&u2, &u2)) != MP_OKAY) { + goto _ERR; + } + if ((err = mp_neg(&u3, &u3)) != MP_OKAY) { + goto _ERR; + } + } + + /* copy result out */ + if (U1 != NULL) { + mp_exch(U1, &u1); + } + if (U2 != NULL) { + mp_exch(U2, &u2); + } + if (U3 != NULL) { + mp_exch(U3, &u3); + } + + err = MP_OKAY; +_ERR: mp_clear_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL); + return err; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_FREAD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* read a bigint from a file stream in ASCII */ +int mp_fread(mp_int *a, int radix, FILE *stream) { + int err, ch, neg, y; + + /* clear a */ + mp_zero(a); + + /* if first digit is - then set negative */ + ch = fgetc(stream); + if (ch == '-') { + neg = MP_NEG; + ch = fgetc(stream); + } else { + neg = MP_ZPOS; + } + + for ( ; ; ) { + /* find y in the radix map */ + for (y = 0; y < radix; y++) { + if (mp_s_rmap[y] == ch) { + break; + } + } + if (y == radix) { + break; + } + + /* shift up and add */ + if ((err = mp_mul_d(a, radix, a)) != MP_OKAY) { + return err; + } + if ((err = mp_add_d(a, y, a)) != MP_OKAY) { + return err; + } + + ch = fgetc(stream); + } + if (mp_cmp_d(a, 0) != MP_EQ) { + a->sign = neg; + } + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_FWRITE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +int mp_fwrite(mp_int *a, int radix, FILE *stream) { + char *buf; + int err, len, x; + + if ((err = mp_radix_size(a, radix, &len)) != MP_OKAY) { + return err; + } + + buf = OPT_CAST(char) XMALLOC(len); + if (buf == NULL) { + return MP_MEM; + } + + if ((err = mp_toradix(a, buf, radix)) != MP_OKAY) { + XFREE(buf); + return err; + } + + for (x = 0; x < len; x++) { + if (fputc(buf[x], stream) == EOF) { + XFREE(buf); + return MP_VAL; + } + } + + XFREE(buf); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_GCD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* Greatest Common Divisor using the binary method */ +int mp_gcd(mp_int *a, mp_int *b, mp_int *c) { + mp_int u, v; + int k, u_lsb, v_lsb, res; + + /* either zero than gcd is the largest */ + if (mp_iszero(a) == MP_YES) { + return mp_abs(b, c); + } + if (mp_iszero(b) == MP_YES) { + return mp_abs(a, c); + } + + /* get copies of a and b we can modify */ + if ((res = mp_init_copy(&u, a)) != MP_OKAY) { + return res; + } + + if ((res = mp_init_copy(&v, b)) != MP_OKAY) { + goto LBL_U; + } + + /* must be positive for the remainder of the algorithm */ + u.sign = v.sign = MP_ZPOS; + + /* B1. Find the common power of two for u and v */ + u_lsb = mp_cnt_lsb(&u); + v_lsb = mp_cnt_lsb(&v); + k = MIN(u_lsb, v_lsb); + + if (k > 0) { + /* divide the power of two out */ + if ((res = mp_div_2d(&u, k, &u, NULL)) != MP_OKAY) { + goto LBL_V; + } + + if ((res = mp_div_2d(&v, k, &v, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + /* divide any remaining factors of two out */ + if (u_lsb != k) { + if ((res = mp_div_2d(&u, u_lsb - k, &u, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + if (v_lsb != k) { + if ((res = mp_div_2d(&v, v_lsb - k, &v, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + while (mp_iszero(&v) == MP_NO) { + /* make sure v is the largest */ + if (mp_cmp_mag(&u, &v) == MP_GT) { + /* swap u and v to make sure v is >= u */ + mp_exch(&u, &v); + } + + /* subtract smallest from largest */ + if ((res = s_mp_sub(&v, &u, &v)) != MP_OKAY) { + goto LBL_V; + } + + /* Divide out all factors of two */ + if ((res = mp_div_2d(&v, mp_cnt_lsb(&v), &v, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + /* multiply by 2**k which we divided out at the beginning */ + if ((res = mp_mul_2d(&u, k, c)) != MP_OKAY) { + goto LBL_V; + } + c->sign = MP_ZPOS; + res = MP_OKAY; +LBL_V: mp_clear(&u); +LBL_U: mp_clear(&v); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_GET_INT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* get the lower 32-bits of an mp_int */ +unsigned long mp_get_int(mp_int *a) { + int i; + mp_min_u32 res; + + if (a->used == 0) { + return 0; + } + + /* get number of digits of the lsb we have to read */ + i = MIN(a->used, (int)(((sizeof(unsigned long) * CHAR_BIT) + DIGIT_BIT - 1) / DIGIT_BIT)) - 1; + + /* get most significant digit of result */ + res = DIGIT(a, i); + + while (--i >= 0) { + res = (res << DIGIT_BIT) | DIGIT(a, i); + } + + /* force result to 32-bits always so it is consistent on non 32-bit platforms */ + return res & 0xFFFFFFFFUL; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_GET_LONG_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* get the lower unsigned long of an mp_int, platform dependent */ +unsigned long mp_get_long(mp_int *a) { + int i; + unsigned long res; + + if (a->used == 0) { + return 0; + } + + /* get number of digits of the lsb we have to read */ + i = MIN(a->used, (int)(((sizeof(unsigned long) * CHAR_BIT) + DIGIT_BIT - 1) / DIGIT_BIT)) - 1; + + /* get most significant digit of result */ + res = DIGIT(a, i); + + #if (ULONG_MAX != 0xffffffffuL) || (DIGIT_BIT < 32) + while (--i >= 0) { + res = (res << DIGIT_BIT) | DIGIT(a, i); + } + #endif + return res; +} +#endif + + + +#ifdef BN_MP_GET_LONG_LONG_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* get the lower unsigned long long of an mp_int, platform dependent */ +unsigned long long mp_get_long_long(mp_int *a) { + int i; + unsigned long long res; + + if (a->used == 0) { + return 0; + } + + /* get number of digits of the lsb we have to read */ + i = MIN(a->used, (int)(((sizeof(unsigned long long) * CHAR_BIT) + DIGIT_BIT - 1) / DIGIT_BIT)) - 1; + + /* get most significant digit of result */ + res = DIGIT(a, i); + + #if DIGIT_BIT < 64 + while (--i >= 0) { + res = (res << DIGIT_BIT) | DIGIT(a, i); + } + #endif + return res; +} +#endif + + + +#ifdef BN_MP_GROW_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* grow as required */ +int mp_grow(mp_int *a, int size) { + int i; + mp_digit *tmp; + + /* if the alloc size is smaller alloc more ram */ + if (a->alloc < size) { + /* ensure there are always at least MP_PREC digits extra on top */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* reallocate the array a->dp + * + * We store the return in a temporary variable + * in case the operation failed we don't want + * to overwrite the dp member of a. + */ + tmp = OPT_CAST(mp_digit) XREALLOC(a->dp, sizeof(mp_digit) * size); + if (tmp == NULL) { + /* reallocation failed but "a" is still valid [can be freed] */ + return MP_MEM; + } + + /* reallocation succeeded so set a->dp */ + a->dp = tmp; + + /* zero excess digits */ + i = a->alloc; + a->alloc = size; + for ( ; i < a->alloc; i++) { + a->dp[i] = 0; + } + } + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_IMPORT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* based on gmp's mpz_import. + * see http://gmplib.org/manual/Integer-Import-and-Export.html + */ +int mp_import(mp_int *rop, size_t count, int order, size_t size, + int endian, size_t nails, const void *op) { + int result; + size_t odd_nails, nail_bytes, i, j; + unsigned char odd_nail_mask; + + mp_zero(rop); + + if (endian == 0) { + union { + unsigned int i; + char c[4]; + } lint; + lint.i = 0x01020304; + + endian = (lint.c[0] == 4) ? -1 : 1; + } + + odd_nails = (nails % 8); + odd_nail_mask = 0xff; + for (i = 0; i < odd_nails; ++i) { + odd_nail_mask ^= (1 << (7 - i)); + } + nail_bytes = nails / 8; + + for (i = 0; i < count; ++i) { + for (j = 0; j < (size - nail_bytes); ++j) { + unsigned char byte = *( + (unsigned char *)op + + (((order == 1) ? i : ((count - 1) - i)) * size) + + ((endian == 1) ? (j + nail_bytes) : (((size - 1) - j) - nail_bytes)) + ); + + if ( + (result = mp_mul_2d(rop, ((j == 0) ? (8 - odd_nails) : 8), rop)) != MP_OKAY) { + return result; + } + + rop->dp[0] |= (j == 0) ? (byte & odd_nail_mask) : byte; + rop->used += 1; + } + } + + mp_clamp(rop); + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_INIT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* init a new mp_int */ +int mp_init(mp_int *a) { + int i; + + /* allocate memory required and clear it */ + a->dp = OPT_CAST(mp_digit) XMALLOC(sizeof(mp_digit) * MP_PREC); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the digits to zero */ + for (i = 0; i < MP_PREC; i++) { + a->dp[i] = 0; + } + + /* set the used to zero, allocated digits to the default precision + * and sign to positive */ + a->used = 0; + a->alloc = MP_PREC; + a->sign = MP_ZPOS; + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_INIT_COPY_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* creates "a" then copies b into it */ +int mp_init_copy(mp_int *a, mp_int *b) { + int res; + + if ((res = mp_init_size(a, b->used)) != MP_OKAY) { + return res; + } + return mp_copy(b, a); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_INIT_MULTI_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ +#include + +int mp_init_multi(mp_int *mp, ...) { + mp_err res = MP_OKAY; /* Assume ok until proven otherwise */ + int n = 0; /* Number of ok inits */ + mp_int *cur_arg = mp; + va_list args; + + va_start(args, mp); /* init args to next argument from caller */ + while (cur_arg != NULL) { + if (mp_init(cur_arg) != MP_OKAY) { + /* Oops - error! Back-track and mp_clear what we already + succeeded in init-ing, then return error. + */ + va_list clean_args; + + /* end the current list */ + va_end(args); + + /* now start cleaning up */ + cur_arg = mp; + va_start(clean_args, mp); + while (n-- != 0) { + mp_clear(cur_arg); + cur_arg = va_arg(clean_args, mp_int *); + } + va_end(clean_args); + res = MP_MEM; + break; + } + n++; + cur_arg = va_arg(args, mp_int *); + } + va_end(args); + return res; /* Assumed ok, if error flagged above. */ +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_INIT_SET_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* initialize and set a digit */ +int mp_init_set(mp_int *a, mp_digit b) { + int err; + + if ((err = mp_init(a)) != MP_OKAY) { + return err; + } + mp_set(a, b); + return err; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_INIT_SET_INT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* initialize and set a digit */ +int mp_init_set_int(mp_int *a, unsigned long b) { + int err; + + if ((err = mp_init(a)) != MP_OKAY) { + return err; + } + return mp_set_int(a, b); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_INIT_SIZE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* init an mp_init for a given size */ +int mp_init_size(mp_int *a, int size) { + int x; + + /* pad size so there are always extra digits */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* alloc mem */ + a->dp = OPT_CAST(mp_digit) XMALLOC(sizeof(mp_digit) * size); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the members */ + a->used = 0; + a->alloc = size; + a->sign = MP_ZPOS; + + /* zero the digits */ + for (x = 0; x < size; x++) { + a->dp[x] = 0; + } + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_INVMOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* hac 14.61, pp608 */ +int mp_invmod(mp_int *a, mp_int *b, mp_int *c) { + /* b cannot be negative */ + if ((b->sign == MP_NEG) || (mp_iszero(b) == MP_YES)) { + return MP_VAL; + } + + #ifdef BN_FAST_MP_INVMOD_C + /* if the modulus is odd we can use a faster routine instead */ + if (mp_isodd(b) == MP_YES) { + return fast_mp_invmod(a, b, c); + } + #endif + + #ifdef BN_MP_INVMOD_SLOW_C + return mp_invmod_slow(a, b, c); + #else + return MP_VAL; + #endif +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_INVMOD_SLOW_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* hac 14.61, pp608 */ +int mp_invmod_slow(mp_int *a, mp_int *b, mp_int *c) { + mp_int x, y, u, v, A, B, C, D; + int res; + + /* b cannot be negative */ + if ((b->sign == MP_NEG) || (mp_iszero(b) == MP_YES)) { + return MP_VAL; + } + + /* init temps */ + if ((res = mp_init_multi(&x, &y, &u, &v, + &A, &B, &C, &D, NULL)) != MP_OKAY) { + return res; + } + + /* x = a, y = b */ + if ((res = mp_mod(a, b, &x)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy(b, &y)) != MP_OKAY) { + goto LBL_ERR; + } + + /* 2. [modified] if x,y are both even then return an error! */ + if ((mp_iseven(&x) == MP_YES) && (mp_iseven(&y) == MP_YES)) { + res = MP_VAL; + goto LBL_ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy(&x, &u)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy(&y, &v)) != MP_OKAY) { + goto LBL_ERR; + } + mp_set(&A, 1); + mp_set(&D, 1); + +top: + /* 4. while u is even do */ + while (mp_iseven(&u) == MP_YES) { + /* 4.1 u = u/2 */ + if ((res = mp_div_2(&u, &u)) != MP_OKAY) { + goto LBL_ERR; + } + /* 4.2 if A or B is odd then */ + if ((mp_isodd(&A) == MP_YES) || (mp_isodd(&B) == MP_YES)) { + /* A = (A+y)/2, B = (B-x)/2 */ + if ((res = mp_add(&A, &y, &A)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_sub(&B, &x, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* A = A/2, B = B/2 */ + if ((res = mp_div_2(&A, &A)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_div_2(&B, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 5. while v is even do */ + while (mp_iseven(&v) == MP_YES) { + /* 5.1 v = v/2 */ + if ((res = mp_div_2(&v, &v)) != MP_OKAY) { + goto LBL_ERR; + } + /* 5.2 if C or D is odd then */ + if ((mp_isodd(&C) == MP_YES) || (mp_isodd(&D) == MP_YES)) { + /* C = (C+y)/2, D = (D-x)/2 */ + if ((res = mp_add(&C, &y, &C)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_sub(&D, &x, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* C = C/2, D = D/2 */ + if ((res = mp_div_2(&C, &C)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_div_2(&D, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 6. if u >= v then */ + if (mp_cmp(&u, &v) != MP_LT) { + /* u = u - v, A = A - C, B = B - D */ + if ((res = mp_sub(&u, &v, &u)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub(&A, &C, &A)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub(&B, &D, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } else { + /* v - v - u, C = C - A, D = D - B */ + if ((res = mp_sub(&v, &u, &v)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub(&C, &A, &C)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub(&D, &B, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* if not zero goto step 4 */ + if (mp_iszero(&u) == MP_NO) + goto top; + + /* now a = C, b = D, gcd == g*v */ + + /* if v != 1 then there is no inverse */ + if (mp_cmp_d(&v, 1) != MP_EQ) { + res = MP_VAL; + goto LBL_ERR; + } + + /* if its too low */ + while (mp_cmp_d(&C, 0) == MP_LT) { + if ((res = mp_add(&C, b, &C)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* too big */ + while (mp_cmp_mag(&C, b) != MP_LT) { + if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* C is now the inverse */ + mp_exch(&C, c); + res = MP_OKAY; +LBL_ERR: mp_clear_multi(&x, &y, &u, &v, &A, &B, &C, &D, NULL); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_IS_SQUARE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* Check if remainders are possible squares - fast exclude non-squares */ +static const char rem_128[128] = { + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1 +}; + +static const char rem_105[105] = { + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 +}; + +/* Store non-zero to ret if arg is square, and zero if not */ +int mp_is_square(mp_int *arg, int *ret) { + int res; + mp_digit c; + mp_int t; + unsigned long r; + + /* Default to Non-square :) */ + *ret = MP_NO; + + if (arg->sign == MP_NEG) { + return MP_VAL; + } + + /* digits used? (TSD) */ + if (arg->used == 0) { + return MP_OKAY; + } + + /* First check mod 128 (suppose that DIGIT_BIT is at least 7) */ + if (rem_128[127 & DIGIT(arg, 0)] == 1) { + return MP_OKAY; + } + + /* Next check mod 105 (3*5*7) */ + if ((res = mp_mod_d(arg, 105, &c)) != MP_OKAY) { + return res; + } + if (rem_105[c] == 1) { + return MP_OKAY; + } + + + if ((res = mp_init_set_int(&t, 11L * 13L * 17L * 19L * 23L * 29L * 31L)) != MP_OKAY) { + return res; + } + if ((res = mp_mod(arg, &t, &t)) != MP_OKAY) { + goto ERR; + } + r = mp_get_int(&t); + + /* Check for other prime modules, note it's not an ERROR but we must + * free "t" so the easiest way is to goto ERR. We know that res + * is already equal to MP_OKAY from the mp_mod call + */ + if (((1L << (r % 11)) & 0x5C4L) != 0L) goto ERR; + if (((1L << (r % 13)) & 0x9E4L) != 0L) goto ERR; + if (((1L << (r % 17)) & 0x5CE8L) != 0L) goto ERR; + if (((1L << (r % 19)) & 0x4F50CL) != 0L) goto ERR; + if (((1L << (r % 23)) & 0x7ACCA0L) != 0L) goto ERR; + if (((1L << (r % 29)) & 0xC2EDD0CL) != 0L) goto ERR; + if (((1L << (r % 31)) & 0x6DE2B848L) != 0L) goto ERR; + + /* Final check - is sqr(sqrt(arg)) == arg ? */ + if ((res = mp_sqrt(arg, &t)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sqr(&t, &t)) != MP_OKAY) { + goto ERR; + } + + *ret = (mp_cmp_mag(&t, arg) == MP_EQ) ? MP_YES : MP_NO; +ERR: mp_clear(&t); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_JACOBI_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* computes the jacobi c = (a | n) (or Legendre if n is prime) + * HAC pp. 73 Algorithm 2.149 + * HAC is wrong here, as the special case of (0 | 1) is not + * handled correctly. + */ +int mp_jacobi(mp_int *a, mp_int *n, int *c) { + mp_int a1, p1; + int k, s, r, res; + mp_digit residue; + + /* if n <= 0 return MP_VAL */ + if (mp_cmp_d(n, 0) != MP_GT) { + return MP_VAL; + } + + /* step 1. handle case of a == 0 */ + if (mp_iszero(a) == MP_YES) { + /* special case of a == 0 and n == 1 */ + if (mp_cmp_d(n, 1) == MP_EQ) { + *c = 1; + } else { + *c = 0; + } + return MP_OKAY; + } + + /* step 2. if a == 1, return 1 */ + if (mp_cmp_d(a, 1) == MP_EQ) { + *c = 1; + return MP_OKAY; + } + + /* default */ + s = 0; + + /* step 3. write a = a1 * 2**k */ + if ((res = mp_init_copy(&a1, a)) != MP_OKAY) { + return res; + } + + if ((res = mp_init(&p1)) != MP_OKAY) { + goto LBL_A1; + } + + /* divide out larger power of two */ + k = mp_cnt_lsb(&a1); + if ((res = mp_div_2d(&a1, k, &a1, NULL)) != MP_OKAY) { + goto LBL_P1; + } + + /* step 4. if e is even set s=1 */ + if ((k & 1) == 0) { + s = 1; + } else { + /* else set s=1 if p = 1/7 (mod 8) or s=-1 if p = 3/5 (mod 8) */ + residue = n->dp[0] & 7; + + if ((residue == 1) || (residue == 7)) { + s = 1; + } else if ((residue == 3) || (residue == 5)) { + s = -1; + } + } + + /* step 5. if p == 3 (mod 4) *and* a1 == 3 (mod 4) then s = -s */ + if (((n->dp[0] & 3) == 3) && ((a1.dp[0] & 3) == 3)) { + s = -s; + } + + /* if a1 == 1 we're done */ + if (mp_cmp_d(&a1, 1) == MP_EQ) { + *c = s; + } else { + /* n1 = n mod a1 */ + if ((res = mp_mod(n, &a1, &p1)) != MP_OKAY) { + goto LBL_P1; + } + if ((res = mp_jacobi(&p1, &a1, &r)) != MP_OKAY) { + goto LBL_P1; + } + *c = s * r; + } + + /* done */ + res = MP_OKAY; +LBL_P1: mp_clear(&p1); +LBL_A1: mp_clear(&a1); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_KARATSUBA_MUL_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* c = |a| * |b| using Karatsuba Multiplication using + * three half size multiplications + * + * Let B represent the radix [e.g. 2**DIGIT_BIT] and + * let n represent half of the number of digits in + * the min(a,b) + * + * a = a1 * B**n + a0 + * b = b1 * B**n + b0 + * + * Then, a * b => + a1b1 * B**2n + ((a1 + a0)(b1 + b0) - (a0b0 + a1b1)) * B + a0b0 + * + * Note that a1b1 and a0b0 are used twice and only need to be + * computed once. So in total three half size (half # of + * digit) multiplications are performed, a0b0, a1b1 and + * (a1+b1)(a0+b0) + * + * Note that a multiplication of half the digits requires + * 1/4th the number of single precision multiplications so in + * total after one call 25% of the single precision multiplications + * are saved. Note also that the call to mp_mul can end up back + * in this function if the a0, a1, b0, or b1 are above the threshold. + * This is known as divide-and-conquer and leads to the famous + * O(N**lg(3)) or O(N**1.584) work which is asymptopically lower than + * the standard O(N**2) that the baseline/comba methods use. + * Generally though the overhead of this method doesn't pay off + * until a certain size (N ~ 80) is reached. + */ +int mp_karatsuba_mul(mp_int *a, mp_int *b, mp_int *c) { + mp_int x0, x1, y0, y1, t1, x0y0, x1y1; + int B, err; + + /* default the return code to an error */ + err = MP_MEM; + + /* min # of digits */ + B = MIN(a->used, b->used); + + /* now divide in two */ + B = B >> 1; + + /* init copy all the temps */ + if (mp_init_size(&x0, B) != MP_OKAY) + goto ERR; + if (mp_init_size(&x1, a->used - B) != MP_OKAY) + goto X0; + if (mp_init_size(&y0, B) != MP_OKAY) + goto X1; + if (mp_init_size(&y1, b->used - B) != MP_OKAY) + goto Y0; + + /* init temps */ + if (mp_init_size(&t1, B * 2) != MP_OKAY) + goto Y1; + if (mp_init_size(&x0y0, B * 2) != MP_OKAY) + goto T1; + if (mp_init_size(&x1y1, B * 2) != MP_OKAY) + goto X0Y0; + + /* now shift the digits */ + x0.used = y0.used = B; + x1.used = a->used - B; + y1.used = b->used - B; + + { + int x; + mp_digit *tmpa, *tmpb, *tmpx, *tmpy; + + /* we copy the digits directly instead of using higher level functions + * since we also need to shift the digits + */ + tmpa = a->dp; + tmpb = b->dp; + + tmpx = x0.dp; + tmpy = y0.dp; + for (x = 0; x < B; x++) { + *tmpx++ = *tmpa++; + *tmpy++ = *tmpb++; + } + + tmpx = x1.dp; + for (x = B; x < a->used; x++) { + *tmpx++ = *tmpa++; + } + + tmpy = y1.dp; + for (x = B; x < b->used; x++) { + *tmpy++ = *tmpb++; + } + } + + /* only need to clamp the lower words since by definition the + * upper words x1/y1 must have a known number of digits + */ + mp_clamp(&x0); + mp_clamp(&y0); + + /* now calc the products x0y0 and x1y1 */ + /* after this x0 is no longer required, free temp [x0==t2]! */ + if (mp_mul(&x0, &y0, &x0y0) != MP_OKAY) + goto X1Y1; /* x0y0 = x0*y0 */ + if (mp_mul(&x1, &y1, &x1y1) != MP_OKAY) + goto X1Y1; /* x1y1 = x1*y1 */ + + /* now calc x1+x0 and y1+y0 */ + if (s_mp_add(&x1, &x0, &t1) != MP_OKAY) + goto X1Y1; /* t1 = x1 - x0 */ + if (s_mp_add(&y1, &y0, &x0) != MP_OKAY) + goto X1Y1; /* t2 = y1 - y0 */ + if (mp_mul(&t1, &x0, &t1) != MP_OKAY) + goto X1Y1; /* t1 = (x1 + x0) * (y1 + y0) */ + + /* add x0y0 */ + if (mp_add(&x0y0, &x1y1, &x0) != MP_OKAY) + goto X1Y1; /* t2 = x0y0 + x1y1 */ + if (s_mp_sub(&t1, &x0, &t1) != MP_OKAY) + goto X1Y1; /* t1 = (x1+x0)*(y1+y0) - (x1y1 + x0y0) */ + + /* shift by B */ + if (mp_lshd(&t1, B) != MP_OKAY) + goto X1Y1; /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<used; + + /* now divide in two */ + B = B >> 1; + + /* init copy all the temps */ + if (mp_init_size(&x0, B) != MP_OKAY) + goto ERR; + if (mp_init_size(&x1, a->used - B) != MP_OKAY) + goto X0; + + /* init temps */ + if (mp_init_size(&t1, a->used * 2) != MP_OKAY) + goto X1; + if (mp_init_size(&t2, a->used * 2) != MP_OKAY) + goto T1; + if (mp_init_size(&x0x0, B * 2) != MP_OKAY) + goto T2; + if (mp_init_size(&x1x1, (a->used - B) * 2) != MP_OKAY) + goto X0X0; + + { + int x; + mp_digit *dst, *src; + + src = a->dp; + + /* now shift the digits */ + dst = x0.dp; + for (x = 0; x < B; x++) { + *dst++ = *src++; + } + + dst = x1.dp; + for (x = B; x < a->used; x++) { + *dst++ = *src++; + } + } + + x0.used = B; + x1.used = a->used - B; + + mp_clamp(&x0); + + /* now calc the products x0*x0 and x1*x1 */ + if (mp_sqr(&x0, &x0x0) != MP_OKAY) + goto X1X1; /* x0x0 = x0*x0 */ + if (mp_sqr(&x1, &x1x1) != MP_OKAY) + goto X1X1; /* x1x1 = x1*x1 */ + + /* now calc (x1+x0)**2 */ + if (s_mp_add(&x1, &x0, &t1) != MP_OKAY) + goto X1X1; /* t1 = x1 - x0 */ + if (mp_sqr(&t1, &t1) != MP_OKAY) + goto X1X1; /* t1 = (x1 - x0) * (x1 - x0) */ + + /* add x0y0 */ + if (s_mp_add(&x0x0, &x1x1, &t2) != MP_OKAY) + goto X1X1; /* t2 = x0x0 + x1x1 */ + if (s_mp_sub(&t1, &t2, &t1) != MP_OKAY) + goto X1X1; /* t1 = (x1+x0)**2 - (x0x0 + x1x1) */ + + /* shift by B */ + if (mp_lshd(&t1, B) != MP_OKAY) + goto X1X1; /* t1 = (x0x0 + x1x1 - (x1-x0)*(x1-x0))<sign = MP_ZPOS; + +LBL_T: + mp_clear_multi(&t1, &t2, NULL); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_LSHD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* shift left a certain amount of digits */ +int mp_lshd(mp_int *a, int b) { + int x, res; + + /* if its less than zero return */ + if (b <= 0) { + return MP_OKAY; + } + + /* grow to fit the new digits */ + if (a->alloc < (a->used + b)) { + if ((res = mp_grow(a, a->used + b)) != MP_OKAY) { + return res; + } + } + + { + mp_digit *top, *bottom; + + /* increment the used by the shift amount then copy upwards */ + a->used += b; + + /* top */ + top = a->dp + a->used - 1; + + /* base */ + bottom = (a->dp + a->used - 1) - b; + + /* much like mp_rshd this is implemented using a sliding window + * except the window goes the otherway around. Copying from + * the bottom to the top. see bn_mp_rshd.c for more info. + */ + for (x = a->used - 1; x >= b; x--) { + *top-- = *bottom--; + } + + /* zero the lower digits */ + top = a->dp; + for (x = 0; x < b; x++) { + *top++ = 0; + } + } + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* c = a mod b, 0 <= c < b if b > 0, b < c <= 0 if b < 0 */ +int +mp_mod(mp_int *a, mp_int *b, mp_int *c) { + mp_int t; + int res; + + if ((res = mp_init(&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_div(a, b, NULL, &t)) != MP_OKAY) { + mp_clear(&t); + return res; + } + + if ((mp_iszero(&t) != MP_NO) || (t.sign == b->sign)) { + res = MP_OKAY; + mp_exch(&t, c); + } else { + res = mp_add(b, &t, c); + } + + mp_clear(&t); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MOD_2D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* calc a value mod 2**b */ +int +mp_mod_2d(mp_int *a, int b, mp_int *c) { + int x, res; + + /* if b is <= 0 then zero the int */ + if (b <= 0) { + mp_zero(c); + return MP_OKAY; + } + + /* if the modulus is larger than the value than return */ + if (b >= (int)(a->used * DIGIT_BIT)) { + res = mp_copy(a, c); + return res; + } + + /* copy */ + if ((res = mp_copy(a, c)) != MP_OKAY) { + return res; + } + + /* zero digits above the last digit of the modulus */ + for (x = (b / DIGIT_BIT) + (((b % DIGIT_BIT) == 0) ? 0 : 1); x < c->used; x++) { + c->dp[x] = 0; + } + /* clear the digit that is not completely outside/inside the modulus */ + c->dp[b / DIGIT_BIT] &= + (mp_digit)((((mp_digit)1) << (((mp_digit)b) % DIGIT_BIT)) - ((mp_digit)1)); + mp_clamp(c); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MOD_D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +int +mp_mod_d(mp_int *a, mp_digit b, mp_digit *c) { + return mp_div_d(a, b, NULL, c); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* + * shifts with subtractions when the result is greater than b. + * + * The method is slightly modified to shift B unconditionally upto just under + * the leading bit of b. This saves alot of multiple precision shifting. + */ +int mp_montgomery_calc_normalization(mp_int *a, mp_int *b) { + int x, bits, res; + + /* how many bits of last digit does b use */ + bits = mp_count_bits(b) % DIGIT_BIT; + + if (b->used > 1) { + if ((res = mp_2expt(a, ((b->used - 1) * DIGIT_BIT) + bits - 1)) != MP_OKAY) { + return res; + } + } else { + mp_set(a, 1); + bits = 1; + } + + + /* now compute C = A * B mod b */ + for (x = bits - 1; x < (int)DIGIT_BIT; x++) { + if ((res = mp_mul_2(a, a)) != MP_OKAY) { + return res; + } + if (mp_cmp_mag(a, b) != MP_LT) { + if ((res = s_mp_sub(a, b, a)) != MP_OKAY) { + return res; + } + } + } + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MONTGOMERY_REDUCE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* computes xR**-1 == x (mod N) via Montgomery Reduction */ +int +mp_montgomery_reduce(mp_int *x, mp_int *n, mp_digit rho) { + int ix, res, digs; + mp_digit mu; + + /* can the fast reduction [comba] method be used? + * + * Note that unlike in mul you're safely allowed *less* + * than the available columns [255 per default] since carries + * are fixed up in the inner loop. + */ + digs = (n->used * 2) + 1; + if ((digs < MP_WARRAY) && + (n->used < + (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { + return fast_mp_montgomery_reduce(x, n, rho); + } + + /* grow the input as required */ + if (x->alloc < digs) { + if ((res = mp_grow(x, digs)) != MP_OKAY) { + return res; + } + } + x->used = digs; + + for (ix = 0; ix < n->used; ix++) { + /* mu = ai * rho mod b + * + * The value of rho must be precalculated via + * montgomery_setup() such that + * it equals -1/n0 mod b this allows the + * following inner loop to reduce the + * input one digit at a time + */ + mu = (mp_digit)(((mp_word)x->dp[ix] * (mp_word)rho) & MP_MASK); + + /* a = a + mu * m * b**i */ + { + int iy; + mp_digit *tmpn, *tmpx, u; + mp_word r; + + /* alias for digits of the modulus */ + tmpn = n->dp; + + /* alias for the digits of x [the input] */ + tmpx = x->dp + ix; + + /* set the carry to zero */ + u = 0; + + /* Multiply and add in place */ + for (iy = 0; iy < n->used; iy++) { + /* compute product and sum */ + r = ((mp_word)mu * (mp_word) * tmpn++) + + (mp_word)u + (mp_word) * tmpx; + + /* get carry */ + u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + + /* fix digit */ + *tmpx++ = (mp_digit)(r & ((mp_word)MP_MASK)); + } + /* At this point the ix'th digit of x should be zero */ + + + /* propagate carries upwards as required*/ + while (u != 0) { + *tmpx += u; + u = *tmpx >> DIGIT_BIT; + *tmpx++ &= MP_MASK; + } + } + } + + /* at this point the n.used'th least + * significant digits of x are all zero + * which means we can shift x to the + * right by n.used digits and the + * residue is unchanged. + */ + + /* x = x/b**n.used */ + mp_clamp(x); + mp_rshd(x, n->used); + + /* if x >= n then x = x - n */ + if (mp_cmp_mag(x, n) != MP_LT) { + return s_mp_sub(x, n, x); + } + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MONTGOMERY_SETUP_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* setups the montgomery reduction stuff */ +int +mp_montgomery_setup(mp_int *n, mp_digit *rho) { + mp_digit x, b; + +/* fast inversion mod 2**k + * + * Based on the fact that + * + * XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n) + * => 2*X*A - X*X*A*A = 1 + * => 2*(1) - (1) = 1 + */ + b = n->dp[0]; + + if ((b & 1) == 0) { + return MP_VAL; + } + + x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */ + x *= 2 - (b * x); /* here x*a==1 mod 2**8 */ + #if !defined(MP_8BIT) + x *= 2 - (b * x); /* here x*a==1 mod 2**16 */ + #endif + #if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT)) + x *= 2 - (b * x); /* here x*a==1 mod 2**32 */ + #endif + #ifdef MP_64BIT + x *= 2 - (b * x); /* here x*a==1 mod 2**64 */ + #endif + + /* rho = -1/m mod b */ + *rho = (mp_digit)(((mp_word)1 << ((mp_word)DIGIT_BIT)) - x) & MP_MASK; + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MUL_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* high level multiplication (handles sign) */ +int mp_mul(mp_int *a, mp_int *b, mp_int *c) { + int res, neg; + + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + + /* use Toom-Cook? */ + #ifdef BN_MP_TOOM_MUL_C + if (MIN(a->used, b->used) >= TOOM_MUL_CUTOFF) { + res = mp_toom_mul(a, b, c); + } else + #endif + #ifdef BN_MP_KARATSUBA_MUL_C + /* use Karatsuba? */ + if (MIN(a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { + res = mp_karatsuba_mul(a, b, c); + } else + #endif + { + /* can we use the fast multiplier? + * + * The fast multiplier can be used if the output will + * have less than MP_WARRAY digits and the number of + * digits won't affect carry propagation + */ + int digs = a->used + b->used + 1; + + #ifdef BN_FAST_S_MP_MUL_DIGS_C + if ((digs < MP_WARRAY) && + (MIN(a->used, b->used) <= + (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { + res = fast_s_mp_mul_digs(a, b, c, digs); + } else + #endif + { + #ifdef BN_S_MP_MUL_DIGS_C + res = s_mp_mul(a, b, c); /* uses s_mp_mul_digs */ + #else + res = MP_VAL; + #endif + } + } + c->sign = (c->used > 0) ? neg : MP_ZPOS; + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MUL_2_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* b = a*2 */ +int mp_mul_2(mp_int *a, mp_int *b) { + int x, res, oldused; + + /* grow to accomodate result */ + if (b->alloc < (a->used + 1)) { + if ((res = mp_grow(b, a->used + 1)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + + { + mp_digit r, rr, *tmpa, *tmpb; + + /* alias for source */ + tmpa = a->dp; + + /* alias for dest */ + tmpb = b->dp; + + /* carry */ + r = 0; + for (x = 0; x < a->used; x++) { + /* get what will be the *next* carry bit from the + * MSB of the current digit + */ + rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); + + /* now shift up this digit, add in the carry [from the previous] */ + *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; + + /* copy the carry that would be from the source + * digit into the next iteration + */ + r = rr; + } + + /* new leading digit? */ + if (r != 0) { + /* add a MSB which is always 1 at this point */ + *tmpb = 1; + ++(b->used); + } + + /* now zero any excess digits on the destination + * that we didn't write to + */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MUL_2D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* shift left by a certain bit count */ +int mp_mul_2d(mp_int *a, int b, mp_int *c) { + mp_digit d; + int res; + + /* copy */ + if (a != c) { + if ((res = mp_copy(a, c)) != MP_OKAY) { + return res; + } + } + + if (c->alloc < (int)(c->used + (b / DIGIT_BIT) + 1)) { + if ((res = mp_grow(c, c->used + (b / DIGIT_BIT) + 1)) != MP_OKAY) { + return res; + } + } + + /* shift by as many digits in the bit count */ + if (b >= (int)DIGIT_BIT) { + if ((res = mp_lshd(c, b / DIGIT_BIT)) != MP_OKAY) { + return res; + } + } + + /* shift any bit count < DIGIT_BIT */ + d = (mp_digit)(b % DIGIT_BIT); + if (d != 0) { + mp_digit *tmpc, shift, mask, r, rr; + int x; + + /* bitmask for carries */ + mask = (((mp_digit)1) << d) - 1; + + /* shift for msbs */ + shift = DIGIT_BIT - d; + + /* alias */ + tmpc = c->dp; + + /* carry */ + r = 0; + for (x = 0; x < c->used; x++) { + /* get the higher bits of the current word */ + rr = (*tmpc >> shift) & mask; + + /* shift the current word and OR in the carry */ + *tmpc = ((*tmpc << d) | r) & MP_MASK; + ++tmpc; + + /* set the carry to the carry bits of the current word */ + r = rr; + } + + /* set final carry */ + if (r != 0) { + c->dp[(c->used)++] = r; + } + } + mp_clamp(c); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MUL_D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* multiply by a digit */ +int +mp_mul_d(mp_int *a, mp_digit b, mp_int *c) { + mp_digit u, *tmpa, *tmpc; + mp_word r; + int ix, res, olduse; + + /* make sure c is big enough to hold a*b */ + if (c->alloc < (a->used + 1)) { + if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* get the original destinations used count */ + olduse = c->used; + + /* set the sign */ + c->sign = a->sign; + + /* alias for a->dp [source] */ + tmpa = a->dp; + + /* alias for c->dp [dest] */ + tmpc = c->dp; + + /* zero carry */ + u = 0; + + /* compute columns */ + for (ix = 0; ix < a->used; ix++) { + /* compute product and carry sum for this term */ + r = (mp_word)u + ((mp_word) * tmpa++ *(mp_word)b); + + /* mask off higher bits to get a single digit */ + *tmpc++ = (mp_digit)(r & ((mp_word)MP_MASK)); + + /* send carry into next iteration */ + u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + } + + /* store final carry [if any] and increment ix offset */ + *tmpc++ = u; + ++ix; + + /* now zero digits above the top */ + while (ix++ < olduse) { + *tmpc++ = 0; + } + + /* set used count */ + c->used = a->used + 1; + mp_clamp(c); + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_MULMOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* d = a * b (mod c) */ +int mp_mulmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d) { + int res; + mp_int t; + + if ((res = mp_init(&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_mul(a, b, &t)) != MP_OKAY) { + mp_clear(&t); + return res; + } + res = mp_mod(&t, c, d); + mp_clear(&t); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_N_ROOT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* wrapper function for mp_n_root_ex() + * computes c = (a)**(1/b) such that (c)**b <= a and (c+1)**b > a + */ +int mp_n_root(mp_int *a, mp_digit b, mp_int *c) { + return mp_n_root_ex(a, b, c, 0); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_N_ROOT_EX_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* find the n'th root of an integer + * + * Result found such that (c)**b <= a and (c+1)**b > a + * + * This algorithm uses Newton's approximation + * x[i+1] = x[i] - f(x[i])/f'(x[i]) + * which will find the root in log(N) time where + * each step involves a fair bit. This is not meant to + * find huge roots [square and cube, etc]. + */ +int mp_n_root_ex(mp_int *a, mp_digit b, mp_int *c, int fast) { + mp_int t1, t2, t3; + int res, neg; + + /* input must be positive if b is even */ + if (((b & 1) == 0) && (a->sign == MP_NEG)) { + return MP_VAL; + } + + if ((res = mp_init(&t1)) != MP_OKAY) { + return res; + } + + if ((res = mp_init(&t2)) != MP_OKAY) { + goto LBL_T1; + } + + if ((res = mp_init(&t3)) != MP_OKAY) { + goto LBL_T2; + } + + /* if a is negative fudge the sign but keep track */ + neg = a->sign; + a->sign = MP_ZPOS; + + /* t2 = 2 */ + mp_set(&t2, 2); + + do { + /* t1 = t2 */ + if ((res = mp_copy(&t2, &t1)) != MP_OKAY) { + goto LBL_T3; + } + + /* t2 = t1 - ((t1**b - a) / (b * t1**(b-1))) */ + + /* t3 = t1**(b-1) */ + if ((res = mp_expt_d_ex(&t1, b - 1, &t3, fast)) != MP_OKAY) { + goto LBL_T3; + } + + /* numerator */ + /* t2 = t1**b */ + if ((res = mp_mul(&t3, &t1, &t2)) != MP_OKAY) { + goto LBL_T3; + } + + /* t2 = t1**b - a */ + if ((res = mp_sub(&t2, a, &t2)) != MP_OKAY) { + goto LBL_T3; + } + + /* denominator */ + /* t3 = t1**(b-1) * b */ + if ((res = mp_mul_d(&t3, b, &t3)) != MP_OKAY) { + goto LBL_T3; + } + + /* t3 = (t1**b - a)/(b * t1**(b-1)) */ + if ((res = mp_div(&t2, &t3, &t3, NULL)) != MP_OKAY) { + goto LBL_T3; + } + + if ((res = mp_sub(&t1, &t3, &t2)) != MP_OKAY) { + goto LBL_T3; + } + } while (mp_cmp(&t1, &t2) != MP_EQ); + + /* result can be off by a few so check */ + for ( ; ; ) { + if ((res = mp_expt_d_ex(&t1, b, &t2, fast)) != MP_OKAY) { + goto LBL_T3; + } + + if (mp_cmp(&t2, a) == MP_GT) { + if ((res = mp_sub_d(&t1, 1, &t1)) != MP_OKAY) { + goto LBL_T3; + } + } else { + break; + } + } + + /* reset the sign of a first */ + a->sign = neg; + + /* set the result */ + mp_exch(&t1, c); + + /* set the sign of the result */ + c->sign = neg; + + res = MP_OKAY; + +LBL_T3: mp_clear(&t3); +LBL_T2: mp_clear(&t2); +LBL_T1: mp_clear(&t1); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_NEG_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* b = -a */ +int mp_neg(mp_int *a, mp_int *b) { + int res; + + if (a != b) { + if ((res = mp_copy(a, b)) != MP_OKAY) { + return res; + } + } + + if (mp_iszero(b) != MP_YES) { + b->sign = (a->sign == MP_ZPOS) ? MP_NEG : MP_ZPOS; + } else { + b->sign = MP_ZPOS; + } + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_OR_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* OR two ints together */ +int mp_or(mp_int *a, mp_int *b, mp_int *c) { + int res, ix, px; + mp_int t, *x; + + if (a->used > b->used) { + if ((res = mp_init_copy(&t, a)) != MP_OKAY) { + return res; + } + px = b->used; + x = b; + } else { + if ((res = mp_init_copy(&t, b)) != MP_OKAY) { + return res; + } + px = a->used; + x = a; + } + + for (ix = 0; ix < px; ix++) { + t.dp[ix] |= x->dp[ix]; + } + mp_clamp(&t); + mp_exch(c, &t); + mp_clear(&t); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_PRIME_FERMAT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* performs one Fermat test. + * + * If "a" were prime then b**a == b (mod a) since the order of + * the multiplicative sub-group would be phi(a) = a-1. That means + * it would be the same as b**(a mod (a-1)) == b**1 == b (mod a). + * + * Sets result to 1 if the congruence holds, or zero otherwise. + */ +int mp_prime_fermat(mp_int *a, mp_int *b, int *result) { + mp_int t; + int err; + + /* default to composite */ + *result = MP_NO; + + /* ensure b > 1 */ + if (mp_cmp_d(b, 1) != MP_GT) { + return MP_VAL; + } + + /* init t */ + if ((err = mp_init(&t)) != MP_OKAY) { + return err; + } + + /* compute t = b**a mod a */ + if ((err = mp_exptmod(b, a, a, &t)) != MP_OKAY) { + goto LBL_T; + } + + /* is it equal to b? */ + if (mp_cmp(&t, b) == MP_EQ) { + *result = MP_YES; + } + + err = MP_OKAY; +LBL_T: mp_clear(&t); + return err; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_PRIME_IS_DIVISIBLE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* determines if an integers is divisible by one + * of the first PRIME_SIZE primes or not + * + * sets result to 0 if not, 1 if yes + */ +int mp_prime_is_divisible(mp_int *a, int *result) { + int err, ix; + mp_digit res; + + /* default to not */ + *result = MP_NO; + + for (ix = 0; ix < PRIME_SIZE; ix++) { + /* what is a mod LBL_prime_tab[ix] */ + if ((err = mp_mod_d(a, ltm_prime_tab[ix], &res)) != MP_OKAY) { + return err; + } + + /* is the residue zero? */ + if (res == 0) { + *result = MP_YES; + return MP_OKAY; + } + } + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_PRIME_IS_PRIME_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* performs a variable number of rounds of Miller-Rabin + * + * Probability of error after t rounds is no more than + + * + * Sets result to 1 if probably prime, 0 otherwise + */ +int mp_prime_is_prime(mp_int *a, int t, int *result) { + mp_int b; + int ix, err, res; + + /* default to no */ + *result = MP_NO; + + /* valid value of t? */ + if ((t <= 0) || (t > PRIME_SIZE)) { + return MP_VAL; + } + + /* is the input equal to one of the primes in the table? */ + for (ix = 0; ix < PRIME_SIZE; ix++) { + if (mp_cmp_d(a, ltm_prime_tab[ix]) == MP_EQ) { + *result = 1; + return MP_OKAY; + } + } + + /* first perform trial division */ + if ((err = mp_prime_is_divisible(a, &res)) != MP_OKAY) { + return err; + } + + /* return if it was trivially divisible */ + if (res == MP_YES) { + return MP_OKAY; + } + + /* now perform the miller-rabin rounds */ + if ((err = mp_init(&b)) != MP_OKAY) { + return err; + } + + for (ix = 0; ix < t; ix++) { + /* set the prime */ + mp_set(&b, ltm_prime_tab[ix]); + + if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) { + goto LBL_B; + } + + if (res == MP_NO) { + goto LBL_B; + } + } + + /* passed the test */ + *result = MP_YES; +LBL_B: mp_clear(&b); + return err; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_PRIME_MILLER_RABIN_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* Miller-Rabin test of "a" to the base of "b" as described in + * HAC pp. 139 Algorithm 4.24 + * + * Sets result to 0 if definitely composite or 1 if probably prime. + * Randomly the chance of error is no more than 1/4 and often + * very much lower. + */ +int mp_prime_miller_rabin(mp_int *a, mp_int *b, int *result) { + mp_int n1, y, r; + int s, j, err; + + /* default */ + *result = MP_NO; + + /* ensure b > 1 */ + if (mp_cmp_d(b, 1) != MP_GT) { + return MP_VAL; + } + + /* get n1 = a - 1 */ + if ((err = mp_init_copy(&n1, a)) != MP_OKAY) { + return err; + } + if ((err = mp_sub_d(&n1, 1, &n1)) != MP_OKAY) { + goto LBL_N1; + } + + /* set 2**s * r = n1 */ + if ((err = mp_init_copy(&r, &n1)) != MP_OKAY) { + goto LBL_N1; + } + + /* count the number of least significant bits + * which are zero + */ + s = mp_cnt_lsb(&r); + + /* now divide n - 1 by 2**s */ + if ((err = mp_div_2d(&r, s, &r, NULL)) != MP_OKAY) { + goto LBL_R; + } + + /* compute y = b**r mod a */ + if ((err = mp_init(&y)) != MP_OKAY) { + goto LBL_R; + } + if ((err = mp_exptmod(b, &r, a, &y)) != MP_OKAY) { + goto LBL_Y; + } + + /* if y != 1 and y != n1 do */ + if ((mp_cmp_d(&y, 1) != MP_EQ) && (mp_cmp(&y, &n1) != MP_EQ)) { + j = 1; + /* while j <= s-1 and y != n1 */ + while ((j <= (s - 1)) && (mp_cmp(&y, &n1) != MP_EQ)) { + if ((err = mp_sqrmod(&y, a, &y)) != MP_OKAY) { + goto LBL_Y; + } + + /* if y == 1 then composite */ + if (mp_cmp_d(&y, 1) == MP_EQ) { + goto LBL_Y; + } + + ++j; + } + + /* if y != n1 then composite */ + if (mp_cmp(&y, &n1) != MP_EQ) { + goto LBL_Y; + } + } + + /* probably prime now */ + *result = MP_YES; +LBL_Y: mp_clear(&y); +LBL_R: mp_clear(&r); +LBL_N1: mp_clear(&n1); + return err; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_PRIME_NEXT_PRIME_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* finds the next prime after the number "a" using "t" trials + * of Miller-Rabin. + * + * bbs_style = 1 means the prime must be congruent to 3 mod 4 + */ +int mp_prime_next_prime(mp_int *a, int t, int bbs_style) { + int err, res = MP_NO, x, y; + mp_digit res_tab[PRIME_SIZE], step, kstep; + mp_int b; + + /* ensure t is valid */ + if ((t <= 0) || (t > PRIME_SIZE)) { + return MP_VAL; + } + + /* force positive */ + a->sign = MP_ZPOS; + + /* simple algo if a is less than the largest prime in the table */ + if (mp_cmp_d(a, ltm_prime_tab[PRIME_SIZE - 1]) == MP_LT) { + /* find which prime it is bigger than */ + for (x = PRIME_SIZE - 2; x >= 0; x--) { + if (mp_cmp_d(a, ltm_prime_tab[x]) != MP_LT) { + if (bbs_style == 1) { + /* ok we found a prime smaller or + * equal [so the next is larger] + * + * however, the prime must be + * congruent to 3 mod 4 + */ + if ((ltm_prime_tab[x + 1] & 3) != 3) { + /* scan upwards for a prime congruent to 3 mod 4 */ + for (y = x + 1; y < PRIME_SIZE; y++) { + if ((ltm_prime_tab[y] & 3) == 3) { + mp_set(a, ltm_prime_tab[y]); + return MP_OKAY; + } + } + } + } else { + mp_set(a, ltm_prime_tab[x + 1]); + return MP_OKAY; + } + } + } + /* at this point a maybe 1 */ + if (mp_cmp_d(a, 1) == MP_EQ) { + mp_set(a, 2); + return MP_OKAY; + } + /* fall through to the sieve */ + } + + /* generate a prime congruent to 3 mod 4 or 1/3 mod 4? */ + if (bbs_style == 1) { + kstep = 4; + } else { + kstep = 2; + } + + /* at this point we will use a combination of a sieve and Miller-Rabin */ + + if (bbs_style == 1) { + /* if a mod 4 != 3 subtract the correct value to make it so */ + if ((a->dp[0] & 3) != 3) { + if ((err = mp_sub_d(a, (a->dp[0] & 3) + 1, a)) != MP_OKAY) { + return err; + } + } + } else { + if (mp_iseven(a) == MP_YES) { + /* force odd */ + if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) { + return err; + } + } + } + + /* generate the restable */ + for (x = 1; x < PRIME_SIZE; x++) { + if ((err = mp_mod_d(a, ltm_prime_tab[x], res_tab + x)) != MP_OKAY) { + return err; + } + } + + /* init temp used for Miller-Rabin Testing */ + if ((err = mp_init(&b)) != MP_OKAY) { + return err; + } + + for ( ; ; ) { + /* skip to the next non-trivially divisible candidate */ + step = 0; + do { + /* y == 1 if any residue was zero [e.g. cannot be prime] */ + y = 0; + + /* increase step to next candidate */ + step += kstep; + + /* compute the new residue without using division */ + for (x = 1; x < PRIME_SIZE; x++) { + /* add the step to each residue */ + res_tab[x] += kstep; + + /* subtract the modulus [instead of using division] */ + if (res_tab[x] >= ltm_prime_tab[x]) { + res_tab[x] -= ltm_prime_tab[x]; + } + + /* set flag if zero */ + if (res_tab[x] == 0) { + y = 1; + } + } + } while ((y == 1) && (step < ((((mp_digit)1) << DIGIT_BIT) - kstep))); + + /* add the step */ + if ((err = mp_add_d(a, step, a)) != MP_OKAY) { + goto LBL_ERR; + } + + /* if didn't pass sieve and step == MAX then skip test */ + if ((y == 1) && (step >= ((((mp_digit)1) << DIGIT_BIT) - kstep))) { + continue; + } + + /* is this prime? */ + for (x = 0; x < t; x++) { + mp_set(&b, ltm_prime_tab[x]); + if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) { + goto LBL_ERR; + } + if (res == MP_NO) { + break; + } + } + + if (res == MP_YES) { + break; + } + } + + err = MP_OKAY; +LBL_ERR: + mp_clear(&b); + return err; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_PRIME_RABIN_MILLER_TRIALS_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + + +static const struct { + int k, t; +} sizes[] = { + { 128, 28 }, + { 256, 16 }, + { 384, 10 }, + { 512, 7 }, + { 640, 6 }, + { 768, 5 }, + { 896, 4 }, + { 1024, 4 } +}; + +/* returns # of RM trials required for a given bit size */ +int mp_prime_rabin_miller_trials(int size) { + int x; + + for (x = 0; x < (int)(sizeof(sizes) / (sizeof(sizes[0]))); x++) { + if (sizes[x].k == size) { + return sizes[x].t; + } else if (sizes[x].k > size) { + return (x == 0) ? sizes[0].t : sizes[x - 1].t; + } + } + return sizes[x - 1].t + 1; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_PRIME_RANDOM_EX_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* makes a truly random prime of a given size (bits), + * + * Flags are as follows: + * + * LTM_PRIME_BBS - make prime congruent to 3 mod 4 + * LTM_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS) + * LTM_PRIME_2MSB_ON - make the 2nd highest bit one + * + * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can + * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself + * so it can be NULL + * + */ + +/* This is possibly the mother of all prime generation functions, muahahahahaha! */ +int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat) { + unsigned char *tmp, maskAND, maskOR_msb, maskOR_lsb; + int res, err, bsize, maskOR_msb_offset; + + /* sanity check the input */ + if ((size <= 1) || (t <= 0)) { + return MP_VAL; + } + + /* LTM_PRIME_SAFE implies LTM_PRIME_BBS */ + if ((flags & LTM_PRIME_SAFE) != 0) { + flags |= LTM_PRIME_BBS; + } + + /* calc the byte size */ + bsize = (size >> 3) + ((size & 7) ? 1 : 0); + + /* we need a buffer of bsize bytes */ + tmp = OPT_CAST(unsigned char) XMALLOC(bsize); + if (tmp == NULL) { + return MP_MEM; + } + + /* calc the maskAND value for the MSbyte*/ + maskAND = ((size & 7) == 0) ? 0xFF : (0xFF >> (8 - (size & 7))); + + /* calc the maskOR_msb */ + maskOR_msb = 0; + maskOR_msb_offset = ((size & 7) == 1) ? 1 : 0; + if ((flags & LTM_PRIME_2MSB_ON) != 0) { + maskOR_msb |= 0x80 >> ((9 - size) & 7); + } + + /* get the maskOR_lsb */ + maskOR_lsb = 1; + if ((flags & LTM_PRIME_BBS) != 0) { + maskOR_lsb |= 3; + } + + do { + /* read the bytes */ + if (cb(tmp, bsize, dat) != bsize) { + err = MP_VAL; + goto error; + } + + /* work over the MSbyte */ + tmp[0] &= maskAND; + tmp[0] |= 1 << ((size - 1) & 7); + + /* mix in the maskORs */ + tmp[maskOR_msb_offset] |= maskOR_msb; + tmp[bsize - 1] |= maskOR_lsb; + + /* read it in */ + if ((err = mp_read_unsigned_bin(a, tmp, bsize)) != MP_OKAY) { + goto error; + } + + /* is it prime? */ + if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { + goto error; + } + if (res == MP_NO) { + continue; + } + + if ((flags & LTM_PRIME_SAFE) != 0) { + /* see if (a-1)/2 is prime */ + if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) { + goto error; + } + if ((err = mp_div_2(a, a)) != MP_OKAY) { + goto error; + } + + /* is it prime? */ + if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { + goto error; + } + } + } while (res == MP_NO); + + if ((flags & LTM_PRIME_SAFE) != 0) { + /* restore a to the original value */ + if ((err = mp_mul_2(a, a)) != MP_OKAY) { + goto error; + } + if ((err = mp_add_d(a, 1, a)) != MP_OKAY) { + goto error; + } + } + + err = MP_OKAY; +error: + XFREE(tmp); + return err; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_RADIX_SIZE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* returns size of ASCII reprensentation */ +int mp_radix_size(mp_int *a, int radix, int *size) { + int res, digs; + mp_int t; + mp_digit d; + + *size = 0; + + /* make sure the radix is in range */ + if ((radix < 2) || (radix > 64)) { + return MP_VAL; + } + + if (mp_iszero(a) == MP_YES) { + *size = 2; + return MP_OKAY; + } + + /* special case for binary */ + if (radix == 2) { + *size = mp_count_bits(a) + ((a->sign == MP_NEG) ? 1 : 0) + 1; + return MP_OKAY; + } + + /* digs is the digit count */ + digs = 0; + + /* if it's negative add one for the sign */ + if (a->sign == MP_NEG) { + ++digs; + } + + /* init a copy of the input */ + if ((res = mp_init_copy(&t, a)) != MP_OKAY) { + return res; + } + + /* force temp to positive */ + t.sign = MP_ZPOS; + + /* fetch out all of the digits */ + while (mp_iszero(&t) == MP_NO) { + if ((res = mp_div_d(&t, (mp_digit)radix, &t, &d)) != MP_OKAY) { + mp_clear(&t); + return res; + } + ++digs; + } + mp_clear(&t); + + /* return digs + 1, the 1 is for the NULL byte that would be required. */ + *size = digs + 1; + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_RADIX_SMAP_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* chars used in radix conversions */ +const char *mp_s_rmap = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_RAND_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* makes a pseudo-random int of a given size */ +int +mp_rand(mp_int *a, int digits) { + int res; + mp_digit d; + + mp_zero(a); + if (digits <= 0) { + return MP_OKAY; + } + + /* first place a random non-zero digit */ + do { + d = ((mp_digit)abs(MP_GEN_RANDOM())) & MP_MASK; + } while (d == 0); + + if ((res = mp_add_d(a, d, a)) != MP_OKAY) { + return res; + } + + while (--digits > 0) { + if ((res = mp_lshd(a, 1)) != MP_OKAY) { + return res; + } + + if ((res = mp_add_d(a, ((mp_digit)abs(MP_GEN_RANDOM())), a)) != MP_OKAY) { + return res; + } + } + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_READ_RADIX_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* read a string [ASCII] in a given radix */ +int mp_read_radix(mp_int *a, const char *str, int radix) { + int y, res, neg; + char ch; + + /* zero the digit bignum */ + mp_zero(a); + + /* make sure the radix is ok */ + if ((radix < 2) || (radix > 64)) { + return MP_VAL; + } + + /* if the leading digit is a + * minus set the sign to negative. + */ + if (*str == '-') { + ++str; + neg = MP_NEG; + } else { + neg = MP_ZPOS; + } + + /* set the integer to the default of zero */ + mp_zero(a); + + /* process each digit of the string */ + while (*str != '\0') { + /* if the radix <= 36 the conversion is case insensitive + * this allows numbers like 1AB and 1ab to represent the same value + * [e.g. in hex] + */ + ch = (radix <= 36) ? (char)toupper((int)*str) : *str; + for (y = 0; y < 64; y++) { + if (ch == mp_s_rmap[y]) { + break; + } + } + + /* if the char was found in the map + * and is less than the given radix add it + * to the number, otherwise exit the loop. + */ + if (y < radix) { + if ((res = mp_mul_d(a, (mp_digit)radix, a)) != MP_OKAY) { + return res; + } + if ((res = mp_add_d(a, (mp_digit)y, a)) != MP_OKAY) { + return res; + } + } else { + break; + } + ++str; + } + + /* set the sign only if a != 0 */ + if (mp_iszero(a) != MP_YES) { + a->sign = neg; + } + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_READ_SIGNED_BIN_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* read signed bin, big endian, first byte is 0==positive or 1==negative */ +int mp_read_signed_bin(mp_int *a, const unsigned char *b, int c) { + int res; + + /* read magnitude */ + if ((res = mp_read_unsigned_bin(a, b + 1, c - 1)) != MP_OKAY) { + return res; + } + + /* first byte is 0 for positive, non-zero for negative */ + if (b[0] == 0) { + a->sign = MP_ZPOS; + } else { + a->sign = MP_NEG; + } + + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_READ_UNSIGNED_BIN_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* reads a unsigned char array, assumes the msb is stored first [big endian] */ +int mp_read_unsigned_bin(mp_int *a, const unsigned char *b, int c) { + int res; + + /* make sure there are at least two digits */ + if (a->alloc < 2) { + if ((res = mp_grow(a, 2)) != MP_OKAY) { + return res; + } + } + + /* zero the int */ + mp_zero(a); + + /* read the bytes in */ + while (c-- > 0) { + if ((res = mp_mul_2d(a, 8, a)) != MP_OKAY) { + return res; + } + + #ifndef MP_8BIT + a->dp[0] |= *b++; + a->used += 1; + #else + a->dp[0] = (*b & MP_MASK); + a->dp[1] |= ((*b++ >> 7U) & 1); + a->used += 2; + #endif + } + mp_clamp(a); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_REDUCE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* reduces x mod m, assumes 0 < x < m**2, mu is + * precomputed via mp_reduce_setup. + * From HAC pp.604 Algorithm 14.42 + */ +int mp_reduce(mp_int *x, mp_int *m, mp_int *mu) { + mp_int q; + int res, um = m->used; + + /* q = x */ + if ((res = mp_init_copy(&q, x)) != MP_OKAY) { + return res; + } + + /* q1 = x / b**(k-1) */ + mp_rshd(&q, um - 1); + + /* according to HAC this optimization is ok */ + if (((mp_digit)um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { + if ((res = mp_mul(&q, mu, &q)) != MP_OKAY) { + goto CLEANUP; + } + } else { + #ifdef BN_S_MP_MUL_HIGH_DIGS_C + if ((res = s_mp_mul_high_digs(&q, mu, &q, um)) != MP_OKAY) { + goto CLEANUP; + } + #elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) + if ((res = fast_s_mp_mul_high_digs(&q, mu, &q, um)) != MP_OKAY) { + goto CLEANUP; + } + #else + { + res = MP_VAL; + goto CLEANUP; + } + #endif + } + + /* q3 = q2 / b**(k+1) */ + mp_rshd(&q, um + 1); + + /* x = x mod b**(k+1), quick (no division) */ + if ((res = mp_mod_2d(x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { + goto CLEANUP; + } + + /* q = q * m mod b**(k+1), quick (no division) */ + if ((res = s_mp_mul_digs(&q, m, &q, um + 1)) != MP_OKAY) { + goto CLEANUP; + } + + /* x = x - q */ + if ((res = mp_sub(x, &q, x)) != MP_OKAY) { + goto CLEANUP; + } + + /* If x < 0, add b**(k+1) to it */ + if (mp_cmp_d(x, 0) == MP_LT) { + mp_set(&q, 1); + if ((res = mp_lshd(&q, um + 1)) != MP_OKAY) + goto CLEANUP; + if ((res = mp_add(x, &q, x)) != MP_OKAY) + goto CLEANUP; + } + + /* Back off if it's too big */ + while (mp_cmp(x, m) != MP_LT) { + if ((res = s_mp_sub(x, m, x)) != MP_OKAY) { + goto CLEANUP; + } + } + +CLEANUP: + mp_clear(&q); + + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_REDUCE_2K_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* reduces a modulo n where n is of the form 2**p - d */ +int mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d) { + mp_int q; + int p, res; + + if ((res = mp_init(&q)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(n); +top: + /* q = a/2**p, a = a mod 2**p */ + if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (d != 1) { + /* q = q * d */ + if ((res = mp_mul_d(&q, d, &q)) != MP_OKAY) { + goto ERR; + } + } + + /* a = a + q */ + if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (mp_cmp_mag(a, n) != MP_LT) { + if ((res = s_mp_sub(a, n, a)) != MP_OKAY) { + goto ERR; + } + goto top; + } + +ERR: + mp_clear(&q); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_REDUCE_2K_L_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* reduces a modulo n where n is of the form 2**p - d + This differs from reduce_2k since "d" can be larger + than a single digit. + */ +int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d) { + mp_int q; + int p, res; + + if ((res = mp_init(&q)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(n); +top: + /* q = a/2**p, a = a mod 2**p */ + if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { + goto ERR; + } + + /* q = q * d */ + if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { + goto ERR; + } + + /* a = a + q */ + if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (mp_cmp_mag(a, n) != MP_LT) { + if ((res = s_mp_sub(a, n, a)) != MP_OKAY) { + goto ERR; + } + goto top; + } + +ERR: + mp_clear(&q); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_REDUCE_2K_SETUP_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* determines the setup value */ +int mp_reduce_2k_setup(mp_int *a, mp_digit *d) { + int res, p; + mp_int tmp; + + if ((res = mp_init(&tmp)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(a); + if ((res = mp_2expt(&tmp, p)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + if ((res = s_mp_sub(&tmp, a, &tmp)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + *d = tmp.dp[0]; + mp_clear(&tmp); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_REDUCE_2K_SETUP_L_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* determines the setup value */ +int mp_reduce_2k_setup_l(mp_int *a, mp_int *d) { + int res; + mp_int tmp; + + if ((res = mp_init(&tmp)) != MP_OKAY) { + return res; + } + + if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { + goto ERR; + } + + if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear(&tmp); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_REDUCE_IS_2K_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* determines if mp_reduce_2k can be used */ +int mp_reduce_is_2k(mp_int *a) { + int ix, iy, iw; + mp_digit iz; + + if (a->used == 0) { + return MP_NO; + } else if (a->used == 1) { + return MP_YES; + } else if (a->used > 1) { + iy = mp_count_bits(a); + iz = 1; + iw = 1; + + /* Test every bit from the second digit up, must be 1 */ + for (ix = DIGIT_BIT; ix < iy; ix++) { + if ((a->dp[iw] & iz) == 0) { + return MP_NO; + } + iz <<= 1; + if (iz > (mp_digit)MP_MASK) { + ++iw; + iz = 1; + } + } + } + return MP_YES; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_REDUCE_IS_2K_L_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* determines if reduce_2k_l can be used */ +int mp_reduce_is_2k_l(mp_int *a) { + int ix, iy; + + if (a->used == 0) { + return MP_NO; + } else if (a->used == 1) { + return MP_YES; + } else if (a->used > 1) { + /* if more than half of the digits are -1 we're sold */ + for (iy = ix = 0; ix < a->used; ix++) { + if (a->dp[ix] == MP_MASK) { + ++iy; + } + } + return (iy >= (a->used / 2)) ? MP_YES : MP_NO; + } + return MP_NO; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_REDUCE_SETUP_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* pre-calculate the value required for Barrett reduction + * For a given modulus "b" it calulates the value required in "a" + */ +int mp_reduce_setup(mp_int *a, mp_int *b) { + int res; + + if ((res = mp_2expt(a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { + return res; + } + return mp_div(a, b, a, NULL); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_RSHD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* shift right a certain amount of digits */ +void mp_rshd(mp_int *a, int b) { + int x; + + /* if b <= 0 then ignore it */ + if (b <= 0) { + return; + } + + /* if b > used then simply zero it and return */ + if (a->used <= b) { + mp_zero(a); + return; + } + + { + mp_digit *bottom, *top; + + /* shift the digits down */ + + /* bottom */ + bottom = a->dp; + + /* top [offset into digits] */ + top = a->dp + b; + + /* this is implemented as a sliding window where + * the window is b-digits long and digits from + * the top of the window are copied to the bottom + * + * e.g. + + b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> + /\ | ----> + **\-------------------/ ----> + */ + for (x = 0; x < (a->used - b); x++) { + *bottom++ = *top++; + } + + /* zero the top digits */ + for ( ; x < a->used; x++) { + *bottom++ = 0; + } + } + + /* remove excess digits */ + a->used -= b; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SET_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* set to a digit */ +void mp_set(mp_int *a, mp_digit b) { + mp_zero(a); + a->dp[0] = b & MP_MASK; + a->used = (a->dp[0] != 0) ? 1 : 0; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SET_INT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* set a 32-bit const */ +int mp_set_int(mp_int *a, unsigned long b) { + int x, res; + + mp_zero(a); + + /* set four bits at a time */ + for (x = 0; x < 8; x++) { + /* shift the number up four bits */ + if ((res = mp_mul_2d(a, 4, a)) != MP_OKAY) { + return res; + } + + /* OR in the top four bits of the source */ + a->dp[0] |= (b >> 28) & 15; + + /* shift the source up to the next four bits */ + b <<= 4; + + /* ensure that digits are not clamped off */ + a->used += 1; + } + mp_clamp(a); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SET_LONG_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* set a platform dependent unsigned long int */ +MP_SET_XLONG(mp_set_long, unsigned long) +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SET_LONG_LONG_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* set a platform dependent unsigned long long int */ +MP_SET_XLONG(mp_set_long_long, unsigned long long) +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SHRINK_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* shrink a bignum */ +int mp_shrink(mp_int *a) { + mp_digit *tmp; + int used = 1; + + if (a->used > 0) { + used = a->used; + } + + if (a->alloc != used) { + if ((tmp = OPT_CAST(mp_digit) XREALLOC(a->dp, sizeof(mp_digit) * used)) == NULL) { + return MP_MEM; + } + a->dp = tmp; + a->alloc = used; + } + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SIGNED_BIN_SIZE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* get the size for an signed equivalent */ +int mp_signed_bin_size(mp_int *a) { + return 1 + mp_unsigned_bin_size(a); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SQR_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* computes b = a*a */ +int +mp_sqr(mp_int *a, mp_int *b) { + int res; + + #ifdef BN_MP_TOOM_SQR_C + /* use Toom-Cook? */ + if (a->used >= TOOM_SQR_CUTOFF) { + res = mp_toom_sqr(a, b); + /* Karatsuba? */ + } else + #endif + #ifdef BN_MP_KARATSUBA_SQR_C + if (a->used >= KARATSUBA_SQR_CUTOFF) { + res = mp_karatsuba_sqr(a, b); + } else + #endif + { + #ifdef BN_FAST_S_MP_SQR_C + /* can we use the fast comba multiplier? */ + if ((((a->used * 2) + 1) < MP_WARRAY) && + (a->used < + (1 << (((sizeof(mp_word) * CHAR_BIT) - (2 * DIGIT_BIT)) - 1)))) { + res = fast_s_mp_sqr(a, b); + } else + #endif + { + #ifdef BN_S_MP_SQR_C + res = s_mp_sqr(a, b); + #else + res = MP_VAL; + #endif + } + } + b->sign = MP_ZPOS; + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SQRMOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* c = a * a (mod b) */ +int +mp_sqrmod(mp_int *a, mp_int *b, mp_int *c) { + int res; + mp_int t; + + if ((res = mp_init(&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_sqr(a, &t)) != MP_OKAY) { + mp_clear(&t); + return res; + } + res = mp_mod(&t, b, c); + mp_clear(&t); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SQRT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* this function is less generic than mp_n_root, simpler and faster */ +int mp_sqrt(mp_int *arg, mp_int *ret) { + int res; + mp_int t1, t2; + + /* must be positive */ + if (arg->sign == MP_NEG) { + return MP_VAL; + } + + /* easy out */ + if (mp_iszero(arg) == MP_YES) { + mp_zero(ret); + return MP_OKAY; + } + + if ((res = mp_init_copy(&t1, arg)) != MP_OKAY) { + return res; + } + + if ((res = mp_init(&t2)) != MP_OKAY) { + goto E2; + } + + /* First approx. (not very bad for large arg) */ + mp_rshd(&t1, t1.used / 2); + + /* t1 > 0 */ + if ((res = mp_div(arg, &t1, &t2, NULL)) != MP_OKAY) { + goto E1; + } + if ((res = mp_add(&t1, &t2, &t1)) != MP_OKAY) { + goto E1; + } + if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) { + goto E1; + } + /* And now t1 > sqrt(arg) */ + do { + if ((res = mp_div(arg, &t1, &t2, NULL)) != MP_OKAY) { + goto E1; + } + if ((res = mp_add(&t1, &t2, &t1)) != MP_OKAY) { + goto E1; + } + if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) { + goto E1; + } + /* t1 >= sqrt(arg) >= t2 at this point */ + } while (mp_cmp_mag(&t1, &t2) == MP_GT); + + mp_exch(&t1, ret); + +E1: mp_clear(&t2); +E2: mp_clear(&t1); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SQRTMOD_PRIME_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is free for all purposes without any express + * guarantee it works. + */ + +/* Tonelli-Shanks algorithm + * https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm + * https://gmplib.org/list-archives/gmp-discuss/2013-April/005300.html + * + */ + +int mp_sqrtmod_prime(mp_int *n, mp_int *prime, mp_int *ret) { + int res, legendre; + mp_int t1, C, Q, S, Z, M, T, R, two; + mp_digit i; + + /* first handle the simple cases */ + if (mp_cmp_d(n, 0) == MP_EQ) { + mp_zero(ret); + return MP_OKAY; + } + if (mp_cmp_d(prime, 2) == MP_EQ) return MP_VAL; /* prime must be odd */ + if ((res = mp_jacobi(n, prime, &legendre)) != MP_OKAY) return res; + if (legendre == -1) return MP_VAL; /* quadratic non-residue mod prime */ + + if ((res = mp_init_multi(&t1, &C, &Q, &S, &Z, &M, &T, &R, &two, NULL)) != MP_OKAY) { + return res; + } + + /* SPECIAL CASE: if prime mod 4 == 3 + * compute directly: res = n^(prime+1)/4 mod prime + * Handbook of Applied Cryptography algorithm 3.36 + */ + if ((res = mp_mod_d(prime, 4, &i)) != MP_OKAY) goto cleanup; + if (i == 3) { + if ((res = mp_add_d(prime, 1, &t1)) != MP_OKAY) goto cleanup; + if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) goto cleanup; + if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) goto cleanup; + if ((res = mp_exptmod(n, &t1, prime, ret)) != MP_OKAY) goto cleanup; + res = MP_OKAY; + goto cleanup; + } + + /* NOW: Tonelli-Shanks algorithm */ + + /* factor out powers of 2 from prime-1, defining Q and S as: prime-1 = Q*2^S */ + if ((res = mp_copy(prime, &Q)) != MP_OKAY) goto cleanup; + if ((res = mp_sub_d(&Q, 1, &Q)) != MP_OKAY) goto cleanup; + /* Q = prime - 1 */ + mp_zero(&S); + /* S = 0 */ + while (mp_iseven(&Q) != MP_NO) { + if ((res = mp_div_2(&Q, &Q)) != MP_OKAY) goto cleanup; + /* Q = Q / 2 */ + if ((res = mp_add_d(&S, 1, &S)) != MP_OKAY) goto cleanup; + /* S = S + 1 */ + } + + /* find a Z such that the Legendre symbol (Z|prime) == -1 */ + if ((res = mp_set_int(&Z, 2)) != MP_OKAY) goto cleanup; + /* Z = 2 */ + while (1) { + if ((res = mp_jacobi(&Z, prime, &legendre)) != MP_OKAY) goto cleanup; + if (legendre == -1) break; + if ((res = mp_add_d(&Z, 1, &Z)) != MP_OKAY) goto cleanup; + /* Z = Z + 1 */ + } + + if ((res = mp_exptmod(&Z, &Q, prime, &C)) != MP_OKAY) goto cleanup; + /* C = Z ^ Q mod prime */ + if ((res = mp_add_d(&Q, 1, &t1)) != MP_OKAY) goto cleanup; + if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) goto cleanup; + /* t1 = (Q + 1) / 2 */ + if ((res = mp_exptmod(n, &t1, prime, &R)) != MP_OKAY) goto cleanup; + /* R = n ^ ((Q + 1) / 2) mod prime */ + if ((res = mp_exptmod(n, &Q, prime, &T)) != MP_OKAY) goto cleanup; + /* T = n ^ Q mod prime */ + if ((res = mp_copy(&S, &M)) != MP_OKAY) goto cleanup; + /* M = S */ + if ((res = mp_set_int(&two, 2)) != MP_OKAY) goto cleanup; + + res = MP_VAL; + while (1) { + if ((res = mp_copy(&T, &t1)) != MP_OKAY) goto cleanup; + i = 0; + while (1) { + if (mp_cmp_d(&t1, 1) == MP_EQ) break; + if ((res = mp_exptmod(&t1, &two, prime, &t1)) != MP_OKAY) goto cleanup; + i++; + } + if (i == 0) { + if ((res = mp_copy(&R, ret)) != MP_OKAY) goto cleanup; + res = MP_OKAY; + goto cleanup; + } + if ((res = mp_sub_d(&M, i, &t1)) != MP_OKAY) goto cleanup; + if ((res = mp_sub_d(&t1, 1, &t1)) != MP_OKAY) goto cleanup; + if ((res = mp_exptmod(&two, &t1, prime, &t1)) != MP_OKAY) goto cleanup; + /* t1 = 2 ^ (M - i - 1) */ + if ((res = mp_exptmod(&C, &t1, prime, &t1)) != MP_OKAY) goto cleanup; + /* t1 = C ^ (2 ^ (M - i - 1)) mod prime */ + if ((res = mp_sqrmod(&t1, prime, &C)) != MP_OKAY) goto cleanup; + /* C = (t1 * t1) mod prime */ + if ((res = mp_mulmod(&R, &t1, prime, &R)) != MP_OKAY) goto cleanup; + /* R = (R * t1) mod prime */ + if ((res = mp_mulmod(&T, &C, prime, &T)) != MP_OKAY) goto cleanup; + /* T = (T * C) mod prime */ + mp_set(&M, i); + /* M = i */ + } + +cleanup: + mp_clear_multi(&t1, &C, &Q, &S, &Z, &M, &T, &R, &two, NULL); + return res; +} +#endif + + + +#ifdef BN_MP_SUB_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* high level subtraction (handles signs) */ +int +mp_sub(mp_int *a, mp_int *b, mp_int *c) { + int sa, sb, res; + + sa = a->sign; + sb = b->sign; + + if (sa != sb) { + /* subtract a negative from a positive, OR */ + /* subtract a positive from a negative. */ + /* In either case, ADD their magnitudes, */ + /* and use the sign of the first number. */ + c->sign = sa; + res = s_mp_add(a, b, c); + } else { + /* subtract a positive from a positive, OR */ + /* subtract a negative from a negative. */ + /* First, take the difference between their */ + /* magnitudes, then... */ + if (mp_cmp_mag(a, b) != MP_LT) { + /* Copy the sign from the first */ + c->sign = sa; + /* The first has a larger or equal magnitude */ + res = s_mp_sub(a, b, c); + } else { + /* The result has the *opposite* sign from */ + /* the first number. */ + c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS; + /* The second has a larger magnitude */ + res = s_mp_sub(b, a, c); + } + } + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SUB_D_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* single digit subtraction */ +int +mp_sub_d(mp_int *a, mp_digit b, mp_int *c) { + mp_digit *tmpa, *tmpc, mu; + int res, ix, oldused; + + /* grow c as required */ + if (c->alloc < (a->used + 1)) { + if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* if a is negative just do an unsigned + * addition [with fudged signs] + */ + if (a->sign == MP_NEG) { + a->sign = MP_ZPOS; + res = mp_add_d(a, b, c); + a->sign = c->sign = MP_NEG; + + /* clamp */ + mp_clamp(c); + + return res; + } + + /* setup regs */ + oldused = c->used; + tmpa = a->dp; + tmpc = c->dp; + + /* if a <= b simply fix the single digit */ + if (((a->used == 1) && (a->dp[0] <= b)) || (a->used == 0)) { + if (a->used == 1) { + *tmpc++ = b - *tmpa; + } else { + *tmpc++ = b; + } + ix = 1; + + /* negative/1digit */ + c->sign = MP_NEG; + c->used = 1; + } else { + /* positive/size */ + c->sign = MP_ZPOS; + c->used = a->used; + + /* subtract first digit */ + *tmpc = *tmpa++ - b; + mu = *tmpc >> ((sizeof(mp_digit) * CHAR_BIT) - 1); + *tmpc++ &= MP_MASK; + + /* handle rest of the digits */ + for (ix = 1; ix < a->used; ix++) { + *tmpc = *tmpa++ - mu; + mu = *tmpc >> ((sizeof(mp_digit) * CHAR_BIT) - 1); + *tmpc++ &= MP_MASK; + } + } + + /* zero excess digits */ + while (ix++ < oldused) { + *tmpc++ = 0; + } + mp_clamp(c); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_SUBMOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* d = a - b (mod c) */ +int +mp_submod(mp_int *a, mp_int *b, mp_int *c, mp_int *d) { + int res; + mp_int t; + + + if ((res = mp_init(&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_sub(a, b, &t)) != MP_OKAY) { + mp_clear(&t); + return res; + } + res = mp_mod(&t, c, d); + mp_clear(&t); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_TO_SIGNED_BIN_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* store in signed [big endian] format */ +int mp_to_signed_bin(mp_int *a, unsigned char *b) { + int res; + + if ((res = mp_to_unsigned_bin(a, b + 1)) != MP_OKAY) { + return res; + } + b[0] = (a->sign == MP_ZPOS) ? (unsigned char)0 : (unsigned char)1; + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_TO_SIGNED_BIN_N_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* store in signed [big endian] format */ +int mp_to_signed_bin_n(mp_int *a, unsigned char *b, unsigned long *outlen) { + if (*outlen < (unsigned long)mp_signed_bin_size(a)) { + return MP_VAL; + } + *outlen = mp_signed_bin_size(a); + return mp_to_signed_bin(a, b); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_TO_UNSIGNED_BIN_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* store in unsigned [big endian] format */ +int mp_to_unsigned_bin(mp_int *a, unsigned char *b) { + int x, res; + mp_int t; + + if ((res = mp_init_copy(&t, a)) != MP_OKAY) { + return res; + } + + x = 0; + while (mp_iszero(&t) == MP_NO) { + #ifndef MP_8BIT + b[x++] = (unsigned char)(t.dp[0] & 255); + #else + b[x++] = (unsigned char)(t.dp[0] | ((t.dp[1] & 0x01) << 7)); + #endif + if ((res = mp_div_2d(&t, 8, &t, NULL)) != MP_OKAY) { + mp_clear(&t); + return res; + } + } + bn_reverse(b, x); + mp_clear(&t); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_TOOM_MUL_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* multiplication using the Toom-Cook 3-way algorithm + * + * Much more complicated than Karatsuba but has a lower + * asymptotic running time of O(N**1.464). This algorithm is + * only particularly useful on VERY large inputs + * (we're talking 1000s of digits here...). + */ +int mp_toom_mul(mp_int *a, mp_int *b, mp_int *c) { + mp_int w0, w1, w2, w3, w4, tmp1, tmp2, a0, a1, a2, b0, b1, b2; + int res, B; + + /* init temps */ + if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, + &a0, &a1, &a2, &b0, &b1, + &b2, &tmp1, &tmp2, NULL)) != MP_OKAY) { + return res; + } + + /* B */ + B = MIN(a->used, b->used) / 3; + + /* a = a2 * B**2 + a1 * B + a0 */ + if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(a, &a1)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a1, B); + if ((res = mp_mod_2d(&a1, DIGIT_BIT * B, &a1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(a, &a2)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a2, B * 2); + + /* b = b2 * B**2 + b1 * B + b0 */ + if ((res = mp_mod_2d(b, DIGIT_BIT * B, &b0)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(b, &b1)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&b1, B); + (void)mp_mod_2d(&b1, DIGIT_BIT * B, &b1); + + if ((res = mp_copy(b, &b2)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&b2, B * 2); + + /* w0 = a0*b0 */ + if ((res = mp_mul(&a0, &b0, &w0)) != MP_OKAY) { + goto ERR; + } + + /* w4 = a2 * b2 */ + if ((res = mp_mul(&a2, &b2, &w4)) != MP_OKAY) { + goto ERR; + } + + /* w1 = (a2 + 2(a1 + 2a0))(b2 + 2(b1 + 2b0)) */ + if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul_2(&b0, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b2, &tmp2)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul(&tmp1, &tmp2, &w1)) != MP_OKAY) { + goto ERR; + } + + /* w3 = (a0 + 2(a1 + 2a2))(b0 + 2(b1 + 2b2)) */ + if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul_2(&b2, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul(&tmp1, &tmp2, &w3)) != MP_OKAY) { + goto ERR; + } + + + /* w2 = (a2 + a1 + a0)(b2 + b1 + b0) */ + if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&b2, &b1, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul(&tmp1, &tmp2, &w2)) != MP_OKAY) { + goto ERR; + } + + /* now solve the matrix + + 0 0 0 0 1 + 1 2 4 8 16 + 1 1 1 1 1 + 16 8 4 2 1 + 1 0 0 0 0 + + using 12 subtractions, 4 shifts, + 2 small divisions and 1 small multiplication + */ + + /* r1 - r4 */ + if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r0 */ + if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/2 */ + if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3/2 */ + if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) { + goto ERR; + } + /* r2 - r0 - r4 */ + if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1 - 8r0 */ + if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - 8r4 */ + if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) { + goto ERR; + } + /* 3r2 - r1 - r3 */ + if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/3 */ + if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) { + goto ERR; + } + /* r3/3 */ + if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) { + goto ERR; + } + + /* at this point shift W[n] by B*n */ + if ((res = mp_lshd(&w1, 1 * B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w2, 2 * B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w3, 3 * B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w4, 4 * B)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_add(&w0, &w1, c)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, c, c)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear_multi(&w0, &w1, &w2, &w3, &w4, + &a0, &a1, &a2, &b0, &b1, + &b2, &tmp1, &tmp2, NULL); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_TOOM_SQR_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* squaring using Toom-Cook 3-way algorithm */ +int +mp_toom_sqr(mp_int *a, mp_int *b) { + mp_int w0, w1, w2, w3, w4, tmp1, a0, a1, a2; + int res, B; + + /* init temps */ + if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL)) != MP_OKAY) { + return res; + } + + /* B */ + B = a->used / 3; + + /* a = a2 * B**2 + a1 * B + a0 */ + if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(a, &a1)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a1, B); + if ((res = mp_mod_2d(&a1, DIGIT_BIT * B, &a1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(a, &a2)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a2, B * 2); + + /* w0 = a0*a0 */ + if ((res = mp_sqr(&a0, &w0)) != MP_OKAY) { + goto ERR; + } + + /* w4 = a2 * a2 */ + if ((res = mp_sqr(&a2, &w4)) != MP_OKAY) { + goto ERR; + } + + /* w1 = (a2 + 2(a1 + 2a0))**2 */ + if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_sqr(&tmp1, &w1)) != MP_OKAY) { + goto ERR; + } + + /* w3 = (a0 + 2(a1 + 2a2))**2 */ + if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_sqr(&tmp1, &w3)) != MP_OKAY) { + goto ERR; + } + + + /* w2 = (a2 + a1 + a0)**2 */ + if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sqr(&tmp1, &w2)) != MP_OKAY) { + goto ERR; + } + + /* now solve the matrix + + 0 0 0 0 1 + 1 2 4 8 16 + 1 1 1 1 1 + 16 8 4 2 1 + 1 0 0 0 0 + + using 12 subtractions, 4 shifts, 2 small divisions and 1 small multiplication. + */ + + /* r1 - r4 */ + if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r0 */ + if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/2 */ + if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3/2 */ + if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) { + goto ERR; + } + /* r2 - r0 - r4 */ + if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1 - 8r0 */ + if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - 8r4 */ + if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) { + goto ERR; + } + /* 3r2 - r1 - r3 */ + if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/3 */ + if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) { + goto ERR; + } + /* r3/3 */ + if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) { + goto ERR; + } + + /* at this point shift W[n] by B*n */ + if ((res = mp_lshd(&w1, 1 * B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w2, 2 * B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w3, 3 * B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w4, 4 * B)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_add(&w0, &w1, b)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, b, b)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL); + return res; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_TORADIX_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* stores a bignum as a ASCII string in a given radix (2..64) */ +int mp_toradix(mp_int *a, char *str, int radix) { + int res, digs; + mp_int t; + mp_digit d; + char *_s = str; + + /* check range of the radix */ + if ((radix < 2) || (radix > 64)) { + return MP_VAL; + } + + /* quick out if its zero */ + if (mp_iszero(a) == MP_YES) { + *str++ = '0'; + *str = '\0'; + return MP_OKAY; + } + + if ((res = mp_init_copy(&t, a)) != MP_OKAY) { + return res; + } + + /* if it is negative output a - */ + if (t.sign == MP_NEG) { + ++_s; + *str++ = '-'; + t.sign = MP_ZPOS; + } + + digs = 0; + while (mp_iszero(&t) == MP_NO) { + if ((res = mp_div_d(&t, (mp_digit)radix, &t, &d)) != MP_OKAY) { + mp_clear(&t); + return res; + } + *str++ = mp_s_rmap[d]; + ++digs; + } + + /* reverse the digits of the string. In this case _s points + * to the first digit [exluding the sign] of the number] + */ + bn_reverse((unsigned char *)_s, digs); + + /* append a NULL so the string is properly terminated */ + *str = '\0'; + + mp_clear(&t); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_UNSIGNED_BIN_SIZE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* get the size for an unsigned equivalent */ +int mp_unsigned_bin_size(mp_int *a) { + int size = mp_count_bits(a); + + return (size / 8) + (((size & 7) != 0) ? 1 : 0); +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_XOR_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* XOR two ints together */ +int +mp_xor(mp_int *a, mp_int *b, mp_int *c) { + int res, ix, px; + mp_int t, *x; + + if (a->used > b->used) { + if ((res = mp_init_copy(&t, a)) != MP_OKAY) { + return res; + } + px = b->used; + x = b; + } else { + if ((res = mp_init_copy(&t, b)) != MP_OKAY) { + return res; + } + px = a->used; + x = a; + } + + for (ix = 0; ix < px; ix++) { + t.dp[ix] ^= x->dp[ix]; + } + mp_clamp(&t); + mp_exch(c, &t); + mp_clear(&t); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_MP_ZERO_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* set to zero */ +void mp_zero(mp_int *a) { + int n; + mp_digit *tmp; + + a->sign = MP_ZPOS; + a->used = 0; + + tmp = a->dp; + for (n = 0; n < a->alloc; n++) { + *tmp++ = 0; + } +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_PRIME_TAB_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ +const mp_digit ltm_prime_tab[] = { + 0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013, + 0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035, + 0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059, + 0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F, + #ifndef MP_8BIT + 0x0083, + 0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD, + 0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF, + 0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107, + 0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137, + + 0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167, + 0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199, + 0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9, + 0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7, + 0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239, + 0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265, + 0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293, + 0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF, + + 0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, 0x02F5, 0x02F9, 0x0301, + 0x0305, 0x0313, 0x031D, 0x0329, 0x032B, 0x0335, 0x0337, 0x033B, + 0x033D, 0x0347, 0x0355, 0x0359, 0x035B, 0x035F, 0x036D, 0x0371, + 0x0373, 0x0377, 0x038B, 0x038F, 0x0397, 0x03A1, 0x03A9, 0x03AD, + 0x03B3, 0x03B9, 0x03C7, 0x03CB, 0x03D1, 0x03D7, 0x03DF, 0x03E5, + 0x03F1, 0x03F5, 0x03FB, 0x03FD, 0x0407, 0x0409, 0x040F, 0x0419, + 0x041B, 0x0425, 0x0427, 0x042D, 0x043F, 0x0443, 0x0445, 0x0449, + 0x044F, 0x0455, 0x045D, 0x0463, 0x0469, 0x047F, 0x0481, 0x048B, + + 0x0493, 0x049D, 0x04A3, 0x04A9, 0x04B1, 0x04BD, 0x04C1, 0x04C7, + 0x04CD, 0x04CF, 0x04D5, 0x04E1, 0x04EB, 0x04FD, 0x04FF, 0x0503, + 0x0509, 0x050B, 0x0511, 0x0515, 0x0517, 0x051B, 0x0527, 0x0529, + 0x052F, 0x0551, 0x0557, 0x055D, 0x0565, 0x0577, 0x0581, 0x058F, + 0x0593, 0x0595, 0x0599, 0x059F, 0x05A7, 0x05AB, 0x05AD, 0x05B3, + 0x05BF, 0x05C9, 0x05CB, 0x05CF, 0x05D1, 0x05D5, 0x05DB, 0x05E7, + 0x05F3, 0x05FB, 0x0607, 0x060D, 0x0611, 0x0617, 0x061F, 0x0623, + 0x062B, 0x062F, 0x063D, 0x0641, 0x0647, 0x0649, 0x064D, 0x0653 + #endif +}; +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_REVERSE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* reverse an array, used for radix code */ +void +bn_reverse(unsigned char *s, int len) { + int ix, iy; + unsigned char t; + + ix = 0; + iy = len - 1; + while (ix < iy) { + t = s[ix]; + s[ix] = s[iy]; + s[iy] = t; + ++ix; + --iy; + } +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_S_MP_ADD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* low level addition, based on HAC pp.594, Algorithm 14.7 */ +int +s_mp_add(mp_int *a, mp_int *b, mp_int *c) { + mp_int *x; + int olduse, res, min, max; + + /* find sizes, we let |a| <= |b| which means we have to sort + * them. "x" will point to the input with the most digits + */ + if (a->used > b->used) { + min = b->used; + max = a->used; + x = a; + } else { + min = a->used; + max = b->used; + x = b; + } + + /* init result */ + if (c->alloc < (max + 1)) { + if ((res = mp_grow(c, max + 1)) != MP_OKAY) { + return res; + } + } + + /* get old used digit count and set new one */ + olduse = c->used; + c->used = max + 1; + + { + mp_digit u, *tmpa, *tmpb, *tmpc; + int i; + + /* alias for digit pointers */ + + /* first input */ + tmpa = a->dp; + + /* second input */ + tmpb = b->dp; + + /* destination */ + tmpc = c->dp; + + /* zero the carry */ + u = 0; + for (i = 0; i < min; i++) { + /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ + *tmpc = *tmpa++ + *tmpb++ + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, that is in A+B + * if A or B has more digits add those in + */ + if (min != max) { + for ( ; i < max; i++) { + /* T[i] = X[i] + U */ + *tmpc = x->dp[i] + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + } + + /* add carry */ + *tmpc++ = u; + + /* clear digits above oldused */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp(c); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_S_MP_EXPTMOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +int s_mp_exptmod(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int redmode) { + mp_int M[TAB_SIZE], res, mu; + mp_digit buf; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + + int (*redux)(mp_int *, mp_int *, mp_int *); + + /* find window size */ + x = mp_count_bits(X); + if (x <= 7) { + winsize = 2; + } else if (x <= 36) { + winsize = 3; + } else if (x <= 140) { + winsize = 4; + } else if (x <= 450) { + winsize = 5; + } else if (x <= 1303) { + winsize = 6; + } else if (x <= 3529) { + winsize = 7; + } else { + winsize = 8; + } + + #ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } + #endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1 << (winsize - 1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1 << (winsize - 1); y < x; y++) { + mp_clear(&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* create mu, used for Barrett reduction */ + if ((err = mp_init(&mu)) != MP_OKAY) { + goto LBL_M; + } + + if (redmode == 0) { + if ((err = mp_reduce_setup(&mu, P)) != MP_OKAY) { + goto LBL_MU; + } + redux = mp_reduce; + } else { + if ((err = mp_reduce_2k_setup_l(P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + redux = mp_reduce_2k_l; + } + + /* create M table + * + * The M table contains powers of the base, + * e.g. M[x] = G**x mod P + * + * The first half of the table is not + * computed though accept for M[0] and M[1] + */ + if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { + goto LBL_MU; + } + + /* compute the value at M[1<<(winsize-1)] by squaring + * M[1] (winsize-1) times + */ + if ((err = mp_copy(&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_MU; + } + + for (x = 0; x < (winsize - 1); x++) { + /* square it */ + if ((err = mp_sqr(&M[1 << (winsize - 1)], + &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_MU; + } + + /* reduce modulo P */ + if ((err = redux(&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + } + + /* create upper table, that is M[x] = M[x-1] * M[1] (mod P) + * for x = (2**(winsize - 1) + 1) to (2**winsize - 1) + */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul(&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto LBL_MU; + } + if ((err = redux(&M[x], P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + } + + /* setup result */ + if ((err = mp_init(&res)) != MP_OKAY) { + goto LBL_MU; + } + mp_set(&res, 1); + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for ( ; ; ) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits */ + if (digidx == -1) { + break; + } + /* read next digit and reset the bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int)DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if ((mode == 0) && (y == 0)) { + continue; + } + + /* if the bit is zero and mode == 1 then we square */ + if ((mode == 1) && (y == 0)) { + if ((err = mp_sqr(&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr(&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* then multiply */ + if ((err = mp_mul(&res, &M[bitbuf], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + + /* empty window and reset */ + bitcpy = 0; + bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if ((mode == 2) && (bitcpy > 0)) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr(&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul(&res, &M[1], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux(&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + } + } + } + + mp_exch(&res, Y); + err = MP_OKAY; +LBL_RES: mp_clear(&res); +LBL_MU: mp_clear(&mu); +LBL_M: + mp_clear(&M[1]); + for (x = 1 << (winsize - 1); x < (1 << winsize); x++) { + mp_clear(&M[x]); + } + return err; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_S_MP_MUL_DIGS_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* multiplies |a| * |b| and only computes upto digs digits of result + * HAC pp. 595, Algorithm 14.12 Modified so you can control how + * many digits of output are created. + */ +int s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs) { + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + /* can we use the fast multiplier? */ + if (((digs) < MP_WARRAY) && + (MIN(a->used, b->used) < + (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { + return fast_s_mp_mul_digs(a, b, c, digs); + } + + if ((res = mp_init_size(&t, digs)) != MP_OKAY) { + return res; + } + t.used = digs; + + /* compute the digits of the product directly */ + pa = a->used; + for (ix = 0; ix < pa; ix++) { + /* set the carry to zero */ + u = 0; + + /* limit ourselves to making digs digits of output */ + pb = MIN(b->used, digs - ix); + + /* setup some aliases */ + /* copy of the digit from a used within the nested loop */ + tmpx = a->dp[ix]; + + /* an alias for the destination shifted ix places */ + tmpt = t.dp + ix; + + /* an alias for the digits of b */ + tmpy = b->dp; + + /* compute the columns of the output and propagate the carry */ + for (iy = 0; iy < pb; iy++) { + /* compute the column as a mp_word */ + r = (mp_word) * tmpt + + ((mp_word)tmpx * (mp_word) * tmpy++) + + (mp_word)u; + + /* the new column is the lower part of the result */ + *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK)); + + /* get the carry word from the result */ + u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + } + /* set carry if it is placed below digs */ + if ((ix + iy) < digs) { + *tmpt = u; + } + } + + mp_clamp(&t); + mp_exch(&t, c); + + mp_clear(&t); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_S_MP_MUL_HIGH_DIGS_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* multiplies |a| * |b| and does not compute the lower digs digits + * [meant to get the higher part of the product] + */ +int +s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs) { + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + /* can we use the fast multiplier? */ + #ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C + if (((a->used + b->used + 1) < MP_WARRAY) && + (MIN(a->used, b->used) < (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { + return fast_s_mp_mul_high_digs(a, b, c, digs); + } + #endif + + if ((res = mp_init_size(&t, a->used + b->used + 1)) != MP_OKAY) { + return res; + } + t.used = a->used + b->used + 1; + + pa = a->used; + pb = b->used; + for (ix = 0; ix < pa; ix++) { + /* clear the carry */ + u = 0; + + /* left hand side of A[ix] * B[iy] */ + tmpx = a->dp[ix]; + + /* alias to the address of where the digits will be stored */ + tmpt = &(t.dp[digs]); + + /* alias for where to read the right hand side from */ + tmpy = b->dp + (digs - ix); + + for (iy = digs - ix; iy < pb; iy++) { + /* calculate the double precision result */ + r = (mp_word) * tmpt + + ((mp_word)tmpx * (mp_word) * tmpy++) + + (mp_word)u; + + /* get the lower part */ + *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK)); + + /* carry the carry */ + u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + } + *tmpt = u; + } + mp_clamp(&t); + mp_exch(&t, c); + mp_clear(&t); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_S_MP_SQR_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */ +int s_mp_sqr(mp_int *a, mp_int *b) { + mp_int t; + int res, ix, iy, pa; + mp_word r; + mp_digit u, tmpx, *tmpt; + + pa = a->used; + if ((res = mp_init_size(&t, (2 * pa) + 1)) != MP_OKAY) { + return res; + } + + /* default used is maximum possible size */ + t.used = (2 * pa) + 1; + + for (ix = 0; ix < pa; ix++) { + /* first calculate the digit at 2*ix */ + /* calculate double precision result */ + r = (mp_word)t.dp[2 * ix] + + ((mp_word)a->dp[ix] * (mp_word)a->dp[ix]); + + /* store lower part in result */ + t.dp[ix + ix] = (mp_digit)(r & ((mp_word)MP_MASK)); + + /* get the carry */ + u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + + /* left hand side of A[ix] * A[iy] */ + tmpx = a->dp[ix]; + + /* alias for where to store the results */ + tmpt = t.dp + ((2 * ix) + 1); + + for (iy = ix + 1; iy < pa; iy++) { + /* first calculate the product */ + r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]); + + /* now calculate the double precision result, note we use + * addition instead of *2 since it's easier to optimize + */ + r = ((mp_word) * tmpt) + r + r + ((mp_word)u); + + /* store lower part */ + *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK)); + + /* get carry */ + u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + } + /* propagate upwards */ + while (u != ((mp_digit)0)) { + r = ((mp_word) * tmpt) + ((mp_word)u); + *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK)); + u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + } + } + + mp_clamp(&t); + mp_exch(&t, b); + mp_clear(&t); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BN_S_MP_SUB_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */ +int +s_mp_sub(mp_int *a, mp_int *b, mp_int *c) { + int olduse, res, min, max; + + /* find sizes */ + min = b->used; + max = a->used; + + /* init result */ + if (c->alloc < max) { + if ((res = mp_grow(c, max)) != MP_OKAY) { + return res; + } + } + olduse = c->used; + c->used = max; + + { + mp_digit u, *tmpa, *tmpb, *tmpc; + int i; + + /* alias for digit pointers */ + tmpa = a->dp; + tmpb = b->dp; + tmpc = c->dp; + + /* set carry to zero */ + u = 0; + for (i = 0; i < min; i++) { + /* T[i] = A[i] - B[i] - U */ + *tmpc = (*tmpa++ - *tmpb++) - u; + + /* U = carry bit of T[i] + * Note this saves performing an AND operation since + * if a carry does occur it will propagate all the way to the + * MSB. As a result a single shift is enough to get the carry + */ + u = *tmpc >> ((mp_digit)((CHAR_BIT * sizeof(mp_digit)) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, e.g. if A has more digits than B */ + for ( ; i < max; i++) { + /* T[i] = A[i] - U */ + *tmpc = *tmpa++ - u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)((CHAR_BIT * sizeof(mp_digit)) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* clear digits above used (since we may not have grown result above) */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp(c); + return MP_OKAY; +} +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + + +#ifdef BNCORE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tstdenis82@gmail.com, http://libtom.org + */ + +/* Known optimal configurations + + CPU /Compiler /MUL CUTOFF/SQR CUTOFF + ------------------------------------------------------------- + Intel P4 Northwood /GCC v3.4.1 / 88/ 128/LTM 0.32 ;-) + AMD Athlon64 /GCC v3.4.4 / 80/ 120/LTM 0.35 + + */ + +int KARATSUBA_MUL_CUTOFF = 80, /* Min. number of digits before Karatsuba multiplication is used. */ + KARATSUBA_SQR_CUTOFF = 120, /* Min. number of digits before Karatsuba squaring is used. */ + + TOOM_MUL_CUTOFF = 350, /* no optimal values of these are known yet so set em high */ + TOOM_SQR_CUTOFF = 400; +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file crypt.c + Build strings, Tom St Denis + */ + +const char *crypt_build_settings = + "LibTomCrypt ""1.17"" (Tom St Denis, tomstdenis@gmail.com)\n" + "LibTomCrypt is public domain software.\n" + "Built on " __DATE__ " at " __TIME__ "\n\n\n" + "Endianess: " +#if defined(ENDIAN_NEUTRAL) + "neutral\n" +#elif defined(ENDIAN_LITTLE) + "little" + #if defined(ENDIAN_32BITWORD) + " (32-bit words)\n" + #else + " (64-bit words)\n" + #endif +#elif defined(ENDIAN_BIG) + "big" + #if defined(ENDIAN_32BITWORD) + " (32-bit words)\n" + #else + " (64-bit words)\n" + #endif +#endif + "Clean stack: " +#if defined(LTC_CLEAN_STACK) + "enabled\n" +#else + "disabled\n" +#endif + "Ciphers built-in:\n" +#if defined(LTC_BLOWFISH) + " Blowfish\n" +#endif +#if defined(LTC_RC2) + " LTC_RC2\n" +#endif +#if defined(LTC_RC5) + " LTC_RC5\n" +#endif +#if defined(LTC_RC6) + " LTC_RC6\n" +#endif +#if defined(LTC_SAFERP) + " Safer+\n" +#endif +#if defined(LTC_SAFER) + " Safer\n" +#endif +#if defined(LTC_RIJNDAEL) + " Rijndael\n" +#endif +#if defined(LTC_XTEA) + " LTC_XTEA\n" +#endif +#if defined(LTC_TWOFISH) + " Twofish " + #if defined(LTC_TWOFISH_SMALL) && defined(LTC_TWOFISH_TABLES) && defined(LTC_TWOFISH_ALL_TABLES) + "(small, tables, all_tables)\n" + #elif defined(LTC_TWOFISH_SMALL) && defined(LTC_TWOFISH_TABLES) + "(small, tables)\n" + #elif defined(LTC_TWOFISH_SMALL) && defined(LTC_TWOFISH_ALL_TABLES) + "(small, all_tables)\n" + #elif defined(LTC_TWOFISH_TABLES) && defined(LTC_TWOFISH_ALL_TABLES) + "(tables, all_tables)\n" + #elif defined(LTC_TWOFISH_SMALL) + "(small)\n" + #elif defined(LTC_TWOFISH_TABLES) + "(tables)\n" + #elif defined(LTC_TWOFISH_ALL_TABLES) + "(all_tables)\n" + #else + "\n" + #endif +#endif +#if defined(LTC_DES) + " LTC_DES\n" +#endif +#if defined(LTC_CAST5) + " LTC_CAST5\n" +#endif +#if defined(LTC_NOEKEON) + " Noekeon\n" +#endif +#if defined(LTC_SKIPJACK) + " Skipjack\n" +#endif +#if defined(LTC_KHAZAD) + " Khazad\n" +#endif +#if defined(LTC_ANUBIS) + " Anubis " +#endif +#if defined(LTC_ANUBIS_TWEAK) + " (tweaked)" +#endif + "\n" +#if defined(LTC_KSEED) + " LTC_KSEED\n" +#endif +#if defined(LTC_KASUMI) + " KASUMI\n" +#endif + + "\nHashes built-in:\n" +#if defined(LTC_SHA512) + " LTC_SHA-512\n" +#endif +#if defined(LTC_SHA384) + " LTC_SHA-384\n" +#endif +#if defined(LTC_SHA256) + " LTC_SHA-256\n" +#endif +#if defined(LTC_SHA224) + " LTC_SHA-224\n" +#endif +#if defined(LTC_TIGER) + " LTC_TIGER\n" +#endif +#if defined(LTC_SHA1) + " LTC_SHA1\n" +#endif +#if defined(LTC_MD5) + " LTC_MD5\n" +#endif +#if defined(LTC_MD4) + " LTC_MD4\n" +#endif +#if defined(LTC_MD2) + " LTC_MD2\n" +#endif +#if defined(LTC_RIPEMD128) + " LTC_RIPEMD128\n" +#endif +#if defined(LTC_RIPEMD160) + " LTC_RIPEMD160\n" +#endif +#if defined(LTC_RIPEMD256) + " LTC_RIPEMD256\n" +#endif +#if defined(LTC_RIPEMD320) + " LTC_RIPEMD320\n" +#endif +#if defined(LTC_WHIRLPOOL) + " LTC_WHIRLPOOL\n" +#endif +#if defined(LTC_CHC_HASH) + " LTC_CHC_HASH \n" +#endif + + "\nBlock Chaining Modes:\n" +#if defined(LTC_CFB_MODE) + " CFB\n" +#endif +#if defined(LTC_OFB_MODE) + " OFB\n" +#endif +#if defined(LTC_ECB_MODE) + " ECB\n" +#endif +#if defined(LTC_CBC_MODE) + " CBC\n" +#endif +#if defined(LTC_CTR_MODE) + " CTR " +#endif +#if defined(LTC_CTR_OLD) + " (CTR_OLD) " +#endif + "\n" +#if defined(LRW_MODE) + " LRW_MODE" + #if defined(LRW_TABLES) + " (LRW_TABLES) " + #endif + "\n" +#endif +#if defined(LTC_F8_MODE) + " F8 MODE\n" +#endif +#if defined(LTC_XTS_MODE) + " LTC_XTS_MODE\n" +#endif + + "\nMACs:\n" +#if defined(LTC_HMAC) + " LTC_HMAC\n" +#endif +#if defined(LTC_OMAC) + " LTC_OMAC\n" +#endif +#if defined(LTC_PMAC) + " PMAC\n" +#endif +#if defined(LTC_PELICAN) + " LTC_PELICAN\n" +#endif +#if defined(LTC_XCBC) + " XCBC-MAC\n" +#endif +#if defined(LTC_F9_MODE) + " F9-MAC\n" +#endif + + "\nENC + AUTH modes:\n" +#if defined(LTC_EAX_MODE) + " LTC_EAX_MODE\n" +#endif +#if defined(LTC_OCB_MODE) + " LTC_OCB_MODE\n" +#endif +#if defined(LTC_CCM_MODE) + " LTC_CCM_MODE\n" +#endif +#if defined(LTC_GCM_MODE) + " LTC_GCM_MODE " +#endif +#if defined(LTC_GCM_TABLES) + " (LTC_GCM_TABLES) " +#endif + "\n" + + "\nPRNG:\n" +#if defined(LTC_YARROW) + " Yarrow\n" +#endif +#if defined(LTC_SPRNG) + " LTC_SPRNG\n" +#endif +#if defined(LTC_RC4) + " LTC_RC4\n" +#endif +#if defined(LTC_FORTUNA) + " Fortuna\n" +#endif +#if defined(LTC_SOBER128) + " LTC_SOBER128\n" +#endif + + "\nPK Algs:\n" +#if defined(LTC_MRSA) + " RSA \n" +#endif +#if defined(LTC_MECC) + " ECC\n" +#endif +#if defined(LTC_MDSA) + " DSA\n" +#endif +#if defined(MKAT) + " Katja\n" +#endif + + "\nCompiler:\n" +#if defined(WIN32) + " WIN32 platform detected.\n" +#endif +#if defined(__CYGWIN__) + " CYGWIN Detected.\n" +#endif +#if defined(__DJGPP__) + " DJGPP Detected.\n" +#endif +#if defined(_MSC_VER) + " MSVC compiler detected.\n" +#endif +#if defined(__GNUC__) + " GCC compiler detected.\n" +#endif +#if defined(INTEL_CC) + " Intel C Compiler detected.\n" +#endif +#if defined(__x86_64__) + " x86-64 detected.\n" +#endif +#if defined(LTC_PPC32) + " LTC_PPC32 defined \n" +#endif + + "\nVarious others: " +#if defined(LTC_BASE64) + " LTC_BASE64 " +#endif +#if defined(MPI) + " MPI " +#endif +#if defined(TRY_UNRANDOM_FIRST) + " TRY_UNRANDOM_FIRST " +#endif +#if defined(LTC_TEST) + " LTC_TEST " +#endif +#if defined(LTC_PKCS_1) + " LTC_PKCS#1 " +#endif +#if defined(LTC_PKCS_5) + " LTC_PKCS#5 " +#endif +#if defined(LTC_SMALL_CODE) + " LTC_SMALL_CODE " +#endif +#if defined(LTC_NO_FILE) + " LTC_NO_FILE " +#endif +#if defined(LTC_DER) + " LTC_DER " +#endif +#if defined(LTC_FAST) + " LTC_FAST " +#endif +#if defined(LTC_NO_FAST) + " LTC_NO_FAST " +#endif +#if defined(LTC_NO_BSWAP) + " LTC_NO_BSWAP " +#endif +#if defined(LTC_NO_ASM) + " LTC_NO_ASM " +#endif +#if defined(LTC_NO_TEST) + " LTC_NO_TEST " +#endif +#if defined(LTC_NO_TABLES) + " LTC_NO_TABLES " +#endif +#if defined(LTC_PTHREAD) + " LTC_PTHREAD " +#endif +#if defined(LTM_LTC_DESC) + " LTM_DESC " +#endif +#if defined(TFM_LTC_DESC) + " TFM_DESC " +#endif +#if defined(LTC_MECC_ACCEL) + " LTC_MECC_ACCEL " +#endif +#if defined(GMP_LTC_DESC) + " GMP_DESC " +#endif +#if defined(LTC_EASY) + " (easy) " +#endif +#if defined(LTC_MECC_FP) + " LTC_MECC_FP " +#endif +#if defined(LTC_ECC_SHAMIR) + " LTC_ECC_SHAMIR " +#endif + "\n" + "\n\n\n" +; + + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt.c,v $ */ +/* $Revision: 1.36 $ */ +/* $Date: 2007/05/12 14:46:12 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include + +/** + @file crypt_argchk.c + Perform argument checking, Tom St Denis + */ + +#if (ARGTYPE == 0) +void crypt_argchk(char *v, char *s, int d) { + fprintf(stderr, "LTC_ARGCHK '%s' failure on line %d of file %s\n", + v, d, s); + (void)raise(SIGABRT); +} +#endif + +#ifndef TOMCRYPT_H_ +#define TOMCRYPT_H_ +#define USE_LTM +#define LTM_DESC +#define LTC_SHA1 +#include +#include +#include +#include +#include +#include +#include + +/* use configuration data */ +#ifndef TOMCRYPT_CUSTOM_H_ +#define TOMCRYPT_CUSTOM_H_ + +/* macros for various libc functions you can change for embedded targets */ +#ifndef XMALLOC + #ifdef malloc + #define LTC_NO_PROTOTYPES + #endif + #define XMALLOC malloc +#endif +#ifndef XREALLOC + #ifdef realloc + #define LTC_NO_PROTOTYPES + #endif + #define XREALLOC realloc +#endif +#ifndef XCALLOC + #ifdef calloc + #define LTC_NO_PROTOTYPES + #endif + #define XCALLOC calloc +#endif +#ifndef XFREE + #ifdef free + #define LTC_NO_PROTOTYPES + #endif + #define XFREE free +#endif + +#ifndef XMEMSET + #ifdef memset + #define LTC_NO_PROTOTYPES + #endif + #define XMEMSET memset +#endif +#ifndef XMEMCPY + #ifdef memcpy + #define LTC_NO_PROTOTYPES + #endif + #define XMEMCPY memcpy +#endif +#ifndef XMEMCMP + #ifdef memcmp + #define LTC_NO_PROTOTYPES + #endif + #define XMEMCMP memcmp +#endif +#ifndef XSTRCMP + #ifdef strcmp + #define LTC_NO_PROTOTYPES + #endif + #define XSTRCMP strcmp +#endif + +#ifndef XCLOCK + #define XCLOCK clock +#endif +#ifndef XCLOCKS_PER_SEC + #define XCLOCKS_PER_SEC CLOCKS_PER_SEC +#endif + +#ifndef XQSORT + #ifdef qsort + #define LTC_NO_PROTOTYPES + #endif + #define XQSORT qsort +#endif + +/* Easy button? */ +#ifdef LTC_EASY + #define LTC_NO_CIPHERS + #define LTC_RIJNDAEL + #define LTC_BLOWFISH + #define LTC_DES + #define LTC_CAST5 + + #define LTC_NO_MODES + #define LTC_ECB_MODE + #define LTC_CBC_MODE + #define LTC_CTR_MODE + + #define LTC_NO_HASHES + #define LTC_SHA1 + #define LTC_SHA512 + #define LTC_SHA384 + #define LTC_SHA256 + #define LTC_SHA224 + + #define LTC_NO_MACS + #define LTC_HMAC + #define LTC_OMAC + #define LTC_CCM_MODE + + #define LTC_NO_PRNGS + #define LTC_SPRNG + #define LTC_YARROW + #define LTC_DEVRANDOM + #define TRY_URANDOM_FIRST + + #define LTC_NO_PK + #define LTC_MRSA + #define LTC_MECC +#endif + +/* Use small code where possible */ +/* #define LTC_SMALL_CODE */ + +/* Enable self-test test vector checking */ +#ifndef LTC_NO_TEST + #define LTC_TEST +#endif + +/* clean the stack of functions which put private information on stack */ +/* #define LTC_CLEAN_STACK */ + +/* disable all file related functions */ +/* #define LTC_NO_FILE */ + +/* disable all forms of ASM */ +/* #define LTC_NO_ASM */ + +/* disable FAST mode */ +/* #define LTC_NO_FAST */ + +/* disable BSWAP on x86 */ +/* #define LTC_NO_BSWAP */ + +/* ---> Symmetric Block Ciphers <--- */ +#ifndef LTC_NO_CIPHERS + + #define LTC_BLOWFISH + #define LTC_RC2 + #define LTC_RC5 + #define LTC_RC6 + #define LTC_SAFERP + #define LTC_RIJNDAEL + #define LTC_XTEA + +/* _TABLES tells it to use tables during setup, _SMALL means to use the smaller scheduled key format + * (saves 4KB of ram), _ALL_TABLES enables all tables during setup */ + #define LTC_TWOFISH + #ifndef LTC_NO_TABLES + #define LTC_TWOFISH_TABLES +/* #define LTC_TWOFISH_ALL_TABLES */ + #else + #define LTC_TWOFISH_SMALL + #endif +/* #define LTC_TWOFISH_SMALL */ +/* LTC_DES includes EDE triple-LTC_DES */ + #define LTC_DES + #define LTC_CAST5 + #define LTC_NOEKEON + #define LTC_SKIPJACK + #define LTC_SAFER + #define LTC_KHAZAD + #define LTC_ANUBIS + #define LTC_ANUBIS_TWEAK + #define LTC_KSEED + #define LTC_KASUMI +#endif /* LTC_NO_CIPHERS */ + + +/* ---> Block Cipher Modes of Operation <--- */ +#ifndef LTC_NO_MODES + + #define LTC_CFB_MODE + #define LTC_OFB_MODE + #define LTC_ECB_MODE + #define LTC_CBC_MODE + #define LTC_CTR_MODE + +/* F8 chaining mode */ + #define LTC_F8_MODE + +/* LRW mode */ + #define LTC_LRW_MODE + #ifndef LTC_NO_TABLES + +/* like GCM mode this will enable 16 8x128 tables [64KB] that make + * seeking very fast. + */ + #define LRW_TABLES + #endif + +/* XTS mode */ + #define LTC_XTS_MODE +#endif /* LTC_NO_MODES */ + +/* ---> One-Way Hash Functions <--- */ +#ifndef LTC_NO_HASHES + + #define LTC_CHC_HASH + #define LTC_WHIRLPOOL + #define LTC_SHA512 + #define LTC_SHA384 + #define LTC_SHA256 + #define LTC_SHA224 + #define LTC_TIGER + #define LTC_SHA1 + #define LTC_MD5 + #define LTC_MD4 + #define LTC_MD2 + #define LTC_RIPEMD128 + #define LTC_RIPEMD160 + #define LTC_RIPEMD256 + #define LTC_RIPEMD320 +#endif /* LTC_NO_HASHES */ + +/* ---> MAC functions <--- */ +#ifndef LTC_NO_MACS + + #define LTC_HMAC + #define LTC_OMAC + #define LTC_PMAC + #define LTC_XCBC + #define LTC_F9_MODE + #define LTC_PELICAN + + #if defined(LTC_PELICAN) && !defined(LTC_RIJNDAEL) + #error Pelican-MAC requires LTC_RIJNDAEL + #endif + +/* ---> Encrypt + Authenticate Modes <--- */ + + #define LTC_EAX_MODE + #if defined(LTC_EAX_MODE) && !(defined(LTC_CTR_MODE) && defined(LTC_OMAC)) + #error LTC_EAX_MODE requires CTR and LTC_OMAC mode + #endif + + #define LTC_OCB_MODE + #define LTC_CCM_MODE + #define LTC_GCM_MODE + +/* Use 64KiB tables */ + #ifndef LTC_NO_TABLES + #define LTC_GCM_TABLES + #endif + +/* USE SSE2? requires GCC works on x86_32 and x86_64*/ + #ifdef LTC_GCM_TABLES +/* #define LTC_GCM_TABLES_SSE2 */ + #endif +#endif /* LTC_NO_MACS */ + +/* Various tidbits of modern neatoness */ +#define LTC_BASE64 + +/* --> Pseudo Random Number Generators <--- */ +#ifndef LTC_NO_PRNGS + +/* Yarrow */ + #define LTC_YARROW +/* which descriptor of AES to use? */ +/* 0 = rijndael_enc 1 = aes_enc, 2 = rijndael [full], 3 = aes [full] */ + #define LTC_YARROW_AES 0 + + #if defined(LTC_YARROW) && !defined(LTC_CTR_MODE) + #error LTC_YARROW requires LTC_CTR_MODE chaining mode to be defined! + #endif + +/* a PRNG that simply reads from an available system source */ + #define LTC_SPRNG + +/* The LTC_RC4 stream cipher */ + #define LTC_RC4 + +/* Fortuna PRNG */ + #define LTC_FORTUNA +/* reseed every N calls to the read function */ + #define LTC_FORTUNA_WD 10 +/* number of pools (4..32) can save a bit of ram by lowering the count */ + #define LTC_FORTUNA_POOLS 32 + +/* Greg's LTC_SOBER128 PRNG ;-0 */ + #define LTC_SOBER128 + +/* the *nix style /dev/random device */ + #define LTC_DEVRANDOM +/* try /dev/urandom before trying /dev/random */ + #define TRY_URANDOM_FIRST +#endif /* LTC_NO_PRNGS */ + +/* ---> math provider? <--- */ +#ifndef LTC_NO_MATH + +/* LibTomMath */ +/* #define LTM_LTC_DESC */ + +/* TomsFastMath */ +/* #define TFM_LTC_DESC */ +#endif /* LTC_NO_MATH */ + +/* ---> Public Key Crypto <--- */ +#ifndef LTC_NO_PK + +/* Include RSA support */ + #define LTC_MRSA + +/* Include Katja (a Rabin variant like RSA) */ +/* #define MKAT */ + +/* Digital Signature Algorithm */ + #define LTC_MDSA + +/* ECC */ + #define LTC_MECC + +/* use Shamir's trick for point mul (speeds up signature verification) */ + #define LTC_ECC_SHAMIR + + #if defined(TFM_LTC_DESC) && defined(LTC_MECC) + #define LTC_MECC_ACCEL + #endif + +/* do we want fixed point ECC */ +/* #define LTC_MECC_FP */ + +/* Timing Resistant? */ +/* #define LTC_ECC_TIMING_RESISTANT */ +#endif /* LTC_NO_PK */ + +/* LTC_PKCS #1 (RSA) and #5 (Password Handling) stuff */ +#ifndef LTC_NO_PKCS + + #define LTC_PKCS_1 + #define LTC_PKCS_5 + +/* Include ASN.1 DER (required by DSA/RSA) */ + #define LTC_DER +#endif /* LTC_NO_PKCS */ + +/* cleanup */ + +#ifdef LTC_MECC +/* Supported ECC Key Sizes */ + #ifndef LTC_NO_CURVES + #define ECC112 + #define ECC128 + #define ECC160 + #define ECC192 + #define ECC224 + #define ECC256 + #define ECC384 + #define ECC521 + #endif +#endif + +#if defined(LTC_MECC) || defined(LTC_MRSA) || defined(LTC_MDSA) || defined(MKATJA) +/* Include the MPI functionality? (required by the PK algorithms) */ + #define MPI +#endif + +#ifdef LTC_MRSA + #define LTC_PKCS_1 +#endif + +#if defined(LTC_DER) && !defined(MPI) + #error ASN.1 DER requires MPI functionality +#endif + +#if (defined(LTC_MDSA) || defined(LTC_MRSA) || defined(LTC_MECC) || defined(MKATJA)) && !defined(LTC_DER) + #error PK requires ASN.1 DER functionality, make sure LTC_DER is enabled +#endif + +/* THREAD management */ +#ifdef LTC_PTHREAD + + #include + + #define LTC_MUTEX_GLOBAL(x) pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER; + #define LTC_MUTEX_PROTO(x) extern pthread_mutex_t x; + #define LTC_MUTEX_TYPE(x) pthread_mutex_t x; + #define LTC_MUTEX_INIT(x) pthread_mutex_init(x, NULL); + #define LTC_MUTEX_LOCK(x) pthread_mutex_lock(x); + #define LTC_MUTEX_UNLOCK(x) pthread_mutex_unlock(x); + +#else + +/* default no functions */ + #define LTC_MUTEX_GLOBAL(x) + #define LTC_MUTEX_PROTO(x) + #define LTC_MUTEX_TYPE(x) + #define LTC_MUTEX_INIT(x) + #define LTC_MUTEX_LOCK(x) + #define LTC_MUTEX_UNLOCK(x) +#endif + +/* Debuggers */ + +/* define this if you use Valgrind, note: it CHANGES the way SOBER-128 and LTC_RC4 work (see the code) */ +/* #define LTC_VALGRIND */ +#endif + + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_custom.h,v $ */ +/* $Revision: 1.73 $ */ +/* $Date: 2007/05/12 14:37:41 $ */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* version */ +#define CRYPT 0x0117 +#define SCRYPT "1.17" + +/* max size of either a cipher/hash block or symmetric key [largest of the two] */ +#define MAXBLOCKSIZE 128 + +/* descriptor table size */ + +/* error codes [will be expanded in future releases] */ +enum { + CRYPT_OK=0, /* Result OK */ + CRYPT_ERROR, /* Generic Error */ + CRYPT_NOP, /* Not a failure but no operation was performed */ + + CRYPT_INVALID_KEYSIZE, /* Invalid key size given */ + CRYPT_INVALID_ROUNDS, /* Invalid number of rounds */ + CRYPT_FAIL_TESTVECTOR, /* Algorithm failed test vectors */ + + CRYPT_BUFFER_OVERFLOW, /* Not enough space for output */ + CRYPT_INVALID_PACKET, /* Invalid input packet given */ + + CRYPT_INVALID_PRNGSIZE, /* Invalid number of bits for a PRNG */ + CRYPT_ERROR_READPRNG, /* Could not read enough from PRNG */ + + CRYPT_INVALID_CIPHER, /* Invalid cipher specified */ + CRYPT_INVALID_HASH, /* Invalid hash specified */ + CRYPT_INVALID_PRNG, /* Invalid PRNG specified */ + + CRYPT_MEM, /* Out of memory */ + + CRYPT_PK_TYPE_MISMATCH, /* Not equivalent types of PK keys */ + CRYPT_PK_NOT_PRIVATE, /* Requires a private PK key */ + + CRYPT_INVALID_ARG, /* Generic invalid argument */ + CRYPT_FILE_NOTFOUND, /* File Not Found */ + + CRYPT_PK_INVALID_TYPE, /* Invalid type of PK key */ + CRYPT_PK_INVALID_SYSTEM, /* Invalid PK system specified */ + CRYPT_PK_DUP, /* Duplicate key already in key ring */ + CRYPT_PK_NOT_FOUND, /* Key not found in keyring */ + CRYPT_PK_INVALID_SIZE, /* Invalid size input for PK parameters */ + + CRYPT_INVALID_PRIME_SIZE, /* Invalid size of prime requested */ + CRYPT_PK_INVALID_PADDING /* Invalid padding on input */ +}; + +/* This is the build config file. + * + * With this you can setup what to inlcude/exclude automatically during any build. Just comment + * out the line that #define's the word for the thing you want to remove. phew! + */ + +#ifndef TOMCRYPT_CFG_H +#define TOMCRYPT_CFG_H + +#if defined(_WIN32) || defined(_MSC_VER) + #define LTC_CALL __cdecl +#else + #ifndef LTC_CALL + #define LTC_CALL + #endif +#endif + +#ifndef LTC_EXPORT + #define LTC_EXPORT +#endif + +/* certain platforms use macros for these, making the prototypes broken */ +#ifndef LTC_NO_PROTOTYPES + +/* you can change how memory allocation works ... */ +LTC_EXPORT void *LTC_CALL XMALLOC(size_t n); +LTC_EXPORT void *LTC_CALL XREALLOC(void *p, size_t n); +LTC_EXPORT void *LTC_CALL XCALLOC(size_t n, size_t s); +LTC_EXPORT void LTC_CALL XFREE(void *p); + +LTC_EXPORT void LTC_CALL XQSORT(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); + + +/* change the clock function too */ +LTC_EXPORT clock_t LTC_CALL XCLOCK(void); + +/* various other functions */ +LTC_EXPORT void *LTC_CALL XMEMCPY(void *dest, const void *src, size_t n); +LTC_EXPORT int LTC_CALL XMEMCMP(const void *s1, const void *s2, size_t n); +LTC_EXPORT void *LTC_CALL XMEMSET(void *s, int c, size_t n); + +LTC_EXPORT int LTC_CALL XSTRCMP(const char *s1, const char *s2); +#endif + +/* type of argument checking, 0=default, 1=fatal and 2=error+continue, 3=nothing */ +#ifndef ARGTYPE + #define ARGTYPE 0 +#endif + +/* Controls endianess and size of registers. Leave uncommented to get platform neutral [slower] code + * + * Note: in order to use the optimized macros your platform must support unaligned 32 and 64 bit read/writes. + * The x86 platforms allow this but some others [ARM for instance] do not. On those platforms you **MUST** + * use the portable [slower] macros. + */ + +/* detect x86-32 machines somewhat */ +#if !defined(__STRICT_ANSI__) && (defined(INTEL_CC) || (defined(_MSC_VER) && defined(WIN32)) || (defined(__GNUC__) && (defined(__DJGPP__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__i386__)))) + #define ENDIAN_LITTLE + #define ENDIAN_32BITWORD + #define LTC_FAST + #define LTC_FAST_TYPE unsigned long +#endif + +/* detects MIPS R5900 processors (PS2) */ +#if (defined(__R5900) || defined(R5900) || defined(__R5900__)) && (defined(_mips) || defined(__mips__) || defined(mips)) + #define ENDIAN_LITTLE + #define ENDIAN_64BITWORD +#endif + +/* detect amd64 */ +#if !defined(__STRICT_ANSI__) && defined(__x86_64__) + #define ENDIAN_LITTLE + #define ENDIAN_64BITWORD + #define LTC_FAST + #define LTC_FAST_TYPE unsigned long +#endif + +/* detect PPC32 */ +#if !defined(__STRICT_ANSI__) && defined(LTC_PPC32) + #define ENDIAN_BIG + #define ENDIAN_32BITWORD + #define LTC_FAST + #define LTC_FAST_TYPE unsigned long +#endif + +/* detect sparc and sparc64 */ +#if defined(__sparc__) + #define ENDIAN_BIG + #if defined(__arch64__) + #define ENDIAN_64BITWORD + #else + #define ENDIAN_32BITWORD + #endif +#endif + + +#ifdef LTC_NO_FAST + #ifdef LTC_FAST + #undef LTC_FAST + #endif +#endif + +/* No asm is a quick way to disable anything "not portable" */ +#ifdef LTC_NO_ASM + #undef ENDIAN_LITTLE + #undef ENDIAN_BIG + #undef ENDIAN_32BITWORD + #undef ENDIAN_64BITWORD + #undef LTC_FAST + #undef LTC_FAST_TYPE + #define LTC_NO_ROLC + #define LTC_NO_BSWAP +#endif + +/* #define ENDIAN_LITTLE */ +/* #define ENDIAN_BIG */ + +/* #define ENDIAN_32BITWORD */ +/* #define ENDIAN_64BITWORD */ + +#if (defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE)) && !(defined(ENDIAN_32BITWORD) || defined(ENDIAN_64BITWORD)) + #error You must specify a word size as well as endianess in tomcrypt_cfg.h +#endif + +#if !(defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE)) + #define ENDIAN_NEUTRAL +#endif +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_cfg.h,v $ */ +/* $Revision: 1.19 $ */ +/* $Date: 2006/12/04 02:19:48 $ */ + +/* fix for MSVC ...evil! */ +#ifdef _MSC_VER + #define CONST64(n) n ## ui64 +typedef unsigned __int64 ulong64; +#else + #define CONST64(n) n ## ULL +typedef unsigned long long ulong64; +#endif + +/* this is the "32-bit at least" data type + * Re-define it to suit your platform but it must be at least 32-bits + */ +#if defined(__x86_64__) || (defined(__sparc__) && defined(__arch64__)) +typedef unsigned ulong32; +#else +typedef unsigned long ulong32; +#endif + +/* ---- HELPER MACROS ---- */ +#ifdef ENDIAN_NEUTRAL + + #define STORE32L(x, y) \ + { (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \ + (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); } + + #define LOAD32L(x, y) \ + { x = ((unsigned long)((y)[3] & 255) << 24) | \ + ((unsigned long)((y)[2] & 255) << 16) | \ + ((unsigned long)((y)[1] & 255) << 8) | \ + ((unsigned long)((y)[0] & 255)); } + + #define STORE64L(x, y) \ + { (y)[7] = (unsigned char)(((x) >> 56) & 255); (y)[6] = (unsigned char)(((x) >> 48) & 255); \ + (y)[5] = (unsigned char)(((x) >> 40) & 255); (y)[4] = (unsigned char)(((x) >> 32) & 255); \ + (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \ + (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); } + + #define LOAD64L(x, y) \ + { x = (((ulong64)((y)[7] & 255)) << 56) | (((ulong64)((y)[6] & 255)) << 48) | \ + (((ulong64)((y)[5] & 255)) << 40) | (((ulong64)((y)[4] & 255)) << 32) | \ + (((ulong64)((y)[3] & 255)) << 24) | (((ulong64)((y)[2] & 255)) << 16) | \ + (((ulong64)((y)[1] & 255)) << 8) | (((ulong64)((y)[0] & 255))); } + + #define STORE32H(x, y) \ + { (y)[0] = (unsigned char)(((x) >> 24) & 255); (y)[1] = (unsigned char)(((x) >> 16) & 255); \ + (y)[2] = (unsigned char)(((x) >> 8) & 255); (y)[3] = (unsigned char)((x) & 255); } + + #define LOAD32H(x, y) \ + { x = ((unsigned long)((y)[0] & 255) << 24) | \ + ((unsigned long)((y)[1] & 255) << 16) | \ + ((unsigned long)((y)[2] & 255) << 8) | \ + ((unsigned long)((y)[3] & 255)); } + + #define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x) >> 56) & 255); (y)[1] = (unsigned char)(((x) >> 48) & 255); \ + (y)[2] = (unsigned char)(((x) >> 40) & 255); (y)[3] = (unsigned char)(((x) >> 32) & 255); \ + (y)[4] = (unsigned char)(((x) >> 24) & 255); (y)[5] = (unsigned char)(((x) >> 16) & 255); \ + (y)[6] = (unsigned char)(((x) >> 8) & 255); (y)[7] = (unsigned char)((x) & 255); } + + #define LOAD64H(x, y) \ + { x = (((ulong64)((y)[0] & 255)) << 56) | (((ulong64)((y)[1] & 255)) << 48) | \ + (((ulong64)((y)[2] & 255)) << 40) | (((ulong64)((y)[3] & 255)) << 32) | \ + (((ulong64)((y)[4] & 255)) << 24) | (((ulong64)((y)[5] & 255)) << 16) | \ + (((ulong64)((y)[6] & 255)) << 8) | (((ulong64)((y)[7] & 255))); } +#endif /* ENDIAN_NEUTRAL */ + +#ifdef ENDIAN_LITTLE + + #if !defined(LTC_NO_BSWAP) && (defined(INTEL_CC) || (defined(__GNUC__) && (defined(__DJGPP__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__i386__) || defined(__x86_64__)))) + + #define STORE32H(x, y) \ + asm __volatile__ ( \ + "bswapl %0 \n\t" \ + "movl %0,(%1)\n\t" \ + "bswapl %0 \n\t" \ + ::"r" (x), "r" (y)); + + #define LOAD32H(x, y) \ + asm __volatile__ ( \ + "movl (%1),%0\n\t" \ + "bswapl %0\n\t" \ + : "=r" (x) : "r" (y)); + + #else + + #define STORE32H(x, y) \ + { (y)[0] = (unsigned char)(((x) >> 24) & 255); (y)[1] = (unsigned char)(((x) >> 16) & 255); \ + (y)[2] = (unsigned char)(((x) >> 8) & 255); (y)[3] = (unsigned char)((x) & 255); } + + #define LOAD32H(x, y) \ + { x = ((unsigned long)((y)[0] & 255) << 24) | \ + ((unsigned long)((y)[1] & 255) << 16) | \ + ((unsigned long)((y)[2] & 255) << 8) | \ + ((unsigned long)((y)[3] & 255)); } + #endif + + +/* x86_64 processor */ + #if !defined(LTC_NO_BSWAP) && (defined(__GNUC__) && defined(__x86_64__)) + + #define STORE64H(x, y) \ + asm __volatile__ ( \ + "bswapq %0 \n\t" \ + "movq %0,(%1)\n\t" \ + "bswapq %0 \n\t" \ + ::"r" (x), "r" (y)); + + #define LOAD64H(x, y) \ + asm __volatile__ ( \ + "movq (%1),%0\n\t" \ + "bswapq %0\n\t" \ + : "=r" (x) : "r" (y)); + + #else + + #define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x) >> 56) & 255); (y)[1] = (unsigned char)(((x) >> 48) & 255); \ + (y)[2] = (unsigned char)(((x) >> 40) & 255); (y)[3] = (unsigned char)(((x) >> 32) & 255); \ + (y)[4] = (unsigned char)(((x) >> 24) & 255); (y)[5] = (unsigned char)(((x) >> 16) & 255); \ + (y)[6] = (unsigned char)(((x) >> 8) & 255); (y)[7] = (unsigned char)((x) & 255); } + + #define LOAD64H(x, y) \ + { x = (((ulong64)((y)[0] & 255)) << 56) | (((ulong64)((y)[1] & 255)) << 48) | \ + (((ulong64)((y)[2] & 255)) << 40) | (((ulong64)((y)[3] & 255)) << 32) | \ + (((ulong64)((y)[4] & 255)) << 24) | (((ulong64)((y)[5] & 255)) << 16) | \ + (((ulong64)((y)[6] & 255)) << 8) | (((ulong64)((y)[7] & 255))); } + #endif + + #ifdef ENDIAN_32BITWORD + + #define STORE32L(x, y) \ + { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + + #define LOAD32L(x, y) \ + XMEMCPY(&(x), y, 4); + + #define STORE64L(x, y) \ + { (y)[7] = (unsigned char)(((x) >> 56) & 255); (y)[6] = (unsigned char)(((x) >> 48) & 255); \ + (y)[5] = (unsigned char)(((x) >> 40) & 255); (y)[4] = (unsigned char)(((x) >> 32) & 255); \ + (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \ + (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); } + + #define LOAD64L(x, y) \ + { x = (((ulong64)((y)[7] & 255)) << 56) | (((ulong64)((y)[6] & 255)) << 48) | \ + (((ulong64)((y)[5] & 255)) << 40) | (((ulong64)((y)[4] & 255)) << 32) | \ + (((ulong64)((y)[3] & 255)) << 24) | (((ulong64)((y)[2] & 255)) << 16) | \ + (((ulong64)((y)[1] & 255)) << 8) | (((ulong64)((y)[0] & 255))); } + + #else /* 64-bit words then */ + + #define STORE32L(x, y) \ + { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + + #define LOAD32L(x, y) \ + { XMEMCPY(&(x), y, 4); x &= 0xFFFFFFFF; } + + #define STORE64L(x, y) \ + { ulong64 __t = (x); XMEMCPY(y, &__t, 8); } + + #define LOAD64L(x, y) \ + { XMEMCPY(&(x), y, 8); } + #endif /* ENDIAN_64BITWORD */ +#endif /* ENDIAN_LITTLE */ + +#ifdef ENDIAN_BIG + #define STORE32L(x, y) \ + { (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \ + (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); } + + #define LOAD32L(x, y) \ + { x = ((unsigned long)((y)[3] & 255) << 24) | \ + ((unsigned long)((y)[2] & 255) << 16) | \ + ((unsigned long)((y)[1] & 255) << 8) | \ + ((unsigned long)((y)[0] & 255)); } + + #define STORE64L(x, y) \ + { (y)[7] = (unsigned char)(((x) >> 56) & 255); (y)[6] = (unsigned char)(((x) >> 48) & 255); \ + (y)[5] = (unsigned char)(((x) >> 40) & 255); (y)[4] = (unsigned char)(((x) >> 32) & 255); \ + (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \ + (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); } + + #define LOAD64L(x, y) \ + { x = (((ulong64)((y)[7] & 255)) << 56) | (((ulong64)((y)[6] & 255)) << 48) | \ + (((ulong64)((y)[5] & 255)) << 40) | (((ulong64)((y)[4] & 255)) << 32) | \ + (((ulong64)((y)[3] & 255)) << 24) | (((ulong64)((y)[2] & 255)) << 16) | \ + (((ulong64)((y)[1] & 255)) << 8) | (((ulong64)((y)[0] & 255))); } + + #ifdef ENDIAN_32BITWORD + + #define STORE32H(x, y) \ + { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + + #define LOAD32H(x, y) \ + XMEMCPY(&(x), y, 4); + + #define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x) >> 56) & 255); (y)[1] = (unsigned char)(((x) >> 48) & 255); \ + (y)[2] = (unsigned char)(((x) >> 40) & 255); (y)[3] = (unsigned char)(((x) >> 32) & 255); \ + (y)[4] = (unsigned char)(((x) >> 24) & 255); (y)[5] = (unsigned char)(((x) >> 16) & 255); \ + (y)[6] = (unsigned char)(((x) >> 8) & 255); (y)[7] = (unsigned char)((x) & 255); } + + #define LOAD64H(x, y) \ + { x = (((ulong64)((y)[0] & 255)) << 56) | (((ulong64)((y)[1] & 255)) << 48) | \ + (((ulong64)((y)[2] & 255)) << 40) | (((ulong64)((y)[3] & 255)) << 32) | \ + (((ulong64)((y)[4] & 255)) << 24) | (((ulong64)((y)[5] & 255)) << 16) | \ + (((ulong64)((y)[6] & 255)) << 8) | (((ulong64)((y)[7] & 255))); } + + #else /* 64-bit words then */ + + #define STORE32H(x, y) \ + { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + + #define LOAD32H(x, y) \ + { XMEMCPY(&(x), y, 4); x &= 0xFFFFFFFF; } + + #define STORE64H(x, y) \ + { ulong64 __t = (x); XMEMCPY(y, &__t, 8); } + + #define LOAD64H(x, y) \ + { XMEMCPY(&(x), y, 8); } + #endif /* ENDIAN_64BITWORD */ +#endif /* ENDIAN_BIG */ + +#define BSWAP(x) \ + (((x >> 24) & 0x000000FFUL) | ((x << 24) & 0xFF000000UL) | \ + ((x >> 8) & 0x0000FF00UL) | ((x << 8) & 0x00FF0000UL)) + + +/* 32-bit Rotates */ +#if defined(_MSC_VER) + +/* instrinsic rotate */ + #include + #pragma intrinsic(_lrotr,_lrotl) + #define ROR(x, n) _lrotr(x, n) + #define ROL(x, n) _lrotl(x, n) + #define RORc(x, n) _lrotr(x, n) + #define ROLc(x, n) _lrotl(x, n) + +#elif !defined(__STRICT_ANSI__) && defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) && !defined(INTEL_CC) && !defined(LTC_NO_ASM) + +static inline unsigned ROL(unsigned word, int i) { + asm ("roll %%cl,%0" + : "=r" (word) + : "0" (word), "c" (i)); + return word; +} + +static inline unsigned ROR(unsigned word, int i) { + asm ("rorl %%cl,%0" + : "=r" (word) + : "0" (word), "c" (i)); + return word; +} + + #ifndef LTC_NO_ROLC + +static inline unsigned ROLc(unsigned word, const int i) { + asm ("roll %2,%0" + : "=r" (word) + : "0" (word), "I" (i)); + return word; +} + +static inline unsigned RORc(unsigned word, const int i) { + asm ("rorl %2,%0" + : "=r" (word) + : "0" (word), "I" (i)); + return word; +} + + #else + + #define ROLc ROL + #define RORc ROR + #endif + +#elif !defined(__STRICT_ANSI__) && defined(LTC_PPC32) + +static inline unsigned ROL(unsigned word, int i) { + asm ("rotlw %0,%0,%2" + : "=r" (word) + : "0" (word), "r" (i)); + return word; +} + +static inline unsigned ROR(unsigned word, int i) { + asm ("rotlw %0,%0,%2" + : "=r" (word) + : "0" (word), "r" (32 - i)); + return word; +} + + #ifndef LTC_NO_ROLC + +static inline unsigned ROLc(unsigned word, const int i) { + asm ("rotlwi %0,%0,%2" + : "=r" (word) + : "0" (word), "I" (i)); + return word; +} + +static inline unsigned RORc(unsigned word, const int i) { + asm ("rotrwi %0,%0,%2" + : "=r" (word) + : "0" (word), "I" (i)); + return word; +} + + #else + + #define ROLc ROL + #define RORc ROR + #endif + + +#else + +/* rotates the hard way */ + #define ROL(x, y) ((((unsigned long)(x) << (unsigned long)((y) & 31)) | (((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) + #define ROR(x, y) (((((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)((y) & 31)) | ((unsigned long)(x) << (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) + #define ROLc(x, y) ((((unsigned long)(x) << (unsigned long)((y) & 31)) | (((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) + #define RORc(x, y) (((((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)((y) & 31)) | ((unsigned long)(x) << (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) +#endif + + +/* 64-bit Rotates */ +#if !defined(__STRICT_ANSI__) && defined(__GNUC__) && defined(__x86_64__) && !defined(LTC_NO_ASM) + +static inline unsigned long ROL64(unsigned long word, int i) { + asm ("rolq %%cl,%0" + : "=r" (word) + : "0" (word), "c" (i)); + return word; +} + +static inline unsigned long ROR64(unsigned long word, int i) { + asm ("rorq %%cl,%0" + : "=r" (word) + : "0" (word), "c" (i)); + return word; +} + + #ifndef LTC_NO_ROLC + +static inline unsigned long ROL64c(unsigned long word, const int i) { + asm ("rolq %2,%0" + : "=r" (word) + : "0" (word), "J" (i)); + return word; +} + +static inline unsigned long ROR64c(unsigned long word, const int i) { + asm ("rorq %2,%0" + : "=r" (word) + : "0" (word), "J" (i)); + return word; +} + + #else /* LTC_NO_ROLC */ + + #define ROL64c ROL64 + #define ROR64c ROR64 + #endif + +#else /* Not x86_64 */ + + #define ROL64(x, y) \ + ((((x) << ((ulong64)(y) & 63)) | \ + (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((ulong64)64 - ((y) & 63)))) & CONST64(0xFFFFFFFFFFFFFFFF)) + + #define ROR64(x, y) \ + (((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((ulong64)(y) & CONST64(63))) | \ + ((x) << ((ulong64)(64 - ((y) & CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF)) + + #define ROL64c(x, y) \ + ((((x) << ((ulong64)(y) & 63)) | \ + (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((ulong64)64 - ((y) & 63)))) & CONST64(0xFFFFFFFFFFFFFFFF)) + + #define ROR64c(x, y) \ + (((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((ulong64)(y) & CONST64(63))) | \ + ((x) << ((ulong64)(64 - ((y) & CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF)) +#endif + +#ifndef MAX + #define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif + +#ifndef MIN + #define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +/* extract a byte portably */ +#ifdef _MSC_VER + #define extract_byte(x, n) ((unsigned char)((x) >> (8 * (n)))) +#else + #define extract_byte(x, n) (((x) >> (8 * (n))) & 255) +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_macros.h,v $ */ +/* $Revision: 1.15 $ */ +/* $Date: 2006/11/29 23:43:57 $ */ + +/* ---- SYMMETRIC KEY STUFF ----- + * + * We put each of the ciphers scheduled keys in their own structs then we put all of + * the key formats in one union. This makes the function prototypes easier to use. + */ +#ifdef LTC_BLOWFISH +struct blowfish_key { + ulong32 S[4][256]; + ulong32 K[18]; +}; +#endif + +#ifdef LTC_RC5 +struct rc5_key { + int rounds; + ulong32 K[50]; +}; +#endif + +#ifdef LTC_RC6 +struct rc6_key { + ulong32 K[44]; +}; +#endif + +#ifdef LTC_SAFERP +struct saferp_key { + unsigned char K[33][16]; + long rounds; +}; +#endif + +#ifdef LTC_RIJNDAEL +struct rijndael_key { + ulong32 eK[60], dK[60]; + int Nr; +}; +#endif + +#ifdef LTC_KSEED +struct kseed_key { + ulong32 K[32], dK[32]; +}; +#endif + +#ifdef LTC_KASUMI +struct kasumi_key { + ulong32 KLi1[8], KLi2[8], + KOi1[8], KOi2[8], KOi3[8], + KIi1[8], KIi2[8], KIi3[8]; +}; +#endif + +#ifdef LTC_XTEA +struct xtea_key { + unsigned long A[32], B[32]; +}; +#endif + +#ifdef LTC_TWOFISH + #ifndef LTC_TWOFISH_SMALL +struct twofish_key { + ulong32 S[4][256], K[40]; +}; + #else +struct twofish_key { + ulong32 K[40]; + unsigned char S[32], start; +}; + #endif +#endif + +#ifdef LTC_SAFER + #define LTC_SAFER_K64_DEFAULT_NOF_ROUNDS 6 + #define LTC_SAFER_K128_DEFAULT_NOF_ROUNDS 10 + #define LTC_SAFER_SK64_DEFAULT_NOF_ROUNDS 8 + #define LTC_SAFER_SK128_DEFAULT_NOF_ROUNDS 10 + #define LTC_SAFER_MAX_NOF_ROUNDS 13 + #define LTC_SAFER_BLOCK_LEN 8 + #define LTC_SAFER_KEY_LEN (1 + LTC_SAFER_BLOCK_LEN * (1 + 2 * LTC_SAFER_MAX_NOF_ROUNDS)) +typedef unsigned char safer_block_t[LTC_SAFER_BLOCK_LEN]; +typedef unsigned char safer_key_t[LTC_SAFER_KEY_LEN]; +struct safer_key { + safer_key_t key; +}; +#endif + +#ifdef LTC_RC2 +struct rc2_key { + unsigned xkey[64]; +}; +#endif + +#ifdef LTC_DES +struct des_key { + ulong32 ek[32], dk[32]; +}; + +struct des3_key { + ulong32 ek[3][32], dk[3][32]; +}; +#endif + +#ifdef LTC_CAST5 +struct cast5_key { + ulong32 K[32], keylen; +}; +#endif + +#ifdef LTC_NOEKEON +struct noekeon_key { + ulong32 K[4], dK[4]; +}; +#endif + +#ifdef LTC_SKIPJACK +struct skipjack_key { + unsigned char key[10]; +}; +#endif + +#ifdef LTC_KHAZAD +struct khazad_key { + ulong64 roundKeyEnc[8 + 1]; + ulong64 roundKeyDec[8 + 1]; +}; +#endif + +#ifdef LTC_ANUBIS +struct anubis_key { + int keyBits; + int R; + ulong32 roundKeyEnc[18 + 1][4]; + ulong32 roundKeyDec[18 + 1][4]; +}; +#endif + +#ifdef LTC_MULTI2 +struct multi2_key { + int N; + ulong32 uk[8]; +}; +#endif + +typedef union Symmetric_key { +#ifdef LTC_DES + struct des_key des; + struct des3_key des3; +#endif +#ifdef LTC_RC2 + struct rc2_key rc2; +#endif +#ifdef LTC_SAFER + struct safer_key safer; +#endif +#ifdef LTC_TWOFISH + struct twofish_key twofish; +#endif +#ifdef LTC_BLOWFISH + struct blowfish_key blowfish; +#endif +#ifdef LTC_RC5 + struct rc5_key rc5; +#endif +#ifdef LTC_RC6 + struct rc6_key rc6; +#endif +#ifdef LTC_SAFERP + struct saferp_key saferp; +#endif +#ifdef LTC_RIJNDAEL + struct rijndael_key rijndael; +#endif +#ifdef LTC_XTEA + struct xtea_key xtea; +#endif +#ifdef LTC_CAST5 + struct cast5_key cast5; +#endif +#ifdef LTC_NOEKEON + struct noekeon_key noekeon; +#endif +#ifdef LTC_SKIPJACK + struct skipjack_key skipjack; +#endif +#ifdef LTC_KHAZAD + struct khazad_key khazad; +#endif +#ifdef LTC_ANUBIS + struct anubis_key anubis; +#endif +#ifdef LTC_KSEED + struct kseed_key kseed; +#endif +#ifdef LTC_KASUMI + struct kasumi_key kasumi; +#endif +#ifdef LTC_MULTI2 + struct multi2_key multi2; +#endif + void *data; +} symmetric_key; + +#ifdef LTC_ECB_MODE +/** A block cipher ECB structure */ +typedef struct { + /** The index of the cipher chosen */ + int cipher, + /** The block size of the given cipher */ + blocklen; + /** The scheduled key */ + symmetric_key key; +} symmetric_ECB; +#endif + +#ifdef LTC_CFB_MODE +/** A block cipher CFB structure */ +typedef struct { + /** The index of the cipher chosen */ + int cipher, + /** The block size of the given cipher */ + blocklen, + /** The padding offset */ + padlen; + /** The current IV */ + unsigned char IV[MAXBLOCKSIZE], + /** The pad used to encrypt/decrypt */ + pad[MAXBLOCKSIZE]; + /** The scheduled key */ + symmetric_key key; +} symmetric_CFB; +#endif + +#ifdef LTC_OFB_MODE +/** A block cipher OFB structure */ +typedef struct { + /** The index of the cipher chosen */ + int cipher, + /** The block size of the given cipher */ + blocklen, + /** The padding offset */ + padlen; + /** The current IV */ + unsigned char IV[MAXBLOCKSIZE]; + /** The scheduled key */ + symmetric_key key; +} symmetric_OFB; +#endif + +#ifdef LTC_CBC_MODE +/** A block cipher CBC structure */ +typedef struct { + /** The index of the cipher chosen */ + int cipher, + /** The block size of the given cipher */ + blocklen; + /** The current IV */ + unsigned char IV[MAXBLOCKSIZE]; + /** The scheduled key */ + symmetric_key key; +} symmetric_CBC; +#endif + + +#ifdef LTC_CTR_MODE +/** A block cipher CTR structure */ +typedef struct { + /** The index of the cipher chosen */ + int cipher, + /** The block size of the given cipher */ + blocklen, + /** The padding offset */ + padlen, + /** The mode (endianess) of the CTR, 0==little, 1==big */ + mode, + /** counter width */ + ctrlen; + + /** The counter */ + unsigned char ctr[MAXBLOCKSIZE], + /** The pad used to encrypt/decrypt */ + pad[MAXBLOCKSIZE]; + /** The scheduled key */ + symmetric_key key; +} symmetric_CTR; +#endif + + +#ifdef LTC_LRW_MODE +/** A LRW structure */ +typedef struct { + /** The index of the cipher chosen (must be a 128-bit block cipher) */ + int cipher; + + /** The current IV */ + unsigned char IV[16], + + /** the tweak key */ + tweak[16], + + /** The current pad, it's the product of the first 15 bytes against the tweak key */ + pad[16]; + + /** The scheduled symmetric key */ + symmetric_key key; + + #ifdef LRW_TABLES + /** The pre-computed multiplication table */ + unsigned char PC[16][256][16]; + #endif +} symmetric_LRW; +#endif + +#ifdef LTC_F8_MODE +/** A block cipher F8 structure */ +typedef struct { + /** The index of the cipher chosen */ + int cipher, + /** The block size of the given cipher */ + blocklen, + /** The padding offset */ + padlen; + /** The current IV */ + unsigned char IV[MAXBLOCKSIZE], + MIV[MAXBLOCKSIZE]; + /** Current block count */ + ulong32 blockcnt; + /** The scheduled key */ + symmetric_key key; +} symmetric_F8; +#endif + + +/** cipher descriptor table, last entry has "name == NULL" to mark the end of table */ +extern struct ltc_cipher_descriptor { + /** name of cipher */ + char *name; + /** internal ID */ + unsigned char ID; + /** min keysize (octets) */ + int min_key_length, + /** max keysize (octets) */ + max_key_length, + /** block size (octets) */ + block_length, + /** default number of rounds */ + default_rounds; + + /** Setup the cipher + @param key The input symmetric key + @param keylen The length of the input key (octets) + @param num_rounds The requested number of rounds (0==default) + @param skey [out] The destination of the scheduled key + @return CRYPT_OK if successful + */ + int (*setup)(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); + + /** Encrypt a block + @param pt The plaintext + @param ct [out] The ciphertext + @param skey The scheduled key + @return CRYPT_OK if successful + */ + int (*ecb_encrypt)(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); + + /** Decrypt a block + @param ct The ciphertext + @param pt [out] The plaintext + @param skey The scheduled key + @return CRYPT_OK if successful + */ + int (*ecb_decrypt)(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); + + /** Test the block cipher + @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled + */ + int (*test)(void); + + /** Terminate the context + @param skey The scheduled key + */ + void (*done)(symmetric_key *skey); + + /** Determine a key size + @param keysize [in/out] The size of the key desired and the suggested size + @return CRYPT_OK if successful + */ + int (*keysize)(int *keysize); + +/** Accelerators **/ + + /** Accelerated ECB encryption + @param pt Plaintext + @param ct Ciphertext + @param blocks The number of complete blocks to process + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_ecb_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, symmetric_key *skey); + + /** Accelerated ECB decryption + @param pt Plaintext + @param ct Ciphertext + @param blocks The number of complete blocks to process + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_ecb_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, symmetric_key *skey); + + /** Accelerated CBC encryption + @param pt Plaintext + @param ct Ciphertext + @param blocks The number of complete blocks to process + @param IV The initial value (input/output) + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_cbc_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, symmetric_key *skey); + + /** Accelerated CBC decryption + @param pt Plaintext + @param ct Ciphertext + @param blocks The number of complete blocks to process + @param IV The initial value (input/output) + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_cbc_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, unsigned char *IV, symmetric_key *skey); + + /** Accelerated CTR encryption + @param pt Plaintext + @param ct Ciphertext + @param blocks The number of complete blocks to process + @param IV The initial value (input/output) + @param mode little or big endian counter (mode=0 or mode=1) + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_ctr_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, int mode, symmetric_key *skey); + + /** Accelerated LRW + @param pt Plaintext + @param ct Ciphertext + @param blocks The number of complete blocks to process + @param IV The initial value (input/output) + @param tweak The LRW tweak + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_lrw_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, const unsigned char *tweak, symmetric_key *skey); + + /** Accelerated LRW + @param ct Ciphertext + @param pt Plaintext + @param blocks The number of complete blocks to process + @param IV The initial value (input/output) + @param tweak The LRW tweak + @param skey The scheduled key context + @return CRYPT_OK if successful + */ + int (*accel_lrw_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, unsigned char *IV, const unsigned char *tweak, symmetric_key *skey); + + /** Accelerated CCM packet (one-shot) + @param key The secret key to use + @param keylen The length of the secret key (octets) + @param uskey A previously scheduled key [optional can be NULL] + @param nonce The session nonce [use once] + @param noncelen The length of the nonce + @param header The header for the session + @param headerlen The length of the header (octets) + @param pt [out] The plaintext + @param ptlen The length of the plaintext (octets) + @param ct [out] The ciphertext + @param tag [out] The destination tag + @param taglen [in/out] The max size and resulting size of the authentication tag + @param direction Encrypt or Decrypt direction (0 or 1) + @return CRYPT_OK if successful + */ + int (*accel_ccm_memory)( + const unsigned char *key, unsigned long keylen, + symmetric_key *uskey, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *header, unsigned long headerlen, + unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen, + int direction); + + /** Accelerated GCM packet (one shot) + @param key The secret key + @param keylen The length of the secret key + @param IV The initial vector + @param IVlen The length of the initial vector + @param adata The additional authentication data (header) + @param adatalen The length of the adata + @param pt The plaintext + @param ptlen The length of the plaintext (ciphertext length is the same) + @param ct The ciphertext + @param tag [out] The MAC tag + @param taglen [in/out] The MAC tag length + @param direction Encrypt or Decrypt mode (GCM_ENCRYPT or GCM_DECRYPT) + @return CRYPT_OK on success + */ + int (*accel_gcm_memory)( + const unsigned char *key, unsigned long keylen, + const unsigned char *IV, unsigned long IVlen, + const unsigned char *adata, unsigned long adatalen, + unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen, + int direction); + + /** Accelerated one shot LTC_OMAC + @param key The secret key + @param keylen The key length (octets) + @param in The message + @param inlen Length of message (octets) + @param out [out] Destination for tag + @param outlen [in/out] Initial and final size of out + @return CRYPT_OK on success + */ + int (*omac_memory)( + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); + + /** Accelerated one shot XCBC + @param key The secret key + @param keylen The key length (octets) + @param in The message + @param inlen Length of message (octets) + @param out [out] Destination for tag + @param outlen [in/out] Initial and final size of out + @return CRYPT_OK on success + */ + int (*xcbc_memory)( + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); + + /** Accelerated one shot F9 + @param key The secret key + @param keylen The key length (octets) + @param in The message + @param inlen Length of message (octets) + @param out [out] Destination for tag + @param outlen [in/out] Initial and final size of out + @return CRYPT_OK on success + @remark Requires manual padding + */ + int (*f9_memory)( + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +} cipher_descriptor[]; + +#ifdef LTC_BLOWFISH +int blowfish_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int blowfish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int blowfish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int blowfish_test(void); +void blowfish_done(symmetric_key *skey); +int blowfish_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor blowfish_desc; +#endif + +#ifdef LTC_RC5 +int rc5_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rc5_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int rc5_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int rc5_test(void); +void rc5_done(symmetric_key *skey); +int rc5_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor rc5_desc; +#endif + +#ifdef LTC_RC6 +int rc6_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rc6_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int rc6_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int rc6_test(void); +void rc6_done(symmetric_key *skey); +int rc6_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor rc6_desc; +#endif + +#ifdef LTC_RC2 +int rc2_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rc2_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int rc2_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int rc2_test(void); +void rc2_done(symmetric_key *skey); +int rc2_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor rc2_desc; +#endif + +#ifdef LTC_SAFERP +int saferp_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int saferp_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int saferp_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int saferp_test(void); +void saferp_done(symmetric_key *skey); +int saferp_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor saferp_desc; +#endif + +#ifdef LTC_SAFER +int safer_k64_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int safer_sk64_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int safer_k128_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int safer_sk128_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int safer_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *key); +int safer_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *key); +int safer_k64_test(void); +int safer_sk64_test(void); +int safer_sk128_test(void); +void safer_done(symmetric_key *skey); +int safer_64_keysize(int *keysize); +int safer_128_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor safer_k64_desc, safer_k128_desc, safer_sk64_desc, safer_sk128_desc; +#endif + +#ifdef LTC_RIJNDAEL + +/* make aes an alias */ + #define aes_setup rijndael_setup + #define aes_ecb_encrypt rijndael_ecb_encrypt + #define aes_ecb_decrypt rijndael_ecb_decrypt + #define aes_test rijndael_test + #define aes_done rijndael_done + #define aes_keysize rijndael_keysize + + #define aes_enc_setup rijndael_enc_setup + #define aes_enc_ecb_encrypt rijndael_enc_ecb_encrypt + #define aes_enc_keysize rijndael_enc_keysize + +int rijndael_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rijndael_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int rijndael_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int rijndael_test(void); +void rijndael_done(symmetric_key *skey); +int rijndael_keysize(int *keysize); +int rijndael_enc_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rijndael_enc_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +void rijndael_enc_done(symmetric_key *skey); +int rijndael_enc_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor rijndael_desc, aes_desc; +extern const struct ltc_cipher_descriptor rijndael_enc_desc, aes_enc_desc; +#endif + +#ifdef LTC_XTEA +int xtea_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int xtea_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int xtea_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int xtea_test(void); +void xtea_done(symmetric_key *skey); +int xtea_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor xtea_desc; +#endif + +#ifdef LTC_TWOFISH +int twofish_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int twofish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int twofish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int twofish_test(void); +void twofish_done(symmetric_key *skey); +int twofish_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor twofish_desc; +#endif + +#ifdef LTC_DES +int des_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int des_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int des_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int des_test(void); +void des_done(symmetric_key *skey); +int des_keysize(int *keysize); +int des3_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int des3_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int des3_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int des3_test(void); +void des3_done(symmetric_key *skey); +int des3_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor des_desc, des3_desc; +#endif + +#ifdef LTC_CAST5 +int cast5_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int cast5_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int cast5_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int cast5_test(void); +void cast5_done(symmetric_key *skey); +int cast5_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor cast5_desc; +#endif + +#ifdef LTC_NOEKEON +int noekeon_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int noekeon_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int noekeon_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int noekeon_test(void); +void noekeon_done(symmetric_key *skey); +int noekeon_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor noekeon_desc; +#endif + +#ifdef LTC_SKIPJACK +int skipjack_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int skipjack_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int skipjack_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int skipjack_test(void); +void skipjack_done(symmetric_key *skey); +int skipjack_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor skipjack_desc; +#endif + +#ifdef LTC_KHAZAD +int khazad_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int khazad_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int khazad_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int khazad_test(void); +void khazad_done(symmetric_key *skey); +int khazad_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor khazad_desc; +#endif + +#ifdef LTC_ANUBIS +int anubis_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int anubis_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int anubis_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int anubis_test(void); +void anubis_done(symmetric_key *skey); +int anubis_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor anubis_desc; +#endif + +#ifdef LTC_KSEED +int kseed_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int kseed_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int kseed_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int kseed_test(void); +void kseed_done(symmetric_key *skey); +int kseed_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor kseed_desc; +#endif + +#ifdef LTC_KASUMI +int kasumi_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int kasumi_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int kasumi_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int kasumi_test(void); +void kasumi_done(symmetric_key *skey); +int kasumi_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor kasumi_desc; +#endif + + +#ifdef LTC_MULTI2 +int multi2_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int multi2_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int multi2_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int multi2_test(void); +void multi2_done(symmetric_key *skey); +int multi2_keysize(int *keysize); + +extern const struct ltc_cipher_descriptor multi2_desc; +#endif + +#ifdef LTC_ECB_MODE +int ecb_start(int cipher, const unsigned char *key, + int keylen, int num_rounds, symmetric_ECB *ecb); +int ecb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_ECB *ecb); +int ecb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_ECB *ecb); +int ecb_done(symmetric_ECB *ecb); +#endif + +#ifdef LTC_CFB_MODE +int cfb_start(int cipher, const unsigned char *IV, const unsigned char *key, + int keylen, int num_rounds, symmetric_CFB *cfb); +int cfb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CFB *cfb); +int cfb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CFB *cfb); +int cfb_getiv(unsigned char *IV, unsigned long *len, symmetric_CFB *cfb); +int cfb_setiv(const unsigned char *IV, unsigned long len, symmetric_CFB *cfb); +int cfb_done(symmetric_CFB *cfb); +#endif + +#ifdef LTC_OFB_MODE +int ofb_start(int cipher, const unsigned char *IV, const unsigned char *key, + int keylen, int num_rounds, symmetric_OFB *ofb); +int ofb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_OFB *ofb); +int ofb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_OFB *ofb); +int ofb_getiv(unsigned char *IV, unsigned long *len, symmetric_OFB *ofb); +int ofb_setiv(const unsigned char *IV, unsigned long len, symmetric_OFB *ofb); +int ofb_done(symmetric_OFB *ofb); +#endif + +#ifdef LTC_CBC_MODE +int cbc_start(int cipher, const unsigned char *IV, const unsigned char *key, + int keylen, int num_rounds, symmetric_CBC *cbc); +int cbc_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CBC *cbc); +int cbc_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CBC *cbc); +int cbc_getiv(unsigned char *IV, unsigned long *len, symmetric_CBC *cbc); +int cbc_setiv(const unsigned char *IV, unsigned long len, symmetric_CBC *cbc); +int cbc_done(symmetric_CBC *cbc); +#endif + +#ifdef LTC_CTR_MODE + + #define CTR_COUNTER_LITTLE_ENDIAN 0x0000 + #define CTR_COUNTER_BIG_ENDIAN 0x1000 + #define LTC_CTR_RFC3686 0x2000 + +int ctr_start(int cipher, + const unsigned char *IV, + const unsigned char *key, int keylen, + int num_rounds, int ctr_mode, + symmetric_CTR *ctr); +int ctr_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CTR *ctr); +int ctr_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CTR *ctr); +int ctr_getiv(unsigned char *IV, unsigned long *len, symmetric_CTR *ctr); +int ctr_setiv(const unsigned char *IV, unsigned long len, symmetric_CTR *ctr); +int ctr_done(symmetric_CTR *ctr); +int ctr_test(void); +#endif + +#ifdef LTC_LRW_MODE + + #define LRW_ENCRYPT 0 + #define LRW_DECRYPT 1 + +int lrw_start(int cipher, + const unsigned char *IV, + const unsigned char *key, int keylen, + const unsigned char *tweak, + int num_rounds, + symmetric_LRW *lrw); +int lrw_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_LRW *lrw); +int lrw_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_LRW *lrw); +int lrw_getiv(unsigned char *IV, unsigned long *len, symmetric_LRW *lrw); +int lrw_setiv(const unsigned char *IV, unsigned long len, symmetric_LRW *lrw); +int lrw_done(symmetric_LRW *lrw); +int lrw_test(void); + +/* don't call */ +int lrw_process(const unsigned char *pt, unsigned char *ct, unsigned long len, int mode, symmetric_LRW *lrw); +#endif + +#ifdef LTC_F8_MODE +int f8_start(int cipher, const unsigned char *IV, + const unsigned char *key, int keylen, + const unsigned char *salt_key, int skeylen, + int num_rounds, symmetric_F8 *f8); +int f8_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_F8 *f8); +int f8_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_F8 *f8); +int f8_getiv(unsigned char *IV, unsigned long *len, symmetric_F8 *f8); +int f8_setiv(const unsigned char *IV, unsigned long len, symmetric_F8 *f8); +int f8_done(symmetric_F8 *f8); +int f8_test_mode(void); +#endif + +#ifdef LTC_XTS_MODE +typedef struct { + symmetric_key key1, key2; + int cipher; +} symmetric_xts; + +int xts_start(int cipher, + const unsigned char *key1, + const unsigned char *key2, + unsigned long keylen, + int num_rounds, + symmetric_xts *xts); + +int xts_encrypt( + const unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + const unsigned char *tweak, + symmetric_xts *xts); +int xts_decrypt( + const unsigned char *ct, unsigned long ptlen, + unsigned char *pt, + const unsigned char *tweak, + symmetric_xts *xts); + +void xts_done(symmetric_xts *xts); +int xts_test(void); +void xts_mult_x(unsigned char *I); +#endif + +int find_cipher(const char *name); +int find_cipher_any(const char *name, int blocklen, int keylen); +int find_cipher_id(unsigned char ID); +int register_cipher(const struct ltc_cipher_descriptor *cipher); +int unregister_cipher(const struct ltc_cipher_descriptor *cipher); +int cipher_is_valid(int idx); + +LTC_MUTEX_PROTO(ltc_cipher_mutex) + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_cipher.h,v $ */ +/* $Revision: 1.54 $ */ +/* $Date: 2007/05/12 14:37:41 $ */ + +#define LTC_SHA1 +/* ---- HASH FUNCTIONS ---- */ +#ifdef LTC_SHA512 +struct sha512_state { + ulong64 length, state[8]; + unsigned long curlen; + unsigned char buf[128]; +}; +#endif + +#ifdef LTC_SHA256 +struct sha256_state { + ulong64 length; + ulong32 state[8], curlen; + unsigned char buf[64]; +}; +#endif + +#ifdef LTC_SHA1 +struct sha1_state { + ulong64 length; + ulong32 state[5], curlen; + unsigned char buf[64]; +}; +#endif + +#ifdef LTC_MD5 +struct md5_state { + ulong64 length; + ulong32 state[4], curlen; + unsigned char buf[64]; +}; +#endif + +#ifdef LTC_MD4 +struct md4_state { + ulong64 length; + ulong32 state[4], curlen; + unsigned char buf[64]; +}; +#endif + +#ifdef LTC_TIGER +struct tiger_state { + ulong64 state[3], length; + unsigned long curlen; + unsigned char buf[64]; +}; +#endif + +#ifdef LTC_MD2 +struct md2_state { + unsigned char chksum[16], X[48], buf[16]; + unsigned long curlen; +}; +#endif + +#ifdef LTC_RIPEMD128 +struct rmd128_state { + ulong64 length; + unsigned char buf[64]; + ulong32 curlen, state[4]; +}; +#endif + +#ifdef LTC_RIPEMD160 +struct rmd160_state { + ulong64 length; + unsigned char buf[64]; + ulong32 curlen, state[5]; +}; +#endif + +#ifdef LTC_RIPEMD256 +struct rmd256_state { + ulong64 length; + unsigned char buf[64]; + ulong32 curlen, state[8]; +}; +#endif + +#ifdef LTC_RIPEMD320 +struct rmd320_state { + ulong64 length; + unsigned char buf[64]; + ulong32 curlen, state[10]; +}; +#endif + +#ifdef LTC_WHIRLPOOL +struct whirlpool_state { + ulong64 length, state[8]; + unsigned char buf[64]; + ulong32 curlen; +}; +#endif + +#ifdef LTC_CHC_HASH +struct chc_state { + ulong64 length; + unsigned char state[MAXBLOCKSIZE], buf[MAXBLOCKSIZE]; + ulong32 curlen; +}; +#endif + +typedef union Hash_state { + char dummy[1]; +#ifdef LTC_CHC_HASH + struct chc_state chc; +#endif +#ifdef LTC_WHIRLPOOL + struct whirlpool_state whirlpool; +#endif +#ifdef LTC_SHA512 + struct sha512_state sha512; +#endif +#ifdef LTC_SHA256 + struct sha256_state sha256; +#endif +#ifdef LTC_SHA1 + struct sha1_state sha1; +#endif +#ifdef LTC_MD5 + struct md5_state md5; +#endif +#ifdef LTC_MD4 + struct md4_state md4; +#endif +#ifdef LTC_MD2 + struct md2_state md2; +#endif +#ifdef LTC_TIGER + struct tiger_state tiger; +#endif +#ifdef LTC_RIPEMD128 + struct rmd128_state rmd128; +#endif +#ifdef LTC_RIPEMD160 + struct rmd160_state rmd160; +#endif +#ifdef LTC_RIPEMD256 + struct rmd256_state rmd256; +#endif +#ifdef LTC_RIPEMD320 + struct rmd320_state rmd320; +#endif + void *data; +} hash_state; + +/** hash descriptor */ +extern struct ltc_hash_descriptor { + /** name of hash */ + char *name; + /** internal ID */ + unsigned char ID; + /** Size of digest in octets */ + unsigned long hashsize; + /** Input block size in octets */ + unsigned long blocksize; + /** ASN.1 OID */ + unsigned long OID[16]; + /** Length of DER encoding */ + unsigned long OIDlen; + + /** Init a hash state + @param hash The hash to initialize + @return CRYPT_OK if successful + */ + int (*init)(hash_state *hash); + + /** Process a block of data + @param hash The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful + */ + int (*process)(hash_state *hash, const unsigned char *in, unsigned long inlen); + + /** Produce the digest and store it + @param hash The hash state + @param out [out] The destination of the digest + @return CRYPT_OK if successful + */ + int (*done)(hash_state *hash, unsigned char *out); + + /** Self-test + @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled + */ + int (*test)(void); + + /* accelerated hmac callback: if you need to-do multiple packets just use the generic hmac_memory and provide a hash callback */ + int (*hmac_block)(const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +} hash_descriptor[]; + +#ifdef LTC_CHC_HASH +int chc_register(int cipher); +int chc_init(hash_state *md); +int chc_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int chc_done(hash_state *md, unsigned char *hash); +int chc_test(void); + +extern const struct ltc_hash_descriptor chc_desc; +#endif + +#ifdef LTC_WHIRLPOOL +int whirlpool_init(hash_state *md); +int whirlpool_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int whirlpool_done(hash_state *md, unsigned char *hash); +int whirlpool_test(void); + +extern const struct ltc_hash_descriptor whirlpool_desc; +#endif + +#ifdef LTC_SHA512 +int sha512_init(hash_state *md); +int sha512_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int sha512_done(hash_state *md, unsigned char *hash); +int sha512_test(void); + +extern const struct ltc_hash_descriptor sha512_desc; +#endif + +#ifdef LTC_SHA384 + #ifndef LTC_SHA512 + #error LTC_SHA512 is required for LTC_SHA384 + #endif +int sha384_init(hash_state *md); + + #define sha384_process sha512_process +int sha384_done(hash_state *md, unsigned char *hash); +int sha384_test(void); + +extern const struct ltc_hash_descriptor sha384_desc; +#endif + +#ifdef LTC_SHA256 +int sha256_init(hash_state *md); +int sha256_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int sha256_done(hash_state *md, unsigned char *hash); +int sha256_test(void); + +extern const struct ltc_hash_descriptor sha256_desc; + + #ifdef LTC_SHA224 + #ifndef LTC_SHA256 + #error LTC_SHA256 is required for LTC_SHA224 + #endif +int sha224_init(hash_state *md); + + #define sha224_process sha256_process +int sha224_done(hash_state *md, unsigned char *hash); +int sha224_test(void); + +extern const struct ltc_hash_descriptor sha224_desc; + #endif +#endif + +#ifdef LTC_SHA1 +int sha1_init(hash_state *md); +int sha1_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int sha1_done(hash_state *md, unsigned char *hash); +int sha1_test(void); + +extern const struct ltc_hash_descriptor sha1_desc; +#endif + +#ifdef LTC_MD5 +int md5_init(hash_state *md); +int md5_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int md5_done(hash_state *md, unsigned char *hash); +int md5_test(void); + +extern const struct ltc_hash_descriptor md5_desc; +#endif + +#ifdef LTC_MD4 +int md4_init(hash_state *md); +int md4_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int md4_done(hash_state *md, unsigned char *hash); +int md4_test(void); + +extern const struct ltc_hash_descriptor md4_desc; +#endif + +#ifdef LTC_MD2 +int md2_init(hash_state *md); +int md2_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int md2_done(hash_state *md, unsigned char *hash); +int md2_test(void); + +extern const struct ltc_hash_descriptor md2_desc; +#endif + +#ifdef LTC_TIGER +int tiger_init(hash_state *md); +int tiger_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int tiger_done(hash_state *md, unsigned char *hash); +int tiger_test(void); + +extern const struct ltc_hash_descriptor tiger_desc; +#endif + +#ifdef LTC_RIPEMD128 +int rmd128_init(hash_state *md); +int rmd128_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int rmd128_done(hash_state *md, unsigned char *hash); +int rmd128_test(void); + +extern const struct ltc_hash_descriptor rmd128_desc; +#endif + +#ifdef LTC_RIPEMD160 +int rmd160_init(hash_state *md); +int rmd160_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int rmd160_done(hash_state *md, unsigned char *hash); +int rmd160_test(void); + +extern const struct ltc_hash_descriptor rmd160_desc; +#endif + +#ifdef LTC_RIPEMD256 +int rmd256_init(hash_state *md); +int rmd256_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int rmd256_done(hash_state *md, unsigned char *hash); +int rmd256_test(void); + +extern const struct ltc_hash_descriptor rmd256_desc; +#endif + +#ifdef LTC_RIPEMD320 +int rmd320_init(hash_state *md); +int rmd320_process(hash_state *md, const unsigned char *in, unsigned long inlen); +int rmd320_done(hash_state *md, unsigned char *hash); +int rmd320_test(void); + +extern const struct ltc_hash_descriptor rmd320_desc; +#endif + + +int find_hash(const char *name); +int find_hash_id(unsigned char ID); +int find_hash_oid(const unsigned long *ID, unsigned long IDlen); +int find_hash_any(const char *name, int digestlen); +int register_hash(const struct ltc_hash_descriptor *hash); +int unregister_hash(const struct ltc_hash_descriptor *hash); +int hash_is_valid(int idx); + +LTC_MUTEX_PROTO(ltc_hash_mutex) + +int hash_memory(int hash, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int hash_memory_multi(int hash, unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...); +int hash_filehandle(int hash, FILE *in, unsigned char *out, unsigned long *outlen); +int hash_file(int hash, const char *fname, unsigned char *out, unsigned long *outlen); + +/* a simple macro for making hash "process" functions */ +#define HASH_PROCESS(func_name, compress_name, state_var, block_size) \ + int func_name(hash_state * md, const unsigned char *in, unsigned long inlen) \ + { \ + unsigned long n; \ + int err; \ + LTC_ARGCHK(md != NULL); \ + LTC_ARGCHK(in != NULL); \ + if (md->state_var.curlen > sizeof(md->state_var.buf)) { \ + return CRYPT_INVALID_ARG; \ + } \ + while (inlen > 0) { \ + if (md->state_var.curlen == 0 && inlen >= block_size) { \ + if ((err = compress_name(md, (unsigned char *)in)) != CRYPT_OK) { \ + return err; \ + } \ + md->state_var.length += block_size * 8; \ + in += block_size; \ + inlen -= block_size; \ + } else { \ + n = MIN(inlen, (block_size - md->state_var.curlen)); \ + memcpy(md->state_var.buf + md->state_var.curlen, in, (size_t)n); \ + md->state_var.curlen += n; \ + in += n; \ + inlen -= n; \ + if (md->state_var.curlen == block_size) { \ + if ((err = compress_name(md, md->state_var.buf)) != CRYPT_OK) { \ + return err; \ + } \ + md->state_var.length += 8 * block_size; \ + md->state_var.curlen = 0; \ + } \ + } \ + } \ + return CRYPT_OK; \ + } + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_hash.h,v $ */ +/* $Revision: 1.22 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + +#ifdef LTC_HMAC +typedef struct Hmac_state { + hash_state md; + int hash; + hash_state hashstate; + unsigned char *key; +} hmac_state; + +int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, unsigned long keylen); +int hmac_process(hmac_state *hmac, const unsigned char *in, unsigned long inlen); +int hmac_done(hmac_state *hmac, unsigned char *out, unsigned long *outlen); +int hmac_test(void); +int hmac_memory(int hash, + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int hmac_memory_multi(int hash, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...); +int hmac_file(int hash, const char *fname, const unsigned char *key, + unsigned long keylen, + unsigned char *dst, unsigned long *dstlen); +#endif + +#ifdef LTC_OMAC + +typedef struct { + int cipher_idx, + buflen, + blklen; + unsigned char block[MAXBLOCKSIZE], + prev[MAXBLOCKSIZE], + Lu[2][MAXBLOCKSIZE]; + symmetric_key key; +} omac_state; + +int omac_init(omac_state *omac, int cipher, const unsigned char *key, unsigned long keylen); +int omac_process(omac_state *omac, const unsigned char *in, unsigned long inlen); +int omac_done(omac_state *omac, unsigned char *out, unsigned long *outlen); +int omac_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int omac_memory_multi(int cipher, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...); +int omac_file(int cipher, + const unsigned char *key, unsigned long keylen, + const char *filename, + unsigned char *out, unsigned long *outlen); +int omac_test(void); +#endif /* LTC_OMAC */ + +#ifdef LTC_PMAC + +typedef struct { + unsigned char Ls[32][MAXBLOCKSIZE], /* L shifted by i bits to the left */ + Li[MAXBLOCKSIZE], /* value of Li [current value, we calc from previous recall] */ + Lr[MAXBLOCKSIZE], /* L * x^-1 */ + block[MAXBLOCKSIZE], /* currently accumulated block */ + checksum[MAXBLOCKSIZE]; /* current checksum */ + + symmetric_key key; /* scheduled key for cipher */ + unsigned long block_index; /* index # for current block */ + int cipher_idx, /* cipher idx */ + block_len, /* length of block */ + buflen; /* number of bytes in the buffer */ +} pmac_state; + +int pmac_init(pmac_state *pmac, int cipher, const unsigned char *key, unsigned long keylen); +int pmac_process(pmac_state *pmac, const unsigned char *in, unsigned long inlen); +int pmac_done(pmac_state *pmac, unsigned char *out, unsigned long *outlen); + +int pmac_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *msg, unsigned long msglen, + unsigned char *out, unsigned long *outlen); + +int pmac_memory_multi(int cipher, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...); + +int pmac_file(int cipher, + const unsigned char *key, unsigned long keylen, + const char *filename, + unsigned char *out, unsigned long *outlen); + +int pmac_test(void); + +/* internal functions */ +int pmac_ntz(unsigned long x); +void pmac_shift_xor(pmac_state *pmac); +#endif /* PMAC */ + +#ifdef LTC_EAX_MODE + + #if !(defined(LTC_OMAC) && defined(LTC_CTR_MODE)) + #error LTC_EAX_MODE requires LTC_OMAC and CTR + #endif + +typedef struct { + unsigned char N[MAXBLOCKSIZE]; + symmetric_CTR ctr; + omac_state headeromac, ctomac; +} eax_state; + +int eax_init(eax_state *eax, int cipher, const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *header, unsigned long headerlen); + +int eax_encrypt(eax_state *eax, const unsigned char *pt, unsigned char *ct, unsigned long length); +int eax_decrypt(eax_state *eax, const unsigned char *ct, unsigned char *pt, unsigned long length); +int eax_addheader(eax_state *eax, const unsigned char *header, unsigned long length); +int eax_done(eax_state *eax, unsigned char *tag, unsigned long *taglen); + +int eax_encrypt_authenticate_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *header, unsigned long headerlen, + const unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen); + +int eax_decrypt_verify_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *header, unsigned long headerlen, + const unsigned char *ct, unsigned long ctlen, + unsigned char *pt, + unsigned char *tag, unsigned long taglen, + int *stat); + +int eax_test(void); +#endif /* EAX MODE */ + +#ifdef LTC_OCB_MODE +typedef struct { + unsigned char L[MAXBLOCKSIZE], /* L value */ + Ls[32][MAXBLOCKSIZE], /* L shifted by i bits to the left */ + Li[MAXBLOCKSIZE], /* value of Li [current value, we calc from previous recall] */ + Lr[MAXBLOCKSIZE], /* L * x^-1 */ + R[MAXBLOCKSIZE], /* R value */ + checksum[MAXBLOCKSIZE]; /* current checksum */ + + symmetric_key key; /* scheduled key for cipher */ + unsigned long block_index; /* index # for current block */ + int cipher, /* cipher idx */ + block_len; /* length of block */ +} ocb_state; + +int ocb_init(ocb_state *ocb, int cipher, + const unsigned char *key, unsigned long keylen, const unsigned char *nonce); + +int ocb_encrypt(ocb_state *ocb, const unsigned char *pt, unsigned char *ct); +int ocb_decrypt(ocb_state *ocb, const unsigned char *ct, unsigned char *pt); + +int ocb_done_encrypt(ocb_state *ocb, + const unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen); + +int ocb_done_decrypt(ocb_state *ocb, + const unsigned char *ct, unsigned long ctlen, + unsigned char *pt, + const unsigned char *tag, unsigned long taglen, int *stat); + +int ocb_encrypt_authenticate_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, + const unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen); + +int ocb_decrypt_verify_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, + const unsigned char *ct, unsigned long ctlen, + unsigned char *pt, + const unsigned char *tag, unsigned long taglen, + int *stat); + +int ocb_test(void); + +/* internal functions */ +void ocb_shift_xor(ocb_state *ocb, unsigned char *Z); +int ocb_ntz(unsigned long x); +int s_ocb_done(ocb_state *ocb, const unsigned char *pt, unsigned long ptlen, + unsigned char *ct, unsigned char *tag, unsigned long *taglen, int mode); +#endif /* LTC_OCB_MODE */ + +#ifdef LTC_CCM_MODE + + #define CCM_ENCRYPT 0 + #define CCM_DECRYPT 1 + +int ccm_memory(int cipher, + const unsigned char *key, unsigned long keylen, + symmetric_key *uskey, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *header, unsigned long headerlen, + unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen, + int direction); + +int ccm_test(void); +#endif /* LTC_CCM_MODE */ + +#if defined(LRW_MODE) || defined(LTC_GCM_MODE) +void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c); +#endif + + +/* table shared between GCM and LRW */ +#if defined(LTC_GCM_TABLES) || defined(LRW_TABLES) || ((defined(LTC_GCM_MODE) || defined(LTC_GCM_MODE)) && defined(LTC_FAST)) +extern const unsigned char gcm_shift_table[]; +#endif + +#ifdef LTC_GCM_MODE + + #define GCM_ENCRYPT 0 + #define GCM_DECRYPT 1 + + #define LTC_GCM_MODE_IV 0 + #define LTC_GCM_MODE_AAD 1 + #define LTC_GCM_MODE_TEXT 2 + +typedef struct { + symmetric_key K; + unsigned char H[16], /* multiplier */ + X[16], /* accumulator */ + Y[16], /* counter */ + Y_0[16], /* initial counter */ + buf[16]; /* buffer for stuff */ + + int cipher, /* which cipher */ + ivmode, /* Which mode is the IV in? */ + mode, /* mode the GCM code is in */ + buflen; /* length of data in buf */ + + ulong64 totlen, /* 64-bit counter used for IV and AAD */ + pttotlen; /* 64-bit counter for the PT */ + + #ifdef LTC_GCM_TABLES + unsigned char PC[16][256][16] /* 16 tables of 8x128 */ + #ifdef LTC_GCM_TABLES_SSE2 + __attribute__ ((aligned(16))) + #endif + ; + #endif +} gcm_state; + +void gcm_mult_h(gcm_state *gcm, unsigned char *I); + +int gcm_init(gcm_state *gcm, int cipher, + const unsigned char *key, int keylen); + +int gcm_reset(gcm_state *gcm); + +int gcm_add_iv(gcm_state *gcm, + const unsigned char *IV, unsigned long IVlen); + +int gcm_add_aad(gcm_state *gcm, + const unsigned char *adata, unsigned long adatalen); + +int gcm_process(gcm_state *gcm, + unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + int direction); + +int gcm_done(gcm_state *gcm, + unsigned char *tag, unsigned long *taglen); + +int gcm_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *IV, unsigned long IVlen, + const unsigned char *adata, unsigned long adatalen, + unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + unsigned char *tag, unsigned long *taglen, + int direction); +int gcm_test(void); +#endif /* LTC_GCM_MODE */ + +#ifdef LTC_PELICAN + +typedef struct pelican_state { + symmetric_key K; + unsigned char state[16]; + int buflen; +} pelican_state; + +int pelican_init(pelican_state *pelmac, const unsigned char *key, unsigned long keylen); +int pelican_process(pelican_state *pelmac, const unsigned char *in, unsigned long inlen); +int pelican_done(pelican_state *pelmac, unsigned char *out); +int pelican_test(void); + +int pelican_memory(const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out); +#endif + +#ifdef LTC_XCBC + +/* add this to "keylen" to xcbc_init to use a pure three-key XCBC MAC */ + #define LTC_XCBC_PURE 0x8000UL + +typedef struct { + unsigned char K[3][MAXBLOCKSIZE], + IV[MAXBLOCKSIZE]; + + symmetric_key key; + + int cipher, + buflen, + blocksize; +} xcbc_state; + +int xcbc_init(xcbc_state *xcbc, int cipher, const unsigned char *key, unsigned long keylen); +int xcbc_process(xcbc_state *xcbc, const unsigned char *in, unsigned long inlen); +int xcbc_done(xcbc_state *xcbc, unsigned char *out, unsigned long *outlen); +int xcbc_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int xcbc_memory_multi(int cipher, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...); +int xcbc_file(int cipher, + const unsigned char *key, unsigned long keylen, + const char *filename, + unsigned char *out, unsigned long *outlen); +int xcbc_test(void); +#endif + +#ifdef LTC_F9_MODE + +typedef struct { + unsigned char akey[MAXBLOCKSIZE], + ACC[MAXBLOCKSIZE], + IV[MAXBLOCKSIZE]; + + symmetric_key key; + + int cipher, + buflen, + keylen, + blocksize; +} f9_state; + +int f9_init(f9_state *f9, int cipher, const unsigned char *key, unsigned long keylen); +int f9_process(f9_state *f9, const unsigned char *in, unsigned long inlen); +int f9_done(f9_state *f9, unsigned char *out, unsigned long *outlen); +int f9_memory(int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int f9_memory_multi(int cipher, + const unsigned char *key, unsigned long keylen, + unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...); +int f9_file(int cipher, + const unsigned char *key, unsigned long keylen, + const char *filename, + unsigned char *out, unsigned long *outlen); +int f9_test(void); +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_mac.h,v $ */ +/* $Revision: 1.23 $ */ +/* $Date: 2007/05/12 14:37:41 $ */ + +/* ---- PRNG Stuff ---- */ +#ifdef LTC_YARROW +struct yarrow_prng { + int cipher, hash; + unsigned char pool[MAXBLOCKSIZE]; + symmetric_CTR ctr; + LTC_MUTEX_TYPE(prng_lock) +}; +#endif + +#ifdef LTC_RC4 +struct rc4_prng { + int x, y; + unsigned char buf[256]; +}; +#endif + +#ifdef LTC_FORTUNA +struct fortuna_prng { + hash_state pool[LTC_FORTUNA_POOLS]; /* the pools */ + + symmetric_key skey; + + unsigned char K[32], /* the current key */ + IV[16]; /* IV for CTR mode */ + + unsigned long pool_idx, /* current pool we will add to */ + pool0_len, /* length of 0'th pool */ + wd; + + ulong64 reset_cnt; /* number of times we have reset */ + LTC_MUTEX_TYPE(prng_lock) +}; +#endif + +#ifdef LTC_SOBER128 +struct sober128_prng { + ulong32 R[17], /* Working storage for the shift register */ + initR[17], /* saved register contents */ + konst, /* key dependent constant */ + sbuf; /* partial word encryption buffer */ + + int nbuf, /* number of part-word stream bits buffered */ + flag, /* first add_entropy call or not? */ + set; /* did we call add_entropy to set key? */ +}; +#endif + +typedef union Prng_state { + char dummy[1]; +#ifdef LTC_YARROW + struct yarrow_prng yarrow; +#endif +#ifdef LTC_RC4 + struct rc4_prng rc4; +#endif +#ifdef LTC_FORTUNA + struct fortuna_prng fortuna; +#endif +#ifdef LTC_SOBER128 + struct sober128_prng sober128; +#endif +} prng_state; + +/** PRNG descriptor */ +extern struct ltc_prng_descriptor { + /** Name of the PRNG */ + char *name; + /** size in bytes of exported state */ + int export_size; + + /** Start a PRNG state + @param prng [out] The state to initialize + @return CRYPT_OK if successful + */ + int (*start)(prng_state *prng); + + /** Add entropy to the PRNG + @param in The entropy + @param inlen Length of the entropy (octets)\ + @param prng The PRNG state + @return CRYPT_OK if successful + */ + int (*add_entropy)(const unsigned char *in, unsigned long inlen, prng_state *prng); + + /** Ready a PRNG state to read from + @param prng The PRNG state to ready + @return CRYPT_OK if successful + */ + int (*ready)(prng_state *prng); + + /** Read from the PRNG + @param out [out] Where to store the data + @param outlen Length of data desired (octets) + @param prng The PRNG state to read from + @return Number of octets read + */ + unsigned long (*read)(unsigned char *out, unsigned long outlen, prng_state *prng); + + /** Terminate a PRNG state + @param prng The PRNG state to terminate + @return CRYPT_OK if successful + */ + int (*done)(prng_state *prng); + + /** Export a PRNG state + @param out [out] The destination for the state + @param outlen [in/out] The max size and resulting size of the PRNG state + @param prng The PRNG to export + @return CRYPT_OK if successful + */ + int (*pexport)(unsigned char *out, unsigned long *outlen, prng_state *prng); + + /** Import a PRNG state + @param in The data to import + @param inlen The length of the data to import (octets) + @param prng The PRNG to initialize/import + @return CRYPT_OK if successful + */ + int (*pimport)(const unsigned char *in, unsigned long inlen, prng_state *prng); + + /** Self-test the PRNG + @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled + */ + int (*test)(void); +} prng_descriptor[]; + +#ifdef LTC_YARROW +int yarrow_start(prng_state *prng); +int yarrow_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int yarrow_ready(prng_state *prng); +unsigned long yarrow_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int yarrow_done(prng_state *prng); +int yarrow_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int yarrow_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int yarrow_test(void); + +extern const struct ltc_prng_descriptor yarrow_desc; +#endif + +#ifdef LTC_FORTUNA +int fortuna_start(prng_state *prng); +int fortuna_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int fortuna_ready(prng_state *prng); +unsigned long fortuna_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int fortuna_done(prng_state *prng); +int fortuna_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int fortuna_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int fortuna_test(void); + +extern const struct ltc_prng_descriptor fortuna_desc; +#endif + +#ifdef LTC_RC4 +int rc4_start(prng_state *prng); +int rc4_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int rc4_ready(prng_state *prng); +unsigned long rc4_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int rc4_done(prng_state *prng); +int rc4_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int rc4_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int rc4_test(void); + +extern const struct ltc_prng_descriptor rc4_desc; +#endif + +#ifdef LTC_SPRNG +int sprng_start(prng_state *prng); +int sprng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int sprng_ready(prng_state *prng); +unsigned long sprng_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int sprng_done(prng_state *prng); +int sprng_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int sprng_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int sprng_test(void); + +extern const struct ltc_prng_descriptor sprng_desc; +#endif + +#ifdef LTC_SOBER128 +int sober128_start(prng_state *prng); +int sober128_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int sober128_ready(prng_state *prng); +unsigned long sober128_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int sober128_done(prng_state *prng); +int sober128_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int sober128_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int sober128_test(void); + +extern const struct ltc_prng_descriptor sober128_desc; +#endif + +int find_prng(const char *name); +int register_prng(const struct ltc_prng_descriptor *prng); +int unregister_prng(const struct ltc_prng_descriptor *prng); +int prng_is_valid(int idx); + +LTC_MUTEX_PROTO(ltc_prng_mutex) + +/* Slow RNG you **might** be able to use to seed a PRNG with. Be careful as this + * might not work on all platforms as planned + */ +unsigned long rng_get_bytes(unsigned char *out, + unsigned long outlen, + void ( *callback)(void)); + +int rng_make_prng(int bits, int wprng, prng_state *prng, void (*callback)(void)); + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_prng.h,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + +/* ---- NUMBER THEORY ---- */ + +enum { + PK_PUBLIC =0, + PK_PRIVATE=1 +}; + +int rand_prime(void *N, long len, prng_state *prng, int wprng); + +/* ---- RSA ---- */ +#ifdef LTC_MRSA + +/* Min and Max RSA key sizes (in bits) */ + #define MIN_RSA_SIZE 1024 + #define MAX_RSA_SIZE 4096 + +/** RSA LTC_PKCS style key */ +typedef struct Rsa_key { + /** Type of key, PK_PRIVATE or PK_PUBLIC */ + int type; + /** The public exponent */ + void *e; + /** The private exponent */ + void *d; + /** The modulus */ + void *N; + /** The p factor of N */ + void *p; + /** The q factor of N */ + void *q; + /** The 1/q mod p CRT param */ + void *qP; + /** The d mod (p - 1) CRT param */ + void *dP; + /** The d mod (q - 1) CRT param */ + void *dQ; +} rsa_key; + +int rsa_make_key(prng_state *prng, int wprng, int size, long e, rsa_key *key); + +int rsa_exptmod(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, int which, + rsa_key *key); + +void rsa_free(rsa_key *key); + +/* These use LTC_PKCS #1 v2.0 padding */ + #define rsa_encrypt_key(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _prng, _prng_idx, _hash_idx, _key) \ + rsa_encrypt_key_ex(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _prng, _prng_idx, _hash_idx, LTC_LTC_PKCS_1_OAEP, _key) + + #define rsa_decrypt_key(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _hash_idx, _stat, _key) \ + rsa_decrypt_key_ex(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _hash_idx, LTC_LTC_PKCS_1_OAEP, _stat, _key) + + #define rsa_sign_hash(_in, _inlen, _out, _outlen, _prng, _prng_idx, _hash_idx, _saltlen, _key) \ + rsa_sign_hash_ex(_in, _inlen, _out, _outlen, LTC_LTC_PKCS_1_PSS, _prng, _prng_idx, _hash_idx, _saltlen, _key) + + #define rsa_verify_hash(_sig, _siglen, _hash, _hashlen, _hash_idx, _saltlen, _stat, _key) \ + rsa_verify_hash_ex(_sig, _siglen, _hash, _hashlen, LTC_LTC_PKCS_1_PSS, _hash_idx, _saltlen, _stat, _key) + +/* These can be switched between LTC_PKCS #1 v2.x and LTC_PKCS #1 v1.5 paddings */ +int rsa_encrypt_key_ex(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + const unsigned char *lparam, unsigned long lparamlen, + prng_state *prng, int prng_idx, int hash_idx, int padding, rsa_key *key); + +int rsa_decrypt_key_ex(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + const unsigned char *lparam, unsigned long lparamlen, + int hash_idx, int padding, + int *stat, rsa_key *key); + +int rsa_sign_hash_ex(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + int padding, + prng_state *prng, int prng_idx, + int hash_idx, unsigned long saltlen, + rsa_key *key); + +int rsa_verify_hash_ex(const unsigned char *sig, unsigned long siglen, + const unsigned char *hash, unsigned long hashlen, + int padding, + int hash_idx, unsigned long saltlen, + int *stat, rsa_key *key); + +/* LTC_PKCS #1 import/export */ +int rsa_export(unsigned char *out, unsigned long *outlen, int type, rsa_key *key); +int rsa_import(const unsigned char *in, unsigned long inlen, rsa_key *key); +#endif + +/* ---- Katja ---- */ +#ifdef MKAT + +/* Min and Max KAT key sizes (in bits) */ + #define MIN_KAT_SIZE 1024 + #define MAX_KAT_SIZE 4096 + +/** Katja LTC_PKCS style key */ +typedef struct KAT_key { + /** Type of key, PK_PRIVATE or PK_PUBLIC */ + int type; + /** The private exponent */ + void *d; + /** The modulus */ + void *N; + /** The p factor of N */ + void *p; + /** The q factor of N */ + void *q; + /** The 1/q mod p CRT param */ + void *qP; + /** The d mod (p - 1) CRT param */ + void *dP; + /** The d mod (q - 1) CRT param */ + void *dQ; + /** The pq param */ + void *pq; +} katja_key; + +int katja_make_key(prng_state *prng, int wprng, int size, katja_key *key); + +int katja_exptmod(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, int which, + katja_key *key); + +void katja_free(katja_key *key); + +/* These use LTC_PKCS #1 v2.0 padding */ +int katja_encrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + const unsigned char *lparam, unsigned long lparamlen, + prng_state *prng, int prng_idx, int hash_idx, katja_key *key); + +int katja_decrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + const unsigned char *lparam, unsigned long lparamlen, + int hash_idx, int *stat, + katja_key *key); + +/* LTC_PKCS #1 import/export */ +int katja_export(unsigned char *out, unsigned long *outlen, int type, katja_key *key); +int katja_import(const unsigned char *in, unsigned long inlen, katja_key *key); +#endif + +/* ---- ECC Routines ---- */ +#ifdef LTC_MECC + +/* size of our temp buffers for exported keys */ + #define ECC_BUF_SIZE 256 + +/* max private key size */ + #define ECC_MAXSIZE 66 + +/** Structure defines a NIST GF(p) curve */ +typedef struct { + /** The size of the curve in octets */ + int size; + + /** name of curve */ + char *name; + + /** The prime that defines the field the curve is in (encoded in hex) */ + char *prime; + + /** The fields B param (hex) */ + char *B; + + /** The order of the curve (hex) */ + char *order; + + /** The x co-ordinate of the base point on the curve (hex) */ + char *Gx; + + /** The y co-ordinate of the base point on the curve (hex) */ + char *Gy; +} ltc_ecc_set_type; + +/** A point on a ECC curve, stored in Jacbobian format such that (x,y,z) => (x/z^2, y/z^3, 1) when interpretted as affine */ +typedef struct { + /** The x co-ordinate */ + void *x; + + /** The y co-ordinate */ + void *y; + + /** The z co-ordinate */ + void *z; +} ecc_point; + +/** An ECC key */ +typedef struct { + /** Type of key, PK_PRIVATE or PK_PUBLIC */ + int type; + + /** Index into the ltc_ecc_sets[] for the parameters of this curve; if -1, then this key is using user supplied curve in dp */ + int idx; + + /** pointer to domain parameters; either points to NIST curves (identified by idx >= 0) or user supplied curve */ + const ltc_ecc_set_type *dp; + + /** The public key */ + ecc_point pubkey; + + /** The private key */ + void *k; +} ecc_key; + +/** the ECC params provided */ +extern const ltc_ecc_set_type ltc_ecc_sets[]; + +int ecc_test(void); +void ecc_sizes(int *low, int *high); +int ecc_get_size(ecc_key *key); + +int ecc_make_key(prng_state *prng, int wprng, int keysize, ecc_key *key); +int ecc_make_key_ex(prng_state *prng, int wprng, ecc_key *key, const ltc_ecc_set_type *dp); +void ecc_free(ecc_key *key); + +int ecc_export(unsigned char *out, unsigned long *outlen, int type, ecc_key *key); +int ecc_import(const unsigned char *in, unsigned long inlen, ecc_key *key); +int ecc_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, const ltc_ecc_set_type *dp); + +int ecc_ansi_x963_export(ecc_key *key, unsigned char *out, unsigned long *outlen); +int ecc_ansi_x963_import(const unsigned char *in, unsigned long inlen, ecc_key *key); +int ecc_ansi_x963_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, ltc_ecc_set_type *dp); + +int ecc_shared_secret(ecc_key *private_key, ecc_key *public_key, + unsigned char *out, unsigned long *outlen); + +int ecc_encrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, int hash, + ecc_key *key); + +int ecc_decrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + ecc_key *key); + +int ecc_sign_hash(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, ecc_key *key); + +int ecc_verify_hash(const unsigned char *sig, unsigned long siglen, + const unsigned char *hash, unsigned long hashlen, + int *stat, ecc_key *key); + +/* low level functions */ +ecc_point *ltc_ecc_new_point(void); +void ltc_ecc_del_point(ecc_point *p); +int ltc_ecc_is_valid_idx(int n); + +/* point ops (mp == montgomery digit) */ + #if !defined(LTC_MECC_ACCEL) || defined(LTM_LTC_DESC) || defined(GMP_LTC_DESC) +/* R = 2P */ +int ltc_ecc_projective_dbl_point(ecc_point *P, ecc_point *R, void *modulus, void *mp); + +/* R = P + Q */ +int ltc_ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp); + #endif + + #if defined(LTC_MECC_FP) +/* optimized point multiplication using fixed point cache (HAC algorithm 14.117) */ +int ltc_ecc_fp_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map); + +/* functions for saving/loading/freeing/adding to fixed point cache */ +int ltc_ecc_fp_save_state(unsigned char **out, unsigned long *outlen); +int ltc_ecc_fp_restore_state(unsigned char *in, unsigned long inlen); +void ltc_ecc_fp_free(void); +int ltc_ecc_fp_add_point(ecc_point *g, void *modulus, int lock); + +/* lock/unlock all points currently in fixed point cache */ +void ltc_ecc_fp_tablelock(int lock); + #endif + +/* R = kG */ +int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map); + + #ifdef LTC_ECC_SHAMIR +/* kA*A + kB*B = C */ +int ltc_ecc_mul2add(ecc_point *A, void *kA, + ecc_point *B, void *kB, + ecc_point *C, + void *modulus); + + #ifdef LTC_MECC_FP +/* Shamir's trick with optimized point multiplication using fixed point cache */ +int ltc_ecc_fp_mul2add(ecc_point *A, void *kA, + ecc_point *B, void *kB, + ecc_point *C, void *modulus); + #endif + #endif + + +/* map P to affine from projective */ +int ltc_ecc_map(ecc_point *P, void *modulus, void *mp); +#endif + +#ifdef LTC_MDSA + +/* Max diff between group and modulus size in bytes */ + #define LTC_MDSA_DELTA 512 + +/* Max DSA group size in bytes (default allows 4k-bit groups) */ + #define LTC_MDSA_MAX_GROUP 512 + +/** DSA key structure */ +typedef struct { + /** The key type, PK_PRIVATE or PK_PUBLIC */ + int type; + + /** The order of the sub-group used in octets */ + int qord; + + /** The generator */ + void *g; + + /** The prime used to generate the sub-group */ + void *q; + + /** The large prime that generats the field the contains the sub-group */ + void *p; + + /** The private key */ + void *x; + + /** The public key */ + void *y; +} dsa_key; + +int dsa_make_key(prng_state *prng, int wprng, int group_size, int modulus_size, dsa_key *key); +void dsa_free(dsa_key *key); + +int dsa_sign_hash_raw(const unsigned char *in, unsigned long inlen, + void *r, void *s, + prng_state *prng, int wprng, dsa_key *key); + +int dsa_sign_hash(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, dsa_key *key); + +int dsa_verify_hash_raw(void *r, void *s, + const unsigned char *hash, unsigned long hashlen, + int *stat, dsa_key *key); + +int dsa_verify_hash(const unsigned char *sig, unsigned long siglen, + const unsigned char *hash, unsigned long hashlen, + int *stat, dsa_key *key); + +int dsa_encrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, int hash, + dsa_key *key); + +int dsa_decrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + dsa_key *key); + +int dsa_import(const unsigned char *in, unsigned long inlen, dsa_key *key); +int dsa_export(unsigned char *out, unsigned long *outlen, int type, dsa_key *key); +int dsa_verify_key(dsa_key *key, int *stat); + +int dsa_shared_secret(void *private_key, void *base, + dsa_key *public_key, + unsigned char *out, unsigned long *outlen); +#endif + +#ifdef LTC_DER +/* DER handling */ + +enum { + LTC_ASN1_EOL, + LTC_ASN1_BOOLEAN, + LTC_ASN1_INTEGER, + LTC_ASN1_SHORT_INTEGER, + LTC_ASN1_BIT_STRING, + LTC_ASN1_OCTET_STRING, + LTC_ASN1_NULL, + LTC_ASN1_OBJECT_IDENTIFIER, + LTC_ASN1_IA5_STRING, + LTC_ASN1_PRINTABLE_STRING, + LTC_ASN1_UTF8_STRING, + LTC_ASN1_UTCTIME, + LTC_ASN1_CHOICE, + LTC_ASN1_SEQUENCE, + LTC_ASN1_SET, + LTC_ASN1_SETOF +}; + +/** A LTC ASN.1 list type */ +typedef struct ltc_asn1_list_ { + /** The LTC ASN.1 enumerated type identifier */ + int type; + /** The data to encode or place for decoding */ + void *data; + /** The size of the input or resulting output */ + unsigned long size; + /** The used flag, this is used by the CHOICE ASN.1 type to indicate which choice was made */ + int used; + /** prev/next entry in the list */ + struct ltc_asn1_list_ *prev, *next, *child, *parent; +} ltc_asn1_list; + + #define LTC_SET_ASN1(list, index, Type, Data, Size) \ + do { \ + int LTC_MACRO_temp = (index); \ + ltc_asn1_list *LTC_MACRO_list = (list); \ + LTC_MACRO_list[LTC_MACRO_temp].type = (Type); \ + LTC_MACRO_list[LTC_MACRO_temp].data = (void *)(Data); \ + LTC_MACRO_list[LTC_MACRO_temp].size = (Size); \ + LTC_MACRO_list[LTC_MACRO_temp].used = 0; \ + } while (0); + +/* SEQUENCE */ +int der_encode_sequence_ex(ltc_asn1_list *list, unsigned long inlen, + unsigned char *out, unsigned long *outlen, int type_of); + + #define der_encode_sequence(list, inlen, out, outlen) der_encode_sequence_ex(list, inlen, out, outlen, LTC_ASN1_SEQUENCE) + +int der_decode_sequence_ex(const unsigned char *in, unsigned long inlen, + ltc_asn1_list *list, unsigned long outlen, int ordered); + + #define der_decode_sequence(in, inlen, list, outlen) der_decode_sequence_ex(in, inlen, list, outlen, 1) + +int der_length_sequence(ltc_asn1_list *list, unsigned long inlen, + unsigned long *outlen); + +/* SET */ + #define der_decode_set(in, inlen, list, outlen) der_decode_sequence_ex(in, inlen, list, outlen, 0) + #define der_length_set der_length_sequence +int der_encode_set(ltc_asn1_list *list, unsigned long inlen, + unsigned char *out, unsigned long *outlen); + +int der_encode_setof(ltc_asn1_list *list, unsigned long inlen, + unsigned char *out, unsigned long *outlen); + +/* VA list handy helpers with triplets of */ +int der_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...); +int der_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...); + +/* FLEXI DECODER handle unknown list decoder */ +int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out); +void der_free_sequence_flexi(ltc_asn1_list *list); +void der_sequence_free(ltc_asn1_list *in); + +/* BOOLEAN */ +int der_length_boolean(unsigned long *outlen); +int der_encode_boolean(int in, + unsigned char *out, unsigned long *outlen); +int der_decode_boolean(const unsigned char *in, unsigned long inlen, + int *out); + +/* INTEGER */ +int der_encode_integer(void *num, unsigned char *out, unsigned long *outlen); +int der_decode_integer(const unsigned char *in, unsigned long inlen, void *num); +int der_length_integer(void *num, unsigned long *len); + +/* INTEGER -- handy for 0..2^32-1 values */ +int der_decode_short_integer(const unsigned char *in, unsigned long inlen, unsigned long *num); +int der_encode_short_integer(unsigned long num, unsigned char *out, unsigned long *outlen); +int der_length_short_integer(unsigned long num, unsigned long *outlen); + +/* BIT STRING */ +int der_encode_bit_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_decode_bit_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_length_bit_string(unsigned long nbits, unsigned long *outlen); + +/* OCTET STRING */ +int der_encode_octet_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_decode_octet_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_length_octet_string(unsigned long noctets, unsigned long *outlen); + +/* OBJECT IDENTIFIER */ +int der_encode_object_identifier(unsigned long *words, unsigned long nwords, + unsigned char *out, unsigned long *outlen); +int der_decode_object_identifier(const unsigned char *in, unsigned long inlen, + unsigned long *words, unsigned long *outlen); +int der_length_object_identifier(unsigned long *words, unsigned long nwords, unsigned long *outlen); +unsigned long der_object_identifier_bits(unsigned long x); + +/* IA5 STRING */ +int der_encode_ia5_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_decode_ia5_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_length_ia5_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen); + +int der_ia5_char_encode(int c); +int der_ia5_value_decode(int v); + +/* Printable STRING */ +int der_encode_printable_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_decode_printable_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); +int der_length_printable_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen); + +int der_printable_char_encode(int c); +int der_printable_value_decode(int v); + +/* UTF-8 */ + #if (defined(SIZE_MAX) || __STDC_VERSION__ >= 199901L || defined(WCHAR_MAX) || defined(_WCHAR_T) || defined(_WCHAR_T_DEFINED) || defined (__WCHAR_TYPE__)) && !defined(LTC_NO_WCHAR) + #include + #else +typedef ulong32 wchar_t; + #endif + +int der_encode_utf8_string(const wchar_t *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen); + +int der_decode_utf8_string(const unsigned char *in, unsigned long inlen, + wchar_t *out, unsigned long *outlen); +unsigned long der_utf8_charsize(const wchar_t c); +int der_length_utf8_string(const wchar_t *in, unsigned long noctets, unsigned long *outlen); + + +/* CHOICE */ +int der_decode_choice(const unsigned char *in, unsigned long *inlen, + ltc_asn1_list *list, unsigned long outlen); + +/* UTCTime */ +typedef struct { + unsigned YY, /* year */ + MM, /* month */ + DD, /* day */ + hh, /* hour */ + mm, /* minute */ + ss, /* second */ + off_dir, /* timezone offset direction 0 == +, 1 == - */ + off_hh, /* timezone offset hours */ + off_mm; /* timezone offset minutes */ +} ltc_utctime; + +int der_encode_utctime(ltc_utctime *utctime, + unsigned char *out, unsigned long *outlen); + +int der_decode_utctime(const unsigned char *in, unsigned long *inlen, + ltc_utctime *out); + +int der_length_utctime(ltc_utctime *utctime, unsigned long *outlen); +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_pk.h,v $ */ +/* $Revision: 1.81 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + +/** math functions **/ +#define LTC_SOURCE +#define LTC_MP_LT -1 +#define LTC_MP_EQ 0 +#define LTC_MP_GT 1 + +#define LTC_MP_NO 0 +#define LTC_MP_YES 1 + +#ifndef LTC_MECC +typedef void ecc_point; +#endif + +#ifndef LTC_MRSA +typedef void rsa_key; +#endif + +/** math descriptor */ +typedef struct { + /** Name of the math provider */ + char *name; + + /** Bits per digit, amount of bits must fit in an unsigned long */ + int bits_per_digit; + +/* ---- init/deinit functions ---- */ + + /** initialize a bignum + @param a The number to initialize + @return CRYPT_OK on success + */ + int (*init)(void **a); + + /** init copy + @param dst The number to initialize and write to + @param src The number to copy from + @return CRYPT_OK on success + */ + int (*init_copy)(void **dst, void *src); + + /** deinit + @param a The number to free + @return CRYPT_OK on success + */ + void (*deinit)(void *a); + +/* ---- data movement ---- */ + + /** negate + @param src The number to negate + @param dst The destination + @return CRYPT_OK on success + */ + int (*neg)(void *src, void *dst); + + /** copy + @param src The number to copy from + @param dst The number to write to + @return CRYPT_OK on success + */ + int (*copy)(void *src, void *dst); + +/* ---- trivial low level functions ---- */ + + /** set small constant + @param a Number to write to + @param n Source upto bits_per_digit (actually meant for very small constants) + @return CRYPT_OK on succcess + */ + int (*set_int)(void *a, unsigned long n); + + /** get small constant + @param a Number to read, only fetches upto bits_per_digit from the number + @return The lower bits_per_digit of the integer (unsigned) + */ + unsigned long (*get_int)(void *a); + + /** get digit n + @param a The number to read from + @param n The number of the digit to fetch + @return The bits_per_digit sized n'th digit of a + */ + unsigned long (*get_digit)(void *a, int n); + + /** Get the number of digits that represent the number + @param a The number to count + @return The number of digits used to represent the number + */ + int (*get_digit_count)(void *a); + + /** compare two integers + @param a The left side integer + @param b The right side integer + @return LTC_MP_LT if a < b, LTC_MP_GT if a > b and LTC_MP_EQ otherwise. (signed comparison) + */ + int (*compare)(void *a, void *b); + + /** compare against int + @param a The left side integer + @param b The right side integer (upto bits_per_digit) + @return LTC_MP_LT if a < b, LTC_MP_GT if a > b and LTC_MP_EQ otherwise. (signed comparison) + */ + int (*compare_d)(void *a, unsigned long n); + + /** Count the number of bits used to represent the integer + @param a The integer to count + @return The number of bits required to represent the integer + */ + int (*count_bits)(void *a); + + /** Count the number of LSB bits which are zero + @param a The integer to count + @return The number of contiguous zero LSB bits + */ + int (*count_lsb_bits)(void *a); + + /** Compute a power of two + @param a The integer to store the power in + @param n The power of two you want to store (a = 2^n) + @return CRYPT_OK on success + */ + int (*twoexpt)(void *a, int n); + +/* ---- radix conversions ---- */ + + /** read ascii string + @param a The integer to store into + @param str The string to read + @param radix The radix the integer has been represented in (2-64) + @return CRYPT_OK on success + */ + int (*read_radix)(void *a, const char *str, int radix); + + /** write number to string + @param a The integer to store + @param str The destination for the string + @param radix The radix the integer is to be represented in (2-64) + @return CRYPT_OK on success + */ + int (*write_radix)(void *a, char *str, int radix); + + /** get size as unsigned char string + @param a The integer to get the size (when stored in array of octets) + @return The length of the integer + */ + unsigned long (*unsigned_size)(void *a); + + /** store an integer as an array of octets + @param src The integer to store + @param dst The buffer to store the integer in + @return CRYPT_OK on success + */ + int (*unsigned_write)(void *src, unsigned char *dst); + + /** read an array of octets and store as integer + @param dst The integer to load + @param src The array of octets + @param len The number of octets + @return CRYPT_OK on success + */ + int (*unsigned_read)(void *dst, unsigned char *src, unsigned long len); + +/* ---- basic math ---- */ + + /** add two integers + @param a The first source integer + @param b The second source integer + @param c The destination of "a + b" + @return CRYPT_OK on success + */ + int (*add)(void *a, void *b, void *c); + + + /** add two integers + @param a The first source integer + @param b The second source integer (single digit of upto bits_per_digit in length) + @param c The destination of "a + b" + @return CRYPT_OK on success + */ + int (*addi)(void *a, unsigned long b, void *c); + + /** subtract two integers + @param a The first source integer + @param b The second source integer + @param c The destination of "a - b" + @return CRYPT_OK on success + */ + int (*sub)(void *a, void *b, void *c); + + /** subtract two integers + @param a The first source integer + @param b The second source integer (single digit of upto bits_per_digit in length) + @param c The destination of "a - b" + @return CRYPT_OK on success + */ + int (*subi)(void *a, unsigned long b, void *c); + + /** multiply two integers + @param a The first source integer + @param b The second source integer (single digit of upto bits_per_digit in length) + @param c The destination of "a * b" + @return CRYPT_OK on success + */ + int (*mul)(void *a, void *b, void *c); + + /** multiply two integers + @param a The first source integer + @param b The second source integer (single digit of upto bits_per_digit in length) + @param c The destination of "a * b" + @return CRYPT_OK on success + */ + int (*muli)(void *a, unsigned long b, void *c); + + /** Square an integer + @param a The integer to square + @param b The destination + @return CRYPT_OK on success + */ + int (*sqr)(void *a, void *b); + + /** Divide an integer + @param a The dividend + @param b The divisor + @param c The quotient (can be NULL to signify don't care) + @param d The remainder (can be NULL to signify don't care) + @return CRYPT_OK on success + */ + int (*mpdiv)(void *a, void *b, void *c, void *d); + + /** divide by two + @param a The integer to divide (shift right) + @param b The destination + @return CRYPT_OK on success + */ + int (*div_2)(void *a, void *b); + + /** Get remainder (small value) + @param a The integer to reduce + @param b The modulus (upto bits_per_digit in length) + @param c The destination for the residue + @return CRYPT_OK on success + */ + int (*modi)(void *a, unsigned long b, unsigned long *c); + + /** gcd + @param a The first integer + @param b The second integer + @param c The destination for (a, b) + @return CRYPT_OK on success + */ + int (*gcd)(void *a, void *b, void *c); + + /** lcm + @param a The first integer + @param b The second integer + @param c The destination for [a, b] + @return CRYPT_OK on success + */ + int (*lcm)(void *a, void *b, void *c); + + /** Modular multiplication + @param a The first source + @param b The second source + @param c The modulus + @param d The destination (a*b mod c) + @return CRYPT_OK on success + */ + int (*mulmod)(void *a, void *b, void *c, void *d); + + /** Modular squaring + @param a The first source + @param b The modulus + @param c The destination (a*a mod b) + @return CRYPT_OK on success + */ + int (*sqrmod)(void *a, void *b, void *c); + + /** Modular inversion + @param a The value to invert + @param b The modulus + @param c The destination (1/a mod b) + @return CRYPT_OK on success + */ + int (*invmod)(void *, void *, void *); + +/* ---- reduction ---- */ + + /** setup montgomery + @param a The modulus + @param b The destination for the reduction digit + @return CRYPT_OK on success + */ + int (*montgomery_setup)(void *a, void **b); + + /** get normalization value + @param a The destination for the normalization value + @param b The modulus + @return CRYPT_OK on success + */ + int (*montgomery_normalization)(void *a, void *b); + + /** reduce a number + @param a The number [and dest] to reduce + @param b The modulus + @param c The value "b" from montgomery_setup() + @return CRYPT_OK on success + */ + int (*montgomery_reduce)(void *a, void *b, void *c); + + /** clean up (frees memory) + @param a The value "b" from montgomery_setup() + @return CRYPT_OK on success + */ + void (*montgomery_deinit)(void *a); + +/* ---- exponentiation ---- */ + + /** Modular exponentiation + @param a The base integer + @param b The power (can be negative) integer + @param c The modulus integer + @param d The destination + @return CRYPT_OK on success + */ + int (*exptmod)(void *a, void *b, void *c, void *d); + + /** Primality testing + @param a The integer to test + @param b The destination of the result (FP_YES if prime) + @return CRYPT_OK on success + */ + int (*isprime)(void *a, int *b); + +/* ---- (optional) ecc point math ---- */ + + /** ECC GF(p) point multiplication (from the NIST curves) + @param k The integer to multiply the point by + @param G The point to multiply + @param R The destination for kG + @param modulus The modulus for the field + @param map Boolean indicated whether to map back to affine or not (can be ignored if you work in affine only) + @return CRYPT_OK on success + */ + int (*ecc_ptmul)(void *k, ecc_point *G, ecc_point *R, void *modulus, int map); + + /** ECC GF(p) point addition + @param P The first point + @param Q The second point + @param R The destination of P + Q + @param modulus The modulus + @param mp The "b" value from montgomery_setup() + @return CRYPT_OK on success + */ + int (*ecc_ptadd)(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp); + + /** ECC GF(p) point double + @param P The first point + @param R The destination of 2P + @param modulus The modulus + @param mp The "b" value from montgomery_setup() + @return CRYPT_OK on success + */ + int (*ecc_ptdbl)(ecc_point *P, ecc_point *R, void *modulus, void *mp); + + /** ECC mapping from projective to affine, currently uses (x,y,z) => (x/z^2, y/z^3, 1) + @param P The point to map + @param modulus The modulus + @param mp The "b" value from montgomery_setup() + @return CRYPT_OK on success + @remark The mapping can be different but keep in mind a ecc_point only has three + integers (x,y,z) so if you use a different mapping you have to make it fit. + */ + int (*ecc_map)(ecc_point *P, void *modulus, void *mp); + + /** Computes kA*A + kB*B = C using Shamir's Trick + @param A First point to multiply + @param kA What to multiple A by + @param B Second point to multiply + @param kB What to multiple B by + @param C [out] Destination point (can overlap with A or B + @param modulus Modulus for curve + @return CRYPT_OK on success + */ + int (*ecc_mul2add)(ecc_point *A, void *kA, + ecc_point *B, void *kB, + ecc_point *C, + void *modulus); + +/* ---- (optional) rsa optimized math (for internal CRT) ---- */ + + /** RSA Key Generation + @param prng An active PRNG state + @param wprng The index of the PRNG desired + @param size The size of the modulus (key size) desired (octets) + @param e The "e" value (public key). e==65537 is a good choice + @param key [out] Destination of a newly created private key pair + @return CRYPT_OK if successful, upon error all allocated ram is freed + */ + int (*rsa_keygen)(prng_state *prng, int wprng, int size, long e, rsa_key *key); + + + /** RSA exponentiation + @param in The octet array representing the base + @param inlen The length of the input + @param out The destination (to be stored in an octet array format) + @param outlen The length of the output buffer and the resulting size (zero padded to the size of the modulus) + @param which PK_PUBLIC for public RSA and PK_PRIVATE for private RSA + @param key The RSA key to use + @return CRYPT_OK on success + */ + int (*rsa_me)(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, int which, + rsa_key *key); +} ltc_math_descriptor; + +extern ltc_math_descriptor ltc_mp; + +int ltc_init_multi(void **a, ...); +void ltc_deinit_multi(void *a, ...); + +#ifdef LTM_DESC +extern const ltc_math_descriptor ltm_desc; +#endif + +#ifdef TFM_DESC +extern const ltc_math_descriptor tfm_desc; +#endif + +#ifdef GMP_DESC +extern const ltc_math_descriptor gmp_desc; +#endif + +#if !defined(DESC_DEF_ONLY) && defined(LTC_SOURCE) + #undef MP_DIGIT_BIT + #undef mp_iszero + #undef mp_isodd + #undef mp_tohex + + #define MP_DIGIT_BIT ltc_mp.bits_per_digit + +/* some handy macros */ + #define mp_init(a) ltc_mp.init(a) + #define mp_init_multi ltc_init_multi + #define mp_clear(a) ltc_mp.deinit(a) + #define mp_clear_multi ltc_deinit_multi + #define mp_init_copy(a, b) ltc_mp.init_copy(a, b) + + #define mp_neg(a, b) ltc_mp.neg(a, b) + #define mp_copy(a, b) ltc_mp.copy(a, b) + + #define mp_set(a, b) ltc_mp.set_int(a, b) + #define mp_set_int(a, b) ltc_mp.set_int(a, b) + #define mp_get_int(a) ltc_mp.get_int(a) + #define mp_get_digit(a, n) ltc_mp.get_digit(a, n) + #define mp_get_digit_count(a) ltc_mp.get_digit_count(a) + #define mp_cmp(a, b) ltc_mp.compare(a, b) + #define mp_cmp_d(a, b) ltc_mp.compare_d(a, b) + #define mp_count_bits(a) ltc_mp.count_bits(a) + #define mp_cnt_lsb(a) ltc_mp.count_lsb_bits(a) + #define mp_2expt(a, b) ltc_mp.twoexpt(a, b) + + #define mp_read_radix(a, b, c) ltc_mp.read_radix(a, b, c) + #define mp_toradix(a, b, c) ltc_mp.write_radix(a, b, c) + #define mp_unsigned_bin_size(a) ltc_mp.unsigned_size(a) + #define mp_to_unsigned_bin(a, b) ltc_mp.unsigned_write(a, b) + #define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c) + + #define mp_add(a, b, c) ltc_mp.add(a, b, c) + #define mp_add_d(a, b, c) ltc_mp.addi(a, b, c) + #define mp_sub(a, b, c) ltc_mp.sub(a, b, c) + #define mp_sub_d(a, b, c) ltc_mp.subi(a, b, c) + #define mp_mul(a, b, c) ltc_mp.mul(a, b, c) + #define mp_mul_d(a, b, c) ltc_mp.muli(a, b, c) + #define mp_sqr(a, b) ltc_mp.sqr(a, b) + #define mp_div(a, b, c, d) ltc_mp.mpdiv(a, b, c, d) + #define mp_div_2(a, b) ltc_mp.div_2(a, b) + #define mp_mod(a, b, c) ltc_mp.mpdiv(a, b, NULL, c) + #define mp_mod_d(a, b, c) ltc_mp.modi(a, b, c) + #define mp_gcd(a, b, c) ltc_mp.gcd(a, b, c) + #define mp_lcm(a, b, c) ltc_mp.lcm(a, b, c) + + #define mp_mulmod(a, b, c, d) ltc_mp.mulmod(a, b, c, d) + #define mp_sqrmod(a, b, c) ltc_mp.sqrmod(a, b, c) + #define mp_invmod(a, b, c) ltc_mp.invmod(a, b, c) + + #define mp_montgomery_setup(a, b) ltc_mp.montgomery_setup(a, b) + #define mp_montgomery_normalization(a, b) ltc_mp.montgomery_normalization(a, b) + #define mp_montgomery_reduce(a, b, c) ltc_mp.montgomery_reduce(a, b, c) + #define mp_montgomery_free(a) ltc_mp.montgomery_deinit(a) + + #define mp_exptmod(a, b, c, d) ltc_mp.exptmod(a, b, c, d) + #define mp_prime_is_prime(a, b, c) ltc_mp.isprime(a, c) + + #define mp_iszero(a) (mp_cmp_d(a, 0) == LTC_MP_EQ ? LTC_MP_YES : LTC_MP_NO) + #define mp_isodd(a) (mp_get_digit_count(a) > 0 ? (mp_get_digit(a, 0) & 1 ? LTC_MP_YES : LTC_MP_NO) : LTC_MP_NO) + #define mp_exch(a, b) do { void *ABC__tmp = a; a = b; b = ABC__tmp; } while (0); + + #define mp_tohex(a, b) mp_toradix(a, b, 16) +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_math.h,v $ */ +/* $Revision: 1.44 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + +/* ---- LTC_BASE64 Routines ---- */ +#ifdef LTC_BASE64 +int base64_encode(const unsigned char *in, unsigned long len, + unsigned char *out, unsigned long *outlen); + +int base64_decode(const unsigned char *in, unsigned long len, + unsigned char *out, unsigned long *outlen); +#endif + +/* ---- MEM routines ---- */ +void zeromem(void *dst, size_t len); +void burn_stack(unsigned long len); + +const char *error_to_string(int err); + +extern const char *crypt_build_settings; + +/* ---- HMM ---- */ +int crypt_fsa(void *mp, ...); + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_misc.h,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + +/* Defines the LTC_ARGCHK macro used within the library */ +/* ARGTYPE is defined in mycrypt_cfg.h */ +#if ARGTYPE == 0 + + #include + +/* this is the default LibTomCrypt macro */ +void crypt_argchk(char *v, char *s, int d); + + #define LTC_ARGCHK(x) if (!(x)) { crypt_argchk(#x, __FILE__, __LINE__); } + #define LTC_ARGCHKVD(x) LTC_ARGCHK(x) + +#elif ARGTYPE == 1 + +/* fatal type of error */ + #define LTC_ARGCHK(x) assert((x)) + #define LTC_ARGCHKVD(x) LTC_ARGCHK(x) + +#elif ARGTYPE == 2 + + #define LTC_ARGCHK(x) if (!(x)) { fprintf(stderr, "\nwarning: ARGCHK failed at %s:%d\n", __FILE__, __LINE__); } + #define LTC_ARGCHKVD(x) LTC_ARGCHK(x) + +#elif ARGTYPE == 3 + + #define LTC_ARGCHK(x) + #define LTC_ARGCHKVD(x) LTC_ARGCHK(x) + +#elif ARGTYPE == 4 + + #define LTC_ARGCHK(x) if (!(x)) return CRYPT_INVALID_ARG; + #define LTC_ARGCHKVD(x) if (!(x)) return; +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_argchk.h,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/08/27 20:50:21 $ */ + +/* LTC_PKCS Header Info */ + +/* ===> LTC_PKCS #1 -- RSA Cryptography <=== */ +#ifdef LTC_PKCS_1 + +enum ltc_pkcs_1_v1_5_blocks { + LTC_LTC_PKCS_1_EMSA = 1, /* Block type 1 (LTC_PKCS #1 v1.5 signature padding) */ + LTC_LTC_PKCS_1_EME = 2 /* Block type 2 (LTC_PKCS #1 v1.5 encryption padding) */ +}; + +enum ltc_pkcs_1_paddings { + LTC_LTC_PKCS_1_V1_5 = 1, /* LTC_PKCS #1 v1.5 padding (\sa ltc_pkcs_1_v1_5_blocks) */ + LTC_LTC_PKCS_1_OAEP = 2, /* LTC_PKCS #1 v2.0 encryption padding */ + LTC_LTC_PKCS_1_PSS = 3 /* LTC_PKCS #1 v2.1 signature padding */ +}; + +int pkcs_1_mgf1(int hash_idx, + const unsigned char *seed, unsigned long seedlen, + unsigned char *mask, unsigned long masklen); + +int pkcs_1_i2osp(void *n, unsigned long modulus_len, unsigned char *out); +int pkcs_1_os2ip(void *n, unsigned char *in, unsigned long inlen); + +/* *** v1.5 padding */ +int pkcs_1_v1_5_encode(const unsigned char *msg, + unsigned long msglen, + int block_type, + unsigned long modulus_bitlen, + prng_state *prng, + int prng_idx, + unsigned char *out, + unsigned long *outlen); + +int pkcs_1_v1_5_decode(const unsigned char *msg, + unsigned long msglen, + int block_type, + unsigned long modulus_bitlen, + unsigned char *out, + unsigned long *outlen, + int *is_valid); + +/* *** v2.1 padding */ +int pkcs_1_oaep_encode(const unsigned char *msg, unsigned long msglen, + const unsigned char *lparam, unsigned long lparamlen, + unsigned long modulus_bitlen, prng_state *prng, + int prng_idx, int hash_idx, + unsigned char *out, unsigned long *outlen); + +int pkcs_1_oaep_decode(const unsigned char *msg, unsigned long msglen, + const unsigned char *lparam, unsigned long lparamlen, + unsigned long modulus_bitlen, int hash_idx, + unsigned char *out, unsigned long *outlen, + int *res); + +int pkcs_1_pss_encode(const unsigned char *msghash, unsigned long msghashlen, + unsigned long saltlen, prng_state *prng, + int prng_idx, int hash_idx, + unsigned long modulus_bitlen, + unsigned char *out, unsigned long *outlen); + +int pkcs_1_pss_decode(const unsigned char *msghash, unsigned long msghashlen, + const unsigned char *sig, unsigned long siglen, + unsigned long saltlen, int hash_idx, + unsigned long modulus_bitlen, int *res); +#endif /* LTC_PKCS_1 */ + +/* ===> LTC_PKCS #5 -- Password Based Cryptography <=== */ +#ifdef LTC_PKCS_5 + +/* Algorithm #1 (old) */ +int pkcs_5_alg1(const unsigned char *password, unsigned long password_len, + const unsigned char *salt, + int iteration_count, int hash_idx, + unsigned char *out, unsigned long *outlen); + +/* Algorithm #2 (new) */ +int pkcs_5_alg2(const unsigned char *password, unsigned long password_len, + const unsigned char *salt, unsigned long salt_len, + int iteration_count, int hash_idx, + unsigned char *out, unsigned long *outlen); +#endif /* LTC_PKCS_5 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_pkcs.h,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + +#ifdef __cplusplus +} +#endif +#endif /* TOMCRYPT_H_ */ + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt.h,v $ */ +/* $Revision: 1.21 $ */ +/* $Date: 2006/12/16 19:34:05 $ */ + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_argchk.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_cipher_descriptor.c + Stores the cipher descriptor table, Tom St Denis + */ + +struct ltc_cipher_descriptor cipher_descriptor[TAB_SIZE] = { + { NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +}; + +LTC_MUTEX_GLOBAL(ltc_cipher_mutex) + + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_cipher_descriptor.c,v $ */ +/* $Revision: 1.13 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_cipher_is_valid.c + Determine if cipher is valid, Tom St Denis + */ + +/* + Test if a cipher index is valid + @param idx The index of the cipher to search for + @return CRYPT_OK if valid + */ +int cipher_is_valid(int idx) { + LTC_MUTEX_LOCK(<c_cipher_mutex); + if ((idx < 0) || (idx >= TAB_SIZE) || (cipher_descriptor[idx].name == NULL)) { + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return CRYPT_INVALID_CIPHER; + } + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return CRYPT_OK; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_cipher_is_valid.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_find_cipher.c + Find a cipher in the descriptor tables, Tom St Denis + */ + +/** + Find a registered cipher by name + @param name The name of the cipher to look for + @return >= 0 if found, -1 if not present + */ +int find_cipher(const char *name) { + int x; + + LTC_ARGCHK(name != NULL); + LTC_MUTEX_LOCK(<c_cipher_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if ((cipher_descriptor[x].name != NULL) && !XSTRCMP(cipher_descriptor[x].name, name)) { + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return x; + } + } + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_cipher.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_find_cipher_any.c + Find a cipher in the descriptor tables, Tom St Denis + */ + +/** + Find a cipher flexibly. First by name then if not present by block and key size + @param name The name of the cipher desired + @param blocklen The minimum length of the block cipher desired (octets) + @param keylen The minimum length of the key size desired (octets) + @return >= 0 if found, -1 if not present + */ +int find_cipher_any(const char *name, int blocklen, int keylen) { + int x; + + LTC_ARGCHK(name != NULL); + + x = find_cipher(name); + if (x != -1) return x; + + LTC_MUTEX_LOCK(<c_cipher_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if (cipher_descriptor[x].name == NULL) { + continue; + } + if ((blocklen <= (int)cipher_descriptor[x].block_length) && (keylen <= (int)cipher_descriptor[x].max_key_length)) { + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return x; + } + } + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_cipher_any.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_find_cipher_id.c + Find cipher by ID, Tom St Denis + */ + +/** + Find a cipher by ID number + @param ID The ID (not same as index) of the cipher to find + @return >= 0 if found, -1 if not present + */ +int find_cipher_id(unsigned char ID) { + int x; + + LTC_MUTEX_LOCK(<c_cipher_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if (cipher_descriptor[x].ID == ID) { + x = (cipher_descriptor[x].name == NULL) ? -1 : x; + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return x; + } + } + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_cipher_id.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_find_hash.c + Find a hash, Tom St Denis + */ + +/** + Find a registered hash by name + @param name The name of the hash to look for + @return >= 0 if found, -1 if not present + */ +int find_hash(const char *name) { + int x; + + LTC_ARGCHK(name != NULL); + LTC_MUTEX_LOCK(<c_hash_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if ((hash_descriptor[x].name != NULL) && (XSTRCMP(hash_descriptor[x].name, name) == 0)) { + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return x; + } + } + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_hash.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_find_hash_any.c + Find a hash, Tom St Denis + */ + +/** + Find a hash flexibly. First by name then if not present by digest size + @param name The name of the hash desired + @param digestlen The minimum length of the digest size (octets) + @return >= 0 if found, -1 if not present + */int find_hash_any(const char *name, int digestlen) { + int x, y, z; + + LTC_ARGCHK(name != NULL); + + x = find_hash(name); + if (x != -1) return x; + + LTC_MUTEX_LOCK(<c_hash_mutex); + y = MAXBLOCKSIZE + 1; + z = -1; + for (x = 0; x < TAB_SIZE; x++) { + if (hash_descriptor[x].name == NULL) { + continue; + } + if (((int)hash_descriptor[x].hashsize >= digestlen) && ((int)hash_descriptor[x].hashsize < y)) { + z = x; + y = hash_descriptor[x].hashsize; + } + } + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return z; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_hash_any.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_find_hash_id.c + Find hash by ID, Tom St Denis + */ + +/** + Find a hash by ID number + @param ID The ID (not same as index) of the hash to find + @return >= 0 if found, -1 if not present + */ +int find_hash_id(unsigned char ID) { + int x; + + LTC_MUTEX_LOCK(<c_hash_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if (hash_descriptor[x].ID == ID) { + x = (hash_descriptor[x].name == NULL) ? -1 : x; + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return x; + } + } + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_hash_id.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_find_hash_oid.c + Find a hash, Tom St Denis + */ + +int find_hash_oid(const unsigned long *ID, unsigned long IDlen) { + int x; + + LTC_ARGCHK(ID != NULL); + LTC_MUTEX_LOCK(<c_hash_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if ((hash_descriptor[x].name != NULL) && (hash_descriptor[x].OIDlen == IDlen) && !XMEMCMP(hash_descriptor[x].OID, ID, sizeof(unsigned long) * IDlen)) { + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return x; + } + } + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_hash_oid.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_find_prng.c + Find a PRNG, Tom St Denis + */ + +/** + Find a registered PRNG by name + @param name The name of the PRNG to look for + @return >= 0 if found, -1 if not present + */ +int find_prng(const char *name) { + int x; + + LTC_ARGCHK(name != NULL); + LTC_MUTEX_LOCK(<c_prng_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if ((prng_descriptor[x].name != NULL) && (XSTRCMP(prng_descriptor[x].name, name) == 0)) { + LTC_MUTEX_UNLOCK(<c_prng_mutex); + return x; + } + } + LTC_MUTEX_UNLOCK(<c_prng_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_prng.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#include + +/** + @file crypt_fsa.c + LibTomCrypt FULL SPEED AHEAD!, Tom St Denis + */ + +/* format is ltc_mp, cipher_desc, [cipher_desc], NULL, hash_desc, [hash_desc], NULL, prng_desc, [prng_desc], NULL */ +int crypt_fsa(void *mp, ...) { + int err; + va_list args; + void *p; + + va_start(args, mp); + if (mp != NULL) { + XMEMCPY(<c_mp, mp, sizeof(ltc_mp)); + } + + while ((p = va_arg(args, void *)) != NULL) { + if ((err = register_cipher(p)) != CRYPT_OK) { + va_end(args); + return err; + } + } + + while ((p = va_arg(args, void *)) != NULL) { + if ((err = register_hash(p)) != CRYPT_OK) { + va_end(args); + return err; + } + } + + while ((p = va_arg(args, void *)) != NULL) { + if ((err = register_prng(p)) != CRYPT_OK) { + va_end(args); + return err; + } + } + + va_end(args); + return CRYPT_OK; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_fsa.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_hash_descriptor.c + Stores the hash descriptor table, Tom St Denis + */ + +struct ltc_hash_descriptor hash_descriptor[TAB_SIZE] = { + { NULL, 0, 0, 0, { 0 }, 0, NULL, NULL, NULL, NULL, NULL } +}; + +LTC_MUTEX_GLOBAL(ltc_hash_mutex) + + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_hash_descriptor.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_hash_is_valid.c + Determine if hash is valid, Tom St Denis + */ + +/* + Test if a hash index is valid + @param idx The index of the hash to search for + @return CRYPT_OK if valid + */ +int hash_is_valid(int idx) { + LTC_MUTEX_LOCK(<c_hash_mutex); + if ((idx < 0) || (idx >= TAB_SIZE) || (hash_descriptor[idx].name == NULL)) { + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return CRYPT_INVALID_HASH; + } + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return CRYPT_OK; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_hash_is_valid.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +ltc_math_descriptor ltc_mp; + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_prng_descriptor.c + Stores the PRNG descriptors, Tom St Denis + */ +struct ltc_prng_descriptor prng_descriptor[TAB_SIZE] = { + { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +}; + +LTC_MUTEX_GLOBAL(ltc_prng_mutex) + + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_prng_descriptor.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_prng_is_valid.c + Determine if PRNG is valid, Tom St Denis + */ + +/* + Test if a PRNG index is valid + @param idx The index of the PRNG to search for + @return CRYPT_OK if valid + */ +int prng_is_valid(int idx) { + LTC_MUTEX_LOCK(<c_prng_mutex); + if ((idx < 0) || (idx >= TAB_SIZE) || (prng_descriptor[idx].name == NULL)) { + LTC_MUTEX_UNLOCK(<c_prng_mutex); + return CRYPT_INVALID_PRNG; + } + LTC_MUTEX_UNLOCK(<c_prng_mutex); + return CRYPT_OK; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_prng_is_valid.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_register_cipher.c + Register a cipher, Tom St Denis + */ + +/** + Register a cipher with the descriptor table + @param cipher The cipher you wish to register + @return value >= 0 if successfully added (or already present), -1 if unsuccessful + */ +int register_cipher(const struct ltc_cipher_descriptor *cipher) { + int x; + + LTC_ARGCHK(cipher != NULL); + + /* is it already registered? */ + LTC_MUTEX_LOCK(<c_cipher_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if ((cipher_descriptor[x].name != NULL) && (cipher_descriptor[x].ID == cipher->ID)) { + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return x; + } + } + + /* find a blank spot */ + for (x = 0; x < TAB_SIZE; x++) { + if (cipher_descriptor[x].name == NULL) { + XMEMCPY(&cipher_descriptor[x], cipher, sizeof(struct ltc_cipher_descriptor)); + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return x; + } + } + + /* no spot */ + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_register_cipher.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_register_hash.c + Register a HASH, Tom St Denis + */ + +/** + Register a hash with the descriptor table + @param hash The hash you wish to register + @return value >= 0 if successfully added (or already present), -1 if unsuccessful + */ +int register_hash(const struct ltc_hash_descriptor *hash) { + int x; + + LTC_ARGCHK(hash != NULL); + + /* is it already registered? */ + LTC_MUTEX_LOCK(<c_hash_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if (XMEMCMP(&hash_descriptor[x], hash, sizeof(struct ltc_hash_descriptor)) == 0) { + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return x; + } + } + + /* find a blank spot */ + for (x = 0; x < TAB_SIZE; x++) { + if (hash_descriptor[x].name == NULL) { + XMEMCPY(&hash_descriptor[x], hash, sizeof(struct ltc_hash_descriptor)); + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return x; + } + } + + /* no spot */ + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_register_hash.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_register_prng.c + Register a PRNG, Tom St Denis + */ + +/** + Register a PRNG with the descriptor table + @param prng The PRNG you wish to register + @return value >= 0 if successfully added (or already present), -1 if unsuccessful + */ +int register_prng(const struct ltc_prng_descriptor *prng) { + int x; + + LTC_ARGCHK(prng != NULL); + + /* is it already registered? */ + LTC_MUTEX_LOCK(<c_prng_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if (XMEMCMP(&prng_descriptor[x], prng, sizeof(struct ltc_prng_descriptor)) == 0) { + LTC_MUTEX_UNLOCK(<c_prng_mutex); + return x; + } + } + + /* find a blank spot */ + for (x = 0; x < TAB_SIZE; x++) { + if (prng_descriptor[x].name == NULL) { + XMEMCPY(&prng_descriptor[x], prng, sizeof(struct ltc_prng_descriptor)); + LTC_MUTEX_UNLOCK(<c_prng_mutex); + return x; + } + } + + /* no spot */ + LTC_MUTEX_UNLOCK(<c_prng_mutex); + return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_register_prng.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_unregister_cipher.c + Unregister a cipher, Tom St Denis + */ + +/** + Unregister a cipher from the descriptor table + @param cipher The cipher descriptor to remove + @return CRYPT_OK on success + */ +int unregister_cipher(const struct ltc_cipher_descriptor *cipher) { + int x; + + LTC_ARGCHK(cipher != NULL); + + /* is it already registered? */ + LTC_MUTEX_LOCK(<c_cipher_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if (XMEMCMP(&cipher_descriptor[x], cipher, sizeof(struct ltc_cipher_descriptor)) == 0) { + cipher_descriptor[x].name = NULL; + cipher_descriptor[x].ID = 255; + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return CRYPT_OK; + } + } + LTC_MUTEX_UNLOCK(<c_cipher_mutex); + return CRYPT_ERROR; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_unregister_cipher.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_unregister_hash.c + Unregister a hash, Tom St Denis + */ + +/** + Unregister a hash from the descriptor table + @param hash The hash descriptor to remove + @return CRYPT_OK on success + */ +int unregister_hash(const struct ltc_hash_descriptor *hash) { + int x; + + LTC_ARGCHK(hash != NULL); + + /* is it already registered? */ + LTC_MUTEX_LOCK(<c_hash_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if (XMEMCMP(&hash_descriptor[x], hash, sizeof(struct ltc_hash_descriptor)) == 0) { + hash_descriptor[x].name = NULL; + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return CRYPT_OK; + } + } + LTC_MUTEX_UNLOCK(<c_hash_mutex); + return CRYPT_ERROR; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_unregister_hash.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file crypt_unregister_prng.c + Unregister a PRNG, Tom St Denis + */ + +/** + Unregister a PRNG from the descriptor table + @param prng The PRNG descriptor to remove + @return CRYPT_OK on success + */ +int unregister_prng(const struct ltc_prng_descriptor *prng) { + int x; + + LTC_ARGCHK(prng != NULL); + + /* is it already registered? */ + LTC_MUTEX_LOCK(<c_prng_mutex); + for (x = 0; x < TAB_SIZE; x++) { + if (XMEMCMP(&prng_descriptor[x], prng, sizeof(struct ltc_prng_descriptor)) != 0) { + prng_descriptor[x].name = NULL; + LTC_MUTEX_UNLOCK(<c_prng_mutex); + return CRYPT_OK; + } + } + LTC_MUTEX_UNLOCK(<c_prng_mutex); + return CRYPT_ERROR; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_unregister_prng.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_bit_string.c + ASN.1 DER, encode a BIT STRING, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store a BIT STRING + @param in The DER encoded BIT STRING + @param inlen The size of the DER BIT STRING + @param out [out] The array of bits stored (one per char) + @param outlen [in/out] The number of bits stored + @return CRYPT_OK if successful + */ +int der_decode_bit_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long dlen, blen, x, y; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* packet must be at least 4 bytes */ + if (inlen < 4) { + return CRYPT_INVALID_ARG; + } + + /* check for 0x03 */ + if ((in[0] & 0x1F) != 0x03) { + return CRYPT_INVALID_PACKET; + } + + /* offset in the data */ + x = 1; + + /* get the length of the data */ + if (in[x] & 0x80) { + /* long format get number of length bytes */ + y = in[x++] & 0x7F; + + /* invalid if 0 or > 2 */ + if ((y == 0) || (y > 2)) { + return CRYPT_INVALID_PACKET; + } + + /* read the data len */ + dlen = 0; + while (y--) { + dlen = (dlen << 8) | (unsigned long)in[x++]; + } + } else { + /* short format */ + dlen = in[x++] & 0x7F; + } + + /* is the data len too long or too short? */ + if ((dlen == 0) || (dlen + x > inlen)) { + return CRYPT_INVALID_PACKET; + } + + /* get padding count */ + blen = ((dlen - 1) << 3) - (in[x++] & 7); + + /* too many bits? */ + if (blen > *outlen) { + *outlen = blen; + return CRYPT_BUFFER_OVERFLOW; + } + + /* decode/store the bits */ + for (y = 0; y < blen; y++) { + out[y] = (in[x] & (1 << (7 - (y & 7)))) ? 1 : 0; + if ((y & 7) == 7) { + ++x; + } + } + + /* we done */ + *outlen = blen; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/bit/der_decode_bit_string.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_boolean.c + ASN.1 DER, decode a BOOLEAN, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Read a BOOLEAN + @param in The destination for the DER encoded BOOLEAN + @param inlen The size of the DER BOOLEAN + @param out [out] The boolean to decode + @return CRYPT_OK if successful + */ +int der_decode_boolean(const unsigned char *in, unsigned long inlen, + int *out) { + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + + if ((inlen != 3) || (in[0] != 0x01) || (in[1] != 0x01) || ((in[2] != 0x00) && (in[2] != 0xFF))) { + return CRYPT_INVALID_ARG; + } + + *out = (in[2] == 0xFF) ? 1 : 0; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/boolean/der_decode_boolean.c,v $ */ +/* $Revision: 1.2 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_choice.c + ASN.1 DER, decode a CHOICE, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Decode a CHOICE + @param in The DER encoded input + @param inlen [in/out] The size of the input and resulting size of read type + @param list The list of items to decode + @param outlen The number of items in the list + @return CRYPT_OK on success + */ +int der_decode_choice(const unsigned char *in, unsigned long *inlen, + ltc_asn1_list *list, unsigned long outlen) { + unsigned long size, x, z; + void *data; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(inlen != NULL); + LTC_ARGCHK(list != NULL); + + /* get blk size */ + if (*inlen < 2) { + return CRYPT_INVALID_PACKET; + } + + /* set all of the "used" flags to zero */ + for (x = 0; x < outlen; x++) { + list[x].used = 0; + } + + /* now scan until we have a winner */ + for (x = 0; x < outlen; x++) { + size = list[x].size; + data = list[x].data; + + switch (list[x].type) { + case LTC_ASN1_INTEGER: + if (der_decode_integer(in, *inlen, data) == CRYPT_OK) { + if (der_length_integer(data, &z) == CRYPT_OK) { + list[x].used = 1; + *inlen = z; + return CRYPT_OK; + } + } + break; + + case LTC_ASN1_SHORT_INTEGER: + if (der_decode_short_integer(in, *inlen, data) == CRYPT_OK) { + if (der_length_short_integer(size, &z) == CRYPT_OK) { + list[x].used = 1; + *inlen = z; + return CRYPT_OK; + } + } + break; + + case LTC_ASN1_BIT_STRING: + if (der_decode_bit_string(in, *inlen, data, &size) == CRYPT_OK) { + if (der_length_bit_string(size, &z) == CRYPT_OK) { + list[x].used = 1; + list[x].size = size; + *inlen = z; + return CRYPT_OK; + } + } + break; + + case LTC_ASN1_OCTET_STRING: + if (der_decode_octet_string(in, *inlen, data, &size) == CRYPT_OK) { + if (der_length_octet_string(size, &z) == CRYPT_OK) { + list[x].used = 1; + list[x].size = size; + *inlen = z; + return CRYPT_OK; + } + } + break; + + case LTC_ASN1_NULL: + if ((*inlen == 2) && (in[x] == 0x05) && (in[x + 1] == 0x00)) { + *inlen = 2; + list[x].used = 1; + return CRYPT_OK; + } + break; + + case LTC_ASN1_OBJECT_IDENTIFIER: + if (der_decode_object_identifier(in, *inlen, data, &size) == CRYPT_OK) { + if (der_length_object_identifier(data, size, &z) == CRYPT_OK) { + list[x].used = 1; + list[x].size = size; + *inlen = z; + return CRYPT_OK; + } + } + break; + + case LTC_ASN1_IA5_STRING: + if (der_decode_ia5_string(in, *inlen, data, &size) == CRYPT_OK) { + if (der_length_ia5_string(data, size, &z) == CRYPT_OK) { + list[x].used = 1; + list[x].size = size; + *inlen = z; + return CRYPT_OK; + } + } + break; + + + case LTC_ASN1_PRINTABLE_STRING: + if (der_decode_printable_string(in, *inlen, data, &size) == CRYPT_OK) { + if (der_length_printable_string(data, size, &z) == CRYPT_OK) { + list[x].used = 1; + list[x].size = size; + *inlen = z; + return CRYPT_OK; + } + } + break; + + case LTC_ASN1_UTF8_STRING: + if (der_decode_utf8_string(in, *inlen, data, &size) == CRYPT_OK) { + if (der_length_utf8_string(data, size, &z) == CRYPT_OK) { + list[x].used = 1; + list[x].size = size; + *inlen = z; + return CRYPT_OK; + } + } + break; + + case LTC_ASN1_UTCTIME: + z = *inlen; + if (der_decode_utctime(in, &z, data) == CRYPT_OK) { + list[x].used = 1; + *inlen = z; + return CRYPT_OK; + } + break; + + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + case LTC_ASN1_SEQUENCE: + if (der_decode_sequence(in, *inlen, data, size) == CRYPT_OK) { + if (der_length_sequence(data, size, &z) == CRYPT_OK) { + list[x].used = 1; + *inlen = z; + return CRYPT_OK; + } + } + break; + + default: + return CRYPT_INVALID_ARG; + } + } + + return CRYPT_INVALID_PACKET; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/choice/der_decode_choice.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_ia5_string.c + ASN.1 DER, encode a IA5 STRING, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store a IA5 STRING + @param in The DER encoded IA5 STRING + @param inlen The size of the DER IA5 STRING + @param out [out] The array of octets stored (one per char) + @param outlen [in/out] The number of octets stored + @return CRYPT_OK if successful + */ +int der_decode_ia5_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long x, y, len; + int t; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* must have header at least */ + if (inlen < 2) { + return CRYPT_INVALID_PACKET; + } + + /* check for 0x16 */ + if ((in[0] & 0x1F) != 0x16) { + return CRYPT_INVALID_PACKET; + } + x = 1; + + /* decode the length */ + if (in[x] & 0x80) { + /* valid # of bytes in length are 1,2,3 */ + y = in[x] & 0x7F; + if ((y == 0) || (y > 3) || ((x + y) > inlen)) { + return CRYPT_INVALID_PACKET; + } + + /* read the length in */ + len = 0; + ++x; + while (y--) { + len = (len << 8) | in[x++]; + } + } else { + len = in[x++] & 0x7F; + } + + /* is it too long? */ + if (len > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + if (len + x > inlen) { + return CRYPT_INVALID_PACKET; + } + + /* read the data */ + for (y = 0; y < len; y++) { + t = der_ia5_value_decode(in[x++]); + if (t == -1) { + return CRYPT_INVALID_ARG; + } + out[y] = t; + } + + *outlen = y; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/ia5/der_decode_ia5_string.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_integer.c + ASN.1 DER, decode an integer, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Read a mp_int integer + @param in The DER encoded data + @param inlen Size of DER encoded data + @param num The first mp_int to decode + @return CRYPT_OK if successful + */ +int der_decode_integer(const unsigned char *in, unsigned long inlen, void *num) { + unsigned long x, y, z; + int err; + + LTC_ARGCHK(num != NULL); + LTC_ARGCHK(in != NULL); + + /* min DER INTEGER is 0x02 01 00 == 0 */ + if (inlen < (1 + 1 + 1)) { + return CRYPT_INVALID_PACKET; + } + + /* ok expect 0x02 when we AND with 0001 1111 [1F] */ + x = 0; + if ((in[x++] & 0x1F) != 0x02) { + return CRYPT_INVALID_PACKET; + } + + /* now decode the len stuff */ + z = in[x++]; + + if ((z & 0x80) == 0x00) { + /* short form */ + + /* will it overflow? */ + if (x + z > inlen) { + return CRYPT_INVALID_PACKET; + } + + /* no so read it */ + if ((err = mp_read_unsigned_bin(num, (unsigned char *)in + x, z)) != CRYPT_OK) { + return err; + } + } else { + /* long form */ + z &= 0x7F; + + /* will number of length bytes overflow? (or > 4) */ + if (((x + z) > inlen) || (z > 4) || (z == 0)) { + return CRYPT_INVALID_PACKET; + } + + /* now read it in */ + y = 0; + while (z--) { + y = ((unsigned long)(in[x++])) | (y << 8); + } + + /* now will reading y bytes overrun? */ + if ((x + y) > inlen) { + return CRYPT_INVALID_PACKET; + } + + /* no so read it */ + if ((err = mp_read_unsigned_bin(num, (unsigned char *)in + x, y)) != CRYPT_OK) { + return err; + } + } + + /* see if it's negative */ + if (in[x] & 0x80) { + void *tmp; + if (mp_init(&tmp) != CRYPT_OK) { + return CRYPT_MEM; + } + + if ((mp_2expt(tmp, mp_count_bits(num)) != CRYPT_OK) || (mp_sub(num, tmp, num) != CRYPT_OK)) { + mp_clear(tmp); + return CRYPT_MEM; + } + mp_clear(tmp); + } + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/integer/der_decode_integer.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_object_identifier.c + ASN.1 DER, Decode Object Identifier, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Decode OID data and store the array of integers in words + @param in The OID DER encoded data + @param inlen The length of the OID data + @param words [out] The destination of the OID words + @param outlen [in/out] The number of OID words + @return CRYPT_OK if successful + */ +int der_decode_object_identifier(const unsigned char *in, unsigned long inlen, + unsigned long *words, unsigned long *outlen) { + unsigned long x, y, t, len; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(words != NULL); + LTC_ARGCHK(outlen != NULL); + + /* header is at least 3 bytes */ + if (inlen < 3) { + return CRYPT_INVALID_PACKET; + } + + /* must be room for at least two words */ + if (*outlen < 2) { + return CRYPT_BUFFER_OVERFLOW; + } + + /* decode the packet header */ + x = 0; + if ((in[x++] & 0x1F) != 0x06) { + return CRYPT_INVALID_PACKET; + } + + /* get the length */ + if (in[x] < 128) { + len = in[x++]; + } else { + if ((in[x] < 0x81) || (in[x] > 0x82)) { + return CRYPT_INVALID_PACKET; + } + y = in[x++] & 0x7F; + len = 0; + while (y--) { + len = (len << 8) | (unsigned long)in[x++]; + } + } + + if ((len < 1) || ((len + x) > inlen)) { + return CRYPT_INVALID_PACKET; + } + + /* decode words */ + y = 0; + t = 0; + while (len--) { + t = (t << 7) | (in[x] & 0x7F); + if (!(in[x++] & 0x80)) { + /* store t */ + if (y >= *outlen) { + return CRYPT_BUFFER_OVERFLOW; + } + if (y == 0) { + words[0] = t / 40; + words[1] = t % 40; + y = 2; + } else { + words[y++] = t; + } + t = 0; + } + } + + *outlen = y; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/object_identifier/der_decode_object_identifier.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_octet_string.c + ASN.1 DER, encode a OCTET STRING, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store a OCTET STRING + @param in The DER encoded OCTET STRING + @param inlen The size of the DER OCTET STRING + @param out [out] The array of octets stored (one per char) + @param outlen [in/out] The number of octets stored + @return CRYPT_OK if successful + */ +int der_decode_octet_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long x, y, len; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* must have header at least */ + if (inlen < 2) { + return CRYPT_INVALID_PACKET; + } + + /* check for 0x04 */ + if ((in[0] & 0x1F) != 0x04) { + return CRYPT_INVALID_PACKET; + } + x = 1; + + /* decode the length */ + if (in[x] & 0x80) { + /* valid # of bytes in length are 1,2,3 */ + y = in[x] & 0x7F; + if ((y == 0) || (y > 3) || ((x + y) > inlen)) { + return CRYPT_INVALID_PACKET; + } + + /* read the length in */ + len = 0; + ++x; + while (y--) { + len = (len << 8) | in[x++]; + } + } else { + len = in[x++] & 0x7F; + } + + /* is it too long? */ + if (len > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + if (len + x > inlen) { + return CRYPT_INVALID_PACKET; + } + + /* read the data */ + for (y = 0; y < len; y++) { + out[y] = in[x++]; + } + + *outlen = y; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/octet/der_decode_octet_string.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_printable_string.c + ASN.1 DER, encode a printable STRING, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store a printable STRING + @param in The DER encoded printable STRING + @param inlen The size of the DER printable STRING + @param out [out] The array of octets stored (one per char) + @param outlen [in/out] The number of octets stored + @return CRYPT_OK if successful + */ +int der_decode_printable_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long x, y, len; + int t; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* must have header at least */ + if (inlen < 2) { + return CRYPT_INVALID_PACKET; + } + + /* check for 0x13 */ + if ((in[0] & 0x1F) != 0x13) { + return CRYPT_INVALID_PACKET; + } + x = 1; + + /* decode the length */ + if (in[x] & 0x80) { + /* valid # of bytes in length are 1,2,3 */ + y = in[x] & 0x7F; + if ((y == 0) || (y > 3) || ((x + y) > inlen)) { + return CRYPT_INVALID_PACKET; + } + + /* read the length in */ + len = 0; + ++x; + while (y--) { + len = (len << 8) | in[x++]; + } + } else { + len = in[x++] & 0x7F; + } + + /* is it too long? */ + if (len > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + if (len + x > inlen) { + return CRYPT_INVALID_PACKET; + } + + /* read the data */ + for (y = 0; y < len; y++) { + t = der_printable_value_decode(in[x++]); + if (t == -1) { + return CRYPT_INVALID_ARG; + } + out[y] = t; + } + + *outlen = y; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/printable_string/der_decode_printable_string.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#include + + +/** + @file der_decode_sequence_ex.c + ASN.1 DER, decode a SEQUENCE, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Decode a SEQUENCE + @param in The DER encoded input + @param inlen The size of the input + @param list The list of items to decode + @param outlen The number of items in the list + @param ordered Search an unordered or ordered list + @return CRYPT_OK on success + */ +int der_decode_sequence_ex(const unsigned char *in, unsigned long inlen, + ltc_asn1_list *list, unsigned long outlen, int ordered) { + int err, type; + unsigned long size, x, y, z, i, blksize; + void *data; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(list != NULL); + + /* get blk size */ + if (inlen < 2) { + return CRYPT_INVALID_PACKET; + } + + /* sequence type? We allow 0x30 SEQUENCE and 0x31 SET since fundamentally they're the same structure */ + x = 0; + if ((in[x] != 0x30) && (in[x] != 0x31)) { + return CRYPT_INVALID_PACKET; + } + ++x; + + if (in[x] < 128) { + blksize = in[x++]; + } else if (in[x] & 0x80) { + if ((in[x] < 0x81) || (in[x] > 0x83)) { + return CRYPT_INVALID_PACKET; + } + y = in[x++] & 0x7F; + + /* would reading the len bytes overrun? */ + if (x + y > inlen) { + return CRYPT_INVALID_PACKET; + } + + /* read len */ + blksize = 0; + while (y--) { + blksize = (blksize << 8) | (unsigned long)in[x++]; + } + } + + /* would this blksize overflow? */ + if (x + blksize > inlen) { + return CRYPT_INVALID_PACKET; + } + + /* mark all as unused */ + for (i = 0; i < outlen; i++) { + list[i].used = 0; + } + + /* ok read data */ + inlen = blksize; + for (i = 0; i < outlen; i++) { + z = 0; + type = list[i].type; + size = list[i].size; + data = list[i].data; + if (!ordered && (list[i].used == 1)) { + continue; + } + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + z = inlen; + if ((err = der_decode_boolean(in + x, z, ((int *)data))) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = der_length_boolean(&z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + case LTC_ASN1_INTEGER: + z = inlen; + if ((err = der_decode_integer(in + x, z, data)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + if ((err = der_length_integer(data, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + case LTC_ASN1_SHORT_INTEGER: + z = inlen; + if ((err = der_decode_short_integer(in + x, z, data)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + if ((err = der_length_short_integer(((unsigned long *)data)[0], &z)) != CRYPT_OK) { + goto LBL_ERR; + } + + break; + + case LTC_ASN1_BIT_STRING: + z = inlen; + if ((err = der_decode_bit_string(in + x, z, data, &size)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + list[i].size = size; + if ((err = der_length_bit_string(size, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + case LTC_ASN1_OCTET_STRING: + z = inlen; + if ((err = der_decode_octet_string(in + x, z, data, &size)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + list[i].size = size; + if ((err = der_length_octet_string(size, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + case LTC_ASN1_NULL: + if ((inlen < 2) || (in[x] != 0x05) || (in[x + 1] != 0x00)) { + if (!ordered) { + continue; + } + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } + z = 2; + break; + + case LTC_ASN1_OBJECT_IDENTIFIER: + z = inlen; + if ((err = der_decode_object_identifier(in + x, z, data, &size)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + list[i].size = size; + if ((err = der_length_object_identifier(data, size, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + case LTC_ASN1_IA5_STRING: + z = inlen; + if ((err = der_decode_ia5_string(in + x, z, data, &size)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + list[i].size = size; + if ((err = der_length_ia5_string(data, size, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + + case LTC_ASN1_PRINTABLE_STRING: + z = inlen; + if ((err = der_decode_printable_string(in + x, z, data, &size)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + list[i].size = size; + if ((err = der_length_printable_string(data, size, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + case LTC_ASN1_UTF8_STRING: + z = inlen; + if ((err = der_decode_utf8_string(in + x, z, data, &size)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + list[i].size = size; + if ((err = der_length_utf8_string(data, size, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + case LTC_ASN1_UTCTIME: + z = inlen; + if ((err = der_decode_utctime(in + x, &z, data)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + break; + + case LTC_ASN1_SET: + z = inlen; + if ((err = der_decode_set(in + x, z, data, size)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + if ((err = der_length_sequence(data, size, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + case LTC_ASN1_SETOF: + case LTC_ASN1_SEQUENCE: + /* detect if we have the right type */ + if (((type == LTC_ASN1_SETOF) && ((in[x] & 0x3F) != 0x31)) || ((type == LTC_ASN1_SEQUENCE) && ((in[x] & 0x3F) != 0x30))) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } + + z = inlen; + if ((err = der_decode_sequence(in + x, z, data, size)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + if ((err = der_length_sequence(data, size, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + break; + + + case LTC_ASN1_CHOICE: + z = inlen; + if ((err = der_decode_choice(in + x, &z, data, size)) != CRYPT_OK) { + if (!ordered) { + continue; + } + goto LBL_ERR; + } + break; + + default: + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + x += z; + inlen -= z; + list[i].used = 1; + if (!ordered) { + /* restart the decoder */ + i = -1; + } + } + + for (i = 0; i < outlen; i++) { + if (list[i].used == 0) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } + } + err = CRYPT_OK; + +LBL_ERR: + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_ex.c,v $ */ +/* $Revision: 1.16 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_sequence_flexi.c + ASN.1 DER, decode an array of ASN.1 types with a flexi parser, Tom St Denis + */ + +#ifdef LTC_DER + +static unsigned long fetch_length(const unsigned char *in, unsigned long inlen) { + unsigned long x, y, z; + + y = 0; + + /* skip type and read len */ + if (inlen < 2) { + return 0xFFFFFFFF; + } + ++in; + ++y; + + /* read len */ + x = *in++; + ++y; + + /* <128 means literal */ + if (x < 128) { + return x + y; + } + x &= 0x7F; /* the lower 7 bits are the length of the length */ + inlen -= 2; + + /* len means len of len! */ + if ((x == 0) || (x > 4) || (x > inlen)) { + return 0xFFFFFFFF; + } + + y += x; + z = 0; + while (x--) { + z = (z << 8) | ((unsigned long)*in); + ++in; + } + return z + y; +} + +/** + ASN.1 DER Flexi(ble) decoder will decode arbitrary DER packets and create a linked list of the decoded elements. + @param in The input buffer + @param inlen [in/out] The length of the input buffer and on output the amount of decoded data + @param out [out] A pointer to the linked list + @return CRYPT_OK on success. + */ +int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out) { + ltc_asn1_list *l; + unsigned long err, type, len, totlen, x, y; + void *realloc_tmp; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(inlen != NULL); + LTC_ARGCHK(out != NULL); + + l = NULL; + totlen = 0; + + /* scan the input and and get lengths and what not */ + while (*inlen) { + /* read the type byte */ + type = *in; + + /* fetch length */ + len = fetch_length(in, *inlen); + if (len > *inlen) { + err = CRYPT_INVALID_PACKET; + goto error; + } + + /* alloc new link */ + if (l == NULL) { + l = XCALLOC(1, sizeof(*l)); + if (l == NULL) { + err = CRYPT_MEM; + goto error; + } + } else { + l->next = XCALLOC(1, sizeof(*l)); + if (l->next == NULL) { + err = CRYPT_MEM; + goto error; + } + l->next->prev = l; + l = l->next; + } + + /* now switch on type */ + switch (type) { + case 0x01: /* BOOLEAN */ + l->type = LTC_ASN1_BOOLEAN; + l->size = 1; + l->data = XCALLOC(1, sizeof(int)); + + if ((err = der_decode_boolean(in, *inlen, l->data)) != CRYPT_OK) { + goto error; + } + + if ((err = der_length_boolean(&len)) != CRYPT_OK) { + goto error; + } + break; + + case 0x02: /* INTEGER */ + /* init field */ + l->type = LTC_ASN1_INTEGER; + l->size = 1; + if ((err = mp_init(&l->data)) != CRYPT_OK) { + goto error; + } + + /* decode field */ + if ((err = der_decode_integer(in, *inlen, l->data)) != CRYPT_OK) { + goto error; + } + + /* calc length of object */ + if ((err = der_length_integer(l->data, &len)) != CRYPT_OK) { + goto error; + } + break; + + case 0x03: /* BIT */ + /* init field */ + l->type = LTC_ASN1_BIT_STRING; + l->size = len * 8; /* *8 because we store decoded bits one per char and they are encoded 8 per char. */ + + if ((l->data = XCALLOC(1, l->size)) == NULL) { + err = CRYPT_MEM; + goto error; + } + + if ((err = der_decode_bit_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { + goto error; + } + + if ((err = der_length_bit_string(l->size, &len)) != CRYPT_OK) { + goto error; + } + break; + + case 0x04: /* OCTET */ + + /* init field */ + l->type = LTC_ASN1_OCTET_STRING; + l->size = len; + + if ((l->data = XCALLOC(1, l->size)) == NULL) { + err = CRYPT_MEM; + goto error; + } + + if ((err = der_decode_octet_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { + goto error; + } + + if ((err = der_length_octet_string(l->size, &len)) != CRYPT_OK) { + goto error; + } + break; + + case 0x05: /* NULL */ + + /* valid NULL is 0x05 0x00 */ + if ((in[0] != 0x05) || (in[1] != 0x00)) { + err = CRYPT_INVALID_PACKET; + goto error; + } + + /* simple to store ;-) */ + l->type = LTC_ASN1_NULL; + l->data = NULL; + l->size = 0; + len = 2; + + break; + + case 0x06: /* OID */ + + /* init field */ + l->type = LTC_ASN1_OBJECT_IDENTIFIER; + l->size = len; + + if ((l->data = XCALLOC(len, sizeof(unsigned long))) == NULL) { + err = CRYPT_MEM; + goto error; + } + + if ((err = der_decode_object_identifier(in, *inlen, l->data, &l->size)) != CRYPT_OK) { + goto error; + } + + if ((err = der_length_object_identifier(l->data, l->size, &len)) != CRYPT_OK) { + goto error; + } + + /* resize it to save a bunch of mem */ + if ((realloc_tmp = XREALLOC(l->data, l->size * sizeof(unsigned long))) == NULL) { + /* out of heap but this is not an error */ + break; + } + l->data = realloc_tmp; + break; + + case 0x0C: /* UTF8 */ + + /* init field */ + l->type = LTC_ASN1_UTF8_STRING; + l->size = len; + + if ((l->data = XCALLOC(sizeof(wchar_t), l->size)) == NULL) { + err = CRYPT_MEM; + goto error; + } + + if ((err = der_decode_utf8_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { + goto error; + } + + if ((err = der_length_utf8_string(l->data, l->size, &len)) != CRYPT_OK) { + goto error; + } + break; + + case 0x13: /* PRINTABLE */ + + /* init field */ + l->type = LTC_ASN1_PRINTABLE_STRING; + l->size = len; + + if ((l->data = XCALLOC(1, l->size)) == NULL) { + err = CRYPT_MEM; + goto error; + } + + if ((err = der_decode_printable_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { + goto error; + } + + if ((err = der_length_printable_string(l->data, l->size, &len)) != CRYPT_OK) { + goto error; + } + break; + + case 0x16: /* IA5 */ + + /* init field */ + l->type = LTC_ASN1_IA5_STRING; + l->size = len; + + if ((l->data = XCALLOC(1, l->size)) == NULL) { + err = CRYPT_MEM; + goto error; + } + + if ((err = der_decode_ia5_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { + goto error; + } + + if ((err = der_length_ia5_string(l->data, l->size, &len)) != CRYPT_OK) { + goto error; + } + break; + + case 0x17: /* UTC TIME */ + + /* init field */ + l->type = LTC_ASN1_UTCTIME; + l->size = 1; + + if ((l->data = XCALLOC(1, sizeof(ltc_utctime))) == NULL) { + err = CRYPT_MEM; + goto error; + } + + len = *inlen; + if ((err = der_decode_utctime(in, &len, l->data)) != CRYPT_OK) { + goto error; + } + + if ((err = der_length_utctime(l->data, &len)) != CRYPT_OK) { + goto error; + } + break; + + case 0x30: /* SEQUENCE */ + case 0x31: /* SET */ + + /* init field */ + l->type = (type == 0x30) ? LTC_ASN1_SEQUENCE : LTC_ASN1_SET; + + /* we have to decode the SEQUENCE header and get it's length */ + + /* move past type */ + ++in; + --(*inlen); + + /* read length byte */ + x = *in++; + --(*inlen); + + /* smallest SEQUENCE/SET header */ + y = 2; + + /* now if it's > 127 the next bytes are the length of the length */ + if (x > 128) { + x &= 0x7F; + in += x; + *inlen -= x; + + /* update sequence header len */ + y += x; + } + + /* Sequence elements go as child */ + len = len - y; + if ((err = der_decode_sequence_flexi(in, &len, &(l->child))) != CRYPT_OK) { + goto error; + } + + /* len update */ + totlen += y; + + /* link them up y0 */ + l->child->parent = l; + + break; + + default: + /* invalid byte ... this is a soft error */ + /* remove link */ + l = l->prev; + XFREE(l->next); + l->next = NULL; + goto outside; + } + + /* advance pointers */ + totlen += len; + in += len; + *inlen -= len; + } + +outside: + + /* rewind l please */ + while (l->prev != NULL || l->parent != NULL) { + if (l->parent != NULL) { + l = l->parent; + } else { + l = l->prev; + } + } + + /* return */ + *out = l; + *inlen = totlen; + return CRYPT_OK; + +error: + /* free list */ + der_sequence_free(l); + + return err; +} +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c,v $ */ +/* $Revision: 1.26 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#include + + +/** + @file der_decode_sequence_multi.c + ASN.1 DER, decode a SEQUENCE, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Decode a SEQUENCE type using a VA list + @param in Input buffer + @param inlen Length of input in octets + @remark <...> is of the form (int, unsigned long, void*) + @return CRYPT_OK on success + */ +int der_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...) { + int err, type; + unsigned long size, x; + void *data; + va_list args; + ltc_asn1_list *list; + + LTC_ARGCHK(in != NULL); + + /* get size of output that will be required */ + va_start(args, inlen); + x = 0; + for ( ; ; ) { + type = va_arg(args, int); + size = va_arg(args, unsigned long); + data = va_arg(args, void *); + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + case LTC_ASN1_INTEGER: + case LTC_ASN1_SHORT_INTEGER: + case LTC_ASN1_BIT_STRING: + case LTC_ASN1_OCTET_STRING: + case LTC_ASN1_NULL: + case LTC_ASN1_OBJECT_IDENTIFIER: + case LTC_ASN1_IA5_STRING: + case LTC_ASN1_PRINTABLE_STRING: + case LTC_ASN1_UTF8_STRING: + case LTC_ASN1_UTCTIME: + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + case LTC_ASN1_SEQUENCE: + case LTC_ASN1_CHOICE: + ++x; + break; + + default: + va_end(args); + return CRYPT_INVALID_ARG; + } + } + va_end(args); + + /* allocate structure for x elements */ + if (x == 0) { + return CRYPT_NOP; + } + + list = XCALLOC(sizeof(*list), x); + if (list == NULL) { + return CRYPT_MEM; + } + + /* fill in the structure */ + va_start(args, inlen); + x = 0; + for ( ; ; ) { + type = va_arg(args, int); + size = va_arg(args, unsigned long); + data = va_arg(args, void *); + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + case LTC_ASN1_INTEGER: + case LTC_ASN1_SHORT_INTEGER: + case LTC_ASN1_BIT_STRING: + case LTC_ASN1_OCTET_STRING: + case LTC_ASN1_NULL: + case LTC_ASN1_OBJECT_IDENTIFIER: + case LTC_ASN1_IA5_STRING: + case LTC_ASN1_PRINTABLE_STRING: + case LTC_ASN1_UTF8_STRING: + case LTC_ASN1_UTCTIME: + case LTC_ASN1_SEQUENCE: + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + case LTC_ASN1_CHOICE: + list[x].type = type; + list[x].size = size; + list[x++].data = data; + break; + + default: + va_end(args); + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + } + va_end(args); + + err = der_decode_sequence(in, inlen, list, x); +LBL_ERR: + XFREE(list); + return err; +} +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_multi.c,v $ */ +/* $Revision: 1.13 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_short_integer.c + ASN.1 DER, decode an integer, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Read a short integer + @param in The DER encoded data + @param inlen Size of data + @param num [out] The integer to decode + @return CRYPT_OK if successful + */ +int der_decode_short_integer(const unsigned char *in, unsigned long inlen, unsigned long *num) { + unsigned long len, x, y; + + LTC_ARGCHK(num != NULL); + LTC_ARGCHK(in != NULL); + + /* check length */ + if (inlen < 2) { + return CRYPT_INVALID_PACKET; + } + + /* check header */ + x = 0; + if ((in[x++] & 0x1F) != 0x02) { + return CRYPT_INVALID_PACKET; + } + + /* get the packet len */ + len = in[x++]; + + if (x + len > inlen) { + return CRYPT_INVALID_PACKET; + } + + /* read number */ + y = 0; + while (len--) { + y = (y << 8) | (unsigned long)in[x++]; + } + *num = y; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/short_integer/der_decode_short_integer.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_utctime.c + ASN.1 DER, decode a UTCTIME, Tom St Denis + */ + +#ifdef LTC_DER + +static int char_to_int(unsigned char x) { + switch (x) { + case '0': + return 0; + + case '1': + return 1; + + case '2': + return 2; + + case '3': + return 3; + + case '4': + return 4; + + case '5': + return 5; + + case '6': + return 6; + + case '7': + return 7; + + case '8': + return 8; + + case '9': + return 9; + } + return 100; +} + + #define DECODE_V(y, max) \ + y = char_to_int(buf[x]) * 10 + char_to_int(buf[x + 1]); \ + if (y >= max) return CRYPT_INVALID_PACKET; \ + x += 2; + +/** + Decodes a UTC time structure in DER format (reads all 6 valid encoding formats) + @param in Input buffer + @param inlen Length of input buffer in octets + @param out [out] Destination of UTC time structure + @return CRYPT_OK if successful + */ +int der_decode_utctime(const unsigned char *in, unsigned long *inlen, + ltc_utctime *out) { + unsigned char buf[32]; + unsigned long x; + int y; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(inlen != NULL); + LTC_ARGCHK(out != NULL); + + /* check header */ + if ((*inlen < 2UL) || (in[1] >= sizeof(buf)) || ((in[1] + 2UL) > *inlen)) { + return CRYPT_INVALID_PACKET; + } + + /* decode the string */ + for (x = 0; x < in[1]; x++) { + y = der_ia5_value_decode(in[x + 2]); + if (y == -1) { + return CRYPT_INVALID_PACKET; + } + buf[x] = y; + } + *inlen = 2 + x; + + + /* possible encodings are + YYMMDDhhmmZ + YYMMDDhhmm+hh'mm' + YYMMDDhhmm-hh'mm' + YYMMDDhhmmssZ + YYMMDDhhmmss+hh'mm' + YYMMDDhhmmss-hh'mm' + + So let's do a trivial decode upto [including] mm + */ + + x = 0; + DECODE_V(out->YY, 100); + DECODE_V(out->MM, 13); + DECODE_V(out->DD, 32); + DECODE_V(out->hh, 24); + DECODE_V(out->mm, 60); + + /* clear timezone and seconds info */ + out->off_dir = out->off_hh = out->off_mm = out->ss = 0; + + /* now is it Z, +, - or 0-9 */ + if (buf[x] == 'Z') { + return CRYPT_OK; + } else if ((buf[x] == '+') || (buf[x] == '-')) { + out->off_dir = (buf[x++] == '+') ? 0 : 1; + DECODE_V(out->off_hh, 24); + DECODE_V(out->off_mm, 60); + return CRYPT_OK; + } + + /* decode seconds */ + DECODE_V(out->ss, 60); + + /* now is it Z, +, - */ + if (buf[x] == 'Z') { + return CRYPT_OK; + } else if ((buf[x] == '+') || (buf[x] == '-')) { + out->off_dir = (buf[x++] == '+') ? 0 : 1; + DECODE_V(out->off_hh, 24); + DECODE_V(out->off_mm, 60); + return CRYPT_OK; + } else { + return CRYPT_INVALID_PACKET; + } +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utctime/der_decode_utctime.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_decode_utf8_string.c + ASN.1 DER, encode a UTF8 STRING, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store a UTF8 STRING + @param in The DER encoded UTF8 STRING + @param inlen The size of the DER UTF8 STRING + @param out [out] The array of utf8s stored (one per char) + @param outlen [in/out] The number of utf8s stored + @return CRYPT_OK if successful + */ +int der_decode_utf8_string(const unsigned char *in, unsigned long inlen, + wchar_t *out, unsigned long *outlen) { + wchar_t tmp; + unsigned long x, y, z, len; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* must have header at least */ + if (inlen < 2) { + return CRYPT_INVALID_PACKET; + } + + /* check for 0x0C */ + if ((in[0] & 0x1F) != 0x0C) { + return CRYPT_INVALID_PACKET; + } + x = 1; + + /* decode the length */ + if (in[x] & 0x80) { + /* valid # of bytes in length are 1,2,3 */ + y = in[x] & 0x7F; + if ((y == 0) || (y > 3) || ((x + y) > inlen)) { + return CRYPT_INVALID_PACKET; + } + + /* read the length in */ + len = 0; + ++x; + while (y--) { + len = (len << 8) | in[x++]; + } + } else { + len = in[x++] & 0x7F; + } + + if (len + x > inlen) { + return CRYPT_INVALID_PACKET; + } + + /* proceed to decode */ + for (y = 0; x < inlen; ) { + /* get first byte */ + tmp = in[x++]; + + /* count number of bytes */ + for (z = 0; (tmp & 0x80) && (z <= 4); z++, tmp = (tmp << 1) & 0xFF); + + if ((z > 4) || (x + (z - 1) > inlen)) { + return CRYPT_INVALID_PACKET; + } + + /* decode, grab upper bits */ + tmp >>= z; + + /* grab remaining bytes */ + if (z > 1) { + --z; + } + while (z-- != 0) { + if ((in[x] & 0xC0) != 0x80) { + return CRYPT_INVALID_PACKET; + } + tmp = (tmp << 6) | ((wchar_t)in[x++] & 0x3F); + } + + if (y > *outlen) { + *outlen = y; + return CRYPT_BUFFER_OVERFLOW; + } + out[y++] = tmp; + } + *outlen = y; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utf8/der_decode_utf8_string.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_bit_string.c + ASN.1 DER, encode a BIT STRING, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store a BIT STRING + @param in The array of bits to store (one per char) + @param inlen The number of bits tostore + @param out [out] The destination for the DER encoded BIT STRING + @param outlen [in/out] The max size and resulting size of the DER BIT STRING + @return CRYPT_OK if successful + */ +int der_encode_bit_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long len, x, y; + unsigned char buf; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* avoid overflows */ + if ((err = der_length_bit_string(inlen, &len)) != CRYPT_OK) { + return err; + } + + if (len > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + /* store header (include bit padding count in length) */ + x = 0; + y = (inlen >> 3) + ((inlen & 7) ? 1 : 0) + 1; + + out[x++] = 0x03; + if (y < 128) { + out[x++] = (unsigned char)y; + } else if (y < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)y; + } else if (y < 65536) { + out[x++] = 0x82; + out[x++] = (unsigned char)((y >> 8) & 255); + out[x++] = (unsigned char)(y & 255); + } + + /* store number of zero padding bits */ + out[x++] = (unsigned char)((8 - inlen) & 7); + + /* store the bits in big endian format */ + for (y = buf = 0; y < inlen; y++) { + buf |= (in[y] ? 1 : 0) << (7 - (y & 7)); + if ((y & 7) == 7) { + out[x++] = buf; + buf = 0; + } + } + /* store last byte */ + if (inlen & 7) { + out[x++] = buf; + } + *outlen = x; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/bit/der_encode_bit_string.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_boolean.c + ASN.1 DER, encode a BOOLEAN, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store a BOOLEAN + @param in The boolean to encode + @param out [out] The destination for the DER encoded BOOLEAN + @param outlen [in/out] The max size and resulting size of the DER BOOLEAN + @return CRYPT_OK if successful + */ +int der_encode_boolean(int in, + unsigned char *out, unsigned long *outlen) { + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(out != NULL); + + if (*outlen < 3) { + *outlen = 3; + return CRYPT_BUFFER_OVERFLOW; + } + + *outlen = 3; + out[0] = 0x01; + out[1] = 0x01; + out[2] = in ? 0xFF : 0x00; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/boolean/der_encode_boolean.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_ia5_string.c + ASN.1 DER, encode a IA5 STRING, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Store an IA5 STRING + @param in The array of IA5 to store (one per char) + @param inlen The number of IA5 to store + @param out [out] The destination for the DER encoded IA5 STRING + @param outlen [in/out] The max size and resulting size of the DER IA5 STRING + @return CRYPT_OK if successful + */ +int der_encode_ia5_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long x, y, len; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get the size */ + if ((err = der_length_ia5_string(in, inlen, &len)) != CRYPT_OK) { + return err; + } + + /* too big? */ + if (len > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + /* encode the header+len */ + x = 0; + out[x++] = 0x16; + if (inlen < 128) { + out[x++] = (unsigned char)inlen; + } else if (inlen < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)inlen; + } else if (inlen < 65536UL) { + out[x++] = 0x82; + out[x++] = (unsigned char)((inlen >> 8) & 255); + out[x++] = (unsigned char)(inlen & 255); + } else if (inlen < 16777216UL) { + out[x++] = 0x83; + out[x++] = (unsigned char)((inlen >> 16) & 255); + out[x++] = (unsigned char)((inlen >> 8) & 255); + out[x++] = (unsigned char)(inlen & 255); + } else { + return CRYPT_INVALID_ARG; + } + + /* store octets */ + for (y = 0; y < inlen; y++) { + out[x++] = der_ia5_char_encode(in[y]); + } + + /* retun length */ + *outlen = x; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/ia5/der_encode_ia5_string.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_integer.c + ASN.1 DER, encode an integer, Tom St Denis + */ + + +#ifdef LTC_DER + +/* Exports a positive bignum as DER format (upto 2^32 bytes in size) */ + +/** + Store a mp_int integer + @param num The first mp_int to encode + @param out [out] The destination for the DER encoded integers + @param outlen [in/out] The max size and resulting size of the DER encoded integers + @return CRYPT_OK if successful + */ +int der_encode_integer(void *num, unsigned char *out, unsigned long *outlen) { + unsigned long tmplen, y; + int err, leading_zero; + + LTC_ARGCHK(num != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* find out how big this will be */ + if ((err = der_length_integer(num, &tmplen)) != CRYPT_OK) { + return err; + } + + if (*outlen < tmplen) { + *outlen = tmplen; + return CRYPT_BUFFER_OVERFLOW; + } + + if (mp_cmp_d(num, 0) != LTC_MP_LT) { + /* we only need a leading zero if the msb of the first byte is one */ + if (((mp_count_bits(num) & 7) == 0) || (mp_iszero(num) == LTC_MP_YES)) { + leading_zero = 1; + } else { + leading_zero = 0; + } + + /* get length of num in bytes (plus 1 since we force the msbyte to zero) */ + y = mp_unsigned_bin_size(num) + leading_zero; + } else { + leading_zero = 0; + y = mp_count_bits(num); + y = y + (8 - (y & 7)); + y = y >> 3; + if (((mp_cnt_lsb(num) + 1) == mp_count_bits(num)) && ((mp_count_bits(num) & 7) == 0)) --y; + } + + /* now store initial data */ + *out++ = 0x02; + if (y < 128) { + /* short form */ + *out++ = (unsigned char)y; + } else if (y < 256) { + *out++ = 0x81; + *out++ = (unsigned char)y; + } else if (y < 65536UL) { + *out++ = 0x82; + *out++ = (unsigned char)((y >> 8) & 255); + *out++ = (unsigned char)y; + } else if (y < 16777216UL) { + *out++ = 0x83; + *out++ = (unsigned char)((y >> 16) & 255); + *out++ = (unsigned char)((y >> 8) & 255); + *out++ = (unsigned char)y; + } else { + return CRYPT_INVALID_ARG; + } + + /* now store msbyte of zero if num is non-zero */ + if (leading_zero) { + *out++ = 0x00; + } + + /* if it's not zero store it as big endian */ + if (mp_cmp_d(num, 0) == LTC_MP_GT) { + /* now store the mpint */ + if ((err = mp_to_unsigned_bin(num, out)) != CRYPT_OK) { + return err; + } + } else if (mp_iszero(num) != LTC_MP_YES) { + void *tmp; + + /* negative */ + if (mp_init(&tmp) != CRYPT_OK) { + return CRYPT_MEM; + } + + /* 2^roundup and subtract */ + y = mp_count_bits(num); + y = y + (8 - (y & 7)); + if (((mp_cnt_lsb(num) + 1) == mp_count_bits(num)) && ((mp_count_bits(num) & 7) == 0)) y -= 8; + if ((mp_2expt(tmp, y) != CRYPT_OK) || (mp_add(tmp, num, tmp) != CRYPT_OK)) { + mp_clear(tmp); + return CRYPT_MEM; + } + if ((err = mp_to_unsigned_bin(tmp, out)) != CRYPT_OK) { + mp_clear(tmp); + return err; + } + mp_clear(tmp); + } + + /* we good */ + *outlen = tmplen; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/integer/der_encode_integer.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_object_identifier.c + ASN.1 DER, Encode Object Identifier, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Encode an OID + @param words The words to encode (upto 32-bits each) + @param nwords The number of words in the OID + @param out [out] Destination of OID data + @param outlen [in/out] The max and resulting size of the OID + @return CRYPT_OK if successful + */ +int der_encode_object_identifier(unsigned long *words, unsigned long nwords, + unsigned char *out, unsigned long *outlen) { + unsigned long i, x, y, z, t, mask, wordbuf; + int err; + + LTC_ARGCHK(words != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* check length */ + if ((err = der_length_object_identifier(words, nwords, &x)) != CRYPT_OK) { + return err; + } + if (x > *outlen) { + *outlen = x; + return CRYPT_BUFFER_OVERFLOW; + } + + /* compute length to store OID data */ + z = 0; + wordbuf = words[0] * 40 + words[1]; + for (y = 1; y < nwords; y++) { + t = der_object_identifier_bits(wordbuf); + z += t / 7 + ((t % 7) ? 1 : 0) + (wordbuf == 0 ? 1 : 0); + if (y < nwords - 1) { + wordbuf = words[y + 1]; + } + } + + /* store header + length */ + x = 0; + out[x++] = 0x06; + if (z < 128) { + out[x++] = (unsigned char)z; + } else if (z < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)z; + } else if (z < 65536UL) { + out[x++] = 0x82; + out[x++] = (unsigned char)((z >> 8) & 255); + out[x++] = (unsigned char)(z & 255); + } else { + return CRYPT_INVALID_ARG; + } + + /* store first byte */ + wordbuf = words[0] * 40 + words[1]; + for (i = 1; i < nwords; i++) { + /* store 7 bit words in little endian */ + t = wordbuf & 0xFFFFFFFF; + if (t) { + y = x; + mask = 0; + while (t) { + out[x++] = (unsigned char)((t & 0x7F) | mask); + t >>= 7; + mask |= 0x80; /* upper bit is set on all but the last byte */ + } + /* now swap bytes y...x-1 */ + z = x - 1; + while (y < z) { + t = out[y]; + out[y] = out[z]; + out[z] = (unsigned char)t; + ++y; + --z; + } + } else { + /* zero word */ + out[x++] = 0x00; + } + + if (i < nwords - 1) { + wordbuf = words[i + 1]; + } + } + + *outlen = x; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/object_identifier/der_encode_object_identifier.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_octet_string.c + ASN.1 DER, encode a OCTET STRING, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store an OCTET STRING + @param in The array of OCTETS to store (one per char) + @param inlen The number of OCTETS to store + @param out [out] The destination for the DER encoded OCTET STRING + @param outlen [in/out] The max size and resulting size of the DER OCTET STRING + @return CRYPT_OK if successful + */ +int der_encode_octet_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long x, y, len; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get the size */ + if ((err = der_length_octet_string(inlen, &len)) != CRYPT_OK) { + return err; + } + + /* too big? */ + if (len > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + /* encode the header+len */ + x = 0; + out[x++] = 0x04; + if (inlen < 128) { + out[x++] = (unsigned char)inlen; + } else if (inlen < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)inlen; + } else if (inlen < 65536UL) { + out[x++] = 0x82; + out[x++] = (unsigned char)((inlen >> 8) & 255); + out[x++] = (unsigned char)(inlen & 255); + } else if (inlen < 16777216UL) { + out[x++] = 0x83; + out[x++] = (unsigned char)((inlen >> 16) & 255); + out[x++] = (unsigned char)((inlen >> 8) & 255); + out[x++] = (unsigned char)(inlen & 255); + } else { + return CRYPT_INVALID_ARG; + } + + /* store octets */ + for (y = 0; y < inlen; y++) { + out[x++] = in[y]; + } + + /* retun length */ + *outlen = x; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/octet/der_encode_octet_string.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_printable_string.c + ASN.1 DER, encode a printable STRING, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Store an printable STRING + @param in The array of printable to store (one per char) + @param inlen The number of printable to store + @param out [out] The destination for the DER encoded printable STRING + @param outlen [in/out] The max size and resulting size of the DER printable STRING + @return CRYPT_OK if successful + */ +int der_encode_printable_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long x, y, len; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get the size */ + if ((err = der_length_printable_string(in, inlen, &len)) != CRYPT_OK) { + return err; + } + + /* too big? */ + if (len > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + /* encode the header+len */ + x = 0; + out[x++] = 0x13; + if (inlen < 128) { + out[x++] = (unsigned char)inlen; + } else if (inlen < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)inlen; + } else if (inlen < 65536UL) { + out[x++] = 0x82; + out[x++] = (unsigned char)((inlen >> 8) & 255); + out[x++] = (unsigned char)(inlen & 255); + } else if (inlen < 16777216UL) { + out[x++] = 0x83; + out[x++] = (unsigned char)((inlen >> 16) & 255); + out[x++] = (unsigned char)((inlen >> 8) & 255); + out[x++] = (unsigned char)(inlen & 255); + } else { + return CRYPT_INVALID_ARG; + } + + /* store octets */ + for (y = 0; y < inlen; y++) { + out[x++] = der_printable_char_encode(in[y]); + } + + /* retun length */ + *outlen = x; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/printable_string/der_encode_printable_string.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#include + + +/** + @file der_encode_sequence_ex.c + ASN.1 DER, encode a SEQUENCE, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Encode a SEQUENCE + @param list The list of items to encode + @param inlen The number of items in the list + @param out [out] The destination + @param outlen [in/out] The size of the output + @param type_of LTC_ASN1_SEQUENCE or LTC_ASN1_SET/LTC_ASN1_SETOF + @return CRYPT_OK on success + */ +int der_encode_sequence_ex(ltc_asn1_list *list, unsigned long inlen, + unsigned char *out, unsigned long *outlen, int type_of) { + int err, type; + unsigned long size, x, y, z, i; + void *data; + + LTC_ARGCHK(list != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get size of output that will be required */ + y = 0; + for (i = 0; i < inlen; i++) { + type = list[i].type; + size = list[i].size; + data = list[i].data; + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + if ((err = der_length_boolean(&x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_INTEGER: + if ((err = der_length_integer(data, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_SHORT_INTEGER: + if ((err = der_length_short_integer(*((unsigned long *)data), &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_BIT_STRING: + if ((err = der_length_bit_string(size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_OCTET_STRING: + if ((err = der_length_octet_string(size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_NULL: + y += 2; + break; + + case LTC_ASN1_OBJECT_IDENTIFIER: + if ((err = der_length_object_identifier(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_IA5_STRING: + if ((err = der_length_ia5_string(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_PRINTABLE_STRING: + if ((err = der_length_printable_string(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_UTF8_STRING: + if ((err = der_length_utf8_string(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_UTCTIME: + if ((err = der_length_utctime(data, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + case LTC_ASN1_SEQUENCE: + if ((err = der_length_sequence(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + default: + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + } + + /* calc header size */ + z = y; + if (y < 128) { + y += 2; + } else if (y < 256) { + /* 0x30 0x81 LL */ + y += 3; + } else if (y < 65536UL) { + /* 0x30 0x82 LL LL */ + y += 4; + } else if (y < 16777216UL) { + /* 0x30 0x83 LL LL LL */ + y += 5; + } else { + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + + /* too big ? */ + if (*outlen < y) { + *outlen = y; + err = CRYPT_BUFFER_OVERFLOW; + goto LBL_ERR; + } + + /* store header */ + x = 0; + out[x++] = (type_of == LTC_ASN1_SEQUENCE) ? 0x30 : 0x31; + + if (z < 128) { + out[x++] = (unsigned char)z; + } else if (z < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)z; + } else if (z < 65536UL) { + out[x++] = 0x82; + out[x++] = (unsigned char)((z >> 8UL) & 255); + out[x++] = (unsigned char)(z & 255); + } else if (z < 16777216UL) { + out[x++] = 0x83; + out[x++] = (unsigned char)((z >> 16UL) & 255); + out[x++] = (unsigned char)((z >> 8UL) & 255); + out[x++] = (unsigned char)(z & 255); + } + + /* store data */ + *outlen -= x; + for (i = 0; i < inlen; i++) { + type = list[i].type; + size = list[i].size; + data = list[i].data; + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + z = *outlen; + if ((err = der_encode_boolean(*((int *)data), out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_INTEGER: + z = *outlen; + if ((err = der_encode_integer(data, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_SHORT_INTEGER: + z = *outlen; + if ((err = der_encode_short_integer(*((unsigned long *)data), out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_BIT_STRING: + z = *outlen; + if ((err = der_encode_bit_string(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_OCTET_STRING: + z = *outlen; + if ((err = der_encode_octet_string(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_NULL: + out[x++] = 0x05; + out[x++] = 0x00; + *outlen -= 2; + break; + + case LTC_ASN1_OBJECT_IDENTIFIER: + z = *outlen; + if ((err = der_encode_object_identifier(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_IA5_STRING: + z = *outlen; + if ((err = der_encode_ia5_string(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_PRINTABLE_STRING: + z = *outlen; + if ((err = der_encode_printable_string(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_UTF8_STRING: + z = *outlen; + if ((err = der_encode_utf8_string(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_UTCTIME: + z = *outlen; + if ((err = der_encode_utctime(data, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_SET: + z = *outlen; + if ((err = der_encode_set(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_SETOF: + z = *outlen; + if ((err = der_encode_setof(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_SEQUENCE: + z = *outlen; + if ((err = der_encode_sequence_ex(data, size, out + x, &z, type)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + default: + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + } + *outlen = x; + err = CRYPT_OK; + +LBL_ERR: + return err; +} +#endif + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#include + + +/** + @file der_encode_sequence_multi.c + ASN.1 DER, encode a SEQUENCE, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Encode a SEQUENCE type using a VA list + @param out [out] Destination for data + @param outlen [in/out] Length of buffer and resulting length of output + @remark <...> is of the form (int, unsigned long, void*) + @return CRYPT_OK on success + */ +int der_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...) { + int err, type; + unsigned long size, x; + void *data; + va_list args; + ltc_asn1_list *list; + + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get size of output that will be required */ + va_start(args, outlen); + x = 0; + for ( ; ; ) { + type = va_arg(args, int); + size = va_arg(args, unsigned long); + data = va_arg(args, void *); + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + case LTC_ASN1_INTEGER: + case LTC_ASN1_SHORT_INTEGER: + case LTC_ASN1_BIT_STRING: + case LTC_ASN1_OCTET_STRING: + case LTC_ASN1_NULL: + case LTC_ASN1_OBJECT_IDENTIFIER: + case LTC_ASN1_IA5_STRING: + case LTC_ASN1_PRINTABLE_STRING: + case LTC_ASN1_UTF8_STRING: + case LTC_ASN1_UTCTIME: + case LTC_ASN1_SEQUENCE: + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + ++x; + break; + + default: + va_end(args); + return CRYPT_INVALID_ARG; + } + } + va_end(args); + + /* allocate structure for x elements */ + if (x == 0) { + return CRYPT_NOP; + } + + list = XCALLOC(sizeof(*list), x); + if (list == NULL) { + return CRYPT_MEM; + } + + /* fill in the structure */ + va_start(args, outlen); + x = 0; + for ( ; ; ) { + type = va_arg(args, int); + size = va_arg(args, unsigned long); + data = va_arg(args, void *); + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + case LTC_ASN1_INTEGER: + case LTC_ASN1_SHORT_INTEGER: + case LTC_ASN1_BIT_STRING: + case LTC_ASN1_OCTET_STRING: + case LTC_ASN1_NULL: + case LTC_ASN1_OBJECT_IDENTIFIER: + case LTC_ASN1_IA5_STRING: + case LTC_ASN1_PRINTABLE_STRING: + case LTC_ASN1_UTF8_STRING: + case LTC_ASN1_UTCTIME: + case LTC_ASN1_SEQUENCE: + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + list[x].type = type; + list[x].size = size; + list[x++].data = data; + break; + + default: + va_end(args); + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + } + va_end(args); + + err = der_encode_sequence(list, x, out, outlen); +LBL_ERR: + XFREE(list); + return err; +} +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_encode_sequence_multi.c,v $ */ +/* $Revision: 1.12 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_set.c + ASN.1 DER, Encode a SET, Tom St Denis + */ + +#ifdef LTC_DER + +/* LTC define to ASN.1 TAG */ +static int ltc_to_asn1(int v) { + switch (v) { + case LTC_ASN1_BOOLEAN: + return 0x01; + + case LTC_ASN1_INTEGER: + case LTC_ASN1_SHORT_INTEGER: + return 0x02; + + case LTC_ASN1_BIT_STRING: + return 0x03; + + case LTC_ASN1_OCTET_STRING: + return 0x04; + + case LTC_ASN1_NULL: + return 0x05; + + case LTC_ASN1_OBJECT_IDENTIFIER: + return 0x06; + + case LTC_ASN1_UTF8_STRING: + return 0x0C; + + case LTC_ASN1_PRINTABLE_STRING: + return 0x13; + + case LTC_ASN1_IA5_STRING: + return 0x16; + + case LTC_ASN1_UTCTIME: + return 0x17; + + case LTC_ASN1_SEQUENCE: + return 0x30; + + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + return 0x31; + + default: + return -1; + } +} + +static int qsort_helper_set(const void *a, const void *b) { + ltc_asn1_list *A = (ltc_asn1_list *)a, *B = (ltc_asn1_list *)b; + int r; + + r = ltc_to_asn1(A->type) - ltc_to_asn1(B->type); + + /* for QSORT the order is UNDEFINED if they are "equal" which means it is NOT DETERMINISTIC. So we force it to be :-) */ + if (r == 0) { + /* their order in the original list now determines the position */ + return A->used - B->used; + } else { + return r; + } +} + +/* + Encode a SET type + @param list The list of items to encode + @param inlen The number of items in the list + @param out [out] The destination + @param outlen [in/out] The size of the output + @return CRYPT_OK on success + */ +int der_encode_set(ltc_asn1_list *list, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + ltc_asn1_list *copy; + unsigned long x; + int err; + + /* make copy of list */ + copy = XCALLOC(inlen, sizeof(*copy)); + if (copy == NULL) { + return CRYPT_MEM; + } + + /* fill in used member with index so we can fully sort it */ + for (x = 0; x < inlen; x++) { + copy[x] = list[x]; + copy[x].used = x; + } + + /* sort it by the "type" field */ + XQSORT(copy, inlen, sizeof(*copy), &qsort_helper_set); + + /* call der_encode_sequence_ex() */ + err = der_encode_sequence_ex(copy, inlen, out, outlen, LTC_ASN1_SET); + + /* free list */ + XFREE(copy); + + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/set/der_encode_set.c,v $ */ +/* $Revision: 1.12 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_setof.c + ASN.1 DER, Encode SET OF, Tom St Denis + */ + +#ifdef LTC_DER + +struct edge { + unsigned char *start; + unsigned long size; +}; + +static int qsort_helper(const void *a, const void *b) { + struct edge *A = (struct edge *)a, *B = (struct edge *)b; + int r; + unsigned long x; + + /* compare min length */ + r = XMEMCMP(A->start, B->start, MIN(A->size, B->size)); + + if ((r == 0) && (A->size != B->size)) { + if (A->size > B->size) { + for (x = B->size; x < A->size; x++) { + if (A->start[x]) { + return 1; + } + } + } else { + for (x = A->size; x < B->size; x++) { + if (B->start[x]) { + return -1; + } + } + } + } + + return r; +} + +/** + Encode a SETOF stucture + @param list The list of items to encode + @param inlen The number of items in the list + @param out [out] The destination + @param outlen [in/out] The size of the output + @return CRYPT_OK on success + */ +int der_encode_setof(ltc_asn1_list *list, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long x, y, z; + uintptr_t hdrlen; // TODO: [ECHTTP] Why was this unsigned long? + int err; + struct edge *edges; + unsigned char *ptr, *buf; + + /* check that they're all the same type */ + for (x = 1; x < inlen; x++) { + if (list[x].type != list[x - 1].type) { + return CRYPT_INVALID_ARG; + } + } + + /* alloc buffer to store copy of output */ + buf = XCALLOC(1, *outlen); + if (buf == NULL) { + return CRYPT_MEM; + } + + /* encode list */ + if ((err = der_encode_sequence_ex(list, inlen, buf, outlen, LTC_ASN1_SETOF)) != CRYPT_OK) { + XFREE(buf); + return err; + } + + /* allocate edges */ + edges = XCALLOC(inlen, sizeof(*edges)); + if (edges == NULL) { + XFREE(buf); + return CRYPT_MEM; + } + + /* skip header */ + ptr = buf + 1; + + /* now skip length data */ + x = *ptr++; + if (x >= 0x80) { + ptr += (x & 0x7F); + } + + /* get the size of the static header */ + hdrlen = (uintptr_t)ptr - (uintptr_t)buf; + + + /* scan for edges */ + x = 0; + while (ptr < (buf + *outlen)) { + /* store start */ + edges[x].start = ptr; + + /* skip type */ + z = 1; + + /* parse length */ + y = ptr[z++]; + if (y < 128) { + edges[x].size = y; + } else { + y &= 0x7F; + edges[x].size = 0; + while (y--) { + edges[x].size = (edges[x].size << 8) | ((unsigned long)ptr[z++]); + } + } + + /* skip content */ + edges[x].size += z; + ptr += edges[x].size; + ++x; + } + + /* sort based on contents (using edges) */ + XQSORT(edges, inlen, sizeof(*edges), &qsort_helper); + + /* copy static header */ + XMEMCPY(out, buf, hdrlen); + + /* copy+sort using edges+indecies to output from buffer */ + for (y = hdrlen, x = 0; x < inlen; x++) { + XMEMCPY(out + y, edges[x].start, edges[x].size); + y += edges[x].size; + } + + #ifdef LTC_CLEAN_STACK + zeromem(buf, *outlen); + #endif + + /* free buffers */ + XFREE(edges); + XFREE(buf); + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/set/der_encode_setof.c,v $ */ +/* $Revision: 1.12 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_short_integer.c + ASN.1 DER, encode an integer, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store a short integer in the range (0,2^32-1) + @param num The integer to encode + @param out [out] The destination for the DER encoded integers + @param outlen [in/out] The max size and resulting size of the DER encoded integers + @return CRYPT_OK if successful + */ +int der_encode_short_integer(unsigned long num, unsigned char *out, unsigned long *outlen) { + unsigned long len, x, y, z; + int err; + + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* force to 32 bits */ + num &= 0xFFFFFFFFUL; + + /* find out how big this will be */ + if ((err = der_length_short_integer(num, &len)) != CRYPT_OK) { + return err; + } + + if (*outlen < len) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + /* get len of output */ + z = 0; + y = num; + while (y) { + ++z; + y >>= 8; + } + + /* handle zero */ + if (z == 0) { + z = 1; + } + + /* see if msb is set */ + z += (num & (1UL << ((z << 3) - 1))) ? 1 : 0; + + /* adjust the number so the msB is non-zero */ + for (x = 0; (z <= 4) && (x < (4 - z)); x++) { + num <<= 8; + } + + /* store header */ + x = 0; + out[x++] = 0x02; + out[x++] = (unsigned char)z; + + /* if 31st bit is set output a leading zero and decrement count */ + if (z == 5) { + out[x++] = 0; + --z; + } + + /* store values */ + for (y = 0; y < z; y++) { + out[x++] = (unsigned char)((num >> 24) & 0xFF); + num <<= 8; + } + + /* we good */ + *outlen = x; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/short_integer/der_encode_short_integer.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_utctime.c + ASN.1 DER, encode a UTCTIME, Tom St Denis + */ + +#ifdef LTC_DER + +static const char baseten[] = "0123456789"; + + #define STORE_V(y) \ + out[x++] = der_ia5_char_encode(baseten[(y / 10) % 10]); \ + out[x++] = der_ia5_char_encode(baseten[y % 10]); + +/** + Encodes a UTC time structure in DER format + @param utctime The UTC time structure to encode + @param out The destination of the DER encoding of the UTC time structure + @param outlen [in/out] The length of the DER encoding + @return CRYPT_OK if successful + */ +int der_encode_utctime(ltc_utctime *utctime, + unsigned char *out, unsigned long *outlen) { + unsigned long x, tmplen; + int err; + + LTC_ARGCHK(utctime != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + if ((err = der_length_utctime(utctime, &tmplen)) != CRYPT_OK) { + return err; + } + if (tmplen > *outlen) { + *outlen = tmplen; + return CRYPT_BUFFER_OVERFLOW; + } + + /* store header */ + out[0] = 0x17; + + /* store values */ + x = 2; + STORE_V(utctime->YY); + STORE_V(utctime->MM); + STORE_V(utctime->DD); + STORE_V(utctime->hh); + STORE_V(utctime->mm); + STORE_V(utctime->ss); + + if (utctime->off_mm || utctime->off_hh) { + out[x++] = der_ia5_char_encode(utctime->off_dir ? '-' : '+'); + STORE_V(utctime->off_hh); + STORE_V(utctime->off_mm); + } else { + out[x++] = der_ia5_char_encode('Z'); + } + + /* store length */ + out[1] = (unsigned char)(x - 2); + + /* all good let's return */ + *outlen = x; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utctime/der_encode_utctime.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_encode_utf8_string.c + ASN.1 DER, encode a UTF8 STRING, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Store an UTF8 STRING + @param in The array of UTF8 to store (one per wchar_t) + @param inlen The number of UTF8 to store + @param out [out] The destination for the DER encoded UTF8 STRING + @param outlen [in/out] The max size and resulting size of the DER UTF8 STRING + @return CRYPT_OK if successful + */ +int der_encode_utf8_string(const wchar_t *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) { + unsigned long x, y, len; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get the size */ + for (x = len = 0; x < inlen; x++) { + if ((in[x] < 0) || (in[x] > 0x1FFFF)) { + return CRYPT_INVALID_ARG; + } + len += der_utf8_charsize(in[x]); + } + + if (len < 128) { + y = 2 + len; + } else if (len < 256) { + y = 3 + len; + } else if (len < 65536UL) { + y = 4 + len; + } else if (len < 16777216UL) { + y = 5 + len; + } else { + return CRYPT_INVALID_ARG; + } + + /* too big? */ + if (y > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + /* encode the header+len */ + x = 0; + out[x++] = 0x0C; + if (len < 128) { + out[x++] = (unsigned char)len; + } else if (len < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)len; + } else if (len < 65536UL) { + out[x++] = 0x82; + out[x++] = (unsigned char)((len >> 8) & 255); + out[x++] = (unsigned char)(len & 255); + } else if (len < 16777216UL) { + out[x++] = 0x83; + out[x++] = (unsigned char)((len >> 16) & 255); + out[x++] = (unsigned char)((len >> 8) & 255); + out[x++] = (unsigned char)(len & 255); + } else { + return CRYPT_INVALID_ARG; + } + + /* store UTF8 */ + for (y = 0; y < inlen; y++) { + switch (der_utf8_charsize(in[y])) { + case 1: + out[x++] = (unsigned char)in[y]; + break; + + case 2: + out[x++] = 0xC0 | ((in[y] >> 6) & 0x1F); + out[x++] = 0x80 | (in[y] & 0x3F); + break; + + case 3: + out[x++] = 0xE0 | ((in[y] >> 12) & 0x0F); + out[x++] = 0x80 | ((in[y] >> 6) & 0x3F); + out[x++] = 0x80 | (in[y] & 0x3F); + break; + + case 4: + out[x++] = 0xF0 | ((in[y] >> 18) & 0x07); + out[x++] = 0x80 | ((in[y] >> 12) & 0x3F); + out[x++] = 0x80 | ((in[y] >> 6) & 0x3F); + out[x++] = 0x80 | (in[y] & 0x3F); + break; + } + } + + /* retun length */ + *outlen = x; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utf8/der_encode_utf8_string.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_bit_string.c + ASN.1 DER, get length of BIT STRING, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Gets length of DER encoding of BIT STRING + @param nbits The number of bits in the string to encode + @param outlen [out] The length of the DER encoding for the given string + @return CRYPT_OK if successful + */ +int der_length_bit_string(unsigned long nbits, unsigned long *outlen) { + unsigned long nbytes; + + LTC_ARGCHK(outlen != NULL); + + /* get the number of the bytes */ + nbytes = (nbits >> 3) + ((nbits & 7) ? 1 : 0) + 1; + + if (nbytes < 128) { + /* 03 LL PP DD DD DD ... */ + *outlen = 2 + nbytes; + } else if (nbytes < 256) { + /* 03 81 LL PP DD DD DD ... */ + *outlen = 3 + nbytes; + } else if (nbytes < 65536) { + /* 03 82 LL LL PP DD DD DD ... */ + *outlen = 4 + nbytes; + } else { + return CRYPT_INVALID_ARG; + } + + return CRYPT_OK; +} +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/bit/der_length_bit_string.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_boolean.c + ASN.1 DER, get length of a BOOLEAN, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Gets length of DER encoding of a BOOLEAN + @param outlen [out] The length of the DER encoding + @return CRYPT_OK if successful + */ +int der_length_boolean(unsigned long *outlen) { + LTC_ARGCHK(outlen != NULL); + *outlen = 3; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/boolean/der_length_boolean.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_ia5_string.c + ASN.1 DER, get length of IA5 STRING, Tom St Denis + */ + +#ifdef LTC_DER + +static const struct { + int code, value; +} ia5_table[] = { + { '\0', 0 }, + { '\a', 7 }, + { '\b', 8 }, + { '\t', 9 }, + { '\n', 10 }, + { '\f', 12 }, + { '\r', 13 }, + { ' ', 32 }, + { '!', 33 }, + { '"', 34 }, + { '#', 35 }, + { '$', 36 }, + { '%', 37 }, + { '&', 38 }, + { '\'', 39 }, + { '(', 40 }, + { ')', 41 }, + { '*', 42 }, + { '+', 43 }, + { ',', 44 }, + { '-', 45 }, + { '.', 46 }, + { '/', 47 }, + { '0', 48 }, + { '1', 49 }, + { '2', 50 }, + { '3', 51 }, + { '4', 52 }, + { '5', 53 }, + { '6', 54 }, + { '7', 55 }, + { '8', 56 }, + { '9', 57 }, + { ':', 58 }, + { ';', 59 }, + { '<', 60 }, + { '=', 61 }, + { '>', 62 }, + { '?', 63 }, + { '@', 64 }, + { 'A', 65 }, + { 'B', 66 }, + { 'C', 67 }, + { 'D', 68 }, + { 'E', 69 }, + { 'F', 70 }, + { 'G', 71 }, + { 'H', 72 }, + { 'I', 73 }, + { 'J', 74 }, + { 'K', 75 }, + { 'L', 76 }, + { 'M', 77 }, + { 'N', 78 }, + { 'O', 79 }, + { 'P', 80 }, + { 'Q', 81 }, + { 'R', 82 }, + { 'S', 83 }, + { 'T', 84 }, + { 'U', 85 }, + { 'V', 86 }, + { 'W', 87 }, + { 'X', 88 }, + { 'Y', 89 }, + { 'Z', 90 }, + { '[', 91 }, + { '\\', 92 }, + { ']', 93 }, + { '^', 94 }, + { '_', 95 }, + { '`', 96 }, + { 'a', 97 }, + { 'b', 98 }, + { 'c', 99 }, + { 'd', 100 }, + { 'e', 101 }, + { 'f', 102 }, + { 'g', 103 }, + { 'h', 104 }, + { 'i', 105 }, + { 'j', 106 }, + { 'k', 107 }, + { 'l', 108 }, + { 'm', 109 }, + { 'n', 110 }, + { 'o', 111 }, + { 'p', 112 }, + { 'q', 113 }, + { 'r', 114 }, + { 's', 115 }, + { 't', 116 }, + { 'u', 117 }, + { 'v', 118 }, + { 'w', 119 }, + { 'x', 120 }, + { 'y', 121 }, + { 'z', 122 }, + { '{', 123 }, + { '|', 124 }, + { '}', 125 }, + { '~', 126 } +}; + +int der_ia5_char_encode(int c) { + int x; + + for (x = 0; x < (int)(sizeof(ia5_table) / sizeof(ia5_table[0])); x++) { + if (ia5_table[x].code == c) { + return ia5_table[x].value; + } + } + return -1; +} + +int der_ia5_value_decode(int v) { + int x; + + for (x = 0; x < (int)(sizeof(ia5_table) / sizeof(ia5_table[0])); x++) { + if (ia5_table[x].value == v) { + return ia5_table[x].code; + } + } + return -1; +} + +/** + Gets length of DER encoding of IA5 STRING + @param octets The values you want to encode + @param noctets The number of octets in the string to encode + @param outlen [out] The length of the DER encoding for the given string + @return CRYPT_OK if successful + */ +int der_length_ia5_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen) { + unsigned long x; + + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(octets != NULL); + + /* scan string for validity */ + for (x = 0; x < noctets; x++) { + if (der_ia5_char_encode(octets[x]) == -1) { + return CRYPT_INVALID_ARG; + } + } + + if (noctets < 128) { + /* 16 LL DD DD DD ... */ + *outlen = 2 + noctets; + } else if (noctets < 256) { + /* 16 81 LL DD DD DD ... */ + *outlen = 3 + noctets; + } else if (noctets < 65536UL) { + /* 16 82 LL LL DD DD DD ... */ + *outlen = 4 + noctets; + } else if (noctets < 16777216UL) { + /* 16 83 LL LL LL DD DD DD ... */ + *outlen = 5 + noctets; + } else { + return CRYPT_INVALID_ARG; + } + + return CRYPT_OK; +} +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/ia5/der_length_ia5_string.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_integer.c + ASN.1 DER, get length of encoding, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Gets length of DER encoding of num + @param num The int to get the size of + @param outlen [out] The length of the DER encoding for the given integer + @return CRYPT_OK if successful + */ +int der_length_integer(void *num, unsigned long *outlen) { + unsigned long z, len; + int leading_zero; + + LTC_ARGCHK(num != NULL); + LTC_ARGCHK(outlen != NULL); + + if (mp_cmp_d(num, 0) != LTC_MP_LT) { + /* positive */ + + /* we only need a leading zero if the msb of the first byte is one */ + if (((mp_count_bits(num) & 7) == 0) || (mp_iszero(num) == LTC_MP_YES)) { + leading_zero = 1; + } else { + leading_zero = 0; + } + + /* size for bignum */ + z = len = leading_zero + mp_unsigned_bin_size(num); + } else { + /* it's negative */ + /* find power of 2 that is a multiple of eight and greater than count bits */ + leading_zero = 0; + z = mp_count_bits(num); + z = z + (8 - (z & 7)); + if (((mp_cnt_lsb(num) + 1) == mp_count_bits(num)) && ((mp_count_bits(num) & 7) == 0)) --z; + len = z = z >> 3; + } + + /* now we need a length */ + if (z < 128) { + /* short form */ + ++len; + } else { + /* long form (relies on z != 0), assumes length bytes < 128 */ + ++len; + + while (z) { + ++len; + z >>= 8; + } + } + + /* we need a 0x02 to indicate it's INTEGER */ + ++len; + + /* return length */ + *outlen = len; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/integer/der_length_integer.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_object_identifier.c + ASN.1 DER, get length of Object Identifier, Tom St Denis + */ + +#ifdef LTC_DER + +unsigned long der_object_identifier_bits(unsigned long x) { + unsigned long c; + + x &= 0xFFFFFFFF; + c = 0; + while (x) { + ++c; + x >>= 1; + } + return c; +} + +/** + Gets length of DER encoding of Object Identifier + @param nwords The number of OID words + @param words The actual OID words to get the size of + @param outlen [out] The length of the DER encoding for the given string + @return CRYPT_OK if successful + */ +int der_length_object_identifier(unsigned long *words, unsigned long nwords, unsigned long *outlen) { + unsigned long y, z, t, wordbuf; + + LTC_ARGCHK(words != NULL); + LTC_ARGCHK(outlen != NULL); + + + /* must be >= 2 words */ + if (nwords < 2) { + return CRYPT_INVALID_ARG; + } + + /* word1 = 0,1,2,3 and word2 0..39 */ + if ((words[0] > 3) || ((words[0] < 2) && (words[1] > 39))) { + return CRYPT_INVALID_ARG; + } + + /* leading word is the first two */ + z = 0; + wordbuf = words[0] * 40 + words[1]; + for (y = 1; y < nwords; y++) { + t = der_object_identifier_bits(wordbuf); + z += t / 7 + ((t % 7) ? 1 : 0) + (wordbuf == 0 ? 1 : 0); + if (y < nwords - 1) { + /* grab next word */ + wordbuf = words[y + 1]; + } + } + + /* now depending on the length our length encoding changes */ + if (z < 128) { + z += 2; + } else if (z < 256) { + z += 3; + } else if (z < 65536UL) { + z += 4; + } else { + return CRYPT_INVALID_ARG; + } + + *outlen = z; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/object_identifier/der_length_object_identifier.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_octet_string.c + ASN.1 DER, get length of OCTET STRING, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Gets length of DER encoding of OCTET STRING + @param noctets The number of octets in the string to encode + @param outlen [out] The length of the DER encoding for the given string + @return CRYPT_OK if successful + */ +int der_length_octet_string(unsigned long noctets, unsigned long *outlen) { + LTC_ARGCHK(outlen != NULL); + + if (noctets < 128) { + /* 04 LL DD DD DD ... */ + *outlen = 2 + noctets; + } else if (noctets < 256) { + /* 04 81 LL DD DD DD ... */ + *outlen = 3 + noctets; + } else if (noctets < 65536UL) { + /* 04 82 LL LL DD DD DD ... */ + *outlen = 4 + noctets; + } else if (noctets < 16777216UL) { + /* 04 83 LL LL LL DD DD DD ... */ + *outlen = 5 + noctets; + } else { + return CRYPT_INVALID_ARG; + } + + return CRYPT_OK; +} +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/octet/der_length_octet_string.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_printable_string.c + ASN.1 DER, get length of Printable STRING, Tom St Denis + */ + +#ifdef LTC_DER + +static const struct { + int code, value; +} printable_table[] = { + { ' ', 32 }, + { '\'', 39 }, + { '(', 40 }, + { ')', 41 }, + { '+', 43 }, + { ',', 44 }, + { '-', 45 }, + { '.', 46 }, + { '/', 47 }, + { '0', 48 }, + { '1', 49 }, + { '2', 50 }, + { '3', 51 }, + { '4', 52 }, + { '5', 53 }, + { '6', 54 }, + { '7', 55 }, + { '8', 56 }, + { '9', 57 }, + { ':', 58 }, + { '=', 61 }, + { '?', 63 }, + { 'A', 65 }, + { 'B', 66 }, + { 'C', 67 }, + { 'D', 68 }, + { 'E', 69 }, + { 'F', 70 }, + { 'G', 71 }, + { 'H', 72 }, + { 'I', 73 }, + { 'J', 74 }, + { 'K', 75 }, + { 'L', 76 }, + { 'M', 77 }, + { 'N', 78 }, + { 'O', 79 }, + { 'P', 80 }, + { 'Q', 81 }, + { 'R', 82 }, + { 'S', 83 }, + { 'T', 84 }, + { 'U', 85 }, + { 'V', 86 }, + { 'W', 87 }, + { 'X', 88 }, + { 'Y', 89 }, + { 'Z', 90 }, + { 'a', 97 }, + { 'b', 98 }, + { 'c', 99 }, + { 'd', 100 }, + { 'e', 101 }, + { 'f', 102 }, + { 'g', 103 }, + { 'h', 104 }, + { 'i', 105 }, + { 'j', 106 }, + { 'k', 107 }, + { 'l', 108 }, + { 'm', 109 }, + { 'n', 110 }, + { 'o', 111 }, + { 'p', 112 }, + { 'q', 113 }, + { 'r', 114 }, + { 's', 115 }, + { 't', 116 }, + { 'u', 117 }, + { 'v', 118 }, + { 'w', 119 }, + { 'x', 120 }, + { 'y', 121 }, + { 'z', 122 }, +}; + +int der_printable_char_encode(int c) { + int x; + + for (x = 0; x < (int)(sizeof(printable_table) / sizeof(printable_table[0])); x++) { + if (printable_table[x].code == c) { + return printable_table[x].value; + } + } + return -1; +} + +int der_printable_value_decode(int v) { + int x; + + for (x = 0; x < (int)(sizeof(printable_table) / sizeof(printable_table[0])); x++) { + if (printable_table[x].value == v) { + return printable_table[x].code; + } + } + return -1; +} + +/** + Gets length of DER encoding of Printable STRING + @param octets The values you want to encode + @param noctets The number of octets in the string to encode + @param outlen [out] The length of the DER encoding for the given string + @return CRYPT_OK if successful + */ +int der_length_printable_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen) { + unsigned long x; + + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(octets != NULL); + + /* scan string for validity */ + for (x = 0; x < noctets; x++) { + if (der_printable_char_encode(octets[x]) == -1) { + return CRYPT_INVALID_ARG; + } + } + + if (noctets < 128) { + /* 16 LL DD DD DD ... */ + *outlen = 2 + noctets; + } else if (noctets < 256) { + /* 16 81 LL DD DD DD ... */ + *outlen = 3 + noctets; + } else if (noctets < 65536UL) { + /* 16 82 LL LL DD DD DD ... */ + *outlen = 4 + noctets; + } else if (noctets < 16777216UL) { + /* 16 83 LL LL LL DD DD DD ... */ + *outlen = 5 + noctets; + } else { + return CRYPT_INVALID_ARG; + } + + return CRYPT_OK; +} +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/printable_string/der_length_printable_string.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_sequence.c + ASN.1 DER, length a SEQUENCE, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Get the length of a DER sequence + @param list The sequences of items in the SEQUENCE + @param inlen The number of items + @param outlen [out] The length required in octets to store it + @return CRYPT_OK on success + */ +int der_length_sequence(ltc_asn1_list *list, unsigned long inlen, + unsigned long *outlen) { + int err, type; + unsigned long size, x, y, z, i; + void *data; + + LTC_ARGCHK(list != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get size of output that will be required */ + y = 0; + for (i = 0; i < inlen; i++) { + type = list[i].type; + size = list[i].size; + data = list[i].data; + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + if ((err = der_length_boolean(&x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_INTEGER: + if ((err = der_length_integer(data, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_SHORT_INTEGER: + if ((err = der_length_short_integer(*((unsigned long *)data), &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_BIT_STRING: + if ((err = der_length_bit_string(size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_OCTET_STRING: + if ((err = der_length_octet_string(size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_NULL: + y += 2; + break; + + case LTC_ASN1_OBJECT_IDENTIFIER: + if ((err = der_length_object_identifier(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_IA5_STRING: + if ((err = der_length_ia5_string(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_PRINTABLE_STRING: + if ((err = der_length_printable_string(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_UTCTIME: + if ((err = der_length_utctime(data, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_UTF8_STRING: + if ((err = der_length_utf8_string(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + case LTC_ASN1_SEQUENCE: + if ((err = der_length_sequence(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + + default: + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + } + + /* calc header size */ + z = y; + if (y < 128) { + y += 2; + } else if (y < 256) { + /* 0x30 0x81 LL */ + y += 3; + } else if (y < 65536UL) { + /* 0x30 0x82 LL LL */ + y += 4; + } else if (y < 16777216UL) { + /* 0x30 0x83 LL LL LL */ + y += 5; + } else { + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + + /* store size */ + *outlen = y; + err = CRYPT_OK; + +LBL_ERR: + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_length_sequence.c,v $ */ +/* $Revision: 1.14 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_short_integer.c + ASN.1 DER, get length of encoding, Tom St Denis + */ + + +#ifdef LTC_DER + +/** + Gets length of DER encoding of num + @param num The integer to get the size of + @param outlen [out] The length of the DER encoding for the given integer + @return CRYPT_OK if successful + */ +int der_length_short_integer(unsigned long num, unsigned long *outlen) { + unsigned long z, y, len; + + LTC_ARGCHK(outlen != NULL); + + /* force to 32 bits */ + num &= 0xFFFFFFFFUL; + + /* get the number of bytes */ + z = 0; + y = num; + while (y) { + ++z; + y >>= 8; + } + + /* handle zero */ + if (z == 0) { + z = 1; + } + + /* we need a 0x02 to indicate it's INTEGER */ + len = 1; + + /* length byte */ + ++len; + + /* bytes in value */ + len += z; + + /* see if msb is set */ + len += (num & (1UL << ((z << 3) - 1))) ? 1 : 0; + + /* return length */ + *outlen = len; + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/short_integer/der_length_short_integer.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_utctime.c + ASN.1 DER, get length of UTCTIME, Tom St Denis + */ + +#ifdef LTC_DER + +/** + Gets length of DER encoding of UTCTIME + @param utctime The UTC time structure to get the size of + @param outlen [out] The length of the DER encoding + @return CRYPT_OK if successful + */ +int der_length_utctime(ltc_utctime *utctime, unsigned long *outlen) { + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(utctime != NULL); + + if ((utctime->off_hh == 0) && (utctime->off_mm == 0)) { + /* we encode as YYMMDDhhmmssZ */ + *outlen = 2 + 13; + } else { + /* we encode as YYMMDDhhmmss{+|-}hh'mm' */ + *outlen = 2 + 17; + } + + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utctime/der_length_utctime.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_length_utf8_string.c + ASN.1 DER, get length of UTF8 STRING, Tom St Denis + */ + +#ifdef LTC_DER + +/** Return the size in bytes of a UTF-8 character + @param c The UTF-8 character to measure + @return The size in bytes + */ +unsigned long der_utf8_charsize(const wchar_t c) { + if (c <= 0x7F) { + return 1; + } else if (c <= 0x7FF) { + return 2; + } else if (c <= 0xFFFF) { + return 3; + } else { + return 4; + } +} + +/** + Gets length of DER encoding of UTF8 STRING + @param in The characters to measure the length of + @param noctets The number of octets in the string to encode + @param outlen [out] The length of the DER encoding for the given string + @return CRYPT_OK if successful + */ +int der_length_utf8_string(const wchar_t *in, unsigned long noctets, unsigned long *outlen) { + unsigned long x, len; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(outlen != NULL); + + len = 0; + for (x = 0; x < noctets; x++) { + if ((in[x] < 0) || (in[x] > 0x10FFFF)) { + return CRYPT_INVALID_ARG; + } + len += der_utf8_charsize(in[x]); + } + + if (len < 128) { + /* 0C LL DD DD DD ... */ + *outlen = 2 + len; + } else if (len < 256) { + /* 0C 81 LL DD DD DD ... */ + *outlen = 3 + len; + } else if (len < 65536UL) { + /* 0C 82 LL LL DD DD DD ... */ + *outlen = 4 + len; + } else if (len < 16777216UL) { + /* 0C 83 LL LL LL DD DD DD ... */ + *outlen = 5 + len; + } else { + return CRYPT_INVALID_ARG; + } + + return CRYPT_OK; +} +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utf8/der_length_utf8_string.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file der_sequence_free.c + ASN.1 DER, free's a structure allocated by der_decode_sequence_flexi(), Tom St Denis + */ + +#ifdef LTC_DER + +/** + Free memory allocated by der_decode_sequence_flexi() + @param in The list to free + */ +void der_sequence_free(ltc_asn1_list *in) { + ltc_asn1_list *l; + + /* walk to the start of the chain */ + while (in->prev != NULL || in->parent != NULL) { + if (in->parent != NULL) { + in = in->parent; + } else { + in = in->prev; + } + } + + /* now walk the list and free stuff */ + while (in != NULL) { + /* is there a child? */ + if (in->child) { + /* disconnect */ + in->child->parent = NULL; + der_sequence_free(in->child); + } + + switch (in->type) { + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + case LTC_ASN1_SEQUENCE: + break; + + case LTC_ASN1_INTEGER: + if (in->data != NULL) { + mp_clear(in->data); + } + break; + + default: + if (in->data != NULL) { + XFREE(in->data); + } + } + + /* move to next and free current */ + l = in->next; + XFREE(in); + in = l; + } +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_sequence_free.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/* This holds the key settings. ***MUST*** be organized by size from smallest to largest. */ +const ltc_ecc_set_type ltc_ecc_sets[] = { + #ifdef ECC112 + { + 14, + "SECP112R1", + "DB7C2ABF62E35E668076BEAD208B", + "659EF8BA043916EEDE8911702B22", + "DB7C2ABF62E35E7628DFAC6561C5", + "09487239995A5EE76B55F9C2F098", + "A89CE5AF8724C0A23E0E0FF77500" + }, + #endif + #ifdef ECC128 + { + 16, + "SECP128R1", + "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF", + "E87579C11079F43DD824993C2CEE5ED3", + "FFFFFFFE0000000075A30D1B9038A115", + "161FF7528B899B2D0C28607CA52C5B86", + "CF5AC8395BAFEB13C02DA292DDED7A83", + }, + #endif + #ifdef ECC160 + { + 20, + "SECP160R1", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF", + "1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45", + "0100000000000000000001F4C8F927AED3CA752257", + "4A96B5688EF573284664698968C38BB913CBFC82", + "23A628553168947D59DCC912042351377AC5FB32", + }, + #endif + #ifdef ECC192 + { + 24, + "ECC-192", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", + "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", + "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831", + "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012", + "7192B95FFC8DA78631011ED6B24CDD573F977A11E794811", + }, + #endif + #ifdef ECC224 + { + 28, + "ECC-224", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001", + "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D", + "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21", + "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34", + }, + #endif + #ifdef ECC256 + { + 32, + "ECC-256", + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", + "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", + "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", + }, + #endif + #ifdef ECC384 + { + 48, + "ECC-384", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", + "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", + }, + #endif + #ifdef ECC521 + { + 66, + "ECC-521", + "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "51953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", + "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", + "C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", + "11839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", + }, + #endif + { + 0, + NULL, NULL, NULL, NULL, NULL, NULL + } +}; +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc.c,v $ */ +/* $Revision: 1.40 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_ansi_x963_export.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** ECC X9.63 (Sec. 4.3.6) uncompressed export + @param key Key to export + @param out [out] destination of export + @param outlen [in/out] Length of destination and final output size + Return CRYPT_OK on success + */ +int ecc_ansi_x963_export(ecc_key *key, unsigned char *out, unsigned long *outlen) { + unsigned char buf[ECC_BUF_SIZE]; + unsigned long numlen; + + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + if (ltc_ecc_is_valid_idx(key->idx) == 0) { + return CRYPT_INVALID_ARG; + } + numlen = key->dp->size; + + if (*outlen < (1 + 2 * numlen)) { + *outlen = 1 + 2 * numlen; + return CRYPT_BUFFER_OVERFLOW; + } + + /* store byte 0x04 */ + out[0] = 0x04; + + /* pad and store x */ + zeromem(buf, sizeof(buf)); + mp_to_unsigned_bin(key->pubkey.x, buf + (numlen - mp_unsigned_bin_size(key->pubkey.x))); + XMEMCPY(out + 1, buf, numlen); + + /* pad and store y */ + zeromem(buf, sizeof(buf)); + mp_to_unsigned_bin(key->pubkey.y, buf + (numlen - mp_unsigned_bin_size(key->pubkey.y))); + XMEMCPY(out + 1 + numlen, buf, numlen); + + *outlen = 1 + 2 * numlen; + return CRYPT_OK; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_ansi_x963_export.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_ansi_x963_import.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** Import an ANSI X9.63 format public key + @param in The input data to read + @param inlen The length of the input data + @param key [out] destination to store imported key \ + */ +int ecc_ansi_x963_import(const unsigned char *in, unsigned long inlen, ecc_key *key) { + return ecc_ansi_x963_import_ex(in, inlen, key, NULL); +} + +int ecc_ansi_x963_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, ltc_ecc_set_type *dp) { + int x, err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(key != NULL); + + /* must be odd */ + if ((inlen & 1) == 0) { + return CRYPT_INVALID_ARG; + } + + /* init key */ + if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, NULL) != CRYPT_OK) { + return CRYPT_MEM; + } + + /* check for 4, 6 or 7 */ + if ((in[0] != 4) && (in[0] != 6) && (in[0] != 7)) { + err = CRYPT_INVALID_PACKET; + goto error; + } + + /* read data */ + if ((err = mp_read_unsigned_bin(key->pubkey.x, (unsigned char *)in + 1, (inlen - 1) >> 1)) != CRYPT_OK) { + goto error; + } + + if ((err = mp_read_unsigned_bin(key->pubkey.y, (unsigned char *)in + 1 + ((inlen - 1) >> 1), (inlen - 1) >> 1)) != CRYPT_OK) { + goto error; + } + if ((err = mp_set(key->pubkey.z, 1)) != CRYPT_OK) { + goto error; + } + + if (dp == NULL) { + /* determine the idx */ + for (x = 0; ltc_ecc_sets[x].size != 0; x++) { + if ((unsigned)ltc_ecc_sets[x].size >= ((inlen - 1) >> 1)) { + break; + } + } + if (ltc_ecc_sets[x].size == 0) { + err = CRYPT_INVALID_PACKET; + goto error; + } + /* set the idx */ + key->idx = x; + key->dp = <c_ecc_sets[x]; + } else { + if (((inlen - 1) >> 1) != (unsigned long)dp->size) { + err = CRYPT_INVALID_PACKET; + goto error; + } + key->idx = -1; + key->dp = dp; + } + key->type = PK_PUBLIC; + + /* we're done */ + return CRYPT_OK; +error: + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_ansi_x963_import.c,v $ */ +/* $Revision: 1.11 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_decrypt_key.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Decrypt an ECC encrypted key + @param in The ciphertext + @param inlen The length of the ciphertext (octets) + @param out [out] The plaintext + @param outlen [in/out] The max size and resulting size of the plaintext + @param key The corresponding private ECC key + @return CRYPT_OK if successful + */ +int ecc_decrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + ecc_key *key) { + unsigned char *ecc_shared, *skey, *pub_expt; + unsigned long x, y, hashOID[32]; + int hash, err; + ecc_key pubkey; + ltc_asn1_list decode[3]; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* right key type? */ + if (key->type != PK_PRIVATE) { + return CRYPT_PK_NOT_PRIVATE; + } + + /* decode to find out hash */ + LTC_SET_ASN1(decode, 0, LTC_ASN1_OBJECT_IDENTIFIER, hashOID, sizeof(hashOID) / sizeof(hashOID[0])); + + if ((err = der_decode_sequence(in, inlen, decode, 1)) != CRYPT_OK) { + return err; + } + + hash = find_hash_oid(hashOID, decode[0].size); + if (hash_is_valid(hash) != CRYPT_OK) { + return CRYPT_INVALID_PACKET; + } + + /* we now have the hash! */ + + /* allocate memory */ + pub_expt = XMALLOC(ECC_BUF_SIZE); + ecc_shared = XMALLOC(ECC_BUF_SIZE); + skey = XMALLOC(MAXBLOCKSIZE); + if ((pub_expt == NULL) || (ecc_shared == NULL) || (skey == NULL)) { + if (pub_expt != NULL) { + XFREE(pub_expt); + } + if (ecc_shared != NULL) { + XFREE(ecc_shared); + } + if (skey != NULL) { + XFREE(skey); + } + return CRYPT_MEM; + } + LTC_SET_ASN1(decode, 1, LTC_ASN1_OCTET_STRING, pub_expt, ECC_BUF_SIZE); + LTC_SET_ASN1(decode, 2, LTC_ASN1_OCTET_STRING, skey, MAXBLOCKSIZE); + + /* read the structure in now */ + if ((err = der_decode_sequence(in, inlen, decode, 3)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* import ECC key from packet */ + if ((err = ecc_import(decode[1].data, decode[1].size, &pubkey)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* make shared key */ + x = ECC_BUF_SIZE; + if ((err = ecc_shared_secret(key, &pubkey, ecc_shared, &x)) != CRYPT_OK) { + ecc_free(&pubkey); + goto LBL_ERR; + } + ecc_free(&pubkey); + + y = MIN(ECC_BUF_SIZE, MAXBLOCKSIZE); + if ((err = hash_memory(hash, ecc_shared, x, ecc_shared, &y)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* ensure the hash of the shared secret is at least as big as the encrypt itself */ + if (decode[2].size > y) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } + + /* avoid buffer overflow */ + if (*outlen < decode[2].size) { + *outlen = decode[2].size; + err = CRYPT_BUFFER_OVERFLOW; + goto LBL_ERR; + } + + /* Decrypt the key */ + for (x = 0; x < decode[2].size; x++) { + out[x] = skey[x] ^ ecc_shared[x]; + } + *outlen = x; + + err = CRYPT_OK; +LBL_ERR: + #ifdef LTC_CLEAN_STACK + zeromem(pub_expt, ECC_BUF_SIZE); + zeromem(ecc_shared, ECC_BUF_SIZE); + zeromem(skey, MAXBLOCKSIZE); + #endif + + XFREE(pub_expt); + XFREE(ecc_shared); + XFREE(skey); + + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_decrypt_key.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_encrypt_key.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Encrypt a symmetric key with ECC + @param in The symmetric key you want to encrypt + @param inlen The length of the key to encrypt (octets) + @param out [out] The destination for the ciphertext + @param outlen [in/out] The max size and resulting size of the ciphertext + @param prng An active PRNG state + @param wprng The index of the PRNG you wish to use + @param hash The index of the hash you want to use + @param key The ECC key you want to encrypt to + @return CRYPT_OK if successful + */ +int ecc_encrypt_key(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, int hash, + ecc_key *key) { + unsigned char *pub_expt, *ecc_shared, *skey; + ecc_key pubkey; + unsigned long x, y, pubkeysize; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* check that wprng/cipher/hash are not invalid */ + if ((err = prng_is_valid(wprng)) != CRYPT_OK) { + return err; + } + + if ((err = hash_is_valid(hash)) != CRYPT_OK) { + return err; + } + + if (inlen > hash_descriptor[hash].hashsize) { + return CRYPT_INVALID_HASH; + } + + /* make a random key and export the public copy */ + if ((err = ecc_make_key_ex(prng, wprng, &pubkey, key->dp)) != CRYPT_OK) { + return err; + } + + pub_expt = XMALLOC(ECC_BUF_SIZE); + ecc_shared = XMALLOC(ECC_BUF_SIZE); + skey = XMALLOC(MAXBLOCKSIZE); + if ((pub_expt == NULL) || (ecc_shared == NULL) || (skey == NULL)) { + if (pub_expt != NULL) { + XFREE(pub_expt); + } + if (ecc_shared != NULL) { + XFREE(ecc_shared); + } + if (skey != NULL) { + XFREE(skey); + } + ecc_free(&pubkey); + return CRYPT_MEM; + } + + pubkeysize = ECC_BUF_SIZE; + if ((err = ecc_export(pub_expt, &pubkeysize, PK_PUBLIC, &pubkey)) != CRYPT_OK) { + ecc_free(&pubkey); + goto LBL_ERR; + } + + /* make random key */ + x = ECC_BUF_SIZE; + if ((err = ecc_shared_secret(&pubkey, key, ecc_shared, &x)) != CRYPT_OK) { + ecc_free(&pubkey); + goto LBL_ERR; + } + ecc_free(&pubkey); + y = MAXBLOCKSIZE; + if ((err = hash_memory(hash, ecc_shared, x, skey, &y)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* Encrypt key */ + for (x = 0; x < inlen; x++) { + skey[x] ^= in[x]; + } + + err = der_encode_sequence_multi(out, outlen, + LTC_ASN1_OBJECT_IDENTIFIER, hash_descriptor[hash].OIDlen, hash_descriptor[hash].OID, + LTC_ASN1_OCTET_STRING, pubkeysize, pub_expt, + LTC_ASN1_OCTET_STRING, inlen, skey, + LTC_ASN1_EOL, 0UL, NULL); + +LBL_ERR: + #ifdef LTC_CLEAN_STACK + /* clean up */ + zeromem(pub_expt, ECC_BUF_SIZE); + zeromem(ecc_shared, ECC_BUF_SIZE); + zeromem(skey, MAXBLOCKSIZE); + #endif + + XFREE(skey); + XFREE(ecc_shared); + XFREE(pub_expt); + + return err; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_encrypt_key.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_export.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Export an ECC key as a binary packet + @param out [out] Destination for the key + @param outlen [in/out] Max size and resulting size of the exported key + @param type The type of key you want to export (PK_PRIVATE or PK_PUBLIC) + @param key The key to export + @return CRYPT_OK if successful + */ +int ecc_export(unsigned char *out, unsigned long *outlen, int type, ecc_key *key) { + int err; + unsigned char flags[1]; + unsigned long key_size; + + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* type valid? */ + if ((key->type != PK_PRIVATE) && (type == PK_PRIVATE)) { + return CRYPT_PK_TYPE_MISMATCH; + } + + if (ltc_ecc_is_valid_idx(key->idx) == 0) { + return CRYPT_INVALID_ARG; + } + + /* we store the NIST byte size */ + key_size = key->dp->size; + + if (type == PK_PRIVATE) { + flags[0] = 1; + err = der_encode_sequence_multi(out, outlen, + LTC_ASN1_BIT_STRING, 1UL, flags, + LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, + LTC_ASN1_INTEGER, 1UL, key->pubkey.x, + LTC_ASN1_INTEGER, 1UL, key->pubkey.y, + LTC_ASN1_INTEGER, 1UL, key->k, + LTC_ASN1_EOL, 0UL, NULL); + } else { + flags[0] = 0; + err = der_encode_sequence_multi(out, outlen, + LTC_ASN1_BIT_STRING, 1UL, flags, + LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, + LTC_ASN1_INTEGER, 1UL, key->pubkey.x, + LTC_ASN1_INTEGER, 1UL, key->pubkey.y, + LTC_ASN1_EOL, 0UL, NULL); + } + + return err; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_export.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_free.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Free an ECC key from memory + @param key The key you wish to free + */ +void ecc_free(ecc_key *key) { + LTC_ARGCHKVD(key != NULL); + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_free.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_get_size.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Get the size of an ECC key + @param key The key to get the size of + @return The size (octets) of the key or INT_MAX on error + */ +int ecc_get_size(ecc_key *key) { + LTC_ARGCHK(key != NULL); + if (ltc_ecc_is_valid_idx(key->idx)) + return key->dp->size; + else + return INT_MAX; /* large value known to cause it to fail when passed to ecc_make_key() */ +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_get_size.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_import.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +static int is_point(ecc_key *key) { + void *prime, *b, *t1, *t2; + int err; + + if ((err = mp_init_multi(&prime, &b, &t1, &t2, NULL)) != CRYPT_OK) { + return err; + } + + /* load prime and b */ + if ((err = mp_read_radix(prime, key->dp->prime, 16)) != CRYPT_OK) { + goto error; + } + if ((err = mp_read_radix(b, key->dp->B, 16)) != CRYPT_OK) { + goto error; + } + + /* compute y^2 */ + if ((err = mp_sqr(key->pubkey.y, t1)) != CRYPT_OK) { + goto error; + } + + /* compute x^3 */ + if ((err = mp_sqr(key->pubkey.x, t2)) != CRYPT_OK) { + goto error; + } + if ((err = mp_mod(t2, prime, t2)) != CRYPT_OK) { + goto error; + } + if ((err = mp_mul(key->pubkey.x, t2, t2)) != CRYPT_OK) { + goto error; + } + + /* compute y^2 - x^3 */ + if ((err = mp_sub(t1, t2, t1)) != CRYPT_OK) { + goto error; + } + + /* compute y^2 - x^3 + 3x */ + if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { + goto error; + } + if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { + goto error; + } + if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { + goto error; + } + if ((err = mp_mod(t1, prime, t1)) != CRYPT_OK) { + goto error; + } + while (mp_cmp_d(t1, 0) == LTC_MP_LT) { + if ((err = mp_add(t1, prime, t1)) != CRYPT_OK) { + goto error; + } + } + while (mp_cmp(t1, prime) != LTC_MP_LT) { + if ((err = mp_sub(t1, prime, t1)) != CRYPT_OK) { + goto error; + } + } + + /* compare to b */ + if (mp_cmp(t1, b) != LTC_MP_EQ) { + err = CRYPT_INVALID_PACKET; + } else { + err = CRYPT_OK; + } + +error: + mp_clear_multi(prime, b, t1, t2, NULL); + return err; +} + +/** + Import an ECC key from a binary packet + @param in The packet to import + @param inlen The length of the packet + @param key [out] The destination of the import + @return CRYPT_OK if successful, upon error all allocated memory will be freed + */ +int ecc_import(const unsigned char *in, unsigned long inlen, ecc_key *key) { + return ecc_import_ex(in, inlen, key, NULL); +} + +/** + Import an ECC key from a binary packet, using user supplied domain params rather than one of the NIST ones + @param in The packet to import + @param inlen The length of the packet + @param key [out] The destination of the import + @param dp pointer to user supplied params; must be the same as the params used when exporting + @return CRYPT_OK if successful, upon error all allocated memory will be freed + */ +int ecc_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, const ltc_ecc_set_type *dp) { + unsigned long key_size; + unsigned char flags[1]; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(ltc_mp.name != NULL); + + /* init key */ + if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, NULL) != CRYPT_OK) { + return CRYPT_MEM; + } + + /* find out what type of key it is */ + if ((err = der_decode_sequence_multi(in, inlen, + LTC_ASN1_BIT_STRING, 1UL, &flags, + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { + goto done; + } + + + if (flags[0] == 1) { + /* private key */ + key->type = PK_PRIVATE; + if ((err = der_decode_sequence_multi(in, inlen, + LTC_ASN1_BIT_STRING, 1UL, flags, + LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, + LTC_ASN1_INTEGER, 1UL, key->pubkey.x, + LTC_ASN1_INTEGER, 1UL, key->pubkey.y, + LTC_ASN1_INTEGER, 1UL, key->k, + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { + goto done; + } + } else { + /* public key */ + key->type = PK_PUBLIC; + if ((err = der_decode_sequence_multi(in, inlen, + LTC_ASN1_BIT_STRING, 1UL, flags, + LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, + LTC_ASN1_INTEGER, 1UL, key->pubkey.x, + LTC_ASN1_INTEGER, 1UL, key->pubkey.y, + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { + goto done; + } + } + + if (dp == NULL) { + /* find the idx */ + for (key->idx = 0; ltc_ecc_sets[key->idx].size && (unsigned long)ltc_ecc_sets[key->idx].size != key_size; ++key->idx); + if (ltc_ecc_sets[key->idx].size == 0) { + err = CRYPT_INVALID_PACKET; + goto done; + } + key->dp = <c_ecc_sets[key->idx]; + } else { + key->idx = -1; + key->dp = dp; + } + /* set z */ + if ((err = mp_set(key->pubkey.z, 1)) != CRYPT_OK) { + goto done; + } + + /* is it a point on the curve? */ + if ((err = is_point(key)) != CRYPT_OK) { + goto done; + } + + /* we're good */ + return CRYPT_OK; +done: + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_import.c,v $ */ +/* $Revision: 1.13 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_make_key.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Make a new ECC key + @param prng An active PRNG state + @param wprng The index of the PRNG you wish to use + @param keysize The keysize for the new key (in octets from 20 to 65 bytes) + @param key [out] Destination of the newly created key + @return CRYPT_OK if successful, upon error all allocated memory will be freed + */ +int ecc_make_key(prng_state *prng, int wprng, int keysize, ecc_key *key) { + int x, err; + + /* find key size */ + for (x = 0; (keysize > ltc_ecc_sets[x].size) && (ltc_ecc_sets[x].size != 0); x++); + keysize = ltc_ecc_sets[x].size; + + if ((keysize > ECC_MAXSIZE) || (ltc_ecc_sets[x].size == 0)) { + return CRYPT_INVALID_KEYSIZE; + } + err = ecc_make_key_ex(prng, wprng, key, <c_ecc_sets[x]); + key->idx = x; + return err; +} + +int ecc_make_key_ex(prng_state *prng, int wprng, ecc_key *key, const ltc_ecc_set_type *dp) { + int err; + ecc_point *base; + void *prime, *order; + unsigned char *buf; + int keysize; + + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(ltc_mp.name != NULL); + LTC_ARGCHK(dp != NULL); + + /* good prng? */ + if ((err = prng_is_valid(wprng)) != CRYPT_OK) { + return err; + } + + key->idx = -1; + key->dp = dp; + keysize = dp->size; + + /* allocate ram */ + base = NULL; + buf = XMALLOC(ECC_MAXSIZE); + if (buf == NULL) { + return CRYPT_MEM; + } + + /* make up random string */ + if (prng_descriptor[wprng].read(buf, (unsigned long)keysize, prng) != (unsigned long)keysize) { + err = CRYPT_ERROR_READPRNG; + goto ERR_BUF; + } + + /* setup the key variables */ + if ((err = mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, &prime, &order, NULL)) != CRYPT_OK) { + goto ERR_BUF; + } + base = ltc_ecc_new_point(); + if (base == NULL) { + err = CRYPT_MEM; + goto errkey; + } + + /* read in the specs for this key */ + if ((err = mp_read_radix(prime, (char *)key->dp->prime, 16)) != CRYPT_OK) { + goto errkey; + } + if ((err = mp_read_radix(order, (char *)key->dp->order, 16)) != CRYPT_OK) { + goto errkey; + } + if ((err = mp_read_radix(base->x, (char *)key->dp->Gx, 16)) != CRYPT_OK) { + goto errkey; + } + if ((err = mp_read_radix(base->y, (char *)key->dp->Gy, 16)) != CRYPT_OK) { + goto errkey; + } + if ((err = mp_set(base->z, 1)) != CRYPT_OK) { + goto errkey; + } + if ((err = mp_read_unsigned_bin(key->k, (unsigned char *)buf, keysize)) != CRYPT_OK) { + goto errkey; + } + + /* the key should be smaller than the order of base point */ + if (mp_cmp(key->k, order) != LTC_MP_LT) { + if ((err = mp_mod(key->k, order, key->k)) != CRYPT_OK) { + goto errkey; + } + } + /* make the public key */ + if ((err = ltc_mp.ecc_ptmul(key->k, base, &key->pubkey, prime, 1)) != CRYPT_OK) { + goto errkey; + } + key->type = PK_PRIVATE; + + /* free up ram */ + err = CRYPT_OK; + goto cleanup; +errkey: + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); +cleanup: + ltc_ecc_del_point(base); + mp_clear_multi(prime, order, NULL); +ERR_BUF: + #ifdef LTC_CLEAN_STACK + zeromem(buf, ECC_MAXSIZE); + #endif + XFREE(buf); + return err; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_make_key.c,v $ */ +/* $Revision: 1.13 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_shared_secret.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Create an ECC shared secret between two keys + @param private_key The private ECC key + @param public_key The public key + @param out [out] Destination of the shared secret (Conforms to EC-DH from ANSI X9.63) + @param outlen [in/out] The max size and resulting size of the shared secret + @return CRYPT_OK if successful + */ +int ecc_shared_secret(ecc_key *private_key, ecc_key *public_key, + unsigned char *out, unsigned long *outlen) { + unsigned long x; + ecc_point *result; + void *prime; + int err; + + LTC_ARGCHK(private_key != NULL); + LTC_ARGCHK(public_key != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* type valid? */ + if (private_key->type != PK_PRIVATE) { + return CRYPT_PK_NOT_PRIVATE; + } + + if ((ltc_ecc_is_valid_idx(private_key->idx) == 0) || (ltc_ecc_is_valid_idx(public_key->idx) == 0)) { + return CRYPT_INVALID_ARG; + } + + if (XSTRCMP(private_key->dp->name, public_key->dp->name) != 0) { + return CRYPT_PK_TYPE_MISMATCH; + } + + /* make new point */ + result = ltc_ecc_new_point(); + if (result == NULL) { + return CRYPT_MEM; + } + + if ((err = mp_init(&prime)) != CRYPT_OK) { + ltc_ecc_del_point(result); + return err; + } + + if ((err = mp_read_radix(prime, (char *)private_key->dp->prime, 16)) != CRYPT_OK) { + goto done; + } + if ((err = ltc_mp.ecc_ptmul(private_key->k, &public_key->pubkey, result, prime, 1)) != CRYPT_OK) { + goto done; + } + + x = (unsigned long)mp_unsigned_bin_size(prime); + if (*outlen < x) { + *outlen = x; + err = CRYPT_BUFFER_OVERFLOW; + goto done; + } + zeromem(out, x); + if ((err = mp_to_unsigned_bin(result->x, out + (x - mp_unsigned_bin_size(result->x)))) != CRYPT_OK) { + goto done; + } + + err = CRYPT_OK; + *outlen = x; +done: + mp_clear(prime); + ltc_ecc_del_point(result); + return err; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_shared_secret.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_sign_hash.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Sign a message digest + @param in The message digest to sign + @param inlen The length of the digest + @param out [out] The destination for the signature + @param outlen [in/out] The max size and resulting size of the signature + @param prng An active PRNG state + @param wprng The index of the PRNG you wish to use + @param key A private ECC key + @return CRYPT_OK if successful + */ +int ecc_sign_hash(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + prng_state *prng, int wprng, ecc_key *key) { + ecc_key pubkey; + void *r, *s, *e, *p; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* is this a private key? */ + if (key->type != PK_PRIVATE) { + return CRYPT_PK_NOT_PRIVATE; + } + + /* is the IDX valid ? */ + if (ltc_ecc_is_valid_idx(key->idx) != 1) { + return CRYPT_PK_INVALID_TYPE; + } + + if ((err = prng_is_valid(wprng)) != CRYPT_OK) { + return err; + } + + /* get the hash and load it as a bignum into 'e' */ + /* init the bignums */ + if ((err = mp_init_multi(&r, &s, &p, &e, NULL)) != CRYPT_OK) { + return err; + } + if ((err = mp_read_radix(p, (char *)key->dp->order, 16)) != CRYPT_OK) { + goto errnokey; + } + if ((err = mp_read_unsigned_bin(e, (unsigned char *)in, (int)inlen)) != CRYPT_OK) { + goto errnokey; + } + + /* make up a key and export the public copy */ + for ( ; ; ) { + if ((err = ecc_make_key_ex(prng, wprng, &pubkey, key->dp)) != CRYPT_OK) { + goto errnokey; + } + + /* find r = x1 mod n */ + if ((err = mp_mod(pubkey.pubkey.x, p, r)) != CRYPT_OK) { + goto error; + } + + if (mp_iszero(r) == LTC_MP_YES) { + ecc_free(&pubkey); + } else { + /* find s = (e + xr)/k */ + if ((err = mp_invmod(pubkey.k, p, pubkey.k)) != CRYPT_OK) { + goto error; + } /* k = 1/k */ + if ((err = mp_mulmod(key->k, r, p, s)) != CRYPT_OK) { + goto error; + } /* s = xr */ + if ((err = mp_add(e, s, s)) != CRYPT_OK) { + goto error; + } /* s = e + xr */ + if ((err = mp_mod(s, p, s)) != CRYPT_OK) { + goto error; + } /* s = e + xr */ + if ((err = mp_mulmod(s, pubkey.k, p, s)) != CRYPT_OK) { + goto error; + } /* s = (e + xr)/k */ + ecc_free(&pubkey); + if (mp_iszero(s) == LTC_MP_NO) { + break; + } + } + } + + /* store as SEQUENCE { r, s -- integer } */ + err = der_encode_sequence_multi(out, outlen, + LTC_ASN1_INTEGER, 1UL, r, + LTC_ASN1_INTEGER, 1UL, s, + LTC_ASN1_EOL, 0UL, NULL); + goto errnokey; +error: + ecc_free(&pubkey); +errnokey: + mp_clear_multi(r, s, p, e, NULL); + return err; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_sign_hash.c,v $ */ +/* $Revision: 1.11 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_sizes.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +void ecc_sizes(int *low, int *high) { + int i; + + LTC_ARGCHKVD(low != NULL); + LTC_ARGCHKVD(high != NULL); + + *low = INT_MAX; + *high = 0; + for (i = 0; ltc_ecc_sets[i].size != 0; i++) { + if (ltc_ecc_sets[i].size < *low) { + *low = ltc_ecc_sets[i].size; + } + if (ltc_ecc_sets[i].size > *high) { + *high = ltc_ecc_sets[i].size; + } + } +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_sizes.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_test.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Perform on the ECC system + @return CRYPT_OK if successful + */ +int ecc_test(void) { + void *modulus, *order; + ecc_point *G, *GG; + int i, err, primality; + + if ((err = mp_init_multi(&modulus, &order, NULL)) != CRYPT_OK) { + return err; + } + + G = ltc_ecc_new_point(); + GG = ltc_ecc_new_point(); + if ((G == NULL) || (GG == NULL)) { + mp_clear_multi(modulus, order, NULL); + ltc_ecc_del_point(G); + ltc_ecc_del_point(GG); + return CRYPT_MEM; + } + + for (i = 0; ltc_ecc_sets[i].size; i++) { + #if 0 + printf("Testing %d\n", ltc_ecc_sets[i].size); + #endif + if ((err = mp_read_radix(modulus, (char *)ltc_ecc_sets[i].prime, 16)) != CRYPT_OK) { + goto done; + } + if ((err = mp_read_radix(order, (char *)ltc_ecc_sets[i].order, 16)) != CRYPT_OK) { + goto done; + } + + /* is prime actually prime? */ + if ((err = mp_prime_is_prime(modulus, 8, &primality)) != CRYPT_OK) { + goto done; + } + if (primality == 0) { + err = CRYPT_FAIL_TESTVECTOR; + goto done; + } + + /* is order prime ? */ + if ((err = mp_prime_is_prime(order, 8, &primality)) != CRYPT_OK) { + goto done; + } + if (primality == 0) { + err = CRYPT_FAIL_TESTVECTOR; + goto done; + } + + if ((err = mp_read_radix(G->x, (char *)ltc_ecc_sets[i].Gx, 16)) != CRYPT_OK) { + goto done; + } + if ((err = mp_read_radix(G->y, (char *)ltc_ecc_sets[i].Gy, 16)) != CRYPT_OK) { + goto done; + } + mp_set(G->z, 1); + + /* then we should have G == (order + 1)G */ + if ((err = mp_add_d(order, 1, order)) != CRYPT_OK) { + goto done; + } + if ((err = ltc_mp.ecc_ptmul(order, G, GG, modulus, 1)) != CRYPT_OK) { + goto done; + } + if ((mp_cmp(G->x, GG->x) != LTC_MP_EQ) || (mp_cmp(G->y, GG->y) != LTC_MP_EQ)) { + err = CRYPT_FAIL_TESTVECTOR; + goto done; + } + } + err = CRYPT_OK; +done: + ltc_ecc_del_point(GG); + ltc_ecc_del_point(G); + mp_clear_multi(order, modulus, NULL); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_test.c,v $ */ +/* $Revision: 1.12 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ecc_verify_hash.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/* verify + * + * w = s^-1 mod n + * u1 = xw + * u2 = rw + * X = u1*G + u2*Q + * v = X_x1 mod n + * accept if v == r + */ + +/** + Verify an ECC signature + @param sig The signature to verify + @param siglen The length of the signature (octets) + @param hash The hash (message digest) that was signed + @param hashlen The length of the hash (octets) + @param stat Result of signature, 1==valid, 0==invalid + @param key The corresponding public ECC key + @return CRYPT_OK if successful (even if the signature is not valid) + */ +int ecc_verify_hash(const unsigned char *sig, unsigned long siglen, + const unsigned char *hash, unsigned long hashlen, + int *stat, ecc_key *key) { + ecc_point *mG, *mQ; + void *r, *s, *v, *w, *u1, *u2, *e, *p, *m; + void *mp; + int err; + + LTC_ARGCHK(sig != NULL); + LTC_ARGCHK(hash != NULL); + LTC_ARGCHK(stat != NULL); + LTC_ARGCHK(key != NULL); + + /* default to invalid signature */ + *stat = 0; + mp = NULL; + + /* is the IDX valid ? */ + if (ltc_ecc_is_valid_idx(key->idx) != 1) { + return CRYPT_PK_INVALID_TYPE; + } + + /* allocate ints */ + if ((err = mp_init_multi(&r, &s, &v, &w, &u1, &u2, &p, &e, &m, NULL)) != CRYPT_OK) { + return CRYPT_MEM; + } + + /* allocate points */ + mG = ltc_ecc_new_point(); + mQ = ltc_ecc_new_point(); + if ((mQ == NULL) || (mG == NULL)) { + err = CRYPT_MEM; + goto error; + } + + /* parse header */ + if ((err = der_decode_sequence_multi(sig, siglen, + LTC_ASN1_INTEGER, 1UL, r, + LTC_ASN1_INTEGER, 1UL, s, + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { + goto error; + } + + /* get the order */ + if ((err = mp_read_radix(p, (char *)key->dp->order, 16)) != CRYPT_OK) { + goto error; + } + + /* get the modulus */ + if ((err = mp_read_radix(m, (char *)key->dp->prime, 16)) != CRYPT_OK) { + goto error; + } + + /* check for zero */ + if (mp_iszero(r) || mp_iszero(s) || (mp_cmp(r, p) != LTC_MP_LT) || (mp_cmp(s, p) != LTC_MP_LT)) { + err = CRYPT_INVALID_PACKET; + goto error; + } + + /* read hash */ + if ((err = mp_read_unsigned_bin(e, (unsigned char *)hash, (int)hashlen)) != CRYPT_OK) { + goto error; + } + + /* w = s^-1 mod n */ + if ((err = mp_invmod(s, p, w)) != CRYPT_OK) { + goto error; + } + + /* u1 = ew */ + if ((err = mp_mulmod(e, w, p, u1)) != CRYPT_OK) { + goto error; + } + + /* u2 = rw */ + if ((err = mp_mulmod(r, w, p, u2)) != CRYPT_OK) { + goto error; + } + + /* find mG and mQ */ + if ((err = mp_read_radix(mG->x, (char *)key->dp->Gx, 16)) != CRYPT_OK) { + goto error; + } + if ((err = mp_read_radix(mG->y, (char *)key->dp->Gy, 16)) != CRYPT_OK) { + goto error; + } + if ((err = mp_set(mG->z, 1)) != CRYPT_OK) { + goto error; + } + + if ((err = mp_copy(key->pubkey.x, mQ->x)) != CRYPT_OK) { + goto error; + } + if ((err = mp_copy(key->pubkey.y, mQ->y)) != CRYPT_OK) { + goto error; + } + if ((err = mp_copy(key->pubkey.z, mQ->z)) != CRYPT_OK) { + goto error; + } + + /* compute u1*mG + u2*mQ = mG */ + if (ltc_mp.ecc_mul2add == NULL) { + if ((err = ltc_mp.ecc_ptmul(u1, mG, mG, m, 0)) != CRYPT_OK) { + goto error; + } + if ((err = ltc_mp.ecc_ptmul(u2, mQ, mQ, m, 0)) != CRYPT_OK) { + goto error; + } + + /* find the montgomery mp */ + if ((err = mp_montgomery_setup(m, &mp)) != CRYPT_OK) { + goto error; + } + + /* add them */ + if ((err = ltc_mp.ecc_ptadd(mQ, mG, mG, m, mp)) != CRYPT_OK) { + goto error; + } + + /* reduce */ + if ((err = ltc_mp.ecc_map(mG, m, mp)) != CRYPT_OK) { + goto error; + } + } else { + /* use Shamir's trick to compute u1*mG + u2*mQ using half of the doubles */ + if ((err = ltc_mp.ecc_mul2add(mG, u1, mQ, u2, mG, m)) != CRYPT_OK) { + goto error; + } + } + + /* v = X_x1 mod n */ + if ((err = mp_mod(mG->x, p, v)) != CRYPT_OK) { + goto error; + } + + /* does v == r */ + if (mp_cmp(v, r) == LTC_MP_EQ) { + *stat = 1; + } + + /* clear up and return */ + err = CRYPT_OK; +error: + ltc_ecc_del_point(mG); + ltc_ecc_del_point(mQ); + mp_clear_multi(r, s, v, w, u1, u2, p, e, m, NULL); + if (mp != NULL) { + mp_montgomery_free(mp); + } + return err; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_verify_hash.c,v $ */ +/* $Revision: 1.14 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + + +/** + @file error_to_string.c + Convert error codes to ASCII strings, Tom St Denis + */ + +static const char * const err_2_str[] = +{ + "CRYPT_OK", + "CRYPT_ERROR", + "Non-fatal 'no-operation' requested.", + + "Invalid keysize for block cipher.", + "Invalid number of rounds for block cipher.", + "Algorithm failed test vectors.", + + "Buffer overflow.", + "Invalid input packet.", + + "Invalid number of bits for a PRNG.", + "Error reading the PRNG.", + + "Invalid cipher specified.", + "Invalid hash specified.", + "Invalid PRNG specified.", + + "Out of memory.", + + "Invalid PK key or key type specified for function.", + "A private PK key is required.", + + "Invalid argument provided.", + "File Not Found", + + "Invalid PK type.", + "Invalid PK system.", + "Duplicate PK key found on keyring.", + "Key not found in keyring.", + "Invalid sized parameter.", + + "Invalid size for prime.", +}; + +/** + Convert an LTC error code to ASCII + @param err The error code + @return A pointer to the ASCII NUL terminated string for the error or "Invalid error code." if the err code was not valid. + */ +const char *error_to_string(int err) { + if ((err < 0) || (err >= (int)(sizeof(err_2_str) / sizeof(err_2_str[0])))) { + return "Invalid error code."; + } else { + return err_2_str[err]; + } +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/error_to_string.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#define DESC_DEF_ONLY + + + +/* $Source: /cvs/libtom/libtomcrypt/src/math/gmp_desc.c,v $ */ +/* $Revision: 1.16 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file hash_file.c + Hash a file, Tom St Denis + */ + +/** + @param hash The index of the hash desired + @param fname The name of the file you wish to hash + @param out [out] The destination of the digest + @param outlen [in/out] The max size and resulting size of the message digest + @result CRYPT_OK if successful + */ +int hash_file(int hash, const char *fname, unsigned char *out, unsigned long *outlen) { +#ifdef LTC_NO_FILE + return CRYPT_NOP; +#else + FILE *in; + int err; + LTC_ARGCHK(fname != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + if ((err = hash_is_valid(hash)) != CRYPT_OK) { + return err; + } + + in = fopen(fname, "rb"); + if (in == NULL) { + return CRYPT_FILE_NOTFOUND; + } + + err = hash_filehandle(hash, in, out, outlen); + if (fclose(in) != 0) { + return CRYPT_ERROR; + } + + return err; +#endif +} + +/* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_file.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:23 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file hash_filehandle.c + Hash open files, Tom St Denis + */ + +/** + Hash data from an open file handle. + @param hash The index of the hash you want to use + @param in The FILE* handle of the file you want to hash + @param out [out] The destination of the digest + @param outlen [in/out] The max size and resulting size of the digest + @result CRYPT_OK if successful + */ +int hash_filehandle(int hash, FILE *in, unsigned char *out, unsigned long *outlen) { +#ifdef LTC_NO_FILE + return CRYPT_NOP; +#else + hash_state md; + unsigned char buf[512]; + size_t x; + int err; + + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(in != NULL); + + if ((err = hash_is_valid(hash)) != CRYPT_OK) { + return err; + } + + if (*outlen < hash_descriptor[hash].hashsize) { + *outlen = hash_descriptor[hash].hashsize; + return CRYPT_BUFFER_OVERFLOW; + } + if ((err = hash_descriptor[hash].init(&md)) != CRYPT_OK) { + return err; + } + + *outlen = hash_descriptor[hash].hashsize; + do { + x = fread(buf, 1, sizeof(buf), in); + if ((err = hash_descriptor[hash].process(&md, buf, x)) != CRYPT_OK) { + return err; + } + } while (x == sizeof(buf)); + err = hash_descriptor[hash].done(&md, out); + + #ifdef LTC_CLEAN_STACK + zeromem(buf, sizeof(buf)); + #endif + return err; +#endif +} + +/* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_filehandle.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:23 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file hash_memory.c + Hash memory helper, Tom St Denis + */ + +/** + Hash a block of memory and store the digest. + @param hash The index of the hash you wish to use + @param in The data you wish to hash + @param inlen The length of the data to hash (octets) + @param out [out] Where to store the digest + @param outlen [in/out] Max size and resulting size of the digest + @return CRYPT_OK if successful + */ +int hash_memory(int hash, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) { + hash_state *md; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + if ((err = hash_is_valid(hash)) != CRYPT_OK) { + return err; + } + + if (*outlen < hash_descriptor[hash].hashsize) { + *outlen = hash_descriptor[hash].hashsize; + return CRYPT_BUFFER_OVERFLOW; + } + + md = XMALLOC(sizeof(hash_state)); + if (md == NULL) { + return CRYPT_MEM; + } + + if ((err = hash_descriptor[hash].init(md)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash].process(md, in, inlen)) != CRYPT_OK) { + goto LBL_ERR; + } + err = hash_descriptor[hash].done(md, out); + *outlen = hash_descriptor[hash].hashsize; +LBL_ERR: +#ifdef LTC_CLEAN_STACK + zeromem(md, sizeof(hash_state)); +#endif + XFREE(md); + + return err; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_memory.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:23 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#include + +/** + @file hash_memory_multi.c + Hash (multiple buffers) memory helper, Tom St Denis + */ + +/** + Hash multiple (non-adjacent) blocks of memory at once. + @param hash The index of the hash you wish to use + @param out [out] Where to store the digest + @param outlen [in/out] Max size and resulting size of the digest + @param in The data you wish to hash + @param inlen The length of the data to hash (octets) + @param ... tuples of (data,len) pairs to hash, terminated with a (NULL,x) (x=don't care) + @return CRYPT_OK if successful + */ +int hash_memory_multi(int hash, unsigned char *out, unsigned long *outlen, + const unsigned char *in, unsigned long inlen, ...) { + hash_state *md; + int err; + va_list args; + const unsigned char *curptr; + unsigned long curlen; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + if ((err = hash_is_valid(hash)) != CRYPT_OK) { + return err; + } + + if (*outlen < hash_descriptor[hash].hashsize) { + *outlen = hash_descriptor[hash].hashsize; + return CRYPT_BUFFER_OVERFLOW; + } + + md = XMALLOC(sizeof(hash_state)); + if (md == NULL) { + return CRYPT_MEM; + } + + if ((err = hash_descriptor[hash].init(md)) != CRYPT_OK) { + goto LBL_ERR; + } + + va_start(args, inlen); + curptr = in; + curlen = inlen; + for ( ; ; ) { + /* process buf */ + if ((err = hash_descriptor[hash].process(md, curptr, curlen)) != CRYPT_OK) { + goto LBL_ERR; + } + /* step to next */ + curptr = va_arg(args, const unsigned char *); + if (curptr == NULL) { + break; + } + curlen = va_arg(args, unsigned long); + } + err = hash_descriptor[hash].done(md, out); + *outlen = hash_descriptor[hash].hashsize; +LBL_ERR: +#ifdef LTC_CLEAN_STACK + zeromem(md, sizeof(hash_state)); +#endif + XFREE(md); + va_end(args); + return err; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_memory_multi.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:23 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ltc_ecc_is_valid_idx.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** Returns whether an ECC idx is valid or not + @param n The idx number to check + @return 1 if valid, 0 if not + */ +int ltc_ecc_is_valid_idx(int n) { + int x; + + for (x = 0; ltc_ecc_sets[x].size != 0; x++); + /* -1 is a valid index --- indicating that the domain params were supplied by the user */ + if ((n >= -1) && (n < x)) { + return 1; + } + return 0; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_is_valid_idx.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ltc_ecc_map.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Map a projective jacbobian point back to affine space + @param P [in/out] The point to map + @param modulus The modulus of the field the ECC curve is in + @param mp The "b" value from montgomery_setup() + @return CRYPT_OK on success + */ +int ltc_ecc_map(ecc_point *P, void *modulus, void *mp) { + void *t1, *t2; + int err; + + LTC_ARGCHK(P != NULL); + LTC_ARGCHK(modulus != NULL); + LTC_ARGCHK(mp != NULL); + + if ((err = mp_init_multi(&t1, &t2, NULL)) != CRYPT_OK) { + return CRYPT_MEM; + } + + /* first map z back to normal */ + if ((err = mp_montgomery_reduce(P->z, modulus, mp)) != CRYPT_OK) { + goto done; + } + + /* get 1/z */ + if ((err = mp_invmod(P->z, modulus, t1)) != CRYPT_OK) { + goto done; + } + + /* get 1/z^2 and 1/z^3 */ + if ((err = mp_sqr(t1, t2)) != CRYPT_OK) { + goto done; + } + if ((err = mp_mod(t2, modulus, t2)) != CRYPT_OK) { + goto done; + } + if ((err = mp_mul(t1, t2, t1)) != CRYPT_OK) { + goto done; + } + if ((err = mp_mod(t1, modulus, t1)) != CRYPT_OK) { + goto done; + } + + /* multiply against x/y */ + if ((err = mp_mul(P->x, t2, P->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(P->x, modulus, mp)) != CRYPT_OK) { + goto done; + } + if ((err = mp_mul(P->y, t1, P->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(P->y, modulus, mp)) != CRYPT_OK) { + goto done; + } + if ((err = mp_set(P->z, 1)) != CRYPT_OK) { + goto done; + } + + err = CRYPT_OK; +done: + mp_clear_multi(t1, t2, NULL); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_map.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ltc_ecc_mul2add.c + ECC Crypto, Shamir's Trick, Tom St Denis + */ + +#ifdef LTC_MECC + + #ifdef LTC_ECC_SHAMIR + +/** Computes kA*A + kB*B = C using Shamir's Trick + @param A First point to multiply + @param kA What to multiple A by + @param B Second point to multiply + @param kB What to multiple B by + @param C [out] Destination point (can overlap with A or B + @param modulus Modulus for curve + @return CRYPT_OK on success + */ +int ltc_ecc_mul2add(ecc_point *A, void *kA, + ecc_point *B, void *kB, + ecc_point *C, + void *modulus) { + ecc_point *precomp[16]; + unsigned bitbufA, bitbufB, lenA, lenB, len, x, y, nA, nB, nibble; + unsigned char *tA, *tB; + int err, first; + void *mp, *mu; + + /* argchks */ + LTC_ARGCHK(A != NULL); + LTC_ARGCHK(B != NULL); + LTC_ARGCHK(C != NULL); + LTC_ARGCHK(kA != NULL); + LTC_ARGCHK(kB != NULL); + LTC_ARGCHK(modulus != NULL); + + /* allocate memory */ + tA = XCALLOC(1, ECC_BUF_SIZE); + if (tA == NULL) { + return CRYPT_MEM; + } + tB = XCALLOC(1, ECC_BUF_SIZE); + if (tB == NULL) { + XFREE(tA); + return CRYPT_MEM; + } + + /* get sizes */ + lenA = mp_unsigned_bin_size(kA); + lenB = mp_unsigned_bin_size(kB); + len = MAX(lenA, lenB); + + /* sanity check */ + if ((lenA > ECC_BUF_SIZE) || (lenB > ECC_BUF_SIZE)) { + err = CRYPT_INVALID_ARG; + goto ERR_T; + } + + /* extract and justify kA */ + mp_to_unsigned_bin(kA, (len - lenA) + tA); + + /* extract and justify kB */ + mp_to_unsigned_bin(kB, (len - lenB) + tB); + + /* allocate the table */ + for (x = 0; x < 16; x++) { + precomp[x] = ltc_ecc_new_point(); + if (precomp[x] == NULL) { + for (y = 0; y < x; ++y) { + ltc_ecc_del_point(precomp[y]); + } + err = CRYPT_MEM; + goto ERR_T; + } + } + + /* init montgomery reduction */ + if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { + goto ERR_P; + } + if ((err = mp_init(&mu)) != CRYPT_OK) { + goto ERR_MP; + } + if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) { + goto ERR_MU; + } + + /* copy ones ... */ + if ((err = mp_mulmod(A->x, mu, modulus, precomp[1]->x)) != CRYPT_OK) { + goto ERR_MU; + } + if ((err = mp_mulmod(A->y, mu, modulus, precomp[1]->y)) != CRYPT_OK) { + goto ERR_MU; + } + if ((err = mp_mulmod(A->z, mu, modulus, precomp[1]->z)) != CRYPT_OK) { + goto ERR_MU; + } + + if ((err = mp_mulmod(B->x, mu, modulus, precomp[1 << 2]->x)) != CRYPT_OK) { + goto ERR_MU; + } + if ((err = mp_mulmod(B->y, mu, modulus, precomp[1 << 2]->y)) != CRYPT_OK) { + goto ERR_MU; + } + if ((err = mp_mulmod(B->z, mu, modulus, precomp[1 << 2]->z)) != CRYPT_OK) { + goto ERR_MU; + } + + /* precomp [i,0](A + B) table */ + if ((err = ltc_mp.ecc_ptdbl(precomp[1], precomp[2], modulus, mp)) != CRYPT_OK) { + goto ERR_MU; + } + if ((err = ltc_mp.ecc_ptadd(precomp[1], precomp[2], precomp[3], modulus, mp)) != CRYPT_OK) { + goto ERR_MU; + } + + /* precomp [0,i](A + B) table */ + if ((err = ltc_mp.ecc_ptdbl(precomp[1 << 2], precomp[2 << 2], modulus, mp)) != CRYPT_OK) { + goto ERR_MU; + } + if ((err = ltc_mp.ecc_ptadd(precomp[1 << 2], precomp[2 << 2], precomp[3 << 2], modulus, mp)) != CRYPT_OK) { + goto ERR_MU; + } + + /* precomp [i,j](A + B) table (i != 0, j != 0) */ + for (x = 1; x < 4; x++) { + for (y = 1; y < 4; y++) { + if ((err = ltc_mp.ecc_ptadd(precomp[x], precomp[(y << 2)], precomp[x + (y << 2)], modulus, mp)) != CRYPT_OK) { + goto ERR_MU; + } + } + } + + nibble = 3; + first = 1; + bitbufA = tA[0]; + bitbufB = tB[0]; + + /* for every byte of the multiplicands */ + for (x = -1; ; ) { + /* grab a nibble */ + if (++nibble == 4) { + ++x; + if (x == len) break; + bitbufA = tA[x]; + bitbufB = tB[x]; + nibble = 0; + } + + /* extract two bits from both, shift/update */ + nA = (bitbufA >> 6) & 0x03; + nB = (bitbufB >> 6) & 0x03; + bitbufA = (bitbufA << 2) & 0xFF; + bitbufB = (bitbufB << 2) & 0xFF; + + /* if both zero, if first, continue */ + if ((nA == 0) && (nB == 0) && (first == 1)) { + continue; + } + + /* double twice, only if this isn't the first */ + if (first == 0) { + /* double twice */ + if ((err = ltc_mp.ecc_ptdbl(C, C, modulus, mp)) != CRYPT_OK) { + goto ERR_MU; + } + if ((err = ltc_mp.ecc_ptdbl(C, C, modulus, mp)) != CRYPT_OK) { + goto ERR_MU; + } + } + + /* if not both zero */ + if ((nA != 0) || (nB != 0)) { + if (first == 1) { + /* if first, copy from table */ + first = 0; + if ((err = mp_copy(precomp[nA + (nB << 2)]->x, C->x)) != CRYPT_OK) { + goto ERR_MU; + } + if ((err = mp_copy(precomp[nA + (nB << 2)]->y, C->y)) != CRYPT_OK) { + goto ERR_MU; + } + if ((err = mp_copy(precomp[nA + (nB << 2)]->z, C->z)) != CRYPT_OK) { + goto ERR_MU; + } + } else { + /* if not first, add from table */ + if ((err = ltc_mp.ecc_ptadd(C, precomp[nA + (nB << 2)], C, modulus, mp)) != CRYPT_OK) { + goto ERR_MU; + } + } + } + } + + /* reduce to affine */ + err = ltc_ecc_map(C, modulus, mp); + + /* clean up */ +ERR_MU: + mp_clear(mu); +ERR_MP: + mp_montgomery_free(mp); +ERR_P: + for (x = 0; x < 16; x++) { + ltc_ecc_del_point(precomp[x]); + } +ERR_T: + #ifdef LTC_CLEAN_STACK + zeromem(tA, ECC_BUF_SIZE); + zeromem(tB, ECC_BUF_SIZE); + #endif + XFREE(tA); + XFREE(tB); + + return err; +} + #endif +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_mul2add.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ltc_ecc_mulmod.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + #ifndef LTC_ECC_TIMING_RESISTANT + +/* size of sliding window, don't change this! */ + #define WINSIZE 4 + +/** + Perform a point multiplication + @param k The scalar to multiply by + @param G The base point + @param R [out] Destination for kG + @param modulus The modulus of the field the ECC curve is in + @param map Boolean whether to map back to affine or not (1==map, 0 == leave in projective) + @return CRYPT_OK on success + */ +int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map) { + ecc_point *tG, *M[8]; + int i, j, err; + void *mu, *mp; + unsigned long buf; + int first, bitbuf, bitcpy, bitcnt, mode, digidx; + + LTC_ARGCHK(k != NULL); + LTC_ARGCHK(G != NULL); + LTC_ARGCHK(R != NULL); + LTC_ARGCHK(modulus != NULL); + + /* init montgomery reduction */ + if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { + return err; + } + if ((err = mp_init(&mu)) != CRYPT_OK) { + mp_montgomery_free(mp); + return err; + } + if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) { + mp_montgomery_free(mp); + mp_clear(mu); + return err; + } + + /* alloc ram for window temps */ + for (i = 0; i < 8; i++) { + M[i] = ltc_ecc_new_point(); + if (M[i] == NULL) { + for (j = 0; j < i; j++) { + ltc_ecc_del_point(M[j]); + } + mp_montgomery_free(mp); + mp_clear(mu); + return CRYPT_MEM; + } + } + + /* make a copy of G incase R==G */ + tG = ltc_ecc_new_point(); + if (tG == NULL) { + err = CRYPT_MEM; + goto done; + } + + /* tG = G and convert to montgomery */ + if (mp_cmp_d(mu, 1) == LTC_MP_EQ) { + if ((err = mp_copy(G->x, tG->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(G->y, tG->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(G->z, tG->z)) != CRYPT_OK) { + goto done; + } + } else { + if ((err = mp_mulmod(G->x, mu, modulus, tG->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_mulmod(G->y, mu, modulus, tG->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_mulmod(G->z, mu, modulus, tG->z)) != CRYPT_OK) { + goto done; + } + } + mp_clear(mu); + mu = NULL; + + /* calc the M tab, which holds kG for k==8..15 */ + /* M[0] == 8G */ + if ((err = ltc_mp.ecc_ptdbl(tG, M[0], modulus, mp)) != CRYPT_OK) { + goto done; + } + if ((err = ltc_mp.ecc_ptdbl(M[0], M[0], modulus, mp)) != CRYPT_OK) { + goto done; + } + if ((err = ltc_mp.ecc_ptdbl(M[0], M[0], modulus, mp)) != CRYPT_OK) { + goto done; + } + + /* now find (8+k)G for k=1..7 */ + for (j = 9; j < 16; j++) { + if ((err = ltc_mp.ecc_ptadd(M[j - 9], tG, M[j - 8], modulus, mp)) != CRYPT_OK) { + goto done; + } + } + + /* setup sliding window */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = mp_get_digit_count(k) - 1; + bitcpy = bitbuf = 0; + first = 1; + + /* perform ops */ + for ( ; ; ) { + /* grab next digit as required */ + if (--bitcnt == 0) { + if (digidx == -1) { + break; + } + buf = mp_get_digit(k, digidx); + bitcnt = (int)ltc_mp.bits_per_digit; + --digidx; + } + + /* grab the next msb from the ltiplicand */ + i = (buf >> (ltc_mp.bits_per_digit - 1)) & 1; + buf <<= 1; + + /* skip leading zero bits */ + if ((mode == 0) && (i == 0)) { + continue; + } + + /* if the bit is zero and mode == 1 then we double */ + if ((mode == 1) && (i == 0)) { + if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK) { + goto done; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (i << (WINSIZE - ++bitcpy)); + mode = 2; + + if (bitcpy == WINSIZE) { + /* if this is the first window we do a simple copy */ + if (first == 1) { + /* R = kG [k = first window] */ + if ((err = mp_copy(M[bitbuf - 8]->x, R->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(M[bitbuf - 8]->y, R->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(M[bitbuf - 8]->z, R->z)) != CRYPT_OK) { + goto done; + } + first = 0; + } else { + /* normal window */ + /* ok window is filled so double as required and add */ + /* double first */ + for (j = 0; j < WINSIZE; j++) { + if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK) { + goto done; + } + } + + /* then add, bitbuf will be 8..15 [8..2^WINSIZE] guaranteed */ + if ((err = ltc_mp.ecc_ptadd(R, M[bitbuf - 8], R, modulus, mp)) != CRYPT_OK) { + goto done; + } + } + /* empty window and reset */ + bitcpy = bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then double/add */ + if ((mode == 2) && (bitcpy > 0)) { + /* double then add */ + for (j = 0; j < bitcpy; j++) { + /* only double if we have had at least one add first */ + if (first == 0) { + if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK) { + goto done; + } + } + + bitbuf <<= 1; + if ((bitbuf & (1 << WINSIZE)) != 0) { + if (first == 1) { + /* first add, so copy */ + if ((err = mp_copy(tG->x, R->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(tG->y, R->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(tG->z, R->z)) != CRYPT_OK) { + goto done; + } + first = 0; + } else { + /* then add */ + if ((err = ltc_mp.ecc_ptadd(R, tG, R, modulus, mp)) != CRYPT_OK) { + goto done; + } + } + } + } + } + + /* map R back from projective space */ + if (map) { + err = ltc_ecc_map(R, modulus, mp); + } else { + err = CRYPT_OK; + } +done: + if (mu != NULL) { + mp_clear(mu); + } + mp_montgomery_free(mp); + ltc_ecc_del_point(tG); + for (i = 0; i < 8; i++) { + ltc_ecc_del_point(M[i]); + } + return err; +} + #endif + + #undef WINSIZE +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_mulmod.c,v $ */ +/* $Revision: 1.26 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ltc_ecc_mulmod_timing.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + + #ifdef LTC_ECC_TIMING_RESISTANT + +/** + Perform a point multiplication (timing resistant) + @param k The scalar to multiply by + @param G The base point + @param R [out] Destination for kG + @param modulus The modulus of the field the ECC curve is in + @param map Boolean whether to map back to affine or not (1==map, 0 == leave in projective) + @return CRYPT_OK on success + */ +int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map) { + ecc_point *tG, *M[3]; + int i, j, err; + void *mu, *mp; + unsigned long buf; + int first, bitbuf, bitcpy, bitcnt, mode, digidx; + + LTC_ARGCHK(k != NULL); + LTC_ARGCHK(G != NULL); + LTC_ARGCHK(R != NULL); + LTC_ARGCHK(modulus != NULL); + + /* init montgomery reduction */ + if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { + return err; + } + if ((err = mp_init(&mu)) != CRYPT_OK) { + mp_montgomery_free(mp); + return err; + } + if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) { + mp_clear(mu); + mp_montgomery_free(mp); + return err; + } + + /* alloc ram for window temps */ + for (i = 0; i < 3; i++) { + M[i] = ltc_ecc_new_point(); + if (M[i] == NULL) { + for (j = 0; j < i; j++) { + ltc_ecc_del_point(M[j]); + } + mp_clear(mu); + mp_montgomery_free(mp); + return CRYPT_MEM; + } + } + + /* make a copy of G incase R==G */ + tG = ltc_ecc_new_point(); + if (tG == NULL) { + err = CRYPT_MEM; + goto done; + } + + /* tG = G and convert to montgomery */ + if ((err = mp_mulmod(G->x, mu, modulus, tG->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_mulmod(G->y, mu, modulus, tG->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_mulmod(G->z, mu, modulus, tG->z)) != CRYPT_OK) { + goto done; + } + mp_clear(mu); + mu = NULL; + + /* calc the M tab */ + /* M[0] == G */ + if ((err = mp_copy(tG->x, M[0]->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(tG->y, M[0]->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(tG->z, M[0]->z)) != CRYPT_OK) { + goto done; + } + /* M[1] == 2G */ + if ((err = ltc_mp.ecc_ptdbl(tG, M[1], modulus, mp)) != CRYPT_OK) { + goto done; + } + + /* setup sliding window */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = mp_get_digit_count(k) - 1; + bitcpy = bitbuf = 0; + first = 1; + + /* perform ops */ + for ( ; ; ) { + /* grab next digit as required */ + if (--bitcnt == 0) { + if (digidx == -1) { + break; + } + buf = mp_get_digit(k, digidx); + bitcnt = (int)MP_DIGIT_BIT; + --digidx; + } + + /* grab the next msb from the ltiplicand */ + i = (buf >> (MP_DIGIT_BIT - 1)) & 1; + buf <<= 1; + + if ((mode == 0) && (i == 0)) { + /* dummy operations */ + if ((err = ltc_mp.ecc_ptadd(M[0], M[1], M[2], modulus, mp)) != CRYPT_OK) { + goto done; + } + if ((err = ltc_mp.ecc_ptdbl(M[1], M[2], modulus, mp)) != CRYPT_OK) { + goto done; + } + continue; + } + + if ((mode == 0) && (i == 1)) { + mode = 1; + /* dummy operations */ + if ((err = ltc_mp.ecc_ptadd(M[0], M[1], M[2], modulus, mp)) != CRYPT_OK) { + goto done; + } + if ((err = ltc_mp.ecc_ptdbl(M[1], M[2], modulus, mp)) != CRYPT_OK) { + goto done; + } + continue; + } + + if ((err = ltc_mp.ecc_ptadd(M[0], M[1], M[i ^ 1], modulus, mp)) != CRYPT_OK) { + goto done; + } + if ((err = ltc_mp.ecc_ptdbl(M[i], M[i], modulus, mp)) != CRYPT_OK) { + goto done; + } + } + + /* copy result out */ + if ((err = mp_copy(M[0]->x, R->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(M[0]->y, R->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(M[0]->z, R->z)) != CRYPT_OK) { + goto done; + } + + /* map R back from projective space */ + if (map) { + err = ltc_ecc_map(R, modulus, mp); + } else { + err = CRYPT_OK; + } +done: + if (mu != NULL) { + mp_clear(mu); + } + mp_montgomery_free(mp); + ltc_ecc_del_point(tG); + for (i = 0; i < 3; i++) { + ltc_ecc_del_point(M[i]); + } + return err; +} + #endif +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_mulmod_timing.c,v $ */ +/* $Revision: 1.13 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ltc_ecc_points.c + ECC Crypto, Tom St Denis + */ + +#ifdef LTC_MECC + +/** + Allocate a new ECC point + @return A newly allocated point or NULL on error + */ +ecc_point *ltc_ecc_new_point(void) { + ecc_point *p; + + p = XCALLOC(1, sizeof(*p)); + if (p == NULL) { + return NULL; + } + if (mp_init_multi(&p->x, &p->y, &p->z, NULL) != CRYPT_OK) { + XFREE(p); + return NULL; + } + return p; +} + +/** Free an ECC point from memory + @param p The point to free + */ +void ltc_ecc_del_point(ecc_point *p) { + /* prevents free'ing null arguments */ + if (p != NULL) { + mp_clear_multi(p->x, p->y, p->z, NULL); /* note: p->z may be NULL but that's ok with this function anyways */ + XFREE(p); + } +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_points.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ltc_ecc_projective_add_point.c + ECC Crypto, Tom St Denis + */ + +#if defined(LTC_MECC) && (!defined(LTC_MECC_ACCEL) || defined(LTM_LTC_DESC)) + +/** + Add two ECC points + @param P The point to add + @param Q The point to add + @param R [out] The destination of the double + @param modulus The modulus of the field the ECC curve is in + @param mp The "b" value from montgomery_setup() + @return CRYPT_OK on success + */ +int ltc_ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp) { + void *t1, *t2, *x, *y, *z; + int err; + + LTC_ARGCHK(P != NULL); + LTC_ARGCHK(Q != NULL); + LTC_ARGCHK(R != NULL); + LTC_ARGCHK(modulus != NULL); + LTC_ARGCHK(mp != NULL); + + if ((err = mp_init_multi(&t1, &t2, &x, &y, &z, NULL)) != CRYPT_OK) { + return err; + } + + /* should we dbl instead? */ + if ((err = mp_sub(modulus, Q->y, t1)) != CRYPT_OK) { + goto done; + } + + if ((mp_cmp(P->x, Q->x) == LTC_MP_EQ) && + ((Q->z != NULL) && (mp_cmp(P->z, Q->z) == LTC_MP_EQ)) && + ((mp_cmp(P->y, Q->y) == LTC_MP_EQ) || (mp_cmp(P->y, t1) == LTC_MP_EQ))) { + mp_clear_multi(t1, t2, x, y, z, NULL); + return ltc_ecc_projective_dbl_point(P, R, modulus, mp); + } + + if ((err = mp_copy(P->x, x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(P->y, y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(P->z, z)) != CRYPT_OK) { + goto done; + } + + /* if Z is one then these are no-operations */ + if (Q->z != NULL) { + /* T1 = Z' * Z' */ + if ((err = mp_sqr(Q->z, t1)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* X = X * T1 */ + if ((err = mp_mul(t1, x, x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(x, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* T1 = Z' * T1 */ + if ((err = mp_mul(Q->z, t1, t1)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* Y = Y * T1 */ + if ((err = mp_mul(t1, y, y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(y, modulus, mp)) != CRYPT_OK) { + goto done; + } + } + + /* T1 = Z*Z */ + if ((err = mp_sqr(z, t1)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* T2 = X' * T1 */ + if ((err = mp_mul(Q->x, t1, t2)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* T1 = Z * T1 */ + if ((err = mp_mul(z, t1, t1)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* T1 = Y' * T1 */ + if ((err = mp_mul(Q->y, t1, t1)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { + goto done; + } + + /* Y = Y - T1 */ + if ((err = mp_sub(y, t1, y)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(y, 0) == LTC_MP_LT) { + if ((err = mp_add(y, modulus, y)) != CRYPT_OK) { + goto done; + } + } + /* T1 = 2T1 */ + if ((err = mp_add(t1, t1, t1)) != CRYPT_OK) { + goto done; + } + if (mp_cmp(t1, modulus) != LTC_MP_LT) { + if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) { + goto done; + } + } + /* T1 = Y + T1 */ + if ((err = mp_add(t1, y, t1)) != CRYPT_OK) { + goto done; + } + if (mp_cmp(t1, modulus) != LTC_MP_LT) { + if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) { + goto done; + } + } + /* X = X - T2 */ + if ((err = mp_sub(x, t2, x)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(x, 0) == LTC_MP_LT) { + if ((err = mp_add(x, modulus, x)) != CRYPT_OK) { + goto done; + } + } + /* T2 = 2T2 */ + if ((err = mp_add(t2, t2, t2)) != CRYPT_OK) { + goto done; + } + if (mp_cmp(t2, modulus) != LTC_MP_LT) { + if ((err = mp_sub(t2, modulus, t2)) != CRYPT_OK) { + goto done; + } + } + /* T2 = X + T2 */ + if ((err = mp_add(t2, x, t2)) != CRYPT_OK) { + goto done; + } + if (mp_cmp(t2, modulus) != LTC_MP_LT) { + if ((err = mp_sub(t2, modulus, t2)) != CRYPT_OK) { + goto done; + } + } + + /* if Z' != 1 */ + if (Q->z != NULL) { + /* Z = Z * Z' */ + if ((err = mp_mul(z, Q->z, z)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(z, modulus, mp)) != CRYPT_OK) { + goto done; + } + } + + /* Z = Z * X */ + if ((err = mp_mul(z, x, z)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(z, modulus, mp)) != CRYPT_OK) { + goto done; + } + + /* T1 = T1 * X */ + if ((err = mp_mul(t1, x, t1)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* X = X * X */ + if ((err = mp_sqr(x, x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(x, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* T2 = T2 * x */ + if ((err = mp_mul(t2, x, t2)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* T1 = T1 * X */ + if ((err = mp_mul(t1, x, t1)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { + goto done; + } + + /* X = Y*Y */ + if ((err = mp_sqr(y, x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(x, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* X = X - T2 */ + if ((err = mp_sub(x, t2, x)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(x, 0) == LTC_MP_LT) { + if ((err = mp_add(x, modulus, x)) != CRYPT_OK) { + goto done; + } + } + + /* T2 = T2 - X */ + if ((err = mp_sub(t2, x, t2)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(t2, 0) == LTC_MP_LT) { + if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK) { + goto done; + } + } + /* T2 = T2 - X */ + if ((err = mp_sub(t2, x, t2)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(t2, 0) == LTC_MP_LT) { + if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK) { + goto done; + } + } + /* T2 = T2 * Y */ + if ((err = mp_mul(t2, y, t2)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* Y = T2 - T1 */ + if ((err = mp_sub(t2, t1, y)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(y, 0) == LTC_MP_LT) { + if ((err = mp_add(y, modulus, y)) != CRYPT_OK) { + goto done; + } + } + /* Y = Y/2 */ + if (mp_isodd(y)) { + if ((err = mp_add(y, modulus, y)) != CRYPT_OK) { + goto done; + } + } + if ((err = mp_div_2(y, y)) != CRYPT_OK) { + goto done; + } + + if ((err = mp_copy(x, R->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(y, R->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(z, R->z)) != CRYPT_OK) { + goto done; + } + + err = CRYPT_OK; +done: + mp_clear_multi(t1, t2, x, y, z, NULL); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_projective_add_point.c,v $ */ +/* $Revision: 1.16 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ + + +/** + @file ltc_ecc_projective_dbl_point.c + ECC Crypto, Tom St Denis + */ + +#if defined(LTC_MECC) && (!defined(LTC_MECC_ACCEL) || defined(LTM_LTC_DESC)) + +/** + Double an ECC point + @param P The point to double + @param R [out] The destination of the double + @param modulus The modulus of the field the ECC curve is in + @param mp The "b" value from montgomery_setup() + @return CRYPT_OK on success + */ +int ltc_ecc_projective_dbl_point(ecc_point *P, ecc_point *R, void *modulus, void *mp) { + void *t1, *t2; + int err; + + LTC_ARGCHK(P != NULL); + LTC_ARGCHK(R != NULL); + LTC_ARGCHK(modulus != NULL); + LTC_ARGCHK(mp != NULL); + + if ((err = mp_init_multi(&t1, &t2, NULL)) != CRYPT_OK) { + return err; + } + + if (P != R) { + if ((err = mp_copy(P->x, R->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(P->y, R->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_copy(P->z, R->z)) != CRYPT_OK) { + goto done; + } + } + + /* t1 = Z * Z */ + if ((err = mp_sqr(R->z, t1)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* Z = Y * Z */ + if ((err = mp_mul(R->z, R->y, R->z)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(R->z, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* Z = 2Z */ + if ((err = mp_add(R->z, R->z, R->z)) != CRYPT_OK) { + goto done; + } + if (mp_cmp(R->z, modulus) != LTC_MP_LT) { + if ((err = mp_sub(R->z, modulus, R->z)) != CRYPT_OK) { + goto done; + } + } + + /* T2 = X - T1 */ + if ((err = mp_sub(R->x, t1, t2)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(t2, 0) == LTC_MP_LT) { + if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK) { + goto done; + } + } + /* T1 = X + T1 */ + if ((err = mp_add(t1, R->x, t1)) != CRYPT_OK) { + goto done; + } + if (mp_cmp(t1, modulus) != LTC_MP_LT) { + if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) { + goto done; + } + } + /* T2 = T1 * T2 */ + if ((err = mp_mul(t1, t2, t2)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* T1 = 2T2 */ + if ((err = mp_add(t2, t2, t1)) != CRYPT_OK) { + goto done; + } + if (mp_cmp(t1, modulus) != LTC_MP_LT) { + if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) { + goto done; + } + } + /* T1 = T1 + T2 */ + if ((err = mp_add(t1, t2, t1)) != CRYPT_OK) { + goto done; + } + if (mp_cmp(t1, modulus) != LTC_MP_LT) { + if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) { + goto done; + } + } + + /* Y = 2Y */ + if ((err = mp_add(R->y, R->y, R->y)) != CRYPT_OK) { + goto done; + } + if (mp_cmp(R->y, modulus) != LTC_MP_LT) { + if ((err = mp_sub(R->y, modulus, R->y)) != CRYPT_OK) { + goto done; + } + } + /* Y = Y * Y */ + if ((err = mp_sqr(R->y, R->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(R->y, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* T2 = Y * Y */ + if ((err = mp_sqr(R->y, t2)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* T2 = T2/2 */ + if (mp_isodd(t2)) { + if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK) { + goto done; + } + } + if ((err = mp_div_2(t2, t2)) != CRYPT_OK) { + goto done; + } + /* Y = Y * X */ + if ((err = mp_mul(R->y, R->x, R->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(R->y, modulus, mp)) != CRYPT_OK) { + goto done; + } + + /* X = T1 * T1 */ + if ((err = mp_sqr(t1, R->x)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(R->x, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* X = X - Y */ + if ((err = mp_sub(R->x, R->y, R->x)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(R->x, 0) == LTC_MP_LT) { + if ((err = mp_add(R->x, modulus, R->x)) != CRYPT_OK) { + goto done; + } + } + /* X = X - Y */ + if ((err = mp_sub(R->x, R->y, R->x)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(R->x, 0) == LTC_MP_LT) { + if ((err = mp_add(R->x, modulus, R->x)) != CRYPT_OK) { + goto done; + } + } + + /* Y = Y - X */ + if ((err = mp_sub(R->y, R->x, R->y)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(R->y, 0) == LTC_MP_LT) { + if ((err = mp_add(R->y, modulus, R->y)) != CRYPT_OK) { + goto done; + } + } + /* Y = Y * T1 */ + if ((err = mp_mul(R->y, t1, R->y)) != CRYPT_OK) { + goto done; + } + if ((err = mp_montgomery_reduce(R->y, modulus, mp)) != CRYPT_OK) { + goto done; + } + /* Y = Y - T2 */ + if ((err = mp_sub(R->y, t2, R->y)) != CRYPT_OK) { + goto done; + } + if (mp_cmp_d(R->y, 0) == LTC_MP_LT) { + if ((err = mp_add(R->y, modulus, R->y)) != CRYPT_OK) { + goto done; + } + } + + err = CRYPT_OK; +done: + mp_clear_multi(t1, t2, NULL); + return err; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_projective_dbl_point.c,v $ */ +/* $Revision: 1.11 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#define DESC_DEF_ONLY + +#ifdef LTM_DESC + +#undef mp_init +#undef mp_init_multi +#undef mp_clear +#undef mp_clear_multi +#undef mp_init_copy +#undef mp_neg +#undef mp_copy +#undef mp_set +#undef mp_set_int +#undef mp_get_int +#undef mp_get_digit +#undef mp_get_digit_count +#undef mp_cmp +#undef mp_cmp_d +#undef mp_count_bits +#undef mp_cnt_lsb +#undef mp_2expt +#undef mp_read_radix +#undef mp_toradix +#undef mp_unsigned_bin_size +#undef mp_to_unsigned_bin +#undef mp_read_unsigned_bin +#undef mp_add +#undef mp_add_d +#undef mp_sub +#undef mp_sub_d +#undef mp_mul +#undef mp_mul_d +#undef mp_sqr +#undef mp_div +#undef mp_div_2 +#undef mp_mod +#undef mp_mod_d +#undef mp_gcd +#undef mp_lcm +#undef mp_mulmod +#undef mp_sqrmod +#undef mp_invmod +#undef mp_montgomery_setup +#undef mp_montgomery_normalization +#undef mp_montgomery_reduce +#undef mp_montgomery_free +#undef mp_exptmod +#undef mp_prime_is_prime +#undef mp_iszero +#undef mp_isodd +#undef mp_exch +#undef mp_tohex + +static const struct { + int mpi_code, ltc_code; +} mpi_to_ltc_codes[] = { + { MP_OKAY, CRYPT_OK }, + { MP_MEM, CRYPT_MEM }, + { MP_VAL, CRYPT_INVALID_ARG }, +}; + +/** + Convert a MPI error to a LTC error (Possibly the most powerful function ever! Oh wait... no) + @param err The error to convert + @return The equivalent LTC error code or CRYPT_ERROR if none found + */ +static int mpi_to_ltc_error(int err) { + int x; + + for (x = 0; x < (int)(sizeof(mpi_to_ltc_codes) / sizeof(mpi_to_ltc_codes[0])); x++) { + if (err == mpi_to_ltc_codes[x].mpi_code) { + return mpi_to_ltc_codes[x].ltc_code; + } + } + return CRYPT_ERROR; +} + +static int init(void **a) { + int err; + + LTC_ARGCHK(a != NULL); + + *a = XCALLOC(1, sizeof(mp_int)); + if (*a == NULL) { + return CRYPT_MEM; + } + if ((err = mpi_to_ltc_error(mp_init(*a))) != CRYPT_OK) { + XFREE(*a); + } + return err; +} + +static void deinit(void *a) { + LTC_ARGCHKVD(a != NULL); + mp_clear(a); + XFREE(a); +} + +static int neg(void *a, void *b) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_neg(a, b)); +} + +static int _copy(void *a, void *b) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_copy(a, b)); +} + +static int init_copy(void **a, void *b) { + if (init(a) != CRYPT_OK) { + return CRYPT_MEM; + } + return _copy(b, *a); +} + +/* ---- trivial ---- */ +static int set_int(void *a, unsigned long b) { + LTC_ARGCHK(a != NULL); + return mpi_to_ltc_error(mp_set_int(a, b)); +} + +static unsigned long get_int(void *a) { + LTC_ARGCHK(a != NULL); + return mp_get_int(a); +} + +static unsigned long get_digit(void *a, int n) { + mp_int *A; + + LTC_ARGCHK(a != NULL); + A = a; + return (n >= A->used || n < 0) ? 0 : A->dp[n]; +} + +static int get_digit_count(void *a) { + mp_int *A; + + LTC_ARGCHK(a != NULL); + A = a; + return A->used; +} + +static int compare(void *a, void *b) { + int ret; + + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + ret = mp_cmp(a, b); + switch (ret) { + case MP_LT: + return LTC_MP_LT; + + case MP_EQ: + return LTC_MP_EQ; + + case MP_GT: + return LTC_MP_GT; + } + return 0; +} + +static int compare_d(void *a, unsigned long b) { + int ret; + + LTC_ARGCHK(a != NULL); + ret = mp_cmp_d(a, b); + switch (ret) { + case MP_LT: + return LTC_MP_LT; + + case MP_EQ: + return LTC_MP_EQ; + + case MP_GT: + return LTC_MP_GT; + } + return 0; +} + +static int count_bits(void *a) { + LTC_ARGCHK(a != NULL); + return mp_count_bits(a); +} + +static int count_lsb_bits(void *a) { + LTC_ARGCHK(a != NULL); + return mp_cnt_lsb(a); +} + +static int twoexpt(void *a, int n) { + LTC_ARGCHK(a != NULL); + return mpi_to_ltc_error(mp_2expt(a, n)); +} + +/* ---- conversions ---- */ + +/* read ascii string */ +static int read_radix(void *a, const char *b, int radix) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_read_radix(a, b, radix)); +} + +/* write one */ +static int write_radix(void *a, char *b, int radix) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_toradix(a, b, radix)); +} + +/* get size as unsigned char string */ +static unsigned long unsigned_size(void *a) { + LTC_ARGCHK(a != NULL); + return mp_unsigned_bin_size(a); +} + +/* store */ +static int unsigned_write(void *a, unsigned char *b) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_to_unsigned_bin(a, b)); +} + +/* read */ +static int unsigned_read(void *a, unsigned char *b, unsigned long len) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_read_unsigned_bin(a, b, len)); +} + +/* add */ +static int add(void *a, void *b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_add(a, b, c)); +} + +static int addi(void *a, unsigned long b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_add_d(a, b, c)); +} + +/* sub */ +static int sub(void *a, void *b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_sub(a, b, c)); +} + +static int subi(void *a, unsigned long b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_sub_d(a, b, c)); +} + +/* mul */ +static int mul(void *a, void *b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_mul(a, b, c)); +} + +static int muli(void *a, unsigned long b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_mul_d(a, b, c)); +} + +/* sqr */ +static int sqr(void *a, void *b) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_sqr(a, b)); +} + +/* div */ +static int divide(void *a, void *b, void *c, void *d) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_div(a, b, c, d)); +} + +static int div_2(void *a, void *b) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_div_2(a, b)); +} + +/* modi */ +static int modi(void *a, unsigned long b, unsigned long *c) { + mp_digit tmp; + int err; + + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(c != NULL); + + if ((err = mpi_to_ltc_error(mp_mod_d(a, b, &tmp))) != CRYPT_OK) { + return err; + } + *c = tmp; + return CRYPT_OK; +} + +/* gcd */ +static int gcd(void *a, void *b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_gcd(a, b, c)); +} + +/* lcm */ +static int lcm(void *a, void *b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_lcm(a, b, c)); +} + +static int mulmod(void *a, void *b, void *c, void *d) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + LTC_ARGCHK(d != NULL); + return mpi_to_ltc_error(mp_mulmod(a, b, c, d)); +} + +static int sqrmod(void *a, void *b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_sqrmod(a, b, c)); +} + +/* invmod */ +static int invmod(void *a, void *b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_invmod(a, b, c)); +} + +/* setup */ +static int montgomery_setup(void *a, void **b) { + int err; + + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + *b = XCALLOC(1, sizeof(mp_digit)); + if (*b == NULL) { + return CRYPT_MEM; + } + if ((err = mpi_to_ltc_error(mp_montgomery_setup(a, (mp_digit *)*b))) != CRYPT_OK) { + XFREE(*b); + } + return err; +} + +/* get normalization value */ +static int montgomery_normalization(void *a, void *b) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + return mpi_to_ltc_error(mp_montgomery_calc_normalization(a, b)); +} + +/* reduce */ +static int montgomery_reduce(void *a, void *b, void *c) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + return mpi_to_ltc_error(mp_montgomery_reduce(a, b, *((mp_digit *)c))); +} + +/* clean up */ +static void montgomery_deinit(void *a) { + XFREE(a); +} + +static int exptmod(void *a, void *b, void *c, void *d) { + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + LTC_ARGCHK(c != NULL); + LTC_ARGCHK(d != NULL); + return mpi_to_ltc_error(mp_exptmod(a, b, c, d)); +} + +static int isprime(void *a, int *b) { + int err; + + LTC_ARGCHK(a != NULL); + LTC_ARGCHK(b != NULL); + err = mpi_to_ltc_error(mp_prime_is_prime(a, 8, b)); + *b = (*b == MP_YES) ? LTC_MP_YES : LTC_MP_NO; + return err; +} + +const ltc_math_descriptor ltm_desc = { + "LibTomMath", + (int)DIGIT_BIT, + + &init, + &init_copy, + &deinit, + + &neg, + &_copy, + + &set_int, + &get_int, + &get_digit, + &get_digit_count, + &compare, + &compare_d, + &count_bits, + &count_lsb_bits, + &twoexpt, + + &read_radix, + &write_radix, + &unsigned_size, + &unsigned_write, + &unsigned_read, + + &add, + &addi, + &sub, + &subi, + &mul, + &muli, + &sqr, + ÷, + &div_2, + &modi, + &gcd, + &lcm, + + &mulmod, + &sqrmod, + &invmod, + + &montgomery_setup, + &montgomery_normalization, + &montgomery_reduce, + &montgomery_deinit, + + &exptmod, + &isprime, + + #ifdef LTC_MECC + #ifdef LTC_MECC_FP + <c_ecc_fp_mulmod, + #else + <c_ecc_mulmod, + #endif + <c_ecc_projective_add_point, + <c_ecc_projective_dbl_point, + <c_ecc_map, + #ifdef LTC_ECC_SHAMIR + #ifdef LTC_MECC_FP + <c_ecc_fp_mul2add, + #else + <c_ecc_mul2add, + #endif /* LTC_MECC_FP */ + #else + NULL, + #endif /* LTC_ECC_SHAMIR */ + #else + NULL, NULL,NULL, NULL, NULL, + #endif /* LTC_MECC */ + + #ifdef LTC_MRSA + &rsa_make_key, + &rsa_exptmod, + #else + NULL, NULL + #endif +}; + + #define mp_init(a) ltc_mp.init(a) + #define mp_init_multi ltc_init_multi + #define mp_clear(a) ltc_mp.deinit(a) + #define mp_clear_multi ltc_deinit_multi + #define mp_init_copy(a, b) ltc_mp.init_copy(a, b) + + #define mp_neg(a, b) ltc_mp.neg(a, b) + #define mp_copy(a, b) ltc_mp.copy(a, b) + + #define mp_set(a, b) ltc_mp.set_int(a, b) + #define mp_set_int(a, b) ltc_mp.set_int(a, b) + #define mp_get_int(a) ltc_mp.get_int(a) + #define mp_get_digit(a, n) ltc_mp.get_digit(a, n) + #define mp_get_digit_count(a) ltc_mp.get_digit_count(a) + #define mp_cmp(a, b) ltc_mp.compare(a, b) + #define mp_cmp_d(a, b) ltc_mp.compare_d(a, b) + #define mp_count_bits(a) ltc_mp.count_bits(a) + #define mp_cnt_lsb(a) ltc_mp.count_lsb_bits(a) + #define mp_2expt(a, b) ltc_mp.twoexpt(a, b) + + #define mp_read_radix(a, b, c) ltc_mp.read_radix(a, b, c) + #define mp_toradix(a, b, c) ltc_mp.write_radix(a, b, c) + #define mp_unsigned_bin_size(a) ltc_mp.unsigned_size(a) + #define mp_to_unsigned_bin(a, b) ltc_mp.unsigned_write(a, b) + #define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c) + + #define mp_add(a, b, c) ltc_mp.add(a, b, c) + #define mp_add_d(a, b, c) ltc_mp.addi(a, b, c) + #define mp_sub(a, b, c) ltc_mp.sub(a, b, c) + #define mp_sub_d(a, b, c) ltc_mp.subi(a, b, c) + #define mp_mul(a, b, c) ltc_mp.mul(a, b, c) + #define mp_mul_d(a, b, c) ltc_mp.muli(a, b, c) + #define mp_sqr(a, b) ltc_mp.sqr(a, b) + #define mp_div(a, b, c, d) ltc_mp.mpdiv(a, b, c, d) + #define mp_div_2(a, b) ltc_mp.div_2(a, b) + #define mp_mod(a, b, c) ltc_mp.mpdiv(a, b, NULL, c) + #define mp_mod_d(a, b, c) ltc_mp.modi(a, b, c) + #define mp_gcd(a, b, c) ltc_mp.gcd(a, b, c) + #define mp_lcm(a, b, c) ltc_mp.lcm(a, b, c) + + #define mp_mulmod(a, b, c, d) ltc_mp.mulmod(a, b, c, d) + #define mp_sqrmod(a, b, c) ltc_mp.sqrmod(a, b, c) + #define mp_invmod(a, b, c) ltc_mp.invmod(a, b, c) + + #define mp_montgomery_setup(a, b) ltc_mp.montgomery_setup(a, b) + #define mp_montgomery_normalization(a, b) ltc_mp.montgomery_normalization(a, b) + #define mp_montgomery_reduce(a, b, c) ltc_mp.montgomery_reduce(a, b, c) + #define mp_montgomery_free(a) ltc_mp.montgomery_deinit(a) + + #define mp_exptmod(a, b, c, d) ltc_mp.exptmod(a, b, c, d) + #define mp_prime_is_prime(a, b, c) ltc_mp.isprime(a, c) + + #define mp_iszero(a) (mp_cmp_d(a, 0) == LTC_MP_EQ ? LTC_MP_YES : LTC_MP_NO) + #define mp_isodd(a) (mp_get_digit_count(a) > 0 ? (mp_get_digit(a, 0) & 1 ? LTC_MP_YES : LTC_MP_NO) : LTC_MP_NO) + #define mp_exch(a, b) do { void *ABC__tmp = a; a = b; b = ABC__tmp; } while (0); + + #define mp_tohex(a, b) mp_toradix(a, b, 16) + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/math/ltm_desc.c,v $ */ +/* $Revision: 1.31 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +#ifdef MPI +#include + +int ltc_init_multi(void **a, ...) { + void **cur = a; + int np = 0; + va_list args; + + va_start(args, a); + while (cur != NULL) { + if (mp_init(cur) != CRYPT_OK) { + /* failed */ + va_list clean_list; + + va_start(clean_list, a); + cur = a; + while (np--) { + mp_clear(*cur); + cur = va_arg(clean_list, void **); + } + va_end(clean_list); + return CRYPT_MEM; + } + ++np; + cur = va_arg(args, void **); + } + va_end(args); + return CRYPT_OK; +} + +void ltc_deinit_multi(void *a, ...) { + void *cur = a; + va_list args; + + va_start(args, a); + while (cur != NULL) { + mp_clear(cur); + cur = va_arg(args, void *); + } + va_end(args); +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/math/multi.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:23 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file pkcs_1_i2osp.c + Integer to Octet I2OSP, Tom St Denis + */ + +#ifdef LTC_PKCS_1 + +/* always stores the same # of bytes, pads with leading zero bytes + as required + */ + +/** + LTC_PKCS #1 Integer to binary + @param n The integer to store + @param modulus_len The length of the RSA modulus + @param out [out] The destination for the integer + @return CRYPT_OK if successful + */ +int pkcs_1_i2osp(void *n, unsigned long modulus_len, unsigned char *out) { + unsigned long size; + + size = mp_unsigned_bin_size(n); + + if (size > modulus_len) { + return CRYPT_BUFFER_OVERFLOW; + } + + /* store it */ + zeromem(out, modulus_len); + return mp_to_unsigned_bin(n, out + (modulus_len - size)); +} +#endif /* LTC_PKCS_1 */ + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_i2osp.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file pkcs_1_mgf1.c + The Mask Generation Function (MGF1) for LTC_PKCS #1, Tom St Denis + */ + +#ifdef LTC_PKCS_1 + +/** + Perform LTC_PKCS #1 MGF1 (internal) + @param seed The seed for MGF1 + @param seedlen The length of the seed + @param hash_idx The index of the hash desired + @param mask [out] The destination + @param masklen The length of the mask desired + @return CRYPT_OK if successful + */ +int pkcs_1_mgf1(int hash_idx, + const unsigned char *seed, unsigned long seedlen, + unsigned char *mask, unsigned long masklen) { + unsigned long hLen, x; + ulong32 counter; + int err; + hash_state *md; + unsigned char *buf; + + LTC_ARGCHK(seed != NULL); + LTC_ARGCHK(mask != NULL); + + /* ensure valid hash */ + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + + /* get hash output size */ + hLen = hash_descriptor[hash_idx].hashsize; + + /* allocate memory */ + md = XMALLOC(sizeof(hash_state)); + buf = XMALLOC(hLen); + if ((md == NULL) || (buf == NULL)) { + if (md != NULL) { + XFREE(md); + } + if (buf != NULL) { + XFREE(buf); + } + return CRYPT_MEM; + } + + /* start counter */ + counter = 0; + + while (masklen > 0) { + /* handle counter */ + STORE32H(counter, buf); + ++counter; + + /* get hash of seed || counter */ + if ((err = hash_descriptor[hash_idx].init(md)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].process(md, seed, seedlen)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].process(md, buf, 4)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].done(md, buf)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* store it */ + for (x = 0; x < hLen && masklen > 0; x++, masklen--) { + *mask++ = buf[x]; + } + } + + err = CRYPT_OK; +LBL_ERR: + #ifdef LTC_CLEAN_STACK + zeromem(buf, hLen); + zeromem(md, sizeof(hash_state)); + #endif + + XFREE(buf); + XFREE(md); + + return err; +} +#endif /* LTC_PKCS_1 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_mgf1.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file pkcs_1_oaep_decode.c + OAEP Padding for LTC_PKCS #1, Tom St Denis + */ + +#ifdef LTC_PKCS_1 + +/** + LTC_PKCS #1 v2.00 OAEP decode + @param msg The encoded data to decode + @param msglen The length of the encoded data (octets) + @param lparam The session or system data (can be NULL) + @param lparamlen The length of the lparam + @param modulus_bitlen The bit length of the RSA modulus + @param hash_idx The index of the hash desired + @param out [out] Destination of decoding + @param outlen [in/out] The max size and resulting size of the decoding + @param res [out] Result of decoding, 1==valid, 0==invalid + @return CRYPT_OK if successful (even if invalid) + */ +int pkcs_1_oaep_decode(const unsigned char *msg, unsigned long msglen, + const unsigned char *lparam, unsigned long lparamlen, + unsigned long modulus_bitlen, int hash_idx, + unsigned char *out, unsigned long *outlen, + int *res) { + unsigned char *DB, *seed, *mask; + unsigned long hLen, x, y, modulus_len; + int err; + + LTC_ARGCHK(msg != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(res != NULL); + + /* default to invalid packet */ + *res = 0; + + /* test valid hash */ + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + hLen = hash_descriptor[hash_idx].hashsize; + modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); + + /* test hash/message size */ + if ((2 * hLen >= (modulus_len - 2)) || (msglen != modulus_len)) { + return CRYPT_PK_INVALID_SIZE; + } + + /* allocate ram for DB/mask/salt of size modulus_len */ + DB = XMALLOC(modulus_len); + mask = XMALLOC(modulus_len); + seed = XMALLOC(hLen); + if ((DB == NULL) || (mask == NULL) || (seed == NULL)) { + if (DB != NULL) { + XFREE(DB); + } + if (mask != NULL) { + XFREE(mask); + } + if (seed != NULL) { + XFREE(seed); + } + return CRYPT_MEM; + } + + /* ok so it's now in the form + + 0x00 || maskedseed || maskedDB + + 1 || hLen || modulus_len - hLen - 1 + + */ + + /* must have leading 0x00 byte */ + if (msg[0] != 0x00) { + err = CRYPT_OK; + goto LBL_ERR; + } + + /* now read the masked seed */ + x = 1; + XMEMCPY(seed, msg + x, hLen); + x += hLen; + + /* now read the masked DB */ + XMEMCPY(DB, msg + x, modulus_len - hLen - 1); + x += modulus_len - hLen - 1; + + /* compute MGF1 of maskedDB (hLen) */ + if ((err = pkcs_1_mgf1(hash_idx, DB, modulus_len - hLen - 1, mask, hLen)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* XOR against seed */ + for (y = 0; y < hLen; y++) { + seed[y] ^= mask[y]; + } + + /* compute MGF1 of seed (k - hlen - 1) */ + if ((err = pkcs_1_mgf1(hash_idx, seed, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* xor against DB */ + for (y = 0; y < (modulus_len - hLen - 1); y++) { + DB[y] ^= mask[y]; + } + + /* now DB == lhash || PS || 0x01 || M, PS == k - mlen - 2hlen - 2 zeroes */ + + /* compute lhash and store it in seed [reuse temps!] */ + x = modulus_len; + if (lparam != NULL) { + if ((err = hash_memory(hash_idx, lparam, lparamlen, seed, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + } else { + /* can't pass hash_memory a NULL so use DB with zero length */ + if ((err = hash_memory(hash_idx, DB, 0, seed, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + } + + /* compare the lhash'es */ + if (XMEMCMP(seed, DB, hLen) != 0) { + err = CRYPT_OK; + goto LBL_ERR; + } + + /* now zeroes before a 0x01 */ + for (x = hLen; x < (modulus_len - hLen - 1) && DB[x] == 0x00; x++) { + /* step... */ + } + + /* error out if wasn't 0x01 */ + if ((x == (modulus_len - hLen - 1)) || (DB[x] != 0x01)) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } + + /* rest is the message (and skip 0x01) */ + if ((modulus_len - hLen - 1 - ++x) > *outlen) { + *outlen = modulus_len - hLen - 1 - x; + err = CRYPT_BUFFER_OVERFLOW; + goto LBL_ERR; + } + + /* copy message */ + *outlen = modulus_len - hLen - 1 - x; + XMEMCPY(out, DB + x, modulus_len - hLen - 1 - x); + x += modulus_len - hLen - 1; + + /* valid packet */ + *res = 1; + + err = CRYPT_OK; +LBL_ERR: + #ifdef LTC_CLEAN_STACK + zeromem(DB, modulus_len); + zeromem(seed, hLen); + zeromem(mask, modulus_len); + #endif + + XFREE(seed); + XFREE(mask); + XFREE(DB); + + return err; +} +#endif /* LTC_PKCS_1 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_oaep_decode.c,v $ */ +/* $Revision: 1.13 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file pkcs_1_oaep_encode.c + OAEP Padding for LTC_PKCS #1, Tom St Denis + */ + +#ifdef LTC_PKCS_1 + +/** + LTC_PKCS #1 v2.00 OAEP encode + @param msg The data to encode + @param msglen The length of the data to encode (octets) + @param lparam A session or system parameter (can be NULL) + @param lparamlen The length of the lparam data + @param modulus_bitlen The bit length of the RSA modulus + @param prng An active PRNG state + @param prng_idx The index of the PRNG desired + @param hash_idx The index of the hash desired + @param out [out] The destination for the encoded data + @param outlen [in/out] The max size and resulting size of the encoded data + @return CRYPT_OK if successful + */ +int pkcs_1_oaep_encode(const unsigned char *msg, unsigned long msglen, + const unsigned char *lparam, unsigned long lparamlen, + unsigned long modulus_bitlen, prng_state *prng, + int prng_idx, int hash_idx, + unsigned char *out, unsigned long *outlen) { + unsigned char *DB, *seed, *mask; + unsigned long hLen, x, y, modulus_len; + int err; + + LTC_ARGCHK(msg != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* test valid hash */ + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + + /* valid prng */ + if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) { + return err; + } + + hLen = hash_descriptor[hash_idx].hashsize; + modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); + + /* test message size */ + if ((2 * hLen >= (modulus_len - 2)) || (msglen > (modulus_len - 2 * hLen - 2))) { + return CRYPT_PK_INVALID_SIZE; + } + + /* allocate ram for DB/mask/salt of size modulus_len */ + DB = XMALLOC(modulus_len); + mask = XMALLOC(modulus_len); + seed = XMALLOC(hLen); + if ((DB == NULL) || (mask == NULL) || (seed == NULL)) { + if (DB != NULL) { + XFREE(DB); + } + if (mask != NULL) { + XFREE(mask); + } + if (seed != NULL) { + XFREE(seed); + } + return CRYPT_MEM; + } + + /* get lhash */ + /* DB == lhash || PS || 0x01 || M, PS == k - mlen - 2hlen - 2 zeroes */ + x = modulus_len; + if (lparam != NULL) { + if ((err = hash_memory(hash_idx, lparam, lparamlen, DB, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + } else { + /* can't pass hash_memory a NULL so use DB with zero length */ + if ((err = hash_memory(hash_idx, DB, 0, DB, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + } + + /* append PS then 0x01 (to lhash) */ + x = hLen; + y = modulus_len - msglen - 2 * hLen - 2; + XMEMSET(DB + x, 0, y); + x += y; + + /* 0x01 byte */ + DB[x++] = 0x01; + + /* message (length = msglen) */ + XMEMCPY(DB + x, msg, msglen); + x += msglen; + + /* now choose a random seed */ + if (prng_descriptor[prng_idx].read(seed, hLen, prng) != hLen) { + err = CRYPT_ERROR_READPRNG; + goto LBL_ERR; + } + + /* compute MGF1 of seed (k - hlen - 1) */ + if ((err = pkcs_1_mgf1(hash_idx, seed, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* xor against DB */ + for (y = 0; y < (modulus_len - hLen - 1); y++) { + DB[y] ^= mask[y]; + } + + /* compute MGF1 of maskedDB (hLen) */ + if ((err = pkcs_1_mgf1(hash_idx, DB, modulus_len - hLen - 1, mask, hLen)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* XOR against seed */ + for (y = 0; y < hLen; y++) { + seed[y] ^= mask[y]; + } + + /* create string of length modulus_len */ + if (*outlen < modulus_len) { + *outlen = modulus_len; + err = CRYPT_BUFFER_OVERFLOW; + goto LBL_ERR; + } + + /* start output which is 0x00 || maskedSeed || maskedDB */ + x = 0; + out[x++] = 0x00; + XMEMCPY(out + x, seed, hLen); + x += hLen; + XMEMCPY(out + x, DB, modulus_len - hLen - 1); + x += modulus_len - hLen - 1; + + *outlen = x; + + err = CRYPT_OK; +LBL_ERR: + #ifdef LTC_CLEAN_STACK + zeromem(DB, modulus_len); + zeromem(seed, hLen); + zeromem(mask, modulus_len); + #endif + + XFREE(seed); + XFREE(mask); + XFREE(DB); + + return err; +} +#endif /* LTC_PKCS_1 */ + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_oaep_encode.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file pkcs_1_os2ip.c + Octet to Integer OS2IP, Tom St Denis + */ +#ifdef LTC_PKCS_1 + +/** + Read a binary string into an mp_int + @param n [out] The mp_int destination + @param in The binary string to read + @param inlen The length of the binary string + @return CRYPT_OK if successful + */ +int pkcs_1_os2ip(void *n, unsigned char *in, unsigned long inlen) { + return mp_read_unsigned_bin(n, in, inlen); +} +#endif /* LTC_PKCS_1 */ + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_os2ip.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file pkcs_1_pss_decode.c + LTC_PKCS #1 PSS Signature Padding, Tom St Denis + */ + +#ifdef LTC_PKCS_1 + +/** + LTC_PKCS #1 v2.00 PSS decode + @param msghash The hash to verify + @param msghashlen The length of the hash (octets) + @param sig The signature data (encoded data) + @param siglen The length of the signature data (octets) + @param saltlen The length of the salt used (octets) + @param hash_idx The index of the hash desired + @param modulus_bitlen The bit length of the RSA modulus + @param res [out] The result of the comparison, 1==valid, 0==invalid + @return CRYPT_OK if successful (even if the comparison failed) + */ +int pkcs_1_pss_decode(const unsigned char *msghash, unsigned long msghashlen, + const unsigned char *sig, unsigned long siglen, + unsigned long saltlen, int hash_idx, + unsigned long modulus_bitlen, int *res) { + unsigned char *DB, *mask, *salt, *hash; + unsigned long x, y, hLen, modulus_len; + int err; + hash_state md; + + LTC_ARGCHK(msghash != NULL); + LTC_ARGCHK(res != NULL); + + /* default to invalid */ + *res = 0; + + /* ensure hash is valid */ + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + + hLen = hash_descriptor[hash_idx].hashsize; + modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); + + /* check sizes */ + if ((saltlen > modulus_len) || + (modulus_len < hLen + saltlen + 2) || (siglen != modulus_len)) { + return CRYPT_PK_INVALID_SIZE; + } + + /* allocate ram for DB/mask/salt/hash of size modulus_len */ + DB = XMALLOC(modulus_len); + mask = XMALLOC(modulus_len); + salt = XMALLOC(modulus_len); + hash = XMALLOC(modulus_len); + if ((DB == NULL) || (mask == NULL) || (salt == NULL) || (hash == NULL)) { + if (DB != NULL) { + XFREE(DB); + } + if (mask != NULL) { + XFREE(mask); + } + if (salt != NULL) { + XFREE(salt); + } + if (hash != NULL) { + XFREE(hash); + } + return CRYPT_MEM; + } + + /* ensure the 0xBC byte */ + if (sig[siglen - 1] != 0xBC) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } + + /* copy out the DB */ + x = 0; + XMEMCPY(DB, sig + x, modulus_len - hLen - 1); + x += modulus_len - hLen - 1; + + /* copy out the hash */ + XMEMCPY(hash, sig + x, hLen); + x += hLen; + + /* check the MSB */ + if ((sig[0] & ~(0xFF >> ((modulus_len << 3) - (modulus_bitlen - 1)))) != 0) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } + + /* generate mask of length modulus_len - hLen - 1 from hash */ + if ((err = pkcs_1_mgf1(hash_idx, hash, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* xor against DB */ + for (y = 0; y < (modulus_len - hLen - 1); y++) { + DB[y] ^= mask[y]; + } + + /* now clear the first byte [make sure smaller than modulus] */ + DB[0] &= 0xFF >> ((modulus_len << 3) - (modulus_bitlen - 1)); + + /* DB = PS || 0x01 || salt, PS == modulus_len - saltlen - hLen - 2 zero bytes */ + + /* check for zeroes and 0x01 */ + for (x = 0; x < modulus_len - saltlen - hLen - 2; x++) { + if (DB[x] != 0x00) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } + } + + /* check for the 0x01 */ + if (DB[x++] != 0x01) { + err = CRYPT_INVALID_PACKET; + goto LBL_ERR; + } + + /* M = (eight) 0x00 || msghash || salt, mask = H(M) */ + if ((err = hash_descriptor[hash_idx].init(&md)) != CRYPT_OK) { + goto LBL_ERR; + } + zeromem(mask, 8); + if ((err = hash_descriptor[hash_idx].process(&md, mask, 8)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].process(&md, msghash, msghashlen)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].process(&md, DB + x, saltlen)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].done(&md, mask)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* mask == hash means valid signature */ + if (XMEMCMP(mask, hash, hLen) == 0) { + *res = 1; + } + + err = CRYPT_OK; +LBL_ERR: + #ifdef LTC_CLEAN_STACK + zeromem(DB, modulus_len); + zeromem(mask, modulus_len); + zeromem(salt, modulus_len); + zeromem(hash, modulus_len); + #endif + + XFREE(hash); + XFREE(salt); + XFREE(mask); + XFREE(DB); + + return err; +} +#endif /* LTC_PKCS_1 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_decode.c,v $ */ +/* $Revision: 1.11 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file pkcs_1_pss_encode.c + LTC_PKCS #1 PSS Signature Padding, Tom St Denis + */ + +#ifdef LTC_PKCS_1 + +/** + LTC_PKCS #1 v2.00 Signature Encoding + @param msghash The hash to encode + @param msghashlen The length of the hash (octets) + @param saltlen The length of the salt desired (octets) + @param prng An active PRNG context + @param prng_idx The index of the PRNG desired + @param hash_idx The index of the hash desired + @param modulus_bitlen The bit length of the RSA modulus + @param out [out] The destination of the encoding + @param outlen [in/out] The max size and resulting size of the encoded data + @return CRYPT_OK if successful + */ +int pkcs_1_pss_encode(const unsigned char *msghash, unsigned long msghashlen, + unsigned long saltlen, prng_state *prng, + int prng_idx, int hash_idx, + unsigned long modulus_bitlen, + unsigned char *out, unsigned long *outlen) { + unsigned char *DB, *mask, *salt, *hash; + unsigned long x, y, hLen, modulus_len; + int err; + hash_state md; + + LTC_ARGCHK(msghash != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* ensure hash and PRNG are valid */ + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) { + return err; + } + + hLen = hash_descriptor[hash_idx].hashsize; + modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); + + /* check sizes */ + if ((saltlen > modulus_len) || (modulus_len < hLen + saltlen + 2)) { + return CRYPT_PK_INVALID_SIZE; + } + + /* allocate ram for DB/mask/salt/hash of size modulus_len */ + DB = XMALLOC(modulus_len); + mask = XMALLOC(modulus_len); + salt = XMALLOC(modulus_len); + hash = XMALLOC(modulus_len); + if ((DB == NULL) || (mask == NULL) || (salt == NULL) || (hash == NULL)) { + if (DB != NULL) { + XFREE(DB); + } + if (mask != NULL) { + XFREE(mask); + } + if (salt != NULL) { + XFREE(salt); + } + if (hash != NULL) { + XFREE(hash); + } + return CRYPT_MEM; + } + + + /* generate random salt */ + if (saltlen > 0) { + if (prng_descriptor[prng_idx].read(salt, saltlen, prng) != saltlen) { + err = CRYPT_ERROR_READPRNG; + goto LBL_ERR; + } + } + + /* M = (eight) 0x00 || msghash || salt, hash = H(M) */ + if ((err = hash_descriptor[hash_idx].init(&md)) != CRYPT_OK) { + goto LBL_ERR; + } + zeromem(DB, 8); + if ((err = hash_descriptor[hash_idx].process(&md, DB, 8)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].process(&md, msghash, msghashlen)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].process(&md, salt, saltlen)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].done(&md, hash)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* generate DB = PS || 0x01 || salt, PS == modulus_len - saltlen - hLen - 2 zero bytes */ + x = 0; + XMEMSET(DB + x, 0, modulus_len - saltlen - hLen - 2); + x += modulus_len - saltlen - hLen - 2; + DB[x++] = 0x01; + XMEMCPY(DB + x, salt, saltlen); + x += saltlen; + + /* generate mask of length modulus_len - hLen - 1 from hash */ + if ((err = pkcs_1_mgf1(hash_idx, hash, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* xor against DB */ + for (y = 0; y < (modulus_len - hLen - 1); y++) { + DB[y] ^= mask[y]; + } + + /* output is DB || hash || 0xBC */ + if (*outlen < modulus_len) { + *outlen = modulus_len; + err = CRYPT_BUFFER_OVERFLOW; + goto LBL_ERR; + } + + /* DB len = modulus_len - hLen - 1 */ + y = 0; + XMEMCPY(out + y, DB, modulus_len - hLen - 1); + y += modulus_len - hLen - 1; + + /* hash */ + XMEMCPY(out + y, hash, hLen); + y += hLen; + + /* 0xBC */ + out[y] = 0xBC; + + /* now clear the 8*modulus_len - modulus_bitlen most significant bits */ + out[0] &= 0xFF >> ((modulus_len << 3) - (modulus_bitlen - 1)); + + /* store output size */ + *outlen = modulus_len; + err = CRYPT_OK; +LBL_ERR: + #ifdef LTC_CLEAN_STACK + zeromem(DB, modulus_len); + zeromem(mask, modulus_len); + zeromem(salt, modulus_len); + zeromem(hash, modulus_len); + #endif + + XFREE(hash); + XFREE(salt); + XFREE(mask); + XFREE(DB); + + return err; +} +#endif /* LTC_PKCS_1 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_encode.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** @file pkcs_1_v1_5_decode.c + * + * LTC_PKCS #1 v1.5 Padding. (Andreas Lange) + */ + +#ifdef LTC_PKCS_1 + +/** @brief LTC_PKCS #1 v1.5 decode. + * + * @param msg The encoded data to decode + * @param msglen The length of the encoded data (octets) + * @param block_type Block type to use in padding (\sa ltc_pkcs_1_v1_5_blocks) + * @param modulus_bitlen The bit length of the RSA modulus + * @param out [out] Destination of decoding + * @param outlen [in/out] The max size and resulting size of the decoding + * @param is_valid [out] Boolean whether the padding was valid + * + * @return CRYPT_OK if successful (even if invalid) + */ +int pkcs_1_v1_5_decode(const unsigned char *msg, + unsigned long msglen, + int block_type, + unsigned long modulus_bitlen, + unsigned char *out, + unsigned long *outlen, + int *is_valid) { + unsigned long modulus_len, ps_len, i; + int result; + + /* default to invalid packet */ + *is_valid = 0; + + modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); + + /* test message size */ + + if ((msglen > modulus_len) || (modulus_len < 11)) { + return CRYPT_PK_INVALID_SIZE; + } + + /* separate encoded message */ + + if ((msg[0] != 0x00) || (msg[1] != (unsigned char)block_type)) { + result = CRYPT_INVALID_PACKET; + goto bail; + } + + if (block_type == LTC_LTC_PKCS_1_EME) { + for (i = 2; i < modulus_len; i++) { + /* separator */ + if (msg[i] == 0x00) { + break; + } + } + ps_len = i++ - 2; + + if ((i >= modulus_len) || (ps_len < 8)) { + /* There was no octet with hexadecimal value 0x00 to separate ps from m, + * or the length of ps is less than 8 octets. + */ + result = CRYPT_INVALID_PACKET; + goto bail; + } + } else { + for (i = 2; i < modulus_len - 1; i++) { + if (msg[i] != 0xFF) { + break; + } + } + + /* separator check */ + if (msg[i] != 0) { + /* There was no octet with hexadecimal value 0x00 to separate ps from m. */ + result = CRYPT_INVALID_PACKET; + goto bail; + } + + ps_len = i - 2; + } + + if (*outlen < (msglen - (2 + ps_len + 1))) { + *outlen = msglen - (2 + ps_len + 1); + result = CRYPT_BUFFER_OVERFLOW; + goto bail; + } + + *outlen = (msglen - (2 + ps_len + 1)); + XMEMCPY(out, &msg[2 + ps_len + 1], *outlen); + + /* valid packet */ + *is_valid = 1; + result = CRYPT_OK; +bail: + return result; +} /* pkcs_1_v1_5_decode */ +#endif /* #ifdef LTC_PKCS_1 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_decode.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/*! \file pkcs_1_v1_5_encode.c + * + * LTC_PKCS #1 v1.5 Padding (Andreas Lange) + */ + +#ifdef LTC_PKCS_1 + +/*! \brief LTC_PKCS #1 v1.5 encode. + * + * \param msg The data to encode + * \param msglen The length of the data to encode (octets) + * \param block_type Block type to use in padding (\sa ltc_pkcs_1_v1_5_blocks) + * \param modulus_bitlen The bit length of the RSA modulus + * \param prng An active PRNG state (only for LTC_LTC_PKCS_1_EME) + * \param prng_idx The index of the PRNG desired (only for LTC_LTC_PKCS_1_EME) + * \param out [out] The destination for the encoded data + * \param outlen [in/out] The max size and resulting size of the encoded data + * + * \return CRYPT_OK if successful + */ +int pkcs_1_v1_5_encode(const unsigned char *msg, + unsigned long msglen, + int block_type, + unsigned long modulus_bitlen, + prng_state *prng, + int prng_idx, + unsigned char *out, + unsigned long *outlen) { + unsigned long modulus_len, ps_len, i; + unsigned char *ps; + int result; + + /* valid block_type? */ + if ((block_type != LTC_LTC_PKCS_1_EMSA) && + (block_type != LTC_LTC_PKCS_1_EME)) { + return CRYPT_PK_INVALID_PADDING; + } + + if (block_type == LTC_LTC_PKCS_1_EME) { /* encryption padding, we need a valid PRNG */ + if ((result = prng_is_valid(prng_idx)) != CRYPT_OK) { + return result; + } + } + + modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); + + /* test message size */ + if ((msglen + 11) > modulus_len) { + return CRYPT_PK_INVALID_SIZE; + } + + if (*outlen < modulus_len) { + *outlen = modulus_len; + result = CRYPT_BUFFER_OVERFLOW; + goto bail; + } + + /* generate an octets string PS */ + ps = &out[2]; + ps_len = modulus_len - msglen - 3; + + if (block_type == LTC_LTC_PKCS_1_EME) { + /* now choose a random ps */ + if (prng_descriptor[prng_idx].read(ps, ps_len, prng) != ps_len) { + result = CRYPT_ERROR_READPRNG; + goto bail; + } + + /* transform zero bytes (if any) to non-zero random bytes */ + for (i = 0; i < ps_len; i++) { + while (ps[i] == 0) { + if (prng_descriptor[prng_idx].read(&ps[i], 1, prng) != 1) { + result = CRYPT_ERROR_READPRNG; + goto bail; + } + } + } + } else { + XMEMSET(ps, 0xFF, ps_len); + } + + /* create string of length modulus_len */ + out[0] = 0x00; + out[1] = (unsigned char)block_type;/* block_type 1 or 2 */ + out[2 + ps_len] = 0x00; + XMEMCPY(&out[2 + ps_len + 1], msg, msglen); + *outlen = modulus_len; + + result = CRYPT_OK; +bail: + return result; +} /* pkcs_1_v1_5_encode */ +#endif /* #ifdef LTC_PKCS_1 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_encode.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rand_prime.c + Generate a random prime, Tom St Denis + */ + +#define USE_BBS 1 + +int rand_prime(void *N, long len, prng_state *prng, int wprng) { + int err, res, type; + unsigned char *buf; + + LTC_ARGCHK(N != NULL); + + /* get type */ + if (len < 0) { + type = USE_BBS; + len = -len; + } else { + type = 0; + } + + /* allow sizes between 2 and 512 bytes for a prime size */ + if ((len < 2) || (len > 512)) { + return CRYPT_INVALID_PRIME_SIZE; + } + + /* valid PRNG? Better be! */ + if ((err = prng_is_valid(wprng)) != CRYPT_OK) { + return err; + } + + /* allocate buffer to work with */ + buf = XCALLOC(1, len); + if (buf == NULL) { + return CRYPT_MEM; + } + + do { + /* generate value */ + if (prng_descriptor[wprng].read(buf, len, prng) != (unsigned long)len) { + XFREE(buf); + return CRYPT_ERROR_READPRNG; + } + + /* munge bits */ + buf[0] |= 0x80 | 0x40; + buf[len - 1] |= 0x01 | ((type & USE_BBS) ? 0x02 : 0x00); + + /* load value */ + if ((err = mp_read_unsigned_bin(N, buf, len)) != CRYPT_OK) { + XFREE(buf); + return err; + } + + /* test */ + if ((err = mp_prime_is_prime(N, 8, &res)) != CRYPT_OK) { + XFREE(buf); + return err; + } + } while (res == LTC_MP_NO); + +#ifdef LTC_CLEAN_STACK + zeromem(buf, len); +#endif + + XFREE(buf); + return CRYPT_OK; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/math/rand_prime.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:23 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rng_get_bytes.c + portable way to get secure random bits to feed a PRNG (Tom St Denis) + */ + +#ifdef LTC_DEVRANDOM +/* on *NIX read /dev/random */ +static unsigned long rng_nix(unsigned char *buf, unsigned long len, + void (*callback)(void)) { + #ifdef LTC_NO_FILE + return 0; + #else + FILE *f; + unsigned long x; + #ifdef TRY_URANDOM_FIRST + f = fopen("/dev/urandom", "rb"); + if (f == NULL) + #endif /* TRY_URANDOM_FIRST */ + f = fopen("/dev/random", "rb"); + + if (f == NULL) { + return 0; + } + + /* disable buffering */ + if (setvbuf(f, NULL, _IONBF, 0) != 0) { + fclose(f); + return 0; + } + + x = (unsigned long)fread(buf, 1, (size_t)len, f); + fclose(f); + return x; + #endif /* LTC_NO_FILE */ +} +#endif /* LTC_DEVRANDOM */ + +/* on ANSI C platforms with 100 < CLOCKS_PER_SEC < 10000 */ +#if defined(CLOCKS_PER_SEC) && !defined(WINCE) + + #define ANSI_RNG + +static unsigned long rng_ansic(unsigned char *buf, unsigned long len, + void (*callback)(void)) { + clock_t t1; + int l, acc, bits, a, b; + + if ((XCLOCKS_PER_SEC < 100) || (XCLOCKS_PER_SEC > 10000)) { + return 0; + } + + l = len; + bits = 8; + acc = a = b = 0; + while (len--) { + if (callback != NULL) callback(); + while (bits--) { + do { + t1 = XCLOCK(); + while (t1 == XCLOCK()) a ^= 1; + t1 = XCLOCK(); + while (t1 == XCLOCK()) b ^= 1; + } while (a == b); + acc = (acc << 1) | a; + } + *buf++ = acc; + acc = 0; + bits = 8; + } + acc = bits = a = b = 0; + return l; +} +#endif + +/* Try the Microsoft CSP */ +#if defined(WIN32) || defined(WINCE) + #undef _WIN32_WINNT // TODO: [ECHTTP] Why is this being redefined? + #define _WIN32_WINNT 0x0400 + #ifdef WINCE + #define UNDER_CE + #define ARM + #endif +#include +#include + +static unsigned long rng_win32(unsigned char *buf, unsigned long len, + void (*callback)(void)) { + HCRYPTPROV hProv = 0; + + if (!CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, + (CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET)) && + !CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET)) + return 0; + + if (CryptGenRandom(hProv, len, buf) == TRUE) { + CryptReleaseContext(hProv, 0); + return len; + } else { + CryptReleaseContext(hProv, 0); + return 0; + } +} +#endif /* WIN32 */ + +/** + Read the system RNG + @param out Destination + @param outlen Length desired (octets) + @param callback Pointer to void function to act as "callback" when RNG is slow. This can be NULL + @return Number of octets read + */ +unsigned long rng_get_bytes(unsigned char *out, unsigned long outlen, + void (*callback)(void)) { + unsigned long x; + + LTC_ARGCHK(out != NULL); + +#if defined(LTC_DEVRANDOM) + x = rng_nix(out, outlen, callback); + if (x != 0) { + return x; + } +#endif +#ifdef WIN32 + x = rng_win32(out, outlen, callback); + if (x != 0) { + return x; + } +#endif +#ifdef ANSI_RNG + x = rng_ansic(out, outlen, callback); + if (x != 0) { + return x; + } +#endif + return 0; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/prngs/rng_get_bytes.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rng_make_prng.c + portable way to get secure random bits to feed a PRNG (Tom St Denis) + */ + +/** + Create a PRNG from a RNG + @param bits Number of bits of entropy desired (64 ... 1024) + @param wprng Index of which PRNG to setup + @param prng [out] PRNG state to initialize + @param callback A pointer to a void function for when the RNG is slow, this can be NULL + @return CRYPT_OK if successful + */ +int rng_make_prng(int bits, int wprng, prng_state *prng, + void (*callback)(void)) { + unsigned char buf[256]; + int err; + + LTC_ARGCHK(prng != NULL); + + /* check parameter */ + if ((err = prng_is_valid(wprng)) != CRYPT_OK) { + return err; + } + + if ((bits < 64) || (bits > 1024)) { + return CRYPT_INVALID_PRNGSIZE; + } + + if ((err = prng_descriptor[wprng].start(prng)) != CRYPT_OK) { + return err; + } + + bits = ((bits / 8) + ((bits & 7) != 0 ? 1 : 0)) * 2; + if (rng_get_bytes(buf, (unsigned long)bits, callback) != (unsigned long)bits) { + return CRYPT_ERROR_READPRNG; + } + + if ((err = prng_descriptor[wprng].add_entropy(buf, (unsigned long)bits, prng)) != CRYPT_OK) { + return err; + } + + if ((err = prng_descriptor[wprng].ready(prng)) != CRYPT_OK) { + return err; + } + +#ifdef LTC_CLEAN_STACK + zeromem(buf, sizeof(buf)); +#endif + return CRYPT_OK; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/prngs/rng_make_prng.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rsa_decrypt_key.c + RSA LTC_PKCS #1 Decryption, Tom St Denis and Andreas Lange + */ + +#ifdef LTC_MRSA + +/** + LTC_PKCS #1 decrypt then v1.5 or OAEP depad + @param in The ciphertext + @param inlen The length of the ciphertext (octets) + @param out [out] The plaintext + @param outlen [in/out] The max size and resulting size of the plaintext (octets) + @param lparam The system "lparam" value + @param lparamlen The length of the lparam value (octets) + @param hash_idx The index of the hash desired + @param padding Type of padding (LTC_LTC_PKCS_1_OAEP or LTC_LTC_PKCS_1_V1_5) + @param stat [out] Result of the decryption, 1==valid, 0==invalid + @param key The corresponding private RSA key + @return CRYPT_OK if succcessul (even if invalid) + */ +int rsa_decrypt_key_ex(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + const unsigned char *lparam, unsigned long lparamlen, + int hash_idx, int padding, + int *stat, rsa_key *key) { + unsigned long modulus_bitlen, modulus_bytelen, x; + int err; + unsigned char *tmp; + + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(stat != NULL); + + /* default to invalid */ + *stat = 0; + + /* valid padding? */ + + if ((padding != LTC_LTC_PKCS_1_V1_5) && + (padding != LTC_LTC_PKCS_1_OAEP)) { + return CRYPT_PK_INVALID_PADDING; + } + + if (padding == LTC_LTC_PKCS_1_OAEP) { + /* valid hash ? */ + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + } + + /* get modulus len in bits */ + modulus_bitlen = mp_count_bits((key->N)); + + /* outlen must be at least the size of the modulus */ + modulus_bytelen = mp_unsigned_bin_size((key->N)); + if (modulus_bytelen != inlen) { + return CRYPT_INVALID_PACKET; + } + + /* allocate ram */ + tmp = XMALLOC(inlen); + if (tmp == NULL) { + return CRYPT_MEM; + } + + /* rsa decode the packet */ + x = inlen; + if ((err = ltc_mp.rsa_me(in, inlen, tmp, &x, PK_PRIVATE, key)) != CRYPT_OK) { + XFREE(tmp); + return err; + } + + if (padding == LTC_LTC_PKCS_1_OAEP) { + /* now OAEP decode the packet */ + err = pkcs_1_oaep_decode(tmp, x, lparam, lparamlen, modulus_bitlen, hash_idx, + out, outlen, stat); + } else { + /* now LTC_PKCS #1 v1.5 depad the packet */ + err = pkcs_1_v1_5_decode(tmp, x, LTC_LTC_PKCS_1_EME, modulus_bitlen, out, outlen, stat); + } + + XFREE(tmp); + return err; +} +#endif /* LTC_MRSA */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_decrypt_key.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rsa_encrypt_key.c + RSA LTC_PKCS #1 encryption, Tom St Denis and Andreas Lange + */ + +#ifdef LTC_MRSA + +/** + (LTC_PKCS #1 v2.0) OAEP pad then encrypt + @param in The plaintext + @param inlen The length of the plaintext (octets) + @param out [out] The ciphertext + @param outlen [in/out] The max size and resulting size of the ciphertext + @param lparam The system "lparam" for the encryption + @param lparamlen The length of lparam (octets) + @param prng An active PRNG + @param prng_idx The index of the desired prng + @param hash_idx The index of the desired hash + @param padding Type of padding (LTC_LTC_PKCS_1_OAEP or LTC_LTC_PKCS_1_V1_5) + @param key The RSA key to encrypt to + @return CRYPT_OK if successful + */ +int rsa_encrypt_key_ex(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + const unsigned char *lparam, unsigned long lparamlen, + prng_state *prng, int prng_idx, int hash_idx, int padding, rsa_key *key) { + unsigned long modulus_bitlen, modulus_bytelen, x; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* valid padding? */ + if ((padding != LTC_LTC_PKCS_1_V1_5) && + (padding != LTC_LTC_PKCS_1_OAEP)) { + return CRYPT_PK_INVALID_PADDING; + } + + /* valid prng? */ + if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) { + return err; + } + + if (padding == LTC_LTC_PKCS_1_OAEP) { + /* valid hash? */ + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + } + + /* get modulus len in bits */ + modulus_bitlen = mp_count_bits((key->N)); + + /* outlen must be at least the size of the modulus */ + modulus_bytelen = mp_unsigned_bin_size((key->N)); + if (modulus_bytelen > *outlen) { + *outlen = modulus_bytelen; + return CRYPT_BUFFER_OVERFLOW; + } + + if (padding == LTC_LTC_PKCS_1_OAEP) { + /* OAEP pad the key */ + x = *outlen; + if ((err = pkcs_1_oaep_encode(in, inlen, lparam, + lparamlen, modulus_bitlen, prng, prng_idx, hash_idx, + out, &x)) != CRYPT_OK) { + return err; + } + } else { + /* LTC_PKCS #1 v1.5 pad the key */ + x = *outlen; + if ((err = pkcs_1_v1_5_encode(in, inlen, LTC_LTC_PKCS_1_EME, + modulus_bitlen, prng, prng_idx, + out, &x)) != CRYPT_OK) { + return err; + } + } + + /* rsa exptmod the OAEP or LTC_PKCS #1 v1.5 pad */ + return ltc_mp.rsa_me(out, x, out, outlen, PK_PUBLIC, key); +} +#endif /* LTC_MRSA */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_encrypt_key.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rsa_exptmod.c + RSA LTC_PKCS exptmod, Tom St Denis + */ + +#ifdef LTC_MRSA + +/** + Compute an RSA modular exponentiation + @param in The input data to send into RSA + @param inlen The length of the input (octets) + @param out [out] The destination + @param outlen [in/out] The max size and resulting size of the output + @param which Which exponent to use, e.g. PK_PRIVATE or PK_PUBLIC + @param key The RSA key to use + @return CRYPT_OK if successful + */ +int rsa_exptmod(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, int which, + rsa_key *key) { + void *tmp, *tmpa, *tmpb; + unsigned long x; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* is the key of the right type for the operation? */ + if ((which == PK_PRIVATE) && (key->type != PK_PRIVATE)) { + return CRYPT_PK_NOT_PRIVATE; + } + + /* must be a private or public operation */ + if ((which != PK_PRIVATE) && (which != PK_PUBLIC)) { + return CRYPT_PK_INVALID_TYPE; + } + + /* init and copy into tmp */ + if ((err = mp_init_multi(&tmp, &tmpa, &tmpb, NULL)) != CRYPT_OK) { + return err; + } + if ((err = mp_read_unsigned_bin(tmp, (unsigned char *)in, (int)inlen)) != CRYPT_OK) { + goto error; + } + + /* sanity check on the input */ + if (mp_cmp(key->N, tmp) == LTC_MP_LT) { + err = CRYPT_PK_INVALID_SIZE; + goto error; + } + + /* are we using the private exponent and is the key optimized? */ + if (which == PK_PRIVATE) { + /* tmpa = tmp^dP mod p */ + if ((err = mp_exptmod(tmp, key->dP, key->p, tmpa)) != CRYPT_OK) { + goto error; + } + + /* tmpb = tmp^dQ mod q */ + if ((err = mp_exptmod(tmp, key->dQ, key->q, tmpb)) != CRYPT_OK) { + goto error; + } + + /* tmp = (tmpa - tmpb) * qInv (mod p) */ + if ((err = mp_sub(tmpa, tmpb, tmp)) != CRYPT_OK) { + goto error; + } + if ((err = mp_mulmod(tmp, key->qP, key->p, tmp)) != CRYPT_OK) { + goto error; + } + + /* tmp = tmpb + q * tmp */ + if ((err = mp_mul(tmp, key->q, tmp)) != CRYPT_OK) { + goto error; + } + if ((err = mp_add(tmp, tmpb, tmp)) != CRYPT_OK) { + goto error; + } + } else { + /* exptmod it */ + if ((err = mp_exptmod(tmp, key->e, key->N, tmp)) != CRYPT_OK) { + goto error; + } + } + + /* read it back */ + x = (unsigned long)mp_unsigned_bin_size(key->N); + if (x > *outlen) { + *outlen = x; + err = CRYPT_BUFFER_OVERFLOW; + goto error; + } + + /* this should never happen ... */ + if (mp_unsigned_bin_size(tmp) > mp_unsigned_bin_size(key->N)) { + err = CRYPT_ERROR; + goto error; + } + *outlen = x; + + /* convert it */ + zeromem(out, x); + if ((err = mp_to_unsigned_bin(tmp, out + (x - mp_unsigned_bin_size(tmp)))) != CRYPT_OK) { + goto error; + } + + /* clean up and return */ + err = CRYPT_OK; +error: + mp_clear_multi(tmp, tmpa, tmpb, NULL); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_exptmod.c,v $ */ +/* $Revision: 1.18 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rsa_free.c + Free an RSA key, Tom St Denis + */ + +#ifdef LTC_MRSA + +/** + Free an RSA key from memory + @param key The RSA key to free + */ +void rsa_free(rsa_key *key) { + LTC_ARGCHKVD(key != NULL); + mp_clear_multi(key->e, key->d, key->N, key->dQ, key->dP, key->qP, key->p, key->q, NULL); +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_free.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rsa_import.c + Import a LTC_PKCS RSA key, Tom St Denis + */ + +#ifdef LTC_MRSA + +/** + Import an RSAPublicKey or RSAPrivateKey [two-prime only, only support >= 1024-bit keys, defined in LTC_PKCS #1 v2.1] + @param in The packet to import from + @param inlen It's length (octets) + @param key [out] Destination for newly imported key + @return CRYPT_OK if successful, upon error allocated memory is freed + */ +int rsa_import(const unsigned char *in, unsigned long inlen, rsa_key *key) { + int err; + void *zero; + unsigned char *tmpbuf; + unsigned long t, x, y, z, tmpoid[16]; + ltc_asn1_list ssl_pubkey_hashoid[2]; + ltc_asn1_list ssl_pubkey[2]; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(ltc_mp.name != NULL); + + /* init key */ + if ((err = mp_init_multi(&key->e, &key->d, &key->N, &key->dQ, + &key->dP, &key->qP, &key->p, &key->q, NULL)) != CRYPT_OK) { + return err; + } + + /* see if the OpenSSL DER format RSA public key will work */ + tmpbuf = XCALLOC(1, MAX_RSA_SIZE * 8); + if (tmpbuf == NULL) { + err = CRYPT_MEM; + goto LBL_ERR; + } + + /* this includes the internal hash ID and optional params (NULL in this case) */ + LTC_SET_ASN1(ssl_pubkey_hashoid, 0, LTC_ASN1_OBJECT_IDENTIFIER, tmpoid, sizeof(tmpoid) / sizeof(tmpoid[0])); + LTC_SET_ASN1(ssl_pubkey_hashoid, 1, LTC_ASN1_NULL, NULL, 0); + + /* the actual format of the SSL DER key is odd, it stores a RSAPublicKey in a **BIT** string ... so we have to extract it + then proceed to convert bit to octet + */ + LTC_SET_ASN1(ssl_pubkey, 0, LTC_ASN1_SEQUENCE, &ssl_pubkey_hashoid, 2); + LTC_SET_ASN1(ssl_pubkey, 1, LTC_ASN1_BIT_STRING, tmpbuf, MAX_RSA_SIZE * 8); + + if (der_decode_sequence(in, inlen, + ssl_pubkey, 2UL) == CRYPT_OK) { + /* ok now we have to reassemble the BIT STRING to an OCTET STRING. Thanks OpenSSL... */ + for (t = y = z = x = 0; x < ssl_pubkey[1].size; x++) { + y = (y << 1) | tmpbuf[x]; + if (++z == 8) { + tmpbuf[t++] = (unsigned char)y; + y = 0; + z = 0; + } + } + + /* now it should be SEQUENCE { INTEGER, INTEGER } */ + if ((err = der_decode_sequence_multi(tmpbuf, t, + LTC_ASN1_INTEGER, 1UL, key->N, + LTC_ASN1_INTEGER, 1UL, key->e, + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { + XFREE(tmpbuf); + goto LBL_ERR; + } + XFREE(tmpbuf); + key->type = PK_PUBLIC; + return CRYPT_OK; + } + XFREE(tmpbuf); + + /* not SSL public key, try to match against LTC_PKCS #1 standards */ + if ((err = der_decode_sequence_multi(in, inlen, + LTC_ASN1_INTEGER, 1UL, key->N, + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { + goto LBL_ERR; + } + + if (mp_cmp_d(key->N, 0) == LTC_MP_EQ) { + if ((err = mp_init(&zero)) != CRYPT_OK) { + goto LBL_ERR; + } + /* it's a private key */ + if ((err = der_decode_sequence_multi(in, inlen, + LTC_ASN1_INTEGER, 1UL, zero, + LTC_ASN1_INTEGER, 1UL, key->N, + LTC_ASN1_INTEGER, 1UL, key->e, + LTC_ASN1_INTEGER, 1UL, key->d, + LTC_ASN1_INTEGER, 1UL, key->p, + LTC_ASN1_INTEGER, 1UL, key->q, + LTC_ASN1_INTEGER, 1UL, key->dP, + LTC_ASN1_INTEGER, 1UL, key->dQ, + LTC_ASN1_INTEGER, 1UL, key->qP, + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { + mp_clear(zero); + goto LBL_ERR; + } + mp_clear(zero); + key->type = PK_PRIVATE; + } else if (mp_cmp_d(key->N, 1) == LTC_MP_EQ) { + /* we don't support multi-prime RSA */ + err = CRYPT_PK_INVALID_TYPE; + goto LBL_ERR; + } else { + /* it's a public key and we lack e */ + if ((err = der_decode_sequence_multi(in, inlen, + LTC_ASN1_INTEGER, 1UL, key->N, + LTC_ASN1_INTEGER, 1UL, key->e, + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { + goto LBL_ERR; + } + key->type = PK_PUBLIC; + } + return CRYPT_OK; +LBL_ERR: + mp_clear_multi(key->d, key->e, key->N, key->dQ, key->dP, key->qP, key->p, key->q, NULL); + return err; +} +#endif /* LTC_MRSA */ + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_import.c,v $ */ +/* $Revision: 1.23 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rsa_make_key.c + RSA key generation, Tom St Denis + */ + +#ifdef LTC_MRSA + +/** + Create an RSA key + @param prng An active PRNG state + @param wprng The index of the PRNG desired + @param size The size of the modulus (key size) desired (octets) + @param e The "e" value (public key). e==65537 is a good choice + @param key [out] Destination of a newly created private key pair + @return CRYPT_OK if successful, upon error all allocated ram is freed + */ +int rsa_make_key(prng_state *prng, int wprng, int size, long e, rsa_key *key) { + void *p, *q, *tmp1, *tmp2, *tmp3; + int err; + + LTC_ARGCHK(ltc_mp.name != NULL); + LTC_ARGCHK(key != NULL); + + if ((size < (MIN_RSA_SIZE / 8)) || (size > (MAX_RSA_SIZE / 8))) { + return CRYPT_INVALID_KEYSIZE; + } + + if ((e < 3) || ((e & 1) == 0)) { + return CRYPT_INVALID_ARG; + } + + if ((err = prng_is_valid(wprng)) != CRYPT_OK) { + return err; + } + + if ((err = mp_init_multi(&p, &q, &tmp1, &tmp2, &tmp3, NULL)) != CRYPT_OK) { + return err; + } + + /* make primes p and q (optimization provided by Wayne Scott) */ + if ((err = mp_set_int(tmp3, e)) != CRYPT_OK) { + goto errkey; + } /* tmp3 = e */ + + /* make prime "p" */ + do { + if ((err = rand_prime(p, size / 2, prng, wprng)) != CRYPT_OK) { + goto errkey; + } + if ((err = mp_sub_d(p, 1, tmp1)) != CRYPT_OK) { + goto errkey; + } /* tmp1 = p-1 */ + if ((err = mp_gcd(tmp1, tmp3, tmp2)) != CRYPT_OK) { + goto errkey; + } /* tmp2 = gcd(p-1, e) */ + } while (mp_cmp_d(tmp2, 1) != 0); /* while e divides p-1 */ + + /* make prime "q" */ + do { + if ((err = rand_prime(q, size / 2, prng, wprng)) != CRYPT_OK) { + goto errkey; + } + if ((err = mp_sub_d(q, 1, tmp1)) != CRYPT_OK) { + goto errkey; + } /* tmp1 = q-1 */ + if ((err = mp_gcd(tmp1, tmp3, tmp2)) != CRYPT_OK) { + goto errkey; + } /* tmp2 = gcd(q-1, e) */ + } while (mp_cmp_d(tmp2, 1) != 0); /* while e divides q-1 */ + + /* tmp1 = lcm(p-1, q-1) */ + if ((err = mp_sub_d(p, 1, tmp2)) != CRYPT_OK) { + goto errkey; + } /* tmp2 = p-1 */ + /* tmp1 = q-1 (previous do/while loop) */ + if ((err = mp_lcm(tmp1, tmp2, tmp1)) != CRYPT_OK) { + goto errkey; + } /* tmp1 = lcm(p-1, q-1) */ + + /* make key */ + if ((err = mp_init_multi(&key->e, &key->d, &key->N, &key->dQ, &key->dP, &key->qP, &key->p, &key->q, NULL)) != CRYPT_OK) { + goto errkey; + } + + if ((err = mp_set_int(key->e, e)) != CRYPT_OK) { + goto errkey; + } /* key->e = e */ + if ((err = mp_invmod(key->e, tmp1, key->d)) != CRYPT_OK) { + goto errkey; + } /* key->d = 1/e mod lcm(p-1,q-1) */ + if ((err = mp_mul(p, q, key->N)) != CRYPT_OK) { + goto errkey; + } /* key->N = pq */ + + /* optimize for CRT now */ + /* find d mod q-1 and d mod p-1 */ + if ((err = mp_sub_d(p, 1, tmp1)) != CRYPT_OK) { + goto errkey; + } /* tmp1 = q-1 */ + if ((err = mp_sub_d(q, 1, tmp2)) != CRYPT_OK) { + goto errkey; + } /* tmp2 = p-1 */ + if ((err = mp_mod(key->d, tmp1, key->dP)) != CRYPT_OK) { + goto errkey; + } /* dP = d mod p-1 */ + if ((err = mp_mod(key->d, tmp2, key->dQ)) != CRYPT_OK) { + goto errkey; + } /* dQ = d mod q-1 */ + if ((err = mp_invmod(q, p, key->qP)) != CRYPT_OK) { + goto errkey; + } /* qP = 1/q mod p */ + + if ((err = mp_copy(p, key->p)) != CRYPT_OK) { + goto errkey; + } + if ((err = mp_copy(q, key->q)) != CRYPT_OK) { + goto errkey; + } + + /* set key type (in this case it's CRT optimized) */ + key->type = PK_PRIVATE; + + /* return ok and free temps */ + err = CRYPT_OK; + goto cleanup; +errkey: + mp_clear_multi(key->d, key->e, key->N, key->dQ, key->dP, key->qP, key->p, key->q, NULL); +cleanup: + mp_clear_multi(tmp3, tmp2, tmp1, p, q, NULL); + return err; +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_make_key.c,v $ */ +/* $Revision: 1.16 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rsa_sign_hash.c + RSA LTC_PKCS #1 v1.5 and v2 PSS sign hash, Tom St Denis and Andreas Lange + */ + +#ifdef LTC_MRSA + +/** + LTC_PKCS #1 pad then sign + @param in The hash to sign + @param inlen The length of the hash to sign (octets) + @param out [out] The signature + @param outlen [in/out] The max size and resulting size of the signature + @param padding Type of padding (LTC_LTC_PKCS_1_PSS or LTC_LTC_PKCS_1_V1_5) + @param prng An active PRNG state + @param prng_idx The index of the PRNG desired + @param hash_idx The index of the hash desired + @param saltlen The length of the salt desired (octets) + @param key The private RSA key to use + @return CRYPT_OK if successful + */ +int rsa_sign_hash_ex(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + int padding, + prng_state *prng, int prng_idx, + int hash_idx, unsigned long saltlen, + rsa_key *key) { + unsigned long modulus_bitlen, modulus_bytelen, x, y; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* valid padding? */ + if ((padding != LTC_LTC_PKCS_1_V1_5) && (padding != LTC_LTC_PKCS_1_PSS)) { + return CRYPT_PK_INVALID_PADDING; + } + + if (padding == LTC_LTC_PKCS_1_PSS) { + /* valid prng and hash ? */ + if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) { + return err; + } + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + } + + /* get modulus len in bits */ + modulus_bitlen = mp_count_bits((key->N)); + + /* outlen must be at least the size of the modulus */ + modulus_bytelen = mp_unsigned_bin_size((key->N)); + if (modulus_bytelen > *outlen) { + *outlen = modulus_bytelen; + return CRYPT_BUFFER_OVERFLOW; + } + + if (padding == LTC_LTC_PKCS_1_PSS) { + /* PSS pad the key */ + x = *outlen; + if ((err = pkcs_1_pss_encode(in, inlen, saltlen, prng, prng_idx, + hash_idx, modulus_bitlen, out, &x)) != CRYPT_OK) { + return err; + } + } else { + /* LTC_PKCS #1 v1.5 pad the hash */ + unsigned char *tmpin; + ltc_asn1_list digestinfo[2], siginfo[2]; + + /* not all hashes have OIDs... so sad */ + if (hash_descriptor[hash_idx].OIDlen == 0) { + return CRYPT_INVALID_ARG; + } + + /* construct the SEQUENCE + SEQUENCE { + SEQUENCE {hashoid OID + blah NULL + } + hash OCTET STRING + } + */ + LTC_SET_ASN1(digestinfo, 0, LTC_ASN1_OBJECT_IDENTIFIER, hash_descriptor[hash_idx].OID, hash_descriptor[hash_idx].OIDlen); + LTC_SET_ASN1(digestinfo, 1, LTC_ASN1_NULL, NULL, 0); + LTC_SET_ASN1(siginfo, 0, LTC_ASN1_SEQUENCE, digestinfo, 2); + LTC_SET_ASN1(siginfo, 1, LTC_ASN1_OCTET_STRING, in, inlen); + + /* allocate memory for the encoding */ + y = mp_unsigned_bin_size(key->N); + tmpin = XMALLOC(y); + if (tmpin == NULL) { + return CRYPT_MEM; + } + + if ((err = der_encode_sequence(siginfo, 2, tmpin, &y)) != CRYPT_OK) { + XFREE(tmpin); + return err; + } + + x = *outlen; + if ((err = pkcs_1_v1_5_encode(tmpin, y, LTC_LTC_PKCS_1_EMSA, + modulus_bitlen, NULL, 0, + out, &x)) != CRYPT_OK) { + XFREE(tmpin); + return err; + } + XFREE(tmpin); + } + + /* RSA encode it */ + return ltc_mp.rsa_me(out, x, out, outlen, PK_PRIVATE, key); +} +#endif /* LTC_MRSA */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_sign_hash.c,v $ */ +/* $Revision: 1.11 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file rsa_verify_hash.c + RSA LTC_PKCS #1 v1.5 or v2 PSS signature verification, Tom St Denis and Andreas Lange + */ + +#ifdef LTC_MRSA + +/** + LTC_PKCS #1 de-sign then v1.5 or PSS depad + @param sig The signature data + @param siglen The length of the signature data (octets) + @param hash The hash of the message that was signed + @param hashlen The length of the hash of the message that was signed (octets) + @param padding Type of padding (LTC_LTC_PKCS_1_PSS or LTC_LTC_PKCS_1_V1_5) + @param hash_idx The index of the desired hash + @param saltlen The length of the salt used during signature + @param stat [out] The result of the signature comparison, 1==valid, 0==invalid + @param key The public RSA key corresponding to the key that performed the signature + @return CRYPT_OK on success (even if the signature is invalid) + */ +int rsa_verify_hash_ex(const unsigned char *sig, unsigned long siglen, + const unsigned char *hash, unsigned long hashlen, + int padding, + int hash_idx, unsigned long saltlen, + int *stat, rsa_key *key) { + unsigned long modulus_bitlen, modulus_bytelen, x; + int err; + unsigned char *tmpbuf; + + LTC_ARGCHK(hash != NULL); + LTC_ARGCHK(sig != NULL); + LTC_ARGCHK(stat != NULL); + LTC_ARGCHK(key != NULL); + + /* default to invalid */ + *stat = 0; + + /* valid padding? */ + + if ((padding != LTC_LTC_PKCS_1_V1_5) && + (padding != LTC_LTC_PKCS_1_PSS)) { + return CRYPT_PK_INVALID_PADDING; + } + + if (padding == LTC_LTC_PKCS_1_PSS) { + /* valid hash ? */ + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + } + + /* get modulus len in bits */ + modulus_bitlen = mp_count_bits((key->N)); + + /* outlen must be at least the size of the modulus */ + modulus_bytelen = mp_unsigned_bin_size((key->N)); + if (modulus_bytelen != siglen) { + return CRYPT_INVALID_PACKET; + } + + /* allocate temp buffer for decoded sig */ + tmpbuf = XMALLOC(siglen); + if (tmpbuf == NULL) { + return CRYPT_MEM; + } + + /* RSA decode it */ + x = siglen; + if ((err = ltc_mp.rsa_me(sig, siglen, tmpbuf, &x, PK_PUBLIC, key)) != CRYPT_OK) { + XFREE(tmpbuf); + return err; + } + + /* make sure the output is the right size */ + if (x != siglen) { + XFREE(tmpbuf); + return CRYPT_INVALID_PACKET; + } + + if (padding == LTC_LTC_PKCS_1_PSS) { + /* PSS decode and verify it */ + err = pkcs_1_pss_decode(hash, hashlen, tmpbuf, x, saltlen, hash_idx, modulus_bitlen, stat); + } else { + /* LTC_PKCS #1 v1.5 decode it */ + unsigned char *out; + unsigned long outlen, loid[16]; + int decoded; + ltc_asn1_list digestinfo[2], siginfo[2]; + + /* not all hashes have OIDs... so sad */ + if (hash_descriptor[hash_idx].OIDlen == 0) { + err = CRYPT_INVALID_ARG; + goto bail_2; + } + + /* allocate temp buffer for decoded hash */ + outlen = ((modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0)) - 3; + out = XMALLOC(outlen); + if (out == NULL) { + err = CRYPT_MEM; + goto bail_2; + } + + if ((err = pkcs_1_v1_5_decode(tmpbuf, x, LTC_LTC_PKCS_1_EMSA, modulus_bitlen, out, &outlen, &decoded)) != CRYPT_OK) { + XFREE(out); + goto bail_2; + } + + /* now we must decode out[0...outlen-1] using ASN.1, test the OID and then test the hash */ + + /* construct the SEQUENCE + SEQUENCE { + SEQUENCE {hashoid OID + blah NULL + } + hash OCTET STRING + } + */ + LTC_SET_ASN1(digestinfo, 0, LTC_ASN1_OBJECT_IDENTIFIER, loid, sizeof(loid) / sizeof(loid[0])); + LTC_SET_ASN1(digestinfo, 1, LTC_ASN1_NULL, NULL, 0); + LTC_SET_ASN1(siginfo, 0, LTC_ASN1_SEQUENCE, digestinfo, 2); + LTC_SET_ASN1(siginfo, 1, LTC_ASN1_OCTET_STRING, tmpbuf, siglen); + + if ((err = der_decode_sequence(out, outlen, siginfo, 2)) != CRYPT_OK) { + XFREE(out); + goto bail_2; + } + + /* test OID */ + if ((digestinfo[0].size == hash_descriptor[hash_idx].OIDlen) && + (XMEMCMP(digestinfo[0].data, hash_descriptor[hash_idx].OID, sizeof(unsigned long) * hash_descriptor[hash_idx].OIDlen) == 0) && + (siginfo[1].size == hashlen) && + (XMEMCMP(siginfo[1].data, hash, hashlen) == 0)) { + *stat = 1; + } + + #ifdef LTC_CLEAN_STACK + zeromem(out, outlen); + #endif + XFREE(out); + } + +bail_2: + #ifdef LTC_CLEAN_STACK + zeromem(tmpbuf, siglen); + #endif + XFREE(tmpbuf); + return err; +} +#endif /* LTC_MRSA */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_verify_hash.c,v $ */ +/* $Revision: 1.13 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file sprng.c + Secure PRNG, Tom St Denis + */ + +/* A secure PRNG using the RNG functions. Basically this is a + * wrapper that allows you to use a secure RNG as a PRNG + * in the various other functions. + */ + +#ifdef LTC_SPRNG + +const struct ltc_prng_descriptor sprng_desc = +{ + "sprng", 0, + &sprng_start, + &sprng_add_entropy, + &sprng_ready, + &sprng_read, + &sprng_done, + &sprng_export, + &sprng_import, + &sprng_test +}; + +/** + Start the PRNG + @param prng [out] The PRNG state to initialize + @return CRYPT_OK if successful + */ +int sprng_start(prng_state *prng) { + return CRYPT_OK; +} + +/** + Add entropy to the PRNG state + @param in The data to add + @param inlen Length of the data to add + @param prng PRNG state to update + @return CRYPT_OK if successful + */ +int sprng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng) { + return CRYPT_OK; +} + +/** + Make the PRNG ready to read from + @param prng The PRNG to make active + @return CRYPT_OK if successful + */ +int sprng_ready(prng_state *prng) { + return CRYPT_OK; +} + +/** + Read from the PRNG + @param out Destination + @param outlen Length of output + @param prng The active PRNG to read from + @return Number of octets read + */ +unsigned long sprng_read(unsigned char *out, unsigned long outlen, prng_state *prng) { + LTC_ARGCHK(out != NULL); + return rng_get_bytes(out, outlen, NULL); +} + +/** + Terminate the PRNG + @param prng The PRNG to terminate + @return CRYPT_OK if successful + */ +int sprng_done(prng_state *prng) { + return CRYPT_OK; +} + +/** + Export the PRNG state + @param out [out] Destination + @param outlen [in/out] Max size and resulting size of the state + @param prng The PRNG to export + @return CRYPT_OK if successful + */ +int sprng_export(unsigned char *out, unsigned long *outlen, prng_state *prng) { + LTC_ARGCHK(outlen != NULL); + + *outlen = 0; + return CRYPT_OK; +} + +/** + Import a PRNG state + @param in The PRNG state + @param inlen Size of the state + @param prng The PRNG to import + @return CRYPT_OK if successful + */ +int sprng_import(const unsigned char *in, unsigned long inlen, prng_state *prng) { + return CRYPT_OK; +} + +/** + PRNG self-test + @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled + */ +int sprng_test(void) { + return CRYPT_OK; +} +#endif + + + +/* $Source: /cvs/libtom/libtomcrypt/src/prngs/sprng.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file zeromem.c + Zero a block of memory, Tom St Denis + */ + +/** + Zero a block of memory + @param out The destination of the area to zero + @param outlen The length of the area to zero (octets) + */ +void zeromem(void *out, size_t outlen) { + unsigned char *mem = out; + + LTC_ARGCHKVD(out != NULL); + while (outlen-- > 0) { + *mem++ = 0; + } +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/zeromem.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file sha1.c + LTC_SHA1 code by Tom St Denis + */ + + +#ifdef LTC_SHA1 + +const struct ltc_hash_descriptor sha1_desc = +{ + "sha1", + 2, + 20, + 64, + + /* OID */ + { 1, 3, 14, 3, 2, 26, }, + 6, + + &sha1_init, + &sha1_process, + &sha1_done, + &sha1_test, + NULL +}; + + #define F0(x, y, z) (z ^ (x & (y ^ z))) + #define F1(x, y, z) (x ^ y ^ z) + #define F2(x, y, z) ((x & y) | (z & (x | y))) + #define F3(x, y, z) (x ^ y ^ z) + + #ifdef LTC_CLEAN_STACK +static int _sha1_compress(hash_state *md, unsigned char *buf) + #else +static int sha1_compress(hash_state *md, unsigned char *buf) + #endif +{ + ulong32 a, b, c, d, e, W[80], i; + + #ifdef LTC_SMALL_CODE + ulong32 t; + #endif + + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD32H(W[i], buf + (4 * i)); + } + + /* copy state */ + a = md->sha1.state[0]; + b = md->sha1.state[1]; + c = md->sha1.state[2]; + d = md->sha1.state[3]; + e = md->sha1.state[4]; + + /* expand it */ + for (i = 16; i < 80; i++) { + W[i] = ROL(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1); + } + + /* compress */ + /* round one */ + #define FF0(a, b, c, d, e, i) e = (ROLc(a, 5) + F0(b, c, d) + e + W[i] + 0x5a827999UL); b = ROLc(b, 30); + #define FF1(a, b, c, d, e, i) e = (ROLc(a, 5) + F1(b, c, d) + e + W[i] + 0x6ed9eba1UL); b = ROLc(b, 30); + #define FF2(a, b, c, d, e, i) e = (ROLc(a, 5) + F2(b, c, d) + e + W[i] + 0x8f1bbcdcUL); b = ROLc(b, 30); + #define FF3(a, b, c, d, e, i) e = (ROLc(a, 5) + F3(b, c, d) + e + W[i] + 0xca62c1d6UL); b = ROLc(b, 30); + + #ifdef LTC_SMALL_CODE + for (i = 0; i < 20; ) { + FF0(a, b, c, d, e, i++); + t = e; + e = d; + d = c; + c = b; + b = a; + a = t; + } + + for ( ; i < 40; ) { + FF1(a, b, c, d, e, i++); + t = e; + e = d; + d = c; + c = b; + b = a; + a = t; + } + + for ( ; i < 60; ) { + FF2(a, b, c, d, e, i++); + t = e; + e = d; + d = c; + c = b; + b = a; + a = t; + } + + for ( ; i < 80; ) { + FF3(a, b, c, d, e, i++); + t = e; + e = d; + d = c; + c = b; + b = a; + a = t; + } + + #else + for (i = 0; i < 20; ) { + FF0(a, b, c, d, e, i++); + FF0(e, a, b, c, d, i++); + FF0(d, e, a, b, c, i++); + FF0(c, d, e, a, b, i++); + FF0(b, c, d, e, a, i++); + } + + /* round two */ + for ( ; i < 40; ) { + FF1(a, b, c, d, e, i++); + FF1(e, a, b, c, d, i++); + FF1(d, e, a, b, c, i++); + FF1(c, d, e, a, b, i++); + FF1(b, c, d, e, a, i++); + } + + /* round three */ + for ( ; i < 60; ) { + FF2(a, b, c, d, e, i++); + FF2(e, a, b, c, d, i++); + FF2(d, e, a, b, c, i++); + FF2(c, d, e, a, b, i++); + FF2(b, c, d, e, a, i++); + } + + /* round four */ + for ( ; i < 80; ) { + FF3(a, b, c, d, e, i++); + FF3(e, a, b, c, d, i++); + FF3(d, e, a, b, c, i++); + FF3(c, d, e, a, b, i++); + FF3(b, c, d, e, a, i++); + } + #endif + + #undef FF0 + #undef FF1 + #undef FF2 + #undef FF3 + + /* store */ + md->sha1.state[0] = md->sha1.state[0] + a; + md->sha1.state[1] = md->sha1.state[1] + b; + md->sha1.state[2] = md->sha1.state[2] + c; + md->sha1.state[3] = md->sha1.state[3] + d; + md->sha1.state[4] = md->sha1.state[4] + e; + + return CRYPT_OK; +} + + #ifdef LTC_CLEAN_STACK +static int sha1_compress(hash_state *md, unsigned char *buf) { + int err; + + err = _sha1_compress(md, buf); + burn_stack(sizeof(ulong32) * 87); + return err; +} + #endif + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful + */ +int sha1_init(hash_state *md) { + LTC_ARGCHK(md != NULL); + md->sha1.state[0] = 0x67452301UL; + md->sha1.state[1] = 0xefcdab89UL; + md->sha1.state[2] = 0x98badcfeUL; + md->sha1.state[3] = 0x10325476UL; + md->sha1.state[4] = 0xc3d2e1f0UL; + md->sha1.curlen = 0; + md->sha1.length = 0; + return CRYPT_OK; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful + */ +HASH_PROCESS(sha1_process, sha1_compress, sha1, 64) + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (20 bytes) + @return CRYPT_OK if successful + */ +int sha1_done(hash_state *md, unsigned char *out) { + int i; + + LTC_ARGCHK(md != NULL); + LTC_ARGCHK(out != NULL); + + if (md->sha1.curlen >= sizeof(md->sha1.buf)) { + return CRYPT_INVALID_ARG; + } + + /* increase the length of the message */ + md->sha1.length += md->sha1.curlen * 8; + + /* append the '1' bit */ + md->sha1.buf[md->sha1.curlen++] = (unsigned char)0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->sha1.curlen > 56) { + while (md->sha1.curlen < 64) { + md->sha1.buf[md->sha1.curlen++] = (unsigned char)0; + } + sha1_compress(md, md->sha1.buf); + md->sha1.curlen = 0; + } + + /* pad upto 56 bytes of zeroes */ + while (md->sha1.curlen < 56) { + md->sha1.buf[md->sha1.curlen++] = (unsigned char)0; + } + + /* store length */ + STORE64H(md->sha1.length, md->sha1.buf + 56); + sha1_compress(md, md->sha1.buf); + + /* copy output */ + for (i = 0; i < 5; i++) { + STORE32H(md->sha1.state[i], out + (4 * i)); + } + #ifdef LTC_CLEAN_STACK + zeromem(md, sizeof(hash_state)); + #endif + return CRYPT_OK; +} + +/** + Self-test the hash + @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled + */ +int sha1_test(void) { + #ifndef LTC_TEST + return CRYPT_NOP; + #else + static const struct { + char *msg; + unsigned char hash[20]; + } tests[] = { + { "abc", + { 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, + 0xba, 0x3e, 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, + 0x9c, 0xd0, 0xd8, 0x9d } }, + { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, + 0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, + 0xE5, 0x46, 0x70, 0xF1 } } + }; + + int i; + unsigned char tmp[20]; + hash_state md; + + for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) { + sha1_init(&md); + sha1_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg)); + sha1_done(&md, tmp); + if (XMEMCMP(tmp, tests[i].hash, 20) != 0) { + return CRYPT_FAIL_TESTVECTOR; + } + } + return CRYPT_OK; + #endif +} +#endif + + + +/* $Source: /cvs/libtom/libtomcrypt/src/hashes/sha1.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2007/05/12 14:25:28 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file sha256.c + LTC_SHA256 by Tom St Denis +*/ + +#ifdef LTC_SHA256 + +const struct ltc_hash_descriptor sha256_desc = +{ + "sha256", + 0, + 32, + 64, + + /* OID */ + { 2, 16, 840, 1, 101, 3, 4, 2, 1, }, + 9, + + &sha256_init, + &sha256_process, + &sha256_done, + &sha256_test, + NULL +}; + +#ifdef LTC_SMALL_CODE +/* the K array */ +static const ulong32 K[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, + 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, + 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, + 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, + 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, + 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, + 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, + 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, + 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; +#endif + +/* Various logical functions */ +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) RORc((x),(n)) +#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) + +/* compress 512-bits */ +#ifdef LTC_CLEAN_STACK +static int _sha256_compress(hash_state * md, unsigned char *buf) +#else +static int sha256_compress(hash_state * md, unsigned char *buf) +#endif +{ + ulong32 S[8], W[64], t0, t1; +#ifdef LTC_SMALL_CODE + ulong32 t; +#endif + int i; + + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->sha256.state[i]; + } + + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD32H(W[i], buf + (4*i)); + } + + /* fill W[16..63] */ + for (i = 16; i < 64; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + } + + /* Compress */ +#ifdef LTC_SMALL_CODE +#define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + for (i = 0; i < 64; ++i) { + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i); + t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; + S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; + } +#else +#define RND(a,b,c,d,e,f,g,h,i,ki) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2); + +#undef RND + +#endif + + /* feedback */ + for (i = 0; i < 8; i++) { + md->sha256.state[i] = md->sha256.state[i] + S[i]; + } + return CRYPT_OK; +} + +#ifdef LTC_CLEAN_STACK +static int sha256_compress(hash_state * md, unsigned char *buf) +{ + int err; + err = _sha256_compress(md, buf); + burn_stack(sizeof(ulong32) * 74); + return err; +} +#endif + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful +*/ +int sha256_init(hash_state * md) +{ + LTC_ARGCHK(md != NULL); + + md->sha256.curlen = 0; + md->sha256.length = 0; + md->sha256.state[0] = 0x6A09E667UL; + md->sha256.state[1] = 0xBB67AE85UL; + md->sha256.state[2] = 0x3C6EF372UL; + md->sha256.state[3] = 0xA54FF53AUL; + md->sha256.state[4] = 0x510E527FUL; + md->sha256.state[5] = 0x9B05688CUL; + md->sha256.state[6] = 0x1F83D9ABUL; + md->sha256.state[7] = 0x5BE0CD19UL; + return CRYPT_OK; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful +*/ +HASH_PROCESS(sha256_process, sha256_compress, sha256, 64) + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (32 bytes) + @return CRYPT_OK if successful +*/ +int sha256_done(hash_state * md, unsigned char *out) +{ + int i; + + LTC_ARGCHK(md != NULL); + LTC_ARGCHK(out != NULL); + + if (md->sha256.curlen >= sizeof(md->sha256.buf)) { + return CRYPT_INVALID_ARG; + } + + + /* increase the length of the message */ + md->sha256.length += md->sha256.curlen * 8; + + /* append the '1' bit */ + md->sha256.buf[md->sha256.curlen++] = (unsigned char)0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->sha256.curlen > 56) { + while (md->sha256.curlen < 64) { + md->sha256.buf[md->sha256.curlen++] = (unsigned char)0; + } + sha256_compress(md, md->sha256.buf); + md->sha256.curlen = 0; + } + + /* pad upto 56 bytes of zeroes */ + while (md->sha256.curlen < 56) { + md->sha256.buf[md->sha256.curlen++] = (unsigned char)0; + } + + /* store length */ + STORE64H(md->sha256.length, md->sha256.buf+56); + sha256_compress(md, md->sha256.buf); + + /* copy output */ + for (i = 0; i < 8; i++) { + STORE32H(md->sha256.state[i], out+(4*i)); + } +#ifdef LTC_CLEAN_STACK + zeromem(md, sizeof(hash_state)); +#endif + return CRYPT_OK; +} + +/** + Self-test the hash + @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled +*/ +int sha256_test(void) +{ + #ifndef LTC_TEST + return CRYPT_NOP; + #else + static const struct { + char *msg; + unsigned char hash[32]; + } tests[] = { + { "abc", + { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad } + }, + { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + { 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, + 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39, + 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, + 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1 } + }, + }; + + int i; + unsigned char tmp[32]; + hash_state md; + + for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) { + sha256_init(&md); + sha256_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg)); + sha256_done(&md, tmp); + if (XMEMCMP(tmp, tests[i].hash, 32) != 0) { + return CRYPT_FAIL_TESTVECTOR; + } + } + return CRYPT_OK; + #endif +} + +#endif + + + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +/** + @param sha384.c + LTC_SHA384 hash included in sha512.c, Tom St Denis +*/ + + + +#if defined(LTC_SHA384) && defined(LTC_SHA512) + +const struct ltc_hash_descriptor sha384_desc = +{ + "sha384", + 4, + 48, + 128, + + /* OID */ + { 2, 16, 840, 1, 101, 3, 4, 2, 2, }, + 9, + + &sha384_init, + &sha512_process, + &sha384_done, + &sha384_test, + NULL +}; + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful +*/ +int sha384_init(hash_state * md) +{ + LTC_ARGCHK(md != NULL); + + md->sha512.curlen = 0; + md->sha512.length = 0; + md->sha512.state[0] = CONST64(0xcbbb9d5dc1059ed8); + md->sha512.state[1] = CONST64(0x629a292a367cd507); + md->sha512.state[2] = CONST64(0x9159015a3070dd17); + md->sha512.state[3] = CONST64(0x152fecd8f70e5939); + md->sha512.state[4] = CONST64(0x67332667ffc00b31); + md->sha512.state[5] = CONST64(0x8eb44a8768581511); + md->sha512.state[6] = CONST64(0xdb0c2e0d64f98fa7); + md->sha512.state[7] = CONST64(0x47b5481dbefa4fa4); + return CRYPT_OK; +} + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (48 bytes) + @return CRYPT_OK if successful +*/ +int sha384_done(hash_state * md, unsigned char *out) +{ + unsigned char buf[64]; + + LTC_ARGCHK(md != NULL); + LTC_ARGCHK(out != NULL); + + if (md->sha512.curlen >= sizeof(md->sha512.buf)) { + return CRYPT_INVALID_ARG; + } + + sha512_done(md, buf); + XMEMCPY(out, buf, 48); +#ifdef LTC_CLEAN_STACK + zeromem(buf, sizeof(buf)); +#endif + return CRYPT_OK; +} + +/** + Self-test the hash + @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled +*/ +int sha384_test(void) +{ + #ifndef LTC_TEST + return CRYPT_NOP; + #else + static const struct { + char *msg; + unsigned char hash[48]; + } tests[] = { + { "abc", + { 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b, + 0xb5, 0xa0, 0x3d, 0x69, 0x9a, 0xc6, 0x50, 0x07, + 0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63, + 0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed, + 0x80, 0x86, 0x07, 0x2b, 0xa1, 0xe7, 0xcc, 0x23, + 0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7 } + }, + { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + { 0x09, 0x33, 0x0c, 0x33, 0xf7, 0x11, 0x47, 0xe8, + 0x3d, 0x19, 0x2f, 0xc7, 0x82, 0xcd, 0x1b, 0x47, + 0x53, 0x11, 0x1b, 0x17, 0x3b, 0x3b, 0x05, 0xd2, + 0x2f, 0xa0, 0x80, 0x86, 0xe3, 0xb0, 0xf7, 0x12, + 0xfc, 0xc7, 0xc7, 0x1a, 0x55, 0x7e, 0x2d, 0xb9, + 0x66, 0xc3, 0xe9, 0xfa, 0x91, 0x74, 0x60, 0x39 } + }, + }; + + int i; + unsigned char tmp[48]; + hash_state md; + + for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) { + sha384_init(&md); + sha384_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg)); + sha384_done(&md, tmp); + if (XMEMCMP(tmp, tests[i].hash, 48) != 0) { + return CRYPT_FAIL_TESTVECTOR; + } + } + return CRYPT_OK; + #endif +} + +#endif /* defined(LTC_SHA384) && defined(LTC_SHA512) */ + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @param sha512.c + LTC_SHA512 by Tom St Denis +*/ + +#ifdef LTC_SHA512 + +const struct ltc_hash_descriptor sha512_desc = +{ + "sha512", + 5, + 64, + 128, + + /* OID */ + { 2, 16, 840, 1, 101, 3, 4, 2, 3, }, + 9, + + &sha512_init, + &sha512_process, + &sha512_done, + &sha512_test, + NULL +}; + +/* the K array */ +static const ulong64 K[80] = { +CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd), +CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc), +CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019), +CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118), +CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe), +CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2), +CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1), +CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694), +CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3), +CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65), +CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483), +CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5), +CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210), +CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4), +CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725), +CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70), +CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926), +CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df), +CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8), +CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b), +CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001), +CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30), +CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910), +CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8), +CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53), +CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8), +CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb), +CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3), +CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60), +CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec), +CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9), +CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b), +CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207), +CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178), +CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6), +CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b), +CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493), +CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c), +CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a), +CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817) +}; + +/* Various logical functions */ +#undef S +#undef R +#undef Sigma0 +#undef Sigma1 +#undef Gamma0 +#undef Gamma1 + +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) ROR64c(x, n) +#define R(x, n) (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)n)) +#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) +#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) +#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) +#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) + +/* compress 1024-bits */ +#ifdef LTC_CLEAN_STACK +static int _sha512_compress(hash_state * md, unsigned char *buf) +#else +static int sha512_compress(hash_state * md, unsigned char *buf) +#endif +{ + ulong64 S[8], W[80], t0, t1; + int i; + + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->sha512.state[i]; + } + + /* copy the state into 1024-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD64H(W[i], buf + (8*i)); + } + + /* fill W[16..79] */ + for (i = 16; i < 80; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + } + + /* Compress */ +#ifdef LTC_SMALL_CODE + for (i = 0; i < 80; i++) { + t0 = S[7] + Sigma1(S[4]) + Ch(S[4], S[5], S[6]) + K[i] + W[i]; + t1 = Sigma0(S[0]) + Maj(S[0], S[1], S[2]); + S[7] = S[6]; + S[6] = S[5]; + S[5] = S[4]; + S[4] = S[3] + t0; + S[3] = S[2]; + S[2] = S[1]; + S[1] = S[0]; + S[0] = t0 + t1; + } +#else +#define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + for (i = 0; i < 80; i += 8) { + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7); + } +#endif + + + /* feedback */ + for (i = 0; i < 8; i++) { + md->sha512.state[i] = md->sha512.state[i] + S[i]; + } + + return CRYPT_OK; +} + +/* compress 1024-bits */ +#ifdef LTC_CLEAN_STACK +static int sha512_compress(hash_state * md, unsigned char *buf) +{ + int err; + err = _sha512_compress(md, buf); + burn_stack(sizeof(ulong64) * 90 + sizeof(int)); + return err; +} +#endif + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful +*/ +int sha512_init(hash_state * md) +{ + LTC_ARGCHK(md != NULL); + md->sha512.curlen = 0; + md->sha512.length = 0; + md->sha512.state[0] = CONST64(0x6a09e667f3bcc908); + md->sha512.state[1] = CONST64(0xbb67ae8584caa73b); + md->sha512.state[2] = CONST64(0x3c6ef372fe94f82b); + md->sha512.state[3] = CONST64(0xa54ff53a5f1d36f1); + md->sha512.state[4] = CONST64(0x510e527fade682d1); + md->sha512.state[5] = CONST64(0x9b05688c2b3e6c1f); + md->sha512.state[6] = CONST64(0x1f83d9abfb41bd6b); + md->sha512.state[7] = CONST64(0x5be0cd19137e2179); + return CRYPT_OK; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful +*/ +HASH_PROCESS(sha512_process, sha512_compress, sha512, 128) + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (64 bytes) + @return CRYPT_OK if successful +*/ +int sha512_done(hash_state * md, unsigned char *out) +{ + int i; + + LTC_ARGCHK(md != NULL); + LTC_ARGCHK(out != NULL); + + if (md->sha512.curlen >= sizeof(md->sha512.buf)) { + return CRYPT_INVALID_ARG; + } + + /* increase the length of the message */ + md->sha512.length += md->sha512.curlen * CONST64(8); + + /* append the '1' bit */ + md->sha512.buf[md->sha512.curlen++] = (unsigned char)0x80; + + /* if the length is currently above 112 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->sha512.curlen > 112) { + while (md->sha512.curlen < 128) { + md->sha512.buf[md->sha512.curlen++] = (unsigned char)0; + } + sha512_compress(md, md->sha512.buf); + md->sha512.curlen = 0; + } + + /* pad upto 120 bytes of zeroes + * note: that from 112 to 120 is the 64 MSB of the length. We assume that you won't hash + * > 2^64 bits of data... :-) + */ + while (md->sha512.curlen < 120) { + md->sha512.buf[md->sha512.curlen++] = (unsigned char)0; + } + + /* store length */ + STORE64H(md->sha512.length, md->sha512.buf+120); + sha512_compress(md, md->sha512.buf); + + /* copy output */ + for (i = 0; i < 8; i++) { + STORE64H(md->sha512.state[i], out+(8*i)); + } +#ifdef LTC_CLEAN_STACK + zeromem(md, sizeof(hash_state)); +#endif + return CRYPT_OK; +} + +/** + Self-test the hash + @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled +*/ +int sha512_test(void) +{ + #ifndef LTC_TEST + return CRYPT_NOP; + #else + static const struct { + char *msg; + unsigned char hash[64]; + } tests[] = { + { "abc", + { 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, + 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31, + 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, + 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, + 0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8, + 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, + 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, + 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f } + }, + { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + { 0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda, + 0x8c, 0xf4, 0xf7, 0x28, 0x14, 0xfc, 0x14, 0x3f, + 0x8f, 0x77, 0x79, 0xc6, 0xeb, 0x9f, 0x7f, 0xa1, + 0x72, 0x99, 0xae, 0xad, 0xb6, 0x88, 0x90, 0x18, + 0x50, 0x1d, 0x28, 0x9e, 0x49, 0x00, 0xf7, 0xe4, + 0x33, 0x1b, 0x99, 0xde, 0xc4, 0xb5, 0x43, 0x3a, + 0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, 0x26, 0x54, + 0x5e, 0x96, 0xe5, 0x5b, 0x87, 0x4b, 0xe9, 0x09 } + }, + }; + + int i; + unsigned char tmp[64]; + hash_state md; + + for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) { + sha512_init(&md); + sha512_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg)); + sha512_done(&md, tmp); + if (XMEMCMP(tmp, tests[i].hash, 64) != 0) { + return CRYPT_FAIL_TESTVECTOR; + } + } + return CRYPT_OK; + #endif +} + +#endif + + + + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file hmac_init.c + HMAC support, initialize state, Tom St Denis/Dobes Vandermeer +*/ + +#ifdef LTC_HMAC + +#define LTC_HMAC_BLOCKSIZE hash_descriptor[hash].blocksize + +/** + Initialize an HMAC context. + @param hmac The HMAC state + @param hash The index of the hash you want to use + @param key The secret key + @param keylen The length of the secret key (octets) + @return CRYPT_OK if successful +*/ +int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, unsigned long keylen) +{ + unsigned char *buf; + unsigned long hashsize; + unsigned long i, z; + int err; + + LTC_ARGCHK(hmac != NULL); + LTC_ARGCHK(key != NULL); + + /* valid hash? */ + if ((err = hash_is_valid(hash)) != CRYPT_OK) { + return err; + } + hmac->hash = hash; + hashsize = hash_descriptor[hash].hashsize; + + /* valid key length? */ + if (keylen == 0) { + return CRYPT_INVALID_KEYSIZE; + } + + /* allocate ram for buf */ + buf = XMALLOC(LTC_HMAC_BLOCKSIZE); + if (buf == NULL) { + return CRYPT_MEM; + } + + /* allocate memory for key */ + hmac->key = XMALLOC(LTC_HMAC_BLOCKSIZE); + if (hmac->key == NULL) { + XFREE(buf); + return CRYPT_MEM; + } + + /* (1) make sure we have a large enough key */ + if(keylen > LTC_HMAC_BLOCKSIZE) { + z = LTC_HMAC_BLOCKSIZE; + if ((err = hash_memory(hash, key, keylen, hmac->key, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + keylen = hashsize; + } else { + XMEMCPY(hmac->key, key, (size_t)keylen); + } + + if(keylen < LTC_HMAC_BLOCKSIZE) { + zeromem((hmac->key) + keylen, (size_t)(LTC_HMAC_BLOCKSIZE - keylen)); + } + + /* Create the initial vector for step (3) */ + for(i=0; i < LTC_HMAC_BLOCKSIZE; i++) { + buf[i] = hmac->key[i] ^ 0x36; + } + + /* Pre-pend that to the hash data */ + if ((err = hash_descriptor[hash].init(&hmac->md)) != CRYPT_OK) { + goto LBL_ERR; + } + + if ((err = hash_descriptor[hash].process(&hmac->md, buf, LTC_HMAC_BLOCKSIZE)) != CRYPT_OK) { + goto LBL_ERR; + } + goto done; +LBL_ERR: + /* free the key since we failed */ + XFREE(hmac->key); +done: +#ifdef LTC_CLEAN_STACK + zeromem(buf, LTC_HMAC_BLOCKSIZE); +#endif + + XFREE(buf); + return err; +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file hmac_process.c + HMAC support, process data, Tom St Denis/Dobes Vandermeer +*/ + +#ifdef LTC_HMAC + +/** + Process data through HMAC + @param hmac The hmac state + @param in The data to send through HMAC + @param inlen The length of the data to HMAC (octets) + @return CRYPT_OK if successful +*/ +int hmac_process(hmac_state *hmac, const unsigned char *in, unsigned long inlen) +{ + int err; + LTC_ARGCHK(hmac != NULL); + LTC_ARGCHK(in != NULL); + if ((err = hash_is_valid(hmac->hash)) != CRYPT_OK) { + return err; + } + return hash_descriptor[hmac->hash].process(&hmac->md, in, inlen); +} + +#endif + + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file hmac_done.c + HMAC support, terminate stream, Tom St Denis/Dobes Vandermeer +*/ + +#ifdef LTC_HMAC + +#define LTC_HMAC_BLOCKSIZE hash_descriptor[hash].blocksize + +/** + Terminate an HMAC session + @param hmac The HMAC state + @param out [out] The destination of the HMAC authentication tag + @param outlen [in/out] The max size and resulting size of the HMAC authentication tag + @return CRYPT_OK if successful +*/ +int hmac_done(hmac_state *hmac, unsigned char *out, unsigned long *outlen) +{ + unsigned char *buf, *isha; + unsigned long hashsize, i; + int hash, err; + + LTC_ARGCHK(hmac != NULL); + LTC_ARGCHK(out != NULL); + + /* test hash */ + hash = hmac->hash; + if((err = hash_is_valid(hash)) != CRYPT_OK) { + return err; + } + + /* get the hash message digest size */ + hashsize = hash_descriptor[hash].hashsize; + + /* allocate buffers */ + buf = XMALLOC(LTC_HMAC_BLOCKSIZE); + isha = XMALLOC(hashsize); + if (buf == NULL || isha == NULL) { + if (buf != NULL) { + XFREE(buf); + } + if (isha != NULL) { + XFREE(isha); + } + return CRYPT_MEM; + } + + /* Get the hash of the first HMAC vector plus the data */ + if ((err = hash_descriptor[hash].done(&hmac->md, isha)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* Create the second HMAC vector vector for step (3) */ + for(i=0; i < LTC_HMAC_BLOCKSIZE; i++) { + buf[i] = hmac->key[i] ^ 0x5C; + } + + /* Now calculate the "outer" hash for step (5), (6), and (7) */ + if ((err = hash_descriptor[hash].init(&hmac->md)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash].process(&hmac->md, buf, LTC_HMAC_BLOCKSIZE)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash].process(&hmac->md, isha, hashsize)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash].done(&hmac->md, buf)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* copy to output */ + for (i = 0; i < hashsize && i < *outlen; i++) { + out[i] = buf[i]; + } + *outlen = i; + + err = CRYPT_OK; +LBL_ERR: + XFREE(hmac->key); +#ifdef LTC_CLEAN_STACK + zeromem(isha, hashsize); + zeromem(buf, hashsize); + zeromem(hmac, sizeof(*hmac)); +#endif + + XFREE(isha); + XFREE(buf); + + return err; +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +#define __LTC_AES_TAB_C__ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +/* The precomputed tables for AES */ +/* +Te0[x] = S [x].[02, 01, 01, 03]; +Te1[x] = S [x].[03, 02, 01, 01]; +Te2[x] = S [x].[01, 03, 02, 01]; +Te3[x] = S [x].[01, 01, 03, 02]; +Te4[x] = S [x].[01, 01, 01, 01]; + +Td0[x] = Si[x].[0e, 09, 0d, 0b]; +Td1[x] = Si[x].[0b, 0e, 09, 0d]; +Td2[x] = Si[x].[0d, 0b, 0e, 09]; +Td3[x] = Si[x].[09, 0d, 0b, 0e]; +Td4[x] = Si[x].[01, 01, 01, 01]; +*/ + +#ifdef __LTC_AES_TAB_C__ + +/** + @file aes_tab.c + AES tables +*/ +static const ulong32 TE0[256] = { + 0xc66363a5UL, 0xf87c7c84UL, 0xee777799UL, 0xf67b7b8dUL, + 0xfff2f20dUL, 0xd66b6bbdUL, 0xde6f6fb1UL, 0x91c5c554UL, + 0x60303050UL, 0x02010103UL, 0xce6767a9UL, 0x562b2b7dUL, + 0xe7fefe19UL, 0xb5d7d762UL, 0x4dababe6UL, 0xec76769aUL, + 0x8fcaca45UL, 0x1f82829dUL, 0x89c9c940UL, 0xfa7d7d87UL, + 0xeffafa15UL, 0xb25959ebUL, 0x8e4747c9UL, 0xfbf0f00bUL, + 0x41adadecUL, 0xb3d4d467UL, 0x5fa2a2fdUL, 0x45afafeaUL, + 0x239c9cbfUL, 0x53a4a4f7UL, 0xe4727296UL, 0x9bc0c05bUL, + 0x75b7b7c2UL, 0xe1fdfd1cUL, 0x3d9393aeUL, 0x4c26266aUL, + 0x6c36365aUL, 0x7e3f3f41UL, 0xf5f7f702UL, 0x83cccc4fUL, + 0x6834345cUL, 0x51a5a5f4UL, 0xd1e5e534UL, 0xf9f1f108UL, + 0xe2717193UL, 0xabd8d873UL, 0x62313153UL, 0x2a15153fUL, + 0x0804040cUL, 0x95c7c752UL, 0x46232365UL, 0x9dc3c35eUL, + 0x30181828UL, 0x379696a1UL, 0x0a05050fUL, 0x2f9a9ab5UL, + 0x0e070709UL, 0x24121236UL, 0x1b80809bUL, 0xdfe2e23dUL, + 0xcdebeb26UL, 0x4e272769UL, 0x7fb2b2cdUL, 0xea75759fUL, + 0x1209091bUL, 0x1d83839eUL, 0x582c2c74UL, 0x341a1a2eUL, + 0x361b1b2dUL, 0xdc6e6eb2UL, 0xb45a5aeeUL, 0x5ba0a0fbUL, + 0xa45252f6UL, 0x763b3b4dUL, 0xb7d6d661UL, 0x7db3b3ceUL, + 0x5229297bUL, 0xdde3e33eUL, 0x5e2f2f71UL, 0x13848497UL, + 0xa65353f5UL, 0xb9d1d168UL, 0x00000000UL, 0xc1eded2cUL, + 0x40202060UL, 0xe3fcfc1fUL, 0x79b1b1c8UL, 0xb65b5bedUL, + 0xd46a6abeUL, 0x8dcbcb46UL, 0x67bebed9UL, 0x7239394bUL, + 0x944a4adeUL, 0x984c4cd4UL, 0xb05858e8UL, 0x85cfcf4aUL, + 0xbbd0d06bUL, 0xc5efef2aUL, 0x4faaaae5UL, 0xedfbfb16UL, + 0x864343c5UL, 0x9a4d4dd7UL, 0x66333355UL, 0x11858594UL, + 0x8a4545cfUL, 0xe9f9f910UL, 0x04020206UL, 0xfe7f7f81UL, + 0xa05050f0UL, 0x783c3c44UL, 0x259f9fbaUL, 0x4ba8a8e3UL, + 0xa25151f3UL, 0x5da3a3feUL, 0x804040c0UL, 0x058f8f8aUL, + 0x3f9292adUL, 0x219d9dbcUL, 0x70383848UL, 0xf1f5f504UL, + 0x63bcbcdfUL, 0x77b6b6c1UL, 0xafdada75UL, 0x42212163UL, + 0x20101030UL, 0xe5ffff1aUL, 0xfdf3f30eUL, 0xbfd2d26dUL, + 0x81cdcd4cUL, 0x180c0c14UL, 0x26131335UL, 0xc3ecec2fUL, + 0xbe5f5fe1UL, 0x359797a2UL, 0x884444ccUL, 0x2e171739UL, + 0x93c4c457UL, 0x55a7a7f2UL, 0xfc7e7e82UL, 0x7a3d3d47UL, + 0xc86464acUL, 0xba5d5de7UL, 0x3219192bUL, 0xe6737395UL, + 0xc06060a0UL, 0x19818198UL, 0x9e4f4fd1UL, 0xa3dcdc7fUL, + 0x44222266UL, 0x542a2a7eUL, 0x3b9090abUL, 0x0b888883UL, + 0x8c4646caUL, 0xc7eeee29UL, 0x6bb8b8d3UL, 0x2814143cUL, + 0xa7dede79UL, 0xbc5e5ee2UL, 0x160b0b1dUL, 0xaddbdb76UL, + 0xdbe0e03bUL, 0x64323256UL, 0x743a3a4eUL, 0x140a0a1eUL, + 0x924949dbUL, 0x0c06060aUL, 0x4824246cUL, 0xb85c5ce4UL, + 0x9fc2c25dUL, 0xbdd3d36eUL, 0x43acacefUL, 0xc46262a6UL, + 0x399191a8UL, 0x319595a4UL, 0xd3e4e437UL, 0xf279798bUL, + 0xd5e7e732UL, 0x8bc8c843UL, 0x6e373759UL, 0xda6d6db7UL, + 0x018d8d8cUL, 0xb1d5d564UL, 0x9c4e4ed2UL, 0x49a9a9e0UL, + 0xd86c6cb4UL, 0xac5656faUL, 0xf3f4f407UL, 0xcfeaea25UL, + 0xca6565afUL, 0xf47a7a8eUL, 0x47aeaee9UL, 0x10080818UL, + 0x6fbabad5UL, 0xf0787888UL, 0x4a25256fUL, 0x5c2e2e72UL, + 0x381c1c24UL, 0x57a6a6f1UL, 0x73b4b4c7UL, 0x97c6c651UL, + 0xcbe8e823UL, 0xa1dddd7cUL, 0xe874749cUL, 0x3e1f1f21UL, + 0x964b4bddUL, 0x61bdbddcUL, 0x0d8b8b86UL, 0x0f8a8a85UL, + 0xe0707090UL, 0x7c3e3e42UL, 0x71b5b5c4UL, 0xcc6666aaUL, + 0x904848d8UL, 0x06030305UL, 0xf7f6f601UL, 0x1c0e0e12UL, + 0xc26161a3UL, 0x6a35355fUL, 0xae5757f9UL, 0x69b9b9d0UL, + 0x17868691UL, 0x99c1c158UL, 0x3a1d1d27UL, 0x279e9eb9UL, + 0xd9e1e138UL, 0xebf8f813UL, 0x2b9898b3UL, 0x22111133UL, + 0xd26969bbUL, 0xa9d9d970UL, 0x078e8e89UL, 0x339494a7UL, + 0x2d9b9bb6UL, 0x3c1e1e22UL, 0x15878792UL, 0xc9e9e920UL, + 0x87cece49UL, 0xaa5555ffUL, 0x50282878UL, 0xa5dfdf7aUL, + 0x038c8c8fUL, 0x59a1a1f8UL, 0x09898980UL, 0x1a0d0d17UL, + 0x65bfbfdaUL, 0xd7e6e631UL, 0x844242c6UL, 0xd06868b8UL, + 0x824141c3UL, 0x299999b0UL, 0x5a2d2d77UL, 0x1e0f0f11UL, + 0x7bb0b0cbUL, 0xa85454fcUL, 0x6dbbbbd6UL, 0x2c16163aUL, +}; + +#ifndef PELI_TAB +static const ulong32 Te4[256] = { + 0x63636363UL, 0x7c7c7c7cUL, 0x77777777UL, 0x7b7b7b7bUL, + 0xf2f2f2f2UL, 0x6b6b6b6bUL, 0x6f6f6f6fUL, 0xc5c5c5c5UL, + 0x30303030UL, 0x01010101UL, 0x67676767UL, 0x2b2b2b2bUL, + 0xfefefefeUL, 0xd7d7d7d7UL, 0xababababUL, 0x76767676UL, + 0xcacacacaUL, 0x82828282UL, 0xc9c9c9c9UL, 0x7d7d7d7dUL, + 0xfafafafaUL, 0x59595959UL, 0x47474747UL, 0xf0f0f0f0UL, + 0xadadadadUL, 0xd4d4d4d4UL, 0xa2a2a2a2UL, 0xafafafafUL, + 0x9c9c9c9cUL, 0xa4a4a4a4UL, 0x72727272UL, 0xc0c0c0c0UL, + 0xb7b7b7b7UL, 0xfdfdfdfdUL, 0x93939393UL, 0x26262626UL, + 0x36363636UL, 0x3f3f3f3fUL, 0xf7f7f7f7UL, 0xccccccccUL, + 0x34343434UL, 0xa5a5a5a5UL, 0xe5e5e5e5UL, 0xf1f1f1f1UL, + 0x71717171UL, 0xd8d8d8d8UL, 0x31313131UL, 0x15151515UL, + 0x04040404UL, 0xc7c7c7c7UL, 0x23232323UL, 0xc3c3c3c3UL, + 0x18181818UL, 0x96969696UL, 0x05050505UL, 0x9a9a9a9aUL, + 0x07070707UL, 0x12121212UL, 0x80808080UL, 0xe2e2e2e2UL, + 0xebebebebUL, 0x27272727UL, 0xb2b2b2b2UL, 0x75757575UL, + 0x09090909UL, 0x83838383UL, 0x2c2c2c2cUL, 0x1a1a1a1aUL, + 0x1b1b1b1bUL, 0x6e6e6e6eUL, 0x5a5a5a5aUL, 0xa0a0a0a0UL, + 0x52525252UL, 0x3b3b3b3bUL, 0xd6d6d6d6UL, 0xb3b3b3b3UL, + 0x29292929UL, 0xe3e3e3e3UL, 0x2f2f2f2fUL, 0x84848484UL, + 0x53535353UL, 0xd1d1d1d1UL, 0x00000000UL, 0xededededUL, + 0x20202020UL, 0xfcfcfcfcUL, 0xb1b1b1b1UL, 0x5b5b5b5bUL, + 0x6a6a6a6aUL, 0xcbcbcbcbUL, 0xbebebebeUL, 0x39393939UL, + 0x4a4a4a4aUL, 0x4c4c4c4cUL, 0x58585858UL, 0xcfcfcfcfUL, + 0xd0d0d0d0UL, 0xefefefefUL, 0xaaaaaaaaUL, 0xfbfbfbfbUL, + 0x43434343UL, 0x4d4d4d4dUL, 0x33333333UL, 0x85858585UL, + 0x45454545UL, 0xf9f9f9f9UL, 0x02020202UL, 0x7f7f7f7fUL, + 0x50505050UL, 0x3c3c3c3cUL, 0x9f9f9f9fUL, 0xa8a8a8a8UL, + 0x51515151UL, 0xa3a3a3a3UL, 0x40404040UL, 0x8f8f8f8fUL, + 0x92929292UL, 0x9d9d9d9dUL, 0x38383838UL, 0xf5f5f5f5UL, + 0xbcbcbcbcUL, 0xb6b6b6b6UL, 0xdadadadaUL, 0x21212121UL, + 0x10101010UL, 0xffffffffUL, 0xf3f3f3f3UL, 0xd2d2d2d2UL, + 0xcdcdcdcdUL, 0x0c0c0c0cUL, 0x13131313UL, 0xececececUL, + 0x5f5f5f5fUL, 0x97979797UL, 0x44444444UL, 0x17171717UL, + 0xc4c4c4c4UL, 0xa7a7a7a7UL, 0x7e7e7e7eUL, 0x3d3d3d3dUL, + 0x64646464UL, 0x5d5d5d5dUL, 0x19191919UL, 0x73737373UL, + 0x60606060UL, 0x81818181UL, 0x4f4f4f4fUL, 0xdcdcdcdcUL, + 0x22222222UL, 0x2a2a2a2aUL, 0x90909090UL, 0x88888888UL, + 0x46464646UL, 0xeeeeeeeeUL, 0xb8b8b8b8UL, 0x14141414UL, + 0xdedededeUL, 0x5e5e5e5eUL, 0x0b0b0b0bUL, 0xdbdbdbdbUL, + 0xe0e0e0e0UL, 0x32323232UL, 0x3a3a3a3aUL, 0x0a0a0a0aUL, + 0x49494949UL, 0x06060606UL, 0x24242424UL, 0x5c5c5c5cUL, + 0xc2c2c2c2UL, 0xd3d3d3d3UL, 0xacacacacUL, 0x62626262UL, + 0x91919191UL, 0x95959595UL, 0xe4e4e4e4UL, 0x79797979UL, + 0xe7e7e7e7UL, 0xc8c8c8c8UL, 0x37373737UL, 0x6d6d6d6dUL, + 0x8d8d8d8dUL, 0xd5d5d5d5UL, 0x4e4e4e4eUL, 0xa9a9a9a9UL, + 0x6c6c6c6cUL, 0x56565656UL, 0xf4f4f4f4UL, 0xeaeaeaeaUL, + 0x65656565UL, 0x7a7a7a7aUL, 0xaeaeaeaeUL, 0x08080808UL, + 0xbabababaUL, 0x78787878UL, 0x25252525UL, 0x2e2e2e2eUL, + 0x1c1c1c1cUL, 0xa6a6a6a6UL, 0xb4b4b4b4UL, 0xc6c6c6c6UL, + 0xe8e8e8e8UL, 0xddddddddUL, 0x74747474UL, 0x1f1f1f1fUL, + 0x4b4b4b4bUL, 0xbdbdbdbdUL, 0x8b8b8b8bUL, 0x8a8a8a8aUL, + 0x70707070UL, 0x3e3e3e3eUL, 0xb5b5b5b5UL, 0x66666666UL, + 0x48484848UL, 0x03030303UL, 0xf6f6f6f6UL, 0x0e0e0e0eUL, + 0x61616161UL, 0x35353535UL, 0x57575757UL, 0xb9b9b9b9UL, + 0x86868686UL, 0xc1c1c1c1UL, 0x1d1d1d1dUL, 0x9e9e9e9eUL, + 0xe1e1e1e1UL, 0xf8f8f8f8UL, 0x98989898UL, 0x11111111UL, + 0x69696969UL, 0xd9d9d9d9UL, 0x8e8e8e8eUL, 0x94949494UL, + 0x9b9b9b9bUL, 0x1e1e1e1eUL, 0x87878787UL, 0xe9e9e9e9UL, + 0xcecececeUL, 0x55555555UL, 0x28282828UL, 0xdfdfdfdfUL, + 0x8c8c8c8cUL, 0xa1a1a1a1UL, 0x89898989UL, 0x0d0d0d0dUL, + 0xbfbfbfbfUL, 0xe6e6e6e6UL, 0x42424242UL, 0x68686868UL, + 0x41414141UL, 0x99999999UL, 0x2d2d2d2dUL, 0x0f0f0f0fUL, + 0xb0b0b0b0UL, 0x54545454UL, 0xbbbbbbbbUL, 0x16161616UL, +}; +#endif + +#ifndef ENCRYPT_ONLY + +static const ulong32 TD0[256] = { + 0x51f4a750UL, 0x7e416553UL, 0x1a17a4c3UL, 0x3a275e96UL, + 0x3bab6bcbUL, 0x1f9d45f1UL, 0xacfa58abUL, 0x4be30393UL, + 0x2030fa55UL, 0xad766df6UL, 0x88cc7691UL, 0xf5024c25UL, + 0x4fe5d7fcUL, 0xc52acbd7UL, 0x26354480UL, 0xb562a38fUL, + 0xdeb15a49UL, 0x25ba1b67UL, 0x45ea0e98UL, 0x5dfec0e1UL, + 0xc32f7502UL, 0x814cf012UL, 0x8d4697a3UL, 0x6bd3f9c6UL, + 0x038f5fe7UL, 0x15929c95UL, 0xbf6d7aebUL, 0x955259daUL, + 0xd4be832dUL, 0x587421d3UL, 0x49e06929UL, 0x8ec9c844UL, + 0x75c2896aUL, 0xf48e7978UL, 0x99583e6bUL, 0x27b971ddUL, + 0xbee14fb6UL, 0xf088ad17UL, 0xc920ac66UL, 0x7dce3ab4UL, + 0x63df4a18UL, 0xe51a3182UL, 0x97513360UL, 0x62537f45UL, + 0xb16477e0UL, 0xbb6bae84UL, 0xfe81a01cUL, 0xf9082b94UL, + 0x70486858UL, 0x8f45fd19UL, 0x94de6c87UL, 0x527bf8b7UL, + 0xab73d323UL, 0x724b02e2UL, 0xe31f8f57UL, 0x6655ab2aUL, + 0xb2eb2807UL, 0x2fb5c203UL, 0x86c57b9aUL, 0xd33708a5UL, + 0x302887f2UL, 0x23bfa5b2UL, 0x02036abaUL, 0xed16825cUL, + 0x8acf1c2bUL, 0xa779b492UL, 0xf307f2f0UL, 0x4e69e2a1UL, + 0x65daf4cdUL, 0x0605bed5UL, 0xd134621fUL, 0xc4a6fe8aUL, + 0x342e539dUL, 0xa2f355a0UL, 0x058ae132UL, 0xa4f6eb75UL, + 0x0b83ec39UL, 0x4060efaaUL, 0x5e719f06UL, 0xbd6e1051UL, + 0x3e218af9UL, 0x96dd063dUL, 0xdd3e05aeUL, 0x4de6bd46UL, + 0x91548db5UL, 0x71c45d05UL, 0x0406d46fUL, 0x605015ffUL, + 0x1998fb24UL, 0xd6bde997UL, 0x894043ccUL, 0x67d99e77UL, + 0xb0e842bdUL, 0x07898b88UL, 0xe7195b38UL, 0x79c8eedbUL, + 0xa17c0a47UL, 0x7c420fe9UL, 0xf8841ec9UL, 0x00000000UL, + 0x09808683UL, 0x322bed48UL, 0x1e1170acUL, 0x6c5a724eUL, + 0xfd0efffbUL, 0x0f853856UL, 0x3daed51eUL, 0x362d3927UL, + 0x0a0fd964UL, 0x685ca621UL, 0x9b5b54d1UL, 0x24362e3aUL, + 0x0c0a67b1UL, 0x9357e70fUL, 0xb4ee96d2UL, 0x1b9b919eUL, + 0x80c0c54fUL, 0x61dc20a2UL, 0x5a774b69UL, 0x1c121a16UL, + 0xe293ba0aUL, 0xc0a02ae5UL, 0x3c22e043UL, 0x121b171dUL, + 0x0e090d0bUL, 0xf28bc7adUL, 0x2db6a8b9UL, 0x141ea9c8UL, + 0x57f11985UL, 0xaf75074cUL, 0xee99ddbbUL, 0xa37f60fdUL, + 0xf701269fUL, 0x5c72f5bcUL, 0x44663bc5UL, 0x5bfb7e34UL, + 0x8b432976UL, 0xcb23c6dcUL, 0xb6edfc68UL, 0xb8e4f163UL, + 0xd731dccaUL, 0x42638510UL, 0x13972240UL, 0x84c61120UL, + 0x854a247dUL, 0xd2bb3df8UL, 0xaef93211UL, 0xc729a16dUL, + 0x1d9e2f4bUL, 0xdcb230f3UL, 0x0d8652ecUL, 0x77c1e3d0UL, + 0x2bb3166cUL, 0xa970b999UL, 0x119448faUL, 0x47e96422UL, + 0xa8fc8cc4UL, 0xa0f03f1aUL, 0x567d2cd8UL, 0x223390efUL, + 0x87494ec7UL, 0xd938d1c1UL, 0x8ccaa2feUL, 0x98d40b36UL, + 0xa6f581cfUL, 0xa57ade28UL, 0xdab78e26UL, 0x3fadbfa4UL, + 0x2c3a9de4UL, 0x5078920dUL, 0x6a5fcc9bUL, 0x547e4662UL, + 0xf68d13c2UL, 0x90d8b8e8UL, 0x2e39f75eUL, 0x82c3aff5UL, + 0x9f5d80beUL, 0x69d0937cUL, 0x6fd52da9UL, 0xcf2512b3UL, + 0xc8ac993bUL, 0x10187da7UL, 0xe89c636eUL, 0xdb3bbb7bUL, + 0xcd267809UL, 0x6e5918f4UL, 0xec9ab701UL, 0x834f9aa8UL, + 0xe6956e65UL, 0xaaffe67eUL, 0x21bccf08UL, 0xef15e8e6UL, + 0xbae79bd9UL, 0x4a6f36ceUL, 0xea9f09d4UL, 0x29b07cd6UL, + 0x31a4b2afUL, 0x2a3f2331UL, 0xc6a59430UL, 0x35a266c0UL, + 0x744ebc37UL, 0xfc82caa6UL, 0xe090d0b0UL, 0x33a7d815UL, + 0xf104984aUL, 0x41ecdaf7UL, 0x7fcd500eUL, 0x1791f62fUL, + 0x764dd68dUL, 0x43efb04dUL, 0xccaa4d54UL, 0xe49604dfUL, + 0x9ed1b5e3UL, 0x4c6a881bUL, 0xc12c1fb8UL, 0x4665517fUL, + 0x9d5eea04UL, 0x018c355dUL, 0xfa877473UL, 0xfb0b412eUL, + 0xb3671d5aUL, 0x92dbd252UL, 0xe9105633UL, 0x6dd64713UL, + 0x9ad7618cUL, 0x37a10c7aUL, 0x59f8148eUL, 0xeb133c89UL, + 0xcea927eeUL, 0xb761c935UL, 0xe11ce5edUL, 0x7a47b13cUL, + 0x9cd2df59UL, 0x55f2733fUL, 0x1814ce79UL, 0x73c737bfUL, + 0x53f7cdeaUL, 0x5ffdaa5bUL, 0xdf3d6f14UL, 0x7844db86UL, + 0xcaaff381UL, 0xb968c43eUL, 0x3824342cUL, 0xc2a3405fUL, + 0x161dc372UL, 0xbce2250cUL, 0x283c498bUL, 0xff0d9541UL, + 0x39a80171UL, 0x080cb3deUL, 0xd8b4e49cUL, 0x6456c190UL, + 0x7bcb8461UL, 0xd532b670UL, 0x486c5c74UL, 0xd0b85742UL, +}; + +static const ulong32 Td4[256] = { + 0x52525252UL, 0x09090909UL, 0x6a6a6a6aUL, 0xd5d5d5d5UL, + 0x30303030UL, 0x36363636UL, 0xa5a5a5a5UL, 0x38383838UL, + 0xbfbfbfbfUL, 0x40404040UL, 0xa3a3a3a3UL, 0x9e9e9e9eUL, + 0x81818181UL, 0xf3f3f3f3UL, 0xd7d7d7d7UL, 0xfbfbfbfbUL, + 0x7c7c7c7cUL, 0xe3e3e3e3UL, 0x39393939UL, 0x82828282UL, + 0x9b9b9b9bUL, 0x2f2f2f2fUL, 0xffffffffUL, 0x87878787UL, + 0x34343434UL, 0x8e8e8e8eUL, 0x43434343UL, 0x44444444UL, + 0xc4c4c4c4UL, 0xdedededeUL, 0xe9e9e9e9UL, 0xcbcbcbcbUL, + 0x54545454UL, 0x7b7b7b7bUL, 0x94949494UL, 0x32323232UL, + 0xa6a6a6a6UL, 0xc2c2c2c2UL, 0x23232323UL, 0x3d3d3d3dUL, + 0xeeeeeeeeUL, 0x4c4c4c4cUL, 0x95959595UL, 0x0b0b0b0bUL, + 0x42424242UL, 0xfafafafaUL, 0xc3c3c3c3UL, 0x4e4e4e4eUL, + 0x08080808UL, 0x2e2e2e2eUL, 0xa1a1a1a1UL, 0x66666666UL, + 0x28282828UL, 0xd9d9d9d9UL, 0x24242424UL, 0xb2b2b2b2UL, + 0x76767676UL, 0x5b5b5b5bUL, 0xa2a2a2a2UL, 0x49494949UL, + 0x6d6d6d6dUL, 0x8b8b8b8bUL, 0xd1d1d1d1UL, 0x25252525UL, + 0x72727272UL, 0xf8f8f8f8UL, 0xf6f6f6f6UL, 0x64646464UL, + 0x86868686UL, 0x68686868UL, 0x98989898UL, 0x16161616UL, + 0xd4d4d4d4UL, 0xa4a4a4a4UL, 0x5c5c5c5cUL, 0xccccccccUL, + 0x5d5d5d5dUL, 0x65656565UL, 0xb6b6b6b6UL, 0x92929292UL, + 0x6c6c6c6cUL, 0x70707070UL, 0x48484848UL, 0x50505050UL, + 0xfdfdfdfdUL, 0xededededUL, 0xb9b9b9b9UL, 0xdadadadaUL, + 0x5e5e5e5eUL, 0x15151515UL, 0x46464646UL, 0x57575757UL, + 0xa7a7a7a7UL, 0x8d8d8d8dUL, 0x9d9d9d9dUL, 0x84848484UL, + 0x90909090UL, 0xd8d8d8d8UL, 0xababababUL, 0x00000000UL, + 0x8c8c8c8cUL, 0xbcbcbcbcUL, 0xd3d3d3d3UL, 0x0a0a0a0aUL, + 0xf7f7f7f7UL, 0xe4e4e4e4UL, 0x58585858UL, 0x05050505UL, + 0xb8b8b8b8UL, 0xb3b3b3b3UL, 0x45454545UL, 0x06060606UL, + 0xd0d0d0d0UL, 0x2c2c2c2cUL, 0x1e1e1e1eUL, 0x8f8f8f8fUL, + 0xcacacacaUL, 0x3f3f3f3fUL, 0x0f0f0f0fUL, 0x02020202UL, + 0xc1c1c1c1UL, 0xafafafafUL, 0xbdbdbdbdUL, 0x03030303UL, + 0x01010101UL, 0x13131313UL, 0x8a8a8a8aUL, 0x6b6b6b6bUL, + 0x3a3a3a3aUL, 0x91919191UL, 0x11111111UL, 0x41414141UL, + 0x4f4f4f4fUL, 0x67676767UL, 0xdcdcdcdcUL, 0xeaeaeaeaUL, + 0x97979797UL, 0xf2f2f2f2UL, 0xcfcfcfcfUL, 0xcecececeUL, + 0xf0f0f0f0UL, 0xb4b4b4b4UL, 0xe6e6e6e6UL, 0x73737373UL, + 0x96969696UL, 0xacacacacUL, 0x74747474UL, 0x22222222UL, + 0xe7e7e7e7UL, 0xadadadadUL, 0x35353535UL, 0x85858585UL, + 0xe2e2e2e2UL, 0xf9f9f9f9UL, 0x37373737UL, 0xe8e8e8e8UL, + 0x1c1c1c1cUL, 0x75757575UL, 0xdfdfdfdfUL, 0x6e6e6e6eUL, + 0x47474747UL, 0xf1f1f1f1UL, 0x1a1a1a1aUL, 0x71717171UL, + 0x1d1d1d1dUL, 0x29292929UL, 0xc5c5c5c5UL, 0x89898989UL, + 0x6f6f6f6fUL, 0xb7b7b7b7UL, 0x62626262UL, 0x0e0e0e0eUL, + 0xaaaaaaaaUL, 0x18181818UL, 0xbebebebeUL, 0x1b1b1b1bUL, + 0xfcfcfcfcUL, 0x56565656UL, 0x3e3e3e3eUL, 0x4b4b4b4bUL, + 0xc6c6c6c6UL, 0xd2d2d2d2UL, 0x79797979UL, 0x20202020UL, + 0x9a9a9a9aUL, 0xdbdbdbdbUL, 0xc0c0c0c0UL, 0xfefefefeUL, + 0x78787878UL, 0xcdcdcdcdUL, 0x5a5a5a5aUL, 0xf4f4f4f4UL, + 0x1f1f1f1fUL, 0xddddddddUL, 0xa8a8a8a8UL, 0x33333333UL, + 0x88888888UL, 0x07070707UL, 0xc7c7c7c7UL, 0x31313131UL, + 0xb1b1b1b1UL, 0x12121212UL, 0x10101010UL, 0x59595959UL, + 0x27272727UL, 0x80808080UL, 0xececececUL, 0x5f5f5f5fUL, + 0x60606060UL, 0x51515151UL, 0x7f7f7f7fUL, 0xa9a9a9a9UL, + 0x19191919UL, 0xb5b5b5b5UL, 0x4a4a4a4aUL, 0x0d0d0d0dUL, + 0x2d2d2d2dUL, 0xe5e5e5e5UL, 0x7a7a7a7aUL, 0x9f9f9f9fUL, + 0x93939393UL, 0xc9c9c9c9UL, 0x9c9c9c9cUL, 0xefefefefUL, + 0xa0a0a0a0UL, 0xe0e0e0e0UL, 0x3b3b3b3bUL, 0x4d4d4d4dUL, + 0xaeaeaeaeUL, 0x2a2a2a2aUL, 0xf5f5f5f5UL, 0xb0b0b0b0UL, + 0xc8c8c8c8UL, 0xebebebebUL, 0xbbbbbbbbUL, 0x3c3c3c3cUL, + 0x83838383UL, 0x53535353UL, 0x99999999UL, 0x61616161UL, + 0x17171717UL, 0x2b2b2b2bUL, 0x04040404UL, 0x7e7e7e7eUL, + 0xbabababaUL, 0x77777777UL, 0xd6d6d6d6UL, 0x26262626UL, + 0xe1e1e1e1UL, 0x69696969UL, 0x14141414UL, 0x63636363UL, + 0x55555555UL, 0x21212121UL, 0x0c0c0c0cUL, 0x7d7d7d7dUL, +}; + +#endif /* ENCRYPT_ONLY */ + +#ifdef LTC_SMALL_CODE + +#define Te0(x) TE0[x] +#define Te1(x) RORc(TE0[x], 8) +#define Te2(x) RORc(TE0[x], 16) +#define Te3(x) RORc(TE0[x], 24) + +#define Td0(x) TD0[x] +#define Td1(x) RORc(TD0[x], 8) +#define Td2(x) RORc(TD0[x], 16) +#define Td3(x) RORc(TD0[x], 24) + +#define Te4_0 0x000000FF & Te4 +#define Te4_1 0x0000FF00 & Te4 +#define Te4_2 0x00FF0000 & Te4 +#define Te4_3 0xFF000000 & Te4 + +#else + +#define Te0(x) TE0[x] +#define Te1(x) TE1[x] +#define Te2(x) TE2[x] +#define Te3(x) TE3[x] + +#define Td0(x) TD0[x] +#define Td1(x) TD1[x] +#define Td2(x) TD2[x] +#define Td3(x) TD3[x] + +static const ulong32 TE1[256] = { + 0xa5c66363UL, 0x84f87c7cUL, 0x99ee7777UL, 0x8df67b7bUL, + 0x0dfff2f2UL, 0xbdd66b6bUL, 0xb1de6f6fUL, 0x5491c5c5UL, + 0x50603030UL, 0x03020101UL, 0xa9ce6767UL, 0x7d562b2bUL, + 0x19e7fefeUL, 0x62b5d7d7UL, 0xe64dababUL, 0x9aec7676UL, + 0x458fcacaUL, 0x9d1f8282UL, 0x4089c9c9UL, 0x87fa7d7dUL, + 0x15effafaUL, 0xebb25959UL, 0xc98e4747UL, 0x0bfbf0f0UL, + 0xec41adadUL, 0x67b3d4d4UL, 0xfd5fa2a2UL, 0xea45afafUL, + 0xbf239c9cUL, 0xf753a4a4UL, 0x96e47272UL, 0x5b9bc0c0UL, + 0xc275b7b7UL, 0x1ce1fdfdUL, 0xae3d9393UL, 0x6a4c2626UL, + 0x5a6c3636UL, 0x417e3f3fUL, 0x02f5f7f7UL, 0x4f83ccccUL, + 0x5c683434UL, 0xf451a5a5UL, 0x34d1e5e5UL, 0x08f9f1f1UL, + 0x93e27171UL, 0x73abd8d8UL, 0x53623131UL, 0x3f2a1515UL, + 0x0c080404UL, 0x5295c7c7UL, 0x65462323UL, 0x5e9dc3c3UL, + 0x28301818UL, 0xa1379696UL, 0x0f0a0505UL, 0xb52f9a9aUL, + 0x090e0707UL, 0x36241212UL, 0x9b1b8080UL, 0x3ddfe2e2UL, + 0x26cdebebUL, 0x694e2727UL, 0xcd7fb2b2UL, 0x9fea7575UL, + 0x1b120909UL, 0x9e1d8383UL, 0x74582c2cUL, 0x2e341a1aUL, + 0x2d361b1bUL, 0xb2dc6e6eUL, 0xeeb45a5aUL, 0xfb5ba0a0UL, + 0xf6a45252UL, 0x4d763b3bUL, 0x61b7d6d6UL, 0xce7db3b3UL, + 0x7b522929UL, 0x3edde3e3UL, 0x715e2f2fUL, 0x97138484UL, + 0xf5a65353UL, 0x68b9d1d1UL, 0x00000000UL, 0x2cc1ededUL, + 0x60402020UL, 0x1fe3fcfcUL, 0xc879b1b1UL, 0xedb65b5bUL, + 0xbed46a6aUL, 0x468dcbcbUL, 0xd967bebeUL, 0x4b723939UL, + 0xde944a4aUL, 0xd4984c4cUL, 0xe8b05858UL, 0x4a85cfcfUL, + 0x6bbbd0d0UL, 0x2ac5efefUL, 0xe54faaaaUL, 0x16edfbfbUL, + 0xc5864343UL, 0xd79a4d4dUL, 0x55663333UL, 0x94118585UL, + 0xcf8a4545UL, 0x10e9f9f9UL, 0x06040202UL, 0x81fe7f7fUL, + 0xf0a05050UL, 0x44783c3cUL, 0xba259f9fUL, 0xe34ba8a8UL, + 0xf3a25151UL, 0xfe5da3a3UL, 0xc0804040UL, 0x8a058f8fUL, + 0xad3f9292UL, 0xbc219d9dUL, 0x48703838UL, 0x04f1f5f5UL, + 0xdf63bcbcUL, 0xc177b6b6UL, 0x75afdadaUL, 0x63422121UL, + 0x30201010UL, 0x1ae5ffffUL, 0x0efdf3f3UL, 0x6dbfd2d2UL, + 0x4c81cdcdUL, 0x14180c0cUL, 0x35261313UL, 0x2fc3ececUL, + 0xe1be5f5fUL, 0xa2359797UL, 0xcc884444UL, 0x392e1717UL, + 0x5793c4c4UL, 0xf255a7a7UL, 0x82fc7e7eUL, 0x477a3d3dUL, + 0xacc86464UL, 0xe7ba5d5dUL, 0x2b321919UL, 0x95e67373UL, + 0xa0c06060UL, 0x98198181UL, 0xd19e4f4fUL, 0x7fa3dcdcUL, + 0x66442222UL, 0x7e542a2aUL, 0xab3b9090UL, 0x830b8888UL, + 0xca8c4646UL, 0x29c7eeeeUL, 0xd36bb8b8UL, 0x3c281414UL, + 0x79a7dedeUL, 0xe2bc5e5eUL, 0x1d160b0bUL, 0x76addbdbUL, + 0x3bdbe0e0UL, 0x56643232UL, 0x4e743a3aUL, 0x1e140a0aUL, + 0xdb924949UL, 0x0a0c0606UL, 0x6c482424UL, 0xe4b85c5cUL, + 0x5d9fc2c2UL, 0x6ebdd3d3UL, 0xef43acacUL, 0xa6c46262UL, + 0xa8399191UL, 0xa4319595UL, 0x37d3e4e4UL, 0x8bf27979UL, + 0x32d5e7e7UL, 0x438bc8c8UL, 0x596e3737UL, 0xb7da6d6dUL, + 0x8c018d8dUL, 0x64b1d5d5UL, 0xd29c4e4eUL, 0xe049a9a9UL, + 0xb4d86c6cUL, 0xfaac5656UL, 0x07f3f4f4UL, 0x25cfeaeaUL, + 0xafca6565UL, 0x8ef47a7aUL, 0xe947aeaeUL, 0x18100808UL, + 0xd56fbabaUL, 0x88f07878UL, 0x6f4a2525UL, 0x725c2e2eUL, + 0x24381c1cUL, 0xf157a6a6UL, 0xc773b4b4UL, 0x5197c6c6UL, + 0x23cbe8e8UL, 0x7ca1ddddUL, 0x9ce87474UL, 0x213e1f1fUL, + 0xdd964b4bUL, 0xdc61bdbdUL, 0x860d8b8bUL, 0x850f8a8aUL, + 0x90e07070UL, 0x427c3e3eUL, 0xc471b5b5UL, 0xaacc6666UL, + 0xd8904848UL, 0x05060303UL, 0x01f7f6f6UL, 0x121c0e0eUL, + 0xa3c26161UL, 0x5f6a3535UL, 0xf9ae5757UL, 0xd069b9b9UL, + 0x91178686UL, 0x5899c1c1UL, 0x273a1d1dUL, 0xb9279e9eUL, + 0x38d9e1e1UL, 0x13ebf8f8UL, 0xb32b9898UL, 0x33221111UL, + 0xbbd26969UL, 0x70a9d9d9UL, 0x89078e8eUL, 0xa7339494UL, + 0xb62d9b9bUL, 0x223c1e1eUL, 0x92158787UL, 0x20c9e9e9UL, + 0x4987ceceUL, 0xffaa5555UL, 0x78502828UL, 0x7aa5dfdfUL, + 0x8f038c8cUL, 0xf859a1a1UL, 0x80098989UL, 0x171a0d0dUL, + 0xda65bfbfUL, 0x31d7e6e6UL, 0xc6844242UL, 0xb8d06868UL, + 0xc3824141UL, 0xb0299999UL, 0x775a2d2dUL, 0x111e0f0fUL, + 0xcb7bb0b0UL, 0xfca85454UL, 0xd66dbbbbUL, 0x3a2c1616UL, +}; +static const ulong32 TE2[256] = { + 0x63a5c663UL, 0x7c84f87cUL, 0x7799ee77UL, 0x7b8df67bUL, + 0xf20dfff2UL, 0x6bbdd66bUL, 0x6fb1de6fUL, 0xc55491c5UL, + 0x30506030UL, 0x01030201UL, 0x67a9ce67UL, 0x2b7d562bUL, + 0xfe19e7feUL, 0xd762b5d7UL, 0xabe64dabUL, 0x769aec76UL, + 0xca458fcaUL, 0x829d1f82UL, 0xc94089c9UL, 0x7d87fa7dUL, + 0xfa15effaUL, 0x59ebb259UL, 0x47c98e47UL, 0xf00bfbf0UL, + 0xadec41adUL, 0xd467b3d4UL, 0xa2fd5fa2UL, 0xafea45afUL, + 0x9cbf239cUL, 0xa4f753a4UL, 0x7296e472UL, 0xc05b9bc0UL, + 0xb7c275b7UL, 0xfd1ce1fdUL, 0x93ae3d93UL, 0x266a4c26UL, + 0x365a6c36UL, 0x3f417e3fUL, 0xf702f5f7UL, 0xcc4f83ccUL, + 0x345c6834UL, 0xa5f451a5UL, 0xe534d1e5UL, 0xf108f9f1UL, + 0x7193e271UL, 0xd873abd8UL, 0x31536231UL, 0x153f2a15UL, + 0x040c0804UL, 0xc75295c7UL, 0x23654623UL, 0xc35e9dc3UL, + 0x18283018UL, 0x96a13796UL, 0x050f0a05UL, 0x9ab52f9aUL, + 0x07090e07UL, 0x12362412UL, 0x809b1b80UL, 0xe23ddfe2UL, + 0xeb26cdebUL, 0x27694e27UL, 0xb2cd7fb2UL, 0x759fea75UL, + 0x091b1209UL, 0x839e1d83UL, 0x2c74582cUL, 0x1a2e341aUL, + 0x1b2d361bUL, 0x6eb2dc6eUL, 0x5aeeb45aUL, 0xa0fb5ba0UL, + 0x52f6a452UL, 0x3b4d763bUL, 0xd661b7d6UL, 0xb3ce7db3UL, + 0x297b5229UL, 0xe33edde3UL, 0x2f715e2fUL, 0x84971384UL, + 0x53f5a653UL, 0xd168b9d1UL, 0x00000000UL, 0xed2cc1edUL, + 0x20604020UL, 0xfc1fe3fcUL, 0xb1c879b1UL, 0x5bedb65bUL, + 0x6abed46aUL, 0xcb468dcbUL, 0xbed967beUL, 0x394b7239UL, + 0x4ade944aUL, 0x4cd4984cUL, 0x58e8b058UL, 0xcf4a85cfUL, + 0xd06bbbd0UL, 0xef2ac5efUL, 0xaae54faaUL, 0xfb16edfbUL, + 0x43c58643UL, 0x4dd79a4dUL, 0x33556633UL, 0x85941185UL, + 0x45cf8a45UL, 0xf910e9f9UL, 0x02060402UL, 0x7f81fe7fUL, + 0x50f0a050UL, 0x3c44783cUL, 0x9fba259fUL, 0xa8e34ba8UL, + 0x51f3a251UL, 0xa3fe5da3UL, 0x40c08040UL, 0x8f8a058fUL, + 0x92ad3f92UL, 0x9dbc219dUL, 0x38487038UL, 0xf504f1f5UL, + 0xbcdf63bcUL, 0xb6c177b6UL, 0xda75afdaUL, 0x21634221UL, + 0x10302010UL, 0xff1ae5ffUL, 0xf30efdf3UL, 0xd26dbfd2UL, + 0xcd4c81cdUL, 0x0c14180cUL, 0x13352613UL, 0xec2fc3ecUL, + 0x5fe1be5fUL, 0x97a23597UL, 0x44cc8844UL, 0x17392e17UL, + 0xc45793c4UL, 0xa7f255a7UL, 0x7e82fc7eUL, 0x3d477a3dUL, + 0x64acc864UL, 0x5de7ba5dUL, 0x192b3219UL, 0x7395e673UL, + 0x60a0c060UL, 0x81981981UL, 0x4fd19e4fUL, 0xdc7fa3dcUL, + 0x22664422UL, 0x2a7e542aUL, 0x90ab3b90UL, 0x88830b88UL, + 0x46ca8c46UL, 0xee29c7eeUL, 0xb8d36bb8UL, 0x143c2814UL, + 0xde79a7deUL, 0x5ee2bc5eUL, 0x0b1d160bUL, 0xdb76addbUL, + 0xe03bdbe0UL, 0x32566432UL, 0x3a4e743aUL, 0x0a1e140aUL, + 0x49db9249UL, 0x060a0c06UL, 0x246c4824UL, 0x5ce4b85cUL, + 0xc25d9fc2UL, 0xd36ebdd3UL, 0xacef43acUL, 0x62a6c462UL, + 0x91a83991UL, 0x95a43195UL, 0xe437d3e4UL, 0x798bf279UL, + 0xe732d5e7UL, 0xc8438bc8UL, 0x37596e37UL, 0x6db7da6dUL, + 0x8d8c018dUL, 0xd564b1d5UL, 0x4ed29c4eUL, 0xa9e049a9UL, + 0x6cb4d86cUL, 0x56faac56UL, 0xf407f3f4UL, 0xea25cfeaUL, + 0x65afca65UL, 0x7a8ef47aUL, 0xaee947aeUL, 0x08181008UL, + 0xbad56fbaUL, 0x7888f078UL, 0x256f4a25UL, 0x2e725c2eUL, + 0x1c24381cUL, 0xa6f157a6UL, 0xb4c773b4UL, 0xc65197c6UL, + 0xe823cbe8UL, 0xdd7ca1ddUL, 0x749ce874UL, 0x1f213e1fUL, + 0x4bdd964bUL, 0xbddc61bdUL, 0x8b860d8bUL, 0x8a850f8aUL, + 0x7090e070UL, 0x3e427c3eUL, 0xb5c471b5UL, 0x66aacc66UL, + 0x48d89048UL, 0x03050603UL, 0xf601f7f6UL, 0x0e121c0eUL, + 0x61a3c261UL, 0x355f6a35UL, 0x57f9ae57UL, 0xb9d069b9UL, + 0x86911786UL, 0xc15899c1UL, 0x1d273a1dUL, 0x9eb9279eUL, + 0xe138d9e1UL, 0xf813ebf8UL, 0x98b32b98UL, 0x11332211UL, + 0x69bbd269UL, 0xd970a9d9UL, 0x8e89078eUL, 0x94a73394UL, + 0x9bb62d9bUL, 0x1e223c1eUL, 0x87921587UL, 0xe920c9e9UL, + 0xce4987ceUL, 0x55ffaa55UL, 0x28785028UL, 0xdf7aa5dfUL, + 0x8c8f038cUL, 0xa1f859a1UL, 0x89800989UL, 0x0d171a0dUL, + 0xbfda65bfUL, 0xe631d7e6UL, 0x42c68442UL, 0x68b8d068UL, + 0x41c38241UL, 0x99b02999UL, 0x2d775a2dUL, 0x0f111e0fUL, + 0xb0cb7bb0UL, 0x54fca854UL, 0xbbd66dbbUL, 0x163a2c16UL, +}; +static const ulong32 TE3[256] = { + + 0x6363a5c6UL, 0x7c7c84f8UL, 0x777799eeUL, 0x7b7b8df6UL, + 0xf2f20dffUL, 0x6b6bbdd6UL, 0x6f6fb1deUL, 0xc5c55491UL, + 0x30305060UL, 0x01010302UL, 0x6767a9ceUL, 0x2b2b7d56UL, + 0xfefe19e7UL, 0xd7d762b5UL, 0xababe64dUL, 0x76769aecUL, + 0xcaca458fUL, 0x82829d1fUL, 0xc9c94089UL, 0x7d7d87faUL, + 0xfafa15efUL, 0x5959ebb2UL, 0x4747c98eUL, 0xf0f00bfbUL, + 0xadadec41UL, 0xd4d467b3UL, 0xa2a2fd5fUL, 0xafafea45UL, + 0x9c9cbf23UL, 0xa4a4f753UL, 0x727296e4UL, 0xc0c05b9bUL, + 0xb7b7c275UL, 0xfdfd1ce1UL, 0x9393ae3dUL, 0x26266a4cUL, + 0x36365a6cUL, 0x3f3f417eUL, 0xf7f702f5UL, 0xcccc4f83UL, + 0x34345c68UL, 0xa5a5f451UL, 0xe5e534d1UL, 0xf1f108f9UL, + 0x717193e2UL, 0xd8d873abUL, 0x31315362UL, 0x15153f2aUL, + 0x04040c08UL, 0xc7c75295UL, 0x23236546UL, 0xc3c35e9dUL, + 0x18182830UL, 0x9696a137UL, 0x05050f0aUL, 0x9a9ab52fUL, + 0x0707090eUL, 0x12123624UL, 0x80809b1bUL, 0xe2e23ddfUL, + 0xebeb26cdUL, 0x2727694eUL, 0xb2b2cd7fUL, 0x75759feaUL, + 0x09091b12UL, 0x83839e1dUL, 0x2c2c7458UL, 0x1a1a2e34UL, + 0x1b1b2d36UL, 0x6e6eb2dcUL, 0x5a5aeeb4UL, 0xa0a0fb5bUL, + 0x5252f6a4UL, 0x3b3b4d76UL, 0xd6d661b7UL, 0xb3b3ce7dUL, + 0x29297b52UL, 0xe3e33eddUL, 0x2f2f715eUL, 0x84849713UL, + 0x5353f5a6UL, 0xd1d168b9UL, 0x00000000UL, 0xeded2cc1UL, + 0x20206040UL, 0xfcfc1fe3UL, 0xb1b1c879UL, 0x5b5bedb6UL, + 0x6a6abed4UL, 0xcbcb468dUL, 0xbebed967UL, 0x39394b72UL, + 0x4a4ade94UL, 0x4c4cd498UL, 0x5858e8b0UL, 0xcfcf4a85UL, + 0xd0d06bbbUL, 0xefef2ac5UL, 0xaaaae54fUL, 0xfbfb16edUL, + 0x4343c586UL, 0x4d4dd79aUL, 0x33335566UL, 0x85859411UL, + 0x4545cf8aUL, 0xf9f910e9UL, 0x02020604UL, 0x7f7f81feUL, + 0x5050f0a0UL, 0x3c3c4478UL, 0x9f9fba25UL, 0xa8a8e34bUL, + 0x5151f3a2UL, 0xa3a3fe5dUL, 0x4040c080UL, 0x8f8f8a05UL, + 0x9292ad3fUL, 0x9d9dbc21UL, 0x38384870UL, 0xf5f504f1UL, + 0xbcbcdf63UL, 0xb6b6c177UL, 0xdada75afUL, 0x21216342UL, + 0x10103020UL, 0xffff1ae5UL, 0xf3f30efdUL, 0xd2d26dbfUL, + 0xcdcd4c81UL, 0x0c0c1418UL, 0x13133526UL, 0xecec2fc3UL, + 0x5f5fe1beUL, 0x9797a235UL, 0x4444cc88UL, 0x1717392eUL, + 0xc4c45793UL, 0xa7a7f255UL, 0x7e7e82fcUL, 0x3d3d477aUL, + 0x6464acc8UL, 0x5d5de7baUL, 0x19192b32UL, 0x737395e6UL, + 0x6060a0c0UL, 0x81819819UL, 0x4f4fd19eUL, 0xdcdc7fa3UL, + 0x22226644UL, 0x2a2a7e54UL, 0x9090ab3bUL, 0x8888830bUL, + 0x4646ca8cUL, 0xeeee29c7UL, 0xb8b8d36bUL, 0x14143c28UL, + 0xdede79a7UL, 0x5e5ee2bcUL, 0x0b0b1d16UL, 0xdbdb76adUL, + 0xe0e03bdbUL, 0x32325664UL, 0x3a3a4e74UL, 0x0a0a1e14UL, + 0x4949db92UL, 0x06060a0cUL, 0x24246c48UL, 0x5c5ce4b8UL, + 0xc2c25d9fUL, 0xd3d36ebdUL, 0xacacef43UL, 0x6262a6c4UL, + 0x9191a839UL, 0x9595a431UL, 0xe4e437d3UL, 0x79798bf2UL, + 0xe7e732d5UL, 0xc8c8438bUL, 0x3737596eUL, 0x6d6db7daUL, + 0x8d8d8c01UL, 0xd5d564b1UL, 0x4e4ed29cUL, 0xa9a9e049UL, + 0x6c6cb4d8UL, 0x5656faacUL, 0xf4f407f3UL, 0xeaea25cfUL, + 0x6565afcaUL, 0x7a7a8ef4UL, 0xaeaee947UL, 0x08081810UL, + 0xbabad56fUL, 0x787888f0UL, 0x25256f4aUL, 0x2e2e725cUL, + 0x1c1c2438UL, 0xa6a6f157UL, 0xb4b4c773UL, 0xc6c65197UL, + 0xe8e823cbUL, 0xdddd7ca1UL, 0x74749ce8UL, 0x1f1f213eUL, + 0x4b4bdd96UL, 0xbdbddc61UL, 0x8b8b860dUL, 0x8a8a850fUL, + 0x707090e0UL, 0x3e3e427cUL, 0xb5b5c471UL, 0x6666aaccUL, + 0x4848d890UL, 0x03030506UL, 0xf6f601f7UL, 0x0e0e121cUL, + 0x6161a3c2UL, 0x35355f6aUL, 0x5757f9aeUL, 0xb9b9d069UL, + 0x86869117UL, 0xc1c15899UL, 0x1d1d273aUL, 0x9e9eb927UL, + 0xe1e138d9UL, 0xf8f813ebUL, 0x9898b32bUL, 0x11113322UL, + 0x6969bbd2UL, 0xd9d970a9UL, 0x8e8e8907UL, 0x9494a733UL, + 0x9b9bb62dUL, 0x1e1e223cUL, 0x87879215UL, 0xe9e920c9UL, + 0xcece4987UL, 0x5555ffaaUL, 0x28287850UL, 0xdfdf7aa5UL, + 0x8c8c8f03UL, 0xa1a1f859UL, 0x89898009UL, 0x0d0d171aUL, + 0xbfbfda65UL, 0xe6e631d7UL, 0x4242c684UL, 0x6868b8d0UL, + 0x4141c382UL, 0x9999b029UL, 0x2d2d775aUL, 0x0f0f111eUL, + 0xb0b0cb7bUL, 0x5454fca8UL, 0xbbbbd66dUL, 0x16163a2cUL, +}; + +#ifndef PELI_TAB +static const ulong32 Te4_0[] = { +0x00000063UL, 0x0000007cUL, 0x00000077UL, 0x0000007bUL, 0x000000f2UL, 0x0000006bUL, 0x0000006fUL, 0x000000c5UL, +0x00000030UL, 0x00000001UL, 0x00000067UL, 0x0000002bUL, 0x000000feUL, 0x000000d7UL, 0x000000abUL, 0x00000076UL, +0x000000caUL, 0x00000082UL, 0x000000c9UL, 0x0000007dUL, 0x000000faUL, 0x00000059UL, 0x00000047UL, 0x000000f0UL, +0x000000adUL, 0x000000d4UL, 0x000000a2UL, 0x000000afUL, 0x0000009cUL, 0x000000a4UL, 0x00000072UL, 0x000000c0UL, +0x000000b7UL, 0x000000fdUL, 0x00000093UL, 0x00000026UL, 0x00000036UL, 0x0000003fUL, 0x000000f7UL, 0x000000ccUL, +0x00000034UL, 0x000000a5UL, 0x000000e5UL, 0x000000f1UL, 0x00000071UL, 0x000000d8UL, 0x00000031UL, 0x00000015UL, +0x00000004UL, 0x000000c7UL, 0x00000023UL, 0x000000c3UL, 0x00000018UL, 0x00000096UL, 0x00000005UL, 0x0000009aUL, +0x00000007UL, 0x00000012UL, 0x00000080UL, 0x000000e2UL, 0x000000ebUL, 0x00000027UL, 0x000000b2UL, 0x00000075UL, +0x00000009UL, 0x00000083UL, 0x0000002cUL, 0x0000001aUL, 0x0000001bUL, 0x0000006eUL, 0x0000005aUL, 0x000000a0UL, +0x00000052UL, 0x0000003bUL, 0x000000d6UL, 0x000000b3UL, 0x00000029UL, 0x000000e3UL, 0x0000002fUL, 0x00000084UL, +0x00000053UL, 0x000000d1UL, 0x00000000UL, 0x000000edUL, 0x00000020UL, 0x000000fcUL, 0x000000b1UL, 0x0000005bUL, +0x0000006aUL, 0x000000cbUL, 0x000000beUL, 0x00000039UL, 0x0000004aUL, 0x0000004cUL, 0x00000058UL, 0x000000cfUL, +0x000000d0UL, 0x000000efUL, 0x000000aaUL, 0x000000fbUL, 0x00000043UL, 0x0000004dUL, 0x00000033UL, 0x00000085UL, +0x00000045UL, 0x000000f9UL, 0x00000002UL, 0x0000007fUL, 0x00000050UL, 0x0000003cUL, 0x0000009fUL, 0x000000a8UL, +0x00000051UL, 0x000000a3UL, 0x00000040UL, 0x0000008fUL, 0x00000092UL, 0x0000009dUL, 0x00000038UL, 0x000000f5UL, +0x000000bcUL, 0x000000b6UL, 0x000000daUL, 0x00000021UL, 0x00000010UL, 0x000000ffUL, 0x000000f3UL, 0x000000d2UL, +0x000000cdUL, 0x0000000cUL, 0x00000013UL, 0x000000ecUL, 0x0000005fUL, 0x00000097UL, 0x00000044UL, 0x00000017UL, +0x000000c4UL, 0x000000a7UL, 0x0000007eUL, 0x0000003dUL, 0x00000064UL, 0x0000005dUL, 0x00000019UL, 0x00000073UL, +0x00000060UL, 0x00000081UL, 0x0000004fUL, 0x000000dcUL, 0x00000022UL, 0x0000002aUL, 0x00000090UL, 0x00000088UL, +0x00000046UL, 0x000000eeUL, 0x000000b8UL, 0x00000014UL, 0x000000deUL, 0x0000005eUL, 0x0000000bUL, 0x000000dbUL, +0x000000e0UL, 0x00000032UL, 0x0000003aUL, 0x0000000aUL, 0x00000049UL, 0x00000006UL, 0x00000024UL, 0x0000005cUL, +0x000000c2UL, 0x000000d3UL, 0x000000acUL, 0x00000062UL, 0x00000091UL, 0x00000095UL, 0x000000e4UL, 0x00000079UL, +0x000000e7UL, 0x000000c8UL, 0x00000037UL, 0x0000006dUL, 0x0000008dUL, 0x000000d5UL, 0x0000004eUL, 0x000000a9UL, +0x0000006cUL, 0x00000056UL, 0x000000f4UL, 0x000000eaUL, 0x00000065UL, 0x0000007aUL, 0x000000aeUL, 0x00000008UL, +0x000000baUL, 0x00000078UL, 0x00000025UL, 0x0000002eUL, 0x0000001cUL, 0x000000a6UL, 0x000000b4UL, 0x000000c6UL, +0x000000e8UL, 0x000000ddUL, 0x00000074UL, 0x0000001fUL, 0x0000004bUL, 0x000000bdUL, 0x0000008bUL, 0x0000008aUL, +0x00000070UL, 0x0000003eUL, 0x000000b5UL, 0x00000066UL, 0x00000048UL, 0x00000003UL, 0x000000f6UL, 0x0000000eUL, +0x00000061UL, 0x00000035UL, 0x00000057UL, 0x000000b9UL, 0x00000086UL, 0x000000c1UL, 0x0000001dUL, 0x0000009eUL, +0x000000e1UL, 0x000000f8UL, 0x00000098UL, 0x00000011UL, 0x00000069UL, 0x000000d9UL, 0x0000008eUL, 0x00000094UL, +0x0000009bUL, 0x0000001eUL, 0x00000087UL, 0x000000e9UL, 0x000000ceUL, 0x00000055UL, 0x00000028UL, 0x000000dfUL, +0x0000008cUL, 0x000000a1UL, 0x00000089UL, 0x0000000dUL, 0x000000bfUL, 0x000000e6UL, 0x00000042UL, 0x00000068UL, +0x00000041UL, 0x00000099UL, 0x0000002dUL, 0x0000000fUL, 0x000000b0UL, 0x00000054UL, 0x000000bbUL, 0x00000016UL +}; + +static const ulong32 Te4_1[] = { +0x00006300UL, 0x00007c00UL, 0x00007700UL, 0x00007b00UL, 0x0000f200UL, 0x00006b00UL, 0x00006f00UL, 0x0000c500UL, +0x00003000UL, 0x00000100UL, 0x00006700UL, 0x00002b00UL, 0x0000fe00UL, 0x0000d700UL, 0x0000ab00UL, 0x00007600UL, +0x0000ca00UL, 0x00008200UL, 0x0000c900UL, 0x00007d00UL, 0x0000fa00UL, 0x00005900UL, 0x00004700UL, 0x0000f000UL, +0x0000ad00UL, 0x0000d400UL, 0x0000a200UL, 0x0000af00UL, 0x00009c00UL, 0x0000a400UL, 0x00007200UL, 0x0000c000UL, +0x0000b700UL, 0x0000fd00UL, 0x00009300UL, 0x00002600UL, 0x00003600UL, 0x00003f00UL, 0x0000f700UL, 0x0000cc00UL, +0x00003400UL, 0x0000a500UL, 0x0000e500UL, 0x0000f100UL, 0x00007100UL, 0x0000d800UL, 0x00003100UL, 0x00001500UL, +0x00000400UL, 0x0000c700UL, 0x00002300UL, 0x0000c300UL, 0x00001800UL, 0x00009600UL, 0x00000500UL, 0x00009a00UL, +0x00000700UL, 0x00001200UL, 0x00008000UL, 0x0000e200UL, 0x0000eb00UL, 0x00002700UL, 0x0000b200UL, 0x00007500UL, +0x00000900UL, 0x00008300UL, 0x00002c00UL, 0x00001a00UL, 0x00001b00UL, 0x00006e00UL, 0x00005a00UL, 0x0000a000UL, +0x00005200UL, 0x00003b00UL, 0x0000d600UL, 0x0000b300UL, 0x00002900UL, 0x0000e300UL, 0x00002f00UL, 0x00008400UL, +0x00005300UL, 0x0000d100UL, 0x00000000UL, 0x0000ed00UL, 0x00002000UL, 0x0000fc00UL, 0x0000b100UL, 0x00005b00UL, +0x00006a00UL, 0x0000cb00UL, 0x0000be00UL, 0x00003900UL, 0x00004a00UL, 0x00004c00UL, 0x00005800UL, 0x0000cf00UL, +0x0000d000UL, 0x0000ef00UL, 0x0000aa00UL, 0x0000fb00UL, 0x00004300UL, 0x00004d00UL, 0x00003300UL, 0x00008500UL, +0x00004500UL, 0x0000f900UL, 0x00000200UL, 0x00007f00UL, 0x00005000UL, 0x00003c00UL, 0x00009f00UL, 0x0000a800UL, +0x00005100UL, 0x0000a300UL, 0x00004000UL, 0x00008f00UL, 0x00009200UL, 0x00009d00UL, 0x00003800UL, 0x0000f500UL, +0x0000bc00UL, 0x0000b600UL, 0x0000da00UL, 0x00002100UL, 0x00001000UL, 0x0000ff00UL, 0x0000f300UL, 0x0000d200UL, +0x0000cd00UL, 0x00000c00UL, 0x00001300UL, 0x0000ec00UL, 0x00005f00UL, 0x00009700UL, 0x00004400UL, 0x00001700UL, +0x0000c400UL, 0x0000a700UL, 0x00007e00UL, 0x00003d00UL, 0x00006400UL, 0x00005d00UL, 0x00001900UL, 0x00007300UL, +0x00006000UL, 0x00008100UL, 0x00004f00UL, 0x0000dc00UL, 0x00002200UL, 0x00002a00UL, 0x00009000UL, 0x00008800UL, +0x00004600UL, 0x0000ee00UL, 0x0000b800UL, 0x00001400UL, 0x0000de00UL, 0x00005e00UL, 0x00000b00UL, 0x0000db00UL, +0x0000e000UL, 0x00003200UL, 0x00003a00UL, 0x00000a00UL, 0x00004900UL, 0x00000600UL, 0x00002400UL, 0x00005c00UL, +0x0000c200UL, 0x0000d300UL, 0x0000ac00UL, 0x00006200UL, 0x00009100UL, 0x00009500UL, 0x0000e400UL, 0x00007900UL, +0x0000e700UL, 0x0000c800UL, 0x00003700UL, 0x00006d00UL, 0x00008d00UL, 0x0000d500UL, 0x00004e00UL, 0x0000a900UL, +0x00006c00UL, 0x00005600UL, 0x0000f400UL, 0x0000ea00UL, 0x00006500UL, 0x00007a00UL, 0x0000ae00UL, 0x00000800UL, +0x0000ba00UL, 0x00007800UL, 0x00002500UL, 0x00002e00UL, 0x00001c00UL, 0x0000a600UL, 0x0000b400UL, 0x0000c600UL, +0x0000e800UL, 0x0000dd00UL, 0x00007400UL, 0x00001f00UL, 0x00004b00UL, 0x0000bd00UL, 0x00008b00UL, 0x00008a00UL, +0x00007000UL, 0x00003e00UL, 0x0000b500UL, 0x00006600UL, 0x00004800UL, 0x00000300UL, 0x0000f600UL, 0x00000e00UL, +0x00006100UL, 0x00003500UL, 0x00005700UL, 0x0000b900UL, 0x00008600UL, 0x0000c100UL, 0x00001d00UL, 0x00009e00UL, +0x0000e100UL, 0x0000f800UL, 0x00009800UL, 0x00001100UL, 0x00006900UL, 0x0000d900UL, 0x00008e00UL, 0x00009400UL, +0x00009b00UL, 0x00001e00UL, 0x00008700UL, 0x0000e900UL, 0x0000ce00UL, 0x00005500UL, 0x00002800UL, 0x0000df00UL, +0x00008c00UL, 0x0000a100UL, 0x00008900UL, 0x00000d00UL, 0x0000bf00UL, 0x0000e600UL, 0x00004200UL, 0x00006800UL, +0x00004100UL, 0x00009900UL, 0x00002d00UL, 0x00000f00UL, 0x0000b000UL, 0x00005400UL, 0x0000bb00UL, 0x00001600UL +}; + +static const ulong32 Te4_2[] = { +0x00630000UL, 0x007c0000UL, 0x00770000UL, 0x007b0000UL, 0x00f20000UL, 0x006b0000UL, 0x006f0000UL, 0x00c50000UL, +0x00300000UL, 0x00010000UL, 0x00670000UL, 0x002b0000UL, 0x00fe0000UL, 0x00d70000UL, 0x00ab0000UL, 0x00760000UL, +0x00ca0000UL, 0x00820000UL, 0x00c90000UL, 0x007d0000UL, 0x00fa0000UL, 0x00590000UL, 0x00470000UL, 0x00f00000UL, +0x00ad0000UL, 0x00d40000UL, 0x00a20000UL, 0x00af0000UL, 0x009c0000UL, 0x00a40000UL, 0x00720000UL, 0x00c00000UL, +0x00b70000UL, 0x00fd0000UL, 0x00930000UL, 0x00260000UL, 0x00360000UL, 0x003f0000UL, 0x00f70000UL, 0x00cc0000UL, +0x00340000UL, 0x00a50000UL, 0x00e50000UL, 0x00f10000UL, 0x00710000UL, 0x00d80000UL, 0x00310000UL, 0x00150000UL, +0x00040000UL, 0x00c70000UL, 0x00230000UL, 0x00c30000UL, 0x00180000UL, 0x00960000UL, 0x00050000UL, 0x009a0000UL, +0x00070000UL, 0x00120000UL, 0x00800000UL, 0x00e20000UL, 0x00eb0000UL, 0x00270000UL, 0x00b20000UL, 0x00750000UL, +0x00090000UL, 0x00830000UL, 0x002c0000UL, 0x001a0000UL, 0x001b0000UL, 0x006e0000UL, 0x005a0000UL, 0x00a00000UL, +0x00520000UL, 0x003b0000UL, 0x00d60000UL, 0x00b30000UL, 0x00290000UL, 0x00e30000UL, 0x002f0000UL, 0x00840000UL, +0x00530000UL, 0x00d10000UL, 0x00000000UL, 0x00ed0000UL, 0x00200000UL, 0x00fc0000UL, 0x00b10000UL, 0x005b0000UL, +0x006a0000UL, 0x00cb0000UL, 0x00be0000UL, 0x00390000UL, 0x004a0000UL, 0x004c0000UL, 0x00580000UL, 0x00cf0000UL, +0x00d00000UL, 0x00ef0000UL, 0x00aa0000UL, 0x00fb0000UL, 0x00430000UL, 0x004d0000UL, 0x00330000UL, 0x00850000UL, +0x00450000UL, 0x00f90000UL, 0x00020000UL, 0x007f0000UL, 0x00500000UL, 0x003c0000UL, 0x009f0000UL, 0x00a80000UL, +0x00510000UL, 0x00a30000UL, 0x00400000UL, 0x008f0000UL, 0x00920000UL, 0x009d0000UL, 0x00380000UL, 0x00f50000UL, +0x00bc0000UL, 0x00b60000UL, 0x00da0000UL, 0x00210000UL, 0x00100000UL, 0x00ff0000UL, 0x00f30000UL, 0x00d20000UL, +0x00cd0000UL, 0x000c0000UL, 0x00130000UL, 0x00ec0000UL, 0x005f0000UL, 0x00970000UL, 0x00440000UL, 0x00170000UL, +0x00c40000UL, 0x00a70000UL, 0x007e0000UL, 0x003d0000UL, 0x00640000UL, 0x005d0000UL, 0x00190000UL, 0x00730000UL, +0x00600000UL, 0x00810000UL, 0x004f0000UL, 0x00dc0000UL, 0x00220000UL, 0x002a0000UL, 0x00900000UL, 0x00880000UL, +0x00460000UL, 0x00ee0000UL, 0x00b80000UL, 0x00140000UL, 0x00de0000UL, 0x005e0000UL, 0x000b0000UL, 0x00db0000UL, +0x00e00000UL, 0x00320000UL, 0x003a0000UL, 0x000a0000UL, 0x00490000UL, 0x00060000UL, 0x00240000UL, 0x005c0000UL, +0x00c20000UL, 0x00d30000UL, 0x00ac0000UL, 0x00620000UL, 0x00910000UL, 0x00950000UL, 0x00e40000UL, 0x00790000UL, +0x00e70000UL, 0x00c80000UL, 0x00370000UL, 0x006d0000UL, 0x008d0000UL, 0x00d50000UL, 0x004e0000UL, 0x00a90000UL, +0x006c0000UL, 0x00560000UL, 0x00f40000UL, 0x00ea0000UL, 0x00650000UL, 0x007a0000UL, 0x00ae0000UL, 0x00080000UL, +0x00ba0000UL, 0x00780000UL, 0x00250000UL, 0x002e0000UL, 0x001c0000UL, 0x00a60000UL, 0x00b40000UL, 0x00c60000UL, +0x00e80000UL, 0x00dd0000UL, 0x00740000UL, 0x001f0000UL, 0x004b0000UL, 0x00bd0000UL, 0x008b0000UL, 0x008a0000UL, +0x00700000UL, 0x003e0000UL, 0x00b50000UL, 0x00660000UL, 0x00480000UL, 0x00030000UL, 0x00f60000UL, 0x000e0000UL, +0x00610000UL, 0x00350000UL, 0x00570000UL, 0x00b90000UL, 0x00860000UL, 0x00c10000UL, 0x001d0000UL, 0x009e0000UL, +0x00e10000UL, 0x00f80000UL, 0x00980000UL, 0x00110000UL, 0x00690000UL, 0x00d90000UL, 0x008e0000UL, 0x00940000UL, +0x009b0000UL, 0x001e0000UL, 0x00870000UL, 0x00e90000UL, 0x00ce0000UL, 0x00550000UL, 0x00280000UL, 0x00df0000UL, +0x008c0000UL, 0x00a10000UL, 0x00890000UL, 0x000d0000UL, 0x00bf0000UL, 0x00e60000UL, 0x00420000UL, 0x00680000UL, +0x00410000UL, 0x00990000UL, 0x002d0000UL, 0x000f0000UL, 0x00b00000UL, 0x00540000UL, 0x00bb0000UL, 0x00160000UL +}; + +static const ulong32 Te4_3[] = { +0x63000000UL, 0x7c000000UL, 0x77000000UL, 0x7b000000UL, 0xf2000000UL, 0x6b000000UL, 0x6f000000UL, 0xc5000000UL, +0x30000000UL, 0x01000000UL, 0x67000000UL, 0x2b000000UL, 0xfe000000UL, 0xd7000000UL, 0xab000000UL, 0x76000000UL, +0xca000000UL, 0x82000000UL, 0xc9000000UL, 0x7d000000UL, 0xfa000000UL, 0x59000000UL, 0x47000000UL, 0xf0000000UL, +0xad000000UL, 0xd4000000UL, 0xa2000000UL, 0xaf000000UL, 0x9c000000UL, 0xa4000000UL, 0x72000000UL, 0xc0000000UL, +0xb7000000UL, 0xfd000000UL, 0x93000000UL, 0x26000000UL, 0x36000000UL, 0x3f000000UL, 0xf7000000UL, 0xcc000000UL, +0x34000000UL, 0xa5000000UL, 0xe5000000UL, 0xf1000000UL, 0x71000000UL, 0xd8000000UL, 0x31000000UL, 0x15000000UL, +0x04000000UL, 0xc7000000UL, 0x23000000UL, 0xc3000000UL, 0x18000000UL, 0x96000000UL, 0x05000000UL, 0x9a000000UL, +0x07000000UL, 0x12000000UL, 0x80000000UL, 0xe2000000UL, 0xeb000000UL, 0x27000000UL, 0xb2000000UL, 0x75000000UL, +0x09000000UL, 0x83000000UL, 0x2c000000UL, 0x1a000000UL, 0x1b000000UL, 0x6e000000UL, 0x5a000000UL, 0xa0000000UL, +0x52000000UL, 0x3b000000UL, 0xd6000000UL, 0xb3000000UL, 0x29000000UL, 0xe3000000UL, 0x2f000000UL, 0x84000000UL, +0x53000000UL, 0xd1000000UL, 0x00000000UL, 0xed000000UL, 0x20000000UL, 0xfc000000UL, 0xb1000000UL, 0x5b000000UL, +0x6a000000UL, 0xcb000000UL, 0xbe000000UL, 0x39000000UL, 0x4a000000UL, 0x4c000000UL, 0x58000000UL, 0xcf000000UL, +0xd0000000UL, 0xef000000UL, 0xaa000000UL, 0xfb000000UL, 0x43000000UL, 0x4d000000UL, 0x33000000UL, 0x85000000UL, +0x45000000UL, 0xf9000000UL, 0x02000000UL, 0x7f000000UL, 0x50000000UL, 0x3c000000UL, 0x9f000000UL, 0xa8000000UL, +0x51000000UL, 0xa3000000UL, 0x40000000UL, 0x8f000000UL, 0x92000000UL, 0x9d000000UL, 0x38000000UL, 0xf5000000UL, +0xbc000000UL, 0xb6000000UL, 0xda000000UL, 0x21000000UL, 0x10000000UL, 0xff000000UL, 0xf3000000UL, 0xd2000000UL, +0xcd000000UL, 0x0c000000UL, 0x13000000UL, 0xec000000UL, 0x5f000000UL, 0x97000000UL, 0x44000000UL, 0x17000000UL, +0xc4000000UL, 0xa7000000UL, 0x7e000000UL, 0x3d000000UL, 0x64000000UL, 0x5d000000UL, 0x19000000UL, 0x73000000UL, +0x60000000UL, 0x81000000UL, 0x4f000000UL, 0xdc000000UL, 0x22000000UL, 0x2a000000UL, 0x90000000UL, 0x88000000UL, +0x46000000UL, 0xee000000UL, 0xb8000000UL, 0x14000000UL, 0xde000000UL, 0x5e000000UL, 0x0b000000UL, 0xdb000000UL, +0xe0000000UL, 0x32000000UL, 0x3a000000UL, 0x0a000000UL, 0x49000000UL, 0x06000000UL, 0x24000000UL, 0x5c000000UL, +0xc2000000UL, 0xd3000000UL, 0xac000000UL, 0x62000000UL, 0x91000000UL, 0x95000000UL, 0xe4000000UL, 0x79000000UL, +0xe7000000UL, 0xc8000000UL, 0x37000000UL, 0x6d000000UL, 0x8d000000UL, 0xd5000000UL, 0x4e000000UL, 0xa9000000UL, +0x6c000000UL, 0x56000000UL, 0xf4000000UL, 0xea000000UL, 0x65000000UL, 0x7a000000UL, 0xae000000UL, 0x08000000UL, +0xba000000UL, 0x78000000UL, 0x25000000UL, 0x2e000000UL, 0x1c000000UL, 0xa6000000UL, 0xb4000000UL, 0xc6000000UL, +0xe8000000UL, 0xdd000000UL, 0x74000000UL, 0x1f000000UL, 0x4b000000UL, 0xbd000000UL, 0x8b000000UL, 0x8a000000UL, +0x70000000UL, 0x3e000000UL, 0xb5000000UL, 0x66000000UL, 0x48000000UL, 0x03000000UL, 0xf6000000UL, 0x0e000000UL, +0x61000000UL, 0x35000000UL, 0x57000000UL, 0xb9000000UL, 0x86000000UL, 0xc1000000UL, 0x1d000000UL, 0x9e000000UL, +0xe1000000UL, 0xf8000000UL, 0x98000000UL, 0x11000000UL, 0x69000000UL, 0xd9000000UL, 0x8e000000UL, 0x94000000UL, +0x9b000000UL, 0x1e000000UL, 0x87000000UL, 0xe9000000UL, 0xce000000UL, 0x55000000UL, 0x28000000UL, 0xdf000000UL, +0x8c000000UL, 0xa1000000UL, 0x89000000UL, 0x0d000000UL, 0xbf000000UL, 0xe6000000UL, 0x42000000UL, 0x68000000UL, +0x41000000UL, 0x99000000UL, 0x2d000000UL, 0x0f000000UL, 0xb0000000UL, 0x54000000UL, 0xbb000000UL, 0x16000000UL +}; +#endif /* pelimac */ + +#ifndef ENCRYPT_ONLY + +static const ulong32 TD1[256] = { + 0x5051f4a7UL, 0x537e4165UL, 0xc31a17a4UL, 0x963a275eUL, + 0xcb3bab6bUL, 0xf11f9d45UL, 0xabacfa58UL, 0x934be303UL, + 0x552030faUL, 0xf6ad766dUL, 0x9188cc76UL, 0x25f5024cUL, + 0xfc4fe5d7UL, 0xd7c52acbUL, 0x80263544UL, 0x8fb562a3UL, + 0x49deb15aUL, 0x6725ba1bUL, 0x9845ea0eUL, 0xe15dfec0UL, + 0x02c32f75UL, 0x12814cf0UL, 0xa38d4697UL, 0xc66bd3f9UL, + 0xe7038f5fUL, 0x9515929cUL, 0xebbf6d7aUL, 0xda955259UL, + 0x2dd4be83UL, 0xd3587421UL, 0x2949e069UL, 0x448ec9c8UL, + 0x6a75c289UL, 0x78f48e79UL, 0x6b99583eUL, 0xdd27b971UL, + 0xb6bee14fUL, 0x17f088adUL, 0x66c920acUL, 0xb47dce3aUL, + 0x1863df4aUL, 0x82e51a31UL, 0x60975133UL, 0x4562537fUL, + 0xe0b16477UL, 0x84bb6baeUL, 0x1cfe81a0UL, 0x94f9082bUL, + 0x58704868UL, 0x198f45fdUL, 0x8794de6cUL, 0xb7527bf8UL, + 0x23ab73d3UL, 0xe2724b02UL, 0x57e31f8fUL, 0x2a6655abUL, + 0x07b2eb28UL, 0x032fb5c2UL, 0x9a86c57bUL, 0xa5d33708UL, + 0xf2302887UL, 0xb223bfa5UL, 0xba02036aUL, 0x5ced1682UL, + 0x2b8acf1cUL, 0x92a779b4UL, 0xf0f307f2UL, 0xa14e69e2UL, + 0xcd65daf4UL, 0xd50605beUL, 0x1fd13462UL, 0x8ac4a6feUL, + 0x9d342e53UL, 0xa0a2f355UL, 0x32058ae1UL, 0x75a4f6ebUL, + 0x390b83ecUL, 0xaa4060efUL, 0x065e719fUL, 0x51bd6e10UL, + 0xf93e218aUL, 0x3d96dd06UL, 0xaedd3e05UL, 0x464de6bdUL, + 0xb591548dUL, 0x0571c45dUL, 0x6f0406d4UL, 0xff605015UL, + 0x241998fbUL, 0x97d6bde9UL, 0xcc894043UL, 0x7767d99eUL, + 0xbdb0e842UL, 0x8807898bUL, 0x38e7195bUL, 0xdb79c8eeUL, + 0x47a17c0aUL, 0xe97c420fUL, 0xc9f8841eUL, 0x00000000UL, + 0x83098086UL, 0x48322bedUL, 0xac1e1170UL, 0x4e6c5a72UL, + 0xfbfd0effUL, 0x560f8538UL, 0x1e3daed5UL, 0x27362d39UL, + 0x640a0fd9UL, 0x21685ca6UL, 0xd19b5b54UL, 0x3a24362eUL, + 0xb10c0a67UL, 0x0f9357e7UL, 0xd2b4ee96UL, 0x9e1b9b91UL, + 0x4f80c0c5UL, 0xa261dc20UL, 0x695a774bUL, 0x161c121aUL, + 0x0ae293baUL, 0xe5c0a02aUL, 0x433c22e0UL, 0x1d121b17UL, + 0x0b0e090dUL, 0xadf28bc7UL, 0xb92db6a8UL, 0xc8141ea9UL, + 0x8557f119UL, 0x4caf7507UL, 0xbbee99ddUL, 0xfda37f60UL, + 0x9ff70126UL, 0xbc5c72f5UL, 0xc544663bUL, 0x345bfb7eUL, + 0x768b4329UL, 0xdccb23c6UL, 0x68b6edfcUL, 0x63b8e4f1UL, + 0xcad731dcUL, 0x10426385UL, 0x40139722UL, 0x2084c611UL, + 0x7d854a24UL, 0xf8d2bb3dUL, 0x11aef932UL, 0x6dc729a1UL, + 0x4b1d9e2fUL, 0xf3dcb230UL, 0xec0d8652UL, 0xd077c1e3UL, + 0x6c2bb316UL, 0x99a970b9UL, 0xfa119448UL, 0x2247e964UL, + 0xc4a8fc8cUL, 0x1aa0f03fUL, 0xd8567d2cUL, 0xef223390UL, + 0xc787494eUL, 0xc1d938d1UL, 0xfe8ccaa2UL, 0x3698d40bUL, + 0xcfa6f581UL, 0x28a57adeUL, 0x26dab78eUL, 0xa43fadbfUL, + 0xe42c3a9dUL, 0x0d507892UL, 0x9b6a5fccUL, 0x62547e46UL, + 0xc2f68d13UL, 0xe890d8b8UL, 0x5e2e39f7UL, 0xf582c3afUL, + 0xbe9f5d80UL, 0x7c69d093UL, 0xa96fd52dUL, 0xb3cf2512UL, + 0x3bc8ac99UL, 0xa710187dUL, 0x6ee89c63UL, 0x7bdb3bbbUL, + 0x09cd2678UL, 0xf46e5918UL, 0x01ec9ab7UL, 0xa8834f9aUL, + 0x65e6956eUL, 0x7eaaffe6UL, 0x0821bccfUL, 0xe6ef15e8UL, + 0xd9bae79bUL, 0xce4a6f36UL, 0xd4ea9f09UL, 0xd629b07cUL, + 0xaf31a4b2UL, 0x312a3f23UL, 0x30c6a594UL, 0xc035a266UL, + 0x37744ebcUL, 0xa6fc82caUL, 0xb0e090d0UL, 0x1533a7d8UL, + 0x4af10498UL, 0xf741ecdaUL, 0x0e7fcd50UL, 0x2f1791f6UL, + 0x8d764dd6UL, 0x4d43efb0UL, 0x54ccaa4dUL, 0xdfe49604UL, + 0xe39ed1b5UL, 0x1b4c6a88UL, 0xb8c12c1fUL, 0x7f466551UL, + 0x049d5eeaUL, 0x5d018c35UL, 0x73fa8774UL, 0x2efb0b41UL, + 0x5ab3671dUL, 0x5292dbd2UL, 0x33e91056UL, 0x136dd647UL, + 0x8c9ad761UL, 0x7a37a10cUL, 0x8e59f814UL, 0x89eb133cUL, + 0xeecea927UL, 0x35b761c9UL, 0xede11ce5UL, 0x3c7a47b1UL, + 0x599cd2dfUL, 0x3f55f273UL, 0x791814ceUL, 0xbf73c737UL, + 0xea53f7cdUL, 0x5b5ffdaaUL, 0x14df3d6fUL, 0x867844dbUL, + 0x81caaff3UL, 0x3eb968c4UL, 0x2c382434UL, 0x5fc2a340UL, + 0x72161dc3UL, 0x0cbce225UL, 0x8b283c49UL, 0x41ff0d95UL, + 0x7139a801UL, 0xde080cb3UL, 0x9cd8b4e4UL, 0x906456c1UL, + 0x617bcb84UL, 0x70d532b6UL, 0x74486c5cUL, 0x42d0b857UL, +}; +static const ulong32 TD2[256] = { + 0xa75051f4UL, 0x65537e41UL, 0xa4c31a17UL, 0x5e963a27UL, + 0x6bcb3babUL, 0x45f11f9dUL, 0x58abacfaUL, 0x03934be3UL, + 0xfa552030UL, 0x6df6ad76UL, 0x769188ccUL, 0x4c25f502UL, + 0xd7fc4fe5UL, 0xcbd7c52aUL, 0x44802635UL, 0xa38fb562UL, + 0x5a49deb1UL, 0x1b6725baUL, 0x0e9845eaUL, 0xc0e15dfeUL, + 0x7502c32fUL, 0xf012814cUL, 0x97a38d46UL, 0xf9c66bd3UL, + 0x5fe7038fUL, 0x9c951592UL, 0x7aebbf6dUL, 0x59da9552UL, + 0x832dd4beUL, 0x21d35874UL, 0x692949e0UL, 0xc8448ec9UL, + 0x896a75c2UL, 0x7978f48eUL, 0x3e6b9958UL, 0x71dd27b9UL, + 0x4fb6bee1UL, 0xad17f088UL, 0xac66c920UL, 0x3ab47dceUL, + 0x4a1863dfUL, 0x3182e51aUL, 0x33609751UL, 0x7f456253UL, + 0x77e0b164UL, 0xae84bb6bUL, 0xa01cfe81UL, 0x2b94f908UL, + 0x68587048UL, 0xfd198f45UL, 0x6c8794deUL, 0xf8b7527bUL, + 0xd323ab73UL, 0x02e2724bUL, 0x8f57e31fUL, 0xab2a6655UL, + 0x2807b2ebUL, 0xc2032fb5UL, 0x7b9a86c5UL, 0x08a5d337UL, + 0x87f23028UL, 0xa5b223bfUL, 0x6aba0203UL, 0x825ced16UL, + 0x1c2b8acfUL, 0xb492a779UL, 0xf2f0f307UL, 0xe2a14e69UL, + 0xf4cd65daUL, 0xbed50605UL, 0x621fd134UL, 0xfe8ac4a6UL, + 0x539d342eUL, 0x55a0a2f3UL, 0xe132058aUL, 0xeb75a4f6UL, + 0xec390b83UL, 0xefaa4060UL, 0x9f065e71UL, 0x1051bd6eUL, + 0x8af93e21UL, 0x063d96ddUL, 0x05aedd3eUL, 0xbd464de6UL, + 0x8db59154UL, 0x5d0571c4UL, 0xd46f0406UL, 0x15ff6050UL, + 0xfb241998UL, 0xe997d6bdUL, 0x43cc8940UL, 0x9e7767d9UL, + 0x42bdb0e8UL, 0x8b880789UL, 0x5b38e719UL, 0xeedb79c8UL, + 0x0a47a17cUL, 0x0fe97c42UL, 0x1ec9f884UL, 0x00000000UL, + 0x86830980UL, 0xed48322bUL, 0x70ac1e11UL, 0x724e6c5aUL, + 0xfffbfd0eUL, 0x38560f85UL, 0xd51e3daeUL, 0x3927362dUL, + 0xd9640a0fUL, 0xa621685cUL, 0x54d19b5bUL, 0x2e3a2436UL, + 0x67b10c0aUL, 0xe70f9357UL, 0x96d2b4eeUL, 0x919e1b9bUL, + 0xc54f80c0UL, 0x20a261dcUL, 0x4b695a77UL, 0x1a161c12UL, + 0xba0ae293UL, 0x2ae5c0a0UL, 0xe0433c22UL, 0x171d121bUL, + 0x0d0b0e09UL, 0xc7adf28bUL, 0xa8b92db6UL, 0xa9c8141eUL, + 0x198557f1UL, 0x074caf75UL, 0xddbbee99UL, 0x60fda37fUL, + 0x269ff701UL, 0xf5bc5c72UL, 0x3bc54466UL, 0x7e345bfbUL, + 0x29768b43UL, 0xc6dccb23UL, 0xfc68b6edUL, 0xf163b8e4UL, + 0xdccad731UL, 0x85104263UL, 0x22401397UL, 0x112084c6UL, + 0x247d854aUL, 0x3df8d2bbUL, 0x3211aef9UL, 0xa16dc729UL, + 0x2f4b1d9eUL, 0x30f3dcb2UL, 0x52ec0d86UL, 0xe3d077c1UL, + 0x166c2bb3UL, 0xb999a970UL, 0x48fa1194UL, 0x642247e9UL, + 0x8cc4a8fcUL, 0x3f1aa0f0UL, 0x2cd8567dUL, 0x90ef2233UL, + 0x4ec78749UL, 0xd1c1d938UL, 0xa2fe8ccaUL, 0x0b3698d4UL, + 0x81cfa6f5UL, 0xde28a57aUL, 0x8e26dab7UL, 0xbfa43fadUL, + 0x9de42c3aUL, 0x920d5078UL, 0xcc9b6a5fUL, 0x4662547eUL, + 0x13c2f68dUL, 0xb8e890d8UL, 0xf75e2e39UL, 0xaff582c3UL, + 0x80be9f5dUL, 0x937c69d0UL, 0x2da96fd5UL, 0x12b3cf25UL, + 0x993bc8acUL, 0x7da71018UL, 0x636ee89cUL, 0xbb7bdb3bUL, + 0x7809cd26UL, 0x18f46e59UL, 0xb701ec9aUL, 0x9aa8834fUL, + 0x6e65e695UL, 0xe67eaaffUL, 0xcf0821bcUL, 0xe8e6ef15UL, + 0x9bd9bae7UL, 0x36ce4a6fUL, 0x09d4ea9fUL, 0x7cd629b0UL, + 0xb2af31a4UL, 0x23312a3fUL, 0x9430c6a5UL, 0x66c035a2UL, + 0xbc37744eUL, 0xcaa6fc82UL, 0xd0b0e090UL, 0xd81533a7UL, + 0x984af104UL, 0xdaf741ecUL, 0x500e7fcdUL, 0xf62f1791UL, + 0xd68d764dUL, 0xb04d43efUL, 0x4d54ccaaUL, 0x04dfe496UL, + 0xb5e39ed1UL, 0x881b4c6aUL, 0x1fb8c12cUL, 0x517f4665UL, + 0xea049d5eUL, 0x355d018cUL, 0x7473fa87UL, 0x412efb0bUL, + 0x1d5ab367UL, 0xd25292dbUL, 0x5633e910UL, 0x47136dd6UL, + 0x618c9ad7UL, 0x0c7a37a1UL, 0x148e59f8UL, 0x3c89eb13UL, + 0x27eecea9UL, 0xc935b761UL, 0xe5ede11cUL, 0xb13c7a47UL, + 0xdf599cd2UL, 0x733f55f2UL, 0xce791814UL, 0x37bf73c7UL, + 0xcdea53f7UL, 0xaa5b5ffdUL, 0x6f14df3dUL, 0xdb867844UL, + 0xf381caafUL, 0xc43eb968UL, 0x342c3824UL, 0x405fc2a3UL, + 0xc372161dUL, 0x250cbce2UL, 0x498b283cUL, 0x9541ff0dUL, + 0x017139a8UL, 0xb3de080cUL, 0xe49cd8b4UL, 0xc1906456UL, + 0x84617bcbUL, 0xb670d532UL, 0x5c74486cUL, 0x5742d0b8UL, +}; +static const ulong32 TD3[256] = { + 0xf4a75051UL, 0x4165537eUL, 0x17a4c31aUL, 0x275e963aUL, + 0xab6bcb3bUL, 0x9d45f11fUL, 0xfa58abacUL, 0xe303934bUL, + 0x30fa5520UL, 0x766df6adUL, 0xcc769188UL, 0x024c25f5UL, + 0xe5d7fc4fUL, 0x2acbd7c5UL, 0x35448026UL, 0x62a38fb5UL, + 0xb15a49deUL, 0xba1b6725UL, 0xea0e9845UL, 0xfec0e15dUL, + 0x2f7502c3UL, 0x4cf01281UL, 0x4697a38dUL, 0xd3f9c66bUL, + 0x8f5fe703UL, 0x929c9515UL, 0x6d7aebbfUL, 0x5259da95UL, + 0xbe832dd4UL, 0x7421d358UL, 0xe0692949UL, 0xc9c8448eUL, + 0xc2896a75UL, 0x8e7978f4UL, 0x583e6b99UL, 0xb971dd27UL, + 0xe14fb6beUL, 0x88ad17f0UL, 0x20ac66c9UL, 0xce3ab47dUL, + 0xdf4a1863UL, 0x1a3182e5UL, 0x51336097UL, 0x537f4562UL, + 0x6477e0b1UL, 0x6bae84bbUL, 0x81a01cfeUL, 0x082b94f9UL, + 0x48685870UL, 0x45fd198fUL, 0xde6c8794UL, 0x7bf8b752UL, + 0x73d323abUL, 0x4b02e272UL, 0x1f8f57e3UL, 0x55ab2a66UL, + 0xeb2807b2UL, 0xb5c2032fUL, 0xc57b9a86UL, 0x3708a5d3UL, + 0x2887f230UL, 0xbfa5b223UL, 0x036aba02UL, 0x16825cedUL, + 0xcf1c2b8aUL, 0x79b492a7UL, 0x07f2f0f3UL, 0x69e2a14eUL, + 0xdaf4cd65UL, 0x05bed506UL, 0x34621fd1UL, 0xa6fe8ac4UL, + 0x2e539d34UL, 0xf355a0a2UL, 0x8ae13205UL, 0xf6eb75a4UL, + 0x83ec390bUL, 0x60efaa40UL, 0x719f065eUL, 0x6e1051bdUL, + 0x218af93eUL, 0xdd063d96UL, 0x3e05aeddUL, 0xe6bd464dUL, + 0x548db591UL, 0xc45d0571UL, 0x06d46f04UL, 0x5015ff60UL, + 0x98fb2419UL, 0xbde997d6UL, 0x4043cc89UL, 0xd99e7767UL, + 0xe842bdb0UL, 0x898b8807UL, 0x195b38e7UL, 0xc8eedb79UL, + 0x7c0a47a1UL, 0x420fe97cUL, 0x841ec9f8UL, 0x00000000UL, + 0x80868309UL, 0x2bed4832UL, 0x1170ac1eUL, 0x5a724e6cUL, + 0x0efffbfdUL, 0x8538560fUL, 0xaed51e3dUL, 0x2d392736UL, + 0x0fd9640aUL, 0x5ca62168UL, 0x5b54d19bUL, 0x362e3a24UL, + 0x0a67b10cUL, 0x57e70f93UL, 0xee96d2b4UL, 0x9b919e1bUL, + 0xc0c54f80UL, 0xdc20a261UL, 0x774b695aUL, 0x121a161cUL, + 0x93ba0ae2UL, 0xa02ae5c0UL, 0x22e0433cUL, 0x1b171d12UL, + 0x090d0b0eUL, 0x8bc7adf2UL, 0xb6a8b92dUL, 0x1ea9c814UL, + 0xf1198557UL, 0x75074cafUL, 0x99ddbbeeUL, 0x7f60fda3UL, + 0x01269ff7UL, 0x72f5bc5cUL, 0x663bc544UL, 0xfb7e345bUL, + 0x4329768bUL, 0x23c6dccbUL, 0xedfc68b6UL, 0xe4f163b8UL, + 0x31dccad7UL, 0x63851042UL, 0x97224013UL, 0xc6112084UL, + 0x4a247d85UL, 0xbb3df8d2UL, 0xf93211aeUL, 0x29a16dc7UL, + 0x9e2f4b1dUL, 0xb230f3dcUL, 0x8652ec0dUL, 0xc1e3d077UL, + 0xb3166c2bUL, 0x70b999a9UL, 0x9448fa11UL, 0xe9642247UL, + 0xfc8cc4a8UL, 0xf03f1aa0UL, 0x7d2cd856UL, 0x3390ef22UL, + 0x494ec787UL, 0x38d1c1d9UL, 0xcaa2fe8cUL, 0xd40b3698UL, + 0xf581cfa6UL, 0x7ade28a5UL, 0xb78e26daUL, 0xadbfa43fUL, + 0x3a9de42cUL, 0x78920d50UL, 0x5fcc9b6aUL, 0x7e466254UL, + 0x8d13c2f6UL, 0xd8b8e890UL, 0x39f75e2eUL, 0xc3aff582UL, + 0x5d80be9fUL, 0xd0937c69UL, 0xd52da96fUL, 0x2512b3cfUL, + 0xac993bc8UL, 0x187da710UL, 0x9c636ee8UL, 0x3bbb7bdbUL, + 0x267809cdUL, 0x5918f46eUL, 0x9ab701ecUL, 0x4f9aa883UL, + 0x956e65e6UL, 0xffe67eaaUL, 0xbccf0821UL, 0x15e8e6efUL, + 0xe79bd9baUL, 0x6f36ce4aUL, 0x9f09d4eaUL, 0xb07cd629UL, + 0xa4b2af31UL, 0x3f23312aUL, 0xa59430c6UL, 0xa266c035UL, + 0x4ebc3774UL, 0x82caa6fcUL, 0x90d0b0e0UL, 0xa7d81533UL, + 0x04984af1UL, 0xecdaf741UL, 0xcd500e7fUL, 0x91f62f17UL, + 0x4dd68d76UL, 0xefb04d43UL, 0xaa4d54ccUL, 0x9604dfe4UL, + 0xd1b5e39eUL, 0x6a881b4cUL, 0x2c1fb8c1UL, 0x65517f46UL, + 0x5eea049dUL, 0x8c355d01UL, 0x877473faUL, 0x0b412efbUL, + 0x671d5ab3UL, 0xdbd25292UL, 0x105633e9UL, 0xd647136dUL, + 0xd7618c9aUL, 0xa10c7a37UL, 0xf8148e59UL, 0x133c89ebUL, + 0xa927eeceUL, 0x61c935b7UL, 0x1ce5ede1UL, 0x47b13c7aUL, + 0xd2df599cUL, 0xf2733f55UL, 0x14ce7918UL, 0xc737bf73UL, + 0xf7cdea53UL, 0xfdaa5b5fUL, 0x3d6f14dfUL, 0x44db8678UL, + 0xaff381caUL, 0x68c43eb9UL, 0x24342c38UL, 0xa3405fc2UL, + 0x1dc37216UL, 0xe2250cbcUL, 0x3c498b28UL, 0x0d9541ffUL, + 0xa8017139UL, 0x0cb3de08UL, 0xb4e49cd8UL, 0x56c19064UL, + 0xcb84617bUL, 0x32b670d5UL, 0x6c5c7448UL, 0xb85742d0UL, +}; + +static const ulong32 Tks0[] = { +0x00000000UL, 0x0e090d0bUL, 0x1c121a16UL, 0x121b171dUL, 0x3824342cUL, 0x362d3927UL, 0x24362e3aUL, 0x2a3f2331UL, +0x70486858UL, 0x7e416553UL, 0x6c5a724eUL, 0x62537f45UL, 0x486c5c74UL, 0x4665517fUL, 0x547e4662UL, 0x5a774b69UL, +0xe090d0b0UL, 0xee99ddbbUL, 0xfc82caa6UL, 0xf28bc7adUL, 0xd8b4e49cUL, 0xd6bde997UL, 0xc4a6fe8aUL, 0xcaaff381UL, +0x90d8b8e8UL, 0x9ed1b5e3UL, 0x8ccaa2feUL, 0x82c3aff5UL, 0xa8fc8cc4UL, 0xa6f581cfUL, 0xb4ee96d2UL, 0xbae79bd9UL, +0xdb3bbb7bUL, 0xd532b670UL, 0xc729a16dUL, 0xc920ac66UL, 0xe31f8f57UL, 0xed16825cUL, 0xff0d9541UL, 0xf104984aUL, +0xab73d323UL, 0xa57ade28UL, 0xb761c935UL, 0xb968c43eUL, 0x9357e70fUL, 0x9d5eea04UL, 0x8f45fd19UL, 0x814cf012UL, +0x3bab6bcbUL, 0x35a266c0UL, 0x27b971ddUL, 0x29b07cd6UL, 0x038f5fe7UL, 0x0d8652ecUL, 0x1f9d45f1UL, 0x119448faUL, +0x4be30393UL, 0x45ea0e98UL, 0x57f11985UL, 0x59f8148eUL, 0x73c737bfUL, 0x7dce3ab4UL, 0x6fd52da9UL, 0x61dc20a2UL, +0xad766df6UL, 0xa37f60fdUL, 0xb16477e0UL, 0xbf6d7aebUL, 0x955259daUL, 0x9b5b54d1UL, 0x894043ccUL, 0x87494ec7UL, +0xdd3e05aeUL, 0xd33708a5UL, 0xc12c1fb8UL, 0xcf2512b3UL, 0xe51a3182UL, 0xeb133c89UL, 0xf9082b94UL, 0xf701269fUL, +0x4de6bd46UL, 0x43efb04dUL, 0x51f4a750UL, 0x5ffdaa5bUL, 0x75c2896aUL, 0x7bcb8461UL, 0x69d0937cUL, 0x67d99e77UL, +0x3daed51eUL, 0x33a7d815UL, 0x21bccf08UL, 0x2fb5c203UL, 0x058ae132UL, 0x0b83ec39UL, 0x1998fb24UL, 0x1791f62fUL, +0x764dd68dUL, 0x7844db86UL, 0x6a5fcc9bUL, 0x6456c190UL, 0x4e69e2a1UL, 0x4060efaaUL, 0x527bf8b7UL, 0x5c72f5bcUL, +0x0605bed5UL, 0x080cb3deUL, 0x1a17a4c3UL, 0x141ea9c8UL, 0x3e218af9UL, 0x302887f2UL, 0x223390efUL, 0x2c3a9de4UL, +0x96dd063dUL, 0x98d40b36UL, 0x8acf1c2bUL, 0x84c61120UL, 0xaef93211UL, 0xa0f03f1aUL, 0xb2eb2807UL, 0xbce2250cUL, +0xe6956e65UL, 0xe89c636eUL, 0xfa877473UL, 0xf48e7978UL, 0xdeb15a49UL, 0xd0b85742UL, 0xc2a3405fUL, 0xccaa4d54UL, +0x41ecdaf7UL, 0x4fe5d7fcUL, 0x5dfec0e1UL, 0x53f7cdeaUL, 0x79c8eedbUL, 0x77c1e3d0UL, 0x65daf4cdUL, 0x6bd3f9c6UL, +0x31a4b2afUL, 0x3fadbfa4UL, 0x2db6a8b9UL, 0x23bfa5b2UL, 0x09808683UL, 0x07898b88UL, 0x15929c95UL, 0x1b9b919eUL, +0xa17c0a47UL, 0xaf75074cUL, 0xbd6e1051UL, 0xb3671d5aUL, 0x99583e6bUL, 0x97513360UL, 0x854a247dUL, 0x8b432976UL, +0xd134621fUL, 0xdf3d6f14UL, 0xcd267809UL, 0xc32f7502UL, 0xe9105633UL, 0xe7195b38UL, 0xf5024c25UL, 0xfb0b412eUL, +0x9ad7618cUL, 0x94de6c87UL, 0x86c57b9aUL, 0x88cc7691UL, 0xa2f355a0UL, 0xacfa58abUL, 0xbee14fb6UL, 0xb0e842bdUL, +0xea9f09d4UL, 0xe49604dfUL, 0xf68d13c2UL, 0xf8841ec9UL, 0xd2bb3df8UL, 0xdcb230f3UL, 0xcea927eeUL, 0xc0a02ae5UL, +0x7a47b13cUL, 0x744ebc37UL, 0x6655ab2aUL, 0x685ca621UL, 0x42638510UL, 0x4c6a881bUL, 0x5e719f06UL, 0x5078920dUL, +0x0a0fd964UL, 0x0406d46fUL, 0x161dc372UL, 0x1814ce79UL, 0x322bed48UL, 0x3c22e043UL, 0x2e39f75eUL, 0x2030fa55UL, +0xec9ab701UL, 0xe293ba0aUL, 0xf088ad17UL, 0xfe81a01cUL, 0xd4be832dUL, 0xdab78e26UL, 0xc8ac993bUL, 0xc6a59430UL, +0x9cd2df59UL, 0x92dbd252UL, 0x80c0c54fUL, 0x8ec9c844UL, 0xa4f6eb75UL, 0xaaffe67eUL, 0xb8e4f163UL, 0xb6edfc68UL, +0x0c0a67b1UL, 0x02036abaUL, 0x10187da7UL, 0x1e1170acUL, 0x342e539dUL, 0x3a275e96UL, 0x283c498bUL, 0x26354480UL, +0x7c420fe9UL, 0x724b02e2UL, 0x605015ffUL, 0x6e5918f4UL, 0x44663bc5UL, 0x4a6f36ceUL, 0x587421d3UL, 0x567d2cd8UL, +0x37a10c7aUL, 0x39a80171UL, 0x2bb3166cUL, 0x25ba1b67UL, 0x0f853856UL, 0x018c355dUL, 0x13972240UL, 0x1d9e2f4bUL, +0x47e96422UL, 0x49e06929UL, 0x5bfb7e34UL, 0x55f2733fUL, 0x7fcd500eUL, 0x71c45d05UL, 0x63df4a18UL, 0x6dd64713UL, +0xd731dccaUL, 0xd938d1c1UL, 0xcb23c6dcUL, 0xc52acbd7UL, 0xef15e8e6UL, 0xe11ce5edUL, 0xf307f2f0UL, 0xfd0efffbUL, +0xa779b492UL, 0xa970b999UL, 0xbb6bae84UL, 0xb562a38fUL, 0x9f5d80beUL, 0x91548db5UL, 0x834f9aa8UL, 0x8d4697a3UL +}; + +static const ulong32 Tks1[] = { +0x00000000UL, 0x0b0e090dUL, 0x161c121aUL, 0x1d121b17UL, 0x2c382434UL, 0x27362d39UL, 0x3a24362eUL, 0x312a3f23UL, +0x58704868UL, 0x537e4165UL, 0x4e6c5a72UL, 0x4562537fUL, 0x74486c5cUL, 0x7f466551UL, 0x62547e46UL, 0x695a774bUL, +0xb0e090d0UL, 0xbbee99ddUL, 0xa6fc82caUL, 0xadf28bc7UL, 0x9cd8b4e4UL, 0x97d6bde9UL, 0x8ac4a6feUL, 0x81caaff3UL, +0xe890d8b8UL, 0xe39ed1b5UL, 0xfe8ccaa2UL, 0xf582c3afUL, 0xc4a8fc8cUL, 0xcfa6f581UL, 0xd2b4ee96UL, 0xd9bae79bUL, +0x7bdb3bbbUL, 0x70d532b6UL, 0x6dc729a1UL, 0x66c920acUL, 0x57e31f8fUL, 0x5ced1682UL, 0x41ff0d95UL, 0x4af10498UL, +0x23ab73d3UL, 0x28a57adeUL, 0x35b761c9UL, 0x3eb968c4UL, 0x0f9357e7UL, 0x049d5eeaUL, 0x198f45fdUL, 0x12814cf0UL, +0xcb3bab6bUL, 0xc035a266UL, 0xdd27b971UL, 0xd629b07cUL, 0xe7038f5fUL, 0xec0d8652UL, 0xf11f9d45UL, 0xfa119448UL, +0x934be303UL, 0x9845ea0eUL, 0x8557f119UL, 0x8e59f814UL, 0xbf73c737UL, 0xb47dce3aUL, 0xa96fd52dUL, 0xa261dc20UL, +0xf6ad766dUL, 0xfda37f60UL, 0xe0b16477UL, 0xebbf6d7aUL, 0xda955259UL, 0xd19b5b54UL, 0xcc894043UL, 0xc787494eUL, +0xaedd3e05UL, 0xa5d33708UL, 0xb8c12c1fUL, 0xb3cf2512UL, 0x82e51a31UL, 0x89eb133cUL, 0x94f9082bUL, 0x9ff70126UL, +0x464de6bdUL, 0x4d43efb0UL, 0x5051f4a7UL, 0x5b5ffdaaUL, 0x6a75c289UL, 0x617bcb84UL, 0x7c69d093UL, 0x7767d99eUL, +0x1e3daed5UL, 0x1533a7d8UL, 0x0821bccfUL, 0x032fb5c2UL, 0x32058ae1UL, 0x390b83ecUL, 0x241998fbUL, 0x2f1791f6UL, +0x8d764dd6UL, 0x867844dbUL, 0x9b6a5fccUL, 0x906456c1UL, 0xa14e69e2UL, 0xaa4060efUL, 0xb7527bf8UL, 0xbc5c72f5UL, +0xd50605beUL, 0xde080cb3UL, 0xc31a17a4UL, 0xc8141ea9UL, 0xf93e218aUL, 0xf2302887UL, 0xef223390UL, 0xe42c3a9dUL, +0x3d96dd06UL, 0x3698d40bUL, 0x2b8acf1cUL, 0x2084c611UL, 0x11aef932UL, 0x1aa0f03fUL, 0x07b2eb28UL, 0x0cbce225UL, +0x65e6956eUL, 0x6ee89c63UL, 0x73fa8774UL, 0x78f48e79UL, 0x49deb15aUL, 0x42d0b857UL, 0x5fc2a340UL, 0x54ccaa4dUL, +0xf741ecdaUL, 0xfc4fe5d7UL, 0xe15dfec0UL, 0xea53f7cdUL, 0xdb79c8eeUL, 0xd077c1e3UL, 0xcd65daf4UL, 0xc66bd3f9UL, +0xaf31a4b2UL, 0xa43fadbfUL, 0xb92db6a8UL, 0xb223bfa5UL, 0x83098086UL, 0x8807898bUL, 0x9515929cUL, 0x9e1b9b91UL, +0x47a17c0aUL, 0x4caf7507UL, 0x51bd6e10UL, 0x5ab3671dUL, 0x6b99583eUL, 0x60975133UL, 0x7d854a24UL, 0x768b4329UL, +0x1fd13462UL, 0x14df3d6fUL, 0x09cd2678UL, 0x02c32f75UL, 0x33e91056UL, 0x38e7195bUL, 0x25f5024cUL, 0x2efb0b41UL, +0x8c9ad761UL, 0x8794de6cUL, 0x9a86c57bUL, 0x9188cc76UL, 0xa0a2f355UL, 0xabacfa58UL, 0xb6bee14fUL, 0xbdb0e842UL, +0xd4ea9f09UL, 0xdfe49604UL, 0xc2f68d13UL, 0xc9f8841eUL, 0xf8d2bb3dUL, 0xf3dcb230UL, 0xeecea927UL, 0xe5c0a02aUL, +0x3c7a47b1UL, 0x37744ebcUL, 0x2a6655abUL, 0x21685ca6UL, 0x10426385UL, 0x1b4c6a88UL, 0x065e719fUL, 0x0d507892UL, +0x640a0fd9UL, 0x6f0406d4UL, 0x72161dc3UL, 0x791814ceUL, 0x48322bedUL, 0x433c22e0UL, 0x5e2e39f7UL, 0x552030faUL, +0x01ec9ab7UL, 0x0ae293baUL, 0x17f088adUL, 0x1cfe81a0UL, 0x2dd4be83UL, 0x26dab78eUL, 0x3bc8ac99UL, 0x30c6a594UL, +0x599cd2dfUL, 0x5292dbd2UL, 0x4f80c0c5UL, 0x448ec9c8UL, 0x75a4f6ebUL, 0x7eaaffe6UL, 0x63b8e4f1UL, 0x68b6edfcUL, +0xb10c0a67UL, 0xba02036aUL, 0xa710187dUL, 0xac1e1170UL, 0x9d342e53UL, 0x963a275eUL, 0x8b283c49UL, 0x80263544UL, +0xe97c420fUL, 0xe2724b02UL, 0xff605015UL, 0xf46e5918UL, 0xc544663bUL, 0xce4a6f36UL, 0xd3587421UL, 0xd8567d2cUL, +0x7a37a10cUL, 0x7139a801UL, 0x6c2bb316UL, 0x6725ba1bUL, 0x560f8538UL, 0x5d018c35UL, 0x40139722UL, 0x4b1d9e2fUL, +0x2247e964UL, 0x2949e069UL, 0x345bfb7eUL, 0x3f55f273UL, 0x0e7fcd50UL, 0x0571c45dUL, 0x1863df4aUL, 0x136dd647UL, +0xcad731dcUL, 0xc1d938d1UL, 0xdccb23c6UL, 0xd7c52acbUL, 0xe6ef15e8UL, 0xede11ce5UL, 0xf0f307f2UL, 0xfbfd0effUL, +0x92a779b4UL, 0x99a970b9UL, 0x84bb6baeUL, 0x8fb562a3UL, 0xbe9f5d80UL, 0xb591548dUL, 0xa8834f9aUL, 0xa38d4697UL +}; + +static const ulong32 Tks2[] = { +0x00000000UL, 0x0d0b0e09UL, 0x1a161c12UL, 0x171d121bUL, 0x342c3824UL, 0x3927362dUL, 0x2e3a2436UL, 0x23312a3fUL, +0x68587048UL, 0x65537e41UL, 0x724e6c5aUL, 0x7f456253UL, 0x5c74486cUL, 0x517f4665UL, 0x4662547eUL, 0x4b695a77UL, +0xd0b0e090UL, 0xddbbee99UL, 0xcaa6fc82UL, 0xc7adf28bUL, 0xe49cd8b4UL, 0xe997d6bdUL, 0xfe8ac4a6UL, 0xf381caafUL, +0xb8e890d8UL, 0xb5e39ed1UL, 0xa2fe8ccaUL, 0xaff582c3UL, 0x8cc4a8fcUL, 0x81cfa6f5UL, 0x96d2b4eeUL, 0x9bd9bae7UL, +0xbb7bdb3bUL, 0xb670d532UL, 0xa16dc729UL, 0xac66c920UL, 0x8f57e31fUL, 0x825ced16UL, 0x9541ff0dUL, 0x984af104UL, +0xd323ab73UL, 0xde28a57aUL, 0xc935b761UL, 0xc43eb968UL, 0xe70f9357UL, 0xea049d5eUL, 0xfd198f45UL, 0xf012814cUL, +0x6bcb3babUL, 0x66c035a2UL, 0x71dd27b9UL, 0x7cd629b0UL, 0x5fe7038fUL, 0x52ec0d86UL, 0x45f11f9dUL, 0x48fa1194UL, +0x03934be3UL, 0x0e9845eaUL, 0x198557f1UL, 0x148e59f8UL, 0x37bf73c7UL, 0x3ab47dceUL, 0x2da96fd5UL, 0x20a261dcUL, +0x6df6ad76UL, 0x60fda37fUL, 0x77e0b164UL, 0x7aebbf6dUL, 0x59da9552UL, 0x54d19b5bUL, 0x43cc8940UL, 0x4ec78749UL, +0x05aedd3eUL, 0x08a5d337UL, 0x1fb8c12cUL, 0x12b3cf25UL, 0x3182e51aUL, 0x3c89eb13UL, 0x2b94f908UL, 0x269ff701UL, +0xbd464de6UL, 0xb04d43efUL, 0xa75051f4UL, 0xaa5b5ffdUL, 0x896a75c2UL, 0x84617bcbUL, 0x937c69d0UL, 0x9e7767d9UL, +0xd51e3daeUL, 0xd81533a7UL, 0xcf0821bcUL, 0xc2032fb5UL, 0xe132058aUL, 0xec390b83UL, 0xfb241998UL, 0xf62f1791UL, +0xd68d764dUL, 0xdb867844UL, 0xcc9b6a5fUL, 0xc1906456UL, 0xe2a14e69UL, 0xefaa4060UL, 0xf8b7527bUL, 0xf5bc5c72UL, +0xbed50605UL, 0xb3de080cUL, 0xa4c31a17UL, 0xa9c8141eUL, 0x8af93e21UL, 0x87f23028UL, 0x90ef2233UL, 0x9de42c3aUL, +0x063d96ddUL, 0x0b3698d4UL, 0x1c2b8acfUL, 0x112084c6UL, 0x3211aef9UL, 0x3f1aa0f0UL, 0x2807b2ebUL, 0x250cbce2UL, +0x6e65e695UL, 0x636ee89cUL, 0x7473fa87UL, 0x7978f48eUL, 0x5a49deb1UL, 0x5742d0b8UL, 0x405fc2a3UL, 0x4d54ccaaUL, +0xdaf741ecUL, 0xd7fc4fe5UL, 0xc0e15dfeUL, 0xcdea53f7UL, 0xeedb79c8UL, 0xe3d077c1UL, 0xf4cd65daUL, 0xf9c66bd3UL, +0xb2af31a4UL, 0xbfa43fadUL, 0xa8b92db6UL, 0xa5b223bfUL, 0x86830980UL, 0x8b880789UL, 0x9c951592UL, 0x919e1b9bUL, +0x0a47a17cUL, 0x074caf75UL, 0x1051bd6eUL, 0x1d5ab367UL, 0x3e6b9958UL, 0x33609751UL, 0x247d854aUL, 0x29768b43UL, +0x621fd134UL, 0x6f14df3dUL, 0x7809cd26UL, 0x7502c32fUL, 0x5633e910UL, 0x5b38e719UL, 0x4c25f502UL, 0x412efb0bUL, +0x618c9ad7UL, 0x6c8794deUL, 0x7b9a86c5UL, 0x769188ccUL, 0x55a0a2f3UL, 0x58abacfaUL, 0x4fb6bee1UL, 0x42bdb0e8UL, +0x09d4ea9fUL, 0x04dfe496UL, 0x13c2f68dUL, 0x1ec9f884UL, 0x3df8d2bbUL, 0x30f3dcb2UL, 0x27eecea9UL, 0x2ae5c0a0UL, +0xb13c7a47UL, 0xbc37744eUL, 0xab2a6655UL, 0xa621685cUL, 0x85104263UL, 0x881b4c6aUL, 0x9f065e71UL, 0x920d5078UL, +0xd9640a0fUL, 0xd46f0406UL, 0xc372161dUL, 0xce791814UL, 0xed48322bUL, 0xe0433c22UL, 0xf75e2e39UL, 0xfa552030UL, +0xb701ec9aUL, 0xba0ae293UL, 0xad17f088UL, 0xa01cfe81UL, 0x832dd4beUL, 0x8e26dab7UL, 0x993bc8acUL, 0x9430c6a5UL, +0xdf599cd2UL, 0xd25292dbUL, 0xc54f80c0UL, 0xc8448ec9UL, 0xeb75a4f6UL, 0xe67eaaffUL, 0xf163b8e4UL, 0xfc68b6edUL, +0x67b10c0aUL, 0x6aba0203UL, 0x7da71018UL, 0x70ac1e11UL, 0x539d342eUL, 0x5e963a27UL, 0x498b283cUL, 0x44802635UL, +0x0fe97c42UL, 0x02e2724bUL, 0x15ff6050UL, 0x18f46e59UL, 0x3bc54466UL, 0x36ce4a6fUL, 0x21d35874UL, 0x2cd8567dUL, +0x0c7a37a1UL, 0x017139a8UL, 0x166c2bb3UL, 0x1b6725baUL, 0x38560f85UL, 0x355d018cUL, 0x22401397UL, 0x2f4b1d9eUL, +0x642247e9UL, 0x692949e0UL, 0x7e345bfbUL, 0x733f55f2UL, 0x500e7fcdUL, 0x5d0571c4UL, 0x4a1863dfUL, 0x47136dd6UL, +0xdccad731UL, 0xd1c1d938UL, 0xc6dccb23UL, 0xcbd7c52aUL, 0xe8e6ef15UL, 0xe5ede11cUL, 0xf2f0f307UL, 0xfffbfd0eUL, +0xb492a779UL, 0xb999a970UL, 0xae84bb6bUL, 0xa38fb562UL, 0x80be9f5dUL, 0x8db59154UL, 0x9aa8834fUL, 0x97a38d46UL +}; + +static const ulong32 Tks3[] = { +0x00000000UL, 0x090d0b0eUL, 0x121a161cUL, 0x1b171d12UL, 0x24342c38UL, 0x2d392736UL, 0x362e3a24UL, 0x3f23312aUL, +0x48685870UL, 0x4165537eUL, 0x5a724e6cUL, 0x537f4562UL, 0x6c5c7448UL, 0x65517f46UL, 0x7e466254UL, 0x774b695aUL, +0x90d0b0e0UL, 0x99ddbbeeUL, 0x82caa6fcUL, 0x8bc7adf2UL, 0xb4e49cd8UL, 0xbde997d6UL, 0xa6fe8ac4UL, 0xaff381caUL, +0xd8b8e890UL, 0xd1b5e39eUL, 0xcaa2fe8cUL, 0xc3aff582UL, 0xfc8cc4a8UL, 0xf581cfa6UL, 0xee96d2b4UL, 0xe79bd9baUL, +0x3bbb7bdbUL, 0x32b670d5UL, 0x29a16dc7UL, 0x20ac66c9UL, 0x1f8f57e3UL, 0x16825cedUL, 0x0d9541ffUL, 0x04984af1UL, +0x73d323abUL, 0x7ade28a5UL, 0x61c935b7UL, 0x68c43eb9UL, 0x57e70f93UL, 0x5eea049dUL, 0x45fd198fUL, 0x4cf01281UL, +0xab6bcb3bUL, 0xa266c035UL, 0xb971dd27UL, 0xb07cd629UL, 0x8f5fe703UL, 0x8652ec0dUL, 0x9d45f11fUL, 0x9448fa11UL, +0xe303934bUL, 0xea0e9845UL, 0xf1198557UL, 0xf8148e59UL, 0xc737bf73UL, 0xce3ab47dUL, 0xd52da96fUL, 0xdc20a261UL, +0x766df6adUL, 0x7f60fda3UL, 0x6477e0b1UL, 0x6d7aebbfUL, 0x5259da95UL, 0x5b54d19bUL, 0x4043cc89UL, 0x494ec787UL, +0x3e05aeddUL, 0x3708a5d3UL, 0x2c1fb8c1UL, 0x2512b3cfUL, 0x1a3182e5UL, 0x133c89ebUL, 0x082b94f9UL, 0x01269ff7UL, +0xe6bd464dUL, 0xefb04d43UL, 0xf4a75051UL, 0xfdaa5b5fUL, 0xc2896a75UL, 0xcb84617bUL, 0xd0937c69UL, 0xd99e7767UL, +0xaed51e3dUL, 0xa7d81533UL, 0xbccf0821UL, 0xb5c2032fUL, 0x8ae13205UL, 0x83ec390bUL, 0x98fb2419UL, 0x91f62f17UL, +0x4dd68d76UL, 0x44db8678UL, 0x5fcc9b6aUL, 0x56c19064UL, 0x69e2a14eUL, 0x60efaa40UL, 0x7bf8b752UL, 0x72f5bc5cUL, +0x05bed506UL, 0x0cb3de08UL, 0x17a4c31aUL, 0x1ea9c814UL, 0x218af93eUL, 0x2887f230UL, 0x3390ef22UL, 0x3a9de42cUL, +0xdd063d96UL, 0xd40b3698UL, 0xcf1c2b8aUL, 0xc6112084UL, 0xf93211aeUL, 0xf03f1aa0UL, 0xeb2807b2UL, 0xe2250cbcUL, +0x956e65e6UL, 0x9c636ee8UL, 0x877473faUL, 0x8e7978f4UL, 0xb15a49deUL, 0xb85742d0UL, 0xa3405fc2UL, 0xaa4d54ccUL, +0xecdaf741UL, 0xe5d7fc4fUL, 0xfec0e15dUL, 0xf7cdea53UL, 0xc8eedb79UL, 0xc1e3d077UL, 0xdaf4cd65UL, 0xd3f9c66bUL, +0xa4b2af31UL, 0xadbfa43fUL, 0xb6a8b92dUL, 0xbfa5b223UL, 0x80868309UL, 0x898b8807UL, 0x929c9515UL, 0x9b919e1bUL, +0x7c0a47a1UL, 0x75074cafUL, 0x6e1051bdUL, 0x671d5ab3UL, 0x583e6b99UL, 0x51336097UL, 0x4a247d85UL, 0x4329768bUL, +0x34621fd1UL, 0x3d6f14dfUL, 0x267809cdUL, 0x2f7502c3UL, 0x105633e9UL, 0x195b38e7UL, 0x024c25f5UL, 0x0b412efbUL, +0xd7618c9aUL, 0xde6c8794UL, 0xc57b9a86UL, 0xcc769188UL, 0xf355a0a2UL, 0xfa58abacUL, 0xe14fb6beUL, 0xe842bdb0UL, +0x9f09d4eaUL, 0x9604dfe4UL, 0x8d13c2f6UL, 0x841ec9f8UL, 0xbb3df8d2UL, 0xb230f3dcUL, 0xa927eeceUL, 0xa02ae5c0UL, +0x47b13c7aUL, 0x4ebc3774UL, 0x55ab2a66UL, 0x5ca62168UL, 0x63851042UL, 0x6a881b4cUL, 0x719f065eUL, 0x78920d50UL, +0x0fd9640aUL, 0x06d46f04UL, 0x1dc37216UL, 0x14ce7918UL, 0x2bed4832UL, 0x22e0433cUL, 0x39f75e2eUL, 0x30fa5520UL, +0x9ab701ecUL, 0x93ba0ae2UL, 0x88ad17f0UL, 0x81a01cfeUL, 0xbe832dd4UL, 0xb78e26daUL, 0xac993bc8UL, 0xa59430c6UL, +0xd2df599cUL, 0xdbd25292UL, 0xc0c54f80UL, 0xc9c8448eUL, 0xf6eb75a4UL, 0xffe67eaaUL, 0xe4f163b8UL, 0xedfc68b6UL, +0x0a67b10cUL, 0x036aba02UL, 0x187da710UL, 0x1170ac1eUL, 0x2e539d34UL, 0x275e963aUL, 0x3c498b28UL, 0x35448026UL, +0x420fe97cUL, 0x4b02e272UL, 0x5015ff60UL, 0x5918f46eUL, 0x663bc544UL, 0x6f36ce4aUL, 0x7421d358UL, 0x7d2cd856UL, +0xa10c7a37UL, 0xa8017139UL, 0xb3166c2bUL, 0xba1b6725UL, 0x8538560fUL, 0x8c355d01UL, 0x97224013UL, 0x9e2f4b1dUL, +0xe9642247UL, 0xe0692949UL, 0xfb7e345bUL, 0xf2733f55UL, 0xcd500e7fUL, 0xc45d0571UL, 0xdf4a1863UL, 0xd647136dUL, +0x31dccad7UL, 0x38d1c1d9UL, 0x23c6dccbUL, 0x2acbd7c5UL, 0x15e8e6efUL, 0x1ce5ede1UL, 0x07f2f0f3UL, 0x0efffbfdUL, +0x79b492a7UL, 0x70b999a9UL, 0x6bae84bbUL, 0x62a38fb5UL, 0x5d80be9fUL, 0x548db591UL, 0x4f9aa883UL, 0x4697a38dUL +}; + +#endif /* ENCRYPT_ONLY */ + +#endif /* SMALL CODE */ + +static const ulong32 rcon[] = { + 0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL, + 0x10000000UL, 0x20000000UL, 0x40000000UL, 0x80000000UL, + 0x1B000000UL, 0x36000000UL, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; + +#endif /* __LTC_AES_TAB_C__ */ + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* AES implementation by Tom St Denis + * + * Derived from the Public Domain source code by + +--- + * rijndael-alg-fst.c + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto +--- + */ +/** + @file aes.c + Implementation of AES +*/ + + + +#ifdef LTC_RIJNDAEL + +#ifndef ENCRYPT_ONLY + +#define SETUP rijndael_setup +#define ECB_ENC rijndael_ecb_encrypt +#define ECB_DEC rijndael_ecb_decrypt +#define ECB_DONE rijndael_done +#define ECB_TEST rijndael_test +#define ECB_KS rijndael_keysize + +const struct ltc_cipher_descriptor rijndael_desc = +{ + "rijndael", + 6, + 16, 32, 16, 10, + SETUP, ECB_ENC, ECB_DEC, ECB_TEST, ECB_DONE, ECB_KS, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +const struct ltc_cipher_descriptor aes_desc = +{ + "aes", + 6, + 16, 32, 16, 10, + SETUP, ECB_ENC, ECB_DEC, ECB_TEST, ECB_DONE, ECB_KS, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +#else + +#define SETUP rijndael_enc_setup +#define ECB_ENC rijndael_enc_ecb_encrypt +#define ECB_KS rijndael_enc_keysize +#define ECB_DONE rijndael_enc_done + +const struct ltc_cipher_descriptor rijndael_enc_desc = +{ + "rijndael", + 6, + 16, 32, 16, 10, + SETUP, ECB_ENC, NULL, NULL, ECB_DONE, ECB_KS, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +const struct ltc_cipher_descriptor aes_enc_desc = +{ + "aes", + 6, + 16, 32, 16, 10, + SETUP, ECB_ENC, NULL, NULL, ECB_DONE, ECB_KS, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +#endif + +#define __LTC_AES_TAB_C__ + + +static ulong32 setup_mix(ulong32 temp) +{ + return (Te4_3[extract_byte(temp, 2)]) ^ + (Te4_2[extract_byte(temp, 1)]) ^ + (Te4_1[extract_byte(temp, 0)]) ^ + (Te4_0[extract_byte(temp, 3)]); +} + +#ifndef ENCRYPT_ONLY +#ifdef LTC_SMALL_CODE +static ulong32 setup_mix2(ulong32 temp) +{ + return Td0(255 & Te4[extract_byte(temp, 3)]) ^ + Td1(255 & Te4[extract_byte(temp, 2)]) ^ + Td2(255 & Te4[extract_byte(temp, 1)]) ^ + Td3(255 & Te4[extract_byte(temp, 0)]); +} +#endif +#endif + + /** + Initialize the AES (Rijndael) block cipher + @param key The symmetric key you wish to pass + @param keylen The key length in bytes + @param num_rounds The number of rounds desired (0 for default) + @param skey The key in as scheduled by this function. + @return CRYPT_OK if successful + */ +int SETUP(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey) +{ + int i; + ulong32 temp, *rk; +#ifndef ENCRYPT_ONLY + ulong32 *rrk; +#endif + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(skey != NULL); + + if (keylen != 16 && keylen != 24 && keylen != 32) { + return CRYPT_INVALID_KEYSIZE; + } + + if (num_rounds != 0 && num_rounds != (10 + ((keylen/8)-2)*2)) { + return CRYPT_INVALID_ROUNDS; + } + + skey->rijndael.Nr = 10 + ((keylen/8)-2)*2; + + /* setup the forward key */ + i = 0; + rk = skey->rijndael.eK; + LOAD32H(rk[0], key ); + LOAD32H(rk[1], key + 4); + LOAD32H(rk[2], key + 8); + LOAD32H(rk[3], key + 12); + if (keylen == 16) { + for (;;) { + temp = rk[3]; + rk[4] = rk[0] ^ setup_mix(temp) ^ rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if (++i == 10) { + break; + } + rk += 4; + } + } else if (keylen == 24) { + LOAD32H(rk[4], key + 16); + LOAD32H(rk[5], key + 20); + for (;;) { + #ifdef _MSC_VER + temp = skey->rijndael.eK[rk - skey->rijndael.eK + 5]; + #else + temp = rk[5]; + #endif + rk[ 6] = rk[ 0] ^ setup_mix(temp) ^ rcon[i]; + rk[ 7] = rk[ 1] ^ rk[ 6]; + rk[ 8] = rk[ 2] ^ rk[ 7]; + rk[ 9] = rk[ 3] ^ rk[ 8]; + if (++i == 8) { + break; + } + rk[10] = rk[ 4] ^ rk[ 9]; + rk[11] = rk[ 5] ^ rk[10]; + rk += 6; + } + } else if (keylen == 32) { + LOAD32H(rk[4], key + 16); + LOAD32H(rk[5], key + 20); + LOAD32H(rk[6], key + 24); + LOAD32H(rk[7], key + 28); + for (;;) { + #ifdef _MSC_VER + temp = skey->rijndael.eK[rk - skey->rijndael.eK + 7]; + #else + temp = rk[7]; + #endif + rk[ 8] = rk[ 0] ^ setup_mix(temp) ^ rcon[i]; + rk[ 9] = rk[ 1] ^ rk[ 8]; + rk[10] = rk[ 2] ^ rk[ 9]; + rk[11] = rk[ 3] ^ rk[10]; + if (++i == 7) { + break; + } + temp = rk[11]; + rk[12] = rk[ 4] ^ setup_mix(RORc(temp, 8)); + rk[13] = rk[ 5] ^ rk[12]; + rk[14] = rk[ 6] ^ rk[13]; + rk[15] = rk[ 7] ^ rk[14]; + rk += 8; + } + } else { + /* this can't happen */ + /* coverity[dead_error_line] */ + return CRYPT_ERROR; + } + +#ifndef ENCRYPT_ONLY + /* setup the inverse key now */ + rk = skey->rijndael.dK; + rrk = skey->rijndael.eK + (28 + keylen) - 4; + + /* apply the inverse MixColumn transform to all round keys but the first and the last: */ + /* copy first */ + *rk++ = *rrk++; + *rk++ = *rrk++; + *rk++ = *rrk++; + *rk = *rrk; + rk -= 3; rrk -= 3; + + for (i = 1; i < skey->rijndael.Nr; i++) { + rrk -= 4; + rk += 4; + #ifdef LTC_SMALL_CODE + temp = rrk[0]; + rk[0] = setup_mix2(temp); + temp = rrk[1]; + rk[1] = setup_mix2(temp); + temp = rrk[2]; + rk[2] = setup_mix2(temp); + temp = rrk[3]; + rk[3] = setup_mix2(temp); + #else + temp = rrk[0]; + rk[0] = + Tks0[extract_byte(temp, 3)] ^ + Tks1[extract_byte(temp, 2)] ^ + Tks2[extract_byte(temp, 1)] ^ + Tks3[extract_byte(temp, 0)]; + temp = rrk[1]; + rk[1] = + Tks0[extract_byte(temp, 3)] ^ + Tks1[extract_byte(temp, 2)] ^ + Tks2[extract_byte(temp, 1)] ^ + Tks3[extract_byte(temp, 0)]; + temp = rrk[2]; + rk[2] = + Tks0[extract_byte(temp, 3)] ^ + Tks1[extract_byte(temp, 2)] ^ + Tks2[extract_byte(temp, 1)] ^ + Tks3[extract_byte(temp, 0)]; + temp = rrk[3]; + rk[3] = + Tks0[extract_byte(temp, 3)] ^ + Tks1[extract_byte(temp, 2)] ^ + Tks2[extract_byte(temp, 1)] ^ + Tks3[extract_byte(temp, 0)]; + #endif + + } + + /* copy last */ + rrk -= 4; + rk += 4; + *rk++ = *rrk++; + *rk++ = *rrk++; + *rk++ = *rrk++; + *rk = *rrk; +#endif /* ENCRYPT_ONLY */ + + return CRYPT_OK; +} + +/** + Encrypts a block of text with AES + @param pt The input plaintext (16 bytes) + @param ct The output ciphertext (16 bytes) + @param skey The key as scheduled + @return CRYPT_OK if successful +*/ +#ifdef LTC_CLEAN_STACK +static int _rijndael_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey) +#else +int ECB_ENC(const unsigned char *pt, unsigned char *ct, symmetric_key *skey) +#endif +{ + ulong32 s0, s1, s2, s3, t0, t1, t2, t3, *rk; + int Nr, r; + + LTC_ARGCHK(pt != NULL); + LTC_ARGCHK(ct != NULL); + LTC_ARGCHK(skey != NULL); + + Nr = skey->rijndael.Nr; + rk = skey->rijndael.eK; + + /* + * map byte array block to cipher state + * and add initial round key: + */ + LOAD32H(s0, pt ); s0 ^= rk[0]; + LOAD32H(s1, pt + 4); s1 ^= rk[1]; + LOAD32H(s2, pt + 8); s2 ^= rk[2]; + LOAD32H(s3, pt + 12); s3 ^= rk[3]; + +#ifdef LTC_SMALL_CODE + + for (r = 0; ; r++) { + rk += 4; + t0 = + Te0(extract_byte(s0, 3)) ^ + Te1(extract_byte(s1, 2)) ^ + Te2(extract_byte(s2, 1)) ^ + Te3(extract_byte(s3, 0)) ^ + rk[0]; + t1 = + Te0(extract_byte(s1, 3)) ^ + Te1(extract_byte(s2, 2)) ^ + Te2(extract_byte(s3, 1)) ^ + Te3(extract_byte(s0, 0)) ^ + rk[1]; + t2 = + Te0(extract_byte(s2, 3)) ^ + Te1(extract_byte(s3, 2)) ^ + Te2(extract_byte(s0, 1)) ^ + Te3(extract_byte(s1, 0)) ^ + rk[2]; + t3 = + Te0(extract_byte(s3, 3)) ^ + Te1(extract_byte(s0, 2)) ^ + Te2(extract_byte(s1, 1)) ^ + Te3(extract_byte(s2, 0)) ^ + rk[3]; + if (r == Nr-2) { + break; + } + s0 = t0; s1 = t1; s2 = t2; s3 = t3; + } + rk += 4; + +#else + + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = + Te0(extract_byte(s0, 3)) ^ + Te1(extract_byte(s1, 2)) ^ + Te2(extract_byte(s2, 1)) ^ + Te3(extract_byte(s3, 0)) ^ + rk[4]; + t1 = + Te0(extract_byte(s1, 3)) ^ + Te1(extract_byte(s2, 2)) ^ + Te2(extract_byte(s3, 1)) ^ + Te3(extract_byte(s0, 0)) ^ + rk[5]; + t2 = + Te0(extract_byte(s2, 3)) ^ + Te1(extract_byte(s3, 2)) ^ + Te2(extract_byte(s0, 1)) ^ + Te3(extract_byte(s1, 0)) ^ + rk[6]; + t3 = + Te0(extract_byte(s3, 3)) ^ + Te1(extract_byte(s0, 2)) ^ + Te2(extract_byte(s1, 1)) ^ + Te3(extract_byte(s2, 0)) ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Te0(extract_byte(t0, 3)) ^ + Te1(extract_byte(t1, 2)) ^ + Te2(extract_byte(t2, 1)) ^ + Te3(extract_byte(t3, 0)) ^ + rk[0]; + s1 = + Te0(extract_byte(t1, 3)) ^ + Te1(extract_byte(t2, 2)) ^ + Te2(extract_byte(t3, 1)) ^ + Te3(extract_byte(t0, 0)) ^ + rk[1]; + s2 = + Te0(extract_byte(t2, 3)) ^ + Te1(extract_byte(t3, 2)) ^ + Te2(extract_byte(t0, 1)) ^ + Te3(extract_byte(t1, 0)) ^ + rk[2]; + s3 = + Te0(extract_byte(t3, 3)) ^ + Te1(extract_byte(t0, 2)) ^ + Te2(extract_byte(t1, 1)) ^ + Te3(extract_byte(t2, 0)) ^ + rk[3]; + } + +#endif + + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te4_3[extract_byte(t0, 3)]) ^ + (Te4_2[extract_byte(t1, 2)]) ^ + (Te4_1[extract_byte(t2, 1)]) ^ + (Te4_0[extract_byte(t3, 0)]) ^ + rk[0]; + STORE32H(s0, ct); + s1 = + (Te4_3[extract_byte(t1, 3)]) ^ + (Te4_2[extract_byte(t2, 2)]) ^ + (Te4_1[extract_byte(t3, 1)]) ^ + (Te4_0[extract_byte(t0, 0)]) ^ + rk[1]; + STORE32H(s1, ct+4); + s2 = + (Te4_3[extract_byte(t2, 3)]) ^ + (Te4_2[extract_byte(t3, 2)]) ^ + (Te4_1[extract_byte(t0, 1)]) ^ + (Te4_0[extract_byte(t1, 0)]) ^ + rk[2]; + STORE32H(s2, ct+8); + s3 = + (Te4_3[extract_byte(t3, 3)]) ^ + (Te4_2[extract_byte(t0, 2)]) ^ + (Te4_1[extract_byte(t1, 1)]) ^ + (Te4_0[extract_byte(t2, 0)]) ^ + rk[3]; + STORE32H(s3, ct+12); + + return CRYPT_OK; +} + +#ifdef LTC_CLEAN_STACK +int ECB_ENC(const unsigned char *pt, unsigned char *ct, symmetric_key *skey) +{ + int err = _rijndael_ecb_encrypt(pt, ct, skey); + burn_stack(sizeof(unsigned long)*8 + sizeof(unsigned long*) + sizeof(int)*2); + return err; +} +#endif + +#ifndef ENCRYPT_ONLY + +/** + Decrypts a block of text with AES + @param ct The input ciphertext (16 bytes) + @param pt The output plaintext (16 bytes) + @param skey The key as scheduled + @return CRYPT_OK if successful +*/ +#ifdef LTC_CLEAN_STACK +static int _rijndael_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey) +#else +int ECB_DEC(const unsigned char *ct, unsigned char *pt, symmetric_key *skey) +#endif +{ + ulong32 s0, s1, s2, s3, t0, t1, t2, t3, *rk; + int Nr, r; + + LTC_ARGCHK(pt != NULL); + LTC_ARGCHK(ct != NULL); + LTC_ARGCHK(skey != NULL); + + Nr = skey->rijndael.Nr; + rk = skey->rijndael.dK; + + /* + * map byte array block to cipher state + * and add initial round key: + */ + LOAD32H(s0, ct ); s0 ^= rk[0]; + LOAD32H(s1, ct + 4); s1 ^= rk[1]; + LOAD32H(s2, ct + 8); s2 ^= rk[2]; + LOAD32H(s3, ct + 12); s3 ^= rk[3]; + +#ifdef LTC_SMALL_CODE + for (r = 0; ; r++) { + rk += 4; + t0 = + Td0(extract_byte(s0, 3)) ^ + Td1(extract_byte(s3, 2)) ^ + Td2(extract_byte(s2, 1)) ^ + Td3(extract_byte(s1, 0)) ^ + rk[0]; + t1 = + Td0(extract_byte(s1, 3)) ^ + Td1(extract_byte(s0, 2)) ^ + Td2(extract_byte(s3, 1)) ^ + Td3(extract_byte(s2, 0)) ^ + rk[1]; + t2 = + Td0(extract_byte(s2, 3)) ^ + Td1(extract_byte(s1, 2)) ^ + Td2(extract_byte(s0, 1)) ^ + Td3(extract_byte(s3, 0)) ^ + rk[2]; + t3 = + Td0(extract_byte(s3, 3)) ^ + Td1(extract_byte(s2, 2)) ^ + Td2(extract_byte(s1, 1)) ^ + Td3(extract_byte(s0, 0)) ^ + rk[3]; + if (r == Nr-2) { + break; + } + s0 = t0; s1 = t1; s2 = t2; s3 = t3; + } + rk += 4; + +#else + + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + + t0 = + Td0(extract_byte(s0, 3)) ^ + Td1(extract_byte(s3, 2)) ^ + Td2(extract_byte(s2, 1)) ^ + Td3(extract_byte(s1, 0)) ^ + rk[4]; + t1 = + Td0(extract_byte(s1, 3)) ^ + Td1(extract_byte(s0, 2)) ^ + Td2(extract_byte(s3, 1)) ^ + Td3(extract_byte(s2, 0)) ^ + rk[5]; + t2 = + Td0(extract_byte(s2, 3)) ^ + Td1(extract_byte(s1, 2)) ^ + Td2(extract_byte(s0, 1)) ^ + Td3(extract_byte(s3, 0)) ^ + rk[6]; + t3 = + Td0(extract_byte(s3, 3)) ^ + Td1(extract_byte(s2, 2)) ^ + Td2(extract_byte(s1, 1)) ^ + Td3(extract_byte(s0, 0)) ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + + s0 = + Td0(extract_byte(t0, 3)) ^ + Td1(extract_byte(t3, 2)) ^ + Td2(extract_byte(t2, 1)) ^ + Td3(extract_byte(t1, 0)) ^ + rk[0]; + s1 = + Td0(extract_byte(t1, 3)) ^ + Td1(extract_byte(t0, 2)) ^ + Td2(extract_byte(t3, 1)) ^ + Td3(extract_byte(t2, 0)) ^ + rk[1]; + s2 = + Td0(extract_byte(t2, 3)) ^ + Td1(extract_byte(t1, 2)) ^ + Td2(extract_byte(t0, 1)) ^ + Td3(extract_byte(t3, 0)) ^ + rk[2]; + s3 = + Td0(extract_byte(t3, 3)) ^ + Td1(extract_byte(t2, 2)) ^ + Td2(extract_byte(t1, 1)) ^ + Td3(extract_byte(t0, 0)) ^ + rk[3]; + } +#endif + + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Td4[extract_byte(t0, 3)] & 0xff000000) ^ + (Td4[extract_byte(t3, 2)] & 0x00ff0000) ^ + (Td4[extract_byte(t2, 1)] & 0x0000ff00) ^ + (Td4[extract_byte(t1, 0)] & 0x000000ff) ^ + rk[0]; + STORE32H(s0, pt); + s1 = + (Td4[extract_byte(t1, 3)] & 0xff000000) ^ + (Td4[extract_byte(t0, 2)] & 0x00ff0000) ^ + (Td4[extract_byte(t3, 1)] & 0x0000ff00) ^ + (Td4[extract_byte(t2, 0)] & 0x000000ff) ^ + rk[1]; + STORE32H(s1, pt+4); + s2 = + (Td4[extract_byte(t2, 3)] & 0xff000000) ^ + (Td4[extract_byte(t1, 2)] & 0x00ff0000) ^ + (Td4[extract_byte(t0, 1)] & 0x0000ff00) ^ + (Td4[extract_byte(t3, 0)] & 0x000000ff) ^ + rk[2]; + STORE32H(s2, pt+8); + s3 = + (Td4[extract_byte(t3, 3)] & 0xff000000) ^ + (Td4[extract_byte(t2, 2)] & 0x00ff0000) ^ + (Td4[extract_byte(t1, 1)] & 0x0000ff00) ^ + (Td4[extract_byte(t0, 0)] & 0x000000ff) ^ + rk[3]; + STORE32H(s3, pt+12); + + return CRYPT_OK; +} + + +#ifdef LTC_CLEAN_STACK +int ECB_DEC(const unsigned char *ct, unsigned char *pt, symmetric_key *skey) +{ + int err = _rijndael_ecb_decrypt(ct, pt, skey); + burn_stack(sizeof(unsigned long)*8 + sizeof(unsigned long*) + sizeof(int)*2); + return err; +} +#endif + +/** + Performs a self-test of the AES block cipher + @return CRYPT_OK if functional, CRYPT_NOP if self-test has been disabled +*/ +int ECB_TEST(void) +{ + #ifndef LTC_TEST + return CRYPT_NOP; + #else + int err; + static const struct { + int keylen; + unsigned char key[32], pt[16], ct[16]; + } tests[] = { + { 16, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, + { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }, + { 0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30, + 0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a } + }, { + 24, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }, + { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }, + { 0xdd, 0xa9, 0x7c, 0xa4, 0x86, 0x4c, 0xdf, 0xe0, + 0x6e, 0xaf, 0x70, 0xa0, 0xec, 0x0d, 0x71, 0x91 } + }, { + 32, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f }, + { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }, + { 0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf, + 0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89 } + } + }; + + symmetric_key key; + unsigned char tmp[2][16]; + int i, y; + + for (i = 0; i < (int)(sizeof(tests)/sizeof(tests[0])); i++) { + zeromem(&key, sizeof(key)); + if ((err = rijndael_setup(tests[i].key, tests[i].keylen, 0, &key)) != CRYPT_OK) { + return err; + } + + rijndael_ecb_encrypt(tests[i].pt, tmp[0], &key); + rijndael_ecb_decrypt(tmp[0], tmp[1], &key); + if (XMEMCMP(tmp[0], tests[i].ct, 16) || XMEMCMP(tmp[1], tests[i].pt, 16)) { +#if 0 + printf("\n\nTest %d failed\n", i); + if (XMEMCMP(tmp[0], tests[i].ct, 16)) { + printf("CT: "); + for (i = 0; i < 16; i++) { + printf("%02x ", tmp[0][i]); + } + printf("\n"); + } else { + printf("PT: "); + for (i = 0; i < 16; i++) { + printf("%02x ", tmp[1][i]); + } + printf("\n"); + } +#endif + return CRYPT_FAIL_TESTVECTOR; + } + + /* now see if we can encrypt all zero bytes 1000 times, decrypt and come back where we started */ + for (y = 0; y < 16; y++) tmp[0][y] = 0; + for (y = 0; y < 1000; y++) rijndael_ecb_encrypt(tmp[0], tmp[0], &key); + for (y = 0; y < 1000; y++) rijndael_ecb_decrypt(tmp[0], tmp[0], &key); + for (y = 0; y < 16; y++) if (tmp[0][y] != 0) return CRYPT_FAIL_TESTVECTOR; + } + return CRYPT_OK; + #endif +} + +#endif /* ENCRYPT_ONLY */ + + +/** Terminate the context + @param skey The scheduled key +*/ +void ECB_DONE(symmetric_key *skey) +{ + //LTC_UNUSED_PARAM(skey); +} + + +/** + Gets suitable key size + @param keysize [in/out] The length of the recommended key (in bytes). This function will store the suitable size back in this variable. + @return CRYPT_OK if the input key size is acceptable. +*/ +int ECB_KS(int *keysize) +{ + LTC_ARGCHK(keysize != NULL); + + if (*keysize < 16) + return CRYPT_INVALID_KEYSIZE; + if (*keysize < 24) { + *keysize = 16; + return CRYPT_OK; + } else if (*keysize < 32) { + *keysize = 24; + return CRYPT_OK; + } else { + *keysize = 32; + return CRYPT_OK; + } +} + +#endif + + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file cbc_decrypt.c + CBC implementation, encrypt block, Tom St Denis +*/ + + +#ifdef LTC_CBC_MODE + +/** + CBC decrypt + @param ct Ciphertext + @param pt [out] Plaintext + @param len The number of bytes to process (must be multiple of block length) + @param cbc CBC state + @return CRYPT_OK if successful +*/ +int cbc_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CBC *cbc) +{ + int x, err; + unsigned char tmp[16]; +#ifdef LTC_FAST + LTC_FAST_TYPE tmpy; +#else + unsigned char tmpy; +#endif + + LTC_ARGCHK(pt != NULL); + LTC_ARGCHK(ct != NULL); + LTC_ARGCHK(cbc != NULL); + + if ((err = cipher_is_valid(cbc->cipher)) != CRYPT_OK) { + return err; + } + + /* is blocklen valid? */ + if (cbc->blocklen < 1 || cbc->blocklen > (int)sizeof(cbc->IV)) { + return CRYPT_INVALID_ARG; + } + + if (len % cbc->blocklen) { + return CRYPT_INVALID_ARG; + } +#ifdef LTC_FAST + if (cbc->blocklen % sizeof(LTC_FAST_TYPE)) { + return CRYPT_INVALID_ARG; + } +#endif + + if (cipher_descriptor[cbc->cipher].accel_cbc_decrypt != NULL) { + return cipher_descriptor[cbc->cipher].accel_cbc_decrypt(ct, pt, len / cbc->blocklen, cbc->IV, &cbc->key); + } else { + while (len) { + /* decrypt */ + if ((err = cipher_descriptor[cbc->cipher].ecb_decrypt(ct, tmp, &cbc->key)) != CRYPT_OK) { + return err; + } + + /* xor IV against plaintext */ + #if defined(LTC_FAST) + for (x = 0; x < cbc->blocklen; x += sizeof(LTC_FAST_TYPE)) { + tmpy = *((LTC_FAST_TYPE*)((unsigned char *)cbc->IV + x)) ^ *((LTC_FAST_TYPE*)((unsigned char *)tmp + x)); + *((LTC_FAST_TYPE*)((unsigned char *)cbc->IV + x)) = *((LTC_FAST_TYPE*)((unsigned char *)ct + x)); + *((LTC_FAST_TYPE*)((unsigned char *)pt + x)) = tmpy; + } + #else + for (x = 0; x < cbc->blocklen; x++) { + tmpy = tmp[x] ^ cbc->IV[x]; + cbc->IV[x] = ct[x]; + pt[x] = tmpy; + } + #endif + + ct += cbc->blocklen; + pt += cbc->blocklen; + len -= cbc->blocklen; + } + } + return CRYPT_OK; +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file cbc_done.c + CBC implementation, finish chain, Tom St Denis +*/ + +#ifdef LTC_CBC_MODE + +/** Terminate the chain + @param cbc The CBC chain to terminate + @return CRYPT_OK on success +*/ +int cbc_done(symmetric_CBC *cbc) +{ + int err; + LTC_ARGCHK(cbc != NULL); + + if ((err = cipher_is_valid(cbc->cipher)) != CRYPT_OK) { + return err; + } + cipher_descriptor[cbc->cipher].done(&cbc->key); + return CRYPT_OK; +} + + + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file cbc_encrypt.c + CBC implementation, encrypt block, Tom St Denis +*/ + + +#ifdef LTC_CBC_MODE + +/** + CBC encrypt + @param pt Plaintext + @param ct [out] Ciphertext + @param len The number of bytes to process (must be multiple of block length) + @param cbc CBC state + @return CRYPT_OK if successful +*/ +int cbc_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CBC *cbc) +{ + int x, err; + + LTC_ARGCHK(pt != NULL); + LTC_ARGCHK(ct != NULL); + LTC_ARGCHK(cbc != NULL); + + if ((err = cipher_is_valid(cbc->cipher)) != CRYPT_OK) { + return err; + } + + /* is blocklen valid? */ + if (cbc->blocklen < 1 || cbc->blocklen > (int)sizeof(cbc->IV)) { + return CRYPT_INVALID_ARG; + } + + if (len % cbc->blocklen) { + return CRYPT_INVALID_ARG; + } +#ifdef LTC_FAST + if (cbc->blocklen % sizeof(LTC_FAST_TYPE)) { + return CRYPT_INVALID_ARG; + } +#endif + + if (cipher_descriptor[cbc->cipher].accel_cbc_encrypt != NULL) { + return cipher_descriptor[cbc->cipher].accel_cbc_encrypt(pt, ct, len / cbc->blocklen, cbc->IV, &cbc->key); + } else { + while (len) { + /* xor IV against plaintext */ + #if defined(LTC_FAST) + for (x = 0; x < cbc->blocklen; x += sizeof(LTC_FAST_TYPE)) { + *((LTC_FAST_TYPE*)((unsigned char *)cbc->IV + x)) ^= *((LTC_FAST_TYPE*)((unsigned char *)pt + x)); + } + #else + for (x = 0; x < cbc->blocklen; x++) { + cbc->IV[x] ^= pt[x]; + } + #endif + + /* encrypt */ + if ((err = cipher_descriptor[cbc->cipher].ecb_encrypt(cbc->IV, ct, &cbc->key)) != CRYPT_OK) { + return err; + } + + /* store IV [ciphertext] for a future block */ + #if defined(LTC_FAST) + for (x = 0; x < cbc->blocklen; x += sizeof(LTC_FAST_TYPE)) { + *((LTC_FAST_TYPE*)((unsigned char *)cbc->IV + x)) = *((LTC_FAST_TYPE*)((unsigned char *)ct + x)); + } + #else + for (x = 0; x < cbc->blocklen; x++) { + cbc->IV[x] = ct[x]; + } + #endif + + ct += cbc->blocklen; + pt += cbc->blocklen; + len -= cbc->blocklen; + } + } + return CRYPT_OK; +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file cbc_getiv.c + CBC implementation, get IV, Tom St Denis +*/ + +#ifdef LTC_CBC_MODE + +/** + Get the current initial vector + @param IV [out] The destination of the initial vector + @param len [in/out] The max size and resulting size of the initial vector + @param cbc The CBC state + @return CRYPT_OK if successful +*/ +int cbc_getiv(unsigned char *IV, unsigned long *len, symmetric_CBC *cbc) +{ + LTC_ARGCHK(IV != NULL); + LTC_ARGCHK(len != NULL); + LTC_ARGCHK(cbc != NULL); + if ((unsigned long)cbc->blocklen > *len) { + *len = cbc->blocklen; + return CRYPT_BUFFER_OVERFLOW; + } + XMEMCPY(IV, cbc->IV, cbc->blocklen); + *len = cbc->blocklen; + + return CRYPT_OK; +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file cbc_setiv.c + CBC implementation, set IV, Tom St Denis +*/ + + +#ifdef LTC_CBC_MODE + +/** + Set an initial vector + @param IV The initial vector + @param len The length of the vector (in octets) + @param cbc The CBC state + @return CRYPT_OK if successful +*/ +int cbc_setiv(const unsigned char *IV, unsigned long len, symmetric_CBC *cbc) +{ + LTC_ARGCHK(IV != NULL); + LTC_ARGCHK(cbc != NULL); + if (len != (unsigned long)cbc->blocklen) { + return CRYPT_INVALID_ARG; + } + XMEMCPY(cbc->IV, IV, len); + return CRYPT_OK; +} + +#endif + + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/** + @file cbc_start.c + CBC implementation, start chain, Tom St Denis +*/ + +#ifdef LTC_CBC_MODE + +/** + Initialize a CBC context + @param cipher The index of the cipher desired + @param IV The initial vector + @param key The secret key + @param keylen The length of the secret key (octets) + @param num_rounds Number of rounds in the cipher desired (0 for default) + @param cbc The CBC state to initialize + @return CRYPT_OK if successful +*/ +int cbc_start(int cipher, const unsigned char *IV, const unsigned char *key, + int keylen, int num_rounds, symmetric_CBC *cbc) +{ + int x, err; + + LTC_ARGCHK(IV != NULL); + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(cbc != NULL); + + /* bad param? */ + if ((err = cipher_is_valid(cipher)) != CRYPT_OK) { + return err; + } + + /* setup cipher */ + if ((err = cipher_descriptor[cipher].setup(key, keylen, num_rounds, &cbc->key)) != CRYPT_OK) { + return err; + } + + /* copy IV */ + cbc->blocklen = cipher_descriptor[cipher].block_length; + cbc->cipher = cipher; + for (x = 0; x < cbc->blocklen; x++) { + cbc->IV[x] = IV[x]; + } + return CRYPT_OK; +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file gcm_add_iv.c + GCM implementation, add IV data to the state, by Tom St Denis +*/ + + +#ifdef LTC_GCM_MODE + +/** + Add IV data to the GCM state + @param gcm The GCM state + @param IV The initial value data to add + @param IVlen The length of the IV + @return CRYPT_OK on success + */ +int gcm_add_iv(gcm_state *gcm, + const unsigned char *IV, unsigned long IVlen) +{ + unsigned long x, y; + int err; + + LTC_ARGCHK(gcm != NULL); + if (IVlen > 0) { + LTC_ARGCHK(IV != NULL); + } + + /* must be in IV mode */ + if (gcm->mode != LTC_GCM_MODE_IV) { + return CRYPT_INVALID_ARG; + } + + if (gcm->buflen >= 16 || gcm->buflen < 0) { + return CRYPT_INVALID_ARG; + } + + if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) { + return err; + } + + + /* trip the ivmode flag */ + if (IVlen + gcm->buflen > 12) { + gcm->ivmode |= 1; + } + + x = 0; +#ifdef LTC_FAST + if (gcm->buflen == 0) { + for (x = 0; x < (IVlen & ~15); x += 16) { + for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) { + *((LTC_FAST_TYPE*)(&gcm->X[y])) ^= *((LTC_FAST_TYPE*)(&IV[x + y])); + } + gcm_mult_h(gcm, gcm->X); + gcm->totlen += 128; + } + IV += x; + } +#endif + + /* start adding IV data to the state */ + for (; x < IVlen; x++) { + gcm->buf[gcm->buflen++] = *IV++; + + if (gcm->buflen == 16) { + /* GF mult it */ + for (y = 0; y < 16; y++) { + gcm->X[y] ^= gcm->buf[y]; + } + gcm_mult_h(gcm, gcm->X); + gcm->buflen = 0; + gcm->totlen += 128; + } + } + + return CRYPT_OK; +} + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_add_iv.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file gcm_done.c + GCM implementation, Terminate the stream, by Tom St Denis +*/ + + +#ifdef LTC_GCM_MODE + +/** + Terminate a GCM stream + @param gcm The GCM state + @param tag [out] The destination for the MAC tag + @param taglen [in/out] The length of the MAC tag + @return CRYPT_OK on success + */ +int gcm_done(gcm_state *gcm, + unsigned char *tag, unsigned long *taglen) +{ + unsigned long x; + int err; + + LTC_ARGCHK(gcm != NULL); + LTC_ARGCHK(tag != NULL); + LTC_ARGCHK(taglen != NULL); + + if (gcm->buflen > 16 || gcm->buflen < 0) { + return CRYPT_INVALID_ARG; + } + + if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) { + return err; + } + + + if (gcm->mode != LTC_GCM_MODE_TEXT) { + return CRYPT_INVALID_ARG; + } + + /* handle remaining ciphertext */ + if (gcm->buflen) { + gcm->pttotlen += gcm->buflen * CONST64(8); + gcm_mult_h(gcm, gcm->X); + } + + /* length */ + STORE64H(gcm->totlen, gcm->buf); + STORE64H(gcm->pttotlen, gcm->buf+8); + for (x = 0; x < 16; x++) { + gcm->X[x] ^= gcm->buf[x]; + } + gcm_mult_h(gcm, gcm->X); + + /* encrypt original counter */ + if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y_0, gcm->buf, &gcm->K)) != CRYPT_OK) { + return err; + } + for (x = 0; x < 16 && x < *taglen; x++) { + tag[x] = gcm->buf[x] ^ gcm->X[x]; + } + *taglen = x; + + cipher_descriptor[gcm->cipher].done(&gcm->K); + + return CRYPT_OK; +} + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_done.c,v $ */ +/* $Revision: 1.11 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file gcm_init.c + GCM implementation, initialize state, by Tom St Denis +*/ + + +#ifdef LTC_GCM_MODE + +/** + Initialize a GCM state + @param gcm The GCM state to initialize + @param cipher The index of the cipher to use + @param key The secret key + @param keylen The length of the secret key + @return CRYPT_OK on success + */ +int gcm_init(gcm_state *gcm, int cipher, + const unsigned char *key, int keylen) +{ + int err; + unsigned char B[16]; +#ifdef LTC_GCM_TABLES + int x, y, z, t; +#endif + + LTC_ARGCHK(gcm != NULL); + LTC_ARGCHK(key != NULL); + +#ifdef LTC_FAST + if (16 % sizeof(LTC_FAST_TYPE)) { + return CRYPT_INVALID_ARG; + } +#endif + + /* is cipher valid? */ + if ((err = cipher_is_valid(cipher)) != CRYPT_OK) { + return err; + } + if (cipher_descriptor[cipher].block_length != 16) { + return CRYPT_INVALID_CIPHER; + } + + /* schedule key */ + if ((err = cipher_descriptor[cipher].setup(key, keylen, 0, &gcm->K)) != CRYPT_OK) { + return err; + } + + /* H = E(0) */ + zeromem(B, 16); + if ((err = cipher_descriptor[cipher].ecb_encrypt(B, gcm->H, &gcm->K)) != CRYPT_OK) { + return err; + } + + /* setup state */ + zeromem(gcm->buf, sizeof(gcm->buf)); + zeromem(gcm->X, sizeof(gcm->X)); + gcm->cipher = cipher; + gcm->mode = LTC_GCM_MODE_IV; + gcm->ivmode = 0; + gcm->buflen = 0; + gcm->totlen = 0; + gcm->pttotlen = 0; + +#ifdef LTC_GCM_TABLES + /* setup tables */ + + /* generate the first table as it has no shifting (from which we make the other tables) */ + zeromem(B, 16); + for (y = 0; y < 256; y++) { + B[0] = y; + gcm_gf_mult(gcm->H, B, &gcm->PC[0][y][0]); + } + + /* now generate the rest of the tables based the previous table */ + for (x = 1; x < 16; x++) { + for (y = 0; y < 256; y++) { + /* now shift it right by 8 bits */ + t = gcm->PC[x-1][y][15]; + for (z = 15; z > 0; z--) { + gcm->PC[x][y][z] = gcm->PC[x-1][y][z-1]; + } + gcm->PC[x][y][0] = gcm_shift_table[t<<1]; + gcm->PC[x][y][1] ^= gcm_shift_table[(t<<1)+1]; + } + } + +#endif + + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_init.c,v $ */ +/* $Revision: 1.20 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file gcm_process.c + GCM implementation, process message data, by Tom St Denis +*/ + + +#ifdef LTC_GCM_MODE + +/** + Process plaintext/ciphertext through GCM + @param gcm The GCM state + @param pt The plaintext + @param ptlen The plaintext length (ciphertext length is the same) + @param ct The ciphertext + @param direction Encrypt or Decrypt mode (GCM_ENCRYPT or GCM_DECRYPT) + @return CRYPT_OK on success + */ +int gcm_process(gcm_state *gcm, + unsigned char *pt, unsigned long ptlen, + unsigned char *ct, + int direction) +{ + unsigned long x; + int y, err; + unsigned char b; + + LTC_ARGCHK(gcm != NULL); + if (ptlen > 0) { + LTC_ARGCHK(pt != NULL); + LTC_ARGCHK(ct != NULL); + } + + if (gcm->buflen > 16 || gcm->buflen < 0) { + return CRYPT_INVALID_ARG; + } + + if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) { + return err; + } + + /* in AAD mode? */ + if (gcm->mode == LTC_GCM_MODE_AAD) { + /* let's process the AAD */ + if (gcm->buflen) { + gcm->totlen += gcm->buflen * CONST64(8); + gcm_mult_h(gcm, gcm->X); + } + + /* increment counter */ + for (y = 15; y >= 12; y--) { + if (++gcm->Y[y] & 255) { break; } + } + /* encrypt the counter */ + if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) { + return err; + } + + gcm->buflen = 0; + gcm->mode = LTC_GCM_MODE_TEXT; + } + + if (gcm->mode != LTC_GCM_MODE_TEXT) { + return CRYPT_INVALID_ARG; + } + + x = 0; +#ifdef LTC_FAST + if (gcm->buflen == 0) { + if (direction == GCM_ENCRYPT) { + for (x = 0; x < (ptlen & ~15); x += 16) { + /* ctr encrypt */ + for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) { + *((LTC_FAST_TYPE*)(&ct[x + y])) = *((LTC_FAST_TYPE*)(&pt[x+y])) ^ *((LTC_FAST_TYPE*)(&gcm->buf[y])); + *((LTC_FAST_TYPE*)(&gcm->X[y])) ^= *((LTC_FAST_TYPE*)(&ct[x+y])); + } + /* GMAC it */ + gcm->pttotlen += 128; + gcm_mult_h(gcm, gcm->X); + /* increment counter */ + for (y = 15; y >= 12; y--) { + if (++gcm->Y[y] & 255) { break; } + } + if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) { + return err; + } + } + } else { + for (x = 0; x < (ptlen & ~15); x += 16) { + /* ctr encrypt */ + for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) { + *((LTC_FAST_TYPE*)(&gcm->X[y])) ^= *((LTC_FAST_TYPE*)(&ct[x+y])); + *((LTC_FAST_TYPE*)(&pt[x + y])) = *((LTC_FAST_TYPE*)(&ct[x+y])) ^ *((LTC_FAST_TYPE*)(&gcm->buf[y])); + } + /* GMAC it */ + gcm->pttotlen += 128; + gcm_mult_h(gcm, gcm->X); + /* increment counter */ + for (y = 15; y >= 12; y--) { + if (++gcm->Y[y] & 255) { break; } + } + if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) { + return err; + } + } + } + } +#endif + + /* process text */ + for (; x < ptlen; x++) { + if (gcm->buflen == 16) { + gcm->pttotlen += 128; + gcm_mult_h(gcm, gcm->X); + + /* increment counter */ + for (y = 15; y >= 12; y--) { + if (++gcm->Y[y] & 255) { break; } + } + if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) { + return err; + } + gcm->buflen = 0; + } + + if (direction == GCM_ENCRYPT) { + b = ct[x] = pt[x] ^ gcm->buf[gcm->buflen]; + } else { + b = ct[x]; + pt[x] = ct[x] ^ gcm->buf[gcm->buflen]; + } + gcm->X[gcm->buflen++] ^= b; + } + + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_process.c,v $ */ +/* $Revision: 1.16 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file gcm_mult_h.c + GCM implementation, do the GF mult, by Tom St Denis +*/ + + +#if defined(LTC_GCM_MODE) +/** + GCM multiply by H + @param gcm The GCM state which holds the H value + @param I The value to multiply H by + */ +void gcm_mult_h(gcm_state *gcm, unsigned char *I) +{ + unsigned char T[16]; +#ifdef LTC_GCM_TABLES + int x, y; +#ifdef LTC_GCM_TABLES_SSE2 + asm("movdqa (%0),%%xmm0"::"r"(&gcm->PC[0][I[0]][0])); + for (x = 1; x < 16; x++) { + asm("pxor (%0),%%xmm0"::"r"(&gcm->PC[x][I[x]][0])); + } + asm("movdqa %%xmm0,(%0)"::"r"(&T)); +#else + XMEMCPY(T, &gcm->PC[0][I[0]][0], 16); + for (x = 1; x < 16; x++) { +#ifdef LTC_FAST + for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) { + *((LTC_FAST_TYPE *)(T + y)) ^= *((LTC_FAST_TYPE *)(&gcm->PC[x][I[x]][y])); + } +#else + for (y = 0; y < 16; y++) { + T[y] ^= gcm->PC[x][I[x]][y]; + } +#endif /* LTC_FAST */ + } +#endif /* LTC_GCM_TABLES_SSE2 */ +#else + gcm_gf_mult(gcm->H, I, T); +#endif + XMEMCPY(I, T, 16); +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_mult_h.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file gcm_gf_mult.c + GCM implementation, do the GF mult, by Tom St Denis +*/ + + +#if defined(LTC_GCM_TABLES) || defined(LRW_TABLES) || ((defined(LTC_GCM_MODE) || defined(LTC_GCM_MODE)) && defined(LTC_FAST)) + +/* this is x*2^128 mod p(x) ... the results are 16 bytes each stored in a packed format. Since only the + * lower 16 bits are not zero'ed I removed the upper 14 bytes */ +const unsigned char gcm_shift_table[256*2] = { +0x00, 0x00, 0x01, 0xc2, 0x03, 0x84, 0x02, 0x46, 0x07, 0x08, 0x06, 0xca, 0x04, 0x8c, 0x05, 0x4e, +0x0e, 0x10, 0x0f, 0xd2, 0x0d, 0x94, 0x0c, 0x56, 0x09, 0x18, 0x08, 0xda, 0x0a, 0x9c, 0x0b, 0x5e, +0x1c, 0x20, 0x1d, 0xe2, 0x1f, 0xa4, 0x1e, 0x66, 0x1b, 0x28, 0x1a, 0xea, 0x18, 0xac, 0x19, 0x6e, +0x12, 0x30, 0x13, 0xf2, 0x11, 0xb4, 0x10, 0x76, 0x15, 0x38, 0x14, 0xfa, 0x16, 0xbc, 0x17, 0x7e, +0x38, 0x40, 0x39, 0x82, 0x3b, 0xc4, 0x3a, 0x06, 0x3f, 0x48, 0x3e, 0x8a, 0x3c, 0xcc, 0x3d, 0x0e, +0x36, 0x50, 0x37, 0x92, 0x35, 0xd4, 0x34, 0x16, 0x31, 0x58, 0x30, 0x9a, 0x32, 0xdc, 0x33, 0x1e, +0x24, 0x60, 0x25, 0xa2, 0x27, 0xe4, 0x26, 0x26, 0x23, 0x68, 0x22, 0xaa, 0x20, 0xec, 0x21, 0x2e, +0x2a, 0x70, 0x2b, 0xb2, 0x29, 0xf4, 0x28, 0x36, 0x2d, 0x78, 0x2c, 0xba, 0x2e, 0xfc, 0x2f, 0x3e, +0x70, 0x80, 0x71, 0x42, 0x73, 0x04, 0x72, 0xc6, 0x77, 0x88, 0x76, 0x4a, 0x74, 0x0c, 0x75, 0xce, +0x7e, 0x90, 0x7f, 0x52, 0x7d, 0x14, 0x7c, 0xd6, 0x79, 0x98, 0x78, 0x5a, 0x7a, 0x1c, 0x7b, 0xde, +0x6c, 0xa0, 0x6d, 0x62, 0x6f, 0x24, 0x6e, 0xe6, 0x6b, 0xa8, 0x6a, 0x6a, 0x68, 0x2c, 0x69, 0xee, +0x62, 0xb0, 0x63, 0x72, 0x61, 0x34, 0x60, 0xf6, 0x65, 0xb8, 0x64, 0x7a, 0x66, 0x3c, 0x67, 0xfe, +0x48, 0xc0, 0x49, 0x02, 0x4b, 0x44, 0x4a, 0x86, 0x4f, 0xc8, 0x4e, 0x0a, 0x4c, 0x4c, 0x4d, 0x8e, +0x46, 0xd0, 0x47, 0x12, 0x45, 0x54, 0x44, 0x96, 0x41, 0xd8, 0x40, 0x1a, 0x42, 0x5c, 0x43, 0x9e, +0x54, 0xe0, 0x55, 0x22, 0x57, 0x64, 0x56, 0xa6, 0x53, 0xe8, 0x52, 0x2a, 0x50, 0x6c, 0x51, 0xae, +0x5a, 0xf0, 0x5b, 0x32, 0x59, 0x74, 0x58, 0xb6, 0x5d, 0xf8, 0x5c, 0x3a, 0x5e, 0x7c, 0x5f, 0xbe, +0xe1, 0x00, 0xe0, 0xc2, 0xe2, 0x84, 0xe3, 0x46, 0xe6, 0x08, 0xe7, 0xca, 0xe5, 0x8c, 0xe4, 0x4e, +0xef, 0x10, 0xee, 0xd2, 0xec, 0x94, 0xed, 0x56, 0xe8, 0x18, 0xe9, 0xda, 0xeb, 0x9c, 0xea, 0x5e, +0xfd, 0x20, 0xfc, 0xe2, 0xfe, 0xa4, 0xff, 0x66, 0xfa, 0x28, 0xfb, 0xea, 0xf9, 0xac, 0xf8, 0x6e, +0xf3, 0x30, 0xf2, 0xf2, 0xf0, 0xb4, 0xf1, 0x76, 0xf4, 0x38, 0xf5, 0xfa, 0xf7, 0xbc, 0xf6, 0x7e, +0xd9, 0x40, 0xd8, 0x82, 0xda, 0xc4, 0xdb, 0x06, 0xde, 0x48, 0xdf, 0x8a, 0xdd, 0xcc, 0xdc, 0x0e, +0xd7, 0x50, 0xd6, 0x92, 0xd4, 0xd4, 0xd5, 0x16, 0xd0, 0x58, 0xd1, 0x9a, 0xd3, 0xdc, 0xd2, 0x1e, +0xc5, 0x60, 0xc4, 0xa2, 0xc6, 0xe4, 0xc7, 0x26, 0xc2, 0x68, 0xc3, 0xaa, 0xc1, 0xec, 0xc0, 0x2e, +0xcb, 0x70, 0xca, 0xb2, 0xc8, 0xf4, 0xc9, 0x36, 0xcc, 0x78, 0xcd, 0xba, 0xcf, 0xfc, 0xce, 0x3e, +0x91, 0x80, 0x90, 0x42, 0x92, 0x04, 0x93, 0xc6, 0x96, 0x88, 0x97, 0x4a, 0x95, 0x0c, 0x94, 0xce, +0x9f, 0x90, 0x9e, 0x52, 0x9c, 0x14, 0x9d, 0xd6, 0x98, 0x98, 0x99, 0x5a, 0x9b, 0x1c, 0x9a, 0xde, +0x8d, 0xa0, 0x8c, 0x62, 0x8e, 0x24, 0x8f, 0xe6, 0x8a, 0xa8, 0x8b, 0x6a, 0x89, 0x2c, 0x88, 0xee, +0x83, 0xb0, 0x82, 0x72, 0x80, 0x34, 0x81, 0xf6, 0x84, 0xb8, 0x85, 0x7a, 0x87, 0x3c, 0x86, 0xfe, +0xa9, 0xc0, 0xa8, 0x02, 0xaa, 0x44, 0xab, 0x86, 0xae, 0xc8, 0xaf, 0x0a, 0xad, 0x4c, 0xac, 0x8e, +0xa7, 0xd0, 0xa6, 0x12, 0xa4, 0x54, 0xa5, 0x96, 0xa0, 0xd8, 0xa1, 0x1a, 0xa3, 0x5c, 0xa2, 0x9e, +0xb5, 0xe0, 0xb4, 0x22, 0xb6, 0x64, 0xb7, 0xa6, 0xb2, 0xe8, 0xb3, 0x2a, 0xb1, 0x6c, 0xb0, 0xae, +0xbb, 0xf0, 0xba, 0x32, 0xb8, 0x74, 0xb9, 0xb6, 0xbc, 0xf8, 0xbd, 0x3a, 0xbf, 0x7c, 0xbe, 0xbe }; + +#endif + + +#if defined(LTC_GCM_MODE) || defined(LRW_MODE) + +#ifndef LTC_FAST +/* right shift */ +static void gcm_rightshift(unsigned char *a) +{ + int x; + for (x = 15; x > 0; x--) { + a[x] = (a[x]>>1) | ((a[x-1]<<7)&0x80); + } + a[0] >>= 1; +} + +/* c = b*a */ +static const unsigned char mask[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; +static const unsigned char poly[] = { 0x00, 0xE1 }; + + +/** + GCM GF multiplier (internal use only) bitserial + @param a First value + @param b Second value + @param c Destination for a * b + */ +void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c) +{ + unsigned char Z[16], V[16]; + unsigned char x, y, z; + + zeromem(Z, 16); + XMEMCPY(V, a, 16); + for (x = 0; x < 128; x++) { + if (b[x>>3] & mask[x&7]) { + for (y = 0; y < 16; y++) { + Z[y] ^= V[y]; + } + } + z = V[15] & 0x01; + gcm_rightshift(V); + V[0] ^= poly[z]; + } + XMEMCPY(c, Z, 16); +} + +#else + +/* map normal numbers to "ieee" way ... e.g. bit reversed */ +#define M(x) ( ((x&8)>>3) | ((x&4)>>1) | ((x&2)<<1) | ((x&1)<<3) ) + +#define BPD (sizeof(LTC_FAST_TYPE) * 8) +#define WPV (1 + (16 / sizeof(LTC_FAST_TYPE))) + +/** + GCM GF multiplier (internal use only) word oriented + @param a First value + @param b Second value + @param c Destination for a * b + */ +void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c) +{ + int i, j, k, u; + LTC_FAST_TYPE B[16][WPV], tmp[32 / sizeof(LTC_FAST_TYPE)], pB[16 / sizeof(LTC_FAST_TYPE)], zz, z; + unsigned char pTmp[32]; + + /* create simple tables */ + zeromem(B[0], sizeof(B[0])); + zeromem(B[M(1)], sizeof(B[M(1)])); + +#ifdef ENDIAN_32BITWORD + for (i = 0; i < 4; i++) { + LOAD32H(B[M(1)][i], a + (i<<2)); + LOAD32L(pB[i], b + (i<<2)); + } +#else + for (i = 0; i < 2; i++) { + LOAD64H(B[M(1)][i], a + (i<<3)); + LOAD64L(pB[i], b + (i<<3)); + } +#endif + + /* now create 2, 4 and 8 */ + B[M(2)][0] = B[M(1)][0] >> 1; + B[M(4)][0] = B[M(1)][0] >> 2; + B[M(8)][0] = B[M(1)][0] >> 3; + for (i = 1; i < (int)WPV; i++) { + B[M(2)][i] = (B[M(1)][i-1] << (BPD-1)) | (B[M(1)][i] >> 1); + B[M(4)][i] = (B[M(1)][i-1] << (BPD-2)) | (B[M(1)][i] >> 2); + B[M(8)][i] = (B[M(1)][i-1] << (BPD-3)) | (B[M(1)][i] >> 3); + } + + /* now all values with two bits which are 3, 5, 6, 9, 10, 12 */ + for (i = 0; i < (int)WPV; i++) { + B[M(3)][i] = B[M(1)][i] ^ B[M(2)][i]; + B[M(5)][i] = B[M(1)][i] ^ B[M(4)][i]; + B[M(6)][i] = B[M(2)][i] ^ B[M(4)][i]; + B[M(9)][i] = B[M(1)][i] ^ B[M(8)][i]; + B[M(10)][i] = B[M(2)][i] ^ B[M(8)][i]; + B[M(12)][i] = B[M(8)][i] ^ B[M(4)][i]; + + /* now all 3 bit values and the only 4 bit value: 7, 11, 13, 14, 15 */ + B[M(7)][i] = B[M(3)][i] ^ B[M(4)][i]; + B[M(11)][i] = B[M(3)][i] ^ B[M(8)][i]; + B[M(13)][i] = B[M(1)][i] ^ B[M(12)][i]; + B[M(14)][i] = B[M(6)][i] ^ B[M(8)][i]; + B[M(15)][i] = B[M(7)][i] ^ B[M(8)][i]; + } + + zeromem(tmp, sizeof(tmp)); + + /* compute product four bits of each word at a time */ + /* for each nibble */ + for (i = (BPD/4)-1; i >= 0; i--) { + /* for each word */ + for (j = 0; j < (int)(WPV-1); j++) { + /* grab the 4 bits recall the nibbles are backwards so it's a shift by (i^1)*4 */ + u = (pB[j] >> ((i^1)<<2)) & 15; + + /* add offset by the word count the table looked up value to the result */ + for (k = 0; k < (int)WPV; k++) { + tmp[k+j] ^= B[u][k]; + } + } + /* shift result up by 4 bits */ + if (i != 0) { + for (z = j = 0; j < (int)(32 / sizeof(LTC_FAST_TYPE)); j++) { + zz = tmp[j] << (BPD-4); + tmp[j] = (tmp[j] >> 4) | z; + z = zz; + } + } + } + + /* store product */ +#ifdef ENDIAN_32BITWORD + for (i = 0; i < 8; i++) { + STORE32H(tmp[i], pTmp + (i<<2)); + } +#else + for (i = 0; i < 4; i++) { + STORE64H(tmp[i], pTmp + (i<<3)); + } +#endif + + /* reduce by taking most significant byte and adding the appropriate two byte sequence 16 bytes down */ + for (i = 31; i >= 16; i--) { + pTmp[i-16] ^= gcm_shift_table[((unsigned)pTmp[i]<<1)]; + pTmp[i-15] ^= gcm_shift_table[((unsigned)pTmp[i]<<1)+1]; + } + + for (i = 0; i < 16; i++) { + c[i] = pTmp[i]; + } + +} + +#endif + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_gf_mult.c,v $ */ +/* $Revision: 1.25 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file gcm_add_aad.c + GCM implementation, Add AAD data to the stream, by Tom St Denis +*/ + + +#ifdef LTC_GCM_MODE + +/** + Add AAD to the GCM state + @param gcm The GCM state + @param adata The additional authentication data to add to the GCM state + @param adatalen The length of the AAD data. + @return CRYPT_OK on success + */ +int gcm_add_aad(gcm_state *gcm, + const unsigned char *adata, unsigned long adatalen) +{ + unsigned long x; + int err; +#ifdef LTC_FAST + unsigned long y; +#endif + + LTC_ARGCHK(gcm != NULL); + if (adatalen > 0) { + LTC_ARGCHK(adata != NULL); + } + + if (gcm->buflen > 16 || gcm->buflen < 0) { + return CRYPT_INVALID_ARG; + } + + if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) { + return err; + } + + /* in IV mode? */ + if (gcm->mode == LTC_GCM_MODE_IV) { + /* let's process the IV */ + if (gcm->ivmode || gcm->buflen != 12) { + for (x = 0; x < (unsigned long)gcm->buflen; x++) { + gcm->X[x] ^= gcm->buf[x]; + } + if (gcm->buflen) { + gcm->totlen += gcm->buflen * CONST64(8); + gcm_mult_h(gcm, gcm->X); + } + + /* mix in the length */ + zeromem(gcm->buf, 8); + STORE64H(gcm->totlen, gcm->buf+8); + for (x = 0; x < 16; x++) { + gcm->X[x] ^= gcm->buf[x]; + } + gcm_mult_h(gcm, gcm->X); + + /* copy counter out */ + XMEMCPY(gcm->Y, gcm->X, 16); + zeromem(gcm->X, 16); + } else { + XMEMCPY(gcm->Y, gcm->buf, 12); + gcm->Y[12] = 0; + gcm->Y[13] = 0; + gcm->Y[14] = 0; + gcm->Y[15] = 1; + } + XMEMCPY(gcm->Y_0, gcm->Y, 16); + zeromem(gcm->buf, 16); + gcm->buflen = 0; + gcm->totlen = 0; + gcm->mode = LTC_GCM_MODE_AAD; + } + + if (gcm->mode != LTC_GCM_MODE_AAD || gcm->buflen >= 16) { + return CRYPT_INVALID_ARG; + } + + x = 0; +#ifdef LTC_FAST + if (gcm->buflen == 0) { + for (x = 0; x < (adatalen & ~15); x += 16) { + for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) { + *((LTC_FAST_TYPE*)(&gcm->X[y])) ^= *((LTC_FAST_TYPE*)(&adata[x + y])); + } + gcm_mult_h(gcm, gcm->X); + gcm->totlen += 128; + } + adata += x; + } +#endif + + + /* start adding AAD data to the state */ + for (; x < adatalen; x++) { + gcm->X[gcm->buflen++] ^= *adata++; + + if (gcm->buflen == 16) { + /* GF mult it */ + gcm_mult_h(gcm, gcm->X); + gcm->buflen = 0; + gcm->totlen += 128; + } + } + + return CRYPT_OK; +} +#endif + + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file gcm_reset.c + GCM implementation, reset a used state so it can accept IV data, by Tom St Denis +*/ + + +#ifdef LTC_GCM_MODE + +/** + Reset a GCM state to as if you just called gcm_init(). This saves the initialization time. + @param gcm The GCM state to reset + @return CRYPT_OK on success +*/ +int gcm_reset(gcm_state *gcm) +{ + LTC_ARGCHK(gcm != NULL); + + zeromem(gcm->buf, sizeof(gcm->buf)); + zeromem(gcm->X, sizeof(gcm->X)); + gcm->mode = LTC_GCM_MODE_IV; + gcm->ivmode = 0; + gcm->buflen = 0; + gcm->totlen = 0; + gcm->pttotlen = 0; + + return CRYPT_OK; +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + + +/** + @file md5.c + LTC_MD5 hash function by Tom St Denis +*/ + +#ifdef LTC_MD5 + +const struct ltc_hash_descriptor md5_desc = +{ + "md5", + 3, + 16, + 64, + + /* OID */ + { 1, 2, 840, 113549, 2, 5, }, + 6, + + &md5_init, + &md5_process, + &md5_done, + &md5_test, + NULL +}; + +#define F(x,y,z) (z ^ (x & (y ^ z))) +#define G(x,y,z) (y ^ (z & (y ^ x))) +#define H(x,y,z) (x^y^z) +#define I(x,y,z) (y^(x|(~z))) + +#ifdef LTC_SMALL_CODE + +#define FF(a,b,c,d,M,s,t) \ + a = (a + F(b,c,d) + M + t); a = ROL(a, s) + b; + +#define GG(a,b,c,d,M,s,t) \ + a = (a + G(b,c,d) + M + t); a = ROL(a, s) + b; + +#define HH(a,b,c,d,M,s,t) \ + a = (a + H(b,c,d) + M + t); a = ROL(a, s) + b; + +#define II(a,b,c,d,M,s,t) \ + a = (a + I(b,c,d) + M + t); a = ROL(a, s) + b; + +static const unsigned char Worder[64] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, + 1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12, + 5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2, + 0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9 +}; + +static const unsigned char Rorder[64] = { + 7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22, + 5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20, + 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23, + 6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21 +}; + +static const ulong32 Korder[64] = { +0xd76aa478UL, 0xe8c7b756UL, 0x242070dbUL, 0xc1bdceeeUL, 0xf57c0fafUL, 0x4787c62aUL, 0xa8304613UL, 0xfd469501UL, +0x698098d8UL, 0x8b44f7afUL, 0xffff5bb1UL, 0x895cd7beUL, 0x6b901122UL, 0xfd987193UL, 0xa679438eUL, 0x49b40821UL, +0xf61e2562UL, 0xc040b340UL, 0x265e5a51UL, 0xe9b6c7aaUL, 0xd62f105dUL, 0x02441453UL, 0xd8a1e681UL, 0xe7d3fbc8UL, +0x21e1cde6UL, 0xc33707d6UL, 0xf4d50d87UL, 0x455a14edUL, 0xa9e3e905UL, 0xfcefa3f8UL, 0x676f02d9UL, 0x8d2a4c8aUL, +0xfffa3942UL, 0x8771f681UL, 0x6d9d6122UL, 0xfde5380cUL, 0xa4beea44UL, 0x4bdecfa9UL, 0xf6bb4b60UL, 0xbebfbc70UL, +0x289b7ec6UL, 0xeaa127faUL, 0xd4ef3085UL, 0x04881d05UL, 0xd9d4d039UL, 0xe6db99e5UL, 0x1fa27cf8UL, 0xc4ac5665UL, +0xf4292244UL, 0x432aff97UL, 0xab9423a7UL, 0xfc93a039UL, 0x655b59c3UL, 0x8f0ccc92UL, 0xffeff47dUL, 0x85845dd1UL, +0x6fa87e4fUL, 0xfe2ce6e0UL, 0xa3014314UL, 0x4e0811a1UL, 0xf7537e82UL, 0xbd3af235UL, 0x2ad7d2bbUL, 0xeb86d391UL +}; + +#else + +#define FF(a,b,c,d,M,s,t) \ + a = (a + F(b,c,d) + M + t); a = ROLc(a, s) + b; + +#define GG(a,b,c,d,M,s,t) \ + a = (a + G(b,c,d) + M + t); a = ROLc(a, s) + b; + +#define HH(a,b,c,d,M,s,t) \ + a = (a + H(b,c,d) + M + t); a = ROLc(a, s) + b; + +#define II(a,b,c,d,M,s,t) \ + a = (a + I(b,c,d) + M + t); a = ROLc(a, s) + b; + + +#endif + +#ifdef LTC_CLEAN_STACK +static int _md5_compress(hash_state *md, unsigned char *buf) +#else +static int md5_compress(hash_state *md, unsigned char *buf) +#endif +{ + ulong32 i, W[16], a, b, c, d; +#ifdef LTC_SMALL_CODE + ulong32 t; +#endif + + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD32L(W[i], buf + (4*i)); + } + + /* copy state */ + a = md->md5.state[0]; + b = md->md5.state[1]; + c = md->md5.state[2]; + d = md->md5.state[3]; + +#ifdef LTC_SMALL_CODE + for (i = 0; i < 16; ++i) { + FF(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); + t = d; d = c; c = b; b = a; a = t; + } + + for (; i < 32; ++i) { + GG(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); + t = d; d = c; c = b; b = a; a = t; + } + + for (; i < 48; ++i) { + HH(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); + t = d; d = c; c = b; b = a; a = t; + } + + for (; i < 64; ++i) { + II(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); + t = d; d = c; c = b; b = a; a = t; + } + +#else + FF(a,b,c,d,W[0],7,0xd76aa478UL) + FF(d,a,b,c,W[1],12,0xe8c7b756UL) + FF(c,d,a,b,W[2],17,0x242070dbUL) + FF(b,c,d,a,W[3],22,0xc1bdceeeUL) + FF(a,b,c,d,W[4],7,0xf57c0fafUL) + FF(d,a,b,c,W[5],12,0x4787c62aUL) + FF(c,d,a,b,W[6],17,0xa8304613UL) + FF(b,c,d,a,W[7],22,0xfd469501UL) + FF(a,b,c,d,W[8],7,0x698098d8UL) + FF(d,a,b,c,W[9],12,0x8b44f7afUL) + FF(c,d,a,b,W[10],17,0xffff5bb1UL) + FF(b,c,d,a,W[11],22,0x895cd7beUL) + FF(a,b,c,d,W[12],7,0x6b901122UL) + FF(d,a,b,c,W[13],12,0xfd987193UL) + FF(c,d,a,b,W[14],17,0xa679438eUL) + FF(b,c,d,a,W[15],22,0x49b40821UL) + GG(a,b,c,d,W[1],5,0xf61e2562UL) + GG(d,a,b,c,W[6],9,0xc040b340UL) + GG(c,d,a,b,W[11],14,0x265e5a51UL) + GG(b,c,d,a,W[0],20,0xe9b6c7aaUL) + GG(a,b,c,d,W[5],5,0xd62f105dUL) + GG(d,a,b,c,W[10],9,0x02441453UL) + GG(c,d,a,b,W[15],14,0xd8a1e681UL) + GG(b,c,d,a,W[4],20,0xe7d3fbc8UL) + GG(a,b,c,d,W[9],5,0x21e1cde6UL) + GG(d,a,b,c,W[14],9,0xc33707d6UL) + GG(c,d,a,b,W[3],14,0xf4d50d87UL) + GG(b,c,d,a,W[8],20,0x455a14edUL) + GG(a,b,c,d,W[13],5,0xa9e3e905UL) + GG(d,a,b,c,W[2],9,0xfcefa3f8UL) + GG(c,d,a,b,W[7],14,0x676f02d9UL) + GG(b,c,d,a,W[12],20,0x8d2a4c8aUL) + HH(a,b,c,d,W[5],4,0xfffa3942UL) + HH(d,a,b,c,W[8],11,0x8771f681UL) + HH(c,d,a,b,W[11],16,0x6d9d6122UL) + HH(b,c,d,a,W[14],23,0xfde5380cUL) + HH(a,b,c,d,W[1],4,0xa4beea44UL) + HH(d,a,b,c,W[4],11,0x4bdecfa9UL) + HH(c,d,a,b,W[7],16,0xf6bb4b60UL) + HH(b,c,d,a,W[10],23,0xbebfbc70UL) + HH(a,b,c,d,W[13],4,0x289b7ec6UL) + HH(d,a,b,c,W[0],11,0xeaa127faUL) + HH(c,d,a,b,W[3],16,0xd4ef3085UL) + HH(b,c,d,a,W[6],23,0x04881d05UL) + HH(a,b,c,d,W[9],4,0xd9d4d039UL) + HH(d,a,b,c,W[12],11,0xe6db99e5UL) + HH(c,d,a,b,W[15],16,0x1fa27cf8UL) + HH(b,c,d,a,W[2],23,0xc4ac5665UL) + II(a,b,c,d,W[0],6,0xf4292244UL) + II(d,a,b,c,W[7],10,0x432aff97UL) + II(c,d,a,b,W[14],15,0xab9423a7UL) + II(b,c,d,a,W[5],21,0xfc93a039UL) + II(a,b,c,d,W[12],6,0x655b59c3UL) + II(d,a,b,c,W[3],10,0x8f0ccc92UL) + II(c,d,a,b,W[10],15,0xffeff47dUL) + II(b,c,d,a,W[1],21,0x85845dd1UL) + II(a,b,c,d,W[8],6,0x6fa87e4fUL) + II(d,a,b,c,W[15],10,0xfe2ce6e0UL) + II(c,d,a,b,W[6],15,0xa3014314UL) + II(b,c,d,a,W[13],21,0x4e0811a1UL) + II(a,b,c,d,W[4],6,0xf7537e82UL) + II(d,a,b,c,W[11],10,0xbd3af235UL) + II(c,d,a,b,W[2],15,0x2ad7d2bbUL) + II(b,c,d,a,W[9],21,0xeb86d391UL) +#endif + + md->md5.state[0] = md->md5.state[0] + a; + md->md5.state[1] = md->md5.state[1] + b; + md->md5.state[2] = md->md5.state[2] + c; + md->md5.state[3] = md->md5.state[3] + d; + + return CRYPT_OK; +} + +#ifdef LTC_CLEAN_STACK +static int md5_compress(hash_state *md, unsigned char *buf) +{ + int err; + err = _md5_compress(md, buf); + burn_stack(sizeof(ulong32) * 21); + return err; +} +#endif + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful +*/ +int md5_init(hash_state * md) +{ + LTC_ARGCHK(md != NULL); + md->md5.state[0] = 0x67452301UL; + md->md5.state[1] = 0xefcdab89UL; + md->md5.state[2] = 0x98badcfeUL; + md->md5.state[3] = 0x10325476UL; + md->md5.curlen = 0; + md->md5.length = 0; + return CRYPT_OK; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful +*/ +HASH_PROCESS(md5_process, md5_compress, md5, 64) + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (16 bytes) + @return CRYPT_OK if successful +*/ +int md5_done(hash_state * md, unsigned char *out) +{ + int i; + + LTC_ARGCHK(md != NULL); + LTC_ARGCHK(out != NULL); + + if (md->md5.curlen >= sizeof(md->md5.buf)) { + return CRYPT_INVALID_ARG; + } + + + /* increase the length of the message */ + md->md5.length += md->md5.curlen * 8; + + /* append the '1' bit */ + md->md5.buf[md->md5.curlen++] = (unsigned char)0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->md5.curlen > 56) { + while (md->md5.curlen < 64) { + md->md5.buf[md->md5.curlen++] = (unsigned char)0; + } + md5_compress(md, md->md5.buf); + md->md5.curlen = 0; + } + + /* pad upto 56 bytes of zeroes */ + while (md->md5.curlen < 56) { + md->md5.buf[md->md5.curlen++] = (unsigned char)0; + } + + /* store length */ + STORE64L(md->md5.length, md->md5.buf+56); + md5_compress(md, md->md5.buf); + + /* copy output */ + for (i = 0; i < 4; i++) { + STORE32L(md->md5.state[i], out+(4*i)); + } +#ifdef LTC_CLEAN_STACK + zeromem(md, sizeof(hash_state)); +#endif + return CRYPT_OK; +} + +/** + Self-test the hash + @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled +*/ +int md5_test(void) +{ + #ifndef LTC_TEST + return CRYPT_NOP; + #else + static const struct { + char *msg; + unsigned char hash[16]; + } tests[] = { + { "", + { 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, + 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e } }, + { "a", + {0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, + 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61 } }, + { "abc", + { 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, + 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 } }, + { "message digest", + { 0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d, + 0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0 } }, + { "abcdefghijklmnopqrstuvwxyz", + { 0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00, + 0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b } }, + { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + { 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5, + 0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f } }, + { "12345678901234567890123456789012345678901234567890123456789012345678901234567890", + { 0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55, + 0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a } }, + { NULL, { 0 } } + }; + + int i; + unsigned char tmp[16]; + hash_state md; + + for (i = 0; tests[i].msg != NULL; i++) { + md5_init(&md); + md5_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg)); + md5_done(&md, tmp); + if (XMEMCMP(tmp, tests[i].hash, 16) != 0) { + return CRYPT_FAIL_TESTVECTOR; + } + } + return CRYPT_OK; + #endif +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file ctr_encrypt.c + CTR implementation, encrypt data, Tom St Denis +*/ + + +#ifdef LTC_CTR_MODE + +/** + CTR encrypt + @param pt Plaintext + @param ct [out] Ciphertext + @param len Length of plaintext (octets) + @param ctr CTR state + @return CRYPT_OK if successful +*/ +int ctr_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CTR *ctr) +{ + int x, err; + + LTC_ARGCHK(pt != NULL); + LTC_ARGCHK(ct != NULL); + LTC_ARGCHK(ctr != NULL); + + if ((err = cipher_is_valid(ctr->cipher)) != CRYPT_OK) { + return err; + } + + /* is blocklen/padlen valid? */ + if (ctr->blocklen < 1 || ctr->blocklen > (int)sizeof(ctr->ctr) || + ctr->padlen < 0 || ctr->padlen > (int)sizeof(ctr->pad)) { + return CRYPT_INVALID_ARG; + } + +#ifdef LTC_FAST + if (ctr->blocklen % sizeof(LTC_FAST_TYPE)) { + return CRYPT_INVALID_ARG; + } +#endif + + /* handle acceleration only if pad is empty, accelerator is present and length is >= a block size */ + if ((ctr->padlen == ctr->blocklen) && cipher_descriptor[ctr->cipher].accel_ctr_encrypt != NULL && (len >= (unsigned long)ctr->blocklen)) { + if ((err = cipher_descriptor[ctr->cipher].accel_ctr_encrypt(pt, ct, len/ctr->blocklen, ctr->ctr, ctr->mode, &ctr->key)) != CRYPT_OK) { + return err; + } + len %= ctr->blocklen; + } + + while (len) { + /* is the pad empty? */ + if (ctr->padlen == ctr->blocklen) { + /* increment counter */ + if (ctr->mode == CTR_COUNTER_LITTLE_ENDIAN) { + /* little-endian */ + for (x = 0; x < ctr->ctrlen; x++) { + ctr->ctr[x] = (ctr->ctr[x] + (unsigned char)1) & (unsigned char)255; + if (ctr->ctr[x] != (unsigned char)0) { + break; + } + } + } else { + /* big-endian */ + for (x = ctr->blocklen-1; x >= ctr->ctrlen; x--) { + ctr->ctr[x] = (ctr->ctr[x] + (unsigned char)1) & (unsigned char)255; + if (ctr->ctr[x] != (unsigned char)0) { + break; + } + } + } + + /* encrypt it */ + if ((err = cipher_descriptor[ctr->cipher].ecb_encrypt(ctr->ctr, ctr->pad, &ctr->key)) != CRYPT_OK) { + return err; + } + ctr->padlen = 0; + } +#ifdef LTC_FAST + if (ctr->padlen == 0 && len >= (unsigned long)ctr->blocklen) { + for (x = 0; x < ctr->blocklen; x += sizeof(LTC_FAST_TYPE)) { + *((LTC_FAST_TYPE*)((unsigned char *)ct + x)) = *((LTC_FAST_TYPE*)((unsigned char *)pt + x)) ^ + *((LTC_FAST_TYPE*)((unsigned char *)ctr->pad + x)); + } + pt += ctr->blocklen; + ct += ctr->blocklen; + len -= ctr->blocklen; + ctr->padlen = ctr->blocklen; + continue; + } +#endif + *ct++ = *pt++ ^ ctr->pad[ctr->padlen++]; + --len; + } + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/modes/ctr/ctr_encrypt.c,v $ */ +/* $Revision: 1.22 $ */ +/* $Date: 2007/02/22 20:26:05 $ */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file ctr_done.c + CTR implementation, finish chain, Tom St Denis +*/ + +#ifdef LTC_CTR_MODE + +/** Terminate the chain + @param ctr The CTR chain to terminate + @return CRYPT_OK on success +*/ +int ctr_done(symmetric_CTR *ctr) +{ + int err; + LTC_ARGCHK(ctr != NULL); + + if ((err = cipher_is_valid(ctr->cipher)) != CRYPT_OK) { + return err; + } + cipher_descriptor[ctr->cipher].done(&ctr->key); + return CRYPT_OK; +} + + + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/modes/ctr/ctr_done.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file ctr_decrypt.c + CTR implementation, decrypt data, Tom St Denis +*/ + +#ifdef LTC_CTR_MODE + +/** + CTR decrypt + @param ct Ciphertext + @param pt [out] Plaintext + @param len Length of ciphertext (octets) + @param ctr CTR state + @return CRYPT_OK if successful +*/ +int ctr_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CTR *ctr) +{ + LTC_ARGCHK(pt != NULL); + LTC_ARGCHK(ct != NULL); + LTC_ARGCHK(ctr != NULL); + + return ctr_encrypt(ct, pt, len, ctr); +} + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/modes/ctr/ctr_decrypt.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file ctr_start.c + CTR implementation, start chain, Tom St Denis +*/ + + +#ifdef LTC_CTR_MODE + +/** + Initialize a CTR context + @param cipher The index of the cipher desired + @param IV The initial vector + @param key The secret key + @param keylen The length of the secret key (octets) + @param num_rounds Number of rounds in the cipher desired (0 for default) + @param ctr_mode The counter mode (CTR_COUNTER_LITTLE_ENDIAN or CTR_COUNTER_BIG_ENDIAN) + @param ctr The CTR state to initialize + @return CRYPT_OK if successful +*/ +int ctr_start( int cipher, + const unsigned char *IV, + const unsigned char *key, int keylen, + int num_rounds, int ctr_mode, + symmetric_CTR *ctr) +{ + int x, err; + + LTC_ARGCHK(IV != NULL); + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(ctr != NULL); + + /* bad param? */ + if ((err = cipher_is_valid(cipher)) != CRYPT_OK) { + return err; + } + + /* ctrlen == counter width */ + ctr->ctrlen = (ctr_mode & 255) ? (ctr_mode & 255) : cipher_descriptor[cipher].block_length; + if (ctr->ctrlen > cipher_descriptor[cipher].block_length) { + return CRYPT_INVALID_ARG; + } + + if ((ctr_mode & 0x1000) == CTR_COUNTER_BIG_ENDIAN) { + ctr->ctrlen = cipher_descriptor[cipher].block_length - ctr->ctrlen; + } + + /* setup cipher */ + if ((err = cipher_descriptor[cipher].setup(key, keylen, num_rounds, &ctr->key)) != CRYPT_OK) { + return err; + } + + /* copy ctr */ + ctr->blocklen = cipher_descriptor[cipher].block_length; + ctr->cipher = cipher; + ctr->padlen = 0; + ctr->mode = ctr_mode & 0x1000; + for (x = 0; x < ctr->blocklen; x++) { + ctr->ctr[x] = IV[x]; + } + + if (ctr_mode & LTC_CTR_RFC3686) { + /* increment the IV as per RFC 3686 */ + if (ctr->mode == CTR_COUNTER_LITTLE_ENDIAN) { + /* little-endian */ + for (x = 0; x < ctr->ctrlen; x++) { + ctr->ctr[x] = (ctr->ctr[x] + (unsigned char)1) & (unsigned char)255; + if (ctr->ctr[x] != (unsigned char)0) { + break; + } + } + } else { + /* big-endian */ + for (x = ctr->blocklen-1; x >= ctr->ctrlen; x--) { + ctr->ctr[x] = (ctr->ctr[x] + (unsigned char)1) & (unsigned char)255; + if (ctr->ctr[x] != (unsigned char)0) { + break; + } + } + } + } + + return cipher_descriptor[ctr->cipher].ecb_encrypt(ctr->ctr, ctr->pad, &ctr->key); +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/modes/ctr/ctr_start.c,v $ */ +/* $Revision: 1.15 $ */ +/* $Date: 2007/02/23 14:18:37 $ */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file ctr_setiv.c + CTR implementation, set IV, Tom St Denis +*/ + +#ifdef LTC_CTR_MODE + +/** + Set an initial vector + @param IV The initial vector + @param len The length of the vector (in octets) + @param ctr The CTR state + @return CRYPT_OK if successful +*/ +int ctr_setiv(const unsigned char *IV, unsigned long len, symmetric_CTR *ctr) +{ + int err; + + LTC_ARGCHK(IV != NULL); + LTC_ARGCHK(ctr != NULL); + + /* bad param? */ + if ((err = cipher_is_valid(ctr->cipher)) != CRYPT_OK) { + return err; + } + + if (len != (unsigned long)ctr->blocklen) { + return CRYPT_INVALID_ARG; + } + + /* set IV */ + XMEMCPY(ctr->ctr, IV, len); + + /* force next block */ + ctr->padlen = 0; + return cipher_descriptor[ctr->cipher].ecb_encrypt(IV, ctr->pad, &ctr->key); +} + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/modes/ctr/ctr_setiv.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/** + @file ctr_getiv.c + CTR implementation, get IV, Tom St Denis +*/ + +#ifdef LTC_CTR_MODE + +/** + Get the current initial vector + @param IV [out] The destination of the initial vector + @param len [in/out] The max size and resulting size of the initial vector + @param ctr The CTR state + @return CRYPT_OK if successful +*/ +int ctr_getiv(unsigned char *IV, unsigned long *len, symmetric_CTR *ctr) +{ + LTC_ARGCHK(IV != NULL); + LTC_ARGCHK(len != NULL); + LTC_ARGCHK(ctr != NULL); + if ((unsigned long)ctr->blocklen > *len) { + *len = ctr->blocklen; + return CRYPT_BUFFER_OVERFLOW; + } + XMEMCPY(IV, ctr->ctr, ctr->blocklen); + *len = ctr->blocklen; + + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/modes/ctr/ctr_getiv.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ \ No newline at end of file diff --git a/thirdparty/echttp/thirdparty/tlse.c b/thirdparty/echttp/thirdparty/tlse.c new file mode 100644 index 0000000..5fd0dc3 --- /dev/null +++ b/thirdparty/echttp/thirdparty/tlse.c @@ -0,0 +1,10750 @@ +/******************************************************************************** + Copyright (c) 2016-2021, Eduard Suica + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + ********************************************************************************/ +#ifndef TLSE_C +#define TLSE_C + +#include +#include +#include +#include +#include +#ifdef _WIN32 +#ifdef SSL_COMPATIBLE_INTERFACE +#include +#endif +#include +#include +#ifndef strcasecmp + #define strcasecmp stricmp +#endif +#else +// hton* and ntoh* functions +#include +#include +#include +#endif + +#ifdef TLS_AMALGAMATION +#ifdef I +#pragma push_macro("I") +#define TLS_I_MACRO +#undef I +#endif +#include "libtomcrypt.c" +#ifdef TLS_I_MACRO +#pragma pop_macro("I") +#undef TLS_I_MACRO +#endif +#else +#include +#endif + +#if (CRYPT <= 0x0117) + #define LTC_PKCS_1_EMSA LTC_LTC_PKCS_1_EMSA + #define LTC_PKCS_1_V1_5 LTC_LTC_PKCS_1_V1_5 + #define LTC_PKCS_1_PSS LTC_LTC_PKCS_1_PSS +#endif + +#ifdef WITH_KTLS + #include + #include + #include + // should get uapi/linux/tls.h (linux headers) + // rename it to ktls.h and add it to your project + // or just include tls.h instead of ktls.h + #include "ktls.h" +#endif + +#include "tlse.h" +#ifdef TLS_CURVE25519 + #include "curve25519.c" +#endif +// using ChaCha20 implementation by D. J. Bernstein + +#ifndef TLS_FORWARD_SECRECY +#undef TLS_ECDSA_SUPPORTED +#endif + +#ifndef TLS_ECDSA_SUPPORTED +// disable client ECDSA if not supported +#undef TLS_CLIENT_ECDSA +#endif + +#define TLS_DH_DEFAULT_P "87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597" +#define TLS_DH_DEFAULT_G "3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659" +#define TLS_DHE_KEY_SIZE 2048 + +// you should never use weak DH groups (1024 bits) +// but if you have old devices (like grandstream ip phones) +// that can't handle 2048bit DHE, uncomment next lines +// and define TLS_WEAK_DH_LEGACY_DEVICES +// #ifdef TLS_WEAK_DH_LEGACY_DEVICES +// #define TLS_DH_DEFAULT_P "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C69A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C013ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD7098488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708DF1FB2BC2E4A4371" +// #define TLS_DH_DEFAULT_G "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507FD6406CFF14266D31266FEA1E5C41564B777E690F5504F213160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28AD662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24855E6EEB22B3B2E5" +// #define TLS_DHE_KEY_SIZE 1024 +// #endif + +#ifndef TLS_MALLOC + #define TLS_MALLOC(size) malloc(size) +#endif +#ifndef TLS_REALLOC + #define TLS_REALLOC(ptr, size) realloc(ptr, size) +#endif +#ifndef TLS_FREE + #define TLS_FREE(ptr) if (ptr) free(ptr) +#endif + +#define TLS_ERROR(err, statement) if (err) statement; + +#ifdef DEBUG +#define DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__) +#define DEBUG_DUMP_HEX(buf, len) {if (buf) { int _i_; for (_i_ = 0; _i_ < len; _i_++) { DEBUG_PRINT("%02X ", (unsigned int)(buf)[_i_]); } } else { fprintf(stderr, "(null)"); } } +#define DEBUG_INDEX(fields) print_index(fields) +#define DEBUG_DUMP(buf, length) fwrite(buf, 1, length, stderr); +#define DEBUG_DUMP_HEX_LABEL(title, buf, len) {fprintf(stderr, "%s (%i): ", title, (int)len); DEBUG_DUMP_HEX(buf, len); fprintf(stderr, "\n");} +#else +#define DEBUG_PRINT(...) { } +#define DEBUG_DUMP_HEX(buf, len) { } +#define DEBUG_INDEX(fields) { } +#define DEBUG_DUMP(buf, length) { } +#define DEBUG_DUMP_HEX_LABEL(title, buf, len) { } +#endif + +#ifndef htonll +#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) +#endif + +#ifndef ntohll +#define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32)) +#endif + +#define TLS_CHANGE_CIPHER 0x14 +#define TLS_ALERT 0x15 +#define TLS_HANDSHAKE 0x16 +#define TLS_APPLICATION_DATA 0x17 + +#define TLS_SERIALIZED_OBJECT 0xFE + +#define TLS_CLIENT_HELLO_MINSIZE 41 +#define TLS_CLIENT_RANDOM_SIZE 32 +#define TLS_SERVER_RANDOM_SIZE 32 +#define TLS_MAX_SESSION_ID 32 +#define TLS_SHA256_MAC_SIZE 32 +#define TLS_SHA1_MAC_SIZE 20 +#define TLS_SHA384_MAC_SIZE 48 +#define TLS_MAX_MAC_SIZE TLS_SHA384_MAC_SIZE + // 160 +#define TLS_MAX_KEY_EXPANSION_SIZE 192 +// 512bits (sha256) = 64 bytes +#define TLS_MAX_HASH_LEN 64 +#define TLS_AES_IV_LENGTH 16 +#define TLS_AES_BLOCK_SIZE 16 +#define TLS_AES_GCM_IV_LENGTH 4 +#define TLS_13_AES_GCM_IV_LENGTH 12 +#define TLS_GCM_TAG_LEN 16 +#define TLS_MAX_TAG_LEN 16 +#define TLS_MIN_FINISHED_OPAQUE_LEN 12 + +#define TLS_BLOB_INCREMENT 0xFFF +#define TLS_ASN1_MAXLEVEL 0xFF + +#define DTLS_COOKIE_SIZE 32 + +#define TLS_MAX_SHA_SIZE 48 +// 16(md5) + 20(sha1) +#define TLS_V11_HASH_SIZE 36 +#define TLS_MAX_HASH_SIZE TLS_MAX_SHA_SIZE +// 16(md5) + 20(sha1) +#define TLS_MAX_RSA_KEY 2048 + +#define TLS_MAXTLS_APP_SIZE 0x4000 +// max 1 second sleep +#define TLS_MAX_ERROR_SLEEP_uS 1000000 +// max 5 seconds context sleep +#define TLS_MAX_ERROR_IDLE_S 5 + +#define TLS_V13_MAX_KEY_SIZE 32 +#define TLS_V13_MAX_IV_SIZE 12 + +#define VERSION_SUPPORTED(version, err) if ((version != TLS_V13) && (version != TLS_V12) && (version != TLS_V11) && (version != TLS_V10) && (version != DTLS_V13) && (version != DTLS_V12) && (version != DTLS_V10)) { if ((version == SSL_V30) && (context->connection_status == 0)) { version = TLS_V12; } else { DEBUG_PRINT("UNSUPPORTED TLS VERSION %x\n", (int)version); return err;} } +#define CHECK_SIZE(size, buf_size, err) if (((int)(size) > (int)(buf_size)) || ((int)(buf_size) < 0)) return err; +#define TLS_IMPORT_CHECK_SIZE(buf_pos, size, buf_size) if (((int)size > (int)buf_size - buf_pos) || ((int)buf_pos > (int)buf_size)) { DEBUG_PRINT("IMPORT ELEMENT SIZE ERROR\n"); tls_destroy_context(context); return NULL; } +#define CHECK_HANDSHAKE_STATE(context, n, limit) { if (context->hs_messages[n] >= limit) { DEBUG_PRINT("* UNEXPECTED MESSAGE (%i)\n", (int)n); payload_res = TLS_UNEXPECTED_MESSAGE; break; } context->hs_messages[n]++; } + +#if CRYPT > 0x0118 + #define TLS_TOMCRYPT_PRIVATE_DP(key) ((key).dp) + #define TLS_TOMCRYPT_PRIVATE_SET_INDEX(key, k_idx) +#else + #define TLS_TOMCRYPT_PRIVATE_DP(key) ((key)->dp) + #define TLS_TOMCRYPT_PRIVATE_SET_INDEX(key, k_idx) key->idx = k_idx +#endif + +#ifdef TLS_WITH_CHACHA20_POLY1305 +#define TLS_CHACHA20_IV_LENGTH 12 + +// ChaCha20 implementation by D. J. Bernstein +// Public domain. + +#define CHACHA_MINKEYLEN 16 +#define CHACHA_NONCELEN 8 +#define CHACHA_NONCELEN_96 12 +#define CHACHA_CTRLEN 8 +#define CHACHA_CTRLEN_96 4 +#define CHACHA_STATELEN (CHACHA_NONCELEN+CHACHA_CTRLEN) +#define CHACHA_BLOCKLEN 64 + +#define POLY1305_MAX_AAD 32 +#define POLY1305_KEYLEN 32 +#define POLY1305_TAGLEN 16 + +#define u_int unsigned int +#define uint8_t unsigned char +#define u_char unsigned char +#ifndef NULL +#define NULL (void *)0 +#endif + +#if (CRYPT >= 0x0117) && (0) + // to do: use ltc chacha/poly1305 implementation (working on big-endian machines) + #define chacha_ctx chacha20poly1305_state + #define poly1305_context poly1305_state + + #define _private_tls_poly1305_init(ctx, key, len) poly1305_init(ctx, key, len) + #define _private_tls_poly1305_update(ctx, in, len) poly1305_process(ctx, in, len) + #define _private_tls_poly1305_finish(ctx, mac) poly1305_done(ctx, mac, 16) +#else +struct chacha_ctx { + u_int input[16]; + uint8_t ks[CHACHA_BLOCKLEN]; + uint8_t unused; +}; + +static inline void chacha_keysetup(struct chacha_ctx *x, const u_char *k, u_int kbits); +static inline void chacha_ivsetup(struct chacha_ctx *x, const u_char *iv, const u_char *ctr); +static inline void chacha_ivsetup_96bitnonce(struct chacha_ctx *x, const u_char *iv, const u_char *ctr); +static inline void chacha_encrypt_bytes(struct chacha_ctx *x, const u_char *m, u_char *c, u_int bytes); +static inline int poly1305_generate_key(unsigned char *key256, unsigned char *nonce, unsigned int noncelen, unsigned char *poly_key, unsigned int counter); + +#define poly1305_block_size 16 +#define poly1305_context poly1305_state_internal_t + +//========== ChaCha20 from D. J. Bernstein ========= // +// Source available at https://cr.yp.to/chacha.html // + +typedef unsigned char u8; +typedef unsigned int u32; + +typedef struct chacha_ctx chacha_ctx; + +#define U8C(v) (v##U) +#define U32C(v) (v##U) + +#define U8V(v) ((u8)(v) & U8C(0xFF)) +#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF)) + +#define ROTL32(v, n) \ + (U32V((v) << (n)) | ((v) >> (32 - (n)))) + +#define _private_tls_U8TO32_LITTLE(p) \ + (((u32)((p)[0])) | \ + ((u32)((p)[1]) << 8) | \ + ((u32)((p)[2]) << 16) | \ + ((u32)((p)[3]) << 24)) + +#define _private_tls_U32TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v)); \ + (p)[1] = U8V((v) >> 8); \ + (p)[2] = U8V((v) >> 16); \ + (p)[3] = U8V((v) >> 24); \ + } while (0) + +#define ROTATE(v,c) (ROTL32(v,c)) +#define XOR(v,w) ((v) ^ (w)) +#define PLUS(v,w) (U32V((v) + (w))) +#define PLUSONE(v) (PLUS((v),1)) + +#define QUARTERROUND(a,b,c,d) \ + a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ + a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); + +static const char sigma[] = "expand 32-byte k"; +static const char tau[] = "expand 16-byte k"; + +static inline void chacha_keysetup(chacha_ctx *x, const u8 *k, u32 kbits) { + const char *constants; + + x->input[4] = _private_tls_U8TO32_LITTLE(k + 0); + x->input[5] = _private_tls_U8TO32_LITTLE(k + 4); + x->input[6] = _private_tls_U8TO32_LITTLE(k + 8); + x->input[7] = _private_tls_U8TO32_LITTLE(k + 12); + if (kbits == 256) { /* recommended */ + k += 16; + constants = sigma; + } else { /* kbits == 128 */ + constants = tau; + } + x->input[8] = _private_tls_U8TO32_LITTLE(k + 0); + x->input[9] = _private_tls_U8TO32_LITTLE(k + 4); + x->input[10] = _private_tls_U8TO32_LITTLE(k + 8); + x->input[11] = _private_tls_U8TO32_LITTLE(k + 12); + x->input[0] = _private_tls_U8TO32_LITTLE(constants + 0); + x->input[1] = _private_tls_U8TO32_LITTLE(constants + 4); + x->input[2] = _private_tls_U8TO32_LITTLE(constants + 8); + x->input[3] = _private_tls_U8TO32_LITTLE(constants + 12); +} + +static inline void chacha_key(chacha_ctx *x, u8 *k) { + _private_tls_U32TO8_LITTLE(k, x->input[4]); + _private_tls_U32TO8_LITTLE(k + 4, x->input[5]); + _private_tls_U32TO8_LITTLE(k + 8, x->input[6]); + _private_tls_U32TO8_LITTLE(k + 12, x->input[7]); + + _private_tls_U32TO8_LITTLE(k + 16, x->input[8]); + _private_tls_U32TO8_LITTLE(k + 20, x->input[9]); + _private_tls_U32TO8_LITTLE(k + 24, x->input[10]); + _private_tls_U32TO8_LITTLE(k + 28, x->input[11]); +} + +static inline void chacha_nonce(chacha_ctx *x, u8 *nonce) { + _private_tls_U32TO8_LITTLE(nonce + 0, x->input[13]); + _private_tls_U32TO8_LITTLE(nonce + 4, x->input[14]); + _private_tls_U32TO8_LITTLE(nonce + 8, x->input[15]); +} + +static inline void chacha_ivsetup(chacha_ctx *x, const u8 *iv, const u8 *counter) { + x->input[12] = counter == NULL ? 0 : _private_tls_U8TO32_LITTLE(counter + 0); + x->input[13] = counter == NULL ? 0 : _private_tls_U8TO32_LITTLE(counter + 4); + if (iv) { + x->input[14] = _private_tls_U8TO32_LITTLE(iv + 0); + x->input[15] = _private_tls_U8TO32_LITTLE(iv + 4); + } +} + +static inline void chacha_ivsetup_96bitnonce(chacha_ctx *x, const u8 *iv, const u8 *counter) { + x->input[12] = counter == NULL ? 0 : _private_tls_U8TO32_LITTLE(counter + 0); + if (iv) { + x->input[13] = _private_tls_U8TO32_LITTLE(iv + 0); + x->input[14] = _private_tls_U8TO32_LITTLE(iv + 4); + x->input[15] = _private_tls_U8TO32_LITTLE(iv + 8); + } +} + +static inline void chacha_ivupdate(chacha_ctx *x, const u8 *iv, const u8 *aad, const u8 *counter) { + x->input[12] = counter == NULL ? 0 : _private_tls_U8TO32_LITTLE(counter + 0); + x->input[13] = _private_tls_U8TO32_LITTLE(iv + 0); + x->input[14] = _private_tls_U8TO32_LITTLE(iv + 4) ^ _private_tls_U8TO32_LITTLE(aad); + x->input[15] = _private_tls_U8TO32_LITTLE(iv + 8) ^ _private_tls_U8TO32_LITTLE(aad + 4); +} + +static inline void chacha_encrypt_bytes(chacha_ctx *x, const u8 *m, u8 *c, u32 bytes) { + u32 x0, x1, x2, x3, x4, x5, x6, x7; + u32 x8, x9, x10, x11, x12, x13, x14, x15; + u32 j0, j1, j2, j3, j4, j5, j6, j7; + u32 j8, j9, j10, j11, j12, j13, j14, j15; + u8 *ctarget = NULL; + u8 tmp[64]; + u_int i; + + if (!bytes) + return; + + j0 = x->input[0]; + j1 = x->input[1]; + j2 = x->input[2]; + j3 = x->input[3]; + j4 = x->input[4]; + j5 = x->input[5]; + j6 = x->input[6]; + j7 = x->input[7]; + j8 = x->input[8]; + j9 = x->input[9]; + j10 = x->input[10]; + j11 = x->input[11]; + j12 = x->input[12]; + j13 = x->input[13]; + j14 = x->input[14]; + j15 = x->input[15]; + + for (;;) { + if (bytes < 64) { + for (i = 0; i < bytes; ++i) + tmp[i] = m[i]; + m = tmp; + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 20; i > 0; i -= 2) { + QUARTERROUND(x0, x4, x8, x12) + QUARTERROUND(x1, x5, x9, x13) + QUARTERROUND(x2, x6, x10, x14) + QUARTERROUND(x3, x7, x11, x15) + QUARTERROUND(x0, x5, x10, x15) + QUARTERROUND(x1, x6, x11, x12) + QUARTERROUND(x2, x7, x8, x13) + QUARTERROUND(x3, x4, x9, x14) + } + x0 = PLUS(x0, j0); + x1 = PLUS(x1, j1); + x2 = PLUS(x2, j2); + x3 = PLUS(x3, j3); + x4 = PLUS(x4, j4); + x5 = PLUS(x5, j5); + x6 = PLUS(x6, j6); + x7 = PLUS(x7, j7); + x8 = PLUS(x8, j8); + x9 = PLUS(x9, j9); + x10 = PLUS(x10, j10); + x11 = PLUS(x11, j11); + x12 = PLUS(x12, j12); + x13 = PLUS(x13, j13); + x14 = PLUS(x14, j14); + x15 = PLUS(x15, j15); + + if (bytes < 64) { + _private_tls_U32TO8_LITTLE(x->ks + 0, x0); + _private_tls_U32TO8_LITTLE(x->ks + 4, x1); + _private_tls_U32TO8_LITTLE(x->ks + 8, x2); + _private_tls_U32TO8_LITTLE(x->ks + 12, x3); + _private_tls_U32TO8_LITTLE(x->ks + 16, x4); + _private_tls_U32TO8_LITTLE(x->ks + 20, x5); + _private_tls_U32TO8_LITTLE(x->ks + 24, x6); + _private_tls_U32TO8_LITTLE(x->ks + 28, x7); + _private_tls_U32TO8_LITTLE(x->ks + 32, x8); + _private_tls_U32TO8_LITTLE(x->ks + 36, x9); + _private_tls_U32TO8_LITTLE(x->ks + 40, x10); + _private_tls_U32TO8_LITTLE(x->ks + 44, x11); + _private_tls_U32TO8_LITTLE(x->ks + 48, x12); + _private_tls_U32TO8_LITTLE(x->ks + 52, x13); + _private_tls_U32TO8_LITTLE(x->ks + 56, x14); + _private_tls_U32TO8_LITTLE(x->ks + 60, x15); + } + + x0 = XOR(x0, _private_tls_U8TO32_LITTLE(m + 0)); + x1 = XOR(x1, _private_tls_U8TO32_LITTLE(m + 4)); + x2 = XOR(x2, _private_tls_U8TO32_LITTLE(m + 8)); + x3 = XOR(x3, _private_tls_U8TO32_LITTLE(m + 12)); + x4 = XOR(x4, _private_tls_U8TO32_LITTLE(m + 16)); + x5 = XOR(x5, _private_tls_U8TO32_LITTLE(m + 20)); + x6 = XOR(x6, _private_tls_U8TO32_LITTLE(m + 24)); + x7 = XOR(x7, _private_tls_U8TO32_LITTLE(m + 28)); + x8 = XOR(x8, _private_tls_U8TO32_LITTLE(m + 32)); + x9 = XOR(x9, _private_tls_U8TO32_LITTLE(m + 36)); + x10 = XOR(x10, _private_tls_U8TO32_LITTLE(m + 40)); + x11 = XOR(x11, _private_tls_U8TO32_LITTLE(m + 44)); + x12 = XOR(x12, _private_tls_U8TO32_LITTLE(m + 48)); + x13 = XOR(x13, _private_tls_U8TO32_LITTLE(m + 52)); + x14 = XOR(x14, _private_tls_U8TO32_LITTLE(m + 56)); + x15 = XOR(x15, _private_tls_U8TO32_LITTLE(m + 60)); + + j12 = PLUSONE(j12); + if (!j12) { + j13 = PLUSONE(j13); + /* + * Stopping at 2^70 bytes per nonce is the user's + * responsibility. + */ + } + + _private_tls_U32TO8_LITTLE(c + 0, x0); + _private_tls_U32TO8_LITTLE(c + 4, x1); + _private_tls_U32TO8_LITTLE(c + 8, x2); + _private_tls_U32TO8_LITTLE(c + 12, x3); + _private_tls_U32TO8_LITTLE(c + 16, x4); + _private_tls_U32TO8_LITTLE(c + 20, x5); + _private_tls_U32TO8_LITTLE(c + 24, x6); + _private_tls_U32TO8_LITTLE(c + 28, x7); + _private_tls_U32TO8_LITTLE(c + 32, x8); + _private_tls_U32TO8_LITTLE(c + 36, x9); + _private_tls_U32TO8_LITTLE(c + 40, x10); + _private_tls_U32TO8_LITTLE(c + 44, x11); + _private_tls_U32TO8_LITTLE(c + 48, x12); + _private_tls_U32TO8_LITTLE(c + 52, x13); + _private_tls_U32TO8_LITTLE(c + 56, x14); + _private_tls_U32TO8_LITTLE(c + 60, x15); + + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0; i < bytes; ++i) + ctarget[i] = c[i]; + } + x->input[12] = j12; + x->input[13] = j13; + x->unused = 64 - bytes; + return; + } + bytes -= 64; + c += 64; + m += 64; + } +} + +static inline void chacha20_block(chacha_ctx *x, unsigned char *c, u_int len) { + u_int i; + + unsigned int state[16]; + for (i = 0; i < 16; i++) + state[i] = x->input[i]; + for (i = 20; i > 0; i -= 2) { + QUARTERROUND(state[0], state[4], state[8], state[12]) + QUARTERROUND(state[1], state[5], state[9], state[13]) + QUARTERROUND(state[2], state[6], state[10], state[14]) + QUARTERROUND(state[3], state[7], state[11], state[15]) + QUARTERROUND(state[0], state[5], state[10], state[15]) + QUARTERROUND(state[1], state[6], state[11], state[12]) + QUARTERROUND(state[2], state[7], state[8], state[13]) + QUARTERROUND(state[3], state[4], state[9], state[14]) + } + + for (i = 0; i < 16; i++) + x->input[i] = PLUS(x->input[i], state[i]); + + for (i = 0; i < len; i += 4) { + _private_tls_U32TO8_LITTLE(c + i, x->input[i/4]); + } +} + +static inline int poly1305_generate_key(unsigned char *key256, unsigned char *nonce, unsigned int noncelen, unsigned char *poly_key, unsigned int counter) { + struct chacha_ctx ctx; + uint64_t ctr; + memset(&ctx, 0, sizeof(ctx)); + chacha_keysetup(&ctx, key256, 256); + switch (noncelen) { + case 8: + ctr = counter; + chacha_ivsetup(&ctx, nonce, (unsigned char *)&ctr); + break; + case 12: + chacha_ivsetup_96bitnonce(&ctx, nonce, (unsigned char *)&counter); + break; + default: + return -1; + } + chacha20_block(&ctx, poly_key, POLY1305_KEYLEN); + return 0; +} + +/* 17 + sizeof(size_t) + 14*sizeof(unsigned long) */ +typedef struct poly1305_state_internal_t { + unsigned long r[5]; + unsigned long h[5]; + unsigned long pad[4]; + size_t leftover; + unsigned char buffer[poly1305_block_size]; + unsigned char final; +} poly1305_state_internal_t; + +/* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little endian */ +static unsigned long _private_tls_U8TO32(const unsigned char *p) { + return + (((unsigned long)(p[0] & 0xff) ) | + ((unsigned long)(p[1] & 0xff) << 8) | + ((unsigned long)(p[2] & 0xff) << 16) | + ((unsigned long)(p[3] & 0xff) << 24)); +} + +/* store a 32 bit unsigned integer as four 8 bit unsigned integers in little endian */ +static void _private_tls_U32TO8(unsigned char *p, unsigned long v) { + p[0] = (v ) & 0xff; + p[1] = (v >> 8) & 0xff; + p[2] = (v >> 16) & 0xff; + p[3] = (v >> 24) & 0xff; +} + +void _private_tls_poly1305_init(poly1305_context *ctx, const unsigned char key[32]) { + poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; + + /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */ + st->r[0] = (_private_tls_U8TO32(&key[ 0]) ) & 0x3ffffff; + st->r[1] = (_private_tls_U8TO32(&key[ 3]) >> 2) & 0x3ffff03; + st->r[2] = (_private_tls_U8TO32(&key[ 6]) >> 4) & 0x3ffc0ff; + st->r[3] = (_private_tls_U8TO32(&key[ 9]) >> 6) & 0x3f03fff; + st->r[4] = (_private_tls_U8TO32(&key[12]) >> 8) & 0x00fffff; + + /* h = 0 */ + st->h[0] = 0; + st->h[1] = 0; + st->h[2] = 0; + st->h[3] = 0; + st->h[4] = 0; + + /* save pad for later */ + st->pad[0] = _private_tls_U8TO32(&key[16]); + st->pad[1] = _private_tls_U8TO32(&key[20]); + st->pad[2] = _private_tls_U8TO32(&key[24]); + st->pad[3] = _private_tls_U8TO32(&key[28]); + + st->leftover = 0; + st->final = 0; +} + +static void _private_tls_poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) { + const unsigned long hibit = (st->final) ? 0 : (1UL << 24); /* 1 << 128 */ + unsigned long r0,r1,r2,r3,r4; + unsigned long s1,s2,s3,s4; + unsigned long h0,h1,h2,h3,h4; + unsigned long long d0,d1,d2,d3,d4; + unsigned long c; + + r0 = st->r[0]; + r1 = st->r[1]; + r2 = st->r[2]; + r3 = st->r[3]; + r4 = st->r[4]; + + s1 = r1 * 5; + s2 = r2 * 5; + s3 = r3 * 5; + s4 = r4 * 5; + + h0 = st->h[0]; + h1 = st->h[1]; + h2 = st->h[2]; + h3 = st->h[3]; + h4 = st->h[4]; + + while (bytes >= poly1305_block_size) { + /* h += m[i] */ + h0 += (_private_tls_U8TO32(m+ 0) ) & 0x3ffffff; + h1 += (_private_tls_U8TO32(m+ 3) >> 2) & 0x3ffffff; + h2 += (_private_tls_U8TO32(m+ 6) >> 4) & 0x3ffffff; + h3 += (_private_tls_U8TO32(m+ 9) >> 6) & 0x3ffffff; + h4 += (_private_tls_U8TO32(m+12) >> 8) | hibit; + + /* h *= r */ + d0 = ((unsigned long long)h0 * r0) + ((unsigned long long)h1 * s4) + ((unsigned long long)h2 * s3) + ((unsigned long long)h3 * s2) + ((unsigned long long)h4 * s1); + d1 = ((unsigned long long)h0 * r1) + ((unsigned long long)h1 * r0) + ((unsigned long long)h2 * s4) + ((unsigned long long)h3 * s3) + ((unsigned long long)h4 * s2); + d2 = ((unsigned long long)h0 * r2) + ((unsigned long long)h1 * r1) + ((unsigned long long)h2 * r0) + ((unsigned long long)h3 * s4) + ((unsigned long long)h4 * s3); + d3 = ((unsigned long long)h0 * r3) + ((unsigned long long)h1 * r2) + ((unsigned long long)h2 * r1) + ((unsigned long long)h3 * r0) + ((unsigned long long)h4 * s4); + d4 = ((unsigned long long)h0 * r4) + ((unsigned long long)h1 * r3) + ((unsigned long long)h2 * r2) + ((unsigned long long)h3 * r1) + ((unsigned long long)h4 * r0); + + /* (partial) h %= p */ + c = (unsigned long)(d0 >> 26); h0 = (unsigned long)d0 & 0x3ffffff; + d1 += c; c = (unsigned long)(d1 >> 26); h1 = (unsigned long)d1 & 0x3ffffff; + d2 += c; c = (unsigned long)(d2 >> 26); h2 = (unsigned long)d2 & 0x3ffffff; + d3 += c; c = (unsigned long)(d3 >> 26); h3 = (unsigned long)d3 & 0x3ffffff; + d4 += c; c = (unsigned long)(d4 >> 26); h4 = (unsigned long)d4 & 0x3ffffff; + h0 += c * 5; c = (h0 >> 26); h0 = h0 & 0x3ffffff; + h1 += c; + + m += poly1305_block_size; + bytes -= poly1305_block_size; + } + + st->h[0] = h0; + st->h[1] = h1; + st->h[2] = h2; + st->h[3] = h3; + st->h[4] = h4; +} + +void _private_tls_poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) { + poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; + unsigned long h0,h1,h2,h3,h4,c; + unsigned long g0,g1,g2,g3,g4; + unsigned long long f; + unsigned long mask; + + /* process the remaining block */ + if (st->leftover) { + size_t i = st->leftover; + st->buffer[i++] = 1; + for (; i < poly1305_block_size; i++) + st->buffer[i] = 0; + st->final = 1; + _private_tls_poly1305_blocks(st, st->buffer, poly1305_block_size); + } + + /* fully carry h */ + h0 = st->h[0]; + h1 = st->h[1]; + h2 = st->h[2]; + h3 = st->h[3]; + h4 = st->h[4]; + + c = h1 >> 26; h1 = h1 & 0x3ffffff; + h2 += c; c = h2 >> 26; h2 = h2 & 0x3ffffff; + h3 += c; c = h3 >> 26; h3 = h3 & 0x3ffffff; + h4 += c; c = h4 >> 26; h4 = h4 & 0x3ffffff; + h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff; + h1 += c; + + /* compute h + -p */ + g0 = h0 + 5; c = g0 >> 26; g0 &= 0x3ffffff; + g1 = h1 + c; c = g1 >> 26; g1 &= 0x3ffffff; + g2 = h2 + c; c = g2 >> 26; g2 &= 0x3ffffff; + g3 = h3 + c; c = g3 >> 26; g3 &= 0x3ffffff; + g4 = h4 + c - (1UL << 26); + + /* select h if h < p, or h + -p if h >= p */ + mask = (g4 >> ((sizeof(unsigned long) * 8) - 1)) - 1; + g0 &= mask; + g1 &= mask; + g2 &= mask; + g3 &= mask; + g4 &= mask; + mask = ~mask; + h0 = (h0 & mask) | g0; + h1 = (h1 & mask) | g1; + h2 = (h2 & mask) | g2; + h3 = (h3 & mask) | g3; + h4 = (h4 & mask) | g4; + + /* h = h % (2^128) */ + h0 = ((h0 ) | (h1 << 26)) & 0xffffffff; + h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff; + h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff; + h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff; + + /* mac = (h + pad) % (2^128) */ + f = (unsigned long long)h0 + st->pad[0] ; h0 = (unsigned long)f; + f = (unsigned long long)h1 + st->pad[1] + (f >> 32); h1 = (unsigned long)f; + f = (unsigned long long)h2 + st->pad[2] + (f >> 32); h2 = (unsigned long)f; + f = (unsigned long long)h3 + st->pad[3] + (f >> 32); h3 = (unsigned long)f; + + _private_tls_U32TO8(mac + 0, h0); + _private_tls_U32TO8(mac + 4, h1); + _private_tls_U32TO8(mac + 8, h2); + _private_tls_U32TO8(mac + 12, h3); + + /* zero out the state */ + st->h[0] = 0; + st->h[1] = 0; + st->h[2] = 0; + st->h[3] = 0; + st->h[4] = 0; + st->r[0] = 0; + st->r[1] = 0; + st->r[2] = 0; + st->r[3] = 0; + st->r[4] = 0; + st->pad[0] = 0; + st->pad[1] = 0; + st->pad[2] = 0; + st->pad[3] = 0; +} + +void _private_tls_poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) { + poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; + size_t i; + /* handle leftover */ + if (st->leftover) { + size_t want = (poly1305_block_size - st->leftover); + if (want > bytes) + want = bytes; + for (i = 0; i < want; i++) + st->buffer[st->leftover + i] = m[i]; + bytes -= want; + m += want; + st->leftover += want; + if (st->leftover < poly1305_block_size) + return; + _private_tls_poly1305_blocks(st, st->buffer, poly1305_block_size); + st->leftover = 0; + } + + /* process full blocks */ + if (bytes >= poly1305_block_size) { + size_t want = (bytes & ~(poly1305_block_size - 1)); + _private_tls_poly1305_blocks(st, m, want); + m += want; + bytes -= want; + } + + /* store leftover */ + if (bytes) { + for (i = 0; i < bytes; i++) + st->buffer[st->leftover + i] = m[i]; + st->leftover += bytes; + } +} + +int poly1305_verify(const unsigned char mac1[16], const unsigned char mac2[16]) { + size_t i; + unsigned int dif = 0; + for (i = 0; i < 16; i++) + dif |= (mac1[i] ^ mac2[i]); + dif = (dif - 1) >> ((sizeof(unsigned int) * 8) - 1); + return (dif & 1); +} + +void chacha20_poly1305_key(struct chacha_ctx *ctx, unsigned char *poly1305_key) { + unsigned char key[32]; + unsigned char nonce[12]; + chacha_key(ctx, key); + chacha_nonce(ctx, nonce); + poly1305_generate_key(key, nonce, sizeof(nonce), poly1305_key, 0); +} + +int chacha20_poly1305_aead(struct chacha_ctx *ctx, unsigned char *pt, unsigned int len, unsigned char *aad, unsigned int aad_len, unsigned char *poly_key, unsigned char *out) { + static unsigned char zeropad[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + if (aad_len > POLY1305_MAX_AAD) + return -1; + + unsigned int counter = 1; + chacha_ivsetup_96bitnonce(ctx, NULL, (unsigned char *)&counter); + chacha_encrypt_bytes(ctx, pt, out, len); + + poly1305_context aead_ctx; + _private_tls_poly1305_init(&aead_ctx, poly_key); + _private_tls_poly1305_update(&aead_ctx, aad, aad_len); + int rem = aad_len % 16; + if (rem) + _private_tls_poly1305_update(&aead_ctx, zeropad, 16 - rem); + _private_tls_poly1305_update(&aead_ctx, out, len); + rem = len % 16; + if (rem) + _private_tls_poly1305_update(&aead_ctx, zeropad, 16 - rem); + + unsigned char trail[16]; + _private_tls_U32TO8(trail, aad_len); + *(int *)(trail + 4) = 0; + _private_tls_U32TO8(trail + 8, len); + *(int *)(trail + 12) = 0; + + _private_tls_poly1305_update(&aead_ctx, trail, 16); + _private_tls_poly1305_finish(&aead_ctx, out + len); + + return len + POLY1305_TAGLEN; +} +#endif +#endif + +typedef enum { + KEA_dhe_dss, + KEA_dhe_rsa, + KEA_dh_anon, + KEA_rsa, + KEA_dh_dss, + KEA_dh_rsa, + KEA_ec_diffie_hellman +} KeyExchangeAlgorithm; + +typedef enum { + rsa_sign = 1, + dss_sign = 2, + rsa_fixed_dh = 3, + dss_fixed_dh = 4, + rsa_ephemeral_dh_RESERVED = 5, + dss_ephemeral_dh_RESERVED = 6, + fortezza_dms_RESERVED = 20, + ecdsa_sign = 64, + rsa_fixed_ecdh = 65, + ecdsa_fixed_ecdh = 66 +} TLSClientCertificateType; + +typedef enum { + _none = 0, + md5 = 1, + sha1 = 2, + sha224 = 3, + sha256 = 4, + sha384 = 5, + sha512 = 6, + _md5_sha1 = 255 +} TLSHashAlgorithm; + +typedef enum { + anonymous = 0, + rsa = 1, + dsa = 2, + ecdsa = 3 +} TLSSignatureAlgorithm; + +struct _private_OID_chain { + void *top; + unsigned char *oid; +}; + +struct TLSCertificate { + unsigned short version; + unsigned int algorithm; + unsigned int key_algorithm; + unsigned int ec_algorithm; + unsigned char *exponent; + unsigned int exponent_len; + unsigned char *pk; + unsigned int pk_len; + unsigned char *priv; + unsigned int priv_len; + unsigned char *issuer_country; + unsigned char *issuer_state; + unsigned char *issuer_location; + unsigned char *issuer_entity; + unsigned char *issuer_subject; + unsigned char *not_before; + unsigned char *not_after; + unsigned char *country; + unsigned char *state; + unsigned char *location; + unsigned char *entity; + unsigned char *subject; + unsigned char **san; + unsigned short san_length; + unsigned char *ocsp; + unsigned char *serial_number; + unsigned int serial_len; + unsigned char *sign_key; + unsigned int sign_len; + unsigned char *fingerprint; + unsigned char *der_bytes; + unsigned int der_len; + unsigned char *bytes; + unsigned int len; +}; + +typedef struct { + union { + symmetric_CBC aes_local; + gcm_state aes_gcm_local; +#ifdef TLS_WITH_CHACHA20_POLY1305 + chacha_ctx chacha_local; +#endif + } ctx_local; + union { + symmetric_CBC aes_remote; + gcm_state aes_gcm_remote; +#ifdef TLS_WITH_CHACHA20_POLY1305 + chacha_ctx chacha_remote; +#endif + } ctx_remote; + union { + unsigned char local_mac[TLS_MAX_MAC_SIZE]; + unsigned char local_aead_iv[TLS_AES_GCM_IV_LENGTH]; +#ifdef WITH_TLS_13 + unsigned char local_iv[TLS_13_AES_GCM_IV_LENGTH]; +#endif +#ifdef TLS_WITH_CHACHA20_POLY1305 + unsigned char local_nonce[TLS_CHACHA20_IV_LENGTH]; +#endif + } ctx_local_mac; + union { + unsigned char remote_aead_iv[TLS_AES_GCM_IV_LENGTH]; + unsigned char remote_mac[TLS_MAX_MAC_SIZE]; +#ifdef WITH_TLS_13 + unsigned char remote_iv[TLS_13_AES_GCM_IV_LENGTH]; +#endif +#ifdef TLS_WITH_CHACHA20_POLY1305 + unsigned char remote_nonce[TLS_CHACHA20_IV_LENGTH]; +#endif + } ctx_remote_mac; + unsigned char created; +} TLSCipher; + +typedef struct { + hash_state hash32; + hash_state hash48; +#ifdef TLS_LEGACY_SUPPORT + hash_state hash2; +#endif + unsigned char created; +} TLSHash; + +#ifdef TLS_FORWARD_SECRECY +#define mp_init(a) ltc_mp.init(a) +#define mp_init_multi ltc_init_multi +#define mp_clear(a) ltc_mp.deinit(a) +#define mp_clear_multi ltc_deinit_multi +#define mp_count_bits(a) ltc_mp.count_bits(a) +#define mp_read_radix(a, b, c) ltc_mp.read_radix(a, b, c) +#define mp_unsigned_bin_size(a) ltc_mp.unsigned_size(a) +#define mp_to_unsigned_bin(a, b) ltc_mp.unsigned_write(a, b) +#define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c) +#define mp_exptmod(a, b, c, d) ltc_mp.exptmod(a, b, c, d) +#define mp_add(a, b, c) ltc_mp.add(a, b, c) +#define mp_mul(a, b, c) ltc_mp.mul(a, b, c) +#define mp_cmp(a, b) ltc_mp.compare(a, b) +#define mp_cmp_d(a, b) ltc_mp.compare_d(a, b) +#define mp_sqr(a, b) ltc_mp.sqr(a, b) +#define mp_mod(a, b, c) ltc_mp.mpdiv(a, b, NULL, c) +#define mp_sub(a, b, c) ltc_mp.sub(a, b, c) +#define mp_set(a, b) ltc_mp.set_int(a, b) + +typedef struct { + int iana; + void *x; + void *y; + void *p; + void *g; +} DHKey; + +#ifdef WITH_TLS_13 +static DHKey ffdhe2048 = { + 0x0100, + NULL, + NULL, + (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B423861285C97FFFFFFFFFFFFFFFF", + (void *)"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002" +}; + +static DHKey ffdhe3072 = { + 0x0101, + NULL, + NULL, + (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B66C62E37FFFFFFFFFFFFFFFF", + (void *)"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002" +}; + +static DHKey ffdhe4096 = { + 0x0102, + NULL, + NULL, + (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E655F6AFFFFFFFFFFFFFFFF", + (void *)"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002" +}; + +static DHKey ffdhe6144 = { + 0x0103, + NULL, + NULL, + (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD9020BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA63BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3ACDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477A52471F7A9A96910B855322EDB6340D8A00EF092350511E30ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538CD72B03746AE77F5E62292C311562A846505DC82DB854338AE49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B045B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1A41D570D7938DAD4A40E329CD0E40E65FFFFFFFFFFFFFFFF", + (void *)"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002" +}; + +static DHKey ffdhe8192 = { + 0x0104, + NULL, + NULL, + (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD9020BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA63BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3ACDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477A52471F7A9A96910B855322EDB6340D8A00EF092350511E30ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538CD72B03746AE77F5E62292C311562A846505DC82DB854338AE49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B045B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1A41D570D7938DAD4A40E329CCFF46AAA36AD004CF600C8381E425A31D951AE64FDB23FCEC9509D43687FEB69EDD1CC5E0B8CC3BDF64B10EF86B63142A3AB8829555B2F747C932665CB2C0F1CC01BD70229388839D2AF05E454504AC78B7582822846C0BA35C35F5C59160CC046FD8251541FC68C9C86B022BB7099876A460E7451A8A93109703FEE1C217E6C3826E52C51AA691E0E423CFC99E9E31650C1217B624816CDAD9A95F9D5B8019488D9C0A0A1FE3075A577E23183F81D4A3F2FA4571EFC8CE0BA8A4FE8B6855DFE72B0A66EDED2FBABFBE58A30FAFABE1C5D71A87E2F741EF8C1FE86FEA6BBFDE530677F0D97D11D49F7A8443D0822E506A9F4614E011E2A94838FF88CD68C8BB7C5C6424CFFFFFFFFFFFFFFFF", + (void *)"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002" +}; +#endif + +struct ECCCurveParameters { + int size; + int iana; + const char *name; + const char *P; + const char *A; + const char *B; + const char *Gx; + const char *Gy; + const char *order; + ltc_ecc_set_type dp; +}; + +static struct ECCCurveParameters secp192r1 = { + 24, + 19, + "secp192r1", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", // P + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", // A + "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", // B + "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012", // Gx + "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811", // Gy + "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831" // order (n) +}; + + +static struct ECCCurveParameters secp224r1 = { + 28, + 21, + "secp224r1", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001", // P + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE", // A + "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4", // B + "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21", // Gx + "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34", // Gy + "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D" // order (n) +}; + +static struct ECCCurveParameters secp224k1 = { + 28, + 20, + "secp224k1", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D", // P + "00000000000000000000000000000000000000000000000000000000", // A + "00000000000000000000000000000000000000000000000000000005", // B + "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C", // Gx + "7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5", // Gy + "0000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7" // order (n) +}; + +static struct ECCCurveParameters secp256r1 = { + 32, + 23, + "secp256r1", + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", // P + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", // A + "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", // B + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", // Gx + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", // Gy + "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551" // order (n) +}; + +static struct ECCCurveParameters secp256k1 = { + 32, + 22, + "secp256k1", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", // P + "0000000000000000000000000000000000000000000000000000000000000000", // A + "0000000000000000000000000000000000000000000000000000000000000007", // B + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", // Gx + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", // Gy + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141" // order (n) +}; + +static struct ECCCurveParameters secp384r1 = { + 48, + 24, + "secp384r1", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", // P + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", // A + "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", // B + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", // Gx + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", // Gy + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973" // order (n) +}; + +static struct ECCCurveParameters secp521r1 = { + 66, + 25, + "secp521r1", + "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", // P + "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", // A + "0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", // B + "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", // Gx + "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", // Gy + "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409" // order (n) +}; + +#ifdef TLS_CURVE25519 +// dummy +static struct ECCCurveParameters x25519 = { + 32, + 29, + "x25519", + "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED", // P + "0000000000000000000000000000000000000000000000000000000000076D06", // A + "0000000000000000000000000000000000000000000000000000000000000000", // B + "0000000000000000000000000000000000000000000000000000000000000009", // Gx + "20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9", // Gy + "1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED" // order (n) +}; +#endif + +static struct ECCCurveParameters * const default_curve = &secp256r1; + +void init_curve(struct ECCCurveParameters *curve) { + curve->dp.size = curve->size; + curve->dp.name = (char *)curve->name; + curve->dp.B = (char *)curve->B; + curve->dp.prime = (char *)curve->P; + curve->dp.Gx = (char *)curve->Gx; + curve->dp.Gy = (char *)curve->Gy; + curve->dp.order = (char *)curve->order; +} + +void init_curves() { + init_curve(&secp192r1); + init_curve(&secp224r1); + init_curve(&secp224k1); + init_curve(&secp256r1); + init_curve(&secp256k1); + init_curve(&secp384r1); + init_curve(&secp521r1); +} +#endif + +struct TLSContext { + unsigned char remote_random[TLS_CLIENT_RANDOM_SIZE]; + unsigned char local_random[TLS_SERVER_RANDOM_SIZE]; + unsigned char session[TLS_MAX_SESSION_ID]; + unsigned char session_size; + unsigned short cipher; + unsigned short version; + unsigned char is_server; + struct TLSCertificate **certificates; + struct TLSCertificate *private_key; +#ifdef TLS_ECDSA_SUPPORTED + struct TLSCertificate *ec_private_key; +#endif +#ifdef TLS_FORWARD_SECRECY + DHKey *dhe; + ecc_key *ecc_dhe; + char *default_dhe_p; + char *default_dhe_g; + const struct ECCCurveParameters *curve; +#endif + struct TLSCertificate **client_certificates; + unsigned int certificates_count; + unsigned int client_certificates_count; + unsigned char *master_key; + unsigned int master_key_len; + unsigned char *premaster_key; + unsigned int premaster_key_len; + unsigned char cipher_spec_set; + TLSCipher crypto; + TLSHash *handshake_hash; + + unsigned char *message_buffer; + unsigned int message_buffer_len; + uint64_t remote_sequence_number; + uint64_t local_sequence_number; + + unsigned char connection_status; + unsigned char critical_error; + unsigned char error_code; + + unsigned char *tls_buffer; + unsigned int tls_buffer_len; + + unsigned char *application_buffer; + unsigned int application_buffer_len; + unsigned char is_child; + unsigned char exportable; + unsigned char *exportable_keys; + unsigned char exportable_size; + char *sni; + unsigned char request_client_certificate; + unsigned char dtls; + unsigned short dtls_epoch_local; + unsigned short dtls_epoch_remote; + unsigned char *dtls_cookie; + unsigned char dtls_cookie_len; + unsigned char dtls_seq; + unsigned char *cached_handshake; + unsigned int cached_handshake_len; + unsigned char client_verified; + // handshake messages flags + unsigned char hs_messages[11]; + void *user_data; + struct TLSCertificate **root_certificates; + unsigned int root_count; +#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION + unsigned char *verify_data; + unsigned char verify_len; +#endif +#ifdef WITH_TLS_13 + unsigned char *finished_key; + unsigned char *remote_finished_key; + unsigned char *server_finished_hash; +#endif +#ifdef TLS_CURVE25519 + unsigned char *client_secret; +#endif + char **alpn; + unsigned char alpn_count; + char *negotiated_alpn; + unsigned int sleep_until; + unsigned short tls13_version; +#ifdef TLS_12_FALSE_START + unsigned char false_start; +#endif +}; + +struct TLSPacket { + unsigned char *buf; + unsigned int len; + unsigned int size; + unsigned char broken; + struct TLSContext *context; +}; + +#ifdef SSL_COMPATIBLE_INTERFACE + +typedef int (*SOCKET_RECV_CALLBACK)(int socket, void *buffer, size_t length, int flags); +typedef int (*SOCKET_SEND_CALLBACK)(int socket, const void *buffer, size_t length, int flags); + +#ifndef _WIN32 +#include +#endif +#endif + +static const unsigned int version_id[] = {1, 1, 1, 0}; +static const unsigned int pk_id[] = {1, 1, 7, 0}; +static const unsigned int serial_id[] = {1, 1, 2, 1, 0}; +static const unsigned int issurer_id[] = {1, 1, 4, 0}; +static const unsigned int owner_id[] = {1, 1, 6, 0}; +static const unsigned int validity_id[] = {1, 1, 5, 0}; +static const unsigned int algorithm_id[] = {1, 1, 3, 0}; +static const unsigned int sign_id[] = {1, 3, 2, 1, 0}; +static const unsigned int priv_id[] = {1, 4, 0}; +static const unsigned int priv_der_id[] = {1, 3, 1, 0}; +static const unsigned int ecc_priv_id[] = {1, 2, 0}; + +static const unsigned char country_oid[] = {0x55, 0x04, 0x06, 0x00}; +static const unsigned char state_oid[] = {0x55, 0x04, 0x08, 0x00}; +static const unsigned char location_oid[] = {0x55, 0x04, 0x07, 0x00}; +static const unsigned char entity_oid[] = {0x55, 0x04, 0x0A, 0x00}; +static const unsigned char subject_oid[] = {0x55, 0x04, 0x03, 0x00}; +static const unsigned char san_oid[] = {0x55, 0x1D, 0x11, 0x00}; +static const unsigned char ocsp_oid[] = {0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x00}; + +static const unsigned char TLS_RSA_SIGN_RSA_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x00}; +static const unsigned char TLS_RSA_SIGN_MD5_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04, 0x00}; +static const unsigned char TLS_RSA_SIGN_SHA1_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x00}; +static const unsigned char TLS_RSA_SIGN_SHA256_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x00}; +static const unsigned char TLS_RSA_SIGN_SHA384_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C, 0x00}; +static const unsigned char TLS_RSA_SIGN_SHA512_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D, 0x00}; + +// static const unsigned char TLS_ECDSA_SIGN_SHA1_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x01, 0x05, 0x00, 0x00}; +// static const unsigned char TLS_ECDSA_SIGN_SHA224_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01, 0x05, 0x00, 0x00}; +static const unsigned char TLS_ECDSA_SIGN_SHA256_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x05, 0x00, 0x00}; +// static const unsigned char TLS_ECDSA_SIGN_SHA384_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03, 0x05, 0x00, 0x00}; +// static const unsigned char TLS_ECDSA_SIGN_SHA512_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04, 0x05, 0x00, 0x00}; + +static const unsigned char TLS_EC_PUBLIC_KEY_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x00}; + +static const unsigned char TLS_EC_prime192v1_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01, 0x00}; +static const unsigned char TLS_EC_prime192v2_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x02, 0x00}; +static const unsigned char TLS_EC_prime192v3_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x03, 0x00}; +static const unsigned char TLS_EC_prime239v1_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x04, 0x00}; +static const unsigned char TLS_EC_prime239v2_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x05, 0x00}; +static const unsigned char TLS_EC_prime239v3_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x06, 0x00}; +static const unsigned char TLS_EC_prime256v1_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x00}; + +#define TLS_EC_secp256r1_OID TLS_EC_prime256v1_OID +static const unsigned char TLS_EC_secp224r1_OID[] = {0x2B, 0x81, 0x04, 0x00, 0x21, 0x00}; +static const unsigned char TLS_EC_secp384r1_OID[] = {0x2B, 0x81, 0x04, 0x00, 0x22, 0x00}; +static const unsigned char TLS_EC_secp521r1_OID[] = {0x2B, 0x81, 0x04, 0x00, 0x23, 0x00}; + +struct TLSCertificate *asn1_parse(struct TLSContext *context, const unsigned char *buffer, unsigned int size, int client_cert); +int _private_tls_update_hash(struct TLSContext *context, const unsigned char *in, unsigned int len); +struct TLSPacket *tls_build_finished(struct TLSContext *context); +unsigned int _private_tls_hmac_message(unsigned char local, struct TLSContext *context, const unsigned char *buf, int buf_len, const unsigned char *buf2, int buf_len2, unsigned char *out, unsigned int outlen, uint64_t remote_sequence_number); +int tls_random(unsigned char *key, int len); +void tls_destroy_packet(struct TLSPacket *packet); +struct TLSPacket *tls_build_hello(struct TLSContext *context, int tls13_downgrade); +struct TLSPacket *tls_build_certificate(struct TLSContext *context); +struct TLSPacket *tls_build_done(struct TLSContext *context); +struct TLSPacket *tls_build_alert(struct TLSContext *context, char critical, unsigned char code); +struct TLSPacket *tls_build_change_cipher_spec(struct TLSContext *context); +struct TLSPacket *tls_build_verify_request(struct TLSContext *context); +int _private_tls_crypto_create(struct TLSContext *context, int key_length, unsigned char *localkey, unsigned char *localiv, unsigned char *remotekey, unsigned char *remoteiv); +int _private_tls_get_hash(struct TLSContext *context, unsigned char *hout); +int _private_tls_done_hash(struct TLSContext *context, unsigned char *hout); +int _private_tls_get_hash_idx(struct TLSContext *context); +int _private_tls_build_random(struct TLSPacket *packet); +unsigned int _private_tls_mac_length(struct TLSContext *context); +void _private_dtls_handshake_data(struct TLSContext *context, struct TLSPacket *packet, unsigned int dataframe); +#ifdef TLS_FORWARD_SECRECY +void _private_tls_dhe_free(struct TLSContext *context); +void _private_tls_ecc_dhe_free(struct TLSContext *context); +void _private_tls_dh_clear_key(DHKey *key); +#endif + +#ifdef WITH_TLS_13 +struct TLSPacket *tls_build_encrypted_extensions(struct TLSContext *context); +struct TLSPacket *tls_build_certificate_verify(struct TLSContext *context); +#endif + +// dtls base secret +static unsigned char dtls_secret[32]; + +static unsigned char dependecies_loaded = 0; +// not supported +// static unsigned char TLS_DSA_SIGN_SHA1_OID[] = {0x2A, 0x86, 0x52, 0xCE, 0x38, 0x04, 0x03, 0x00}; + +// base64 stuff +static const char cd64[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq"; + +void _private_b64_decodeblock(unsigned char in[4], unsigned char out[3]) { + out[0] = (unsigned char )(in[0] << 2 | in[1] >> 4); + out[1] = (unsigned char )(in[1] << 4 | in[2] >> 2); + out[2] = (unsigned char )(((in[2] << 6) & 0xc0) | in[3]); +} + +int _private_b64_decode(const char *in_buffer, int in_buffer_size, unsigned char *out_buffer) { + unsigned char in[4], out[3], v; + int i, len; + + const char *ptr = in_buffer; + char *out_ptr = (char *)out_buffer; + + while (ptr <= in_buffer + in_buffer_size) { + for (len = 0, i = 0; i < 4 && (ptr <= in_buffer + in_buffer_size); i++) { + v = 0; + while ((ptr <= in_buffer + in_buffer_size) && v == 0) { + v = (unsigned char)ptr[0]; + ptr++; + v = (unsigned char)((v < 43 || v > 122) ? 0 : cd64[v - 43]); + if (v) + v = (unsigned char)((v == '$') ? 0 : v - 61); + } + if (ptr <= in_buffer + in_buffer_size) { + len++; + if (v) + in[i] = (unsigned char)(v - 1); + } else { + in[i] = 0; + } + } + if (len) { + _private_b64_decodeblock(in, out); + for (i = 0; i < len - 1; i++) { + out_ptr[0] = out[i]; + out_ptr++; + } + } + } + return (int)((intptr_t)out_ptr - (intptr_t)out_buffer); +} + +void dtls_reset_cookie_secret() { + tls_random(dtls_secret, sizeof(dtls_secret)); +} + +void tls_init() { + if (dependecies_loaded) + return; + DEBUG_PRINT("Initializing dependencies\n"); + dependecies_loaded = 1; +#ifdef LTM_DESC + ltc_mp = ltm_desc; +#else +#ifdef TFM_DESC + ltc_mp = tfm_desc; +#else +#ifdef GMP_DESC + ltc_mp = gmp_desc; +#endif +#endif +#endif + register_prng(&sprng_desc); + register_hash(&sha256_desc); + register_hash(&sha1_desc); + register_hash(&sha384_desc); + register_hash(&sha512_desc); + register_hash(&md5_desc); + register_cipher(&aes_desc); +#ifdef TLS_FORWARD_SECRECY + init_curves(); +#endif + dtls_reset_cookie_secret(); +} + +#ifdef TLS_FORWARD_SECRECY +int _private_tls_dh_shared_secret(DHKey *private_key, DHKey *public_key, unsigned char *out, unsigned long *outlen) { + void *tmp; + unsigned long x; + int err; + + if ((!private_key) || (!public_key) || (!out) || (!outlen)) + return TLS_GENERIC_ERROR; + + /* compute y^x mod p */ + if ((err = mp_init(&tmp)) != CRYPT_OK) + return err; + + if ((err = mp_exptmod(public_key->y, private_key->x, private_key->p, tmp)) != CRYPT_OK) { + mp_clear(tmp); + return err; + } + + x = (unsigned long)mp_unsigned_bin_size(tmp); + if (*outlen < x) { + err = CRYPT_BUFFER_OVERFLOW; + mp_clear(tmp); + return err; + } + + if ((err = mp_to_unsigned_bin(tmp, out)) != CRYPT_OK) { + mp_clear(tmp); + return err; + } + *outlen = x; + mp_clear(tmp); + return 0; +} + +unsigned char *_private_tls_decrypt_dhe(struct TLSContext *context, const unsigned char *buffer, unsigned int len, unsigned int *size, int clear_key) { + *size = 0; + if ((!len) || (!context) || (!context->dhe)) { + DEBUG_PRINT("No private DHE key set\n"); + return NULL; + } + + unsigned long out_size = len; + void *Yc = NULL; + + if (mp_init(&Yc)) { + DEBUG_PRINT("ERROR CREATING Yc\n"); + return NULL; + } + if (mp_read_unsigned_bin(Yc, (unsigned char *)buffer, len)) { + DEBUG_PRINT("ERROR LOADING DHE Yc\n"); + mp_clear(Yc); + return NULL; + } + + unsigned char *out = (unsigned char *)TLS_MALLOC(len); + DHKey client_key; + memset(&client_key, 0, sizeof(DHKey)); + + client_key.p = context->dhe->p; + client_key.g = context->dhe->g; + client_key.y = Yc; + int err = _private_tls_dh_shared_secret(context->dhe, &client_key, out, &out_size); + // don't delete p and g + client_key.p = NULL; + client_key.g = NULL; + _private_tls_dh_clear_key(&client_key); + // not needing the dhe key anymore + if (clear_key) + _private_tls_dhe_free(context); + if (err) { + DEBUG_PRINT("DHE DECRYPT ERROR %i\n", err); + TLS_FREE(out); + return NULL; + } + DEBUG_PRINT("OUT_SIZE: %lu\n", out_size); + DEBUG_DUMP_HEX_LABEL("DHE", out, out_size); + *size = (unsigned int)out_size; + return out; +} + +unsigned char *_private_tls_decrypt_ecc_dhe(struct TLSContext *context, const unsigned char *buffer, unsigned int len, unsigned int *size, int clear_key) { + *size = 0; + if ((!len) || (!context) || (!context->ecc_dhe)) { + DEBUG_PRINT("No private ECC DHE key set\n"); + return NULL; + } + + const struct ECCCurveParameters *curve; + if (context->curve) + curve = context->curve; + else + curve = default_curve; + + ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&curve->dp; + + ecc_key client_key; + memset(&client_key, 0, sizeof(client_key)); + if (ecc_ansi_x963_import_ex(buffer, len, &client_key, dp)) { + DEBUG_PRINT("Error importing ECC DHE key\n"); + return NULL; + } + unsigned char *out = (unsigned char *)TLS_MALLOC(len); + unsigned long out_size = len; + + int err = ecc_shared_secret(context->ecc_dhe, &client_key, out, &out_size); + ecc_free(&client_key); + if (clear_key) + _private_tls_ecc_dhe_free(context); + if (err) { + DEBUG_PRINT("ECC DHE DECRYPT ERROR %i\n", err); + TLS_FREE(out); + return NULL; + } + DEBUG_PRINT("OUT_SIZE: %lu\n", out_size); + DEBUG_DUMP_HEX_LABEL("ECC DHE", out, out_size); + *size = (unsigned int)out_size; + return out; +} +#endif + +unsigned char *_private_tls_decrypt_rsa(struct TLSContext *context, const unsigned char *buffer, unsigned int len, unsigned int *size) { + *size = 0; + if ((!len) || (!context) || (!context->private_key) || (!context->private_key->der_bytes) || (!context->private_key->der_len)) { + DEBUG_PRINT("No private key set\n"); + return NULL; + } + tls_init(); + rsa_key key; + int err; + err = rsa_import(context->private_key->der_bytes, context->private_key->der_len, &key); + + if (err) { + DEBUG_PRINT("Error importing RSA key (code: %i)\n", err); + return NULL; + } + unsigned char *out = (unsigned char *)TLS_MALLOC(len); + unsigned long out_size = len; + int res = 0; + + err = rsa_decrypt_key_ex(buffer, len, out, &out_size, NULL, 0, -1, LTC_PKCS_1_V1_5, &res, &key); + rsa_free(&key); + + if ((err) || (out_size != 48) || (ntohs(*(unsigned short *)out) != context->version)) { + // generate a random secret and continue (ROBOT fix) + // silently ignore and generate a random secret + out_size = 48; + tls_random(out, out_size); + *(unsigned short *)out = htons(context->version); + } + *size = (unsigned int)out_size; + return out; +} + +unsigned char *_private_tls_encrypt_rsa(struct TLSContext *context, const unsigned char *buffer, unsigned int len, unsigned int *size) { + *size = 0; + if ((!len) || (!context) || (!context->certificates) || (!context->certificates_count) || (!context->certificates[0]) || + (!context->certificates[0]->der_bytes) || (!context->certificates[0]->der_len)) { + DEBUG_PRINT("No certificate set\n"); + return NULL; + } + tls_init(); + rsa_key key; + int err; + err = rsa_import(context->certificates[0]->der_bytes, context->certificates[0]->der_len, &key); + + if (err) { + DEBUG_PRINT("Error importing RSA certificate (code: %i)\n", err); + return NULL; + } + unsigned long out_size = TLS_MAX_RSA_KEY; + unsigned char *out = (unsigned char *)TLS_MALLOC(out_size); + int hash_idx = find_hash("sha256"); + int prng_idx = find_prng("sprng"); + err = rsa_encrypt_key_ex(buffer, len, out, &out_size, (unsigned char *)"Concept", 7, NULL, prng_idx, hash_idx, LTC_PKCS_1_V1_5, &key); + rsa_free(&key); + if ((err) || (!out_size)) { + TLS_FREE(out); + return NULL; + } + *size = (unsigned int)out_size; + return out; +} + +#ifdef TLS_LEGACY_SUPPORT +int _private_rsa_verify_hash_md5sha1(const unsigned char *sig, unsigned long siglen, unsigned char *hash, unsigned long hashlen, int *stat, rsa_key *key) { + unsigned long modulus_bitlen, modulus_bytelen, x; + int err; + unsigned char *tmpbuf = NULL; + + if ((hash == NULL) || (sig == NULL) || (stat == NULL) || (key == NULL) || (!siglen) || (!hashlen)) + return TLS_GENERIC_ERROR; + + *stat = 0; + + modulus_bitlen = mp_count_bits((key->N)); + + modulus_bytelen = mp_unsigned_bin_size((key->N)); + if (modulus_bytelen != siglen) + return TLS_GENERIC_ERROR; + + tmpbuf = (unsigned char *)TLS_MALLOC(siglen); + if (!tmpbuf) + return TLS_GENERIC_ERROR; + + x = siglen; + if ((err = ltc_mp.rsa_me(sig, siglen, tmpbuf, &x, PK_PUBLIC, key)) != CRYPT_OK) { + TLS_FREE(tmpbuf); + return err; + } + + if (x != siglen) { + TLS_FREE(tmpbuf); + return CRYPT_INVALID_PACKET; + } + unsigned long out_len = siglen; + unsigned char *out = (unsigned char *)TLS_MALLOC(siglen); + if (!out) { + TLS_FREE(tmpbuf); + return TLS_GENERIC_ERROR; + } + + int decoded = 0; + err = pkcs_1_v1_5_decode(tmpbuf, x, LTC_PKCS_1_EMSA, modulus_bitlen, out, &out_len, &decoded); + if (decoded) { + if (out_len == hashlen) { + if (!memcmp(out, hash, hashlen)) + *stat = 1; + } + } + + TLS_FREE(tmpbuf); + TLS_FREE(out); + return err; +} +#endif + +int _private_tls_verify_rsa(struct TLSContext *context, unsigned int hash_type, const unsigned char *buffer, unsigned int len, const unsigned char *message, unsigned int message_len) { + tls_init(); + rsa_key key; + int err; + + if (context->is_server) { + if ((!len) || (!context->client_certificates) || (!context->client_certificates_count) || (!context->client_certificates[0]) || + (!context->client_certificates[0]->der_bytes) || (!context->client_certificates[0]->der_len)) { + DEBUG_PRINT("No client certificate set\n"); + return TLS_GENERIC_ERROR; + } + err = rsa_import(context->client_certificates[0]->der_bytes, context->client_certificates[0]->der_len, &key); + } else { + if ((!len) || (!context->certificates) || (!context->certificates_count) || (!context->certificates[0]) || + (!context->certificates[0]->der_bytes) || (!context->certificates[0]->der_len)) { + DEBUG_PRINT("No server certificate set\n"); + return TLS_GENERIC_ERROR; + } + err = rsa_import(context->certificates[0]->der_bytes, context->certificates[0]->der_len, &key); + } + if (err) { + DEBUG_PRINT("Error importing RSA certificate (code: %i)\n", err); + return TLS_GENERIC_ERROR; + } + int hash_idx = -1; + unsigned char hash[TLS_MAX_HASH_LEN]; + unsigned int hash_len = 0; + hash_state state; + switch (hash_type) { + case md5: + hash_idx = find_hash("md5"); + err = md5_init(&state); + TLS_ERROR(err, break); + err = md5_process(&state, message, message_len); + TLS_ERROR(err, break); + err = md5_done(&state, hash); + TLS_ERROR(err, break); + hash_len = 16; + break; + case sha1: + hash_idx = find_hash("sha1"); + err = sha1_init(&state); + TLS_ERROR(err, break) + err = sha1_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha1_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 20; + break; + case sha256: + hash_idx = find_hash("sha256"); + err = sha256_init(&state); + TLS_ERROR(err, break) + err = sha256_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha256_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 32; + break; + case sha384: + hash_idx = find_hash("sha384"); + err = sha384_init(&state); + TLS_ERROR(err, break) + err = sha384_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha384_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 48; + break; + case sha512: + hash_idx = find_hash("sha512"); + err = sha512_init(&state); + TLS_ERROR(err, break) + err = sha512_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha512_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 64; + break; +#ifdef TLS_LEGACY_SUPPORT + case _md5_sha1: + hash_idx = find_hash("md5"); + err = md5_init(&state); + TLS_ERROR(err, break) + err = md5_process(&state, message, message_len); + TLS_ERROR(err, break) + err = md5_done(&state, hash); + TLS_ERROR(err, break) + hash_idx = find_hash("sha1"); + err = sha1_init(&state); + TLS_ERROR(err, break) + err = sha1_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha1_done(&state, hash + 16); + TLS_ERROR(err, break) + hash_len = 36; + err = sha1_init(&state); + TLS_ERROR(err, break) + err = sha1_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha1_done(&state, hash + 16); + TLS_ERROR(err, break) + hash_len = 36; + break; +#endif + } + if ((hash_idx < 0) || (err)) { + DEBUG_PRINT("Unsupported hash type: %i\n", hash_type); + return TLS_GENERIC_ERROR; + } + int rsa_stat = 0; +#ifdef TLS_LEGACY_SUPPORT + if (hash_type == _md5_sha1) + err = _private_rsa_verify_hash_md5sha1(buffer, len, hash, hash_len, &rsa_stat, &key); + else +#endif +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + err = rsa_verify_hash_ex(buffer, len, hash, hash_len, LTC_PKCS_1_PSS, hash_idx, 0, &rsa_stat, &key); + else +#endif + err = rsa_verify_hash_ex(buffer, len, hash, hash_len, LTC_PKCS_1_V1_5, hash_idx, 0, &rsa_stat, &key); + rsa_free(&key); + if (err) + return 0; + return rsa_stat; +} + +#ifdef TLS_LEGACY_SUPPORT +int _private_rsa_sign_hash_md5sha1(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, rsa_key *key) { + unsigned long modulus_bitlen, modulus_bytelen, x; + int err; + + if ((in == NULL) || (out == NULL) || (outlen == NULL) || (key == NULL)) + return TLS_GENERIC_ERROR; + + modulus_bitlen = mp_count_bits((key->N)); + + modulus_bytelen = mp_unsigned_bin_size((key->N)); + if (modulus_bytelen > *outlen) { + *outlen = modulus_bytelen; + return CRYPT_BUFFER_OVERFLOW; + } + x = modulus_bytelen; + err = pkcs_1_v1_5_encode(in, inlen, LTC_PKCS_1_EMSA, modulus_bitlen, NULL, 0, out, &x); + if (err != CRYPT_OK) + return err; + + return ltc_mp.rsa_me(out, x, out, outlen, PK_PRIVATE, key); +} +#endif + +int _private_tls_sign_rsa(struct TLSContext *context, unsigned int hash_type, const unsigned char *message, unsigned int message_len, unsigned char *out, unsigned long *outlen) { + if ((!outlen) || (!context) || (!out) || (!outlen) || (!context->private_key) || (!context->private_key->der_bytes) || (!context->private_key->der_len)) { + DEBUG_PRINT("No private key set\n"); + return TLS_GENERIC_ERROR; + } + tls_init(); + rsa_key key; + int err; + err = rsa_import(context->private_key->der_bytes, context->private_key->der_len, &key); + + if (err) { + DEBUG_PRINT("Error importing RSA certificate (code: %i)\n", err); + return TLS_GENERIC_ERROR; + } + int hash_idx = -1; + unsigned char hash[TLS_MAX_HASH_LEN]; + unsigned int hash_len = 0; + hash_state state; + switch (hash_type) { + case md5: + hash_idx = find_hash("md5"); + err = md5_init(&state); + TLS_ERROR(err, break) + err = md5_process(&state, message, message_len); + TLS_ERROR(err, break) + err = md5_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 16; + break; + case sha1: + hash_idx = find_hash("sha1"); + err = sha1_init(&state); + TLS_ERROR(err, break) + err = sha1_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha1_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 20; + break; + case sha256: + hash_idx = find_hash("sha256"); + err = sha256_init(&state); + TLS_ERROR(err, break) + err = sha256_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha256_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 32; + break; + case sha384: + hash_idx = find_hash("sha384"); + err = sha384_init(&state); + TLS_ERROR(err, break) + err = sha384_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha384_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 48; + break; + case sha512: + hash_idx = find_hash("sha512"); + err = sha512_init(&state); + TLS_ERROR(err, break) + err = sha512_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha512_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 64; + break; + case _md5_sha1: + hash_idx = find_hash("md5"); + err = md5_init(&state); + TLS_ERROR(err, break) + err = md5_process(&state, message, message_len); + TLS_ERROR(err, break) + err = md5_done(&state, hash); + TLS_ERROR(err, break) + hash_idx = find_hash("sha1"); + err = sha1_init(&state); + TLS_ERROR(err, break) + err = sha1_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha1_done(&state, hash + 16); + TLS_ERROR(err, break) + hash_len = 36; + err = sha1_init(&state); + TLS_ERROR(err, break) + err = sha1_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha1_done(&state, hash + 16); + TLS_ERROR(err, break) + hash_len = 36; + break; + } +#ifdef TLS_LEGACY_SUPPORT + if (hash_type == _md5_sha1) { + if (err) { + DEBUG_PRINT("Unsupported hash type: %i\n", hash_type); + return TLS_GENERIC_ERROR; + } + err = _private_rsa_sign_hash_md5sha1(hash, hash_len, out, outlen, &key); + } else +#endif + { + if ((hash_idx < 0) || (err)) { + DEBUG_PRINT("Unsupported hash type: %i\n", hash_type); + return TLS_GENERIC_ERROR; + } +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + err = rsa_sign_hash_ex(hash, hash_len, out, outlen, LTC_PKCS_1_PSS, NULL, find_prng("sprng"), hash_idx, hash_type == sha256 ? 32 : 48, &key); + else +#endif + err = rsa_sign_hash_ex(hash, hash_len, out, outlen, LTC_PKCS_1_V1_5, NULL, find_prng("sprng"), hash_idx, 0, &key); + } + rsa_free(&key); + if (err) + return 0; + + return 1; +} + +#ifdef TLS_ECDSA_SUPPORTED +static int _private_tls_is_point(ecc_key *key) { + void *prime, *b, *t1, *t2; + int err; + + if ((err = mp_init_multi(&prime, &b, &t1, &t2, NULL)) != CRYPT_OK) { + return err; + } + + /* load prime and b */ + if ((err = mp_read_radix(prime, TLS_TOMCRYPT_PRIVATE_DP(key)->prime, 16)) != CRYPT_OK) { + goto error; + } + if ((err = mp_read_radix(b, TLS_TOMCRYPT_PRIVATE_DP(key)->B, 16)) != CRYPT_OK) { + goto error; + } + + /* compute y^2 */ + if ((err = mp_sqr(key->pubkey.y, t1)) != CRYPT_OK) { + goto error; + } + + /* compute x^3 */ + if ((err = mp_sqr(key->pubkey.x, t2)) != CRYPT_OK) { + goto error; + } + if ((err = mp_mod(t2, prime, t2)) != CRYPT_OK) { + goto error; + } + if ((err = mp_mul(key->pubkey.x, t2, t2)) != CRYPT_OK) { + goto error; + } + + /* compute y^2 - x^3 */ + if ((err = mp_sub(t1, t2, t1)) != CRYPT_OK) { + goto error; + } + + /* compute y^2 - x^3 + 3x */ + if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { + goto error; + } + if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { + goto error; + } + if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { + goto error; + } + if ((err = mp_mod(t1, prime, t1)) != CRYPT_OK) { + goto error; + } + while (mp_cmp_d(t1, 0) == LTC_MP_LT) { + if ((err = mp_add(t1, prime, t1)) != CRYPT_OK) { + goto error; + } + } + while (mp_cmp(t1, prime) != LTC_MP_LT) { + if ((err = mp_sub(t1, prime, t1)) != CRYPT_OK) { + goto error; + } + } + + /* compare to b */ + if (mp_cmp(t1, b) != LTC_MP_EQ) { + err = CRYPT_INVALID_PACKET; + } else { + err = CRYPT_OK; + } + +error: + mp_clear_multi(prime, b, t1, t2, NULL); + return err; +} + +int _private_tls_ecc_import_key(const unsigned char *private_key, int private_len, const unsigned char *public_key, int public_len, ecc_key *key, const ltc_ecc_set_type *dp) { + int err; + + if ((!key) || (!ltc_mp.name)) + return CRYPT_MEM; + + key->type = PK_PRIVATE; + + if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, NULL) != CRYPT_OK) + return CRYPT_MEM; + + if ((public_len) && (!public_key[0])) { + public_key++; + public_len--; + } + if ((err = mp_read_unsigned_bin(key->pubkey.x, (unsigned char *)public_key + 1, (public_len - 1) >> 1)) != CRYPT_OK) { + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; + } + + if ((err = mp_read_unsigned_bin(key->pubkey.y, (unsigned char *)public_key + 1 + ((public_len - 1) >> 1), (public_len - 1) >> 1)) != CRYPT_OK) { + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; + } + + if ((err = mp_read_unsigned_bin(key->k, (unsigned char *)private_key, private_len)) != CRYPT_OK) { + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; + } + + TLS_TOMCRYPT_PRIVATE_SET_INDEX(key, -1); + TLS_TOMCRYPT_PRIVATE_DP(key) = dp; + + /* set z */ + if ((err = mp_set(key->pubkey.z, 1)) != CRYPT_OK) { + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; + } + + /* is it a point on the curve? */ + if ((err = _private_tls_is_point(key)) != CRYPT_OK) { + DEBUG_PRINT("KEY IS NOT ON CURVE\n"); + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; + } + + /* we're good */ + return CRYPT_OK; +} + +int _private_tls_sign_ecdsa(struct TLSContext *context, unsigned int hash_type, const unsigned char *message, unsigned int message_len, unsigned char *out, unsigned long *outlen) { + if ((!outlen) || (!context) || (!out) || (!outlen) || (!context->ec_private_key) || + (!context->ec_private_key->priv) || (!context->ec_private_key->priv_len) || (!context->ec_private_key->pk) || (!context->ec_private_key->pk_len)) { + DEBUG_PRINT("No private ECDSA key set\n"); + return TLS_GENERIC_ERROR; + } + + const struct ECCCurveParameters *curve = NULL; + + switch (context->ec_private_key->ec_algorithm) { + case 19: + curve = &secp192r1; + break; + case 20: + curve = &secp224k1; + break; + case 21: + curve = &secp224r1; + break; + case 22: + curve = &secp256k1; + break; + case 23: + curve = &secp256r1; + break; + case 24: + curve = &secp384r1; + break; + case 25: + curve = &secp521r1; + break; + default: + DEBUG_PRINT("UNSUPPORTED CURVE\n"); + } + + if (!curve) + return TLS_GENERIC_ERROR; + + tls_init(); + ecc_key key; + int err; + + ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&curve->dp; + + // broken ... fix this + err = _private_tls_ecc_import_key(context->ec_private_key->priv, context->ec_private_key->priv_len, context->ec_private_key->pk, context->ec_private_key->pk_len, &key, dp); + if (err) { + DEBUG_PRINT("Error importing ECC certificate (code: %i)\n", (int)err); + return TLS_GENERIC_ERROR; + } + unsigned char hash[TLS_MAX_HASH_LEN]; + unsigned int hash_len = 0; + hash_state state; + switch (hash_type) { + case md5: + err = md5_init(&state); + TLS_ERROR(err, break) + err = md5_process(&state, message, message_len); + TLS_ERROR(err, break) + err = md5_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 16; + break; + case sha1: + err = sha1_init(&state); + TLS_ERROR(err, break) + err = sha1_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha1_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 20; + break; + case sha256: + err = sha256_init(&state); + TLS_ERROR(err, break) + err = sha256_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha256_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 32; + break; + case sha384: + err = sha384_init(&state); + TLS_ERROR(err, break) + err = sha384_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha384_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 48; + break; + case sha512: + err = sha512_init(&state); + TLS_ERROR(err, break) + err = sha512_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha512_done(&state, hash); + TLS_ERROR(err, break) + hash_len = 64; + break; + case _md5_sha1: + err = md5_init(&state); + TLS_ERROR(err, break) + err = md5_process(&state, message, message_len); + TLS_ERROR(err, break) + err = md5_done(&state, hash); + TLS_ERROR(err, break) + err = sha1_init(&state); + TLS_ERROR(err, break) + err = sha1_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha1_done(&state, hash + 16); + TLS_ERROR(err, break) + hash_len = 36; + err = sha1_init(&state); + TLS_ERROR(err, break) + err = sha1_process(&state, message, message_len); + TLS_ERROR(err, break) + err = sha1_done(&state, hash + 16); + TLS_ERROR(err, break) + hash_len = 36; + break; + } + + if (err) { + DEBUG_PRINT("Unsupported hash type: %i\n", hash_type); + return TLS_GENERIC_ERROR; + } + // "Let z be the Ln leftmost bits of e, where Ln is the bit length of the group order n." + if (hash_len > (unsigned int)curve->size) + hash_len = (unsigned int)curve->size; + err = ecc_sign_hash(hash, hash_len, out, outlen, NULL, find_prng("sprng"), &key); + DEBUG_DUMP_HEX_LABEL("ECC SIGNATURE", out, *outlen); + ecc_free(&key); + if (err) + return 0; + + return 1; +} + +#if defined(TLS_CLIENT_ECDSA) || defined(WITH_TLS_13) +int _private_tls_ecc_import_pk(const unsigned char *public_key, int public_len, ecc_key *key, const ltc_ecc_set_type *dp) { + int err; + + if ((!key) || (!ltc_mp.name)) + return CRYPT_MEM; + + key->type = PK_PUBLIC; + + if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, NULL) != CRYPT_OK) + return CRYPT_MEM; + + if ((public_len) && (!public_key[0])) { + public_key++; + public_len--; + } + if ((err = mp_read_unsigned_bin(key->pubkey.x, (unsigned char *)public_key + 1, (public_len - 1) >> 1)) != CRYPT_OK) { + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; + } + + if ((err = mp_read_unsigned_bin(key->pubkey.y, (unsigned char *)public_key + 1 + ((public_len - 1) >> 1), (public_len - 1) >> 1)) != CRYPT_OK) { + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; + } + + + TLS_TOMCRYPT_PRIVATE_SET_INDEX(key, -1); + TLS_TOMCRYPT_PRIVATE_DP(key) = dp; + + /* set z */ + if ((err = mp_set(key->pubkey.z, 1)) != CRYPT_OK) { + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; + } + + /* is it a point on the curve? */ + if ((err = _private_tls_is_point(key)) != CRYPT_OK) { + DEBUG_PRINT("KEY IS NOT ON CURVE\n"); + mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); + return err; + } + + /* we're good */ + return CRYPT_OK; +} + +int _private_tls_verify_ecdsa(struct TLSContext *context, unsigned int hash_type, const unsigned char *buffer, unsigned int len, const unsigned char *message, unsigned int message_len, const struct ECCCurveParameters *curve_hint) { + tls_init(); + ecc_key key; + int err; + + if (!curve_hint) + curve_hint = context->curve; + + if (context->is_server) { + if ((!len) || (!context->client_certificates) || (!context->client_certificates_count) || (!context->client_certificates[0]) || + (!context->client_certificates[0]->pk) || (!context->client_certificates[0]->pk_len) || (!curve_hint)) { + DEBUG_PRINT("No client certificate set\n"); + return TLS_GENERIC_ERROR; + } + err = _private_tls_ecc_import_pk(context->client_certificates[0]->pk, context->client_certificates[0]->pk_len, &key, (ltc_ecc_set_type *)&curve_hint->dp); + } else { + if ((!len) || (!context->certificates) || (!context->certificates_count) || (!context->certificates[0]) || + (!context->certificates[0]->pk) || (!context->certificates[0]->pk_len) || (!curve_hint)) { + DEBUG_PRINT("No server certificate set\n"); + return TLS_GENERIC_ERROR; + } + err = _private_tls_ecc_import_pk(context->certificates[0]->pk, context->certificates[0]->pk_len, &key, (ltc_ecc_set_type *)&curve_hint->dp); + } + if (err) { + DEBUG_PRINT("Error importing ECC certificate (code: %i)", err); + return TLS_GENERIC_ERROR; + } + int hash_idx = -1; + unsigned char hash[TLS_MAX_HASH_LEN]; + unsigned int hash_len = 0; + hash_state state; + switch (hash_type) { + case md5: + hash_idx = find_hash("md5"); + err = md5_init(&state); + if (!err) { + err = md5_process(&state, message, message_len); + if (!err) + err = md5_done(&state, hash); + } + hash_len = 16; + break; + case sha1: + hash_idx = find_hash("sha1"); + err = sha1_init(&state); + if (!err) { + err = sha1_process(&state, message, message_len); + if (!err) + err = sha1_done(&state, hash); + } + hash_len = 20; + break; + case sha256: + hash_idx = find_hash("sha256"); + err = sha256_init(&state); + if (!err) { + err = sha256_process(&state, message, message_len); + if (!err) + err = sha256_done(&state, hash); + } + hash_len = 32; + break; + case sha384: + hash_idx = find_hash("sha384"); + err = sha384_init(&state); + if (!err) { + err = sha384_process(&state, message, message_len); + if (!err) + err = sha384_done(&state, hash); + } + hash_len = 48; + break; + case sha512: + hash_idx = find_hash("sha512"); + err = sha512_init(&state); + if (!err) { + err = sha512_process(&state, message, message_len); + if (!err) + err = sha512_done(&state, hash); + } + hash_len = 64; + break; +#ifdef TLS_LEGACY_SUPPORT + case _md5_sha1: + hash_idx = find_hash("md5"); + err = md5_init(&state); + if (!err) { + err = md5_process(&state, message, message_len); + if (!err) + err = md5_done(&state, hash); + } + hash_idx = find_hash("sha1"); + err = sha1_init(&state); + if (!err) { + err = sha1_process(&state, message, message_len); + if (!err) + err = sha1_done(&state, hash + 16); + } + hash_len = 36; + err = sha1_init(&state); + if (!err) { + err = sha1_process(&state, message, message_len); + if (!err) + err = sha1_done(&state, hash + 16); + } + hash_len = 36; + break; +#endif + } + if ((hash_idx < 0) || (err)) { + DEBUG_PRINT("Unsupported hash type: %i\n", hash_type); + return TLS_GENERIC_ERROR; + } + int ecc_stat = 0; + err = ecc_verify_hash(buffer, len, hash, hash_len, &ecc_stat, &key); + ecc_free(&key); + if (err) + return 0; + return ecc_stat; +} +#endif + +#endif + +unsigned int _private_tls_random_int(int limit) { + unsigned int res = 0; + tls_random((unsigned char *)&res, sizeof(int)); + if (limit) + res %= limit; + return res; +} + +void _private_tls_sleep(unsigned int microseconds) { +#ifdef _WIN32 + Sleep(microseconds/1000); +#else + struct timespec ts; + + ts.tv_sec = (unsigned int) (microseconds / 1000000); + ts.tv_nsec = (unsigned int) (microseconds % 1000000) * 1000ul; + + nanosleep(&ts, NULL); +#endif +} + +void _private_random_sleep(struct TLSContext *context, int max_microseconds) { + if (context) + context->sleep_until = (unsigned int)time(NULL) + _private_tls_random_int(max_microseconds/1000000 * TLS_MAX_ERROR_IDLE_S); + else + _private_tls_sleep(_private_tls_random_int(max_microseconds)); +} + +void _private_tls_prf_helper(int hash_idx, unsigned long dlen, unsigned char *output, unsigned int outlen, const unsigned char *secret, const unsigned int secret_len, + const unsigned char *label, unsigned int label_len, unsigned char *seed, unsigned int seed_len, + unsigned char *seed_b, unsigned int seed_b_len) { + unsigned char digest_out0[TLS_MAX_HASH_LEN]; + unsigned char digest_out1[TLS_MAX_HASH_LEN]; + unsigned int i; + hmac_state hmac; + + hmac_init(&hmac, hash_idx, secret, secret_len); + hmac_process(&hmac, label, label_len); + + hmac_process(&hmac, seed, seed_len); + if ((seed_b) && (seed_b_len)) + hmac_process(&hmac, seed_b, seed_b_len); + hmac_done(&hmac, digest_out0, &dlen); + int idx = 0; + while (outlen) { + hmac_init(&hmac, hash_idx, secret, secret_len); + hmac_process(&hmac, digest_out0, dlen); + hmac_process(&hmac, label, label_len); + hmac_process(&hmac, seed, seed_len); + if ((seed_b) && (seed_b_len)) + hmac_process(&hmac, seed_b, seed_b_len); + hmac_done(&hmac, digest_out1, &dlen); + + unsigned int copylen = outlen; + if (copylen > dlen) + copylen = dlen; + + for (i = 0; i < copylen; i++) { + output[idx++] ^= digest_out1[i]; + outlen--; + } + + if (!outlen) + break; + + hmac_init(&hmac, hash_idx, secret, secret_len); + hmac_process(&hmac, digest_out0, dlen); + hmac_done(&hmac, digest_out0, &dlen); + } +} + +#ifdef WITH_TLS_13 +int _private_tls_hkdf_label(const char *label, unsigned char label_len, const unsigned char *data, unsigned char data_len, unsigned char *hkdflabel, unsigned short length, const char *prefix) { + *(unsigned short *)hkdflabel = htons(length); + int prefix_len; + if (prefix) { + prefix_len = (int)strlen(prefix); + memcpy(&hkdflabel[3], prefix, prefix_len); + } else { + memcpy(&hkdflabel[3], "tls13 ", 6); + prefix_len = 6; + } + hkdflabel[2] = (unsigned char)prefix_len + label_len; + memcpy(&hkdflabel[3 + prefix_len], label, label_len); + hkdflabel[3 + prefix_len + label_len] = (unsigned char)data_len; + if (data_len) + memcpy(&hkdflabel[4 + prefix_len + label_len], data, data_len); + return 4 + prefix_len + label_len + data_len; +} + +int _private_tls_hkdf_extract(unsigned int mac_length, unsigned char *output, unsigned int outlen, const unsigned char *salt, unsigned int salt_len, const unsigned char *ikm, unsigned char ikm_len) { + unsigned long dlen = outlen; + static unsigned char dummy_label[1] = { 0 }; + if ((!salt) || (salt_len == 0)) { + salt_len = 1; + salt = dummy_label; + } + int hash_idx; + if (mac_length == TLS_SHA384_MAC_SIZE) { + hash_idx = find_hash("sha384"); + dlen = mac_length; + } else + hash_idx = find_hash("sha256"); + + hmac_state hmac; + hmac_init(&hmac, hash_idx, salt, salt_len); + hmac_process(&hmac, ikm, ikm_len); + hmac_done(&hmac, output, &dlen); + DEBUG_DUMP_HEX_LABEL("EXTRACT", output, dlen); + return dlen; +} + +void _private_tls_hkdf_expand(unsigned int mac_length, unsigned char *output, unsigned int outlen, const unsigned char *secret, unsigned int secret_len, const unsigned char *info, unsigned char info_len) { + unsigned char digest_out[TLS_MAX_HASH_LEN]; + unsigned long dlen = 32; + int hash_idx; + if (mac_length == TLS_SHA384_MAC_SIZE) { + hash_idx = find_hash("sha384"); + dlen = mac_length; + } else + hash_idx = find_hash("sha256"); + unsigned int i; + unsigned int idx = 0; + hmac_state hmac; + unsigned char i2 = 0; + while (outlen) { + hmac_init(&hmac, hash_idx, secret, secret_len); + if (i2) + hmac_process(&hmac, digest_out, dlen); + if ((info) && (info_len)) + hmac_process(&hmac, info, info_len); + i2++; + hmac_process(&hmac, &i2, 1); + hmac_done(&hmac, digest_out, &dlen); + + unsigned int copylen = outlen; + if (copylen > dlen) + copylen = (unsigned int)dlen; + + for (i = 0; i < copylen; i++) { + output[idx++] = digest_out[i]; + outlen--; + } + + if (!outlen) + break; + } +} + +void _private_tls_hkdf_expand_label(unsigned int mac_length, unsigned char *output, unsigned int outlen, const unsigned char *secret, unsigned int secret_len, const char *label, unsigned char label_len, const unsigned char *data, unsigned char data_len) { + unsigned char hkdf_label[512]; + int len = _private_tls_hkdf_label(label, label_len, data, data_len, hkdf_label, outlen, NULL); + DEBUG_DUMP_HEX_LABEL("INFO", hkdf_label, len); + _private_tls_hkdf_expand(mac_length, output, outlen, secret, secret_len, hkdf_label, len); +} +#endif + +void _private_tls_prf(struct TLSContext *context, + unsigned char *output, unsigned int outlen, const unsigned char *secret, const unsigned int secret_len, + const unsigned char *label, unsigned int label_len, unsigned char *seed, unsigned int seed_len, + unsigned char *seed_b, unsigned int seed_b_len) { + if ((!secret) || (!secret_len)) { + DEBUG_PRINT("NULL SECRET\n"); + return; + } + if ((context->version != TLS_V12) && (context->version != DTLS_V12)) { + int md5_hash_idx = find_hash("md5"); + int sha1_hash_idx = find_hash("sha1"); + int half_secret = (secret_len + 1) / 2; + + memset(output, 0, outlen); + _private_tls_prf_helper(md5_hash_idx, 16, output, outlen, secret, half_secret, label, label_len, seed, seed_len, seed_b, seed_b_len); + _private_tls_prf_helper(sha1_hash_idx, 20, output, outlen, secret + (secret_len - half_secret), secret_len - half_secret, label, label_len, seed, seed_len, seed_b, seed_b_len); + } else { + // sha256_hmac + unsigned char digest_out0[TLS_MAX_HASH_LEN]; + unsigned char digest_out1[TLS_MAX_HASH_LEN]; + unsigned long dlen = 32; + int hash_idx; + unsigned int mac_length = _private_tls_mac_length(context); + if (mac_length == TLS_SHA384_MAC_SIZE) { + hash_idx = find_hash("sha384"); + dlen = mac_length; + } else + hash_idx = find_hash("sha256"); + unsigned int i; + hmac_state hmac; + + hmac_init(&hmac, hash_idx, secret, secret_len); + hmac_process(&hmac, label, label_len); + + hmac_process(&hmac, seed, seed_len); + if ((seed_b) && (seed_b_len)) + hmac_process(&hmac, seed_b, seed_b_len); + hmac_done(&hmac, digest_out0, &dlen); + int idx = 0; + while (outlen) { + hmac_init(&hmac, hash_idx, secret, secret_len); + hmac_process(&hmac, digest_out0, dlen); + hmac_process(&hmac, label, label_len); + hmac_process(&hmac, seed, seed_len); + if ((seed_b) && (seed_b_len)) + hmac_process(&hmac, seed_b, seed_b_len); + hmac_done(&hmac, digest_out1, &dlen); + + unsigned int copylen = outlen; + if (copylen > dlen) + copylen = (unsigned int)dlen; + + for (i = 0; i < copylen; i++) { + output[idx++] = digest_out1[i]; + outlen--; + } + + if (!outlen) + break; + + hmac_init(&hmac, hash_idx, secret, secret_len); + hmac_process(&hmac, digest_out0, dlen); + hmac_done(&hmac, digest_out0, &dlen); + } + } +} + +int _private_tls_key_length(struct TLSContext *context) { + switch (context->cipher) { + case TLS_RSA_WITH_AES_128_CBC_SHA: + case TLS_RSA_WITH_AES_128_CBC_SHA256: + case TLS_RSA_WITH_AES_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case TLS_AES_128_GCM_SHA256: + return 16; + case TLS_RSA_WITH_AES_256_CBC_SHA: + case TLS_RSA_WITH_AES_256_CBC_SHA256: + case TLS_RSA_WITH_AES_256_GCM_SHA384: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_AES_256_GCM_SHA384: + case TLS_CHACHA20_POLY1305_SHA256: + return 32; + } + return 0; +} + +int _private_tls_is_aead(struct TLSContext *context) { + switch (context->cipher) { + case TLS_RSA_WITH_AES_128_GCM_SHA256: + case TLS_RSA_WITH_AES_256_GCM_SHA384: + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case TLS_AES_128_GCM_SHA256: + case TLS_AES_256_GCM_SHA384: + return 1; + case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_CHACHA20_POLY1305_SHA256: + return 2; + } + return 0; +} + + + +unsigned int _private_tls_mac_length(struct TLSContext *context) { + switch (context->cipher) { + case TLS_RSA_WITH_AES_128_CBC_SHA: + case TLS_RSA_WITH_AES_256_CBC_SHA: + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + return TLS_SHA1_MAC_SIZE; + case TLS_RSA_WITH_AES_128_CBC_SHA256: + case TLS_RSA_WITH_AES_256_CBC_SHA256: + case TLS_RSA_WITH_AES_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: +#ifdef WITH_TLS_13 + case TLS_AES_128_GCM_SHA256: + case TLS_CHACHA20_POLY1305_SHA256: + case TLS_AES_128_CCM_SHA256: + case TLS_AES_128_CCM_8_SHA256: +#endif + return TLS_SHA256_MAC_SIZE; + case TLS_RSA_WITH_AES_256_GCM_SHA384: + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: +#ifdef WITH_TLS_13 + case TLS_AES_256_GCM_SHA384: +#endif + return TLS_SHA384_MAC_SIZE; + } + return 0; +} + +#ifdef WITH_TLS_13 +int _private_tls13_key(struct TLSContext *context, int handshake) { + tls_init(); + + int key_length = _private_tls_key_length(context); + unsigned int mac_length = _private_tls_mac_length(context); + + if ((!context->premaster_key) || (!context->premaster_key_len)) + return 0; + + if ((!key_length) || (!mac_length)) { + DEBUG_PRINT("KEY EXPANSION FAILED, KEY LENGTH: %i, MAC LENGTH: %i\n", key_length, mac_length); + return 0; + } + + unsigned char *clientkey = NULL; + unsigned char *serverkey = NULL; + unsigned char *clientiv = NULL; + unsigned char *serveriv = NULL; + int is_aead = _private_tls_is_aead(context); + + unsigned char local_keybuffer[TLS_V13_MAX_KEY_SIZE]; + unsigned char local_ivbuffer[TLS_V13_MAX_IV_SIZE]; + unsigned char remote_keybuffer[TLS_V13_MAX_KEY_SIZE]; + unsigned char remote_ivbuffer[TLS_V13_MAX_IV_SIZE]; + + unsigned char prk[TLS_MAX_HASH_SIZE]; + unsigned char hash[TLS_MAX_HASH_SIZE]; + static unsigned char earlysecret[TLS_MAX_HASH_SIZE]; + + const char *server_key = "s ap traffic"; + const char *client_key = "c ap traffic"; + if (handshake) { + server_key = "s hs traffic"; + client_key = "c hs traffic"; + } + + unsigned char salt[TLS_MAX_HASH_SIZE]; + + hash_state md; + if (mac_length == TLS_SHA384_MAC_SIZE) { + sha384_init(&md); + sha384_done(&md, hash); + } else { + sha256_init(&md); + sha256_done(&md, hash); + } + // extract secret "early" + if ((context->master_key) && (context->master_key_len) && (!handshake)) { + DEBUG_DUMP_HEX_LABEL("USING PREVIOUS SECRET", context->master_key, context->master_key_len); + _private_tls_hkdf_expand_label(mac_length, salt, mac_length, context->master_key, context->master_key_len, "derived", 7, hash, mac_length); + DEBUG_DUMP_HEX_LABEL("salt", salt, mac_length); + _private_tls_hkdf_extract(mac_length, prk, mac_length, salt, mac_length, earlysecret, mac_length); + } else { + _private_tls_hkdf_extract(mac_length, prk, mac_length, NULL, 0, earlysecret, mac_length); + // derive secret for handshake "tls13 derived": + DEBUG_DUMP_HEX_LABEL("null hash", hash, mac_length); + _private_tls_hkdf_expand_label(mac_length, salt, mac_length, prk, mac_length, "derived", 7, hash, mac_length); + // extract secret "handshake": + DEBUG_DUMP_HEX_LABEL("salt", salt, mac_length); + _private_tls_hkdf_extract(mac_length, prk, mac_length, salt, mac_length, context->premaster_key, context->premaster_key_len); + } + + if (!is_aead) { + DEBUG_PRINT("KEY EXPANSION FAILED, NON AEAD CIPHER\n"); + return 0; + } + + unsigned char secret[TLS_MAX_MAC_SIZE]; + unsigned char hs_secret[TLS_MAX_HASH_SIZE]; + + int hash_size; + if (handshake) + hash_size = _private_tls_get_hash(context, hash); + else + hash_size = _private_tls_done_hash(context, hash); + DEBUG_DUMP_HEX_LABEL("messages hash", hash, hash_size); + + if (context->is_server) { + _private_tls_hkdf_expand_label(mac_length, hs_secret, mac_length, prk, mac_length, server_key, 12, context->server_finished_hash ? context->server_finished_hash : hash, hash_size); + DEBUG_DUMP_HEX_LABEL(server_key, hs_secret, mac_length); + serverkey = local_keybuffer; + serveriv = local_ivbuffer; + clientkey = remote_keybuffer; + clientiv = remote_ivbuffer; + } else { + _private_tls_hkdf_expand_label(mac_length, hs_secret, mac_length, prk, mac_length, client_key, 12, context->server_finished_hash ? context->server_finished_hash : hash, hash_size); + DEBUG_DUMP_HEX_LABEL(client_key, hs_secret, mac_length); + serverkey = remote_keybuffer; + serveriv = remote_ivbuffer; + clientkey = local_keybuffer; + clientiv = local_ivbuffer; + } + + int iv_length = TLS_13_AES_GCM_IV_LENGTH; +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (is_aead == 2) + iv_length = TLS_CHACHA20_IV_LENGTH; +#endif + + _private_tls_hkdf_expand_label(mac_length, local_keybuffer, key_length, hs_secret, mac_length, "key", 3, NULL, 0); + _private_tls_hkdf_expand_label(mac_length, local_ivbuffer, iv_length, hs_secret, mac_length, "iv", 2, NULL, 0); + if (context->is_server) + _private_tls_hkdf_expand_label(mac_length, secret, mac_length, prk, mac_length, client_key, 12, context->server_finished_hash ? context->server_finished_hash : hash, hash_size); + else + _private_tls_hkdf_expand_label(mac_length, secret, mac_length, prk, mac_length, server_key, 12, context->server_finished_hash ? context->server_finished_hash : hash, hash_size); + + _private_tls_hkdf_expand_label(mac_length, remote_keybuffer, key_length, secret, mac_length, "key", 3, NULL, 0); + _private_tls_hkdf_expand_label(mac_length, remote_ivbuffer, iv_length, secret, mac_length, "iv", 2, NULL, 0); + + DEBUG_DUMP_HEX_LABEL("CLIENT KEY", clientkey, key_length) + DEBUG_DUMP_HEX_LABEL("CLIENT IV", clientiv, iv_length) + DEBUG_DUMP_HEX_LABEL("SERVER KEY", serverkey, key_length) + DEBUG_DUMP_HEX_LABEL("SERVER IV", serveriv, iv_length) + + TLS_FREE(context->finished_key); + TLS_FREE(context->remote_finished_key); + if (handshake) { + context->finished_key = (unsigned char *)TLS_MALLOC(mac_length); + context->remote_finished_key = (unsigned char *)TLS_MALLOC(mac_length); + + if (context->finished_key) { + _private_tls_hkdf_expand_label(mac_length, context->finished_key, mac_length, hs_secret, mac_length, "finished", 8, NULL, 0); + DEBUG_DUMP_HEX_LABEL("FINISHED", context->finished_key, mac_length) + } + + if (context->remote_finished_key) { + _private_tls_hkdf_expand_label(mac_length, context->remote_finished_key, mac_length, secret, mac_length, "finished", 8, NULL, 0); + DEBUG_DUMP_HEX_LABEL("REMOTE FINISHED", context->remote_finished_key, mac_length) + } + } else { + context->finished_key = NULL; + context->remote_finished_key = NULL; + TLS_FREE(context->server_finished_hash); + context->server_finished_hash = NULL; + } + + if (context->is_server) { +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (is_aead == 2) { + memcpy(context->crypto.ctx_remote_mac.remote_nonce, clientiv, iv_length); + memcpy(context->crypto.ctx_local_mac.local_nonce, serveriv, iv_length); + } else +#endif + if (is_aead) { + memcpy(context->crypto.ctx_remote_mac.remote_iv, clientiv, iv_length); + memcpy(context->crypto.ctx_local_mac.local_iv, serveriv, iv_length); + } + if (_private_tls_crypto_create(context, key_length, serverkey, serveriv, clientkey, clientiv)) + return 0; + } else { +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (is_aead == 2) { + memcpy(context->crypto.ctx_local_mac.local_nonce, clientiv, iv_length); + memcpy(context->crypto.ctx_remote_mac.remote_nonce, serveriv, iv_length); + } else +#endif + if (is_aead) { + memcpy(context->crypto.ctx_local_mac.local_iv, clientiv, iv_length); + memcpy(context->crypto.ctx_remote_mac.remote_iv, serveriv, iv_length); + } + if (_private_tls_crypto_create(context, key_length, clientkey, clientiv, serverkey, serveriv)) + return 0; + } + context->crypto.created = 1 + is_aead; + if (context->exportable) { + TLS_FREE(context->exportable_keys); + context->exportable_keys = (unsigned char *)TLS_MALLOC(key_length * 2); + if (context->exportable_keys) { + if (context->is_server) { + memcpy(context->exportable_keys, serverkey, key_length); + memcpy(context->exportable_keys + key_length, clientkey, key_length); + } else { + memcpy(context->exportable_keys, clientkey, key_length); + memcpy(context->exportable_keys + key_length, serverkey, key_length); + } + context->exportable_size = key_length * 2; + } + } + TLS_FREE(context->master_key); + context->master_key = (unsigned char *)TLS_MALLOC(mac_length); + if (context->master_key) { + memcpy(context->master_key, prk, mac_length); + context->master_key_len = mac_length; + } + context->local_sequence_number = 0; + context->remote_sequence_number = 0; + + // extract client_mac_key(mac_key_length) + // extract server_mac_key(mac_key_length) + // extract client_key(enc_key_length) + // extract server_key(enc_key_length) + // extract client_iv(fixed_iv_lengh) + // extract server_iv(fixed_iv_length) + return 1; +} +#endif + +int _private_tls_expand_key(struct TLSContext *context) { + unsigned char key[TLS_MAX_KEY_EXPANSION_SIZE]; +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + return 0; +#endif + + if ((!context->master_key) || (!context->master_key_len)) + return 0; + + int key_length = _private_tls_key_length(context); + int mac_length = _private_tls_mac_length(context); + + if ((!key_length) || (!mac_length)) { + DEBUG_PRINT("KEY EXPANSION FAILED, KEY LENGTH: %i, MAC LENGTH: %i\n", key_length, mac_length); + return 0; + } + unsigned char *clientkey = NULL; + unsigned char *serverkey = NULL; + unsigned char *clientiv = NULL; + unsigned char *serveriv = NULL; + int iv_length = TLS_AES_IV_LENGTH; + int is_aead = _private_tls_is_aead(context); + if (context->is_server) + _private_tls_prf(context, key, sizeof(key), context->master_key, context->master_key_len, (unsigned char *)"key expansion", 13, context->local_random, TLS_SERVER_RANDOM_SIZE, context->remote_random, TLS_CLIENT_RANDOM_SIZE); + else + _private_tls_prf(context, key, sizeof(key), context->master_key, context->master_key_len, (unsigned char *)"key expansion", 13, context->remote_random, TLS_SERVER_RANDOM_SIZE, context->local_random, TLS_CLIENT_RANDOM_SIZE); + + DEBUG_DUMP_HEX_LABEL("LOCAL RANDOM ", context->local_random, TLS_SERVER_RANDOM_SIZE); + DEBUG_DUMP_HEX_LABEL("REMOTE RANDOM", context->remote_random, TLS_CLIENT_RANDOM_SIZE); + DEBUG_PRINT("\n=========== EXPANSION ===========\n"); + DEBUG_DUMP_HEX(key, TLS_MAX_KEY_EXPANSION_SIZE); + DEBUG_PRINT("\n"); + + int pos = 0; +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (is_aead == 2) { + iv_length = TLS_CHACHA20_IV_LENGTH; + } else +#endif + if (is_aead) { + iv_length = TLS_AES_GCM_IV_LENGTH; + } else { + if (context->is_server) { + memcpy(context->crypto.ctx_remote_mac.remote_mac, &key[pos], mac_length); + pos += mac_length; + memcpy(context->crypto.ctx_local_mac.local_mac, &key[pos], mac_length); + pos += mac_length; + } else { + memcpy(context->crypto.ctx_local_mac.local_mac, &key[pos], mac_length); + pos += mac_length; + memcpy(context->crypto.ctx_remote_mac.remote_mac, &key[pos], mac_length); + pos += mac_length; + } + } + + clientkey = &key[pos]; + pos += key_length; + serverkey = &key[pos]; + pos += key_length; + clientiv = &key[pos]; + pos += iv_length; + serveriv = &key[pos]; + pos += iv_length; + DEBUG_PRINT("EXPANSION %i/%i\n", (int)pos, (int)TLS_MAX_KEY_EXPANSION_SIZE); + DEBUG_DUMP_HEX_LABEL("CLIENT KEY", clientkey, key_length) + DEBUG_DUMP_HEX_LABEL("CLIENT IV", clientiv, iv_length) + DEBUG_DUMP_HEX_LABEL("CLIENT MAC KEY", context->is_server ? context->crypto.ctx_remote_mac.remote_mac : context->crypto.ctx_local_mac.local_mac, mac_length) + DEBUG_DUMP_HEX_LABEL("SERVER KEY", serverkey, key_length) + DEBUG_DUMP_HEX_LABEL("SERVER IV", serveriv, iv_length) + DEBUG_DUMP_HEX_LABEL("SERVER MAC KEY", context->is_server ? context->crypto.ctx_local_mac.local_mac : context->crypto.ctx_remote_mac.remote_mac, mac_length) + + if (context->is_server) { +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (is_aead == 2) { + memcpy(context->crypto.ctx_remote_mac.remote_nonce, clientiv, iv_length); + memcpy(context->crypto.ctx_local_mac.local_nonce, serveriv, iv_length); + } else +#endif + if (is_aead) { + memcpy(context->crypto.ctx_remote_mac.remote_aead_iv, clientiv, iv_length); + memcpy(context->crypto.ctx_local_mac.local_aead_iv, serveriv, iv_length); + } + if (_private_tls_crypto_create(context, key_length, serverkey, serveriv, clientkey, clientiv)) + return 0; + } else { +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (is_aead == 2) { + memcpy(context->crypto.ctx_local_mac.local_nonce, clientiv, iv_length); + memcpy(context->crypto.ctx_remote_mac.remote_nonce, serveriv, iv_length); + } else +#endif + if (is_aead) { + memcpy(context->crypto.ctx_local_mac.local_aead_iv, clientiv, iv_length); + memcpy(context->crypto.ctx_remote_mac.remote_aead_iv, serveriv, iv_length); + } + if (_private_tls_crypto_create(context, key_length, clientkey, clientiv, serverkey, serveriv)) + return 0; + } + + if (context->exportable) { + TLS_FREE(context->exportable_keys); + context->exportable_keys = (unsigned char *)TLS_MALLOC(key_length * 2); + if (context->exportable_keys) { + if (context->is_server) { + memcpy(context->exportable_keys, serverkey, key_length); + memcpy(context->exportable_keys + key_length, clientkey, key_length); + } else { + memcpy(context->exportable_keys, clientkey, key_length); + memcpy(context->exportable_keys + key_length, serverkey, key_length); + } + context->exportable_size = key_length * 2; + } + } + + // extract client_mac_key(mac_key_length) + // extract server_mac_key(mac_key_length) + // extract client_key(enc_key_length) + // extract server_key(enc_key_length) + // extract client_iv(fixed_iv_lengh) + // extract server_iv(fixed_iv_length) + return 1; +} + +int _private_tls_compute_key(struct TLSContext *context, unsigned int key_len) { +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + return 0; +#endif + if ((!context->premaster_key) || (!context->premaster_key_len) || (key_len < 48)) { + DEBUG_PRINT("CANNOT COMPUTE MASTER SECRET\n"); + return 0; + } + unsigned char master_secret_label[] = "master secret"; +#ifdef TLS_CHECK_PREMASTER_KEY + if (!tls_cipher_is_ephemeral(context)) { + unsigned short version = ntohs(*(unsigned short *)context->premaster_key); + // this check is not true for DHE/ECDHE ciphers + if (context->version > version) { + DEBUG_PRINT("Mismatch protocol version 0x(%x)\n", version); + return 0; + } + } +#endif + TLS_FREE(context->master_key); + context->master_key_len = 0; + context->master_key = NULL; + if ((context->version == TLS_V13) || (context->version == TLS_V12) || (context->version == TLS_V11) || (context->version == TLS_V10) || (context->version == DTLS_V13) || (context->version == DTLS_V12) || (context->version == DTLS_V10)) { + context->master_key = (unsigned char *)TLS_MALLOC(key_len); + if (!context->master_key) + return 0; + context->master_key_len = key_len; + if (context->is_server) { + _private_tls_prf(context, + context->master_key, context->master_key_len, + context->premaster_key, context->premaster_key_len, + master_secret_label, 13, + context->remote_random, TLS_CLIENT_RANDOM_SIZE, + context->local_random, TLS_SERVER_RANDOM_SIZE + ); + } else { + _private_tls_prf(context, + context->master_key, context->master_key_len, + context->premaster_key, context->premaster_key_len, + master_secret_label, 13, + context->local_random, TLS_CLIENT_RANDOM_SIZE, + context->remote_random, TLS_SERVER_RANDOM_SIZE + ); + } + TLS_FREE(context->premaster_key); + context->premaster_key = NULL; + context->premaster_key_len = 0; + DEBUG_PRINT("\n=========== Master key ===========\n"); + DEBUG_DUMP_HEX(context->master_key, context->master_key_len); + DEBUG_PRINT("\n"); + _private_tls_expand_key(context); + return 1; + } + return 0; +} + +unsigned char *tls_pem_decode(const unsigned char *data_in, unsigned int input_length, int cert_index, unsigned int *output_len) { + unsigned int i; + *output_len = 0; + int alloc_len = input_length / 4 * 3; + unsigned char *output = (unsigned char *)TLS_MALLOC(alloc_len); + if (!output) + return NULL; + unsigned int start_at = 0; + unsigned int idx = 0; + for (i = 0; i < input_length; i++) { + if ((data_in[i] == '\n') || (data_in[i] == '\r')) + continue; + + if (data_in[i] != '-') { + // read entire line + while ((i < input_length) && (data_in[i] != '\n')) + i++; + continue; + } + + if (data_in[i] == '-') { + unsigned int end_idx = i; + //read until end of line + while ((i < input_length) && (data_in[i] != '\n')) + i++; + if (start_at) { + if (cert_index > 0) { + cert_index--; + start_at = 0; + } else { + idx = _private_b64_decode((const char *)&data_in[start_at], end_idx - start_at, output); + break; + } + } else + start_at = i + 1; + } + } + *output_len = idx; + if (!idx) { + TLS_FREE(output); + return NULL; + } + return output; +} + +int _is_oid(const unsigned char *oid, const unsigned char *compare_to, int compare_to_len) { + int i = 0; + while ((oid[i]) && (i < compare_to_len)) { + if (oid[i] != compare_to[i]) + return 0; + + i++; + } + return 1; +} + +int _is_oid2(const unsigned char *oid, const unsigned char *compare_to, int compare_to_len, int oid_len) { + int i = 0; + if (oid_len < compare_to_len) + compare_to_len = oid_len; + while (i < compare_to_len) { + if (oid[i] != compare_to[i]) + return 0; + + i++; + } + return 1; +} + +struct TLSCertificate *tls_create_certificate() { + struct TLSCertificate *cert = (struct TLSCertificate *)TLS_MALLOC(sizeof(struct TLSCertificate)); + if (cert) + memset(cert, 0, sizeof(struct TLSCertificate)); + return cert; +} + +int tls_certificate_valid_subject_name(const unsigned char *cert_subject, const char *subject) { + // no subjects ... + if (((!cert_subject) || (!cert_subject[0])) && ((!subject) || (!subject[0]))) + return 0; + + if ((!subject) || (!subject[0])) + return bad_certificate; + + if ((!cert_subject) || (!cert_subject[0])) + return bad_certificate; + + // exact match + if (!strcmp((const char *)cert_subject, subject)) + return 0; + + const char *wildcard = strchr((const char *)cert_subject, '*'); + if (wildcard) { + // 6.4.3 (1) The client SHOULD NOT attempt to match a presented identifier in + // which the wildcard character comprises a label other than the left-most label + if (!wildcard[1]) { + // subject is [*] + // or + // subject is [something*] .. invalid + return bad_certificate; + } + wildcard++; + const char *match = strstr(subject, wildcard); + if ((!match) && (wildcard[0] == '.')) { + // check *.domain.com agains domain.com + wildcard++; + if (!strcasecmp(subject, wildcard)) + return 0; + } + if (match) { + uintptr_t offset = (uintptr_t)match - (uintptr_t)subject; + if (offset) { + // check for foo.*.domain.com against *.domain.com (invalid) + if (memchr(subject, '.', offset)) + return bad_certificate; + } + // check if exact match + if (!strcasecmp(match, wildcard)) + return 0; + } + } + + return bad_certificate; +} + +int tls_certificate_valid_subject(struct TLSCertificate *cert, const char *subject) { + int i; + if (!cert) + return certificate_unknown; + int err = tls_certificate_valid_subject_name(cert->subject, subject); + if ((err) && (cert->san)) { + for (i = 0; i < cert->san_length; i++) { + err = tls_certificate_valid_subject_name(cert->san[i], subject); + if (!err) + return err; + } + } + return err; +} + +int tls_certificate_is_valid(struct TLSCertificate *cert) { + if (!cert) + return certificate_unknown; + if (!cert->not_before) + return certificate_unknown; + if (!cert->not_after) + return certificate_unknown; + //20160224182300Z// + char current_time[16]; + time_t t = time(NULL); + struct tm *utct = gmtime(&t); + if (utct) { + current_time[0] = 0; + snprintf(current_time, sizeof(current_time), "%04d%02d%02d%02d%02d%02dZ", 1900 + utct->tm_year, utct->tm_mon + 1, utct->tm_mday, utct->tm_hour, utct->tm_min, utct->tm_sec); + if (strcasecmp((char *)cert->not_before, current_time) > 0) { + DEBUG_PRINT("Certificate is not yer valid, now: %s (validity: %s - %s)\n", current_time, cert->not_before, cert->not_after); + return certificate_expired; + } + if (strcasecmp((char *)cert->not_after, current_time) < 0) { + DEBUG_PRINT("Expired certificate, now: %s (validity: %s - %s)\n", current_time, cert->not_before, cert->not_after); + return certificate_expired; + } + DEBUG_PRINT("Valid certificate, now: %s (validity: %s - %s)\n", current_time, cert->not_before, cert->not_after); + } + return 0; +} + +void tls_certificate_set_copy(unsigned char **member, const unsigned char *val, int len) { + if (!member) + return; + TLS_FREE(*member); + if (len) { + *member = (unsigned char *)TLS_MALLOC(len + 1); + if (*member) { + memcpy(*member, val, len); + (*member)[len] = 0; + } + } else + *member = NULL; +} + +void tls_certificate_set_copy_date(unsigned char **member, const unsigned char *val, int len) { + if (!member) + return; + TLS_FREE(*member); + if (len > 4) { + *member = (unsigned char *)TLS_MALLOC(len + 3); + if (*member) { + if (val[0] == '9') { + (*member)[0]='1'; + (*member)[1]='9'; + } else { + (*member)[0]='2'; + (*member)[1]='0'; + } + memcpy(*member + 2, val, len); + (*member)[len] = 0; + } + } else + *member = NULL; +} + +void tls_certificate_set_key(struct TLSCertificate *cert, const unsigned char *val, int len) { + if ((!val[0]) && (len % 2)) { + val++; + len--; + } + tls_certificate_set_copy(&cert->pk, val, len); + if (cert->pk) + cert->pk_len = len; +} + +void tls_certificate_set_priv(struct TLSCertificate *cert, const unsigned char *val, int len) { + tls_certificate_set_copy(&cert->priv, val, len); + if (cert->priv) + cert->priv_len = len; +} + +void tls_certificate_set_sign_key(struct TLSCertificate *cert, const unsigned char *val, int len) { + if ((!val[0]) && (len % 2)) { + val++; + len--; + } + tls_certificate_set_copy(&cert->sign_key, val, len); + if (cert->sign_key) + cert->sign_len = len; +} + +char *tls_certificate_to_string(struct TLSCertificate *cert, char *buffer, int len) { + unsigned int i; + if (!buffer) + return NULL; + buffer[0] = 0; + if (cert->version) { + int res = snprintf(buffer, len, "X.509v%i certificate\n Issued by: [%s]%s (%s)\n Issued to: [%s]%s (%s, %s)\n Subject: %s\n Validity: %s - %s\n OCSP: %s\n Serial number: ", + (int)cert->version, + cert->issuer_country, cert->issuer_entity, cert->issuer_subject, + cert->country, cert->entity, cert->state, cert->location, + cert->subject, + cert->not_before, cert->not_after, + cert->ocsp + ); + if (res > 0) { + for (i = 0; i < cert->serial_len; i++) + res += snprintf(buffer + res, len - res, "%02x", (int)cert->serial_number[i]); + } + if ((cert->san) && (cert->san_length)) { + res += snprintf(buffer + res, len - res, "\n Alternative subjects: "); + for (i = 0; i < cert->san_length; i++) { + if (i) + res += snprintf(buffer + res, len - res, ", %s", cert->san[i]); + else + res += snprintf(buffer + res, len - res, "%s", cert->san[i]); + } + } + res += snprintf(buffer + res, len - res, "\n Key (%i bits, ", cert->pk_len * 8); + if (res > 0) { + switch (cert->key_algorithm) { + case TLS_RSA_SIGN_RSA: + res += snprintf(buffer + res, len - res, "RSA_SIGN_RSA"); + break; + case TLS_RSA_SIGN_MD5: + res += snprintf(buffer + res, len - res, "RSA_SIGN_MD5"); + break; + case TLS_RSA_SIGN_SHA1: + res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA1"); + break; + case TLS_RSA_SIGN_SHA256: + res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA256"); + break; + case TLS_RSA_SIGN_SHA384: + res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA384"); + break; + case TLS_RSA_SIGN_SHA512: + res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA512"); + break; + case TLS_ECDSA_SIGN_SHA256: + res += snprintf(buffer + res, len - res, "ECDSA_SIGN_SHA512"); + break; + case TLS_EC_PUBLIC_KEY: + res += snprintf(buffer + res, len - res, "EC_PUBLIC_KEY"); + break; + default: + res += snprintf(buffer + res, len - res, "not supported (%i)", (int)cert->key_algorithm); + } + } + if ((res > 0) && (cert->ec_algorithm)) { + switch (cert->ec_algorithm) { + case TLS_EC_prime192v1: + res += snprintf(buffer + res, len - res, " prime192v1"); + break; + case TLS_EC_prime192v2: + res += snprintf(buffer + res, len - res, " prime192v2"); + break; + case TLS_EC_prime192v3: + res += snprintf(buffer + res, len - res, " prime192v3"); + break; + case TLS_EC_prime239v2: + res += snprintf(buffer + res, len - res, " prime239v2"); + break; + case TLS_EC_secp256r1: + res += snprintf(buffer + res, len - res, " EC_secp256r1"); + break; + case TLS_EC_secp224r1: + res += snprintf(buffer + res, len - res, " EC_secp224r1"); + break; + case TLS_EC_secp384r1: + res += snprintf(buffer + res, len - res, " EC_secp384r1"); + break; + case TLS_EC_secp521r1: + res += snprintf(buffer + res, len - res, " EC_secp521r1"); + break; + default: + res += snprintf(buffer + res, len - res, " unknown(%i)", (int)cert->ec_algorithm); + } + } + res += snprintf(buffer + res, len - res, "):\n"); + if (res > 0) { + for (i = 0; i < cert->pk_len; i++) + res += snprintf(buffer + res, len - res, "%02x", (int)cert->pk[i]); + res += snprintf(buffer + res, len - res, "\n Signature (%i bits, ", cert->sign_len * 8); + switch (cert->algorithm) { + case TLS_RSA_SIGN_RSA: + res += snprintf(buffer + res, len - res, "RSA_SIGN_RSA):\n"); + break; + case TLS_RSA_SIGN_MD5: + res += snprintf(buffer + res, len - res, "RSA_SIGN_MD5):\n"); + break; + case TLS_RSA_SIGN_SHA1: + res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA1):\n"); + break; + case TLS_RSA_SIGN_SHA256: + res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA256):\n"); + break; + case TLS_RSA_SIGN_SHA384: + res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA384):\n"); + break; + case TLS_RSA_SIGN_SHA512: + res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA512):\n"); + break; + case TLS_EC_PUBLIC_KEY: + res += snprintf(buffer + res, len - res, "EC_PUBLIC_KEY):\n"); + break; + default: + res += snprintf(buffer + res, len - res, "not supported):\n"); + } + + for (i = 0; i < cert->sign_len; i++) + res += snprintf(buffer + res, len - res, "%02x", (int)cert->sign_key[i]); + } + } else + if ((cert->priv) && (cert->priv_len)) { + int res = snprintf(buffer, len, "X.509 private key\n"); + res += snprintf(buffer + res, len - res, " Private Key: "); + if (res > 0) { + for (i = 0; i < cert->priv_len; i++) + res += snprintf(buffer + res, len - res, "%02x", (int)cert->priv[i]); + } + } else + snprintf(buffer, len, "Empty ASN1 file"); + return buffer; +} + +void tls_certificate_set_exponent(struct TLSCertificate *cert, const unsigned char *val, int len) { + tls_certificate_set_copy(&cert->exponent, val, len); + if (cert->exponent) + cert->exponent_len = len; +} + +void tls_certificate_set_serial(struct TLSCertificate *cert, const unsigned char *val, int len) { + tls_certificate_set_copy(&cert->serial_number, val, len); + if (cert->serial_number) + cert->serial_len = len; +} + +void tls_certificate_set_algorithm(struct TLSContext *context, unsigned int *algorithm, const unsigned char *val, int len) { + if ((len == 7) && (_is_oid(val, TLS_EC_PUBLIC_KEY_OID, 7))) { + *algorithm = TLS_EC_PUBLIC_KEY; + return; + } + if (len == 8) { + if (_is_oid(val, TLS_EC_prime192v1_OID, len)) { + *algorithm = TLS_EC_prime192v1; + return; + } + if (_is_oid(val, TLS_EC_prime192v2_OID, len)) { + *algorithm = TLS_EC_prime192v2; + return; + } + if (_is_oid(val, TLS_EC_prime192v3_OID, len)) { + *algorithm = TLS_EC_prime192v3; + return; + } + if (_is_oid(val, TLS_EC_prime239v1_OID, len)) { + *algorithm = TLS_EC_prime239v1; + return; + } + if (_is_oid(val, TLS_EC_prime239v2_OID, len)) { + *algorithm = TLS_EC_prime239v2; + return; + } + if (_is_oid(val, TLS_EC_prime239v3_OID, len)) { + *algorithm = TLS_EC_prime239v3; + return; + } + if (_is_oid(val, TLS_EC_prime256v1_OID, len)) { + *algorithm = TLS_EC_prime256v1; + return; + } + } + if (len == 5) { + if (_is_oid2(val, TLS_EC_secp224r1_OID, len, sizeof(TLS_EC_secp224r1_OID) - 1)) { + *algorithm = TLS_EC_secp224r1; + return; + } + if (_is_oid2(val, TLS_EC_secp384r1_OID, len, sizeof(TLS_EC_secp384r1_OID) - 1)) { + *algorithm = TLS_EC_secp384r1; + return; + } + if (_is_oid2(val, TLS_EC_secp521r1_OID, len, sizeof(TLS_EC_secp521r1_OID) - 1)) { + *algorithm = TLS_EC_secp521r1; + return; + } + } + if (len != 9) + return; + + if (_is_oid(val, TLS_RSA_SIGN_SHA256_OID, 9)) { + *algorithm = TLS_RSA_SIGN_SHA256; + return; + } + + if (_is_oid(val, TLS_RSA_SIGN_RSA_OID, 9)) { + *algorithm = TLS_RSA_SIGN_RSA; + return; + } + + if (_is_oid(val, TLS_RSA_SIGN_SHA1_OID, 9)) { + *algorithm = TLS_RSA_SIGN_SHA1; + return; + } + + if (_is_oid(val, TLS_RSA_SIGN_SHA512_OID, 9)) { + *algorithm = TLS_RSA_SIGN_SHA512; + return; + } + + if (_is_oid(val, TLS_RSA_SIGN_SHA384_OID, 9)) { + *algorithm = TLS_RSA_SIGN_SHA384; + return; + } + + if (_is_oid(val, TLS_RSA_SIGN_MD5_OID, 9)) { + *algorithm = TLS_RSA_SIGN_MD5; + return; + } + + if (_is_oid(val, TLS_ECDSA_SIGN_SHA256_OID, 9)) { + *algorithm = TLS_ECDSA_SIGN_SHA256; + return; + } + // client should fail on unsupported signature + if (!context->is_server) { + DEBUG_PRINT("UNSUPPORTED SIGNATURE ALGORITHM\n"); + context->critical_error = 1; + } +} + +void tls_destroy_certificate(struct TLSCertificate *cert) { + if (cert) { + int i; + TLS_FREE(cert->exponent); + TLS_FREE(cert->pk); + TLS_FREE(cert->issuer_country); + TLS_FREE(cert->issuer_state); + TLS_FREE(cert->issuer_location); + TLS_FREE(cert->issuer_entity); + TLS_FREE(cert->issuer_subject); + TLS_FREE(cert->country); + TLS_FREE(cert->state); + TLS_FREE(cert->location); + TLS_FREE(cert->subject); + for (i = 0; i < cert->san_length; i++) { + TLS_FREE(cert->san[i]); + } + TLS_FREE(cert->san); + TLS_FREE(cert->ocsp); + TLS_FREE(cert->serial_number); + TLS_FREE(cert->entity); + TLS_FREE(cert->not_before); + TLS_FREE(cert->not_after); + TLS_FREE(cert->sign_key); + TLS_FREE(cert->priv); + TLS_FREE(cert->der_bytes); + TLS_FREE(cert->bytes); + TLS_FREE(cert->fingerprint); + TLS_FREE(cert); + } +} + +struct TLSPacket *tls_create_packet(struct TLSContext *context, unsigned char type, unsigned short version, int payload_size_hint) { + struct TLSPacket *packet = (struct TLSPacket *)TLS_MALLOC(sizeof(struct TLSPacket)); + if (!packet) + return NULL; + packet->broken = 0; + if (payload_size_hint > 0) + packet->size = payload_size_hint + 10; + else + packet->size = TLS_BLOB_INCREMENT; + packet->buf = (unsigned char *)TLS_MALLOC(packet->size); + packet->context = context; + if (!packet->buf) { + TLS_FREE(packet); + return NULL; + } + if ((context) && (context->dtls)) + packet->len = 13; + else + packet->len = 5; + packet->buf[0] = type; +#ifdef WITH_TLS_13 + switch (version) { + case TLS_V13: + // check if context is not null. If null, is a tls_export_context call + if (context) + *(unsigned short *)(packet->buf + 1) = 0x0303; // no need to reorder (same bytes) + else + *(unsigned short *)(packet->buf + 1) = htons(version); + break; + case DTLS_V13: + *(unsigned short *)(packet->buf + 1) = htons(DTLS_V13); + break; + default: + *(unsigned short *)(packet->buf + 1) = htons(version); + } +#else + *(unsigned short *)(packet->buf + 1) = htons(version); +#endif + return packet; +} + +void tls_destroy_packet(struct TLSPacket *packet) { + if (packet) { + if (packet->buf) + TLS_FREE(packet->buf); + TLS_FREE(packet); + } +} + +int _private_tls_crypto_create(struct TLSContext *context, int key_length, unsigned char *localkey, unsigned char *localiv, unsigned char *remotekey, unsigned char *remoteiv) { + if (context->crypto.created) { + if (context->crypto.created == 1) { + cbc_done(&context->crypto.ctx_remote.aes_remote); + cbc_done(&context->crypto.ctx_local.aes_local); + } else { +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (context->crypto.created == 2) { +#endif + unsigned char dummy_buffer[32]; + unsigned long tag_len = 0; + gcm_done(&context->crypto.ctx_remote.aes_gcm_remote, dummy_buffer, &tag_len); + gcm_done(&context->crypto.ctx_local.aes_gcm_local, dummy_buffer, &tag_len); +#ifdef TLS_WITH_CHACHA20_POLY1305 + } +#endif + } + context->crypto.created = 0; + } + tls_init(); + int is_aead = _private_tls_is_aead(context); + int cipherID = find_cipher("aes"); + DEBUG_PRINT("Using cipher ID: %x\n", (int)context->cipher); +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (is_aead == 2) { + unsigned int counter = 1; + + chacha_keysetup(&context->crypto.ctx_local.chacha_local, localkey, key_length * 8); + chacha_ivsetup_96bitnonce(&context->crypto.ctx_local.chacha_local, localiv, (unsigned char *)&counter); + + chacha_keysetup(&context->crypto.ctx_remote.chacha_remote, remotekey, key_length * 8); + chacha_ivsetup_96bitnonce(&context->crypto.ctx_remote.chacha_remote, remoteiv, (unsigned char *)&counter); + + context->crypto.created = 3; + } else +#endif + if (is_aead) { + int res1 = gcm_init(&context->crypto.ctx_local.aes_gcm_local, cipherID, localkey, key_length); + int res2 = gcm_init(&context->crypto.ctx_remote.aes_gcm_remote, cipherID, remotekey, key_length); + + if ((res1) || (res2)) + return TLS_GENERIC_ERROR; + context->crypto.created = 2; + } else { + int res1 = cbc_start(cipherID, localiv, localkey, key_length, 0, &context->crypto.ctx_local.aes_local); + int res2 = cbc_start(cipherID, remoteiv, remotekey, key_length, 0, &context->crypto.ctx_remote.aes_remote); + + if ((res1) || (res2)) + return TLS_GENERIC_ERROR; + context->crypto.created = 1; + } + return 0; +} + +int _private_tls_crypto_encrypt(struct TLSContext *context, unsigned char *buf, unsigned char *ct, unsigned int len) { + if (context->crypto.created == 1) + return cbc_encrypt(buf, ct, len, &context->crypto.ctx_local.aes_local); + + memset(ct, 0, len); + return TLS_GENERIC_ERROR; +} + +int _private_tls_crypto_decrypt(struct TLSContext *context, unsigned char *buf, unsigned char *pt, unsigned int len) { + if (context->crypto.created == 1) + return cbc_decrypt(buf, pt, len, &context->crypto.ctx_remote.aes_remote); + + memset(pt, 0, len); + return TLS_GENERIC_ERROR; +} + +void _private_tls_crypto_done(struct TLSContext *context) { + unsigned char dummy_buffer[32]; + unsigned long tag_len = 0; + switch (context->crypto.created) { + case 1: + cbc_done(&context->crypto.ctx_remote.aes_remote); + cbc_done(&context->crypto.ctx_local.aes_local); + break; + case 2: + gcm_done(&context->crypto.ctx_remote.aes_gcm_remote, dummy_buffer, &tag_len); + gcm_done(&context->crypto.ctx_local.aes_gcm_local, dummy_buffer, &tag_len); + break; + } + context->crypto.created = 0; +} + +void tls_packet_update(struct TLSPacket *packet) { + if ((packet) && (!packet->broken)) { + int footer_size = 0; +#ifdef WITH_TLS_13 + if ((packet->context) && ((packet->context->version == TLS_V13) || (packet->context->version == DTLS_V13)) && (packet->context->cipher_spec_set) && (packet->context->crypto.created)) { + // type + tls_packet_uint8(packet, packet->buf[0]); + // no padding + // tls_packet_uint8(packet, 0); + footer_size = 1; + } +#endif + unsigned int header_size = 5; + if ((packet->context) && (packet->context->dtls)) { + header_size = 13; + *(unsigned short *)(packet->buf + 3) = htons(packet->context->dtls_epoch_local); + uint64_t sequence_number = packet->context->local_sequence_number; + packet->buf[5] = (unsigned char)(sequence_number / 0x10000000000LL); + sequence_number %= 0x10000000000LL; + packet->buf[6] = (unsigned char)(sequence_number / 0x100000000LL); + sequence_number %= 0x100000000LL; + packet->buf[7] = (unsigned char)(sequence_number / 0x1000000); + sequence_number %= 0x1000000; + packet->buf[8] = (unsigned char)(sequence_number / 0x10000); + sequence_number %= 0x10000; + packet->buf[9] = (unsigned char)(sequence_number / 0x100); + sequence_number %= 0x100; + packet->buf[10] = (unsigned char)sequence_number; + + *(unsigned short *)(packet->buf + 11) = htons(packet->len - header_size); + } else + *(unsigned short *)(packet->buf + 3) = htons(packet->len - header_size); + if (packet->context) { + if (packet->buf[0] != TLS_CHANGE_CIPHER) { + if ((packet->buf[0] == TLS_HANDSHAKE) && (packet->len > header_size)) { + unsigned char handshake_type = packet->buf[header_size]; + if ((handshake_type != 0x00) && (handshake_type != 0x03)) + _private_tls_update_hash(packet->context, packet->buf + header_size, packet->len - header_size - footer_size); + } +#ifdef TLS_12_FALSE_START + if (((packet->context->cipher_spec_set) || (packet->context->false_start)) && (packet->context->crypto.created)) { +#else + if ((packet->context->cipher_spec_set) && (packet->context->crypto.created)) { +#endif + int block_size = TLS_AES_BLOCK_SIZE; + int mac_size = 0; + unsigned int length = 0; + unsigned char padding = 0; + unsigned int pt_length = packet->len - header_size; + + if (packet->context->crypto.created == 1) { + mac_size = _private_tls_mac_length(packet->context); +#ifdef TLS_LEGACY_SUPPORT + if (packet->context->version == TLS_V10) + length = packet->len - header_size + mac_size; + else +#endif + length = packet->len - header_size + TLS_AES_IV_LENGTH + mac_size; + padding = block_size - length % block_size; + length += padding; +#ifdef TLS_WITH_CHACHA20_POLY1305 + } else + if (packet->context->crypto.created == 3) { + mac_size = POLY1305_TAGLEN; + length = packet->len - header_size + mac_size; +#endif + } else { + mac_size = TLS_GCM_TAG_LEN; + length = packet->len - header_size + 8 + mac_size; + } + if (packet->context->crypto.created == 1) { + unsigned char *buf = (unsigned char *)TLS_MALLOC(length); + if (buf) { + unsigned char *ct = (unsigned char *)TLS_MALLOC(length + header_size); + if (ct) { + unsigned int buf_pos = 0; + memcpy(ct, packet->buf, header_size - 2); + *(unsigned short *)&ct[header_size - 2] = htons(length); +#ifdef TLS_LEGACY_SUPPORT + if (packet->context->version != TLS_V10) +#endif + { + tls_random(buf, TLS_AES_IV_LENGTH); + buf_pos += TLS_AES_IV_LENGTH; + } + // copy payload + memcpy(buf + buf_pos, packet->buf + header_size, packet->len - header_size); + buf_pos += packet->len - header_size; + if (packet->context->dtls) { + unsigned char temp_buf[5]; + memcpy(temp_buf, packet->buf, 3); + *(unsigned short *)(temp_buf + 3) = *(unsigned short *)&packet->buf[header_size - 2]; + uint64_t dtls_sequence_number = ntohll(*(uint64_t *)&packet->buf[3]); + _private_tls_hmac_message(1, packet->context, temp_buf, 5, packet->buf + header_size, packet->len - header_size, buf + buf_pos, mac_size, dtls_sequence_number); + } else + _private_tls_hmac_message(1, packet->context, packet->buf, packet->len, NULL, 0, buf + buf_pos, mac_size, 0); + buf_pos += mac_size; + + memset(buf + buf_pos, padding - 1, padding); + buf_pos += padding; + + //DEBUG_DUMP_HEX_LABEL("PT BUFFER", buf, length); + _private_tls_crypto_encrypt(packet->context, buf, ct + header_size, length); + TLS_FREE(packet->buf); + packet->buf = ct; + packet->len = length + header_size; + packet->size = packet->len; + } else { + // invalidate packet + memset(packet->buf, 0, packet->len); + } + TLS_FREE(buf); + } else { + // invalidate packet + memset(packet->buf, 0, packet->len); + } + } else +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (packet->context->crypto.created >= 2) { +#else + if (packet->context->crypto.created == 2) { +#endif + // + 1 = type + int ct_size = length + header_size + 12 + TLS_MAX_TAG_LEN + 1; + unsigned char *ct = (unsigned char *)TLS_MALLOC(ct_size); + if (ct) { + memset(ct, 0, ct_size); + // AEAD + // sequence number (8 bytes) + // content type (1 byte) + // version (2 bytes) + // length (2 bytes) + unsigned char aad[13]; + int aad_size = sizeof(aad); + unsigned char *sequence = aad; +#ifdef WITH_TLS_13 + if ((packet->context->version == TLS_V13) || (packet->context->version == DTLS_V13)) { + aad[0] = TLS_APPLICATION_DATA; + aad[1] = packet->buf[1]; + aad[2] = packet->buf[2]; +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (packet->context->crypto.created == 3) + *((unsigned short *)(aad + 3)) = htons(packet->len + POLY1305_TAGLEN - header_size); + else +#endif + *((unsigned short *)(aad + 3)) = htons(packet->len + TLS_GCM_TAG_LEN - header_size); + aad_size = 5; + sequence = aad + 5; + if (packet->context->dtls) + *((uint64_t *)sequence) = *(uint64_t *)&packet->buf[3]; + else + *((uint64_t *)sequence) = htonll(packet->context->local_sequence_number); + } else { +#endif + if (packet->context->dtls) + *((uint64_t *)aad) = *(uint64_t *)&packet->buf[3]; + else + *((uint64_t *)aad) = htonll(packet->context->local_sequence_number); + aad[8] = packet->buf[0]; + aad[9] = packet->buf[1]; + aad[10] = packet->buf[2]; + *((unsigned short *)(aad + 11)) = htons(packet->len - header_size); +#ifdef WITH_TLS_13 + } +#endif + int ct_pos = header_size; +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (packet->context->crypto.created == 3) { + unsigned int counter = 1; + unsigned char poly1305_key[POLY1305_KEYLEN]; + chacha_ivupdate(&packet->context->crypto.ctx_local.chacha_local, packet->context->crypto.ctx_local_mac.local_aead_iv, sequence, (u8 *)&counter); + chacha20_poly1305_key(&packet->context->crypto.ctx_local.chacha_local, poly1305_key); + ct_pos += chacha20_poly1305_aead(&packet->context->crypto.ctx_local.chacha_local, packet->buf + header_size, pt_length, aad, aad_size, poly1305_key, ct + ct_pos); + } else { +#endif + unsigned char iv[TLS_13_AES_GCM_IV_LENGTH]; +#ifdef WITH_TLS_13 + if ((packet->context->version == TLS_V13) || (packet->context->version == DTLS_V13)) { + memcpy(iv, packet->context->crypto.ctx_local_mac.local_iv, TLS_13_AES_GCM_IV_LENGTH); + int i; + int offset = TLS_13_AES_GCM_IV_LENGTH - 8; + for (i = 0; i < 8; i++) + iv[offset + i] = packet->context->crypto.ctx_local_mac.local_iv[offset + i] ^ sequence[i]; + } else { +#endif + memcpy(iv, packet->context->crypto.ctx_local_mac.local_aead_iv, TLS_AES_GCM_IV_LENGTH); + tls_random(iv + TLS_AES_GCM_IV_LENGTH, 8); + memcpy(ct + ct_pos, iv + TLS_AES_GCM_IV_LENGTH, 8); + ct_pos += 8; +#ifdef WITH_TLS_13 + } +#endif + + gcm_reset(&packet->context->crypto.ctx_local.aes_gcm_local); + gcm_add_iv(&packet->context->crypto.ctx_local.aes_gcm_local, iv, 12); + gcm_add_aad(&packet->context->crypto.ctx_local.aes_gcm_local, aad, aad_size); + gcm_process(&packet->context->crypto.ctx_local.aes_gcm_local, packet->buf + header_size, pt_length, ct + ct_pos, GCM_ENCRYPT); + ct_pos += pt_length; + + unsigned long taglen = TLS_GCM_TAG_LEN; + gcm_done(&packet->context->crypto.ctx_local.aes_gcm_local, ct + ct_pos, &taglen); + ct_pos += taglen; +#ifdef TLS_WITH_CHACHA20_POLY1305 + } +#endif +#ifdef WITH_TLS_13 + if ((packet->context->version == TLS_V13) || (packet->context->version == DTLS_V13)) { + ct[0] = TLS_APPLICATION_DATA; + *(unsigned short *)&ct[1] = htons(packet->context->version == TLS_V13 ? TLS_V12 : DTLS_V12); + // is dtls ? + if (header_size != 5) + memcpy(ct, packet->buf + 3, header_size - 2); + } else +#endif + memcpy(ct, packet->buf, header_size - 2); + *(unsigned short *)&ct[header_size - 2] = htons(ct_pos - header_size); + TLS_FREE(packet->buf); + packet->buf = ct; + packet->len = ct_pos; + packet->size = ct_pos; + } else { + // invalidate packet + memset(packet->buf, 0, packet->len); + } + } else { + // invalidate packet (never reached) + memset(packet->buf, 0, packet->len); + } + } + } else + packet->context->dtls_epoch_local++; + packet->context->local_sequence_number++; + } + } +} + +int tls_packet_append(struct TLSPacket *packet, const unsigned char *buf, unsigned int len) { + if ((!packet) || (packet->broken)) + return -1; + + if (!len) + return 0; + + unsigned int new_len = packet->len + len; + + if (new_len > packet->size) { + packet->size = (new_len / TLS_BLOB_INCREMENT + 1) * TLS_BLOB_INCREMENT; + packet->buf = (unsigned char *)TLS_REALLOC(packet->buf, packet->size); + if (!packet->buf) { + packet->size = 0; + packet->len = 0; + packet->broken = 1; + return -1; + } + } + memcpy(packet->buf + packet->len, buf, len); + packet->len = new_len; + return new_len; +} + +int tls_packet_uint8(struct TLSPacket *packet, unsigned char i) { + return tls_packet_append(packet, &i, 1); +} + +int tls_packet_uint16(struct TLSPacket *packet, unsigned short i) { + unsigned short ni = htons(i); + return tls_packet_append(packet, (unsigned char *)&ni, 2); +} + +int tls_packet_uint32(struct TLSPacket *packet, unsigned int i) { + unsigned int ni = htonl(i); + return tls_packet_append(packet, (unsigned char *)&ni, 4); +} + +int tls_packet_uint24(struct TLSPacket *packet, unsigned int i) { + unsigned char buf[3]; + buf[0] = i / 0x10000; + i %= 0x10000; + buf[1] = i / 0x100; + i %= 0x100; + buf[2] = i; + + return tls_packet_append(packet, buf, 3); +} + +int tls_random(unsigned char *key, int len) { +#ifdef TLS_USE_RANDOM_SOURCE + TLS_USE_RANDOM_SOURCE(key, len); +#else +#ifdef __APPLE__ + for (int i = 0; i < len; i++) { + unsigned int v = arc4random() % 0x100; + key[i] = (char)v; + } + return 1; +#else +#ifdef _WIN32 + HCRYPTPROV hProvider = 0; + if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { + if (CryptGenRandom(hProvider, len, (BYTE *)key)) { + CryptReleaseContext(hProvider, 0); + return 1; + } + CryptReleaseContext(hProvider, 0); + } +#else + FILE *fp = fopen("/dev/urandom", "r"); + if (fp) { + int key_len = fread(key, 1, len, fp); + fclose(fp); + if (key_len == len) + return 1; + } +#endif +#endif +#endif + return 0; +} + +TLSHash *_private_tls_ensure_hash(struct TLSContext *context) { + TLSHash *hash = context->handshake_hash; + if (!hash) { + hash = (TLSHash *)TLS_MALLOC(sizeof(TLSHash)); + if (hash) + memset(hash, 0, sizeof(TLSHash)); + context->handshake_hash = hash; + } + return hash; +} + +void _private_tls_destroy_hash(struct TLSContext *context) { + if (context) { + TLS_FREE(context->handshake_hash); + context->handshake_hash = NULL; + } +} + +void _private_tls_create_hash(struct TLSContext *context) { + if (!context) + return; + + TLSHash *hash = _private_tls_ensure_hash(context); + if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { + int hash_size = _private_tls_mac_length(context); + if (hash->created) { + unsigned char temp[TLS_MAX_SHA_SIZE]; + sha384_done(&hash->hash32, temp); + sha256_done(&hash->hash48, temp); + } + sha384_init(&hash->hash48); + sha256_init(&hash->hash32); + hash->created = 1; + } else { +#ifdef TLS_LEGACY_SUPPORT + // TLS_V11 + if (hash->created) { + unsigned char temp[TLS_V11_HASH_SIZE]; + md5_done(&hash->hash32, temp); + sha1_done(&hash->hash2, temp); + } + md5_init(&hash->hash32); + sha1_init(&hash->hash2); + hash->created = 1; +#endif + } +} + +int _private_tls_update_hash(struct TLSContext *context, const unsigned char *in, unsigned int len) { + if (!context) + return 0; + TLSHash *hash = _private_tls_ensure_hash(context); + if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { + if (!hash->created) { + _private_tls_create_hash(context); +#ifdef TLS_LEGACY_SUPPORT + // cache first hello in case of protocol downgrade + if ((!context->is_server) && (!context->cached_handshake) && (!context->request_client_certificate) && (len)) { + context->cached_handshake = (unsigned char *)TLS_MALLOC(len); + if (context->cached_handshake) { + memcpy(context->cached_handshake, in, len); + context->cached_handshake_len = len; + } + } +#endif + } + int hash_size = _private_tls_mac_length(context); + sha256_process(&hash->hash32, in, len); + sha384_process(&hash->hash48, in, len); + if (!hash_size) + hash_size = TLS_SHA256_MAC_SIZE; + } else { +#ifdef TLS_LEGACY_SUPPORT + if (!hash->created) + _private_tls_create_hash(context); + md5_process(&hash->hash32, in, len); + sha1_process(&hash->hash2, in, len); +#endif + } + if ((context->request_client_certificate) && (len)) { + // cache all messages for verification + int new_len = context->cached_handshake_len + len; + context->cached_handshake = (unsigned char *)TLS_REALLOC(context->cached_handshake, new_len); + if (context->cached_handshake) { + memcpy(context->cached_handshake + context->cached_handshake_len, in, len); + context->cached_handshake_len = new_len; + } else + context->cached_handshake_len = 0; + } + return 0; +} + +#ifdef TLS_LEGACY_SUPPORT +int _private_tls_change_hash_type(struct TLSContext *context) { + if (!context) + return 0; + TLSHash *hash = _private_tls_ensure_hash(context); + if ((hash) && (hash->created) && (context->cached_handshake) && (context->cached_handshake_len)) { + _private_tls_destroy_hash(context); + int res = _private_tls_update_hash(context, context->cached_handshake, context->cached_handshake_len); + TLS_FREE(context->cached_handshake); + context->cached_handshake = NULL; + context->cached_handshake_len = 0; + return res; + } + return 0; +} +#endif + +int _private_tls_done_hash(struct TLSContext *context, unsigned char *hout) { + if (!context) + return 0; + + TLSHash *hash = _private_tls_ensure_hash(context); + if (!hash->created) + return 0; + + int hash_size = 0; + if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { + unsigned char temp[TLS_MAX_SHA_SIZE]; + if (!hout) + hout = temp; + //TLS_HASH_DONE(&hash->hash, hout); + hash_size = _private_tls_mac_length(context); + if (hash_size == TLS_SHA384_MAC_SIZE) { + sha256_done(&hash->hash32, temp); + sha384_done(&hash->hash48, hout); + } else { + sha256_done(&hash->hash32, hout); + sha384_done(&hash->hash48, temp); + hash_size = TLS_SHA256_MAC_SIZE; + } + } else { +#ifdef TLS_LEGACY_SUPPORT + // TLS_V11 + unsigned char temp[TLS_V11_HASH_SIZE]; + if (!hout) + hout = temp; + md5_done(&hash->hash32, hout); + sha1_done(&hash->hash2, hout + 16); + hash_size = TLS_V11_HASH_SIZE; +#endif + } + hash->created = 0; + if (context->cached_handshake) { + // not needed anymore + TLS_FREE(context->cached_handshake); + context->cached_handshake = NULL; + context->cached_handshake_len = 0; + } + return hash_size; +} + +int _private_tls_get_hash_idx(struct TLSContext *context) { + if (!context) + return -1; + switch (_private_tls_mac_length(context)) { + case TLS_SHA256_MAC_SIZE: + return find_hash("sha256"); + case TLS_SHA384_MAC_SIZE: + return find_hash("sha384"); + case TLS_SHA1_MAC_SIZE: + return find_hash("sha1"); + } + return -1; +} + +int _private_tls_get_hash(struct TLSContext *context, unsigned char *hout) { + if (!context) + return 0; + + TLSHash *hash = _private_tls_ensure_hash(context); + if (!hash->created) + return 0; + + int hash_size = 0; + if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { + hash_size = _private_tls_mac_length(context); + hash_state prec; + if (hash_size == TLS_SHA384_MAC_SIZE) { + memcpy(&prec, &hash->hash48, sizeof(hash_state)); + sha384_done(&hash->hash48, hout); + memcpy(&hash->hash48, &prec, sizeof(hash_state)); + } else { + memcpy(&prec, &hash->hash32, sizeof(hash_state)); + hash_size = TLS_SHA256_MAC_SIZE; + sha256_done(&hash->hash32, hout); + memcpy(&hash->hash32, &prec, sizeof(hash_state)); + } + } else { +#ifdef TLS_LEGACY_SUPPORT + // TLS_V11 + hash_state prec; + + memcpy(&prec, &hash->hash32, sizeof(hash_state)); + md5_done(&hash->hash32, hout); + memcpy(&hash->hash32, &prec, sizeof(hash_state)); + + memcpy(&prec, &hash->hash2, sizeof(hash_state)); + sha1_done(&hash->hash2, hout + 16); + memcpy(&hash->hash2, &prec, sizeof(hash_state)); + + hash_size = TLS_V11_HASH_SIZE; +#endif + } + return hash_size; +} + +int _private_tls_write_packet(struct TLSPacket *packet) { + if (!packet) + return -1; + struct TLSContext *context = packet->context; + if (!context) + return -1; + + if (context->tls_buffer) { + int len = context->tls_buffer_len + packet->len; + context->tls_buffer = (unsigned char *)TLS_REALLOC(context->tls_buffer, len); + if (!context->tls_buffer) { + context->tls_buffer_len = 0; + return -1; + } + memcpy(context->tls_buffer + context->tls_buffer_len, packet->buf, packet->len); + context->tls_buffer_len = len; + int written = packet->len; + tls_destroy_packet(packet); + return written; + } + context->tls_buffer_len = packet->len; + context->tls_buffer = packet->buf; + packet->buf = NULL; + packet->len = 0; + packet->size = 0; + tls_destroy_packet(packet); + return context->tls_buffer_len; +} + +int _private_tls_write_app_data(struct TLSContext *context, const unsigned char *buf, unsigned int buf_len) { + if (!context) + return -1; + if ((!buf) || (!buf_len)) + return 0; + + int len = context->application_buffer_len + buf_len; + context->application_buffer = (unsigned char *)TLS_REALLOC(context->application_buffer, len); + if (!context->application_buffer) { + context->application_buffer_len = 0; + return -1; + } + memcpy(context->application_buffer + context->application_buffer_len, buf, buf_len); + context->application_buffer_len = len; + return buf_len; +} + +const unsigned char *tls_get_write_buffer(struct TLSContext *context, unsigned int *outlen) { + if (!outlen) + return NULL; + if (!context) { + *outlen = 0; + return NULL; + } + // check if any error + if (context->sleep_until) { + if (context->sleep_until < time(NULL)) { + *outlen = 0; + return NULL; + } + context->sleep_until = 0; + } + *outlen = context->tls_buffer_len; + return context->tls_buffer; +} + +const unsigned char *tls_get_message(struct TLSContext *context, unsigned int *outlen, unsigned int offset) { + if (!outlen) + return NULL; + if ((!context) || (!context->tls_buffer)) { + *outlen = 0; + return NULL; + } + + if (offset >= context->tls_buffer_len) { + *outlen = 0; + return NULL; + } + // check if any error + if (context->sleep_until) { + if (context->sleep_until < time(NULL)) { + *outlen = 0; + return NULL; + } + context->sleep_until = 0; + } + unsigned char *tls_buffer = &context->tls_buffer[offset]; + unsigned int tls_buffer_len = context->tls_buffer_len - offset; + unsigned int len = 0; + if (context->dtls) { + if (tls_buffer_len < 13) { + *outlen = 0; + return NULL; + } + + len = ntohs(*(unsigned short *)&tls_buffer[11]) + 13; + } else { + if (tls_buffer_len < 5) { + *outlen = 0; + return NULL; + } + len = ntohs(*(unsigned short *)&tls_buffer[3]) + 5; + } + if (len > tls_buffer_len) { + *outlen = 0; + return NULL; + } + + *outlen = len; + return tls_buffer; +} + +void tls_buffer_clear(struct TLSContext *context) { + if ((context) && (context->tls_buffer)) { + TLS_FREE(context->tls_buffer); + context->tls_buffer = NULL; + context->tls_buffer_len = 0; + } +} + +int tls_established(struct TLSContext *context) { + if (context) { + if (context->critical_error) + return -1; + + if (context->connection_status == 0xFF) + return 1; + +#ifdef TLS_12_FALSE_START + // allow false start + if ((!context->is_server) && (context->version == TLS_V12) && (context->false_start)) + return 1; +#endif + } + return 0; +} + +void tls_read_clear(struct TLSContext *context) { + if ((context) && (context->application_buffer)) { + TLS_FREE(context->application_buffer); + context->application_buffer = NULL; + context->application_buffer_len = 0; + } +} + +int tls_read(struct TLSContext *context, unsigned char *buf, unsigned int size) { + if (!context) + return -1; + if ((context->application_buffer) && (context->application_buffer_len)) { + if (context->application_buffer_len < size) + size = context->application_buffer_len; + + memcpy(buf, context->application_buffer, size); + if (context->application_buffer_len == size) { + TLS_FREE(context->application_buffer); + context->application_buffer = NULL; + context->application_buffer_len = 0; + return size; + } + context->application_buffer_len -= size; + memmove(context->application_buffer, context->application_buffer + size, context->application_buffer_len); + return size; + } + return 0; +} + +struct TLSContext *tls_create_context(unsigned char is_server, unsigned short version) { + struct TLSContext *context = (struct TLSContext *)TLS_MALLOC(sizeof(struct TLSContext)); + if (context) { + memset(context, 0, sizeof(struct TLSContext)); + context->is_server = is_server; + if ((version == DTLS_V13) || (version == DTLS_V12) || (version == DTLS_V10)) + context->dtls = 1; + context->version = version; + } + return context; +} + +#ifdef TLS_FORWARD_SECRECY +const struct ECCCurveParameters *tls_set_curve(struct TLSContext *context, const struct ECCCurveParameters *curve) { + if (!context->is_server) + return NULL; + const struct ECCCurveParameters *old_curve = context->curve; + context->curve = curve; + return old_curve; +} +#endif + +struct TLSContext *tls_accept(struct TLSContext *context) { + if ((!context) || (!context->is_server)) + return NULL; + + struct TLSContext *child = (struct TLSContext *)TLS_MALLOC(sizeof(struct TLSContext)); + if (child) { + memset(child, 0, sizeof(struct TLSContext)); + child->is_server = 1; + child->is_child = 1; + child->dtls = context->dtls; + child->version = context->version; + child->certificates = context->certificates; + child->certificates_count = context->certificates_count; + child->private_key = context->private_key; +#ifdef TLS_ECDSA_SUPPORTED + child->ec_private_key = context->ec_private_key; +#endif + child->exportable = context->exportable; + child->root_certificates = context->root_certificates; + child->root_count = context->root_count; +#ifdef TLS_FORWARD_SECRECY + child->default_dhe_p = context->default_dhe_p; + child->default_dhe_g = context->default_dhe_g; + child->curve = context->curve; +#endif + child->alpn = context->alpn; + child->alpn_count = context->alpn_count; + } + return child; +} + +#ifdef TLS_FORWARD_SECRECY +void _private_tls_dhe_free(struct TLSContext *context) { + if (context->dhe) { + _private_tls_dh_clear_key(context->dhe); + TLS_FREE(context->dhe); + context->dhe = NULL; + } +} + +void _private_tls_dhe_create(struct TLSContext *context) { + _private_tls_dhe_free(context); + context->dhe = (DHKey *)TLS_MALLOC(sizeof(DHKey)); + if (context->dhe) + memset(context->dhe, 0, sizeof(DHKey)); +} + +void _private_tls_ecc_dhe_free(struct TLSContext *context) { + if (context->ecc_dhe) { + ecc_free(context->ecc_dhe); + TLS_FREE(context->ecc_dhe); + context->ecc_dhe = NULL; + } +} + +void _private_tls_ecc_dhe_create(struct TLSContext *context) { + _private_tls_ecc_dhe_free(context); + context->ecc_dhe = (ecc_key *)TLS_MALLOC(sizeof(ecc_key)); + memset(context->ecc_dhe, 0, sizeof(ecc_key)); +} + +int tls_set_default_dhe_pg(struct TLSContext *context, const char *p_hex_str, const char *g_hex_str) { + if ((!context) || (context->is_child) || (!context->is_server) || (!p_hex_str) || (!g_hex_str)) + return 0; + + TLS_FREE(context->default_dhe_p); + TLS_FREE(context->default_dhe_g); + + context->default_dhe_p = NULL; + context->default_dhe_g = NULL; + + size_t p_len = strlen(p_hex_str); + size_t g_len = strlen(g_hex_str); + if ((p_len <= 0) || (g_len <= 0)) + return 0; + context->default_dhe_p = (char *)TLS_MALLOC(p_len + 1); + if (!context->default_dhe_p) + return 0; + context->default_dhe_g = (char *)TLS_MALLOC(g_len + 1); + if (!context->default_dhe_g) + return 0; + + memcpy(context->default_dhe_p, p_hex_str, p_len); + context->default_dhe_p[p_len] = 0; + + memcpy(context->default_dhe_g, g_hex_str, g_len); + context->default_dhe_g[g_len] = 0; + return 1; +} +#endif + +const char *tls_alpn(struct TLSContext *context) { + if (!context) + return NULL; + return context->negotiated_alpn; +} + +int tls_add_alpn(struct TLSContext *context, const char *alpn) { + if ((!context) || (!alpn) || (!alpn[0]) || ((context->is_server) && (context->is_child))) + return TLS_GENERIC_ERROR; + int len = strlen(alpn); + if (tls_alpn_contains(context, alpn, len)) + return 0; + context->alpn = (char **)TLS_REALLOC(context->alpn, (context->alpn_count + 1) * sizeof(char *)); + if (!context->alpn) { + context->alpn_count = 0; + return TLS_NO_MEMORY; + } + char *alpn_ref = (char *)TLS_MALLOC(len+1); + context->alpn[context->alpn_count] = alpn_ref; + if (alpn_ref) { + memcpy(alpn_ref, alpn, len); + alpn_ref[len] = 0; + context->alpn_count++; + } else + return TLS_NO_MEMORY; + return 0; +} + +int tls_alpn_contains(struct TLSContext *context, const char *alpn, unsigned char alpn_size) { + if ((!context) || (!alpn) || (!alpn_size)) + return 0; + + if (context->alpn) { + int i; + for (i = 0; i < context->alpn_count; i++) { + const char *alpn_local = context->alpn[i]; + if (alpn_local) { + int len = strlen(alpn_local); + if (alpn_size == len) { + if (!memcmp(alpn_local, alpn, alpn_size)) + return 1; + } + } + } + } + return 0; +} + +void tls_destroy_context(struct TLSContext *context) { + unsigned int i; + if (!context) + return; + if (!context->is_child) { + if (context->certificates) { + for (i = 0; i < context->certificates_count; i++) + tls_destroy_certificate(context->certificates[i]); + } + if (context->root_certificates) { + for (i = 0; i < context->root_count; i++) + tls_destroy_certificate(context->root_certificates[i]); + TLS_FREE(context->root_certificates); + context->root_certificates = NULL; + } + if (context->private_key) + tls_destroy_certificate(context->private_key); +#ifdef TLS_ECDSA_SUPPORTED + if (context->ec_private_key) + tls_destroy_certificate(context->ec_private_key); +#endif + TLS_FREE(context->certificates); +#ifdef TLS_FORWARD_SECRECY + TLS_FREE(context->default_dhe_p); + TLS_FREE(context->default_dhe_g); +#endif + if (context->alpn) { + for (i = 0; i < context->alpn_count; i++) + TLS_FREE(context->alpn[i]); + TLS_FREE(context->alpn); + } + } + if (context->client_certificates) { + for (i = 0; i < context->client_certificates_count; i++) + tls_destroy_certificate(context->client_certificates[i]); + TLS_FREE(context->client_certificates); + } + context->client_certificates = NULL; + TLS_FREE(context->master_key); + TLS_FREE(context->premaster_key); + if (context->crypto.created) + _private_tls_crypto_done(context); + TLS_FREE(context->message_buffer); + _private_tls_done_hash(context, NULL); + _private_tls_destroy_hash(context); + TLS_FREE(context->tls_buffer); + TLS_FREE(context->application_buffer); + // zero out the keys before free + if ((context->exportable_keys) && (context->exportable_size)) + memset(context->exportable_keys, 0, context->exportable_size); + TLS_FREE(context->exportable_keys); + TLS_FREE(context->sni); + TLS_FREE(context->dtls_cookie); + TLS_FREE(context->cached_handshake); +#ifdef TLS_FORWARD_SECRECY + _private_tls_dhe_free(context); + _private_tls_ecc_dhe_free(context); +#endif +#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION + TLS_FREE(context->verify_data); +#endif + TLS_FREE(context->negotiated_alpn); +#ifdef WITH_TLS_13 + TLS_FREE(context->finished_key); + TLS_FREE(context->remote_finished_key); + TLS_FREE(context->server_finished_hash); +#endif +#ifdef TLS_CURVE25519 + TLS_FREE(context->client_secret); +#endif + TLS_FREE(context); +} + +#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION +void _private_tls_reset_context(struct TLSContext *context) { + unsigned int i; + if (!context) + return; + if (!context->is_child) { + if (context->certificates) { + for (i = 0; i < context->certificates_count; i++) + tls_destroy_certificate(context->certificates[i]); + } + context->certificates = NULL; + if (context->private_key) { + tls_destroy_certificate(context->private_key); + context->private_key = NULL; + } +#ifdef TLS_ECDSA_SUPPORTED + if (context->ec_private_key) { + tls_destroy_certificate(context->ec_private_key); + context->ec_private_key = NULL; + } +#endif + TLS_FREE(context->certificates); + context->certificates = NULL; +#ifdef TLS_FORWARD_SECRECY + TLS_FREE(context->default_dhe_p); + TLS_FREE(context->default_dhe_g); + context->default_dhe_p = NULL; + context->default_dhe_g = NULL; +#endif + } + if (context->client_certificates) { + for (i = 0; i < context->client_certificates_count; i++) + tls_destroy_certificate(context->client_certificates[i]); + TLS_FREE(context->client_certificates); + } + context->client_certificates = NULL; + TLS_FREE(context->master_key); + context->master_key = NULL; + TLS_FREE(context->premaster_key); + context->premaster_key = NULL; + if (context->crypto.created) + _private_tls_crypto_done(context); + _private_tls_done_hash(context, NULL); + _private_tls_destroy_hash(context); + TLS_FREE(context->application_buffer); + context->application_buffer = NULL; + // zero out the keys before free + if ((context->exportable_keys) && (context->exportable_size)) + memset(context->exportable_keys, 0, context->exportable_size); + TLS_FREE(context->exportable_keys); + context->exportable_keys = NULL; + TLS_FREE(context->sni); + context->sni = NULL; + TLS_FREE(context->dtls_cookie); + context->dtls_cookie = NULL; + TLS_FREE(context->cached_handshake); + context->cached_handshake = NULL; + context->connection_status = 0; +#ifdef TLS_FORWARD_SECRECY + _private_tls_dhe_free(context); + _private_tls_ecc_dhe_free(context); +#endif +} +#endif + +int tls_cipher_supported(struct TLSContext *context, unsigned short cipher) { + if (!context) + return 0; + + switch (cipher) { +#ifdef WITH_TLS_13 + case TLS_AES_128_GCM_SHA256: + case TLS_AES_256_GCM_SHA384: + case TLS_CHACHA20_POLY1305_SHA256: + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + return 1; + return 0; +#endif +#ifdef TLS_FORWARD_SECRECY +#ifdef TLS_ECDSA_SUPPORTED + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: +#ifdef TLS_CLIENT_ECDSA + if ((context) && (((context->certificates) && (context->certificates_count) && (context->ec_private_key)) || (!context->is_server))) +#else + if ((context) && (context->certificates) && (context->certificates_count) && (context->ec_private_key)) +#endif + return 1; + return 0; + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: +#ifdef TLS_WITH_CHACHA20_POLY1305 + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: +#endif + if ((context->version == TLS_V12) || (context->version == DTLS_V12)) { +#ifdef TLS_CLIENT_ECDSA + if ((context) && (((context->certificates) && (context->certificates_count) && (context->ec_private_key)) || (!context->is_server))) +#else + if ((context) && (context->certificates) && (context->certificates_count) && (context->ec_private_key)) +#endif + return 1; + } + return 0; +#endif + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: +#endif + case TLS_RSA_WITH_AES_128_CBC_SHA: + case TLS_RSA_WITH_AES_256_CBC_SHA: + return 1; +#ifdef TLS_FORWARD_SECRECY + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: +#ifdef TLS_WITH_CHACHA20_POLY1305 + case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: +#endif +#endif + case TLS_RSA_WITH_AES_128_GCM_SHA256: + case TLS_RSA_WITH_AES_128_CBC_SHA256: + case TLS_RSA_WITH_AES_256_CBC_SHA256: + case TLS_RSA_WITH_AES_256_GCM_SHA384: + if ((context->version == TLS_V12) || (context->version == DTLS_V12)) + return 1; + return 0; + } + return 0; +} + +int tls_cipher_is_fs(struct TLSContext *context, unsigned short cipher) { + if (!context) + return 0; +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + switch (cipher) { + case TLS_AES_128_GCM_SHA256: + case TLS_AES_256_GCM_SHA384: + case TLS_CHACHA20_POLY1305_SHA256: + return 1; + } + return 0; + } +#endif + switch (cipher) { +#ifdef TLS_ECDSA_SUPPORTED + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: +#ifdef TLS_WITH_CHACHA20_POLY1305 + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: +#endif + if ((context) && (context->certificates) && (context->certificates_count) && (context->ec_private_key)) + return 1; + return 0; + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + if ((context->version == TLS_V12) || (context->version == DTLS_V12)) { + if ((context) && (context->certificates) && (context->certificates_count) && (context->ec_private_key)) + return 1; + } + return 0; +#endif + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + return 1; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: +#ifdef TLS_WITH_CHACHA20_POLY1305 + case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: +#endif + if ((context->version == TLS_V12) || (context->version == DTLS_V12)) + return 1; + break; + } + return 0; +} + +#ifdef WITH_KTLS +int _private_tls_prefer_ktls(struct TLSContext *context, unsigned short cipher) { + if ((context->version == TLS_V13) || (context->version == DTLS_V13) || ((context->version != TLS_V12) && (context->version != DTLS_V12))) + return 0; + + switch (cipher) { + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + if ((context->version == TLS_V13) || (context->version == DTLS_V13) || (context->version == TLS_V12) || (context->version == DTLS_V12)) { + if ((context->certificates) && (context->certificates_count) && (context->ec_private_key)) + return 1; + } + break; + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + return 1; + } + return 0; +} +#endif + +int tls_choose_cipher(struct TLSContext *context, const unsigned char *buf, int buf_len, int *scsv_set) { + int i; + if (scsv_set) + *scsv_set = 0; + if (!context) + return 0; + int selected_cipher = TLS_NO_COMMON_CIPHER; +#ifdef TLS_FORWARD_SECRECY +#ifdef WITH_KTLS + for (i = 0; i < buf_len; i+=2) { + unsigned short cipher = ntohs(*(unsigned short *)&buf[i]); + if (_private_tls_prefer_ktls(context, cipher)) { + selected_cipher = cipher; + break; + } + } +#endif + if (selected_cipher == TLS_NO_COMMON_CIPHER) { + for (i = 0; i < buf_len; i+=2) { + unsigned short cipher = ntohs(*(unsigned short *)&buf[i]); + if (tls_cipher_is_fs(context, cipher)) { + selected_cipher = cipher; + break; + } + } + } +#endif + for (i = 0; i < buf_len; i+=2) { + unsigned short cipher = ntohs(*(unsigned short *)&buf[i]); + if (cipher == TLS_FALLBACK_SCSV) { + if (scsv_set) + *scsv_set = 1; + if (selected_cipher != TLS_NO_COMMON_CIPHER) + break; + } +#ifndef TLS_ROBOT_MITIGATION + else + if ((selected_cipher == TLS_NO_COMMON_CIPHER) && (tls_cipher_supported(context, cipher))) + selected_cipher = cipher; +#endif + } + return selected_cipher; +} + +int tls_cipher_is_ephemeral(struct TLSContext *context) { + if (context) { + switch (context->cipher) { + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + return 1; + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + return 2; +#ifdef WITH_TLS_13 + case TLS_AES_128_GCM_SHA256: + case TLS_CHACHA20_POLY1305_SHA256: + case TLS_AES_128_CCM_SHA256: + case TLS_AES_128_CCM_8_SHA256: + case TLS_AES_256_GCM_SHA384: + if (context->dhe) + return 1; + return 2; +#endif + } + } + return 0; +} + +const char *tls_cipher_name(struct TLSContext *context) { + if (context) { + switch (context->cipher) { + case TLS_RSA_WITH_AES_128_CBC_SHA: + return "RSA-AES128CBC-SHA"; + case TLS_RSA_WITH_AES_256_CBC_SHA: + return "RSA-AES256CBC-SHA"; + case TLS_RSA_WITH_AES_128_CBC_SHA256: + return "RSA-AES128CBC-SHA256"; + case TLS_RSA_WITH_AES_256_CBC_SHA256: + return "RSA-AES256CBC-SHA256"; + case TLS_RSA_WITH_AES_128_GCM_SHA256: + return "RSA-AES128GCM-SHA256"; + case TLS_RSA_WITH_AES_256_GCM_SHA384: + return "RSA-AES256GCM-SHA384"; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + return "DHE-RSA-AES128CBC-SHA"; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + return "DHE-RSA-AES256CBC-SHA"; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + return "DHE-RSA-AES128CBC-SHA256"; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + return "DHE-RSA-AES256CBC-SHA256"; + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + return "DHE-RSA-AES128GCM-SHA256"; + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + return "DHE-RSA-AES256GCM-SHA384"; + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + return "ECDHE-RSA-AES128CBC-SHA"; + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + return "ECDHE-RSA-AES256CBC-SHA"; + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + return "ECDHE-RSA-AES128CBC-SHA256"; + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + return "ECDHE-RSA-AES128GCM-SHA256"; + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + return "ECDHE-RSA-AES256GCM-SHA384"; + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + return "ECDHE-ECDSA-AES128CBC-SHA"; + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + return "ECDHE-ECDSA-AES256CBC-SHA"; + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + return "ECDHE-ECDSA-AES128CBC-SHA256"; + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + return "ECDHE-ECDSA-AES256CBC-SHA384"; + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + return "ECDHE-ECDSA-AES128GCM-SHA256"; + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + return "ECDHE-ECDSA-AES256GCM-SHA384"; + case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + return "ECDHE-RSA-CHACHA20-POLY1305-SHA256"; + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + return "ECDHE-ECDSA-CHACHA20-POLY1305-SHA256"; + case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + return "ECDHE-DHE-CHACHA20-POLY1305-SHA256"; + case TLS_AES_128_GCM_SHA256: + return "TLS-AES-128-GCM-SHA256"; + case TLS_AES_256_GCM_SHA384: + return "TLS-AES-256-GCM-SHA384"; + case TLS_CHACHA20_POLY1305_SHA256: + return "TLS-CHACHA20-POLY1305-SHA256"; + case TLS_AES_128_CCM_SHA256: + return "TLS-AES-128-CCM-SHA256"; + case TLS_AES_128_CCM_8_SHA256: + return "TLS-AES-128-CCM-8-SHA256"; + } + } + return "UNKNOWN"; +} + +#ifdef TLS_FORWARD_SECRECY +int _private_tls_dh_export_Y(unsigned char *Ybuf, unsigned long *Ylen, DHKey *key) { + unsigned long len; + + if ((Ybuf == NULL) || (Ylen == NULL) || (key == NULL)) + return TLS_GENERIC_ERROR; + + len = mp_unsigned_bin_size(key->y); + if (len > *Ylen) + return TLS_GENERIC_ERROR; + + *Ylen = len; + return 0; + } + +int _private_tls_dh_export_pqY(unsigned char *pbuf, unsigned long *plen, unsigned char *gbuf, unsigned long *glen, unsigned char *Ybuf, unsigned long *Ylen, DHKey *key) { + unsigned long len; + int err; + + if ((pbuf == NULL) || (plen == NULL) || (gbuf == NULL) || (glen == NULL) || (Ybuf == NULL) || (Ylen == NULL) || (key == NULL)) + return TLS_GENERIC_ERROR; + + len = mp_unsigned_bin_size(key->y); + if (len > *Ylen) + return TLS_GENERIC_ERROR; + + if ((err = mp_to_unsigned_bin(key->y, Ybuf)) != CRYPT_OK) + return err; + + *Ylen = len; + + len = mp_unsigned_bin_size(key->p); + if (len > *plen) + return TLS_GENERIC_ERROR; + + if ((err = mp_to_unsigned_bin(key->p, pbuf)) != CRYPT_OK) + return err; + + *plen = len; + + len = mp_unsigned_bin_size(key->g); + if (len > *glen) + return TLS_GENERIC_ERROR; + + if ((err = mp_to_unsigned_bin(key->g, gbuf)) != CRYPT_OK) + return err; + + *glen = len; + + return 0; +} + +void _private_tls_dh_clear_key(DHKey *key) { + mp_clear_multi(key->g, key->p, key->x, key->y, NULL); + key->g = NULL; + key->p = NULL; + key->x = NULL; + key->y = NULL; +} + +int _private_tls_dh_make_key(int keysize, DHKey *key, const char *pbuf, const char *gbuf, int pbuf_len, int gbuf_len) { + unsigned char *buf; + int err; + if (!key) + return TLS_GENERIC_ERROR; + + static prng_state prng; + int wprng = find_prng("sprng"); + if ((err = prng_is_valid(wprng)) != CRYPT_OK) + return err; + + buf = (unsigned char *)TLS_MALLOC(keysize); + if (!buf) + return TLS_NO_MEMORY; + + if (rng_make_prng(keysize, wprng, &prng, NULL) != CRYPT_OK) { + TLS_FREE(buf); + return TLS_GENERIC_ERROR; + } + + if (prng_descriptor[wprng].read(buf, keysize, &prng) != (unsigned long)keysize) { + TLS_FREE(buf); + return TLS_GENERIC_ERROR; + } + + if ((err = mp_init_multi(&key->g, &key->p, &key->x, &key->y, NULL)) != CRYPT_OK) { + TLS_FREE(buf); + + return TLS_GENERIC_ERROR; + } + + if (gbuf_len <= 0) { + if ((err = mp_read_radix(key->g, gbuf, 16)) != CRYPT_OK) { + TLS_FREE(buf); + _private_tls_dh_clear_key(key); + return TLS_GENERIC_ERROR; + } + } else { + if ((err = mp_read_unsigned_bin(key->g, (unsigned char *)gbuf, gbuf_len)) != CRYPT_OK) { + TLS_FREE(buf); + _private_tls_dh_clear_key(key); + return TLS_GENERIC_ERROR; + } + } + + if (pbuf_len <= 0) { + if ((err = mp_read_radix(key->p, pbuf, 16)) != CRYPT_OK) { + TLS_FREE(buf); + _private_tls_dh_clear_key(key); + return TLS_GENERIC_ERROR; + } + } else { + if ((err = mp_read_unsigned_bin(key->p, (unsigned char *)pbuf, pbuf_len)) != CRYPT_OK) { + TLS_FREE(buf); + _private_tls_dh_clear_key(key); + return TLS_GENERIC_ERROR; + } + } + + if ((err = mp_read_unsigned_bin(key->x, buf, keysize)) != CRYPT_OK) { + TLS_FREE(buf); + _private_tls_dh_clear_key(key); + return TLS_GENERIC_ERROR; + } + + if ((err = mp_exptmod(key->g, key->x, key->p, key->y)) != CRYPT_OK) { + TLS_FREE(buf); + _private_tls_dh_clear_key(key); + return TLS_GENERIC_ERROR; + } + + TLS_FREE(buf); + return 0; +} +#endif + +int tls_is_ecdsa(struct TLSContext *context) { + if (!context) + return 0; + switch (context->cipher) { + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: +#ifdef TLS_WITH_CHACHA20_POLY1305 + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: +#endif + return 1; + } +#ifdef WITH_TLS_13 + if (context->ec_private_key) + return 1; +#endif + return 0; +} + +struct TLSPacket *tls_build_client_key_exchange(struct TLSContext *context) { + if (context->is_server) { + DEBUG_PRINT("CANNOT BUILD CLIENT KEY EXCHANGE MESSAGE FOR SERVERS\n"); + return NULL; + } + + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0); + tls_packet_uint8(packet, 0x10); +#ifdef TLS_FORWARD_SECRECY + int ephemeral = tls_cipher_is_ephemeral(context); + if ((ephemeral) && (context->premaster_key) && (context->premaster_key_len)) { + if (ephemeral == 1) { + unsigned char dh_Ys[0xFFF]; + unsigned char dh_p[0xFFF]; + unsigned char dh_g[0xFFF]; + unsigned long dh_p_len = sizeof(dh_p); + unsigned long dh_g_len = sizeof(dh_g); + unsigned long dh_Ys_len = sizeof(dh_Ys); + + if (_private_tls_dh_export_pqY(dh_p, &dh_p_len, dh_g, &dh_g_len, dh_Ys, &dh_Ys_len, context->dhe)) { + DEBUG_PRINT("ERROR EXPORTING DHE KEY %p\n", context->dhe); + TLS_FREE(packet); + _private_tls_dhe_free(context); + return NULL; + } + _private_tls_dhe_free(context); + DEBUG_DUMP_HEX_LABEL("Yc", dh_Ys, dh_Ys_len); + tls_packet_uint24(packet, dh_Ys_len + 2); + if (context->dtls) + _private_dtls_handshake_data(context, packet, dh_Ys_len + 2); + tls_packet_uint16(packet, dh_Ys_len); + tls_packet_append(packet, dh_Ys, dh_Ys_len); + } else + if (context->ecc_dhe) { + unsigned char out[TLS_MAX_RSA_KEY]; + unsigned long out_len = TLS_MAX_RSA_KEY; + + if (ecc_ansi_x963_export(context->ecc_dhe, out, &out_len)) { + DEBUG_PRINT("Error exporting ECC key\n"); + TLS_FREE(packet); + return NULL; + } + _private_tls_ecc_dhe_free(context); + tls_packet_uint24(packet, out_len + 1); + if (context->dtls) { + _private_dtls_handshake_data(context, packet, out_len + 1); + context->dtls_seq++; + } + tls_packet_uint8(packet, out_len); + tls_packet_append(packet, out, out_len); + } +#ifdef TLS_CURVE25519 + else + if ((context->curve == &x25519) && (context->client_secret)) { + static const unsigned char basepoint[32] = {9}; + unsigned char shared_key[32]; + curve25519(shared_key, context->client_secret, basepoint); + tls_packet_uint24(packet, 32 + 1); + tls_packet_uint8(packet, 32); + tls_packet_append(packet, shared_key, 32); + TLS_FREE(context->client_secret); + context->client_secret = NULL; + } +#endif + _private_tls_compute_key(context, 48); + } else +#endif + _private_tls_build_random(packet); + context->connection_status = 2; + tls_packet_update(packet); + return packet; +} + +void _private_dtls_handshake_data(struct TLSContext *context, struct TLSPacket *packet, unsigned int framelength) { + // message seq + tls_packet_uint16(packet, context->dtls_seq); + // fragment offset + tls_packet_uint24(packet, 0); + // fragment length + tls_packet_uint24(packet, framelength); +} + +void _private_dtls_handshake_copyframesize(struct TLSPacket *packet) { + packet->buf[22] = packet->buf[14]; + packet->buf[23] = packet->buf[15]; + packet->buf[24] = packet->buf[16]; +} + +struct TLSPacket *tls_build_server_key_exchange(struct TLSContext *context, int method) { + if (!context->is_server) { + DEBUG_PRINT("CANNOT BUILD SERVER KEY EXCHANGE MESSAGE FOR CLIENTS\n"); + return NULL; + } + + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0); + tls_packet_uint8(packet, 0x0C); + unsigned char dummy[3]; + tls_packet_append(packet, dummy, 3); + if (context->dtls) + _private_dtls_handshake_data(context, packet, 0); + int start_len = packet->len; +#ifdef TLS_FORWARD_SECRECY + if (method == KEA_dhe_rsa) { + tls_init(); + _private_tls_dhe_create(context); + + const char *default_dhe_p = context->default_dhe_p; + const char *default_dhe_g = context->default_dhe_g; + int key_size; + if ((!default_dhe_p) || (!default_dhe_g)) { + default_dhe_p = TLS_DH_DEFAULT_P; + default_dhe_g = TLS_DH_DEFAULT_G; + key_size = TLS_DHE_KEY_SIZE / 8; + } else { + key_size = strlen(default_dhe_p); + } + if (_private_tls_dh_make_key(key_size, context->dhe, default_dhe_p, default_dhe_g, 0, 0)) { + DEBUG_PRINT("ERROR CREATING DHE KEY\n"); + TLS_FREE(packet); + TLS_FREE(context->dhe); + context->dhe = NULL; + return NULL; + } + + unsigned char dh_Ys[0xFFF]; + unsigned char dh_p[0xFFF]; + unsigned char dh_g[0xFFF]; + unsigned long dh_p_len = sizeof(dh_p); + unsigned long dh_g_len = sizeof(dh_g); + unsigned long dh_Ys_len = sizeof(dh_Ys); + + if (_private_tls_dh_export_pqY(dh_p, &dh_p_len, dh_g, &dh_g_len, dh_Ys, &dh_Ys_len, context->dhe)) { + DEBUG_PRINT("ERROR EXPORTING DHE KEY\n"); + TLS_FREE(packet); + return NULL; + } + + DEBUG_PRINT("LEN: %lu (%lu, %lu)\n", dh_Ys_len, dh_p_len, dh_g_len); + DEBUG_DUMP_HEX_LABEL("DHE PK", dh_Ys, dh_Ys_len); + DEBUG_DUMP_HEX_LABEL("DHE P", dh_p, dh_p_len); + DEBUG_DUMP_HEX_LABEL("DHE G", dh_g, dh_g_len); + + tls_packet_uint16(packet, dh_p_len); + tls_packet_append(packet, dh_p, dh_p_len); + + tls_packet_uint16(packet, dh_g_len); + tls_packet_append(packet, dh_g, dh_g_len); + + tls_packet_uint16(packet, dh_Ys_len); + tls_packet_append(packet, dh_Ys, dh_Ys_len); + //dh_p + //dh_g + //dh_Ys + } else + if (method == KEA_ec_diffie_hellman) { + // 3 = named curve + if (!context->curve) + context->curve = default_curve; + tls_packet_uint8(packet, 3); + tls_packet_uint16(packet, context->curve->iana); + tls_init(); + _private_tls_ecc_dhe_create(context); + + ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&context->curve->dp; + + if (ecc_make_key_ex(NULL, find_prng("sprng"), context->ecc_dhe, dp)) { + TLS_FREE(context->ecc_dhe); + context->ecc_dhe = NULL; + DEBUG_PRINT("Error generating ECC key\n"); + TLS_FREE(packet); + return NULL; + } + unsigned char out[TLS_MAX_RSA_KEY]; + unsigned long out_len = TLS_MAX_RSA_KEY; + if (ecc_ansi_x963_export(context->ecc_dhe, out, &out_len)) { + DEBUG_PRINT("Error exporting ECC key\n"); + TLS_FREE(packet); + return NULL; + } + tls_packet_uint8(packet, out_len); + tls_packet_append(packet, out, out_len); + } else +#endif + { + TLS_FREE(packet); + DEBUG_PRINT("Unsupported ephemeral method: %i\n", method); + return NULL; + } + + // signature + unsigned int params_len = packet->len - start_len; + unsigned int message_len = params_len + TLS_CLIENT_RANDOM_SIZE + TLS_SERVER_RANDOM_SIZE; + unsigned char *message = (unsigned char *)TLS_MALLOC(message_len); + if (message) { + unsigned char out[TLS_MAX_RSA_KEY]; + unsigned long out_len = TLS_MAX_RSA_KEY; + + int hash_algorithm; + if ((context->version != TLS_V13) && (context->version != DTLS_V13) && (context->version != TLS_V12) && (context->version != DTLS_V12)) { + hash_algorithm = _md5_sha1; + } else { + if ((context->version == TLS_V13) || (context->version == DTLS_V13) || (context->version == TLS_V12) || (context->version == DTLS_V12)) + hash_algorithm = sha256; + else + hash_algorithm = sha1; + +#ifdef TLS_ECDSA_SUPPORTED + if (tls_is_ecdsa(context)) { + if ((context->version == TLS_V13) || (context->version == DTLS_V13) || (context->version == TLS_V12) || (context->version == DTLS_V12)) + hash_algorithm = sha512; + tls_packet_uint8(packet, hash_algorithm); + tls_packet_uint8(packet, ecdsa); + } else +#endif + { + tls_packet_uint8(packet, hash_algorithm); + tls_packet_uint8(packet, rsa_sign); + } + } + + memcpy(message, context->remote_random, TLS_CLIENT_RANDOM_SIZE); + memcpy(message + TLS_CLIENT_RANDOM_SIZE, context->local_random, TLS_SERVER_RANDOM_SIZE); + memcpy(message + TLS_CLIENT_RANDOM_SIZE + TLS_SERVER_RANDOM_SIZE, packet->buf + start_len, params_len); +#ifdef TLS_ECDSA_SUPPORTED + if (tls_is_ecdsa(context)) { + if (_private_tls_sign_ecdsa(context, hash_algorithm, message, message_len, out, &out_len) == 1) { + DEBUG_PRINT("Signing OK! (ECDSA, length %lu)\n", out_len); + tls_packet_uint16(packet, out_len); + tls_packet_append(packet, out, out_len); + } + } else +#endif + if (_private_tls_sign_rsa(context, hash_algorithm, message, message_len, out, &out_len) == 1) { + DEBUG_PRINT("Signing OK! (length %lu)\n", out_len); + tls_packet_uint16(packet, out_len); + tls_packet_append(packet, out, out_len); + } + TLS_FREE(message); + } + if ((!packet->broken) && (packet->buf)) { + int remaining = packet->len - start_len; + int payload_pos = 6; + if (context->dtls) + payload_pos = 14; + packet->buf[payload_pos] = remaining / 0x10000; + remaining %= 0x10000; + packet->buf[payload_pos + 1] = remaining / 0x100; + remaining %= 0x100; + packet->buf[payload_pos + 2] = remaining; + if (context->dtls) { + _private_dtls_handshake_copyframesize(packet); + context->dtls_seq++; + } + } + tls_packet_update(packet); + return packet; +} + +void _private_tls_set_session_id(struct TLSContext *context) { + if (((context->version == TLS_V13) || (context->version == DTLS_V13)) && (context->session_size == TLS_MAX_SESSION_ID)) + return; + if (tls_random(context->session, TLS_MAX_SESSION_ID)) + context->session_size = TLS_MAX_SESSION_ID; + else + context->session_size = 0; +} + +struct TLSPacket *tls_build_hello(struct TLSContext *context, int tls13_downgrade) { + tls_init(); +#ifdef WITH_TLS_13 + if (context->connection_status == 4) { + static unsigned char sha256_helloretryrequest[] = {0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C}; + memcpy(context->local_random, sha256_helloretryrequest, 32); + unsigned char header[4] = {0xFE, 0, 0, 0}; + unsigned char hash[TLS_MAX_SHA_SIZE ]; + int hash_len = _private_tls_done_hash(context, hash); + header[3] = (unsigned char)hash_len; + _private_tls_update_hash(context, header, sizeof(header)); + _private_tls_update_hash(context, hash, hash_len); + } else + if ((!context->is_server) || ((context->version != TLS_V13) && (context->version != DTLS_V13))) +#endif + if (!tls_random(context->local_random, context->is_server ? TLS_SERVER_RANDOM_SIZE : TLS_CLIENT_RANDOM_SIZE)) + return NULL; + if (!context->is_server) + *(unsigned int *)context->local_random = htonl((unsigned int)time(NULL)); + + if ((context->is_server) && (tls13_downgrade)) { + if ((tls13_downgrade == TLS_V12) || (tls13_downgrade == DTLS_V12)) + memcpy(context->local_random + TLS_SERVER_RANDOM_SIZE - 8, "DOWNGRD\x01", 8); + else + memcpy(context->local_random + TLS_SERVER_RANDOM_SIZE - 8, "DOWNGRD\x00", 8); + } + unsigned short packet_version = context->version; + unsigned short version = context->version; +#ifdef WITH_TLS_13 + if (context->version == TLS_V13) + version = TLS_V12; + else + if (context->version == DTLS_V13) + version = DTLS_V12; +#endif + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, version, 0); + if (packet) { + // hello + if (context->is_server) + tls_packet_uint8(packet, 0x02); + else + tls_packet_uint8(packet, 0x01); + unsigned char dummy[3]; + tls_packet_append(packet, dummy, 3); + + if (context->dtls) + _private_dtls_handshake_data(context, packet, 0); + + int start_len = packet->len; + tls_packet_uint16(packet, version); + if (context->is_server) + tls_packet_append(packet, context->local_random, TLS_SERVER_RANDOM_SIZE); + else + tls_packet_append(packet, context->local_random, TLS_CLIENT_RANDOM_SIZE); + +#ifdef IGNORE_SESSION_ID + // session size + tls_packet_uint8(packet, 0); +#else + _private_tls_set_session_id(context); + // session size + tls_packet_uint8(packet, context->session_size); + if (context->session_size) + tls_packet_append(packet, context->session, context->session_size); +#endif + + int extension_len = 0; + int alpn_len = 0; + int alpn_negotiated_len = 0; + int i; +#ifdef WITH_TLS_13 + unsigned char shared_key[TLS_MAX_RSA_KEY]; + unsigned long shared_key_len = TLS_MAX_RSA_KEY; + unsigned short shared_key_short = 0; + int selected_group = 0; + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + if (context->connection_status == 4) { + // connection_status == 4 => hello retry request + extension_len += 6; + } else + if (context->is_server) { +#ifdef TLS_CURVE25519 + if (context->curve == &x25519) { + extension_len += 8 + 32; + shared_key_short = (unsigned short)32; + if (context->finished_key) { + memcpy(shared_key, context->finished_key, 32); + TLS_FREE(context->finished_key); + context->finished_key = NULL; + } + selected_group = context->curve->iana; + // make context->curve NULL (x25519 is a different implementation) + context->curve = NULL; + } else +#endif + if (context->ecc_dhe) { + if (ecc_ansi_x963_export(context->ecc_dhe, shared_key, &shared_key_len)) { + DEBUG_PRINT("Error exporting ECC DHE key\n"); + tls_destroy_packet(packet); + return tls_build_alert(context, 1, internal_error); + } + _private_tls_ecc_dhe_free(context); + extension_len += 8 + shared_key_len; + shared_key_short = (unsigned short)shared_key_len; + if (context->curve) + selected_group = context->curve->iana; + } else + if (context->dhe) { + selected_group = context->dhe->iana; + _private_tls_dh_export_Y(shared_key, &shared_key_len, context->dhe); + _private_tls_dhe_free(context); + extension_len += 8 + shared_key_len; + shared_key_short = (unsigned short)shared_key_len; + } + } + // supported versions + if (context->is_server) + extension_len += 6; + else + extension_len += 9; + } + if ((context->is_server) && (context->negotiated_alpn) && (context->version != TLS_V13) && (context->version != DTLS_V13)) { +#else + if ((context->is_server) && (context->negotiated_alpn)) { +#endif + alpn_negotiated_len = strlen(context->negotiated_alpn); + alpn_len = alpn_negotiated_len + 1; + extension_len += alpn_len + 6; + } else + if ((!context->is_server) && (context->alpn_count)) { + for (i = 0; i < context->alpn_count;i++) { + if (context->alpn[i]) { + int len = strlen(context->alpn[i]); + if (len) + alpn_len += len + 1; + } + } + if (alpn_len) + extension_len += alpn_len + 6; + } + + // ciphers + if (context->is_server) { + // fallback ... this should never happen + if (!context->cipher) + context->cipher = TLS_DHE_RSA_WITH_AES_128_CBC_SHA; + + tls_packet_uint16(packet, context->cipher); + // no compression + tls_packet_uint8(packet, 0); +#ifndef STRICT_TLS + if ((context->version == TLS_V13) || (context->version == DTLS_V13) || (context->version == TLS_V12) || (context->version == DTLS_V12)) { + // extensions size +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + tls_packet_uint16(packet, extension_len); + } else +#endif + { + tls_packet_uint16(packet, 5 + extension_len); + // secure renegotation + // advertise it, but refuse renegotiation + tls_packet_uint16(packet, 0xff01); +#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION + // a little defensive + if ((context->verify_len) && (!context->verify_data)) + context->verify_len = 0; + tls_packet_uint16(packet, context->verify_len + 1); + tls_packet_uint8(packet, context->verify_len); + if (context->verify_len) + tls_packet_append(packet, (unsigned char *)context->verify_data, context->verify_len); +#else + tls_packet_uint16(packet, 1); + tls_packet_uint8(packet, 0); +#endif + } + if (alpn_len) { + tls_packet_uint16(packet, 0x10); + tls_packet_uint16(packet, alpn_len + 2); + tls_packet_uint16(packet, alpn_len); + + tls_packet_uint8(packet, alpn_negotiated_len); + tls_packet_append(packet, (unsigned char *)context->negotiated_alpn, alpn_negotiated_len); + } + } +#endif + } else { + if (context->dtls) { + tls_packet_uint8(packet, context->dtls_cookie_len); + if (context->dtls_cookie_len) + tls_packet_append(packet, context->dtls_cookie, context->dtls_cookie_len); + } + +#ifndef STRICT_TLS +#ifdef WITH_TLS_13 +#ifdef TLS_FORWARD_SECRECY + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + #ifdef TLS_WITH_CHACHA20_POLY1305 + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(9, 0)); + tls_packet_uint16(packet, TLS_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_AES_256_GCM_SHA384); + tls_packet_uint16(packet, TLS_CHACHA20_POLY1305_SHA256); + #else + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(8, 0)); + tls_packet_uint16(packet, TLS_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_AES_256_GCM_SHA384); + #endif + #ifdef TLS_PREFER_CHACHA20 + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256); + #else + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256); + #endif + } else +#endif +#endif + if ((context->version == TLS_V12) || (context->version == DTLS_V12)) { +#endif +#ifdef TLS_FORWARD_SECRECY +#ifdef TLS_CLIENT_ECDHE +#ifdef TLS_WITH_CHACHA20_POLY1305 + #ifdef TLS_CLIENT_ECDSA + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(16, 5)); + #ifdef TLS_PREFER_CHACHA20 + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256); + #endif + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256); + #ifndef TLS_PREFER_CHACHA20 + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256); + #endif + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA); + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA); + #else + // sizeof ciphers (16 ciphers * 2 bytes) + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(11, 5)); + #endif +#else + #ifdef TLS_CLIENT_ECDSA + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(13, 5)); + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA); + tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA); + #else + // sizeof ciphers (14 ciphers * 2 bytes) + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(9, 5)); + #endif +#endif +#ifdef TLS_WITH_CHACHA20_POLY1305 + #ifdef TLS_PREFER_CHACHA20 + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256); + #endif +#endif + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA); + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA); + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256); +#ifdef TLS_WITH_CHACHA20_POLY1305 + #ifndef TLS_PREFER_CHACHA20 + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256); + #endif +#endif +#else +#ifdef TLS_WITH_CHACHA20_POLY1305 + // sizeof ciphers (11 ciphers * 2 bytes) + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(6, 5)); +#else + // sizeof ciphers (10 ciphers * 2 bytes) + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(5, 5)); +#endif +#endif + // not yet supported, because the first message sent (this one) + // is already hashed by the client with sha256 (sha384 not yet supported client-side) + // but is fully suported server-side + // tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_CBC_SHA); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_CBC_SHA); +#ifdef TLS_WITH_CHACHA20_POLY1305 + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256); +#endif +#else + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(0, 5)); +#endif + // tls_packet_uint16(packet, TLS_RSA_WITH_AES_256_GCM_SHA384); +#ifndef TLS_ROBOT_MITIGATION + tls_packet_uint16(packet, TLS_RSA_WITH_AES_128_GCM_SHA256); + tls_packet_uint16(packet, TLS_RSA_WITH_AES_256_CBC_SHA256); + tls_packet_uint16(packet, TLS_RSA_WITH_AES_128_CBC_SHA256); + tls_packet_uint16(packet, TLS_RSA_WITH_AES_256_CBC_SHA); + tls_packet_uint16(packet, TLS_RSA_WITH_AES_128_CBC_SHA); +#endif +#ifndef STRICT_TLS + } else { +#ifdef TLS_FORWARD_SECRECY +#ifdef TLS_CLIENT_ECDHE + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(5, 2)); + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA); + tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA); +#else + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(3, 2)); +#endif + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_CBC_SHA); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_CBC_SHA); + tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_CBC_SHA); +#else + tls_packet_uint16(packet, TLS_CIPHERS_SIZE(0, 2)); +#endif +#ifndef TLS_ROBOT_MITIGATION + tls_packet_uint16(packet, TLS_RSA_WITH_AES_256_CBC_SHA); + tls_packet_uint16(packet, TLS_RSA_WITH_AES_128_CBC_SHA); +#endif + } +#endif + // compression + tls_packet_uint8(packet, 1); + // no compression + tls_packet_uint8(packet, 0); + if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { + int sni_len = 0; + if (context->sni) + sni_len = strlen(context->sni); + +#ifdef TLS_CLIENT_ECDHE + extension_len += 12; +#endif + if (sni_len) + extension_len += sni_len + 9; +#ifdef WITH_TLS_13 + if ((!context->is_server) && ((context->version == TLS_V13) || (context->version == DTLS_V13))) { +#ifdef TLS_CURVE25519 + extension_len += 70; +#else + // secp256r1 produces 65 bytes export + extension_len += 103; +#endif + } +#endif + tls_packet_uint16(packet, extension_len); + + if (sni_len) { + // sni extension + tls_packet_uint16(packet, 0x00); + // sni extension len + tls_packet_uint16(packet, sni_len + 5); + // sni len + tls_packet_uint16(packet, sni_len + 3); + // sni type + tls_packet_uint8(packet, 0); + // sni host len + tls_packet_uint16(packet, sni_len); + tls_packet_append(packet, (unsigned char *)context->sni, sni_len); + } +#ifdef TLS_FORWARD_SECRECY +#ifdef TLS_CLIENT_ECDHE + // supported groups + tls_packet_uint16(packet, 0x0A); + tls_packet_uint16(packet, 8); + // 3 curves x 2 bytes + tls_packet_uint16(packet, 6); + tls_packet_uint16(packet, secp256r1.iana); + tls_packet_uint16(packet, secp384r1.iana); +#ifdef TLS_CURVE25519 + tls_packet_uint16(packet, x25519.iana); +#else + tls_packet_uint16(packet, secp224r1.iana); +#endif +#endif +#endif + if (alpn_len) { + tls_packet_uint16(packet, 0x10); + tls_packet_uint16(packet, alpn_len + 2); + tls_packet_uint16(packet, alpn_len); + + for (i = 0; i < context->alpn_count;i++) { + if (context->alpn[i]) { + int len = strlen(context->alpn[i]); + if (len) { + tls_packet_uint8(packet, len); + tls_packet_append(packet, (unsigned char *)context->alpn[i], len); + } + } + } + } + } + } +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + // supported versions + tls_packet_uint16(packet, 0x2B); + if (context->is_server) { + tls_packet_uint16(packet, 2); + if (context->version == TLS_V13) + tls_packet_uint16(packet, context->tls13_version ? context->tls13_version : TLS_V13); + else + tls_packet_uint16(packet, context->version); + } else { + tls_packet_uint16(packet, 5); + tls_packet_uint8(packet, 4); + tls_packet_uint16(packet, TLS_V13); + tls_packet_uint16(packet, 0x7F1C); + } + if (context->connection_status == 4) { + // fallback to the mandatory secp256r1 + tls_packet_uint16(packet, 0x33); + tls_packet_uint16(packet, 2); + tls_packet_uint16(packet, (unsigned short)secp256r1.iana); + } + if (((shared_key_short) && (selected_group)) || (!context->is_server)) { + // key share + tls_packet_uint16(packet, 0x33); + if (context->is_server) { + tls_packet_uint16(packet, shared_key_short + 4); + tls_packet_uint16(packet, (unsigned short)selected_group); + tls_packet_uint16(packet, shared_key_short); + tls_packet_append(packet, (unsigned char *)shared_key, shared_key_short); + } else { +#ifdef TLS_CURVE25519 + // make key + shared_key_short = 32; + tls_packet_uint16(packet, shared_key_short + 6); + tls_packet_uint16(packet, shared_key_short + 4); + + TLS_FREE(context->client_secret); + context->client_secret = (unsigned char *)TLS_MALLOC(32); + if (!context->client_secret) { + DEBUG_PRINT("ERROR IN TLS_MALLOC"); + TLS_FREE(packet); + return NULL; + + } + + static const unsigned char basepoint[32] = {9}; + + tls_random(context->client_secret, 32); + + context->client_secret[0] &= 248; + context->client_secret[31] &= 127; + context->client_secret[31] |= 64; + + curve25519(shared_key, context->client_secret, basepoint); + + tls_packet_uint16(packet, (unsigned short)x25519.iana); + tls_packet_uint16(packet, shared_key_short); + tls_packet_append(packet, (unsigned char *)shared_key, shared_key_short); +#else + // make key + shared_key_short = 65; + tls_packet_uint16(packet, shared_key_short + 6); + tls_packet_uint16(packet, shared_key_short + 4); + + _private_tls_ecc_dhe_create(context); + ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&secp256r1.dp; + + if (ecc_make_key_ex(NULL, find_prng("sprng"), context->ecc_dhe, dp)) { + TLS_FREE(context->ecc_dhe); + context->ecc_dhe = NULL; + DEBUG_PRINT("Error generating ECC key\n"); + TLS_FREE(packet); + return NULL; + } + unsigned char out[TLS_MAX_RSA_KEY]; + unsigned long out_len = shared_key_short; + if (ecc_ansi_x963_export(context->ecc_dhe, out, &out_len)) { + DEBUG_PRINT("Error exporting ECC key\n"); + TLS_FREE(packet); + return NULL; + } + + tls_packet_uint16(packet, (unsigned short)secp256r1.iana); + tls_packet_uint16(packet, out_len); + tls_packet_append(packet, (unsigned char *)out, shared_key_short); +#endif + } + } + if (!context->is_server) { + // signature algorithms + tls_packet_uint16(packet, 0x0D); + tls_packet_uint16(packet, 24); + tls_packet_uint16(packet, 22); + tls_packet_uint16(packet, 0x0403); + tls_packet_uint16(packet, 0x0503); + tls_packet_uint16(packet, 0x0603); + tls_packet_uint16(packet, 0x0804); + tls_packet_uint16(packet, 0x0805); + tls_packet_uint16(packet, 0x0806); + tls_packet_uint16(packet, 0x0401); + tls_packet_uint16(packet, 0x0501); + tls_packet_uint16(packet, 0x0601); + tls_packet_uint16(packet, 0x0203); + tls_packet_uint16(packet, 0x0201); + } + } +#endif + + if ((!packet->broken) && (packet->buf)) { + int remaining = packet->len - start_len; + int payload_pos = 6; + if (context->dtls) + payload_pos = 14; + packet->buf[payload_pos] = remaining / 0x10000; + remaining %= 0x10000; + packet->buf[payload_pos + 1] = remaining / 0x100; + remaining %= 0x100; + packet->buf[payload_pos + 2] = remaining; + if (context->dtls) { + _private_dtls_handshake_copyframesize(packet); + context->dtls_seq++; + } + } + tls_packet_update(packet); + } + return packet; +} + +struct TLSPacket *tls_certificate_request(struct TLSContext *context) { + if ((!context) || (!context->is_server)) + return NULL; + + unsigned short packet_version = context->version; + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, packet_version, 0); + if (packet) { + // certificate request + tls_packet_uint8(packet, 0x0D); + unsigned char dummy[3]; + tls_packet_append(packet, dummy, 3); + if (context->dtls) + _private_dtls_handshake_data(context, packet, 0); + int start_len = packet->len; +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + // certificate request context + tls_packet_uint8(packet, 0); + // extensions + tls_packet_uint16(packet, 18); + // signature algorithms + tls_packet_uint16(packet, 0x0D); + tls_packet_uint16(packet, 14); + tls_packet_uint16(packet, 12); + // rsa_pkcs1_sha256 + // tls_packet_uint16(packet, 0x0401); + // rsa_pkcs1_sha384 + // tls_packet_uint16(packet, 0x0501); + // rsa_pkcs1_sha512 + // tls_packet_uint16(packet, 0x0601); + + // ecdsa_secp256r1_sha256 + tls_packet_uint16(packet, 0x0403); + // ecdsa_secp384r1_sha384 + tls_packet_uint16(packet, 0x0503); + // ecdsa_secp521r1_sha512 + tls_packet_uint16(packet, 0x0604); + // rsa_pss_rsae_sha256 + tls_packet_uint16(packet, 0x0804); + // rsa_pss_rsae_sha384 + tls_packet_uint16(packet, 0x0805); + // rsa_pss_rsae_sha512 + tls_packet_uint16(packet, 0x0806); + } else +#endif + { + tls_packet_uint8(packet, 1); + tls_packet_uint8(packet, rsa_sign); + if ((context->version == TLS_V12) || (context->version == DTLS_V12)) { + // 10 pairs or 2 bytes + tls_packet_uint16(packet, 10); + tls_packet_uint8(packet, sha256); + tls_packet_uint8(packet, rsa); + tls_packet_uint8(packet, sha1); + tls_packet_uint8(packet, rsa); + tls_packet_uint8(packet, sha384); + tls_packet_uint8(packet, rsa); + tls_packet_uint8(packet, sha512); + tls_packet_uint8(packet, rsa); + tls_packet_uint8(packet, md5); + tls_packet_uint8(packet, rsa); + } + // no DistinguishedName yet + tls_packet_uint16(packet, 0); + } + if (!packet->broken) { + int remaining = packet->len - start_len; + int payload_pos = 6; + if (context->dtls) + payload_pos = 14; + packet->buf[payload_pos] = remaining / 0x10000; + remaining %= 0x10000; + packet->buf[payload_pos + 1] = remaining / 0x100; + remaining %= 0x100; + packet->buf[payload_pos + 2] = remaining; + + if (context->dtls) { + _private_dtls_handshake_copyframesize(packet); + context->dtls_seq++; + } + } + tls_packet_update(packet); + } + return packet; +} + +int _private_dtls_build_cookie(struct TLSContext *context) { + if ((!context->dtls_cookie) || (!context->dtls_cookie_len)) { + context->dtls_cookie = (unsigned char *)TLS_MALLOC(DTLS_COOKIE_SIZE); + if (!context->dtls_cookie) + return 0; + +#ifdef WITH_RANDOM_DLTS_COOKIE + if (!tls_random(context->dtls_cookie, DTLS_COOKIE_SIZE)) { + TLS_FREE(context->dtls_cookie); + context->dtls_cookie = NULL; + return 0; + } + context->dtls_cookie_len = DTLS_COOKIE_SIZE; +#else + hmac_state hmac; + hmac_init(&hmac, find_hash("sha256"), dtls_secret, sizeof(dtls_secret)); + hmac_process(&hmac, context->remote_random, TLS_CLIENT_RANDOM_SIZE); + + unsigned long out_size = DTLS_COOKIE_SIZE; + hmac_done(&hmac, context->dtls_cookie, &out_size); +#endif + } + return 1; +} + +struct TLSPacket *tls_build_verify_request(struct TLSContext *context) { + if ((!context->is_server) || (!context->dtls)) + return NULL; + + if ((!context->dtls_cookie) || (!context->dtls_cookie_len)) { + if (!_private_dtls_build_cookie(context)) + return NULL; + } + + unsigned short packet_version = context->version; + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, packet_version, 0); + if (packet) { + // verify request + tls_packet_uint8(packet, 0x03); + // 24-bit length + tls_packet_uint24(packet, context->dtls_cookie_len + 3); + // 16-bit message_sequence + tls_packet_uint16(packet, 0); + // 24-bit fragment_offset + tls_packet_uint24(packet, 0); + // 24-bit fragment_offset + tls_packet_uint24(packet, context->dtls_cookie_len + 3); + // server_version + tls_packet_uint16(packet, context->version); + tls_packet_uint8(packet, context->dtls_cookie_len); + tls_packet_append(packet, context->dtls_cookie, context->dtls_cookie_len); + tls_packet_update(packet); + } + return packet; +} + +int _private_dtls_check_packet(const unsigned char *buf, int buf_len) { + CHECK_SIZE(11, buf_len, TLS_NEED_MORE_DATA) + + unsigned int bytes_to_follow = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; + // not used: unsigned short message_seq = ntohs(*(unsigned short *)&buf[3]); + unsigned int fragment_offset = buf[5] * 0x10000 + buf[6] * 0x100 + buf[7]; + unsigned int fragment_length = buf[8] * 0x10000 + buf[9] * 0x100 + buf[10]; + + if ((fragment_offset) || (fragment_length != bytes_to_follow)) { + DEBUG_PRINT("FRAGMENTED PACKETS NOT SUPPORTED\n"); + return TLS_FEATURE_NOT_SUPPORTED; + } + return bytes_to_follow; +} + +void _private_dtls_reset(struct TLSContext *context) { + context->dtls_epoch_local = 0; + context->dtls_epoch_remote = 0; + context->dtls_seq = 0; + _private_tls_destroy_hash(context); + context->connection_status = 0; +} + +int tls_parse_verify_request(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets) { + *write_packets = 0; + if ((context->connection_status != 0) || (!context->dtls)) { + DEBUG_PRINT("UNEXPECTED VERIFY REQUEST MESSAGE\n"); + return TLS_UNEXPECTED_MESSAGE; + } + int res = 11; + int bytes_to_follow = _private_dtls_check_packet(buf, buf_len); + if (bytes_to_follow < 0) + return bytes_to_follow; + + CHECK_SIZE(bytes_to_follow, buf_len - res, TLS_NEED_MORE_DATA) + // not used: unsigned short version = ntohs(*(unsigned short *)&buf[res]); + res += 2; + unsigned char len = buf[res]; + res++; + TLS_FREE(context->dtls_cookie); + context->dtls_cookie_len = 0; + if (len) { + CHECK_SIZE(len, buf_len - res, TLS_NEED_MORE_DATA) + context->dtls_cookie = (unsigned char *)TLS_MALLOC(len); + if (!context->dtls_cookie) + return TLS_NO_MEMORY; + context->dtls_cookie_len = len; + memcpy(context->dtls_cookie, &buf[res], len); + res += len; + *write_packets = 4; + } + + // reset context + _private_dtls_reset(context); + return res; +} + +void _private_dtls_reset_cookie(struct TLSContext *context) { + TLS_FREE(context->dtls_cookie); + context->dtls_cookie = NULL; + context->dtls_cookie_len = 0; +} + +#ifdef WITH_TLS_13 +int _private_tls_parse_key_share(struct TLSContext *context, const unsigned char *buf, int buf_len) { + int i = 0; + struct ECCCurveParameters *curve = 0; + DHKey *dhkey = 0; + int dhe_key_size = 0; + const unsigned char *buffer = NULL; + unsigned char *out2; + unsigned long out_size; + unsigned short key_size = 0; + while (buf_len >= 4) { + unsigned short named_group = ntohs(*(unsigned short *)&buf[i]); + i += 2; + buf_len -= 2; + + key_size = ntohs(*(unsigned short *)&buf[i]); + i += 2; + buf_len -= 2; + + if (key_size > buf_len) + return TLS_BROKEN_PACKET; + + switch (named_group) { + case 0x0017: + curve = &secp256r1; + buffer = &buf[i]; + DEBUG_PRINT("KEY SHARE => secp256r1\n"); + buf_len = 0; + continue; + case 0x0018: + // secp384r1 + curve = &secp384r1; + buffer = &buf[i]; + DEBUG_PRINT("KEY SHARE => secp384r1\n"); + buf_len = 0; + continue; + case 0x0019: + // secp521r1 + break; + case 0x001D: + // x25519 +#ifdef TLS_CURVE25519 + if (key_size != 32) { + DEBUG_PRINT("INVALID x25519 KEY SIZE (%i)\n", key_size); + continue; + } + curve = &x25519; + buffer = &buf[i]; + DEBUG_PRINT("KEY SHARE => x25519\n"); + buf_len = 0; + continue; +#endif + break; + + case 0x001E: + // x448 + break; + case 0x0100: + dhkey = &ffdhe2048; + dhe_key_size = 2048; + break; + case 0x0101: + dhkey = &ffdhe3072; + dhe_key_size = 3072; + break; + case 0x0102: + dhkey = &ffdhe4096; + dhe_key_size = 4096; + break; + case 0x0103: + dhkey = &ffdhe6144; + dhe_key_size = 6144; + break; + case 0x0104: + dhkey = &ffdhe8192; + dhe_key_size = 8192; + break; + } + i += key_size; + buf_len -= key_size; + + if (!context->is_server) + break; + } + tls_init(); + if (curve) { + context->curve = curve; +#ifdef TLS_CURVE25519 + if (curve == &x25519) { + if ((context->is_server) && (!tls_random(context->local_random, TLS_SERVER_RANDOM_SIZE))) + return TLS_GENERIC_ERROR; + unsigned char secret[32]; + static const unsigned char basepoint[32] = {9}; + + if ((context->is_server) || (!context->client_secret)) { + tls_random(secret, 32); + + secret[0] &= 248; + secret[31] &= 127; + secret[31] |= 64; + + // use finished key to store public key + TLS_FREE(context->finished_key); + context->finished_key = (unsigned char *)TLS_MALLOC(32); + if (!context->finished_key) + return TLS_GENERIC_ERROR; + + curve25519(context->finished_key, secret, basepoint); + + TLS_FREE(context->premaster_key); + context->premaster_key = (unsigned char *)TLS_MALLOC(32); + if (!context->premaster_key) + return TLS_GENERIC_ERROR; + + curve25519(context->premaster_key, secret, buffer); + context->premaster_key_len = 32; + } else { + TLS_FREE(context->premaster_key); + context->premaster_key = (unsigned char *)TLS_MALLOC(32); + if (!context->premaster_key) + return TLS_GENERIC_ERROR; + + curve25519(context->premaster_key, context->client_secret, buffer); + context->premaster_key_len = 32; + + TLS_FREE(context->client_secret); + context->client_secret = NULL; + } + DEBUG_DUMP_HEX_LABEL("x25519 KEY", context->premaster_key, context->premaster_key_len); + + return 0; + } +#endif + if (context->is_server) { + _private_tls_ecc_dhe_create(context); + if (ecc_make_key_ex(NULL, find_prng("sprng"), context->ecc_dhe, (ltc_ecc_set_type *)&context->curve->dp)) { + TLS_FREE(context->ecc_dhe); + context->ecc_dhe = NULL; + DEBUG_PRINT("Error generating ECC DHE key\n"); + return TLS_GENERIC_ERROR; + } + } + + ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&context->curve->dp; + + if ((context->is_server) && (!tls_random(context->local_random, TLS_SERVER_RANDOM_SIZE))) + return TLS_GENERIC_ERROR; + + ecc_key client_key; + memset(&client_key, 0, sizeof(client_key)); + if (ecc_ansi_x963_import_ex(buffer, key_size, &client_key, dp)) { + DEBUG_PRINT("Error importing ECC DHE key\n"); + return TLS_GENERIC_ERROR; + } + out2 = (unsigned char *)TLS_MALLOC(key_size); + out_size = key_size; + + int err = ecc_shared_secret(context->ecc_dhe, &client_key, out2, &out_size); + ecc_free(&client_key); + + if (err) { + DEBUG_PRINT("ECC DHE DECRYPT ERROR %i\n", err); + TLS_FREE(out2); + return TLS_GENERIC_ERROR; + } + DEBUG_PRINT("OUT_SIZE: %lu\n", out_size); + DEBUG_DUMP_HEX_LABEL("ECC DHE", out2, out_size); + + TLS_FREE(context->premaster_key); + context->premaster_key = out2; + context->premaster_key_len = out_size; + return 0; + } else + if (dhkey) { + _private_tls_dhe_create(context); + if (!tls_random(context->local_random, TLS_SERVER_RANDOM_SIZE)) + return TLS_GENERIC_ERROR; + if (_private_tls_dh_make_key(dhe_key_size / 8, context->dhe, (const char *)dhkey->p, (const char *)dhkey->g, 0, 0)) { + TLS_FREE(context->dhe); + context->dhe = NULL; + DEBUG_PRINT("Error generating DHE key\n"); + return TLS_GENERIC_ERROR; + } + + unsigned int dhe_out_size; + out2 = _private_tls_decrypt_dhe(context, buffer, key_size, &dhe_out_size, 0); + if (!out2) { + DEBUG_PRINT("Error generating DHE shared key\n"); + return TLS_GENERIC_ERROR; + } + + TLS_FREE(context->premaster_key); + context->premaster_key = out2; + context->premaster_key_len = dhe_out_size; + if (context->dhe) + context->dhe->iana = dhkey->iana; + return 0; + } + DEBUG_PRINT("NO COMMON KEY SHARE SUPPORTED\n"); + return TLS_NO_COMMON_CIPHER; +} +#endif + +int tls_parse_hello(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets, unsigned int *dtls_verified) { + *write_packets = 0; + *dtls_verified = 0; + if ((context->connection_status != 0) && (context->connection_status != 4)) { + // ignore multiple hello on dtls + if (context->dtls) { + DEBUG_PRINT("RETRANSMITTED HELLO MESSAGE RECEIVED\n"); + return 1; + } + DEBUG_PRINT("UNEXPECTED HELLO MESSAGE\n"); + return TLS_UNEXPECTED_MESSAGE; + } + + int res = 0; + int downgraded = 0; + int hello_min_size = context->dtls ? TLS_CLIENT_HELLO_MINSIZE + 8 : TLS_CLIENT_HELLO_MINSIZE; + CHECK_SIZE(hello_min_size, buf_len, TLS_NEED_MORE_DATA) + // big endian + unsigned int bytes_to_follow = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; + res += 3; + if (context->dtls) { + int dtls_check = _private_dtls_check_packet(buf, buf_len); + if (dtls_check < 0) + return dtls_check; + // 16 bit message seq + 24 bit fragment offset + 24 bit fragment length + res += 8; + } + CHECK_SIZE(bytes_to_follow, buf_len - res, TLS_NEED_MORE_DATA) + + CHECK_SIZE(2, buf_len - res, TLS_NEED_MORE_DATA) + unsigned short version = ntohs(*(unsigned short *)&buf[res]); + unsigned short cipher = 0; + + res += 2; + VERSION_SUPPORTED(version, TLS_NOT_SAFE) + DEBUG_PRINT("VERSION REQUIRED BY REMOTE %x, VERSION NOW %x\n", (int)version, (int)context->version); +#ifdef TLS_LEGACY_SUPPORT + // when no legacy support, don't downgrade +#ifndef TLS_FORCE_LOCAL_VERSION + // downgrade ? + if (context->dtls) { + // for dlts, newer version has lower id (1.0 = FEFF, 1.2 = FEFD) + if (context->version < version) + downgraded = 1; + } else { + if (context->version > version) + downgraded = 1; + } + if (downgraded) { + context->version = version; + if (!context->is_server) + _private_tls_change_hash_type(context); + } +#endif +#endif + memcpy(context->remote_random, &buf[res], TLS_CLIENT_RANDOM_SIZE); + res += TLS_CLIENT_RANDOM_SIZE; + + unsigned char session_len = buf[res++]; + CHECK_SIZE(session_len, buf_len - res, TLS_NEED_MORE_DATA) + if ((session_len) && (session_len <= TLS_MAX_SESSION_ID)) { + memcpy(context->session, &buf[res], session_len); + context->session_size = session_len; + DEBUG_DUMP_HEX_LABEL("REMOTE SESSION ID: ", context->session, context->session_size); + } else + context->session_size = 0; + res += session_len; + + const unsigned char *cipher_buffer = NULL; + unsigned short cipher_len = 0; + int scsv_set = 0; + if (context->is_server) { + if (context->dtls) { + CHECK_SIZE(1, buf_len - res, TLS_NEED_MORE_DATA) + unsigned char tls_cookie_len = buf[res++]; + if (tls_cookie_len) { + CHECK_SIZE(tls_cookie_len, buf_len - res, TLS_NEED_MORE_DATA) + if ((!context->dtls_cookie_len) || (!context->dtls_cookie)) + _private_dtls_build_cookie(context); + + if ((context->dtls_cookie_len != tls_cookie_len) || (!context->dtls_cookie)) { + *dtls_verified = 2; + _private_dtls_reset_cookie(context); + DEBUG_PRINT("INVALID DTLS COOKIE\n"); + return TLS_BROKEN_PACKET; + } + if (memcmp(context->dtls_cookie, &buf[res], tls_cookie_len)) { + *dtls_verified = 3; + _private_dtls_reset_cookie(context); + DEBUG_PRINT("MISMATCH DTLS COOKIE\n"); + return TLS_BROKEN_PACKET; + } + _private_dtls_reset_cookie(context); + context->dtls_seq++; + *dtls_verified = 1; + res += tls_cookie_len; + } else { + *write_packets = 2; + return buf_len; + } + } + CHECK_SIZE(2, buf_len - res, TLS_NEED_MORE_DATA) + cipher_len = ntohs(*(unsigned short *)&buf[res]); + res += 2; + CHECK_SIZE(cipher_len, buf_len - res, TLS_NEED_MORE_DATA) + // faster than cipher_len % 2 + if (cipher_len & 1) + return TLS_BROKEN_PACKET; + + cipher_buffer = &buf[res]; + res += cipher_len; + + CHECK_SIZE(1, buf_len - res, TLS_NEED_MORE_DATA) + unsigned char compression_list_size = buf[res++]; + CHECK_SIZE(compression_list_size, buf_len - res, TLS_NEED_MORE_DATA) + + // no compression support + res += compression_list_size; + } else { + CHECK_SIZE(2, buf_len - res, TLS_NEED_MORE_DATA) + cipher = ntohs(*(unsigned short *)&buf[res]); + res += 2; + context->cipher = cipher; +#ifndef WITH_TLS_13 + if (!tls_cipher_supported(context, cipher)) { + context->cipher = 0; + DEBUG_PRINT("NO CIPHER SUPPORTED\n"); + return TLS_NO_COMMON_CIPHER; + } + DEBUG_PRINT("CIPHER: %s\n", tls_cipher_name(context)); +#endif + CHECK_SIZE(1, buf_len - res, TLS_NEED_MORE_DATA) + unsigned char compression = buf[res++]; + if (compression != 0) { + DEBUG_PRINT("COMPRESSION NOT SUPPORTED\n"); + return TLS_COMPRESSION_NOT_SUPPORTED; + } + } + + if (res > 0) { + if (context->is_server) + *write_packets = 2; + if (context->connection_status != 4) + context->connection_status = 1; + } + + + if (res > 2) + res += 2; +#ifdef WITH_TLS_13 + const unsigned char *key_share = NULL; + unsigned short key_size = 0; +#endif + while (buf_len - res >= 4) { + // have extensions + unsigned short extension_type = ntohs(*(unsigned short *)&buf[res]); + res += 2; + unsigned short extension_len = ntohs(*(unsigned short *)&buf[res]); + res += 2; + DEBUG_PRINT("Extension: 0x0%x (%i), len: %i\n", (int)extension_type, (int)extension_type, (int)extension_len); + if (extension_len) { + // SNI extension + CHECK_SIZE(extension_len, buf_len - res, TLS_NEED_MORE_DATA) + if (extension_type == 0x00) { + // unsigned short sni_len = ntohs(*(unsigned short *)&buf[res]); + // unsigned char sni_type = buf[res + 2]; + unsigned short sni_host_len = ntohs(*(unsigned short *)&buf[res + 3]); + CHECK_SIZE(sni_host_len, buf_len - res - 5, TLS_NEED_MORE_DATA) + if (sni_host_len) { + TLS_FREE(context->sni); + context->sni = (char *)TLS_MALLOC(sni_host_len + 1); + if (context->sni) { + memcpy(context->sni, &buf[res + 5], sni_host_len); + context->sni[sni_host_len] = 0; + DEBUG_PRINT("SNI HOST INDICATOR: [%s]\n", context->sni); + } + } + } else +#ifdef TLS_FORWARD_SECRECY + if (extension_type == 0x0A) { + // supported groups + if (buf_len - res > 2) { + unsigned short group_len = ntohs(*(unsigned short *)&buf[res]); + if (buf_len - res >= group_len + 2) { + DEBUG_DUMP_HEX_LABEL("SUPPORTED GROUPS", &buf[res + 2], group_len); + int i; + int selected = 0; + for (i = 0; i < group_len; i += 2) { + unsigned short iana_n = ntohs(*(unsigned short *)&buf[res + 2 + i]); + switch (iana_n) { + case 23: + context->curve = &secp256r1; + selected = 1; + break; + case 24: + context->curve = &secp384r1; + selected = 1; + break; +#ifdef WITH_TLS_13 + // needs different implementation + // case 29: + // context->curve = &x25519; + // selected = 1; + // break; +#endif + // do not use it anymore + // case 25: + // context->curve = &secp521r1; + // selected = 1; + // break; + } + if (selected) { + DEBUG_PRINT("SELECTED CURVE %s\n", context->curve->name); + break; + } + } + } + } + } else +#endif + if ((extension_type == 0x10) && (context->alpn) && (context->alpn_count)) { + if (buf_len - res > 2) { + unsigned short alpn_len = ntohs(*(unsigned short *)&buf[res]); + if ((alpn_len) && (alpn_len <= extension_len - 2)) { + unsigned char *alpn = (unsigned char *)&buf[res + 2]; + int alpn_pos = 0; + while (alpn_pos < alpn_len) { + unsigned char alpn_size = alpn[alpn_pos++]; + if (alpn_size + alpn_pos >= extension_len) + break; + if ((alpn_size) && (tls_alpn_contains(context, (char *)&alpn[alpn_pos], alpn_size))) { + TLS_FREE(context->negotiated_alpn); + context->negotiated_alpn = (char *)TLS_MALLOC(alpn_size + 1); + if (context->negotiated_alpn) { + memcpy(context->negotiated_alpn, &alpn[alpn_pos], alpn_size); + context->negotiated_alpn[alpn_size] = 0; + DEBUG_PRINT("NEGOTIATED ALPN: %s\n", context->negotiated_alpn); + } + break; + } + alpn_pos += alpn_size; + // ServerHello contains just one alpn + if (!context->is_server) + break; + } + } + } + } else + if (extension_type == 0x0D) { + // supported signatures + DEBUG_DUMP_HEX_LABEL("SUPPORTED SIGNATURES", &buf[res], extension_len); + } else + if (extension_type == 0x0B) { + // supported point formats + DEBUG_DUMP_HEX_LABEL("SUPPORTED POINT FORMATS", &buf[res], extension_len); + } +#ifdef WITH_TLS_13 + else + if (extension_type == 0x2B) { + // supported versions + if ((context->is_server) && (buf[res] == extension_len - 1)) { + if (extension_len > 2) { + DEBUG_DUMP_HEX_LABEL("SUPPORTED VERSIONS", &buf[res], extension_len); + int i; + int limit = (int)buf[res]; + if (limit == extension_len - 1) { + for (i = 1; i < limit; i += 2) { + if ((ntohs(*(unsigned short *)&buf[res + i]) == TLS_V13) || (ntohs(*(unsigned short *)&buf[res + i]) == 0x7F1C)) { + context->version = TLS_V13; + context->tls13_version = ntohs(*(unsigned short *)&buf[res + i]); + DEBUG_PRINT("TLS 1.3 SUPPORTED\n"); + break; + } + } + } + } + } else + if ((!context->is_server) && (extension_len == 2)) { + if ((ntohs(*(unsigned short *)&buf[res]) == TLS_V13) || (ntohs(*(unsigned short *)&buf[res]) == 0x7F1C)) { + context->version = TLS_V13; + context->tls13_version = ntohs(*(unsigned short *)&buf[res]); + DEBUG_PRINT("TLS 1.3 SUPPORTED\n"); + } + } + } else + if (extension_type == 0x2A) { + // early data + DEBUG_DUMP_HEX_LABEL("EXTENSION, EARLY DATA", &buf[res], extension_len); + } else + if (extension_type == 0x29) { + // pre shared key + DEBUG_DUMP_HEX_LABEL("EXTENSION, PRE SHARED KEY", &buf[res], extension_len); + } else + if (extension_type == 0x33) { + // key share + if (context->is_server) { + key_size = ntohs(*(unsigned short *)&buf[res]); + if ((context->is_server) && (key_size > extension_len - 2)) { + DEBUG_PRINT("BROKEN KEY SHARE\n"); + return TLS_BROKEN_PACKET; + } + } else { + key_size = extension_len; + } + DEBUG_DUMP_HEX_LABEL("EXTENSION, KEY SHARE", &buf[res], extension_len); + if (context->is_server) + key_share = &buf[res + 2]; + else + key_share = &buf[res]; + } else + if (extension_type == 0x0D) { + // signature algorithms + DEBUG_DUMP_HEX_LABEL("EXTENSION, SIGNATURE ALGORITHMS", &buf[res], extension_len); + } else + if (extension_type == 0x2D) { + // psk key exchange modes + DEBUG_DUMP_HEX_LABEL("EXTENSION, PSK KEY EXCHANGE MODES", &buf[res], extension_len); + } +#endif + res += extension_len; + } + } + if (buf_len != res) + return TLS_NEED_MORE_DATA; + if ((context->is_server) && (cipher_buffer) && (cipher_len)) { + int cipher = tls_choose_cipher(context, cipher_buffer, cipher_len, &scsv_set); + if (cipher < 0) { + DEBUG_PRINT("NO COMMON CIPHERS\n"); + return cipher; + } + if ((downgraded) && (scsv_set)) { + DEBUG_PRINT("NO DOWNGRADE (SCSV SET)\n"); + _private_tls_write_packet(tls_build_alert(context, 1, inappropriate_fallback)); + context->critical_error = 1; + return TLS_NOT_SAFE; + } + context->cipher = cipher; + } +#ifdef WITH_TLS_13 + if (!context->is_server) { + if (!tls_cipher_supported(context, cipher)) { + context->cipher = 0; + DEBUG_PRINT("NO CIPHER SUPPORTED\n"); + return TLS_NO_COMMON_CIPHER; + } + DEBUG_PRINT("CIPHER: %s\n", tls_cipher_name(context)); + } + + if ((key_share) && (key_size) && ((context->version == TLS_V13) || (context->version == DTLS_V13))) { + int key_share_err = _private_tls_parse_key_share(context, key_share, key_size); + if (key_share_err) { + // request hello retry + if (context->connection_status != 4) { + *write_packets = 5; + context->hs_messages[1] = 0; + context->connection_status = 4; + return res; + } else + return key_share_err; + } + // we have key share + if (context->is_server) + context->connection_status = 3; + else + context->connection_status = 2; + } +#endif + return res; +} + +int tls_parse_certificate(struct TLSContext *context, const unsigned char *buf, int buf_len, int is_client) { + int res = 0; + CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA) + unsigned int size_of_all_certificates = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; + + if (size_of_all_certificates <= 4) + return 3 + size_of_all_certificates; + res += 3; + if (context->dtls) { + int dtls_check = _private_dtls_check_packet(buf, buf_len); + if (dtls_check < 0) + return dtls_check; + res += 8; + } +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + int context_size = buf[res]; + res++; + // must be 0 + if (context_size) + res += context_size; + } +#endif + + CHECK_SIZE(size_of_all_certificates, buf_len - res, TLS_NEED_MORE_DATA); + int size = size_of_all_certificates; + + int idx = 0; + int valid_certificate = 0; + while (size > 0) { + idx++; + CHECK_SIZE(3, buf_len - res, TLS_NEED_MORE_DATA); + unsigned int certificate_size = buf[res] * 0x10000 + buf[res + 1] * 0x100 + buf[res + 2]; + res += 3; + CHECK_SIZE(certificate_size, buf_len - res, TLS_NEED_MORE_DATA) + // load chain + int certificates_in_chain = 0; + int res2 = res; + unsigned int remaining = certificate_size; + do { + if (remaining <= 3) + break; + certificates_in_chain++; + unsigned int certificate_size2 = buf[res2] * 0x10000 + buf[res2 + 1] * 0x100 + buf[res2 + 2]; + res2 += 3; + remaining -= 3; + if (certificate_size2 > remaining) { + DEBUG_PRINT("Invalid certificate size (%i from %i bytes remaining)\n", certificate_size2, remaining); + break; + } + remaining -= certificate_size2; + + struct TLSCertificate *cert = asn1_parse(context, &buf[res2], certificate_size2, is_client); + if (cert) { + if (certificate_size2) { + cert->bytes = (unsigned char *)TLS_MALLOC(certificate_size2); + if (cert->bytes) { + cert->len = certificate_size2; + memcpy(cert->bytes, &buf[res2], certificate_size2); + } + } + // valid certificate + if (is_client) { + valid_certificate = 1; + context->client_certificates = (struct TLSCertificate **)TLS_REALLOC(context->client_certificates, (context->client_certificates_count + 1) * sizeof(struct TLSCertificate *)); + context->client_certificates[context->client_certificates_count] = cert; + context->client_certificates_count++; + } else { + context->certificates = (struct TLSCertificate **)TLS_REALLOC(context->certificates, (context->certificates_count + 1) * sizeof(struct TLSCertificate *)); + context->certificates[context->certificates_count] = cert; + context->certificates_count++; + if ((cert->pk) || (cert->priv)) + valid_certificate = 1; + else + if (!context->is_server) + valid_certificate = 1; + } + } + res2 += certificate_size2; +#ifdef WITH_TLS_13 + // extension + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + if (remaining >= 2) { + // ignore extensions + remaining -= 2; + unsigned short size = ntohs(*(unsigned short *)&buf[res2]); + if ((size) && (size >= remaining)) { + res2 += size; + remaining -= size; + } + } + } +#endif + } while (remaining > 0); + if (remaining) { + DEBUG_PRINT("Extra %i bytes after certificate\n", remaining); + } + size -= certificate_size + 3; + res += certificate_size; + } + if (!valid_certificate) + return TLS_UNSUPPORTED_CERTIFICATE; + if (res != buf_len) { + DEBUG_PRINT("Warning: %i bytes read from %i byte buffer\n", (int)res, (int)buf_len); + } + return res; +} + +int _private_tls_parse_dh(const unsigned char *buf, int buf_len, const unsigned char **out, int *out_size) { + int res = 0; + *out = NULL; + *out_size = 0; + CHECK_SIZE(2, buf_len, TLS_NEED_MORE_DATA) + unsigned short size = ntohs(*(unsigned short *)buf); + res += 2; + CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA) + DEBUG_DUMP_HEX(&buf[res], size); + *out = &buf[res]; + *out_size = size; + res += size; + return res; +} + +int _private_tls_parse_random(struct TLSContext *context, const unsigned char *buf, int buf_len) { + int res = 0; + int ephemeral = tls_cipher_is_ephemeral(context); + unsigned short size; + if (ephemeral == 2) { + CHECK_SIZE(1, buf_len, TLS_NEED_MORE_DATA) + size = buf[0]; + res += 1; + } else { + CHECK_SIZE(2, buf_len, TLS_NEED_MORE_DATA) + size = ntohs(*(unsigned short *)buf); + res += 2; + } + + CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA) + unsigned int out_len = 0; + unsigned char *random = NULL; + switch (ephemeral) { +#ifdef TLS_FORWARD_SECRECY + case 1: + random = _private_tls_decrypt_dhe(context, &buf[res], size, &out_len, 1); + break; + case 2: + random = _private_tls_decrypt_ecc_dhe(context, &buf[res], size, &out_len, 1); + break; +#endif + default: + random = _private_tls_decrypt_rsa(context, &buf[res], size, &out_len); + } + + if ((random) && (out_len > 2)) { + DEBUG_DUMP_HEX_LABEL("PRE MASTER KEY", random, out_len); + TLS_FREE(context->premaster_key); + context->premaster_key = random; + context->premaster_key_len = out_len; + _private_tls_compute_key(context, 48); + } else { + TLS_FREE(random); + return 0; + } + res += size; + return res; +} + +int _private_tls_build_random(struct TLSPacket *packet) { + int res = 0; + unsigned char rand_bytes[48]; + int bytes = 48; + if (!tls_random(rand_bytes, bytes)) + return TLS_GENERIC_ERROR; + + // max supported version + if (packet->context->is_server) + *(unsigned short *)rand_bytes = htons(packet->context->version); + else + if (packet->context->dtls) + *(unsigned short *)rand_bytes = htons(DTLS_V12); + else + *(unsigned short *)rand_bytes = htons(TLS_V12); + //DEBUG_DUMP_HEX_LABEL("PREMASTER KEY", rand_bytes, bytes); + + TLS_FREE(packet->context->premaster_key); + packet->context->premaster_key = (unsigned char *)TLS_MALLOC(bytes); + if (!packet->context->premaster_key) + return TLS_NO_MEMORY; + + packet->context->premaster_key_len = bytes; + memcpy(packet->context->premaster_key, rand_bytes, packet->context->premaster_key_len); + + unsigned int out_len; + unsigned char *random = _private_tls_encrypt_rsa(packet->context, packet->context->premaster_key, packet->context->premaster_key_len, &out_len); + + _private_tls_compute_key(packet->context, bytes); + if ((random) && (out_len > 2)) { + tls_packet_uint24(packet, out_len + 2); + if (packet->context->dtls) + _private_dtls_handshake_data(packet->context, packet, out_len + 2); + tls_packet_uint16(packet, out_len); + tls_packet_append(packet, random, out_len); + } else + res = TLS_GENERIC_ERROR; + TLS_FREE(random); + if (res) + return res; + + return out_len + 2; +} + +const unsigned char *_private_tls_parse_signature(struct TLSContext *context, const unsigned char *buf, int buf_len, int *hash_algorithm, int *sign_algorithm, int *sig_size, int *offset) { + int res = 0; + CHECK_SIZE(2, buf_len, NULL) + *hash_algorithm = _md5_sha1; + *sign_algorithm = rsa_sign; + *sig_size = 0; + if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { + *hash_algorithm = buf[res]; + res++; + *sign_algorithm = buf[res]; + res++; + } + unsigned short size = ntohs(*(unsigned short *)&buf[res]); + res += 2; + CHECK_SIZE(size, buf_len - res, NULL) + DEBUG_DUMP_HEX(&buf[res], size); + *sig_size = size; + *offset = res + size; + return &buf[res]; +} + +int tls_parse_server_key_exchange(struct TLSContext *context, const unsigned char *buf, int buf_len) { + int res = 0; + int dh_res = 0; + CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA) + unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; + res += 3; + if (context->dtls) { + int dtls_check = _private_dtls_check_packet(buf, buf_len); + if (dtls_check < 0) + return dtls_check; + res += 8; + } + const unsigned char *packet_ref = buf + res; + CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA); + + if (!size) + return res; + + unsigned char has_ds_params = 0; + unsigned int key_size = 0; +#ifdef TLS_FORWARD_SECRECY + const struct ECCCurveParameters *curve = NULL; + const unsigned char *pk_key = NULL; + int ephemeral = tls_cipher_is_ephemeral(context); + if (ephemeral) { + if (ephemeral == 1) { + has_ds_params = 1; + } else { + if (buf[res++] != 3) { + // named curve + // any other method is not supported + return 0; + } + CHECK_SIZE(3, buf_len - res, TLS_NEED_MORE_DATA); + int iana_n = ntohs(*(unsigned short *)&buf[res]); + res += 2; + key_size = buf[res]; + res++; + CHECK_SIZE(key_size, buf_len - res, TLS_NEED_MORE_DATA); + DEBUG_PRINT("IANA CURVE NUMBER: %i\n", iana_n); + switch (iana_n) { + case 19: + curve = &secp192r1; + break; + case 20: + curve = &secp224k1; + break; + case 21: + curve = &secp224r1; + break; + case 22: + curve = &secp256k1; + break; + case 23: + curve = &secp256r1; + break; + case 24: + curve = &secp384r1; + break; + case 25: + curve = &secp521r1; + break; +#ifdef TLS_CURVE25519 + case 29: + curve = &x25519; + break; +#endif + default: + DEBUG_PRINT("UNSUPPORTED CURVE\n"); + return TLS_GENERIC_ERROR; + } + pk_key = &buf[res]; + res += key_size; + context->curve = curve; + } + } +#endif + const unsigned char *dh_p = NULL; + int dh_p_len = 0; + const unsigned char *dh_g = NULL; + int dh_g_len = 0; + const unsigned char *dh_Ys = NULL; + int dh_Ys_len = 0; + if (has_ds_params) { + DEBUG_PRINT(" dh_p: "); + dh_res = _private_tls_parse_dh(&buf[res], buf_len - res, &dh_p, &dh_p_len); + if (dh_res <= 0) + return TLS_BROKEN_PACKET; + res += dh_res; + DEBUG_PRINT("\n"); + + DEBUG_PRINT(" dh_q: "); + dh_res = _private_tls_parse_dh(&buf[res], buf_len - res, &dh_g, &dh_g_len); + if (dh_res <= 0) + return TLS_BROKEN_PACKET; + res += dh_res; + DEBUG_PRINT("\n"); + + DEBUG_PRINT(" dh_Ys: "); + dh_res = _private_tls_parse_dh(&buf[res], buf_len - res, &dh_Ys, &dh_Ys_len); + if (dh_res <= 0) + return TLS_BROKEN_PACKET; + res += dh_res; + DEBUG_PRINT("\n"); + } + int sign_size; + int hash_algorithm; + int sign_algorithm; + int packet_size = res - 3; + if (context->dtls) + packet_size -= 8; + int offset = 0; + DEBUG_PRINT(" SIGNATURE (%i/%i/%i): ", packet_size, dh_res, key_size); + const unsigned char *signature = _private_tls_parse_signature(context, &buf[res], buf_len - res, &hash_algorithm, &sign_algorithm, &sign_size, &offset); + DEBUG_PRINT("\n"); + if ((sign_size <= 0) || (!signature)) + return TLS_BROKEN_PACKET; + res += offset; + // check signature + unsigned int message_len = packet_size + TLS_CLIENT_RANDOM_SIZE + TLS_SERVER_RANDOM_SIZE; + unsigned char *message = (unsigned char *)TLS_MALLOC(message_len); + if (message) { + memcpy(message, context->local_random, TLS_CLIENT_RANDOM_SIZE); + memcpy(message + TLS_CLIENT_RANDOM_SIZE, context->remote_random, TLS_SERVER_RANDOM_SIZE); + memcpy(message + TLS_CLIENT_RANDOM_SIZE + TLS_SERVER_RANDOM_SIZE, packet_ref, packet_size); +#ifdef TLS_CLIENT_ECDSA + if (tls_is_ecdsa(context)) { + if (_private_tls_verify_ecdsa(context, hash_algorithm, signature, sign_size, message, message_len, NULL) != 1) { + DEBUG_PRINT("ECC Server signature FAILED!\n"); + TLS_FREE(message); + return TLS_BROKEN_PACKET; + } + } else +#endif + { + if (_private_tls_verify_rsa(context, hash_algorithm, signature, sign_size, message, message_len) != 1) { + DEBUG_PRINT("Server signature FAILED!\n"); + TLS_FREE(message); + return TLS_BROKEN_PACKET; + } + } + TLS_FREE(message); + } + + if (buf_len - res) { + DEBUG_PRINT("EXTRA %i BYTES AT THE END OF MESSAGE\n", buf_len - res); + DEBUG_DUMP_HEX(&buf[res], buf_len - res); + DEBUG_PRINT("\n"); + } +#ifdef TLS_FORWARD_SECRECY + if (ephemeral == 1) { + _private_tls_dhe_create(context); + DEBUG_DUMP_HEX_LABEL("DHP", dh_p, dh_p_len); + DEBUG_DUMP_HEX_LABEL("DHG", dh_g, dh_g_len); + int dhe_key_size = dh_p_len; + if (dh_g_len > dh_p_len) + dhe_key_size = dh_g_len; + if (_private_tls_dh_make_key(dhe_key_size, context->dhe, (const char *)dh_p, (const char *)dh_g, dh_p_len, dh_g_len)) { + DEBUG_PRINT("ERROR CREATING DHE KEY\n"); + TLS_FREE(context->dhe); + context->dhe = NULL; + return TLS_GENERIC_ERROR; + } + + unsigned int dh_key_size = 0; + unsigned char *key = _private_tls_decrypt_dhe(context, dh_Ys, dh_Ys_len, &dh_key_size, 0); + DEBUG_DUMP_HEX_LABEL("DH COMMON SECRET", key, dh_key_size); + if ((key) && (dh_key_size)) { + TLS_FREE(context->premaster_key); + context->premaster_key = key; + context->premaster_key_len = dh_key_size; + } + } else + if ((ephemeral == 2) && (curve) && (pk_key) && (key_size)) { +#ifdef TLS_CURVE25519 + if (curve == &x25519) { + if (key_size != 32) { + DEBUG_PRINT("INVALID X25519 PUBLIC SIZE"); + return TLS_GENERIC_ERROR; + } + + TLS_FREE(context->client_secret); + context->client_secret = (unsigned char *)TLS_MALLOC(32); + if (!context->client_secret) { + DEBUG_PRINT("ERROR IN TLS_MALLOC"); + return TLS_GENERIC_ERROR; + } + + tls_random(context->client_secret, 32); + + context->client_secret[0] &= 248; + context->client_secret[31] &= 127; + context->client_secret[31] |= 64; + + TLS_FREE(context->premaster_key); + context->premaster_key = (unsigned char *)TLS_MALLOC(32); + if (!context->premaster_key) + return TLS_GENERIC_ERROR; + + curve25519(context->premaster_key, context->client_secret, pk_key); + context->premaster_key_len = 32; + } else +#endif + { + tls_init(); + _private_tls_ecc_dhe_create(context); + + ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&curve->dp; + if (ecc_make_key_ex(NULL, find_prng("sprng"), context->ecc_dhe, dp)) { + TLS_FREE(context->ecc_dhe); + context->ecc_dhe = NULL; + DEBUG_PRINT("Error generating ECC key\n"); + return TLS_GENERIC_ERROR; + } + + TLS_FREE(context->premaster_key); + context->premaster_key_len = 0; + + unsigned int out_len = 0; + context->premaster_key = _private_tls_decrypt_ecc_dhe(context, pk_key, key_size, &out_len, 0); + if (context->premaster_key) + context->premaster_key_len = out_len; + } + } +#endif + return res; +} + +int tls_parse_client_key_exchange(struct TLSContext *context, const unsigned char *buf, int buf_len) { + if (context->connection_status != 1) { + DEBUG_PRINT("UNEXPECTED CLIENT KEY EXCHANGE MESSAGE (connections status: %i)\n", (int)context->connection_status); + return TLS_UNEXPECTED_MESSAGE; + } + + int res = 0; + int dh_res = 0; + CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA) + + unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; + res += 3; + if (context->dtls) { + int dtls_check = _private_dtls_check_packet(buf, buf_len); + if (dtls_check < 0) + return dtls_check; + res += 8; + } + + CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA); + + if (!size) + return res; + + dh_res = _private_tls_parse_random(context, &buf[res], size); + if (dh_res <= 0) { + DEBUG_PRINT("broken key\n"); + return TLS_BROKEN_PACKET; + } + DEBUG_PRINT("\n"); + + res += size; + context->connection_status = 2; + return res; +} + +int tls_parse_server_hello_done(struct TLSContext *context, const unsigned char *buf, int buf_len) { + int res = 0; + CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA) + + unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; + res += 3; + if (context->dtls) { + int dtls_check = _private_dtls_check_packet(buf, buf_len); + if (dtls_check < 0) + return dtls_check; + res += 8; + } + + CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA); + + res += size; + return res; +} + +int tls_parse_finished(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets) { + if ((context->connection_status < 2) || (context->connection_status == 0xFF)) { + DEBUG_PRINT("UNEXPECTED FINISHED MESSAGE\n"); + return TLS_UNEXPECTED_MESSAGE; + } + + int res = 0; + *write_packets = 0; + CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA) + + unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; + res += 3; + if (context->dtls) { + int dtls_check = _private_dtls_check_packet(buf, buf_len); + if (dtls_check < 0) + return dtls_check; + res += 8; + } + + if (size < TLS_MIN_FINISHED_OPAQUE_LEN) { + DEBUG_PRINT("Invalid finished pachet size: %i\n", size); + return TLS_BROKEN_PACKET; + } + + CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA); + + unsigned char hash[TLS_MAX_SHA_SIZE]; + unsigned int hash_len = _private_tls_get_hash(context, hash); + +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + unsigned char hash_out[TLS_MAX_SHA_SIZE]; + unsigned long out_size = TLS_MAX_SHA_SIZE; + if ((!context->remote_finished_key) || (!hash_len)) { + DEBUG_PRINT("NO FINISHED KEY COMPUTED OR NO HANDSHAKE HASH\n"); + return TLS_NOT_VERIFIED; + } + + DEBUG_DUMP_HEX_LABEL("HS HASH", hash, hash_len); + DEBUG_DUMP_HEX_LABEL("HS FINISH", context->finished_key, hash_len); + DEBUG_DUMP_HEX_LABEL("HS REMOTE FINISH", context->remote_finished_key, hash_len); + + out_size = hash_len; + hmac_state hmac; + hmac_init(&hmac, _private_tls_get_hash_idx(context), context->remote_finished_key, hash_len); + hmac_process(&hmac, hash, hash_len); + hmac_done(&hmac, hash_out, &out_size); + + if ((size != out_size) || (memcmp(hash_out, &buf[res], size))) { + DEBUG_PRINT("Finished validation error (sequence number, local: %i, remote: %i)\n", (int)context->local_sequence_number, (int)context->remote_sequence_number); + DEBUG_DUMP_HEX_LABEL("FINISHED OPAQUE", &buf[res], size); + DEBUG_DUMP_HEX_LABEL("VERIFY", hash_out, out_size); + return TLS_NOT_VERIFIED; + } + if (context->is_server) { + context->connection_status = 0xFF; + res += size; + _private_tls13_key(context, 0); + context->local_sequence_number = 0; + context->remote_sequence_number = 0; + return res; + } + } else +#endif + { + // verify + unsigned char *out = (unsigned char *)TLS_MALLOC(size); + if (!out) { + DEBUG_PRINT("Error in TLS_MALLOC (%i bytes)\n", (int)size); + return TLS_NO_MEMORY; + } + + // server verifies client's message + if (context->is_server) + _private_tls_prf(context, out, size, context->master_key, context->master_key_len, (unsigned char *)"client finished", 15, hash, hash_len, NULL, 0); + else + _private_tls_prf(context, out, size, context->master_key, context->master_key_len, (unsigned char *)"server finished", 15, hash, hash_len, NULL, 0); + + if (memcmp(out, &buf[res], size)) { + TLS_FREE(out); + DEBUG_PRINT("Finished validation error (sequence number, local: %i, remote: %i)\n", (int)context->local_sequence_number, (int)context->remote_sequence_number); + DEBUG_DUMP_HEX_LABEL("FINISHED OPAQUE", &buf[res], size); + DEBUG_DUMP_HEX_LABEL("VERIFY", out, size); + return TLS_NOT_VERIFIED; + } +#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION + if (size) { + if (context->is_server) { + TLS_FREE(context->verify_data); + context->verify_data = (unsigned char *)TLS_MALLOC(size); + if (context->verify_data) { + memcpy(context->verify_data, out, size); + context->verify_len = size; + } + } else { + // concatenate client verify and server verify + context->verify_data = (unsigned char *)TLS_REALLOC(context->verify_data, size); + if (context->verify_data) { + memcpy(context->verify_data + context->verify_len, out, size); + context->verify_len += size; + } else + context->verify_len = 0; + } + } +#endif + TLS_FREE(out); + } + if (context->is_server) + *write_packets = 3; + else + context->connection_status = 0xFF; + res += size; + return res; +} + +#ifdef WITH_TLS_13 +int tls_parse_verify_tls13(struct TLSContext *context, const unsigned char *buf, int buf_len) { + CHECK_SIZE(7, buf_len, TLS_NEED_MORE_DATA) + unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; + + if (size < 2) + return buf_len; + + unsigned char signing_data[TLS_MAX_HASH_SIZE + 98]; + int signing_data_len; + + // first 64 bytes to 0x20 (32) + memset(signing_data, 0x20, 64); + // context string 33 bytes + if (context->is_server) + memcpy(signing_data + 64, "TLS 1.3, server CertificateVerify", 33); + else + memcpy(signing_data + 64, "TLS 1.3, client CertificateVerify", 33); + // a single 0 byte separator + signing_data[97] = 0; + signing_data_len = 98; + + signing_data_len += _private_tls_get_hash(context, signing_data + 98); + DEBUG_DUMP_HEX_LABEL("signature data", signing_data, signing_data_len); + unsigned short signature = ntohs(*(unsigned short *)&buf[3]); + unsigned short signature_size = ntohs(*(unsigned short *)&buf[5]); + int valid = 0; + CHECK_SIZE(7 + size, buf_len, TLS_NEED_MORE_DATA) + switch (signature) { +#ifdef TLS_ECDSA_SUPPORTED + case 0x0403: + // secp256r1 + sha256 + valid = _private_tls_verify_ecdsa(context, sha256, buf + 7, signature_size, signing_data, signing_data_len, &secp256r1); + break; + case 0x0503: + // secp384r1 + sha384 + valid = _private_tls_verify_ecdsa(context, sha384, buf + 7, signature_size, signing_data, signing_data_len, &secp384r1); + break; + case 0x0603: + // secp521r1 + sha512 + valid = _private_tls_verify_ecdsa(context, sha512, buf + 7, signature_size, signing_data, signing_data_len, &secp521r1); + break; +#endif + case 0x0804: + valid = _private_tls_verify_rsa(context, sha256, buf + 7, signature_size, signing_data, signing_data_len); + break; + default: + DEBUG_PRINT("Unsupported signature: %x\n", (int)signature); + return TLS_UNSUPPORTED_CERTIFICATE; + } + if (valid != 1) { + DEBUG_PRINT("Signature FAILED!\n"); + return TLS_DECRYPTION_FAILED; + } + return buf_len; +} +#endif + +int tls_parse_verify(struct TLSContext *context, const unsigned char *buf, int buf_len) { +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + return tls_parse_verify_tls13(context, buf, buf_len); +#endif + CHECK_SIZE(7, buf_len, TLS_BAD_CERTIFICATE) + unsigned int bytes_to_follow = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; + CHECK_SIZE(bytes_to_follow, buf_len - 3, TLS_BAD_CERTIFICATE) + int res = -1; + + if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { + unsigned int hash = buf[3]; + unsigned int algorithm = buf[4]; + if (algorithm != rsa) + return TLS_UNSUPPORTED_CERTIFICATE; + unsigned short size = ntohs(*(unsigned short *)&buf[5]); + CHECK_SIZE(size, bytes_to_follow - 4, TLS_BAD_CERTIFICATE) + DEBUG_PRINT("ALGORITHM %i/%i (%i)\n", hash, algorithm, (int)size); + DEBUG_DUMP_HEX_LABEL("VERIFY", &buf[7], bytes_to_follow - 7); + + res = _private_tls_verify_rsa(context, hash, &buf[7], size, context->cached_handshake, context->cached_handshake_len); + } else { +#ifdef TLS_LEGACY_SUPPORT + unsigned short size = ntohs(*(unsigned short *)&buf[3]); + CHECK_SIZE(size, bytes_to_follow - 2, TLS_BAD_CERTIFICATE) + res = _private_tls_verify_rsa(context, md5, &buf[5], size, context->cached_handshake, context->cached_handshake_len); +#endif + } + if (context->cached_handshake) { + // not needed anymore + TLS_FREE(context->cached_handshake); + context->cached_handshake = NULL; + context->cached_handshake_len = 0; + } + if (res == 1) { + DEBUG_PRINT("Signature OK\n"); + context->client_verified = 1; + } else { + DEBUG_PRINT("Signature FAILED\n"); + context->client_verified = 0; + } + return 1; +} + +int tls_parse_payload(struct TLSContext *context, const unsigned char *buf, int buf_len, tls_validation_function certificate_verify) { + int orig_len = buf_len; + if (context->connection_status == 0xFF) { +#ifndef TLS_ACCEPT_SECURE_RENEGOTIATION + // renegotiation disabled (emit warning alert) + _private_tls_write_packet(tls_build_alert(context, 0, no_renegotiation)); + return 1; +#endif + } + + while ((buf_len >= 4) && (!context->critical_error)) { + int payload_res = 0; + unsigned char update_hash = 1; + CHECK_SIZE(1, buf_len, TLS_NEED_MORE_DATA) + unsigned char type = buf[0]; + unsigned int write_packets = 0; + unsigned int dtls_cookie_verified = 0; + int certificate_verify_alert = no_error; + unsigned int payload_size = buf[1] * 0x10000 + buf[2] * 0x100 + buf[3] + 3; + if (context->dtls) + payload_size += 8; + CHECK_SIZE(payload_size + 1, buf_len, TLS_NEED_MORE_DATA) + switch (type) { + // hello request + case 0x00: + CHECK_HANDSHAKE_STATE(context, 0, 1); + DEBUG_PRINT(" => HELLO REQUEST (RENEGOTIATION?)\n"); + if (context->dtls) + context->dtls_seq = 0; + if (context->is_server) + payload_res = TLS_UNEXPECTED_MESSAGE; + else { + if (context->connection_status == 0xFF) { + // renegotiation +#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION + if (context->critical_error) + payload_res = TLS_UNEXPECTED_MESSAGE; + else { + _private_tls_reset_context(context); + _private_tls_write_packet(tls_build_hello(context, 0)); + return 1; + } +#else + payload_res = TLS_NO_RENEGOTIATION; +#endif + } else + payload_res = TLS_UNEXPECTED_MESSAGE; + } + // no payload + break; + // client hello + case 0x01: + CHECK_HANDSHAKE_STATE(context, 1, (context->dtls ? 2 : 1)); + DEBUG_PRINT(" => CLIENT HELLO\n"); + if (context->is_server) { + payload_res = tls_parse_hello(context, buf + 1, payload_size, &write_packets, &dtls_cookie_verified); + DEBUG_PRINT(" => DTLS COOKIE VERIFIED: %i (%i)\n", dtls_cookie_verified, payload_res); + if ((context->dtls) && (payload_res > 0) && (!dtls_cookie_verified) && (context->connection_status == 1)) { + // wait client hello + context->connection_status = 3; + update_hash = 0; + } + } else + payload_res = TLS_UNEXPECTED_MESSAGE; + break; + // server hello + case 0x02: + CHECK_HANDSHAKE_STATE(context, 2, 1); + DEBUG_PRINT(" => SERVER HELLO\n"); + if (context->is_server) + payload_res = TLS_UNEXPECTED_MESSAGE; + else + payload_res = tls_parse_hello(context, buf + 1, payload_size, &write_packets, &dtls_cookie_verified); + break; + // hello verify request + case 0x03: + DEBUG_PRINT(" => VERIFY REQUEST\n"); + CHECK_HANDSHAKE_STATE(context, 3, 1); + if ((context->dtls) && (!context->is_server)) { + payload_res = tls_parse_verify_request(context, buf + 1, payload_size, &write_packets); + update_hash = 0; + } else + payload_res = TLS_UNEXPECTED_MESSAGE; + break; + // certificate + case 0x0B: + CHECK_HANDSHAKE_STATE(context, 4, 1); + DEBUG_PRINT(" => CERTIFICATE\n"); +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + if (context->connection_status == 2) { + payload_res = tls_parse_certificate(context, buf + 1, payload_size, context->is_server); + if (context->is_server) { + if ((certificate_verify) && (context->client_certificates_count)) + certificate_verify_alert = certificate_verify(context, context->client_certificates, context->client_certificates_count); + // empty certificates are permitted for client + if (payload_res <= 0) + payload_res = 1; + } else { + if ((certificate_verify) && (context->certificates_count)) + certificate_verify_alert = certificate_verify(context, context->certificates, context->certificates_count); + } + } else + payload_res = TLS_UNEXPECTED_MESSAGE; + } else +#endif + if (context->connection_status == 1) { + if (context->is_server) { + // client certificate + payload_res = tls_parse_certificate(context, buf + 1, payload_size, 1); + if ((certificate_verify) && (context->client_certificates_count)) + certificate_verify_alert = certificate_verify(context, context->client_certificates, context->client_certificates_count); + // empty certificates are permitted for client + if (payload_res <= 0) + payload_res = 1; + } else { + payload_res = tls_parse_certificate(context, buf + 1, payload_size, 0); + if ((certificate_verify) && (context->certificates_count)) + certificate_verify_alert = certificate_verify(context, context->certificates, context->certificates_count); + } + } else + payload_res = TLS_UNEXPECTED_MESSAGE; + break; + // server key exchange + case 0x0C: + CHECK_HANDSHAKE_STATE(context, 5, 1); + DEBUG_PRINT(" => SERVER KEY EXCHANGE\n"); + if (context->is_server) + payload_res = TLS_UNEXPECTED_MESSAGE; + else + payload_res = tls_parse_server_key_exchange(context, buf + 1, payload_size); + break; + // certificate request + case 0x0D: + CHECK_HANDSHAKE_STATE(context, 6, 1); + // server to client + if (context->is_server) + payload_res = TLS_UNEXPECTED_MESSAGE; + else + context->client_verified = 2; + DEBUG_PRINT(" => CERTIFICATE REQUEST\n"); + break; + // server hello done + case 0x0E: + CHECK_HANDSHAKE_STATE(context, 7, 1); + DEBUG_PRINT(" => SERVER HELLO DONE\n"); + if (context->is_server) { + payload_res = TLS_UNEXPECTED_MESSAGE; + } else { + payload_res = tls_parse_server_hello_done(context, buf + 1, payload_size); + if (payload_res > 0) + write_packets = 1; + } + break; + // certificate verify + case 0x0F: + CHECK_HANDSHAKE_STATE(context, 8, 1); + DEBUG_PRINT(" => CERTIFICATE VERIFY\n"); + if (context->connection_status == 2) + payload_res = tls_parse_verify(context, buf + 1, payload_size); + else + payload_res = TLS_UNEXPECTED_MESSAGE; + break; + // client key exchange + case 0x10: + CHECK_HANDSHAKE_STATE(context, 9, 1); + DEBUG_PRINT(" => CLIENT KEY EXCHANGE\n"); + if (context->is_server) + payload_res = tls_parse_client_key_exchange(context, buf + 1, payload_size); + else + payload_res = TLS_UNEXPECTED_MESSAGE; + break; + // finished + case 0x14: + if (context->cached_handshake) { + TLS_FREE(context->cached_handshake); + context->cached_handshake = NULL; + context->cached_handshake_len = 0; + } + CHECK_HANDSHAKE_STATE(context, 10, 1); + DEBUG_PRINT(" => FINISHED\n"); + payload_res = tls_parse_finished(context, buf + 1, payload_size, &write_packets); + if (payload_res > 0) + memset(context->hs_messages, 0, sizeof(context->hs_messages)); + #ifdef WITH_TLS_13 + if ((!context->is_server) && ((context->version == TLS_V13) || (context->version == DTLS_V13))) { + update_hash = 0; + DEBUG_PRINT("<= SENDING FINISHED\n"); + _private_tls_update_hash(context, buf, payload_size + 1); + _private_tls_write_packet(tls_build_finished(context)); + _private_tls13_key(context, 0); + context->connection_status = 0xFF; + context->local_sequence_number = 0; + context->remote_sequence_number = 0; + } +#endif + break; +#ifdef WITH_TLS_13 + case 0x08: + // encrypted extensions ... ignore it for now + break; +#endif + default: + DEBUG_PRINT(" => NOT UNDERSTOOD PAYLOAD TYPE: %x\n", (int)type); + return TLS_NOT_UNDERSTOOD; + } + if ((type != 0x00) && (update_hash)) + _private_tls_update_hash(context, buf, payload_size + 1); + + if (certificate_verify_alert != no_error) { + _private_tls_write_packet(tls_build_alert(context, 1, certificate_verify_alert)); + context->critical_error = 1; + } + + if (payload_res < 0) { + switch (payload_res) { + case TLS_UNEXPECTED_MESSAGE: + _private_tls_write_packet(tls_build_alert(context, 1, unexpected_message)); + break; + case TLS_COMPRESSION_NOT_SUPPORTED: + _private_tls_write_packet(tls_build_alert(context, 1, decompression_failure)); + break; + case TLS_BROKEN_PACKET: + _private_tls_write_packet(tls_build_alert(context, 1, decode_error)); + break; + case TLS_NO_MEMORY: + _private_tls_write_packet(tls_build_alert(context, 1, internal_error)); + break; + case TLS_NOT_VERIFIED: + _private_tls_write_packet(tls_build_alert(context, 1, bad_record_mac)); + break; + case TLS_BAD_CERTIFICATE: + if (context->is_server) { + // bad client certificate, continue + _private_tls_write_packet(tls_build_alert(context, 0, bad_certificate)); + payload_res = 0; + } else + _private_tls_write_packet(tls_build_alert(context, 1, bad_certificate)); + break; + case TLS_UNSUPPORTED_CERTIFICATE: + _private_tls_write_packet(tls_build_alert(context, 1, unsupported_certificate)); + break; + case TLS_NO_COMMON_CIPHER: + _private_tls_write_packet(tls_build_alert(context, 1, insufficient_security)); + break; + case TLS_NOT_UNDERSTOOD: + _private_tls_write_packet(tls_build_alert(context, 1, internal_error)); + break; + case TLS_NO_RENEGOTIATION: + _private_tls_write_packet(tls_build_alert(context, 0, no_renegotiation)); + payload_res = 0; + break; + case TLS_DECRYPTION_FAILED: + _private_tls_write_packet(tls_build_alert(context, 1, decryption_failed_RESERVED)); + break; + } + if (payload_res < 0) + return payload_res; + } + if (certificate_verify_alert != no_error) + payload_res = TLS_BAD_CERTIFICATE; + + // except renegotiation + switch (write_packets) { + case 1: + if (context->client_verified == 2) { + DEBUG_PRINT("<= Building CERTIFICATE \n"); + _private_tls_write_packet(tls_build_certificate(context)); + context->client_verified = 0; + } + // client handshake + DEBUG_PRINT("<= Building KEY EXCHANGE\n"); + _private_tls_write_packet(tls_build_client_key_exchange(context)); + DEBUG_PRINT("<= Building CHANGE CIPHER SPEC\n"); + _private_tls_write_packet(tls_build_change_cipher_spec(context)); + context->cipher_spec_set = 1; + context->local_sequence_number = 0; + DEBUG_PRINT("<= Building CLIENT FINISHED\n"); + _private_tls_write_packet(tls_build_finished(context)); + context->cipher_spec_set = 0; +#ifdef TLS_12_FALSE_START + if ((!context->is_server) && (context->version == TLS_V12)) { + // https://tools.ietf.org/html/rfc7918 + // 5.1. Symmetric Cipher + // Clients MUST NOT use the False Start protocol modification in a + // handshake unless the cipher suite uses a symmetric cipher that is + // considered cryptographically strong. + switch (context->cipher) { + case TLS_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + context->false_start = 1; + break; + } + } +#endif + break; + case 2: + // server handshake + if ((context->dtls) && (dtls_cookie_verified == 0)) { + _private_tls_write_packet(tls_build_verify_request(context)); + _private_dtls_reset(context); + } else { + DEBUG_PRINT("<= SENDING SERVER HELLO\n"); +#ifdef WITH_TLS_13 + if (context->connection_status == 3) { + context->connection_status = 2; + _private_tls_write_packet(tls_build_hello(context, 0)); + _private_tls_write_packet(tls_build_change_cipher_spec(context)); + _private_tls13_key(context, 1); + context->cipher_spec_set = 1; + DEBUG_PRINT("<= SENDING ENCRYPTED EXTENSIONS\n"); + _private_tls_write_packet(tls_build_encrypted_extensions(context)); + if (context->request_client_certificate) { + DEBUG_PRINT("<= SENDING CERTIFICATE REQUEST\n"); + _private_tls_write_packet(tls_certificate_request(context)); + } + DEBUG_PRINT("<= SENDING CERTIFICATE\n"); + _private_tls_write_packet(tls_build_certificate(context)); + DEBUG_PRINT("<= SENDING CERTIFICATE VERIFY\n"); + _private_tls_write_packet(tls_build_certificate_verify(context)); + DEBUG_PRINT("<= SENDING FINISHED\n"); + _private_tls_write_packet(tls_build_finished(context)); + // new key + TLS_FREE(context->server_finished_hash); + context->server_finished_hash = (unsigned char *)TLS_MALLOC(_private_tls_mac_length(context)); + if (context->server_finished_hash) + _private_tls_get_hash(context, context->server_finished_hash); + break; + } +#endif + _private_tls_write_packet(tls_build_hello(context, 0)); + DEBUG_PRINT("<= SENDING CERTIFICATE\n"); + _private_tls_write_packet(tls_build_certificate(context)); + int ephemeral_cipher = tls_cipher_is_ephemeral(context); + if (ephemeral_cipher) { + DEBUG_PRINT("<= SENDING EPHEMERAL DH KEY\n"); + _private_tls_write_packet(tls_build_server_key_exchange(context, ephemeral_cipher == 1 ? KEA_dhe_rsa : KEA_ec_diffie_hellman)); + } + if (context->request_client_certificate) { + DEBUG_PRINT("<= SENDING CERTIFICATE REQUEST\n"); + _private_tls_write_packet(tls_certificate_request(context)); + } + DEBUG_PRINT("<= SENDING DONE\n"); + _private_tls_write_packet(tls_build_done(context)); + } + break; + case 3: + // finished + _private_tls_write_packet(tls_build_change_cipher_spec(context)); + _private_tls_write_packet(tls_build_finished(context)); + context->connection_status = 0xFF; + break; + case 4: + // dtls only + context->dtls_seq = 1; + _private_tls_write_packet(tls_build_hello(context, 0)); + break; +#ifdef WITH_TLS_13 + case 5: + // hello retry request + DEBUG_PRINT("<= SENDING HELLO RETRY REQUEST\n"); + _private_tls_write_packet(tls_build_hello(context, 0)); + break; +#endif + } + payload_size++; + buf += payload_size; + buf_len -= payload_size; + } + return orig_len; +} + +unsigned int _private_tls_hmac_message(unsigned char local, struct TLSContext *context, const unsigned char *buf, int buf_len, const unsigned char *buf2, int buf_len2, unsigned char *out, unsigned int outlen, uint64_t remote_sequence_number) { + hmac_state hash; + int mac_size = outlen; + int hash_idx; + if (mac_size == TLS_SHA1_MAC_SIZE) + hash_idx = find_hash("sha1"); + else + if (mac_size == TLS_SHA384_MAC_SIZE) + hash_idx = find_hash("sha384"); + else + hash_idx = find_hash("sha256"); + + if (hmac_init(&hash, hash_idx, local ? context->crypto.ctx_local_mac.local_mac : context->crypto.ctx_remote_mac.remote_mac, mac_size)) + return 0; + + uint64_t squence_number; + if (context->dtls) + squence_number = htonll(remote_sequence_number); + else + if (local) + squence_number = htonll(context->local_sequence_number); + else + squence_number = htonll(context->remote_sequence_number); + + if (hmac_process(&hash, (unsigned char *)&squence_number, sizeof(uint64_t))) + return 0; + + if (hmac_process(&hash, buf, buf_len)) + return 0; + if ((buf2) && (buf_len2)) { + if (hmac_process(&hash, buf2, buf_len2)) + return 0; + } + unsigned long ref_outlen = outlen; + if (hmac_done(&hash, out, &ref_outlen)) + return 0; + + return (unsigned int)ref_outlen; +} + +int tls_parse_message(struct TLSContext *context, unsigned char *buf, int buf_len, tls_validation_function certificate_verify) { + int res = 5; + if (context->dtls) + res = 13; + int header_size = res; + int payload_res = 0; + + CHECK_SIZE(res, buf_len, TLS_NEED_MORE_DATA) + + unsigned char type = *buf; + + int buf_pos = 1; + + unsigned short version = ntohs(*(unsigned short *)&buf[buf_pos]); + buf_pos += 2; + + uint64_t dtls_sequence_number = 0; + if (context->dtls) { + CHECK_SIZE(buf_pos + 8, buf_len, TLS_NEED_MORE_DATA) + dtls_sequence_number = ntohll(*(uint64_t *)&buf[buf_pos]); + buf_pos += 8; + } + + VERSION_SUPPORTED(version, TLS_NOT_SAFE) + unsigned short length; + length = ntohs(*(unsigned short *)&buf[buf_pos]); + buf_pos += 2; + + unsigned char *pt = NULL; + const unsigned char *ptr = buf + buf_pos; + + CHECK_SIZE(buf_pos + length, buf_len, TLS_NEED_MORE_DATA) + DEBUG_PRINT("Message type: %0x, length: %i\n", (int)type, (int)length); + if ((context->cipher_spec_set) && (type != TLS_CHANGE_CIPHER)) { + DEBUG_DUMP_HEX_LABEL("encrypted", &buf[header_size], length); + if (!context->crypto.created) { + DEBUG_PRINT("Encryption context not created\n"); + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + return TLS_BROKEN_PACKET; + } + pt = (unsigned char *)TLS_MALLOC(length); + if (!pt) { + DEBUG_PRINT("Error in TLS_MALLOC (%i bytes)\n", (int)length); + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + return TLS_NO_MEMORY; + } + + unsigned char aad[16]; + int aad_size = sizeof(aad); + unsigned char *sequence = aad; + + if (context->crypto.created == 2) { + int delta = 8; + int pt_length; + unsigned char iv[TLS_13_AES_GCM_IV_LENGTH]; + gcm_reset(&context->crypto.ctx_remote.aes_gcm_remote); + +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + aad[0] = TLS_APPLICATION_DATA; + aad[1] = 0x03; + aad[2] = 0x03; + *((unsigned short *)(aad + 3)) = htons(buf_len - header_size); + aad_size = 5; + sequence = aad + 5; + if (context->dtls) + *((uint64_t *)sequence) = *(uint64_t *)(buf + 3); + else + *((uint64_t *)sequence) = htonll(context->remote_sequence_number); + memcpy(iv, context->crypto.ctx_remote_mac.remote_iv, TLS_13_AES_GCM_IV_LENGTH); + int i; + int offset = TLS_13_AES_GCM_IV_LENGTH - 8; + for (i = 0; i < 8; i++) + iv[offset + i] = context->crypto.ctx_remote_mac.remote_iv[offset + i] ^ sequence[i]; + pt_length = buf_len - header_size - TLS_GCM_TAG_LEN; + delta = 0; + } else { +#endif + aad_size = 13; + pt_length = length - 8 - TLS_GCM_TAG_LEN; + // build aad and iv + if (context->dtls) + *((uint64_t *)aad) = htonll(dtls_sequence_number); + else + *((uint64_t *)aad) = htonll(context->remote_sequence_number); + aad[8] = buf[0]; + aad[9] = buf[1]; + aad[10] = buf[2]; + + memcpy(iv, context->crypto.ctx_remote_mac.remote_aead_iv, 4); + memcpy(iv + 4, buf + header_size, 8); + *((unsigned short *)(aad + 11)) = htons((unsigned short)pt_length); +#ifdef WITH_TLS_13 + } +#endif + if (pt_length < 0) { + DEBUG_PRINT("Invalid packet length"); + TLS_FREE(pt); + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + return TLS_BROKEN_PACKET; + } + DEBUG_DUMP_HEX_LABEL("aad", aad, aad_size); + DEBUG_DUMP_HEX_LABEL("aad iv", iv, 12); + + int res0 = gcm_add_iv(&context->crypto.ctx_remote.aes_gcm_remote, iv, 12); + int res1 = gcm_add_aad(&context->crypto.ctx_remote.aes_gcm_remote, aad, aad_size); + memset(pt, 0, length); + DEBUG_PRINT("PT SIZE: %i\n", pt_length); + int res2 = gcm_process(&context->crypto.ctx_remote.aes_gcm_remote, pt, pt_length, buf + header_size + delta, GCM_DECRYPT); + unsigned char tag[32]; + unsigned long taglen = 32; + int res3 = gcm_done(&context->crypto.ctx_remote.aes_gcm_remote, tag, &taglen); + if ((res0) || (res1) || (res2) || (res3) || (taglen != TLS_GCM_TAG_LEN)) { + DEBUG_PRINT("ERROR: gcm_add_iv: %i, gcm_add_aad: %i, gcm_process: %i, gcm_done: %i\n", res0, res1, res2, res3); + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + return TLS_BROKEN_PACKET; + } + DEBUG_DUMP_HEX_LABEL("decrypted", pt, pt_length); + DEBUG_DUMP_HEX_LABEL("tag", tag, taglen); + // check tag + if (memcmp(buf + header_size + delta + pt_length, tag, taglen)) { + DEBUG_PRINT("INTEGRITY CHECK FAILED (msg length %i)\n", pt_length); + DEBUG_DUMP_HEX_LABEL("TAG RECEIVED", buf + header_size + delta + pt_length, taglen); + DEBUG_DUMP_HEX_LABEL("TAG COMPUTED", tag, taglen); + TLS_FREE(pt); + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + _private_tls_write_packet(tls_build_alert(context, 1, bad_record_mac)); + return TLS_INTEGRITY_FAILED; + } + ptr = pt; + length = (unsigned short)pt_length; +#ifdef TLS_WITH_CHACHA20_POLY1305 + } else + if (context->crypto.created == 3) { + int pt_length = length - POLY1305_TAGLEN; + unsigned int counter = 1; + unsigned char poly1305_key[POLY1305_KEYLEN]; + unsigned char trail[16]; + unsigned char mac_tag[POLY1305_TAGLEN]; + aad_size = 16; + if (pt_length < 0) { + DEBUG_PRINT("Invalid packet length"); + TLS_FREE(pt); + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + return TLS_BROKEN_PACKET; + } +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + aad[0] = TLS_APPLICATION_DATA; + aad[1] = 0x03; + aad[2] = 0x03; + *((unsigned short *)(aad + 3)) = htons(buf_len - header_size); + aad_size = 5; + sequence = aad + 5; + if (context->dtls) + *((uint64_t *)sequence) = *(uint64_t *)(buf + 3); + else + *((uint64_t *)sequence) = htonll(context->remote_sequence_number); + } else { +#endif + if (context->dtls) + *((uint64_t *)aad) = htonll(dtls_sequence_number); + else + *((uint64_t *)aad) = htonll(context->remote_sequence_number); + aad[8] = buf[0]; + aad[9] = buf[1]; + aad[10] = buf[2]; + *((unsigned short *)(aad + 11)) = htons((unsigned short)pt_length); + aad[13] = 0; + aad[14] = 0; + aad[15] = 0; +#ifdef WITH_TLS_13 + } +#endif + + chacha_ivupdate(&context->crypto.ctx_remote.chacha_remote, context->crypto.ctx_remote_mac.remote_aead_iv, sequence, (unsigned char *)&counter); + + chacha_encrypt_bytes(&context->crypto.ctx_remote.chacha_remote, buf + header_size, pt, pt_length); + DEBUG_DUMP_HEX_LABEL("decrypted", pt, pt_length); + ptr = pt; + length = (unsigned short)pt_length; + + chacha20_poly1305_key(&context->crypto.ctx_remote.chacha_remote, poly1305_key); + poly1305_context ctx; + _private_tls_poly1305_init(&ctx, poly1305_key); + _private_tls_poly1305_update(&ctx, aad, aad_size); + static unsigned char zeropad[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int rem = aad_size % 16; + if (rem) + _private_tls_poly1305_update(&ctx, zeropad, 16 - rem); + _private_tls_poly1305_update(&ctx, buf + header_size, pt_length); + rem = pt_length % 16; + if (rem) + _private_tls_poly1305_update(&ctx, zeropad, 16 - rem); + + _private_tls_U32TO8(&trail[0], aad_size == 5 ? 5 : 13); + *(int *)&trail[4] = 0; + _private_tls_U32TO8(&trail[8], pt_length); + *(int *)&trail[12] = 0; + + _private_tls_poly1305_update(&ctx, trail, 16); + _private_tls_poly1305_finish(&ctx, mac_tag); + if (memcmp(mac_tag, buf + header_size + pt_length, POLY1305_TAGLEN)) { + DEBUG_PRINT("INTEGRITY CHECK FAILED (msg length %i)\n", length); + DEBUG_DUMP_HEX_LABEL("POLY1305 TAG RECEIVED", buf + header_size + pt_length, POLY1305_TAGLEN); + DEBUG_DUMP_HEX_LABEL("POLY1305 TAG COMPUTED", mac_tag, POLY1305_TAGLEN); + TLS_FREE(pt); + + // silently ignore packet for DTLS + if (context->dtls) + return header_size + length; + + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + _private_tls_write_packet(tls_build_alert(context, 1, bad_record_mac)); + return TLS_INTEGRITY_FAILED; + } +#endif + } else { + int err = _private_tls_crypto_decrypt(context, buf + header_size, pt, length); + if (err) { + TLS_FREE(pt); + DEBUG_PRINT("Decryption error %i\n", (int)err); + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + return TLS_BROKEN_PACKET; + } + unsigned char padding_byte = pt[length - 1]; + unsigned char padding = padding_byte + 1; + + // poodle check + int padding_index = length - padding; + if (padding_index > 0) { + int i; + int limit = length - 1; + for (i = length - padding; i < limit; i++) { + if (pt[i] != padding_byte) { + TLS_FREE(pt); + DEBUG_PRINT("BROKEN PACKET (POODLE ?)\n"); + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + _private_tls_write_packet(tls_build_alert(context, 1, decrypt_error)); + return TLS_BROKEN_PACKET; + } + } + } + + unsigned int decrypted_length = length; + if (padding < decrypted_length) + decrypted_length -= padding; + + DEBUG_DUMP_HEX_LABEL("decrypted", pt, decrypted_length); + ptr = pt; +#ifdef TLS_LEGACY_SUPPORT + if ((context->version != TLS_V10) && (decrypted_length > TLS_AES_IV_LENGTH)) { + decrypted_length -= TLS_AES_IV_LENGTH; + ptr += TLS_AES_IV_LENGTH; + } +#else + if (decrypted_length > TLS_AES_IV_LENGTH) { + decrypted_length -= TLS_AES_IV_LENGTH; + ptr += TLS_AES_IV_LENGTH; + } +#endif + length = decrypted_length; + + unsigned int mac_size = _private_tls_mac_length(context); + if ((length < mac_size) || (!mac_size)) { + TLS_FREE(pt); + DEBUG_PRINT("BROKEN PACKET\n"); + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + _private_tls_write_packet(tls_build_alert(context, 1, decrypt_error)); + return TLS_BROKEN_PACKET; + } + + length -= mac_size; + + const unsigned char *message_hmac = &ptr[length]; + unsigned char hmac_out[TLS_MAX_MAC_SIZE]; + unsigned char temp_buf[5]; + memcpy(temp_buf, buf, 3); + *(unsigned short *)(temp_buf + 3) = htons(length); + unsigned int hmac_out_len = _private_tls_hmac_message(0, context, temp_buf, 5, ptr, length, hmac_out, mac_size, dtls_sequence_number); + if ((hmac_out_len != mac_size) || (memcmp(message_hmac, hmac_out, mac_size))) { + DEBUG_PRINT("INTEGRITY CHECK FAILED (msg length %i)\n", length); + DEBUG_DUMP_HEX_LABEL("HMAC RECEIVED", message_hmac, mac_size); + DEBUG_DUMP_HEX_LABEL("HMAC COMPUTED", hmac_out, hmac_out_len); + TLS_FREE(pt); + + // silently ignore packet for DTLS + if (context->dtls) + return header_size + length; + + _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); + _private_tls_write_packet(tls_build_alert(context, 1, bad_record_mac)); + + return TLS_INTEGRITY_FAILED; + } + } + } + context->remote_sequence_number++; +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + if (/*(context->connection_status == 2) && */(type == TLS_APPLICATION_DATA) && (context->crypto.created)) { + do { + length--; + type = ptr[length]; + } while (!type); + } + } +#endif + switch (type) { + // application data + case TLS_APPLICATION_DATA: + if (context->connection_status != 0xFF) { + DEBUG_PRINT("UNEXPECTED APPLICATION DATA MESSAGE\n"); + payload_res = TLS_UNEXPECTED_MESSAGE; + _private_tls_write_packet(tls_build_alert(context, 1, unexpected_message)); + } else { + DEBUG_PRINT("APPLICATION DATA MESSAGE (TLS VERSION: %x):\n", (int)context->version); + DEBUG_DUMP(ptr, length); + DEBUG_PRINT("\n"); + _private_tls_write_app_data(context, ptr, length); + } + break; + // handshake + case TLS_HANDSHAKE: + DEBUG_PRINT("HANDSHAKE MESSAGE\n"); + payload_res = tls_parse_payload(context, ptr, length, certificate_verify); + break; + // change cipher spec + case TLS_CHANGE_CIPHER: + context->dtls_epoch_remote++; + if (context->connection_status != 2) { +#ifdef WITH_TLS_13 + if (context->connection_status == 4) { + DEBUG_PRINT("IGNORING CHANGE CIPHER SPEC MESSAGE (HELLO RETRY REQUEST)\n"); + break; + } +#endif + DEBUG_PRINT("UNEXPECTED CHANGE CIPHER SPEC MESSAGE (%i)\n", context->connection_status); + _private_tls_write_packet(tls_build_alert(context, 1, unexpected_message)); + payload_res = TLS_UNEXPECTED_MESSAGE; + } else { + DEBUG_PRINT("CHANGE CIPHER SPEC MESSAGE\n"); + context->cipher_spec_set = 1; + // reset sequence numbers + context->remote_sequence_number = 0; + } +#ifdef WITH_TLS_13 + if (!context->is_server) + _private_tls13_key(context, 1); +#endif + break; + // alert + case TLS_ALERT: + DEBUG_PRINT("ALERT MESSAGE\n"); + if (length >= 2) { + DEBUG_DUMP_HEX(ptr, length); + int level = ptr[0]; + int code = ptr[1]; + if (level == TLS_ALERT_CRITICAL) { + context->critical_error = 1; + res = TLS_ERROR_ALERT; + } + context->error_code = code; + } + break; + default: + DEBUG_PRINT("NOT UNDERSTOOD MESSAGE TYPE: %x\n", (int)type); + TLS_FREE(pt); + return TLS_NOT_UNDERSTOOD; + } + TLS_FREE(pt); + + if (payload_res < 0) + return payload_res; + + if (res > 0) + return header_size + length; + + return res; +} + +unsigned int asn1_get_len(const unsigned char *buffer, int buf_len, unsigned int *octets) { + *octets = 0; + + if (buf_len < 1) + return 0; + + unsigned char size = buffer[0]; + int i; + if (size & 0x80) { + *octets = size & 0x7F; + if ((int)*octets > buf_len - 1) + return 0; + // max 32 bits + unsigned int ref_octets = *octets; + if (*octets > 4) + ref_octets = 4; + if ((int)*octets > buf_len -1) + return 0; + unsigned int long_size = 0; + unsigned int coef = 1; + + for (i = ref_octets; i > 0; i--) { + long_size += buffer[i] * coef; + coef *= 0x100; + } + ++*octets; + return long_size; + } + ++*octets; + return size; +} + +void print_index(const unsigned int *fields) { + int i = 0; + while (fields[i]) { + if (i) + DEBUG_PRINT("."); + DEBUG_PRINT("%i", fields[i]); + i++; + } + while (i < 6) { + DEBUG_PRINT(" "); + i++; + } +} + +int _is_field(const unsigned int *fields, const unsigned int *prefix) { + int i = 0; + while (prefix[i]) { + if (fields[i] != prefix[i]) + return 0; + i++; + } + return 1; +} + +int _private_tls_hash_len(int algorithm) { + switch (algorithm) { + case TLS_RSA_SIGN_MD5: + return 16; + case TLS_RSA_SIGN_SHA1: + return 20; + case TLS_RSA_SIGN_SHA256: + case TLS_ECDSA_SIGN_SHA256: + return 32; + case TLS_RSA_SIGN_SHA384: + return 48; + case TLS_RSA_SIGN_SHA512: + return 64; + } + return 0; +} + +unsigned char *_private_tls_compute_hash(int algorithm, const unsigned char *message, unsigned int message_len) { + unsigned char *hash = NULL; + if ((!message) || (!message_len)) + return hash; + int err; + hash_state state; + switch (algorithm) { + case TLS_RSA_SIGN_MD5: + DEBUG_PRINT("SIGN MD5\n"); + hash = (unsigned char *)TLS_MALLOC(16); + if (!hash) + return NULL; + + err = md5_init(&state); + if (!err) { + err = md5_process(&state, message, message_len); + if (!err) + err = md5_done(&state, hash); + } + break; + case TLS_RSA_SIGN_SHA1: + DEBUG_PRINT("SIGN SHA1\n"); + hash = (unsigned char *)TLS_MALLOC(20); + if (!hash) + return NULL; + + err = sha1_init(&state); + if (!err) { + err = sha1_process(&state, message, message_len); + if (!err) + err = sha1_done(&state, hash); + } + break; + case TLS_RSA_SIGN_SHA256: + case TLS_ECDSA_SIGN_SHA256: + DEBUG_PRINT("SIGN SHA256\n"); + hash = (unsigned char *)TLS_MALLOC(32); + if (!hash) + return NULL; + + err = sha256_init(&state); + if (!err) { + err = sha256_process(&state, message, message_len); + if (!err) + err = sha256_done(&state, hash); + } + break; + case TLS_RSA_SIGN_SHA384: + DEBUG_PRINT("SIGN SHA384\n"); + hash = (unsigned char *)TLS_MALLOC(48); + if (!hash) + return NULL; + + err = sha384_init(&state); + if (!err) { + err = sha384_process(&state, message, message_len); + if (!err) + err = sha384_done(&state, hash); + } + break; + case TLS_RSA_SIGN_SHA512: + DEBUG_PRINT("SIGN SHA512\n"); + hash = (unsigned char *)TLS_MALLOC(64); + if (!hash) + return NULL; + + err = sha512_init(&state); + if (!err) { + err = sha512_process(&state, message, message_len); + if (!err) + err = sha512_done(&state, hash); + } + break; + default: + DEBUG_PRINT("UNKNOWN SIGNATURE ALGORITHM\n"); + } + return hash; +} + +int tls_certificate_verify_signature(struct TLSCertificate *cert, struct TLSCertificate *parent) { + if ((!cert) || (!parent) || (!cert->sign_key) || (!cert->fingerprint) || (!cert->sign_len) || (!parent->der_bytes) || (!parent->der_len)) { + DEBUG_PRINT("CANNOT VERIFY SIGNATURE"); + return 0; + } + tls_init(); + int hash_len = _private_tls_hash_len(cert->algorithm); + if (hash_len <= 0) + return 0; + + int hash_index = -1; + switch (cert->algorithm) { + case TLS_RSA_SIGN_MD5: + hash_index = find_hash("md5"); + break; + case TLS_RSA_SIGN_SHA1: + hash_index = find_hash("sha1"); + break; + case TLS_RSA_SIGN_SHA256: + case TLS_ECDSA_SIGN_SHA256: + hash_index = find_hash("sha256"); + break; + case TLS_RSA_SIGN_SHA384: + hash_index = find_hash("sha384"); + break; + case TLS_RSA_SIGN_SHA512: + hash_index = find_hash("sha512"); + break; + default: + DEBUG_PRINT("UNKNOWN SIGNATURE ALGORITHM\n"); + return 0; + } +#ifdef TLS_ECDSA_SUPPORTED + if (cert->algorithm == TLS_ECDSA_SIGN_SHA256) { + ecc_key key; + int err = ecc_import(parent->der_bytes, parent->der_len, &key); + if (err) { + DEBUG_PRINT("Error importing ECC certificate (code: %i)\n", err); + DEBUG_DUMP_HEX_LABEL("CERTIFICATE", parent->der_bytes, parent->der_len); + return 0; + } + int ecc_stat = 0; + unsigned char *signature = cert->sign_key; + int signature_len = cert->sign_len; + if (!signature[0]) { + signature++; + signature_len--; + } + err = ecc_verify_hash(signature, signature_len, cert->fingerprint, hash_len, &ecc_stat, &key); + ecc_free(&key); + if (err) { + DEBUG_PRINT("ECC HASH VERIFY ERROR %i\n", err); + return 0; + } + DEBUG_PRINT("ECC CERTIFICATE VALIDATION: %i\n", ecc_stat); + return ecc_stat; + } +#endif + + rsa_key key; + int err = rsa_import(parent->der_bytes, parent->der_len, &key); + if (err) { + DEBUG_PRINT("Error importing RSA certificate (code: %i)\n", err); + DEBUG_DUMP_HEX_LABEL("CERTIFICATE", parent->der_bytes, parent->der_len); + return 0; + } + int rsa_stat = 0; + unsigned char *signature = cert->sign_key; + int signature_len = cert->sign_len; + if (!signature[0]) { + signature++; + signature_len--; + } + err = rsa_verify_hash_ex(signature, signature_len, cert->fingerprint, hash_len, LTC_PKCS_1_V1_5, hash_index, 0, &rsa_stat, &key); + rsa_free(&key); + if (err) { + DEBUG_PRINT("HASH VERIFY ERROR %i\n", err); + return 0; + } + DEBUG_PRINT("CERTIFICATE VALIDATION: %i\n", rsa_stat); + return rsa_stat; +} + +int tls_certificate_chain_is_valid(struct TLSCertificate **certificates, int len) { + if ((!certificates) || (!len)) + return bad_certificate; + + int i; + len--; + + // expired certificate or not yet valid ? + if (tls_certificate_is_valid(certificates[0])) + return bad_certificate; + + // check + for (i = 0; i < len; i++) { + // certificate in chain is expired ? + if (tls_certificate_is_valid(certificates[i+1])) + return bad_certificate; + if (!tls_certificate_verify_signature(certificates[i], certificates[i+1])) + return bad_certificate; + } + return 0; +} + +int tls_certificate_chain_is_valid_root(struct TLSContext *context, struct TLSCertificate **certificates, int len) { + if ((!certificates) || (!len) || (!context->root_certificates) || (!context->root_count)) + return bad_certificate; + int i; + unsigned int j; + for (i = 0; i < len; i++) { + for (j = 0; j < context->root_count; j++) { + // check if root certificate expired + if (tls_certificate_is_valid(context->root_certificates[j])) + continue; + // if any root validates any certificate in the chain, then is root validated + if (tls_certificate_verify_signature(certificates[i], context->root_certificates[j])) + return 0; + } + } + return bad_certificate; +} + +int _private_is_oid(struct _private_OID_chain *ref_chain, const unsigned char *looked_oid, int looked_oid_len) { + while (ref_chain) { + if (ref_chain->oid) { + if (_is_oid2(ref_chain->oid, looked_oid, 16, looked_oid_len)) + return 1; + } + ref_chain = (struct _private_OID_chain *)ref_chain->top; + } + return 0; +} + +int _private_asn1_parse(struct TLSContext *context, struct TLSCertificate *cert, const unsigned char *buffer, unsigned int size, int level, unsigned int *fields, unsigned char *has_key, int client_cert, unsigned char *top_oid, struct _private_OID_chain *chain) { + struct _private_OID_chain local_chain; + local_chain.top = chain; + unsigned int pos = 0; + // X.690 + int idx = 0; + unsigned char oid[16]; + memset(oid, 0, 16); + local_chain.oid = oid; + if (has_key) + *has_key = 0; + unsigned char local_has_key = 0; + const unsigned char *cert_data = NULL; + unsigned int cert_len = 0; + while (pos < size) { + unsigned int start_pos = pos; + CHECK_SIZE(2, size - pos, TLS_NEED_MORE_DATA) + unsigned char first = buffer[pos++]; + unsigned char type = first & 0x1F; + unsigned char constructed = first & 0x20; + unsigned char element_class = first >> 6; + unsigned int octets = 0; + unsigned int temp; + idx++; + if (level <= TLS_ASN1_MAXLEVEL) + fields[level - 1] = idx; + unsigned int length = asn1_get_len((unsigned char *)&buffer[pos], size - pos, &octets); + if ((octets > 4) || (octets > size - pos)) { + DEBUG_PRINT("CANNOT READ CERTIFICATE\n"); + return pos; + } + pos += octets; + CHECK_SIZE(length, size - pos, TLS_NEED_MORE_DATA) + //DEBUG_PRINT("FIRST: %x => %x (%i)\n", (int)first, (int)type, length); + // sequence + //DEBUG_PRINT("%2i: ", level); +#ifdef DEBUG + DEBUG_INDEX(fields); + int i1; + for (i1 = 1; i1 < level; i1++) + DEBUG_PRINT(" "); +#endif + + if ((length) && (constructed)) { + switch (type) { + case 0x03: + DEBUG_PRINT("CONSTRUCTED BITSTREAM\n"); + break; + case 0x10: + DEBUG_PRINT("SEQUENCE\n"); + if ((level == 2) && (idx == 1)) { + cert_len = length + (pos - start_pos); + cert_data = &buffer[start_pos]; + } + // private key on server or public key on client + if ((!cert->version) && (_is_field(fields, priv_der_id))) { + TLS_FREE(cert->der_bytes); + temp = length + (pos - start_pos); + cert->der_bytes = (unsigned char *)TLS_MALLOC(temp); + if (cert->der_bytes) { + memcpy(cert->der_bytes, &buffer[start_pos], temp); + cert->der_len = temp; + } else + cert->der_len = 0; + } + break; + case 0x11: + DEBUG_PRINT("EMBEDDED PDV\n"); + break; + case 0x00: + if (element_class == 0x02) { + DEBUG_PRINT("CONTEXT-SPECIFIC\n"); + break; + } + default: + DEBUG_PRINT("CONSTRUCT TYPE %02X\n", (int)type); + } + local_has_key = 0; + _private_asn1_parse(context, cert, &buffer[pos], length, level + 1, fields, &local_has_key, client_cert, top_oid, &local_chain); + if ((((local_has_key) && (context) && ((!context->is_server) || (client_cert))) || (!context)) && (_is_field(fields, pk_id))) { + TLS_FREE(cert->der_bytes); + temp = length + (pos - start_pos); + cert->der_bytes = (unsigned char *)TLS_MALLOC(temp); + if (cert->der_bytes) { + memcpy(cert->der_bytes, &buffer[start_pos], temp); + cert->der_len = temp; + } else + cert->der_len = 0; + } + } else { + switch (type) { + case 0x00: + // end of content + DEBUG_PRINT("END OF CONTENT\n"); + return pos; + break; + case 0x01: + // boolean + temp = buffer[pos]; + DEBUG_PRINT("BOOLEAN: %i\n", temp); + break; + case 0x02: + // integer + if (_is_field(fields, pk_id)) { + if (has_key) + *has_key = 1; + + if (idx == 1) + tls_certificate_set_key(cert, &buffer[pos], length); + else + if (idx == 2) + tls_certificate_set_exponent(cert, &buffer[pos], length); + } else + if (_is_field(fields, serial_id)) + tls_certificate_set_serial(cert, &buffer[pos], length); + if (_is_field(fields, version_id)) { + if (length == 1) + cert->version = buffer[pos]; +#ifdef TLS_X509_V1_SUPPORT + else + cert->version = 0; + idx++; +#endif + } + if (level >= 2) { + unsigned int fields_temp[3]; + fields_temp[0] = fields[level - 2]; + fields_temp[1] = fields[level - 1]; + fields_temp[2] = 0; + if (_is_field(fields_temp, priv_id)) + tls_certificate_set_priv(cert, &buffer[pos], length); + } + DEBUG_PRINT("INTEGER(%i): ", length); + DEBUG_DUMP_HEX(&buffer[pos], length); + if ((chain) && (length > 2)) { + if (_private_is_oid(chain, san_oid, sizeof(san_oid) - 1)) { + cert->san = (unsigned char **)TLS_REALLOC(cert->san, sizeof(unsigned char *) * (cert->san_length + 1)); + if (cert->san) { + cert->san[cert->san_length] = NULL; + tls_certificate_set_copy(&cert->san[cert->san_length], &buffer[pos], length); + DEBUG_PRINT(" => SUBJECT ALTERNATIVE NAME: %s", cert->san[cert->san_length ]); + cert->san_length++; + } else + cert->san_length = 0; + } + } + DEBUG_PRINT("\n"); + break; + case 0x03: + if (_is_field(fields, pk_id)) { + if (has_key) + *has_key = 1; + } + // bitstream + DEBUG_PRINT("BITSTREAM(%i): ", length); + DEBUG_DUMP_HEX(&buffer[pos], length); + DEBUG_PRINT("\n"); + if (_is_field(fields, sign_id)) { + tls_certificate_set_sign_key(cert, &buffer[pos], length); + } else + if ((cert->ec_algorithm) && (_is_field(fields, pk_id))) { + tls_certificate_set_key(cert, &buffer[pos], length); + } else { + if ((buffer[pos] == 0x00) && (length > 256)) + _private_asn1_parse(context, cert, &buffer[pos]+1, length - 1, level + 1, fields, &local_has_key, client_cert, top_oid, &local_chain); + else + _private_asn1_parse(context, cert, &buffer[pos], length, level + 1, fields, &local_has_key, client_cert, top_oid, &local_chain); +#ifdef TLS_FORWARD_SECRECY + #ifdef TLS_ECDSA_SUPPORTED + if (top_oid) { + if (_is_oid2(top_oid, TLS_EC_prime256v1_OID, sizeof(oid), sizeof(TLS_EC_prime256v1) - 1)) { + cert->ec_algorithm = secp256r1.iana; + } else + if (_is_oid2(top_oid, TLS_EC_secp224r1_OID, sizeof(oid), sizeof(TLS_EC_secp224r1_OID) - 1)) { + cert->ec_algorithm = secp224r1.iana; + } else + if (_is_oid2(top_oid, TLS_EC_secp384r1_OID, sizeof(oid), sizeof(TLS_EC_secp384r1_OID) - 1)) { + cert->ec_algorithm = secp384r1.iana; + } else + if (_is_oid2(top_oid, TLS_EC_secp521r1_OID, sizeof(oid), sizeof(TLS_EC_secp521r1_OID) - 1)) { + cert->ec_algorithm = secp521r1.iana; + } + if ((cert->ec_algorithm) && (!cert->pk)) + tls_certificate_set_key(cert, &buffer[pos], length); + } + #endif +#endif + } + break; + case 0x04: + if ((top_oid) && (_is_field(fields, ecc_priv_id)) && (!cert->priv)) { + DEBUG_PRINT("BINARY STRING(%i): ", length); + DEBUG_DUMP_HEX(&buffer[pos], length); + DEBUG_PRINT("\n"); + tls_certificate_set_priv(cert, &buffer[pos], length); + } else + _private_asn1_parse(context, cert, &buffer[pos], length, level + 1, fields, &local_has_key, client_cert, top_oid, &local_chain); + break; + case 0x05: + DEBUG_PRINT("NULL\n"); + break; + case 0x06: + // object identifier + if (_is_field(fields, pk_id)) { +#ifdef TLS_ECDSA_SUPPORTED + if ((length == 8) || (length == 5)) + tls_certificate_set_algorithm(context, &cert->ec_algorithm, &buffer[pos], length); + else +#endif + tls_certificate_set_algorithm(context, &cert->key_algorithm, &buffer[pos], length); + } + if (_is_field(fields, algorithm_id)) + tls_certificate_set_algorithm(context, &cert->algorithm, &buffer[pos], length); + + DEBUG_PRINT("OBJECT IDENTIFIER(%i): ", length); + DEBUG_DUMP_HEX(&buffer[pos], length); + DEBUG_PRINT("\n"); + // check previous oid + if (_is_oid2(oid, ocsp_oid, 16, sizeof(ocsp_oid) - 1)) + tls_certificate_set_copy(&cert->ocsp, &buffer[pos], length); + + if (length < 16) + memcpy(oid, &buffer[pos], length); + else + memcpy(oid, &buffer[pos], 16); + if (top_oid) + memcpy(top_oid, oid, 16); + break; + case 0x09: + DEBUG_PRINT("REAL NUMBER(%i): ", length); + DEBUG_DUMP_HEX(&buffer[pos], length); + DEBUG_PRINT("\n"); + break; + case 0x17: + // utc time + DEBUG_PRINT("UTC TIME: ["); + DEBUG_DUMP(&buffer[pos], length); + DEBUG_PRINT("]\n"); + + if (_is_field(fields, validity_id)) { + if (idx == 1) + tls_certificate_set_copy_date(&cert->not_before, &buffer[pos], length); + else + tls_certificate_set_copy_date(&cert->not_after, &buffer[pos], length); + } + break; + case 0x18: + // generalized time + DEBUG_PRINT("GENERALIZED TIME: ["); + DEBUG_DUMP(&buffer[pos], length); + DEBUG_PRINT("]\n"); + break; + case 0x13: + // printable string + case 0x0C: + case 0x14: + case 0x15: + case 0x16: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + if (_is_field(fields, issurer_id)) { + if (_is_oid(oid, country_oid, 3)) + tls_certificate_set_copy(&cert->issuer_country, &buffer[pos], length); + else + if (_is_oid(oid, state_oid, 3)) + tls_certificate_set_copy(&cert->issuer_state, &buffer[pos], length); + else + if (_is_oid(oid, location_oid, 3)) + tls_certificate_set_copy(&cert->issuer_location, &buffer[pos], length); + else + if (_is_oid(oid, entity_oid, 3)) + tls_certificate_set_copy(&cert->issuer_entity, &buffer[pos], length); + else + if (_is_oid(oid, subject_oid, 3)) + tls_certificate_set_copy(&cert->issuer_subject, &buffer[pos], length); + } else + if (_is_field(fields, owner_id)) { + if (_is_oid(oid, country_oid, 3)) + tls_certificate_set_copy(&cert->country, &buffer[pos], length); + else + if (_is_oid(oid, state_oid, 3)) + tls_certificate_set_copy(&cert->state, &buffer[pos], length); + else + if (_is_oid(oid, location_oid, 3)) + tls_certificate_set_copy(&cert->location, &buffer[pos], length); + else + if (_is_oid(oid, entity_oid, 3)) + tls_certificate_set_copy(&cert->entity, &buffer[pos], length); + else + if (_is_oid(oid, subject_oid, 3)) + tls_certificate_set_copy(&cert->subject, &buffer[pos], length); + } + DEBUG_PRINT("STR: ["); + DEBUG_DUMP(&buffer[pos], length); + DEBUG_PRINT("]\n"); + break; + case 0x10: + DEBUG_PRINT("EMPTY SEQUENCE\n"); + break; + case 0xA: + DEBUG_PRINT("ENUMERATED(%i): ", length); + DEBUG_DUMP_HEX(&buffer[pos], length); + DEBUG_PRINT("\n"); + break; + default: + DEBUG_PRINT("========> NOT SUPPORTED %x\n", (int)type); + // not supported / needed + break; + } + } + pos += length; + } + if ((level == 2) && (cert->sign_key) && (cert->sign_len) && (cert_len) && (cert_data)) { + TLS_FREE(cert->fingerprint); + cert->fingerprint = _private_tls_compute_hash(cert->algorithm, cert_data, cert_len); +#ifdef DEBUG + if (cert->fingerprint) { + DEBUG_DUMP_HEX_LABEL("FINGERPRINT", cert->fingerprint, _private_tls_hash_len(cert->algorithm)); + } +#endif + } + return pos; +} + +struct TLSCertificate *asn1_parse(struct TLSContext *context, const unsigned char *buffer, unsigned int size, int client_cert) { + unsigned int fields[TLS_ASN1_MAXLEVEL]; + memset(fields, 0, sizeof(int) * TLS_ASN1_MAXLEVEL); + struct TLSCertificate *cert = tls_create_certificate(); + if (cert) { + if (client_cert < 0) { + client_cert = 0; + // private key + unsigned char top_oid[16]; + memset(top_oid, 0, sizeof(top_oid)); + _private_asn1_parse(context, cert, buffer, size, 1, fields, NULL, client_cert, top_oid, NULL); + } else + _private_asn1_parse(context, cert, buffer, size, 1, fields, NULL, client_cert, NULL, NULL); + } + return cert; +} + +int tls_load_certificates(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size) { + if (!context) + return TLS_GENERIC_ERROR; + + unsigned int len; + int idx = 0; + do { + unsigned char *data = tls_pem_decode(pem_buffer, pem_size, idx++, &len); + if ((!data) || (!len)) + break; + struct TLSCertificate *cert = asn1_parse(context, data, len, 0); + if (cert) { + if ((cert->version == 2) +#ifdef TLS_X509_V1_SUPPORT + || (cert->version == 0) +#endif + ) { + TLS_FREE(cert->der_bytes); + cert->der_bytes = data; + cert->der_len = len; + data = NULL; + if (cert->priv) { + DEBUG_PRINT("WARNING - parse error (private key encountered in certificate)\n"); + TLS_FREE(cert->priv); + cert->priv = NULL; + cert->priv_len = 0; + } + if (context->is_server) { + context->certificates = (struct TLSCertificate **)TLS_REALLOC(context->certificates, (context->certificates_count + 1) * sizeof(struct TLSCertificate *)); + context->certificates[context->certificates_count] = cert; + context->certificates_count++; + DEBUG_PRINT("Loaded certificate: %i\n", (int)context->certificates_count); + } else { + context->client_certificates = (struct TLSCertificate **)TLS_REALLOC(context->client_certificates, (context->client_certificates_count + 1) * sizeof(struct TLSCertificate *)); + context->client_certificates[context->client_certificates_count] = cert; + context->client_certificates_count++; + DEBUG_PRINT("Loaded client certificate: %i\n", (int)context->client_certificates_count); + } + } else { + DEBUG_PRINT("WARNING - certificate version error (v%i)\n", (int)cert->version); + tls_destroy_certificate(cert); + } + } + TLS_FREE(data); + } while (1); + return context->certificates_count; +} + +int tls_load_private_key(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size) { + if (!context) + return TLS_GENERIC_ERROR; + + unsigned int len; + int idx = 0; + do { + unsigned char *data = tls_pem_decode(pem_buffer, pem_size, idx++, &len); + if ((!data) || (!len)) + break; + struct TLSCertificate *cert = asn1_parse(context, data, len, -1); + if (cert) { + if (!cert->der_len) { + TLS_FREE(cert->der_bytes); + cert->der_bytes = data; + cert->der_len = len; + } else + TLS_FREE(data); + if ((cert) && (cert->priv) && (cert->priv_len)) { +#ifdef TLS_ECDSA_SUPPORTED + if (cert->ec_algorithm) { + DEBUG_PRINT("Loaded ECC private key\n"); + if (context->ec_private_key) + tls_destroy_certificate(context->ec_private_key); + context->ec_private_key = cert; + return 1; + } else +#endif + { + DEBUG_PRINT("Loaded private key\n"); + if (context->private_key) + tls_destroy_certificate(context->private_key); + context->private_key = cert; + return 1; + } + } + tls_destroy_certificate(cert); + } else + TLS_FREE(data); + } while (1); + return 0; +} + +int tls_clear_certificates(struct TLSContext *context) { + unsigned int i; + if ((!context) || (!context->is_server) || (context->is_child)) + return TLS_GENERIC_ERROR; + + if (context->root_certificates) { + for (i = 0; i < context->root_count; i++) + tls_destroy_certificate(context->root_certificates[i]); + } + context->root_certificates = NULL; + context->root_count = 0; + if (context->private_key) + tls_destroy_certificate(context->private_key); + context->private_key = NULL; +#ifdef TLS_ECDSA_SUPPORTED + if (context->ec_private_key) + tls_destroy_certificate(context->ec_private_key); + context->ec_private_key = NULL; +#endif + TLS_FREE(context->certificates); + context->certificates = NULL; + context->certificates_count = 0; + return 0; +} + +#ifdef WITH_TLS_13 +struct TLSPacket *tls_build_certificate_verify(struct TLSContext *context) { + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0); + //certificate verify + tls_packet_uint8(packet, 0x0F); + unsigned int size_offset = packet->len; + tls_packet_uint24(packet, 0); + + unsigned char out[TLS_MAX_RSA_KEY]; +#ifdef TLS_ECDSA_SUPPORTED + unsigned long out_len = TLS_MAX_RSA_KEY; +#endif + + unsigned char signing_data[TLS_MAX_HASH_SIZE + 98]; + int signing_data_len; + + // first 64 bytes to 0x20 (32) + memset(signing_data, 0x20, 64); + // context string 33 bytes + if (context->is_server) + memcpy(signing_data + 64, "TLS 1.3, server CertificateVerify", 33); + else + memcpy(signing_data + 64, "TLS 1.3, client CertificateVerify", 33); + // a single 0 byte separator + signing_data[97] = 0; + signing_data_len = 98; + + signing_data_len += _private_tls_get_hash(context, signing_data + 98); + DEBUG_DUMP_HEX_LABEL("verify data", signing_data, signing_data_len); + int hash_algorithm = sha256; +#ifdef TLS_ECDSA_SUPPORTED + if (tls_is_ecdsa(context)) { + switch (context->ec_private_key->ec_algorithm) { + case 23: + // secp256r1 + sha256 + tls_packet_uint16(packet, 0x0403); + break; + case 24: + // secp384r1 + sha384 + tls_packet_uint16(packet, 0x0503); + hash_algorithm = sha384; + break; + case 25: + // secp521r1 + sha512 + tls_packet_uint16(packet, 0x0603); + hash_algorithm = sha512; + break; + default: + DEBUG_PRINT("UNSUPPORTED CURVE (SIGNING)\n"); + packet->broken = 1; + return packet; + } + } else +#endif + { + tls_packet_uint16(packet, 0x0804); + } + + int packet_size = 2; +#ifdef TLS_ECDSA_SUPPORTED + if (tls_is_ecdsa(context)) { + if (_private_tls_sign_ecdsa(context, hash_algorithm, signing_data, signing_data_len, out, &out_len) == 1) { + DEBUG_PRINT("ECDSA signing OK! (ECDSA, length %lu)\n", out_len); + tls_packet_uint16(packet, out_len); + tls_packet_append(packet, out, out_len); + packet_size += out_len + 2; + } + } else +#endif + if (_private_tls_sign_rsa(context, hash_algorithm, signing_data, signing_data_len, out, &out_len) == 1) { + DEBUG_PRINT("RSA signing OK! (length %lu)\n", out_len); + tls_packet_uint16(packet, out_len); + tls_packet_append(packet, out, out_len); + packet_size += out_len + 2; + } + packet->buf[size_offset] = packet_size / 0x10000; + packet_size %= 0x10000; + packet->buf[size_offset + 1] = packet_size / 0x100; + packet_size %= 0x100; + packet->buf[size_offset + 2] = packet_size; + + tls_packet_update(packet); + return packet; +} +#endif + +struct TLSPacket *tls_build_certificate(struct TLSContext *context) { + int i; + unsigned int all_certificate_size = 0; + int certificates_count; + struct TLSCertificate **certificates; + if (context->is_server) { + certificates_count = context->certificates_count; + certificates = context->certificates; + } else { + certificates_count = context->client_certificates_count; + certificates = context->client_certificates; + } + int delta = 3; +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + delta = 5; +#endif +#ifdef TLS_ECDSA_SUPPORTED + int is_ecdsa = tls_is_ecdsa(context); + if (is_ecdsa) { + for (i = 0; i < certificates_count; i++) { + struct TLSCertificate *cert = certificates[i]; + if ((cert) && (cert->der_len) && (cert->ec_algorithm)) + all_certificate_size += cert->der_len + delta; + } + } else { + for (i = 0; i < certificates_count; i++) { + struct TLSCertificate *cert = certificates[i]; + if ((cert) && (cert->der_len) && (!cert->ec_algorithm)) + all_certificate_size += cert->der_len + delta; + } + } +#else + for (i = 0; i < certificates_count; i++) { + struct TLSCertificate *cert = certificates[i]; + if ((cert) && (cert->der_len)) + all_certificate_size += cert->der_len + delta; + } +#endif + if (!all_certificate_size) { + DEBUG_PRINT("NO CERTIFICATE SET\n"); + } + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0); + tls_packet_uint8(packet, 0x0B); + if (all_certificate_size) { +#ifdef WITH_TLS_13 + // context + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + tls_packet_uint24(packet, all_certificate_size + 4); + tls_packet_uint8(packet, 0); + } else +#endif + tls_packet_uint24(packet, all_certificate_size + 3); + + if (context->dtls) + _private_dtls_handshake_data(context, packet, all_certificate_size + 3); + + tls_packet_uint24(packet, all_certificate_size); + for (i = 0; i < certificates_count; i++) { + struct TLSCertificate *cert = certificates[i]; + if ((cert) && (cert->der_len)) { +#ifdef TLS_ECDSA_SUPPORTED + // is RSA certificate ? + if ((is_ecdsa) && (!cert->ec_algorithm)) + continue; + // is ECC certificate ? + if ((!is_ecdsa) && (cert->ec_algorithm)) + continue; +#endif + // 2 times -> one certificate + tls_packet_uint24(packet, cert->der_len); + tls_packet_append(packet, cert->der_bytes, cert->der_len); +#ifdef WITH_TLS_13 + // extension + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + tls_packet_uint16(packet, 0); +#endif + } + } + } else { + tls_packet_uint24(packet, all_certificate_size); +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + tls_packet_uint8(packet, 0); +#endif + + if (context->dtls) + _private_dtls_handshake_data(context, packet, all_certificate_size); + } + tls_packet_update(packet); + if (context->dtls) + context->dtls_seq++; + return packet; +} + +#ifdef WITH_TLS_13 +struct TLSPacket *tls_build_encrypted_extensions(struct TLSContext *context) { + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 3); + tls_packet_uint8(packet, 0x08); + if (context->negotiated_alpn) { + int alpn_negotiated_len = strlen(context->negotiated_alpn); + int alpn_len = alpn_negotiated_len + 1; + + tls_packet_uint24(packet, alpn_len + 8); + tls_packet_uint16(packet, alpn_len + 6); + tls_packet_uint16(packet, 0x10); + tls_packet_uint16(packet, alpn_len + 2); + tls_packet_uint16(packet, alpn_len); + + tls_packet_uint8(packet, alpn_negotiated_len); + tls_packet_append(packet, (unsigned char *)context->negotiated_alpn, alpn_negotiated_len); + } else { + tls_packet_uint24(packet, 2); + tls_packet_uint16(packet, 0); + } + tls_packet_update(packet); + return packet; +} +#endif + +struct TLSPacket *tls_build_finished(struct TLSContext *context) { + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, TLS_MIN_FINISHED_OPAQUE_LEN + 64); + tls_packet_uint8(packet, 0x14); +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) + tls_packet_uint24(packet, _private_tls_mac_length(context)); + else +#endif + tls_packet_uint24(packet, TLS_MIN_FINISHED_OPAQUE_LEN); + if (context->dtls) + _private_dtls_handshake_data(context, packet, TLS_MIN_FINISHED_OPAQUE_LEN); + // verify + unsigned char hash[TLS_MAX_HASH_SIZE]; + unsigned long out_size = TLS_MIN_FINISHED_OPAQUE_LEN; +#ifdef WITH_TLS_13 + unsigned char out[TLS_MAX_HASH_SIZE]; +#else + unsigned char out[TLS_MIN_FINISHED_OPAQUE_LEN]; +#endif + unsigned int hash_len; + + // server verifies client's message + if (context->is_server) { +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + hash_len = _private_tls_get_hash(context, hash); + if ((!context->finished_key) || (!hash_len)) { + DEBUG_PRINT("NO FINISHED KEY COMPUTED OR NO HANDSHAKE HASH\n"); + packet->broken = 1; + return packet; + } + + DEBUG_DUMP_HEX_LABEL("HS HASH", hash, hash_len); + DEBUG_DUMP_HEX_LABEL("HS FINISH", context->finished_key, hash_len); + DEBUG_DUMP_HEX_LABEL("HS REMOTE FINISH", context->remote_finished_key, hash_len); + + out_size = hash_len; + hmac_state hmac; + hmac_init(&hmac, _private_tls_get_hash_idx(context), context->finished_key, hash_len); + hmac_process(&hmac, hash, hash_len); + hmac_done(&hmac, out, &out_size); + } else +#endif + { + hash_len = _private_tls_done_hash(context, hash); + _private_tls_prf(context, out, TLS_MIN_FINISHED_OPAQUE_LEN, context->master_key, context->master_key_len, (unsigned char *)"server finished", 15, hash, hash_len, NULL, 0); + _private_tls_destroy_hash(context); + } + } else { +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + hash_len = _private_tls_get_hash(context, hash); + if ((!context->finished_key) || (!hash_len)) { + DEBUG_PRINT("NO FINISHED KEY COMPUTED OR NO HANDSHAKE HASH\n"); + packet->broken = 1; + return packet; + } + DEBUG_DUMP_HEX_LABEL("HS HASH", hash, hash_len); + DEBUG_DUMP_HEX_LABEL("HS FINISH", context->finished_key, hash_len); + DEBUG_DUMP_HEX_LABEL("HS REMOTE FINISH", context->remote_finished_key, hash_len); + + TLS_FREE(context->server_finished_hash); + context->server_finished_hash = (unsigned char *)TLS_MALLOC(hash_len); + if (context->server_finished_hash) + memcpy(context->server_finished_hash, hash, hash_len); + + out_size = hash_len; + hmac_state hmac; + hmac_init(&hmac, _private_tls_get_hash_idx(context), context->finished_key, hash_len); + hmac_process(&hmac, hash, hash_len); + hmac_done(&hmac, out, &out_size); + } else { +#endif + hash_len = _private_tls_get_hash(context, hash); + _private_tls_prf(context, out, TLS_MIN_FINISHED_OPAQUE_LEN, context->master_key, context->master_key_len, (unsigned char *)"client finished", 15, hash, hash_len, NULL, 0); +#ifdef WITH_TLS_13 + } +#endif + } + tls_packet_append(packet, out, out_size); + tls_packet_update(packet); + DEBUG_DUMP_HEX_LABEL("VERIFY DATA", out, out_size); +#ifdef TLS_ACCEPT_SECURE_RENEGOTIATION + if (context->is_server) { + // concatenate client verify and server verify + context->verify_data = (unsigned char *)TLS_REALLOC(context->verify_data, out_size); + if (context->verify_data) { + memcpy(context->verify_data + context->verify_len, out, out_size); + context->verify_len += out_size; + } else + context->verify_len = 0; + } else { + TLS_FREE(context->verify_data); + context->verify_data = (unsigned char *)TLS_MALLOC(out_size); + if (context->verify_data) { + memcpy(context->verify_data, out, out_size); + context->verify_len = out_size; + } + } +#endif + return packet; +} + +struct TLSPacket *tls_build_change_cipher_spec(struct TLSContext *context) { + struct TLSPacket *packet = tls_create_packet(context, TLS_CHANGE_CIPHER, context->version, 64); + tls_packet_uint8(packet, 1); + tls_packet_update(packet); + context->local_sequence_number = 0; + return packet; +} + +struct TLSPacket *tls_build_done(struct TLSContext *context) { + struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0); + tls_packet_uint8(packet, 0x0E); + tls_packet_uint24(packet, 0); + if (context->dtls) { + _private_dtls_handshake_data(context, packet, 0); + context->dtls_seq++; + } + tls_packet_update(packet); + return packet; +} + +struct TLSPacket *tls_build_message(struct TLSContext *context, const unsigned char *data, unsigned int len) { + if ((!data) || (!len)) + return 0; + struct TLSPacket *packet = tls_create_packet(context, TLS_APPLICATION_DATA, context->version, len); + tls_packet_append(packet, data, len); + tls_packet_update(packet); + return packet; +} + +int tls_client_connect(struct TLSContext *context) { + if ((context->is_server) || (context->critical_error)) + return TLS_UNEXPECTED_MESSAGE; + + return _private_tls_write_packet(tls_build_hello(context, 0)); +} + +int tls_write(struct TLSContext *context, const unsigned char *data, unsigned int len) { + if (!context) + return TLS_GENERIC_ERROR; +#ifdef TLS_12_FALSE_START + if ((context->connection_status != 0xFF) && ((context->is_server) || (context->version != TLS_V12) || (context->critical_error) || (!context->false_start))) + return TLS_UNEXPECTED_MESSAGE; +#else + if (context->connection_status != 0xFF) + return TLS_UNEXPECTED_MESSAGE; +#endif + if (len > TLS_MAXTLS_APP_SIZE) + len = TLS_MAXTLS_APP_SIZE; + int actually_written = _private_tls_write_packet(tls_build_message(context, data, len)); + if (actually_written <= 0) + return actually_written; + return len; +} + +struct TLSPacket *tls_build_alert(struct TLSContext *context, char critical, unsigned char code) { + struct TLSPacket *packet = tls_create_packet(context, TLS_ALERT, context->version, 0); + tls_packet_uint8(packet, critical ? TLS_ALERT_CRITICAL : TLS_ALERT_WARNING); + if (critical) + context->critical_error = 1; + tls_packet_uint8(packet, code); + tls_packet_update(packet); + return packet; +} + +int _private_tls_read_from_file(const char *fname, void *buf, int max_len) { + FILE *f = fopen(fname, "rb"); + if (f) { + int size = (int)fread(buf, 1, max_len, f); + fclose(f); + return size; + } + return 0; +} + +int tls_consume_stream(struct TLSContext *context, const unsigned char *buf, int buf_len, tls_validation_function certificate_verify) { + if (!context) + return TLS_GENERIC_ERROR; + + if (context->critical_error) + return TLS_BROKEN_CONNECTION; + + if (buf_len <= 0) { + DEBUG_PRINT("tls_consume_stream called with buf_len %i\n", buf_len); + return 0; + } + + if (!buf) { + DEBUG_PRINT("tls_consume_stream called NULL buffer\n"); + context->critical_error = 1; + return TLS_NO_MEMORY; + } + + unsigned int orig_len = context->message_buffer_len; + context->message_buffer_len += buf_len; + context->message_buffer = (unsigned char *)TLS_REALLOC(context->message_buffer, context->message_buffer_len); + if (!context->message_buffer) { + context->message_buffer_len = 0; + return TLS_NO_MEMORY; + } + memcpy(context->message_buffer + orig_len, buf, buf_len); + unsigned int index = 0; + unsigned int tls_buffer_len = context->message_buffer_len; + int err_flag = 0; + + int tls_header_size; + int tls_size_offset; + + if (context->dtls) { + tls_size_offset = 11; + tls_header_size = 13; + } else { + tls_size_offset = 3; + tls_header_size = 5; + } + while (tls_buffer_len >= 5) { + unsigned int length = ntohs(*(unsigned short *)&context->message_buffer[index + tls_size_offset]) + tls_header_size; + if (length > tls_buffer_len) { + DEBUG_PRINT("NEED DATA: %i/%i\n", length, tls_buffer_len); + break; + } + int consumed = tls_parse_message(context, &context->message_buffer[index], length, certificate_verify); + DEBUG_PRINT("Consumed %i bytes\n", consumed); + if (consumed < 0) { + if (!context->critical_error) + context->critical_error = 1; + err_flag = consumed; + break; + } + index += length; + tls_buffer_len -= length; + if (context->critical_error) { + err_flag = TLS_BROKEN_CONNECTION; + break; + } + } + if (err_flag) { + DEBUG_PRINT("ERROR IN CONSUME: %i\n", err_flag); + context->message_buffer_len = 0; + TLS_FREE(context->message_buffer); + context->message_buffer = NULL; + return err_flag; + } + if (index) { + context->message_buffer_len -= index; + if (context->message_buffer_len) { + // no realloc here + memmove(context->message_buffer, context->message_buffer + index, context->message_buffer_len); + } else { + TLS_FREE(context->message_buffer); + context->message_buffer = NULL; + } + } + return index; +} + +void tls_close_notify(struct TLSContext *context) { + if ((!context) || (context->critical_error)) + return; + context->critical_error = 1; + DEBUG_PRINT("CLOSE\n"); + _private_tls_write_packet(tls_build_alert(context, 0, close_notify)); +} + +void tls_alert(struct TLSContext *context, unsigned char critical, int code) { + if (!context) + return; + if ((!context->critical_error) && (critical)) + context->critical_error = 1; + DEBUG_PRINT("ALERT\n"); + _private_tls_write_packet(tls_build_alert(context, critical, code)); +} + +int tls_pending(struct TLSContext *context) { + if (!context->message_buffer) + return 0; + return context->message_buffer_len; +} + +void tls_make_exportable(struct TLSContext *context, unsigned char exportable_flag) { + context->exportable = exportable_flag; + if (!exportable_flag) { + // zero the memory + if ((context->exportable_keys) && (context->exportable_size)) + memset(context->exportable_keys, 0, context->exportable_size); + // free the memory, if alocated + TLS_FREE(context->exportable_keys); + context->exportable_size = 0; + } +} + +int tls_export_context(struct TLSContext *context, unsigned char *buffer, unsigned int buf_len, unsigned char small_version) { + // only negotiated AND exportable connections may be exported + if ((!context) || (context->critical_error) || (context->connection_status != 0xFF) || (!context->exportable) || (!context->exportable_keys) || (!context->exportable_size) || (!context->crypto.created)) { + DEBUG_PRINT("CANNOT EXPORT CONTEXT %i\n", (int)context->connection_status); + return 0; + } + + struct TLSPacket *packet = tls_create_packet(NULL, TLS_SERIALIZED_OBJECT, context->version, 0); + // export buffer version + tls_packet_uint8(packet, 0x01); + tls_packet_uint8(packet, context->connection_status); + tls_packet_uint16(packet, context->cipher); + if (context->is_child) + tls_packet_uint8(packet, 2); + else + tls_packet_uint8(packet, context->is_server); + + if (context->crypto.created == 2) { + // aead +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + tls_packet_uint8(packet, TLS_13_AES_GCM_IV_LENGTH); + tls_packet_append(packet, context->crypto.ctx_local_mac.local_iv, TLS_13_AES_GCM_IV_LENGTH); + tls_packet_append(packet, context->crypto.ctx_remote_mac.remote_iv, TLS_13_AES_GCM_IV_LENGTH); + } else { +#endif + tls_packet_uint8(packet, TLS_AES_GCM_IV_LENGTH); + tls_packet_append(packet, context->crypto.ctx_local_mac.local_aead_iv, TLS_AES_GCM_IV_LENGTH); + tls_packet_append(packet, context->crypto.ctx_remote_mac.remote_aead_iv, TLS_AES_GCM_IV_LENGTH); +#ifdef WITH_TLS_13 + } +#endif +#ifdef TLS_WITH_CHACHA20_POLY1305 + } else + if (context->crypto.created == 3) { + // ChaCha20 + tls_packet_uint8(packet, TLS_CHACHA20_IV_LENGTH); + tls_packet_append(packet, context->crypto.ctx_local_mac.local_nonce, TLS_CHACHA20_IV_LENGTH); + tls_packet_append(packet, context->crypto.ctx_remote_mac.remote_nonce, TLS_CHACHA20_IV_LENGTH); +#endif + } else { + unsigned char iv[TLS_AES_IV_LENGTH]; + unsigned long len = TLS_AES_IV_LENGTH; + + memset(iv, 0, TLS_AES_IV_LENGTH); + cbc_getiv(iv, &len, &context->crypto.ctx_local.aes_local); + tls_packet_uint8(packet, TLS_AES_IV_LENGTH); + tls_packet_append(packet, iv, len); + + memset(iv, 0, TLS_AES_IV_LENGTH); + cbc_getiv(iv, &len, &context->crypto.ctx_remote.aes_remote); + tls_packet_append(packet, iv, TLS_AES_IV_LENGTH); + } + + tls_packet_uint8(packet, context->exportable_size); + tls_packet_append(packet, context->exportable_keys, context->exportable_size); + + if (context->crypto.created == 2) { + tls_packet_uint8(packet, 0); +#ifdef TLS_WITH_CHACHA20_POLY1305 + } else + if (context->crypto.created == 3) { + // ChaCha20 + tls_packet_uint8(packet, 0); + unsigned int i; + for (i = 0; i < 16; i++) + tls_packet_uint32(packet, context->crypto.ctx_local.chacha_local.input[i]); + for (i = 0; i < 16; i++) + tls_packet_uint32(packet, context->crypto.ctx_remote.chacha_remote.input[i]); + tls_packet_append(packet, context->crypto.ctx_local.chacha_local.ks, CHACHA_BLOCKLEN); + tls_packet_append(packet, context->crypto.ctx_remote.chacha_remote.ks, CHACHA_BLOCKLEN); +#endif + } else { + unsigned char mac_length = (unsigned char)_private_tls_mac_length(context); + tls_packet_uint8(packet, mac_length); + tls_packet_append(packet, context->crypto.ctx_local_mac.local_mac, mac_length); + tls_packet_append(packet, context->crypto.ctx_remote_mac.remote_mac, mac_length); + } + + if (small_version) { + tls_packet_uint16(packet, 0); + } else { + tls_packet_uint16(packet, context->master_key_len); + tls_packet_append(packet, context->master_key, context->master_key_len); + } + + uint64_t sequence_number = htonll(context->local_sequence_number); + tls_packet_append(packet, (unsigned char *)&sequence_number, sizeof(uint64_t)); + sequence_number = htonll(context->remote_sequence_number); + tls_packet_append(packet, (unsigned char *)&sequence_number, sizeof(uint64_t)); + + tls_packet_uint32(packet, context->tls_buffer_len); + tls_packet_append(packet, context->tls_buffer, context->tls_buffer_len); + + tls_packet_uint32(packet, context->message_buffer_len); + tls_packet_append(packet, context->message_buffer, context->message_buffer_len); + + tls_packet_uint32(packet, context->application_buffer_len); + tls_packet_append(packet, context->application_buffer, context->application_buffer_len); + tls_packet_uint8(packet, context->dtls); + if (context->dtls) { + tls_packet_uint16(packet, context->dtls_epoch_local); + tls_packet_uint16(packet, context->dtls_epoch_remote); + } + tls_packet_update(packet); + unsigned int size = packet->len; + if ((buffer) && (buf_len)) { + if (size > buf_len) { + tls_destroy_packet(packet); + DEBUG_PRINT("EXPORT BUFFER TO SMALL\n"); + return (int)buf_len - (int)size; + } + memcpy(buffer, packet->buf, size); + } + tls_destroy_packet(packet); + return size; +} + +struct TLSContext *tls_import_context(const unsigned char *buffer, unsigned int buf_len) { + if ((!buffer) || (buf_len < 64) || (buffer[0] != TLS_SERIALIZED_OBJECT) || (buffer[5] != 0x01)) { + DEBUG_PRINT("CANNOT IMPORT CONTEXT BUFFER\n"); + return NULL; + } + // create a context object + struct TLSContext *context = tls_create_context(0, TLS_V12); + if (context) { + unsigned char temp[0xFF]; + context->version = ntohs(*(unsigned short *)&buffer[1]); + unsigned short length = ntohs(*(unsigned short *)&buffer[3]); + if (length != buf_len - 5) { + DEBUG_PRINT("INVALID IMPORT BUFFER SIZE\n"); + tls_destroy_context(context); + return NULL; + } + context->connection_status = buffer[6]; + context->cipher = ntohs(*(unsigned short *)&buffer[7]); + unsigned char server = buffer[9]; + if (server == 2) { + context->is_server = 1; + context->is_child = 1; + } else + context->is_server = server; + + unsigned char local_iv[TLS_AES_IV_LENGTH]; + unsigned char remote_iv[TLS_AES_IV_LENGTH]; + unsigned char iv_len = buffer[10]; + if (iv_len > TLS_AES_IV_LENGTH) { + DEBUG_PRINT("INVALID IV LENGTH\n"); + tls_destroy_context(context); + return NULL; + } + + // get the initialization vectors + int buf_pos = 11; + memcpy(local_iv, &buffer[buf_pos], iv_len); + buf_pos += iv_len; + memcpy(remote_iv, &buffer[buf_pos], iv_len); + buf_pos += iv_len; + + unsigned char key_lengths = buffer[buf_pos++]; + TLS_IMPORT_CHECK_SIZE(buf_pos, key_lengths, buf_len) + memcpy(temp, &buffer[buf_pos], key_lengths); + buf_pos += key_lengths; +#ifdef TLS_REEXPORTABLE + context->exportable = 1; + context->exportable_keys = (unsigned char *)TLS_MALLOC(key_lengths); + memcpy(context->exportable_keys, temp, key_lengths); + context->exportable_size = key_lengths; +#else + context->exportable = 0; +#endif + int is_aead = _private_tls_is_aead(context); +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (is_aead == 2) { + // ChaCha20 + if (iv_len > TLS_CHACHA20_IV_LENGTH) + iv_len = TLS_CHACHA20_IV_LENGTH; + memcpy(context->crypto.ctx_local_mac.local_nonce, local_iv, iv_len); + memcpy(context->crypto.ctx_remote_mac.remote_nonce, remote_iv, iv_len); + } else +#endif + if (is_aead) { +#ifdef WITH_TLS_13 + if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { + if (iv_len > TLS_13_AES_GCM_IV_LENGTH) + iv_len = TLS_13_AES_GCM_IV_LENGTH; + memcpy(context->crypto.ctx_local_mac.local_iv, local_iv, iv_len); + memcpy(context->crypto.ctx_remote_mac.remote_iv, remote_iv, iv_len); + } else { +#endif + if (iv_len > TLS_AES_GCM_IV_LENGTH) + iv_len = TLS_AES_GCM_IV_LENGTH; + memcpy(context->crypto.ctx_local_mac.local_aead_iv, local_iv, iv_len); + memcpy(context->crypto.ctx_remote_mac.remote_aead_iv, remote_iv, iv_len); +#ifdef WITH_TLS_13 + } +#endif + } + if (context->is_server) { + if (_private_tls_crypto_create(context, key_lengths / 2, temp, local_iv, temp + key_lengths / 2, remote_iv)) { + DEBUG_PRINT("ERROR CREATING KEY CONTEXT\n"); + tls_destroy_context(context); + return NULL; + } + } else { + if (_private_tls_crypto_create(context, key_lengths / 2, temp + key_lengths / 2, remote_iv, temp, local_iv)) { + DEBUG_PRINT("ERROR CREATING KEY CONTEXT (CLIENT)\n"); + tls_destroy_context(context); + return NULL; + } + } + memset(temp, 0, sizeof(temp)); + + unsigned char mac_length = buffer[buf_pos++]; + if (mac_length > TLS_MAX_MAC_SIZE) { + DEBUG_PRINT("INVALID MAC SIZE\n"); + tls_destroy_context(context); + return NULL; + } + + if (mac_length) { + TLS_IMPORT_CHECK_SIZE(buf_pos, mac_length, buf_len) + memcpy(context->crypto.ctx_local_mac.local_mac, &buffer[buf_pos], mac_length); + buf_pos += mac_length; + + TLS_IMPORT_CHECK_SIZE(buf_pos, mac_length, buf_len) + memcpy(context->crypto.ctx_remote_mac.remote_mac, &buffer[buf_pos], mac_length); + buf_pos += mac_length; + } else +#ifdef TLS_WITH_CHACHA20_POLY1305 + if (is_aead == 2) { + // ChaCha20 + unsigned int i; + TLS_IMPORT_CHECK_SIZE(buf_pos, 128 + CHACHA_BLOCKLEN * 2, buf_len) + for (i = 0; i < 16; i++) { + context->crypto.ctx_local.chacha_local.input[i] = ntohl(*(unsigned int *)(buffer + buf_pos)); + buf_pos += sizeof(unsigned int); + } + for (i = 0; i < 16; i++) { + context->crypto.ctx_remote.chacha_remote.input[i] = ntohl(*(unsigned int *)(buffer + buf_pos)); + buf_pos += sizeof(unsigned int); + } + memcpy(context->crypto.ctx_local.chacha_local.ks, buffer + buf_pos, CHACHA_BLOCKLEN); + buf_pos += CHACHA_BLOCKLEN; + memcpy(context->crypto.ctx_remote.chacha_remote.ks, buffer + buf_pos, CHACHA_BLOCKLEN); + buf_pos += CHACHA_BLOCKLEN; + } +#endif + + TLS_IMPORT_CHECK_SIZE(buf_pos, 2, buf_len) + unsigned short master_key_len = ntohs(*(unsigned short *)(buffer + buf_pos)); + buf_pos += 2; + if (master_key_len) { + TLS_IMPORT_CHECK_SIZE(buf_pos, master_key_len, buf_len) + context->master_key = (unsigned char *)TLS_MALLOC(master_key_len); + if (context->master_key) { + memcpy(context->master_key, &buffer[buf_pos], master_key_len); + context->master_key_len = master_key_len; + } + buf_pos += master_key_len; + } + + TLS_IMPORT_CHECK_SIZE(buf_pos, 16, buf_len) + + context->local_sequence_number = ntohll(*(uint64_t *)&buffer[buf_pos]); + buf_pos += 8; + context->remote_sequence_number = ntohll(*(uint64_t *)&buffer[buf_pos]); + buf_pos += 8; + + TLS_IMPORT_CHECK_SIZE(buf_pos, 4, buf_len) + unsigned int tls_buffer_len = ntohl(*(unsigned int *)&buffer[buf_pos]); + buf_pos += 4; + TLS_IMPORT_CHECK_SIZE(buf_pos, tls_buffer_len, buf_len) + if (tls_buffer_len) { + context->tls_buffer = (unsigned char *)TLS_MALLOC(tls_buffer_len); + if (context->tls_buffer) { + memcpy(context->tls_buffer, &buffer[buf_pos], tls_buffer_len); + context->tls_buffer_len = tls_buffer_len; + } + buf_pos += tls_buffer_len; + } + + TLS_IMPORT_CHECK_SIZE(buf_pos, 4, buf_len) + unsigned int message_buffer_len = ntohl(*(unsigned int *)&buffer[buf_pos]); + buf_pos += 4; + TLS_IMPORT_CHECK_SIZE(buf_pos, message_buffer_len, buf_len) + if (message_buffer_len) { + context->message_buffer = (unsigned char *)TLS_MALLOC(message_buffer_len); + if (context->message_buffer) { + memcpy(context->message_buffer, &buffer[buf_pos], message_buffer_len); + context->message_buffer_len = message_buffer_len; + } + buf_pos += message_buffer_len; + } + + TLS_IMPORT_CHECK_SIZE(buf_pos, 4, buf_len) + unsigned int application_buffer_len = ntohl(*(unsigned int *)&buffer[buf_pos]); + buf_pos += 4; + context->cipher_spec_set = 1; + TLS_IMPORT_CHECK_SIZE(buf_pos, application_buffer_len, buf_len) + if (application_buffer_len) { + context->application_buffer = (unsigned char *)TLS_MALLOC(application_buffer_len); + if (context->application_buffer) { + memcpy(context->application_buffer, &buffer[buf_pos], application_buffer_len); + context->application_buffer_len = application_buffer_len; + } + buf_pos += application_buffer_len; + } + TLS_IMPORT_CHECK_SIZE(buf_pos, 1, buf_len) + context->dtls = buffer[buf_pos]; + buf_pos++; + if (context->dtls) { + TLS_IMPORT_CHECK_SIZE(buf_pos, 4, buf_len) + context->dtls_epoch_local = ntohs(*(unsigned short *)&buffer[buf_pos]); + buf_pos += 2; + context->dtls_epoch_remote = ntohs(*(unsigned short *)&buffer[buf_pos]); + } + } + return context; +} + +int tls_is_broken(struct TLSContext *context) { + if ((!context) || (context->critical_error)) + return 1; + return 0; +} + +int tls_request_client_certificate(struct TLSContext *context) { + if ((!context) || (!context->is_server)) + return 0; + + context->request_client_certificate = 1; + return 1; +} + +int tls_client_verified(struct TLSContext *context) { + if ((!context) || (context->critical_error)) + return 0; + + return (context->client_verified == 1); +} + +const char *tls_sni(struct TLSContext *context) { + if (!context) + return NULL; + return context->sni; +} + +int tls_sni_set(struct TLSContext *context, const char *sni) { + if ((!context) || (context->is_server) || (context->critical_error) || (context->connection_status != 0)) + return 0; + TLS_FREE(context->sni); + context->sni = NULL; + if (sni) { + size_t len = strlen(sni); + if (len > 0) { + context->sni = (char *)TLS_MALLOC(len + 1); + if (context->sni) { + context->sni[len] = 0; + memcpy(context->sni, sni, len); + return 1; + } + } + } + return 0; +} + +int tls_load_root_certificates(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size) { + if (!context) + return TLS_GENERIC_ERROR; + + unsigned int len; + int idx = 0; + + do { + unsigned char *data = tls_pem_decode(pem_buffer, pem_size, idx++, &len); + if ((!data) || (!len)) + break; + struct TLSCertificate *cert = asn1_parse(NULL, data, len, 0); + if (cert) { + if ((cert->version == 2) +#ifdef TLS_X509_V1_SUPPORT + || (cert->version == 0) +#endif + ) { + if (cert->priv) { + DEBUG_PRINT("WARNING - parse error (private key encountered in certificate)\n"); + TLS_FREE(cert->priv); + cert->priv = NULL; + cert->priv_len = 0; + } + context->root_certificates = (struct TLSCertificate **)TLS_REALLOC(context->root_certificates, (context->root_count + 1) * sizeof(struct TLSCertificate *)); + if (!context->root_certificates) { + context->root_count = 0; + return TLS_GENERIC_ERROR; + } + context->root_certificates[context->root_count] = cert; + context->root_count++; + DEBUG_PRINT("Loaded certificate: %i\n", (int)context->root_count); + } else { + DEBUG_PRINT("WARNING - certificate version error (v%i)\n", (int)cert->version); + tls_destroy_certificate(cert); + } + } + TLS_FREE(data); + } while (1); + return context->root_count; +} + +int tls_default_verify(struct TLSContext *context, struct TLSCertificate **certificate_chain, int len) { + int i; + int err; + + if (certificate_chain) { + for (i = 0; i < len; i++) { + struct TLSCertificate *certificate = certificate_chain[i]; + // check validity date + err = tls_certificate_is_valid(certificate); + if (err) + return err; + } + } + // check if chain is valid + err = tls_certificate_chain_is_valid(certificate_chain, len); + if (err) + return err; + + // check certificate subject + if ((!context->is_server) && (context->sni) && (len > 0) && (certificate_chain)) { + err = tls_certificate_valid_subject(certificate_chain[0], context->sni); + if (err) + return err; + } + + err = tls_certificate_chain_is_valid_root(context, certificate_chain, len); + if (err) + return err; + + DEBUG_PRINT("Certificate OK\n"); + return no_error; +} + +int tls_unmake_ktls(struct TLSContext *context, int socket) { +#ifdef WITH_KTLS + struct tls12_crypto_info_aes_gcm_128 crypto_info; + socklen_t crypt_info_size = sizeof(crypto_info); + if (getsockopt(socket, SOL_TLS, TLS_TX, &crypto_info, &crypt_info_size)) { + DEBUG_PRINT("ERROR IN getsockopt\n"); + return TLS_GENERIC_ERROR; + } + memcpy(crypto_info.rec_seq, &context->local_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE); + context->local_sequence_number = ntohll(context->local_sequence_number); +#ifdef TLS_RX + crypt_info_size = sizeof(crypto_info); + if (getsockopt(socket, SOL_TLS, TLS_RX, &crypto_info, &crypt_info_size)) { + DEBUG_PRINT("ERROR IN getsockopt\n"); + return TLS_GENERIC_ERROR; + } + memcpy(crypto_info.rec_seq, &context->remote_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE); + context->remote_sequence_number = ntohll(context->remote_sequence_number); +#endif + return 0; +#endif + DEBUG_PRINT("TLSe COMPILED WITHOUT kTLS SUPPORT\n"); + return TLS_FEATURE_NOT_SUPPORTED; +} + +int tls_make_ktls(struct TLSContext *context, int socket) { + if ((!context) || (context->critical_error) || (context->connection_status != 0xFF) || (!context->crypto.created)) { + DEBUG_PRINT("CANNOT SWITCH TO kTLS\n"); + return TLS_GENERIC_ERROR; + } + if ((!context->exportable) || (!context->exportable_keys)) { + DEBUG_PRINT("KEY MUST BE EXPORTABLE TO BE ABLE TO USE kTLS\n"); + return TLS_GENERIC_ERROR; + } + if ((context->version != TLS_V12) && (context->version != DTLS_V12) && (context->version != TLS_V13) && (context->version != DTLS_V13)) { + DEBUG_PRINT("kTLS IS SUPPORTED ONLY FOR TLS >= 1.2 AND DTLS >= 1.2\n"); + return TLS_FEATURE_NOT_SUPPORTED; + } + switch (context->cipher) { + case TLS_RSA_WITH_AES_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + break; + default: + DEBUG_PRINT("CIPHER UNSUPPORTED: kTLS SUPPORTS ONLY AES 128 GCM CIPHERS\n"); + return TLS_FEATURE_NOT_SUPPORTED; + } +#ifdef WITH_KTLS + if (context->exportable_size < TLS_CIPHER_AES_GCM_128_KEY_SIZE * 2) { + DEBUG_PRINT("INVALID KEY SIZE\n"); + return TLS_GENERIC_ERROR; + } + int err; + struct tls12_crypto_info_aes_gcm_128 crypto_info; + + crypto_info.info.version = TLS_1_2_VERSION; + crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_128; + + uint64_t local_sequence_number = htonll(context->local_sequence_number); + memcpy(crypto_info.iv, &local_sequence_number, TLS_CIPHER_AES_GCM_128_IV_SIZE); + memcpy(crypto_info.rec_seq, &local_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE); + memcpy(crypto_info.key, context->exportable_keys, TLS_CIPHER_AES_GCM_128_KEY_SIZE); + memcpy(crypto_info.salt, context->crypto.ctx_local_mac.local_aead_iv, TLS_CIPHER_AES_GCM_128_SALT_SIZE); + + err = setsockopt(socket, SOL_TCP, TCP_ULP, "tls", sizeof("tls")); + if (err) + return err; + +#ifdef TLS_RX + // kernel 4.17 adds TLS_RX support + struct tls12_crypto_info_aes_gcm_128 crypto_info_read; + + crypto_info_read.info.version = TLS_1_2_VERSION; + crypto_info_read.info.cipher_type = TLS_CIPHER_AES_GCM_128; + + uint64_t remote_sequence_number = htonll(context->remote_sequence_number); + memcpy(crypto_info_read.iv, &remote_sequence_number, TLS_CIPHER_AES_GCM_128_IV_SIZE); + memcpy(crypto_info_read.rec_seq, &remote_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE); + memcpy(crypto_info_read.key, context->exportable_keys + TLS_CIPHER_AES_GCM_128_KEY_SIZE, TLS_CIPHER_AES_GCM_128_KEY_SIZE); + memcpy(crypto_info_read.salt, context->crypto.ctx_remote_mac.remote_aead_iv, TLS_CIPHER_AES_GCM_128_SALT_SIZE); + + err = setsockopt(socket, SOL_TLS, TLS_RX, &crypto_info_read, sizeof(crypto_info_read)); + if (err) + return err; +#endif + return setsockopt(socket, SOL_TLS, TLS_TX, &crypto_info, sizeof(crypto_info)); +#else + DEBUG_PRINT("TLSe COMPILED WITHOUT kTLS SUPPORT\n"); + return TLS_FEATURE_NOT_SUPPORTED; +#endif +} + +#ifdef DEBUG +void tls_print_certificate(const char *fname) { + unsigned char buf[0xFFFF]; + char out_buf[0xFFFF]; + int size = _private_tls_read_from_file(fname, buf, 0xFFFF); + if (size > 0) { + int idx = 0; + unsigned int len; + do { + unsigned char *data; + if (buf[0] == '-') { + data = tls_pem_decode(buf, size, idx++, &len); + } else { + data = buf; + len = size; + } + if ((!data) || (!len)) + return; + struct TLSCertificate *cert = asn1_parse(NULL, data, len, -1); + if (data != buf) + TLS_FREE(data); + if (cert) { + fprintf(stderr, "%s", tls_certificate_to_string(cert, out_buf, 0xFFFF)); + tls_destroy_certificate(cert); + } + if (data == buf) + break; + } while (1); + } +} +#endif + +int tls_remote_error(struct TLSContext *context) { + if (!context) + return TLS_GENERIC_ERROR; + + return context->error_code; +} + +#ifdef SSL_COMPATIBLE_INTERFACE + +int SSL_library_init() { + // dummy function + return 1; +} + +void SSL_load_error_strings() { + // dummy function +} + +void OpenSSL_add_all_algorithms() { + // dummy function +} + +void OpenSSL_add_all_ciphers() { + // dummy function +} + +void OpenSSL_add_all_digests() { + // dummy function +} + +void EVP_cleanup() { + // dummy function +} + +int _tls_ssl_private_send_pending(int client_sock, struct TLSContext *context) { + unsigned int out_buffer_len = 0; + const unsigned char *out_buffer = tls_get_write_buffer(context, &out_buffer_len); + unsigned int out_buffer_index = 0; + int send_res = 0; + SOCKET_SEND_CALLBACK write_cb = NULL; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if (ssl_data) + write_cb = (SOCKET_SEND_CALLBACK)ssl_data->send; + while ((out_buffer) && (out_buffer_len > 0)) { + int res; + if (write_cb) + res = write_cb(client_sock, (char *)&out_buffer[out_buffer_index], out_buffer_len, 0); + else + res = send(client_sock, (char *)&out_buffer[out_buffer_index], out_buffer_len, 0); + if (res <= 0) { + if ((!write_cb) && (res < 0)) { +#ifdef _WIN32 + if (WSAGetLastError() == WSAEWOULDBLOCK) { + context->tls_buffer_len = out_buffer_len; + memmove(context->tls_buffer, out_buffer + out_buffer_index, out_buffer_len); + return res; + } +#else + if ((errno == EAGAIN) || (errno == EINTR)) { + context->tls_buffer_len = out_buffer_len; + memmove(context->tls_buffer, out_buffer + out_buffer_index, out_buffer_len); + return res; + } +#endif + } + send_res = res; + break; + } + out_buffer_len -= res; + out_buffer_index += res; + send_res += res; + } + tls_buffer_clear(context); + return send_res; +} + +struct TLSContext *SSL_new(struct TLSContext *context) { + return tls_accept(context); +} + +int SSLv3_server_method() { + return 1; +} + +int SSLv3_client_method() { + return 0; +} + +int SSL_CTX_use_certificate_file(struct TLSContext *context, const char *filename, int dummy) { + // max 64k buffer + unsigned char buf[0xFFFF]; + int size = _private_tls_read_from_file(filename, buf, sizeof(buf)); + if (size > 0) + return tls_load_certificates(context, buf, size); + return size; +} + +int SSL_CTX_use_PrivateKey_file(struct TLSContext *context, const char *filename, int dummy) { + unsigned char buf[0xFFFF]; + int size = _private_tls_read_from_file(filename, buf, sizeof(buf)); + if (size > 0) + return tls_load_private_key(context, buf, size); + + return size; +} + +int SSL_CTX_check_private_key(struct TLSContext *context) { + if ((!context) || (((!context->private_key) || (!context->private_key->der_bytes) || (!context->private_key->der_len)) +#ifdef TLS_ECDSA_SUPPORTED + && ((!context->ec_private_key) || (!context->ec_private_key->der_bytes) || (!context->ec_private_key->der_len)) +#endif + )) + return 0; + return 1; +} + +struct TLSContext *SSL_CTX_new(int method) { +#ifdef WITH_TLS_13 + return tls_create_context(method, TLS_V13); +#else + return tls_create_context(method, TLS_V12); +#endif +} + +void SSL_free(struct TLSContext *context) { + if (context) { + TLS_FREE(context->user_data); + tls_destroy_context(context); + } +} + +void SSL_CTX_free(struct TLSContext *context) { + SSL_free(context); +} + +int SSL_get_error(struct TLSContext *context, int ret) { + if (!context) + return TLS_GENERIC_ERROR; + return context->critical_error; +} + +int SSL_set_fd(struct TLSContext *context, int socket) { + if (!context) + return 0; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if (!ssl_data) { + ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData)); + if (!ssl_data) + return TLS_NO_MEMORY; + memset(ssl_data, 0, sizeof(SSLUserData)); + context->user_data = ssl_data; + } + ssl_data->fd = socket; + return 1; +} + +void *SSL_set_userdata(struct TLSContext *context, void *data) { + if (!context) + return NULL; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if (!ssl_data) { + ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData)); + if (!ssl_data) + return NULL; + memset(ssl_data, 0, sizeof(SSLUserData)); + context->user_data = ssl_data; + } + void *old_data = ssl_data->user_data; + ssl_data->user_data = data; + return old_data; +} + +void *SSL_userdata(struct TLSContext *context) { + if (!context) + return NULL; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if (!ssl_data) + return NULL; + + return ssl_data->user_data; +} + +int SSL_CTX_root_ca(struct TLSContext *context, const char *pem_filename) { + if (!context) + return TLS_GENERIC_ERROR; + + int count = TLS_GENERIC_ERROR; + FILE *f = fopen(pem_filename, "rb"); + if (f) { + fseek(f, 0, SEEK_END); + size_t size = (size_t)ftell(f); + fseek(f, 0, SEEK_SET); + if (size) { + unsigned char *buf = (unsigned char *)TLS_MALLOC(size + 1); + if (buf) { + buf[size] = 1; + if (fread(buf, 1, size, f) == size) { + count = tls_load_root_certificates(context, buf, size); + if (count > 0) { + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if (!ssl_data) { + ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData)); + if (!ssl_data) { + fclose(f); + return TLS_NO_MEMORY; + } + memset(ssl_data, 0, sizeof(SSLUserData)); + context->user_data = ssl_data; + } + if (!ssl_data->certificate_verify) + ssl_data->certificate_verify = tls_default_verify; + } + } + TLS_FREE(buf); + } + } + fclose(f); + } + return count; +} + +void SSL_CTX_set_verify(struct TLSContext *context, int mode, tls_validation_function verify_callback) { + if (!context) + return; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if (!ssl_data) { + ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData)); + if (!ssl_data) + return; + memset(ssl_data, 0, sizeof(SSLUserData)); + context->user_data = ssl_data; + } + if (mode == SSL_VERIFY_NONE) + ssl_data->certificate_verify = NULL; + else + ssl_data->certificate_verify = verify_callback; +} + +int _private_tls_safe_read(struct TLSContext *context, void *buffer, int buf_size) { + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if ((!ssl_data) || (ssl_data->fd < 0)) + return TLS_GENERIC_ERROR; + + SOCKET_RECV_CALLBACK read_cb = (SOCKET_RECV_CALLBACK)ssl_data->recv; + if (read_cb) + return read_cb(ssl_data->fd, (char *)buffer, buf_size, 0); + return recv(ssl_data->fd, (char *)buffer, buf_size, 0); +} + +int SSL_accept(struct TLSContext *context) { + if (!context) + return TLS_GENERIC_ERROR; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if ((!ssl_data) || (ssl_data->fd < 0)) + return TLS_GENERIC_ERROR; + if (tls_established(context)) + return 1; + unsigned char client_message[0xFFFF]; + // accept + int read_size = 0; + while ((read_size = _private_tls_safe_read(context, (char *)client_message, sizeof(client_message))) > 0) { + if (tls_consume_stream(context, client_message, read_size, ssl_data->certificate_verify) >= 0) { + int res = _tls_ssl_private_send_pending(ssl_data->fd, context); + if (res < 0) + return res; + } + if (tls_established(context)) + return 1; + } + if (read_size <= 0) + return TLS_BROKEN_CONNECTION; + return 0; +} + +int SSL_connect(struct TLSContext *context) { + if (!context) + return TLS_GENERIC_ERROR; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if ((!ssl_data) || (ssl_data->fd < 0) || (context->critical_error)) + return TLS_GENERIC_ERROR; + int res = tls_client_connect(context); + if (res < 0) + return res; + res = _tls_ssl_private_send_pending(ssl_data->fd, context); + if (res < 0) + return res; + + int read_size; + unsigned char client_message[0xFFFF]; + + while ((read_size = _private_tls_safe_read(context, (char *)client_message, sizeof(client_message))) > 0) { + if (tls_consume_stream(context, client_message, read_size, ssl_data->certificate_verify) >= 0) { + res = _tls_ssl_private_send_pending(ssl_data->fd, context); + if (res < 0) + return res; + } + if (tls_established(context)) + return 1; + if (context->critical_error) + return TLS_GENERIC_ERROR; + } + return read_size; +} + +int SSL_shutdown(struct TLSContext *context) { + if (!context) + return TLS_GENERIC_ERROR; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if ((!ssl_data) || (ssl_data->fd < 0)) + return TLS_GENERIC_ERROR; + + tls_close_notify(context); + return 0; +} + +int SSL_write(struct TLSContext *context, const void *buf, unsigned int len) { + if (!context) + return TLS_GENERIC_ERROR; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if ((!ssl_data) || (ssl_data->fd < 0)) + return TLS_GENERIC_ERROR; + + int written_size = tls_write(context, (const unsigned char *)buf, len); + if (written_size > 0) { + int res = _tls_ssl_private_send_pending(ssl_data->fd, context); + if (res <= 0) + return res; + } + return written_size; +} + +int SSL_read(struct TLSContext *context, void *buf, unsigned int len) { + if (!context) + return TLS_GENERIC_ERROR; + + if (context->application_buffer_len) + return tls_read(context, (unsigned char *)buf, len); + + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if ((!ssl_data) || (ssl_data->fd < 0) || (context->critical_error)) + return TLS_GENERIC_ERROR; + if (tls_established(context) != 1) + return TLS_GENERIC_ERROR; + + unsigned char client_message[0xFFFF]; + // accept + int read_size; + while ((!context->application_buffer_len) && ((read_size = _private_tls_safe_read(context, (char *)client_message, sizeof(client_message))) > 0)) { + if (tls_consume_stream(context, client_message, read_size, ssl_data->certificate_verify) > 0) + _tls_ssl_private_send_pending(ssl_data->fd, context); + + if ((context->critical_error) && (!context->application_buffer_len)) + return TLS_GENERIC_ERROR; + } + if ((read_size <= 0) && (!context->application_buffer_len)) + return read_size; + + return tls_read(context, (unsigned char *)buf, len); +} + +int SSL_pending(struct TLSContext *context) { + if (!context) + return TLS_GENERIC_ERROR; + return context->application_buffer_len; +} + +int SSL_set_io(struct TLSContext *context, void *recv_cb, void *send_cb) { + if (!context) + return TLS_GENERIC_ERROR; + SSLUserData *ssl_data = (SSLUserData *)context->user_data; + if (!ssl_data) { + ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData)); + if (!ssl_data) + return TLS_NO_MEMORY; + memset(ssl_data, 0, sizeof(SSLUserData)); + context->user_data = ssl_data; + } + ssl_data->recv = recv_cb; + ssl_data->send = send_cb; + return 0; +} +#endif // SSL_COMPATIBLE_INTERFACE + + +#ifdef TLS_SRTP + +struct SRTPContext { + symmetric_CTR aes; + unsigned int salt[4]; + unsigned char mac[TLS_SHA1_MAC_SIZE]; + unsigned int tag_size; + unsigned int roc; + unsigned short seq; + + unsigned char mode; + unsigned char auth_mode; +}; + +struct SRTPContext *srtp_init(unsigned char mode, unsigned char auth_mode) { + struct SRTPContext *context = NULL; + tls_init(); + switch (mode) { + case SRTP_NULL: + break; + case SRTP_AES_CM: + break; + default: + return NULL; + } + + switch (auth_mode) { + case SRTP_AUTH_NULL: + break; + case SRTP_AUTH_HMAC_SHA1: + break; + default: + return NULL; + } + context = (struct SRTPContext *)TLS_MALLOC(sizeof(struct SRTPContext)); + if (context) { + memset(context, 0, sizeof(struct SRTPContext)); + context->mode = mode; + context->auth_mode = auth_mode; + } + return context; +} + +static int _private_tls_srtp_key_derive(const void *key, int keylen, const void *salt, unsigned char label, void *out, int outlen) { + unsigned char iv[16]; + memcpy(iv, salt, 14); + iv[14] = iv[15] = 0; + void *in = TLS_MALLOC(outlen); + if (!in) + return TLS_GENERIC_ERROR; + memset(in, 0, outlen); + + iv[7] ^= label; + + symmetric_CTR aes; + + if (ctr_start(find_cipher("aes"), iv, (const unsigned char *)key, keylen, 0, CTR_COUNTER_BIG_ENDIAN, &aes)) + return TLS_GENERIC_ERROR; + + ctr_encrypt((unsigned char *)in, (unsigned char *)out, outlen, &aes); + TLS_FREE(in); + ctr_done(&aes); + return 0; +} + +int srtp_key(struct SRTPContext *context, const void *key, int keylen, const void *salt, int saltlen, int tag_bits) { + if (!context) + return TLS_GENERIC_ERROR; + if (context->mode == SRTP_AES_CM) { + if ((saltlen < 14) || (keylen < 16)) + return TLS_GENERIC_ERROR; + // key + unsigned char key_buf[16]; + unsigned char iv[16]; + + memset(iv, 0, sizeof(iv)); + + if (_private_tls_srtp_key_derive(key, keylen, salt, 0, key_buf, sizeof(key_buf))) + return TLS_GENERIC_ERROR; + + DEBUG_DUMP_HEX_LABEL("KEY", key_buf, 16) + + if (_private_tls_srtp_key_derive(key, keylen, salt, 1, context->mac, 20)) + return TLS_GENERIC_ERROR; + + DEBUG_DUMP_HEX_LABEL("AUTH", context->mac, 20) + + memset(context->salt, 0, sizeof(context->salt)); + if (_private_tls_srtp_key_derive(key, keylen, salt, 2, context->salt, 14)) + return TLS_GENERIC_ERROR; + + DEBUG_DUMP_HEX_LABEL("SALT", ((unsigned char *)context->salt), 14) + + if (ctr_start(find_cipher("aes"), iv, key_buf, sizeof(key_buf), 0, CTR_COUNTER_BIG_ENDIAN, &context->aes)) + return TLS_GENERIC_ERROR; + } + if (context->auth_mode) + context->tag_size = tag_bits / 8; + return 0; +} + +int srtp_inline(struct SRTPContext *context, const char *b64, int tag_bits) { + char out_buffer[1024]; + + if (!b64) + return TLS_GENERIC_ERROR; + + int len = strlen(b64); + if (len >= sizeof(out_buffer)) + len = sizeof(out_buffer); + int size = _private_b64_decode(b64, len, (unsigned char *)out_buffer); + if (size <= 0) + return TLS_GENERIC_ERROR; + switch (context->mode) { + case SRTP_AES_CM: + if (size < 30) + return TLS_BROKEN_PACKET; + return srtp_key(context, out_buffer, 16, out_buffer + 16, 14, tag_bits); + } + return TLS_GENERIC_ERROR; +} + +int srtp_encrypt(struct SRTPContext *context, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len) { + if ((!context) || (!out) || (!out_buffer_len) || (*out_buffer_len < payload_len)) + return TLS_GENERIC_ERROR; + + int out_len = payload_len; + + unsigned short seq = 0; + unsigned int roc = context->roc; + unsigned int ssrc = 0; + + if ((pt_header) && (pt_len >= 12)) { + seq = ntohs(*((unsigned short *)&pt_header[2])); + ssrc = ntohl(*((unsigned long *)&pt_header[8])); + } + + if (seq < context->seq) + roc++; + + unsigned int roc_be = htonl(roc); + if (context->mode) { + if (*out_buffer_len < out_len) + return TLS_NO_MEMORY; + + unsigned int counter[4]; + counter[0] = context->salt[0]; + counter[1] = context->salt[1] ^ htonl (ssrc); + counter[2] = context->salt[2] ^ roc_be; + counter[3] = context->salt[3] ^ htonl (seq << 16); + ctr_setiv((unsigned char *)&counter, 16, &context->aes); + if (ctr_encrypt(payload, out, payload_len, &context->aes)) + return TLS_GENERIC_ERROR; + } else { + memcpy(out, payload, payload_len); + } + + *out_buffer_len = out_len; + + if (context->auth_mode == SRTP_AUTH_HMAC_SHA1) { + unsigned char digest_out[TLS_SHA1_MAC_SIZE]; + unsigned long dlen = TLS_SHA1_MAC_SIZE; + hmac_state hmac; + int err = hmac_init(&hmac, find_hash("sha1"), context->mac, 20); + if (!err) { + if (pt_len) + err = hmac_process(&hmac, pt_header, pt_len); + if (out_len) + err = hmac_process(&hmac, out, payload_len); + err = hmac_process(&hmac, (unsigned char *)&roc_be, 4); + if (!err) + err = hmac_done(&hmac, digest_out, &dlen); + } + if (err) + return TLS_GENERIC_ERROR; + if (dlen > context->tag_size) + dlen = context->tag_size; + + *out_buffer_len += dlen; + memcpy(out + out_len, digest_out, dlen); + } + context->roc = roc; + context->seq = seq; + return 0; +} + +int srtp_decrypt(struct SRTPContext *context, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len) { + if ((!context) || (!out) || (!out_buffer_len) || (*out_buffer_len < payload_len) || (payload_len < context->tag_size) || (!pt_header) || (pt_len < 12)) + return TLS_GENERIC_ERROR; + + int out_len = payload_len; + + unsigned short seq = ntohs(*((unsigned short *)&pt_header[2])); + unsigned int roc = context->roc; + unsigned int ssrc = ntohl(*((unsigned long *)&pt_header[8])); + + if (seq < context->seq) + roc++; + + unsigned int roc_be = htonl(roc); + if (context->mode) { + unsigned int counter[4]; + counter[0] = context->salt[0]; + counter[1] = context->salt[1] ^ htonl (ssrc); + counter[2] = context->salt[2] ^ roc_be; + counter[3] = context->salt[3] ^ htonl (seq << 16); + ctr_setiv((unsigned char *)&counter, 16, &context->aes); + + if (ctr_decrypt(payload, out, payload_len - context->tag_size, &context->aes)) + return TLS_GENERIC_ERROR; + + if (context->auth_mode == SRTP_AUTH_HMAC_SHA1) { + unsigned char digest_out[TLS_SHA1_MAC_SIZE]; + unsigned long dlen = TLS_SHA1_MAC_SIZE; + hmac_state hmac; + int err = hmac_init(&hmac, find_hash("sha1"), context->mac, 20); + if (!err) { + if (pt_len) + err = hmac_process(&hmac, pt_header, pt_len); + if (out_len) + err = hmac_process(&hmac, payload, payload_len - context->tag_size); + err = hmac_process(&hmac, (unsigned char *)&roc_be, 4); + if (!err) + err = hmac_done(&hmac, digest_out, &dlen); + } + if (err) + return TLS_GENERIC_ERROR; + if (dlen > context->tag_size) + dlen = context->tag_size; + + if (memcmp(digest_out, payload + payload_len - context->tag_size, dlen)) + return TLS_INTEGRITY_FAILED; + } + } else { + memcpy(out, payload, payload_len - context->tag_size); + } + context->seq = seq; + context->roc = roc; + *out_buffer_len = payload_len - context->tag_size; + return 0; +} + +void srtp_destroy(struct SRTPContext *context) { + if (context) { + if (context->mode) + ctr_done(&context->aes); + TLS_FREE(context); + } +} + +#endif // TLS_SRTP + +#endif // TLSE_C \ No newline at end of file diff --git a/thirdparty/echttp/thirdparty/tlse.h b/thirdparty/echttp/thirdparty/tlse.h new file mode 100644 index 0000000..86387b2 --- /dev/null +++ b/thirdparty/echttp/thirdparty/tlse.h @@ -0,0 +1,430 @@ +#ifndef TLSE_H +#define TLSE_H + +// #define DEBUG + +// define TLS_LEGACY_SUPPORT to support TLS 1.1/1.0 (legacy) +// legacy support it will use an additional 272 bytes / context +#ifndef NO_TLS_LEGACY_SUPPORT +#define TLS_LEGACY_SUPPORT +#endif +// SSL_* style blocking APIs +#ifndef NO_SSL_COMPATIBLE_INTERFACE +#define SSL_COMPATIBLE_INTERFACE +#endif +// support ChaCha20/Poly1305 +#if !defined(__BIG_ENDIAN__) && ((!defined(__BYTE_ORDER)) || (__BYTE_ORDER == __LITTLE_ENDIAN)) + // not working on big endian machines + #ifndef NO_TLS_WITH_CHACHA20_POLY1305 + #define TLS_WITH_CHACHA20_POLY1305 + #endif +#endif +#ifndef NO_TLS_13 +#define WITH_TLS_13 +#endif +// support forward secrecy (Diffie-Hellman ephemeral) +#ifndef NO_TLS_FORWARD_SECRECY +#define TLS_FORWARD_SECRECY +#endif +// support client-side ECDHE +#ifndef NO_TLS_CLIENT_ECDHE +#define TLS_CLIENT_ECDHE +#endif +// suport ecdsa +#ifndef NO_TLS_ECDSA_SUPPORTED +#define TLS_ECDSA_SUPPORTED +#endif +// suport ecdsa client-side +// #define TLS_CLIENT_ECDSA +// TLS renegotiation is disabled by default (secured or not) +// do not uncomment next line! +// #define TLS_ACCEPT_SECURE_RENEGOTIATION +// basic superficial X509v1 certificate support +#ifndef NO_TLS_X509_V1_SUPPORT +#define TLS_X509_V1_SUPPORT +#endif + +// disable TLS_RSA_WITH_* ciphers +#ifndef NO_TLS_ROBOT_MITIGATION +#define TLS_ROBOT_MITIGATION +#endif + +#define SSL_V30 0x0300 +#define TLS_V10 0x0301 +#define TLS_V11 0x0302 +#define TLS_V12 0x0303 +#define TLS_V13 0x0304 +#define DTLS_V10 0xFEFF +#define DTLS_V12 0xFEFD +#define DTLS_V13 0xFEFC + +#define TLS_NEED_MORE_DATA 0 +#define TLS_GENERIC_ERROR -1 +#define TLS_BROKEN_PACKET -2 +#define TLS_NOT_UNDERSTOOD -3 +#define TLS_NOT_SAFE -4 +#define TLS_NO_COMMON_CIPHER -5 +#define TLS_UNEXPECTED_MESSAGE -6 +#define TLS_CLOSE_CONNECTION -7 +#define TLS_COMPRESSION_NOT_SUPPORTED -8 +#define TLS_NO_MEMORY -9 +#define TLS_NOT_VERIFIED -10 +#define TLS_INTEGRITY_FAILED -11 +#define TLS_ERROR_ALERT -12 +#define TLS_BROKEN_CONNECTION -13 +#define TLS_BAD_CERTIFICATE -14 +#define TLS_UNSUPPORTED_CERTIFICATE -15 +#define TLS_NO_RENEGOTIATION -16 +#define TLS_FEATURE_NOT_SUPPORTED -17 +#define TLS_DECRYPTION_FAILED -20 + +#define TLS_AES_128_GCM_SHA256 0x1301 +#define TLS_AES_256_GCM_SHA384 0x1302 +#define TLS_CHACHA20_POLY1305_SHA256 0x1303 +#define TLS_AES_128_CCM_SHA256 0x1304 +#define TLS_AES_128_CCM_8_SHA256 0x1305 + +#define TLS_RSA_WITH_AES_128_CBC_SHA 0x002F +#define TLS_RSA_WITH_AES_256_CBC_SHA 0x0035 +#define TLS_RSA_WITH_AES_128_CBC_SHA256 0x003C +#define TLS_RSA_WITH_AES_256_CBC_SHA256 0x003D +#define TLS_RSA_WITH_AES_128_GCM_SHA256 0x009C +#define TLS_RSA_WITH_AES_256_GCM_SHA384 0x009D + +// forward secrecy +#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0x0033 +#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x0039 +#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x0067 +#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x006B +#define TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 0x009E +#define TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 0x009F + +#define TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA 0xC013 +#define TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA 0xC014 +#define TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 0xC027 +#define TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 0xC02F +#define TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 0xC030 + +#define TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA 0xC009 +#define TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA 0xC00A +#define TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 0xC023 +#define TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 0xC024 +#define TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 0xC02B +#define TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 0xC02C + +#define TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA8 +#define TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA9 +#define TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCAA + +#define TLS_FALLBACK_SCSV 0x5600 + +#define TLS_UNSUPPORTED_ALGORITHM 0x00 +#define TLS_RSA_SIGN_RSA 0x01 +#define TLS_RSA_SIGN_MD5 0x04 +#define TLS_RSA_SIGN_SHA1 0x05 +#define TLS_RSA_SIGN_SHA256 0x0B +#define TLS_RSA_SIGN_SHA384 0x0C +#define TLS_RSA_SIGN_SHA512 0x0D +#define TLS_ECDSA_SIGN_SHA256 0x0E + +#define TLS_EC_PUBLIC_KEY 0x11 +#define TLS_EC_prime192v1 0x12 +#define TLS_EC_prime192v2 0x13 +#define TLS_EC_prime192v3 0x14 +#define TLS_EC_prime239v1 0x15 +#define TLS_EC_prime239v2 0x16 +#define TLS_EC_prime239v3 0x17 +#define TLS_EC_prime256v1 0x18 +#define TLS_EC_secp224r1 21 +#define TLS_EC_secp256r1 23 +#define TLS_EC_secp384r1 24 +#define TLS_EC_secp521r1 25 + +#define TLS_ALERT_WARNING 0x01 +#define TLS_ALERT_CRITICAL 0x02 + +#ifdef TLS_ROBOT_MITIGATION + #define TLS_CIPHERS_SIZE(n, mitigated) n * 2 +#else + #define TLS_CIPHERS_SIZE(n, mitigated) (n + mitigated) * 2 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + close_notify = 0, + unexpected_message = 10, + bad_record_mac = 20, + decryption_failed_RESERVED = 21, + record_overflow = 22, + decompression_failure = 30, + handshake_failure = 40, + no_certificate_RESERVED = 41, + bad_certificate = 42, + unsupported_certificate = 43, + certificate_revoked = 44, + certificate_expired = 45, + certificate_unknown = 46, + illegal_parameter = 47, + unknown_ca = 48, + access_denied = 49, + decode_error = 50, + decrypt_error = 51, + export_restriction_RESERVED = 60, + protocol_version = 70, + insufficient_security = 71, + internal_error = 80, + inappropriate_fallback = 86, + user_canceled = 90, + no_renegotiation = 100, + unsupported_extension = 110, + no_error = 255 +} TLSAlertDescription; + +// forward declarations +struct TLSPacket; +struct TLSCertificate; +struct TLSContext; +struct ECCCurveParameters; +typedef struct TLSContext TLS; +typedef struct TLSCertificate Certificate; + +typedef int (*tls_validation_function)(struct TLSContext *context, struct TLSCertificate **certificate_chain, int len); + +/* + Global initialization. Optional, as it will be called automatically; + however, the initialization is not thread-safe, so if you intend to use TLSe + from multiple threads, you'll need to call tls_init() once, from a single thread, + before using the library. + */ +void tls_init(); +unsigned char *tls_pem_decode(const unsigned char *data_in, unsigned int input_length, int cert_index, unsigned int *output_len); +struct TLSCertificate *tls_create_certificate(); +int tls_certificate_valid_subject(struct TLSCertificate *cert, const char *subject); +int tls_certificate_valid_subject_name(const unsigned char *cert_subject, const char *subject); +int tls_certificate_is_valid(struct TLSCertificate *cert); +void tls_certificate_set_copy(unsigned char **member, const unsigned char *val, int len); +void tls_certificate_set_copy_date(unsigned char **member, const unsigned char *val, int len); +void tls_certificate_set_key(struct TLSCertificate *cert, const unsigned char *val, int len); +void tls_certificate_set_priv(struct TLSCertificate *cert, const unsigned char *val, int len); +void tls_certificate_set_sign_key(struct TLSCertificate *cert, const unsigned char *val, int len); +char *tls_certificate_to_string(struct TLSCertificate *cert, char *buffer, int len); +void tls_certificate_set_exponent(struct TLSCertificate *cert, const unsigned char *val, int len); +void tls_certificate_set_serial(struct TLSCertificate *cert, const unsigned char *val, int len); +void tls_certificate_set_algorithm(struct TLSContext *context, unsigned int *algorithm, const unsigned char *val, int len); +void tls_destroy_certificate(struct TLSCertificate *cert); +struct TLSPacket *tls_create_packet(struct TLSContext *context, unsigned char type, unsigned short version, int payload_size_hint); +void tls_destroy_packet(struct TLSPacket *packet); +void tls_packet_update(struct TLSPacket *packet); +int tls_packet_append(struct TLSPacket *packet, const unsigned char *buf, unsigned int len); +int tls_packet_uint8(struct TLSPacket *packet, unsigned char i); +int tls_packet_uint16(struct TLSPacket *packet, unsigned short i); +int tls_packet_uint32(struct TLSPacket *packet, unsigned int i); +int tls_packet_uint24(struct TLSPacket *packet, unsigned int i); +int tls_random(unsigned char *key, int len); + +/* + Get encrypted data to write, if any. Once you've sent all of it, call + tls_buffer_clear(). + */ +const unsigned char *tls_get_write_buffer(struct TLSContext *context, unsigned int *outlen); + +void tls_buffer_clear(struct TLSContext *context); + +/* Returns 1 for established, 0 for not established yet, and -1 for a critical error. */ +int tls_established(struct TLSContext *context); + +/* Discards any unread decrypted data not consumed by tls_read(). */ +void tls_read_clear(struct TLSContext *context); + +/* + Reads any unread decrypted data (see tls_consume_stream). If you don't read all of it, + the remainder will be left in the internal buffers for next tls_read(). Returns -1 for + fatal error, 0 for no more data, or otherwise the number of bytes copied into the buffer + (up to a maximum of the given size). + */ +int tls_read(struct TLSContext *context, unsigned char *buf, unsigned int size); + +struct TLSContext *tls_create_context(unsigned char is_server, unsigned short version); +const struct ECCCurveParameters *tls_set_curve(struct TLSContext *context, const struct ECCCurveParameters *curve); + +/* Create a context for a given client, from a server context. Returns NULL on error. */ +struct TLSContext *tls_accept(struct TLSContext *context); + +int tls_set_default_dhe_pg(struct TLSContext *context, const char *p_hex_str, const char *g_hex_str); +void tls_destroy_context(struct TLSContext *context); +int tls_cipher_supported(struct TLSContext *context, unsigned short cipher); +int tls_cipher_is_fs(struct TLSContext *context, unsigned short cipher); +int tls_choose_cipher(struct TLSContext *context, const unsigned char *buf, int buf_len, int *scsv_set); +int tls_cipher_is_ephemeral(struct TLSContext *context); +const char *tls_cipher_name(struct TLSContext *context); +int tls_is_ecdsa(struct TLSContext *context); +struct TLSPacket *tls_build_client_key_exchange(struct TLSContext *context); +struct TLSPacket *tls_build_server_key_exchange(struct TLSContext *context, int method); +struct TLSPacket *tls_build_hello(struct TLSContext *context, int tls13_downgrade); +struct TLSPacket *tls_certificate_request(struct TLSContext *context); +struct TLSPacket *tls_build_verify_request(struct TLSContext *context); +int tls_parse_hello(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets, unsigned int *dtls_verified); +int tls_parse_certificate(struct TLSContext *context, const unsigned char *buf, int buf_len, int is_client); +int tls_parse_server_key_exchange(struct TLSContext *context, const unsigned char *buf, int buf_len); +int tls_parse_client_key_exchange(struct TLSContext *context, const unsigned char *buf, int buf_len); +int tls_parse_server_hello_done(struct TLSContext *context, const unsigned char *buf, int buf_len); +int tls_parse_finished(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets); +int tls_parse_verify(struct TLSContext *context, const unsigned char *buf, int buf_len); +int tls_parse_payload(struct TLSContext *context, const unsigned char *buf, int buf_len, tls_validation_function certificate_verify); +int tls_parse_message(struct TLSContext *context, unsigned char *buf, int buf_len, tls_validation_function certificate_verify); +int tls_certificate_verify_signature(struct TLSCertificate *cert, struct TLSCertificate *parent); +int tls_certificate_chain_is_valid(struct TLSCertificate **certificates, int len); +int tls_certificate_chain_is_valid_root(struct TLSContext *context, struct TLSCertificate **certificates, int len); + +/* + Add a certificate or a certificate chain to the given context, in PEM form. + Returns a negative value (TLS_GENERIC_ERROR etc.) on error, 0 if there were no + certificates in the buffer, or the number of loaded certificates on success. + */ +int tls_load_certificates(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size); + +/* + Add a private key to the given context, in PEM form. Returns a negative value + (TLS_GENERIC_ERROR etc.) on error, 0 if there was no private key in the + buffer, or 1 on success. + */ +int tls_load_private_key(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size); +struct TLSPacket *tls_build_certificate(struct TLSContext *context); +struct TLSPacket *tls_build_finished(struct TLSContext *context); +struct TLSPacket *tls_build_change_cipher_spec(struct TLSContext *context); +struct TLSPacket *tls_build_done(struct TLSContext *context); +struct TLSPacket *tls_build_message(struct TLSContext *context, const unsigned char *data, unsigned int len); +int tls_client_connect(struct TLSContext *context); +int tls_write(struct TLSContext *context, const unsigned char *data, unsigned int len); +struct TLSPacket *tls_build_alert(struct TLSContext *context, char critical, unsigned char code); + +/* + Process a given number of input bytes from a socket. If the other side just + presented a certificate and certificate_verify is not NULL, it will be called. + + Returns 0 if there's no data ready yet, a negative value (see + TLS_GENERIC_ERROR etc.) for an error, or a positive value (the number of bytes + used from buf) if one or more complete TLS messages were received. The data + is copied into an internal buffer even if not all of it was consumed, + so you should not re-send it the next time. + + Decrypted data, if any, should be read back with tls_read(). Can change the + status of tls_established(). If the library has anything to send back on the + socket (e.g. as part of the handshake), tls_get_write_buffer() will return + non-NULL. + */ +int tls_consume_stream(struct TLSContext *context, const unsigned char *buf, int buf_len, tls_validation_function certificate_verify); +void tls_close_notify(struct TLSContext *context); +void tls_alert(struct TLSContext *context, unsigned char critical, int code); + +/* Whether tls_consume_stream() has data in its buffer that is not processed yet. */ +int tls_pending(struct TLSContext *context); + +/* + Set the context as serializable or not. Must be called before negotiation. + Exportable contexts use a bit more memory, to be able to hold the keys. + + Note that imported keys are not reexportable unless TLS_REEXPORTABLE is set. + */ +void tls_make_exportable(struct TLSContext *context, unsigned char exportable_flag); + +int tls_export_context(struct TLSContext *context, unsigned char *buffer, unsigned int buf_len, unsigned char small_version); +struct TLSContext *tls_import_context(const unsigned char *buffer, unsigned int buf_len); +int tls_is_broken(struct TLSContext *context); +int tls_request_client_certificate(struct TLSContext *context); +int tls_client_verified(struct TLSContext *context); +const char *tls_sni(struct TLSContext *context); +int tls_sni_set(struct TLSContext *context, const char *sni); +int tls_load_root_certificates(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size); +int tls_default_verify(struct TLSContext *context, struct TLSCertificate **certificate_chain, int len); +void tls_print_certificate(const char *fname); +int tls_add_alpn(struct TLSContext *context, const char *alpn); +int tls_alpn_contains(struct TLSContext *context, const char *alpn, unsigned char alpn_size); +const char *tls_alpn(struct TLSContext *context); +// useful when renewing certificates for servers, without the need to restart the server +int tls_clear_certificates(struct TLSContext *context); +int tls_make_ktls(struct TLSContext *context, int socket); +int tls_unmake_ktls(struct TLSContext *context, int socket); +/* + Creates a new DTLS random cookie secret to be used in HelloVerifyRequest (server-side). + It is recommended to call this function from time to time, to protect against some + DoS attacks. +*/ +void dtls_reset_cookie_secret(); + +int tls_remote_error(struct TLSContext *context); + +#ifdef SSL_COMPATIBLE_INTERFACE + #define SSL_SERVER_RSA_CERT 1 + #define SSL_SERVER_RSA_KEY 2 + typedef struct TLSContext SSL_CTX; + typedef struct TLSContext SSL; + + #define SSL_FILETYPE_PEM 1 + #define SSL_VERIFY_NONE 0 + #define SSL_VERIFY_PEER 1 + #define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 2 + #define SSL_VERIFY_CLIENT_ONCE 3 + + typedef struct { + int fd; + tls_validation_function certificate_verify; + void *recv; + void *send; + void *user_data; + } SSLUserData; + + int SSL_library_init(); + void SSL_load_error_strings(); + void OpenSSL_add_all_algorithms(); + void OpenSSL_add_all_ciphers(); + void OpenSSL_add_all_digests(); + void EVP_cleanup(); + + int SSLv3_server_method(); + int SSLv3_client_method(); + struct TLSContext *SSL_new(struct TLSContext *context); + int SSL_CTX_use_certificate_file(struct TLSContext *context, const char *filename, int dummy); + int SSL_CTX_use_PrivateKey_file(struct TLSContext *context, const char *filename, int dummy); + int SSL_CTX_check_private_key(struct TLSContext *context); + struct TLSContext *SSL_CTX_new(int method); + void SSL_free(struct TLSContext *context); + void SSL_CTX_free(struct TLSContext *context); + int SSL_get_error(struct TLSContext *context, int ret); + int SSL_set_fd(struct TLSContext *context, int socket); + void *SSL_set_userdata(struct TLSContext *context, void *data); + void *SSL_userdata(struct TLSContext *context); + int SSL_CTX_root_ca(struct TLSContext *context, const char *pem_filename); + void SSL_CTX_set_verify(struct TLSContext *context, int mode, tls_validation_function verify_callback); + int SSL_accept(struct TLSContext *context); + int SSL_connect(struct TLSContext *context); + int SSL_shutdown(struct TLSContext *context); + int SSL_write(struct TLSContext *context, const void *buf, unsigned int len); + int SSL_read(struct TLSContext *context, void *buf, unsigned int len); + int SSL_pending(struct TLSContext *context); + int SSL_set_io(struct TLSContext *context, void *recv, void *send); +#endif + +#ifdef TLS_SRTP + struct SRTPContext; + #define SRTP_NULL 0 + #define SRTP_AES_CM 1 + #define SRTP_AUTH_NULL 0 + #define SRTP_AUTH_HMAC_SHA1 1 + + struct SRTPContext *srtp_init(unsigned char mode, unsigned char auth_mode); + int srtp_key(struct SRTPContext *context, const void *key, int keylen, const void *salt, int saltlen, int tag_bits); + int srtp_inline(struct SRTPContext *context, const char *b64, int tag_bits); + int srtp_encrypt(struct SRTPContext *context, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len); + int srtp_decrypt(struct SRTPContext *context, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len); + void srtp_destroy(struct SRTPContext *context); +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif \ No newline at end of file diff --git a/thirdparty/echttp/thirdparty/tlse_adapter.c b/thirdparty/echttp/thirdparty/tlse_adapter.c new file mode 100644 index 0000000..1436cac --- /dev/null +++ b/thirdparty/echttp/thirdparty/tlse_adapter.c @@ -0,0 +1,151 @@ +#ifdef _WIN32 +#include +#else +#include +#endif + +#ifdef ECHTTP_TLSE_IMPLEMENTATION +#define TLS_AMALGAMATION +#define LTC_NO_ASM +#define NO_SSL_COMPATIBLE_INTERFACE +#define TLS_MALLOC ECHTTP_MALLOC +#define TLS_REALLOC ECHTTP_REALLOC +#define TLS_FREE ECHTTP_FREE +#include "tlse.c" +#else +#include "tlse.h" +#endif + +void echttp_tlse_wrapper_error(char* msg) +{ + perror(msg); + exit(0); +} + +int echttp_tlse_wrapper_send_pending(int client_sock, struct TLSContext* context) +{ + unsigned int out_buffer_len = 0; + const unsigned char* out_buffer = tls_get_write_buffer(context, &out_buffer_len); + unsigned int out_buffer_index = 0; + int send_res = 0; + while ((out_buffer) && (out_buffer_len > 0)) + { + int res = send(client_sock, (char*)&out_buffer[out_buffer_index], out_buffer_len, 0); + if (res <= 0) + { + send_res = res; + break; + } + out_buffer_len -= res; + out_buffer_index += res; + } + tls_buffer_clear(context); + return send_res; +} + +int echttp_tlse_wrapper_validate_certificate(struct TLSContext* context, struct TLSCertificate** certificate_chain, int len) +{ + int i; + if (certificate_chain) + { + for (i = 0; i < len; i++) + { + struct TLSCertificate* certificate = certificate_chain[i]; + // TODO: Validate certificate + } + } + // return certificate_expired; + // return certificate_revoked; + // return certificate_unknown; + return no_error; +} + +int echttp_tlse_wrapper_connect_socket(const char* host, int port) +{ + int sockfd; + struct sockaddr_in serv_addr; + struct hostent* server; +#ifdef _WIN32 + WSADATA wsaData; + WSAStartup(MAKEWORD(2, 2), &wsaData); +#else + signal(SIGPIPE, SIG_IGN); +#endif + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) + echttp_tlse_wrapper_error("ERROR opening socket"); + server = gethostbyname(host); + if (server == NULL) + { + fprintf(stderr, "ERROR, no such host\n"); + exit(0); + } + memset((char*)&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + memcpy((char*)&serv_addr.sin_addr.s_addr, (char*)server->h_addr, server->h_length); + serv_addr.sin_port = htons(port); + if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) + echttp_tlse_wrapper_error("ERROR connecting"); + return sockfd; +} + +int echttp_tlse_wrapper_connect_tls(int sockfd, struct TLSContext* context) +{ + int res = tls_client_connect(context); + echttp_tlse_wrapper_send_pending(sockfd, context); + unsigned char client_message[0xFFFF]; + for (;;) + { + int read_size = recv(sockfd, (char*)client_message, sizeof(client_message), 0); + tls_consume_stream(context, (const unsigned char*)client_message, read_size, echttp_tlse_wrapper_validate_certificate); + echttp_tlse_wrapper_send_pending(sockfd, context); + if (tls_established(context)) + { + break; + } + } + return res; +} + +int echttp_tlse_wrapper_read_tls(int sockfd, struct TLSContext* context, void* buffer, int len) +{ + unsigned char client_message[0xFFFF]; + int read_res; + int read_size; + int read = 0; + for (;;) + { + if (tls_established(context)) + { + unsigned char read_buffer[len]; + read_res = tls_read(context, read_buffer, sizeof(read_buffer) - read); + if (read_res > 0) + { + memcpy(buffer + read, read_buffer, read_res); + read += read_res; + } + } + if (read >= len) + { + break; + } + read_size = recv(sockfd, (char*)client_message, sizeof(client_message), 0); + if (read_size <= 0) + { + break; + } + tls_consume_stream(context, (const unsigned char*)client_message, read_size, echttp_tlse_wrapper_validate_certificate); + echttp_tlse_wrapper_send_pending(sockfd, context); + } + return read; +} + +int echttp_tlse_wrapper_write_tls(int sockfd, struct TLSContext* context, const unsigned char* data, int len) +{ + int write_res = tls_write(context, data, len); + if (write_res > 0) + { + echttp_tlse_wrapper_send_pending(sockfd, context); + } + return write_res; +} \ No newline at end of file diff --git a/thirdparty/hashmap/README.md b/thirdparty/hashmap/README.md new file mode 100644 index 0000000..c9b8cba --- /dev/null +++ b/thirdparty/hashmap/README.md @@ -0,0 +1,25 @@ +# HashMap +This is an implementation of https://github.com/Wertzui123/HashMap translated to C. + +## License +MIT License + +Copyright (c) 2022 Wertzui123 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/thirdparty/hashmap/hashmap.h b/thirdparty/hashmap/hashmap.h new file mode 100644 index 0000000..0e8e22a --- /dev/null +++ b/thirdparty/hashmap/hashmap.h @@ -0,0 +1,453 @@ +#include +#include +#include + +#ifndef HASHMAP_PREFIX +#error "HASHMAP_PREFIX must be defined" +#endif +#define HASHMAP_CONCAT(a, b) a##_##b +#define _HASHMAP_FUNC_NAME(x, name) HASHMAP_CONCAT(x, name) +#define _HASHMAP_STRUCT_NAME(x, name) HASHMAP_CONCAT(x, name) +#define HASHMAP_FUNC_NAME(name) _HASHMAP_FUNC_NAME(HASHMAP_PREFIX, name) +#define HASHMAP_STRUCT_NAME(name) _HASHMAP_FUNC_NAME(HASHMAP_PREFIX, name) + +#ifndef HASHMAP_KEY_TYPE +#error "HASHMAP_KEY_TYPE must be defined" +#endif +#ifndef HASHMAP_KEY_NULL_VALUE +#error "HASHMAP_KEY_NULL_VALUE must be defined" +#endif +#ifndef HASHMAP_VALUE_TYPE +#error "HASHMAP_VALUE_TYPE must be defined" +#endif +#ifndef HASHMAP_VALUE_NULL_VALUE +#error "HASHMAP_VALUE_NULL_VALUE must be defined" +#endif + +#ifndef HASHMAP_MALLOC +#define HASHMAP_MALLOC malloc +#endif +#ifndef HASHMAP_FREE +#define HASHMAP_FREE free +#endif +#ifndef HASHMAP_REALLOC +#define HASHMAP_REALLOC realloc +#endif + +unsigned int HASHMAP_FUNC_NAME(hash_key)(HASHMAP_KEY_TYPE key); +int HASHMAP_FUNC_NAME(equals_key)(HASHMAP_KEY_TYPE a, HASHMAP_KEY_TYPE b); +unsigned int HASHMAP_FUNC_NAME(hash_value)(HASHMAP_VALUE_TYPE value); +int HASHMAP_FUNC_NAME(equals_value)(HASHMAP_VALUE_TYPE a, HASHMAP_VALUE_TYPE b); + +typedef struct HASHMAP_STRUCT_NAME(Pair) +{ + HASHMAP_KEY_TYPE key; + HASHMAP_VALUE_TYPE value; +} HASHMAP_STRUCT_NAME(Pair); + +typedef struct HASHMAP_STRUCT_NAME(Bucket) +{ + HASHMAP_STRUCT_NAME(Pair)** pairs; + int num_pairs; +} HASHMAP_STRUCT_NAME(Bucket); + +typedef struct HASHMAP_STRUCT_NAME(HashMap) +{ + int pair_index; + HASHMAP_STRUCT_NAME(Pair)** pairs; + HASHMAP_STRUCT_NAME(Bucket)** buckets; + int buckets_length; + int len; +} HASHMAP_STRUCT_NAME(HashMap); + +typedef struct HASHMAP_STRUCT_NAME(HashMapConfig) +{ + int initial_capacity; +} HASHMAP_STRUCT_NAME(HashMapConfig); + +void HASHMAP_FUNC_NAME(hashmap_rehash)(HASHMAP_STRUCT_NAME(HashMap)* m, int new_capacity); + +HASHMAP_STRUCT_NAME(HashMap)* HASHMAP_FUNC_NAME(new_hashmap)(HASHMAP_STRUCT_NAME(HashMapConfig) config) +{ + if (config.initial_capacity <= 0) + { + printf("initial_capacity of hashmap must be greater than 0\n"); + return NULL; + } + + HASHMAP_STRUCT_NAME(Bucket)** buckets = (HASHMAP_STRUCT_NAME(Bucket)**)HASHMAP_MALLOC(config.initial_capacity * sizeof(HASHMAP_STRUCT_NAME(Bucket)*)); + for (int i = 0; i < config.initial_capacity; i++) + { + buckets[i] = (HASHMAP_STRUCT_NAME(Bucket)*)HASHMAP_MALLOC(sizeof(HASHMAP_STRUCT_NAME(Bucket))); + buckets[i]->pairs = NULL; + buckets[i]->num_pairs = 0; + } + + HASHMAP_STRUCT_NAME(HashMap)* hashmap = (HASHMAP_STRUCT_NAME(HashMap)*)HASHMAP_MALLOC(sizeof(HASHMAP_STRUCT_NAME(HashMap))); + hashmap->pair_index = 0; + hashmap->pairs = HASHMAP_MALLOC(sizeof(HASHMAP_STRUCT_NAME(Pair)*) * config.initial_capacity); + hashmap->buckets = buckets; + hashmap->buckets_length = config.initial_capacity; + hashmap->len = 0; + + return hashmap; +} + +int HASHMAP_FUNC_NAME(hashmap_contains_key)(HASHMAP_STRUCT_NAME(HashMap)* m, HASHMAP_KEY_TYPE key) +{ + if (m->len == 0) + { + return 0; + } + + unsigned int key_hash = HASHMAP_STRUCT_NAME(hash_key)(key); + HASHMAP_STRUCT_NAME(Bucket)* bucket = m->buckets[key_hash % m->buckets_length]; + if (bucket == NULL) + { + return 0; + } + + for (int i = 0; i < bucket->num_pairs; i++) + { + HASHMAP_STRUCT_NAME(Pair)* pair = bucket->pairs[i]; + if (HASHMAP_STRUCT_NAME(equals_key)(pair->key, key)) + { + return 1; + } + } + + return 0; +} + +int HASHMAP_FUNC_NAME(hashmap_contains_value)(HASHMAP_STRUCT_NAME(HashMap)* m, HASHMAP_VALUE_TYPE value) +{ + if (m->len == 0) + { + return 0; + } + + for (int i = 0; i < m->buckets_length; i++) + { + HASHMAP_STRUCT_NAME(Bucket)* bucket = m->buckets[i]; + for (int j = 0; j < bucket->num_pairs; j++) + { + HASHMAP_STRUCT_NAME(Pair)* pair = bucket->pairs[j]; + if (HASHMAP_STRUCT_NAME(equals_value)(pair->value, value)) + { + return 1; + } + } + } + + return 0; +} + +HASHMAP_VALUE_TYPE HASHMAP_FUNC_NAME(hashmap_get_value)(HASHMAP_STRUCT_NAME(HashMap)* m, HASHMAP_KEY_TYPE key) +{ + if (m->len == 0) + { + return HASHMAP_VALUE_NULL_VALUE; + } + + unsigned int key_hash = HASHMAP_STRUCT_NAME(hash_key)(key); + HASHMAP_STRUCT_NAME(Bucket)* bucket = m->buckets[key_hash % m->buckets_length]; + if (bucket == NULL) + { + return HASHMAP_VALUE_NULL_VALUE; + } + + for (int i = 0; i < bucket->num_pairs; i++) + { + HASHMAP_STRUCT_NAME(Pair)* pair = bucket->pairs[i]; + if (HASHMAP_STRUCT_NAME(equals_key)(pair->key, key)) + { + return pair->value; + } + } + + return HASHMAP_VALUE_NULL_VALUE; +} + +HASHMAP_KEY_TYPE HASHMAP_FUNC_NAME(hashmap_get_key)(HASHMAP_STRUCT_NAME(HashMap)* m, HASHMAP_VALUE_TYPE value) +{ + if (m->len == 0) + { + return HASHMAP_KEY_NULL_VALUE; + } + + for (int i = 0; i < m->buckets_length; i++) + { + HASHMAP_STRUCT_NAME(Bucket)* bucket = m->buckets[i]; + for (int j = 0; j < bucket->num_pairs; j++) + { + HASHMAP_STRUCT_NAME(Pair)* pair = bucket->pairs[j]; + if (HASHMAP_STRUCT_NAME(equals_value)(pair->value, value)) + { + return pair->key; + } + } + } + + return HASHMAP_KEY_NULL_VALUE; +} + +void HASHMAP_FUNC_NAME(hashmap_set)(HASHMAP_STRUCT_NAME(HashMap)* m, HASHMAP_KEY_TYPE key, HASHMAP_VALUE_TYPE value) +{ + unsigned int key_hash = HASHMAP_STRUCT_NAME(hash_key)(key); + int index = key_hash % m->buckets_length; + + HASHMAP_STRUCT_NAME(Bucket)* bucket = m->buckets[index]; + for (int i = 0; i < bucket->num_pairs; i++) + { + HASHMAP_STRUCT_NAME(Pair)* pair = bucket->pairs[i]; + if (HASHMAP_STRUCT_NAME(equals_key)(pair->key, key)) + { + pair->value = value; + return; + } + } + + HASHMAP_STRUCT_NAME(Pair)* pair = (HASHMAP_STRUCT_NAME(Pair)*)HASHMAP_MALLOC(sizeof(HASHMAP_STRUCT_NAME(Pair))); + pair->key = key; + pair->value = value; + + HASHMAP_STRUCT_NAME(Pair)** new_pairs = (HASHMAP_STRUCT_NAME(Pair)**)HASHMAP_REALLOC(bucket->pairs, (bucket->num_pairs + 1) * sizeof(HASHMAP_STRUCT_NAME(Pair)*)); + new_pairs[bucket->num_pairs] = pair; + bucket->pairs = new_pairs; + bucket->num_pairs++; + + HASHMAP_STRUCT_NAME(Pair)** new_all_pairs = (HASHMAP_STRUCT_NAME(Pair)**)HASHMAP_REALLOC(m->pairs, (m->len + 1) * sizeof(HASHMAP_STRUCT_NAME(Pair)*)); + new_all_pairs[m->len] = pair; + m->pairs = new_all_pairs; + + m->len++; + + const float load_factor_threshold = 0.75; + if ((float)m->len / m->buckets_length > load_factor_threshold) + { + HASHMAP_FUNC_NAME(hashmap_rehash)(m, m->buckets_length * 2); + } +} + +int HASHMAP_FUNC_NAME(hashmap_remove)(HASHMAP_STRUCT_NAME(HashMap)* m, HASHMAP_KEY_TYPE key) +{ + unsigned int key_hash = HASHMAP_STRUCT_NAME(hash_key)(key); + int index = key_hash % m->buckets_length; + + HASHMAP_STRUCT_NAME(Bucket)* bucket = m->buckets[index]; + if (bucket == NULL) + { + return 0; + } + + for (int i = 0; i < bucket->num_pairs; i++) + { + HASHMAP_STRUCT_NAME(Pair)* pair = bucket->pairs[i]; + if (HASHMAP_STRUCT_NAME(equals_key)(pair->key, key)) + { + HASHMAP_FREE(pair); + for (int j = 0; j < m->len; j++) + { + if (m->pairs[j] == pair) + { + for (int k = j; k < m->len - 1; k++) + { + m->pairs[k] = m->pairs[k + 1]; + } + break; + } + } + m->len--; + if (bucket->num_pairs == 1) + { + HASHMAP_FREE(bucket->pairs); + bucket->pairs = NULL; + } + else + { + HASHMAP_STRUCT_NAME(Pair)** new_pairs = (HASHMAP_STRUCT_NAME(Pair)**)HASHMAP_MALLOC((bucket->num_pairs - 1) * sizeof(HASHMAP_STRUCT_NAME(Pair)*)); + for (int j = 0, k = 0; j < bucket->num_pairs; j++) + { + if (bucket->pairs[j] != pair) + { + new_pairs[k] = bucket->pairs[j]; + k++; + } + } + HASHMAP_FREE(bucket->pairs); + bucket->pairs = new_pairs; + bucket->num_pairs--; + } + + const float load_factor_threshold = 0.25; + if ((float)m->len / m->buckets_length < load_factor_threshold) + { + int new_capacity = m->buckets_length / 2; + if (new_capacity < 1) + { + new_capacity = 1; + } + HASHMAP_FUNC_NAME(hashmap_rehash)(m, new_capacity); + } + + return 1; + } + } + + return 0; +} + +void HASHMAP_FUNC_NAME(hashmap_rehash)(HASHMAP_STRUCT_NAME(HashMap)* m, int new_capacity) +{ + HASHMAP_STRUCT_NAME(Bucket)** old_buckets = m->buckets; + HASHMAP_STRUCT_NAME(Pair)** old_pairs = m->pairs; + int old_buckets_length = m->buckets_length; + int old_length = m->len; + + m->pairs = (HASHMAP_STRUCT_NAME(Pair)**)HASHMAP_MALLOC(sizeof(HASHMAP_STRUCT_NAME(Pair)*)); + m->buckets = (HASHMAP_STRUCT_NAME(Bucket)**)HASHMAP_MALLOC(new_capacity * sizeof(HASHMAP_STRUCT_NAME(Bucket)*)); + m->buckets_length = new_capacity; + m->len = 0; + + for (int i = 0; i < new_capacity; i++) + { + m->buckets[i] = (HASHMAP_STRUCT_NAME(Bucket)*)HASHMAP_MALLOC(sizeof(HASHMAP_STRUCT_NAME(Bucket))); + m->buckets[i]->pairs = NULL; + m->buckets[i]->num_pairs = 0; + } + + for (int i = 0; i < old_length; i++) + { + HASHMAP_STRUCT_NAME(Pair)* pair = old_pairs[i]; + HASHMAP_FUNC_NAME(hashmap_set)(m, pair->key, pair->value); + } + + for (int i = 0; i < old_length; i++) + { + HASHMAP_FREE(old_pairs[i]); + } + + HASHMAP_FREE(old_pairs); + + for (int i = 0; i < old_buckets_length; i++) + { + HASHMAP_FREE(old_buckets[i]->pairs); + HASHMAP_FREE(old_buckets[i]); + } + + HASHMAP_FREE(old_buckets); +} + +unsigned int HASHMAP_FUNC_NAME(hashmap_hash)(HASHMAP_STRUCT_NAME(HashMap)* m) +{ + unsigned int i = 0; + for (int j = 0; j < m->buckets_length; j++) + { + HASHMAP_STRUCT_NAME(Bucket)* bucket = m->buckets[j]; + for (int k = 0; k < bucket->num_pairs; k++) + { + HASHMAP_STRUCT_NAME(Pair)* pair = bucket->pairs[k]; + i += i * 31 + HASHMAP_STRUCT_NAME(hash_key)(pair->key); + i += i * 31 + HASHMAP_STRUCT_NAME(hash_value)(pair->value); + } + } + return i; +} + +int HASHMAP_FUNC_NAME(hashmap_equals)(HASHMAP_STRUCT_NAME(HashMap)* a, HASHMAP_STRUCT_NAME(HashMap)* b) +{ + if (a->len != b->len) + { + return 0; + } + + for (int i = 0; i < a->buckets_length; i++) + { + HASHMAP_STRUCT_NAME(Bucket)* bucket = a->buckets[i]; + for (int j = 0; j < bucket->num_pairs; j++) + { + HASHMAP_STRUCT_NAME(Pair)* pair = bucket->pairs[j]; + if (!HASHMAP_FUNC_NAME(hashmap_contains_key)(b, pair->key)) + { + return 0; + } + HASHMAP_VALUE_TYPE b_value = HASHMAP_FUNC_NAME(hashmap_get_value)(b, pair->key); + if (b_value == HASHMAP_VALUE_NULL_VALUE || !HASHMAP_STRUCT_NAME(equals_value)(pair->value, b_value)) + { + return 0; + } + } + } + + return 1; +} + +HASHMAP_STRUCT_NAME(HashMap)* HASHMAP_FUNC_NAME(hashmap_reverse)(HASHMAP_STRUCT_NAME(HashMap)* m) +{ + HASHMAP_STRUCT_NAME(HashMap)* reverse = HASHMAP_FUNC_NAME(new_hashmap)((HASHMAP_STRUCT_NAME(HashMapConfig)) { m->buckets_length }); + + for (int i = m->len - 1; i >= 0; i--) + { + HASHMAP_STRUCT_NAME(Pair)* pair = m->pairs[i]; + HASHMAP_FUNC_NAME(hashmap_set) + (reverse, pair->key, pair->value); + } + + return reverse; +} + +HASHMAP_STRUCT_NAME(HashMap)* HASHMAP_FUNC_NAME(hashmap_clear)(HASHMAP_STRUCT_NAME(HashMap)* m) +{ + for (int i = 0; i < m->buckets_length; i++) + { + HASHMAP_STRUCT_NAME(Bucket)* bucket = m->buckets[i]; + for (int j = 0; j < bucket->num_pairs; j++) + { + HASHMAP_FREE(bucket->pairs[j]); + } + HASHMAP_FREE(bucket->pairs); + bucket->pairs = NULL; + bucket->num_pairs = 0; + } + + HASHMAP_STRUCT_NAME(Pair)** new_pairs = (HASHMAP_STRUCT_NAME(Pair)**)HASHMAP_REALLOC(m->pairs, sizeof(HASHMAP_STRUCT_NAME(Pair)*)); + m->pairs = new_pairs; + m->len = 0; + + return m; +} + +HASHMAP_STRUCT_NAME(Pair)* HASHMAP_FUNC_NAME(hashmap_next)(HASHMAP_STRUCT_NAME(HashMap)* m) +{ + if (m->len == 0) + { + return NULL; + } + + if (m->pair_index == m->len) + { + m->pair_index = 0; + return NULL; + } + + HASHMAP_STRUCT_NAME(Pair)* pair = m->pairs[m->pair_index]; + m->pair_index++; + return pair; +} + +void HASHMAP_FUNC_NAME(hashmap_free_hashmap)(HASHMAP_STRUCT_NAME(HashMap)* m) +{ + for (int i = 0; i < m->buckets_length; i++) + { + HASHMAP_STRUCT_NAME(Bucket)* bucket = m->buckets[i]; + for (int j = 0; j < bucket->num_pairs; j++) + { + HASHMAP_FREE(bucket->pairs[j]); + } + HASHMAP_FREE(bucket->pairs); + HASHMAP_FREE(bucket); + } + HASHMAP_FREE(m->buckets); + HASHMAP_FREE(m->pairs); + HASHMAP_FREE(m); +} diff --git a/thirdparty/icylib/.gitignore b/thirdparty/icylib/.gitignore new file mode 100644 index 0000000..68d66f8 --- /dev/null +++ b/thirdparty/icylib/.gitignore @@ -0,0 +1,4 @@ +.vscode +*.exe +*.png +!examples/*/*.png diff --git a/thirdparty/icylib/README.md b/thirdparty/icylib/README.md new file mode 100644 index 0000000..9b3c0ce --- /dev/null +++ b/thirdparty/icylib/README.md @@ -0,0 +1,55 @@ +# icylib 🧊 +`icylib` is a simple CPU-based image processing and manipulation library built on top of [stb](https://github.com/nothings/stb). + +It abstracts away the complexity of working with images by providing a simple interface for drawing shapes, rendering text, chunking images, and much more. + +Please note that `icylib` does **not** utilize any hardware acceleration (except SIMD instructions) and is thus not really suitable for real-time rendering. Nonetheless, it does use some fairly optimized algorithms and is still quite fast for a lot of use cases. + +## How to use? +icylib is **not** a single-header library for the sake of readability and because of its dependencies. Yet, it is still very easy to use, as it only uses header files for both its interface and its implementation. + +If you want to use icylib in your project, you can simply include the header files you need in your source files, and some fancy include guards will take care of the rest. + +### Example: [Julia Set](examples/julia) +```c +#define ICYLIB_IMPLEMENTATION + +#include "regular_image.h" +#include "color.h" + +void main() { + icylib_RegularImage* image = icylib_regular_create_from_size(800, 800, 3); + + for (int y = 0; y < image->height; y++) { + for (int x = 0; x < image->width; x++) { + double a = (double)x / image->width * 4 - 2; + double b = (double)y / image->height * 4 - 2; + double ca = -0.8; + double cb = 0.156; + int n = 0; + while (n < 100) { + double aa = a * a - b * b; + double bb = 2 * a * b; + a = aa + ca; + b = bb + cb; + if (a * a + b * b > 16) { + break; + } + n++; + } + icylib_Color color = icylib_color_from_rgb(0, 0, 0); + if (n < 100) { + color = icylib_color_from_rgb(255 * n / 100, 255 * n / 100, 255 * n / 100); + } + icylib_regular_set_pixel(image, x, y, color); + } + } + + icylib_regular_save_to_file(image, "julia_set.png"); +} +``` +```console +$ gcc julia.c -o julia -I path/to/icylib +$ ./julia +``` +[![Julia Set](examples/julia/julia_set.png)](examples/julia/julia_set.png) \ No newline at end of file diff --git a/thirdparty/icylib/bresenham.h b/thirdparty/icylib/bresenham.h new file mode 100644 index 0000000..df1e265 --- /dev/null +++ b/thirdparty/icylib/bresenham.h @@ -0,0 +1,78 @@ +#ifndef ICYLIB_BRESENHAM_H +#define ICYLIB_BRESENHAM_H + +#include "color.h" + +void icylib_draw_bresenham_circle(unsigned char* image, int xm, int ym, int r, icylib_Color color, void (*set_pixel)(unsigned char* image, int, int, icylib_Color)); + +void icylib_draw_bresenham_thick_line(unsigned char* image, int x0, int y0, int x1, int y1, int thickness, icylib_Color color, void (*set_pixel)(unsigned char* image, int, int, icylib_Color)); + +#ifdef ICYLIB_IMPLEMENTATION + +#include + +#include "icylib.h" + +// The following functions were originally written by Alois Zingl and slightly modified to fit this project. +/* +MIT License + +Copyright (c) 2020 zingl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +void icylib_draw_bresenham_circle(unsigned char* image, int xm, int ym, int r, icylib_Color color, void (*set_pixel)(unsigned char* image, int, int, icylib_Color)) +{ + int x = -r, y = 0, err = 2 - 2 * r; /* bottom left to top right */ + do { + set_pixel(image, xm - x, ym + y, color); /* I. Quadrant +x +y */ + set_pixel(image, xm - y, ym - x, color); /* II. Quadrant -x +y */ + set_pixel(image, xm + x, ym - y, color); /* III. Quadrant -x -y */ + set_pixel(image, xm + y, ym + x, color); /* IV. Quadrant +x -y */ + r = err; + if (r <= y) err += ++y * 2 + 1; /* e_xy+e_y < 0 */ + if (r > x || err > y) /* e_xy+e_x > 0 or no 2nd y-step */ + err += ++x * 2 + 1; /* -> x-step now */ + } while (x < 0); +} + +void icylib_draw_bresenham_thick_line(unsigned char* image, int x0, int y0, int x1, int y1, int thickness, icylib_Color color, void (*set_pixel)(unsigned char* image, int, int, icylib_Color)) +{ + int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; + int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1; + int err = dx + dy, e2; /* error value e_xy */ + + for (;;) { /* loop */ + for (int i = 0; i < thickness; i++) { + set_pixel(image, x0, y0 + i, color); + set_pixel(image, x0, y0 - i, color); + set_pixel(image, x0 + i, y0, color); + set_pixel(image, x0 - i, y0, color); + } + if (x0 == x1 && y0 == y1) break; + e2 = 2 * err; + if (e2 >= dy) { err += dy; x0 += sx; } /* e_xy+e_x > 0 */ + if (e2 <= dx) { err += dx; y0 += sy; } /* e_xy+e_y < 0 */ + } +} + +#endif + +#endif \ No newline at end of file diff --git a/thirdparty/icylib/color.h b/thirdparty/icylib/color.h new file mode 100644 index 0000000..bf81556 --- /dev/null +++ b/thirdparty/icylib/color.h @@ -0,0 +1,38 @@ +#ifndef ICYLIB_COLOR_H +#define ICYLIB_COLOR_H + +#include "icylib.h" + +typedef struct icylib_Color { + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; +} icylib_Color; + +icylib_Color icylib_color_from_rgb(unsigned char r, unsigned char g, unsigned char b); +icylib_Color icylib_color_from_rgba(unsigned char r, unsigned char g, unsigned char b, unsigned char a); + +#ifdef ICYLIB_IMPLEMENTATION + +icylib_Color icylib_color_from_rgb(unsigned char r, unsigned char g, unsigned char b) { + icylib_Color color; + color.r = r; + color.g = g; + color.b = b; + color.a = 255; + return color; +} + +icylib_Color icylib_color_from_rgba(unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + icylib_Color color; + color.r = r; + color.g = g; + color.b = b; + color.a = a; + return color; +} + +#endif + +#endif \ No newline at end of file diff --git a/thirdparty/icylib/examples/julia/julia.c b/thirdparty/icylib/examples/julia/julia.c new file mode 100644 index 0000000..1a8a2f8 --- /dev/null +++ b/thirdparty/icylib/examples/julia/julia.c @@ -0,0 +1,35 @@ +#define ICYLIB_IMPLEMENTATION + +#include "regular_image.h" +#include "color.h" + +void main() { + icylib_RegularImage* image = icylib_regular_create_from_size(800, 800, 3); + + for (int y = 0; y < image->height; y++) { + for (int x = 0; x < image->width; x++) { + double a = (double)x / image->width * 4 - 2; + double b = (double)y / image->height * 4 - 2; + double ca = -0.8; + double cb = 0.156; + int n = 0; + while (n < 100) { + double aa = a * a - b * b; + double bb = 2 * a * b; + a = aa + ca; + b = bb + cb; + if (a * a + b * b > 16) { + break; + } + n++; + } + icylib_Color color = icylib_color_from_rgb(0, 0, 0); + if (n < 100) { + color = icylib_color_from_rgb(255 * n / 100, 255 * n / 100, 255 * n / 100); + } + icylib_regular_set_pixel(image, x, y, color); + } + } + + icylib_regular_save_to_file(image, "julia_set.png"); +} \ No newline at end of file diff --git a/thirdparty/icylib/examples/julia/julia_set.png b/thirdparty/icylib/examples/julia/julia_set.png new file mode 100644 index 0000000..e21d5ea Binary files /dev/null and b/thirdparty/icylib/examples/julia/julia_set.png differ diff --git a/thirdparty/icylib/examples/shapes/shapes.c b/thirdparty/icylib/examples/shapes/shapes.c new file mode 100644 index 0000000..0ae9769 --- /dev/null +++ b/thirdparty/icylib/examples/shapes/shapes.c @@ -0,0 +1,15 @@ +#define ICYLIB_IMPLEMENTATION + +#include "regular_image.h" +#include "color.h" + +void main() { + icylib_RegularImage* image = icylib_regular_create_from_size(500, 500, 4); + + icylib_regular_fill_rectangle(image, 100, 100, 400, 400, icylib_color_from_rgb(128, 64, 32), 0); + icylib_regular_draw_rectangle(image, 150, 150, 350, 350, icylib_color_from_rgb(32, 64, 128), 0); + icylib_regular_fill_circle(image, 250, 250, 100, icylib_color_from_rgb(128, 64, 128), 0); + icylib_regular_draw_circle(image, 250, 250, 200, icylib_color_from_rgb(32, 64, 32), 0); + + icylib_regular_save_to_file(image, "shapes.png"); +} \ No newline at end of file diff --git a/thirdparty/icylib/examples/shapes/shapes.png b/thirdparty/icylib/examples/shapes/shapes.png new file mode 100644 index 0000000..8c838b6 Binary files /dev/null and b/thirdparty/icylib/examples/shapes/shapes.png differ diff --git a/thirdparty/icylib/examples/text/text.c b/thirdparty/icylib/examples/text/text.c new file mode 100644 index 0000000..2153b17 --- /dev/null +++ b/thirdparty/icylib/examples/text/text.c @@ -0,0 +1,14 @@ +#define ICYLIB_IMPLEMENTATION + +#include "regular_image.h" +#include "color.h" + +void main() { + icylib_RegularImage* image = icylib_regular_create_from_size(500, 500, 4); + + icylib_regular_draw_text(image, "Left aligned", 250, 125, icylib_color_from_rgb(255, 0, 0), icylib_get_default_font_path(), 30, ICYLIB_HORIZONTAL_ALIGNMENT_LEFT, ICYLIB_VERTICAL_ALIGNMENT_CENTER, 1); + icylib_regular_draw_text(image, "Middle aligned", 250, 250, icylib_color_from_rgb(0, 255, 0), icylib_get_default_font_path(), 40, ICYLIB_HORIZONTAL_ALIGNMENT_CENTER, ICYLIB_VERTICAL_ALIGNMENT_CENTER, 1); + icylib_regular_draw_text(image, "Middle aligned", 250, 375, icylib_color_from_rgb(0, 0, 255), icylib_get_default_font_path(), 30, ICYLIB_HORIZONTAL_ALIGNMENT_RIGHT, ICYLIB_VERTICAL_ALIGNMENT_CENTER, 1); + + icylib_regular_save_to_file(image, "text.png"); +} \ No newline at end of file diff --git a/thirdparty/icylib/examples/text/text.png b/thirdparty/icylib/examples/text/text.png new file mode 100644 index 0000000..3c2ad5b Binary files /dev/null and b/thirdparty/icylib/examples/text/text.png differ diff --git a/thirdparty/icylib/file_utils.h b/thirdparty/icylib/file_utils.h new file mode 100644 index 0000000..cf06a0c --- /dev/null +++ b/thirdparty/icylib/file_utils.h @@ -0,0 +1,42 @@ +#ifndef ICYLIB_FILE_UTILS_H +#define ICYLIB_FILE_UTILS_H + +char icylib_file_exists(const char* path); +size_t icylib_file_size(const char* path); +char icylib_file_delete(const char* path); + +#ifdef ICYLIB_IMPLEMENTATION + +#ifdef _WIN32 +#include +#else +#include +#endif +#include + +char icylib_file_exists(const char* path) { +#ifdef _WIN32 + return _access(path, 0) != -1; +#else + return access(path, F_OK) != -1; +#endif +} + +size_t icylib_file_size(const char* path) { + FILE* file = fopen(path, "rb"); + if (!file) { + return 0; + } + fseek(file, 0, SEEK_END); + size_t size = ftell(file); + fclose(file); + return size; +} + +char icylib_file_delete(const char* path) { + return remove(path) == 0; +} + +#endif + +#endif \ No newline at end of file diff --git a/thirdparty/icylib/icylib.h b/thirdparty/icylib/icylib.h new file mode 100644 index 0000000..6b776d0 --- /dev/null +++ b/thirdparty/icylib/icylib.h @@ -0,0 +1,66 @@ +#if !defined(ICYLIB_MALLOC) || !defined(ICYLIB_REALLOC) || !defined(ICYLIB_FREE) +#include +#endif +#ifndef ICYLIB_MALLOC +#define ICYLIB_MALLOC malloc +#endif +#ifndef ICYLIB_MALLOC_ATOMIC +#define ICYLIB_MALLOC_ATOMIC ICYLIB_MALLOC +#endif +#ifndef ICYLIB_REALLOC +#define ICYLIB_REALLOC realloc +#endif +#ifndef ICYLIB_FREE +#define ICYLIB_FREE free +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC ICYLIB_MALLOC +#endif +#ifndef STBI_REALLOC +#define STBI_REALLOC ICYLIB_REALLOC +#endif +#ifndef STBI_FREE +#define STBI_FREE ICYLIB_FREE +#endif + +#ifndef STBIR_MALLOC +#define STBIR_MALLOC(size, user_data) ((void)(user_data), ICYLIB_MALLOC(size)) +#endif +// realloc is not used by stb_image_resize2 +#ifndef STBIR_FREE +#define STBIR_FREE(ptr, user_data) ((void)(user_data), ICYLIB_FREE(ptr)) +#endif + +#ifndef STBTT_malloc +#define STBTT_malloc(x, u) ICYLIB_MALLOC(x) +#endif +// realloc is not used by stb_truetype +#ifndef STBTT_free +#define STBTT_free(x, u) ICYLIB_FREE(x) +#endif + +#ifndef ICYLIB_H +#define ICYLIB_H +#ifdef ICYLIB_IMPLEMENTATION +// TODO: Maybe add another include guard for the individual stb implementations? +#define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_WRITE_IMPLEMENTATION +#define STB_IMAGE_RESIZE2_IMPLEMENTATION +#define STB_TRUETYPE_IMPLEMENTATION +#endif +#include "thirdparty/stb_image.h" +#include "thirdparty/stb_image_write.h" +#include "thirdparty/stb_image_resize2.h" +#include "thirdparty/stb_truetype.h" +#endif + +// undefine to prevent double includes +#undef STB_IMAGE_IMPLEMENTATION +#undef STB_IMAGE_WRITE_IMPLEMENTATION +#undef STB_TRUETYPE_IMPLEMENTATION + +#ifndef ICYLIB_ERROR +#include +#define ICYLIB_ERROR(message) { assert(!message); } +#endif \ No newline at end of file diff --git a/thirdparty/icylib/lazy_chunked_image.h b/thirdparty/icylib/lazy_chunked_image.h new file mode 100644 index 0000000..4d04f9c --- /dev/null +++ b/thirdparty/icylib/lazy_chunked_image.h @@ -0,0 +1,630 @@ +#ifndef ICYLIB_LAZY_CHUNKED_IMAGE_H +#define ICYLIB_LAZY_CHUNKED_IMAGE_H + +#include + +#include "thirdparty/stb_image_write.h" + +#include "icylib.h" +#include "regular_image.h" +#include "color.h" +#include "thick_xiaolin_wu.h" +#include "text.h" +#include "math_utils.h" +#include "file_utils.h" + +const unsigned int ICYLIB_LAZY_CHUNK_WIDTH = 256; +const unsigned int ICYLIB_LAZY_CHUNK_HEIGHT = 256; + +typedef struct icylib_LazyChunkRow { + icylib_RegularImage** chunk_columns; +} icylib_LazyChunkRow; + +typedef struct icylib_LazyChunkedImage { + int width; + int height; + int channels; + icylib_LazyChunkRow* chunk_rows; + int rows_length; + int columns_length; + const char* directory; +} icylib_LazyChunkedImage; + +icylib_LazyChunkedImage* icylib_lazy_chunked_create_from_size(int width, int height, int channels, const char* directory); + +icylib_LazyChunkedImage* icylib_lazy_chunked_create_from_memory(unsigned char* data, int width, int height, int channels); + +icylib_LazyChunkedImage* icylib_lazy_chunked_load_from_file(const char* filename, const char* directory); + +int icylib_lazy_chunked_save_to_file(icylib_LazyChunkedImage* image, const char* filename, int* length); + +void icylib_lazy_chunked_free(icylib_LazyChunkedImage* image); + +char icylib_lazy_chunked_is_chunk_loaded(icylib_LazyChunkedImage* image, int x, int y); + +void icylib_lazy_chunked_load_chunk(icylib_LazyChunkedImage* image, int x, int y); + +void icylib_lazy_chunked_unload_chunk(icylib_LazyChunkedImage* image, int x, int y); + +void icylib_lazy_chunked_require_area(icylib_LazyChunkedImage* image, int x, int y, int width, int height); + +icylib_Color icylib_lazy_chunked_get_pixel(icylib_LazyChunkedImage* image, int x, int y); + +void icylib_lazy_chunked_set_pixel(icylib_LazyChunkedImage* image, int x, int y, icylib_Color color); + +void icylib_lazy_chunked_set_pixel_blend(icylib_LazyChunkedImage* image, int x, int y, icylib_Color color); + +void icylib_lazy_chunked_fill(icylib_LazyChunkedImage* image, icylib_Color color, unsigned char blend); + +void icylib_lazy_chunked_draw_rectangle(icylib_LazyChunkedImage* image, int x1, int y1, int x2, int y2, icylib_Color color, unsigned char blend); + +void icylib_lazy_chunked_fill_rectangle(icylib_LazyChunkedImage* image, int x1, int y1, int x2, int y2, icylib_Color color, unsigned char blend); + +void icylib_lazy_chunked_draw_image(icylib_LazyChunkedImage* image, int x, int y, icylib_RegularImage* other, unsigned char blend); + +void icylib_lazy_chunked_draw_circle(icylib_LazyChunkedImage* image, int x, int y, int radius, icylib_Color color, unsigned char blend); + +void icylib_lazy_chunked_fill_circle(icylib_LazyChunkedImage* image, int x, int y, int radius, icylib_Color color, unsigned char blend); + +void icylib_lazy_chunked_draw_line_with_thickness(icylib_LazyChunkedImage* image, int x1, int y1, int x2, int y2, int thickness, icylib_Color color, unsigned char blend, unsigned char antialias); + +void icylib_lazy_chunked_draw_line(icylib_LazyChunkedImage* image, int x1, int y1, int x2, int y2, icylib_Color color, unsigned char blend, unsigned char antialias); + +void icylib_lazy_chunked_draw_text(icylib_LazyChunkedImage* image, char* text, int x, int y, icylib_Color color, char* fontPath, int pixelSize, icylib_HorizontalAlignment horizontalAlignment, icylib_VerticalAlignment verticalAlignment, unsigned char blend); + +void icylib_lazy_chunked_replace_color(icylib_LazyChunkedImage* image, icylib_Color old_color, icylib_Color new_color, unsigned char blend); + +void icylib_lazy_chunked_replace_color_ignore_alpha(icylib_LazyChunkedImage* image, icylib_Color old_color, icylib_Color new_color); + +void icylib_lazy_chunked_blur(icylib_LazyChunkedImage* image, int radius); + +icylib_RegularImage* icylib_lazy_chunked_get_sub_image(icylib_LazyChunkedImage* image, int x, int y, int width, int height); + +icylib_LazyChunkedImage* icylib_lazy_chunked_copy(icylib_LazyChunkedImage* image); + +void icylib_lazy_chunked_extend_to(icylib_LazyChunkedImage* image, int new_width, int new_height); + +void icylib_lazy_chunked_resize(icylib_LazyChunkedImage* image, int width, int height); + +void icylib_lazy_chunked_resize_scale(icylib_LazyChunkedImage* image, float scale); + +#ifdef ICYLIB_IMPLEMENTATION + +icylib_LazyChunkedImage* icylib_lazy_chunked_create_from_size(int width, int height, int channels, const char* directory) { + icylib_LazyChunkRow* chunk_rows = ICYLIB_MALLOC(sizeof(icylib_LazyChunkRow) * icylib_ceil((float)height / ICYLIB_LAZY_CHUNK_HEIGHT)); + for (int j = 0; j < icylib_ceil((float)height / ICYLIB_LAZY_CHUNK_HEIGHT); j++) { + icylib_LazyChunkRow row; + icylib_RegularImage** chunk_columns = ICYLIB_MALLOC(sizeof(icylib_RegularImage*) * icylib_ceil((float)width / ICYLIB_LAZY_CHUNK_WIDTH)); + for (int i = 0; i < icylib_ceil((float)width / ICYLIB_LAZY_CHUNK_WIDTH); i++) { + chunk_columns[i] = icylib_regular_create_from_size(ICYLIB_LAZY_CHUNK_WIDTH, ICYLIB_LAZY_CHUNK_HEIGHT, channels); + } + row.chunk_columns = chunk_columns; + chunk_rows[j] = row; + } + icylib_LazyChunkedImage* image = ICYLIB_MALLOC(sizeof(icylib_LazyChunkedImage)); + image->width = width; + image->height = height; + image->channels = channels; + image->chunk_rows = chunk_rows; + image->rows_length = icylib_ceil((float)height / ICYLIB_LAZY_CHUNK_HEIGHT); + image->columns_length = icylib_ceil((float)width / ICYLIB_LAZY_CHUNK_WIDTH); + image->directory = directory; + return image; +} + +icylib_LazyChunkedImage* icylib_lazy_chunked_load_from_file(const char* filename, const char* directory) { + FILE* file = fopen(filename, "rb"); + if (!file) { + ICYLIB_ERROR("Failed to open file"); + return NULL; + } + fseek(file, 0, SEEK_END); + long length = ftell(file); + fseek(file, 0, SEEK_SET); + unsigned char* data = ICYLIB_MALLOC_ATOMIC(length); + fread(data, 1, length, file); + fclose(file); + + int width = (int)data[0] | (int)data[1] << 8 | (int)data[2] << 16 | (int)data[3] << 24; + int height = (int)data[4] | (int)data[5] << 8 | (int)data[6] << 16 | (int)data[7] << 24; + int columns = (int)data[8] | (int)data[9] << 8 | (int)data[10] << 16 | (int)data[11] << 24; + int rows = (int)data[12] | (int)data[13] << 8 | (int)data[14] << 16 | (int)data[15] << 24; + icylib_LazyChunkRow* chunk_rows = ICYLIB_MALLOC(sizeof(icylib_LazyChunkRow) * rows); + for (int j = 0; j < rows; j++) { + icylib_LazyChunkRow row; + icylib_RegularImage** chunk_columns = ICYLIB_MALLOC(sizeof(icylib_RegularImage*) * columns); + for (int i = 0; i < columns; i++) { + chunk_columns[i] = NULL; + } + row.chunk_columns = chunk_columns; + chunk_rows[j] = row; + } + icylib_LazyChunkedImage* image = ICYLIB_MALLOC(sizeof(icylib_LazyChunkedImage)); + image->width = width; + image->height = height; + image->channels = 4; + image->chunk_rows = chunk_rows; + image->rows_length = rows; + image->columns_length = columns; + image->directory = directory; + + ICYLIB_FREE(data); + return image; +} + +int icylib_lazy_chunked_save_to_file(icylib_LazyChunkedImage* image, const char* filename, int* length) { + unsigned int data_length = 16; + unsigned char* data = ICYLIB_MALLOC_ATOMIC(data_length); + + data[0] = (image->width >> 0) & 0xFF; + data[1] = (image->width >> 8) & 0xFF; + data[2] = (image->width >> 16) & 0xFF; + data[3] = (image->width >> 24) & 0xFF; + + data[4] = (image->height >> 0) & 0xFF; + data[5] = (image->height >> 8) & 0xFF; + data[6] = (image->height >> 16) & 0xFF; + data[7] = (image->height >> 24) & 0xFF; + + data[8] = (image->columns_length >> 0) & 0xFF; + data[9] = (image->columns_length >> 8) & 0xFF; + data[10] = (image->columns_length >> 16) & 0xFF; + data[11] = (image->columns_length >> 24) & 0xFF; + + data[12] = (image->rows_length >> 0) & 0xFF; + data[13] = (image->rows_length >> 8) & 0xFF; + data[14] = (image->rows_length >> 16) & 0xFF; + data[15] = (image->rows_length >> 24) & 0xFF; + + for (int j = 0; j < image->rows_length; j++) { + for (int i = 0; i < image->columns_length; i++) { + if (image->chunk_rows[j].chunk_columns[i] != NULL) { + char* path = ICYLIB_MALLOC_ATOMIC(strlen(image->directory) + + 1 // '/' + + 1 // '\0' + + 1 // '_' + + 10 // max int length + + 1 // '_' + + 10 // max int length + + 4 // ".png" + ); + sprintf(path, "%s/%d_%d.png", image->directory, i, j); + while (1) { + if (icylib_regular_save_to_file(image->chunk_rows[j].chunk_columns[i], path)) break; + // TODO: Add a limit to the number of retries + } + ICYLIB_FREE(path); + } + } + } + + FILE* file = fopen(filename, "wb"); + if (!file) { + ICYLIB_ERROR("Failed to open file"); + return 0; + } + fwrite(data, 1, data_length, file); + fclose(file); + ICYLIB_FREE(data); + + *length = data_length; + return 1; +} + +void icylib_lazy_chunked_free(icylib_LazyChunkedImage* image) { + for (int j = 0; j < (image->height / ICYLIB_LAZY_CHUNK_HEIGHT); j++) { + for (int i = 0; i < (image->width / ICYLIB_LAZY_CHUNK_WIDTH); i++) { + if (image->chunk_rows[j].chunk_columns[i] != NULL) { + icylib_regular_free(image->chunk_rows[j].chunk_columns[i]); + } + } + } + ICYLIB_FREE(image->chunk_rows); + ICYLIB_FREE(image); +} + +char icylib_lazy_chunked_is_chunk_loaded(icylib_LazyChunkedImage* image, int x, int y) { + return image->chunk_rows[y].chunk_columns[x] != NULL; +} + +void icylib_lazy_chunked_load_chunk(icylib_LazyChunkedImage* image, int x, int y) { + if (image->chunk_rows[y].chunk_columns[x] == NULL) { + char* path = ICYLIB_MALLOC_ATOMIC(strlen(image->directory) + + 1 // '/' + + 1 // '\0' + + 1 // '_' + + 10 // max int length + + 1 // '_' + + 10 // max int length + + 4 // ".png" + ); + sprintf(path, "%s/%d_%d.png", image->directory, x, y); + if (icylib_file_exists(path) && icylib_file_size(path) == 0) { // safety measure in case chunks got corrupted somehow + icylib_file_delete(path); + icylib_RegularImage* chunk = icylib_regular_create_from_size(ICYLIB_LAZY_CHUNK_WIDTH, ICYLIB_LAZY_CHUNK_HEIGHT, image->channels); + image->chunk_rows[y].chunk_columns[x] = chunk; + } + else { + icylib_RegularImage* f; + while (1) { + f = icylib_regular_load_from_file(path); + if (f != NULL) break; + // TODO: Add a limit to the number of retries + } + image->chunk_rows[y].chunk_columns[x] = f; + } + ICYLIB_FREE(path); + } +} + +void icylib_lazy_chunked_unload_chunk(icylib_LazyChunkedImage* image, int x, int y) { + if (image->chunk_rows[y].chunk_columns[x] != NULL) { + char* path = ICYLIB_MALLOC_ATOMIC(strlen(image->directory) + + 1 // '/' + + 1 // '\0' + + 1 // '_' + + 10 // max int length + + 1 // '_' + + 10 // max int length + + 4 // ".png" + ); + sprintf(path, "%s/%d_%d.png", image->directory, x, y); + while (1) { + if (icylib_regular_save_to_file(image->chunk_rows[y].chunk_columns[x], path)) break; + // TODO: Add a limit to the number of retries + } + ICYLIB_FREE(path); + icylib_regular_free(image->chunk_rows[y].chunk_columns[x]); + image->chunk_rows[y].chunk_columns[x] = NULL; + } +} + +void icylib_lazy_chunked_require_area(icylib_LazyChunkedImage* image, int x, int y, int width, int height) { + for (int j = 0; j < image->rows_length; j++) { + for (int i = 0; i < image->columns_length; i++) { + if (i * ICYLIB_LAZY_CHUNK_WIDTH <= x + width && i * ICYLIB_LAZY_CHUNK_WIDTH + ICYLIB_LAZY_CHUNK_WIDTH >= x + && j * ICYLIB_LAZY_CHUNK_HEIGHT <= y + height && j * ICYLIB_LAZY_CHUNK_HEIGHT + ICYLIB_LAZY_CHUNK_HEIGHT >= y) { + if (image->chunk_rows[j].chunk_columns[i] == NULL) { + char* path = ICYLIB_MALLOC_ATOMIC(strlen(image->directory) + + 1 // '/' + + 1 // '\0' + + 1 // '_' + + 10 // max int length + + 1 // '_' + + 10 // max int length + + 4 // ".png" + ); + sprintf(path, "%s/%d_%d.png", image->directory, i, j); + if (icylib_file_exists(path)) { + icylib_lazy_chunked_load_chunk(image, i, j); + } + else { + image->chunk_rows[j].chunk_columns[i] = icylib_regular_create_from_size(ICYLIB_LAZY_CHUNK_WIDTH, ICYLIB_LAZY_CHUNK_HEIGHT, image->channels); + } + ICYLIB_FREE(path); + } + } + else { + if (image->chunk_rows[j].chunk_columns[i] != NULL) { + icylib_lazy_chunked_unload_chunk(image, i, j); + } + } + } + } +} + +icylib_Color icylib_lazy_chunked_get_pixel(icylib_LazyChunkedImage* image, int x, int y) { + return icylib_regular_get_pixel(image->chunk_rows[y / ICYLIB_LAZY_CHUNK_HEIGHT].chunk_columns[x / ICYLIB_LAZY_CHUNK_WIDTH], x % ICYLIB_LAZY_CHUNK_WIDTH, y % ICYLIB_LAZY_CHUNK_HEIGHT); +} + +void icylib_lazy_chunked_set_pixel(icylib_LazyChunkedImage* image, int x, int y, icylib_Color color) { + icylib_regular_set_pixel(image->chunk_rows[y / ICYLIB_LAZY_CHUNK_HEIGHT].chunk_columns[x / ICYLIB_LAZY_CHUNK_WIDTH], x % ICYLIB_LAZY_CHUNK_WIDTH, y % ICYLIB_LAZY_CHUNK_HEIGHT, color); +} + +void icylib_lazy_chunked_set_pixel_blend(icylib_LazyChunkedImage* image, int x, int y, icylib_Color color) { + icylib_regular_set_pixel_blend(image->chunk_rows[y / ICYLIB_LAZY_CHUNK_HEIGHT].chunk_columns[x / ICYLIB_LAZY_CHUNK_WIDTH], x % ICYLIB_LAZY_CHUNK_WIDTH, y % ICYLIB_LAZY_CHUNK_HEIGHT, color); +} + +void icylib_lazy_chunked_fill(icylib_LazyChunkedImage* image, icylib_Color color, unsigned char blend) { + for (int j = 0; j < icylib_floor((float)image->height / ICYLIB_LAZY_CHUNK_HEIGHT); j++) { + for (int i = 0; i < icylib_floor((float)image->width / ICYLIB_LAZY_CHUNK_WIDTH); i++) { + icylib_regular_fill(image->chunk_rows[j].chunk_columns[i], color, blend); + } + } + if (image->width % ICYLIB_LAZY_CHUNK_WIDTH != 0) { + for (int j = 0; j < icylib_floor((float)image->height / ICYLIB_LAZY_CHUNK_HEIGHT); j++) { + icylib_regular_fill_rectangle(image->chunk_rows[j].chunk_columns[icylib_floor((float)image->width / ICYLIB_LAZY_CHUNK_WIDTH)], 0, 0, image->width % ICYLIB_LAZY_CHUNK_WIDTH, ICYLIB_LAZY_CHUNK_HEIGHT, color, blend); + } + } + if (image->height % ICYLIB_LAZY_CHUNK_HEIGHT != 0) { + for (int i = 0; i < icylib_floor((float)image->width / ICYLIB_LAZY_CHUNK_WIDTH); i++) { + icylib_regular_fill_rectangle(image->chunk_rows[icylib_floor((float)image->height / ICYLIB_LAZY_CHUNK_HEIGHT)].chunk_columns[i], 0, 0, ICYLIB_LAZY_CHUNK_WIDTH, image->height % ICYLIB_LAZY_CHUNK_HEIGHT, color, blend); + } + } +} + +void icylib_lazy_chunked_draw_rectangle(icylib_LazyChunkedImage* image, int x1, int y1, int x2, int y2, icylib_Color color, unsigned char blend) { + icylib_lazy_chunked_draw_line(image, x1, y1, x2, y1, color, blend, 0); + icylib_lazy_chunked_draw_line(image, x2, y1, x2, y2, color, blend, 0); + icylib_lazy_chunked_draw_line(image, x2, y2, x1, y2, color, blend, 0); + icylib_lazy_chunked_draw_line(image, x1, y2, x1, y1, color, blend, 0); +} + +void icylib_lazy_chunked_fill_rectangle(icylib_LazyChunkedImage* image, int x1, int y1, int x2, int y2, icylib_Color color, unsigned char blend) { + for (int j = icylib_imax(0, y1); j < icylib_imin(image->height, y2); j++) { + for (int i = icylib_imax(0, x1); i < icylib_imin(image->width, x2); i++) { + if (blend) { + icylib_lazy_chunked_set_pixel_blend(image, i, j, color); + } + else { + icylib_lazy_chunked_set_pixel(image, i, j, color); + } + } + } +} + +void icylib_lazy_chunked_draw_image(icylib_LazyChunkedImage* image, int x, int y, icylib_RegularImage* other, unsigned char blend) { + if (blend) { + for (int j = 0; j < other->height; j++) { + for (int i = 0; i < other->width; i++) { + icylib_lazy_chunked_set_pixel_blend(image, x + i, y + j, icylib_color_from_rgba(other->data[(j * other->width + i) * other->channels], other->data[(j * other->width + i) * other->channels + 1], other->data[(j * other->width + i) * other->channels + 2], other->channels == 4 ? other->data[(j * other->width + i) * other->channels + 3] : 255)); + } + } + } + else { + for (int j = 0; j < other->height; j++) { + for (int i = 0; i < other->width; i++) { + icylib_lazy_chunked_set_pixel(image, x + i, y + j, icylib_color_from_rgba(other->data[(j * other->width + i) * other->channels], other->data[(j * other->width + i) * other->channels + 1], other->data[(j * other->width + i) * other->channels + 2], other->channels == 4 ? other->data[(j * other->width + i) * other->channels + 3] : 255)); + } + } + } +} + +void icylib_lazy_chunked_draw_circle(icylib_LazyChunkedImage* image, int x, int y, int radius, icylib_Color color, unsigned char blend) { + icylib_draw_bresenham_circle((unsigned char*)image, x, y, radius, color, (void (*)(unsigned char*, int, int, icylib_Color))(blend ? icylib_lazy_chunked_set_pixel_blend : icylib_lazy_chunked_set_pixel)); +} + +void icylib_lazy_chunked_fill_circle(icylib_LazyChunkedImage* image, int x, int y, int radius, icylib_Color color, unsigned char blend) { + int rsquared = radius * radius; + for (int j = -radius; j < radius; j++) { + for (int i = -radius; i < radius; i++) { + if ((i * i + j * j) < rsquared) { + if (blend) { + icylib_lazy_chunked_set_pixel_blend(image, x + i, y + j, color); + } + else { + icylib_lazy_chunked_set_pixel(image, x + i, y + j, color); + } + } + } + } +} + +void icylib_lazy_chunked_draw_line_with_thickness(icylib_LazyChunkedImage* image, int x1, int y1, int x2, int y2, int thickness, icylib_Color color, unsigned char blend, unsigned char antialias) { + if (antialias) { + icylib_draw_thick_xiaolin_wu_aa_line((unsigned char*)image, x1, y1, x2, y2, thickness, color, (void (*)(unsigned char*, int, int, icylib_Color))(blend ? icylib_lazy_chunked_set_pixel_blend : icylib_lazy_chunked_set_pixel)); + } + else { + icylib_draw_bresenham_thick_line((unsigned char*)image, x1, y1, x2, y2, thickness, color, (void (*)(unsigned char*, int, int, icylib_Color))(blend ? icylib_lazy_chunked_set_pixel_blend : icylib_lazy_chunked_set_pixel)); + } +} + +void icylib_lazy_chunked_draw_line(icylib_LazyChunkedImage* image, int x1, int y1, int x2, int y2, icylib_Color color, unsigned char blend, unsigned char antialias) { + icylib_lazy_chunked_draw_line_with_thickness(image, x1, y1, x2, y2, 1, color, blend, antialias); +} + +void icylib_lazy_chunked_draw_text(icylib_LazyChunkedImage* image, char* text, int x, int y, icylib_Color color, char* fontPath, int pixelSize, icylib_HorizontalAlignment horizontalAlignment, icylib_VerticalAlignment verticalAlignment, unsigned char blend) { + double width, height; + icylib_measure_text_size(text, fontPath, pixelSize, &width, &height); + + stbtt_fontinfo font; + icylib_FontCache* fonts = icylib_get_font_cache(); + + char found = 0; + for (int i = 0; i < fonts->length; ++i) { + if (strcmp(fonts->data[i].name, fontPath) == 0) { + font = *fonts->data[i].font; + found = 1; + break; + } + } + if (!found) { + unsigned char* font_file; + icylib_read_bytes(fontPath, &font_file); + + int offset = stbtt_GetFontOffsetForIndex(font_file, 0); + stbtt_InitFont(&font, font_file, offset); + + fonts->data = ICYLIB_REALLOC(fonts->data, sizeof(icylib_FontCacheEntry) * (fonts->length + 1)); + fonts->data[fonts->length].name = fontPath; + fonts->data[fonts->length].font = ICYLIB_MALLOC(sizeof(stbtt_fontinfo)); + memcpy(fonts->data[fonts->length].font, &font, sizeof(stbtt_fontinfo)); + fonts->length++; + } + + float scale = stbtt_ScaleForPixelHeight(&font, pixelSize); + + int ascent, zero; + stbtt_GetFontVMetrics(&font, &ascent, &zero, &zero); + + int advance, lsb, x0, y0, x1, y1; + + float x_cursor = x; + if (horizontalAlignment == ICYLIB_HORIZONTAL_ALIGNMENT_CENTER) { + x_cursor -= width / 2; + } + else if (horizontalAlignment == ICYLIB_HORIZONTAL_ALIGNMENT_RIGHT) { + x_cursor -= width; + } + float y_cursor = y; + if (verticalAlignment == ICYLIB_VERTICAL_ALIGNMENT_CENTER) { + y_cursor -= height / 2; + } + else if (verticalAlignment == ICYLIB_VERTICAL_ALIGNMENT_TOP) { + y_cursor -= height; + } + for (size_t i = 0; i < strlen(text); ++i) { + if (text[i] == '\n') { + y_cursor += ascent * scale; + x_cursor = x; + continue; + } + + int c = (int)text[i]; + + stbtt_GetCodepointHMetrics(&font, c, &advance, &lsb); + stbtt_GetCodepointBitmapBox(&font, c, scale, scale, &x0, &y0, &x1, &y1); + + int w = 0; + int h = 0; + unsigned char* bitmap = stbtt_GetCodepointBitmap(&font, 0, scale, c, &w, &h, 0, 0); + for (int b = 0; b < h; ++b) { + for (int a = 0; a < w; ++a) { + if (bitmap[b * w + a] != 0) { + int pixel_x = (int)x_cursor + x0 + a; + int pixel_y = (int)y_cursor + y0 + b; + if (pixel_x < 0 || pixel_x >= image->width || pixel_y < 0 || pixel_y >= image->height) { + continue; + } + if (blend) { + icylib_lazy_chunked_set_pixel_blend(image, pixel_x, pixel_y, icylib_color_from_rgba(color.r, color.g, color.b, (unsigned char)(bitmap[b * w + a]))); + } + else { + icylib_lazy_chunked_set_pixel(image, pixel_x, pixel_y, icylib_color_from_rgba(color.r, color.g, color.b, (unsigned char)(bitmap[b * w + a]))); + } + } + } + } + + if (i < strlen(text) - 1) { + x_cursor += advance * scale; + x_cursor += scale * stbtt_GetCodepointKernAdvance(&font, c, (int)text[i + 1]); + } + } +} + +void icylib_lazy_chunked_replace_color(icylib_LazyChunkedImage* image, icylib_Color old_color, icylib_Color new_color, unsigned char blend) { + for (int j = 0; j < (image->height / ICYLIB_LAZY_CHUNK_HEIGHT); j++) { + for (int i = 0; i < (image->width / ICYLIB_LAZY_CHUNK_WIDTH); i++) { + if (image->chunk_rows[j].chunk_columns[i] != NULL) { + icylib_regular_replace_color(image->chunk_rows[j].chunk_columns[i], old_color, new_color, blend); + } + } + } +} + +void icylib_lazy_chunked_replace_color_ignore_alpha(icylib_LazyChunkedImage* image, icylib_Color old_color, icylib_Color new_color) { + for (int j = 0; j < (image->height / ICYLIB_LAZY_CHUNK_HEIGHT); j++) { + for (int i = 0; i < (image->width / ICYLIB_LAZY_CHUNK_WIDTH); i++) { + if (image->chunk_rows[j].chunk_columns[i] != NULL) { + icylib_regular_replace_color_ignore_alpha(image->chunk_rows[j].chunk_columns[i], old_color, new_color); + } + } + } +} + +void icylib_lazy_chunked_blur(icylib_LazyChunkedImage* image, int radius) { + for (int j = 0; j < (image->height / ICYLIB_LAZY_CHUNK_HEIGHT); j++) { + for (int i = 0; i < (image->width / ICYLIB_LAZY_CHUNK_WIDTH); i++) { + if (image->chunk_rows[j].chunk_columns[i] != NULL) { + icylib_regular_blur(image->chunk_rows[j].chunk_columns[i], radius); + } + } + } +} + +icylib_RegularImage* icylib_lazy_chunked_get_sub_image(icylib_LazyChunkedImage* image, int x, int y, int width, int height) { + unsigned int start_chunk_x = x / ICYLIB_LAZY_CHUNK_WIDTH; + unsigned int start_chunk_y = y / ICYLIB_LAZY_CHUNK_HEIGHT; + unsigned int end_chunk_x = (x + width - 1) / ICYLIB_LAZY_CHUNK_WIDTH; + unsigned int end_chunk_y = (y + height - 1) / ICYLIB_LAZY_CHUNK_HEIGHT; + icylib_RegularImage* img = icylib_regular_create_from_size(icylib_ceil(((float)(width + x % ICYLIB_LAZY_CHUNK_WIDTH)) / ICYLIB_LAZY_CHUNK_WIDTH) * ICYLIB_LAZY_CHUNK_WIDTH, icylib_ceil(((float)(height + y % ICYLIB_LAZY_CHUNK_HEIGHT)) / ICYLIB_LAZY_CHUNK_HEIGHT) * ICYLIB_LAZY_CHUNK_HEIGHT, image->channels); + int j2 = 0; + for (int j = start_chunk_y; j <= end_chunk_y; j++) { + int i2 = 0; + for (int i = start_chunk_x; i <= end_chunk_x; i++) { + icylib_regular_draw_image(img, i2 * ICYLIB_LAZY_CHUNK_WIDTH, j2 * ICYLIB_LAZY_CHUNK_HEIGHT, image->chunk_rows[j].chunk_columns[i], 0); + i2++; + } + j2++; + } + return icylib_regular_get_sub_image(img, x % ICYLIB_LAZY_CHUNK_WIDTH, y % ICYLIB_LAZY_CHUNK_HEIGHT, width, height); +} + +icylib_LazyChunkedImage* icylib_lazy_chunked_copy(icylib_LazyChunkedImage* image) { + icylib_LazyChunkedImage* new_image = ICYLIB_MALLOC(sizeof(icylib_LazyChunkedImage)); + new_image->width = image->width; + new_image->height = image->height; + new_image->channels = image->channels; + new_image->chunk_rows = ICYLIB_MALLOC(sizeof(icylib_LazyChunkRow*) * (image->height / ICYLIB_LAZY_CHUNK_HEIGHT)); + new_image->rows_length = image->rows_length; + new_image->columns_length = image->columns_length; + for (int j = 0; j < icylib_ceil((float)image->height / ICYLIB_LAZY_CHUNK_HEIGHT); j++) { + icylib_LazyChunkRow row; + row.chunk_columns = ICYLIB_MALLOC(sizeof(icylib_RegularImage*) * (image->width / ICYLIB_LAZY_CHUNK_WIDTH)); + for (int i = 0; i < (image->width / ICYLIB_LAZY_CHUNK_WIDTH); i++) { + row.chunk_columns[i] = icylib_regular_copy(image->chunk_rows[j].chunk_columns[i]); + } + new_image->chunk_rows[j] = row; + } + return new_image; +} + +void icylib_lazy_chunked_extend_to(icylib_LazyChunkedImage* image, int new_width, int new_height) { + if (new_width > image->rows_length * ICYLIB_LAZY_CHUNK_WIDTH || new_height > image->columns_length * ICYLIB_LAZY_CHUNK_HEIGHT) { + int new_rows_length = icylib_ceil((float)new_height / ICYLIB_LAZY_CHUNK_HEIGHT); + int new_columns_length = icylib_ceil((float)new_width / ICYLIB_LAZY_CHUNK_WIDTH); + icylib_LazyChunkRow* new_chunk_rows = ICYLIB_MALLOC(sizeof(icylib_LazyChunkRow) * new_rows_length); + for (int j = 0; j < new_rows_length; j++) { + icylib_LazyChunkRow row; + icylib_RegularImage** chunk_columns = ICYLIB_MALLOC(sizeof(icylib_RegularImage*) * new_columns_length); + for (int i = 0; i < new_columns_length; i++) { + if (j < image->rows_length && i < image->columns_length) { + chunk_columns[i] = image->chunk_rows[j].chunk_columns[i]; + } + else { + chunk_columns[i] = icylib_regular_create_from_size(ICYLIB_LAZY_CHUNK_WIDTH, ICYLIB_LAZY_CHUNK_HEIGHT, image->channels); + } + } + row.chunk_columns = chunk_columns; + new_chunk_rows[j] = row; + } + ICYLIB_FREE(image->chunk_rows); + image->chunk_rows = new_chunk_rows; + image->rows_length = new_rows_length; + image->columns_length = new_columns_length; + } + image->width = new_width; + image->height = new_height; +} + +void icylib_lazy_chunked_resize(icylib_LazyChunkedImage* image, int width, int height) { + icylib_lazy_chunked_extend_to(image, width, height); + // scale the invididual chunks and split them up into multiple chunks + icylib_LazyChunkRow* new_chunk_rows = ICYLIB_MALLOC(sizeof(icylib_LazyChunkRow) * icylib_ceil((float)height / ICYLIB_LAZY_CHUNK_HEIGHT)); + for (int j = 0; j < icylib_ceil((float)height / ICYLIB_LAZY_CHUNK_HEIGHT); j++) { + icylib_LazyChunkRow row; + icylib_RegularImage** chunk_columns = ICYLIB_MALLOC(sizeof(icylib_RegularImage*) * icylib_ceil((float)width / ICYLIB_LAZY_CHUNK_WIDTH)); + for (int i = 0; i < icylib_ceil((float)width / ICYLIB_LAZY_CHUNK_WIDTH); i++) { + icylib_RegularImage* sub = icylib_lazy_chunked_get_sub_image(image, i * ICYLIB_LAZY_CHUNK_WIDTH, j * ICYLIB_LAZY_CHUNK_HEIGHT, ICYLIB_LAZY_CHUNK_WIDTH, ICYLIB_LAZY_CHUNK_HEIGHT); + icylib_regular_resize(sub, ICYLIB_LAZY_CHUNK_WIDTH, ICYLIB_LAZY_CHUNK_HEIGHT); + chunk_columns[i] = sub; + } + row.chunk_columns = chunk_columns; + new_chunk_rows[j] = row; + } + ICYLIB_FREE(image->chunk_rows); + image->chunk_rows = new_chunk_rows; + image->rows_length = icylib_ceil((float)height / ICYLIB_LAZY_CHUNK_HEIGHT); + image->columns_length = icylib_ceil((float)width / ICYLIB_LAZY_CHUNK_WIDTH); + image->width = width; + image->height = height; +} + +void icylib_lazy_chunked_resize_scale(icylib_LazyChunkedImage* image, float scale) { + icylib_lazy_chunked_resize(image, image->width * scale, image->height * scale); +} + +#endif + +#endif \ No newline at end of file diff --git a/thirdparty/icylib/math_utils.h b/thirdparty/icylib/math_utils.h new file mode 100644 index 0000000..fa99a98 --- /dev/null +++ b/thirdparty/icylib/math_utils.h @@ -0,0 +1,34 @@ +#ifndef ICYLIB_MATH_UTILS_H +#define ICYLIB_MATH_UTILS_H + +int icylib_imin(int a, int b); + +int icylib_imax(int a, int b); + +int icylib_floor(double x); + +int icylib_ceil(double x); + +#ifdef ICYLIB_IMPLEMENTATION + +#include "icylib.h" + +int icylib_imin(int a, int b) { + return a < b ? a : b; +} + +int icylib_imax(int a, int b) { + return a > b ? a : b; +} + +int icylib_floor(double x) { + return (int)x; +} + +int icylib_ceil(double x) { + return (int)x + 1; +} + +#endif + +#endif \ No newline at end of file diff --git a/thirdparty/icylib/primitive_chunked_image.h b/thirdparty/icylib/primitive_chunked_image.h new file mode 100644 index 0000000..e36cf28 --- /dev/null +++ b/thirdparty/icylib/primitive_chunked_image.h @@ -0,0 +1,557 @@ +#ifndef ICYLIB_PRIMITIVE_CHUNKED_IMAGE_H +#define ICYLIB_PRIMITIVE_CHUNKED_IMAGE_H + +#include + +#include "thirdparty/stb_image_write.h" + +#include "icylib.h" +#include "regular_image.h" +#include "color.h" +#include "thick_xiaolin_wu.h" +#include "text.h" +#include "math_utils.h" + +const unsigned int ICYLIB_PRIMITIVE_CHUNK_WIDTH = 256; +const unsigned int ICYLIB_PRIMITIVE_CHUNK_HEIGHT = 256; + +typedef struct icylib_PrimitiveChunkRow { + icylib_RegularImage** chunk_columns; +} icylib_PrimitiveChunkRow; + +typedef struct icylib_PrimitiveChunkedImage { + int width; + int height; + int channels; + icylib_PrimitiveChunkRow* chunk_rows; + int rows_length; + int columns_length; +} icylib_PrimitiveChunkedImage; + +icylib_PrimitiveChunkedImage* icylib_primitive_chunked_create_from_size(int width, int height, int channels); + +icylib_PrimitiveChunkedImage* icylib_primitive_chunked_create_from_memory(unsigned char* data, int width, int height, int channels); + +icylib_PrimitiveChunkedImage* icylib_primitive_chunked_load_from_file_data(unsigned char* data, int length); + +icylib_PrimitiveChunkedImage* icylib_primitive_chunked_load_from_file(const char* filename); + +unsigned char* icylib_primitive_chunked_save_to_file_data(icylib_PrimitiveChunkedImage* image, icylib_RegularImageFormat format, int* length); + +int icylib_primitive_chunked_save_to_file(icylib_PrimitiveChunkedImage* image, const char* filename); + +void icylib_primitive_chunked_free(icylib_PrimitiveChunkedImage* image); + +icylib_Color icylib_primitive_chunked_get_pixel(icylib_PrimitiveChunkedImage* image, int x, int y); + +void icylib_primitive_chunked_set_pixel(icylib_PrimitiveChunkedImage* image, int x, int y, icylib_Color color); + +void icylib_primitive_chunked_set_pixel_blend(icylib_PrimitiveChunkedImage* image, int x, int y, icylib_Color color); + +void icylib_primitive_chunked_fill(icylib_PrimitiveChunkedImage* image, icylib_Color color, unsigned char blend); + +void icylib_primitive_chunked_draw_rectangle(icylib_PrimitiveChunkedImage* image, int x1, int y1, int x2, int y2, icylib_Color color, unsigned char blend); + +void icylib_primitive_chunked_fill_rectangle(icylib_PrimitiveChunkedImage* image, int x1, int y1, int x2, int y2, icylib_Color color, unsigned char blend); + +void icylib_primitive_chunked_draw_image(icylib_PrimitiveChunkedImage* image, int x, int y, icylib_RegularImage* other, unsigned char blend); + +void icylib_primitive_chunked_draw_circle(icylib_PrimitiveChunkedImage* image, int x, int y, int radius, icylib_Color color, unsigned char blend); + +void icylib_primitive_chunked_fill_circle(icylib_PrimitiveChunkedImage* image, int x, int y, int radius, icylib_Color color, unsigned char blend); + +void icylib_primitive_chunked_draw_line_with_thickness(icylib_PrimitiveChunkedImage* image, int x1, int y1, int x2, int y2, int thickness, icylib_Color color, unsigned char blend, unsigned char antialias); + +void icylib_primitive_chunked_draw_line(icylib_PrimitiveChunkedImage* image, int x1, int y1, int x2, int y2, icylib_Color color, unsigned char blend, unsigned char antialias); + +void icylib_primitive_chunked_draw_text(icylib_PrimitiveChunkedImage* image, char* text, int x, int y, icylib_Color color, char* fontPath, int pixelSize, icylib_HorizontalAlignment horizontalAlignment, icylib_VerticalAlignment verticalAlignment, unsigned char blend); + +void icylib_primitive_chunked_replace_color(icylib_PrimitiveChunkedImage* image, icylib_Color old_color, icylib_Color new_color, unsigned char blend); + +void icylib_primitive_chunked_replace_color_ignore_alpha(icylib_PrimitiveChunkedImage* image, icylib_Color old_color, icylib_Color new_color); + +void icylib_primitive_chunked_blur(icylib_PrimitiveChunkedImage* image, int radius); + +icylib_RegularImage* icylib_primitive_chunked_get_sub_image(icylib_PrimitiveChunkedImage* image, int x, int y, int width, int height); + +icylib_PrimitiveChunkedImage* icylib_primitive_chunked_copy(icylib_PrimitiveChunkedImage* image); + +void icylib_primitive_chunked_extend_to(icylib_PrimitiveChunkedImage* image, int new_width, int new_height); + +void icylib_primitive_chunked_resize(icylib_PrimitiveChunkedImage* image, int width, int height); + +void icylib_primitive_chunked_resize_scale(icylib_PrimitiveChunkedImage* image, float scale); + +#ifdef ICYLIB_IMPLEMENTATION + +icylib_PrimitiveChunkedImage* icylib_primitive_chunked_create_from_size(int width, int height, int channels) { + icylib_PrimitiveChunkRow* chunk_rows = ICYLIB_MALLOC(sizeof(icylib_PrimitiveChunkRow) * icylib_ceil((float)height / ICYLIB_PRIMITIVE_CHUNK_HEIGHT)); + for (int j = 0; j < icylib_ceil((float)height / ICYLIB_PRIMITIVE_CHUNK_HEIGHT); j++) { + icylib_PrimitiveChunkRow row; + icylib_RegularImage** chunk_columns = ICYLIB_MALLOC(sizeof(icylib_RegularImage*) * icylib_ceil((float)width / ICYLIB_PRIMITIVE_CHUNK_WIDTH)); + for (int i = 0; i < icylib_ceil((float)width / ICYLIB_PRIMITIVE_CHUNK_WIDTH); i++) { + chunk_columns[i] = icylib_regular_create_from_size(ICYLIB_PRIMITIVE_CHUNK_WIDTH, ICYLIB_PRIMITIVE_CHUNK_HEIGHT, channels); + } + row.chunk_columns = chunk_columns; + chunk_rows[j] = row; + } + icylib_PrimitiveChunkedImage* image = ICYLIB_MALLOC(sizeof(icylib_PrimitiveChunkedImage)); + image->width = width; + image->height = height; + image->channels = channels; + image->chunk_rows = chunk_rows; + image->rows_length = height / ICYLIB_PRIMITIVE_CHUNK_HEIGHT; + image->columns_length = width / ICYLIB_PRIMITIVE_CHUNK_WIDTH; + return image; +} + +icylib_PrimitiveChunkedImage* icylib_primitive_chunked_load_from_file_data(unsigned char* data, int length) { + int width = (int)data[0] | (int)data[1] << 8 | (int)data[2] << 16 | (int)data[3] << 24; + int height = (int)data[4] | (int)data[5] << 8 | (int)data[6] << 16 | (int)data[7] << 24; + int rows = (int)data[8] | (int)data[9] << 8 | (int)data[10] << 16 | (int)data[11] << 24; + int columns = (int)data[12] | (int)data[13] << 8 | (int)data[14] << 16 | (int)data[15] << 24; + unsigned int data_pointer = 16; + icylib_PrimitiveChunkRow* chunk_rows = ICYLIB_MALLOC(sizeof(icylib_PrimitiveChunkRow) * rows); + for (int j = 0; j < rows; j++) { + icylib_PrimitiveChunkRow row; + icylib_RegularImage** chunk_columns = ICYLIB_MALLOC(sizeof(icylib_RegularImage*) * columns); + for (int i = 0; i < columns; i++) { + int chunk_data_length = (int)data[data_pointer] | (int)data[data_pointer + 1] << 8 | (int)data[data_pointer + 2] << 16 | (int)data[data_pointer + 3] << 24; + data_pointer += 4; + unsigned char* chunk_data = ICYLIB_MALLOC_ATOMIC(chunk_data_length); + memcpy(chunk_data, data + data_pointer, chunk_data_length); + data_pointer += chunk_data_length; + chunk_columns[i] = icylib_regular_load_from_file_data(chunk_data, chunk_data_length); + } + row.chunk_columns = chunk_columns; + chunk_rows[j] = row; + } + icylib_PrimitiveChunkedImage* image = ICYLIB_MALLOC(sizeof(icylib_PrimitiveChunkedImage)); + image->width = width; + image->height = height; + image->channels = 4; + image->chunk_rows = chunk_rows; + image->rows_length = rows; + image->columns_length = columns; + return image; +} + +icylib_PrimitiveChunkedImage* icylib_primitive_chunked_load_from_file(const char* filename) { + FILE* file = fopen(filename, "rb"); + if (!file) { + ICYLIB_ERROR("Failed to open file"); + return NULL; + } + fseek(file, 0, SEEK_END); + long length = ftell(file); + fseek(file, 0, SEEK_SET); + unsigned char* data = ICYLIB_MALLOC_ATOMIC(length); + fread(data, 1, length, file); + fclose(file); + + icylib_PrimitiveChunkedImage* image = icylib_primitive_chunked_load_from_file_data(data, length); + ICYLIB_FREE(data); + return image; +} + +unsigned char* icylib_primitive_chunked_save_to_file_data(icylib_PrimitiveChunkedImage* image, icylib_RegularImageFormat format, int* length) { + unsigned char* data = ICYLIB_MALLOC_ATOMIC(16); + unsigned int data_length = 16; + + data[0] = (image->width >> 0) & 0xFF; + data[1] = (image->width >> 8) & 0xFF; + data[2] = (image->width >> 16) & 0xFF; + data[3] = (image->width >> 24) & 0xFF; + + data[4] = (image->height >> 0) & 0xFF; + data[5] = (image->height >> 8) & 0xFF; + data[6] = (image->height >> 16) & 0xFF; + data[7] = (image->height >> 24) & 0xFF; + + data[8] = (image->rows_length >> 0) & 0xFF; + data[9] = (image->rows_length >> 8) & 0xFF; + data[10] = (image->rows_length >> 16) & 0xFF; + data[11] = (image->rows_length >> 24) & 0xFF; + + data[12] = (image->columns_length >> 0) & 0xFF; + data[13] = (image->columns_length >> 8) & 0xFF; + data[14] = (image->columns_length >> 16) & 0xFF; + data[15] = (image->columns_length >> 24) & 0xFF; + + for (int j = 0; j < image->rows_length; j++) { + for (int i = 0; i < image->columns_length; i++) { + int chunk_data_length = 0; + unsigned char* chunk_data = icylib_regular_save_to_file_data(image->chunk_rows[j].chunk_columns[i], format, &chunk_data_length); + data = ICYLIB_REALLOC(data, data_length + chunk_data_length + 4); + data[data_length] = (chunk_data_length >> 0) & 0xFF; + data[data_length + 1] = (chunk_data_length >> 8) & 0xFF; + data[data_length + 2] = (chunk_data_length >> 16) & 0xFF; + data[data_length + 3] = (chunk_data_length >> 24) & 0xFF; + memcpy(data + data_length + 4, chunk_data, chunk_data_length); + data_length += chunk_data_length + 4; + ICYLIB_FREE(chunk_data); + } + } + + *length = data_length; + return data; +} + +int icylib_primitive_chunked_save_to_file(icylib_PrimitiveChunkedImage* image, const char* filename) { + const char* extension = strrchr(filename, '.'); + icylib_RegularImageFormat format; + if (extension == NULL) { + format = ICYLIB_REGULAR_IMAGE_FORMAT_PNG; + } + else { + if (strcmp(extension, ".png") == 0) { + format = ICYLIB_REGULAR_IMAGE_FORMAT_PNG; + } + else if (strcmp(extension, ".jpg") == 0) { + format = ICYLIB_REGULAR_IMAGE_FORMAT_JPG; + } + else if (strcmp(extension, ".bmp") == 0) { + format = ICYLIB_REGULAR_IMAGE_FORMAT_BMP; + } + else if (strcmp(extension, ".tga") == 0) { + format = ICYLIB_REGULAR_IMAGE_FORMAT_TGA; + } + else if (strcmp(extension, ".hdr") == 0) { + format = ICYLIB_REGULAR_IMAGE_FORMAT_HDR; + } + else { + format = ICYLIB_REGULAR_IMAGE_FORMAT_PNG; + } + } + + FILE* file = fopen(filename, "wb"); + if (!file) { + ICYLIB_ERROR("Failed to open file"); + return 0; + } + int length; + unsigned char* data = icylib_primitive_chunked_save_to_file_data(image, format, &length); + fwrite(data, 1, length, file); + fclose(file); + ICYLIB_FREE(data); + return 1; +} + +void icylib_primitive_chunked_free(icylib_PrimitiveChunkedImage* image) { + for (int j = 0; j < (image->height / ICYLIB_PRIMITIVE_CHUNK_HEIGHT); j++) { + for (int i = 0; i < (image->width / ICYLIB_PRIMITIVE_CHUNK_WIDTH); i++) { + icylib_regular_free(image->chunk_rows[j].chunk_columns[i]); + } + } + ICYLIB_FREE(image->chunk_rows); + ICYLIB_FREE(image); +} + +icylib_Color icylib_primitive_chunked_get_pixel(icylib_PrimitiveChunkedImage* image, int x, int y) { + return icylib_regular_get_pixel(image->chunk_rows[y / ICYLIB_PRIMITIVE_CHUNK_HEIGHT].chunk_columns[x / ICYLIB_PRIMITIVE_CHUNK_WIDTH], x % ICYLIB_PRIMITIVE_CHUNK_WIDTH, y % ICYLIB_PRIMITIVE_CHUNK_HEIGHT); +} + +void icylib_primitive_chunked_set_pixel(icylib_PrimitiveChunkedImage* image, int x, int y, icylib_Color color) { + icylib_regular_set_pixel(image->chunk_rows[y / ICYLIB_PRIMITIVE_CHUNK_HEIGHT].chunk_columns[x / ICYLIB_PRIMITIVE_CHUNK_WIDTH], x % ICYLIB_PRIMITIVE_CHUNK_WIDTH, y % ICYLIB_PRIMITIVE_CHUNK_HEIGHT, color); +} + +void icylib_primitive_chunked_set_pixel_blend(icylib_PrimitiveChunkedImage* image, int x, int y, icylib_Color color) { + icylib_regular_set_pixel_blend(image->chunk_rows[y / ICYLIB_PRIMITIVE_CHUNK_HEIGHT].chunk_columns[x / ICYLIB_PRIMITIVE_CHUNK_WIDTH], x % ICYLIB_PRIMITIVE_CHUNK_WIDTH, y % ICYLIB_PRIMITIVE_CHUNK_HEIGHT, color); +} + +void icylib_primitive_chunked_fill(icylib_PrimitiveChunkedImage* image, icylib_Color color, unsigned char blend) { + for (int j = 0; j < icylib_floor((float)image->height / ICYLIB_PRIMITIVE_CHUNK_HEIGHT); j++) { + for (int i = 0; i < icylib_floor((float)image->width / ICYLIB_PRIMITIVE_CHUNK_WIDTH); i++) { + icylib_regular_fill(image->chunk_rows[j].chunk_columns[i], color, blend); + } + } + if (image->width % ICYLIB_PRIMITIVE_CHUNK_WIDTH != 0) { + for (int j = 0; j < icylib_floor((float)image->height / ICYLIB_PRIMITIVE_CHUNK_HEIGHT); j++) { + icylib_regular_fill_rectangle(image->chunk_rows[j].chunk_columns[icylib_floor((float)image->width / ICYLIB_PRIMITIVE_CHUNK_WIDTH)], 0, 0, image->width % ICYLIB_PRIMITIVE_CHUNK_WIDTH, ICYLIB_PRIMITIVE_CHUNK_HEIGHT, color, blend); + } + } + if (image->height % ICYLIB_PRIMITIVE_CHUNK_HEIGHT != 0) { + for (int i = 0; i < icylib_floor((float)image->width / ICYLIB_PRIMITIVE_CHUNK_WIDTH); i++) { + icylib_regular_fill_rectangle(image->chunk_rows[icylib_floor((float)image->height / ICYLIB_PRIMITIVE_CHUNK_HEIGHT)].chunk_columns[i], 0, 0, ICYLIB_PRIMITIVE_CHUNK_WIDTH, image->height % ICYLIB_PRIMITIVE_CHUNK_HEIGHT, color, blend); + } + } +} + +void icylib_primitive_chunked_draw_rectangle(icylib_PrimitiveChunkedImage* image, int x1, int y1, int x2, int y2, icylib_Color color, unsigned char blend) { + icylib_primitive_chunked_draw_line(image, x1, y1, x2, y1, color, blend, 0); + icylib_primitive_chunked_draw_line(image, x1, y1, x1, y2, color, blend, 0); + icylib_primitive_chunked_draw_line(image, x2, y1, x2, y2, color, blend, 0); + icylib_primitive_chunked_draw_line(image, x1, y2, x2, y2, color, blend, 0); +} + +void icylib_primitive_chunked_fill_rectangle(icylib_PrimitiveChunkedImage* image, int x1, int y1, int x2, int y2, icylib_Color color, unsigned char blend) { + for (int j = icylib_imax(0, y1); j < icylib_imin(image->height, y2); j++) { + for (int i = icylib_imax(0, x1); i < icylib_imin(image->width, x2); i++) { + if (blend) { + icylib_primitive_chunked_set_pixel_blend(image, i, j, color); + } + else { + icylib_primitive_chunked_set_pixel(image, i, j, color); + } + } + } +} + +void icylib_primitive_chunked_draw_image(icylib_PrimitiveChunkedImage* image, int x, int y, icylib_RegularImage* other, unsigned char blend) { + if (blend) { + for (int j = 0; j < other->height; j++) { + for (int i = 0; i < other->width; i++) { + icylib_primitive_chunked_set_pixel_blend(image, x + i, y + j, icylib_color_from_rgba(other->data[(j * other->width + i) * other->channels], other->data[(j * other->width + i) * other->channels + 1], other->data[(j * other->width + i) * other->channels + 2], other->channels == 4 ? other->data[(j * other->width + i) * other->channels + 3] : 255)); + } + } + } + else { + for (int j = 0; j < other->height; j++) { + for (int i = 0; i < other->width; i++) { + icylib_primitive_chunked_set_pixel(image, x + i, y + j, icylib_color_from_rgba(other->data[(j * other->width + i) * other->channels], other->data[(j * other->width + i) * other->channels + 1], other->data[(j * other->width + i) * other->channels + 2], other->channels == 4 ? other->data[(j * other->width + i) * other->channels + 3] : 255)); + } + } + } +} + +void icylib_primitive_chunked_draw_circle(icylib_PrimitiveChunkedImage* image, int x, int y, int radius, icylib_Color color, unsigned char blend) { + icylib_draw_bresenham_circle((unsigned char*)image, x, y, radius, color, (void (*)(unsigned char*, int, int, icylib_Color))(blend ? icylib_primitive_chunked_set_pixel_blend : icylib_primitive_chunked_set_pixel)); +} + +void icylib_primitive_chunked_fill_circle(icylib_PrimitiveChunkedImage* image, int x, int y, int radius, icylib_Color color, unsigned char blend) { + int rsquared = radius * radius; + for (int j = -radius; j < radius; j++) { + for (int i = -radius; i < radius; i++) { + if ((i * i + j * j) < rsquared) { + if (blend) { + icylib_primitive_chunked_set_pixel_blend(image, x + i, y + j, color); + } + else { + icylib_primitive_chunked_set_pixel(image, x + i, y + j, color); + } + } + } + } +} + +void icylib_primitive_chunked_draw_line_with_thickness(icylib_PrimitiveChunkedImage* image, int x1, int y1, int x2, int y2, int thickness, icylib_Color color, unsigned char blend, unsigned char antialias) { + if (antialias) { + icylib_draw_thick_xiaolin_wu_aa_line((unsigned char*)image, x1, y1, x2, y2, thickness, color, (void (*)(unsigned char*, int, int, icylib_Color))(blend ? icylib_primitive_chunked_set_pixel_blend : icylib_primitive_chunked_set_pixel)); + } + else { + icylib_draw_bresenham_thick_line((unsigned char*)image, x1, y1, x2, y2, thickness, color, (void (*)(unsigned char*, int, int, icylib_Color))(blend ? icylib_primitive_chunked_set_pixel_blend : icylib_primitive_chunked_set_pixel)); + } +} + +void icylib_primitive_chunked_draw_line(icylib_PrimitiveChunkedImage* image, int x1, int y1, int x2, int y2, icylib_Color color, unsigned char blend, unsigned char antialias) { + icylib_primitive_chunked_draw_line_with_thickness(image, x1, y1, x2, y2, 1, color, blend, antialias); +} + +void icylib_primitive_chunked_draw_text(icylib_PrimitiveChunkedImage* image, char* text, int x, int y, icylib_Color color, char* fontPath, int pixelSize, icylib_HorizontalAlignment horizontalAlignment, icylib_VerticalAlignment verticalAlignment, unsigned char blend) { + double width, height; + icylib_measure_text_size(text, fontPath, pixelSize, &width, &height); + + stbtt_fontinfo font; + icylib_FontCache* fonts = icylib_get_font_cache(); + + char found = 0; + for (int i = 0; i < fonts->length; ++i) { + if (strcmp(fonts->data[i].name, fontPath) == 0) { + font = *fonts->data[i].font; + found = 1; + break; + } + } + if (!found) { + unsigned char* font_file; + icylib_read_bytes(fontPath, &font_file); + + int offset = stbtt_GetFontOffsetForIndex(font_file, 0); + stbtt_InitFont(&font, font_file, offset); + + fonts->data = ICYLIB_REALLOC(fonts->data, sizeof(icylib_FontCacheEntry) * (fonts->length + 1)); + fonts->data[fonts->length].name = fontPath; + fonts->data[fonts->length].font = ICYLIB_MALLOC(sizeof(stbtt_fontinfo)); + memcpy(fonts->data[fonts->length].font, &font, sizeof(stbtt_fontinfo)); + fonts->length++; + } + + float scale = stbtt_ScaleForPixelHeight(&font, pixelSize); + + int ascent, zero; + stbtt_GetFontVMetrics(&font, &ascent, &zero, &zero); + + int advance, lsb, x0, y0, x1, y1; + + float x_cursor = x; + if (horizontalAlignment == ICYLIB_HORIZONTAL_ALIGNMENT_CENTER) { + x_cursor -= width / 2; + } + else if (horizontalAlignment == ICYLIB_HORIZONTAL_ALIGNMENT_RIGHT) { + x_cursor -= width; + } + float y_cursor = y; + if (verticalAlignment == ICYLIB_VERTICAL_ALIGNMENT_CENTER) { + y_cursor -= height / 2; + } + else if (verticalAlignment == ICYLIB_VERTICAL_ALIGNMENT_TOP) { + y_cursor -= height; + } + for (size_t i = 0; i < strlen(text); ++i) { + if (text[i] == '\n') { + y_cursor += ascent * scale; + x_cursor = x; + continue; + } + + int c = (int)text[i]; + + stbtt_GetCodepointHMetrics(&font, c, &advance, &lsb); + stbtt_GetCodepointBitmapBox(&font, c, scale, scale, &x0, &y0, &x1, &y1); + + int w = 0; + int h = 0; + unsigned char* bitmap = stbtt_GetCodepointBitmap(&font, 0, scale, c, &w, &h, 0, 0); + for (int b = 0; b < h; ++b) { + for (int a = 0; a < w; ++a) { + if (bitmap[b * w + a] != 0) { + int pixel_x = (int)x_cursor + x0 + a; + int pixel_y = (int)y_cursor + y0 + b; + if (pixel_x < 0 || pixel_x >= image->width || pixel_y < 0 || pixel_y >= image->height) { + continue; + } + if (blend) { + icylib_primitive_chunked_set_pixel_blend(image, pixel_x, pixel_y, icylib_color_from_rgba(color.r, color.g, color.b, (unsigned char)(bitmap[b * w + a]))); + } + else { + icylib_primitive_chunked_set_pixel(image, pixel_x, pixel_y, icylib_color_from_rgba(color.r, color.g, color.b, (unsigned char)(bitmap[b * w + a]))); + } + } + } + } + + if (i < strlen(text) - 1) { + x_cursor += advance * scale; + x_cursor += scale * stbtt_GetCodepointKernAdvance(&font, c, (int)text[i + 1]); + } + } +} + +void icylib_primitive_chunked_replace_color(icylib_PrimitiveChunkedImage* image, icylib_Color old_color, icylib_Color new_color, unsigned char blend) { + for (int j = 0; j < (image->height / ICYLIB_PRIMITIVE_CHUNK_HEIGHT); j++) { + for (int i = 0; i < (image->width / ICYLIB_PRIMITIVE_CHUNK_WIDTH); i++) { + icylib_regular_replace_color(image->chunk_rows[j].chunk_columns[i], old_color, new_color, blend); + } + } +} + +void icylib_primitive_chunked_replace_color_ignore_alpha(icylib_PrimitiveChunkedImage* image, icylib_Color old_color, icylib_Color new_color) { + for (int j = 0; j < (image->height / ICYLIB_PRIMITIVE_CHUNK_HEIGHT); j++) { + for (int i = 0; i < (image->width / ICYLIB_PRIMITIVE_CHUNK_WIDTH); i++) { + icylib_regular_replace_color_ignore_alpha(image->chunk_rows[j].chunk_columns[i], old_color, new_color); + } + } +} + +void icylib_primitive_chunked_blur(icylib_PrimitiveChunkedImage* image, int radius) { + for (int j = 0; j < (image->height / ICYLIB_PRIMITIVE_CHUNK_HEIGHT); j++) { + for (int i = 0; i < (image->width / ICYLIB_PRIMITIVE_CHUNK_WIDTH); i++) { + icylib_regular_blur(image->chunk_rows[j].chunk_columns[i], radius); + } + } +} + +icylib_RegularImage* icylib_primitive_chunked_get_sub_image(icylib_PrimitiveChunkedImage* image, int x, int y, int width, int height) { + unsigned int start_chunk_x = x / ICYLIB_PRIMITIVE_CHUNK_WIDTH; + unsigned int start_chunk_y = y / ICYLIB_PRIMITIVE_CHUNK_HEIGHT; + unsigned int end_chunk_x = (x + width - 1) / ICYLIB_PRIMITIVE_CHUNK_WIDTH; + unsigned int end_chunk_y = (y + height - 1) / ICYLIB_PRIMITIVE_CHUNK_HEIGHT; + icylib_RegularImage* img = icylib_regular_create_from_size(icylib_ceil(((float)(width + x % ICYLIB_PRIMITIVE_CHUNK_WIDTH)) / ICYLIB_PRIMITIVE_CHUNK_WIDTH) * ICYLIB_PRIMITIVE_CHUNK_WIDTH, icylib_ceil(((float)(height + y % ICYLIB_PRIMITIVE_CHUNK_HEIGHT)) / ICYLIB_PRIMITIVE_CHUNK_HEIGHT) * ICYLIB_PRIMITIVE_CHUNK_HEIGHT, image->channels); + int j2 = 0; + for (int j = start_chunk_y; j <= end_chunk_y; j++) { + int i2 = 0; + for (int i = start_chunk_x; i <= end_chunk_x; i++) { + icylib_regular_draw_image(img, i2 * ICYLIB_PRIMITIVE_CHUNK_WIDTH, j2 * ICYLIB_PRIMITIVE_CHUNK_HEIGHT, image->chunk_rows[i].chunk_columns[j], 0); + i2++; + } + j2++; + } + return icylib_regular_get_sub_image(img, x % ICYLIB_PRIMITIVE_CHUNK_WIDTH, y % ICYLIB_PRIMITIVE_CHUNK_HEIGHT, width, height); +} + +icylib_PrimitiveChunkedImage* icylib_primitive_chunked_copy(icylib_PrimitiveChunkedImage* image) { + icylib_PrimitiveChunkedImage* new_image = ICYLIB_MALLOC(sizeof(icylib_PrimitiveChunkedImage)); + new_image->width = image->width; + new_image->height = image->height; + new_image->chunk_rows = ICYLIB_MALLOC(sizeof(icylib_PrimitiveChunkRow*) * (image->height / ICYLIB_PRIMITIVE_CHUNK_HEIGHT)); + new_image->rows_length = image->height / ICYLIB_PRIMITIVE_CHUNK_HEIGHT; + new_image->columns_length = image->width / ICYLIB_PRIMITIVE_CHUNK_WIDTH; + for (int j = 0; j < (image->height / ICYLIB_PRIMITIVE_CHUNK_HEIGHT); j++) { + icylib_PrimitiveChunkRow row; + row.chunk_columns = ICYLIB_MALLOC(sizeof(icylib_RegularImage*) * (image->width / ICYLIB_PRIMITIVE_CHUNK_WIDTH)); + for (int i = 0; i < (image->width / ICYLIB_PRIMITIVE_CHUNK_WIDTH); i++) { + row.chunk_columns[i] = icylib_regular_copy(image->chunk_rows[j].chunk_columns[i]); + } + new_image->chunk_rows[j] = row; + } + return new_image; +} + +void icylib_primitive_chunked_extend_to(icylib_PrimitiveChunkedImage* image, int new_width, int new_height) { + if (new_width > image->rows_length * ICYLIB_PRIMITIVE_CHUNK_WIDTH || new_height > image->columns_length * ICYLIB_PRIMITIVE_CHUNK_HEIGHT) { + int new_rows_length = new_height / ICYLIB_PRIMITIVE_CHUNK_HEIGHT; + int new_columns_length = new_width / ICYLIB_PRIMITIVE_CHUNK_WIDTH; + icylib_PrimitiveChunkRow* new_chunk_rows = ICYLIB_MALLOC(sizeof(icylib_PrimitiveChunkRow) * new_rows_length); + for (int j = 0; j < new_rows_length; j++) { + icylib_PrimitiveChunkRow row; + icylib_RegularImage** chunk_columns = ICYLIB_MALLOC(sizeof(icylib_RegularImage*) * new_columns_length); + for (int i = 0; i < new_columns_length; i++) { + if (j < image->rows_length && i < image->columns_length) { + chunk_columns[i] = image->chunk_rows[j].chunk_columns[i]; + } + else { + chunk_columns[i] = icylib_regular_create_from_size(ICYLIB_PRIMITIVE_CHUNK_WIDTH, ICYLIB_PRIMITIVE_CHUNK_HEIGHT, image->channels); + } + } + row.chunk_columns = chunk_columns; + new_chunk_rows[j] = row; + } + ICYLIB_FREE(image->chunk_rows); + image->chunk_rows = new_chunk_rows; + image->rows_length = new_rows_length; + image->columns_length = new_columns_length; + } + image->width = new_width; + image->height = new_height; +} + +void icylib_primitive_chunked_resize(icylib_PrimitiveChunkedImage* image, int width, int height) { + icylib_primitive_chunked_extend_to(image, width, height); + // scale the invididual chunks and split them up into multiple chunks + icylib_PrimitiveChunkRow* new_chunk_rows = ICYLIB_MALLOC(sizeof(icylib_PrimitiveChunkRow) * icylib_ceil((float)height / ICYLIB_PRIMITIVE_CHUNK_HEIGHT)); + for (int j = 0; j < icylib_ceil((float)height / ICYLIB_PRIMITIVE_CHUNK_HEIGHT); j++) { + icylib_PrimitiveChunkRow row; + icylib_RegularImage** chunk_columns = ICYLIB_MALLOC(sizeof(icylib_RegularImage*) * icylib_ceil((float)width / ICYLIB_PRIMITIVE_CHUNK_WIDTH)); + for (int i = 0; i < icylib_ceil((float)width / ICYLIB_PRIMITIVE_CHUNK_WIDTH); i++) { + icylib_RegularImage* sub = icylib_primitive_chunked_get_sub_image(image, i * ICYLIB_PRIMITIVE_CHUNK_WIDTH, j * ICYLIB_PRIMITIVE_CHUNK_HEIGHT, ICYLIB_PRIMITIVE_CHUNK_WIDTH, ICYLIB_PRIMITIVE_CHUNK_HEIGHT); + icylib_regular_resize(sub, ICYLIB_PRIMITIVE_CHUNK_WIDTH, ICYLIB_PRIMITIVE_CHUNK_HEIGHT); + chunk_columns[i] = sub; + } + row.chunk_columns = chunk_columns; + new_chunk_rows[j] = row; + } + ICYLIB_FREE(image->chunk_rows); + image->chunk_rows = new_chunk_rows; + image->rows_length = width / ICYLIB_PRIMITIVE_CHUNK_WIDTH; + image->columns_length = height / ICYLIB_PRIMITIVE_CHUNK_HEIGHT; + image->width = width; + image->height = height; +} + +void icylib_primitive_chunked_resize_scale(icylib_PrimitiveChunkedImage* image, float scale) { + icylib_primitive_chunked_resize(image, image->width * scale, image->height * scale); +} + +#endif + +#endif \ No newline at end of file diff --git a/thirdparty/icylib/regular_image.h b/thirdparty/icylib/regular_image.h new file mode 100644 index 0000000..793e51e --- /dev/null +++ b/thirdparty/icylib/regular_image.h @@ -0,0 +1,582 @@ +#ifndef ICYLIB_REGULAR_IMAGE_H +#define ICYLIB_REGULAR_IMAGE_H + +#include + +#include "thirdparty/stb_image_write.h" + +#include "icylib.h" +#include "color.h" +#include "bresenham.h" +#include "thick_xiaolin_wu.h" +#include "text.h" +#include "math_utils.h" + +typedef struct icylib_RegularImage { + int width; + int height; + int channels; + unsigned char* data; +} icylib_RegularImage; + +typedef enum icylib_RegularImageFormat { + ICYLIB_REGULAR_IMAGE_FORMAT_PNG, + ICYLIB_REGULAR_IMAGE_FORMAT_BMP, + ICYLIB_REGULAR_IMAGE_FORMAT_TGA, + ICYLIB_REGULAR_IMAGE_FORMAT_JPG, + ICYLIB_REGULAR_IMAGE_FORMAT_HDR +} icylib_RegularImageFormat; + +int icylib_regular_get_width_from_file(const char* filename); + +int icylib_regular_get_height_from_file(const char* filename); + +icylib_RegularImage* icylib_regular_create_from_size(int width, int height, int channels); + +icylib_RegularImage* icylib_regular_create_from_memory(unsigned char* data, int width, int height, int channels); + +icylib_RegularImage* icylib_regular_load_from_file(const char* filename); + +icylib_RegularImage* icylib_regular_load_from_file_data(unsigned char* data, int length); + +int icylib_regular_save_to_file(icylib_RegularImage* image, const char* filename); + +unsigned char* icylib_regular_save_to_file_data(icylib_RegularImage* image, icylib_RegularImageFormat format, int* length); + +void icylib_regular_free(icylib_RegularImage* image); + +icylib_Color icylib_regular_get_pixel(icylib_RegularImage* image, int x, int y); + +void icylib_regular_set_pixel(icylib_RegularImage* image, int x, int y, icylib_Color color); + +void icylib_regular_set_pixel_blend(icylib_RegularImage* image, int x, int y, icylib_Color color); + +void icylib_regular_fill(icylib_RegularImage* image, icylib_Color color, unsigned char blend); + +void icylib_regular_draw_rectangle(icylib_RegularImage* image, int x1, int y1, int x2, int y2, icylib_Color color, unsigned char blend); + +void icylib_regular_fill_rectangle(icylib_RegularImage* image, int x1, int y1, int x2, int y2, icylib_Color color, unsigned char blend); + +void icylib_regular_draw_image(icylib_RegularImage* image, int x, int y, icylib_RegularImage* other, unsigned char blend); + +void icylib_regular_draw_circle(icylib_RegularImage* image, int x, int y, int radius, icylib_Color color, unsigned char blend); + +void icylib_regular_fill_circle(icylib_RegularImage* image, int x, int y, int radius, icylib_Color color, unsigned char blend); + +void icylib_regular_draw_line_with_thickness(icylib_RegularImage* image, int x1, int y1, int x2, int y2, int thickness, icylib_Color color, unsigned char blend, unsigned char antialias); + +void icylib_regular_draw_line(icylib_RegularImage* image, int x1, int y1, int x2, int y2, icylib_Color color, unsigned char blend, unsigned char antialias); + +void icylib_regular_draw_text(icylib_RegularImage* image, char* text, int x, int y, icylib_Color color, char* fontPath, int pixelSize, icylib_HorizontalAlignment horizontalAlignment, icylib_VerticalAlignment verticalAlignment, unsigned char blend); + +void icylib_regular_replace_color(icylib_RegularImage* image, icylib_Color old, icylib_Color new, unsigned char blend); + +void icylib_regular_replace_color_ignore_alpha(icylib_RegularImage* image, icylib_Color old, icylib_Color new); + +void icylib_regular_blur(icylib_RegularImage* image, int radius); + +icylib_RegularImage* icylib_regular_get_sub_image(icylib_RegularImage* image, int x, int y, int width, int height); + +icylib_RegularImage* icylib_regular_copy(icylib_RegularImage* image); + +void icylib_regular_extend_to(icylib_RegularImage* image, int new_width, int new_height); + +void icylib_regular_resize(icylib_RegularImage* image, int width, int height); + +void icylib_regular_resize_scale(icylib_RegularImage* image, float scale); + +#ifdef ICYLIB_IMPLEMENTATION + +int icylib_regular_get_width_from_file(const char* filename) { + int width, height, channels; + stbi_info(filename, &width, &height, &channels); + return width; +} + +int icylib_regular_get_height_from_file(const char* filename) { + int width, height, channels; + stbi_info(filename, &width, &height, &channels); + return height; +} + +icylib_RegularImage* icylib_regular_create_from_size(int width, int height, int channels) { + icylib_RegularImage* image = ICYLIB_MALLOC(sizeof(icylib_RegularImage)); + image->width = width; + image->height = height; + image->channels = channels; + image->data = ICYLIB_MALLOC_ATOMIC(width * height * channels); + return image; +} + +icylib_RegularImage* icylib_regular_create_from_memory(unsigned char* data, int width, int height, int channels) { + icylib_RegularImage* image = ICYLIB_MALLOC(sizeof(icylib_RegularImage)); + image->width = width; + image->height = height; + image->channels = channels; + image->data = data; + return image; +} + +icylib_RegularImage* icylib_regular_load_from_file(const char* filename) { + icylib_RegularImage* image = ICYLIB_MALLOC(sizeof(icylib_RegularImage)); + image->data = stbi_load(filename, &image->width, &image->height, &image->channels, 0); + if (image->data == NULL) { + ICYLIB_FREE(image); + return NULL; + } + return image; +} + +icylib_RegularImage* icylib_regular_load_from_file_data(unsigned char* data, int length) { + icylib_RegularImage* image = ICYLIB_MALLOC(sizeof(icylib_RegularImage)); + image->data = stbi_load_from_memory(data, length, &image->width, &image->height, &image->channels, 0); + return image; +} + +int icylib_regular_save_to_file(icylib_RegularImage* image, const char* filename) { + const char* extension = strrchr(filename, '.'); + if (strcmp(extension, ".png") == 0) { + return stbi_write_png(filename, image->width, image->height, image->channels, image->data, image->width * image->channels); + } + else if (strcmp(extension, ".bmp") == 0) { + return stbi_write_bmp(filename, image->width, image->height, image->channels, image->data); + } + else if (strcmp(extension, ".tga") == 0) { + return stbi_write_tga(filename, image->width, image->height, image->channels, image->data); + } + else if (strcmp(extension, ".jpg") == 0 || strcmp(extension, ".jpeg") == 0) { + return stbi_write_jpg(filename, image->width, image->height, image->channels, image->data, 100); + } + else if (strcmp(extension, ".hdr") == 0) { + return stbi_write_hdr(filename, image->width, image->height, image->channels, (float*)image->data); + } + else { + ICYLIB_ERROR("Unsupported image format"); + return 0; + } +} + +unsigned char* icylib_regular_save_to_file_data(icylib_RegularImage* image, icylib_RegularImageFormat format, int* length) { + unsigned char* data; + switch (format) { + case ICYLIB_REGULAR_IMAGE_FORMAT_PNG: + return stbi_write_png_to_mem(image->data, image->width * image->channels, image->width, image->height, image->channels, length); + case ICYLIB_REGULAR_IMAGE_FORMAT_BMP: + case ICYLIB_REGULAR_IMAGE_FORMAT_TGA: + case ICYLIB_REGULAR_IMAGE_FORMAT_JPG: + case ICYLIB_REGULAR_IMAGE_FORMAT_HDR: + // TODO: Implement + default: + ICYLIB_ERROR("Unsupported image format"); + return NULL; + } + return data; +} + +void icylib_regular_free(icylib_RegularImage* image) { + stbi_image_free(image->data); + ICYLIB_FREE(image); +} + +icylib_Color icylib_regular_get_pixel(icylib_RegularImage* image, int x, int y) { + if (x < 0 || x >= image->width || y < 0 || y >= image->height) { + return (icylib_Color) { 0, 0, 0, 0 }; + } + + int index = (y * image->width + x) * image->channels; + return (icylib_Color) { image->data[index], image->data[index + 1], image->data[index + 2], image->channels == 4 ? image->data[index + 3] : 255 }; +} + +void icylib_regular_set_pixel(icylib_RegularImage* image, int x, int y, icylib_Color color) { + if (x < 0 || x >= image->width || y < 0 || y >= image->height) { + return; + } + + int index = (y * image->width + x) * image->channels; + image->data[index] = color.r; + image->data[index + 1] = color.g; + image->data[index + 2] = color.b; + if (image->channels == 4) { + image->data[index + 3] = color.a; + } +} + +void icylib_regular_set_pixel_blend(icylib_RegularImage* image, int x, int y, icylib_Color color) { + if (x < 0 || x >= image->width || y < 0 || y >= image->height) { + return; + } + + int index = (y * image->width + x) * image->channels; + float aa = (float)color.a / 255; + float ba = (float)image->data[index + 3] / 255; + float ca = aa + (1 - aa) * ba; + image->data[index] = (unsigned char)(1 / ca * (aa * ((float)color.r / 255) + (1 - aa) * ba * ((float)image->data[index] / 255)) * 255); + image->data[index + 1] = (unsigned char)(1 / ca * (aa * ((float)color.g / 255) + (1 - aa) * ba * ((float)image->data[index + 1] / 255)) * 255); + image->data[index + 2] = (unsigned char)(1 / ca * (aa * ((float)color.b / 255) + (1 - aa) * ba * ((float)image->data[index + 2] / 255)) * 255); + if (image->channels == 4) { + image->data[index + 3] = (unsigned char)(ca * 255); + } +} + +void icylib_regular_fill(icylib_RegularImage* image, icylib_Color color, unsigned char blend) { + if (blend) { + icylib_regular_fill_rectangle(image, 0, 0, image->width, image->height, color, blend); + } + else { + for (int i = 0; i < image->width * image->height; i++) { + memcpy(image->data + i * image->channels, (unsigned char*)&color, image->channels); + } + } +} + +void icylib_regular_draw_rectangle(icylib_RegularImage* image, int x1, int y1, int x2, int y2, icylib_Color color, unsigned char blend) { + icylib_regular_draw_line(image, x1, y1, x2, y1, color, blend, 0); + icylib_regular_draw_line(image, x1, y2, x2, y2, color, blend, 0); + icylib_regular_draw_line(image, x1, y1, x1, y2, color, blend, 0); + icylib_regular_draw_line(image, x2, y1, x2, y2, color, blend, 0); +} + +void icylib_regular_fill_rectangle(icylib_RegularImage* image, int x1, int y1, int x2, int y2, icylib_Color color, unsigned char blend) { + if (blend) { + for (int j = icylib_imax(0, y1); j < icylib_imin(image->height, y2); j++) { + for (int i = icylib_imax(0, x1); i < icylib_imin(image->width, x2); i++) { + icylib_regular_set_pixel_blend(image, i, j, color); + } + } + } + else { + for (int j = icylib_imax(0, y1); j < icylib_imin(image->height, y2); j++) { + for (int i = icylib_imax(0, x1); i < icylib_imin(image->width, x2); i++) { + memcpy(image->data + (j * image->width + i) * image->channels, (unsigned char*)&color, image->channels); + } + } + } +} + +#ifndef ICYLIB_NO_SIMD +#include +#endif + +void icylib_regular_draw_image(icylib_RegularImage* image, int x, int y, icylib_RegularImage* other, unsigned char blend) { + int minY = icylib_imax(-y, 0); + int minX = icylib_imax(-x, 0); + int maxY = icylib_imin(other->height, image->height - icylib_imax(y, 0)); + int maxX = icylib_imin(other->width, image->width - icylib_imax(x, 0)); + if (blend) { + for (int j = minY; j < maxY; j++) { + for (int i = minX; i < maxX; i++) { + icylib_regular_set_pixel_blend(image, x + i, y + j, icylib_color_from_rgba(other->data[(j * other->width + i) * other->channels], other->data[(j * other->width + i) * other->channels + 1], other->data[(j * other->width + i) * other->channels + 2], other->channels == 4 ? other->data[(j * other->width + i) * other->channels + 3] : 255)); + } + } + } + else { +#ifndef ICYLIB_NO_SIMD + if (image->channels == 4) { + for (int j = minY; j < maxY; j++) { + for (int i = minX; i < maxX / 4; i++) { + unsigned char* source_address = other->data + (i * 4 + other->width * j) * other->channels; + unsigned char* destination_address = image->data + ((x + i * 4) + image->width * (y + j)) * image->channels; + __m128i source = _mm_loadu_si128((__m128i*)source_address); + _mm_storeu_si128((__m128i*)destination_address, source); + } + } + // Handle the remaining pixels separately + for (int j = minY; j < maxY; j++) { + for (int i = (minX / 4) * 4; i < maxX; i++) { + unsigned char* source_address = other->data + (i + other->width * j) * other->channels; + unsigned char* destination_address = image->data + ((x + i) + image->width * (y + j)) * image->channels; + memcpy(destination_address, source_address, other->channels); + } + } + } + else { +#endif + for (int j = minY; j < maxY; j++) { + for (int i = minX; i < maxX; i++) { + memcpy(image->data + ((x + i) + image->width * (y + j)) * image->channels, other->data + (i + other->width * j) * other->channels, other->channels); + } + } +#ifndef ICYLIB_NO_SIMD + } +#endif + } +} + +void icylib_regular_draw_circle(icylib_RegularImage* image, int x, int y, int radius, icylib_Color color, unsigned char blend) { + icylib_draw_bresenham_circle((unsigned char*)image, x, y, radius, color, (void (*)(unsigned char*, int, int, icylib_Color))(blend ? icylib_regular_set_pixel_blend : icylib_regular_set_pixel)); +} + +void icylib_regular_fill_circle(icylib_RegularImage* image, int x, int y, int radius, icylib_Color color, unsigned char blend) { + int rsquared = radius * radius; + for (int j = -radius; j < radius; j++) { + for (int i = -radius; i < radius; i++) { + if ((i * i + j * j) < rsquared) { + if (blend) { + icylib_regular_set_pixel_blend(image, x + i, y + j, color); + } + else { + icylib_regular_set_pixel(image, x + i, y + j, color); + } + } + } + } +} + +void icylib_regular_draw_line_with_thickness(icylib_RegularImage* image, int x1, int y1, int x2, int y2, int thickness, icylib_Color color, unsigned char blend, unsigned char antialias) { + if (antialias) { + icylib_draw_thick_xiaolin_wu_aa_line((unsigned char*)image, x1, y1, x2, y2, thickness, color, (void (*)(unsigned char*, int, int, icylib_Color))(blend ? icylib_regular_set_pixel_blend : icylib_regular_set_pixel)); + } + else { + icylib_draw_bresenham_thick_line((unsigned char*)image, x1, y1, x2, y2, thickness, color, (void (*)(unsigned char*, int, int, icylib_Color))(blend ? icylib_regular_set_pixel_blend : icylib_regular_set_pixel)); + } +} + +void icylib_regular_draw_line(icylib_RegularImage* image, int x1, int y1, int x2, int y2, icylib_Color color, unsigned char blend, unsigned char antialias) { + icylib_regular_draw_line_with_thickness(image, x1, y1, x2, y2, 1, color, blend, antialias); +} + +void icylib_regular_draw_text(icylib_RegularImage* image, char* text, int x, int y, icylib_Color color, char* fontPath, int pixelSize, icylib_HorizontalAlignment horizontalAlignment, icylib_VerticalAlignment verticalAlignment, unsigned char blend) { + double width, height; + icylib_measure_text_size(text, fontPath, pixelSize, &width, &height); + + stbtt_fontinfo* font = NULL; + icylib_FontCache* fonts = icylib_get_font_cache(); + + for (int i = 0; i < fonts->length; ++i) { + if (strcmp(fonts->data[i].name, fontPath) == 0) { + font = fonts->data[i].font; + break; + } + } + if (font == NULL) { + unsigned char* font_file; + icylib_read_bytes(fontPath, &font_file); + + font = ICYLIB_MALLOC(sizeof(stbtt_fontinfo)); + + int offset = stbtt_GetFontOffsetForIndex(font_file, 0); + stbtt_InitFont(font, font_file, offset); + + fonts->data = ICYLIB_REALLOC(fonts->data, sizeof(icylib_FontCacheEntry) * (fonts->length + 1)); + fonts->data[fonts->length].name = fontPath; + fonts->data[fonts->length].font = font; + fonts->length++; + } + + float scale = stbtt_ScaleForPixelHeight(font, pixelSize); + + int ascent, zero; + stbtt_GetFontVMetrics(font, &ascent, &zero, &zero); + + int advance, lsb, x0, y0, x1, y1; + + float x_cursor = x; + if (horizontalAlignment == ICYLIB_HORIZONTAL_ALIGNMENT_CENTER) { + x_cursor -= width / 2; + } + else if (horizontalAlignment == ICYLIB_HORIZONTAL_ALIGNMENT_RIGHT) { + x_cursor -= width; + } + float y_cursor = y; + if (verticalAlignment == ICYLIB_VERTICAL_ALIGNMENT_CENTER) { + y_cursor -= height / 2; + } + else if (verticalAlignment == ICYLIB_VERTICAL_ALIGNMENT_TOP) { + y_cursor += height; + } + for (size_t i = 0; i < strlen(text); ++i) { + if (text[i] == '\n') { + y_cursor += ascent * scale; + x_cursor = x; + continue; + } + + int c = (int)text[i]; + + stbtt_GetCodepointHMetrics(font, c, &advance, &lsb); + stbtt_GetCodepointBitmapBox(font, c, scale, scale, &x0, &y0, &x1, &y1); + + int w = 0; + int h = 0; + unsigned char* bitmap = stbtt_GetCodepointBitmap(font, 0, scale, c, &w, &h, 0, 0); + for (int b = 0; b < h; ++b) { + for (int a = 0; a < w; ++a) { + if (bitmap[b * w + a] != 0) { + int pixel_x = (int)x_cursor + x0 + a; + int pixel_y = (int)y_cursor + y0 + b; + if (pixel_x < 0 || pixel_x >= image->width || pixel_y < 0 || pixel_y >= image->height) { + continue; + } + if (blend) { + icylib_regular_set_pixel_blend(image, pixel_x, pixel_y, icylib_color_from_rgba(color.r, color.g, color.b, (unsigned char)(bitmap[b * w + a]))); + } + else { + icylib_regular_set_pixel(image, pixel_x, pixel_y, icylib_color_from_rgba(color.r, color.g, color.b, (unsigned char)(bitmap[b * w + a]))); + } + } + } + } + + if (i < strlen(text) - 1) { + x_cursor += advance * scale; + x_cursor += scale * stbtt_GetCodepointKernAdvance(font, c, (int)text[i + 1]); + } + } +} + +void icylib_regular_replace_color(icylib_RegularImage* image, icylib_Color old, icylib_Color new, unsigned char blend) { + for (int j = 0; j < image->height; j++) { + for (int i = 0; i < image->width; i++) { + icylib_Color pixel = icylib_regular_get_pixel(image, i, j); + if (pixel.r == old.r && pixel.g == old.g && pixel.b == old.b && pixel.a == old.a) { + if (blend) { + icylib_regular_set_pixel_blend(image, i, j, new); + } + else { + icylib_regular_set_pixel(image, i, j, new); + } + } + } + } +} + +void icylib_regular_replace_color_ignore_alpha(icylib_RegularImage* image, icylib_Color old, icylib_Color new) { + for (int j = 0; j < image->height; j++) { + for (int i = 0; i < image->width; i++) { + icylib_Color pixel = icylib_regular_get_pixel(image, i, j); + if (pixel.r == old.r && pixel.g == old.g && pixel.b == old.b) { + icylib_regular_set_pixel(image, i, j, icylib_color_from_rgba(new.r, new.g, new.b, pixel.a)); + } + } + } +} + +void icylib_regular_blur(icylib_RegularImage* image, int radius) { + int width = image->width; + int height = image->height; + int channels = image->channels; + unsigned char* temp_data = ICYLIB_MALLOC_ATOMIC(width * height * channels); + unsigned char* new_data = ICYLIB_MALLOC_ATOMIC(width * height * channels); + + // Horizontal pass + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) { + int r = 0; + int g = 0; + int b = 0; + int a = 0; + int count = 0; + + for (int x = -radius; x <= radius; x++) { + int nx = i + x; + if (nx >= 0 && nx < width) { + icylib_Color pixel = icylib_regular_get_pixel(image, nx, j); + r += pixel.r; + g += pixel.g; + b += pixel.b; + a += pixel.a; + count++; + } + } + + temp_data[(j * width + i) * channels] = r / count; + temp_data[(j * width + i) * channels + 1] = g / count; + temp_data[(j * width + i) * channels + 2] = b / count; + temp_data[(j * width + i) * channels + 3] = a / count; + } + } + + // Vertical pass + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) { + int r = 0; + int g = 0; + int b = 0; + int a = 0; + int count = 0; + + for (int y = -radius; y <= radius; y++) { + int ny = j + y; + if (ny >= 0 && ny < height) { + r += temp_data[(ny * width + i) * channels]; + g += temp_data[(ny * width + i) * channels + 1]; + b += temp_data[(ny * width + i) * channels + 2]; + a += temp_data[(ny * width + i) * channels + 3]; + count++; + } + } + + new_data[(j * width + i) * channels] = r / count; + new_data[(j * width + i) * channels + 1] = g / count; + new_data[(j * width + i) * channels + 2] = b / count; + new_data[(j * width + i) * channels + 3] = a / count; + } + } + + ICYLIB_FREE(temp_data); + ICYLIB_FREE(image->data); + image->data = new_data; +} + +icylib_RegularImage* icylib_regular_get_sub_image(icylib_RegularImage* image, int x, int y, int width, int height) { + icylib_RegularImage* sub_image = ICYLIB_MALLOC(sizeof(icylib_RegularImage)); + sub_image->width = width; + sub_image->height = height; + sub_image->channels = image->channels; + sub_image->data = ICYLIB_MALLOC_ATOMIC(width * height * image->channels); + for (int j = 0; j < height; j++) { + memcpy(sub_image->data + j * width * image->channels, image->data + ((j + y) * image->width + x) * image->channels, width * image->channels); + } + return sub_image; +} + +icylib_RegularImage* icylib_regular_copy(icylib_RegularImage* image) { + icylib_RegularImage* copy = icylib_regular_create_from_size(image->width, image->height, image->channels); + memcpy(copy->data, image->data, image->width * image->height * image->channels); + return copy; +} + +void icylib_regular_extend_to(icylib_RegularImage* image, int new_width, int new_height) { + if (new_width <= image->width && new_height <= image->height) { + return; + } + + int old_width = image->width; + int old_height = image->height; + int channels = image->channels; + + unsigned char* new_data = ICYLIB_MALLOC_ATOMIC(new_width * new_height * channels); + + for (int j = 0; j < old_height; j++) { + for (int i = 0; i < old_width; i++) { + int old_index = (j * old_width + i) * channels; + int new_index = (j * new_width + i) * channels; + for (int c = 0; c < channels; c++) { + new_data[new_index + c] = image->data[old_index + c]; + } + } + } + + ICYLIB_FREE(image->data); + + image->data = new_data; + image->width = new_width; + image->height = new_height; +} + +void icylib_regular_resize(icylib_RegularImage* image, int width, int height) { + unsigned char* new_data = ICYLIB_MALLOC_ATOMIC(width * height * image->channels); + stbir_resize_uint8_linear(image->data, image->width, image->height, 0, new_data, width, height, 0, (stbir_pixel_layout)image->channels); + ICYLIB_FREE(image->data); + image->data = new_data; + image->width = width; + image->height = height; +} + +void icylib_regular_resize_scale(icylib_RegularImage* image, float scale) { + icylib_regular_resize(image, image->width * scale, image->height * scale); +} + +#endif + +#endif \ No newline at end of file diff --git a/thirdparty/icylib/text.h b/thirdparty/icylib/text.h new file mode 100644 index 0000000..23ae7ea --- /dev/null +++ b/thirdparty/icylib/text.h @@ -0,0 +1,277 @@ +#ifndef ICYLIB_TEXT_H +#define ICYLIB_TEXT_H + +#include +#include +#include + +#include "thirdparty/stb_truetype.h" + +#include "icylib.h" + +typedef struct icylib_FontCacheEntry { + const char* name; + stbtt_fontinfo* font; +} icylib_FontCacheEntry; + +typedef struct icylib_FontCache { + icylib_FontCacheEntry* data; + int length; +} icylib_FontCache; + +typedef enum icylib_HorizontalAlignment { + ICYLIB_HORIZONTAL_ALIGNMENT_LEFT, + ICYLIB_HORIZONTAL_ALIGNMENT_CENTER, + ICYLIB_HORIZONTAL_ALIGNMENT_RIGHT +} icylib_HorizontalAlignment; + +typedef enum icylib_VerticalAlignment { + ICYLIB_VERTICAL_ALIGNMENT_TOP, + ICYLIB_VERTICAL_ALIGNMENT_CENTER, + ICYLIB_VERTICAL_ALIGNMENT_BOTTOM +} icylib_VerticalAlignment; + +icylib_FontCache* icylib_get_font_cache(); + +void icylib_set_font_cache(icylib_FontCache* fonts); + +char* icylib_get_default_font_path(); + +char* icylib_get_font_variant_path(const char* font_path, const char* variant); + +void icylib_read_bytes(const char* path, unsigned char** font_file); + +void icylib_measure_text_size(const char* text, const char* font_path, int pixel_size, double* result_x, double* result_y); + +#ifdef ICYLIB_IMPLEMENTATION + +icylib_FontCache* icylib_get_font_cache() { + static icylib_FontCache* fonts = NULL; + if (!fonts) { + fonts = ICYLIB_MALLOC(sizeof(icylib_FontCache)); + fonts->data = ICYLIB_MALLOC(1); + fonts->length = 0; + } + return fonts; +} + +void icylib_set_font_cache(icylib_FontCache* fonts) { + icylib_FontCache* old_fonts = icylib_get_font_cache(); + if (old_fonts->data) { + ICYLIB_FREE(old_fonts->data); + } + old_fonts->data = fonts->data; +} + +char* icylib_get_default_font_path() { + // TODO: Improve this +#ifdef _WIN32 + return "C:/Windows/Fonts/arial.ttf"; +#elif __APPLE__ + return "/Library/Fonts/Arial.ttf"; +#elif __ANDROID__ + // taken from the V standard library + char* xml_files[] = { "/system/etc/system_fonts.xml", "/system/etc/fonts.xml", "/etc/system_fonts.xml", "/etc/fonts.xml", "/data/fonts/fonts.xml", "/etc/fallback_fonts.xml" }; + char* font_locations[] = { "/system/fonts", "/data/fonts" }; + for (int i = 0; i < 6; ++i) { + FILE* file = fopen(xml_files[i], "r"); + if (file) { + char* xml = NULL; + size_t n = 0; + ssize_t read; + while ((read = getline(&xml, &n, file)) != -1) { + char* candidate_font = NULL; + if (strstr(xml, "") + 1; + candidate_font = strtok(candidate_font, "<"); + while (*candidate_font == ' ' || *candidate_font == '\t' || *candidate_font == '\n' || *candidate_font == '\r') { + candidate_font++; + } + char* end = candidate_font + strlen(candidate_font) - 1; + while (end > candidate_font && (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r')) { + *end = '\0'; + end--; + } + if (strstr(candidate_font, ".ttf")) { + for (int j = 0; j < 2; ++j) { + char* candidate_path = ICYLIB_MALLOC_ATOMIC(strlen(font_locations[j]) + strlen(candidate_font) + 2); + strcpy(candidate_path, font_locations[j]); + strcat(candidate_path, "/"); + strcat(candidate_path, candidate_font); + if (access(candidate_path, F_OK) == 0) { + return candidate_path; + } + ICYLIB_FREE(candidate_path); + } + } + } + } + fclose(file); + } + } + ICYLIB_ERROR("No default font found"); +#else + return "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"; +#endif +} + +// This is a modified version of os.font.get_path_variant() of the V standard library +char* icylib_get_font_variant_path(const char* font_path, const char* variant) { + // Find some way to make this shorter and more eye-pleasant + // NotoSans, LiberationSans, DejaVuSans, Arial, and SFNS should work + char* file = strrchr(font_path, '/'); + char* fpath = ICYLIB_MALLOC_ATOMIC(file - font_path + 2); + strncpy(fpath, font_path, file - font_path + 1); + fpath[file - font_path + 1] = '\0'; + file++; + file = strrchr(font_path, '/'); + file = file ? file + 1 : (char*)font_path; + + char* file_copy = ICYLIB_MALLOC_ATOMIC(strlen(file) + 1); + strcpy(file_copy, file); + char* dot_position = strrchr(file_copy, '.'); + if (dot_position != NULL) { + *dot_position = '\0'; + } + + if (strcmp(variant, "normal") == 0) { + // No changes for normal variant + } + else if (strcmp(variant, "bold") == 0) { + if (strstr(fpath, "-Regular") != NULL || strstr(file_copy, "-Regular") != NULL) { + strcpy(file_copy, strstr(file_copy, "-Regular") ? strstr(file_copy, "-Regular") + 1 : strstr(fpath, "-Regular") + 1); + strcat(file_copy, "-Bold"); + } + else if (strncmp(file_copy, "DejaVuSans", 10) == 0 || strncmp(file_copy, "DroidSans", 9) == 0) { + strcat(file_copy, "-Bold"); + } + else if (strncasecmp(file_copy, "arial", 5) == 0) { + strcat(file_copy, "bd"); + } + else { + strcat(file_copy, "-bold"); + } +#ifdef __APPLE__ + if (access("SFNS-bold", F_OK) == 0) { + strcpy(file_copy, "SFNS-bold"); + } +#endif + } + else if (strcmp(variant, "italic") == 0) { + if (strstr(file_copy, "-Regular") != NULL) { + strcpy(file_copy, strstr(file_copy, "-Regular") + 1); + strcat(file_copy, "-Italic"); + } + else if (strncmp(file_copy, "DejaVuSans", 10) == 0) { + strcat(file_copy, "-Oblique"); + } + else if (strncasecmp(file_copy, "arial", 5) == 0) { + strcat(file_copy, "i"); + } + else { + strcat(file_copy, "Italic"); + } + } + else if (strcmp(variant, "mono") == 0) { + if (strcmp(file_copy, "Mono-Regular") != 0 && strstr(file_copy, "-Regular") != NULL) { + strcpy(file_copy, strstr(file_copy, "-Regular") + 1); + strcat(file_copy, "Mono-Regular"); + } + else if (strncasecmp(file_copy, "arial", 5) != 0) { + strcat(file_copy, "Mono"); + } + } + + char* result = ICYLIB_MALLOC_ATOMIC(strlen(fpath) + strlen(file_copy) + 5); // 5 for ".ttf" and null terminator + strcpy(result, fpath); + strcat(result, file_copy); + strcat(result, ".ttf"); + + ICYLIB_FREE(fpath); + ICYLIB_FREE(file_copy); + + return result; +} + +void icylib_read_bytes(const char* path, unsigned char** font_file) { + FILE* file = fopen(path, "rb"); + fseek(file, 0, SEEK_END); + long length = ftell(file); + fseek(file, 0, SEEK_SET); + *font_file = ICYLIB_MALLOC_ATOMIC(length); + fread(*font_file, 1, length, file); + fclose(file); +} + +void icylib_measure_text_size(const char* text, const char* font_path, int pixel_size, double* result_x, double* result_y) { + stbtt_fontinfo* font = NULL; + icylib_FontCache* fonts = icylib_get_font_cache(); + + for (int i = 0; i < fonts->length; ++i) { + if (strcmp(fonts->data[i].name, font_path) == 0) { + font = fonts->data[i].font; + break; + } + } + if (font == NULL) { + unsigned char* font_file; + icylib_read_bytes(font_path, &font_file); + + font = ICYLIB_MALLOC(sizeof(stbtt_fontinfo)); + + int offset = stbtt_GetFontOffsetForIndex(font_file, 0); + stbtt_InitFont(font, font_file, offset); + + fonts->data = ICYLIB_REALLOC(fonts->data, sizeof(icylib_FontCacheEntry) * (fonts->length + 1)); + fonts->data[fonts->length].name = font_path; + fonts->data[fonts->length].font = font; + fonts->length++; + } + + float scale = stbtt_ScaleForPixelHeight(font, pixel_size); + + int ascent, zero; + stbtt_GetFontVMetrics(font, &ascent, &zero, &zero); + + int advance, lsb, x0, y0, x1, y1; + double x_cursor = 0.0; + double max_x = 0.0; + double y_cursor = 0.0; + + size_t text_len = strlen(text); + for (size_t i = 0; i < text_len; ++i) { + if (text[i] == '\n') { + if (x_cursor > max_x) { + max_x = x_cursor; + } + y_cursor += ascent * scale; + x_cursor = 0; + continue; + } + + int c = (int)text[i]; + + stbtt_GetCodepointHMetrics(font, c, &advance, &lsb); + stbtt_GetCodepointBitmapBox(font, c, scale, scale, &x0, &y0, &x1, &y1); + + if (i < text_len - 1) { + x_cursor += advance * scale; + x_cursor += scale * stbtt_GetCodepointKernAdvance(font, c, (int)text[i + 1]); + } + } + + if (text_len >= 1) { + x_cursor += advance * scale; + } + + if (x_cursor > max_x) { + max_x = x_cursor; + } + + *result_x = max_x; + *result_y = y_cursor + ascent * scale; +} + +#endif + +#endif \ No newline at end of file diff --git a/thirdparty/icylib/thick_xiaolin_wu.h b/thirdparty/icylib/thick_xiaolin_wu.h new file mode 100644 index 0000000..21f7220 --- /dev/null +++ b/thirdparty/icylib/thick_xiaolin_wu.h @@ -0,0 +1,171 @@ +#ifndef ICYLIB_THICK_XIAOLIN_WU_H +#define ICYLIB_THICK_XIAOLIN_WU_H + +#include "color.h" + +void icylib_draw_thick_xiaolin_wu_aa_line(unsigned char* image, int x0, int y0, int x1, int y1, int w, icylib_Color color, void (*set_pixel)(unsigned char* image, int, int, icylib_Color)); + +#ifdef ICYLIB_IMPLEMENTATION + +#include + +#include "icylib.h" + +// The following function was originally written by John Bolton in CoffeScript and translated to C for this project. +/* +MIT License Copyright (c) 2022 John Bolton + +Permission is hereby granted, free of +charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice +(including the next paragraph) shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// last argument is a pointer to a function that takes an x, y, and a color +void icylib_draw_thick_xiaolin_wu_aa_line(unsigned char* image, int x0, int y0, int x1, int y1, int w, icylib_Color color, void (*set_pixel)(unsigned char* image, int, int, icylib_Color)) { + // Ensure positive integer values for width + if (w < 1) + { + w = 1; + } + + // steep means that m > 1 + int steep = abs(y1 - y0) > abs(x1 - x0); + + // If steep, then x and y must be swapped because the width is fixed in the y direction and that won't work if + // dx < dy. Note that they are swapped again when plotting. + if (steep) + { + int temp = x0; + x0 = y0; + y0 = temp; + temp = x1; + x1 = y1; + y1 = temp; + } + + // Swap endpoints to ensure that dx > 0 + if (x0 > x1) + { + int temp = x0; + x0 = x1; + x1 = temp; + temp = y0; + y0 = y1; + y1 = temp; + } + + int dx = x1 - x0; + int dy = y1 - y0; + double gradient = (dx > 0) ? (double)dy / (double)dx : 1.0; + + // Rotate w + w = w * sqrt(1 + (gradient * gradient)); + + // Handle first endpoint + int xend = round(x0); + double yend = y0 - (double)(w - 1) * 0.5 + gradient * (xend - x0); + double xgap = 1 - ((double)x0 + 0.5 - xend); + int xpxl1 = xend; // this will be used in the main loop + int ypxl1 = (int)floor(yend); + double fpart = yend - floor(yend); + double rfpart = 1 - fpart; + + if (steep) + { + set_pixel(image, ypxl1, xpxl1, icylib_color_from_rgba(color.r, color.g, color.b, color.a * (rfpart * xgap))); + for (int i = 1; i < w; i++) + { + set_pixel(image, ypxl1 + i, xpxl1, icylib_color_from_rgba(color.r, color.g, color.b, color.a)); + } + set_pixel(image, ypxl1 + w, xpxl1, icylib_color_from_rgba(color.r, color.g, color.b, color.a * (fpart * xgap))); + } + else + { + set_pixel(image, xpxl1, ypxl1, icylib_color_from_rgba(color.r, color.g, color.b, color.a * (rfpart * xgap))); + for (int i = 1; i < w; i++) + { + set_pixel(image, xpxl1, ypxl1 + i, icylib_color_from_rgba(color.r, color.g, color.b, color.a)); + } + set_pixel(image, xpxl1, ypxl1 + w, icylib_color_from_rgba(color.r, color.g, color.b, color.a * (fpart * xgap))); + } + + double intery = yend + gradient; // first y-intersection for the main loop + + // Handle second endpoint + xend = round(x1); + yend = y1 - (w - 1) * 0.5 + gradient * (xend - x1); + xgap = 1 - (x1 + 0.5 - xend); + int xpxl2 = xend; // this will be used in the main loop + int ypxl2 = (int)floor(yend); + fpart = yend - floor(yend); + rfpart = 1 - fpart; + + if (steep) { + set_pixel(image, ypxl2, xpxl2, icylib_color_from_rgba(color.r, color.g, color.b, color.a * (rfpart * xgap))); + for (int i = 1; i < w; i++) { + set_pixel(image, ypxl2 + i, xpxl2, icylib_color_from_rgba(color.r, color.g, color.b, color.a)); + } + set_pixel(image, ypxl2 + w, xpxl2, icylib_color_from_rgba(color.r, color.g, color.b, color.a * (fpart * xgap))); + } + else { + set_pixel(image, xpxl2, ypxl2, icylib_color_from_rgba(color.r, color.g, color.b, color.a * (rfpart * xgap))); + for (int i = 1; i < w; i++) { + set_pixel(image, xpxl2, ypxl2 + i, icylib_color_from_rgba(color.r, color.g, color.b, color.a)); + } + set_pixel(image, xpxl2, ypxl2 + w, icylib_color_from_rgba(color.r, color.g, color.b, color.a * (fpart * xgap))); + } + + // Main loop + if (steep) + { + for (int x = xpxl1 + 1; x < xpxl2; x++) + { + fpart = intery - floor(intery); + rfpart = 1 - fpart; + int y = (int)floor(intery); + set_pixel(image, y, x, icylib_color_from_rgba(color.r, color.g, color.b, color.a * rfpart)); + for (int i = 1; i < w; i++) + { + set_pixel(image, y + i, x, icylib_color_from_rgba(color.r, color.g, color.b, color.a)); + } + set_pixel(image, y + w, x, icylib_color_from_rgba(color.r, color.g, color.b, color.a * fpart)); + intery = intery + gradient; + } + } + else + { + for (int x = xpxl1 + 1; x < xpxl2; x++) + { + fpart = intery - floor(intery); + rfpart = 1 - fpart; + int y = (int)floor(intery); + set_pixel(image, x, y, icylib_color_from_rgba(color.r, color.g, color.b, color.a * rfpart)); + for (int i = 1; i < w; i++) + { + set_pixel(image, x, y + i, icylib_color_from_rgba(color.r, color.g, color.b, color.a)); + } + set_pixel(image, x, y + w, icylib_color_from_rgba(color.r, color.g, color.b, color.a * fpart)); + intery = intery + gradient; + } + } +} + +#endif + +#endif \ No newline at end of file diff --git a/thirdparty/icylib/thirdparty/stb_image.h b/thirdparty/icylib/thirdparty/stb_image.h new file mode 100644 index 0000000..5e807a0 --- /dev/null +++ b/thirdparty/icylib/thirdparty/stb_image.h @@ -0,0 +1,7987 @@ +/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.28 (2023-01-29) many error fixes, security errors, just tons of stuff + 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine Simon Breuss (16-bit PNM) + John-Mark Allen + Carmelo J Fdez-Aguera + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Eugene Golushkov Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko github:mosra + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + Jacko Dirks + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data); +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// To query the width, height and component count of an image without having to +// decode the full file, you can use the stbi_info family of functions: +// +// int x,y,n,ok; +// ok = stbi_info(filename, &x, &y, &n); +// // returns ok=1 and sets x, y, n if image is a supported format, +// // 0 otherwise. +// +// Note that stb_image pervasively uses ints in its public API for sizes, +// including sizes of memory buffers. This is now part of the API and thus +// hard to change without causing breakage. As a result, the various image +// loaders all have certain limits on image size; these differ somewhat +// by format but generally boil down to either just under 2GB or just under +// 1GB. When the decoded image would be larger than this, stb_image decoding +// will fail. +// +// Additionally, stb_image will reject image files that have any of their +// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, +// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, +// the only way to have an image with such dimensions load correctly +// is for it to have a rather extreme aspect ratio. Either way, the +// assumption here is that such larger images are likely to be malformed +// or malicious. If you do need to load an image with individual dimensions +// larger than that, and it still fits in the overall size limit, you can +// #define STBI_MAX_DIMENSIONS on your own to be something larger. +// +// =========================================================================== +// +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy-to-use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// provide more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// We optionally support converting iPhone-formatted PNGs (which store +// premultiplied BGRA) back to RGB, even though they're internally encoded +// differently. To enable this conversion, call +// stbi_convert_iphone_png_to_rgb(1). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// on most compilers (and ALL modern mainstream compilers) this is threadsafe +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif + +#if defined(_MSC_VER) || defined(__SYMBIAN32__) +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +#ifdef _MSC_VER +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#else +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + int callback_already_read; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + int ch; + fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user) || ferror((FILE *) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pnm_is16(stbi__context *s); +#endif + +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +#ifndef STBI_NO_FAILURE_STRINGS +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} +#endif + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} +#endif + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} +#endif + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. +static int stbi__addints_valid(int a, int b) +{ + if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow + if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. + return a <= INT_MAX - b; +} + +// returns 1 if the product of two signed shorts is valid, 0 on overflow. +static int stbi__mul2shorts_valid(short a, short b) +{ + if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow + if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid + if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN + return a >= SHRT_MIN / b; +} + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load_global = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 16) { + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else +static void stbi__skip(stbi__context *s, int n) +{ + if (n == 0) return; // already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} +#endif + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + z += (stbi__uint32)stbi__get16le(s) << 16; + return z; +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) { + for (j=0; j < count[i]; ++j) { + h->size[k++] = (stbi_uc) (i+1); + if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); + } + } + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + if(c < 0 || c >= 256) // symbol id out of bounds! + return -1; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing + + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & (sgn - 1)); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; + + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * (1 << j->succ_low)); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * (1 << shift)); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i=0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +static int stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) +{ + // some JPEGs have junk at end, skip over it but if we find what looks + // like a valid marker, resume there + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + while (x == 255) { // might be a marker + if (stbi__at_eof(j->s)) return STBI__MARKER_none; + x = stbi__get8(j->s); + if (x != 0x00 && x != 0xff) { + // not a stuffed zero or lead-in to another marker, looks + // like an actual marker, return it + return x; + } + // stuffed zero has x=0 now which ends the loop, meaning we go + // back to regular scan loop. + // repeated 0xff keeps trying to read the next byte of the marker. + } + } + return STBI__MARKER_none; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + j->marker = stbi__skip_jpeg_junk_at_end(j); + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + m = stbi__get_marker(j); + if (STBI__RESTART(m)) + m = stbi__get_marker(j); + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + m = stbi__get_marker(j); + } else { + if (!stbi__process_marker(j, m)) return 1; + m = stbi__get_marker(j); + } + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + return stbi__zeof(z) ? 0 : *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s >= 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + return -1; /* report error for unexpected end of data. */ + } + stbi__fill_bits(a); + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + unsigned int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + } else if (c == 18) { + c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load_global = 0; +static int stbi__de_iphone_flag_global = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_global = flag_true_if_should_convert; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#else +static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; +static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; + +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; +} + +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + } + // even with SCAN_header, have to scan to see if we have a tRNS + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. + if (scan == STBI__SCAN_header) { ++s->img_n; return 1; } + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { + // header scan definitely stops at first IDAT + if (pal_img_n) + s->img_n = pal_img_n; + return 1; + } + if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; + int extra_read; +} stbi__bmp_data; + +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) +{ + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; + + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error +} + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + stbi__bmp_set_mask_defaults(info, compress); + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + // V4/V5 header + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + // accept some number of extra bytes after the header, but if the offset points either to before + // the header ends or implies a large amount of extra data, reject the file as malformed + int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); + int header_limit = 1024; // max we actually read is below 256 bytes currently. + int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. + if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { + return stbi__errpuc("bad header", "Corrupt BMP"); + } + // we established that bytes_read_so_far is positive and sensible. + // the first half of this test rejects offsets that are either too small positives, or + // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn + // ensures the number computed in the second half of the test can't overflow. + if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } else { + stbi__skip(s, info.offset - bytes_read_so_far); + } + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + if (!result) return stbi__errpuc("outofmem", "Out of memory"); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) +{ + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); + + if (out) STBI_FREE(out); + if (delays && *delays) STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; + + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); + + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + + if (delays) { + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); + out_size = layers * stride; + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + delays_size = layers * sizeof(int); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + if (p == NULL) { + stbi__rewind( s ); + return 0; + } + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) + return 0; + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { + STBI_FREE(out); + return stbi__errpuc("bad PNM", "PNM file truncated"); + } + + if (req_comp && req_comp != s->img_n) { + if (ri->bits_per_channel == 16) { + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y); + } else { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + } + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + if((value > 214748364) || (value == 214748364 && *c > '7')) + return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + if(*x == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + if (*y == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; + else + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) +{ + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; + #endif + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/thirdparty/icylib/thirdparty/stb_image_resize2.h b/thirdparty/icylib/thirdparty/stb_image_resize2.h new file mode 100644 index 0000000..faf1b08 --- /dev/null +++ b/thirdparty/icylib/thirdparty/stb_image_resize2.h @@ -0,0 +1,10325 @@ +/* stb_image_resize2 - v2.04 - public domain image resizing + + by Jeff Roberts (v2) and Jorge L Rodriguez + http://github.com/nothings/stb + + Can be threaded with the extended API. SSE2, AVX, Neon and WASM SIMD support. Only + scaling and translation is supported, no rotations or shears. + + COMPILING & LINKING + In one C/C++ file that #includes this file, do this: + #define STB_IMAGE_RESIZE_IMPLEMENTATION + before the #include. That will create the implementation in that file. + + PORTING FROM VERSION 1 + + The API has changed. You can continue to use the old version of stb_image_resize.h, + which is available in the "deprecated/" directory. + + If you're using the old simple-to-use API, porting is straightforward. + (For more advanced APIs, read the documentation.) + + stbir_resize_uint8(): + - call `stbir_resize_uint8_linear`, cast channel count to `stbir_pixel_layout` + + stbir_resize_float(): + - call `stbir_resize_float_linear`, cast channel count to `stbir_pixel_layout` + + stbir_resize_uint8_srgb(): + - function name is unchanged + - cast channel count to `stbir_pixel_layout` + - above is sufficient unless your image has alpha and it's not RGBA/BGRA + - in that case, follow the below instructions for stbir_resize_uint8_srgb_edgemode + + stbir_resize_uint8_srgb_edgemode() + - switch to the "medium complexity" API + - stbir_resize(), very similar API but a few more parameters: + - pixel_layout: cast channel count to `stbir_pixel_layout` + - data_type: STBIR_TYPE_UINT8_SRGB + - edge: unchanged (STBIR_EDGE_WRAP, etc.) + - filter: STBIR_FILTER_DEFAULT + - which channel is alpha is specified in stbir_pixel_layout, see enum for details + + EASY API CALLS: + Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation, clamps to edge. + + stbir_resize_uint8_srgb( input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout_enum ) + + stbir_resize_uint8_linear( input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout_enum ) + + stbir_resize_float_linear( input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout_enum ) + + If you pass NULL or zero for the output_pixels, we will allocate the output buffer + for you and return it from the function (free with free() or STBIR_FREE). + As a special case, XX_stride_in_bytes of 0 means packed continuously in memory. + + API LEVELS + There are three levels of API - easy-to-use, medium-complexity and extended-complexity. + + See the "header file" section of the source for API documentation. + + ADDITIONAL DOCUMENTATION + + MEMORY ALLOCATION + By default, we use malloc and free for memory allocation. To override the + memory allocation, before the implementation #include, add a: + + #define STBIR_MALLOC(size,user_data) ... + #define STBIR_FREE(ptr,user_data) ... + + Each resize makes exactly one call to malloc/free (unless you use the + extended API where you can do one allocation for many resizes). Under + address sanitizer, we do separate allocations to find overread/writes. + + PERFORMANCE + This library was written with an emphasis on performance. When testing + stb_image_resize with RGBA, the fastest mode is STBIR_4CHANNEL with + STBIR_TYPE_UINT8 pixels and CLAMPed edges (which is what many other resize + libs do by default). Also, make sure SIMD is turned on of course (default + for 64-bit targets). Avoid WRAP edge mode if you want the fastest speed. + + This library also comes with profiling built-in. If you define STBIR_PROFILE, + you can use the advanced API and get low-level profiling information by + calling stbir_resize_extended_profile_info() or stbir_resize_split_profile_info() + after a resize. + + SIMD + Most of the routines have optimized SSE2, AVX, NEON and WASM versions. + + On Microsoft compilers, we automatically turn on SIMD for 64-bit x64 and + ARM; for 32-bit x86 and ARM, you select SIMD mode by defining STBIR_SSE2 or + STBIR_NEON. For AVX and AVX2, we auto-select it by detecting the /arch:AVX + or /arch:AVX2 switches. You can also always manually turn SSE2, AVX or AVX2 + support on by defining STBIR_SSE2, STBIR_AVX or STBIR_AVX2. + + On Linux, SSE2 and Neon is on by default for 64-bit x64 or ARM64. For 32-bit, + we select x86 SIMD mode by whether you have -msse2, -mavx or -mavx2 enabled + on the command line. For 32-bit ARM, you must pass -mfpu=neon-vfpv4 for both + clang and GCC, but GCC also requires an additional -mfp16-format=ieee to + automatically enable NEON. + + On x86 platforms, you can also define STBIR_FP16C to turn on FP16C instructions + for converting back and forth to half-floats. This is autoselected when we + are using AVX2. Clang and GCC also require the -mf16c switch. ARM always uses + the built-in half float hardware NEON instructions. + + You can also tell us to use multiply-add instructions with STBIR_USE_FMA. + Because x86 doesn't always have fma, we turn it off by default to maintain + determinism across all platforms. If you don't care about non-FMA determinism + and are willing to restrict yourself to more recent x86 CPUs (around the AVX + timeframe), then fma will give you around a 15% speedup. + + You can force off SIMD in all cases by defining STBIR_NO_SIMD. You can turn + off AVX or AVX2 specifically with STBIR_NO_AVX or STBIR_NO_AVX2. AVX is 10% + to 40% faster, and AVX2 is generally another 12%. + + ALPHA CHANNEL + Most of the resizing functions provide the ability to control how the alpha + channel of an image is processed. + + When alpha represents transparency, it is important that when combining + colors with filtering, the pixels should not be treated equally; they + should use a weighted average based on their alpha values. For example, + if a pixel is 1% opaque bright green and another pixel is 99% opaque + black and you average them, the average will be 50% opaque, but the + unweighted average and will be a middling green color, while the weighted + average will be nearly black. This means the unweighted version introduced + green energy that didn't exist in the source image. + + (If you want to know why this makes sense, you can work out the math for + the following: consider what happens if you alpha composite a source image + over a fixed color and then average the output, vs. if you average the + source image pixels and then composite that over the same fixed color. + Only the weighted average produces the same result as the ground truth + composite-then-average result.) + + Therefore, it is in general best to "alpha weight" the pixels when applying + filters to them. This essentially means multiplying the colors by the alpha + values before combining them, and then dividing by the alpha value at the + end. + + The computer graphics industry introduced a technique called "premultiplied + alpha" or "associated alpha" in which image colors are stored in image files + already multiplied by their alpha. This saves some math when compositing, + and also avoids the need to divide by the alpha at the end (which is quite + inefficient). However, while premultiplied alpha is common in the movie CGI + industry, it is not commonplace in other industries like videogames, and most + consumer file formats are generally expected to contain not-premultiplied + colors. For example, Photoshop saves PNG files "unpremultiplied", and web + browsers like Chrome and Firefox expect PNG images to be unpremultiplied. + + Note that there are three possibilities that might describe your image + and resize expectation: + + 1. images are not premultiplied, alpha weighting is desired + 2. images are not premultiplied, alpha weighting is not desired + 3. images are premultiplied + + Both case #2 and case #3 require the exact same math: no alpha weighting + should be applied or removed. Only case 1 requires extra math operations; + the other two cases can be handled identically. + + stb_image_resize expects case #1 by default, applying alpha weighting to + images, expecting the input images to be unpremultiplied. This is what the + COLOR+ALPHA buffer types tell the resizer to do. + + When you use the pixel layouts STBIR_RGBA, STBIR_BGRA, STBIR_ARGB, + STBIR_ABGR, STBIR_RX, or STBIR_XR you are telling us that the pixels are + non-premultiplied. In these cases, the resizer will alpha weight the colors + (effectively creating the premultiplied image), do the filtering, and then + convert back to non-premult on exit. + + When you use the pixel layouts STBIR_RGBA_PM, STBIR_RGBA_PM, STBIR_RGBA_PM, + STBIR_RGBA_PM, STBIR_RX_PM or STBIR_XR_PM, you are telling that the pixels + ARE premultiplied. In this case, the resizer doesn't have to do the + premultipling - it can filter directly on the input. This about twice as + fast as the non-premultiplied case, so it's the right option if your data is + already setup correctly. + + When you use the pixel layout STBIR_4CHANNEL or STBIR_2CHANNEL, you are + telling us that there is no channel that represents transparency; it may be + RGB and some unrelated fourth channel that has been stored in the alpha + channel, but it is actually not alpha. No special processing will be + performed. + + The difference between the generic 4 or 2 channel layouts, and the + specialized _PM versions is with the _PM versions you are telling us that + the data *is* alpha, just don't premultiply it. That's important when + using SRGB pixel formats, we need to know where the alpha is, because + it is converted linearly (rather than with the SRGB converters). + + Because alpha weighting produces the same effect as premultiplying, you + even have the option with non-premultiplied inputs to let the resizer + produce a premultiplied output. Because the intially computed alpha-weighted + output image is effectively premultiplied, this is actually more performant + than the normal path which un-premultiplies the output image as a final step. + + Finally, when converting both in and out of non-premulitplied space (for + example, when using STBIR_RGBA), we go to somewhat heroic measures to + ensure that areas with zero alpha value pixels get something reasonable + in the RGB values. If you don't care about the RGB values of zero alpha + pixels, you can call the stbir_set_non_pm_alpha_speed_over_quality() + function - this runs a premultiplied resize about 25% faster. That said, + when you really care about speed, using premultiplied pixels for both in + and out (STBIR_RGBA_PM, etc) much faster than both of these premultiplied + options. + + PIXEL LAYOUT CONVERSION + The resizer can convert from some pixel layouts to others. When using the + stbir_set_pixel_layouts(), you can, for example, specify STBIR_RGBA + on input, and STBIR_ARGB on output, and it will re-organize the channels + during the resize. Currently, you can only convert between two pixel + layouts with the same number of channels. + + DETERMINISM + We commit to being deterministic (from x64 to ARM to scalar to SIMD, etc). + This requires compiling with fast-math off (using at least /fp:precise). + Also, you must turn off fp-contracting (which turns mult+adds into fmas)! + We attempt to do this with pragmas, but with Clang, you usually want to add + -ffp-contract=off to the command line as well. + + For 32-bit x86, you must use SSE and SSE2 codegen for determinism. That is, + if the scalar x87 unit gets used at all, we immediately lose determinism. + On Microsoft Visual Studio 2008 and earlier, from what we can tell there is + no way to be deterministic in 32-bit x86 (some x87 always leaks in, even + with fp:strict). On 32-bit x86 GCC, determinism requires both -msse2 and + -fpmath=sse. + + Note that we will not be deterministic with float data containing NaNs - + the NaNs will propagate differently on different SIMD and platforms. + + If you turn on STBIR_USE_FMA, then we will be deterministic with other + fma targets, but we will differ from non-fma targets (this is unavoidable, + because a fma isn't simply an add with a mult - it also introduces a + rounding difference compared to non-fma instruction sequences. + + FLOAT PIXEL FORMAT RANGE + Any range of values can be used for the non-alpha float data that you pass + in (0 to 1, -1 to 1, whatever). However, if you are inputting float values + but *outputting* bytes or shorts, you must use a range of 0 to 1 so that we + scale back properly. The alpha channel must also be 0 to 1 for any format + that does premultiplication prior to resizing. + + Note also that with float output, using filters with negative lobes, the + output filtered values might go slightly out of range. You can define + STBIR_FLOAT_LOW_CLAMP and/or STBIR_FLOAT_HIGH_CLAMP to specify the range + to clamp to on output, if that's important. + + MAX/MIN SCALE FACTORS + The input pixel resolutions are in integers, and we do the internal pointer + resolution in size_t sized integers. However, the scale ratio from input + resolution to output resolution is calculated in float form. This means + the effective possible scale ratio is limited to 24 bits (or 16 million + to 1). As you get close to the size of the float resolution (again, 16 + million pixels wide or high), you might start seeing float inaccuracy + issues in general in the pipeline. If you have to do extreme resizes, + you can usually do this is multiple stages (using float intermediate + buffers). + + FLIPPED IMAGES + Stride is just the delta from one scanline to the next. This means you can + use a negative stride to handle inverted images (point to the final + scanline and use a negative stride). You can invert the input or output, + using negative strides. + + DEFAULT FILTERS + For functions which don't provide explicit control over what filters to + use, you can change the compile-time defaults with: + + #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something + #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something + + See stbir_filter in the header-file section for the list of filters. + + NEW FILTERS + A number of 1D filter kernels are supplied. For a list of supported + filters, see the stbir_filter enum. You can install your own filters by + using the stbir_set_filter_callbacks function. + + PROGRESS + For interactive use with slow resize operations, you can use the the + scanline callbacks in the extended API. It would have to be a *very* large + image resample to need progress though - we're very fast. + + CEIL and FLOOR + In scalar mode, the only functions we use from math.h are ceilf and floorf, + but if you have your own versions, you can define the STBIR_CEILF(v) and + STBIR_FLOORF(v) macros and we'll use them instead. In SIMD, we just use + our own versions. + + ASSERT + Define STBIR_ASSERT(boolval) to override assert() and not use assert.h + + FUTURE TODOS + * For polyphase integral filters, we just memcpy the coeffs to dupe + them, but we should indirect and use the same coeff memory. + * Add pixel layout conversions for sensible different channel counts + (maybe, 1->3/4, 3->4, 4->1, 3->1). + * For SIMD encode and decode scanline routines, do any pre-aligning + for bad input/output buffer alignments and pitch? + * For very wide scanlines, we should we do vertical strips to stay within + L2 cache. Maybe do chunks of 1K pixels at a time. There would be + some pixel reconversion, but probably dwarfed by things falling out + of cache. Probably also something possible with alternating between + scattering and gathering at high resize scales? + * Rewrite the coefficient generator to do many at once. + * AVX-512 vertical kernels - worried about downclocking here. + * Convert the reincludes to macros when we know they aren't changing. + * Experiment with pivoting the horizontal and always using the + vertical filters (which are faster, but perhaps not enough to overcome + the pivot cost and the extra memory touches). Need to buffer the whole + image so have to balance memory use. + * Most of our code is internally function pointers, should we compile + all the SIMD stuff always and dynamically dispatch? + + CONTRIBUTORS + Jeff Roberts: 2.0 implementation, optimizations, SIMD + Martins Mozeiko: NEON simd, WASM simd, clang and GCC whisperer. + Fabian Giesen: half float and srgb converters + Sean Barrett: API design, optimizations + Jorge L Rodriguez: Original 1.0 implementation + Aras Pranckevicius: bugfixes for 1.0 + Nathan Reed: warning fixes for 1.0 + + REVISIONS + 2.04 (2023-11-17) Fix for rare AVX bug, shadowed symbol (thanks Nikola Smiljanic). + 2.03 (2023-11-01) ASAN and TSAN warnings fixed, minor tweaks. + 2.00 (2023-10-10) mostly new source: new api, optimizations, simd, vertical-first, etc + (2x-5x faster without simd, 4x-12x faster with simd) + (in some cases, 20x to 40x faster - resizing to very small for example) + 0.96 (2019-03-04) fixed warnings + 0.95 (2017-07-23) fixed warnings + 0.94 (2017-03-18) fixed warnings + 0.93 (2017-03-03) fixed bug with certain combinations of heights + 0.92 (2017-01-02) fix integer overflow on large (>2GB) images + 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions + 0.90 (2014-09-17) first released version + + LICENSE + See end of file for license information. +*/ + +#if !defined(STB_IMAGE_RESIZE_DO_HORIZONTALS) && !defined(STB_IMAGE_RESIZE_DO_VERTICALS) && !defined(STB_IMAGE_RESIZE_DO_CODERS) // for internal re-includes + +#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE2_H +#define STBIR_INCLUDE_STB_IMAGE_RESIZE2_H + +#include +#ifdef _MSC_VER +typedef unsigned char stbir_uint8; +typedef unsigned short stbir_uint16; +typedef unsigned int stbir_uint32; +typedef unsigned __int64 stbir_uint64; +#else +#include +typedef uint8_t stbir_uint8; +typedef uint16_t stbir_uint16; +typedef uint32_t stbir_uint32; +typedef uint64_t stbir_uint64; +#endif + +#ifdef _M_IX86_FP +#if ( _M_IX86_FP >= 1 ) +#ifndef STBIR_SSE +#define STBIR_SSE +#endif +#endif +#endif + +#if defined(_x86_64) || defined( __x86_64__ ) || defined( _M_X64 ) || defined(__x86_64) || defined(_M_AMD64) || defined(__SSE2__) || defined(STBIR_SSE) || defined(STBIR_SSE2) + #ifndef STBIR_SSE2 + #define STBIR_SSE2 + #endif + #if defined(__AVX__) || defined(STBIR_AVX2) + #ifndef STBIR_AVX + #ifndef STBIR_NO_AVX + #define STBIR_AVX + #endif + #endif + #endif + #if defined(__AVX2__) || defined(STBIR_AVX2) + #ifndef STBIR_NO_AVX2 + #ifndef STBIR_AVX2 + #define STBIR_AVX2 + #endif + #if defined( _MSC_VER ) && !defined(__clang__) + #ifndef STBIR_FP16C // FP16C instructions are on all AVX2 cpus, so we can autoselect it here on microsoft - clang needs -m16c + #define STBIR_FP16C + #endif + #endif + #endif + #endif + #ifdef __F16C__ + #ifndef STBIR_FP16C // turn on FP16C instructions if the define is set (for clang and gcc) + #define STBIR_FP16C + #endif + #endif +#endif + +#if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) || defined(_M_ARM) || (__ARM_NEON_FP & 4) != 0 && __ARM_FP16_FORMAT_IEEE != 0 +#ifndef STBIR_NEON +#define STBIR_NEON +#endif +#endif + +#if defined(_M_ARM) +#ifdef STBIR_USE_FMA +#undef STBIR_USE_FMA // no FMA for 32-bit arm on MSVC +#endif +#endif + +#if defined(__wasm__) && defined(__wasm_simd128__) +#ifndef STBIR_WASM +#define STBIR_WASM +#endif +#endif + +#ifndef STBIRDEF +#ifdef STB_IMAGE_RESIZE_STATIC +#define STBIRDEF static +#else +#ifdef __cplusplus +#define STBIRDEF extern "C" +#else +#define STBIRDEF extern +#endif +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +//// start "header file" /////////////////////////////////////////////////// +// +// Easy-to-use API: +// +// * stride is the offset between successive rows of image data +// in memory, in bytes. specify 0 for packed continuously in memory +// * colorspace is linear or sRGB as specified by function name +// * Uses the default filters +// * Uses edge mode clamped +// * returned result is 1 for success or 0 in case of an error. + + +// stbir_pixel_layout specifies: +// number of channels +// order of channels +// whether color is premultiplied by alpha +// for back compatibility, you can cast the old channel count to an stbir_pixel_layout +typedef enum +{ + STBIR_1CHANNEL = 1, + STBIR_2CHANNEL = 2, + STBIR_RGB = 3, // 3-chan, with order specified (for channel flipping) + STBIR_BGR = 0, // 3-chan, with order specified (for channel flipping) + STBIR_4CHANNEL = 5, + + STBIR_RGBA = 4, // alpha formats, where alpha is NOT premultiplied into color channels + STBIR_BGRA = 6, + STBIR_ARGB = 7, + STBIR_ABGR = 8, + STBIR_RA = 9, + STBIR_AR = 10, + + STBIR_RGBA_PM = 11, // alpha formats, where alpha is premultiplied into color channels + STBIR_BGRA_PM = 12, + STBIR_ARGB_PM = 13, + STBIR_ABGR_PM = 14, + STBIR_RA_PM = 15, + STBIR_AR_PM = 16, + + STBIR_RGBA_NO_AW = 11, // alpha formats, where NO alpha weighting is applied at all! + STBIR_BGRA_NO_AW = 12, // these are just synonyms for the _PM flags (which also do + STBIR_ARGB_NO_AW = 13, // no alpha weighting). These names just make it more clear + STBIR_ABGR_NO_AW = 14, // for some folks). + STBIR_RA_NO_AW = 15, + STBIR_AR_NO_AW = 16, + +} stbir_pixel_layout; + +//=============================================================== +// Simple-complexity API +// +// If output_pixels is NULL (0), then we will allocate the buffer and return it to you. +//-------------------------------- + +STBIRDEF unsigned char * stbir_resize_uint8_srgb( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_type ); + +STBIRDEF unsigned char * stbir_resize_uint8_linear( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_type ); + +STBIRDEF float * stbir_resize_float_linear( const float *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_type ); +//=============================================================== + +//=============================================================== +// Medium-complexity API +// +// This extends the easy-to-use API as follows: +// +// * Can specify the datatype - U8, U8_SRGB, U16, FLOAT, HALF_FLOAT +// * Edge wrap can selected explicitly +// * Filter can be selected explicitly +//-------------------------------- + +typedef enum +{ + STBIR_EDGE_CLAMP = 0, + STBIR_EDGE_REFLECT = 1, + STBIR_EDGE_WRAP = 2, // this edge mode is slower and uses more memory + STBIR_EDGE_ZERO = 3, +} stbir_edge; + +typedef enum +{ + STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses + STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios + STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering + STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque + STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline + STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 + STBIR_FILTER_POINT_SAMPLE = 6, // Simple point sampling + STBIR_FILTER_OTHER = 7, // User callback specified +} stbir_filter; + +typedef enum +{ + STBIR_TYPE_UINT8 = 0, + STBIR_TYPE_UINT8_SRGB = 1, + STBIR_TYPE_UINT8_SRGB_ALPHA = 2, // alpha channel, when present, should also be SRGB (this is very unusual) + STBIR_TYPE_UINT16 = 3, + STBIR_TYPE_FLOAT = 4, + STBIR_TYPE_HALF_FLOAT = 5 +} stbir_datatype; + +// medium api +STBIRDEF void * stbir_resize( const void *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout, stbir_datatype data_type, + stbir_edge edge, stbir_filter filter ); +//=============================================================== + + + +//=============================================================== +// Extended-complexity API +// +// This API exposes all resize functionality. +// +// * Separate filter types for each axis +// * Separate edge modes for each axis +// * Separate input and output data types +// * Can specify regions with subpixel correctness +// * Can specify alpha flags +// * Can specify a memory callback +// * Can specify a callback data type for pixel input and output +// * Can be threaded for a single resize +// * Can be used to resize many frames without recalculating the sampler info +// +// Use this API as follows: +// 1) Call the stbir_resize_init function on a local STBIR_RESIZE structure +// 2) Call any of the stbir_set functions +// 3) Optionally call stbir_build_samplers() if you are going to resample multiple times +// with the same input and output dimensions (like resizing video frames) +// 4) Resample by calling stbir_resize_extended(). +// 5) Call stbir_free_samplers() if you called stbir_build_samplers() +//-------------------------------- + + +// Types: + +// INPUT CALLBACK: this callback is used for input scanlines +typedef void const * stbir_input_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context ); + +// OUTPUT CALLBACK: this callback is used for output scanlines +typedef void stbir_output_callback( void const * output_ptr, int num_pixels, int y, void * context ); + +// callbacks for user installed filters +typedef float stbir__kernel_callback( float x, float scale, void * user_data ); // centered at zero +typedef float stbir__support_callback( float scale, void * user_data ); + +// internal structure with precomputed scaling +typedef struct stbir__info stbir__info; + +typedef struct STBIR_RESIZE // use the stbir_resize_init and stbir_override functions to set these values for future compatibility +{ + void * user_data; + void const * input_pixels; + int input_w, input_h; + double input_s0, input_t0, input_s1, input_t1; + stbir_input_callback * input_cb; + void * output_pixels; + int output_w, output_h; + int output_subx, output_suby, output_subw, output_subh; + stbir_output_callback * output_cb; + int input_stride_in_bytes; + int output_stride_in_bytes; + int splits; + int fast_alpha; + int needs_rebuild; + int called_alloc; + stbir_pixel_layout input_pixel_layout_public; + stbir_pixel_layout output_pixel_layout_public; + stbir_datatype input_data_type; + stbir_datatype output_data_type; + stbir_filter horizontal_filter, vertical_filter; + stbir_edge horizontal_edge, vertical_edge; + stbir__kernel_callback * horizontal_filter_kernel; stbir__support_callback * horizontal_filter_support; + stbir__kernel_callback * vertical_filter_kernel; stbir__support_callback * vertical_filter_support; + stbir__info * samplers; +} STBIR_RESIZE; + +// extended complexity api + + +// First off, you must ALWAYS call stbir_resize_init on your resize structure before any of the other calls! +STBIRDEF void stbir_resize_init( STBIR_RESIZE * resize, + const void *input_pixels, int input_w, int input_h, int input_stride_in_bytes, // stride can be zero + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, // stride can be zero + stbir_pixel_layout pixel_layout, stbir_datatype data_type ); + +//=============================================================== +// You can update these parameters any time after resize_init and there is no cost +//-------------------------------- + +STBIRDEF void stbir_set_datatypes( STBIR_RESIZE * resize, stbir_datatype input_type, stbir_datatype output_type ); +STBIRDEF void stbir_set_pixel_callbacks( STBIR_RESIZE * resize, stbir_input_callback * input_cb, stbir_output_callback * output_cb ); // no callbacks by default +STBIRDEF void stbir_set_user_data( STBIR_RESIZE * resize, void * user_data ); // pass back STBIR_RESIZE* by default +STBIRDEF void stbir_set_buffer_ptrs( STBIR_RESIZE * resize, const void * input_pixels, int input_stride_in_bytes, void * output_pixels, int output_stride_in_bytes ); + +//=============================================================== + + +//=============================================================== +// If you call any of these functions, you will trigger a sampler rebuild! +//-------------------------------- + +STBIRDEF int stbir_set_pixel_layouts( STBIR_RESIZE * resize, stbir_pixel_layout input_pixel_layout, stbir_pixel_layout output_pixel_layout ); // sets new buffer layouts +STBIRDEF int stbir_set_edgemodes( STBIR_RESIZE * resize, stbir_edge horizontal_edge, stbir_edge vertical_edge ); // CLAMP by default + +STBIRDEF int stbir_set_filters( STBIR_RESIZE * resize, stbir_filter horizontal_filter, stbir_filter vertical_filter ); // STBIR_DEFAULT_FILTER_UPSAMPLE/DOWNSAMPLE by default +STBIRDEF int stbir_set_filter_callbacks( STBIR_RESIZE * resize, stbir__kernel_callback * horizontal_filter, stbir__support_callback * horizontal_support, stbir__kernel_callback * vertical_filter, stbir__support_callback * vertical_support ); + +STBIRDEF int stbir_set_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ); // sets both sub-regions (full regions by default) +STBIRDEF int stbir_set_input_subrect( STBIR_RESIZE * resize, double s0, double t0, double s1, double t1 ); // sets input sub-region (full region by default) +STBIRDEF int stbir_set_output_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ); // sets output sub-region (full region by default) + +// when inputting AND outputting non-premultiplied alpha pixels, we use a slower but higher quality technique +// that fills the zero alpha pixel's RGB values with something plausible. If you don't care about areas of +// zero alpha, you can call this function to get about a 25% speed improvement for STBIR_RGBA to STBIR_RGBA +// types of resizes. +STBIRDEF int stbir_set_non_pm_alpha_speed_over_quality( STBIR_RESIZE * resize, int non_pma_alpha_speed_over_quality ); +//=============================================================== + + +//=============================================================== +// You can call build_samplers to prebuild all the internal data we need to resample. +// Then, if you call resize_extended many times with the same resize, you only pay the +// cost once. +// If you do call build_samplers, you MUST call free_samplers eventually. +//-------------------------------- + +// This builds the samplers and does one allocation +STBIRDEF int stbir_build_samplers( STBIR_RESIZE * resize ); + +// You MUST call this, if you call stbir_build_samplers or stbir_build_samplers_with_splits +STBIRDEF void stbir_free_samplers( STBIR_RESIZE * resize ); +//=============================================================== + + +// And this is the main function to perform the resize synchronously on one thread. +STBIRDEF int stbir_resize_extended( STBIR_RESIZE * resize ); + + +//=============================================================== +// Use these functions for multithreading. +// 1) You call stbir_build_samplers_with_splits first on the main thread +// 2) Then stbir_resize_with_split on each thread +// 3) stbir_free_samplers when done on the main thread +//-------------------------------- + +// This will build samplers for threading. +// You can pass in the number of threads you'd like to use (try_splits). +// It returns the number of splits (threads) that you can call it with. +/// It might be less if the image resize can't be split up that many ways. + +STBIRDEF int stbir_build_samplers_with_splits( STBIR_RESIZE * resize, int try_splits ); + +// This function does a split of the resizing (you call this fuction for each +// split, on multiple threads). A split is a piece of the output resize pixel space. + +// Note that you MUST call stbir_build_samplers_with_splits before stbir_resize_extended_split! + +// Usually, you will always call stbir_resize_split with split_start as the thread_index +// and "1" for the split_count. +// But, if you have a weird situation where you MIGHT want 8 threads, but sometimes +// only 4 threads, you can use 0,2,4,6 for the split_start's and use "2" for the +// split_count each time to turn in into a 4 thread resize. (This is unusual). + +STBIRDEF int stbir_resize_extended_split( STBIR_RESIZE * resize, int split_start, int split_count ); +//=============================================================== + + +//=============================================================== +// Pixel Callbacks info: +//-------------------------------- + +// The input callback is super flexible - it calls you with the input address +// (based on the stride and base pointer), it gives you an optional_output +// pointer that you can fill, or you can just return your own pointer into +// your own data. +// +// You can also do conversion from non-supported data types if necessary - in +// this case, you ignore the input_ptr and just use the x and y parameters to +// calculate your own input_ptr based on the size of each non-supported pixel. +// (Something like the third example below.) +// +// You can also install just an input or just an output callback by setting the +// callback that you don't want to zero. +// +// First example, progress: (getting a callback that you can monitor the progress): +// void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context ) +// { +// percentage_done = y / input_height; +// return input_ptr; // use buffer from call +// } +// +// Next example, copying: (copy from some other buffer or stream): +// void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context ) +// { +// CopyOrStreamData( optional_output, other_data_src, num_pixels * pixel_width_in_bytes ); +// return optional_output; // return the optional buffer that we filled +// } +// +// Third example, input another buffer without copying: (zero-copy from other buffer): +// void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context ) +// { +// void * pixels = ( (char*) other_image_base ) + ( y * other_image_stride ) + ( x * other_pixel_width_in_bytes ); +// return pixels; // return pointer to your data without copying +// } +// +// +// The output callback is considerably simpler - it just calls you so that you can dump +// out each scanline. You could even directly copy out to disk if you have a simple format +// like TGA or BMP. You can also convert to other output types here if you want. +// +// Simple example: +// void const * my_output( void * output_ptr, int num_pixels, int y, void * context ) +// { +// percentage_done = y / output_height; +// fwrite( output_ptr, pixel_width_in_bytes, num_pixels, output_file ); +// } +//=============================================================== + + + + +//=============================================================== +// optional built-in profiling API +//-------------------------------- + +#ifdef STBIR_PROFILE + +typedef struct STBIR_PROFILE_INFO +{ + stbir_uint64 total_clocks; + + // how many clocks spent (of total_clocks) in the various resize routines, along with a string description + // there are "resize_count" number of zones + stbir_uint64 clocks[ 8 ]; + char const ** descriptions; + + // count of clocks and descriptions + stbir_uint32 count; +} STBIR_PROFILE_INFO; + +// use after calling stbir_resize_extended (or stbir_build_samplers or stbir_build_samplers_with_splits) +STBIRDEF void stbir_resize_build_profile_info( STBIR_PROFILE_INFO * out_info, STBIR_RESIZE const * resize ); + +// use after calling stbir_resize_extended +STBIRDEF void stbir_resize_extended_profile_info( STBIR_PROFILE_INFO * out_info, STBIR_RESIZE const * resize ); + +// use after calling stbir_resize_extended_split +STBIRDEF void stbir_resize_split_profile_info( STBIR_PROFILE_INFO * out_info, STBIR_RESIZE const * resize, int split_start, int split_num ); + +//=============================================================== + +#endif + + +//// end header file ///////////////////////////////////////////////////// +#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE2_H + +#if defined(STB_IMAGE_RESIZE_IMPLEMENTATION) || defined(STB_IMAGE_RESIZE2_IMPLEMENTATION) + +#ifndef STBIR_ASSERT +#include +#define STBIR_ASSERT(x) assert(x) +#endif + +#ifndef STBIR_MALLOC +#include +#define STBIR_MALLOC(size,user_data) ((void)(user_data), malloc(size)) +#define STBIR_FREE(ptr,user_data) ((void)(user_data), free(ptr)) +// (we used the comma operator to evaluate user_data, to avoid "unused parameter" warnings) +#endif + +#ifdef _MSC_VER + +#define stbir__inline __forceinline + +#else + +#define stbir__inline __inline__ + +// Clang address sanitizer +#if defined(__has_feature) + #if __has_feature(address_sanitizer) || __has_feature(memory_sanitizer) + #ifndef STBIR__SEPARATE_ALLOCATIONS + #define STBIR__SEPARATE_ALLOCATIONS + #endif + #endif +#endif + +#endif + +// GCC and MSVC +#if defined(__SANITIZE_ADDRESS__) + #ifndef STBIR__SEPARATE_ALLOCATIONS + #define STBIR__SEPARATE_ALLOCATIONS + #endif +#endif + +// Always turn off automatic FMA use - use STBIR_USE_FMA if you want. +// Otherwise, this is a determinism disaster. +#ifndef STBIR_DONT_CHANGE_FP_CONTRACT // override in case you don't want this behavior +#if defined(_MSC_VER) && !defined(__clang__) +#if _MSC_VER > 1200 +#pragma fp_contract(off) +#endif +#elif defined(__GNUC__) && !defined(__clang__) +#pragma GCC optimize("fp-contract=off") +#else +#pragma STDC FP_CONTRACT OFF +#endif +#endif + +#ifdef _MSC_VER +#define STBIR__UNUSED(v) (void)(v) +#else +#define STBIR__UNUSED(v) (void)sizeof(v) +#endif + +#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) + + +#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE +#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM +#endif + +#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE +#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL +#endif + + +#ifndef STBIR__HEADER_FILENAME +#define STBIR__HEADER_FILENAME "stb_image_resize2.h" +#endif + +// the internal pixel layout enums are in a different order, so we can easily do range comparisons of types +// the public pixel layout is ordered in a way that if you cast num_channels (1-4) to the enum, you get something sensible +typedef enum +{ + STBIRI_1CHANNEL = 0, + STBIRI_2CHANNEL = 1, + STBIRI_RGB = 2, + STBIRI_BGR = 3, + STBIRI_4CHANNEL = 4, + + STBIRI_RGBA = 5, + STBIRI_BGRA = 6, + STBIRI_ARGB = 7, + STBIRI_ABGR = 8, + STBIRI_RA = 9, + STBIRI_AR = 10, + + STBIRI_RGBA_PM = 11, + STBIRI_BGRA_PM = 12, + STBIRI_ARGB_PM = 13, + STBIRI_ABGR_PM = 14, + STBIRI_RA_PM = 15, + STBIRI_AR_PM = 16, +} stbir_internal_pixel_layout; + +// define the public pixel layouts to not compile inside the implementation (to avoid accidental use) +#define STBIR_BGR bad_dont_use_in_implementation +#define STBIR_1CHANNEL STBIR_BGR +#define STBIR_2CHANNEL STBIR_BGR +#define STBIR_RGB STBIR_BGR +#define STBIR_RGBA STBIR_BGR +#define STBIR_4CHANNEL STBIR_BGR +#define STBIR_BGRA STBIR_BGR +#define STBIR_ARGB STBIR_BGR +#define STBIR_ABGR STBIR_BGR +#define STBIR_RA STBIR_BGR +#define STBIR_AR STBIR_BGR +#define STBIR_RGBA_PM STBIR_BGR +#define STBIR_BGRA_PM STBIR_BGR +#define STBIR_ARGB_PM STBIR_BGR +#define STBIR_ABGR_PM STBIR_BGR +#define STBIR_RA_PM STBIR_BGR +#define STBIR_AR_PM STBIR_BGR + +// must match stbir_datatype +static unsigned char stbir__type_size[] = { + 1,1,1,2,4,2 // STBIR_TYPE_UINT8,STBIR_TYPE_UINT8_SRGB,STBIR_TYPE_UINT8_SRGB_ALPHA,STBIR_TYPE_UINT16,STBIR_TYPE_FLOAT,STBIR_TYPE_HALF_FLOAT +}; + +// When gathering, the contributors are which source pixels contribute. +// When scattering, the contributors are which destination pixels are contributed to. +typedef struct +{ + int n0; // First contributing pixel + int n1; // Last contributing pixel +} stbir__contributors; + +typedef struct +{ + int lowest; // First sample index for whole filter + int highest; // Last sample index for whole filter + int widest; // widest single set of samples for an output +} stbir__filter_extent_info; + +typedef struct +{ + int n0; // First pixel of decode buffer to write to + int n1; // Last pixel of decode that will be written to + int pixel_offset_for_input; // Pixel offset into input_scanline +} stbir__span; + +typedef struct stbir__scale_info +{ + int input_full_size; + int output_sub_size; + float scale; + float inv_scale; + float pixel_shift; // starting shift in output pixel space (in pixels) + int scale_is_rational; + stbir_uint32 scale_numerator, scale_denominator; +} stbir__scale_info; + +typedef struct +{ + stbir__contributors * contributors; + float* coefficients; + stbir__contributors * gather_prescatter_contributors; + float * gather_prescatter_coefficients; + stbir__scale_info scale_info; + float support; + stbir_filter filter_enum; + stbir__kernel_callback * filter_kernel; + stbir__support_callback * filter_support; + stbir_edge edge; + int coefficient_width; + int filter_pixel_width; + int filter_pixel_margin; + int num_contributors; + int contributors_size; + int coefficients_size; + stbir__filter_extent_info extent_info; + int is_gather; // 0 = scatter, 1 = gather with scale >= 1, 2 = gather with scale < 1 + int gather_prescatter_num_contributors; + int gather_prescatter_coefficient_width; + int gather_prescatter_contributors_size; + int gather_prescatter_coefficients_size; +} stbir__sampler; + +typedef struct +{ + stbir__contributors conservative; + int edge_sizes[2]; // this can be less than filter_pixel_margin, if the filter and scaling falls off + stbir__span spans[2]; // can be two spans, if doing input subrect with clamp mode WRAP +} stbir__extents; + +typedef struct +{ +#ifdef STBIR_PROFILE + union + { + struct { stbir_uint64 total, looping, vertical, horizontal, decode, encode, alpha, unalpha; } named; + stbir_uint64 array[8]; + } profile; + stbir_uint64 * current_zone_excluded_ptr; +#endif + float* decode_buffer; + + int ring_buffer_first_scanline; + int ring_buffer_last_scanline; + int ring_buffer_begin_index; // first_scanline is at this index in the ring buffer + int start_output_y, end_output_y; + int start_input_y, end_input_y; // used in scatter only + + #ifdef STBIR__SEPARATE_ALLOCATIONS + float** ring_buffers; // one pointer for each ring buffer + #else + float* ring_buffer; // one big buffer that we index into + #endif + + float* vertical_buffer; + + char no_cache_straddle[64]; +} stbir__per_split_info; + +typedef void stbir__decode_pixels_func( float * decode, int width_times_channels, void const * input ); +typedef void stbir__alpha_weight_func( float * decode_buffer, int width_times_channels ); +typedef void stbir__horizontal_gather_channels_func( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, + stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ); +typedef void stbir__alpha_unweight_func(float * encode_buffer, int width_times_channels ); +typedef void stbir__encode_pixels_func( void * output, int width_times_channels, float const * encode ); + +struct stbir__info +{ +#ifdef STBIR_PROFILE + union + { + struct { stbir_uint64 total, build, alloc, horizontal, vertical, cleanup, pivot; } named; + stbir_uint64 array[7]; + } profile; + stbir_uint64 * current_zone_excluded_ptr; +#endif + stbir__sampler horizontal; + stbir__sampler vertical; + + void const * input_data; + void * output_data; + + int input_stride_bytes; + int output_stride_bytes; + int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) + int ring_buffer_num_entries; // Total number of entries in the ring buffer. + + stbir_datatype input_type; + stbir_datatype output_type; + + stbir_input_callback * in_pixels_cb; + void * user_data; + stbir_output_callback * out_pixels_cb; + + stbir__extents scanline_extents; + + void * alloced_mem; + stbir__per_split_info * split_info; // by default 1, but there will be N of these allocated based on the thread init you did + + stbir__decode_pixels_func * decode_pixels; + stbir__alpha_weight_func * alpha_weight; + stbir__horizontal_gather_channels_func * horizontal_gather_channels; + stbir__alpha_unweight_func * alpha_unweight; + stbir__encode_pixels_func * encode_pixels; + + int alloced_total; + int splits; // count of splits + + stbir_internal_pixel_layout input_pixel_layout_internal; + stbir_internal_pixel_layout output_pixel_layout_internal; + + int input_color_and_type; + int offset_x, offset_y; // offset within output_data + int vertical_first; + int channels; + int effective_channels; // same as channels, except on RGBA/ARGB (7), or XA/AX (3) + int alloc_ring_buffer_num_entries; // Number of entries in the ring buffer that will be allocated +}; + + +#define stbir__max_uint8_as_float 255.0f +#define stbir__max_uint16_as_float 65535.0f +#define stbir__max_uint8_as_float_inverted (1.0f/255.0f) +#define stbir__max_uint16_as_float_inverted (1.0f/65535.0f) +#define stbir__small_float ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) + +// min/max friendly +#define STBIR_CLAMP(x, xmin, xmax) do { \ + if ( (x) < (xmin) ) (x) = (xmin); \ + if ( (x) > (xmax) ) (x) = (xmax); \ +} while (0) + +static stbir__inline int stbir__min(int a, int b) +{ + return a < b ? a : b; +} + +static stbir__inline int stbir__max(int a, int b) +{ + return a > b ? a : b; +} + +static float stbir__srgb_uchar_to_linear_float[256] = { + 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, + 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, + 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, + 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, + 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, + 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, + 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, + 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, + 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, + 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, + 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, + 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, + 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, + 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, + 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, + 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, + 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, + 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, + 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, + 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, + 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, + 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, + 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, + 0.982251f, 0.991102f, 1.0f +}; + +typedef union +{ + unsigned int u; + float f; +} stbir__FP32; + +// From https://gist.github.com/rygorous/2203834 + +static const stbir_uint32 fp32_to_srgb8_tab4[104] = { + 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, + 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, + 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, + 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067, + 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5, + 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2, + 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143, + 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af, + 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240, + 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300, + 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, + 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, + 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, +}; + +static stbir__inline stbir_uint8 stbir__linear_to_srgb_uchar(float in) +{ + static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps + static const stbir__FP32 minval = { (127-13) << 23 }; + stbir_uint32 tab,bias,scale,t; + stbir__FP32 f; + + // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively. + // The tests are carefully written so that NaNs map to 0, same as in the reference + // implementation. + if (!(in > minval.f)) // written this way to catch NaNs + return 0; + if (in > almostone.f) + return 255; + + // Do the table lookup and unpack bias, scale + f.f = in; + tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20]; + bias = (tab >> 16) << 9; + scale = tab & 0xffff; + + // Grab next-highest mantissa bits and perform linear interpolation + t = (f.u >> 12) & 0xff; + return (unsigned char) ((bias + scale*t) >> 16); +} + +#ifndef STBIR_FORCE_GATHER_FILTER_SCANLINES_AMOUNT +#define STBIR_FORCE_GATHER_FILTER_SCANLINES_AMOUNT 32 // when downsampling and <= 32 scanlines of buffering, use gather. gather used down to 1/8th scaling for 25% win. +#endif + +#ifndef STBIR_FORCE_MINIMUM_SCANLINES_FOR_SPLITS +#define STBIR_FORCE_MINIMUM_SCANLINES_FOR_SPLITS 4 // when threading, what is the minimum number of scanlines for a split? +#endif + +// restrict pointers for the output pointers +#if defined( _MSC_VER ) && !defined(__clang__) + #define STBIR_STREAMOUT_PTR( star ) star __restrict + #define STBIR_NO_UNROLL( ptr ) __assume(ptr) // this oddly keeps msvc from unrolling a loop +#elif defined( __clang__ ) + #define STBIR_STREAMOUT_PTR( star ) star __restrict__ + #define STBIR_NO_UNROLL( ptr ) __asm__ (""::"r"(ptr)) +#elif defined( __GNUC__ ) + #define STBIR_STREAMOUT_PTR( star ) star __restrict__ + #define STBIR_NO_UNROLL( ptr ) __asm__ (""::"r"(ptr)) +#else + #define STBIR_STREAMOUT_PTR( star ) star + #define STBIR_NO_UNROLL( ptr ) +#endif + +#ifdef STBIR_NO_SIMD // force simd off for whatever reason + +// force simd off overrides everything else, so clear it all + +#ifdef STBIR_SSE2 +#undef STBIR_SSE2 +#endif + +#ifdef STBIR_AVX +#undef STBIR_AVX +#endif + +#ifdef STBIR_NEON +#undef STBIR_NEON +#endif + +#ifdef STBIR_AVX2 +#undef STBIR_AVX2 +#endif + +#ifdef STBIR_FP16C +#undef STBIR_FP16C +#endif + +#ifdef STBIR_WASM +#undef STBIR_WASM +#endif + +#ifdef STBIR_SIMD +#undef STBIR_SIMD +#endif + +#else // STBIR_SIMD + +#ifdef STBIR_SSE2 + #include + + #define stbir__simdf __m128 + #define stbir__simdi __m128i + + #define stbir_simdi_castf( reg ) _mm_castps_si128(reg) + #define stbir_simdf_casti( reg ) _mm_castsi128_ps(reg) + + #define stbir__simdf_load( reg, ptr ) (reg) = _mm_loadu_ps( (float const*)(ptr) ) + #define stbir__simdi_load( reg, ptr ) (reg) = _mm_loadu_si128 ( (stbir__simdi const*)(ptr) ) + #define stbir__simdf_load1( out, ptr ) (out) = _mm_load_ss( (float const*)(ptr) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdi_load1( out, ptr ) (out) = _mm_castps_si128( _mm_load_ss( (float const*)(ptr) )) + #define stbir__simdf_load1z( out, ptr ) (out) = _mm_load_ss( (float const*)(ptr) ) // top values must be zero + #define stbir__simdf_frep4( fvar ) _mm_set_ps1( fvar ) + #define stbir__simdf_load1frep4( out, fvar ) (out) = _mm_set_ps1( fvar ) + #define stbir__simdf_load2( out, ptr ) (out) = _mm_castsi128_ps( _mm_loadl_epi64( (__m128i*)(ptr)) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdf_load2z( out, ptr ) (out) = _mm_castsi128_ps( _mm_loadl_epi64( (__m128i*)(ptr)) ) // top values must be zero + #define stbir__simdf_load2hmerge( out, reg, ptr ) (out) = _mm_castpd_ps(_mm_loadh_pd( _mm_castps_pd(reg), (double*)(ptr) )) + + #define stbir__simdf_zeroP() _mm_setzero_ps() + #define stbir__simdf_zero( reg ) (reg) = _mm_setzero_ps() + + #define stbir__simdf_store( ptr, reg ) _mm_storeu_ps( (float*)(ptr), reg ) + #define stbir__simdf_store1( ptr, reg ) _mm_store_ss( (float*)(ptr), reg ) + #define stbir__simdf_store2( ptr, reg ) _mm_storel_epi64( (__m128i*)(ptr), _mm_castps_si128(reg) ) + #define stbir__simdf_store2h( ptr, reg ) _mm_storeh_pd( (double*)(ptr), _mm_castps_pd(reg) ) + + #define stbir__simdi_store( ptr, reg ) _mm_storeu_si128( (__m128i*)(ptr), reg ) + #define stbir__simdi_store1( ptr, reg ) _mm_store_ss( (float*)(ptr), _mm_castsi128_ps(reg) ) + #define stbir__simdi_store2( ptr, reg ) _mm_storel_epi64( (__m128i*)(ptr), (reg) ) + + #define stbir__prefetch( ptr ) _mm_prefetch((char*)(ptr), _MM_HINT_T0 ) + + #define stbir__simdi_expand_u8_to_u32(out0,out1,out2,out3,ireg) \ + { \ + stbir__simdi zero = _mm_setzero_si128(); \ + out2 = _mm_unpacklo_epi8( ireg, zero ); \ + out3 = _mm_unpackhi_epi8( ireg, zero ); \ + out0 = _mm_unpacklo_epi16( out2, zero ); \ + out1 = _mm_unpackhi_epi16( out2, zero ); \ + out2 = _mm_unpacklo_epi16( out3, zero ); \ + out3 = _mm_unpackhi_epi16( out3, zero ); \ + } + +#define stbir__simdi_expand_u8_to_1u32(out,ireg) \ + { \ + stbir__simdi zero = _mm_setzero_si128(); \ + out = _mm_unpacklo_epi8( ireg, zero ); \ + out = _mm_unpacklo_epi16( out, zero ); \ + } + + #define stbir__simdi_expand_u16_to_u32(out0,out1,ireg) \ + { \ + stbir__simdi zero = _mm_setzero_si128(); \ + out0 = _mm_unpacklo_epi16( ireg, zero ); \ + out1 = _mm_unpackhi_epi16( ireg, zero ); \ + } + + #define stbir__simdf_convert_float_to_i32( i, f ) (i) = _mm_cvttps_epi32(f) + #define stbir__simdf_convert_float_to_int( f ) _mm_cvtt_ss2si(f) + #define stbir__simdf_convert_float_to_uint8( f ) ((unsigned char)_mm_cvtsi128_si32(_mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(f,STBIR__CONSTF(STBIR_max_uint8_as_float)),_mm_setzero_ps())))) + #define stbir__simdf_convert_float_to_short( f ) ((unsigned short)_mm_cvtsi128_si32(_mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(f,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())))) + + #define stbir__simdi_to_int( i ) _mm_cvtsi128_si32(i) + #define stbir__simdi_convert_i32_to_float(out, ireg) (out) = _mm_cvtepi32_ps( ireg ) + #define stbir__simdf_add( out, reg0, reg1 ) (out) = _mm_add_ps( reg0, reg1 ) + #define stbir__simdf_mult( out, reg0, reg1 ) (out) = _mm_mul_ps( reg0, reg1 ) + #define stbir__simdf_mult_mem( out, reg, ptr ) (out) = _mm_mul_ps( reg, _mm_loadu_ps( (float const*)(ptr) ) ) + #define stbir__simdf_mult1_mem( out, reg, ptr ) (out) = _mm_mul_ss( reg, _mm_load_ss( (float const*)(ptr) ) ) + #define stbir__simdf_add_mem( out, reg, ptr ) (out) = _mm_add_ps( reg, _mm_loadu_ps( (float const*)(ptr) ) ) + #define stbir__simdf_add1_mem( out, reg, ptr ) (out) = _mm_add_ss( reg, _mm_load_ss( (float const*)(ptr) ) ) + + #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd + #include + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = _mm_fmadd_ps( mul1, mul2, add ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = _mm_fmadd_ss( mul1, mul2, add ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = _mm_fmadd_ps( mul, _mm_loadu_ps( (float const*)(ptr) ), add ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = _mm_fmadd_ss( mul, _mm_load_ss( (float const*)(ptr) ), add ) + #else + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = _mm_add_ps( add, _mm_mul_ps( mul1, mul2 ) ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = _mm_add_ss( add, _mm_mul_ss( mul1, mul2 ) ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = _mm_add_ps( add, _mm_mul_ps( mul, _mm_loadu_ps( (float const*)(ptr) ) ) ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = _mm_add_ss( add, _mm_mul_ss( mul, _mm_load_ss( (float const*)(ptr) ) ) ) + #endif + + #define stbir__simdf_add1( out, reg0, reg1 ) (out) = _mm_add_ss( reg0, reg1 ) + #define stbir__simdf_mult1( out, reg0, reg1 ) (out) = _mm_mul_ss( reg0, reg1 ) + + #define stbir__simdf_and( out, reg0, reg1 ) (out) = _mm_and_ps( reg0, reg1 ) + #define stbir__simdf_or( out, reg0, reg1 ) (out) = _mm_or_ps( reg0, reg1 ) + + #define stbir__simdf_min( out, reg0, reg1 ) (out) = _mm_min_ps( reg0, reg1 ) + #define stbir__simdf_max( out, reg0, reg1 ) (out) = _mm_max_ps( reg0, reg1 ) + #define stbir__simdf_min1( out, reg0, reg1 ) (out) = _mm_min_ss( reg0, reg1 ) + #define stbir__simdf_max1( out, reg0, reg1 ) (out) = _mm_max_ss( reg0, reg1 ) + + #define stbir__simdf_0123ABCDto3ABx( out, reg0, reg1 ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_shuffle_ps( reg1,reg0, (0<<0) + (1<<2) + (2<<4) + (3<<6) )), (3<<0) + (0<<2) + (1<<4) + (2<<6) ) ) + #define stbir__simdf_0123ABCDto23Ax( out, reg0, reg1 ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_shuffle_ps( reg1,reg0, (0<<0) + (1<<2) + (2<<4) + (3<<6) )), (2<<0) + (3<<2) + (0<<4) + (1<<6) ) ) + + static const stbir__simdf STBIR_zeroones = { 0.0f,1.0f,0.0f,1.0f }; + static const stbir__simdf STBIR_onezeros = { 1.0f,0.0f,1.0f,0.0f }; + #define stbir__simdf_aaa1( out, alp, ones ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_movehl_ps( ones, alp ) ), (1<<0) + (1<<2) + (1<<4) + (2<<6) ) ) + #define stbir__simdf_1aaa( out, alp, ones ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_movelh_ps( ones, alp ) ), (0<<0) + (2<<2) + (2<<4) + (2<<6) ) ) + #define stbir__simdf_a1a1( out, alp, ones) (out) = _mm_or_ps( _mm_castsi128_ps( _mm_srli_epi64( _mm_castps_si128(alp), 32 ) ), STBIR_zeroones ) + #define stbir__simdf_1a1a( out, alp, ones) (out) = _mm_or_ps( _mm_castsi128_ps( _mm_slli_epi64( _mm_castps_si128(alp), 32 ) ), STBIR_onezeros ) + + #define stbir__simdf_swiz( reg, one, two, three, four ) _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( reg ), (one<<0) + (two<<2) + (three<<4) + (four<<6) ) ) + + #define stbir__simdi_and( out, reg0, reg1 ) (out) = _mm_and_si128( reg0, reg1 ) + #define stbir__simdi_or( out, reg0, reg1 ) (out) = _mm_or_si128( reg0, reg1 ) + #define stbir__simdi_16madd( out, reg0, reg1 ) (out) = _mm_madd_epi16( reg0, reg1 ) + + #define stbir__simdf_pack_to_8bytes(out,aa,bb) \ + { \ + stbir__simdf af,bf; \ + stbir__simdi a,b; \ + af = _mm_min_ps( aa, STBIR_max_uint8_as_float ); \ + bf = _mm_min_ps( bb, STBIR_max_uint8_as_float ); \ + af = _mm_max_ps( af, _mm_setzero_ps() ); \ + bf = _mm_max_ps( bf, _mm_setzero_ps() ); \ + a = _mm_cvttps_epi32( af ); \ + b = _mm_cvttps_epi32( bf ); \ + a = _mm_packs_epi32( a, b ); \ + out = _mm_packus_epi16( a, a ); \ + } + + #define stbir__simdf_load4_transposed( o0, o1, o2, o3, ptr ) \ + stbir__simdf_load( o0, (ptr) ); \ + stbir__simdf_load( o1, (ptr)+4 ); \ + stbir__simdf_load( o2, (ptr)+8 ); \ + stbir__simdf_load( o3, (ptr)+12 ); \ + { \ + __m128 tmp0, tmp1, tmp2, tmp3; \ + tmp0 = _mm_unpacklo_ps(o0, o1); \ + tmp2 = _mm_unpacklo_ps(o2, o3); \ + tmp1 = _mm_unpackhi_ps(o0, o1); \ + tmp3 = _mm_unpackhi_ps(o2, o3); \ + o0 = _mm_movelh_ps(tmp0, tmp2); \ + o1 = _mm_movehl_ps(tmp2, tmp0); \ + o2 = _mm_movelh_ps(tmp1, tmp3); \ + o3 = _mm_movehl_ps(tmp3, tmp1); \ + } + + #define stbir__interleave_pack_and_store_16_u8( ptr, r0, r1, r2, r3 ) \ + r0 = _mm_packs_epi32( r0, r1 ); \ + r2 = _mm_packs_epi32( r2, r3 ); \ + r1 = _mm_unpacklo_epi16( r0, r2 ); \ + r3 = _mm_unpackhi_epi16( r0, r2 ); \ + r0 = _mm_unpacklo_epi16( r1, r3 ); \ + r2 = _mm_unpackhi_epi16( r1, r3 ); \ + r0 = _mm_packus_epi16( r0, r2 ); \ + stbir__simdi_store( ptr, r0 ); \ + + #define stbir__simdi_32shr( out, reg, imm ) out = _mm_srli_epi32( reg, imm ) + + #if defined(_MSC_VER) && !defined(__clang__) + // msvc inits with 8 bytes + #define STBIR__CONST_32_TO_8( v ) (char)(unsigned char)((v)&255),(char)(unsigned char)(((v)>>8)&255),(char)(unsigned char)(((v)>>16)&255),(char)(unsigned char)(((v)>>24)&255) + #define STBIR__CONST_4_32i( v ) STBIR__CONST_32_TO_8( v ), STBIR__CONST_32_TO_8( v ), STBIR__CONST_32_TO_8( v ), STBIR__CONST_32_TO_8( v ) + #define STBIR__CONST_4d_32i( v0, v1, v2, v3 ) STBIR__CONST_32_TO_8( v0 ), STBIR__CONST_32_TO_8( v1 ), STBIR__CONST_32_TO_8( v2 ), STBIR__CONST_32_TO_8( v3 ) + #else + // everything else inits with long long's + #define STBIR__CONST_4_32i( v ) (long long)((((stbir_uint64)(stbir_uint32)(v))<<32)|((stbir_uint64)(stbir_uint32)(v))),(long long)((((stbir_uint64)(stbir_uint32)(v))<<32)|((stbir_uint64)(stbir_uint32)(v))) + #define STBIR__CONST_4d_32i( v0, v1, v2, v3 ) (long long)((((stbir_uint64)(stbir_uint32)(v1))<<32)|((stbir_uint64)(stbir_uint32)(v0))),(long long)((((stbir_uint64)(stbir_uint32)(v3))<<32)|((stbir_uint64)(stbir_uint32)(v2))) + #endif + + #define STBIR__SIMDF_CONST(var, x) stbir__simdf var = { x, x, x, x } + #define STBIR__SIMDI_CONST(var, x) stbir__simdi var = { STBIR__CONST_4_32i(x) } + #define STBIR__CONSTF(var) (var) + #define STBIR__CONSTI(var) (var) + + #if defined(STBIR_AVX) || defined(__SSE4_1__) + #include + #define stbir__simdf_pack_to_8words(out,reg0,reg1) out = _mm_packus_epi32(_mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg0,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())), _mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg1,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps()))) + #else + STBIR__SIMDI_CONST(stbir__s32_32768, 32768); + STBIR__SIMDI_CONST(stbir__s16_32768, ((32768<<16)|32768)); + + #define stbir__simdf_pack_to_8words(out,reg0,reg1) \ + { \ + stbir__simdi tmp0,tmp1; \ + tmp0 = _mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg0,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())); \ + tmp1 = _mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg1,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())); \ + tmp0 = _mm_sub_epi32( tmp0, stbir__s32_32768 ); \ + tmp1 = _mm_sub_epi32( tmp1, stbir__s32_32768 ); \ + out = _mm_packs_epi32( tmp0, tmp1 ); \ + out = _mm_sub_epi16( out, stbir__s16_32768 ); \ + } + + #endif + + #define STBIR_SIMD + + // if we detect AVX, set the simd8 defines + #ifdef STBIR_AVX + #include + #define STBIR_SIMD8 + #define stbir__simdf8 __m256 + #define stbir__simdi8 __m256i + #define stbir__simdf8_load( out, ptr ) (out) = _mm256_loadu_ps( (float const *)(ptr) ) + #define stbir__simdi8_load( out, ptr ) (out) = _mm256_loadu_si256( (__m256i const *)(ptr) ) + #define stbir__simdf8_mult( out, a, b ) (out) = _mm256_mul_ps( (a), (b) ) + #define stbir__simdf8_store( ptr, out ) _mm256_storeu_ps( (float*)(ptr), out ) + #define stbir__simdi8_store( ptr, reg ) _mm256_storeu_si256( (__m256i*)(ptr), reg ) + #define stbir__simdf8_frep8( fval ) _mm256_set1_ps( fval ) + + #define stbir__simdf8_min( out, reg0, reg1 ) (out) = _mm256_min_ps( reg0, reg1 ) + #define stbir__simdf8_max( out, reg0, reg1 ) (out) = _mm256_max_ps( reg0, reg1 ) + + #define stbir__simdf8_add4halves( out, bot4, top8 ) (out) = _mm_add_ps( bot4, _mm256_extractf128_ps( top8, 1 ) ) + #define stbir__simdf8_mult_mem( out, reg, ptr ) (out) = _mm256_mul_ps( reg, _mm256_loadu_ps( (float const*)(ptr) ) ) + #define stbir__simdf8_add_mem( out, reg, ptr ) (out) = _mm256_add_ps( reg, _mm256_loadu_ps( (float const*)(ptr) ) ) + #define stbir__simdf8_add( out, a, b ) (out) = _mm256_add_ps( a, b ) + #define stbir__simdf8_load1b( out, ptr ) (out) = _mm256_broadcast_ss( ptr ) + #define stbir__simdf_load1rep4( out, ptr ) (out) = _mm_broadcast_ss( ptr ) // avx load instruction + + #define stbir__simdi8_convert_i32_to_float(out, ireg) (out) = _mm256_cvtepi32_ps( ireg ) + #define stbir__simdf8_convert_float_to_i32( i, f ) (i) = _mm256_cvttps_epi32(f) + + #define stbir__simdf8_bot4s( out, a, b ) (out) = _mm256_permute2f128_ps(a,b, (0<<0)+(2<<4) ) + #define stbir__simdf8_top4s( out, a, b ) (out) = _mm256_permute2f128_ps(a,b, (1<<0)+(3<<4) ) + + #define stbir__simdf8_gettop4( reg ) _mm256_extractf128_ps(reg,1) + + #ifdef STBIR_AVX2 + + #define stbir__simdi8_expand_u8_to_u32(out0,out1,ireg) \ + { \ + stbir__simdi8 a, zero =_mm256_setzero_si256();\ + a = _mm256_permute4x64_epi64( _mm256_unpacklo_epi8( _mm256_permute4x64_epi64(_mm256_castsi128_si256(ireg),(0<<0)+(2<<2)+(1<<4)+(3<<6)), zero ),(0<<0)+(2<<2)+(1<<4)+(3<<6)); \ + out0 = _mm256_unpacklo_epi16( a, zero ); \ + out1 = _mm256_unpackhi_epi16( a, zero ); \ + } + + #define stbir__simdf8_pack_to_16bytes(out,aa,bb) \ + { \ + stbir__simdi8 t; \ + stbir__simdf8 af,bf; \ + stbir__simdi8 a,b; \ + af = _mm256_min_ps( aa, STBIR_max_uint8_as_floatX ); \ + bf = _mm256_min_ps( bb, STBIR_max_uint8_as_floatX ); \ + af = _mm256_max_ps( af, _mm256_setzero_ps() ); \ + bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \ + a = _mm256_cvttps_epi32( af ); \ + b = _mm256_cvttps_epi32( bf ); \ + t = _mm256_permute4x64_epi64( _mm256_packs_epi32( a, b ), (0<<0)+(2<<2)+(1<<4)+(3<<6) ); \ + out = _mm256_castsi256_si128( _mm256_permute4x64_epi64( _mm256_packus_epi16( t, t ), (0<<0)+(2<<2)+(1<<4)+(3<<6) ) ); \ + } + + #define stbir__simdi8_expand_u16_to_u32(out,ireg) out = _mm256_unpacklo_epi16( _mm256_permute4x64_epi64(_mm256_castsi128_si256(ireg),(0<<0)+(2<<2)+(1<<4)+(3<<6)), _mm256_setzero_si256() ); + + #define stbir__simdf8_pack_to_16words(out,aa,bb) \ + { \ + stbir__simdf8 af,bf; \ + stbir__simdi8 a,b; \ + af = _mm256_min_ps( aa, STBIR_max_uint16_as_floatX ); \ + bf = _mm256_min_ps( bb, STBIR_max_uint16_as_floatX ); \ + af = _mm256_max_ps( af, _mm256_setzero_ps() ); \ + bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \ + a = _mm256_cvttps_epi32( af ); \ + b = _mm256_cvttps_epi32( bf ); \ + (out) = _mm256_permute4x64_epi64( _mm256_packus_epi32(a, b), (0<<0)+(2<<2)+(1<<4)+(3<<6) ); \ + } + + #else + + #define stbir__simdi8_expand_u8_to_u32(out0,out1,ireg) \ + { \ + stbir__simdi a,zero = _mm_setzero_si128(); \ + a = _mm_unpacklo_epi8( ireg, zero ); \ + out0 = _mm256_setr_m128i( _mm_unpacklo_epi16( a, zero ), _mm_unpackhi_epi16( a, zero ) ); \ + a = _mm_unpackhi_epi8( ireg, zero ); \ + out1 = _mm256_setr_m128i( _mm_unpacklo_epi16( a, zero ), _mm_unpackhi_epi16( a, zero ) ); \ + } + + #define stbir__simdf8_pack_to_16bytes(out,aa,bb) \ + { \ + stbir__simdi t; \ + stbir__simdf8 af,bf; \ + stbir__simdi8 a,b; \ + af = _mm256_min_ps( aa, STBIR_max_uint8_as_floatX ); \ + bf = _mm256_min_ps( bb, STBIR_max_uint8_as_floatX ); \ + af = _mm256_max_ps( af, _mm256_setzero_ps() ); \ + bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \ + a = _mm256_cvttps_epi32( af ); \ + b = _mm256_cvttps_epi32( bf ); \ + out = _mm_packs_epi32( _mm256_castsi256_si128(a), _mm256_extractf128_si256( a, 1 ) ); \ + out = _mm_packus_epi16( out, out ); \ + t = _mm_packs_epi32( _mm256_castsi256_si128(b), _mm256_extractf128_si256( b, 1 ) ); \ + t = _mm_packus_epi16( t, t ); \ + out = _mm_castps_si128( _mm_shuffle_ps( _mm_castsi128_ps(out), _mm_castsi128_ps(t), (0<<0)+(1<<2)+(0<<4)+(1<<6) ) ); \ + } + + #define stbir__simdi8_expand_u16_to_u32(out,ireg) \ + { \ + stbir__simdi a,b,zero = _mm_setzero_si128(); \ + a = _mm_unpacklo_epi16( ireg, zero ); \ + b = _mm_unpackhi_epi16( ireg, zero ); \ + out = _mm256_insertf128_si256( _mm256_castsi128_si256( a ), b, 1 ); \ + } + + #define stbir__simdf8_pack_to_16words(out,aa,bb) \ + { \ + stbir__simdi t0,t1; \ + stbir__simdf8 af,bf; \ + stbir__simdi8 a,b; \ + af = _mm256_min_ps( aa, STBIR_max_uint16_as_floatX ); \ + bf = _mm256_min_ps( bb, STBIR_max_uint16_as_floatX ); \ + af = _mm256_max_ps( af, _mm256_setzero_ps() ); \ + bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \ + a = _mm256_cvttps_epi32( af ); \ + b = _mm256_cvttps_epi32( bf ); \ + t0 = _mm_packus_epi32( _mm256_castsi256_si128(a), _mm256_extractf128_si256( a, 1 ) ); \ + t1 = _mm_packus_epi32( _mm256_castsi256_si128(b), _mm256_extractf128_si256( b, 1 ) ); \ + out = _mm256_setr_m128i( t0, t1 ); \ + } + + #endif + + static __m256i stbir_00001111 = { STBIR__CONST_4d_32i( 0, 0, 0, 0 ), STBIR__CONST_4d_32i( 1, 1, 1, 1 ) }; + #define stbir__simdf8_0123to00001111( out, in ) (out) = _mm256_permutevar_ps ( in, stbir_00001111 ) + + static __m256i stbir_22223333 = { STBIR__CONST_4d_32i( 2, 2, 2, 2 ), STBIR__CONST_4d_32i( 3, 3, 3, 3 ) }; + #define stbir__simdf8_0123to22223333( out, in ) (out) = _mm256_permutevar_ps ( in, stbir_22223333 ) + + #define stbir__simdf8_0123to2222( out, in ) (out) = stbir__simdf_swiz(_mm256_castps256_ps128(in), 2,2,2,2 ) + + #define stbir__simdf8_load4b( out, ptr ) (out) = _mm256_broadcast_ps( (__m128 const *)(ptr) ) + + static __m256i stbir_00112233 = { STBIR__CONST_4d_32i( 0, 0, 1, 1 ), STBIR__CONST_4d_32i( 2, 2, 3, 3 ) }; + #define stbir__simdf8_0123to00112233( out, in ) (out) = _mm256_permutevar_ps ( in, stbir_00112233 ) + #define stbir__simdf8_add4( out, a8, b ) (out) = _mm256_add_ps( a8, _mm256_castps128_ps256( b ) ) + + static __m256i stbir_load6 = { STBIR__CONST_4_32i( 0x80000000 ), STBIR__CONST_4d_32i( 0x80000000, 0x80000000, 0, 0 ) }; + #define stbir__simdf8_load6z( out, ptr ) (out) = _mm256_maskload_ps( ptr, stbir_load6 ) + + #define stbir__simdf8_0123to00000000( out, in ) (out) = _mm256_shuffle_ps ( in, in, (0<<0)+(0<<2)+(0<<4)+(0<<6) ) + #define stbir__simdf8_0123to11111111( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(1<<2)+(1<<4)+(1<<6) ) + #define stbir__simdf8_0123to22222222( out, in ) (out) = _mm256_shuffle_ps ( in, in, (2<<0)+(2<<2)+(2<<4)+(2<<6) ) + #define stbir__simdf8_0123to33333333( out, in ) (out) = _mm256_shuffle_ps ( in, in, (3<<0)+(3<<2)+(3<<4)+(3<<6) ) + #define stbir__simdf8_0123to21032103( out, in ) (out) = _mm256_shuffle_ps ( in, in, (2<<0)+(1<<2)+(0<<4)+(3<<6) ) + #define stbir__simdf8_0123to32103210( out, in ) (out) = _mm256_shuffle_ps ( in, in, (3<<0)+(2<<2)+(1<<4)+(0<<6) ) + #define stbir__simdf8_0123to12301230( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(2<<2)+(3<<4)+(0<<6) ) + #define stbir__simdf8_0123to10321032( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(0<<2)+(3<<4)+(2<<6) ) + #define stbir__simdf8_0123to30123012( out, in ) (out) = _mm256_shuffle_ps ( in, in, (3<<0)+(0<<2)+(1<<4)+(2<<6) ) + + #define stbir__simdf8_0123to11331133( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(1<<2)+(3<<4)+(3<<6) ) + #define stbir__simdf8_0123to00220022( out, in ) (out) = _mm256_shuffle_ps ( in, in, (0<<0)+(0<<2)+(2<<4)+(2<<6) ) + + #define stbir__simdf8_aaa1( out, alp, ones ) (out) = _mm256_blend_ps( alp, ones, (1<<0)+(1<<1)+(1<<2)+(0<<3)+(1<<4)+(1<<5)+(1<<6)+(0<<7)); (out)=_mm256_shuffle_ps( out,out, (3<<0) + (3<<2) + (3<<4) + (0<<6) ) + #define stbir__simdf8_1aaa( out, alp, ones ) (out) = _mm256_blend_ps( alp, ones, (0<<0)+(1<<1)+(1<<2)+(1<<3)+(0<<4)+(1<<5)+(1<<6)+(1<<7)); (out)=_mm256_shuffle_ps( out,out, (1<<0) + (0<<2) + (0<<4) + (0<<6) ) + #define stbir__simdf8_a1a1( out, alp, ones) (out) = _mm256_blend_ps( alp, ones, (1<<0)+(0<<1)+(1<<2)+(0<<3)+(1<<4)+(0<<5)+(1<<6)+(0<<7)); (out)=_mm256_shuffle_ps( out,out, (1<<0) + (0<<2) + (3<<4) + (2<<6) ) + #define stbir__simdf8_1a1a( out, alp, ones) (out) = _mm256_blend_ps( alp, ones, (0<<0)+(1<<1)+(0<<2)+(1<<3)+(0<<4)+(1<<5)+(0<<6)+(1<<7)); (out)=_mm256_shuffle_ps( out,out, (1<<0) + (0<<2) + (3<<4) + (2<<6) ) + + #define stbir__simdf8_zero( reg ) (reg) = _mm256_setzero_ps() + + #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd + #define stbir__simdf8_madd( out, add, mul1, mul2 ) (out) = _mm256_fmadd_ps( mul1, mul2, add ) + #define stbir__simdf8_madd_mem( out, add, mul, ptr ) (out) = _mm256_fmadd_ps( mul, _mm256_loadu_ps( (float const*)(ptr) ), add ) + #define stbir__simdf8_madd_mem4( out, add, mul, ptr )(out) = _mm256_fmadd_ps( _mm256_setr_m128( mul, _mm_setzero_ps() ), _mm256_setr_m128( _mm_loadu_ps( (float const*)(ptr) ), _mm_setzero_ps() ), add ) + #else + #define stbir__simdf8_madd( out, add, mul1, mul2 ) (out) = _mm256_add_ps( add, _mm256_mul_ps( mul1, mul2 ) ) + #define stbir__simdf8_madd_mem( out, add, mul, ptr ) (out) = _mm256_add_ps( add, _mm256_mul_ps( mul, _mm256_loadu_ps( (float const*)(ptr) ) ) ) + #define stbir__simdf8_madd_mem4( out, add, mul, ptr ) (out) = _mm256_add_ps( add, _mm256_setr_m128( _mm_mul_ps( mul, _mm_loadu_ps( (float const*)(ptr) ) ), _mm_setzero_ps() ) ) + #endif + #define stbir__if_simdf8_cast_to_simdf4( val ) _mm256_castps256_ps128( val ) + + #endif + + #ifdef STBIR_FLOORF + #undef STBIR_FLOORF + #endif + #define STBIR_FLOORF stbir_simd_floorf + static stbir__inline float stbir_simd_floorf(float x) // martins floorf + { + #if defined(STBIR_AVX) || defined(__SSE4_1__) || defined(STBIR_SSE41) + __m128 t = _mm_set_ss(x); + return _mm_cvtss_f32( _mm_floor_ss(t, t) ); + #else + __m128 f = _mm_set_ss(x); + __m128 t = _mm_cvtepi32_ps(_mm_cvttps_epi32(f)); + __m128 r = _mm_add_ss(t, _mm_and_ps(_mm_cmplt_ss(f, t), _mm_set_ss(-1.0f))); + return _mm_cvtss_f32(r); + #endif + } + + #ifdef STBIR_CEILF + #undef STBIR_CEILF + #endif + #define STBIR_CEILF stbir_simd_ceilf + static stbir__inline float stbir_simd_ceilf(float x) // martins ceilf + { + #if defined(STBIR_AVX) || defined(__SSE4_1__) || defined(STBIR_SSE41) + __m128 t = _mm_set_ss(x); + return _mm_cvtss_f32( _mm_ceil_ss(t, t) ); + #else + __m128 f = _mm_set_ss(x); + __m128 t = _mm_cvtepi32_ps(_mm_cvttps_epi32(f)); + __m128 r = _mm_add_ss(t, _mm_and_ps(_mm_cmplt_ss(t, f), _mm_set_ss(1.0f))); + return _mm_cvtss_f32(r); + #endif + } + +#elif defined(STBIR_NEON) + + #include + + #define stbir__simdf float32x4_t + #define stbir__simdi uint32x4_t + + #define stbir_simdi_castf( reg ) vreinterpretq_u32_f32(reg) + #define stbir_simdf_casti( reg ) vreinterpretq_f32_u32(reg) + + #define stbir__simdf_load( reg, ptr ) (reg) = vld1q_f32( (float const*)(ptr) ) + #define stbir__simdi_load( reg, ptr ) (reg) = vld1q_u32( (uint32_t const*)(ptr) ) + #define stbir__simdf_load1( out, ptr ) (out) = vld1q_dup_f32( (float const*)(ptr) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdi_load1( out, ptr ) (out) = vld1q_dup_u32( (uint32_t const*)(ptr) ) + #define stbir__simdf_load1z( out, ptr ) (out) = vld1q_lane_f32( (float const*)(ptr), vdupq_n_f32(0), 0 ) // top values must be zero + #define stbir__simdf_frep4( fvar ) vdupq_n_f32( fvar ) + #define stbir__simdf_load1frep4( out, fvar ) (out) = vdupq_n_f32( fvar ) + #define stbir__simdf_load2( out, ptr ) (out) = vcombine_f32( vld1_f32( (float const*)(ptr) ), vcreate_f32(0) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdf_load2z( out, ptr ) (out) = vcombine_f32( vld1_f32( (float const*)(ptr) ), vcreate_f32(0) ) // top values must be zero + #define stbir__simdf_load2hmerge( out, reg, ptr ) (out) = vcombine_f32( vget_low_f32(reg), vld1_f32( (float const*)(ptr) ) ) + + #define stbir__simdf_zeroP() vdupq_n_f32(0) + #define stbir__simdf_zero( reg ) (reg) = vdupq_n_f32(0) + + #define stbir__simdf_store( ptr, reg ) vst1q_f32( (float*)(ptr), reg ) + #define stbir__simdf_store1( ptr, reg ) vst1q_lane_f32( (float*)(ptr), reg, 0) + #define stbir__simdf_store2( ptr, reg ) vst1_f32( (float*)(ptr), vget_low_f32(reg) ) + #define stbir__simdf_store2h( ptr, reg ) vst1_f32( (float*)(ptr), vget_high_f32(reg) ) + + #define stbir__simdi_store( ptr, reg ) vst1q_u32( (uint32_t*)(ptr), reg ) + #define stbir__simdi_store1( ptr, reg ) vst1q_lane_u32( (uint32_t*)(ptr), reg, 0 ) + #define stbir__simdi_store2( ptr, reg ) vst1_u32( (uint32_t*)(ptr), vget_low_u32(reg) ) + + #define stbir__prefetch( ptr ) + + #define stbir__simdi_expand_u8_to_u32(out0,out1,out2,out3,ireg) \ + { \ + uint16x8_t l = vmovl_u8( vget_low_u8 ( vreinterpretq_u8_u32(ireg) ) ); \ + uint16x8_t h = vmovl_u8( vget_high_u8( vreinterpretq_u8_u32(ireg) ) ); \ + out0 = vmovl_u16( vget_low_u16 ( l ) ); \ + out1 = vmovl_u16( vget_high_u16( l ) ); \ + out2 = vmovl_u16( vget_low_u16 ( h ) ); \ + out3 = vmovl_u16( vget_high_u16( h ) ); \ + } + + #define stbir__simdi_expand_u8_to_1u32(out,ireg) \ + { \ + uint16x8_t tmp = vmovl_u8( vget_low_u8( vreinterpretq_u8_u32(ireg) ) ); \ + out = vmovl_u16( vget_low_u16( tmp ) ); \ + } + + #define stbir__simdi_expand_u16_to_u32(out0,out1,ireg) \ + { \ + uint16x8_t tmp = vreinterpretq_u16_u32(ireg); \ + out0 = vmovl_u16( vget_low_u16 ( tmp ) ); \ + out1 = vmovl_u16( vget_high_u16( tmp ) ); \ + } + + #define stbir__simdf_convert_float_to_i32( i, f ) (i) = vreinterpretq_u32_s32( vcvtq_s32_f32(f) ) + #define stbir__simdf_convert_float_to_int( f ) vgetq_lane_s32(vcvtq_s32_f32(f), 0) + #define stbir__simdi_to_int( i ) (int)vgetq_lane_u32(i, 0) + #define stbir__simdf_convert_float_to_uint8( f ) ((unsigned char)vgetq_lane_s32(vcvtq_s32_f32(vmaxq_f32(vminq_f32(f,STBIR__CONSTF(STBIR_max_uint8_as_float)),vdupq_n_f32(0))), 0)) + #define stbir__simdf_convert_float_to_short( f ) ((unsigned short)vgetq_lane_s32(vcvtq_s32_f32(vmaxq_f32(vminq_f32(f,STBIR__CONSTF(STBIR_max_uint16_as_float)),vdupq_n_f32(0))), 0)) + #define stbir__simdi_convert_i32_to_float(out, ireg) (out) = vcvtq_f32_s32( vreinterpretq_s32_u32(ireg) ) + #define stbir__simdf_add( out, reg0, reg1 ) (out) = vaddq_f32( reg0, reg1 ) + #define stbir__simdf_mult( out, reg0, reg1 ) (out) = vmulq_f32( reg0, reg1 ) + #define stbir__simdf_mult_mem( out, reg, ptr ) (out) = vmulq_f32( reg, vld1q_f32( (float const*)(ptr) ) ) + #define stbir__simdf_mult1_mem( out, reg, ptr ) (out) = vmulq_f32( reg, vld1q_dup_f32( (float const*)(ptr) ) ) + #define stbir__simdf_add_mem( out, reg, ptr ) (out) = vaddq_f32( reg, vld1q_f32( (float const*)(ptr) ) ) + #define stbir__simdf_add1_mem( out, reg, ptr ) (out) = vaddq_f32( reg, vld1q_dup_f32( (float const*)(ptr) ) ) + + #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd (and also x64 no madd to arm madd) + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = vfmaq_f32( add, mul1, mul2 ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = vfmaq_f32( add, mul1, mul2 ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = vfmaq_f32( add, mul, vld1q_f32( (float const*)(ptr) ) ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = vfmaq_f32( add, mul, vld1q_dup_f32( (float const*)(ptr) ) ) + #else + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = vaddq_f32( add, vmulq_f32( mul1, mul2 ) ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = vaddq_f32( add, vmulq_f32( mul1, mul2 ) ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = vaddq_f32( add, vmulq_f32( mul, vld1q_f32( (float const*)(ptr) ) ) ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = vaddq_f32( add, vmulq_f32( mul, vld1q_dup_f32( (float const*)(ptr) ) ) ) + #endif + + #define stbir__simdf_add1( out, reg0, reg1 ) (out) = vaddq_f32( reg0, reg1 ) + #define stbir__simdf_mult1( out, reg0, reg1 ) (out) = vmulq_f32( reg0, reg1 ) + + #define stbir__simdf_and( out, reg0, reg1 ) (out) = vreinterpretq_f32_u32( vandq_u32( vreinterpretq_u32_f32(reg0), vreinterpretq_u32_f32(reg1) ) ) + #define stbir__simdf_or( out, reg0, reg1 ) (out) = vreinterpretq_f32_u32( vorrq_u32( vreinterpretq_u32_f32(reg0), vreinterpretq_u32_f32(reg1) ) ) + + #define stbir__simdf_min( out, reg0, reg1 ) (out) = vminq_f32( reg0, reg1 ) + #define stbir__simdf_max( out, reg0, reg1 ) (out) = vmaxq_f32( reg0, reg1 ) + #define stbir__simdf_min1( out, reg0, reg1 ) (out) = vminq_f32( reg0, reg1 ) + #define stbir__simdf_max1( out, reg0, reg1 ) (out) = vmaxq_f32( reg0, reg1 ) + + #define stbir__simdf_0123ABCDto3ABx( out, reg0, reg1 ) (out) = vextq_f32( reg0, reg1, 3 ) + #define stbir__simdf_0123ABCDto23Ax( out, reg0, reg1 ) (out) = vextq_f32( reg0, reg1, 2 ) + + #define stbir__simdf_a1a1( out, alp, ones ) (out) = vzipq_f32(vuzpq_f32(alp, alp).val[1], ones).val[0] + #define stbir__simdf_1a1a( out, alp, ones ) (out) = vzipq_f32(ones, vuzpq_f32(alp, alp).val[0]).val[0] + + #if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) + + #define stbir__simdf_aaa1( out, alp, ones ) (out) = vcopyq_laneq_f32(vdupq_n_f32(vgetq_lane_f32(alp, 3)), 3, ones, 3) + #define stbir__simdf_1aaa( out, alp, ones ) (out) = vcopyq_laneq_f32(vdupq_n_f32(vgetq_lane_f32(alp, 0)), 0, ones, 0) + + #if defined( _MSC_VER ) && !defined(__clang__) + #define stbir_make16(a,b,c,d) vcombine_u8( \ + vcreate_u8( (4*a+0) | ((4*a+1)<<8) | ((4*a+2)<<16) | ((4*a+3)<<24) | \ + ((stbir_uint64)(4*b+0)<<32) | ((stbir_uint64)(4*b+1)<<40) | ((stbir_uint64)(4*b+2)<<48) | ((stbir_uint64)(4*b+3)<<56)), \ + vcreate_u8( (4*c+0) | ((4*c+1)<<8) | ((4*c+2)<<16) | ((4*c+3)<<24) | \ + ((stbir_uint64)(4*d+0)<<32) | ((stbir_uint64)(4*d+1)<<40) | ((stbir_uint64)(4*d+2)<<48) | ((stbir_uint64)(4*d+3)<<56) ) ) + #else + #define stbir_make16(a,b,c,d) (uint8x16_t){4*a+0,4*a+1,4*a+2,4*a+3,4*b+0,4*b+1,4*b+2,4*b+3,4*c+0,4*c+1,4*c+2,4*c+3,4*d+0,4*d+1,4*d+2,4*d+3} + #endif + + #define stbir__simdf_swiz( reg, one, two, three, four ) vreinterpretq_f32_u8( vqtbl1q_u8( vreinterpretq_u8_f32(reg), stbir_make16(one, two, three, four) ) ) + + #define stbir__simdi_16madd( out, reg0, reg1 ) \ + { \ + int16x8_t r0 = vreinterpretq_s16_u32(reg0); \ + int16x8_t r1 = vreinterpretq_s16_u32(reg1); \ + int32x4_t tmp0 = vmull_s16( vget_low_s16(r0), vget_low_s16(r1) ); \ + int32x4_t tmp1 = vmull_s16( vget_high_s16(r0), vget_high_s16(r1) ); \ + (out) = vreinterpretq_u32_s32( vpaddq_s32(tmp0, tmp1) ); \ + } + + #else + + #define stbir__simdf_aaa1( out, alp, ones ) (out) = vsetq_lane_f32(1.0f, vdupq_n_f32(vgetq_lane_f32(alp, 3)), 3) + #define stbir__simdf_1aaa( out, alp, ones ) (out) = vsetq_lane_f32(1.0f, vdupq_n_f32(vgetq_lane_f32(alp, 0)), 0) + + #if defined( _MSC_VER ) && !defined(__clang__) + static stbir__inline uint8x8x2_t stbir_make8x2(float32x4_t reg) + { + uint8x8x2_t r = { { vget_low_u8(vreinterpretq_u8_f32(reg)), vget_high_u8(vreinterpretq_u8_f32(reg)) } }; + return r; + } + #define stbir_make8(a,b) vcreate_u8( \ + (4*a+0) | ((4*a+1)<<8) | ((4*a+2)<<16) | ((4*a+3)<<24) | \ + ((stbir_uint64)(4*b+0)<<32) | ((stbir_uint64)(4*b+1)<<40) | ((stbir_uint64)(4*b+2)<<48) | ((stbir_uint64)(4*b+3)<<56) ) + #else + #define stbir_make8x2(reg) (uint8x8x2_t){ { vget_low_u8(vreinterpretq_u8_f32(reg)), vget_high_u8(vreinterpretq_u8_f32(reg)) } } + #define stbir_make8(a,b) (uint8x8_t){4*a+0,4*a+1,4*a+2,4*a+3,4*b+0,4*b+1,4*b+2,4*b+3} + #endif + + #define stbir__simdf_swiz( reg, one, two, three, four ) vreinterpretq_f32_u8( vcombine_u8( \ + vtbl2_u8( stbir_make8x2( reg ), stbir_make8( one, two ) ), \ + vtbl2_u8( stbir_make8x2( reg ), stbir_make8( three, four ) ) ) ) + + #define stbir__simdi_16madd( out, reg0, reg1 ) \ + { \ + int16x8_t r0 = vreinterpretq_s16_u32(reg0); \ + int16x8_t r1 = vreinterpretq_s16_u32(reg1); \ + int32x4_t tmp0 = vmull_s16( vget_low_s16(r0), vget_low_s16(r1) ); \ + int32x4_t tmp1 = vmull_s16( vget_high_s16(r0), vget_high_s16(r1) ); \ + int32x2_t out0 = vpadd_s32( vget_low_s32(tmp0), vget_high_s32(tmp0) ); \ + int32x2_t out1 = vpadd_s32( vget_low_s32(tmp1), vget_high_s32(tmp1) ); \ + (out) = vreinterpretq_u32_s32( vcombine_s32(out0, out1) ); \ + } + + #endif + + #define stbir__simdi_and( out, reg0, reg1 ) (out) = vandq_u32( reg0, reg1 ) + #define stbir__simdi_or( out, reg0, reg1 ) (out) = vorrq_u32( reg0, reg1 ) + + #define stbir__simdf_pack_to_8bytes(out,aa,bb) \ + { \ + float32x4_t af = vmaxq_f32( vminq_f32(aa,STBIR__CONSTF(STBIR_max_uint8_as_float) ), vdupq_n_f32(0) ); \ + float32x4_t bf = vmaxq_f32( vminq_f32(bb,STBIR__CONSTF(STBIR_max_uint8_as_float) ), vdupq_n_f32(0) ); \ + int16x4_t ai = vqmovn_s32( vcvtq_s32_f32( af ) ); \ + int16x4_t bi = vqmovn_s32( vcvtq_s32_f32( bf ) ); \ + uint8x8_t out8 = vqmovun_s16( vcombine_s16(ai, bi) ); \ + out = vreinterpretq_u32_u8( vcombine_u8(out8, out8) ); \ + } + + #define stbir__simdf_pack_to_8words(out,aa,bb) \ + { \ + float32x4_t af = vmaxq_f32( vminq_f32(aa,STBIR__CONSTF(STBIR_max_uint16_as_float) ), vdupq_n_f32(0) ); \ + float32x4_t bf = vmaxq_f32( vminq_f32(bb,STBIR__CONSTF(STBIR_max_uint16_as_float) ), vdupq_n_f32(0) ); \ + int32x4_t ai = vcvtq_s32_f32( af ); \ + int32x4_t bi = vcvtq_s32_f32( bf ); \ + out = vreinterpretq_u32_u16( vcombine_u16(vqmovun_s32(ai), vqmovun_s32(bi)) ); \ + } + + #define stbir__interleave_pack_and_store_16_u8( ptr, r0, r1, r2, r3 ) \ + { \ + int16x4x2_t tmp0 = vzip_s16( vqmovn_s32(vreinterpretq_s32_u32(r0)), vqmovn_s32(vreinterpretq_s32_u32(r2)) ); \ + int16x4x2_t tmp1 = vzip_s16( vqmovn_s32(vreinterpretq_s32_u32(r1)), vqmovn_s32(vreinterpretq_s32_u32(r3)) ); \ + uint8x8x2_t out = \ + { { \ + vqmovun_s16( vcombine_s16(tmp0.val[0], tmp0.val[1]) ), \ + vqmovun_s16( vcombine_s16(tmp1.val[0], tmp1.val[1]) ), \ + } }; \ + vst2_u8(ptr, out); \ + } + + #define stbir__simdf_load4_transposed( o0, o1, o2, o3, ptr ) \ + { \ + float32x4x4_t tmp = vld4q_f32(ptr); \ + o0 = tmp.val[0]; \ + o1 = tmp.val[1]; \ + o2 = tmp.val[2]; \ + o3 = tmp.val[3]; \ + } + + #define stbir__simdi_32shr( out, reg, imm ) out = vshrq_n_u32( reg, imm ) + + #if defined( _MSC_VER ) && !defined(__clang__) + #define STBIR__SIMDF_CONST(var, x) __declspec(align(8)) float var[] = { x, x, x, x } + #define STBIR__SIMDI_CONST(var, x) __declspec(align(8)) uint32_t var[] = { x, x, x, x } + #define STBIR__CONSTF(var) (*(const float32x4_t*)var) + #define STBIR__CONSTI(var) (*(const uint32x4_t*)var) + #else + #define STBIR__SIMDF_CONST(var, x) stbir__simdf var = { x, x, x, x } + #define STBIR__SIMDI_CONST(var, x) stbir__simdi var = { x, x, x, x } + #define STBIR__CONSTF(var) (var) + #define STBIR__CONSTI(var) (var) + #endif + + #ifdef STBIR_FLOORF + #undef STBIR_FLOORF + #endif + #define STBIR_FLOORF stbir_simd_floorf + static stbir__inline float stbir_simd_floorf(float x) + { + #if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) + return vget_lane_f32( vrndm_f32( vdup_n_f32(x) ), 0); + #else + float32x2_t f = vdup_n_f32(x); + float32x2_t t = vcvt_f32_s32(vcvt_s32_f32(f)); + uint32x2_t a = vclt_f32(f, t); + uint32x2_t b = vreinterpret_u32_f32(vdup_n_f32(-1.0f)); + float32x2_t r = vadd_f32(t, vreinterpret_f32_u32(vand_u32(a, b))); + return vget_lane_f32(r, 0); + #endif + } + + #ifdef STBIR_CEILF + #undef STBIR_CEILF + #endif + #define STBIR_CEILF stbir_simd_ceilf + static stbir__inline float stbir_simd_ceilf(float x) + { + #if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) + return vget_lane_f32( vrndp_f32( vdup_n_f32(x) ), 0); + #else + float32x2_t f = vdup_n_f32(x); + float32x2_t t = vcvt_f32_s32(vcvt_s32_f32(f)); + uint32x2_t a = vclt_f32(t, f); + uint32x2_t b = vreinterpret_u32_f32(vdup_n_f32(1.0f)); + float32x2_t r = vadd_f32(t, vreinterpret_f32_u32(vand_u32(a, b))); + return vget_lane_f32(r, 0); + #endif + } + + #define STBIR_SIMD + +#elif defined(STBIR_WASM) + + #include + + #define stbir__simdf v128_t + #define stbir__simdi v128_t + + #define stbir_simdi_castf( reg ) (reg) + #define stbir_simdf_casti( reg ) (reg) + + #define stbir__simdf_load( reg, ptr ) (reg) = wasm_v128_load( (void const*)(ptr) ) + #define stbir__simdi_load( reg, ptr ) (reg) = wasm_v128_load( (void const*)(ptr) ) + #define stbir__simdf_load1( out, ptr ) (out) = wasm_v128_load32_splat( (void const*)(ptr) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdi_load1( out, ptr ) (out) = wasm_v128_load32_splat( (void const*)(ptr) ) + #define stbir__simdf_load1z( out, ptr ) (out) = wasm_v128_load32_zero( (void const*)(ptr) ) // top values must be zero + #define stbir__simdf_frep4( fvar ) wasm_f32x4_splat( fvar ) + #define stbir__simdf_load1frep4( out, fvar ) (out) = wasm_f32x4_splat( fvar ) + #define stbir__simdf_load2( out, ptr ) (out) = wasm_v128_load64_splat( (void const*)(ptr) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdf_load2z( out, ptr ) (out) = wasm_v128_load64_zero( (void const*)(ptr) ) // top values must be zero + #define stbir__simdf_load2hmerge( out, reg, ptr ) (out) = wasm_v128_load64_lane( (void const*)(ptr), reg, 1 ) + + #define stbir__simdf_zeroP() wasm_f32x4_const_splat(0) + #define stbir__simdf_zero( reg ) (reg) = wasm_f32x4_const_splat(0) + + #define stbir__simdf_store( ptr, reg ) wasm_v128_store( (void*)(ptr), reg ) + #define stbir__simdf_store1( ptr, reg ) wasm_v128_store32_lane( (void*)(ptr), reg, 0 ) + #define stbir__simdf_store2( ptr, reg ) wasm_v128_store64_lane( (void*)(ptr), reg, 0 ) + #define stbir__simdf_store2h( ptr, reg ) wasm_v128_store64_lane( (void*)(ptr), reg, 1 ) + + #define stbir__simdi_store( ptr, reg ) wasm_v128_store( (void*)(ptr), reg ) + #define stbir__simdi_store1( ptr, reg ) wasm_v128_store32_lane( (void*)(ptr), reg, 0 ) + #define stbir__simdi_store2( ptr, reg ) wasm_v128_store64_lane( (void*)(ptr), reg, 0 ) + + #define stbir__prefetch( ptr ) + + #define stbir__simdi_expand_u8_to_u32(out0,out1,out2,out3,ireg) \ + { \ + v128_t l = wasm_u16x8_extend_low_u8x16 ( ireg ); \ + v128_t h = wasm_u16x8_extend_high_u8x16( ireg ); \ + out0 = wasm_u32x4_extend_low_u16x8 ( l ); \ + out1 = wasm_u32x4_extend_high_u16x8( l ); \ + out2 = wasm_u32x4_extend_low_u16x8 ( h ); \ + out3 = wasm_u32x4_extend_high_u16x8( h ); \ + } + + #define stbir__simdi_expand_u8_to_1u32(out,ireg) \ + { \ + v128_t tmp = wasm_u16x8_extend_low_u8x16(ireg); \ + out = wasm_u32x4_extend_low_u16x8(tmp); \ + } + + #define stbir__simdi_expand_u16_to_u32(out0,out1,ireg) \ + { \ + out0 = wasm_u32x4_extend_low_u16x8 ( ireg ); \ + out1 = wasm_u32x4_extend_high_u16x8( ireg ); \ + } + + #define stbir__simdf_convert_float_to_i32( i, f ) (i) = wasm_i32x4_trunc_sat_f32x4(f) + #define stbir__simdf_convert_float_to_int( f ) wasm_i32x4_extract_lane(wasm_i32x4_trunc_sat_f32x4(f), 0) + #define stbir__simdi_to_int( i ) wasm_i32x4_extract_lane(i, 0) + #define stbir__simdf_convert_float_to_uint8( f ) ((unsigned char)wasm_i32x4_extract_lane(wasm_i32x4_trunc_sat_f32x4(wasm_f32x4_max(wasm_f32x4_min(f,STBIR_max_uint8_as_float),wasm_f32x4_const_splat(0))), 0)) + #define stbir__simdf_convert_float_to_short( f ) ((unsigned short)wasm_i32x4_extract_lane(wasm_i32x4_trunc_sat_f32x4(wasm_f32x4_max(wasm_f32x4_min(f,STBIR_max_uint16_as_float),wasm_f32x4_const_splat(0))), 0)) + #define stbir__simdi_convert_i32_to_float(out, ireg) (out) = wasm_f32x4_convert_i32x4(ireg) + #define stbir__simdf_add( out, reg0, reg1 ) (out) = wasm_f32x4_add( reg0, reg1 ) + #define stbir__simdf_mult( out, reg0, reg1 ) (out) = wasm_f32x4_mul( reg0, reg1 ) + #define stbir__simdf_mult_mem( out, reg, ptr ) (out) = wasm_f32x4_mul( reg, wasm_v128_load( (void const*)(ptr) ) ) + #define stbir__simdf_mult1_mem( out, reg, ptr ) (out) = wasm_f32x4_mul( reg, wasm_v128_load32_splat( (void const*)(ptr) ) ) + #define stbir__simdf_add_mem( out, reg, ptr ) (out) = wasm_f32x4_add( reg, wasm_v128_load( (void const*)(ptr) ) ) + #define stbir__simdf_add1_mem( out, reg, ptr ) (out) = wasm_f32x4_add( reg, wasm_v128_load32_splat( (void const*)(ptr) ) ) + + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul1, mul2 ) ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul1, mul2 ) ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul, wasm_v128_load( (void const*)(ptr) ) ) ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul, wasm_v128_load32_splat( (void const*)(ptr) ) ) ) + + #define stbir__simdf_add1( out, reg0, reg1 ) (out) = wasm_f32x4_add( reg0, reg1 ) + #define stbir__simdf_mult1( out, reg0, reg1 ) (out) = wasm_f32x4_mul( reg0, reg1 ) + + #define stbir__simdf_and( out, reg0, reg1 ) (out) = wasm_v128_and( reg0, reg1 ) + #define stbir__simdf_or( out, reg0, reg1 ) (out) = wasm_v128_or( reg0, reg1 ) + + #define stbir__simdf_min( out, reg0, reg1 ) (out) = wasm_f32x4_min( reg0, reg1 ) + #define stbir__simdf_max( out, reg0, reg1 ) (out) = wasm_f32x4_max( reg0, reg1 ) + #define stbir__simdf_min1( out, reg0, reg1 ) (out) = wasm_f32x4_min( reg0, reg1 ) + #define stbir__simdf_max1( out, reg0, reg1 ) (out) = wasm_f32x4_max( reg0, reg1 ) + + #define stbir__simdf_0123ABCDto3ABx( out, reg0, reg1 ) (out) = wasm_i32x4_shuffle( reg0, reg1, 3, 4, 5, -1 ) + #define stbir__simdf_0123ABCDto23Ax( out, reg0, reg1 ) (out) = wasm_i32x4_shuffle( reg0, reg1, 2, 3, 4, -1 ) + + #define stbir__simdf_aaa1(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 3, 3, 3, 4) + #define stbir__simdf_1aaa(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 4, 0, 0, 0) + #define stbir__simdf_a1a1(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 1, 4, 3, 4) + #define stbir__simdf_1a1a(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 4, 0, 4, 2) + + #define stbir__simdf_swiz( reg, one, two, three, four ) wasm_i32x4_shuffle(reg, reg, one, two, three, four) + + #define stbir__simdi_and( out, reg0, reg1 ) (out) = wasm_v128_and( reg0, reg1 ) + #define stbir__simdi_or( out, reg0, reg1 ) (out) = wasm_v128_or( reg0, reg1 ) + #define stbir__simdi_16madd( out, reg0, reg1 ) (out) = wasm_i32x4_dot_i16x8( reg0, reg1 ) + + #define stbir__simdf_pack_to_8bytes(out,aa,bb) \ + { \ + v128_t af = wasm_f32x4_max( wasm_f32x4_min(aa, STBIR_max_uint8_as_float), wasm_f32x4_const_splat(0) ); \ + v128_t bf = wasm_f32x4_max( wasm_f32x4_min(bb, STBIR_max_uint8_as_float), wasm_f32x4_const_splat(0) ); \ + v128_t ai = wasm_i32x4_trunc_sat_f32x4( af ); \ + v128_t bi = wasm_i32x4_trunc_sat_f32x4( bf ); \ + v128_t out16 = wasm_i16x8_narrow_i32x4( ai, bi ); \ + out = wasm_u8x16_narrow_i16x8( out16, out16 ); \ + } + + #define stbir__simdf_pack_to_8words(out,aa,bb) \ + { \ + v128_t af = wasm_f32x4_max( wasm_f32x4_min(aa, STBIR_max_uint16_as_float), wasm_f32x4_const_splat(0)); \ + v128_t bf = wasm_f32x4_max( wasm_f32x4_min(bb, STBIR_max_uint16_as_float), wasm_f32x4_const_splat(0)); \ + v128_t ai = wasm_i32x4_trunc_sat_f32x4( af ); \ + v128_t bi = wasm_i32x4_trunc_sat_f32x4( bf ); \ + out = wasm_u16x8_narrow_i32x4( ai, bi ); \ + } + + #define stbir__interleave_pack_and_store_16_u8( ptr, r0, r1, r2, r3 ) \ + { \ + v128_t tmp0 = wasm_i16x8_narrow_i32x4(r0, r1); \ + v128_t tmp1 = wasm_i16x8_narrow_i32x4(r2, r3); \ + v128_t tmp = wasm_u8x16_narrow_i16x8(tmp0, tmp1); \ + tmp = wasm_i8x16_shuffle(tmp, tmp, 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15); \ + wasm_v128_store( (void*)(ptr), tmp); \ + } + + #define stbir__simdf_load4_transposed( o0, o1, o2, o3, ptr ) \ + { \ + v128_t t0 = wasm_v128_load( ptr ); \ + v128_t t1 = wasm_v128_load( ptr+4 ); \ + v128_t t2 = wasm_v128_load( ptr+8 ); \ + v128_t t3 = wasm_v128_load( ptr+12 ); \ + v128_t s0 = wasm_i32x4_shuffle(t0, t1, 0, 4, 2, 6); \ + v128_t s1 = wasm_i32x4_shuffle(t0, t1, 1, 5, 3, 7); \ + v128_t s2 = wasm_i32x4_shuffle(t2, t3, 0, 4, 2, 6); \ + v128_t s3 = wasm_i32x4_shuffle(t2, t3, 1, 5, 3, 7); \ + o0 = wasm_i32x4_shuffle(s0, s2, 0, 1, 4, 5); \ + o1 = wasm_i32x4_shuffle(s1, s3, 0, 1, 4, 5); \ + o2 = wasm_i32x4_shuffle(s0, s2, 2, 3, 6, 7); \ + o3 = wasm_i32x4_shuffle(s1, s3, 2, 3, 6, 7); \ + } + + #define stbir__simdi_32shr( out, reg, imm ) out = wasm_u32x4_shr( reg, imm ) + + typedef float stbir__f32x4 __attribute__((__vector_size__(16), __aligned__(16))); + #define STBIR__SIMDF_CONST(var, x) stbir__simdf var = (v128_t)(stbir__f32x4){ x, x, x, x } + #define STBIR__SIMDI_CONST(var, x) stbir__simdi var = { x, x, x, x } + #define STBIR__CONSTF(var) (var) + #define STBIR__CONSTI(var) (var) + + #ifdef STBIR_FLOORF + #undef STBIR_FLOORF + #endif + #define STBIR_FLOORF stbir_simd_floorf + static stbir__inline float stbir_simd_floorf(float x) + { + return wasm_f32x4_extract_lane( wasm_f32x4_floor( wasm_f32x4_splat(x) ), 0); + } + + #ifdef STBIR_CEILF + #undef STBIR_CEILF + #endif + #define STBIR_CEILF stbir_simd_ceilf + static stbir__inline float stbir_simd_ceilf(float x) + { + return wasm_f32x4_extract_lane( wasm_f32x4_ceil( wasm_f32x4_splat(x) ), 0); + } + + #define STBIR_SIMD + +#endif // SSE2/NEON/WASM + +#endif // NO SIMD + +#ifdef STBIR_SIMD8 + #define stbir__simdfX stbir__simdf8 + #define stbir__simdiX stbir__simdi8 + #define stbir__simdfX_load stbir__simdf8_load + #define stbir__simdiX_load stbir__simdi8_load + #define stbir__simdfX_mult stbir__simdf8_mult + #define stbir__simdfX_add_mem stbir__simdf8_add_mem + #define stbir__simdfX_madd_mem stbir__simdf8_madd_mem + #define stbir__simdfX_store stbir__simdf8_store + #define stbir__simdiX_store stbir__simdi8_store + #define stbir__simdf_frepX stbir__simdf8_frep8 + #define stbir__simdfX_madd stbir__simdf8_madd + #define stbir__simdfX_min stbir__simdf8_min + #define stbir__simdfX_max stbir__simdf8_max + #define stbir__simdfX_aaa1 stbir__simdf8_aaa1 + #define stbir__simdfX_1aaa stbir__simdf8_1aaa + #define stbir__simdfX_a1a1 stbir__simdf8_a1a1 + #define stbir__simdfX_1a1a stbir__simdf8_1a1a + #define stbir__simdfX_convert_float_to_i32 stbir__simdf8_convert_float_to_i32 + #define stbir__simdfX_pack_to_words stbir__simdf8_pack_to_16words + #define stbir__simdfX_zero stbir__simdf8_zero + #define STBIR_onesX STBIR_ones8 + #define STBIR_max_uint8_as_floatX STBIR_max_uint8_as_float8 + #define STBIR_max_uint16_as_floatX STBIR_max_uint16_as_float8 + #define STBIR_simd_point5X STBIR_simd_point58 + #define stbir__simdfX_float_count 8 + #define stbir__simdfX_0123to1230 stbir__simdf8_0123to12301230 + #define stbir__simdfX_0123to2103 stbir__simdf8_0123to21032103 + static const stbir__simdf8 STBIR_max_uint16_as_float_inverted8 = { stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted }; + static const stbir__simdf8 STBIR_max_uint8_as_float_inverted8 = { stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted }; + static const stbir__simdf8 STBIR_ones8 = { 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 }; + static const stbir__simdf8 STBIR_simd_point58 = { 0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5 }; + static const stbir__simdf8 STBIR_max_uint8_as_float8 = { stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float, stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float }; + static const stbir__simdf8 STBIR_max_uint16_as_float8 = { stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float, stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float }; +#else + #define stbir__simdfX stbir__simdf + #define stbir__simdiX stbir__simdi + #define stbir__simdfX_load stbir__simdf_load + #define stbir__simdiX_load stbir__simdi_load + #define stbir__simdfX_mult stbir__simdf_mult + #define stbir__simdfX_add_mem stbir__simdf_add_mem + #define stbir__simdfX_madd_mem stbir__simdf_madd_mem + #define stbir__simdfX_store stbir__simdf_store + #define stbir__simdiX_store stbir__simdi_store + #define stbir__simdf_frepX stbir__simdf_frep4 + #define stbir__simdfX_madd stbir__simdf_madd + #define stbir__simdfX_min stbir__simdf_min + #define stbir__simdfX_max stbir__simdf_max + #define stbir__simdfX_aaa1 stbir__simdf_aaa1 + #define stbir__simdfX_1aaa stbir__simdf_1aaa + #define stbir__simdfX_a1a1 stbir__simdf_a1a1 + #define stbir__simdfX_1a1a stbir__simdf_1a1a + #define stbir__simdfX_convert_float_to_i32 stbir__simdf_convert_float_to_i32 + #define stbir__simdfX_pack_to_words stbir__simdf_pack_to_8words + #define stbir__simdfX_zero stbir__simdf_zero + #define STBIR_onesX STBIR__CONSTF(STBIR_ones) + #define STBIR_simd_point5X STBIR__CONSTF(STBIR_simd_point5) + #define STBIR_max_uint8_as_floatX STBIR__CONSTF(STBIR_max_uint8_as_float) + #define STBIR_max_uint16_as_floatX STBIR__CONSTF(STBIR_max_uint16_as_float) + #define stbir__simdfX_float_count 4 + #define stbir__if_simdf8_cast_to_simdf4( val ) ( val ) + #define stbir__simdfX_0123to1230 stbir__simdf_0123to1230 + #define stbir__simdfX_0123to2103 stbir__simdf_0123to2103 +#endif + + +#if defined(STBIR_NEON) && !defined(_M_ARM) + + #if defined( _MSC_VER ) && !defined(__clang__) + typedef __int16 stbir__FP16; + #else + typedef float16_t stbir__FP16; + #endif + +#else // no NEON, or 32-bit ARM for MSVC + + typedef union stbir__FP16 + { + unsigned short u; + } stbir__FP16; + +#endif + +#if !defined(STBIR_NEON) && !defined(STBIR_FP16C) || defined(STBIR_NEON) && defined(_M_ARM) + + // Fabian's half float routines, see: https://gist.github.com/rygorous/2156668 + + static stbir__inline float stbir__half_to_float( stbir__FP16 h ) + { + static const stbir__FP32 magic = { (254 - 15) << 23 }; + static const stbir__FP32 was_infnan = { (127 + 16) << 23 }; + stbir__FP32 o; + + o.u = (h.u & 0x7fff) << 13; // exponent/mantissa bits + o.f *= magic.f; // exponent adjust + if (o.f >= was_infnan.f) // make sure Inf/NaN survive + o.u |= 255 << 23; + o.u |= (h.u & 0x8000) << 16; // sign bit + return o.f; + } + + static stbir__inline stbir__FP16 stbir__float_to_half(float val) + { + stbir__FP32 f32infty = { 255 << 23 }; + stbir__FP32 f16max = { (127 + 16) << 23 }; + stbir__FP32 denorm_magic = { ((127 - 15) + (23 - 10) + 1) << 23 }; + unsigned int sign_mask = 0x80000000u; + stbir__FP16 o = { 0 }; + stbir__FP32 f; + unsigned int sign; + + f.f = val; + sign = f.u & sign_mask; + f.u ^= sign; + + if (f.u >= f16max.u) // result is Inf or NaN (all exponent bits set) + o.u = (f.u > f32infty.u) ? 0x7e00 : 0x7c00; // NaN->qNaN and Inf->Inf + else // (De)normalized number or zero + { + if (f.u < (113 << 23)) // resulting FP16 is subnormal or zero + { + // use a magic value to align our 10 mantissa bits at the bottom of + // the float. as long as FP addition is round-to-nearest-even this + // just works. + f.f += denorm_magic.f; + // and one integer subtract of the bias later, we have our final float! + o.u = (unsigned short) ( f.u - denorm_magic.u ); + } + else + { + unsigned int mant_odd = (f.u >> 13) & 1; // resulting mantissa is odd + // update exponent, rounding bias part 1 + f.u = f.u + ((15u - 127) << 23) + 0xfff; + // rounding bias part 2 + f.u += mant_odd; + // take the bits! + o.u = (unsigned short) ( f.u >> 13 ); + } + } + + o.u |= sign >> 16; + return o; + } + +#endif + + +#if defined(STBIR_FP16C) + + #include + + static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) + { + _mm256_storeu_ps( (float*)output, _mm256_cvtph_ps( _mm_loadu_si128( (__m128i const* )input ) ) ); + } + + static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input) + { + _mm_storeu_si128( (__m128i*)output, _mm256_cvtps_ph( _mm256_loadu_ps( input ), 0 ) ); + } + + static stbir__inline float stbir__half_to_float( stbir__FP16 h ) + { + return _mm_cvtss_f32( _mm_cvtph_ps( _mm_cvtsi32_si128( (int)h.u ) ) ); + } + + static stbir__inline stbir__FP16 stbir__float_to_half( float f ) + { + stbir__FP16 h; + h.u = (unsigned short) _mm_cvtsi128_si32( _mm_cvtps_ph( _mm_set_ss( f ), 0 ) ); + return h; + } + +#elif defined(STBIR_SSE2) + + // Fabian's half float routines, see: https://gist.github.com/rygorous/2156668 + stbir__inline static void stbir__half_to_float_SIMD(float * output, void const * input) + { + static const STBIR__SIMDI_CONST(mask_nosign, 0x7fff); + static const STBIR__SIMDI_CONST(smallest_normal, 0x0400); + static const STBIR__SIMDI_CONST(infinity, 0x7c00); + static const STBIR__SIMDI_CONST(expadjust_normal, (127 - 15) << 23); + static const STBIR__SIMDI_CONST(magic_denorm, 113 << 23); + + __m128i i = _mm_loadu_si128 ( (__m128i const*)(input) ); + __m128i h = _mm_unpacklo_epi16 ( i, _mm_setzero_si128() ); + __m128i mnosign = STBIR__CONSTI(mask_nosign); + __m128i eadjust = STBIR__CONSTI(expadjust_normal); + __m128i smallest = STBIR__CONSTI(smallest_normal); + __m128i infty = STBIR__CONSTI(infinity); + __m128i expmant = _mm_and_si128(mnosign, h); + __m128i justsign = _mm_xor_si128(h, expmant); + __m128i b_notinfnan = _mm_cmpgt_epi32(infty, expmant); + __m128i b_isdenorm = _mm_cmpgt_epi32(smallest, expmant); + __m128i shifted = _mm_slli_epi32(expmant, 13); + __m128i adj_infnan = _mm_andnot_si128(b_notinfnan, eadjust); + __m128i adjusted = _mm_add_epi32(eadjust, shifted); + __m128i den1 = _mm_add_epi32(shifted, STBIR__CONSTI(magic_denorm)); + __m128i adjusted2 = _mm_add_epi32(adjusted, adj_infnan); + __m128 den2 = _mm_sub_ps(_mm_castsi128_ps(den1), *(const __m128 *)&magic_denorm); + __m128 adjusted3 = _mm_and_ps(den2, _mm_castsi128_ps(b_isdenorm)); + __m128 adjusted4 = _mm_andnot_ps(_mm_castsi128_ps(b_isdenorm), _mm_castsi128_ps(adjusted2)); + __m128 adjusted5 = _mm_or_ps(adjusted3, adjusted4); + __m128i sign = _mm_slli_epi32(justsign, 16); + __m128 final = _mm_or_ps(adjusted5, _mm_castsi128_ps(sign)); + stbir__simdf_store( output + 0, final ); + + h = _mm_unpackhi_epi16 ( i, _mm_setzero_si128() ); + expmant = _mm_and_si128(mnosign, h); + justsign = _mm_xor_si128(h, expmant); + b_notinfnan = _mm_cmpgt_epi32(infty, expmant); + b_isdenorm = _mm_cmpgt_epi32(smallest, expmant); + shifted = _mm_slli_epi32(expmant, 13); + adj_infnan = _mm_andnot_si128(b_notinfnan, eadjust); + adjusted = _mm_add_epi32(eadjust, shifted); + den1 = _mm_add_epi32(shifted, STBIR__CONSTI(magic_denorm)); + adjusted2 = _mm_add_epi32(adjusted, adj_infnan); + den2 = _mm_sub_ps(_mm_castsi128_ps(den1), *(const __m128 *)&magic_denorm); + adjusted3 = _mm_and_ps(den2, _mm_castsi128_ps(b_isdenorm)); + adjusted4 = _mm_andnot_ps(_mm_castsi128_ps(b_isdenorm), _mm_castsi128_ps(adjusted2)); + adjusted5 = _mm_or_ps(adjusted3, adjusted4); + sign = _mm_slli_epi32(justsign, 16); + final = _mm_or_ps(adjusted5, _mm_castsi128_ps(sign)); + stbir__simdf_store( output + 4, final ); + + // ~38 SSE2 ops for 8 values + } + + // Fabian's round-to-nearest-even float to half + // ~48 SSE2 ops for 8 output + stbir__inline static void stbir__float_to_half_SIMD(void * output, float const * input) + { + static const STBIR__SIMDI_CONST(mask_sign, 0x80000000u); + static const STBIR__SIMDI_CONST(c_f16max, (127 + 16) << 23); // all FP32 values >=this round to +inf + static const STBIR__SIMDI_CONST(c_nanbit, 0x200); + static const STBIR__SIMDI_CONST(c_infty_as_fp16, 0x7c00); + static const STBIR__SIMDI_CONST(c_min_normal, (127 - 14) << 23); // smallest FP32 that yields a normalized FP16 + static const STBIR__SIMDI_CONST(c_subnorm_magic, ((127 - 15) + (23 - 10) + 1) << 23); + static const STBIR__SIMDI_CONST(c_normal_bias, 0xfff - ((127 - 15) << 23)); // adjust exponent and add mantissa rounding + + __m128 f = _mm_loadu_ps(input); + __m128 msign = _mm_castsi128_ps(STBIR__CONSTI(mask_sign)); + __m128 justsign = _mm_and_ps(msign, f); + __m128 absf = _mm_xor_ps(f, justsign); + __m128i absf_int = _mm_castps_si128(absf); // the cast is "free" (extra bypass latency, but no thruput hit) + __m128i f16max = STBIR__CONSTI(c_f16max); + __m128 b_isnan = _mm_cmpunord_ps(absf, absf); // is this a NaN? + __m128i b_isregular = _mm_cmpgt_epi32(f16max, absf_int); // (sub)normalized or special? + __m128i nanbit = _mm_and_si128(_mm_castps_si128(b_isnan), STBIR__CONSTI(c_nanbit)); + __m128i inf_or_nan = _mm_or_si128(nanbit, STBIR__CONSTI(c_infty_as_fp16)); // output for specials + + __m128i min_normal = STBIR__CONSTI(c_min_normal); + __m128i b_issub = _mm_cmpgt_epi32(min_normal, absf_int); + + // "result is subnormal" path + __m128 subnorm1 = _mm_add_ps(absf, _mm_castsi128_ps(STBIR__CONSTI(c_subnorm_magic))); // magic value to round output mantissa + __m128i subnorm2 = _mm_sub_epi32(_mm_castps_si128(subnorm1), STBIR__CONSTI(c_subnorm_magic)); // subtract out bias + + // "result is normal" path + __m128i mantoddbit = _mm_slli_epi32(absf_int, 31 - 13); // shift bit 13 (mantissa LSB) to sign + __m128i mantodd = _mm_srai_epi32(mantoddbit, 31); // -1 if FP16 mantissa odd, else 0 + + __m128i round1 = _mm_add_epi32(absf_int, STBIR__CONSTI(c_normal_bias)); + __m128i round2 = _mm_sub_epi32(round1, mantodd); // if mantissa LSB odd, bias towards rounding up (RTNE) + __m128i normal = _mm_srli_epi32(round2, 13); // rounded result + + // combine the two non-specials + __m128i nonspecial = _mm_or_si128(_mm_and_si128(subnorm2, b_issub), _mm_andnot_si128(b_issub, normal)); + + // merge in specials as well + __m128i joined = _mm_or_si128(_mm_and_si128(nonspecial, b_isregular), _mm_andnot_si128(b_isregular, inf_or_nan)); + + __m128i sign_shift = _mm_srai_epi32(_mm_castps_si128(justsign), 16); + __m128i final2, final= _mm_or_si128(joined, sign_shift); + + f = _mm_loadu_ps(input+4); + justsign = _mm_and_ps(msign, f); + absf = _mm_xor_ps(f, justsign); + absf_int = _mm_castps_si128(absf); // the cast is "free" (extra bypass latency, but no thruput hit) + b_isnan = _mm_cmpunord_ps(absf, absf); // is this a NaN? + b_isregular = _mm_cmpgt_epi32(f16max, absf_int); // (sub)normalized or special? + nanbit = _mm_and_si128(_mm_castps_si128(b_isnan), c_nanbit); + inf_or_nan = _mm_or_si128(nanbit, STBIR__CONSTI(c_infty_as_fp16)); // output for specials + + b_issub = _mm_cmpgt_epi32(min_normal, absf_int); + + // "result is subnormal" path + subnorm1 = _mm_add_ps(absf, _mm_castsi128_ps(STBIR__CONSTI(c_subnorm_magic))); // magic value to round output mantissa + subnorm2 = _mm_sub_epi32(_mm_castps_si128(subnorm1), STBIR__CONSTI(c_subnorm_magic)); // subtract out bias + + // "result is normal" path + mantoddbit = _mm_slli_epi32(absf_int, 31 - 13); // shift bit 13 (mantissa LSB) to sign + mantodd = _mm_srai_epi32(mantoddbit, 31); // -1 if FP16 mantissa odd, else 0 + + round1 = _mm_add_epi32(absf_int, STBIR__CONSTI(c_normal_bias)); + round2 = _mm_sub_epi32(round1, mantodd); // if mantissa LSB odd, bias towards rounding up (RTNE) + normal = _mm_srli_epi32(round2, 13); // rounded result + + // combine the two non-specials + nonspecial = _mm_or_si128(_mm_and_si128(subnorm2, b_issub), _mm_andnot_si128(b_issub, normal)); + + // merge in specials as well + joined = _mm_or_si128(_mm_and_si128(nonspecial, b_isregular), _mm_andnot_si128(b_isregular, inf_or_nan)); + + sign_shift = _mm_srai_epi32(_mm_castps_si128(justsign), 16); + final2 = _mm_or_si128(joined, sign_shift); + final = _mm_packs_epi32(final, final2); + stbir__simdi_store( output,final ); + } + +#elif defined(STBIR_WASM) || (defined(STBIR_NEON) && defined(_MSC_VER) && defined(_M_ARM)) // WASM or 32-bit ARM on MSVC/clang + + static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) + { + for (int i=0; i<8; i++) + { + output[i] = stbir__half_to_float(input[i]); + } + } + + static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input) + { + for (int i=0; i<8; i++) + { + output[i] = stbir__float_to_half(input[i]); + } + } + +#elif defined(STBIR_NEON) && defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__) // 64-bit ARM on MSVC (not clang) + + static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) + { + float16x4_t in0 = vld1_f16(input + 0); + float16x4_t in1 = vld1_f16(input + 4); + vst1q_f32(output + 0, vcvt_f32_f16(in0)); + vst1q_f32(output + 4, vcvt_f32_f16(in1)); + } + + static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input) + { + float16x4_t out0 = vcvt_f16_f32(vld1q_f32(input + 0)); + float16x4_t out1 = vcvt_f16_f32(vld1q_f32(input + 4)); + vst1_f16(output+0, out0); + vst1_f16(output+4, out1); + } + + static stbir__inline float stbir__half_to_float( stbir__FP16 h ) + { + return vgetq_lane_f32(vcvt_f32_f16(vld1_dup_f16(&h)), 0); + } + + static stbir__inline stbir__FP16 stbir__float_to_half( float f ) + { + return vget_lane_f16(vcvt_f16_f32(vdupq_n_f32(f)), 0).n16_u16[0]; + } + +#elif defined(STBIR_NEON) // 64-bit ARM + + static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) + { + float16x8_t in = vld1q_f16(input); + vst1q_f32(output + 0, vcvt_f32_f16(vget_low_f16(in))); + vst1q_f32(output + 4, vcvt_f32_f16(vget_high_f16(in))); + } + + static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input) + { + float16x4_t out0 = vcvt_f16_f32(vld1q_f32(input + 0)); + float16x4_t out1 = vcvt_f16_f32(vld1q_f32(input + 4)); + vst1q_f16(output, vcombine_f16(out0, out1)); + } + + static stbir__inline float stbir__half_to_float( stbir__FP16 h ) + { + return vgetq_lane_f32(vcvt_f32_f16(vdup_n_f16(h)), 0); + } + + static stbir__inline stbir__FP16 stbir__float_to_half( float f ) + { + return vget_lane_f16(vcvt_f16_f32(vdupq_n_f32(f)), 0); + } + +#endif + + +#ifdef STBIR_SIMD + +#define stbir__simdf_0123to3333( out, reg ) (out) = stbir__simdf_swiz( reg, 3,3,3,3 ) +#define stbir__simdf_0123to2222( out, reg ) (out) = stbir__simdf_swiz( reg, 2,2,2,2 ) +#define stbir__simdf_0123to1111( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,1,1 ) +#define stbir__simdf_0123to0000( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,0,0 ) +#define stbir__simdf_0123to0003( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,0,3 ) +#define stbir__simdf_0123to0001( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,0,1 ) +#define stbir__simdf_0123to1122( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,2,2 ) +#define stbir__simdf_0123to2333( out, reg ) (out) = stbir__simdf_swiz( reg, 2,3,3,3 ) +#define stbir__simdf_0123to0023( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,2,3 ) +#define stbir__simdf_0123to1230( out, reg ) (out) = stbir__simdf_swiz( reg, 1,2,3,0 ) +#define stbir__simdf_0123to2103( out, reg ) (out) = stbir__simdf_swiz( reg, 2,1,0,3 ) +#define stbir__simdf_0123to3210( out, reg ) (out) = stbir__simdf_swiz( reg, 3,2,1,0 ) +#define stbir__simdf_0123to2301( out, reg ) (out) = stbir__simdf_swiz( reg, 2,3,0,1 ) +#define stbir__simdf_0123to3012( out, reg ) (out) = stbir__simdf_swiz( reg, 3,0,1,2 ) +#define stbir__simdf_0123to0011( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,1,1 ) +#define stbir__simdf_0123to1100( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,0,0 ) +#define stbir__simdf_0123to2233( out, reg ) (out) = stbir__simdf_swiz( reg, 2,2,3,3 ) +#define stbir__simdf_0123to1133( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,3,3 ) +#define stbir__simdf_0123to0022( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,2,2 ) +#define stbir__simdf_0123to1032( out, reg ) (out) = stbir__simdf_swiz( reg, 1,0,3,2 ) + +typedef union stbir__simdi_u32 +{ + stbir_uint32 m128i_u32[4]; + int m128i_i32[4]; + stbir__simdi m128i_i128; +} stbir__simdi_u32; + +static const int STBIR_mask[9] = { 0,0,0,-1,-1,-1,0,0,0 }; + +static const STBIR__SIMDF_CONST(STBIR_max_uint8_as_float, stbir__max_uint8_as_float); +static const STBIR__SIMDF_CONST(STBIR_max_uint16_as_float, stbir__max_uint16_as_float); +static const STBIR__SIMDF_CONST(STBIR_max_uint8_as_float_inverted, stbir__max_uint8_as_float_inverted); +static const STBIR__SIMDF_CONST(STBIR_max_uint16_as_float_inverted, stbir__max_uint16_as_float_inverted); + +static const STBIR__SIMDF_CONST(STBIR_simd_point5, 0.5f); +static const STBIR__SIMDF_CONST(STBIR_ones, 1.0f); +static const STBIR__SIMDI_CONST(STBIR_almost_zero, (127 - 13) << 23); +static const STBIR__SIMDI_CONST(STBIR_almost_one, 0x3f7fffff); +static const STBIR__SIMDI_CONST(STBIR_mastissa_mask, 0xff); +static const STBIR__SIMDI_CONST(STBIR_topscale, 0x02000000); + +// Basically, in simd mode, we unroll the proper amount, and we don't want +// the non-simd remnant loops to be unroll because they only run a few times +// Adding this switch saves about 5K on clang which is Captain Unroll the 3rd. +#define STBIR_SIMD_STREAMOUT_PTR( star ) STBIR_STREAMOUT_PTR( star ) +#define STBIR_SIMD_NO_UNROLL(ptr) STBIR_NO_UNROLL(ptr) + +#ifdef STBIR_MEMCPY +#undef STBIR_MEMCPY +#define STBIR_MEMCPY stbir_simd_memcpy +#endif + +// override normal use of memcpy with much simpler copy (faster and smaller with our sized copies) +static void stbir_simd_memcpy( void * dest, void const * src, size_t bytes ) +{ + char STBIR_SIMD_STREAMOUT_PTR (*) d = (char*) dest; + char STBIR_SIMD_STREAMOUT_PTR( * ) d_end = ((char*) dest) + bytes; + ptrdiff_t ofs_to_src = (char*)src - (char*)dest; + + // check overlaps + STBIR_ASSERT( ( ( d >= ( (char*)src) + bytes ) ) || ( ( d + bytes ) <= (char*)src ) ); + + if ( bytes < (16*stbir__simdfX_float_count) ) + { + if ( bytes < 16 ) + { + if ( bytes ) + { + do + { + STBIR_SIMD_NO_UNROLL(d); + d[ 0 ] = d[ ofs_to_src ]; + ++d; + } while ( d < d_end ); + } + } + else + { + stbir__simdf x; + // do one unaligned to get us aligned for the stream out below + stbir__simdf_load( x, ( d + ofs_to_src ) ); + stbir__simdf_store( d, x ); + d = (char*)( ( ( (ptrdiff_t)d ) + 16 ) & ~15 ); + + for(;;) + { + STBIR_SIMD_NO_UNROLL(d); + + if ( d > ( d_end - 16 ) ) + { + if ( d == d_end ) + return; + d = d_end - 16; + } + + stbir__simdf_load( x, ( d + ofs_to_src ) ); + stbir__simdf_store( d, x ); + d += 16; + } + } + } + else + { + stbir__simdfX x0,x1,x2,x3; + + // do one unaligned to get us aligned for the stream out below + stbir__simdfX_load( x0, ( d + ofs_to_src ) + 0*stbir__simdfX_float_count ); + stbir__simdfX_load( x1, ( d + ofs_to_src ) + 4*stbir__simdfX_float_count ); + stbir__simdfX_load( x2, ( d + ofs_to_src ) + 8*stbir__simdfX_float_count ); + stbir__simdfX_load( x3, ( d + ofs_to_src ) + 12*stbir__simdfX_float_count ); + stbir__simdfX_store( d + 0*stbir__simdfX_float_count, x0 ); + stbir__simdfX_store( d + 4*stbir__simdfX_float_count, x1 ); + stbir__simdfX_store( d + 8*stbir__simdfX_float_count, x2 ); + stbir__simdfX_store( d + 12*stbir__simdfX_float_count, x3 ); + d = (char*)( ( ( (ptrdiff_t)d ) + (16*stbir__simdfX_float_count) ) & ~((16*stbir__simdfX_float_count)-1) ); + + for(;;) + { + STBIR_SIMD_NO_UNROLL(d); + + if ( d > ( d_end - (16*stbir__simdfX_float_count) ) ) + { + if ( d == d_end ) + return; + d = d_end - (16*stbir__simdfX_float_count); + } + + stbir__simdfX_load( x0, ( d + ofs_to_src ) + 0*stbir__simdfX_float_count ); + stbir__simdfX_load( x1, ( d + ofs_to_src ) + 4*stbir__simdfX_float_count ); + stbir__simdfX_load( x2, ( d + ofs_to_src ) + 8*stbir__simdfX_float_count ); + stbir__simdfX_load( x3, ( d + ofs_to_src ) + 12*stbir__simdfX_float_count ); + stbir__simdfX_store( d + 0*stbir__simdfX_float_count, x0 ); + stbir__simdfX_store( d + 4*stbir__simdfX_float_count, x1 ); + stbir__simdfX_store( d + 8*stbir__simdfX_float_count, x2 ); + stbir__simdfX_store( d + 12*stbir__simdfX_float_count, x3 ); + d += (16*stbir__simdfX_float_count); + } + } +} + +// memcpy that is specically intentionally overlapping (src is smaller then dest, so can be +// a normal forward copy, bytes is divisible by 4 and bytes is greater than or equal to +// the diff between dest and src) +static void stbir_overlapping_memcpy( void * dest, void const * src, size_t bytes ) +{ + char STBIR_SIMD_STREAMOUT_PTR (*) sd = (char*) src; + char STBIR_SIMD_STREAMOUT_PTR( * ) s_end = ((char*) src) + bytes; + ptrdiff_t ofs_to_dest = (char*)dest - (char*)src; + + if ( ofs_to_dest >= 16 ) // is the overlap more than 16 away? + { + char STBIR_SIMD_STREAMOUT_PTR( * ) s_end16 = ((char*) src) + (bytes&~15); + do + { + stbir__simdf x; + STBIR_SIMD_NO_UNROLL(sd); + stbir__simdf_load( x, sd ); + stbir__simdf_store( ( sd + ofs_to_dest ), x ); + sd += 16; + } while ( sd < s_end16 ); + + if ( sd == s_end ) + return; + } + + do + { + STBIR_SIMD_NO_UNROLL(sd); + *(int*)( sd + ofs_to_dest ) = *(int*) sd; + sd += 4; + } while ( sd < s_end ); +} + +#else // no SSE2 + +// when in scalar mode, we let unrolling happen, so this macro just does the __restrict +#define STBIR_SIMD_STREAMOUT_PTR( star ) STBIR_STREAMOUT_PTR( star ) +#define STBIR_SIMD_NO_UNROLL(ptr) + +#endif // SSE2 + + +#ifdef STBIR_PROFILE + +#if defined(_x86_64) || defined( __x86_64__ ) || defined( _M_X64 ) || defined(__x86_64) || defined(__SSE2__) || defined(STBIR_SSE) || defined( _M_IX86_FP ) || defined(__i386) || defined( __i386__ ) || defined( _M_IX86 ) || defined( _X86_ ) + +#ifdef _MSC_VER + + STBIRDEF stbir_uint64 __rdtsc(); + #define STBIR_PROFILE_FUNC() __rdtsc() + +#else // non msvc + + static stbir__inline stbir_uint64 STBIR_PROFILE_FUNC() + { + stbir_uint32 lo, hi; + asm volatile ("rdtsc" : "=a" (lo), "=d" (hi) ); + return ( ( (stbir_uint64) hi ) << 32 ) | ( (stbir_uint64) lo ); + } + +#endif // msvc + +#elif defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) || defined(__ARM_NEON__) + +#if defined( _MSC_VER ) && !defined(__clang__) + + #define STBIR_PROFILE_FUNC() _ReadStatusReg(ARM64_CNTVCT) + +#else + + static stbir__inline stbir_uint64 STBIR_PROFILE_FUNC() + { + stbir_uint64 tsc; + asm volatile("mrs %0, cntvct_el0" : "=r" (tsc)); + return tsc; + } + +#endif + +#else // x64, arm + +#error Unknown platform for profiling. + +#endif //x64 and + + +#define STBIR_ONLY_PROFILE_GET_SPLIT_INFO ,stbir__per_split_info * split_info +#define STBIR_ONLY_PROFILE_SET_SPLIT_INFO ,split_info + +#define STBIR_ONLY_PROFILE_BUILD_GET_INFO ,stbir__info * profile_info +#define STBIR_ONLY_PROFILE_BUILD_SET_INFO ,profile_info + +// super light-weight micro profiler +#define STBIR_PROFILE_START_ll( info, wh ) { stbir_uint64 wh##thiszonetime = STBIR_PROFILE_FUNC(); stbir_uint64 * wh##save_parent_excluded_ptr = info->current_zone_excluded_ptr; stbir_uint64 wh##current_zone_excluded = 0; info->current_zone_excluded_ptr = &wh##current_zone_excluded; +#define STBIR_PROFILE_END_ll( info, wh ) wh##thiszonetime = STBIR_PROFILE_FUNC() - wh##thiszonetime; info->profile.named.wh += wh##thiszonetime - wh##current_zone_excluded; *wh##save_parent_excluded_ptr += wh##thiszonetime; info->current_zone_excluded_ptr = wh##save_parent_excluded_ptr; } +#define STBIR_PROFILE_FIRST_START_ll( info, wh ) { int i; info->current_zone_excluded_ptr = &info->profile.named.total; for(i=0;iprofile.array);i++) info->profile.array[i]=0; } STBIR_PROFILE_START_ll( info, wh ); +#define STBIR_PROFILE_CLEAR_EXTRAS_ll( info, num ) { int extra; for(extra=1;extra<(num);extra++) { int i; for(i=0;iprofile.array);i++) (info)[extra].profile.array[i]=0; } } + +// for thread data +#define STBIR_PROFILE_START( wh ) STBIR_PROFILE_START_ll( split_info, wh ) +#define STBIR_PROFILE_END( wh ) STBIR_PROFILE_END_ll( split_info, wh ) +#define STBIR_PROFILE_FIRST_START( wh ) STBIR_PROFILE_FIRST_START_ll( split_info, wh ) +#define STBIR_PROFILE_CLEAR_EXTRAS() STBIR_PROFILE_CLEAR_EXTRAS_ll( split_info, split_count ) + +// for build data +#define STBIR_PROFILE_BUILD_START( wh ) STBIR_PROFILE_START_ll( profile_info, wh ) +#define STBIR_PROFILE_BUILD_END( wh ) STBIR_PROFILE_END_ll( profile_info, wh ) +#define STBIR_PROFILE_BUILD_FIRST_START( wh ) STBIR_PROFILE_FIRST_START_ll( profile_info, wh ) +#define STBIR_PROFILE_BUILD_CLEAR( info ) { int i; for(i=0;iprofile.array);i++) info->profile.array[i]=0; } + +#else // no profile + +#define STBIR_ONLY_PROFILE_GET_SPLIT_INFO +#define STBIR_ONLY_PROFILE_SET_SPLIT_INFO + +#define STBIR_ONLY_PROFILE_BUILD_GET_INFO +#define STBIR_ONLY_PROFILE_BUILD_SET_INFO + +#define STBIR_PROFILE_START( wh ) +#define STBIR_PROFILE_END( wh ) +#define STBIR_PROFILE_FIRST_START( wh ) +#define STBIR_PROFILE_CLEAR_EXTRAS( ) + +#define STBIR_PROFILE_BUILD_START( wh ) +#define STBIR_PROFILE_BUILD_END( wh ) +#define STBIR_PROFILE_BUILD_FIRST_START( wh ) +#define STBIR_PROFILE_BUILD_CLEAR( info ) + +#endif // stbir_profile + +#ifndef STBIR_CEILF +#include +#if _MSC_VER <= 1200 // support VC6 for Sean +#define STBIR_CEILF(x) ((float)ceil((float)(x))) +#define STBIR_FLOORF(x) ((float)floor((float)(x))) +#else +#define STBIR_CEILF(x) ceilf(x) +#define STBIR_FLOORF(x) floorf(x) +#endif +#endif + +#ifndef STBIR_MEMCPY +// For memcpy +#include +#define STBIR_MEMCPY( dest, src, len ) memcpy( dest, src, len ) +#endif + +#ifndef STBIR_SIMD + +// memcpy that is specically intentionally overlapping (src is smaller then dest, so can be +// a normal forward copy, bytes is divisible by 4 and bytes is greater than or equal to +// the diff between dest and src) +static void stbir_overlapping_memcpy( void * dest, void const * src, size_t bytes ) +{ + char STBIR_SIMD_STREAMOUT_PTR (*) sd = (char*) src; + char STBIR_SIMD_STREAMOUT_PTR( * ) s_end = ((char*) src) + bytes; + ptrdiff_t ofs_to_dest = (char*)dest - (char*)src; + + if ( ofs_to_dest >= 8 ) // is the overlap more than 8 away? + { + char STBIR_SIMD_STREAMOUT_PTR( * ) s_end8 = ((char*) src) + (bytes&~7); + do + { + STBIR_NO_UNROLL(sd); + *(stbir_uint64*)( sd + ofs_to_dest ) = *(stbir_uint64*) sd; + sd += 8; + } while ( sd < s_end8 ); + + if ( sd == s_end ) + return; + } + + do + { + STBIR_NO_UNROLL(sd); + *(int*)( sd + ofs_to_dest ) = *(int*) sd; + sd += 4; + } while ( sd < s_end ); +} + +#endif + +static float stbir__filter_trapezoid(float x, float scale, void * user_data) +{ + float halfscale = scale / 2; + float t = 0.5f + halfscale; + STBIR_ASSERT(scale <= 1); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x >= t) + return 0.0f; + else + { + float r = 0.5f - halfscale; + if (x <= r) + return 1.0f; + else + return (t - x) / scale; + } +} + +static float stbir__support_trapezoid(float scale, void * user_data) +{ + STBIR__UNUSED(user_data); + return 0.5f + scale / 2.0f; +} + +static float stbir__filter_triangle(float x, float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x <= 1.0f) + return 1.0f - x; + else + return 0.0f; +} + +static float stbir__filter_point(float x, float s, void * user_data) +{ + STBIR__UNUSED(x); + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + return 1.0f; +} + +static float stbir__filter_cubic(float x, float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x < 1.0f) + return (4.0f + x*x*(3.0f*x - 6.0f))/6.0f; + else if (x < 2.0f) + return (8.0f + x*(-12.0f + x*(6.0f - x)))/6.0f; + + return (0.0f); +} + +static float stbir__filter_catmullrom(float x, float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x < 1.0f) + return 1.0f - x*x*(2.5f - 1.5f*x); + else if (x < 2.0f) + return 2.0f - x*(4.0f + x*(0.5f*x - 2.5f)); + + return (0.0f); +} + +static float stbir__filter_mitchell(float x, float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x < 1.0f) + return (16.0f + x*x*(21.0f * x - 36.0f))/18.0f; + else if (x < 2.0f) + return (32.0f + x*(-60.0f + x*(36.0f - 7.0f*x)))/18.0f; + + return (0.0f); +} + +static float stbir__support_zero(float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + return 0; +} + +static float stbir__support_zeropoint5(float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + return 0.5f; +} + +static float stbir__support_one(float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + return 1; +} + +static float stbir__support_two(float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + return 2; +} + +// This is the maximum number of input samples that can affect an output sample +// with the given filter from the output pixel's perspective +static int stbir__get_filter_pixel_width(stbir__support_callback * support, float scale, void * user_data) +{ + STBIR_ASSERT(support != 0); + + if ( scale >= ( 1.0f-stbir__small_float ) ) // upscale + return (int)STBIR_CEILF(support(1.0f/scale,user_data) * 2.0f); + else + return (int)STBIR_CEILF(support(scale,user_data) * 2.0f / scale); +} + +// this is how many coefficents per run of the filter (which is different +// from the filter_pixel_width depending on if we are scattering or gathering) +static int stbir__get_coefficient_width(stbir__sampler * samp, int is_gather, void * user_data) +{ + float scale = samp->scale_info.scale; + stbir__support_callback * support = samp->filter_support; + + switch( is_gather ) + { + case 1: + return (int)STBIR_CEILF(support(1.0f / scale, user_data) * 2.0f); + case 2: + return (int)STBIR_CEILF(support(scale, user_data) * 2.0f / scale); + case 0: + return (int)STBIR_CEILF(support(scale, user_data) * 2.0f); + default: + STBIR_ASSERT( (is_gather >= 0 ) && (is_gather <= 2 ) ); + return 0; + } +} + +static int stbir__get_contributors(stbir__sampler * samp, int is_gather) +{ + if (is_gather) + return samp->scale_info.output_sub_size; + else + return (samp->scale_info.input_full_size + samp->filter_pixel_margin * 2); +} + +static int stbir__edge_zero_full( int n, int max ) +{ + STBIR__UNUSED(n); + STBIR__UNUSED(max); + return 0; // NOTREACHED +} + +static int stbir__edge_clamp_full( int n, int max ) +{ + if (n < 0) + return 0; + + if (n >= max) + return max - 1; + + return n; // NOTREACHED +} + +static int stbir__edge_reflect_full( int n, int max ) +{ + if (n < 0) + { + if (n > -max) + return -n; + else + return max - 1; + } + + if (n >= max) + { + int max2 = max * 2; + if (n >= max2) + return 0; + else + return max2 - n - 1; + } + + return n; // NOTREACHED +} + +static int stbir__edge_wrap_full( int n, int max ) +{ + if (n >= 0) + return (n % max); + else + { + int m = (-n) % max; + + if (m != 0) + m = max - m; + + return (m); + } +} + +typedef int stbir__edge_wrap_func( int n, int max ); +static stbir__edge_wrap_func * stbir__edge_wrap_slow[] = +{ + stbir__edge_clamp_full, // STBIR_EDGE_CLAMP + stbir__edge_reflect_full, // STBIR_EDGE_REFLECT + stbir__edge_wrap_full, // STBIR_EDGE_WRAP + stbir__edge_zero_full, // STBIR_EDGE_ZERO +}; + +stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) +{ + // avoid per-pixel switch + if (n >= 0 && n < max) + return n; + return stbir__edge_wrap_slow[edge]( n, max ); +} + +#define STBIR__MERGE_RUNS_PIXEL_THRESHOLD 16 + +// get information on the extents of a sampler +static void stbir__get_extents( stbir__sampler * samp, stbir__extents * scanline_extents ) +{ + int j, stop; + int left_margin, right_margin; + int min_n = 0x7fffffff, max_n = -0x7fffffff; + int min_left = 0x7fffffff, max_left = -0x7fffffff; + int min_right = 0x7fffffff, max_right = -0x7fffffff; + stbir_edge edge = samp->edge; + stbir__contributors* contributors = samp->contributors; + int output_sub_size = samp->scale_info.output_sub_size; + int input_full_size = samp->scale_info.input_full_size; + int filter_pixel_margin = samp->filter_pixel_margin; + + STBIR_ASSERT( samp->is_gather ); + + stop = output_sub_size; + for (j = 0; j < stop; j++ ) + { + STBIR_ASSERT( contributors[j].n1 >= contributors[j].n0 ); + if ( contributors[j].n0 < min_n ) + { + min_n = contributors[j].n0; + stop = j + filter_pixel_margin; // if we find a new min, only scan another filter width + if ( stop > output_sub_size ) stop = output_sub_size; + } + } + + stop = 0; + for (j = output_sub_size - 1; j >= stop; j-- ) + { + STBIR_ASSERT( contributors[j].n1 >= contributors[j].n0 ); + if ( contributors[j].n1 > max_n ) + { + max_n = contributors[j].n1; + stop = j - filter_pixel_margin; // if we find a new max, only scan another filter width + if (stop<0) stop = 0; + } + } + + STBIR_ASSERT( scanline_extents->conservative.n0 <= min_n ); + STBIR_ASSERT( scanline_extents->conservative.n1 >= max_n ); + + // now calculate how much into the margins we really read + left_margin = 0; + if ( min_n < 0 ) + { + left_margin = -min_n; + min_n = 0; + } + + right_margin = 0; + if ( max_n >= input_full_size ) + { + right_margin = max_n - input_full_size + 1; + max_n = input_full_size - 1; + } + + // index 1 is margin pixel extents (how many pixels we hang over the edge) + scanline_extents->edge_sizes[0] = left_margin; + scanline_extents->edge_sizes[1] = right_margin; + + // index 2 is pixels read from the input + scanline_extents->spans[0].n0 = min_n; + scanline_extents->spans[0].n1 = max_n; + scanline_extents->spans[0].pixel_offset_for_input = min_n; + + // default to no other input range + scanline_extents->spans[1].n0 = 0; + scanline_extents->spans[1].n1 = -1; + scanline_extents->spans[1].pixel_offset_for_input = 0; + + // don't have to do edge calc for zero clamp + if ( edge == STBIR_EDGE_ZERO ) + return; + + // convert margin pixels to the pixels within the input (min and max) + for( j = -left_margin ; j < 0 ; j++ ) + { + int p = stbir__edge_wrap( edge, j, input_full_size ); + if ( p < min_left ) + min_left = p; + if ( p > max_left ) + max_left = p; + } + + for( j = input_full_size ; j < (input_full_size + right_margin) ; j++ ) + { + int p = stbir__edge_wrap( edge, j, input_full_size ); + if ( p < min_right ) + min_right = p; + if ( p > max_right ) + max_right = p; + } + + // merge the left margin pixel region if it connects within 4 pixels of main pixel region + if ( min_left != 0x7fffffff ) + { + if ( ( ( min_left <= min_n ) && ( ( max_left + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= min_n ) ) || + ( ( min_n <= min_left ) && ( ( max_n + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= max_left ) ) ) + { + scanline_extents->spans[0].n0 = min_n = stbir__min( min_n, min_left ); + scanline_extents->spans[0].n1 = max_n = stbir__max( max_n, max_left ); + scanline_extents->spans[0].pixel_offset_for_input = min_n; + left_margin = 0; + } + } + + // merge the right margin pixel region if it connects within 4 pixels of main pixel region + if ( min_right != 0x7fffffff ) + { + if ( ( ( min_right <= min_n ) && ( ( max_right + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= min_n ) ) || + ( ( min_n <= min_right ) && ( ( max_n + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= max_right ) ) ) + { + scanline_extents->spans[0].n0 = min_n = stbir__min( min_n, min_right ); + scanline_extents->spans[0].n1 = max_n = stbir__max( max_n, max_right ); + scanline_extents->spans[0].pixel_offset_for_input = min_n; + right_margin = 0; + } + } + + STBIR_ASSERT( scanline_extents->conservative.n0 <= min_n ); + STBIR_ASSERT( scanline_extents->conservative.n1 >= max_n ); + + // you get two ranges when you have the WRAP edge mode and you are doing just the a piece of the resize + // so you need to get a second run of pixels from the opposite side of the scanline (which you + // wouldn't need except for WRAP) + + + // if we can't merge the min_left range, add it as a second range + if ( ( left_margin ) && ( min_left != 0x7fffffff ) ) + { + stbir__span * newspan = scanline_extents->spans + 1; + STBIR_ASSERT( right_margin == 0 ); + if ( min_left < scanline_extents->spans[0].n0 ) + { + scanline_extents->spans[1].pixel_offset_for_input = scanline_extents->spans[0].n0; + scanline_extents->spans[1].n0 = scanline_extents->spans[0].n0; + scanline_extents->spans[1].n1 = scanline_extents->spans[0].n1; + --newspan; + } + newspan->pixel_offset_for_input = min_left; + newspan->n0 = -left_margin; + newspan->n1 = ( max_left - min_left ) - left_margin; + scanline_extents->edge_sizes[0] = 0; // don't need to copy the left margin, since we are directly decoding into the margin + return; + } + + // if we can't merge the min_left range, add it as a second range + if ( ( right_margin ) && ( min_right != 0x7fffffff ) ) + { + stbir__span * newspan = scanline_extents->spans + 1; + if ( min_right < scanline_extents->spans[0].n0 ) + { + scanline_extents->spans[1].pixel_offset_for_input = scanline_extents->spans[0].n0; + scanline_extents->spans[1].n0 = scanline_extents->spans[0].n0; + scanline_extents->spans[1].n1 = scanline_extents->spans[0].n1; + --newspan; + } + newspan->pixel_offset_for_input = min_right; + newspan->n0 = scanline_extents->spans[1].n1 + 1; + newspan->n1 = scanline_extents->spans[1].n1 + 1 + ( max_right - min_right ); + scanline_extents->edge_sizes[1] = 0; // don't need to copy the right margin, since we are directly decoding into the margin + return; + } +} + +static void stbir__calculate_in_pixel_range( int * first_pixel, int * last_pixel, float out_pixel_center, float out_filter_radius, float inv_scale, float out_shift, int input_size, stbir_edge edge ) +{ + int first, last; + float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; + float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; + + float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) * inv_scale; + float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) * inv_scale; + + first = (int)(STBIR_FLOORF(in_pixel_influence_lowerbound + 0.5f)); + last = (int)(STBIR_FLOORF(in_pixel_influence_upperbound - 0.5f)); + + if ( edge == STBIR_EDGE_WRAP ) + { + if ( first <= -input_size ) + first = -(input_size-1); + if ( last >= (input_size*2)) + last = (input_size*2) - 1; + } + + *first_pixel = first; + *last_pixel = last; +} + +static void stbir__calculate_coefficients_for_gather_upsample( float out_filter_radius, stbir__kernel_callback * kernel, stbir__scale_info * scale_info, int num_contributors, stbir__contributors* contributors, float* coefficient_group, int coefficient_width, stbir_edge edge, void * user_data ) +{ + int n, end; + float inv_scale = scale_info->inv_scale; + float out_shift = scale_info->pixel_shift; + int input_size = scale_info->input_full_size; + int numerator = scale_info->scale_numerator; + int polyphase = ( ( scale_info->scale_is_rational ) && ( numerator < num_contributors ) ); + + // Looping through out pixels + end = num_contributors; if ( polyphase ) end = numerator; + for (n = 0; n < end; n++) + { + int i; + int last_non_zero; + float out_pixel_center = (float)n + 0.5f; + float in_center_of_out = (out_pixel_center + out_shift) * inv_scale; + + int in_first_pixel, in_last_pixel; + + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, out_pixel_center, out_filter_radius, inv_scale, out_shift, input_size, edge ); + + last_non_zero = -1; + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + { + float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; + float coeff = kernel(in_center_of_out - in_pixel_center, inv_scale, user_data); + + // kill denormals + if ( ( ( coeff < stbir__small_float ) && ( coeff > -stbir__small_float ) ) ) + { + if ( i == 0 ) // if we're at the front, just eat zero contributors + { + STBIR_ASSERT ( ( in_last_pixel - in_first_pixel ) != 0 ); // there should be at least one contrib + ++in_first_pixel; + i--; + continue; + } + coeff = 0; // make sure is fully zero (should keep denormals away) + } + else + last_non_zero = i; + + coefficient_group[i] = coeff; + } + + in_last_pixel = last_non_zero+in_first_pixel; // kills trailing zeros + contributors->n0 = in_first_pixel; + contributors->n1 = in_last_pixel; + + STBIR_ASSERT(contributors->n1 >= contributors->n0); + + ++contributors; + coefficient_group += coefficient_width; + } +} + +static void stbir__insert_coeff( stbir__contributors * contribs, float * coeffs, int new_pixel, float new_coeff ) +{ + if ( new_pixel <= contribs->n1 ) // before the end + { + if ( new_pixel < contribs->n0 ) // before the front? + { + int j, o = contribs->n0 - new_pixel; + for ( j = contribs->n1 - contribs->n0 ; j <= 0 ; j-- ) + coeffs[ j + o ] = coeffs[ j ]; + for ( j = 1 ; j < o ; j-- ) + coeffs[ j ] = coeffs[ 0 ]; + coeffs[ 0 ] = new_coeff; + contribs->n0 = new_pixel; + } + else + { + coeffs[ new_pixel - contribs->n0 ] += new_coeff; + } + } + else + { + int j, e = new_pixel - contribs->n0; + for( j = ( contribs->n1 - contribs->n0 ) + 1 ; j < e ; j++ ) // clear in-betweens coeffs if there are any + coeffs[j] = 0; + + coeffs[ e ] = new_coeff; + contribs->n1 = new_pixel; + } +} + +static void stbir__calculate_out_pixel_range( int * first_pixel, int * last_pixel, float in_pixel_center, float in_pixels_radius, float scale, float out_shift, int out_size ) +{ + float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; + float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; + float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale - out_shift; + float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale - out_shift; + int out_first_pixel = (int)(STBIR_FLOORF(out_pixel_influence_lowerbound + 0.5f)); + int out_last_pixel = (int)(STBIR_FLOORF(out_pixel_influence_upperbound - 0.5f)); + + if ( out_first_pixel < 0 ) + out_first_pixel = 0; + if ( out_last_pixel >= out_size ) + out_last_pixel = out_size - 1; + *first_pixel = out_first_pixel; + *last_pixel = out_last_pixel; +} + +static void stbir__calculate_coefficients_for_gather_downsample( int start, int end, float in_pixels_radius, stbir__kernel_callback * kernel, stbir__scale_info * scale_info, int coefficient_width, int num_contributors, stbir__contributors * contributors, float * coefficient_group, void * user_data ) +{ + int in_pixel; + int i; + int first_out_inited = -1; + float scale = scale_info->scale; + float out_shift = scale_info->pixel_shift; + int out_size = scale_info->output_sub_size; + int numerator = scale_info->scale_numerator; + int polyphase = ( ( scale_info->scale_is_rational ) && ( numerator < out_size ) ); + + STBIR__UNUSED(num_contributors); + + // Loop through the input pixels + for (in_pixel = start; in_pixel < end; in_pixel++) + { + float in_pixel_center = (float)in_pixel + 0.5f; + float out_center_of_in = in_pixel_center * scale - out_shift; + int out_first_pixel, out_last_pixel; + + stbir__calculate_out_pixel_range( &out_first_pixel, &out_last_pixel, in_pixel_center, in_pixels_radius, scale, out_shift, out_size ); + + if ( out_first_pixel > out_last_pixel ) + continue; + + // clamp or exit if we are using polyphase filtering, and the limit is up + if ( polyphase ) + { + // when polyphase, you only have to do coeffs up to the numerator count + if ( out_first_pixel == numerator ) + break; + + // don't do any extra work, clamp last pixel at numerator too + if ( out_last_pixel >= numerator ) + out_last_pixel = numerator - 1; + } + + for (i = 0; i <= out_last_pixel - out_first_pixel; i++) + { + float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; + float x = out_pixel_center - out_center_of_in; + float coeff = kernel(x, scale, user_data) * scale; + + // kill the coeff if it's too small (avoid denormals) + if ( ( ( coeff < stbir__small_float ) && ( coeff > -stbir__small_float ) ) ) + coeff = 0.0f; + + { + int out = i + out_first_pixel; + float * coeffs = coefficient_group + out * coefficient_width; + stbir__contributors * contribs = contributors + out; + + // is this the first time this output pixel has been seen? Init it. + if ( out > first_out_inited ) + { + STBIR_ASSERT( out == ( first_out_inited + 1 ) ); // ensure we have only advanced one at time + first_out_inited = out; + contribs->n0 = in_pixel; + contribs->n1 = in_pixel; + coeffs[0] = coeff; + } + else + { + // insert on end (always in order) + if ( coeffs[0] == 0.0f ) // if the first coefficent is zero, then zap it for this coeffs + { + STBIR_ASSERT( ( in_pixel - contribs->n0 ) == 1 ); // ensure that when we zap, we're at the 2nd pos + contribs->n0 = in_pixel; + } + contribs->n1 = in_pixel; + STBIR_ASSERT( ( in_pixel - contribs->n0 ) < coefficient_width ); + coeffs[in_pixel - contribs->n0] = coeff; + } + } + } + } +} + +static void stbir__cleanup_gathered_coefficients( stbir_edge edge, stbir__filter_extent_info* filter_info, stbir__scale_info * scale_info, int num_contributors, stbir__contributors* contributors, float * coefficient_group, int coefficient_width ) +{ + int input_size = scale_info->input_full_size; + int input_last_n1 = input_size - 1; + int n, end; + int lowest = 0x7fffffff; + int highest = -0x7fffffff; + int widest = -1; + int numerator = scale_info->scale_numerator; + int denominator = scale_info->scale_denominator; + int polyphase = ( ( scale_info->scale_is_rational ) && ( numerator < num_contributors ) ); + float * coeffs; + stbir__contributors * contribs; + + // weight all the coeffs for each sample + coeffs = coefficient_group; + contribs = contributors; + end = num_contributors; if ( polyphase ) end = numerator; + for (n = 0; n < end; n++) + { + int i; + float filter_scale, total_filter = 0; + int e; + + // add all contribs + e = contribs->n1 - contribs->n0; + for( i = 0 ; i <= e ; i++ ) + { + total_filter += coeffs[i]; + STBIR_ASSERT( ( coeffs[i] >= -2.0f ) && ( coeffs[i] <= 2.0f ) ); // check for wonky weights + } + + // rescale + if ( ( total_filter < stbir__small_float ) && ( total_filter > -stbir__small_float ) ) + { + // all coeffs are extremely small, just zero it + contribs->n1 = contribs->n0; + coeffs[0] = 0.0f; + } + else + { + // if the total isn't 1.0, rescale everything + if ( ( total_filter < (1.0f-stbir__small_float) ) || ( total_filter > (1.0f+stbir__small_float) ) ) + { + filter_scale = 1.0f / total_filter; + // scale them all + for (i = 0; i <= e; i++) + coeffs[i] *= filter_scale; + } + } + ++contribs; + coeffs += coefficient_width; + } + + // if we have a rational for the scale, we can exploit the polyphaseness to not calculate + // most of the coefficients, so we copy them here + if ( polyphase ) + { + stbir__contributors * prev_contribs = contributors; + stbir__contributors * cur_contribs = contributors + numerator; + + for( n = numerator ; n < num_contributors ; n++ ) + { + cur_contribs->n0 = prev_contribs->n0 + denominator; + cur_contribs->n1 = prev_contribs->n1 + denominator; + ++cur_contribs; + ++prev_contribs; + } + stbir_overlapping_memcpy( coefficient_group + numerator * coefficient_width, coefficient_group, ( num_contributors - numerator ) * coefficient_width * sizeof( coeffs[ 0 ] ) ); + } + + coeffs = coefficient_group; + contribs = contributors; + for (n = 0; n < num_contributors; n++) + { + int i; + + // in zero edge mode, just remove out of bounds contribs completely (since their weights are accounted for now) + if ( edge == STBIR_EDGE_ZERO ) + { + // shrink the right side if necessary + if ( contribs->n1 > input_last_n1 ) + contribs->n1 = input_last_n1; + + // shrink the left side + if ( contribs->n0 < 0 ) + { + int j, left, skips = 0; + + skips = -contribs->n0; + contribs->n0 = 0; + + // now move down the weights + left = contribs->n1 - contribs->n0 + 1; + if ( left > 0 ) + { + for( j = 0 ; j < left ; j++ ) + coeffs[ j ] = coeffs[ j + skips ]; + } + } + } + else if ( ( edge == STBIR_EDGE_CLAMP ) || ( edge == STBIR_EDGE_REFLECT ) ) + { + // for clamp and reflect, calculate the true inbounds position (based on edge type) and just add that to the existing weight + + // right hand side first + if ( contribs->n1 > input_last_n1 ) + { + int start = contribs->n0; + int endi = contribs->n1; + contribs->n1 = input_last_n1; + for( i = input_size; i <= endi; i++ ) + stbir__insert_coeff( contribs, coeffs, stbir__edge_wrap_slow[edge]( i, input_size ), coeffs[i-start] ); + } + + // now check left hand edge + if ( contribs->n0 < 0 ) + { + int save_n0; + float save_n0_coeff; + float * c = coeffs - ( contribs->n0 + 1 ); + + // reinsert the coeffs with it reflected or clamped (insert accumulates, if the coeffs exist) + for( i = -1 ; i > contribs->n0 ; i-- ) + stbir__insert_coeff( contribs, coeffs, stbir__edge_wrap_slow[edge]( i, input_size ), *c-- ); + save_n0 = contribs->n0; + save_n0_coeff = c[0]; // save it, since we didn't do the final one (i==n0), because there might be too many coeffs to hold (before we resize)! + + // now slide all the coeffs down (since we have accumulated them in the positive contribs) and reset the first contrib + contribs->n0 = 0; + for(i = 0 ; i <= contribs->n1 ; i++ ) + coeffs[i] = coeffs[i-save_n0]; + + // now that we have shrunk down the contribs, we insert the first one safely + stbir__insert_coeff( contribs, coeffs, stbir__edge_wrap_slow[edge]( save_n0, input_size ), save_n0_coeff ); + } + } + + if ( contribs->n0 <= contribs->n1 ) + { + int diff = contribs->n1 - contribs->n0 + 1; + while ( diff && ( coeffs[ diff-1 ] == 0.0f ) ) + --diff; + contribs->n1 = contribs->n0 + diff - 1; + + if ( contribs->n0 <= contribs->n1 ) + { + if ( contribs->n0 < lowest ) + lowest = contribs->n0; + if ( contribs->n1 > highest ) + highest = contribs->n1; + if ( diff > widest ) + widest = diff; + } + + // re-zero out unused coefficients (if any) + for( i = diff ; i < coefficient_width ; i++ ) + coeffs[i] = 0.0f; + } + + ++contribs; + coeffs += coefficient_width; + } + filter_info->lowest = lowest; + filter_info->highest = highest; + filter_info->widest = widest; +} + +static int stbir__pack_coefficients( int num_contributors, stbir__contributors* contributors, float * coefficents, int coefficient_width, int widest, int row_width ) +{ + #define STBIR_MOVE_1( dest, src ) { STBIR_NO_UNROLL(dest); ((stbir_uint32*)(dest))[0] = ((stbir_uint32*)(src))[0]; } + #define STBIR_MOVE_2( dest, src ) { STBIR_NO_UNROLL(dest); ((stbir_uint64*)(dest))[0] = ((stbir_uint64*)(src))[0]; } + #ifdef STBIR_SIMD + #define STBIR_MOVE_4( dest, src ) { stbir__simdf t; STBIR_NO_UNROLL(dest); stbir__simdf_load( t, src ); stbir__simdf_store( dest, t ); } + #else + #define STBIR_MOVE_4( dest, src ) { STBIR_NO_UNROLL(dest); ((stbir_uint64*)(dest))[0] = ((stbir_uint64*)(src))[0]; ((stbir_uint64*)(dest))[1] = ((stbir_uint64*)(src))[1]; } + #endif + if ( coefficient_width != widest ) + { + float * pc = coefficents; + float * coeffs = coefficents; + float * pc_end = coefficents + num_contributors * widest; + switch( widest ) + { + case 1: + do { + STBIR_MOVE_1( pc, coeffs ); + ++pc; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 2: + do { + STBIR_MOVE_2( pc, coeffs ); + pc += 2; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 3: + do { + STBIR_MOVE_2( pc, coeffs ); + STBIR_MOVE_1( pc+2, coeffs+2 ); + pc += 3; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 4: + do { + STBIR_MOVE_4( pc, coeffs ); + pc += 4; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 5: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_1( pc+4, coeffs+4 ); + pc += 5; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 6: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_2( pc+4, coeffs+4 ); + pc += 6; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 7: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_2( pc+4, coeffs+4 ); + STBIR_MOVE_1( pc+6, coeffs+6 ); + pc += 7; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 8: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + pc += 8; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 9: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + STBIR_MOVE_1( pc+8, coeffs+8 ); + pc += 9; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 10: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + STBIR_MOVE_2( pc+8, coeffs+8 ); + pc += 10; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 11: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + STBIR_MOVE_2( pc+8, coeffs+8 ); + STBIR_MOVE_1( pc+10, coeffs+10 ); + pc += 11; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 12: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + STBIR_MOVE_4( pc+8, coeffs+8 ); + pc += 12; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + default: + do { + float * copy_end = pc + widest - 4; + float * c = coeffs; + do { + STBIR_NO_UNROLL( pc ); + STBIR_MOVE_4( pc, c ); + pc += 4; + c += 4; + } while ( pc <= copy_end ); + copy_end += 4; + while ( pc < copy_end ) + { + STBIR_MOVE_1( pc, c ); + ++pc; ++c; + } + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + } + } + + // some horizontal routines read one float off the end (which is then masked off), so put in a sentinal so we don't read an snan or denormal + coefficents[ widest * num_contributors ] = 8888.0f; + + // the minimum we might read for unrolled filters widths is 12. So, we need to + // make sure we never read outside the decode buffer, by possibly moving + // the sample area back into the scanline, and putting zeros weights first. + // we start on the right edge and check until we're well past the possible + // clip area (2*widest). + { + stbir__contributors * contribs = contributors + num_contributors - 1; + float * coeffs = coefficents + widest * ( num_contributors - 1 ); + + // go until no chance of clipping (this is usually less than 8 lops) + while ( ( contribs >= contributors ) && ( ( contribs->n0 + widest*2 ) >= row_width ) ) + { + // might we clip?? + if ( ( contribs->n0 + widest ) > row_width ) + { + int stop_range = widest; + + // if range is larger than 12, it will be handled by generic loops that can terminate on the exact length + // of this contrib n1, instead of a fixed widest amount - so calculate this + if ( widest > 12 ) + { + int mod; + + // how far will be read in the n_coeff loop (which depends on the widest count mod4); + mod = widest & 3; + stop_range = ( ( ( contribs->n1 - contribs->n0 + 1 ) - mod + 3 ) & ~3 ) + mod; + + // the n_coeff loops do a minimum amount of coeffs, so factor that in! + if ( stop_range < ( 8 + mod ) ) stop_range = 8 + mod; + } + + // now see if we still clip with the refined range + if ( ( contribs->n0 + stop_range ) > row_width ) + { + int new_n0 = row_width - stop_range; + int num = contribs->n1 - contribs->n0 + 1; + int backup = contribs->n0 - new_n0; + float * from_co = coeffs + num - 1; + float * to_co = from_co + backup; + + STBIR_ASSERT( ( new_n0 >= 0 ) && ( new_n0 < contribs->n0 ) ); + + // move the coeffs over + while( num ) + { + *to_co-- = *from_co--; + --num; + } + // zero new positions + while ( to_co >= coeffs ) + *to_co-- = 0; + // set new start point + contribs->n0 = new_n0; + if ( widest > 12 ) + { + int mod; + + // how far will be read in the n_coeff loop (which depends on the widest count mod4); + mod = widest & 3; + stop_range = ( ( ( contribs->n1 - contribs->n0 + 1 ) - mod + 3 ) & ~3 ) + mod; + + // the n_coeff loops do a minimum amount of coeffs, so factor that in! + if ( stop_range < ( 8 + mod ) ) stop_range = 8 + mod; + } + } + } + --contribs; + coeffs -= widest; + } + } + + return widest; + #undef STBIR_MOVE_1 + #undef STBIR_MOVE_2 + #undef STBIR_MOVE_4 +} + +static void stbir__calculate_filters( stbir__sampler * samp, stbir__sampler * other_axis_for_pivot, void * user_data STBIR_ONLY_PROFILE_BUILD_GET_INFO ) +{ + int n; + float scale = samp->scale_info.scale; + stbir__kernel_callback * kernel = samp->filter_kernel; + stbir__support_callback * support = samp->filter_support; + float inv_scale = samp->scale_info.inv_scale; + int input_full_size = samp->scale_info.input_full_size; + int gather_num_contributors = samp->num_contributors; + stbir__contributors* gather_contributors = samp->contributors; + float * gather_coeffs = samp->coefficients; + int gather_coefficient_width = samp->coefficient_width; + + switch ( samp->is_gather ) + { + case 1: // gather upsample + { + float out_pixels_radius = support(inv_scale,user_data) * scale; + + stbir__calculate_coefficients_for_gather_upsample( out_pixels_radius, kernel, &samp->scale_info, gather_num_contributors, gather_contributors, gather_coeffs, gather_coefficient_width, samp->edge, user_data ); + + STBIR_PROFILE_BUILD_START( cleanup ); + stbir__cleanup_gathered_coefficients( samp->edge, &samp->extent_info, &samp->scale_info, gather_num_contributors, gather_contributors, gather_coeffs, gather_coefficient_width ); + STBIR_PROFILE_BUILD_END( cleanup ); + } + break; + + case 0: // scatter downsample (only on vertical) + case 2: // gather downsample + { + float in_pixels_radius = support(scale,user_data) * inv_scale; + int filter_pixel_margin = samp->filter_pixel_margin; + int input_end = input_full_size + filter_pixel_margin; + + // if this is a scatter, we do a downsample gather to get the coeffs, and then pivot after + if ( !samp->is_gather ) + { + // check if we are using the same gather downsample on the horizontal as this vertical, + // if so, then we don't have to generate them, we can just pivot from the horizontal. + if ( other_axis_for_pivot ) + { + gather_contributors = other_axis_for_pivot->contributors; + gather_coeffs = other_axis_for_pivot->coefficients; + gather_coefficient_width = other_axis_for_pivot->coefficient_width; + gather_num_contributors = other_axis_for_pivot->num_contributors; + samp->extent_info.lowest = other_axis_for_pivot->extent_info.lowest; + samp->extent_info.highest = other_axis_for_pivot->extent_info.highest; + samp->extent_info.widest = other_axis_for_pivot->extent_info.widest; + goto jump_right_to_pivot; + } + + gather_contributors = samp->gather_prescatter_contributors; + gather_coeffs = samp->gather_prescatter_coefficients; + gather_coefficient_width = samp->gather_prescatter_coefficient_width; + gather_num_contributors = samp->gather_prescatter_num_contributors; + } + + stbir__calculate_coefficients_for_gather_downsample( -filter_pixel_margin, input_end, in_pixels_radius, kernel, &samp->scale_info, gather_coefficient_width, gather_num_contributors, gather_contributors, gather_coeffs, user_data ); + + STBIR_PROFILE_BUILD_START( cleanup ); + stbir__cleanup_gathered_coefficients( samp->edge, &samp->extent_info, &samp->scale_info, gather_num_contributors, gather_contributors, gather_coeffs, gather_coefficient_width ); + STBIR_PROFILE_BUILD_END( cleanup ); + + if ( !samp->is_gather ) + { + // if this is a scatter (vertical only), then we need to pivot the coeffs + stbir__contributors * scatter_contributors; + int highest_set; + + jump_right_to_pivot: + + STBIR_PROFILE_BUILD_START( pivot ); + + highest_set = (-filter_pixel_margin) - 1; + for (n = 0; n < gather_num_contributors; n++) + { + int k; + int gn0 = gather_contributors->n0, gn1 = gather_contributors->n1; + int scatter_coefficient_width = samp->coefficient_width; + float * scatter_coeffs = samp->coefficients + ( gn0 + filter_pixel_margin ) * scatter_coefficient_width; + float * g_coeffs = gather_coeffs; + scatter_contributors = samp->contributors + ( gn0 + filter_pixel_margin ); + + for (k = gn0 ; k <= gn1 ; k++ ) + { + float gc = *g_coeffs++; + if ( ( k > highest_set ) || ( scatter_contributors->n0 > scatter_contributors->n1 ) ) + { + { + // if we are skipping over several contributors, we need to clear the skipped ones + stbir__contributors * clear_contributors = samp->contributors + ( highest_set + filter_pixel_margin + 1); + while ( clear_contributors < scatter_contributors ) + { + clear_contributors->n0 = 0; + clear_contributors->n1 = -1; + ++clear_contributors; + } + } + scatter_contributors->n0 = n; + scatter_contributors->n1 = n; + scatter_coeffs[0] = gc; + highest_set = k; + } + else + { + stbir__insert_coeff( scatter_contributors, scatter_coeffs, n, gc ); + } + ++scatter_contributors; + scatter_coeffs += scatter_coefficient_width; + } + + ++gather_contributors; + gather_coeffs += gather_coefficient_width; + } + + // now clear any unset contribs + { + stbir__contributors * clear_contributors = samp->contributors + ( highest_set + filter_pixel_margin + 1); + stbir__contributors * end_contributors = samp->contributors + samp->num_contributors; + while ( clear_contributors < end_contributors ) + { + clear_contributors->n0 = 0; + clear_contributors->n1 = -1; + ++clear_contributors; + } + } + + STBIR_PROFILE_BUILD_END( pivot ); + } + } + break; + } +} + + +//======================================================================================================== +// scanline decoders and encoders + +#define stbir__coder_min_num 1 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + +#define stbir__decode_suffix BGRA +#define stbir__decode_swizzle +#define stbir__decode_order0 2 +#define stbir__decode_order1 1 +#define stbir__decode_order2 0 +#define stbir__decode_order3 3 +#define stbir__encode_order0 2 +#define stbir__encode_order1 1 +#define stbir__encode_order2 0 +#define stbir__encode_order3 3 +#define stbir__coder_min_num 4 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + +#define stbir__decode_suffix ARGB +#define stbir__decode_swizzle +#define stbir__decode_order0 1 +#define stbir__decode_order1 2 +#define stbir__decode_order2 3 +#define stbir__decode_order3 0 +#define stbir__encode_order0 3 +#define stbir__encode_order1 0 +#define stbir__encode_order2 1 +#define stbir__encode_order3 2 +#define stbir__coder_min_num 4 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + +#define stbir__decode_suffix ABGR +#define stbir__decode_swizzle +#define stbir__decode_order0 3 +#define stbir__decode_order1 2 +#define stbir__decode_order2 1 +#define stbir__decode_order3 0 +#define stbir__encode_order0 3 +#define stbir__encode_order1 2 +#define stbir__encode_order2 1 +#define stbir__encode_order3 0 +#define stbir__coder_min_num 4 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + +#define stbir__decode_suffix AR +#define stbir__decode_swizzle +#define stbir__decode_order0 1 +#define stbir__decode_order1 0 +#define stbir__decode_order2 3 +#define stbir__decode_order3 2 +#define stbir__encode_order0 1 +#define stbir__encode_order1 0 +#define stbir__encode_order2 3 +#define stbir__encode_order3 2 +#define stbir__coder_min_num 2 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + + +// fancy alpha means we expand to keep both premultipied and non-premultiplied color channels +static void stbir__fancy_alpha_weight_4ch( float * out_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) out = out_buffer; + float const * end_decode = out_buffer + ( width_times_channels / 4 ) * 7; // decode buffer aligned to end of out_buffer + float STBIR_STREAMOUT_PTR(*) decode = (float*)end_decode - width_times_channels; + + // fancy alpha is stored internally as R G B A Rpm Gpm Bpm + + #ifdef STBIR_SIMD + + #ifdef STBIR_SIMD8 + decode += 16; + while ( decode <= end_decode ) + { + stbir__simdf8 d0,d1,a0,a1,p0,p1; + STBIR_NO_UNROLL(decode); + stbir__simdf8_load( d0, decode-16 ); + stbir__simdf8_load( d1, decode-16+8 ); + stbir__simdf8_0123to33333333( a0, d0 ); + stbir__simdf8_0123to33333333( a1, d1 ); + stbir__simdf8_mult( p0, a0, d0 ); + stbir__simdf8_mult( p1, a1, d1 ); + stbir__simdf8_bot4s( a0, d0, p0 ); + stbir__simdf8_bot4s( a1, d1, p1 ); + stbir__simdf8_top4s( d0, d0, p0 ); + stbir__simdf8_top4s( d1, d1, p1 ); + stbir__simdf8_store ( out, a0 ); + stbir__simdf8_store ( out+7, d0 ); + stbir__simdf8_store ( out+14, a1 ); + stbir__simdf8_store ( out+21, d1 ); + decode += 16; + out += 28; + } + decode -= 16; + #else + decode += 8; + while ( decode <= end_decode ) + { + stbir__simdf d0,a0,d1,a1,p0,p1; + STBIR_NO_UNROLL(decode); + stbir__simdf_load( d0, decode-8 ); + stbir__simdf_load( d1, decode-8+4 ); + stbir__simdf_0123to3333( a0, d0 ); + stbir__simdf_0123to3333( a1, d1 ); + stbir__simdf_mult( p0, a0, d0 ); + stbir__simdf_mult( p1, a1, d1 ); + stbir__simdf_store ( out, d0 ); + stbir__simdf_store ( out+4, p0 ); + stbir__simdf_store ( out+7, d1 ); + stbir__simdf_store ( out+7+4, p1 ); + decode += 8; + out += 14; + } + decode -= 8; + #endif + + // might be one last odd pixel + #ifdef STBIR_SIMD8 + while ( decode < end_decode ) + #else + if ( decode < end_decode ) + #endif + { + stbir__simdf d,a,p; + stbir__simdf_load( d, decode ); + stbir__simdf_0123to3333( a, d ); + stbir__simdf_mult( p, a, d ); + stbir__simdf_store ( out, d ); + stbir__simdf_store ( out+4, p ); + decode += 4; + out += 7; + } + + #else + + while( decode < end_decode ) + { + float r = decode[0], g = decode[1], b = decode[2], alpha = decode[3]; + out[0] = r; + out[1] = g; + out[2] = b; + out[3] = alpha; + out[4] = r * alpha; + out[5] = g * alpha; + out[6] = b * alpha; + out += 7; + decode += 4; + } + + #endif +} + +static void stbir__fancy_alpha_weight_2ch( float * out_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) out = out_buffer; + float const * end_decode = out_buffer + ( width_times_channels / 2 ) * 3; + float STBIR_STREAMOUT_PTR(*) decode = (float*)end_decode - width_times_channels; + + // for fancy alpha, turns into: [X A Xpm][X A Xpm],etc + + #ifdef STBIR_SIMD + + decode += 8; + if ( decode <= end_decode ) + { + do { + #ifdef STBIR_SIMD8 + stbir__simdf8 d0,a0,p0; + STBIR_NO_UNROLL(decode); + stbir__simdf8_load( d0, decode-8 ); + stbir__simdf8_0123to11331133( p0, d0 ); + stbir__simdf8_0123to00220022( a0, d0 ); + stbir__simdf8_mult( p0, p0, a0 ); + + stbir__simdf_store2( out, stbir__if_simdf8_cast_to_simdf4( d0 ) ); + stbir__simdf_store( out+2, stbir__if_simdf8_cast_to_simdf4( p0 ) ); + stbir__simdf_store2h( out+3, stbir__if_simdf8_cast_to_simdf4( d0 ) ); + + stbir__simdf_store2( out+6, stbir__simdf8_gettop4( d0 ) ); + stbir__simdf_store( out+8, stbir__simdf8_gettop4( p0 ) ); + stbir__simdf_store2h( out+9, stbir__simdf8_gettop4( d0 ) ); + #else + stbir__simdf d0,a0,d1,a1,p0,p1; + STBIR_NO_UNROLL(decode); + stbir__simdf_load( d0, decode-8 ); + stbir__simdf_load( d1, decode-8+4 ); + stbir__simdf_0123to1133( p0, d0 ); + stbir__simdf_0123to1133( p1, d1 ); + stbir__simdf_0123to0022( a0, d0 ); + stbir__simdf_0123to0022( a1, d1 ); + stbir__simdf_mult( p0, p0, a0 ); + stbir__simdf_mult( p1, p1, a1 ); + + stbir__simdf_store2( out, d0 ); + stbir__simdf_store( out+2, p0 ); + stbir__simdf_store2h( out+3, d0 ); + + stbir__simdf_store2( out+6, d1 ); + stbir__simdf_store( out+8, p1 ); + stbir__simdf_store2h( out+9, d1 ); + #endif + decode += 8; + out += 12; + } while ( decode <= end_decode ); + } + decode -= 8; + #endif + + while( decode < end_decode ) + { + float x = decode[0], y = decode[1]; + STBIR_SIMD_NO_UNROLL(decode); + out[0] = x; + out[1] = y; + out[2] = x * y; + out += 3; + decode += 2; + } +} + +static void stbir__fancy_alpha_unweight_4ch( float * encode_buffer, int width_times_channels ) +{ + float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer; + float STBIR_SIMD_STREAMOUT_PTR(*) input = encode_buffer; + float const * end_output = encode_buffer + width_times_channels; + + // fancy RGBA is stored internally as R G B A Rpm Gpm Bpm + + do { + float alpha = input[3]; +#ifdef STBIR_SIMD + stbir__simdf i,ia; + STBIR_SIMD_NO_UNROLL(encode); + if ( alpha < stbir__small_float ) + { + stbir__simdf_load( i, input ); + stbir__simdf_store( encode, i ); + } + else + { + stbir__simdf_load1frep4( ia, 1.0f / alpha ); + stbir__simdf_load( i, input+4 ); + stbir__simdf_mult( i, i, ia ); + stbir__simdf_store( encode, i ); + encode[3] = alpha; + } +#else + if ( alpha < stbir__small_float ) + { + encode[0] = input[0]; + encode[1] = input[1]; + encode[2] = input[2]; + } + else + { + float ialpha = 1.0f / alpha; + encode[0] = input[4] * ialpha; + encode[1] = input[5] * ialpha; + encode[2] = input[6] * ialpha; + } + encode[3] = alpha; +#endif + + input += 7; + encode += 4; + } while ( encode < end_output ); +} + +// format: [X A Xpm][X A Xpm] etc +static void stbir__fancy_alpha_unweight_2ch( float * encode_buffer, int width_times_channels ) +{ + float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer; + float STBIR_SIMD_STREAMOUT_PTR(*) input = encode_buffer; + float const * end_output = encode_buffer + width_times_channels; + + do { + float alpha = input[1]; + encode[0] = input[0]; + if ( alpha >= stbir__small_float ) + encode[0] = input[2] / alpha; + encode[1] = alpha; + + input += 3; + encode += 2; + } while ( encode < end_output ); +} + +static void stbir__simple_alpha_weight_4ch( float * decode_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) decode = decode_buffer; + float const * end_decode = decode_buffer + width_times_channels; + + #ifdef STBIR_SIMD + { + decode += 2 * stbir__simdfX_float_count; + while ( decode <= end_decode ) + { + stbir__simdfX d0,a0,d1,a1; + STBIR_NO_UNROLL(decode); + stbir__simdfX_load( d0, decode-2*stbir__simdfX_float_count ); + stbir__simdfX_load( d1, decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count ); + stbir__simdfX_aaa1( a0, d0, STBIR_onesX ); + stbir__simdfX_aaa1( a1, d1, STBIR_onesX ); + stbir__simdfX_mult( d0, d0, a0 ); + stbir__simdfX_mult( d1, d1, a1 ); + stbir__simdfX_store ( decode-2*stbir__simdfX_float_count, d0 ); + stbir__simdfX_store ( decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count, d1 ); + decode += 2 * stbir__simdfX_float_count; + } + decode -= 2 * stbir__simdfX_float_count; + + // few last pixels remnants + #ifdef STBIR_SIMD8 + while ( decode < end_decode ) + #else + if ( decode < end_decode ) + #endif + { + stbir__simdf d,a; + stbir__simdf_load( d, decode ); + stbir__simdf_aaa1( a, d, STBIR__CONSTF(STBIR_ones) ); + stbir__simdf_mult( d, d, a ); + stbir__simdf_store ( decode, d ); + decode += 4; + } + } + + #else + + while( decode < end_decode ) + { + float alpha = decode[3]; + decode[0] *= alpha; + decode[1] *= alpha; + decode[2] *= alpha; + decode += 4; + } + + #endif +} + +static void stbir__simple_alpha_weight_2ch( float * decode_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) decode = decode_buffer; + float const * end_decode = decode_buffer + width_times_channels; + + #ifdef STBIR_SIMD + decode += 2 * stbir__simdfX_float_count; + while ( decode <= end_decode ) + { + stbir__simdfX d0,a0,d1,a1; + STBIR_NO_UNROLL(decode); + stbir__simdfX_load( d0, decode-2*stbir__simdfX_float_count ); + stbir__simdfX_load( d1, decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count ); + stbir__simdfX_a1a1( a0, d0, STBIR_onesX ); + stbir__simdfX_a1a1( a1, d1, STBIR_onesX ); + stbir__simdfX_mult( d0, d0, a0 ); + stbir__simdfX_mult( d1, d1, a1 ); + stbir__simdfX_store ( decode-2*stbir__simdfX_float_count, d0 ); + stbir__simdfX_store ( decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count, d1 ); + decode += 2 * stbir__simdfX_float_count; + } + decode -= 2 * stbir__simdfX_float_count; + #endif + + while( decode < end_decode ) + { + float alpha = decode[1]; + STBIR_SIMD_NO_UNROLL(decode); + decode[0] *= alpha; + decode += 2; + } +} + +static void stbir__simple_alpha_unweight_4ch( float * encode_buffer, int width_times_channels ) +{ + float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer; + float const * end_output = encode_buffer + width_times_channels; + + do { + float alpha = encode[3]; + +#ifdef STBIR_SIMD + stbir__simdf i,ia; + STBIR_SIMD_NO_UNROLL(encode); + if ( alpha >= stbir__small_float ) + { + stbir__simdf_load1frep4( ia, 1.0f / alpha ); + stbir__simdf_load( i, encode ); + stbir__simdf_mult( i, i, ia ); + stbir__simdf_store( encode, i ); + encode[3] = alpha; + } +#else + if ( alpha >= stbir__small_float ) + { + float ialpha = 1.0f / alpha; + encode[0] *= ialpha; + encode[1] *= ialpha; + encode[2] *= ialpha; + } +#endif + encode += 4; + } while ( encode < end_output ); +} + +static void stbir__simple_alpha_unweight_2ch( float * encode_buffer, int width_times_channels ) +{ + float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer; + float const * end_output = encode_buffer + width_times_channels; + + do { + float alpha = encode[1]; + if ( alpha >= stbir__small_float ) + encode[0] /= alpha; + encode += 2; + } while ( encode < end_output ); +} + + +// only used in RGB->BGR or BGR->RGB +static void stbir__simple_flip_3ch( float * decode_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) decode = decode_buffer; + float const * end_decode = decode_buffer + width_times_channels; + + decode += 12; + while( decode <= end_decode ) + { + float t0,t1,t2,t3; + STBIR_NO_UNROLL(decode); + t0 = decode[0]; t1 = decode[3]; t2 = decode[6]; t3 = decode[9]; + decode[0] = decode[2]; decode[3] = decode[5]; decode[6] = decode[8]; decode[9] = decode[11]; + decode[2] = t0; decode[5] = t1; decode[8] = t2; decode[11] = t3; + decode += 12; + } + decode -= 12; + + while( decode < end_decode ) + { + float t = decode[0]; + STBIR_NO_UNROLL(decode); + decode[0] = decode[2]; + decode[2] = t; + decode += 3; + } +} + + + +static void stbir__decode_scanline(stbir__info const * stbir_info, int n, float * output_buffer STBIR_ONLY_PROFILE_GET_SPLIT_INFO ) +{ + int channels = stbir_info->channels; + int effective_channels = stbir_info->effective_channels; + int input_sample_in_bytes = stbir__type_size[stbir_info->input_type] * channels; + stbir_edge edge_horizontal = stbir_info->horizontal.edge; + stbir_edge edge_vertical = stbir_info->vertical.edge; + int row = stbir__edge_wrap(edge_vertical, n, stbir_info->vertical.scale_info.input_full_size); + const void* input_plane_data = ( (char *) stbir_info->input_data ) + (ptrdiff_t)row * (ptrdiff_t) stbir_info->input_stride_bytes; + stbir__span const * spans = stbir_info->scanline_extents.spans; + float* full_decode_buffer = output_buffer - stbir_info->scanline_extents.conservative.n0 * effective_channels; + + // if we are on edge_zero, and we get in here with an out of bounds n, then the calculate filters has failed + STBIR_ASSERT( !(edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->vertical.scale_info.input_full_size)) ); + + do + { + float * decode_buffer; + void const * input_data; + float * end_decode; + int width_times_channels; + int width; + + if ( spans->n1 < spans->n0 ) + break; + + width = spans->n1 + 1 - spans->n0; + decode_buffer = full_decode_buffer + spans->n0 * effective_channels; + end_decode = full_decode_buffer + ( spans->n1 + 1 ) * effective_channels; + width_times_channels = width * channels; + + // read directly out of input plane by default + input_data = ( (char*)input_plane_data ) + spans->pixel_offset_for_input * input_sample_in_bytes; + + // if we have an input callback, call it to get the input data + if ( stbir_info->in_pixels_cb ) + { + // call the callback with a temp buffer (that they can choose to use or not). the temp is just right aligned memory in the decode_buffer itself + input_data = stbir_info->in_pixels_cb( ( (char*) end_decode ) - ( width * input_sample_in_bytes ), input_plane_data, width, spans->pixel_offset_for_input, row, stbir_info->user_data ); + } + + STBIR_PROFILE_START( decode ); + // convert the pixels info the float decode_buffer, (we index from end_decode, so that when channelsdecode_pixels( (float*)end_decode - width_times_channels, width_times_channels, input_data ); + STBIR_PROFILE_END( decode ); + + if (stbir_info->alpha_weight) + { + STBIR_PROFILE_START( alpha ); + stbir_info->alpha_weight( decode_buffer, width_times_channels ); + STBIR_PROFILE_END( alpha ); + } + + ++spans; + } while ( spans <= ( &stbir_info->scanline_extents.spans[1] ) ); + + // handle the edge_wrap filter (all other types are handled back out at the calculate_filter stage) + // basically the idea here is that if we have the whole scanline in memory, we don't redecode the + // wrapped edge pixels, and instead just memcpy them from the scanline into the edge positions + if ( ( edge_horizontal == STBIR_EDGE_WRAP ) && ( stbir_info->scanline_extents.edge_sizes[0] | stbir_info->scanline_extents.edge_sizes[1] ) ) + { + // this code only runs if we're in edge_wrap, and we're doing the entire scanline + int e, start_x[2]; + int input_full_size = stbir_info->horizontal.scale_info.input_full_size; + + start_x[0] = -stbir_info->scanline_extents.edge_sizes[0]; // left edge start x + start_x[1] = input_full_size; // right edge + + for( e = 0; e < 2 ; e++ ) + { + // do each margin + int margin = stbir_info->scanline_extents.edge_sizes[e]; + if ( margin ) + { + int x = start_x[e]; + float * marg = full_decode_buffer + x * effective_channels; + float const * src = full_decode_buffer + stbir__edge_wrap(edge_horizontal, x, input_full_size) * effective_channels; + STBIR_MEMCPY( marg, src, margin * effective_channels * sizeof(float) ); + } + } + } +} + + +//================= +// Do 1 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc ); \ + stbir__simdf_mult1_mem( tot, c, decode ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot,c,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2z( c, hc ); \ + stbir__simdf_load2( d, decode ); \ + stbir__simdf_mult( tot, c, d ); \ + stbir__simdf_0123to1230( c, tot ); \ + stbir__simdf_add1( tot, tot, c ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot,c,t; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( c, hc ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + stbir__simdf_0123to1230( c, tot ); \ + stbir__simdf_0123to2301( t, tot ); \ + stbir__simdf_add1( tot, tot, c ); \ + stbir__simdf_add1( tot, tot, t ); + +#define stbir__store_output_tiny() \ + stbir__simdf_store1( output, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 1; + +#define stbir__4_coeff_start() \ + stbir__simdf tot,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( c, hc ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( c, hc + (ofs) ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+(ofs) ); + +#define stbir__1_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + stbir__simdf_load1z( c, hc + (ofs) ); \ + stbir__simdf_load1( d, decode + (ofs) ); \ + stbir__simdf_madd( tot, tot, d, c ); } + +#define stbir__2_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + stbir__simdf_load2z( c, hc+(ofs) ); \ + stbir__simdf_load2( d, decode+(ofs) ); \ + stbir__simdf_madd( tot, tot, d, c ); } + +#define stbir__3_coeff_setup() \ + stbir__simdf mask; \ + stbir__simdf_load( mask, STBIR_mask + 3 ); + +#define stbir__3_coeff_remnant( ofs ) \ + stbir__simdf_load( c, hc+(ofs) ); \ + stbir__simdf_and( c, c, mask ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+(ofs) ); + +#define stbir__store_output() \ + stbir__simdf_0123to2301( c, tot ); \ + stbir__simdf_add( tot, tot, c ); \ + stbir__simdf_0123to1230( c, tot ); \ + stbir__simdf_add1( tot, tot, c ); \ + stbir__simdf_store1( output, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 1; + +#else + +#define stbir__1_coeff_only() \ + float tot; \ + tot = decode[0]*hc[0]; + +#define stbir__2_coeff_only() \ + float tot; \ + tot = decode[0] * hc[0]; \ + tot += decode[1] * hc[1]; + +#define stbir__3_coeff_only() \ + float tot; \ + tot = decode[0] * hc[0]; \ + tot += decode[1] * hc[1]; \ + tot += decode[2] * hc[2]; + +#define stbir__store_output_tiny() \ + output[0] = tot; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 1; + +#define stbir__4_coeff_start() \ + float tot0,tot1,tot2,tot3; \ + tot0 = decode[0] * hc[0]; \ + tot1 = decode[1] * hc[1]; \ + tot2 = decode[2] * hc[2]; \ + tot3 = decode[3] * hc[3]; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + tot0 += decode[0+(ofs)] * hc[0+(ofs)]; \ + tot1 += decode[1+(ofs)] * hc[1+(ofs)]; \ + tot2 += decode[2+(ofs)] * hc[2+(ofs)]; \ + tot3 += decode[3+(ofs)] * hc[3+(ofs)]; + +#define stbir__1_coeff_remnant( ofs ) \ + tot0 += decode[0+(ofs)] * hc[0+(ofs)]; + +#define stbir__2_coeff_remnant( ofs ) \ + tot0 += decode[0+(ofs)] * hc[0+(ofs)]; \ + tot1 += decode[1+(ofs)] * hc[1+(ofs)]; \ + +#define stbir__3_coeff_remnant( ofs ) \ + tot0 += decode[0+(ofs)] * hc[0+(ofs)]; \ + tot1 += decode[1+(ofs)] * hc[1+(ofs)]; \ + tot2 += decode[2+(ofs)] * hc[2+(ofs)]; + +#define stbir__store_output() \ + output[0] = (tot0+tot2)+(tot1+tot3); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 1; + +#endif + +#define STBIR__horizontal_channels 1 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + + +//================= +// Do 2 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot,c,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1z( c, hc ); \ + stbir__simdf_0123to0011( c, c ); \ + stbir__simdf_load2( d, decode ); \ + stbir__simdf_mult( tot, d, c ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( c, hc ); \ + stbir__simdf_0123to0011( c, c ); \ + stbir__simdf_mult_mem( tot, c, decode ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot,c,cs,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_load2z( d, decode+4 ); \ + stbir__simdf_madd( tot, tot, d, c ); + +#define stbir__store_output_tiny() \ + stbir__simdf_0123to2301( c, tot ); \ + stbir__simdf_add( tot, tot, c ); \ + stbir__simdf_store2( output, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; + +#ifdef STBIR_SIMD8 + +#define stbir__4_coeff_start() \ + stbir__simdf8 tot0,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc ); \ + stbir__simdf8_0123to00112233( c, cs ); \ + stbir__simdf8_mult_mem( tot0, c, decode ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00112233( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); + +#define stbir__1_coeff_remnant( ofs ) \ + { stbir__simdf t; \ + stbir__simdf_load1z( t, hc + (ofs) ); \ + stbir__simdf_0123to0011( t, t ); \ + stbir__simdf_mult_mem( t, t, decode+(ofs)*2 ); \ + stbir__simdf8_add4( tot0, tot0, t ); } + +#define stbir__2_coeff_remnant( ofs ) \ + { stbir__simdf t; \ + stbir__simdf_load2( t, hc + (ofs) ); \ + stbir__simdf_0123to0011( t, t ); \ + stbir__simdf_mult_mem( t, t, decode+(ofs)*2 ); \ + stbir__simdf8_add4( tot0, tot0, t ); } + +#define stbir__3_coeff_remnant( ofs ) \ + { stbir__simdf8 d; \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00112233( c, cs ); \ + stbir__simdf8_load6z( d, decode+(ofs)*2 ); \ + stbir__simdf8_madd( tot0, tot0, c, d ); } + +#define stbir__store_output() \ + { stbir__simdf t,d; \ + stbir__simdf8_add4halves( t, stbir__if_simdf8_cast_to_simdf4(tot0), tot0 ); \ + stbir__simdf_0123to2301( d, t ); \ + stbir__simdf_add( t, t, d ); \ + stbir__simdf_store2( output, t ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; } + +#else + +#define stbir__4_coeff_start() \ + stbir__simdf tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_0123to2233( c, cs ); \ + stbir__simdf_mult_mem( tot1, c, decode+4 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); \ + stbir__simdf_0123to2233( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*2+4 ); + +#define stbir__1_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + stbir__simdf_load1z( cs, hc + (ofs) ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_load2( d, decode + (ofs) * 2 ); \ + stbir__simdf_madd( tot0, tot0, d, c ); } + +#define stbir__2_coeff_remnant( ofs ) \ + stbir__simdf_load2( cs, hc + (ofs) ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); + +#define stbir__3_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_load2z( d, decode + (ofs) * 2 + 4 ); \ + stbir__simdf_madd( tot1, tot1, d, c ); } + +#define stbir__store_output() \ + stbir__simdf_add( tot0, tot0, tot1 ); \ + stbir__simdf_0123to2301( c, tot0 ); \ + stbir__simdf_add( tot0, tot0, c ); \ + stbir__simdf_store2( output, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; + +#endif + +#else + +#define stbir__1_coeff_only() \ + float tota,totb,c; \ + c = hc[0]; \ + tota = decode[0]*c; \ + totb = decode[1]*c; + +#define stbir__2_coeff_only() \ + float tota,totb,c; \ + c = hc[0]; \ + tota = decode[0]*c; \ + totb = decode[1]*c; \ + c = hc[1]; \ + tota += decode[2]*c; \ + totb += decode[3]*c; + +// this weird order of add matches the simd +#define stbir__3_coeff_only() \ + float tota,totb,c; \ + c = hc[0]; \ + tota = decode[0]*c; \ + totb = decode[1]*c; \ + c = hc[2]; \ + tota += decode[4]*c; \ + totb += decode[5]*c; \ + c = hc[1]; \ + tota += decode[2]*c; \ + totb += decode[3]*c; + +#define stbir__store_output_tiny() \ + output[0] = tota; \ + output[1] = totb; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; + +#define stbir__4_coeff_start() \ + float tota0,tota1,tota2,tota3,totb0,totb1,totb2,totb3,c; \ + c = hc[0]; \ + tota0 = decode[0]*c; \ + totb0 = decode[1]*c; \ + c = hc[1]; \ + tota1 = decode[2]*c; \ + totb1 = decode[3]*c; \ + c = hc[2]; \ + tota2 = decode[4]*c; \ + totb2 = decode[5]*c; \ + c = hc[3]; \ + tota3 = decode[6]*c; \ + totb3 = decode[7]*c; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*2]*c; \ + totb0 += decode[1+(ofs)*2]*c; \ + c = hc[1+(ofs)]; \ + tota1 += decode[2+(ofs)*2]*c; \ + totb1 += decode[3+(ofs)*2]*c; \ + c = hc[2+(ofs)]; \ + tota2 += decode[4+(ofs)*2]*c; \ + totb2 += decode[5+(ofs)*2]*c; \ + c = hc[3+(ofs)]; \ + tota3 += decode[6+(ofs)*2]*c; \ + totb3 += decode[7+(ofs)*2]*c; + +#define stbir__1_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*2] * c; \ + totb0 += decode[1+(ofs)*2] * c; + +#define stbir__2_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*2] * c; \ + totb0 += decode[1+(ofs)*2] * c; \ + c = hc[1+(ofs)]; \ + tota1 += decode[2+(ofs)*2] * c; \ + totb1 += decode[3+(ofs)*2] * c; + +#define stbir__3_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*2] * c; \ + totb0 += decode[1+(ofs)*2] * c; \ + c = hc[1+(ofs)]; \ + tota1 += decode[2+(ofs)*2] * c; \ + totb1 += decode[3+(ofs)*2] * c; \ + c = hc[2+(ofs)]; \ + tota2 += decode[4+(ofs)*2] * c; \ + totb2 += decode[5+(ofs)*2] * c; + +#define stbir__store_output() \ + output[0] = (tota0+tota2)+(tota1+tota3); \ + output[1] = (totb0+totb2)+(totb1+totb3); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; + +#endif + +#define STBIR__horizontal_channels 2 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + + +//================= +// Do 3 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot,c,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1z( c, hc ); \ + stbir__simdf_0123to0001( c, c ); \ + stbir__simdf_load( d, decode ); \ + stbir__simdf_mult( tot, d, c ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot,c,cs,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_load( d, decode ); \ + stbir__simdf_mult( tot, d, c ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_load( d, decode+3 ); \ + stbir__simdf_madd( tot, tot, d, c ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot,c,d,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_load( d, decode ); \ + stbir__simdf_mult( tot, d, c ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_load( d, decode+3 ); \ + stbir__simdf_madd( tot, tot, d, c ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_load( d, decode+6 ); \ + stbir__simdf_madd( tot, tot, d, c ); + +#define stbir__store_output_tiny() \ + stbir__simdf_store2( output, tot ); \ + stbir__simdf_0123to2301( tot, tot ); \ + stbir__simdf_store1( output+2, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; + +#ifdef STBIR_SIMD8 + +// we're loading from the XXXYYY decode by -1 to get the XXXYYY into different halves of the AVX reg fyi +#define stbir__4_coeff_start() \ + stbir__simdf8 tot0,tot1,c,cs; stbir__simdf t; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_mult_mem( tot0, c, decode - 1 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_mult_mem( tot1, c, decode+6 - 1 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*3 - 1 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*3 + 6 - 1 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1rep4( t, hc + (ofs) ); \ + stbir__simdf8_madd_mem4( tot0, tot0, t, decode+(ofs)*3 - 1 ); + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) - 2 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*3 - 1 ); + + #define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*3 - 1 ); \ + stbir__simdf8_0123to2222( t, cs ); \ + stbir__simdf8_madd_mem4( tot1, tot1, t, decode+(ofs)*3 + 6 - 1 ); + +#define stbir__store_output() \ + stbir__simdf8_add( tot0, tot0, tot1 ); \ + stbir__simdf_0123to1230( t, stbir__if_simdf8_cast_to_simdf4( tot0 ) ); \ + stbir__simdf8_add4halves( t, t, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; \ + if ( output < output_end ) \ + { \ + stbir__simdf_store( output-3, t ); \ + continue; \ + } \ + { stbir__simdf tt; stbir__simdf_0123to2301( tt, t ); \ + stbir__simdf_store2( output-3, t ); \ + stbir__simdf_store1( output+2-3, tt ); } \ + break; + + +#else + +#define stbir__4_coeff_start() \ + stbir__simdf tot0,tot1,tot2,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0001( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_0123to1122( c, cs ); \ + stbir__simdf_mult_mem( tot1, c, decode+4 ); \ + stbir__simdf_0123to2333( c, cs ); \ + stbir__simdf_mult_mem( tot2, c, decode+8 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0001( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); \ + stbir__simdf_0123to1122( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*3+4 ); \ + stbir__simdf_0123to2333( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*3+8 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1z( c, hc + (ofs) ); \ + stbir__simdf_0123to0001( c, c ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); + +#define stbir__2_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2z( cs, hc + (ofs) ); \ + stbir__simdf_0123to0001( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); \ + stbir__simdf_0123to1122( c, cs ); \ + stbir__simdf_load2z( d, decode+(ofs)*3+4 ); \ + stbir__simdf_madd( tot1, tot1, c, d ); } + +#define stbir__3_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0001( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); \ + stbir__simdf_0123to1122( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*3+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_load1z( d, decode+(ofs)*3+8 ); \ + stbir__simdf_madd( tot2, tot2, c, d ); } + +#define stbir__store_output() \ + stbir__simdf_0123ABCDto3ABx( c, tot0, tot1 ); \ + stbir__simdf_0123ABCDto23Ax( cs, tot1, tot2 ); \ + stbir__simdf_0123to1230( tot2, tot2 ); \ + stbir__simdf_add( tot0, tot0, cs ); \ + stbir__simdf_add( c, c, tot2 ); \ + stbir__simdf_add( tot0, tot0, c ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; \ + if ( output < output_end ) \ + { \ + stbir__simdf_store( output-3, tot0 ); \ + continue; \ + } \ + stbir__simdf_0123to2301( tot1, tot0 ); \ + stbir__simdf_store2( output-3, tot0 ); \ + stbir__simdf_store1( output+2-3, tot1 ); \ + break; + +#endif + +#else + +#define stbir__1_coeff_only() \ + float tot0, tot1, tot2, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; + +#define stbir__2_coeff_only() \ + float tot0, tot1, tot2, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + c = hc[1]; \ + tot0 += decode[3]*c; \ + tot1 += decode[4]*c; \ + tot2 += decode[5]*c; + +#define stbir__3_coeff_only() \ + float tot0, tot1, tot2, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + c = hc[1]; \ + tot0 += decode[3]*c; \ + tot1 += decode[4]*c; \ + tot2 += decode[5]*c; \ + c = hc[2]; \ + tot0 += decode[6]*c; \ + tot1 += decode[7]*c; \ + tot2 += decode[8]*c; + +#define stbir__store_output_tiny() \ + output[0] = tot0; \ + output[1] = tot1; \ + output[2] = tot2; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; + +#define stbir__4_coeff_start() \ + float tota0,tota1,tota2,totb0,totb1,totb2,totc0,totc1,totc2,totd0,totd1,totd2,c; \ + c = hc[0]; \ + tota0 = decode[0]*c; \ + tota1 = decode[1]*c; \ + tota2 = decode[2]*c; \ + c = hc[1]; \ + totb0 = decode[3]*c; \ + totb1 = decode[4]*c; \ + totb2 = decode[5]*c; \ + c = hc[2]; \ + totc0 = decode[6]*c; \ + totc1 = decode[7]*c; \ + totc2 = decode[8]*c; \ + c = hc[3]; \ + totd0 = decode[9]*c; \ + totd1 = decode[10]*c; \ + totd2 = decode[11]*c; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*3]*c; \ + tota1 += decode[1+(ofs)*3]*c; \ + tota2 += decode[2+(ofs)*3]*c; \ + c = hc[1+(ofs)]; \ + totb0 += decode[3+(ofs)*3]*c; \ + totb1 += decode[4+(ofs)*3]*c; \ + totb2 += decode[5+(ofs)*3]*c; \ + c = hc[2+(ofs)]; \ + totc0 += decode[6+(ofs)*3]*c; \ + totc1 += decode[7+(ofs)*3]*c; \ + totc2 += decode[8+(ofs)*3]*c; \ + c = hc[3+(ofs)]; \ + totd0 += decode[9+(ofs)*3]*c; \ + totd1 += decode[10+(ofs)*3]*c; \ + totd2 += decode[11+(ofs)*3]*c; + +#define stbir__1_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*3]*c; \ + tota1 += decode[1+(ofs)*3]*c; \ + tota2 += decode[2+(ofs)*3]*c; + +#define stbir__2_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*3]*c; \ + tota1 += decode[1+(ofs)*3]*c; \ + tota2 += decode[2+(ofs)*3]*c; \ + c = hc[1+(ofs)]; \ + totb0 += decode[3+(ofs)*3]*c; \ + totb1 += decode[4+(ofs)*3]*c; \ + totb2 += decode[5+(ofs)*3]*c; \ + +#define stbir__3_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*3]*c; \ + tota1 += decode[1+(ofs)*3]*c; \ + tota2 += decode[2+(ofs)*3]*c; \ + c = hc[1+(ofs)]; \ + totb0 += decode[3+(ofs)*3]*c; \ + totb1 += decode[4+(ofs)*3]*c; \ + totb2 += decode[5+(ofs)*3]*c; \ + c = hc[2+(ofs)]; \ + totc0 += decode[6+(ofs)*3]*c; \ + totc1 += decode[7+(ofs)*3]*c; \ + totc2 += decode[8+(ofs)*3]*c; + +#define stbir__store_output() \ + output[0] = (tota0+totc0)+(totb0+totd0); \ + output[1] = (tota1+totc1)+(totb1+totd1); \ + output[2] = (tota2+totc2)+(totb2+totd2); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; + +#endif + +#define STBIR__horizontal_channels 3 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + +//================= +// Do 4 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc ); \ + stbir__simdf_0123to0000( c, c ); \ + stbir__simdf_mult_mem( tot, c, decode ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+4 ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+8 ); + +#define stbir__store_output_tiny() \ + stbir__simdf_store( output, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#ifdef STBIR_SIMD8 + +#define stbir__4_coeff_start() \ + stbir__simdf8 tot0,c,cs; stbir__simdf t; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_mult_mem( tot0, c, decode ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+8 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4+8 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1rep4( t, hc + (ofs) ); \ + stbir__simdf8_madd_mem4( tot0, tot0, t, decode+(ofs)*4 ); + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) - 2 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); + + #define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf8_0123to2222( t, cs ); \ + stbir__simdf8_madd_mem4( tot0, tot0, t, decode+(ofs)*4+8 ); + +#define stbir__store_output() \ + stbir__simdf8_add4halves( t, stbir__if_simdf8_cast_to_simdf4(tot0), tot0 ); \ + stbir__simdf_store( output, t ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#else + +#define stbir__4_coeff_start() \ + stbir__simdf tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_mult_mem( tot1, c, decode+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+8 ); \ + stbir__simdf_0123to3333( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+12 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4+8 ); \ + stbir__simdf_0123to3333( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+12 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, c ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+4 ); + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4+8 ); + +#define stbir__store_output() \ + stbir__simdf_add( tot0, tot0, tot1 ); \ + stbir__simdf_store( output, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#endif + +#else + +#define stbir__1_coeff_only() \ + float p0,p1,p2,p3,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + p0 = decode[0] * c; \ + p1 = decode[1] * c; \ + p2 = decode[2] * c; \ + p3 = decode[3] * c; + +#define stbir__2_coeff_only() \ + float p0,p1,p2,p3,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + p0 = decode[0] * c; \ + p1 = decode[1] * c; \ + p2 = decode[2] * c; \ + p3 = decode[3] * c; \ + c = hc[1]; \ + p0 += decode[4] * c; \ + p1 += decode[5] * c; \ + p2 += decode[6] * c; \ + p3 += decode[7] * c; + +#define stbir__3_coeff_only() \ + float p0,p1,p2,p3,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + p0 = decode[0] * c; \ + p1 = decode[1] * c; \ + p2 = decode[2] * c; \ + p3 = decode[3] * c; \ + c = hc[1]; \ + p0 += decode[4] * c; \ + p1 += decode[5] * c; \ + p2 += decode[6] * c; \ + p3 += decode[7] * c; \ + c = hc[2]; \ + p0 += decode[8] * c; \ + p1 += decode[9] * c; \ + p2 += decode[10] * c; \ + p3 += decode[11] * c; + +#define stbir__store_output_tiny() \ + output[0] = p0; \ + output[1] = p1; \ + output[2] = p2; \ + output[3] = p3; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#define stbir__4_coeff_start() \ + float x0,x1,x2,x3,y0,y1,y2,y3,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + x0 = decode[0] * c; \ + x1 = decode[1] * c; \ + x2 = decode[2] * c; \ + x3 = decode[3] * c; \ + c = hc[1]; \ + y0 = decode[4] * c; \ + y1 = decode[5] * c; \ + y2 = decode[6] * c; \ + y3 = decode[7] * c; \ + c = hc[2]; \ + x0 += decode[8] * c; \ + x1 += decode[9] * c; \ + x2 += decode[10] * c; \ + x3 += decode[11] * c; \ + c = hc[3]; \ + y0 += decode[12] * c; \ + y1 += decode[13] * c; \ + y2 += decode[14] * c; \ + y3 += decode[15] * c; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*4] * c; \ + x1 += decode[1+(ofs)*4] * c; \ + x2 += decode[2+(ofs)*4] * c; \ + x3 += decode[3+(ofs)*4] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[4+(ofs)*4] * c; \ + y1 += decode[5+(ofs)*4] * c; \ + y2 += decode[6+(ofs)*4] * c; \ + y3 += decode[7+(ofs)*4] * c; \ + c = hc[2+(ofs)]; \ + x0 += decode[8+(ofs)*4] * c; \ + x1 += decode[9+(ofs)*4] * c; \ + x2 += decode[10+(ofs)*4] * c; \ + x3 += decode[11+(ofs)*4] * c; \ + c = hc[3+(ofs)]; \ + y0 += decode[12+(ofs)*4] * c; \ + y1 += decode[13+(ofs)*4] * c; \ + y2 += decode[14+(ofs)*4] * c; \ + y3 += decode[15+(ofs)*4] * c; + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*4] * c; \ + x1 += decode[1+(ofs)*4] * c; \ + x2 += decode[2+(ofs)*4] * c; \ + x3 += decode[3+(ofs)*4] * c; + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*4] * c; \ + x1 += decode[1+(ofs)*4] * c; \ + x2 += decode[2+(ofs)*4] * c; \ + x3 += decode[3+(ofs)*4] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[4+(ofs)*4] * c; \ + y1 += decode[5+(ofs)*4] * c; \ + y2 += decode[6+(ofs)*4] * c; \ + y3 += decode[7+(ofs)*4] * c; + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*4] * c; \ + x1 += decode[1+(ofs)*4] * c; \ + x2 += decode[2+(ofs)*4] * c; \ + x3 += decode[3+(ofs)*4] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[4+(ofs)*4] * c; \ + y1 += decode[5+(ofs)*4] * c; \ + y2 += decode[6+(ofs)*4] * c; \ + y3 += decode[7+(ofs)*4] * c; \ + c = hc[2+(ofs)]; \ + x0 += decode[8+(ofs)*4] * c; \ + x1 += decode[9+(ofs)*4] * c; \ + x2 += decode[10+(ofs)*4] * c; \ + x3 += decode[11+(ofs)*4] * c; + +#define stbir__store_output() \ + output[0] = x0 + y0; \ + output[1] = x1 + y1; \ + output[2] = x2 + y2; \ + output[3] = x3 + y3; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#endif + +#define STBIR__horizontal_channels 4 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + + + +//================= +// Do 7 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot0,tot1,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc ); \ + stbir__simdf_0123to0000( c, c ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_mult_mem( tot1, c, decode+3 ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_mult_mem( tot1, c, decode+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c,decode+10 ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_mult_mem( tot1, c, decode+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+10 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+14 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+17 ); + +#define stbir__store_output_tiny() \ + stbir__simdf_store( output+3, tot1 ); \ + stbir__simdf_store( output, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; + +#ifdef STBIR_SIMD8 + +#define stbir__4_coeff_start() \ + stbir__simdf8 tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc ); \ + stbir__simdf8_0123to00000000( c, cs ); \ + stbir__simdf8_mult_mem( tot0, c, decode ); \ + stbir__simdf8_0123to11111111( c, cs ); \ + stbir__simdf8_mult_mem( tot1, c, decode+7 ); \ + stbir__simdf8_0123to22222222( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+14 ); \ + stbir__simdf8_0123to33333333( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+21 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00000000( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf8_0123to11111111( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+7 ); \ + stbir__simdf8_0123to22222222( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); \ + stbir__simdf8_0123to33333333( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+21 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load1b( c, hc + (ofs) ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load1b( c, hc + (ofs) ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf8_load1b( c, hc + (ofs)+1 ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+7 ); + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00000000( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf8_0123to11111111( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+7 ); \ + stbir__simdf8_0123to22222222( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); + +#define stbir__store_output() \ + stbir__simdf8_add( tot0, tot0, tot1 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; \ + if ( output < output_end ) \ + { \ + stbir__simdf8_store( output-7, tot0 ); \ + continue; \ + } \ + stbir__simdf_store( output-7+3, stbir__simdf_swiz(stbir__simdf8_gettop4(tot0),0,0,1,2) ); \ + stbir__simdf_store( output-7, stbir__if_simdf8_cast_to_simdf4(tot0) ); \ + break; + +#else + +#define stbir__4_coeff_start() \ + stbir__simdf tot0,tot1,tot2,tot3,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_mult_mem( tot1, c, decode+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_mult_mem( tot2, c, decode+7 ); \ + stbir__simdf_mult_mem( tot3, c, decode+10 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+14 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+17 ); \ + stbir__simdf_0123to3333( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+21 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+24 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+7 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+10 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+17 ); \ + stbir__simdf_0123to3333( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+21 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+24 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, c ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \ + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+7 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+10 ); + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+7 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+10 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+17 ); + +#define stbir__store_output() \ + stbir__simdf_add( tot0, tot0, tot2 ); \ + stbir__simdf_add( tot1, tot1, tot3 ); \ + stbir__simdf_store( output+3, tot1 ); \ + stbir__simdf_store( output, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; + +#endif + +#else + +#define stbir__1_coeff_only() \ + float tot0, tot1, tot2, tot3, tot4, tot5, tot6, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + tot3 = decode[3]*c; \ + tot4 = decode[4]*c; \ + tot5 = decode[5]*c; \ + tot6 = decode[6]*c; + +#define stbir__2_coeff_only() \ + float tot0, tot1, tot2, tot3, tot4, tot5, tot6, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + tot3 = decode[3]*c; \ + tot4 = decode[4]*c; \ + tot5 = decode[5]*c; \ + tot6 = decode[6]*c; \ + c = hc[1]; \ + tot0 += decode[7]*c; \ + tot1 += decode[8]*c; \ + tot2 += decode[9]*c; \ + tot3 += decode[10]*c; \ + tot4 += decode[11]*c; \ + tot5 += decode[12]*c; \ + tot6 += decode[13]*c; \ + +#define stbir__3_coeff_only() \ + float tot0, tot1, tot2, tot3, tot4, tot5, tot6, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + tot3 = decode[3]*c; \ + tot4 = decode[4]*c; \ + tot5 = decode[5]*c; \ + tot6 = decode[6]*c; \ + c = hc[1]; \ + tot0 += decode[7]*c; \ + tot1 += decode[8]*c; \ + tot2 += decode[9]*c; \ + tot3 += decode[10]*c; \ + tot4 += decode[11]*c; \ + tot5 += decode[12]*c; \ + tot6 += decode[13]*c; \ + c = hc[2]; \ + tot0 += decode[14]*c; \ + tot1 += decode[15]*c; \ + tot2 += decode[16]*c; \ + tot3 += decode[17]*c; \ + tot4 += decode[18]*c; \ + tot5 += decode[19]*c; \ + tot6 += decode[20]*c; \ + +#define stbir__store_output_tiny() \ + output[0] = tot0; \ + output[1] = tot1; \ + output[2] = tot2; \ + output[3] = tot3; \ + output[4] = tot4; \ + output[5] = tot5; \ + output[6] = tot6; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; + +#define stbir__4_coeff_start() \ + float x0,x1,x2,x3,x4,x5,x6,y0,y1,y2,y3,y4,y5,y6,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + x0 = decode[0] * c; \ + x1 = decode[1] * c; \ + x2 = decode[2] * c; \ + x3 = decode[3] * c; \ + x4 = decode[4] * c; \ + x5 = decode[5] * c; \ + x6 = decode[6] * c; \ + c = hc[1]; \ + y0 = decode[7] * c; \ + y1 = decode[8] * c; \ + y2 = decode[9] * c; \ + y3 = decode[10] * c; \ + y4 = decode[11] * c; \ + y5 = decode[12] * c; \ + y6 = decode[13] * c; \ + c = hc[2]; \ + x0 += decode[14] * c; \ + x1 += decode[15] * c; \ + x2 += decode[16] * c; \ + x3 += decode[17] * c; \ + x4 += decode[18] * c; \ + x5 += decode[19] * c; \ + x6 += decode[20] * c; \ + c = hc[3]; \ + y0 += decode[21] * c; \ + y1 += decode[22] * c; \ + y2 += decode[23] * c; \ + y3 += decode[24] * c; \ + y4 += decode[25] * c; \ + y5 += decode[26] * c; \ + y6 += decode[27] * c; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*7] * c; \ + x1 += decode[1+(ofs)*7] * c; \ + x2 += decode[2+(ofs)*7] * c; \ + x3 += decode[3+(ofs)*7] * c; \ + x4 += decode[4+(ofs)*7] * c; \ + x5 += decode[5+(ofs)*7] * c; \ + x6 += decode[6+(ofs)*7] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[7+(ofs)*7] * c; \ + y1 += decode[8+(ofs)*7] * c; \ + y2 += decode[9+(ofs)*7] * c; \ + y3 += decode[10+(ofs)*7] * c; \ + y4 += decode[11+(ofs)*7] * c; \ + y5 += decode[12+(ofs)*7] * c; \ + y6 += decode[13+(ofs)*7] * c; \ + c = hc[2+(ofs)]; \ + x0 += decode[14+(ofs)*7] * c; \ + x1 += decode[15+(ofs)*7] * c; \ + x2 += decode[16+(ofs)*7] * c; \ + x3 += decode[17+(ofs)*7] * c; \ + x4 += decode[18+(ofs)*7] * c; \ + x5 += decode[19+(ofs)*7] * c; \ + x6 += decode[20+(ofs)*7] * c; \ + c = hc[3+(ofs)]; \ + y0 += decode[21+(ofs)*7] * c; \ + y1 += decode[22+(ofs)*7] * c; \ + y2 += decode[23+(ofs)*7] * c; \ + y3 += decode[24+(ofs)*7] * c; \ + y4 += decode[25+(ofs)*7] * c; \ + y5 += decode[26+(ofs)*7] * c; \ + y6 += decode[27+(ofs)*7] * c; + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*7] * c; \ + x1 += decode[1+(ofs)*7] * c; \ + x2 += decode[2+(ofs)*7] * c; \ + x3 += decode[3+(ofs)*7] * c; \ + x4 += decode[4+(ofs)*7] * c; \ + x5 += decode[5+(ofs)*7] * c; \ + x6 += decode[6+(ofs)*7] * c; \ + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*7] * c; \ + x1 += decode[1+(ofs)*7] * c; \ + x2 += decode[2+(ofs)*7] * c; \ + x3 += decode[3+(ofs)*7] * c; \ + x4 += decode[4+(ofs)*7] * c; \ + x5 += decode[5+(ofs)*7] * c; \ + x6 += decode[6+(ofs)*7] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[7+(ofs)*7] * c; \ + y1 += decode[8+(ofs)*7] * c; \ + y2 += decode[9+(ofs)*7] * c; \ + y3 += decode[10+(ofs)*7] * c; \ + y4 += decode[11+(ofs)*7] * c; \ + y5 += decode[12+(ofs)*7] * c; \ + y6 += decode[13+(ofs)*7] * c; \ + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*7] * c; \ + x1 += decode[1+(ofs)*7] * c; \ + x2 += decode[2+(ofs)*7] * c; \ + x3 += decode[3+(ofs)*7] * c; \ + x4 += decode[4+(ofs)*7] * c; \ + x5 += decode[5+(ofs)*7] * c; \ + x6 += decode[6+(ofs)*7] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[7+(ofs)*7] * c; \ + y1 += decode[8+(ofs)*7] * c; \ + y2 += decode[9+(ofs)*7] * c; \ + y3 += decode[10+(ofs)*7] * c; \ + y4 += decode[11+(ofs)*7] * c; \ + y5 += decode[12+(ofs)*7] * c; \ + y6 += decode[13+(ofs)*7] * c; \ + c = hc[2+(ofs)]; \ + x0 += decode[14+(ofs)*7] * c; \ + x1 += decode[15+(ofs)*7] * c; \ + x2 += decode[16+(ofs)*7] * c; \ + x3 += decode[17+(ofs)*7] * c; \ + x4 += decode[18+(ofs)*7] * c; \ + x5 += decode[19+(ofs)*7] * c; \ + x6 += decode[20+(ofs)*7] * c; \ + +#define stbir__store_output() \ + output[0] = x0 + y0; \ + output[1] = x1 + y1; \ + output[2] = x2 + y2; \ + output[3] = x3 + y3; \ + output[4] = x4 + y4; \ + output[5] = x5 + y5; \ + output[6] = x6 + y6; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; + +#endif + +#define STBIR__horizontal_channels 7 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + + +// include all of the vertical resamplers (both scatter and gather versions) + +#define STBIR__vertical_channels 1 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 1 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 2 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 2 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 3 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 3 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 4 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 4 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 5 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 5 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 6 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 6 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 7 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 7 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 8 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 8 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +typedef void STBIR_VERTICAL_GATHERFUNC( float * output, float const * coeffs, float const ** inputs, float const * input0_end ); + +static STBIR_VERTICAL_GATHERFUNC * stbir__vertical_gathers[ 8 ] = +{ + stbir__vertical_gather_with_1_coeffs,stbir__vertical_gather_with_2_coeffs,stbir__vertical_gather_with_3_coeffs,stbir__vertical_gather_with_4_coeffs,stbir__vertical_gather_with_5_coeffs,stbir__vertical_gather_with_6_coeffs,stbir__vertical_gather_with_7_coeffs,stbir__vertical_gather_with_8_coeffs +}; + +static STBIR_VERTICAL_GATHERFUNC * stbir__vertical_gathers_continues[ 8 ] = +{ + stbir__vertical_gather_with_1_coeffs_cont,stbir__vertical_gather_with_2_coeffs_cont,stbir__vertical_gather_with_3_coeffs_cont,stbir__vertical_gather_with_4_coeffs_cont,stbir__vertical_gather_with_5_coeffs_cont,stbir__vertical_gather_with_6_coeffs_cont,stbir__vertical_gather_with_7_coeffs_cont,stbir__vertical_gather_with_8_coeffs_cont +}; + +typedef void STBIR_VERTICAL_SCATTERFUNC( float ** outputs, float const * coeffs, float const * input, float const * input_end ); + +static STBIR_VERTICAL_SCATTERFUNC * stbir__vertical_scatter_sets[ 8 ] = +{ + stbir__vertical_scatter_with_1_coeffs,stbir__vertical_scatter_with_2_coeffs,stbir__vertical_scatter_with_3_coeffs,stbir__vertical_scatter_with_4_coeffs,stbir__vertical_scatter_with_5_coeffs,stbir__vertical_scatter_with_6_coeffs,stbir__vertical_scatter_with_7_coeffs,stbir__vertical_scatter_with_8_coeffs +}; + +static STBIR_VERTICAL_SCATTERFUNC * stbir__vertical_scatter_blends[ 8 ] = +{ + stbir__vertical_scatter_with_1_coeffs_cont,stbir__vertical_scatter_with_2_coeffs_cont,stbir__vertical_scatter_with_3_coeffs_cont,stbir__vertical_scatter_with_4_coeffs_cont,stbir__vertical_scatter_with_5_coeffs_cont,stbir__vertical_scatter_with_6_coeffs_cont,stbir__vertical_scatter_with_7_coeffs_cont,stbir__vertical_scatter_with_8_coeffs_cont +}; + + +static void stbir__encode_scanline( stbir__info const * stbir_info, void *output_buffer_data, float * encode_buffer, int row STBIR_ONLY_PROFILE_GET_SPLIT_INFO ) +{ + int num_pixels = stbir_info->horizontal.scale_info.output_sub_size; + int channels = stbir_info->channels; + int width_times_channels = num_pixels * channels; + void * output_buffer; + + // un-alpha weight if we need to + if ( stbir_info->alpha_unweight ) + { + STBIR_PROFILE_START( unalpha ); + stbir_info->alpha_unweight( encode_buffer, width_times_channels ); + STBIR_PROFILE_END( unalpha ); + } + + // write directly into output by default + output_buffer = output_buffer_data; + + // if we have an output callback, we first convert the decode buffer in place (and then hand that to the callback) + if ( stbir_info->out_pixels_cb ) + output_buffer = encode_buffer; + + STBIR_PROFILE_START( encode ); + // convert into the output buffer + stbir_info->encode_pixels( output_buffer, width_times_channels, encode_buffer ); + STBIR_PROFILE_END( encode ); + + // if we have an output callback, call it to send the data + if ( stbir_info->out_pixels_cb ) + stbir_info->out_pixels_cb( output_buffer_data, num_pixels, row, stbir_info->user_data ); +} + + +// Get the ring buffer pointer for an index +static float* stbir__get_ring_buffer_entry(stbir__info const * stbir_info, stbir__per_split_info const * split_info, int index ) +{ + STBIR_ASSERT( index < stbir_info->ring_buffer_num_entries ); + + #ifdef STBIR__SEPARATE_ALLOCATIONS + return split_info->ring_buffers[ index ]; + #else + return (float*) ( ( (char*) split_info->ring_buffer ) + ( index * stbir_info->ring_buffer_length_bytes ) ); + #endif +} + +// Get the specified scan line from the ring buffer +static float* stbir__get_ring_buffer_scanline(stbir__info const * stbir_info, stbir__per_split_info const * split_info, int get_scanline) +{ + int ring_buffer_index = (split_info->ring_buffer_begin_index + (get_scanline - split_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries; + return stbir__get_ring_buffer_entry( stbir_info, split_info, ring_buffer_index ); +} + +static void stbir__resample_horizontal_gather(stbir__info const * stbir_info, float* output_buffer, float const * input_buffer STBIR_ONLY_PROFILE_GET_SPLIT_INFO ) +{ + float const * decode_buffer = input_buffer - ( stbir_info->scanline_extents.conservative.n0 * stbir_info->effective_channels ); + + STBIR_PROFILE_START( horizontal ); + if ( ( stbir_info->horizontal.filter_enum == STBIR_FILTER_POINT_SAMPLE ) && ( stbir_info->horizontal.scale_info.scale == 1.0f ) ) + STBIR_MEMCPY( output_buffer, input_buffer, stbir_info->horizontal.scale_info.output_sub_size * sizeof( float ) * stbir_info->effective_channels ); + else + stbir_info->horizontal_gather_channels( output_buffer, stbir_info->horizontal.scale_info.output_sub_size, decode_buffer, stbir_info->horizontal.contributors, stbir_info->horizontal.coefficients, stbir_info->horizontal.coefficient_width ); + STBIR_PROFILE_END( horizontal ); +} + +static void stbir__resample_vertical_gather(stbir__info const * stbir_info, stbir__per_split_info* split_info, int n, int contrib_n0, int contrib_n1, float const * vertical_coefficients ) +{ + float* encode_buffer = split_info->vertical_buffer; + float* decode_buffer = split_info->decode_buffer; + int vertical_first = stbir_info->vertical_first; + int width = (vertical_first) ? ( stbir_info->scanline_extents.conservative.n1-stbir_info->scanline_extents.conservative.n0+1 ) : stbir_info->horizontal.scale_info.output_sub_size; + int width_times_channels = stbir_info->effective_channels * width; + + STBIR_ASSERT( stbir_info->vertical.is_gather ); + + // loop over the contributing scanlines and scale into the buffer + STBIR_PROFILE_START( vertical ); + { + int k = 0, total = contrib_n1 - contrib_n0 + 1; + STBIR_ASSERT( total > 0 ); + do { + float const * inputs[8]; + int i, cnt = total; if ( cnt > 8 ) cnt = 8; + for( i = 0 ; i < cnt ; i++ ) + inputs[ i ] = stbir__get_ring_buffer_scanline(stbir_info, split_info, k+i+contrib_n0 ); + + // call the N scanlines at a time function (up to 8 scanlines of blending at once) + ((k==0)?stbir__vertical_gathers:stbir__vertical_gathers_continues)[cnt-1]( (vertical_first) ? decode_buffer : encode_buffer, vertical_coefficients + k, inputs, inputs[0] + width_times_channels ); + k += cnt; + total -= cnt; + } while ( total ); + } + STBIR_PROFILE_END( vertical ); + + if ( vertical_first ) + { + // Now resample the gathered vertical data in the horizontal axis into the encode buffer + stbir__resample_horizontal_gather(stbir_info, encode_buffer, decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + } + + stbir__encode_scanline( stbir_info, ( (char *) stbir_info->output_data ) + ((ptrdiff_t)n * (ptrdiff_t)stbir_info->output_stride_bytes), + encode_buffer, n STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); +} + +static void stbir__decode_and_resample_for_vertical_gather_loop(stbir__info const * stbir_info, stbir__per_split_info* split_info, int n) +{ + int ring_buffer_index; + float* ring_buffer; + + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline( stbir_info, n, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // update new end scanline + split_info->ring_buffer_last_scanline = n; + + // get ring buffer + ring_buffer_index = (split_info->ring_buffer_begin_index + (split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries; + ring_buffer = stbir__get_ring_buffer_entry(stbir_info, split_info, ring_buffer_index); + + // Now resample it into the ring buffer. + stbir__resample_horizontal_gather( stbir_info, ring_buffer, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. +} + +static void stbir__vertical_gather_loop( stbir__info const * stbir_info, stbir__per_split_info* split_info, int split_count ) +{ + int y, start_output_y, end_output_y; + stbir__contributors* vertical_contributors = stbir_info->vertical.contributors; + float const * vertical_coefficients = stbir_info->vertical.coefficients; + + STBIR_ASSERT( stbir_info->vertical.is_gather ); + + start_output_y = split_info->start_output_y; + end_output_y = split_info[split_count-1].end_output_y; + + vertical_contributors += start_output_y; + vertical_coefficients += start_output_y * stbir_info->vertical.coefficient_width; + + // initialize the ring buffer for gathering + split_info->ring_buffer_begin_index = 0; + split_info->ring_buffer_first_scanline = stbir_info->vertical.extent_info.lowest; + split_info->ring_buffer_last_scanline = split_info->ring_buffer_first_scanline - 1; // means "empty" + + for (y = start_output_y; y < end_output_y; y++) + { + int in_first_scanline, in_last_scanline; + + in_first_scanline = vertical_contributors->n0; + in_last_scanline = vertical_contributors->n1; + + // make sure the indexing hasn't broken + STBIR_ASSERT( in_first_scanline >= split_info->ring_buffer_first_scanline ); + + // Load in new scanlines + while (in_last_scanline > split_info->ring_buffer_last_scanline) + { + STBIR_ASSERT( ( split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline + 1 ) <= stbir_info->ring_buffer_num_entries ); + + // make sure there was room in the ring buffer when we add new scanlines + if ( ( split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline + 1 ) == stbir_info->ring_buffer_num_entries ) + { + split_info->ring_buffer_first_scanline++; + split_info->ring_buffer_begin_index++; + } + + if ( stbir_info->vertical_first ) + { + float * ring_buffer = stbir__get_ring_buffer_scanline( stbir_info, split_info, ++split_info->ring_buffer_last_scanline ); + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline( stbir_info, split_info->ring_buffer_last_scanline, ring_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + } + else + { + stbir__decode_and_resample_for_vertical_gather_loop(stbir_info, split_info, split_info->ring_buffer_last_scanline + 1); + } + } + + // Now all buffers should be ready to write a row of vertical sampling, so do it. + stbir__resample_vertical_gather(stbir_info, split_info, y, in_first_scanline, in_last_scanline, vertical_coefficients ); + + ++vertical_contributors; + vertical_coefficients += stbir_info->vertical.coefficient_width; + } +} + +#define STBIR__FLOAT_EMPTY_MARKER 3.0e+38F +#define STBIR__FLOAT_BUFFER_IS_EMPTY(ptr) ((ptr)[0]==STBIR__FLOAT_EMPTY_MARKER) + +static void stbir__encode_first_scanline_from_scatter(stbir__info const * stbir_info, stbir__per_split_info* split_info) +{ + // evict a scanline out into the output buffer + float* ring_buffer_entry = stbir__get_ring_buffer_entry(stbir_info, split_info, split_info->ring_buffer_begin_index ); + + // dump the scanline out + stbir__encode_scanline( stbir_info, ( (char *)stbir_info->output_data ) + ( (ptrdiff_t)split_info->ring_buffer_first_scanline * (ptrdiff_t)stbir_info->output_stride_bytes ), ring_buffer_entry, split_info->ring_buffer_first_scanline STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // mark it as empty + ring_buffer_entry[ 0 ] = STBIR__FLOAT_EMPTY_MARKER; + + // advance the first scanline + split_info->ring_buffer_first_scanline++; + if ( ++split_info->ring_buffer_begin_index == stbir_info->ring_buffer_num_entries ) + split_info->ring_buffer_begin_index = 0; +} + +static void stbir__horizontal_resample_and_encode_first_scanline_from_scatter(stbir__info const * stbir_info, stbir__per_split_info* split_info) +{ + // evict a scanline out into the output buffer + + float* ring_buffer_entry = stbir__get_ring_buffer_entry(stbir_info, split_info, split_info->ring_buffer_begin_index ); + + // Now resample it into the buffer. + stbir__resample_horizontal_gather( stbir_info, split_info->vertical_buffer, ring_buffer_entry STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // dump the scanline out + stbir__encode_scanline( stbir_info, ( (char *)stbir_info->output_data ) + ( (ptrdiff_t)split_info->ring_buffer_first_scanline * (ptrdiff_t)stbir_info->output_stride_bytes ), split_info->vertical_buffer, split_info->ring_buffer_first_scanline STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // mark it as empty + ring_buffer_entry[ 0 ] = STBIR__FLOAT_EMPTY_MARKER; + + // advance the first scanline + split_info->ring_buffer_first_scanline++; + if ( ++split_info->ring_buffer_begin_index == stbir_info->ring_buffer_num_entries ) + split_info->ring_buffer_begin_index = 0; +} + +static void stbir__resample_vertical_scatter(stbir__info const * stbir_info, stbir__per_split_info* split_info, int n0, int n1, float const * vertical_coefficients, float const * vertical_buffer, float const * vertical_buffer_end ) +{ + STBIR_ASSERT( !stbir_info->vertical.is_gather ); + + STBIR_PROFILE_START( vertical ); + { + int k = 0, total = n1 - n0 + 1; + STBIR_ASSERT( total > 0 ); + do { + float * outputs[8]; + int i, n = total; if ( n > 8 ) n = 8; + for( i = 0 ; i < n ; i++ ) + { + outputs[ i ] = stbir__get_ring_buffer_scanline(stbir_info, split_info, k+i+n0 ); + if ( ( i ) && ( STBIR__FLOAT_BUFFER_IS_EMPTY( outputs[i] ) != STBIR__FLOAT_BUFFER_IS_EMPTY( outputs[0] ) ) ) // make sure runs are of the same type + { + n = i; + break; + } + } + // call the scatter to N scanlines at a time function (up to 8 scanlines of scattering at once) + ((STBIR__FLOAT_BUFFER_IS_EMPTY( outputs[0] ))?stbir__vertical_scatter_sets:stbir__vertical_scatter_blends)[n-1]( outputs, vertical_coefficients + k, vertical_buffer, vertical_buffer_end ); + k += n; + total -= n; + } while ( total ); + } + + STBIR_PROFILE_END( vertical ); +} + +typedef void stbir__handle_scanline_for_scatter_func(stbir__info const * stbir_info, stbir__per_split_info* split_info); + +static void stbir__vertical_scatter_loop( stbir__info const * stbir_info, stbir__per_split_info* split_info, int split_count ) +{ + int y, start_output_y, end_output_y, start_input_y, end_input_y; + stbir__contributors* vertical_contributors = stbir_info->vertical.contributors; + float const * vertical_coefficients = stbir_info->vertical.coefficients; + stbir__handle_scanline_for_scatter_func * handle_scanline_for_scatter; + void * scanline_scatter_buffer; + void * scanline_scatter_buffer_end; + int on_first_input_y, last_input_y; + + STBIR_ASSERT( !stbir_info->vertical.is_gather ); + + start_output_y = split_info->start_output_y; + end_output_y = split_info[split_count-1].end_output_y; // may do multiple split counts + + start_input_y = split_info->start_input_y; + end_input_y = split_info[split_count-1].end_input_y; + + // adjust for starting offset start_input_y + y = start_input_y + stbir_info->vertical.filter_pixel_margin; + vertical_contributors += y ; + vertical_coefficients += stbir_info->vertical.coefficient_width * y; + + if ( stbir_info->vertical_first ) + { + handle_scanline_for_scatter = stbir__horizontal_resample_and_encode_first_scanline_from_scatter; + scanline_scatter_buffer = split_info->decode_buffer; + scanline_scatter_buffer_end = ( (char*) scanline_scatter_buffer ) + sizeof( float ) * stbir_info->effective_channels * (stbir_info->scanline_extents.conservative.n1-stbir_info->scanline_extents.conservative.n0+1); + } + else + { + handle_scanline_for_scatter = stbir__encode_first_scanline_from_scatter; + scanline_scatter_buffer = split_info->vertical_buffer; + scanline_scatter_buffer_end = ( (char*) scanline_scatter_buffer ) + sizeof( float ) * stbir_info->effective_channels * stbir_info->horizontal.scale_info.output_sub_size; + } + + // initialize the ring buffer for scattering + split_info->ring_buffer_first_scanline = start_output_y; + split_info->ring_buffer_last_scanline = -1; + split_info->ring_buffer_begin_index = -1; + + // mark all the buffers as empty to start + for( y = 0 ; y < stbir_info->ring_buffer_num_entries ; y++ ) + stbir__get_ring_buffer_entry( stbir_info, split_info, y )[0] = STBIR__FLOAT_EMPTY_MARKER; // only used on scatter + + // do the loop in input space + on_first_input_y = 1; last_input_y = start_input_y; + for (y = start_input_y ; y < end_input_y; y++) + { + int out_first_scanline, out_last_scanline; + + out_first_scanline = vertical_contributors->n0; + out_last_scanline = vertical_contributors->n1; + + STBIR_ASSERT(out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); + + if ( ( out_last_scanline >= out_first_scanline ) && ( ( ( out_first_scanline >= start_output_y ) && ( out_first_scanline < end_output_y ) ) || ( ( out_last_scanline >= start_output_y ) && ( out_last_scanline < end_output_y ) ) ) ) + { + float const * vc = vertical_coefficients; + + // keep track of the range actually seen for the next resize + last_input_y = y; + if ( ( on_first_input_y ) && ( y > start_input_y ) ) + split_info->start_input_y = y; + on_first_input_y = 0; + + // clip the region + if ( out_first_scanline < start_output_y ) + { + vc += start_output_y - out_first_scanline; + out_first_scanline = start_output_y; + } + + if ( out_last_scanline >= end_output_y ) + out_last_scanline = end_output_y - 1; + + // if very first scanline, init the index + if (split_info->ring_buffer_begin_index < 0) + split_info->ring_buffer_begin_index = out_first_scanline - start_output_y; + + STBIR_ASSERT( split_info->ring_buffer_begin_index <= out_first_scanline ); + + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline( stbir_info, y, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // When horizontal first, we resample horizontally into the vertical buffer before we scatter it out + if ( !stbir_info->vertical_first ) + stbir__resample_horizontal_gather( stbir_info, split_info->vertical_buffer, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // Now it's sitting in the buffer ready to be distributed into the ring buffers. + + // evict from the ringbuffer, if we need are full + if ( ( ( split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline + 1 ) == stbir_info->ring_buffer_num_entries ) && + ( out_last_scanline > split_info->ring_buffer_last_scanline ) ) + handle_scanline_for_scatter( stbir_info, split_info ); + + // Now the horizontal buffer is ready to write to all ring buffer rows, so do it. + stbir__resample_vertical_scatter(stbir_info, split_info, out_first_scanline, out_last_scanline, vc, (float*)scanline_scatter_buffer, (float*)scanline_scatter_buffer_end ); + + // update the end of the buffer + if ( out_last_scanline > split_info->ring_buffer_last_scanline ) + split_info->ring_buffer_last_scanline = out_last_scanline; + } + ++vertical_contributors; + vertical_coefficients += stbir_info->vertical.coefficient_width; + } + + // now evict the scanlines that are left over in the ring buffer + while ( split_info->ring_buffer_first_scanline < end_output_y ) + handle_scanline_for_scatter(stbir_info, split_info); + + // update the end_input_y if we do multiple resizes with the same data + ++last_input_y; + for( y = 0 ; y < split_count; y++ ) + if ( split_info[y].end_input_y > last_input_y ) + split_info[y].end_input_y = last_input_y; +} + + +static stbir__kernel_callback * stbir__builtin_kernels[] = { 0, stbir__filter_trapezoid, stbir__filter_triangle, stbir__filter_cubic, stbir__filter_catmullrom, stbir__filter_mitchell, stbir__filter_point }; +static stbir__support_callback * stbir__builtin_supports[] = { 0, stbir__support_trapezoid, stbir__support_one, stbir__support_two, stbir__support_two, stbir__support_two, stbir__support_zeropoint5 }; + +static void stbir__set_sampler(stbir__sampler * samp, stbir_filter filter, stbir__kernel_callback * kernel, stbir__support_callback * support, stbir_edge edge, stbir__scale_info * scale_info, int always_gather, void * user_data ) +{ + // set filter + if (filter == 0) + { + filter = STBIR_DEFAULT_FILTER_DOWNSAMPLE; // default to downsample + if (scale_info->scale >= ( 1.0f - stbir__small_float ) ) + { + if ( (scale_info->scale <= ( 1.0f + stbir__small_float ) ) && ( STBIR_CEILF(scale_info->pixel_shift) == scale_info->pixel_shift ) ) + filter = STBIR_FILTER_POINT_SAMPLE; + else + filter = STBIR_DEFAULT_FILTER_UPSAMPLE; + } + } + samp->filter_enum = filter; + + STBIR_ASSERT(samp->filter_enum != 0); + STBIR_ASSERT((unsigned)samp->filter_enum < STBIR_FILTER_OTHER); + samp->filter_kernel = stbir__builtin_kernels[ filter ]; + samp->filter_support = stbir__builtin_supports[ filter ]; + + if ( kernel && support ) + { + samp->filter_kernel = kernel; + samp->filter_support = support; + samp->filter_enum = STBIR_FILTER_OTHER; + } + + samp->edge = edge; + samp->filter_pixel_width = stbir__get_filter_pixel_width (samp->filter_support, scale_info->scale, user_data ); + // Gather is always better, but in extreme downsamples, you have to most or all of the data in memory + // For horizontal, we always have all the pixels, so we always use gather here (always_gather==1). + // For vertical, we use gather if scaling up (which means we will have samp->filter_pixel_width + // scanlines in memory at once). + samp->is_gather = 0; + if ( scale_info->scale >= ( 1.0f - stbir__small_float ) ) + samp->is_gather = 1; + else if ( ( always_gather ) || ( samp->filter_pixel_width <= STBIR_FORCE_GATHER_FILTER_SCANLINES_AMOUNT ) ) + samp->is_gather = 2; + + // pre calculate stuff based on the above + samp->coefficient_width = stbir__get_coefficient_width(samp, samp->is_gather, user_data); + + if ( edge == STBIR_EDGE_WRAP ) + if ( samp->filter_pixel_width > ( scale_info->input_full_size * 2 ) ) // this can only happen when shrinking to a single pixel + samp->filter_pixel_width = scale_info->input_full_size * 2; + + // This is how much to expand buffers to account for filters seeking outside + // the image boundaries. + samp->filter_pixel_margin = samp->filter_pixel_width / 2; + + samp->num_contributors = stbir__get_contributors(samp, samp->is_gather); + samp->contributors_size = samp->num_contributors * sizeof(stbir__contributors); + samp->coefficients_size = samp->num_contributors * samp->coefficient_width * sizeof(float) + sizeof(float); // extra sizeof(float) is padding + + samp->gather_prescatter_contributors = 0; + samp->gather_prescatter_coefficients = 0; + if ( samp->is_gather == 0 ) + { + samp->gather_prescatter_coefficient_width = samp->filter_pixel_width; + samp->gather_prescatter_num_contributors = stbir__get_contributors(samp, 2); + samp->gather_prescatter_contributors_size = samp->gather_prescatter_num_contributors * sizeof(stbir__contributors); + samp->gather_prescatter_coefficients_size = samp->gather_prescatter_num_contributors * samp->gather_prescatter_coefficient_width * sizeof(float); + } +} + +static void stbir__get_conservative_extents( stbir__sampler * samp, stbir__contributors * range, void * user_data ) +{ + float scale = samp->scale_info.scale; + float out_shift = samp->scale_info.pixel_shift; + stbir__support_callback * support = samp->filter_support; + int input_full_size = samp->scale_info.input_full_size; + stbir_edge edge = samp->edge; + float inv_scale = samp->scale_info.inv_scale; + + STBIR_ASSERT( samp->is_gather != 0 ); + + if ( samp->is_gather == 1 ) + { + int in_first_pixel, in_last_pixel; + float out_filter_radius = support(inv_scale, user_data) * scale; + + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, 0.5, out_filter_radius, inv_scale, out_shift, input_full_size, edge ); + range->n0 = in_first_pixel; + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, ( (float)(samp->scale_info.output_sub_size-1) ) + 0.5f, out_filter_radius, inv_scale, out_shift, input_full_size, edge ); + range->n1 = in_last_pixel; + } + else if ( samp->is_gather == 2 ) // downsample gather, refine + { + float in_pixels_radius = support(scale, user_data) * inv_scale; + int filter_pixel_margin = samp->filter_pixel_margin; + int output_sub_size = samp->scale_info.output_sub_size; + int input_end; + int n; + int in_first_pixel, in_last_pixel; + + // get a conservative area of the input range + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, 0, 0, inv_scale, out_shift, input_full_size, edge ); + range->n0 = in_first_pixel; + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, (float)output_sub_size, 0, inv_scale, out_shift, input_full_size, edge ); + range->n1 = in_last_pixel; + + // now go through the margin to the start of area to find bottom + n = range->n0 + 1; + input_end = -filter_pixel_margin; + while( n >= input_end ) + { + int out_first_pixel, out_last_pixel; + stbir__calculate_out_pixel_range( &out_first_pixel, &out_last_pixel, ((float)n)+0.5f, in_pixels_radius, scale, out_shift, output_sub_size ); + if ( out_first_pixel > out_last_pixel ) + break; + + if ( ( out_first_pixel < output_sub_size ) || ( out_last_pixel >= 0 ) ) + range->n0 = n; + --n; + } + + // now go through the end of the area through the margin to find top + n = range->n1 - 1; + input_end = n + 1 + filter_pixel_margin; + while( n <= input_end ) + { + int out_first_pixel, out_last_pixel; + stbir__calculate_out_pixel_range( &out_first_pixel, &out_last_pixel, ((float)n)+0.5f, in_pixels_radius, scale, out_shift, output_sub_size ); + if ( out_first_pixel > out_last_pixel ) + break; + if ( ( out_first_pixel < output_sub_size ) || ( out_last_pixel >= 0 ) ) + range->n1 = n; + ++n; + } + } + + if ( samp->edge == STBIR_EDGE_WRAP ) + { + // if we are wrapping, and we are very close to the image size (so the edges might merge), just use the scanline up to the edge + if ( ( range->n0 > 0 ) && ( range->n1 >= input_full_size ) ) + { + int marg = range->n1 - input_full_size + 1; + if ( ( marg + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= range->n0 ) + range->n0 = 0; + } + if ( ( range->n0 < 0 ) && ( range->n1 < (input_full_size-1) ) ) + { + int marg = -range->n0; + if ( ( input_full_size - marg - STBIR__MERGE_RUNS_PIXEL_THRESHOLD - 1 ) <= range->n1 ) + range->n1 = input_full_size - 1; + } + } + else + { + // for non-edge-wrap modes, we never read over the edge, so clamp + if ( range->n0 < 0 ) + range->n0 = 0; + if ( range->n1 >= input_full_size ) + range->n1 = input_full_size - 1; + } +} + +static void stbir__get_split_info( stbir__per_split_info* split_info, int splits, int output_height, int vertical_pixel_margin, int input_full_height ) +{ + int i, cur; + int left = output_height; + + cur = 0; + for( i = 0 ; i < splits ; i++ ) + { + int each; + split_info[i].start_output_y = cur; + each = left / ( splits - i ); + split_info[i].end_output_y = cur + each; + cur += each; + left -= each; + + // scatter range (updated to minimum as you run it) + split_info[i].start_input_y = -vertical_pixel_margin; + split_info[i].end_input_y = input_full_height + vertical_pixel_margin; + } +} + +static void stbir__free_internal_mem( stbir__info *info ) +{ + #define STBIR__FREE_AND_CLEAR( ptr ) { if ( ptr ) { void * p = (ptr); (ptr) = 0; STBIR_FREE( p, info->user_data); } } + + if ( info ) + { + #ifndef STBIR__SEPARATE_ALLOCATIONS + STBIR__FREE_AND_CLEAR( info->alloced_mem ); + #else + int i,j; + + if ( ( info->vertical.gather_prescatter_contributors ) && ( (void*)info->vertical.gather_prescatter_contributors != (void*)info->split_info[0].decode_buffer ) ) + { + STBIR__FREE_AND_CLEAR( info->vertical.gather_prescatter_coefficients ); + STBIR__FREE_AND_CLEAR( info->vertical.gather_prescatter_contributors ); + } + for( i = 0 ; i < info->splits ; i++ ) + { + for( j = 0 ; j < info->alloc_ring_buffer_num_entries ; j++ ) + { + #ifdef STBIR_SIMD8 + if ( info->effective_channels == 3 ) + --info->split_info[i].ring_buffers[j]; // avx in 3 channel mode needs one float at the start of the buffer + #endif + STBIR__FREE_AND_CLEAR( info->split_info[i].ring_buffers[j] ); + } + + #ifdef STBIR_SIMD8 + if ( info->effective_channels == 3 ) + --info->split_info[i].decode_buffer; // avx in 3 channel mode needs one float at the start of the buffer + #endif + STBIR__FREE_AND_CLEAR( info->split_info[i].decode_buffer ); + STBIR__FREE_AND_CLEAR( info->split_info[i].ring_buffers ); + STBIR__FREE_AND_CLEAR( info->split_info[i].vertical_buffer ); + } + STBIR__FREE_AND_CLEAR( info->split_info ); + if ( info->vertical.coefficients != info->horizontal.coefficients ) + { + STBIR__FREE_AND_CLEAR( info->vertical.coefficients ); + STBIR__FREE_AND_CLEAR( info->vertical.contributors ); + } + STBIR__FREE_AND_CLEAR( info->horizontal.coefficients ); + STBIR__FREE_AND_CLEAR( info->horizontal.contributors ); + STBIR__FREE_AND_CLEAR( info->alloced_mem ); + STBIR__FREE_AND_CLEAR( info ); + #endif + } + + #undef STBIR__FREE_AND_CLEAR +} + +static int stbir__get_max_split( int splits, int height ) +{ + int i; + int max = 0; + + for( i = 0 ; i < splits ; i++ ) + { + int each = height / ( splits - i ); + if ( each > max ) + max = each; + height -= each; + } + return max; +} + +static stbir__horizontal_gather_channels_func ** stbir__horizontal_gather_n_coeffs_funcs[8] = +{ + 0, stbir__horizontal_gather_1_channels_with_n_coeffs_funcs, stbir__horizontal_gather_2_channels_with_n_coeffs_funcs, stbir__horizontal_gather_3_channels_with_n_coeffs_funcs, stbir__horizontal_gather_4_channels_with_n_coeffs_funcs, 0,0, stbir__horizontal_gather_7_channels_with_n_coeffs_funcs +}; + +static stbir__horizontal_gather_channels_func ** stbir__horizontal_gather_channels_funcs[8] = +{ + 0, stbir__horizontal_gather_1_channels_funcs, stbir__horizontal_gather_2_channels_funcs, stbir__horizontal_gather_3_channels_funcs, stbir__horizontal_gather_4_channels_funcs, 0,0, stbir__horizontal_gather_7_channels_funcs +}; + +// there are six resize classifications: 0 == vertical scatter, 1 == vertical gather < 1x scale, 2 == vertical gather 1x-2x scale, 4 == vertical gather < 3x scale, 4 == vertical gather > 3x scale, 5 == <=4 pixel height, 6 == <=4 pixel wide column +#define STBIR_RESIZE_CLASSIFICATIONS 8 + +static float stbir__compute_weights[5][STBIR_RESIZE_CLASSIFICATIONS][4]= // 5 = 0=1chan, 1=2chan, 2=3chan, 3=4chan, 4=7chan +{ + { + { 1.00000f, 1.00000f, 0.31250f, 1.00000f }, + { 0.56250f, 0.59375f, 0.00000f, 0.96875f }, + { 1.00000f, 0.06250f, 0.00000f, 1.00000f }, + { 0.00000f, 0.09375f, 1.00000f, 1.00000f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.03125f, 0.12500f, 1.00000f, 1.00000f }, + { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.00000f, 0.03125f }, + }, { + { 0.00000f, 0.84375f, 0.00000f, 0.03125f }, + { 0.09375f, 0.93750f, 0.00000f, 0.78125f }, + { 0.87500f, 0.21875f, 0.00000f, 0.96875f }, + { 0.09375f, 0.09375f, 1.00000f, 1.00000f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.03125f, 0.12500f, 1.00000f, 1.00000f }, + { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.00000f, 0.53125f }, + }, { + { 0.00000f, 0.53125f, 0.00000f, 0.03125f }, + { 0.06250f, 0.96875f, 0.00000f, 0.53125f }, + { 0.87500f, 0.18750f, 0.00000f, 0.93750f }, + { 0.00000f, 0.09375f, 1.00000f, 1.00000f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.03125f, 0.12500f, 1.00000f, 1.00000f }, + { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.00000f, 0.56250f }, + }, { + { 0.00000f, 0.50000f, 0.00000f, 0.71875f }, + { 0.06250f, 0.84375f, 0.00000f, 0.87500f }, + { 1.00000f, 0.50000f, 0.50000f, 0.96875f }, + { 1.00000f, 0.09375f, 0.31250f, 0.50000f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 1.00000f, 0.03125f, 0.03125f, 0.53125f }, + { 0.18750f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.03125f, 0.18750f }, + }, { + { 0.00000f, 0.59375f, 0.00000f, 0.96875f }, + { 0.06250f, 0.81250f, 0.06250f, 0.59375f }, + { 0.75000f, 0.43750f, 0.12500f, 0.96875f }, + { 0.87500f, 0.06250f, 0.18750f, 0.43750f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.15625f, 0.12500f, 1.00000f, 1.00000f }, + { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.03125f, 0.34375f }, + } +}; + +// structure that allow us to query and override info for training the costs +typedef struct STBIR__V_FIRST_INFO +{ + double v_cost, h_cost; + int control_v_first; // 0 = no control, 1 = force hori, 2 = force vert + int v_first; + int v_resize_classification; + int is_gather; +} STBIR__V_FIRST_INFO; + +#ifdef STBIR__V_FIRST_INFO_BUFFER +static STBIR__V_FIRST_INFO STBIR__V_FIRST_INFO_BUFFER = {0}; +#define STBIR__V_FIRST_INFO_POINTER &STBIR__V_FIRST_INFO_BUFFER +#else +#define STBIR__V_FIRST_INFO_POINTER 0 +#endif + +// Figure out whether to scale along the horizontal or vertical first. +// This only *super* important when you are scaling by a massively +// different amount in the vertical vs the horizontal (for example, if +// you are scaling by 2x in the width, and 0.5x in the height, then you +// want to do the vertical scale first, because it's around 3x faster +// in that order. +// +// In more normal circumstances, this makes a 20-40% differences, so +// it's good to get right, but not critical. The normal way that you +// decide which direction goes first is just figuring out which +// direction does more multiplies. But with modern CPUs with their +// fancy caches and SIMD and high IPC abilities, so there's just a lot +// more that goes into it. +// +// My handwavy sort of solution is to have an app that does a whole +// bunch of timing for both vertical and horizontal first modes, +// and then another app that can read lots of these timing files +// and try to search for the best weights to use. Dotimings.c +// is the app that does a bunch of timings, and vf_train.c is the +// app that solves for the best weights (and shows how well it +// does currently). + +static int stbir__should_do_vertical_first( float weights_table[STBIR_RESIZE_CLASSIFICATIONS][4], int horizontal_filter_pixel_width, float horizontal_scale, int horizontal_output_size, int vertical_filter_pixel_width, float vertical_scale, int vertical_output_size, int is_gather, STBIR__V_FIRST_INFO * info ) +{ + double v_cost, h_cost; + float * weights; + int vertical_first; + int v_classification; + + // categorize the resize into buckets + if ( ( vertical_output_size <= 4 ) || ( horizontal_output_size <= 4 ) ) + v_classification = ( vertical_output_size < horizontal_output_size ) ? 6 : 7; + else if ( vertical_scale <= 1.0f ) + v_classification = ( is_gather ) ? 1 : 0; + else if ( vertical_scale <= 2.0f) + v_classification = 2; + else if ( vertical_scale <= 3.0f) + v_classification = 3; + else if ( vertical_scale <= 4.0f) + v_classification = 5; + else + v_classification = 6; + + // use the right weights + weights = weights_table[ v_classification ]; + + // this is the costs when you don't take into account modern CPUs with high ipc and simd and caches - wish we had a better estimate + h_cost = (float)horizontal_filter_pixel_width * weights[0] + horizontal_scale * (float)vertical_filter_pixel_width * weights[1]; + v_cost = (float)vertical_filter_pixel_width * weights[2] + vertical_scale * (float)horizontal_filter_pixel_width * weights[3]; + + // use computation estimate to decide vertical first or not + vertical_first = ( v_cost <= h_cost ) ? 1 : 0; + + // save these, if requested + if ( info ) + { + info->h_cost = h_cost; + info->v_cost = v_cost; + info->v_resize_classification = v_classification; + info->v_first = vertical_first; + info->is_gather = is_gather; + } + + // and this allows us to override everything for testing (see dotiming.c) + if ( ( info ) && ( info->control_v_first ) ) + vertical_first = ( info->control_v_first == 2 ) ? 1 : 0; + + return vertical_first; +} + +// layout lookups - must match stbir_internal_pixel_layout +static unsigned char stbir__pixel_channels[] = { + 1,2,3,3,4, // 1ch, 2ch, rgb, bgr, 4ch + 4,4,4,4,2,2, // RGBA,BGRA,ARGB,ABGR,RA,AR + 4,4,4,4,2,2, // RGBA_PM,BGRA_PM,ARGB_PM,ABGR_PM,RA_PM,AR_PM +}; + +// the internal pixel layout enums are in a different order, so we can easily do range comparisons of types +// the public pixel layout is ordered in a way that if you cast num_channels (1-4) to the enum, you get something sensible +static stbir_internal_pixel_layout stbir__pixel_layout_convert_public_to_internal[] = { + STBIRI_BGR, STBIRI_1CHANNEL, STBIRI_2CHANNEL, STBIRI_RGB, STBIRI_RGBA, + STBIRI_4CHANNEL, STBIRI_BGRA, STBIRI_ARGB, STBIRI_ABGR, STBIRI_RA, STBIRI_AR, + STBIRI_RGBA_PM, STBIRI_BGRA_PM, STBIRI_ARGB_PM, STBIRI_ABGR_PM, STBIRI_RA_PM, STBIRI_AR_PM, +}; + +static stbir__info * stbir__alloc_internal_mem_and_build_samplers( stbir__sampler * horizontal, stbir__sampler * vertical, stbir__contributors * conservative, stbir_pixel_layout input_pixel_layout_public, stbir_pixel_layout output_pixel_layout_public, int splits, int new_x, int new_y, int fast_alpha, void * user_data STBIR_ONLY_PROFILE_BUILD_GET_INFO ) +{ + static char stbir_channel_count_index[8]={ 9,0,1,2, 3,9,9,4 }; + + stbir__info * info = 0; + void * alloced = 0; + int alloced_total = 0; + int vertical_first; + int decode_buffer_size, ring_buffer_length_bytes, ring_buffer_size, vertical_buffer_size, alloc_ring_buffer_num_entries; + + int alpha_weighting_type = 0; // 0=none, 1=simple, 2=fancy + int conservative_split_output_size = stbir__get_max_split( splits, vertical->scale_info.output_sub_size ); + stbir_internal_pixel_layout input_pixel_layout = stbir__pixel_layout_convert_public_to_internal[ input_pixel_layout_public ]; + stbir_internal_pixel_layout output_pixel_layout = stbir__pixel_layout_convert_public_to_internal[ output_pixel_layout_public ]; + int channels = stbir__pixel_channels[ input_pixel_layout ]; + int effective_channels = channels; + + // first figure out what type of alpha weighting to use (if any) + if ( ( horizontal->filter_enum != STBIR_FILTER_POINT_SAMPLE ) || ( vertical->filter_enum != STBIR_FILTER_POINT_SAMPLE ) ) // no alpha weighting on point sampling + { + if ( ( input_pixel_layout >= STBIRI_RGBA ) && ( input_pixel_layout <= STBIRI_AR ) && ( output_pixel_layout >= STBIRI_RGBA ) && ( output_pixel_layout <= STBIRI_AR ) ) + { + if ( fast_alpha ) + { + alpha_weighting_type = 4; + } + else + { + static int fancy_alpha_effective_cnts[6] = { 7, 7, 7, 7, 3, 3 }; + alpha_weighting_type = 2; + effective_channels = fancy_alpha_effective_cnts[ input_pixel_layout - STBIRI_RGBA ]; + } + } + else if ( ( input_pixel_layout >= STBIRI_RGBA_PM ) && ( input_pixel_layout <= STBIRI_AR_PM ) && ( output_pixel_layout >= STBIRI_RGBA ) && ( output_pixel_layout <= STBIRI_AR ) ) + { + // input premult, output non-premult + alpha_weighting_type = 3; + } + else if ( ( input_pixel_layout >= STBIRI_RGBA ) && ( input_pixel_layout <= STBIRI_AR ) && ( output_pixel_layout >= STBIRI_RGBA_PM ) && ( output_pixel_layout <= STBIRI_AR_PM ) ) + { + // input non-premult, output premult + alpha_weighting_type = 1; + } + } + + // channel in and out count must match currently + if ( channels != stbir__pixel_channels[ output_pixel_layout ] ) + return 0; + + // get vertical first + vertical_first = stbir__should_do_vertical_first( stbir__compute_weights[ (int)stbir_channel_count_index[ effective_channels ] ], horizontal->filter_pixel_width, horizontal->scale_info.scale, horizontal->scale_info.output_sub_size, vertical->filter_pixel_width, vertical->scale_info.scale, vertical->scale_info.output_sub_size, vertical->is_gather, STBIR__V_FIRST_INFO_POINTER ); + + // sometimes read one float off in some of the unrolled loops (with a weight of zero coeff, so it doesn't have an effect) + decode_buffer_size = ( conservative->n1 - conservative->n0 + 1 ) * effective_channels * sizeof(float) + sizeof(float); // extra float for padding + +#if defined( STBIR__SEPARATE_ALLOCATIONS ) && defined(STBIR_SIMD8) + if ( effective_channels == 3 ) + decode_buffer_size += sizeof(float); // avx in 3 channel mode needs one float at the start of the buffer (only with separate allocations) +#endif + + ring_buffer_length_bytes = horizontal->scale_info.output_sub_size * effective_channels * sizeof(float) + sizeof(float); // extra float for padding + + // if we do vertical first, the ring buffer holds a whole decoded line + if ( vertical_first ) + ring_buffer_length_bytes = ( decode_buffer_size + 15 ) & ~15; + + if ( ( ring_buffer_length_bytes & 4095 ) == 0 ) ring_buffer_length_bytes += 64*3; // avoid 4k alias + + // One extra entry because floating point precision problems sometimes cause an extra to be necessary. + alloc_ring_buffer_num_entries = vertical->filter_pixel_width + 1; + + // we never need more ring buffer entries than the scanlines we're outputting when in scatter mode + if ( ( !vertical->is_gather ) && ( alloc_ring_buffer_num_entries > conservative_split_output_size ) ) + alloc_ring_buffer_num_entries = conservative_split_output_size; + + ring_buffer_size = alloc_ring_buffer_num_entries * ring_buffer_length_bytes; + + // The vertical buffer is used differently, depending on whether we are scattering + // the vertical scanlines, or gathering them. + // If scattering, it's used at the temp buffer to accumulate each output. + // If gathering, it's just the output buffer. + vertical_buffer_size = horizontal->scale_info.output_sub_size * effective_channels * sizeof(float) + sizeof(float); // extra float for padding + + // we make two passes through this loop, 1st to add everything up, 2nd to allocate and init + for(;;) + { + int i; + void * advance_mem = alloced; + int copy_horizontal = 0; + stbir__sampler * possibly_use_horizontal_for_pivot = 0; + +#ifdef STBIR__SEPARATE_ALLOCATIONS + #define STBIR__NEXT_PTR( ptr, size, ntype ) if ( alloced ) { void * p = STBIR_MALLOC( size, user_data); if ( p == 0 ) { stbir__free_internal_mem( info ); return 0; } (ptr) = (ntype*)p; } +#else + #define STBIR__NEXT_PTR( ptr, size, ntype ) advance_mem = (void*) ( ( ((size_t)advance_mem) + 15 ) & ~15 ); if ( alloced ) ptr = (ntype*)advance_mem; advance_mem = ((char*)advance_mem) + (size); +#endif + + STBIR__NEXT_PTR( info, sizeof( stbir__info ), stbir__info ); + + STBIR__NEXT_PTR( info->split_info, sizeof( stbir__per_split_info ) * splits, stbir__per_split_info ); + + if ( info ) + { + static stbir__alpha_weight_func * fancy_alpha_weights[6] = { stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_2ch, stbir__fancy_alpha_weight_2ch }; + static stbir__alpha_unweight_func * fancy_alpha_unweights[6] = { stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_2ch, stbir__fancy_alpha_unweight_2ch }; + static stbir__alpha_weight_func * simple_alpha_weights[6] = { stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_2ch, stbir__simple_alpha_weight_2ch }; + static stbir__alpha_unweight_func * simple_alpha_unweights[6] = { stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_2ch, stbir__simple_alpha_unweight_2ch }; + + // initialize info fields + info->alloced_mem = alloced; + info->alloced_total = alloced_total; + + info->channels = channels; + info->effective_channels = effective_channels; + + info->offset_x = new_x; + info->offset_y = new_y; + info->alloc_ring_buffer_num_entries = alloc_ring_buffer_num_entries; + info->ring_buffer_num_entries = 0; + info->ring_buffer_length_bytes = ring_buffer_length_bytes; + info->splits = splits; + info->vertical_first = vertical_first; + + info->input_pixel_layout_internal = input_pixel_layout; + info->output_pixel_layout_internal = output_pixel_layout; + + // setup alpha weight functions + info->alpha_weight = 0; + info->alpha_unweight = 0; + + // handle alpha weighting functions and overrides + if ( alpha_weighting_type == 2 ) + { + // high quality alpha multiplying on the way in, dividing on the way out + info->alpha_weight = fancy_alpha_weights[ input_pixel_layout - STBIRI_RGBA ]; + info->alpha_unweight = fancy_alpha_unweights[ output_pixel_layout - STBIRI_RGBA ]; + } + else if ( alpha_weighting_type == 4 ) + { + // fast alpha multiplying on the way in, dividing on the way out + info->alpha_weight = simple_alpha_weights[ input_pixel_layout - STBIRI_RGBA ]; + info->alpha_unweight = simple_alpha_unweights[ output_pixel_layout - STBIRI_RGBA ]; + } + else if ( alpha_weighting_type == 1 ) + { + // fast alpha on the way in, leave in premultiplied form on way out + info->alpha_weight = simple_alpha_weights[ input_pixel_layout - STBIRI_RGBA ]; + } + else if ( alpha_weighting_type == 3 ) + { + // incoming is premultiplied, fast alpha dividing on the way out - non-premultiplied output + info->alpha_unweight = simple_alpha_unweights[ output_pixel_layout - STBIRI_RGBA ]; + } + + // handle 3-chan color flipping, using the alpha weight path + if ( ( ( input_pixel_layout == STBIRI_RGB ) && ( output_pixel_layout == STBIRI_BGR ) ) || + ( ( input_pixel_layout == STBIRI_BGR ) && ( output_pixel_layout == STBIRI_RGB ) ) ) + { + // do the flipping on the smaller of the two ends + if ( horizontal->scale_info.scale < 1.0f ) + info->alpha_unweight = stbir__simple_flip_3ch; + else + info->alpha_weight = stbir__simple_flip_3ch; + } + + } + + // get all the per-split buffers + for( i = 0 ; i < splits ; i++ ) + { + STBIR__NEXT_PTR( info->split_info[i].decode_buffer, decode_buffer_size, float ); + +#ifdef STBIR__SEPARATE_ALLOCATIONS + + #ifdef STBIR_SIMD8 + if ( ( info ) && ( effective_channels == 3 ) ) + ++info->split_info[i].decode_buffer; // avx in 3 channel mode needs one float at the start of the buffer + #endif + + STBIR__NEXT_PTR( info->split_info[i].ring_buffers, alloc_ring_buffer_num_entries * sizeof(float*), float* ); + { + int j; + for( j = 0 ; j < alloc_ring_buffer_num_entries ; j++ ) + { + STBIR__NEXT_PTR( info->split_info[i].ring_buffers[j], ring_buffer_length_bytes, float ); + #ifdef STBIR_SIMD8 + if ( ( info ) && ( effective_channels == 3 ) ) + ++info->split_info[i].ring_buffers[j]; // avx in 3 channel mode needs one float at the start of the buffer + #endif + } + } +#else + STBIR__NEXT_PTR( info->split_info[i].ring_buffer, ring_buffer_size, float ); +#endif + STBIR__NEXT_PTR( info->split_info[i].vertical_buffer, vertical_buffer_size, float ); + } + + // alloc memory for to-be-pivoted coeffs (if necessary) + if ( vertical->is_gather == 0 ) + { + int both; + int temp_mem_amt; + + // when in vertical scatter mode, we first build the coefficients in gather mode, and then pivot after, + // that means we need two buffers, so we try to use the decode buffer and ring buffer for this. if that + // is too small, we just allocate extra memory to use as this temp. + + both = vertical->gather_prescatter_contributors_size + vertical->gather_prescatter_coefficients_size; + +#ifdef STBIR__SEPARATE_ALLOCATIONS + temp_mem_amt = decode_buffer_size; +#else + temp_mem_amt = ( decode_buffer_size + ring_buffer_size + vertical_buffer_size ) * splits; +#endif + if ( temp_mem_amt >= both ) + { + if ( info ) + { + vertical->gather_prescatter_contributors = (stbir__contributors*)info->split_info[0].decode_buffer; + vertical->gather_prescatter_coefficients = (float*) ( ( (char*)info->split_info[0].decode_buffer ) + vertical->gather_prescatter_contributors_size ); + } + } + else + { + // ring+decode memory is too small, so allocate temp memory + STBIR__NEXT_PTR( vertical->gather_prescatter_contributors, vertical->gather_prescatter_contributors_size, stbir__contributors ); + STBIR__NEXT_PTR( vertical->gather_prescatter_coefficients, vertical->gather_prescatter_coefficients_size, float ); + } + } + + STBIR__NEXT_PTR( horizontal->contributors, horizontal->contributors_size, stbir__contributors ); + STBIR__NEXT_PTR( horizontal->coefficients, horizontal->coefficients_size, float ); + + // are the two filters identical?? (happens a lot with mipmap generation) + if ( ( horizontal->filter_kernel == vertical->filter_kernel ) && ( horizontal->filter_support == vertical->filter_support ) && ( horizontal->edge == vertical->edge ) && ( horizontal->scale_info.output_sub_size == vertical->scale_info.output_sub_size ) ) + { + float diff_scale = horizontal->scale_info.scale - vertical->scale_info.scale; + float diff_shift = horizontal->scale_info.pixel_shift - vertical->scale_info.pixel_shift; + if ( diff_scale < 0.0f ) diff_scale = -diff_scale; + if ( diff_shift < 0.0f ) diff_shift = -diff_shift; + if ( ( diff_scale <= stbir__small_float ) && ( diff_shift <= stbir__small_float ) ) + { + if ( horizontal->is_gather == vertical->is_gather ) + { + copy_horizontal = 1; + goto no_vert_alloc; + } + // everything matches, but vertical is scatter, horizontal is gather, use horizontal coeffs for vertical pivot coeffs + possibly_use_horizontal_for_pivot = horizontal; + } + } + + STBIR__NEXT_PTR( vertical->contributors, vertical->contributors_size, stbir__contributors ); + STBIR__NEXT_PTR( vertical->coefficients, vertical->coefficients_size, float ); + + no_vert_alloc: + + if ( info ) + { + STBIR_PROFILE_BUILD_START( horizontal ); + + stbir__calculate_filters( horizontal, 0, user_data STBIR_ONLY_PROFILE_BUILD_SET_INFO ); + + // setup the horizontal gather functions + // start with defaulting to the n_coeffs functions (specialized on channels and remnant leftover) + info->horizontal_gather_channels = stbir__horizontal_gather_n_coeffs_funcs[ effective_channels ][ horizontal->extent_info.widest & 3 ]; + // but if the number of coeffs <= 12, use another set of special cases. <=12 coeffs is any enlarging resize, or shrinking resize down to about 1/3 size + if ( horizontal->extent_info.widest <= 12 ) + info->horizontal_gather_channels = stbir__horizontal_gather_channels_funcs[ effective_channels ][ horizontal->extent_info.widest - 1 ]; + + info->scanline_extents.conservative.n0 = conservative->n0; + info->scanline_extents.conservative.n1 = conservative->n1; + + // get exact extents + stbir__get_extents( horizontal, &info->scanline_extents ); + + // pack the horizontal coeffs + horizontal->coefficient_width = stbir__pack_coefficients(horizontal->num_contributors, horizontal->contributors, horizontal->coefficients, horizontal->coefficient_width, horizontal->extent_info.widest, info->scanline_extents.conservative.n1 + 1 ); + + STBIR_MEMCPY( &info->horizontal, horizontal, sizeof( stbir__sampler ) ); + + STBIR_PROFILE_BUILD_END( horizontal ); + + if ( copy_horizontal ) + { + STBIR_MEMCPY( &info->vertical, horizontal, sizeof( stbir__sampler ) ); + } + else + { + STBIR_PROFILE_BUILD_START( vertical ); + + stbir__calculate_filters( vertical, possibly_use_horizontal_for_pivot, user_data STBIR_ONLY_PROFILE_BUILD_SET_INFO ); + STBIR_MEMCPY( &info->vertical, vertical, sizeof( stbir__sampler ) ); + + STBIR_PROFILE_BUILD_END( vertical ); + } + + // setup the vertical split ranges + stbir__get_split_info( info->split_info, info->splits, info->vertical.scale_info.output_sub_size, info->vertical.filter_pixel_margin, info->vertical.scale_info.input_full_size ); + + // now we know precisely how many entries we need + info->ring_buffer_num_entries = info->vertical.extent_info.widest; + + // we never need more ring buffer entries than the scanlines we're outputting + if ( ( !info->vertical.is_gather ) && ( info->ring_buffer_num_entries > conservative_split_output_size ) ) + info->ring_buffer_num_entries = conservative_split_output_size; + STBIR_ASSERT( info->ring_buffer_num_entries <= info->alloc_ring_buffer_num_entries ); + + // a few of the horizontal gather functions read one dword past the end (but mask it out), so put in a normal value so no snans or denormals accidentally sneak in + for( i = 0 ; i < splits ; i++ ) + { + int width, ofs; + + // find the right most span + if ( info->scanline_extents.spans[0].n1 > info->scanline_extents.spans[1].n1 ) + width = info->scanline_extents.spans[0].n1 - info->scanline_extents.spans[0].n0; + else + width = info->scanline_extents.spans[1].n1 - info->scanline_extents.spans[1].n0; + + // this calc finds the exact end of the decoded scanline for all filter modes. + // usually this is just the width * effective channels. But we have to account + // for the area to the left of the scanline for wrap filtering and alignment, this + // is stored as a negative value in info->scanline_extents.conservative.n0. Next, + // we need to skip the exact size of the right hand size filter area (again for + // wrap mode), this is in info->scanline_extents.edge_sizes[1]). + ofs = ( width + 1 - info->scanline_extents.conservative.n0 + info->scanline_extents.edge_sizes[1] ) * effective_channels; + + // place a known, but numerically valid value in the decode buffer + info->split_info[i].decode_buffer[ ofs ] = 9999.0f; + + // if vertical filtering first, place a known, but numerically valid value in the all + // of the ring buffer accumulators + if ( vertical_first ) + { + int j; + for( j = 0; j < info->ring_buffer_num_entries ; j++ ) + { + stbir__get_ring_buffer_entry( info, info->split_info + i, j )[ ofs ] = 9999.0f; + } + } + } + } + + #undef STBIR__NEXT_PTR + + + // is this the first time through loop? + if ( info == 0 ) + { + alloced_total = (int) ( 15 + (size_t)advance_mem ); + alloced = STBIR_MALLOC( alloced_total, user_data ); + if ( alloced == 0 ) + return 0; + } + else + return info; // success + } +} + +static int stbir__perform_resize( stbir__info const * info, int split_start, int split_count ) +{ + stbir__per_split_info * split_info = info->split_info + split_start; + + STBIR_PROFILE_CLEAR_EXTRAS(); + + STBIR_PROFILE_FIRST_START( looping ); + if (info->vertical.is_gather) + stbir__vertical_gather_loop( info, split_info, split_count ); + else + stbir__vertical_scatter_loop( info, split_info, split_count ); + STBIR_PROFILE_END( looping ); + + return 1; +} + +static void stbir__update_info_from_resize( stbir__info * info, STBIR_RESIZE * resize ) +{ + static stbir__decode_pixels_func * decode_simple[STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]= + { + /* 1ch-4ch */ stbir__decode_uint8_srgb, stbir__decode_uint8_srgb, 0, stbir__decode_float_linear, stbir__decode_half_float_linear, + }; + + static stbir__decode_pixels_func * decode_alphas[STBIRI_AR-STBIRI_RGBA+1][STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]= + { + { /* RGBA */ stbir__decode_uint8_srgb4_linearalpha, stbir__decode_uint8_srgb, 0, stbir__decode_float_linear, stbir__decode_half_float_linear }, + { /* BGRA */ stbir__decode_uint8_srgb4_linearalpha_BGRA, stbir__decode_uint8_srgb_BGRA, 0, stbir__decode_float_linear_BGRA, stbir__decode_half_float_linear_BGRA }, + { /* ARGB */ stbir__decode_uint8_srgb4_linearalpha_ARGB, stbir__decode_uint8_srgb_ARGB, 0, stbir__decode_float_linear_ARGB, stbir__decode_half_float_linear_ARGB }, + { /* ABGR */ stbir__decode_uint8_srgb4_linearalpha_ABGR, stbir__decode_uint8_srgb_ABGR, 0, stbir__decode_float_linear_ABGR, stbir__decode_half_float_linear_ABGR }, + { /* RA */ stbir__decode_uint8_srgb2_linearalpha, stbir__decode_uint8_srgb, 0, stbir__decode_float_linear, stbir__decode_half_float_linear }, + { /* AR */ stbir__decode_uint8_srgb2_linearalpha_AR, stbir__decode_uint8_srgb_AR, 0, stbir__decode_float_linear_AR, stbir__decode_half_float_linear_AR }, + }; + + static stbir__decode_pixels_func * decode_simple_scaled_or_not[2][2]= + { + { stbir__decode_uint8_linear_scaled, stbir__decode_uint8_linear }, { stbir__decode_uint16_linear_scaled, stbir__decode_uint16_linear }, + }; + + static stbir__decode_pixels_func * decode_alphas_scaled_or_not[STBIRI_AR-STBIRI_RGBA+1][2][2]= + { + { /* RGBA */ { stbir__decode_uint8_linear_scaled, stbir__decode_uint8_linear }, { stbir__decode_uint16_linear_scaled, stbir__decode_uint16_linear } }, + { /* BGRA */ { stbir__decode_uint8_linear_scaled_BGRA, stbir__decode_uint8_linear_BGRA }, { stbir__decode_uint16_linear_scaled_BGRA, stbir__decode_uint16_linear_BGRA } }, + { /* ARGB */ { stbir__decode_uint8_linear_scaled_ARGB, stbir__decode_uint8_linear_ARGB }, { stbir__decode_uint16_linear_scaled_ARGB, stbir__decode_uint16_linear_ARGB } }, + { /* ABGR */ { stbir__decode_uint8_linear_scaled_ABGR, stbir__decode_uint8_linear_ABGR }, { stbir__decode_uint16_linear_scaled_ABGR, stbir__decode_uint16_linear_ABGR } }, + { /* RA */ { stbir__decode_uint8_linear_scaled, stbir__decode_uint8_linear }, { stbir__decode_uint16_linear_scaled, stbir__decode_uint16_linear } }, + { /* AR */ { stbir__decode_uint8_linear_scaled_AR, stbir__decode_uint8_linear_AR }, { stbir__decode_uint16_linear_scaled_AR, stbir__decode_uint16_linear_AR } } + }; + + static stbir__encode_pixels_func * encode_simple[STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]= + { + /* 1ch-4ch */ stbir__encode_uint8_srgb, stbir__encode_uint8_srgb, 0, stbir__encode_float_linear, stbir__encode_half_float_linear, + }; + + static stbir__encode_pixels_func * encode_alphas[STBIRI_AR-STBIRI_RGBA+1][STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]= + { + { /* RGBA */ stbir__encode_uint8_srgb4_linearalpha, stbir__encode_uint8_srgb, 0, stbir__encode_float_linear, stbir__encode_half_float_linear }, + { /* BGRA */ stbir__encode_uint8_srgb4_linearalpha_BGRA, stbir__encode_uint8_srgb_BGRA, 0, stbir__encode_float_linear_BGRA, stbir__encode_half_float_linear_BGRA }, + { /* ARGB */ stbir__encode_uint8_srgb4_linearalpha_ARGB, stbir__encode_uint8_srgb_ARGB, 0, stbir__encode_float_linear_ARGB, stbir__encode_half_float_linear_ARGB }, + { /* ABGR */ stbir__encode_uint8_srgb4_linearalpha_ABGR, stbir__encode_uint8_srgb_ABGR, 0, stbir__encode_float_linear_ABGR, stbir__encode_half_float_linear_ABGR }, + { /* RA */ stbir__encode_uint8_srgb2_linearalpha, stbir__encode_uint8_srgb, 0, stbir__encode_float_linear, stbir__encode_half_float_linear }, + { /* AR */ stbir__encode_uint8_srgb2_linearalpha_AR, stbir__encode_uint8_srgb_AR, 0, stbir__encode_float_linear_AR, stbir__encode_half_float_linear_AR } + }; + + static stbir__encode_pixels_func * encode_simple_scaled_or_not[2][2]= + { + { stbir__encode_uint8_linear_scaled, stbir__encode_uint8_linear }, { stbir__encode_uint16_linear_scaled, stbir__encode_uint16_linear }, + }; + + static stbir__encode_pixels_func * encode_alphas_scaled_or_not[STBIRI_AR-STBIRI_RGBA+1][2][2]= + { + { /* RGBA */ { stbir__encode_uint8_linear_scaled, stbir__encode_uint8_linear }, { stbir__encode_uint16_linear_scaled, stbir__encode_uint16_linear } }, + { /* BGRA */ { stbir__encode_uint8_linear_scaled_BGRA, stbir__encode_uint8_linear_BGRA }, { stbir__encode_uint16_linear_scaled_BGRA, stbir__encode_uint16_linear_BGRA } }, + { /* ARGB */ { stbir__encode_uint8_linear_scaled_ARGB, stbir__encode_uint8_linear_ARGB }, { stbir__encode_uint16_linear_scaled_ARGB, stbir__encode_uint16_linear_ARGB } }, + { /* ABGR */ { stbir__encode_uint8_linear_scaled_ABGR, stbir__encode_uint8_linear_ABGR }, { stbir__encode_uint16_linear_scaled_ABGR, stbir__encode_uint16_linear_ABGR } }, + { /* RA */ { stbir__encode_uint8_linear_scaled, stbir__encode_uint8_linear }, { stbir__encode_uint16_linear_scaled, stbir__encode_uint16_linear } }, + { /* AR */ { stbir__encode_uint8_linear_scaled_AR, stbir__encode_uint8_linear_AR }, { stbir__encode_uint16_linear_scaled_AR, stbir__encode_uint16_linear_AR } } + }; + + stbir__decode_pixels_func * decode_pixels = 0; + stbir__encode_pixels_func * encode_pixels = 0; + stbir_datatype input_type, output_type; + + input_type = resize->input_data_type; + output_type = resize->output_data_type; + info->input_data = resize->input_pixels; + info->input_stride_bytes = resize->input_stride_in_bytes; + info->output_stride_bytes = resize->output_stride_in_bytes; + + // if we're completely point sampling, then we can turn off SRGB + if ( ( info->horizontal.filter_enum == STBIR_FILTER_POINT_SAMPLE ) && ( info->vertical.filter_enum == STBIR_FILTER_POINT_SAMPLE ) ) + { + if ( ( ( input_type == STBIR_TYPE_UINT8_SRGB ) || ( input_type == STBIR_TYPE_UINT8_SRGB_ALPHA ) ) && + ( ( output_type == STBIR_TYPE_UINT8_SRGB ) || ( output_type == STBIR_TYPE_UINT8_SRGB_ALPHA ) ) ) + { + input_type = STBIR_TYPE_UINT8; + output_type = STBIR_TYPE_UINT8; + } + } + + // recalc the output and input strides + if ( info->input_stride_bytes == 0 ) + info->input_stride_bytes = info->channels * info->horizontal.scale_info.input_full_size * stbir__type_size[input_type]; + + if ( info->output_stride_bytes == 0 ) + info->output_stride_bytes = info->channels * info->horizontal.scale_info.output_sub_size * stbir__type_size[output_type]; + + // calc offset + info->output_data = ( (char*) resize->output_pixels ) + ( (ptrdiff_t) info->offset_y * (ptrdiff_t) resize->output_stride_in_bytes ) + ( info->offset_x * info->channels * stbir__type_size[output_type] ); + + info->in_pixels_cb = resize->input_cb; + info->user_data = resize->user_data; + info->out_pixels_cb = resize->output_cb; + + // setup the input format converters + if ( ( input_type == STBIR_TYPE_UINT8 ) || ( input_type == STBIR_TYPE_UINT16 ) ) + { + int non_scaled = 0; + + // check if we can run unscaled - 0-255.0/0-65535.0 instead of 0-1.0 (which is a tiny bit faster when doing linear 8->8 or 16->16) + if ( ( !info->alpha_weight ) && ( !info->alpha_unweight ) ) // don't short circuit when alpha weighting (get everything to 0-1.0 as usual) + if ( ( ( input_type == STBIR_TYPE_UINT8 ) && ( output_type == STBIR_TYPE_UINT8 ) ) || ( ( input_type == STBIR_TYPE_UINT16 ) && ( output_type == STBIR_TYPE_UINT16 ) ) ) + non_scaled = 1; + + if ( info->input_pixel_layout_internal <= STBIRI_4CHANNEL ) + decode_pixels = decode_simple_scaled_or_not[ input_type == STBIR_TYPE_UINT16 ][ non_scaled ]; + else + decode_pixels = decode_alphas_scaled_or_not[ ( info->input_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ input_type == STBIR_TYPE_UINT16 ][ non_scaled ]; + } + else + { + if ( info->input_pixel_layout_internal <= STBIRI_4CHANNEL ) + decode_pixels = decode_simple[ input_type - STBIR_TYPE_UINT8_SRGB ]; + else + decode_pixels = decode_alphas[ ( info->input_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ input_type - STBIR_TYPE_UINT8_SRGB ]; + } + + // setup the output format converters + if ( ( output_type == STBIR_TYPE_UINT8 ) || ( output_type == STBIR_TYPE_UINT16 ) ) + { + int non_scaled = 0; + + // check if we can run unscaled - 0-255.0/0-65535.0 instead of 0-1.0 (which is a tiny bit faster when doing linear 8->8 or 16->16) + if ( ( !info->alpha_weight ) && ( !info->alpha_unweight ) ) // don't short circuit when alpha weighting (get everything to 0-1.0 as usual) + if ( ( ( input_type == STBIR_TYPE_UINT8 ) && ( output_type == STBIR_TYPE_UINT8 ) ) || ( ( input_type == STBIR_TYPE_UINT16 ) && ( output_type == STBIR_TYPE_UINT16 ) ) ) + non_scaled = 1; + + if ( info->output_pixel_layout_internal <= STBIRI_4CHANNEL ) + encode_pixels = encode_simple_scaled_or_not[ output_type == STBIR_TYPE_UINT16 ][ non_scaled ]; + else + encode_pixels = encode_alphas_scaled_or_not[ ( info->output_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ output_type == STBIR_TYPE_UINT16 ][ non_scaled ]; + } + else + { + if ( info->output_pixel_layout_internal <= STBIRI_4CHANNEL ) + encode_pixels = encode_simple[ output_type - STBIR_TYPE_UINT8_SRGB ]; + else + encode_pixels = encode_alphas[ ( info->output_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ output_type - STBIR_TYPE_UINT8_SRGB ]; + } + + info->input_type = input_type; + info->output_type = output_type; + info->decode_pixels = decode_pixels; + info->encode_pixels = encode_pixels; +} + +static void stbir__clip( int * outx, int * outsubw, int outw, double * u0, double * u1 ) +{ + double per, adj; + int over; + + // do left/top edge + if ( *outx < 0 ) + { + per = ( (double)*outx ) / ( (double)*outsubw ); // is negative + adj = per * ( *u1 - *u0 ); + *u0 -= adj; // increases u0 + *outx = 0; + } + + // do right/bot edge + over = outw - ( *outx + *outsubw ); + if ( over < 0 ) + { + per = ( (double)over ) / ( (double)*outsubw ); // is negative + adj = per * ( *u1 - *u0 ); + *u1 += adj; // decrease u1 + *outsubw = outw - *outx; + } +} + +// converts a double to a rational that has less than one float bit of error (returns 0 if unable to do so) +static int stbir__double_to_rational(double f, stbir_uint32 limit, stbir_uint32 *numer, stbir_uint32 *denom, int limit_denom ) // limit_denom (1) or limit numer (0) +{ + double err; + stbir_uint64 top, bot; + stbir_uint64 numer_last = 0; + stbir_uint64 denom_last = 1; + stbir_uint64 numer_estimate = 1; + stbir_uint64 denom_estimate = 0; + + // scale to past float error range + top = (stbir_uint64)( f * (double)(1 << 25) ); + bot = 1 << 25; + + // keep refining, but usually stops in a few loops - usually 5 for bad cases + for(;;) + { + stbir_uint64 est, temp; + + // hit limit, break out and do best full range estimate + if ( ( ( limit_denom ) ? denom_estimate : numer_estimate ) >= limit ) + break; + + // is the current error less than 1 bit of a float? if so, we're done + if ( denom_estimate ) + { + err = ( (double)numer_estimate / (double)denom_estimate ) - f; + if ( err < 0.0 ) err = -err; + if ( err < ( 1.0 / (double)(1<<24) ) ) + { + // yup, found it + *numer = (stbir_uint32) numer_estimate; + *denom = (stbir_uint32) denom_estimate; + return 1; + } + } + + // no more refinement bits left? break out and do full range estimate + if ( bot == 0 ) + break; + + // gcd the estimate bits + est = top / bot; + temp = top % bot; + top = bot; + bot = temp; + + // move remainders + temp = est * denom_estimate + denom_last; + denom_last = denom_estimate; + denom_estimate = temp; + + // move remainders + temp = est * numer_estimate + numer_last; + numer_last = numer_estimate; + numer_estimate = temp; + } + + // we didn't fine anything good enough for float, use a full range estimate + if ( limit_denom ) + { + numer_estimate= (stbir_uint64)( f * (double)limit + 0.5 ); + denom_estimate = limit; + } + else + { + numer_estimate = limit; + denom_estimate = (stbir_uint64)( ( (double)limit / f ) + 0.5 ); + } + + *numer = (stbir_uint32) numer_estimate; + *denom = (stbir_uint32) denom_estimate; + + err = ( denom_estimate ) ? ( ( (double)(stbir_uint32)numer_estimate / (double)(stbir_uint32)denom_estimate ) - f ) : 1.0; + if ( err < 0.0 ) err = -err; + return ( err < ( 1.0 / (double)(1<<24) ) ) ? 1 : 0; +} + +static int stbir__calculate_region_transform( stbir__scale_info * scale_info, int output_full_range, int * output_offset, int output_sub_range, int input_full_range, double input_s0, double input_s1 ) +{ + double output_range, input_range, output_s, input_s, ratio, scale; + + input_s = input_s1 - input_s0; + + // null area + if ( ( output_full_range == 0 ) || ( input_full_range == 0 ) || + ( output_sub_range == 0 ) || ( input_s <= stbir__small_float ) ) + return 0; + + // are either of the ranges completely out of bounds? + if ( ( *output_offset >= output_full_range ) || ( ( *output_offset + output_sub_range ) <= 0 ) || ( input_s0 >= (1.0f-stbir__small_float) ) || ( input_s1 <= stbir__small_float ) ) + return 0; + + output_range = (double)output_full_range; + input_range = (double)input_full_range; + + output_s = ( (double)output_sub_range) / output_range; + + // figure out the scaling to use + ratio = output_s / input_s; + + // save scale before clipping + scale = ( output_range / input_range ) * ratio; + scale_info->scale = (float)scale; + scale_info->inv_scale = (float)( 1.0 / scale ); + + // clip output area to left/right output edges (and adjust input area) + stbir__clip( output_offset, &output_sub_range, output_full_range, &input_s0, &input_s1 ); + + // recalc input area + input_s = input_s1 - input_s0; + + // after clipping do we have zero input area? + if ( input_s <= stbir__small_float ) + return 0; + + // calculate and store the starting source offsets in output pixel space + scale_info->pixel_shift = (float) ( input_s0 * ratio * output_range ); + + scale_info->scale_is_rational = stbir__double_to_rational( scale, ( scale <= 1.0 ) ? output_full_range : input_full_range, &scale_info->scale_numerator, &scale_info->scale_denominator, ( scale >= 1.0 ) ); + + scale_info->input_full_size = input_full_range; + scale_info->output_sub_size = output_sub_range; + + return 1; +} + + +static void stbir__init_and_set_layout( STBIR_RESIZE * resize, stbir_pixel_layout pixel_layout, stbir_datatype data_type ) +{ + resize->input_cb = 0; + resize->output_cb = 0; + resize->user_data = resize; + resize->samplers = 0; + resize->called_alloc = 0; + resize->horizontal_filter = STBIR_FILTER_DEFAULT; + resize->horizontal_filter_kernel = 0; resize->horizontal_filter_support = 0; + resize->vertical_filter = STBIR_FILTER_DEFAULT; + resize->vertical_filter_kernel = 0; resize->vertical_filter_support = 0; + resize->horizontal_edge = STBIR_EDGE_CLAMP; + resize->vertical_edge = STBIR_EDGE_CLAMP; + resize->input_s0 = 0; resize->input_t0 = 0; resize->input_s1 = 1; resize->input_t1 = 1; + resize->output_subx = 0; resize->output_suby = 0; resize->output_subw = resize->output_w; resize->output_subh = resize->output_h; + resize->input_data_type = data_type; + resize->output_data_type = data_type; + resize->input_pixel_layout_public = pixel_layout; + resize->output_pixel_layout_public = pixel_layout; + resize->needs_rebuild = 1; +} + +STBIRDEF void stbir_resize_init( STBIR_RESIZE * resize, + const void *input_pixels, int input_w, int input_h, int input_stride_in_bytes, // stride can be zero + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, // stride can be zero + stbir_pixel_layout pixel_layout, stbir_datatype data_type ) +{ + resize->input_pixels = input_pixels; + resize->input_w = input_w; + resize->input_h = input_h; + resize->input_stride_in_bytes = input_stride_in_bytes; + resize->output_pixels = output_pixels; + resize->output_w = output_w; + resize->output_h = output_h; + resize->output_stride_in_bytes = output_stride_in_bytes; + resize->fast_alpha = 0; + + stbir__init_and_set_layout( resize, pixel_layout, data_type ); +} + +// You can update parameters any time after resize_init +STBIRDEF void stbir_set_datatypes( STBIR_RESIZE * resize, stbir_datatype input_type, stbir_datatype output_type ) // by default, datatype from resize_init +{ + resize->input_data_type = input_type; + resize->output_data_type = output_type; + if ( ( resize->samplers ) && ( !resize->needs_rebuild ) ) + stbir__update_info_from_resize( resize->samplers, resize ); +} + +STBIRDEF void stbir_set_pixel_callbacks( STBIR_RESIZE * resize, stbir_input_callback * input_cb, stbir_output_callback * output_cb ) // no callbacks by default +{ + resize->input_cb = input_cb; + resize->output_cb = output_cb; + + if ( ( resize->samplers ) && ( !resize->needs_rebuild ) ) + { + resize->samplers->in_pixels_cb = input_cb; + resize->samplers->out_pixels_cb = output_cb; + } +} + +STBIRDEF void stbir_set_user_data( STBIR_RESIZE * resize, void * user_data ) // pass back STBIR_RESIZE* by default +{ + resize->user_data = user_data; + if ( ( resize->samplers ) && ( !resize->needs_rebuild ) ) + resize->samplers->user_data = user_data; +} + +STBIRDEF void stbir_set_buffer_ptrs( STBIR_RESIZE * resize, const void * input_pixels, int input_stride_in_bytes, void * output_pixels, int output_stride_in_bytes ) +{ + resize->input_pixels = input_pixels; + resize->input_stride_in_bytes = input_stride_in_bytes; + resize->output_pixels = output_pixels; + resize->output_stride_in_bytes = output_stride_in_bytes; + if ( ( resize->samplers ) && ( !resize->needs_rebuild ) ) + stbir__update_info_from_resize( resize->samplers, resize ); +} + + +STBIRDEF int stbir_set_edgemodes( STBIR_RESIZE * resize, stbir_edge horizontal_edge, stbir_edge vertical_edge ) // CLAMP by default +{ + resize->horizontal_edge = horizontal_edge; + resize->vertical_edge = vertical_edge; + resize->needs_rebuild = 1; + return 1; +} + +STBIRDEF int stbir_set_filters( STBIR_RESIZE * resize, stbir_filter horizontal_filter, stbir_filter vertical_filter ) // STBIR_DEFAULT_FILTER_UPSAMPLE/DOWNSAMPLE by default +{ + resize->horizontal_filter = horizontal_filter; + resize->vertical_filter = vertical_filter; + resize->needs_rebuild = 1; + return 1; +} + +STBIRDEF int stbir_set_filter_callbacks( STBIR_RESIZE * resize, stbir__kernel_callback * horizontal_filter, stbir__support_callback * horizontal_support, stbir__kernel_callback * vertical_filter, stbir__support_callback * vertical_support ) +{ + resize->horizontal_filter_kernel = horizontal_filter; resize->horizontal_filter_support = horizontal_support; + resize->vertical_filter_kernel = vertical_filter; resize->vertical_filter_support = vertical_support; + resize->needs_rebuild = 1; + return 1; +} + +STBIRDEF int stbir_set_pixel_layouts( STBIR_RESIZE * resize, stbir_pixel_layout input_pixel_layout, stbir_pixel_layout output_pixel_layout ) // sets new pixel layouts +{ + resize->input_pixel_layout_public = input_pixel_layout; + resize->output_pixel_layout_public = output_pixel_layout; + resize->needs_rebuild = 1; + return 1; +} + + +STBIRDEF int stbir_set_non_pm_alpha_speed_over_quality( STBIR_RESIZE * resize, int non_pma_alpha_speed_over_quality ) // sets alpha speed +{ + resize->fast_alpha = non_pma_alpha_speed_over_quality; + resize->needs_rebuild = 1; + return 1; +} + +STBIRDEF int stbir_set_input_subrect( STBIR_RESIZE * resize, double s0, double t0, double s1, double t1 ) // sets input region (full region by default) +{ + resize->input_s0 = s0; + resize->input_t0 = t0; + resize->input_s1 = s1; + resize->input_t1 = t1; + resize->needs_rebuild = 1; + + // are we inbounds? + if ( ( s1 < stbir__small_float ) || ( (s1-s0) < stbir__small_float ) || + ( t1 < stbir__small_float ) || ( (t1-t0) < stbir__small_float ) || + ( s0 > (1.0f-stbir__small_float) ) || + ( t0 > (1.0f-stbir__small_float) ) ) + return 0; + + return 1; +} + +STBIRDEF int stbir_set_output_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ) // sets input region (full region by default) +{ + resize->output_subx = subx; + resize->output_suby = suby; + resize->output_subw = subw; + resize->output_subh = subh; + resize->needs_rebuild = 1; + + // are we inbounds? + if ( ( subx >= resize->output_w ) || ( ( subx + subw ) <= 0 ) || ( suby >= resize->output_h ) || ( ( suby + subh ) <= 0 ) || ( subw == 0 ) || ( subh == 0 ) ) + return 0; + + return 1; +} + +STBIRDEF int stbir_set_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ) // sets both regions (full regions by default) +{ + double s0, t0, s1, t1; + + s0 = ( (double)subx ) / ( (double)resize->output_w ); + t0 = ( (double)suby ) / ( (double)resize->output_h ); + s1 = ( (double)(subx+subw) ) / ( (double)resize->output_w ); + t1 = ( (double)(suby+subh) ) / ( (double)resize->output_h ); + + resize->input_s0 = s0; + resize->input_t0 = t0; + resize->input_s1 = s1; + resize->input_t1 = t1; + resize->output_subx = subx; + resize->output_suby = suby; + resize->output_subw = subw; + resize->output_subh = subh; + resize->needs_rebuild = 1; + + // are we inbounds? + if ( ( subx >= resize->output_w ) || ( ( subx + subw ) <= 0 ) || ( suby >= resize->output_h ) || ( ( suby + subh ) <= 0 ) || ( subw == 0 ) || ( subh == 0 ) ) + return 0; + + return 1; +} + +static int stbir__perform_build( STBIR_RESIZE * resize, int splits ) +{ + stbir__contributors conservative = { 0, 0 }; + stbir__sampler horizontal, vertical; + int new_output_subx, new_output_suby; + stbir__info * out_info; + #ifdef STBIR_PROFILE + stbir__info profile_infod; // used to contain building profile info before everything is allocated + stbir__info * profile_info = &profile_infod; + #endif + + // have we already built the samplers? + if ( resize->samplers ) + return 0; + + #define STBIR_RETURN_ERROR_AND_ASSERT( exp ) STBIR_ASSERT( !(exp) ); if (exp) return 0; + STBIR_RETURN_ERROR_AND_ASSERT( (unsigned)resize->horizontal_filter >= STBIR_FILTER_OTHER) + STBIR_RETURN_ERROR_AND_ASSERT( (unsigned)resize->vertical_filter >= STBIR_FILTER_OTHER) + #undef STBIR_RETURN_ERROR_AND_ASSERT + + if ( splits <= 0 ) + return 0; + + STBIR_PROFILE_BUILD_FIRST_START( build ); + + new_output_subx = resize->output_subx; + new_output_suby = resize->output_suby; + + // do horizontal clip and scale calcs + if ( !stbir__calculate_region_transform( &horizontal.scale_info, resize->output_w, &new_output_subx, resize->output_subw, resize->input_w, resize->input_s0, resize->input_s1 ) ) + return 0; + + // do vertical clip and scale calcs + if ( !stbir__calculate_region_transform( &vertical.scale_info, resize->output_h, &new_output_suby, resize->output_subh, resize->input_h, resize->input_t0, resize->input_t1 ) ) + return 0; + + // if nothing to do, just return + if ( ( horizontal.scale_info.output_sub_size == 0 ) || ( vertical.scale_info.output_sub_size == 0 ) ) + return 0; + + stbir__set_sampler(&horizontal, resize->horizontal_filter, resize->horizontal_filter_kernel, resize->horizontal_filter_support, resize->horizontal_edge, &horizontal.scale_info, 1, resize->user_data ); + stbir__get_conservative_extents( &horizontal, &conservative, resize->user_data ); + stbir__set_sampler(&vertical, resize->vertical_filter, resize->horizontal_filter_kernel, resize->vertical_filter_support, resize->vertical_edge, &vertical.scale_info, 0, resize->user_data ); + + if ( ( vertical.scale_info.output_sub_size / splits ) < STBIR_FORCE_MINIMUM_SCANLINES_FOR_SPLITS ) // each split should be a minimum of 4 scanlines (handwavey choice) + { + splits = vertical.scale_info.output_sub_size / STBIR_FORCE_MINIMUM_SCANLINES_FOR_SPLITS; + if ( splits == 0 ) splits = 1; + } + + STBIR_PROFILE_BUILD_START( alloc ); + out_info = stbir__alloc_internal_mem_and_build_samplers( &horizontal, &vertical, &conservative, resize->input_pixel_layout_public, resize->output_pixel_layout_public, splits, new_output_subx, new_output_suby, resize->fast_alpha, resize->user_data STBIR_ONLY_PROFILE_BUILD_SET_INFO ); + STBIR_PROFILE_BUILD_END( alloc ); + STBIR_PROFILE_BUILD_END( build ); + + if ( out_info ) + { + resize->splits = splits; + resize->samplers = out_info; + resize->needs_rebuild = 0; + #ifdef STBIR_PROFILE + STBIR_MEMCPY( &out_info->profile, &profile_infod.profile, sizeof( out_info->profile ) ); + #endif + + // update anything that can be changed without recalcing samplers + stbir__update_info_from_resize( out_info, resize ); + + return splits; + } + + return 0; +} + +void stbir_free_samplers( STBIR_RESIZE * resize ) +{ + if ( resize->samplers ) + { + stbir__free_internal_mem( resize->samplers ); + resize->samplers = 0; + resize->called_alloc = 0; + } +} + +STBIRDEF int stbir_build_samplers_with_splits( STBIR_RESIZE * resize, int splits ) +{ + if ( ( resize->samplers == 0 ) || ( resize->needs_rebuild ) ) + { + if ( resize->samplers ) + stbir_free_samplers( resize ); + + resize->called_alloc = 1; + return stbir__perform_build( resize, splits ); + } + + STBIR_PROFILE_BUILD_CLEAR( resize->samplers ); + + return 1; +} + +STBIRDEF int stbir_build_samplers( STBIR_RESIZE * resize ) +{ + return stbir_build_samplers_with_splits( resize, 1 ); +} + +STBIRDEF int stbir_resize_extended( STBIR_RESIZE * resize ) +{ + int result; + + if ( ( resize->samplers == 0 ) || ( resize->needs_rebuild ) ) + { + int alloc_state = resize->called_alloc; // remember allocated state + + if ( resize->samplers ) + { + stbir__free_internal_mem( resize->samplers ); + resize->samplers = 0; + } + + if ( !stbir_build_samplers( resize ) ) + return 0; + + resize->called_alloc = alloc_state; + + // if build_samplers succeeded (above), but there are no samplers set, then + // the area to stretch into was zero pixels, so don't do anything and return + // success + if ( resize->samplers == 0 ) + return 1; + } + else + { + // didn't build anything - clear it + STBIR_PROFILE_BUILD_CLEAR( resize->samplers ); + } + + // do resize + result = stbir__perform_resize( resize->samplers, 0, resize->splits ); + + // if we alloced, then free + if ( !resize->called_alloc ) + { + stbir_free_samplers( resize ); + resize->samplers = 0; + } + + return result; +} + +STBIRDEF int stbir_resize_extended_split( STBIR_RESIZE * resize, int split_start, int split_count ) +{ + STBIR_ASSERT( resize->samplers ); + + // if we're just doing the whole thing, call full + if ( ( split_start == -1 ) || ( ( split_start == 0 ) && ( split_count == resize->splits ) ) ) + return stbir_resize_extended( resize ); + + // you **must** build samplers first when using split resize + if ( ( resize->samplers == 0 ) || ( resize->needs_rebuild ) ) + return 0; + + if ( ( split_start >= resize->splits ) || ( split_start < 0 ) || ( ( split_start + split_count ) > resize->splits ) || ( split_count <= 0 ) ) + return 0; + + // do resize + return stbir__perform_resize( resize->samplers, split_start, split_count ); +} + +static int stbir__check_output_stuff( void ** ret_ptr, int * ret_pitch, void * output_pixels, int type_size, int output_w, int output_h, int output_stride_in_bytes, stbir_internal_pixel_layout pixel_layout ) +{ + size_t size; + int pitch; + void * ptr; + + pitch = output_w * type_size * stbir__pixel_channels[ pixel_layout ]; + if ( pitch == 0 ) + return 0; + + if ( output_stride_in_bytes == 0 ) + output_stride_in_bytes = pitch; + + if ( output_stride_in_bytes < pitch ) + return 0; + + size = output_stride_in_bytes * output_h; + if ( size == 0 ) + return 0; + + *ret_ptr = 0; + *ret_pitch = output_stride_in_bytes; + + if ( output_pixels == 0 ) + { + ptr = STBIR_MALLOC( size, 0 ); + if ( ptr == 0 ) + return 0; + + *ret_ptr = ptr; + *ret_pitch = pitch; + } + + return 1; +} + + +STBIRDEF unsigned char * stbir_resize_uint8_linear( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout ) +{ + STBIR_RESIZE resize; + unsigned char * optr; + int opitch; + + if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, sizeof( unsigned char ), output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) + return 0; + + stbir_resize_init( &resize, + input_pixels, input_w, input_h, input_stride_in_bytes, + (optr) ? optr : output_pixels, output_w, output_h, opitch, + pixel_layout, STBIR_TYPE_UINT8 ); + + if ( !stbir_resize_extended( &resize ) ) + { + if ( optr ) + STBIR_FREE( optr, 0 ); + return 0; + } + + return (optr) ? optr : output_pixels; +} + +STBIRDEF unsigned char * stbir_resize_uint8_srgb( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout ) +{ + STBIR_RESIZE resize; + unsigned char * optr; + int opitch; + + if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, sizeof( unsigned char ), output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) + return 0; + + stbir_resize_init( &resize, + input_pixels, input_w, input_h, input_stride_in_bytes, + (optr) ? optr : output_pixels, output_w, output_h, opitch, + pixel_layout, STBIR_TYPE_UINT8_SRGB ); + + if ( !stbir_resize_extended( &resize ) ) + { + if ( optr ) + STBIR_FREE( optr, 0 ); + return 0; + } + + return (optr) ? optr : output_pixels; +} + + +STBIRDEF float * stbir_resize_float_linear( const float *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout ) +{ + STBIR_RESIZE resize; + float * optr; + int opitch; + + if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, sizeof( float ), output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) + return 0; + + stbir_resize_init( &resize, + input_pixels, input_w, input_h, input_stride_in_bytes, + (optr) ? optr : output_pixels, output_w, output_h, opitch, + pixel_layout, STBIR_TYPE_FLOAT ); + + if ( !stbir_resize_extended( &resize ) ) + { + if ( optr ) + STBIR_FREE( optr, 0 ); + return 0; + } + + return (optr) ? optr : output_pixels; +} + + +STBIRDEF void * stbir_resize( const void *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout, stbir_datatype data_type, + stbir_edge edge, stbir_filter filter ) +{ + STBIR_RESIZE resize; + float * optr; + int opitch; + + if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, stbir__type_size[data_type], output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) + return 0; + + stbir_resize_init( &resize, + input_pixels, input_w, input_h, input_stride_in_bytes, + (optr) ? optr : output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout, data_type ); + + resize.horizontal_edge = edge; + resize.vertical_edge = edge; + resize.horizontal_filter = filter; + resize.vertical_filter = filter; + + if ( !stbir_resize_extended( &resize ) ) + { + if ( optr ) + STBIR_FREE( optr, 0 ); + return 0; + } + + return (optr) ? optr : output_pixels; +} + +#ifdef STBIR_PROFILE + +STBIRDEF void stbir_resize_build_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize ) +{ + static char const * bdescriptions[6] = { "Building", "Allocating", "Horizontal sampler", "Vertical sampler", "Coefficient cleanup", "Coefficient piovot" } ; + stbir__info* samp = resize->samplers; + int i; + + typedef int testa[ (STBIR__ARRAY_SIZE( bdescriptions ) == (STBIR__ARRAY_SIZE( samp->profile.array )-1) )?1:-1]; + typedef int testb[ (sizeof( samp->profile.array ) == (sizeof(samp->profile.named)) )?1:-1]; + typedef int testc[ (sizeof( info->clocks ) >= (sizeof(samp->profile.named)) )?1:-1]; + + for( i = 0 ; i < STBIR__ARRAY_SIZE( bdescriptions ) ; i++) + info->clocks[i] = samp->profile.array[i+1]; + + info->total_clocks = samp->profile.named.total; + info->descriptions = bdescriptions; + info->count = STBIR__ARRAY_SIZE( bdescriptions ); +} + +STBIRDEF void stbir_resize_split_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize, int split_start, int split_count ) +{ + static char const * descriptions[7] = { "Looping", "Vertical sampling", "Horizontal sampling", "Scanline input", "Scanline output", "Alpha weighting", "Alpha unweighting" }; + stbir__per_split_info * split_info; + int s, i; + + typedef int testa[ (STBIR__ARRAY_SIZE( descriptions ) == (STBIR__ARRAY_SIZE( split_info->profile.array )-1) )?1:-1]; + typedef int testb[ (sizeof( split_info->profile.array ) == (sizeof(split_info->profile.named)) )?1:-1]; + typedef int testc[ (sizeof( info->clocks ) >= (sizeof(split_info->profile.named)) )?1:-1]; + + if ( split_start == -1 ) + { + split_start = 0; + split_count = resize->samplers->splits; + } + + if ( ( split_start >= resize->splits ) || ( split_start < 0 ) || ( ( split_start + split_count ) > resize->splits ) || ( split_count <= 0 ) ) + { + info->total_clocks = 0; + info->descriptions = 0; + info->count = 0; + return; + } + + split_info = resize->samplers->split_info + split_start; + + // sum up the profile from all the splits + for( i = 0 ; i < STBIR__ARRAY_SIZE( descriptions ) ; i++ ) + { + stbir_uint64 sum = 0; + for( s = 0 ; s < split_count ; s++ ) + sum += split_info[s].profile.array[i+1]; + info->clocks[i] = sum; + } + + info->total_clocks = split_info->profile.named.total; + info->descriptions = descriptions; + info->count = STBIR__ARRAY_SIZE( descriptions ); +} + +STBIRDEF void stbir_resize_extended_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize ) +{ + stbir_resize_split_profile_info( info, resize, -1, 0 ); +} + +#endif // STBIR_PROFILE + +#undef STBIR_BGR +#undef STBIR_1CHANNEL +#undef STBIR_2CHANNEL +#undef STBIR_RGB +#undef STBIR_RGBA +#undef STBIR_4CHANNEL +#undef STBIR_BGRA +#undef STBIR_ARGB +#undef STBIR_ABGR +#undef STBIR_RA +#undef STBIR_AR +#undef STBIR_RGBA_PM +#undef STBIR_BGRA_PM +#undef STBIR_ARGB_PM +#undef STBIR_ABGR_PM +#undef STBIR_RA_PM +#undef STBIR_AR_PM + +#endif // STB_IMAGE_RESIZE_IMPLEMENTATION + +#else // STB_IMAGE_RESIZE_HORIZONTALS&STB_IMAGE_RESIZE_DO_VERTICALS + +// we reinclude the header file to define all the horizontal functions +// specializing each function for the number of coeffs is 20-40% faster *OVERALL* + +// by including the header file again this way, we can still debug the functions + +#define STBIR_strs_join2( start, mid, end ) start##mid##end +#define STBIR_strs_join1( start, mid, end ) STBIR_strs_join2( start, mid, end ) + +#define STBIR_strs_join24( start, mid1, mid2, end ) start##mid1##mid2##end +#define STBIR_strs_join14( start, mid1, mid2, end ) STBIR_strs_join24( start, mid1, mid2, end ) + +#ifdef STB_IMAGE_RESIZE_DO_CODERS + +#ifdef stbir__decode_suffix +#define STBIR__CODER_NAME( name ) STBIR_strs_join1( name, _, stbir__decode_suffix ) +#else +#define STBIR__CODER_NAME( name ) name +#endif + +#ifdef stbir__decode_swizzle +#define stbir__decode_simdf8_flip(reg) STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( stbir__simdf8_0123to,stbir__decode_order0,stbir__decode_order1),stbir__decode_order2,stbir__decode_order3),stbir__decode_order0,stbir__decode_order1),stbir__decode_order2,stbir__decode_order3)(reg, reg) +#define stbir__decode_simdf4_flip(reg) STBIR_strs_join1( STBIR_strs_join1( stbir__simdf_0123to,stbir__decode_order0,stbir__decode_order1),stbir__decode_order2,stbir__decode_order3)(reg, reg) +#define stbir__encode_simdf8_unflip(reg) STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( stbir__simdf8_0123to,stbir__encode_order0,stbir__encode_order1),stbir__encode_order2,stbir__encode_order3),stbir__encode_order0,stbir__encode_order1),stbir__encode_order2,stbir__encode_order3)(reg, reg) +#define stbir__encode_simdf4_unflip(reg) STBIR_strs_join1( STBIR_strs_join1( stbir__simdf_0123to,stbir__encode_order0,stbir__encode_order1),stbir__encode_order2,stbir__encode_order3)(reg, reg) +#else +#define stbir__decode_order0 0 +#define stbir__decode_order1 1 +#define stbir__decode_order2 2 +#define stbir__decode_order3 3 +#define stbir__encode_order0 0 +#define stbir__encode_order1 1 +#define stbir__encode_order2 2 +#define stbir__encode_order3 3 +#define stbir__decode_simdf8_flip(reg) +#define stbir__decode_simdf4_flip(reg) +#define stbir__encode_simdf8_unflip(reg) +#define stbir__encode_simdf4_unflip(reg) +#endif + +#ifdef STBIR_SIMD8 +#define stbir__encode_simdfX_unflip stbir__encode_simdf8_unflip +#else +#define stbir__encode_simdfX_unflip stbir__encode_simdf4_unflip +#endif + +static void STBIR__CODER_NAME( stbir__decode_uint8_linear_scaled )( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const*)inputp; + + #ifdef STBIR_SIMD + unsigned char const * end_input_m16 = input + width_times_channels - 16; + if ( width_times_channels >= 16 ) + { + decode_end -= 16; + for(;;) + { + #ifdef STBIR_SIMD8 + stbir__simdi i; stbir__simdi8 o0,o1; + stbir__simdf8 of0, of1; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi8_expand_u8_to_u32( o0, o1, i ); + stbir__simdi8_convert_i32_to_float( of0, o0 ); + stbir__simdi8_convert_i32_to_float( of1, o1 ); + stbir__simdf8_mult( of0, of0, STBIR_max_uint8_as_float_inverted8); + stbir__simdf8_mult( of1, of1, STBIR_max_uint8_as_float_inverted8); + stbir__decode_simdf8_flip( of0 ); + stbir__decode_simdf8_flip( of1 ); + stbir__simdf8_store( decode + 0, of0 ); + stbir__simdf8_store( decode + 8, of1 ); + #else + stbir__simdi i, o0, o1, o2, o3; + stbir__simdf of0, of1, of2, of3; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi_expand_u8_to_u32( o0,o1,o2,o3,i); + stbir__simdi_convert_i32_to_float( of0, o0 ); + stbir__simdi_convert_i32_to_float( of1, o1 ); + stbir__simdi_convert_i32_to_float( of2, o2 ); + stbir__simdi_convert_i32_to_float( of3, o3 ); + stbir__simdf_mult( of0, of0, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) ); + stbir__simdf_mult( of1, of1, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) ); + stbir__simdf_mult( of2, of2, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) ); + stbir__simdf_mult( of3, of3, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__decode_simdf4_flip( of2 ); + stbir__decode_simdf4_flip( of3 ); + stbir__simdf_store( decode + 0, of0 ); + stbir__simdf_store( decode + 4, of1 ); + stbir__simdf_store( decode + 8, of2 ); + stbir__simdf_store( decode + 12, of3 ); + #endif + decode += 16; + input += 16; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 16 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m16; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = ((float)(input[stbir__decode_order0])) * stbir__max_uint8_as_float_inverted; + decode[1-4] = ((float)(input[stbir__decode_order1])) * stbir__max_uint8_as_float_inverted; + decode[2-4] = ((float)(input[stbir__decode_order2])) * stbir__max_uint8_as_float_inverted; + decode[3-4] = ((float)(input[stbir__decode_order3])) * stbir__max_uint8_as_float_inverted; + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = ((float)(input[stbir__decode_order0])) * stbir__max_uint8_as_float_inverted; + #if stbir__coder_min_num >= 2 + decode[1] = ((float)(input[stbir__decode_order1])) * stbir__max_uint8_as_float_inverted; + #endif + #if stbir__coder_min_num >= 3 + decode[2] = ((float)(input[stbir__decode_order2])) * stbir__max_uint8_as_float_inverted; + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME( stbir__encode_uint8_linear_scaled )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char *) outputp; + unsigned char * end_output = ( (unsigned char *) output ) + width_times_channels; + + #ifdef STBIR_SIMD + if ( width_times_channels >= stbir__simdfX_float_count*2 ) + { + float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2; + end_output -= stbir__simdfX_float_count*2; + for(;;) + { + stbir__simdfX e0, e1; + stbir__simdi i; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_madd_mem( e0, STBIR_simd_point5X, STBIR_max_uint8_as_floatX, encode ); + stbir__simdfX_madd_mem( e1, STBIR_simd_point5X, STBIR_max_uint8_as_floatX, encode+stbir__simdfX_float_count ); + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + #ifdef STBIR_SIMD8 + stbir__simdf8_pack_to_16bytes( i, e0, e1 ); + stbir__simdi_store( output, i ); + #else + stbir__simdf_pack_to_8bytes( i, e0, e1 ); + stbir__simdi_store2( output, i ); + #endif + encode += stbir__simdfX_float_count*2; + output += stbir__simdfX_float_count*2; + if ( output <= end_output ) + continue; + if ( output == ( end_output + stbir__simdfX_float_count*2 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e0; + stbir__simdi i0; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e0, encode ); + stbir__simdf_madd( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), e0 ); + stbir__encode_simdf4_unflip( e0 ); + stbir__simdf_pack_to_8bytes( i0, e0, e0 ); // only use first 4 + *(int*)(output-4) = stbir__simdi_to_int( i0 ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + stbir__simdf e0; + STBIR_NO_UNROLL(encode); + stbir__simdf_madd1_mem( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), encode+stbir__encode_order0 ); output[0] = stbir__simdf_convert_float_to_uint8( e0 ); + #if stbir__coder_min_num >= 2 + stbir__simdf_madd1_mem( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), encode+stbir__encode_order1 ); output[1] = stbir__simdf_convert_float_to_uint8( e0 ); + #endif + #if stbir__coder_min_num >= 3 + stbir__simdf_madd1_mem( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), encode+stbir__encode_order2 ); output[2] = stbir__simdf_convert_float_to_uint8( e0 ); + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float f; + f = encode[stbir__encode_order0] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[0-4] = (unsigned char)f; + f = encode[stbir__encode_order1] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[1-4] = (unsigned char)f; + f = encode[stbir__encode_order2] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[2-4] = (unsigned char)f; + f = encode[stbir__encode_order3] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[3-4] = (unsigned char)f; + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float f; + STBIR_NO_UNROLL(encode); + f = encode[stbir__encode_order0] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[0] = (unsigned char)f; + #if stbir__coder_min_num >= 2 + f = encode[stbir__encode_order1] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[1] = (unsigned char)f; + #endif + #if stbir__coder_min_num >= 3 + f = encode[stbir__encode_order2] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[2] = (unsigned char)f; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_uint8_linear)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const*)inputp; + + #ifdef STBIR_SIMD + unsigned char const * end_input_m16 = input + width_times_channels - 16; + if ( width_times_channels >= 16 ) + { + decode_end -= 16; + for(;;) + { + #ifdef STBIR_SIMD8 + stbir__simdi i; stbir__simdi8 o0,o1; + stbir__simdf8 of0, of1; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi8_expand_u8_to_u32( o0, o1, i ); + stbir__simdi8_convert_i32_to_float( of0, o0 ); + stbir__simdi8_convert_i32_to_float( of1, o1 ); + stbir__decode_simdf8_flip( of0 ); + stbir__decode_simdf8_flip( of1 ); + stbir__simdf8_store( decode + 0, of0 ); + stbir__simdf8_store( decode + 8, of1 ); + #else + stbir__simdi i, o0, o1, o2, o3; + stbir__simdf of0, of1, of2, of3; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi_expand_u8_to_u32( o0,o1,o2,o3,i); + stbir__simdi_convert_i32_to_float( of0, o0 ); + stbir__simdi_convert_i32_to_float( of1, o1 ); + stbir__simdi_convert_i32_to_float( of2, o2 ); + stbir__simdi_convert_i32_to_float( of3, o3 ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__decode_simdf4_flip( of2 ); + stbir__decode_simdf4_flip( of3 ); + stbir__simdf_store( decode + 0, of0 ); + stbir__simdf_store( decode + 4, of1 ); + stbir__simdf_store( decode + 8, of2 ); + stbir__simdf_store( decode + 12, of3 ); +#endif + decode += 16; + input += 16; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 16 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m16; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = ((float)(input[stbir__decode_order0])); + decode[1-4] = ((float)(input[stbir__decode_order1])); + decode[2-4] = ((float)(input[stbir__decode_order2])); + decode[3-4] = ((float)(input[stbir__decode_order3])); + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = ((float)(input[stbir__decode_order0])); + #if stbir__coder_min_num >= 2 + decode[1] = ((float)(input[stbir__decode_order1])); + #endif + #if stbir__coder_min_num >= 3 + decode[2] = ((float)(input[stbir__decode_order2])); + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME( stbir__encode_uint8_linear )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char *) outputp; + unsigned char * end_output = ( (unsigned char *) output ) + width_times_channels; + + #ifdef STBIR_SIMD + if ( width_times_channels >= stbir__simdfX_float_count*2 ) + { + float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2; + end_output -= stbir__simdfX_float_count*2; + for(;;) + { + stbir__simdfX e0, e1; + stbir__simdi i; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_add_mem( e0, STBIR_simd_point5X, encode ); + stbir__simdfX_add_mem( e1, STBIR_simd_point5X, encode+stbir__simdfX_float_count ); + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + #ifdef STBIR_SIMD8 + stbir__simdf8_pack_to_16bytes( i, e0, e1 ); + stbir__simdi_store( output, i ); + #else + stbir__simdf_pack_to_8bytes( i, e0, e1 ); + stbir__simdi_store2( output, i ); + #endif + encode += stbir__simdfX_float_count*2; + output += stbir__simdfX_float_count*2; + if ( output <= end_output ) + continue; + if ( output == ( end_output + stbir__simdfX_float_count*2 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e0; + stbir__simdi i0; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e0, encode ); + stbir__simdf_add( e0, STBIR__CONSTF(STBIR_simd_point5), e0 ); + stbir__encode_simdf4_unflip( e0 ); + stbir__simdf_pack_to_8bytes( i0, e0, e0 ); // only use first 4 + *(int*)(output-4) = stbir__simdi_to_int( i0 ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float f; + f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 255); output[0-4] = (unsigned char)f; + f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 255); output[1-4] = (unsigned char)f; + f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 255); output[2-4] = (unsigned char)f; + f = encode[stbir__encode_order3] + 0.5f; STBIR_CLAMP(f, 0, 255); output[3-4] = (unsigned char)f; + output += 4; + encode += 4; + } + output -= 4; + #endif + + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float f; + STBIR_NO_UNROLL(encode); + f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 255); output[0] = (unsigned char)f; + #if stbir__coder_min_num >= 2 + f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 255); output[1] = (unsigned char)f; + #endif + #if stbir__coder_min_num >= 3 + f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 255); output[2] = (unsigned char)f; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_uint8_srgb)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float const * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const *)inputp; + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + decode[0-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order0 ] ]; + decode[1-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order1 ] ]; + decode[2-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order2 ] ]; + decode[3-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order3 ] ]; + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order0 ] ]; + #if stbir__coder_min_num >= 2 + decode[1] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order1 ] ]; + #endif + #if stbir__coder_min_num >= 3 + decode[2] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order2 ] ]; + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +#define stbir__min_max_shift20( i, f ) \ + stbir__simdf_max( f, f, stbir_simdf_casti(STBIR__CONSTI( STBIR_almost_zero )) ); \ + stbir__simdf_min( f, f, stbir_simdf_casti(STBIR__CONSTI( STBIR_almost_one )) ); \ + stbir__simdi_32shr( i, stbir_simdi_castf( f ), 20 ); + +#define stbir__scale_and_convert( i, f ) \ + stbir__simdf_madd( f, STBIR__CONSTF( STBIR_simd_point5 ), STBIR__CONSTF( STBIR_max_uint8_as_float ), f ); \ + stbir__simdf_max( f, f, stbir__simdf_zeroP() ); \ + stbir__simdf_min( f, f, STBIR__CONSTF( STBIR_max_uint8_as_float ) ); \ + stbir__simdf_convert_float_to_i32( i, f ); + +#define stbir__linear_to_srgb_finish( i, f ) \ +{ \ + stbir__simdi temp; \ + stbir__simdi_32shr( temp, stbir_simdi_castf( f ), 12 ) ; \ + stbir__simdi_and( temp, temp, STBIR__CONSTI(STBIR_mastissa_mask) ); \ + stbir__simdi_or( temp, temp, STBIR__CONSTI(STBIR_topscale) ); \ + stbir__simdi_16madd( i, i, temp ); \ + stbir__simdi_32shr( i, i, 16 ); \ +} + +#define stbir__simdi_table_lookup2( v0,v1, table ) \ +{ \ + stbir__simdi_u32 temp0,temp1; \ + temp0.m128i_i128 = v0; \ + temp1.m128i_i128 = v1; \ + temp0.m128i_u32[0] = table[temp0.m128i_i32[0]]; temp0.m128i_u32[1] = table[temp0.m128i_i32[1]]; temp0.m128i_u32[2] = table[temp0.m128i_i32[2]]; temp0.m128i_u32[3] = table[temp0.m128i_i32[3]]; \ + temp1.m128i_u32[0] = table[temp1.m128i_i32[0]]; temp1.m128i_u32[1] = table[temp1.m128i_i32[1]]; temp1.m128i_u32[2] = table[temp1.m128i_i32[2]]; temp1.m128i_u32[3] = table[temp1.m128i_i32[3]]; \ + v0 = temp0.m128i_i128; \ + v1 = temp1.m128i_i128; \ +} + +#define stbir__simdi_table_lookup3( v0,v1,v2, table ) \ +{ \ + stbir__simdi_u32 temp0,temp1,temp2; \ + temp0.m128i_i128 = v0; \ + temp1.m128i_i128 = v1; \ + temp2.m128i_i128 = v2; \ + temp0.m128i_u32[0] = table[temp0.m128i_i32[0]]; temp0.m128i_u32[1] = table[temp0.m128i_i32[1]]; temp0.m128i_u32[2] = table[temp0.m128i_i32[2]]; temp0.m128i_u32[3] = table[temp0.m128i_i32[3]]; \ + temp1.m128i_u32[0] = table[temp1.m128i_i32[0]]; temp1.m128i_u32[1] = table[temp1.m128i_i32[1]]; temp1.m128i_u32[2] = table[temp1.m128i_i32[2]]; temp1.m128i_u32[3] = table[temp1.m128i_i32[3]]; \ + temp2.m128i_u32[0] = table[temp2.m128i_i32[0]]; temp2.m128i_u32[1] = table[temp2.m128i_i32[1]]; temp2.m128i_u32[2] = table[temp2.m128i_i32[2]]; temp2.m128i_u32[3] = table[temp2.m128i_i32[3]]; \ + v0 = temp0.m128i_i128; \ + v1 = temp1.m128i_i128; \ + v2 = temp2.m128i_i128; \ +} + +#define stbir__simdi_table_lookup4( v0,v1,v2,v3, table ) \ +{ \ + stbir__simdi_u32 temp0,temp1,temp2,temp3; \ + temp0.m128i_i128 = v0; \ + temp1.m128i_i128 = v1; \ + temp2.m128i_i128 = v2; \ + temp3.m128i_i128 = v3; \ + temp0.m128i_u32[0] = table[temp0.m128i_i32[0]]; temp0.m128i_u32[1] = table[temp0.m128i_i32[1]]; temp0.m128i_u32[2] = table[temp0.m128i_i32[2]]; temp0.m128i_u32[3] = table[temp0.m128i_i32[3]]; \ + temp1.m128i_u32[0] = table[temp1.m128i_i32[0]]; temp1.m128i_u32[1] = table[temp1.m128i_i32[1]]; temp1.m128i_u32[2] = table[temp1.m128i_i32[2]]; temp1.m128i_u32[3] = table[temp1.m128i_i32[3]]; \ + temp2.m128i_u32[0] = table[temp2.m128i_i32[0]]; temp2.m128i_u32[1] = table[temp2.m128i_i32[1]]; temp2.m128i_u32[2] = table[temp2.m128i_i32[2]]; temp2.m128i_u32[3] = table[temp2.m128i_i32[3]]; \ + temp3.m128i_u32[0] = table[temp3.m128i_i32[0]]; temp3.m128i_u32[1] = table[temp3.m128i_i32[1]]; temp3.m128i_u32[2] = table[temp3.m128i_i32[2]]; temp3.m128i_u32[3] = table[temp3.m128i_i32[3]]; \ + v0 = temp0.m128i_i128; \ + v1 = temp1.m128i_i128; \ + v2 = temp2.m128i_i128; \ + v3 = temp3.m128i_i128; \ +} + +static void STBIR__CODER_NAME( stbir__encode_uint8_srgb )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char*) outputp; + unsigned char * end_output = ( (unsigned char*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + stbir_uint32 const * to_srgb = fp32_to_srgb8_tab4 - (127-13)*8; + + if ( width_times_channels >= 16 ) + { + float const * end_encode_m16 = encode + width_times_channels - 16; + end_output -= 16; + for(;;) + { + stbir__simdf f0, f1, f2, f3; + stbir__simdi i0, i1, i2, i3; + STBIR_SIMD_NO_UNROLL(encode); + + stbir__simdf_load4_transposed( f0, f1, f2, f3, encode ); + + stbir__min_max_shift20( i0, f0 ); + stbir__min_max_shift20( i1, f1 ); + stbir__min_max_shift20( i2, f2 ); + stbir__min_max_shift20( i3, f3 ); + + stbir__simdi_table_lookup4( i0, i1, i2, i3, to_srgb ); + + stbir__linear_to_srgb_finish( i0, f0 ); + stbir__linear_to_srgb_finish( i1, f1 ); + stbir__linear_to_srgb_finish( i2, f2 ); + stbir__linear_to_srgb_finish( i3, f3 ); + + stbir__interleave_pack_and_store_16_u8( output, STBIR_strs_join1(i, ,stbir__encode_order0), STBIR_strs_join1(i, ,stbir__encode_order1), STBIR_strs_join1(i, ,stbir__encode_order2), STBIR_strs_join1(i, ,stbir__encode_order3) ); + + encode += 16; + output += 16; + if ( output <= end_output ) + continue; + if ( output == ( end_output + 16 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m16; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while ( output <= end_output ) + { + STBIR_SIMD_NO_UNROLL(encode); + + output[0-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order0] ); + output[1-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order1] ); + output[2-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order2] ); + output[3-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order3] ); + + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + STBIR_NO_UNROLL(encode); + output[0] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order0] ); + #if stbir__coder_min_num >= 2 + output[1] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order1] ); + #endif + #if stbir__coder_min_num >= 3 + output[2] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order2] ); + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif +} + +#if ( stbir__coder_min_num == 4 ) || ( ( stbir__coder_min_num == 1 ) && ( !defined(stbir__decode_swizzle) ) ) + +static void STBIR__CODER_NAME(stbir__decode_uint8_srgb4_linearalpha)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float const * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const *)inputp; + do { + decode[0] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0] ]; + decode[1] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order1] ]; + decode[2] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order2] ]; + decode[3] = ( (float) input[stbir__decode_order3] ) * stbir__max_uint8_as_float_inverted; + input += 4; + decode += 4; + } while( decode < decode_end ); +} + + +static void STBIR__CODER_NAME( stbir__encode_uint8_srgb4_linearalpha )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char*) outputp; + unsigned char * end_output = ( (unsigned char*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + stbir_uint32 const * to_srgb = fp32_to_srgb8_tab4 - (127-13)*8; + + if ( width_times_channels >= 16 ) + { + float const * end_encode_m16 = encode + width_times_channels - 16; + end_output -= 16; + for(;;) + { + stbir__simdf f0, f1, f2, f3; + stbir__simdi i0, i1, i2, i3; + + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdf_load4_transposed( f0, f1, f2, f3, encode ); + + stbir__min_max_shift20( i0, f0 ); + stbir__min_max_shift20( i1, f1 ); + stbir__min_max_shift20( i2, f2 ); + stbir__scale_and_convert( i3, f3 ); + + stbir__simdi_table_lookup3( i0, i1, i2, to_srgb ); + + stbir__linear_to_srgb_finish( i0, f0 ); + stbir__linear_to_srgb_finish( i1, f1 ); + stbir__linear_to_srgb_finish( i2, f2 ); + + stbir__interleave_pack_and_store_16_u8( output, STBIR_strs_join1(i, ,stbir__encode_order0), STBIR_strs_join1(i, ,stbir__encode_order1), STBIR_strs_join1(i, ,stbir__encode_order2), STBIR_strs_join1(i, ,stbir__encode_order3) ); + + output += 16; + encode += 16; + + if ( output <= end_output ) + continue; + if ( output == ( end_output + 16 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m16; + } + return; + } + #endif + + do { + float f; + STBIR_SIMD_NO_UNROLL(encode); + + output[stbir__decode_order0] = stbir__linear_to_srgb_uchar( encode[0] ); + output[stbir__decode_order1] = stbir__linear_to_srgb_uchar( encode[1] ); + output[stbir__decode_order2] = stbir__linear_to_srgb_uchar( encode[2] ); + + f = encode[3] * stbir__max_uint8_as_float + 0.5f; + STBIR_CLAMP(f, 0, 255); + output[stbir__decode_order3] = (unsigned char) f; + + output += 4; + encode += 4; + } while( output < end_output ); +} + +#endif + +#if ( stbir__coder_min_num == 2 ) || ( ( stbir__coder_min_num == 1 ) && ( !defined(stbir__decode_swizzle) ) ) + +static void STBIR__CODER_NAME(stbir__decode_uint8_srgb2_linearalpha)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float const * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const *)inputp; + decode += 4; + while( decode <= decode_end ) + { + decode[0-4] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0] ]; + decode[1-4] = ( (float) input[stbir__decode_order1] ) * stbir__max_uint8_as_float_inverted; + decode[2-4] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0+2] ]; + decode[3-4] = ( (float) input[stbir__decode_order1+2] ) * stbir__max_uint8_as_float_inverted; + input += 4; + decode += 4; + } + decode -= 4; + if( decode < decode_end ) + { + decode[0] = stbir__srgb_uchar_to_linear_float[ stbir__decode_order0 ]; + decode[1] = ( (float) input[stbir__decode_order1] ) * stbir__max_uint8_as_float_inverted; + } +} + +static void STBIR__CODER_NAME( stbir__encode_uint8_srgb2_linearalpha )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char*) outputp; + unsigned char * end_output = ( (unsigned char*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + stbir_uint32 const * to_srgb = fp32_to_srgb8_tab4 - (127-13)*8; + + if ( width_times_channels >= 16 ) + { + float const * end_encode_m16 = encode + width_times_channels - 16; + end_output -= 16; + for(;;) + { + stbir__simdf f0, f1, f2, f3; + stbir__simdi i0, i1, i2, i3; + + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdf_load4_transposed( f0, f1, f2, f3, encode ); + + stbir__min_max_shift20( i0, f0 ); + stbir__scale_and_convert( i1, f1 ); + stbir__min_max_shift20( i2, f2 ); + stbir__scale_and_convert( i3, f3 ); + + stbir__simdi_table_lookup2( i0, i2, to_srgb ); + + stbir__linear_to_srgb_finish( i0, f0 ); + stbir__linear_to_srgb_finish( i2, f2 ); + + stbir__interleave_pack_and_store_16_u8( output, STBIR_strs_join1(i, ,stbir__encode_order0), STBIR_strs_join1(i, ,stbir__encode_order1), STBIR_strs_join1(i, ,stbir__encode_order2), STBIR_strs_join1(i, ,stbir__encode_order3) ); + + output += 16; + encode += 16; + if ( output <= end_output ) + continue; + if ( output == ( end_output + 16 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m16; + } + return; + } + #endif + + do { + float f; + STBIR_SIMD_NO_UNROLL(encode); + + output[stbir__decode_order0] = stbir__linear_to_srgb_uchar( encode[0] ); + + f = encode[1] * stbir__max_uint8_as_float + 0.5f; + STBIR_CLAMP(f, 0, 255); + output[stbir__decode_order1] = (unsigned char) f; + + output += 2; + encode += 2; + } while( output < end_output ); +} + +#endif + +static void STBIR__CODER_NAME(stbir__decode_uint16_linear_scaled)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + unsigned short const * input = (unsigned short const *)inputp; + + #ifdef STBIR_SIMD + unsigned short const * end_input_m8 = input + width_times_channels - 8; + if ( width_times_channels >= 8 ) + { + decode_end -= 8; + for(;;) + { + #ifdef STBIR_SIMD8 + stbir__simdi i; stbir__simdi8 o; + stbir__simdf8 of; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi8_expand_u16_to_u32( o, i ); + stbir__simdi8_convert_i32_to_float( of, o ); + stbir__simdf8_mult( of, of, STBIR_max_uint16_as_float_inverted8); + stbir__decode_simdf8_flip( of ); + stbir__simdf8_store( decode + 0, of ); + #else + stbir__simdi i, o0, o1; + stbir__simdf of0, of1; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi_expand_u16_to_u32( o0,o1,i ); + stbir__simdi_convert_i32_to_float( of0, o0 ); + stbir__simdi_convert_i32_to_float( of1, o1 ); + stbir__simdf_mult( of0, of0, STBIR__CONSTF(STBIR_max_uint16_as_float_inverted) ); + stbir__simdf_mult( of1, of1, STBIR__CONSTF(STBIR_max_uint16_as_float_inverted)); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__simdf_store( decode + 0, of0 ); + stbir__simdf_store( decode + 4, of1 ); + #endif + decode += 8; + input += 8; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 8 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m8; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = ((float)(input[stbir__decode_order0])) * stbir__max_uint16_as_float_inverted; + decode[1-4] = ((float)(input[stbir__decode_order1])) * stbir__max_uint16_as_float_inverted; + decode[2-4] = ((float)(input[stbir__decode_order2])) * stbir__max_uint16_as_float_inverted; + decode[3-4] = ((float)(input[stbir__decode_order3])) * stbir__max_uint16_as_float_inverted; + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = ((float)(input[stbir__decode_order0])) * stbir__max_uint16_as_float_inverted; + #if stbir__coder_min_num >= 2 + decode[1] = ((float)(input[stbir__decode_order1])) * stbir__max_uint16_as_float_inverted; + #endif + #if stbir__coder_min_num >= 3 + decode[2] = ((float)(input[stbir__decode_order2])) * stbir__max_uint16_as_float_inverted; + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + + +static void STBIR__CODER_NAME(stbir__encode_uint16_linear_scaled)( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned short STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned short*) outputp; + unsigned short * end_output = ( (unsigned short*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + { + if ( width_times_channels >= stbir__simdfX_float_count*2 ) + { + float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2; + end_output -= stbir__simdfX_float_count*2; + for(;;) + { + stbir__simdfX e0, e1; + stbir__simdiX i; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_madd_mem( e0, STBIR_simd_point5X, STBIR_max_uint16_as_floatX, encode ); + stbir__simdfX_madd_mem( e1, STBIR_simd_point5X, STBIR_max_uint16_as_floatX, encode+stbir__simdfX_float_count ); + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + stbir__simdfX_pack_to_words( i, e0, e1 ); + stbir__simdiX_store( output, i ); + encode += stbir__simdfX_float_count*2; + output += stbir__simdfX_float_count*2; + if ( output <= end_output ) + continue; + if ( output == ( end_output + stbir__simdfX_float_count*2 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e; + stbir__simdi i; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e, encode ); + stbir__simdf_madd( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), e ); + stbir__encode_simdf4_unflip( e ); + stbir__simdf_pack_to_8words( i, e, e ); // only use first 4 + stbir__simdi_store2( output-4, i ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + stbir__simdf e; + STBIR_NO_UNROLL(encode); + stbir__simdf_madd1_mem( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), encode+stbir__encode_order0 ); output[0] = stbir__simdf_convert_float_to_short( e ); + #if stbir__coder_min_num >= 2 + stbir__simdf_madd1_mem( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), encode+stbir__encode_order1 ); output[1] = stbir__simdf_convert_float_to_short( e ); + #endif + #if stbir__coder_min_num >= 3 + stbir__simdf_madd1_mem( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), encode+stbir__encode_order2 ); output[2] = stbir__simdf_convert_float_to_short( e ); + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float f; + STBIR_SIMD_NO_UNROLL(encode); + f = encode[stbir__encode_order0] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0-4] = (unsigned short)f; + f = encode[stbir__encode_order1] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1-4] = (unsigned short)f; + f = encode[stbir__encode_order2] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2-4] = (unsigned short)f; + f = encode[stbir__encode_order3] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[3-4] = (unsigned short)f; + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float f; + STBIR_NO_UNROLL(encode); + f = encode[stbir__encode_order0] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0] = (unsigned short)f; + #if stbir__coder_min_num >= 2 + f = encode[stbir__encode_order1] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1] = (unsigned short)f; + #endif + #if stbir__coder_min_num >= 3 + f = encode[stbir__encode_order2] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2] = (unsigned short)f; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_uint16_linear)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + unsigned short const * input = (unsigned short const *)inputp; + + #ifdef STBIR_SIMD + unsigned short const * end_input_m8 = input + width_times_channels - 8; + if ( width_times_channels >= 8 ) + { + decode_end -= 8; + for(;;) + { + #ifdef STBIR_SIMD8 + stbir__simdi i; stbir__simdi8 o; + stbir__simdf8 of; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi8_expand_u16_to_u32( o, i ); + stbir__simdi8_convert_i32_to_float( of, o ); + stbir__decode_simdf8_flip( of ); + stbir__simdf8_store( decode + 0, of ); + #else + stbir__simdi i, o0, o1; + stbir__simdf of0, of1; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi_expand_u16_to_u32( o0, o1, i ); + stbir__simdi_convert_i32_to_float( of0, o0 ); + stbir__simdi_convert_i32_to_float( of1, o1 ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__simdf_store( decode + 0, of0 ); + stbir__simdf_store( decode + 4, of1 ); + #endif + decode += 8; + input += 8; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 8 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m8; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = ((float)(input[stbir__decode_order0])); + decode[1-4] = ((float)(input[stbir__decode_order1])); + decode[2-4] = ((float)(input[stbir__decode_order2])); + decode[3-4] = ((float)(input[stbir__decode_order3])); + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = ((float)(input[stbir__decode_order0])); + #if stbir__coder_min_num >= 2 + decode[1] = ((float)(input[stbir__decode_order1])); + #endif + #if stbir__coder_min_num >= 3 + decode[2] = ((float)(input[stbir__decode_order2])); + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME(stbir__encode_uint16_linear)( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned short STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned short*) outputp; + unsigned short * end_output = ( (unsigned short*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + { + if ( width_times_channels >= stbir__simdfX_float_count*2 ) + { + float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2; + end_output -= stbir__simdfX_float_count*2; + for(;;) + { + stbir__simdfX e0, e1; + stbir__simdiX i; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_add_mem( e0, STBIR_simd_point5X, encode ); + stbir__simdfX_add_mem( e1, STBIR_simd_point5X, encode+stbir__simdfX_float_count ); + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + stbir__simdfX_pack_to_words( i, e0, e1 ); + stbir__simdiX_store( output, i ); + encode += stbir__simdfX_float_count*2; + output += stbir__simdfX_float_count*2; + if ( output <= end_output ) + continue; + if ( output == ( end_output + stbir__simdfX_float_count*2 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e; + stbir__simdi i; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e, encode ); + stbir__simdf_add( e, STBIR__CONSTF(STBIR_simd_point5), e ); + stbir__encode_simdf4_unflip( e ); + stbir__simdf_pack_to_8words( i, e, e ); // only use first 4 + stbir__simdi_store2( output-4, i ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float f; + STBIR_SIMD_NO_UNROLL(encode); + f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0-4] = (unsigned short)f; + f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1-4] = (unsigned short)f; + f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2-4] = (unsigned short)f; + f = encode[stbir__encode_order3] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[3-4] = (unsigned short)f; + output += 4; + encode += 4; + } + output -= 4; + #endif + + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float f; + STBIR_NO_UNROLL(encode); + f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0] = (unsigned short)f; + #if stbir__coder_min_num >= 2 + f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1] = (unsigned short)f; + #endif + #if stbir__coder_min_num >= 3 + f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2] = (unsigned short)f; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_half_float_linear)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + stbir__FP16 const * input = (stbir__FP16 const *)inputp; + + #ifdef STBIR_SIMD + if ( width_times_channels >= 8 ) + { + stbir__FP16 const * end_input_m8 = input + width_times_channels - 8; + decode_end -= 8; + for(;;) + { + STBIR_NO_UNROLL(decode); + + stbir__half_to_float_SIMD( decode, input ); + #ifdef stbir__decode_swizzle + #ifdef STBIR_SIMD8 + { + stbir__simdf8 of; + stbir__simdf8_load( of, decode ); + stbir__decode_simdf8_flip( of ); + stbir__simdf8_store( decode, of ); + } + #else + { + stbir__simdf of0,of1; + stbir__simdf_load( of0, decode ); + stbir__simdf_load( of1, decode+4 ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__simdf_store( decode, of0 ); + stbir__simdf_store( decode+4, of1 ); + } + #endif + #endif + decode += 8; + input += 8; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 8 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m8; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = stbir__half_to_float(input[stbir__decode_order0]); + decode[1-4] = stbir__half_to_float(input[stbir__decode_order1]); + decode[2-4] = stbir__half_to_float(input[stbir__decode_order2]); + decode[3-4] = stbir__half_to_float(input[stbir__decode_order3]); + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = stbir__half_to_float(input[stbir__decode_order0]); + #if stbir__coder_min_num >= 2 + decode[1] = stbir__half_to_float(input[stbir__decode_order1]); + #endif + #if stbir__coder_min_num >= 3 + decode[2] = stbir__half_to_float(input[stbir__decode_order2]); + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME( stbir__encode_half_float_linear )( void * outputp, int width_times_channels, float const * encode ) +{ + stbir__FP16 STBIR_SIMD_STREAMOUT_PTR( * ) output = (stbir__FP16*) outputp; + stbir__FP16 * end_output = ( (stbir__FP16*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + if ( width_times_channels >= 8 ) + { + float const * end_encode_m8 = encode + width_times_channels - 8; + end_output -= 8; + for(;;) + { + STBIR_SIMD_NO_UNROLL(encode); + #ifdef stbir__decode_swizzle + #ifdef STBIR_SIMD8 + { + stbir__simdf8 of; + stbir__simdf8_load( of, encode ); + stbir__encode_simdf8_unflip( of ); + stbir__float_to_half_SIMD( output, (float*)&of ); + } + #else + { + stbir__simdf of[2]; + stbir__simdf_load( of[0], encode ); + stbir__simdf_load( of[1], encode+4 ); + stbir__encode_simdf4_unflip( of[0] ); + stbir__encode_simdf4_unflip( of[1] ); + stbir__float_to_half_SIMD( output, (float*)of ); + } + #endif + #else + stbir__float_to_half_SIMD( output, encode ); + #endif + encode += 8; + output += 8; + if ( output <= end_output ) + continue; + if ( output == ( end_output + 8 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + STBIR_SIMD_NO_UNROLL(output); + output[0-4] = stbir__float_to_half(encode[stbir__encode_order0]); + output[1-4] = stbir__float_to_half(encode[stbir__encode_order1]); + output[2-4] = stbir__float_to_half(encode[stbir__encode_order2]); + output[3-4] = stbir__float_to_half(encode[stbir__encode_order3]); + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + STBIR_NO_UNROLL(output); + output[0] = stbir__float_to_half(encode[stbir__encode_order0]); + #if stbir__coder_min_num >= 2 + output[1] = stbir__float_to_half(encode[stbir__encode_order1]); + #endif + #if stbir__coder_min_num >= 3 + output[2] = stbir__float_to_half(encode[stbir__encode_order2]); + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_float_linear)( float * decodep, int width_times_channels, void const * inputp ) +{ + #ifdef stbir__decode_swizzle + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + float const * input = (float const *)inputp; + + #ifdef STBIR_SIMD + if ( width_times_channels >= 16 ) + { + float const * end_input_m16 = input + width_times_channels - 16; + decode_end -= 16; + for(;;) + { + STBIR_NO_UNROLL(decode); + #ifdef stbir__decode_swizzle + #ifdef STBIR_SIMD8 + { + stbir__simdf8 of0,of1; + stbir__simdf8_load( of0, input ); + stbir__simdf8_load( of1, input+8 ); + stbir__decode_simdf8_flip( of0 ); + stbir__decode_simdf8_flip( of1 ); + stbir__simdf8_store( decode, of0 ); + stbir__simdf8_store( decode+8, of1 ); + } + #else + { + stbir__simdf of0,of1,of2,of3; + stbir__simdf_load( of0, input ); + stbir__simdf_load( of1, input+4 ); + stbir__simdf_load( of2, input+8 ); + stbir__simdf_load( of3, input+12 ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__decode_simdf4_flip( of2 ); + stbir__decode_simdf4_flip( of3 ); + stbir__simdf_store( decode, of0 ); + stbir__simdf_store( decode+4, of1 ); + stbir__simdf_store( decode+8, of2 ); + stbir__simdf_store( decode+12, of3 ); + } + #endif + #endif + decode += 16; + input += 16; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 16 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m16; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = input[stbir__decode_order0]; + decode[1-4] = input[stbir__decode_order1]; + decode[2-4] = input[stbir__decode_order2]; + decode[3-4] = input[stbir__decode_order3]; + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = input[stbir__decode_order0]; + #if stbir__coder_min_num >= 2 + decode[1] = input[stbir__decode_order1]; + #endif + #if stbir__coder_min_num >= 3 + decode[2] = input[stbir__decode_order2]; + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif + + #else + + if ( (void*)decodep != inputp ) + STBIR_MEMCPY( decodep, inputp, width_times_channels * sizeof( float ) ); + + #endif +} + +static void STBIR__CODER_NAME( stbir__encode_float_linear )( void * outputp, int width_times_channels, float const * encode ) +{ + #if !defined( STBIR_FLOAT_HIGH_CLAMP ) && !defined(STBIR_FLOAT_LO_CLAMP) && !defined(stbir__decode_swizzle) + + if ( (void*)outputp != (void*) encode ) + STBIR_MEMCPY( outputp, encode, width_times_channels * sizeof( float ) ); + + #else + + float STBIR_SIMD_STREAMOUT_PTR( * ) output = (float*) outputp; + float * end_output = ( (float*) output ) + width_times_channels; + + #ifdef STBIR_FLOAT_HIGH_CLAMP + #define stbir_scalar_hi_clamp( v ) if ( v > STBIR_FLOAT_HIGH_CLAMP ) v = STBIR_FLOAT_HIGH_CLAMP; + #else + #define stbir_scalar_hi_clamp( v ) + #endif + #ifdef STBIR_FLOAT_LOW_CLAMP + #define stbir_scalar_lo_clamp( v ) if ( v < STBIR_FLOAT_LOW_CLAMP ) v = STBIR_FLOAT_LOW_CLAMP; + #else + #define stbir_scalar_lo_clamp( v ) + #endif + + #ifdef STBIR_SIMD + + #ifdef STBIR_FLOAT_HIGH_CLAMP + const stbir__simdfX high_clamp = stbir__simdf_frepX(STBIR_FLOAT_HIGH_CLAMP); + #endif + #ifdef STBIR_FLOAT_LOW_CLAMP + const stbir__simdfX low_clamp = stbir__simdf_frepX(STBIR_FLOAT_LOW_CLAMP); + #endif + + if ( width_times_channels >= ( stbir__simdfX_float_count * 2 ) ) + { + float const * end_encode_m8 = encode + width_times_channels - ( stbir__simdfX_float_count * 2 ); + end_output -= ( stbir__simdfX_float_count * 2 ); + for(;;) + { + stbir__simdfX e0, e1; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_load( e0, encode ); + stbir__simdfX_load( e1, encode+stbir__simdfX_float_count ); +#ifdef STBIR_FLOAT_HIGH_CLAMP + stbir__simdfX_min( e0, e0, high_clamp ); + stbir__simdfX_min( e1, e1, high_clamp ); +#endif +#ifdef STBIR_FLOAT_LOW_CLAMP + stbir__simdfX_max( e0, e0, low_clamp ); + stbir__simdfX_max( e1, e1, low_clamp ); +#endif + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + stbir__simdfX_store( output, e0 ); + stbir__simdfX_store( output+stbir__simdfX_float_count, e1 ); + encode += stbir__simdfX_float_count * 2; + output += stbir__simdfX_float_count * 2; + if ( output < end_output ) + continue; + if ( output == ( end_output + ( stbir__simdfX_float_count * 2 ) ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e0; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e0, encode ); +#ifdef STBIR_FLOAT_HIGH_CLAMP + stbir__simdf_min( e0, e0, high_clamp ); +#endif +#ifdef STBIR_FLOAT_LOW_CLAMP + stbir__simdf_max( e0, e0, low_clamp ); +#endif + stbir__encode_simdf4_unflip( e0 ); + stbir__simdf_store( output-4, e0 ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float e; + STBIR_SIMD_NO_UNROLL(encode); + e = encode[ stbir__encode_order0 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[0-4] = e; + e = encode[ stbir__encode_order1 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[1-4] = e; + e = encode[ stbir__encode_order2 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[2-4] = e; + e = encode[ stbir__encode_order3 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[3-4] = e; + output += 4; + encode += 4; + } + output -= 4; + + #endif + + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float e; + STBIR_NO_UNROLL(encode); + e = encode[ stbir__encode_order0 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[0] = e; + #if stbir__coder_min_num >= 2 + e = encode[ stbir__encode_order1 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[1] = e; + #endif + #if stbir__coder_min_num >= 3 + e = encode[ stbir__encode_order2 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[2] = e; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + + #endif +} + +#undef stbir__decode_suffix +#undef stbir__decode_simdf8_flip +#undef stbir__decode_simdf4_flip +#undef stbir__decode_order0 +#undef stbir__decode_order1 +#undef stbir__decode_order2 +#undef stbir__decode_order3 +#undef stbir__encode_order0 +#undef stbir__encode_order1 +#undef stbir__encode_order2 +#undef stbir__encode_order3 +#undef stbir__encode_simdf8_unflip +#undef stbir__encode_simdf4_unflip +#undef stbir__encode_simdfX_unflip +#undef STBIR__CODER_NAME +#undef stbir__coder_min_num +#undef stbir__decode_swizzle +#undef stbir_scalar_hi_clamp +#undef stbir_scalar_lo_clamp +#undef STB_IMAGE_RESIZE_DO_CODERS + +#elif defined( STB_IMAGE_RESIZE_DO_VERTICALS) + +#ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#define STBIR_chans( start, end ) STBIR_strs_join14(start,STBIR__vertical_channels,end,_cont) +#else +#define STBIR_chans( start, end ) STBIR_strs_join1(start,STBIR__vertical_channels,end) +#endif + +#if STBIR__vertical_channels >= 1 +#define stbIF0( code ) code +#else +#define stbIF0( code ) +#endif +#if STBIR__vertical_channels >= 2 +#define stbIF1( code ) code +#else +#define stbIF1( code ) +#endif +#if STBIR__vertical_channels >= 3 +#define stbIF2( code ) code +#else +#define stbIF2( code ) +#endif +#if STBIR__vertical_channels >= 4 +#define stbIF3( code ) code +#else +#define stbIF3( code ) +#endif +#if STBIR__vertical_channels >= 5 +#define stbIF4( code ) code +#else +#define stbIF4( code ) +#endif +#if STBIR__vertical_channels >= 6 +#define stbIF5( code ) code +#else +#define stbIF5( code ) +#endif +#if STBIR__vertical_channels >= 7 +#define stbIF6( code ) code +#else +#define stbIF6( code ) +#endif +#if STBIR__vertical_channels >= 8 +#define stbIF7( code ) code +#else +#define stbIF7( code ) +#endif + +static void STBIR_chans( stbir__vertical_scatter_with_,_coeffs)( float ** outputs, float const * vertical_coefficients, float const * input, float const * input_end ) +{ + stbIF0( float STBIR_SIMD_STREAMOUT_PTR( * ) output0 = outputs[0]; float c0s = vertical_coefficients[0]; ) + stbIF1( float STBIR_SIMD_STREAMOUT_PTR( * ) output1 = outputs[1]; float c1s = vertical_coefficients[1]; ) + stbIF2( float STBIR_SIMD_STREAMOUT_PTR( * ) output2 = outputs[2]; float c2s = vertical_coefficients[2]; ) + stbIF3( float STBIR_SIMD_STREAMOUT_PTR( * ) output3 = outputs[3]; float c3s = vertical_coefficients[3]; ) + stbIF4( float STBIR_SIMD_STREAMOUT_PTR( * ) output4 = outputs[4]; float c4s = vertical_coefficients[4]; ) + stbIF5( float STBIR_SIMD_STREAMOUT_PTR( * ) output5 = outputs[5]; float c5s = vertical_coefficients[5]; ) + stbIF6( float STBIR_SIMD_STREAMOUT_PTR( * ) output6 = outputs[6]; float c6s = vertical_coefficients[6]; ) + stbIF7( float STBIR_SIMD_STREAMOUT_PTR( * ) output7 = outputs[7]; float c7s = vertical_coefficients[7]; ) + + #ifdef STBIR_SIMD + { + stbIF0(stbir__simdfX c0 = stbir__simdf_frepX( c0s ); ) + stbIF1(stbir__simdfX c1 = stbir__simdf_frepX( c1s ); ) + stbIF2(stbir__simdfX c2 = stbir__simdf_frepX( c2s ); ) + stbIF3(stbir__simdfX c3 = stbir__simdf_frepX( c3s ); ) + stbIF4(stbir__simdfX c4 = stbir__simdf_frepX( c4s ); ) + stbIF5(stbir__simdfX c5 = stbir__simdf_frepX( c5s ); ) + stbIF6(stbir__simdfX c6 = stbir__simdf_frepX( c6s ); ) + stbIF7(stbir__simdfX c7 = stbir__simdf_frepX( c7s ); ) + while ( ( (char*)input_end - (char*) input ) >= (16*stbir__simdfX_float_count) ) + { + stbir__simdfX o0, o1, o2, o3, r0, r1, r2, r3; + STBIR_SIMD_NO_UNROLL(output0); + + stbir__simdfX_load( r0, input ); stbir__simdfX_load( r1, input+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input+(3*stbir__simdfX_float_count) ); + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( stbir__simdfX_load( o0, output0 ); stbir__simdfX_load( o1, output0+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output0+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output0+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c0 ); stbir__simdfX_madd( o1, o1, r1, c0 ); stbir__simdfX_madd( o2, o2, r2, c0 ); stbir__simdfX_madd( o3, o3, r3, c0 ); + stbir__simdfX_store( output0, o0 ); stbir__simdfX_store( output0+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output0+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output0+(3*stbir__simdfX_float_count), o3 ); ) + stbIF1( stbir__simdfX_load( o0, output1 ); stbir__simdfX_load( o1, output1+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output1+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output1+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c1 ); stbir__simdfX_madd( o1, o1, r1, c1 ); stbir__simdfX_madd( o2, o2, r2, c1 ); stbir__simdfX_madd( o3, o3, r3, c1 ); + stbir__simdfX_store( output1, o0 ); stbir__simdfX_store( output1+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output1+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output1+(3*stbir__simdfX_float_count), o3 ); ) + stbIF2( stbir__simdfX_load( o0, output2 ); stbir__simdfX_load( o1, output2+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output2+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output2+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c2 ); stbir__simdfX_madd( o1, o1, r1, c2 ); stbir__simdfX_madd( o2, o2, r2, c2 ); stbir__simdfX_madd( o3, o3, r3, c2 ); + stbir__simdfX_store( output2, o0 ); stbir__simdfX_store( output2+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output2+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output2+(3*stbir__simdfX_float_count), o3 ); ) + stbIF3( stbir__simdfX_load( o0, output3 ); stbir__simdfX_load( o1, output3+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output3+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output3+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c3 ); stbir__simdfX_madd( o1, o1, r1, c3 ); stbir__simdfX_madd( o2, o2, r2, c3 ); stbir__simdfX_madd( o3, o3, r3, c3 ); + stbir__simdfX_store( output3, o0 ); stbir__simdfX_store( output3+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output3+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output3+(3*stbir__simdfX_float_count), o3 ); ) + stbIF4( stbir__simdfX_load( o0, output4 ); stbir__simdfX_load( o1, output4+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output4+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output4+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c4 ); stbir__simdfX_madd( o1, o1, r1, c4 ); stbir__simdfX_madd( o2, o2, r2, c4 ); stbir__simdfX_madd( o3, o3, r3, c4 ); + stbir__simdfX_store( output4, o0 ); stbir__simdfX_store( output4+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output4+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output4+(3*stbir__simdfX_float_count), o3 ); ) + stbIF5( stbir__simdfX_load( o0, output5 ); stbir__simdfX_load( o1, output5+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output5+(2*stbir__simdfX_float_count)); stbir__simdfX_load( o3, output5+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c5 ); stbir__simdfX_madd( o1, o1, r1, c5 ); stbir__simdfX_madd( o2, o2, r2, c5 ); stbir__simdfX_madd( o3, o3, r3, c5 ); + stbir__simdfX_store( output5, o0 ); stbir__simdfX_store( output5+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output5+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output5+(3*stbir__simdfX_float_count), o3 ); ) + stbIF6( stbir__simdfX_load( o0, output6 ); stbir__simdfX_load( o1, output6+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output6+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output6+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c6 ); stbir__simdfX_madd( o1, o1, r1, c6 ); stbir__simdfX_madd( o2, o2, r2, c6 ); stbir__simdfX_madd( o3, o3, r3, c6 ); + stbir__simdfX_store( output6, o0 ); stbir__simdfX_store( output6+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output6+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output6+(3*stbir__simdfX_float_count), o3 ); ) + stbIF7( stbir__simdfX_load( o0, output7 ); stbir__simdfX_load( o1, output7+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output7+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output7+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c7 ); stbir__simdfX_madd( o1, o1, r1, c7 ); stbir__simdfX_madd( o2, o2, r2, c7 ); stbir__simdfX_madd( o3, o3, r3, c7 ); + stbir__simdfX_store( output7, o0 ); stbir__simdfX_store( output7+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output7+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output7+(3*stbir__simdfX_float_count), o3 ); ) + #else + stbIF0( stbir__simdfX_mult( o0, r0, c0 ); stbir__simdfX_mult( o1, r1, c0 ); stbir__simdfX_mult( o2, r2, c0 ); stbir__simdfX_mult( o3, r3, c0 ); + stbir__simdfX_store( output0, o0 ); stbir__simdfX_store( output0+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output0+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output0+(3*stbir__simdfX_float_count), o3 ); ) + stbIF1( stbir__simdfX_mult( o0, r0, c1 ); stbir__simdfX_mult( o1, r1, c1 ); stbir__simdfX_mult( o2, r2, c1 ); stbir__simdfX_mult( o3, r3, c1 ); + stbir__simdfX_store( output1, o0 ); stbir__simdfX_store( output1+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output1+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output1+(3*stbir__simdfX_float_count), o3 ); ) + stbIF2( stbir__simdfX_mult( o0, r0, c2 ); stbir__simdfX_mult( o1, r1, c2 ); stbir__simdfX_mult( o2, r2, c2 ); stbir__simdfX_mult( o3, r3, c2 ); + stbir__simdfX_store( output2, o0 ); stbir__simdfX_store( output2+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output2+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output2+(3*stbir__simdfX_float_count), o3 ); ) + stbIF3( stbir__simdfX_mult( o0, r0, c3 ); stbir__simdfX_mult( o1, r1, c3 ); stbir__simdfX_mult( o2, r2, c3 ); stbir__simdfX_mult( o3, r3, c3 ); + stbir__simdfX_store( output3, o0 ); stbir__simdfX_store( output3+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output3+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output3+(3*stbir__simdfX_float_count), o3 ); ) + stbIF4( stbir__simdfX_mult( o0, r0, c4 ); stbir__simdfX_mult( o1, r1, c4 ); stbir__simdfX_mult( o2, r2, c4 ); stbir__simdfX_mult( o3, r3, c4 ); + stbir__simdfX_store( output4, o0 ); stbir__simdfX_store( output4+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output4+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output4+(3*stbir__simdfX_float_count), o3 ); ) + stbIF5( stbir__simdfX_mult( o0, r0, c5 ); stbir__simdfX_mult( o1, r1, c5 ); stbir__simdfX_mult( o2, r2, c5 ); stbir__simdfX_mult( o3, r3, c5 ); + stbir__simdfX_store( output5, o0 ); stbir__simdfX_store( output5+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output5+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output5+(3*stbir__simdfX_float_count), o3 ); ) + stbIF6( stbir__simdfX_mult( o0, r0, c6 ); stbir__simdfX_mult( o1, r1, c6 ); stbir__simdfX_mult( o2, r2, c6 ); stbir__simdfX_mult( o3, r3, c6 ); + stbir__simdfX_store( output6, o0 ); stbir__simdfX_store( output6+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output6+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output6+(3*stbir__simdfX_float_count), o3 ); ) + stbIF7( stbir__simdfX_mult( o0, r0, c7 ); stbir__simdfX_mult( o1, r1, c7 ); stbir__simdfX_mult( o2, r2, c7 ); stbir__simdfX_mult( o3, r3, c7 ); + stbir__simdfX_store( output7, o0 ); stbir__simdfX_store( output7+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output7+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output7+(3*stbir__simdfX_float_count), o3 ); ) + #endif + + input += (4*stbir__simdfX_float_count); + stbIF0( output0 += (4*stbir__simdfX_float_count); ) stbIF1( output1 += (4*stbir__simdfX_float_count); ) stbIF2( output2 += (4*stbir__simdfX_float_count); ) stbIF3( output3 += (4*stbir__simdfX_float_count); ) stbIF4( output4 += (4*stbir__simdfX_float_count); ) stbIF5( output5 += (4*stbir__simdfX_float_count); ) stbIF6( output6 += (4*stbir__simdfX_float_count); ) stbIF7( output7 += (4*stbir__simdfX_float_count); ) + } + while ( ( (char*)input_end - (char*) input ) >= 16 ) + { + stbir__simdf o0, r0; + STBIR_SIMD_NO_UNROLL(output0); + + stbir__simdf_load( r0, input ); + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( stbir__simdf_load( o0, output0 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); stbir__simdf_store( output0, o0 ); ) + stbIF1( stbir__simdf_load( o0, output1 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c1 ) ); stbir__simdf_store( output1, o0 ); ) + stbIF2( stbir__simdf_load( o0, output2 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c2 ) ); stbir__simdf_store( output2, o0 ); ) + stbIF3( stbir__simdf_load( o0, output3 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c3 ) ); stbir__simdf_store( output3, o0 ); ) + stbIF4( stbir__simdf_load( o0, output4 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c4 ) ); stbir__simdf_store( output4, o0 ); ) + stbIF5( stbir__simdf_load( o0, output5 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c5 ) ); stbir__simdf_store( output5, o0 ); ) + stbIF6( stbir__simdf_load( o0, output6 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c6 ) ); stbir__simdf_store( output6, o0 ); ) + stbIF7( stbir__simdf_load( o0, output7 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c7 ) ); stbir__simdf_store( output7, o0 ); ) + #else + stbIF0( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); stbir__simdf_store( output0, o0 ); ) + stbIF1( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c1 ) ); stbir__simdf_store( output1, o0 ); ) + stbIF2( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c2 ) ); stbir__simdf_store( output2, o0 ); ) + stbIF3( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c3 ) ); stbir__simdf_store( output3, o0 ); ) + stbIF4( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c4 ) ); stbir__simdf_store( output4, o0 ); ) + stbIF5( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c5 ) ); stbir__simdf_store( output5, o0 ); ) + stbIF6( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c6 ) ); stbir__simdf_store( output6, o0 ); ) + stbIF7( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c7 ) ); stbir__simdf_store( output7, o0 ); ) + #endif + + input += 4; + stbIF0( output0 += 4; ) stbIF1( output1 += 4; ) stbIF2( output2 += 4; ) stbIF3( output3 += 4; ) stbIF4( output4 += 4; ) stbIF5( output5 += 4; ) stbIF6( output6 += 4; ) stbIF7( output7 += 4; ) + } + } + #else + while ( ( (char*)input_end - (char*) input ) >= 16 ) + { + float r0, r1, r2, r3; + STBIR_NO_UNROLL(input); + + r0 = input[0], r1 = input[1], r2 = input[2], r3 = input[3]; + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( output0[0] += ( r0 * c0s ); output0[1] += ( r1 * c0s ); output0[2] += ( r2 * c0s ); output0[3] += ( r3 * c0s ); ) + stbIF1( output1[0] += ( r0 * c1s ); output1[1] += ( r1 * c1s ); output1[2] += ( r2 * c1s ); output1[3] += ( r3 * c1s ); ) + stbIF2( output2[0] += ( r0 * c2s ); output2[1] += ( r1 * c2s ); output2[2] += ( r2 * c2s ); output2[3] += ( r3 * c2s ); ) + stbIF3( output3[0] += ( r0 * c3s ); output3[1] += ( r1 * c3s ); output3[2] += ( r2 * c3s ); output3[3] += ( r3 * c3s ); ) + stbIF4( output4[0] += ( r0 * c4s ); output4[1] += ( r1 * c4s ); output4[2] += ( r2 * c4s ); output4[3] += ( r3 * c4s ); ) + stbIF5( output5[0] += ( r0 * c5s ); output5[1] += ( r1 * c5s ); output5[2] += ( r2 * c5s ); output5[3] += ( r3 * c5s ); ) + stbIF6( output6[0] += ( r0 * c6s ); output6[1] += ( r1 * c6s ); output6[2] += ( r2 * c6s ); output6[3] += ( r3 * c6s ); ) + stbIF7( output7[0] += ( r0 * c7s ); output7[1] += ( r1 * c7s ); output7[2] += ( r2 * c7s ); output7[3] += ( r3 * c7s ); ) + #else + stbIF0( output0[0] = ( r0 * c0s ); output0[1] = ( r1 * c0s ); output0[2] = ( r2 * c0s ); output0[3] = ( r3 * c0s ); ) + stbIF1( output1[0] = ( r0 * c1s ); output1[1] = ( r1 * c1s ); output1[2] = ( r2 * c1s ); output1[3] = ( r3 * c1s ); ) + stbIF2( output2[0] = ( r0 * c2s ); output2[1] = ( r1 * c2s ); output2[2] = ( r2 * c2s ); output2[3] = ( r3 * c2s ); ) + stbIF3( output3[0] = ( r0 * c3s ); output3[1] = ( r1 * c3s ); output3[2] = ( r2 * c3s ); output3[3] = ( r3 * c3s ); ) + stbIF4( output4[0] = ( r0 * c4s ); output4[1] = ( r1 * c4s ); output4[2] = ( r2 * c4s ); output4[3] = ( r3 * c4s ); ) + stbIF5( output5[0] = ( r0 * c5s ); output5[1] = ( r1 * c5s ); output5[2] = ( r2 * c5s ); output5[3] = ( r3 * c5s ); ) + stbIF6( output6[0] = ( r0 * c6s ); output6[1] = ( r1 * c6s ); output6[2] = ( r2 * c6s ); output6[3] = ( r3 * c6s ); ) + stbIF7( output7[0] = ( r0 * c7s ); output7[1] = ( r1 * c7s ); output7[2] = ( r2 * c7s ); output7[3] = ( r3 * c7s ); ) + #endif + + input += 4; + stbIF0( output0 += 4; ) stbIF1( output1 += 4; ) stbIF2( output2 += 4; ) stbIF3( output3 += 4; ) stbIF4( output4 += 4; ) stbIF5( output5 += 4; ) stbIF6( output6 += 4; ) stbIF7( output7 += 4; ) + } + #endif + while ( input < input_end ) + { + float r = input[0]; + STBIR_NO_UNROLL(output0); + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( output0[0] += ( r * c0s ); ) + stbIF1( output1[0] += ( r * c1s ); ) + stbIF2( output2[0] += ( r * c2s ); ) + stbIF3( output3[0] += ( r * c3s ); ) + stbIF4( output4[0] += ( r * c4s ); ) + stbIF5( output5[0] += ( r * c5s ); ) + stbIF6( output6[0] += ( r * c6s ); ) + stbIF7( output7[0] += ( r * c7s ); ) + #else + stbIF0( output0[0] = ( r * c0s ); ) + stbIF1( output1[0] = ( r * c1s ); ) + stbIF2( output2[0] = ( r * c2s ); ) + stbIF3( output3[0] = ( r * c3s ); ) + stbIF4( output4[0] = ( r * c4s ); ) + stbIF5( output5[0] = ( r * c5s ); ) + stbIF6( output6[0] = ( r * c6s ); ) + stbIF7( output7[0] = ( r * c7s ); ) + #endif + + ++input; + stbIF0( ++output0; ) stbIF1( ++output1; ) stbIF2( ++output2; ) stbIF3( ++output3; ) stbIF4( ++output4; ) stbIF5( ++output5; ) stbIF6( ++output6; ) stbIF7( ++output7; ) + } +} + +static void STBIR_chans( stbir__vertical_gather_with_,_coeffs)( float * outputp, float const * vertical_coefficients, float const ** inputs, float const * input0_end ) +{ + float STBIR_SIMD_STREAMOUT_PTR( * ) output = outputp; + + stbIF0( float const * input0 = inputs[0]; float c0s = vertical_coefficients[0]; ) + stbIF1( float const * input1 = inputs[1]; float c1s = vertical_coefficients[1]; ) + stbIF2( float const * input2 = inputs[2]; float c2s = vertical_coefficients[2]; ) + stbIF3( float const * input3 = inputs[3]; float c3s = vertical_coefficients[3]; ) + stbIF4( float const * input4 = inputs[4]; float c4s = vertical_coefficients[4]; ) + stbIF5( float const * input5 = inputs[5]; float c5s = vertical_coefficients[5]; ) + stbIF6( float const * input6 = inputs[6]; float c6s = vertical_coefficients[6]; ) + stbIF7( float const * input7 = inputs[7]; float c7s = vertical_coefficients[7]; ) + +#if ( STBIR__vertical_channels == 1 ) && !defined(STB_IMAGE_RESIZE_VERTICAL_CONTINUE) + // check single channel one weight + if ( ( c0s >= (1.0f-0.000001f) ) && ( c0s <= (1.0f+0.000001f) ) ) + { + STBIR_MEMCPY( output, input0, (char*)input0_end - (char*)input0 ); + return; + } +#endif + + #ifdef STBIR_SIMD + { + stbIF0(stbir__simdfX c0 = stbir__simdf_frepX( c0s ); ) + stbIF1(stbir__simdfX c1 = stbir__simdf_frepX( c1s ); ) + stbIF2(stbir__simdfX c2 = stbir__simdf_frepX( c2s ); ) + stbIF3(stbir__simdfX c3 = stbir__simdf_frepX( c3s ); ) + stbIF4(stbir__simdfX c4 = stbir__simdf_frepX( c4s ); ) + stbIF5(stbir__simdfX c5 = stbir__simdf_frepX( c5s ); ) + stbIF6(stbir__simdfX c6 = stbir__simdf_frepX( c6s ); ) + stbIF7(stbir__simdfX c7 = stbir__simdf_frepX( c7s ); ) + + while ( ( (char*)input0_end - (char*) input0 ) >= (16*stbir__simdfX_float_count) ) + { + stbir__simdfX o0, o1, o2, o3, r0, r1, r2, r3; + STBIR_SIMD_NO_UNROLL(output); + + // prefetch four loop iterations ahead (doesn't affect much for small resizes, but helps with big ones) + stbIF0( stbir__prefetch( input0 + (16*stbir__simdfX_float_count) ); ) + stbIF1( stbir__prefetch( input1 + (16*stbir__simdfX_float_count) ); ) + stbIF2( stbir__prefetch( input2 + (16*stbir__simdfX_float_count) ); ) + stbIF3( stbir__prefetch( input3 + (16*stbir__simdfX_float_count) ); ) + stbIF4( stbir__prefetch( input4 + (16*stbir__simdfX_float_count) ); ) + stbIF5( stbir__prefetch( input5 + (16*stbir__simdfX_float_count) ); ) + stbIF6( stbir__prefetch( input6 + (16*stbir__simdfX_float_count) ); ) + stbIF7( stbir__prefetch( input7 + (16*stbir__simdfX_float_count) ); ) + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( stbir__simdfX_load( o0, output ); stbir__simdfX_load( o1, output+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output+(3*stbir__simdfX_float_count) ); + stbir__simdfX_load( r0, input0 ); stbir__simdfX_load( r1, input0+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input0+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input0+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c0 ); stbir__simdfX_madd( o1, o1, r1, c0 ); stbir__simdfX_madd( o2, o2, r2, c0 ); stbir__simdfX_madd( o3, o3, r3, c0 ); ) + #else + stbIF0( stbir__simdfX_load( r0, input0 ); stbir__simdfX_load( r1, input0+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input0+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input0+(3*stbir__simdfX_float_count) ); + stbir__simdfX_mult( o0, r0, c0 ); stbir__simdfX_mult( o1, r1, c0 ); stbir__simdfX_mult( o2, r2, c0 ); stbir__simdfX_mult( o3, r3, c0 ); ) + #endif + + stbIF1( stbir__simdfX_load( r0, input1 ); stbir__simdfX_load( r1, input1+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input1+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input1+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c1 ); stbir__simdfX_madd( o1, o1, r1, c1 ); stbir__simdfX_madd( o2, o2, r2, c1 ); stbir__simdfX_madd( o3, o3, r3, c1 ); ) + stbIF2( stbir__simdfX_load( r0, input2 ); stbir__simdfX_load( r1, input2+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input2+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input2+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c2 ); stbir__simdfX_madd( o1, o1, r1, c2 ); stbir__simdfX_madd( o2, o2, r2, c2 ); stbir__simdfX_madd( o3, o3, r3, c2 ); ) + stbIF3( stbir__simdfX_load( r0, input3 ); stbir__simdfX_load( r1, input3+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input3+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input3+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c3 ); stbir__simdfX_madd( o1, o1, r1, c3 ); stbir__simdfX_madd( o2, o2, r2, c3 ); stbir__simdfX_madd( o3, o3, r3, c3 ); ) + stbIF4( stbir__simdfX_load( r0, input4 ); stbir__simdfX_load( r1, input4+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input4+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input4+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c4 ); stbir__simdfX_madd( o1, o1, r1, c4 ); stbir__simdfX_madd( o2, o2, r2, c4 ); stbir__simdfX_madd( o3, o3, r3, c4 ); ) + stbIF5( stbir__simdfX_load( r0, input5 ); stbir__simdfX_load( r1, input5+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input5+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input5+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c5 ); stbir__simdfX_madd( o1, o1, r1, c5 ); stbir__simdfX_madd( o2, o2, r2, c5 ); stbir__simdfX_madd( o3, o3, r3, c5 ); ) + stbIF6( stbir__simdfX_load( r0, input6 ); stbir__simdfX_load( r1, input6+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input6+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input6+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c6 ); stbir__simdfX_madd( o1, o1, r1, c6 ); stbir__simdfX_madd( o2, o2, r2, c6 ); stbir__simdfX_madd( o3, o3, r3, c6 ); ) + stbIF7( stbir__simdfX_load( r0, input7 ); stbir__simdfX_load( r1, input7+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input7+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input7+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c7 ); stbir__simdfX_madd( o1, o1, r1, c7 ); stbir__simdfX_madd( o2, o2, r2, c7 ); stbir__simdfX_madd( o3, o3, r3, c7 ); ) + + stbir__simdfX_store( output, o0 ); stbir__simdfX_store( output+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output+(3*stbir__simdfX_float_count), o3 ); + output += (4*stbir__simdfX_float_count); + stbIF0( input0 += (4*stbir__simdfX_float_count); ) stbIF1( input1 += (4*stbir__simdfX_float_count); ) stbIF2( input2 += (4*stbir__simdfX_float_count); ) stbIF3( input3 += (4*stbir__simdfX_float_count); ) stbIF4( input4 += (4*stbir__simdfX_float_count); ) stbIF5( input5 += (4*stbir__simdfX_float_count); ) stbIF6( input6 += (4*stbir__simdfX_float_count); ) stbIF7( input7 += (4*stbir__simdfX_float_count); ) + } + + while ( ( (char*)input0_end - (char*) input0 ) >= 16 ) + { + stbir__simdf o0, r0; + STBIR_SIMD_NO_UNROLL(output); + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( stbir__simdf_load( o0, output ); stbir__simdf_load( r0, input0 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); ) + #else + stbIF0( stbir__simdf_load( r0, input0 ); stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); ) + #endif + stbIF1( stbir__simdf_load( r0, input1 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c1 ) ); ) + stbIF2( stbir__simdf_load( r0, input2 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c2 ) ); ) + stbIF3( stbir__simdf_load( r0, input3 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c3 ) ); ) + stbIF4( stbir__simdf_load( r0, input4 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c4 ) ); ) + stbIF5( stbir__simdf_load( r0, input5 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c5 ) ); ) + stbIF6( stbir__simdf_load( r0, input6 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c6 ) ); ) + stbIF7( stbir__simdf_load( r0, input7 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c7 ) ); ) + + stbir__simdf_store( output, o0 ); + output += 4; + stbIF0( input0 += 4; ) stbIF1( input1 += 4; ) stbIF2( input2 += 4; ) stbIF3( input3 += 4; ) stbIF4( input4 += 4; ) stbIF5( input5 += 4; ) stbIF6( input6 += 4; ) stbIF7( input7 += 4; ) + } + } + #else + while ( ( (char*)input0_end - (char*) input0 ) >= 16 ) + { + float o0, o1, o2, o3; + STBIR_NO_UNROLL(output); + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( o0 = output[0] + input0[0] * c0s; o1 = output[1] + input0[1] * c0s; o2 = output[2] + input0[2] * c0s; o3 = output[3] + input0[3] * c0s; ) + #else + stbIF0( o0 = input0[0] * c0s; o1 = input0[1] * c0s; o2 = input0[2] * c0s; o3 = input0[3] * c0s; ) + #endif + stbIF1( o0 += input1[0] * c1s; o1 += input1[1] * c1s; o2 += input1[2] * c1s; o3 += input1[3] * c1s; ) + stbIF2( o0 += input2[0] * c2s; o1 += input2[1] * c2s; o2 += input2[2] * c2s; o3 += input2[3] * c2s; ) + stbIF3( o0 += input3[0] * c3s; o1 += input3[1] * c3s; o2 += input3[2] * c3s; o3 += input3[3] * c3s; ) + stbIF4( o0 += input4[0] * c4s; o1 += input4[1] * c4s; o2 += input4[2] * c4s; o3 += input4[3] * c4s; ) + stbIF5( o0 += input5[0] * c5s; o1 += input5[1] * c5s; o2 += input5[2] * c5s; o3 += input5[3] * c5s; ) + stbIF6( o0 += input6[0] * c6s; o1 += input6[1] * c6s; o2 += input6[2] * c6s; o3 += input6[3] * c6s; ) + stbIF7( o0 += input7[0] * c7s; o1 += input7[1] * c7s; o2 += input7[2] * c7s; o3 += input7[3] * c7s; ) + output[0] = o0; output[1] = o1; output[2] = o2; output[3] = o3; + output += 4; + stbIF0( input0 += 4; ) stbIF1( input1 += 4; ) stbIF2( input2 += 4; ) stbIF3( input3 += 4; ) stbIF4( input4 += 4; ) stbIF5( input5 += 4; ) stbIF6( input6 += 4; ) stbIF7( input7 += 4; ) + } + #endif + while ( input0 < input0_end ) + { + float o0; + STBIR_NO_UNROLL(output); + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( o0 = output[0] + input0[0] * c0s; ) + #else + stbIF0( o0 = input0[0] * c0s; ) + #endif + stbIF1( o0 += input1[0] * c1s; ) + stbIF2( o0 += input2[0] * c2s; ) + stbIF3( o0 += input3[0] * c3s; ) + stbIF4( o0 += input4[0] * c4s; ) + stbIF5( o0 += input5[0] * c5s; ) + stbIF6( o0 += input6[0] * c6s; ) + stbIF7( o0 += input7[0] * c7s; ) + output[0] = o0; + ++output; + stbIF0( ++input0; ) stbIF1( ++input1; ) stbIF2( ++input2; ) stbIF3( ++input3; ) stbIF4( ++input4; ) stbIF5( ++input5; ) stbIF6( ++input6; ) stbIF7( ++input7; ) + } +} + +#undef stbIF0 +#undef stbIF1 +#undef stbIF2 +#undef stbIF3 +#undef stbIF4 +#undef stbIF5 +#undef stbIF6 +#undef stbIF7 +#undef STB_IMAGE_RESIZE_DO_VERTICALS +#undef STBIR__vertical_channels +#undef STB_IMAGE_RESIZE_DO_HORIZONTALS +#undef STBIR_strs_join24 +#undef STBIR_strs_join14 +#undef STBIR_chans +#ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#undef STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#endif + +#else // !STB_IMAGE_RESIZE_DO_VERTICALS + +#define STBIR_chans( start, end ) STBIR_strs_join1(start,STBIR__horizontal_channels,end) + +#ifndef stbir__2_coeff_only +#define stbir__2_coeff_only() \ + stbir__1_coeff_only(); \ + stbir__1_coeff_remnant(1); +#endif + +#ifndef stbir__2_coeff_remnant +#define stbir__2_coeff_remnant( ofs ) \ + stbir__1_coeff_remnant(ofs); \ + stbir__1_coeff_remnant((ofs)+1); +#endif + +#ifndef stbir__3_coeff_only +#define stbir__3_coeff_only() \ + stbir__2_coeff_only(); \ + stbir__1_coeff_remnant(2); +#endif + +#ifndef stbir__3_coeff_remnant +#define stbir__3_coeff_remnant( ofs ) \ + stbir__2_coeff_remnant(ofs); \ + stbir__1_coeff_remnant((ofs)+2); +#endif + +#ifndef stbir__3_coeff_setup +#define stbir__3_coeff_setup() +#endif + +#ifndef stbir__4_coeff_start +#define stbir__4_coeff_start() \ + stbir__2_coeff_only(); \ + stbir__2_coeff_remnant(2); +#endif + +#ifndef stbir__4_coeff_continue_from_4 +#define stbir__4_coeff_continue_from_4( ofs ) \ + stbir__2_coeff_remnant(ofs); \ + stbir__2_coeff_remnant((ofs)+2); +#endif + +#ifndef stbir__store_output_tiny +#define stbir__store_output_tiny stbir__store_output +#endif + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_1_coeff)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__1_coeff_only(); + stbir__store_output_tiny(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_2_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__2_coeff_only(); + stbir__store_output_tiny(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_3_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__3_coeff_only(); + stbir__store_output_tiny(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_4_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_5_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__1_coeff_remnant(4); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_6_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__2_coeff_remnant(4); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_7_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + stbir__3_coeff_setup(); + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + stbir__3_coeff_remnant(4); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_8_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_9_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__1_coeff_remnant(8); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_10_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__2_coeff_remnant(8); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_11_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + stbir__3_coeff_setup(); + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__3_coeff_remnant(8); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_12_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__4_coeff_continue_from_4(8); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod0 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 4 + 3 ) >> 2; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + do { + hc += 4; + decode += STBIR__horizontal_channels * 4; + stbir__4_coeff_continue_from_4( 0 ); + --n; + } while ( n > 0 ); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod1 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 5 + 3 ) >> 2; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + do { + hc += 4; + decode += STBIR__horizontal_channels * 4; + stbir__4_coeff_continue_from_4( 0 ); + --n; + } while ( n > 0 ); + stbir__1_coeff_remnant( 4 ); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod2 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 6 + 3 ) >> 2; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + do { + hc += 4; + decode += STBIR__horizontal_channels * 4; + stbir__4_coeff_continue_from_4( 0 ); + --n; + } while ( n > 0 ); + stbir__2_coeff_remnant( 4 ); + + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod3 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + stbir__3_coeff_setup(); + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 7 + 3 ) >> 2; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + do { + hc += 4; + decode += STBIR__horizontal_channels * 4; + stbir__4_coeff_continue_from_4( 0 ); + --n; + } while ( n > 0 ); + stbir__3_coeff_remnant( 4 ); + + stbir__store_output(); + } while ( output < output_end ); +} + +static stbir__horizontal_gather_channels_func * STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_funcs)[4]= +{ + STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod0), + STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod1), + STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod2), + STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod3), +}; + +static stbir__horizontal_gather_channels_func * STBIR_chans(stbir__horizontal_gather_,_channels_funcs)[12]= +{ + STBIR_chans(stbir__horizontal_gather_,_channels_with_1_coeff), + STBIR_chans(stbir__horizontal_gather_,_channels_with_2_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_3_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_4_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_5_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_6_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_7_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_8_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_9_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_10_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_11_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_12_coeffs), +}; + +#undef STBIR__horizontal_channels +#undef STB_IMAGE_RESIZE_DO_HORIZONTALS +#undef stbir__1_coeff_only +#undef stbir__1_coeff_remnant +#undef stbir__2_coeff_only +#undef stbir__2_coeff_remnant +#undef stbir__3_coeff_only +#undef stbir__3_coeff_remnant +#undef stbir__3_coeff_setup +#undef stbir__4_coeff_start +#undef stbir__4_coeff_continue_from_4 +#undef stbir__store_output +#undef stbir__store_output_tiny +#undef STBIR_chans + +#endif // HORIZONALS + +#undef STBIR_strs_join2 +#undef STBIR_strs_join1 + +#endif // STB_IMAGE_RESIZE_DO_HORIZONTALS/VERTICALS/CODERS + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/thirdparty/icylib/thirdparty/stb_image_write.h b/thirdparty/icylib/thirdparty/stb_image_write.h new file mode 100644 index 0000000..e4b32ed --- /dev/null +++ b/thirdparty/icylib/thirdparty/stb_image_write.h @@ -0,0 +1,1724 @@ +/* stb_image_write - v1.16 - public domain - http://nothings.org/stb + writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio or a callback. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation; though providing a custom + zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. + This library is designed for source code compactness and simplicity, + not optimal image file size or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can #define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function + for PNG compression (instead of the builtin one), it must have the following signature: + unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); + The returned data will be freed with STBIW_FREE() (free() by default), + so it must be heap allocated with STBIW_MALLOC() (malloc() by default), + +UNICODE: + + If compiling for Windows and you wish to use Unicode filenames, compile + with + #define STBIW_WINDOWS_UTF8 + and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert + Windows wchar_t filenames to utf8. + +USAGE: + + There are five functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + + void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically + + There are also five equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can configure it with these global variables: + int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE + int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression + int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode + + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + PNG allows you to set the deflate compression level by setting the global + variable 'stbi_write_png_compression_level' (it defaults to 8). + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + + JPEG does ignore alpha channels in input data; quality is between 1 and 100. + Higher quality looks better but results in a bigger image. + JPEG baseline (no JPEG progressive). + +CREDITS: + + + Sean Barrett - PNG/BMP/TGA + Baldur Karlsson - HDR + Jean-Sebastien Guay - TGA monochrome + Tim Kelsey - misc enhancements + Alan Hickman - TGA RLE + Emmanuel Julien - initial file IO callback implementation + Jon Olick - original jo_jpeg.cpp code + Daniel Gibson - integrate JPEG, allow external zlib + Aarni Koskela - allow choosing PNG filter + + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + github:poppolopoppo + Patrick Boettcher + github:xeekworx + Cap Petschulat + Simon Rodriguez + Ivan Tikhonov + github:ignotion + Adam Schackart + Andrew Kensler + +LICENSE + + See end of file for license information. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#include + +// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' +#ifndef STBIWDEF +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#ifdef __cplusplus +#define STBIWDEF extern "C" +#else +#define STBIWDEF extern +#endif +#endif +#endif + +#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations +STBIWDEF int stbi_write_tga_with_rle; +STBIWDEF int stbi_write_png_compression_level; +STBIWDEF int stbi_write_force_png_filter; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); + +#ifdef STBIW_WINDOWS_UTF8 +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + +STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + +#include +#include +#include +#include + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#endif + + +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include +#define STBIW_ASSERT(x) assert(x) +#endif + +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi_write_png_compression_level = 8; +static int stbi_write_tga_with_rle = 1; +static int stbi_write_force_png_filter = -1; +#else +int stbi_write_png_compression_level = 8; +int stbi_write_tga_with_rle = 1; +int stbi_write_force_png_filter = -1; +#endif + +static int stbi__flip_vertically_on_write = 0; + +STBIWDEF void stbi_flip_vertically_on_write(int flag) +{ + stbi__flip_vertically_on_write = flag; +} + +typedef struct +{ + stbi_write_func *func; + void *context; + unsigned char buffer[64]; + int buf_used; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) +#ifdef __cplusplus +#define STBIW_EXTERN extern "C" +#else +#define STBIW_EXTERN extern +#endif +STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); + +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbiw__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = stbiw__fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__write_flush(stbi__write_context *s) +{ + if (s->buf_used) { + s->func(s->context, &s->buffer, s->buf_used); + s->buf_used = 0; + } +} + +static void stbiw__putc(stbi__write_context *s, unsigned char c) +{ + s->func(s->context, &c, 1); +} + +static void stbiw__write1(stbi__write_context *s, unsigned char a) +{ + if ((size_t)s->buf_used + 1 > sizeof(s->buffer)) + stbiw__write_flush(s); + s->buffer[s->buf_used++] = a; +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + int n; + if ((size_t)s->buf_used + 3 > sizeof(s->buffer)) + stbiw__write_flush(s); + n = s->buf_used; + s->buf_used = n+3; + s->buffer[n+0] = a; + s->buffer[n+1] = b; + s->buffer[n+2] = c; +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + stbiw__write1(s, d[comp - 1]); + + switch (comp) { + case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case + case 1: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + stbiw__write1(s, d[0]); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + stbiw__write1(s, d[comp - 1]); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (stbi__flip_vertically_on_write) + vdir *= -1; + + if (vdir < 0) { + j_end = -1; j = y-1; + } else { + j_end = y; j = 0; + } + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + stbiw__write_flush(s); + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + if (comp != 4) { + // write RGB bitmap + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header + } else { + // RGBA bitmaps need a v4 header + // use BI_BITFIELDS mode with 32bpp and alpha mask + // (straight BI_RGB with alpha mask doesn't work in most readers) + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *)data,1,0, + "11 4 22 4" "4 44 22 444444 4444 4 444 444 444 444", + 'B', 'M', 14+108+x*y*4, 0, 0, 14+108, // file header + 108, x,y, 1,32, 3,0,0,0,0,0, 0xff0000,0xff00,0xff,0xff000000u, 0, 0,0,0, 0,0,0, 0,0,0, 0,0,0); // bitmap V4 header + } +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + int jend, jdir; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + if (stbi__flip_vertically_on_write) { + j = 0; + jend = y; + jdir = 1; + } else { + j = y-1; + jend = -1; + jdir = -1; + } + for (; j != jend; j += jdir) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + stbiw__write1(s, header); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + stbiw__write1(s, header); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + stbiw__write_flush(s); + } + return 1; +} + +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +#ifndef STBI_WRITE_NO_STDIO + +static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32f) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = STBIW_UCHAR(length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + +#ifdef __STDC_LIB_EXT1__ + len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#else + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#endif + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); + STBIW_FREE(scratch); + return 1; + } +} + +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +#ifndef STBIW_ZLIB_COMPRESS +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (void *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +#endif // STBIW_ZLIB_COMPRESS + +STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ +#ifdef STBIW_ZLIB_COMPRESS + // user provided a zlib compress implementation, use that + return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); +#else // use builtin + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); + if (hash_table == NULL) + return NULL; + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) { best=d; bestloc=hlist[j]; } + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + // store uncompressed instead if compression was worse + if (stbiw__sbn(out) > data_len + 2 + ((data_len+32766)/32767)*5) { + stbiw__sbn(out) = 2; // truncate to DEFLATE 32K window and FLEVEL = 1 + for (j = 0; j < data_len;) { + int blocklen = data_len - j; + if (blocklen > 32767) blocklen = 32767; + stbiw__sbpush(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression + stbiw__sbpush(out, STBIW_UCHAR(blocklen)); // LEN + stbiw__sbpush(out, STBIW_UCHAR(blocklen >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(~blocklen)); // NLEN + stbiw__sbpush(out, STBIW_UCHAR(~blocklen >> 8)); + memcpy(out+stbiw__sbn(out), data+j, blocklen); + stbiw__sbn(out) += blocklen; + j += blocklen; + } + } + + { + // compute adler32 on input + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } + s1 %= 65521; s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +#endif // STBIW_ZLIB_COMPRESS +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ +#ifdef STBIW_CRC32 + return STBIW_CRC32(buffer, len); +#else + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + unsigned int crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +#endif +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +// @OPTIMIZE: provide an option that always forces left-predict or paeth predict +static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) +{ + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (y != 0) ? mapping : firstmap; + int i; + int type = mymap[filter_type]; + unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); + int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; + + if (type==0) { + memcpy(line_buffer, z, width*n); + return; + } + + // first loop isn't optimized since it's just one pixel + for (i = 0; i < n; ++i) { + switch (type) { + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + } + switch (type) { + case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; + case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; + case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; + case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } +} + +STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int force_filter = stbi_write_force_png_filter; + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int j,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + if (force_filter >= 5) { + force_filter = -1; + } + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); + } else { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); + + // Estimate the entropy of the line using this filter; the less, the better. + est = 0; + for (i = 0; i < x*n; ++i) { + est += abs((signed char) line_buffer[i]); + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); + filter_type = best_filter; + } + } + // when we get here, filter_type contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) filter_type; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + + f = stbiw__fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + + +/* *************************************************************************** + * + * JPEG writer + * + * This is based on Jon Olick's jo_jpeg.cpp: + * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html + */ + +static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, + 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; + +static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { + int bitBuf = *bitBufP, bitCnt = *bitCntP; + bitCnt += bs[1]; + bitBuf |= bs[0] << (24 - bitCnt); + while(bitCnt >= 8) { + unsigned char c = (bitBuf >> 16) & 255; + stbiw__putc(s, c); + if(c == 255) { + stbiw__putc(s, 0); + } + bitBuf <<= 8; + bitCnt -= 8; + } + *bitBufP = bitBuf; + *bitCntP = bitCnt; +} + +static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { + float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; + float z1, z2, z3, z4, z5, z11, z13; + + float tmp0 = d0 + d7; + float tmp7 = d0 - d7; + float tmp1 = d1 + d6; + float tmp6 = d1 - d6; + float tmp2 = d2 + d5; + float tmp5 = d2 - d5; + float tmp3 = d3 + d4; + float tmp4 = d3 - d4; + + // Even part + float tmp10 = tmp0 + tmp3; // phase 2 + float tmp13 = tmp0 - tmp3; + float tmp11 = tmp1 + tmp2; + float tmp12 = tmp1 - tmp2; + + d0 = tmp10 + tmp11; // phase 3 + d4 = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * 0.707106781f; // c4 + d2 = tmp13 + z1; // phase 5 + d6 = tmp13 - z1; + + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + // The rotator is modified from fig 4-8 to avoid extra negations. + z5 = (tmp10 - tmp12) * 0.382683433f; // c6 + z2 = tmp10 * 0.541196100f + z5; // c2-c6 + z4 = tmp12 * 1.306562965f + z5; // c2+c6 + z3 = tmp11 * 0.707106781f; // c4 + + z11 = tmp7 + z3; // phase 5 + z13 = tmp7 - z3; + + *d5p = z13 + z2; // phase 6 + *d3p = z13 - z2; + *d1p = z11 + z4; + *d7p = z11 - z4; + + *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; +} + +static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { + int tmp1 = val < 0 ? -val : val; + val = val < 0 ? val-1 : val; + bits[1] = 1; + while(tmp1 >>= 1) { + ++bits[1]; + } + bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { + } + // end0pos = first element in reverse order !=0 + if(end0pos == 0) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + return DU[0]; + } + for(i = 1; i <= end0pos; ++i) { + int startpos = i; + int nrzeroes; + unsigned short bits[2]; + for (; DU[i]==0 && i<=end0pos; ++i) { + } + nrzeroes = i-startpos; + if ( nrzeroes >= 16 ) { + int lng = nrzeroes>>4; + int nrmarker; + for (nrmarker=1; nrmarker <= lng; ++nrmarker) + stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); + nrzeroes &= 15; + } + stbiw__jpg_calcBits(DU[i], bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + if(end0pos != 63) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + } + return DU[0]; +} + +static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { + // Constants that don't pollute global namespace + static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; + static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; + static const unsigned char std_ac_luminance_values[] = { + 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, + 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, + 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, + 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, + 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, + 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, + 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; + static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; + static const unsigned char std_ac_chrominance_values[] = { + 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, + 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, + 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, + 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, + 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, + 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + // Huffman tables + static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; + static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; + static const unsigned short YAC_HT[256][2] = { + {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const unsigned short UVAC_HT[256][2] = { + {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, + 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; + static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; + static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, + 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; + + int row, col, i, k, subsample; + float fdtbl_Y[64], fdtbl_UV[64]; + unsigned char YTable[64], UVTable[64]; + + if(!data || !width || !height || comp > 4 || comp < 1) { + return 0; + } + + quality = quality ? quality : 90; + subsample = quality <= 90 ? 1 : 0; + quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; + quality = quality < 50 ? 5000 / quality : 200 - quality * 2; + + for(i = 0; i < 64; ++i) { + int uvti, yti = (YQT[i]*quality+50)/100; + YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); + uvti = (UVQT[i]*quality+50)/100; + UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); + } + + for(row = 0, k = 0; row < 8; ++row) { + for(col = 0; col < 8; ++col, ++k) { + fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + } + } + + // Write Headers + { + static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; + static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; + const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), + 3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; + s->func(s->context, (void*)head0, sizeof(head0)); + s->func(s->context, (void*)YTable, sizeof(YTable)); + stbiw__putc(s, 1); + s->func(s->context, UVTable, sizeof(UVTable)); + s->func(s->context, (void*)head1, sizeof(head1)); + s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); + s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); + stbiw__putc(s, 0x10); // HTYACinfo + s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); + s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); + stbiw__putc(s, 1); // HTUDCinfo + s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); + stbiw__putc(s, 0x11); // HTUACinfo + s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); + s->func(s->context, (void*)head2, sizeof(head2)); + } + + // Encode 8x8 macroblocks + { + static const unsigned short fillBits[] = {0x7F, 7}; + int DCY=0, DCU=0, DCV=0; + int bitBuf=0, bitCnt=0; + // comp == 2 is grey+alpha (alpha is ignored) + int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + const unsigned char *dataR = (const unsigned char *)data; + const unsigned char *dataG = dataR + ofsG; + const unsigned char *dataB = dataR + ofsB; + int x, y, pos; + if(subsample) { + for(y = 0; y < height; y += 16) { + for(x = 0; x < width; x += 16) { + float Y[256], U[256], V[256]; + for(row = y, pos = 0; row < y+16; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+16; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; + U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; + V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; + } + } + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + + // subsample U,V + { + float subU[64], subV[64]; + int yy, xx; + for(yy = 0, pos = 0; yy < 8; ++yy) { + for(xx = 0; xx < 8; ++xx, ++pos) { + int j = yy*32+xx*2; + subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f; + subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f; + } + } + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + } + } else { + for(y = 0; y < height; y += 8) { + for(x = 0; x < width; x += 8) { + float Y[64], U[64], V[64]; + for(row = y, pos = 0; row < y+8; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+8; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; + U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; + V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; + } + } + + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + } + + // Do the bit alignment of the EOI marker + stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); + } + + // EOI + stbiw__putc(s, 0xFF); + stbiw__putc(s, 0xD9); + + return 1; +} + +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); +} + + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.16 (2021-07-11) + make Deflate code emit uncompressed blocks when it would otherwise expand + support writing BMPs with alpha channel + 1.15 (2020-07-13) unknown + 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels + 1.13 + 1.12 + 1.11 (2019-08-11) + + 1.10 (2019-02-07) + support utf8 filenames in Windows; fix warnings and platform ifdefs + 1.09 (2018-02-11) + fix typo in zlib quality API, improve STB_I_W_STATIC in C++ + 1.08 (2018-01-29) + add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter + 1.07 (2017-07-24) + doc fix + 1.06 (2017-07-23) + writing JPEG (using Jon Olick's code) + 1.05 ??? + 1.04 (2017-03-03) + monochrome BMP expansion + 1.03 ??? + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/thirdparty/icylib/thirdparty/stb_truetype.h b/thirdparty/icylib/thirdparty/stb_truetype.h new file mode 100644 index 0000000..5e2a2e4 --- /dev/null +++ b/thirdparty/icylib/thirdparty/stb_truetype.h @@ -0,0 +1,5077 @@ +// stb_truetype.h - v1.26 - public domain +// authored from 2009-2021 by Sean Barrett / RAD Game Tools +// +// ======================================================================= +// +// NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES +// +// This library does no range checking of the offsets found in the file, +// meaning an attacker can use it to read arbitrary memory. +// +// ======================================================================= +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// render glyphs to one-channel SDF bitmaps (signed-distance field/function) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// Dougall Johnson: OpenType / Type 2 font handling +// Daniel Ribeiro Maciel: basic GPOS-based kerning +// +// Misc other: +// Ryan Gordon +// Simon Glass +// github:IntellectualKitty +// Imanol Celaya +// Daniel Ribeiro Maciel +// +// Bug/warning reports/fixes: +// "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe +// Cass Everitt Martins Mozeiko github:aloucks +// stoiko (Haemimont Games) Cap Petschulat github:oyvindjam +// Brian Hook Omar Cornut github:vassvik +// Walter van Niftrik Ryan Griege +// David Gow Peter LaValle +// David Given Sergey Popov +// Ivan-Assen Ivanov Giumo X. Clanjor +// Anthony Pesch Higor Euripedes +// Johan Duparc Thomas Fields +// Hou Qiming Derek Vinyard +// Rob Loach Cort Stratton +// Kenney Phillis Jr. Brian Costabile +// Ken Voskuil (kaesve) +// +// VERSION HISTORY +// +// 1.26 (2021-08-28) fix broken rasterizer +// 1.25 (2021-07-11) many fixes +// 1.24 (2020-02-05) fix warning +// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) +// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined +// 1.21 (2019-02-25) fix warning +// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() +// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// variant PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// +// Full history can be found at the end of this file. +// +// LICENSE +// +// See end of file for license information. +// +// USAGE +// +// Include this file in whatever places need to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// To make the implementation private to the file that generates the implementation, +// #define STBTT_STATIC +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// Improved 3D API (more shippable): +// #include "stb_rect_pack.h" -- optional, but you really want it +// stbtt_PackBegin() +// stbtt_PackSetOversampling() -- for improved quality on small fonts +// stbtt_PackFontRanges() -- pack and renders +// stbtt_PackEnd() +// stbtt_GetPackedQuad() +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections +// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetFontVMetricsOS2() +// stbtt_GetCodepointKernAdvance() +// +// Starting with version 1.06, the rasterizer was replaced with a new, +// faster and generally-more-precise rasterizer. The new rasterizer more +// accurately measures pixel coverage for anti-aliasing, except in the case +// where multiple shapes overlap, in which case it overestimates the AA pixel +// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If +// this turns out to be a problem, you can re-enable the old rasterizer with +// #define STBTT_RASTERIZER_VERSION 1 +// which will incur about a 15% speed hit. +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// DETAILED USAGE: +// +// Scale: +// Select how high you want the font to be, in points or pixels. +// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute +// a scale factor SF that will be used by all other functions. +// +// Baseline: +// You need to select a y-coordinate that is the baseline of where +// your text will appear. Call GetFontBoundingBox to get the baseline-relative +// bounding box for all characters. SF*-y0 will be the distance in pixels +// that the worst-case character could extend above the baseline, so if +// you want the top edge of characters to appear at the top of the +// screen where y=0, then you would set the baseline to SF*-y0. +// +// Current point: +// Set the current point where the first character will appear. The +// first character could extend left of the current point; this is font +// dependent. You can either choose a current point that is the leftmost +// point and hope, or add some padding, or check the bounding box or +// left-side-bearing of the first character to be displayed and set +// the current point based on that. +// +// Displaying a character: +// Compute the bounding box of the character. It will contain signed values +// relative to . I.e. if it returns x0,y0,x1,y1, +// then the character should be displayed in the rectangle from +// to = 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=2; // leave a little padding in case the character extends left + char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype, e.g. if you don't +//// link with the C runtime library. + +#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + #ifndef STBTT_sqrt + #include + #define STBTT_sqrt(x) sqrt(x) + #define STBTT_pow(x,y) pow(x,y) + #endif + + #ifndef STBTT_fmod + #include + #define STBTT_fmod(x,y) fmod(x,y) + #endif + + #ifndef STBTT_cos + #include + #define STBTT_cos(x) cos(x) + #define STBTT_acos(x) acos(x) + #endif + + #ifndef STBTT_fabs + #include + #define STBTT_fabs(x) fabs(x) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include + #define STBTT_malloc(x,u) ((void)(u),malloc(x)) + #define STBTT_free(x,u) ((void)(u),free(x)) + #endif + + #ifndef STBTT_assert + #include + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef STBTT_STATIC +#define STBTT_DEF static +#else +#define STBTT_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// private structure +typedef struct +{ + unsigned char *data; + int cursor; + int size; +} stbtt__buf; + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +// if return is positive, the first unused row of the bitmap +// if return is negative, returns the negative of the number of characters that fit +// if return is 0, no characters fit and no rows were used +// This uses a very crappy packing. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// Call GetBakedQuad with char_index = 'character - first_char', and it +// creates the quad you need to draw and advances the current position. +// +// The coordinate system used assumes y increases downwards. +// +// Characters will extend both above and below the current position; +// see discussion of "BASELINE" above. +// +// It's inefficient; you might want to c&p it and optimize it. + +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap); +// Query the font vertical metrics without having to create a font first. + + +////////////////////////////////////////////////////////////////////////////// +// +// NEW TEXTURE BAKING API +// +// This provides options for packing multiple fonts into one atlas, not +// perfectly but better than nothing. + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; +} stbtt_packedchar; + +typedef struct stbtt_pack_context stbtt_pack_context; +typedef struct stbtt_fontinfo stbtt_fontinfo; +#ifndef STB_RECT_PACK_VERSION +typedef struct stbrp_rect stbrp_rect; +#endif + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); +// Initializes a packing context stored in the passed-in stbtt_pack_context. +// Future calls using this context will pack characters into the bitmap passed +// in here: a 1-channel bitmap that is width * height. stride_in_bytes is +// the distance from one row to the next (or 0 to mean they are packed tightly +// together). "padding" is the amount of padding to leave between each +// character (normally you want '1' for bitmaps you'll use as textures with +// bilinear filtering). +// +// Returns 0 on failure, 1 on success. + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); +// Cleans up the packing context and frees all memory. + +#define STBTT_POINT_SIZE(x) (-(x)) + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); +// Creates character bitmaps from the font_index'th font found in fontdata (use +// font_index=0 if you don't know what that is). It creates num_chars_in_range +// bitmaps for characters with unicode values starting at first_unicode_char_in_range +// and increasing. Data for how to render them is stored in chardata_for_range; +// pass these to stbtt_GetPackedQuad to get back renderable quads. +// +// font_size is the full height of the character from ascender to descender, +// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed +// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() +// and pass that result as 'font_size': +// ..., 20 , ... // font max minus min y is 20 pixels tall +// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall + +typedef struct +{ + float font_size; + int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint + int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints + int num_chars; + stbtt_packedchar *chardata_for_range; // output + unsigned char h_oversample, v_oversample; // don't set these, they're used internally +} stbtt_pack_range; + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +// Creates character bitmaps from multiple ranges of characters stored in +// ranges. This will usually create a better-packed bitmap than multiple +// calls to stbtt_PackFontRange. Note that you can call this multiple +// times within a single PackBegin/PackEnd. + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); +// Oversampling a font increases the quality by allowing higher-quality subpixel +// positioning, and is especially valuable at smaller text sizes. +// +// This function sets the amount of oversampling for all following calls to +// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given +// pack context. The default (no oversampling) is achieved by h_oversample=1 +// and v_oversample=1. The total number of pixels required is +// h_oversample*v_oversample larger than the default; for example, 2x2 +// oversampling requires 4x the storage of 1x1. For best results, render +// oversampled textures with bilinear filtering. Look at the readme in +// stb/tests/oversample for information about oversampled fonts +// +// To use with PackFontRangesGather etc., you must set it before calls +// call to PackFontRangesGatherRects. + +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip); +// If skip != 0, this tells stb_truetype to skip any codepoints for which +// there is no corresponding glyph. If skip=0, which is the default, then +// codepoints without a glyph recived the font's "missing character" glyph, +// typically an empty box by convention. + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int align_to_integer); + +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +// Calling these functions in sequence is roughly equivalent to calling +// stbtt_PackFontRanges(). If you more control over the packing of multiple +// fonts, or if you want to pack custom data into a font texture, take a look +// at the source to of stbtt_PackFontRanges() and create a custom version +// using these functions, e.g. call GatherRects multiple times, +// building up a single array of rects, then call PackRects once, +// then call RenderIntoRects repeatedly. This may result in a +// better packing than calling PackFontRanges multiple times +// (or it may not). + +// this is an opaque structure that you shouldn't mess with which holds +// all the context needed from PackBegin to PackEnd. +struct stbtt_pack_context { + void *user_allocator_context; + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + int skip_missing; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data); +// This function will determine the number of fonts in a font file. TrueType +// collection (.ttc) files may contain multiple fonts, while TrueType font +// (.ttf) files only contain one font. The number of fonts can be used for +// indexing with the previous function where the index is between zero and one +// less than the total fonts. If an error occurs, -1 is returned. + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// Each .ttf/.ttc file may have more than one font. Each font has a sequential +// index number starting from 0. Call this function to get the font offset for +// a given index; it returns -1 if the index is out of range. A regular .ttf +// file will only define one font and it always be at offset 0, so it will +// return '0' for index 0, and -1 for all other indices. + +// The following structure is defined publicly so you can declare one on +// the stack or as a global or etc, but you should treat it as opaque. +struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph + + stbtt__buf cff; // cff font data + stbtt__buf charstrings; // the charstring index + stbtt__buf gsubrs; // global charstring subroutines index + stbtt__buf subrs; // private charstring subroutines index + stbtt__buf fontdicts; // array of font dicts + stbtt__buf fdselect; // map from glyph to fontdict +}; + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// Given an offset into the file that defines a font, this function builds +// the necessary cached info for the rest of the system. You must allocate +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +// need to do anything special to free it, because the contents are pure +// value data with no additional data structures. Returns 0 on failure. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// If you're going to perform multiple operations on the same character +// and you want a speed-up, call this function with the character you're +// going to process, then use glyph-based functions instead of the +// codepoint-based functions. +// Returns 0 if the character codepoint is not defined in the font. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose "height" is 'pixels' tall. +// Height is measured as the distance from the highest ascender to the lowest +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +// and computing: +// scale = pixels / (ascent - descent) +// so if you prefer to measure height by the ascent only, use a similar calculation. + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose EM size is mapped to +// 'pixels' tall. This is probably what traditional APIs compute, but +// I'm not positive. + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// ascent is the coordinate above the baseline the font extends; descent +// is the coordinate below the baseline the font extends (i.e. it is typically negative) +// lineGap is the spacing between one row's descent and the next row's ascent... +// so you should advance the vertical position by "*ascent - *descent + *lineGap" +// these are expressed in unscaled coordinates, so you must multiply by +// the scale factor for a given size + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap); +// analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2 +// table (specific to MS/Windows TTF files). +// +// Returns 1 on success (table present), 0 on failure. + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +// the bounding box around all possible characters + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// leftSideBearing is the offset from the current horizontal position to the left edge of the character +// advanceWidth is the offset from the current horizontal position to the next horizontal position +// these are expressed in unscaled coordinates + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + +typedef struct stbtt_kerningentry +{ + int glyph1; // use stbtt_FindGlyphIndex + int glyph2; + int advance; +} stbtt_kerningentry; + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info); +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length); +// Retrieves a complete list of all of the kerning pairs provided by the font +// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write. +// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1) + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve, + STBTT_vcubic + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy,cx1,cy1; + unsigned char type,padding; + } stbtt_vertex; +#endif + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +// returns non-zero if nothing is drawn for this glyph + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// returns # of vertices and fills *vertices with the pointer to them +// these are expressed in "unscaled" coordinates +// +// The shape is a series of contours. Each one starts with +// a STBTT_moveto, then consists of a series of mixed +// STBTT_lineto and STBTT_curveto segments. A lineto +// draws a line from previous endpoint to its x,y; a curveto +// draws a quadratic bezier from previous endpoint to +// its x,y, using cx,cy as the bezier control point. + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +STBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl); +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); +// fills svg with the character's SVG data. +// returns data size or 0 if SVG not found. + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// allocates a large-enough single-channel 8bpp bitmap and renders the +// specified character/glyph at the specified scale into it, with +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +// *width & *height are filled out with the width & height of the bitmap, +// which is stored left-to-right, top-to-bottom. +// +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap +// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap +// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the +// width and height and positioning info for it first. + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint); +// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering +// is performed (see stbtt_PackSetOversampling) + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +// get the bbox of the bitmap centered around the glyph origin; so the +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +// the bitmap top left is (leftSideBearing*scale,iy0). +// (Note that the bitmap uses y-increases-down, but the shape uses +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel +// shift for the character + +// the following functions are equivalent to the above functions, but operate +// on glyph indices instead of Unicode codepoints (for efficiency) +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph); +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +// rasterize a shape with quadratic beziers into a bitmap +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into + float flatness_in_pixels, // allowable error of curve in pixels + stbtt_vertex *vertices, // array of vertices defining shape + int num_verts, // number of vertices in above array + float scale_x, float scale_y, // scale applied to input vertices + float shift_x, float shift_y, // translation applied to input vertices + int x_off, int y_off, // another translation applied to input + int invert, // if non-zero, vertically flip shape + void *userdata); // context for to STBTT_MALLOC + +////////////////////////////////////////////////////////////////////////////// +// +// Signed Distance Function (or Field) rendering + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); +// frees the SDF bitmap allocated below + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +// These functions compute a discretized SDF field for a single character, suitable for storing +// in a single-channel texture, sampling with bilinear filtering, and testing against +// larger than some threshold to produce scalable fonts. +// info -- the font +// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap +// glyph/codepoint -- the character to generate the SDF for +// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), +// which allows effects like bit outlines +// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) +// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) +// if positive, > onedge_value is inside; if negative, < onedge_value is inside +// width,height -- output height & width of the SDF bitmap (including padding) +// xoff,yoff -- output origin of the character +// return value -- a 2D array of bytes 0..255, width*height in size +// +// pixel_dist_scale & onedge_value are a scale & bias that allows you to make +// optimal use of the limited 0..255 for your application, trading off precision +// and special effects. SDF values outside the range 0..255 are clamped to 0..255. +// +// Example: +// scale = stbtt_ScaleForPixelHeight(22) +// padding = 5 +// onedge_value = 180 +// pixel_dist_scale = 180/5.0 = 36.0 +// +// This will create an SDF bitmap in which the character is about 22 pixels +// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled +// shape, sample the SDF at each pixel and fill the pixel if the SDF value +// is greater than or equal to 180/255. (You'll actually want to antialias, +// which is beyond the scope of this example.) Additionally, you can compute +// offset outlines (e.g. to stroke the character border inside & outside, +// or only outside). For example, to fill outside the character up to 3 SDF +// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above +// choice of variables maps a range from 5 pixels outside the shape to +// 2 pixels inside the shape to 0..255; this is intended primarily for apply +// outside effects only (the interior range is needed to allow proper +// antialiasing of the font at *smaller* sizes) +// +// The function computes the SDF analytically at each SDF pixel, not by e.g. +// building a higher-res bitmap and approximating it. In theory the quality +// should be as high as possible for an SDF of this size & representation, but +// unclear if this is true in practice (perhaps building a higher-res bitmap +// and computing from that can allow drop-out prevention). +// +// The algorithm has not been optimized at all, so expect it to be slow +// if computing lots of characters or very large sizes. + + + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// returns the offset (not index) of the font that matches, or -1 if none +// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +// if you use any other flag, use a font name like "Arial"; this checks +// the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// returns 1/0 whether the first string interpreted as utf8 is identical to +// the second string interpreted as big-endian utf16... useful for strings from next func + +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// returns the string (which may be big-endian double byte, e.g. for unicode) +// and puts the length in bytes in *length. +// +// some of the values for the IDs are below; for more see the truetype spec: +// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +// http://www.microsoft.com/typography/otspec/name.htm + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +#ifndef STBTT_MAX_OVERSAMPLE +#define STBTT_MAX_OVERSAMPLE 8 +#endif + +#if STBTT_MAX_OVERSAMPLE > 255 +#error "STBTT_MAX_OVERSAMPLE cannot be > 255" +#endif + +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +#ifndef STBTT_RASTERIZER_VERSION +#define STBTT_RASTERIZER_VERSION 2 +#endif + +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +////////////////////////////////////////////////////////////////////////// +// +// stbtt__buf helpers to parse data from file +// + +static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor++]; +} + +static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor]; +} + +static void stbtt__buf_seek(stbtt__buf *b, int o) +{ + STBTT_assert(!(o > b->size || o < 0)); + b->cursor = (o > b->size || o < 0) ? b->size : o; +} + +static void stbtt__buf_skip(stbtt__buf *b, int o) +{ + stbtt__buf_seek(b, b->cursor + o); +} + +static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) +{ + stbtt_uint32 v = 0; + int i; + STBTT_assert(n >= 1 && n <= 4); + for (i = 0; i < n; i++) + v = (v << 8) | stbtt__buf_get8(b); + return v; +} + +static stbtt__buf stbtt__new_buf(const void *p, size_t size) +{ + stbtt__buf r; + STBTT_assert(size < 0x40000000); + r.data = (stbtt_uint8*) p; + r.size = (int) size; + r.cursor = 0; + return r; +} + +#define stbtt__buf_get16(b) stbtt__buf_get((b), 2) +#define stbtt__buf_get32(b) stbtt__buf_get((b), 4) + +static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s) +{ + stbtt__buf r = stbtt__new_buf(NULL, 0); + if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; + r.data = b->data + o; + r.size = s; + return r; +} + +static stbtt__buf stbtt__cff_get_index(stbtt__buf *b) +{ + int count, start, offsize; + start = b->cursor; + count = stbtt__buf_get16(b); + if (count) { + offsize = stbtt__buf_get8(b); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(b, offsize * count); + stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); + } + return stbtt__buf_range(b, start, b->cursor - start); +} + +static stbtt_uint32 stbtt__cff_int(stbtt__buf *b) +{ + int b0 = stbtt__buf_get8(b); + if (b0 >= 32 && b0 <= 246) return b0 - 139; + else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; + else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; + else if (b0 == 28) return stbtt__buf_get16(b); + else if (b0 == 29) return stbtt__buf_get32(b); + STBTT_assert(0); + return 0; +} + +static void stbtt__cff_skip_operand(stbtt__buf *b) { + int v, b0 = stbtt__buf_peek8(b); + STBTT_assert(b0 >= 28); + if (b0 == 30) { + stbtt__buf_skip(b, 1); + while (b->cursor < b->size) { + v = stbtt__buf_get8(b); + if ((v & 0xF) == 0xF || (v >> 4) == 0xF) + break; + } + } else { + stbtt__cff_int(b); + } +} + +static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) +{ + stbtt__buf_seek(b, 0); + while (b->cursor < b->size) { + int start = b->cursor, end, op; + while (stbtt__buf_peek8(b) >= 28) + stbtt__cff_skip_operand(b); + end = b->cursor; + op = stbtt__buf_get8(b); + if (op == 12) op = stbtt__buf_get8(b) | 0x100; + if (op == key) return stbtt__buf_range(b, start, end-start); + } + return stbtt__buf_range(b, 0, 0); +} + +static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out) +{ + int i; + stbtt__buf operands = stbtt__dict_get(b, key); + for (i = 0; i < outcount && operands.cursor < operands.size; i++) + out[i] = stbtt__cff_int(&operands); +} + +static int stbtt__cff_index_count(stbtt__buf *b) +{ + stbtt__buf_seek(b, 0); + return stbtt__buf_get16(b); +} + +static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) +{ + int count, offsize, start, end; + stbtt__buf_seek(&b, 0); + count = stbtt__buf_get16(&b); + offsize = stbtt__buf_get8(&b); + STBTT_assert(i >= 0 && i < count); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(&b, i*offsize); + start = stbtt__buf_get(&b, offsize); + end = stbtt__buf_get(&b, offsize); + return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); +} + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } +static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*4); + } + } + return -1; +} + +static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) +{ + // if it's just a font, there's only one valid font + if (stbtt__isfont(font_collection)) + return 1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + return ttLONG(font_collection+8); + } + } + return 0; +} + +static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) +{ + stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; + stbtt__buf pdict; + stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); + if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); + pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); + stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); + if (!subrsoff) return stbtt__new_buf(NULL, 0); + stbtt__buf_seek(&cff, private_loc[1]+subrsoff); + return stbtt__cff_get_index(&cff); +} + +// since most people won't use this, find this table the first time it's needed +static int stbtt__get_svg(stbtt_fontinfo *info) +{ + stbtt_uint32 t; + if (info->svg < 0) { + t = stbtt__find_table(info->data, info->fontstart, "SVG "); + if (t) { + stbtt_uint32 offset = ttULONG(info->data + t + 2); + info->svg = t + offset; + } else { + info->svg = 0; + } + } + return info->svg; +} + +static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) +{ + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + info->cff = stbtt__new_buf(NULL, 0); + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required + + if (!cmap || !info->head || !info->hhea || !info->hmtx) + return 0; + if (info->glyf) { + // required for truetype + if (!info->loca) return 0; + } else { + // initialization for CFF / Type2 fonts (OTF) + stbtt__buf b, topdict, topdictidx; + stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; + stbtt_uint32 cff; + + cff = stbtt__find_table(data, fontstart, "CFF "); + if (!cff) return 0; + + info->fontdicts = stbtt__new_buf(NULL, 0); + info->fdselect = stbtt__new_buf(NULL, 0); + + // @TODO this should use size from table (not 512MB) + info->cff = stbtt__new_buf(data+cff, 512*1024*1024); + b = info->cff; + + // read the header + stbtt__buf_skip(&b, 2); + stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize + + // @TODO the name INDEX could list multiple fonts, + // but we just use the first one. + stbtt__cff_get_index(&b); // name INDEX + topdictidx = stbtt__cff_get_index(&b); + topdict = stbtt__cff_index_get(topdictidx, 0); + stbtt__cff_get_index(&b); // string INDEX + info->gsubrs = stbtt__cff_get_index(&b); + + stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); + stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); + stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); + stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); + info->subrs = stbtt__get_subrs(b, topdict); + + // we only support Type 2 charstrings + if (cstype != 2) return 0; + if (charstrings == 0) return 0; + + if (fdarrayoff) { + // looks like a CID font + if (!fdselectoff) return 0; + stbtt__buf_seek(&b, fdarrayoff); + info->fontdicts = stbtt__cff_get_index(&b); + info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); + } + + stbtt__buf_seek(&b, charstrings); + info->charstrings = stbtt__cff_get_index(&b); + } + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + info->svg = -1; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + // Mac/iOS has these + // all the encodingIDs are unicode, so we don't bother to check it + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + stbtt_uint16 end; + searchRange >>= 1; + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + { + stbtt_uint16 offset, start, last; + stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); + + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + last = ttUSHORT(data + endCount + 2*item); + if (unicode_codepoint < start || unicode_codepoint > last) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + STBTT_assert(!info->cff.size); + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); + +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + if (info->cff.size) { + stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); + } else { + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + } + return 1; +} + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g; + if (info->cff.size) + return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0; + g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours < 0) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +typedef struct +{ + int bounds; + int started; + float first_x, first_y; + float x, y; + stbtt_int32 min_x, max_x, min_y, max_y; + + stbtt_vertex *pvertices; + int num_vertices; +} stbtt__csctx; + +#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} + +static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) +{ + if (x > c->max_x || !c->started) c->max_x = x; + if (y > c->max_y || !c->started) c->max_y = y; + if (x < c->min_x || !c->started) c->min_x = x; + if (y < c->min_y || !c->started) c->min_y = y; + c->started = 1; +} + +static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1) +{ + if (c->bounds) { + stbtt__track_vertex(c, x, y); + if (type == STBTT_vcubic) { + stbtt__track_vertex(c, cx, cy); + stbtt__track_vertex(c, cx1, cy1); + } + } else { + stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); + c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1; + c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1; + } + c->num_vertices++; +} + +static void stbtt__csctx_close_shape(stbtt__csctx *ctx) +{ + if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) +{ + stbtt__csctx_close_shape(ctx); + ctx->first_x = ctx->x = ctx->x + dx; + ctx->first_y = ctx->y = ctx->y + dy; + stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) +{ + ctx->x += dx; + ctx->y += dy; + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) +{ + float cx1 = ctx->x + dx1; + float cy1 = ctx->y + dy1; + float cx2 = cx1 + dx2; + float cy2 = cy1 + dy2; + ctx->x = cx2 + dx3; + ctx->y = cy2 + dy3; + stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); +} + +static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) +{ + int count = stbtt__cff_index_count(&idx); + int bias = 107; + if (count >= 33900) + bias = 32768; + else if (count >= 1240) + bias = 1131; + n += bias; + if (n < 0 || n >= count) + return stbtt__new_buf(NULL, 0); + return stbtt__cff_index_get(idx, n); +} + +static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt__buf fdselect = info->fdselect; + int nranges, start, end, v, fmt, fdselector = -1, i; + + stbtt__buf_seek(&fdselect, 0); + fmt = stbtt__buf_get8(&fdselect); + if (fmt == 0) { + // untested + stbtt__buf_skip(&fdselect, glyph_index); + fdselector = stbtt__buf_get8(&fdselect); + } else if (fmt == 3) { + nranges = stbtt__buf_get16(&fdselect); + start = stbtt__buf_get16(&fdselect); + for (i = 0; i < nranges; i++) { + v = stbtt__buf_get8(&fdselect); + end = stbtt__buf_get16(&fdselect); + if (glyph_index >= start && glyph_index < end) { + fdselector = v; + break; + } + start = end; + } + } + if (fdselector == -1) stbtt__new_buf(NULL, 0); + return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); +} + +static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c) +{ + int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; + int has_subrs = 0, clear_stack; + float s[48]; + stbtt__buf subr_stack[10], subrs = info->subrs, b; + float f; + +#define STBTT__CSERR(s) (0) + + // this currently ignores the initial width value, which isn't needed if we have hmtx + b = stbtt__cff_index_get(info->charstrings, glyph_index); + while (b.cursor < b.size) { + i = 0; + clear_stack = 1; + b0 = stbtt__buf_get8(&b); + switch (b0) { + // @TODO implement hinting + case 0x13: // hintmask + case 0x14: // cntrmask + if (in_header) + maskbits += (sp / 2); // implicit "vstem" + in_header = 0; + stbtt__buf_skip(&b, (maskbits + 7) / 8); + break; + + case 0x01: // hstem + case 0x03: // vstem + case 0x12: // hstemhm + case 0x17: // vstemhm + maskbits += (sp / 2); + break; + + case 0x15: // rmoveto + in_header = 0; + if (sp < 2) return STBTT__CSERR("rmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); + break; + case 0x04: // vmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("vmoveto stack"); + stbtt__csctx_rmove_to(c, 0, s[sp-1]); + break; + case 0x16: // hmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("hmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-1], 0); + break; + + case 0x05: // rlineto + if (sp < 2) return STBTT__CSERR("rlineto stack"); + for (; i + 1 < sp; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical + // starting from a different place. + + case 0x07: // vlineto + if (sp < 1) return STBTT__CSERR("vlineto stack"); + goto vlineto; + case 0x06: // hlineto + if (sp < 1) return STBTT__CSERR("hlineto stack"); + for (;;) { + if (i >= sp) break; + stbtt__csctx_rline_to(c, s[i], 0); + i++; + vlineto: + if (i >= sp) break; + stbtt__csctx_rline_to(c, 0, s[i]); + i++; + } + break; + + case 0x1F: // hvcurveto + if (sp < 4) return STBTT__CSERR("hvcurveto stack"); + goto hvcurveto; + case 0x1E: // vhcurveto + if (sp < 4) return STBTT__CSERR("vhcurveto stack"); + for (;;) { + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); + i += 4; + hvcurveto: + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); + i += 4; + } + break; + + case 0x08: // rrcurveto + if (sp < 6) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x18: // rcurveline + if (sp < 8) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp - 2; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + case 0x19: // rlinecurve + if (sp < 8) return STBTT__CSERR("rlinecurve stack"); + for (; i + 1 < sp - 6; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x1A: // vvcurveto + case 0x1B: // hhcurveto + if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); + f = 0.0; + if (sp & 1) { f = s[i]; i++; } + for (; i + 3 < sp; i += 4) { + if (b0 == 0x1B) + stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); + else + stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); + f = 0.0; + } + break; + + case 0x0A: // callsubr + if (!has_subrs) { + if (info->fdselect.size) + subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); + has_subrs = 1; + } + // FALLTHROUGH + case 0x1D: // callgsubr + if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); + v = (int) s[--sp]; + if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); + subr_stack[subr_stack_height++] = b; + b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); + if (b.size == 0) return STBTT__CSERR("subr not found"); + b.cursor = 0; + clear_stack = 0; + break; + + case 0x0B: // return + if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); + b = subr_stack[--subr_stack_height]; + clear_stack = 0; + break; + + case 0x0E: // endchar + stbtt__csctx_close_shape(c); + return 1; + + case 0x0C: { // two-byte escape + float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; + float dx, dy; + int b1 = stbtt__buf_get8(&b); + switch (b1) { + // @TODO These "flex" implementations ignore the flex-depth and resolution, + // and always draw beziers. + case 0x22: // hflex + if (sp < 7) return STBTT__CSERR("hflex stack"); + dx1 = s[0]; + dx2 = s[1]; + dy2 = s[2]; + dx3 = s[3]; + dx4 = s[4]; + dx5 = s[5]; + dx6 = s[6]; + stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); + break; + + case 0x23: // flex + if (sp < 13) return STBTT__CSERR("flex stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = s[10]; + dy6 = s[11]; + //fd is s[12] + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + case 0x24: // hflex1 + if (sp < 9) return STBTT__CSERR("hflex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dx4 = s[5]; + dx5 = s[6]; + dy5 = s[7]; + dx6 = s[8]; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); + break; + + case 0x25: // flex1 + if (sp < 11) return STBTT__CSERR("flex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = dy6 = s[10]; + dx = dx1+dx2+dx3+dx4+dx5; + dy = dy1+dy2+dy3+dy4+dy5; + if (STBTT_fabs(dx) > STBTT_fabs(dy)) + dy6 = -dy; + else + dx6 = -dx; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + default: + return STBTT__CSERR("unimplemented"); + } + } break; + + default: + if (b0 != 255 && b0 != 28 && b0 < 32) + return STBTT__CSERR("reserved operator"); + + // push immediate + if (b0 == 255) { + f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000; + } else { + stbtt__buf_skip(&b, -1); + f = (float)(stbtt_int16)stbtt__cff_int(&b); + } + if (sp >= 48) return STBTT__CSERR("push stack overflow"); + s[sp++] = f; + clear_stack = 0; + break; + } + if (clear_stack) sp = 0; + } + return STBTT__CSERR("no endchar"); + +#undef STBTT__CSERR +} + +static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + // runs the charstring twice, once to count and once to output (to avoid realloc) + stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1); + stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0); + if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { + *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata); + output_ctx.pvertices = *pvertices; + if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { + STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices); + return output_ctx.num_vertices; + } + } + *pvertices = NULL; + return 0; +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + stbtt__csctx c = STBTT__CSCTX_INIT(1); + int r = stbtt__run_charstring(info, glyph_index, &c); + if (x0) *x0 = r ? c.min_x : 0; + if (y0) *y0 = r ? c.min_y : 0; + if (x1) *x1 = r ? c.max_x : 0; + if (y1) *y1 = r ? c.max_y : 0; + return r ? c.num_vertices : 0; +} + +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + if (!info->cff.size) + return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); + else + return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); +} + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info) +{ + stbtt_uint8 *data = info->data + info->kern; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + return ttUSHORT(data+10); +} + +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length) +{ + stbtt_uint8 *data = info->data + info->kern; + int k, length; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + length = ttUSHORT(data+10); + if (table_length < length) + length = table_length; + + for (k = 0; k < length; k++) + { + table[k].glyph1 = ttUSHORT(data+18+(k*6)); + table[k].glyph2 = ttUSHORT(data+20+(k*6)); + table[k].advance = ttSHORT(data+22+(k*6)); + } + + return length; +} + +static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) +{ + stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); + switch (coverageFormat) { + case 1: { + stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); + + // Binary search. + stbtt_int32 l=0, r=glyphCount-1, m; + int straw, needle=glyph; + while (l <= r) { + stbtt_uint8 *glyphArray = coverageTable + 4; + stbtt_uint16 glyphID; + m = (l + r) >> 1; + glyphID = ttUSHORT(glyphArray + 2 * m); + straw = glyphID; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + return m; + } + } + break; + } + + case 2: { + stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); + stbtt_uint8 *rangeArray = coverageTable + 4; + + // Binary search. + stbtt_int32 l=0, r=rangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *rangeRecord; + m = (l + r) >> 1; + rangeRecord = rangeArray + 6 * m; + strawStart = ttUSHORT(rangeRecord); + strawEnd = ttUSHORT(rangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else { + stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); + return startCoverageIndex + glyph - strawStart; + } + } + break; + } + + default: return -1; // unsupported + } + + return -1; +} + +static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) +{ + stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); + switch (classDefFormat) + { + case 1: { + stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); + stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); + stbtt_uint8 *classDef1ValueArray = classDefTable + 6; + + if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) + return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); + break; + } + + case 2: { + stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); + stbtt_uint8 *classRangeRecords = classDefTable + 4; + + // Binary search. + stbtt_int32 l=0, r=classRangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *classRangeRecord; + m = (l + r) >> 1; + classRangeRecord = classRangeRecords + 6 * m; + strawStart = ttUSHORT(classRangeRecord); + strawEnd = ttUSHORT(classRangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else + return (stbtt_int32)ttUSHORT(classRangeRecord + 4); + } + break; + } + + default: + return -1; // Unsupported definition type, return an error. + } + + // "All glyphs not assigned to a class fall into class 0". (OpenType spec) + return 0; +} + +// Define to STBTT_assert(x) if you want to break on unimplemented formats. +#define STBTT_GPOS_TODO_assert(x) + +static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint16 lookupListOffset; + stbtt_uint8 *lookupList; + stbtt_uint16 lookupCount; + stbtt_uint8 *data; + stbtt_int32 i, sti; + + if (!info->gpos) return 0; + + data = info->data + info->gpos; + + if (ttUSHORT(data+0) != 1) return 0; // Major version 1 + if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 + + lookupListOffset = ttUSHORT(data+8); + lookupList = data + lookupListOffset; + lookupCount = ttUSHORT(lookupList); + + for (i=0; i= pairSetCount) return 0; + + needle=glyph2; + r=pairValueCount-1; + l=0; + + // Binary search. + while (l <= r) { + stbtt_uint16 secondGlyph; + stbtt_uint8 *pairValue; + m = (l + r) >> 1; + pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; + secondGlyph = ttUSHORT(pairValue); + straw = secondGlyph; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + stbtt_int16 xAdvance = ttSHORT(pairValue + 2); + return xAdvance; + } + } + } else + return 0; + break; + } + + case 2: { + stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); + stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); + if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats? + stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); + stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); + int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); + int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); + + stbtt_uint16 class1Count = ttUSHORT(table + 12); + stbtt_uint16 class2Count = ttUSHORT(table + 14); + stbtt_uint8 *class1Records, *class2Records; + stbtt_int16 xAdvance; + + if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed + if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed + + class1Records = table + 16; + class2Records = class1Records + 2 * (glyph1class * class2Count); + xAdvance = ttSHORT(class2Records + 2 * glyph2class); + return xAdvance; + } else + return 0; + break; + } + + default: + return 0; // Unsupported position format + } + } + } + + return 0; +} + +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) +{ + int xAdvance = 0; + + if (info->gpos) + xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); + else if (info->kern) + xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); + + return xAdvance; +} + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap) +{ + int tab = stbtt__find_table(info->data, info->fontstart, "OS/2"); + if (!tab) + return 0; + if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68); + if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70); + if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72); + return 1; +} + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl) +{ + int i; + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info); + + int numEntries = ttUSHORT(svg_doc_list); + stbtt_uint8 *svg_docs = svg_doc_list + 2; + + for(i=0; i= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2))) + return svg_doc; + } + return 0; +} + +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg) +{ + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc; + + if (info->svg == 0) + return 0; + + svg_doc = stbtt_FindSVGDoc(info, gl); + if (svg_doc != NULL) { + *svg = (char *) data + info->svg + ttULONG(svg_doc + 4); + return ttULONG(svg_doc + 8); + } else { + return 0; + } +} + +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg) +{ + return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + // e.g. space character + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + // move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); + if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); + if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); + } +} + +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Rasterizer + +typedef struct stbtt__hheap_chunk +{ + struct stbtt__hheap_chunk *next; +} stbtt__hheap_chunk; + +typedef struct stbtt__hheap +{ + struct stbtt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +} stbtt__hheap; + +static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); + if (c == NULL) + return NULL; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk; + } +} + +static void stbtt__hheap_free(stbtt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) +{ + stbtt__hheap_chunk *c = hh->head; + while (c) { + stbtt__hheap_chunk *n = c->next; + STBTT_free(c, userdata); + c = n; + } +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + + +typedef struct stbtt__active_edge +{ + struct stbtt__active_edge *next; + #if STBTT_RASTERIZER_VERSION==1 + int x,dx; + float ey; + int direction; + #elif STBTT_RASTERIZER_VERSION==2 + float fx,fdx,fdy; + float direction; + float sy; + float ey; + #else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" + #endif +} stbtt__active_edge; + +#if STBTT_RASTERIZER_VERSION == 1 +#define STBTT_FIXSHIFT 10 +#define STBTT_FIX (1 << STBTT_FIXSHIFT) +#define STBTT_FIXMASK (STBTT_FIX-1) + +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + if (!z) return z; + + // round dx down to avoid overshooting + if (dxdy < 0) + z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); + else + z->dx = STBTT_ifloor(STBTT_FIX * dxdy); + + z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount + z->x -= off_x * STBTT_FIX; + + z->ey = e->y1; + z->next = 0; + z->direction = e->invert ? 1 : -1; + return z; +} +#elif STBTT_RASTERIZER_VERSION == 2 +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + //STBTT_assert(e->y0 <= start_point); + if (!z) return z; + z->fdx = dxdy; + z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#if STBTT_RASTERIZER_VERSION == 1 +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->direction; + } else { + int x1 = e->x; w += e->direction; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> STBTT_FIXSHIFT; + int j = x1 >> STBTT_FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); + if (z != NULL) { + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +#elif STBTT_RASTERIZER_VERSION == 2 + +// the edge passed in here does not cross the vertical line at x or the vertical line at x+1 +// (i.e. it has already been clipped to those) +static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) +{ + if (y0 == y1) return; + STBTT_assert(y0 < y1); + STBTT_assert(e->sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (x0 == x) + STBTT_assert(x1 <= x+1); + else if (x0 == x+1) + STBTT_assert(x1 >= x); + else if (x0 <= x) + STBTT_assert(x1 <= x); + else if (x0 >= x+1) + STBTT_assert(x1 >= x+1); + else + STBTT_assert(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1) + ; + else { + STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position + } +} + +static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width) +{ + STBTT_assert(top_width >= 0); + STBTT_assert(bottom_width >= 0); + return (top_width + bottom_width) / 2.0f * height; +} + +static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1) +{ + return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0); +} + +static float stbtt__sized_triangle_area(float height, float width) +{ + return height * width / 2; +} + +static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + + while (e) { + // brute force every pixel + + // compute intersection points with top & bottom + STBTT_assert(e->ey >= y_top); + + if (e->fdx == 0) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float sy0,sy1; + float dy = e->fdy; + STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); + + // compute endpoints of line segment clipped to this scanline (if the + // line segment starts on this scanline. x0 is the intersection of the + // line with y_top, but that may be off the line segment. + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + sy0 = e->sy; + } else { + x_top = x0; + sy0 = y_top; + } + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + sy1 = e->ey; + } else { + x_bottom = xb; + sy1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { + // from here on, we don't have to range check x values + + if ((int) x_top == (int) x_bottom) { + float height; + // simple case, only spans one pixel + int x = (int) x_top; + height = (sy1 - sy0) * e->direction; + STBTT_assert(x >= 0 && x < len); + scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f); + scanline_fill[x] += height; // everything right of this pixel is filled + } else { + int x,x1,x2; + float y_crossing, y_final, step, sign, area; + // covers 2+ pixels + if (x_top > x_bottom) { + // flip scanline vertically; signed area is the same + float t; + sy0 = y_bottom - (sy0 - y_top); + sy1 = y_bottom - (sy1 - y_top); + t = sy0, sy0 = sy1, sy1 = t; + t = x_bottom, x_bottom = x_top, x_top = t; + dx = -dx; + dy = -dy; + t = x0, x0 = xb, xb = t; + } + STBTT_assert(dy >= 0); + STBTT_assert(dx >= 0); + + x1 = (int) x_top; + x2 = (int) x_bottom; + // compute intersection with y axis at x1+1 + y_crossing = y_top + dy * (x1+1 - x0); + + // compute intersection with y axis at x2 + y_final = y_top + dy * (x2 - x0); + + // x1 x_top x2 x_bottom + // y_top +------|-----+------------+------------+--------|---+------------+ + // | | | | | | + // | | | | | | + // sy0 | Txxxxx|............|............|............|............| + // y_crossing | *xxxxx.......|............|............|............| + // | | xxxxx..|............|............|............| + // | | /- xx*xxxx........|............|............| + // | | dy < | xxxxxx..|............|............| + // y_final | | \- | xx*xxx.........|............| + // sy1 | | | | xxxxxB...|............| + // | | | | | | + // | | | | | | + // y_bottom +------------+------------+------------+------------+------------+ + // + // goal is to measure the area covered by '.' in each pixel + + // if x2 is right at the right edge of x1, y_crossing can blow up, github #1057 + // @TODO: maybe test against sy1 rather than y_bottom? + if (y_crossing > y_bottom) + y_crossing = y_bottom; + + sign = e->direction; + + // area of the rectangle covered from sy0..y_crossing + area = sign * (y_crossing-sy0); + + // area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing) + scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top); + + // check if final y_crossing is blown up; no test case for this + if (y_final > y_bottom) { + y_final = y_bottom; + dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom + } + + // in second pixel, area covered by line segment found in first pixel + // is always a rectangle 1 wide * the height of that line segment; this + // is exactly what the variable 'area' stores. it also gets a contribution + // from the line segment within it. the THIRD pixel will get the first + // pixel's rectangle contribution, the second pixel's rectangle contribution, + // and its own contribution. the 'own contribution' is the same in every pixel except + // the leftmost and rightmost, a trapezoid that slides down in each pixel. + // the second pixel's contribution to the third pixel will be the + // rectangle 1 wide times the height change in the second pixel, which is dy. + + step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x, + // which multiplied by 1-pixel-width is how much pixel area changes for each step in x + // so the area advances by 'step' every time + + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; // area of trapezoid is 1*step/2 + area += step; + } + STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down + STBTT_assert(sy1 > y_final-0.01f); + + // area covered in the last pixel is the rectangle from all the pixels to the left, + // plus the trapezoid filled by the line segment in this pixel all the way to the right edge + scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f); + + // the rest of the line is filled based on the total height of the line segment in this pixel + scanline_fill[x2] += sign * (sy1-sy0); + } + } else { + // if edge goes outside of box we're drawing, we require + // clipping logic. since this does not match the intended use + // of this library, we use a different, very slow brute + // force implementation + // note though that this does happen some of the time because + // x_top and x_bottom can be extrapolated at the top & bottom of + // the shape and actually lie outside the bounding box + int x; + for (x=0; x < len; ++x) { + // cases: + // + // there can be up to two intersections with the pixel. any intersection + // with left or right edges can be handled by splitting into two (or three) + // regions. intersections with top & bottom do not necessitate case-wise logic. + // + // the old way of doing this found the intersections with the left & right edges, + // then used some simple logic to produce up to three segments in sorted order + // from top-to-bottom. however, this had a problem: if an x edge was epsilon + // across the x border, then the corresponding y position might not be distinct + // from the other y segment, and it might ignored as an empty segment. to avoid + // that, we need to explicitly produce segments based on x positions. + + // rename variables to clearly-defined pairs + float y0 = y_top; + float x1 = (float) (x); + float x2 = (float) (x+1); + float x3 = xb; + float y3 = y_bottom; + + // x = e->x + e->dx * (y-y_top) + // (y-y_top) = (x - e->x) / e->dx + // y = (x - e->x) / e->dx + y_top + float y1 = (x - x0) / dx + y_top; + float y2 = (x+1 - x0) / dx + y_top; + + if (x0 < x1 && x3 > x2) { // three segments descending down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x1 && x0 > x2) { // three segments descending down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else { // one segment + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); + } + } + } + } + e = e->next; + } +} + +// directly AA rasterize edges w/o supersampling +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + STBTT__NOTUSED(vsubsample); + + if (result->w > 64) + scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); + else + scanline = scanline_data; + + scanline2 = scanline + result->w; + + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) { + // find center of pixel for this scanline + float scan_y_top = y + 0.0f; + float scan_y_bottom = y + 1.0f; + stbtt__active_edge **step = &active; + + STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); + STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); + + // update all active edges; + // remove all active edges that terminate before the top of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + step = &((*step)->next); // advance through list + } + } + + // insert all edges that start before the bottom of this scanline + while (e->y0 <= scan_y_bottom) { + if (e->y0 != e->y1) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); + if (z != NULL) { + if (j == 0 && off_y != 0) { + if (z->ey < scan_y_top) { + // this can happen due to subpixel positioning and some kind of fp rounding error i think + z->ey = scan_y_top; + } + } + STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds + // insert at front + z->next = active; + active = z; + } + } + ++e; + } + + // now process all active edges + if (active) + stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) STBTT_fabs(k)*255 + 0.5f; + m = (int) k; + if (m > 255) m = 255; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + // advance all the edges + step = &active; + while (*step) { + stbtt__active_edge *z = *step; + z->fx += z->fdx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + + ++y; + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) + +static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + stbtt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + stbtt__edge *b = &p[j-1]; + int c = STBTT__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) +{ + /* threshold for transitioning to insertion sort */ + while (n > 12) { + stbtt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = STBTT__COMPARE(&p[0],&p[m]); + c12 = STBTT__COMPARE(&p[m],&p[n-1]); + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = STBTT__COMPARE(&p[0],&p[n-1]); + /* 0>mid && midn => n; 0 0 */ + /* 0n: 0>n => 0; 0 n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!STBTT__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!STBTT__COMPARE(&p[0], &p[j])) break; + } + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + } + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + stbtt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + stbtt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +static void stbtt__sort_edges(stbtt__edge *p, int n) +{ + stbtt__sort_edges_quicksort(p, n); + stbtt__sort_edges_ins_sort(p, n); +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; +#if STBTT_RASTERIZER_VERSION == 1 + int vsubsample = result->h < 8 ? 15 : 5; +#elif STBTT_RASTERIZER_VERSION == 2 + int vsubsample = 1; +#else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + stbtt__sort_edges(e, n); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) +{ + // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough + float dx0 = x1-x0; + float dy0 = y1-y0; + float dx1 = x2-x1; + float dy1 = y2-y1; + float dx2 = x3-x2; + float dy2 = y3-y2; + float dx = x3-x0; + float dy = y3-y0; + float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); + float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy); + float flatness_squared = longlen*longlen-shortlen*shortlen; + + if (n > 16) // 65536 segments on one curve better be enough! + return; + + if (flatness_squared > objspace_flatness_squared) { + float x01 = (x0+x1)/2; + float y01 = (y0+y1)/2; + float x12 = (x1+x2)/2; + float y12 = (y1+y2)/2; + float x23 = (x2+x3)/2; + float y23 = (y2+y3)/2; + + float xa = (x01+x12)/2; + float ya = (y01+y12)/2; + float xb = (x12+x23)/2; + float yb = (y12+y23)/2; + + float mx = (xa+xb)/2; + float my = (ya+yb)/2; + + stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x3,y3); + *num_points = *num_points+1; + } +} + +// returns number of contours +static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + case STBTT_vcubic: + stbtt__tesselate_cubic(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].cx1, vertices[i].cy1, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count = 0; + int *winding_lengths = NULL; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) { + STBTT_free(vertices, info->userdata); + return NULL; + } + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + f.userdata = NULL; + if (!stbtt_InitFont(&f, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 1; + if (y+gh+1 > bottom_y) + bottom_y = y+gh+1; + } + return bottom_y; +} + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// rectangle packing replacement routines if you don't have stb_rect_pack.h +// + +#ifndef STB_RECT_PACK_VERSION + +typedef int stbrp_coord; + +//////////////////////////////////////////////////////////////////////////////////// +// // +// // +// COMPILER WARNING ?!?!? // +// // +// // +// if you get a compile warning due to these symbols being defined more than // +// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // +// // +//////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int width,height; + int x,y,bottom_y; +} stbrp_context; + +typedef struct +{ + unsigned char x; +} stbrp_node; + +struct stbrp_rect +{ + stbrp_coord x,y; + int id,w,h,was_packed; +}; + +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) +{ + con->width = pw; + con->height = ph; + con->x = 0; + con->y = 0; + con->bottom_y = 0; + STBTT__NOTUSED(nodes); + STBTT__NOTUSED(num_nodes); +} + +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) +{ + int i; + for (i=0; i < num_rects; ++i) { + if (con->x + rects[i].w > con->width) { + con->x = 0; + con->y = con->bottom_y; + } + if (con->y + rects[i].h > con->height) + break; + rects[i].x = con->x; + rects[i].y = con->y; + rects[i].was_packed = 1; + con->x += rects[i].w; + if (con->y + rects[i].h > con->bottom_y) + con->bottom_y = con->y + rects[i].h; + } + for ( ; i < num_rects; ++i) + rects[i].was_packed = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If +// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +{ + stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); + int num_nodes = pw - padding; + stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); + + if (context == NULL || nodes == NULL) { + if (context != NULL) STBTT_free(context, alloc_context); + if (nodes != NULL) STBTT_free(nodes , alloc_context); + return 0; + } + + spc->user_allocator_context = alloc_context; + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + spc->skip_missing = 0; + + stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + + if (pixels) + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + + return 1; +} + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) +{ + STBTT_free(spc->nodes , spc->user_allocator_context); + STBTT_free(spc->pack_info, spc->user_allocator_context); +} + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +{ + STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); + STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); + if (h_oversample <= STBTT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= STBTT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip) +{ + spc->skip_missing = skip; +} + +#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) + +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < h; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < w; ++i) { + STBTT_assert(pixels[i] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i] = (unsigned char) (total / kernel_width); + } + + pixels += stride_in_bytes; + } +} + +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < w; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < h; ++i) { + STBTT_assert(pixels[i*stride_in_bytes] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + + pixels += 1; + } +} + +static float stbtt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + // The prefilter is a box filter of width "oversample", + // which shifts phase by (oversample - 1)/2 pixels in + // oversampled space. We want to shift in the opposite + // direction to counter this. + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k; + int missing_glyph_added = 0; + + k=0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + ranges[i].h_oversample = (unsigned char) spc->h_oversample; + ranges[i].v_oversample = (unsigned char) spc->v_oversample; + for (j=0; j < ranges[i].num_chars; ++j) { + int x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) { + rects[k].w = rects[k].h = 0; + } else { + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + if (glyph == 0) + missing_glyph_added = 1; + } + ++k; + } + } + + return k; +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, + output, + out_w - (prefilter_x - 1), + out_h - (prefilter_y - 1), + out_stride, + scale_x, + scale_y, + shift_x, + shift_y, + glyph); + + if (prefilter_x > 1) + stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); + + if (prefilter_y > 1) + stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); + + *sub_x = stbtt__oversample_shift(prefilter_x); + *sub_y = stbtt__oversample_shift(prefilter_y); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k, missing_glyph = -1, return_value = 1; + + // save current values + int old_h_over = spc->h_oversample; + int old_v_over = spc->v_oversample; + + k = 0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + float recip_h,recip_v,sub_x,sub_y; + spc->h_oversample = ranges[i].h_oversample; + spc->v_oversample = ranges[i].v_oversample; + recip_h = 1.0f / spc->h_oversample; + recip_v = 1.0f / spc->v_oversample; + sub_x = stbtt__oversample_shift(spc->h_oversample); + sub_y = stbtt__oversample_shift(spc->v_oversample); + for (j=0; j < ranges[i].num_chars; ++j) { + stbrp_rect *r = &rects[k]; + if (r->was_packed && r->w != 0 && r->h != 0) { + stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbrp_coord pad = (stbrp_coord) spc->padding; + + // pad on left and top + r->x += pad; + r->y += pad; + r->w -= pad; + r->h -= pad; + stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); + stbtt_GetGlyphBitmapBox(info, glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + &x0,&y0,&x1,&y1); + stbtt_MakeGlyphBitmapSubpixel(info, + spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w - spc->h_oversample+1, + r->h - spc->v_oversample+1, + spc->stride_in_bytes, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + glyph); + + if (spc->h_oversample > 1) + stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->h_oversample); + + if (spc->v_oversample > 1) + stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->v_oversample); + + bc->x0 = (stbtt_int16) r->x; + bc->y0 = (stbtt_int16) r->y; + bc->x1 = (stbtt_int16) (r->x + r->w); + bc->y1 = (stbtt_int16) (r->y + r->h); + bc->xadvance = scale * advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = (x0 + r->w) * recip_h + sub_x; + bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + + if (glyph == 0) + missing_glyph = j; + } else if (spc->skip_missing) { + return_value = 0; + } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { + ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; + } else { + return_value = 0; // if any fail, report failure + } + + ++k; + } + } + + // restore original values + spc->h_oversample = old_h_over; + spc->v_oversample = old_v_over; + + return return_value; +} + +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) +{ + stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); +} + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +{ + stbtt_fontinfo info; + int i,j,n, return_value = 1; + //stbrp_context *context = (stbrp_context *) spc->pack_info; + stbrp_rect *rects; + + // flag all characters as NOT packed + for (i=0; i < num_ranges; ++i) + for (j=0; j < ranges[i].num_chars; ++j) + ranges[i].chardata_for_range[j].x0 = + ranges[i].chardata_for_range[j].y0 = + ranges[i].chardata_for_range[j].x1 = + ranges[i].chardata_for_range[j].y1 = 0; + + n = 0; + for (i=0; i < num_ranges; ++i) + n += ranges[i].num_chars; + + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); + if (rects == NULL) + return 0; + + info.userdata = spc->user_allocator_context; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); + + n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); + + stbtt_PackFontRangesPackRects(spc, rects, n); + + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); + + STBTT_free(rects, spc->user_allocator_context); + return return_value; +} + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) +{ + stbtt_pack_range range; + range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; + range.array_of_unicode_codepoints = NULL; + range.num_chars = num_chars_in_range; + range.chardata_for_range = chardata_for_range; + range.font_size = font_size; + return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); +} + +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap) +{ + int i_ascent, i_descent, i_lineGap; + float scale; + stbtt_fontinfo info; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index)); + scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size); + stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap); + *ascent = (float) i_ascent * scale; + *descent = (float) i_descent * scale; + *lineGap = (float) i_lineGap * scale; +} + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +{ + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_packedchar *b = chardata + char_index; + + if (align_to_integer) { + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// sdf computation +// + +#define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) +#define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) + +static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2]) +{ + float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; + float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; + float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; + float roperp = orig[1]*ray[0] - orig[0]*ray[1]; + + float a = q0perp - 2*q1perp + q2perp; + float b = q1perp - q0perp; + float c = q0perp - roperp; + + float s0 = 0., s1 = 0.; + int num_s = 0; + + if (a != 0.0) { + float discr = b*b - a*c; + if (discr > 0.0) { + float rcpna = -1 / a; + float d = (float) STBTT_sqrt(discr); + s0 = (b+d) * rcpna; + s1 = (b-d) * rcpna; + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { + if (num_s == 0) s0 = s1; + ++num_s; + } + } + } else { + // 2*b*s + c = 0 + // s = -c / (2*b) + s0 = c / (-2 * b); + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + } + + if (num_s == 0) + return 0; + else { + float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); + float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; + + float q0d = q0[0]*rayn_x + q0[1]*rayn_y; + float q1d = q1[0]*rayn_x + q1[1]*rayn_y; + float q2d = q2[0]*rayn_x + q2[1]*rayn_y; + float rod = orig[0]*rayn_x + orig[1]*rayn_y; + + float q10d = q1d - q0d; + float q20d = q2d - q0d; + float q0rd = q0d - rod; + + hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; + hits[0][1] = a*s0+b; + + if (num_s > 1) { + hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; + hits[1][1] = a*s1+b; + return 2; + } else { + return 1; + } + } +} + +static int equal(float *a, float *b) +{ + return (a[0] == b[0] && a[1] == b[1]); +} + +static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) +{ + int i; + float orig[2], ray[2] = { 1, 0 }; + float y_frac; + int winding = 0; + + // make sure y never passes through a vertex of the shape + y_frac = (float) STBTT_fmod(y, 1.0f); + if (y_frac < 0.01f) + y += 0.01f; + else if (y_frac > 0.99f) + y -= 0.01f; + + orig[0] = x; + orig[1] = y; + + // test a ray from (-infinity,y) to (x,y) + for (i=0; i < nverts; ++i) { + if (verts[i].type == STBTT_vline) { + int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y; + int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } + if (verts[i].type == STBTT_vcurve) { + int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ; + int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy; + int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ; + int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); + int by = STBTT_max(y0,STBTT_max(y1,y2)); + if (y > ay && y < by && x > ax) { + float q0[2],q1[2],q2[2]; + float hits[2][2]; + q0[0] = (float)x0; + q0[1] = (float)y0; + q1[0] = (float)x1; + q1[1] = (float)y1; + q2[0] = (float)x2; + q2[1] = (float)y2; + if (equal(q0,q1) || equal(q1,q2)) { + x0 = (int)verts[i-1].x; + y0 = (int)verts[i-1].y; + x1 = (int)verts[i ].x; + y1 = (int)verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } else { + int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); + if (num_hits >= 1) + if (hits[0][0] < 0) + winding += (hits[0][1] < 0 ? -1 : 1); + if (num_hits >= 2) + if (hits[1][0] < 0) + winding += (hits[1][1] < 0 ? -1 : 1); + } + } + } + } + return winding; +} + +static float stbtt__cuberoot( float x ) +{ + if (x<0) + return -(float) STBTT_pow(-x,1.0f/3.0f); + else + return (float) STBTT_pow( x,1.0f/3.0f); +} + +// x^3 + a*x^2 + b*x + c = 0 +static int stbtt__solve_cubic(float a, float b, float c, float* r) +{ + float s = -a / 3; + float p = b - a*a / 3; + float q = a * (2*a*a - 9*b) / 27 + c; + float p3 = p*p*p; + float d = q*q + 4*p3 / 27; + if (d >= 0) { + float z = (float) STBTT_sqrt(d); + float u = (-q + z) / 2; + float v = (-q - z) / 2; + u = stbtt__cuberoot(u); + v = stbtt__cuberoot(v); + r[0] = s + u + v; + return 1; + } else { + float u = (float) STBTT_sqrt(-p/3); + float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative + float m = (float) STBTT_cos(v); + float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; + r[0] = s + u * 2 * m; + r[1] = s - u * (m + n); + r[2] = s - u * (m - n); + + //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? + //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); + //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); + return 3; + } +} + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + float scale_x = scale, scale_y = scale; + int ix0,iy0,ix1,iy1; + int w,h; + unsigned char *data; + + if (scale == 0) return NULL; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); + + // if empty, return NULL + if (ix0 == ix1 || iy0 == iy1) + return NULL; + + ix0 -= padding; + iy0 -= padding; + ix1 += padding; + iy1 += padding; + + w = (ix1 - ix0); + h = (iy1 - iy0); + + if (width ) *width = w; + if (height) *height = h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + // invert for y-downwards bitmaps + scale_y = -scale_y; + + { + int x,y,i,j; + float *precompute; + stbtt_vertex *verts; + int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); + data = (unsigned char *) STBTT_malloc(w * h, info->userdata); + precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); + + for (i=0,j=num_verts-1; i < num_verts; j=i++) { + if (verts[i].type == STBTT_vline) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; + float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); + precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; + float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; + float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float len2 = bx*bx + by*by; + if (len2 != 0.0f) + precompute[i] = 1.0f / (bx*bx + by*by); + else + precompute[i] = 0.0f; + } else + precompute[i] = 0.0f; + } + + for (y=iy0; y < iy1; ++y) { + for (x=ix0; x < ix1; ++x) { + float val; + float min_dist = 999999.0f; + float sx = (float) x + 0.5f; + float sy = (float) y + 0.5f; + float x_gspace = (sx / scale_x); + float y_gspace = (sy / scale_y); + + int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path + + for (i=0; i < num_verts; ++i) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + + if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) { + float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; + + float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + // coarse culling against bbox + //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && + // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) + dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; + STBTT_assert(i != 0); + if (dist < min_dist) { + // check position along line + // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) + // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) + float dx = x1-x0, dy = y1-y0; + float px = x0-sx, py = y0-sy; + // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy + // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve + float t = -(px*dx + py*dy) / (dx*dx + dy*dy); + if (t >= 0.0f && t <= 1.0f) + min_dist = dist; + } + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; + float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; + float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); + float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); + float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); + float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); + // coarse culling against bbox to avoid computing cubic unnecessarily + if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { + int num=0; + float ax = x1-x0, ay = y1-y0; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float mx = x0 - sx, my = y0 - sy; + float res[3] = {0.f,0.f,0.f}; + float px,py,t,it,dist2; + float a_inv = precompute[i]; + if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula + float a = 3*(ax*bx + ay*by); + float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); + float c = mx*ax+my*ay; + if (a == 0.0) { // if a is 0, it's linear + if (b != 0.0) { + res[num++] = -c/b; + } + } else { + float discriminant = b*b - 4*a*c; + if (discriminant < 0) + num = 0; + else { + float root = (float) STBTT_sqrt(discriminant); + res[0] = (-b - root)/(2*a); + res[1] = (-b + root)/(2*a); + num = 2; // don't bother distinguishing 1-solution case, as code below will still work + } + } + } else { + float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point + float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; + float d = (mx*ax+my*ay) * a_inv; + num = stbtt__solve_cubic(b, c, d, res); + } + dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { + t = res[0], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { + t = res[1], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { + t = res[2], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + } + } + } + if (winding == 0) + min_dist = -min_dist; // if outside the shape, value is negative + val = onedge_value + pixel_dist_scale * min_dist; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + data[(y-iy0)*w+(x-ix0)] = (unsigned char) val; + } + } + STBTT_free(precompute, info->userdata); + STBTT_free(verts, info->userdata); + } + return data; +} + +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8); + stbtt_int32 off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + slen = ttUSHORT(fc+loc+12+8); + off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, + float pixel_height, unsigned char *pixels, int pw, int ph, + int first_char, int num_chars, stbtt_bakedchar *chardata) +{ + return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata); +} + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) +{ + return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); +} + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) +{ + return stbtt_GetNumberOfFonts_internal((unsigned char *) data); +} + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset) +{ + return stbtt_InitFont_internal(info, (unsigned char *) data, offset); +} + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags) +{ + return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags); +} + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2); +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#endif // STB_TRUETYPE_IMPLEMENTATION + + +// FULL VERSION HISTORY +// +// 1.25 (2021-07-11) many fixes +// 1.24 (2020-02-05) fix warning +// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) +// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined +// 1.21 (2019-02-25) fix warning +// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() +// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) allow user-defined fabs() replacement +// fix memory leak if fontsize=0.0 +// fix warning from duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// allow PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) +// also more precise AA rasterizer, except if shapes overlap +// remove need for STBTT_sort +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example +// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes +// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ +// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match +// non-oversampled; STBTT_POINT_SIZE for packed case only +// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling +// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) +// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID +// 0.8b (2014-07-07) fix a warning +// 0.8 (2014-05-25) fix a few more warnings +// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (stb) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ \ No newline at end of file diff --git a/thirdparty/libgc/.gitignore b/thirdparty/libgc/.gitignore new file mode 100644 index 0000000..74e5b1d --- /dev/null +++ b/thirdparty/libgc/.gitignore @@ -0,0 +1 @@ +!* \ No newline at end of file diff --git a/thirdparty/libgc/amalgamation.txt b/thirdparty/libgc/amalgamation.txt new file mode 100644 index 0000000..de25a3d --- /dev/null +++ b/thirdparty/libgc/amalgamation.txt @@ -0,0 +1,7 @@ +The libgc source is distributed here as an amalgamation (https://sqlite.org/amalgamation.html). +This means that, rather than mirroring the entire bdwgc repo here, +[this script](https://gist.github.com/spaceface777/34d25420f2dc4953fb7864f44a211105) was used +to bundle all local includes together into a single C file, which is much easier to handle. +Furthermore, the script above was also used to minify (i.e. remove comments and whitespace in) +the garbage collector source. Together, these details help keep the V source distribution small, +can reduce compile times by 3%-15%, and can help C compilers generate more optimized code. diff --git a/thirdparty/libgc/gc.c b/thirdparty/libgc/gc.c new file mode 100644 index 0000000..2c946b1 --- /dev/null +++ b/thirdparty/libgc/gc.c @@ -0,0 +1,41829 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * Copyright (c) 2009-2018 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * Copyright (c) 2009-2018 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* This file could be used for the following purposes: */ +/* - get the complete GC as a single link object file (module); */ +/* - enable more compiler optimizations. */ + +/* Tip: to get the highest level of compiler optimizations, the typical */ +/* compiler options (GCC) to use are: */ +/* -O3 -fno-strict-aliasing -march=native -Wall -fprofile-generate/use */ + +/* Warning: GCC for Linux (for C++ clients only): Use -fexceptions both */ +/* for GC and the client otherwise GC_thread_exit_proc() is not */ +/* guaranteed to be invoked (see the comments in pthread_start.c). */ + +#ifndef __cplusplus + /* static is desirable here for more efficient linkage. */ + /* TODO: Enable this in case of the compilation as C++ code. */ +# define GC_INNER STATIC +# define GC_EXTERN GC_INNER + /* STATIC is defined in gcconfig.h. */ +#endif + +/* Small files go first... */ +/* + * Copyright (c) 2001 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright (c) 1997 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * This is mostly an internal header file. Typical clients should + * not use it. Clients that define their own object kinds with + * debugging allocators will probably want to include this, however. + * No attempt is made to keep the namespace clean. This should not be + * included from header files that are frequently included by clients. + */ + +#ifndef GC_DBG_MLC_H +#define GC_DBG_MLC_H + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_PRIVATE_H +#define GC_PRIVATE_H + +#ifdef HAVE_CONFIG_H +/* include/config.h. Generated from config.h.in by configure. */ +/* include/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to recognise all pointers to the interior of objects. */ +#define ALL_INTERIOR_POINTERS 1 + +/* AO load, store and/or test-and-set primitives are implemented in + libatomic_ops using locks. */ +/* #undef BASE_ATOMIC_OPS_EMULATED */ + +/* Erroneously cleared dirty bits checking. Use only for debugging of the + incremental collector. */ +/* #undef CHECKSUMS */ + +/* Define to discover thread stack bounds on Darwin without trying to walk the + frames on the stack. */ +/* #undef DARWIN_DONT_PARSE_STACK */ + +/* Define to force debug headers on all objects. */ +#define DBG_HDRS_ALL 1 + +/* Do not use user32.dll import library (Win32). */ +/* #undef DONT_USE_USER32_DLL */ + +/* Define to support pointer mask/shift set at runtime. */ +/* #undef DYNAMIC_POINTER_MASK */ + +/* Define to enable eCos target support. */ +/* #undef ECOS */ + +/* Wine getenv may not return NULL for missing entry. */ +/* #undef EMPTY_GETENV_RESULTS */ + +/* Define to enable alternative finalization interface. */ +#define ENABLE_DISCLAIM 1 + +/* Define to enable internal debug assertions. */ +/* #undef GC_ASSERTIONS */ + +/* Define to enable atomic uncollectible allocation. */ +#define GC_ATOMIC_UNCOLLECTABLE 1 + +/* Use GCC atomic intrinsics instead of libatomic_ops primitives. */ +#define GC_BUILTIN_ATOMIC 1 + +/* Define to build dynamic libraries with only API symbols exposed. */ +/* #undef GC_DLL */ + +/* Skip the initial guess of data root sets. */ +/* #undef GC_DONT_REGISTER_MAIN_STATIC_DATA */ + +/* Define to turn on GC_suspend_thread support (Linux only). */ +#define GC_ENABLE_SUSPEND_THREAD 1 + +/* Define to include support for gcj. */ +#define GC_GCJ_SUPPORT 1 + +/* Define if backtrace information is supported. */ +/* #undef GC_HAVE_BUILTIN_BACKTRACE */ + +/* Define to use 'pthread_sigmask' function if needed. */ +/* #undef GC_HAVE_PTHREAD_SIGMASK */ + +/* Enable Win32 DllMain-based approach of threads registering. */ +/* #undef GC_INSIDE_DLL */ + +/* Missing execinfo.h header. */ +/* #undef GC_MISSING_EXECINFO_H */ + +/* Missing sigsetjmp function. */ +/* #undef GC_NO_SIGSETJMP */ + +/* Disable threads discovery in GC. */ +/* #undef GC_NO_THREADS_DISCOVERY */ + +/* Read environment variables from the GC 'env' file. */ +/* #undef GC_READ_ENV_FILE */ + +/* Define and export GC_wcsdup function. */ +#define GC_REQUIRE_WCSDUP 1 + +/* Define to support platform-specific threads. */ +#define GC_THREADS 1 + +/* Force the GC to use signals based on SIGRTMIN+k. */ +/* #undef GC_USESIGRT_SIGNALS */ + +/* Define to cause the collector to redefine malloc and intercepted pthread + routines with their real names while using dlsym to refer to the original + routines. */ +/* #undef GC_USE_DLOPEN_WRAP */ + +/* The major version number of this GC release. */ +#define GC_VERSION_MAJOR 8 + +/* The micro version number of this GC release. */ +#define GC_VERSION_MICRO 0 + +/* The minor version number of this GC release. */ +#define GC_VERSION_MINOR 3 + +/* Define to support pthreads-win32 or winpthreads. */ +/* #undef GC_WIN32_PTHREADS */ + +/* Define to install pthread_atfork() handlers by default. */ +#define HANDLE_FORK 1 + +/* Define to use 'dladdr' function. */ +#define HAVE_DLADDR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the 'dl_iterate_phdr' function. */ +/* #undef HAVE_DL_ITERATE_PHDR */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* libatomic_ops AO_or primitive implementation is lock-free. */ +/* #undef HAVE_LOCKFREE_AO_OR */ + +/* Define to use 'pthread_setname_np(const char*)' function. */ +#define HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID 1 + +/* Define to use 'pthread_setname_np(pthread_t, const char*)' function. */ +/* #undef HAVE_PTHREAD_SETNAME_NP_WITH_TID */ + +/* Define to use 'pthread_setname_np(pthread_t, const char*, void *)' + function. */ +/* #undef HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG */ + +/* Define to use 'pthread_set_name_np(pthread_t, const char*)' function. */ +/* #undef HAVE_PTHREAD_SET_NAME_NP */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Do not define DYNAMIC_LOADING even if supported (i.e., build the collector + with disabled tracing of dynamic library data roots). */ +/* #undef IGNORE_DYNAMIC_LOADING */ + +/* Define to make it somewhat safer by default to finalize objects out of + order by specifying a nonstandard finalization mark procedure. */ +#define JAVA_FINALIZATION 1 + +/* Define to save back-pointers in debugging headers. */ +#define KEEP_BACK_PTRS 1 + +/* Define to optimize for large heaps or root sets. */ +/* #undef LARGE_CONFIG */ + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* Define to build the collector with the support of the functionality to + print max length of chain through unreachable objects ending in a reachable + one. */ +/* #undef MAKE_BACK_GRAPH */ + +/* Number of sequential garbage collections during those a candidate block for + unmapping should be marked as free. */ +#define MUNMAP_THRESHOLD 7 + +/* Define to not use system clock (cross compiling). */ +/* #undef NO_CLOCK */ + +/* Disable debugging, like GC_dump and its callees. */ +/* #undef NO_DEBUGGING */ + +/* Define to make the collector not allocate executable memory by default. */ +#define NO_EXECUTE_PERMISSION 1 + +/* Missing getcontext function. */ +/* #undef NO_GETCONTEXT */ + +/* Prohibit installation of pthread_atfork() handlers. */ +/* #undef NO_HANDLE_FORK */ + +/* Name of package */ +#define PACKAGE "gc" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "https://github.com/ivmai/bdwgc/issues" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "gc" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "gc 8.3.0" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "gc" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "8.3.0" + +/* Define to enable parallel marking. */ +#define PARALLEL_MARK 1 + +/* If defined, redirect free to this function. */ +/* #undef REDIRECT_FREE */ + +/* If defined, redirect malloc to this function. */ +/* #undef REDIRECT_MALLOC */ + +/* If defined, redirect GC_realloc to this function. */ +/* #undef REDIRECT_REALLOC */ + +/* The number of caller frames saved when allocating with the debugging API. + */ +/* #undef SAVE_CALL_COUNT */ + +/* Shorten the headers to minimize object size at the expense of checking for + writes past the end. */ +/* #undef SHORT_DBG_HDRS */ + +/* Define to tune the collector for small heap sizes. */ +/* #undef SMALL_CONFIG */ + +/* See the comment in gcconfig.h. */ +/* #undef SOLARIS25_PROC_VDB_BUG_FIXED */ + +/* Define to 1 if all of the C89 standard headers exist (not just the ones + required in a freestanding environment). This macro is provided for + backward compatibility; new code need not use it. */ +#define STDC_HEADERS 1 + +/* Define to work around a Solaris 5.3 bug (see dyn_load.c). */ +/* #undef SUNOS53_SHARED_LIB */ + +/* Define to enable thread-local allocation optimization. */ +#define THREAD_LOCAL_ALLOC 1 + +/* Use Unicode (W) variant of Win32 API instead of ASCII (A) one. */ +/* #undef UNICODE */ + +/* Define to use of compiler-support for thread-local variables. */ +/* #undef USE_COMPILER_TLS */ + +/* Define to use mmap instead of sbrk to expand the heap. */ +#define USE_MMAP 1 + +/* Define to return memory to OS with munmap calls. */ +#define USE_MUNMAP 1 + +/* Use rwlock for the allocator lock instead of mutex. */ +/* #undef USE_RWLOCK */ + +/* Define to use Win32 VirtualAlloc (instead of sbrk or mmap) to expand the + heap. */ +/* #undef USE_WINALLOC */ + +/* Version number of package */ +#define VERSION "8.3.0" + +/* The POSIX feature macro. */ +/* #undef _POSIX_C_SOURCE */ + +/* Indicates the use of pthreads (NetBSD). */ +/* #undef _PTHREADS */ + +/* Required define if using POSIX threads. */ +#define _REENTRANT 1 + +/* Define to '__inline__' or '__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +#endif + +#if !defined(GC_BUILD) && !defined(NOT_GCBUILD) +# define GC_BUILD +#endif + +#if (defined(__linux__) || defined(__GLIBC__) || defined(__GNU__) \ + || defined(__CYGWIN__) || defined(HAVE_DLADDR) \ + || defined(GC_HAVE_PTHREAD_SIGMASK) \ + || defined(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID) \ + || defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG) \ + || defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID)) && !defined(_GNU_SOURCE) + /* Can't test LINUX, since this must be defined before other includes. */ +# define _GNU_SOURCE 1 +#endif + +#if defined(__INTERIX) && !defined(_ALL_SOURCE) +# define _ALL_SOURCE 1 +#endif + +#if (defined(DGUX) && defined(GC_THREADS) || defined(DGUX386_THREADS) \ + || defined(GC_DGUX386_THREADS)) && !defined(_USING_POSIX4A_DRAFT10) +# define _USING_POSIX4A_DRAFT10 1 +#endif + +#if defined(__MINGW32__) && !defined(__MINGW_EXCPT_DEFINE_PSDK) \ + && defined(__i386__) && defined(GC_EXTERN) /* defined in gc.c */ + /* See the description in mark.c. */ +# define __MINGW_EXCPT_DEFINE_PSDK 1 +#endif + +# if defined(NO_DEBUGGING) && !defined(GC_ASSERTIONS) && !defined(NDEBUG) + /* To turn off assertion checking (in atomic_ops.h). */ +# define NDEBUG 1 +# endif + +#ifndef GC_H +/* This file is installed for backward compatibility. */ + + +#endif + +#include +#if !defined(sony_news) +# include +#endif + +#ifdef DGUX +# include +# include +#endif /* DGUX */ + +#ifdef BSD_TIME +# include +# include +#endif /* BSD_TIME */ + +#ifdef PARALLEL_MARK +# define AO_REQUIRE_CAS +# if !defined(__GNUC__) && !defined(AO_ASSUME_WINDOWS98) +# define AO_ASSUME_WINDOWS98 +# endif +#endif + +#include "gc/gc_tiny_fl.h" +#include "gc/gc_mark.h" + +typedef GC_word word; +typedef GC_signed_word signed_word; + +typedef int GC_bool; +#define TRUE 1 +#define FALSE 0 + +#ifndef PTR_T_DEFINED + typedef char * ptr_t; /* A generic pointer to which we can add */ + /* byte displacements and which can be used */ + /* for address comparisons. */ +# define PTR_T_DEFINED +#endif + +#ifndef SIZE_MAX +# include +#endif +#if defined(SIZE_MAX) && !defined(CPPCHECK) +# define GC_SIZE_MAX ((size_t)SIZE_MAX) + /* Extra cast to workaround some buggy SIZE_MAX definitions. */ +#else +# define GC_SIZE_MAX (~(size_t)0) +#endif + +#if (GC_GNUC_PREREQ(3, 0) || defined(__clang__)) && !defined(LINT2) +# define EXPECT(expr, outcome) __builtin_expect(expr,outcome) + /* Equivalent to (expr), but predict that usually (expr)==outcome. */ +#else +# define EXPECT(expr, outcome) (expr) +#endif /* __GNUC__ */ + +/* Saturated addition of size_t values. Used to avoid value wrap */ +/* around on overflow. The arguments should have no side effects. */ +#define SIZET_SAT_ADD(a, b) \ + (EXPECT((a) < GC_SIZE_MAX - (b), TRUE) ? (a) + (b) : GC_SIZE_MAX) + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 2000-2004 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2009-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * This header is private to the gc. It is almost always included from + * gc_priv.h. However it is possible to include it by itself if just the + * configuration macros are needed. In that + * case, a few declarations relying on types declared in gc_priv.h will be + * omitted. + */ + +#ifndef GCCONFIG_H +#define GCCONFIG_H + +#ifndef GC_H +# ifdef HAVE_CONFIG_H + +# endif + +#endif + +#ifdef CPPCHECK +# undef CLOCKS_PER_SEC +# undef FIXUP_POINTER +# undef POINTER_MASK +# undef POINTER_SHIFT +# undef REDIRECT_REALLOC +# undef _MAX_PATH +#endif + +#ifndef PTR_T_DEFINED + typedef char * ptr_t; +# define PTR_T_DEFINED +#endif + +#if !defined(sony_news) +# include /* For size_t, etc. */ +#endif + +/* Note: Only wrap our own declarations, and not the included headers. */ +/* In this case, wrap our entire file, but temporarily unwrap/rewrap */ +/* around #includes. Types and macros do not need such wrapping, only */ +/* the declared global data and functions. */ +#ifdef __cplusplus +# define EXTERN_C_BEGIN extern "C" { +# define EXTERN_C_END } /* extern "C" */ +#else +# define EXTERN_C_BEGIN /* empty */ +# define EXTERN_C_END /* empty */ +#endif + +EXTERN_C_BEGIN + +/* Convenient internal macro to test version of Clang. */ +#if defined(__clang__) && defined(__clang_major__) +# define GC_CLANG_PREREQ(major, minor) \ + ((__clang_major__ << 8) + __clang_minor__ >= ((major) << 8) + (minor)) +# define GC_CLANG_PREREQ_FULL(major, minor, patchlevel) \ + (GC_CLANG_PREREQ(major, (minor) + 1) \ + || (__clang_major__ == (major) && __clang_minor__ == (minor) \ + && __clang_patchlevel__ >= (patchlevel))) +#else +# define GC_CLANG_PREREQ(major, minor) 0 /* FALSE */ +# define GC_CLANG_PREREQ_FULL(major, minor, patchlevel) 0 +#endif + +#ifdef LINT2 + /* A macro (based on a tricky expression) to prevent false warnings */ + /* like "Array compared to 0", "Comparison of identical expressions", */ + /* "Untrusted loop bound" output by some static code analysis tools. */ + /* The argument should not be a literal value. The result is */ + /* converted to word type. (Actually, GC_word is used instead of */ + /* word type as the latter might be undefined at the place of use.) */ +# define COVERT_DATAFLOW(w) (~(GC_word)(w)^(~(GC_word)0)) +#else +# define COVERT_DATAFLOW(w) ((GC_word)(w)) +#endif + +/* Machine dependent parameters. Some tuning parameters can be found */ +/* near the top of gc_priv.h. */ + +/* Machine specific parts contributed by various people. See README file. */ + +#if defined(__ANDROID__) && !defined(HOST_ANDROID) + /* __ANDROID__ macro is defined by Android NDK gcc. */ +# define HOST_ANDROID 1 +#endif + +#if defined(TIZEN) && !defined(HOST_TIZEN) +# define HOST_TIZEN 1 +#endif + +#if defined(__SYMBIAN32__) && !defined(SYMBIAN) +# define SYMBIAN +# ifdef __WINS__ +# pragma data_seg(".data2") +# endif +#endif + +/* First a unified test for Linux: */ +# if (defined(linux) || defined(__linux__) || defined(HOST_ANDROID)) \ + && !defined(LINUX) && !defined(__native_client__) +# define LINUX +# endif + +/* And one for NetBSD: */ +# if defined(__NetBSD__) +# define NETBSD +# endif + +/* And one for OpenBSD: */ +# if defined(__OpenBSD__) +# define OPENBSD +# endif + +/* And one for FreeBSD: */ +# if (defined(__FreeBSD__) || defined(__DragonFly__) \ + || defined(__FreeBSD_kernel__)) && !defined(FREEBSD) \ + && !defined(GC_NO_FREEBSD) /* Orbis compiler defines __FreeBSD__ */ +# define FREEBSD +# endif + +#if defined(FREEBSD) || defined(NETBSD) || defined(OPENBSD) +# define ANY_BSD +#endif + +# if defined(__EMBOX__) +# define EMBOX +# endif + +# if defined(__KOS__) +# define KOS +# endif + +# if defined(__QNX__) && !defined(QNX) +# define QNX +# endif + +/* And one for Darwin: */ +# if defined(macosx) || (defined(__APPLE__) && defined(__MACH__)) +# define DARWIN + EXTERN_C_END +# include + EXTERN_C_BEGIN +# endif + +/* Determine the machine type: */ +# if defined(__native_client__) +# define NACL +# if !defined(__portable_native_client__) && !defined(__arm__) +# define I386 +# define mach_type_known +# else + /* Here we will rely upon arch-specific defines. */ +# endif +# endif +# if defined(__aarch64__) && !defined(ANY_BSD) && !defined(DARWIN) \ + && !defined(LINUX) && !defined(KOS) && !defined(QNX) \ + && !defined(NN_BUILD_TARGET_PLATFORM_NX) && !defined(_WIN32) +# define AARCH64 +# define NOSYS +# define mach_type_known +# endif +# if defined(__arm) || defined(__arm__) || defined(__thumb__) +# define ARM32 +# if defined(NACL) || defined(SYMBIAN) +# define mach_type_known +# elif !defined(ANY_BSD) && !defined(DARWIN) && !defined(LINUX) \ + && !defined(QNX) && !defined(NN_PLATFORM_CTR) \ + && !defined(SN_TARGET_PSP2) && !defined(_WIN32) \ + && !defined(__CEGCC__) && !defined(GC_NO_NOSYS) +# define NOSYS +# define mach_type_known +# endif +# endif +# if defined(sun) && defined(mc68000) && !defined(CPPCHECK) +# error SUNOS4 no longer supported +# endif +# if defined(hp9000s300) && !defined(CPPCHECK) +# error M68K based HP machines no longer supported +# endif +# if defined(vax) || defined(__vax__) +# define VAX +# ifdef ultrix +# define ULTRIX +# else +# define BSD +# endif +# define mach_type_known +# endif +# if defined(NETBSD) && defined(__vax__) +# define VAX +# define mach_type_known +# endif +# if (defined(mips) || defined(__mips) || defined(_mips)) \ + && !defined(__TANDEM) && !defined(ANY_BSD) && !defined(LINUX) +# define MIPS +# if defined(nec_ews) || defined(_nec_ews) +# define EWS4800 +# define mach_type_known +# elif defined(ultrix) || defined(__ultrix) +# define ULTRIX +# define mach_type_known +# elif !defined(_WIN32_WCE) && !defined(__CEGCC__) && !defined(__MINGW32CE__) +# define IRIX5 /* or IRIX 6.X */ +# define mach_type_known +# endif /* !MSWINCE */ +# endif +# if defined(DGUX) && (defined(i386) || defined(__i386__)) +# define I386 +# ifndef _USING_DGUX +# define _USING_DGUX +# endif +# define mach_type_known +# endif +# if defined(sequent) && (defined(i386) || defined(__i386__)) +# define I386 +# define SEQUENT +# define mach_type_known +# endif +# if (defined(sun) || defined(__sun)) && (defined(i386) || defined(__i386__)) +# define I386 +# define SOLARIS +# define mach_type_known +# endif +# if (defined(sun) || defined(__sun)) && defined(__amd64) +# define X86_64 +# define SOLARIS +# define mach_type_known +# endif +# if (defined(__OS2__) || defined(__EMX__)) && defined(__32BIT__) +# define I386 +# define OS2 +# define mach_type_known +# endif +# if defined(ibm032) && !defined(CPPCHECK) +# error IBM PC/RT no longer supported +# endif +# if (defined(sun) || defined(__sun)) && (defined(sparc) || defined(__sparc)) + /* Test for SunOS 5.x */ + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define SPARC +# define SOLARIS +# define mach_type_known +# elif defined(sparc) && defined(unix) && !defined(sun) && !defined(linux) \ + && !defined(ANY_BSD) +# define SPARC +# define DRSNX +# define mach_type_known +# endif +# if defined(_IBMR2) +# define POWERPC +# define AIX +# define mach_type_known +# endif +# if defined(_M_XENIX) && defined(_M_SYSV) && defined(_M_I386) + /* TODO: The above test may need refinement. */ +# define I386 +# if defined(_SCO_ELF) +# define SCO_ELF +# else +# define SCO +# endif +# define mach_type_known +# endif +# if defined(_AUX_SOURCE) && !defined(CPPCHECK) +# error A/UX no longer supported +# endif +# if defined(_PA_RISC1_0) || defined(_PA_RISC1_1) || defined(_PA_RISC2_0) \ + || defined(hppa) || defined(__hppa__) +# define HP_PA +# if !defined(LINUX) && !defined(HPUX) && !defined(OPENBSD) +# define HPUX +# endif +# define mach_type_known +# endif +# if defined(__ia64) && (defined(_HPUX_SOURCE) || defined(__HP_aCC)) +# define IA64 +# ifndef HPUX +# define HPUX +# endif +# define mach_type_known +# endif +# if (defined(__BEOS__) || defined(__HAIKU__)) && defined(_X86_) +# define I386 +# define HAIKU +# define mach_type_known +# endif +# if defined(__HAIKU__) && (defined(__amd64__) || defined(__x86_64__)) +# define X86_64 +# define HAIKU +# define mach_type_known +# endif +# if defined(__alpha) || defined(__alpha__) +# define ALPHA +# if !defined(ANY_BSD) && !defined(LINUX) +# define OSF1 /* a.k.a Digital Unix */ +# endif +# define mach_type_known +# endif +# if defined(_AMIGA) && !defined(AMIGA) +# define AMIGA +# endif +# ifdef AMIGA +# define M68K +# define mach_type_known +# endif +# if defined(THINK_C) \ + || (defined(__MWERKS__) && !defined(__powerc) && !defined(SYMBIAN)) +# define M68K +# define MACOS +# define mach_type_known +# endif +# if defined(__MWERKS__) && defined(__powerc) && !defined(__MACH__) \ + && !defined(SYMBIAN) +# define POWERPC +# define MACOS +# define mach_type_known +# endif +# if defined(__rtems__) && (defined(i386) || defined(__i386__)) +# define I386 +# define RTEMS +# define mach_type_known +# endif +# if defined(NeXT) && defined(mc68000) +# define M68K +# define NEXT +# define mach_type_known +# endif +# if defined(NeXT) && (defined(i386) || defined(__i386__)) +# define I386 +# define NEXT +# define mach_type_known +# endif +# if defined(bsdi) && (defined(i386) || defined(__i386__)) +# define I386 +# define BSDI +# define mach_type_known +# endif +# if defined(__386BSD__) && !defined(mach_type_known) +# define I386 +# define THREE86BSD +# define mach_type_known +# endif +# if defined(_CX_UX) && defined(_M88K) +# define M88K +# define CX_UX +# define mach_type_known +# endif +# if defined(DGUX) && defined(m88k) +# define M88K + /* DGUX defined */ +# define mach_type_known +# endif +# if defined(_WIN32_WCE) || defined(__CEGCC__) || defined(__MINGW32CE__) + /* SH3, SH4, MIPS already defined for corresponding architectures */ +# if defined(SH3) || defined(SH4) +# define SH +# endif +# if defined(x86) || defined(__i386__) +# define I386 +# endif +# if defined(_M_ARM) || defined(ARM) || defined(_ARM_) +# define ARM32 +# endif +# define MSWINCE +# define mach_type_known +# else +# if ((defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300)) \ + || (defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__CYGWIN__) \ + && !defined(__INTERIX) && !defined(SYMBIAN)) \ + || defined(__MINGW32__) +# if defined(__LP64__) || defined(_M_X64) +# define X86_64 +# elif defined(_M_ARM) +# define ARM32 +# elif defined(_M_ARM64) +# define AARCH64 +# else /* _M_IX86 */ +# define I386 +# endif +# ifdef _XBOX_ONE +# define MSWIN_XBOX1 +# else +# ifndef MSWIN32 +# define MSWIN32 /* or Win64 */ +# endif +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define MSWINRT_FLAVOR +# endif +# endif +# define mach_type_known +# endif +# if defined(_MSC_VER) && defined(_M_IA64) +# define IA64 +# define MSWIN32 /* Really Win64, but we do not treat 64-bit */ + /* variants as a different platform. */ +# endif +# endif /* !_WIN32_WCE && !__CEGCC__ && !__MINGW32CE__ */ +# if defined(__DJGPP__) +# define I386 +# ifndef DJGPP +# define DJGPP /* MSDOS running the DJGPP port of GCC */ +# endif +# define mach_type_known +# endif +# if defined(__CYGWIN32__) || defined(__CYGWIN__) +# if defined(__LP64__) +# define X86_64 +# else +# define I386 +# endif +# define CYGWIN32 +# define mach_type_known +# endif /* __CYGWIN__ */ +# if defined(__INTERIX) +# define I386 +# define INTERIX +# define mach_type_known +# endif +# if defined(_UTS) && !defined(mach_type_known) +# define S370 +# define UTS4 +# define mach_type_known +# endif +# if defined(__pj__) && !defined(CPPCHECK) +# error PicoJava no longer supported + /* The implementation had problems, and I haven't heard of users */ + /* in ages. If you want it resurrected, let me know. */ +# endif +# if defined(__embedded__) && defined(PPC) +# define POWERPC +# define NOSYS +# define mach_type_known +# endif +# if defined(__WATCOMC__) && defined(__386__) +# define I386 +# if !defined(OS2) && !defined(MSWIN32) && !defined(DOS4GW) +# if defined(__OS2__) +# define OS2 +# elif defined(__WINDOWS_386__) || defined(__NT__) +# define MSWIN32 +# else +# define DOS4GW +# endif +# endif +# define mach_type_known +# endif /* __WATCOMC__ && __386__ */ +# if defined(__GNU__) && defined(__i386__) + /* The Debian Hurd running on generic PC */ +# define HURD +# define I386 +# define mach_type_known +# endif +# if defined(__GNU__) && defined(__x86_64__) +# define HURD +# define X86_64 +# define mach_type_known +# endif +# if defined(__TANDEM) + /* Nonstop S-series */ + /* FIXME: Should recognize Integrity series? */ +# define MIPS +# define NONSTOP +# define mach_type_known +# endif +# if defined(__tile__) && defined(LINUX) +# ifdef __tilegx__ +# define TILEGX +# else +# define TILEPRO +# endif +# define mach_type_known +# endif /* __tile__ */ +# if defined(NN_BUILD_TARGET_PLATFORM_NX) +# define AARCH64 +# define NINTENDO_SWITCH +# define mach_type_known +# endif +# if defined(__EMSCRIPTEN__) || defined(EMSCRIPTEN) +# define WEBASSEMBLY +# ifndef EMSCRIPTEN +# define EMSCRIPTEN +# endif +# define mach_type_known +# endif +# if defined(__wasi__) +# define WEBASSEMBLY +# define WASI +# define mach_type_known +# endif + +# if defined(__aarch64__) \ + && (defined(ANY_BSD) || defined(DARWIN) || defined(LINUX) \ + || defined(KOS) || defined(QNX)) +# define AARCH64 +# define mach_type_known +# elif defined(__arc__) && defined(LINUX) +# define ARC +# define mach_type_known +# elif (defined(__arm) || defined(__arm__) || defined(__arm32__) \ + || defined(__ARM__)) \ + && (defined(ANY_BSD) || defined(DARWIN) || defined(LINUX) \ + || defined(QNX) || defined(NN_PLATFORM_CTR) \ + || defined(SN_TARGET_PSP2)) +# define ARM32 +# define mach_type_known +# elif defined(__avr32__) && defined(LINUX) +# define AVR32 +# define mach_type_known +# elif defined(__cris__) && defined(LINUX) +# ifndef CRIS +# define CRIS +# endif +# define mach_type_known +# elif defined(__e2k__) && defined(LINUX) +# define E2K +# define mach_type_known +# elif defined(__hexagon__) && defined(LINUX) +# define HEXAGON +# define mach_type_known +# elif (defined(__i386__) || defined(i386) || defined(__X86__)) \ + && (defined(ANY_BSD) || defined(DARWIN) || defined(EMBOX) \ + || defined(LINUX) || defined(QNX)) +# define I386 +# define mach_type_known +# elif (defined(__ia64) || defined(__ia64__)) && defined(LINUX) +# define IA64 +# define mach_type_known +# elif defined(__loongarch__) && defined(LINUX) +# define LOONGARCH +# define mach_type_known +# elif defined(__m32r__) && defined(LINUX) +# define M32R +# define mach_type_known +# elif ((defined(__m68k__) || defined(m68k)) \ + && (defined(NETBSD) || defined(OPENBSD))) \ + || (defined(__mc68000__) && defined(LINUX)) +# define M68K +# define mach_type_known +# elif (defined(__mips) || defined(_mips) || defined(mips)) \ + && (defined(ANY_BSD) || defined(LINUX)) +# define MIPS +# define mach_type_known +# elif (defined(__NIOS2__) || defined(__NIOS2) || defined(__nios2__)) \ + && defined(LINUX) +# define NIOS2 /* Altera NIOS2 */ +# define mach_type_known +# elif defined(__or1k__) && defined(LINUX) +# define OR1K /* OpenRISC (or1k) */ +# define mach_type_known +# elif (defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) \ + || defined(__ppc64__) || defined(powerpc) || defined(powerpc64)) \ + && (defined(ANY_BSD) || defined(DARWIN) || defined(LINUX)) +# define POWERPC +# define mach_type_known +# elif defined(__riscv) && (defined(ANY_BSD) || defined(LINUX)) +# define RISCV +# define mach_type_known +# elif defined(__s390__) && defined(LINUX) +# define S390 +# define mach_type_known +# elif defined(__sh__) \ + && (defined(LINUX) || defined(NETBSD) || defined(OPENBSD)) +# define SH +# define mach_type_known +# elif (defined(__sparc__) || defined(sparc)) \ + && (defined(ANY_BSD) || defined(LINUX)) +# define SPARC +# define mach_type_known +# elif defined(__sw_64__) && defined(LINUX) +# define SW_64 +# define mach_type_known +# elif (defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) \ + || defined(__X86_64__)) \ + && (defined(ANY_BSD) || defined(DARWIN) || defined(LINUX) \ + || defined(QNX)) +# define X86_64 +# define mach_type_known +# endif + +/* Feel free to add more clauses here. Or manually define the machine */ +/* type here. A machine type is characterized by the architecture. */ +/* Some machine types are further subdivided by OS. Macros such as */ +/* LINUX, FREEBSD, etc. distinguish them. The distinction in these */ +/* cases is usually the stack starting address. */ + +# if !defined(mach_type_known) && !defined(CPPCHECK) +# error The collector has not been ported to this machine/OS combination +# endif + + /* Mapping is: M68K ==> Motorola 680X0 */ + /* (NEXT, and SYSV (A/UX), */ + /* MACOS and AMIGA variants) */ + /* I386 ==> Intel 386 */ + /* (SEQUENT, OS2, SCO, LINUX, NETBSD, */ + /* FREEBSD, THREE86BSD, MSWIN32, */ + /* BSDI, SOLARIS, NEXT and others) */ + /* NS32K ==> Encore Multimax */ + /* MIPS ==> R2000 through R14K */ + /* (many variants) */ + /* VAX ==> DEC VAX */ + /* (BSD, ULTRIX variants) */ + /* HP_PA ==> HP9000/700 & /800 */ + /* HP/UX, LINUX */ + /* SPARC ==> SPARC v7/v8/v9 */ + /* (SOLARIS, LINUX, DRSNX variants) */ + /* ALPHA ==> DEC Alpha */ + /* (OSF1 and LINUX variants) */ + /* LOONGARCH ==> Loongson LoongArch */ + /* (LINUX 32- and 64-bit variants) */ + /* M88K ==> Motorola 88XX0 */ + /* (CX_UX and DGUX) */ + /* S370 ==> 370-like machine */ + /* running Amdahl UTS4 */ + /* S390 ==> 390-like machine */ + /* running LINUX */ + /* AARCH64 ==> ARM AArch64 */ + /* (LP64 and ILP32 variants) */ + /* E2K ==> Elbrus 2000 */ + /* running LINUX */ + /* ARM32 ==> Intel StrongARM */ + /* (many variants) */ + /* IA64 ==> Intel IPF */ + /* (e.g. Itanium) */ + /* (LINUX and HPUX) */ + /* SH ==> Hitachi SuperH */ + /* (LINUX & MSWINCE) */ + /* SW_64 ==> Sunway (Shenwei) */ + /* running LINUX */ + /* X86_64 ==> AMD x86-64 */ + /* POWERPC ==> IBM/Apple PowerPC */ + /* (MACOS(<=9),DARWIN(incl.MACOSX),*/ + /* LINUX, NETBSD, AIX, NOSYS */ + /* variants) */ + /* Handles 32 and 64-bit variants. */ + /* ARC ==> Synopsys ARC */ + /* AVR32 ==> Atmel RISC 32-bit */ + /* CRIS ==> Axis Etrax */ + /* M32R ==> Renesas M32R */ + /* NIOS2 ==> Altera NIOS2 */ + /* HEXAGON ==> Qualcomm Hexagon */ + /* OR1K ==> OpenRISC/or1k */ + /* RISCV ==> RISC-V 32/64-bit */ + /* TILEPRO ==> Tilera TILEPro */ + /* TILEGX ==> Tilera TILE-Gx */ + +/* + * For each architecture and OS, the following need to be defined: + * + * CPP_WORDSZ is a simple integer constant representing the word size + * in bits. We assume byte addressability, where a byte has 8 bits. + * We also assume CPP_WORDSZ is either 32 or 64. + * (We care about the length of pointers, not hardware + * bus widths. Thus a 64-bit processor with a C compiler that uses + * 32-bit pointers should use CPP_WORDSZ of 32, not 64.) + * + * MACH_TYPE is a string representation of the machine type. + * OS_TYPE is analogous for the OS. + * + * ALIGNMENT is the largest N, such that + * all pointer are guaranteed to be aligned on N byte boundaries. + * defining it to be 1 will always work, but perform poorly. + * + * DATASTART is the beginning of the data segment. + * On some platforms SEARCH_FOR_DATA_START is defined. + * The latter will cause GC_data_start to + * be set to an address determined by accessing data backwards from _end + * until an unmapped page is found. DATASTART will be defined to be + * GC_data_start. + * On UNIX-like systems, the collector will scan the area between DATASTART + * and DATAEND for root pointers. + * + * DATAEND, if not "end", where "end" is defined as "extern int end[]". + * RTH suggests gaining access to linker script synth'd values with + * this idiom instead of "&end", where "end" is defined as "extern int end". + * Otherwise, "GCC will assume these are in .sdata/.sbss" and it will, e.g., + * cause failures on alpha*-*-* with -msmall-data or -fpic or mips-*-* + * without any special options. + * + * STACKBOTTOM is the cold end of the stack, which is usually the + * highest address in the stack. + * Under PCR or OS/2, we have other ways of finding thread stacks. + * For each machine, the following should: + * 1) define STACK_GROWS_UP if the stack grows toward higher addresses, and + * 2) define exactly one of + * STACKBOTTOM (should be defined to be an expression) + * LINUX_STACKBOTTOM + * HEURISTIC1 + * HEURISTIC2 + * If STACKBOTTOM is defined, then its value will be used directly (as the + * stack bottom). If LINUX_STACKBOTTOM is defined, then it will be determined + * with a method appropriate for most Linux systems. Currently we look + * first for __libc_stack_end (currently only if USE_LIBC_PRIVATES is + * defined), and if that fails read it from /proc. (If USE_LIBC_PRIVATES + * is not defined and NO_PROC_STAT is defined, we revert to HEURISTIC2.) + * If either of the last two macros are defined, then STACKBOTTOM is computed + * during collector startup using one of the following two heuristics: + * HEURISTIC1: Take an address inside GC_init's frame, and round it up to + * the next multiple of STACK_GRAN. + * HEURISTIC2: Take an address inside GC_init's frame, increment it repeatedly + * in small steps (decrement if STACK_GROWS_UP), and read the value + * at each location. Remember the value when the first + * Segmentation violation or Bus error is signaled. Round that + * to the nearest plausible page boundary, and use that instead + * of STACKBOTTOM. + * + * Gustavo Rodriguez-Rivera points out that on most (all?) Unix machines, + * the value of environ is a pointer that can serve as STACKBOTTOM. + * I expect that HEURISTIC2 can be replaced by this approach, which + * interferes far less with debugging. However it has the disadvantage + * that it's confused by a putenv call before the collector is initialized. + * This could be dealt with by intercepting putenv ... + * + * If no expression for STACKBOTTOM can be found, and neither of the above + * heuristics are usable, the collector can still be used with all of the above + * undefined, provided one of the following is done: + * 1) GC_mark_roots can be changed to somehow mark from the correct stack(s) + * without reference to STACKBOTTOM. This is appropriate for use in + * conjunction with thread packages, since there will be multiple stacks. + * (Allocating thread stacks in the heap, and treating them as ordinary + * heap data objects is also possible as a last resort. However, this is + * likely to introduce significant amounts of excess storage retention + * unless the dead parts of the thread stacks are periodically cleared.) + * 2) Client code may set GC_stackbottom before calling any GC_ routines. + * If the author of the client code controls the main program, this is + * easily accomplished by introducing a new main program, setting + * GC_stackbottom to the address of a local variable, and then calling + * the original main program. The new main program would read something + * like (provided real_main() is not inlined by the compiler): + * + + * + * main(argc, argv, envp) + * int argc; + * char **argv, **envp; + * { + * volatile int dummy; + * + * GC_stackbottom = (ptr_t)(&dummy); + * return real_main(argc, argv, envp); + * } + * + * + * Each architecture may also define the style of virtual dirty bit + * implementation to be used: + * GWW_VDB: Use Win32 GetWriteWatch primitive. + * MPROTECT_VDB: Write protect the heap and catch faults. + * PROC_VDB: Use the SVR4 /proc primitives to read dirty bits. + * SOFT_VDB: Use the Linux /proc primitives to track dirty bits. + * + * The first and second one may be combined, in which case a runtime + * selection will be made, based on GetWriteWatch availability. + * + * An architecture may define DYNAMIC_LOADING if dyn_load.c + * defined GC_register_dynamic_libraries() for the architecture. + * + * An architecture may define PREFETCH(x) to preload the cache with *x. + * This defaults to GCC built-in operation (or a no-op for other compilers). + * + * GC_PREFETCH_FOR_WRITE(x) is used if *x is about to be written. + * + * An architecture may also define CLEAR_DOUBLE(x) to be a fast way to + * clear the two words at GC_malloc-aligned address x. By default, + * word stores of 0 are used instead. + * + * HEAP_START may be defined as the initial address hint for mmap-based + * allocation. + */ + +# ifdef LINUX /* TODO: FreeBSD too? */ + EXTERN_C_END +# include /* for __GLIBC__ and __GLIBC_MINOR__, at least */ + EXTERN_C_BEGIN +# endif + +/* Convenient internal macro to test glibc version (if compiled against). */ +#if defined(__GLIBC__) && defined(__GLIBC_MINOR__) +# define GC_GLIBC_PREREQ(major, minor) \ + ((__GLIBC__ << 8) + __GLIBC_MINOR__ >= ((major) << 8) + (minor)) +#else +# define GC_GLIBC_PREREQ(major, minor) 0 /* FALSE */ +#endif + +#define PTRT_ROUNDUP_BY_MASK(p, mask) \ + ((ptr_t)(((word)(p) + (mask)) & ~(word)(mask))) + +/* If available, we can use __builtin_unwind_init() to push the */ +/* relevant registers onto the stack. */ +# if GC_GNUC_PREREQ(2, 8) \ + && !GC_GNUC_PREREQ(11, 0) /* broken at least in 11.2.0 on cygwin64 */ \ + && !defined(__INTEL_COMPILER) && !defined(__PATHCC__) \ + && !defined(__FUJITSU) /* for FX10 system */ \ + && !(defined(POWERPC) && defined(DARWIN)) /* for MacOS X 10.3.9 */ \ + && !defined(E2K) && !defined(RTEMS) \ + && !defined(__ARMCC_VERSION) /* does not exist in armcc gnu emu */ \ + && (!defined(__clang__) \ + || GC_CLANG_PREREQ(8, 0) /* was no-op in clang-3 at least */) +# define HAVE_BUILTIN_UNWIND_INIT +# endif + +#if (defined(__CC_ARM) || defined(CX_UX) || defined(DJGPP) || defined(EMBOX) \ + || defined(EWS4800) || defined(LINUX) || defined(OS2) \ + || defined(RTEMS) || defined(UTS4) || defined(MSWIN32) \ + || defined(MSWINCE)) && !defined(NO_UNDERSCORE_SETJMP) +# define NO_UNDERSCORE_SETJMP +#endif + +#define STACK_GRAN 0x1000000 + +/* The common OS-specific definitions (should be applicable to */ +/* all (or most, at least) supported architectures). */ + +# ifdef CYGWIN32 +# define OS_TYPE "CYGWIN32" +# define RETRY_GET_THREAD_CONTEXT +# ifdef USE_WINALLOC +# define GWW_VDB +# elif defined(USE_MMAP) +# define USE_MMAP_ANON +# endif +# endif /* CYGWIN32 */ + +# ifdef DARWIN +# define OS_TYPE "DARWIN" +# define DYNAMIC_LOADING + /* TODO: see get_end(3), get_etext() and get_end() should not be used. */ + /* These aren't used when dyld support is enabled (it is by default). */ +# define DATASTART ((ptr_t)get_etext()) +# define DATAEND ((ptr_t)get_end()) +# define USE_MMAP_ANON + /* There seems to be some issues with trylock hanging on darwin. */ + /* TODO: This should be looked into some more. */ +# define NO_PTHREAD_TRYLOCK +# ifndef TARGET_OS_XR +# define TARGET_OS_XR 0 +# endif +# ifndef TARGET_OS_VISION +# define TARGET_OS_VISION 0 +# endif +# endif /* DARWIN */ + +# ifdef EMBOX +# define OS_TYPE "EMBOX" + extern int _modules_data_start[]; + extern int _apps_bss_end[]; +# define DATASTART ((ptr_t)_modules_data_start) +# define DATAEND ((ptr_t)_apps_bss_end) + /* Note: the designated area might be quite large (several */ + /* dozens of MBs) as it includes data and bss of all apps */ + /* and modules of the built binary image. */ +# endif /* EMBOX */ + +# ifdef FREEBSD +# define OS_TYPE "FREEBSD" +# define FREEBSD_STACKBOTTOM +# ifdef __ELF__ +# define DYNAMIC_LOADING +# endif +# if !defined(ALPHA) && !defined(SPARC) + extern char etext[]; +# define DATASTART GC_FreeBSDGetDataStart(0x1000, (ptr_t)etext) +# define DATASTART_USES_BSDGETDATASTART +# ifndef GC_FREEBSD_THREADS +# define MPROTECT_VDB +# endif +# endif +# endif /* FREEBSD */ + +# ifdef HAIKU +# define OS_TYPE "HAIKU" +# define DYNAMIC_LOADING +# define MPROTECT_VDB + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define GETPAGESIZE() (unsigned)B_PAGE_SIZE +# endif /* HAIKU */ + +# ifdef HPUX +# define OS_TYPE "HPUX" + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) +# ifdef USE_MMAP +# define USE_MMAP_ANON +# endif +# define DYNAMIC_LOADING +# define GETPAGESIZE() (unsigned)sysconf(_SC_PAGE_SIZE) +# endif /* HPUX */ + +# ifdef HURD +# define OS_TYPE "HURD" +# define HEURISTIC2 +# define SEARCH_FOR_DATA_START + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) + /* TODO: MPROTECT_VDB is not quite working yet? */ +# define DYNAMIC_LOADING +# define USE_MMAP_ANON +# endif /* HURD */ + +# ifdef LINUX +# define OS_TYPE "LINUX" +# if defined(FORCE_MPROTECT_BEFORE_MADVISE) \ + || defined(PREFER_MMAP_PROT_NONE) +# define COUNT_UNMAPPED_REGIONS +# endif +# define RETRY_TKILL_ON_EAGAIN +# if !defined(MIPS) && !defined(POWERPC) +# define LINUX_STACKBOTTOM +# endif +# if defined(__ELF__) && !defined(IA64) +# define DYNAMIC_LOADING +# endif +# if defined(__ELF__) && !defined(ARC) && !defined(RISCV) \ + && !defined(S390) && !defined(TILEGX) && !defined(TILEPRO) + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# endif +# if !defined(REDIRECT_MALLOC) && !defined(E2K) + /* Requires Linux 2.3.47 or later. */ +# define MPROTECT_VDB +# else + /* We seem to get random errors in the incremental mode, */ + /* possibly because the Linux threads implementation */ + /* itself is a malloc client and cannot deal with the */ + /* signals. fread() uses malloc too. */ + /* In case of e2k, unless -fsemi-spec-ld (or -O0) option */ + /* is passed to gcc (both when compiling libgc and the */ + /* client), a semi-speculative optimization may lead to */ + /* SIGILL (with ILL_ILLOPN si_code) instead of SIGSEGV. */ +# endif +# endif /* LINUX */ + +# ifdef KOS +# define OS_TYPE "KOS" +# define HEURISTIC1 /* relies on pthread_attr_getstack actually */ +# ifndef USE_GET_STACKBASE_FOR_MAIN + /* Note: this requires -lpthread option. */ +# define USE_GET_STACKBASE_FOR_MAIN +# endif + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) +# endif /* KOS */ + +# ifdef MACOS +# define OS_TYPE "MACOS" +# ifndef __LOWMEM__ + EXTERN_C_END +# include + EXTERN_C_BEGIN +# endif + /* See os_dep.c for details of global data segments. */ +# define STACKBOTTOM ((ptr_t)LMGetCurStackBase()) +# define DATAEND /* not needed */ +# endif /* MACOS */ + +# ifdef MSWIN32 +# define OS_TYPE "MSWIN32" + /* STACKBOTTOM and DATASTART are handled specially in os_dep.c. */ +# if !defined(CPPCHECK) +# define DATAEND /* not needed */ +# endif +# define GWW_VDB +# endif + +# ifdef MSWINCE +# define OS_TYPE "MSWINCE" +# if !defined(CPPCHECK) +# define DATAEND /* not needed */ +# endif +# endif + +# ifdef NACL +# define OS_TYPE "NACL" +# if defined(__GLIBC__) +# define DYNAMIC_LOADING +# endif +# define DATASTART ((ptr_t)0x10020000) + extern int _end[]; +# define DATAEND ((ptr_t)_end) +# undef STACK_GRAN +# define STACK_GRAN 0x10000 +# define HEURISTIC1 +# define NO_PTHREAD_GETATTR_NP +# define USE_MMAP_ANON +# define GETPAGESIZE() 65536 /* FIXME: Not real page size */ +# define MAX_NACL_GC_THREADS 1024 +# endif /* NACL */ + +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# define HEURISTIC2 +# ifdef __ELF__ +# define SEARCH_FOR_DATA_START +# define DYNAMIC_LOADING +# elif !defined(MIPS) /* TODO: probably do not exclude it */ + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# endif +# endif /* NETBSD */ + +# ifdef NEXT +# define OS_TYPE "NEXT" +# define DATASTART ((ptr_t)get_etext()) +# define DATASTART_IS_FUNC +# define DATAEND /* not needed */ +# endif + +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# ifndef GC_OPENBSD_THREADS +# define HEURISTIC2 +# endif +# ifdef __ELF__ + extern int __data_start[]; +# define DATASTART ((ptr_t)__data_start) + extern int _end[]; +# define DATAEND ((ptr_t)(&_end)) +# define DYNAMIC_LOADING +# else + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# endif +# define MPROTECT_VDB +# endif /* OPENBSD */ + +# ifdef QNX +# define OS_TYPE "QNX" +# define SA_RESTART 0 +# ifndef QNX_STACKBOTTOM /* TODO: not the default one for now */ +# define HEURISTIC1 +# endif + extern char etext[]; +# define DATASTART ((ptr_t)etext) + extern int _end[]; +# define DATAEND ((ptr_t)_end) +# endif /* QNX */ + +# ifdef SOLARIS +# define OS_TYPE "SOLARIS" + extern int _etext[], _end[]; + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# define DATASTART_IS_FUNC +# define DATAEND ((ptr_t)(_end)) +# if !defined(USE_MMAP) && defined(REDIRECT_MALLOC) +# define USE_MMAP 1 + /* Otherwise we now use calloc. Mmap may result in the */ + /* heap interleaved with thread stacks, which can result in */ + /* excessive blacklisting. Sbrk is unusable since it */ + /* doesn't interact correctly with the system malloc. */ +# endif +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x40000000 +# else +# define HEAP_START DATAEND +# endif +# ifndef GC_THREADS +# define MPROTECT_VDB +# endif +# define DYNAMIC_LOADING + /* Define STACKBOTTOM as (ptr_t)_start worked through 2.7, */ + /* but reportedly breaks under 2.8. It appears that the stack */ + /* base is a property of the executable, so this should not */ + /* break old executables. */ + /* HEURISTIC1 reportedly no longer works under Solaris 2.7. */ + /* HEURISTIC2 probably works, but this appears to be preferable.*/ + /* Apparently USRSTACK is defined to be USERLIMIT, but in some */ + /* installations that's undefined. We work around this with a */ + /* gross hack: */ + EXTERN_C_END +# include + EXTERN_C_BEGIN +# ifdef USERLIMIT + /* This should work everywhere, but doesn't. */ +# define STACKBOTTOM ((ptr_t)USRSTACK) +# else +# define HEURISTIC2 +# endif +# endif /* SOLARIS */ + +# ifdef SYMBIAN +# define OS_TYPE "SYMBIAN" +# define DATASTART (ptr_t)ALIGNMENT /* cannot be null */ +# define DATAEND (ptr_t)ALIGNMENT +# ifndef USE_MMAP + /* sbrk() is not available. */ +# define USE_MMAP 1 +# endif +# endif /* SYMBIAN */ + +# ifdef M68K +# define MACH_TYPE "M68K" +# define CPP_WORDSZ 32 +# define ALIGNMENT 2 +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# ifdef NETBSD + /* Nothing specific. */ +# endif +# ifdef LINUX +# ifdef __ELF__ +# if GC_GLIBC_PREREQ(2, 0) +# define SEARCH_FOR_DATA_START +# else + extern char **__environ; +# define DATASTART ((ptr_t)(&__environ)) + /* hideous kludge: __environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ + /* We could use _etext instead, but that */ + /* would include .rodata, which may */ + /* contain large read-only data tables */ + /* that we'd rather not scan. */ +# endif +# else + extern int etext[]; +# define DATASTART PTRT_ROUNDUP_BY_MASK(etext, 0xfff) +# endif +# endif +# ifdef AMIGA +# define OS_TYPE "AMIGA" + /* STACKBOTTOM and DATASTART handled specially */ + /* in os_dep.c */ +# define DATAEND /* not needed */ +# define GETPAGESIZE() 4096 +# endif +# ifdef MACOS +# define GETPAGESIZE() 4096 +# endif +# ifdef NEXT +# define STACKBOTTOM ((ptr_t)0x4000000) +# endif +# endif + +# ifdef POWERPC +# define MACH_TYPE "POWERPC" +# ifdef MACOS +# define CPP_WORDSZ 32 +# define ALIGNMENT 2 /* Still necessary? Could it be 4? */ +# endif +# ifdef LINUX +# if defined(__powerpc64__) +# define CPP_WORDSZ 64 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# else +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# endif + /* HEURISTIC1 has been reliably reported to fail for a 32-bit */ + /* executable on a 64-bit kernel. */ +# if defined(__bg__) + /* The Linux Compute Node Kernel (used on BlueGene systems) */ + /* does not support LINUX_STACKBOTTOM way. */ +# define HEURISTIC2 +# define NO_PTHREAD_GETATTR_NP +# else +# define LINUX_STACKBOTTOM +# endif +# define SEARCH_FOR_DATA_START +# ifndef SOFT_VDB +# define SOFT_VDB +# endif +# endif +# ifdef DARWIN +# if defined(__ppc64__) +# define CPP_WORDSZ 64 +# define STACKBOTTOM ((ptr_t)0x7fff5fc00000) +# define CACHE_LINE_SIZE 64 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# else +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# define STACKBOTTOM ((ptr_t)0xc0000000) +# endif +# define MPROTECT_VDB +# if defined(USE_PPC_PREFETCH) && defined(__GNUC__) + /* The performance impact of prefetches is untested */ +# define PREFETCH(x) \ + __asm__ __volatile__ ("dcbt 0,%0" : : "r" ((const void *) (x))) +# define GC_PREFETCH_FOR_WRITE(x) \ + __asm__ __volatile__ ("dcbtst 0,%0" : : "r" ((const void *) (x))) +# endif +# endif +# ifdef OPENBSD +# if defined(__powerpc64__) +# define CPP_WORDSZ 64 +# else +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# endif +# endif +# ifdef FREEBSD +# if defined(__powerpc64__) +# define CPP_WORDSZ 64 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# else +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# endif +# endif +# ifdef NETBSD +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# endif +# ifdef SN_TARGET_PS3 +# define OS_TYPE "SN_TARGET_PS3" +# define CPP_WORDSZ 32 +# define NO_GETENV + extern int _end[]; + extern int __bss_start; +# define DATASTART ((ptr_t)(__bss_start)) +# define DATAEND ((ptr_t)(_end)) +# define STACKBOTTOM ((ptr_t)ps3_get_stack_bottom()) +# define NO_PTHREAD_TRYLOCK + /* Current LOCK() implementation for PS3 explicitly */ + /* use pthread_mutex_lock for some reason. */ +# endif +# ifdef AIX +# define OS_TYPE "AIX" +# undef ALIGNMENT /* in case it's defined */ +# undef IA64 + /* DOB: some AIX installs stupidly define IA64 in */ + /* /usr/include/sys/systemcfg.h */ +# ifdef __64BIT__ +# define CPP_WORDSZ 64 +# define STACKBOTTOM ((ptr_t)0x1000000000000000) +# else +# define CPP_WORDSZ 32 +# define STACKBOTTOM ((ptr_t)((ulong)&errno)) +# endif +# define USE_MMAP_ANON + /* From AIX linker man page: + _text Specifies the first location of the program. + _etext Specifies the first location after the program. + _data Specifies the first location of the data. + _edata Specifies the first location after the initialized data + _end or end Specifies the first location after all data. + */ + extern int _data[], _end[]; +# define DATASTART ((ptr_t)((ulong)_data)) +# define DATAEND ((ptr_t)((ulong)_end)) + extern int errno; +# define MPROTECT_VDB +# define DYNAMIC_LOADING + /* For really old versions of AIX, this may have to be removed. */ +# endif +# ifdef NOSYS +# define OS_TYPE "NOSYS" +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 + extern void __end[], __dso_handle[]; +# define DATASTART ((ptr_t)__dso_handle) /* OK, that's ugly. */ +# define DATAEND ((ptr_t)(__end)) + /* Stack starts at 0xE0000000 for the simulator. */ +# undef STACK_GRAN +# define STACK_GRAN 0x10000000 +# define HEURISTIC1 +# endif +# endif /* POWERPC */ + +# ifdef VAX +# define MACH_TYPE "VAX" +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 /* Pointers are longword aligned by 4.2 C compiler */ + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# ifdef BSD +# define OS_TYPE "BSD" +# define HEURISTIC1 + /* HEURISTIC2 may be OK, but it's hard to test. */ +# endif +# ifdef ULTRIX +# define OS_TYPE "ULTRIX" +# define STACKBOTTOM ((ptr_t)0x7fffc800) +# endif +# endif /* VAX */ + +# ifdef SPARC +# define MACH_TYPE "SPARC" +# if defined(__arch64__) || defined(__sparcv9) +# define CPP_WORDSZ 64 +# define ELF_CLASS ELFCLASS64 +# else +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 /* Required by hardware */ +# endif + /* Don't define USE_ASM_PUSH_REGS. We do use an asm helper, but */ + /* not to push the registers on the mark stack. */ +# ifdef SOLARIS +# define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)_etext) +# define PROC_VDB +# define GETPAGESIZE() (unsigned)sysconf(_SC_PAGESIZE) + /* getpagesize() appeared to be missing from at least */ + /* one Solaris 5.4 installation. Weird. */ +# endif +# ifdef DRSNX +# define OS_TYPE "DRSNX" + extern int etext[]; + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)etext) +# define DATASTART_IS_FUNC +# define MPROTECT_VDB +# define STACKBOTTOM ((ptr_t)0xdfff0000) +# define DYNAMIC_LOADING +# endif +# ifdef LINUX +# if !defined(__ELF__) && !defined(CPPCHECK) +# error Linux SPARC a.out not supported +# endif +# define SVR4 + extern int _etext[]; + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# ifdef __arch64__ +# define DATASTART GC_SysVGetDataStart(0x100000, (ptr_t)_etext) +# else +# define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)_etext) +# endif +# define DATASTART_IS_FUNC +# endif +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# ifdef NETBSD + /* Nothing specific. */ +# endif +# ifdef FREEBSD + extern char etext[]; + extern char edata[]; +# if !defined(CPPCHECK) + extern char end[]; +# endif +# define NEED_FIND_LIMIT +# define DATASTART ((ptr_t)(&etext)) + void * GC_find_limit(void *, int); +# define DATAEND (ptr_t)GC_find_limit(DATASTART, TRUE) +# define DATAEND_IS_FUNC +# define GC_HAVE_DATAREGION2 +# define DATASTART2 ((ptr_t)(&edata)) +# define DATAEND2 ((ptr_t)(&end)) +# endif +# endif /* SPARC */ + +# ifdef I386 +# define MACH_TYPE "I386" +# if (defined(__LP64__) || defined(_WIN64)) && !defined(CPPCHECK) +# error This should be handled as X86_64 +# else +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 + /* Appears to hold for all "32 bit" compilers */ + /* except Borland. The -a4 option fixes */ + /* Borland. For Watcom the option is -zp4. */ +# endif +# ifdef SEQUENT +# define OS_TYPE "SEQUENT" + extern int etext[]; +# define DATASTART PTRT_ROUNDUP_BY_MASK(etext, 0xfff) +# define STACKBOTTOM ((ptr_t)0x3ffff000) +# endif +# ifdef HAIKU + extern int etext[]; +# define DATASTART PTRT_ROUNDUP_BY_MASK(etext, 0xfff) +# endif +# ifdef HURD + /* Nothing specific. */ +# endif +# ifdef EMBOX + /* Nothing specific. */ +# endif +# ifdef NACL + /* Nothing specific. */ +# endif +# ifdef QNX + /* Nothing specific. */ +# endif +# ifdef SOLARIS +# define DATASTART GC_SysVGetDataStart(0x1000, (ptr_t)_etext) + /* At least in Solaris 2.5, PROC_VDB gives wrong values for */ + /* dirty bits. It appears to be fixed in 2.8 and 2.9. */ +# ifdef SOLARIS25_PROC_VDB_BUG_FIXED +# define PROC_VDB +# endif +# endif +# ifdef SCO +# define OS_TYPE "SCO" + extern int etext[]; +# define DATASTART (PTRT_ROUNDUP_BY_MASK(etext, 0x3fffff) \ + + ((word)(etext) & 0xfff)) +# define STACKBOTTOM ((ptr_t)0x7ffffffc) +# endif +# ifdef SCO_ELF +# define OS_TYPE "SCO_ELF" + extern int etext[]; +# define DATASTART ((ptr_t)(etext)) +# define STACKBOTTOM ((ptr_t)0x08048000) +# define DYNAMIC_LOADING +# define ELF_CLASS ELFCLASS32 +# endif +# ifdef DGUX +# define OS_TYPE "DGUX" + extern int _etext, _end; + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# define DATASTART GC_SysVGetDataStart(0x1000, (ptr_t)(&_etext)) +# define DATASTART_IS_FUNC +# define DATAEND ((ptr_t)(&_end)) +# define HEURISTIC2 +# define DYNAMIC_LOADING +# ifndef USE_MMAP +# define USE_MMAP 1 +# endif +# define MAP_FAILED (void *) ((word)-1) +# define HEAP_START (ptr_t)0x40000000 +# endif /* DGUX */ +# ifdef LINUX +# define HEAP_START (ptr_t)0x1000 + /* This encourages mmap to give us low addresses, */ + /* thus allowing the heap to grow to ~3 GB. */ +# ifdef __ELF__ +# if GC_GLIBC_PREREQ(2, 0) || defined(HOST_ANDROID) +# define SEARCH_FOR_DATA_START +# else + extern char **__environ; +# define DATASTART ((ptr_t)(&__environ)) + /* hideous kludge: __environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ + /* We could use _etext instead, but that */ + /* would include .rodata, which may */ + /* contain large read-only data tables */ + /* that we'd rather not scan. */ +# endif +# if !defined(GC_NO_SIGSETJMP) && (defined(HOST_TIZEN) \ + || (defined(HOST_ANDROID) \ + && !(GC_GNUC_PREREQ(4, 8) || GC_CLANG_PREREQ(3, 2) \ + || __ANDROID_API__ >= 18))) + /* Older Android NDK releases lack sigsetjmp in x86 libc */ + /* (setjmp is used instead to find data_start). The bug */ + /* is fixed in Android NDK r8e (so, ok to use sigsetjmp */ + /* if gcc4.8+, clang3.2+ or Android API level 18+). */ +# define GC_NO_SIGSETJMP 1 +# endif +# else + extern int etext[]; +# define DATASTART PTRT_ROUNDUP_BY_MASK(etext, 0xfff) +# endif +# ifdef USE_I686_PREFETCH +# define PREFETCH(x) \ + __asm__ __volatile__ ("prefetchnta %0" : : "m"(*(char *)(x))) + /* Empirically prefetcht0 is much more effective at reducing */ + /* cache miss stalls for the targeted load instructions. But it */ + /* seems to interfere enough with other cache traffic that the */ + /* net result is worse than prefetchnta. */ +# ifdef FORCE_WRITE_PREFETCH + /* Using prefetches for write seems to have a slight negative */ + /* impact on performance, at least for a PIII/500. */ +# define GC_PREFETCH_FOR_WRITE(x) \ + __asm__ __volatile__ ("prefetcht0 %0" : : "m"(*(char *)(x))) +# else +# define GC_NO_PREFETCH_FOR_WRITE +# endif +# elif defined(USE_3DNOW_PREFETCH) +# define PREFETCH(x) \ + __asm__ __volatile__ ("prefetch %0" : : "m"(*(char *)(x))) +# define GC_PREFETCH_FOR_WRITE(x) \ + __asm__ __volatile__ ("prefetchw %0" : : "m"(*(char *)(x))) +# endif +# if defined(__GLIBC__) && !defined(__UCLIBC__) \ + && !defined(GLIBC_TSX_BUG_FIXED) + /* Workaround lock elision implementation for some glibc. */ +# define GLIBC_2_19_TSX_BUG + EXTERN_C_END +# include /* for gnu_get_libc_version() */ + EXTERN_C_BEGIN +# endif +# ifndef SOFT_VDB +# define SOFT_VDB +# endif +# endif +# ifdef CYGWIN32 +# define WOW64_THREAD_CONTEXT_WORKAROUND +# define DATASTART ((ptr_t)GC_DATASTART) /* From gc.h */ +# define DATAEND ((ptr_t)GC_DATAEND) +# ifndef USE_WINALLOC +# /* MPROTECT_VDB does not work, it leads to a spurious exit. */ +# endif +# endif +# ifdef INTERIX +# define OS_TYPE "INTERIX" + extern int _data_start__[]; + extern int _bss_end__[]; +# define DATASTART ((ptr_t)_data_start__) +# define DATAEND ((ptr_t)_bss_end__) +# define STACKBOTTOM ({ ptr_t rv; \ + __asm__ __volatile__ ("movl %%fs:4, %%eax" \ + : "=a" (rv)); \ + rv; }) +# define USE_MMAP_ANON +# endif +# ifdef OS2 +# define OS_TYPE "OS2" + /* STACKBOTTOM and DATASTART are handled specially in */ + /* os_dep.c. OS2 actually has the right */ + /* system call! */ +# define DATAEND /* not needed */ +# define GETPAGESIZE() os2_getpagesize() +# endif +# ifdef MSWIN32 +# define WOW64_THREAD_CONTEXT_WORKAROUND +# define RETRY_GET_THREAD_CONTEXT +# define MPROTECT_VDB +# endif +# ifdef MSWINCE + /* Nothing specific. */ +# endif +# ifdef DJGPP +# define OS_TYPE "DJGPP" + EXTERN_C_END +# include "stubinfo.h" + EXTERN_C_BEGIN + extern int etext[]; + extern int _stklen; + extern int __djgpp_stack_limit; +# define DATASTART PTRT_ROUNDUP_BY_MASK(etext, 0x1ff) +/* #define STACKBOTTOM ((ptr_t)((word)_stubinfo+_stubinfo->size+_stklen)) */ +# define STACKBOTTOM ((ptr_t)((word)__djgpp_stack_limit + _stklen)) + /* This may not be right. */ +# endif +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# ifdef FREEBSD +# if defined(__GLIBC__) + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# endif +# endif +# ifdef NETBSD + /* Nothing specific. */ +# endif +# ifdef THREE86BSD +# define OS_TYPE "THREE86BSD" +# define HEURISTIC2 + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# endif +# ifdef BSDI +# define OS_TYPE "BSDI" +# define HEURISTIC2 + extern char etext[]; +# define DATASTART ((ptr_t)(etext)) +# endif +# ifdef NEXT +# define STACKBOTTOM ((ptr_t)0xc0000000) +# endif +# ifdef RTEMS +# define OS_TYPE "RTEMS" + EXTERN_C_END +# include + EXTERN_C_BEGIN + extern int etext[]; + void *rtems_get_stack_bottom(void); +# define InitStackBottom rtems_get_stack_bottom() +# define DATASTART ((ptr_t)etext) +# define STACKBOTTOM ((ptr_t)InitStackBottom) +# endif +# ifdef DOS4GW +# define OS_TYPE "DOS4GW" + extern long __nullarea; + extern char _end; + extern char *_STACKTOP; + /* Depending on calling conventions Watcom C either precedes */ + /* or does not precedes with underscore names of C-variables. */ + /* Make sure startup code variables always have the same names. */ +# pragma aux __nullarea "*"; +# pragma aux _end "*"; +# define STACKBOTTOM ((ptr_t)_STACKTOP) + /* confused? me too. */ +# define DATASTART ((ptr_t)(&__nullarea)) +# define DATAEND ((ptr_t)(&_end)) +# endif +# ifdef DARWIN +# define DARWIN_DONT_PARSE_STACK 1 +# define STACKBOTTOM ((ptr_t)0xc0000000) +# define MPROTECT_VDB +# endif +# endif /* I386 */ + +# ifdef NS32K +# define MACH_TYPE "NS32K" +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 + extern char **environ; +# define DATASTART ((ptr_t)(&environ)) + /* hideous kludge: environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ +# define STACKBOTTOM ((ptr_t)0xfffff000) /* for Encore */ +# endif /* NS32K */ + +# ifdef LOONGARCH +# define MACH_TYPE "LoongArch" +# define CPP_WORDSZ _LOONGARCH_SZPTR +# ifdef LINUX +# pragma weak __data_start + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) +# endif +# endif /* LOONGARCH */ + +# ifdef SW_64 +# define MACH_TYPE "SW_64" +# define CPP_WORDSZ 64 +# ifdef LINUX + /* Nothing specific. */ +# endif +# endif /* SW_64 */ + +# ifdef MIPS +# define MACH_TYPE "MIPS" +# ifdef LINUX +# ifdef _MIPS_SZPTR +# define CPP_WORDSZ _MIPS_SZPTR +# else +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# endif +# pragma weak __data_start + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# if GC_GLIBC_PREREQ(2, 2) +# define LINUX_STACKBOTTOM +# else +# define STACKBOTTOM ((ptr_t)0x7fff8000) +# endif +# endif +# ifdef EWS4800 +# define OS_TYPE "EWS4800" +# define HEURISTIC2 +# if defined(_MIPS_SZPTR) && (_MIPS_SZPTR == 64) +# define CPP_WORDSZ _MIPS_SZPTR + extern int _fdata[], _end[]; +# define DATASTART ((ptr_t)_fdata) +# define DATAEND ((ptr_t)_end) +# else +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 + extern int etext[], edata[]; +# if !defined(CPPCHECK) + extern int end[]; +# endif + extern int _DYNAMIC_LINKING[], _gp[]; +# define DATASTART (PTRT_ROUNDUP_BY_MASK(etext, 0x3ffff) \ + + ((word)(etext) & 0xffff)) +# define DATAEND ((ptr_t)(edata)) +# define GC_HAVE_DATAREGION2 +# define DATASTART2 (_DYNAMIC_LINKING \ + ? PTRT_ROUNDUP_BY_MASK((word)_gp + 0x8000, 0x3ffff) \ + : (ptr_t)edata) +# define DATAEND2 ((ptr_t)(end)) +# endif +# endif +# ifdef ULTRIX +# define OS_TYPE "ULTRIX" +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# define HEURISTIC2 +# define DATASTART ((ptr_t)0x10000000) + /* Could probably be slightly higher since */ + /* startup code allocates lots of stuff. */ +# endif +# ifdef IRIX5 +# define OS_TYPE "IRIX5" +# ifdef _MIPS_SZPTR +# define CPP_WORDSZ _MIPS_SZPTR +# else +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# endif +# define HEURISTIC2 + extern int _fdata[]; +# define DATASTART ((ptr_t)(_fdata)) +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x30000000 +# else +# define HEAP_START DATASTART +# endif + /* Lowest plausible heap address. */ + /* In the MMAP case, we map there. */ + /* In either case it is used to identify */ + /* heap sections so they're not */ + /* considered as roots. */ +/*# define MPROTECT_VDB DOB: this should work, but there is evidence */ +/* of recent breakage. */ +# define DYNAMIC_LOADING +# endif +# ifdef MSWINCE +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# endif +# ifdef NETBSD +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# ifndef __ELF__ +# define DATASTART ((ptr_t)0x10000000) +# define STACKBOTTOM ((ptr_t)0x7ffff000) +# endif +# endif +# ifdef OPENBSD +# define CPP_WORDSZ 64 /* all OpenBSD/mips platforms are 64-bit */ +# endif +# ifdef FREEBSD +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# endif +# ifdef NONSTOP +# define OS_TYPE "NONSTOP" +# define CPP_WORDSZ 32 +# define DATASTART ((ptr_t)0x08000000) + extern char **environ; +# define DATAEND ((ptr_t)(environ - 0x10)) +# define STACKBOTTOM ((ptr_t)0x4fffffff) +# endif +# endif /* MIPS */ + +# ifdef NIOS2 +# define MACH_TYPE "NIOS2" +# define CPP_WORDSZ 32 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# ifdef LINUX + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) +# endif +# endif /* NIOS2 */ + +# ifdef OR1K +# define MACH_TYPE "OR1K" +# define CPP_WORDSZ 32 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# ifdef LINUX + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) +# endif +# endif /* OR1K */ + +# ifdef HP_PA +# define MACH_TYPE "HP_PA" +# ifdef __LP64__ +# define CPP_WORDSZ 64 +# else +# define CPP_WORDSZ 32 +# endif +# define STACK_GROWS_UP +# ifdef HPUX +# ifndef GC_THREADS +# define MPROTECT_VDB +# endif +# ifdef USE_HPUX_FIXED_STACKBOTTOM + /* The following appears to work for 7xx systems running HP/UX */ + /* 9.xx. Furthermore, it might result in much faster */ + /* collections than HEURISTIC2, which may involve scanning */ + /* segments that directly precede the stack. It is not the */ + /* default, since it may not work on older machine/OS */ + /* combinations. (Thanks to Raymond X.T. Nijssen for uncovering */ + /* this.) */ + /* This technique also doesn't work with HP/UX 11.xx. The */ + /* stack size is settable using the kernel maxssiz variable, */ + /* and in 11.23 and latter, the size can be set dynamically. */ + /* It also doesn't handle SHMEM_MAGIC binaries which have */ + /* stack and data in the first quadrant. */ +# define STACKBOTTOM ((ptr_t)0x7b033000) /* from /etc/conf/h/param.h */ +# elif defined(USE_ENVIRON_POINTER) + /* Gustavo Rodriguez-Rivera suggested changing HEURISTIC2 */ + /* to this. Note that the GC must be initialized before the */ + /* first putenv call. Unfortunately, some clients do not obey. */ + extern char ** environ; +# define STACKBOTTOM ((ptr_t)environ) +# elif !defined(HEURISTIC2) + /* This uses pst_vm_status support. */ +# define HPUX_MAIN_STACKBOTTOM +# endif +# ifndef __GNUC__ +# define PREFETCH(x) do { \ + register long addr = (long)(x); \ + (void) _asm ("LDW", 0, 0, addr, 0); \ + } while (0) +# endif +# endif /* HPUX */ +# ifdef LINUX +# define SEARCH_FOR_DATA_START +# endif +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# endif /* HP_PA */ + +# ifdef ALPHA +# define MACH_TYPE "ALPHA" +# define CPP_WORDSZ 64 +# ifdef NETBSD +# define ELFCLASS32 32 +# define ELFCLASS64 64 +# define ELF_CLASS ELFCLASS64 +# endif +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# ifdef FREEBSD + /* MPROTECT_VDB is not yet supported at all on FreeBSD/alpha. */ +/* Handle unmapped hole alpha*-*-freebsd[45]* puts between etext and edata. */ + extern char etext[]; + extern char edata[]; +# if !defined(CPPCHECK) + extern char end[]; +# endif +# define NEED_FIND_LIMIT +# define DATASTART ((ptr_t)(&etext)) + void * GC_find_limit(void *, int); +# define DATAEND (ptr_t)GC_find_limit(DATASTART, TRUE) +# define DATAEND_IS_FUNC +# define GC_HAVE_DATAREGION2 +# define DATASTART2 ((ptr_t)(&edata)) +# define DATAEND2 ((ptr_t)(&end)) +# endif +# ifdef OSF1 +# define OS_TYPE "OSF1" +# define DATASTART ((ptr_t)0x140000000) + extern int _end[]; +# define DATAEND ((ptr_t)(&_end)) + extern char ** environ; + /* round up from the value of environ to the nearest page boundary */ + /* Probably breaks if putenv is called before collector */ + /* initialization. */ +# define STACKBOTTOM ((ptr_t)(((word)(environ) | (getpagesize()-1))+1)) +/* #define HEURISTIC2 */ + /* Normally HEURISTIC2 is too conservative, since */ + /* the text segment immediately follows the stack. */ + /* Hence we give an upper pound. */ + /* This is currently unused, since we disabled HEURISTIC2 */ + extern int __start[]; +# define HEURISTIC2_LIMIT ((ptr_t)((word)(__start) \ + & ~(word)(getpagesize()-1))) +# ifndef GC_OSF1_THREADS + /* Unresolved signal issues with threads. */ +# define MPROTECT_VDB +# endif +# define DYNAMIC_LOADING +# endif +# ifdef LINUX +# ifdef __ELF__ +# define SEARCH_FOR_DATA_START +# else +# define DATASTART ((ptr_t)0x140000000) + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# endif +# endif +# endif /* ALPHA */ + +# ifdef IA64 +# define MACH_TYPE "IA64" +# ifdef HPUX +# ifdef _ILP32 +# define CPP_WORDSZ 32 + /* Requires 8 byte alignment for malloc */ +# define ALIGNMENT 4 +# else +# if !defined(_LP64) && !defined(CPPCHECK) +# error Unknown ABI +# endif +# define CPP_WORDSZ 64 + /* Requires 16 byte alignment for malloc */ +# define ALIGNMENT 8 +# endif + /* Note that the GC must be initialized before the 1st putenv call. */ + extern char ** environ; +# define STACKBOTTOM ((ptr_t)environ) +# define HPUX_STACKBOTTOM + /* The following was empirically determined, and is probably */ + /* not very robust. */ + /* Note that the backing store base seems to be at a nice */ + /* address minus one page. */ +# define BACKING_STORE_DISPLACEMENT 0x1000000 +# define BACKING_STORE_ALIGNMENT 0x1000 + /* Known to be wrong for recent HP/UX versions!!! */ +# endif +# ifdef LINUX +# define CPP_WORDSZ 64 + /* The following works on NUE and older kernels: */ + /* define STACKBOTTOM ((ptr_t)0xa000000000000000l) */ + /* TODO: LINUX_STACKBOTTOM does not work on NUE. */ + /* We also need the base address of the register stack */ + /* backing store. */ +# define SEARCH_FOR_DATA_START +# ifdef __GNUC__ +# define DYNAMIC_LOADING +# else + /* In the Intel compiler environment, we seem to end up with */ + /* statically linked executables and an undefined reference */ + /* to _DYNAMIC */ +# endif +# ifdef __GNUC__ +# ifndef __INTEL_COMPILER +# define PREFETCH(x) \ + __asm__ (" lfetch [%0]": : "r"(x)) +# define GC_PREFETCH_FOR_WRITE(x) \ + __asm__ (" lfetch.excl [%0]": : "r"(x)) +# define CLEAR_DOUBLE(x) \ + __asm__ (" stf.spill [%0]=f0": : "r"((void *)(x))) +# else + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define PREFETCH(x) __lfetch(__lfhint_none, (x)) +# define GC_PREFETCH_FOR_WRITE(x) __lfetch(__lfhint_nta, (x)) +# define CLEAR_DOUBLE(x) __stf_spill((void *)(x), 0) +# endif /* __INTEL_COMPILER */ +# endif +# endif +# ifdef MSWIN32 + /* FIXME: This is a very partial guess. There is no port, yet. */ +# if defined(_WIN64) +# define CPP_WORDSZ 64 +# else +# define CPP_WORDSZ 32 /* Is this possible? */ +# define ALIGNMENT 8 +# endif +# endif +# endif /* IA64 */ + +# ifdef E2K +# define MACH_TYPE "E2K" +# ifdef __LP64__ +# define CPP_WORDSZ 64 +# else +# define CPP_WORDSZ 32 +# endif +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# ifdef LINUX + extern int __dso_handle[]; +# define DATASTART ((ptr_t)__dso_handle) +# ifdef REDIRECT_MALLOC +# define NO_PROC_FOR_LIBRARIES +# endif +# endif +# endif /* E2K */ + +# ifdef M88K +# define MACH_TYPE "M88K" +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# define STACKBOTTOM ((char*)0xf0000000) /* determined empirically */ + extern int etext[]; +# ifdef CX_UX +# define OS_TYPE "CX_UX" +# define DATASTART (PTRT_ROUNDUP_BY_MASK(etext, 0x3fffff) + 0x10000) +# endif +# ifdef DGUX +# define OS_TYPE "DGUX" + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)etext) +# define DATASTART_IS_FUNC +# endif +# endif /* M88K */ + +# ifdef S370 + /* If this still works, and if anyone cares, this should probably */ + /* be moved to the S390 category. */ +# define MACH_TYPE "S370" +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 /* Required by hardware */ +# ifdef UTS4 +# define OS_TYPE "UTS4" + extern int _etext[]; + extern int _end[]; + ptr_t GC_SysVGetDataStart(size_t, ptr_t); +# define DATASTART GC_SysVGetDataStart(0x10000, (ptr_t)_etext) +# define DATASTART_IS_FUNC +# define DATAEND ((ptr_t)(_end)) +# define HEURISTIC2 +# endif +# endif /* S370 */ + +# ifdef S390 +# define MACH_TYPE "S390" +# ifndef __s390x__ +# define CPP_WORDSZ 32 +# else +# define CPP_WORDSZ 64 +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# endif +# ifdef LINUX + extern int __data_start[] __attribute__((__weak__)); +# define DATASTART ((ptr_t)(__data_start)) + extern int _end[] __attribute__((__weak__)); +# define DATAEND ((ptr_t)(_end)) +# define CACHE_LINE_SIZE 256 +# define GETPAGESIZE() 4096 +# ifndef SOFT_VDB +# define SOFT_VDB +# endif +# endif +# endif /* S390 */ + +# ifdef AARCH64 +# define MACH_TYPE "AARCH64" +# ifdef __ILP32__ +# define CPP_WORDSZ 32 +# else +# define CPP_WORDSZ 64 +# endif +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# ifdef LINUX +# if defined(HOST_ANDROID) +# define SEARCH_FOR_DATA_START +# else + extern int __data_start[] __attribute__((__weak__)); +# define DATASTART ((ptr_t)__data_start) +# endif +# endif +# ifdef DARWIN + /* OS X, iOS, visionOS */ +# define DARWIN_DONT_PARSE_STACK 1 +# define STACKBOTTOM ((ptr_t)0x16fdfffff) +# if (TARGET_OS_IPHONE || TARGET_OS_XR || TARGET_OS_VISION) + /* MPROTECT_VDB causes use of non-public API like exc_server, */ + /* this could be a reason for blocking the client application */ + /* in the store. */ +# elif TARGET_OS_OSX +# define MPROTECT_VDB +# endif +# endif +# ifdef FREEBSD + /* Nothing specific. */ +# endif +# ifdef NETBSD +# define ELF_CLASS ELFCLASS64 +# endif +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# ifdef NINTENDO_SWITCH +# define OS_TYPE "NINTENDO_SWITCH" + extern int __bss_end[]; +# define NO_HANDLE_FORK 1 +# define DATASTART (ptr_t)ALIGNMENT /* cannot be null */ +# define DATAEND (ptr_t)(&__bss_end) + void *switch_get_stack_bottom(void); +# define STACKBOTTOM ((ptr_t)switch_get_stack_bottom()) +# ifndef HAVE_CLOCK_GETTIME +# define HAVE_CLOCK_GETTIME 1 +# endif +# endif +# ifdef KOS + /* Nothing specific. */ +# endif +# ifdef QNX + /* Nothing specific. */ +# endif +# ifdef MSWIN32 /* UWP */ + /* TODO: Enable MPROTECT_VDB */ +# endif +# ifdef NOSYS +# define OS_TYPE "NOSYS" + /* __data_start is usually defined in the target linker script. */ + extern int __data_start[]; +# define DATASTART ((ptr_t)__data_start) + extern void *__stack_base__; +# define STACKBOTTOM ((ptr_t)__stack_base__) +# endif +# endif /* AARCH64 */ + +# ifdef ARM32 +# if defined(NACL) +# define MACH_TYPE "NACL" +# else +# define MACH_TYPE "ARM32" +# endif +# define CPP_WORDSZ 32 +# ifdef LINUX +# if GC_GLIBC_PREREQ(2, 0) || defined(HOST_ANDROID) +# define SEARCH_FOR_DATA_START +# else + extern char **__environ; +# define DATASTART ((ptr_t)(&__environ)) + /* hideous kludge: __environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ + /* We could use _etext instead, but that */ + /* would include .rodata, which may */ + /* contain large read-only data tables */ + /* that we'd rather not scan. */ +# endif +# endif +# ifdef MSWINCE + /* Nothing specific. */ +# endif +# ifdef FREEBSD + /* Nothing specific. */ +# endif +# ifdef DARWIN + /* iOS */ +# define DARWIN_DONT_PARSE_STACK 1 +# define STACKBOTTOM ((ptr_t)0x30000000) + /* MPROTECT_VDB causes use of non-public API. */ +# endif +# ifdef NACL + /* Nothing specific. */ +# endif +# ifdef NETBSD + /* Nothing specific. */ +# endif +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# ifdef QNX + /* Nothing specific. */ +# endif +# ifdef SN_TARGET_PSP2 +# define OS_TYPE "SN_TARGET_PSP2" +# define NO_HANDLE_FORK 1 +# ifndef HBLKSIZE +# define HBLKSIZE 65536 /* page size is 64 KB */ +# endif +# define DATASTART (ptr_t)ALIGNMENT +# define DATAEND (ptr_t)ALIGNMENT + void *psp2_get_stack_bottom(void); +# define STACKBOTTOM ((ptr_t)psp2_get_stack_bottom()) +# endif +# ifdef NN_PLATFORM_CTR +# define OS_TYPE "NN_PLATFORM_CTR" + extern unsigned char Image$$ZI$$ZI$$Base[]; +# define DATASTART (ptr_t)(Image$$ZI$$ZI$$Base) + extern unsigned char Image$$ZI$$ZI$$Limit[]; +# define DATAEND (ptr_t)(Image$$ZI$$ZI$$Limit) + void *n3ds_get_stack_bottom(void); +# define STACKBOTTOM ((ptr_t)n3ds_get_stack_bottom()) +# endif +# ifdef MSWIN32 /* UWP */ + /* TODO: Enable MPROTECT_VDB */ +# endif +# ifdef NOSYS +# define OS_TYPE "NOSYS" + /* __data_start is usually defined in the target linker script. */ + extern int __data_start[]; +# define DATASTART ((ptr_t)(__data_start)) + /* __stack_base__ is set in newlib/libc/sys/arm/crt0.S */ + extern void *__stack_base__; +# define STACKBOTTOM ((ptr_t)(__stack_base__)) +# endif +# ifdef SYMBIAN + /* Nothing specific. */ +# endif +#endif /* ARM32 */ + +# ifdef CRIS +# define MACH_TYPE "CRIS" +# define CPP_WORDSZ 32 +# define ALIGNMENT 1 +# ifdef LINUX +# define SEARCH_FOR_DATA_START +# endif +# endif /* CRIS */ + +# if defined(SH) && !defined(SH4) +# define MACH_TYPE "SH" +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# ifdef LINUX +# define SEARCH_FOR_DATA_START +# endif +# ifdef NETBSD + /* Nothing specific. */ +# endif +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# ifdef MSWINCE + /* Nothing specific. */ +# endif +# endif + +# ifdef SH4 +# define MACH_TYPE "SH4" +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# ifdef MSWINCE + /* Nothing specific. */ +# endif +# endif /* SH4 */ + +# ifdef AVR32 +# define MACH_TYPE "AVR32" +# define CPP_WORDSZ 32 +# ifdef LINUX +# define SEARCH_FOR_DATA_START +# endif +# endif /* AVR32 */ + +# ifdef M32R +# define MACH_TYPE "M32R" +# define CPP_WORDSZ 32 +# ifdef LINUX +# define SEARCH_FOR_DATA_START +# endif +# endif /* M32R */ + +# ifdef X86_64 +# define MACH_TYPE "X86_64" +# ifdef __ILP32__ +# define CPP_WORDSZ 32 +# else +# define CPP_WORDSZ 64 +# endif +# ifndef HBLKSIZE +# define HBLKSIZE 4096 +# endif +# ifndef CACHE_LINE_SIZE +# define CACHE_LINE_SIZE 64 +# endif +# ifdef PLATFORM_GETMEM +# define OS_TYPE "PLATFORM_GETMEM" +# define DATASTART (ptr_t)ALIGNMENT +# define DATAEND (ptr_t)ALIGNMENT + EXTERN_C_END +# include + EXTERN_C_BEGIN + void *platform_get_stack_bottom(void); +# define STACKBOTTOM ((ptr_t)platform_get_stack_bottom()) +# endif +# ifdef LINUX +# define SEARCH_FOR_DATA_START +# if defined(__GLIBC__) && !defined(__UCLIBC__) + /* A workaround for GCF (Google Cloud Function) which does */ + /* not support mmap() for "/dev/zero". Should not cause any */ + /* harm to other targets. */ +# define USE_MMAP_ANON +# endif +# if defined(__GLIBC__) && !defined(__UCLIBC__) \ + && !defined(GETCONTEXT_FPU_BUG_FIXED) + /* At present, there's a bug in glibc getcontext() on */ + /* Linux/x64 (it clears FPU exception mask). We define this */ + /* macro to workaround it. */ + /* TODO: This seems to be fixed in glibc 2.14. */ +# define GETCONTEXT_FPU_EXCMASK_BUG +# endif +# if defined(__GLIBC__) && !defined(__UCLIBC__) \ + && !defined(GLIBC_TSX_BUG_FIXED) + /* Workaround lock elision implementation for some glibc. */ +# define GLIBC_2_19_TSX_BUG + EXTERN_C_END +# include /* for gnu_get_libc_version() */ + EXTERN_C_BEGIN +# endif +# ifndef SOFT_VDB +# define SOFT_VDB +# endif +# endif +# ifdef DARWIN +# define DARWIN_DONT_PARSE_STACK 1 +# define STACKBOTTOM ((ptr_t)0x7fff5fc00000) +# define MPROTECT_VDB +# endif +# ifdef FREEBSD +# if defined(__GLIBC__) + extern int _end[]; +# define DATAEND ((ptr_t)(_end)) +# endif +# if defined(__DragonFly__) + /* DragonFly BSD still has vm.max_proc_mmap, according to */ + /* its mmap(2) man page. */ +# define COUNT_UNMAPPED_REGIONS +# endif +# endif +# ifdef NETBSD + /* Nothing specific. */ +# endif +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# ifdef HAIKU +# define HEURISTIC2 +# define SEARCH_FOR_DATA_START +# endif +# ifdef HURD + /* Nothing specific. */ +# endif +# ifdef QNX + /* Nothing specific. */ +# endif +# ifdef SOLARIS +# define ELF_CLASS ELFCLASS64 +# define DATASTART GC_SysVGetDataStart(0x1000, (ptr_t)_etext) +# ifdef SOLARIS25_PROC_VDB_BUG_FIXED +# define PROC_VDB +# endif +# endif +# ifdef CYGWIN32 +# ifndef USE_WINALLOC +# if defined(THREAD_LOCAL_ALLOC) + /* TODO: For an unknown reason, thread-local allocations */ + /* lead to spurious process exit after the fault handler is */ + /* once invoked. */ +# else +# define MPROTECT_VDB +# endif +# endif +# endif +# ifdef MSWIN_XBOX1 +# define OS_TYPE "MSWIN_XBOX1" +# define NO_GETENV +# define DATASTART (ptr_t)ALIGNMENT +# define DATAEND (ptr_t)ALIGNMENT + LONG64 durango_get_stack_bottom(void); +# define STACKBOTTOM ((ptr_t)durango_get_stack_bottom()) +# define GETPAGESIZE() 4096 +# ifndef USE_MMAP +# define USE_MMAP 1 +# endif + /* The following is from sys/mman.h: */ +# define PROT_NONE 0 +# define PROT_READ 1 +# define PROT_WRITE 2 +# define PROT_EXEC 4 +# define MAP_PRIVATE 2 +# define MAP_FIXED 0x10 +# define MAP_FAILED ((void *)-1) +# endif +# ifdef MSWIN32 +# define RETRY_GET_THREAD_CONTEXT +# if !defined(__GNUC__) || defined(__INTEL_COMPILER) \ + || (GC_GNUC_PREREQ(4, 7) && !defined(__MINGW64__)) + /* Older GCC and Mingw-w64 (both GCC and Clang) do not */ + /* support SetUnhandledExceptionFilter() properly on x64. */ +# define MPROTECT_VDB +# endif +# endif +# endif /* X86_64 */ + +# ifdef ARC +# define MACH_TYPE "ARC" +# define CPP_WORDSZ 32 +# define CACHE_LINE_SIZE 64 +# ifdef LINUX + extern int __data_start[] __attribute__((__weak__)); +# define DATASTART ((ptr_t)__data_start) +# endif +# endif /* ARC */ + +# ifdef HEXAGON +# define MACH_TYPE "HEXAGON" +# define CPP_WORDSZ 32 +# ifdef LINUX +# if defined(__GLIBC__) +# define SEARCH_FOR_DATA_START +# elif !defined(CPPCHECK) +# error Unknown Hexagon libc configuration +# endif +# endif +# endif /* HEXAGON */ + +# ifdef TILEPRO +# define MACH_TYPE "TILEPro" +# define CPP_WORDSZ 32 +# define PREFETCH(x) __insn_prefetch(x) +# define CACHE_LINE_SIZE 64 +# ifdef LINUX + extern int __data_start[]; +# define DATASTART ((ptr_t)__data_start) +# endif +# endif /* TILEPRO */ + +# ifdef TILEGX +# define MACH_TYPE "TILE-Gx" +# define CPP_WORDSZ (__SIZEOF_POINTER__ * 8) +# if CPP_WORDSZ < 64 +# define CLEAR_DOUBLE(x) (*(long long *)(x) = 0) +# endif +# define PREFETCH(x) __insn_prefetch_l1(x) +# define CACHE_LINE_SIZE 64 +# ifdef LINUX + extern int __data_start[]; +# define DATASTART ((ptr_t)__data_start) +# endif +# endif /* TILEGX */ + +# ifdef RISCV +# define MACH_TYPE "RISC-V" +# define CPP_WORDSZ __riscv_xlen /* 32 or 64 */ +# ifdef FREEBSD + /* Nothing specific. */ +# endif +# ifdef LINUX + extern int __data_start[] __attribute__((__weak__)); +# define DATASTART ((ptr_t)__data_start) +# endif +# ifdef NETBSD + /* Nothing specific. */ +# endif +# ifdef OPENBSD + /* Nothing specific. */ +# endif +# endif /* RISCV */ + +# ifdef WEBASSEMBLY +# define MACH_TYPE "WebAssembly" +# if defined(__wasm64__) && !defined(CPPCHECK) +# error 64-bit WebAssembly is not yet supported +# endif +# define CPP_WORDSZ 32 +# define ALIGNMENT 4 +# ifdef EMSCRIPTEN +# define OS_TYPE "EMSCRIPTEN" +# define DATASTART (ptr_t)ALIGNMENT +# define DATAEND (ptr_t)ALIGNMENT + /* Emscripten does emulate mmap and munmap, but those should */ + /* not be used in the collector, since WebAssembly lacks the */ + /* native support of memory mapping. Use sbrk() instead. */ +# undef USE_MMAP +# undef USE_MUNMAP +# if defined(GC_THREADS) && !defined(CPPCHECK) +# error No threads support yet +# endif +# endif +# ifdef WASI +# define OS_TYPE "WASI" + extern char __global_base, __heap_base; +# define STACKBOTTOM ((ptr_t)&__global_base) +# define DATASTART ((ptr_t)&__global_base) +# define DATAEND ((ptr_t)&__heap_base) +# ifndef GC_NO_SIGSETJMP +# define GC_NO_SIGSETJMP 1 /* no support of signals */ +# endif +# ifndef NO_CLOCK +# define NO_CLOCK 1 /* no support of clock */ +# endif +# undef USE_MMAP /* similar to Emscripten */ +# undef USE_MUNMAP +# if defined(GC_THREADS) && !defined(CPPCHECK) +# error No threads support yet +# endif +# endif +# endif /* WEBASSEMBLY */ + +#if defined(__GLIBC__) && !defined(DONT_USE_LIBC_PRIVATES) + /* Use glibc's stack-end marker. */ +# define USE_LIBC_PRIVATES +#endif + +#ifdef NO_RETRY_GET_THREAD_CONTEXT +# undef RETRY_GET_THREAD_CONTEXT +#endif + +#if defined(LINUX_STACKBOTTOM) && defined(NO_PROC_STAT) \ + && !defined(USE_LIBC_PRIVATES) + /* This combination will fail, since we have no way to get */ + /* the stack bottom. Use HEURISTIC2 instead. */ +# undef LINUX_STACKBOTTOM +# define HEURISTIC2 + /* This may still fail on some architectures like IA64. */ + /* We tried ... */ +#endif + +#if defined(USE_MMAP_ANON) && !defined(USE_MMAP) +# define USE_MMAP 1 +#elif (defined(LINUX) || defined(OPENBSD)) && defined(USE_MMAP) + /* The kernel may do a somewhat better job merging mappings etc. */ + /* with anonymous mappings. */ +# define USE_MMAP_ANON +#endif + +#if defined(GC_LINUX_THREADS) && defined(REDIRECT_MALLOC) \ + && !defined(USE_PROC_FOR_LIBRARIES) && !defined(NO_PROC_FOR_LIBRARIES) + /* Nptl allocates thread stacks with mmap, which is fine. But it */ + /* keeps a cache of thread stacks. Thread stacks contain the */ + /* thread control blocks. These in turn contain a pointer to */ + /* (sizeof(void*) from the beginning of) the dtv for thread-local */ + /* storage, which is calloc allocated. If we don't scan the cached */ + /* thread stacks, we appear to lose the dtv. This tends to */ + /* result in something that looks like a bogus dtv count, which */ + /* tends to result in a memset call on a block that is way too */ + /* large. Sometimes we're lucky and the process just dies ... */ + /* There seems to be a similar issue with some other memory */ + /* allocated by the dynamic loader. */ + /* This should be avoidable by either: */ + /* - Defining USE_PROC_FOR_LIBRARIES here. */ + /* That performs very poorly, precisely because we end up */ + /* scanning cached stacks. */ + /* - Have calloc look at its callers. */ + /* In spite of the fact that it is gross and disgusting. */ + /* In fact neither seems to suffice, probably in part because */ + /* even with USE_PROC_FOR_LIBRARIES, we don't scan parts of stack */ + /* segments that appear to be out of bounds. Thus we actually */ + /* do both, which seems to yield the best results. */ +# define USE_PROC_FOR_LIBRARIES +#endif + +#ifndef OS_TYPE +# define OS_TYPE "" +#endif + +#ifndef DATAEND +# if !defined(CPPCHECK) + extern int end[]; +# endif +# define DATAEND ((ptr_t)(end)) +#endif + +/* Workaround for Android NDK clang 3.5+ (as of NDK r10e) which does */ +/* not provide correct _end symbol. Unfortunately, alternate __end__ */ +/* symbol is provided only by NDK "bfd" linker. */ +#if defined(HOST_ANDROID) && defined(__clang__) \ + && !defined(BROKEN_UUENDUU_SYM) +# undef DATAEND +# pragma weak __end__ + extern int __end__[]; +# define DATAEND (__end__ != 0 ? (ptr_t)__end__ : (ptr_t)_end) +#endif + +#if defined(SOLARIS) || defined(DRSNX) || defined(UTS4) + /* OS has SVR4 generic features. */ + /* Probably others also qualify. */ +# define SVR4 +#endif + +#if defined(CYGWIN32) || defined(MSWIN32) || defined(MSWINCE) +# define ANY_MSWIN +#endif + +#if defined(HAVE_SYS_TYPES_H) \ + || !(defined(AMIGA) || defined(MACOS) || defined(MSWINCE) \ + || defined(OS2) || defined(PCR) || defined(SN_TARGET_ORBIS) \ + || defined(SN_TARGET_PSP2) || defined(__CC_ARM)) + EXTERN_C_END +# include + EXTERN_C_BEGIN +#endif /* HAVE_SYS_TYPES_H */ + +#if defined(HAVE_UNISTD_H) \ + || !(defined(AMIGA) || defined(MACOS) \ + || defined(MSWIN32) || defined(MSWINCE) || defined(MSWIN_XBOX1) \ + || defined(NINTENDO_SWITCH) || defined(NN_PLATFORM_CTR) \ + || defined(OS2) || defined(PCR) || defined(SN_TARGET_ORBIS) \ + || defined(SN_TARGET_PSP2) || defined(__CC_ARM)) + EXTERN_C_END +# include + EXTERN_C_BEGIN +#endif /* HAVE_UNISTD_H */ + +#if !defined(ANY_MSWIN) && !defined(GETPAGESIZE) +# if defined(DGUX) || defined(HOST_ANDROID) || defined(HOST_TIZEN) \ + || defined(KOS) || (defined(LINUX) && defined(SPARC)) +# define GETPAGESIZE() (unsigned)sysconf(_SC_PAGESIZE) +# else +# define GETPAGESIZE() (unsigned)getpagesize() +# endif +#endif /* !ANY_MSWIN && !GETPAGESIZE */ + +#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \ + && ((defined(MIPS) && (CPP_WORDSZ == 32)) \ + || defined(ARM32) || defined(I386) /* but not x32 */) + /* tkill() exists only on arm32/mips(32)/x86. */ + /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */ +# define USE_TKILL_ON_ANDROID +#endif + +#if defined(MPROTECT_VDB) && defined(__GLIBC__) && !GC_GLIBC_PREREQ(2, 2) +# error glibc too old? +#endif + +#if defined(SOLARIS) || defined(DRSNX) + /* OS has SOLARIS style semi-undocumented interface */ + /* to dynamic loader. */ +# define SOLARISDL + /* OS has SOLARIS style signal handlers. */ +# define SUNOS5SIGS +#endif + +#if defined(HPUX) +# define SUNOS5SIGS +#endif + +#if defined(FREEBSD) && (defined(__DragonFly__) || __FreeBSD__ >= 4 \ + || __FreeBSD_kernel__ >= 4 || defined(__GLIBC__)) +# define SUNOS5SIGS +#endif + +#if defined(ANY_BSD) || defined(HAIKU) || defined(HURD) || defined(IRIX5) \ + || defined(OSF1) || defined(SUNOS5SIGS) +# define USE_SEGV_SIGACT +# if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \ + || (defined(FREEBSD) && defined(SUNOS5SIGS)) \ + || defined(HPUX) || defined(HURD) || defined(NETBSD) + /* We may get SIGBUS. */ +# define USE_BUS_SIGACT +# endif +#endif + +#if !defined(GC_EXPLICIT_SIGNALS_UNBLOCK) && defined(SUNOS5SIGS) \ + && !defined(GC_NO_PTHREAD_SIGMASK) +# define GC_EXPLICIT_SIGNALS_UNBLOCK +#endif + +#if !defined(NO_SIGNALS_UNBLOCK_IN_MAIN) && defined(GC_NO_PTHREAD_SIGMASK) +# define NO_SIGNALS_UNBLOCK_IN_MAIN +#endif + +#ifndef PARALLEL_MARK +# undef GC_PTHREADS_PARAMARK /* just in case it is defined by client */ +#elif defined(GC_PTHREADS) && !defined(GC_PTHREADS_PARAMARK) \ + && !defined(__MINGW32__) + /* Use pthread-based parallel mark implementation. */ + /* Except for MinGW 32/64 to workaround a deadlock in */ + /* winpthreads-3.0b internals. */ +# define GC_PTHREADS_PARAMARK +#endif + +#if !defined(NO_MARKER_SPECIAL_SIGMASK) \ + && (defined(NACL) || defined(GC_WIN32_PTHREADS) \ + || (defined(GC_PTHREADS_PARAMARK) && defined(GC_WIN32_THREADS)) \ + || defined(GC_NO_PTHREAD_SIGMASK)) + /* Either there is no pthread_sigmask(), or GC marker thread cannot */ + /* steal and drop user signal calls. */ +# define NO_MARKER_SPECIAL_SIGMASK +#endif + +#ifdef GC_NETBSD_THREADS +# define SIGRTMIN 33 +# define SIGRTMAX 63 + /* It seems to be necessary to wait until threads have restarted. */ + /* But it is unclear why that is the case. */ +# define GC_NETBSD_THREADS_WORKAROUND +#endif + +#ifdef GC_OPENBSD_THREADS + EXTERN_C_END +# include + EXTERN_C_BEGIN +#endif /* GC_OPENBSD_THREADS */ + +#if defined(AIX) || defined(ANY_BSD) || defined(BSD) || defined(DARWIN) \ + || defined(DGUX) || defined(HAIKU) || defined(HPUX) || defined(HURD) \ + || defined(IRIX5) || defined(LINUX) || defined(OSF1) || defined(QNX) \ + || defined(SVR4) +# define UNIX_LIKE /* Basic Unix-like system calls work. */ +#endif + +#if defined(CPPCHECK) +# undef CPP_WORDSZ +# define CPP_WORDSZ (__SIZEOF_POINTER__ * 8) +#elif CPP_WORDSZ != 32 && CPP_WORDSZ != 64 +# error Bad word size +#endif + +#ifndef ALIGNMENT +# if !defined(CPP_WORDSZ) && !defined(CPPCHECK) +# error Undefined both ALIGNMENT and CPP_WORDSZ +# endif +# define ALIGNMENT (CPP_WORDSZ >> 3) +#endif /* !ALIGNMENT */ + +#ifdef PCR +# undef DYNAMIC_LOADING +# undef STACKBOTTOM +# undef HEURISTIC1 +# undef HEURISTIC2 +# undef PROC_VDB +# undef MPROTECT_VDB +# define PCR_VDB +#endif + +#if !defined(STACKBOTTOM) && (defined(ECOS) || defined(NOSYS)) \ + && !defined(CPPCHECK) +# error Undefined STACKBOTTOM +#endif + +#ifdef IGNORE_DYNAMIC_LOADING +# undef DYNAMIC_LOADING +#endif + +#if defined(SMALL_CONFIG) && !defined(GC_DISABLE_INCREMENTAL) + /* Presumably not worth the space it takes. */ +# define GC_DISABLE_INCREMENTAL +#endif + +#if (defined(MSWIN32) || defined(MSWINCE)) && !defined(USE_WINALLOC) + /* USE_WINALLOC is only an option for Cygwin. */ +# define USE_WINALLOC 1 +#endif + +#ifdef USE_WINALLOC +# undef USE_MMAP +#endif + +#if defined(ANY_BSD) || defined(DARWIN) || defined(HAIKU) \ + || defined(IRIX5) || defined(LINUX) || defined(SOLARIS) \ + || ((defined(CYGWIN32) || defined(USE_MMAP) || defined(USE_MUNMAP)) \ + && !defined(USE_WINALLOC)) + /* Try both sbrk and mmap, in that order. */ +# define MMAP_SUPPORTED +#endif + +/* Xbox One (DURANGO) may not need to be this aggressive, but the */ +/* default is likely too lax under heavy allocation pressure. */ +/* The platform does not have a virtual paging system, so it does not */ +/* have a large virtual address space that a standard x64 platform has. */ +#if defined(USE_MUNMAP) && !defined(MUNMAP_THRESHOLD) \ + && (defined(SN_TARGET_PS3) \ + || defined(SN_TARGET_PSP2) || defined(MSWIN_XBOX1)) +# define MUNMAP_THRESHOLD 3 +#endif + +#if defined(USE_MUNMAP) && defined(COUNT_UNMAPPED_REGIONS) \ + && !defined(GC_UNMAPPED_REGIONS_SOFT_LIMIT) + /* The default limit of vm.max_map_count on Linux is ~65530. */ + /* There is approximately one mapped region to every unmapped region. */ + /* Therefore if we aim to use up to half of vm.max_map_count for the */ + /* GC (leaving half for the rest of the process) then the number of */ + /* unmapped regions should be one quarter of vm.max_map_count. */ +# if defined(__DragonFly__) +# define GC_UNMAPPED_REGIONS_SOFT_LIMIT (1000000 / 4) +# else +# define GC_UNMAPPED_REGIONS_SOFT_LIMIT 16384 +# endif +#endif + +#if defined(GC_DISABLE_INCREMENTAL) || defined(DEFAULT_VDB) +# undef GWW_VDB +# undef MPROTECT_VDB +# undef PCR_VDB +# undef PROC_VDB +# undef SOFT_VDB +#endif + +#ifdef NO_GWW_VDB +# undef GWW_VDB +#endif + +#ifdef NO_MPROTECT_VDB +# undef MPROTECT_VDB +#endif + +#ifdef NO_SOFT_VDB +# undef SOFT_VDB +#endif + +#if defined(SOFT_VDB) && defined(SOFT_VDB_LINUX_VER_STATIC_CHECK) + EXTERN_C_END +# include /* for LINUX_VERSION[_CODE] */ + EXTERN_C_BEGIN +# if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) + /* Not reliable in kernels prior to v3.18. */ +# undef SOFT_VDB +# endif +#endif /* SOFT_VDB */ + +#ifdef GC_DISABLE_INCREMENTAL +# undef CHECKSUMS +#endif + +#ifdef USE_GLOBAL_ALLOC + /* Cannot pass MEM_WRITE_WATCH to GlobalAlloc(). */ +# undef GWW_VDB +#endif + +#if defined(BASE_ATOMIC_OPS_EMULATED) + /* GC_write_fault_handler() cannot use lock-based atomic primitives */ + /* as this could lead to a deadlock. */ +# undef MPROTECT_VDB +#endif + +#if defined(USE_PROC_FOR_LIBRARIES) && defined(GC_LINUX_THREADS) + /* Incremental GC based on mprotect is incompatible with /proc roots. */ +# undef MPROTECT_VDB +#endif + +#if defined(MPROTECT_VDB) && defined(GC_PREFER_MPROTECT_VDB) + /* Choose MPROTECT_VDB manually (if multiple strategies available). */ +# undef PCR_VDB +# undef PROC_VDB + /* GWW_VDB, SOFT_VDB are handled in os_dep.c. */ +#endif + +#ifdef PROC_VDB + /* Mutually exclusive VDB implementations (for now). */ +# undef MPROTECT_VDB + /* For a test purpose only. */ +# undef SOFT_VDB +#endif + +#if defined(MPROTECT_VDB) && !defined(MSWIN32) && !defined(MSWINCE) + EXTERN_C_END +# include /* for SA_SIGINFO, SIGBUS */ + EXTERN_C_BEGIN +#endif + +#if defined(SIGBUS) && !defined(HAVE_SIGBUS) && !defined(CPPCHECK) +# define HAVE_SIGBUS +#endif + +#ifndef SA_SIGINFO +# define NO_SA_SIGACTION +#endif + +#if (defined(NO_SA_SIGACTION) || defined(GC_NO_SIGSETJMP)) \ + && defined(MPROTECT_VDB) && !defined(DARWIN) \ + && !defined(MSWIN32) && !defined(MSWINCE) +# undef MPROTECT_VDB +#endif + +#if !defined(PCR_VDB) && !defined(PROC_VDB) && !defined(MPROTECT_VDB) \ + && !defined(GWW_VDB) && !defined(SOFT_VDB) && !defined(DEFAULT_VDB) \ + && !defined(GC_DISABLE_INCREMENTAL) +# define DEFAULT_VDB +#endif + +#if defined(CHECK_SOFT_VDB) && !defined(CPPCHECK) \ + && (defined(GC_PREFER_MPROTECT_VDB) \ + || !defined(SOFT_VDB) || !defined(MPROTECT_VDB)) +# error Invalid config for CHECK_SOFT_VDB +#endif + +#if (defined(GC_DISABLE_INCREMENTAL) || defined(BASE_ATOMIC_OPS_EMULATED) \ + || defined(REDIRECT_MALLOC) || defined(SMALL_CONFIG) \ + || defined(REDIRECT_MALLOC_IN_HEADER) || defined(CHECKSUMS)) \ + && !defined(NO_MANUAL_VDB) + /* TODO: Implement CHECKSUMS for manual VDB. */ +# define NO_MANUAL_VDB +#endif + +#if !defined(PROC_VDB) && !defined(SOFT_VDB) \ + && !defined(NO_VDB_FOR_STATIC_ROOTS) + /* Cannot determine whether a static root page is dirty? */ +# define NO_VDB_FOR_STATIC_ROOTS +#endif + +#if ((defined(UNIX_LIKE) && (defined(DARWIN) || defined(HAIKU) \ + || defined(HURD) || defined(OPENBSD) \ + || defined(QNX) || defined(ARM32) \ + || defined(AVR32) || defined(MIPS) \ + || defined(NIOS2) || defined(OR1K))) \ + || (defined(LINUX) && !defined(__gnu_linux__)) \ + || (defined(RTEMS) && defined(I386)) || defined(HOST_ANDROID)) \ + && !defined(NO_GETCONTEXT) +# define NO_GETCONTEXT 1 +#endif + +#if defined(MSWIN32) && !defined(CONSOLE_LOG) && defined(_MSC_VER) \ + && defined(_DEBUG) && !defined(NO_CRT) + /* This should be included before intrin.h to workaround some bug */ + /* in Windows Kit (as of 10.0.17763) headers causing redefinition */ + /* of _malloca macro. */ + EXTERN_C_END +# include /* for _CrtDbgReport */ + EXTERN_C_BEGIN +#endif + +#ifndef PREFETCH +# if (GC_GNUC_PREREQ(3, 0) || defined(__clang__)) && !defined(NO_PREFETCH) +# define PREFETCH(x) __builtin_prefetch((x), 0, 0) +# elif defined(_MSC_VER) && !defined(NO_PREFETCH) \ + && (defined(_M_IX86) || defined(_M_X64)) && !defined(_CHPE_ONLY_) \ + && (_MSC_VER >= 1900) /* VS 2015+ */ + EXTERN_C_END +# include + EXTERN_C_BEGIN +# define PREFETCH(x) _mm_prefetch((const char *)(x), _MM_HINT_T0) + /* TODO: Support also _M_ARM and _M_ARM64 (__prefetch). */ +# else +# define PREFETCH(x) (void)0 +# endif +#endif /* !PREFETCH */ + +#ifndef GC_PREFETCH_FOR_WRITE + /* The default GC_PREFETCH_FOR_WRITE(x) is defined in gc_inline.h, */ + /* the later one is included from gc_priv.h. */ +#endif + +#ifndef CACHE_LINE_SIZE +# define CACHE_LINE_SIZE 32 /* Wild guess */ +#endif + +#ifndef STATIC +# ifdef GC_ASSERTIONS +# define STATIC /* ignore to aid debugging (or profiling) */ +# else +# define STATIC static +# endif +#endif + +#if defined(AMIGA) || defined(DOS4GW) || defined(EMBOX) || defined(KOS) \ + || defined(MACOS) || defined(NINTENDO_SWITCH) || defined(NONSTOP) \ + || defined(OS2) || defined(PCR) || defined(RTEMS) \ + || defined(SN_TARGET_ORBIS) || defined(SN_TARGET_PS3) \ + || defined(SN_TARGET_PSP2) || defined(USE_WINALLOC) || defined(__CC_ARM) +# define NO_UNIX_GET_MEM +#endif + +/* Do we need the GC_find_limit machinery to find the end of */ +/* a data segment (or the backing store base)? */ +#if defined(HEURISTIC2) || defined(SEARCH_FOR_DATA_START) \ + || defined(HPUX_MAIN_STACKBOTTOM) || defined(IA64) \ + || (defined(CYGWIN32) && defined(I386) && defined(USE_MMAP) \ + && !defined(USE_WINALLOC)) \ + || (defined(NETBSD) && defined(__ELF__)) || defined(OPENBSD) \ + || ((defined(SVR4) || defined(AIX) || defined(DGUX) \ + || defined(DATASTART_USES_BSDGETDATASTART)) && !defined(PCR)) +# define NEED_FIND_LIMIT +#endif + +#if defined(LINUX) && (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64) \ + || !defined(SMALL_CONFIG)) +# define NEED_PROC_MAPS +#endif + +#if defined(LINUX) || defined(HURD) || defined(__GLIBC__) +# define REGISTER_LIBRARIES_EARLY + /* We sometimes use dl_iterate_phdr, which may acquire an internal */ + /* lock. This isn't safe after the world has stopped. So we must */ + /* call GC_register_dynamic_libraries before stopping the world. */ + /* For performance reasons, this may be beneficial on other */ + /* platforms as well, though it should be avoided on Windows. */ +#endif /* LINUX */ + +#if defined(SEARCH_FOR_DATA_START) + extern ptr_t GC_data_start; +# define DATASTART GC_data_start +#endif + +#ifndef HEAP_START +# define HEAP_START ((ptr_t)0) +#endif + +#ifndef CLEAR_DOUBLE +# define CLEAR_DOUBLE(x) (((word*)(x))[0] = 0, ((word*)(x))[1] = 0) +#endif + +/* Some libc implementations like bionic, musl and glibc 2.34 */ +/* do not have libpthread.so because the pthreads-related code */ +/* is located in libc.so, thus potential calloc calls from such */ +/* code are forwarded to real (libc) calloc without any special */ +/* handling on the libgc side. Checking glibc version at */ +/* compile time for the purpose seems to be fine. */ +#if defined(GC_LINUX_THREADS) && defined(REDIRECT_MALLOC) \ + && defined(__GLIBC__) && !GC_GLIBC_PREREQ(2, 34) \ + && !defined(HAVE_LIBPTHREAD_SO) +# define HAVE_LIBPTHREAD_SO +#endif + +#if defined(GC_LINUX_THREADS) && defined(REDIRECT_MALLOC) \ + && !defined(INCLUDE_LINUX_THREAD_DESCR) + /* Will not work, since libc and the dynamic loader use thread */ + /* locals, sometimes as the only reference. */ +# define INCLUDE_LINUX_THREAD_DESCR +#endif + +#if !defined(CPPCHECK) +# if defined(GC_IRIX_THREADS) && !defined(IRIX5) +# error Inconsistent configuration +# endif +# if defined(GC_LINUX_THREADS) && !defined(LINUX) && !defined(NACL) +# error Inconsistent configuration +# endif +# if defined(GC_NETBSD_THREADS) && !defined(NETBSD) +# error Inconsistent configuration +# endif +# if defined(GC_FREEBSD_THREADS) && !defined(FREEBSD) +# error Inconsistent configuration +# endif +# if defined(GC_SOLARIS_THREADS) && !defined(SOLARIS) +# error Inconsistent configuration +# endif +# if defined(GC_HPUX_THREADS) && !defined(HPUX) +# error Inconsistent configuration +# endif +# if defined(GC_AIX_THREADS) && !defined(_AIX) +# error Inconsistent configuration +# endif +# if defined(GC_WIN32_THREADS) && !defined(ANY_MSWIN) && !defined(MSWIN_XBOX1) +# error Inconsistent configuration +# endif +# if defined(GC_WIN32_PTHREADS) && defined(CYGWIN32) +# error Inconsistent configuration +# endif +#endif /* !CPPCHECK */ + +#if defined(PCR) || defined(GC_WIN32_THREADS) || defined(GC_PTHREADS) \ + || ((defined(NN_PLATFORM_CTR) || defined(NINTENDO_SWITCH) \ + || defined(SN_TARGET_PS3) \ + || defined(SN_TARGET_PSP2)) && defined(GC_THREADS)) +# define THREADS +#endif + +#if defined(PARALLEL_MARK) && !defined(THREADS) && !defined(CPPCHECK) +# error Invalid config: PARALLEL_MARK requires GC_THREADS +#endif + +#if defined(GWW_VDB) && !defined(USE_WINALLOC) && !defined(CPPCHECK) +# error Invalid config: GWW_VDB requires USE_WINALLOC +#endif + +/* Whether GC_page_size is to be set to a value other than page size. */ +#if defined(CYGWIN32) && (defined(MPROTECT_VDB) || defined(USE_MUNMAP)) \ + || (!defined(ANY_MSWIN) && !defined(WASI) && !defined(USE_MMAP) \ + && (defined(GC_DISABLE_INCREMENTAL) || defined(DEFAULT_VDB))) + /* Cygwin: use the allocation granularity instead. Other than WASI */ + /* or Windows: use HBLKSIZE instead (unless mmap() is used). */ +# define ALT_PAGESIZE_USED +# ifndef GC_NO_VALLOC + /* Nonetheless, we need the real page size is some extra functions. */ +# define REAL_PAGESIZE_NEEDED +# endif +#endif + +#if defined(GC_PTHREADS) && !defined(GC_DARWIN_THREADS) \ + && !defined(GC_WIN32_THREADS) && !defined(PLATFORM_STOP_WORLD) \ + && !defined(SN_TARGET_PSP2) +# define PTHREAD_STOP_WORLD_IMPL +#endif + +#if defined(PTHREAD_STOP_WORLD_IMPL) && !defined(NACL) +# define SIGNAL_BASED_STOP_WORLD +#endif + +#if (defined(E2K) || defined(HP_PA) || defined(IA64) || defined(M68K) \ + || defined(NO_SA_SIGACTION)) && defined(SIGNAL_BASED_STOP_WORLD) +# define SUSPEND_HANDLER_NO_CONTEXT +#endif + +#if (defined(MSWIN32) || defined(MSWINCE) \ + || (defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS))) \ + && !defined(NO_CRT) && !defined(NO_WRAP_MARK_SOME) + /* Under rare conditions, we may end up marking from nonexistent */ + /* memory. Hence we need to be prepared to recover by running */ + /* GC_mark_some with a suitable handler in place. */ + /* TODO: Should we also define it for Cygwin? */ +# define WRAP_MARK_SOME +#endif + +#if !defined(MSWIN32) && !defined(MSWINCE) || defined(__GNUC__) \ + || defined(NO_CRT) +# define NO_SEH_AVAILABLE +#endif + +#ifdef GC_WIN32_THREADS + /* The number of copied registers in copy_ptr_regs. */ +# if defined(I386) +# ifdef WOW64_THREAD_CONTEXT_WORKAROUND +# define PUSHED_REGS_COUNT 9 +# else +# define PUSHED_REGS_COUNT 7 +# endif +# elif defined(X86_64) +# ifdef XMM_CANT_STORE_PTRS + /* If pointers can't be located in Xmm registers. */ +# define PUSHED_REGS_COUNT 15 +# else + /* gcc-13 may store pointers into SIMD registers when */ + /* certain compiler optimizations are enabled. */ +# define PUSHED_REGS_COUNT (15+32) +# endif +# elif defined(SHx) +# define PUSHED_REGS_COUNT 15 +# elif defined(ARM32) +# define PUSHED_REGS_COUNT 13 +# elif defined(AARCH64) +# define PUSHED_REGS_COUNT 30 +# elif defined(MIPS) || defined(ALPHA) +# define PUSHED_REGS_COUNT 28 +# elif defined(PPC) +# define PUSHED_REGS_COUNT 29 +# endif +#endif /* GC_WIN32_THREADS */ + +#if !defined(GC_PTHREADS) && !defined(GC_PTHREADS_PARAMARK) +# undef HAVE_PTHREAD_SETNAME_NP_WITH_TID +# undef HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG +# undef HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID +# undef HAVE_PTHREAD_SET_NAME_NP +#endif + +#ifdef USE_RWLOCK + /* At least in the Linux threads implementation, rwlock primitives */ + /* are not atomic in respect to signals, and suspending externally */ + /* a thread which is running inside pthread_rwlock_rdlock() may lead */ + /* to a deadlock. */ + /* TODO: As a workaround GC_suspend_thread() API is disabled. */ +# undef GC_ENABLE_SUSPEND_THREAD +#endif + +#ifndef GC_NO_THREADS_DISCOVERY +# ifdef GC_DARWIN_THREADS + /* Task-based thread registration requires stack-frame-walking code. */ +# if defined(DARWIN_DONT_PARSE_STACK) +# define GC_NO_THREADS_DISCOVERY +# endif +# elif defined(GC_WIN32_THREADS) + /* DllMain-based thread registration is currently incompatible */ + /* with thread-local allocation, pthreads and WinCE. */ +# if (!defined(GC_DLL) && !defined(GC_INSIDE_DLL)) || defined(GC_PTHREADS) \ + || defined(MSWINCE) || defined(NO_CRT) || defined(THREAD_LOCAL_ALLOC) +# define GC_NO_THREADS_DISCOVERY +# endif +# else +# define GC_NO_THREADS_DISCOVERY +# endif +#endif /* !GC_NO_THREADS_DISCOVERY */ + +#if defined(GC_DISCOVER_TASK_THREADS) && defined(GC_NO_THREADS_DISCOVERY) \ + && !defined(CPPCHECK) +# error Defined both GC_DISCOVER_TASK_THREADS and GC_NO_THREADS_DISCOVERY +#endif + +#if defined(PARALLEL_MARK) && !defined(DEFAULT_STACK_MAYBE_SMALL) \ + && (defined(HPUX) || defined(GC_DGUX386_THREADS) \ + || defined(NO_GETCONTEXT) /* e.g. musl */) + /* TODO: Test default stack size in configure. */ +# define DEFAULT_STACK_MAYBE_SMALL +#endif + +#ifdef PARALLEL_MARK + /* The minimum stack size for a marker thread. */ +# define MIN_STACK_SIZE (8 * HBLKSIZE * sizeof(word)) +#endif + +#if defined(HOST_ANDROID) && !defined(THREADS) \ + && !defined(USE_GET_STACKBASE_FOR_MAIN) + /* Always use pthread_attr_getstack on Android ("-lpthread" option is */ + /* not needed to be specified manually) since GC_linux_main_stack_base */ + /* causes app crash if invoked inside Dalvik VM. */ +# define USE_GET_STACKBASE_FOR_MAIN +#endif + +/* Outline pthread primitives to use in GC_get_[main_]stack_base. */ +#if ((defined(FREEBSD) && defined(__GLIBC__)) /* kFreeBSD */ \ + || defined(LINUX) || defined(KOS) || defined(NETBSD)) \ + && !defined(NO_PTHREAD_GETATTR_NP) +# define HAVE_PTHREAD_GETATTR_NP 1 +#elif defined(FREEBSD) && !defined(__GLIBC__) \ + && !defined(NO_PTHREAD_ATTR_GET_NP) +# define HAVE_PTHREAD_NP_H 1 /* requires include pthread_np.h */ +# define HAVE_PTHREAD_ATTR_GET_NP 1 +#endif + +#if !defined(HAVE_CLOCK_GETTIME) && defined(_POSIX_TIMERS) \ + && (defined(CYGWIN32) || (defined(LINUX) && defined(__USE_POSIX199309))) +# define HAVE_CLOCK_GETTIME 1 +#endif + +#if defined(GC_PTHREADS) && !defined(E2K) && !defined(IA64) \ + && (!defined(DARWIN) || defined(DARWIN_DONT_PARSE_STACK)) \ + && !defined(SN_TARGET_PSP2) && !defined(REDIRECT_MALLOC) + /* Note: unimplemented in case of redirection of malloc() because */ + /* the client-provided function might call some pthreads primitive */ + /* which, in turn, may use malloc() internally. */ +# define STACKPTR_CORRECTOR_AVAILABLE +#endif + +#if defined(UNIX_LIKE) && defined(THREADS) && !defined(NO_CANCEL_SAFE) \ + && !defined(HOST_ANDROID) + /* Make the code cancellation-safe. This basically means that we */ + /* ensure that cancellation requests are ignored while we are in */ + /* the collector. This applies only to Posix deferred cancellation; */ + /* we don't handle Posix asynchronous cancellation. */ + /* Note that this only works if pthread_setcancelstate is */ + /* async-signal-safe, at least in the absence of asynchronous */ + /* cancellation. This appears to be true for the glibc version, */ + /* though it is not documented. Without that assumption, there */ + /* seems to be no way to safely wait in a signal handler, which */ + /* we need to do for thread suspension. */ + /* Also note that little other code appears to be cancellation-safe. */ + /* Hence it may make sense to turn this off for performance. */ +# define CANCEL_SAFE +#endif + +#ifdef CANCEL_SAFE +# define IF_CANCEL(x) x +#else +# define IF_CANCEL(x) /* empty */ +#endif + +#if !defined(CAN_HANDLE_FORK) && !defined(NO_HANDLE_FORK) \ + && !defined(HAVE_NO_FORK) \ + && ((defined(GC_PTHREADS) && !defined(NACL) \ + && !defined(GC_WIN32_PTHREADS) && !defined(USE_WINALLOC)) \ + || (defined(DARWIN) && defined(MPROTECT_VDB) /* && !THREADS */) \ + || (defined(HANDLE_FORK) && defined(GC_PTHREADS))) + /* Attempts (where supported and requested) to make GC_malloc work in */ + /* a child process fork'ed from a multi-threaded parent. */ +# define CAN_HANDLE_FORK +#endif + +/* Workaround "failed to create new win32 semaphore" Cygwin fatal error */ +/* during semaphores fixup-after-fork. */ +#if defined(CYGWIN32) && defined(GC_WIN32_THREADS) \ + && defined(CAN_HANDLE_FORK) && !defined(EMULATE_PTHREAD_SEMAPHORE) \ + && !defined(CYGWIN_SEM_FIXUP_AFTER_FORK_BUG_FIXED) +# define EMULATE_PTHREAD_SEMAPHORE +#endif + +#if defined(CAN_HANDLE_FORK) && !defined(CAN_CALL_ATFORK) \ + && !defined(GC_NO_CAN_CALL_ATFORK) && !defined(HOST_TIZEN) \ + && !defined(HURD) && (!defined(HOST_ANDROID) || __ANDROID_API__ >= 21) + /* Have working pthread_atfork(). */ +# define CAN_CALL_ATFORK +#endif + +#if !defined(CAN_HANDLE_FORK) && !defined(HAVE_NO_FORK) \ + && !(defined(CYGWIN32) || defined(SOLARIS) || defined(UNIX_LIKE)) +# define HAVE_NO_FORK +#endif + +#if !defined(USE_MARK_BITS) && !defined(USE_MARK_BYTES) \ + && defined(PARALLEL_MARK) + /* Minimize compare-and-swap usage. */ +# define USE_MARK_BYTES +#endif + +#if (defined(MSWINCE) && !defined(__CEGCC__) || defined(MSWINRT_FLAVOR)) \ + && !defined(NO_GETENV) +# define NO_GETENV +#endif + +#if (defined(NO_GETENV) || defined(MSWINCE)) && !defined(NO_GETENV_WIN32) +# define NO_GETENV_WIN32 +#endif + +#if !defined(MSGBOX_ON_ERROR) && !defined(NO_MSGBOX_ON_ERROR) \ + && !defined(SMALL_CONFIG) && defined(MSWIN32) \ + && !defined(MSWINRT_FLAVOR) && !defined(MSWIN_XBOX1) + /* Show a Windows message box with "OK" button on a GC fatal error. */ + /* Client application is terminated once the user clicks the button. */ +# define MSGBOX_ON_ERROR +#endif + +#ifndef STRTOULL +# if defined(_WIN64) && !defined(__GNUC__) +# define STRTOULL _strtoui64 +# elif defined(_LLP64) || defined(__LLP64__) || defined(_WIN64) +# define STRTOULL strtoull +# else + /* strtoul() fits since sizeof(long) >= sizeof(word). */ +# define STRTOULL strtoul +# endif +#endif /* !STRTOULL */ + +#ifndef GC_WORD_C +# if defined(_WIN64) && !defined(__GNUC__) +# define GC_WORD_C(val) val##ui64 +# elif defined(_LLP64) || defined(__LLP64__) || defined(_WIN64) +# define GC_WORD_C(val) val##ULL +# else +# define GC_WORD_C(val) ((word)val##UL) +# endif +#endif /* !GC_WORD_C */ + +#if defined(__has_feature) + /* __has_feature() is supported. */ +# if __has_feature(address_sanitizer) +# define ADDRESS_SANITIZER +# endif +# if __has_feature(memory_sanitizer) +# define MEMORY_SANITIZER +# endif +# if __has_feature(thread_sanitizer) && defined(THREADS) +# define THREAD_SANITIZER +# endif +#else +# ifdef __SANITIZE_ADDRESS__ + /* GCC v4.8+ */ +# define ADDRESS_SANITIZER +# endif +# if defined(__SANITIZE_THREAD__) && defined(THREADS) + /* GCC v7.1+ */ +# define THREAD_SANITIZER +# endif +#endif /* !__has_feature */ + +#if defined(SPARC) +# define ASM_CLEAR_CODE /* Stack clearing is crucial, and we */ + /* include assembly code to do it well. */ +#endif + +/* Can we save call chain in objects for debugging? */ +/* SET NFRAMES (# of saved frames) and NARGS (#of args for each */ +/* frame) to reasonable values for the platform. */ +/* Set SAVE_CALL_CHAIN if we can. SAVE_CALL_COUNT can be specified */ +/* at build time, though we feel free to adjust it slightly. */ +/* Define NEED_CALLINFO if we either save the call stack or */ +/* GC_ADD_CALLER is defined. */ +/* GC_CAN_SAVE_CALL_STACKS is set in gc.h. */ +#if defined(SPARC) +# define CAN_SAVE_CALL_ARGS +#endif +#if (defined(I386) || defined(X86_64)) \ + && (defined(LINUX) || defined(__GLIBC__)) + /* SAVE_CALL_CHAIN is supported if the code is compiled to save */ + /* frame pointers by default, i.e. no -fomit-frame-pointer flag. */ +# define CAN_SAVE_CALL_ARGS +#endif + +#if defined(SAVE_CALL_COUNT) && !defined(GC_ADD_CALLER) \ + && defined(GC_CAN_SAVE_CALL_STACKS) +# define SAVE_CALL_CHAIN +#endif + +#ifdef SAVE_CALL_CHAIN +# if defined(SAVE_CALL_NARGS) && defined(CAN_SAVE_CALL_ARGS) +# define NARGS SAVE_CALL_NARGS +# else +# define NARGS 0 /* Number of arguments to save for each call. */ +# endif +# if !defined(SAVE_CALL_COUNT) || defined(CPPCHECK) +# define NFRAMES 6 /* Number of frames to save. Even for */ + /* alignment reasons. */ +# else +# define NFRAMES ((SAVE_CALL_COUNT + 1) & ~1) +# endif +# define NEED_CALLINFO +#elif defined(GC_ADD_CALLER) +# define NFRAMES 1 +# define NARGS 0 +# define NEED_CALLINFO +#endif + +#if (defined(FREEBSD) || (defined(DARWIN) && !defined(_POSIX_C_SOURCE)) \ + || (defined(SOLARIS) && (!defined(_XOPEN_SOURCE) \ + || defined(__EXTENSIONS__))) \ + || defined(LINUX)) && !defined(HAVE_DLADDR) +# define HAVE_DLADDR 1 +#endif + +#if defined(MAKE_BACK_GRAPH) && !defined(DBG_HDRS_ALL) +# define DBG_HDRS_ALL 1 +#endif + +#if defined(POINTER_MASK) && !defined(POINTER_SHIFT) +# define POINTER_SHIFT 0 +#elif !defined(POINTER_MASK) && defined(POINTER_SHIFT) +# define POINTER_MASK GC_WORD_MAX +#endif + +#if defined(FIXUP_POINTER) + /* Custom FIXUP_POINTER(p). */ +# define NEED_FIXUP_POINTER +#elif defined(DYNAMIC_POINTER_MASK) +# define FIXUP_POINTER(p) (p = ((p) & GC_pointer_mask) << GC_pointer_shift) +# undef POINTER_MASK +# undef POINTER_SHIFT +# define NEED_FIXUP_POINTER +#elif defined(POINTER_MASK) +# define FIXUP_POINTER(p) (p = ((p) & (POINTER_MASK)) << (POINTER_SHIFT)) + /* Extra parentheses around custom-defined POINTER_MASK/SHIFT. */ +# define NEED_FIXUP_POINTER +#else +# define FIXUP_POINTER(p) (void)(p) +#endif + +#if defined(REDIRECT_MALLOC) && defined(THREADS) && !defined(LINUX) \ + && !defined(REDIRECT_MALLOC_IN_HEADER) + /* May work on other platforms (e.g. Darwin) provided the client */ + /* ensures all the client threads are registered with the GC, */ + /* e.g. by using the preprocessor-based interception of the thread */ + /* primitives (i.e., define GC_THREADS and include gc.h from all */ + /* the client files those are using pthread_create and friends). */ +#endif + +#ifdef GC_PRIVATE_H + /* This relies on some type definitions from gc_priv.h, from */ + /* where it's normally included. */ + /* */ + /* How to get heap memory from the OS: */ + /* Note that sbrk()-like allocation is preferred, since it */ + /* usually makes it possible to merge consecutively allocated */ + /* chunks. It also avoids unintended recursion with */ + /* REDIRECT_MALLOC macro defined. */ + /* GET_MEM() argument should be of size_t type and have */ + /* no side-effect. GET_MEM() returns HBLKSIZE-aligned chunk; */ + /* 0 is taken to mean failure. */ + /* In case of MMAP_SUPPORTED, the argument must also be */ + /* a multiple of a physical page size. */ + /* GET_MEM is currently not assumed to retrieve 0 filled space, */ + /* though we should perhaps take advantage of the case in which */ + /* does. */ +# define hblk GC_hblk_s + struct hblk; /* See gc_priv.h. */ +# if defined(PCR) + char * real_malloc(size_t bytes); +# define GET_MEM(bytes) HBLKPTR(real_malloc(SIZET_SAT_ADD(bytes, \ + GC_page_size)) \ + + GC_page_size-1) +# elif defined(OS2) + void * os2_alloc(size_t bytes); +# define GET_MEM(bytes) HBLKPTR((ptr_t)os2_alloc( \ + SIZET_SAT_ADD(bytes, \ + GC_page_size)) \ + + GC_page_size-1) +# elif defined(NEXT) || defined(DOS4GW) || defined(NONSTOP) \ + || (defined(AMIGA) && !defined(GC_AMIGA_FASTALLOC)) \ + || (defined(SOLARIS) && !defined(USE_MMAP)) || defined(RTEMS) \ + || defined(EMBOX) || defined(KOS) || defined(__CC_ARM) + /* TODO: Use page_alloc() directly on Embox. */ +# if defined(REDIRECT_MALLOC) && !defined(CPPCHECK) +# error Malloc redirection is unsupported +# endif +# define GET_MEM(bytes) HBLKPTR((size_t)calloc(1, \ + SIZET_SAT_ADD(bytes, \ + GC_page_size)) \ + + GC_page_size - 1) +# elif defined(MSWIN_XBOX1) + ptr_t GC_durango_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk *)GC_durango_get_mem(bytes) +# elif defined(MSWIN32) || defined(CYGWIN32) + ptr_t GC_win32_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk *)GC_win32_get_mem(bytes) +# elif defined(MACOS) +# if defined(USE_TEMPORARY_MEMORY) + Ptr GC_MacTemporaryNewPtr(size_t size, Boolean clearMemory); +# define GET_MEM(bytes) HBLKPTR(GC_MacTemporaryNewPtr( \ + SIZET_SAT_ADD(bytes, \ + GC_page_size), true) \ + + GC_page_size-1) +# else +# define GET_MEM(bytes) HBLKPTR(NewPtrClear(SIZET_SAT_ADD(bytes, \ + GC_page_size)) \ + + GC_page_size-1) +# endif +# elif defined(MSWINCE) + ptr_t GC_wince_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk *)GC_wince_get_mem(bytes) +# elif defined(AMIGA) && defined(GC_AMIGA_FASTALLOC) + void *GC_amiga_get_mem(size_t bytes); +# define GET_MEM(bytes) HBLKPTR((size_t)GC_amiga_get_mem( \ + SIZET_SAT_ADD(bytes, \ + GC_page_size)) \ + + GC_page_size-1) +# elif defined(PLATFORM_GETMEM) + void *platform_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk*)platform_get_mem(bytes) +# elif defined(SN_TARGET_PS3) + void *ps3_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk*)ps3_get_mem(bytes) +# elif defined(SN_TARGET_PSP2) + void *psp2_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk*)psp2_get_mem(bytes) +# elif defined(NINTENDO_SWITCH) + void *switch_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk*)switch_get_mem(bytes) +# elif defined(HAIKU) + ptr_t GC_haiku_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk*)GC_haiku_get_mem(bytes) +# elif defined(EMSCRIPTEN_TINY) + void *emmalloc_memalign(size_t alignment, size_t size); +# define GET_MEM(bytes) (struct hblk*)emmalloc_memalign(GC_page_size, bytes) +# else + ptr_t GC_unix_get_mem(size_t bytes); +# define GET_MEM(bytes) (struct hblk *)GC_unix_get_mem(bytes) +# endif +#endif /* GC_PRIVATE_H */ + +EXTERN_C_END + +#endif /* GCCONFIG_H */ + + +#if !defined(GC_ATOMIC_UNCOLLECTABLE) && defined(ATOMIC_UNCOLLECTABLE) + /* For compatibility with old-style naming. */ +# define GC_ATOMIC_UNCOLLECTABLE +#endif + +#ifndef GC_INNER + /* This tagging macro must be used at the start of every variable */ + /* definition which is declared with GC_EXTERN. Should be also used */ + /* for the GC-scope function definitions and prototypes. Must not be */ + /* used in gcconfig.h. Shouldn't be used for the debugging-only */ + /* functions. Currently, not used for the functions declared in or */ + /* called from the "dated" source files (located in "extra" folder). */ +# if defined(GC_DLL) && defined(__GNUC__) && !defined(ANY_MSWIN) +# if GC_GNUC_PREREQ(4, 0) && !defined(GC_NO_VISIBILITY) + /* See the corresponding GC_API definition. */ +# define GC_INNER __attribute__((__visibility__("hidden"))) +# else + /* The attribute is unsupported. */ +# define GC_INNER /* empty */ +# endif +# else +# define GC_INNER /* empty */ +# endif + +# define GC_EXTERN extern GC_INNER + /* Used only for the GC-scope variables (prefixed with "GC_") */ + /* declared in the header files. Must not be used for thread-local */ + /* variables. Must not be used in gcconfig.h. Shouldn't be used for */ + /* the debugging-only or profiling-only variables. Currently, not */ + /* used for the variables accessed from the "dated" source files */ + /* (specific.c/h, and in the "extra" folder). */ + /* The corresponding variable definition must start with GC_INNER. */ +#endif /* !GC_INNER */ + +#ifdef __cplusplus + /* Register storage specifier is deprecated in C++11. */ +# define REGISTER /* empty */ +#else + /* Used only for several local variables in the performance-critical */ + /* functions. Should not be used for new code. */ +# define REGISTER register +#endif + +#if defined(CPPCHECK) +# define MACRO_BLKSTMT_BEGIN { +# define MACRO_BLKSTMT_END } +# define LOCAL_VAR_INIT_OK =0 /* to avoid "uninit var" false positive */ +#else +# define MACRO_BLKSTMT_BEGIN do { +# define MACRO_BLKSTMT_END } while (0) +# define LOCAL_VAR_INIT_OK /* empty */ +#endif + +#if defined(M68K) && defined(__GNUC__) + /* By default, __alignof__(word) is 2 on m68k. Use this attribute to */ + /* have proper word alignment (i.e. 4-byte on a 32-bit arch). */ +# define GC_ATTR_WORD_ALIGNED __attribute__((__aligned__(sizeof(word)))) +#else +# define GC_ATTR_WORD_ALIGNED /* empty */ +#endif + + typedef GC_word GC_funcptr_uint; +# define FUNCPTR_IS_WORD + +typedef unsigned int unsigned32; + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_HEADERS_H +#define GC_HEADERS_H + +#if !defined(GC_PRIVATE_H) && !defined(CPPCHECK) +# error gc_hdrs.h should be included from gc_priv.h +#endif + +#if CPP_WORDSZ != 32 && CPP_WORDSZ < 36 && !defined(CPPCHECK) +# error Get a real machine +#endif + +EXTERN_C_BEGIN + +typedef struct hblkhdr hdr; + +/* + * The 2 level tree data structure that is used to find block headers. + * If there are more than 32 bits in a pointer, the top level is a hash + * table. + * + * This defines HDR, GET_HDR, and SET_HDR, the main macros used to + * retrieve and set object headers. + * + * We take advantage of a header lookup + * cache. This is a locally declared direct mapped cache, used inside + * the marker. The HC_GET_HDR macro uses and maintains this + * cache. Assuming we get reasonable hit rates, this shaves a few + * memory references from each pointer validation. + */ + +#if CPP_WORDSZ > 32 +# define HASH_TL +#endif + +/* Define appropriate out-degrees for each of the two tree levels */ +#if defined(LARGE_CONFIG) || !defined(SMALL_CONFIG) +# define LOG_BOTTOM_SZ 10 +#else +# define LOG_BOTTOM_SZ 11 + /* Keep top index size reasonable with smaller blocks. */ +#endif +#define BOTTOM_SZ (1 << LOG_BOTTOM_SZ) + +#ifndef HASH_TL +# define LOG_TOP_SZ (CPP_WORDSZ - LOG_BOTTOM_SZ - LOG_HBLKSIZE) +#else +# define LOG_TOP_SZ 11 +#endif +#define TOP_SZ (1 << LOG_TOP_SZ) + +/* #define COUNT_HDR_CACHE_HITS */ + +#ifdef COUNT_HDR_CACHE_HITS + extern word GC_hdr_cache_hits; /* used for debugging/profiling */ + extern word GC_hdr_cache_misses; +# define HC_HIT() (void)(++GC_hdr_cache_hits) +# define HC_MISS() (void)(++GC_hdr_cache_misses) +#else +# define HC_HIT() /* empty */ +# define HC_MISS() /* empty */ +#endif + +typedef struct hce { + word block_addr; /* right shifted by LOG_HBLKSIZE */ + hdr * hce_hdr; +} hdr_cache_entry; + +#define HDR_CACHE_SIZE 8 /* power of 2 */ + +#define DECLARE_HDR_CACHE \ + hdr_cache_entry hdr_cache[HDR_CACHE_SIZE] + +#define INIT_HDR_CACHE BZERO(hdr_cache, sizeof(hdr_cache)) + +#define HCE(h) \ + (hdr_cache + (((word)(h) >> LOG_HBLKSIZE) & (HDR_CACHE_SIZE-1))) + +#define HCE_VALID_FOR(hce, h) ((hce) -> block_addr == \ + ((word)(h) >> LOG_HBLKSIZE)) + +#define HCE_HDR(h) ((hce) -> hce_hdr) + +#ifdef PRINT_BLACK_LIST + GC_INNER hdr * GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce, + ptr_t source); +# define HEADER_CACHE_MISS(p, hce, source) \ + GC_header_cache_miss(p, hce, source) +#else + GC_INNER hdr * GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce); +# define HEADER_CACHE_MISS(p, hce, source) GC_header_cache_miss(p, hce) +#endif + +/* Set hhdr to the header for p. Analogous to GET_HDR below, */ +/* except that in the case of large objects, it gets the header for */ +/* the object beginning if GC_all_interior_pointers is set. */ +/* Returns zero if p points to somewhere other than the first page */ +/* of an object, and it is not a valid pointer to the object. */ +#define HC_GET_HDR(p, hhdr, source) \ + { /* cannot use do-while(0) here */ \ + hdr_cache_entry * hce = HCE(p); \ + if (EXPECT(HCE_VALID_FOR(hce, p), TRUE)) { \ + HC_HIT(); \ + hhdr = hce -> hce_hdr; \ + } else { \ + hhdr = HEADER_CACHE_MISS(p, hce, source); \ + if (NULL == hhdr) break; /* go to the enclosing loop end */ \ + } \ + } + +typedef struct bi { + hdr * index[BOTTOM_SZ]; + /* + * The bottom level index contains one of three kinds of values: + * 0 means we're not responsible for this block, + * or this is a block other than the first one in a free block. + * 1 < (long)X <= MAX_JUMP means the block starts at least + * X * HBLKSIZE bytes before the current address. + * A valid pointer points to a hdr structure. (The above can't be + * valid pointers due to the GET_MEM return convention.) + */ + struct bi * asc_link; /* All indices are linked in */ + /* ascending order... */ + struct bi * desc_link; /* ... and in descending order. */ + word key; /* high order address bits. */ +# ifdef HASH_TL + struct bi * hash_link; /* Hash chain link. */ +# endif +} bottom_index; + +/* bottom_index GC_all_nils; - really part of GC_arrays */ + +/* extern bottom_index * GC_top_index []; - really part of GC_arrays */ + /* Each entry points to a bottom_index. */ + /* On a 32 bit machine, it points to */ + /* the index for a set of high order */ + /* bits equal to the index. For longer */ + /* addresses, we hash the high order */ + /* bits to compute the index in */ + /* GC_top_index, and each entry points */ + /* to a hash chain. */ + /* The last entry in each chain is */ + /* GC_all_nils. */ + + +#define MAX_JUMP (HBLKSIZE-1) + +#define HDR_FROM_BI(bi, p) \ + (bi)->index[((word)(p) >> LOG_HBLKSIZE) & (BOTTOM_SZ - 1)] +#ifndef HASH_TL +# define BI(p) (GC_top_index \ + [(word)(p) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE)]) +# define HDR_INNER(p) HDR_FROM_BI(BI(p),p) +# ifdef SMALL_CONFIG +# define HDR(p) GC_find_header((ptr_t)(p)) +# else +# define HDR(p) HDR_INNER(p) +# endif +# define GET_BI(p, bottom_indx) (void)((bottom_indx) = BI(p)) +# define GET_HDR(p, hhdr) (void)((hhdr) = HDR(p)) +# define SET_HDR(p, hhdr) (void)(HDR_INNER(p) = (hhdr)) +# define GET_HDR_ADDR(p, ha) (void)((ha) = &HDR_INNER(p)) +#else /* hash */ + /* Hash function for tree top level */ +# define TL_HASH(hi) ((hi) & (TOP_SZ - 1)) + /* Set bottom_indx to point to the bottom index for address p */ +# define GET_BI(p, bottom_indx) \ + do { \ + REGISTER word hi = (word)(p) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); \ + REGISTER bottom_index * _bi = GC_top_index[TL_HASH(hi)]; \ + while (_bi -> key != hi && _bi != GC_all_nils) \ + _bi = _bi -> hash_link; \ + (bottom_indx) = _bi; \ + } while (0) +# define GET_HDR_ADDR(p, ha) \ + do { \ + REGISTER bottom_index * bi; \ + GET_BI(p, bi); \ + (ha) = &HDR_FROM_BI(bi, p); \ + } while (0) +# define GET_HDR(p, hhdr) \ + do { \ + REGISTER hdr ** _ha; \ + GET_HDR_ADDR(p, _ha); \ + (hhdr) = *_ha; \ + } while (0) +# define SET_HDR(p, hhdr) \ + do { \ + REGISTER bottom_index * bi; \ + GET_BI(p, bi); \ + GC_ASSERT(bi != GC_all_nils); \ + HDR_FROM_BI(bi, p) = (hhdr); \ + } while (0) +# define HDR(p) GC_find_header((ptr_t)(p)) +#endif + +/* Is the result a forwarding address to someplace closer to the */ +/* beginning of the block or NULL? */ +#define IS_FORWARDING_ADDR_OR_NIL(hhdr) ((size_t) (hhdr) <= MAX_JUMP) + +/* Get an HBLKSIZE aligned address closer to the beginning of the block */ +/* h. Assumes hhdr == HDR(h) and IS_FORWARDING_ADDR(hhdr). */ +#define FORWARDED_ADDR(h, hhdr) ((struct hblk *)(h) - (size_t)(hhdr)) + +EXTERN_C_END + +#endif /* GC_HEADERS_H */ + + +#ifndef GC_ATTR_NO_SANITIZE_ADDR +# ifndef ADDRESS_SANITIZER +# define GC_ATTR_NO_SANITIZE_ADDR /* empty */ +# elif GC_CLANG_PREREQ(3, 8) +# define GC_ATTR_NO_SANITIZE_ADDR __attribute__((no_sanitize("address"))) +# else +# define GC_ATTR_NO_SANITIZE_ADDR __attribute__((no_sanitize_address)) +# endif +#endif /* !GC_ATTR_NO_SANITIZE_ADDR */ + +#ifndef GC_ATTR_NO_SANITIZE_MEMORY +# ifndef MEMORY_SANITIZER +# define GC_ATTR_NO_SANITIZE_MEMORY /* empty */ +# elif GC_CLANG_PREREQ(3, 8) +# define GC_ATTR_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory"))) +# else +# define GC_ATTR_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) +# endif +#endif /* !GC_ATTR_NO_SANITIZE_MEMORY */ + +#ifndef GC_ATTR_NO_SANITIZE_THREAD +# ifndef THREAD_SANITIZER +# define GC_ATTR_NO_SANITIZE_THREAD /* empty */ +# elif GC_CLANG_PREREQ(3, 8) +# define GC_ATTR_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread"))) +# else + /* It seems that no_sanitize_thread attribute has no effect if the */ + /* function is inlined (as of gcc 11.1.0, at least). */ +# define GC_ATTR_NO_SANITIZE_THREAD \ + GC_ATTR_NOINLINE __attribute__((no_sanitize_thread)) +# endif +#endif /* !GC_ATTR_NO_SANITIZE_THREAD */ + +#ifndef UNUSED_ARG +# define UNUSED_ARG(arg) ((void)(arg)) +#endif + +#ifdef HAVE_CONFIG_H + /* The "inline" keyword is determined by Autoconf AC_C_INLINE. */ +# define GC_INLINE static inline +#elif defined(_MSC_VER) || defined(__INTEL_COMPILER) || defined(__DMC__) \ + || (GC_GNUC_PREREQ(3, 0) && defined(__STRICT_ANSI__)) \ + || defined(__BORLANDC__) || defined(__WATCOMC__) +# define GC_INLINE static __inline +#elif GC_GNUC_PREREQ(3, 0) || defined(__sun) +# define GC_INLINE static inline +#else +# define GC_INLINE static +#endif + +#ifndef GC_ATTR_NOINLINE +# if GC_GNUC_PREREQ(4, 0) +# define GC_ATTR_NOINLINE __attribute__((__noinline__)) +# elif _MSC_VER >= 1400 +# define GC_ATTR_NOINLINE __declspec(noinline) +# else +# define GC_ATTR_NOINLINE /* empty */ +# endif +#endif + +#ifndef GC_API_OSCALL + /* This is used to identify GC routines called by name from OS. */ +# if defined(__GNUC__) +# if GC_GNUC_PREREQ(4, 0) && !defined(GC_NO_VISIBILITY) + /* Same as GC_API if GC_DLL. */ +# define GC_API_OSCALL extern __attribute__((__visibility__("default"))) +# else + /* The attribute is unsupported. */ +# define GC_API_OSCALL extern +# endif +# else +# define GC_API_OSCALL GC_API +# endif +#endif + +#ifndef GC_API_PRIV +# define GC_API_PRIV GC_API +#endif + +#if defined(THREADS) && !defined(NN_PLATFORM_CTR) +/* + * Copyright (c) 2017 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* This is a private GC header which provides an implementation of */ +/* libatomic_ops subset primitives sufficient for GC assuming that GCC */ +/* atomic intrinsics are available (and have correct implementation). */ +/* This is enabled by defining GC_BUILTIN_ATOMIC macro. Otherwise, */ +/* libatomic_ops library is used to define the primitives. */ + +#ifndef GC_ATOMIC_OPS_H +#define GC_ATOMIC_OPS_H + +#ifdef GC_BUILTIN_ATOMIC + + + +# ifdef __cplusplus + extern "C" { +# endif + + typedef GC_word AO_t; + +# ifdef GC_PRIVATE_H /* have GC_INLINE */ +# define AO_INLINE GC_INLINE +# else +# define AO_INLINE static __inline +# endif + +# if !defined(THREAD_SANITIZER) && !defined(GC_PRIVATE_H) + /* Similar to that in gcconfig.h. */ +# if defined(__has_feature) +# if __has_feature(thread_sanitizer) +# define THREAD_SANITIZER +# endif +# elif defined(__SANITIZE_THREAD__) +# define THREAD_SANITIZER +# endif +# endif /* !THREAD_SANITIZER && !GC_PRIVATE_H */ + + typedef unsigned char AO_TS_t; +# define AO_TS_CLEAR 0 +# define AO_TS_INITIALIZER (AO_TS_t)AO_TS_CLEAR +# if defined(__GCC_ATOMIC_TEST_AND_SET_TRUEVAL) && !defined(CPPCHECK) +# define AO_TS_SET __GCC_ATOMIC_TEST_AND_SET_TRUEVAL +# else +# define AO_TS_SET (AO_TS_t)1 /* true */ +# endif +# define AO_CLEAR(p) __atomic_clear(p, __ATOMIC_RELEASE) +# define AO_test_and_set_acquire(p) \ + (__atomic_test_and_set(p, __ATOMIC_ACQUIRE) ? AO_TS_SET : AO_TS_CLEAR) +# define AO_HAVE_test_and_set_acquire + +# define AO_compiler_barrier() __atomic_signal_fence(__ATOMIC_SEQ_CST) + +# if defined(THREAD_SANITIZER) && !defined(AO_USE_ATOMIC_THREAD_FENCE) + /* Workaround a compiler warning (reported by gcc-11, at least) */ + /* that atomic_thread_fence is unsupported with thread sanitizer. */ + AO_INLINE void + AO_nop_full(void) + { + volatile AO_TS_t dummy = AO_TS_INITIALIZER; + (void)__atomic_test_and_set(&dummy, __ATOMIC_SEQ_CST); + } +# else +# define AO_nop_full() __atomic_thread_fence(__ATOMIC_SEQ_CST) +# endif +# define AO_HAVE_nop_full + +# define AO_fetch_and_add(p, v) __atomic_fetch_add(p, v, __ATOMIC_RELAXED) +# define AO_HAVE_fetch_and_add +# define AO_fetch_and_add1(p) AO_fetch_and_add(p, 1) +# define AO_HAVE_fetch_and_add1 +# define AO_fetch_and_sub1(p) AO_fetch_and_add(p, (AO_t)(GC_signed_word)-1) +# define AO_HAVE_fetch_and_sub1 + +# define AO_or(p, v) (void)__atomic_or_fetch(p, v, __ATOMIC_RELAXED) +# define AO_HAVE_or + +# define AO_load(p) __atomic_load_n(p, __ATOMIC_RELAXED) +# define AO_HAVE_load +# define AO_load_acquire(p) __atomic_load_n(p, __ATOMIC_ACQUIRE) +# define AO_HAVE_load_acquire +# define AO_load_acquire_read(p) AO_load_acquire(p) +# define AO_HAVE_load_acquire_read + +# define AO_store(p, v) __atomic_store_n(p, v, __ATOMIC_RELAXED) +# define AO_HAVE_store +# define AO_store_release(p, v) __atomic_store_n(p, v, __ATOMIC_RELEASE) +# define AO_HAVE_store_release +# define AO_store_release_write(p, v) AO_store_release(p, v) +# define AO_HAVE_store_release_write + +# define AO_char_load(p) __atomic_load_n(p, __ATOMIC_RELAXED) +# define AO_HAVE_char_load +# define AO_char_store(p, v) __atomic_store_n(p, v, __ATOMIC_RELAXED) +# define AO_HAVE_char_store + +# ifdef AO_REQUIRE_CAS + AO_INLINE int + AO_compare_and_swap(volatile AO_t *p, AO_t ov, AO_t nv) + { + return (int)__atomic_compare_exchange_n(p, &ov, nv, 0, + __ATOMIC_RELAXED, __ATOMIC_RELAXED); + } + + AO_INLINE int + AO_compare_and_swap_release(volatile AO_t *p, AO_t ov, AO_t nv) + { + return (int)__atomic_compare_exchange_n(p, &ov, nv, 0, + __ATOMIC_RELEASE, __ATOMIC_RELAXED); + } +# define AO_HAVE_compare_and_swap_release +# endif + +# ifdef __cplusplus + } /* extern "C" */ +# endif + +# ifndef NO_LOCKFREE_AO_OR + /* __atomic_or_fetch is assumed to be lock-free. */ +# define HAVE_LOCKFREE_AO_OR 1 +# endif + +#else + /* Fallback to libatomic_ops. */ +# include "atomic_ops.h" + + /* AO_compiler_barrier, AO_load and AO_store should be defined for */ + /* all targets; the rest of the primitives are guaranteed to exist */ + /* only if AO_REQUIRE_CAS is defined (or if the corresponding */ + /* AO_HAVE_x macro is defined). x86/x64 targets have AO_nop_full, */ + /* AO_load_acquire, AO_store_release, at least. */ +# if (!defined(AO_HAVE_load) || !defined(AO_HAVE_store)) && !defined(CPPCHECK) +# error AO_load or AO_store is missing; probably old version of atomic_ops +# endif + +#endif /* !GC_BUILTIN_ATOMIC */ + +#endif /* GC_ATOMIC_OPS_H */ + +# ifndef AO_HAVE_compiler_barrier +# define AO_HAVE_compiler_barrier 1 +# endif +#endif + +#ifdef ANY_MSWIN +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# define NOSERVICE +# include +# include +#endif /* ANY_MSWIN */ + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_LOCKS_H +#define GC_LOCKS_H + +#if !defined(GC_PRIVATE_H) && !defined(CPPCHECK) +# error gc_locks.h should be included from gc_priv.h +#endif + +/* Mutual exclusion between allocator/collector routines. Needed if */ +/* there is more than one allocator thread. Note that I_HOLD_LOCK, */ +/* I_DONT_HOLD_LOCK and I_HOLD_READER_LOCK are used only positively in */ +/* assertions, and may return TRUE in the "don't know" case. */ + +#ifdef THREADS + +# ifdef PCR +# include +# include +# endif + + EXTERN_C_BEGIN + +# ifdef PCR + GC_EXTERN PCR_Th_ML GC_allocate_ml; +# define UNCOND_LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml) +# define UNCOND_UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml) +# elif defined(NN_PLATFORM_CTR) || defined(NINTENDO_SWITCH) + extern void GC_lock(void); + extern void GC_unlock(void); +# define UNCOND_LOCK() GC_lock() +# define UNCOND_UNLOCK() GC_unlock() +# endif + +# if (!defined(AO_HAVE_test_and_set_acquire) || defined(GC_RTEMS_PTHREADS) \ + || defined(SN_TARGET_PS3) \ + || defined(GC_WIN32_THREADS) || defined(BASE_ATOMIC_OPS_EMULATED) \ + || defined(LINT2) || defined(USE_RWLOCK)) && defined(GC_PTHREADS) +# define USE_PTHREAD_LOCKS +# undef USE_SPIN_LOCK +# if (defined(LINT2) || defined(GC_WIN32_THREADS) || defined(USE_RWLOCK)) \ + && !defined(NO_PTHREAD_TRYLOCK) + /* pthread_mutex_trylock may not win in GC_lock on Win32, */ + /* due to builtin support for spinning first? */ +# define NO_PTHREAD_TRYLOCK +# endif +# endif + +# if defined(GC_WIN32_THREADS) && !defined(USE_PTHREAD_LOCKS) \ + || defined(GC_PTHREADS) +# define NO_THREAD ((unsigned long)(-1L)) + /* != NUMERIC_THREAD_ID(pthread_self()) for any thread */ +# ifdef GC_ASSERTIONS + GC_EXTERN unsigned long GC_lock_holder; +# define UNSET_LOCK_HOLDER() (void)(GC_lock_holder = NO_THREAD) +# endif +# endif /* GC_WIN32_THREADS || GC_PTHREADS */ + +# if defined(GC_WIN32_THREADS) && !defined(USE_PTHREAD_LOCKS) +# ifdef USE_RWLOCK + GC_EXTERN SRWLOCK GC_allocate_ml; +# else + GC_EXTERN CRITICAL_SECTION GC_allocate_ml; +# endif +# ifdef GC_ASSERTIONS +# define SET_LOCK_HOLDER() (void)(GC_lock_holder = GetCurrentThreadId()) +# define I_HOLD_LOCK() (!GC_need_to_lock \ + || GC_lock_holder == GetCurrentThreadId()) +# ifdef THREAD_SANITIZER +# define I_DONT_HOLD_LOCK() TRUE /* Conservatively say yes */ +# else +# define I_DONT_HOLD_LOCK() (!GC_need_to_lock \ + || GC_lock_holder != GetCurrentThreadId()) +# endif +# ifdef USE_RWLOCK +# define UNCOND_READER_LOCK() \ + { GC_ASSERT(I_DONT_HOLD_LOCK()); \ + AcquireSRWLockShared(&GC_allocate_ml); } +# define UNCOND_READER_UNLOCK() \ + { GC_ASSERT(I_DONT_HOLD_LOCK()); \ + ReleaseSRWLockShared(&GC_allocate_ml); } +# define UNCOND_LOCK() \ + { GC_ASSERT(I_DONT_HOLD_LOCK()); \ + AcquireSRWLockExclusive(&GC_allocate_ml); \ + SET_LOCK_HOLDER(); } +# define UNCOND_UNLOCK() \ + { GC_ASSERT(I_HOLD_LOCK()); \ + UNSET_LOCK_HOLDER(); \ + ReleaseSRWLockExclusive(&GC_allocate_ml); } +# else +# define UNCOND_LOCK() \ + { GC_ASSERT(I_DONT_HOLD_LOCK()); \ + EnterCriticalSection(&GC_allocate_ml); \ + SET_LOCK_HOLDER(); } +# define UNCOND_UNLOCK() \ + { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \ + LeaveCriticalSection(&GC_allocate_ml); } +# endif +# else +# ifdef USE_RWLOCK +# define UNCOND_READER_LOCK() AcquireSRWLockShared(&GC_allocate_ml) +# define UNCOND_READER_UNLOCK() ReleaseSRWLockShared(&GC_allocate_ml) +# define UNCOND_LOCK() AcquireSRWLockExclusive(&GC_allocate_ml) +# define UNCOND_UNLOCK() ReleaseSRWLockExclusive(&GC_allocate_ml) +# else +# define UNCOND_LOCK() EnterCriticalSection(&GC_allocate_ml) +# define UNCOND_UNLOCK() LeaveCriticalSection(&GC_allocate_ml) +# endif +# endif /* !GC_ASSERTIONS */ +# elif defined(GC_PTHREADS) + EXTERN_C_END +# include + EXTERN_C_BEGIN + /* Posix allows pthread_t to be a struct, though it rarely is. */ + /* Unfortunately, we need to use a pthread_t to index a data */ + /* structure. It also helps if comparisons don't involve a */ + /* function call. Hence we introduce platform-dependent macros */ + /* to compare pthread_t ids and to map them to integers. */ + /* The mapping to integers does not need to result in different */ + /* integers for each thread, though that should be true as much */ + /* as possible. */ + /* Refine to exclude platforms on which pthread_t is struct. */ +# if !defined(GC_WIN32_PTHREADS) +# define NUMERIC_THREAD_ID(id) ((unsigned long)(id)) +# define THREAD_EQUAL(id1, id2) ((id1) == (id2)) +# define NUMERIC_THREAD_ID_UNIQUE +# elif defined(__WINPTHREADS_VERSION_MAJOR) /* winpthreads */ +# define NUMERIC_THREAD_ID(id) ((unsigned long)(id)) +# define THREAD_EQUAL(id1, id2) ((id1) == (id2)) +# ifndef _WIN64 + /* NUMERIC_THREAD_ID is 32-bit and not unique on Win64. */ +# define NUMERIC_THREAD_ID_UNIQUE +# endif +# else /* pthreads-win32 */ +# define NUMERIC_THREAD_ID(id) ((unsigned long)(word)(id.p)) + /* Using documented internal details of pthreads-win32 library. */ + /* Faster than pthread_equal(). Should not change with */ + /* future versions of pthreads-win32 library. */ +# define THREAD_EQUAL(id1, id2) ((id1.p == id2.p) && (id1.x == id2.x)) +# undef NUMERIC_THREAD_ID_UNIQUE + /* Generic definitions based on pthread_equal() always work but */ + /* will result in poor performance (as NUMERIC_THREAD_ID is */ + /* defined to just a constant) and weak assertion checking. */ +# endif + +# ifdef SN_TARGET_PSP2 + EXTERN_C_END +# include "psp2-support.h" + EXTERN_C_BEGIN + GC_EXTERN WapiMutex GC_allocate_ml_PSP2; +# define UNCOND_LOCK() { int res; GC_ASSERT(I_DONT_HOLD_LOCK()); \ + res = PSP2_MutexLock(&GC_allocate_ml_PSP2); \ + GC_ASSERT(0 == res); (void)res; \ + SET_LOCK_HOLDER(); } +# define UNCOND_UNLOCK() { int res; GC_ASSERT(I_HOLD_LOCK()); \ + UNSET_LOCK_HOLDER(); \ + res = PSP2_MutexUnlock(&GC_allocate_ml_PSP2); \ + GC_ASSERT(0 == res); (void)res; } + +# elif (!defined(THREAD_LOCAL_ALLOC) || defined(USE_SPIN_LOCK)) \ + && !defined(USE_PTHREAD_LOCKS) && !defined(THREAD_SANITIZER) \ + && !defined(USE_RWLOCK) + /* In the THREAD_LOCAL_ALLOC case, the allocator lock tends to */ + /* be held for long periods, if it is held at all. Thus spinning */ + /* and sleeping for fixed periods are likely to result in */ + /* significant wasted time. We thus rely mostly on queued locks. */ +# undef USE_SPIN_LOCK +# define USE_SPIN_LOCK + GC_EXTERN volatile AO_TS_t GC_allocate_lock; + GC_INNER void GC_lock(void); +# ifdef GC_ASSERTIONS +# define UNCOND_LOCK() \ + { GC_ASSERT(I_DONT_HOLD_LOCK()); \ + if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_SET) \ + GC_lock(); \ + SET_LOCK_HOLDER(); } +# define UNCOND_UNLOCK() \ + { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \ + AO_CLEAR(&GC_allocate_lock); } +# else +# define UNCOND_LOCK() \ + { if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_SET) \ + GC_lock(); } +# define UNCOND_UNLOCK() AO_CLEAR(&GC_allocate_lock) +# endif /* !GC_ASSERTIONS */ +# else /* THREAD_LOCAL_ALLOC || USE_PTHREAD_LOCKS */ +# ifndef USE_PTHREAD_LOCKS +# define USE_PTHREAD_LOCKS +# endif +# endif /* THREAD_LOCAL_ALLOC || USE_PTHREAD_LOCKS */ +# ifdef USE_PTHREAD_LOCKS + EXTERN_C_END +# include + EXTERN_C_BEGIN +# ifdef GC_ASSERTIONS + GC_INNER void GC_lock(void); +# define UNCOND_LOCK() { GC_ASSERT(I_DONT_HOLD_LOCK()); \ + GC_lock(); SET_LOCK_HOLDER(); } +# endif +# ifdef USE_RWLOCK + GC_EXTERN pthread_rwlock_t GC_allocate_ml; +# ifdef GC_ASSERTIONS +# define UNCOND_READER_LOCK() \ + { GC_ASSERT(I_DONT_HOLD_LOCK()); \ + (void)pthread_rwlock_rdlock(&GC_allocate_ml); } +# define UNCOND_READER_UNLOCK() \ + { GC_ASSERT(I_DONT_HOLD_LOCK()); \ + (void)pthread_rwlock_unlock(&GC_allocate_ml); } +# define UNCOND_UNLOCK() \ + { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \ + (void)pthread_rwlock_unlock(&GC_allocate_ml); } +# else +# define UNCOND_READER_LOCK() \ + (void)pthread_rwlock_rdlock(&GC_allocate_ml) +# define UNCOND_READER_UNLOCK() UNCOND_UNLOCK() +# define UNCOND_LOCK() (void)pthread_rwlock_wrlock(&GC_allocate_ml) +# define UNCOND_UNLOCK() (void)pthread_rwlock_unlock(&GC_allocate_ml) +# endif /* !GC_ASSERTIONS */ +# else + GC_EXTERN pthread_mutex_t GC_allocate_ml; +# ifdef GC_ASSERTIONS +# define UNCOND_UNLOCK() \ + { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \ + pthread_mutex_unlock(&GC_allocate_ml); } +# else +# if defined(NO_PTHREAD_TRYLOCK) +# define UNCOND_LOCK() pthread_mutex_lock(&GC_allocate_ml) +# else + GC_INNER void GC_lock(void); +# define UNCOND_LOCK() \ + { if (0 != pthread_mutex_trylock(&GC_allocate_ml)) \ + GC_lock(); } +# endif +# define UNCOND_UNLOCK() pthread_mutex_unlock(&GC_allocate_ml) +# endif /* !GC_ASSERTIONS */ +# endif /* !USE_RWLOCK */ +# endif /* USE_PTHREAD_LOCKS */ +# ifdef GC_ASSERTIONS + /* The allocator lock holder. */ +# define SET_LOCK_HOLDER() \ + (void)(GC_lock_holder = NUMERIC_THREAD_ID(pthread_self())) +# define I_HOLD_LOCK() \ + (!GC_need_to_lock \ + || GC_lock_holder == NUMERIC_THREAD_ID(pthread_self())) +# if !defined(NUMERIC_THREAD_ID_UNIQUE) || defined(THREAD_SANITIZER) +# define I_DONT_HOLD_LOCK() TRUE /* Conservatively say yes */ +# else +# define I_DONT_HOLD_LOCK() \ + (!GC_need_to_lock \ + || GC_lock_holder != NUMERIC_THREAD_ID(pthread_self())) +# endif +# endif /* GC_ASSERTIONS */ +# ifndef GC_WIN32_THREADS + GC_EXTERN volatile unsigned char GC_collecting; +# ifdef AO_HAVE_char_store +# define ENTER_GC() AO_char_store(&GC_collecting, TRUE) +# define EXIT_GC() AO_char_store(&GC_collecting, FALSE) +# else +# define ENTER_GC() (void)(GC_collecting = TRUE) +# define EXIT_GC() (void)(GC_collecting = FALSE) +# endif +# endif /* !GC_WIN32_THREADS */ +# endif /* GC_PTHREADS */ +# if defined(GC_ALWAYS_MULTITHREADED) \ + && (defined(USE_PTHREAD_LOCKS) || defined(USE_SPIN_LOCK)) +# define GC_need_to_lock TRUE +# define set_need_to_lock() (void)0 +# else +# if defined(GC_ALWAYS_MULTITHREADED) && !defined(CPPCHECK) +# error Runtime initialization of the allocator lock is needed! +# endif +# undef GC_ALWAYS_MULTITHREADED + GC_EXTERN GC_bool GC_need_to_lock; +# ifdef THREAD_SANITIZER + /* To workaround TSan false positive (e.g., when */ + /* GC_pthread_create is called from multiple threads in */ + /* parallel), do not set GC_need_to_lock if it is already set. */ +# define set_need_to_lock() \ + (void)(*(GC_bool volatile *)&GC_need_to_lock \ + ? FALSE \ + : (GC_need_to_lock = TRUE)) +# else +# define set_need_to_lock() (void)(GC_need_to_lock = TRUE) + /* We are multi-threaded now. */ +# endif +# endif + + EXTERN_C_END + +#else /* !THREADS */ +# define LOCK() (void)0 +# define UNLOCK() (void)0 +# ifdef GC_ASSERTIONS +# define I_HOLD_LOCK() TRUE +# define I_DONT_HOLD_LOCK() TRUE + /* Used only in positive assertions or to test whether */ + /* we still need to acquire the allocator lock. */ + /* TRUE works in either case. */ +# endif +#endif /* !THREADS */ + +#if defined(UNCOND_LOCK) && !defined(LOCK) +# if (defined(LINT2) && defined(USE_PTHREAD_LOCKS)) \ + || defined(GC_ALWAYS_MULTITHREADED) + /* Instruct code analysis tools not to care about GC_need_to_lock */ + /* influence to LOCK/UNLOCK semantic. */ +# define LOCK() UNCOND_LOCK() +# define UNLOCK() UNCOND_UNLOCK() +# ifdef UNCOND_READER_LOCK +# define READER_LOCK() UNCOND_READER_LOCK() +# define READER_UNLOCK() UNCOND_READER_UNLOCK() +# endif +# else + /* At least two thread running; need to lock. */ +# define LOCK() do { if (GC_need_to_lock) UNCOND_LOCK(); } while (0) +# define UNLOCK() do { if (GC_need_to_lock) UNCOND_UNLOCK(); } while (0) +# ifdef UNCOND_READER_LOCK +# define READER_LOCK() \ + do { if (GC_need_to_lock) UNCOND_READER_LOCK(); } while (0) +# define READER_UNLOCK() \ + do { if (GC_need_to_lock) UNCOND_READER_UNLOCK(); } while (0) +# endif +# endif +#endif /* UNCOND_LOCK && !LOCK */ + +#ifdef READER_LOCK +# define HAS_REAL_READER_LOCK +# define I_HOLD_READER_LOCK() TRUE /* TODO: implement */ +#else +# define READER_LOCK() LOCK() +# define READER_UNLOCK() UNLOCK() +# ifdef GC_ASSERTIONS + /* A macro to check that the allocator lock is held at least in the */ + /* reader mode. */ +# define I_HOLD_READER_LOCK() I_HOLD_LOCK() +# endif +#endif /* !READER_LOCK */ + +/* A variant of READER_UNLOCK() which ensures that data written before */ +/* the unlock will be visible to the thread which acquires the */ +/* allocator lock in the exclusive mode. But according to some rwlock */ +/* documentation: writers synchronize with prior writers and readers. */ +#define READER_UNLOCK_RELEASE() READER_UNLOCK() + +# ifndef ENTER_GC +# define ENTER_GC() +# define EXIT_GC() +# endif + +#endif /* GC_LOCKS_H */ + + +#define GC_WORD_MAX (~(word)0) + +#ifdef STACK_GROWS_UP +# define COOLER_THAN < +# define HOTTER_THAN > +# define MAKE_COOLER(x,y) if ((word)((x) - (y)) < (word)(x)) {(x) -= (y);} \ + else (x) = 0 +# define MAKE_HOTTER(x,y) (void)((x) += (y)) +#else +# define COOLER_THAN > +# define HOTTER_THAN < +# define MAKE_COOLER(x,y) if ((word)((x) + (y)) > (word)(x)) {(x) += (y);} \ + else (x) = (ptr_t)GC_WORD_MAX +# define MAKE_HOTTER(x,y) (void)((x) -= (y)) +#endif + +#if defined(AMIGA) && defined(__SASC) +# define GC_FAR __far +#else +# define GC_FAR +#endif + +#ifdef GC_ASSERTIONS +# define GC_ASSERT(expr) \ + do { \ + if (EXPECT(!(expr), FALSE)) { \ + GC_err_printf("Assertion failure: %s:%d\n", __FILE__, __LINE__); \ + ABORT("assertion failure"); \ + } \ + } while (0) +#else +# define GC_ASSERT(expr) +#endif + +#include "gc/gc_inline.h" + +/*********************************/ +/* */ +/* Definitions for conservative */ +/* collector */ +/* */ +/*********************************/ + +/*********************************/ +/* */ +/* Easily changeable parameters */ +/* */ +/*********************************/ + +/* #define ALL_INTERIOR_POINTERS */ + /* Forces all pointers into the interior of an */ + /* object to be considered valid. Also causes the */ + /* sizes of all objects to be inflated by at least */ + /* one byte. This should suffice to guarantee */ + /* that in the presence of a compiler that does */ + /* not perform garbage-collector-unsafe */ + /* optimizations, all portable, strictly ANSI */ + /* conforming C programs should be safely usable */ + /* with malloc replaced by GC_malloc and free */ + /* calls removed. There are several disadvantages: */ + /* 1. There are probably no interesting, portable, */ + /* strictly ANSI conforming C programs. */ + /* 2. This option makes it hard for the collector */ + /* to allocate space that is not "pointed to" */ + /* by integers, etc. Under SunOS 4.X with a */ + /* statically linked libc, we empirically */ + /* observed that it would be difficult to */ + /* allocate individual objects > 100 KB. */ + /* Even if only smaller objects are allocated, */ + /* more swap space is likely to be needed. */ + /* Fortunately, much of this will never be */ + /* touched. */ + /* If you can easily avoid using this option, do. */ + /* If not, try to keep individual objects small. */ + /* This is now really controlled at startup, */ + /* through GC_all_interior_pointers. */ + +EXTERN_C_BEGIN + +#ifndef GC_NO_FINALIZATION +# define GC_INVOKE_FINALIZERS() GC_notify_or_invoke_finalizers() + GC_INNER void GC_notify_or_invoke_finalizers(void); + /* If GC_finalize_on_demand is not set, invoke */ + /* eligible finalizers. Otherwise: */ + /* Call *GC_finalizer_notifier if there are */ + /* finalizers to be run, and we haven't called */ + /* this procedure yet this GC cycle. */ + + GC_INNER void GC_finalize(void); + /* Perform all indicated finalization actions */ + /* on unmarked objects. */ + /* Unreachable finalizable objects are enqueued */ + /* for processing by GC_invoke_finalizers. */ + /* Invoked with the allocator lock. */ + +# ifndef GC_TOGGLE_REFS_NOT_NEEDED + GC_INNER void GC_process_togglerefs(void); + /* Process the toggle-refs before GC starts. */ +# endif +# ifndef SMALL_CONFIG + GC_INNER void GC_print_finalization_stats(void); +# endif +#else +# define GC_INVOKE_FINALIZERS() (void)0 +#endif /* GC_NO_FINALIZATION */ + +#if !defined(DONT_ADD_BYTE_AT_END) +# ifdef LINT2 + /* Explicitly instruct the code analysis tool that */ + /* GC_all_interior_pointers is assumed to have only 0 or 1 value. */ +# define EXTRA_BYTES ((size_t)(GC_all_interior_pointers? 1 : 0)) +# else +# define EXTRA_BYTES (size_t)GC_all_interior_pointers +# endif +# define MAX_EXTRA_BYTES 1 +#else +# define EXTRA_BYTES 0 +# define MAX_EXTRA_BYTES 0 +#endif + +# ifdef LARGE_CONFIG +# define MINHINCR 64 +# define MAXHINCR 4096 +# else +# define MINHINCR 16 /* Minimum heap increment, in blocks of HBLKSIZE. */ + /* Note: must be multiple of largest page size. */ +# define MAXHINCR 2048 /* Maximum heap increment, in blocks. */ +# endif /* !LARGE_CONFIG */ + +# define BL_LIMIT GC_black_list_spacing + /* If we need a block of N bytes, and we have */ + /* a block of N + BL_LIMIT bytes available, */ + /* and N > BL_LIMIT, */ + /* but all possible positions in it are */ + /* blacklisted, we just use it anyway (and */ + /* print a warning, if warnings are enabled). */ + /* This risks subsequently leaking the block */ + /* due to a false reference. But not using */ + /* the block risks unreasonable immediate */ + /* heap growth. */ + +/*********************************/ +/* */ +/* Stack saving for debugging */ +/* */ +/*********************************/ + +#ifdef NEED_CALLINFO + struct callinfo { + word ci_pc; /* pc of caller, not callee */ +# if NARGS > 0 + GC_hidden_pointer ci_arg[NARGS]; /* hide to avoid retention */ +# endif +# if (NFRAMES * (NARGS + 1)) % 2 == 1 + /* Likely alignment problem. */ + word ci_dummy; +# endif + }; + +# ifdef SAVE_CALL_CHAIN + /* Fill in the pc and argument information for up to NFRAMES of my */ + /* callers. Ignore my frame and my callers frame. */ + GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]); +# endif + + GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]); +#endif /* NEED_CALLINFO */ + +EXTERN_C_END + +/*********************************/ +/* */ +/* OS interface routines */ +/* */ +/*********************************/ + +#ifndef NO_CLOCK +#ifdef BSD_TIME +# undef CLOCK_TYPE +# undef GET_TIME +# undef MS_TIME_DIFF +# define CLOCK_TYPE struct timeval +# define CLOCK_TYPE_INITIALIZER { 0, 0 } +# define GET_TIME(x) \ + do { \ + struct rusage rusage; \ + getrusage(RUSAGE_SELF, &rusage); \ + x = rusage.ru_utime; \ + } while (0) +# define MS_TIME_DIFF(a,b) ((unsigned long)((long)(a.tv_sec-b.tv_sec) * 1000 \ + + (long)(a.tv_usec - b.tv_usec) / 1000 \ + - (a.tv_usec < b.tv_usec \ + && (long)(a.tv_usec - b.tv_usec) % 1000 != 0 ? 1 : 0))) + /* "a" time is expected to be not earlier than */ + /* "b" one; the result has unsigned long type. */ +# define NS_FRAC_TIME_DIFF(a, b) ((unsigned long) \ + ((a.tv_usec < b.tv_usec \ + && (long)(a.tv_usec - b.tv_usec) % 1000 != 0 ? 1000L : 0) \ + + (long)(a.tv_usec - b.tv_usec) % 1000) * 1000) + /* The total time difference could be computed as */ + /* MS_TIME_DIFF(a,b)*1000000+NS_FRAC_TIME_DIFF(a,b).*/ + +#elif defined(MSWIN32) || defined(MSWINCE) || defined(WINXP_USE_PERF_COUNTER) +# if defined(MSWINRT_FLAVOR) || defined(WINXP_USE_PERF_COUNTER) +# define CLOCK_TYPE ULONGLONG +# define GET_TIME(x) \ + do { \ + LARGE_INTEGER freq, tc; \ + if (!QueryPerformanceFrequency(&freq)) \ + ABORT("QueryPerformanceFrequency requires WinXP+"); \ + /* Note: two standalone if statements are needed to */ \ + /* avoid MS VC false warning about potentially */ \ + /* uninitialized tc variable. */ \ + if (!QueryPerformanceCounter(&tc)) \ + ABORT("QueryPerformanceCounter failed"); \ + x = (CLOCK_TYPE)((double)tc.QuadPart/freq.QuadPart * 1e9); \ + } while (0) + /* TODO: Call QueryPerformanceFrequency once at GC init. */ +# define MS_TIME_DIFF(a, b) ((unsigned long)(((a) - (b)) / 1000000UL)) +# define NS_FRAC_TIME_DIFF(a, b) ((unsigned long)(((a) - (b)) % 1000000UL)) +# else +# define CLOCK_TYPE DWORD +# define GET_TIME(x) (void)(x = GetTickCount()) +# define MS_TIME_DIFF(a, b) ((unsigned long)((a) - (b))) +# define NS_FRAC_TIME_DIFF(a, b) 0UL +# endif /* !WINXP_USE_PERF_COUNTER */ + +#elif defined(NN_PLATFORM_CTR) +# define CLOCK_TYPE long long + EXTERN_C_BEGIN + CLOCK_TYPE n3ds_get_system_tick(void); + CLOCK_TYPE n3ds_convert_tick_to_ms(CLOCK_TYPE tick); + EXTERN_C_END +# define GET_TIME(x) (void)(x = n3ds_get_system_tick()) +# define MS_TIME_DIFF(a,b) ((unsigned long)n3ds_convert_tick_to_ms((a)-(b))) +# define NS_FRAC_TIME_DIFF(a, b) 0UL /* TODO: implement it */ + +#elif defined(HAVE_CLOCK_GETTIME) +# include +# define CLOCK_TYPE struct timespec +# define CLOCK_TYPE_INITIALIZER { 0, 0 } +# if defined(_POSIX_MONOTONIC_CLOCK) && !defined(NINTENDO_SWITCH) +# define GET_TIME(x) \ + do { \ + if (clock_gettime(CLOCK_MONOTONIC, &x) == -1) \ + ABORT("clock_gettime failed"); \ + } while (0) +# else +# define GET_TIME(x) \ + do { \ + if (clock_gettime(CLOCK_REALTIME, &x) == -1) \ + ABORT("clock_gettime failed"); \ + } while (0) +# endif +# define MS_TIME_DIFF(a, b) \ + /* a.tv_nsec - b.tv_nsec is in range -1e9 to 1e9 exclusively */ \ + ((unsigned long)((a).tv_nsec + (1000000L*1000 - (b).tv_nsec)) / 1000000UL \ + + ((unsigned long)((a).tv_sec - (b).tv_sec) * 1000UL) - 1000UL) +# define NS_FRAC_TIME_DIFF(a, b) \ + ((unsigned long)((a).tv_nsec + (1000000L*1000 - (b).tv_nsec)) % 1000000UL) + +#else /* !BSD_TIME && !LINUX && !NN_PLATFORM_CTR && !MSWIN32 */ +# include +# if defined(FREEBSD) && !defined(CLOCKS_PER_SEC) +# include +# define CLOCKS_PER_SEC CLK_TCK +# endif +# if !defined(CLOCKS_PER_SEC) +# define CLOCKS_PER_SEC 1000000 + /* This is technically a bug in the implementation. */ + /* ANSI requires that CLOCKS_PER_SEC be defined. But at least */ + /* under SunOS 4.1.1, it isn't. Also note that the combination of */ + /* ANSI C and POSIX is incredibly gross here. The type clock_t */ + /* is used by both clock() and times(). But on some machines */ + /* these use different notions of a clock tick, CLOCKS_PER_SEC */ + /* seems to apply only to clock. Hence we use it here. On many */ + /* machines, including SunOS, clock actually uses units of */ + /* microseconds (which are not really clock ticks). */ +# endif +# define CLOCK_TYPE clock_t +# define GET_TIME(x) (void)(x = clock()) +# define MS_TIME_DIFF(a,b) (CLOCKS_PER_SEC % 1000 == 0 ? \ + (unsigned long)((a) - (b)) / (unsigned long)(CLOCKS_PER_SEC / 1000) \ + : ((unsigned long)((a) - (b)) * 1000) / (unsigned long)CLOCKS_PER_SEC) + /* Avoid using double type since some targets (like ARM) might */ + /* require -lm option for double-to-long conversion. */ +# define NS_FRAC_TIME_DIFF(a, b) (CLOCKS_PER_SEC <= 1000 ? 0UL \ + : (unsigned long)(CLOCKS_PER_SEC <= (clock_t)1000000UL \ + ? (((a) - (b)) * ((clock_t)1000000UL / CLOCKS_PER_SEC) % 1000) * 1000 \ + : (CLOCKS_PER_SEC <= (clock_t)1000000UL * 1000 \ + ? ((a) - (b)) * ((clock_t)1000000UL * 1000 / CLOCKS_PER_SEC) \ + : (((a) - (b)) * (clock_t)1000000UL * 1000) / CLOCKS_PER_SEC) \ + % (clock_t)1000000UL)) +#endif /* !BSD_TIME && !MSWIN32 */ +# ifndef CLOCK_TYPE_INITIALIZER + /* This is used to initialize CLOCK_TYPE variables (to some value) */ + /* to avoid "variable might be uninitialized" compiler warnings. */ +# define CLOCK_TYPE_INITIALIZER 0 +# endif +#endif /* !NO_CLOCK */ + +/* We use bzero and bcopy internally. They may not be available. */ +# if defined(SPARC) && defined(SUNOS4) \ + || (defined(M68K) && defined(NEXT)) || defined(VAX) +# define BCOPY_EXISTS +# elif defined(AMIGA) || defined(DARWIN) +# include +# define BCOPY_EXISTS +# elif defined(MACOS) && defined(POWERPC) +# include +# define bcopy(x,y,n) BlockMoveData(x, y, n) +# define bzero(x,n) BlockZero(x, n) +# define BCOPY_EXISTS +# endif + +# if !defined(BCOPY_EXISTS) || defined(CPPCHECK) +# include +# define BCOPY(x,y,n) memcpy(y, x, (size_t)(n)) +# define BZERO(x,n) memset(x, 0, (size_t)(n)) +# else +# define BCOPY(x,y,n) bcopy((void *)(x),(void *)(y),(size_t)(n)) +# define BZERO(x,n) bzero((void *)(x),(size_t)(n)) +# endif + +#ifdef PCR +# include "th/PCR_ThCtl.h" +#endif + +EXTERN_C_BEGIN + +#if defined(CPPCHECK) && defined(ANY_MSWIN) +# undef TEXT +# ifdef UNICODE +# define TEXT(s) L##s +# else +# define TEXT(s) s +# endif +#endif /* CPPCHECK && ANY_MSWIN */ + +/* + * Stop and restart mutator threads. + */ +# ifdef PCR +# define STOP_WORLD() \ + PCR_ThCtl_SetExclusiveMode(PCR_ThCtl_ExclusiveMode_stopNormal, \ + PCR_allSigsBlocked, \ + PCR_waitForever) +# define START_WORLD() \ + PCR_ThCtl_SetExclusiveMode(PCR_ThCtl_ExclusiveMode_null, \ + PCR_allSigsBlocked, \ + PCR_waitForever) +# else +# if defined(NN_PLATFORM_CTR) || defined(NINTENDO_SWITCH) \ + || defined(GC_WIN32_THREADS) || defined(GC_PTHREADS) + GC_INNER void GC_stop_world(void); + GC_INNER void GC_start_world(void); +# define STOP_WORLD() GC_stop_world() +# define START_WORLD() GC_start_world() +# else + /* Just do a sanity check: we are not inside GC_do_blocking(). */ +# define STOP_WORLD() GC_ASSERT(GC_blocked_sp == NULL) +# define START_WORLD() +# endif +# endif + +/* Abandon ship */ +# if defined(SMALL_CONFIG) || defined(PCR) +# define GC_on_abort(msg) (void)0 /* be silent on abort */ +# else + GC_API_PRIV GC_abort_func GC_on_abort; +# endif +# if defined(CPPCHECK) +# define ABORT(msg) { GC_on_abort(msg); abort(); } +# elif defined(PCR) +# define ABORT(s) PCR_Base_Panic(s) +# else +# if defined(MSWIN_XBOX1) && !defined(DebugBreak) +# define DebugBreak() __debugbreak() +# elif defined(MSWINCE) && !defined(DebugBreak) \ + && (!defined(UNDER_CE) || (defined(__MINGW32CE__) && !defined(ARM32))) + /* This simplifies linking for WinCE (and, probably, doesn't */ + /* hurt debugging much); use -DDebugBreak=DebugBreak to override */ + /* this behavior if really needed. This is also a workaround for */ + /* x86mingw32ce toolchain (if it is still declaring DebugBreak() */ + /* instead of defining it as a macro). */ +# define DebugBreak() _exit(-1) /* there is no abort() in WinCE */ +# endif +# if defined(MSWIN32) && (defined(NO_DEBUGGING) || defined(LINT2)) + /* A more user-friendly abort after showing fatal message. */ +# define ABORT(msg) (GC_on_abort(msg), _exit(-1)) + /* Exit on error without running "at-exit" callbacks. */ +# elif defined(MSWINCE) && defined(NO_DEBUGGING) +# define ABORT(msg) (GC_on_abort(msg), ExitProcess(-1)) +# elif defined(MSWIN32) || defined(MSWINCE) +# if defined(_CrtDbgBreak) && defined(_DEBUG) && defined(_MSC_VER) +# define ABORT(msg) { GC_on_abort(msg); \ + _CrtDbgBreak() /* __debugbreak() */; } +# else +# define ABORT(msg) { GC_on_abort(msg); DebugBreak(); } + /* Note that: on a WinCE box, this could be silently */ + /* ignored (i.e., the program is not aborted); */ + /* DebugBreak is a statement in some toolchains. */ +# endif +# else +# define ABORT(msg) (GC_on_abort(msg), abort()) +# endif /* !MSWIN32 */ +# endif /* !PCR */ + +/* For abort message with 1-3 arguments. C_msg and C_fmt should be */ +/* literals. C_msg should not contain format specifiers. Arguments */ +/* should match their format specifiers. */ +#define ABORT_ARG1(C_msg, C_fmt, arg1) \ + MACRO_BLKSTMT_BEGIN \ + GC_ERRINFO_PRINTF(C_msg /* + */ C_fmt "\n", arg1); \ + ABORT(C_msg); \ + MACRO_BLKSTMT_END +#define ABORT_ARG2(C_msg, C_fmt, arg1, arg2) \ + MACRO_BLKSTMT_BEGIN \ + GC_ERRINFO_PRINTF(C_msg /* + */ C_fmt "\n", arg1, arg2); \ + ABORT(C_msg); \ + MACRO_BLKSTMT_END +#define ABORT_ARG3(C_msg, C_fmt, arg1, arg2, arg3) \ + MACRO_BLKSTMT_BEGIN \ + GC_ERRINFO_PRINTF(C_msg /* + */ C_fmt "\n", \ + arg1, arg2, arg3); \ + ABORT(C_msg); \ + MACRO_BLKSTMT_END + +/* Same as ABORT but does not have 'no-return' attribute. */ +/* ABORT on a dummy condition (which is always true). */ +#define ABORT_RET(msg) \ + if ((GC_funcptr_uint)GC_current_warn_proc == ~(GC_funcptr_uint)0) {} \ + else ABORT(msg) + +/* Exit abnormally, but without making a mess (e.g. out of memory) */ +# ifdef PCR +# define EXIT() PCR_Base_Exit(1,PCR_waitForever) +# else +# define EXIT() (GC_on_abort(NULL), exit(1 /* EXIT_FAILURE */)) +# endif + +/* Print warning message, e.g. almost out of memory. */ +/* The argument (if any) format specifier should be: */ +/* "%s", "%p", "%"WARN_PRIdPTR or "%"WARN_PRIuPTR. */ +#define WARN(msg, arg) \ + (*GC_current_warn_proc)((/* no const */ char *) \ + (word)("GC Warning: " msg), \ + (word)(arg)) +GC_EXTERN GC_warn_proc GC_current_warn_proc; + +/* Print format type macro for decimal signed_word value passed WARN(). */ +/* This could be redefined for Win64 or LLP64, but typically should */ +/* not be done as the WARN format string is, possibly, processed on the */ +/* client side, so non-standard print type modifiers (like MS "I64d") */ +/* should be avoided here if possible. */ +#ifndef WARN_PRIdPTR + /* Assume sizeof(void *) == sizeof(long) or a little-endian machine. */ +# define WARN_PRIdPTR "ld" +# define WARN_PRIuPTR "lu" +#endif + +/* A tagging macro (for a code static analyzer) to indicate that the */ +/* string obtained from an untrusted source (e.g., argv[], getenv) is */ +/* safe to use in a vulnerable operation (e.g., open, exec). */ +#define TRUSTED_STRING(s) (char*)COVERT_DATAFLOW(s) + +/* Get environment entry */ +#ifdef GC_READ_ENV_FILE + GC_INNER char * GC_envfile_getenv(const char *name); +# define GETENV(name) GC_envfile_getenv(name) +#elif defined(NO_GETENV) && !defined(CPPCHECK) +# define GETENV(name) NULL +#elif defined(EMPTY_GETENV_RESULTS) + /* Workaround for a reputed Wine bug. */ + GC_INLINE char * fixed_getenv(const char *name) + { + char *value = getenv(name); + return value != NULL && *value != '\0' ? value : NULL; + } +# define GETENV(name) fixed_getenv(name) +#else +# define GETENV(name) getenv(name) +#endif + +EXTERN_C_END + +#if defined(DARWIN) +# include +# ifndef MAC_OS_X_VERSION_MAX_ALLOWED +# include + /* Include this header just to import the above macro. */ +# endif +# if defined(POWERPC) +# if CPP_WORDSZ == 32 +# define GC_THREAD_STATE_T ppc_thread_state_t +# else +# define GC_THREAD_STATE_T ppc_thread_state64_t +# define GC_MACH_THREAD_STATE PPC_THREAD_STATE64 +# define GC_MACH_THREAD_STATE_COUNT PPC_THREAD_STATE64_COUNT +# endif +# elif defined(I386) || defined(X86_64) +# if CPP_WORDSZ == 32 +# if defined(i386_THREAD_STATE_COUNT) && !defined(x86_THREAD_STATE32_COUNT) + /* Use old naming convention for 32-bit x86. */ +# define GC_THREAD_STATE_T i386_thread_state_t +# define GC_MACH_THREAD_STATE i386_THREAD_STATE +# define GC_MACH_THREAD_STATE_COUNT i386_THREAD_STATE_COUNT +# else +# define GC_THREAD_STATE_T x86_thread_state32_t +# define GC_MACH_THREAD_STATE x86_THREAD_STATE32 +# define GC_MACH_THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT +# endif +# else +# define GC_THREAD_STATE_T x86_thread_state64_t +# define GC_MACH_THREAD_STATE x86_THREAD_STATE64 +# define GC_MACH_THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT +# endif +# elif defined(ARM32) && defined(ARM_UNIFIED_THREAD_STATE) \ + && !defined(CPPCHECK) +# define GC_THREAD_STATE_T arm_unified_thread_state_t +# define GC_MACH_THREAD_STATE ARM_UNIFIED_THREAD_STATE +# define GC_MACH_THREAD_STATE_COUNT ARM_UNIFIED_THREAD_STATE_COUNT +# elif defined(ARM32) +# define GC_THREAD_STATE_T arm_thread_state_t +# ifdef ARM_MACHINE_THREAD_STATE_COUNT +# define GC_MACH_THREAD_STATE ARM_MACHINE_THREAD_STATE +# define GC_MACH_THREAD_STATE_COUNT ARM_MACHINE_THREAD_STATE_COUNT +# endif +# elif defined(AARCH64) +# define GC_THREAD_STATE_T arm_thread_state64_t +# define GC_MACH_THREAD_STATE ARM_THREAD_STATE64 +# define GC_MACH_THREAD_STATE_COUNT ARM_THREAD_STATE64_COUNT +# elif !defined(CPPCHECK) +# error define GC_THREAD_STATE_T +# endif +# ifndef GC_MACH_THREAD_STATE +# define GC_MACH_THREAD_STATE MACHINE_THREAD_STATE +# define GC_MACH_THREAD_STATE_COUNT MACHINE_THREAD_STATE_COUNT +# endif + + /* Try to work out the right way to access thread state structure */ + /* members. The structure has changed its definition in different */ + /* Darwin versions. This now defaults to the (older) names */ + /* without __, thus hopefully, not breaking any existing */ + /* Makefile.direct builds. */ +# if __DARWIN_UNIX03 +# define THREAD_FLD_NAME(x) __ ## x +# else +# define THREAD_FLD_NAME(x) x +# endif +# if defined(ARM32) && defined(ARM_UNIFIED_THREAD_STATE) +# define THREAD_FLD(x) ts_32.THREAD_FLD_NAME(x) +# else +# define THREAD_FLD(x) THREAD_FLD_NAME(x) +# endif +#endif /* DARWIN */ + +#ifndef WASI +# include +#endif + +#include + +#if __STDC_VERSION__ >= 201112L +# include /* for static_assert */ +#endif + +EXTERN_C_BEGIN + +/*********************************/ +/* */ +/* Word-size-dependent defines */ +/* */ +/*********************************/ + +/* log[2] of CPP_WORDSZ. */ +#if CPP_WORDSZ == 32 +# define LOGWL 5 +#elif CPP_WORDSZ == 64 +# define LOGWL 6 +#endif + +#define WORDS_TO_BYTES(x) ((x) << (LOGWL-3)) +#define BYTES_TO_WORDS(x) ((x) >> (LOGWL-3)) +#define modWORDSZ(n) ((n) & (CPP_WORDSZ-1)) /* n mod size of word */ +#define divWORDSZ(n) ((n) >> LOGWL) /* divide n by size of word */ + +#define SIGNB ((word)1 << (CPP_WORDSZ-1)) +#define BYTES_PER_WORD ((word)sizeof(word)) + +#if CPP_WORDSZ / 8 != ALIGNMENT +# define UNALIGNED_PTRS +#endif + +#if GC_GRANULE_BYTES == 4 +# define BYTES_TO_GRANULES(n) ((n)>>2) +# define GRANULES_TO_BYTES(n) ((n)<<2) +# define GRANULES_TO_WORDS(n) BYTES_TO_WORDS(GRANULES_TO_BYTES(n)) +#elif GC_GRANULE_BYTES == 8 +# define BYTES_TO_GRANULES(n) ((n)>>3) +# define GRANULES_TO_BYTES(n) ((n)<<3) +# if CPP_WORDSZ == 64 +# define GRANULES_TO_WORDS(n) (n) +# elif CPP_WORDSZ == 32 +# define GRANULES_TO_WORDS(n) ((n)<<1) +# else +# define GRANULES_TO_WORDS(n) BYTES_TO_WORDS(GRANULES_TO_BYTES(n)) +# endif +#elif GC_GRANULE_BYTES == 16 +# define BYTES_TO_GRANULES(n) ((n)>>4) +# define GRANULES_TO_BYTES(n) ((n)<<4) +# if CPP_WORDSZ == 64 +# define GRANULES_TO_WORDS(n) ((n)<<1) +# elif CPP_WORDSZ == 32 +# define GRANULES_TO_WORDS(n) ((n)<<2) +# else +# define GRANULES_TO_WORDS(n) BYTES_TO_WORDS(GRANULES_TO_BYTES(n)) +# endif +#else +# error Bad GC_GRANULE_BYTES value +#endif + +/*********************/ +/* */ +/* Size Parameters */ +/* */ +/*********************/ + +/* Heap block size, bytes. Should be power of 2. */ +/* Incremental GC with MPROTECT_VDB currently requires the */ +/* page size to be a multiple of HBLKSIZE. Since most modern */ +/* architectures support variable page sizes down to 4 KB, and */ +/* x86 is generally 4 KB, we now default to 4 KB, except for */ +/* Alpha: Seems to be used with 8 KB pages. */ +/* SMALL_CONFIG: Want less block-level fragmentation. */ +#ifndef HBLKSIZE +# if defined(SMALL_CONFIG) && !defined(LARGE_CONFIG) +# define CPP_LOG_HBLKSIZE 10 +# elif defined(ALPHA) +# define CPP_LOG_HBLKSIZE 13 +# else +# define CPP_LOG_HBLKSIZE 12 +# endif +#else +# if HBLKSIZE == 512 +# define CPP_LOG_HBLKSIZE 9 +# elif HBLKSIZE == 1024 +# define CPP_LOG_HBLKSIZE 10 +# elif HBLKSIZE == 2048 +# define CPP_LOG_HBLKSIZE 11 +# elif HBLKSIZE == 4096 +# define CPP_LOG_HBLKSIZE 12 +# elif HBLKSIZE == 8192 +# define CPP_LOG_HBLKSIZE 13 +# elif HBLKSIZE == 16384 +# define CPP_LOG_HBLKSIZE 14 +# elif HBLKSIZE == 32768 +# define CPP_LOG_HBLKSIZE 15 +# elif HBLKSIZE == 65536 +# define CPP_LOG_HBLKSIZE 16 +# elif !defined(CPPCHECK) +# error Bad HBLKSIZE value +# endif +# undef HBLKSIZE +#endif + +# define CPP_HBLKSIZE (1 << CPP_LOG_HBLKSIZE) +# define LOG_HBLKSIZE ((size_t)CPP_LOG_HBLKSIZE) +# define HBLKSIZE ((size_t)CPP_HBLKSIZE) + +#define GC_SQRT_SIZE_MAX ((((size_t)1) << (CPP_WORDSZ / 2)) - 1) + +/* Max size objects supported by freelist (larger objects are */ +/* allocated directly with allchblk(), by rounding to the next */ +/* multiple of HBLKSIZE). */ +#define CPP_MAXOBJBYTES (CPP_HBLKSIZE/2) +#define MAXOBJBYTES ((size_t)CPP_MAXOBJBYTES) +#define CPP_MAXOBJWORDS BYTES_TO_WORDS(CPP_MAXOBJBYTES) +#define MAXOBJWORDS ((size_t)CPP_MAXOBJWORDS) +#define CPP_MAXOBJGRANULES BYTES_TO_GRANULES(CPP_MAXOBJBYTES) +#define MAXOBJGRANULES ((size_t)CPP_MAXOBJGRANULES) + +# define divHBLKSZ(n) ((n) >> LOG_HBLKSIZE) + +# define HBLK_PTR_DIFF(p,q) divHBLKSZ((ptr_t)p - (ptr_t)q) + /* Equivalent to subtracting 2 hblk pointers. */ + /* We do it this way because a compiler should */ + /* find it hard to use an integer division */ + /* instead of a shift. The bundled SunOS 4.1 */ + /* o.w. sometimes pessimizes the subtraction to */ + /* involve a call to .div. */ + +# define modHBLKSZ(n) ((n) & (HBLKSIZE-1)) + +# define HBLKPTR(objptr) ((struct hblk *)(((word)(objptr)) \ + & ~(word)(HBLKSIZE-1))) +# define HBLKDISPL(objptr) modHBLKSZ((size_t)(objptr)) + +/* Round up allocation size (in bytes) to a multiple of a granule. */ +#define ROUNDUP_GRANULE_SIZE(lb) /* lb should have no side-effect */ \ + (SIZET_SAT_ADD(lb, GC_GRANULE_BYTES-1) \ + & ~(size_t)(GC_GRANULE_BYTES-1)) + +/* Round up byte allocation request (after adding EXTRA_BYTES) to */ +/* a multiple of a granule, then convert it to granules. */ +#define ALLOC_REQUEST_GRANS(lb) /* lb should have no side-effect */ \ + BYTES_TO_GRANULES(SIZET_SAT_ADD(lb, GC_GRANULE_BYTES-1 + EXTRA_BYTES)) + +#if MAX_EXTRA_BYTES == 0 +# define ADD_EXTRA_BYTES(lb) (lb) +# define SMALL_OBJ(bytes) EXPECT((bytes) <= MAXOBJBYTES, TRUE) +#else +# define ADD_EXTRA_BYTES(lb) /* lb should have no side-effect */ \ + SIZET_SAT_ADD(lb, EXTRA_BYTES) +# define SMALL_OBJ(bytes) /* bytes argument should have no side-effect */ \ + (EXPECT((bytes) <= MAXOBJBYTES - MAX_EXTRA_BYTES, TRUE) \ + || (bytes) <= MAXOBJBYTES - EXTRA_BYTES) + /* This really just tests bytes <= MAXOBJBYTES - EXTRA_BYTES. */ + /* But we try to avoid looking up EXTRA_BYTES. */ +#endif + +/* Hash table representation of sets of pages. Implements a map from */ +/* aligned HBLKSIZE chunks of the address space to one bit each. */ +/* This assumes it is OK to spuriously set bits, e.g. because multiple */ +/* addresses are represented by a single location. Used by */ +/* black-listing code, and perhaps by dirty bit maintenance code. */ +#ifndef LOG_PHT_ENTRIES +# ifdef LARGE_CONFIG +# if CPP_WORDSZ == 32 +# define LOG_PHT_ENTRIES 20 /* Collisions are impossible (because */ + /* of a 4 GB space limit). Each table */ + /* takes 128 KB, some of which may */ + /* never be touched. */ +# else +# define LOG_PHT_ENTRIES 21 /* Collisions likely at 2M blocks, */ + /* which is >= 8 GB. Each table takes */ + /* 256 KB, some of which may never be */ + /* touched. */ +# endif +# elif !defined(SMALL_CONFIG) +# define LOG_PHT_ENTRIES 18 /* Collisions are likely if heap grows */ + /* to more than 256K hblks >= 1 GB. */ + /* Each hash table occupies 32 KB. */ + /* Even for somewhat smaller heaps, */ + /* say half that, collisions may be an */ + /* issue because we blacklist */ + /* addresses outside the heap. */ +# else +# define LOG_PHT_ENTRIES 15 /* Collisions are likely if heap grows */ + /* to more than 32K hblks (128 MB). */ + /* Each hash table occupies 4 KB. */ +# endif +#endif /* !LOG_PHT_ENTRIES */ + +# define PHT_ENTRIES ((word)1 << LOG_PHT_ENTRIES) +# define PHT_SIZE (LOG_PHT_ENTRIES > LOGWL ? PHT_ENTRIES >> LOGWL : 1) +typedef word page_hash_table[PHT_SIZE]; + +# define PHT_HASH(addr) ((((word)(addr)) >> LOG_HBLKSIZE) & (PHT_ENTRIES - 1)) + +# define get_pht_entry_from_index(bl, index) \ + (((bl)[divWORDSZ(index)] >> modWORDSZ(index)) & 1) +# define set_pht_entry_from_index(bl, index) \ + (void)((bl)[divWORDSZ(index)] |= (word)1 << modWORDSZ(index)) + +#if defined(THREADS) && defined(AO_HAVE_or) + /* And, one more version for GC_add_to_black_list_normal/stack */ + /* (invoked indirectly by GC_do_local_mark) and */ + /* async_set_pht_entry_from_index (invoked by GC_dirty or the write */ + /* fault handler). */ +# define set_pht_entry_from_index_concurrent(bl, index) \ + AO_or((volatile AO_t *)&(bl)[divWORDSZ(index)], \ + (AO_t)((word)1 << modWORDSZ(index))) +# ifdef MPROTECT_VDB +# define set_pht_entry_from_index_concurrent_volatile(bl, index) \ + set_pht_entry_from_index_concurrent(bl, index) +# endif +#else +# define set_pht_entry_from_index_concurrent(bl, index) \ + set_pht_entry_from_index(bl, index) +# ifdef MPROTECT_VDB + /* Same as set_pht_entry_from_index() but avoiding the compound */ + /* assignment for a volatile array. */ +# define set_pht_entry_from_index_concurrent_volatile(bl, index) \ + (void)((bl)[divWORDSZ(index)] \ + = (bl)[divWORDSZ(index)] | ((word)1 << modWORDSZ(index))) +# endif +#endif + +/********************************************/ +/* */ +/* H e a p B l o c k s */ +/* */ +/********************************************/ + +#define MARK_BITS_PER_HBLK (HBLKSIZE/GC_GRANULE_BYTES) + /* The upper bound. We allocate 1 bit per allocation */ + /* granule. If MARK_BIT_PER_OBJ is not defined, we use */ + /* every n-th bit, where n is the number of allocation */ + /* granules per object. Otherwise, we only use the */ + /* initial group of mark bits, and it is safe to */ + /* allocate smaller header for large objects. */ + +union word_ptr_ao_u { + word w; + signed_word sw; + void *vp; +# ifdef PARALLEL_MARK + volatile AO_t ao; +# endif +}; + +/* We maintain layout maps for heap blocks containing objects of a given */ +/* size. Each entry in this map describes a byte offset and has the */ +/* following type. */ +struct hblkhdr { + struct hblk * hb_next; /* Link field for hblk free list */ + /* and for lists of chunks waiting to be */ + /* reclaimed. */ + struct hblk * hb_prev; /* Backwards link for free list. */ + struct hblk * hb_block; /* The corresponding block. */ + unsigned char hb_obj_kind; + /* Kind of objects in the block. Each kind */ + /* identifies a mark procedure and a set of */ + /* list headers. Sometimes called regions. */ + unsigned char hb_flags; +# define IGNORE_OFF_PAGE 1 /* Ignore pointers that do not */ + /* point to the first hblk of */ + /* this object. */ +# define WAS_UNMAPPED 2 /* This is a free block, which has */ + /* been unmapped from the address */ + /* space. */ + /* GC_remap must be invoked on it */ + /* before it can be reallocated. */ + /* Only set with USE_MUNMAP. */ +# define FREE_BLK 4 /* Block is free, i.e. not in use. */ +# ifdef ENABLE_DISCLAIM +# define HAS_DISCLAIM 8 + /* This kind has a callback on reclaim. */ +# define MARK_UNCONDITIONALLY 0x10 + /* Mark from all objects, marked or */ + /* not. Used to mark objects needed by */ + /* reclaim notifier. */ +# endif +# ifndef MARK_BIT_PER_OBJ +# define LARGE_BLOCK 0x20 +# endif + unsigned short hb_last_reclaimed; + /* Value of GC_gc_no when block was */ + /* last allocated or swept. May wrap. */ + /* For a free block, this is maintained */ + /* only for USE_MUNMAP, and indicates */ + /* when the header was allocated, or */ + /* when the size of the block last */ + /* changed. */ +# ifdef MARK_BIT_PER_OBJ + unsigned32 hb_inv_sz; /* A good upper bound for 2**32/hb_sz. */ + /* For large objects, we use */ + /* LARGE_INV_SZ. */ +# define LARGE_INV_SZ ((unsigned32)1 << 16) +# endif + word hb_sz; /* If in use, size in bytes, of objects in the block. */ + /* if free, the size in bytes of the whole block. */ + /* We assume that this is convertible to signed_word */ + /* without generating a negative result. We avoid */ + /* generating free blocks larger than that. */ + word hb_descr; /* object descriptor for marking. See */ + /* gc_mark.h. */ +# ifndef MARK_BIT_PER_OBJ + unsigned short * hb_map; /* Essentially a table of remainders */ + /* mod BYTES_TO_GRANULES(hb_sz), except */ + /* for large blocks. See GC_obj_map. */ +# endif +# ifdef PARALLEL_MARK + volatile AO_t hb_n_marks; /* Number of set mark bits, excluding */ + /* the one always set at the end. */ + /* Currently it is concurrently */ + /* updated and hence only approximate. */ + /* But a zero value does guarantee that */ + /* the block contains no marked */ + /* objects. */ + /* Ensuring this property means that we */ + /* never decrement it to zero during a */ + /* collection, and hence the count may */ + /* be one too high. Due to concurrent */ + /* updates, an arbitrary number of */ + /* increments, but not all of them (!) */ + /* may be lost, hence it may in theory */ + /* be much too low. */ + /* The count may also be too high if */ + /* multiple mark threads mark the */ + /* same object due to a race. */ +# else + size_t hb_n_marks; /* Without parallel marking, the count */ + /* is accurate. */ +# endif +# ifdef USE_MARK_BYTES +# define MARK_BITS_SZ (MARK_BITS_PER_HBLK + 1) + /* Unlike the other case, this is in units of bytes. */ + /* Since we force double-word alignment, we need at most one */ + /* mark bit per 2 words. But we do allocate and set one */ + /* extra mark bit to avoid an explicit check for the */ + /* partial object at the end of each block. */ + union { + char _hb_marks[MARK_BITS_SZ]; + /* The i'th byte is 1 if the object */ + /* starting at granule i or object i is */ + /* marked, 0 otherwise. */ + /* The mark bit for the "one past the end" */ + /* object is always set to avoid a special */ + /* case test in the marker. */ + word dummy; /* Force word alignment of mark bytes. */ + } _mark_byte_union; +# define hb_marks _mark_byte_union._hb_marks +# else +# define MARK_BITS_SZ (MARK_BITS_PER_HBLK/CPP_WORDSZ + 1) + word hb_marks[MARK_BITS_SZ]; +# endif /* !USE_MARK_BYTES */ +}; + +# define ANY_INDEX 23 /* "Random" mark bit index for assertions */ + +/* heap block body */ + +# define HBLK_WORDS (HBLKSIZE/sizeof(word)) +# define HBLK_GRANULES (HBLKSIZE/GC_GRANULE_BYTES) + +/* The number of objects in a block dedicated to a certain size. */ +/* may erroneously yield zero (instead of one) for large objects. */ +# define HBLK_OBJS(sz_in_bytes) (HBLKSIZE/(sz_in_bytes)) + +struct hblk { + char hb_body[HBLKSIZE]; +}; + +# define HBLK_IS_FREE(hdr) (((hdr) -> hb_flags & FREE_BLK) != 0) + +# define OBJ_SZ_TO_BLOCKS(lb) divHBLKSZ((lb) + HBLKSIZE-1) +# define OBJ_SZ_TO_BLOCKS_CHECKED(lb) /* lb should have no side-effect */ \ + divHBLKSZ(SIZET_SAT_ADD(lb, HBLKSIZE-1)) + /* Size of block (in units of HBLKSIZE) needed to hold objects of */ + /* given lb (in bytes). The checked variant prevents wrap around. */ + +/* Object free list link */ +# define obj_link(p) (*(void **)(p)) + +# define LOG_MAX_MARK_PROCS 6 +# define MAX_MARK_PROCS (1 << LOG_MAX_MARK_PROCS) + +/* Root sets. Logically private to mark_rts.c. But we don't want the */ +/* tables scanned, so we put them here. */ +/* MAX_ROOT_SETS is the maximum number of ranges that can be */ +/* registered as static roots. */ +# ifdef LARGE_CONFIG +# define MAX_ROOT_SETS 8192 +# elif !defined(SMALL_CONFIG) +# define MAX_ROOT_SETS 2048 +# else +# define MAX_ROOT_SETS 512 +# endif + +# define MAX_EXCLUSIONS (MAX_ROOT_SETS/4) +/* Maximum number of segments that can be excluded from root sets. */ + +/* + * Data structure for excluded static roots. + */ +struct exclusion { + ptr_t e_start; + ptr_t e_end; +}; + +/* Data structure for list of root sets. */ +/* We keep a hash table, so that we can filter out duplicate additions. */ +/* Under Win32, we need to do a better job of filtering overlaps, so */ +/* we resort to sequential search, and pay the price. */ +struct roots { + ptr_t r_start;/* multiple of word size */ + ptr_t r_end; /* multiple of word size and greater than r_start */ +# ifndef ANY_MSWIN + struct roots * r_next; +# endif + GC_bool r_tmp; + /* Delete before registering new dynamic libraries */ +}; + +#ifndef ANY_MSWIN + /* Size of hash table index to roots. */ +# define LOG_RT_SIZE 6 +# define RT_SIZE (1 << LOG_RT_SIZE) /* Power of 2, may be != MAX_ROOT_SETS */ +#endif /* !ANY_MSWIN */ + +#if (!defined(MAX_HEAP_SECTS) || defined(CPPCHECK)) \ + && (defined(ANY_MSWIN) || defined(USE_PROC_FOR_LIBRARIES)) +# ifdef LARGE_CONFIG +# if CPP_WORDSZ > 32 +# define MAX_HEAP_SECTS 81920 +# else +# define MAX_HEAP_SECTS 7680 +# endif +# elif defined(SMALL_CONFIG) && !defined(USE_PROC_FOR_LIBRARIES) +# if defined(PARALLEL_MARK) && (defined(MSWIN32) || defined(CYGWIN32)) +# define MAX_HEAP_SECTS 384 +# else +# define MAX_HEAP_SECTS 128 /* Roughly 256 MB (128*2048*1024) */ +# endif +# elif CPP_WORDSZ > 32 +# define MAX_HEAP_SECTS 1024 /* Roughly 8 GB */ +# else +# define MAX_HEAP_SECTS 512 /* Roughly 4 GB */ +# endif +#endif /* !MAX_HEAP_SECTS */ + +typedef struct GC_ms_entry { + ptr_t mse_start; /* First word of object, word aligned. */ + union word_ptr_ao_u mse_descr; + /* Descriptor; low order two bits are tags, */ + /* as described in gc_mark.h. */ +} mse; + +typedef int mark_state_t; /* Current state of marking. */ + /* Used to remember where we are during */ + /* concurrent marking. */ + +struct disappearing_link; +struct finalizable_object; + +struct dl_hashtbl_s { + struct disappearing_link **head; + word entries; + unsigned log_size; +}; + +struct fnlz_roots_s { + struct finalizable_object **fo_head; + /* List of objects that should be finalized now: */ + struct finalizable_object *finalize_now; +}; + +union toggle_ref_u { + /* The least significant bit is used to distinguish between choices. */ + void *strong_ref; + GC_hidden_pointer weak_ref; +}; + +/* Extended descriptors. GC_typed_mark_proc understands these. */ +/* These are used for simple objects that are larger than what */ +/* can be described by a BITMAP_BITS sized bitmap. */ +typedef struct { + word ed_bitmap; /* the least significant bit corresponds to first word. */ + GC_bool ed_continued; /* next entry is continuation. */ +} typed_ext_descr_t; + +struct HeapSect { + ptr_t hs_start; + size_t hs_bytes; +}; + +/* Lists of all heap blocks and free lists */ +/* as well as other random data structures */ +/* that should not be scanned by the */ +/* collector. */ +/* These are grouped together in a struct */ +/* so that they can be easily skipped by the */ +/* GC_mark routine. */ +/* The ordering is weird to make GC_malloc */ +/* faster by keeping the important fields */ +/* sufficiently close together that a */ +/* single load of a base register will do. */ +/* Scalars that could easily appear to */ +/* be pointers are also put here. */ +/* The main fields should precede any */ +/* conditionally included fields, so that */ +/* gc_inline.h will work even if a different */ +/* set of macros is defined when the client is */ +/* compiled. */ + +struct _GC_arrays { + word _heapsize; /* Heap size in bytes (value never goes down). */ + word _requested_heapsize; /* Heap size due to explicit expansion. */ +# define GC_heapsize_on_gc_disable GC_arrays._heapsize_on_gc_disable + word _heapsize_on_gc_disable; + ptr_t _last_heap_addr; + word _large_free_bytes; + /* Total bytes contained in blocks on large object free */ + /* list. */ + word _large_allocd_bytes; + /* Total number of bytes in allocated large objects blocks. */ + /* For the purposes of this counter and the next one only, a */ + /* large object is one that occupies a block of at least */ + /* 2*HBLKSIZE. */ + word _max_large_allocd_bytes; + /* Maximum number of bytes that were ever allocated in */ + /* large object blocks. This is used to help decide when it */ + /* is safe to split up a large block. */ + word _bytes_allocd_before_gc; + /* Number of bytes allocated before this */ + /* collection cycle. */ +# define GC_our_mem_bytes GC_arrays._our_mem_bytes + word _our_mem_bytes; +# ifndef SEPARATE_GLOBALS +# define GC_bytes_allocd GC_arrays._bytes_allocd + word _bytes_allocd; + /* Number of bytes allocated during this collection cycle. */ +# endif + word _bytes_dropped; + /* Number of black-listed bytes dropped during GC cycle */ + /* as a result of repeated scanning during allocation */ + /* attempts. These are treated largely as allocated, */ + /* even though they are not useful to the client. */ + word _bytes_finalized; + /* Approximate number of bytes in objects (and headers) */ + /* that became ready for finalization in the last */ + /* collection. */ + word _bytes_freed; + /* Number of explicitly deallocated bytes of memory */ + /* since last collection. */ + word _finalizer_bytes_freed; + /* Bytes of memory explicitly deallocated while */ + /* finalizers were running. Used to approximate memory */ + /* explicitly deallocated by finalizers. */ + bottom_index *_all_bottom_indices; + /* Pointer to the first (lowest address) bottom_index; */ + /* assumes the allocator lock is held. */ + bottom_index *_all_bottom_indices_end; + /* Pointer to the last (highest address) bottom_index; */ + /* assumes the allocator lock is held. */ + ptr_t _scratch_free_ptr; + hdr *_hdr_free_list; + ptr_t _scratch_end_ptr; + /* GC_scratch_end_ptr is end point of the current scratch area. */ +# if defined(IRIX5) || (defined(USE_PROC_FOR_LIBRARIES) && !defined(LINUX)) +# define USE_SCRATCH_LAST_END_PTR +# define GC_scratch_last_end_ptr GC_arrays._scratch_last_end_ptr + ptr_t _scratch_last_end_ptr; + /* GC_scratch_last_end_ptr is the end point of the last */ + /* obtained scratch area. */ + /* Used by GC_register_dynamic_libraries(). */ +# endif +# if defined(GC_ASSERTIONS) || defined(MAKE_BACK_GRAPH) \ + || defined(INCLUDE_LINUX_THREAD_DESCR) \ + || (defined(KEEP_BACK_PTRS) && ALIGNMENT == 1) +# define SET_REAL_HEAP_BOUNDS +# define GC_least_real_heap_addr GC_arrays._least_real_heap_addr +# define GC_greatest_real_heap_addr GC_arrays._greatest_real_heap_addr + word _least_real_heap_addr; + word _greatest_real_heap_addr; + /* Similar to GC_least/greatest_plausible_heap_addr but */ + /* do not include future (potential) heap expansion. */ + /* Both variables are zero initially. */ +# endif + mse *_mark_stack; + /* Limits of stack for GC_mark routine. All ranges */ + /* between GC_mark_stack (incl.) and GC_mark_stack_top */ + /* (incl.) still need to be marked from. */ + mse *_mark_stack_limit; +# ifdef PARALLEL_MARK + mse *volatile _mark_stack_top; + /* Updated only with the mark lock held, but read asynchronously. */ + /* TODO: Use union to avoid casts to AO_t */ +# else + mse *_mark_stack_top; +# endif +# ifdef DYNAMIC_POINTER_MASK +# define GC_pointer_mask GC_arrays._pointer_mask +# define GC_pointer_shift GC_arrays._pointer_shift + word _pointer_mask; /* Both mask and shift are zeros by default; */ + /* if mask is zero then correct it to ~0 at GC */ + /* initialization. */ + unsigned char _pointer_shift; +# endif +# define GC_mark_stack_too_small GC_arrays._mark_stack_too_small + GC_bool _mark_stack_too_small; + /* We need a larger mark stack. May be set by */ + /* client-supplied mark routines. */ +# define GC_objects_are_marked GC_arrays._objects_are_marked + GC_bool _objects_are_marked; + /* Are there collectible marked objects in the heap? */ +# ifdef THREADS +# define GC_roots_were_cleared GC_arrays._roots_were_cleared + GC_bool _roots_were_cleared; +# endif +# define GC_explicit_typing_initialized GC_arrays._explicit_typing_initialized +# ifdef AO_HAVE_load_acquire + volatile AO_t _explicit_typing_initialized; +# else + GC_bool _explicit_typing_initialized; +# endif + word _composite_in_use; + /* Number of bytes in the accessible composite objects. */ + word _atomic_in_use; + /* Number of bytes in the accessible atomic objects. */ +# define GC_last_heap_growth_gc_no GC_arrays._last_heap_growth_gc_no + word _last_heap_growth_gc_no; + /* GC number of latest successful GC_expand_hp_inner call. */ +# ifdef USE_MUNMAP +# define GC_unmapped_bytes GC_arrays._unmapped_bytes + word _unmapped_bytes; +# ifdef COUNT_UNMAPPED_REGIONS +# define GC_num_unmapped_regions GC_arrays._num_unmapped_regions + signed_word _num_unmapped_regions; +# endif +# else +# define GC_unmapped_bytes 0 +# endif + bottom_index * _all_nils; +# define GC_scan_ptr GC_arrays._scan_ptr + struct hblk * _scan_ptr; +# ifdef PARALLEL_MARK +# define GC_main_local_mark_stack GC_arrays._main_local_mark_stack + mse *_main_local_mark_stack; +# define GC_first_nonempty GC_arrays._first_nonempty + volatile AO_t _first_nonempty; + /* Lowest entry on mark stack that may be */ + /* nonempty. Updated only by initiating thread. */ +# endif +# define GC_mark_stack_size GC_arrays._mark_stack_size + size_t _mark_stack_size; +# define GC_mark_state GC_arrays._mark_state + mark_state_t _mark_state; /* Initialized to MS_NONE (0). */ +# ifdef ENABLE_TRACE +# define GC_trace_addr GC_arrays._trace_addr + ptr_t _trace_addr; +# endif +# define GC_capacity_heap_sects GC_arrays._capacity_heap_sects + size_t _capacity_heap_sects; +# define GC_n_heap_sects GC_arrays._n_heap_sects + word _n_heap_sects; /* Number of separately added heap sections. */ +# ifdef ANY_MSWIN +# define GC_n_heap_bases GC_arrays._n_heap_bases + word _n_heap_bases; /* See GC_heap_bases. */ +# endif +# ifdef USE_PROC_FOR_LIBRARIES +# define GC_n_memory GC_arrays._n_memory + word _n_memory; /* Number of GET_MEM allocated memory sections. */ +# endif +# ifdef GC_GCJ_SUPPORT +# define GC_gcjobjfreelist GC_arrays._gcjobjfreelist + ptr_t *_gcjobjfreelist; +# endif +# define GC_fo_entries GC_arrays._fo_entries + word _fo_entries; +# ifndef GC_NO_FINALIZATION +# define GC_dl_hashtbl GC_arrays._dl_hashtbl +# define GC_fnlz_roots GC_arrays._fnlz_roots +# define GC_log_fo_table_size GC_arrays._log_fo_table_size +# ifndef GC_LONG_REFS_NOT_NEEDED +# define GC_ll_hashtbl GC_arrays._ll_hashtbl + struct dl_hashtbl_s _ll_hashtbl; +# endif + struct dl_hashtbl_s _dl_hashtbl; + struct fnlz_roots_s _fnlz_roots; + unsigned _log_fo_table_size; +# ifndef GC_TOGGLE_REFS_NOT_NEEDED +# define GC_toggleref_arr GC_arrays._toggleref_arr +# define GC_toggleref_array_size GC_arrays._toggleref_array_size +# define GC_toggleref_array_capacity GC_arrays._toggleref_array_capacity + union toggle_ref_u *_toggleref_arr; + size_t _toggleref_array_size; + size_t _toggleref_array_capacity; +# endif +# endif +# ifdef TRACE_BUF +# define GC_trace_buf_ptr GC_arrays._trace_buf_ptr + int _trace_buf_ptr; +# endif +# ifdef ENABLE_DISCLAIM +# define GC_finalized_kind GC_arrays._finalized_kind + unsigned _finalized_kind; +# endif +# define n_root_sets GC_arrays._n_root_sets +# define GC_excl_table_entries GC_arrays._excl_table_entries + int _n_root_sets; /* GC_static_roots[0..n_root_sets) contains the */ + /* valid root sets. */ + size_t _excl_table_entries; /* Number of entries in use. */ +# define GC_ed_size GC_arrays._ed_size +# define GC_avail_descr GC_arrays._avail_descr +# define GC_ext_descriptors GC_arrays._ext_descriptors + size_t _ed_size; /* Current size of above arrays. */ + size_t _avail_descr; /* Next available slot. */ + typed_ext_descr_t *_ext_descriptors; /* Points to array of extended */ + /* descriptors. */ + GC_mark_proc _mark_procs[MAX_MARK_PROCS]; + /* Table of user-defined mark procedures. There is */ + /* a small number of these, which can be referenced */ + /* by DS_PROC mark descriptors. See gc_mark.h. */ + char _modws_valid_offsets[sizeof(word)]; + /* GC_valid_offsets[i] ==> */ + /* GC_modws_valid_offsets[i%sizeof(word)] */ +# ifndef ANY_MSWIN +# define GC_root_index GC_arrays._root_index + struct roots * _root_index[RT_SIZE]; +# endif +# ifdef SAVE_CALL_CHAIN +# define GC_last_stack GC_arrays._last_stack + struct callinfo _last_stack[NFRAMES]; + /* Stack at last garbage collection. Useful for */ + /* debugging mysterious object disappearances. In the */ + /* multi-threaded case, we currently only save the */ + /* calling stack. */ +# endif +# ifndef SEPARATE_GLOBALS +# define GC_objfreelist GC_arrays._objfreelist + void *_objfreelist[MAXOBJGRANULES+1]; + /* free list for objects */ +# define GC_aobjfreelist GC_arrays._aobjfreelist + void *_aobjfreelist[MAXOBJGRANULES+1]; + /* free list for atomic objects */ +# endif + void *_uobjfreelist[MAXOBJGRANULES+1]; + /* Uncollectible but traced objects. */ + /* Objects on this and _auobjfreelist */ + /* are always marked, except during */ + /* garbage collections. */ +# ifdef GC_ATOMIC_UNCOLLECTABLE +# define GC_auobjfreelist GC_arrays._auobjfreelist + void *_auobjfreelist[MAXOBJGRANULES+1]; + /* Atomic uncollectible but traced objects. */ +# endif + size_t _size_map[MAXOBJBYTES+1]; + /* Number of granules to allocate when asked for a certain */ + /* number of bytes (plus EXTRA_BYTES). Should be accessed with */ + /* the allocator lock held. */ +# ifndef MARK_BIT_PER_OBJ +# define GC_obj_map GC_arrays._obj_map + unsigned short * _obj_map[MAXOBJGRANULES + 1]; + /* If not NULL, then a pointer to a map of valid */ + /* object addresses. */ + /* GC_obj_map[sz_in_granules][i] is */ + /* i % sz_in_granules. */ + /* This is now used purely to replace a */ + /* division in the marker by a table lookup. */ + /* _obj_map[0] is used for large objects and */ + /* contains all nonzero entries. This gets us */ + /* out of the marker fast path without an extra */ + /* test. */ +# define OBJ_MAP_LEN BYTES_TO_GRANULES(HBLKSIZE) +# endif +# define VALID_OFFSET_SZ HBLKSIZE + char _valid_offsets[VALID_OFFSET_SZ]; + /* GC_valid_offsets[i] == TRUE ==> i */ + /* is registered as a displacement. */ +# ifndef GC_DISABLE_INCREMENTAL +# define GC_grungy_pages GC_arrays._grungy_pages + page_hash_table _grungy_pages; /* Pages that were dirty at last */ + /* GC_read_dirty. */ +# define GC_dirty_pages GC_arrays._dirty_pages +# ifdef MPROTECT_VDB + volatile +# endif + page_hash_table _dirty_pages; + /* Pages dirtied since last GC_read_dirty. */ +# endif +# if (defined(CHECKSUMS) && (defined(GWW_VDB) || defined(SOFT_VDB))) \ + || defined(PROC_VDB) +# define GC_written_pages GC_arrays._written_pages + page_hash_table _written_pages; /* Pages ever dirtied */ +# endif +# define GC_heap_sects GC_arrays._heap_sects + struct HeapSect *_heap_sects; /* Heap segments potentially */ + /* client objects. */ +# if defined(USE_PROC_FOR_LIBRARIES) +# define GC_our_memory GC_arrays._our_memory + struct HeapSect _our_memory[MAX_HEAP_SECTS]; + /* All GET_MEM allocated */ + /* memory. Includes block */ + /* headers and the like. */ +# endif +# ifdef ANY_MSWIN +# define GC_heap_bases GC_arrays._heap_bases + ptr_t _heap_bases[MAX_HEAP_SECTS]; + /* Start address of memory regions obtained from kernel. */ +# endif +# ifdef MSWINCE +# define GC_heap_lengths GC_arrays._heap_lengths + word _heap_lengths[MAX_HEAP_SECTS]; + /* Committed lengths of memory regions obtained from kernel. */ +# endif + struct roots _static_roots[MAX_ROOT_SETS]; + struct exclusion _excl_table[MAX_EXCLUSIONS]; + /* Block header index; see gc_headers.h */ + bottom_index * _top_index[TOP_SZ]; +}; + +GC_API_PRIV GC_FAR struct _GC_arrays GC_arrays; + +#define GC_all_nils GC_arrays._all_nils +#define GC_atomic_in_use GC_arrays._atomic_in_use +#define GC_bytes_allocd_before_gc GC_arrays._bytes_allocd_before_gc +#define GC_bytes_dropped GC_arrays._bytes_dropped +#define GC_bytes_finalized GC_arrays._bytes_finalized +#define GC_bytes_freed GC_arrays._bytes_freed +#define GC_composite_in_use GC_arrays._composite_in_use +#define GC_excl_table GC_arrays._excl_table +#define GC_finalizer_bytes_freed GC_arrays._finalizer_bytes_freed +#define GC_heapsize GC_arrays._heapsize +#define GC_large_allocd_bytes GC_arrays._large_allocd_bytes +#define GC_large_free_bytes GC_arrays._large_free_bytes +#define GC_last_heap_addr GC_arrays._last_heap_addr +#define GC_mark_stack GC_arrays._mark_stack +#define GC_mark_stack_limit GC_arrays._mark_stack_limit +#define GC_mark_stack_top GC_arrays._mark_stack_top +#define GC_mark_procs GC_arrays._mark_procs +#define GC_max_large_allocd_bytes GC_arrays._max_large_allocd_bytes +#define GC_modws_valid_offsets GC_arrays._modws_valid_offsets +#define GC_requested_heapsize GC_arrays._requested_heapsize +#define GC_all_bottom_indices GC_arrays._all_bottom_indices +#define GC_all_bottom_indices_end GC_arrays._all_bottom_indices_end +#define GC_scratch_free_ptr GC_arrays._scratch_free_ptr +#define GC_hdr_free_list GC_arrays._hdr_free_list +#define GC_scratch_end_ptr GC_arrays._scratch_end_ptr +#define GC_size_map GC_arrays._size_map +#define GC_static_roots GC_arrays._static_roots +#define GC_top_index GC_arrays._top_index +#define GC_uobjfreelist GC_arrays._uobjfreelist +#define GC_valid_offsets GC_arrays._valid_offsets + +#define beginGC_arrays ((ptr_t)(&GC_arrays)) +#define endGC_arrays ((ptr_t)(&GC_arrays) + sizeof(GC_arrays)) + +/* Object kinds: */ +#ifndef MAXOBJKINDS +# define MAXOBJKINDS 16 +#endif +GC_EXTERN struct obj_kind { + void **ok_freelist; /* Array of free list headers for this kind of */ + /* object. Point either to GC_arrays or to */ + /* storage allocated with GC_scratch_alloc. */ + struct hblk **ok_reclaim_list; + /* List headers for lists of blocks waiting to */ + /* be swept. Indexed by object size in */ + /* granules. */ + word ok_descriptor; /* Descriptor template for objects in this */ + /* block. */ + GC_bool ok_relocate_descr; + /* Add object size in bytes to descriptor */ + /* template to obtain descriptor. Otherwise */ + /* template is used as is. */ + GC_bool ok_init; + /* Clear objects before putting them on the free list. */ +# ifdef ENABLE_DISCLAIM + GC_bool ok_mark_unconditionally; + /* Mark from all, including unmarked, objects */ + /* in block. Used to protect objects reachable */ + /* from reclaim notifiers. */ + int (GC_CALLBACK *ok_disclaim_proc)(void * /*obj*/); + /* The disclaim procedure is called before obj */ + /* is reclaimed, but must also tolerate being */ + /* called with object from freelist. Non-zero */ + /* exit prevents object from being reclaimed. */ +# define OK_DISCLAIM_INITZ /* comma */, FALSE, 0 +# else +# define OK_DISCLAIM_INITZ /* empty */ +# endif /* !ENABLE_DISCLAIM */ +} GC_obj_kinds[MAXOBJKINDS]; + +#define beginGC_obj_kinds ((ptr_t)(&GC_obj_kinds[0])) +#define endGC_obj_kinds (beginGC_obj_kinds + sizeof(GC_obj_kinds)) + +/* Variables that used to be in GC_arrays, but need to be accessed by */ +/* inline allocation code. If they were in GC_arrays, the inlined */ +/* allocation code would include GC_arrays offsets (as it did), which */ +/* introduce maintenance problems. */ + +#ifdef SEPARATE_GLOBALS + extern word GC_bytes_allocd; + /* Number of bytes allocated during this collection cycle. */ + extern ptr_t GC_objfreelist[MAXOBJGRANULES+1]; + /* free list for NORMAL objects */ +# define beginGC_objfreelist ((ptr_t)(&GC_objfreelist[0])) +# define endGC_objfreelist (beginGC_objfreelist + sizeof(GC_objfreelist)) + + extern ptr_t GC_aobjfreelist[MAXOBJGRANULES+1]; + /* free list for atomic (PTRFREE) objects */ +# define beginGC_aobjfreelist ((ptr_t)(&GC_aobjfreelist[0])) +# define endGC_aobjfreelist (beginGC_aobjfreelist + sizeof(GC_aobjfreelist)) +#endif /* SEPARATE_GLOBALS */ + +/* Predefined kinds: */ +#define PTRFREE GC_I_PTRFREE +#define NORMAL GC_I_NORMAL +#define UNCOLLECTABLE 2 +#ifdef GC_ATOMIC_UNCOLLECTABLE +# define AUNCOLLECTABLE 3 +# define IS_UNCOLLECTABLE(k) (((k) & ~1) == UNCOLLECTABLE) +# define GC_N_KINDS_INITIAL_VALUE 4 +#else +# define IS_UNCOLLECTABLE(k) ((k) == UNCOLLECTABLE) +# define GC_N_KINDS_INITIAL_VALUE 3 +#endif + +GC_EXTERN unsigned GC_n_kinds; + +GC_EXTERN size_t GC_page_size; + /* May mean the allocation granularity size, not page size. */ + +#ifdef REAL_PAGESIZE_NEEDED + GC_EXTERN size_t GC_real_page_size; +#else +# define GC_real_page_size GC_page_size +#endif + +/* Round up allocation size to a multiple of a page size. */ +/* GC_setpagesize() is assumed to be already invoked. */ +#define ROUNDUP_PAGESIZE(lb) /* lb should have no side-effect */ \ + (SIZET_SAT_ADD(lb, GC_page_size-1) & ~(GC_page_size-1)) + +/* Same as above but used to make GET_MEM() argument safe. */ +#ifdef MMAP_SUPPORTED +# define ROUNDUP_PAGESIZE_IF_MMAP(lb) ROUNDUP_PAGESIZE(lb) +#else +# define ROUNDUP_PAGESIZE_IF_MMAP(lb) (lb) +#endif + +#ifdef ANY_MSWIN + GC_EXTERN SYSTEM_INFO GC_sysinfo; + GC_INNER GC_bool GC_is_heap_base(const void *p); +#endif + +GC_EXTERN word GC_black_list_spacing; + /* Average number of bytes between blacklisted */ + /* blocks. Approximate. */ + /* Counts only blocks that are */ + /* "stack-blacklisted", i.e. that are */ + /* problematic in the interior of an object. */ + +#ifdef GC_GCJ_SUPPORT + extern struct hblk * GC_hblkfreelist[]; + extern word GC_free_bytes[]; /* Both remain visible to GNU GCJ. */ +#endif + +GC_EXTERN word GC_root_size; /* Total size of registered root sections. */ + +GC_EXTERN GC_bool GC_debugging_started; + /* GC_debug_malloc has been called. */ + +/* This is used by GC_do_blocking[_inner](). */ +struct blocking_data { + GC_fn_type fn; + void * client_data; /* and result */ +}; + +/* This is used by GC_call_with_gc_active(), GC_push_all_stack_sections(). */ +struct GC_traced_stack_sect_s { + ptr_t saved_stack_ptr; +# ifdef IA64 + ptr_t saved_backing_store_ptr; + ptr_t backing_store_end; +# endif + struct GC_traced_stack_sect_s *prev; +}; + +#ifdef THREADS + /* Process all "traced stack sections" - scan entire stack except for */ + /* frames belonging to the user functions invoked by GC_do_blocking. */ + GC_INNER void GC_push_all_stack_sections(ptr_t lo, ptr_t hi, + struct GC_traced_stack_sect_s *traced_stack_sect); + GC_EXTERN word GC_total_stacksize; /* updated on every push_all_stacks */ +#else + GC_EXTERN ptr_t GC_blocked_sp; + GC_EXTERN struct GC_traced_stack_sect_s *GC_traced_stack_sect; + /* Points to the "frame" data held in stack by */ + /* the innermost GC_call_with_gc_active(). */ + /* NULL if no such "frame" active. */ +#endif /* !THREADS */ + +#if defined(E2K) && defined(THREADS) || defined(IA64) + /* The bottom of the register stack of the primordial thread. */ + /* E2K: holds the offset (ps_ofs) instead of a pointer. */ + GC_EXTERN ptr_t GC_register_stackbottom; +#endif + +#ifdef IA64 + /* Similar to GC_push_all_stack_sections() but for IA-64 registers store. */ + GC_INNER void GC_push_all_register_sections(ptr_t bs_lo, ptr_t bs_hi, + int eager, struct GC_traced_stack_sect_s *traced_stack_sect); +#endif /* IA64 */ + +/* Marks are in a reserved area in */ +/* each heap block. Each word has one mark bit associated */ +/* with it. Only those corresponding to the beginning of an */ +/* object are used. */ + +/* Mark bit operations */ + +/* + * Retrieve, set, clear the nth mark bit in a given heap block. + * + * (Recall that bit n corresponds to nth object or allocation granule + * relative to the beginning of the block, including unused words) + */ + +#ifdef USE_MARK_BYTES +# define mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[n]) +# define set_mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[n] = 1) +# define clear_mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[n] = 0) +#else +/* Set mark bit correctly, even if mark bits may be concurrently */ +/* accessed. */ +# if defined(PARALLEL_MARK) || (defined(THREAD_SANITIZER) && defined(THREADS)) + /* Workaround TSan false positive: there is no race between */ + /* mark_bit_from_hdr and set_mark_bit_from_hdr when n is different */ + /* (alternatively, USE_MARK_BYTES could be used). If TSan is off, */ + /* AO_or() is used only if we set USE_MARK_BITS explicitly. */ +# define OR_WORD(addr, bits) AO_or((volatile AO_t *)(addr), (AO_t)(bits)) +# else +# define OR_WORD(addr, bits) (void)(*(addr) |= (bits)) +# endif +# define mark_bit_from_hdr(hhdr,n) \ + (((hhdr)->hb_marks[divWORDSZ(n)] >> modWORDSZ(n)) & (word)1) +# define set_mark_bit_from_hdr(hhdr,n) \ + OR_WORD((hhdr)->hb_marks+divWORDSZ(n), (word)1 << modWORDSZ(n)) +# define clear_mark_bit_from_hdr(hhdr,n) \ + ((hhdr)->hb_marks[divWORDSZ(n)] &= ~((word)1 << modWORDSZ(n))) +#endif /* !USE_MARK_BYTES */ + +#ifdef MARK_BIT_PER_OBJ +# define MARK_BIT_NO(offset, sz) (((word)(offset))/(sz)) + /* Get the mark bit index corresponding to the given byte */ + /* offset and size (in bytes). */ +# define MARK_BIT_OFFSET(sz) 1 + /* Spacing between useful mark bits. */ +# define IF_PER_OBJ(x) x +# define FINAL_MARK_BIT(sz) ((sz) > MAXOBJBYTES? 1 : HBLK_OBJS(sz)) + /* Position of final, always set, mark bit. */ +#else +# define MARK_BIT_NO(offset, sz) BYTES_TO_GRANULES((word)(offset)) +# define MARK_BIT_OFFSET(sz) BYTES_TO_GRANULES(sz) +# define IF_PER_OBJ(x) +# define FINAL_MARK_BIT(sz) \ + ((sz) > MAXOBJBYTES ? MARK_BITS_PER_HBLK \ + : BYTES_TO_GRANULES((sz) * HBLK_OBJS(sz))) +#endif /* !MARK_BIT_PER_OBJ */ + +/* Important internal collector routines */ + +GC_INNER ptr_t GC_approx_sp(void); + +GC_INNER GC_bool GC_should_collect(void); + +GC_INNER struct hblk * GC_next_block(struct hblk *h, GC_bool allow_free); + /* Get the next block whose address is at least */ + /* h. Returned block is managed by GC. The */ + /* block must be in use unless allow_free is */ + /* true. Return 0 if there is no such block. */ +GC_INNER struct hblk * GC_prev_block(struct hblk * h); + /* Get the last (highest address) block whose */ + /* address is at most h. Returned block is */ + /* managed by GC, but may or may not be in use. */ + /* Return 0 if there is no such block. */ +GC_INNER void GC_mark_init(void); +GC_INNER void GC_clear_marks(void); + /* Clear mark bits for all heap objects. */ +GC_INNER void GC_invalidate_mark_state(void); + /* Tell the marker that marked */ + /* objects may point to unmarked */ + /* ones, and roots may point to */ + /* unmarked objects. Reset mark stack. */ +GC_INNER GC_bool GC_mark_some(ptr_t cold_gc_frame); + /* Perform about one pages worth of marking */ + /* work of whatever kind is needed. Returns */ + /* quickly if no collection is in progress. */ + /* Return TRUE if mark phase finished. */ +GC_INNER void GC_initiate_gc(void); + /* initiate collection. */ + /* If the mark state is invalid, this */ + /* becomes full collection. Otherwise */ + /* it's partial. */ + +GC_INNER GC_bool GC_collection_in_progress(void); + /* Collection is in progress, or was abandoned. */ + +/* Push contents of the symbol residing in the static roots area */ +/* excluded from scanning by the collector for a reason. */ +/* Note: it should be used only for symbols of relatively small size */ +/* (one or several words). */ +#define GC_PUSH_ALL_SYM(sym) GC_push_all_eager(&(sym), &(sym) + 1) + +GC_INNER void GC_push_all_stack(ptr_t b, ptr_t t); + /* As GC_push_all but consider */ + /* interior pointers as valid. */ + +#ifdef NO_VDB_FOR_STATIC_ROOTS +# define GC_push_conditional_static(b, t, all) \ + ((void)(all), GC_push_all(b, t)) +#else + /* Same as GC_push_conditional (does either of GC_push_all or */ + /* GC_push_selected depending on the third argument) but the caller */ + /* guarantees the region belongs to the registered static roots. */ + GC_INNER void GC_push_conditional_static(void *b, void *t, GC_bool all); +#endif + +#if defined(WRAP_MARK_SOME) && defined(PARALLEL_MARK) + /* GC_mark_local does not handle memory protection faults yet. So, */ + /* the static data regions are scanned immediately by GC_push_roots. */ + GC_INNER void GC_push_conditional_eager(void *bottom, void *top, + GC_bool all); +#endif + + /* In the threads case, we push part of the current thread stack */ + /* with GC_push_all_eager when we push the registers. This gets the */ + /* callee-save registers that may disappear. The remainder of the */ + /* stacks are scheduled for scanning in *GC_push_other_roots, which */ + /* is thread-package-specific. */ + +GC_INNER void GC_push_roots(GC_bool all, ptr_t cold_gc_frame); + /* Push all or dirty roots. */ + +GC_API_PRIV GC_push_other_roots_proc GC_push_other_roots; + /* Push system or application specific roots */ + /* onto the mark stack. In some environments */ + /* (e.g. threads environments) this is */ + /* predefined to be non-zero. A client */ + /* supplied replacement should also call the */ + /* original function. Remains externally */ + /* visible as used by some well-known 3rd-party */ + /* software (e.g., ECL) currently. */ + +#ifdef THREADS + void GC_push_thread_structures(void); +#endif +GC_EXTERN void (*GC_push_typed_structures)(void); + /* A pointer such that we can avoid linking in */ + /* the typed allocation support if unused. */ + +typedef void (*GC_with_callee_saves_func)(ptr_t arg, void *context); +GC_INNER void GC_with_callee_saves_pushed(GC_with_callee_saves_func fn, + ptr_t arg); + +#if defined(IA64) || defined(SPARC) + /* Cause all stacked registers to be saved in memory. Return a */ + /* pointer to the top of the corresponding memory stack. */ + ptr_t GC_save_regs_in_stack(void); +#endif + +#ifdef E2K +# include +# include +# include + +# if defined(CPPCHECK) +# define PS_ALLOCA_BUF(sz) __builtin_alloca(sz) +# else +# define PS_ALLOCA_BUF(sz) alloca(sz) +# endif + + /* Approximate size (in bytes) of the obtained procedure stack part */ + /* belonging the syscall() itself. */ +# define PS_SYSCALL_TAIL_BYTES 0x100 + + /* Determine the current size of the whole procedure stack. The size */ + /* is valid only within the current function. */ +# define GET_PROCEDURE_STACK_SIZE_INNER(psz_ull) \ + do { \ + *(psz_ull) = 0; /* might be redundant */ \ + if (syscall(__NR_access_hw_stacks, E2K_GET_PROCEDURE_STACK_SIZE, \ + NULL, NULL, 0, psz_ull) == -1) \ + ABORT_ARG1("Cannot get size of procedure stack", \ + ": errno= %d", errno); \ + GC_ASSERT(*(psz_ull) > 0 && *(psz_ull) % sizeof(word) == 0); \ + } while (0) + +# ifdef THREADS +# define PS_COMPUTE_ADJUSTED_OFS(padj_ps_ofs, ps_ofs, ofs_sz_ull) \ + do { \ + if ((ofs_sz_ull) <= (ps_ofs) /* && ofs_sz_ull > 0 */) \ + ABORT_ARG2("Incorrect size of procedure stack", \ + ": ofs= %lu, size= %lu", \ + (unsigned long)(ps_ofs), \ + (unsigned long)(ofs_sz_ull)); \ + *(padj_ps_ofs) = (ps_ofs) > PS_SYSCALL_TAIL_BYTES ? \ + (ps_ofs) - PS_SYSCALL_TAIL_BYTES : 0; \ + } while (0) +# else + /* A simplified version of the above assuming ps_ofs is a zero const. */ +# define PS_COMPUTE_ADJUSTED_OFS(padj_ps_ofs, ps_ofs, ofs_sz_ull) \ + do { \ + GC_STATIC_ASSERT((ps_ofs) == 0); \ + (void)(ofs_sz_ull); \ + *(padj_ps_ofs) = 0; \ + } while (0) +# endif /* !THREADS */ + + /* Copy procedure (register) stack to a stack-allocated buffer. */ + /* Usable from a signal handler. The buffer is valid only within */ + /* the current function. ps_ofs designates the offset in the */ + /* procedure stack to copy the contents from. Note: this macro */ + /* cannot be changed to a function because alloca() and both */ + /* syscall() should be called in the context of the caller. */ +# define GET_PROCEDURE_STACK_LOCAL(ps_ofs, pbuf, psz) \ + do { \ + unsigned long long ofs_sz_ull; \ + size_t adj_ps_ofs; \ + \ + GET_PROCEDURE_STACK_SIZE_INNER(&ofs_sz_ull); \ + PS_COMPUTE_ADJUSTED_OFS(&adj_ps_ofs, ps_ofs, ofs_sz_ull); \ + *(psz) = (size_t)ofs_sz_ull - adj_ps_ofs; \ + /* Allocate buffer on the stack; cannot return NULL. */ \ + *(pbuf) = PS_ALLOCA_BUF(*(psz)); \ + /* Copy the procedure stack at the given offset to the buffer. */ \ + for (;;) { \ + ofs_sz_ull = adj_ps_ofs; \ + if (syscall(__NR_access_hw_stacks, E2K_READ_PROCEDURE_STACK_EX, \ + &ofs_sz_ull, *(pbuf), *(psz), NULL) != -1) \ + break; \ + if (errno != EAGAIN) \ + ABORT_ARG2("Cannot read procedure stack", \ + ": sz= %lu, errno= %d", \ + (unsigned long)(*(psz)), errno); \ + } \ + } while (0) +#endif /* E2K */ + +#if defined(E2K) && defined(USE_PTR_HWTAG) + /* Load value and get tag of the target memory. */ +# if defined(__ptr64__) +# define LOAD_TAGGED_VALUE(v, tag, p) \ + do { \ + word val; \ + __asm__ __volatile__ ( \ + "ldd, sm %[adr], 0x0, %[val]\n\t" \ + "gettagd %[val], %[tag]\n" \ + : [val] "=r" (val), \ + [tag] "=r" (tag) \ + : [adr] "r" (p)); \ + v = val; \ + } while (0) +# elif !defined(CPPCHECK) +# error Unsupported -march for e2k target +# endif + +# define LOAD_WORD_OR_CONTINUE(v, p) \ + { \ + int tag LOCAL_VAR_INIT_OK; \ + LOAD_TAGGED_VALUE(v, tag, p); \ + if (tag != 0) continue; \ + } +#else +# define LOAD_WORD_OR_CONTINUE(v, p) (void)(v = *(word *)(p)) +#endif /* !E2K */ + +#if defined(AMIGA) || defined(MACOS) || defined(GC_DARWIN_THREADS) + void GC_push_one(word p); + /* If p points to an object, mark it */ + /* and push contents on the mark stack */ + /* Pointer recognition test always */ + /* accepts interior pointers, i.e. this */ + /* is appropriate for pointers found on */ + /* stack. */ +#endif + +#ifdef GC_WIN32_THREADS + /* Same as GC_push_one but for a sequence of registers. */ + GC_INNER void GC_push_many_regs(const word *regs, unsigned count); +#endif + +#if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS) + GC_INNER void GC_mark_and_push_stack(ptr_t p, ptr_t source); + /* Ditto, omits plausibility test */ +#else + GC_INNER void GC_mark_and_push_stack(ptr_t p); +#endif + +/* Is the block with the given header containing no pointers? */ +#define IS_PTRFREE(hhdr) (0 == (hhdr) -> hb_descr) + +GC_INNER void GC_clear_hdr_marks(hdr * hhdr); + /* Clear the mark bits in a header */ +GC_INNER void GC_set_hdr_marks(hdr * hhdr); + /* Set the mark bits in a header */ +GC_INNER void GC_set_fl_marks(ptr_t p); + /* Set all mark bits associated with */ + /* a free list. */ +#if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC) + void GC_check_fl_marks(void **); + /* Check that all mark bits */ + /* associated with a free list are */ + /* set. Abort if not. */ +#endif + +#ifndef AMIGA + GC_INNER +#endif +void GC_add_roots_inner(ptr_t b, ptr_t e, GC_bool tmp); + +#ifdef USE_PROC_FOR_LIBRARIES + GC_INNER void GC_remove_roots_subregion(ptr_t b, ptr_t e); +#endif +GC_INNER void GC_exclude_static_roots_inner(void *start, void *finish); +#if defined(DYNAMIC_LOADING) || defined(ANY_MSWIN) || defined(PCR) + GC_INNER void GC_register_dynamic_libraries(void); + /* Add dynamic library data sections to the root set. */ +#endif +GC_INNER void GC_cond_register_dynamic_libraries(void); + /* Remove and reregister dynamic libraries if we're */ + /* configured to do that at each GC. */ + +/* Machine dependent startup routines */ +ptr_t GC_get_main_stack_base(void); /* Cold end of stack. */ +#ifdef IA64 + GC_INNER ptr_t GC_get_register_stack_base(void); + /* Cold end of register stack. */ +#endif + +void GC_register_data_segments(void); + +#ifdef THREADS + /* Both are invoked from GC_init only. */ + GC_INNER void GC_thr_init(void); + GC_INNER void GC_init_parallel(void); +# ifndef DONT_USE_ATEXIT + GC_INNER GC_bool GC_is_main_thread(void); +# endif +#else + GC_INNER GC_bool GC_is_static_root(void *p); + /* Is the address p in one of the registered static */ + /* root sections? */ +# ifdef TRACE_BUF + void GC_add_trace_entry(char *kind, word arg1, word arg2); +# endif +#endif /* !THREADS */ + +/* Black listing: */ +#ifdef PRINT_BLACK_LIST + GC_INNER void GC_add_to_black_list_normal(word p, ptr_t source); + /* Register bits as a possible future false */ + /* reference from the heap or static data */ +# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ + if (GC_all_interior_pointers) { \ + GC_add_to_black_list_stack((word)(bits), (source)); \ + } else \ + GC_add_to_black_list_normal((word)(bits), (source)) + GC_INNER void GC_add_to_black_list_stack(word p, ptr_t source); +# define GC_ADD_TO_BLACK_LIST_STACK(bits, source) \ + GC_add_to_black_list_stack((word)(bits), (source)) +#else + GC_INNER void GC_add_to_black_list_normal(word p); +# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ + if (GC_all_interior_pointers) { \ + GC_add_to_black_list_stack((word)(bits)); \ + } else \ + GC_add_to_black_list_normal((word)(bits)) + GC_INNER void GC_add_to_black_list_stack(word p); +# define GC_ADD_TO_BLACK_LIST_STACK(bits, source) \ + GC_add_to_black_list_stack((word)(bits)) +#endif /* PRINT_BLACK_LIST */ + +GC_INNER void GC_promote_black_lists(void); + /* Declare an end to a black listing phase. */ +GC_INNER void GC_unpromote_black_lists(void); + /* Approximately undo the effect of the above. */ + /* This actually loses some information, but */ + /* only in a reasonably safe way. */ + +GC_INNER ptr_t GC_scratch_alloc(size_t bytes); + /* GC internal memory allocation for */ + /* small objects. Deallocation is not */ + /* possible. May return NULL. */ + +#ifdef GWW_VDB + /* GC_scratch_recycle_no_gww() not used. */ +#else +# define GC_scratch_recycle_no_gww GC_scratch_recycle_inner +#endif +GC_INNER void GC_scratch_recycle_inner(void *ptr, size_t bytes); + /* Reuse the memory region by the heap. */ + +/* Heap block layout maps: */ +#ifndef MARK_BIT_PER_OBJ + GC_INNER GC_bool GC_add_map_entry(size_t sz); + /* Add a heap block map for objects of */ + /* size sz to obj_map. */ + /* Return FALSE on failure. */ +#endif + +GC_INNER void GC_register_displacement_inner(size_t offset); + /* Version of GC_register_displacement */ + /* that assumes the allocator lock is */ + /* already held. */ + +/* hblk allocation: */ +GC_INNER void GC_new_hblk(size_t size_in_granules, int kind); + /* Allocate a new heap block, and build */ + /* a free list in it. */ + +GC_INNER ptr_t GC_build_fl(struct hblk *h, size_t words, GC_bool clear, + ptr_t list); + /* Build a free list for objects of */ + /* size sz in block h. Append list to */ + /* end of the free lists. Possibly */ + /* clear objects on the list. Normally */ + /* called by GC_new_hblk, but also */ + /* called explicitly without the */ + /* allocator lock held. */ + +GC_INNER struct hblk * GC_allochblk(size_t size_in_bytes, int kind, + unsigned flags, size_t align_m1); + /* Allocate (and return pointer to) */ + /* a heap block for objects of the */ + /* given size and alignment (in bytes), */ + /* searching over the appropriate free */ + /* block lists; inform the marker */ + /* that the found block is valid for */ + /* objects of the indicated size. */ + /* The client is responsible for */ + /* clearing the block, if necessary. */ + /* Note: we set obj_map field in the */ + /* header correctly; the caller is */ + /* responsible for building an object */ + /* freelist in the block. */ + +GC_INNER ptr_t GC_alloc_large(size_t lb, int k, unsigned flags, + size_t align_m1); + /* Allocate a large block of size lb bytes with */ + /* the requested alignment (align_m1 plus one). */ + /* The block is not cleared. Assumes that */ + /* EXTRA_BYTES value is already added to lb. */ + /* The flags argument should be IGNORE_OFF_PAGE */ + /* or 0. Calls GC_allochblk() to do the actual */ + /* allocation, but also triggers GC and/or heap */ + /* expansion as appropriate. Updates value of */ + /* GC_bytes_allocd; does also other accounting. */ + +GC_INNER void GC_freehblk(struct hblk * p); + /* Deallocate a heap block and mark it */ + /* as invalid. */ + +/* Miscellaneous GC routines. */ + +GC_INNER GC_bool GC_expand_hp_inner(word n); +GC_INNER void GC_start_reclaim(GC_bool abort_if_found); + /* Restore unmarked objects to free */ + /* lists, or (if abort_if_found is */ + /* TRUE) report them. */ + /* Sweeping of small object pages is */ + /* largely deferred. */ +GC_INNER void GC_continue_reclaim(word sz, int kind); + /* Sweep pages of the given size and */ + /* kind, as long as possible, and */ + /* as long as the corresponding free */ + /* list is empty. sz is in granules. */ + +GC_INNER GC_bool GC_reclaim_all(GC_stop_func stop_func, GC_bool ignore_old); + /* Reclaim all blocks. Abort (in a */ + /* consistent state) if stop_func() */ + /* returns TRUE. */ +GC_INNER ptr_t GC_reclaim_generic(struct hblk * hbp, hdr *hhdr, size_t sz, + GC_bool init, ptr_t list, word *pcount); + /* Rebuild free list in hbp with */ + /* header hhdr, with objects of size sz */ + /* bytes. Add list to the end of the */ + /* free list. Add the number of */ + /* reclaimed bytes to *pcount. */ +GC_INNER GC_bool GC_block_empty(hdr * hhdr); + /* Block completely unmarked? */ +GC_INNER int GC_CALLBACK GC_never_stop_func(void); + /* Always returns 0 (FALSE). */ +GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func stop_func); + /* Collect; caller must have acquired */ + /* the allocator lock. Collection is */ + /* aborted if stop_func() returns TRUE. */ + /* Returns TRUE if it completes */ + /* successfully. */ +#define GC_gcollect_inner() \ + (void)GC_try_to_collect_inner(GC_never_stop_func) + +#ifdef THREADS + GC_EXTERN GC_bool GC_in_thread_creation; + /* We may currently be in thread creation or destruction. */ + /* Only set to TRUE while the allocator lock is held. */ + /* When set, it is OK to run GC from unknown thread. */ +#endif + +GC_EXTERN GC_bool GC_is_initialized; /* GC_init() has been run. */ + +GC_INNER void GC_collect_a_little_inner(int n); + /* Do n units worth of garbage */ + /* collection work, if appropriate. */ + /* A unit is an amount appropriate for */ + /* HBLKSIZE bytes of allocation. */ + +GC_INNER void * GC_malloc_kind_aligned_global(size_t lb, int k, + size_t align_m1); + +GC_INNER void * GC_generic_malloc_aligned(size_t lb, int k, unsigned flags, + size_t align_m1); + +GC_INNER void * GC_generic_malloc_inner(size_t lb, int k, unsigned flags); + /* Allocate an object of the given kind */ + /* but assuming the allocator lock is */ + /* already held. Should not be used to */ + /* directly allocate objects requiring */ + /* special handling on allocation. */ + /* The flags argument should be 0 or */ + /* IGNORE_OFF_PAGE. In the latter case */ + /* the client guarantees there will */ + /* always be a pointer to the beginning */ + /* (i.e. within the first hblk) of the */ + /* object while it is live. */ + +GC_INNER GC_bool GC_collect_or_expand(word needed_blocks, unsigned flags, + GC_bool retry); + +GC_INNER ptr_t GC_allocobj(size_t gran, int kind); + /* Make the indicated free list */ + /* nonempty, and return its head. */ + /* The size (gran) is in granules. */ + +#ifdef GC_ADD_CALLER + /* GC_DBG_EXTRAS is used by GC debug API functions (unlike GC_EXTRAS */ + /* used by GC debug API macros) thus GC_RETURN_ADDR_PARENT (pointing */ + /* to client caller) should be used if possible. */ +# ifdef GC_HAVE_RETURN_ADDR_PARENT +# define GC_DBG_EXTRAS GC_RETURN_ADDR_PARENT, NULL, 0 +# else +# define GC_DBG_EXTRAS GC_RETURN_ADDR, NULL, 0 +# endif +#else +# define GC_DBG_EXTRAS "unknown", 0 +#endif /* !GC_ADD_CALLER */ + +#ifdef GC_COLLECT_AT_MALLOC + extern size_t GC_dbg_collect_at_malloc_min_lb; + /* variable visible outside for debugging */ +# define GC_DBG_COLLECT_AT_MALLOC(lb) \ + (void)((lb) >= GC_dbg_collect_at_malloc_min_lb ? \ + (GC_gcollect(), 0) : 0) +#else +# define GC_DBG_COLLECT_AT_MALLOC(lb) (void)0 +#endif /* !GC_COLLECT_AT_MALLOC */ + +/* Allocation routines that bypass the thread local cache. */ +#if defined(THREAD_LOCAL_ALLOC) && defined(GC_GCJ_SUPPORT) + GC_INNER void *GC_core_gcj_malloc(size_t lb, void *, unsigned flags); +#endif + +GC_INNER void GC_init_headers(void); +GC_INNER struct hblkhdr * GC_install_header(struct hblk *h); + /* Install a header for block h. */ + /* Return 0 on failure, or the header */ + /* otherwise. */ +GC_INNER GC_bool GC_install_counts(struct hblk * h, size_t sz); + /* Set up forwarding counts for block */ + /* h of size sz. */ + /* Return FALSE on failure. */ +GC_INNER void GC_remove_header(struct hblk * h); + /* Remove the header for block h. */ +GC_INNER void GC_remove_counts(struct hblk * h, size_t sz); + /* Remove forwarding counts for h. */ +GC_INNER hdr * GC_find_header(ptr_t h); + +GC_INNER ptr_t GC_os_get_mem(size_t bytes); + /* Get HBLKSIZE-aligned heap memory chunk from */ + /* the OS and add the chunk to GC_our_memory. */ + /* Return NULL if out of memory. */ + +GC_INNER void GC_print_all_errors(void); + /* Print smashed and leaked objects, if any. */ + /* Clear the lists of such objects. */ + +GC_EXTERN void (*GC_check_heap)(void); + /* Check that all objects in the heap with */ + /* debugging info are intact. */ + /* Add any that are not to GC_smashed list. */ +GC_EXTERN void (*GC_print_all_smashed)(void); + /* Print GC_smashed if it's not empty. */ + /* Clear GC_smashed list. */ +GC_EXTERN void (*GC_print_heap_obj)(ptr_t p); + /* If possible print (using GC_err_printf) */ + /* a more detailed description (terminated with */ + /* "\n") of the object referred to by p. */ + +#if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG) + void GC_print_address_map(void); + /* Print an address map of the process. */ + /* The caller should hold the allocator lock. */ +#endif + +#ifndef SHORT_DBG_HDRS + GC_EXTERN GC_bool GC_findleak_delay_free; + /* Do not immediately deallocate object on */ + /* free() in the leak-finding mode, just mark */ + /* it as freed (and deallocate it after GC). */ + GC_INNER GC_bool GC_check_leaked(ptr_t base); /* from dbg_mlc.c */ +#endif + +#ifdef AO_HAVE_store + GC_EXTERN volatile AO_t GC_have_errors; +# define GC_SET_HAVE_ERRORS() AO_store(&GC_have_errors, (AO_t)TRUE) +# define get_have_errors() ((GC_bool)AO_load(&GC_have_errors)) + /* The barriers are not needed. */ +#else + GC_EXTERN GC_bool GC_have_errors; +# define GC_SET_HAVE_ERRORS() (void)(GC_have_errors = TRUE) +# define get_have_errors() GC_have_errors +#endif /* We saw a smashed or leaked object. */ + /* Call error printing routine */ + /* occasionally. It is OK to read it */ + /* not acquiring the allocator lock. */ + /* If set to true, it is never cleared. */ + +#define VERBOSE 2 +#if !defined(NO_CLOCK) || !defined(SMALL_CONFIG) + GC_EXTERN int GC_print_stats; + /* Value 1 generates basic GC log; */ + /* VERBOSE generates additional messages. */ +#else /* SMALL_CONFIG */ +# define GC_print_stats 0 + /* Will this remove the message character strings from the executable? */ + /* With a particular level of optimizations, it should... */ +#endif + +#ifdef KEEP_BACK_PTRS + GC_EXTERN long GC_backtraces; +#endif + +/* A trivial (linear congruential) pseudo-random numbers generator, */ +/* safe for the concurrent usage. */ +#define GC_RAND_MAX ((int)(~0U >> 1)) +#if defined(AO_HAVE_store) && defined(THREAD_SANITIZER) +# define GC_RAND_STATE_T volatile AO_t +# define GC_RAND_NEXT(pseed) GC_rand_next(pseed) + GC_INLINE int GC_rand_next(GC_RAND_STATE_T *pseed) + { + AO_t next = (AO_t)((AO_load(pseed) * (unsigned32)1103515245UL + 12345) + & (unsigned32)((unsigned)GC_RAND_MAX)); + AO_store(pseed, next); + return (int)next; + } +#else +# define GC_RAND_STATE_T unsigned32 +# define GC_RAND_NEXT(pseed) /* overflow and race are OK */ \ + (int)(*(pseed) = (*(pseed) * (unsigned32)1103515245UL + 12345) \ + & (unsigned32)((unsigned)GC_RAND_MAX)) +#endif + +GC_EXTERN GC_bool GC_print_back_height; + +#ifdef MAKE_BACK_GRAPH + void GC_print_back_graph_stats(void); +#endif + +#ifdef THREADS + /* Explicitly deallocate the object when we already hold the */ + /* allocator lock. Only used for internally allocated objects. */ + GC_INNER void GC_free_inner(void * p); +#endif + +/* Macros used for collector internal allocation. */ +/* These assume the allocator lock is held. */ +#ifdef DBG_HDRS_ALL + GC_INNER void * GC_debug_generic_malloc_inner(size_t lb, int k, + unsigned flags); +# define GC_INTERNAL_MALLOC(lb, k) GC_debug_generic_malloc_inner(lb, k, 0) +# define GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE(lb, k) \ + GC_debug_generic_malloc_inner(lb, k, IGNORE_OFF_PAGE) +# ifdef THREADS + GC_INNER void GC_debug_free_inner(void * p); +# define GC_INTERNAL_FREE GC_debug_free_inner +# else +# define GC_INTERNAL_FREE GC_debug_free +# endif +#else +# define GC_INTERNAL_MALLOC(lb, k) GC_generic_malloc_inner(lb, k, 0) +# define GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE(lb, k) \ + GC_generic_malloc_inner(lb, k, IGNORE_OFF_PAGE) +# ifdef THREADS +# define GC_INTERNAL_FREE GC_free_inner +# else +# define GC_INTERNAL_FREE GC_free +# endif +#endif /* !DBG_HDRS_ALL */ + +#ifdef USE_MUNMAP + /* Memory unmapping: */ + GC_INNER void GC_unmap_old(unsigned threshold); + GC_INNER void GC_merge_unmapped(void); + GC_INNER void GC_unmap(ptr_t start, size_t bytes); + GC_INNER void GC_remap(ptr_t start, size_t bytes); + GC_INNER void GC_unmap_gap(ptr_t start1, size_t bytes1, ptr_t start2, + size_t bytes2); + +# ifndef NOT_GCBUILD + /* Compute end address for an unmap operation on the indicated block. */ + GC_INLINE ptr_t GC_unmap_end(ptr_t start, size_t bytes) + { + return (ptr_t)((word)(start + bytes) & ~(word)(GC_page_size-1)); + } +# endif +#endif /* USE_MUNMAP */ + +#ifdef CAN_HANDLE_FORK + GC_EXTERN int GC_handle_fork; + /* Fork-handling mode: */ + /* 0 means no fork handling requested (but client could */ + /* anyway call fork() provided it is surrounded with */ + /* GC_atfork_prepare/parent/child calls); */ + /* -1 means GC tries to use pthread_at_fork if it is */ + /* available (if it succeeds then GC_handle_fork value */ + /* is changed to 1), client should nonetheless surround */ + /* fork() with GC_atfork_prepare/parent/child (for the */ + /* case of pthread_at_fork failure or absence); */ + /* 1 (or other values) means client fully relies on */ + /* pthread_at_fork (so if it is missing or failed then */ + /* abort occurs in GC_init), GC_atfork_prepare and the */ + /* accompanying routines are no-op in such a case. */ +#endif + +#ifdef NO_MANUAL_VDB +# define GC_manual_vdb FALSE +# define GC_auto_incremental GC_incremental +# define GC_dirty(p) (void)(p) +# define REACHABLE_AFTER_DIRTY(p) (void)(p) +#else + GC_EXTERN GC_bool GC_manual_vdb; + /* The incremental collection is in the manual VDB */ + /* mode. Assumes GC_incremental is true. Should not */ + /* be modified once GC_incremental is set to true. */ + +# define GC_auto_incremental (GC_incremental && !GC_manual_vdb) + GC_INNER void GC_dirty_inner(const void *p); /* does not require locking */ +# define GC_dirty(p) (GC_manual_vdb ? GC_dirty_inner(p) : (void)0) +# define REACHABLE_AFTER_DIRTY(p) GC_reachable_here(p) +#endif /* !NO_MANUAL_VDB */ + +#ifdef GC_DISABLE_INCREMENTAL +# define GC_incremental FALSE +#else + GC_EXTERN GC_bool GC_incremental; + /* Using incremental/generational collection. */ + /* Assumes dirty bits are being maintained. */ + + /* Virtual dirty bit implementation: */ + /* Each implementation exports the following: */ + GC_INNER void GC_read_dirty(GC_bool output_unneeded); + /* Retrieve dirty bits. Set output_unneeded to */ + /* indicate that reading of the retrieved dirty */ + /* bits is not planned till the next retrieval. */ + GC_INNER GC_bool GC_page_was_dirty(struct hblk *h); + /* Read retrieved dirty bits. */ + + GC_INNER void GC_remove_protection(struct hblk *h, word nblocks, + GC_bool pointerfree); + /* h is about to be written or allocated. Ensure that */ + /* it is not write protected by the virtual dirty bit */ + /* implementation. I.e., this is a call that: */ + /* - hints that [h, h+nblocks) is about to be written; */ + /* - guarantees that protection is removed; */ + /* - may speed up some dirty bit implementations; */ + /* - may be essential if we need to ensure that */ + /* pointer-free system call buffers in the heap are */ + /* not protected. */ + +# if !defined(NO_VDB_FOR_STATIC_ROOTS) && !defined(PROC_VDB) + GC_INNER GC_bool GC_is_vdb_for_static_roots(void); + /* Is VDB working for static roots? */ +# endif + +# ifdef CAN_HANDLE_FORK +# if defined(PROC_VDB) || defined(SOFT_VDB) \ + || (defined(MPROTECT_VDB) && defined(GC_DARWIN_THREADS)) + GC_INNER void GC_dirty_update_child(void); + /* Update pid-specific resources (like /proc file */ + /* descriptors) needed by the dirty bits implementation */ + /* after fork in the child process. */ +# else +# define GC_dirty_update_child() (void)0 +# endif +# endif /* CAN_HANDLE_FORK */ + +# if defined(MPROTECT_VDB) && defined(DARWIN) + EXTERN_C_END +# include + EXTERN_C_BEGIN +# ifdef THREADS + GC_INNER int GC_inner_pthread_create(pthread_t *t, + GC_PTHREAD_CREATE_CONST pthread_attr_t *a, + void *(*fn)(void *), void *arg); +# else +# define GC_inner_pthread_create pthread_create +# endif +# endif /* MPROTECT_VDB && DARWIN */ + + GC_INNER GC_bool GC_dirty_init(void); + /* Returns true if dirty bits are maintained (otherwise */ + /* it is OK to be called again if the client invokes */ + /* GC_enable_incremental once more). */ +#endif /* !GC_DISABLE_INCREMENTAL */ + +/* Same as GC_base but excepts and returns a pointer to const object. */ +#define GC_base_C(p) ((const void *)GC_base((/* no const */ void *)(word)(p))) + +/* Debugging print routines: */ +void GC_print_block_list(void); +void GC_print_hblkfreelist(void); +void GC_print_heap_sects(void); +void GC_print_static_roots(void); + +#ifdef KEEP_BACK_PTRS + GC_INNER void GC_store_back_pointer(ptr_t source, ptr_t dest); + GC_INNER void GC_marked_for_finalization(ptr_t dest); +# define GC_STORE_BACK_PTR(source, dest) GC_store_back_pointer(source, dest) +# define GC_MARKED_FOR_FINALIZATION(dest) GC_marked_for_finalization(dest) +#else +# define GC_STORE_BACK_PTR(source, dest) (void)(source) +# define GC_MARKED_FOR_FINALIZATION(dest) +#endif /* !KEEP_BACK_PTRS */ + +/* Make arguments appear live to compiler */ +void GC_noop6(word, word, word, word, word, word); + +#ifndef GC_ATTR_FORMAT_PRINTF +# if GC_GNUC_PREREQ(3, 0) +# define GC_ATTR_FORMAT_PRINTF(spec_argnum, first_checked) \ + __attribute__((__format__(__printf__, spec_argnum, first_checked))) +# else +# define GC_ATTR_FORMAT_PRINTF(spec_argnum, first_checked) +# endif +#endif + +/* Logging and diagnostic output: */ +/* GC_printf is used typically on client explicit print requests. */ +/* For all GC_X_printf routines, it is recommended to put "\n" at */ +/* 'format' string end (for output atomicity). */ +GC_API_PRIV void GC_printf(const char * format, ...) + GC_ATTR_FORMAT_PRINTF(1, 2); + /* A version of printf that doesn't allocate, */ + /* 1 KB total output length. */ + /* (We use sprintf. Hopefully that doesn't */ + /* allocate for long arguments.) */ +GC_API_PRIV void GC_err_printf(const char * format, ...) + GC_ATTR_FORMAT_PRINTF(1, 2); + +/* Basic logging routine. Typically, GC_log_printf is called directly */ +/* only inside various DEBUG_x blocks. */ +GC_API_PRIV void GC_log_printf(const char * format, ...) + GC_ATTR_FORMAT_PRINTF(1, 2); + +#ifndef GC_ANDROID_LOG +# define GC_PRINT_STATS_FLAG (GC_print_stats != 0) +# define GC_INFOLOG_PRINTF GC_COND_LOG_PRINTF + /* GC_verbose_log_printf is called only if GC_print_stats is VERBOSE. */ +# define GC_verbose_log_printf GC_log_printf +#else + extern GC_bool GC_quiet; +# define GC_PRINT_STATS_FLAG (!GC_quiet) + /* INFO/DBG loggers are enabled even if GC_print_stats is off. */ +# ifndef GC_INFOLOG_PRINTF +# define GC_INFOLOG_PRINTF if (GC_quiet) {} else GC_info_log_printf +# endif + GC_INNER void GC_info_log_printf(const char *format, ...) + GC_ATTR_FORMAT_PRINTF(1, 2); + GC_INNER void GC_verbose_log_printf(const char *format, ...) + GC_ATTR_FORMAT_PRINTF(1, 2); +#endif /* GC_ANDROID_LOG */ + +#if defined(SMALL_CONFIG) || defined(GC_ANDROID_LOG) +# define GC_ERRINFO_PRINTF GC_INFOLOG_PRINTF +#else +# define GC_ERRINFO_PRINTF GC_log_printf +#endif + +/* Convenient macros for GC_[verbose_]log_printf invocation. */ +#define GC_COND_LOG_PRINTF \ + if (EXPECT(!GC_print_stats, TRUE)) {} else GC_log_printf +#define GC_VERBOSE_LOG_PRINTF \ + if (EXPECT(GC_print_stats != VERBOSE, TRUE)) {} else GC_verbose_log_printf +#ifndef GC_DBGLOG_PRINTF +# define GC_DBGLOG_PRINTF if (!GC_PRINT_STATS_FLAG) {} else GC_log_printf +#endif + +void GC_err_puts(const char *s); + /* Write s to stderr, don't buffer, don't add */ + /* newlines, don't ... */ + +/* Handy macro for logging size values (of word type) in KiB (rounding */ +/* to nearest value). */ +#define TO_KiB_UL(v) ((unsigned long)(((v) + ((1 << 9) - 1)) >> 10)) + +GC_EXTERN unsigned GC_fail_count; + /* How many consecutive GC/expansion failures? */ + /* Reset by GC_allochblk(); defined in alloc.c. */ + +GC_EXTERN long GC_large_alloc_warn_interval; /* defined in misc.c */ + +GC_EXTERN signed_word GC_bytes_found; + /* Number of reclaimed bytes after garbage collection; */ + /* protected by the allocator lock. */ + +#ifndef GC_GET_HEAP_USAGE_NOT_NEEDED + GC_EXTERN word GC_reclaimed_bytes_before_gc; + /* Number of bytes reclaimed before this */ + /* collection cycle; used for statistics only. */ +#endif + +#ifdef USE_MUNMAP + GC_EXTERN unsigned GC_unmap_threshold; /* defined in alloc.c */ + GC_EXTERN GC_bool GC_force_unmap_on_gcollect; /* defined in misc.c */ +#endif + +#ifdef MSWIN32 + GC_EXTERN GC_bool GC_no_win32_dlls; /* defined in os_dep.c */ + GC_EXTERN GC_bool GC_wnt; /* Is Windows NT derivative; */ + /* defined and set in os_dep.c. */ +#endif + +#ifdef THREADS +# if (defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE) + GC_EXTERN CRITICAL_SECTION GC_write_cs; /* defined in misc.c */ +# ifdef GC_ASSERTIONS + GC_EXTERN GC_bool GC_write_disabled; + /* defined in win32_threads.c; */ + /* protected by GC_write_cs. */ + +# endif +# endif /* MSWIN32 || MSWINCE */ +# if (!defined(NO_MANUAL_VDB) || defined(MPROTECT_VDB)) \ + && !defined(HAVE_LOCKFREE_AO_OR) && defined(AO_HAVE_test_and_set_acquire) + /* Acquire the spin lock we use to update dirty bits. */ + /* Threads should not get stopped holding it. But we may */ + /* acquire and release it during GC_remove_protection call. */ +# define GC_acquire_dirty_lock() \ + do { /* empty */ \ + } while (AO_test_and_set_acquire(&GC_fault_handler_lock) == AO_TS_SET) +# define GC_release_dirty_lock() AO_CLEAR(&GC_fault_handler_lock) + GC_EXTERN volatile AO_TS_t GC_fault_handler_lock; + /* defined in os_dep.c */ +# else +# define GC_acquire_dirty_lock() (void)0 +# define GC_release_dirty_lock() (void)0 +# endif /* NO_MANUAL_VDB && !MPROTECT_VDB || HAVE_LOCKFREE_AO_OR */ +# ifdef MSWINCE + GC_EXTERN GC_bool GC_dont_query_stack_min; + /* Defined and set in os_dep.c. */ +# endif +#elif defined(IA64) + GC_EXTERN ptr_t GC_save_regs_ret_val; /* defined in mach_dep.c. */ + /* Previously set to backing store pointer. */ +#endif /* !THREADS */ + +#ifdef THREAD_LOCAL_ALLOC + GC_EXTERN GC_bool GC_world_stopped; /* defined in alloc.c */ + GC_INNER void GC_mark_thread_local_free_lists(void); +#endif + +#if defined(GLIBC_2_19_TSX_BUG) && defined(GC_PTHREADS_PARAMARK) + /* Parse string like [.[]] and return major value. */ + GC_INNER int GC_parse_version(int *pminor, const char *pverstr); +#endif + +#if defined(MPROTECT_VDB) && defined(GWW_VDB) + GC_INNER GC_bool GC_gww_dirty_init(void); + /* Returns TRUE if GetWriteWatch is available. */ + /* May be called repeatedly. May be called */ + /* with or without the allocator lock held. */ +#endif + +#if defined(CHECKSUMS) || defined(PROC_VDB) + GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk * h); + /* Could the page contain valid heap pointers? */ +#endif + +#ifdef CHECKSUMS +# ifdef MPROTECT_VDB + void GC_record_fault(struct hblk * h); +# endif + void GC_check_dirty(void); +#endif + +GC_INNER void GC_default_print_heap_obj_proc(ptr_t p); + +GC_INNER void GC_setpagesize(void); + +GC_INNER void GC_initialize_offsets(void); /* defined in obj_map.c */ + +GC_INNER void GC_bl_init(void); +GC_INNER void GC_bl_init_no_interiors(void); /* defined in blacklst.c */ + +GC_INNER void GC_start_debugging_inner(void); /* defined in dbg_mlc.c. */ + /* Should not be called if GC_debugging_started. */ + +/* Store debugging info into p. Return displaced pointer. */ +/* Assume we hold the allocator lock. */ +GC_INNER void *GC_store_debug_info_inner(void *p, word sz, const char *str, + int linenum); + +#if defined(REDIRECT_MALLOC) && !defined(REDIRECT_MALLOC_IN_HEADER) \ + && defined(GC_LINUX_THREADS) + GC_INNER void GC_init_lib_bounds(void); +#else +# define GC_init_lib_bounds() (void)0 +#endif + +#ifdef REDIRECT_MALLOC +# ifdef GC_LINUX_THREADS + GC_INNER GC_bool GC_text_mapping(char *nm, ptr_t *startp, ptr_t *endp); + /* from os_dep.c */ +# endif +#elif defined(USE_WINALLOC) + GC_INNER void GC_add_current_malloc_heap(void); +#endif /* USE_WINALLOC && !REDIRECT_MALLOC */ + +#ifdef MAKE_BACK_GRAPH + GC_INNER void GC_build_back_graph(void); + GC_INNER void GC_traverse_back_graph(void); +#endif + +#ifdef MSWIN32 + GC_INNER void GC_init_win32(void); +#endif + +#ifndef ANY_MSWIN + GC_INNER void * GC_roots_present(ptr_t); + /* The type is a lie, since the real type doesn't make sense here, */ + /* and we only test for NULL. */ +#endif + +#ifdef GC_WIN32_THREADS + GC_INNER void GC_get_next_stack(char *start, char * limit, char **lo, + char **hi); +# if defined(MPROTECT_VDB) && !defined(CYGWIN32) + GC_INNER void GC_set_write_fault_handler(void); +# endif +# if defined(WRAP_MARK_SOME) && !defined(GC_PTHREADS) + GC_INNER GC_bool GC_started_thread_while_stopped(void); + /* Did we invalidate mark phase with an unexpected thread start? */ +# endif +#endif /* GC_WIN32_THREADS */ + +#if defined(GC_DARWIN_THREADS) && defined(MPROTECT_VDB) + GC_INNER void GC_mprotect_stop(void); + GC_INNER void GC_mprotect_resume(void); +# ifndef GC_NO_THREADS_DISCOVERY + GC_INNER void GC_darwin_register_self_mach_handler(void); +# endif +#endif + +#ifdef THREADS +# ifndef GC_NO_FINALIZATION + GC_INNER void GC_reset_finalizer_nested(void); + GC_INNER unsigned char *GC_check_finalizer_nested(void); +# endif + GC_INNER void GC_do_blocking_inner(ptr_t data, void * context); + GC_INNER void GC_push_all_stacks(void); +# ifdef USE_PROC_FOR_LIBRARIES + GC_INNER GC_bool GC_segment_is_thread_stack(ptr_t lo, ptr_t hi); +# endif +# if (defined(HAVE_PTHREAD_ATTR_GET_NP) || defined(HAVE_PTHREAD_GETATTR_NP)) \ + && defined(IA64) + GC_INNER ptr_t GC_greatest_stack_base_below(ptr_t bound); +# endif +#endif /* THREADS */ + +#ifdef DYNAMIC_LOADING + GC_INNER GC_bool GC_register_main_static_data(void); +# ifdef DARWIN + GC_INNER void GC_init_dyld(void); +# endif +#endif /* DYNAMIC_LOADING */ + +#ifdef SEARCH_FOR_DATA_START + GC_INNER void GC_init_linux_data_start(void); + void * GC_find_limit(void *, int); +#endif + +#ifdef NEED_PROC_MAPS +# if defined(DYNAMIC_LOADING) && defined(USE_PROC_FOR_LIBRARIES) \ + || defined(IA64) || defined(INCLUDE_LINUX_THREAD_DESCR) \ + || (defined(CHECK_SOFT_VDB) && defined(MPROTECT_VDB)) \ + || (defined(REDIRECT_MALLOC) && defined(GC_LINUX_THREADS)) + GC_INNER const char *GC_parse_map_entry(const char *maps_ptr, + ptr_t *start, ptr_t *end, + const char **prot, + unsigned *maj_dev, + const char **mapping_name); +# endif +# if defined(IA64) || defined(INCLUDE_LINUX_THREAD_DESCR) \ + || (defined(CHECK_SOFT_VDB) && defined(MPROTECT_VDB)) + GC_INNER GC_bool GC_enclosing_writable_mapping(ptr_t addr, ptr_t *startp, + ptr_t *endp); +# endif + GC_INNER const char *GC_get_maps(void); +#endif /* NEED_PROC_MAPS */ + +#ifdef GC_ASSERTIONS + GC_INNER word GC_compute_large_free_bytes(void); + GC_INNER word GC_compute_root_size(void); +#endif + +/* Check a compile time assertion at compile time. */ +#if defined(_MSC_VER) && (_MSC_VER >= 1700) +# define GC_STATIC_ASSERT(expr) \ + static_assert(expr, "static assertion failed: " #expr) +#elif defined(static_assert) && !defined(CPPCHECK) \ + && (__STDC_VERSION__ >= 201112L) +# define GC_STATIC_ASSERT(expr) static_assert(expr, #expr) +#elif defined(mips) && !defined(__GNUC__) && !defined(CPPCHECK) +/* DOB: MIPSPro C gets an internal error taking the sizeof an array type. + This code works correctly (ugliness is to avoid "unused var" warnings) */ +# define GC_STATIC_ASSERT(expr) \ + do { if (0) { char j[(expr)? 1 : -1]; j[0]='\0'; j[0]=j[0]; } } while(0) +#else + /* The error message for failure is a bit baroque, but ... */ +# define GC_STATIC_ASSERT(expr) (void)sizeof(char[(expr)? 1 : -1]) +#endif + +/* Runtime check for an argument declared as non-null is actually not null. */ +#if GC_GNUC_PREREQ(4, 0) + /* Workaround tautological-pointer-compare Clang warning. */ +# define NONNULL_ARG_NOT_NULL(arg) (*(volatile void **)(word)(&(arg)) != NULL) +#else +# define NONNULL_ARG_NOT_NULL(arg) (NULL != (arg)) +#endif + +#define COND_DUMP_CHECKS \ + do { \ + GC_ASSERT(I_HOLD_LOCK()); \ + GC_ASSERT(GC_compute_large_free_bytes() == GC_large_free_bytes); \ + GC_ASSERT(GC_compute_root_size() == GC_root_size); \ + } while (0) + +#ifndef NO_DEBUGGING + GC_EXTERN GC_bool GC_dump_regularly; + /* Generate regular debugging dumps. */ +# define COND_DUMP if (EXPECT(GC_dump_regularly, FALSE)) { \ + GC_dump_named(NULL); \ + } else COND_DUMP_CHECKS +#else +# define COND_DUMP COND_DUMP_CHECKS +#endif + +#ifdef PARALLEL_MARK + /* We need additional synchronization facilities from the thread */ + /* support. We believe these are less performance critical than */ + /* the allocator lock; standard pthreads-based implementations */ + /* should be sufficient. */ + +# define GC_markers_m1 GC_parallel + /* Number of mark threads we would like to have */ + /* excluding the initiating thread. */ + + GC_EXTERN GC_bool GC_parallel_mark_disabled; + /* A flag to temporarily avoid parallel marking.*/ + + /* The mark lock and condition variable. If the allocator lock is */ + /* also acquired, it must be done first. The mark lock is used to */ + /* both protect some variables used by the parallel marker, and to */ + /* protect GC_fl_builder_count, below. GC_notify_all_marker() is */ + /* called when the state of the parallel marker changes in some */ + /* significant way (see gc_mark.h for details). The latter set of */ + /* events includes incrementing GC_mark_no. */ + /* GC_notify_all_builder() is called when GC_fl_builder_count */ + /* reaches 0. */ + + GC_INNER void GC_wait_for_markers_init(void); + GC_INNER void GC_acquire_mark_lock(void); + GC_INNER void GC_release_mark_lock(void); + GC_INNER void GC_notify_all_builder(void); + GC_INNER void GC_wait_for_reclaim(void); + + GC_EXTERN signed_word GC_fl_builder_count; /* protected by the mark lock */ + + GC_INNER void GC_notify_all_marker(void); + GC_INNER void GC_wait_marker(void); + + GC_EXTERN word GC_mark_no; /* protected by the mark lock */ + + GC_INNER void GC_help_marker(word my_mark_no); + /* Try to help out parallel marker for mark cycle */ + /* my_mark_no. Returns if the mark cycle finishes or */ + /* was already done, or there was nothing to do for */ + /* some other reason. */ + + GC_INNER void GC_start_mark_threads_inner(void); + +# define INCR_MARKS(hhdr) \ + AO_store(&(hhdr)->hb_n_marks, AO_load(&(hhdr)->hb_n_marks) + 1) +#else +# define INCR_MARKS(hhdr) (void)(++(hhdr)->hb_n_marks) +#endif /* !PARALLEL_MARK */ + +#if defined(SIGNAL_BASED_STOP_WORLD) && !defined(SIG_SUSPEND) + /* We define the thread suspension signal here, so that we can refer */ + /* to it in the dirty bit implementation, if necessary. Ideally we */ + /* would allocate a (real-time?) signal using the standard mechanism. */ + /* unfortunately, there is no standard mechanism. (There is one */ + /* in Linux glibc, but it's not exported.) Thus we continue to use */ + /* the same hard-coded signals we've always used. */ +# ifdef THREAD_SANITIZER + /* Unfortunately, use of an asynchronous signal to suspend threads */ + /* leads to the situation when the signal is not delivered (is */ + /* stored to pending_signals in TSan runtime actually) while the */ + /* destination thread is blocked in pthread_mutex_lock. Thus, we */ + /* use some synchronous one instead (which is again unlikely to be */ + /* used by clients directly). */ +# define SIG_SUSPEND SIGSYS +# elif (defined(GC_LINUX_THREADS) || defined(GC_DGUX386_THREADS)) \ + && !defined(GC_USESIGRT_SIGNALS) +# if defined(SPARC) && !defined(SIGPWR) + /* Linux/SPARC doesn't properly define SIGPWR in . */ + /* It is aliased to SIGLOST in asm/signal.h, though. */ +# define SIG_SUSPEND SIGLOST +# else + /* Linuxthreads itself uses SIGUSR1 and SIGUSR2. */ +# define SIG_SUSPEND SIGPWR +# endif +# elif defined(GC_FREEBSD_THREADS) && defined(__GLIBC__) \ + && !defined(GC_USESIGRT_SIGNALS) +# define SIG_SUSPEND (32+6) +# elif (defined(GC_FREEBSD_THREADS) || defined(HURD) || defined(RTEMS)) \ + && !defined(GC_USESIGRT_SIGNALS) +# define SIG_SUSPEND SIGUSR1 + /* SIGTSTP and SIGCONT could be used alternatively on FreeBSD. */ +# elif defined(GC_OPENBSD_THREADS) && !defined(GC_USESIGRT_SIGNALS) +# define SIG_SUSPEND SIGXFSZ +# elif defined(_SIGRTMIN) && !defined(CPPCHECK) +# define SIG_SUSPEND _SIGRTMIN + 6 +# else +# define SIG_SUSPEND SIGRTMIN + 6 +# endif +#endif /* GC_PTHREADS && !SIG_SUSPEND */ + +#if defined(GC_PTHREADS) && !defined(GC_SEM_INIT_PSHARED) +# define GC_SEM_INIT_PSHARED 0 +#endif + +/* Some macros for setjmp that works across signal handlers */ +/* were possible, and a couple of routines to facilitate */ +/* catching accesses to bad addresses when that's */ +/* possible/needed. */ +#if (defined(UNIX_LIKE) || (defined(NEED_FIND_LIMIT) && defined(CYGWIN32))) \ + && !defined(GC_NO_SIGSETJMP) +# if defined(SUNOS5SIGS) && !defined(FREEBSD) && !defined(LINUX) + EXTERN_C_END +# include + EXTERN_C_BEGIN +# endif + /* Define SETJMP and friends to be the version that restores */ + /* the signal mask. */ +# define SETJMP(env) sigsetjmp(env, 1) +# define LONGJMP(env, val) siglongjmp(env, val) +# define JMP_BUF sigjmp_buf +#else +# ifdef ECOS +# define SETJMP(env) hal_setjmp(env) +# else +# define SETJMP(env) setjmp(env) +# endif +# define LONGJMP(env, val) longjmp(env, val) +# define JMP_BUF jmp_buf +#endif /* !UNIX_LIKE || GC_NO_SIGSETJMP */ + +#if defined(DATASTART_USES_BSDGETDATASTART) + EXTERN_C_END +# include + EXTERN_C_BEGIN + GC_INNER ptr_t GC_FreeBSDGetDataStart(size_t, ptr_t); +# define DATASTART_IS_FUNC +#endif /* DATASTART_USES_BSDGETDATASTART */ + +#if defined(NEED_FIND_LIMIT) \ + || (defined(UNIX_LIKE) && !defined(NO_DEBUGGING)) \ + || (defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS)) \ + || (defined(WRAP_MARK_SOME) && defined(NO_SEH_AVAILABLE)) + typedef void (*GC_fault_handler_t)(int); + GC_INNER void GC_set_and_save_fault_handler(GC_fault_handler_t); +#endif + +#if defined(NEED_FIND_LIMIT) \ + || (defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS)) \ + || (defined(WRAP_MARK_SOME) && defined(NO_SEH_AVAILABLE)) + GC_EXTERN JMP_BUF GC_jmp_buf; + + /* Set up a handler for address faults which will longjmp to */ + /* GC_jmp_buf. */ + GC_INNER void GC_setup_temporary_fault_handler(void); + + /* Undo the effect of GC_setup_temporary_fault_handler. */ + GC_INNER void GC_reset_fault_handler(void); +#endif /* NEED_FIND_LIMIT || USE_PROC_FOR_LIBRARIES || WRAP_MARK_SOME */ + +/* Some convenience macros for cancellation support. */ +#ifdef CANCEL_SAFE +# if defined(GC_ASSERTIONS) \ + && (defined(USE_COMPILER_TLS) \ + || (defined(LINUX) && !defined(ARM32) && GC_GNUC_PREREQ(3, 3) \ + || defined(HPUX) /* and probably others ... */)) + extern __thread unsigned char GC_cancel_disable_count; +# define NEED_CANCEL_DISABLE_COUNT +# define INCR_CANCEL_DISABLE() ++GC_cancel_disable_count +# define DECR_CANCEL_DISABLE() --GC_cancel_disable_count +# define ASSERT_CANCEL_DISABLED() GC_ASSERT(GC_cancel_disable_count > 0) +# else +# define INCR_CANCEL_DISABLE() +# define DECR_CANCEL_DISABLE() +# define ASSERT_CANCEL_DISABLED() (void)0 +# endif /* !GC_ASSERTIONS */ +# define DISABLE_CANCEL(state) \ + do { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state); \ + INCR_CANCEL_DISABLE(); } while (0) +# define RESTORE_CANCEL(state) \ + do { ASSERT_CANCEL_DISABLED(); \ + pthread_setcancelstate(state, NULL); \ + DECR_CANCEL_DISABLE(); } while (0) +#else +# define DISABLE_CANCEL(state) (void)0 +# define RESTORE_CANCEL(state) (void)0 +# define ASSERT_CANCEL_DISABLED() (void)0 +#endif /* !CANCEL_SAFE */ + +/* Multiply 32-bit unsigned values (used by GC_push_contents_hdr). */ +#ifdef NO_LONGLONG64 +# define LONG_MULT(hprod, lprod, x, y) \ + do { \ + unsigned32 lx = (x) & 0xffffU; \ + unsigned32 ly = (y) & 0xffffU; \ + unsigned32 hx = (x) >> 16; \ + unsigned32 hy = (y) >> 16; \ + unsigned32 lxhy = lx * hy; \ + unsigned32 mid = hx * ly + lxhy; /* may overflow */ \ + unsigned32 lxly = lx * ly; \ + \ + lprod = (mid << 16) + lxly; /* may overflow */ \ + hprod = hx * hy + ((lprod) < lxly ? 1U : 0) \ + + (mid < lxhy ? (unsigned32)0x10000UL : 0) + (mid >> 16); \ + } while (0) +#elif defined(I386) && defined(__GNUC__) && !defined(NACL) +# define LONG_MULT(hprod, lprod, x, y) \ + __asm__ __volatile__ ("mull %2" \ + : "=a" (lprod), "=d" (hprod) \ + : "r" (y), "0" (x)) +#else +# if defined(__int64) && !defined(__GNUC__) && !defined(CPPCHECK) +# define ULONG_MULT_T unsigned __int64 +# else +# define ULONG_MULT_T unsigned long long +# endif +# define LONG_MULT(hprod, lprod, x, y) \ + do { \ + ULONG_MULT_T prod = (ULONG_MULT_T)(x) * (ULONG_MULT_T)(y); \ + \ + GC_STATIC_ASSERT(sizeof(x) + sizeof(y) <= sizeof(prod)); \ + hprod = (unsigned32)(prod >> 32); \ + lprod = (unsigned32)prod; \ + } while (0) +#endif /* !I386 && !NO_LONGLONG64 */ + +EXTERN_C_END + +#endif /* GC_PRIVATE_H */ + +#ifdef KEEP_BACK_PTRS +# include "gc/gc_backptr.h" +#endif + +EXTERN_C_BEGIN + +#if CPP_WORDSZ == 32 +# define START_FLAG (word)0xfedcedcb +# define END_FLAG (word)0xbcdecdef +#else +# define START_FLAG GC_WORD_C(0xFEDCEDCBfedcedcb) +# define END_FLAG GC_WORD_C(0xBCDECDEFbcdecdef) +#endif + /* Stored both one past the end of user object, and one before */ + /* the end of the object as seen by the allocator. */ + +#if defined(KEEP_BACK_PTRS) || defined(PRINT_BLACK_LIST) + /* Pointer "source"s that aren't real locations. */ + /* Used in oh_back_ptr fields and as "source" */ + /* argument to some marking functions. */ +# define MARKED_FOR_FINALIZATION ((ptr_t)(word)2) + /* Object was marked because it is finalizable. */ +# define MARKED_FROM_REGISTER ((ptr_t)(word)4) + /* Object was marked from a register. Hence the */ + /* source of the reference doesn't have an address. */ +# define NOT_MARKED ((ptr_t)(word)8) +#endif /* KEEP_BACK_PTRS || PRINT_BLACK_LIST */ + +/* Object header */ +typedef struct { +# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) + /* We potentially keep two different kinds of back */ + /* pointers. KEEP_BACK_PTRS stores a single back */ + /* pointer in each reachable object to allow reporting */ + /* of why an object was retained. MAKE_BACK_GRAPH */ + /* builds a graph containing the inverse of all */ + /* "points-to" edges including those involving */ + /* objects that have just become unreachable. This */ + /* allows detection of growing chains of unreachable */ + /* objects. It may be possible to eventually combine */ + /* both, but for now we keep them separate. Both */ + /* kinds of back pointers are hidden using the */ + /* following macros. In both cases, the plain version */ + /* is constrained to have the least significant bit of 1, */ + /* to allow it to be distinguished from a free list */ + /* link. This means the plain version must have the least */ + /* significant bit of zero. Note that blocks dropped by */ + /* black-listing will also have the least significant */ + /* bit clear once debugging has started; we are careful */ + /* never to overwrite such a value. */ +# if ALIGNMENT == 1 + /* Fudge back pointer to be even. */ +# define HIDE_BACK_PTR(p) GC_HIDE_POINTER(~(word)1 & (word)(p)) +# else +# define HIDE_BACK_PTR(p) GC_HIDE_POINTER(p) +# endif +# ifdef KEEP_BACK_PTRS + GC_hidden_pointer oh_back_ptr; +# endif +# ifdef MAKE_BACK_GRAPH + GC_hidden_pointer oh_bg_ptr; +# endif +# if defined(KEEP_BACK_PTRS) != defined(MAKE_BACK_GRAPH) + /* Keep double-pointer-sized alignment. */ + word oh_dummy; +# endif +# endif + const char * oh_string; /* object descriptor string (file name) */ + signed_word oh_int; /* object descriptor integer (line number) */ +# ifdef NEED_CALLINFO + struct callinfo oh_ci[NFRAMES]; +# endif +# ifndef SHORT_DBG_HDRS + word oh_sz; /* Original malloc arg. */ + word oh_sf; /* start flag */ +# endif /* SHORT_DBG_HDRS */ +} oh; +/* The size of the above structure is assumed not to de-align things, */ +/* and to be a multiple of the word length. */ + +#ifdef SHORT_DBG_HDRS +# define DEBUG_BYTES sizeof(oh) +# define UNCOLLECTABLE_DEBUG_BYTES DEBUG_BYTES +#else + /* Add space for END_FLAG, but use any extra space that was already */ + /* added to catch off-the-end pointers. */ + /* For uncollectible objects, the extra byte is not added. */ +# define UNCOLLECTABLE_DEBUG_BYTES (sizeof(oh) + sizeof(word)) +# define DEBUG_BYTES (UNCOLLECTABLE_DEBUG_BYTES - EXTRA_BYTES) +#endif + +/* Round bytes to words without adding extra byte at end. */ +#define SIMPLE_ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + WORDS_TO_BYTES(1) - 1) + +/* ADD_CALL_CHAIN stores a (partial) call chain into an object */ +/* header; it should be called with the allocator lock held. */ +/* PRINT_CALL_CHAIN prints the call chain stored in an object */ +/* to stderr. It requires we do not hold the allocator lock. */ +#if defined(SAVE_CALL_CHAIN) +# define ADD_CALL_CHAIN(base, ra) GC_save_callers(((oh *)(base)) -> oh_ci) +# define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci) +#elif defined(GC_ADD_CALLER) +# define ADD_CALL_CHAIN(base, ra) ((oh *)(base)) -> oh_ci[0].ci_pc = (ra) +# define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci) +#else +# define ADD_CALL_CHAIN(base, ra) +# define PRINT_CALL_CHAIN(base) +#endif + +#ifdef GC_ADD_CALLER +# define OPT_RA ra, +#else +# define OPT_RA +#endif + +/* Check whether object with base pointer p has debugging info */ +/* p is assumed to point to a legitimate object in our part */ +/* of the heap. */ +#ifdef SHORT_DBG_HDRS +# define GC_has_other_debug_info(p) 1 +#else + GC_INNER int GC_has_other_debug_info(ptr_t p); +#endif + +#if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) +# if defined(SHORT_DBG_HDRS) && !defined(CPPCHECK) +# error Non-ptr stored in object results in GC_HAS_DEBUG_INFO malfunction + /* We may mistakenly conclude that p has a debugging wrapper. */ +# endif +# if defined(PARALLEL_MARK) && defined(KEEP_BACK_PTRS) +# define GC_HAS_DEBUG_INFO(p) \ + ((AO_load((volatile AO_t *)(p)) & 1) != 0 \ + && GC_has_other_debug_info(p) > 0) + /* Atomic load is used as GC_store_back_pointer */ + /* stores oh_back_ptr atomically (p might point */ + /* to the field); this prevents a TSan warning. */ +# else +# define GC_HAS_DEBUG_INFO(p) \ + ((*(word *)(p) & 1) && GC_has_other_debug_info(p) > 0) +# endif +#else +# define GC_HAS_DEBUG_INFO(p) (GC_has_other_debug_info(p) > 0) +#endif /* !KEEP_BACK_PTRS && !MAKE_BACK_GRAPH */ + +EXTERN_C_END + +#endif /* GC_DBG_MLC_H */ + + +/* + * This implements a full, though not well-tuned, representation of the + * backwards points-to graph. This is used to test for non-GC-robust + * data structures; the code is not used during normal garbage collection. + * + * One restriction is that we drop all back-edges from nodes with very + * high in-degree, and simply add them add them to a list of such + * nodes. They are then treated as permanent roots. If this by itself + * doesn't introduce a space leak, then such nodes can't contribute to + * a growing space leak. + */ + +#ifdef MAKE_BACK_GRAPH + +#define MAX_IN 10 /* Maximum in-degree we handle directly */ + +#if (!defined(DBG_HDRS_ALL) || (ALIGNMENT != CPP_WORDSZ/8) \ + /* || !defined(UNIX_LIKE) */) && !defined(CPPCHECK) +# error The configuration does not support MAKE_BACK_GRAPH +#endif + +/* We store single back pointers directly in the object's oh_bg_ptr field. */ +/* If there is more than one ptr to an object, we store q | FLAG_MANY, */ +/* where q is a pointer to a back_edges object. */ +/* Every once in a while we use a back_edges object even for a single */ +/* pointer, since we need the other fields in the back_edges structure to */ +/* be present in some fraction of the objects. Otherwise we get serious */ +/* performance issues. */ +#define FLAG_MANY 2 + +typedef struct back_edges_struct { + word n_edges; /* Number of edges, including those in continuation */ + /* structures. */ + unsigned short flags; +# define RETAIN 1 /* Directly points to a reachable object; */ + /* retain for next GC. */ + unsigned short height_gc_no; + /* If height > 0, then the GC_gc_no value when it */ + /* was computed. If it was computed this cycle, then */ + /* it is current. If it was computed during the */ + /* last cycle, then it represents the old height, */ + /* which is only saved for live objects referenced by */ + /* dead ones. This may grow due to refs from newly */ + /* dead objects. */ + signed_word height; + /* Longest path through unreachable nodes to this node */ + /* that we found using depth first search. */ +# define HEIGHT_UNKNOWN (-2) +# define HEIGHT_IN_PROGRESS (-1) + + ptr_t edges[MAX_IN]; + struct back_edges_struct *cont; + /* Pointer to continuation structure; we use only the */ + /* edges field in the continuation. */ + /* also used as free list link. */ +} back_edges; + +/* Allocate a new back edge structure. Should be more sophisticated */ +/* if this were production code. */ +#define MAX_BACK_EDGE_STRUCTS 100000 +static back_edges *back_edge_space = 0; +STATIC int GC_n_back_edge_structs = 0; + /* Serves as pointer to never used */ + /* back_edges space. */ +static back_edges *avail_back_edges = 0; + /* Pointer to free list of deallocated */ + /* back_edges structures. */ + +static back_edges * new_back_edges(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + if (0 == back_edge_space) { + size_t bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP(MAX_BACK_EDGE_STRUCTS + * sizeof(back_edges)); + + GC_ASSERT(GC_page_size != 0); + back_edge_space = (back_edges *)GC_os_get_mem(bytes_to_get); + if (NULL == back_edge_space) + ABORT("Insufficient memory for back edges"); + } + if (0 != avail_back_edges) { + back_edges * result = avail_back_edges; + avail_back_edges = result -> cont; + result -> cont = 0; + return result; + } + if (GC_n_back_edge_structs >= MAX_BACK_EDGE_STRUCTS - 1) { + ABORT("Needed too much space for back edges: adjust " + "MAX_BACK_EDGE_STRUCTS"); + } + return back_edge_space + (GC_n_back_edge_structs++); +} + +/* Deallocate p and its associated continuation structures. */ +static void deallocate_back_edges(back_edges *p) +{ + back_edges *last = p; + + while (0 != last -> cont) last = last -> cont; + last -> cont = avail_back_edges; + avail_back_edges = p; +} + +/* Table of objects that are currently on the depth-first search */ +/* stack. Only objects with in-degree one are in this table. */ +/* Other objects are identified using HEIGHT_IN_PROGRESS. */ +/* FIXME: This data structure NEEDS IMPROVEMENT. */ +#define INITIAL_IN_PROGRESS 10000 +static ptr_t * in_progress_space = 0; +static size_t in_progress_size = 0; +static size_t n_in_progress = 0; + +static void push_in_progress(ptr_t p) +{ + GC_ASSERT(I_HOLD_LOCK()); + if (n_in_progress >= in_progress_size) { + ptr_t * new_in_progress_space; + + GC_ASSERT(GC_page_size != 0); + if (NULL == in_progress_space) { + in_progress_size = ROUNDUP_PAGESIZE_IF_MMAP(INITIAL_IN_PROGRESS + * sizeof(ptr_t)) + / sizeof(ptr_t); + new_in_progress_space = + (ptr_t *)GC_os_get_mem(in_progress_size * sizeof(ptr_t)); + } else { + in_progress_size *= 2; + new_in_progress_space = + (ptr_t *)GC_os_get_mem(in_progress_size * sizeof(ptr_t)); + if (new_in_progress_space != NULL) + BCOPY(in_progress_space, new_in_progress_space, + n_in_progress * sizeof(ptr_t)); + } +# ifndef GWW_VDB + GC_scratch_recycle_no_gww(in_progress_space, + n_in_progress * sizeof(ptr_t)); +# elif defined(LINT2) + /* TODO: implement GWW-aware recycling as in alloc_mark_stack */ + GC_noop1((word)in_progress_space); +# endif + in_progress_space = new_in_progress_space; + } + if (in_progress_space == 0) + ABORT("MAKE_BACK_GRAPH: Out of in-progress space: " + "Huge linear data structure?"); + in_progress_space[n_in_progress++] = p; +} + +static GC_bool is_in_progress(const char *p) +{ + size_t i; + for (i = 0; i < n_in_progress; ++i) { + if (in_progress_space[i] == p) return TRUE; + } + return FALSE; +} + +GC_INLINE void pop_in_progress(ptr_t p) +{ +# ifndef GC_ASSERTIONS + UNUSED_ARG(p); +# endif + --n_in_progress; + GC_ASSERT(in_progress_space[n_in_progress] == p); +} + +#define GET_OH_BG_PTR(p) \ + (ptr_t)GC_REVEAL_POINTER(((oh *)(p)) -> oh_bg_ptr) +#define SET_OH_BG_PTR(p,q) (((oh *)(p)) -> oh_bg_ptr = GC_HIDE_POINTER(q)) + +/* Ensure that p has a back_edges structure associated with it. */ +static void ensure_struct(ptr_t p) +{ + ptr_t old_back_ptr = GET_OH_BG_PTR(p); + + GC_ASSERT(I_HOLD_LOCK()); + if (!((word)old_back_ptr & FLAG_MANY)) { + back_edges *be = new_back_edges(); + be -> flags = 0; + if (0 == old_back_ptr) { + be -> n_edges = 0; + } else { + be -> n_edges = 1; + be -> edges[0] = old_back_ptr; + } + be -> height = HEIGHT_UNKNOWN; + be -> height_gc_no = (unsigned short)(GC_gc_no - 1); + GC_ASSERT((word)be >= (word)back_edge_space); + SET_OH_BG_PTR(p, (word)be | FLAG_MANY); + } +} + +/* Add the (forward) edge from p to q to the backward graph. Both p */ +/* q are pointers to the object base, i.e. pointers to an oh. */ +static void add_edge(ptr_t p, ptr_t q) +{ + ptr_t pred = GET_OH_BG_PTR(q); + back_edges * be, *be_cont; + word i; + + GC_ASSERT(p == GC_base(p) && q == GC_base(q)); + GC_ASSERT(I_HOLD_LOCK()); + if (!GC_HAS_DEBUG_INFO(q) || !GC_HAS_DEBUG_INFO(p)) { + /* This is really a misinterpreted free list link, since we saw */ + /* a pointer to a free list. Don't overwrite it! */ + return; + } + if (NULL == pred) { + static unsigned random_number = 13; +# define GOT_LUCKY_NUMBER (((++random_number) & 0x7f) == 0) + /* A not very random number we use to occasionally allocate a */ + /* back_edges structure even for a single backward edge. This */ + /* prevents us from repeatedly tracing back through very long */ + /* chains, since we will have some place to store height and */ + /* in_progress flags along the way. */ + + SET_OH_BG_PTR(q, p); + if (GOT_LUCKY_NUMBER) ensure_struct(q); + return; + } + + /* Check whether it was already in the list of predecessors. */ + { + back_edges *e = (back_edges *)((word)pred & ~(word)FLAG_MANY); + word n_edges; + word total; + int local = 0; + + if (((word)pred & FLAG_MANY) != 0) { + n_edges = e -> n_edges; + } else if (((word)COVERT_DATAFLOW(pred) & 1) == 0) { + /* A misinterpreted freelist link. */ + n_edges = 1; + local = -1; + } else { + n_edges = 0; + } + for (total = 0; total < n_edges; ++total) { + if (local == MAX_IN) { + e = e -> cont; + local = 0; + } + if (local >= 0) + pred = e -> edges[local++]; + if (pred == p) + return; + } + } + + ensure_struct(q); + be = (back_edges *)((word)GET_OH_BG_PTR(q) & ~(word)FLAG_MANY); + for (i = be -> n_edges, be_cont = be; i > MAX_IN; i -= MAX_IN) + be_cont = be_cont -> cont; + if (i == MAX_IN) { + be_cont -> cont = new_back_edges(); + be_cont = be_cont -> cont; + i = 0; + } + be_cont -> edges[i] = p; + be -> n_edges++; +# ifdef DEBUG_PRINT_BIG_N_EDGES + if (GC_print_stats == VERBOSE && be -> n_edges == 100) { + GC_err_printf("The following object has big in-degree:\n"); + GC_print_heap_obj(q); + } +# endif +} + +typedef void (*per_object_func)(ptr_t p, size_t n_bytes, word gc_descr); + +static GC_CALLBACK void per_object_helper(struct hblk *h, GC_word fn_ptr) +{ + hdr * hhdr = HDR(h); + size_t sz = (size_t)hhdr->hb_sz; + word descr = hhdr -> hb_descr; + per_object_func fn = *(per_object_func *)fn_ptr; + size_t i = 0; + + do { + fn((ptr_t)(h -> hb_body + i), sz, descr); + i += sz; + } while (i + sz <= BYTES_TO_WORDS(HBLKSIZE)); +} + +GC_INLINE void GC_apply_to_each_object(per_object_func fn) +{ + GC_apply_to_all_blocks(per_object_helper, (word)(&fn)); +} + +static void reset_back_edge(ptr_t p, size_t n_bytes, word gc_descr) +{ + UNUSED_ARG(n_bytes); + UNUSED_ARG(gc_descr); + GC_ASSERT(I_HOLD_LOCK()); + /* Skip any free list links, or dropped blocks */ + if (GC_HAS_DEBUG_INFO(p)) { + ptr_t old_back_ptr = GET_OH_BG_PTR(p); + if ((word)old_back_ptr & FLAG_MANY) { + back_edges *be = (back_edges *)((word)old_back_ptr & ~(word)FLAG_MANY); + if (!(be -> flags & RETAIN)) { + deallocate_back_edges(be); + SET_OH_BG_PTR(p, 0); + } else { + + GC_ASSERT(GC_is_marked(p)); + + /* Back edges may point to objects that will not be retained. */ + /* Delete them for now, but remember the height. */ + /* Some will be added back at next GC. */ + be -> n_edges = 0; + if (0 != be -> cont) { + deallocate_back_edges(be -> cont); + be -> cont = 0; + } + + GC_ASSERT(GC_is_marked(p)); + + /* We only retain things for one GC cycle at a time. */ + be -> flags &= (unsigned short)~RETAIN; + } + } else /* Simple back pointer */ { + /* Clear to avoid dangling pointer. */ + SET_OH_BG_PTR(p, 0); + } + } +} + +static void add_back_edges(ptr_t p, size_t n_bytes, word gc_descr) +{ + ptr_t current_p = p + sizeof(oh); + + /* For now, fix up non-length descriptors conservatively. */ + if((gc_descr & GC_DS_TAGS) != GC_DS_LENGTH) { + gc_descr = n_bytes; + } + + for (; (word)current_p < (word)(p + gc_descr); current_p += sizeof(word)) { + word current; + + LOAD_WORD_OR_CONTINUE(current, current_p); + FIXUP_POINTER(current); + if (current > GC_least_real_heap_addr + && current < GC_greatest_real_heap_addr) { + ptr_t target = (ptr_t)GC_base((void *)current); + + if (target != NULL) + add_edge(p, target); + } + } +} + +/* Rebuild the representation of the backward reachability graph. */ +/* Does not examine mark bits. Can be called before GC. */ +GC_INNER void GC_build_back_graph(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + GC_apply_to_each_object(add_back_edges); +} + +/* Return an approximation to the length of the longest simple path */ +/* through unreachable objects to p. We refer to this as the height */ +/* of p. */ +static word backwards_height(ptr_t p) +{ + word result; + ptr_t pred = GET_OH_BG_PTR(p); + back_edges *be; + + GC_ASSERT(I_HOLD_LOCK()); + if (NULL == pred) + return 1; + if (((word)pred & FLAG_MANY) == 0) { + if (is_in_progress(p)) return 0; /* DFS back edge, i.e. we followed */ + /* an edge to an object already */ + /* on our stack: ignore */ + push_in_progress(p); + result = backwards_height(pred) + 1; + pop_in_progress(p); + return result; + } + be = (back_edges *)((word)pred & ~(word)FLAG_MANY); + if (be -> height >= 0 && be -> height_gc_no == (unsigned short)GC_gc_no) + return (word)(be -> height); + /* Ignore back edges in DFS */ + if (be -> height == HEIGHT_IN_PROGRESS) return 0; + result = be -> height > 0 ? (word)(be -> height) : 1U; + be -> height = HEIGHT_IN_PROGRESS; + + { + back_edges *e = be; + word n_edges; + word total; + int local = 0; + + if (((word)pred & FLAG_MANY) != 0) { + n_edges = e -> n_edges; + } else if (((word)pred & 1) == 0) { + /* A misinterpreted freelist link. */ + n_edges = 1; + local = -1; + } else { + n_edges = 0; + } + for (total = 0; total < n_edges; ++total) { + word this_height; + if (local == MAX_IN) { + e = e -> cont; + local = 0; + } + if (local >= 0) + pred = e -> edges[local++]; + + /* Execute the following once for each predecessor pred of p */ + /* in the points-to graph. */ + if (GC_is_marked(pred) && ((word)GET_OH_BG_PTR(p) & FLAG_MANY) == 0) { + GC_COND_LOG_PRINTF("Found bogus pointer from %p to %p\n", + (void *)pred, (void *)p); + /* Reachable object "points to" unreachable one. */ + /* Could be caused by our lax treatment of GC descriptors. */ + this_height = 1; + } else { + this_height = backwards_height(pred); + } + if (this_height >= result) + result = this_height + 1; + } + } + + be -> height = (signed_word)result; + be -> height_gc_no = (unsigned short)GC_gc_no; + return result; +} + +STATIC word GC_max_height = 0; +STATIC ptr_t GC_deepest_obj = NULL; + +/* Compute the maximum height of every unreachable predecessor p of a */ +/* reachable object. Arrange to save the heights of all such objects p */ +/* so that they can be used in calculating the height of objects in the */ +/* next GC. */ +/* Set GC_max_height to be the maximum height we encounter, and */ +/* GC_deepest_obj to be the corresponding object. */ +static void update_max_height(ptr_t p, size_t n_bytes, word gc_descr) +{ + UNUSED_ARG(n_bytes); + UNUSED_ARG(gc_descr); + GC_ASSERT(I_HOLD_LOCK()); + if (GC_is_marked(p) && GC_HAS_DEBUG_INFO(p)) { + word p_height = 0; + ptr_t p_deepest_obj = 0; + ptr_t back_ptr; + back_edges *be = 0; + + /* If we remembered a height last time, use it as a minimum. */ + /* It may have increased due to newly unreachable chains pointing */ + /* to p, but it can't have decreased. */ + back_ptr = GET_OH_BG_PTR(p); + if (0 != back_ptr && ((word)back_ptr & FLAG_MANY)) { + be = (back_edges *)((word)back_ptr & ~(word)FLAG_MANY); + if (be -> height != HEIGHT_UNKNOWN) + p_height = (word)(be -> height); + } + + { + ptr_t pred = GET_OH_BG_PTR(p); + back_edges *e = (back_edges *)((word)pred & ~(word)FLAG_MANY); + word n_edges; + word total; + int local = 0; + + if (((word)pred & FLAG_MANY) != 0) { + n_edges = e -> n_edges; + } else if (pred != NULL && ((word)pred & 1) == 0) { + /* A misinterpreted freelist link. */ + n_edges = 1; + local = -1; + } else { + n_edges = 0; + } + for (total = 0; total < n_edges; ++total) { + if (local == MAX_IN) { + e = e -> cont; + local = 0; + } + if (local >= 0) + pred = e -> edges[local++]; + + /* Execute the following once for each predecessor pred of p */ + /* in the points-to graph. */ + if (!GC_is_marked(pred) && GC_HAS_DEBUG_INFO(pred)) { + word this_height = backwards_height(pred); + if (this_height > p_height) { + p_height = this_height; + p_deepest_obj = pred; + } + } + } + } + + if (p_height > 0) { + /* Remember the height for next time. */ + if (be == 0) { + ensure_struct(p); + back_ptr = GET_OH_BG_PTR(p); + be = (back_edges *)((word)back_ptr & ~(word)FLAG_MANY); + } + be -> flags |= RETAIN; + be -> height = (signed_word)p_height; + be -> height_gc_no = (unsigned short)GC_gc_no; + } + if (p_height > GC_max_height) { + GC_max_height = p_height; + GC_deepest_obj = p_deepest_obj; + } + } +} + +STATIC word GC_max_max_height = 0; + +GC_INNER void GC_traverse_back_graph(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + GC_max_height = 0; + GC_apply_to_each_object(update_max_height); + if (0 != GC_deepest_obj) + GC_set_mark_bit(GC_deepest_obj); /* Keep it until we can print it. */ +} + +void GC_print_back_graph_stats(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + GC_printf("Maximum backwards height of reachable objects" + " at GC #%lu is %lu\n", + (unsigned long)GC_gc_no, (unsigned long)GC_max_height); + if (GC_max_height > GC_max_max_height) { + ptr_t obj = GC_deepest_obj; + + GC_max_max_height = GC_max_height; + UNLOCK(); + GC_err_printf( + "The following unreachable object is last in a longest chain " + "of unreachable objects:\n"); + GC_print_heap_obj(obj); + LOCK(); + } + GC_COND_LOG_PRINTF("Needed max total of %d back-edge structs\n", + GC_n_back_edge_structs); + GC_apply_to_each_object(reset_back_edge); + GC_deepest_obj = 0; +} + +#endif /* MAKE_BACK_GRAPH */ + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +/* + * We maintain several hash tables of hblks that have had false hits. + * Each contains one bit per hash bucket; If any page in the bucket + * has had a false hit, we assume that all of them have. + * See the definition of page_hash_table in gc_priv.h. + * False hits from the stack(s) are much more dangerous than false hits + * from elsewhere, since the former can pin a large object that spans the + * block, even though it does not start on the dangerous block. + */ + +/* Externally callable routines are: */ +/* - GC_add_to_black_list_normal, */ +/* - GC_add_to_black_list_stack, */ +/* - GC_promote_black_lists. */ + +/* Pointers to individual tables. We replace one table by another by */ +/* switching these pointers. */ +STATIC word * GC_old_normal_bl = NULL; + /* Nonstack false references seen at last full */ + /* collection. */ +STATIC word * GC_incomplete_normal_bl = NULL; + /* Nonstack false references seen since last */ + /* full collection. */ +STATIC word * GC_old_stack_bl = NULL; +STATIC word * GC_incomplete_stack_bl = NULL; + +STATIC word GC_total_stack_black_listed = 0; + /* Number of bytes on stack blacklist. */ + +GC_INNER word GC_black_list_spacing = MINHINCR * HBLKSIZE; + /* Initial rough guess. */ + +STATIC void GC_clear_bl(word *); + +GC_INNER void GC_default_print_heap_obj_proc(ptr_t p) +{ + ptr_t base = (ptr_t)GC_base(p); + int kind = HDR(base)->hb_obj_kind; + + GC_err_printf("object at %p of appr. %lu bytes (%s)\n", + (void *)base, (unsigned long)GC_size(base), + kind == PTRFREE ? "atomic" : + IS_UNCOLLECTABLE(kind) ? "uncollectable" : "composite"); +} + +GC_INNER void (*GC_print_heap_obj)(ptr_t p) = GC_default_print_heap_obj_proc; + +#ifdef PRINT_BLACK_LIST + STATIC void GC_print_blacklisted_ptr(word p, ptr_t source, + const char *kind_str) + { + ptr_t base = (ptr_t)GC_base(source); + + if (0 == base) { + GC_err_printf("Black listing (%s) %p referenced from %p in %s\n", + kind_str, (void *)p, (void *)source, + NULL != source ? "root set" : "register"); + } else { + /* FIXME: We can't call the debug version of GC_print_heap_obj */ + /* (with PRINT_CALL_CHAIN) here because the allocator lock is */ + /* held and the world is stopped. */ + GC_err_printf("Black listing (%s) %p referenced from %p in" + " object at %p of appr. %lu bytes\n", + kind_str, (void *)p, (void *)source, + (void *)base, (unsigned long)GC_size(base)); + } + } +#endif /* PRINT_BLACK_LIST */ + +GC_INNER void GC_bl_init_no_interiors(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + if (GC_incomplete_normal_bl == 0) { + GC_old_normal_bl = (word *)GC_scratch_alloc(sizeof(page_hash_table)); + GC_incomplete_normal_bl = (word *)GC_scratch_alloc( + sizeof(page_hash_table)); + if (GC_old_normal_bl == 0 || GC_incomplete_normal_bl == 0) { + GC_err_printf("Insufficient memory for black list\n"); + EXIT(); + } + GC_clear_bl(GC_old_normal_bl); + GC_clear_bl(GC_incomplete_normal_bl); + } +} + +GC_INNER void GC_bl_init(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + if (!GC_all_interior_pointers) { + GC_bl_init_no_interiors(); + } + GC_ASSERT(NULL == GC_old_stack_bl && NULL == GC_incomplete_stack_bl); + GC_old_stack_bl = (word *)GC_scratch_alloc(sizeof(page_hash_table)); + GC_incomplete_stack_bl = (word *)GC_scratch_alloc(sizeof(page_hash_table)); + if (GC_old_stack_bl == 0 || GC_incomplete_stack_bl == 0) { + GC_err_printf("Insufficient memory for black list\n"); + EXIT(); + } + GC_clear_bl(GC_old_stack_bl); + GC_clear_bl(GC_incomplete_stack_bl); +} + +STATIC void GC_clear_bl(word *doomed) +{ + BZERO(doomed, sizeof(page_hash_table)); +} + +STATIC void GC_copy_bl(word *old, word *dest) +{ + BCOPY(old, dest, sizeof(page_hash_table)); +} + +static word total_stack_black_listed(void); + +/* Signal the completion of a collection. Turn the incomplete black */ +/* lists into new black lists, etc. */ +GC_INNER void GC_promote_black_lists(void) +{ + word * very_old_normal_bl = GC_old_normal_bl; + word * very_old_stack_bl = GC_old_stack_bl; + + GC_ASSERT(I_HOLD_LOCK()); + GC_old_normal_bl = GC_incomplete_normal_bl; + GC_old_stack_bl = GC_incomplete_stack_bl; + if (!GC_all_interior_pointers) { + GC_clear_bl(very_old_normal_bl); + } + GC_clear_bl(very_old_stack_bl); + GC_incomplete_normal_bl = very_old_normal_bl; + GC_incomplete_stack_bl = very_old_stack_bl; + GC_total_stack_black_listed = total_stack_black_listed(); + GC_VERBOSE_LOG_PRINTF( + "%lu bytes in heap blacklisted for interior pointers\n", + (unsigned long)GC_total_stack_black_listed); + if (GC_total_stack_black_listed != 0) { + GC_black_list_spacing = + HBLKSIZE*(GC_heapsize/GC_total_stack_black_listed); + } + if (GC_black_list_spacing < 3 * HBLKSIZE) { + GC_black_list_spacing = 3 * HBLKSIZE; + } + if (GC_black_list_spacing > MAXHINCR * HBLKSIZE) { + GC_black_list_spacing = MAXHINCR * HBLKSIZE; + /* Makes it easier to allocate really huge blocks, which otherwise */ + /* may have problems with nonuniform blacklist distributions. */ + /* This way we should always succeed immediately after growing the */ + /* heap. */ + } +} + +GC_INNER void GC_unpromote_black_lists(void) +{ + if (!GC_all_interior_pointers) { + GC_copy_bl(GC_old_normal_bl, GC_incomplete_normal_bl); + } + GC_copy_bl(GC_old_stack_bl, GC_incomplete_stack_bl); +} + +#if defined(PARALLEL_MARK) && defined(THREAD_SANITIZER) +# define backlist_set_pht_entry_from_index(db, index) \ + set_pht_entry_from_index_concurrent(db, index) +#else + /* It is safe to set a bit in a blacklist even without */ + /* synchronization, the only drawback is that we might have */ + /* to redo blacklisting sometimes. */ +# define backlist_set_pht_entry_from_index(bl, index) \ + set_pht_entry_from_index(bl, index) +#endif + +/* P is not a valid pointer reference, but it falls inside */ +/* the plausible heap bounds. */ +/* Add it to the normal incomplete black list if appropriate. */ +#ifdef PRINT_BLACK_LIST + GC_INNER void GC_add_to_black_list_normal(word p, ptr_t source) +#else + GC_INNER void GC_add_to_black_list_normal(word p) +#endif +{ +# ifndef PARALLEL_MARK + GC_ASSERT(I_HOLD_LOCK()); +# endif + if (GC_modws_valid_offsets[p & (sizeof(word)-1)]) { + word index = PHT_HASH((word)p); + + if (HDR(p) == 0 || get_pht_entry_from_index(GC_old_normal_bl, index)) { +# ifdef PRINT_BLACK_LIST + if (!get_pht_entry_from_index(GC_incomplete_normal_bl, index)) { + GC_print_blacklisted_ptr(p, source, "normal"); + } +# endif + backlist_set_pht_entry_from_index(GC_incomplete_normal_bl, index); + } /* else this is probably just an interior pointer to an allocated */ + /* object, and isn't worth black listing. */ + } +} + +/* And the same for false pointers from the stack. */ +#ifdef PRINT_BLACK_LIST + GC_INNER void GC_add_to_black_list_stack(word p, ptr_t source) +#else + GC_INNER void GC_add_to_black_list_stack(word p) +#endif +{ + word index = PHT_HASH((word)p); + +# ifndef PARALLEL_MARK + GC_ASSERT(I_HOLD_LOCK()); +# endif + if (HDR(p) == 0 || get_pht_entry_from_index(GC_old_stack_bl, index)) { +# ifdef PRINT_BLACK_LIST + if (!get_pht_entry_from_index(GC_incomplete_stack_bl, index)) { + GC_print_blacklisted_ptr(p, source, "stack"); + } +# endif + backlist_set_pht_entry_from_index(GC_incomplete_stack_bl, index); + } +} + +/* Is the block starting at h of size len bytes black-listed? If so, */ +/* return the address of the next plausible r such that (r,len) might */ +/* not be black-listed. (Pointer r may not actually be in the heap. */ +/* We guarantee only that every smaller value of r after h is also */ +/* black-listed.) If (h,len) is not, then return NULL. Knows about */ +/* the structure of the black list hash tables. Assumes the allocator */ +/* lock is held but no assertion about it by design. */ +GC_API struct GC_hblk_s *GC_CALL GC_is_black_listed(struct GC_hblk_s *h, + GC_word len) +{ + word index = PHT_HASH((word)h); + word i; + word nblocks; + + if (!GC_all_interior_pointers + && (get_pht_entry_from_index(GC_old_normal_bl, index) + || get_pht_entry_from_index(GC_incomplete_normal_bl, index))) { + return h + 1; + } + + nblocks = divHBLKSZ(len); + for (i = 0;;) { + if (GC_old_stack_bl[divWORDSZ(index)] == 0 + && GC_incomplete_stack_bl[divWORDSZ(index)] == 0) { + /* An easy case. */ + i += (word)CPP_WORDSZ - modWORDSZ(index); + } else { + if (get_pht_entry_from_index(GC_old_stack_bl, index) + || get_pht_entry_from_index(GC_incomplete_stack_bl, index)) { + return h + (i+1); + } + i++; + } + if (i >= nblocks) break; + index = PHT_HASH((word)(h + i)); + } + return NULL; +} + +/* Return the number of blacklisted blocks in a given range. */ +/* Used only for statistical purposes. */ +/* Looks only at the GC_incomplete_stack_bl. */ +STATIC word GC_number_stack_black_listed(struct hblk *start, + struct hblk *endp1) +{ + struct hblk * h; + word result = 0; + + for (h = start; (word)h < (word)endp1; h++) { + word index = PHT_HASH((word)h); + + if (get_pht_entry_from_index(GC_old_stack_bl, index)) result++; + } + return result; +} + +/* Return the total number of (stack) black-listed bytes. */ +static word total_stack_black_listed(void) +{ + unsigned i; + word total = 0; + + for (i = 0; i < GC_n_heap_sects; i++) { + struct hblk * start = (struct hblk *) GC_heap_sects[i].hs_start; + struct hblk * endp1 = start + divHBLKSZ(GC_heap_sects[i].hs_bytes); + + total += GC_number_stack_black_listed(start, endp1); + } + return total * HBLKSIZE; +} + +/* + * Copyright (c) 1992-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +#ifdef CHECKSUMS + +/* This is debugging code intended to verify the results of dirty bit */ +/* computations. Works only in a single threaded environment. */ +# define NSUMS 10000 +# define OFFSET 0x10000 + +typedef struct { + GC_bool new_valid; + word old_sum; + word new_sum; + struct hblk * block; /* Block to which this refers + OFFSET */ + /* to hide it from collector. */ +} page_entry; + +page_entry GC_sums[NSUMS]; + +STATIC word GC_faulted[NSUMS] = { 0 }; + /* Record of pages on which we saw a write fault. */ + +STATIC size_t GC_n_faulted = 0; + +#ifdef MPROTECT_VDB + void GC_record_fault(struct hblk * h) + { + word page = (word)h & ~(word)(GC_page_size-1); + + GC_ASSERT(GC_page_size != 0); + if (GC_n_faulted >= NSUMS) ABORT("write fault log overflowed"); + GC_faulted[GC_n_faulted++] = page; + } +#endif + +STATIC GC_bool GC_was_faulted(struct hblk *h) +{ + size_t i; + word page = (word)h & ~(word)(GC_page_size-1); + + for (i = 0; i < GC_n_faulted; ++i) { + if (GC_faulted[i] == page) return TRUE; + } + return FALSE; +} + +STATIC word GC_checksum(struct hblk *h) +{ + word *p = (word *)h; + word *lim = (word *)(h+1); + word result = 0; + + while ((word)p < (word)lim) { + result += *p++; + } + return result | SIGNB; /* does not look like pointer */ +} + +int GC_n_dirty_errors = 0; +int GC_n_faulted_dirty_errors = 0; +unsigned long GC_n_clean = 0; +unsigned long GC_n_dirty = 0; + +STATIC void GC_update_check_page(struct hblk *h, int index) +{ + page_entry *pe = GC_sums + index; + hdr * hhdr = HDR(h); + struct hblk *b; + + if (pe -> block != 0 && pe -> block != h + OFFSET) ABORT("goofed"); + pe -> old_sum = pe -> new_sum; + pe -> new_sum = GC_checksum(h); +# if !defined(MSWIN32) && !defined(MSWINCE) + if (pe -> new_sum != SIGNB && !GC_page_was_ever_dirty(h)) { + GC_err_printf("GC_page_was_ever_dirty(%p) is wrong\n", (void *)h); + } +# endif + if (GC_page_was_dirty(h)) { + GC_n_dirty++; + } else { + GC_n_clean++; + } + for (b = h; IS_FORWARDING_ADDR_OR_NIL(hhdr) && hhdr != NULL; + hhdr = HDR(b)) { + b = FORWARDED_ADDR(b, hhdr); + } + if (pe -> new_valid && hhdr != NULL && !IS_PTRFREE(hhdr) +# ifdef SOFT_VDB + && !HBLK_IS_FREE(hhdr) +# endif + && pe -> old_sum != pe -> new_sum) { + if (!GC_page_was_dirty(h) || !GC_page_was_ever_dirty(h)) { + GC_bool was_faulted = GC_was_faulted(h); + /* Set breakpoint here */GC_n_dirty_errors++; + if (was_faulted) GC_n_faulted_dirty_errors++; + } + } + pe -> new_valid = TRUE; + pe -> block = h + OFFSET; +} + +word GC_bytes_in_used_blocks = 0; + +STATIC void GC_CALLBACK GC_add_block(struct hblk *h, GC_word dummy) +{ + hdr * hhdr = HDR(h); + + UNUSED_ARG(dummy); + GC_bytes_in_used_blocks += (hhdr->hb_sz + HBLKSIZE-1) & ~(word)(HBLKSIZE-1); +} + +STATIC void GC_check_blocks(void) +{ + word bytes_in_free_blocks = GC_large_free_bytes; + + GC_bytes_in_used_blocks = 0; + GC_apply_to_all_blocks(GC_add_block, 0); + GC_COND_LOG_PRINTF("GC_bytes_in_used_blocks= %lu," + " bytes_in_free_blocks= %lu, heapsize= %lu\n", + (unsigned long)GC_bytes_in_used_blocks, + (unsigned long)bytes_in_free_blocks, + (unsigned long)GC_heapsize); + if (GC_bytes_in_used_blocks + bytes_in_free_blocks != GC_heapsize) { + GC_err_printf("LOST SOME BLOCKS!!\n"); + } +} + +/* Should be called immediately after GC_read_dirty. */ +void GC_check_dirty(void) +{ + int index; + unsigned i; + struct hblk *h; + ptr_t start; + + GC_check_blocks(); + + GC_n_dirty_errors = 0; + GC_n_faulted_dirty_errors = 0; + GC_n_clean = 0; + GC_n_dirty = 0; + + index = 0; + for (i = 0; i < GC_n_heap_sects; i++) { + start = GC_heap_sects[i].hs_start; + for (h = (struct hblk *)start; + (word)h < (word)(start + GC_heap_sects[i].hs_bytes); h++) { + GC_update_check_page(h, index); + index++; + if (index >= NSUMS) goto out; + } + } +out: + GC_COND_LOG_PRINTF("Checked %lu clean and %lu dirty pages\n", + GC_n_clean, GC_n_dirty); + if (GC_n_dirty_errors > 0) { + GC_err_printf("Found %d dirty bit errors (%d were faulted)\n", + GC_n_dirty_errors, GC_n_faulted_dirty_errors); + } + for (i = 0; i < GC_n_faulted; ++i) { + GC_faulted[i] = 0; /* Don't expose block pointers to GC */ + } + GC_n_faulted = 0; +} + +#endif /* CHECKSUMS */ + +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 2001 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + +/* Private declarations of GC marker data structures and macros */ + +/* + * Declarations of mark stack. Needed by marker and client supplied mark + * routines. Transitively include gc_priv.h. + */ +#ifndef GC_PMARK_H +#define GC_PMARK_H + +#if defined(HAVE_CONFIG_H) && !defined(GC_PRIVATE_H) + /* When gc_pmark.h is included from gc_priv.h, some of macros might */ + /* be undefined in gcconfig.h, so skip config.h in this case. */ + +#endif + +#ifndef GC_BUILD +# define GC_BUILD +#endif + +#if (defined(__linux__) || defined(__GLIBC__) || defined(__GNU__)) \ + && !defined(_GNU_SOURCE) && defined(GC_PTHREADS) \ + && !defined(GC_NO_PTHREAD_SIGMASK) +# define _GNU_SOURCE 1 +#endif + +#if defined(KEEP_BACK_PTRS) || defined(PRINT_BLACK_LIST) + +#endif + + + + +EXTERN_C_BEGIN + +/* The real declarations of the following is in gc_priv.h, so that */ +/* we can avoid scanning GC_mark_procs table. */ + +#ifndef MARK_DESCR_OFFSET +# define MARK_DESCR_OFFSET sizeof(word) +#endif + +/* Mark descriptor stuff that should remain private for now, mostly */ +/* because it's hard to export CPP_WORDSZ without including gcconfig.h. */ +#define BITMAP_BITS (CPP_WORDSZ - GC_DS_TAG_BITS) +#define PROC(descr) \ + (GC_mark_procs[((descr) >> GC_DS_TAG_BITS) & (GC_MAX_MARK_PROCS-1)]) +#define ENV(descr) \ + ((descr) >> (GC_DS_TAG_BITS + GC_LOG_MAX_MARK_PROCS)) +#define MAX_ENV (((word)1 << (BITMAP_BITS - GC_LOG_MAX_MARK_PROCS)) - 1) + +GC_EXTERN unsigned GC_n_mark_procs; + +/* Number of mark stack entries to discard on overflow. */ +#define GC_MARK_STACK_DISCARDS (INITIAL_MARK_STACK_SIZE/8) + +#ifdef PARALLEL_MARK + /* + * Allow multiple threads to participate in the marking process. + * This works roughly as follows: + * The main mark stack never shrinks, but it can grow. + * + * The initiating threads holds the allocator lock, sets GC_help_wanted. + * + * Other threads: + * 1) update helper_count (while holding the mark lock). + * 2) allocate a local mark stack + * repeatedly: + * 3) Steal a global mark stack entry by atomically replacing + * its descriptor with 0. + * 4) Copy it to the local stack. + * 5) Mark on the local stack until it is empty, or + * it may be profitable to copy it back. + * 6) If necessary, copy local stack to global one, + * holding the mark lock. + * 7) Stop when the global mark stack is empty. + * 8) decrement helper_count (holding the mark lock). + * + * This is an experiment to see if we can do something along the lines + * of the University of Tokyo SGC in a less intrusive, though probably + * also less performant, way. + */ + + /* GC_mark_stack_top is protected by the mark lock. */ + + /* + * GC_notify_all_marker() is used when GC_help_wanted is first set, + * when the last helper becomes inactive, + * when something is added to the global mark stack, and just after + * GC_mark_no is incremented. + * This could be split into multiple CVs (and probably should be to + * scale to really large numbers of processors.) + */ +#endif /* PARALLEL_MARK */ + +GC_INNER mse * GC_signal_mark_stack_overflow(mse *msp); + +/* Push the object obj with corresponding heap block header hhdr onto */ +/* the mark stack. Returns the updated mark_stack_top value. */ +GC_INLINE mse * GC_push_obj(ptr_t obj, hdr * hhdr, mse * mark_stack_top, + mse * mark_stack_limit) +{ + GC_ASSERT(!HBLK_IS_FREE(hhdr)); + if (!IS_PTRFREE(hhdr)) { + mark_stack_top++; + if ((word)mark_stack_top >= (word)mark_stack_limit) { + mark_stack_top = GC_signal_mark_stack_overflow(mark_stack_top); + } + mark_stack_top -> mse_start = obj; + mark_stack_top -> mse_descr.w = hhdr -> hb_descr; + } + return mark_stack_top; +} + +/* Push the contents of current onto the mark stack if it is a valid */ +/* ptr to a currently unmarked object. Mark it. */ +#define PUSH_CONTENTS(current, mark_stack_top, mark_stack_limit, source) \ + do { \ + hdr * my_hhdr; \ + HC_GET_HDR(current, my_hhdr, source); /* contains "break" */ \ + mark_stack_top = GC_push_contents_hdr(current, mark_stack_top, \ + mark_stack_limit, \ + source, my_hhdr, TRUE); \ + } while (0) + +/* Set mark bit, exit (using "break" statement) if it is already set. */ +#ifdef USE_MARK_BYTES +# if defined(PARALLEL_MARK) && defined(AO_HAVE_char_store) \ + && !defined(BASE_ATOMIC_OPS_EMULATED) + /* There is a race here, and we may set the bit twice in the */ + /* concurrent case. This can result in the object being pushed */ + /* twice. But that is only a performance issue. */ +# define SET_MARK_BIT_EXIT_IF_SET(hhdr, bit_no) \ + { /* cannot use do-while(0) here */ \ + volatile unsigned char * mark_byte_addr = \ + (unsigned char *)(hhdr)->hb_marks + (bit_no); \ + /* Unordered atomic load and store are sufficient here. */ \ + if (AO_char_load(mark_byte_addr) != 0) \ + break; /* go to the enclosing loop end */ \ + AO_char_store(mark_byte_addr, 1); \ + } +# else +# define SET_MARK_BIT_EXIT_IF_SET(hhdr, bit_no) \ + { /* cannot use do-while(0) here */ \ + char * mark_byte_addr = (char *)(hhdr)->hb_marks + (bit_no); \ + if (*mark_byte_addr != 0) break; /* go to the enclosing loop end */ \ + *mark_byte_addr = 1; \ + } +# endif /* !PARALLEL_MARK */ +#else +# ifdef PARALLEL_MARK + /* This is used only if we explicitly set USE_MARK_BITS. */ + /* The following may fail to exit even if the bit was already set. */ + /* For our uses, that's benign: */ +# ifdef THREAD_SANITIZER +# define OR_WORD_EXIT_IF_SET(addr, bits) \ + { /* cannot use do-while(0) here */ \ + if (!((word)AO_load((volatile AO_t *)(addr)) & (bits))) { \ + /* Atomic load is just to avoid TSan false positive. */ \ + AO_or((volatile AO_t *)(addr), (AO_t)(bits)); \ + } else { \ + break; /* go to the enclosing loop end */ \ + } \ + } +# else +# define OR_WORD_EXIT_IF_SET(addr, bits) \ + { /* cannot use do-while(0) here */ \ + if (!(*(addr) & (bits))) { \ + AO_or((volatile AO_t *)(addr), (AO_t)(bits)); \ + } else { \ + break; /* go to the enclosing loop end */ \ + } \ + } +# endif /* !THREAD_SANITIZER */ +# else +# define OR_WORD_EXIT_IF_SET(addr, bits) \ + { /* cannot use do-while(0) here */ \ + word old = *(addr); \ + word my_bits = (bits); \ + if ((old & my_bits) != 0) \ + break; /* go to the enclosing loop end */ \ + *(addr) = old | my_bits; \ + } +# endif /* !PARALLEL_MARK */ +# define SET_MARK_BIT_EXIT_IF_SET(hhdr, bit_no) \ + { /* cannot use do-while(0) here */ \ + word * mark_word_addr = (hhdr)->hb_marks + divWORDSZ(bit_no); \ + OR_WORD_EXIT_IF_SET(mark_word_addr, \ + (word)1 << modWORDSZ(bit_no)); /* contains "break" */ \ + } +#endif /* !USE_MARK_BYTES */ + +#ifdef ENABLE_TRACE +# define TRACE(source, cmd) \ + if (GC_trace_addr != 0 && (ptr_t)(source) == GC_trace_addr) cmd +# define TRACE_TARGET(target, cmd) \ + if (GC_trace_addr != NULL && GC_is_heap_ptr(GC_trace_addr) \ + && (target) == *(ptr_t *)GC_trace_addr) cmd +#else +# define TRACE(source, cmd) +# define TRACE_TARGET(source, cmd) +#endif + +/* If the mark bit corresponding to current is not set, set it, and */ +/* push the contents of the object on the mark stack. Current points */ +/* to the beginning of the object. We rely on the fact that the */ +/* preceding header calculation will succeed for a pointer past the */ +/* first page of an object, only if it is in fact a valid pointer */ +/* to the object. Thus we can omit the otherwise necessary tests */ +/* here. Note in particular that the "displ" value is the displacement */ +/* from the beginning of the heap block, which may itself be in the */ +/* interior of a large object. */ +GC_INLINE mse * GC_push_contents_hdr(ptr_t current, mse * mark_stack_top, + mse * mark_stack_limit, ptr_t source, + hdr * hhdr, GC_bool do_offset_check) +{ + do { + size_t displ = HBLKDISPL(current); /* Displacement in block; in bytes. */ + /* displ is always within range. If current doesn't point to the */ + /* first block, then we are in the all_interior_pointers case, and */ + /* it is safe to use any displacement value. */ + ptr_t base = current; +# ifdef MARK_BIT_PER_OBJ + unsigned32 gran_displ; /* high_prod */ + unsigned32 inv_sz = hhdr -> hb_inv_sz; + +# else + size_t gran_displ = BYTES_TO_GRANULES(displ); + size_t gran_offset = hhdr -> hb_map[gran_displ]; + size_t byte_offset = displ & (GC_GRANULE_BYTES-1); + + /* The following always fails for large block references. */ + if (EXPECT((gran_offset | byte_offset) != 0, FALSE)) +# endif + { +# ifdef MARK_BIT_PER_OBJ + if (EXPECT(inv_sz == LARGE_INV_SZ, FALSE)) +# else + if ((hhdr -> hb_flags & LARGE_BLOCK) != 0) +# endif + { + /* gran_offset is bogus. */ + size_t obj_displ; + + base = (ptr_t)hhdr->hb_block; + obj_displ = (size_t)(current - base); + if (obj_displ != displ) { + GC_ASSERT(obj_displ < hhdr -> hb_sz); + /* Must be in all_interior_pointer case, not first block */ + /* already did validity check on cache miss. */ + } else if (do_offset_check && !GC_valid_offsets[obj_displ]) { + GC_ADD_TO_BLACK_LIST_NORMAL(current, source); + break; + } + GC_ASSERT(hhdr -> hb_sz > HBLKSIZE + || hhdr -> hb_block == HBLKPTR(current)); + GC_ASSERT((word)hhdr->hb_block <= (word)current); + gran_displ = 0; + } else { +# ifndef MARK_BIT_PER_OBJ + size_t obj_displ = GRANULES_TO_BYTES(gran_offset) + byte_offset; + +# else + unsigned32 low_prod; + + LONG_MULT(gran_displ, low_prod, (unsigned32)displ, inv_sz); + if ((low_prod >> 16) != 0) +# endif + { +# ifdef MARK_BIT_PER_OBJ + size_t obj_displ; + + /* Accurate enough if HBLKSIZE <= 2**15. */ + GC_STATIC_ASSERT(HBLKSIZE <= (1 << 15)); + obj_displ = (((low_prod >> 16) + 1) * (size_t)hhdr->hb_sz) >> 16; +# endif + if (do_offset_check && !GC_valid_offsets[obj_displ]) { + GC_ADD_TO_BLACK_LIST_NORMAL(current, source); + break; + } +# ifndef MARK_BIT_PER_OBJ + gran_displ -= gran_offset; +# endif + base -= obj_displ; + } + } + } +# ifdef MARK_BIT_PER_OBJ + /* May get here for pointer to start of block not at the */ + /* beginning of object. If so, it is valid, and we are fine. */ + GC_ASSERT(gran_displ <= HBLK_OBJS(hhdr -> hb_sz)); +# else + GC_ASSERT(hhdr == GC_find_header(base)); + GC_ASSERT(gran_displ % BYTES_TO_GRANULES(hhdr -> hb_sz) == 0); +# endif + TRACE(source, GC_log_printf("GC #%lu: passed validity tests\n", + (unsigned long)GC_gc_no)); + SET_MARK_BIT_EXIT_IF_SET(hhdr, gran_displ); /* contains "break" */ + TRACE(source, GC_log_printf("GC #%lu: previously unmarked\n", + (unsigned long)GC_gc_no)); + TRACE_TARGET(base, GC_log_printf("GC #%lu: marking %p from %p instead\n", + (unsigned long)GC_gc_no, (void *)base, + (void *)source)); + INCR_MARKS(hhdr); + GC_STORE_BACK_PTR(source, base); + mark_stack_top = GC_push_obj(base, hhdr, mark_stack_top, + mark_stack_limit); + } while (0); + return mark_stack_top; +} + +#if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS) +# define PUSH_ONE_CHECKED_STACK(p, source) \ + GC_mark_and_push_stack((ptr_t)(p), (ptr_t)(source)) +#else +# define PUSH_ONE_CHECKED_STACK(p, source) \ + GC_mark_and_push_stack((ptr_t)(p)) +#endif + +/* Push a single value onto mark stack. Mark from the object */ +/* pointed to by p. The argument should be of word type. */ +/* Invoke FIXUP_POINTER() before any further processing. p is */ +/* considered valid even if it is an interior pointer. Previously */ +/* marked objects are not pushed. Hence we make progress even */ +/* if the mark stack overflows. */ +#ifdef NEED_FIXUP_POINTER + /* Try both the raw version and the fixed up one. */ +# define GC_PUSH_ONE_STACK(p, source) \ + do { \ + word pp = (p); \ + \ + if ((p) > (word)GC_least_plausible_heap_addr \ + && (p) < (word)GC_greatest_plausible_heap_addr) { \ + PUSH_ONE_CHECKED_STACK(p, source); \ + } \ + FIXUP_POINTER(pp); \ + if (pp > (word)GC_least_plausible_heap_addr \ + && pp < (word)GC_greatest_plausible_heap_addr) { \ + PUSH_ONE_CHECKED_STACK(pp, source); \ + } \ + } while (0) +#else /* !NEED_FIXUP_POINTER */ +# define GC_PUSH_ONE_STACK(p, source) \ + do { \ + if ((p) > (word)GC_least_plausible_heap_addr \ + && (p) < (word)GC_greatest_plausible_heap_addr) { \ + PUSH_ONE_CHECKED_STACK(p, source); \ + } \ + } while (0) +#endif + +/* As above, but interior pointer recognition as for normal heap pointers. */ +#define GC_PUSH_ONE_HEAP(p, source, mark_stack_top) \ + do { \ + FIXUP_POINTER(p); \ + if ((p) > (word)GC_least_plausible_heap_addr \ + && (p) < (word)GC_greatest_plausible_heap_addr) \ + mark_stack_top = GC_mark_and_push((void *)(p), mark_stack_top, \ + GC_mark_stack_limit, (void * *)(source)); \ + } while (0) + +/* Mark starting at mark stack entry top (incl.) down to */ +/* mark stack entry bottom (incl.). Stop after performing */ +/* about one page worth of work. Return the new mark stack */ +/* top entry. */ +GC_INNER mse * GC_mark_from(mse * top, mse * bottom, mse *limit); + +#define MARK_FROM_MARK_STACK() \ + GC_mark_stack_top = GC_mark_from(GC_mark_stack_top, \ + GC_mark_stack, \ + GC_mark_stack + GC_mark_stack_size); + +#define GC_mark_stack_empty() ((word)GC_mark_stack_top < (word)GC_mark_stack) + + /* Current state of marking, as follows.*/ + + /* We say something is dirty if it was */ + /* written since the last time we */ + /* retrieved dirty bits. We say it's */ + /* grungy if it was marked dirty in the */ + /* last set of bits we retrieved. */ + + /* Invariant "I": all roots and marked */ + /* objects p are either dirty, or point */ + /* to objects q that are either marked */ + /* or a pointer to q appears in a range */ + /* on the mark stack. */ + +#define MS_NONE 0 /* No marking in progress. "I" holds. */ + /* Mark stack is empty. */ + +#define MS_PUSH_RESCUERS 1 /* Rescuing objects are currently */ + /* being pushed. "I" holds, except */ + /* that grungy roots may point to */ + /* unmarked objects, as may marked */ + /* grungy objects above GC_scan_ptr. */ + +#define MS_PUSH_UNCOLLECTABLE 2 /* "I" holds, except that marked */ + /* uncollectible objects above */ + /* GC_scan_ptr may point to unmarked */ + /* objects. Roots may point to */ + /* unmarked objects. */ + +#define MS_ROOTS_PUSHED 3 /* "I" holds, mark stack may be nonempty. */ + +#define MS_PARTIALLY_INVALID 4 /* "I" may not hold, e.g. because of */ + /* the mark stack overflow. However, */ + /* marked heap objects below */ + /* GC_scan_ptr point to marked or */ + /* stacked objects. */ + +#define MS_INVALID 5 /* "I" may not hold. */ + +EXTERN_C_END + +#endif /* GC_PMARK_H */ + + +#ifdef GC_GCJ_SUPPORT + +/* + * This is an allocator interface tuned for gcj (the GNU static + * java compiler). + * + * Each allocated object has a pointer in its first word to a vtable, + * which for our purposes is simply a structure describing the type of + * the object. + * This descriptor structure contains a GC marking descriptor at offset + * MARK_DESCR_OFFSET. + * + * It is hoped that this interface may also be useful for other systems, + * possibly with some tuning of the constants. But the immediate goal + * is to get better gcj performance. + * + * We assume: counting on explicit initialization of this interface is OK. + */ + +#include "gc/gc_gcj.h" + + +int GC_gcj_kind = 0; /* Object kind for objects with descriptors */ + /* in "vtable". */ +int GC_gcj_debug_kind = 0; + /* The kind of objects that is always marked */ + /* with a mark proc call. */ + +STATIC struct GC_ms_entry *GC_CALLBACK GC_gcj_fake_mark_proc(word *addr, + struct GC_ms_entry *mark_stack_ptr, + struct GC_ms_entry * mark_stack_limit, word env) +{ + UNUSED_ARG(addr); + UNUSED_ARG(mark_stack_limit); + UNUSED_ARG(env); +# if defined(FUNCPTR_IS_WORD) && defined(CPPCHECK) + GC_noop1((word)&GC_init_gcj_malloc); +# endif + ABORT_RET("No client gcj mark proc is specified"); + return mark_stack_ptr; +} + +#ifdef FUNCPTR_IS_WORD + GC_API void GC_CALL GC_init_gcj_malloc(int mp_index, void *mp) + { + GC_init_gcj_malloc_mp((unsigned)mp_index, (GC_mark_proc)(word)mp); + } +#endif /* FUNCPTR_IS_WORD */ + +GC_API void GC_CALL GC_init_gcj_malloc_mp(unsigned mp_index, GC_mark_proc mp) +{ +# ifndef GC_IGNORE_GCJ_INFO + GC_bool ignore_gcj_info; +# endif + + if (mp == 0) /* In case GC_DS_PROC is unused. */ + mp = GC_gcj_fake_mark_proc; + + GC_init(); /* In case it's not already done. */ + LOCK(); + if (GC_gcjobjfreelist != NULL) { + /* Already initialized. */ + UNLOCK(); + return; + } +# ifdef GC_IGNORE_GCJ_INFO + /* This is useful for debugging on platforms with missing getenv(). */ +# define ignore_gcj_info TRUE +# else + ignore_gcj_info = (0 != GETENV("GC_IGNORE_GCJ_INFO")); +# endif + if (ignore_gcj_info) { + GC_COND_LOG_PRINTF("Gcj-style type information is disabled!\n"); + } + GC_ASSERT(GC_mark_procs[mp_index] == (GC_mark_proc)0); /* unused */ + GC_mark_procs[mp_index] = mp; + if (mp_index >= GC_n_mark_procs) + ABORT("GC_init_gcj_malloc_mp: bad index"); + /* Set up object kind gcj-style indirect descriptor. */ + GC_gcjobjfreelist = (ptr_t *)GC_new_free_list_inner(); + if (ignore_gcj_info) { + /* Use a simple length-based descriptor, thus forcing a fully */ + /* conservative scan. */ + GC_gcj_kind = (int)GC_new_kind_inner((void **)GC_gcjobjfreelist, + /* 0 | */ GC_DS_LENGTH, + TRUE, TRUE); + GC_gcj_debug_kind = GC_gcj_kind; + } else { + GC_gcj_kind = (int)GC_new_kind_inner( + (void **)GC_gcjobjfreelist, + (((word)(-(signed_word)MARK_DESCR_OFFSET + - GC_INDIR_PER_OBJ_BIAS)) + | GC_DS_PER_OBJECT), + FALSE, TRUE); + /* Set up object kind for objects that require mark proc call. */ + GC_gcj_debug_kind = (int)GC_new_kind_inner(GC_new_free_list_inner(), + GC_MAKE_PROC(mp_index, + 1 /* allocated with debug info */), + FALSE, TRUE); + } + UNLOCK(); +# undef ignore_gcj_info +} + +/* A mechanism to release the allocator lock and invoke finalizers. */ +/* We don't really have an opportunity to do this on a rarely executed */ +/* path on which the allocator lock is not held. Thus we check at */ +/* a rarely executed point at which it is safe to release the allocator */ +/* lock; we do this even where we could just call GC_INVOKE_FINALIZERS, */ +/* since it is probably cheaper and certainly more uniform. */ +/* TODO: Consider doing the same elsewhere? */ +static void maybe_finalize(void) +{ + static word last_finalized_no = 0; + + GC_ASSERT(I_HOLD_LOCK()); + if (GC_gc_no == last_finalized_no || + !EXPECT(GC_is_initialized, TRUE)) return; + UNLOCK(); + GC_INVOKE_FINALIZERS(); + LOCK(); + last_finalized_no = GC_gc_no; +} + +/* Allocate an object, clear it, and store the pointer to the */ +/* type structure (vtable in gcj). This adds a byte at the */ +/* end of the object if GC_malloc would. */ +#ifdef THREAD_LOCAL_ALLOC + GC_INNER +#else + STATIC +#endif +void * GC_core_gcj_malloc(size_t lb, void * ptr_to_struct_containing_descr, + unsigned flags) +{ + ptr_t op; + size_t lg; + + GC_DBG_COLLECT_AT_MALLOC(lb); + LOCK(); + if (SMALL_OBJ(lb) && (op = GC_gcjobjfreelist[lg = GC_size_map[lb]], + EXPECT(op != NULL, TRUE))) { + GC_gcjobjfreelist[lg] = (ptr_t)obj_link(op); + GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + GC_ASSERT(NULL == ((void **)op)[1]); + } else { + maybe_finalize(); + op = (ptr_t)GC_clear_stack(GC_generic_malloc_inner(lb, GC_gcj_kind, + flags)); + if (NULL == op) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK(); + return (*oom_fn)(lb); + } + } + *(void **)op = ptr_to_struct_containing_descr; + UNLOCK(); + GC_dirty(op); + REACHABLE_AFTER_DIRTY(ptr_to_struct_containing_descr); + return (void *)op; +} + +#ifndef THREAD_LOCAL_ALLOC + GC_API GC_ATTR_MALLOC void * GC_CALL GC_gcj_malloc(size_t lb, + void * ptr_to_struct_containing_descr) + { + return GC_core_gcj_malloc(lb, ptr_to_struct_containing_descr, 0); + } +#endif /* !THREAD_LOCAL_ALLOC */ + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_gcj_malloc_ignore_off_page(size_t lb, + void * ptr_to_struct_containing_descr) +{ + return GC_core_gcj_malloc(lb, ptr_to_struct_containing_descr, + IGNORE_OFF_PAGE); +} + +/* Similar to GC_gcj_malloc, but add debug info. This is allocated */ +/* with GC_gcj_debug_kind. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_gcj_malloc(size_t lb, + void * ptr_to_struct_containing_descr, GC_EXTRA_PARAMS) +{ + void * result; + + /* We're careful to avoid extra calls, which could */ + /* confuse the backtrace. */ + LOCK(); + maybe_finalize(); + result = GC_generic_malloc_inner(SIZET_SAT_ADD(lb, DEBUG_BYTES), + GC_gcj_debug_kind, 0 /* flags */); + if (NULL == result) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK(); + GC_err_printf("GC_debug_gcj_malloc(%lu, %p) returning NULL (%s:%d)\n", + (unsigned long)lb, ptr_to_struct_containing_descr, s, i); + return (*oom_fn)(lb); + } + *((void **)((ptr_t)result + sizeof(oh))) = ptr_to_struct_containing_descr; + if (!GC_debugging_started) { + GC_start_debugging_inner(); + } + ADD_CALL_CHAIN(result, ra); + result = GC_store_debug_info_inner(result, (word)lb, s, i); + UNLOCK(); + GC_dirty(result); + REACHABLE_AFTER_DIRTY(ptr_to_struct_containing_descr); + return result; +} + +#endif /* GC_GCJ_SUPPORT */ + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +#if defined(KEEP_BACK_PTRS) && defined(GC_ASSERTIONS) + +#endif +/* + * This implements: + * 1. allocation of heap block headers + * 2. A map from addresses to heap block addresses to heap block headers + * + * Access speed is crucial. We implement an index structure based on a 2 + * level tree. + */ + +/* Non-macro version of header location routine */ +GC_INNER hdr * GC_find_header(ptr_t h) +{ +# ifdef HASH_TL + hdr * result; + GET_HDR(h, result); + return result; +# else + return HDR_INNER(h); +# endif +} + +/* Handle a header cache miss. Returns a pointer to the */ +/* header corresponding to p, if p can possibly be a valid */ +/* object pointer, and 0 otherwise. */ +/* GUARANTEED to return 0 for a pointer past the first page */ +/* of an object unless both GC_all_interior_pointers is set */ +/* and p is in fact a valid object pointer. */ +/* Never returns a pointer to a free hblk. */ +GC_INNER hdr * +#ifdef PRINT_BLACK_LIST + GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce, ptr_t source) +#else + GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce) +#endif +{ + hdr *hhdr; + + HC_MISS(); + GET_HDR(p, hhdr); + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + if (GC_all_interior_pointers) { + if (hhdr != NULL) { + ptr_t current = (ptr_t)HBLKPTR(p); + + do { + current = (ptr_t)FORWARDED_ADDR(current, hhdr); + hhdr = HDR(current); + } while (IS_FORWARDING_ADDR_OR_NIL(hhdr)); + /* current points to near the start of the large object */ + if (hhdr -> hb_flags & IGNORE_OFF_PAGE) + return 0; + if (HBLK_IS_FREE(hhdr) + || p - current >= (signed_word)(hhdr -> hb_sz)) { + GC_ADD_TO_BLACK_LIST_NORMAL(p, source); + /* Pointer past the end of the block */ + return 0; + } + } else { + GC_ADD_TO_BLACK_LIST_NORMAL(p, source); + /* And return zero: */ + } + GC_ASSERT(NULL == hhdr || !HBLK_IS_FREE(hhdr)); + return hhdr; + /* Pointers past the first page are probably too rare */ + /* to add them to the cache. We don't. */ + /* And correctness relies on the fact that we don't. */ + } else { + if (NULL == hhdr) { + GC_ADD_TO_BLACK_LIST_NORMAL(p, source); + } + return 0; + } + } else { + if (HBLK_IS_FREE(hhdr)) { + GC_ADD_TO_BLACK_LIST_NORMAL(p, source); + return 0; + } else { + hce -> block_addr = (word)(p) >> LOG_HBLKSIZE; + hce -> hce_hdr = hhdr; + return hhdr; + } + } +} + +/* Routines to dynamically allocate collector data structures that will */ +/* never be freed. */ + +GC_INNER ptr_t GC_scratch_alloc(size_t bytes) +{ + ptr_t result = GC_scratch_free_ptr; + size_t bytes_to_get; + + GC_ASSERT(I_HOLD_LOCK()); + bytes = ROUNDUP_GRANULE_SIZE(bytes); + for (;;) { + GC_ASSERT((word)GC_scratch_end_ptr >= (word)result); + if (bytes <= (word)GC_scratch_end_ptr - (word)result) { + /* Unallocated space of scratch buffer has enough size. */ + GC_scratch_free_ptr = result + bytes; + return result; + } + + GC_ASSERT(GC_page_size != 0); + if (bytes >= MINHINCR * HBLKSIZE) { + bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP(bytes); + result = GC_os_get_mem(bytes_to_get); + if (result != NULL) { +# if defined(KEEP_BACK_PTRS) && (GC_GRANULE_BYTES < 0x10) + GC_ASSERT((word)result > (word)NOT_MARKED); +# endif + /* No update of scratch free area pointer; */ + /* get memory directly. */ +# ifdef USE_SCRATCH_LAST_END_PTR + /* Update end point of last obtained area (needed only */ + /* by GC_register_dynamic_libraries for some targets). */ + GC_scratch_last_end_ptr = result + bytes; +# endif + } + return result; + } + + bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP(MINHINCR * HBLKSIZE); + /* round up for safety */ + result = GC_os_get_mem(bytes_to_get); + if (EXPECT(NULL == result, FALSE)) { + WARN("Out of memory - trying to allocate requested amount" + " (%" WARN_PRIuPTR " bytes)...\n", bytes); + bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP(bytes); + result = GC_os_get_mem(bytes_to_get); + if (result != NULL) { +# ifdef USE_SCRATCH_LAST_END_PTR + GC_scratch_last_end_ptr = result + bytes; +# endif + } + return result; + } + + /* TODO: some amount of unallocated space may remain unused forever */ + /* Update scratch area pointers and retry. */ + GC_scratch_free_ptr = result; + GC_scratch_end_ptr = GC_scratch_free_ptr + bytes_to_get; +# ifdef USE_SCRATCH_LAST_END_PTR + GC_scratch_last_end_ptr = GC_scratch_end_ptr; +# endif + } +} + +/* Return an uninitialized header */ +static hdr * alloc_hdr(void) +{ + hdr * result; + + GC_ASSERT(I_HOLD_LOCK()); + if (NULL == GC_hdr_free_list) { + result = (hdr *)GC_scratch_alloc(sizeof(hdr)); + } else { + result = GC_hdr_free_list; + GC_hdr_free_list = (hdr *) result -> hb_next; + } + return result; +} + +GC_INLINE void free_hdr(hdr * hhdr) +{ + hhdr -> hb_next = (struct hblk *)GC_hdr_free_list; + GC_hdr_free_list = hhdr; +} + +#ifdef COUNT_HDR_CACHE_HITS + /* Used for debugging/profiling (the symbols are externally visible). */ + word GC_hdr_cache_hits = 0; + word GC_hdr_cache_misses = 0; +#endif + +GC_INNER void GC_init_headers(void) +{ + unsigned i; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(NULL == GC_all_nils); + GC_all_nils = (bottom_index *)GC_scratch_alloc(sizeof(bottom_index)); + if (GC_all_nils == NULL) { + GC_err_printf("Insufficient memory for GC_all_nils\n"); + EXIT(); + } + BZERO(GC_all_nils, sizeof(bottom_index)); + for (i = 0; i < TOP_SZ; i++) { + GC_top_index[i] = GC_all_nils; + } +} + +/* Make sure that there is a bottom level index block for address addr. */ +/* Return FALSE on failure. */ +static GC_bool get_index(word addr) +{ + word hi = (word)(addr) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); + bottom_index * r; + bottom_index * p; + bottom_index ** prev; + bottom_index *pi; /* old_p */ + word i; + + GC_ASSERT(I_HOLD_LOCK()); +# ifdef HASH_TL + i = TL_HASH(hi); + + pi = GC_top_index[i]; + for (p = pi; p != GC_all_nils; p = p -> hash_link) { + if (p -> key == hi) return TRUE; + } +# else + if (GC_top_index[hi] != GC_all_nils) + return TRUE; + i = hi; +# endif + r = (bottom_index *)GC_scratch_alloc(sizeof(bottom_index)); + if (EXPECT(NULL == r, FALSE)) + return FALSE; + BZERO(r, sizeof(bottom_index)); + r -> key = hi; +# ifdef HASH_TL + r -> hash_link = pi; +# endif + + /* Add it to the list of bottom indices */ + prev = &GC_all_bottom_indices; /* pointer to p */ + pi = 0; /* bottom_index preceding p */ + while ((p = *prev) != 0 && p -> key < hi) { + pi = p; + prev = &(p -> asc_link); + } + r -> desc_link = pi; + if (0 == p) { + GC_all_bottom_indices_end = r; + } else { + p -> desc_link = r; + } + r -> asc_link = p; + *prev = r; + + GC_top_index[i] = r; + return TRUE; +} + +/* Install a header for block h. */ +/* The header is uninitialized. */ +/* Returns the header or 0 on failure. */ +GC_INNER struct hblkhdr * GC_install_header(struct hblk *h) +{ + hdr * result; + + GC_ASSERT(I_HOLD_LOCK()); + if (EXPECT(!get_index((word)h), FALSE)) return NULL; + + result = alloc_hdr(); + if (EXPECT(result != NULL, TRUE)) { + GC_ASSERT(!IS_FORWARDING_ADDR_OR_NIL(result)); + SET_HDR(h, result); +# ifdef USE_MUNMAP + result -> hb_last_reclaimed = (unsigned short)GC_gc_no; +# endif + } + return result; +} + +/* Set up forwarding counts for block h of size sz */ +GC_INNER GC_bool GC_install_counts(struct hblk *h, size_t sz/* bytes */) +{ + struct hblk * hbp; + + for (hbp = h; (word)hbp < (word)h + sz; hbp += BOTTOM_SZ) { + if (!get_index((word)hbp)) + return FALSE; + if ((word)hbp > GC_WORD_MAX - (word)BOTTOM_SZ * HBLKSIZE) + break; /* overflow of hbp+=BOTTOM_SZ is expected */ + } + if (!get_index((word)h + sz - 1)) + return FALSE; + for (hbp = h + 1; (word)hbp < (word)h + sz; hbp++) { + word i = (word)HBLK_PTR_DIFF(hbp, h); + + SET_HDR(hbp, (hdr *)(i > MAX_JUMP? MAX_JUMP : i)); + } + return TRUE; +} + +/* Remove the header for block h */ +GC_INNER void GC_remove_header(struct hblk *h) +{ + hdr **ha; + GET_HDR_ADDR(h, ha); + free_hdr(*ha); + *ha = 0; +} + +/* Remove forwarding counts for h */ +GC_INNER void GC_remove_counts(struct hblk *h, size_t sz/* bytes */) +{ + struct hblk * hbp; + + if (sz <= HBLKSIZE) return; + if (NULL == HDR(h + 1)) { +# ifdef GC_ASSERTIONS + for (hbp = h + 2; (word)hbp < (word)h + sz; hbp++) + GC_ASSERT(NULL == HDR(hbp)); +# endif + return; + } + + for (hbp = h + 1; (word)hbp < (word)h + sz; hbp++) { + SET_HDR(hbp, NULL); + } +} + +GC_API void GC_CALL GC_apply_to_all_blocks(GC_walk_hblk_fn fn, + GC_word client_data) +{ + signed_word j; + bottom_index * index_p; + + for (index_p = GC_all_bottom_indices; index_p != NULL; + index_p = index_p -> asc_link) { + for (j = BOTTOM_SZ-1; j >= 0;) { + if (!IS_FORWARDING_ADDR_OR_NIL(index_p->index[j])) { + if (!HBLK_IS_FREE(index_p->index[j])) { + (*fn)(((struct hblk *) + (((index_p->key << LOG_BOTTOM_SZ) + (word)j) + << LOG_HBLKSIZE)), + client_data); + } + j--; + } else if (index_p->index[j] == 0) { + j--; + } else { + j -= (signed_word)(index_p->index[j]); + } + } + } +} + +GC_INNER struct hblk * GC_next_block(struct hblk *h, GC_bool allow_free) +{ + REGISTER bottom_index * bi; + REGISTER word j = ((word)h >> LOG_HBLKSIZE) & (BOTTOM_SZ-1); + + GC_ASSERT(I_HOLD_READER_LOCK()); + GET_BI(h, bi); + if (bi == GC_all_nils) { + REGISTER word hi = (word)h >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); + + bi = GC_all_bottom_indices; + while (bi != 0 && bi -> key < hi) bi = bi -> asc_link; + j = 0; + } + + while (bi != 0) { + while (j < BOTTOM_SZ) { + hdr * hhdr = bi -> index[j]; + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + j++; + } else { + if (allow_free || !HBLK_IS_FREE(hhdr)) { + return (struct hblk *)(((bi -> key << LOG_BOTTOM_SZ) + + j) << LOG_HBLKSIZE); + } else { + j += divHBLKSZ(hhdr -> hb_sz); + } + } + } + j = 0; + bi = bi -> asc_link; + } + return NULL; +} + +GC_INNER struct hblk * GC_prev_block(struct hblk *h) +{ + bottom_index * bi; + signed_word j = ((word)h >> LOG_HBLKSIZE) & (BOTTOM_SZ-1); + + GC_ASSERT(I_HOLD_READER_LOCK()); + GET_BI(h, bi); + if (bi == GC_all_nils) { + word hi = (word)h >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); + + bi = GC_all_bottom_indices_end; + while (bi != NULL && bi -> key > hi) + bi = bi -> desc_link; + j = BOTTOM_SZ - 1; + } + for (; bi != NULL; bi = bi -> desc_link) { + while (j >= 0) { + hdr * hhdr = bi -> index[j]; + + if (NULL == hhdr) { + --j; + } else if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + j -= (signed_word)hhdr; + } else { + return (struct hblk*)(((bi -> key << LOG_BOTTOM_SZ) + (word)j) + << LOG_HBLKSIZE); + } + } + j = BOTTOM_SZ - 1; + } + return NULL; +} + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 2000 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +/* + * This file contains the functions: + * ptr_t GC_build_flXXX(h, old_fl) + * void GC_new_hblk(size, kind) + */ + +#ifndef SMALL_CONFIG + /* Build a free list for size 2 (words) cleared objects inside */ + /* hblk h. Set the last link to be ofl. Return a pointer to the */ + /* first free list entry. */ + STATIC ptr_t GC_build_fl_clear2(struct hblk *h, ptr_t ofl) + { + word * p = (word *)(h -> hb_body); + word * lim = (word *)(h + 1); + + p[0] = (word)ofl; + p[1] = 0; + p[2] = (word)p; + p[3] = 0; + p += 4; + for (; (word)p < (word)lim; p += 4) { + p[0] = (word)(p-2); + p[1] = 0; + p[2] = (word)p; + p[3] = 0; + } + return (ptr_t)(p-2); + } + + /* The same for size 4 cleared objects. */ + STATIC ptr_t GC_build_fl_clear4(struct hblk *h, ptr_t ofl) + { + word * p = (word *)(h -> hb_body); + word * lim = (word *)(h + 1); + + p[0] = (word)ofl; + p[1] = 0; + p[2] = 0; + p[3] = 0; + p += 4; + for (; (word)p < (word)lim; p += 4) { + GC_PREFETCH_FOR_WRITE((ptr_t)(p + 64)); + p[0] = (word)(p-4); + p[1] = 0; + CLEAR_DOUBLE(p+2); + } + return (ptr_t)(p-4); + } + + /* The same for size 2 uncleared objects. */ + STATIC ptr_t GC_build_fl2(struct hblk *h, ptr_t ofl) + { + word * p = (word *)(h -> hb_body); + word * lim = (word *)(h + 1); + + p[0] = (word)ofl; + p[2] = (word)p; + p += 4; + for (; (word)p < (word)lim; p += 4) { + p[0] = (word)(p-2); + p[2] = (word)p; + } + return (ptr_t)(p-2); + } + + /* The same for size 4 uncleared objects. */ + STATIC ptr_t GC_build_fl4(struct hblk *h, ptr_t ofl) + { + word * p = (word *)(h -> hb_body); + word * lim = (word *)(h + 1); + + p[0] = (word)ofl; + p[4] = (word)p; + p += 8; + for (; (word)p < (word)lim; p += 8) { + GC_PREFETCH_FOR_WRITE((ptr_t)(p + 64)); + p[0] = (word)(p-4); + p[4] = (word)p; + } + return (ptr_t)(p-4); + } +#endif /* !SMALL_CONFIG */ + +/* Build a free list for objects of size sz inside heap block h. */ +/* Clear objects inside h if clear is set. Add list to the end of */ +/* the free list we build. Return the new free list. */ +/* This could be called without the allocator lock, if we ensure that */ +/* there is no concurrent collection which might reclaim objects that */ +/* we have not yet allocated. */ +GC_INNER ptr_t GC_build_fl(struct hblk *h, size_t sz, GC_bool clear, + ptr_t list) +{ + word *p, *prev; + word *last_object; /* points to last object in new hblk */ + + /* Do a few prefetches here, just because it's cheap. */ + /* If we were more serious about it, these should go inside */ + /* the loops. But write prefetches usually don't seem to */ + /* matter much. */ + GC_PREFETCH_FOR_WRITE((ptr_t)h); + GC_PREFETCH_FOR_WRITE((ptr_t)h + 128); + GC_PREFETCH_FOR_WRITE((ptr_t)h + 256); + GC_PREFETCH_FOR_WRITE((ptr_t)h + 378); +# ifndef SMALL_CONFIG + /* Handle small objects sizes more efficiently. For larger objects */ + /* the difference is less significant. */ + switch (sz) { + case 2: if (clear) { + return GC_build_fl_clear2(h, list); + } else { + return GC_build_fl2(h, list); + } + case 4: if (clear) { + return GC_build_fl_clear4(h, list); + } else { + return GC_build_fl4(h, list); + } + default: + break; + } +# endif /* !SMALL_CONFIG */ + + /* Clear the page if necessary. */ + if (clear) BZERO(h, HBLKSIZE); + + /* Add objects to free list */ + p = (word *)(h -> hb_body) + sz; /* second object in *h */ + prev = (word *)(h -> hb_body); /* One object behind p */ + last_object = (word *)((char *)h + HBLKSIZE); + last_object -= sz; + /* Last place for last object to start */ + + /* make a list of all objects in *h with head as last object */ + while ((word)p <= (word)last_object) { + /* current object's link points to last object */ + obj_link(p) = (ptr_t)prev; + prev = p; + p += sz; + } + p -= sz; /* p now points to last object */ + + /* Put p (which is now head of list of objects in *h) as first */ + /* pointer in the appropriate free list for this size. */ + *(ptr_t *)h = list; + return (ptr_t)p; +} + +/* Allocate a new heapblock for small objects of the given size in */ +/* granules and kind. Add all of the heapblock's objects to the */ +/* free list for objects of that size. Set all mark bits */ +/* if objects are uncollectible. Will fail to do anything if we */ +/* are out of memory. */ +GC_INNER void GC_new_hblk(size_t gran, int k) +{ + struct hblk *h; /* the new heap block */ + + GC_STATIC_ASSERT(sizeof(struct hblk) == HBLKSIZE); + GC_ASSERT(I_HOLD_LOCK()); + /* Allocate a new heap block. */ + h = GC_allochblk(GRANULES_TO_BYTES(gran), k, 0 /* flags */, 0); + if (EXPECT(NULL == h, FALSE)) return; /* out of memory */ + + /* Mark all objects if appropriate. */ + if (IS_UNCOLLECTABLE(k)) GC_set_hdr_marks(HDR(h)); + + /* Build the free list */ + GC_obj_kinds[k].ok_freelist[gran] = + GC_build_fl(h, GRANULES_TO_WORDS(gran), + GC_debugging_started || GC_obj_kinds[k].ok_init, + (ptr_t)GC_obj_kinds[k].ok_freelist[gran]); +} + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991, 1992 by Xerox Corporation. All rights reserved. + * Copyright (c) 1999-2001 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +/* Routines for maintaining maps describing heap block + * layouts for various object sizes. Allows fast pointer validity checks + * and fast location of object start locations on machines (such as SPARC) + * with slow division. + */ + +/* Consider pointers that are offset bytes displaced from the beginning */ +/* of an object to be valid. */ + +GC_API void GC_CALL GC_register_displacement(size_t offset) +{ + LOCK(); + GC_register_displacement_inner(offset); + UNLOCK(); +} + +GC_INNER void GC_register_displacement_inner(size_t offset) +{ + GC_ASSERT(I_HOLD_LOCK()); + if (offset >= VALID_OFFSET_SZ) { + ABORT("Bad argument to GC_register_displacement"); + } + if (!GC_valid_offsets[offset]) { + GC_valid_offsets[offset] = TRUE; + GC_modws_valid_offsets[offset % sizeof(word)] = TRUE; + } +} + +#ifndef MARK_BIT_PER_OBJ + /* Add a heap block map for objects of size granules to obj_map. */ + /* A size of 0 is used for large objects. Return FALSE on failure. */ + GC_INNER GC_bool GC_add_map_entry(size_t granules) + { + unsigned displ; + unsigned short * new_map; + + GC_ASSERT(I_HOLD_LOCK()); + if (granules > BYTES_TO_GRANULES(MAXOBJBYTES)) granules = 0; + if (GC_obj_map[granules] != 0) return TRUE; + + new_map = (unsigned short *)GC_scratch_alloc(OBJ_MAP_LEN * sizeof(short)); + if (EXPECT(NULL == new_map, FALSE)) return FALSE; + + GC_COND_LOG_PRINTF( + "Adding block map for size of %u granules (%u bytes)\n", + (unsigned)granules, (unsigned)GRANULES_TO_BYTES(granules)); + if (granules == 0) { + for (displ = 0; displ < OBJ_MAP_LEN; displ++) { + new_map[displ] = 1; /* Nonzero to get us out of marker fast path. */ + } + } else { + for (displ = 0; displ < OBJ_MAP_LEN; displ++) { + new_map[displ] = (unsigned short)(displ % granules); + } + } + GC_obj_map[granules] = new_map; + return TRUE; + } +#endif /* !MARK_BIT_PER_OBJ */ + +GC_INNER void GC_initialize_offsets(void) +{ + unsigned i; + if (GC_all_interior_pointers) { + for (i = 0; i < VALID_OFFSET_SZ; ++i) + GC_valid_offsets[i] = TRUE; + } else { + BZERO(GC_valid_offsets, sizeof(GC_valid_offsets)); + for (i = 0; i < sizeof(word); ++i) + GC_modws_valid_offsets[i] = FALSE; + } +} + +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +/* + * These are checking routines calls to which could be inserted by a + * preprocessor to validate C pointer arithmetic. + */ + +STATIC void GC_CALLBACK GC_default_same_obj_print_proc(void * p, void * q) +{ + ABORT_ARG2("GC_same_obj test failed", + ": %p and %p are not in the same object", p, q); +} + +GC_same_obj_print_proc_t GC_same_obj_print_proc = + GC_default_same_obj_print_proc; + +GC_API void * GC_CALL GC_same_obj(void *p, void *q) +{ + struct hblk *h; + hdr *hhdr; + ptr_t base, limit; + word sz; + + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + hhdr = HDR((word)p); + if (NULL == hhdr) { + if (divHBLKSZ((word)p) != divHBLKSZ((word)q) + && HDR((word)q) != NULL) { + goto fail; + } + return p; + } + /* If it's a pointer to the middle of a large object, move it */ + /* to the beginning. */ + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + h = HBLKPTR(p) - (word)hhdr; + for (hhdr = HDR(h); IS_FORWARDING_ADDR_OR_NIL(hhdr); hhdr = HDR(h)) { + h = FORWARDED_ADDR(h, hhdr); + } + limit = (ptr_t)h + hhdr -> hb_sz; + if ((word)p >= (word)limit || (word)q >= (word)limit + || (word)q < (word)h) { + goto fail; + } + return p; + } + sz = hhdr -> hb_sz; + if (sz > MAXOBJBYTES) { + base = (ptr_t)HBLKPTR(p); + limit = base + sz; + if ((word)p >= (word)limit) { + goto fail; + } + } else { + size_t offset; + size_t pdispl = HBLKDISPL(p); + + offset = pdispl % sz; + if (HBLKPTR(p) != HBLKPTR(q)) goto fail; + /* W/o this check, we might miss an error if */ + /* q points to the first object on a page, and */ + /* points just before the page. */ + base = (ptr_t)p - offset; + limit = base + sz; + } + /* [base, limit) delimits the object containing p, if any. */ + /* If p is not inside a valid object, then either q is */ + /* also outside any valid object, or it is outside */ + /* [base, limit). */ + if ((word)q >= (word)limit || (word)q < (word)base) { + goto fail; + } + return p; +fail: + (*GC_same_obj_print_proc)((ptr_t)p, (ptr_t)q); + return p; +} + +STATIC void GC_CALLBACK GC_default_is_valid_displacement_print_proc(void *p) +{ + ABORT_ARG1("GC_is_valid_displacement test failed", ": %p not valid", p); +} + +GC_valid_ptr_print_proc_t GC_is_valid_displacement_print_proc = + GC_default_is_valid_displacement_print_proc; + +GC_API void * GC_CALL GC_is_valid_displacement(void *p) +{ + hdr *hhdr; + word pdispl; + word offset; + struct hblk *h; + word sz; + + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + if (NULL == p) return NULL; + hhdr = HDR((word)p); + if (NULL == hhdr) return p; + h = HBLKPTR(p); + if (GC_all_interior_pointers) { + for (; IS_FORWARDING_ADDR_OR_NIL(hhdr); hhdr = HDR(h)) { + h = FORWARDED_ADDR(h, hhdr); + } + } else if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + goto fail; + } + sz = hhdr -> hb_sz; + pdispl = HBLKDISPL(p); + offset = pdispl % sz; + if ((sz > MAXOBJBYTES && (word)p >= (word)h + sz) + || !GC_valid_offsets[offset] + || ((word)p + (sz - offset) > (word)(h + 1) + && !IS_FORWARDING_ADDR_OR_NIL(HDR(h + 1)))) { + goto fail; + } + return p; +fail: + (*GC_is_valid_displacement_print_proc)((ptr_t)p); + return p; +} + +STATIC void GC_CALLBACK GC_default_is_visible_print_proc(void * p) +{ + ABORT_ARG1("GC_is_visible test failed", ": %p not GC-visible", p); +} + +GC_valid_ptr_print_proc_t GC_is_visible_print_proc = + GC_default_is_visible_print_proc; + +#ifndef THREADS +/* Could p be a stack address? */ + STATIC GC_bool GC_on_stack(void *p) + { + return (word)p HOTTER_THAN (word)GC_stackbottom + && !((word)p HOTTER_THAN (word)GC_approx_sp()); + } +#endif /* !THREADS */ + +GC_API void * GC_CALL GC_is_visible(void *p) +{ + hdr *hhdr; + + if ((word)p & (ALIGNMENT - 1)) goto fail; + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); +# ifdef THREADS + hhdr = HDR((word)p); + if (hhdr != NULL && NULL == GC_base(p)) { + goto fail; + } else { + /* May be inside thread stack. We can't do much. */ + return p; + } +# else + /* Check stack first: */ + if (GC_on_stack(p)) return p; + hhdr = HDR((word)p); + if (NULL == hhdr) { + if (GC_is_static_root(p)) return p; + /* Else do it again correctly: */ +# if defined(DYNAMIC_LOADING) || defined(ANY_MSWIN) || defined(PCR) + if (!GC_no_dls) { + GC_register_dynamic_libraries(); + if (GC_is_static_root(p)) return p; + } +# endif + goto fail; + } else { + /* p points to the heap. */ + word descr; + ptr_t base = (ptr_t)GC_base(p); + /* TODO: should GC_base be manually inlined? */ + + if (NULL == base) goto fail; + if (HBLKPTR(base) != HBLKPTR(p)) + hhdr = HDR(base); + descr = hhdr -> hb_descr; + retry: + switch(descr & GC_DS_TAGS) { + case GC_DS_LENGTH: + if ((word)p - (word)base > descr) goto fail; + break; + case GC_DS_BITMAP: + if ((ptr_t)p - base >= WORDS_TO_BYTES(BITMAP_BITS) + || ((word)p & (sizeof(word)-1)) != 0) goto fail; + if (!(((word)1 << (CPP_WORDSZ-1 - ((word)p - (word)base))) + & descr)) goto fail; + break; + case GC_DS_PROC: + /* We could try to decipher this partially. */ + /* For now we just punt. */ + break; + case GC_DS_PER_OBJECT: + if (!(descr & SIGNB)) { + descr = *(word *)((ptr_t)base + + (descr & ~(word)GC_DS_TAGS)); + } else { + ptr_t type_descr = *(ptr_t *)base; + descr = *(word *)(type_descr + - (descr - (word)(GC_DS_PER_OBJECT + - GC_INDIR_PER_OBJ_BIAS))); + } + goto retry; + } + return p; + } +# endif +fail: + (*GC_is_visible_print_proc)((ptr_t)p); + return p; +} + +GC_API void * GC_CALL GC_pre_incr(void **p, ptrdiff_t how_much) +{ + void * initial = *p; + void * result = GC_same_obj((void *)((ptr_t)initial + how_much), initial); + + if (!GC_all_interior_pointers) { + (void)GC_is_valid_displacement(result); + } + *p = result; + return result; /* updated pointer */ +} + +GC_API void * GC_CALL GC_post_incr(void **p, ptrdiff_t how_much) +{ + void * initial = *p; + void * result = GC_same_obj((void *)((ptr_t)initial + how_much), initial); + + if (!GC_all_interior_pointers) { + (void)GC_is_valid_displacement(result); + } + *p = result; + return initial; /* original *p */ +} + +GC_API void GC_CALL GC_set_same_obj_print_proc(GC_same_obj_print_proc_t fn) +{ + GC_ASSERT(NONNULL_ARG_NOT_NULL(fn)); + GC_same_obj_print_proc = fn; +} + +GC_API GC_same_obj_print_proc_t GC_CALL GC_get_same_obj_print_proc(void) +{ + return GC_same_obj_print_proc; +} + +GC_API void GC_CALL GC_set_is_valid_displacement_print_proc( + GC_valid_ptr_print_proc_t fn) +{ + GC_ASSERT(NONNULL_ARG_NOT_NULL(fn)); + GC_is_valid_displacement_print_proc = fn; +} + +GC_API GC_valid_ptr_print_proc_t GC_CALL +GC_get_is_valid_displacement_print_proc(void) +{ + return GC_is_valid_displacement_print_proc; +} + +GC_API void GC_CALL GC_set_is_visible_print_proc(GC_valid_ptr_print_proc_t fn) +{ + GC_ASSERT(NONNULL_ARG_NOT_NULL(fn)); + GC_is_visible_print_proc = fn; +} + +GC_API GC_valid_ptr_print_proc_t GC_CALL GC_get_is_visible_print_proc(void) +{ + return GC_is_visible_print_proc; +} + + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1998-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +#ifdef GC_USE_ENTIRE_HEAP + int GC_use_entire_heap = TRUE; +#else + int GC_use_entire_heap = FALSE; +#endif + +/* + * Free heap blocks are kept on one of several free lists, + * depending on the size of the block. Each free list is doubly linked. + * Adjacent free blocks are coalesced. + */ + + +# define MAX_BLACK_LIST_ALLOC (2*HBLKSIZE) + /* largest block we will allocate starting on a black */ + /* listed block. Must be >= HBLKSIZE. */ + + +# define UNIQUE_THRESHOLD 32 + /* Sizes up to this many HBLKs each have their own free list */ +# define HUGE_THRESHOLD 256 + /* Sizes of at least this many heap blocks are mapped to a */ + /* single free list. */ +# define FL_COMPRESSION 8 + /* In between sizes map this many distinct sizes to a single */ + /* bin. */ + +# define N_HBLK_FLS ((HUGE_THRESHOLD - UNIQUE_THRESHOLD) / FL_COMPRESSION \ + + UNIQUE_THRESHOLD) + +#ifndef GC_GCJ_SUPPORT + STATIC +#endif + struct hblk * GC_hblkfreelist[N_HBLK_FLS+1] = { 0 }; + /* List of completely empty heap blocks */ + /* Linked through hb_next field of */ + /* header structure associated with */ + /* block. Remains externally visible */ + /* as used by GNU GCJ currently. */ + +GC_API void GC_CALL GC_iterate_free_hblks(GC_walk_free_blk_fn fn, + GC_word client_data) +{ + int i; + + for (i = 0; i <= N_HBLK_FLS; ++i) { + struct hblk *h; + + for (h = GC_hblkfreelist[i]; h != NULL; h = HDR(h) -> hb_next) { + (*fn)(h, i, client_data); + } + } +} + +#ifndef GC_GCJ_SUPPORT + STATIC +#endif + word GC_free_bytes[N_HBLK_FLS+1] = { 0 }; + /* Number of free bytes on each list. Remains visible to GCJ. */ + +/* Return the largest n such that the number of free bytes on lists */ +/* n .. N_HBLK_FLS is greater or equal to GC_max_large_allocd_bytes */ +/* minus GC_large_allocd_bytes. If there is no such n, return 0. */ +GC_INLINE int GC_enough_large_bytes_left(void) +{ + int n; + word bytes = GC_large_allocd_bytes; + + GC_ASSERT(GC_max_large_allocd_bytes <= GC_heapsize); + for (n = N_HBLK_FLS; n >= 0; --n) { + bytes += GC_free_bytes[n]; + if (bytes >= GC_max_large_allocd_bytes) return n; + } + return 0; +} + +/* Map a number of blocks to the appropriate large block free list index. */ +STATIC int GC_hblk_fl_from_blocks(size_t blocks_needed) +{ + if (blocks_needed <= UNIQUE_THRESHOLD) return (int)blocks_needed; + if (blocks_needed >= HUGE_THRESHOLD) return N_HBLK_FLS; + return (int)(blocks_needed - UNIQUE_THRESHOLD)/FL_COMPRESSION + + UNIQUE_THRESHOLD; +} + +# define PHDR(hhdr) HDR((hhdr) -> hb_prev) +# define NHDR(hhdr) HDR((hhdr) -> hb_next) + +# ifdef USE_MUNMAP +# define IS_MAPPED(hhdr) (((hhdr) -> hb_flags & WAS_UNMAPPED) == 0) +# else +# define IS_MAPPED(hhdr) TRUE +# endif /* !USE_MUNMAP */ + +#if !defined(NO_DEBUGGING) || defined(GC_ASSERTIONS) + static void GC_CALLBACK add_hb_sz(struct hblk *h, int i, GC_word client_data) + { + UNUSED_ARG(i); + *(word *)client_data += HDR(h) -> hb_sz; + } + + /* Should return the same value as GC_large_free_bytes. */ + GC_INNER word GC_compute_large_free_bytes(void) + { + word total_free = 0; + + GC_iterate_free_hblks(add_hb_sz, (word)&total_free); + return total_free; + } +#endif /* !NO_DEBUGGING || GC_ASSERTIONS */ + +# if !defined(NO_DEBUGGING) + static void GC_CALLBACK print_hblkfreelist_item(struct hblk *h, int i, + GC_word prev_index_ptr) + { + hdr *hhdr = HDR(h); + + if (i != *(int *)prev_index_ptr) { + GC_printf("Free list %d (total size %lu):\n", + i, (unsigned long)GC_free_bytes[i]); + *(int *)prev_index_ptr = i; + } + + GC_printf("\t%p size %lu %s black listed\n", + (void *)h, (unsigned long)(hhdr -> hb_sz), + GC_is_black_listed(h, HBLKSIZE) != NULL ? "start" + : GC_is_black_listed(h, hhdr -> hb_sz) != NULL ? "partially" + : "not"); + } + + void GC_print_hblkfreelist(void) + { + word total; + int prev_index = -1; + + GC_iterate_free_hblks(print_hblkfreelist_item, (word)&prev_index); + GC_printf("GC_large_free_bytes: %lu\n", + (unsigned long)GC_large_free_bytes); + total = GC_compute_large_free_bytes(); + if (total != GC_large_free_bytes) + GC_err_printf("GC_large_free_bytes INCONSISTENT!! Should be: %lu\n", + (unsigned long)total); + } + +/* Return the free list index on which the block described by the header */ +/* appears, or -1 if it appears nowhere. */ +static int free_list_index_of(const hdr *wanted) +{ + int i; + + for (i = 0; i <= N_HBLK_FLS; ++i) { + struct hblk * h; + hdr * hhdr; + + for (h = GC_hblkfreelist[i]; h != 0; h = hhdr -> hb_next) { + hhdr = HDR(h); + if (hhdr == wanted) return i; + } + } + return -1; +} + +GC_API void GC_CALL GC_dump_regions(void) +{ + unsigned i; + + for (i = 0; i < GC_n_heap_sects; ++i) { + ptr_t start = GC_heap_sects[i].hs_start; + size_t bytes = GC_heap_sects[i].hs_bytes; + ptr_t end = start + bytes; + ptr_t p; + + /* Merge in contiguous sections. */ + while (i+1 < GC_n_heap_sects && GC_heap_sects[i+1].hs_start == end) { + ++i; + end = GC_heap_sects[i].hs_start + GC_heap_sects[i].hs_bytes; + } + GC_printf("***Section from %p to %p\n", (void *)start, (void *)end); + for (p = start; (word)p < (word)end; ) { + hdr *hhdr = HDR(p); + + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + GC_printf("\t%p Missing header!!(%p)\n", + (void *)p, (void *)hhdr); + p += HBLKSIZE; + continue; + } + if (HBLK_IS_FREE(hhdr)) { + int correct_index = GC_hblk_fl_from_blocks( + (size_t)divHBLKSZ(hhdr -> hb_sz)); + int actual_index; + + GC_printf("\t%p\tfree block of size 0x%lx bytes%s\n", + (void *)p, (unsigned long)(hhdr -> hb_sz), + IS_MAPPED(hhdr) ? "" : " (unmapped)"); + actual_index = free_list_index_of(hhdr); + if (-1 == actual_index) { + GC_printf("\t\tBlock not on free list %d!!\n", + correct_index); + } else if (correct_index != actual_index) { + GC_printf("\t\tBlock on list %d, should be on %d!!\n", + actual_index, correct_index); + } + p += hhdr -> hb_sz; + } else { + GC_printf("\t%p\tused for blocks of size 0x%lx bytes\n", + (void *)p, (unsigned long)(hhdr -> hb_sz)); + p += HBLKSIZE * OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); + } + } + } +} + +# endif /* NO_DEBUGGING */ + +/* Initialize hdr for a block containing the indicated size and */ +/* kind of objects. Return FALSE on failure. */ +static GC_bool setup_header(hdr *hhdr, struct hblk *block, size_t byte_sz, + int kind, unsigned flags) +{ + struct obj_kind *ok; + word descr; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(byte_sz >= ALIGNMENT); +# ifndef MARK_BIT_PER_OBJ + if (byte_sz > MAXOBJBYTES) + flags |= LARGE_BLOCK; +# endif + ok = &GC_obj_kinds[kind]; +# ifdef ENABLE_DISCLAIM + if (ok -> ok_disclaim_proc) + flags |= HAS_DISCLAIM; + if (ok -> ok_mark_unconditionally) + flags |= MARK_UNCONDITIONALLY; +# endif + + /* Set size, kind and mark proc fields. */ + hhdr -> hb_sz = byte_sz; + hhdr -> hb_obj_kind = (unsigned char)kind; + hhdr -> hb_flags = (unsigned char)flags; + hhdr -> hb_block = block; + descr = ok -> ok_descriptor; +# if ALIGNMENT > GC_DS_TAGS + /* An extra byte is not added in case of ignore-off-page */ + /* allocated objects not smaller than HBLKSIZE. */ + if (EXTRA_BYTES != 0 && (flags & IGNORE_OFF_PAGE) != 0 + && kind == NORMAL && byte_sz >= HBLKSIZE) + descr += ALIGNMENT; /* or set to 0 */ +# endif + if (ok -> ok_relocate_descr) descr += byte_sz; + hhdr -> hb_descr = descr; + +# ifdef MARK_BIT_PER_OBJ + /* Set hb_inv_sz as portably as possible. */ + /* We set it to the smallest value such that sz*inv_sz >= 2**32. */ + /* This may be more precision than necessary. */ + if (byte_sz > MAXOBJBYTES) { + hhdr -> hb_inv_sz = LARGE_INV_SZ; + } else { + unsigned32 inv_sz; + + GC_ASSERT(byte_sz > 1); +# if CPP_WORDSZ > 32 + inv_sz = (unsigned32)(((word)1 << 32) / byte_sz); + if (((inv_sz * (word)byte_sz) >> 32) == 0) ++inv_sz; +# else + inv_sz = (((unsigned32)1 << 31) / byte_sz) << 1; + while ((inv_sz * byte_sz) > byte_sz) + inv_sz++; +# endif +# if (CPP_WORDSZ == 32) && defined(__GNUC__) + GC_ASSERT(((1ULL << 32) + byte_sz - 1) / byte_sz == inv_sz); +# endif + hhdr -> hb_inv_sz = inv_sz; + } +# else + { + size_t granules = BYTES_TO_GRANULES(byte_sz); + + if (EXPECT(!GC_add_map_entry(granules), FALSE)) { + /* Make it look like a valid block. */ + hhdr -> hb_sz = HBLKSIZE; + hhdr -> hb_descr = 0; + hhdr -> hb_flags |= LARGE_BLOCK; + hhdr -> hb_map = 0; + return FALSE; + } + hhdr -> hb_map = GC_obj_map[(hhdr -> hb_flags & LARGE_BLOCK) != 0 ? + 0 : granules]; + } +# endif + + /* Clear mark bits */ + GC_clear_hdr_marks(hhdr); + + hhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no; + return TRUE; +} + +/* Remove hhdr from the free list (it is assumed to specified by index). */ +STATIC void GC_remove_from_fl_at(hdr *hhdr, int index) +{ + GC_ASSERT(modHBLKSZ(hhdr -> hb_sz) == 0); + if (hhdr -> hb_prev == 0) { + GC_ASSERT(HDR(GC_hblkfreelist[index]) == hhdr); + GC_hblkfreelist[index] = hhdr -> hb_next; + } else { + hdr *phdr; + GET_HDR(hhdr -> hb_prev, phdr); + phdr -> hb_next = hhdr -> hb_next; + } + /* We always need index to maintain free counts. */ + GC_ASSERT(GC_free_bytes[index] >= hhdr -> hb_sz); + GC_free_bytes[index] -= hhdr -> hb_sz; + if (0 != hhdr -> hb_next) { + hdr *nhdr; + GC_ASSERT(!IS_FORWARDING_ADDR_OR_NIL(NHDR(hhdr))); + GET_HDR(hhdr -> hb_next, nhdr); + nhdr -> hb_prev = hhdr -> hb_prev; + } +} + +/* Remove hhdr from the appropriate free list (we assume it is on the */ +/* size-appropriate free list). */ +GC_INLINE void GC_remove_from_fl(hdr *hhdr) +{ + GC_remove_from_fl_at(hhdr, GC_hblk_fl_from_blocks( + (size_t)divHBLKSZ(hhdr -> hb_sz))); +} + +/* Return a pointer to the block ending just before h, if any. */ +static struct hblk * get_block_ending_at(struct hblk *h) +{ + struct hblk * p = h - 1; + hdr *hhdr; + + GET_HDR(p, hhdr); + for (; IS_FORWARDING_ADDR_OR_NIL(hhdr) && hhdr != NULL; hhdr = HDR(p)) { + p = FORWARDED_ADDR(p, hhdr); + } + if (hhdr != NULL) { + return p; + } + p = GC_prev_block(h - 1); + if (p != NULL) { + hhdr = HDR(p); + if ((ptr_t)p + hhdr -> hb_sz == (ptr_t)h) { + return p; + } + } + return NULL; +} + +/* Return a pointer to the free block ending just before h, if any. */ +STATIC struct hblk * GC_free_block_ending_at(struct hblk *h) +{ + struct hblk * p = get_block_ending_at(h); + + if (p /* != NULL */) { /* CPPCHECK */ + hdr *hhdr = HDR(p); + + if (HBLK_IS_FREE(hhdr)) { + return p; + } + } + return 0; +} + +/* Add hhdr to the appropriate free list. */ +/* We maintain individual free lists sorted by address. */ +STATIC void GC_add_to_fl(struct hblk *h, hdr *hhdr) +{ + int index = GC_hblk_fl_from_blocks((size_t)divHBLKSZ(hhdr -> hb_sz)); + struct hblk *second = GC_hblkfreelist[index]; + +# if defined(GC_ASSERTIONS) && !defined(USE_MUNMAP) + { + struct hblk *next = (struct hblk *)((word)h + hhdr -> hb_sz); + hdr * nexthdr = HDR(next); + struct hblk *prev = GC_free_block_ending_at(h); + hdr * prevhdr = HDR(prev); + + GC_ASSERT(nexthdr == 0 || !HBLK_IS_FREE(nexthdr) + || (GC_heapsize & SIGNB) != 0); + /* In the last case, blocks may be too large to merge. */ + GC_ASSERT(NULL == prev || !HBLK_IS_FREE(prevhdr) + || (GC_heapsize & SIGNB) != 0); + } +# endif + GC_ASSERT(modHBLKSZ(hhdr -> hb_sz) == 0); + GC_hblkfreelist[index] = h; + GC_free_bytes[index] += hhdr -> hb_sz; + GC_ASSERT(GC_free_bytes[index] <= GC_large_free_bytes); + hhdr -> hb_next = second; + hhdr -> hb_prev = 0; + if (second /* != NULL */) { /* CPPCHECK */ + hdr * second_hdr; + + GET_HDR(second, second_hdr); + second_hdr -> hb_prev = h; + } + hhdr -> hb_flags |= FREE_BLK; +} + +#ifdef USE_MUNMAP + +#ifdef COUNT_UNMAPPED_REGIONS + /* GC_unmap_old will avoid creating more than this many unmapped regions, */ + /* but an unmapped region may be split again so exceeding the limit. */ + + /* Return the change in number of unmapped regions if the block h swaps */ + /* from its current state of mapped/unmapped to the opposite state. */ + static int calc_num_unmapped_regions_delta(struct hblk *h, hdr *hhdr) + { + struct hblk * prev = get_block_ending_at(h); + struct hblk * next; + GC_bool prev_unmapped = FALSE; + GC_bool next_unmapped = FALSE; + + next = GC_next_block((struct hblk *)((ptr_t)h + hhdr->hb_sz), TRUE); + /* Ensure next is contiguous with h. */ + if ((ptr_t)next != GC_unmap_end((ptr_t)h, (size_t)hhdr->hb_sz)) { + next = NULL; + } + if (prev != NULL) { + hdr * prevhdr = HDR(prev); + prev_unmapped = !IS_MAPPED(prevhdr); + } + if (next != NULL) { + hdr * nexthdr = HDR(next); + next_unmapped = !IS_MAPPED(nexthdr); + } + + if (prev_unmapped && next_unmapped) { + /* If h unmapped, merge two unmapped regions into one. */ + /* If h remapped, split one unmapped region into two. */ + return IS_MAPPED(hhdr) ? -1 : 1; + } + if (!prev_unmapped && !next_unmapped) { + /* If h unmapped, create an isolated unmapped region. */ + /* If h remapped, remove it. */ + return IS_MAPPED(hhdr) ? 1 : -1; + } + /* If h unmapped, merge it with previous or next unmapped region. */ + /* If h remapped, reduce either previous or next unmapped region. */ + /* In either way, no change to the number of unmapped regions. */ + return 0; + } +#endif /* COUNT_UNMAPPED_REGIONS */ + +/* Update GC_num_unmapped_regions assuming the block h changes */ +/* from its current state of mapped/unmapped to the opposite state. */ +GC_INLINE void GC_adjust_num_unmapped(struct hblk *h, hdr *hhdr) +{ +# ifdef COUNT_UNMAPPED_REGIONS + GC_num_unmapped_regions += calc_num_unmapped_regions_delta(h, hhdr); +# else + UNUSED_ARG(h); + UNUSED_ARG(hhdr); +# endif +} + +/* Unmap blocks that haven't been recently touched. This is the only */ +/* way blocks are ever unmapped. */ +GC_INNER void GC_unmap_old(unsigned threshold) +{ + int i; + +# ifdef COUNT_UNMAPPED_REGIONS + /* Skip unmapping if we have already exceeded the soft limit. */ + /* This forgoes any opportunities to merge unmapped regions though. */ + if (GC_num_unmapped_regions >= GC_UNMAPPED_REGIONS_SOFT_LIMIT) + return; +# endif + + for (i = 0; i <= N_HBLK_FLS; ++i) { + struct hblk * h; + hdr * hhdr; + + for (h = GC_hblkfreelist[i]; 0 != h; h = hhdr -> hb_next) { + hhdr = HDR(h); + if (!IS_MAPPED(hhdr)) continue; + + /* Check that the interval is not smaller than the threshold. */ + /* The truncated counter value wrapping is handled correctly. */ + if ((unsigned short)(GC_gc_no - hhdr->hb_last_reclaimed) + >= (unsigned short)threshold) { +# ifdef COUNT_UNMAPPED_REGIONS + /* Continue with unmapping the block only if it will not */ + /* create too many unmapped regions, or if unmapping */ + /* reduces the number of regions. */ + int delta = calc_num_unmapped_regions_delta(h, hhdr); + signed_word regions = GC_num_unmapped_regions + delta; + + if (delta >= 0 && regions >= GC_UNMAPPED_REGIONS_SOFT_LIMIT) { + GC_COND_LOG_PRINTF("Unmapped regions limit reached!\n"); + return; + } + GC_num_unmapped_regions = regions; +# endif + GC_unmap((ptr_t)h, (size_t)(hhdr -> hb_sz)); + hhdr -> hb_flags |= WAS_UNMAPPED; + } + } + } +} + +/* Merge all unmapped blocks that are adjacent to other free */ +/* blocks. This may involve remapping, since all blocks are either */ +/* fully mapped or fully unmapped. */ +GC_INNER void GC_merge_unmapped(void) +{ + int i; + + for (i = 0; i <= N_HBLK_FLS; ++i) { + struct hblk *h = GC_hblkfreelist[i]; + + while (h != 0) { + struct hblk *next; + hdr *hhdr, *nexthdr; + word size, nextsize; + + GET_HDR(h, hhdr); + size = hhdr->hb_sz; + next = (struct hblk *)((word)h + size); + GET_HDR(next, nexthdr); + /* Coalesce with successor, if possible */ + if (nexthdr != NULL && HBLK_IS_FREE(nexthdr) + && !((size + (nextsize = nexthdr -> hb_sz)) & SIGNB) + /* no overflow */) { + /* Note that we usually try to avoid adjacent free blocks */ + /* that are either both mapped or both unmapped. But that */ + /* isn't guaranteed to hold since we remap blocks when we */ + /* split them, and don't merge at that point. It may also */ + /* not hold if the merged block would be too big. */ + if (IS_MAPPED(hhdr) && !IS_MAPPED(nexthdr)) { + /* make both consistent, so that we can merge */ + if (size > nextsize) { + GC_adjust_num_unmapped(next, nexthdr); + GC_remap((ptr_t)next, nextsize); + } else { + GC_adjust_num_unmapped(h, hhdr); + GC_unmap((ptr_t)h, size); + GC_unmap_gap((ptr_t)h, size, (ptr_t)next, nextsize); + hhdr -> hb_flags |= WAS_UNMAPPED; + } + } else if (IS_MAPPED(nexthdr) && !IS_MAPPED(hhdr)) { + if (size > nextsize) { + GC_adjust_num_unmapped(next, nexthdr); + GC_unmap((ptr_t)next, nextsize); + GC_unmap_gap((ptr_t)h, size, (ptr_t)next, nextsize); + } else { + GC_adjust_num_unmapped(h, hhdr); + GC_remap((ptr_t)h, size); + hhdr -> hb_flags &= (unsigned char)~WAS_UNMAPPED; + hhdr -> hb_last_reclaimed = nexthdr -> hb_last_reclaimed; + } + } else if (!IS_MAPPED(hhdr) && !IS_MAPPED(nexthdr)) { + /* Unmap any gap in the middle */ + GC_unmap_gap((ptr_t)h, size, (ptr_t)next, nextsize); + } + /* If they are both unmapped, we merge, but leave unmapped. */ + GC_remove_from_fl_at(hhdr, i); + GC_remove_from_fl(nexthdr); + hhdr -> hb_sz += nexthdr -> hb_sz; + GC_remove_header(next); + GC_add_to_fl(h, hhdr); + /* Start over at beginning of list */ + h = GC_hblkfreelist[i]; + } else /* not mergeable with successor */ { + h = hhdr -> hb_next; + } + } /* while (h != 0) ... */ + } /* for ... */ +} + +#endif /* USE_MUNMAP */ + +/* + * Return a pointer to a block starting at h of length bytes. + * Memory for the block is mapped. + * Remove the block from its free list, and return the remainder (if any) + * to its appropriate free list. + * May fail by returning 0. + * The header for the returned block must be set up by the caller. + * If the return value is not 0, then hhdr is the header for it. + */ +STATIC struct hblk * GC_get_first_part(struct hblk *h, hdr *hhdr, + size_t bytes, int index) +{ + size_t total_size; + struct hblk * rest; + hdr * rest_hdr; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(modHBLKSZ(bytes) == 0); + total_size = (size_t)(hhdr -> hb_sz); + GC_ASSERT(modHBLKSZ(total_size) == 0); + GC_remove_from_fl_at(hhdr, index); + if (total_size == bytes) return h; + + rest = (struct hblk *)((word)h + bytes); + rest_hdr = GC_install_header(rest); + if (EXPECT(NULL == rest_hdr, FALSE)) { + /* FIXME: This is likely to be very bad news ... */ + WARN("Header allocation failed: dropping block\n", 0); + return NULL; + } + rest_hdr -> hb_sz = total_size - bytes; + rest_hdr -> hb_flags = 0; +# ifdef GC_ASSERTIONS + /* Mark h not free, to avoid assertion about adjacent free blocks. */ + hhdr -> hb_flags &= (unsigned char)~FREE_BLK; +# endif + GC_add_to_fl(rest, rest_hdr); + return h; +} + +/* + * H is a free block. N points at an address inside it. + * A new header for n has already been set up. Fix up h's header + * to reflect the fact that it is being split, move it to the + * appropriate free list. + * N replaces h in the original free list. + * + * Nhdr is not completely filled in, since it is about to allocated. + * It may in fact end up on the wrong free list for its size. + * That's not a disaster, since n is about to be allocated + * by our caller. + * (Hence adding it to a free list is silly. But this path is hopefully + * rare enough that it doesn't matter. The code is cleaner this way.) + */ +STATIC void GC_split_block(struct hblk *h, hdr *hhdr, struct hblk *n, + hdr *nhdr, int index /* of free list */) +{ + word total_size = hhdr -> hb_sz; + word h_size = (word)n - (word)h; + struct hblk *prev = hhdr -> hb_prev; + struct hblk *next = hhdr -> hb_next; + + /* Replace h with n on its freelist */ + nhdr -> hb_prev = prev; + nhdr -> hb_next = next; + nhdr -> hb_sz = total_size - h_size; + nhdr -> hb_flags = 0; + if (prev /* != NULL */) { /* CPPCHECK */ + HDR(prev) -> hb_next = n; + } else { + GC_hblkfreelist[index] = n; + } + if (next /* != NULL */) { + HDR(next) -> hb_prev = n; + } + GC_ASSERT(GC_free_bytes[index] > h_size); + GC_free_bytes[index] -= h_size; +# ifdef USE_MUNMAP + hhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no; +# endif + hhdr -> hb_sz = h_size; + GC_add_to_fl(h, hhdr); + nhdr -> hb_flags |= FREE_BLK; +} + +STATIC struct hblk *GC_allochblk_nth(size_t sz /* bytes */, int kind, + unsigned flags, int n, int may_split, + size_t align_m1); + +#ifdef USE_MUNMAP +# define AVOID_SPLIT_REMAPPED 2 +#endif + +GC_INNER struct hblk *GC_allochblk(size_t sz, int kind, + unsigned flags /* IGNORE_OFF_PAGE or 0 */, + size_t align_m1) +{ + size_t blocks; + int start_list; + struct hblk *result; + int may_split; + int split_limit; /* highest index of free list whose blocks we split */ + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT((sz & (GC_GRANULE_BYTES-1)) == 0); + blocks = OBJ_SZ_TO_BLOCKS_CHECKED(sz); + if (EXPECT(SIZET_SAT_ADD(blocks * HBLKSIZE, align_m1) + >= (GC_SIZE_MAX >> 1), FALSE)) + return NULL; /* overflow */ + + start_list = GC_hblk_fl_from_blocks(blocks); + /* Try for an exact match first. */ + result = GC_allochblk_nth(sz, kind, flags, start_list, FALSE, align_m1); + if (result != NULL) return result; + + may_split = TRUE; + if (GC_use_entire_heap || GC_dont_gc + || GC_heapsize - GC_large_free_bytes < GC_requested_heapsize + || GC_incremental || !GC_should_collect()) { + /* Should use more of the heap, even if it requires splitting. */ + split_limit = N_HBLK_FLS; + } else if (GC_finalizer_bytes_freed > (GC_heapsize >> 4)) { + /* If we are deallocating lots of memory from */ + /* finalizers, fail and collect sooner rather */ + /* than later. */ + split_limit = 0; + } else { + /* If we have enough large blocks left to cover any */ + /* previous request for large blocks, we go ahead */ + /* and split. Assuming a steady state, that should */ + /* be safe. It means that we can use the full */ + /* heap if we allocate only small objects. */ + split_limit = GC_enough_large_bytes_left(); +# ifdef USE_MUNMAP + if (split_limit > 0) + may_split = AVOID_SPLIT_REMAPPED; +# endif + } + if (start_list < UNIQUE_THRESHOLD && 0 == align_m1) { + /* No reason to try start_list again, since all blocks are exact */ + /* matches. */ + ++start_list; + } + for (; start_list <= split_limit; ++start_list) { + result = GC_allochblk_nth(sz, kind, flags, start_list, may_split, + align_m1); + if (result != NULL) break; + } + return result; +} + +STATIC long GC_large_alloc_warn_suppressed = 0; + /* Number of warnings suppressed so far. */ + +STATIC unsigned GC_drop_blacklisted_count = 0; + /* Counter of the cases when found block by */ + /* GC_allochblk_nth is blacklisted completely. */ + +#define ALIGN_PAD_SZ(p, align_m1) \ + (((align_m1) + 1 - (size_t)(word)(p)) & (align_m1)) + +static GC_bool next_hblk_fits_better(hdr *hhdr, word size_avail, + word size_needed, size_t align_m1) +{ + hdr *next_hdr; + word next_size; + size_t next_ofs; + struct hblk *next_hbp = hhdr -> hb_next; + + if (NULL == next_hbp) return FALSE; /* no next block */ + GET_HDR(next_hbp, next_hdr); + next_size = next_hdr -> hb_sz; + if (size_avail <= next_size) return FALSE; /* not enough size */ + + next_ofs = ALIGN_PAD_SZ(next_hbp, align_m1); + return next_size >= size_needed + next_ofs + && !GC_is_black_listed(next_hbp + divHBLKSZ(next_ofs), size_needed); +} + +static struct hblk *find_nonbl_hblk(struct hblk *last_hbp, word size_remain, + word eff_size_needed, size_t align_m1) +{ + word search_end = ((word)last_hbp + size_remain) & ~(word)align_m1; + + do { + struct hblk *next_hbp; + + last_hbp += divHBLKSZ(ALIGN_PAD_SZ(last_hbp, align_m1)); + next_hbp = GC_is_black_listed(last_hbp, eff_size_needed); + if (NULL == next_hbp) return last_hbp; /* not black-listed */ + last_hbp = next_hbp; + } while ((word)last_hbp <= search_end); + return NULL; +} + +/* Allocate and drop the block in small chunks, to maximize the chance */ +/* that we will recover some later. hhdr should correspond to hbp. */ +static void drop_hblk_in_chunks(int n, struct hblk *hbp, hdr *hhdr) +{ + size_t total_size = (size_t)(hhdr -> hb_sz); + struct hblk *limit = hbp + divHBLKSZ(total_size); + + GC_ASSERT(HDR(hbp) == hhdr); + GC_ASSERT(modHBLKSZ(total_size) == 0 && total_size > 0); + GC_large_free_bytes -= total_size; + GC_bytes_dropped += total_size; + GC_remove_from_fl_at(hhdr, n); + do { + (void)setup_header(hhdr, hbp, HBLKSIZE, PTRFREE, 0); /* cannot fail */ + if (GC_debugging_started) BZERO(hbp, HBLKSIZE); + if ((word)(++hbp) >= (word)limit) break; + + hhdr = GC_install_header(hbp); + } while (EXPECT(hhdr != NULL, TRUE)); /* no header allocation failure? */ +} + +/* The same as GC_allochblk, but with search restricted to the n-th */ +/* free list. flags should be IGNORE_OFF_PAGE or zero; may_split */ +/* indicates whether it is OK to split larger blocks; sz is in bytes. */ +/* If may_split is set to AVOID_SPLIT_REMAPPED, then memory remapping */ +/* followed by splitting should be generally avoided. Rounded-up sz */ +/* plus align_m1 value should be less than GC_SIZE_MAX/2. */ +STATIC struct hblk *GC_allochblk_nth(size_t sz, int kind, unsigned flags, + int n, int may_split, size_t align_m1) +{ + struct hblk *hbp, *last_hbp; + hdr *hhdr; /* header corresponding to hbp */ + word size_needed = HBLKSIZE * OBJ_SZ_TO_BLOCKS_CHECKED(sz); + /* number of bytes in requested objects */ + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(((align_m1 + 1) & align_m1) == 0 && sz > 0); + GC_ASSERT(0 == align_m1 || modHBLKSZ(align_m1 + 1) == 0); + retry: + /* Search for a big enough block in free list. */ + for (hbp = GC_hblkfreelist[n];; hbp = hhdr -> hb_next) { + word size_avail; /* bytes available in this block */ + size_t align_ofs; + + if (hbp /* != NULL */) { + /* CPPCHECK */ + } else { + return NULL; + } + GET_HDR(hbp, hhdr); /* set hhdr value */ + size_avail = hhdr -> hb_sz; + if (!may_split && size_avail != size_needed) continue; + + align_ofs = ALIGN_PAD_SZ(hbp, align_m1); + if (size_avail < size_needed + align_ofs) + continue; /* the block is too small */ + + if (size_avail != size_needed) { + /* If the next heap block is obviously better, go on. */ + /* This prevents us from disassembling a single large */ + /* block to get tiny blocks. */ + if (next_hblk_fits_better(hhdr, size_avail, size_needed, align_m1)) + continue; + } + + if (IS_UNCOLLECTABLE(kind) + || (kind == PTRFREE && size_needed <= MAX_BLACK_LIST_ALLOC)) { + last_hbp = hbp + divHBLKSZ(align_ofs); + break; + } + + last_hbp = find_nonbl_hblk(hbp, size_avail - size_needed, + (flags & IGNORE_OFF_PAGE) != 0 ? HBLKSIZE : size_needed, + align_m1); + /* Is non-blacklisted part of enough size? */ + if (last_hbp != NULL) { +# ifdef USE_MUNMAP + /* Avoid remapping followed by splitting. */ + if (may_split == AVOID_SPLIT_REMAPPED && last_hbp != hbp + && !IS_MAPPED(hhdr)) + continue; +# endif + break; + } + + /* The block is completely blacklisted. If so, we need to */ + /* drop some such blocks, since otherwise we spend all our */ + /* time traversing them if pointer-free blocks are unpopular. */ + /* A dropped block will be reconsidered at next GC. */ + if (size_needed == HBLKSIZE && 0 == align_m1 + && !GC_find_leak && IS_MAPPED(hhdr) + && (++GC_drop_blacklisted_count & 3) == 0) { + struct hblk *prev = hhdr -> hb_prev; + + drop_hblk_in_chunks(n, hbp, hhdr); + if (NULL == prev) goto retry; + /* Restore hhdr to point at free block. */ + hhdr = HDR(prev); + continue; + } + + if (size_needed > BL_LIMIT && size_avail - size_needed > BL_LIMIT) { + /* Punt, since anything else risks unreasonable heap growth. */ + if (++GC_large_alloc_warn_suppressed + >= GC_large_alloc_warn_interval) { + WARN("Repeated allocation of very large block" + " (appr. size %" WARN_PRIuPTR " KiB):\n" + "\tMay lead to memory leak and poor performance\n", + size_needed >> 10); + GC_large_alloc_warn_suppressed = 0; + } + last_hbp = hbp + divHBLKSZ(align_ofs); + break; + } + } + + GC_ASSERT(((word)last_hbp & align_m1) == 0); + if (last_hbp != hbp) { + hdr *last_hdr = GC_install_header(last_hbp); + + if (EXPECT(NULL == last_hdr, FALSE)) return NULL; + /* Make sure it's mapped before we mangle it. */ +# ifdef USE_MUNMAP + if (!IS_MAPPED(hhdr)) { + GC_adjust_num_unmapped(hbp, hhdr); + GC_remap((ptr_t)hbp, (size_t)(hhdr -> hb_sz)); + hhdr -> hb_flags &= (unsigned char)~WAS_UNMAPPED; + } +# endif + /* Split the block at last_hbp. */ + GC_split_block(hbp, hhdr, last_hbp, last_hdr, n); + /* We must now allocate last_hbp, since it may be on the */ + /* wrong free list. */ + hbp = last_hbp; + hhdr = last_hdr; + } + GC_ASSERT(hhdr -> hb_sz >= size_needed); + +# ifdef USE_MUNMAP + if (!IS_MAPPED(hhdr)) { + GC_adjust_num_unmapped(hbp, hhdr); + GC_remap((ptr_t)hbp, (size_t)(hhdr -> hb_sz)); + hhdr -> hb_flags &= (unsigned char)~WAS_UNMAPPED; + /* Note: This may leave adjacent, mapped free blocks. */ + } +# endif + /* hbp may be on the wrong freelist; the parameter n is important. */ + hbp = GC_get_first_part(hbp, hhdr, (size_t)size_needed, n); + if (EXPECT(NULL == hbp, FALSE)) return NULL; + + /* Add it to map of valid blocks. */ + if (EXPECT(!GC_install_counts(hbp, (size_t)size_needed), FALSE)) + return NULL; /* This leaks memory under very rare conditions. */ + + /* Set up the header. */ + GC_ASSERT(HDR(hbp) == hhdr); + if (EXPECT(!setup_header(hhdr, hbp, sz, kind, flags), FALSE)) { + GC_remove_counts(hbp, (size_t)size_needed); + return NULL; /* ditto */ + } + +# ifndef GC_DISABLE_INCREMENTAL + /* Notify virtual dirty bit implementation that we are about to */ + /* write. Ensure that pointer-free objects are not protected */ + /* if it is avoidable. This also ensures that newly allocated */ + /* blocks are treated as dirty. Necessary since we don't */ + /* protect free blocks. */ + GC_ASSERT(modHBLKSZ(size_needed) == 0); + GC_remove_protection(hbp, divHBLKSZ(size_needed), IS_PTRFREE(hhdr)); +# endif + /* We just successfully allocated a block. Restart count of */ + /* consecutive failures. */ + GC_fail_count = 0; + + GC_large_free_bytes -= size_needed; + GC_ASSERT(IS_MAPPED(hhdr)); + return hbp; +} + +/* + * Free a heap block. + * + * Coalesce the block with its neighbors if possible. + * + * All mark words are assumed to be cleared. + */ +GC_INNER void GC_freehblk(struct hblk *hbp) +{ + struct hblk *next, *prev; + hdr *hhdr, *prevhdr, *nexthdr; + word size; + + GET_HDR(hbp, hhdr); + size = HBLKSIZE * OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); + if ((size & SIGNB) != 0) + ABORT("Deallocating excessively large block. Too large an allocation?"); + /* Probably possible if we try to allocate more than half the address */ + /* space at once. If we don't catch it here, strange things happen */ + /* later. */ + GC_remove_counts(hbp, (size_t)size); + hhdr -> hb_sz = size; +# ifdef USE_MUNMAP + hhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no; +# endif + + /* Check for duplicate deallocation in the easy case */ + if (HBLK_IS_FREE(hhdr)) { + ABORT_ARG1("Duplicate large block deallocation", + " of %p", (void *)hbp); + } + + GC_ASSERT(IS_MAPPED(hhdr)); + hhdr -> hb_flags |= FREE_BLK; + next = (struct hblk *)((ptr_t)hbp + size); + GET_HDR(next, nexthdr); + prev = GC_free_block_ending_at(hbp); + /* Coalesce with successor, if possible */ + if (nexthdr != NULL && HBLK_IS_FREE(nexthdr) && IS_MAPPED(nexthdr) + && !((hhdr -> hb_sz + nexthdr -> hb_sz) & SIGNB) /* no overflow */) { + GC_remove_from_fl(nexthdr); + hhdr -> hb_sz += nexthdr -> hb_sz; + GC_remove_header(next); + } + /* Coalesce with predecessor, if possible. */ + if (prev /* != NULL */) { /* CPPCHECK */ + prevhdr = HDR(prev); + if (IS_MAPPED(prevhdr) + && !((hhdr -> hb_sz + prevhdr -> hb_sz) & SIGNB)) { + GC_remove_from_fl(prevhdr); + prevhdr -> hb_sz += hhdr -> hb_sz; +# ifdef USE_MUNMAP + prevhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no; +# endif + GC_remove_header(hbp); + hbp = prev; + hhdr = prevhdr; + } + } + /* FIXME: It is not clear we really always want to do these merges */ + /* with USE_MUNMAP, since it updates ages and hence prevents */ + /* unmapping. */ + + GC_large_free_bytes += size; + GC_add_to_fl(hbp, hhdr); +} + +/* + * Copyright (c) 1988-1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2011 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + + + +/* + * Separate free lists are maintained for different sized objects + * up to MAXOBJBYTES. + * The call GC_allocobj(i,k) ensures that the freelist for + * kind k objects of size i points to a non-empty + * free list. It returns a pointer to the first entry on the free list. + * In a single-threaded world, GC_allocobj may be called to allocate + * an object of small size lb (and NORMAL kind) as follows + * (GC_generic_malloc_inner is a wrapper over GC_allocobj which also + * fills in GC_size_map if needed): + * + * lg = GC_size_map[lb]; + * op = GC_objfreelist[lg]; + * if (NULL == op) { + * op = GC_generic_malloc_inner(lb, NORMAL, 0); + * } else { + * GC_objfreelist[lg] = obj_link(op); + * GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + * } + * + * Note that this is very fast if the free list is non-empty; it should + * only involve the execution of 4 or 5 simple instructions. + * All composite objects on freelists are cleared, except for + * their first word. + */ + +/* + * The allocator uses GC_allochblk to allocate large chunks of objects. + * These chunks all start on addresses which are multiples of + * HBLKSZ. Each allocated chunk has an associated header, + * which can be located quickly based on the address of the chunk. + * (See headers.c for details.) + * This makes it possible to check quickly whether an + * arbitrary address corresponds to an object administered by the + * allocator. + */ + +word GC_non_gc_bytes = 0; /* Number of bytes not intended to be collected */ + +word GC_gc_no = 0; + +#ifndef NO_CLOCK + static unsigned long full_gc_total_time = 0; /* in ms, may wrap */ + static unsigned long stopped_mark_total_time = 0; + static unsigned32 full_gc_total_ns_frac = 0; /* fraction of 1 ms */ + static unsigned32 stopped_mark_total_ns_frac = 0; + static GC_bool measure_performance = FALSE; + /* Do performance measurements if set to true (e.g., */ + /* accumulation of the total time of full collections). */ + + GC_API void GC_CALL GC_start_performance_measurement(void) + { + measure_performance = TRUE; + } + + GC_API unsigned long GC_CALL GC_get_full_gc_total_time(void) + { + return full_gc_total_time; + } + + GC_API unsigned long GC_CALL GC_get_stopped_mark_total_time(void) + { + return stopped_mark_total_time; + } +#endif /* !NO_CLOCK */ + +#ifndef GC_DISABLE_INCREMENTAL + GC_INNER GC_bool GC_incremental = FALSE; /* By default, stop the world. */ + STATIC GC_bool GC_should_start_incremental_collection = FALSE; +#endif + +GC_API int GC_CALL GC_is_incremental_mode(void) +{ + return (int)GC_incremental; +} + +#ifdef THREADS + int GC_parallel = FALSE; /* By default, parallel GC is off. */ +#endif + +#if defined(GC_FULL_FREQ) && !defined(CPPCHECK) + int GC_full_freq = GC_FULL_FREQ; +#else + int GC_full_freq = 19; /* Every 20th collection is a full */ + /* collection, whether we need it */ + /* or not. */ +#endif + +STATIC GC_bool GC_need_full_gc = FALSE; + /* Need full GC due to heap growth. */ + +#ifdef THREAD_LOCAL_ALLOC + GC_INNER GC_bool GC_world_stopped = FALSE; +#endif + +STATIC GC_bool GC_disable_automatic_collection = FALSE; + +GC_API void GC_CALL GC_set_disable_automatic_collection(int value) +{ + LOCK(); + GC_disable_automatic_collection = (GC_bool)value; + UNLOCK(); +} + +GC_API int GC_CALL GC_get_disable_automatic_collection(void) +{ + int value; + + READER_LOCK(); + value = (int)GC_disable_automatic_collection; + READER_UNLOCK(); + return value; +} + +STATIC word GC_used_heap_size_after_full = 0; + +/* Version macros are now defined in gc_version.h, which is included by */ +/* gc.h, which is included by gc_priv.h. */ +#ifndef GC_NO_VERSION_VAR + EXTERN_C_BEGIN + extern const GC_VERSION_VAL_T GC_version; + EXTERN_C_END + + const GC_VERSION_VAL_T GC_version = + ((GC_VERSION_VAL_T)GC_VERSION_MAJOR << 16) + | (GC_VERSION_MINOR << 8) | GC_VERSION_MICRO; +#endif + +GC_API GC_VERSION_VAL_T GC_CALL GC_get_version(void) +{ + return ((GC_VERSION_VAL_T)GC_VERSION_MAJOR << 16) + | (GC_VERSION_MINOR << 8) | GC_VERSION_MICRO; +} + +/* some more variables */ + +#ifdef GC_DONT_EXPAND + int GC_dont_expand = TRUE; +#else + int GC_dont_expand = FALSE; +#endif + +#if defined(GC_FREE_SPACE_DIVISOR) && !defined(CPPCHECK) + word GC_free_space_divisor = GC_FREE_SPACE_DIVISOR; /* must be > 0 */ +#else + word GC_free_space_divisor = 3; +#endif + +GC_INNER int GC_CALLBACK GC_never_stop_func(void) +{ + return FALSE; +} + +#if defined(GC_TIME_LIMIT) && !defined(CPPCHECK) + unsigned long GC_time_limit = GC_TIME_LIMIT; + /* We try to keep pause times from exceeding */ + /* this by much. In milliseconds. */ +#elif defined(PARALLEL_MARK) + unsigned long GC_time_limit = GC_TIME_UNLIMITED; + /* The parallel marker cannot be interrupted for */ + /* now, so the time limit is absent by default. */ +#else + unsigned long GC_time_limit = 15; +#endif + +#ifndef NO_CLOCK + STATIC unsigned long GC_time_lim_nsec = 0; + /* The nanoseconds add-on to GC_time_limit */ + /* value. Not updated by GC_set_time_limit(). */ + /* Ignored if the value of GC_time_limit is */ + /* GC_TIME_UNLIMITED. */ + +# define TV_NSEC_LIMIT (1000UL * 1000) /* amount of nanoseconds in 1 ms */ + + GC_API void GC_CALL GC_set_time_limit_tv(struct GC_timeval_s tv) + { + GC_ASSERT(tv.tv_ms <= GC_TIME_UNLIMITED); + GC_ASSERT(tv.tv_nsec < TV_NSEC_LIMIT); + GC_time_limit = tv.tv_ms; + GC_time_lim_nsec = tv.tv_nsec; + } + + GC_API struct GC_timeval_s GC_CALL GC_get_time_limit_tv(void) + { + struct GC_timeval_s tv; + + tv.tv_ms = GC_time_limit; + tv.tv_nsec = GC_time_lim_nsec; + return tv; + } + + STATIC CLOCK_TYPE GC_start_time = CLOCK_TYPE_INITIALIZER; + /* Time at which we stopped world. */ + /* used only in GC_timeout_stop_func. */ +#endif /* !NO_CLOCK */ + +STATIC int GC_n_attempts = 0; /* Number of attempts at finishing */ + /* collection within GC_time_limit. */ + +STATIC GC_stop_func GC_default_stop_func = GC_never_stop_func; + /* Accessed holding the allocator lock. */ + +GC_API void GC_CALL GC_set_stop_func(GC_stop_func stop_func) +{ + GC_ASSERT(NONNULL_ARG_NOT_NULL(stop_func)); + LOCK(); + GC_default_stop_func = stop_func; + UNLOCK(); +} + +GC_API GC_stop_func GC_CALL GC_get_stop_func(void) +{ + GC_stop_func stop_func; + + READER_LOCK(); + stop_func = GC_default_stop_func; + READER_UNLOCK(); + return stop_func; +} + +#if defined(GC_DISABLE_INCREMENTAL) || defined(NO_CLOCK) +# define GC_timeout_stop_func GC_default_stop_func +#else + STATIC int GC_CALLBACK GC_timeout_stop_func(void) + { + CLOCK_TYPE current_time; + static unsigned count = 0; + unsigned long time_diff, nsec_diff; + + GC_ASSERT(I_HOLD_LOCK()); + if (GC_default_stop_func()) + return TRUE; + + if (GC_time_limit == GC_TIME_UNLIMITED || (count++ & 3) != 0) + return FALSE; + + GET_TIME(current_time); + time_diff = MS_TIME_DIFF(current_time, GC_start_time); + nsec_diff = NS_FRAC_TIME_DIFF(current_time, GC_start_time); +# if defined(CPPCHECK) + GC_noop1((word)&nsec_diff); +# endif + if (time_diff >= GC_time_limit + && (time_diff > GC_time_limit || nsec_diff >= GC_time_lim_nsec)) { + GC_COND_LOG_PRINTF("Abandoning stopped marking after %lu ms %lu ns" + " (attempt %d)\n", + time_diff, nsec_diff, GC_n_attempts); + return TRUE; + } + + return FALSE; + } +#endif /* !GC_DISABLE_INCREMENTAL */ + +#ifdef THREADS + GC_INNER word GC_total_stacksize = 0; /* updated on every push_all_stacks */ +#endif + +static size_t min_bytes_allocd_minimum = 1; + /* The lowest value returned by min_bytes_allocd(). */ + +GC_API void GC_CALL GC_set_min_bytes_allocd(size_t value) +{ + GC_ASSERT(value > 0); + min_bytes_allocd_minimum = value; +} + +GC_API size_t GC_CALL GC_get_min_bytes_allocd(void) +{ + return min_bytes_allocd_minimum; +} + +/* Return the minimum number of bytes that must be allocated between */ +/* collections to amortize the collection cost. Should be non-zero. */ +static word min_bytes_allocd(void) +{ + word result; + word stack_size; + word total_root_size; /* includes double stack size, */ + /* since the stack is expensive */ + /* to scan. */ + word scan_size; /* Estimate of memory to be scanned */ + /* during normal GC. */ + + GC_ASSERT(I_HOLD_LOCK()); +# ifdef THREADS + if (GC_need_to_lock) { + /* We are multi-threaded... */ + stack_size = GC_total_stacksize; + /* For now, we just use the value computed during the latest GC. */ +# ifdef DEBUG_THREADS + GC_log_printf("Total stacks size: %lu\n", + (unsigned long)stack_size); +# endif + } else +# endif + /* else*/ { +# ifdef STACK_NOT_SCANNED + stack_size = 0; +# elif defined(STACK_GROWS_UP) + stack_size = (word)(GC_approx_sp() - GC_stackbottom); +# else + stack_size = (word)(GC_stackbottom - GC_approx_sp()); +# endif + } + + total_root_size = 2 * stack_size + GC_root_size; + scan_size = 2 * GC_composite_in_use + GC_atomic_in_use / 4 + + total_root_size; + result = scan_size / GC_free_space_divisor; + if (GC_incremental) { + result /= 2; + } + return result > min_bytes_allocd_minimum + ? result : min_bytes_allocd_minimum; +} + +STATIC word GC_non_gc_bytes_at_gc = 0; + /* Number of explicitly managed bytes of storage */ + /* at last collection. */ + +/* Return the number of bytes allocated, adjusted for explicit storage */ +/* management, etc. This number is used in deciding when to trigger */ +/* collections. */ +STATIC word GC_adj_bytes_allocd(void) +{ + signed_word result; + signed_word expl_managed = (signed_word)GC_non_gc_bytes + - (signed_word)GC_non_gc_bytes_at_gc; + + /* Don't count what was explicitly freed, or newly allocated for */ + /* explicit management. Note that deallocating an explicitly */ + /* managed object should not alter result, assuming the client */ + /* is playing by the rules. */ + result = (signed_word)GC_bytes_allocd + + (signed_word)GC_bytes_dropped + - (signed_word)GC_bytes_freed + + (signed_word)GC_finalizer_bytes_freed + - expl_managed; + if (result > (signed_word)GC_bytes_allocd) { + result = (signed_word)GC_bytes_allocd; + /* probably client bug or unfortunate scheduling */ + } + result += (signed_word)GC_bytes_finalized; + /* We count objects enqueued for finalization as though they */ + /* had been reallocated this round. Finalization is user */ + /* visible progress. And if we don't count this, we have */ + /* stability problems for programs that finalize all objects. */ + if (result < (signed_word)(GC_bytes_allocd >> 3)) { + /* Always count at least 1/8 of the allocations. We don't want */ + /* to collect too infrequently, since that would inhibit */ + /* coalescing of free storage blocks. */ + /* This also makes us partially robust against client bugs. */ + result = (signed_word)(GC_bytes_allocd >> 3); + } + return (word)result; +} + + +/* Clear up a few frames worth of garbage left at the top of the stack. */ +/* This is used to prevent us from accidentally treating garbage left */ +/* on the stack by other parts of the collector as roots. This */ +/* differs from the code in misc.c, which actually tries to keep the */ +/* stack clear of long-lived, client-generated garbage. */ +STATIC void GC_clear_a_few_frames(void) +{ +# ifndef CLEAR_NWORDS +# define CLEAR_NWORDS 64 +# endif + volatile word frames[CLEAR_NWORDS]; + BZERO((/* no volatile */ word *)((word)frames), + CLEAR_NWORDS * sizeof(word)); +} + +GC_API void GC_CALL GC_start_incremental_collection(void) +{ +# ifndef GC_DISABLE_INCREMENTAL + LOCK(); + if (GC_incremental) { + GC_should_start_incremental_collection = TRUE; + if (!GC_dont_gc) { + ENTER_GC(); + GC_collect_a_little_inner(1); + EXIT_GC(); + } + } + UNLOCK(); +# endif +} + +/* Have we allocated enough to amortize a collection? */ +GC_INNER GC_bool GC_should_collect(void) +{ + static word last_min_bytes_allocd; + static word last_gc_no; + + GC_ASSERT(I_HOLD_LOCK()); + if (last_gc_no != GC_gc_no) { + last_min_bytes_allocd = min_bytes_allocd(); + last_gc_no = GC_gc_no; + } +# ifndef GC_DISABLE_INCREMENTAL + if (GC_should_start_incremental_collection) { + GC_should_start_incremental_collection = FALSE; + return TRUE; + } +# endif + if (GC_disable_automatic_collection) return FALSE; + + if (GC_last_heap_growth_gc_no == GC_gc_no) + return TRUE; /* avoid expanding past limits used by blacklisting */ + + return GC_adj_bytes_allocd() >= last_min_bytes_allocd; +} + +/* STATIC */ GC_start_callback_proc GC_start_call_back = 0; + /* Called at start of full collections. */ + /* Not called if 0. Called with the allocator */ + /* lock held. Not used by GC itself. */ + +GC_API void GC_CALL GC_set_start_callback(GC_start_callback_proc fn) +{ + LOCK(); + GC_start_call_back = fn; + UNLOCK(); +} + +GC_API GC_start_callback_proc GC_CALL GC_get_start_callback(void) +{ + GC_start_callback_proc fn; + + READER_LOCK(); + fn = GC_start_call_back; + READER_UNLOCK(); + return fn; +} + +GC_INLINE void GC_notify_full_gc(void) +{ + if (GC_start_call_back != 0) { + (*GC_start_call_back)(); + } +} + +STATIC GC_bool GC_is_full_gc = FALSE; + +STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func); +STATIC void GC_finish_collection(void); + +/* Initiate a garbage collection if appropriate. Choose judiciously */ +/* between partial, full, and stop-world collections. */ +STATIC void GC_maybe_gc(void) +{ + static int n_partial_gcs = 0; + + GC_ASSERT(I_HOLD_LOCK()); + ASSERT_CANCEL_DISABLED(); + if (!GC_should_collect()) return; + + if (!GC_incremental) { + GC_gcollect_inner(); + return; + } + + GC_ASSERT(!GC_collection_in_progress()); +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_wait_for_reclaim(); +# endif + if (GC_need_full_gc || n_partial_gcs >= GC_full_freq) { + GC_COND_LOG_PRINTF( + "***>Full mark for collection #%lu after %lu allocd bytes\n", + (unsigned long)GC_gc_no + 1, (unsigned long)GC_bytes_allocd); + GC_promote_black_lists(); + (void)GC_reclaim_all((GC_stop_func)0, TRUE); + GC_notify_full_gc(); + GC_clear_marks(); + n_partial_gcs = 0; + GC_is_full_gc = TRUE; + } else { + n_partial_gcs++; + } + + /* Try to mark with the world stopped. If we run out of */ + /* time, this turns into an incremental marking. */ +# ifndef NO_CLOCK + if (GC_time_limit != GC_TIME_UNLIMITED) GET_TIME(GC_start_time); +# endif + if (GC_stopped_mark(GC_timeout_stop_func)) { +# ifdef SAVE_CALL_CHAIN + GC_save_callers(GC_last_stack); +# endif + GC_finish_collection(); + } else if (!GC_is_full_gc) { + /* Count this as the first attempt. */ + GC_n_attempts++; + } +} + +STATIC GC_on_collection_event_proc GC_on_collection_event = 0; + +GC_API void GC_CALL GC_set_on_collection_event(GC_on_collection_event_proc fn) +{ + /* fn may be 0 (means no event notifier). */ + LOCK(); + GC_on_collection_event = fn; + UNLOCK(); +} + +GC_API GC_on_collection_event_proc GC_CALL GC_get_on_collection_event(void) +{ + GC_on_collection_event_proc fn; + + READER_LOCK(); + fn = GC_on_collection_event; + READER_UNLOCK(); + return fn; +} + +/* Stop the world garbage collection. If stop_func is not */ +/* GC_never_stop_func then abort if stop_func returns TRUE. */ +/* Return TRUE if we successfully completed the collection. */ +GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func stop_func) +{ +# ifndef NO_CLOCK + CLOCK_TYPE start_time = CLOCK_TYPE_INITIALIZER; + GC_bool start_time_valid; +# endif + + ASSERT_CANCEL_DISABLED(); + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_is_initialized); + if (GC_dont_gc || (*stop_func)()) return FALSE; + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_START); + if (GC_incremental && GC_collection_in_progress()) { + GC_COND_LOG_PRINTF( + "GC_try_to_collect_inner: finishing collection in progress\n"); + /* Just finish collection already in progress. */ + do { + if ((*stop_func)()) { + /* TODO: Notify GC_EVENT_ABANDON */ + return FALSE; + } + ENTER_GC(); + GC_collect_a_little_inner(1); + EXIT_GC(); + } while (GC_collection_in_progress()); + } + GC_notify_full_gc(); +# ifndef NO_CLOCK + start_time_valid = FALSE; + if ((GC_print_stats | (int)measure_performance) != 0) { + if (GC_print_stats) + GC_log_printf("Initiating full world-stop collection!\n"); + start_time_valid = TRUE; + GET_TIME(start_time); + } +# endif + GC_promote_black_lists(); + /* Make sure all blocks have been reclaimed, so sweep routines */ + /* don't see cleared mark bits. */ + /* If we're guaranteed to finish, then this is unnecessary. */ + /* In the find_leak case, we have to finish to guarantee that */ + /* previously unmarked objects are not reported as leaks. */ +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_wait_for_reclaim(); +# endif + if ((GC_find_leak || stop_func != GC_never_stop_func) + && !GC_reclaim_all(stop_func, FALSE)) { + /* Aborted. So far everything is still consistent. */ + /* TODO: Notify GC_EVENT_ABANDON */ + return FALSE; + } + GC_invalidate_mark_state(); /* Flush mark stack. */ + GC_clear_marks(); +# ifdef SAVE_CALL_CHAIN + GC_save_callers(GC_last_stack); +# endif + GC_is_full_gc = TRUE; + if (!GC_stopped_mark(stop_func)) { + if (!GC_incremental) { + /* We're partially done and have no way to complete or use */ + /* current work. Reestablish invariants as cheaply as */ + /* possible. */ + GC_invalidate_mark_state(); + GC_unpromote_black_lists(); + } /* else we claim the world is already still consistent. We'll */ + /* finish incrementally. */ + /* TODO: Notify GC_EVENT_ABANDON */ + return FALSE; + } + GC_finish_collection(); +# ifndef NO_CLOCK + if (start_time_valid) { + CLOCK_TYPE current_time; + unsigned long time_diff, ns_frac_diff; + + GET_TIME(current_time); + time_diff = MS_TIME_DIFF(current_time, start_time); + ns_frac_diff = NS_FRAC_TIME_DIFF(current_time, start_time); + if (measure_performance) { + full_gc_total_time += time_diff; /* may wrap */ + full_gc_total_ns_frac += (unsigned32)ns_frac_diff; + if (full_gc_total_ns_frac >= (unsigned32)1000000UL) { + /* Overflow of the nanoseconds part. */ + full_gc_total_ns_frac -= (unsigned32)1000000UL; + full_gc_total_time++; + } + } + if (GC_print_stats) + GC_log_printf("Complete collection took %lu ms %lu ns\n", + time_diff, ns_frac_diff); + } +# endif + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_END); + return TRUE; +} + +/* The number of extra calls to GC_mark_some that we have made. */ +STATIC int GC_deficit = 0; + +/* The default value of GC_rate. */ +#ifndef GC_RATE +# define GC_RATE 10 +#endif + +/* When GC_collect_a_little_inner() performs n units of GC work, a unit */ +/* is intended to touch roughly GC_rate pages. (But, every once in */ +/* a while, we do more than that.) This needs to be a fairly large */ +/* number with our current incremental GC strategy, since otherwise we */ +/* allocate too much during GC, and the cleanup gets expensive. */ +STATIC int GC_rate = GC_RATE; + +GC_API void GC_CALL GC_set_rate(int value) +{ + GC_ASSERT(value > 0); + GC_rate = value; +} + +GC_API int GC_CALL GC_get_rate(void) +{ + return GC_rate; +} + +/* The default maximum number of prior attempts at world stop marking. */ +#ifndef MAX_PRIOR_ATTEMPTS +# define MAX_PRIOR_ATTEMPTS 3 +#endif + +/* The maximum number of prior attempts at world stop marking. */ +/* A value of 1 means that we finish the second time, no matter how */ +/* long it takes. Does not count the initial root scan for a full GC. */ +static int max_prior_attempts = MAX_PRIOR_ATTEMPTS; + +GC_API void GC_CALL GC_set_max_prior_attempts(int value) +{ + GC_ASSERT(value >= 0); + max_prior_attempts = value; +} + +GC_API int GC_CALL GC_get_max_prior_attempts(void) +{ + return max_prior_attempts; +} + +GC_INNER void GC_collect_a_little_inner(int n) +{ + IF_CANCEL(int cancel_state;) + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_is_initialized); + DISABLE_CANCEL(cancel_state); + if (GC_incremental && GC_collection_in_progress()) { + int i; + int max_deficit = GC_rate * n; + +# ifdef PARALLEL_MARK + if (GC_time_limit != GC_TIME_UNLIMITED) + GC_parallel_mark_disabled = TRUE; +# endif + for (i = GC_deficit; i < max_deficit; i++) { + if (GC_mark_some(NULL)) + break; + } +# ifdef PARALLEL_MARK + GC_parallel_mark_disabled = FALSE; +# endif + + if (i < max_deficit && !GC_dont_gc) { + GC_ASSERT(!GC_collection_in_progress()); + /* Need to follow up with a full collection. */ +# ifdef SAVE_CALL_CHAIN + GC_save_callers(GC_last_stack); +# endif +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_wait_for_reclaim(); +# endif +# ifndef NO_CLOCK + if (GC_time_limit != GC_TIME_UNLIMITED + && GC_n_attempts < max_prior_attempts) + GET_TIME(GC_start_time); +# endif + if (GC_stopped_mark(GC_n_attempts < max_prior_attempts ? + GC_timeout_stop_func : GC_never_stop_func)) { + GC_finish_collection(); + } else { + GC_n_attempts++; + } + } + if (GC_deficit > 0) { + GC_deficit -= max_deficit; + if (GC_deficit < 0) + GC_deficit = 0; + } + } else if (!GC_dont_gc) { + GC_maybe_gc(); + } + RESTORE_CANCEL(cancel_state); +} + +GC_INNER void (*GC_check_heap)(void) = 0; +GC_INNER void (*GC_print_all_smashed)(void) = 0; + +GC_API int GC_CALL GC_collect_a_little(void) +{ + int result; + + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + LOCK(); + ENTER_GC(); + /* Note: if the collection is in progress, this may do marking (not */ + /* stopping the world) even in case of disabled GC. */ + GC_collect_a_little_inner(1); + EXIT_GC(); + result = (int)GC_collection_in_progress(); + UNLOCK(); + if (!result && GC_debugging_started) GC_print_all_smashed(); + return result; +} + +#ifdef THREADS + GC_API void GC_CALL GC_stop_world_external(void) + { + GC_ASSERT(GC_is_initialized); + LOCK(); +# ifdef THREAD_LOCAL_ALLOC + GC_ASSERT(!GC_world_stopped); +# endif + STOP_WORLD(); +# ifdef THREAD_LOCAL_ALLOC + GC_world_stopped = TRUE; +# endif + } + + GC_API void GC_CALL GC_start_world_external(void) + { +# ifdef THREAD_LOCAL_ALLOC + GC_ASSERT(GC_world_stopped); + GC_world_stopped = FALSE; +# else + GC_ASSERT(GC_is_initialized); +# endif + START_WORLD(); + UNLOCK(); + } +#endif /* THREADS */ + +#ifndef NO_CLOCK + /* Variables for world-stop average delay time statistic computation. */ + /* "divisor" is incremented every world-stop and halved when reached */ + /* its maximum (or upon "total_time" overflow). */ + static unsigned world_stopped_total_time = 0; + static unsigned world_stopped_total_divisor = 0; +# ifndef MAX_TOTAL_TIME_DIVISOR + /* We shall not use big values here (so "outdated" delay time */ + /* values would have less impact on "average" delay time value than */ + /* newer ones). */ +# define MAX_TOTAL_TIME_DIVISOR 1000 +# endif +#endif /* !NO_CLOCK */ + +#ifdef USE_MUNMAP +# ifndef MUNMAP_THRESHOLD +# define MUNMAP_THRESHOLD 7 +# endif + GC_INNER unsigned GC_unmap_threshold = MUNMAP_THRESHOLD; + +# define IF_USE_MUNMAP(x) x +# define COMMA_IF_USE_MUNMAP(x) /* comma */, x +#else +# define IF_USE_MUNMAP(x) /* empty */ +# define COMMA_IF_USE_MUNMAP(x) /* empty */ +#endif + +/* We stop the world and mark from all roots. If stop_func() ever */ +/* returns TRUE, we may fail and return FALSE. Increment GC_gc_no if */ +/* we succeed. */ +STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func) +{ + int abandoned_at; + ptr_t cold_gc_frame = GC_approx_sp(); +# ifndef NO_CLOCK + CLOCK_TYPE start_time = CLOCK_TYPE_INITIALIZER; + GC_bool start_time_valid = FALSE; +# endif + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_is_initialized); +# if !defined(REDIRECT_MALLOC) && defined(USE_WINALLOC) + GC_add_current_malloc_heap(); +# endif +# if defined(REGISTER_LIBRARIES_EARLY) + GC_cond_register_dynamic_libraries(); +# endif + +# if !defined(GC_NO_FINALIZATION) && !defined(GC_TOGGLE_REFS_NOT_NEEDED) + GC_process_togglerefs(); +# endif + + /* Output blank line for convenience here. */ + GC_COND_LOG_PRINTF( + "\n--> Marking for collection #%lu after %lu allocated bytes\n", + (unsigned long)GC_gc_no + 1, (unsigned long)GC_bytes_allocd); +# ifndef NO_CLOCK + if (GC_PRINT_STATS_FLAG || measure_performance) { + GET_TIME(start_time); + start_time_valid = TRUE; + } +# endif +# ifdef THREADS + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_PRE_STOP_WORLD); +# endif + STOP_WORLD(); +# ifdef THREADS + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_POST_STOP_WORLD); +# endif +# ifdef THREAD_LOCAL_ALLOC + GC_world_stopped = TRUE; +# endif + +# ifdef MAKE_BACK_GRAPH + if (GC_print_back_height) { + GC_build_back_graph(); + } +# endif + + /* Notify about marking from all roots. */ + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_MARK_START); + + /* Minimize junk left in my registers and on the stack. */ + GC_clear_a_few_frames(); + GC_noop6(0,0,0,0,0,0); + + GC_initiate_gc(); +# ifdef PARALLEL_MARK + if (stop_func != GC_never_stop_func) + GC_parallel_mark_disabled = TRUE; +# endif + for (abandoned_at = 0; !(*stop_func)(); abandoned_at++) { + if (GC_mark_some(cold_gc_frame)) { +# ifdef PARALLEL_MARK + if (GC_parallel && GC_parallel_mark_disabled) { + GC_COND_LOG_PRINTF("Stopped marking done after %d iterations" + " with disabled parallel marker\n", + abandoned_at); + } +# endif + abandoned_at = -1; + break; + } + } +# ifdef PARALLEL_MARK + GC_parallel_mark_disabled = FALSE; +# endif + + if (abandoned_at >= 0) { + GC_deficit = abandoned_at; /* Give the mutator a chance. */ + /* TODO: Notify GC_EVENT_MARK_ABANDON */ + } else { + GC_gc_no++; + /* Check all debugged objects for consistency. */ + if (GC_debugging_started) { + (*GC_check_heap)(); + } + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_MARK_END); + } + +# ifdef THREADS + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_PRE_START_WORLD); +# endif +# ifdef THREAD_LOCAL_ALLOC + GC_world_stopped = FALSE; +# endif + START_WORLD(); +# ifdef THREADS + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_POST_START_WORLD); +# endif + +# ifndef NO_CLOCK + if (start_time_valid) { + CLOCK_TYPE current_time; + unsigned long time_diff, ns_frac_diff; + + /* TODO: Avoid code duplication from GC_try_to_collect_inner */ + GET_TIME(current_time); + time_diff = MS_TIME_DIFF(current_time, start_time); + ns_frac_diff = NS_FRAC_TIME_DIFF(current_time, start_time); + if (measure_performance) { + stopped_mark_total_time += time_diff; /* may wrap */ + stopped_mark_total_ns_frac += (unsigned32)ns_frac_diff; + if (stopped_mark_total_ns_frac >= (unsigned32)1000000UL) { + stopped_mark_total_ns_frac -= (unsigned32)1000000UL; + stopped_mark_total_time++; + } + } + + if (GC_PRINT_STATS_FLAG) { + unsigned total_time = world_stopped_total_time; + unsigned divisor = world_stopped_total_divisor; + + /* Compute new world-stop delay total time. */ + if (total_time > (((unsigned)-1) >> 1) + || divisor >= MAX_TOTAL_TIME_DIVISOR) { + /* Halve values if overflow occurs. */ + total_time >>= 1; + divisor >>= 1; + } + total_time += time_diff < (((unsigned)-1) >> 1) ? + (unsigned)time_diff : ((unsigned)-1) >> 1; + /* Update old world_stopped_total_time and its divisor. */ + world_stopped_total_time = total_time; + world_stopped_total_divisor = ++divisor; + if (abandoned_at < 0) { + GC_ASSERT(divisor != 0); + GC_log_printf("World-stopped marking took %lu ms %lu ns" + " (%u ms in average)\n", time_diff, ns_frac_diff, + total_time / divisor); + } + } + } +# endif + + if (abandoned_at >= 0) { + GC_COND_LOG_PRINTF("Abandoned stopped marking after %d iterations\n", + abandoned_at); + return FALSE; + } + return TRUE; +} + +/* Set all mark bits for the free list whose first entry is q */ +GC_INNER void GC_set_fl_marks(ptr_t q) +{ + if (q /* != NULL */) { /* CPPCHECK */ + struct hblk *h = HBLKPTR(q); + struct hblk *last_h = h; + hdr *hhdr = HDR(h); + IF_PER_OBJ(word sz = hhdr->hb_sz;) + + for (;;) { + word bit_no = MARK_BIT_NO((ptr_t)q - (ptr_t)h, sz); + + if (!mark_bit_from_hdr(hhdr, bit_no)) { + set_mark_bit_from_hdr(hhdr, bit_no); + INCR_MARKS(hhdr); + } + + q = (ptr_t)obj_link(q); + if (q == NULL) + break; + + h = HBLKPTR(q); + if (h != last_h) { + last_h = h; + hhdr = HDR(h); + IF_PER_OBJ(sz = hhdr->hb_sz;) + } + } + } +} + +#if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC) + /* Check that all mark bits for the free list whose first entry is */ + /* (*pfreelist) are set. Check skipped if points to a special value. */ + void GC_check_fl_marks(void **pfreelist) + { + /* TODO: There is a data race with GC_FAST_MALLOC_GRANS (which does */ + /* not do atomic updates to the free-list). The race seems to be */ + /* harmless, and for now we just skip this check in case of TSan. */ +# if defined(AO_HAVE_load_acquire_read) && !defined(THREAD_SANITIZER) + AO_t *list = (AO_t *)AO_load_acquire_read((AO_t *)pfreelist); + /* Atomic operations are used because the world is running. */ + AO_t *prev; + AO_t *p; + + if ((word)list <= HBLKSIZE) return; + + prev = (AO_t *)pfreelist; + for (p = list; p != NULL;) { + AO_t *next; + + if (!GC_is_marked(p)) { + ABORT_ARG2("Unmarked local free list entry", + ": object %p on list %p", (void *)p, (void *)list); + } + + /* While traversing the free-list, it re-reads the pointer to */ + /* the current node before accepting its next pointer and */ + /* bails out if the latter has changed. That way, it won't */ + /* try to follow the pointer which might be been modified */ + /* after the object was returned to the client. It might */ + /* perform the mark-check on the just allocated object but */ + /* that should be harmless. */ + next = (AO_t *)AO_load_acquire_read(p); + if (AO_load(prev) != (AO_t)p) + break; + prev = p; + p = next; + } +# else + /* FIXME: Not implemented (just skipped). */ + (void)pfreelist; +# endif + } +#endif /* GC_ASSERTIONS && THREAD_LOCAL_ALLOC */ + +/* Clear all mark bits for the free list whose first entry is q */ +/* Decrement GC_bytes_found by number of bytes on free list. */ +STATIC void GC_clear_fl_marks(ptr_t q) +{ + struct hblk *h = HBLKPTR(q); + struct hblk *last_h = h; + hdr *hhdr = HDR(h); + word sz = hhdr->hb_sz; /* Normally set only once. */ + + for (;;) { + word bit_no = MARK_BIT_NO((ptr_t)q - (ptr_t)h, sz); + + if (mark_bit_from_hdr(hhdr, bit_no)) { + size_t n_marks = hhdr -> hb_n_marks; + + GC_ASSERT(n_marks != 0); + clear_mark_bit_from_hdr(hhdr, bit_no); + n_marks--; +# ifdef PARALLEL_MARK + /* Appr. count, don't decrement to zero! */ + if (0 != n_marks || !GC_parallel) { + hhdr -> hb_n_marks = n_marks; + } +# else + hhdr -> hb_n_marks = n_marks; +# endif + } + GC_bytes_found -= (signed_word)sz; + + q = (ptr_t)obj_link(q); + if (q == NULL) + break; + + h = HBLKPTR(q); + if (h != last_h) { + last_h = h; + hhdr = HDR(h); + sz = hhdr->hb_sz; + } + } +} + +#if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC) + void GC_check_tls(void); +#endif + +GC_on_heap_resize_proc GC_on_heap_resize = 0; + +/* Used for logging only. */ +GC_INLINE int GC_compute_heap_usage_percent(void) +{ + word used = GC_composite_in_use + GC_atomic_in_use + GC_bytes_allocd; + word heap_sz = GC_heapsize - GC_unmapped_bytes; +# if defined(CPPCHECK) + word limit = (GC_WORD_MAX >> 1) / 50; /* to avoid a false positive */ +# else + const word limit = GC_WORD_MAX / 100; +# endif + + return used >= heap_sz ? 0 : used < limit ? + (int)((used * 100) / heap_sz) : (int)(used / (heap_sz / 100)); +} + +#define GC_DBGLOG_PRINT_HEAP_IN_USE() \ + GC_DBGLOG_PRINTF("In-use heap: %d%% (%lu KiB pointers + %lu KiB other)\n", \ + GC_compute_heap_usage_percent(), \ + TO_KiB_UL(GC_composite_in_use), \ + TO_KiB_UL(GC_atomic_in_use + GC_bytes_allocd)) + +/* Finish up a collection. Assumes mark bits are consistent, but the */ +/* world is otherwise running. */ +STATIC void GC_finish_collection(void) +{ +# ifndef NO_CLOCK + CLOCK_TYPE start_time = CLOCK_TYPE_INITIALIZER; + CLOCK_TYPE finalize_time = CLOCK_TYPE_INITIALIZER; +# endif + + GC_ASSERT(I_HOLD_LOCK()); +# if defined(GC_ASSERTIONS) \ + && defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL) + /* Check that we marked some of our own data. */ + /* TODO: Add more checks. */ + GC_check_tls(); +# endif + +# ifndef NO_CLOCK + if (GC_print_stats) + GET_TIME(start_time); +# endif + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_RECLAIM_START); + +# ifndef GC_GET_HEAP_USAGE_NOT_NEEDED + if (GC_bytes_found > 0) + GC_reclaimed_bytes_before_gc += (word)GC_bytes_found; +# endif + GC_bytes_found = 0; +# if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG) + if (GETENV("GC_PRINT_ADDRESS_MAP") != 0) { + GC_print_address_map(); + } +# endif + COND_DUMP; + if (GC_find_leak) { + /* Mark all objects on the free list. All objects should be */ + /* marked when we're done. */ + word size; /* current object size */ + unsigned kind; + ptr_t q; + + for (kind = 0; kind < GC_n_kinds; kind++) { + for (size = 1; size <= MAXOBJGRANULES; size++) { + q = (ptr_t)GC_obj_kinds[kind].ok_freelist[size]; + if (q != NULL) + GC_set_fl_marks(q); + } + } + GC_start_reclaim(TRUE); + /* The above just checks; it doesn't really reclaim anything. */ + } + +# ifndef GC_NO_FINALIZATION + GC_finalize(); +# endif +# ifndef NO_CLOCK + if (GC_print_stats) + GET_TIME(finalize_time); +# endif + + if (GC_print_back_height) { +# ifdef MAKE_BACK_GRAPH + GC_traverse_back_graph(); +# elif !defined(SMALL_CONFIG) + GC_err_printf("Back height not available: " + "Rebuild collector with -DMAKE_BACK_GRAPH\n"); +# endif + } + + /* Clear free list mark bits, in case they got accidentally marked */ + /* (or GC_find_leak is set and they were intentionally marked). */ + /* Also subtract memory remaining from GC_bytes_found count. */ + /* Note that composite objects on free list are cleared. */ + /* Thus accidentally marking a free list is not a problem; only */ + /* objects on the list itself will be marked, and that's fixed here. */ + { + word size; /* current object size */ + ptr_t q; /* pointer to current object */ + unsigned kind; + + for (kind = 0; kind < GC_n_kinds; kind++) { + for (size = 1; size <= MAXOBJGRANULES; size++) { + q = (ptr_t)GC_obj_kinds[kind].ok_freelist[size]; + if (q != NULL) + GC_clear_fl_marks(q); + } + } + } + + GC_VERBOSE_LOG_PRINTF("Bytes recovered before sweep - f.l. count = %ld\n", + (long)GC_bytes_found); + + /* Reconstruct free lists to contain everything not marked */ + GC_start_reclaim(FALSE); + +# ifdef USE_MUNMAP + if (GC_unmap_threshold > 0 /* unmapping enabled? */ + && EXPECT(GC_gc_no != 1, TRUE)) /* do not unmap during GC init */ + GC_unmap_old(GC_unmap_threshold); + + GC_ASSERT(GC_heapsize >= GC_unmapped_bytes); +# endif + GC_ASSERT(GC_our_mem_bytes >= GC_heapsize); + GC_DBGLOG_PRINTF("GC #%lu freed %ld bytes, heap %lu KiB (" + IF_USE_MUNMAP("+ %lu KiB unmapped ") + "+ %lu KiB internal)\n", + (unsigned long)GC_gc_no, (long)GC_bytes_found, + TO_KiB_UL(GC_heapsize - GC_unmapped_bytes) /*, */ + COMMA_IF_USE_MUNMAP(TO_KiB_UL(GC_unmapped_bytes)), + TO_KiB_UL(GC_our_mem_bytes - GC_heapsize + + sizeof(GC_arrays))); + GC_DBGLOG_PRINT_HEAP_IN_USE(); + if (GC_is_full_gc) { + GC_used_heap_size_after_full = GC_heapsize - GC_large_free_bytes; + GC_need_full_gc = FALSE; + } else { + GC_need_full_gc = GC_heapsize - GC_used_heap_size_after_full + > min_bytes_allocd() + GC_large_free_bytes; + } + + /* Reset or increment counters for next cycle */ + GC_n_attempts = 0; + GC_is_full_gc = FALSE; + GC_bytes_allocd_before_gc += GC_bytes_allocd; + GC_non_gc_bytes_at_gc = GC_non_gc_bytes; + GC_bytes_allocd = 0; + GC_bytes_dropped = 0; + GC_bytes_freed = 0; + GC_finalizer_bytes_freed = 0; + + if (GC_on_collection_event) + GC_on_collection_event(GC_EVENT_RECLAIM_END); +# ifndef NO_CLOCK + if (GC_print_stats) { + CLOCK_TYPE done_time; + + GET_TIME(done_time); +# if !defined(SMALL_CONFIG) && !defined(GC_NO_FINALIZATION) + /* A convenient place to output finalization statistics. */ + GC_print_finalization_stats(); +# endif + GC_log_printf("Finalize and initiate sweep took %lu ms %lu ns" + " + %lu ms %lu ns\n", + MS_TIME_DIFF(finalize_time, start_time), + NS_FRAC_TIME_DIFF(finalize_time, start_time), + MS_TIME_DIFF(done_time, finalize_time), + NS_FRAC_TIME_DIFF(done_time, finalize_time)); + } +# elif !defined(SMALL_CONFIG) && !defined(GC_NO_FINALIZATION) + if (GC_print_stats) + GC_print_finalization_stats(); +# endif +} + +STATIC word GC_heapsize_at_forced_unmap = 0; + /* accessed with the allocator lock held */ + +/* If stop_func == 0 then GC_default_stop_func is used instead. */ +STATIC GC_bool GC_try_to_collect_general(GC_stop_func stop_func, + GC_bool force_unmap) +{ + GC_bool result; + IF_USE_MUNMAP(unsigned old_unmap_threshold;) + IF_CANCEL(int cancel_state;) + + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + if (GC_debugging_started) GC_print_all_smashed(); + GC_INVOKE_FINALIZERS(); + LOCK(); + if (force_unmap) { + /* Record current heap size to make heap growth more conservative */ + /* afterwards (as if the heap is growing from zero size again). */ + GC_heapsize_at_forced_unmap = GC_heapsize; + } + DISABLE_CANCEL(cancel_state); +# ifdef USE_MUNMAP + old_unmap_threshold = GC_unmap_threshold; + if (force_unmap || + (GC_force_unmap_on_gcollect && old_unmap_threshold > 0)) + GC_unmap_threshold = 1; /* unmap as much as possible */ +# endif + ENTER_GC(); + /* Minimize junk left in my registers */ + GC_noop6(0,0,0,0,0,0); + result = GC_try_to_collect_inner(stop_func != 0 ? stop_func : + GC_default_stop_func); + EXIT_GC(); + IF_USE_MUNMAP(GC_unmap_threshold = old_unmap_threshold); /* restore */ + RESTORE_CANCEL(cancel_state); + UNLOCK(); + if (result) { + if (GC_debugging_started) GC_print_all_smashed(); + GC_INVOKE_FINALIZERS(); + } + return result; +} + +/* Externally callable routines to invoke full, stop-the-world collection. */ + +GC_API int GC_CALL GC_try_to_collect(GC_stop_func stop_func) +{ + GC_ASSERT(NONNULL_ARG_NOT_NULL(stop_func)); + return (int)GC_try_to_collect_general(stop_func, FALSE); +} + +GC_API void GC_CALL GC_gcollect(void) +{ + /* Zero is passed as stop_func to get GC_default_stop_func value */ + /* while holding the allocator lock (to prevent data race). */ + (void)GC_try_to_collect_general(0, FALSE); + if (get_have_errors()) + GC_print_all_errors(); +} + +GC_API void GC_CALL GC_gcollect_and_unmap(void) +{ + /* Collect and force memory unmapping to OS. */ + (void)GC_try_to_collect_general(GC_never_stop_func, TRUE); +} + +GC_INNER ptr_t GC_os_get_mem(size_t bytes) +{ + struct hblk *space = GET_MEM(bytes); /* HBLKSIZE-aligned */ + + GC_ASSERT(I_HOLD_LOCK()); + if (EXPECT(NULL == space, FALSE)) return NULL; +# ifdef USE_PROC_FOR_LIBRARIES + /* Add HBLKSIZE aligned, GET_MEM-generated block to GC_our_memory. */ + if (GC_n_memory >= MAX_HEAP_SECTS) + ABORT("Too many GC-allocated memory sections: Increase MAX_HEAP_SECTS"); + GC_our_memory[GC_n_memory].hs_start = (ptr_t)space; + GC_our_memory[GC_n_memory].hs_bytes = bytes; + GC_n_memory++; +# endif + GC_our_mem_bytes += bytes; + GC_VERBOSE_LOG_PRINTF("Got %lu bytes from OS\n", (unsigned long)bytes); + return (ptr_t)space; +} + +/* Use the chunk of memory starting at p of size bytes as part of the heap. */ +/* Assumes p is HBLKSIZE aligned, bytes argument is a multiple of HBLKSIZE. */ +STATIC void GC_add_to_heap(struct hblk *p, size_t bytes) +{ + hdr *hhdr; + word endp; + size_t old_capacity = 0; + void *old_heap_sects = NULL; +# ifdef GC_ASSERTIONS + unsigned i; +# endif + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT((word)p % HBLKSIZE == 0); + GC_ASSERT(bytes % HBLKSIZE == 0); + GC_ASSERT(bytes > 0); + GC_ASSERT(GC_all_nils != NULL); + + if (EXPECT(GC_n_heap_sects == GC_capacity_heap_sects, FALSE)) { + /* Allocate new GC_heap_sects with sufficient capacity. */ +# ifndef INITIAL_HEAP_SECTS +# define INITIAL_HEAP_SECTS 32 +# endif + size_t new_capacity = GC_n_heap_sects > 0 ? + (size_t)GC_n_heap_sects * 2 : INITIAL_HEAP_SECTS; + void *new_heap_sects = + GC_scratch_alloc(new_capacity * sizeof(struct HeapSect)); + + if (NULL == new_heap_sects) { + /* Retry with smaller yet sufficient capacity. */ + new_capacity = (size_t)GC_n_heap_sects + INITIAL_HEAP_SECTS; + new_heap_sects = + GC_scratch_alloc(new_capacity * sizeof(struct HeapSect)); + if (NULL == new_heap_sects) + ABORT("Insufficient memory for heap sections"); + } + old_capacity = GC_capacity_heap_sects; + old_heap_sects = GC_heap_sects; + /* Transfer GC_heap_sects contents to the newly allocated array. */ + if (GC_n_heap_sects > 0) + BCOPY(old_heap_sects, new_heap_sects, + GC_n_heap_sects * sizeof(struct HeapSect)); + GC_capacity_heap_sects = new_capacity; + GC_heap_sects = (struct HeapSect *)new_heap_sects; + GC_COND_LOG_PRINTF("Grew heap sections array to %lu elements\n", + (unsigned long)new_capacity); + } + + while (EXPECT((word)p <= HBLKSIZE, FALSE)) { + /* Can't handle memory near address zero. */ + ++p; + bytes -= HBLKSIZE; + if (0 == bytes) return; + } + endp = (word)p + bytes; + if (EXPECT(endp <= (word)p, FALSE)) { + /* Address wrapped. */ + bytes -= HBLKSIZE; + if (0 == bytes) return; + endp -= HBLKSIZE; + } + hhdr = GC_install_header(p); + if (EXPECT(NULL == hhdr, FALSE)) { + /* This is extremely unlikely. Can't add it. This will */ + /* almost certainly result in a 0 return from the allocator, */ + /* which is entirely appropriate. */ + return; + } + GC_ASSERT(endp > (word)p && endp == (word)p + bytes); +# ifdef GC_ASSERTIONS + /* Ensure no intersection between sections. */ + for (i = 0; i < GC_n_heap_sects; i++) { + word hs_start = (word)GC_heap_sects[i].hs_start; + word hs_end = hs_start + GC_heap_sects[i].hs_bytes; + + GC_ASSERT(!((hs_start <= (word)p && (word)p < hs_end) + || (hs_start < endp && endp <= hs_end) + || ((word)p < hs_start && hs_end < endp))); + } +# endif + GC_heap_sects[GC_n_heap_sects].hs_start = (ptr_t)p; + GC_heap_sects[GC_n_heap_sects].hs_bytes = bytes; + GC_n_heap_sects++; + hhdr -> hb_sz = bytes; + hhdr -> hb_flags = 0; + GC_freehblk(p); + GC_heapsize += bytes; + + if ((word)p <= (word)GC_least_plausible_heap_addr + || EXPECT(NULL == GC_least_plausible_heap_addr, FALSE)) { + GC_least_plausible_heap_addr = (void *)((ptr_t)p - sizeof(word)); + /* Making it a little smaller than necessary prevents */ + /* us from getting a false hit from the variable */ + /* itself. There's some unintentional reflection */ + /* here. */ + } + if (endp > (word)GC_greatest_plausible_heap_addr) { + GC_greatest_plausible_heap_addr = (void *)endp; + } +# ifdef SET_REAL_HEAP_BOUNDS + if ((word)p < GC_least_real_heap_addr + || EXPECT(0 == GC_least_real_heap_addr, FALSE)) + GC_least_real_heap_addr = (word)p - sizeof(word); + if (endp > GC_greatest_real_heap_addr) { +# ifdef INCLUDE_LINUX_THREAD_DESCR + /* Avoid heap intersection with the static data roots. */ + GC_exclude_static_roots_inner((void *)p, (void *)endp); +# endif + GC_greatest_real_heap_addr = endp; + } +# endif + if (EXPECT(old_capacity > 0, FALSE)) { +# ifndef GWW_VDB + /* Recycling may call GC_add_to_heap() again but should not */ + /* cause resizing of GC_heap_sects. */ + GC_scratch_recycle_no_gww(old_heap_sects, + old_capacity * sizeof(struct HeapSect)); +# else + /* TODO: implement GWW-aware recycling as in alloc_mark_stack */ + GC_noop1((word)old_heap_sects); +# endif + } +} + +#if !defined(NO_DEBUGGING) + void GC_print_heap_sects(void) + { + unsigned i; + + GC_printf("Total heap size: %lu" IF_USE_MUNMAP(" (%lu unmapped)") "\n", + (unsigned long)GC_heapsize /*, */ + COMMA_IF_USE_MUNMAP((unsigned long)GC_unmapped_bytes)); + + for (i = 0; i < GC_n_heap_sects; i++) { + ptr_t start = GC_heap_sects[i].hs_start; + size_t len = GC_heap_sects[i].hs_bytes; + struct hblk *h; + unsigned nbl = 0; + + for (h = (struct hblk *)start; (word)h < (word)(start + len); h++) { + if (GC_is_black_listed(h, HBLKSIZE)) nbl++; + } + GC_printf("Section %d from %p to %p %u/%lu blacklisted\n", + i, (void *)start, (void *)&start[len], + nbl, (unsigned long)divHBLKSZ(len)); + } + } +#endif + +void * GC_least_plausible_heap_addr = (void *)GC_WORD_MAX; +void * GC_greatest_plausible_heap_addr = 0; + +STATIC word GC_max_heapsize = 0; + +GC_API void GC_CALL GC_set_max_heap_size(GC_word n) +{ + GC_max_heapsize = n; +} + +word GC_max_retries = 0; + +GC_INNER void GC_scratch_recycle_inner(void *ptr, size_t bytes) +{ + size_t page_offset; + size_t displ = 0; + size_t recycled_bytes; + + GC_ASSERT(I_HOLD_LOCK()); + if (NULL == ptr) return; + + GC_ASSERT(bytes != 0); + GC_ASSERT(GC_page_size != 0); + /* TODO: Assert correct memory flags if GWW_VDB */ + page_offset = (word)ptr & (GC_page_size - 1); + if (page_offset != 0) + displ = GC_page_size - page_offset; + recycled_bytes = bytes > displ ? (bytes - displ) & ~(GC_page_size - 1) : 0; + GC_COND_LOG_PRINTF("Recycle %lu/%lu scratch-allocated bytes at %p\n", + (unsigned long)recycled_bytes, (unsigned long)bytes, ptr); + if (recycled_bytes > 0) + GC_add_to_heap((struct hblk *)((word)ptr + displ), recycled_bytes); +} + +/* This explicitly increases the size of the heap. It is used */ +/* internally, but may also be invoked from GC_expand_hp by the user. */ +/* The argument is in units of HBLKSIZE (zero is treated as 1). */ +/* Returns FALSE on failure. */ +GC_INNER GC_bool GC_expand_hp_inner(word n) +{ + size_t bytes; + struct hblk * space; + word expansion_slop; /* Number of bytes by which we expect */ + /* the heap to expand soon. */ + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_page_size != 0); + if (0 == n) n = 1; + bytes = ROUNDUP_PAGESIZE((size_t)n * HBLKSIZE); + GC_DBGLOG_PRINT_HEAP_IN_USE(); + if (GC_max_heapsize != 0 + && (GC_max_heapsize < (word)bytes + || GC_heapsize > GC_max_heapsize - (word)bytes)) { + /* Exceeded self-imposed limit */ + return FALSE; + } + space = (struct hblk *)GC_os_get_mem(bytes); + if (EXPECT(NULL == space, FALSE)) { + WARN("Failed to expand heap by %" WARN_PRIuPTR " KiB\n", bytes >> 10); + return FALSE; + } + GC_last_heap_growth_gc_no = GC_gc_no; + GC_INFOLOG_PRINTF("Grow heap to %lu KiB after %lu bytes allocated\n", + TO_KiB_UL(GC_heapsize + bytes), + (unsigned long)GC_bytes_allocd); + + /* Adjust heap limits generously for blacklisting to work better. */ + /* GC_add_to_heap performs minimal adjustment needed for */ + /* correctness. */ + expansion_slop = min_bytes_allocd() + 4 * MAXHINCR * HBLKSIZE; + if ((GC_last_heap_addr == 0 && !((word)space & SIGNB)) + || (GC_last_heap_addr != 0 + && (word)GC_last_heap_addr < (word)space)) { + /* Assume the heap is growing up. */ + word new_limit = (word)space + (word)bytes + expansion_slop; + if (new_limit > (word)space + && (word)GC_greatest_plausible_heap_addr < new_limit) + GC_greatest_plausible_heap_addr = (void *)new_limit; + } else { + /* Heap is growing down. */ + word new_limit = (word)space - expansion_slop - sizeof(word); + if (new_limit < (word)space + && (word)GC_least_plausible_heap_addr > new_limit) + GC_least_plausible_heap_addr = (void *)new_limit; + } + GC_last_heap_addr = (ptr_t)space; + + GC_add_to_heap(space, bytes); + if (GC_on_heap_resize) + (*GC_on_heap_resize)(GC_heapsize); + + return TRUE; +} + +/* Really returns a bool, but it's externally visible, so that's clumsy. */ +GC_API int GC_CALL GC_expand_hp(size_t bytes) +{ + word n_blocks = OBJ_SZ_TO_BLOCKS_CHECKED(bytes); + word old_heapsize; + GC_bool result; + + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + LOCK(); + old_heapsize = GC_heapsize; + result = GC_expand_hp_inner(n_blocks); + if (result) { + GC_requested_heapsize += bytes; + if (GC_dont_gc) { + /* Do not call WARN if the heap growth is intentional. */ + GC_ASSERT(GC_heapsize >= old_heapsize); + GC_heapsize_on_gc_disable += GC_heapsize - old_heapsize; + } + } + UNLOCK(); + return (int)result; +} + +GC_INNER unsigned GC_fail_count = 0; + /* How many consecutive GC/expansion failures? */ + /* Reset by GC_allochblk. */ + +/* The minimum value of the ratio of allocated bytes since the latest */ +/* GC to the amount of finalizers created since that GC which triggers */ +/* the collection instead heap expansion. Has no effect in the */ +/* incremental mode. */ +#if defined(GC_ALLOCD_BYTES_PER_FINALIZER) && !defined(CPPCHECK) + STATIC word GC_allocd_bytes_per_finalizer = GC_ALLOCD_BYTES_PER_FINALIZER; +#else + STATIC word GC_allocd_bytes_per_finalizer = 10000; +#endif + +GC_API void GC_CALL GC_set_allocd_bytes_per_finalizer(GC_word value) +{ + GC_allocd_bytes_per_finalizer = value; +} + +GC_API GC_word GC_CALL GC_get_allocd_bytes_per_finalizer(void) +{ + return GC_allocd_bytes_per_finalizer; +} + +static word last_fo_entries = 0; +static word last_bytes_finalized = 0; + +/* Collect or expand heap in an attempt make the indicated number of */ +/* free blocks available. Should be called until the blocks are */ +/* available (setting retry value to TRUE unless this is the first call */ +/* in a loop) or until it fails by returning FALSE. The flags argument */ +/* should be IGNORE_OFF_PAGE or 0. */ +GC_INNER GC_bool GC_collect_or_expand(word needed_blocks, + unsigned flags, + GC_bool retry) +{ + GC_bool gc_not_stopped = TRUE; + word blocks_to_get; + IF_CANCEL(int cancel_state;) + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_is_initialized); + DISABLE_CANCEL(cancel_state); + if (!GC_incremental && !GC_dont_gc && + ((GC_dont_expand && GC_bytes_allocd > 0) + || (GC_fo_entries > last_fo_entries + && (last_bytes_finalized | GC_bytes_finalized) != 0 + && (GC_fo_entries - last_fo_entries) + * GC_allocd_bytes_per_finalizer > GC_bytes_allocd) + || GC_should_collect())) { + /* Try to do a full collection using 'default' stop_func (unless */ + /* nothing has been allocated since the latest collection or heap */ + /* expansion is disabled). */ + gc_not_stopped = GC_try_to_collect_inner( + GC_bytes_allocd > 0 && (!GC_dont_expand || !retry) ? + GC_default_stop_func : GC_never_stop_func); + if (gc_not_stopped == TRUE || !retry) { + /* Either the collection hasn't been aborted or this is the */ + /* first attempt (in a loop). */ + last_fo_entries = GC_fo_entries; + last_bytes_finalized = GC_bytes_finalized; + RESTORE_CANCEL(cancel_state); + return TRUE; + } + } + + blocks_to_get = (GC_heapsize - GC_heapsize_at_forced_unmap) + / (HBLKSIZE * GC_free_space_divisor) + + needed_blocks; + if (blocks_to_get > MAXHINCR) { + word slop; + + /* Get the minimum required to make it likely that we can satisfy */ + /* the current request in the presence of black-listing. */ + /* This will probably be more than MAXHINCR. */ + if ((flags & IGNORE_OFF_PAGE) != 0) { + slop = 4; + } else { + slop = 2 * divHBLKSZ(BL_LIMIT); + if (slop > needed_blocks) slop = needed_blocks; + } + if (needed_blocks + slop > MAXHINCR) { + blocks_to_get = needed_blocks + slop; + } else { + blocks_to_get = MAXHINCR; + } + if (blocks_to_get > divHBLKSZ(GC_WORD_MAX)) + blocks_to_get = divHBLKSZ(GC_WORD_MAX); + } else if (blocks_to_get < MINHINCR) { + blocks_to_get = MINHINCR; + } + + if (GC_max_heapsize > GC_heapsize) { + word max_get_blocks = divHBLKSZ(GC_max_heapsize - GC_heapsize); + if (blocks_to_get > max_get_blocks) + blocks_to_get = max_get_blocks > needed_blocks + ? max_get_blocks : needed_blocks; + } + +# ifdef USE_MUNMAP + if (GC_unmap_threshold > 1) { + /* Return as much memory to the OS as possible before */ + /* trying to get memory from it. */ + GC_unmap_old(0); + } +# endif + if (!GC_expand_hp_inner(blocks_to_get) + && (blocks_to_get == needed_blocks + || !GC_expand_hp_inner(needed_blocks))) { + if (gc_not_stopped == FALSE) { + /* Don't increment GC_fail_count here (and no warning). */ + GC_gcollect_inner(); + GC_ASSERT(GC_bytes_allocd == 0); + } else if (GC_fail_count++ < GC_max_retries) { + WARN("Out of Memory! Trying to continue...\n", 0); + GC_gcollect_inner(); + } else { +# if !defined(AMIGA) || !defined(GC_AMIGA_FASTALLOC) +# ifdef USE_MUNMAP + GC_ASSERT(GC_heapsize >= GC_unmapped_bytes); +# endif +# if !defined(SMALL_CONFIG) && (CPP_WORDSZ >= 32) +# define MAX_HEAPSIZE_WARNED_IN_BYTES (5 << 20) /* 5 MB */ + if (GC_heapsize > (word)MAX_HEAPSIZE_WARNED_IN_BYTES) { + WARN("Out of Memory! Heap size: %" WARN_PRIuPTR " MiB." + " Returning NULL!\n", + (GC_heapsize - GC_unmapped_bytes) >> 20); + } else +# endif + /* else */ { + WARN("Out of Memory! Heap size: %" WARN_PRIuPTR " bytes." + " Returning NULL!\n", GC_heapsize - GC_unmapped_bytes); + } +# endif + RESTORE_CANCEL(cancel_state); + return FALSE; + } + } else if (GC_fail_count) { + GC_COND_LOG_PRINTF("Memory available again...\n"); + } + RESTORE_CANCEL(cancel_state); + return TRUE; +} + +/* + * Make sure the object free list for size gran (in granules) is not empty. + * Return a pointer to the first object on the free list. + * The object MUST BE REMOVED FROM THE FREE LIST BY THE CALLER. + */ +GC_INNER ptr_t GC_allocobj(size_t gran, int kind) +{ + void ** flh = &GC_obj_kinds[kind].ok_freelist[gran]; + GC_bool tried_minor = FALSE; + GC_bool retry = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_is_initialized); + if (0 == gran) return NULL; + + while (NULL == *flh) { + ENTER_GC(); +# ifndef GC_DISABLE_INCREMENTAL + if (GC_incremental && GC_time_limit != GC_TIME_UNLIMITED + && !GC_dont_gc) { + /* True incremental mode, not just generational. */ + /* Do our share of marking work. */ + GC_collect_a_little_inner(1); + } +# endif + /* Sweep blocks for objects of this size */ + GC_ASSERT(!GC_is_full_gc + || NULL == GC_obj_kinds[kind].ok_reclaim_list + || NULL == GC_obj_kinds[kind].ok_reclaim_list[gran]); + GC_continue_reclaim(gran, kind); + EXIT_GC(); +# if defined(CPPCHECK) + GC_noop1((word)&flh); +# endif + if (NULL == *flh) { + GC_new_hblk(gran, kind); +# if defined(CPPCHECK) + GC_noop1((word)&flh); +# endif + if (NULL == *flh) { + ENTER_GC(); + if (GC_incremental && GC_time_limit == GC_TIME_UNLIMITED + && !tried_minor && !GC_dont_gc) { + GC_collect_a_little_inner(1); + tried_minor = TRUE; + } else { + if (!GC_collect_or_expand(1, 0 /* flags */, retry)) { + EXIT_GC(); + return NULL; + } + retry = TRUE; + } + EXIT_GC(); + } + } + } + /* Successful allocation; reset failure count. */ + GC_fail_count = 0; + + return (ptr_t)(*flh); +} + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright (c) 1997 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2007 Free Software Foundation, Inc. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +#ifndef MSWINCE +# include +#endif +#include + +#ifndef SHORT_DBG_HDRS + /* Check whether object with base pointer p has debugging info. */ + /* p is assumed to point to a legitimate object in our part */ + /* of the heap. */ + /* This excludes the check as to whether the back pointer is */ + /* odd, which is added by the GC_HAS_DEBUG_INFO macro. */ + /* Note that if DBG_HDRS_ALL is set, uncollectible objects */ + /* on free lists may not have debug information set. Thus it's */ + /* not always safe to return TRUE (1), even if the client does */ + /* its part. Return -1 if the object with debug info has been */ + /* marked as deallocated. */ + GC_INNER int GC_has_other_debug_info(ptr_t p) + { + ptr_t body = (ptr_t)((oh *)p + 1); + word sz = GC_size(p); + + if (HBLKPTR(p) != HBLKPTR((ptr_t)body) + || sz < DEBUG_BYTES + EXTRA_BYTES) { + return 0; + } + if (((oh *)p) -> oh_sf != (START_FLAG ^ (word)body) + && ((word *)p)[BYTES_TO_WORDS(sz)-1] != (END_FLAG ^ (word)body)) { + return 0; + } + if (((oh *)p)->oh_sz == sz) { + /* Object may have had debug info, but has been deallocated */ + return -1; + } + return 1; + } +#endif /* !SHORT_DBG_HDRS */ + +#ifdef KEEP_BACK_PTRS + + /* Use a custom trivial random() implementation as the standard */ + /* one might lead to crashes (if used from a multi-threaded code) */ + /* or to a compiler warning about the deterministic result. */ + static int GC_rand(void) + { + static GC_RAND_STATE_T seed; + + return GC_RAND_NEXT(&seed); + } + +# define RANDOM() (long)GC_rand() + + /* Store back pointer to source in dest, if that appears to be possible. */ + /* This is not completely safe, since we may mistakenly conclude that */ + /* dest has a debugging wrapper. But the error probability is very */ + /* small, and this shouldn't be used in production code. */ + /* We assume that dest is the real base pointer. Source will usually */ + /* be a pointer to the interior of an object. */ + GC_INNER void GC_store_back_pointer(ptr_t source, ptr_t dest) + { + if (GC_HAS_DEBUG_INFO(dest)) { +# ifdef PARALLEL_MARK + AO_store((volatile AO_t *)&((oh *)dest)->oh_back_ptr, + (AO_t)HIDE_BACK_PTR(source)); +# else + ((oh *)dest) -> oh_back_ptr = HIDE_BACK_PTR(source); +# endif + } + } + + GC_INNER void GC_marked_for_finalization(ptr_t dest) + { + GC_store_back_pointer(MARKED_FOR_FINALIZATION, dest); + } + + GC_API GC_ref_kind GC_CALL GC_get_back_ptr_info(void *dest, void **base_p, + size_t *offset_p) + { + oh * hdr = (oh *)GC_base(dest); + ptr_t bp; + ptr_t bp_base; + +# ifdef LINT2 + /* Explicitly instruct the code analysis tool that */ + /* GC_get_back_ptr_info is not expected to be called with an */ + /* incorrect "dest" value. */ + if (!hdr) ABORT("Invalid GC_get_back_ptr_info argument"); +# endif + if (!GC_HAS_DEBUG_INFO((ptr_t)hdr)) return GC_NO_SPACE; + bp = (ptr_t)GC_REVEAL_POINTER(hdr -> oh_back_ptr); + if (MARKED_FOR_FINALIZATION == bp) return GC_FINALIZER_REFD; + if (MARKED_FROM_REGISTER == bp) return GC_REFD_FROM_REG; + if (NOT_MARKED == bp) return GC_UNREFERENCED; +# if ALIGNMENT == 1 + /* Heuristically try to fix off-by-one errors we introduced by */ + /* insisting on even addresses. */ + { + ptr_t alternate_ptr = bp + 1; + ptr_t target = *(ptr_t *)bp; + ptr_t alternate_target = *(ptr_t *)alternate_ptr; + + if ((word)alternate_target > GC_least_real_heap_addr + && (word)alternate_target < GC_greatest_real_heap_addr + && ((word)target <= GC_least_real_heap_addr + || (word)target >= GC_greatest_real_heap_addr)) { + bp = alternate_ptr; + } + } +# endif + bp_base = (ptr_t)GC_base(bp); + if (NULL == bp_base) { + *base_p = bp; + *offset_p = 0; + return GC_REFD_FROM_ROOT; + } else { + if (GC_HAS_DEBUG_INFO(bp_base)) bp_base += sizeof(oh); + *base_p = bp_base; + *offset_p = (size_t)(bp - bp_base); + return GC_REFD_FROM_HEAP; + } + } + + /* Generate a random heap address. */ + /* The resulting address is in the heap, but */ + /* not necessarily inside a valid object. */ + GC_API void * GC_CALL GC_generate_random_heap_address(void) + { + size_t i; + word heap_offset = (word)RANDOM(); + + if (GC_heapsize > (word)GC_RAND_MAX) { + heap_offset *= GC_RAND_MAX; + heap_offset += (word)RANDOM(); + } + heap_offset %= GC_heapsize; + /* This doesn't yield a uniform distribution, especially if */ + /* e.g. RAND_MAX is 1.5*GC_heapsize. But for typical cases, */ + /* it's not too bad. */ + + for (i = 0;; ++i) { + size_t size; + + if (i >= GC_n_heap_sects) + ABORT("GC_generate_random_heap_address: size inconsistency"); + + size = GC_heap_sects[i].hs_bytes; + if (heap_offset < size) { + break; + } else { + heap_offset -= size; + } + } + return GC_heap_sects[i].hs_start + heap_offset; + } + + /* Generate a random address inside a valid marked heap object. */ + GC_API void * GC_CALL GC_generate_random_valid_address(void) + { + ptr_t result; + ptr_t base; + do { + result = (ptr_t)GC_generate_random_heap_address(); + base = (ptr_t)GC_base(result); + } while (NULL == base || !GC_is_marked(base)); + return result; + } + + /* Print back trace for p */ + GC_API void GC_CALL GC_print_backtrace(void *p) + { + void *current = p; + int i; + GC_ref_kind source; + size_t offset; + void *base; + + GC_ASSERT(I_DONT_HOLD_LOCK()); + GC_print_heap_obj((ptr_t)GC_base(current)); + + for (i = 0; ; ++i) { + source = GC_get_back_ptr_info(current, &base, &offset); + if (GC_UNREFERENCED == source) { + GC_err_printf("Reference could not be found\n"); + goto out; + } + if (GC_NO_SPACE == source) { + GC_err_printf("No debug info in object: Can't find reference\n"); + goto out; + } + GC_err_printf("Reachable via %d levels of pointers from ", i); + switch(source) { + case GC_REFD_FROM_ROOT: + GC_err_printf("root at %p\n\n", base); + goto out; + case GC_REFD_FROM_REG: + GC_err_printf("root in register\n\n"); + goto out; + case GC_FINALIZER_REFD: + GC_err_printf("list of finalizable objects\n\n"); + goto out; + case GC_REFD_FROM_HEAP: + GC_err_printf("offset %ld in object:\n", (long)offset); + /* Take GC_base(base) to get real base, i.e. header. */ + GC_print_heap_obj((ptr_t)GC_base(base)); + break; + default: + GC_err_printf("INTERNAL ERROR: UNEXPECTED SOURCE!!!!\n"); + goto out; + } + current = base; + } + out:; + } + + GC_API void GC_CALL GC_generate_random_backtrace(void) + { + void *current; + + GC_ASSERT(I_DONT_HOLD_LOCK()); + if (GC_try_to_collect(GC_never_stop_func) == 0) { + GC_err_printf("Cannot generate a backtrace: " + "garbage collection is disabled!\n"); + return; + } + + /* Generate/print a backtrace from a random heap address. */ + LOCK(); + current = GC_generate_random_valid_address(); + UNLOCK(); + GC_printf("\n****Chosen address %p in object\n", current); + GC_print_backtrace(current); + } + +#endif /* KEEP_BACK_PTRS */ + +# define CROSSES_HBLK(p, sz) \ + (((word)((p) + sizeof(oh) + (sz) - 1) ^ (word)(p)) >= HBLKSIZE) + +GC_INNER void *GC_store_debug_info_inner(void *p, word sz, + const char *string, int linenum) +{ + word * result = (word *)((oh *)p + 1); + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_size(p) >= sizeof(oh) + sz); + GC_ASSERT(!(SMALL_OBJ(sz) && CROSSES_HBLK((ptr_t)p, sz))); +# ifdef KEEP_BACK_PTRS + ((oh *)p) -> oh_back_ptr = HIDE_BACK_PTR(NOT_MARKED); +# endif +# ifdef MAKE_BACK_GRAPH + ((oh *)p) -> oh_bg_ptr = HIDE_BACK_PTR((ptr_t)0); +# endif + ((oh *)p) -> oh_string = string; + ((oh *)p) -> oh_int = linenum; +# ifdef SHORT_DBG_HDRS + UNUSED_ARG(sz); +# else + ((oh *)p) -> oh_sz = sz; + ((oh *)p) -> oh_sf = START_FLAG ^ (word)result; + ((word *)p)[BYTES_TO_WORDS(GC_size(p))-1] = + result[SIMPLE_ROUNDED_UP_WORDS(sz)] = END_FLAG ^ (word)result; +# endif + return result; +} + +/* Check the allocation is successful, store debugging info into p, */ +/* start the debugging mode (if not yet), and return displaced pointer. */ +static void *store_debug_info(void *p, size_t lb, + const char *fn, GC_EXTRA_PARAMS) +{ + void *result; + + if (NULL == p) { + GC_err_printf("%s(%lu) returning NULL (%s:%d)\n", + fn, (unsigned long)lb, s, i); + return NULL; + } + LOCK(); + if (!GC_debugging_started) + GC_start_debugging_inner(); + ADD_CALL_CHAIN(p, ra); + result = GC_store_debug_info_inner(p, (word)lb, s, i); + UNLOCK(); + return result; +} + +#ifndef SHORT_DBG_HDRS + /* Check the object with debugging info at ohdr. */ + /* Return NULL if it's OK. Else return clobbered */ + /* address. */ + STATIC ptr_t GC_check_annotated_obj(oh *ohdr) + { + ptr_t body = (ptr_t)(ohdr + 1); + word gc_sz = GC_size((ptr_t)ohdr); + if (ohdr -> oh_sz + DEBUG_BYTES > gc_sz) { + return (ptr_t)(&(ohdr -> oh_sz)); + } + if (ohdr -> oh_sf != (START_FLAG ^ (word)body)) { + return (ptr_t)(&(ohdr -> oh_sf)); + } + if (((word *)ohdr)[BYTES_TO_WORDS(gc_sz)-1] != (END_FLAG ^ (word)body)) { + return (ptr_t)(&((word *)ohdr)[BYTES_TO_WORDS(gc_sz)-1]); + } + if (((word *)body)[SIMPLE_ROUNDED_UP_WORDS(ohdr -> oh_sz)] + != (END_FLAG ^ (word)body)) { + return (ptr_t)(&((word *)body)[SIMPLE_ROUNDED_UP_WORDS(ohdr->oh_sz)]); + } + return NULL; + } +#endif /* !SHORT_DBG_HDRS */ + +STATIC GC_describe_type_fn GC_describe_type_fns[MAXOBJKINDS] = {0}; + +GC_API void GC_CALL GC_register_describe_type_fn(int kind, + GC_describe_type_fn fn) +{ + GC_ASSERT((unsigned)kind < MAXOBJKINDS); + GC_describe_type_fns[kind] = fn; +} + +#define GET_OH_LINENUM(ohdr) ((int)(ohdr)->oh_int) + +#ifndef SHORT_DBG_HDRS +# define IF_NOT_SHORTDBG_HDRS(x) x +# define COMMA_IFNOT_SHORTDBG_HDRS(x) /* comma */, x +#else +# define IF_NOT_SHORTDBG_HDRS(x) /* empty */ +# define COMMA_IFNOT_SHORTDBG_HDRS(x) /* empty */ +#endif + +/* Print a human-readable description of the object to stderr. */ +/* p points to somewhere inside an object with the debugging info. */ +STATIC void GC_print_obj(ptr_t p) +{ + oh * ohdr = (oh *)GC_base(p); + ptr_t q; + hdr * hhdr; + int kind; + const char *kind_str; + char buffer[GC_TYPE_DESCR_LEN + 1]; + + GC_ASSERT(I_DONT_HOLD_LOCK()); +# ifdef LINT2 + if (!ohdr) ABORT("Invalid GC_print_obj argument"); +# endif + + q = (ptr_t)(ohdr + 1); + /* Print a type description for the object whose client-visible */ + /* address is q. */ + hhdr = GC_find_header(q); + kind = hhdr -> hb_obj_kind; + if (0 != GC_describe_type_fns[kind] && GC_is_marked(ohdr)) { + /* This should preclude free list objects except with */ + /* thread-local allocation. */ + buffer[GC_TYPE_DESCR_LEN] = 0; + (GC_describe_type_fns[kind])(q, buffer); + GC_ASSERT(buffer[GC_TYPE_DESCR_LEN] == 0); + kind_str = buffer; + } else { + switch(kind) { + case PTRFREE: + kind_str = "PTRFREE"; + break; + case NORMAL: + kind_str = "NORMAL"; + break; + case UNCOLLECTABLE: + kind_str = "UNCOLLECTABLE"; + break; +# ifdef GC_ATOMIC_UNCOLLECTABLE + case AUNCOLLECTABLE: + kind_str = "ATOMIC_UNCOLLECTABLE"; + break; +# endif + default: + kind_str = NULL; + /* The alternative is to use snprintf(buffer) but it is */ + /* not quite portable (see vsnprintf in misc.c). */ + } + } + + if (NULL != kind_str) { + GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(" sz= %lu,") " %s)\n", + (void *)((ptr_t)ohdr + sizeof(oh)), + ohdr->oh_string, GET_OH_LINENUM(ohdr) /*, */ + COMMA_IFNOT_SHORTDBG_HDRS((unsigned long)ohdr->oh_sz), + kind_str); + } else { + GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(" sz= %lu,") + " kind= %d, descr= 0x%lx)\n", + (void *)((ptr_t)ohdr + sizeof(oh)), + ohdr->oh_string, GET_OH_LINENUM(ohdr) /*, */ + COMMA_IFNOT_SHORTDBG_HDRS((unsigned long)ohdr->oh_sz), + kind, (unsigned long)hhdr->hb_descr); + } + PRINT_CALL_CHAIN(ohdr); +} + +STATIC void GC_debug_print_heap_obj_proc(ptr_t p) +{ + GC_ASSERT(I_DONT_HOLD_LOCK()); + if (GC_HAS_DEBUG_INFO(p)) { + GC_print_obj(p); + } else { + GC_default_print_heap_obj_proc(p); + } +} + +#ifndef SHORT_DBG_HDRS + /* Use GC_err_printf and friends to print a description of the object */ + /* whose client-visible address is p, and which was smashed at */ + /* clobbered_addr. */ + STATIC void GC_print_smashed_obj(const char *msg, void *p, + ptr_t clobbered_addr) + { + oh * ohdr = (oh *)GC_base(p); + + GC_ASSERT(I_DONT_HOLD_LOCK()); +# ifdef LINT2 + if (!ohdr) ABORT("Invalid GC_print_smashed_obj argument"); +# endif + if ((word)clobbered_addr <= (word)(&ohdr->oh_sz) + || ohdr -> oh_string == 0) { + GC_err_printf( + "%s %p in or near object at %p(, appr. sz= %lu)\n", + msg, (void *)clobbered_addr, p, + (unsigned long)(GC_size((ptr_t)ohdr) - DEBUG_BYTES)); + } else { + GC_err_printf("%s %p in or near object at %p (%s:%d, sz= %lu)\n", + msg, (void *)clobbered_addr, p, + (word)(ohdr -> oh_string) < HBLKSIZE ? "(smashed string)" : + ohdr -> oh_string[0] == '\0' ? "EMPTY(smashed?)" : + ohdr -> oh_string, + GET_OH_LINENUM(ohdr), (unsigned long)(ohdr -> oh_sz)); + PRINT_CALL_CHAIN(ohdr); + } + } + + STATIC void GC_check_heap_proc (void); + STATIC void GC_print_all_smashed_proc (void); +#else + STATIC void GC_do_nothing(void) {} +#endif /* SHORT_DBG_HDRS */ + +GC_INNER void GC_start_debugging_inner(void) +{ + GC_ASSERT(I_HOLD_LOCK()); +# ifndef SHORT_DBG_HDRS + GC_check_heap = GC_check_heap_proc; + GC_print_all_smashed = GC_print_all_smashed_proc; +# else + GC_check_heap = GC_do_nothing; + GC_print_all_smashed = GC_do_nothing; +# endif + GC_print_heap_obj = GC_debug_print_heap_obj_proc; + GC_debugging_started = TRUE; + GC_register_displacement_inner((word)sizeof(oh)); +# if defined(CPPCHECK) + GC_noop1(GC_debug_header_size); +# endif +} + +const size_t GC_debug_header_size = sizeof(oh); + +GC_API size_t GC_CALL GC_get_debug_header_size(void) { + return sizeof(oh); +} + +GC_API void GC_CALL GC_debug_register_displacement(size_t offset) +{ + LOCK(); + GC_register_displacement_inner(offset); + GC_register_displacement_inner((word)sizeof(oh) + offset); + UNLOCK(); +} + +#ifdef GC_ADD_CALLER +# if defined(HAVE_DLADDR) && defined(GC_HAVE_RETURN_ADDR_PARENT) +# include + + STATIC void GC_caller_func_offset(word ad, const char **symp, int *offp) + { + Dl_info caller; + + if (ad && dladdr((void *)ad, &caller) && caller.dli_sname != NULL) { + *symp = caller.dli_sname; + *offp = (int)((char *)ad - (char *)caller.dli_saddr); + } + if (NULL == *symp) { + *symp = "unknown"; + } + } +# else +# define GC_caller_func_offset(ad, symp, offp) (void)(*(symp) = "unknown") +# endif +#endif /* GC_ADD_CALLER */ + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc(size_t lb, + GC_EXTRA_PARAMS) +{ + void * result; + + /* Note that according to malloc() specification, if size is 0 then */ + /* malloc() returns either NULL, or a unique pointer value that can */ + /* later be successfully passed to free(). We always do the latter. */ +# if defined(_FORTIFY_SOURCE) && !defined(__clang__) + /* Workaround to avoid "exceeds maximum object size" gcc warning. */ + result = GC_malloc(lb < GC_SIZE_MAX - DEBUG_BYTES ? lb + DEBUG_BYTES + : GC_SIZE_MAX >> 1); +# else + result = GC_malloc(SIZET_SAT_ADD(lb, DEBUG_BYTES)); +# endif +# ifdef GC_ADD_CALLER + if (s == NULL) { + GC_caller_func_offset(ra, &s, &i); + } +# endif + return store_debug_info(result, lb, "GC_debug_malloc", OPT_RA s, i); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_debug_malloc_ignore_off_page(size_t lb, GC_EXTRA_PARAMS) +{ + void * result = GC_malloc_ignore_off_page(SIZET_SAT_ADD(lb, DEBUG_BYTES)); + + return store_debug_info(result, lb, "GC_debug_malloc_ignore_off_page", + OPT_RA s, i); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_debug_malloc_atomic_ignore_off_page(size_t lb, GC_EXTRA_PARAMS) +{ + void * result = GC_malloc_atomic_ignore_off_page( + SIZET_SAT_ADD(lb, DEBUG_BYTES)); + + return store_debug_info(result, lb, + "GC_debug_malloc_atomic_ignore_off_page", + OPT_RA s, i); +} + +STATIC void * GC_debug_generic_malloc(size_t lb, int k, GC_EXTRA_PARAMS) +{ + void * result = GC_generic_malloc_aligned(SIZET_SAT_ADD(lb, DEBUG_BYTES), + k, 0 /* flags */, + 0 /* align_m1 */); + + return store_debug_info(result, lb, "GC_debug_generic_malloc", + OPT_RA s, i); +} + +#ifdef DBG_HDRS_ALL + /* An allocation function for internal use. Normally internally */ + /* allocated objects do not have debug information. But in this */ + /* case, we need to make sure that all objects have debug headers. */ + GC_INNER void * GC_debug_generic_malloc_inner(size_t lb, int k, + unsigned flags) + { + void * result; + + GC_ASSERT(I_HOLD_LOCK()); + result = GC_generic_malloc_inner(SIZET_SAT_ADD(lb, DEBUG_BYTES), k, flags); + if (NULL == result) { + GC_err_printf("GC internal allocation (%lu bytes) returning NULL\n", + (unsigned long) lb); + return NULL; + } + if (!GC_debugging_started) { + GC_start_debugging_inner(); + } + ADD_CALL_CHAIN(result, GC_RETURN_ADDR); + return GC_store_debug_info_inner(result, (word)lb, "INTERNAL", 0); + } +#endif /* DBG_HDRS_ALL */ + +#ifndef CPPCHECK + GC_API void * GC_CALL GC_debug_malloc_stubborn(size_t lb, GC_EXTRA_PARAMS) + { + return GC_debug_malloc(lb, OPT_RA s, i); + } + + GC_API void GC_CALL GC_debug_change_stubborn(const void *p) + { + UNUSED_ARG(p); + } +#endif /* !CPPCHECK */ + +GC_API void GC_CALL GC_debug_end_stubborn_change(const void *p) +{ + const void * q = GC_base_C(p); + + if (NULL == q) { + ABORT_ARG1("GC_debug_end_stubborn_change: bad arg", ": %p", p); + } + GC_end_stubborn_change(q); +} + +GC_API void GC_CALL GC_debug_ptr_store_and_dirty(void *p, const void *q) +{ + *(void **)GC_is_visible(p) = GC_is_valid_displacement( + (/* no const */ void *)(word)q); + GC_debug_end_stubborn_change(p); + REACHABLE_AFTER_DIRTY(q); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_atomic(size_t lb, + GC_EXTRA_PARAMS) +{ + void * result = GC_malloc_atomic(SIZET_SAT_ADD(lb, DEBUG_BYTES)); + + return store_debug_info(result, lb, "GC_debug_malloc_atomic", + OPT_RA s, i); +} + +GC_API GC_ATTR_MALLOC char * GC_CALL GC_debug_strdup(const char *str, + GC_EXTRA_PARAMS) +{ + char *copy; + size_t lb; + if (str == NULL) { + if (GC_find_leak) + GC_err_printf("strdup(NULL) behavior is undefined\n"); + return NULL; + } + + lb = strlen(str) + 1; + copy = (char *)GC_debug_malloc_atomic(lb, OPT_RA s, i); + if (copy == NULL) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + BCOPY(str, copy, lb); + return copy; +} + +GC_API GC_ATTR_MALLOC char * GC_CALL GC_debug_strndup(const char *str, + size_t size, GC_EXTRA_PARAMS) +{ + char *copy; + size_t len = strlen(str); /* str is expected to be non-NULL */ + if (len > size) + len = size; + copy = (char *)GC_debug_malloc_atomic(len + 1, OPT_RA s, i); + if (copy == NULL) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + if (len > 0) + BCOPY(str, copy, len); + copy[len] = '\0'; + return copy; +} + +#ifdef GC_REQUIRE_WCSDUP +# include /* for wcslen() */ + + GC_API GC_ATTR_MALLOC wchar_t * GC_CALL GC_debug_wcsdup(const wchar_t *str, + GC_EXTRA_PARAMS) + { + size_t lb = (wcslen(str) + 1) * sizeof(wchar_t); + wchar_t *copy = (wchar_t *)GC_debug_malloc_atomic(lb, OPT_RA s, i); + if (copy == NULL) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + BCOPY(str, copy, lb); + return copy; + } +#endif /* GC_REQUIRE_WCSDUP */ + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_uncollectable(size_t lb, + GC_EXTRA_PARAMS) +{ + void * result = GC_malloc_uncollectable( + SIZET_SAT_ADD(lb, UNCOLLECTABLE_DEBUG_BYTES)); + + return store_debug_info(result, lb, "GC_debug_malloc_uncollectable", + OPT_RA s, i); +} + +#ifdef GC_ATOMIC_UNCOLLECTABLE + GC_API GC_ATTR_MALLOC void * GC_CALL + GC_debug_malloc_atomic_uncollectable(size_t lb, GC_EXTRA_PARAMS) + { + void * result = GC_malloc_atomic_uncollectable( + SIZET_SAT_ADD(lb, UNCOLLECTABLE_DEBUG_BYTES)); + + return store_debug_info(result, lb, + "GC_debug_malloc_atomic_uncollectable", + OPT_RA s, i); + } +#endif /* GC_ATOMIC_UNCOLLECTABLE */ + +#ifndef GC_FREED_MEM_MARKER +# if CPP_WORDSZ == 32 +# define GC_FREED_MEM_MARKER 0xdeadbeef +# else +# define GC_FREED_MEM_MARKER GC_WORD_C(0xEFBEADDEdeadbeef) +# endif +#endif + +#ifdef LINT2 +/* + * Copyright (c) 1996-1998 by Silicon Graphics. All rights reserved. + * Copyright (c) 2018-2021 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* This file is kept for a binary compatibility purpose only. */ + +#ifndef GC_ALLOC_PTRS_H +#define GC_ALLOC_PTRS_H + + + +#ifdef __cplusplus + extern "C" { +#endif + +#ifndef GC_API_PRIV +# define GC_API_PRIV GC_API +#endif + +/* Some compilers do not accept "const" together with the dllimport */ +/* attribute, so the symbols below are exported as non-constant ones. */ +#ifndef GC_APIVAR_CONST +# if defined(GC_BUILD) || !defined(GC_DLL) +# define GC_APIVAR_CONST const +# else +# define GC_APIVAR_CONST /* empty */ +# endif +#endif + +GC_API_PRIV void ** GC_APIVAR_CONST GC_objfreelist_ptr; +GC_API_PRIV void ** GC_APIVAR_CONST GC_aobjfreelist_ptr; +GC_API_PRIV void ** GC_APIVAR_CONST GC_uobjfreelist_ptr; + +#ifdef GC_ATOMIC_UNCOLLECTABLE + GC_API_PRIV void ** GC_APIVAR_CONST GC_auobjfreelist_ptr; +#endif + +/* Manually update the number of bytes allocated during the current */ +/* collection cycle and the number of explicitly deallocated bytes of */ +/* memory since the last collection, respectively. Both functions are */ +/* unsynchronized, GC_call_with_alloc_lock() should be used to avoid */ +/* data race. */ +GC_API_PRIV void GC_CALL GC_incr_bytes_allocd(size_t /* bytes */); +GC_API_PRIV void GC_CALL GC_incr_bytes_freed(size_t /* bytes */); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_ALLOC_PTRS_H */ + +#endif + +GC_API void GC_CALL GC_debug_free(void * p) +{ + ptr_t base; + if (0 == p) return; + + base = (ptr_t)GC_base(p); + if (NULL == base) { +# if defined(REDIRECT_MALLOC) \ + && ((defined(NEED_CALLINFO) && defined(GC_HAVE_BUILTIN_BACKTRACE)) \ + || defined(GC_LINUX_THREADS) || defined(GC_SOLARIS_THREADS) \ + || defined(MSWIN32)) + /* In some cases, we should ignore objects that do not belong */ + /* to the GC heap. See the comment in GC_free. */ + if (!GC_is_heap_ptr(p)) return; +# endif + ABORT_ARG1("Invalid pointer passed to free()", ": %p", p); + } + if ((word)p - (word)base != sizeof(oh)) { +# if defined(REDIRECT_FREE) && defined(USE_PROC_FOR_LIBRARIES) + /* TODO: Suppress the warning if free() caller is in libpthread */ + /* or libdl. */ +# endif + /* TODO: Suppress the warning for objects allocated by */ + /* GC_memalign and friends (these ones do not have the debugging */ + /* counterpart). */ + GC_err_printf( + "GC_debug_free called on pointer %p w/o debugging info\n", p); + } else { +# ifndef SHORT_DBG_HDRS + ptr_t clobbered = GC_check_annotated_obj((oh *)base); + word sz = GC_size(base); + if (clobbered != 0) { + GC_SET_HAVE_ERRORS(); /* no "release" barrier is needed */ + if (((oh *)base) -> oh_sz == sz) { + GC_print_smashed_obj( + "GC_debug_free: found previously deallocated (?) object at", + p, clobbered); + return; /* ignore double free */ + } else { + GC_print_smashed_obj("GC_debug_free: found smashed location at", + p, clobbered); + } + } + /* Invalidate size (mark the object as deallocated) */ + ((oh *)base) -> oh_sz = sz; +# endif /* !SHORT_DBG_HDRS */ + } + if (GC_find_leak +# ifndef SHORT_DBG_HDRS + && ((word)p - (word)base != sizeof(oh) || !GC_findleak_delay_free) +# endif + ) { + GC_free(base); + } else { + hdr * hhdr = HDR(p); + if (hhdr -> hb_obj_kind == UNCOLLECTABLE +# ifdef GC_ATOMIC_UNCOLLECTABLE + || hhdr -> hb_obj_kind == AUNCOLLECTABLE +# endif + ) { + GC_free(base); + } else { + word i; + word sz = hhdr -> hb_sz; + word obj_sz = BYTES_TO_WORDS(sz - sizeof(oh)); + + for (i = 0; i < obj_sz; ++i) + ((word *)p)[i] = GC_FREED_MEM_MARKER; + GC_ASSERT((word *)p + i == (word *)(base + sz)); + /* Update the counter even though the real deallocation */ + /* is deferred. */ + LOCK(); +# ifdef LINT2 + GC_incr_bytes_freed((size_t)sz); +# else + GC_bytes_freed += sz; +# endif + UNLOCK(); + } + } /* !GC_find_leak */ +} + +#if defined(THREADS) && defined(DBG_HDRS_ALL) + /* Used internally; we assume it's called correctly. */ + GC_INNER void GC_debug_free_inner(void * p) + { + ptr_t base = (ptr_t)GC_base(p); + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT((word)p - (word)base == sizeof(oh)); +# ifdef LINT2 + if (!base) ABORT("Invalid GC_debug_free_inner argument"); +# endif +# ifndef SHORT_DBG_HDRS + /* Invalidate size */ + ((oh *)base) -> oh_sz = GC_size(base); +# endif + GC_free_inner(base); + } +#endif + +GC_API void * GC_CALL GC_debug_realloc(void * p, size_t lb, GC_EXTRA_PARAMS) +{ + void * base; + void * result; + hdr * hhdr; + + if (p == 0) { + return GC_debug_malloc(lb, OPT_RA s, i); + } + if (0 == lb) /* and p != NULL */ { + GC_debug_free(p); + return NULL; + } + +# ifdef GC_ADD_CALLER + if (s == NULL) { + GC_caller_func_offset(ra, &s, &i); + } +# endif + base = GC_base(p); + if (base == 0) { + ABORT_ARG1("Invalid pointer passed to realloc()", ": %p", p); + } + if ((word)p - (word)base != sizeof(oh)) { + GC_err_printf( + "GC_debug_realloc called on pointer %p w/o debugging info\n", p); + return GC_realloc(p, lb); + } + hhdr = HDR(base); + switch (hhdr -> hb_obj_kind) { + case NORMAL: + result = GC_debug_malloc(lb, OPT_RA s, i); + break; + case PTRFREE: + result = GC_debug_malloc_atomic(lb, OPT_RA s, i); + break; + case UNCOLLECTABLE: + result = GC_debug_malloc_uncollectable(lb, OPT_RA s, i); + break; +# ifdef GC_ATOMIC_UNCOLLECTABLE + case AUNCOLLECTABLE: + result = GC_debug_malloc_atomic_uncollectable(lb, OPT_RA s, i); + break; +# endif + default: + result = NULL; /* initialized to prevent warning. */ + ABORT_RET("GC_debug_realloc: encountered bad kind"); + } + + if (result != NULL) { + size_t old_sz; +# ifdef SHORT_DBG_HDRS + old_sz = GC_size(base) - sizeof(oh); +# else + old_sz = ((oh *)base) -> oh_sz; +# endif + if (old_sz > 0) + BCOPY(p, result, old_sz < lb ? old_sz : lb); + GC_debug_free(p); + } + return result; +} + +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_debug_generic_or_special_malloc(size_t lb, int k, GC_EXTRA_PARAMS) +{ + switch (k) { + case PTRFREE: + return GC_debug_malloc_atomic(lb, OPT_RA s, i); + case NORMAL: + return GC_debug_malloc(lb, OPT_RA s, i); + case UNCOLLECTABLE: + return GC_debug_malloc_uncollectable(lb, OPT_RA s, i); +# ifdef GC_ATOMIC_UNCOLLECTABLE + case AUNCOLLECTABLE: + return GC_debug_malloc_atomic_uncollectable(lb, OPT_RA s, i); +# endif + default: + return GC_debug_generic_malloc(lb, k, OPT_RA s, i); + } +} + +#ifndef SHORT_DBG_HDRS + +/* List of smashed (clobbered) locations. We defer printing these, */ +/* since we cannot always print them nicely with the allocator lock */ +/* held. We put them here instead of in GC_arrays, since it may be */ +/* useful to be able to look at them with the debugger. */ +#ifndef MAX_SMASHED +# define MAX_SMASHED 20 +#endif +STATIC ptr_t GC_smashed[MAX_SMASHED] = {0}; +STATIC unsigned GC_n_smashed = 0; + +STATIC void GC_add_smashed(ptr_t smashed) +{ + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_is_marked(GC_base(smashed))); + /* FIXME: Prevent adding an object while printing smashed list. */ + GC_smashed[GC_n_smashed] = smashed; + if (GC_n_smashed < MAX_SMASHED - 1) ++GC_n_smashed; + /* In case of overflow, we keep the first MAX_SMASHED-1 */ + /* entries plus the last one. */ + GC_SET_HAVE_ERRORS(); +} + +/* Print all objects on the list. Clear the list. */ +STATIC void GC_print_all_smashed_proc(void) +{ + unsigned i; + + GC_ASSERT(I_DONT_HOLD_LOCK()); + if (GC_n_smashed == 0) return; + GC_err_printf("GC_check_heap_block: found %u smashed heap objects:\n", + GC_n_smashed); + for (i = 0; i < GC_n_smashed; ++i) { + ptr_t base = (ptr_t)GC_base(GC_smashed[i]); + +# ifdef LINT2 + if (!base) ABORT("Invalid GC_smashed element"); +# endif + GC_print_smashed_obj("", base + sizeof(oh), GC_smashed[i]); + GC_smashed[i] = 0; + } + GC_n_smashed = 0; +} + +/* Check all marked objects in the given block for validity */ +/* Avoid GC_apply_to_each_object for performance reasons. */ +STATIC void GC_CALLBACK GC_check_heap_block(struct hblk *hbp, GC_word dummy) +{ + struct hblkhdr * hhdr = HDR(hbp); + word sz = hhdr -> hb_sz; + word bit_no; + char *p, *plim; + + UNUSED_ARG(dummy); + p = hbp->hb_body; + if (sz > MAXOBJBYTES) { + plim = p; + } else { + plim = hbp->hb_body + HBLKSIZE - sz; + } + /* go through all words in block */ + for (bit_no = 0; (word)p <= (word)plim; + bit_no += MARK_BIT_OFFSET(sz), p += sz) { + if (mark_bit_from_hdr(hhdr, bit_no) && GC_HAS_DEBUG_INFO((ptr_t)p)) { + ptr_t clobbered = GC_check_annotated_obj((oh *)p); + if (clobbered != 0) + GC_add_smashed(clobbered); + } + } +} + +/* This assumes that all accessible objects are marked. */ +/* Normally called by collector. */ +STATIC void GC_check_heap_proc(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + GC_STATIC_ASSERT((sizeof(oh) & (GC_GRANULE_BYTES-1)) == 0); + /* FIXME: Should we check for twice that alignment? */ + GC_apply_to_all_blocks(GC_check_heap_block, 0); +} + +GC_INNER GC_bool GC_check_leaked(ptr_t base) +{ + word i; + word obj_sz; + word *p; + + if ( +# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) + (*(word *)base & 1) != 0 && +# endif + GC_has_other_debug_info(base) >= 0) + return TRUE; /* object has leaked */ + + /* Validate freed object's content. */ + p = (word *)(base + sizeof(oh)); + obj_sz = BYTES_TO_WORDS(HDR(base)->hb_sz - sizeof(oh)); + for (i = 0; i < obj_sz; ++i) + if (p[i] != GC_FREED_MEM_MARKER) { + GC_set_mark_bit(base); /* do not reclaim it in this cycle */ + GC_add_smashed((ptr_t)(&p[i])); /* alter-after-free detected */ + break; /* don't report any other smashed locations in the object */ + } + + return FALSE; /* GC_debug_free() has been called */ +} + +#endif /* !SHORT_DBG_HDRS */ + +#ifndef GC_NO_FINALIZATION + +struct closure { + GC_finalization_proc cl_fn; + void * cl_data; +}; + +STATIC void * GC_make_closure(GC_finalization_proc fn, void * data) +{ + struct closure * result = +# ifdef DBG_HDRS_ALL + (struct closure *)GC_debug_malloc(sizeof(struct closure), GC_EXTRAS); +# else + (struct closure *)GC_malloc(sizeof(struct closure)); +# endif + if (result != NULL) { + result -> cl_fn = fn; + result -> cl_data = data; + } + return (void *)result; +} + +/* An auxiliary fns to make finalization work correctly with displaced */ +/* pointers introduced by the debugging allocators. */ +STATIC void GC_CALLBACK GC_debug_invoke_finalizer(void * obj, void * data) +{ + struct closure * cl = (struct closure *) data; + (*(cl -> cl_fn))((void *)((char *)obj + sizeof(oh)), cl -> cl_data); +} + +/* Special finalizer_proc value to detect GC_register_finalizer() failure. */ +#define OFN_UNSET ((GC_finalization_proc)~(GC_funcptr_uint)0) + +/* Set ofn and ocd to reflect the values we got back. */ +static void store_old(void *obj, GC_finalization_proc my_old_fn, + struct closure *my_old_cd, GC_finalization_proc *ofn, + void **ocd) +{ + if (0 != my_old_fn) { + if (my_old_fn == OFN_UNSET) { + /* GC_register_finalizer() failed; (*ofn) and (*ocd) are unchanged. */ + return; + } + if (my_old_fn != GC_debug_invoke_finalizer) { + GC_err_printf("Debuggable object at %p had a non-debug finalizer\n", + obj); + /* This should probably be fatal. */ + } else { + if (ofn) *ofn = my_old_cd -> cl_fn; + if (ocd) *ocd = my_old_cd -> cl_data; + } + } else { + if (ofn) *ofn = 0; + if (ocd) *ocd = 0; + } +} + +GC_API void GC_CALL GC_debug_register_finalizer(void * obj, + GC_finalization_proc fn, + void * cd, GC_finalization_proc *ofn, + void * *ocd) +{ + GC_finalization_proc my_old_fn = OFN_UNSET; + void * my_old_cd = NULL; /* to avoid "might be uninitialized" warning */ + ptr_t base = (ptr_t)GC_base(obj); + if (NULL == base) { + /* We won't collect it, hence finalizer wouldn't be run. */ + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + return; + } + if ((ptr_t)obj - base != sizeof(oh)) { + GC_err_printf("GC_debug_register_finalizer called with" + " non-base-pointer %p\n", obj); + } + if (0 == fn) { + GC_register_finalizer(base, 0, 0, &my_old_fn, &my_old_cd); + } else { + cd = GC_make_closure(fn, cd); + if (cd == 0) return; /* out of memory; *ofn and *ocd are unchanged */ + GC_register_finalizer(base, GC_debug_invoke_finalizer, + cd, &my_old_fn, &my_old_cd); + } + store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd); +} + +GC_API void GC_CALL GC_debug_register_finalizer_no_order + (void * obj, GC_finalization_proc fn, + void * cd, GC_finalization_proc *ofn, + void * *ocd) +{ + GC_finalization_proc my_old_fn = OFN_UNSET; + void * my_old_cd = NULL; + ptr_t base = (ptr_t)GC_base(obj); + if (NULL == base) { + /* We won't collect it, hence finalizer wouldn't be run. */ + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + return; + } + if ((ptr_t)obj - base != sizeof(oh)) { + GC_err_printf("GC_debug_register_finalizer_no_order called with" + " non-base-pointer %p\n", obj); + } + if (0 == fn) { + GC_register_finalizer_no_order(base, 0, 0, &my_old_fn, &my_old_cd); + } else { + cd = GC_make_closure(fn, cd); + if (cd == 0) return; /* out of memory */ + GC_register_finalizer_no_order(base, GC_debug_invoke_finalizer, + cd, &my_old_fn, &my_old_cd); + } + store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd); +} + +GC_API void GC_CALL GC_debug_register_finalizer_unreachable + (void * obj, GC_finalization_proc fn, + void * cd, GC_finalization_proc *ofn, + void * *ocd) +{ + GC_finalization_proc my_old_fn = OFN_UNSET; + void * my_old_cd = NULL; + ptr_t base = (ptr_t)GC_base(obj); + if (NULL == base) { + /* We won't collect it, hence finalizer wouldn't be run. */ + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + return; + } + if ((ptr_t)obj - base != sizeof(oh)) { + GC_err_printf("GC_debug_register_finalizer_unreachable called with" + " non-base-pointer %p\n", obj); + } + if (0 == fn) { + GC_register_finalizer_unreachable(base, 0, 0, &my_old_fn, &my_old_cd); + } else { + cd = GC_make_closure(fn, cd); + if (cd == 0) return; /* out of memory */ + GC_register_finalizer_unreachable(base, GC_debug_invoke_finalizer, + cd, &my_old_fn, &my_old_cd); + } + store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd); +} + +GC_API void GC_CALL GC_debug_register_finalizer_ignore_self + (void * obj, GC_finalization_proc fn, + void * cd, GC_finalization_proc *ofn, + void * *ocd) +{ + GC_finalization_proc my_old_fn = OFN_UNSET; + void * my_old_cd = NULL; + ptr_t base = (ptr_t)GC_base(obj); + if (NULL == base) { + /* We won't collect it, hence finalizer wouldn't be run. */ + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + return; + } + if ((ptr_t)obj - base != sizeof(oh)) { + GC_err_printf("GC_debug_register_finalizer_ignore_self called with" + " non-base-pointer %p\n", obj); + } + if (0 == fn) { + GC_register_finalizer_ignore_self(base, 0, 0, &my_old_fn, &my_old_cd); + } else { + cd = GC_make_closure(fn, cd); + if (cd == 0) return; /* out of memory */ + GC_register_finalizer_ignore_self(base, GC_debug_invoke_finalizer, + cd, &my_old_fn, &my_old_cd); + } + store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd); +} + +# ifndef GC_TOGGLE_REFS_NOT_NEEDED + GC_API int GC_CALL GC_debug_toggleref_add(void *obj, int is_strong_ref) + { + ptr_t base = (ptr_t)GC_base(obj); + + if ((ptr_t)obj - base != sizeof(oh)) { + GC_err_printf("GC_debug_toggleref_add called with" + " non-base-pointer %p\n", obj); + } + return GC_toggleref_add(base, is_strong_ref); + } +# endif /* !GC_TOGGLE_REFS_NOT_NEEDED */ + +#endif /* !GC_NO_FINALIZATION */ + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_debug_malloc_replacement(size_t lb) +{ + return GC_debug_malloc(lb, GC_DBG_EXTRAS); +} + +GC_API void * GC_CALL GC_debug_realloc_replacement(void *p, size_t lb) +{ + return GC_debug_realloc(p, lb, GC_DBG_EXTRAS); +} + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 2007 Free Software Foundation, Inc. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +#ifndef GC_NO_FINALIZATION +# include "gc/javaxfc.h" /* to get GC_finalize_all() as extern "C" */ + +/* Type of mark procedure used for marking from finalizable object. */ +/* This procedure normally does not mark the object, only its */ +/* descendants. */ +typedef void (* finalization_mark_proc)(ptr_t /* finalizable_obj_ptr */); + +#define HASH3(addr,size,log_size) \ + ((((word)(addr) >> 3) ^ ((word)(addr) >> (3 + (log_size)))) \ + & ((size) - 1)) +#define HASH2(addr,log_size) HASH3(addr, (word)1 << (log_size), log_size) + +struct hash_chain_entry { + word hidden_key; + struct hash_chain_entry * next; +}; + +struct disappearing_link { + struct hash_chain_entry prolog; +# define dl_hidden_link prolog.hidden_key + /* Field to be cleared. */ +# define dl_next(x) (struct disappearing_link *)((x) -> prolog.next) +# define dl_set_next(x, y) \ + (void)((x)->prolog.next = (struct hash_chain_entry *)(y)) + word dl_hidden_obj; /* Pointer to object base */ +}; + +struct finalizable_object { + struct hash_chain_entry prolog; +# define fo_hidden_base prolog.hidden_key + /* Pointer to object base. */ + /* No longer hidden once object */ + /* is on finalize_now queue. */ +# define fo_next(x) (struct finalizable_object *)((x) -> prolog.next) +# define fo_set_next(x,y) ((x)->prolog.next = (struct hash_chain_entry *)(y)) + GC_finalization_proc fo_fn; /* Finalizer. */ + ptr_t fo_client_data; + word fo_object_size; /* In bytes. */ + finalization_mark_proc fo_mark_proc; /* Mark-through procedure */ +}; + +#ifdef AO_HAVE_store + /* Update finalize_now atomically as GC_should_invoke_finalizers does */ + /* not acquire the allocator lock. */ +# define SET_FINALIZE_NOW(fo) \ + AO_store((volatile AO_t *)&GC_fnlz_roots.finalize_now, (AO_t)(fo)) +#else +# define SET_FINALIZE_NOW(fo) (void)(GC_fnlz_roots.finalize_now = (fo)) +#endif /* !THREADS */ + +GC_API void GC_CALL GC_push_finalizer_structures(void) +{ + GC_ASSERT((word)(&GC_dl_hashtbl.head) % sizeof(word) == 0); + GC_ASSERT((word)(&GC_fnlz_roots) % sizeof(word) == 0); +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_ASSERT((word)(&GC_ll_hashtbl.head) % sizeof(word) == 0); + GC_PUSH_ALL_SYM(GC_ll_hashtbl.head); +# endif + GC_PUSH_ALL_SYM(GC_dl_hashtbl.head); + GC_PUSH_ALL_SYM(GC_fnlz_roots); + /* GC_toggleref_arr is pushed specially by GC_mark_togglerefs. */ +} + +/* Threshold of log_size to initiate full collection before growing */ +/* a hash table. */ +#ifndef GC_ON_GROW_LOG_SIZE_MIN +# define GC_ON_GROW_LOG_SIZE_MIN CPP_LOG_HBLKSIZE +#endif + +/* Double the size of a hash table. *log_size_ptr is the log of its */ +/* current size. May be a no-op. *table is a pointer to an array of */ +/* hash headers. We update both *table and *log_size_ptr on success. */ +STATIC void GC_grow_table(struct hash_chain_entry ***table, + unsigned *log_size_ptr, const word *entries_ptr) +{ + word i; + struct hash_chain_entry *p; + unsigned log_old_size = *log_size_ptr; + unsigned log_new_size = log_old_size + 1; + word old_size = *table == NULL ? 0 : (word)1 << log_old_size; + word new_size = (word)1 << log_new_size; + /* FIXME: Power of 2 size often gets rounded up to one more page. */ + struct hash_chain_entry **new_table; + + GC_ASSERT(I_HOLD_LOCK()); + /* Avoid growing the table in case of at least 25% of entries can */ + /* be deleted by enforcing a collection. Ignored for small tables. */ + /* In incremental mode we skip this optimization, as we want to */ + /* avoid triggering a full GC whenever possible. */ + if (log_old_size >= GC_ON_GROW_LOG_SIZE_MIN && !GC_incremental) { + IF_CANCEL(int cancel_state;) + + DISABLE_CANCEL(cancel_state); + GC_gcollect_inner(); + RESTORE_CANCEL(cancel_state); + /* GC_finalize might decrease entries value. */ + if (*entries_ptr < ((word)1 << log_old_size) - (*entries_ptr >> 2)) + return; + } + + new_table = (struct hash_chain_entry **) + GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE( + (size_t)new_size * sizeof(struct hash_chain_entry *), + NORMAL); + if (new_table == 0) { + if (*table == 0) { + ABORT("Insufficient space for initial table allocation"); + } else { + return; + } + } + for (i = 0; i < old_size; i++) { + p = (*table)[i]; + while (p != 0) { + ptr_t real_key = (ptr_t)GC_REVEAL_POINTER(p -> hidden_key); + struct hash_chain_entry *next = p -> next; + size_t new_hash = HASH3(real_key, new_size, log_new_size); + + p -> next = new_table[new_hash]; + GC_dirty(p); + new_table[new_hash] = p; + p = next; + } + } + *log_size_ptr = log_new_size; + *table = new_table; + GC_dirty(new_table); /* entire object */ +} + +GC_API int GC_CALL GC_register_disappearing_link(void * * link) +{ + ptr_t base; + + base = (ptr_t)GC_base(link); + if (base == 0) + ABORT("Bad arg to GC_register_disappearing_link"); + return GC_general_register_disappearing_link(link, base); +} + +STATIC int GC_register_disappearing_link_inner( + struct dl_hashtbl_s *dl_hashtbl, void **link, + const void *obj, const char *tbl_log_name) +{ + struct disappearing_link *curr_dl; + size_t index; + struct disappearing_link * new_dl; + + GC_ASSERT(GC_is_initialized); + if (EXPECT(GC_find_leak, FALSE)) return GC_UNIMPLEMENTED; +# ifdef GC_ASSERTIONS + GC_noop1((word)(*link)); /* check accessibility */ +# endif + LOCK(); + GC_ASSERT(obj != NULL && GC_base_C(obj) == obj); + if (EXPECT(NULL == dl_hashtbl -> head, FALSE) + || EXPECT(dl_hashtbl -> entries + > ((word)1 << dl_hashtbl -> log_size), FALSE)) { + GC_grow_table((struct hash_chain_entry ***)&dl_hashtbl -> head, + &dl_hashtbl -> log_size, &dl_hashtbl -> entries); + GC_COND_LOG_PRINTF("Grew %s table to %u entries\n", tbl_log_name, + 1U << dl_hashtbl -> log_size); + } + index = HASH2(link, dl_hashtbl -> log_size); + for (curr_dl = dl_hashtbl -> head[index]; curr_dl != 0; + curr_dl = dl_next(curr_dl)) { + if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) { + /* Alternatively, GC_HIDE_NZ_POINTER() could be used instead. */ + curr_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj); + UNLOCK(); + return GC_DUPLICATE; + } + } + new_dl = (struct disappearing_link *) + GC_INTERNAL_MALLOC(sizeof(struct disappearing_link), NORMAL); + if (EXPECT(NULL == new_dl, FALSE)) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK(); + new_dl = (struct disappearing_link *) + (*oom_fn)(sizeof(struct disappearing_link)); + if (0 == new_dl) { + return GC_NO_MEMORY; + } + /* It's not likely we'll make it here, but ... */ + LOCK(); + /* Recalculate index since the table may grow. */ + index = HASH2(link, dl_hashtbl -> log_size); + /* Check again that our disappearing link not in the table. */ + for (curr_dl = dl_hashtbl -> head[index]; curr_dl != 0; + curr_dl = dl_next(curr_dl)) { + if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) { + curr_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj); + UNLOCK(); +# ifndef DBG_HDRS_ALL + /* Free unused new_dl returned by GC_oom_fn() */ + GC_free((void *)new_dl); +# endif + return GC_DUPLICATE; + } + } + } + new_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj); + new_dl -> dl_hidden_link = GC_HIDE_POINTER(link); + dl_set_next(new_dl, dl_hashtbl -> head[index]); + GC_dirty(new_dl); + dl_hashtbl -> head[index] = new_dl; + dl_hashtbl -> entries++; + GC_dirty(dl_hashtbl->head + index); + UNLOCK(); + return GC_SUCCESS; +} + +GC_API int GC_CALL GC_general_register_disappearing_link(void * * link, + const void * obj) +{ + if (((word)link & (ALIGNMENT-1)) != 0 || !NONNULL_ARG_NOT_NULL(link)) + ABORT("Bad arg to GC_general_register_disappearing_link"); + return GC_register_disappearing_link_inner(&GC_dl_hashtbl, link, obj, + "dl"); +} + +#ifdef DBG_HDRS_ALL +# define FREE_DL_ENTRY(curr_dl) dl_set_next(curr_dl, NULL) +#else +# define FREE_DL_ENTRY(curr_dl) GC_free(curr_dl) +#endif + +/* Unregisters given link and returns the link entry to free. */ +GC_INLINE struct disappearing_link *GC_unregister_disappearing_link_inner( + struct dl_hashtbl_s *dl_hashtbl, void **link) +{ + struct disappearing_link *curr_dl; + struct disappearing_link *prev_dl = NULL; + size_t index; + + GC_ASSERT(I_HOLD_LOCK()); + if (EXPECT(NULL == dl_hashtbl -> head, FALSE)) return NULL; + + index = HASH2(link, dl_hashtbl -> log_size); + for (curr_dl = dl_hashtbl -> head[index]; curr_dl; + curr_dl = dl_next(curr_dl)) { + if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) { + /* Remove found entry from the table. */ + if (NULL == prev_dl) { + dl_hashtbl -> head[index] = dl_next(curr_dl); + GC_dirty(dl_hashtbl->head + index); + } else { + dl_set_next(prev_dl, dl_next(curr_dl)); + GC_dirty(prev_dl); + } + dl_hashtbl -> entries--; + break; + } + prev_dl = curr_dl; + } + return curr_dl; +} + +GC_API int GC_CALL GC_unregister_disappearing_link(void * * link) +{ + struct disappearing_link *curr_dl; + + if (((word)link & (ALIGNMENT-1)) != 0) return 0; /* Nothing to do. */ + + LOCK(); + curr_dl = GC_unregister_disappearing_link_inner(&GC_dl_hashtbl, link); + UNLOCK(); + if (NULL == curr_dl) return 0; + FREE_DL_ENTRY(curr_dl); + return 1; +} + +/* Mark from one finalizable object using the specified mark proc. */ +/* May not mark the object pointed to by real_ptr (i.e, it is the job */ +/* of the caller, if appropriate). Note that this is called with the */ +/* mutator running. This is safe only if the mutator (client) gets */ +/* the allocator lock to reveal hidden pointers. */ +GC_INLINE void GC_mark_fo(ptr_t real_ptr, finalization_mark_proc fo_mark_proc) +{ + GC_ASSERT(I_HOLD_LOCK()); + fo_mark_proc(real_ptr); + /* Process objects pushed by the mark procedure. */ + while (!GC_mark_stack_empty()) + MARK_FROM_MARK_STACK(); +} + +/* Complete a collection in progress, if any. */ +GC_INLINE void GC_complete_ongoing_collection(void) { + if (EXPECT(GC_collection_in_progress(), FALSE)) { + while (!GC_mark_some(NULL)) { /* empty */ } + } +} + +/* Toggle-ref support. */ +#ifndef GC_TOGGLE_REFS_NOT_NEEDED + typedef union toggle_ref_u GCToggleRef; + + STATIC GC_toggleref_func GC_toggleref_callback = 0; + + GC_INNER void GC_process_togglerefs(void) + { + size_t i; + size_t new_size = 0; + GC_bool needs_barrier = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); + for (i = 0; i < GC_toggleref_array_size; ++i) { + GCToggleRef *r = &GC_toggleref_arr[i]; + void *obj = r -> strong_ref; + + if (((word)obj & 1) != 0) { + obj = GC_REVEAL_POINTER(r -> weak_ref); + GC_ASSERT(((word)obj & 1) == 0); + } + if (NULL == obj) continue; + + switch (GC_toggleref_callback(obj)) { + case GC_TOGGLE_REF_DROP: + break; + case GC_TOGGLE_REF_STRONG: + GC_toggleref_arr[new_size++].strong_ref = obj; + needs_barrier = TRUE; + break; + case GC_TOGGLE_REF_WEAK: + GC_toggleref_arr[new_size++].weak_ref = GC_HIDE_POINTER(obj); + break; + default: + ABORT("Bad toggle-ref status returned by callback"); + } + } + + if (new_size < GC_toggleref_array_size) { + BZERO(&GC_toggleref_arr[new_size], + (GC_toggleref_array_size - new_size) * sizeof(GCToggleRef)); + GC_toggleref_array_size = new_size; + } + if (needs_barrier) + GC_dirty(GC_toggleref_arr); /* entire object */ + } + + STATIC void GC_normal_finalize_mark_proc(ptr_t); + + STATIC void GC_mark_togglerefs(void) + { + size_t i; + + GC_ASSERT(I_HOLD_LOCK()); + if (NULL == GC_toggleref_arr) + return; + + GC_set_mark_bit(GC_toggleref_arr); + for (i = 0; i < GC_toggleref_array_size; ++i) { + void *obj = GC_toggleref_arr[i].strong_ref; + if (obj != NULL && ((word)obj & 1) == 0) { + /* Push and mark the object. */ + GC_mark_fo((ptr_t)obj, GC_normal_finalize_mark_proc); + GC_set_mark_bit(obj); + GC_complete_ongoing_collection(); + } + } + } + + STATIC void GC_clear_togglerefs(void) + { + size_t i; + + GC_ASSERT(I_HOLD_LOCK()); + for (i = 0; i < GC_toggleref_array_size; ++i) { + GCToggleRef *r = &GC_toggleref_arr[i]; + + if (((word)(r -> strong_ref) & 1) != 0) { + if (!GC_is_marked(GC_REVEAL_POINTER(r -> weak_ref))) { + r -> weak_ref = 0; + } else { + /* No need to copy, BDWGC is a non-moving collector. */ + } + } + } + } + + GC_API void GC_CALL GC_set_toggleref_func(GC_toggleref_func fn) + { + LOCK(); + GC_toggleref_callback = fn; + UNLOCK(); + } + + GC_API GC_toggleref_func GC_CALL GC_get_toggleref_func(void) + { + GC_toggleref_func fn; + + READER_LOCK(); + fn = GC_toggleref_callback; + READER_UNLOCK(); + return fn; + } + + static GC_bool ensure_toggleref_capacity(size_t capacity_inc) + { + GC_ASSERT(I_HOLD_LOCK()); + if (NULL == GC_toggleref_arr) { + GC_toggleref_array_capacity = 32; /* initial capacity */ + GC_toggleref_arr = (GCToggleRef *)GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE( + GC_toggleref_array_capacity * sizeof(GCToggleRef), + NORMAL); + if (NULL == GC_toggleref_arr) + return FALSE; + } + if (GC_toggleref_array_size + capacity_inc + >= GC_toggleref_array_capacity) { + GCToggleRef *new_array; + while (GC_toggleref_array_capacity + < GC_toggleref_array_size + capacity_inc) { + GC_toggleref_array_capacity *= 2; + if ((GC_toggleref_array_capacity + & ((size_t)1 << (sizeof(size_t) * 8 - 1))) != 0) + return FALSE; /* overflow */ + } + + new_array = (GCToggleRef *)GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE( + GC_toggleref_array_capacity * sizeof(GCToggleRef), + NORMAL); + if (NULL == new_array) + return FALSE; + if (EXPECT(GC_toggleref_array_size > 0, TRUE)) + BCOPY(GC_toggleref_arr, new_array, + GC_toggleref_array_size * sizeof(GCToggleRef)); + GC_INTERNAL_FREE(GC_toggleref_arr); + GC_toggleref_arr = new_array; + } + return TRUE; + } + + GC_API int GC_CALL GC_toggleref_add(void *obj, int is_strong_ref) + { + int res = GC_SUCCESS; + + GC_ASSERT(NONNULL_ARG_NOT_NULL(obj)); + LOCK(); + GC_ASSERT(((word)obj & 1) == 0 && obj == GC_base(obj)); + if (GC_toggleref_callback != 0) { + if (!ensure_toggleref_capacity(1)) { + res = GC_NO_MEMORY; + } else { + GCToggleRef *r = &GC_toggleref_arr[GC_toggleref_array_size]; + + if (is_strong_ref) { + r -> strong_ref = obj; + GC_dirty(GC_toggleref_arr + GC_toggleref_array_size); + } else { + r -> weak_ref = GC_HIDE_POINTER(obj); + GC_ASSERT((r -> weak_ref & 1) != 0); + } + GC_toggleref_array_size++; + } + } + UNLOCK(); + return res; + } +#endif /* !GC_TOGGLE_REFS_NOT_NEEDED */ + +/* Finalizer callback support. */ +STATIC GC_await_finalize_proc GC_object_finalized_proc = 0; + +GC_API void GC_CALL GC_set_await_finalize_proc(GC_await_finalize_proc fn) +{ + LOCK(); + GC_object_finalized_proc = fn; + UNLOCK(); +} + +GC_API GC_await_finalize_proc GC_CALL GC_get_await_finalize_proc(void) +{ + GC_await_finalize_proc fn; + + READER_LOCK(); + fn = GC_object_finalized_proc; + READER_UNLOCK(); + return fn; +} + +#ifndef GC_LONG_REFS_NOT_NEEDED + GC_API int GC_CALL GC_register_long_link(void * * link, const void * obj) + { + if (((word)link & (ALIGNMENT-1)) != 0 || !NONNULL_ARG_NOT_NULL(link)) + ABORT("Bad arg to GC_register_long_link"); + return GC_register_disappearing_link_inner(&GC_ll_hashtbl, link, obj, + "long dl"); + } + + GC_API int GC_CALL GC_unregister_long_link(void * * link) + { + struct disappearing_link *curr_dl; + + if (((word)link & (ALIGNMENT-1)) != 0) return 0; /* Nothing to do. */ + + LOCK(); + curr_dl = GC_unregister_disappearing_link_inner(&GC_ll_hashtbl, link); + UNLOCK(); + if (NULL == curr_dl) return 0; + FREE_DL_ENTRY(curr_dl); + return 1; + } +#endif /* !GC_LONG_REFS_NOT_NEEDED */ + +#ifndef GC_MOVE_DISAPPEARING_LINK_NOT_NEEDED + STATIC int GC_move_disappearing_link_inner( + struct dl_hashtbl_s *dl_hashtbl, + void **link, void **new_link) + { + struct disappearing_link *curr_dl, *new_dl; + struct disappearing_link *prev_dl = NULL; + size_t curr_index, new_index; + word curr_hidden_link, new_hidden_link; + +# ifdef GC_ASSERTIONS + GC_noop1((word)(*new_link)); +# endif + GC_ASSERT(I_HOLD_LOCK()); + if (EXPECT(NULL == dl_hashtbl -> head, FALSE)) return GC_NOT_FOUND; + + /* Find current link. */ + curr_index = HASH2(link, dl_hashtbl -> log_size); + curr_hidden_link = GC_HIDE_POINTER(link); + for (curr_dl = dl_hashtbl -> head[curr_index]; curr_dl; + curr_dl = dl_next(curr_dl)) { + if (curr_dl -> dl_hidden_link == curr_hidden_link) + break; + prev_dl = curr_dl; + } + if (EXPECT(NULL == curr_dl, FALSE)) { + return GC_NOT_FOUND; + } else if (link == new_link) { + return GC_SUCCESS; /* Nothing to do. */ + } + + /* link found; now check new_link not present. */ + new_index = HASH2(new_link, dl_hashtbl -> log_size); + new_hidden_link = GC_HIDE_POINTER(new_link); + for (new_dl = dl_hashtbl -> head[new_index]; new_dl; + new_dl = dl_next(new_dl)) { + if (new_dl -> dl_hidden_link == new_hidden_link) { + /* Target already registered; bail. */ + return GC_DUPLICATE; + } + } + + /* Remove from old, add to new, update link. */ + if (NULL == prev_dl) { + dl_hashtbl -> head[curr_index] = dl_next(curr_dl); + } else { + dl_set_next(prev_dl, dl_next(curr_dl)); + GC_dirty(prev_dl); + } + curr_dl -> dl_hidden_link = new_hidden_link; + dl_set_next(curr_dl, dl_hashtbl -> head[new_index]); + dl_hashtbl -> head[new_index] = curr_dl; + GC_dirty(curr_dl); + GC_dirty(dl_hashtbl->head); /* entire object */ + return GC_SUCCESS; + } + + GC_API int GC_CALL GC_move_disappearing_link(void **link, void **new_link) + { + int result; + + if (((word)new_link & (ALIGNMENT-1)) != 0 + || !NONNULL_ARG_NOT_NULL(new_link)) + ABORT("Bad new_link arg to GC_move_disappearing_link"); + if (((word)link & (ALIGNMENT-1)) != 0) + return GC_NOT_FOUND; /* Nothing to do. */ + + LOCK(); + result = GC_move_disappearing_link_inner(&GC_dl_hashtbl, link, new_link); + UNLOCK(); + return result; + } + +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_API int GC_CALL GC_move_long_link(void **link, void **new_link) + { + int result; + + if (((word)new_link & (ALIGNMENT-1)) != 0 + || !NONNULL_ARG_NOT_NULL(new_link)) + ABORT("Bad new_link arg to GC_move_long_link"); + if (((word)link & (ALIGNMENT-1)) != 0) + return GC_NOT_FOUND; /* Nothing to do. */ + + LOCK(); + result = GC_move_disappearing_link_inner(&GC_ll_hashtbl, link, new_link); + UNLOCK(); + return result; + } +# endif /* !GC_LONG_REFS_NOT_NEEDED */ +#endif /* !GC_MOVE_DISAPPEARING_LINK_NOT_NEEDED */ + +/* Possible finalization_marker procedures. Note that mark stack */ +/* overflow is handled by the caller, and is not a disaster. */ +#if defined(_MSC_VER) && defined(I386) + GC_ATTR_NOINLINE + /* Otherwise some optimizer bug is tickled in VC for x86 (v19, at least). */ +#endif +STATIC void GC_normal_finalize_mark_proc(ptr_t p) +{ + GC_mark_stack_top = GC_push_obj(p, HDR(p), GC_mark_stack_top, + GC_mark_stack + GC_mark_stack_size); +} + +/* This only pays very partial attention to the mark descriptor. */ +/* It does the right thing for normal and atomic objects, and treats */ +/* most others as normal. */ +STATIC void GC_ignore_self_finalize_mark_proc(ptr_t p) +{ + hdr * hhdr = HDR(p); + word descr = hhdr -> hb_descr; + ptr_t current_p; + ptr_t scan_limit; + ptr_t target_limit = p + hhdr -> hb_sz - 1; + + if ((descr & GC_DS_TAGS) == GC_DS_LENGTH) { + scan_limit = p + descr - sizeof(word); + } else { + scan_limit = target_limit + 1 - sizeof(word); + } + for (current_p = p; (word)current_p <= (word)scan_limit; + current_p += ALIGNMENT) { + word q; + + LOAD_WORD_OR_CONTINUE(q, current_p); + if (q < (word)p || q > (word)target_limit) { + GC_PUSH_ONE_HEAP(q, current_p, GC_mark_stack_top); + } + } +} + +STATIC void GC_null_finalize_mark_proc(ptr_t p) +{ + UNUSED_ARG(p); +} + +/* Possible finalization_marker procedures. Note that mark stack */ +/* overflow is handled by the caller, and is not a disaster. */ + +/* GC_unreachable_finalize_mark_proc is an alias for normal marking, */ +/* but it is explicitly tested for, and triggers different */ +/* behavior. Objects registered in this way are not finalized */ +/* if they are reachable by other finalizable objects, even if those */ +/* other objects specify no ordering. */ +STATIC void GC_unreachable_finalize_mark_proc(ptr_t p) +{ + /* A dummy comparison to ensure the compiler not to optimize two */ + /* identical functions into a single one (thus, to ensure a unique */ + /* address of each). Alternatively, GC_noop1(p) could be used. */ + if (EXPECT(NULL == p, FALSE)) return; + + GC_normal_finalize_mark_proc(p); +} + +static GC_bool need_unreachable_finalization = FALSE; + /* Avoid the work if this is not used. */ + /* TODO: turn need_unreachable_finalization into a counter */ + +/* Register a finalization function. See gc.h for details. */ +/* The last parameter is a procedure that determines */ +/* marking for finalization ordering. Any objects marked */ +/* by that procedure will be guaranteed to not have been */ +/* finalized when this finalizer is invoked. */ +STATIC void GC_register_finalizer_inner(void * obj, + GC_finalization_proc fn, void *cd, + GC_finalization_proc *ofn, void **ocd, + finalization_mark_proc mp) +{ + struct finalizable_object * curr_fo; + size_t index; + struct finalizable_object *new_fo = 0; + hdr *hhdr = NULL; /* initialized to prevent warning. */ + + GC_ASSERT(GC_is_initialized); + if (EXPECT(GC_find_leak, FALSE)) { + /* No-op. *ocd and *ofn remain unchanged. */ + return; + } + LOCK(); + GC_ASSERT(obj != NULL && GC_base_C(obj) == obj); + if (mp == GC_unreachable_finalize_mark_proc) + need_unreachable_finalization = TRUE; + if (EXPECT(NULL == GC_fnlz_roots.fo_head, FALSE) + || EXPECT(GC_fo_entries > ((word)1 << GC_log_fo_table_size), FALSE)) { + GC_grow_table((struct hash_chain_entry ***)&GC_fnlz_roots.fo_head, + &GC_log_fo_table_size, &GC_fo_entries); + GC_COND_LOG_PRINTF("Grew fo table to %u entries\n", + 1U << GC_log_fo_table_size); + } + for (;;) { + struct finalizable_object *prev_fo = NULL; + GC_oom_func oom_fn; + + index = HASH2(obj, GC_log_fo_table_size); + curr_fo = GC_fnlz_roots.fo_head[index]; + while (curr_fo != 0) { + GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object)); + if (curr_fo -> fo_hidden_base == GC_HIDE_POINTER(obj)) { + /* Interruption by a signal in the middle of this */ + /* should be safe. The client may see only *ocd */ + /* updated, but we'll declare that to be his problem. */ + if (ocd) *ocd = (void *)(curr_fo -> fo_client_data); + if (ofn) *ofn = curr_fo -> fo_fn; + /* Delete the structure for obj. */ + if (prev_fo == 0) { + GC_fnlz_roots.fo_head[index] = fo_next(curr_fo); + } else { + fo_set_next(prev_fo, fo_next(curr_fo)); + GC_dirty(prev_fo); + } + if (fn == 0) { + GC_fo_entries--; + /* May not happen if we get a signal. But a high */ + /* estimate will only make the table larger than */ + /* necessary. */ +# if !defined(THREADS) && !defined(DBG_HDRS_ALL) + GC_free((void *)curr_fo); +# endif + } else { + curr_fo -> fo_fn = fn; + curr_fo -> fo_client_data = (ptr_t)cd; + curr_fo -> fo_mark_proc = mp; + GC_dirty(curr_fo); + /* Reinsert it. We deleted it first to maintain */ + /* consistency in the event of a signal. */ + if (prev_fo == 0) { + GC_fnlz_roots.fo_head[index] = curr_fo; + } else { + fo_set_next(prev_fo, curr_fo); + GC_dirty(prev_fo); + } + } + if (NULL == prev_fo) + GC_dirty(GC_fnlz_roots.fo_head + index); + UNLOCK(); +# ifndef DBG_HDRS_ALL + /* Free unused new_fo returned by GC_oom_fn() */ + GC_free((void *)new_fo); +# endif + return; + } + prev_fo = curr_fo; + curr_fo = fo_next(curr_fo); + } + if (EXPECT(new_fo != 0, FALSE)) { + /* new_fo is returned by GC_oom_fn(). */ + GC_ASSERT(fn != 0); +# ifdef LINT2 + if (NULL == hhdr) ABORT("Bad hhdr in GC_register_finalizer_inner"); +# endif + break; + } + if (fn == 0) { + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + UNLOCK(); + return; + } + GET_HDR(obj, hhdr); + if (EXPECT(0 == hhdr, FALSE)) { + /* We won't collect it, hence finalizer wouldn't be run. */ + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + UNLOCK(); + return; + } + new_fo = (struct finalizable_object *) + GC_INTERNAL_MALLOC(sizeof(struct finalizable_object), NORMAL); + if (EXPECT(new_fo != 0, TRUE)) + break; + oom_fn = GC_oom_fn; + UNLOCK(); + new_fo = (struct finalizable_object *) + (*oom_fn)(sizeof(struct finalizable_object)); + if (0 == new_fo) { + /* No enough memory. *ocd and *ofn remain unchanged. */ + return; + } + /* It's not likely we'll make it here, but ... */ + LOCK(); + /* Recalculate index since the table may grow and */ + /* check again that our finalizer is not in the table. */ + } + GC_ASSERT(GC_size(new_fo) >= sizeof(struct finalizable_object)); + if (ocd) *ocd = 0; + if (ofn) *ofn = 0; + new_fo -> fo_hidden_base = GC_HIDE_POINTER(obj); + new_fo -> fo_fn = fn; + new_fo -> fo_client_data = (ptr_t)cd; + new_fo -> fo_object_size = hhdr -> hb_sz; + new_fo -> fo_mark_proc = mp; + fo_set_next(new_fo, GC_fnlz_roots.fo_head[index]); + GC_dirty(new_fo); + GC_fo_entries++; + GC_fnlz_roots.fo_head[index] = new_fo; + GC_dirty(GC_fnlz_roots.fo_head + index); + UNLOCK(); +} + +GC_API void GC_CALL GC_register_finalizer(void * obj, + GC_finalization_proc fn, void * cd, + GC_finalization_proc *ofn, void ** ocd) +{ + GC_register_finalizer_inner(obj, fn, cd, ofn, + ocd, GC_normal_finalize_mark_proc); +} + +GC_API void GC_CALL GC_register_finalizer_ignore_self(void * obj, + GC_finalization_proc fn, void * cd, + GC_finalization_proc *ofn, void ** ocd) +{ + GC_register_finalizer_inner(obj, fn, cd, ofn, + ocd, GC_ignore_self_finalize_mark_proc); +} + +GC_API void GC_CALL GC_register_finalizer_no_order(void * obj, + GC_finalization_proc fn, void * cd, + GC_finalization_proc *ofn, void ** ocd) +{ + GC_register_finalizer_inner(obj, fn, cd, ofn, + ocd, GC_null_finalize_mark_proc); +} + +GC_API void GC_CALL GC_register_finalizer_unreachable(void * obj, + GC_finalization_proc fn, void * cd, + GC_finalization_proc *ofn, void ** ocd) +{ + GC_ASSERT(GC_java_finalization); + GC_register_finalizer_inner(obj, fn, cd, ofn, + ocd, GC_unreachable_finalize_mark_proc); +} + +#ifndef NO_DEBUGGING + STATIC void GC_dump_finalization_links( + const struct dl_hashtbl_s *dl_hashtbl) + { + size_t dl_size = (size_t)1 << dl_hashtbl -> log_size; + size_t i; + + if (NULL == dl_hashtbl -> head) return; /* empty table */ + + for (i = 0; i < dl_size; i++) { + struct disappearing_link *curr_dl; + + for (curr_dl = dl_hashtbl -> head[i]; curr_dl != 0; + curr_dl = dl_next(curr_dl)) { + ptr_t real_ptr = (ptr_t)GC_REVEAL_POINTER(curr_dl -> dl_hidden_obj); + ptr_t real_link = (ptr_t)GC_REVEAL_POINTER(curr_dl -> dl_hidden_link); + + GC_printf("Object: %p, link value: %p, link addr: %p\n", + (void *)real_ptr, *(void **)real_link, (void *)real_link); + } + } + } + + GC_API void GC_CALL GC_dump_finalization(void) + { + struct finalizable_object * curr_fo; + size_t i; + size_t fo_size = GC_fnlz_roots.fo_head == NULL ? 0 : + (size_t)1 << GC_log_fo_table_size; + + GC_printf("\n***Disappearing (short) links:\n"); + GC_dump_finalization_links(&GC_dl_hashtbl); +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_printf("\n***Disappearing long links:\n"); + GC_dump_finalization_links(&GC_ll_hashtbl); +# endif + GC_printf("\n***Finalizers:\n"); + for (i = 0; i < fo_size; i++) { + for (curr_fo = GC_fnlz_roots.fo_head[i]; + curr_fo != NULL; curr_fo = fo_next(curr_fo)) { + ptr_t real_ptr = (ptr_t)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base); + + GC_printf("Finalizable object: %p\n", (void *)real_ptr); + } + } + } +#endif /* !NO_DEBUGGING */ + +#ifndef SMALL_CONFIG + STATIC word GC_old_dl_entries = 0; /* for stats printing */ +# ifndef GC_LONG_REFS_NOT_NEEDED + STATIC word GC_old_ll_entries = 0; +# endif +#endif /* !SMALL_CONFIG */ + +#ifndef THREADS + /* Global variables to minimize the level of recursion when a client */ + /* finalizer allocates memory. */ + STATIC int GC_finalizer_nested = 0; + /* Only the lowest byte is used, the rest is */ + /* padding for proper global data alignment */ + /* required for some compilers (like Watcom). */ + STATIC unsigned GC_finalizer_skipped = 0; + + /* Checks and updates the level of finalizers recursion. */ + /* Returns NULL if GC_invoke_finalizers() should not be called by the */ + /* collector (to minimize the risk of a deep finalizers recursion), */ + /* otherwise returns a pointer to GC_finalizer_nested. */ + STATIC unsigned char *GC_check_finalizer_nested(void) + { + unsigned nesting_level = *(unsigned char *)&GC_finalizer_nested; + if (nesting_level) { + /* We are inside another GC_invoke_finalizers(). */ + /* Skip some implicitly-called GC_invoke_finalizers() */ + /* depending on the nesting (recursion) level. */ + if (++GC_finalizer_skipped < (1U << nesting_level)) return NULL; + GC_finalizer_skipped = 0; + } + *(char *)&GC_finalizer_nested = (char)(nesting_level + 1); + return (unsigned char *)&GC_finalizer_nested; + } +#endif /* !THREADS */ + +GC_INLINE void GC_make_disappearing_links_disappear( + struct dl_hashtbl_s* dl_hashtbl, + GC_bool is_remove_dangling) +{ + size_t i; + size_t dl_size = (size_t)1 << dl_hashtbl -> log_size; + GC_bool needs_barrier = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); + if (NULL == dl_hashtbl -> head) return; /* empty table */ + + for (i = 0; i < dl_size; i++) { + struct disappearing_link *curr_dl, *next_dl; + struct disappearing_link *prev_dl = NULL; + + for (curr_dl = dl_hashtbl->head[i]; curr_dl != NULL; curr_dl = next_dl) { + next_dl = dl_next(curr_dl); +# if defined(GC_ASSERTIONS) && !defined(THREAD_SANITIZER) + /* Check accessibility of the location pointed by link. */ + GC_noop1(*(word *)GC_REVEAL_POINTER(curr_dl -> dl_hidden_link)); +# endif + if (is_remove_dangling) { + ptr_t real_link = (ptr_t)GC_base(GC_REVEAL_POINTER( + curr_dl -> dl_hidden_link)); + + if (NULL == real_link || EXPECT(GC_is_marked(real_link), TRUE)) { + prev_dl = curr_dl; + continue; + } + } else { + if (EXPECT(GC_is_marked((ptr_t)GC_REVEAL_POINTER( + curr_dl -> dl_hidden_obj)), TRUE)) { + prev_dl = curr_dl; + continue; + } + *(ptr_t *)GC_REVEAL_POINTER(curr_dl -> dl_hidden_link) = NULL; + } + + /* Delete curr_dl entry from dl_hashtbl. */ + if (NULL == prev_dl) { + dl_hashtbl -> head[i] = next_dl; + needs_barrier = TRUE; + } else { + dl_set_next(prev_dl, next_dl); + GC_dirty(prev_dl); + } + GC_clear_mark_bit(curr_dl); + dl_hashtbl -> entries--; + } + } + if (needs_barrier) + GC_dirty(dl_hashtbl -> head); /* entire object */ +} + +/* Cause disappearing links to disappear and unreachable objects to be */ +/* enqueued for finalization. Called with the world running. */ +GC_INNER void GC_finalize(void) +{ + struct finalizable_object * curr_fo, * prev_fo, * next_fo; + ptr_t real_ptr; + size_t i; + size_t fo_size = GC_fnlz_roots.fo_head == NULL ? 0 : + (size_t)1 << GC_log_fo_table_size; + GC_bool needs_barrier = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); +# ifndef SMALL_CONFIG + /* Save current GC_[dl/ll]_entries value for stats printing */ + GC_old_dl_entries = GC_dl_hashtbl.entries; +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_old_ll_entries = GC_ll_hashtbl.entries; +# endif +# endif + +# ifndef GC_TOGGLE_REFS_NOT_NEEDED + GC_mark_togglerefs(); +# endif + GC_make_disappearing_links_disappear(&GC_dl_hashtbl, FALSE); + + /* Mark all objects reachable via chains of 1 or more pointers */ + /* from finalizable objects. */ + GC_ASSERT(!GC_collection_in_progress()); + for (i = 0; i < fo_size; i++) { + for (curr_fo = GC_fnlz_roots.fo_head[i]; + curr_fo != NULL; curr_fo = fo_next(curr_fo)) { + GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object)); + real_ptr = (ptr_t)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base); + if (!GC_is_marked(real_ptr)) { + GC_MARKED_FOR_FINALIZATION(real_ptr); + GC_mark_fo(real_ptr, curr_fo -> fo_mark_proc); + if (GC_is_marked(real_ptr)) { + WARN("Finalization cycle involving %p\n", real_ptr); + } + } + } + } + /* Enqueue for finalization all objects that are still */ + /* unreachable. */ + GC_bytes_finalized = 0; + for (i = 0; i < fo_size; i++) { + curr_fo = GC_fnlz_roots.fo_head[i]; + prev_fo = NULL; + while (curr_fo != NULL) { + real_ptr = (ptr_t)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base); + if (!GC_is_marked(real_ptr)) { + if (!GC_java_finalization) { + GC_set_mark_bit(real_ptr); + } + /* Delete from hash table. */ + next_fo = fo_next(curr_fo); + if (NULL == prev_fo) { + GC_fnlz_roots.fo_head[i] = next_fo; + if (GC_object_finalized_proc) { + GC_dirty(GC_fnlz_roots.fo_head + i); + } else { + needs_barrier = TRUE; + } + } else { + fo_set_next(prev_fo, next_fo); + GC_dirty(prev_fo); + } + GC_fo_entries--; + if (GC_object_finalized_proc) + GC_object_finalized_proc(real_ptr); + + /* Add to list of objects awaiting finalization. */ + fo_set_next(curr_fo, GC_fnlz_roots.finalize_now); + GC_dirty(curr_fo); + SET_FINALIZE_NOW(curr_fo); + /* Unhide object pointer so any future collections will */ + /* see it. */ + curr_fo -> fo_hidden_base = + (word)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base); + GC_bytes_finalized += + curr_fo -> fo_object_size + + sizeof(struct finalizable_object); + GC_ASSERT(GC_is_marked(GC_base(curr_fo))); + curr_fo = next_fo; + } else { + prev_fo = curr_fo; + curr_fo = fo_next(curr_fo); + } + } + } + + if (GC_java_finalization) { + /* Make sure we mark everything reachable from objects finalized */ + /* using the no-order fo_mark_proc. */ + for (curr_fo = GC_fnlz_roots.finalize_now; + curr_fo != NULL; curr_fo = fo_next(curr_fo)) { + real_ptr = (ptr_t)(curr_fo -> fo_hidden_base); /* revealed */ + if (!GC_is_marked(real_ptr)) { + if (curr_fo -> fo_mark_proc == GC_null_finalize_mark_proc) { + GC_mark_fo(real_ptr, GC_normal_finalize_mark_proc); + } + if (curr_fo -> fo_mark_proc != GC_unreachable_finalize_mark_proc) { + GC_set_mark_bit(real_ptr); + } + } + } + + /* Now revive finalize-when-unreachable objects reachable from */ + /* other finalizable objects. */ + if (need_unreachable_finalization) { + curr_fo = GC_fnlz_roots.finalize_now; + GC_ASSERT(NULL == curr_fo || GC_fnlz_roots.fo_head != NULL); + for (prev_fo = NULL; curr_fo != NULL; + prev_fo = curr_fo, curr_fo = next_fo) { + next_fo = fo_next(curr_fo); + if (curr_fo -> fo_mark_proc != GC_unreachable_finalize_mark_proc) + continue; + + real_ptr = (ptr_t)(curr_fo -> fo_hidden_base); /* revealed */ + if (!GC_is_marked(real_ptr)) { + GC_set_mark_bit(real_ptr); + continue; + } + if (NULL == prev_fo) { + SET_FINALIZE_NOW(next_fo); + } else { + fo_set_next(prev_fo, next_fo); + GC_dirty(prev_fo); + } + curr_fo -> fo_hidden_base = GC_HIDE_POINTER(real_ptr); + GC_bytes_finalized -= + (curr_fo -> fo_object_size) + sizeof(struct finalizable_object); + + i = HASH2(real_ptr, GC_log_fo_table_size); + fo_set_next(curr_fo, GC_fnlz_roots.fo_head[i]); + GC_dirty(curr_fo); + GC_fo_entries++; + GC_fnlz_roots.fo_head[i] = curr_fo; + curr_fo = prev_fo; + needs_barrier = TRUE; + } + } + } + if (needs_barrier) + GC_dirty(GC_fnlz_roots.fo_head); /* entire object */ + + /* Remove dangling disappearing links. */ + GC_make_disappearing_links_disappear(&GC_dl_hashtbl, TRUE); + +# ifndef GC_TOGGLE_REFS_NOT_NEEDED + GC_clear_togglerefs(); +# endif +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_make_disappearing_links_disappear(&GC_ll_hashtbl, FALSE); + GC_make_disappearing_links_disappear(&GC_ll_hashtbl, TRUE); +# endif + + if (GC_fail_count) { + /* Don't prevent running finalizers if there has been an allocation */ + /* failure recently. */ +# ifdef THREADS + GC_reset_finalizer_nested(); +# else + GC_finalizer_nested = 0; +# endif + } +} + +/* Count of finalizers to run, at most, during a single invocation */ +/* of GC_invoke_finalizers(); zero means no limit. Accessed with the */ +/* allocator lock held. */ +STATIC unsigned GC_interrupt_finalizers = 0; + +#ifndef JAVA_FINALIZATION_NOT_NEEDED + + /* Enqueue all remaining finalizers to be run. */ + /* A collection in progress, if any, is completed */ + /* when the first finalizer is enqueued. */ + STATIC void GC_enqueue_all_finalizers(void) + { + size_t i; + size_t fo_size = GC_fnlz_roots.fo_head == NULL ? 0 : + (size_t)1 << GC_log_fo_table_size; + + GC_ASSERT(I_HOLD_LOCK()); + GC_bytes_finalized = 0; + for (i = 0; i < fo_size; i++) { + struct finalizable_object * curr_fo = GC_fnlz_roots.fo_head[i]; + + GC_fnlz_roots.fo_head[i] = NULL; + while (curr_fo != NULL) { + struct finalizable_object * next_fo; + ptr_t real_ptr = (ptr_t)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base); + + GC_mark_fo(real_ptr, GC_normal_finalize_mark_proc); + GC_set_mark_bit(real_ptr); + GC_complete_ongoing_collection(); + next_fo = fo_next(curr_fo); + + /* Add to list of objects awaiting finalization. */ + fo_set_next(curr_fo, GC_fnlz_roots.finalize_now); + GC_dirty(curr_fo); + SET_FINALIZE_NOW(curr_fo); + + /* Unhide object pointer so any future collections will */ + /* see it. */ + curr_fo -> fo_hidden_base = + (word)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base); + GC_bytes_finalized += + curr_fo -> fo_object_size + sizeof(struct finalizable_object); + curr_fo = next_fo; + } + } + GC_fo_entries = 0; /* all entries deleted from the hash table */ + } + + /* Invoke all remaining finalizers that haven't yet been run. + * This is needed for strict compliance with the Java standard, + * which can make the runtime guarantee that all finalizers are run. + * Unfortunately, the Java standard implies we have to keep running + * finalizers until there are no more left, a potential infinite loop. + * YUCK. + * Note that this is even more dangerous than the usual Java + * finalizers, in that objects reachable from static variables + * may have been finalized when these finalizers are run. + * Finalizers run at this point must be prepared to deal with a + * mostly broken world. + */ + GC_API void GC_CALL GC_finalize_all(void) + { + LOCK(); + while (GC_fo_entries > 0) { + GC_enqueue_all_finalizers(); + GC_interrupt_finalizers = 0; /* reset */ + UNLOCK(); + GC_invoke_finalizers(); + /* Running the finalizers in this thread is arguably not a good */ + /* idea when we should be notifying another thread to run them. */ + /* But otherwise we don't have a great way to wait for them to */ + /* run. */ + LOCK(); + } + UNLOCK(); + } + +#endif /* !JAVA_FINALIZATION_NOT_NEEDED */ + +GC_API void GC_CALL GC_set_interrupt_finalizers(unsigned value) +{ + LOCK(); + GC_interrupt_finalizers = value; + UNLOCK(); +} + +GC_API unsigned GC_CALL GC_get_interrupt_finalizers(void) +{ + unsigned value; + + READER_LOCK(); + value = GC_interrupt_finalizers; + READER_UNLOCK(); + return value; +} + +/* Returns true if it is worth calling GC_invoke_finalizers. (Useful if */ +/* finalizers can only be called from some kind of "safe state" and */ +/* getting into that safe state is expensive.) */ +GC_API int GC_CALL GC_should_invoke_finalizers(void) +{ +# ifdef AO_HAVE_load + return AO_load((volatile AO_t *)&GC_fnlz_roots.finalize_now) != 0; +# else + return GC_fnlz_roots.finalize_now != NULL; +# endif /* !THREADS */ +} + +/* Invoke finalizers for all objects that are ready to be finalized. */ +GC_API int GC_CALL GC_invoke_finalizers(void) +{ + int count = 0; + word bytes_freed_before = 0; /* initialized to prevent warning. */ + + GC_ASSERT(I_DONT_HOLD_LOCK()); + while (GC_should_invoke_finalizers()) { + struct finalizable_object * curr_fo; + ptr_t real_ptr; + + LOCK(); + if (count == 0) { + bytes_freed_before = GC_bytes_freed; + /* Note: we hold the allocator lock here. */ + } else if (EXPECT(GC_interrupt_finalizers != 0, FALSE) + && (unsigned)count >= GC_interrupt_finalizers) { + UNLOCK(); + break; + } + curr_fo = GC_fnlz_roots.finalize_now; +# ifdef THREADS + if (EXPECT(NULL == curr_fo, FALSE)) { + UNLOCK(); + break; + } +# endif + SET_FINALIZE_NOW(fo_next(curr_fo)); + UNLOCK(); + fo_set_next(curr_fo, 0); + real_ptr = (ptr_t)(curr_fo -> fo_hidden_base); /* revealed */ + (*(curr_fo -> fo_fn))(real_ptr, curr_fo -> fo_client_data); + curr_fo -> fo_client_data = 0; + ++count; + /* Explicit freeing of curr_fo is probably a bad idea. */ + /* It throws off accounting if nearly all objects are */ + /* finalizable. Otherwise it should not matter. */ + } + /* bytes_freed_before is initialized whenever count != 0 */ + if (count != 0 +# if defined(THREADS) && !defined(THREAD_SANITIZER) + /* A quick check whether some memory was freed. */ + /* The race with GC_free() is safe to be ignored */ + /* because we only need to know if the current */ + /* thread has deallocated something. */ + && bytes_freed_before != GC_bytes_freed +# endif + ) { + LOCK(); + GC_finalizer_bytes_freed += (GC_bytes_freed - bytes_freed_before); + UNLOCK(); + } + return count; +} + +static word last_finalizer_notification = 0; + +GC_INNER void GC_notify_or_invoke_finalizers(void) +{ + GC_finalizer_notifier_proc notifier_fn = 0; +# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) + static word last_back_trace_gc_no = 1; /* Skip first one. */ +# endif + +# if defined(THREADS) && !defined(KEEP_BACK_PTRS) \ + && !defined(MAKE_BACK_GRAPH) + /* Quick check (while unlocked) for an empty finalization queue. */ + if (!GC_should_invoke_finalizers()) + return; +# endif + LOCK(); + + /* This is a convenient place to generate backtraces if appropriate, */ + /* since that code is not callable with the allocator lock. */ +# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) + if (GC_gc_no != last_back_trace_gc_no) { +# ifdef KEEP_BACK_PTRS + static GC_bool bt_in_progress = FALSE; + + if (!bt_in_progress) { + long i; + + bt_in_progress = TRUE; /* prevent recursion or parallel usage */ + for (i = 0; i < GC_backtraces; ++i) { + /* FIXME: This tolerates concurrent heap mutation, which */ + /* may cause occasional mysterious results. We need to */ + /* release the allocator lock, since GC_print_callers() */ + /* acquires it. It probably shouldn't. */ + void *current = GC_generate_random_valid_address(); + + UNLOCK(); + GC_printf("\n****Chosen address %p in object\n", current); + GC_print_backtrace(current); + LOCK(); + } + bt_in_progress = FALSE; + } +# endif + last_back_trace_gc_no = GC_gc_no; +# ifdef MAKE_BACK_GRAPH + if (GC_print_back_height) { + GC_print_back_graph_stats(); + } +# endif + } +# endif + if (NULL == GC_fnlz_roots.finalize_now) { + UNLOCK(); + return; + } + + if (!GC_finalize_on_demand) { + unsigned char *pnested; + +# ifdef THREADS + if (EXPECT(GC_in_thread_creation, FALSE)) { + UNLOCK(); + return; + } +# endif + pnested = GC_check_finalizer_nested(); + UNLOCK(); + /* Skip GC_invoke_finalizers() if nested. */ + if (pnested != NULL) { + (void)GC_invoke_finalizers(); + *pnested = 0; /* Reset since no more finalizers or interrupted. */ +# ifndef THREADS + GC_ASSERT(NULL == GC_fnlz_roots.finalize_now + || GC_interrupt_finalizers > 0); +# endif /* Otherwise GC can run concurrently and add more */ + } + return; + } + + /* These variables require synchronization to avoid data race. */ + if (last_finalizer_notification != GC_gc_no) { + notifier_fn = GC_finalizer_notifier; + last_finalizer_notification = GC_gc_no; + } + UNLOCK(); + if (notifier_fn != 0) + (*notifier_fn)(); /* Invoke the notifier */ +} + +#ifndef SMALL_CONFIG +# ifndef GC_LONG_REFS_NOT_NEEDED +# define IF_LONG_REFS_PRESENT_ELSE(x,y) (x) +# else +# define IF_LONG_REFS_PRESENT_ELSE(x,y) (y) +# endif + + GC_INNER void GC_print_finalization_stats(void) + { + struct finalizable_object *fo; + unsigned long ready = 0; + + GC_log_printf("%lu finalization entries;" + " %lu/%lu short/long disappearing links alive\n", + (unsigned long)GC_fo_entries, + (unsigned long)GC_dl_hashtbl.entries, + (unsigned long)IF_LONG_REFS_PRESENT_ELSE( + GC_ll_hashtbl.entries, 0)); + + for (fo = GC_fnlz_roots.finalize_now; fo != NULL; fo = fo_next(fo)) + ++ready; + GC_log_printf("%lu finalization-ready objects;" + " %ld/%ld short/long links cleared\n", + ready, + (long)GC_old_dl_entries - (long)GC_dl_hashtbl.entries, + (long)IF_LONG_REFS_PRESENT_ELSE( + GC_old_ll_entries - GC_ll_hashtbl.entries, 0)); + } +#endif /* !SMALL_CONFIG */ + +#endif /* !GC_NO_FINALIZATION */ + +/* + * Copyright (c) 2011 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + + + +#ifdef ENABLE_DISCLAIM + +#include "gc/gc_disclaim.h" + + +#if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) + /* The first bit is already used for a debug purpose. */ +# define FINALIZER_CLOSURE_FLAG 0x2 +#else +# define FINALIZER_CLOSURE_FLAG 0x1 +#endif + +STATIC int GC_CALLBACK GC_finalized_disclaim(void *obj) +{ +# ifdef AO_HAVE_load + word fc_word = (word)AO_load((volatile AO_t *)obj); +# else + word fc_word = *(word *)obj; +# endif + + if ((fc_word & FINALIZER_CLOSURE_FLAG) != 0) { + /* The disclaim function may be passed fragments from the */ + /* free-list, on which it should not run finalization. */ + /* To recognize this case, we use the fact that the first word */ + /* on such fragments is always multiple of 4 (a link to the next */ + /* fragment, or NULL). If it is desirable to have a finalizer */ + /* which does not use the first word for storing finalization */ + /* info, GC_disclaim_and_reclaim() must be extended to clear */ + /* fragments so that the assumption holds for the selected word. */ + const struct GC_finalizer_closure *fc + = (struct GC_finalizer_closure *)(fc_word + & ~(word)FINALIZER_CLOSURE_FLAG); + GC_ASSERT(!GC_find_leak); + (*fc->proc)((word *)obj + 1, fc->cd); + } + return 0; +} + +STATIC void GC_register_disclaim_proc_inner(unsigned kind, + GC_disclaim_proc proc, + GC_bool mark_unconditionally) +{ + GC_ASSERT(kind < MAXOBJKINDS); + if (EXPECT(GC_find_leak, FALSE)) return; + + GC_obj_kinds[kind].ok_disclaim_proc = proc; + GC_obj_kinds[kind].ok_mark_unconditionally = mark_unconditionally; +} + +GC_API void GC_CALL GC_init_finalized_malloc(void) +{ + GC_init(); /* In case it's not already done. */ + LOCK(); + if (GC_finalized_kind != 0) { + UNLOCK(); + return; + } + + /* The finalizer closure is placed in the first word in order to */ + /* use the lower bits to distinguish live objects from objects on */ + /* the free list. The downside of this is that we need one-word */ + /* offset interior pointers, and that GC_base does not return the */ + /* start of the user region. */ + GC_register_displacement_inner(sizeof(word)); + + /* And, the pointer to the finalizer closure object itself is */ + /* displaced due to baking in this indicator. */ + GC_register_displacement_inner(FINALIZER_CLOSURE_FLAG); + GC_register_displacement_inner(sizeof(oh) + FINALIZER_CLOSURE_FLAG); + + GC_finalized_kind = GC_new_kind_inner(GC_new_free_list_inner(), + GC_DS_LENGTH, TRUE, TRUE); + GC_ASSERT(GC_finalized_kind != 0); + GC_register_disclaim_proc_inner(GC_finalized_kind, GC_finalized_disclaim, + TRUE); + UNLOCK(); +} + +GC_API void GC_CALL GC_register_disclaim_proc(int kind, GC_disclaim_proc proc, + int mark_unconditionally) +{ + LOCK(); + GC_register_disclaim_proc_inner((unsigned)kind, proc, + (GC_bool)mark_unconditionally); + UNLOCK(); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_finalized_malloc(size_t lb, + const struct GC_finalizer_closure *fclos) +{ + void *op; + +# ifndef LINT2 /* no data race because the variable is set once */ + GC_ASSERT(GC_finalized_kind != 0); +# endif + GC_ASSERT(NONNULL_ARG_NOT_NULL(fclos)); + GC_ASSERT(((word)fclos & FINALIZER_CLOSURE_FLAG) == 0); + op = GC_malloc_kind(SIZET_SAT_ADD(lb, sizeof(word)), + (int)GC_finalized_kind); + if (EXPECT(NULL == op, FALSE)) + return NULL; +# ifdef AO_HAVE_store + AO_store((volatile AO_t *)op, (AO_t)fclos | FINALIZER_CLOSURE_FLAG); +# else + *(word *)op = (word)fclos | FINALIZER_CLOSURE_FLAG; +# endif + GC_dirty(op); + REACHABLE_AFTER_DIRTY(fclos); + return (word *)op + 1; +} + +#endif /* ENABLE_DISCLAIM */ + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +#include + +/* Allocate reclaim list for the kind. Returns TRUE on success. */ +STATIC GC_bool GC_alloc_reclaim_list(struct obj_kind *ok) +{ + struct hblk ** result; + + GC_ASSERT(I_HOLD_LOCK()); + result = (struct hblk **)GC_scratch_alloc( + (MAXOBJGRANULES+1) * sizeof(struct hblk *)); + if (EXPECT(NULL == result, FALSE)) return FALSE; + + BZERO(result, (MAXOBJGRANULES+1)*sizeof(struct hblk *)); + ok -> ok_reclaim_list = result; + return TRUE; +} + +GC_INNER ptr_t GC_alloc_large(size_t lb, int k, unsigned flags, + size_t align_m1) +{ + struct hblk * h; + size_t n_blocks; /* includes alignment */ + ptr_t result = NULL; + GC_bool retry = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); + lb = ROUNDUP_GRANULE_SIZE(lb); + n_blocks = OBJ_SZ_TO_BLOCKS_CHECKED(SIZET_SAT_ADD(lb, align_m1)); + if (!EXPECT(GC_is_initialized, TRUE)) { + UNLOCK(); /* just to unset GC_lock_holder */ + GC_init(); + LOCK(); + } + /* Do our share of marking work. */ + if (GC_incremental && !GC_dont_gc) { + ENTER_GC(); + GC_collect_a_little_inner((int)n_blocks); + EXIT_GC(); + } + + h = GC_allochblk(lb, k, flags, align_m1); +# ifdef USE_MUNMAP + if (NULL == h) { + GC_merge_unmapped(); + h = GC_allochblk(lb, k, flags, align_m1); + } +# endif + while (0 == h && GC_collect_or_expand(n_blocks, flags, retry)) { + h = GC_allochblk(lb, k, flags, align_m1); + retry = TRUE; + } + if (EXPECT(h != NULL, TRUE)) { + GC_bytes_allocd += lb; + if (lb > HBLKSIZE) { + GC_large_allocd_bytes += HBLKSIZE * OBJ_SZ_TO_BLOCKS(lb); + if (GC_large_allocd_bytes > GC_max_large_allocd_bytes) + GC_max_large_allocd_bytes = GC_large_allocd_bytes; + } + /* FIXME: Do we need some way to reset GC_max_large_allocd_bytes? */ + result = h -> hb_body; + GC_ASSERT(((word)result & align_m1) == 0); + } + return result; +} + +/* Allocate a large block of size lb bytes. Clear if appropriate. */ +/* EXTRA_BYTES were already added to lb. Update GC_bytes_allocd. */ +STATIC ptr_t GC_alloc_large_and_clear(size_t lb, int k, unsigned flags) +{ + ptr_t result; + + GC_ASSERT(I_HOLD_LOCK()); + result = GC_alloc_large(lb, k, flags, 0 /* align_m1 */); + if (EXPECT(result != NULL, TRUE) + && (GC_debugging_started || GC_obj_kinds[k].ok_init)) { + /* Clear the whole block, in case of GC_realloc call. */ + BZERO(result, HBLKSIZE * OBJ_SZ_TO_BLOCKS(lb)); + } + return result; +} + +/* Fill in additional entries in GC_size_map, including the i-th one. */ +/* Note that a filled in section of the array ending at n always */ +/* has the length of at least n/4. */ +STATIC void GC_extend_size_map(size_t i) +{ + size_t orig_granule_sz = ALLOC_REQUEST_GRANS(i); + size_t granule_sz; + size_t byte_sz = GRANULES_TO_BYTES(orig_granule_sz); + /* The size we try to preserve. */ + /* Close to i, unless this would */ + /* introduce too many distinct sizes. */ + size_t smaller_than_i = byte_sz - (byte_sz >> 3); + size_t low_limit; /* The lowest indexed entry we initialize. */ + size_t number_of_objs; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(0 == GC_size_map[i]); + if (0 == GC_size_map[smaller_than_i]) { + low_limit = byte_sz - (byte_sz >> 2); /* much smaller than i */ + granule_sz = orig_granule_sz; + while (GC_size_map[low_limit] != 0) + low_limit++; + } else { + low_limit = smaller_than_i + 1; + while (GC_size_map[low_limit] != 0) + low_limit++; + + granule_sz = ALLOC_REQUEST_GRANS(low_limit); + granule_sz += granule_sz >> 3; + if (granule_sz < orig_granule_sz) + granule_sz = orig_granule_sz; + } + + /* For these larger sizes, we use an even number of granules. */ + /* This makes it easier to, e.g., construct a 16-byte-aligned */ + /* allocator even if GC_GRANULE_BYTES is 8. */ + granule_sz = (granule_sz + 1) & ~(size_t)1; + if (granule_sz > MAXOBJGRANULES) + granule_sz = MAXOBJGRANULES; + + /* If we can fit the same number of larger objects in a block, do so. */ + number_of_objs = HBLK_GRANULES / granule_sz; + GC_ASSERT(number_of_objs != 0); + granule_sz = (HBLK_GRANULES / number_of_objs) & ~(size_t)1; + + byte_sz = GRANULES_TO_BYTES(granule_sz) - EXTRA_BYTES; + /* We may need one extra byte; do not always */ + /* fill in GC_size_map[byte_sz]. */ + + for (; low_limit <= byte_sz; low_limit++) + GC_size_map[low_limit] = granule_sz; +} + +STATIC void * GC_generic_malloc_inner_small(size_t lb, int k) +{ + struct obj_kind *ok = &GC_obj_kinds[k]; + size_t lg = GC_size_map[lb]; + void ** opp = &(ok -> ok_freelist[lg]); + void *op = *opp; + + GC_ASSERT(I_HOLD_LOCK()); + if (EXPECT(NULL == op, FALSE)) { + if (lg == 0) { + if (!EXPECT(GC_is_initialized, TRUE)) { + UNLOCK(); /* just to unset GC_lock_holder */ + GC_init(); + LOCK(); + lg = GC_size_map[lb]; + } + if (0 == lg) { + GC_extend_size_map(lb); + lg = GC_size_map[lb]; + GC_ASSERT(lg != 0); + } + /* Retry */ + opp = &(ok -> ok_freelist[lg]); + op = *opp; + } + if (NULL == op) { + if (NULL == ok -> ok_reclaim_list + && !GC_alloc_reclaim_list(ok)) + return NULL; + op = GC_allocobj(lg, k); + if (NULL == op) return NULL; + } + } + *opp = obj_link(op); + obj_link(op) = NULL; + GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + return op; +} + +GC_INNER void * GC_generic_malloc_inner(size_t lb, int k, unsigned flags) +{ + size_t lb_adjusted; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(k < MAXOBJKINDS); + if (SMALL_OBJ(lb)) { + return GC_generic_malloc_inner_small(lb, k); + } + +# if MAX_EXTRA_BYTES > 0 + if ((flags & IGNORE_OFF_PAGE) != 0 && lb >= HBLKSIZE) { + /* No need to add EXTRA_BYTES. */ + lb_adjusted = lb; + } else +# endif + /* else */ { + lb_adjusted = ADD_EXTRA_BYTES(lb); + } + return GC_alloc_large_and_clear(lb_adjusted, k, flags); +} + +#ifdef GC_COLLECT_AT_MALLOC + /* Parameter to force GC at every malloc of size greater or equal to */ + /* the given value. This might be handy during debugging. */ +# if defined(CPPCHECK) + size_t GC_dbg_collect_at_malloc_min_lb = 16*1024; /* e.g. */ +# else + size_t GC_dbg_collect_at_malloc_min_lb = (GC_COLLECT_AT_MALLOC); +# endif +#endif + +GC_INNER void * GC_generic_malloc_aligned(size_t lb, int k, unsigned flags, + size_t align_m1) +{ + void * result; + + GC_ASSERT(k < MAXOBJKINDS); + if (EXPECT(get_have_errors(), FALSE)) + GC_print_all_errors(); + GC_INVOKE_FINALIZERS(); + GC_DBG_COLLECT_AT_MALLOC(lb); + if (SMALL_OBJ(lb) && EXPECT(align_m1 < GC_GRANULE_BYTES, TRUE)) { + LOCK(); + result = GC_generic_malloc_inner_small(lb, k); + UNLOCK(); + } else { +# ifdef THREADS + size_t lg; +# endif + size_t lb_rounded; + GC_bool init; + +# if MAX_EXTRA_BYTES > 0 + if ((flags & IGNORE_OFF_PAGE) != 0 && lb >= HBLKSIZE) { + /* No need to add EXTRA_BYTES. */ + lb_rounded = ROUNDUP_GRANULE_SIZE(lb); +# ifdef THREADS + lg = BYTES_TO_GRANULES(lb_rounded); +# endif + } else +# endif + /* else */ { +# ifndef THREADS + size_t lg; /* CPPCHECK */ +# endif + + if (EXPECT(0 == lb, FALSE)) lb = 1; + lg = ALLOC_REQUEST_GRANS(lb); + lb_rounded = GRANULES_TO_BYTES(lg); + } + + init = GC_obj_kinds[k].ok_init; + if (EXPECT(align_m1 < GC_GRANULE_BYTES, TRUE)) { + align_m1 = 0; + } else if (align_m1 < HBLKSIZE) { + align_m1 = HBLKSIZE - 1; + } + LOCK(); + result = GC_alloc_large(lb_rounded, k, flags, align_m1); + if (EXPECT(result != NULL, TRUE)) { + if (GC_debugging_started +# ifndef THREADS + || init +# endif + ) { + BZERO(result, HBLKSIZE * OBJ_SZ_TO_BLOCKS(lb_rounded)); + } else { +# ifdef THREADS + GC_ASSERT(GRANULES_TO_WORDS(lg) >= 2); + /* Clear any memory that might be used for GC descriptors */ + /* before we release the allocator lock. */ + ((word *)result)[0] = 0; + ((word *)result)[1] = 0; + ((word *)result)[GRANULES_TO_WORDS(lg)-1] = 0; + ((word *)result)[GRANULES_TO_WORDS(lg)-2] = 0; +# endif + } + } + UNLOCK(); +# ifdef THREADS + if (init && !GC_debugging_started && result != NULL) { + /* Clear the rest (i.e. excluding the initial 2 words). */ + BZERO((word *)result + 2, + HBLKSIZE * OBJ_SZ_TO_BLOCKS(lb_rounded) - 2 * sizeof(word)); + } +# endif + } + if (EXPECT(NULL == result, FALSE)) + result = (*GC_get_oom_fn())(lb); /* might be misaligned */ + return result; +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_generic_malloc(size_t lb, int k) +{ + return GC_generic_malloc_aligned(lb, k, 0 /* flags */, 0 /* align_m1 */); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_kind_global(size_t lb, int k) +{ + return GC_malloc_kind_aligned_global(lb, k, 0 /* align_m1 */); +} + +GC_INNER void * GC_malloc_kind_aligned_global(size_t lb, int k, + size_t align_m1) +{ + GC_ASSERT(k < MAXOBJKINDS); + if (SMALL_OBJ(lb) && EXPECT(align_m1 < HBLKSIZE / 2, TRUE)) { + void *op; + void **opp; + size_t lg; + + GC_DBG_COLLECT_AT_MALLOC(lb); + LOCK(); + lg = GC_size_map[lb]; + opp = &GC_obj_kinds[k].ok_freelist[lg]; + op = *opp; + if (EXPECT(align_m1 >= GC_GRANULE_BYTES, FALSE)) { + /* TODO: Avoid linear search. */ + for (; ((word)op & align_m1) != 0; op = *opp) { + opp = &obj_link(op); + } + } + if (EXPECT(op != NULL, TRUE)) { + GC_ASSERT(PTRFREE == k || NULL == obj_link(op) + || ((word)obj_link(op) < GC_greatest_real_heap_addr + && (word)obj_link(op) > GC_least_real_heap_addr)); + *opp = obj_link(op); + if (k != PTRFREE) + obj_link(op) = NULL; + GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + UNLOCK(); + GC_ASSERT(((word)op & align_m1) == 0); + return op; + } + UNLOCK(); + } + + /* We make the GC_clear_stack() call a tail one, hoping to get more */ + /* of the stack. */ + return GC_clear_stack(GC_generic_malloc_aligned(lb, k, 0 /* flags */, + align_m1)); +} + +#if defined(THREADS) && !defined(THREAD_LOCAL_ALLOC) + GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_kind(size_t lb, int k) + { + return GC_malloc_kind_global(lb, k); + } +#endif + +/* Allocate lb bytes of atomic (pointer-free) data. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_atomic(size_t lb) +{ + return GC_malloc_kind(lb, PTRFREE); +} + +/* Allocate lb bytes of composite (pointerful) data. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc(size_t lb) +{ + return GC_malloc_kind(lb, NORMAL); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_generic_malloc_uncollectable( + size_t lb, int k) +{ + void *op; + size_t lb_orig = lb; + + GC_ASSERT(k < MAXOBJKINDS); + if (EXTRA_BYTES != 0 && EXPECT(lb != 0, TRUE)) lb--; + /* We do not need the extra byte, since this will */ + /* not be collected anyway. */ + + if (SMALL_OBJ(lb)) { + void **opp; + size_t lg; + + if (EXPECT(get_have_errors(), FALSE)) + GC_print_all_errors(); + GC_INVOKE_FINALIZERS(); + GC_DBG_COLLECT_AT_MALLOC(lb_orig); + LOCK(); + lg = GC_size_map[lb]; + opp = &GC_obj_kinds[k].ok_freelist[lg]; + op = *opp; + if (EXPECT(op != NULL, TRUE)) { + *opp = obj_link(op); + obj_link(op) = 0; + GC_bytes_allocd += GRANULES_TO_BYTES((word)lg); + /* Mark bit was already set on free list. It will be */ + /* cleared only temporarily during a collection, as a */ + /* result of the normal free list mark bit clearing. */ + GC_non_gc_bytes += GRANULES_TO_BYTES((word)lg); + } else { + op = GC_generic_malloc_inner_small(lb, k); + if (NULL == op) { + GC_oom_func oom_fn = GC_oom_fn; + UNLOCK(); + return (*oom_fn)(lb_orig); + } + /* For small objects, the free lists are completely marked. */ + } + GC_ASSERT(GC_is_marked(op)); + UNLOCK(); + } else { + op = GC_generic_malloc_aligned(lb, k, 0 /* flags */, 0 /* align_m1 */); + if (op /* != NULL */) { /* CPPCHECK */ + hdr * hhdr = HDR(op); + + GC_ASSERT(HBLKDISPL(op) == 0); /* large block */ + /* We do not need to acquire the allocator lock before HDR(op), */ + /* since we have an undisguised pointer, but we need it while */ + /* we adjust the mark bits. */ + LOCK(); + set_mark_bit_from_hdr(hhdr, 0); /* Only object. */ +# ifndef THREADS + GC_ASSERT(hhdr -> hb_n_marks == 0); + /* This is not guaranteed in the multi-threaded case */ + /* because the counter could be updated before locking. */ +# endif + hhdr -> hb_n_marks = 1; + UNLOCK(); + } + } + return op; +} + +/* Allocate lb bytes of pointerful, traced, but not collectible data. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_uncollectable(size_t lb) +{ + return GC_generic_malloc_uncollectable(lb, UNCOLLECTABLE); +} + +#ifdef GC_ATOMIC_UNCOLLECTABLE + /* Allocate lb bytes of pointer-free, untraced, uncollectible data */ + /* This is normally roughly equivalent to the system malloc. */ + /* But it may be useful if malloc is redefined. */ + GC_API GC_ATTR_MALLOC void * GC_CALL + GC_malloc_atomic_uncollectable(size_t lb) + { + return GC_generic_malloc_uncollectable(lb, AUNCOLLECTABLE); + } +#endif /* GC_ATOMIC_UNCOLLECTABLE */ + +#if defined(REDIRECT_MALLOC) && !defined(REDIRECT_MALLOC_IN_HEADER) + +# ifndef MSWINCE +# include +# endif + + /* Avoid unnecessary nested procedure calls here, by #defining some */ + /* malloc replacements. Otherwise we end up saving a meaningless */ + /* return address in the object. It also speeds things up, but it is */ + /* admittedly quite ugly. */ +# define GC_debug_malloc_replacement(lb) GC_debug_malloc(lb, GC_DBG_EXTRAS) + +# if defined(CPPCHECK) +# define REDIRECT_MALLOC_F GC_malloc /* e.g. */ +# else +# define REDIRECT_MALLOC_F REDIRECT_MALLOC +# endif + + void * malloc(size_t lb) + { + /* It might help to manually inline the GC_malloc call here. */ + /* But any decent compiler should reduce the extra procedure call */ + /* to at most a jump instruction in this case. */ +# if defined(I386) && defined(GC_SOLARIS_THREADS) + /* Thread initialization can call malloc before we are ready for. */ + /* It is not clear that this is enough to help matters. */ + /* The thread implementation may well call malloc at other */ + /* inopportune times. */ + if (!EXPECT(GC_is_initialized, TRUE)) return sbrk(lb); +# endif + return (void *)REDIRECT_MALLOC_F(lb); + } + +# if defined(GC_LINUX_THREADS) +# ifdef HAVE_LIBPTHREAD_SO + STATIC ptr_t GC_libpthread_start = NULL; + STATIC ptr_t GC_libpthread_end = NULL; +# endif + STATIC ptr_t GC_libld_start = NULL; + STATIC ptr_t GC_libld_end = NULL; + static GC_bool lib_bounds_set = FALSE; + + GC_INNER void GC_init_lib_bounds(void) + { + IF_CANCEL(int cancel_state;) + + /* This test does not need to ensure memory visibility, since */ + /* the bounds will be set when/if we create another thread. */ + if (EXPECT(lib_bounds_set, TRUE)) return; + + DISABLE_CANCEL(cancel_state); + GC_init(); /* if not called yet */ +# if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED) + LOCK(); /* just to set GC_lock_holder */ +# endif +# ifdef HAVE_LIBPTHREAD_SO + if (!GC_text_mapping("libpthread-", + &GC_libpthread_start, &GC_libpthread_end)) { + WARN("Failed to find libpthread.so text mapping: Expect crash\n", 0); + /* This might still work with some versions of libpthread, */ + /* so we do not abort. */ + } +# endif + if (!GC_text_mapping("ld-", &GC_libld_start, &GC_libld_end)) { + WARN("Failed to find ld.so text mapping: Expect crash\n", 0); + } +# if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED) + UNLOCK(); +# endif + RESTORE_CANCEL(cancel_state); + lib_bounds_set = TRUE; + } +# endif /* GC_LINUX_THREADS */ + + void * calloc(size_t n, size_t lb) + { + if (EXPECT((lb | n) > GC_SQRT_SIZE_MAX, FALSE) /* fast initial test */ + && lb && n > GC_SIZE_MAX / lb) + return (*GC_get_oom_fn())(GC_SIZE_MAX); /* n*lb overflow */ +# if defined(GC_LINUX_THREADS) + /* The linker may allocate some memory that is only pointed to by */ + /* mmapped thread stacks. Make sure it is not collectible. */ + { + ptr_t caller = (ptr_t)__builtin_return_address(0); + + GC_init_lib_bounds(); + if (((word)caller >= (word)GC_libld_start + && (word)caller < (word)GC_libld_end) +# ifdef HAVE_LIBPTHREAD_SO + || ((word)caller >= (word)GC_libpthread_start + && (word)caller < (word)GC_libpthread_end) + /* The two ranges are actually usually adjacent, */ + /* so there may be a way to speed this up. */ +# endif + ) { + return GC_generic_malloc_uncollectable(n * lb, UNCOLLECTABLE); + } + } +# endif + return (void *)REDIRECT_MALLOC_F(n * lb); + } + +# ifndef strdup + char *strdup(const char *s) + { + size_t lb = strlen(s) + 1; + char *result = (char *)REDIRECT_MALLOC_F(lb); + + if (EXPECT(NULL == result, FALSE)) { + errno = ENOMEM; + return NULL; + } + BCOPY(s, result, lb); + return result; + } +# endif /* !defined(strdup) */ + /* If strdup is macro defined, we assume that it actually calls malloc, */ + /* and thus the right thing will happen even without overriding it. */ + /* This seems to be true on most Linux systems. */ + +# ifndef strndup + /* This is similar to strdup(). */ + char *strndup(const char *str, size_t size) + { + char *copy; + size_t len = strlen(str); + if (EXPECT(len > size, FALSE)) + len = size; + copy = (char *)REDIRECT_MALLOC_F(len + 1); + if (EXPECT(NULL == copy, FALSE)) { + errno = ENOMEM; + return NULL; + } + if (EXPECT(len > 0, TRUE)) + BCOPY(str, copy, len); + copy[len] = '\0'; + return copy; + } +# endif /* !strndup */ + +# undef GC_debug_malloc_replacement + +#endif /* REDIRECT_MALLOC */ + +/* Explicitly deallocate the object. hhdr should correspond to p. */ +static void free_internal(void *p, hdr *hhdr) +{ + size_t sz = (size_t)(hhdr -> hb_sz); /* in bytes */ + size_t ngranules = BYTES_TO_GRANULES(sz); /* size in granules */ + int k = hhdr -> hb_obj_kind; + + GC_bytes_freed += sz; + if (IS_UNCOLLECTABLE(k)) GC_non_gc_bytes -= sz; + if (EXPECT(ngranules <= MAXOBJGRANULES, TRUE)) { + struct obj_kind *ok = &GC_obj_kinds[k]; + void **flh; + + /* It is unnecessary to clear the mark bit. If the object is */ + /* reallocated, it does not matter. Otherwise, the collector will */ + /* do it, since it is on a free list. */ + if (ok -> ok_init && EXPECT(sz > sizeof(word), TRUE)) { + BZERO((word *)p + 1, sz - sizeof(word)); + } + + flh = &(ok -> ok_freelist[ngranules]); + obj_link(p) = *flh; + *flh = (ptr_t)p; + } else { + if (sz > HBLKSIZE) { + GC_large_allocd_bytes -= HBLKSIZE * OBJ_SZ_TO_BLOCKS(sz); + } + GC_freehblk(HBLKPTR(p)); + } +} + +GC_API void GC_CALL GC_free(void * p) +{ + hdr *hhdr; + + if (p /* != NULL */) { + /* CPPCHECK */ + } else { + /* Required by ANSI. It's not my fault ... */ + return; + } + +# ifdef LOG_ALLOCS + GC_log_printf("GC_free(%p) after GC #%lu\n", + p, (unsigned long)GC_gc_no); +# endif + hhdr = HDR(p); +# if defined(REDIRECT_MALLOC) && \ + ((defined(NEED_CALLINFO) && defined(GC_HAVE_BUILTIN_BACKTRACE)) \ + || defined(GC_SOLARIS_THREADS) || defined(GC_LINUX_THREADS) \ + || defined(MSWIN32)) + /* This might be called indirectly by GC_print_callers to free */ + /* the result of backtrace_symbols. */ + /* For Solaris, we have to redirect malloc calls during */ + /* initialization. For the others, this seems to happen */ + /* implicitly. */ + /* Don't try to deallocate that memory. */ + if (EXPECT(NULL == hhdr, FALSE)) return; +# endif + GC_ASSERT(GC_base(p) == p); + LOCK(); + free_internal(p, hhdr); + UNLOCK(); +} + +#ifdef THREADS + GC_INNER void GC_free_inner(void * p) + { + GC_ASSERT(I_HOLD_LOCK()); + free_internal(p, HDR(p)); + } +#endif /* THREADS */ + +#if defined(REDIRECT_MALLOC) && !defined(REDIRECT_FREE) +# define REDIRECT_FREE GC_free +#endif + +#if defined(REDIRECT_FREE) && !defined(REDIRECT_MALLOC_IN_HEADER) + +# if defined(CPPCHECK) +# define REDIRECT_FREE_F GC_free /* e.g. */ +# else +# define REDIRECT_FREE_F REDIRECT_FREE +# endif + + void free(void * p) + { +# ifdef IGNORE_FREE +# UNUSED_ARG(p); +# else +# if defined(GC_LINUX_THREADS) && !defined(USE_PROC_FOR_LIBRARIES) + /* Don't bother with initialization checks. If nothing */ + /* has been initialized, the check fails, and that's safe, */ + /* since we have not allocated uncollectible objects neither. */ + ptr_t caller = (ptr_t)__builtin_return_address(0); + /* This test does not need to ensure memory visibility, since */ + /* the bounds will be set when/if we create another thread. */ + if (((word)caller >= (word)GC_libld_start + && (word)caller < (word)GC_libld_end) +# ifdef HAVE_LIBPTHREAD_SO + || ((word)caller >= (word)GC_libpthread_start + && (word)caller < (word)GC_libpthread_end) +# endif + ) { + GC_free(p); + return; + } +# endif + REDIRECT_FREE_F(p); +# endif + } +#endif /* REDIRECT_FREE */ + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 2000 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2009-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +/* + * These are extra allocation routines which are likely to be less + * frequently used than those in malloc.c. They are separate in the + * hope that the .o file will be excluded from statically linked + * executables. We should probably break this up further. + */ + +#include + +#ifndef MSWINCE +# include +#endif + +/* Some externally visible but unadvertised variables to allow access to */ +/* free lists from inlined allocators without including gc_priv.h */ +/* or introducing dependencies on internal data structure layouts. */ + +void ** const GC_objfreelist_ptr = GC_objfreelist; +void ** const GC_aobjfreelist_ptr = GC_aobjfreelist; +void ** const GC_uobjfreelist_ptr = GC_uobjfreelist; +# ifdef GC_ATOMIC_UNCOLLECTABLE + void ** const GC_auobjfreelist_ptr = GC_auobjfreelist; +# endif + +GC_API int GC_CALL GC_get_kind_and_size(const void * p, size_t * psize) +{ + hdr * hhdr = HDR((/* no const */ void *)(word)p); + + if (psize != NULL) { + *psize = (size_t)(hhdr -> hb_sz); + } + return hhdr -> hb_obj_kind; +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_generic_or_special_malloc(size_t lb, + int k) +{ + switch (k) { + case PTRFREE: + case NORMAL: + return GC_malloc_kind(lb, k); + case UNCOLLECTABLE: +# ifdef GC_ATOMIC_UNCOLLECTABLE + case AUNCOLLECTABLE: +# endif + return GC_generic_malloc_uncollectable(lb, k); + default: + return GC_generic_malloc_aligned(lb, k, 0 /* flags */, 0); + } +} + +/* Change the size of the block pointed to by p to contain at least */ +/* lb bytes. The object may be (and quite likely will be) moved. */ +/* The kind (e.g. atomic) is the same as that of the old. */ +/* Shrinking of large blocks is not implemented well. */ +GC_API void * GC_CALL GC_realloc(void * p, size_t lb) +{ + struct hblk * h; + hdr * hhdr; + void * result; +# if defined(_FORTIFY_SOURCE) && defined(__GNUC__) && !defined(__clang__) + volatile /* Use cleared_p instead of p as a workaround to avoid */ + /* passing alloc_size(lb) attribute associated with p */ + /* to memset (including memset call inside GC_free). */ +# endif + word cleared_p = (word)p; + size_t sz; /* Current size in bytes */ + size_t orig_sz; /* Original sz in bytes */ + int obj_kind; + + if (NULL == p) return GC_malloc(lb); /* Required by ANSI */ + if (0 == lb) /* and p != NULL */ { +# ifndef IGNORE_FREE + GC_free(p); +# endif + return NULL; + } + h = HBLKPTR(p); + hhdr = HDR(h); + sz = (size_t)hhdr->hb_sz; + obj_kind = hhdr -> hb_obj_kind; + orig_sz = sz; + + if (sz > MAXOBJBYTES) { + struct obj_kind * ok = &GC_obj_kinds[obj_kind]; + word descr = ok -> ok_descriptor; + + /* Round it up to the next whole heap block. */ + sz = (sz + HBLKSIZE-1) & ~(HBLKSIZE-1); +# if ALIGNMENT > GC_DS_TAGS + /* An extra byte is not added in case of ignore-off-page */ + /* allocated objects not smaller than HBLKSIZE. */ + GC_ASSERT(sz >= HBLKSIZE); + if (EXTRA_BYTES != 0 && (hhdr -> hb_flags & IGNORE_OFF_PAGE) != 0 + && obj_kind == NORMAL) + descr += ALIGNMENT; /* or set to 0 */ +# endif + if (ok -> ok_relocate_descr) + descr += sz; + /* GC_realloc might be changing the block size while */ + /* GC_reclaim_block or GC_clear_hdr_marks is examining it. */ + /* The change to the size field is benign, in that GC_reclaim */ + /* (and GC_clear_hdr_marks) would work correctly with either */ + /* value, since we are not changing the number of objects in */ + /* the block. But seeing a half-updated value (though unlikely */ + /* to occur in practice) could be probably bad. */ + /* Using unordered atomic accesses on the size and hb_descr */ + /* fields would solve the issue. (The alternate solution might */ + /* be to initially overallocate large objects, so we do not */ + /* have to adjust the size in GC_realloc, if they still fit. */ + /* But that is probably more expensive, since we may end up */ + /* scanning a bunch of zeros during GC.) */ +# ifdef AO_HAVE_store + GC_STATIC_ASSERT(sizeof(hhdr->hb_sz) == sizeof(AO_t)); + AO_store((volatile AO_t *)&hhdr->hb_sz, (AO_t)sz); + AO_store((volatile AO_t *)&hhdr->hb_descr, (AO_t)descr); +# else + { + LOCK(); + hhdr -> hb_sz = sz; + hhdr -> hb_descr = descr; + UNLOCK(); + } +# endif + +# ifdef MARK_BIT_PER_OBJ + GC_ASSERT(hhdr -> hb_inv_sz == LARGE_INV_SZ); +# else + GC_ASSERT((hhdr -> hb_flags & LARGE_BLOCK) != 0 + && hhdr -> hb_map[ANY_INDEX] == 1); +# endif + if (IS_UNCOLLECTABLE(obj_kind)) GC_non_gc_bytes += (sz - orig_sz); + /* Extra area is already cleared by GC_alloc_large_and_clear. */ + } + if (ADD_EXTRA_BYTES(lb) <= sz) { + if (lb >= (sz >> 1)) { + if (orig_sz > lb) { + /* Clear unneeded part of object to avoid bogus pointer */ + /* tracing. */ + BZERO((ptr_t)cleared_p + lb, orig_sz - lb); + } + return p; + } + /* shrink */ + sz = lb; + } + result = GC_generic_or_special_malloc((word)lb, obj_kind); + if (EXPECT(result != NULL, TRUE)) { + /* In case of shrink, it could also return original object. */ + /* But this gives the client warning of imminent disaster. */ + BCOPY(p, result, sz); +# ifndef IGNORE_FREE + GC_free((ptr_t)cleared_p); +# endif + } + return result; +} + +# if defined(REDIRECT_MALLOC) && !defined(REDIRECT_REALLOC) +# define REDIRECT_REALLOC GC_realloc +# endif + +# ifdef REDIRECT_REALLOC + +/* As with malloc, avoid two levels of extra calls here. */ +# define GC_debug_realloc_replacement(p, lb) \ + GC_debug_realloc(p, lb, GC_DBG_EXTRAS) + +# if !defined(REDIRECT_MALLOC_IN_HEADER) + void * realloc(void * p, size_t lb) + { + return REDIRECT_REALLOC(p, lb); + } +# endif + +# undef GC_debug_realloc_replacement +# endif /* REDIRECT_REALLOC */ + +/* Allocate memory such that only pointers to near the beginning of */ +/* the object are considered. We avoid holding the allocator lock */ +/* while we clear the memory. */ +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_generic_malloc_ignore_off_page(size_t lb, int k) +{ + return GC_generic_malloc_aligned(lb, k, IGNORE_OFF_PAGE, 0 /* align_m1 */); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_ignore_off_page(size_t lb) +{ + return GC_generic_malloc_aligned(lb, NORMAL, IGNORE_OFF_PAGE, 0); +} + +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_malloc_atomic_ignore_off_page(size_t lb) +{ + return GC_generic_malloc_aligned(lb, PTRFREE, IGNORE_OFF_PAGE, 0); +} + +/* Increment GC_bytes_allocd from code that doesn't have direct access */ +/* to GC_arrays. */ +void GC_CALL GC_incr_bytes_allocd(size_t n) +{ + GC_bytes_allocd += n; +} + +/* The same for GC_bytes_freed. */ +void GC_CALL GC_incr_bytes_freed(size_t n) +{ + GC_bytes_freed += n; +} + +GC_API size_t GC_CALL GC_get_expl_freed_bytes_since_gc(void) +{ + return (size_t)GC_bytes_freed; +} + +# ifdef PARALLEL_MARK + /* Number of bytes of memory allocated since we released the */ + /* allocator lock. Instead of reacquiring the allocator lock just */ + /* to add this in, we add it in the next time we reacquire the */ + /* allocator lock. (Atomically adding it does not work, since we */ + /* would have to atomically update it in GC_malloc, which is too */ + /* expensive.) */ + STATIC volatile AO_t GC_bytes_allocd_tmp = 0; +# endif /* PARALLEL_MARK */ + +/* Return a list of 1 or more objects of the indicated size, linked */ +/* through the first word in the object. This has the advantage that */ +/* it acquires the allocator lock only once, and may greatly reduce */ +/* time wasted contending for the allocator lock. Typical usage would */ +/* be in a thread that requires many items of the same size. It would */ +/* keep its own free list in thread-local storage, and call */ +/* GC_malloc_many or friends to replenish it. (We do not round up */ +/* object sizes, since a call indicates the intention to consume many */ +/* objects of exactly this size.) */ +/* We assume that the size is a multiple of GC_GRANULE_BYTES. */ +/* We return the free-list by assigning it to *result, since it is */ +/* not safe to return, e.g. a linked list of pointer-free objects, */ +/* since the collector would not retain the entire list if it were */ +/* invoked just as we were returning. */ +/* Note that the client should usually clear the link field. */ +GC_API void GC_CALL GC_generic_malloc_many(size_t lb, int k, void **result) +{ + void *op; + void *p; + void **opp; + size_t lw; /* Length in words. */ + size_t lg; /* Length in granules. */ + word my_bytes_allocd = 0; + struct obj_kind * ok; + struct hblk ** rlh; + + GC_ASSERT(lb != 0 && (lb & (GC_GRANULE_BYTES-1)) == 0); + /* Currently a single object is always allocated if manual VDB. */ + /* TODO: GC_dirty should be called for each linked object (but */ + /* the last one) to support multiple objects allocation. */ + if (!SMALL_OBJ(lb) || GC_manual_vdb) { + op = GC_generic_malloc_aligned(lb, k, 0 /* flags */, 0 /* align_m1 */); + if (EXPECT(0 != op, TRUE)) + obj_link(op) = 0; + *result = op; +# ifndef NO_MANUAL_VDB + if (GC_manual_vdb && GC_is_heap_ptr(result)) { + GC_dirty_inner(result); + REACHABLE_AFTER_DIRTY(op); + } +# endif + return; + } + GC_ASSERT(k < MAXOBJKINDS); + lw = BYTES_TO_WORDS(lb); + lg = BYTES_TO_GRANULES(lb); + if (EXPECT(get_have_errors(), FALSE)) + GC_print_all_errors(); + GC_INVOKE_FINALIZERS(); + GC_DBG_COLLECT_AT_MALLOC(lb); + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + LOCK(); + /* Do our share of marking work */ + if (GC_incremental && !GC_dont_gc) { + ENTER_GC(); + GC_collect_a_little_inner(1); + EXIT_GC(); + } + /* First see if we can reclaim a page of objects waiting to be */ + /* reclaimed. */ + ok = &GC_obj_kinds[k]; + rlh = ok -> ok_reclaim_list; + if (rlh != NULL) { + struct hblk * hbp; + hdr * hhdr; + + while ((hbp = rlh[lg]) != NULL) { + hhdr = HDR(hbp); + rlh[lg] = hhdr -> hb_next; + GC_ASSERT(hhdr -> hb_sz == lb); + hhdr -> hb_last_reclaimed = (unsigned short) GC_gc_no; +# ifdef PARALLEL_MARK + if (GC_parallel) { + signed_word my_bytes_allocd_tmp = + (signed_word)AO_load(&GC_bytes_allocd_tmp); + GC_ASSERT(my_bytes_allocd_tmp >= 0); + /* We only decrement it while holding the allocator */ + /* lock. Thus, we cannot accidentally adjust it down */ + /* in more than one thread simultaneously. */ + if (my_bytes_allocd_tmp != 0) { + (void)AO_fetch_and_add(&GC_bytes_allocd_tmp, + (AO_t)(-my_bytes_allocd_tmp)); + GC_bytes_allocd += (word)my_bytes_allocd_tmp; + } + GC_acquire_mark_lock(); + ++ GC_fl_builder_count; + UNLOCK(); + GC_release_mark_lock(); + } +# endif + op = GC_reclaim_generic(hbp, hhdr, lb, + ok -> ok_init, 0, &my_bytes_allocd); + if (op != 0) { +# ifdef PARALLEL_MARK + if (GC_parallel) { + *result = op; + (void)AO_fetch_and_add(&GC_bytes_allocd_tmp, + (AO_t)my_bytes_allocd); + GC_acquire_mark_lock(); + -- GC_fl_builder_count; + if (GC_fl_builder_count == 0) GC_notify_all_builder(); +# ifdef THREAD_SANITIZER + GC_release_mark_lock(); + LOCK(); + GC_bytes_found += (signed_word)my_bytes_allocd; + UNLOCK(); +# else + GC_bytes_found += (signed_word)my_bytes_allocd; + /* The result may be inaccurate. */ + GC_release_mark_lock(); +# endif + (void) GC_clear_stack(0); + return; + } +# endif + /* We also reclaimed memory, so we need to adjust */ + /* that count. */ + GC_bytes_found += (signed_word)my_bytes_allocd; + GC_bytes_allocd += my_bytes_allocd; + goto out; + } +# ifdef PARALLEL_MARK + if (GC_parallel) { + GC_acquire_mark_lock(); + -- GC_fl_builder_count; + if (GC_fl_builder_count == 0) GC_notify_all_builder(); + GC_release_mark_lock(); + LOCK(); + /* The allocator lock is needed for access to the */ + /* reclaim list. We must decrement fl_builder_count */ + /* before reacquiring the allocator lock. Hopefully */ + /* this path is rare. */ + + rlh = ok -> ok_reclaim_list; /* reload rlh after locking */ + if (NULL == rlh) break; + } +# endif + } + } + /* Next try to use prefix of global free list if there is one. */ + /* We don't refill it, but we need to use it up before allocating */ + /* a new block ourselves. */ + opp = &(ok -> ok_freelist[lg]); + if ((op = *opp) != NULL) { + *opp = 0; + my_bytes_allocd = 0; + for (p = op; p != 0; p = obj_link(p)) { + my_bytes_allocd += lb; + if ((word)my_bytes_allocd >= HBLKSIZE) { + *opp = obj_link(p); + obj_link(p) = 0; + break; + } + } + GC_bytes_allocd += my_bytes_allocd; + goto out; + } + /* Next try to allocate a new block worth of objects of this size. */ + { + struct hblk *h = GC_allochblk(lb, k, 0 /* flags */, 0 /* align_m1 */); + + if (h /* != NULL */) { /* CPPCHECK */ + if (IS_UNCOLLECTABLE(k)) GC_set_hdr_marks(HDR(h)); + GC_bytes_allocd += HBLKSIZE - HBLKSIZE % lb; +# ifdef PARALLEL_MARK + if (GC_parallel) { + GC_acquire_mark_lock(); + ++ GC_fl_builder_count; + UNLOCK(); + GC_release_mark_lock(); + + op = GC_build_fl(h, lw, + (ok -> ok_init || GC_debugging_started), 0); + + *result = op; + GC_acquire_mark_lock(); + -- GC_fl_builder_count; + if (GC_fl_builder_count == 0) GC_notify_all_builder(); + GC_release_mark_lock(); + (void) GC_clear_stack(0); + return; + } +# endif + op = GC_build_fl(h, lw, (ok -> ok_init || GC_debugging_started), 0); + goto out; + } + } + + /* As a last attempt, try allocating a single object. Note that */ + /* this may trigger a collection or expand the heap. */ + op = GC_generic_malloc_inner(lb, k, 0 /* flags */); + if (op != NULL) obj_link(op) = NULL; + + out: + *result = op; + UNLOCK(); + (void) GC_clear_stack(0); +} + +/* Note that the "atomic" version of this would be unsafe, since the */ +/* links would not be seen by the collector. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_many(size_t lb) +{ + void *result; + size_t lg = ALLOC_REQUEST_GRANS(lb); + + GC_generic_malloc_many(GRANULES_TO_BYTES(lg), NORMAL, &result); + return result; +} + +/* TODO: The debugging version of GC_memalign and friends is tricky */ +/* and currently missing. The major difficulty is: */ +/* - store_debug_info() should return the pointer of the object with */ +/* the requested alignment (unlike the object header). */ + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_memalign(size_t align, size_t lb) +{ + size_t align_m1 = align - 1; + + /* Check the alignment argument. */ + if (EXPECT(0 == align || (align & align_m1) != 0, FALSE)) return NULL; + /* TODO: use thread-local allocation */ + if (align <= GC_GRANULE_BYTES) return GC_malloc(lb); + return GC_malloc_kind_aligned_global(lb, NORMAL, align_m1); +} + +/* This one exists largely to redirect posix_memalign for leaks finding. */ +GC_API int GC_CALL GC_posix_memalign(void **memptr, size_t align, size_t lb) +{ + void *p; + size_t align_minus_one = align - 1; /* to workaround a cppcheck warning */ + + /* Check alignment properly. */ + if (EXPECT(align < sizeof(void *) + || (align_minus_one & align) != 0, FALSE)) { +# ifdef MSWINCE + return ERROR_INVALID_PARAMETER; +# else + return EINVAL; +# endif + } + + p = GC_memalign(align, lb); + if (EXPECT(NULL == p, FALSE)) { +# ifdef MSWINCE + return ERROR_NOT_ENOUGH_MEMORY; +# else + return ENOMEM; +# endif + } + *memptr = p; + return 0; /* success */ +} + +#ifndef GC_NO_VALLOC + GC_API GC_ATTR_MALLOC void * GC_CALL GC_valloc(size_t lb) + { + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + GC_ASSERT(GC_real_page_size != 0); + return GC_memalign(GC_real_page_size, lb); + } + + GC_API GC_ATTR_MALLOC void * GC_CALL GC_pvalloc(size_t lb) + { + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + GC_ASSERT(GC_real_page_size != 0); + lb = SIZET_SAT_ADD(lb, GC_real_page_size - 1) & ~(GC_real_page_size - 1); + return GC_memalign(GC_real_page_size, lb); + } +#endif /* !GC_NO_VALLOC */ + +/* Provide a version of strdup() that uses the collector to allocate */ +/* the copy of the string. */ +GC_API GC_ATTR_MALLOC char * GC_CALL GC_strdup(const char *s) +{ + char *copy; + size_t lb; + if (s == NULL) return NULL; + lb = strlen(s) + 1; + copy = (char *)GC_malloc_atomic(lb); + if (EXPECT(NULL == copy, FALSE)) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + BCOPY(s, copy, lb); + return copy; +} + +GC_API GC_ATTR_MALLOC char * GC_CALL GC_strndup(const char *str, size_t size) +{ + char *copy; + size_t len = strlen(str); /* str is expected to be non-NULL */ + if (EXPECT(len > size, FALSE)) + len = size; + copy = (char *)GC_malloc_atomic(len + 1); + if (EXPECT(NULL == copy, FALSE)) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + if (EXPECT(len > 0, TRUE)) + BCOPY(str, copy, len); + copy[len] = '\0'; + return copy; +} + +#ifdef GC_REQUIRE_WCSDUP +# include /* for wcslen() */ + + GC_API GC_ATTR_MALLOC wchar_t * GC_CALL GC_wcsdup(const wchar_t *str) + { + size_t lb = (wcslen(str) + 1) * sizeof(wchar_t); + wchar_t *copy = (wchar_t *)GC_malloc_atomic(lb); + + if (EXPECT(NULL == copy, FALSE)) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + BCOPY(str, copy, lb); + return copy; + } +#endif /* GC_REQUIRE_WCSDUP */ + +#ifndef CPPCHECK + GC_API void * GC_CALL GC_malloc_stubborn(size_t lb) + { + return GC_malloc(lb); + } + + GC_API void GC_CALL GC_change_stubborn(const void *p) + { + UNUSED_ARG(p); + } +#endif /* !CPPCHECK */ + +GC_API void GC_CALL GC_end_stubborn_change(const void *p) +{ + GC_dirty(p); /* entire object */ +} + +GC_API void GC_CALL GC_ptr_store_and_dirty(void *p, const void *q) +{ + *(const void **)p = q; + GC_dirty(p); + REACHABLE_AFTER_DIRTY(q); +} + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright (c) 2000 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + + + +/* Make arguments appear live to compiler. Put here to minimize the */ +/* risk of inlining. Used to minimize junk left in registers. */ +GC_ATTR_NOINLINE +void GC_noop6(word arg1, word arg2, word arg3, word arg4, word arg5, word arg6) +{ + UNUSED_ARG(arg1); + UNUSED_ARG(arg2); + UNUSED_ARG(arg3); + UNUSED_ARG(arg4); + UNUSED_ARG(arg5); + UNUSED_ARG(arg6); + /* Avoid GC_noop6 calls to be optimized away. */ +# if defined(AO_HAVE_compiler_barrier) && !defined(BASE_ATOMIC_OPS_EMULATED) + AO_compiler_barrier(); /* to serve as a special side-effect */ +# else + GC_noop1(0); +# endif +} + +#if defined(AO_HAVE_store) && defined(THREAD_SANITIZER) + volatile AO_t GC_noop_sink; +#else + volatile word GC_noop_sink; +#endif + +/* Make the argument appear live to compiler. This is similar */ +/* to GC_noop6(), but with a single argument. Robust against */ +/* whole program analysis. */ +GC_API void GC_CALL GC_noop1(GC_word x) +{ +# if defined(AO_HAVE_store) && defined(THREAD_SANITIZER) + AO_store(&GC_noop_sink, (AO_t)x); +# else + GC_noop_sink = x; +# endif +} + +/* Initialize GC_obj_kinds properly and standard free lists properly. */ +/* This must be done statically since they may be accessed before */ +/* GC_init is called. */ +/* It's done here, since we need to deal with mark descriptors. */ +GC_INNER struct obj_kind GC_obj_kinds[MAXOBJKINDS] = { +/* PTRFREE */ { &GC_aobjfreelist[0], 0 /* filled in dynamically */, + /* 0 | */ GC_DS_LENGTH, FALSE, FALSE + /*, */ OK_DISCLAIM_INITZ }, +/* NORMAL */ { &GC_objfreelist[0], 0, + /* 0 | */ GC_DS_LENGTH, + /* adjusted in GC_init for EXTRA_BYTES */ + TRUE /* add length to descr */, TRUE + /*, */ OK_DISCLAIM_INITZ }, +/* UNCOLLECTABLE */ + { &GC_uobjfreelist[0], 0, + /* 0 | */ GC_DS_LENGTH, TRUE /* add length to descr */, TRUE + /*, */ OK_DISCLAIM_INITZ }, +# ifdef GC_ATOMIC_UNCOLLECTABLE + { &GC_auobjfreelist[0], 0, + /* 0 | */ GC_DS_LENGTH, FALSE, FALSE + /*, */ OK_DISCLAIM_INITZ }, +# endif +}; + +#ifndef INITIAL_MARK_STACK_SIZE +# define INITIAL_MARK_STACK_SIZE (1*HBLKSIZE) + /* INITIAL_MARK_STACK_SIZE * sizeof(mse) should be a */ + /* multiple of HBLKSIZE. */ + /* The incremental collector actually likes a larger */ + /* size, since it wants to push all marked dirty */ + /* objects before marking anything new. Currently we */ + /* let it grow dynamically. */ +#endif /* !INITIAL_MARK_STACK_SIZE */ + +#if !defined(GC_DISABLE_INCREMENTAL) + STATIC word GC_n_rescuing_pages = 0; + /* Number of dirty pages we marked from */ + /* excludes ptrfree pages, etc. */ + /* Used for logging only. */ +#endif + +#ifdef PARALLEL_MARK + GC_INNER GC_bool GC_parallel_mark_disabled = FALSE; +#endif + +GC_API void GC_CALL GC_set_pointer_mask(GC_word value) +{ +# ifdef DYNAMIC_POINTER_MASK + GC_ASSERT(value >= 0xff); /* a simple sanity check */ + GC_pointer_mask = value; +# else + if (value +# ifdef POINTER_MASK + != (word)(POINTER_MASK) +# else + != GC_WORD_MAX +# endif + ) { + ABORT("Dynamic pointer mask/shift is unsupported"); + } +# endif +} + +GC_API GC_word GC_CALL GC_get_pointer_mask(void) +{ +# ifdef DYNAMIC_POINTER_MASK + GC_word value = GC_pointer_mask; + + if (0 == value) { + GC_ASSERT(!GC_is_initialized); + value = GC_WORD_MAX; + } + return value; +# elif defined(POINTER_MASK) + return POINTER_MASK; +# else + return GC_WORD_MAX; +# endif +} + +GC_API void GC_CALL GC_set_pointer_shift(unsigned value) +{ +# ifdef DYNAMIC_POINTER_MASK + GC_ASSERT(value < CPP_WORDSZ); + GC_pointer_shift = (unsigned char)value; +# else + if (value +# ifdef POINTER_SHIFT + != (unsigned)(POINTER_SHIFT) +# endif /* else is not zero */ + ) { + ABORT("Dynamic pointer mask/shift is unsupported"); + } +# endif +} + +GC_API unsigned GC_CALL GC_get_pointer_shift(void) +{ +# ifdef DYNAMIC_POINTER_MASK + return GC_pointer_shift; +# elif defined(POINTER_SHIFT) + GC_STATIC_ASSERT((unsigned)(POINTER_SHIFT) < CPP_WORDSZ); + return POINTER_SHIFT; +# else + return 0; +# endif +} + +/* Is a collection in progress? Note that this can return true in the */ +/* non-incremental case, if a collection has been abandoned and the */ +/* mark state is now MS_INVALID. */ +GC_INNER GC_bool GC_collection_in_progress(void) +{ + return GC_mark_state != MS_NONE; +} + +/* Clear all mark bits in the header. */ +GC_INNER void GC_clear_hdr_marks(hdr *hhdr) +{ + size_t last_bit; + +# ifdef AO_HAVE_load + /* Atomic access is used to avoid racing with GC_realloc. */ + last_bit = FINAL_MARK_BIT((size_t)AO_load((volatile AO_t *)&hhdr->hb_sz)); +# else + /* No race as GC_realloc holds the allocator lock while updating hb_sz. */ + last_bit = FINAL_MARK_BIT((size_t)hhdr->hb_sz); +# endif + + BZERO(hhdr -> hb_marks, sizeof(hhdr->hb_marks)); + set_mark_bit_from_hdr(hhdr, last_bit); + hhdr -> hb_n_marks = 0; +} + +/* Set all mark bits in the header. Used for uncollectible blocks. */ +GC_INNER void GC_set_hdr_marks(hdr *hhdr) +{ + unsigned i; + size_t sz = (size_t)hhdr->hb_sz; + unsigned n_marks = (unsigned)FINAL_MARK_BIT(sz); + +# ifdef USE_MARK_BYTES + for (i = 0; i <= n_marks; i += (unsigned)MARK_BIT_OFFSET(sz)) { + hhdr -> hb_marks[i] = 1; + } +# else + /* Note that all bits are set even in case of not MARK_BIT_PER_OBJ, */ + /* instead of setting every n-th bit where n is MARK_BIT_OFFSET(sz). */ + /* This is done for a performance reason. */ + for (i = 0; i < divWORDSZ(n_marks); ++i) { + hhdr -> hb_marks[i] = GC_WORD_MAX; + } + /* Set the remaining bits near the end (plus one bit past the end). */ + hhdr -> hb_marks[i] = ((((word)1 << modWORDSZ(n_marks)) - 1) << 1) | 1; +# endif +# ifdef MARK_BIT_PER_OBJ + hhdr -> hb_n_marks = n_marks; +# else + hhdr -> hb_n_marks = HBLK_OBJS(sz); +# endif +} + +/* Clear all mark bits associated with block h. */ +static void GC_CALLBACK clear_marks_for_block(struct hblk *h, GC_word dummy) +{ + hdr * hhdr = HDR(h); + + UNUSED_ARG(dummy); + if (IS_UNCOLLECTABLE(hhdr -> hb_obj_kind)) return; + /* Mark bit for these is cleared only once the object is */ + /* explicitly deallocated. This either frees the block, or */ + /* the bit is cleared once the object is on the free list. */ + GC_clear_hdr_marks(hhdr); +} + +/* Slow but general routines for setting/clearing/asking about mark bits. */ +GC_API void GC_CALL GC_set_mark_bit(const void *p) +{ + struct hblk *h = HBLKPTR(p); + hdr * hhdr = HDR(h); + word bit_no = MARK_BIT_NO((word)p - (word)h, hhdr -> hb_sz); + + if (!mark_bit_from_hdr(hhdr, bit_no)) { + set_mark_bit_from_hdr(hhdr, bit_no); + INCR_MARKS(hhdr); + } +} + +GC_API void GC_CALL GC_clear_mark_bit(const void *p) +{ + struct hblk *h = HBLKPTR(p); + hdr * hhdr = HDR(h); + word bit_no = MARK_BIT_NO((word)p - (word)h, hhdr -> hb_sz); + + if (mark_bit_from_hdr(hhdr, bit_no)) { + size_t n_marks = hhdr -> hb_n_marks; + + GC_ASSERT(n_marks != 0); + clear_mark_bit_from_hdr(hhdr, bit_no); + n_marks--; +# ifdef PARALLEL_MARK + if (n_marks != 0 || !GC_parallel) + hhdr -> hb_n_marks = n_marks; + /* Don't decrement to zero. The counts are approximate due to */ + /* concurrency issues, but we need to ensure that a count of */ + /* zero implies an empty block. */ +# else + hhdr -> hb_n_marks = n_marks; +# endif + } +} + +GC_API int GC_CALL GC_is_marked(const void *p) +{ + struct hblk *h = HBLKPTR(p); + hdr * hhdr = HDR(h); + word bit_no = MARK_BIT_NO((word)p - (word)h, hhdr -> hb_sz); + + return (int)mark_bit_from_hdr(hhdr, bit_no); /* 0 or 1 */ +} + +/* Clear mark bits in all allocated heap blocks. This invalidates the */ +/* marker invariant, and sets GC_mark_state to reflect this. (This */ +/* implicitly starts marking to reestablish the invariant.) */ +GC_INNER void GC_clear_marks(void) +{ + GC_ASSERT(GC_is_initialized); /* needed for GC_push_roots */ + GC_apply_to_all_blocks(clear_marks_for_block, (word)0); + GC_objects_are_marked = FALSE; + GC_mark_state = MS_INVALID; + GC_scan_ptr = NULL; +} + +/* Initiate a garbage collection. Initiates a full collection if the */ +/* mark state is invalid. */ +GC_INNER void GC_initiate_gc(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_is_initialized); +# ifndef GC_DISABLE_INCREMENTAL + if (GC_incremental) { +# ifdef CHECKSUMS + GC_read_dirty(FALSE); + GC_check_dirty(); +# else + GC_read_dirty(GC_mark_state == MS_INVALID); +# endif + } + GC_n_rescuing_pages = 0; +# endif + if (GC_mark_state == MS_NONE) { + GC_mark_state = MS_PUSH_RESCUERS; + } else { + GC_ASSERT(GC_mark_state == MS_INVALID); + /* This is really a full collection, and mark bits are invalid. */ + } + GC_scan_ptr = NULL; +} + +#ifdef PARALLEL_MARK + STATIC void GC_do_parallel_mark(void); /* Initiate parallel marking. */ +#endif /* PARALLEL_MARK */ + +#ifdef GC_DISABLE_INCREMENTAL +# define GC_push_next_marked_dirty(h) GC_push_next_marked(h) +#else + STATIC struct hblk * GC_push_next_marked_dirty(struct hblk *h); + /* Invoke GC_push_marked on next dirty block above h. */ + /* Return a pointer just past the end of this block. */ +#endif /* !GC_DISABLE_INCREMENTAL */ +STATIC struct hblk * GC_push_next_marked(struct hblk *h); + /* Ditto, but also mark from clean pages. */ +STATIC struct hblk * GC_push_next_marked_uncollectable(struct hblk *h); + /* Ditto, but mark only from uncollectible pages. */ + +static void alloc_mark_stack(size_t); + +static void push_roots_and_advance(GC_bool push_all, ptr_t cold_gc_frame) +{ + if (GC_scan_ptr != NULL) return; /* not ready to push */ + + GC_push_roots(push_all, cold_gc_frame); + GC_objects_are_marked = TRUE; + if (GC_mark_state != MS_INVALID) + GC_mark_state = MS_ROOTS_PUSHED; +} + +STATIC GC_on_mark_stack_empty_proc GC_on_mark_stack_empty; + +GC_API void GC_CALL GC_set_on_mark_stack_empty(GC_on_mark_stack_empty_proc fn) +{ + LOCK(); + GC_on_mark_stack_empty = fn; + UNLOCK(); +} + +GC_API GC_on_mark_stack_empty_proc GC_CALL GC_get_on_mark_stack_empty(void) +{ + GC_on_mark_stack_empty_proc fn; + + READER_LOCK(); + fn = GC_on_mark_stack_empty; + READER_UNLOCK(); + return fn; +} + +/* Perform a small amount of marking. */ +/* We try to touch roughly a page of memory. */ +/* Return TRUE if we just finished a mark phase. */ +/* Cold_gc_frame is an address inside a GC frame that */ +/* remains valid until all marking is complete. */ +/* A zero value indicates that it's OK to miss some */ +/* register values. In the case of an incremental */ +/* collection, the world may be running. */ +#ifdef WRAP_MARK_SOME + /* For Win32, this is called after we establish a structured */ + /* exception (or signal) handler, in case Windows unmaps one */ + /* of our root segments. Note that this code should never */ + /* generate an incremental GC write fault. */ + STATIC GC_bool GC_mark_some_inner(ptr_t cold_gc_frame) +#else + GC_INNER GC_bool GC_mark_some(ptr_t cold_gc_frame) +#endif +{ + GC_ASSERT(I_HOLD_LOCK()); + switch (GC_mark_state) { + case MS_NONE: + return TRUE; + + case MS_PUSH_RESCUERS: + if ((word)GC_mark_stack_top + >= (word)(GC_mark_stack_limit - INITIAL_MARK_STACK_SIZE/2)) { + /* Go ahead and mark, even though that might cause us to */ + /* see more marked dirty objects later on. Avoid this */ + /* in the future. */ + GC_mark_stack_too_small = TRUE; + MARK_FROM_MARK_STACK(); + } else { + GC_scan_ptr = GC_push_next_marked_dirty(GC_scan_ptr); +# ifndef GC_DISABLE_INCREMENTAL + if (NULL == GC_scan_ptr) { + GC_COND_LOG_PRINTF("Marked from %lu dirty pages\n", + (unsigned long)GC_n_rescuing_pages); + } +# endif + push_roots_and_advance(FALSE, cold_gc_frame); + } + GC_ASSERT(GC_mark_state == MS_PUSH_RESCUERS + || GC_mark_state == MS_ROOTS_PUSHED + || GC_mark_state == MS_INVALID); + break; + + case MS_PUSH_UNCOLLECTABLE: + if ((word)GC_mark_stack_top + >= (word)(GC_mark_stack + GC_mark_stack_size/4)) { +# ifdef PARALLEL_MARK + /* Avoid this, since we don't parallelize the marker */ + /* here. */ + if (GC_parallel) GC_mark_stack_too_small = TRUE; +# endif + MARK_FROM_MARK_STACK(); + } else { + GC_scan_ptr = GC_push_next_marked_uncollectable(GC_scan_ptr); + push_roots_and_advance(TRUE, cold_gc_frame); + } + GC_ASSERT(GC_mark_state == MS_PUSH_UNCOLLECTABLE + || GC_mark_state == MS_ROOTS_PUSHED + || GC_mark_state == MS_INVALID); + break; + + case MS_ROOTS_PUSHED: +# ifdef PARALLEL_MARK + /* Eventually, incremental marking should run */ + /* asynchronously in multiple threads, without acquiring */ + /* the allocator lock. */ + /* For now, parallel marker is disabled if there is */ + /* a chance that marking could be interrupted by */ + /* a client-supplied time limit or custom stop function. */ + if (GC_parallel && !GC_parallel_mark_disabled) { + GC_do_parallel_mark(); + GC_ASSERT((word)GC_mark_stack_top < (word)GC_first_nonempty); + GC_mark_stack_top = GC_mark_stack - 1; + if (GC_mark_stack_too_small) { + alloc_mark_stack(2*GC_mark_stack_size); + } + if (GC_mark_state == MS_ROOTS_PUSHED) { + GC_mark_state = MS_NONE; + return TRUE; + } + GC_ASSERT(GC_mark_state == MS_INVALID); + break; + } +# endif + if ((word)GC_mark_stack_top >= (word)GC_mark_stack) { + MARK_FROM_MARK_STACK(); + } else { + GC_on_mark_stack_empty_proc on_ms_empty; + + if (GC_mark_stack_too_small) { + GC_mark_state = MS_NONE; + alloc_mark_stack(2*GC_mark_stack_size); + return TRUE; + } + on_ms_empty = GC_on_mark_stack_empty; + if (on_ms_empty != 0) { + GC_mark_stack_top = on_ms_empty(GC_mark_stack_top, + GC_mark_stack_limit); + /* If we pushed new items or overflowed the stack, */ + /* we need to continue processing. */ + if ((word)GC_mark_stack_top >= (word)GC_mark_stack + || GC_mark_stack_too_small) + break; + } + + GC_mark_state = MS_NONE; + return TRUE; + } + GC_ASSERT(GC_mark_state == MS_ROOTS_PUSHED + || GC_mark_state == MS_INVALID); + break; + + case MS_INVALID: + case MS_PARTIALLY_INVALID: + if (!GC_objects_are_marked) { + GC_mark_state = MS_PUSH_UNCOLLECTABLE; + break; + } + if ((word)GC_mark_stack_top >= (word)GC_mark_stack) { + MARK_FROM_MARK_STACK(); + GC_ASSERT(GC_mark_state == MS_PARTIALLY_INVALID + || GC_mark_state == MS_INVALID); + break; + } + if (NULL == GC_scan_ptr && GC_mark_state == MS_INVALID) { + /* About to start a heap scan for marked objects. */ + /* Mark stack is empty. OK to reallocate. */ + if (GC_mark_stack_too_small) { + alloc_mark_stack(2*GC_mark_stack_size); + } + GC_mark_state = MS_PARTIALLY_INVALID; + } + GC_scan_ptr = GC_push_next_marked(GC_scan_ptr); + if (GC_mark_state == MS_PARTIALLY_INVALID) + push_roots_and_advance(TRUE, cold_gc_frame); + GC_ASSERT(GC_mark_state == MS_ROOTS_PUSHED + || GC_mark_state == MS_PARTIALLY_INVALID + || GC_mark_state == MS_INVALID); + break; + + default: + ABORT("GC_mark_some: bad state"); + } + return FALSE; +} + +#ifdef WRAP_MARK_SOME + GC_INNER GC_bool GC_mark_some(ptr_t cold_gc_frame) + { + GC_bool ret_val; + + if (GC_no_dls) { + ret_val = GC_mark_some_inner(cold_gc_frame); + } else { + /* Windows appears to asynchronously create and remove */ + /* writable memory mappings, for reasons we haven't yet */ + /* understood. Since we look for writable regions to */ + /* determine the root set, we may try to mark from an */ + /* address range that disappeared since we started the */ + /* collection. Thus we have to recover from faults here. */ + /* This code seems to be necessary for WinCE (at least in */ + /* the case we'd decide to add MEM_PRIVATE sections to */ + /* data roots in GC_register_dynamic_libraries()). */ + /* It's conceivable that this is the same issue as with */ + /* terminating threads that we see with Linux and */ + /* USE_PROC_FOR_LIBRARIES. */ +# ifndef NO_SEH_AVAILABLE + __try { + ret_val = GC_mark_some_inner(cold_gc_frame); + } __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + goto handle_ex; + } +# else +# if defined(USE_PROC_FOR_LIBRARIES) && !defined(DEFAULT_VDB) + if (GC_auto_incremental) { + static GC_bool is_warned = FALSE; + + if (!is_warned) { + is_warned = TRUE; + WARN("Incremental GC incompatible with /proc roots\n", 0); + } + /* I'm not sure if this could still work ... */ + } +# endif + /* If USE_PROC_FOR_LIBRARIES, we are handling the case in */ + /* which /proc is used for root finding, and we have threads. */ + /* We may find a stack for a thread that is in the process of */ + /* exiting, and disappears while we are marking it. */ + /* This seems extremely difficult to avoid otherwise. */ + GC_setup_temporary_fault_handler(); + if (SETJMP(GC_jmp_buf) != 0) goto handle_ex; + ret_val = GC_mark_some_inner(cold_gc_frame); + GC_reset_fault_handler(); +# endif + } + +# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) + /* With DllMain-based thread tracking, a thread may have */ + /* started while we were marking. This is logically equivalent */ + /* to the exception case; our results are invalid and we have */ + /* to start over. This cannot be prevented since we can't */ + /* block in DllMain. */ + if (GC_started_thread_while_stopped()) + goto handle_thr_start; +# endif + return ret_val; + + handle_ex: + /* Exception handler starts here for all cases. */ +# if defined(NO_SEH_AVAILABLE) + GC_reset_fault_handler(); +# endif + { + static word warned_gc_no; + + /* Report caught ACCESS_VIOLATION, once per collection. */ + if (warned_gc_no != GC_gc_no) { + GC_COND_LOG_PRINTF("Memory mapping disappeared at collection #%lu\n", + (unsigned long)GC_gc_no + 1); + warned_gc_no = GC_gc_no; + } + } +# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) + handle_thr_start: +# endif + /* We have bad roots on the mark stack - discard it. */ + /* Rescan from marked objects. Redetermine roots. */ +# ifdef REGISTER_LIBRARIES_EARLY + START_WORLD(); + GC_cond_register_dynamic_libraries(); + STOP_WORLD(); +# endif + GC_invalidate_mark_state(); + GC_scan_ptr = NULL; + return FALSE; + } +#endif /* WRAP_MARK_SOME */ + +GC_INNER void GC_invalidate_mark_state(void) +{ + GC_mark_state = MS_INVALID; + GC_mark_stack_top = GC_mark_stack-1; +} + +GC_INNER mse * GC_signal_mark_stack_overflow(mse *msp) +{ + GC_mark_state = MS_INVALID; +# ifdef PARALLEL_MARK + /* We are using a local_mark_stack in parallel mode, so */ + /* do not signal the global mark stack to be resized. */ + /* That will be done if required in GC_return_mark_stack. */ + if (!GC_parallel) + GC_mark_stack_too_small = TRUE; +# else + GC_mark_stack_too_small = TRUE; +# endif + GC_COND_LOG_PRINTF("Mark stack overflow; current size: %lu entries\n", + (unsigned long)GC_mark_stack_size); + return msp - GC_MARK_STACK_DISCARDS; +} + +/* + * Mark objects pointed to by the regions described by + * mark stack entries between mark_stack and mark_stack_top, + * inclusive. Assumes the upper limit of a mark stack entry + * is never 0. A mark stack entry never has size 0. + * We try to traverse on the order of a hblk of memory before we return. + * Caller is responsible for calling this until the mark stack is empty. + * Note that this is the most performance critical routine in the + * collector. Hence it contains all sorts of ugly hacks to speed + * things up. In particular, we avoid procedure calls on the common + * path, we take advantage of peculiarities of the mark descriptor + * encoding, we optionally maintain a cache for the block address to + * header mapping, we prefetch when an object is "grayed", etc. + */ +GC_ATTR_NO_SANITIZE_ADDR GC_ATTR_NO_SANITIZE_MEMORY GC_ATTR_NO_SANITIZE_THREAD +GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack, + mse *mark_stack_limit) +{ + signed_word credit = HBLKSIZE; /* Remaining credit for marking work. */ + ptr_t current_p; /* Pointer to current candidate ptr. */ + word current; /* Candidate pointer. */ + ptr_t limit = 0; /* (Incl) limit of current candidate range. */ + word descr; + ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr; + DECLARE_HDR_CACHE; + +# define SPLIT_RANGE_WORDS 128 /* Must be power of 2. */ + + GC_objects_are_marked = TRUE; + INIT_HDR_CACHE; +# ifdef OS2 /* Use untweaked version to circumvent compiler problem. */ + while ((word)mark_stack_top >= (word)mark_stack && credit >= 0) +# else + while (((((word)mark_stack_top - (word)mark_stack) | (word)credit) + & SIGNB) == 0) +# endif + { + current_p = mark_stack_top -> mse_start; + descr = mark_stack_top -> mse_descr.w; + retry: + /* current_p and descr describe the current object. */ + /* (*mark_stack_top) is vacant. */ + /* The following is 0 only for small objects described by a simple */ + /* length descriptor. For many applications this is the common */ + /* case, so we try to detect it quickly. */ + if (descr & (~(word)(WORDS_TO_BYTES(SPLIT_RANGE_WORDS)-1) | GC_DS_TAGS)) { + word tag = descr & GC_DS_TAGS; + + GC_STATIC_ASSERT(GC_DS_TAGS == 0x3); + switch (tag) { + case GC_DS_LENGTH: + /* Large length. Process part of the range to avoid pushing */ + /* too much on the stack. */ + + /* Either it is a heap object or a region outside the heap. */ + GC_ASSERT(descr < GC_greatest_real_heap_addr-GC_least_real_heap_addr + || (word)current_p + descr + <= GC_least_real_heap_addr + sizeof(word) + || (word)current_p >= GC_greatest_real_heap_addr); +# ifdef PARALLEL_MARK +# define SHARE_BYTES 2048 + if (descr > SHARE_BYTES && GC_parallel + && (word)mark_stack_top < (word)(mark_stack_limit - 1)) { + word new_size = (descr/2) & ~(word)(sizeof(word)-1); + + mark_stack_top -> mse_start = current_p; + mark_stack_top -> mse_descr.w = + (new_size + sizeof(word)) | GC_DS_LENGTH; + /* Makes sure we handle */ + /* misaligned pointers. */ + mark_stack_top++; +# ifdef ENABLE_TRACE + if ((word)GC_trace_addr >= (word)current_p + && (word)GC_trace_addr < (word)(current_p + descr)) { + GC_log_printf("GC #%lu: large section; start %p, len %lu," + " splitting (parallel) at %p\n", + (unsigned long)GC_gc_no, (void *)current_p, + (unsigned long)descr, + (void *)(current_p + new_size)); + } +# endif + current_p += new_size; + descr -= new_size; + goto retry; + } +# endif /* PARALLEL_MARK */ + mark_stack_top -> mse_start = + limit = current_p + WORDS_TO_BYTES(SPLIT_RANGE_WORDS-1); + mark_stack_top -> mse_descr.w = + descr - WORDS_TO_BYTES(SPLIT_RANGE_WORDS-1); +# ifdef ENABLE_TRACE + if ((word)GC_trace_addr >= (word)current_p + && (word)GC_trace_addr < (word)(current_p + descr)) { + GC_log_printf("GC #%lu: large section; start %p, len %lu," + " splitting at %p\n", + (unsigned long)GC_gc_no, (void *)current_p, + (unsigned long)descr, (void *)limit); + } +# endif + /* Make sure that pointers overlapping the two ranges are */ + /* considered. */ + limit += sizeof(word) - ALIGNMENT; + break; + case GC_DS_BITMAP: + mark_stack_top--; +# ifdef ENABLE_TRACE + if ((word)GC_trace_addr >= (word)current_p + && (word)GC_trace_addr + < (word)(current_p + WORDS_TO_BYTES(CPP_WORDSZ-2))) { + GC_log_printf("GC #%lu: tracing from %p bitmap descr %lu\n", + (unsigned long)GC_gc_no, (void *)current_p, + (unsigned long)descr); + } +# endif /* ENABLE_TRACE */ + descr &= ~(word)GC_DS_TAGS; + credit -= (signed_word)WORDS_TO_BYTES(CPP_WORDSZ / 2); /* guess */ + for (; descr != 0; descr <<= 1, current_p += sizeof(word)) { + if ((descr & SIGNB) == 0) continue; + LOAD_WORD_OR_CONTINUE(current, current_p); + FIXUP_POINTER(current); + if (current > (word)least_ha && current < (word)greatest_ha) { + PREFETCH((ptr_t)current); +# ifdef ENABLE_TRACE + if (GC_trace_addr == current_p) { + GC_log_printf("GC #%lu: considering(3) %p -> %p\n", + (unsigned long)GC_gc_no, (void *)current_p, + (void *)current); + } +# endif /* ENABLE_TRACE */ + PUSH_CONTENTS((ptr_t)current, mark_stack_top, + mark_stack_limit, current_p); + } + } + continue; + case GC_DS_PROC: + mark_stack_top--; +# ifdef ENABLE_TRACE + if ((word)GC_trace_addr >= (word)current_p + && GC_base(current_p) != 0 + && GC_base(current_p) == GC_base(GC_trace_addr)) { + GC_log_printf("GC #%lu: tracing from %p, proc descr %lu\n", + (unsigned long)GC_gc_no, (void *)current_p, + (unsigned long)descr); + } +# endif /* ENABLE_TRACE */ + credit -= GC_PROC_BYTES; + mark_stack_top = (*PROC(descr))((word *)current_p, mark_stack_top, + mark_stack_limit, ENV(descr)); + continue; + case GC_DS_PER_OBJECT: + if (!(descr & SIGNB)) { + /* Descriptor is in the object. */ + descr = *(word *)(current_p + descr - GC_DS_PER_OBJECT); + } else { + /* Descriptor is in type descriptor pointed to by first */ + /* word in object. */ + ptr_t type_descr = *(ptr_t *)current_p; + /* type_descr is either a valid pointer to the descriptor */ + /* structure, or this object was on a free list. */ + /* If it was anything but the last object on the free list, */ + /* we will misinterpret the next object on the free list as */ + /* the type descriptor, and get a 0 GC descriptor, which */ + /* is ideal. Unfortunately, we need to check for the last */ + /* object case explicitly. */ + if (EXPECT(0 == type_descr, FALSE)) { + mark_stack_top--; + continue; + } + descr = *(word *)(type_descr + - ((signed_word)descr + (GC_INDIR_PER_OBJ_BIAS + - GC_DS_PER_OBJECT))); + } + if (0 == descr) { + /* Can happen either because we generated a 0 descriptor */ + /* or we saw a pointer to a free object. */ + mark_stack_top--; + continue; + } + goto retry; + } + } else { + /* Small object with length descriptor. */ + mark_stack_top--; +# ifndef SMALL_CONFIG + if (descr < sizeof(word)) + continue; +# endif +# ifdef ENABLE_TRACE + if ((word)GC_trace_addr >= (word)current_p + && (word)GC_trace_addr < (word)(current_p + descr)) { + GC_log_printf("GC #%lu: small object; start %p, len %lu\n", + (unsigned long)GC_gc_no, (void *)current_p, + (unsigned long)descr); + } +# endif + limit = current_p + (word)descr; + } + /* The simple case in which we're scanning a range. */ + GC_ASSERT(!((word)current_p & (ALIGNMENT-1))); + credit -= limit - current_p; + limit -= sizeof(word); + { +# define PREF_DIST 4 + +# if !defined(SMALL_CONFIG) && !defined(USE_PTR_HWTAG) + word deferred; + + /* Try to prefetch the next pointer to be examined ASAP. */ + /* Empirically, this also seems to help slightly without */ + /* prefetches, at least on linux/x86. Presumably this loop */ + /* ends up with less register pressure, and gcc thus ends up */ + /* generating slightly better code. Overall gcc code quality */ + /* for this loop is still not great. */ + for(;;) { + PREFETCH(limit - PREF_DIST*CACHE_LINE_SIZE); + GC_ASSERT((word)limit >= (word)current_p); + deferred = *(word *)limit; + FIXUP_POINTER(deferred); + limit -= ALIGNMENT; + if (deferred > (word)least_ha && deferred < (word)greatest_ha) { + PREFETCH((ptr_t)deferred); + break; + } + if ((word)current_p > (word)limit) goto next_object; + /* Unroll once, so we don't do too many of the prefetches */ + /* based on limit. */ + deferred = *(word *)limit; + FIXUP_POINTER(deferred); + limit -= ALIGNMENT; + if (deferred > (word)least_ha && deferred < (word)greatest_ha) { + PREFETCH((ptr_t)deferred); + break; + } + if ((word)current_p > (word)limit) goto next_object; + } +# endif + + for (; (word)current_p <= (word)limit; current_p += ALIGNMENT) { + /* Empirically, unrolling this loop doesn't help a lot. */ + /* Since PUSH_CONTENTS expands to a lot of code, */ + /* we don't. */ + LOAD_WORD_OR_CONTINUE(current, current_p); + FIXUP_POINTER(current); + PREFETCH(current_p + PREF_DIST*CACHE_LINE_SIZE); + if (current > (word)least_ha && current < (word)greatest_ha) { + /* Prefetch the contents of the object we just pushed. It's */ + /* likely we will need them soon. */ + PREFETCH((ptr_t)current); +# ifdef ENABLE_TRACE + if (GC_trace_addr == current_p) { + GC_log_printf("GC #%lu: considering(1) %p -> %p\n", + (unsigned long)GC_gc_no, (void *)current_p, + (void *)current); + } +# endif /* ENABLE_TRACE */ + PUSH_CONTENTS((ptr_t)current, mark_stack_top, + mark_stack_limit, current_p); + } + } + +# if !defined(SMALL_CONFIG) && !defined(USE_PTR_HWTAG) + /* We still need to mark the entry we previously prefetched. */ + /* We already know that it passes the preliminary pointer */ + /* validity test. */ +# ifdef ENABLE_TRACE + if (GC_trace_addr == current_p) { + GC_log_printf("GC #%lu: considering(2) %p -> %p\n", + (unsigned long)GC_gc_no, (void *)current_p, + (void *)deferred); + } +# endif /* ENABLE_TRACE */ + PUSH_CONTENTS((ptr_t)deferred, mark_stack_top, + mark_stack_limit, current_p); + next_object:; +# endif + } + } + return mark_stack_top; +} + +#ifdef PARALLEL_MARK + +STATIC GC_bool GC_help_wanted = FALSE; /* Protected by the mark lock. */ +STATIC unsigned GC_helper_count = 0; /* Number of running helpers. */ + /* Protected by the mark lock. */ +STATIC unsigned GC_active_count = 0; /* Number of active helpers. */ + /* Protected by the mark lock. */ + /* May increase and decrease */ + /* within each mark cycle. But */ + /* once it returns to 0, it */ + /* stays zero for the cycle. */ + +GC_INNER word GC_mark_no = 0; + +#ifdef LINT2 +# define LOCAL_MARK_STACK_SIZE (HBLKSIZE / 8) +#else +# define LOCAL_MARK_STACK_SIZE HBLKSIZE + /* Under normal circumstances, this is big enough to guarantee */ + /* we don't overflow half of it in a single call to */ + /* GC_mark_from. */ +#endif + +/* Wait all markers to finish initialization (i.e. store */ +/* marker_[b]sp, marker_mach_threads, GC_marker_Id). */ +GC_INNER void GC_wait_for_markers_init(void) +{ + signed_word count; + + GC_ASSERT(I_HOLD_LOCK()); + if (GC_markers_m1 == 0) + return; + + /* Allocate the local mark stack for the thread that holds */ + /* the allocator lock. */ +# ifndef CAN_HANDLE_FORK + GC_ASSERT(NULL == GC_main_local_mark_stack); +# else + if (NULL == GC_main_local_mark_stack) +# endif + { + size_t bytes_to_get = + ROUNDUP_PAGESIZE_IF_MMAP(LOCAL_MARK_STACK_SIZE * sizeof(mse)); + + GC_ASSERT(GC_page_size != 0); + GC_main_local_mark_stack = (mse *)GC_os_get_mem(bytes_to_get); + if (NULL == GC_main_local_mark_stack) + ABORT("Insufficient memory for main local_mark_stack"); + } + + /* Reuse the mark lock and builders count to synchronize */ + /* marker threads startup. */ + GC_acquire_mark_lock(); + GC_fl_builder_count += GC_markers_m1; + count = GC_fl_builder_count; + GC_release_mark_lock(); + if (count != 0) { + GC_ASSERT(count > 0); + GC_wait_for_reclaim(); + } +} + +/* Steal mark stack entries starting at mse low into mark stack local */ +/* until we either steal mse high, or we have max entries. */ +/* Return a pointer to the top of the local mark stack. */ +/* (*next) is replaced by a pointer to the next unscanned mark stack */ +/* entry. */ +STATIC mse * GC_steal_mark_stack(mse * low, mse * high, mse * local, + unsigned max, mse **next) +{ + mse *p; + mse *top = local - 1; + unsigned i = 0; + + GC_ASSERT((word)high >= (word)(low - 1) + && (word)(high - low + 1) <= GC_mark_stack_size); + for (p = low; (word)p <= (word)high && i <= max; ++p) { + word descr = (word)AO_load(&p->mse_descr.ao); + if (descr != 0) { + /* Must be ordered after read of descr: */ + AO_store_release_write(&p->mse_descr.ao, 0); + /* More than one thread may get this entry, but that's only */ + /* a minor performance problem. */ + ++top; + top -> mse_descr.w = descr; + top -> mse_start = p -> mse_start; + GC_ASSERT((descr & GC_DS_TAGS) != GC_DS_LENGTH /* 0 */ + || descr < GC_greatest_real_heap_addr-GC_least_real_heap_addr + || (word)(p -> mse_start + descr) + <= GC_least_real_heap_addr + sizeof(word) + || (word)(p -> mse_start) >= GC_greatest_real_heap_addr); + /* If this is a big object, count it as size/256 + 1 objects. */ + ++i; + if ((descr & GC_DS_TAGS) == GC_DS_LENGTH) i += (int)(descr >> 8); + } + } + *next = p; + return top; +} + +/* Copy back a local mark stack. */ +/* low and high are inclusive bounds. */ +STATIC void GC_return_mark_stack(mse * low, mse * high) +{ + mse * my_top; + mse * my_start; + size_t stack_size; + + if ((word)high < (word)low) return; + stack_size = high - low + 1; + GC_acquire_mark_lock(); + my_top = GC_mark_stack_top; /* Concurrent modification impossible. */ + my_start = my_top + 1; + if ((word)(my_start - GC_mark_stack + stack_size) + > (word)GC_mark_stack_size) { + GC_COND_LOG_PRINTF("No room to copy back mark stack\n"); + GC_mark_state = MS_INVALID; + GC_mark_stack_too_small = TRUE; + /* We drop the local mark stack. We'll fix things later. */ + } else { + BCOPY(low, my_start, stack_size * sizeof(mse)); + GC_ASSERT((mse *)AO_load((volatile AO_t *)(&GC_mark_stack_top)) + == my_top); + AO_store_release_write((volatile AO_t *)(&GC_mark_stack_top), + (AO_t)(my_top + stack_size)); + /* Ensures visibility of previously written stack contents. */ + } + GC_release_mark_lock(); + GC_notify_all_marker(); +} + +#ifndef N_LOCAL_ITERS +# define N_LOCAL_ITERS 1 +#endif + +/* This function is only called when the local */ +/* and the main mark stacks are both empty. */ +static GC_bool has_inactive_helpers(void) +{ + GC_bool res; + + GC_acquire_mark_lock(); + res = GC_active_count < GC_helper_count; + GC_release_mark_lock(); + return res; +} + +/* Mark from the local mark stack. */ +/* On return, the local mark stack is empty. */ +/* But this may be achieved by copying the */ +/* local mark stack back into the global one. */ +/* We do not hold the mark lock. */ +STATIC void GC_do_local_mark(mse *local_mark_stack, mse *local_top) +{ + unsigned n; + + for (;;) { + for (n = 0; n < N_LOCAL_ITERS; ++n) { + local_top = GC_mark_from(local_top, local_mark_stack, + local_mark_stack + LOCAL_MARK_STACK_SIZE); + if ((word)local_top < (word)local_mark_stack) return; + if ((word)(local_top - local_mark_stack) + >= LOCAL_MARK_STACK_SIZE / 2) { + GC_return_mark_stack(local_mark_stack, local_top); + return; + } + } + if ((word)AO_load((volatile AO_t *)&GC_mark_stack_top) + < (word)AO_load(&GC_first_nonempty) + && (word)local_top > (word)(local_mark_stack + 1) + && has_inactive_helpers()) { + /* Try to share the load, since the main stack is empty, */ + /* and helper threads are waiting for a refill. */ + /* The entries near the bottom of the stack are likely */ + /* to require more work. Thus we return those, even though */ + /* it's harder. */ + mse * new_bottom = local_mark_stack + + (local_top - local_mark_stack)/2; + GC_ASSERT((word)new_bottom > (word)local_mark_stack + && (word)new_bottom < (word)local_top); + GC_return_mark_stack(local_mark_stack, new_bottom - 1); + memmove(local_mark_stack, new_bottom, + (local_top - new_bottom + 1) * sizeof(mse)); + local_top -= (new_bottom - local_mark_stack); + } + } +} + +#ifndef ENTRIES_TO_GET +# define ENTRIES_TO_GET 5 +#endif + +/* Mark using the local mark stack until the global mark stack is empty */ +/* and there are no active workers. Update GC_first_nonempty to reflect */ +/* progress. Caller holds the mark lock. */ +/* Caller has already incremented GC_helper_count. We decrement it, */ +/* and maintain GC_active_count. */ +STATIC void GC_mark_local(mse *local_mark_stack, int id) +{ + mse * my_first_nonempty; + + GC_active_count++; + my_first_nonempty = (mse *)AO_load(&GC_first_nonempty); + GC_ASSERT((word)GC_mark_stack <= (word)my_first_nonempty); + GC_ASSERT((word)my_first_nonempty + <= (word)AO_load((volatile AO_t *)&GC_mark_stack_top) + sizeof(mse)); + GC_VERBOSE_LOG_PRINTF("Starting mark helper %d\n", id); + GC_release_mark_lock(); + for (;;) { + size_t n_on_stack; + unsigned n_to_get; + mse * my_top; + mse * local_top; + mse * global_first_nonempty = (mse *)AO_load(&GC_first_nonempty); + + GC_ASSERT((word)my_first_nonempty >= (word)GC_mark_stack && + (word)my_first_nonempty <= + (word)AO_load((volatile AO_t *)&GC_mark_stack_top) + + sizeof(mse)); + GC_ASSERT((word)global_first_nonempty >= (word)GC_mark_stack); + if ((word)my_first_nonempty < (word)global_first_nonempty) { + my_first_nonempty = global_first_nonempty; + } else if ((word)global_first_nonempty < (word)my_first_nonempty) { + (void)AO_compare_and_swap(&GC_first_nonempty, + (AO_t)global_first_nonempty, + (AO_t)my_first_nonempty); + /* If this fails, we just go ahead, without updating */ + /* GC_first_nonempty. */ + } + /* Perhaps we should also update GC_first_nonempty, if it */ + /* is less. But that would require using atomic updates. */ + my_top = (mse *)AO_load_acquire((volatile AO_t *)(&GC_mark_stack_top)); + if ((word)my_top < (word)my_first_nonempty) { + GC_acquire_mark_lock(); + my_top = GC_mark_stack_top; + /* Asynchronous modification impossible here, */ + /* since we hold the mark lock. */ + n_on_stack = my_top - my_first_nonempty + 1; + if (0 == n_on_stack) { + GC_active_count--; + GC_ASSERT(GC_active_count <= GC_helper_count); + /* Other markers may redeposit objects */ + /* on the stack. */ + if (0 == GC_active_count) GC_notify_all_marker(); + while (GC_active_count > 0 + && (word)AO_load(&GC_first_nonempty) + > (word)GC_mark_stack_top) { + /* We will be notified if either GC_active_count */ + /* reaches zero, or if more objects are pushed on */ + /* the global mark stack. */ + GC_wait_marker(); + } + if (GC_active_count == 0 + && (word)AO_load(&GC_first_nonempty) + > (word)GC_mark_stack_top) { + GC_bool need_to_notify = FALSE; + /* The above conditions can't be falsified while we */ + /* hold the mark lock, since neither */ + /* GC_active_count nor GC_mark_stack_top can */ + /* change. GC_first_nonempty can only be */ + /* incremented asynchronously. Thus we know that */ + /* both conditions actually held simultaneously. */ + GC_helper_count--; + if (0 == GC_helper_count) need_to_notify = TRUE; + GC_VERBOSE_LOG_PRINTF("Finished mark helper %d\n", id); + if (need_to_notify) GC_notify_all_marker(); + return; + } + /* Else there's something on the stack again, or */ + /* another helper may push something. */ + GC_active_count++; + GC_ASSERT(GC_active_count > 0); + GC_release_mark_lock(); + continue; + } else { + GC_release_mark_lock(); + } + } else { + n_on_stack = my_top - my_first_nonempty + 1; + } + n_to_get = ENTRIES_TO_GET; + if (n_on_stack < 2 * ENTRIES_TO_GET) n_to_get = 1; + local_top = GC_steal_mark_stack(my_first_nonempty, my_top, + local_mark_stack, n_to_get, + &my_first_nonempty); + GC_ASSERT((word)my_first_nonempty >= (word)GC_mark_stack && + (word)my_first_nonempty <= + (word)AO_load((volatile AO_t *)&GC_mark_stack_top) + + sizeof(mse)); + GC_do_local_mark(local_mark_stack, local_top); + } +} + +/* Perform parallel mark. We hold the allocator lock, but not the mark */ +/* lock. Currently runs until the mark stack is empty. */ +STATIC void GC_do_parallel_mark(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + GC_acquire_mark_lock(); + + /* This could be a GC_ASSERT, but it seems safer to keep it on */ + /* all the time, especially since it's cheap. */ + if (GC_help_wanted || GC_active_count != 0 || GC_helper_count != 0) + ABORT("Tried to start parallel mark in bad state"); + GC_VERBOSE_LOG_PRINTF("Starting marking for mark phase number %lu\n", + (unsigned long)GC_mark_no); + GC_first_nonempty = (AO_t)GC_mark_stack; + GC_active_count = 0; + GC_helper_count = 1; + GC_help_wanted = TRUE; + GC_notify_all_marker(); + /* Wake up potential helpers. */ + GC_mark_local(GC_main_local_mark_stack, 0); + GC_help_wanted = FALSE; + /* Done; clean up. */ + while (GC_helper_count > 0) { + GC_wait_marker(); + } + /* GC_helper_count cannot be incremented while not GC_help_wanted. */ + GC_VERBOSE_LOG_PRINTF("Finished marking for mark phase number %lu\n", + (unsigned long)GC_mark_no); + GC_mark_no++; + GC_release_mark_lock(); + GC_notify_all_marker(); +} + +/* Try to help out the marker, if it's running. We hold the mark lock */ +/* only, the initiating thread holds the allocator lock. */ +GC_INNER void GC_help_marker(word my_mark_no) +{ +# define my_id my_id_mse.mse_descr.w + mse my_id_mse; /* align local_mark_stack explicitly */ + mse local_mark_stack[LOCAL_MARK_STACK_SIZE]; + /* Note: local_mark_stack is quite big (up to 128 KiB). */ + + GC_ASSERT(I_DONT_HOLD_LOCK()); + GC_ASSERT(GC_parallel); + while (GC_mark_no < my_mark_no + || (!GC_help_wanted && GC_mark_no == my_mark_no)) { + GC_wait_marker(); + } + my_id = GC_helper_count; + if (GC_mark_no != my_mark_no || my_id > (unsigned)GC_markers_m1) { + /* Second test is useful only if original threads can also */ + /* act as helpers. Under Linux they can't. */ + return; + } + GC_helper_count = (unsigned)my_id + 1; + GC_mark_local(local_mark_stack, (int)my_id); + /* GC_mark_local decrements GC_helper_count. */ +# undef my_id +} + +#endif /* PARALLEL_MARK */ + +/* Allocate or reallocate space for mark stack of size n entries. */ +/* May silently fail. */ +static void alloc_mark_stack(size_t n) +{ +# ifdef GWW_VDB + static GC_bool GC_incremental_at_stack_alloc = FALSE; + + GC_bool recycle_old; +# endif + mse * new_stack; + + GC_ASSERT(I_HOLD_LOCK()); + new_stack = (mse *)GC_scratch_alloc(n * sizeof(struct GC_ms_entry)); +# ifdef GWW_VDB + /* Don't recycle a stack segment obtained with the wrong flags. */ + /* Win32 GetWriteWatch requires the right kind of memory. */ + recycle_old = !GC_auto_incremental || GC_incremental_at_stack_alloc; + GC_incremental_at_stack_alloc = GC_auto_incremental; +# endif + + GC_mark_stack_too_small = FALSE; + if (GC_mark_stack != NULL) { + if (new_stack != 0) { +# ifdef GWW_VDB + if (recycle_old) +# endif + { + /* Recycle old space. */ + GC_scratch_recycle_inner(GC_mark_stack, + GC_mark_stack_size * sizeof(struct GC_ms_entry)); + } + GC_mark_stack = new_stack; + GC_mark_stack_size = n; + /* FIXME: Do we need some way to reset GC_mark_stack_size? */ + GC_mark_stack_limit = new_stack + n; + GC_COND_LOG_PRINTF("Grew mark stack to %lu frames\n", + (unsigned long)GC_mark_stack_size); + } else { + WARN("Failed to grow mark stack to %" WARN_PRIuPTR " frames\n", n); + } + } else if (NULL == new_stack) { + GC_err_printf("No space for mark stack\n"); + EXIT(); + } else { + GC_mark_stack = new_stack; + GC_mark_stack_size = n; + GC_mark_stack_limit = new_stack + n; + } + GC_mark_stack_top = GC_mark_stack-1; +} + +GC_INNER void GC_mark_init(void) +{ + alloc_mark_stack(INITIAL_MARK_STACK_SIZE); +} + +/* + * Push all locations between b and t onto the mark stack. + * b is the first location to be checked. t is one past the last + * location to be checked. + * Should only be used if there is no possibility of mark stack + * overflow. + */ +GC_API void GC_CALL GC_push_all(void *bottom, void *top) +{ + mse * mark_stack_top; + word length; + + bottom = PTRT_ROUNDUP_BY_MASK(bottom, ALIGNMENT-1); + top = (void *)((word)top & ~(word)(ALIGNMENT-1)); + if ((word)bottom >= (word)top) return; + + mark_stack_top = GC_mark_stack_top + 1; + if ((word)mark_stack_top >= (word)GC_mark_stack_limit) { + ABORT("Unexpected mark stack overflow"); + } + length = (word)top - (word)bottom; +# if GC_DS_TAGS > ALIGNMENT - 1 + length = (length + GC_DS_TAGS) & ~(word)GC_DS_TAGS; /* round up */ +# endif + mark_stack_top -> mse_start = (ptr_t)bottom; + mark_stack_top -> mse_descr.w = length | GC_DS_LENGTH; + GC_mark_stack_top = mark_stack_top; +} + +#ifndef GC_DISABLE_INCREMENTAL + + /* Analogous to the above, but push only those pages h with */ + /* dirty_fn(h) != 0. We use GC_push_all to actually push the block. */ + /* Used both to selectively push dirty pages, or to push a block in */ + /* piecemeal fashion, to allow for more marking concurrency. */ + /* Will not overflow mark stack if GC_push_all pushes a small fixed */ + /* number of entries. (This is invoked only if GC_push_all pushes */ + /* a single entry, or if it marks each object before pushing it, thus */ + /* ensuring progress in the event of a stack overflow.) */ + STATIC void GC_push_selected(ptr_t bottom, ptr_t top, + GC_bool (*dirty_fn)(struct hblk *)) + { + struct hblk * h; + + bottom = PTRT_ROUNDUP_BY_MASK(bottom, ALIGNMENT-1); + top = (ptr_t)((word)top & ~(word)(ALIGNMENT-1)); + if ((word)bottom >= (word)top) return; + + h = HBLKPTR(bottom + HBLKSIZE); + if ((word)top <= (word)h) { + if ((*dirty_fn)(h-1)) { + GC_push_all(bottom, top); + } + return; + } + if ((*dirty_fn)(h-1)) { + if ((word)(GC_mark_stack_top - GC_mark_stack) + > 3 * GC_mark_stack_size / 4) { + GC_push_all(bottom, top); + return; + } + GC_push_all(bottom, h); + } + + while ((word)(h+1) <= (word)top) { + if ((*dirty_fn)(h)) { + if ((word)(GC_mark_stack_top - GC_mark_stack) + > 3 * GC_mark_stack_size / 4) { + /* Danger of mark stack overflow. */ + GC_push_all(h, top); + return; + } else { + GC_push_all(h, h + 1); + } + } + h++; + } + + if ((ptr_t)h != top && (*dirty_fn)(h)) { + GC_push_all(h, top); + } + } + + GC_API void GC_CALL GC_push_conditional(void *bottom, void *top, int all) + { + if (!all) { + GC_push_selected((ptr_t)bottom, (ptr_t)top, GC_page_was_dirty); + } else { +# ifdef PROC_VDB + if (GC_auto_incremental) { + /* Pages that were never dirtied cannot contain pointers. */ + GC_push_selected((ptr_t)bottom, (ptr_t)top, GC_page_was_ever_dirty); + } else +# endif + /* else */ { + GC_push_all(bottom, top); + } + } + } + +# ifndef NO_VDB_FOR_STATIC_ROOTS +# ifndef PROC_VDB + /* Same as GC_page_was_dirty but h is allowed to point to some */ + /* page in the registered static roots only. Not used if */ + /* manual VDB is on. */ + STATIC GC_bool GC_static_page_was_dirty(struct hblk *h) + { + return get_pht_entry_from_index(GC_grungy_pages, PHT_HASH(h)); + } +# endif + + GC_INNER void GC_push_conditional_static(void *bottom, void *top, + GC_bool all) + { +# ifdef PROC_VDB + /* Just redirect to the generic routine because PROC_VDB */ + /* implementation gets the dirty bits map for the whole */ + /* process memory. */ + GC_push_conditional(bottom, top, all); +# else + if (all || !GC_is_vdb_for_static_roots()) { + GC_push_all(bottom, top); + } else { + GC_push_selected((ptr_t)bottom, (ptr_t)top, + GC_static_page_was_dirty); + } +# endif + } +# endif /* !NO_VDB_FOR_STATIC_ROOTS */ + +#else + GC_API void GC_CALL GC_push_conditional(void *bottom, void *top, int all) + { + UNUSED_ARG(all); + GC_push_all(bottom, top); + } +#endif /* GC_DISABLE_INCREMENTAL */ + +#if defined(AMIGA) || defined(MACOS) || defined(GC_DARWIN_THREADS) + void GC_push_one(word p) + { + GC_PUSH_ONE_STACK(p, MARKED_FROM_REGISTER); + } +#endif + +#ifdef GC_WIN32_THREADS + GC_INNER void GC_push_many_regs(const word *regs, unsigned count) + { + unsigned i; + for (i = 0; i < count; i++) + GC_PUSH_ONE_STACK(regs[i], MARKED_FROM_REGISTER); + } +#endif + +GC_API struct GC_ms_entry * GC_CALL GC_mark_and_push(void *obj, + mse *mark_stack_ptr, mse *mark_stack_limit, void **src) +{ + hdr * hhdr; + + PREFETCH(obj); + GET_HDR(obj, hhdr); + if ((EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr), FALSE) + && (!GC_all_interior_pointers + || NULL == (hhdr = GC_find_header((ptr_t)GC_base(obj))))) + || EXPECT(HBLK_IS_FREE(hhdr), FALSE)) { + GC_ADD_TO_BLACK_LIST_NORMAL(obj, (ptr_t)src); + return mark_stack_ptr; + } + return GC_push_contents_hdr((ptr_t)obj, mark_stack_ptr, mark_stack_limit, + (ptr_t)src, hhdr, TRUE); +} + +/* Mark and push (i.e. gray) a single object p onto the main */ +/* mark stack. Consider p to be valid if it is an interior */ +/* pointer. */ +/* The object p has passed a preliminary pointer validity */ +/* test, but we do not definitely know whether it is valid. */ +/* Mark bits are NOT atomically updated. Thus this must be the */ +/* only thread setting them. */ +GC_ATTR_NO_SANITIZE_ADDR +GC_INNER void +# if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS) + GC_mark_and_push_stack(ptr_t p, ptr_t source) +# else + GC_mark_and_push_stack(ptr_t p) +# define source ((ptr_t)0) +# endif +{ + hdr * hhdr; + ptr_t r = p; + + PREFETCH(p); + GET_HDR(p, hhdr); + if (EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr), FALSE)) { + if (NULL == hhdr + || (r = (ptr_t)GC_base(p)) == NULL + || (hhdr = HDR(r)) == NULL) { + GC_ADD_TO_BLACK_LIST_STACK(p, source); + return; + } + } + if (EXPECT(HBLK_IS_FREE(hhdr), FALSE)) { + GC_ADD_TO_BLACK_LIST_NORMAL(p, source); + return; + } +# ifdef THREADS + /* Pointer is on the stack. We may have dirtied the object */ + /* it points to, but have not called GC_dirty yet. */ + GC_dirty(p); /* entire object */ +# endif + GC_mark_stack_top = GC_push_contents_hdr(r, GC_mark_stack_top, + GC_mark_stack_limit, + source, hhdr, FALSE); + /* We silently ignore pointers to near the end of a block, */ + /* which is very mildly suboptimal. */ + /* FIXME: We should probably add a header word to address */ + /* this. */ +} +# undef source + +#ifdef TRACE_BUF + +# ifndef TRACE_ENTRIES +# define TRACE_ENTRIES 1000 +# endif + +struct trace_entry { + char * kind; + word gc_no; + word bytes_allocd; + word arg1; + word arg2; +} GC_trace_buf[TRACE_ENTRIES] = { { NULL, 0, 0, 0, 0 } }; + +void GC_add_trace_entry(char *kind, word arg1, word arg2) +{ + GC_trace_buf[GC_trace_buf_ptr].kind = kind; + GC_trace_buf[GC_trace_buf_ptr].gc_no = GC_gc_no; + GC_trace_buf[GC_trace_buf_ptr].bytes_allocd = GC_bytes_allocd; + GC_trace_buf[GC_trace_buf_ptr].arg1 = arg1 ^ SIGNB; + GC_trace_buf[GC_trace_buf_ptr].arg2 = arg2 ^ SIGNB; + GC_trace_buf_ptr++; + if (GC_trace_buf_ptr >= TRACE_ENTRIES) GC_trace_buf_ptr = 0; +} + +GC_API void GC_CALL GC_print_trace_inner(GC_word gc_no) +{ + int i; + + for (i = GC_trace_buf_ptr-1; i != GC_trace_buf_ptr; i--) { + struct trace_entry *p; + + if (i < 0) i = TRACE_ENTRIES-1; + p = GC_trace_buf + i; + /* Compare gc_no values (p->gc_no is less than given gc_no) */ + /* taking into account that the counter may overflow. */ + if ((((p -> gc_no) - gc_no) & SIGNB) != 0 || p -> kind == 0) { + return; + } + GC_printf("Trace:%s (gc:%u, bytes:%lu) %p, %p\n", + p -> kind, (unsigned)(p -> gc_no), + (unsigned long)(p -> bytes_allocd), + (void *)(p -> arg1 ^ SIGNB), (void *)(p -> arg2 ^ SIGNB)); + } + GC_printf("Trace incomplete\n"); +} + +GC_API void GC_CALL GC_print_trace(GC_word gc_no) +{ + READER_LOCK(); + GC_print_trace_inner(gc_no); + READER_UNLOCK(); +} + +#endif /* TRACE_BUF */ + +/* A version of GC_push_all that treats all interior pointers as valid */ +/* and scans the entire region immediately, in case the contents change.*/ +GC_ATTR_NO_SANITIZE_ADDR GC_ATTR_NO_SANITIZE_MEMORY GC_ATTR_NO_SANITIZE_THREAD +GC_API void GC_CALL GC_push_all_eager(void *bottom, void *top) +{ + REGISTER ptr_t current_p; + REGISTER word *lim; + REGISTER ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + REGISTER ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr; +# define GC_greatest_plausible_heap_addr greatest_ha +# define GC_least_plausible_heap_addr least_ha + + if (top == 0) return; + + /* Check all pointers in range and push if they appear to be valid. */ + current_p = PTRT_ROUNDUP_BY_MASK(bottom, ALIGNMENT-1); + lim = (word *)((word)top & ~(word)(ALIGNMENT-1)) - 1; + for (; (word)current_p <= (word)lim; current_p += ALIGNMENT) { + REGISTER word q; + + LOAD_WORD_OR_CONTINUE(q, current_p); + GC_PUSH_ONE_STACK(q, current_p); + } +# undef GC_greatest_plausible_heap_addr +# undef GC_least_plausible_heap_addr +} + +GC_INNER void GC_push_all_stack(ptr_t bottom, ptr_t top) +{ + GC_ASSERT(I_HOLD_LOCK()); +# ifndef NEED_FIXUP_POINTER + if (GC_all_interior_pointers +# if defined(THREADS) && defined(MPROTECT_VDB) + && !GC_auto_incremental +# endif + && (word)GC_mark_stack_top + < (word)(GC_mark_stack_limit - INITIAL_MARK_STACK_SIZE/8)) { + GC_push_all(bottom, top); + } else +# endif + /* else */ { + GC_push_all_eager(bottom, top); + } +} + +#if defined(WRAP_MARK_SOME) && defined(PARALLEL_MARK) + /* Similar to GC_push_conditional but scans the whole region immediately. */ + GC_ATTR_NO_SANITIZE_ADDR GC_ATTR_NO_SANITIZE_MEMORY + GC_ATTR_NO_SANITIZE_THREAD + GC_INNER void GC_push_conditional_eager(void *bottom, void *top, + GC_bool all) + { + REGISTER ptr_t current_p; + REGISTER word *lim; + REGISTER ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + REGISTER ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr; +# define GC_greatest_plausible_heap_addr greatest_ha +# define GC_least_plausible_heap_addr least_ha + + if (top == NULL) + return; + (void)all; /* TODO: If !all then scan only dirty pages. */ + + current_p = PTRT_ROUNDUP_BY_MASK(bottom, ALIGNMENT-1); + lim = (word *)((word)top & ~(word)(ALIGNMENT-1)) - 1; + for (; (word)current_p <= (word)lim; current_p += ALIGNMENT) { + REGISTER word q; + + LOAD_WORD_OR_CONTINUE(q, current_p); + GC_PUSH_ONE_HEAP(q, current_p, GC_mark_stack_top); + } +# undef GC_greatest_plausible_heap_addr +# undef GC_least_plausible_heap_addr + } +#endif /* WRAP_MARK_SOME && PARALLEL_MARK */ + +#if !defined(SMALL_CONFIG) && !defined(USE_MARK_BYTES) \ + && !defined(MARK_BIT_PER_OBJ) +# if GC_GRANULE_WORDS == 1 +# define USE_PUSH_MARKED_ACCELERATORS +# define PUSH_GRANULE(q) \ + do { \ + word qcontents = (q)[0]; \ + GC_PUSH_ONE_HEAP(qcontents, q, GC_mark_stack_top); \ + } while (0) +# elif GC_GRANULE_WORDS == 2 +# define USE_PUSH_MARKED_ACCELERATORS +# define PUSH_GRANULE(q) \ + do { \ + word qcontents = (q)[0]; \ + GC_PUSH_ONE_HEAP(qcontents, q, GC_mark_stack_top); \ + qcontents = (q)[1]; \ + GC_PUSH_ONE_HEAP(qcontents, (q)+1, GC_mark_stack_top); \ + } while (0) +# elif GC_GRANULE_WORDS == 4 +# define USE_PUSH_MARKED_ACCELERATORS +# define PUSH_GRANULE(q) \ + do { \ + word qcontents = (q)[0]; \ + GC_PUSH_ONE_HEAP(qcontents, q, GC_mark_stack_top); \ + qcontents = (q)[1]; \ + GC_PUSH_ONE_HEAP(qcontents, (q)+1, GC_mark_stack_top); \ + qcontents = (q)[2]; \ + GC_PUSH_ONE_HEAP(qcontents, (q)+2, GC_mark_stack_top); \ + qcontents = (q)[3]; \ + GC_PUSH_ONE_HEAP(qcontents, (q)+3, GC_mark_stack_top); \ + } while (0) +# endif +#endif /* !USE_MARK_BYTES && !MARK_BIT_PER_OBJ && !SMALL_CONFIG */ + +#ifdef USE_PUSH_MARKED_ACCELERATORS +/* Push all objects reachable from marked objects in the given block */ +/* containing objects of size 1 granule. */ +GC_ATTR_NO_SANITIZE_THREAD +STATIC void GC_push_marked1(struct hblk *h, hdr *hhdr) +{ + word * mark_word_addr = &(hhdr->hb_marks[0]); + word *p; + word *plim; + + /* Allow registers to be used for some frequently accessed */ + /* global variables. Otherwise aliasing issues are likely */ + /* to prevent that. */ + ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr; + mse * mark_stack_top = GC_mark_stack_top; + mse * mark_stack_limit = GC_mark_stack_limit; + +# undef GC_mark_stack_top +# undef GC_mark_stack_limit +# define GC_mark_stack_top mark_stack_top +# define GC_mark_stack_limit mark_stack_limit +# define GC_greatest_plausible_heap_addr greatest_ha +# define GC_least_plausible_heap_addr least_ha + + p = (word *)(h->hb_body); + plim = (word *)(((word)h) + HBLKSIZE); + + /* Go through all words in block. */ + while ((word)p < (word)plim) { + word mark_word = *mark_word_addr++; + word *q = p; + + while(mark_word != 0) { + if (mark_word & 1) { + PUSH_GRANULE(q); + } + q += GC_GRANULE_WORDS; + mark_word >>= 1; + } + p += CPP_WORDSZ * GC_GRANULE_WORDS; + } + +# undef GC_greatest_plausible_heap_addr +# undef GC_least_plausible_heap_addr +# undef GC_mark_stack_top +# undef GC_mark_stack_limit +# define GC_mark_stack_limit GC_arrays._mark_stack_limit +# define GC_mark_stack_top GC_arrays._mark_stack_top + GC_mark_stack_top = mark_stack_top; +} + + +#ifndef UNALIGNED_PTRS + +/* Push all objects reachable from marked objects in the given block */ +/* of size 2 (granules) objects. */ +GC_ATTR_NO_SANITIZE_THREAD +STATIC void GC_push_marked2(struct hblk *h, hdr *hhdr) +{ + word * mark_word_addr = &(hhdr->hb_marks[0]); + word *p; + word *plim; + + ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr; + mse * mark_stack_top = GC_mark_stack_top; + mse * mark_stack_limit = GC_mark_stack_limit; + +# undef GC_mark_stack_top +# undef GC_mark_stack_limit +# define GC_mark_stack_top mark_stack_top +# define GC_mark_stack_limit mark_stack_limit +# define GC_greatest_plausible_heap_addr greatest_ha +# define GC_least_plausible_heap_addr least_ha + + p = (word *)(h->hb_body); + plim = (word *)(((word)h) + HBLKSIZE); + + /* Go through all words in block. */ + while ((word)p < (word)plim) { + word mark_word = *mark_word_addr++; + word *q = p; + + while(mark_word != 0) { + if (mark_word & 1) { + PUSH_GRANULE(q); + PUSH_GRANULE(q + GC_GRANULE_WORDS); + } + q += 2 * GC_GRANULE_WORDS; + mark_word >>= 2; + } + p += CPP_WORDSZ * GC_GRANULE_WORDS; + } + +# undef GC_greatest_plausible_heap_addr +# undef GC_least_plausible_heap_addr +# undef GC_mark_stack_top +# undef GC_mark_stack_limit +# define GC_mark_stack_limit GC_arrays._mark_stack_limit +# define GC_mark_stack_top GC_arrays._mark_stack_top + GC_mark_stack_top = mark_stack_top; +} + +# if GC_GRANULE_WORDS < 4 +/* Push all objects reachable from marked objects in the given block */ +/* of size 4 (granules) objects. */ +/* There is a risk of mark stack overflow here. But we handle that. */ +/* And only unmarked objects get pushed, so it's not very likely. */ +GC_ATTR_NO_SANITIZE_THREAD +STATIC void GC_push_marked4(struct hblk *h, hdr *hhdr) +{ + word * mark_word_addr = &(hhdr->hb_marks[0]); + word *p; + word *plim; + + ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr; + mse * mark_stack_top = GC_mark_stack_top; + mse * mark_stack_limit = GC_mark_stack_limit; + +# undef GC_mark_stack_top +# undef GC_mark_stack_limit +# define GC_mark_stack_top mark_stack_top +# define GC_mark_stack_limit mark_stack_limit +# define GC_greatest_plausible_heap_addr greatest_ha +# define GC_least_plausible_heap_addr least_ha + + p = (word *)(h->hb_body); + plim = (word *)(((word)h) + HBLKSIZE); + + /* Go through all words in block. */ + while ((word)p < (word)plim) { + word mark_word = *mark_word_addr++; + word *q = p; + + while(mark_word != 0) { + if (mark_word & 1) { + PUSH_GRANULE(q); + PUSH_GRANULE(q + GC_GRANULE_WORDS); + PUSH_GRANULE(q + 2*GC_GRANULE_WORDS); + PUSH_GRANULE(q + 3*GC_GRANULE_WORDS); + } + q += 4 * GC_GRANULE_WORDS; + mark_word >>= 4; + } + p += CPP_WORDSZ * GC_GRANULE_WORDS; + } +# undef GC_greatest_plausible_heap_addr +# undef GC_least_plausible_heap_addr +# undef GC_mark_stack_top +# undef GC_mark_stack_limit +# define GC_mark_stack_limit GC_arrays._mark_stack_limit +# define GC_mark_stack_top GC_arrays._mark_stack_top + GC_mark_stack_top = mark_stack_top; +} + +#endif /* GC_GRANULE_WORDS < 4 */ + +#endif /* UNALIGNED_PTRS */ + +#endif /* USE_PUSH_MARKED_ACCELERATORS */ + +/* Push all objects reachable from marked objects in the given block. */ +STATIC void GC_push_marked(struct hblk *h, hdr *hhdr) +{ + word sz = hhdr -> hb_sz; + word descr = hhdr -> hb_descr; + ptr_t p; + word bit_no; + ptr_t lim; + mse * mark_stack_top; + mse * mark_stack_limit = GC_mark_stack_limit; + + /* Some quick shortcuts: */ + if ((/* 0 | */ GC_DS_LENGTH) == descr) return; + if (GC_block_empty(hhdr)/* nothing marked */) return; + +# if !defined(GC_DISABLE_INCREMENTAL) + GC_n_rescuing_pages++; +# endif + GC_objects_are_marked = TRUE; + switch (BYTES_TO_GRANULES(sz)) { +# if defined(USE_PUSH_MARKED_ACCELERATORS) + case 1: + GC_push_marked1(h, hhdr); + break; +# if !defined(UNALIGNED_PTRS) + case 2: + GC_push_marked2(h, hhdr); + break; +# if GC_GRANULE_WORDS < 4 + case 4: + GC_push_marked4(h, hhdr); + break; +# endif +# endif /* !UNALIGNED_PTRS */ +# else + case 1: /* to suppress "switch statement contains no case" warning */ +# endif + default: + lim = sz > MAXOBJBYTES ? h -> hb_body + : (ptr_t)((word)(h + 1) -> hb_body - sz); + mark_stack_top = GC_mark_stack_top; + for (p = h -> hb_body, bit_no = 0; (word)p <= (word)lim; + p += sz, bit_no += MARK_BIT_OFFSET(sz)) { + /* Mark from fields inside the object. */ + if (mark_bit_from_hdr(hhdr, bit_no)) { + mark_stack_top = GC_push_obj(p, hhdr, mark_stack_top, + mark_stack_limit); + } + } + GC_mark_stack_top = mark_stack_top; + } +} + +#ifdef ENABLE_DISCLAIM +/* Unconditionally mark from all objects which have not been reclaimed. */ +/* This is useful in order to retain pointers which are reachable from */ +/* the disclaim notifiers. */ +/* To determine whether an object has been reclaimed, we require that */ +/* any live object has a non-zero as one of the two least significant */ +/* bits of the first word. On the other hand, a reclaimed object is */ +/* a members of free-lists, and thus contains a word-aligned */ +/* next-pointer as the first word. */ + GC_ATTR_NO_SANITIZE_THREAD + STATIC void GC_push_unconditionally(struct hblk *h, hdr *hhdr) + { + word sz = hhdr -> hb_sz; + word descr = hhdr -> hb_descr; + ptr_t p; + ptr_t lim; + mse * mark_stack_top; + mse * mark_stack_limit = GC_mark_stack_limit; + + if ((/* 0 | */ GC_DS_LENGTH) == descr) return; + +# if !defined(GC_DISABLE_INCREMENTAL) + GC_n_rescuing_pages++; +# endif + GC_objects_are_marked = TRUE; + lim = sz > MAXOBJBYTES ? h -> hb_body + : (ptr_t)((word)(h + 1) -> hb_body - sz); + mark_stack_top = GC_mark_stack_top; + for (p = h -> hb_body; (word)p <= (word)lim; p += sz) { + if ((*(word *)p & 0x3) != 0) { + mark_stack_top = GC_push_obj(p, hhdr, mark_stack_top, + mark_stack_limit); + } + } + GC_mark_stack_top = mark_stack_top; + } +#endif /* ENABLE_DISCLAIM */ + +#ifndef GC_DISABLE_INCREMENTAL + /* Test whether any page in the given block is dirty. */ + STATIC GC_bool GC_block_was_dirty(struct hblk *h, hdr *hhdr) + { + word sz; + ptr_t p; + +# ifdef AO_HAVE_load + /* Atomic access is used to avoid racing with GC_realloc. */ + sz = (word)AO_load((volatile AO_t *)&(hhdr -> hb_sz)); +# else + sz = hhdr -> hb_sz; +# endif + if (sz <= MAXOBJBYTES) { + return GC_page_was_dirty(h); + } + + for (p = (ptr_t)h; (word)p < (word)h + sz; p += HBLKSIZE) { + if (GC_page_was_dirty((struct hblk *)p)) return TRUE; + } + return FALSE; + } +#endif /* GC_DISABLE_INCREMENTAL */ + +/* Similar to GC_push_marked, but skip over unallocated blocks */ +/* and return address of next plausible block. */ +STATIC struct hblk * GC_push_next_marked(struct hblk *h) +{ + hdr * hhdr = HDR(h); + + if (EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr) || HBLK_IS_FREE(hhdr), FALSE)) { + h = GC_next_block(h, FALSE); + if (NULL == h) return NULL; + hhdr = GC_find_header((ptr_t)h); + } else { +# ifdef LINT2 + if (NULL == h) ABORT("Bad HDR() definition"); +# endif + } + GC_push_marked(h, hhdr); + return h + OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); +} + +#ifndef GC_DISABLE_INCREMENTAL + /* Identical to above, but mark only from dirty pages. */ + STATIC struct hblk * GC_push_next_marked_dirty(struct hblk *h) + { + hdr * hhdr; + + GC_ASSERT(I_HOLD_LOCK()); + if (!GC_incremental) ABORT("Dirty bits not set up"); + for (;; h += OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz)) { + hhdr = HDR(h); + if (EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr) + || HBLK_IS_FREE(hhdr), FALSE)) { + h = GC_next_block(h, FALSE); + if (NULL == h) return NULL; + hhdr = GC_find_header((ptr_t)h); + } else { +# ifdef LINT2 + if (NULL == h) ABORT("Bad HDR() definition"); +# endif + } + if (GC_block_was_dirty(h, hhdr)) + break; + } +# ifdef ENABLE_DISCLAIM + if ((hhdr -> hb_flags & MARK_UNCONDITIONALLY) != 0) { + GC_push_unconditionally(h, hhdr); + + /* Then we may ask, why not also add the MARK_UNCONDITIONALLY */ + /* case to GC_push_next_marked, which is also applied to */ + /* uncollectible blocks? But it seems to me that the function */ + /* does not need to scan uncollectible (and unconditionally */ + /* marked) blocks since those are already handled in the */ + /* MS_PUSH_UNCOLLECTABLE phase. */ + } else +# endif + /* else */ { + GC_push_marked(h, hhdr); + } + return h + OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); + } +#endif /* !GC_DISABLE_INCREMENTAL */ + +/* Similar to above, but for uncollectible pages. Needed since we */ +/* do not clear marks for such pages, even for full collections. */ +STATIC struct hblk * GC_push_next_marked_uncollectable(struct hblk *h) +{ + hdr * hhdr = HDR(h); + + for (;;) { + if (EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr) + || HBLK_IS_FREE(hhdr), FALSE)) { + h = GC_next_block(h, FALSE); + if (NULL == h) return NULL; + hhdr = GC_find_header((ptr_t)h); + } else { +# ifdef LINT2 + if (NULL == h) ABORT("Bad HDR() definition"); +# endif + } + if (hhdr -> hb_obj_kind == UNCOLLECTABLE) { + GC_push_marked(h, hhdr); + break; + } +# ifdef ENABLE_DISCLAIM + if ((hhdr -> hb_flags & MARK_UNCONDITIONALLY) != 0) { + GC_push_unconditionally(h, hhdr); + break; + } +# endif + h += OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); + hhdr = HDR(h); + } + return h + OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); +} + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 2009-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +#if defined(E2K) && !defined(THREADS) +# include +#endif + +/* Data structure for list of root sets. */ +/* We keep a hash table, so that we can filter out duplicate additions. */ +/* Under Win32, we need to do a better job of filtering overlaps, so */ +/* we resort to sequential search, and pay the price. */ +/* This is really declared in gc_priv.h: +struct roots { + ptr_t r_start; + ptr_t r_end; +# ifndef ANY_MSWIN + struct roots * r_next; +# endif + GC_bool r_tmp; + -- Delete before registering new dynamic libraries +}; + +struct roots GC_static_roots[MAX_ROOT_SETS]; +*/ + +int GC_no_dls = 0; /* Register dynamic library data segments. */ + +#if !defined(NO_DEBUGGING) || defined(GC_ASSERTIONS) + /* Should return the same value as GC_root_size. */ + GC_INNER word GC_compute_root_size(void) + { + int i; + word size = 0; + + for (i = 0; i < n_root_sets; i++) { + size += (word)(GC_static_roots[i].r_end - GC_static_roots[i].r_start); + } + return size; + } +#endif /* !NO_DEBUGGING || GC_ASSERTIONS */ + +#if !defined(NO_DEBUGGING) + /* For debugging: */ + void GC_print_static_roots(void) + { + int i; + word size; + + for (i = 0; i < n_root_sets; i++) { + GC_printf("From %p to %p%s\n", + (void *)GC_static_roots[i].r_start, + (void *)GC_static_roots[i].r_end, + GC_static_roots[i].r_tmp ? " (temporary)" : ""); + } + GC_printf("GC_root_size= %lu\n", (unsigned long)GC_root_size); + + if ((size = GC_compute_root_size()) != GC_root_size) + GC_err_printf("GC_root_size incorrect!! Should be: %lu\n", + (unsigned long)size); + } +#endif /* !NO_DEBUGGING */ + +#ifndef THREADS + /* Primarily for debugging support: */ + /* Is the address p in one of the registered static root sections? */ + GC_INNER GC_bool GC_is_static_root(void *p) + { + static int last_root_set = MAX_ROOT_SETS; + int i; + + if (last_root_set < n_root_sets + && (word)p >= (word)GC_static_roots[last_root_set].r_start + && (word)p < (word)GC_static_roots[last_root_set].r_end) + return TRUE; + for (i = 0; i < n_root_sets; i++) { + if ((word)p >= (word)GC_static_roots[i].r_start + && (word)p < (word)GC_static_roots[i].r_end) { + last_root_set = i; + return TRUE; + } + } + return FALSE; + } +#endif /* !THREADS */ + +#ifndef ANY_MSWIN +/* +# define LOG_RT_SIZE 6 +# define RT_SIZE (1 << LOG_RT_SIZE) -- Power of 2, may be != MAX_ROOT_SETS + + struct roots * GC_root_index[RT_SIZE]; + -- Hash table header. Used only to check whether a range is + -- already present. + -- really defined in gc_priv.h +*/ + + GC_INLINE int rt_hash(ptr_t addr) + { + word val = (word)addr; + +# if CPP_WORDSZ > 4*LOG_RT_SIZE +# if CPP_WORDSZ > 8*LOG_RT_SIZE + val ^= val >> (8*LOG_RT_SIZE); +# endif + val ^= val >> (4*LOG_RT_SIZE); +# endif + val ^= val >> (2*LOG_RT_SIZE); + return ((val >> LOG_RT_SIZE) ^ val) & (RT_SIZE-1); + } + + /* Is a range starting at b already in the table? If so, return a */ + /* pointer to it, else NULL. */ + GC_INNER void * GC_roots_present(ptr_t b) + { + int h; + struct roots *p; + + GC_ASSERT(I_HOLD_READER_LOCK()); + h = rt_hash(b); + for (p = GC_root_index[h]; p != NULL; p = p -> r_next) { + if (p -> r_start == (ptr_t)b) break; + } + return p; + } + + /* Add the given root structure to the index. */ + GC_INLINE void add_roots_to_index(struct roots *p) + { + int h = rt_hash(p -> r_start); + + p -> r_next = GC_root_index[h]; + GC_root_index[h] = p; + } +#endif /* !ANY_MSWIN */ + +GC_INNER word GC_root_size = 0; + +GC_API void GC_CALL GC_add_roots(void *b, void *e) +{ + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + LOCK(); + GC_add_roots_inner((ptr_t)b, (ptr_t)e, FALSE); + UNLOCK(); +} + + +/* Add [b,e) to the root set. Adding the same interval a second time */ +/* is a moderately fast no-op, and hence benign. We do not handle */ +/* different but overlapping intervals efficiently. (We do handle */ +/* them correctly.) */ +/* Tmp specifies that the interval may be deleted before */ +/* re-registering dynamic libraries. */ +#ifndef AMIGA + GC_INNER +#endif +void GC_add_roots_inner(ptr_t b, ptr_t e, GC_bool tmp) +{ + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT((word)b <= (word)e); + b = PTRT_ROUNDUP_BY_MASK(b, sizeof(word)-1); + e = (ptr_t)((word)e & ~(word)(sizeof(word)-1)); + /* round e down to word boundary */ + if ((word)b >= (word)e) return; /* nothing to do */ + +# ifdef ANY_MSWIN + /* Spend the time to ensure that there are no overlapping */ + /* or adjacent intervals. */ + /* This could be done faster with e.g. a */ + /* balanced tree. But the execution time here is */ + /* virtually guaranteed to be dominated by the time it */ + /* takes to scan the roots. */ + { + int i; + struct roots * old = NULL; /* initialized to prevent warning. */ + + for (i = 0; i < n_root_sets; i++) { + old = GC_static_roots + i; + if ((word)b <= (word)old->r_end + && (word)e >= (word)old->r_start) { + if ((word)b < (word)old->r_start) { + GC_root_size += (word)(old -> r_start - b); + old -> r_start = b; + } + if ((word)e > (word)old->r_end) { + GC_root_size += (word)(e - old -> r_end); + old -> r_end = e; + } + old -> r_tmp &= tmp; + break; + } + } + if (i < n_root_sets) { + /* merge other overlapping intervals */ + struct roots *other; + + for (i++; i < n_root_sets; i++) { + other = GC_static_roots + i; + b = other -> r_start; + e = other -> r_end; + if ((word)b <= (word)old->r_end + && (word)e >= (word)old->r_start) { + if ((word)b < (word)old->r_start) { + GC_root_size += (word)(old -> r_start - b); + old -> r_start = b; + } + if ((word)e > (word)old->r_end) { + GC_root_size += (word)(e - old -> r_end); + old -> r_end = e; + } + old -> r_tmp &= other -> r_tmp; + /* Delete this entry. */ + GC_root_size -= (word)(other -> r_end - other -> r_start); + other -> r_start = GC_static_roots[n_root_sets-1].r_start; + other -> r_end = GC_static_roots[n_root_sets-1].r_end; + n_root_sets--; + } + } + return; + } + } +# else + { + struct roots * old = (struct roots *)GC_roots_present(b); + + if (old != 0) { + if ((word)e <= (word)old->r_end) { + old -> r_tmp &= tmp; + return; /* already there */ + } + if (old -> r_tmp == tmp || !tmp) { + /* Extend the existing root. */ + GC_root_size += (word)(e - old -> r_end); + old -> r_end = e; + old -> r_tmp = tmp; + return; + } + b = old -> r_end; + } + } +# endif + if (n_root_sets == MAX_ROOT_SETS) { + ABORT("Too many root sets"); + } + +# ifdef DEBUG_ADD_DEL_ROOTS + GC_log_printf("Adding data root section %d: %p .. %p%s\n", + n_root_sets, (void *)b, (void *)e, + tmp ? " (temporary)" : ""); +# endif + GC_static_roots[n_root_sets].r_start = (ptr_t)b; + GC_static_roots[n_root_sets].r_end = (ptr_t)e; + GC_static_roots[n_root_sets].r_tmp = tmp; +# ifndef ANY_MSWIN + GC_static_roots[n_root_sets].r_next = 0; + add_roots_to_index(GC_static_roots + n_root_sets); +# endif + GC_root_size += (word)(e - b); + n_root_sets++; +} + +GC_API void GC_CALL GC_clear_roots(void) +{ + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + LOCK(); +# ifdef THREADS + GC_roots_were_cleared = TRUE; +# endif + n_root_sets = 0; + GC_root_size = 0; +# ifndef ANY_MSWIN + BZERO(GC_root_index, RT_SIZE * sizeof(void *)); +# endif +# ifdef DEBUG_ADD_DEL_ROOTS + GC_log_printf("Clear all data root sections\n"); +# endif + UNLOCK(); +} + +STATIC void GC_remove_root_at_pos(int i) +{ + GC_ASSERT(I_HOLD_LOCK()); +# ifdef DEBUG_ADD_DEL_ROOTS + GC_log_printf("Remove data root section at %d: %p .. %p%s\n", + i, (void *)GC_static_roots[i].r_start, + (void *)GC_static_roots[i].r_end, + GC_static_roots[i].r_tmp ? " (temporary)" : ""); +# endif + GC_root_size -= (word)(GC_static_roots[i].r_end - + GC_static_roots[i].r_start); + GC_static_roots[i].r_start = GC_static_roots[n_root_sets-1].r_start; + GC_static_roots[i].r_end = GC_static_roots[n_root_sets-1].r_end; + GC_static_roots[i].r_tmp = GC_static_roots[n_root_sets-1].r_tmp; + n_root_sets--; +} + +#ifndef ANY_MSWIN + STATIC void GC_rebuild_root_index(void) + { + int i; + BZERO(GC_root_index, RT_SIZE * sizeof(void *)); + for (i = 0; i < n_root_sets; i++) + add_roots_to_index(GC_static_roots + i); + } +#endif /* !ANY_MSWIN */ + +#if defined(DYNAMIC_LOADING) || defined(ANY_MSWIN) || defined(PCR) + STATIC void GC_remove_tmp_roots(void) + { + int i; +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + int old_n_roots = n_root_sets; +# endif + + GC_ASSERT(I_HOLD_LOCK()); + for (i = 0; i < n_root_sets; ) { + if (GC_static_roots[i].r_tmp) { + GC_remove_root_at_pos(i); + } else { + i++; + } + } +# if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + if (n_root_sets < old_n_roots) + GC_rebuild_root_index(); +# endif + } +#endif /* DYNAMIC_LOADING || ANY_MSWIN || PCR */ + +STATIC void GC_remove_roots_inner(ptr_t b, ptr_t e); + +GC_API void GC_CALL GC_remove_roots(void *b, void *e) +{ + /* Quick check whether has nothing to do */ + if ((word)PTRT_ROUNDUP_BY_MASK(b, sizeof(word)-1) + >= ((word)e & ~(word)(sizeof(word)-1))) + return; + + LOCK(); + GC_remove_roots_inner((ptr_t)b, (ptr_t)e); + UNLOCK(); +} + +STATIC void GC_remove_roots_inner(ptr_t b, ptr_t e) +{ + int i; +# ifndef ANY_MSWIN + int old_n_roots = n_root_sets; +# endif + + GC_ASSERT(I_HOLD_LOCK()); + for (i = 0; i < n_root_sets; ) { + if ((word)GC_static_roots[i].r_start >= (word)b + && (word)GC_static_roots[i].r_end <= (word)e) { + GC_remove_root_at_pos(i); + } else { + i++; + } + } +# ifndef ANY_MSWIN + if (n_root_sets < old_n_roots) + GC_rebuild_root_index(); +# endif +} + +#ifdef USE_PROC_FOR_LIBRARIES + /* Exchange the elements of the roots table. Requires rebuild of */ + /* the roots index table after the swap. */ + GC_INLINE void swap_static_roots(int i, int j) + { + ptr_t r_start = GC_static_roots[i].r_start; + ptr_t r_end = GC_static_roots[i].r_end; + GC_bool r_tmp = GC_static_roots[i].r_tmp; + + GC_static_roots[i].r_start = GC_static_roots[j].r_start; + GC_static_roots[i].r_end = GC_static_roots[j].r_end; + GC_static_roots[i].r_tmp = GC_static_roots[j].r_tmp; + /* No need to swap r_next values. */ + GC_static_roots[j].r_start = r_start; + GC_static_roots[j].r_end = r_end; + GC_static_roots[j].r_tmp = r_tmp; + } + + /* Remove given range from every static root which intersects with */ + /* the range. It is assumed GC_remove_tmp_roots is called before */ + /* this function is called repeatedly by GC_register_map_entries. */ + GC_INNER void GC_remove_roots_subregion(ptr_t b, ptr_t e) + { + int i; + GC_bool rebuild = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT((word)b % sizeof(word) == 0 && (word)e % sizeof(word) == 0); + for (i = 0; i < n_root_sets; i++) { + ptr_t r_start, r_end; + + if (GC_static_roots[i].r_tmp) { + /* The remaining roots are skipped as they are all temporary. */ +# ifdef GC_ASSERTIONS + int j; + for (j = i + 1; j < n_root_sets; j++) { + GC_ASSERT(GC_static_roots[j].r_tmp); + } +# endif + break; + } + r_start = GC_static_roots[i].r_start; + r_end = GC_static_roots[i].r_end; + if (!EXPECT((word)e <= (word)r_start || (word)r_end <= (word)b, TRUE)) { +# ifdef DEBUG_ADD_DEL_ROOTS + GC_log_printf("Removing %p .. %p from root section %d (%p .. %p)\n", + (void *)b, (void *)e, + i, (void *)r_start, (void *)r_end); +# endif + if ((word)r_start < (word)b) { + GC_root_size -= (word)(r_end - b); + GC_static_roots[i].r_end = b; + /* No need to rebuild as hash does not use r_end value. */ + if ((word)e < (word)r_end) { + int j; + + if (rebuild) { + GC_rebuild_root_index(); + rebuild = FALSE; + } + GC_add_roots_inner(e, r_end, FALSE); /* updates n_root_sets */ + for (j = i + 1; j < n_root_sets; j++) + if (GC_static_roots[j].r_tmp) + break; + if (j < n_root_sets-1 && !GC_static_roots[n_root_sets-1].r_tmp) { + /* Exchange the roots to have all temporary ones at the end. */ + swap_static_roots(j, n_root_sets - 1); + rebuild = TRUE; + } + } + } else { + if ((word)e < (word)r_end) { + GC_root_size -= (word)(e - r_start); + GC_static_roots[i].r_start = e; + } else { + GC_remove_root_at_pos(i); + if (i < n_root_sets - 1 && GC_static_roots[i].r_tmp + && !GC_static_roots[i + 1].r_tmp) { + int j; + + for (j = i + 2; j < n_root_sets; j++) + if (GC_static_roots[j].r_tmp) + break; + /* Exchange the roots to have all temporary ones at the end. */ + swap_static_roots(i, j - 1); + } + i--; + } + rebuild = TRUE; + } + } + } + if (rebuild) + GC_rebuild_root_index(); + } +#endif /* USE_PROC_FOR_LIBRARIES */ + +#if !defined(NO_DEBUGGING) + /* For the debugging purpose only. */ + /* Workaround for the OS mapping and unmapping behind our back: */ + /* Is the address p in one of the temporary static root sections? */ + GC_API int GC_CALL GC_is_tmp_root(void *p) + { +# ifndef HAS_REAL_READER_LOCK + static int last_root_set; /* initialized to 0; no shared access */ +# elif defined(AO_HAVE_load) || defined(AO_HAVE_store) + static volatile AO_t last_root_set; +# else + static volatile int last_root_set; + /* A race is acceptable, it's just a cached index. */ +# endif + int i; + int res; + + READER_LOCK(); + /* First try the cached root. */ +# if defined(AO_HAVE_load) && defined(HAS_REAL_READER_LOCK) + i = (int)(unsigned)AO_load(&last_root_set); +# else + i = last_root_set; +# endif + if (i < n_root_sets + && (word)p >= (word)GC_static_roots[i].r_start + && (word)p < (word)GC_static_roots[i].r_end) { + res = (int)GC_static_roots[i].r_tmp; + } else { + res = 0; + for (i = 0; i < n_root_sets; i++) { + if ((word)p >= (word)GC_static_roots[i].r_start + && (word)p < (word)GC_static_roots[i].r_end) { + res = (int)GC_static_roots[i].r_tmp; +# if defined(AO_HAVE_store) && defined(HAS_REAL_READER_LOCK) + AO_store(&last_root_set, (AO_t)(unsigned)i); +# else + last_root_set = i; +# endif + break; + } + } + } + READER_UNLOCK(); + return res; + } +#endif /* !NO_DEBUGGING */ + +GC_INNER ptr_t GC_approx_sp(void) +{ + volatile word sp; +# if ((defined(E2K) && defined(__clang__)) \ + || (defined(S390) && (__clang_major__ < 8))) && !defined(CPPCHECK) + /* Workaround some bugs in clang: */ + /* "undefined reference to llvm.frameaddress" error (clang-9/e2k); */ + /* a crash in SystemZTargetLowering of libLLVM-3.8 (S390). */ + sp = (word)&sp; +# elif defined(CPPCHECK) || (__GNUC__ >= 4 /* GC_GNUC_PREREQ(4, 0) */ \ + && !defined(STACK_NOT_SCANNED)) + /* TODO: Use GC_GNUC_PREREQ after fixing a bug in cppcheck. */ + sp = (word)__builtin_frame_address(0); +# else + sp = (word)&sp; +# endif + /* Also force stack to grow if necessary. Otherwise the */ + /* later accesses might cause the kernel to think we're */ + /* doing something wrong. */ + return (ptr_t)sp; +} + +/* + * Data structure for excluded static roots. + * Real declaration is in gc_priv.h. + +struct exclusion { + ptr_t e_start; + ptr_t e_end; +}; + +struct exclusion GC_excl_table[MAX_EXCLUSIONS]; + -- Array of exclusions, ascending + -- address order. +*/ + +/* Clear the number of entries in the exclusion table. The caller */ +/* should acquire the allocator lock (to avoid data race) but no */ +/* assertion about it by design. */ +GC_API void GC_CALL GC_clear_exclusion_table(void) +{ +# ifdef DEBUG_ADD_DEL_ROOTS + GC_log_printf("Clear static root exclusions (%u elements)\n", + (unsigned)GC_excl_table_entries); +# endif + GC_excl_table_entries = 0; +} + +/* Return the first exclusion range that includes an address not */ +/* lower than start_addr. */ +STATIC struct exclusion * GC_next_exclusion(ptr_t start_addr) +{ + size_t low = 0; + size_t high; + + if (EXPECT(0 == GC_excl_table_entries, FALSE)) return NULL; + high = GC_excl_table_entries - 1; + while (high > low) { + size_t mid = (low + high) >> 1; + + /* low <= mid < high */ + if ((word)GC_excl_table[mid].e_end <= (word)start_addr) { + low = mid + 1; + } else { + high = mid; + } + } + if ((word)GC_excl_table[low].e_end <= (word)start_addr) return NULL; + return GC_excl_table + low; +} + +/* The range boundaries should be properly aligned and valid. */ +GC_INNER void GC_exclude_static_roots_inner(void *start, void *finish) +{ + struct exclusion * next; + size_t next_index; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT((word)start % sizeof(word) == 0); + GC_ASSERT((word)start < (word)finish); + + next = GC_next_exclusion((ptr_t)start); + if (next != NULL) { + if ((word)(next -> e_start) < (word)finish) { + /* Incomplete error check. */ + ABORT("Exclusion ranges overlap"); + } + if ((word)(next -> e_start) == (word)finish) { + /* Extend old range backwards. */ + next -> e_start = (ptr_t)start; +# ifdef DEBUG_ADD_DEL_ROOTS + GC_log_printf("Updating static root exclusion to %p .. %p\n", + start, (void *)(next -> e_end)); +# endif + return; + } + } + + next_index = GC_excl_table_entries; + if (next_index >= MAX_EXCLUSIONS) ABORT("Too many exclusions"); + if (next != NULL) { + size_t i; + + next_index = (size_t)(next - GC_excl_table); + for (i = GC_excl_table_entries; i > next_index; --i) { + GC_excl_table[i] = GC_excl_table[i-1]; + } + } +# ifdef DEBUG_ADD_DEL_ROOTS + GC_log_printf("Adding static root exclusion at %u: %p .. %p\n", + (unsigned)next_index, start, finish); +# endif + GC_excl_table[next_index].e_start = (ptr_t)start; + GC_excl_table[next_index].e_end = (ptr_t)finish; + ++GC_excl_table_entries; +} + +GC_API void GC_CALL GC_exclude_static_roots(void *b, void *e) +{ + if (b == e) return; /* nothing to exclude? */ + + /* Round boundaries (in direction reverse to that of GC_add_roots). */ + b = (void *)((word)b & ~(word)(sizeof(word)-1)); + e = PTRT_ROUNDUP_BY_MASK(e, sizeof(word)-1); + if (NULL == e) + e = (void *)(~(word)(sizeof(word)-1)); /* handle overflow */ + + LOCK(); + GC_exclude_static_roots_inner(b, e); + UNLOCK(); +} + +#if defined(WRAP_MARK_SOME) && defined(PARALLEL_MARK) +# define GC_PUSH_CONDITIONAL(b, t, all) \ + (GC_parallel \ + ? GC_push_conditional_eager(b, t, all) \ + : GC_push_conditional_static(b, t, all)) +#else +# define GC_PUSH_CONDITIONAL(b, t, all) GC_push_conditional_static(b, t, all) +#endif + +/* Invoke push_conditional on ranges that are not excluded. */ +STATIC void GC_push_conditional_with_exclusions(ptr_t bottom, ptr_t top, + GC_bool all) +{ + while ((word)bottom < (word)top) { + struct exclusion *next = GC_next_exclusion(bottom); + ptr_t excl_start; + + if (NULL == next + || (word)(excl_start = next -> e_start) >= (word)top) { + next = NULL; + excl_start = top; + } + if ((word)bottom < (word)excl_start) + GC_PUSH_CONDITIONAL(bottom, excl_start, all); + if (NULL == next) break; + bottom = next -> e_end; + } +} + +#ifdef IA64 + /* Similar to GC_push_all_stack_sections() but for IA-64 registers store. */ + GC_INNER void GC_push_all_register_sections(ptr_t bs_lo, ptr_t bs_hi, + int eager, struct GC_traced_stack_sect_s *traced_stack_sect) + { + GC_ASSERT(I_HOLD_LOCK()); + while (traced_stack_sect != NULL) { + ptr_t frame_bs_lo = traced_stack_sect -> backing_store_end; + + GC_ASSERT((word)frame_bs_lo <= (word)bs_hi); + if (eager) { + GC_push_all_eager(frame_bs_lo, bs_hi); + } else { + GC_push_all_stack(frame_bs_lo, bs_hi); + } + bs_hi = traced_stack_sect -> saved_backing_store_ptr; + traced_stack_sect = traced_stack_sect -> prev; + } + GC_ASSERT((word)bs_lo <= (word)bs_hi); + if (eager) { + GC_push_all_eager(bs_lo, bs_hi); + } else { + GC_push_all_stack(bs_lo, bs_hi); + } + } +#endif /* IA64 */ + +#ifdef THREADS + + GC_INNER void GC_push_all_stack_sections( + ptr_t lo /* top */, ptr_t hi /* bottom */, + struct GC_traced_stack_sect_s *traced_stack_sect) + { + GC_ASSERT(I_HOLD_LOCK()); + while (traced_stack_sect != NULL) { + GC_ASSERT((word)lo HOTTER_THAN (word)traced_stack_sect); +# ifdef STACK_GROWS_UP + GC_push_all_stack((ptr_t)traced_stack_sect, lo); +# else + GC_push_all_stack(lo, (ptr_t)traced_stack_sect); +# endif + lo = traced_stack_sect -> saved_stack_ptr; + GC_ASSERT(lo != NULL); + traced_stack_sect = traced_stack_sect -> prev; + } + GC_ASSERT(!((word)hi HOTTER_THAN (word)lo)); +# ifdef STACK_GROWS_UP + /* We got them backwards! */ + GC_push_all_stack(hi, lo); +# else + GC_push_all_stack(lo, hi); +# endif + } + +#else /* !THREADS */ + + /* Similar to GC_push_all_eager, but only the */ + /* part hotter than cold_gc_frame is scanned */ + /* immediately. Needed to ensure that callee- */ + /* save registers are not missed. */ + + /* A version of GC_push_all that treats all interior pointers as */ + /* valid and scans part of the area immediately, to make sure */ + /* that saved register values are not lost. Cold_gc_frame */ + /* delimits the stack section that must be scanned eagerly. */ + /* A zero value indicates that no eager scanning is needed. */ + /* We do not need to worry about the manual VDB case here, since */ + /* this is only called in the single-threaded case. We assume */ + /* that we cannot collect between an assignment and the */ + /* corresponding GC_dirty() call. */ + STATIC void GC_push_all_stack_partially_eager(ptr_t bottom, ptr_t top, + ptr_t cold_gc_frame) + { +# ifndef NEED_FIXUP_POINTER + if (GC_all_interior_pointers) { + /* Push the hot end of the stack eagerly, so that register values */ + /* saved inside GC frames are marked before they disappear. */ + /* The rest of the marking can be deferred until later. */ + if (0 == cold_gc_frame) { + GC_push_all_stack(bottom, top); + return; + } + GC_ASSERT((word)bottom <= (word)cold_gc_frame + && (word)cold_gc_frame <= (word)top); +# ifdef STACK_GROWS_UP + GC_push_all(bottom, cold_gc_frame + sizeof(ptr_t)); + GC_push_all_eager(cold_gc_frame, top); +# else + GC_push_all(cold_gc_frame - sizeof(ptr_t), top); + GC_push_all_eager(bottom, cold_gc_frame); +# endif + } else +# endif + /* else */ { + GC_push_all_eager(bottom, top); + } +# ifdef TRACE_BUF + GC_add_trace_entry("GC_push_all_stack", (word)bottom, (word)top); +# endif + } + + /* Similar to GC_push_all_stack_sections() but also uses cold_gc_frame. */ + STATIC void GC_push_all_stack_part_eager_sections( + ptr_t lo /* top */, ptr_t hi /* bottom */, ptr_t cold_gc_frame, + struct GC_traced_stack_sect_s *traced_stack_sect) + { + GC_ASSERT(traced_stack_sect == NULL || cold_gc_frame == NULL || + (word)cold_gc_frame HOTTER_THAN (word)traced_stack_sect); + + while (traced_stack_sect != NULL) { + GC_ASSERT((word)lo HOTTER_THAN (word)traced_stack_sect); +# ifdef STACK_GROWS_UP + GC_push_all_stack_partially_eager((ptr_t)traced_stack_sect, lo, + cold_gc_frame); +# else + GC_push_all_stack_partially_eager(lo, (ptr_t)traced_stack_sect, + cold_gc_frame); +# endif + lo = traced_stack_sect -> saved_stack_ptr; + GC_ASSERT(lo != NULL); + traced_stack_sect = traced_stack_sect -> prev; + cold_gc_frame = NULL; /* Use at most once. */ + } + + GC_ASSERT(!((word)hi HOTTER_THAN (word)lo)); +# ifdef STACK_GROWS_UP + /* We got them backwards! */ + GC_push_all_stack_partially_eager(hi, lo, cold_gc_frame); +# else + GC_push_all_stack_partially_eager(lo, hi, cold_gc_frame); +# endif + } + +#endif /* !THREADS */ + +/* Push enough of the current stack eagerly to ensure that callee-save */ +/* registers saved in GC frames are scanned. In the non-threads case, */ +/* schedule entire stack for scanning. The 2nd argument is a pointer */ +/* to the (possibly null) thread context, for (currently hypothetical) */ +/* more precise stack scanning. In the presence of threads, push */ +/* enough of the current stack to ensure that callee-save registers */ +/* saved in collector frames have been seen. */ +/* TODO: Merge it with per-thread stuff. */ +STATIC void GC_push_current_stack(ptr_t cold_gc_frame, void *context) +{ + UNUSED_ARG(context); + GC_ASSERT(I_HOLD_LOCK()); +# if defined(THREADS) + /* cold_gc_frame is non-NULL. */ +# ifdef STACK_GROWS_UP + GC_push_all_eager(cold_gc_frame, GC_approx_sp()); +# else + GC_push_all_eager(GC_approx_sp(), cold_gc_frame); + /* For IA64, the register stack backing store is handled */ + /* in the thread-specific code. */ +# endif +# else + GC_push_all_stack_part_eager_sections(GC_approx_sp(), GC_stackbottom, + cold_gc_frame, GC_traced_stack_sect); +# ifdef IA64 + /* We also need to push the register stack backing store. */ + /* This should really be done in the same way as the */ + /* regular stack. For now we fudge it a bit. */ + /* Note that the backing store grows up, so we can't use */ + /* GC_push_all_stack_partially_eager. */ + { + ptr_t bsp = GC_save_regs_ret_val; + ptr_t cold_gc_bs_pointer = bsp - 2048; + if (GC_all_interior_pointers && (word)cold_gc_bs_pointer + > (word)GC_register_stackbottom) { + /* Adjust cold_gc_bs_pointer if below our innermost */ + /* "traced stack section" in backing store. */ + if (GC_traced_stack_sect != NULL + && (word)cold_gc_bs_pointer + < (word)(GC_traced_stack_sect -> backing_store_end)) + cold_gc_bs_pointer = + GC_traced_stack_sect -> backing_store_end; + GC_push_all_register_sections(GC_register_stackbottom, + cold_gc_bs_pointer, FALSE, GC_traced_stack_sect); + GC_push_all_eager(cold_gc_bs_pointer, bsp); + } else { + GC_push_all_register_sections(GC_register_stackbottom, bsp, + TRUE /* eager */, GC_traced_stack_sect); + } + /* All values should be sufficiently aligned that we */ + /* don't have to worry about the boundary. */ + } +# elif defined(E2K) + /* We also need to push procedure stack store. */ + /* Procedure stack grows up. */ + { + ptr_t bs_lo; + size_t stack_size; + + /* TODO: support ps_ofs here and in GC_do_blocking_inner */ + GET_PROCEDURE_STACK_LOCAL(0, &bs_lo, &stack_size); + GC_push_all_eager(bs_lo, bs_lo + stack_size); + } +# endif +# endif /* !THREADS */ +} + +GC_INNER void (*GC_push_typed_structures)(void) = 0; + +GC_INNER void GC_cond_register_dynamic_libraries(void) +{ + GC_ASSERT(I_HOLD_LOCK()); +# if defined(DYNAMIC_LOADING) && !defined(MSWIN_XBOX1) \ + || defined(ANY_MSWIN) || defined(PCR) + GC_remove_tmp_roots(); + if (!GC_no_dls) GC_register_dynamic_libraries(); +# else + GC_no_dls = TRUE; +# endif +} + +STATIC void GC_push_regs_and_stack(ptr_t cold_gc_frame) +{ + GC_ASSERT(I_HOLD_LOCK()); +# ifdef THREADS + if (NULL == cold_gc_frame) + return; /* GC_push_all_stacks should push registers and stack */ +# endif + GC_with_callee_saves_pushed(GC_push_current_stack, cold_gc_frame); +} + +/* Call the mark routines (GC_push_one for a single pointer, */ +/* GC_push_conditional on groups of pointers) on every top level */ +/* accessible pointer. If all is false, arrange to push only possibly */ +/* altered values. Cold_gc_frame is an address inside a GC frame that */ +/* remains valid until all marking is complete; a NULL value indicates */ +/* that it is OK to miss some register values. */ +GC_INNER void GC_push_roots(GC_bool all, ptr_t cold_gc_frame) +{ + int i; + unsigned kind; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_is_initialized); /* needed for GC_push_all_stacks */ + + /* Next push static data. This must happen early on, since it is */ + /* not robust against mark stack overflow. */ + /* Re-register dynamic libraries, in case one got added. */ + /* There is some argument for doing this as late as possible, */ + /* especially on Win32, where it can change asynchronously. */ + /* In those cases, we do it here. But on other platforms, it's */ + /* not safe with the world stopped, so we do it earlier. */ +# if !defined(REGISTER_LIBRARIES_EARLY) + GC_cond_register_dynamic_libraries(); +# endif + + /* Mark everything in static data areas. */ + for (i = 0; i < n_root_sets; i++) { + GC_push_conditional_with_exclusions( + GC_static_roots[i].r_start, + GC_static_roots[i].r_end, all); + } + + /* Mark all free list header blocks, if those were allocated from */ + /* the garbage collected heap. This makes sure they don't */ + /* disappear if we are not marking from static data. It also */ + /* saves us the trouble of scanning them, and possibly that of */ + /* marking the freelists. */ + for (kind = 0; kind < GC_n_kinds; kind++) { + void *base = GC_base(GC_obj_kinds[kind].ok_freelist); + if (base != NULL) { + GC_set_mark_bit(base); + } + } + + /* Mark from GC internal roots if those might otherwise have */ + /* been excluded. */ +# ifndef GC_NO_FINALIZATION + GC_push_finalizer_structures(); +# endif +# ifdef THREADS + if (GC_no_dls || GC_roots_were_cleared) + GC_push_thread_structures(); +# endif + if (GC_push_typed_structures) + GC_push_typed_structures(); + + /* Mark thread local free lists, even if their mark */ + /* descriptor excludes the link field. */ + /* If the world is not stopped, this is unsafe. It is */ + /* also unnecessary, since we will do this again with the */ + /* world stopped. */ +# if defined(THREAD_LOCAL_ALLOC) + if (GC_world_stopped) + GC_mark_thread_local_free_lists(); +# endif + + /* Now traverse stacks, and mark from register contents. */ + /* These must be done last, since they can legitimately */ + /* overflow the mark stack. This is usually done by saving */ + /* the current context on the stack, and then just tracing */ + /* from the stack. */ +# ifdef STACK_NOT_SCANNED + UNUSED_ARG(cold_gc_frame); +# else + GC_push_regs_and_stack(cold_gc_frame); +# endif + + if (GC_push_other_roots != 0) { + /* In the threads case, this also pushes thread stacks. */ + /* Note that without interior pointer recognition lots */ + /* of stuff may have been pushed already, and this */ + /* should be careful about mark stack overflows. */ + (*GC_push_other_roots)(); + } +} + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2009-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +#ifdef ENABLE_DISCLAIM + +#endif + +GC_INNER signed_word GC_bytes_found = 0; + /* Number of bytes of memory reclaimed */ + /* minus the number of bytes originally */ + /* on free lists which we had to drop. */ + +#if defined(PARALLEL_MARK) + GC_INNER signed_word GC_fl_builder_count = 0; + /* Number of threads currently building free lists without */ + /* holding the allocator lock. It is not safe to collect if */ + /* this is nonzero. Also, together with the mark lock, it is */ + /* used as a semaphore during marker threads startup. */ +#endif /* PARALLEL_MARK */ + +/* We defer printing of leaked objects until we're done with the GC */ +/* cycle, since the routine for printing objects needs to run outside */ +/* the collector, e.g. without the allocator lock. */ +#ifndef MAX_LEAKED +# define MAX_LEAKED 40 +#endif +STATIC ptr_t GC_leaked[MAX_LEAKED] = { NULL }; +STATIC unsigned GC_n_leaked = 0; + +#ifdef AO_HAVE_store + GC_INNER volatile AO_t GC_have_errors = 0; +#else + GC_INNER GC_bool GC_have_errors = FALSE; +#endif + +#if !defined(EAGER_SWEEP) && defined(ENABLE_DISCLAIM) + STATIC void GC_reclaim_unconditionally_marked(void); +#endif + +GC_INLINE void GC_add_leaked(ptr_t leaked) +{ + GC_ASSERT(I_HOLD_LOCK()); +# ifndef SHORT_DBG_HDRS + if (GC_findleak_delay_free && !GC_check_leaked(leaked)) + return; +# endif + + GC_SET_HAVE_ERRORS(); + if (GC_n_leaked < MAX_LEAKED) { + GC_leaked[GC_n_leaked++] = leaked; + /* Make sure it's not reclaimed this cycle */ + GC_set_mark_bit(leaked); + } +} + +/* Print all objects on the list after printing any smashed objects. */ +/* Clear both lists. Called without the allocator lock held. */ +GC_INNER void GC_print_all_errors(void) +{ + static GC_bool printing_errors = FALSE; + GC_bool have_errors; + unsigned i, n_leaked; + ptr_t leaked[MAX_LEAKED]; + + LOCK(); + if (printing_errors) { + UNLOCK(); + return; + } + have_errors = get_have_errors(); + printing_errors = TRUE; + n_leaked = GC_n_leaked; + if (n_leaked > 0) { + GC_ASSERT(n_leaked <= MAX_LEAKED); + BCOPY(GC_leaked, leaked, n_leaked * sizeof(ptr_t)); + GC_n_leaked = 0; + BZERO(GC_leaked, n_leaked * sizeof(ptr_t)); + } + UNLOCK(); + + if (GC_debugging_started) { + GC_print_all_smashed(); + } else { + have_errors = FALSE; + } + + if (n_leaked > 0) { + GC_err_printf("Found %u leaked objects:\n", n_leaked); + have_errors = TRUE; + } + for (i = 0; i < n_leaked; i++) { + ptr_t p = leaked[i]; +# ifndef SKIP_LEAKED_OBJECTS_PRINTING + GC_print_heap_obj(p); +# endif + GC_free(p); + } + + if (have_errors +# ifndef GC_ABORT_ON_LEAK + && GETENV("GC_ABORT_ON_LEAK") != NULL +# endif + ) { + ABORT("Leaked or smashed objects encountered"); + } + + LOCK(); + printing_errors = FALSE; + UNLOCK(); +} + + +/* + * reclaim phase + * + */ + +/* Test whether a block is completely empty, i.e. contains no marked */ +/* objects. This does not require the block to be in physical memory. */ +GC_INNER GC_bool GC_block_empty(hdr *hhdr) +{ + return 0 == hhdr -> hb_n_marks; +} + +STATIC GC_bool GC_block_nearly_full(hdr *hhdr, word sz) +{ + return hhdr -> hb_n_marks > HBLK_OBJS(sz) * 7 / 8; +} + +/* TODO: This should perhaps again be specialized for USE_MARK_BYTES */ +/* and USE_MARK_BITS cases. */ + +GC_INLINE word *GC_clear_block(word *p, word sz, word *pcount) +{ + word *q = (word *)((ptr_t)p + sz); + + /* Clear object, advance p to next object in the process. */ +# ifdef USE_MARK_BYTES + GC_ASSERT((sz & 1) == 0); + GC_ASSERT(((word)p & (2 * sizeof(word) - 1)) == 0); + p[1] = 0; + p += 2; + while ((word)p < (word)q) { + CLEAR_DOUBLE(p); + p += 2; + } +# else + p++; /* Skip link field */ + while ((word)p < (word)q) { + *p++ = 0; + } +# endif + *pcount += sz; + return p; +} + +/* + * Restore unmarked small objects in h of size sz to the object + * free list. Returns the new list. + * Clears unmarked objects. Sz is in bytes. + */ +STATIC ptr_t GC_reclaim_clear(struct hblk *hbp, hdr *hhdr, word sz, + ptr_t list, word *pcount) +{ + word bit_no = 0; + ptr_t p, plim; + + GC_ASSERT(hhdr == GC_find_header((ptr_t)hbp)); +# ifndef THREADS + GC_ASSERT(sz == hhdr -> hb_sz); +# else + /* Skip the assertion because of a potential race with GC_realloc. */ +# endif + GC_ASSERT((sz & (BYTES_PER_WORD-1)) == 0); + p = hbp->hb_body; + plim = p + HBLKSIZE - sz; + + /* go through all words in block */ + while ((word)p <= (word)plim) { + if (mark_bit_from_hdr(hhdr, bit_no)) { + p += sz; + } else { + /* Object is available - put it on list. */ + obj_link(p) = list; + list = p; + + p = (ptr_t)GC_clear_block((word *)p, sz, pcount); + } + bit_no += MARK_BIT_OFFSET(sz); + } + return list; +} + +/* The same thing, but don't clear objects: */ +STATIC ptr_t GC_reclaim_uninit(struct hblk *hbp, hdr *hhdr, word sz, + ptr_t list, word *pcount) +{ + word bit_no = 0; + word *p, *plim; + word n_bytes_found = 0; + +# ifndef THREADS + GC_ASSERT(sz == hhdr -> hb_sz); +# endif + p = (word *)(hbp->hb_body); + plim = (word *)((ptr_t)hbp + HBLKSIZE - sz); + + /* go through all words in block */ + while ((word)p <= (word)plim) { + if (!mark_bit_from_hdr(hhdr, bit_no)) { + n_bytes_found += sz; + /* object is available - put on list */ + obj_link(p) = list; + list = ((ptr_t)p); + } + p = (word *)((ptr_t)p + sz); + bit_no += MARK_BIT_OFFSET(sz); + } + *pcount += n_bytes_found; + return list; +} + +#ifdef ENABLE_DISCLAIM + /* Call reclaim notifier for block's kind on each unmarked object in */ + /* block, all within a pair of corresponding enter/leave callbacks. */ + STATIC ptr_t GC_disclaim_and_reclaim(struct hblk *hbp, hdr *hhdr, word sz, + ptr_t list, word *pcount) + { + word bit_no = 0; + ptr_t p, plim; + int (GC_CALLBACK *disclaim)(void *) = + GC_obj_kinds[hhdr -> hb_obj_kind].ok_disclaim_proc; + + GC_ASSERT(disclaim != 0); +# ifndef THREADS + GC_ASSERT(sz == hhdr -> hb_sz); +# endif + p = hbp -> hb_body; + plim = p + HBLKSIZE - sz; + + for (; (word)p <= (word)plim; bit_no += MARK_BIT_OFFSET(sz)) { + if (mark_bit_from_hdr(hhdr, bit_no)) { + p += sz; + } else if (disclaim(p)) { + set_mark_bit_from_hdr(hhdr, bit_no); + INCR_MARKS(hhdr); + p += sz; + } else { + obj_link(p) = list; + list = p; + p = (ptr_t)GC_clear_block((word *)p, sz, pcount); + } + } + return list; + } +#endif /* ENABLE_DISCLAIM */ + +/* Don't really reclaim objects, just check for unmarked ones: */ +STATIC void GC_reclaim_check(struct hblk *hbp, hdr *hhdr, word sz) +{ + word bit_no; + ptr_t p, plim; + +# ifndef THREADS + GC_ASSERT(sz == hhdr -> hb_sz); +# endif + /* go through all words in block */ + p = hbp->hb_body; + plim = p + HBLKSIZE - sz; + for (bit_no = 0; (word)p <= (word)plim; + p += sz, bit_no += MARK_BIT_OFFSET(sz)) { + if (!mark_bit_from_hdr(hhdr, bit_no)) { + GC_add_leaked(p); + } + } +} + +/* Is a pointer-free block? Same as IS_PTRFREE() macro but uses */ +/* unordered atomic access to avoid racing with GC_realloc. */ +#ifdef AO_HAVE_load +# define IS_PTRFREE_SAFE(hhdr) \ + (AO_load((volatile AO_t *)&(hhdr)->hb_descr) == 0) +#else + /* No race as GC_realloc holds the allocator lock when updating hb_descr. */ +# define IS_PTRFREE_SAFE(hhdr) IS_PTRFREE(hhdr) +#endif + +/* Generic procedure to rebuild a free list in hbp. Also called */ +/* directly from GC_malloc_many. sz is in bytes. */ +GC_INNER ptr_t GC_reclaim_generic(struct hblk *hbp, hdr *hhdr, size_t sz, + GC_bool init, ptr_t list, word *pcount) +{ + ptr_t result; + +# ifndef PARALLEL_MARK + GC_ASSERT(I_HOLD_LOCK()); +# endif + GC_ASSERT(GC_find_header((ptr_t)hbp) == hhdr); +# ifndef GC_DISABLE_INCREMENTAL + GC_remove_protection(hbp, 1, IS_PTRFREE_SAFE(hhdr)); +# endif +# ifdef ENABLE_DISCLAIM + if ((hhdr -> hb_flags & HAS_DISCLAIM) != 0) { + result = GC_disclaim_and_reclaim(hbp, hhdr, sz, list, pcount); + } else +# endif + /* else */ if (init || GC_debugging_started) { + result = GC_reclaim_clear(hbp, hhdr, sz, list, pcount); + } else { +# ifndef AO_HAVE_load + GC_ASSERT(IS_PTRFREE(hhdr)); +# endif + result = GC_reclaim_uninit(hbp, hhdr, sz, list, pcount); + } + if (IS_UNCOLLECTABLE(hhdr -> hb_obj_kind)) GC_set_hdr_marks(hhdr); + return result; +} + +/* + * Restore unmarked small objects in the block pointed to by hbp + * to the appropriate object free list. + * If entirely empty blocks are to be completely deallocated, then + * caller should perform that check. + */ +STATIC void GC_reclaim_small_nonempty_block(struct hblk *hbp, word sz, + GC_bool report_if_found) +{ + hdr *hhdr; + struct obj_kind *ok; + void **flh; + + GC_ASSERT(I_HOLD_LOCK()); + hhdr = HDR(hbp); + ok = &GC_obj_kinds[hhdr -> hb_obj_kind]; + flh = &(ok -> ok_freelist[BYTES_TO_GRANULES(sz)]); + + hhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no; + if (report_if_found) { + GC_reclaim_check(hbp, hhdr, sz); + } else { + *flh = GC_reclaim_generic(hbp, hhdr, sz, ok -> ok_init, + (ptr_t)(*flh), (word *)&GC_bytes_found); + } +} + +#ifdef ENABLE_DISCLAIM + STATIC void GC_disclaim_and_reclaim_or_free_small_block(struct hblk *hbp) + { + hdr *hhdr; + word sz; + struct obj_kind *ok; + void **flh; + void *flh_next; + + GC_ASSERT(I_HOLD_LOCK()); + hhdr = HDR(hbp); + sz = hhdr -> hb_sz; + ok = &GC_obj_kinds[hhdr -> hb_obj_kind]; + flh = &(ok -> ok_freelist[BYTES_TO_GRANULES(sz)]); + + hhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no; + flh_next = GC_reclaim_generic(hbp, hhdr, sz, ok -> ok_init, + (ptr_t)(*flh), (word *)&GC_bytes_found); + if (hhdr -> hb_n_marks) + *flh = flh_next; + else { + GC_bytes_found += (signed_word)HBLKSIZE; + GC_freehblk(hbp); + } + } +#endif /* ENABLE_DISCLAIM */ + +/* + * Restore an unmarked large object or an entirely empty blocks of small objects + * to the heap block free list. + * Otherwise enqueue the block for later processing + * by GC_reclaim_small_nonempty_block. + * If report_if_found is TRUE, then process any block immediately, and + * simply report free objects; do not actually reclaim them. + */ +STATIC void GC_CALLBACK GC_reclaim_block(struct hblk *hbp, + GC_word report_if_found) +{ + hdr *hhdr; + word sz; /* size of objects in current block */ + struct obj_kind *ok; + + GC_ASSERT(I_HOLD_LOCK()); + hhdr = HDR(hbp); + ok = &GC_obj_kinds[hhdr -> hb_obj_kind]; +# ifdef AO_HAVE_load + /* Atomic access is used to avoid racing with GC_realloc. */ + sz = (word)AO_load((volatile AO_t *)&(hhdr -> hb_sz)); +# else + /* No race as GC_realloc holds the allocator lock while */ + /* updating hb_sz. */ + sz = hhdr -> hb_sz; +# endif + if (sz > MAXOBJBYTES) { /* 1 big object */ + if (!mark_bit_from_hdr(hhdr, 0)) { + if (report_if_found) { + GC_add_leaked((ptr_t)hbp); + } else { +# ifdef ENABLE_DISCLAIM + if (EXPECT(hhdr -> hb_flags & HAS_DISCLAIM, 0)) { + if (ok -> ok_disclaim_proc(hbp)) { + /* Not disclaimed => resurrect the object. */ + set_mark_bit_from_hdr(hhdr, 0); + goto in_use; + } + } +# endif + if (sz > HBLKSIZE) { + GC_large_allocd_bytes -= HBLKSIZE * OBJ_SZ_TO_BLOCKS(sz); + } + GC_bytes_found += (signed_word)sz; + GC_freehblk(hbp); + } + } else { +# ifdef ENABLE_DISCLAIM + in_use: +# endif + if (IS_PTRFREE_SAFE(hhdr)) { + GC_atomic_in_use += sz; + } else { + GC_composite_in_use += sz; + } + } + } else { + GC_bool empty = GC_block_empty(hhdr); +# ifdef PARALLEL_MARK + /* Count can be low or one too high because we sometimes */ + /* have to ignore decrements. Objects can also potentially */ + /* be repeatedly marked by each marker. */ + /* Here we assume 3 markers at most, but this is extremely */ + /* unlikely to fail spuriously with more. And if it does, it */ + /* should be looked at. */ + GC_ASSERT(sz != 0 && (GC_markers_m1 > 1 ? 3 : GC_markers_m1 + 1) + * (HBLKSIZE/sz + 1) + 16 >= hhdr->hb_n_marks); +# else + GC_ASSERT(sz * hhdr -> hb_n_marks <= HBLKSIZE); +# endif + if (report_if_found) { + GC_reclaim_small_nonempty_block(hbp, sz, + TRUE /* report_if_found */); + } else if (empty) { +# ifdef ENABLE_DISCLAIM + if ((hhdr -> hb_flags & HAS_DISCLAIM) != 0) { + GC_disclaim_and_reclaim_or_free_small_block(hbp); + } else +# endif + /* else */ { + GC_bytes_found += (signed_word)HBLKSIZE; + GC_freehblk(hbp); + } + } else if (GC_find_leak || !GC_block_nearly_full(hhdr, sz)) { + /* group of smaller objects, enqueue the real work */ + struct hblk **rlh = ok -> ok_reclaim_list; + + if (rlh != NULL) { + rlh += BYTES_TO_GRANULES(sz); + hhdr -> hb_next = *rlh; + *rlh = hbp; + } + } /* else not worth salvaging. */ + /* We used to do the nearly_full check later, but we */ + /* already have the right cache context here. Also */ + /* doing it here avoids some silly lock contention in */ + /* GC_malloc_many. */ + if (IS_PTRFREE_SAFE(hhdr)) { + GC_atomic_in_use += sz * hhdr -> hb_n_marks; + } else { + GC_composite_in_use += sz * hhdr -> hb_n_marks; + } + } +} + +#if !defined(NO_DEBUGGING) +/* Routines to gather and print heap block info */ +/* intended for debugging. Otherwise should be called */ +/* with the allocator lock held. */ + +struct Print_stats +{ + size_t number_of_blocks; + size_t total_bytes; +}; + +EXTERN_C_BEGIN /* to avoid "no previous prototype" clang warning */ +unsigned GC_n_set_marks(hdr *); +EXTERN_C_END + +#ifdef USE_MARK_BYTES + +/* Return the number of set mark bits in the given header. */ +/* Remains externally visible as used by GNU GCJ currently. */ +/* There could be a race between GC_clear_hdr_marks and this */ +/* function but the latter is for a debug purpose. */ +GC_ATTR_NO_SANITIZE_THREAD +unsigned GC_n_set_marks(hdr *hhdr) +{ + unsigned result = 0; + word i; + word offset = MARK_BIT_OFFSET(hhdr -> hb_sz); + word limit = FINAL_MARK_BIT(hhdr -> hb_sz); + + for (i = 0; i < limit; i += offset) { + result += hhdr -> hb_marks[i]; + } + GC_ASSERT(hhdr -> hb_marks[limit]); /* the one set past the end */ + return result; +} + +#else + +/* Number of set bits in a word. Not performance critical. */ +static unsigned count_ones(word n) +{ + unsigned result = 0; + + for (; n > 0; n >>= 1) + if (n & 1) result++; + + return result; +} + +unsigned GC_n_set_marks(hdr *hhdr) +{ + unsigned result = 0; + word sz = hhdr -> hb_sz; + word i; +# ifdef MARK_BIT_PER_OBJ + word n_objs = HBLK_OBJS(sz); + word n_mark_words = divWORDSZ(n_objs > 0 ? n_objs : 1); /* round down */ + + for (i = 0; i <= n_mark_words; i++) { + result += count_ones(hhdr -> hb_marks[i]); + } +# else + + for (i = 0; i < MARK_BITS_SZ; i++) { + result += count_ones(hhdr -> hb_marks[i]); + } +# endif + GC_ASSERT(result > 0); + result--; /* exclude the one bit set past the end */ +# ifndef MARK_BIT_PER_OBJ + if (IS_UNCOLLECTABLE(hhdr -> hb_obj_kind)) { + unsigned ngranules = (unsigned)BYTES_TO_GRANULES(sz); + + /* As mentioned in GC_set_hdr_marks(), all the bits are set */ + /* instead of every n-th, thus the result should be adjusted. */ + GC_ASSERT(ngranules > 0 && result % ngranules == 0); + result /= ngranules; + } +# endif + return result; +} + +#endif /* !USE_MARK_BYTES */ + +GC_API unsigned GC_CALL GC_count_set_marks_in_hblk(const void *p) { + return GC_n_set_marks(HDR((/* no const */ void *)(word)p)); +} + +STATIC void GC_CALLBACK GC_print_block_descr(struct hblk *h, + GC_word /* struct PrintStats */ raw_ps) +{ + hdr *hhdr = HDR(h); + word sz = hhdr -> hb_sz; + struct Print_stats *ps = (struct Print_stats *)raw_ps; + unsigned n_marks = GC_n_set_marks(hhdr); + unsigned n_objs = (unsigned)HBLK_OBJS(sz); + +# ifndef PARALLEL_MARK + GC_ASSERT(hhdr -> hb_n_marks == n_marks); +# endif + GC_ASSERT((n_objs > 0 ? n_objs : 1) >= n_marks); + GC_printf("%u,%u,%u,%u\n", + hhdr -> hb_obj_kind, (unsigned)sz, n_marks, n_objs); + ps -> number_of_blocks++; + ps -> total_bytes += + (sz + HBLKSIZE-1) & ~(word)(HBLKSIZE-1); /* round up */ +} + +void GC_print_block_list(void) +{ + struct Print_stats pstats; + + GC_printf("kind(0=ptrfree/1=normal/2=unc.)," + "obj_sz,#marks_set,#objs_in_block\n"); + BZERO(&pstats, sizeof(pstats)); + GC_apply_to_all_blocks(GC_print_block_descr, (word)&pstats); + GC_printf("blocks= %lu, total_bytes= %lu\n", + (unsigned long)pstats.number_of_blocks, + (unsigned long)pstats.total_bytes); +} + +/* Currently for debugger use only. Assumes the allocator lock is held */ +/* at least in the reader mode but no assertion about it by design. */ +GC_API void GC_CALL GC_print_free_list(int kind, size_t sz_in_granules) +{ + void *flh_next; + int n; + + GC_ASSERT(kind < MAXOBJKINDS); + GC_ASSERT(sz_in_granules <= MAXOBJGRANULES); + flh_next = GC_obj_kinds[kind].ok_freelist[sz_in_granules]; + for (n = 0; flh_next; n++) { + GC_printf("Free object in heap block %p [%d]: %p\n", + (void *)HBLKPTR(flh_next), n, flh_next); + flh_next = obj_link(flh_next); + } +} + +#endif /* !NO_DEBUGGING */ + +/* + * Clear all obj_link pointers in the list of free objects *flp. + * Clear *flp. + * This must be done before dropping a list of free gcj-style objects, + * since may otherwise end up with dangling "descriptor" pointers. + * It may help for other pointer-containing objects. + */ +STATIC void GC_clear_fl_links(void **flp) +{ + void *next = *flp; + + while (0 != next) { + *flp = 0; + flp = &(obj_link(next)); + next = *flp; + } +} + +/* + * Perform GC_reclaim_block on the entire heap, after first clearing + * small object free lists (if we are not just looking for leaks). + */ +GC_INNER void GC_start_reclaim(GC_bool report_if_found) +{ + unsigned kind; + + GC_ASSERT(I_HOLD_LOCK()); +# if defined(PARALLEL_MARK) + GC_ASSERT(0 == GC_fl_builder_count); +# endif + /* Reset in use counters. GC_reclaim_block recomputes them. */ + GC_composite_in_use = 0; + GC_atomic_in_use = 0; + /* Clear reclaim- and free-lists */ + for (kind = 0; kind < GC_n_kinds; kind++) { + struct hblk ** rlist = GC_obj_kinds[kind].ok_reclaim_list; + GC_bool should_clobber = (GC_obj_kinds[kind].ok_descriptor != 0); + + if (rlist == 0) continue; /* This kind not used. */ + if (!report_if_found) { + void **fop; + void **lim = &GC_obj_kinds[kind].ok_freelist[MAXOBJGRANULES+1]; + + for (fop = GC_obj_kinds[kind].ok_freelist; + (word)fop < (word)lim; (*(word **)&fop)++) { + if (*fop != 0) { + if (should_clobber) { + GC_clear_fl_links(fop); + } else { + *fop = 0; + } + } + } + } /* otherwise free list objects are marked, */ + /* and it's safe to leave them. */ + BZERO(rlist, (MAXOBJGRANULES + 1) * sizeof(void *)); + } + + + /* Go through all heap blocks (in hblklist) and reclaim unmarked objects */ + /* or enqueue the block for later processing. */ + GC_apply_to_all_blocks(GC_reclaim_block, (word)report_if_found); + +# ifdef EAGER_SWEEP + /* This is a very stupid thing to do. We make it possible anyway, */ + /* so that you can convince yourself that it really is very stupid. */ + GC_reclaim_all((GC_stop_func)0, FALSE); +# elif defined(ENABLE_DISCLAIM) + /* However, make sure to clear reclaimable objects of kinds with */ + /* unconditional marking enabled before we do any significant */ + /* marking work. */ + GC_reclaim_unconditionally_marked(); +# endif +# if defined(PARALLEL_MARK) + GC_ASSERT(0 == GC_fl_builder_count); +# endif +} + +/* Sweep blocks of the indicated object size and kind until either */ +/* the appropriate free list is nonempty, or there are no more */ +/* blocks to sweep. */ +GC_INNER void GC_continue_reclaim(word sz /* granules */, int kind) +{ + struct hblk * hbp; + struct obj_kind * ok = &GC_obj_kinds[kind]; + struct hblk ** rlh = ok -> ok_reclaim_list; + void **flh = &(ok -> ok_freelist[sz]); + + GC_ASSERT(I_HOLD_LOCK()); + if (NULL == rlh) + return; /* No blocks of this kind. */ + + for (rlh += sz; (hbp = *rlh) != NULL; ) { + hdr *hhdr = HDR(hbp); + + *rlh = hhdr -> hb_next; + GC_reclaim_small_nonempty_block(hbp, hhdr -> hb_sz, FALSE); + if (*flh != NULL) + break; /* the appropriate free list is nonempty */ + } +} + +/* + * Reclaim all small blocks waiting to be reclaimed. + * Abort and return FALSE when/if (*stop_func)() returns TRUE. + * If this returns TRUE, then it's safe to restart the world + * with incorrectly cleared mark bits. + * If ignore_old is TRUE, then reclaim only blocks that have been + * recently reclaimed, and discard the rest. + * Stop_func may be 0. + */ +GC_INNER GC_bool GC_reclaim_all(GC_stop_func stop_func, GC_bool ignore_old) +{ + word sz; + unsigned kind; + hdr * hhdr; + struct hblk * hbp; + struct hblk ** rlp; + struct hblk ** rlh; +# ifndef NO_CLOCK + CLOCK_TYPE start_time = CLOCK_TYPE_INITIALIZER; + + if (GC_print_stats == VERBOSE) + GET_TIME(start_time); +# endif + GC_ASSERT(I_HOLD_LOCK()); + + for (kind = 0; kind < GC_n_kinds; kind++) { + rlp = GC_obj_kinds[kind].ok_reclaim_list; + if (rlp == 0) continue; + for (sz = 1; sz <= MAXOBJGRANULES; sz++) { + for (rlh = rlp + sz; (hbp = *rlh) != NULL; ) { + if (stop_func != (GC_stop_func)0 && (*stop_func)()) { + return FALSE; + } + hhdr = HDR(hbp); + *rlh = hhdr -> hb_next; + if (!ignore_old + || (word)hhdr->hb_last_reclaimed == GC_gc_no - 1) { + /* It's likely we'll need it this time, too */ + /* It's been touched recently, so this */ + /* shouldn't trigger paging. */ + GC_reclaim_small_nonempty_block(hbp, hhdr->hb_sz, FALSE); + } + } + } + } +# ifndef NO_CLOCK + if (GC_print_stats == VERBOSE) { + CLOCK_TYPE done_time; + + GET_TIME(done_time); + GC_verbose_log_printf( + "Disposing of reclaim lists took %lu ms %lu ns\n", + MS_TIME_DIFF(done_time, start_time), + NS_FRAC_TIME_DIFF(done_time, start_time)); + } +# endif + return TRUE; +} + +#if !defined(EAGER_SWEEP) && defined(ENABLE_DISCLAIM) +/* We do an eager sweep on heap blocks where unconditional marking has */ +/* been enabled, so that any reclaimable objects have been reclaimed */ +/* before we start marking. This is a simplified GC_reclaim_all */ +/* restricted to kinds where ok_mark_unconditionally is true. */ + STATIC void GC_reclaim_unconditionally_marked(void) + { + unsigned kind; + + GC_ASSERT(I_HOLD_LOCK()); + for (kind = 0; kind < GC_n_kinds; kind++) { + word sz; + struct obj_kind *ok = &GC_obj_kinds[kind]; + struct hblk **rlp = ok -> ok_reclaim_list; + + if (NULL == rlp || !(ok -> ok_mark_unconditionally)) continue; + + for (sz = 1; sz <= MAXOBJGRANULES; sz++) { + struct hblk **rlh = rlp + sz; + struct hblk *hbp; + + while ((hbp = *rlh) != NULL) { + hdr *hhdr = HDR(hbp); + + *rlh = hhdr -> hb_next; + GC_reclaim_small_nonempty_block(hbp, hhdr -> hb_sz, FALSE); + } + } + } + } +#endif /* !EAGER_SWEEP && ENABLE_DISCLAIM */ + +struct enumerate_reachable_s { + GC_reachable_object_proc proc; + void *client_data; +}; + +STATIC void GC_CALLBACK GC_do_enumerate_reachable_objects(struct hblk *hbp, + GC_word ped) +{ + struct hblkhdr *hhdr = HDR(hbp); + size_t sz = (size_t)hhdr->hb_sz; + size_t bit_no; + char *p, *plim; + + if (GC_block_empty(hhdr)) { + return; + } + + p = hbp->hb_body; + if (sz > MAXOBJBYTES) { /* one big object */ + plim = p; + } else { + plim = hbp->hb_body + HBLKSIZE - sz; + } + /* Go through all words in block. */ + for (bit_no = 0; p <= plim; bit_no += MARK_BIT_OFFSET(sz), p += sz) { + if (mark_bit_from_hdr(hhdr, bit_no)) { + ((struct enumerate_reachable_s *)ped)->proc(p, sz, + ((struct enumerate_reachable_s *)ped)->client_data); + } + } +} + +GC_API void GC_CALL GC_enumerate_reachable_objects_inner( + GC_reachable_object_proc proc, + void *client_data) +{ + struct enumerate_reachable_s ed; + + GC_ASSERT(I_HOLD_READER_LOCK()); + ed.proc = proc; + ed.client_data = client_data; + GC_apply_to_all_blocks(GC_do_enumerate_reachable_objects, (word)&ed); +} + +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1999-2000 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + + + +/* + * Some simple primitives for allocation with explicit type information. + * Simple objects are allocated such that they contain a GC_descr at the + * end (in the last allocated word). This descriptor may be a procedure + * which then examines an extended descriptor passed as its environment. + * + * Arrays are treated as simple objects if they have sufficiently simple + * structure. Otherwise they are allocated from an array kind that supplies + * a special mark procedure. These arrays contain a pointer to a + * complex_descriptor as their last word. + * This is done because the environment field is too small, and the collector + * must trace the complex_descriptor. + * + * Note that descriptors inside objects may appear cleared, if we encounter a + * false reference to an object on a free list. In the GC_descr case, this + * is OK, since a 0 descriptor corresponds to examining no fields. + * In the complex_descriptor case, we explicitly check for that case. + * + * MAJOR PARTS OF THIS CODE HAVE NOT BEEN TESTED AT ALL and are not testable, + * since they are not accessible through the current interface. + */ + +#include "gc/gc_typed.h" + +#define TYPD_EXTRA_BYTES (sizeof(word) - EXTRA_BYTES) + +STATIC int GC_explicit_kind = 0; + /* Object kind for objects with indirect */ + /* (possibly extended) descriptors. */ + +STATIC int GC_array_kind = 0; + /* Object kind for objects with complex */ + /* descriptors and GC_array_mark_proc. */ + +#define ED_INITIAL_SIZE 100 + +STATIC unsigned GC_typed_mark_proc_index = 0; /* Indices of the typed */ +STATIC unsigned GC_array_mark_proc_index = 0; /* mark procedures. */ + +STATIC void GC_push_typed_structures_proc(void) +{ + GC_PUSH_ALL_SYM(GC_ext_descriptors); +} + +/* Add a multiword bitmap to GC_ext_descriptors arrays. */ +/* Returns starting index on success, -1 otherwise. */ +STATIC signed_word GC_add_ext_descriptor(const word * bm, word nbits) +{ + size_t nwords = divWORDSZ(nbits + CPP_WORDSZ-1); + signed_word result; + size_t i; + + LOCK(); + while (EXPECT(GC_avail_descr + nwords >= GC_ed_size, FALSE)) { + typed_ext_descr_t *newExtD; + size_t new_size; + word ed_size = GC_ed_size; + + if (ed_size == 0) { + GC_ASSERT((word)(&GC_ext_descriptors) % sizeof(word) == 0); + GC_push_typed_structures = GC_push_typed_structures_proc; + UNLOCK(); + new_size = ED_INITIAL_SIZE; + } else { + UNLOCK(); + new_size = 2 * ed_size; + if (new_size > MAX_ENV) return -1; + } + newExtD = (typed_ext_descr_t*)GC_malloc_atomic(new_size + * sizeof(typed_ext_descr_t)); + if (NULL == newExtD) + return -1; + LOCK(); + if (ed_size == GC_ed_size) { + if (GC_avail_descr != 0) { + BCOPY(GC_ext_descriptors, newExtD, + GC_avail_descr * sizeof(typed_ext_descr_t)); + } + GC_ed_size = new_size; + GC_ext_descriptors = newExtD; + } /* else another thread already resized it in the meantime */ + } + result = (signed_word)GC_avail_descr; + for (i = 0; i < nwords-1; i++) { + GC_ext_descriptors[(size_t)result + i].ed_bitmap = bm[i]; + GC_ext_descriptors[(size_t)result + i].ed_continued = TRUE; + } + /* Clear irrelevant (highest) bits for the last element. */ + GC_ext_descriptors[(size_t)result + i].ed_bitmap = + bm[i] & (GC_WORD_MAX >> (nwords * CPP_WORDSZ - nbits)); + GC_ext_descriptors[(size_t)result + i].ed_continued = FALSE; + GC_avail_descr += nwords; + GC_ASSERT(result >= 0); + UNLOCK(); + return result; +} + +/* Table of bitmap descriptors for n word long all pointer objects. */ +STATIC GC_descr GC_bm_table[CPP_WORDSZ / 2]; + +/* Return a descriptor for the concatenation of 2 nwords long objects, */ +/* each of which is described by descriptor d. The result is known */ +/* to be short enough to fit into a bitmap descriptor. */ +/* d is a GC_DS_LENGTH or GC_DS_BITMAP descriptor. */ +STATIC GC_descr GC_double_descr(GC_descr d, size_t nwords) +{ + GC_ASSERT(GC_bm_table[0] == GC_DS_BITMAP); /* bm table is initialized */ + if ((d & GC_DS_TAGS) == GC_DS_LENGTH) { + d = GC_bm_table[BYTES_TO_WORDS((word)d)]; + } + d |= (d & ~(GC_descr)GC_DS_TAGS) >> nwords; + return d; +} + +STATIC mse *GC_CALLBACK GC_typed_mark_proc(word * addr, mse * mark_stack_ptr, + mse * mark_stack_limit, word env); + +STATIC mse *GC_CALLBACK GC_array_mark_proc(word * addr, mse * mark_stack_ptr, + mse * mark_stack_limit, word env); + +STATIC void GC_init_explicit_typing(void) +{ + unsigned i; + + /* Set up object kind with simple indirect descriptor. */ + /* Descriptor is in the last word of the object. */ + GC_typed_mark_proc_index = GC_new_proc_inner(GC_typed_mark_proc); + GC_explicit_kind = (int)GC_new_kind_inner(GC_new_free_list_inner(), + (WORDS_TO_BYTES((word)-1) | GC_DS_PER_OBJECT), + TRUE, TRUE); + + /* Set up object kind with array descriptor. */ + GC_array_mark_proc_index = GC_new_proc_inner(GC_array_mark_proc); + GC_array_kind = (int)GC_new_kind_inner(GC_new_free_list_inner(), + GC_MAKE_PROC(GC_array_mark_proc_index, 0), + FALSE, TRUE); + + GC_bm_table[0] = GC_DS_BITMAP; + for (i = 1; i < CPP_WORDSZ / 2; i++) { + GC_bm_table[i] = (((word)-1) << (CPP_WORDSZ - i)) | GC_DS_BITMAP; + } +} + +STATIC mse *GC_CALLBACK GC_typed_mark_proc(word * addr, mse * mark_stack_ptr, + mse * mark_stack_limit, word env) +{ + word bm; + ptr_t current_p = (ptr_t)addr; + ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr; + ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr; + DECLARE_HDR_CACHE; + + /* The allocator lock is held by the collection initiating thread. */ + GC_ASSERT(GC_get_parallel() || I_HOLD_LOCK()); + bm = GC_ext_descriptors[env].ed_bitmap; + + INIT_HDR_CACHE; + for (; bm != 0; bm >>= 1, current_p += sizeof(word)) { + if (bm & 1) { + word current; + + LOAD_WORD_OR_CONTINUE(current, current_p); + FIXUP_POINTER(current); + if (current > (word)least_ha && current < (word)greatest_ha) { + PUSH_CONTENTS((ptr_t)current, mark_stack_ptr, + mark_stack_limit, current_p); + } + } + } + if (GC_ext_descriptors[env].ed_continued) { + /* Push an entry with the rest of the descriptor back onto the */ + /* stack. Thus we never do too much work at once. Note that */ + /* we also can't overflow the mark stack unless we actually */ + /* mark something. */ + mark_stack_ptr++; + if ((word)mark_stack_ptr >= (word)mark_stack_limit) { + mark_stack_ptr = GC_signal_mark_stack_overflow(mark_stack_ptr); + } + mark_stack_ptr -> mse_start = (ptr_t)(addr + CPP_WORDSZ); + mark_stack_ptr -> mse_descr.w = + GC_MAKE_PROC(GC_typed_mark_proc_index, env + 1); + } + return mark_stack_ptr; +} + +GC_API GC_descr GC_CALL GC_make_descriptor(const GC_word * bm, size_t len) +{ + signed_word last_set_bit = (signed_word)len - 1; + GC_descr d; + +# if defined(AO_HAVE_load_acquire) && defined(AO_HAVE_store_release) + if (!EXPECT(AO_load_acquire(&GC_explicit_typing_initialized), TRUE)) { + LOCK(); + if (!GC_explicit_typing_initialized) { + GC_init_explicit_typing(); + AO_store_release(&GC_explicit_typing_initialized, TRUE); + } + UNLOCK(); + } +# else + LOCK(); + if (!EXPECT(GC_explicit_typing_initialized, TRUE)) { + GC_init_explicit_typing(); + GC_explicit_typing_initialized = TRUE; + } + UNLOCK(); +# endif + + while (last_set_bit >= 0 && !GC_get_bit(bm, (word)last_set_bit)) + last_set_bit--; + if (last_set_bit < 0) return 0; /* no pointers */ + +# if ALIGNMENT == CPP_WORDSZ/8 + { + signed_word i; + + for (i = 0; i < last_set_bit; i++) { + if (!GC_get_bit(bm, (word)i)) { + break; + } + } + if (i == last_set_bit) { + /* An initial section contains all pointers. Use length descriptor. */ + return WORDS_TO_BYTES((word)last_set_bit + 1) | GC_DS_LENGTH; + } + } +# endif + if (last_set_bit < BITMAP_BITS) { + signed_word i; + + /* Hopefully the common case. */ + /* Build bitmap descriptor (with bits reversed) */ + d = SIGNB; + for (i = last_set_bit - 1; i >= 0; i--) { + d >>= 1; + if (GC_get_bit(bm, (word)i)) d |= SIGNB; + } + d |= GC_DS_BITMAP; + } else { + signed_word index = GC_add_ext_descriptor(bm, (word)last_set_bit + 1); + + if (EXPECT(index == -1, FALSE)) { + /* Out of memory: use a conservative approximation. */ + return WORDS_TO_BYTES((word)last_set_bit + 1) | GC_DS_LENGTH; + } + d = GC_MAKE_PROC(GC_typed_mark_proc_index, index); + } + return d; +} + +#ifdef AO_HAVE_store_release +# define set_obj_descr(op, nwords, d) \ + AO_store_release((volatile AO_t *)(op) + (nwords) - 1, (AO_t)(d)) +#else +# define set_obj_descr(op, nwords, d) \ + (void)(((word *)(op))[(nwords) - 1] = (word)(d)) +#endif + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_explicitly_typed(size_t lb, + GC_descr d) +{ + void *op; + size_t nwords; + + GC_ASSERT(GC_explicit_typing_initialized); + if (EXPECT(0 == lb, FALSE)) lb = 1; /* ensure nwords > 1 */ + op = GC_malloc_kind(SIZET_SAT_ADD(lb, TYPD_EXTRA_BYTES), GC_explicit_kind); + if (EXPECT(NULL == op, FALSE)) return NULL; + + /* It is not safe to use GC_size_map to compute nwords here as */ + /* the former might be updated asynchronously. */ + nwords = GRANULES_TO_WORDS(BYTES_TO_GRANULES(GC_size(op))); + set_obj_descr(op, nwords, d); + GC_dirty((word *)op + nwords - 1); + REACHABLE_AFTER_DIRTY(d); + return op; +} + +GC_API GC_ATTR_MALLOC void * GC_CALL + GC_malloc_explicitly_typed_ignore_off_page(size_t lb, GC_descr d) +{ + void *op; + size_t nwords; + + if (lb < HBLKSIZE - sizeof(word)) + return GC_malloc_explicitly_typed(lb, d); + + GC_ASSERT(GC_explicit_typing_initialized); + /* TYPD_EXTRA_BYTES is not used here because ignore-off-page */ + /* objects with the requested size of at least HBLKSIZE do not */ + /* have EXTRA_BYTES added by GC_generic_malloc_aligned(). */ + op = GC_clear_stack(GC_generic_malloc_aligned( + SIZET_SAT_ADD(lb, sizeof(word)), + GC_explicit_kind, IGNORE_OFF_PAGE, 0)); + if (EXPECT(NULL == op, FALSE)) return NULL; + + nwords = GRANULES_TO_WORDS(BYTES_TO_GRANULES(GC_size(op))); + set_obj_descr(op, nwords, d); + GC_dirty((word *)op + nwords - 1); + REACHABLE_AFTER_DIRTY(d); + return op; +} + +/* Array descriptors. GC_array_mark_proc understands these. */ +/* We may eventually need to add provisions for headers and */ +/* trailers. Hence we provide for tree structured descriptors, */ +/* though we don't really use them currently. */ + +struct LeafDescriptor { /* Describes simple array. */ + word ld_tag; +# define LEAF_TAG 1 + word ld_size; /* Bytes per element; non-zero, */ + /* multiple of ALIGNMENT. */ + word ld_nelements; /* Number of elements. */ + GC_descr ld_descriptor; /* A simple length, bitmap, */ + /* or procedure descriptor. */ +}; + +struct ComplexArrayDescriptor { + word ad_tag; +# define ARRAY_TAG 2 + word ad_nelements; + union ComplexDescriptor *ad_element_descr; +}; + +struct SequenceDescriptor { + word sd_tag; +# define SEQUENCE_TAG 3 + union ComplexDescriptor *sd_first; + union ComplexDescriptor *sd_second; +}; + +typedef union ComplexDescriptor { + struct LeafDescriptor ld; + struct ComplexArrayDescriptor ad; + struct SequenceDescriptor sd; +} complex_descriptor; + +STATIC complex_descriptor *GC_make_leaf_descriptor(word size, word nelements, + GC_descr d) +{ + complex_descriptor *result = (complex_descriptor *) + GC_malloc_atomic(sizeof(struct LeafDescriptor)); + + GC_ASSERT(size != 0); + if (EXPECT(NULL == result, FALSE)) return NULL; + + result -> ld.ld_tag = LEAF_TAG; + result -> ld.ld_size = size; + result -> ld.ld_nelements = nelements; + result -> ld.ld_descriptor = d; + return result; +} + +STATIC complex_descriptor *GC_make_sequence_descriptor( + complex_descriptor *first, + complex_descriptor *second) +{ + struct SequenceDescriptor *result = (struct SequenceDescriptor *) + GC_malloc(sizeof(struct SequenceDescriptor)); + /* Note: for a reason, the sanitizer runtime complains */ + /* of insufficient space for complex_descriptor if the */ + /* pointer type of result variable is changed to. */ + + if (EXPECT(NULL == result, FALSE)) return NULL; + + /* Can't result in overly conservative marking, since tags are */ + /* very small integers. Probably faster than maintaining type info. */ + result -> sd_tag = SEQUENCE_TAG; + result -> sd_first = first; + result -> sd_second = second; + GC_dirty(result); + REACHABLE_AFTER_DIRTY(first); + REACHABLE_AFTER_DIRTY(second); + return (complex_descriptor *)result; +} + +#define NO_MEM (-1) +#define SIMPLE 0 +#define LEAF 1 +#define COMPLEX 2 + +/* Build a descriptor for an array with nelements elements, each of */ +/* which can be described by a simple descriptor d. We try to optimize */ +/* some common cases. If the result is COMPLEX, a complex_descriptor* */ +/* value is returned in *pcomplex_d. If the result is LEAF, then a */ +/* LeafDescriptor value is built in the structure pointed to by pleaf. */ +/* The tag in the *pleaf structure is not set. If the result is */ +/* SIMPLE, then a GC_descr value is returned in *psimple_d. If the */ +/* result is NO_MEM, then we failed to allocate the descriptor. */ +/* The implementation assumes GC_DS_LENGTH is 0. *pleaf, *pcomplex_d */ +/* and *psimple_d may be used as temporaries during the construction. */ +STATIC int GC_make_array_descriptor(size_t nelements, size_t size, + GC_descr d, GC_descr *psimple_d, + complex_descriptor **pcomplex_d, + struct LeafDescriptor *pleaf) +{ +# define OPT_THRESHOLD 50 + /* For larger arrays, we try to combine descriptors of adjacent */ + /* descriptors to speed up marking, and to reduce the amount */ + /* of space needed on the mark stack. */ + + GC_ASSERT(size != 0); + if ((d & GC_DS_TAGS) == GC_DS_LENGTH) { + if (d == (GC_descr)size) { + *psimple_d = nelements * d; /* no overflow guaranteed by caller */ + return SIMPLE; + } else if (0 == d) { + *psimple_d = 0; + return SIMPLE; + } + } + + if (nelements <= OPT_THRESHOLD) { + if (nelements <= 1) { + *psimple_d = nelements == 1 ? d : 0; + return SIMPLE; + } + } else if (size <= BITMAP_BITS/2 + && (d & GC_DS_TAGS) != GC_DS_PROC + && (size & (sizeof(word)-1)) == 0) { + complex_descriptor *one_element, *beginning; + int result = GC_make_array_descriptor(nelements / 2, 2 * size, + GC_double_descr(d, BYTES_TO_WORDS(size)), + psimple_d, pcomplex_d, pleaf); + + if ((nelements & 1) == 0 || EXPECT(NO_MEM == result, FALSE)) + return result; + + one_element = GC_make_leaf_descriptor(size, 1, d); + if (EXPECT(NULL == one_element, FALSE)) return NO_MEM; + + if (COMPLEX == result) { + beginning = *pcomplex_d; + } else { + beginning = SIMPLE == result ? + GC_make_leaf_descriptor(size, 1, *psimple_d) : + GC_make_leaf_descriptor(pleaf -> ld_size, + pleaf -> ld_nelements, + pleaf -> ld_descriptor); + if (EXPECT(NULL == beginning, FALSE)) return NO_MEM; + } + *pcomplex_d = GC_make_sequence_descriptor(beginning, one_element); + if (EXPECT(NULL == *pcomplex_d, FALSE)) return NO_MEM; + + return COMPLEX; + } + + pleaf -> ld_size = size; + pleaf -> ld_nelements = nelements; + pleaf -> ld_descriptor = d; + return LEAF; +} + +struct GC_calloc_typed_descr_s { + struct LeafDescriptor leaf; + GC_descr simple_d; + complex_descriptor *complex_d; + word alloc_lb; /* size_t actually */ + signed_word descr_type; /* int actually */ +}; + +GC_API int GC_CALL GC_calloc_prepare_explicitly_typed( + struct GC_calloc_typed_descr_s *pctd, + size_t ctd_sz, + size_t n, size_t lb, GC_descr d) +{ + GC_STATIC_ASSERT(sizeof(struct LeafDescriptor) % sizeof(word) == 0); + GC_STATIC_ASSERT(sizeof(struct GC_calloc_typed_descr_s) + == GC_CALLOC_TYPED_DESCR_WORDS * sizeof(word)); + GC_ASSERT(GC_explicit_typing_initialized); + GC_ASSERT(sizeof(struct GC_calloc_typed_descr_s) == ctd_sz); + (void)ctd_sz; /* unused currently */ + if (EXPECT(0 == lb || 0 == n, FALSE)) lb = n = 1; + if (EXPECT((lb | n) > GC_SQRT_SIZE_MAX, FALSE) /* fast initial check */ + && n > GC_SIZE_MAX / lb) { + pctd -> alloc_lb = GC_SIZE_MAX; /* n*lb overflow */ + pctd -> descr_type = NO_MEM; + /* The rest of the fields are unset. */ + return 0; /* failure */ + } + + pctd -> descr_type = GC_make_array_descriptor((word)n, (word)lb, d, + &(pctd -> simple_d), &(pctd -> complex_d), + &(pctd -> leaf)); + switch (pctd -> descr_type) { + case NO_MEM: + case SIMPLE: + pctd -> alloc_lb = (word)lb * n; + break; + case LEAF: + pctd -> alloc_lb = (word)SIZET_SAT_ADD(lb * n, + sizeof(struct LeafDescriptor) + TYPD_EXTRA_BYTES); + break; + case COMPLEX: + pctd -> alloc_lb = (word)SIZET_SAT_ADD(lb * n, TYPD_EXTRA_BYTES); + break; + } + return 1; /* success */ +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_calloc_do_explicitly_typed( + const struct GC_calloc_typed_descr_s *pctd, + size_t ctd_sz) +{ + void *op; + size_t nwords; + + GC_ASSERT(sizeof(struct GC_calloc_typed_descr_s) == ctd_sz); + (void)ctd_sz; /* unused currently */ + switch (pctd -> descr_type) { + case NO_MEM: + return (*GC_get_oom_fn())((size_t)(pctd -> alloc_lb)); + case SIMPLE: + return GC_malloc_explicitly_typed((size_t)(pctd -> alloc_lb), + pctd -> simple_d); + case LEAF: + case COMPLEX: + break; + default: + ABORT_RET("Bad descriptor type"); + return NULL; + } + op = GC_malloc_kind((size_t)(pctd -> alloc_lb), GC_array_kind); + if (EXPECT(NULL == op, FALSE)) + return NULL; + + nwords = GRANULES_TO_WORDS(BYTES_TO_GRANULES(GC_size(op))); + if (pctd -> descr_type == LEAF) { + /* Set up the descriptor inside the object itself. */ + struct LeafDescriptor *lp = + (struct LeafDescriptor *)((word *)op + nwords - + (BYTES_TO_WORDS(sizeof(struct LeafDescriptor)) + 1)); + + lp -> ld_tag = LEAF_TAG; + lp -> ld_size = pctd -> leaf.ld_size; + lp -> ld_nelements = pctd -> leaf.ld_nelements; + lp -> ld_descriptor = pctd -> leaf.ld_descriptor; + /* Hold the allocator lock (in the reader mode which should be */ + /* enough) while writing the descriptor word to the object to */ + /* ensure that the descriptor contents are seen by */ + /* GC_array_mark_proc as expected. */ + /* TODO: It should be possible to replace locking with the atomic */ + /* operations (with the release barrier here) but, in this case, */ + /* avoiding the acquire barrier in GC_array_mark_proc seems to */ + /* be tricky as GC_mark_some might be invoked with the world */ + /* running. */ + READER_LOCK(); + ((word *)op)[nwords - 1] = (word)lp; + READER_UNLOCK_RELEASE(); + } else { +# ifndef GC_NO_FINALIZATION + READER_LOCK(); + ((word *)op)[nwords - 1] = (word)(pctd -> complex_d); + READER_UNLOCK_RELEASE(); + + GC_dirty((word *)op + nwords - 1); + REACHABLE_AFTER_DIRTY(pctd -> complex_d); + + /* Make sure the descriptor is cleared once there is any danger */ + /* it may have been collected. */ + if (EXPECT(GC_general_register_disappearing_link( + (void **)op + nwords - 1, op) == GC_NO_MEMORY, FALSE)) +# endif + { + /* Couldn't register it due to lack of memory. Punt. */ + return (*GC_get_oom_fn())((size_t)(pctd -> alloc_lb)); + } + } + return op; +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_calloc_explicitly_typed(size_t n, + size_t lb, + GC_descr d) +{ + struct GC_calloc_typed_descr_s ctd; + + (void)GC_calloc_prepare_explicitly_typed(&ctd, sizeof(ctd), n, lb, d); + return GC_calloc_do_explicitly_typed(&ctd, sizeof(ctd)); +} + +/* Return the size of the object described by complex_d. It would be */ +/* faster to store this directly, or to compute it as part of */ +/* GC_push_complex_descriptor, but hopefully it does not matter. */ +STATIC word GC_descr_obj_size(complex_descriptor *complex_d) +{ + switch(complex_d -> ad.ad_tag) { + case LEAF_TAG: + return complex_d -> ld.ld_nelements * complex_d -> ld.ld_size; + case ARRAY_TAG: + return complex_d -> ad.ad_nelements + * GC_descr_obj_size(complex_d -> ad.ad_element_descr); + case SEQUENCE_TAG: + return GC_descr_obj_size(complex_d -> sd.sd_first) + + GC_descr_obj_size(complex_d -> sd.sd_second); + default: + ABORT_RET("Bad complex descriptor"); + return 0; + } +} + +/* Push descriptors for the object at addr with complex descriptor */ +/* onto the mark stack. Return NULL if the mark stack overflowed. */ +STATIC mse *GC_push_complex_descriptor(word *addr, + complex_descriptor *complex_d, + mse *msp, mse *msl) +{ + ptr_t current = (ptr_t)addr; + word nelements; + word sz; + word i; + GC_descr d; + complex_descriptor *element_descr; + + switch(complex_d -> ad.ad_tag) { + case LEAF_TAG: + d = complex_d -> ld.ld_descriptor; + nelements = complex_d -> ld.ld_nelements; + sz = complex_d -> ld.ld_size; + + if (EXPECT(msl - msp <= (signed_word)nelements, FALSE)) return NULL; + GC_ASSERT(sz != 0); + for (i = 0; i < nelements; i++) { + msp++; + msp -> mse_start = current; + msp -> mse_descr.w = d; + current += sz; + } + break; + case ARRAY_TAG: + element_descr = complex_d -> ad.ad_element_descr; + nelements = complex_d -> ad.ad_nelements; + sz = GC_descr_obj_size(element_descr); + GC_ASSERT(sz != 0 || 0 == nelements); + for (i = 0; i < nelements; i++) { + msp = GC_push_complex_descriptor((word *)current, element_descr, + msp, msl); + if (EXPECT(NULL == msp, FALSE)) return NULL; + current += sz; + } + break; + case SEQUENCE_TAG: + sz = GC_descr_obj_size(complex_d -> sd.sd_first); + msp = GC_push_complex_descriptor((word *)current, + complex_d -> sd.sd_first, msp, msl); + if (EXPECT(NULL == msp, FALSE)) return NULL; + GC_ASSERT(sz != 0); + current += sz; + msp = GC_push_complex_descriptor((word *)current, + complex_d -> sd.sd_second, msp, msl); + break; + default: + ABORT("Bad complex descriptor"); + } + return msp; +} + +GC_ATTR_NO_SANITIZE_THREAD +static complex_descriptor *get_complex_descr(word *addr, size_t nwords) +{ + return (complex_descriptor *)addr[nwords - 1]; +} + +/* Used by GC_calloc_do_explicitly_typed via GC_array_kind. */ +STATIC mse *GC_CALLBACK GC_array_mark_proc(word *addr, mse *mark_stack_ptr, + mse *mark_stack_limit, word env) +{ + hdr *hhdr = HDR(addr); + word sz = hhdr -> hb_sz; + size_t nwords = (size_t)BYTES_TO_WORDS(sz); + complex_descriptor *complex_d = get_complex_descr(addr, nwords); + mse *orig_mark_stack_ptr = mark_stack_ptr; + mse *new_mark_stack_ptr; + + UNUSED_ARG(env); + if (NULL == complex_d) { + /* Found a reference to a free list entry. Ignore it. */ + return orig_mark_stack_ptr; + } + /* In use counts were already updated when array descriptor was */ + /* pushed. Here we only replace it by subobject descriptors, so */ + /* no update is necessary. */ + new_mark_stack_ptr = GC_push_complex_descriptor(addr, complex_d, + mark_stack_ptr, + mark_stack_limit-1); + if (new_mark_stack_ptr == 0) { + /* Explicitly instruct Clang Static Analyzer that ptr is non-null. */ + if (NULL == mark_stack_ptr) ABORT("Bad mark_stack_ptr"); + + /* Does not fit. Conservatively push the whole array as a unit and */ + /* request a mark stack expansion. This cannot cause a mark stack */ + /* overflow, since it replaces the original array entry. */ +# ifdef PARALLEL_MARK + /* We might be using a local_mark_stack in parallel mode. */ + if (GC_mark_stack + GC_mark_stack_size == mark_stack_limit) +# endif + { + GC_mark_stack_too_small = TRUE; + } + new_mark_stack_ptr = orig_mark_stack_ptr + 1; + new_mark_stack_ptr -> mse_start = (ptr_t)addr; + new_mark_stack_ptr -> mse_descr.w = sz | GC_DS_LENGTH; + } else { + /* Push descriptor itself. */ + new_mark_stack_ptr++; + new_mark_stack_ptr -> mse_start = (ptr_t)(addr + nwords - 1); + new_mark_stack_ptr -> mse_descr.w = sizeof(word) | GC_DS_LENGTH; + } + return new_mark_stack_ptr; +} + + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1999-2001 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +#include +#include + +#ifdef GC_SOLARIS_THREADS +# include +#endif + +#if defined(UNIX_LIKE) || defined(CYGWIN32) || defined(SYMBIAN) \ + || (defined(CONSOLE_LOG) && defined(MSWIN32)) +# include +# include +#endif + +#if defined(CONSOLE_LOG) && defined(MSWIN32) && !defined(__GNUC__) +# include +#endif + +#ifdef NONSTOP +# include +#endif + +#ifdef THREADS +# ifdef PCR +# include "il/PCR_IL.h" + GC_INNER PCR_Th_ML GC_allocate_ml; +# elif defined(SN_TARGET_PSP2) + GC_INNER WapiMutex GC_allocate_ml_PSP2 = { 0, NULL }; +# elif defined(GC_DEFN_ALLOCATE_ML) && !defined(USE_RWLOCK) \ + || defined(SN_TARGET_PS3) +# include + GC_INNER pthread_mutex_t GC_allocate_ml; +# endif + /* For other platforms with threads, the allocator lock and possibly */ + /* GC_lock_holder variables are defined in the thread support code. */ +#endif /* THREADS */ + +#ifdef DYNAMIC_LOADING + /* We need to register the main data segment. Returns TRUE unless */ + /* this is done implicitly as part of dynamic library registration. */ +# define GC_REGISTER_MAIN_STATIC_DATA() GC_register_main_static_data() +#elif defined(GC_DONT_REGISTER_MAIN_STATIC_DATA) +# define GC_REGISTER_MAIN_STATIC_DATA() FALSE +#else + /* Don't unnecessarily call GC_register_main_static_data() in case */ + /* dyn_load.c isn't linked in. */ +# define GC_REGISTER_MAIN_STATIC_DATA() TRUE +#endif + +#ifdef NEED_CANCEL_DISABLE_COUNT + __thread unsigned char GC_cancel_disable_count = 0; +#endif + +GC_FAR struct _GC_arrays GC_arrays /* = { 0 } */; + +GC_INNER unsigned GC_n_mark_procs = GC_RESERVED_MARK_PROCS; + +GC_INNER unsigned GC_n_kinds = GC_N_KINDS_INITIAL_VALUE; + +GC_INNER GC_bool GC_debugging_started = FALSE; + /* defined here so we don't have to load dbg_mlc.o */ + +ptr_t GC_stackbottom = 0; + +#if defined(E2K) && defined(THREADS) || defined(IA64) + GC_INNER ptr_t GC_register_stackbottom = NULL; +#endif + +int GC_dont_gc = FALSE; + +int GC_dont_precollect = FALSE; + +GC_bool GC_quiet = 0; /* used also in pcr_interface.c */ + +#if !defined(NO_CLOCK) || !defined(SMALL_CONFIG) + GC_INNER int GC_print_stats = 0; +#endif + +#ifdef GC_PRINT_BACK_HEIGHT + GC_INNER GC_bool GC_print_back_height = TRUE; +#else + GC_INNER GC_bool GC_print_back_height = FALSE; +#endif + +#ifndef NO_DEBUGGING +# ifdef GC_DUMP_REGULARLY + GC_INNER GC_bool GC_dump_regularly = TRUE; + /* Generate regular debugging dumps. */ +# else + GC_INNER GC_bool GC_dump_regularly = FALSE; +# endif +# ifndef NO_CLOCK + STATIC CLOCK_TYPE GC_init_time; + /* The time that the GC was initialized at. */ +# endif +#endif /* !NO_DEBUGGING */ + +#ifdef KEEP_BACK_PTRS + GC_INNER long GC_backtraces = 0; + /* Number of random backtraces to generate for each GC. */ +#endif + +#ifdef FIND_LEAK + int GC_find_leak = 1; +#else + int GC_find_leak = 0; +#endif + +#ifndef SHORT_DBG_HDRS +# ifdef GC_FINDLEAK_DELAY_FREE + GC_INNER GC_bool GC_findleak_delay_free = TRUE; +# else + GC_INNER GC_bool GC_findleak_delay_free = FALSE; +# endif +#endif /* !SHORT_DBG_HDRS */ + +#ifdef ALL_INTERIOR_POINTERS + int GC_all_interior_pointers = 1; +#else + int GC_all_interior_pointers = 0; +#endif + +#ifdef FINALIZE_ON_DEMAND + int GC_finalize_on_demand = 1; +#else + int GC_finalize_on_demand = 0; +#endif + +#ifdef JAVA_FINALIZATION + int GC_java_finalization = 1; +#else + int GC_java_finalization = 0; +#endif + +/* All accesses to it should be synchronized to avoid data race. */ +GC_finalizer_notifier_proc GC_finalizer_notifier = + (GC_finalizer_notifier_proc)0; + +#ifdef GC_FORCE_UNMAP_ON_GCOLLECT + /* Has no effect unless USE_MUNMAP. */ + /* Has no effect on implicitly-initiated garbage collections. */ + GC_INNER GC_bool GC_force_unmap_on_gcollect = TRUE; +#else + GC_INNER GC_bool GC_force_unmap_on_gcollect = FALSE; +#endif + +#ifndef GC_LARGE_ALLOC_WARN_INTERVAL +# define GC_LARGE_ALLOC_WARN_INTERVAL 5 +#endif +GC_INNER long GC_large_alloc_warn_interval = GC_LARGE_ALLOC_WARN_INTERVAL; + /* Interval between unsuppressed warnings. */ + +STATIC void * GC_CALLBACK GC_default_oom_fn(size_t bytes_requested) +{ + UNUSED_ARG(bytes_requested); + return NULL; +} + +/* All accesses to it should be synchronized to avoid data race. */ +GC_oom_func GC_oom_fn = GC_default_oom_fn; + +#ifdef CAN_HANDLE_FORK +# ifdef HANDLE_FORK + GC_INNER int GC_handle_fork = 1; + /* The value is examined by GC_thr_init. */ +# else + GC_INNER int GC_handle_fork = FALSE; +# endif + +#elif !defined(HAVE_NO_FORK) + GC_API void GC_CALL GC_atfork_prepare(void) + { +# ifdef THREADS + ABORT("fork() handling unsupported"); +# endif + } + + GC_API void GC_CALL GC_atfork_parent(void) + { + /* empty */ + } + + GC_API void GC_CALL GC_atfork_child(void) + { + /* empty */ + } +#endif /* !CAN_HANDLE_FORK && !HAVE_NO_FORK */ + +/* Overrides the default automatic handle-fork mode. Has effect only */ +/* if called before GC_INIT. */ +GC_API void GC_CALL GC_set_handle_fork(int value) +{ +# ifdef CAN_HANDLE_FORK + if (!GC_is_initialized) + GC_handle_fork = value >= -1 ? value : 1; + /* Map all negative values except for -1 to a positive one. */ +# elif defined(THREADS) || (defined(DARWIN) && defined(MPROTECT_VDB)) + if (!GC_is_initialized && value) { +# ifndef SMALL_CONFIG + GC_init(); /* to initialize GC_manual_vdb and GC_stderr */ +# ifndef THREADS + if (GC_manual_vdb) + return; +# endif +# endif + ABORT("fork() handling unsupported"); + } +# else + /* No at-fork handler is needed in the single-threaded mode. */ + UNUSED_ARG(value); +# endif +} + +/* Set things up so that GC_size_map[i] >= granules(i), */ +/* but not too much bigger */ +/* and so that size_map contains relatively few distinct entries */ +/* This was originally stolen from Russ Atkinson's Cedar */ +/* quantization algorithm (but we precompute it). */ +STATIC void GC_init_size_map(void) +{ + size_t i = 1; + + /* Map size 0 to something bigger. */ + /* This avoids problems at lower levels. */ + GC_size_map[0] = 1; + + for (; i <= GRANULES_TO_BYTES(GC_TINY_FREELISTS-1) - EXTRA_BYTES; i++) { + GC_size_map[i] = ALLOC_REQUEST_GRANS(i); +# ifndef _MSC_VER + GC_ASSERT(GC_size_map[i] < GC_TINY_FREELISTS); + /* Seems to tickle bug in VC++ 2008 for x64 */ +# endif + } + /* We leave the rest of the array to be filled in on demand. */ +} + +/* + * The following is a gross hack to deal with a problem that can occur + * on machines that are sloppy about stack frame sizes, notably SPARC. + * Bogus pointers may be written to the stack and not cleared for + * a LONG time, because they always fall into holes in stack frames + * that are not written. We partially address this by clearing + * sections of the stack whenever we get control. + */ + +#ifndef SMALL_CLEAR_SIZE +# define SMALL_CLEAR_SIZE 256 /* Clear this much every time. */ +#endif + +#if defined(ALWAYS_SMALL_CLEAR_STACK) || defined(STACK_NOT_SCANNED) + GC_API void * GC_CALL GC_clear_stack(void *arg) + { +# ifndef STACK_NOT_SCANNED + word volatile dummy[SMALL_CLEAR_SIZE]; + BZERO((/* no volatile */ word *)((word)dummy), sizeof(dummy)); +# endif + return arg; + } +#else + +# ifdef THREADS +# define BIG_CLEAR_SIZE 2048 /* Clear this much now and then. */ +# else + STATIC word GC_stack_last_cleared = 0; + /* GC_gc_no value when we last did this. */ + STATIC ptr_t GC_min_sp = NULL; + /* Coolest stack pointer value from which */ + /* we've already cleared the stack. */ + STATIC ptr_t GC_high_water = NULL; + /* "hottest" stack pointer value we have seen */ + /* recently. Degrades over time. */ + STATIC word GC_bytes_allocd_at_reset = 0; +# define DEGRADE_RATE 50 +# endif + +# if defined(__APPLE_CC__) && !GC_CLANG_PREREQ(6, 0) +# define CLEARSTACK_LIMIT_MODIFIER volatile /* to workaround some bug */ +# else +# define CLEARSTACK_LIMIT_MODIFIER /* empty */ +# endif + + EXTERN_C_BEGIN + void *GC_clear_stack_inner(void *, CLEARSTACK_LIMIT_MODIFIER ptr_t); + EXTERN_C_END + +# ifndef ASM_CLEAR_CODE + /* Clear the stack up to about limit. Return arg. This function */ + /* is not static because it could also be erroneously defined in .S */ + /* file, so this error would be caught by the linker. */ + void *GC_clear_stack_inner(void *arg, + CLEARSTACK_LIMIT_MODIFIER ptr_t limit) + { +# define CLEAR_SIZE 213 /* granularity */ + volatile word dummy[CLEAR_SIZE]; + + BZERO((/* no volatile */ word *)((word)dummy), sizeof(dummy)); + if ((word)GC_approx_sp() COOLER_THAN (word)limit) { + (void)GC_clear_stack_inner(arg, limit); + } + /* Make sure the recursive call is not a tail call, and the bzero */ + /* call is not recognized as dead code. */ +# if defined(CPPCHECK) + GC_noop1(dummy[0]); +# else + GC_noop1(COVERT_DATAFLOW(dummy)); +# endif + return arg; + } +# endif /* !ASM_CLEAR_CODE */ + +# ifdef THREADS + /* Used to occasionally clear a bigger chunk. */ + /* TODO: Should be more random than it is ... */ + static unsigned next_random_no(void) + { +# ifdef AO_HAVE_fetch_and_add1 + static volatile AO_t random_no; + + return (unsigned)AO_fetch_and_add1(&random_no) % 13; +# else + static unsigned random_no = 0; + + return (random_no++) % 13; +# endif + } +# endif /* THREADS */ + +/* Clear some of the inaccessible part of the stack. Returns its */ +/* argument, so it can be used in a tail call position, hence clearing */ +/* another frame. */ + GC_API void * GC_CALL GC_clear_stack(void *arg) + { + ptr_t sp = GC_approx_sp(); /* Hotter than actual sp */ +# ifdef THREADS + word volatile dummy[SMALL_CLEAR_SIZE]; +# endif + +# define SLOP 400 + /* Extra bytes we clear every time. This clears our own */ + /* activation record, and should cause more frequent */ + /* clearing near the cold end of the stack, a good thing. */ + +# define GC_SLOP 4000 + /* We make GC_high_water this much hotter than we really saw */ + /* it, to cover for GC noise etc. above our current frame. */ + +# define CLEAR_THRESHOLD 100000 + /* We restart the clearing process after this many bytes of */ + /* allocation. Otherwise very heavily recursive programs */ + /* with sparse stacks may result in heaps that grow almost */ + /* without bounds. As the heap gets larger, collection */ + /* frequency decreases, thus clearing frequency would decrease, */ + /* thus more junk remains accessible, thus the heap gets */ + /* larger ... */ + +# ifdef THREADS + if (next_random_no() == 0) { + ptr_t limit = sp; + + MAKE_HOTTER(limit, BIG_CLEAR_SIZE*sizeof(word)); + limit = (ptr_t)((word)limit & ~(word)0xf); + /* Make it sufficiently aligned for assembly */ + /* implementations of GC_clear_stack_inner. */ + return GC_clear_stack_inner(arg, limit); + } + BZERO((void *)dummy, SMALL_CLEAR_SIZE*sizeof(word)); +# else + if (GC_gc_no != GC_stack_last_cleared) { + /* Start things over, so we clear the entire stack again. */ + if (EXPECT(NULL == GC_high_water, FALSE)) + GC_high_water = (ptr_t)GC_stackbottom; + GC_min_sp = GC_high_water; + GC_stack_last_cleared = GC_gc_no; + GC_bytes_allocd_at_reset = GC_bytes_allocd; + } + /* Adjust GC_high_water. */ + MAKE_COOLER(GC_high_water, WORDS_TO_BYTES(DEGRADE_RATE) + GC_SLOP); + if ((word)sp HOTTER_THAN (word)GC_high_water) { + GC_high_water = sp; + } + MAKE_HOTTER(GC_high_water, GC_SLOP); + { + ptr_t limit = GC_min_sp; + + MAKE_HOTTER(limit, SLOP); + if ((word)sp COOLER_THAN (word)limit) { + limit = (ptr_t)((word)limit & ~(word)0xf); + /* Make it sufficiently aligned for assembly */ + /* implementations of GC_clear_stack_inner. */ + GC_min_sp = sp; + return GC_clear_stack_inner(arg, limit); + } + } + if (GC_bytes_allocd - GC_bytes_allocd_at_reset > CLEAR_THRESHOLD) { + /* Restart clearing process, but limit how much clearing we do. */ + GC_min_sp = sp; + MAKE_HOTTER(GC_min_sp, CLEAR_THRESHOLD/4); + if ((word)GC_min_sp HOTTER_THAN (word)GC_high_water) + GC_min_sp = GC_high_water; + GC_bytes_allocd_at_reset = GC_bytes_allocd; + } +# endif + return arg; + } + +#endif /* !ALWAYS_SMALL_CLEAR_STACK && !STACK_NOT_SCANNED */ + +/* Return a pointer to the base address of p, given a pointer to a */ +/* an address within an object. Return 0 o.w. */ +GC_API void * GC_CALL GC_base(void * p) +{ + ptr_t r; + struct hblk *h; + bottom_index *bi; + hdr *candidate_hdr; + + r = (ptr_t)p; + if (!EXPECT(GC_is_initialized, TRUE)) return NULL; + h = HBLKPTR(r); + GET_BI(r, bi); + candidate_hdr = HDR_FROM_BI(bi, r); + if (NULL == candidate_hdr) return NULL; + /* If it's a pointer to the middle of a large object, move it */ + /* to the beginning. */ + while (IS_FORWARDING_ADDR_OR_NIL(candidate_hdr)) { + h = FORWARDED_ADDR(h, candidate_hdr); + r = (ptr_t)h; + candidate_hdr = HDR(h); + } + if (HBLK_IS_FREE(candidate_hdr)) return NULL; + /* Make sure r points to the beginning of the object */ + r = (ptr_t)((word)r & ~(word)(WORDS_TO_BYTES(1)-1)); + { + word sz = candidate_hdr -> hb_sz; + ptr_t limit; + + r -= HBLKDISPL(r) % sz; + limit = r + sz; + if (((word)limit > (word)(h + 1) && sz <= HBLKSIZE) + || (word)p >= (word)limit) + return NULL; + } + return (void *)r; +} + +/* Return TRUE if and only if p points to somewhere in GC heap. */ +GC_API int GC_CALL GC_is_heap_ptr(const void *p) +{ + bottom_index *bi; + + GC_ASSERT(GC_is_initialized); + GET_BI(p, bi); + return HDR_FROM_BI(bi, p) != 0; +} + +/* Return the size of an object, given a pointer to its base. */ +/* (For small objects this also happens to work from interior pointers, */ +/* but that shouldn't be relied upon.) */ +GC_API size_t GC_CALL GC_size(const void * p) +{ + hdr * hhdr = HDR((/* no const */ void *)(word)p); + + return (size_t)(hhdr -> hb_sz); +} + + +/* These getters remain unsynchronized for compatibility (since some */ +/* clients could call some of them from a GC callback holding the */ +/* allocator lock). */ +GC_API size_t GC_CALL GC_get_heap_size(void) +{ + /* ignore the memory space returned to OS (i.e. count only the */ + /* space owned by the garbage collector) */ + return (size_t)(GC_heapsize - GC_unmapped_bytes); +} + +GC_API size_t GC_CALL GC_get_obtained_from_os_bytes(void) +{ + return (size_t)GC_our_mem_bytes; +} + +GC_API size_t GC_CALL GC_get_free_bytes(void) +{ + /* ignore the memory space returned to OS */ + return (size_t)(GC_large_free_bytes - GC_unmapped_bytes); +} + +GC_API size_t GC_CALL GC_get_unmapped_bytes(void) +{ + return (size_t)GC_unmapped_bytes; +} + +GC_API size_t GC_CALL GC_get_bytes_since_gc(void) +{ + return (size_t)GC_bytes_allocd; +} + +GC_API size_t GC_CALL GC_get_total_bytes(void) +{ + return (size_t)(GC_bytes_allocd + GC_bytes_allocd_before_gc); +} + +#ifndef GC_GET_HEAP_USAGE_NOT_NEEDED + +GC_API size_t GC_CALL GC_get_size_map_at(int i) +{ + if ((unsigned)i > MAXOBJBYTES) + return GC_SIZE_MAX; + return GRANULES_TO_BYTES(GC_size_map[i]); +} + +/* Return the heap usage information. This is a thread-safe (atomic) */ +/* alternative for the five above getters. NULL pointer is allowed for */ +/* any argument. Returned (filled in) values are of word type. */ +GC_API void GC_CALL GC_get_heap_usage_safe(GC_word *pheap_size, + GC_word *pfree_bytes, GC_word *punmapped_bytes, + GC_word *pbytes_since_gc, GC_word *ptotal_bytes) +{ + READER_LOCK(); + if (pheap_size != NULL) + *pheap_size = GC_heapsize - GC_unmapped_bytes; + if (pfree_bytes != NULL) + *pfree_bytes = GC_large_free_bytes - GC_unmapped_bytes; + if (punmapped_bytes != NULL) + *punmapped_bytes = GC_unmapped_bytes; + if (pbytes_since_gc != NULL) + *pbytes_since_gc = GC_bytes_allocd; + if (ptotal_bytes != NULL) + *ptotal_bytes = GC_bytes_allocd + GC_bytes_allocd_before_gc; + READER_UNLOCK(); +} + + GC_INNER word GC_reclaimed_bytes_before_gc = 0; + + /* Fill in GC statistics provided the destination is of enough size. */ + static void fill_prof_stats(struct GC_prof_stats_s *pstats) + { + pstats->heapsize_full = GC_heapsize; + pstats->free_bytes_full = GC_large_free_bytes; + pstats->unmapped_bytes = GC_unmapped_bytes; + pstats->bytes_allocd_since_gc = GC_bytes_allocd; + pstats->allocd_bytes_before_gc = GC_bytes_allocd_before_gc; + pstats->non_gc_bytes = GC_non_gc_bytes; + pstats->gc_no = GC_gc_no; /* could be -1 */ +# ifdef PARALLEL_MARK + pstats->markers_m1 = (word)((signed_word)GC_markers_m1); +# else + pstats->markers_m1 = 0; /* one marker */ +# endif + pstats->bytes_reclaimed_since_gc = GC_bytes_found > 0 ? + (word)GC_bytes_found : 0; + pstats->reclaimed_bytes_before_gc = GC_reclaimed_bytes_before_gc; + pstats->expl_freed_bytes_since_gc = GC_bytes_freed; /* since gc-7.7 */ + pstats->obtained_from_os_bytes = GC_our_mem_bytes; /* since gc-8.2 */ + } + +# include /* for memset() */ + + GC_API size_t GC_CALL GC_get_prof_stats(struct GC_prof_stats_s *pstats, + size_t stats_sz) + { + struct GC_prof_stats_s stats; + + READER_LOCK(); + fill_prof_stats(stats_sz >= sizeof(stats) ? pstats : &stats); + READER_UNLOCK(); + + if (stats_sz == sizeof(stats)) { + return sizeof(stats); + } else if (stats_sz > sizeof(stats)) { + /* Fill in the remaining part with -1. */ + memset((char *)pstats + sizeof(stats), 0xff, stats_sz - sizeof(stats)); + return sizeof(stats); + } else { + if (EXPECT(stats_sz > 0, TRUE)) + BCOPY(&stats, pstats, stats_sz); + return stats_sz; + } + } + +# ifdef THREADS + /* The _unsafe version assumes the caller holds the allocator lock, */ + /* at least in the reader mode. */ + GC_API size_t GC_CALL GC_get_prof_stats_unsafe( + struct GC_prof_stats_s *pstats, + size_t stats_sz) + { + struct GC_prof_stats_s stats; + + if (stats_sz >= sizeof(stats)) { + fill_prof_stats(pstats); + if (stats_sz > sizeof(stats)) + memset((char *)pstats + sizeof(stats), 0xff, + stats_sz - sizeof(stats)); + return sizeof(stats); + } else { + if (EXPECT(stats_sz > 0, TRUE)) { + fill_prof_stats(&stats); + BCOPY(&stats, pstats, stats_sz); + } + return stats_sz; + } + } +# endif /* THREADS */ + +#endif /* !GC_GET_HEAP_USAGE_NOT_NEEDED */ + +#if defined(THREADS) && !defined(SIGNAL_BASED_STOP_WORLD) + /* GC does not use signals to suspend and restart threads. */ + GC_API void GC_CALL GC_set_suspend_signal(int sig) + { + UNUSED_ARG(sig); + } + + GC_API void GC_CALL GC_set_thr_restart_signal(int sig) + { + UNUSED_ARG(sig); + } + + GC_API int GC_CALL GC_get_suspend_signal(void) + { + return -1; + } + + GC_API int GC_CALL GC_get_thr_restart_signal(void) + { + return -1; + } +#endif /* THREADS && !SIGNAL_BASED_STOP_WORLD */ + +#if !defined(_MAX_PATH) && defined(ANY_MSWIN) +# define _MAX_PATH MAX_PATH +#endif + +#ifdef GC_READ_ENV_FILE + /* This works for Win32/WinCE for now. Really useful only for WinCE. */ + STATIC char *GC_envfile_content = NULL; + /* The content of the GC "env" file with CR and */ + /* LF replaced to '\0'. NULL if the file is */ + /* missing or empty. Otherwise, always ends */ + /* with '\0'. */ + STATIC unsigned GC_envfile_length = 0; + /* Length of GC_envfile_content (if non-NULL). */ + +# ifndef GC_ENVFILE_MAXLEN +# define GC_ENVFILE_MAXLEN 0x4000 +# endif + +# define GC_ENV_FILE_EXT ".gc.env" + + /* The routine initializes GC_envfile_content from the GC "env" file. */ + STATIC void GC_envfile_init(void) + { +# ifdef ANY_MSWIN + HANDLE hFile; + char *content; + unsigned ofs; + unsigned len; + DWORD nBytesRead; + TCHAR path[_MAX_PATH + 0x10]; /* buffer for path + ext */ + size_t bytes_to_get; + + GC_ASSERT(I_HOLD_LOCK()); + len = (unsigned)GetModuleFileName(NULL /* hModule */, path, + _MAX_PATH + 1); + /* If GetModuleFileName() has failed then len is 0. */ + if (len > 4 && path[len - 4] == (TCHAR)'.') { + len -= 4; /* strip executable file extension */ + } + BCOPY(TEXT(GC_ENV_FILE_EXT), &path[len], sizeof(TEXT(GC_ENV_FILE_EXT))); + hFile = CreateFile(path, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL /* lpSecurityAttributes */, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL /* hTemplateFile */); + if (hFile == INVALID_HANDLE_VALUE) + return; /* the file is absent or the operation is failed */ + len = (unsigned)GetFileSize(hFile, NULL); + if (len <= 1 || len >= GC_ENVFILE_MAXLEN) { + CloseHandle(hFile); + return; /* invalid file length - ignoring the file content */ + } + /* At this execution point, GC_setpagesize() and GC_init_win32() */ + /* must already be called (for GET_MEM() to work correctly). */ + GC_ASSERT(GC_page_size != 0); + bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP((size_t)len + 1); + content = GC_os_get_mem(bytes_to_get); + if (content == NULL) { + CloseHandle(hFile); + return; /* allocation failure */ + } + ofs = 0; + nBytesRead = (DWORD)-1L; + /* Last ReadFile() call should clear nBytesRead on success. */ + while (ReadFile(hFile, content + ofs, len - ofs + 1, &nBytesRead, + NULL /* lpOverlapped */) && nBytesRead != 0) { + if ((ofs += nBytesRead) > len) + break; + } + CloseHandle(hFile); + if (ofs != len || nBytesRead != 0) { + /* TODO: recycle content */ + return; /* read operation is failed - ignoring the file content */ + } + content[ofs] = '\0'; + while (ofs-- > 0) { + if (content[ofs] == '\r' || content[ofs] == '\n') + content[ofs] = '\0'; + } + GC_ASSERT(NULL == GC_envfile_content); + GC_envfile_length = len + 1; + GC_envfile_content = content; +# endif + } + + /* This routine scans GC_envfile_content for the specified */ + /* environment variable (and returns its value if found). */ + GC_INNER char * GC_envfile_getenv(const char *name) + { + char *p; + char *end_of_content; + size_t namelen; + +# ifndef NO_GETENV + p = getenv(name); /* try the standard getenv() first */ + if (p != NULL) + return *p != '\0' ? p : NULL; +# endif + p = GC_envfile_content; + if (p == NULL) + return NULL; /* "env" file is absent (or empty) */ + namelen = strlen(name); + if (namelen == 0) /* a sanity check */ + return NULL; + for (end_of_content = p + GC_envfile_length; + p != end_of_content; p += strlen(p) + 1) { + if (strncmp(p, name, namelen) == 0 && *(p += namelen) == '=') { + p++; /* the match is found; skip '=' */ + return *p != '\0' ? p : NULL; + } + /* If not matching then skip to the next line. */ + } + return NULL; /* no match found */ + } +#endif /* GC_READ_ENV_FILE */ + +GC_INNER GC_bool GC_is_initialized = FALSE; + +GC_API int GC_CALL GC_is_init_called(void) +{ + return (int)GC_is_initialized; +} + +#if defined(GC_WIN32_THREADS) \ + && ((defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE)) + GC_INNER CRITICAL_SECTION GC_write_cs; +#endif + +#ifndef DONT_USE_ATEXIT +# if !defined(PCR) && !defined(SMALL_CONFIG) + /* A dedicated variable to avoid a garbage collection on abort. */ + /* GC_find_leak cannot be used for this purpose as otherwise */ + /* TSan finds a data race (between GC_default_on_abort and, e.g., */ + /* GC_finish_collection). */ + static GC_bool skip_gc_atexit = FALSE; +# else +# define skip_gc_atexit FALSE +# endif + + STATIC void GC_exit_check(void) + { + if (GC_find_leak && !skip_gc_atexit) { +# ifdef THREADS + /* Check that the thread executing at-exit functions is */ + /* the same as the one performed the GC initialization, */ + /* otherwise the latter thread might already be dead but */ + /* still registered and this, as a consequence, might */ + /* cause a signal delivery fail when suspending the threads */ + /* on platforms that do not guarantee ESRCH returned if */ + /* the signal is not delivered. */ + /* It should also prevent "Collecting from unknown thread" */ + /* abort in GC_push_all_stacks(). */ + if (!GC_is_main_thread() || !GC_thread_is_registered()) return; +# endif + GC_gcollect(); + } + } +#endif + +#if defined(UNIX_LIKE) && !defined(NO_DEBUGGING) + static void looping_handler(int sig) + { + GC_err_printf("Caught signal %d: looping in handler\n", sig); + for (;;) { + /* empty */ + } + } + + static GC_bool installed_looping_handler = FALSE; + + static void maybe_install_looping_handler(void) + { + /* Install looping handler before the write fault handler, so we */ + /* handle write faults correctly. */ + if (!installed_looping_handler && 0 != GETENV("GC_LOOP_ON_ABORT")) { + GC_set_and_save_fault_handler(looping_handler); + installed_looping_handler = TRUE; + } + } + +#else /* !UNIX_LIKE */ +# define maybe_install_looping_handler() +#endif + +#define GC_DEFAULT_STDERR_FD 2 +#ifdef KOS +# define GC_DEFAULT_STDOUT_FD GC_DEFAULT_STDERR_FD +#else +# define GC_DEFAULT_STDOUT_FD 1 +#endif + +#if !defined(OS2) && !defined(MACOS) && !defined(GC_ANDROID_LOG) \ + && !defined(NN_PLATFORM_CTR) && !defined(NINTENDO_SWITCH) \ + && (!defined(MSWIN32) || defined(CONSOLE_LOG)) && !defined(MSWINCE) + STATIC int GC_stdout = GC_DEFAULT_STDOUT_FD; + STATIC int GC_stderr = GC_DEFAULT_STDERR_FD; + STATIC int GC_log = GC_DEFAULT_STDERR_FD; + +# ifndef MSWIN32 + GC_API void GC_CALL GC_set_log_fd(int fd) + { + GC_log = fd; + } +# endif +#endif + +#ifdef MSGBOX_ON_ERROR + STATIC void GC_win32_MessageBoxA(const char *msg, const char *caption, + unsigned flags) + { +# ifndef DONT_USE_USER32_DLL + /* Use static binding to "user32.dll". */ + (void)MessageBoxA(NULL, msg, caption, flags); +# else + /* This simplifies linking - resolve "MessageBoxA" at run-time. */ + HINSTANCE hU32 = LoadLibrary(TEXT("user32.dll")); + if (hU32) { + FARPROC pfn = GetProcAddress(hU32, "MessageBoxA"); + if (pfn) + (void)(*(int (WINAPI *)(HWND, LPCSTR, LPCSTR, UINT)) + (GC_funcptr_uint)pfn)(NULL /* hWnd */, msg, caption, flags); + (void)FreeLibrary(hU32); + } +# endif + } +#endif /* MSGBOX_ON_ERROR */ + +#if defined(THREADS) && defined(UNIX_LIKE) && !defined(NO_GETCONTEXT) + static void callee_saves_pushed_dummy_fn(ptr_t data, void *context) + { + UNUSED_ARG(data); + UNUSED_ARG(context); + } +#endif + +#ifdef MANUAL_VDB + static GC_bool manual_vdb_allowed = TRUE; +#else + static GC_bool manual_vdb_allowed = FALSE; +#endif + +GC_API void GC_CALL GC_set_manual_vdb_allowed(int value) +{ + manual_vdb_allowed = (GC_bool)value; +} + +GC_API int GC_CALL GC_get_manual_vdb_allowed(void) +{ + return (int)manual_vdb_allowed; +} + +GC_API unsigned GC_CALL GC_get_supported_vdbs(void) +{ +# ifdef GC_DISABLE_INCREMENTAL + return GC_VDB_NONE; +# else + return 0 +# ifndef NO_MANUAL_VDB + | GC_VDB_MANUAL +# endif +# ifdef DEFAULT_VDB + | GC_VDB_DEFAULT +# endif +# ifdef MPROTECT_VDB + | GC_VDB_MPROTECT +# endif +# ifdef GWW_VDB + | GC_VDB_GWW +# endif +# ifdef PCR_VDB + | GC_VDB_PCR +# endif +# ifdef PROC_VDB + | GC_VDB_PROC +# endif +# ifdef SOFT_VDB + | GC_VDB_SOFT +# endif + ; +# endif +} + +#ifndef GC_DISABLE_INCREMENTAL + static void set_incremental_mode_on(void) + { + GC_ASSERT(I_HOLD_LOCK()); +# ifndef NO_MANUAL_VDB + if (manual_vdb_allowed) { + GC_manual_vdb = TRUE; + GC_incremental = TRUE; + } else +# endif + /* else */ { + /* For GWW_VDB on Win32, this needs to happen before any */ + /* heap memory is allocated. */ + GC_incremental = GC_dirty_init(); + } + } +#endif /* !GC_DISABLE_INCREMENTAL */ + +STATIC word GC_parse_mem_size_arg(const char *str) +{ + word result; + char *endptr; + char ch; + + if ('\0' == *str) return GC_WORD_MAX; /* bad value */ + result = (word)STRTOULL(str, &endptr, 10); + ch = *endptr; + if (ch != '\0') { + if (*(endptr + 1) != '\0') return GC_WORD_MAX; + /* Allow k, M or G suffix. */ + switch (ch) { + case 'K': + case 'k': + result <<= 10; + break; +# if CPP_WORDSZ >= 32 + case 'M': + case 'm': + result <<= 20; + break; + case 'G': + case 'g': + result <<= 30; + break; +# endif + default: + result = GC_WORD_MAX; + } + } + return result; +} + +#define GC_LOG_STD_NAME "gc.log" + +GC_API void GC_CALL GC_init(void) +{ + /* LOCK(); -- no longer does anything this early. */ + word initial_heap_sz; + IF_CANCEL(int cancel_state;) + + if (EXPECT(GC_is_initialized, TRUE)) return; +# ifdef REDIRECT_MALLOC + { + static GC_bool init_started = FALSE; + if (init_started) + ABORT("Redirected malloc() called during GC init"); + init_started = TRUE; + } +# endif + +# if defined(GC_INITIAL_HEAP_SIZE) && !defined(CPPCHECK) + initial_heap_sz = GC_INITIAL_HEAP_SIZE; +# else + initial_heap_sz = MINHINCR * HBLKSIZE; +# endif + + DISABLE_CANCEL(cancel_state); + /* Note that although we are nominally called with the allocator */ + /* lock held, now it is only really acquired once a second thread */ + /* is forked. And the initialization code needs to run before */ + /* then. Thus we really don't hold any locks, and can in fact */ + /* safely initialize them here. */ +# ifdef THREADS +# ifndef GC_ALWAYS_MULTITHREADED + GC_ASSERT(!GC_need_to_lock); +# endif +# ifdef SN_TARGET_PS3 + { + pthread_mutexattr_t mattr; + + if (0 != pthread_mutexattr_init(&mattr)) { + ABORT("pthread_mutexattr_init failed"); + } + if (0 != pthread_mutex_init(&GC_allocate_ml, &mattr)) { + ABORT("pthread_mutex_init failed"); + } + (void)pthread_mutexattr_destroy(&mattr); + } +# endif +# endif /* THREADS */ +# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) +# ifndef SPIN_COUNT +# define SPIN_COUNT 4000 +# endif +# ifdef USE_RWLOCK + /* TODO: probably use SRWLOCK_INIT instead */ + InitializeSRWLock(&GC_allocate_ml); +# elif defined(MSWINRT_FLAVOR) + InitializeCriticalSectionAndSpinCount(&GC_allocate_ml, SPIN_COUNT); +# else + { +# ifndef MSWINCE + FARPROC pfn = 0; + HMODULE hK32 = GetModuleHandle(TEXT("kernel32.dll")); + if (hK32) + pfn = GetProcAddress(hK32, + "InitializeCriticalSectionAndSpinCount"); + if (pfn) { + (*(BOOL (WINAPI *)(LPCRITICAL_SECTION, DWORD)) + (GC_funcptr_uint)pfn)(&GC_allocate_ml, SPIN_COUNT); + } else +# endif /* !MSWINCE */ + /* else */ InitializeCriticalSection(&GC_allocate_ml); + + } +# endif +# endif /* GC_WIN32_THREADS && !GC_PTHREADS */ +# if defined(GC_WIN32_THREADS) \ + && ((defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE)) + InitializeCriticalSection(&GC_write_cs); +# endif +# if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED) + LOCK(); /* just to set GC_lock_holder */ +# endif +# ifdef DYNAMIC_POINTER_MASK + if (0 == GC_pointer_mask) GC_pointer_mask = GC_WORD_MAX; +# endif + GC_setpagesize(); +# ifdef MSWIN32 + GC_init_win32(); +# endif +# ifdef GC_READ_ENV_FILE + GC_envfile_init(); +# endif +# if !defined(NO_CLOCK) || !defined(SMALL_CONFIG) +# ifdef GC_PRINT_VERBOSE_STATS + /* This is useful for debugging and profiling on platforms with */ + /* missing getenv() (like WinCE). */ + GC_print_stats = VERBOSE; +# else + if (0 != GETENV("GC_PRINT_VERBOSE_STATS")) { + GC_print_stats = VERBOSE; + } else if (0 != GETENV("GC_PRINT_STATS")) { + GC_print_stats = 1; + } +# endif +# endif +# if ((defined(UNIX_LIKE) && !defined(GC_ANDROID_LOG)) \ + || (defined(CONSOLE_LOG) && defined(MSWIN32)) \ + || defined(CYGWIN32) || defined(SYMBIAN)) && !defined(SMALL_CONFIG) + { + char * file_name = TRUSTED_STRING(GETENV("GC_LOG_FILE")); +# ifdef GC_LOG_TO_FILE_ALWAYS + if (NULL == file_name) + file_name = GC_LOG_STD_NAME; +# else + if (0 != file_name) +# endif + { +# if defined(_MSC_VER) + int log_d = _open(file_name, O_CREAT | O_WRONLY | O_APPEND); +# else + int log_d = open(file_name, O_CREAT | O_WRONLY | O_APPEND, 0644); +# endif + if (log_d < 0) { + GC_err_printf("Failed to open %s as log file\n", file_name); + } else { + char *str; + GC_log = log_d; + str = GETENV("GC_ONLY_LOG_TO_FILE"); +# ifdef GC_ONLY_LOG_TO_FILE + /* The similar environment variable set to "0" */ + /* overrides the effect of the macro defined. */ + if (str != NULL && *str == '0' && *(str + 1) == '\0') +# else + /* Otherwise setting the environment variable */ + /* to anything other than "0" will prevent from */ + /* redirecting stdout/err to the log file. */ + if (str == NULL || (*str == '0' && *(str + 1) == '\0')) +# endif + { + GC_stdout = log_d; + GC_stderr = log_d; + } + } + } + } +# endif +# if !defined(NO_DEBUGGING) && !defined(GC_DUMP_REGULARLY) + if (0 != GETENV("GC_DUMP_REGULARLY")) { + GC_dump_regularly = TRUE; + } +# endif +# ifdef KEEP_BACK_PTRS + { + char * backtraces_string = GETENV("GC_BACKTRACES"); + if (0 != backtraces_string) { + GC_backtraces = atol(backtraces_string); + if (backtraces_string[0] == '\0') GC_backtraces = 1; + } + } +# endif + if (0 != GETENV("GC_FIND_LEAK")) { + GC_find_leak = 1; + } +# ifndef SHORT_DBG_HDRS + if (0 != GETENV("GC_FINDLEAK_DELAY_FREE")) { + GC_findleak_delay_free = TRUE; + } +# endif + if (0 != GETENV("GC_ALL_INTERIOR_POINTERS")) { + GC_all_interior_pointers = 1; + } + if (0 != GETENV("GC_DONT_GC")) { +# if defined(LINT2) \ + && !(defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED)) + GC_disable(); +# else + GC_dont_gc = 1; +# endif + } + if (0 != GETENV("GC_PRINT_BACK_HEIGHT")) { + GC_print_back_height = TRUE; + } + if (0 != GETENV("GC_NO_BLACKLIST_WARNING")) { + GC_large_alloc_warn_interval = LONG_MAX; + } + { + char * addr_string = GETENV("GC_TRACE"); + if (0 != addr_string) { +# ifndef ENABLE_TRACE + WARN("Tracing not enabled: Ignoring GC_TRACE value\n", 0); +# else + word addr = (word)STRTOULL(addr_string, NULL, 16); + if (addr < 0x1000) + WARN("Unlikely trace address: %p\n", (void *)addr); + GC_trace_addr = (ptr_t)addr; +# endif + } + } +# ifdef GC_COLLECT_AT_MALLOC + { + char * string = GETENV("GC_COLLECT_AT_MALLOC"); + if (0 != string) { + size_t min_lb = (size_t)STRTOULL(string, NULL, 10); + if (min_lb > 0) + GC_dbg_collect_at_malloc_min_lb = min_lb; + } + } +# endif +# if !defined(GC_DISABLE_INCREMENTAL) && !defined(NO_CLOCK) + { + char * time_limit_string = GETENV("GC_PAUSE_TIME_TARGET"); + if (0 != time_limit_string) { + long time_limit = atol(time_limit_string); + if (time_limit > 0) { + GC_time_limit = (unsigned long)time_limit; + } + } + } +# endif +# ifndef SMALL_CONFIG + { + char * full_freq_string = GETENV("GC_FULL_FREQUENCY"); + if (full_freq_string != NULL) { + int full_freq = atoi(full_freq_string); + if (full_freq > 0) + GC_full_freq = full_freq; + } + } +# endif + { + char * interval_string = GETENV("GC_LARGE_ALLOC_WARN_INTERVAL"); + if (0 != interval_string) { + long interval = atol(interval_string); + if (interval <= 0) { + WARN("GC_LARGE_ALLOC_WARN_INTERVAL environment variable has" + " bad value - ignoring\n", 0); + } else { + GC_large_alloc_warn_interval = interval; + } + } + } + { + char * space_divisor_string = GETENV("GC_FREE_SPACE_DIVISOR"); + if (space_divisor_string != NULL) { + int space_divisor = atoi(space_divisor_string); + if (space_divisor > 0) + GC_free_space_divisor = (unsigned)space_divisor; + } + } +# ifdef USE_MUNMAP + { + char * string = GETENV("GC_UNMAP_THRESHOLD"); + if (string != NULL) { + if (*string == '0' && *(string + 1) == '\0') { + /* "0" is used to disable unmapping. */ + GC_unmap_threshold = 0; + } else { + int unmap_threshold = atoi(string); + if (unmap_threshold > 0) + GC_unmap_threshold = (unsigned)unmap_threshold; + } + } + } + { + char * string = GETENV("GC_FORCE_UNMAP_ON_GCOLLECT"); + if (string != NULL) { + if (*string == '0' && *(string + 1) == '\0') { + /* "0" is used to turn off the mode. */ + GC_force_unmap_on_gcollect = FALSE; + } else { + GC_force_unmap_on_gcollect = TRUE; + } + } + } + { + char * string = GETENV("GC_USE_ENTIRE_HEAP"); + if (string != NULL) { + if (*string == '0' && *(string + 1) == '\0') { + /* "0" is used to turn off the mode. */ + GC_use_entire_heap = FALSE; + } else { + GC_use_entire_heap = TRUE; + } + } + } +# endif +# if !defined(NO_DEBUGGING) && !defined(NO_CLOCK) + GET_TIME(GC_init_time); +# endif + maybe_install_looping_handler(); +# if ALIGNMENT > GC_DS_TAGS + /* Adjust normal object descriptor for extra allocation. */ + if (EXTRA_BYTES != 0) + GC_obj_kinds[NORMAL].ok_descriptor = + ((~(word)ALIGNMENT) + 1) | GC_DS_LENGTH; +# endif + GC_exclude_static_roots_inner(beginGC_arrays, endGC_arrays); + GC_exclude_static_roots_inner(beginGC_obj_kinds, endGC_obj_kinds); +# ifdef SEPARATE_GLOBALS + GC_exclude_static_roots_inner(beginGC_objfreelist, endGC_objfreelist); + GC_exclude_static_roots_inner(beginGC_aobjfreelist, endGC_aobjfreelist); +# endif +# if defined(USE_PROC_FOR_LIBRARIES) && defined(GC_LINUX_THREADS) + /* TODO: USE_PROC_FOR_LIBRARIES+GC_LINUX_THREADS performs poorly! */ + /* If thread stacks are cached, they tend to be scanned in */ + /* entirety as part of the root set. This will grow them to */ + /* maximum size, and is generally not desirable. */ +# endif +# if !defined(THREADS) || defined(GC_PTHREADS) \ + || defined(NN_PLATFORM_CTR) || defined(NINTENDO_SWITCH) \ + || defined(GC_WIN32_THREADS) || defined(GC_SOLARIS_THREADS) + if (GC_stackbottom == 0) { + GC_stackbottom = GC_get_main_stack_base(); +# if (defined(LINUX) || defined(HPUX)) && defined(IA64) + GC_register_stackbottom = GC_get_register_stack_base(); +# endif + } else { +# if (defined(LINUX) || defined(HPUX)) && defined(IA64) + if (GC_register_stackbottom == 0) { + WARN("GC_register_stackbottom should be set with GC_stackbottom\n", + 0); + /* The following may fail, since we may rely on */ + /* alignment properties that may not hold with a user set */ + /* GC_stackbottom. */ + GC_register_stackbottom = GC_get_register_stack_base(); + } +# endif + } +# endif +# if !defined(CPPCHECK) + GC_STATIC_ASSERT(sizeof(ptr_t) == sizeof(word)); + GC_STATIC_ASSERT(sizeof(signed_word) == sizeof(word)); + GC_STATIC_ASSERT(sizeof(GC_oom_func) == sizeof(GC_funcptr_uint)); +# ifdef FUNCPTR_IS_WORD + GC_STATIC_ASSERT(sizeof(word) == sizeof(GC_funcptr_uint)); +# endif +# if !defined(_AUX_SOURCE) || defined(__GNUC__) + GC_STATIC_ASSERT((word)(-1) > (word)0); + /* word should be unsigned */ +# endif + /* We no longer check for ((void*)(-1) > NULL) since all pointers */ + /* are explicitly cast to word in every less/greater comparison. */ + GC_STATIC_ASSERT((signed_word)(-1) < (signed_word)0); +# endif + GC_STATIC_ASSERT(sizeof(struct hblk) == HBLKSIZE); +# ifndef THREADS + GC_ASSERT(!((word)GC_stackbottom HOTTER_THAN (word)GC_approx_sp())); +# endif + GC_init_headers(); +# ifdef SEARCH_FOR_DATA_START + /* For MPROTECT_VDB, the temporary fault handler should be */ + /* installed first, before the write fault one in GC_dirty_init. */ + if (GC_REGISTER_MAIN_STATIC_DATA()) GC_init_linux_data_start(); +# endif +# ifndef GC_DISABLE_INCREMENTAL + if (GC_incremental || 0 != GETENV("GC_ENABLE_INCREMENTAL")) { + set_incremental_mode_on(); + GC_ASSERT(GC_bytes_allocd == 0); + } +# endif + + /* Add initial guess of root sets. Do this first, since sbrk(0) */ + /* might be used. */ + if (GC_REGISTER_MAIN_STATIC_DATA()) GC_register_data_segments(); + + GC_bl_init(); + GC_mark_init(); + { + char * sz_str = GETENV("GC_INITIAL_HEAP_SIZE"); + if (sz_str != NULL) { + word value = GC_parse_mem_size_arg(sz_str); + if (GC_WORD_MAX == value) { + WARN("Bad initial heap size %s - ignoring\n", sz_str); + } else { + initial_heap_sz = value; + } + } + } + { + char * sz_str = GETENV("GC_MAXIMUM_HEAP_SIZE"); + if (sz_str != NULL) { + word max_heap_sz = GC_parse_mem_size_arg(sz_str); + if (max_heap_sz < initial_heap_sz || GC_WORD_MAX == max_heap_sz) { + WARN("Bad maximum heap size %s - ignoring\n", sz_str); + } else { + if (0 == GC_max_retries) GC_max_retries = 2; + GC_set_max_heap_size(max_heap_sz); + } + } + } + if (initial_heap_sz != 0) { + if (!GC_expand_hp_inner(divHBLKSZ(initial_heap_sz))) { + GC_err_printf("Can't start up: not enough memory\n"); + EXIT(); + } else { + GC_requested_heapsize += initial_heap_sz; + } + } + if (GC_all_interior_pointers) + GC_initialize_offsets(); + GC_register_displacement_inner(0L); +# if defined(GC_LINUX_THREADS) && defined(REDIRECT_MALLOC) + if (!GC_all_interior_pointers) { + /* TLS ABI uses pointer-sized offsets for dtv. */ + GC_register_displacement_inner(sizeof(void *)); + } +# endif + GC_init_size_map(); +# ifdef PCR + if (PCR_IL_Lock(PCR_Bool_false, PCR_allSigsBlocked, PCR_waitForever) + != PCR_ERes_okay) { + ABORT("Can't lock load state"); + } else if (PCR_IL_Unlock() != PCR_ERes_okay) { + ABORT("Can't unlock load state"); + } + PCR_IL_Unlock(); + GC_pcr_install(); +# endif + GC_is_initialized = TRUE; +# ifdef THREADS +# if defined(LINT2) \ + && !(defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED)) + LOCK(); + GC_thr_init(); + UNLOCK(); +# else + GC_thr_init(); +# endif +# endif + COND_DUMP; + /* Get black list set up and/or incremental GC started */ + if (!GC_dont_precollect || GC_incremental) { +# if defined(DYNAMIC_LOADING) && defined(DARWIN) + GC_ASSERT(0 == GC_bytes_allocd); +# endif + GC_gcollect_inner(); + } +# if defined(GC_ASSERTIONS) && defined(GC_ALWAYS_MULTITHREADED) + UNLOCK(); +# endif +# if defined(THREADS) && defined(UNIX_LIKE) && !defined(NO_GETCONTEXT) + /* Ensure getcontext_works is set to avoid potential data race. */ + if (GC_dont_gc || GC_dont_precollect) + GC_with_callee_saves_pushed(callee_saves_pushed_dummy_fn, NULL); +# endif +# ifndef DONT_USE_ATEXIT + if (GC_find_leak) { + /* This is to give us at least one chance to detect leaks. */ + /* This may report some very benign leaks, but ... */ + atexit(GC_exit_check); + } +# endif + + /* The rest of this again assumes we do not really hold */ + /* the allocator lock. */ + +# ifdef THREADS + /* Initialize thread-local allocation. */ + GC_init_parallel(); +# endif + +# if defined(DYNAMIC_LOADING) && defined(DARWIN) + /* This must be called WITHOUT the allocator lock held */ + /* and before any threads are created. */ + GC_init_dyld(); +# endif + RESTORE_CANCEL(cancel_state); + /* It is not safe to allocate any object till completion of GC_init */ + /* (in particular by GC_thr_init), i.e. before GC_init_dyld() call */ + /* and initialization of the incremental mode (if any). */ +# if defined(GWW_VDB) && !defined(KEEP_BACK_PTRS) + GC_ASSERT(GC_bytes_allocd + GC_bytes_allocd_before_gc == 0); +# endif +} + +GC_API void GC_CALL GC_enable_incremental(void) +{ +# if !defined(GC_DISABLE_INCREMENTAL) && !defined(KEEP_BACK_PTRS) + /* If we are keeping back pointers, the GC itself dirties all */ + /* pages on which objects have been marked, making */ + /* incremental GC pointless. */ + if (!GC_find_leak && 0 == GETENV("GC_DISABLE_INCREMENTAL")) { + LOCK(); + if (!GC_incremental) { + GC_setpagesize(); + /* TODO: Should we skip enabling incremental if win32s? */ + maybe_install_looping_handler(); /* Before write fault handler! */ + if (!GC_is_initialized) { + GC_incremental = TRUE; /* indicate intention to turn it on */ + UNLOCK(); + GC_init(); + LOCK(); + } else { + set_incremental_mode_on(); + } + if (GC_incremental && !GC_dont_gc) { + /* Can't easily do it if GC_dont_gc. */ + IF_CANCEL(int cancel_state;) + + DISABLE_CANCEL(cancel_state); + if (GC_bytes_allocd > 0) { + /* There may be unmarked reachable objects. */ + GC_gcollect_inner(); + } else { + /* We are OK in assuming everything is */ + /* clean since nothing can point to an */ + /* unmarked object. */ +# ifdef CHECKSUMS + GC_read_dirty(FALSE); +# else + GC_read_dirty(TRUE); +# endif + } + RESTORE_CANCEL(cancel_state); + } + } + UNLOCK(); + return; + } +# endif + GC_init(); +} + +GC_API void GC_CALL GC_start_mark_threads(void) +{ +# ifdef PARALLEL_MARK + IF_CANCEL(int cancel_state;) + + DISABLE_CANCEL(cancel_state); + LOCK(); + GC_start_mark_threads_inner(); + UNLOCK(); + RESTORE_CANCEL(cancel_state); +# else + /* No action since parallel markers are disabled (or no POSIX fork). */ + GC_ASSERT(I_DONT_HOLD_LOCK()); +# endif +} + + GC_API void GC_CALL GC_deinit(void) + { + if (GC_is_initialized) { + /* Prevent duplicate resource close. */ + GC_is_initialized = FALSE; + GC_bytes_allocd = 0; + GC_bytes_allocd_before_gc = 0; +# if defined(GC_WIN32_THREADS) && (defined(MSWIN32) || defined(MSWINCE)) +# if !defined(CONSOLE_LOG) || defined(MSWINCE) + DeleteCriticalSection(&GC_write_cs); +# endif +# if !defined(GC_PTHREADS) && !defined(USE_RWLOCK) + DeleteCriticalSection(&GC_allocate_ml); +# endif +# endif + } + } + +#if (defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE) + + STATIC HANDLE GC_log = 0; + +# ifdef THREADS +# if defined(PARALLEL_MARK) && !defined(GC_ALWAYS_MULTITHREADED) +# define IF_NEED_TO_LOCK(x) if (GC_parallel || GC_need_to_lock) x +# else +# define IF_NEED_TO_LOCK(x) if (GC_need_to_lock) x +# endif +# else +# define IF_NEED_TO_LOCK(x) +# endif /* !THREADS */ + +# ifdef MSWINRT_FLAVOR +# include + + /* This API is defined in roapi.h, but we cannot include it here */ + /* since it does not compile in C. */ + DECLSPEC_IMPORT HRESULT WINAPI RoGetActivationFactory( + HSTRING activatableClassId, + REFIID iid, void** factory); + + static GC_bool getWinRTLogPath(wchar_t* buf, size_t bufLen) + { + static const GUID kIID_IApplicationDataStatics = { + 0x5612147B, 0xE843, 0x45E3, + 0x94, 0xD8, 0x06, 0x16, 0x9E, 0x3C, 0x8E, 0x17 + }; + static const GUID kIID_IStorageItem = { + 0x4207A996, 0xCA2F, 0x42F7, + 0xBD, 0xE8, 0x8B, 0x10, 0x45, 0x7A, 0x7F, 0x30 + }; + GC_bool result = FALSE; + HSTRING_HEADER appDataClassNameHeader; + HSTRING appDataClassName; + __x_ABI_CWindows_CStorage_CIApplicationDataStatics* appDataStatics = 0; + + GC_ASSERT(bufLen > 0); + if (SUCCEEDED(WindowsCreateStringReference( + RuntimeClass_Windows_Storage_ApplicationData, + (sizeof(RuntimeClass_Windows_Storage_ApplicationData)-1) + / sizeof(wchar_t), + &appDataClassNameHeader, &appDataClassName)) + && SUCCEEDED(RoGetActivationFactory(appDataClassName, + &kIID_IApplicationDataStatics, + &appDataStatics))) { + __x_ABI_CWindows_CStorage_CIApplicationData* appData = NULL; + __x_ABI_CWindows_CStorage_CIStorageFolder* tempFolder = NULL; + __x_ABI_CWindows_CStorage_CIStorageItem* tempFolderItem = NULL; + HSTRING tempPath = NULL; + + if (SUCCEEDED(appDataStatics->lpVtbl->get_Current(appDataStatics, + &appData)) + && SUCCEEDED(appData->lpVtbl->get_TemporaryFolder(appData, + &tempFolder)) + && SUCCEEDED(tempFolder->lpVtbl->QueryInterface(tempFolder, + &kIID_IStorageItem, + &tempFolderItem)) + && SUCCEEDED(tempFolderItem->lpVtbl->get_Path(tempFolderItem, + &tempPath))) { + UINT32 tempPathLen; + const wchar_t* tempPathBuf = + WindowsGetStringRawBuffer(tempPath, &tempPathLen); + + buf[0] = '\0'; + if (wcsncat_s(buf, bufLen, tempPathBuf, tempPathLen) == 0 + && wcscat_s(buf, bufLen, L"\\") == 0 + && wcscat_s(buf, bufLen, TEXT(GC_LOG_STD_NAME)) == 0) + result = TRUE; + WindowsDeleteString(tempPath); + } + + if (tempFolderItem != NULL) + tempFolderItem->lpVtbl->Release(tempFolderItem); + if (tempFolder != NULL) + tempFolder->lpVtbl->Release(tempFolder); + if (appData != NULL) + appData->lpVtbl->Release(appData); + appDataStatics->lpVtbl->Release(appDataStatics); + } + return result; + } +# endif /* MSWINRT_FLAVOR */ + + STATIC HANDLE GC_CreateLogFile(void) + { + HANDLE hFile; +# ifdef MSWINRT_FLAVOR + TCHAR pathBuf[_MAX_PATH + 0x10]; /* buffer for path + ext */ + + hFile = INVALID_HANDLE_VALUE; + if (getWinRTLogPath(pathBuf, _MAX_PATH + 1)) { + CREATEFILE2_EXTENDED_PARAMETERS extParams; + + BZERO(&extParams, sizeof(extParams)); + extParams.dwSize = sizeof(extParams); + extParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + extParams.dwFileFlags = GC_print_stats == VERBOSE ? 0 + : FILE_FLAG_WRITE_THROUGH; + hFile = CreateFile2(pathBuf, GENERIC_WRITE, FILE_SHARE_READ, + CREATE_ALWAYS, &extParams); + } + +# else + TCHAR *logPath; +# if defined(NO_GETENV_WIN32) && defined(CPPCHECK) +# define appendToFile FALSE +# else + BOOL appendToFile = FALSE; +# endif +# if !defined(NO_GETENV_WIN32) || !defined(OLD_WIN32_LOG_FILE) + TCHAR pathBuf[_MAX_PATH + 0x10]; /* buffer for path + ext */ + + logPath = pathBuf; +# endif + + /* Use GetEnvironmentVariable instead of GETENV() for unicode support. */ +# ifndef NO_GETENV_WIN32 + if (GetEnvironmentVariable(TEXT("GC_LOG_FILE"), pathBuf, + _MAX_PATH + 1) - 1U < (DWORD)_MAX_PATH) { + appendToFile = TRUE; + } else +# endif + /* else */ { + /* Env var not found or its value too long. */ +# ifdef OLD_WIN32_LOG_FILE + logPath = TEXT(GC_LOG_STD_NAME); +# else + int len = (int)GetModuleFileName(NULL /* hModule */, pathBuf, + _MAX_PATH + 1); + /* If GetModuleFileName() has failed then len is 0. */ + if (len > 4 && pathBuf[len - 4] == (TCHAR)'.') { + len -= 4; /* strip executable file extension */ + } + BCOPY(TEXT(".") TEXT(GC_LOG_STD_NAME), &pathBuf[len], + sizeof(TEXT(".") TEXT(GC_LOG_STD_NAME))); +# endif + } + + hFile = CreateFile(logPath, GENERIC_WRITE, FILE_SHARE_READ, + NULL /* lpSecurityAttributes */, + appendToFile ? OPEN_ALWAYS : CREATE_ALWAYS, + GC_print_stats == VERBOSE ? FILE_ATTRIBUTE_NORMAL : + /* immediately flush writes unless very verbose */ + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, + NULL /* hTemplateFile */); + +# ifndef NO_GETENV_WIN32 + if (appendToFile && hFile != INVALID_HANDLE_VALUE) { + LONG posHigh = 0; + (void)SetFilePointer(hFile, 0, &posHigh, FILE_END); + /* Seek to file end (ignoring any error) */ + } +# endif +# undef appendToFile +# endif + return hFile; + } + + STATIC int GC_write(const char *buf, size_t len) + { + BOOL res; + DWORD written; +# if defined(THREADS) && defined(GC_ASSERTIONS) + static GC_bool inside_write = FALSE; + /* to prevent infinite recursion at abort. */ + if (inside_write) + return -1; +# endif + + if (len == 0) + return 0; + IF_NEED_TO_LOCK(EnterCriticalSection(&GC_write_cs)); +# if defined(THREADS) && defined(GC_ASSERTIONS) + if (GC_write_disabled) { + inside_write = TRUE; + ABORT("Assertion failure: GC_write called with write_disabled"); + } +# endif + if (GC_log == 0) { + GC_log = GC_CreateLogFile(); + } + if (GC_log == INVALID_HANDLE_VALUE) { + IF_NEED_TO_LOCK(LeaveCriticalSection(&GC_write_cs)); +# ifdef NO_DEBUGGING + /* Ignore open log failure (e.g., it might be caused by */ + /* read-only folder of the client application). */ + return 0; +# else + return -1; +# endif + } + res = WriteFile(GC_log, buf, (DWORD)len, &written, NULL); +# if defined(_MSC_VER) && defined(_DEBUG) && !defined(NO_CRT) +# ifdef MSWINCE + /* There is no CrtDbgReport() in WinCE */ + { + WCHAR wbuf[1024]; + /* Always use Unicode variant of OutputDebugString() */ + wbuf[MultiByteToWideChar(CP_ACP, 0 /* dwFlags */, + buf, len, wbuf, + sizeof(wbuf) / sizeof(wbuf[0]) - 1)] = 0; + OutputDebugStringW(wbuf); + } +# else + _CrtDbgReport(_CRT_WARN, NULL, 0, NULL, "%.*s", len, buf); +# endif +# endif + IF_NEED_TO_LOCK(LeaveCriticalSection(&GC_write_cs)); + return res ? (int)written : -1; + } + + /* TODO: This is pretty ugly ... */ +# define WRITE(f, buf, len) GC_write(buf, len) + +#elif defined(OS2) || defined(MACOS) + STATIC FILE * GC_stdout = NULL; + STATIC FILE * GC_stderr = NULL; + STATIC FILE * GC_log = NULL; + + /* Initialize GC_log (and the friends) passed to GC_write(). */ + STATIC void GC_set_files(void) + { + if (GC_stdout == NULL) { + GC_stdout = stdout; + } + if (GC_stderr == NULL) { + GC_stderr = stderr; + } + if (GC_log == NULL) { + GC_log = stderr; + } + } + + GC_INLINE int GC_write(FILE *f, const char *buf, size_t len) + { + int res = fwrite(buf, 1, len, f); + fflush(f); + return res; + } + +# define WRITE(f, buf, len) (GC_set_files(), GC_write(f, buf, len)) + +#elif defined(GC_ANDROID_LOG) + +# include + +# ifndef GC_ANDROID_LOG_TAG +# define GC_ANDROID_LOG_TAG "BDWGC" +# endif + +# define GC_stdout ANDROID_LOG_DEBUG +# define GC_stderr ANDROID_LOG_ERROR +# define GC_log GC_stdout + +# define WRITE(level, buf, unused_len) \ + __android_log_write(level, GC_ANDROID_LOG_TAG, buf) + +#elif defined(NN_PLATFORM_CTR) + int n3ds_log_write(const char* text, int length); +# define WRITE(level, buf, len) n3ds_log_write(buf, len) + +#elif defined(NINTENDO_SWITCH) + int switch_log_write(const char* text, int length); +# define WRITE(level, buf, len) switch_log_write(buf, len) + +#else + +# if !defined(ECOS) && !defined(NOSYS) && !defined(PLATFORM_WRITE) \ + && !defined(SN_TARGET_PSP2) +# include +# endif + + STATIC int GC_write(int fd, const char *buf, size_t len) + { +# if defined(ECOS) || defined(PLATFORM_WRITE) || defined(SN_TARGET_PSP2) \ + || defined(NOSYS) +# ifdef ECOS + /* FIXME: This seems to be defined nowhere at present. */ + /* _Jv_diag_write(buf, len); */ +# else + /* No writing. */ +# endif + return (int)len; +# else + int bytes_written = 0; + IF_CANCEL(int cancel_state;) + + DISABLE_CANCEL(cancel_state); + while ((unsigned)bytes_written < len) { +# ifdef GC_SOLARIS_THREADS + int result = syscall(SYS_write, fd, buf + bytes_written, + len - bytes_written); +# elif defined(_MSC_VER) + int result = _write(fd, buf + bytes_written, + (unsigned)(len - bytes_written)); +# else + int result = (int)write(fd, buf + bytes_written, + len - (size_t)bytes_written); +# endif + + if (-1 == result) { + if (EAGAIN == errno) /* Resource temporarily unavailable */ + continue; + RESTORE_CANCEL(cancel_state); + return result; + } + bytes_written += result; + } + RESTORE_CANCEL(cancel_state); + return bytes_written; +# endif + } + +# define WRITE(f, buf, len) GC_write(f, buf, len) +#endif /* !MSWINCE && !OS2 && !MACOS && !GC_ANDROID_LOG */ + +#define BUFSZ 1024 + +#if defined(DJGPP) || defined(__STRICT_ANSI__) + /* vsnprintf is missing in DJGPP (v2.0.3) */ +# define GC_VSNPRINTF(buf, bufsz, format, args) vsprintf(buf, format, args) +#elif defined(_MSC_VER) +# ifdef MSWINCE + /* _vsnprintf is deprecated in WinCE */ +# define GC_VSNPRINTF StringCchVPrintfA +# else +# define GC_VSNPRINTF _vsnprintf +# endif +#else +# define GC_VSNPRINTF vsnprintf +#endif + +/* A version of printf that is unlikely to call malloc, and is thus safer */ +/* to call from the collector in case malloc has been bound to GC_malloc. */ +/* Floating point arguments and formats should be avoided, since FP */ +/* conversion is more likely to allocate memory. */ +/* Assumes that no more than BUFSZ-1 characters are written at once. */ +#define GC_PRINTF_FILLBUF(buf, format) \ + do { \ + va_list args; \ + va_start(args, format); \ + (buf)[sizeof(buf) - 1] = 0x15; /* guard */ \ + (void)GC_VSNPRINTF(buf, sizeof(buf) - 1, format, args); \ + va_end(args); \ + if ((buf)[sizeof(buf) - 1] != 0x15) \ + ABORT("GC_printf clobbered stack"); \ + } while (0) + +void GC_printf(const char *format, ...) +{ + if (!GC_quiet) { + char buf[BUFSZ + 1]; + + GC_PRINTF_FILLBUF(buf, format); +# ifdef NACL + (void)WRITE(GC_stdout, buf, strlen(buf)); + /* Ignore errors silently. */ +# else + if (WRITE(GC_stdout, buf, strlen(buf)) < 0 +# if defined(CYGWIN32) || (defined(CONSOLE_LOG) && defined(MSWIN32)) + && GC_stdout != GC_DEFAULT_STDOUT_FD +# endif + ) { + ABORT("write to stdout failed"); + } +# endif + } +} + +void GC_err_printf(const char *format, ...) +{ + char buf[BUFSZ + 1]; + + GC_PRINTF_FILLBUF(buf, format); + GC_err_puts(buf); +} + +void GC_log_printf(const char *format, ...) +{ + char buf[BUFSZ + 1]; + + GC_PRINTF_FILLBUF(buf, format); +# ifdef NACL + (void)WRITE(GC_log, buf, strlen(buf)); +# else + if (WRITE(GC_log, buf, strlen(buf)) < 0 +# if defined(CYGWIN32) || (defined(CONSOLE_LOG) && defined(MSWIN32)) + && GC_log != GC_DEFAULT_STDERR_FD +# endif + ) { + ABORT("write to GC log failed"); + } +# endif +} + +#ifndef GC_ANDROID_LOG + +# define GC_warn_printf GC_err_printf + +#else + + GC_INNER void GC_info_log_printf(const char *format, ...) + { + char buf[BUFSZ + 1]; + + GC_PRINTF_FILLBUF(buf, format); + (void)WRITE(ANDROID_LOG_INFO, buf, 0 /* unused */); + } + + GC_INNER void GC_verbose_log_printf(const char *format, ...) + { + char buf[BUFSZ + 1]; + + GC_PRINTF_FILLBUF(buf, format); + (void)WRITE(ANDROID_LOG_VERBOSE, buf, 0); /* ignore write errors */ + } + + STATIC void GC_warn_printf(const char *format, ...) + { + char buf[BUFSZ + 1]; + + GC_PRINTF_FILLBUF(buf, format); + (void)WRITE(ANDROID_LOG_WARN, buf, 0); + } + +#endif /* GC_ANDROID_LOG */ + +void GC_err_puts(const char *s) +{ + (void)WRITE(GC_stderr, s, strlen(s)); /* ignore errors */ +} + +STATIC void GC_CALLBACK GC_default_warn_proc(char *msg, GC_word arg) +{ + /* TODO: Add assertion on arg comply with msg (format). */ + GC_warn_printf(msg, arg); +} + +GC_INNER GC_warn_proc GC_current_warn_proc = GC_default_warn_proc; + +/* This is recommended for production code (release). */ +GC_API void GC_CALLBACK GC_ignore_warn_proc(char *msg, GC_word arg) +{ + if (GC_print_stats) { + /* Don't ignore warnings if stats printing is on. */ + GC_default_warn_proc(msg, arg); + } +} + +GC_API void GC_CALL GC_set_warn_proc(GC_warn_proc p) +{ + GC_ASSERT(NONNULL_ARG_NOT_NULL(p)); +# ifdef GC_WIN32_THREADS +# ifdef CYGWIN32 + /* Need explicit GC_INIT call */ + GC_ASSERT(GC_is_initialized); +# else + if (!GC_is_initialized) GC_init(); +# endif +# endif + LOCK(); + GC_current_warn_proc = p; + UNLOCK(); +} + +GC_API GC_warn_proc GC_CALL GC_get_warn_proc(void) +{ + GC_warn_proc result; + + READER_LOCK(); + result = GC_current_warn_proc; + READER_UNLOCK(); + return result; +} + +/* Print (or display) a message before abnormal exit (including */ +/* abort). Invoked from ABORT(msg) macro (there msg is non-NULL) */ +/* and from EXIT() macro (msg is NULL in that case). */ +STATIC void GC_CALLBACK GC_default_on_abort(const char *msg) +{ +# if !defined(PCR) && !defined(SMALL_CONFIG) +# ifndef DONT_USE_ATEXIT + skip_gc_atexit = TRUE; /* disable at-exit GC_gcollect() */ +# endif + + if (msg != NULL) { +# ifdef MSGBOX_ON_ERROR + GC_win32_MessageBoxA(msg, "Fatal error in GC", MB_ICONERROR | MB_OK); + /* Also duplicate msg to GC log file. */ +# endif + +# ifndef GC_ANDROID_LOG + /* Avoid calling GC_err_printf() here, as GC_on_abort() could be */ + /* called from it. Note 1: this is not an atomic output. */ + /* Note 2: possible write errors are ignored. */ +# if defined(GC_WIN32_THREADS) && defined(GC_ASSERTIONS) \ + && ((defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE)) + if (!GC_write_disabled) +# endif + { + if (WRITE(GC_stderr, msg, strlen(msg)) >= 0) + (void)WRITE(GC_stderr, "\n", 1); + } +# else + __android_log_assert("*" /* cond */, GC_ANDROID_LOG_TAG, "%s\n", msg); +# endif + } + +# if !defined(NO_DEBUGGING) && !defined(GC_ANDROID_LOG) + if (GETENV("GC_LOOP_ON_ABORT") != NULL) { + /* In many cases it's easier to debug a running process. */ + /* It's arguably nicer to sleep, but that makes it harder */ + /* to look at the thread if the debugger doesn't know much */ + /* about threads. */ + for(;;) { + /* Empty */ + } + } +# endif +# else + UNUSED_ARG(msg); +# endif +} + +#if !defined(PCR) && !defined(SMALL_CONFIG) + GC_abort_func GC_on_abort = GC_default_on_abort; +#endif + +GC_API void GC_CALL GC_set_abort_func(GC_abort_func fn) +{ + GC_ASSERT(NONNULL_ARG_NOT_NULL(fn)); + LOCK(); +# if !defined(PCR) && !defined(SMALL_CONFIG) + GC_on_abort = fn; +# else + UNUSED_ARG(fn); +# endif + UNLOCK(); +} + +GC_API GC_abort_func GC_CALL GC_get_abort_func(void) +{ + GC_abort_func fn; + + READER_LOCK(); +# if !defined(PCR) && !defined(SMALL_CONFIG) + fn = GC_on_abort; + GC_ASSERT(fn != 0); +# else + fn = GC_default_on_abort; +# endif + READER_UNLOCK(); + return fn; +} + +GC_API void GC_CALL GC_enable(void) +{ + LOCK(); + GC_ASSERT(GC_dont_gc != 0); /* ensure no counter underflow */ + GC_dont_gc--; + if (!GC_dont_gc && GC_heapsize > GC_heapsize_on_gc_disable) + WARN("Heap grown by %" WARN_PRIuPTR " KiB while GC was disabled\n", + (GC_heapsize - GC_heapsize_on_gc_disable) >> 10); + UNLOCK(); +} + +GC_API void GC_CALL GC_disable(void) +{ + LOCK(); + if (!GC_dont_gc) + GC_heapsize_on_gc_disable = GC_heapsize; + GC_dont_gc++; + UNLOCK(); +} + +GC_API int GC_CALL GC_is_disabled(void) +{ + return GC_dont_gc != 0; +} + +/* Helper procedures for new kind creation. */ +GC_API void ** GC_CALL GC_new_free_list_inner(void) +{ + void *result; + + GC_ASSERT(I_HOLD_LOCK()); + result = GC_INTERNAL_MALLOC((MAXOBJGRANULES+1) * sizeof(ptr_t), PTRFREE); + if (NULL == result) ABORT("Failed to allocate freelist for new kind"); + BZERO(result, (MAXOBJGRANULES+1)*sizeof(ptr_t)); + return (void **)result; +} + +GC_API void ** GC_CALL GC_new_free_list(void) +{ + void ** result; + + LOCK(); + result = GC_new_free_list_inner(); + UNLOCK(); + return result; +} + +GC_API unsigned GC_CALL GC_new_kind_inner(void **fl, GC_word descr, + int adjust, int clear) +{ + unsigned result = GC_n_kinds; + + GC_ASSERT(NONNULL_ARG_NOT_NULL(fl)); + GC_ASSERT(adjust == FALSE || adjust == TRUE); + /* If an object is not needed to be cleared (when moved to the */ + /* free list) then its descriptor should be zero to denote */ + /* a pointer-free object (and, as a consequence, the size of the */ + /* object should not be added to the descriptor template). */ + GC_ASSERT(clear == TRUE + || (descr == 0 && adjust == FALSE && clear == FALSE)); + if (result < MAXOBJKINDS) { + GC_ASSERT(result > 0); + GC_n_kinds++; + GC_obj_kinds[result].ok_freelist = fl; + GC_obj_kinds[result].ok_reclaim_list = 0; + GC_obj_kinds[result].ok_descriptor = descr; + GC_obj_kinds[result].ok_relocate_descr = adjust; + GC_obj_kinds[result].ok_init = (GC_bool)clear; +# ifdef ENABLE_DISCLAIM + GC_obj_kinds[result].ok_mark_unconditionally = FALSE; + GC_obj_kinds[result].ok_disclaim_proc = 0; +# endif + } else { + ABORT("Too many kinds"); + } + return result; +} + +GC_API unsigned GC_CALL GC_new_kind(void **fl, GC_word descr, int adjust, + int clear) +{ + unsigned result; + + LOCK(); + result = GC_new_kind_inner(fl, descr, adjust, clear); + UNLOCK(); + return result; +} + +GC_API unsigned GC_CALL GC_new_proc_inner(GC_mark_proc proc) +{ + unsigned result = GC_n_mark_procs; + + if (result < MAX_MARK_PROCS) { + GC_n_mark_procs++; + GC_mark_procs[result] = proc; + } else { + ABORT("Too many mark procedures"); + } + return result; +} + +GC_API unsigned GC_CALL GC_new_proc(GC_mark_proc proc) +{ + unsigned result; + + LOCK(); + result = GC_new_proc_inner(proc); + UNLOCK(); + return result; +} + +GC_API void * GC_CALL GC_call_with_alloc_lock(GC_fn_type fn, void *client_data) +{ + void * result; + + LOCK(); + result = fn(client_data); + UNLOCK(); + return result; +} + +#ifdef THREADS + GC_API void GC_CALL GC_alloc_lock(void) + { + LOCK(); + } + + GC_API void GC_CALL GC_alloc_unlock(void) + { + UNLOCK(); + } + + GC_API void *GC_CALL GC_call_with_reader_lock(GC_fn_type fn, + void *client_data, + int release) + { + void *result; + + READER_LOCK(); + result = fn(client_data); +# ifdef HAS_REAL_READER_LOCK + if (release) { + READER_UNLOCK_RELEASE(); +# ifdef LINT2 + GC_noop1((unsigned)release); +# endif + return result; + } +# else + UNUSED_ARG(release); +# endif + READER_UNLOCK(); + return result; + } +#endif /* THREADS */ + +GC_API void * GC_CALL GC_call_with_stack_base(GC_stack_base_func fn, void *arg) +{ + struct GC_stack_base base; + void *result; + + base.mem_base = (void *)&base; +# ifdef IA64 + base.reg_base = (void *)GC_save_regs_in_stack(); + /* TODO: Unnecessarily flushes register stack, */ + /* but that probably doesn't hurt. */ +# elif defined(E2K) + { + unsigned long long sz_ull; + + GET_PROCEDURE_STACK_SIZE_INNER(&sz_ull); + base.reg_base = (void *)(word)sz_ull; + } +# endif + result = (*(GC_stack_base_func volatile *)&fn)(&base, arg); + /* Strongly discourage the compiler from treating the above */ + /* as a tail call. */ + GC_noop1(COVERT_DATAFLOW(&base)); + return result; +} + +#ifndef THREADS + +GC_INNER ptr_t GC_blocked_sp = NULL; + /* NULL value means we are not inside GC_do_blocking() call. */ +# ifdef IA64 + STATIC ptr_t GC_blocked_register_sp = NULL; +# endif + +GC_INNER struct GC_traced_stack_sect_s *GC_traced_stack_sect = NULL; + +/* This is nearly the same as in pthread_support.c. */ +GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, void *client_data) +{ + struct GC_traced_stack_sect_s stacksect; + GC_ASSERT(GC_is_initialized); + + /* Adjust our stack bottom pointer (this could happen if */ + /* GC_get_main_stack_base() is unimplemented or broken for */ + /* the platform). */ + if ((word)GC_stackbottom HOTTER_THAN (word)(&stacksect)) + GC_stackbottom = (ptr_t)COVERT_DATAFLOW(&stacksect); + + if (GC_blocked_sp == NULL) { + /* We are not inside GC_do_blocking() - do nothing more. */ + client_data = (*(GC_fn_type volatile *)&fn)(client_data); + /* Prevent treating the above as a tail call. */ + GC_noop1(COVERT_DATAFLOW(&stacksect)); + return client_data; /* result */ + } + + /* Setup new "stack section". */ + stacksect.saved_stack_ptr = GC_blocked_sp; +# ifdef IA64 + /* This is the same as in GC_call_with_stack_base(). */ + stacksect.backing_store_end = GC_save_regs_in_stack(); + /* Unnecessarily flushes register stack, */ + /* but that probably doesn't hurt. */ + stacksect.saved_backing_store_ptr = GC_blocked_register_sp; +# endif + stacksect.prev = GC_traced_stack_sect; + GC_blocked_sp = NULL; + GC_traced_stack_sect = &stacksect; + + client_data = (*(GC_fn_type volatile *)&fn)(client_data); + GC_ASSERT(GC_blocked_sp == NULL); + GC_ASSERT(GC_traced_stack_sect == &stacksect); + +# if defined(CPPCHECK) + GC_noop1((word)GC_traced_stack_sect - (word)GC_blocked_sp); +# endif + /* Restore original "stack section". */ + GC_traced_stack_sect = stacksect.prev; +# ifdef IA64 + GC_blocked_register_sp = stacksect.saved_backing_store_ptr; +# endif + GC_blocked_sp = stacksect.saved_stack_ptr; + + return client_data; /* result */ +} + +/* This is nearly the same as in pthread_support.c. */ +STATIC void GC_do_blocking_inner(ptr_t data, void *context) +{ + struct blocking_data * d = (struct blocking_data *)data; + + UNUSED_ARG(context); + GC_ASSERT(GC_is_initialized); + GC_ASSERT(GC_blocked_sp == NULL); +# ifdef SPARC + GC_blocked_sp = GC_save_regs_in_stack(); +# else + GC_blocked_sp = (ptr_t) &d; /* save approx. sp */ +# ifdef IA64 + GC_blocked_register_sp = GC_save_regs_in_stack(); +# endif +# endif + + d -> client_data = (d -> fn)(d -> client_data); + +# ifdef SPARC + GC_ASSERT(GC_blocked_sp != NULL); +# else + GC_ASSERT(GC_blocked_sp == (ptr_t)(&d)); +# endif +# if defined(CPPCHECK) + GC_noop1((word)GC_blocked_sp); +# endif + GC_blocked_sp = NULL; +} + + GC_API void GC_CALL GC_set_stackbottom(void *gc_thread_handle, + const struct GC_stack_base *sb) + { + GC_ASSERT(sb -> mem_base != NULL); + GC_ASSERT(NULL == gc_thread_handle || &GC_stackbottom == gc_thread_handle); + GC_ASSERT(NULL == GC_blocked_sp + && NULL == GC_traced_stack_sect); /* for now */ + UNUSED_ARG(gc_thread_handle); + + GC_stackbottom = (char *)(sb -> mem_base); +# ifdef IA64 + GC_register_stackbottom = (ptr_t)(sb -> reg_base); +# endif + } + + GC_API void * GC_CALL GC_get_my_stackbottom(struct GC_stack_base *sb) + { + GC_ASSERT(GC_is_initialized); + sb -> mem_base = GC_stackbottom; +# ifdef IA64 + sb -> reg_base = GC_register_stackbottom; +# elif defined(E2K) + sb -> reg_base = NULL; +# endif + return &GC_stackbottom; /* gc_thread_handle */ + } + +#endif /* !THREADS */ + +GC_API void * GC_CALL GC_do_blocking(GC_fn_type fn, void * client_data) +{ + struct blocking_data my_data; + + my_data.fn = fn; + my_data.client_data = client_data; + GC_with_callee_saves_pushed(GC_do_blocking_inner, (ptr_t)(&my_data)); + return my_data.client_data; /* result */ +} + +#if !defined(NO_DEBUGGING) + GC_API void GC_CALL GC_dump(void) + { + READER_LOCK(); + GC_dump_named(NULL); + READER_UNLOCK(); + } + + GC_API void GC_CALL GC_dump_named(const char *name) + { +# ifndef NO_CLOCK + CLOCK_TYPE current_time; + + GET_TIME(current_time); +# endif + if (name != NULL) { + GC_printf("\n***GC Dump %s\n", name); + } else { + GC_printf("\n***GC Dump collection #%lu\n", (unsigned long)GC_gc_no); + } +# ifndef NO_CLOCK + /* Note that the time is wrapped in ~49 days if sizeof(long)==4. */ + GC_printf("Time since GC init: %lu ms\n", + MS_TIME_DIFF(current_time, GC_init_time)); +# endif + + GC_printf("\n***Static roots:\n"); + GC_print_static_roots(); + GC_printf("\n***Heap sections:\n"); + GC_print_heap_sects(); + GC_printf("\n***Free blocks:\n"); + GC_print_hblkfreelist(); + GC_printf("\n***Blocks in use:\n"); + GC_print_block_list(); +# ifndef GC_NO_FINALIZATION + GC_dump_finalization(); +# endif + } +#endif /* !NO_DEBUGGING */ + +static void GC_CALLBACK block_add_size(struct hblk *h, GC_word pbytes) +{ + hdr *hhdr = HDR(h); + *(word *)pbytes += (WORDS_TO_BYTES(hhdr->hb_sz) + HBLKSIZE-1) + & ~(word)(HBLKSIZE-1); +} + +GC_API size_t GC_CALL GC_get_memory_use(void) +{ + word bytes = 0; + + READER_LOCK(); + GC_apply_to_all_blocks(block_add_size, (word)(&bytes)); + READER_UNLOCK(); + return (size_t)bytes; +} + +/* Getter functions for the public Read-only variables. */ + +GC_API GC_word GC_CALL GC_get_gc_no(void) +{ + return GC_gc_no; +} + +#ifndef PARALLEL_MARK + GC_API void GC_CALL GC_set_markers_count(unsigned markers) + { + UNUSED_ARG(markers); + } +#endif + +GC_API int GC_CALL GC_get_parallel(void) +{ +# ifdef THREADS + return GC_parallel; +# else + return 0; +# endif +} + +/* Setter and getter functions for the public R/W function variables. */ +/* These functions are synchronized (like GC_set_warn_proc() and */ +/* GC_get_warn_proc()). */ + +GC_API void GC_CALL GC_set_oom_fn(GC_oom_func fn) +{ + GC_ASSERT(NONNULL_ARG_NOT_NULL(fn)); + LOCK(); + GC_oom_fn = fn; + UNLOCK(); +} + +GC_API GC_oom_func GC_CALL GC_get_oom_fn(void) +{ + GC_oom_func fn; + + READER_LOCK(); + fn = GC_oom_fn; + READER_UNLOCK(); + return fn; +} + +GC_API void GC_CALL GC_set_on_heap_resize(GC_on_heap_resize_proc fn) +{ + /* fn may be 0 (means no event notifier). */ + LOCK(); + GC_on_heap_resize = fn; + UNLOCK(); +} + +GC_API GC_on_heap_resize_proc GC_CALL GC_get_on_heap_resize(void) +{ + GC_on_heap_resize_proc fn; + + READER_LOCK(); + fn = GC_on_heap_resize; + READER_UNLOCK(); + return fn; +} + +GC_API void GC_CALL GC_set_finalizer_notifier(GC_finalizer_notifier_proc fn) +{ + /* fn may be 0 (means no finalizer notifier). */ + LOCK(); + GC_finalizer_notifier = fn; + UNLOCK(); +} + +GC_API GC_finalizer_notifier_proc GC_CALL GC_get_finalizer_notifier(void) +{ + GC_finalizer_notifier_proc fn; + + READER_LOCK(); + fn = GC_finalizer_notifier; + READER_UNLOCK(); + return fn; +} + +/* Setter and getter functions for the public numeric R/W variables. */ +/* It is safe to call these functions even before GC_INIT(). */ +/* These functions are unsynchronized and, if called after GC_INIT(), */ +/* should be typically invoked inside the context of */ +/* GC_call_with_alloc_lock() (or GC_call_with_reader_lock() in case of */ +/* the getters) to prevent data race (unless it is guaranteed the */ +/* collector is not multi-threaded at that execution point). */ + +GC_API void GC_CALL GC_set_find_leak(int value) +{ + /* value is of boolean type. */ + GC_find_leak = value; +} + +GC_API int GC_CALL GC_get_find_leak(void) +{ + return GC_find_leak; +} + +GC_API void GC_CALL GC_set_all_interior_pointers(int value) +{ + GC_all_interior_pointers = value ? 1 : 0; + if (GC_is_initialized) { + /* It is not recommended to change GC_all_interior_pointers value */ + /* after GC is initialized but it seems GC could work correctly */ + /* even after switching the mode. */ + LOCK(); + GC_initialize_offsets(); /* NOTE: this resets manual offsets as well */ + if (!GC_all_interior_pointers) + GC_bl_init_no_interiors(); + UNLOCK(); + } +} + +GC_API int GC_CALL GC_get_all_interior_pointers(void) +{ + return GC_all_interior_pointers; +} + +GC_API void GC_CALL GC_set_finalize_on_demand(int value) +{ + GC_ASSERT(value != -1); /* -1 was used to retrieve old value in gc-7.2 */ + /* value is of boolean type. */ + GC_finalize_on_demand = value; +} + +GC_API int GC_CALL GC_get_finalize_on_demand(void) +{ + return GC_finalize_on_demand; +} + +GC_API void GC_CALL GC_set_java_finalization(int value) +{ + GC_ASSERT(value != -1); /* -1 was used to retrieve old value in gc-7.2 */ + /* value is of boolean type. */ + GC_java_finalization = value; +} + +GC_API int GC_CALL GC_get_java_finalization(void) +{ + return GC_java_finalization; +} + +GC_API void GC_CALL GC_set_dont_expand(int value) +{ + GC_ASSERT(value != -1); /* -1 was used to retrieve old value in gc-7.2 */ + /* value is of boolean type. */ + GC_dont_expand = value; +} + +GC_API int GC_CALL GC_get_dont_expand(void) +{ + return GC_dont_expand; +} + +GC_API void GC_CALL GC_set_no_dls(int value) +{ + GC_ASSERT(value != -1); /* -1 was used to retrieve old value in gc-7.2 */ + /* value is of boolean type. */ + GC_no_dls = value; +} + +GC_API int GC_CALL GC_get_no_dls(void) +{ + return GC_no_dls; +} + +GC_API void GC_CALL GC_set_non_gc_bytes(GC_word value) +{ + GC_non_gc_bytes = value; +} + +GC_API GC_word GC_CALL GC_get_non_gc_bytes(void) +{ + return GC_non_gc_bytes; +} + +GC_API void GC_CALL GC_set_free_space_divisor(GC_word value) +{ + GC_ASSERT(value > 0); + GC_free_space_divisor = value; +} + +GC_API GC_word GC_CALL GC_get_free_space_divisor(void) +{ + return GC_free_space_divisor; +} + +GC_API void GC_CALL GC_set_max_retries(GC_word value) +{ + GC_ASSERT((GC_signed_word)value != -1); + /* -1 was used to retrieve old value in gc-7.2 */ + GC_max_retries = value; +} + +GC_API GC_word GC_CALL GC_get_max_retries(void) +{ + return GC_max_retries; +} + +GC_API void GC_CALL GC_set_dont_precollect(int value) +{ + GC_ASSERT(value != -1); /* -1 was used to retrieve old value in gc-7.2 */ + /* value is of boolean type. */ + GC_dont_precollect = value; +} + +GC_API int GC_CALL GC_get_dont_precollect(void) +{ + return GC_dont_precollect; +} + +GC_API void GC_CALL GC_set_full_freq(int value) +{ + GC_ASSERT(value >= 0); + GC_full_freq = value; +} + +GC_API int GC_CALL GC_get_full_freq(void) +{ + return GC_full_freq; +} + +GC_API void GC_CALL GC_set_time_limit(unsigned long value) +{ + GC_ASSERT((long)value != -1L); + /* -1 was used to retrieve old value in gc-7.2 */ + GC_time_limit = value; +} + +GC_API unsigned long GC_CALL GC_get_time_limit(void) +{ + return GC_time_limit; +} + +GC_API void GC_CALL GC_set_force_unmap_on_gcollect(int value) +{ + GC_force_unmap_on_gcollect = (GC_bool)value; +} + +GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void) +{ + return (int)GC_force_unmap_on_gcollect; +} + +GC_API void GC_CALL GC_abort_on_oom(void) +{ + GC_err_printf("Insufficient memory for the allocation\n"); + EXIT(); +} + +GC_API size_t GC_CALL GC_get_hblk_size(void) +{ + return (size_t)HBLKSIZE; +} + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +#if (defined(MPROTECT_VDB) && !defined(MSWIN32) && !defined(MSWINCE)) \ + || defined(GC_SOLARIS_THREADS) || defined(OPENBSD) +# include +#endif + +#if defined(UNIX_LIKE) || defined(CYGWIN32) || defined(NACL) \ + || defined(SYMBIAN) +# include +#endif + +#if defined(LINUX) || defined(LINUX_STACKBOTTOM) +# include +#endif + +/* Blatantly OS dependent routines, except for those that are related */ +/* to dynamic loading. */ + +#ifdef AMIGA +# define GC_AMIGA_DEF + + +/****************************************************************** + + AmigaOS-specific routines for GC. + This file is normally included from os_dep.c + +******************************************************************/ + + +#if !defined(GC_AMIGA_DEF) && !defined(GC_AMIGA_SB) && !defined(GC_AMIGA_DS) && !defined(GC_AMIGA_AM) + +# include +# include +# define GC_AMIGA_DEF +# define GC_AMIGA_SB +# define GC_AMIGA_DS +# define GC_AMIGA_AM +#endif + + +#ifdef GC_AMIGA_DEF + +# ifndef __GNUC__ +# include +# endif +# include +# include +# include +# include + +#endif + + + + +#ifdef GC_AMIGA_SB + +/****************************************************************** + Find the base of the stack. +******************************************************************/ + +ptr_t GC_get_main_stack_base(void) +{ + struct Process *proc = (struct Process*)SysBase->ThisTask; + + /* Reference: Amiga Guru Book Pages: 42,567,574 */ + if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS + && proc->pr_CLI != NULL) { + /* first ULONG is StackSize */ + /*longPtr = proc->pr_ReturnAddr; + size = longPtr[0];*/ + + return (char *)proc->pr_ReturnAddr + sizeof(ULONG); + } else { + return (char *)proc->pr_Task.tc_SPUpper; + } +} + +#endif + + +#ifdef GC_AMIGA_DS +/****************************************************************** + Register data segments. +******************************************************************/ + + void GC_register_data_segments(void) + { + struct Process *proc; + struct CommandLineInterface *cli; + BPTR myseglist; + ULONG *data; + +# ifdef __GNUC__ + ULONG dataSegSize; + GC_bool found_segment = FALSE; + extern char __data_size[]; + + dataSegSize=__data_size+8; + /* Can`t find the Location of __data_size, because + it`s possible that is it, inside the segment. */ + +# endif + + proc= (struct Process*)SysBase->ThisTask; + + /* Reference: Amiga Guru Book Pages: 538ff,565,573 + and XOper.asm */ + myseglist = proc->pr_SegList; + if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS) { + if (proc->pr_CLI != NULL) { + /* ProcLoaded 'Loaded as a command: '*/ + cli = BADDR(proc->pr_CLI); + myseglist = cli->cli_Module; + } + } else { + ABORT("Not a Process."); + } + + if (myseglist == NULL) { + ABORT("Arrrgh.. can't find segments, aborting"); + } + + /* xoper hunks Shell Process */ + + for (data = (ULONG *)BADDR(myseglist); data != NULL; + data = (ULONG *)BADDR(data[0])) { + if ((ULONG)GC_register_data_segments < (ULONG)(&data[1]) + || (ULONG)GC_register_data_segments > (ULONG)(&data[1]) + + data[-1]) { +# ifdef __GNUC__ + if (dataSegSize == data[-1]) { + found_segment = TRUE; + } +# endif + GC_add_roots_inner((char *)&data[1], + ((char *)&data[1]) + data[-1], FALSE); + } + } /* for */ +# ifdef __GNUC__ + if (!found_segment) { + ABORT("Can`t find correct Segments.\nSolution: Use an newer version of ixemul.library"); + } +# endif + } + +#endif + + + +#ifdef GC_AMIGA_AM + +#ifndef GC_AMIGA_FASTALLOC + +void *GC_amiga_allocwrapper(size_t size,void *(*AllocFunction)(size_t size2)){ + return (*AllocFunction)(size); +} + +void *(*GC_amiga_allocwrapper_do)(size_t size,void *(*AllocFunction)(size_t size2)) + =GC_amiga_allocwrapper; + +#else + + + + +void *GC_amiga_allocwrapper_firsttime(size_t size,void *(*AllocFunction)(size_t size2)); + +void *(*GC_amiga_allocwrapper_do)(size_t size,void *(*AllocFunction)(size_t size2)) + =GC_amiga_allocwrapper_firsttime; + + +/****************************************************************** + Amiga-specific routines to obtain memory, and force GC to give + back fast-mem whenever possible. + These hacks makes gc-programs go many times faster when + the Amiga is low on memory, and are therefore strictly necessary. + + -Kjetil S. Matheussen, 2000. +******************************************************************/ + + + +/* List-header for all allocated memory. */ + +struct GC_Amiga_AllocedMemoryHeader{ + ULONG size; + struct GC_Amiga_AllocedMemoryHeader *next; +}; +struct GC_Amiga_AllocedMemoryHeader *GC_AMIGAMEM=(struct GC_Amiga_AllocedMemoryHeader *)(int)~(NULL); + + + +/* Type of memory. Once in the execution of a program, this might change to MEMF_ANY|MEMF_CLEAR */ + +ULONG GC_AMIGA_MEMF = MEMF_FAST | MEMF_CLEAR; + + +/* Prevents GC_amiga_get_mem from allocating memory if this one is TRUE. */ +#ifndef GC_AMIGA_ONLYFAST +BOOL GC_amiga_dontalloc=FALSE; +#endif + +#ifdef GC_AMIGA_PRINTSTATS +int succ=0,succ2=0; +int nsucc=0,nsucc2=0; +int nullretries=0; +int numcollects=0; +int chipa=0; +int allochip=0; +int allocfast=0; +int cur0=0; +int cur1=0; +int cur10=0; +int cur50=0; +int cur150=0; +int cur151=0; +int ncur0=0; +int ncur1=0; +int ncur10=0; +int ncur50=0; +int ncur150=0; +int ncur151=0; +#endif + +/* Free everything at program-end. */ + +void GC_amiga_free_all_mem(void){ + struct GC_Amiga_AllocedMemoryHeader *gc_am=(struct GC_Amiga_AllocedMemoryHeader *)(~(int)(GC_AMIGAMEM)); + +#ifdef GC_AMIGA_PRINTSTATS + printf("\n\n" + "%d bytes of chip-mem, and %d bytes of fast-mem where allocated from the OS.\n", + allochip,allocfast + ); + printf( + "%d bytes of chip-mem were returned from the GC_AMIGA_FASTALLOC supported allocating functions.\n", + chipa + ); + printf("\n"); + printf("GC_gcollect was called %d times to avoid returning NULL or start allocating with the MEMF_ANY flag.\n",numcollects); + printf("%d of them was a success. (the others had to use allocation from the OS.)\n",nullretries); + printf("\n"); + printf("Succeeded forcing %d gc-allocations (%d bytes) of chip-mem to be fast-mem.\n",succ,succ2); + printf("Failed forcing %d gc-allocations (%d bytes) of chip-mem to be fast-mem.\n",nsucc,nsucc2); + printf("\n"); + printf( + "Number of retries before succeeding a chip->fast force:\n" + "0: %d, 1: %d, 2-9: %d, 10-49: %d, 50-149: %d, >150: %d\n", + cur0,cur1,cur10,cur50,cur150,cur151 + ); + printf( + "Number of retries before giving up a chip->fast force:\n" + "0: %d, 1: %d, 2-9: %d, 10-49: %d, 50-149: %d, >150: %d\n", + ncur0,ncur1,ncur10,ncur50,ncur150,ncur151 + ); +#endif + + while(gc_am!=NULL){ + struct GC_Amiga_AllocedMemoryHeader *temp = gc_am->next; + FreeMem(gc_am,gc_am->size); + gc_am=(struct GC_Amiga_AllocedMemoryHeader *)(~(int)(temp)); + } +} + +#ifndef GC_AMIGA_ONLYFAST + +/* All memory with address lower than this one is chip-mem. */ + +char *chipmax; + + +/* + * Always set to the last size of memory tried to be allocated. + * Needed to ensure allocation when the size is bigger than 100000. + * + */ +size_t latestsize; + +#endif + + +#ifdef GC_AMIGA_FASTALLOC + +/* + * The actual function that is called with the GET_MEM macro. + * + */ + +void *GC_amiga_get_mem(size_t size){ + struct GC_Amiga_AllocedMemoryHeader *gc_am; + +#ifndef GC_AMIGA_ONLYFAST + if(GC_amiga_dontalloc==TRUE){ + return NULL; + } + + /* We really don't want to use chip-mem, but if we must, then as little as possible. */ + if(GC_AMIGA_MEMF==(MEMF_ANY|MEMF_CLEAR) && size>100000 && latestsize<50000) return NULL; +#endif + + gc_am=AllocMem((ULONG)(size + sizeof(struct GC_Amiga_AllocedMemoryHeader)),GC_AMIGA_MEMF); + if(gc_am==NULL) return NULL; + + gc_am->next=GC_AMIGAMEM; + gc_am->size=size + sizeof(struct GC_Amiga_AllocedMemoryHeader); + GC_AMIGAMEM=(struct GC_Amiga_AllocedMemoryHeader *)(~(int)(gc_am)); + +#ifdef GC_AMIGA_PRINTSTATS + if((char *)gc_amchipmax || ret==NULL){ + if(ret==NULL){ + nsucc++; + nsucc2+=size; + if(rec==0) ncur0++; + if(rec==1) ncur1++; + if(rec>1 && rec<10) ncur10++; + if(rec>=10 && rec<50) ncur50++; + if(rec>=50 && rec<150) ncur150++; + if(rec>=150) ncur151++; + }else{ + succ++; + succ2+=size; + if(rec==0) cur0++; + if(rec==1) cur1++; + if(rec>1 && rec<10) cur10++; + if(rec>=10 && rec<50) cur50++; + if(rec>=50 && rec<150) cur150++; + if(rec>=150) cur151++; + } + } +#endif + + if (((char *)ret)<=chipmax && ret!=NULL && (rec<(size>500000?9:size/5000))){ + ret=GC_amiga_rec_alloc(size,AllocFunction,rec+1); + } + + return ret; +} +#endif + + +/* The allocating-functions defined inside the Amiga-blocks in gc.h is called + * via these functions. + */ + + +void *GC_amiga_allocwrapper_any(size_t size,void *(*AllocFunction)(size_t size2)){ + void *ret; + + GC_amiga_dontalloc=TRUE; /* Pretty tough thing to do, but it's indeed necessary. */ + latestsize=size; + + ret=(*AllocFunction)(size); + + if(((char *)ret) <= chipmax){ + if(ret==NULL){ + /* Give GC access to allocate memory. */ +#ifdef GC_AMIGA_GC + if(!GC_dont_gc){ + GC_gcollect(); +#ifdef GC_AMIGA_PRINTSTATS + numcollects++; +#endif + ret=(*AllocFunction)(size); + } + if(ret==NULL) +#endif + { + GC_amiga_dontalloc=FALSE; + ret=(*AllocFunction)(size); + if(ret==NULL){ + WARN("Out of Memory! Returning NIL!\n", 0); + } + } +#ifdef GC_AMIGA_PRINTSTATS + else{ + nullretries++; + } + if(ret!=NULL && (char *)ret<=chipmax) chipa+=size; +#endif + } +#ifdef GC_AMIGA_RETRY + else{ + void *ret2; + /* We got chip-mem. Better try again and again and again etc., we might get fast-mem sooner or later... */ + /* Using gctest to check the effectiveness of doing this, does seldom give a very good result. */ + /* However, real programs doesn't normally rapidly allocate and deallocate. */ + if( + AllocFunction!=GC_malloc_uncollectable +#ifdef GC_ATOMIC_UNCOLLECTABLE + && AllocFunction!=GC_malloc_atomic_uncollectable +#endif + ){ + ret2=GC_amiga_rec_alloc(size,AllocFunction,0); + }else{ + ret2=(*AllocFunction)(size); +#ifdef GC_AMIGA_PRINTSTATS + if((char *)ret2chipmax){ + GC_free(ret); + ret=ret2; + }else{ + GC_free(ret2); + } + } +#endif + } + +# if defined(CPPCHECK) + if (GC_amiga_dontalloc) /* variable is actually used by AllocFunction */ +# endif + GC_amiga_dontalloc=FALSE; + + return ret; +} + + + +void (*GC_amiga_toany)(void)=NULL; + +void GC_amiga_set_toany(void (*func)(void)){ + GC_amiga_toany=func; +} + +#endif /* !GC_AMIGA_ONLYFAST */ + + +void *GC_amiga_allocwrapper_fast(size_t size,void *(*AllocFunction)(size_t size2)){ + void *ret; + + ret=(*AllocFunction)(size); + + if(ret==NULL){ + /* Enable chip-mem allocation. */ +#ifdef GC_AMIGA_GC + if(!GC_dont_gc){ + GC_gcollect(); +#ifdef GC_AMIGA_PRINTSTATS + numcollects++; +#endif + ret=(*AllocFunction)(size); + } + if(ret==NULL) +#endif + { +#ifndef GC_AMIGA_ONLYFAST + GC_AMIGA_MEMF=MEMF_ANY | MEMF_CLEAR; + if(GC_amiga_toany!=NULL) (*GC_amiga_toany)(); + GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_any; + return GC_amiga_allocwrapper_any(size,AllocFunction); +#endif + } +#ifdef GC_AMIGA_PRINTSTATS + else{ + nullretries++; + } +#endif + } + + return ret; +} + +void *GC_amiga_allocwrapper_firsttime(size_t size,void *(*AllocFunction)(size_t size2)){ + atexit(&GC_amiga_free_all_mem); + chipmax=(char *)SysBase->MaxLocMem; /* For people still having SysBase in chip-mem, this might speed up a bit. */ + GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_fast; + return GC_amiga_allocwrapper_fast(size,AllocFunction); +} + + +#endif /* GC_AMIGA_FASTALLOC */ + + + +/* + * The wrapped realloc function. + * + */ +void *GC_amiga_realloc(void *old_object,size_t new_size_in_bytes){ +#ifndef GC_AMIGA_FASTALLOC + return GC_realloc(old_object,new_size_in_bytes); +#else + void *ret; + latestsize=new_size_in_bytes; + ret=GC_realloc(old_object,new_size_in_bytes); + if(ret==NULL && new_size_in_bytes != 0 + && GC_AMIGA_MEMF==(MEMF_FAST | MEMF_CLEAR)){ + /* Out of fast-mem. */ +#ifdef GC_AMIGA_GC + if(!GC_dont_gc){ + GC_gcollect(); +#ifdef GC_AMIGA_PRINTSTATS + numcollects++; +#endif + ret=GC_realloc(old_object,new_size_in_bytes); + } + if(ret==NULL) +#endif + { +#ifndef GC_AMIGA_ONLYFAST + GC_AMIGA_MEMF=MEMF_ANY | MEMF_CLEAR; + if(GC_amiga_toany!=NULL) (*GC_amiga_toany)(); + GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_any; + ret=GC_realloc(old_object,new_size_in_bytes); +#endif + } +#ifdef GC_AMIGA_PRINTSTATS + else{ + nullretries++; + } +#endif + } + if(ret==NULL && new_size_in_bytes != 0){ + WARN("Out of Memory! Returning NIL!\n", 0); + } +#ifdef GC_AMIGA_PRINTSTATS + if(((char *)ret) +#endif + +#ifdef IRIX5 +# include +# include /* for locking */ +#endif + +#if defined(MMAP_SUPPORTED) || defined(ADD_HEAP_GUARD_PAGES) +# if defined(USE_MUNMAP) && !defined(USE_MMAP) && !defined(CPPCHECK) +# error Invalid config: USE_MUNMAP requires USE_MMAP +# endif +# include +# include +#endif + +#if defined(ADD_HEAP_GUARD_PAGES) || defined(LINUX_STACKBOTTOM) \ + || defined(MMAP_SUPPORTED) || defined(NEED_PROC_MAPS) +# include +#endif + +#if defined(DARWIN) && !defined(DYNAMIC_LOADING) \ + && !defined(GC_DONT_REGISTER_MAIN_STATIC_DATA) + /* for get_etext and friends */ +# include +#endif + +#ifdef DJGPP + /* Apparently necessary for djgpp 2.01. May cause problems with */ + /* other versions. */ + typedef long unsigned int caddr_t; +#endif + +#ifdef PCR + + +# include "mm/PCR_MM.h" +#endif + +#if !defined(NO_EXECUTE_PERMISSION) + STATIC GC_bool GC_pages_executable = TRUE; +#else + STATIC GC_bool GC_pages_executable = FALSE; +#endif +#define IGNORE_PAGES_EXECUTABLE 1 + /* Undefined on GC_pages_executable real use. */ + +#if ((defined(LINUX_STACKBOTTOM) || defined(NEED_PROC_MAPS) \ + || defined(PROC_VDB) || defined(SOFT_VDB)) && !defined(PROC_READ)) \ + || defined(CPPCHECK) +# define PROC_READ read + /* Should probably call the real read, if read is wrapped. */ +#endif + +#if defined(LINUX_STACKBOTTOM) || defined(NEED_PROC_MAPS) + /* Repeatedly perform a read call until the buffer is filled */ + /* up, or we encounter EOF or an error. */ + STATIC ssize_t GC_repeat_read(int fd, char *buf, size_t count) + { + ssize_t num_read = 0; + + ASSERT_CANCEL_DISABLED(); + while ((size_t)num_read < count) { + ssize_t result = PROC_READ(fd, buf + num_read, + count - (size_t)num_read); + + if (result < 0) return result; + if (result == 0) break; + num_read += result; + } + return num_read; + } +#endif /* LINUX_STACKBOTTOM || NEED_PROC_MAPS */ + +#ifdef NEED_PROC_MAPS +/* We need to parse /proc/self/maps, either to find dynamic libraries, */ +/* and/or to find the register backing store base (IA64). Do it once */ +/* here. */ + +#ifdef THREADS + /* Determine the length of a file by incrementally reading it into a */ + /* buffer. This would be silly to use it on a file supporting lseek, */ + /* but Linux /proc files usually do not. */ + /* As of Linux 4.15.0, lseek(SEEK_END) fails for /proc/self/maps. */ + STATIC size_t GC_get_file_len(int f) + { + size_t total = 0; + ssize_t result; +# define GET_FILE_LEN_BUF_SZ 500 + char buf[GET_FILE_LEN_BUF_SZ]; + + do { + result = PROC_READ(f, buf, sizeof(buf)); + if (result == -1) return 0; + total += (size_t)result; + } while (result > 0); + return total; + } + + STATIC size_t GC_get_maps_len(void) + { + int f = open("/proc/self/maps", O_RDONLY); + size_t result; + if (f < 0) return 0; /* treat missing file as empty */ + result = GC_get_file_len(f); + close(f); + return result; + } +#endif /* THREADS */ + +/* Copy the contents of /proc/self/maps to a buffer in our address */ +/* space. Return the address of the buffer. */ +GC_INNER const char * GC_get_maps(void) +{ + ssize_t result; + static char *maps_buf = NULL; + static size_t maps_buf_sz = 1; + size_t maps_size; +# ifdef THREADS + size_t old_maps_size = 0; +# endif + + /* The buffer is essentially static, so there must be a single client. */ + GC_ASSERT(I_HOLD_LOCK()); + + /* Note that in the presence of threads, the maps file can */ + /* essentially shrink asynchronously and unexpectedly as */ + /* threads that we already think of as dead release their */ + /* stacks. And there is no easy way to read the entire */ + /* file atomically. This is arguably a misfeature of the */ + /* /proc/self/maps interface. */ + /* Since we expect the file can grow asynchronously in rare */ + /* cases, it should suffice to first determine */ + /* the size (using read), and then to reread the file. */ + /* If the size is inconsistent we have to retry. */ + /* This only matters with threads enabled, and if we use */ + /* this to locate roots (not the default). */ + +# ifdef THREADS + /* Determine the initial size of /proc/self/maps. */ + maps_size = GC_get_maps_len(); + if (0 == maps_size) + ABORT("Cannot determine length of /proc/self/maps"); +# else + maps_size = 4000; /* Guess */ +# endif + + /* Read /proc/self/maps, growing maps_buf as necessary. */ + /* Note that we may not allocate conventionally, and */ + /* thus can't use stdio. */ + do { + int f; + + while (maps_size >= maps_buf_sz) { +# ifdef LINT2 + /* Workaround passing tainted maps_buf to a tainted sink. */ + GC_noop1((word)maps_buf); +# else + GC_scratch_recycle_no_gww(maps_buf, maps_buf_sz); +# endif + /* Grow only by powers of 2, since we leak "too small" buffers.*/ + while (maps_size >= maps_buf_sz) maps_buf_sz *= 2; + maps_buf = GC_scratch_alloc(maps_buf_sz); + if (NULL == maps_buf) + ABORT_ARG1("Insufficient space for /proc/self/maps buffer", + ", %lu bytes requested", (unsigned long)maps_buf_sz); +# ifdef THREADS + /* Recompute initial length, since we allocated. */ + /* This can only happen a few times per program */ + /* execution. */ + maps_size = GC_get_maps_len(); + if (0 == maps_size) + ABORT("Cannot determine length of /proc/self/maps"); +# endif + } + GC_ASSERT(maps_buf_sz >= maps_size + 1); + f = open("/proc/self/maps", O_RDONLY); + if (-1 == f) + ABORT_ARG1("Cannot open /proc/self/maps", + ": errno= %d", errno); +# ifdef THREADS + old_maps_size = maps_size; +# endif + maps_size = 0; + do { + result = GC_repeat_read(f, maps_buf, maps_buf_sz-1); + if (result < 0) { + ABORT_ARG1("Failed to read /proc/self/maps", + ": errno= %d", errno); + } + maps_size += (size_t)result; + } while ((size_t)result == maps_buf_sz-1); + close(f); + if (0 == maps_size) + ABORT("Empty /proc/self/maps"); +# ifdef THREADS + if (maps_size > old_maps_size) { + /* This might be caused by e.g. thread creation. */ + WARN("Unexpected asynchronous /proc/self/maps growth" + " (to %" WARN_PRIuPTR " bytes)\n", maps_size); + } +# endif + } while (maps_size >= maps_buf_sz +# ifdef THREADS + || maps_size < old_maps_size +# endif + ); + maps_buf[maps_size] = '\0'; + return maps_buf; +} + +/* + * GC_parse_map_entry parses an entry from /proc/self/maps so we can + * locate all writable data segments that belong to shared libraries. + * The format of one of these entries and the fields we care about + * is as follows: + * XXXXXXXX-XXXXXXXX r-xp 00000000 30:05 260537 name of mapping...\n + * ^^^^^^^^ ^^^^^^^^ ^^^^ ^^ + * start end prot maj_dev + * + * Note that since about august 2003 kernels, the columns no longer have + * fixed offsets on 64-bit kernels. Hence we no longer rely on fixed offsets + * anywhere, which is safer anyway. + */ + +/* Assign various fields of the first line in maps_ptr to (*start), */ +/* (*end), (*prot), (*maj_dev) and (*mapping_name). mapping_name may */ +/* be NULL. (*prot) and (*mapping_name) are assigned pointers into the */ +/* original buffer. */ +#if defined(DYNAMIC_LOADING) && defined(USE_PROC_FOR_LIBRARIES) \ + || defined(IA64) || defined(INCLUDE_LINUX_THREAD_DESCR) \ + || (defined(CHECK_SOFT_VDB) && defined(MPROTECT_VDB)) \ + || (defined(REDIRECT_MALLOC) && defined(GC_LINUX_THREADS)) + GC_INNER const char *GC_parse_map_entry(const char *maps_ptr, + ptr_t *start, ptr_t *end, + const char **prot, unsigned *maj_dev, + const char **mapping_name) + { + const unsigned char *start_start, *end_start, *maj_dev_start; + const unsigned char *p; /* unsigned for isspace, isxdigit */ + + if (maps_ptr == NULL || *maps_ptr == '\0') { + return NULL; + } + + p = (const unsigned char *)maps_ptr; + while (isspace(*p)) ++p; + start_start = p; + GC_ASSERT(isxdigit(*start_start)); + *start = (ptr_t)strtoul((const char *)start_start, (char **)&p, 16); + GC_ASSERT(*p=='-'); + + ++p; + end_start = p; + GC_ASSERT(isxdigit(*end_start)); + *end = (ptr_t)strtoul((const char *)end_start, (char **)&p, 16); + GC_ASSERT(isspace(*p)); + + while (isspace(*p)) ++p; + GC_ASSERT(*p == 'r' || *p == '-'); + *prot = (const char *)p; + /* Skip past protection field to offset field */ + while (!isspace(*p)) ++p; + while (isspace(*p)) p++; + GC_ASSERT(isxdigit(*p)); + /* Skip past offset field, which we ignore */ + while (!isspace(*p)) ++p; + while (isspace(*p)) p++; + maj_dev_start = p; + GC_ASSERT(isxdigit(*maj_dev_start)); + *maj_dev = strtoul((const char *)maj_dev_start, NULL, 16); + + if (mapping_name != NULL) { + while (*p && *p != '\n' && *p != '/' && *p != '[') p++; + *mapping_name = (const char *)p; + } + while (*p && *p++ != '\n'); + return (const char *)p; + } +#endif /* REDIRECT_MALLOC || DYNAMIC_LOADING || IA64 || ... */ + +#if defined(IA64) || defined(INCLUDE_LINUX_THREAD_DESCR) \ + || (defined(CHECK_SOFT_VDB) && defined(MPROTECT_VDB)) + /* Try to read the backing store base from /proc/self/maps. */ + /* Return the bounds of the writable mapping with a 0 major device, */ + /* which includes the address passed as data. */ + /* Return FALSE if there is no such mapping. */ + GC_INNER GC_bool GC_enclosing_writable_mapping(ptr_t addr, ptr_t *startp, + ptr_t *endp) + { + const char *prot; + ptr_t my_start, my_end; + unsigned int maj_dev; + const char *maps_ptr; + + GC_ASSERT(I_HOLD_LOCK()); + maps_ptr = GC_get_maps(); + for (;;) { + maps_ptr = GC_parse_map_entry(maps_ptr, &my_start, &my_end, + &prot, &maj_dev, 0); + if (NULL == maps_ptr) break; + + if ((word)my_end > (word)addr && (word)my_start <= (word)addr) { + if (prot[1] != 'w' || maj_dev != 0) break; + *startp = my_start; + *endp = my_end; + return TRUE; + } + } + return FALSE; + } +#endif /* IA64 || INCLUDE_LINUX_THREAD_DESCR */ + +#if defined(REDIRECT_MALLOC) && defined(GC_LINUX_THREADS) + /* Find the text(code) mapping for the library whose name, after */ + /* stripping the directory part, starts with nm. */ + GC_INNER GC_bool GC_text_mapping(char *nm, ptr_t *startp, ptr_t *endp) + { + size_t nm_len; + const char *prot, *map_path; + ptr_t my_start, my_end; + unsigned int maj_dev; + const char *maps_ptr; + + GC_ASSERT(I_HOLD_LOCK()); + maps_ptr = GC_get_maps(); + nm_len = strlen(nm); + for (;;) { + maps_ptr = GC_parse_map_entry(maps_ptr, &my_start, &my_end, + &prot, &maj_dev, &map_path); + if (NULL == maps_ptr) break; + + if (prot[0] == 'r' && prot[1] == '-' && prot[2] == 'x') { + const char *p = map_path; + + /* Set p to point just past last slash, if any. */ + while (*p != '\0' && *p != '\n' && *p != ' ' && *p != '\t') ++p; + while (*p != '/' && (word)p >= (word)map_path) --p; + ++p; + if (strncmp(nm, p, nm_len) == 0) { + *startp = my_start; + *endp = my_end; + return TRUE; + } + } + } + return FALSE; + } +#endif /* REDIRECT_MALLOC */ + +#ifdef IA64 + static ptr_t backing_store_base_from_proc(void) + { + ptr_t my_start, my_end; + + GC_ASSERT(I_HOLD_LOCK()); + if (!GC_enclosing_writable_mapping(GC_save_regs_in_stack(), + &my_start, &my_end)) { + GC_COND_LOG_PRINTF("Failed to find backing store base from /proc\n"); + return 0; + } + return my_start; + } +#endif + +#endif /* NEED_PROC_MAPS */ + +#if defined(SEARCH_FOR_DATA_START) + /* The x86 case can be handled without a search. The Alpha case */ + /* used to be handled differently as well, but the rules changed */ + /* for recent Linux versions. This seems to be the easiest way to */ + /* cover all versions. */ + +# if defined(LINUX) || defined(HURD) + /* Some Linux distributions arrange to define __data_start. Some */ + /* define data_start as a weak symbol. The latter is technically */ + /* broken, since the user program may define data_start, in which */ + /* case we lose. Nonetheless, we try both, preferring __data_start.*/ + /* We assume gcc-compatible pragmas. */ + EXTERN_C_BEGIN +# pragma weak __data_start +# pragma weak data_start + extern int __data_start[], data_start[]; + EXTERN_C_END +# elif defined(NETBSD) + EXTERN_C_BEGIN + extern char **environ; + EXTERN_C_END +# endif + + ptr_t GC_data_start = NULL; + + GC_INNER void GC_init_linux_data_start(void) + { + ptr_t data_end = DATAEND; + +# if (defined(LINUX) || defined(HURD)) && defined(USE_PROG_DATA_START) + /* Try the easy approaches first: */ + /* However, this may lead to wrong data start value if libgc */ + /* code is put into a shared library (directly or indirectly) */ + /* which is linked with -Bsymbolic-functions option. Thus, */ + /* the following is not used by default. */ + if (COVERT_DATAFLOW(__data_start) != 0) { + GC_data_start = (ptr_t)(__data_start); + } else { + GC_data_start = (ptr_t)(data_start); + } + if (COVERT_DATAFLOW(GC_data_start) != 0) { + if ((word)GC_data_start > (word)data_end) + ABORT_ARG2("Wrong __data_start/_end pair", + ": %p .. %p", (void *)GC_data_start, (void *)data_end); + return; + } +# ifdef DEBUG_ADD_DEL_ROOTS + GC_log_printf("__data_start not provided\n"); +# endif +# endif /* LINUX */ + + if (GC_no_dls) { + /* Not needed, avoids the SIGSEGV caused by */ + /* GC_find_limit which complicates debugging. */ + GC_data_start = data_end; /* set data root size to 0 */ + return; + } + +# ifdef NETBSD + /* This may need to be environ, without the underscore, for */ + /* some versions. */ + GC_data_start = (ptr_t)GC_find_limit(&environ, FALSE); +# else + GC_data_start = (ptr_t)GC_find_limit(data_end, FALSE); +# endif + } +#endif /* SEARCH_FOR_DATA_START */ + +#ifdef ECOS + +# ifndef ECOS_GC_MEMORY_SIZE +# define ECOS_GC_MEMORY_SIZE (448 * 1024) +# endif /* ECOS_GC_MEMORY_SIZE */ + + /* TODO: This is a simple way of allocating memory which is */ + /* compatible with ECOS early releases. Later releases use a more */ + /* sophisticated means of allocating memory than this simple static */ + /* allocator, but this method is at least bound to work. */ + static char ecos_gc_memory[ECOS_GC_MEMORY_SIZE]; + static char *ecos_gc_brk = ecos_gc_memory; + + static void *tiny_sbrk(ptrdiff_t increment) + { + void *p = ecos_gc_brk; + ecos_gc_brk += increment; + if ((word)ecos_gc_brk > (word)(ecos_gc_memory + sizeof(ecos_gc_memory))) { + ecos_gc_brk -= increment; + return NULL; + } + return p; + } +# define sbrk tiny_sbrk +#endif /* ECOS */ + +#if defined(ADDRESS_SANITIZER) && (defined(UNIX_LIKE) \ + || defined(NEED_FIND_LIMIT) || defined(MPROTECT_VDB)) \ + && !defined(CUSTOM_ASAN_DEF_OPTIONS) + EXTERN_C_BEGIN + GC_API const char *__asan_default_options(void); + EXTERN_C_END + + /* To tell ASan to allow GC to use its own SIGBUS/SEGV handlers. */ + /* The function is exported just to be visible to ASan library. */ + GC_API const char *__asan_default_options(void) + { + return "allow_user_segv_handler=1"; + } +#endif + +#ifdef OPENBSD + static struct sigaction old_segv_act; + STATIC JMP_BUF GC_jmp_buf_openbsd; + + STATIC void GC_fault_handler_openbsd(int sig) + { + UNUSED_ARG(sig); + LONGJMP(GC_jmp_buf_openbsd, 1); + } + + static volatile int firstpass; + + /* Return first addressable location > p or bound. */ + STATIC ptr_t GC_skip_hole_openbsd(ptr_t p, ptr_t bound) + { + static volatile ptr_t result; + + struct sigaction act; + word pgsz; + + GC_ASSERT(I_HOLD_LOCK()); + pgsz = (word)sysconf(_SC_PAGESIZE); + GC_ASSERT((word)bound >= pgsz); + + act.sa_handler = GC_fault_handler_openbsd; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_NODEFER | SA_RESTART; + /* act.sa_restorer is deprecated and should not be initialized. */ + sigaction(SIGSEGV, &act, &old_segv_act); + + firstpass = 1; + result = (ptr_t)((word)p & ~(pgsz-1)); + if (SETJMP(GC_jmp_buf_openbsd) != 0 || firstpass) { + firstpass = 0; + if ((word)result >= (word)bound - pgsz) { + result = bound; + } else { + result = result + pgsz; + /* no overflow expected; do not use compound */ + /* assignment with volatile-qualified left operand */ + GC_noop1((word)(*result)); + } + } + + sigaction(SIGSEGV, &old_segv_act, 0); + return result; + } +#endif /* OPENBSD */ + +# ifdef OS2 + +# include + +# if !defined(__IBMC__) && !defined(__WATCOMC__) /* e.g. EMX */ + +struct exe_hdr { + unsigned short magic_number; + unsigned short padding[29]; + long new_exe_offset; +}; + +#define E_MAGIC(x) (x).magic_number +#define EMAGIC 0x5A4D +#define E_LFANEW(x) (x).new_exe_offset + +struct e32_exe { + unsigned char magic_number[2]; + unsigned char byte_order; + unsigned char word_order; + unsigned long exe_format_level; + unsigned short cpu; + unsigned short os; + unsigned long padding1[13]; + unsigned long object_table_offset; + unsigned long object_count; + unsigned long padding2[31]; +}; + +#define E32_MAGIC1(x) (x).magic_number[0] +#define E32MAGIC1 'L' +#define E32_MAGIC2(x) (x).magic_number[1] +#define E32MAGIC2 'X' +#define E32_BORDER(x) (x).byte_order +#define E32LEBO 0 +#define E32_WORDER(x) (x).word_order +#define E32LEWO 0 +#define E32_CPU(x) (x).cpu +#define E32CPU286 1 +#define E32_OBJTAB(x) (x).object_table_offset +#define E32_OBJCNT(x) (x).object_count + +struct o32_obj { + unsigned long size; + unsigned long base; + unsigned long flags; + unsigned long pagemap; + unsigned long mapsize; + unsigned long reserved; +}; + +#define O32_FLAGS(x) (x).flags +#define OBJREAD 0x0001L +#define OBJWRITE 0x0002L +#define OBJINVALID 0x0080L +#define O32_SIZE(x) (x).size +#define O32_BASE(x) (x).base + +# else /* IBM's compiler */ + +/* A kludge to get around what appears to be a header file bug */ +# ifndef WORD +# define WORD unsigned short +# endif +# ifndef DWORD +# define DWORD unsigned long +# endif + +# define EXE386 1 +# include +# include + +# endif /* __IBMC__ */ + +# define INCL_DOSERRORS +# define INCL_DOSEXCEPTIONS +# define INCL_DOSFILEMGR +# define INCL_DOSMEMMGR +# define INCL_DOSMISC +# define INCL_DOSMODULEMGR +# define INCL_DOSPROCESS +# include + +# endif /* OS/2 */ + +/* Find the page size. */ +GC_INNER size_t GC_page_size = 0; +#ifdef REAL_PAGESIZE_NEEDED + GC_INNER size_t GC_real_page_size = 0; +#endif + +#ifdef SOFT_VDB + STATIC unsigned GC_log_pagesize = 0; +#endif + +#ifdef ANY_MSWIN + +# ifndef VER_PLATFORM_WIN32_CE +# define VER_PLATFORM_WIN32_CE 3 +# endif + +# if defined(MSWINCE) && defined(THREADS) + GC_INNER GC_bool GC_dont_query_stack_min = FALSE; +# endif + + GC_INNER SYSTEM_INFO GC_sysinfo; + +# ifndef CYGWIN32 +# define is_writable(prot) ((prot) == PAGE_READWRITE \ + || (prot) == PAGE_WRITECOPY \ + || (prot) == PAGE_EXECUTE_READWRITE \ + || (prot) == PAGE_EXECUTE_WRITECOPY) + /* Return the number of bytes that are writable starting at p. */ + /* The pointer p is assumed to be page aligned. */ + /* If base is not 0, *base becomes the beginning of the */ + /* allocation region containing p. */ + STATIC word GC_get_writable_length(ptr_t p, ptr_t *base) + { + MEMORY_BASIC_INFORMATION buf; + word result; + word protect; + + result = VirtualQuery(p, &buf, sizeof(buf)); + if (result != sizeof(buf)) ABORT("Weird VirtualQuery result"); + if (base != 0) *base = (ptr_t)(buf.AllocationBase); + protect = buf.Protect & ~(word)(PAGE_GUARD | PAGE_NOCACHE); + if (!is_writable(protect) || buf.State != MEM_COMMIT) return 0; + return buf.RegionSize; + } + + /* Fill in the GC_stack_base structure with the stack bottom for */ + /* this thread. Should not acquire the allocator lock as the */ + /* function is used by GC_DllMain. */ + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { + ptr_t trunc_sp; + word size; + + /* Set page size if it is not ready (so client can use this */ + /* function even before GC is initialized). */ + if (!GC_page_size) GC_setpagesize(); + + trunc_sp = (ptr_t)((word)GC_approx_sp() & ~(word)(GC_page_size-1)); + /* FIXME: This won't work if called from a deeply recursive */ + /* client code (and the committed stack space has grown). */ + size = GC_get_writable_length(trunc_sp, 0); + GC_ASSERT(size != 0); + sb -> mem_base = trunc_sp + size; + return GC_SUCCESS; + } +# else /* CYGWIN32 */ + /* An alternate version for Cygwin (adapted from Dave Korn's */ + /* gcc version of boehm-gc). */ + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { +# ifdef X86_64 + sb -> mem_base = ((NT_TIB*)NtCurrentTeb())->StackBase; +# else + void * _tlsbase; + + __asm__ ("movl %%fs:4, %0" + : "=r" (_tlsbase)); + sb -> mem_base = _tlsbase; +# endif + return GC_SUCCESS; + } +# endif /* CYGWIN32 */ +# define HAVE_GET_STACK_BASE + +#elif defined(OS2) + + static int os2_getpagesize(void) + { + ULONG result[1]; + + if (DosQuerySysInfo(QSV_PAGE_SIZE, QSV_PAGE_SIZE, + (void *)result, sizeof(ULONG)) != NO_ERROR) { + WARN("DosQuerySysInfo failed\n", 0); + result[0] = 4096; + } + return (int)result[0]; + } + +#endif /* !ANY_MSWIN && OS2 */ + +GC_INNER void GC_setpagesize(void) +{ +# ifdef ANY_MSWIN + GetSystemInfo(&GC_sysinfo); +# ifdef ALT_PAGESIZE_USED + /* Allocations made with mmap() are aligned to the allocation */ + /* granularity, which (at least on Win64) is not the same as the */ + /* page size. Probably we could distinguish the allocation */ + /* granularity from the actual page size, but in practice there */ + /* is no good reason to make allocations smaller than */ + /* dwAllocationGranularity, so we just use it instead of the */ + /* actual page size here (as Cygwin itself does in many cases). */ + GC_page_size = (size_t)GC_sysinfo.dwAllocationGranularity; +# ifdef REAL_PAGESIZE_NEEDED + GC_real_page_size = (size_t)GC_sysinfo.dwPageSize; + GC_ASSERT(GC_page_size >= GC_real_page_size); +# endif +# else + GC_page_size = (size_t)GC_sysinfo.dwPageSize; +# endif +# if defined(MSWINCE) && !defined(_WIN32_WCE_EMULATION) + { + OSVERSIONINFO verInfo; + /* Check the current WinCE version. */ + verInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (!GetVersionEx(&verInfo)) + ABORT("GetVersionEx failed"); + if (verInfo.dwPlatformId == VER_PLATFORM_WIN32_CE && + verInfo.dwMajorVersion < 6) { + /* Only the first 32 MB of address space belongs to the */ + /* current process (unless WinCE 6.0+ or emulation). */ + GC_sysinfo.lpMaximumApplicationAddress = (LPVOID)((word)32 << 20); +# ifdef THREADS + /* On some old WinCE versions, it's observed that */ + /* VirtualQuery calls don't work properly when used to */ + /* get thread current stack committed minimum. */ + if (verInfo.dwMajorVersion < 5) + GC_dont_query_stack_min = TRUE; +# endif + } + } +# endif +# else +# ifdef ALT_PAGESIZE_USED +# ifdef REAL_PAGESIZE_NEEDED + GC_real_page_size = (size_t)GETPAGESIZE(); +# endif + /* It's acceptable to fake it. */ + GC_page_size = HBLKSIZE; +# else + GC_page_size = (size_t)GETPAGESIZE(); +# if !defined(CPPCHECK) + if (0 == GC_page_size) + ABORT("getpagesize failed"); +# endif +# endif +# endif /* !ANY_MSWIN */ +# ifdef SOFT_VDB + { + size_t pgsize; + unsigned log_pgsize = 0; + +# if !defined(CPPCHECK) + if (((GC_page_size-1) & GC_page_size) != 0) + ABORT("Invalid page size"); /* not a power of two */ +# endif + for (pgsize = GC_page_size; pgsize > 1; pgsize >>= 1) + log_pgsize++; + GC_log_pagesize = log_pgsize; + } +# endif +} + +#ifdef EMBOX +# include +# include + + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { + pthread_t self = pthread_self(); + void *stack_addr = thread_stack_get(self); + + /* TODO: use pthread_getattr_np, pthread_attr_getstack alternatively */ +# ifdef STACK_GROWS_UP + sb -> mem_base = stack_addr; +# else + sb -> mem_base = (ptr_t)stack_addr + thread_stack_get_size(self); +# endif + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* EMBOX */ + +#ifdef HAIKU +# include + + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { + thread_info th; + get_thread_info(find_thread(NULL),&th); + sb->mem_base = th.stack_end; + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* HAIKU */ + +#ifdef OS2 + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { + PTIB ptib; /* thread information block */ + PPIB ppib; + if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) { + WARN("DosGetInfoBlocks failed\n", 0); + return GC_UNIMPLEMENTED; + } + sb->mem_base = ptib->tib_pstacklimit; + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* OS2 */ + +#ifdef AMIGA +# define GC_AMIGA_SB + +# undef GC_AMIGA_SB +# define GET_MAIN_STACKBASE_SPECIAL +#endif /* AMIGA */ + +#if defined(NEED_FIND_LIMIT) \ + || (defined(UNIX_LIKE) && !defined(NO_DEBUGGING)) \ + || (defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS)) \ + || (defined(WRAP_MARK_SOME) && defined(NO_SEH_AVAILABLE)) + +# include + +# ifdef USE_SEGV_SIGACT +# ifndef OPENBSD + static struct sigaction old_segv_act; +# endif +# ifdef USE_BUS_SIGACT + static struct sigaction old_bus_act; +# endif +# else + static GC_fault_handler_t old_segv_hand; +# ifdef HAVE_SIGBUS + static GC_fault_handler_t old_bus_hand; +# endif +# endif /* !USE_SEGV_SIGACT */ + + GC_INNER void GC_set_and_save_fault_handler(GC_fault_handler_t h) + { +# ifdef USE_SEGV_SIGACT + struct sigaction act; + + act.sa_handler = h; +# ifdef SIGACTION_FLAGS_NODEFER_HACK + /* Was necessary for Solaris 2.3 and very temporary */ + /* NetBSD bugs. */ + act.sa_flags = SA_RESTART | SA_NODEFER; +# else + act.sa_flags = SA_RESTART; +# endif + + (void)sigemptyset(&act.sa_mask); + /* act.sa_restorer is deprecated and should not be initialized. */ +# ifdef GC_IRIX_THREADS + /* Older versions have a bug related to retrieving and */ + /* and setting a handler at the same time. */ + (void)sigaction(SIGSEGV, 0, &old_segv_act); + (void)sigaction(SIGSEGV, &act, 0); +# else + (void)sigaction(SIGSEGV, &act, &old_segv_act); +# ifdef USE_BUS_SIGACT + /* Pthreads doesn't exist under Irix 5.x, so we */ + /* don't have to worry in the threads case. */ + (void)sigaction(SIGBUS, &act, &old_bus_act); +# endif +# endif /* !GC_IRIX_THREADS */ +# else + old_segv_hand = signal(SIGSEGV, h); +# ifdef HAVE_SIGBUS + old_bus_hand = signal(SIGBUS, h); +# endif +# endif /* !USE_SEGV_SIGACT */ +# if defined(CPPCHECK) && defined(ADDRESS_SANITIZER) + GC_noop1((word)&__asan_default_options); +# endif + } +#endif /* NEED_FIND_LIMIT || UNIX_LIKE || WRAP_MARK_SOME */ + +#if defined(NEED_FIND_LIMIT) \ + || (defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS)) \ + || (defined(WRAP_MARK_SOME) && defined(NO_SEH_AVAILABLE)) + GC_INNER JMP_BUF GC_jmp_buf; + + STATIC void GC_fault_handler(int sig) + { + UNUSED_ARG(sig); + LONGJMP(GC_jmp_buf, 1); + } + + GC_INNER void GC_setup_temporary_fault_handler(void) + { + /* Handler is process-wide, so this should only happen in */ + /* one thread at a time. */ + GC_ASSERT(I_HOLD_LOCK()); + GC_set_and_save_fault_handler(GC_fault_handler); + } + + GC_INNER void GC_reset_fault_handler(void) + { +# ifdef USE_SEGV_SIGACT + (void)sigaction(SIGSEGV, &old_segv_act, 0); +# ifdef USE_BUS_SIGACT + (void)sigaction(SIGBUS, &old_bus_act, 0); +# endif +# else + (void)signal(SIGSEGV, old_segv_hand); +# ifdef HAVE_SIGBUS + (void)signal(SIGBUS, old_bus_hand); +# endif +# endif + } +#endif /* NEED_FIND_LIMIT || USE_PROC_FOR_LIBRARIES || WRAP_MARK_SOME */ + +#if defined(NEED_FIND_LIMIT) \ + || (defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS)) +# define MIN_PAGE_SIZE 256 /* Smallest conceivable page size, in bytes. */ + + /* Return the first non-addressable location > p (up) or */ + /* the smallest location q s.t. [q,p) is addressable (!up). */ + /* We assume that p (up) or p-1 (!up) is addressable. */ + GC_ATTR_NO_SANITIZE_ADDR + STATIC ptr_t GC_find_limit_with_bound(ptr_t p, GC_bool up, ptr_t bound) + { + static volatile ptr_t result; + /* Safer if static, since otherwise it may not be */ + /* preserved across the longjmp. Can safely be */ + /* static since it's only called with the allocator */ + /* lock held. */ + + GC_ASSERT(up ? (word)bound >= MIN_PAGE_SIZE + : (word)bound <= ~(word)MIN_PAGE_SIZE); + GC_ASSERT(I_HOLD_LOCK()); + GC_setup_temporary_fault_handler(); + if (SETJMP(GC_jmp_buf) == 0) { + result = (ptr_t)((word)p & ~(word)(MIN_PAGE_SIZE-1)); + for (;;) { + if (up) { + if ((word)result >= (word)bound - MIN_PAGE_SIZE) { + result = bound; + break; + } + result = result + MIN_PAGE_SIZE; + /* no overflow expected; do not use compound */ + /* assignment with volatile-qualified left operand */ + } else { + if ((word)result <= (word)bound + MIN_PAGE_SIZE) { + result = bound - MIN_PAGE_SIZE; + /* This is to compensate */ + /* further result increment (we */ + /* do not modify "up" variable */ + /* since it might be clobbered */ + /* by setjmp otherwise). */ + break; + } + result = result - MIN_PAGE_SIZE; + /* no underflow expected; do not use compound */ + /* assignment with volatile-qualified left operand */ + } + GC_noop1((word)(*result)); + } + } + GC_reset_fault_handler(); + return up ? result : result + MIN_PAGE_SIZE; + } + + void * GC_find_limit(void * p, int up) + { + return GC_find_limit_with_bound((ptr_t)p, (GC_bool)up, + up ? (ptr_t)GC_WORD_MAX : 0); + } +#endif /* NEED_FIND_LIMIT || USE_PROC_FOR_LIBRARIES */ + +#ifdef HPUX_MAIN_STACKBOTTOM +# include +# include + + STATIC ptr_t GC_hpux_main_stack_base(void) + { + struct pst_vm_status vm_status; + int i = 0; + + while (pstat_getprocvm(&vm_status, sizeof(vm_status), 0, i++) == 1) { + if (vm_status.pst_type == PS_STACK) + return (ptr_t)vm_status.pst_vaddr; + } + + /* Old way to get the stack bottom. */ +# ifdef STACK_GROWS_UP + return (ptr_t)GC_find_limit(GC_approx_sp(), /* up= */ FALSE); +# else /* not HP_PA */ + return (ptr_t)GC_find_limit(GC_approx_sp(), TRUE); +# endif + } +#endif /* HPUX_MAIN_STACKBOTTOM */ + +#ifdef HPUX_STACKBOTTOM + +#include +#include + + GC_INNER ptr_t GC_get_register_stack_base(void) + { + struct pst_vm_status vm_status; + + int i = 0; + while (pstat_getprocvm(&vm_status, sizeof(vm_status), 0, i++) == 1) { + if (vm_status.pst_type == PS_RSESTACK) { + return (ptr_t) vm_status.pst_vaddr; + } + } + + /* old way to get the register stackbottom */ + GC_ASSERT(GC_stackbottom != NULL); + return (ptr_t)(((word)GC_stackbottom - BACKING_STORE_DISPLACEMENT - 1) + & ~(word)(BACKING_STORE_ALIGNMENT-1)); + } + +#endif /* HPUX_STACK_BOTTOM */ + +#ifdef LINUX_STACKBOTTOM + +# include + +# define STAT_SKIP 27 /* Number of fields preceding startstack */ + /* field in /proc/self/stat */ + +# ifdef USE_LIBC_PRIVATES + EXTERN_C_BEGIN +# pragma weak __libc_stack_end + extern ptr_t __libc_stack_end; +# ifdef IA64 +# pragma weak __libc_ia64_register_backing_store_base + extern ptr_t __libc_ia64_register_backing_store_base; +# endif + EXTERN_C_END +# endif + +# ifdef IA64 + GC_INNER ptr_t GC_get_register_stack_base(void) + { + ptr_t result; + + GC_ASSERT(I_HOLD_LOCK()); +# ifdef USE_LIBC_PRIVATES + if (0 != &__libc_ia64_register_backing_store_base + && 0 != __libc_ia64_register_backing_store_base) { + /* glibc 2.2.4 has a bug such that for dynamically linked */ + /* executables __libc_ia64_register_backing_store_base is */ + /* defined but uninitialized during constructor calls. */ + /* Hence we check for both nonzero address and value. */ + return __libc_ia64_register_backing_store_base; + } +# endif + result = backing_store_base_from_proc(); + if (0 == result) { + result = (ptr_t)GC_find_limit(GC_save_regs_in_stack(), FALSE); + /* This works better than a constant displacement heuristic. */ + } + return result; + } +# endif /* IA64 */ + + STATIC ptr_t GC_linux_main_stack_base(void) + { + /* We read the stack bottom value from /proc/self/stat. We do this */ + /* using direct I/O system calls in order to avoid calling malloc */ + /* in case REDIRECT_MALLOC is defined. */ +# define STAT_BUF_SIZE 4096 + unsigned char stat_buf[STAT_BUF_SIZE]; + int f; + word result; + ssize_t i, buf_offset = 0, len; + + /* First try the easy way. This should work for glibc 2.2. */ + /* This fails in a prelinked ("prelink" command) executable */ + /* since the correct value of __libc_stack_end never */ + /* becomes visible to us. The second test works around */ + /* this. */ +# ifdef USE_LIBC_PRIVATES + if (0 != &__libc_stack_end && 0 != __libc_stack_end ) { +# if defined(IA64) + /* Some versions of glibc set the address 16 bytes too */ + /* low while the initialization code is running. */ + if (((word)__libc_stack_end & 0xfff) + 0x10 < 0x1000) { + return __libc_stack_end + 0x10; + } /* Otherwise it's not safe to add 16 bytes and we fall */ + /* back to using /proc. */ +# elif defined(SPARC) + /* Older versions of glibc for 64-bit SPARC do not set this */ + /* variable correctly, it gets set to either zero or one. */ + if (__libc_stack_end != (ptr_t) (unsigned long)0x1) + return __libc_stack_end; +# else + return __libc_stack_end; +# endif + } +# endif + + f = open("/proc/self/stat", O_RDONLY); + if (-1 == f) + ABORT_ARG1("Could not open /proc/self/stat", ": errno= %d", errno); + len = GC_repeat_read(f, (char*)stat_buf, sizeof(stat_buf)); + if (len < 0) + ABORT_ARG1("Failed to read /proc/self/stat", + ": errno= %d", errno); + close(f); + + /* Skip the required number of fields. This number is hopefully */ + /* constant across all Linux implementations. */ + for (i = 0; i < STAT_SKIP; ++i) { + while (buf_offset < len && isspace(stat_buf[buf_offset++])) { + /* empty */ + } + while (buf_offset < len && !isspace(stat_buf[buf_offset++])) { + /* empty */ + } + } + /* Skip spaces. */ + while (buf_offset < len && isspace(stat_buf[buf_offset])) { + buf_offset++; + } + /* Find the end of the number and cut the buffer there. */ + for (i = 0; buf_offset + i < len; i++) { + if (!isdigit(stat_buf[buf_offset + i])) break; + } + if (buf_offset + i >= len) ABORT("Could not parse /proc/self/stat"); + stat_buf[buf_offset + i] = '\0'; + + result = (word)STRTOULL((char*)stat_buf + buf_offset, NULL, 10); + if (result < 0x100000 || (result & (sizeof(word) - 1)) != 0) + ABORT_ARG1("Absurd stack bottom value", + ": 0x%lx", (unsigned long)result); + return (ptr_t)result; + } +#endif /* LINUX_STACKBOTTOM */ + +#ifdef QNX_STACKBOTTOM + STATIC ptr_t GC_qnx_main_stack_base(void) + { + /* TODO: this approach is not very exact but it works for the */ + /* tests, at least, unlike other available heuristics. */ + return (ptr_t)__builtin_frame_address(0); + } +#endif /* QNX_STACKBOTTOM */ + +#ifdef FREEBSD_STACKBOTTOM + /* This uses an undocumented sysctl call, but at least one expert */ + /* believes it will stay. */ + +# include + + STATIC ptr_t GC_freebsd_main_stack_base(void) + { + int nm[2] = {CTL_KERN, KERN_USRSTACK}; + ptr_t base; + size_t len = sizeof(ptr_t); + int r = sysctl(nm, 2, &base, &len, NULL, 0); + if (r) ABORT("Error getting main stack base"); + return base; + } +#endif /* FREEBSD_STACKBOTTOM */ + +#if defined(ECOS) || defined(NOSYS) + ptr_t GC_get_main_stack_base(void) + { + return STACKBOTTOM; + } +# define GET_MAIN_STACKBASE_SPECIAL +#elif defined(SYMBIAN) + EXTERN_C_BEGIN + extern int GC_get_main_symbian_stack_base(void); + EXTERN_C_END + + ptr_t GC_get_main_stack_base(void) + { + return (ptr_t)GC_get_main_symbian_stack_base(); + } +# define GET_MAIN_STACKBASE_SPECIAL +#elif defined(EMSCRIPTEN) +# include + + ptr_t GC_get_main_stack_base(void) + { + return (ptr_t)emscripten_stack_get_base(); + } +# define GET_MAIN_STACKBASE_SPECIAL +#elif !defined(AMIGA) && !defined(EMBOX) && !defined(HAIKU) && !defined(OS2) \ + && !defined(ANY_MSWIN) && !defined(GC_OPENBSD_THREADS) \ + && (!defined(GC_SOLARIS_THREADS) || defined(_STRICT_STDC)) + +# if (defined(HAVE_PTHREAD_ATTR_GET_NP) || defined(HAVE_PTHREAD_GETATTR_NP)) \ + && (defined(THREADS) || defined(USE_GET_STACKBASE_FOR_MAIN)) +# include +# ifdef HAVE_PTHREAD_NP_H +# include /* for pthread_attr_get_np() */ +# endif +# elif defined(DARWIN) && !defined(NO_PTHREAD_GET_STACKADDR_NP) + /* We could use pthread_get_stackaddr_np even in case of a */ + /* single-threaded gclib (there is no -lpthread on Darwin). */ +# include +# undef STACKBOTTOM +# define STACKBOTTOM (ptr_t)pthread_get_stackaddr_np(pthread_self()) +# endif + + ptr_t GC_get_main_stack_base(void) + { + ptr_t result; +# if (defined(HAVE_PTHREAD_ATTR_GET_NP) \ + || defined(HAVE_PTHREAD_GETATTR_NP)) \ + && (defined(USE_GET_STACKBASE_FOR_MAIN) \ + || (defined(THREADS) && !defined(REDIRECT_MALLOC))) + pthread_attr_t attr; + void *stackaddr; + size_t size; + +# ifdef HAVE_PTHREAD_ATTR_GET_NP + if (pthread_attr_init(&attr) == 0 + && (pthread_attr_get_np(pthread_self(), &attr) == 0 + ? TRUE : (pthread_attr_destroy(&attr), FALSE))) +# else /* HAVE_PTHREAD_GETATTR_NP */ + if (pthread_getattr_np(pthread_self(), &attr) == 0) +# endif + { + if (pthread_attr_getstack(&attr, &stackaddr, &size) == 0 + && stackaddr != NULL) { + (void)pthread_attr_destroy(&attr); +# ifndef STACK_GROWS_UP + stackaddr = (char *)stackaddr + size; +# endif + return (ptr_t)stackaddr; + } + (void)pthread_attr_destroy(&attr); + } + WARN("pthread_getattr_np or pthread_attr_getstack failed" + " for main thread\n", 0); +# endif +# ifdef STACKBOTTOM + result = STACKBOTTOM; +# else +# ifdef HEURISTIC1 +# define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1) +# ifdef STACK_GROWS_UP + result = (ptr_t)((word)GC_approx_sp() + & ~(word)STACKBOTTOM_ALIGNMENT_M1); +# else + result = PTRT_ROUNDUP_BY_MASK(GC_approx_sp(), + STACKBOTTOM_ALIGNMENT_M1); +# endif +# elif defined(HPUX_MAIN_STACKBOTTOM) + result = GC_hpux_main_stack_base(); +# elif defined(LINUX_STACKBOTTOM) + result = GC_linux_main_stack_base(); +# elif defined(QNX_STACKBOTTOM) + result = GC_qnx_main_stack_base(); +# elif defined(FREEBSD_STACKBOTTOM) + result = GC_freebsd_main_stack_base(); +# elif defined(HEURISTIC2) + { + ptr_t sp = GC_approx_sp(); + +# ifdef STACK_GROWS_UP + result = (ptr_t)GC_find_limit(sp, /* up= */ FALSE); +# else + result = (ptr_t)GC_find_limit(sp, TRUE); +# endif +# if defined(HEURISTIC2_LIMIT) && !defined(CPPCHECK) + if ((word)result COOLER_THAN (word)HEURISTIC2_LIMIT + && (word)sp HOTTER_THAN (word)HEURISTIC2_LIMIT) + result = HEURISTIC2_LIMIT; +# endif + } +# elif defined(STACK_NOT_SCANNED) || defined(CPPCHECK) + result = NULL; +# else +# error None of HEURISTIC* and *STACKBOTTOM defined! +# endif +# if !defined(STACK_GROWS_UP) && !defined(CPPCHECK) + if (NULL == result) + result = (ptr_t)(signed_word)(-sizeof(ptr_t)); +# endif +# endif +# if !defined(CPPCHECK) + GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)result); +# endif + return result; + } +# define GET_MAIN_STACKBASE_SPECIAL +#endif /* !AMIGA && !ANY_MSWIN && !HAIKU && !GC_OPENBSD_THREADS && !OS2 */ + +#if (defined(HAVE_PTHREAD_ATTR_GET_NP) || defined(HAVE_PTHREAD_GETATTR_NP)) \ + && defined(THREADS) && !defined(HAVE_GET_STACK_BASE) +# include +# ifdef HAVE_PTHREAD_NP_H +# include +# endif + + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b) + { + pthread_attr_t attr; + size_t size; + +# ifdef HAVE_PTHREAD_ATTR_GET_NP + if (pthread_attr_init(&attr) != 0) + ABORT("pthread_attr_init failed"); + if (pthread_attr_get_np(pthread_self(), &attr) != 0) { + WARN("pthread_attr_get_np failed\n", 0); + (void)pthread_attr_destroy(&attr); + return GC_UNIMPLEMENTED; + } +# else /* HAVE_PTHREAD_GETATTR_NP */ + if (pthread_getattr_np(pthread_self(), &attr) != 0) { + WARN("pthread_getattr_np failed\n", 0); + return GC_UNIMPLEMENTED; + } +# endif + if (pthread_attr_getstack(&attr, &(b -> mem_base), &size) != 0) { + ABORT("pthread_attr_getstack failed"); + } + (void)pthread_attr_destroy(&attr); +# ifndef STACK_GROWS_UP + b -> mem_base = (char *)(b -> mem_base) + size; +# endif +# ifdef IA64 + /* We could try backing_store_base_from_proc, but that's safe */ + /* only if no mappings are being asynchronously created. */ + /* Subtracting the size from the stack base doesn't work for at */ + /* least the main thread. */ + LOCK(); + { + IF_CANCEL(int cancel_state;) + ptr_t bsp; + ptr_t next_stack; + + DISABLE_CANCEL(cancel_state); + bsp = GC_save_regs_in_stack(); + next_stack = GC_greatest_stack_base_below(bsp); + if (0 == next_stack) { + b -> reg_base = GC_find_limit(bsp, FALSE); + } else { + /* Avoid walking backwards into preceding memory stack and */ + /* growing it. */ + b -> reg_base = GC_find_limit_with_bound(bsp, FALSE, next_stack); + } + RESTORE_CANCEL(cancel_state); + } + UNLOCK(); +# elif defined(E2K) + b -> reg_base = NULL; +# endif + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* THREADS && (HAVE_PTHREAD_ATTR_GET_NP || HAVE_PTHREAD_GETATTR_NP) */ + +#if defined(GC_DARWIN_THREADS) && !defined(NO_PTHREAD_GET_STACKADDR_NP) +# include + + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b) + { + /* pthread_get_stackaddr_np() should return stack bottom (highest */ + /* stack address plus 1). */ + b->mem_base = pthread_get_stackaddr_np(pthread_self()); + GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)b->mem_base); + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* GC_DARWIN_THREADS */ + +#ifdef GC_OPENBSD_THREADS +# include +# include +# include + + /* Find the stack using pthread_stackseg_np(). */ + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { + stack_t stack; + if (pthread_stackseg_np(pthread_self(), &stack)) + ABORT("pthread_stackseg_np(self) failed"); + sb->mem_base = stack.ss_sp; + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* GC_OPENBSD_THREADS */ + +#if defined(GC_SOLARIS_THREADS) && !defined(_STRICT_STDC) + +# include +# include + + /* These variables are used to cache ss_sp value for the primordial */ + /* thread (it's better not to call thr_stksegment() twice for this */ + /* thread - see JDK bug #4352906). */ + static pthread_t stackbase_main_self = 0; + /* 0 means stackbase_main_ss_sp value is unset. */ + static void *stackbase_main_ss_sp = NULL; + + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b) + { + stack_t s; + pthread_t self = pthread_self(); + + if (self == stackbase_main_self) + { + /* If the client calls GC_get_stack_base() from the main thread */ + /* then just return the cached value. */ + b -> mem_base = stackbase_main_ss_sp; + GC_ASSERT(b -> mem_base != NULL); + return GC_SUCCESS; + } + + if (thr_stksegment(&s)) { + /* According to the manual, the only failure error code returned */ + /* is EAGAIN meaning "the information is not available due to the */ + /* thread is not yet completely initialized or it is an internal */ + /* thread" - this shouldn't happen here. */ + ABORT("thr_stksegment failed"); + } + /* s.ss_sp holds the pointer to the stack bottom. */ + GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)s.ss_sp); + + if (!stackbase_main_self && thr_main() != 0) + { + /* Cache the stack bottom pointer for the primordial thread */ + /* (this is done during GC_init, so there is no race). */ + stackbase_main_ss_sp = s.ss_sp; + stackbase_main_self = self; + } + + b -> mem_base = s.ss_sp; + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* GC_SOLARIS_THREADS */ + +#ifdef GC_RTEMS_PTHREADS + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) + { + sb->mem_base = rtems_get_stack_bottom(); + return GC_SUCCESS; + } +# define HAVE_GET_STACK_BASE +#endif /* GC_RTEMS_PTHREADS */ + +#ifndef HAVE_GET_STACK_BASE +# ifdef NEED_FIND_LIMIT + /* Retrieve the stack bottom. */ + /* Using the GC_find_limit version is risky. */ + /* On IA64, for example, there is no guard page between the */ + /* stack of one thread and the register backing store of the */ + /* next. Thus this is likely to identify way too large a */ + /* "stack" and thus at least result in disastrous performance. */ + /* TODO: Implement better strategies here. */ + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b) + { + IF_CANCEL(int cancel_state;) + + LOCK(); + DISABLE_CANCEL(cancel_state); /* May be unnecessary? */ +# ifdef STACK_GROWS_UP + b -> mem_base = GC_find_limit(GC_approx_sp(), /* up= */ FALSE); +# else + b -> mem_base = GC_find_limit(GC_approx_sp(), TRUE); +# endif +# ifdef IA64 + b -> reg_base = GC_find_limit(GC_save_regs_in_stack(), FALSE); +# elif defined(E2K) + b -> reg_base = NULL; +# endif + RESTORE_CANCEL(cancel_state); + UNLOCK(); + return GC_SUCCESS; + } +# else + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b) + { +# if defined(GET_MAIN_STACKBASE_SPECIAL) && !defined(THREADS) \ + && !defined(IA64) + b->mem_base = GC_get_main_stack_base(); + return GC_SUCCESS; +# else + UNUSED_ARG(b); + return GC_UNIMPLEMENTED; +# endif + } +# endif /* !NEED_FIND_LIMIT */ +#endif /* !HAVE_GET_STACK_BASE */ + +#ifndef GET_MAIN_STACKBASE_SPECIAL + /* This is always called from the main thread. Default implementation. */ + ptr_t GC_get_main_stack_base(void) + { + struct GC_stack_base sb; + + if (GC_get_stack_base(&sb) != GC_SUCCESS) + ABORT("GC_get_stack_base failed"); + GC_ASSERT((word)GC_approx_sp() HOTTER_THAN (word)sb.mem_base); + return (ptr_t)sb.mem_base; + } +#endif /* !GET_MAIN_STACKBASE_SPECIAL */ + +/* Register static data segment(s) as roots. If more data segments are */ +/* added later then they need to be registered at that point (as we do */ +/* with SunOS dynamic loading), or GC_mark_roots needs to check for */ +/* them (as we do with PCR). */ +# ifdef OS2 + +void GC_register_data_segments(void) +{ + PTIB ptib; + PPIB ppib; + HMODULE module_handle; +# define PBUFSIZ 512 + UCHAR path[PBUFSIZ]; + FILE * myexefile; + struct exe_hdr hdrdos; /* MSDOS header. */ + struct e32_exe hdr386; /* Real header for my executable */ + struct o32_obj seg; /* Current segment */ + int nsegs; + +# if defined(CPPCHECK) + hdrdos.padding[0] = 0; /* to prevent "field unused" warnings */ + hdr386.exe_format_level = 0; + hdr386.os = 0; + hdr386.padding1[0] = 0; + hdr386.padding2[0] = 0; + seg.pagemap = 0; + seg.mapsize = 0; + seg.reserved = 0; +# endif + if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) { + ABORT("DosGetInfoBlocks failed"); + } + module_handle = ppib -> pib_hmte; + if (DosQueryModuleName(module_handle, PBUFSIZ, path) != NO_ERROR) { + ABORT("DosQueryModuleName failed"); + } + myexefile = fopen(path, "rb"); + if (myexefile == 0) { + ABORT_ARG1("Failed to open executable", ": %s", path); + } + if (fread((char *)(&hdrdos), 1, sizeof(hdrdos), myexefile) + < sizeof(hdrdos)) { + ABORT_ARG1("Could not read MSDOS header", " from: %s", path); + } + if (E_MAGIC(hdrdos) != EMAGIC) { + ABORT_ARG1("Bad DOS magic number", " in file: %s", path); + } + if (fseek(myexefile, E_LFANEW(hdrdos), SEEK_SET) != 0) { + ABORT_ARG1("Bad DOS magic number", " in file: %s", path); + } + if (fread((char *)(&hdr386), 1, sizeof(hdr386), myexefile) + < sizeof(hdr386)) { + ABORT_ARG1("Could not read OS/2 header", " from: %s", path); + } + if (E32_MAGIC1(hdr386) != E32MAGIC1 || E32_MAGIC2(hdr386) != E32MAGIC2) { + ABORT_ARG1("Bad OS/2 magic number", " in file: %s", path); + } + if (E32_BORDER(hdr386) != E32LEBO || E32_WORDER(hdr386) != E32LEWO) { + ABORT_ARG1("Bad byte order in executable", " file: %s", path); + } + if (E32_CPU(hdr386) == E32CPU286) { + ABORT_ARG1("GC cannot handle 80286 executables", ": %s", path); + } + if (fseek(myexefile, E_LFANEW(hdrdos) + E32_OBJTAB(hdr386), + SEEK_SET) != 0) { + ABORT_ARG1("Seek to object table failed", " in file: %s", path); + } + for (nsegs = E32_OBJCNT(hdr386); nsegs > 0; nsegs--) { + int flags; + if (fread((char *)(&seg), 1, sizeof(seg), myexefile) < sizeof(seg)) { + ABORT_ARG1("Could not read obj table entry", " from file: %s", path); + } + flags = O32_FLAGS(seg); + if (!(flags & OBJWRITE)) continue; + if (!(flags & OBJREAD)) continue; + if (flags & OBJINVALID) { + GC_err_printf("Object with invalid pages?\n"); + continue; + } + GC_add_roots_inner((ptr_t)O32_BASE(seg), + (ptr_t)(O32_BASE(seg)+O32_SIZE(seg)), FALSE); + } + (void)fclose(myexefile); +} + +# else /* !OS2 */ + +# if defined(GWW_VDB) +# ifndef MEM_WRITE_WATCH +# define MEM_WRITE_WATCH 0x200000 +# endif +# ifndef WRITE_WATCH_FLAG_RESET +# define WRITE_WATCH_FLAG_RESET 1 +# endif + + /* Since we can't easily check whether ULONG_PTR and SIZE_T are */ + /* defined in Win32 basetsd.h, we define own ULONG_PTR. */ +# define GC_ULONG_PTR word + + typedef UINT (WINAPI * GetWriteWatch_type)( + DWORD, PVOID, GC_ULONG_PTR /* SIZE_T */, + PVOID *, GC_ULONG_PTR *, PULONG); + static FARPROC GetWriteWatch_func; + static DWORD GetWriteWatch_alloc_flag; + +# define GC_GWW_AVAILABLE() (GetWriteWatch_func != 0) + + static void detect_GetWriteWatch(void) + { + static GC_bool done; + HMODULE hK32; + if (done) + return; + +# if defined(MPROTECT_VDB) + { + char * str = GETENV("GC_USE_GETWRITEWATCH"); +# if defined(GC_PREFER_MPROTECT_VDB) + if (str == NULL || (*str == '0' && *(str + 1) == '\0')) { + /* GC_USE_GETWRITEWATCH is unset or set to "0". */ + done = TRUE; /* falling back to MPROTECT_VDB strategy. */ + /* This should work as if GWW_VDB is undefined. */ + return; + } +# else + if (str != NULL && *str == '0' && *(str + 1) == '\0') { + /* GC_USE_GETWRITEWATCH is set "0". */ + done = TRUE; /* falling back to MPROTECT_VDB strategy. */ + return; + } +# endif + } +# endif + +# ifdef MSWINRT_FLAVOR + { + MEMORY_BASIC_INFORMATION memInfo; + SIZE_T result = VirtualQuery((void*)(word)GetProcAddress, + &memInfo, sizeof(memInfo)); + if (result != sizeof(memInfo)) + ABORT("Weird VirtualQuery result"); + hK32 = (HMODULE)memInfo.AllocationBase; + } +# else + hK32 = GetModuleHandle(TEXT("kernel32.dll")); +# endif + if (hK32 != (HMODULE)0 && + (GetWriteWatch_func = GetProcAddress(hK32, "GetWriteWatch")) != 0) { + /* Also check whether VirtualAlloc accepts MEM_WRITE_WATCH, */ + /* as some versions of kernel32.dll have one but not the */ + /* other, making the feature completely broken. */ + void * page; + + GC_ASSERT(GC_page_size != 0); + page = VirtualAlloc(NULL, GC_page_size, MEM_WRITE_WATCH | MEM_RESERVE, + PAGE_READWRITE); + if (page != NULL) { + PVOID pages[16]; + GC_ULONG_PTR count = sizeof(pages) / sizeof(PVOID); + DWORD page_size; + /* Check that it actually works. In spite of some */ + /* documentation it actually seems to exist on Win2K. */ + /* This test may be unnecessary, but ... */ + if ((*(GetWriteWatch_type)(GC_funcptr_uint)GetWriteWatch_func)( + WRITE_WATCH_FLAG_RESET, page, + GC_page_size, pages, &count, + &page_size) != 0) { + /* GetWriteWatch always fails. */ + GetWriteWatch_func = 0; + } else { + GetWriteWatch_alloc_flag = MEM_WRITE_WATCH; + } + VirtualFree(page, 0 /* dwSize */, MEM_RELEASE); + } else { + /* GetWriteWatch will be useless. */ + GetWriteWatch_func = 0; + } + } + done = TRUE; + } + +# else +# define GetWriteWatch_alloc_flag 0 +# endif /* !GWW_VDB */ + +# ifdef ANY_MSWIN + +# ifdef MSWIN32 + /* Unfortunately, we have to handle win32s very differently from NT, */ + /* Since VirtualQuery has very different semantics. In particular, */ + /* under win32s a VirtualQuery call on an unmapped page returns an */ + /* invalid result. Under NT, GC_register_data_segments is a no-op */ + /* and all real work is done by GC_register_dynamic_libraries. Under */ + /* win32s, we cannot find the data segments associated with dll's. */ + /* We register the main data segment here. */ + GC_INNER GC_bool GC_no_win32_dlls = FALSE; + /* This used to be set for gcc, to avoid dealing with */ + /* the structured exception handling issues. But we now have */ + /* assembly code to do that right. */ + + GC_INNER GC_bool GC_wnt = FALSE; + /* This is a Windows NT derivative, i.e. NT, Win2K, XP or later. */ + + GC_INNER void GC_init_win32(void) + { +# if defined(_WIN64) || (defined(_MSC_VER) && _MSC_VER >= 1800) + /* MS Visual Studio 2013 deprecates GetVersion, but on the other */ + /* hand it cannot be used to target pre-Win2K. */ + GC_wnt = TRUE; +# else + /* Set GC_wnt. If we're running under win32s, assume that no */ + /* DLLs will be loaded. I doubt anyone still runs win32s, but... */ + DWORD v = GetVersion(); + + GC_wnt = !(v & (DWORD)0x80000000UL); + GC_no_win32_dlls |= ((!GC_wnt) && (v & 0xff) <= 3); +# endif +# ifdef USE_MUNMAP + if (GC_no_win32_dlls) { + /* Turn off unmapping for safety (since may not work well with */ + /* GlobalAlloc). */ + GC_unmap_threshold = 0; + } +# endif + } + + /* Return the smallest address a such that VirtualQuery */ + /* returns correct results for all addresses between a and start. */ + /* Assumes VirtualQuery returns correct information for start. */ + STATIC ptr_t GC_least_described_address(ptr_t start) + { + MEMORY_BASIC_INFORMATION buf; + LPVOID limit = GC_sysinfo.lpMinimumApplicationAddress; + ptr_t p = (ptr_t)((word)start & ~(word)(GC_page_size-1)); + + GC_ASSERT(GC_page_size != 0); + for (;;) { + size_t result; + LPVOID q = (LPVOID)(p - GC_page_size); + + if ((word)q > (word)p /* underflow */ || (word)q < (word)limit) break; + result = VirtualQuery(q, &buf, sizeof(buf)); + if (result != sizeof(buf) || buf.AllocationBase == 0) break; + p = (ptr_t)(buf.AllocationBase); + } + return p; + } +# endif /* MSWIN32 */ + +# if defined(USE_WINALLOC) && !defined(REDIRECT_MALLOC) + /* We maintain a linked list of AllocationBase values that we know */ + /* correspond to malloc heap sections. Currently this is only called */ + /* during a GC. But there is some hope that for long running */ + /* programs we will eventually see most heap sections. */ + + /* In the long run, it would be more reliable to occasionally walk */ + /* the malloc heap with HeapWalk on the default heap. But that */ + /* apparently works only for NT-based Windows. */ + + STATIC size_t GC_max_root_size = 100000; /* Appr. largest root size. */ + + /* In the long run, a better data structure would also be nice ... */ + STATIC struct GC_malloc_heap_list { + void * allocation_base; + struct GC_malloc_heap_list *next; + } *GC_malloc_heap_l = 0; + + /* Is p the base of one of the malloc heap sections we already know */ + /* about? */ + STATIC GC_bool GC_is_malloc_heap_base(const void *p) + { + struct GC_malloc_heap_list *q; + + for (q = GC_malloc_heap_l; q != NULL; q = q -> next) { + if (q -> allocation_base == p) return TRUE; + } + return FALSE; + } + + STATIC void *GC_get_allocation_base(void *p) + { + MEMORY_BASIC_INFORMATION buf; + size_t result = VirtualQuery(p, &buf, sizeof(buf)); + if (result != sizeof(buf)) { + ABORT("Weird VirtualQuery result"); + } + return buf.AllocationBase; + } + + GC_INNER void GC_add_current_malloc_heap(void) + { + struct GC_malloc_heap_list *new_l = (struct GC_malloc_heap_list *) + malloc(sizeof(struct GC_malloc_heap_list)); + void *candidate; + + if (NULL == new_l) return; + new_l -> allocation_base = NULL; + /* to suppress maybe-uninitialized gcc warning */ + + candidate = GC_get_allocation_base(new_l); + if (GC_is_malloc_heap_base(candidate)) { + /* Try a little harder to find malloc heap. */ + size_t req_size = 10000; + do { + void *p = malloc(req_size); + if (0 == p) { + free(new_l); + return; + } + candidate = GC_get_allocation_base(p); + free(p); + req_size *= 2; + } while (GC_is_malloc_heap_base(candidate) + && req_size < GC_max_root_size/10 && req_size < 500000); + if (GC_is_malloc_heap_base(candidate)) { + free(new_l); + return; + } + } + GC_COND_LOG_PRINTF("Found new system malloc AllocationBase at %p\n", + candidate); + new_l -> allocation_base = candidate; + new_l -> next = GC_malloc_heap_l; + GC_malloc_heap_l = new_l; + } + + /* Free all the linked list nodes. Could be invoked at process exit */ + /* to avoid memory leak complains of a dynamic code analysis tool. */ + STATIC void GC_free_malloc_heap_list(void) + { + struct GC_malloc_heap_list *q = GC_malloc_heap_l; + + GC_malloc_heap_l = NULL; + while (q != NULL) { + struct GC_malloc_heap_list *next = q -> next; + free(q); + q = next; + } + } +# endif /* USE_WINALLOC && !REDIRECT_MALLOC */ + + /* Is p the start of either the malloc heap, or of one of our */ + /* heap sections? */ + GC_INNER GC_bool GC_is_heap_base(const void *p) + { + int i; + +# if defined(USE_WINALLOC) && !defined(REDIRECT_MALLOC) + if (GC_root_size > GC_max_root_size) + GC_max_root_size = GC_root_size; + if (GC_is_malloc_heap_base(p)) + return TRUE; +# endif + for (i = 0; i < (int)GC_n_heap_bases; i++) { + if (GC_heap_bases[i] == p) return TRUE; + } + return FALSE; + } + +#ifdef MSWIN32 + STATIC void GC_register_root_section(ptr_t static_root) + { + MEMORY_BASIC_INFORMATION buf; + LPVOID p; + char * base; + char * limit; + + GC_ASSERT(I_HOLD_LOCK()); + if (!GC_no_win32_dlls) return; + p = base = limit = GC_least_described_address(static_root); + while ((word)p < (word)GC_sysinfo.lpMaximumApplicationAddress) { + size_t result = VirtualQuery(p, &buf, sizeof(buf)); + char * new_limit; + DWORD protect; + + if (result != sizeof(buf) || buf.AllocationBase == 0 + || GC_is_heap_base(buf.AllocationBase)) break; + new_limit = (char *)p + buf.RegionSize; + protect = buf.Protect; + if (buf.State == MEM_COMMIT + && is_writable(protect)) { + if ((char *)p == limit) { + limit = new_limit; + } else { + if (base != limit) GC_add_roots_inner(base, limit, FALSE); + base = (char *)p; + limit = new_limit; + } + } + if ((word)p > (word)new_limit /* overflow */) break; + p = (LPVOID)new_limit; + } + if (base != limit) GC_add_roots_inner(base, limit, FALSE); + } +#endif /* MSWIN32 */ + + void GC_register_data_segments(void) + { +# ifdef MSWIN32 + GC_register_root_section((ptr_t)&GC_pages_executable); + /* any other GC global variable would fit too. */ +# endif + } + +# else /* !ANY_MSWIN */ + +# if (defined(SVR4) || defined(AIX) || defined(DGUX)) && !defined(PCR) + ptr_t GC_SysVGetDataStart(size_t max_page_size, ptr_t etext_addr) + { + word page_offset = (word)PTRT_ROUNDUP_BY_MASK(etext_addr, sizeof(word)-1) + & ((word)max_page_size - 1); + volatile ptr_t result = PTRT_ROUNDUP_BY_MASK(etext_addr, max_page_size-1) + + page_offset; + /* Note that this isn't equivalent to just adding */ + /* max_page_size to &etext if etext is at a page boundary. */ + + GC_ASSERT(max_page_size % sizeof(word) == 0); + GC_setup_temporary_fault_handler(); + if (SETJMP(GC_jmp_buf) == 0) { + /* Try writing to the address. */ +# ifdef AO_HAVE_fetch_and_add + volatile AO_t zero = 0; + (void)AO_fetch_and_add((volatile AO_t *)result, zero); +# else + /* Fallback to non-atomic fetch-and-store. */ + char v = *result; +# if defined(CPPCHECK) + GC_noop1((word)&v); +# endif + *result = v; +# endif + GC_reset_fault_handler(); + } else { + GC_reset_fault_handler(); + /* We got here via a longjmp. The address is not readable. */ + /* This is known to happen under Solaris 2.4 + gcc, which place */ + /* string constants in the text segment, but after etext. */ + /* Use plan B. Note that we now know there is a gap between */ + /* text and data segments, so plan A brought us something. */ + result = (char *)GC_find_limit(DATAEND, FALSE); + } + return (/* no volatile */ ptr_t)(word)result; + } +# endif + +#ifdef DATASTART_USES_BSDGETDATASTART +/* It's unclear whether this should be identical to the above, or */ +/* whether it should apply to non-x86 architectures. */ +/* For now we don't assume that there is always an empty page after */ +/* etext. But in some cases there actually seems to be slightly more. */ +/* This also deals with holes between read-only data and writable data. */ + GC_INNER ptr_t GC_FreeBSDGetDataStart(size_t max_page_size, + ptr_t etext_addr) + { + volatile ptr_t result = PTRT_ROUNDUP_BY_MASK(etext_addr, sizeof(word)-1); + volatile ptr_t next_page = PTRT_ROUNDUP_BY_MASK(etext_addr, + max_page_size-1); + + GC_ASSERT(max_page_size % sizeof(word) == 0); + GC_setup_temporary_fault_handler(); + if (SETJMP(GC_jmp_buf) == 0) { + /* Try reading at the address. */ + /* This should happen before there is another thread. */ + for (; (word)next_page < (word)DATAEND; next_page += max_page_size) + GC_noop1((word)(*(volatile unsigned char *)next_page)); + GC_reset_fault_handler(); + } else { + GC_reset_fault_handler(); + /* As above, we go to plan B */ + result = (ptr_t)GC_find_limit(DATAEND, FALSE); + } + return result; + } +#endif /* DATASTART_USES_BSDGETDATASTART */ + +#ifdef AMIGA + +# define GC_AMIGA_DS + +# undef GC_AMIGA_DS + +#elif defined(OPENBSD) + +/* Depending on arch alignment, there can be multiple holes */ +/* between DATASTART and DATAEND. Scan in DATASTART .. DATAEND */ +/* and register each region. */ +void GC_register_data_segments(void) +{ + ptr_t region_start = DATASTART; + + GC_ASSERT(I_HOLD_LOCK()); + if ((word)region_start - 1U >= (word)DATAEND) + ABORT_ARG2("Wrong DATASTART/END pair", + ": %p .. %p", (void *)region_start, (void *)DATAEND); + for (;;) { + ptr_t region_end = GC_find_limit_with_bound(region_start, TRUE, DATAEND); + + GC_add_roots_inner(region_start, region_end, FALSE); + if ((word)region_end >= (word)DATAEND) + break; + region_start = GC_skip_hole_openbsd(region_end, DATAEND); + } +} + +# else /* !AMIGA && !OPENBSD */ + +# if !defined(PCR) && !defined(MACOS) && defined(REDIRECT_MALLOC) \ + && defined(GC_SOLARIS_THREADS) + EXTERN_C_BEGIN + extern caddr_t sbrk(int); + EXTERN_C_END +# endif + + void GC_register_data_segments(void) + { + GC_ASSERT(I_HOLD_LOCK()); +# if !defined(DYNAMIC_LOADING) && defined(GC_DONT_REGISTER_MAIN_STATIC_DATA) + /* Avoid even referencing DATASTART and DATAEND as they are */ + /* unnecessary and cause linker errors when bitcode is enabled. */ + /* GC_register_data_segments() is not called anyway. */ +# elif defined(PCR) || (defined(DYNAMIC_LOADING) && defined(DARWIN)) + /* No-op. GC_register_main_static_data() always returns false. */ +# elif defined(MACOS) + { +# if defined(THINK_C) + extern void *GC_MacGetDataStart(void); + + /* Globals begin above stack and end at a5. */ + GC_add_roots_inner((ptr_t)GC_MacGetDataStart(), + (ptr_t)LMGetCurrentA5(), FALSE); +# elif defined(__MWERKS__) && defined(M68K) + extern void *GC_MacGetDataStart(void); +# if __option(far_data) + extern void *GC_MacGetDataEnd(void); + + /* Handle Far Globals (CW Pro 3) located after the QD globals. */ + GC_add_roots_inner((ptr_t)GC_MacGetDataStart(), + (ptr_t)GC_MacGetDataEnd(), FALSE); +# else + GC_add_roots_inner((ptr_t)GC_MacGetDataStart(), + (ptr_t)LMGetCurrentA5(), FALSE); +# endif +# elif defined(__MWERKS__) && defined(POWERPC) + extern char __data_start__[], __data_end__[]; + + GC_add_roots_inner((ptr_t)&__data_start__, + (ptr_t)&__data_end__, FALSE); +# endif + } +# elif defined(REDIRECT_MALLOC) && defined(GC_SOLARIS_THREADS) + /* As of Solaris 2.3, the Solaris threads implementation */ + /* allocates the data structure for the initial thread with */ + /* sbrk at process startup. It needs to be scanned, so that */ + /* we don't lose some malloc allocated data structures */ + /* hanging from it. We're on thin ice here ... */ + GC_ASSERT(DATASTART); + { + ptr_t p = (ptr_t)sbrk(0); + if ((word)DATASTART < (word)p) + GC_add_roots_inner(DATASTART, p, FALSE); + } +# else + if ((word)DATASTART - 1U >= (word)DATAEND) { + /* Subtract one to check also for NULL */ + /* without a compiler warning. */ + ABORT_ARG2("Wrong DATASTART/END pair", + ": %p .. %p", (void *)DATASTART, (void *)DATAEND); + } + GC_add_roots_inner(DATASTART, DATAEND, FALSE); +# ifdef GC_HAVE_DATAREGION2 + if ((word)DATASTART2 - 1U >= (word)DATAEND2) + ABORT_ARG2("Wrong DATASTART/END2 pair", + ": %p .. %p", (void *)DATASTART2, (void *)DATAEND2); + GC_add_roots_inner(DATASTART2, DATAEND2, FALSE); +# endif +# endif + /* Dynamic libraries are added at every collection, since they may */ + /* change. */ + } + +# endif /* !AMIGA && !OPENBSD */ +# endif /* !ANY_MSWIN */ +# endif /* !OS2 */ + +/* + * Auxiliary routines for obtaining memory from OS. + */ + +#ifndef NO_UNIX_GET_MEM + +# define SBRK_ARG_T ptrdiff_t + +#if defined(MMAP_SUPPORTED) + +#ifdef USE_MMAP_FIXED +# define GC_MMAP_FLAGS MAP_FIXED | MAP_PRIVATE + /* Seems to yield better performance on Solaris 2, but can */ + /* be unreliable if something is already mapped at the address. */ +#else +# define GC_MMAP_FLAGS MAP_PRIVATE +#endif + +#ifdef USE_MMAP_ANON +# define zero_fd -1 +# if defined(MAP_ANONYMOUS) && !defined(CPPCHECK) +# define OPT_MAP_ANON MAP_ANONYMOUS +# else +# define OPT_MAP_ANON MAP_ANON +# endif +#else + static int zero_fd = -1; +# define OPT_MAP_ANON 0 +#endif + +# ifndef MSWIN_XBOX1 +# if defined(SYMBIAN) && !defined(USE_MMAP_ANON) + EXTERN_C_BEGIN + extern char *GC_get_private_path_and_zero_file(void); + EXTERN_C_END +# endif + + STATIC ptr_t GC_unix_mmap_get_mem(size_t bytes) + { + void *result; + static ptr_t last_addr = HEAP_START; + +# ifndef USE_MMAP_ANON + static GC_bool initialized = FALSE; + + if (!EXPECT(initialized, TRUE)) { +# ifdef SYMBIAN + char *path = GC_get_private_path_and_zero_file(); + if (path != NULL) { + zero_fd = open(path, O_RDWR | O_CREAT, 0644); + free(path); + } +# else + zero_fd = open("/dev/zero", O_RDONLY); +# endif + if (zero_fd == -1) + ABORT("Could not open /dev/zero"); + if (fcntl(zero_fd, F_SETFD, FD_CLOEXEC) == -1) + WARN("Could not set FD_CLOEXEC for /dev/zero\n", 0); + + initialized = TRUE; + } +# endif + + GC_ASSERT(GC_page_size != 0); + if (bytes & (GC_page_size-1)) ABORT("Bad GET_MEM arg"); + result = mmap(last_addr, bytes, (PROT_READ | PROT_WRITE) + | (GC_pages_executable ? PROT_EXEC : 0), + GC_MMAP_FLAGS | OPT_MAP_ANON, zero_fd, 0/* offset */); +# undef IGNORE_PAGES_EXECUTABLE + + if (EXPECT(MAP_FAILED == result, FALSE)) { + if (HEAP_START == last_addr && GC_pages_executable + && (EACCES == errno || EPERM == errno)) + ABORT("Cannot allocate executable pages"); + return NULL; + } + last_addr = PTRT_ROUNDUP_BY_MASK((ptr_t)result + bytes, GC_page_size-1); +# if !defined(LINUX) + if (last_addr == 0) { + /* Oops. We got the end of the address space. This isn't */ + /* usable by arbitrary C code, since one-past-end pointers */ + /* don't work, so we discard it and try again. */ + munmap(result, ~GC_page_size - (size_t)result + 1); + /* Leave last page mapped, so we can't repeat. */ + return GC_unix_mmap_get_mem(bytes); + } +# else + GC_ASSERT(last_addr != 0); +# endif + if (((word)result % HBLKSIZE) != 0) + ABORT( + "GC_unix_get_mem: Memory returned by mmap is not aligned to HBLKSIZE."); + return (ptr_t)result; + } +# endif /* !MSWIN_XBOX1 */ + +#endif /* MMAP_SUPPORTED */ + +#if defined(USE_MMAP) + ptr_t GC_unix_get_mem(size_t bytes) + { + return GC_unix_mmap_get_mem(bytes); + } +#else /* !USE_MMAP */ + +STATIC ptr_t GC_unix_sbrk_get_mem(size_t bytes) +{ + ptr_t result; +# ifdef IRIX5 + /* Bare sbrk isn't thread safe. Play by malloc rules. */ + /* The equivalent may be needed on other systems as well. */ + __LOCK_MALLOC(); +# endif + { + ptr_t cur_brk = (ptr_t)sbrk(0); + SBRK_ARG_T lsbs = (word)cur_brk & (GC_page_size-1); + + GC_ASSERT(GC_page_size != 0); + if ((SBRK_ARG_T)bytes < 0) { + result = 0; /* too big */ + goto out; + } + if (lsbs != 0) { + if ((ptr_t)sbrk((SBRK_ARG_T)GC_page_size - lsbs) == (ptr_t)(-1)) { + result = 0; + goto out; + } + } +# ifdef ADD_HEAP_GUARD_PAGES + /* This is useful for catching severe memory overwrite problems that */ + /* span heap sections. It shouldn't otherwise be turned on. */ + { + ptr_t guard = (ptr_t)sbrk((SBRK_ARG_T)GC_page_size); + if (mprotect(guard, GC_page_size, PROT_NONE) != 0) + ABORT("ADD_HEAP_GUARD_PAGES: mprotect failed"); + } +# endif /* ADD_HEAP_GUARD_PAGES */ + result = (ptr_t)sbrk((SBRK_ARG_T)bytes); + if (result == (ptr_t)(-1)) result = 0; + } + out: +# ifdef IRIX5 + __UNLOCK_MALLOC(); +# endif + return result; +} + +ptr_t GC_unix_get_mem(size_t bytes) +{ +# if defined(MMAP_SUPPORTED) + /* By default, we try both sbrk and mmap, in that order. */ + static GC_bool sbrk_failed = FALSE; + ptr_t result = 0; + + if (GC_pages_executable) { + /* If the allocated memory should have the execute permission */ + /* then sbrk() cannot be used. */ + return GC_unix_mmap_get_mem(bytes); + } + if (!sbrk_failed) result = GC_unix_sbrk_get_mem(bytes); + if (0 == result) { + sbrk_failed = TRUE; + result = GC_unix_mmap_get_mem(bytes); + } + if (0 == result) { + /* Try sbrk again, in case sbrk memory became available. */ + result = GC_unix_sbrk_get_mem(bytes); + } + return result; +# else /* !MMAP_SUPPORTED */ + return GC_unix_sbrk_get_mem(bytes); +# endif +} + +#endif /* !USE_MMAP */ + +#endif /* !NO_UNIX_GET_MEM */ + +# ifdef OS2 + +void * os2_alloc(size_t bytes) +{ + void * result; + + if (DosAllocMem(&result, bytes, (PAG_READ | PAG_WRITE | PAG_COMMIT) + | (GC_pages_executable ? PAG_EXECUTE : 0)) + != NO_ERROR) { + return NULL; + } + /* FIXME: What's the purpose of this recursion? (Probably, if */ + /* DosAllocMem returns memory at 0 address then just retry once.) */ + if (NULL == result) return os2_alloc(bytes); + return result; +} + +# endif /* OS2 */ + +#ifdef MSWIN_XBOX1 + ptr_t GC_durango_get_mem(size_t bytes) + { + if (0 == bytes) return NULL; + return (ptr_t)VirtualAlloc(NULL, bytes, MEM_COMMIT | MEM_TOP_DOWN, + PAGE_READWRITE); + } +#elif defined(MSWINCE) + ptr_t GC_wince_get_mem(size_t bytes) + { + ptr_t result = 0; /* initialized to prevent warning. */ + word i; + + GC_ASSERT(GC_page_size != 0); + bytes = ROUNDUP_PAGESIZE(bytes); + + /* Try to find reserved, uncommitted pages */ + for (i = 0; i < GC_n_heap_bases; i++) { + if (((word)(-(signed_word)GC_heap_lengths[i]) + & (GC_sysinfo.dwAllocationGranularity-1)) + >= bytes) { + result = GC_heap_bases[i] + GC_heap_lengths[i]; + break; + } + } + + if (i == GC_n_heap_bases) { + /* Reserve more pages */ + size_t res_bytes = + SIZET_SAT_ADD(bytes, (size_t)GC_sysinfo.dwAllocationGranularity-1) + & ~((size_t)GC_sysinfo.dwAllocationGranularity-1); + /* If we ever support MPROTECT_VDB here, we will probably need to */ + /* ensure that res_bytes is strictly > bytes, so that VirtualProtect */ + /* never spans regions. It seems to be OK for a VirtualFree */ + /* argument to span regions, so we should be OK for now. */ + result = (ptr_t) VirtualAlloc(NULL, res_bytes, + MEM_RESERVE | MEM_TOP_DOWN, + GC_pages_executable ? PAGE_EXECUTE_READWRITE : + PAGE_READWRITE); + if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result"); + /* If I read the documentation correctly, this can */ + /* only happen if HBLKSIZE > 64 KB or not a power of 2. */ + if (GC_n_heap_bases >= MAX_HEAP_SECTS) ABORT("Too many heap sections"); + if (result == NULL) return NULL; + GC_heap_bases[GC_n_heap_bases] = result; + GC_heap_lengths[GC_n_heap_bases] = 0; + GC_n_heap_bases++; + } + + /* Commit pages */ + result = (ptr_t) VirtualAlloc(result, bytes, MEM_COMMIT, + GC_pages_executable ? PAGE_EXECUTE_READWRITE : + PAGE_READWRITE); +# undef IGNORE_PAGES_EXECUTABLE + + if (result != NULL) { + if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result"); + GC_heap_lengths[i] += bytes; + } + return result; + } + +#elif defined(USE_WINALLOC) /* && !MSWIN_XBOX1 */ || defined(CYGWIN32) + +# ifdef USE_GLOBAL_ALLOC +# define GLOBAL_ALLOC_TEST 1 +# else +# define GLOBAL_ALLOC_TEST GC_no_win32_dlls +# endif + +# if (defined(GC_USE_MEM_TOP_DOWN) && defined(USE_WINALLOC)) \ + || defined(CPPCHECK) + DWORD GC_mem_top_down = MEM_TOP_DOWN; + /* Use GC_USE_MEM_TOP_DOWN for better 64-bit */ + /* testing. Otherwise all addresses tend to */ + /* end up in first 4 GB, hiding bugs. */ +# else +# define GC_mem_top_down 0 +# endif /* !GC_USE_MEM_TOP_DOWN */ + + ptr_t GC_win32_get_mem(size_t bytes) + { + ptr_t result; + +# ifndef USE_WINALLOC + result = GC_unix_get_mem(bytes); +# else +# if defined(MSWIN32) && !defined(MSWINRT_FLAVOR) + if (GLOBAL_ALLOC_TEST) { + /* VirtualAlloc doesn't like PAGE_EXECUTE_READWRITE. */ + /* There are also unconfirmed rumors of other */ + /* problems, so we dodge the issue. */ + result = (ptr_t)GlobalAlloc(0, SIZET_SAT_ADD(bytes, HBLKSIZE)); + /* Align it at HBLKSIZE boundary (NULL value remains unchanged). */ + result = PTRT_ROUNDUP_BY_MASK(result, HBLKSIZE-1); + } else +# endif + /* else */ { + /* VirtualProtect only works on regions returned by a */ + /* single VirtualAlloc call. Thus we allocate one */ + /* extra page, which will prevent merging of blocks */ + /* in separate regions, and eliminate any temptation */ + /* to call VirtualProtect on a range spanning regions. */ + /* This wastes a small amount of memory, and risks */ + /* increased fragmentation. But better alternatives */ + /* would require effort. */ +# ifdef MPROTECT_VDB + /* We can't check for GC_incremental here (because */ + /* GC_enable_incremental() might be called some time */ + /* later after the GC initialization). */ +# ifdef GWW_VDB +# define VIRTUAL_ALLOC_PAD (GC_GWW_AVAILABLE() ? 0 : 1) +# else +# define VIRTUAL_ALLOC_PAD 1 +# endif +# else +# define VIRTUAL_ALLOC_PAD 0 +# endif + /* Pass the MEM_WRITE_WATCH only if GetWriteWatch-based */ + /* VDBs are enabled and the GetWriteWatch function is */ + /* available. Otherwise we waste resources or possibly */ + /* cause VirtualAlloc to fail (observed in Windows 2000 */ + /* SP2). */ + result = (ptr_t) VirtualAlloc(NULL, + SIZET_SAT_ADD(bytes, VIRTUAL_ALLOC_PAD), + GetWriteWatch_alloc_flag + | (MEM_COMMIT | MEM_RESERVE) + | GC_mem_top_down, + GC_pages_executable ? PAGE_EXECUTE_READWRITE : + PAGE_READWRITE); +# undef IGNORE_PAGES_EXECUTABLE + } +# endif /* USE_WINALLOC */ + if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result"); + /* If I read the documentation correctly, this can */ + /* only happen if HBLKSIZE > 64 KB or not a power of 2. */ + if (GC_n_heap_bases >= MAX_HEAP_SECTS) ABORT("Too many heap sections"); + if (result != NULL) GC_heap_bases[GC_n_heap_bases++] = result; + return result; + } +#endif /* USE_WINALLOC || CYGWIN32 */ + +#if defined(ANY_MSWIN) || defined(MSWIN_XBOX1) + GC_API void GC_CALL GC_win32_free_heap(void) + { +# if defined(USE_WINALLOC) && !defined(REDIRECT_MALLOC) \ + && !defined(MSWIN_XBOX1) + GC_free_malloc_heap_list(); +# endif +# if (defined(USE_WINALLOC) && !defined(MSWIN_XBOX1) \ + && !defined(MSWINCE)) || defined(CYGWIN32) +# ifndef MSWINRT_FLAVOR +# ifndef CYGWIN32 + if (GLOBAL_ALLOC_TEST) +# endif + { + while (GC_n_heap_bases-- > 0) { +# ifdef CYGWIN32 + /* FIXME: Is it OK to use non-GC free() here? */ +# else + GlobalFree(GC_heap_bases[GC_n_heap_bases]); +# endif + GC_heap_bases[GC_n_heap_bases] = 0; + } + return; + } +# endif /* !MSWINRT_FLAVOR */ +# ifndef CYGWIN32 + /* Avoiding VirtualAlloc leak. */ + while (GC_n_heap_bases > 0) { + VirtualFree(GC_heap_bases[--GC_n_heap_bases], 0, MEM_RELEASE); + GC_heap_bases[GC_n_heap_bases] = 0; + } +# endif +# endif /* USE_WINALLOC || CYGWIN32 */ + } +#endif /* ANY_MSWIN || MSWIN_XBOX1 */ + +#ifdef AMIGA +# define GC_AMIGA_AM + +# undef GC_AMIGA_AM +#endif + +#if defined(HAIKU) +# ifdef GC_LEAK_DETECTOR_H +# undef posix_memalign /* to use the real one */ +# endif + ptr_t GC_haiku_get_mem(size_t bytes) + { + void* mem; + + GC_ASSERT(GC_page_size != 0); + if (posix_memalign(&mem, GC_page_size, bytes) == 0) + return mem; + return NULL; + } +#endif /* HAIKU */ + +#if (defined(USE_MUNMAP) || defined(MPROTECT_VDB)) && !defined(USE_WINALLOC) +# define ABORT_ON_REMAP_FAIL(C_msg_prefix, start_addr, len) \ + ABORT_ARG3(C_msg_prefix " failed", \ + " at %p (length %lu), errno= %d", \ + (void *)(start_addr), (unsigned long)(len), errno) +#endif + +#ifdef USE_MUNMAP + +/* For now, this only works on Win32/WinCE and some Unix-like */ +/* systems. If you have something else, don't define */ +/* USE_MUNMAP. */ + +#if !defined(NN_PLATFORM_CTR) && !defined(MSWIN32) && !defined(MSWINCE) \ + && !defined(MSWIN_XBOX1) +# ifdef SN_TARGET_PS3 +# include +# else +# include +# endif +# include +#endif + +/* Compute a page aligned starting address for the unmap */ +/* operation on a block of size bytes starting at start. */ +/* Return 0 if the block is too small to make this feasible. */ +STATIC ptr_t GC_unmap_start(ptr_t start, size_t bytes) +{ + ptr_t result; + + GC_ASSERT(GC_page_size != 0); + result = PTRT_ROUNDUP_BY_MASK(start, GC_page_size-1); + if ((word)(result + GC_page_size) > (word)(start + bytes)) return 0; + return result; +} + +/* We assume that GC_remap is called on exactly the same range */ +/* as a previous call to GC_unmap. It is safe to consistently */ +/* round the endpoints in both places. */ + +static void block_unmap_inner(ptr_t start_addr, size_t len) +{ + if (0 == start_addr) return; + +# ifdef USE_WINALLOC + /* Under Win32/WinCE we commit (map) and decommit (unmap) */ + /* memory using VirtualAlloc and VirtualFree. These functions */ + /* work on individual allocations of virtual memory, made */ + /* previously using VirtualAlloc with the MEM_RESERVE flag. */ + /* The ranges we need to (de)commit may span several of these */ + /* allocations; therefore we use VirtualQuery to check */ + /* allocation lengths, and split up the range as necessary. */ + while (len != 0) { + MEMORY_BASIC_INFORMATION mem_info; + word free_len; + + if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info)) + != sizeof(mem_info)) + ABORT("Weird VirtualQuery result"); + free_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize; + if (!VirtualFree(start_addr, free_len, MEM_DECOMMIT)) + ABORT("VirtualFree failed"); + GC_unmapped_bytes += free_len; + start_addr += free_len; + len -= free_len; + } +# else + if (len != 0) { +# ifdef SN_TARGET_PS3 + ps3_free_mem(start_addr, len); +# elif defined(AIX) || defined(CYGWIN32) || defined(HAIKU) \ + || (defined(LINUX) && !defined(PREFER_MMAP_PROT_NONE)) \ + || defined(HPUX) + /* On AIX, mmap(PROT_NONE) fails with ENOMEM unless the */ + /* environment variable XPG_SUS_ENV is set to ON. */ + /* On Cygwin, calling mmap() with the new protection flags on */ + /* an existing memory map with MAP_FIXED is broken. */ + /* However, calling mprotect() on the given address range */ + /* with PROT_NONE seems to work fine. */ + /* On Linux, low RLIMIT_AS value may lead to mmap failure. */ +# if defined(LINUX) && !defined(FORCE_MPROTECT_BEFORE_MADVISE) + /* On Linux, at least, madvise() should be sufficient. */ +# else + if (mprotect(start_addr, len, PROT_NONE)) + ABORT_ON_REMAP_FAIL("unmap: mprotect", start_addr, len); +# endif +# if !defined(CYGWIN32) + /* On Linux (and some other platforms probably), */ + /* mprotect(PROT_NONE) is just disabling access to */ + /* the pages but not returning them to OS. */ + if (madvise(start_addr, len, MADV_DONTNEED) == -1) + ABORT_ON_REMAP_FAIL("unmap: madvise", start_addr, len); +# endif +# else + /* We immediately remap it to prevent an intervening mmap() */ + /* from accidentally grabbing the same address space. */ + void * result = mmap(start_addr, len, PROT_NONE, + MAP_PRIVATE | MAP_FIXED | OPT_MAP_ANON, + zero_fd, 0/* offset */); + + if (EXPECT(MAP_FAILED == result, FALSE)) + ABORT_ON_REMAP_FAIL("unmap: mmap", start_addr, len); + if (result != (void *)start_addr) + ABORT("unmap: mmap() result differs from start_addr"); +# if defined(CPPCHECK) || defined(LINT2) + /* Explicitly store the resource handle to a global variable. */ + GC_noop1((word)result); +# endif +# endif + GC_unmapped_bytes += len; + } +# endif +} + +GC_INNER void GC_unmap(ptr_t start, size_t bytes) +{ + ptr_t start_addr = GC_unmap_start(start, bytes); + ptr_t end_addr = GC_unmap_end(start, bytes); + + block_unmap_inner(start_addr, (size_t)(end_addr - start_addr)); +} + +GC_INNER void GC_remap(ptr_t start, size_t bytes) +{ + ptr_t start_addr = GC_unmap_start(start, bytes); + ptr_t end_addr = GC_unmap_end(start, bytes); + word len = (word)(end_addr - start_addr); + if (0 == start_addr) return; + + /* FIXME: Handle out-of-memory correctly (at least for Win32) */ +# ifdef USE_WINALLOC + while (len != 0) { + MEMORY_BASIC_INFORMATION mem_info; + word alloc_len; + ptr_t result; + + if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info)) + != sizeof(mem_info)) + ABORT("Weird VirtualQuery result"); + alloc_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize; + result = (ptr_t)VirtualAlloc(start_addr, alloc_len, MEM_COMMIT, + GC_pages_executable + ? PAGE_EXECUTE_READWRITE + : PAGE_READWRITE); + if (result != start_addr) { + if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY || + GetLastError() == ERROR_OUTOFMEMORY) { + ABORT("Not enough memory to process remapping"); + } else { + ABORT("VirtualAlloc remapping failed"); + } + } +# ifdef LINT2 + GC_noop1((word)result); +# endif + GC_ASSERT(GC_unmapped_bytes >= alloc_len); + GC_unmapped_bytes -= alloc_len; + start_addr += alloc_len; + len -= alloc_len; + } +# undef IGNORE_PAGES_EXECUTABLE +# else + /* It was already remapped with PROT_NONE. */ + { +# if !defined(SN_TARGET_PS3) && !defined(FORCE_MPROTECT_BEFORE_MADVISE) \ + && defined(LINUX) && !defined(PREFER_MMAP_PROT_NONE) + /* Nothing to unprotect as madvise() is just a hint. */ +# elif defined(NACL) || defined(NETBSD) + /* NaCl does not expose mprotect, but mmap should work fine. */ + /* In case of NetBSD, mprotect fails (unlike mmap) even */ + /* without PROT_EXEC if PaX MPROTECT feature is enabled. */ + void *result = mmap(start_addr, len, (PROT_READ | PROT_WRITE) + | (GC_pages_executable ? PROT_EXEC : 0), + MAP_PRIVATE | MAP_FIXED | OPT_MAP_ANON, + zero_fd, 0 /* offset */); + if (EXPECT(MAP_FAILED == result, FALSE)) + ABORT_ON_REMAP_FAIL("remap: mmap", start_addr, len); + if (result != (void *)start_addr) + ABORT("remap: mmap() result differs from start_addr"); +# if defined(CPPCHECK) || defined(LINT2) + GC_noop1((word)result); +# endif +# undef IGNORE_PAGES_EXECUTABLE +# else + if (mprotect(start_addr, len, (PROT_READ | PROT_WRITE) + | (GC_pages_executable ? PROT_EXEC : 0))) + ABORT_ON_REMAP_FAIL("remap: mprotect", start_addr, len); +# undef IGNORE_PAGES_EXECUTABLE +# endif /* !NACL */ + } + GC_ASSERT(GC_unmapped_bytes >= len); + GC_unmapped_bytes -= len; +# endif +} + +/* Two adjacent blocks have already been unmapped and are about to */ +/* be merged. Unmap the whole block. This typically requires */ +/* that we unmap a small section in the middle that was not previously */ +/* unmapped due to alignment constraints. */ +GC_INNER void GC_unmap_gap(ptr_t start1, size_t bytes1, ptr_t start2, + size_t bytes2) +{ + ptr_t start1_addr = GC_unmap_start(start1, bytes1); + ptr_t end1_addr = GC_unmap_end(start1, bytes1); + ptr_t start2_addr = GC_unmap_start(start2, bytes2); + ptr_t start_addr = end1_addr; + ptr_t end_addr = start2_addr; + + GC_ASSERT(start1 + bytes1 == start2); + if (0 == start1_addr) start_addr = GC_unmap_start(start1, bytes1 + bytes2); + if (0 == start2_addr) end_addr = GC_unmap_end(start1, bytes1 + bytes2); + block_unmap_inner(start_addr, (size_t)(end_addr - start_addr)); +} + +#endif /* USE_MUNMAP */ + +/* Routine for pushing any additional roots. In THREADS */ +/* environment, this is also responsible for marking from */ +/* thread stacks. */ +#ifndef THREADS + +# if defined(EMSCRIPTEN) && defined(EMSCRIPTEN_ASYNCIFY) +# include + + static void scan_regs_cb(void *begin, void *end) + { + GC_push_all_stack((ptr_t)begin, (ptr_t)end); + } + + STATIC void GC_CALLBACK GC_default_push_other_roots(void) + { + /* Note: this needs -sASYNCIFY linker flag. */ + emscripten_scan_registers(scan_regs_cb); + } + +# else +# define GC_default_push_other_roots 0 +# endif + +#else /* THREADS */ + +# ifdef PCR +PCR_ERes GC_push_thread_stack(PCR_Th_T *t, PCR_Any dummy) +{ + struct PCR_ThCtl_TInfoRep info; + PCR_ERes result; + + info.ti_stkLow = info.ti_stkHi = 0; + result = PCR_ThCtl_GetInfo(t, &info); + GC_push_all_stack((ptr_t)(info.ti_stkLow), (ptr_t)(info.ti_stkHi)); + return result; +} + +/* Push the contents of an old object. We treat this as stack */ +/* data only because that makes it robust against mark stack */ +/* overflow. */ +PCR_ERes GC_push_old_obj(void *p, size_t size, PCR_Any data) +{ + GC_push_all_stack((ptr_t)p, (ptr_t)p + size); + return PCR_ERes_okay; +} + +extern struct PCR_MM_ProcsRep * GC_old_allocator; + /* defined in pcr_interface.c. */ + +STATIC void GC_CALLBACK GC_default_push_other_roots(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + /* Traverse data allocated by previous memory managers. */ + if ((*(GC_old_allocator->mmp_enumerate))(PCR_Bool_false, + GC_push_old_obj, 0) + != PCR_ERes_okay) { + ABORT("Old object enumeration failed"); + } + /* Traverse all thread stacks. */ + if (PCR_ERes_IsErr( + PCR_ThCtl_ApplyToAllOtherThreads(GC_push_thread_stack,0)) + || PCR_ERes_IsErr(GC_push_thread_stack(PCR_Th_CurrThread(), 0))) { + ABORT("Thread stack marking failed"); + } +} + +# elif defined(SN_TARGET_PS3) + STATIC void GC_CALLBACK GC_default_push_other_roots(void) + { + ABORT("GC_default_push_other_roots is not implemented"); + } + + void GC_push_thread_structures(void) + { + ABORT("GC_push_thread_structures is not implemented"); + } + +# else /* GC_PTHREADS, or GC_WIN32_THREADS, etc. */ + STATIC void GC_CALLBACK GC_default_push_other_roots(void) + { + GC_push_all_stacks(); + } +# endif + +#endif /* THREADS */ + +GC_push_other_roots_proc GC_push_other_roots = GC_default_push_other_roots; + +GC_API void GC_CALL GC_set_push_other_roots(GC_push_other_roots_proc fn) +{ + GC_push_other_roots = fn; +} + +GC_API GC_push_other_roots_proc GC_CALL GC_get_push_other_roots(void) +{ + return GC_push_other_roots; +} + +#if defined(SOFT_VDB) && !defined(NO_SOFT_VDB_LINUX_VER_RUNTIME_CHECK) \ + || (defined(GLIBC_2_19_TSX_BUG) && defined(GC_PTHREADS_PARAMARK)) + GC_INNER int GC_parse_version(int *pminor, const char *pverstr) { + char *endp; + unsigned long value = strtoul(pverstr, &endp, 10); + int major = (int)value; + + if (major < 0 || (char *)pverstr == endp || (unsigned)major != value) { + /* Parse error. */ + return -1; + } + if (*endp != '.') { + /* No minor part. */ + *pminor = -1; + } else { + value = strtoul(endp + 1, &endp, 10); + *pminor = (int)value; + if (*pminor < 0 || (unsigned)(*pminor) != value) { + return -1; + } + } + return major; + } +#endif + +/* + * Routines for accessing dirty bits on virtual pages. + * There are six ways to maintain this information: + * DEFAULT_VDB: A simple dummy implementation that treats every page + * as possibly dirty. This makes incremental collection + * useless, but the implementation is still correct. + * Manual VDB: Stacks and static data are always considered dirty. + * Heap pages are considered dirty if GC_dirty(p) has been + * called on some pointer p pointing to somewhere inside + * an object on that page. A GC_dirty() call on a large + * object directly dirties only a single page, but for the + * manual VDB we are careful to treat an object with a dirty + * page as completely dirty. + * In order to avoid races, an object must be marked dirty + * after it is written, and a reference to the object + * must be kept on a stack or in a register in the interim. + * With threads enabled, an object directly reachable from the + * stack at the time of a collection is treated as dirty. + * In single-threaded mode, it suffices to ensure that no + * collection can take place between the pointer assignment + * and the GC_dirty() call. + * PCR_VDB: Use PPCRs virtual dirty bit facility. + * PROC_VDB: Use the /proc facility for reading dirty bits. Only + * works under some SVR4 variants. Even then, it may be + * too slow to be entirely satisfactory. Requires reading + * dirty bits for entire address space. Implementations tend + * to assume that the client is a (slow) debugger. + * SOFT_VDB: Use the /proc facility for reading soft-dirty PTEs. + * Works on Linux 3.18+ if the kernel is properly configured. + * The proposed implementation iterates over GC_heap_sects and + * GC_static_roots examining the soft-dirty bit of the words + * in /proc/self/pagemap corresponding to the pages of the + * sections; finally all soft-dirty bits of the process are + * cleared (by writing some special value to + * /proc/self/clear_refs file). In case the soft-dirty bit is + * not supported by the kernel, MPROTECT_VDB may be defined as + * a fallback strategy. + * MPROTECT_VDB:Protect pages and then catch the faults to keep track of + * dirtied pages. The implementation (and implementability) + * is highly system dependent. This usually fails when system + * calls write to a protected page. We prevent the read system + * call from doing so. It is the clients responsibility to + * make sure that other system calls are similarly protected + * or write only to the stack. + * GWW_VDB: Use the Win32 GetWriteWatch functions, if available, to + * read dirty bits. In case it is not available (because we + * are running on Windows 95, Windows 2000 or earlier), + * MPROTECT_VDB may be defined as a fallback strategy. + */ + +#if (defined(CHECKSUMS) && defined(GWW_VDB)) || defined(PROC_VDB) + /* Add all pages in pht2 to pht1. */ + STATIC void GC_or_pages(page_hash_table pht1, const word *pht2) + { + unsigned i; + for (i = 0; i < PHT_SIZE; i++) pht1[i] |= pht2[i]; + } +#endif /* CHECKSUMS && GWW_VDB || PROC_VDB */ + +#ifdef GWW_VDB + +# define GC_GWW_BUF_LEN (MAXHINCR * HBLKSIZE / 4096 /* x86 page size */) + /* Still susceptible to overflow, if there are very large allocations, */ + /* and everything is dirty. */ + static PVOID gww_buf[GC_GWW_BUF_LEN]; + +# ifndef MPROTECT_VDB +# define GC_gww_dirty_init GC_dirty_init +# endif + + GC_INNER GC_bool GC_gww_dirty_init(void) + { + /* No assumption about the allocator lock. */ + detect_GetWriteWatch(); + return GC_GWW_AVAILABLE(); + } + + GC_INLINE void GC_gww_read_dirty(GC_bool output_unneeded) + { + word i; + + GC_ASSERT(I_HOLD_LOCK()); + if (!output_unneeded) + BZERO(GC_grungy_pages, sizeof(GC_grungy_pages)); + + for (i = 0; i != GC_n_heap_sects; ++i) { + GC_ULONG_PTR count; + + do { + PVOID * pages = gww_buf; + DWORD page_size; + + count = GC_GWW_BUF_LEN; + /* GetWriteWatch is documented as returning non-zero when it */ + /* fails, but the documentation doesn't explicitly say why it */ + /* would fail or what its behavior will be if it fails. It */ + /* does appear to fail, at least on recent Win2K instances, if */ + /* the underlying memory was not allocated with the appropriate */ + /* flag. This is common if GC_enable_incremental is called */ + /* shortly after GC initialization. To avoid modifying the */ + /* interface, we silently work around such a failure, it only */ + /* affects the initial (small) heap allocation. If there are */ + /* more dirty pages than will fit in the buffer, this is not */ + /* treated as a failure; we must check the page count in the */ + /* loop condition. Since each partial call will reset the */ + /* status of some pages, this should eventually terminate even */ + /* in the overflow case. */ + if ((*(GetWriteWatch_type)(GC_funcptr_uint)GetWriteWatch_func)( + WRITE_WATCH_FLAG_RESET, + GC_heap_sects[i].hs_start, + GC_heap_sects[i].hs_bytes, + pages, &count, &page_size) != 0) { + static int warn_count = 0; + struct hblk * start = (struct hblk *)GC_heap_sects[i].hs_start; + static struct hblk *last_warned = 0; + size_t nblocks = divHBLKSZ(GC_heap_sects[i].hs_bytes); + + if (i != 0 && last_warned != start && warn_count++ < 5) { + last_warned = start; + WARN("GC_gww_read_dirty unexpectedly failed at %p:" + " Falling back to marking all pages dirty\n", start); + } + if (!output_unneeded) { + unsigned j; + + for (j = 0; j < nblocks; ++j) { + word hash = PHT_HASH(start + j); + set_pht_entry_from_index(GC_grungy_pages, hash); + } + } + count = 1; /* Done with this section. */ + } else /* succeeded */ if (!output_unneeded) { + PVOID * pages_end = pages + count; + + while (pages != pages_end) { + struct hblk * h = (struct hblk *) *pages++; + struct hblk * h_end = (struct hblk *) ((char *) h + page_size); + do { + set_pht_entry_from_index(GC_grungy_pages, PHT_HASH(h)); + } while ((word)(++h) < (word)h_end); + } + } + } while (count == GC_GWW_BUF_LEN); + /* FIXME: It's unclear from Microsoft's documentation if this loop */ + /* is useful. We suspect the call just fails if the buffer fills */ + /* up. But that should still be handled correctly. */ + } + +# ifdef CHECKSUMS + GC_ASSERT(!output_unneeded); + GC_or_pages(GC_written_pages, GC_grungy_pages); +# endif + } + +#elif defined(SOFT_VDB) + static int clear_refs_fd = -1; +# define GC_GWW_AVAILABLE() (clear_refs_fd != -1) +#else +# define GC_GWW_AVAILABLE() FALSE +#endif /* !GWW_VDB && !SOFT_VDB */ + +#ifdef DEFAULT_VDB + /* The client asserts that unallocated pages in the heap are never */ + /* written. */ + + /* Initialize virtual dirty bit implementation. */ + GC_INNER GC_bool GC_dirty_init(void) + { + GC_VERBOSE_LOG_PRINTF("Initializing DEFAULT_VDB...\n"); + /* GC_dirty_pages and GC_grungy_pages are already cleared. */ + return TRUE; + } +#endif /* DEFAULT_VDB */ + +#if !defined(NO_MANUAL_VDB) || defined(MPROTECT_VDB) +# if !defined(THREADS) || defined(HAVE_LOCKFREE_AO_OR) +# ifdef MPROTECT_VDB +# define async_set_pht_entry_from_index(db, index) \ + set_pht_entry_from_index_concurrent_volatile(db, index) +# else +# define async_set_pht_entry_from_index(db, index) \ + set_pht_entry_from_index_concurrent(db, index) +# endif +# elif defined(AO_HAVE_test_and_set_acquire) + /* We need to lock around the bitmap update (in the write fault */ + /* handler or GC_dirty) in order to avoid the risk of losing a bit. */ + /* We do this with a test-and-set spin lock if possible. */ + GC_INNER volatile AO_TS_t GC_fault_handler_lock = AO_TS_INITIALIZER; + + static void async_set_pht_entry_from_index(volatile page_hash_table db, + size_t index) + { + GC_acquire_dirty_lock(); + set_pht_entry_from_index(db, index); + GC_release_dirty_lock(); + } +# else +# error No test_and_set operation: Introduces a race. +# endif /* THREADS && !AO_HAVE_test_and_set_acquire */ +#endif /* !NO_MANUAL_VDB || MPROTECT_VDB */ + +#ifdef MPROTECT_VDB + /* + * This implementation maintains dirty bits itself by catching write + * faults and keeping track of them. We assume nobody else catches + * SIGBUS or SIGSEGV. We assume no write faults occur in system calls. + * This means that clients must ensure that system calls don't write + * to the write-protected heap. Probably the best way to do this is to + * ensure that system calls write at most to pointer-free objects in the + * heap, and do even that only if we are on a platform on which those + * are not protected. Another alternative is to wrap system calls + * (see example for read below), but the current implementation holds + * applications. + * We assume the page size is a multiple of HBLKSIZE. + * We prefer them to be the same. We avoid protecting pointer-free + * objects only if they are the same. + */ +# ifdef DARWIN + /* #define BROKEN_EXCEPTION_HANDLING */ + + /* Using vm_protect (mach syscall) over mprotect (BSD syscall) seems to + decrease the likelihood of some of the problems described below. */ +# include + STATIC mach_port_t GC_task_self = 0; +# define PROTECT_INNER(addr, len, allow_write, C_msg_prefix) \ + if (vm_protect(GC_task_self, (vm_address_t)(addr), (vm_size_t)(len), \ + FALSE, VM_PROT_READ \ + | ((allow_write) ? VM_PROT_WRITE : 0) \ + | (GC_pages_executable ? VM_PROT_EXECUTE : 0)) \ + == KERN_SUCCESS) {} else ABORT(C_msg_prefix \ + "vm_protect() failed") + +# elif !defined(USE_WINALLOC) +# include +# if !defined(AIX) && !defined(CYGWIN32) && !defined(HAIKU) +# include +# endif + +# define PROTECT_INNER(addr, len, allow_write, C_msg_prefix) \ + if (mprotect((caddr_t)(addr), (size_t)(len), \ + PROT_READ | ((allow_write) ? PROT_WRITE : 0) \ + | (GC_pages_executable ? PROT_EXEC : 0)) >= 0) { \ + } else if (GC_pages_executable) { \ + ABORT_ON_REMAP_FAIL(C_msg_prefix \ + "mprotect vdb executable pages", \ + addr, len); \ + } else ABORT_ON_REMAP_FAIL(C_msg_prefix "mprotect vdb", addr, len) +# undef IGNORE_PAGES_EXECUTABLE + +# else /* USE_WINALLOC */ + static DWORD protect_junk; +# define PROTECT_INNER(addr, len, allow_write, C_msg_prefix) \ + if (VirtualProtect(addr, len, \ + GC_pages_executable ? \ + ((allow_write) ? PAGE_EXECUTE_READWRITE : \ + PAGE_EXECUTE_READ) : \ + (allow_write) ? PAGE_READWRITE : \ + PAGE_READONLY, \ + &protect_junk)) { \ + } else ABORT_ARG1(C_msg_prefix "VirtualProtect failed", \ + ": errcode= 0x%X", (unsigned)GetLastError()) +# endif /* USE_WINALLOC */ + +# define PROTECT(addr, len) PROTECT_INNER(addr, len, FALSE, "") +# define UNPROTECT(addr, len) PROTECT_INNER(addr, len, TRUE, "un-") + +# if defined(MSWIN32) + typedef LPTOP_LEVEL_EXCEPTION_FILTER SIG_HNDLR_PTR; +# undef SIG_DFL +# define SIG_DFL ((LPTOP_LEVEL_EXCEPTION_FILTER)~(GC_funcptr_uint)0) +# elif defined(MSWINCE) + typedef LONG (WINAPI *SIG_HNDLR_PTR)(struct _EXCEPTION_POINTERS *); +# undef SIG_DFL +# define SIG_DFL ((SIG_HNDLR_PTR)~(GC_funcptr_uint)0) +# elif defined(DARWIN) +# ifdef BROKEN_EXCEPTION_HANDLING + typedef void (*SIG_HNDLR_PTR)(); +# endif +# else + typedef void (*SIG_HNDLR_PTR)(int, siginfo_t *, void *); + typedef void (*PLAIN_HNDLR_PTR)(int); +# endif /* !DARWIN && !MSWIN32 && !MSWINCE */ + +#ifndef DARWIN + STATIC SIG_HNDLR_PTR GC_old_segv_handler = 0; + /* Also old MSWIN32 ACCESS_VIOLATION filter */ +# ifdef USE_BUS_SIGACT + STATIC SIG_HNDLR_PTR GC_old_bus_handler = 0; + STATIC GC_bool GC_old_bus_handler_used_si = FALSE; +# endif +# if !defined(MSWIN32) && !defined(MSWINCE) + STATIC GC_bool GC_old_segv_handler_used_si = FALSE; +# endif /* !MSWIN32 */ +#endif /* !DARWIN */ + +#ifdef THREADS + /* This function is used only by the fault handler. Potential data */ + /* race between this function and GC_install_header, GC_remove_header */ + /* should not be harmful because the added or removed header should */ + /* be already unprotected. */ + GC_ATTR_NO_SANITIZE_THREAD + static GC_bool is_header_found_async(void *addr) + { +# ifdef HASH_TL + hdr *result; + GET_HDR((ptr_t)addr, result); + return result != NULL; +# else + return HDR_INNER(addr) != NULL; +# endif + } +#else +# define is_header_found_async(addr) (HDR(addr) != NULL) +#endif /* !THREADS */ + +#ifndef DARWIN + +# if !defined(MSWIN32) && !defined(MSWINCE) +# include +# ifdef USE_BUS_SIGACT +# define SIG_OK (sig == SIGBUS || sig == SIGSEGV) +# else +# define SIG_OK (sig == SIGSEGV) + /* Catch SIGSEGV but ignore SIGBUS. */ +# endif +# if defined(FREEBSD) || defined(OPENBSD) +# ifndef SEGV_ACCERR +# define SEGV_ACCERR 2 +# endif +# if defined(AARCH64) || defined(ARM32) || defined(MIPS) \ + || (__FreeBSD__ >= 7 || defined(OPENBSD)) +# define CODE_OK (si -> si_code == SEGV_ACCERR) +# elif defined(POWERPC) +# define AIM /* Pretend that we're AIM. */ +# include +# define CODE_OK (si -> si_code == EXC_DSI \ + || si -> si_code == SEGV_ACCERR) +# else +# define CODE_OK (si -> si_code == BUS_PAGE_FAULT \ + || si -> si_code == SEGV_ACCERR) +# endif +# elif defined(OSF1) +# define CODE_OK (si -> si_code == 2 /* experimentally determined */) +# elif defined(IRIX5) +# define CODE_OK (si -> si_code == EACCES) +# elif defined(AIX) || defined(CYGWIN32) || defined(HAIKU) || defined(HURD) +# define CODE_OK TRUE +# elif defined(LINUX) +# define CODE_OK TRUE + /* Empirically c.trapno == 14, on IA32, but is that useful? */ + /* Should probably consider alignment issues on other */ + /* architectures. */ +# elif defined(HPUX) +# define CODE_OK (si -> si_code == SEGV_ACCERR \ + || si -> si_code == BUS_ADRERR \ + || si -> si_code == BUS_UNKNOWN \ + || si -> si_code == SEGV_UNKNOWN \ + || si -> si_code == BUS_OBJERR) +# elif defined(SUNOS5SIGS) +# define CODE_OK (si -> si_code == SEGV_ACCERR) +# endif +# ifndef NO_GETCONTEXT +# include +# endif + STATIC void GC_write_fault_handler(int sig, siginfo_t *si, void *raw_sc) +# else +# define SIG_OK (exc_info -> ExceptionRecord -> ExceptionCode \ + == STATUS_ACCESS_VIOLATION) +# define CODE_OK (exc_info -> ExceptionRecord -> ExceptionInformation[0] \ + == 1) /* Write fault */ + STATIC LONG WINAPI GC_write_fault_handler( + struct _EXCEPTION_POINTERS *exc_info) +# endif /* MSWIN32 || MSWINCE */ + { +# if !defined(MSWIN32) && !defined(MSWINCE) + char *addr = (char *)si->si_addr; +# else + char * addr = (char *) (exc_info -> ExceptionRecord + -> ExceptionInformation[1]); +# endif + + if (SIG_OK && CODE_OK) { + struct hblk * h = (struct hblk *)((word)addr + & ~(word)(GC_page_size-1)); + GC_bool in_allocd_block; + size_t i; + + GC_ASSERT(GC_page_size != 0); +# ifdef CHECKSUMS + GC_record_fault(h); +# endif +# ifdef SUNOS5SIGS + /* Address is only within the correct physical page. */ + in_allocd_block = FALSE; + for (i = 0; i < divHBLKSZ(GC_page_size); i++) { + if (is_header_found_async(&h[i])) { + in_allocd_block = TRUE; + break; + } + } +# else + in_allocd_block = is_header_found_async(addr); +# endif + if (!in_allocd_block) { + /* FIXME: We should make sure that we invoke the */ + /* old handler with the appropriate calling */ + /* sequence, which often depends on SA_SIGINFO. */ + + /* Heap blocks now begin and end on page boundaries */ + SIG_HNDLR_PTR old_handler; + +# if defined(MSWIN32) || defined(MSWINCE) + old_handler = GC_old_segv_handler; +# else + GC_bool used_si; + +# ifdef USE_BUS_SIGACT + if (sig == SIGBUS) { + old_handler = GC_old_bus_handler; + used_si = GC_old_bus_handler_used_si; + } else +# endif + /* else */ { + old_handler = GC_old_segv_handler; + used_si = GC_old_segv_handler_used_si; + } +# endif + + if ((GC_funcptr_uint)old_handler == (GC_funcptr_uint)SIG_DFL) { +# if !defined(MSWIN32) && !defined(MSWINCE) + ABORT_ARG1("Unexpected segmentation fault outside heap", + " at %p", (void *)addr); +# else + return EXCEPTION_CONTINUE_SEARCH; +# endif + } else { + /* + * FIXME: This code should probably check if the + * old signal handler used the traditional style and + * if so call it using that style. + */ +# if defined(MSWIN32) || defined(MSWINCE) + return (*old_handler)(exc_info); +# else + if (used_si) + ((SIG_HNDLR_PTR)old_handler)(sig, si, raw_sc); + else + /* FIXME: should pass nonstandard args as well. */ + ((PLAIN_HNDLR_PTR)(GC_funcptr_uint)old_handler)(sig); + return; +# endif + } + } + UNPROTECT(h, GC_page_size); + /* We need to make sure that no collection occurs between */ + /* the UNPROTECT and the setting of the dirty bit. Otherwise */ + /* a write by a third thread might go unnoticed. Reversing */ + /* the order is just as bad, since we would end up unprotecting */ + /* a page in a GC cycle during which it's not marked. */ + /* Currently we do this by disabling the thread stopping */ + /* signals while this handler is running. An alternative might */ + /* be to record the fact that we're about to unprotect, or */ + /* have just unprotected a page in the GC's thread structure, */ + /* and then to have the thread stopping code set the dirty */ + /* flag, if necessary. */ + for (i = 0; i < divHBLKSZ(GC_page_size); i++) { + word index = PHT_HASH(h+i); + + async_set_pht_entry_from_index(GC_dirty_pages, index); + } + /* The write may not take place before dirty bits are read. */ + /* But then we'll fault again ... */ +# if defined(MSWIN32) || defined(MSWINCE) + return EXCEPTION_CONTINUE_EXECUTION; +# else + return; +# endif + } +# if defined(MSWIN32) || defined(MSWINCE) + return EXCEPTION_CONTINUE_SEARCH; +# else + ABORT_ARG1("Unexpected bus error or segmentation fault", + " at %p", (void *)addr); +# endif + } + +# if defined(GC_WIN32_THREADS) && !defined(CYGWIN32) + GC_INNER void GC_set_write_fault_handler(void) + { + SetUnhandledExceptionFilter(GC_write_fault_handler); + } +# endif + +# ifdef SOFT_VDB + static GC_bool soft_dirty_init(void); +# endif + + GC_INNER GC_bool GC_dirty_init(void) + { +# if !defined(MSWIN32) && !defined(MSWINCE) + struct sigaction act, oldact; +# endif + + GC_ASSERT(I_HOLD_LOCK()); +# if !defined(MSWIN32) && !defined(MSWINCE) + act.sa_flags = SA_RESTART | SA_SIGINFO; + act.sa_sigaction = GC_write_fault_handler; + (void)sigemptyset(&act.sa_mask); +# ifdef SIGNAL_BASED_STOP_WORLD + /* Arrange to postpone the signal while we are in a write fault */ + /* handler. This effectively makes the handler atomic w.r.t. */ + /* stopping the world for GC. */ + (void)sigaddset(&act.sa_mask, GC_get_suspend_signal()); +# endif +# endif /* !MSWIN32 */ + GC_VERBOSE_LOG_PRINTF( + "Initializing mprotect virtual dirty bit implementation\n"); + if (GC_page_size % HBLKSIZE != 0) { + ABORT("Page size not multiple of HBLKSIZE"); + } +# ifdef GWW_VDB + if (GC_gww_dirty_init()) { + GC_COND_LOG_PRINTF("Using GetWriteWatch()\n"); + return TRUE; + } +# elif defined(SOFT_VDB) +# ifdef CHECK_SOFT_VDB + if (!soft_dirty_init()) + ABORT("Soft-dirty bit support is missing"); +# else + if (soft_dirty_init()) { + GC_COND_LOG_PRINTF("Using soft-dirty bit feature\n"); + return TRUE; + } +# endif +# endif +# ifdef MSWIN32 + GC_old_segv_handler = SetUnhandledExceptionFilter( + GC_write_fault_handler); + if (GC_old_segv_handler != NULL) { + GC_COND_LOG_PRINTF("Replaced other UnhandledExceptionFilter\n"); + } else { + GC_old_segv_handler = SIG_DFL; + } +# elif defined(MSWINCE) + /* MPROTECT_VDB is unsupported for WinCE at present. */ + /* FIXME: implement it (if possible). */ +# else + /* act.sa_restorer is deprecated and should not be initialized. */ +# if defined(GC_IRIX_THREADS) + sigaction(SIGSEGV, 0, &oldact); + sigaction(SIGSEGV, &act, 0); +# else + { + int res = sigaction(SIGSEGV, &act, &oldact); + if (res != 0) ABORT("Sigaction failed"); + } +# endif + if (oldact.sa_flags & SA_SIGINFO) { + GC_old_segv_handler = oldact.sa_sigaction; + GC_old_segv_handler_used_si = TRUE; + } else { + GC_old_segv_handler = + (SIG_HNDLR_PTR)(GC_funcptr_uint)oldact.sa_handler; + GC_old_segv_handler_used_si = FALSE; + } + if ((GC_funcptr_uint)GC_old_segv_handler == (GC_funcptr_uint)SIG_IGN) { + WARN("Previously ignored segmentation violation!?\n", 0); + GC_old_segv_handler = (SIG_HNDLR_PTR)(GC_funcptr_uint)SIG_DFL; + } + if ((GC_funcptr_uint)GC_old_segv_handler != (GC_funcptr_uint)SIG_DFL) { + GC_VERBOSE_LOG_PRINTF("Replaced other SIGSEGV handler\n"); + } +# ifdef USE_BUS_SIGACT + sigaction(SIGBUS, &act, &oldact); + if ((oldact.sa_flags & SA_SIGINFO) != 0) { + GC_old_bus_handler = oldact.sa_sigaction; + GC_old_bus_handler_used_si = TRUE; + } else { + GC_old_bus_handler = + (SIG_HNDLR_PTR)(GC_funcptr_uint)oldact.sa_handler; + } + if ((GC_funcptr_uint)GC_old_bus_handler == (GC_funcptr_uint)SIG_IGN) { + WARN("Previously ignored bus error!?\n", 0); + GC_old_bus_handler = (SIG_HNDLR_PTR)(GC_funcptr_uint)SIG_DFL; + } else if ((GC_funcptr_uint)GC_old_bus_handler + != (GC_funcptr_uint)SIG_DFL) { + GC_VERBOSE_LOG_PRINTF("Replaced other SIGBUS handler\n"); + } +# endif +# endif /* !MSWIN32 && !MSWINCE */ +# if defined(CPPCHECK) && defined(ADDRESS_SANITIZER) + GC_noop1((word)&__asan_default_options); +# endif + return TRUE; + } +#endif /* !DARWIN */ + +#define PAGE_ALIGNED(x) !((word)(x) & (GC_page_size-1)) + +STATIC void GC_protect_heap(void) +{ + unsigned i; + GC_bool protect_all = + (0 != (GC_incremental_protection_needs() & GC_PROTECTS_PTRFREE_HEAP)); + + GC_ASSERT(GC_page_size != 0); + for (i = 0; i < GC_n_heap_sects; i++) { + ptr_t start = GC_heap_sects[i].hs_start; + size_t len = GC_heap_sects[i].hs_bytes; + + if (protect_all) { + PROTECT(start, len); + } else { + struct hblk * current; + struct hblk * current_start; /* Start of block to be protected. */ + struct hblk * limit; + + GC_ASSERT(PAGE_ALIGNED(len)); + GC_ASSERT(PAGE_ALIGNED(start)); + current_start = current = (struct hblk *)start; + limit = (struct hblk *)(start + len); + while ((word)current < (word)limit) { + hdr * hhdr; + word nhblks; + GC_bool is_ptrfree; + + GC_ASSERT(PAGE_ALIGNED(current)); + GET_HDR(current, hhdr); + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + /* This can happen only if we're at the beginning of a */ + /* heap segment, and a block spans heap segments. */ + /* We will handle that block as part of the preceding */ + /* segment. */ + GC_ASSERT(current_start == current); + current_start = ++current; + continue; + } + if (HBLK_IS_FREE(hhdr)) { + GC_ASSERT(PAGE_ALIGNED(hhdr -> hb_sz)); + nhblks = divHBLKSZ(hhdr -> hb_sz); + is_ptrfree = TRUE; /* dirty on alloc */ + } else { + nhblks = OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); + is_ptrfree = IS_PTRFREE(hhdr); + } + if (is_ptrfree) { + if ((word)current_start < (word)current) { + PROTECT(current_start, (ptr_t)current - (ptr_t)current_start); + } + current_start = (current += nhblks); + } else { + current += nhblks; + } + } + if ((word)current_start < (word)current) { + PROTECT(current_start, (ptr_t)current - (ptr_t)current_start); + } + } + } +} + +/* + * Acquiring the allocator lock here is dangerous, since this + * can be called from within GC_call_with_alloc_lock, and the cord + * package does so. On systems that allow nested lock acquisition, this + * happens to work. + */ + +/* We no longer wrap read by default, since that was causing too many */ +/* problems. It is preferred that the client instead avoids writing */ +/* to the write-protected heap with a system call. */ +#endif /* MPROTECT_VDB */ + +#if !defined(THREADS) && (defined(PROC_VDB) || defined(SOFT_VDB)) + static pid_t saved_proc_pid; /* pid used to compose /proc file names */ +#endif + +#ifdef PROC_VDB +/* This implementation assumes a Solaris 2.X like /proc */ +/* pseudo-file-system from which we can read page modified bits. This */ +/* facility is far from optimal (e.g. we would like to get the info for */ +/* only some of the address space), but it avoids intercepting system */ +/* calls. */ + +# include +# include +# include +# include + +# ifdef GC_NO_SYS_FAULT_H + /* This exists only to check PROC_VDB code compilation (on Linux). */ +# define PG_MODIFIED 1 + struct prpageheader { + int dummy[2]; /* pr_tstamp */ + unsigned long pr_nmap; + unsigned long pr_npage; + }; + struct prasmap { + char *pr_vaddr; + size_t pr_npage; + char dummy1[64+8]; /* pr_mapname, pr_offset */ + unsigned pr_mflags; + unsigned pr_pagesize; + int dummy2[2]; + }; +# else +# include +# include +# endif + +# define INITIAL_BUF_SZ 16384 + STATIC size_t GC_proc_buf_size = INITIAL_BUF_SZ; + STATIC char *GC_proc_buf = NULL; + STATIC int GC_proc_fd = -1; + + static GC_bool proc_dirty_open_files(void) + { + char buf[40]; + pid_t pid = getpid(); + + (void)snprintf(buf, sizeof(buf), "/proc/%ld/pagedata", (long)pid); + buf[sizeof(buf) - 1] = '\0'; + GC_proc_fd = open(buf, O_RDONLY); + if (-1 == GC_proc_fd) { + WARN("/proc open failed; cannot enable GC incremental mode\n", 0); + return FALSE; + } + if (syscall(SYS_fcntl, GC_proc_fd, F_SETFD, FD_CLOEXEC) == -1) + WARN("Could not set FD_CLOEXEC for /proc\n", 0); +# ifndef THREADS + saved_proc_pid = pid; /* updated on success only */ +# endif + return TRUE; + } + +# ifdef CAN_HANDLE_FORK + GC_INNER void GC_dirty_update_child(void) + { + GC_ASSERT(I_HOLD_LOCK()); + if (-1 == GC_proc_fd) + return; /* GC incremental mode is off */ + + close(GC_proc_fd); + if (!proc_dirty_open_files()) + GC_incremental = FALSE; /* should be safe to turn it off */ + } +# endif /* CAN_HANDLE_FORK */ + +GC_INNER GC_bool GC_dirty_init(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + if (GC_bytes_allocd != 0 || GC_bytes_allocd_before_gc != 0) { + memset(GC_written_pages, 0xff, sizeof(page_hash_table)); + GC_VERBOSE_LOG_PRINTF( + "Allocated %lu bytes: all pages may have been written\n", + (unsigned long)(GC_bytes_allocd + GC_bytes_allocd_before_gc)); + } + if (!proc_dirty_open_files()) + return FALSE; + GC_proc_buf = GC_scratch_alloc(GC_proc_buf_size); + if (GC_proc_buf == NULL) + ABORT("Insufficient space for /proc read"); + return TRUE; +} + +GC_INLINE void GC_proc_read_dirty(GC_bool output_unneeded) +{ + int nmaps; + char * bufp = GC_proc_buf; + int i; + + GC_ASSERT(I_HOLD_LOCK()); +# ifndef THREADS + /* If the current pid differs from the saved one, then we are in */ + /* the forked (child) process, the current /proc file should be */ + /* closed, the new one should be opened with the updated path. */ + /* Note, this is not needed for multi-threaded case because */ + /* fork_child_proc() reopens the file right after fork. */ + if (getpid() != saved_proc_pid + && (-1 == GC_proc_fd /* no need to retry */ + || (close(GC_proc_fd), !proc_dirty_open_files()))) { + /* Failed to reopen the file. Punt! */ + if (!output_unneeded) + memset(GC_grungy_pages, 0xff, sizeof(page_hash_table)); + memset(GC_written_pages, 0xff, sizeof(page_hash_table)); + return; + } +# endif + + BZERO(GC_grungy_pages, sizeof(GC_grungy_pages)); + if (PROC_READ(GC_proc_fd, bufp, GC_proc_buf_size) <= 0) { + /* Retry with larger buffer. */ + size_t new_size = 2 * GC_proc_buf_size; + char *new_buf; + + WARN("/proc read failed (buffer size is %" WARN_PRIuPTR " bytes)\n", + GC_proc_buf_size); + new_buf = GC_scratch_alloc(new_size); + if (new_buf != 0) { + GC_scratch_recycle_no_gww(bufp, GC_proc_buf_size); + GC_proc_buf = bufp = new_buf; + GC_proc_buf_size = new_size; + } + if (PROC_READ(GC_proc_fd, bufp, GC_proc_buf_size) <= 0) { + WARN("Insufficient space for /proc read\n", 0); + /* Punt: */ + if (!output_unneeded) + memset(GC_grungy_pages, 0xff, sizeof(page_hash_table)); + memset(GC_written_pages, 0xff, sizeof(page_hash_table)); + return; + } + } + + /* Copy dirty bits into GC_grungy_pages */ + nmaps = ((struct prpageheader *)bufp) -> pr_nmap; +# ifdef DEBUG_DIRTY_BITS + GC_log_printf("Proc VDB read: pr_nmap= %u, pr_npage= %lu\n", + nmaps, ((struct prpageheader *)bufp)->pr_npage); +# endif +# if defined(GC_NO_SYS_FAULT_H) && defined(CPPCHECK) + GC_noop1(((struct prpageheader *)bufp)->dummy[0]); +# endif + bufp += sizeof(struct prpageheader); + for (i = 0; i < nmaps; i++) { + struct prasmap * map = (struct prasmap *)bufp; + ptr_t vaddr = (ptr_t)(map -> pr_vaddr); + unsigned long npages = map -> pr_npage; + unsigned pagesize = map -> pr_pagesize; + ptr_t limit; + +# if defined(GC_NO_SYS_FAULT_H) && defined(CPPCHECK) + GC_noop1(map->dummy1[0] + map->dummy2[0]); +# endif +# ifdef DEBUG_DIRTY_BITS + GC_log_printf( + "pr_vaddr= %p, npage= %lu, mflags= 0x%x, pagesize= 0x%x\n", + (void *)vaddr, npages, map->pr_mflags, pagesize); +# endif + + bufp += sizeof(struct prasmap); + limit = vaddr + pagesize * npages; + for (; (word)vaddr < (word)limit; vaddr += pagesize) { + if ((*bufp++) & PG_MODIFIED) { + struct hblk * h; + ptr_t next_vaddr = vaddr + pagesize; +# ifdef DEBUG_DIRTY_BITS + GC_log_printf("dirty page at: %p\n", (void *)vaddr); +# endif + for (h = (struct hblk *)vaddr; + (word)h < (word)next_vaddr; h++) { + word index = PHT_HASH(h); + + set_pht_entry_from_index(GC_grungy_pages, index); + } + } + } + bufp = PTRT_ROUNDUP_BY_MASK(bufp, sizeof(long)-1); + } +# ifdef DEBUG_DIRTY_BITS + GC_log_printf("Proc VDB read done\n"); +# endif + + /* Update GC_written_pages (even if output_unneeded). */ + GC_or_pages(GC_written_pages, GC_grungy_pages); +} + +#endif /* PROC_VDB */ + +#ifdef SOFT_VDB +# ifndef VDB_BUF_SZ +# define VDB_BUF_SZ 16384 +# endif + + static int open_proc_fd(pid_t pid, const char *proc_filename, int mode) + { + int f; + char buf[40]; + + (void)snprintf(buf, sizeof(buf), "/proc/%ld/%s", (long)pid, + proc_filename); + buf[sizeof(buf) - 1] = '\0'; + f = open(buf, mode); + if (-1 == f) { + WARN("/proc/self/%s open failed; cannot enable GC incremental mode\n", + proc_filename); + } else if (fcntl(f, F_SETFD, FD_CLOEXEC) == -1) { + WARN("Could not set FD_CLOEXEC for /proc\n", 0); + } + return f; + } + +# include /* for uint64_t */ + + typedef uint64_t pagemap_elem_t; + + static pagemap_elem_t *soft_vdb_buf; + static int pagemap_fd; + + static GC_bool soft_dirty_open_files(void) + { + pid_t pid = getpid(); + + clear_refs_fd = open_proc_fd(pid, "clear_refs", O_WRONLY); + if (-1 == clear_refs_fd) + return FALSE; + pagemap_fd = open_proc_fd(pid, "pagemap", O_RDONLY); + if (-1 == pagemap_fd) { + close(clear_refs_fd); + clear_refs_fd = -1; + return FALSE; + } +# ifndef THREADS + saved_proc_pid = pid; /* updated on success only */ +# endif + return TRUE; + } + +# ifdef CAN_HANDLE_FORK + GC_INNER void GC_dirty_update_child(void) + { + GC_ASSERT(I_HOLD_LOCK()); + if (-1 == clear_refs_fd) + return; /* GC incremental mode is off */ + + close(clear_refs_fd); + close(pagemap_fd); + if (!soft_dirty_open_files()) + GC_incremental = FALSE; + } +# endif /* CAN_HANDLE_FORK */ + + /* Clear soft-dirty bits from the task's PTEs. */ + static void clear_soft_dirty_bits(void) + { + ssize_t res = write(clear_refs_fd, "4\n", 2); + + if (res != 2) + ABORT_ARG1("Failed to write to /proc/self/clear_refs", + ": errno= %d", res < 0 ? errno : 0); + } + + /* The bit 55 of the 64-bit qword of pagemap file is the soft-dirty one. */ +# define PM_SOFTDIRTY_MASK ((pagemap_elem_t)1 << 55) + + static GC_bool detect_soft_dirty_supported(ptr_t vaddr) + { + off_t fpos; + pagemap_elem_t buf[1]; + + GC_ASSERT(GC_log_pagesize != 0); + *vaddr = 1; /* make it dirty */ + fpos = (off_t)(((word)vaddr >> GC_log_pagesize) * sizeof(pagemap_elem_t)); + + for (;;) { + /* Read the relevant PTE from the pagemap file. */ + if (lseek(pagemap_fd, fpos, SEEK_SET) == (off_t)(-1)) + return FALSE; + if (PROC_READ(pagemap_fd, buf, sizeof(buf)) != (int)sizeof(buf)) + return FALSE; + + /* Is the soft-dirty bit unset? */ + if ((buf[0] & PM_SOFTDIRTY_MASK) == 0) return FALSE; + + if (0 == *vaddr) break; + /* Retry to check that writing to clear_refs works as expected. */ + /* This malfunction of the soft-dirty bits implementation is */ + /* observed on some Linux kernels on Power9 (e.g. in Fedora 36). */ + clear_soft_dirty_bits(); + *vaddr = 0; + } + return TRUE; /* success */ + } + +# ifndef NO_SOFT_VDB_LINUX_VER_RUNTIME_CHECK +# include +# include /* for strcmp() */ + + /* Ensure the linux (kernel) major/minor version is as given or higher. */ + static GC_bool ensure_min_linux_ver(int major, int minor) { + struct utsname info; + int actual_major; + int actual_minor = -1; + + if (uname(&info) == -1) { + return FALSE; /* uname() failed, should not happen actually. */ + } + if (strcmp(info.sysname, "Linux")) { + WARN("Cannot ensure Linux version as running on other OS: %s\n", + info.sysname); + return FALSE; + } + actual_major = GC_parse_version(&actual_minor, info.release); + return actual_major > major + || (actual_major == major && actual_minor >= minor); + } +# endif + +# ifdef MPROTECT_VDB + static GC_bool soft_dirty_init(void) +# else + GC_INNER GC_bool GC_dirty_init(void) +# endif + { +# if defined(MPROTECT_VDB) && !defined(CHECK_SOFT_VDB) + char * str = GETENV("GC_USE_GETWRITEWATCH"); +# ifdef GC_PREFER_MPROTECT_VDB + if (str == NULL || (*str == '0' && *(str + 1) == '\0')) + return FALSE; /* the environment variable is unset or set to "0" */ +# else + if (str != NULL && *str == '0' && *(str + 1) == '\0') + return FALSE; /* the environment variable is set "0" */ +# endif +# endif + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(NULL == soft_vdb_buf); +# ifndef NO_SOFT_VDB_LINUX_VER_RUNTIME_CHECK + if (!ensure_min_linux_ver(3, 18)) { + GC_COND_LOG_PRINTF( + "Running on old kernel lacking correct soft-dirty bit support\n"); + return FALSE; + } +# endif + if (!soft_dirty_open_files()) + return FALSE; + soft_vdb_buf = (pagemap_elem_t *)GC_scratch_alloc(VDB_BUF_SZ); + if (NULL == soft_vdb_buf) + ABORT("Insufficient space for /proc pagemap buffer"); + if (!detect_soft_dirty_supported((ptr_t)soft_vdb_buf)) { + GC_COND_LOG_PRINTF("Soft-dirty bit is not supported by kernel\n"); + /* Release the resources. */ + GC_scratch_recycle_no_gww(soft_vdb_buf, VDB_BUF_SZ); + soft_vdb_buf = NULL; + close(clear_refs_fd); + clear_refs_fd = -1; + close(pagemap_fd); + return FALSE; + } + return TRUE; + } + + static off_t pagemap_buf_fpos; /* valid only if pagemap_buf_len > 0 */ + static size_t pagemap_buf_len; + + /* Read bytes from /proc/self/pagemap at given file position. */ + /* len - the maximum number of bytes to read; (*pres) - amount of */ + /* bytes actually read, always bigger than 0 but never exceeds len; */ + /* next_fpos_hint - the file position of the next bytes block to read */ + /* ahead if possible (0 means no information provided). */ + static const pagemap_elem_t *pagemap_buffered_read(size_t *pres, + off_t fpos, size_t len, + off_t next_fpos_hint) + { + ssize_t res; + size_t ofs; + + GC_ASSERT(GC_page_size != 0); + GC_ASSERT(len > 0); + if (pagemap_buf_fpos <= fpos + && fpos < pagemap_buf_fpos + (off_t)pagemap_buf_len) { + /* The requested data is already in the buffer. */ + ofs = (size_t)(fpos - pagemap_buf_fpos); + res = (ssize_t)(pagemap_buf_fpos + pagemap_buf_len - fpos); + } else { + off_t aligned_pos = fpos & ~(off_t)(GC_page_size < VDB_BUF_SZ + ? GC_page_size-1 : VDB_BUF_SZ-1); + + for (;;) { + size_t count; + + if ((0 == pagemap_buf_len + || pagemap_buf_fpos + (off_t)pagemap_buf_len != aligned_pos) + && lseek(pagemap_fd, aligned_pos, SEEK_SET) == (off_t)(-1)) + ABORT_ARG2("Failed to lseek /proc/self/pagemap", + ": offset= %lu, errno= %d", (unsigned long)fpos, errno); + + /* How much to read at once? */ + ofs = (size_t)(fpos - aligned_pos); + GC_ASSERT(ofs < VDB_BUF_SZ); + if (next_fpos_hint > aligned_pos + && next_fpos_hint - aligned_pos < VDB_BUF_SZ) { + count = VDB_BUF_SZ; + } else { + count = len + ofs; + if (count > VDB_BUF_SZ) + count = VDB_BUF_SZ; + } + + GC_ASSERT(count % sizeof(pagemap_elem_t) == 0); + res = PROC_READ(pagemap_fd, soft_vdb_buf, count); + if (res > (ssize_t)ofs) + break; + if (res <= 0) + ABORT_ARG1("Failed to read /proc/self/pagemap", + ": errno= %d", res < 0 ? errno : 0); + /* Retry (once) w/o page-alignment. */ + aligned_pos = fpos; + } + + /* Save the buffer (file window) position and size. */ + pagemap_buf_fpos = aligned_pos; + pagemap_buf_len = (size_t)res; + res -= (ssize_t)ofs; + } + + GC_ASSERT(ofs % sizeof(pagemap_elem_t) == 0); + *pres = (size_t)res < len ? (size_t)res : len; + return &soft_vdb_buf[ofs / sizeof(pagemap_elem_t)]; + } + + static void soft_set_grungy_pages(ptr_t vaddr /* start */, ptr_t limit, + ptr_t next_start_hint, + GC_bool is_static_root) + { + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_log_pagesize != 0); + while ((word)vaddr < (word)limit) { + size_t res; + word limit_buf; + const pagemap_elem_t *bufp = pagemap_buffered_read(&res, + (off_t)(((word)vaddr >> GC_log_pagesize) + * sizeof(pagemap_elem_t)), + (size_t)((((word)limit - (word)vaddr + + GC_page_size - 1) >> GC_log_pagesize) + * sizeof(pagemap_elem_t)), + (off_t)(((word)next_start_hint >> GC_log_pagesize) + * sizeof(pagemap_elem_t))); + + if (res % sizeof(pagemap_elem_t) != 0) { + /* Punt: */ + memset(GC_grungy_pages, 0xff, sizeof(page_hash_table)); + WARN("Incomplete read of pagemap, not multiple of entry size\n", 0); + break; + } + + limit_buf = ((word)vaddr & ~(word)(GC_page_size-1)) + + ((res / sizeof(pagemap_elem_t)) << GC_log_pagesize); + for (; (word)vaddr < limit_buf; vaddr += GC_page_size, bufp++) + if ((*bufp & PM_SOFTDIRTY_MASK) != 0) { + struct hblk * h; + ptr_t next_vaddr = vaddr + GC_page_size; + + /* If the bit is set, the respective PTE was written to */ + /* since clearing the soft-dirty bits. */ +# ifdef DEBUG_DIRTY_BITS + if (is_static_root) + GC_log_printf("static root dirty page at: %p\n", (void *)vaddr); +# endif + for (h = (struct hblk *)vaddr; (word)h < (word)next_vaddr; h++) { + word index = PHT_HASH(h); + + /* Filter out the blocks without pointers. It might worth */ + /* for the case when the heap is large enough for the hash */ + /* collisions to occur frequently. Thus, off by default. */ +# if defined(FILTER_PTRFREE_HBLKS_IN_SOFT_VDB) \ + || defined(CHECKSUMS) || defined(DEBUG_DIRTY_BITS) + if (!is_static_root) { + struct hblk *b; + hdr *hhdr; + +# ifdef CHECKSUMS + set_pht_entry_from_index(GC_written_pages, index); +# endif + GET_HDR(h, hhdr); + if (NULL == hhdr) continue; + for (b = h; IS_FORWARDING_ADDR_OR_NIL(hhdr); hhdr = HDR(b)) { + b = FORWARDED_ADDR(b, hhdr); + } + if (HBLK_IS_FREE(hhdr) || IS_PTRFREE(hhdr)) continue; +# ifdef DEBUG_DIRTY_BITS + GC_log_printf("dirty page (hblk) at: %p\n", (void *)h); +# endif + } +# else + UNUSED_ARG(is_static_root); +# endif + set_pht_entry_from_index(GC_grungy_pages, index); + } + } else { +# if defined(CHECK_SOFT_VDB) /* && MPROTECT_VDB */ + /* Ensure that each clean page according to the soft-dirty */ + /* VDB is also identified such by the mprotect-based one. */ + if (!is_static_root + && get_pht_entry_from_index(GC_dirty_pages, PHT_HASH(vaddr))) { + ptr_t my_start, my_end; /* the values are not used */ + + /* There could be a hash collision, thus we need to */ + /* verify the page is clean using slow GC_get_maps(). */ + if (GC_enclosing_writable_mapping(vaddr, &my_start, &my_end)) { + ABORT("Inconsistent soft-dirty against mprotect dirty bits"); + } + } +# endif + } + /* Read the next portion of pagemap file if incomplete. */ + } + } + + GC_INLINE void GC_soft_read_dirty(GC_bool output_unneeded) + { + GC_ASSERT(I_HOLD_LOCK()); +# ifndef THREADS + /* Similar as for GC_proc_read_dirty. */ + if (getpid() != saved_proc_pid + && (-1 == clear_refs_fd /* no need to retry */ + || (close(clear_refs_fd), close(pagemap_fd), + !soft_dirty_open_files()))) { + /* Failed to reopen the files. */ + if (!output_unneeded) { + /* Punt: */ + memset(GC_grungy_pages, 0xff, sizeof(page_hash_table)); +# ifdef CHECKSUMS + memset(GC_written_pages, 0xff, sizeof(page_hash_table)); +# endif + } + return; + } +# endif + + if (!output_unneeded) { + word i; + + BZERO(GC_grungy_pages, sizeof(GC_grungy_pages)); + pagemap_buf_len = 0; /* invalidate soft_vdb_buf */ + + for (i = 0; i != GC_n_heap_sects; ++i) { + ptr_t vaddr = GC_heap_sects[i].hs_start; + + soft_set_grungy_pages(vaddr, vaddr + GC_heap_sects[i].hs_bytes, + i < GC_n_heap_sects-1 ? + GC_heap_sects[i+1].hs_start : NULL, + FALSE); + } + +# ifndef NO_VDB_FOR_STATIC_ROOTS + for (i = 0; (int)i < n_root_sets; ++i) { + soft_set_grungy_pages(GC_static_roots[i].r_start, + GC_static_roots[i].r_end, + (int)i < n_root_sets-1 ? + GC_static_roots[i+1].r_start : NULL, + TRUE); + } +# endif + } + + clear_soft_dirty_bits(); + } +#endif /* SOFT_VDB */ + +#ifdef PCR_VDB + +# include "vd/PCR_VD.h" + +# define NPAGES (32*1024) /* 128 MB */ + +PCR_VD_DB GC_grungy_bits[NPAGES]; + +STATIC ptr_t GC_vd_base = NULL; + /* Address corresponding to GC_grungy_bits[0] */ + /* HBLKSIZE aligned. */ + +GC_INNER GC_bool GC_dirty_init(void) +{ + /* For the time being, we assume the heap generally grows up */ + GC_vd_base = GC_heap_sects[0].hs_start; + if (GC_vd_base == 0) { + ABORT("Bad initial heap segment"); + } + if (PCR_VD_Start(HBLKSIZE, GC_vd_base, NPAGES*HBLKSIZE) + != PCR_ERes_okay) { + ABORT("Dirty bit initialization failed"); + } + return TRUE; +} +#endif /* PCR_VDB */ + +#ifndef NO_MANUAL_VDB + GC_INNER GC_bool GC_manual_vdb = FALSE; + + /* Manually mark the page containing p as dirty. Logically, this */ + /* dirties the entire object. */ + GC_INNER void GC_dirty_inner(const void *p) + { + word index = PHT_HASH(p); + +# if defined(MPROTECT_VDB) + /* Do not update GC_dirty_pages if it should be followed by the */ + /* page unprotection. */ + GC_ASSERT(GC_manual_vdb); +# endif + async_set_pht_entry_from_index(GC_dirty_pages, index); + } +#endif /* !NO_MANUAL_VDB */ + +#ifndef GC_DISABLE_INCREMENTAL + /* Retrieve system dirty bits for the heap to a local buffer (unless */ + /* output_unneeded). Restore the systems notion of which pages are */ + /* dirty. We assume that either the world is stopped or it is OK to */ + /* lose dirty bits while it is happening (GC_enable_incremental is */ + /* the caller and output_unneeded is TRUE at least if multi-threading */ + /* support is on). */ + GC_INNER void GC_read_dirty(GC_bool output_unneeded) + { + GC_ASSERT(I_HOLD_LOCK()); +# ifdef DEBUG_DIRTY_BITS + GC_log_printf("read dirty begin\n"); +# endif + if (GC_manual_vdb +# if defined(MPROTECT_VDB) + || !GC_GWW_AVAILABLE() +# endif + ) { + if (!output_unneeded) + BCOPY((/* no volatile */ void *)(word)GC_dirty_pages, + GC_grungy_pages, sizeof(GC_dirty_pages)); + BZERO((/* no volatile */ void *)(word)GC_dirty_pages, + sizeof(GC_dirty_pages)); +# ifdef MPROTECT_VDB + if (!GC_manual_vdb) + GC_protect_heap(); +# endif + return; + } + +# ifdef GWW_VDB + GC_gww_read_dirty(output_unneeded); +# elif defined(PROC_VDB) + GC_proc_read_dirty(output_unneeded); +# elif defined(SOFT_VDB) + GC_soft_read_dirty(output_unneeded); +# elif defined(PCR_VDB) + /* lazily enable dirty bits on newly added heap sects */ + { + static int onhs = 0; + int nhs = GC_n_heap_sects; + for (; onhs < nhs; onhs++) { + PCR_VD_WriteProtectEnable( + GC_heap_sects[onhs].hs_start, + GC_heap_sects[onhs].hs_bytes); + } + } + if (PCR_VD_Clear(GC_vd_base, NPAGES*HBLKSIZE, GC_grungy_bits) + != PCR_ERes_okay) { + ABORT("Dirty bit read failed"); + } +# endif +# if defined(CHECK_SOFT_VDB) /* && MPROTECT_VDB */ + BZERO((/* no volatile */ void *)(word)GC_dirty_pages, + sizeof(GC_dirty_pages)); + GC_protect_heap(); +# endif + } + +# if !defined(NO_VDB_FOR_STATIC_ROOTS) && !defined(PROC_VDB) + GC_INNER GC_bool GC_is_vdb_for_static_roots(void) + { + if (GC_manual_vdb) return FALSE; +# if defined(MPROTECT_VDB) + /* Currently used only in conjunction with SOFT_VDB. */ + return GC_GWW_AVAILABLE(); +# else +# ifndef LINT2 + GC_ASSERT(GC_incremental); +# endif + return TRUE; +# endif + } +# endif + + /* Is the HBLKSIZE sized page at h marked dirty in the local buffer? */ + /* If the actual page size is different, this returns TRUE if any */ + /* of the pages overlapping h are dirty. This routine may err on the */ + /* side of labeling pages as dirty (and this implementation does). */ + GC_INNER GC_bool GC_page_was_dirty(struct hblk *h) + { + word index; + +# ifdef PCR_VDB + if (!GC_manual_vdb) { + if ((word)h < (word)GC_vd_base + || (word)h >= (word)(GC_vd_base + NPAGES * HBLKSIZE)) { + return TRUE; + } + return GC_grungy_bits[h-(struct hblk*)GC_vd_base] & PCR_VD_DB_dirtyBit; + } +# elif defined(DEFAULT_VDB) + if (!GC_manual_vdb) + return TRUE; +# elif defined(PROC_VDB) + /* Unless manual VDB is on, the bitmap covers all process memory. */ + if (GC_manual_vdb) +# endif + { + if (NULL == HDR(h)) + return TRUE; + } + index = PHT_HASH(h); + return get_pht_entry_from_index(GC_grungy_pages, index); + } + +# if defined(CHECKSUMS) || defined(PROC_VDB) + /* Could any valid GC heap pointer ever have been written to this page? */ + GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk *h) + { +# if defined(GWW_VDB) || defined(PROC_VDB) || defined(SOFT_VDB) + word index; + +# ifdef MPROTECT_VDB + if (!GC_GWW_AVAILABLE()) + return TRUE; +# endif +# if defined(PROC_VDB) + if (GC_manual_vdb) +# endif + { + if (NULL == HDR(h)) + return TRUE; + } + index = PHT_HASH(h); + return get_pht_entry_from_index(GC_written_pages, index); +# else + /* TODO: implement me for MANUAL_VDB. */ + UNUSED_ARG(h); + return TRUE; +# endif + } +# endif /* CHECKSUMS || PROC_VDB */ + + /* We expect block h to be written shortly. Ensure that all pages */ + /* containing any part of the n hblks starting at h are no longer */ + /* protected. If is_ptrfree is false, also ensure that they will */ + /* subsequently appear to be dirty. Not allowed to call GC_printf */ + /* (and the friends) here, see Win32 GC_stop_world for the details. */ + GC_INNER void GC_remove_protection(struct hblk *h, word nblocks, + GC_bool is_ptrfree) + { +# ifdef MPROTECT_VDB + struct hblk * h_trunc; /* Truncated to page boundary */ + struct hblk * h_end; /* Page boundary following block end */ + struct hblk * current; +# endif + +# ifndef PARALLEL_MARK + GC_ASSERT(I_HOLD_LOCK()); +# endif +# ifdef MPROTECT_VDB + if (!GC_auto_incremental || GC_GWW_AVAILABLE()) + return; + GC_ASSERT(GC_page_size != 0); + h_trunc = (struct hblk *)((word)h & ~(word)(GC_page_size-1)); + h_end = (struct hblk *)PTRT_ROUNDUP_BY_MASK(h + nblocks, GC_page_size-1); + /* Note that we cannot examine GC_dirty_pages to check */ + /* whether the page at h_trunc has already been marked */ + /* dirty as there could be a hash collision. */ + for (current = h_trunc; (word)current < (word)h_end; ++current) { + word index = PHT_HASH(current); + + if (!is_ptrfree || (word)current < (word)h + || (word)current >= (word)(h + nblocks)) { + async_set_pht_entry_from_index(GC_dirty_pages, index); + } + } + UNPROTECT(h_trunc, (ptr_t)h_end - (ptr_t)h_trunc); +# elif defined(PCR_VDB) + UNUSED_ARG(is_ptrfree); + if (!GC_auto_incremental) + return; + PCR_VD_WriteProtectDisable(h, nblocks * HBLKSIZE); + PCR_VD_WriteProtectEnable(h, nblocks * HBLKSIZE); +# else + /* Ignore write hints. They don't help us here. */ + UNUSED_ARG(h); + UNUSED_ARG(nblocks); + UNUSED_ARG(is_ptrfree); +# endif + } +#endif /* !GC_DISABLE_INCREMENTAL */ + +#if defined(MPROTECT_VDB) && defined(DARWIN) +/* The following sources were used as a "reference" for this exception + handling code: + 1. Apple's mach/xnu documentation + 2. Timothy J. Wood's "Mach Exception Handlers 101" post to the + omnigroup's macosx-dev list. + www.omnigroup.com/mailman/archive/macosx-dev/2000-June/014178.html + 3. macosx-nat.c from Apple's GDB source code. +*/ + +/* The bug that caused all this trouble should now be fixed. This should + eventually be removed if all goes well. */ + +#include +#include +#include +#include + +EXTERN_C_BEGIN + +/* Some of the following prototypes are missing in any header, although */ +/* they are documented. Some are in mach/exc.h file. */ +extern boolean_t +exc_server(mach_msg_header_t *, mach_msg_header_t *); + +extern kern_return_t +exception_raise(mach_port_t, mach_port_t, mach_port_t, exception_type_t, + exception_data_t, mach_msg_type_number_t); + +extern kern_return_t +exception_raise_state(mach_port_t, mach_port_t, mach_port_t, exception_type_t, + exception_data_t, mach_msg_type_number_t, + thread_state_flavor_t*, thread_state_t, + mach_msg_type_number_t, thread_state_t, + mach_msg_type_number_t*); + +extern kern_return_t +exception_raise_state_identity(mach_port_t, mach_port_t, mach_port_t, + exception_type_t, exception_data_t, + mach_msg_type_number_t, thread_state_flavor_t*, + thread_state_t, mach_msg_type_number_t, + thread_state_t, mach_msg_type_number_t*); + +GC_API_OSCALL kern_return_t +catch_exception_raise(mach_port_t exception_port, mach_port_t thread, + mach_port_t task, exception_type_t exception, + exception_data_t code, + mach_msg_type_number_t code_count); + +GC_API_OSCALL kern_return_t +catch_exception_raise_state(mach_port_name_t exception_port, + int exception, exception_data_t code, + mach_msg_type_number_t codeCnt, int flavor, + thread_state_t old_state, int old_stateCnt, + thread_state_t new_state, int new_stateCnt); + +GC_API_OSCALL kern_return_t +catch_exception_raise_state_identity(mach_port_name_t exception_port, + mach_port_t thread, mach_port_t task, int exception, + exception_data_t code, mach_msg_type_number_t codeCnt, + int flavor, thread_state_t old_state, int old_stateCnt, + thread_state_t new_state, int new_stateCnt); + +EXTERN_C_END + +/* These should never be called, but just in case... */ +GC_API_OSCALL kern_return_t +catch_exception_raise_state(mach_port_name_t exception_port, int exception, + exception_data_t code, + mach_msg_type_number_t codeCnt, int flavor, + thread_state_t old_state, int old_stateCnt, + thread_state_t new_state, int new_stateCnt) +{ + UNUSED_ARG(exception_port); + UNUSED_ARG(exception); + UNUSED_ARG(code); + UNUSED_ARG(codeCnt); + UNUSED_ARG(flavor); + UNUSED_ARG(old_state); + UNUSED_ARG(old_stateCnt); + UNUSED_ARG(new_state); + UNUSED_ARG(new_stateCnt); + ABORT_RET("Unexpected catch_exception_raise_state invocation"); + return KERN_INVALID_ARGUMENT; +} + +GC_API_OSCALL kern_return_t +catch_exception_raise_state_identity(mach_port_name_t exception_port, + mach_port_t thread, mach_port_t task, + int exception, exception_data_t code, + mach_msg_type_number_t codeCnt, + int flavor, thread_state_t old_state, + int old_stateCnt, + thread_state_t new_state, + int new_stateCnt) +{ + UNUSED_ARG(exception_port); + UNUSED_ARG(thread); + UNUSED_ARG(task); + UNUSED_ARG(exception); + UNUSED_ARG(code); + UNUSED_ARG(codeCnt); + UNUSED_ARG(flavor); + UNUSED_ARG(old_state); + UNUSED_ARG(old_stateCnt); + UNUSED_ARG(new_state); + UNUSED_ARG(new_stateCnt); + ABORT_RET("Unexpected catch_exception_raise_state_identity invocation"); + return KERN_INVALID_ARGUMENT; +} + +#define MAX_EXCEPTION_PORTS 16 + +static struct { + mach_msg_type_number_t count; + exception_mask_t masks[MAX_EXCEPTION_PORTS]; + exception_handler_t ports[MAX_EXCEPTION_PORTS]; + exception_behavior_t behaviors[MAX_EXCEPTION_PORTS]; + thread_state_flavor_t flavors[MAX_EXCEPTION_PORTS]; +} GC_old_exc_ports; + +STATIC struct ports_s { + void (*volatile os_callback[3])(void); + mach_port_t exception; +# if defined(THREADS) + mach_port_t reply; +# endif +} GC_ports = { + { + /* This is to prevent stripping these routines as dead. */ + (void (*)(void))catch_exception_raise, + (void (*)(void))catch_exception_raise_state, + (void (*)(void))catch_exception_raise_state_identity + }, +# ifdef THREADS + 0, /* for 'exception' */ +# endif + 0 +}; + +typedef struct { + mach_msg_header_t head; +} GC_msg_t; + +typedef enum { + GC_MP_NORMAL, + GC_MP_DISCARDING, + GC_MP_STOPPED +} GC_mprotect_state_t; + +#ifdef THREADS + /* FIXME: 1 and 2 seem to be safe to use in the msgh_id field, but it */ + /* is not documented. Use the source and see if they should be OK. */ +# define ID_STOP 1 +# define ID_RESUME 2 + + /* This value is only used on the reply port. */ +# define ID_ACK 3 + + STATIC GC_mprotect_state_t GC_mprotect_state = GC_MP_NORMAL; + + /* The following should ONLY be called when the world is stopped. */ + STATIC void GC_mprotect_thread_notify(mach_msg_id_t id) + { + struct buf_s { + GC_msg_t msg; + mach_msg_trailer_t trailer; + } buf; + mach_msg_return_t r; + + /* remote, local */ + buf.msg.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); + buf.msg.head.msgh_size = sizeof(buf.msg); + buf.msg.head.msgh_remote_port = GC_ports.exception; + buf.msg.head.msgh_local_port = MACH_PORT_NULL; + buf.msg.head.msgh_id = id; + + r = mach_msg(&buf.msg.head, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_LARGE, + sizeof(buf.msg), sizeof(buf), GC_ports.reply, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + if (r != MACH_MSG_SUCCESS) + ABORT("mach_msg failed in GC_mprotect_thread_notify"); + if (buf.msg.head.msgh_id != ID_ACK) + ABORT("Invalid ack in GC_mprotect_thread_notify"); + } + + /* Should only be called by the mprotect thread */ + STATIC void GC_mprotect_thread_reply(void) + { + GC_msg_t msg; + mach_msg_return_t r; + /* remote, local */ + + msg.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); + msg.head.msgh_size = sizeof(msg); + msg.head.msgh_remote_port = GC_ports.reply; + msg.head.msgh_local_port = MACH_PORT_NULL; + msg.head.msgh_id = ID_ACK; + + r = mach_msg(&msg.head, MACH_SEND_MSG, sizeof(msg), 0, MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + if (r != MACH_MSG_SUCCESS) + ABORT("mach_msg failed in GC_mprotect_thread_reply"); + } + + GC_INNER void GC_mprotect_stop(void) + { + GC_mprotect_thread_notify(ID_STOP); + } + + GC_INNER void GC_mprotect_resume(void) + { + GC_mprotect_thread_notify(ID_RESUME); + } + +# ifdef CAN_HANDLE_FORK + GC_INNER void GC_dirty_update_child(void) + { + unsigned i; + + GC_ASSERT(I_HOLD_LOCK()); + if (0 == GC_task_self) return; /* GC incremental mode is off */ + + GC_ASSERT(GC_auto_incremental); + GC_ASSERT(GC_mprotect_state == GC_MP_NORMAL); + + /* Unprotect the entire heap not updating GC_dirty_pages. */ + GC_task_self = mach_task_self(); /* needed by UNPROTECT() */ + for (i = 0; i < GC_n_heap_sects; i++) { + UNPROTECT(GC_heap_sects[i].hs_start, GC_heap_sects[i].hs_bytes); + } + + /* Restore the old task exception ports. */ + /* TODO: Should we do it in fork_prepare/parent_proc? */ + if (GC_old_exc_ports.count > 0) { + /* TODO: Should we check GC_old_exc_ports.count<=1? */ + if (task_set_exception_ports(GC_task_self, GC_old_exc_ports.masks[0], + GC_old_exc_ports.ports[0], GC_old_exc_ports.behaviors[0], + GC_old_exc_ports.flavors[0]) != KERN_SUCCESS) + ABORT("task_set_exception_ports failed (in child)"); + } + + /* TODO: Re-enable incremental mode in child. */ + GC_task_self = 0; + GC_incremental = FALSE; + } +# endif /* CAN_HANDLE_FORK */ + +#else + /* The compiler should optimize away any GC_mprotect_state computations */ +# define GC_mprotect_state GC_MP_NORMAL +#endif /* !THREADS */ + +struct mp_reply_s { + mach_msg_header_t head; + char data[256]; +}; + +struct mp_msg_s { + mach_msg_header_t head; + mach_msg_body_t msgh_body; + char data[1024]; +}; + +STATIC void *GC_mprotect_thread(void *arg) +{ + mach_msg_return_t r; + /* These two structures contain some private kernel data. We don't */ + /* need to access any of it so we don't bother defining a proper */ + /* struct. The correct definitions are in the xnu source code. */ + struct mp_reply_s reply; + struct mp_msg_s msg; + mach_msg_id_t id; + + if ((word)arg == GC_WORD_MAX) return 0; /* to prevent a compiler warning */ +# if defined(CPPCHECK) + reply.data[0] = 0; /* to prevent "field unused" warnings */ + msg.data[0] = 0; +# endif + +# if defined(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID) + (void)pthread_setname_np("GC-mprotect"); +# endif +# if defined(THREADS) && !defined(GC_NO_THREADS_DISCOVERY) + GC_darwin_register_self_mach_handler(); +# endif + + for (;;) { + r = mach_msg(&msg.head, MACH_RCV_MSG | MACH_RCV_LARGE | + (GC_mprotect_state == GC_MP_DISCARDING ? MACH_RCV_TIMEOUT + : 0), 0, sizeof(msg), GC_ports.exception, + GC_mprotect_state == GC_MP_DISCARDING ? 0 + : MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + id = r == MACH_MSG_SUCCESS ? msg.head.msgh_id : -1; + +# if defined(THREADS) + if (GC_mprotect_state == GC_MP_DISCARDING) { + if (r == MACH_RCV_TIMED_OUT) { + GC_mprotect_state = GC_MP_STOPPED; + GC_mprotect_thread_reply(); + continue; + } + if (r == MACH_MSG_SUCCESS && (id == ID_STOP || id == ID_RESUME)) + ABORT("Out of order mprotect thread request"); + } +# endif /* THREADS */ + + if (r != MACH_MSG_SUCCESS) { + ABORT_ARG2("mach_msg failed", + ": errcode= %d (%s)", (int)r, mach_error_string(r)); + } + + switch (id) { +# if defined(THREADS) + case ID_STOP: + if (GC_mprotect_state != GC_MP_NORMAL) + ABORT("Called mprotect_stop when state wasn't normal"); + GC_mprotect_state = GC_MP_DISCARDING; + break; + case ID_RESUME: + if (GC_mprotect_state != GC_MP_STOPPED) + ABORT("Called mprotect_resume when state wasn't stopped"); + GC_mprotect_state = GC_MP_NORMAL; + GC_mprotect_thread_reply(); + break; +# endif /* THREADS */ + default: + /* Handle the message (calls catch_exception_raise) */ + if (!exc_server(&msg.head, &reply.head)) + ABORT("exc_server failed"); + /* Send the reply */ + r = mach_msg(&reply.head, MACH_SEND_MSG, reply.head.msgh_size, 0, + MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + if (r != MACH_MSG_SUCCESS) { + /* This will fail if the thread dies, but the thread */ + /* shouldn't die... */ +# ifdef BROKEN_EXCEPTION_HANDLING + GC_err_printf("mach_msg failed with %d %s while sending " + "exc reply\n", (int)r, mach_error_string(r)); +# else + ABORT("mach_msg failed while sending exception reply"); +# endif + } + } /* switch */ + } /* for */ +} + +/* All this SIGBUS code shouldn't be necessary. All protection faults should + be going through the mach exception handler. However, it seems a SIGBUS is + occasionally sent for some unknown reason. Even more odd, it seems to be + meaningless and safe to ignore. */ +#ifdef BROKEN_EXCEPTION_HANDLING + + /* Updates to this aren't atomic, but the SIGBUS'es seem pretty rare. */ + /* Even if this doesn't get updated property, it isn't really a problem. */ + STATIC int GC_sigbus_count = 0; + + STATIC void GC_darwin_sigbus(int num, siginfo_t *sip, void *context) + { + if (num != SIGBUS) + ABORT("Got a non-sigbus signal in the sigbus handler"); + + /* Ugh... some seem safe to ignore, but too many in a row probably means + trouble. GC_sigbus_count is reset for each mach exception that is + handled */ + if (GC_sigbus_count >= 8) + ABORT("Got many SIGBUS signals in a row!"); + GC_sigbus_count++; + WARN("Ignoring SIGBUS\n", 0); + } +#endif /* BROKEN_EXCEPTION_HANDLING */ + +GC_INNER GC_bool GC_dirty_init(void) +{ + kern_return_t r; + mach_port_t me; + pthread_t thread; + pthread_attr_t attr; + exception_mask_t mask; + + GC_ASSERT(I_HOLD_LOCK()); +# if defined(CAN_HANDLE_FORK) && !defined(THREADS) + if (GC_handle_fork) { + /* To both support GC incremental mode and GC functions usage in */ + /* the forked child, pthread_atfork should be used to install */ + /* handlers that switch off GC_incremental in the child */ + /* gracefully (unprotecting all pages and clearing */ + /* GC_mach_handler_thread). For now, we just disable incremental */ + /* mode if fork() handling is requested by the client. */ + WARN("Can't turn on GC incremental mode as fork()" + " handling requested\n", 0); + return FALSE; + } +# endif + + GC_VERBOSE_LOG_PRINTF("Initializing mach/darwin mprotect" + " virtual dirty bit implementation\n"); +# ifdef BROKEN_EXCEPTION_HANDLING + WARN("Enabling workarounds for various darwin exception handling bugs\n", + 0); +# endif + if (GC_page_size % HBLKSIZE != 0) { + ABORT("Page size not multiple of HBLKSIZE"); + } + + GC_task_self = me = mach_task_self(); + GC_ASSERT(me != 0); + + r = mach_port_allocate(me, MACH_PORT_RIGHT_RECEIVE, &GC_ports.exception); + /* TODO: WARN and return FALSE in case of a failure. */ + if (r != KERN_SUCCESS) + ABORT("mach_port_allocate failed (exception port)"); + + r = mach_port_insert_right(me, GC_ports.exception, GC_ports.exception, + MACH_MSG_TYPE_MAKE_SEND); + if (r != KERN_SUCCESS) + ABORT("mach_port_insert_right failed (exception port)"); + +# if defined(THREADS) + r = mach_port_allocate(me, MACH_PORT_RIGHT_RECEIVE, &GC_ports.reply); + if (r != KERN_SUCCESS) + ABORT("mach_port_allocate failed (reply port)"); +# endif + + /* The exceptions we want to catch. */ + mask = EXC_MASK_BAD_ACCESS; + r = task_get_exception_ports(me, mask, GC_old_exc_ports.masks, + &GC_old_exc_ports.count, GC_old_exc_ports.ports, + GC_old_exc_ports.behaviors, + GC_old_exc_ports.flavors); + if (r != KERN_SUCCESS) + ABORT("task_get_exception_ports failed"); + + r = task_set_exception_ports(me, mask, GC_ports.exception, EXCEPTION_DEFAULT, + GC_MACH_THREAD_STATE); + if (r != KERN_SUCCESS) + ABORT("task_set_exception_ports failed"); + + if (pthread_attr_init(&attr) != 0) + ABORT("pthread_attr_init failed"); + if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) + ABORT("pthread_attr_setdetachedstate failed"); + /* This will call the real pthread function, not our wrapper. */ + if (GC_inner_pthread_create(&thread, &attr, GC_mprotect_thread, NULL) != 0) + ABORT("pthread_create failed"); + (void)pthread_attr_destroy(&attr); + + /* Setup the sigbus handler for ignoring the meaningless SIGBUS signals. */ +# ifdef BROKEN_EXCEPTION_HANDLING + { + struct sigaction sa, oldsa; + sa.sa_handler = (SIG_HNDLR_PTR)GC_darwin_sigbus; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART | SA_SIGINFO; + /* sa.sa_restorer is deprecated and should not be initialized. */ + if (sigaction(SIGBUS, &sa, &oldsa) < 0) + ABORT("sigaction failed"); + if ((GC_funcptr_uint)oldsa.sa_handler != (GC_funcptr_uint)SIG_DFL) { + GC_VERBOSE_LOG_PRINTF("Replaced other SIGBUS handler\n"); + } + } +# endif /* BROKEN_EXCEPTION_HANDLING */ +# if defined(CPPCHECK) + GC_noop1((word)GC_ports.os_callback[0]); +# endif + return TRUE; +} + +/* The source code for Apple's GDB was used as a reference for the */ +/* exception forwarding code. This code is similar to be GDB code only */ +/* because there is only one way to do it. */ +STATIC kern_return_t GC_forward_exception(mach_port_t thread, mach_port_t task, + exception_type_t exception, + exception_data_t data, + mach_msg_type_number_t data_count) +{ + unsigned int i; + kern_return_t r; + mach_port_t port; + exception_behavior_t behavior; + thread_state_flavor_t flavor; + + thread_state_data_t thread_state; + mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX; + + for (i = 0; i < GC_old_exc_ports.count; i++) { + if ((GC_old_exc_ports.masks[i] & ((exception_mask_t)1 << exception)) != 0) + break; + } + if (i == GC_old_exc_ports.count) + ABORT("No handler for exception!"); + + port = GC_old_exc_ports.ports[i]; + behavior = GC_old_exc_ports.behaviors[i]; + flavor = GC_old_exc_ports.flavors[i]; + + if (behavior == EXCEPTION_STATE || behavior == EXCEPTION_STATE_IDENTITY) { + r = thread_get_state(thread, flavor, thread_state, &thread_state_count); + if (r != KERN_SUCCESS) + ABORT("thread_get_state failed in forward_exception"); + } + + switch (behavior) { + case EXCEPTION_STATE: + r = exception_raise_state(port, thread, task, exception, data, + data_count, &flavor, thread_state, + thread_state_count, thread_state, + &thread_state_count); + break; + case EXCEPTION_STATE_IDENTITY: + r = exception_raise_state_identity(port, thread, task, exception, data, + data_count, &flavor, thread_state, + thread_state_count, thread_state, + &thread_state_count); + break; + /* case EXCEPTION_DEFAULT: */ /* default signal handlers */ + default: /* user-supplied signal handlers */ + r = exception_raise(port, thread, task, exception, data, data_count); + } + + if (behavior == EXCEPTION_STATE || behavior == EXCEPTION_STATE_IDENTITY) { + r = thread_set_state(thread, flavor, thread_state, thread_state_count); + if (r != KERN_SUCCESS) + ABORT("thread_set_state failed in forward_exception"); + } + return r; +} + +#define FWD() GC_forward_exception(thread, task, exception, code, code_count) + +#ifdef ARM32 +# define DARWIN_EXC_STATE ARM_EXCEPTION_STATE +# define DARWIN_EXC_STATE_COUNT ARM_EXCEPTION_STATE_COUNT +# define DARWIN_EXC_STATE_T arm_exception_state_t +# define DARWIN_EXC_STATE_DAR THREAD_FLD_NAME(far) +#elif defined(AARCH64) +# define DARWIN_EXC_STATE ARM_EXCEPTION_STATE64 +# define DARWIN_EXC_STATE_COUNT ARM_EXCEPTION_STATE64_COUNT +# define DARWIN_EXC_STATE_T arm_exception_state64_t +# define DARWIN_EXC_STATE_DAR THREAD_FLD_NAME(far) +#elif defined(POWERPC) +# if CPP_WORDSZ == 32 +# define DARWIN_EXC_STATE PPC_EXCEPTION_STATE +# define DARWIN_EXC_STATE_COUNT PPC_EXCEPTION_STATE_COUNT +# define DARWIN_EXC_STATE_T ppc_exception_state_t +# else +# define DARWIN_EXC_STATE PPC_EXCEPTION_STATE64 +# define DARWIN_EXC_STATE_COUNT PPC_EXCEPTION_STATE64_COUNT +# define DARWIN_EXC_STATE_T ppc_exception_state64_t +# endif +# define DARWIN_EXC_STATE_DAR THREAD_FLD_NAME(dar) +#elif defined(I386) || defined(X86_64) +# if CPP_WORDSZ == 32 +# if defined(i386_EXCEPTION_STATE_COUNT) \ + && !defined(x86_EXCEPTION_STATE32_COUNT) + /* Use old naming convention for 32-bit x86. */ +# define DARWIN_EXC_STATE i386_EXCEPTION_STATE +# define DARWIN_EXC_STATE_COUNT i386_EXCEPTION_STATE_COUNT +# define DARWIN_EXC_STATE_T i386_exception_state_t +# else +# define DARWIN_EXC_STATE x86_EXCEPTION_STATE32 +# define DARWIN_EXC_STATE_COUNT x86_EXCEPTION_STATE32_COUNT +# define DARWIN_EXC_STATE_T x86_exception_state32_t +# endif +# else +# define DARWIN_EXC_STATE x86_EXCEPTION_STATE64 +# define DARWIN_EXC_STATE_COUNT x86_EXCEPTION_STATE64_COUNT +# define DARWIN_EXC_STATE_T x86_exception_state64_t +# endif +# define DARWIN_EXC_STATE_DAR THREAD_FLD_NAME(faultvaddr) +#elif !defined(CPPCHECK) +# error FIXME for non-arm/ppc/x86 darwin +#endif + +/* This violates the namespace rules but there isn't anything that can */ +/* be done about it. The exception handling stuff is hard coded to */ +/* call this. catch_exception_raise, catch_exception_raise_state and */ +/* and catch_exception_raise_state_identity are called from OS. */ +GC_API_OSCALL kern_return_t +catch_exception_raise(mach_port_t exception_port, mach_port_t thread, + mach_port_t task, exception_type_t exception, + exception_data_t code, mach_msg_type_number_t code_count) +{ + kern_return_t r; + char *addr; + thread_state_flavor_t flavor = DARWIN_EXC_STATE; + mach_msg_type_number_t exc_state_count = DARWIN_EXC_STATE_COUNT; + DARWIN_EXC_STATE_T exc_state; + + UNUSED_ARG(exception_port); + UNUSED_ARG(task); + if (exception != EXC_BAD_ACCESS || code[0] != KERN_PROTECTION_FAILURE) { +# ifdef DEBUG_EXCEPTION_HANDLING + /* We aren't interested, pass it on to the old handler */ + GC_log_printf("Exception: 0x%x Code: 0x%x 0x%x in catch...\n", + exception, code_count > 0 ? code[0] : -1, + code_count > 1 ? code[1] : -1); +# else + UNUSED_ARG(code_count); +# endif + return FWD(); + } + + r = thread_get_state(thread, flavor, (natural_t*)&exc_state, + &exc_state_count); + if (r != KERN_SUCCESS) { + /* The thread is supposed to be suspended while the exception */ + /* handler is called. This shouldn't fail. */ +# ifdef BROKEN_EXCEPTION_HANDLING + GC_err_printf("thread_get_state failed in catch_exception_raise\n"); + return KERN_SUCCESS; +# else + ABORT("thread_get_state failed in catch_exception_raise"); +# endif + } + + /* This is the address that caused the fault */ + addr = (char*)exc_state.DARWIN_EXC_STATE_DAR; + if (!is_header_found_async(addr)) { + /* Ugh... just like the SIGBUS problem above, it seems we get */ + /* a bogus KERN_PROTECTION_FAILURE every once and a while. We wait */ + /* till we get a bunch in a row before doing anything about it. */ + /* If a "real" fault ever occurs it'll just keep faulting over and */ + /* over and we'll hit the limit pretty quickly. */ +# ifdef BROKEN_EXCEPTION_HANDLING + static char *last_fault; + static int last_fault_count; + + if (addr != last_fault) { + last_fault = addr; + last_fault_count = 0; + } + if (++last_fault_count < 32) { + if (last_fault_count == 1) + WARN("Ignoring KERN_PROTECTION_FAILURE at %p\n", addr); + return KERN_SUCCESS; + } + + GC_err_printf("Unexpected KERN_PROTECTION_FAILURE at %p; aborting...\n", + (void *)addr); + /* Can't pass it along to the signal handler because that is */ + /* ignoring SIGBUS signals. We also shouldn't call ABORT here as */ + /* signals don't always work too well from the exception handler. */ + EXIT(); +# else /* BROKEN_EXCEPTION_HANDLING */ + /* Pass it along to the next exception handler + (which should call SIGBUS/SIGSEGV) */ + return FWD(); +# endif /* !BROKEN_EXCEPTION_HANDLING */ + } + +# ifdef BROKEN_EXCEPTION_HANDLING + /* Reset the number of consecutive SIGBUS signals. */ + GC_sigbus_count = 0; +# endif + + GC_ASSERT(GC_page_size != 0); + if (GC_mprotect_state == GC_MP_NORMAL) { /* common case */ + struct hblk * h = (struct hblk *)((word)addr & ~(word)(GC_page_size-1)); + size_t i; + +# ifdef CHECKSUMS + GC_record_fault(h); +# endif + UNPROTECT(h, GC_page_size); + for (i = 0; i < divHBLKSZ(GC_page_size); i++) { + word index = PHT_HASH(h+i); + async_set_pht_entry_from_index(GC_dirty_pages, index); + } + } else if (GC_mprotect_state == GC_MP_DISCARDING) { + /* Lie to the thread for now. No sense UNPROTECT()ing the memory + when we're just going to PROTECT() it again later. The thread + will just fault again once it resumes */ + } else { + /* Shouldn't happen, i don't think */ + GC_err_printf("KERN_PROTECTION_FAILURE while world is stopped\n"); + return FWD(); + } + return KERN_SUCCESS; +} +#undef FWD + +#ifndef NO_DESC_CATCH_EXCEPTION_RAISE + /* These symbols should have REFERENCED_DYNAMICALLY (0x10) bit set to */ + /* let strip know they are not to be stripped. */ + __asm__(".desc _catch_exception_raise, 0x10"); + __asm__(".desc _catch_exception_raise_state, 0x10"); + __asm__(".desc _catch_exception_raise_state_identity, 0x10"); +#endif + +#endif /* DARWIN && MPROTECT_VDB */ + +GC_API int GC_CALL GC_incremental_protection_needs(void) +{ + GC_ASSERT(GC_is_initialized); +# ifdef MPROTECT_VDB +# if defined(GWW_VDB) || (defined(SOFT_VDB) && !defined(CHECK_SOFT_VDB)) + /* Only if the incremental mode is already switched on. */ + if (GC_GWW_AVAILABLE()) + return GC_PROTECTS_NONE; +# endif + if (GC_page_size == HBLKSIZE) { + return GC_PROTECTS_POINTER_HEAP; + } else { + return GC_PROTECTS_POINTER_HEAP | GC_PROTECTS_PTRFREE_HEAP; + } +# else + return GC_PROTECTS_NONE; +# endif +} + +GC_API unsigned GC_CALL GC_get_actual_vdb(void) +{ +# ifndef GC_DISABLE_INCREMENTAL + if (GC_incremental) { +# ifndef NO_MANUAL_VDB + if (GC_manual_vdb) return GC_VDB_MANUAL; +# endif +# ifdef MPROTECT_VDB +# ifdef GWW_VDB + if (GC_GWW_AVAILABLE()) return GC_VDB_GWW; +# endif +# ifdef SOFT_VDB + if (GC_GWW_AVAILABLE()) return GC_VDB_SOFT; +# endif + return GC_VDB_MPROTECT; +# elif defined(GWW_VDB) + return GC_VDB_GWW; +# elif defined(SOFT_VDB) + return GC_VDB_SOFT; +# elif defined(PCR_VDB) + return GC_VDB_PCR; +# elif defined(PROC_VDB) + return GC_VDB_PROC; +# else /* DEFAULT_VDB */ + return GC_VDB_DEFAULT; +# endif + } +# endif + return GC_VDB_NONE; +} + +#ifdef ECOS + /* Undo sbrk() redirection. */ +# undef sbrk +#endif + +/* If value is non-zero then allocate executable memory. */ +GC_API void GC_CALL GC_set_pages_executable(int value) +{ + GC_ASSERT(!GC_is_initialized); + /* Even if IGNORE_PAGES_EXECUTABLE is defined, GC_pages_executable is */ + /* touched here to prevent a compiler warning. */ + GC_pages_executable = (GC_bool)(value != 0); +} + +/* Returns non-zero if the GC-allocated memory is executable. */ +/* GC_get_pages_executable is defined after all the places */ +/* where GC_get_pages_executable is undefined. */ +GC_API int GC_CALL GC_get_pages_executable(void) +{ +# ifdef IGNORE_PAGES_EXECUTABLE + return 1; /* Always allocate executable memory. */ +# else + return (int)GC_pages_executable; +# endif +} + +/* Call stack save code for debugging. Should probably be in */ +/* mach_dep.c, but that requires reorganization. */ +#ifdef NEED_CALLINFO + +/* I suspect the following works for most *nix x86 variants, so */ +/* long as the frame pointer is explicitly stored. In the case of gcc, */ +/* compiler flags (e.g. -fomit-frame-pointer) determine whether it is. */ +#if defined(I386) && defined(LINUX) && defined(SAVE_CALL_CHAIN) + struct frame { + struct frame *fr_savfp; + long fr_savpc; +# if NARGS > 0 + long fr_arg[NARGS]; /* All the arguments go here. */ +# endif + }; +#endif + +#if defined(SPARC) +# if defined(LINUX) +# if defined(SAVE_CALL_CHAIN) + struct frame { + long fr_local[8]; + long fr_arg[6]; + struct frame *fr_savfp; + long fr_savpc; +# ifndef __arch64__ + char *fr_stret; +# endif + long fr_argd[6]; + long fr_argx[0]; + }; +# endif +# elif defined (DRSNX) +# include +# elif defined(OPENBSD) +# include +# elif defined(FREEBSD) || defined(NETBSD) +# include +# else +# include +# endif +# if NARGS > 6 +# error We only know how to get the first 6 arguments +# endif +#endif /* SPARC */ + +/* Fill in the pc and argument information for up to NFRAMES of my */ +/* callers. Ignore my frame and my callers frame. */ + +#if defined(GC_HAVE_BUILTIN_BACKTRACE) +# ifdef _MSC_VER + EXTERN_C_BEGIN + int backtrace(void* addresses[], int count); + char** backtrace_symbols(void* const addresses[], int count); + EXTERN_C_END +# else +# include +# endif +#endif /* GC_HAVE_BUILTIN_BACKTRACE */ + +#ifdef SAVE_CALL_CHAIN + +#if NARGS == 0 && NFRAMES % 2 == 0 /* No padding */ \ + && defined(GC_HAVE_BUILTIN_BACKTRACE) + +#ifdef REDIRECT_MALLOC + /* Deal with possible malloc calls in backtrace by omitting */ + /* the infinitely recursing backtrace. */ + STATIC GC_bool GC_in_save_callers = FALSE; +#endif + +GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]) +{ + void * tmp_info[NFRAMES + 1]; + int npcs, i; + + GC_ASSERT(I_HOLD_LOCK()); + /* backtrace() may call dl_iterate_phdr which is also */ + /* used by GC_register_dynamic_libraries(), and */ + /* dl_iterate_phdr is not guaranteed to be reentrant. */ + + GC_STATIC_ASSERT(sizeof(struct callinfo) == sizeof(void *)); +# ifdef REDIRECT_MALLOC + if (GC_in_save_callers) { + info[0].ci_pc = (word)(&GC_save_callers); + BZERO(&info[1], sizeof(void *) * (NFRAMES - 1)); + return; + } + GC_in_save_callers = TRUE; +# endif + + /* We retrieve NFRAMES+1 pc values, but discard the first one, since */ + /* it points to our own frame. */ + npcs = backtrace((void **)tmp_info, NFRAMES + 1); + i = 0; + if (npcs > 1) { + i = npcs - 1; + BCOPY(&tmp_info[1], info, (unsigned)i * sizeof(void *)); + } + BZERO(&info[i], sizeof(void *) * (unsigned)(NFRAMES - i)); +# ifdef REDIRECT_MALLOC + GC_in_save_callers = FALSE; +# endif +} + +#elif defined(I386) || defined(SPARC) + +#if defined(ANY_BSD) && defined(SPARC) +# define FR_SAVFP fr_fp +# define FR_SAVPC fr_pc +#else +# define FR_SAVFP fr_savfp +# define FR_SAVPC fr_savpc +#endif + +#if defined(SPARC) && (defined(__arch64__) || defined(__sparcv9)) +# define BIAS 2047 +#else +# define BIAS 0 +#endif + +GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]) +{ + struct frame *frame; + struct frame *fp; + int nframes = 0; +# ifdef I386 + /* We assume this is turned on only with gcc as the compiler. */ + asm("movl %%ebp,%0" : "=r"(frame)); + fp = frame; +# else /* SPARC */ + frame = (struct frame *)GC_save_regs_in_stack(); + fp = (struct frame *)((long)(frame -> FR_SAVFP) + BIAS); +#endif + + for (; !((word)fp HOTTER_THAN (word)frame) +# ifndef THREADS + && !((word)GC_stackbottom HOTTER_THAN (word)fp) +# elif defined(STACK_GROWS_UP) + && fp != NULL +# endif + && nframes < NFRAMES; + fp = (struct frame *)((long)(fp -> FR_SAVFP) + BIAS), nframes++) { +# if NARGS > 0 + int i; +# endif + + info[nframes].ci_pc = fp -> FR_SAVPC; +# if NARGS > 0 + for (i = 0; i < NARGS; i++) { + info[nframes].ci_arg[i] = GC_HIDE_NZ_POINTER( + (void *)(signed_word)(fp -> fr_arg[i])); + } +# endif /* NARGS > 0 */ + } + if (nframes < NFRAMES) info[nframes].ci_pc = 0; +} + +#endif /* !GC_HAVE_BUILTIN_BACKTRACE */ + +#endif /* SAVE_CALL_CHAIN */ + + /* Print info to stderr. We do not hold the allocator lock. */ + GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]) + { + int i, reent_cnt; +# if defined(AO_HAVE_fetch_and_add1) && defined(AO_HAVE_fetch_and_sub1) + static volatile AO_t reentry_count = 0; + + /* Note: alternatively, if available, we may use a thread-local */ + /* storage, thus, enabling concurrent usage of GC_print_callers; */ + /* but practically this has little sense because printing is done */ + /* into a single output stream. */ + GC_ASSERT(I_DONT_HOLD_LOCK()); + reent_cnt = (int)(signed_word)AO_fetch_and_add1(&reentry_count); +# else + static int reentry_count = 0; + + /* Note: this could use a different lock. */ + LOCK(); + reent_cnt = reentry_count++; + UNLOCK(); +# endif +# if NFRAMES == 1 + GC_err_printf("\tCaller at allocation:\n"); +# else + GC_err_printf("\tCall chain at allocation:\n"); +# endif + for (i = 0; i < NFRAMES; i++) { +# if defined(LINUX) && !defined(SMALL_CONFIG) + GC_bool stop = FALSE; +# endif + + if (0 == info[i].ci_pc) + break; +# if NARGS > 0 + { + int j; + + GC_err_printf("\t\targs: "); + for (j = 0; j < NARGS; j++) { + void *p = GC_REVEAL_NZ_POINTER(info[i].ci_arg[j]); + + if (j != 0) GC_err_printf(", "); + GC_err_printf("%ld (%p)", (long)(signed_word)p, p); + } + GC_err_printf("\n"); + } +# endif + if (reent_cnt > 0) { + /* We were called either concurrently or during an allocation */ + /* by backtrace_symbols() called from GC_print_callers; punt. */ + GC_err_printf("\t\t##PC##= 0x%lx\n", + (unsigned long)info[i].ci_pc); + continue; + } + + { + char buf[40]; + char *name; +# if defined(GC_HAVE_BUILTIN_BACKTRACE) \ + && !defined(GC_BACKTRACE_SYMBOLS_BROKEN) + char **sym_name = + backtrace_symbols((void **)(&(info[i].ci_pc)), 1); + if (sym_name != NULL) { + name = sym_name[0]; + } else +# endif + /* else */ { + (void)snprintf(buf, sizeof(buf), "##PC##= 0x%lx", + (unsigned long)info[i].ci_pc); + buf[sizeof(buf) - 1] = '\0'; + name = buf; + } +# if defined(LINUX) && !defined(SMALL_CONFIG) + /* Try for a line number. */ + do { + FILE *pipe; +# define EXE_SZ 100 + static char exe_name[EXE_SZ]; +# define CMD_SZ 200 + char cmd_buf[CMD_SZ]; +# define RESULT_SZ 200 + static char result_buf[RESULT_SZ]; + size_t result_len; + char *old_preload; +# define PRELOAD_SZ 200 + char preload_buf[PRELOAD_SZ]; + static GC_bool found_exe_name = FALSE; + static GC_bool will_fail = FALSE; + + /* Try to get it via a hairy and expensive scheme. */ + /* First we get the name of the executable: */ + if (will_fail) + break; + if (!found_exe_name) { + int ret_code = readlink("/proc/self/exe", exe_name, EXE_SZ); + + if (ret_code < 0 || ret_code >= EXE_SZ + || exe_name[0] != '/') { + will_fail = TRUE; /* Don't try again. */ + break; + } + exe_name[ret_code] = '\0'; + found_exe_name = TRUE; + } + /* Then we use popen to start addr2line -e */ + /* There are faster ways to do this, but hopefully this */ + /* isn't time critical. */ + (void)snprintf(cmd_buf, sizeof(cmd_buf), + "/usr/bin/addr2line -f -e %s 0x%lx", + exe_name, (unsigned long)info[i].ci_pc); + cmd_buf[sizeof(cmd_buf) - 1] = '\0'; + old_preload = GETENV("LD_PRELOAD"); + if (0 != old_preload) { + size_t old_len = strlen(old_preload); + if (old_len >= PRELOAD_SZ) { + will_fail = TRUE; + break; + } + BCOPY(old_preload, preload_buf, old_len + 1); + unsetenv("LD_PRELOAD"); + } + pipe = popen(cmd_buf, "r"); + if (0 != old_preload + && 0 != setenv("LD_PRELOAD", preload_buf, 0)) { + WARN("Failed to reset LD_PRELOAD\n", 0); + } + if (NULL == pipe) { + will_fail = TRUE; + break; + } + result_len = fread(result_buf, 1, RESULT_SZ - 1, pipe); + (void)pclose(pipe); + if (0 == result_len) { + will_fail = TRUE; + break; + } + if (result_buf[result_len - 1] == '\n') --result_len; + result_buf[result_len] = 0; + if (result_buf[0] == '?' + || (result_buf[result_len-2] == ':' + && result_buf[result_len-1] == '0')) + break; + /* Get rid of embedded newline, if any. Test for "main" */ + { + char * nl = strchr(result_buf, '\n'); + if (nl != NULL + && (word)nl < (word)(result_buf + result_len)) { + *nl = ':'; + } + if (strncmp(result_buf, "main", + nl != NULL + ? (size_t)((word)nl /* a cppcheck workaround */ + - COVERT_DATAFLOW(result_buf)) + : result_len) == 0) { + stop = TRUE; + } + } + if (result_len < RESULT_SZ - 25) { + /* Add in hex address */ + (void)snprintf(&result_buf[result_len], + sizeof(result_buf) - result_len, + " [0x%lx]", (unsigned long)info[i].ci_pc); + result_buf[sizeof(result_buf) - 1] = '\0'; + } +# if defined(CPPCHECK) + GC_noop1((unsigned char)name[0]); + /* name computed previously is discarded */ +# endif + name = result_buf; + } while (0); +# endif /* LINUX */ + GC_err_printf("\t\t%s\n", name); +# if defined(GC_HAVE_BUILTIN_BACKTRACE) \ + && !defined(GC_BACKTRACE_SYMBOLS_BROKEN) + if (sym_name != NULL) + free(sym_name); /* May call GC_[debug_]free; that's OK */ +# endif + } +# if defined(LINUX) && !defined(SMALL_CONFIG) + if (stop) + break; +# endif + } +# if defined(AO_HAVE_fetch_and_add1) && defined(AO_HAVE_fetch_and_sub1) + (void)AO_fetch_and_sub1(&reentry_count); +# else + LOCK(); + --reentry_count; + UNLOCK(); +# endif + } + +#endif /* NEED_CALLINFO */ + +#if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG) + /* Dump /proc/self/maps to GC_stderr, to enable looking up names for */ + /* addresses in FIND_LEAK output. */ + void GC_print_address_map(void) + { + const char *maps_ptr; + + GC_ASSERT(I_HOLD_LOCK()); + maps_ptr = GC_get_maps(); + GC_err_printf("---------- Begin address map ----------\n"); + GC_err_puts(maps_ptr); + GC_err_printf("---------- End address map ----------\n"); + } +#endif /* LINUX && ELF */ + +/* + * Copyright (c) 2000-2005 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +#if defined(THREAD_LOCAL_ALLOC) + +#if !defined(THREADS) && !defined(CPPCHECK) +# error Invalid config - THREAD_LOCAL_ALLOC requires GC_THREADS +#endif + +/* + * Copyright (c) 2000-2005 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* Included indirectly from a thread-library-specific file. */ +/* This is the interface for thread-local allocation, whose */ +/* implementation is mostly thread-library-independent. */ +/* Here we describe only the interface that needs to be known */ +/* and invoked from the thread support layer; the actual */ +/* implementation also exports GC_malloc and friends, which */ +/* are declared in gc.h. */ + +#ifndef GC_THREAD_LOCAL_ALLOC_H +#define GC_THREAD_LOCAL_ALLOC_H + + + +#ifdef THREAD_LOCAL_ALLOC + +#if defined(USE_HPUX_TLS) +# error USE_HPUX_TLS macro was replaced by USE_COMPILER_TLS +#endif + +#include + +EXTERN_C_BEGIN + +#if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC) \ + && !defined(USE_WIN32_COMPILER_TLS) && !defined(USE_COMPILER_TLS) \ + && !defined(USE_CUSTOM_SPECIFIC) +# if defined(GC_WIN32_THREADS) +# if defined(CYGWIN32) && GC_GNUC_PREREQ(4, 0) +# if defined(__clang__) + /* As of Cygwin clang3.5.2, thread-local storage is unsupported. */ +# define USE_PTHREAD_SPECIFIC +# else +# define USE_COMPILER_TLS +# endif +# elif defined(__GNUC__) || defined(MSWINCE) +# define USE_WIN32_SPECIFIC +# else +# define USE_WIN32_COMPILER_TLS +# endif /* !GNU */ + +# elif defined(HOST_ANDROID) +# if defined(ARM32) && (GC_GNUC_PREREQ(4, 6) \ + || GC_CLANG_PREREQ_FULL(3, 8, 256229)) +# define USE_COMPILER_TLS +# elif !defined(__clang__) && !defined(ARM32) + /* TODO: Support clang/arm64 */ +# define USE_COMPILER_TLS +# else +# define USE_PTHREAD_SPECIFIC +# endif + +# elif defined(LINUX) && GC_GNUC_PREREQ(3, 3) /* && !HOST_ANDROID */ +# if defined(ARM32) || defined(AVR32) + /* TODO: support Linux/arm */ +# define USE_PTHREAD_SPECIFIC +# elif defined(AARCH64) && defined(__clang__) && !GC_CLANG_PREREQ(8, 0) + /* To avoid "R_AARCH64_ABS64 used with TLS symbol" linker warnings. */ +# define USE_PTHREAD_SPECIFIC +# else +# define USE_COMPILER_TLS +# endif + +# elif (defined(FREEBSD) \ + || (defined(NETBSD) && __NetBSD_Version__ >= 600000000 /* 6.0 */)) \ + && (GC_GNUC_PREREQ(4, 4) || GC_CLANG_PREREQ(3, 9)) +# define USE_COMPILER_TLS + +# elif defined(GC_HPUX_THREADS) +# ifdef __GNUC__ +# define USE_PTHREAD_SPECIFIC + /* Empirically, as of gcc 3.3, USE_COMPILER_TLS doesn't work. */ +# else +# define USE_COMPILER_TLS +# endif + +# elif defined(GC_IRIX_THREADS) || defined(GC_OPENBSD_THREADS) \ + || defined(GC_SOLARIS_THREADS) \ + || defined(NN_PLATFORM_CTR) || defined(NN_BUILD_TARGET_PLATFORM_NX) +# define USE_CUSTOM_SPECIFIC /* Use our own. */ + +# else +# define USE_PTHREAD_SPECIFIC +# endif +#endif /* !USE_x_SPECIFIC */ + +#ifndef THREAD_FREELISTS_KINDS +# ifdef ENABLE_DISCLAIM +# define THREAD_FREELISTS_KINDS (NORMAL+2) +# else +# define THREAD_FREELISTS_KINDS (NORMAL+1) +# endif +#endif /* !THREAD_FREELISTS_KINDS */ + +/* The first GC_TINY_FREELISTS free lists correspond to the first */ +/* GC_TINY_FREELISTS multiples of GC_GRANULE_BYTES, i.e. we keep */ +/* separate free lists for each multiple of GC_GRANULE_BYTES up to */ +/* (GC_TINY_FREELISTS-1) * GC_GRANULE_BYTES. After that they may */ +/* be spread out further. */ + +/* One of these should be declared as the tlfs field in the */ +/* structure pointed to by a GC_thread. */ +typedef struct thread_local_freelists { + void * _freelists[THREAD_FREELISTS_KINDS][GC_TINY_FREELISTS]; +# define ptrfree_freelists _freelists[PTRFREE] +# define normal_freelists _freelists[NORMAL] + /* Note: Preserve *_freelists names for some clients. */ +# ifdef GC_GCJ_SUPPORT + void * gcj_freelists[GC_TINY_FREELISTS]; +# define ERROR_FL ((void *)GC_WORD_MAX) + /* Value used for gcj_freelists[-1]; allocation is */ + /* erroneous. */ +# endif + /* Free lists contain either a pointer or a small count */ + /* reflecting the number of granules allocated at that */ + /* size. */ + /* 0 ==> thread-local allocation in use, free list */ + /* empty. */ + /* > 0, <= DIRECT_GRANULES ==> Using global allocation, */ + /* too few objects of this size have been */ + /* allocated by this thread. */ + /* >= HBLKSIZE => pointer to nonempty free list. */ + /* > DIRECT_GRANULES, < HBLKSIZE ==> transition to */ + /* local alloc, equivalent to 0. */ +# define DIRECT_GRANULES (HBLKSIZE/GC_GRANULE_BYTES) + /* Don't use local free lists for up to this much */ + /* allocation. */ +} *GC_tlfs; + +#if defined(USE_PTHREAD_SPECIFIC) +# define GC_getspecific pthread_getspecific +# define GC_setspecific pthread_setspecific +# define GC_key_create pthread_key_create +# define GC_remove_specific(key) (void)pthread_setspecific(key, NULL) + /* Explicitly delete the value to stop the TLS */ + /* destructor from being called repeatedly. */ +# define GC_remove_specific_after_fork(key, t) (void)0 + /* Should not need any action. */ + typedef pthread_key_t GC_key_t; +#elif defined(USE_COMPILER_TLS) || defined(USE_WIN32_COMPILER_TLS) +# define GC_getspecific(x) (x) +# define GC_setspecific(key, v) ((key) = (v), 0) +# define GC_key_create(key, d) 0 +# define GC_remove_specific(key) (void)GC_setspecific(key, NULL) + /* Just to clear the pointer to tlfs. */ +# define GC_remove_specific_after_fork(key, t) (void)0 + typedef void * GC_key_t; +#elif defined(USE_WIN32_SPECIFIC) +# define GC_getspecific TlsGetValue +# define GC_setspecific(key, v) !TlsSetValue(key, v) + /* We assume 0 == success, msft does the opposite. */ +# ifndef TLS_OUT_OF_INDEXES + /* this is currently missing in WinCE */ +# define TLS_OUT_OF_INDEXES (DWORD)0xFFFFFFFF +# endif +# define GC_key_create(key, d) \ + ((d) != 0 || (*(key) = TlsAlloc()) == TLS_OUT_OF_INDEXES ? -1 : 0) +# define GC_remove_specific(key) (void)GC_setspecific(key, NULL) + /* Need TlsFree on process exit/detach? */ +# define GC_remove_specific_after_fork(key, t) (void)0 + typedef DWORD GC_key_t; +#elif defined(USE_CUSTOM_SPECIFIC) + EXTERN_C_END +/* + * Copyright (c) 2000 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * This is a reimplementation of a subset of the pthread_get/setspecific + * interface. This appears to outperform the standard linuxthreads one + * by a significant margin. + * The major restriction is that each thread may only make a single + * pthread_setspecific call on a single key. (The current data structure + * doesn't really require that. The restriction should be easily removable.) + * We don't currently support the destruction functions, though that + * could be done. + * We also currently assume that only one pthread_setspecific call + * can be executed at a time, though that assumption would be easy to remove + * by adding a lock. + */ + +#ifndef GC_SPECIFIC_H +#define GC_SPECIFIC_H + +#if !defined(GC_THREAD_LOCAL_ALLOC_H) +# error specific.h should be included from thread_local_alloc.h +#endif + +#include + +EXTERN_C_BEGIN + +/* Called during key creation or setspecific. */ +/* For the GC we already hold the allocator lock. */ +/* Currently allocated objects leak on thread exit. */ +/* That's hard to fix, but OK if we allocate garbage */ +/* collected memory. */ +#define MALLOC_CLEAR(n) GC_INTERNAL_MALLOC(n, NORMAL) + +#define TS_CACHE_SIZE 1024 +#define CACHE_HASH(n) ((((n) >> 8) ^ (n)) & (TS_CACHE_SIZE - 1)) + +#define TS_HASH_SIZE 1024 +#define HASH(p) \ + ((unsigned)((((word)(p)) >> 8) ^ (word)(p)) & (TS_HASH_SIZE - 1)) + +#ifdef GC_ASSERTIONS + /* Thread-local storage is not guaranteed to be scanned by GC. */ + /* We hide values stored in "specific" entries for a test purpose. */ + typedef GC_hidden_pointer ts_entry_value_t; +# define TS_HIDE_VALUE(p) GC_HIDE_NZ_POINTER(p) +# define TS_REVEAL_PTR(p) GC_REVEAL_NZ_POINTER(p) +#else + typedef void * ts_entry_value_t; +# define TS_HIDE_VALUE(p) (p) +# define TS_REVEAL_PTR(p) (p) +#endif + +/* An entry describing a thread-specific value for a given thread. */ +/* All such accessible structures preserve the invariant that if either */ +/* thread is a valid pthread id or qtid is a valid "quick thread id" */ +/* for a thread, then value holds the corresponding thread specific */ +/* value. This invariant must be preserved at ALL times, since */ +/* asynchronous reads are allowed. */ +typedef struct thread_specific_entry { + volatile AO_t qtid; /* quick thread id, only for cache */ + ts_entry_value_t value; + struct thread_specific_entry *next; + pthread_t thread; +} tse; + +/* We represent each thread-specific datum as two tables. The first is */ +/* a cache, indexed by a "quick thread identifier". The "quick" thread */ +/* identifier is an easy to compute value, which is guaranteed to */ +/* determine the thread, though a thread may correspond to more than */ +/* one value. We typically use the address of a page in the stack. */ +/* The second is a hash table, indexed by pthread_self(). It is used */ +/* only as a backup. */ + +/* Return the "quick thread id". Default version. Assumes page size, */ +/* or at least thread stack separation, is at least 4 KB. */ +/* Must be defined so that it never returns 0. (Page 0 can't really be */ +/* part of any stack, since that would make 0 a valid stack pointer.) */ +#define quick_thread_id() (((word)GC_approx_sp()) >> 12) + +#define INVALID_QTID ((word)0) +#define INVALID_THREADID ((pthread_t)0) + +union ptse_ao_u { + tse *p; + volatile AO_t ao; +}; + +typedef struct thread_specific_data { + tse * volatile cache[TS_CACHE_SIZE]; + /* A faster index to the hash table */ + union ptse_ao_u hash[TS_HASH_SIZE]; + pthread_mutex_t lock; +} tsd; + +typedef tsd * GC_key_t; + +#define GC_key_create(key, d) GC_key_create_inner(key) +GC_INNER int GC_key_create_inner(tsd ** key_ptr); +GC_INNER int GC_setspecific(tsd * key, void * value); +#define GC_remove_specific(key) \ + GC_remove_specific_after_fork(key, pthread_self()) +GC_INNER void GC_remove_specific_after_fork(tsd * key, pthread_t t); + +/* An internal version of getspecific that assumes a cache miss. */ +GC_INNER void * GC_slow_getspecific(tsd * key, word qtid, + tse * volatile * cache_entry); + +GC_INLINE void * GC_getspecific(tsd * key) +{ + word qtid = quick_thread_id(); + tse * volatile * entry_ptr = &(key -> cache[CACHE_HASH(qtid)]); + tse * entry = *entry_ptr; /* Must be loaded only once. */ + + GC_ASSERT(qtid != INVALID_QTID); + if (EXPECT(entry -> qtid == qtid, TRUE)) { + GC_ASSERT(entry -> thread == pthread_self()); + return TS_REVEAL_PTR(entry -> value); + } + return GC_slow_getspecific(key, qtid, entry_ptr); +} + +EXTERN_C_END + +#endif /* GC_SPECIFIC_H */ + + EXTERN_C_BEGIN +#else +# error implement me +#endif + +/* Each thread structure must be initialized. */ +/* This call must be made from the new thread. */ +/* Caller should hold the allocator lock. */ +GC_INNER void GC_init_thread_local(GC_tlfs p); + +/* Called when a thread is unregistered, or exits. */ +/* Caller should hold the allocator lock. */ +GC_INNER void GC_destroy_thread_local(GC_tlfs p); + +/* The thread support layer must arrange to mark thread-local */ +/* free lists explicitly, since the link field is often */ +/* invisible to the marker. It knows how to find all threads; */ +/* we take care of an individual thread freelist structure. */ +GC_INNER void GC_mark_thread_local_fls_for(GC_tlfs p); + +#ifdef GC_ASSERTIONS + GC_bool GC_is_thread_tsd_valid(void *tsd); + void GC_check_tls_for(GC_tlfs p); +# if defined(USE_CUSTOM_SPECIFIC) + void GC_check_tsd_marks(tsd *key); +# endif +#endif /* GC_ASSERTIONS */ + +#ifndef GC_ATTR_TLS_FAST +# define GC_ATTR_TLS_FAST /* empty */ +#endif + +extern +#if defined(USE_COMPILER_TLS) + __thread GC_ATTR_TLS_FAST +#elif defined(USE_WIN32_COMPILER_TLS) + __declspec(thread) GC_ATTR_TLS_FAST +#endif + GC_key_t GC_thread_key; +/* This is set up by the thread_local_alloc implementation. No need */ +/* for cleanup on thread exit. But the thread support layer makes sure */ +/* that GC_thread_key is traced, if necessary. */ + +EXTERN_C_END + +#endif /* THREAD_LOCAL_ALLOC */ + +#endif /* GC_THREAD_LOCAL_ALLOC_H */ + + +#if defined(USE_COMPILER_TLS) + __thread GC_ATTR_TLS_FAST +#elif defined(USE_WIN32_COMPILER_TLS) + __declspec(thread) GC_ATTR_TLS_FAST +#endif +GC_key_t GC_thread_key; + +static GC_bool keys_initialized; + +/* Return a single nonempty freelist fl to the global one pointed to */ +/* by gfl. */ + +static void return_single_freelist(void *fl, void **gfl) +{ + if (*gfl == 0) { + *gfl = fl; + } else { + void *q, **qptr; + + GC_ASSERT(GC_size(fl) == GC_size(*gfl)); + /* Concatenate: */ + qptr = &(obj_link(fl)); + while ((word)(q = *qptr) >= HBLKSIZE) + qptr = &(obj_link(q)); + GC_ASSERT(0 == q); + *qptr = *gfl; + *gfl = fl; + } +} + +/* Recover the contents of the freelist array fl into the global one gfl. */ +static void return_freelists(void **fl, void **gfl) +{ + int i; + + for (i = 1; i < GC_TINY_FREELISTS; ++i) { + if ((word)(fl[i]) >= HBLKSIZE) { + return_single_freelist(fl[i], &gfl[i]); + } + /* Clear fl[i], since the thread structure may hang around. */ + /* Do it in a way that is likely to trap if we access it. */ + fl[i] = (ptr_t)HBLKSIZE; + } + /* The 0 granule freelist really contains 1 granule objects. */ + if ((word)fl[0] >= HBLKSIZE +# ifdef GC_GCJ_SUPPORT + && fl[0] != ERROR_FL +# endif + ) { + return_single_freelist(fl[0], &gfl[1]); + } +} + +#ifdef USE_PTHREAD_SPECIFIC + /* Re-set the TLS value on thread cleanup to allow thread-local */ + /* allocations to happen in the TLS destructors. */ + /* GC_unregister_my_thread (and similar routines) will finally set */ + /* the GC_thread_key to NULL preventing this destructor from being */ + /* called repeatedly. */ + static void reset_thread_key(void* v) { + pthread_setspecific(GC_thread_key, v); + } +#else +# define reset_thread_key 0 +#endif + +/* Each thread structure must be initialized. */ +/* This call must be made from the new thread. */ +GC_INNER void GC_init_thread_local(GC_tlfs p) +{ + int i, j, res; + + GC_ASSERT(I_HOLD_LOCK()); + if (!EXPECT(keys_initialized, TRUE)) { +# ifdef USE_CUSTOM_SPECIFIC + /* Ensure proper alignment of a "pushed" GC symbol. */ + GC_ASSERT((word)(&GC_thread_key) % sizeof(word) == 0); +# endif + res = GC_key_create(&GC_thread_key, reset_thread_key); + if (COVERT_DATAFLOW(res) != 0) { + ABORT("Failed to create key for local allocator"); + } + keys_initialized = TRUE; + } + res = GC_setspecific(GC_thread_key, p); + if (COVERT_DATAFLOW(res) != 0) { + ABORT("Failed to set thread specific allocation pointers"); + } + for (j = 0; j < GC_TINY_FREELISTS; ++j) { + for (i = 0; i < THREAD_FREELISTS_KINDS; ++i) { + p -> _freelists[i][j] = (void *)(word)1; + } +# ifdef GC_GCJ_SUPPORT + p -> gcj_freelists[j] = (void *)(word)1; +# endif + } + /* The size 0 free lists are handled like the regular free lists, */ + /* to ensure that the explicit deallocation works. However, */ + /* allocation of a size 0 "gcj" object is always an error. */ +# ifdef GC_GCJ_SUPPORT + p -> gcj_freelists[0] = ERROR_FL; +# endif +} + +GC_INNER void GC_destroy_thread_local(GC_tlfs p) +{ + int k; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_getspecific(GC_thread_key) == p); + /* We currently only do this from the thread itself. */ + GC_STATIC_ASSERT(THREAD_FREELISTS_KINDS <= MAXOBJKINDS); + for (k = 0; k < THREAD_FREELISTS_KINDS; ++k) { + if (k == (int)GC_n_kinds) + break; /* kind is not created */ + return_freelists(p -> _freelists[k], GC_obj_kinds[k].ok_freelist); + } +# ifdef GC_GCJ_SUPPORT + return_freelists(p -> gcj_freelists, (void **)GC_gcjobjfreelist); +# endif +} + +STATIC void *GC_get_tlfs(void) +{ +# if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC) + GC_key_t k = GC_thread_key; + + if (EXPECT(0 == k, FALSE)) { + /* We have not yet run GC_init_parallel. That means we also */ + /* are not locking, so GC_malloc_kind_global is fairly cheap. */ + return NULL; + } + return GC_getspecific(k); +# else + if (EXPECT(!keys_initialized, FALSE)) return NULL; + + return GC_getspecific(GC_thread_key); +# endif +} + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_kind(size_t bytes, int kind) +{ + size_t granules; + void *tsd; + void *result; + +# if MAXOBJKINDS > THREAD_FREELISTS_KINDS + if (EXPECT(kind >= THREAD_FREELISTS_KINDS, FALSE)) { + return GC_malloc_kind_global(bytes, kind); + } +# endif + tsd = GC_get_tlfs(); + if (EXPECT(NULL == tsd, FALSE)) { + return GC_malloc_kind_global(bytes, kind); + } + GC_ASSERT(GC_is_initialized); + GC_ASSERT(GC_is_thread_tsd_valid(tsd)); + granules = ALLOC_REQUEST_GRANS(bytes); +# if defined(CPPCHECK) +# define MALLOC_KIND_PTRFREE_INIT (void*)1 +# else +# define MALLOC_KIND_PTRFREE_INIT NULL +# endif + GC_FAST_MALLOC_GRANS(result, granules, + ((GC_tlfs)tsd) -> _freelists[kind], DIRECT_GRANULES, + kind, GC_malloc_kind_global(bytes, kind), + (void)(kind == PTRFREE ? MALLOC_KIND_PTRFREE_INIT + : (obj_link(result) = 0))); +# ifdef LOG_ALLOCS + GC_log_printf("GC_malloc_kind(%lu, %d) returned %p, recent GC #%lu\n", + (unsigned long)bytes, kind, result, + (unsigned long)GC_gc_no); +# endif + return result; +} + +#ifdef GC_GCJ_SUPPORT + + + +/* Gcj-style allocation without locks is extremely tricky. The */ +/* fundamental issue is that we may end up marking a free list, which */ +/* has freelist links instead of "vtable" pointers. That is usually */ +/* OK, since the next object on the free list will be cleared, and */ +/* will thus be interpreted as containing a zero descriptor. That's */ +/* fine if the object has not yet been initialized. But there are */ +/* interesting potential races. */ +/* In the case of incremental collection, this seems hopeless, since */ +/* the marker may run asynchronously, and may pick up the pointer to */ +/* the next freelist entry (which it thinks is a vtable pointer), get */ +/* suspended for a while, and then see an allocated object instead */ +/* of the vtable. This may be avoidable with either a handshake with */ +/* the collector or, probably more easily, by moving the free list */ +/* links to the second word of each object. The latter isn't a */ +/* universal win, since on architecture like Itanium, nonzero offsets */ +/* are not necessarily free. And there may be cache fill order issues. */ +/* For now, we punt with incremental GC. This probably means that */ +/* incremental GC should be enabled before we fork a second thread. */ +/* Unlike the other thread local allocation calls, we assume that the */ +/* collector has been explicitly initialized. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_gcj_malloc(size_t bytes, + void * ptr_to_struct_containing_descr) +{ + if (EXPECT(GC_incremental, FALSE)) { + return GC_core_gcj_malloc(bytes, ptr_to_struct_containing_descr, 0); + } else { + size_t granules = ALLOC_REQUEST_GRANS(bytes); + void *result; + void **tiny_fl; + + GC_ASSERT(GC_gcjobjfreelist != NULL); + tiny_fl = ((GC_tlfs)GC_getspecific(GC_thread_key))->gcj_freelists; + GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES, + GC_gcj_kind, + GC_core_gcj_malloc(bytes, + ptr_to_struct_containing_descr, + 0 /* flags */), + {AO_compiler_barrier(); + *(void **)result = ptr_to_struct_containing_descr;}); + /* This forces the initialization of the "method ptr". */ + /* This is necessary to ensure some very subtle properties */ + /* required if a GC is run in the middle of such an allocation. */ + /* Here we implicitly also assume atomicity for the free list. */ + /* and method pointer assignments. */ + /* We must update the freelist before we store the pointer. */ + /* Otherwise a GC at this point would see a corrupted */ + /* free list. */ + /* A real memory barrier is not needed, since the */ + /* action of stopping this thread will cause prior writes */ + /* to complete. */ + /* We assert that any concurrent marker will stop us. */ + /* Thus it is impossible for a mark procedure to see the */ + /* allocation of the next object, but to see this object */ + /* still containing a free list pointer. Otherwise the */ + /* marker, by misinterpreting the freelist link as a vtable */ + /* pointer, might find a random "mark descriptor" in the next */ + /* object. */ + return result; + } +} + +#endif /* GC_GCJ_SUPPORT */ + +/* The thread support layer must arrange to mark thread-local */ +/* free lists explicitly, since the link field is often */ +/* invisible to the marker. It knows how to find all threads; */ +/* we take care of an individual thread freelist structure. */ +GC_INNER void GC_mark_thread_local_fls_for(GC_tlfs p) +{ + ptr_t q; + int i, j; + + for (j = 0; j < GC_TINY_FREELISTS; ++j) { + for (i = 0; i < THREAD_FREELISTS_KINDS; ++i) { + /* Load the pointer atomically as it might be updated */ + /* concurrently by GC_FAST_MALLOC_GRANS. */ + q = (ptr_t)AO_load((volatile AO_t *)&p->_freelists[i][j]); + if ((word)q > HBLKSIZE) + GC_set_fl_marks(q); + } +# ifdef GC_GCJ_SUPPORT + if (EXPECT(j > 0, TRUE)) { + q = (ptr_t)AO_load((volatile AO_t *)&p->gcj_freelists[j]); + if ((word)q > HBLKSIZE) + GC_set_fl_marks(q); + } +# endif + } +} + +#if defined(GC_ASSERTIONS) + /* Check that all thread-local free-lists in p are completely marked. */ + void GC_check_tls_for(GC_tlfs p) + { + int i, j; + + for (j = 1; j < GC_TINY_FREELISTS; ++j) { + for (i = 0; i < THREAD_FREELISTS_KINDS; ++i) { + GC_check_fl_marks(&p->_freelists[i][j]); + } +# ifdef GC_GCJ_SUPPORT + GC_check_fl_marks(&p->gcj_freelists[j]); +# endif + } + } +#endif /* GC_ASSERTIONS */ + +#endif /* THREAD_LOCAL_ALLOC */ + + +/* Most platform-specific files go here... */ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2010 by Hewlett-Packard Development Company. + * All rights reserved. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * Copyright (c) 2009-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* Private declarations for threads support. */ + +#ifndef GC_PTHREAD_SUPPORT_H +#define GC_PTHREAD_SUPPORT_H + + + +#ifdef THREADS + +#if defined(GC_PTHREADS) || defined(GC_PTHREADS_PARAMARK) +# include +#endif + +#ifdef GC_DARWIN_THREADS +# include +# include +#endif + +#ifdef THREAD_LOCAL_ALLOC + +#endif + +#ifdef THREAD_SANITIZER + +#endif + +EXTERN_C_BEGIN + +typedef struct GC_StackContext_Rep { +# if defined(THREAD_SANITIZER) && defined(SIGNAL_BASED_STOP_WORLD) + char dummy[sizeof(oh)]; /* A dummy field to avoid TSan false */ + /* positive about the race between */ + /* GC_has_other_debug_info and */ + /* GC_suspend_handler_inner (which */ + /* sets stack_ptr). */ +# endif + +# if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS) + volatile +# endif + ptr_t stack_end; /* Cold end of the stack (except for */ + /* main thread on non-Windows). */ + /* On Windows: 0 means entry invalid; */ + /* not in_use implies stack_end is 0. */ + + ptr_t stack_ptr; /* Valid only in some platform-specific states. */ + +# ifdef GC_WIN32_THREADS +# define ADDR_LIMIT ((ptr_t)GC_WORD_MAX) + ptr_t last_stack_min; /* Last known minimum (hottest) address */ + /* in stack or ADDR_LIMIT if unset. */ +# ifdef I386 + ptr_t initial_stack_base; /* The cold end of the stack saved by */ + /* GC_record_stack_base (never modified */ + /* by GC_set_stackbottom); used for the */ + /* old way of the coroutines support. */ +# endif +# elif defined(GC_DARWIN_THREADS) && !defined(DARWIN_DONT_PARSE_STACK) + ptr_t topOfStack; /* Result of GC_FindTopOfStack(0); */ + /* valid only if the thread is blocked; */ + /* non-NULL value means already set. */ +# endif + +# if defined(E2K) || defined(IA64) + ptr_t backing_store_end; + ptr_t backing_store_ptr; +# endif + +# ifdef GC_WIN32_THREADS + /* For now, alt-stack is not implemented for Win32. */ +# else + ptr_t altstack; /* The start of the alt-stack if there */ + /* is one, NULL otherwise. */ + word altstack_size; /* The size of the alt-stack if exists. */ + ptr_t normstack; /* The start and size of the "normal" */ + /* stack (set by GC_register_altstack). */ + word normstack_size; +# endif + +# ifdef E2K + size_t ps_ofs; /* the current offset of the procedure stack */ +# endif + +# ifndef GC_NO_FINALIZATION + unsigned char finalizer_nested; + char fnlz_pad[1]; /* Explicit alignment (for some rare */ + /* compilers such as bcc32 and wcc32). */ + unsigned short finalizer_skipped; + /* Used by GC_check_finalizer_nested() */ + /* to minimize the level of recursion */ + /* when a client finalizer allocates */ + /* memory (initially both are 0). */ +# endif + + struct GC_traced_stack_sect_s *traced_stack_sect; + /* Points to the "frame" data held in */ + /* stack by the innermost */ + /* GC_call_with_gc_active() of this */ + /* stack (thread); may be NULL. */ + +} *GC_stack_context_t; + +#ifdef GC_WIN32_THREADS + typedef DWORD thread_id_t; +# define thread_id_self() GetCurrentThreadId() +# define THREAD_ID_EQUAL(id1, id2) ((id1) == (id2)) +#else + typedef pthread_t thread_id_t; +# define thread_id_self() pthread_self() +# define THREAD_ID_EQUAL(id1, id2) THREAD_EQUAL(id1, id2) +#endif + +typedef struct GC_Thread_Rep { + union { +# if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS) + volatile AO_t in_use; /* Updated without a lock. We assert */ + /* that each unused entry has invalid */ + /* id of zero and zero stack_end. */ + /* Used only with GC_win32_dll_threads. */ + LONG long_in_use; /* The same but of the type that */ + /* matches the first argument of */ + /* InterlockedExchange(); volatile is */ + /* omitted because the ancient version */ + /* of the prototype lacked the */ + /* qualifier. */ +# endif + struct GC_Thread_Rep *next; /* Hash table link without */ + /* GC_win32_dll_threads. */ + /* More recently allocated threads */ + /* with a given pthread id come */ + /* first. (All but the first are */ + /* guaranteed to be dead, but we may */ + /* not yet have registered the join.) */ + } tm; /* table_management */ + + GC_stack_context_t crtn; + + thread_id_t id; /* hash table key */ +# ifdef GC_DARWIN_THREADS + mach_port_t mach_thread; +# elif defined(GC_WIN32_THREADS) && defined(GC_PTHREADS) + pthread_t pthread_id; +# elif defined(USE_TKILL_ON_ANDROID) + pid_t kernel_id; +# endif + +# ifdef MSWINCE + /* According to MSDN specs for WinCE targets: */ + /* - DuplicateHandle() is not applicable to thread handles; and */ + /* - the value returned by GetCurrentThreadId() could be used as */ + /* a "real" thread handle (for SuspendThread(), ResumeThread() */ + /* and GetThreadContext()). */ +# define THREAD_HANDLE(p) ((HANDLE)(word)(p) -> id) +# elif defined(GC_WIN32_THREADS) + HANDLE handle; +# define THREAD_HANDLE(p) ((p) -> handle) +# endif /* GC_WIN32_THREADS && !MSWINCE */ + + unsigned char flags; /* Protected by the allocator lock. */ +# define FINISHED 0x1 /* Thread has exited (pthreads only). */ +# ifndef GC_PTHREADS +# define KNOWN_FINISHED(p) FALSE +# else +# define KNOWN_FINISHED(p) (((p) -> flags & FINISHED) != 0) +# define DETACHED 0x2 /* Thread is treated as detached. */ + /* Thread may really be detached, or */ + /* it may have been explicitly */ + /* registered, in which case we can */ + /* deallocate its GC_Thread_Rep once */ + /* it unregisters itself, since it */ + /* may not return a GC pointer. */ +# endif +# if (defined(GC_HAVE_PTHREAD_EXIT) || !defined(GC_NO_PTHREAD_CANCEL)) \ + && defined(GC_PTHREADS) +# define DISABLED_GC 0x10 /* Collections are disabled while the */ + /* thread is exiting. */ +# endif +# define DO_BLOCKING 0x20 /* Thread is in the do-blocking state. */ + /* If set, the thread will acquire the */ + /* allocator lock before any pointer */ + /* manipulation, and has set its SP */ + /* value. Thus, it does not need */ + /* a signal sent to stop it. */ +# ifdef GC_WIN32_THREADS +# define IS_SUSPENDED 0x40 /* Thread is suspended by SuspendThread. */ +# endif + + char flags_pad[sizeof(word) - 1 /* sizeof(flags) */]; + /* Explicit alignment (for some rare */ + /* compilers such as bcc32 and wcc32). */ + +# ifdef SIGNAL_BASED_STOP_WORLD + volatile AO_t last_stop_count; + /* The value of GC_stop_count when the */ + /* thread last successfully handled */ + /* a suspend signal. */ +# ifdef GC_ENABLE_SUSPEND_THREAD + volatile AO_t ext_suspend_cnt; + /* An odd value means thread was */ + /* suspended externally; incremented on */ + /* every call of GC_suspend_thread() */ + /* and GC_resume_thread(); updated with */ + /* the allocator lock held, but could */ + /* be read from a signal handler. */ +# endif +# endif + +# ifdef GC_PTHREADS + void *status; /* The value returned from the thread. */ + /* Used only to avoid premature */ + /* reclamation of any data it might */ + /* reference. */ + /* This is unfortunately also the */ + /* reason we need to intercept join */ + /* and detach. */ +# endif + +# ifdef THREAD_LOCAL_ALLOC + struct thread_local_freelists tlfs GC_ATTR_WORD_ALIGNED; +# endif + +# ifdef NACL + /* Grab NACL_GC_REG_STORAGE_SIZE pointers off the stack when */ + /* going into a syscall. 20 is more than we need, but it's an */ + /* overestimate in case the instrumented function uses any callee */ + /* saved registers, they may be pushed to the stack much earlier. */ + /* Also, on x64 'push' puts 8 bytes on the stack even though */ + /* our pointers are 4 bytes. */ +# ifdef ARM32 + /* Space for r4-r8, r10-r12, r14. */ +# define NACL_GC_REG_STORAGE_SIZE 9 +# else +# define NACL_GC_REG_STORAGE_SIZE 20 +# endif + ptr_t reg_storage[NACL_GC_REG_STORAGE_SIZE]; +# elif defined(PLATFORM_HAVE_GC_REG_STORAGE_SIZE) + word registers[PLATFORM_GC_REG_STORAGE_SIZE]; /* used externally */ +# endif + +# if defined(WOW64_THREAD_CONTEXT_WORKAROUND) && defined(MSWINRT_FLAVOR) + PNT_TIB tib; +# endif + +# ifdef RETRY_GET_THREAD_CONTEXT /* && GC_WIN32_THREADS */ + ptr_t context_sp; + word context_regs[PUSHED_REGS_COUNT]; + /* Populated as part of GC_suspend() as */ + /* resume/suspend loop may be needed */ + /* for GetThreadContext() to succeed. */ +# endif +} * GC_thread; + +#ifndef THREAD_TABLE_SZ +# define THREAD_TABLE_SZ 256 /* Power of 2 (for speed). */ +#endif + +#ifdef GC_WIN32_THREADS +# define THREAD_TABLE_INDEX(id) /* id is of DWORD type */ \ + (int)((((id) >> 8) ^ (id)) % THREAD_TABLE_SZ) +#elif CPP_WORDSZ == 64 +# define THREAD_TABLE_INDEX(id) \ + (int)(((((NUMERIC_THREAD_ID(id) >> 8) ^ NUMERIC_THREAD_ID(id)) >> 16) \ + ^ ((NUMERIC_THREAD_ID(id) >> 8) ^ NUMERIC_THREAD_ID(id))) \ + % THREAD_TABLE_SZ) +#else +# define THREAD_TABLE_INDEX(id) \ + (int)(((NUMERIC_THREAD_ID(id) >> 16) \ + ^ (NUMERIC_THREAD_ID(id) >> 8) \ + ^ NUMERIC_THREAD_ID(id)) % THREAD_TABLE_SZ) +#endif + +/* The set of all known threads. We intercept thread creation and */ +/* join/detach. Protected by the allocator lock. */ +GC_EXTERN GC_thread GC_threads[THREAD_TABLE_SZ]; + +#ifndef MAX_MARKERS +# define MAX_MARKERS 16 +#endif + +#ifdef GC_ASSERTIONS + GC_EXTERN GC_bool GC_thr_initialized; +#endif + +#ifdef STACKPTR_CORRECTOR_AVAILABLE + GC_EXTERN GC_sp_corrector_proc GC_sp_corrector; +#endif + +GC_EXTERN GC_on_thread_event_proc GC_on_thread_event; + +#ifdef GC_WIN32_THREADS + +# ifdef GC_NO_THREADS_DISCOVERY +# define GC_win32_dll_threads FALSE +# elif defined(GC_DISCOVER_TASK_THREADS) +# define GC_win32_dll_threads TRUE +# else + GC_EXTERN GC_bool GC_win32_dll_threads; +# endif + +# ifdef PARALLEL_MARK + GC_EXTERN int GC_available_markers_m1; + GC_EXTERN unsigned GC_required_markers_cnt; + GC_EXTERN ptr_t GC_marker_sp[MAX_MARKERS - 1]; + GC_EXTERN ptr_t GC_marker_last_stack_min[MAX_MARKERS - 1]; +# ifndef GC_PTHREADS_PARAMARK + GC_EXTERN thread_id_t GC_marker_Id[MAX_MARKERS - 1]; +# endif +# if !defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID) && !defined(MSWINCE) + GC_INNER void GC_init_win32_thread_naming(HMODULE hK32); +# endif +# ifdef GC_PTHREADS_PARAMARK + GC_INNER void *GC_mark_thread(void *); +# elif defined(MSWINCE) + GC_INNER DWORD WINAPI GC_mark_thread(LPVOID); +# else + GC_INNER unsigned __stdcall GC_mark_thread(void *); +# endif +# endif /* PARALLEL_MARK */ + + GC_INNER GC_thread GC_new_thread(thread_id_t); + GC_INNER void GC_record_stack_base(GC_stack_context_t crtn, + const struct GC_stack_base *sb); + GC_INNER GC_thread GC_register_my_thread_inner( + const struct GC_stack_base *sb, + thread_id_t self_id); + +# ifdef GC_PTHREADS + GC_INNER void GC_win32_cache_self_pthread(thread_id_t); +# else + GC_INNER void GC_delete_thread(GC_thread); +# endif + +# ifdef CAN_HANDLE_FORK + GC_INNER void GC_setup_atfork(void); +# endif + +# if !defined(DONT_USE_ATEXIT) || !defined(GC_NO_THREADS_DISCOVERY) + GC_EXTERN thread_id_t GC_main_thread_id; +# endif + +# ifndef GC_NO_THREADS_DISCOVERY + GC_INNER GC_thread GC_win32_dll_lookup_thread(thread_id_t); +# endif + +# ifdef MPROTECT_VDB + /* Make sure given thread descriptor is not protected by the VDB */ + /* implementation. Used to prevent write faults when the world */ + /* is (partially) stopped, since it may have been stopped with */ + /* a system lock held, and that lock may be required for fault */ + /* handling. */ + GC_INNER void GC_win32_unprotect_thread(GC_thread); +# else +# define GC_win32_unprotect_thread(t) (void)(t) +# endif /* !MPROTECT_VDB */ + +#else +# define GC_win32_dll_threads FALSE +#endif /* !GC_WIN32_THREADS */ + +#ifdef GC_PTHREADS +# if defined(GC_WIN32_THREADS) && !defined(CYGWIN32) \ + && (defined(GC_WIN32_PTHREADS) || defined(GC_PTHREADS_PARAMARK)) \ + && !defined(__WINPTHREADS_VERSION_MAJOR) +# define GC_PTHREAD_PTRVAL(pthread_id) pthread_id.p +# else +# define GC_PTHREAD_PTRVAL(pthread_id) pthread_id +# endif /* !GC_WIN32_THREADS || CYGWIN32 */ +# ifdef GC_WIN32_THREADS + GC_INNER GC_thread GC_lookup_by_pthread(pthread_t); +# else +# define GC_lookup_by_pthread(t) GC_lookup_thread(t) +# endif +#endif /* GC_PTHREADS */ + +GC_INNER GC_thread GC_lookup_thread(thread_id_t); +#define GC_self_thread_inner() GC_lookup_thread(thread_id_self()) + +GC_INNER void GC_wait_for_gc_completion(GC_bool); + +#ifdef NACL + GC_INNER void GC_nacl_initialize_gc_thread(GC_thread); + GC_INNER void GC_nacl_shutdown_gc_thread(void); +#endif + +#if defined(PTHREAD_STOP_WORLD_IMPL) && !defined(NO_SIGNALS_UNBLOCK_IN_MAIN) \ + || defined(GC_EXPLICIT_SIGNALS_UNBLOCK) + GC_INNER void GC_unblock_gc_signals(void); +#endif + +#if defined(GC_ENABLE_SUSPEND_THREAD) && defined(SIGNAL_BASED_STOP_WORLD) + GC_INNER void GC_suspend_self_inner(GC_thread me, word suspend_cnt); + + GC_INNER void GC_suspend_self_blocked(ptr_t thread_me, void *context); + /* Wrapper over GC_suspend_self_inner. */ +#endif + +#if defined(GC_PTHREADS) \ + && !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) + +# ifdef GC_PTHREAD_START_STANDALONE +# define GC_INNER_PTHRSTART /* empty */ +# else +# define GC_INNER_PTHRSTART GC_INNER +# endif + + GC_INNER_PTHRSTART void *GC_CALLBACK GC_pthread_start_inner( + struct GC_stack_base *sb, void *arg); + GC_INNER_PTHRSTART GC_thread GC_start_rtn_prepare_thread( + void *(**pstart)(void *), + void **pstart_arg, + struct GC_stack_base *sb, void *arg); + GC_INNER_PTHRSTART void GC_thread_exit_proc(void *); +#endif /* GC_PTHREADS */ + +#ifdef GC_DARWIN_THREADS +# ifndef DARWIN_DONT_PARSE_STACK + GC_INNER ptr_t GC_FindTopOfStack(unsigned long); +# endif +# if defined(PARALLEL_MARK) && !defined(GC_NO_THREADS_DISCOVERY) + GC_INNER GC_bool GC_is_mach_marker(thread_act_t); +# endif +#endif /* GC_DARWIN_THREADS */ + +#ifdef PTHREAD_STOP_WORLD_IMPL + GC_INNER void GC_stop_init(void); +#endif + +EXTERN_C_END + +#endif /* THREADS */ + +#endif /* GC_PTHREAD_SUPPORT_H */ + + +/* This probably needs more porting work to ppc64. */ + +#if defined(GC_DARWIN_THREADS) + +#include +#include + +#if defined(ARM32) && defined(ARM_THREAD_STATE32) +# include +#endif + +/* From "Inside Mac OS X - Mach-O Runtime Architecture" published by Apple + Page 49: + "The space beneath the stack pointer, where a new stack frame would normally + be allocated, is called the red zone. This area as shown in Figure 3-2 may + be used for any purpose as long as a new stack frame does not need to be + added to the stack." + + Page 50: "If a leaf procedure's red zone usage would exceed 224 bytes, then + it must set up a stack frame just like routines that call other routines." +*/ +#ifdef POWERPC +# if CPP_WORDSZ == 32 +# define PPC_RED_ZONE_SIZE 224 +# elif CPP_WORDSZ == 64 +# define PPC_RED_ZONE_SIZE 320 +# endif +#endif + +#ifndef DARWIN_DONT_PARSE_STACK + +typedef struct StackFrame { + unsigned long savedSP; + unsigned long savedCR; + unsigned long savedLR; + unsigned long reserved[2]; + unsigned long savedRTOC; +} StackFrame; + +GC_INNER ptr_t GC_FindTopOfStack(unsigned long stack_start) +{ + StackFrame *frame = (StackFrame *)stack_start; + + if (stack_start == 0) { +# ifdef POWERPC +# if CPP_WORDSZ == 32 + __asm__ __volatile__ ("lwz %0,0(r1)" : "=r" (frame)); +# else + __asm__ __volatile__ ("ld %0,0(r1)" : "=r" (frame)); +# endif +# elif defined(ARM32) + volatile ptr_t sp_reg; + __asm__ __volatile__ ("mov %0, r7\n" : "=r" (sp_reg)); + frame = (StackFrame *)sp_reg; +# elif defined(AARCH64) + volatile ptr_t sp_reg; + __asm__ __volatile__ ("mov %0, x29\n" : "=r" (sp_reg)); + frame = (StackFrame *)sp_reg; +# else +# if defined(CPPCHECK) + GC_noop1((word)&frame); +# endif + ABORT("GC_FindTopOfStack(0) is not implemented"); +# endif + } + +# ifdef DEBUG_THREADS_EXTRA + GC_log_printf("FindTopOfStack start at sp= %p\n", (void *)frame); +# endif + while (frame->savedSP != 0) { /* stop if no more stack frames */ + unsigned long maskedLR; + + frame = (StackFrame*)frame->savedSP; + + /* we do these next two checks after going to the next frame + because the LR for the first stack frame in the loop + is not set up on purpose, so we shouldn't check it. */ + maskedLR = frame -> savedLR & ~0x3UL; + if (0 == maskedLR || ~0x3UL == maskedLR) + break; /* if the next LR is bogus, stop */ + } +# ifdef DEBUG_THREADS_EXTRA + GC_log_printf("FindTopOfStack finish at sp= %p\n", (void *)frame); +# endif + return (ptr_t)frame; +} + +#endif /* !DARWIN_DONT_PARSE_STACK */ + +/* GC_query_task_threads controls whether to obtain the list of */ +/* the threads from the kernel or to use GC_threads table. */ +#ifdef GC_NO_THREADS_DISCOVERY +# define GC_query_task_threads FALSE +#elif defined(GC_DISCOVER_TASK_THREADS) +# define GC_query_task_threads TRUE +#else + STATIC GC_bool GC_query_task_threads = FALSE; +#endif /* !GC_NO_THREADS_DISCOVERY */ + +/* Use implicit threads registration (all task threads excluding the GC */ +/* special ones are stopped and scanned). Should be called before */ +/* GC_INIT() (or, at least, before going multi-threaded). Deprecated. */ +GC_API void GC_CALL GC_use_threads_discovery(void) +{ +# ifdef GC_NO_THREADS_DISCOVERY + ABORT("Darwin task-threads-based stop and push unsupported"); +# else +# ifndef GC_ALWAYS_MULTITHREADED + GC_ASSERT(!GC_need_to_lock); +# endif +# ifndef GC_DISCOVER_TASK_THREADS + GC_query_task_threads = TRUE; +# endif + GC_init(); +# endif +} + +#ifndef kCFCoreFoundationVersionNumber_iOS_8_0 +# define kCFCoreFoundationVersionNumber_iOS_8_0 1140.1 +#endif + +/* Evaluates the stack range for a given thread. Returns the lower */ +/* bound and sets *phi to the upper one. Sets *pfound_me to TRUE if */ +/* this is current thread, otherwise the value is not changed. */ +STATIC ptr_t GC_stack_range_for(ptr_t *phi, thread_act_t thread, GC_thread p, + mach_port_t my_thread, ptr_t *paltstack_lo, + ptr_t *paltstack_hi, GC_bool *pfound_me) +{ +# ifdef DARWIN_DONT_PARSE_STACK + GC_stack_context_t crtn; +# endif + ptr_t lo; + + GC_ASSERT(I_HOLD_LOCK()); + if (thread == my_thread) { + GC_ASSERT(NULL == p || (p -> flags & DO_BLOCKING) == 0); + lo = GC_approx_sp(); +# ifndef DARWIN_DONT_PARSE_STACK + *phi = GC_FindTopOfStack(0); +# endif + *pfound_me = TRUE; + } else if (p != NULL && (p -> flags & DO_BLOCKING) != 0) { + lo = p -> crtn -> stack_ptr; +# ifndef DARWIN_DONT_PARSE_STACK + *phi = p -> crtn -> topOfStack; +# endif + + } else { + /* MACHINE_THREAD_STATE_COUNT does not seem to be defined */ + /* everywhere. Hence we use our own version. Alternatively, */ + /* we could use THREAD_STATE_MAX (but seems to be not optimal). */ + kern_return_t kern_result; + GC_THREAD_STATE_T state; + +# if defined(ARM32) && defined(ARM_THREAD_STATE32) + /* Use ARM_UNIFIED_THREAD_STATE on iOS8+ 32-bit targets and on */ + /* 64-bit H/W (iOS7+ 32-bit mode). */ + size_t size; + static cpu_type_t cputype = 0; + + if (cputype == 0) { + sysctlbyname("hw.cputype", &cputype, &size, NULL, 0); + } + if (cputype == CPU_TYPE_ARM64 + || kCFCoreFoundationVersionNumber + >= kCFCoreFoundationVersionNumber_iOS_8_0) { + arm_unified_thread_state_t unified_state; + mach_msg_type_number_t unified_thread_state_count + = ARM_UNIFIED_THREAD_STATE_COUNT; +# if defined(CPPCHECK) +# define GC_ARM_UNIFIED_THREAD_STATE 1 +# else +# define GC_ARM_UNIFIED_THREAD_STATE ARM_UNIFIED_THREAD_STATE +# endif + kern_result = thread_get_state(thread, GC_ARM_UNIFIED_THREAD_STATE, + (natural_t *)&unified_state, + &unified_thread_state_count); +# if !defined(CPPCHECK) + if (unified_state.ash.flavor != ARM_THREAD_STATE32) { + ABORT("unified_state flavor should be ARM_THREAD_STATE32"); + } +# endif + state = unified_state; + } else +# endif + /* else */ { + mach_msg_type_number_t thread_state_count = GC_MACH_THREAD_STATE_COUNT; + + /* Get the thread state (registers, etc.) */ + do { + kern_result = thread_get_state(thread, GC_MACH_THREAD_STATE, + (natural_t *)&state, + &thread_state_count); + } while (kern_result == KERN_ABORTED); + } +# ifdef DEBUG_THREADS + GC_log_printf("thread_get_state returns %d\n", kern_result); +# endif + if (kern_result != KERN_SUCCESS) + ABORT("thread_get_state failed"); + +# if defined(I386) + lo = (ptr_t)state.THREAD_FLD(esp); +# ifndef DARWIN_DONT_PARSE_STACK + *phi = GC_FindTopOfStack(state.THREAD_FLD(esp)); +# endif + GC_push_one(state.THREAD_FLD(eax)); + GC_push_one(state.THREAD_FLD(ebx)); + GC_push_one(state.THREAD_FLD(ecx)); + GC_push_one(state.THREAD_FLD(edx)); + GC_push_one(state.THREAD_FLD(edi)); + GC_push_one(state.THREAD_FLD(esi)); + GC_push_one(state.THREAD_FLD(ebp)); + +# elif defined(X86_64) + lo = (ptr_t)state.THREAD_FLD(rsp); +# ifndef DARWIN_DONT_PARSE_STACK + *phi = GC_FindTopOfStack(state.THREAD_FLD(rsp)); +# endif + GC_push_one(state.THREAD_FLD(rax)); + GC_push_one(state.THREAD_FLD(rbx)); + GC_push_one(state.THREAD_FLD(rcx)); + GC_push_one(state.THREAD_FLD(rdx)); + GC_push_one(state.THREAD_FLD(rdi)); + GC_push_one(state.THREAD_FLD(rsi)); + GC_push_one(state.THREAD_FLD(rbp)); + /* rsp is skipped. */ + GC_push_one(state.THREAD_FLD(r8)); + GC_push_one(state.THREAD_FLD(r9)); + GC_push_one(state.THREAD_FLD(r10)); + GC_push_one(state.THREAD_FLD(r11)); + GC_push_one(state.THREAD_FLD(r12)); + GC_push_one(state.THREAD_FLD(r13)); + GC_push_one(state.THREAD_FLD(r14)); + GC_push_one(state.THREAD_FLD(r15)); + +# elif defined(POWERPC) + lo = (ptr_t)(state.THREAD_FLD(r1) - PPC_RED_ZONE_SIZE); +# ifndef DARWIN_DONT_PARSE_STACK + *phi = GC_FindTopOfStack(state.THREAD_FLD(r1)); +# endif + GC_push_one(state.THREAD_FLD(r0)); + /* r1 is skipped. */ + GC_push_one(state.THREAD_FLD(r2)); + GC_push_one(state.THREAD_FLD(r3)); + GC_push_one(state.THREAD_FLD(r4)); + GC_push_one(state.THREAD_FLD(r5)); + GC_push_one(state.THREAD_FLD(r6)); + GC_push_one(state.THREAD_FLD(r7)); + GC_push_one(state.THREAD_FLD(r8)); + GC_push_one(state.THREAD_FLD(r9)); + GC_push_one(state.THREAD_FLD(r10)); + GC_push_one(state.THREAD_FLD(r11)); + GC_push_one(state.THREAD_FLD(r12)); + GC_push_one(state.THREAD_FLD(r13)); + GC_push_one(state.THREAD_FLD(r14)); + GC_push_one(state.THREAD_FLD(r15)); + GC_push_one(state.THREAD_FLD(r16)); + GC_push_one(state.THREAD_FLD(r17)); + GC_push_one(state.THREAD_FLD(r18)); + GC_push_one(state.THREAD_FLD(r19)); + GC_push_one(state.THREAD_FLD(r20)); + GC_push_one(state.THREAD_FLD(r21)); + GC_push_one(state.THREAD_FLD(r22)); + GC_push_one(state.THREAD_FLD(r23)); + GC_push_one(state.THREAD_FLD(r24)); + GC_push_one(state.THREAD_FLD(r25)); + GC_push_one(state.THREAD_FLD(r26)); + GC_push_one(state.THREAD_FLD(r27)); + GC_push_one(state.THREAD_FLD(r28)); + GC_push_one(state.THREAD_FLD(r29)); + GC_push_one(state.THREAD_FLD(r30)); + GC_push_one(state.THREAD_FLD(r31)); + +# elif defined(ARM32) + lo = (ptr_t)state.THREAD_FLD(sp); +# ifndef DARWIN_DONT_PARSE_STACK + *phi = GC_FindTopOfStack(state.THREAD_FLD(r[7])); /* fp */ +# endif + { + int j; + for (j = 0; j < 7; j++) + GC_push_one(state.THREAD_FLD(r[j])); + j++; /* "r7" is skipped (iOS uses it as a frame pointer) */ + for (; j <= 12; j++) + GC_push_one(state.THREAD_FLD(r[j])); + } + /* "cpsr", "pc" and "sp" are skipped */ + GC_push_one(state.THREAD_FLD(lr)); + +# elif defined(AARCH64) + lo = (ptr_t)state.THREAD_FLD(sp); +# ifndef DARWIN_DONT_PARSE_STACK + *phi = GC_FindTopOfStack(state.THREAD_FLD(fp)); +# endif + { + int j; + for (j = 0; j <= 28; j++) { + GC_push_one(state.THREAD_FLD(x[j])); + } + } + /* "cpsr", "fp", "pc" and "sp" are skipped */ + GC_push_one(state.THREAD_FLD(lr)); + +# elif defined(CPPCHECK) + lo = NULL; +# else +# error FIXME for non-arm/ppc/x86 architectures +# endif + } /* thread != my_thread */ + +# ifndef DARWIN_DONT_PARSE_STACK + /* TODO: Determine p and handle altstack if !DARWIN_DONT_PARSE_STACK */ + UNUSED_ARG(paltstack_hi); +# else + /* p is guaranteed to be non-NULL regardless of GC_query_task_threads. */ +# ifdef CPPCHECK + if (NULL == p) ABORT("Bad GC_stack_range_for call"); +# endif + crtn = p -> crtn; + *phi = crtn -> stack_end; + if (crtn -> altstack != NULL && (word)(crtn -> altstack) <= (word)lo + && (word)lo <= (word)(crtn -> altstack) + crtn -> altstack_size) { + *paltstack_lo = lo; + *paltstack_hi = crtn -> altstack + crtn -> altstack_size; + lo = crtn -> normstack; + *phi = lo + crtn -> normstack_size; + } else +# endif + /* else */ { + *paltstack_lo = NULL; + } +# if defined(STACKPTR_CORRECTOR_AVAILABLE) && defined(DARWIN_DONT_PARSE_STACK) + if (GC_sp_corrector != 0) + GC_sp_corrector((void **)&lo, (void *)(p -> id)); +# endif +# ifdef DEBUG_THREADS + GC_log_printf("Darwin: Stack for thread %p is [%p,%p)\n", + (void *)(word)thread, (void *)lo, (void *)(*phi)); +# endif + return lo; +} + +GC_INNER void GC_push_all_stacks(void) +{ + ptr_t hi, altstack_lo, altstack_hi; + task_t my_task = current_task(); + mach_port_t my_thread = mach_thread_self(); + GC_bool found_me = FALSE; + int nthreads = 0; + word total_size = 0; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_thr_initialized); + +# ifndef DARWIN_DONT_PARSE_STACK + if (GC_query_task_threads) { + int i; + kern_return_t kern_result; + thread_act_array_t act_list; + mach_msg_type_number_t listcount; + + /* Obtain the list of the threads from the kernel. */ + kern_result = task_threads(my_task, &act_list, &listcount); + if (kern_result != KERN_SUCCESS) + ABORT("task_threads failed"); + + for (i = 0; i < (int)listcount; i++) { + thread_act_t thread = act_list[i]; + ptr_t lo = GC_stack_range_for(&hi, thread, NULL, my_thread, + &altstack_lo, &altstack_hi, &found_me); + + if (lo) { + GC_ASSERT((word)lo <= (word)hi); + total_size += hi - lo; + GC_push_all_stack(lo, hi); + } + /* TODO: Handle altstack */ + nthreads++; + mach_port_deallocate(my_task, thread); + } /* for (i=0; ...) */ + + vm_deallocate(my_task, (vm_address_t)act_list, + sizeof(thread_t) * listcount); + } else +# endif /* !DARWIN_DONT_PARSE_STACK */ + /* else */ { + int i; + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + GC_thread p; + + for (p = GC_threads[i]; p != NULL; p = p -> tm.next) { + GC_ASSERT(THREAD_TABLE_INDEX(p -> id) == i); + if (!KNOWN_FINISHED(p)) { + thread_act_t thread = (thread_act_t)(p -> mach_thread); + ptr_t lo = GC_stack_range_for(&hi, thread, p, my_thread, + &altstack_lo, &altstack_hi, &found_me); + + if (lo) { + GC_ASSERT((word)lo <= (word)hi); + total_size += hi - lo; + GC_push_all_stack_sections(lo, hi, p -> crtn -> traced_stack_sect); + } + if (altstack_lo) { + total_size += altstack_hi - altstack_lo; + GC_push_all_stack(altstack_lo, altstack_hi); + } + nthreads++; + } + } + } /* for (i=0; ...) */ + } + + mach_port_deallocate(my_task, my_thread); + GC_VERBOSE_LOG_PRINTF("Pushed %d thread stacks\n", nthreads); + if (!found_me && !GC_in_thread_creation) + ABORT("Collecting from unknown thread"); + GC_total_stacksize = total_size; +} + +#ifndef GC_NO_THREADS_DISCOVERY + +# ifdef MPROTECT_VDB + STATIC mach_port_t GC_mach_handler_thread = 0; + STATIC GC_bool GC_use_mach_handler_thread = FALSE; + + GC_INNER void GC_darwin_register_self_mach_handler(void) + { + GC_mach_handler_thread = mach_thread_self(); + GC_use_mach_handler_thread = TRUE; + } +# endif /* MPROTECT_VDB */ + +# ifndef GC_MAX_MACH_THREADS +# define GC_MAX_MACH_THREADS THREAD_TABLE_SZ +# endif + + struct GC_mach_thread { + thread_act_t thread; + GC_bool suspended; + }; + + struct GC_mach_thread GC_mach_threads[GC_MAX_MACH_THREADS]; + STATIC int GC_mach_threads_count = 0; + /* FIXME: it is better to implement GC_mach_threads as a hash set. */ + +/* returns true if there's a thread in act_list that wasn't in old_list */ +STATIC GC_bool GC_suspend_thread_list(thread_act_array_t act_list, int count, + thread_act_array_t old_list, + int old_count, task_t my_task, + mach_port_t my_thread) +{ + int i; + int j = -1; + GC_bool changed = FALSE; + + GC_ASSERT(I_HOLD_LOCK()); + for (i = 0; i < count; i++) { + thread_act_t thread = act_list[i]; + GC_bool found; + kern_return_t kern_result; + + if (thread == my_thread +# ifdef MPROTECT_VDB + || (GC_mach_handler_thread == thread && GC_use_mach_handler_thread) +# endif +# ifdef PARALLEL_MARK + || GC_is_mach_marker(thread) /* ignore the parallel markers */ +# endif + ) { + /* Do not add our one, parallel marker and the handler threads; */ + /* consider it as found (e.g., it was processed earlier). */ + mach_port_deallocate(my_task, thread); + continue; + } + + /* find the current thread in the old list */ + found = FALSE; + { + int last_found = j; /* remember the previous found thread index */ + + /* Search for the thread starting from the last found one first. */ + while (++j < old_count) + if (old_list[j] == thread) { + found = TRUE; + break; + } + if (!found) { + /* If not found, search in the rest (beginning) of the list. */ + for (j = 0; j < last_found; j++) + if (old_list[j] == thread) { + found = TRUE; + break; + } + } + } + + if (found) { + /* It is already in the list, skip processing, release mach port. */ + mach_port_deallocate(my_task, thread); + continue; + } + + /* add it to the GC_mach_threads list */ + if (GC_mach_threads_count == GC_MAX_MACH_THREADS) + ABORT("Too many threads"); + GC_mach_threads[GC_mach_threads_count].thread = thread; + /* default is not suspended */ + GC_mach_threads[GC_mach_threads_count].suspended = FALSE; + changed = TRUE; + +# ifdef DEBUG_THREADS + GC_log_printf("Suspending %p\n", (void *)(word)thread); +# endif + /* Unconditionally suspend the thread. It will do no */ + /* harm if it is already suspended by the client logic. */ + GC_acquire_dirty_lock(); + do { + kern_result = thread_suspend(thread); + } while (kern_result == KERN_ABORTED); + GC_release_dirty_lock(); + if (kern_result != KERN_SUCCESS) { + /* The thread may have quit since the thread_threads() call we */ + /* mark already suspended so it's not dealt with anymore later. */ + GC_mach_threads[GC_mach_threads_count].suspended = FALSE; + } else { + /* Mark the thread as suspended and require resume. */ + GC_mach_threads[GC_mach_threads_count].suspended = TRUE; + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, (void *)(word)thread); + } + GC_mach_threads_count++; + } + return changed; +} + +#endif /* !GC_NO_THREADS_DISCOVERY */ + +GC_INNER void GC_stop_world(void) +{ + task_t my_task = current_task(); + mach_port_t my_thread = mach_thread_self(); + kern_return_t kern_result; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_thr_initialized); +# ifdef DEBUG_THREADS + GC_log_printf("Stopping the world from thread %p\n", + (void *)(word)my_thread); +# endif +# ifdef PARALLEL_MARK + if (GC_parallel) { + GC_acquire_mark_lock(); + GC_ASSERT(GC_fl_builder_count == 0); + /* We should have previously waited for it to become zero. */ + } +# endif /* PARALLEL_MARK */ + + if (GC_query_task_threads) { +# ifndef GC_NO_THREADS_DISCOVERY + GC_bool changed; + thread_act_array_t act_list, prev_list; + mach_msg_type_number_t listcount, prevcount; + + /* Clear out the mach threads list table. We do not need to */ + /* really clear GC_mach_threads[] as it is used only in the range */ + /* from 0 to GC_mach_threads_count-1, inclusive. */ + GC_mach_threads_count = 0; + + /* Loop stopping threads until you have gone over the whole list */ + /* twice without a new one appearing. thread_create() won't */ + /* return (and thus the thread stop) until the new thread exists, */ + /* so there is no window whereby you could stop a thread, */ + /* recognize it is stopped, but then have a new thread it created */ + /* before stopping show up later. */ + changed = TRUE; + prev_list = NULL; + prevcount = 0; + do { + kern_result = task_threads(my_task, &act_list, &listcount); + + if (kern_result == KERN_SUCCESS) { + changed = GC_suspend_thread_list(act_list, listcount, prev_list, + prevcount, my_task, my_thread); + + if (prev_list != NULL) { + /* Thread ports are not deallocated by list, unused ports */ + /* deallocated in GC_suspend_thread_list, used - kept in */ + /* GC_mach_threads till GC_start_world as otherwise thread */ + /* object change can occur and GC_start_world will not */ + /* find the thread to resume which will cause app to hang. */ + vm_deallocate(my_task, (vm_address_t)prev_list, + sizeof(thread_t) * prevcount); + } + + /* Repeat while having changes. */ + prev_list = act_list; + prevcount = listcount; + } + } while (changed); + + GC_ASSERT(prev_list != 0); + /* The thread ports are not deallocated by list, see above. */ + vm_deallocate(my_task, (vm_address_t)act_list, + sizeof(thread_t) * listcount); +# endif /* !GC_NO_THREADS_DISCOVERY */ + + } else { + unsigned i; + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + GC_thread p; + + for (p = GC_threads[i]; p != NULL; p = p -> tm.next) { + if ((p -> flags & (FINISHED | DO_BLOCKING)) == 0 + && p -> mach_thread != my_thread) { + GC_acquire_dirty_lock(); + do { + kern_result = thread_suspend(p -> mach_thread); + } while (kern_result == KERN_ABORTED); + GC_release_dirty_lock(); + if (kern_result != KERN_SUCCESS) + ABORT("thread_suspend failed"); + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, + (void *)(word)(p -> mach_thread)); + } + } + } + } + +# ifdef MPROTECT_VDB + if (GC_auto_incremental) { + GC_mprotect_stop(); + } +# endif +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_release_mark_lock(); +# endif + +# ifdef DEBUG_THREADS + GC_log_printf("World stopped from %p\n", (void *)(word)my_thread); +# endif + mach_port_deallocate(my_task, my_thread); +} + +GC_INLINE void GC_thread_resume(thread_act_t thread) +{ + kern_return_t kern_result; +# if defined(DEBUG_THREADS) || defined(GC_ASSERTIONS) + struct thread_basic_info info; + mach_msg_type_number_t outCount = THREAD_BASIC_INFO_COUNT; + +# ifdef CPPCHECK + info.run_state = 0; +# endif + kern_result = thread_info(thread, THREAD_BASIC_INFO, + (thread_info_t)&info, &outCount); + if (kern_result != KERN_SUCCESS) + ABORT("thread_info failed"); +# endif + GC_ASSERT(I_HOLD_LOCK()); +# ifdef DEBUG_THREADS + GC_log_printf("Resuming thread %p with state %d\n", (void *)(word)thread, + info.run_state); +# endif + /* Resume the thread */ + kern_result = thread_resume(thread); + if (kern_result != KERN_SUCCESS) { + WARN("thread_resume(%p) failed: mach port invalid\n", thread); + } else if (GC_on_thread_event) { + GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, (void *)(word)thread); + } +} + +GC_INNER void GC_start_world(void) +{ + task_t my_task = current_task(); + + GC_ASSERT(I_HOLD_LOCK()); /* held continuously since the world stopped */ +# ifdef DEBUG_THREADS + GC_log_printf("World starting\n"); +# endif +# ifdef MPROTECT_VDB + if (GC_auto_incremental) { + GC_mprotect_resume(); + } +# endif + + if (GC_query_task_threads) { +# ifndef GC_NO_THREADS_DISCOVERY + int i, j; + kern_return_t kern_result; + thread_act_array_t act_list; + mach_msg_type_number_t listcount; + + kern_result = task_threads(my_task, &act_list, &listcount); + if (kern_result != KERN_SUCCESS) + ABORT("task_threads failed"); + + j = (int)listcount; + for (i = 0; i < GC_mach_threads_count; i++) { + thread_act_t thread = GC_mach_threads[i].thread; + + if (GC_mach_threads[i].suspended) { + int last_found = j; /* The thread index found during the */ + /* previous iteration (count value */ + /* means no thread found yet). */ + + /* Search for the thread starting from the last found one first. */ + while (++j < (int)listcount) { + if (act_list[j] == thread) + break; + } + if (j >= (int)listcount) { + /* If not found, search in the rest (beginning) of the list. */ + for (j = 0; j < last_found; j++) { + if (act_list[j] == thread) + break; + } + } + if (j != last_found) { + /* The thread is alive, resume it. */ + GC_thread_resume(thread); + } + } else { + /* This thread was failed to be suspended by GC_stop_world, */ + /* no action needed. */ +# ifdef DEBUG_THREADS + GC_log_printf("Not resuming thread %p as it is not suspended\n", + (void *)(word)thread); +# endif + } + mach_port_deallocate(my_task, thread); + } + + for (i = 0; i < (int)listcount; i++) + mach_port_deallocate(my_task, act_list[i]); + vm_deallocate(my_task, (vm_address_t)act_list, + sizeof(thread_t) * listcount); +# endif /* !GC_NO_THREADS_DISCOVERY */ + + } else { + int i; + mach_port_t my_thread = mach_thread_self(); + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + GC_thread p; + + for (p = GC_threads[i]; p != NULL; p = p -> tm.next) { + if ((p -> flags & (FINISHED | DO_BLOCKING)) == 0 + && p -> mach_thread != my_thread) + GC_thread_resume(p -> mach_thread); + } + } + + mach_port_deallocate(my_task, my_thread); + } + +# ifdef DEBUG_THREADS + GC_log_printf("World started\n"); +# endif +} + +#endif /* GC_DARWIN_THREADS */ + +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1997 by Silicon Graphics. All rights reserved. + * Copyright (c) 2009-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +/* + * This is incredibly OS specific code for tracking down data sections in + * dynamic libraries. There appears to be no way of doing this quickly + * without groveling through undocumented data structures. We would argue + * that this is a bug in the design of the dlopen interface. THIS CODE + * MAY BREAK IN FUTURE OS RELEASES. If this matters to you, don't hesitate + * to let your vendor know ... + * + * None of this is safe with dlclose and incremental collection. + * But then not much of anything is safe in the presence of dlclose. + */ + +/* BTL: avoid circular redefinition of dlopen if GC_SOLARIS_THREADS defined */ +#undef GC_MUST_RESTORE_REDEFINED_DLOPEN +#if defined(GC_PTHREADS) && !defined(GC_NO_DLOPEN) \ + && !defined(GC_NO_THREAD_REDIRECTS) && !defined(GC_USE_LD_WRAP) + /* To support threads in Solaris, gc.h interposes on dlopen by */ + /* defining "dlopen" to be "GC_dlopen", which is implemented below. */ + /* However, both GC_FirstDLOpenedLinkMap() and GC_dlopen() use the */ + /* real system dlopen() in their implementation. We first remove */ + /* gc.h's dlopen definition and restore it later, after GC_dlopen(). */ +# undef dlopen +# define GC_MUST_RESTORE_REDEFINED_DLOPEN +#endif /* !GC_NO_DLOPEN */ + +#if defined(SOLARISDL) && defined(THREADS) && !defined(PCR) \ + && !defined(GC_SOLARIS_THREADS) && !defined(CPPCHECK) +# error Fix mutual exclusion with dlopen +#endif + +/* A user-supplied routine (custom filter) that might be called to */ +/* determine whether a DSO really needs to be scanned by the GC. */ +/* 0 means no filter installed. May be unused on some platforms. */ +/* FIXME: Add filter support for more platforms. */ +STATIC GC_has_static_roots_func GC_has_static_roots = 0; + +#if defined(DYNAMIC_LOADING) && !defined(PCR) || defined(ANY_MSWIN) + +#if !(defined(CPPCHECK) || defined(AIX) || defined(ANY_MSWIN) \ + || defined(DARWIN) || defined(DGUX) || defined(IRIX5) \ + || defined(HAIKU) || defined(HPUX) || defined(HURD) \ + || defined(NACL) || defined(SCO_ELF) || defined(SOLARISDL) \ + || ((defined(ANY_BSD) || defined(LINUX)) && defined(__ELF__)) \ + || (defined(ALPHA) && defined(OSF1)) \ + || (defined(OPENBSD) && defined(M68K))) +# error We only know how to find data segments of dynamic libraries for above. +# error Additional SVR4 variants might not be too hard to add. +#endif + +#if defined(DARWIN) && !defined(USE_DYLD_TO_BIND) \ + && !defined(NO_DYLD_BIND_FULLY_IMAGE) +# include +#endif + +#ifdef SOLARISDL +# include +# include +# include +#endif + +#if defined(NETBSD) +# include +# include +# include +# define ELFSIZE ARCH_ELFSIZE +#endif + +#if defined(OPENBSD) +# include +# if (OpenBSD >= 200519) && !defined(HAVE_DL_ITERATE_PHDR) +# define HAVE_DL_ITERATE_PHDR +# endif +#endif /* OPENBSD */ + +#if defined(DGUX) || defined(HURD) || defined(NACL) || defined(SCO_ELF) \ + || ((defined(ANY_BSD) || defined(LINUX)) && defined(__ELF__)) +# include +# if !defined(OPENBSD) && !defined(HOST_ANDROID) + /* OpenBSD does not have elf.h file; link.h below is sufficient. */ + /* Exclude Android because linker.h below includes its own version. */ +# include +# endif +# ifdef HOST_ANDROID + /* If you don't need the "dynamic loading" feature, you may build */ + /* the collector with -D IGNORE_DYNAMIC_LOADING. */ +# ifdef BIONIC_ELFDATA_REDEF_BUG + /* Workaround a problem in Bionic (as of Android 4.2) which has */ + /* mismatching ELF_DATA definitions in sys/exec_elf.h and */ + /* asm/elf.h included from linker.h file (similar to EM_ALPHA). */ +# include +# include +# undef ELF_DATA +# undef EM_ALPHA +# endif +# include +# if !defined(GC_DONT_DEFINE_LINK_MAP) && !(__ANDROID_API__ >= 21) + /* link_map and r_debug are defined in link.h of NDK r10+. */ + /* bionic/linker/linker.h defines them too but the header */ + /* itself is a C++ one starting from Android 4.3. */ + struct link_map { + uintptr_t l_addr; + char* l_name; + uintptr_t l_ld; + struct link_map* l_next; + struct link_map* l_prev; + }; + struct r_debug { + int32_t r_version; + struct link_map* r_map; + void (*r_brk)(void); + int32_t r_state; + uintptr_t r_ldbase; + }; +# endif +# else + EXTERN_C_BEGIN /* Workaround missing extern "C" around _DYNAMIC */ + /* symbol in link.h of some Linux hosts. */ +# include + EXTERN_C_END +# endif +#endif + +/* Newer versions of GNU/Linux define this macro. We + * define it similarly for any ELF systems that don't. */ +# ifndef ElfW +# if defined(FREEBSD) +# if __ELF_WORD_SIZE == 32 +# define ElfW(type) Elf32_##type +# else +# define ElfW(type) Elf64_##type +# endif +# elif defined(NETBSD) || defined(OPENBSD) +# if ELFSIZE == 32 +# define ElfW(type) Elf32_##type +# elif ELFSIZE == 64 +# define ElfW(type) Elf64_##type +# else +# error Missing ELFSIZE define +# endif +# else +# if !defined(ELF_CLASS) || ELF_CLASS == ELFCLASS32 +# define ElfW(type) Elf32_##type +# else +# define ElfW(type) Elf64_##type +# endif +# endif +# endif + +#if defined(SOLARISDL) && !defined(USE_PROC_FOR_LIBRARIES) + + EXTERN_C_BEGIN + extern ElfW(Dyn) _DYNAMIC; + EXTERN_C_END + + STATIC struct link_map * + GC_FirstDLOpenedLinkMap(void) + { + ElfW(Dyn) *dp; + static struct link_map * cachedResult = 0; + static ElfW(Dyn) *dynStructureAddr = 0; + /* BTL: added to avoid Solaris 5.3 ld.so _DYNAMIC bug */ + +# ifdef SUNOS53_SHARED_LIB + /* BTL: Avoid the Solaris 5.3 bug that _DYNAMIC isn't being set */ + /* up properly in dynamically linked .so's. This means we have */ + /* to use its value in the set of original object files loaded */ + /* at program startup. */ + if( dynStructureAddr == 0 ) { + void* startupSyms = dlopen(0, RTLD_LAZY); + dynStructureAddr = (ElfW(Dyn)*)(word)dlsym(startupSyms, "_DYNAMIC"); + } +# else + dynStructureAddr = &_DYNAMIC; +# endif + + if (0 == COVERT_DATAFLOW(dynStructureAddr)) { + /* _DYNAMIC symbol not resolved. */ + return NULL; + } + if (cachedResult == 0) { + int tag; + for( dp = ((ElfW(Dyn) *)(&_DYNAMIC)); (tag = dp->d_tag) != 0; dp++ ) { + if (tag == DT_DEBUG) { + struct r_debug *rd = (struct r_debug *)dp->d_un.d_ptr; + if (rd != NULL) { + struct link_map *lm = rd->r_map; + if (lm != NULL) + cachedResult = lm->l_next; /* might be NULL */ + } + break; + } + } + } + return cachedResult; + } + + GC_INNER void GC_register_dynamic_libraries(void) + { + struct link_map *lm; + + GC_ASSERT(I_HOLD_LOCK()); + for (lm = GC_FirstDLOpenedLinkMap(); lm != 0; lm = lm->l_next) { + ElfW(Ehdr) * e; + ElfW(Phdr) * p; + unsigned long offset; + char * start; + int i; + + e = (ElfW(Ehdr) *) lm->l_addr; + p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff)); + offset = ((unsigned long)(lm->l_addr)); + for( i = 0; i < (int)e->e_phnum; i++, p++ ) { + switch( p->p_type ) { + case PT_LOAD: + { + if( !(p->p_flags & PF_W) ) break; + start = ((char *)(p->p_vaddr)) + offset; + GC_add_roots_inner(start, start + p->p_memsz, TRUE); + } + break; + default: + break; + } + } + } + } + +#endif /* SOLARISDL && !USE_PROC_FOR_LIBRARIES */ + +#if defined(DGUX) || defined(HURD) || defined(NACL) || defined(SCO_ELF) \ + || ((defined(ANY_BSD) || defined(LINUX)) && defined(__ELF__)) + +#ifdef USE_PROC_FOR_LIBRARIES + +#include + +#include +#include + +#define MAPS_BUF_SIZE (32*1024) + +/* Sort an array of HeapSects by start address. */ +/* Unfortunately at least some versions of */ +/* Linux qsort end up calling malloc by way of sysconf, and hence can't */ +/* be used in the collector. Hence we roll our own. Should be */ +/* reasonably fast if the array is already mostly sorted, as we expect */ +/* it to be. */ +static void sort_heap_sects(struct HeapSect *base, size_t number_of_elements) +{ + signed_word n = (signed_word)number_of_elements; + signed_word nsorted = 1; + + while (nsorted < n) { + signed_word i; + + while (nsorted < n && + (word)base[nsorted-1].hs_start < (word)base[nsorted].hs_start) + ++nsorted; + if (nsorted == n) break; + GC_ASSERT((word)base[nsorted-1].hs_start > (word)base[nsorted].hs_start); + i = nsorted - 1; + while (i >= 0 && (word)base[i].hs_start > (word)base[i+1].hs_start) { + struct HeapSect tmp = base[i]; + base[i] = base[i+1]; + base[i+1] = tmp; + --i; + } + GC_ASSERT((word)base[nsorted-1].hs_start < (word)base[nsorted].hs_start); + ++nsorted; + } +} + +STATIC void GC_register_map_entries(const char *maps) +{ + const char *prot, *path; + ptr_t start, end; + unsigned int maj_dev; + ptr_t least_ha, greatest_ha; + unsigned i; + + GC_ASSERT(I_HOLD_LOCK()); + sort_heap_sects(GC_our_memory, GC_n_memory); + least_ha = GC_our_memory[0].hs_start; + greatest_ha = GC_our_memory[GC_n_memory-1].hs_start + + GC_our_memory[GC_n_memory-1].hs_bytes; + + for (;;) { + maps = GC_parse_map_entry(maps, &start, &end, &prot, &maj_dev, &path); + if (NULL == maps) break; + + if (prot[1] == 'w') { + /* This is a writable mapping. Add it to */ + /* the root set unless it is already otherwise */ + /* accounted for. */ +# ifndef THREADS + if ((word)start <= (word)GC_stackbottom + && (word)end >= (word)GC_stackbottom) { + /* Stack mapping; discard */ + continue; + } +# endif +# if defined(E2K) && defined(__ptr64__) + /* TODO: avoid hard-coded addresses */ + if ((word)start == 0xc2fffffff000UL + && (word)end == 0xc30000000000UL && path[0] == '\n') + continue; /* discard some special mapping */ +# endif + if (path[0] == '[' && strncmp(path+1, "heap]", 5) != 0) + continue; /* discard if a pseudo-path unless "[heap]" */ + +# ifdef THREADS + /* This may fail, since a thread may already be */ + /* unregistered, but its thread stack may still be there. */ + /* That can fail because the stack may disappear while */ + /* we're marking. Thus the marker is, and has to be */ + /* prepared to recover from segmentation faults. */ + + if (GC_segment_is_thread_stack(start, end)) continue; + + /* FIXME: NPTL squirrels */ + /* away pointers in pieces of the stack segment that we */ + /* don't scan. We work around this */ + /* by treating anything allocated by libpthread as */ + /* uncollectible, as we do in some other cases. */ + /* A specifically identified problem is that */ + /* thread stacks contain pointers to dynamic thread */ + /* vectors, which may be reused due to thread caching. */ + /* They may not be marked if the thread is still live. */ + /* This specific instance should be addressed by */ + /* INCLUDE_LINUX_THREAD_DESCR, but that doesn't quite */ + /* seem to suffice. */ + /* We currently trace entire thread stacks, if they are */ + /* are currently cached but unused. This is */ + /* very suboptimal for performance reasons. */ +# endif + /* We no longer exclude the main data segment. */ + if ((word)end <= (word)least_ha + || (word)start >= (word)greatest_ha) { + /* The easy case; just trace entire segment */ + GC_add_roots_inner(start, end, TRUE); + continue; + } + /* Add sections that don't belong to us. */ + i = 0; + while ((word)(GC_our_memory[i].hs_start + + GC_our_memory[i].hs_bytes) < (word)start) + ++i; + GC_ASSERT(i < GC_n_memory); + if ((word)GC_our_memory[i].hs_start <= (word)start) { + start = GC_our_memory[i].hs_start + + GC_our_memory[i].hs_bytes; + ++i; + } + while (i < GC_n_memory + && (word)GC_our_memory[i].hs_start < (word)end + && (word)start < (word)end) { + if ((word)start < (word)GC_our_memory[i].hs_start) + GC_add_roots_inner(start, + GC_our_memory[i].hs_start, TRUE); + start = GC_our_memory[i].hs_start + + GC_our_memory[i].hs_bytes; + ++i; + } + if ((word)start < (word)end) + GC_add_roots_inner(start, end, TRUE); + } else if (prot[0] == '-' && prot[1] == '-' && prot[2] == '-') { + /* Even roots added statically might disappear partially */ + /* (e.g. the roots added by INCLUDE_LINUX_THREAD_DESCR). */ + GC_remove_roots_subregion(start, end); + } + } +} + +GC_INNER void GC_register_dynamic_libraries(void) +{ + GC_register_map_entries(GC_get_maps()); +} + +/* We now take care of the main data segment ourselves: */ +GC_INNER GC_bool GC_register_main_static_data(void) +{ + return FALSE; +} + +# define HAVE_REGISTER_MAIN_STATIC_DATA + +#else /* !USE_PROC_FOR_LIBRARIES */ + +/* The following is the preferred way to walk dynamic libraries */ +/* for glibc 2.2.4+. Unfortunately, it doesn't work for older */ +/* versions. Thanks to Jakub Jelinek for most of the code. */ + +#if GC_GLIBC_PREREQ(2, 3) || defined(HOST_ANDROID) + /* Are others OK here, too? */ +# ifndef HAVE_DL_ITERATE_PHDR +# define HAVE_DL_ITERATE_PHDR +# endif +# ifdef HOST_ANDROID + /* Android headers might have no such definition for some targets. */ + EXTERN_C_BEGIN + extern int dl_iterate_phdr(int (*cb)(struct dl_phdr_info *, + size_t, void *), + void *data); + EXTERN_C_END +# endif +#endif /* __GLIBC__ >= 2 || HOST_ANDROID */ + +#if defined(__DragonFly__) || defined(__FreeBSD_kernel__) \ + || (defined(FREEBSD) && __FreeBSD__ >= 7) + /* On the FreeBSD system, any target system at major version 7 shall */ + /* have dl_iterate_phdr; therefore, we need not make it weak as below. */ +# ifndef HAVE_DL_ITERATE_PHDR +# define HAVE_DL_ITERATE_PHDR +# endif +# define DL_ITERATE_PHDR_STRONG +#elif defined(HAVE_DL_ITERATE_PHDR) + /* We have the header files for a glibc that includes dl_iterate_phdr.*/ + /* It may still not be available in the library on the target system. */ + /* Thus we also treat it as a weak symbol. */ + EXTERN_C_BEGIN +# pragma weak dl_iterate_phdr + EXTERN_C_END +#endif + +#if defined(HAVE_DL_ITERATE_PHDR) + +# ifdef PT_GNU_RELRO +/* Instead of registering PT_LOAD sections directly, we keep them */ +/* in a temporary list, and filter them by excluding PT_GNU_RELRO */ +/* segments. Processing PT_GNU_RELRO sections with */ +/* GC_exclude_static_roots instead would be superficially cleaner. But */ +/* it runs into trouble if a client registers an overlapping segment, */ +/* which unfortunately seems quite possible. */ + +# define MAX_LOAD_SEGS MAX_ROOT_SETS + + static struct load_segment { + ptr_t start; + ptr_t end; + /* Room for a second segment if we remove a RELRO segment */ + /* from the middle. */ + ptr_t start2; + ptr_t end2; + } load_segs[MAX_LOAD_SEGS]; + + static int n_load_segs; + static GC_bool load_segs_overflow; +# endif /* PT_GNU_RELRO */ + +STATIC int GC_register_dynlib_callback(struct dl_phdr_info * info, + size_t size, void * ptr) +{ + const ElfW(Phdr) * p; + ptr_t start, end; + int i; + + GC_ASSERT(I_HOLD_LOCK()); + /* Make sure struct dl_phdr_info is at least as big as we need. */ + if (size < offsetof(struct dl_phdr_info, dlpi_phnum) + + sizeof(info->dlpi_phnum)) + return -1; + + p = info->dlpi_phdr; + for (i = 0; i < (int)info->dlpi_phnum; i++, p++) { + if (p->p_type == PT_LOAD) { + GC_has_static_roots_func callback = GC_has_static_roots; + if ((p->p_flags & PF_W) == 0) continue; + + start = (ptr_t)p->p_vaddr + info->dlpi_addr; + end = start + p->p_memsz; + if (callback != 0 && !callback(info->dlpi_name, start, p->p_memsz)) + continue; +# ifdef PT_GNU_RELRO +# if CPP_WORDSZ == 64 + /* TODO: GC_push_all eventually does the correct */ + /* rounding to the next multiple of ALIGNMENT, so, most */ + /* probably, we should remove the corresponding assertion */ + /* check in GC_add_roots_inner along with this code line. */ + /* start pointer value may require aligning. */ + start = (ptr_t)((word)start & ~(word)(sizeof(word)-1)); +# endif + if (n_load_segs >= MAX_LOAD_SEGS) { + if (!load_segs_overflow) { + WARN("Too many PT_LOAD segments;" + " registering as roots directly...\n", 0); + load_segs_overflow = TRUE; + } + GC_add_roots_inner(start, end, TRUE); + } else { + load_segs[n_load_segs].start = start; + load_segs[n_load_segs].end = end; + load_segs[n_load_segs].start2 = 0; + load_segs[n_load_segs].end2 = 0; + ++n_load_segs; + } +# else + GC_add_roots_inner(start, end, TRUE); +# endif /* !PT_GNU_RELRO */ + } + } + +# ifdef PT_GNU_RELRO + p = info->dlpi_phdr; + for (i = 0; i < (int)info->dlpi_phnum; i++, p++) { + if (p->p_type == PT_GNU_RELRO) { + /* This entry is known to be constant and will eventually be */ + /* remapped as read-only. However, the address range covered */ + /* by this entry is typically a subset of a previously */ + /* encountered "LOAD" segment, so we need to exclude it. */ + int j; + + start = (ptr_t)p->p_vaddr + info->dlpi_addr; + end = start + p->p_memsz; + for (j = n_load_segs; --j >= 0; ) { + if ((word)start >= (word)load_segs[j].start + && (word)start < (word)load_segs[j].end) { + if (load_segs[j].start2 != 0) { + WARN("More than one GNU_RELRO segment per load one\n",0); + } else { + GC_ASSERT((word)end <= + (word)PTRT_ROUNDUP_BY_MASK(load_segs[j].end, GC_page_size-1)); + /* Remove from the existing load segment. */ + load_segs[j].end2 = load_segs[j].end; + load_segs[j].end = start; + load_segs[j].start2 = end; + /* Note that start2 may be greater than end2 because of */ + /* p->p_memsz value multiple of page size. */ + } + break; + } + if (0 == j && 0 == GC_has_static_roots) + WARN("Failed to find PT_GNU_RELRO segment" + " inside PT_LOAD region\n", 0); + /* No warning reported in case of the callback is present */ + /* because most likely the segment has been excluded. */ + } + } + } +# endif + + *(int *)ptr = 1; /* Signal that we were called */ + return 0; +} + +/* Do we need to separately register the main static data segment? */ +GC_INNER GC_bool GC_register_main_static_data(void) +{ +# if defined(DL_ITERATE_PHDR_STRONG) && !defined(CPPCHECK) + /* If dl_iterate_phdr is not a weak symbol then don't test against */ + /* zero (otherwise a compiler might issue a warning). */ + return FALSE; +# else + return 0 == COVERT_DATAFLOW(dl_iterate_phdr); +# endif +} + +/* Return TRUE if we succeed, FALSE if dl_iterate_phdr wasn't there. */ +STATIC GC_bool GC_register_dynamic_libraries_dl_iterate_phdr(void) +{ + int did_something; + + GC_ASSERT(I_HOLD_LOCK()); + if (GC_register_main_static_data()) + return FALSE; + +# ifdef PT_GNU_RELRO + { + static GC_bool excluded_segs = FALSE; + n_load_segs = 0; + load_segs_overflow = FALSE; + if (!EXPECT(excluded_segs, TRUE)) { + GC_exclude_static_roots_inner((ptr_t)load_segs, + (ptr_t)load_segs + sizeof(load_segs)); + excluded_segs = TRUE; + } + } +# endif + + did_something = 0; + dl_iterate_phdr(GC_register_dynlib_callback, &did_something); + if (did_something) { +# ifdef PT_GNU_RELRO + int i; + + for (i = 0; i < n_load_segs; ++i) { + if ((word)load_segs[i].end > (word)load_segs[i].start) { + GC_add_roots_inner(load_segs[i].start, load_segs[i].end, TRUE); + } + if ((word)load_segs[i].end2 > (word)load_segs[i].start2) { + GC_add_roots_inner(load_segs[i].start2, load_segs[i].end2, TRUE); + } + } +# endif + } else { + ptr_t datastart, dataend; +# ifdef DATASTART_IS_FUNC + static ptr_t datastart_cached = (ptr_t)GC_WORD_MAX; + + /* Evaluate DATASTART only once. */ + if (datastart_cached == (ptr_t)GC_WORD_MAX) { + datastart_cached = DATASTART; + } + datastart = datastart_cached; +# else + datastart = DATASTART; +# endif +# ifdef DATAEND_IS_FUNC + { + static ptr_t dataend_cached = 0; + /* Evaluate DATAEND only once. */ + if (dataend_cached == 0) { + dataend_cached = DATAEND; + } + dataend = dataend_cached; + } +# else + dataend = DATAEND; +# endif + if (NULL == *(char * volatile *)&datastart + || (word)datastart > (word)dataend) + ABORT_ARG2("Wrong DATASTART/END pair", + ": %p .. %p", (void *)datastart, (void *)dataend); + + /* dl_iterate_phdr may forget the static data segment in */ + /* statically linked executables. */ + GC_add_roots_inner(datastart, dataend, TRUE); +# ifdef GC_HAVE_DATAREGION2 + if ((word)DATASTART2 - 1U >= (word)DATAEND2) { + /* Subtract one to check also for NULL */ + /* without a compiler warning. */ + ABORT_ARG2("Wrong DATASTART/END2 pair", + ": %p .. %p", (void *)DATASTART2, (void *)DATAEND2); + } + GC_add_roots_inner(DATASTART2, DATAEND2, TRUE); +# endif + } + return TRUE; +} + +# define HAVE_REGISTER_MAIN_STATIC_DATA + +#else /* !HAVE_DL_ITERATE_PHDR */ + +/* Dynamic loading code for Linux running ELF. Somewhat tested on + * Linux/x86, untested but hopefully should work on Linux/Alpha. + * This code was derived from the Solaris/ELF support. Thanks to + * whatever kind soul wrote that. - Patrick Bridges */ + +/* This doesn't necessarily work in all cases, e.g. with preloaded + * dynamic libraries. */ + +# if defined(NETBSD) || defined(OPENBSD) +# include + /* for compatibility with 1.4.x */ +# ifndef DT_DEBUG +# define DT_DEBUG 21 +# endif +# ifndef PT_LOAD +# define PT_LOAD 1 +# endif +# ifndef PF_W +# define PF_W 2 +# endif +# elif !defined(HOST_ANDROID) +# include +# endif + +# ifndef HOST_ANDROID +# include +# endif + +#endif /* !HAVE_DL_ITERATE_PHDR */ + +EXTERN_C_BEGIN +#ifdef __GNUC__ +# pragma weak _DYNAMIC +#endif +extern ElfW(Dyn) _DYNAMIC[]; +EXTERN_C_END + +STATIC struct link_map * +GC_FirstDLOpenedLinkMap(void) +{ + static struct link_map *cachedResult = 0; + + if (0 == COVERT_DATAFLOW(_DYNAMIC)) { + /* _DYNAMIC symbol not resolved. */ + return NULL; + } + if (NULL == cachedResult) { +# if defined(NETBSD) && defined(RTLD_DI_LINKMAP) +# if defined(CPPCHECK) +# define GC_RTLD_DI_LINKMAP 2 +# else +# define GC_RTLD_DI_LINKMAP RTLD_DI_LINKMAP +# endif + struct link_map *lm = NULL; + if (!dlinfo(RTLD_SELF, GC_RTLD_DI_LINKMAP, &lm) && lm != NULL) { + /* Now lm points link_map object of libgc. Since it */ + /* might not be the first dynamically linked object, */ + /* try to find it (object next to the main object). */ + while (lm->l_prev != NULL) { + lm = lm->l_prev; + } + cachedResult = lm->l_next; + } +# else + ElfW(Dyn) *dp; + int tag; + + for( dp = _DYNAMIC; (tag = dp->d_tag) != 0; dp++ ) { + if (tag == DT_DEBUG) { + struct r_debug *rd = (struct r_debug *)dp->d_un.d_ptr; + /* d_ptr could be null if libs are linked statically. */ + if (rd != NULL) { + struct link_map *lm = rd->r_map; + if (lm != NULL) + cachedResult = lm->l_next; /* might be NULL */ + } + break; + } + } +# endif /* !NETBSD || !RTLD_DI_LINKMAP */ + } + return cachedResult; +} + +GC_INNER void GC_register_dynamic_libraries(void) +{ + struct link_map *lm; + + GC_ASSERT(I_HOLD_LOCK()); +# ifdef HAVE_DL_ITERATE_PHDR + if (GC_register_dynamic_libraries_dl_iterate_phdr()) { + return; + } +# endif + for (lm = GC_FirstDLOpenedLinkMap(); lm != 0; lm = lm->l_next) + { + ElfW(Ehdr) * e; + ElfW(Phdr) * p; + unsigned long offset; + char * start; + int i; + + e = (ElfW(Ehdr) *) lm->l_addr; +# ifdef HOST_ANDROID + if (e == NULL) + continue; +# endif + p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff)); + offset = ((unsigned long)(lm->l_addr)); + for( i = 0; i < (int)e->e_phnum; i++, p++ ) { + switch( p->p_type ) { + case PT_LOAD: + { + if( !(p->p_flags & PF_W) ) break; + start = ((char *)(p->p_vaddr)) + offset; + GC_add_roots_inner(start, start + p->p_memsz, TRUE); + } + break; + default: + break; + } + } + } +} + +#endif /* !USE_PROC_FOR_LIBRARIES */ + +#endif /* LINUX */ + +#if defined(IRIX5) || (defined(USE_PROC_FOR_LIBRARIES) && !defined(LINUX)) + +#include +#include +#include +#include +#include +#include /* Only for the following test. */ +#ifndef _sigargs +# define IRIX6 +#endif + +/* We use /proc to track down all parts of the address space that are */ +/* mapped by the process, and throw out regions we know we shouldn't */ +/* worry about. This may also work under other SVR4 variants. */ +GC_INNER void GC_register_dynamic_libraries(void) +{ + static int fd = -1; + static prmap_t * addr_map = 0; + static int current_sz = 0; /* Number of records currently in addr_map */ + + char buf[32]; + int needed_sz = 0; /* Required size of addr_map */ + int i; + long flags; + ptr_t start; + ptr_t limit; + ptr_t heap_start = HEAP_START; + ptr_t heap_end = heap_start; + +# ifdef SOLARISDL +# define MA_PHYS 0 +# endif /* SOLARISDL */ + + GC_ASSERT(I_HOLD_LOCK()); + if (fd < 0) { + (void)snprintf(buf, sizeof(buf), "/proc/%ld", (long)getpid()); + buf[sizeof(buf) - 1] = '\0'; + fd = open(buf, O_RDONLY); + if (fd < 0) { + ABORT("/proc open failed"); + } + } + if (ioctl(fd, PIOCNMAP, &needed_sz) < 0) { + ABORT_ARG2("/proc PIOCNMAP ioctl failed", + ": fd= %d, errno= %d", fd, errno); + } + if (needed_sz >= current_sz) { + GC_scratch_recycle_no_gww(addr_map, + (size_t)current_sz * sizeof(prmap_t)); + current_sz = needed_sz * 2 + 1; + /* Expansion, plus room for 0 record */ + addr_map = (prmap_t *)GC_scratch_alloc( + (size_t)current_sz * sizeof(prmap_t)); + if (addr_map == NULL) + ABORT("Insufficient memory for address map"); + } + if (ioctl(fd, PIOCMAP, addr_map) < 0) { + ABORT_ARG3("/proc PIOCMAP ioctl failed", + ": errcode= %d, needed_sz= %d, addr_map= %p", + errno, needed_sz, (void *)addr_map); + } + if (GC_n_heap_sects > 0) { + heap_end = GC_heap_sects[GC_n_heap_sects-1].hs_start + + GC_heap_sects[GC_n_heap_sects-1].hs_bytes; + if ((word)heap_end < (word)GC_scratch_last_end_ptr) + heap_end = GC_scratch_last_end_ptr; + } + for (i = 0; i < needed_sz; i++) { + flags = addr_map[i].pr_mflags; + if ((flags & (MA_BREAK | MA_STACK | MA_PHYS + | MA_FETCHOP | MA_NOTCACHED)) != 0) goto irrelevant; + if ((flags & (MA_READ | MA_WRITE)) != (MA_READ | MA_WRITE)) + goto irrelevant; + /* The latter test is empirically useless in very old Irix */ + /* versions. Other than the */ + /* main data and stack segments, everything appears to be */ + /* mapped readable, writable, executable, and shared(!!). */ + /* This makes no sense to me. - HB */ + start = (ptr_t)(addr_map[i].pr_vaddr); + if (GC_roots_present(start)) goto irrelevant; + if ((word)start < (word)heap_end && (word)start >= (word)heap_start) + goto irrelevant; + + limit = start + addr_map[i].pr_size; + /* The following seemed to be necessary for very old versions */ + /* of Irix, but it has been reported to discard relevant */ + /* segments under Irix 6.5. */ +# ifndef IRIX6 + if (addr_map[i].pr_off == 0 && strncmp(start, ELFMAG, 4) == 0) { + /* Discard text segments, i.e. 0-offset mappings against */ + /* executable files which appear to have ELF headers. */ + caddr_t arg; + int obj; +# define MAP_IRR_SZ 10 + static ptr_t map_irr[MAP_IRR_SZ]; + /* Known irrelevant map entries */ + static int n_irr = 0; + struct stat buf; + int j; + + for (j = 0; j < n_irr; j++) { + if (map_irr[j] == start) goto irrelevant; + } + arg = (caddr_t)start; + obj = ioctl(fd, PIOCOPENM, &arg); + if (obj >= 0) { + fstat(obj, &buf); + close(obj); + if ((buf.st_mode & 0111) != 0) { + if (n_irr < MAP_IRR_SZ) { + map_irr[n_irr++] = start; + } + goto irrelevant; + } + } + } +# endif /* !IRIX6 */ + GC_add_roots_inner(start, limit, TRUE); + irrelevant: ; + } + /* Don't keep cached descriptor, for now. Some kernels don't like us */ + /* to keep a /proc file descriptor around during kill -9. */ + /* Otherwise, it should also require FD_CLOEXEC and proper handling */ + /* at fork (i.e. close because of the pid change). */ + if (close(fd) < 0) ABORT("Couldn't close /proc file"); + fd = -1; +} + +# endif /* USE_PROC_FOR_LIBRARIES || IRIX5 */ + +#ifdef ANY_MSWIN + /* We traverse the entire address space and register all segments */ + /* that could possibly have been written to. */ + STATIC void GC_cond_add_roots(char *base, char * limit) + { +# ifdef THREADS + char * curr_base = base; + char * next_stack_lo; + char * next_stack_hi; +# else + char * stack_top; +# endif + + GC_ASSERT(I_HOLD_LOCK()); + if (base == limit) return; +# ifdef THREADS + for(;;) { + GC_get_next_stack(curr_base, limit, &next_stack_lo, &next_stack_hi); + if ((word)next_stack_lo >= (word)limit) break; + if ((word)next_stack_lo > (word)curr_base) + GC_add_roots_inner(curr_base, next_stack_lo, TRUE); + curr_base = next_stack_hi; + } + if ((word)curr_base < (word)limit) + GC_add_roots_inner(curr_base, limit, TRUE); +# else + stack_top = (char *)((word)GC_approx_sp() & + ~(word)(GC_sysinfo.dwAllocationGranularity - 1)); + if ((word)limit > (word)stack_top + && (word)base < (word)GC_stackbottom) { + /* Part of the stack; ignore it. */ + return; + } + GC_add_roots_inner(base, limit, TRUE); +# endif + } + +#ifdef DYNAMIC_LOADING + /* GC_register_main_static_data is not needed unless DYNAMIC_LOADING. */ + GC_INNER GC_bool GC_register_main_static_data(void) + { +# if defined(MSWINCE) || defined(CYGWIN32) + /* Do we need to separately register the main static data segment? */ + return FALSE; +# else + return GC_no_win32_dlls; +# endif + } +# define HAVE_REGISTER_MAIN_STATIC_DATA +#endif /* DYNAMIC_LOADING */ + +# ifdef DEBUG_VIRTUALQUERY + void GC_dump_meminfo(MEMORY_BASIC_INFORMATION *buf) + { + GC_printf("BaseAddress= 0x%lx, AllocationBase= 0x%lx," + " RegionSize= 0x%lx(%lu)\n", + buf -> BaseAddress, buf -> AllocationBase, + buf -> RegionSize, buf -> RegionSize); + GC_printf("\tAllocationProtect= 0x%lx, State= 0x%lx, Protect= 0x%lx, " + "Type= 0x%lx\n", buf -> AllocationProtect, buf -> State, + buf -> Protect, buf -> Type); + } +# endif /* DEBUG_VIRTUALQUERY */ + +# if defined(MSWINCE) || defined(CYGWIN32) + /* FIXME: Should we really need to scan MEM_PRIVATE sections? */ + /* For now, we don't add MEM_PRIVATE sections to the data roots for */ + /* WinCE because otherwise SEGV fault sometimes happens to occur in */ + /* GC_mark_from() (and, even if we use WRAP_MARK_SOME, WinCE prints */ + /* a "Data Abort" message to the debugging console). */ + /* To workaround that, use -DGC_REGISTER_MEM_PRIVATE. */ +# define GC_wnt TRUE +# endif + + GC_INNER void GC_register_dynamic_libraries(void) + { + MEMORY_BASIC_INFORMATION buf; + DWORD protect; + LPVOID p; + char * base; + char * limit, * new_limit; + + GC_ASSERT(I_HOLD_LOCK()); +# ifdef MSWIN32 + if (GC_no_win32_dlls) return; +# endif + p = GC_sysinfo.lpMinimumApplicationAddress; + base = limit = (char *)p; + while ((word)p < (word)GC_sysinfo.lpMaximumApplicationAddress) { + size_t result = VirtualQuery(p, &buf, sizeof(buf)); + +# ifdef MSWINCE + if (result == 0) { + /* Page is free; advance to the next possible allocation base */ + new_limit = (char *)(((word)p + GC_sysinfo.dwAllocationGranularity) + & ~(GC_sysinfo.dwAllocationGranularity-1)); + } else +# endif + /* else */ { + if (result != sizeof(buf)) { + ABORT("Weird VirtualQuery result"); + } + new_limit = (char *)p + buf.RegionSize; + protect = buf.Protect; + if (buf.State == MEM_COMMIT + && (protect == PAGE_EXECUTE_READWRITE + || protect == PAGE_EXECUTE_WRITECOPY + || protect == PAGE_READWRITE + || protect == PAGE_WRITECOPY) + && (buf.Type == MEM_IMAGE +# ifdef GC_REGISTER_MEM_PRIVATE + || (protect == PAGE_READWRITE && buf.Type == MEM_PRIVATE) +# else + /* There is some evidence that we cannot always */ + /* ignore MEM_PRIVATE sections under Windows ME */ + /* and predecessors. Hence we now also check for */ + /* that case. */ + || (!GC_wnt && buf.Type == MEM_PRIVATE) +# endif + ) + && !GC_is_heap_base(buf.AllocationBase)) { +# ifdef DEBUG_VIRTUALQUERY + GC_dump_meminfo(&buf); +# endif + if ((char *)p != limit) { + GC_cond_add_roots(base, limit); + base = (char *)p; + } + limit = new_limit; + } + } + if ((word)p > (word)new_limit /* overflow */) break; + p = (LPVOID)new_limit; + } + GC_cond_add_roots(base, limit); + } +#endif /* ANY_MSWIN */ + +#if defined(ALPHA) && defined(OSF1) +# include + + EXTERN_C_BEGIN + extern char *sys_errlist[]; + extern int sys_nerr; + extern int errno; + EXTERN_C_END + + GC_INNER void GC_register_dynamic_libraries(void) + { + ldr_module_t moduleid = LDR_NULL_MODULE; + ldr_process_t mypid; + + GC_ASSERT(I_HOLD_LOCK()); + mypid = ldr_my_process(); /* obtain id of this process */ + + /* For each module. */ + for (;;) { + ldr_module_info_t moduleinfo; + size_t modulereturnsize; + ldr_region_t region; + ldr_region_info_t regioninfo; + size_t regionreturnsize; + int status = ldr_next_module(mypid, &moduleid); + /* Get the next (first) module */ + + if (moduleid == LDR_NULL_MODULE) + break; /* no more modules */ + + /* Check status AFTER checking moduleid because */ + /* of a bug in the non-shared ldr_next_module stub. */ + if (status != 0) { + ABORT_ARG3("ldr_next_module failed", + ": status= %d, errcode= %d (%s)", status, errno, + errno < sys_nerr ? sys_errlist[errno] : ""); + } + + /* Get the module information */ + status = ldr_inq_module(mypid, moduleid, &moduleinfo, + sizeof(moduleinfo), &modulereturnsize); + if (status != 0 ) + ABORT("ldr_inq_module failed"); + + /* is module for the main program (i.e. nonshared portion)? */ + if (moduleinfo.lmi_flags & LDR_MAIN) + continue; /* skip the main module */ + +# ifdef DL_VERBOSE + GC_log_printf("---Module---\n"); + GC_log_printf("Module ID: %ld\n", moduleinfo.lmi_modid); + GC_log_printf("Count of regions: %d\n", moduleinfo.lmi_nregion); + GC_log_printf("Flags for module: %016lx\n", moduleinfo.lmi_flags); + GC_log_printf("Module pathname: \"%s\"\n", moduleinfo.lmi_name); +# endif + + /* For each region in this module. */ + for (region = 0; region < moduleinfo.lmi_nregion; region++) { + /* Get the region information */ + status = ldr_inq_region(mypid, moduleid, region, ®ioninfo, + sizeof(regioninfo), ®ionreturnsize); + if (status != 0 ) + ABORT("ldr_inq_region failed"); + + /* only process writable (data) regions */ + if (! (regioninfo.lri_prot & LDR_W)) + continue; + +# ifdef DL_VERBOSE + GC_log_printf("--- Region ---\n"); + GC_log_printf("Region number: %ld\n", regioninfo.lri_region_no); + GC_log_printf("Protection flags: %016x\n", regioninfo.lri_prot); + GC_log_printf("Virtual address: %p\n", regioninfo.lri_vaddr); + GC_log_printf("Mapped address: %p\n", regioninfo.lri_mapaddr); + GC_log_printf("Region size: %ld\n", regioninfo.lri_size); + GC_log_printf("Region name: \"%s\"\n", regioninfo.lri_name); +# endif + + /* register region as a garbage collection root */ + GC_add_roots_inner((char *)regioninfo.lri_mapaddr, + (char *)regioninfo.lri_mapaddr + regioninfo.lri_size, + TRUE); + + } + } + } +#endif /* ALPHA && OSF1 */ + +#if defined(HPUX) +#include +#include + +EXTERN_C_BEGIN +extern char *sys_errlist[]; +extern int sys_nerr; +EXTERN_C_END + +GC_INNER void GC_register_dynamic_libraries(void) +{ + int index = 1; /* Ordinal position in shared library search list */ + + GC_ASSERT(I_HOLD_LOCK()); + /* For each dynamic library loaded. */ + for (;;) { + struct shl_descriptor *shl_desc; /* Shared library info, see dl.h */ + int status = shl_get(index, &shl_desc); + /* Get info about next shared library */ + + /* Check if this is the end of the list or if some error occurred */ + if (status != 0) { +# ifdef GC_HPUX_THREADS + /* I've seen errno values of 0. The man page is not clear */ + /* as to whether errno should get set on a -1 return. */ + break; +# else + if (errno == EINVAL) { + break; /* Moved past end of shared library list --> finished */ + } else { + ABORT_ARG3("shl_get failed", + ": status= %d, errcode= %d (%s)", status, errno, + errno < sys_nerr ? sys_errlist[errno] : ""); + } +# endif + } + +# ifdef DL_VERBOSE + GC_log_printf("---Shared library---\n"); + GC_log_printf("filename= \"%s\"\n", shl_desc->filename); + GC_log_printf("index= %d\n", index); + GC_log_printf("handle= %08x\n", (unsigned long) shl_desc->handle); + GC_log_printf("text seg.start= %08x\n", shl_desc->tstart); + GC_log_printf("text seg.end= %08x\n", shl_desc->tend); + GC_log_printf("data seg.start= %08x\n", shl_desc->dstart); + GC_log_printf("data seg.end= %08x\n", shl_desc->dend); + GC_log_printf("ref.count= %lu\n", shl_desc->ref_count); +# endif + + /* register shared library's data segment as a garbage collection root */ + GC_add_roots_inner((char *) shl_desc->dstart, + (char *) shl_desc->dend, TRUE); + + index++; + } +} +#endif /* HPUX */ + +#ifdef AIX +# include +# include +# include + + GC_INNER void GC_register_dynamic_libraries(void) + { + int ldibuflen = 8192; + + GC_ASSERT(I_HOLD_LOCK()); + for (;;) { + int len; + struct ld_info *ldi; +# if defined(CPPCHECK) + char ldibuf[ldibuflen]; +# else + char *ldibuf = alloca(ldibuflen); +# endif + + len = loadquery(L_GETINFO, ldibuf, ldibuflen); + if (len < 0) { + if (errno != ENOMEM) { + ABORT("loadquery failed"); + } + ldibuflen *= 2; + continue; + } + + ldi = (struct ld_info *)ldibuf; + while (ldi) { + len = ldi->ldinfo_next; + GC_add_roots_inner( + ldi->ldinfo_dataorg, + (ptr_t)(unsigned long)ldi->ldinfo_dataorg + + ldi->ldinfo_datasize, + TRUE); + ldi = len ? (struct ld_info *)((char *)ldi + len) : 0; + } + break; + } + } +#endif /* AIX */ + +#ifdef DARWIN + +/* __private_extern__ hack required for pre-3.4 gcc versions. */ +#ifndef __private_extern__ +# define __private_extern__ extern +# include +# undef __private_extern__ +#else +# include +#endif + +#if CPP_WORDSZ == 64 +# define GC_MACH_HEADER mach_header_64 +#else +# define GC_MACH_HEADER mach_header +#endif + +#ifdef MISSING_MACH_O_GETSECT_H + EXTERN_C_BEGIN + extern uint8_t *getsectiondata(const struct GC_MACH_HEADER *, + const char *seg, const char *sect, unsigned long *psz); + EXTERN_C_END +#else +# include +#endif + +/*#define DARWIN_DEBUG*/ + +/* Writable sections generally available on Darwin. */ +STATIC const struct dyld_sections_s { + const char *seg; + const char *sect; +} GC_dyld_sections[] = { + { SEG_DATA, SECT_DATA }, + /* Used by FSF GCC, but not by OS X system tools, so far. */ + { SEG_DATA, "__static_data" }, + { SEG_DATA, SECT_BSS }, + { SEG_DATA, SECT_COMMON }, + /* FSF GCC - zero-sized object sections for targets */ + /*supporting section anchors. */ + { SEG_DATA, "__zobj_data" }, + { SEG_DATA, "__zobj_bss" } +}; + +/* Additional writable sections: */ +/* GCC on Darwin constructs aligned sections "on demand", where */ +/* the alignment size is embedded in the section name. */ +/* Furthermore, there are distinctions between sections */ +/* containing private vs. public symbols. It also constructs */ +/* sections specifically for zero-sized objects, when the */ +/* target supports section anchors. */ +STATIC const char * const GC_dyld_bss_prefixes[] = { + "__bss", + "__pu_bss", + "__zo_bss", + "__zo_pu_bss" +}; + +/* Currently, mach-o will allow up to the max of 2^15 alignment */ +/* in an object file. */ +#ifndef L2_MAX_OFILE_ALIGNMENT +# define L2_MAX_OFILE_ALIGNMENT 15 +#endif + +STATIC const char *GC_dyld_name_for_hdr(const struct GC_MACH_HEADER *hdr) +{ + unsigned long i, count = _dyld_image_count(); + + for (i = 0; i < count; i++) { + if ((const struct GC_MACH_HEADER *)_dyld_get_image_header(i) == hdr) + return _dyld_get_image_name(i); + } + /* TODO: probably ABORT in this case? */ + return NULL; /* not found */ +} + +/* getsectbynamefromheader is deprecated (first time in macOS 13.0), */ +/* getsectiondata (introduced in macOS 10.7) is used instead if exists. */ +/* Define USE_GETSECTBYNAME to use the deprecated symbol, if needed. */ +#if !defined(USE_GETSECTBYNAME) \ + && (MAC_OS_X_VERSION_MIN_REQUIRED < 1070 /*MAC_OS_X_VERSION_10_7*/) +# define USE_GETSECTBYNAME +#endif + +static void dyld_section_add_del(const struct GC_MACH_HEADER *hdr, + intptr_t slide, const char *dlpi_name, + GC_has_static_roots_func callback, + const char *seg, const char *secnam, + GC_bool is_add) +{ + unsigned long start, end, sec_size; +# ifdef USE_GETSECTBYNAME +# if CPP_WORDSZ == 64 + const struct section_64 *sec = getsectbynamefromheader_64(hdr, seg, + secnam); +# else + const struct section *sec = getsectbynamefromheader(hdr, seg, secnam); +# endif + + if (NULL == sec) return; + sec_size = sec -> size; + start = slide + sec -> addr; +# else + + UNUSED_ARG(slide); + sec_size = 0; + start = (unsigned long)getsectiondata(hdr, seg, secnam, &sec_size); + if (0 == start) return; +# endif + if (sec_size < sizeof(word)) return; + end = start + sec_size; + if (is_add) { + LOCK(); + /* The user callback is invoked holding the allocator lock. */ + if (EXPECT(callback != 0, FALSE) + && !callback(dlpi_name, (void *)start, (size_t)sec_size)) { + UNLOCK(); + return; /* skip section */ + } + GC_add_roots_inner((ptr_t)start, (ptr_t)end, FALSE); + UNLOCK(); + } else { + GC_remove_roots((void *)start, (void *)end); + } +# ifdef DARWIN_DEBUG + GC_log_printf("%s section __DATA,%s at %p-%p (%lu bytes) from image %s\n", + is_add ? "Added" : "Removed", + secnam, (void *)start, (void *)end, sec_size, dlpi_name); +# endif +} + +static void dyld_image_add_del(const struct GC_MACH_HEADER *hdr, + intptr_t slide, + GC_has_static_roots_func callback, + GC_bool is_add) +{ + unsigned i, j; + const char *dlpi_name; + + GC_ASSERT(I_DONT_HOLD_LOCK()); +# ifndef DARWIN_DEBUG + if (0 == callback) { + dlpi_name = NULL; + } else +# endif + /* else */ { + dlpi_name = GC_dyld_name_for_hdr(hdr); + } + for (i = 0; i < sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]); i++) { + dyld_section_add_del(hdr, slide, dlpi_name, callback, + GC_dyld_sections[i].seg, GC_dyld_sections[i].sect, + is_add); + } + + /* Sections constructed on demand. */ + for (j = 0; j < sizeof(GC_dyld_bss_prefixes) / sizeof(char *); j++) { + /* Our manufactured aligned BSS sections. */ + for (i = 0; i <= L2_MAX_OFILE_ALIGNMENT; i++) { + char secnam[16]; + + (void)snprintf(secnam, sizeof(secnam), "%s%u", + GC_dyld_bss_prefixes[j], i); + secnam[sizeof(secnam) - 1] = '\0'; + dyld_section_add_del(hdr, slide, dlpi_name, 0 /* callback */, SEG_DATA, + secnam, is_add); + } + } + +# if defined(DARWIN_DEBUG) && !defined(NO_DEBUGGING) + READER_LOCK(); + GC_print_static_roots(); + READER_UNLOCK(); +# endif +} + +STATIC void GC_dyld_image_add(const struct GC_MACH_HEADER *hdr, intptr_t slide) +{ + if (!GC_no_dls) + dyld_image_add_del(hdr, slide, GC_has_static_roots, TRUE); +} + +STATIC void GC_dyld_image_remove(const struct GC_MACH_HEADER *hdr, + intptr_t slide) +{ + dyld_image_add_del(hdr, slide, 0 /* callback */, FALSE); +} + +GC_INNER void GC_register_dynamic_libraries(void) +{ + /* Currently does nothing. The callbacks are setup by GC_init_dyld() + The dyld library takes it from there. */ +} + +/* The _dyld_* functions have an internal lock, so none of them can be */ +/* called while the world is stopped without the risk of a deadlock. */ +/* Because of this we MUST setup callbacks BEFORE we ever stop the */ +/* world. This should be called BEFORE any thread is created and */ +/* WITHOUT the allocator lock held. */ + +/* _dyld_bind_fully_image_containing_address is deprecated, so use */ +/* dlopen(0,RTLD_NOW) instead; define USE_DYLD_TO_BIND to override this */ +/* if needed. */ + +GC_INNER void GC_init_dyld(void) +{ + static GC_bool initialized = FALSE; + + GC_ASSERT(I_DONT_HOLD_LOCK()); + if (initialized) return; + +# ifdef DARWIN_DEBUG + GC_log_printf("Registering dyld callbacks...\n"); +# endif + + /* Apple's Documentation: + When you call _dyld_register_func_for_add_image, the dynamic linker + runtime calls the specified callback (func) once for each of the images + that is currently loaded into the program. When a new image is added to + the program, your callback is called again with the mach_header for the + new image, and the virtual memory slide amount of the new image. + + This WILL properly register already linked libraries and libraries + linked in the future. + */ + _dyld_register_func_for_add_image( + (void (*)(const struct mach_header*, intptr_t))GC_dyld_image_add); + _dyld_register_func_for_remove_image( + (void (*)(const struct mach_header*, intptr_t))GC_dyld_image_remove); + /* Structure mach_header_64 has the same fields */ + /* as mach_header except for the reserved one */ + /* at the end, so these casts are OK. */ + + /* Set this early to avoid reentrancy issues. */ + initialized = TRUE; + +# ifndef NO_DYLD_BIND_FULLY_IMAGE + if (GC_no_dls) return; /* skip main data segment registration */ + + /* When the environment variable is set, the dynamic linker binds */ + /* all undefined symbols the application needs at launch time. */ + /* This includes function symbols that are normally bound lazily at */ + /* the time of their first invocation. */ + if (GETENV("DYLD_BIND_AT_LAUNCH") != NULL) return; + + /* The environment variable is unset, so we should bind manually. */ +# ifdef DARWIN_DEBUG + GC_log_printf("Forcing full bind of GC code...\n"); +# endif +# ifndef USE_DYLD_TO_BIND + { + void *dl_handle = dlopen(NULL, RTLD_NOW); + + if (!dl_handle) + ABORT("dlopen failed (to bind fully image)"); + /* Note that the handle is never closed. */ +# ifdef LINT2 + GC_noop1((word)dl_handle); +# endif + } +# else + /* Note: '_dyld_bind_fully_image_containing_address' is deprecated. */ + if (!_dyld_bind_fully_image_containing_address( + (unsigned long *)GC_malloc)) + ABORT("_dyld_bind_fully_image_containing_address failed"); +# endif +# endif +} + +#define HAVE_REGISTER_MAIN_STATIC_DATA +GC_INNER GC_bool GC_register_main_static_data(void) +{ + /* Already done through dyld callbacks */ + return FALSE; +} + +#endif /* DARWIN */ + +#if defined(HAIKU) +# include + + GC_INNER void GC_register_dynamic_libraries(void) + { + image_info info; + int32 cookie = 0; + + GC_ASSERT(I_HOLD_LOCK()); + while (get_next_image_info(0, &cookie, &info) == B_OK) { + ptr_t data = (ptr_t)info.data; + GC_add_roots_inner(data, data + info.data_size, TRUE); + } + } +#endif /* HAIKU */ + +#elif defined(PCR) + + + + + + GC_INNER void GC_register_dynamic_libraries(void) + { + /* Add new static data areas of dynamically loaded modules. */ + PCR_IL_LoadedFile * p = PCR_IL_GetLastLoadedFile(); + PCR_IL_LoadedSegment * q; + + /* Skip uncommitted files */ + while (p != NIL && !(p -> lf_commitPoint)) { + /* The loading of this file has not yet been committed */ + /* Hence its description could be inconsistent. */ + /* Furthermore, it hasn't yet been run. Hence its data */ + /* segments can't possibly reference heap allocated */ + /* objects. */ + p = p -> lf_prev; + } + for (; p != NIL; p = p -> lf_prev) { + for (q = p -> lf_ls; q != NIL; q = q -> ls_next) { + if ((q -> ls_flags & PCR_IL_SegFlags_Traced_MASK) + == PCR_IL_SegFlags_Traced_on) { + GC_add_roots_inner((ptr_t)q->ls_addr, + (ptr_t)q->ls_addr + q->ls_bytes, TRUE); + } + } + } + } + +#endif /* PCR && !ANY_MSWIN */ + +#ifdef GC_MUST_RESTORE_REDEFINED_DLOPEN +# define dlopen GC_dlopen +#endif + +#if !defined(HAVE_REGISTER_MAIN_STATIC_DATA) && defined(DYNAMIC_LOADING) + /* Do we need to separately register the main static data segment? */ + GC_INNER GC_bool GC_register_main_static_data(void) + { + return TRUE; + } +#endif /* HAVE_REGISTER_MAIN_STATIC_DATA */ + +/* Register a routine to filter dynamic library registration. */ +GC_API void GC_CALL GC_register_has_static_roots_callback( + GC_has_static_roots_func callback) +{ + GC_has_static_roots = callback; +} + +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1997 by Silicon Graphics. All rights reserved. + * Copyright (c) 2000 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +/* This used to be in dyn_load.c. It was extracted into a separate */ +/* file to avoid having to link against libdl.{a,so} if the client */ +/* doesn't call dlopen. Of course this fails if the collector is in */ +/* a dynamic library. -HB */ +#if defined(GC_PTHREADS) && !defined(GC_NO_DLOPEN) + +#undef GC_MUST_RESTORE_REDEFINED_DLOPEN +#if defined(dlopen) && !defined(GC_USE_LD_WRAP) + /* To support various threads pkgs, gc.h interposes on dlopen by */ + /* defining "dlopen" to be "GC_dlopen", which is implemented below. */ + /* However, both GC_FirstDLOpenedLinkMap() and GC_dlopen() use the */ + /* real system dlopen() in their implementation. We first remove */ + /* gc.h's dlopen definition and restore it later, after GC_dlopen(). */ +# undef dlopen +# define GC_MUST_RESTORE_REDEFINED_DLOPEN +#endif + +/* Make sure we're not in the middle of a collection, and make sure we */ +/* don't start any. This is invoked prior to a dlopen call to avoid */ +/* synchronization issues. We cannot just acquire the allocator lock, */ +/* since startup code in dlopen may try to allocate. This solution */ +/* risks heap growth (or, even, heap overflow) in the presence of many */ +/* dlopen calls in either a multi-threaded environment, or if the */ +/* library initialization code allocates substantial amounts of GC'ed */ +/* memory. */ +#ifndef USE_PROC_FOR_LIBRARIES + static void disable_gc_for_dlopen(void) + { + LOCK(); + while (GC_incremental && GC_collection_in_progress()) { + ENTER_GC(); + GC_collect_a_little_inner(1000); + EXIT_GC(); + } + ++GC_dont_gc; + UNLOCK(); + } +#endif + +/* Redefine dlopen to guarantee mutual exclusion with */ +/* GC_register_dynamic_libraries. Should probably happen for */ +/* other operating systems, too. */ + +/* This is similar to WRAP/REAL_FUNC() in pthread_support.c. */ +#ifdef GC_USE_LD_WRAP +# define WRAP_DLFUNC(f) __wrap_##f +# define REAL_DLFUNC(f) __real_##f + void * REAL_DLFUNC(dlopen)(const char *, int); +#else +# define WRAP_DLFUNC(f) GC_##f +# define REAL_DLFUNC(f) f +#endif + +GC_API void * WRAP_DLFUNC(dlopen)(const char *path, int mode) +{ + void * result; + +# ifndef USE_PROC_FOR_LIBRARIES + /* Disable collections. This solution risks heap growth (or, */ + /* even, heap overflow) but there seems no better solutions. */ + disable_gc_for_dlopen(); +# endif + result = REAL_DLFUNC(dlopen)(path, mode); +# ifndef USE_PROC_FOR_LIBRARIES + GC_enable(); /* undoes disable_gc_for_dlopen */ +# endif + return result; +} + +#ifdef GC_USE_LD_WRAP + /* Define GC_ function as an alias for the plain one, which will be */ + /* intercepted. This allows files which include gc.h, and hence */ + /* generate references to the GC_ symbol, to see the right symbol. */ + GC_API void *GC_dlopen(const char *path, int mode) + { + return dlopen(path, mode); + } +#endif /* GC_USE_LD_WRAP */ + +#ifdef GC_MUST_RESTORE_REDEFINED_DLOPEN +# define dlopen GC_dlopen +#endif + +#endif /* GC_PTHREADS && !GC_NO_DLOPEN */ + +#if !defined(PLATFORM_MACH_DEP) +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +#if !defined(PLATFORM_MACH_DEP) && !defined(SN_TARGET_PSP2) + +#ifdef AMIGA +# ifndef __GNUC__ +# include +# else +# include +# endif +#endif + +#if defined(MACOS) && defined(__MWERKS__) + +#if defined(POWERPC) + +# define NONVOLATILE_GPR_COUNT 19 + struct ppc_registers { + unsigned long gprs[NONVOLATILE_GPR_COUNT]; /* R13-R31 */ + }; + typedef struct ppc_registers ppc_registers; + +# if defined(CPPCHECK) + void getRegisters(ppc_registers* regs); +# else + asm static void getRegisters(register ppc_registers* regs) + { + stmw r13,regs->gprs /* save R13-R31 */ + blr + } +# endif + + static void PushMacRegisters(void) + { + ppc_registers regs; + int i; + getRegisters(®s); + for (i = 0; i < NONVOLATILE_GPR_COUNT; i++) + GC_push_one(regs.gprs[i]); + } + +#else /* M68K */ + + asm static void PushMacRegisters(void) + { + sub.w #4,sp /* reserve space for one parameter */ + move.l a2,(sp) + jsr GC_push_one + move.l a3,(sp) + jsr GC_push_one + move.l a4,(sp) + jsr GC_push_one +# if !__option(a6frames) + /* perhaps a6 should be pushed if stack frames are not being used */ + move.l a6,(sp) + jsr GC_push_one +# endif + /* skip a5 (globals), a6 (frame pointer), and a7 (stack pointer) */ + move.l d2,(sp) + jsr GC_push_one + move.l d3,(sp) + jsr GC_push_one + move.l d4,(sp) + jsr GC_push_one + move.l d5,(sp) + jsr GC_push_one + move.l d6,(sp) + jsr GC_push_one + move.l d7,(sp) + jsr GC_push_one + add.w #4,sp /* fix stack */ + rts + } + +#endif /* M68K */ + +#endif /* MACOS && __MWERKS__ */ + +# if defined(IA64) && !defined(THREADS) + /* Value returned from register flushing routine (ar.bsp). */ + GC_INNER ptr_t GC_save_regs_ret_val = NULL; +# endif + +/* Routine to mark from registers that are preserved by the C compiler. */ +/* This must be ported to every new architecture. It is not optional, */ +/* and should not be used on platforms that are either UNIX-like, or */ +/* require thread support. */ + +#undef HAVE_PUSH_REGS + +#if defined(USE_ASM_PUSH_REGS) +# define HAVE_PUSH_REGS +#else /* No asm implementation */ + +# ifdef STACK_NOT_SCANNED + void GC_push_regs(void) + { + /* empty */ + } +# define HAVE_PUSH_REGS + +# elif defined(M68K) && defined(AMIGA) + /* This function is not static because it could also be */ + /* erroneously defined in .S file, so this error would be caught */ + /* by the linker. */ + void GC_push_regs(void) + { + /* AMIGA - could be replaced by generic code */ + /* a0, a1, d0 and d1 are caller save */ + +# ifdef __GNUC__ + asm("subq.w &0x4,%sp"); /* allocate word on top of stack */ + + asm("mov.l %a2,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a3,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a4,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a5,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a6,(%sp)"); asm("jsr _GC_push_one"); + /* Skip frame pointer and stack pointer */ + asm("mov.l %d2,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d3,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d4,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d5,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d6,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d7,(%sp)"); asm("jsr _GC_push_one"); + + asm("addq.w &0x4,%sp"); /* put stack back where it was */ +# else /* !__GNUC__ */ + GC_push_one(getreg(REG_A2)); + GC_push_one(getreg(REG_A3)); +# ifndef __SASC + /* Can probably be changed to #if 0 -Kjetil M. (a4=globals) */ + GC_push_one(getreg(REG_A4)); +# endif + GC_push_one(getreg(REG_A5)); + GC_push_one(getreg(REG_A6)); + /* Skip stack pointer */ + GC_push_one(getreg(REG_D2)); + GC_push_one(getreg(REG_D3)); + GC_push_one(getreg(REG_D4)); + GC_push_one(getreg(REG_D5)); + GC_push_one(getreg(REG_D6)); + GC_push_one(getreg(REG_D7)); +# endif /* !__GNUC__ */ + } +# define HAVE_PUSH_REGS + +# elif defined(MACOS) + +# if defined(M68K) && defined(THINK_C) && !defined(CPPCHECK) +# define PushMacReg(reg) \ + move.l reg,(sp) \ + jsr GC_push_one + void GC_push_regs(void) + { + asm { + sub.w #4,sp ; reserve space for one parameter. + PushMacReg(a2); + PushMacReg(a3); + PushMacReg(a4); + ; skip a5 (globals), a6 (frame pointer), and a7 (stack pointer) + PushMacReg(d2); + PushMacReg(d3); + PushMacReg(d4); + PushMacReg(d5); + PushMacReg(d6); + PushMacReg(d7); + add.w #4,sp ; fix stack. + } + } +# define HAVE_PUSH_REGS +# undef PushMacReg +# elif defined(__MWERKS__) + void GC_push_regs(void) + { + PushMacRegisters(); + } +# define HAVE_PUSH_REGS +# endif /* __MWERKS__ */ +# endif /* MACOS */ + +#endif /* !USE_ASM_PUSH_REGS */ + +#if defined(HAVE_PUSH_REGS) && defined(THREADS) +# error GC_push_regs cannot be used with threads + /* Would fail for GC_do_blocking. There are probably other safety */ + /* issues. */ +# undef HAVE_PUSH_REGS +#endif + +#if !defined(HAVE_PUSH_REGS) && defined(UNIX_LIKE) +# include +# ifndef NO_GETCONTEXT +# if defined(DARWIN) \ + && (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 /*MAC_OS_X_VERSION_10_6*/) +# include +# else +# include +# endif /* !DARWIN */ +# ifdef GETCONTEXT_FPU_EXCMASK_BUG +# include +# endif +# endif +#endif /* !HAVE_PUSH_REGS */ + +/* Ensure that either registers are pushed, or callee-save registers */ +/* are somewhere on the stack, and then call fn(arg, ctxt). */ +/* ctxt is either a pointer to a ucontext_t we generated, or NULL. */ +/* Could be called with or w/o the allocator lock held; could be called */ +/* from a signal handler as well. */ +GC_ATTR_NO_SANITIZE_ADDR +GC_INNER void GC_with_callee_saves_pushed(GC_with_callee_saves_func fn, + ptr_t arg) +{ + volatile int dummy; + volatile ptr_t context = 0; +# if defined(EMSCRIPTEN) || defined(HAVE_BUILTIN_UNWIND_INIT) \ + || defined(HAVE_PUSH_REGS) || (defined(NO_CRT) && defined(MSWIN32)) \ + || !defined(NO_UNDERSCORE_SETJMP) +# define volatile_arg arg +# else + volatile ptr_t volatile_arg = arg; + /* To avoid 'arg might be clobbered by setjmp' */ + /* warning produced by some compilers. */ +# endif + +# if defined(HAVE_PUSH_REGS) + GC_push_regs(); +# elif defined(EMSCRIPTEN) + /* No-op, "registers" are pushed in GC_push_other_roots(). */ +# else +# if defined(UNIX_LIKE) && !defined(NO_GETCONTEXT) + /* Older versions of Darwin seem to lack getcontext(). */ + /* ARM and MIPS Linux often doesn't support a real */ + /* getcontext(). */ + static signed char getcontext_works = 0; /* (-1) - broken, 1 - works */ + ucontext_t ctxt; +# ifdef GETCONTEXT_FPU_EXCMASK_BUG + /* Workaround a bug (clearing the FPU exception mask) in */ + /* getcontext on Linux/x64. */ +# ifdef X86_64 + /* We manipulate FPU control word here just not to force the */ + /* client application to use -lm linker option. */ + unsigned short old_fcw; + +# if defined(CPPCHECK) + GC_noop1((word)&old_fcw); +# endif + __asm__ __volatile__ ("fstcw %0" : "=m" (*&old_fcw)); +# else + int except_mask = fegetexcept(); +# endif +# endif + + if (getcontext_works >= 0) { + if (getcontext(&ctxt) < 0) { + WARN("getcontext failed:" + " using another register retrieval method...\n", 0); + /* getcontext() is broken, do not try again. */ + /* E.g., to workaround a bug in Docker ubuntu_32bit. */ + } else { + context = (ptr_t)&ctxt; + } + if (EXPECT(0 == getcontext_works, FALSE)) + getcontext_works = context != NULL ? 1 : -1; + } +# ifdef GETCONTEXT_FPU_EXCMASK_BUG +# ifdef X86_64 + __asm__ __volatile__ ("fldcw %0" : : "m" (*&old_fcw)); + { + unsigned mxcsr; + /* And now correct the exception mask in SSE MXCSR. */ + __asm__ __volatile__ ("stmxcsr %0" : "=m" (*&mxcsr)); + mxcsr = (mxcsr & ~(FE_ALL_EXCEPT << 7)) | + ((old_fcw & FE_ALL_EXCEPT) << 7); + __asm__ __volatile__ ("ldmxcsr %0" : : "m" (*&mxcsr)); + } +# else /* !X86_64 */ + if (feenableexcept(except_mask) < 0) + ABORT("feenableexcept failed"); +# endif +# endif /* GETCONTEXT_FPU_EXCMASK_BUG */ +# if defined(IA64) || defined(SPARC) + /* On a register window machine, we need to save register */ + /* contents on the stack for this to work. This may already be */ + /* subsumed by the getcontext() call. */ +# if defined(IA64) && !defined(THREADS) + GC_save_regs_ret_val = +# endif + GC_save_regs_in_stack(); +# endif + if (NULL == context) /* getcontext failed */ +# endif /* !NO_GETCONTEXT */ + { +# if defined(HAVE_BUILTIN_UNWIND_INIT) + /* This was suggested by Richard Henderson as the way to */ + /* force callee-save registers and register windows onto */ + /* the stack. */ + __builtin_unwind_init(); +# elif defined(NO_CRT) && defined(MSWIN32) + CONTEXT ctx; + RtlCaptureContext(&ctx); +# else + /* Generic code */ + /* The idea is due to Parag Patel at HP. */ + /* We're not sure whether he would like */ + /* to be acknowledged for it or not. */ + jmp_buf regs; + word *i = (word *)®s[0]; + ptr_t lim = (ptr_t)(®s[0]) + sizeof(regs); + + /* setjmp doesn't always clear all of the buffer. */ + /* That tends to preserve garbage. Clear it. */ + for (; (word)i < (word)lim; i++) { + *i = 0; + } +# ifdef NO_UNDERSCORE_SETJMP + (void)setjmp(regs); +# else + (void) _setjmp(regs); + /* We don't want to mess with signals. According to */ + /* SUSV3, setjmp() may or may not save signal mask. */ + /* _setjmp won't, but is less portable. */ +# endif +# endif /* !HAVE_BUILTIN_UNWIND_INIT */ + } +# endif /* !HAVE_PUSH_REGS */ + /* TODO: context here is sometimes just zero. At the moment, the */ + /* callees don't really need it. */ + /* Cast fn to a volatile type to prevent call inlining. */ + (*(GC_with_callee_saves_func volatile *)&fn)(volatile_arg, + (/* no volatile */ void *)(word)context); + /* Strongly discourage the compiler from treating the above */ + /* as a tail-call, since that would pop the register */ + /* contents before we get a chance to look at them. */ + GC_noop1(COVERT_DATAFLOW(&dummy)); +# undef volatile_arg +} + +#endif /* !PLATFORM_MACH_DEP && !SN_TARGET_PSP2 */ + +#endif +#if !defined(PLATFORM_STOP_WORLD) +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +#ifdef PTHREAD_STOP_WORLD_IMPL + +#ifdef NACL +# include +#else +# include +# include +# include +# include /* for nanosleep() */ +#endif /* !NACL */ + +#ifdef E2K +# include +#endif + +GC_INLINE void GC_usleep(unsigned us) +{ +# if defined(LINT2) || defined(THREAD_SANITIZER) + /* Workaround "waiting while holding a lock" static analyzer warning. */ + /* Workaround a rare hang in usleep() trying to acquire TSan Lock. */ + while (us-- > 0) + sched_yield(); /* pretending it takes 1us */ +# elif defined(CPPCHECK) /* || _POSIX_C_SOURCE >= 199309L */ + struct timespec ts; + + ts.tv_sec = 0; + ts.tv_nsec = (unsigned32)us * 1000; + /* This requires _POSIX_TIMERS feature. */ + (void)nanosleep(&ts, NULL); +# else + usleep(us); +# endif +} + +#ifdef NACL + + STATIC int GC_nacl_num_gc_threads = 0; + STATIC volatile int GC_nacl_park_threads_now = 0; + STATIC volatile pthread_t GC_nacl_thread_parker = -1; + + STATIC __thread int GC_nacl_thread_idx = -1; + + STATIC __thread GC_thread GC_nacl_gc_thread_self = NULL; + /* TODO: Use GC_get_tlfs() instead. */ + + volatile int GC_nacl_thread_parked[MAX_NACL_GC_THREADS]; + int GC_nacl_thread_used[MAX_NACL_GC_THREADS]; + +#else + +#if (!defined(AO_HAVE_load_acquire) || !defined(AO_HAVE_store_release)) \ + && !defined(CPPCHECK) +# error AO_load_acquire and/or AO_store_release are missing; +# error please define AO_REQUIRE_CAS manually +#endif + +#ifdef DEBUG_THREADS + /* It's safe to call original pthread_sigmask() here. */ +# undef pthread_sigmask + +# ifndef NSIG +# ifdef CPPCHECK +# define NSIG 32 +# elif defined(MAXSIG) +# define NSIG (MAXSIG+1) +# elif defined(_NSIG) +# define NSIG _NSIG +# elif defined(__SIGRTMAX) +# define NSIG (__SIGRTMAX+1) +# else +# error define NSIG +# endif +# endif /* !NSIG */ + + void GC_print_sig_mask(void) + { + sigset_t blocked; + int i; + + if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0) + ABORT("pthread_sigmask failed"); + for (i = 1; i < NSIG; i++) { + if (sigismember(&blocked, i)) + GC_printf("Signal blocked: %d\n", i); + } + } +#endif /* DEBUG_THREADS */ + +/* Remove the signals that we want to allow in thread stopping */ +/* handler from a set. */ +STATIC void GC_remove_allowed_signals(sigset_t *set) +{ + if (sigdelset(set, SIGINT) != 0 + || sigdelset(set, SIGQUIT) != 0 + || sigdelset(set, SIGABRT) != 0 + || sigdelset(set, SIGTERM) != 0) { + ABORT("sigdelset failed"); + } + +# ifdef MPROTECT_VDB + /* Handlers write to the thread structure, which is in the heap, */ + /* and hence can trigger a protection fault. */ + if (sigdelset(set, SIGSEGV) != 0 +# ifdef HAVE_SIGBUS + || sigdelset(set, SIGBUS) != 0 +# endif + ) { + ABORT("sigdelset failed"); + } +# endif +} + +static sigset_t suspend_handler_mask; + +#define THREAD_RESTARTED 0x1 + +STATIC volatile AO_t GC_stop_count; + /* Incremented (to the nearest even value) at */ + /* the beginning of GC_stop_world() (or when */ + /* a thread is requested to be suspended by */ + /* GC_suspend_thread) and once more (to an odd */ + /* value) at the beginning of GC_start_world(). */ + /* The lowest bit is THREAD_RESTARTED one */ + /* which, if set, means it is safe for threads */ + /* to restart, i.e. they will see another */ + /* suspend signal before they are expected to */ + /* stop (unless they have stopped voluntarily). */ + +STATIC GC_bool GC_retry_signals = FALSE; + +/* + * We use signals to stop threads during GC. + * + * Suspended threads wait in signal handler for SIG_THR_RESTART. + * That's more portable than semaphores or condition variables. + * (We do use sem_post from a signal handler, but that should be portable.) + * + * The thread suspension signal SIG_SUSPEND is now defined in gc_priv.h. + * Note that we can't just stop a thread; we need it to save its stack + * pointer(s) and acknowledge. + */ +#ifndef SIG_THR_RESTART +# ifdef SUSPEND_HANDLER_NO_CONTEXT + /* Reuse the suspend signal. */ +# define SIG_THR_RESTART SIG_SUSPEND +# elif defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS) \ + || defined(GC_NETBSD_THREADS) || defined(GC_USESIGRT_SIGNALS) +# if defined(_SIGRTMIN) && !defined(CPPCHECK) +# define SIG_THR_RESTART _SIGRTMIN + 5 +# else +# define SIG_THR_RESTART SIGRTMIN + 5 +# endif +# elif defined(GC_FREEBSD_THREADS) && defined(__GLIBC__) +# define SIG_THR_RESTART (32+5) +# elif defined(GC_FREEBSD_THREADS) || defined(HURD) || defined(RTEMS) +# define SIG_THR_RESTART SIGUSR2 +# else +# define SIG_THR_RESTART SIGXCPU +# endif +#endif /* !SIG_THR_RESTART */ + +#define SIGNAL_UNSET (-1) + /* Since SIG_SUSPEND and/or SIG_THR_RESTART could represent */ + /* a non-constant expression (e.g., in case of SIGRTMIN), */ + /* actual signal numbers are determined by GC_stop_init() */ + /* unless manually set (before GC initialization). */ + /* Might be set to the same signal number. */ +STATIC int GC_sig_suspend = SIGNAL_UNSET; +STATIC int GC_sig_thr_restart = SIGNAL_UNSET; + +GC_API void GC_CALL GC_set_suspend_signal(int sig) +{ + if (GC_is_initialized) return; + + GC_sig_suspend = sig; +} + +GC_API void GC_CALL GC_set_thr_restart_signal(int sig) +{ + if (GC_is_initialized) return; + + GC_sig_thr_restart = sig; +} + +GC_API int GC_CALL GC_get_suspend_signal(void) +{ + return GC_sig_suspend != SIGNAL_UNSET ? GC_sig_suspend : SIG_SUSPEND; +} + +GC_API int GC_CALL GC_get_thr_restart_signal(void) +{ + return GC_sig_thr_restart != SIGNAL_UNSET + ? GC_sig_thr_restart : SIG_THR_RESTART; +} + +#ifdef BASE_ATOMIC_OPS_EMULATED + /* The AO primitives emulated with locks cannot be used inside signal */ + /* handlers as this could cause a deadlock or a double lock. */ + /* The following "async" macro definitions are correct only for */ + /* an uniprocessor case and are provided for a test purpose. */ +# define ao_load_acquire_async(p) (*(p)) +# define ao_load_async(p) ao_load_acquire_async(p) +# define ao_store_release_async(p, v) (void)(*(p) = (v)) +# define ao_store_async(p, v) ao_store_release_async(p, v) +#else +# define ao_load_acquire_async(p) AO_load_acquire(p) +# define ao_load_async(p) AO_load(p) +# define ao_store_release_async(p, v) AO_store_release(p, v) +# define ao_store_async(p, v) AO_store(p, v) +#endif /* !BASE_ATOMIC_OPS_EMULATED */ + +STATIC sem_t GC_suspend_ack_sem; /* also used to acknowledge restart */ + +STATIC void GC_suspend_handler_inner(ptr_t dummy, void *context); + +#ifdef SUSPEND_HANDLER_NO_CONTEXT + STATIC void GC_suspend_handler(int sig) +#else + STATIC void GC_suspend_sigaction(int sig, siginfo_t *info, void *context) +#endif +{ + int old_errno = errno; + + if (sig != GC_sig_suspend) { +# if defined(GC_FREEBSD_THREADS) + /* Workaround "deferred signal handling" bug in FreeBSD 9.2. */ + if (0 == sig) return; +# endif + ABORT("Bad signal in suspend_handler"); + } + +# ifdef SUSPEND_HANDLER_NO_CONTEXT + /* A quick check if the signal is called to restart the world. */ + if ((ao_load_async(&GC_stop_count) & THREAD_RESTARTED) != 0) + return; + GC_with_callee_saves_pushed(GC_suspend_handler_inner, NULL); +# else + UNUSED_ARG(info); + /* We believe that in this case the full context is already */ + /* in the signal handler frame. */ + GC_suspend_handler_inner(NULL, context); +# endif + errno = old_errno; +} + +/* The lookup here is safe, since this is done on behalf */ +/* of a thread which holds the allocator lock in order */ +/* to stop the world. Thus concurrent modification of the */ +/* data structure is impossible. Unfortunately, we have to */ +/* instruct TSan that the lookup is safe. */ +#ifdef THREAD_SANITIZER + /* Almost same as GC_self_thread_inner() except for the */ + /* no-sanitize attribute added and the result is never NULL. */ + GC_ATTR_NO_SANITIZE_THREAD + static GC_thread GC_lookup_self_thread_async(void) + { + thread_id_t self_id = thread_id_self(); + GC_thread p = GC_threads[THREAD_TABLE_INDEX(self_id)]; + + for (;; p = p -> tm.next) { + if (THREAD_EQUAL(p -> id, self_id)) break; + } + return p; + } +#else +# define GC_lookup_self_thread_async() GC_self_thread_inner() +#endif + +GC_INLINE void GC_store_stack_ptr(GC_stack_context_t crtn) +{ + /* There is no data race between the suspend handler (storing */ + /* stack_ptr) and GC_push_all_stacks (fetching stack_ptr) because */ + /* GC_push_all_stacks is executed after GC_stop_world exits and the */ + /* latter runs sem_wait repeatedly waiting for all the suspended */ + /* threads to call sem_post. Nonetheless, stack_ptr is stored (here) */ + /* and fetched (by GC_push_all_stacks) using the atomic primitives to */ + /* avoid the related TSan warning. */ +# ifdef SPARC + ao_store_async((volatile AO_t *)&(crtn -> stack_ptr), + (AO_t)GC_save_regs_in_stack()); + /* TODO: regs saving already done by GC_with_callee_saves_pushed */ +# else +# ifdef IA64 + crtn -> backing_store_ptr = GC_save_regs_in_stack(); +# endif + ao_store_async((volatile AO_t *)&(crtn -> stack_ptr), + (AO_t)GC_approx_sp()); +# endif +} + +STATIC void GC_suspend_handler_inner(ptr_t dummy, void *context) +{ + GC_thread me; + GC_stack_context_t crtn; +# ifdef E2K + ptr_t bs_lo; + size_t stack_size; +# endif + IF_CANCEL(int cancel_state;) +# ifdef GC_ENABLE_SUSPEND_THREAD + word suspend_cnt; +# endif + AO_t my_stop_count = ao_load_acquire_async(&GC_stop_count); + /* After the barrier, this thread should see */ + /* the actual content of GC_threads. */ + + UNUSED_ARG(dummy); + UNUSED_ARG(context); + if ((my_stop_count & THREAD_RESTARTED) != 0) + return; /* Restarting the world. */ + + DISABLE_CANCEL(cancel_state); + /* pthread_setcancelstate is not defined to be async-signal-safe. */ + /* But the glibc version appears to be in the absence of */ + /* asynchronous cancellation. And since this signal handler */ + /* to block on sigsuspend, which is both async-signal-safe */ + /* and a cancellation point, there seems to be no obvious way */ + /* out of it. In fact, it looks to me like an async-signal-safe */ + /* cancellation point is inherently a problem, unless there is */ + /* some way to disable cancellation in the handler. */ + +# ifdef DEBUG_THREADS + GC_log_printf("Suspending %p\n", (void *)pthread_self()); +# endif + me = GC_lookup_self_thread_async(); + if ((me -> last_stop_count & ~(word)THREAD_RESTARTED) == my_stop_count) { + /* Duplicate signal. OK if we are retrying. */ + if (!GC_retry_signals) { + WARN("Duplicate suspend signal in thread %p\n", pthread_self()); + } + RESTORE_CANCEL(cancel_state); + return; + } + crtn = me -> crtn; + GC_store_stack_ptr(crtn); +# ifdef E2K + GC_ASSERT(NULL == crtn -> backing_store_end); + GET_PROCEDURE_STACK_LOCAL(crtn -> ps_ofs, &bs_lo, &stack_size); + crtn -> backing_store_end = bs_lo; + crtn -> backing_store_ptr = bs_lo + stack_size; +# endif +# ifdef GC_ENABLE_SUSPEND_THREAD + suspend_cnt = (word)ao_load_async(&(me -> ext_suspend_cnt)); +# endif + + /* Tell the thread that wants to stop the world that this */ + /* thread has been stopped. Note that sem_post() is */ + /* the only async-signal-safe primitive in LinuxThreads. */ + sem_post(&GC_suspend_ack_sem); + ao_store_release_async(&(me -> last_stop_count), my_stop_count); + + /* Wait until that thread tells us to restart by sending */ + /* this thread a GC_sig_thr_restart signal (should be masked */ + /* at this point thus there is no race). */ + /* We do not continue until we receive that signal, */ + /* but we do not take that as authoritative. (We may be */ + /* accidentally restarted by one of the user signals we */ + /* don't block.) After we receive the signal, we use a */ + /* primitive and expensive mechanism to wait until it's */ + /* really safe to proceed. Under normal circumstances, */ + /* this code should not be executed. */ + do { + sigsuspend(&suspend_handler_mask); + /* Iterate while not restarting the world or thread is suspended. */ + } while (ao_load_acquire_async(&GC_stop_count) == my_stop_count +# ifdef GC_ENABLE_SUSPEND_THREAD + || ((suspend_cnt & 1) != 0 + && (word)ao_load_async(&(me -> ext_suspend_cnt)) + == suspend_cnt) +# endif + ); + +# ifdef DEBUG_THREADS + GC_log_printf("Resuming %p\n", (void *)pthread_self()); +# endif +# ifdef E2K + GC_ASSERT(crtn -> backing_store_end == bs_lo); + crtn -> backing_store_ptr = NULL; + crtn -> backing_store_end = NULL; +# endif + +# ifndef GC_NETBSD_THREADS_WORKAROUND + if (GC_retry_signals || GC_sig_suspend == GC_sig_thr_restart) +# endif + { + /* If the RESTART signal loss is possible (though it should be */ + /* less likely than losing the SUSPEND signal as we do not do */ + /* much between the first sem_post and sigsuspend calls), more */ + /* handshaking is provided to work around it. */ + sem_post(&GC_suspend_ack_sem); + /* Set the flag that the thread has been restarted. */ + if (GC_retry_signals) + ao_store_release_async(&(me -> last_stop_count), + my_stop_count | THREAD_RESTARTED); + } + RESTORE_CANCEL(cancel_state); +} + +static void suspend_restart_barrier(int n_live_threads) +{ + int i; + + for (i = 0; i < n_live_threads; i++) { + while (0 != sem_wait(&GC_suspend_ack_sem)) { + /* On Linux, sem_wait is documented to always return zero. */ + /* But the documentation appears to be incorrect. */ + /* EINTR seems to happen with some versions of gdb. */ + if (errno != EINTR) + ABORT("sem_wait failed"); + } + } +# ifdef GC_ASSERTIONS + sem_getvalue(&GC_suspend_ack_sem, &i); + GC_ASSERT(0 == i); +# endif +} + +# define WAIT_UNIT 3000 /* us */ + +static int resend_lost_signals(int n_live_threads, + int (*suspend_restart_all)(void)) +{ +# define RETRY_INTERVAL 100000 /* us */ +# define RESEND_SIGNALS_LIMIT 150 + + if (n_live_threads > 0) { + unsigned long wait_usecs = 0; /* Total wait since retry. */ + int retry = 0; + int prev_sent = 0; + + for (;;) { + int ack_count; + + sem_getvalue(&GC_suspend_ack_sem, &ack_count); + if (ack_count == n_live_threads) + break; + if (wait_usecs > RETRY_INTERVAL) { + int newly_sent = suspend_restart_all(); + + if (newly_sent != prev_sent) { + retry = 0; /* restart the counter */ + } else if (++retry >= RESEND_SIGNALS_LIMIT) /* no progress */ + ABORT_ARG1("Signals delivery fails constantly", + " at GC #%lu", (unsigned long)GC_gc_no); + + GC_COND_LOG_PRINTF("Resent %d signals after timeout, retry: %d\n", + newly_sent, retry); + sem_getvalue(&GC_suspend_ack_sem, &ack_count); + if (newly_sent < n_live_threads - ack_count) { + WARN("Lost some threads while stopping or starting world?!\n", 0); + n_live_threads = ack_count + newly_sent; + } + prev_sent = newly_sent; + wait_usecs = 0; + } + GC_usleep(WAIT_UNIT); + wait_usecs += WAIT_UNIT; + } + } + return n_live_threads; +} + +#ifdef HAVE_CLOCK_GETTIME +# define TS_NSEC_ADD(ts, ns) \ + (ts.tv_nsec += (ns), \ + (void)(ts.tv_nsec >= 1000000L*1000 ? \ + (ts.tv_nsec -= 1000000L*1000, ts.tv_sec++, 0) : 0)) +#endif + +static void resend_lost_signals_retry(int n_live_threads, + int (*suspend_restart_all)(void)) +{ +# if defined(HAVE_CLOCK_GETTIME) && !defined(DONT_TIMEDWAIT_ACK_SEM) +# define TIMEOUT_BEFORE_RESEND 10000 /* us */ + struct timespec ts; + + if (n_live_threads > 0 && clock_gettime(CLOCK_REALTIME, &ts) == 0) { + int i; + + TS_NSEC_ADD(ts, TIMEOUT_BEFORE_RESEND * (unsigned32)1000); + /* First, try to wait for the semaphore with some timeout. */ + /* On failure, fallback to WAIT_UNIT pause and resend of the signal. */ + for (i = 0; i < n_live_threads; i++) { + if (0 != sem_timedwait(&GC_suspend_ack_sem, &ts)) + break; /* Wait timed out or any other error. */ + } + /* Update the count of threads to wait the ack from. */ + n_live_threads -= i; + } +# endif + n_live_threads = resend_lost_signals(n_live_threads, suspend_restart_all); + suspend_restart_barrier(n_live_threads); +} + +STATIC void GC_restart_handler(int sig) +{ +# if defined(DEBUG_THREADS) + int old_errno = errno; /* Preserve errno value. */ +# endif + + if (sig != GC_sig_thr_restart) + ABORT("Bad signal in restart handler"); + + /* Note: even if we do not do anything useful here, it would still */ + /* be necessary to have a signal handler, rather than ignoring the */ + /* signals, otherwise the signals will not be delivered at all, */ + /* and will thus not interrupt the sigsuspend() above. */ +# ifdef DEBUG_THREADS + GC_log_printf("In GC_restart_handler for %p\n", (void *)pthread_self()); + errno = old_errno; +# endif +} + +# ifdef USE_TKILL_ON_ANDROID + EXTERN_C_BEGIN + extern int tkill(pid_t tid, int sig); /* from sys/linux-unistd.h */ + EXTERN_C_END +# define THREAD_SYSTEM_ID(t) (t)->kernel_id +# else +# define THREAD_SYSTEM_ID(t) (t)->id +# endif + +# ifndef RETRY_TKILL_EAGAIN_LIMIT +# define RETRY_TKILL_EAGAIN_LIMIT 16 +# endif + + static int raise_signal(GC_thread p, int sig) + { + int res; +# ifdef RETRY_TKILL_ON_EAGAIN + int retry; +# endif +# if defined(SIMULATE_LOST_SIGNALS) && !defined(GC_ENABLE_SUSPEND_THREAD) +# ifndef LOST_SIGNALS_RATIO +# define LOST_SIGNALS_RATIO 25 +# endif + static int signal_cnt; /* race is OK, it is for test purpose only */ + + if (GC_retry_signals && (++signal_cnt) % LOST_SIGNALS_RATIO == 0) + return 0; /* simulate the signal is sent but lost */ +# endif +# ifdef RETRY_TKILL_ON_EAGAIN + for (retry = 0; ; retry++) +# endif + { +# ifdef USE_TKILL_ON_ANDROID + int old_errno = errno; + + res = tkill(THREAD_SYSTEM_ID(p), sig); + if (res < 0) { + res = errno; + errno = old_errno; + } +# else + res = pthread_kill(THREAD_SYSTEM_ID(p), sig); +# endif +# ifdef RETRY_TKILL_ON_EAGAIN + if (res != EAGAIN || retry >= RETRY_TKILL_EAGAIN_LIMIT) break; + /* A temporal overflow of the real-time signal queue. */ + GC_usleep(WAIT_UNIT); +# endif + } + return res; + } + +# ifdef GC_ENABLE_SUSPEND_THREAD +# include + + + STATIC void GC_brief_async_signal_safe_sleep(void) + { + struct timeval tv; + tv.tv_sec = 0; +# if defined(GC_TIME_LIMIT) && !defined(CPPCHECK) + tv.tv_usec = 1000 * GC_TIME_LIMIT / 2; +# else + tv.tv_usec = 1000 * 15 / 2; +# endif + (void)select(0, 0, 0, 0, &tv); + } + + GC_INNER void GC_suspend_self_inner(GC_thread me, word suspend_cnt) { + IF_CANCEL(int cancel_state;) + + GC_ASSERT((suspend_cnt & 1) != 0); + DISABLE_CANCEL(cancel_state); +# ifdef DEBUG_THREADS + GC_log_printf("Suspend self: %p\n", (void *)(me -> id)); +# endif + while ((word)ao_load_acquire_async(&(me -> ext_suspend_cnt)) + == suspend_cnt) { + /* TODO: Use sigsuspend() even for self-suspended threads. */ + GC_brief_async_signal_safe_sleep(); + } +# ifdef DEBUG_THREADS + GC_log_printf("Resume self: %p\n", (void *)(me -> id)); +# endif + RESTORE_CANCEL(cancel_state); + } + + GC_API void GC_CALL GC_suspend_thread(GC_SUSPEND_THREAD_ID thread) { + GC_thread t; + AO_t next_stop_count; + word suspend_cnt; + IF_CANCEL(int cancel_state;) + + LOCK(); + t = GC_lookup_by_pthread((pthread_t)thread); + if (NULL == t) { + UNLOCK(); + return; + } + suspend_cnt = (word)(t -> ext_suspend_cnt); + if ((suspend_cnt & 1) != 0) /* already suspended? */ { + GC_ASSERT(!THREAD_EQUAL((pthread_t)thread, pthread_self())); + UNLOCK(); + return; + } + if ((t -> flags & (FINISHED | DO_BLOCKING)) != 0) { + t -> ext_suspend_cnt = (AO_t)(suspend_cnt | 1); /* suspend */ + /* Terminated but not joined yet, or in do-blocking state. */ + UNLOCK(); + return; + } + + if (THREAD_EQUAL((pthread_t)thread, pthread_self())) { + t -> ext_suspend_cnt = (AO_t)(suspend_cnt | 1); + GC_with_callee_saves_pushed(GC_suspend_self_blocked, (ptr_t)t); + UNLOCK(); + return; + } + + DISABLE_CANCEL(cancel_state); + /* GC_suspend_thread is not a cancellation point. */ +# ifdef PARALLEL_MARK + /* Ensure we do not suspend a thread while it is rebuilding */ + /* a free list, otherwise such a deadlock is possible: */ + /* thread 1 is blocked in GC_wait_for_reclaim holding */ + /* the allocator lock, thread 2 is suspended in */ + /* GC_reclaim_generic invoked from GC_generic_malloc_many */ + /* (with GC_fl_builder_count > 0), and thread 3 is blocked */ + /* acquiring the allocator lock in GC_resume_thread. */ + if (GC_parallel) + GC_wait_for_reclaim(); +# endif + + if (GC_manual_vdb) { + /* See the relevant comment in GC_stop_world. */ + GC_acquire_dirty_lock(); + } + /* Else do not acquire the dirty lock as the write fault handler */ + /* might be trying to acquire it too, and the suspend handler */ + /* execution is deferred until the write fault handler completes. */ + + next_stop_count = GC_stop_count + THREAD_RESTARTED; + GC_ASSERT((next_stop_count & THREAD_RESTARTED) == 0); + AO_store(&GC_stop_count, next_stop_count); + + /* Set the flag making the change visible to the signal handler. */ + AO_store_release(&(t -> ext_suspend_cnt), (AO_t)(suspend_cnt | 1)); + + /* TODO: Support GC_retry_signals (not needed for TSan) */ + switch (raise_signal(t, GC_sig_suspend)) { + /* ESRCH cannot happen as terminated threads are handled above. */ + case 0: + break; + default: + ABORT("pthread_kill failed"); + } + + /* Wait for the thread to complete threads table lookup and */ + /* stack_ptr assignment. */ + GC_ASSERT(GC_thr_initialized); + suspend_restart_barrier(1); + if (GC_manual_vdb) + GC_release_dirty_lock(); + AO_store(&GC_stop_count, next_stop_count | THREAD_RESTARTED); + + RESTORE_CANCEL(cancel_state); + UNLOCK(); + } + + GC_API void GC_CALL GC_resume_thread(GC_SUSPEND_THREAD_ID thread) { + GC_thread t; + + LOCK(); + t = GC_lookup_by_pthread((pthread_t)thread); + if (t != NULL) { + word suspend_cnt = (word)(t -> ext_suspend_cnt); + + if ((suspend_cnt & 1) != 0) /* is suspended? */ { + GC_ASSERT((GC_stop_count & THREAD_RESTARTED) != 0); + /* Mark the thread as not suspended - it will be resumed shortly. */ + AO_store(&(t -> ext_suspend_cnt), (AO_t)(suspend_cnt + 1)); + + if ((t -> flags & (FINISHED | DO_BLOCKING)) == 0) { + int result = raise_signal(t, GC_sig_thr_restart); + + /* TODO: Support signal resending on GC_retry_signals */ + if (result != 0) + ABORT_ARG1("pthread_kill failed in GC_resume_thread", + ": errcode= %d", result); +# ifndef GC_NETBSD_THREADS_WORKAROUND + if (GC_retry_signals || GC_sig_suspend == GC_sig_thr_restart) +# endif + { + IF_CANCEL(int cancel_state;) + + DISABLE_CANCEL(cancel_state); + suspend_restart_barrier(1); + RESTORE_CANCEL(cancel_state); + } + } + } + } + UNLOCK(); + } + + GC_API int GC_CALL GC_is_thread_suspended(GC_SUSPEND_THREAD_ID thread) { + GC_thread t; + int is_suspended = 0; + + READER_LOCK(); + t = GC_lookup_by_pthread((pthread_t)thread); + if (t != NULL && (t -> ext_suspend_cnt & 1) != 0) + is_suspended = (int)TRUE; + READER_UNLOCK(); + return is_suspended; + } +# endif /* GC_ENABLE_SUSPEND_THREAD */ + +# undef ao_load_acquire_async +# undef ao_load_async +# undef ao_store_async +# undef ao_store_release_async +#endif /* !NACL */ + +/* Should do exactly the right thing if the world is stopped; should */ +/* not fail if it is not. */ +GC_INNER void GC_push_all_stacks(void) +{ + GC_bool found_me = FALSE; + size_t nthreads = 0; + int i; + GC_thread p; + ptr_t lo; /* stack top (sp) */ + ptr_t hi; /* bottom */ +# if defined(E2K) || defined(IA64) + /* We also need to scan the register backing store. */ + ptr_t bs_lo, bs_hi; +# endif + struct GC_traced_stack_sect_s *traced_stack_sect; + pthread_t self = pthread_self(); + word total_size = 0; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_thr_initialized); +# ifdef DEBUG_THREADS + GC_log_printf("Pushing stacks from thread %p\n", (void *)self); +# endif + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != NULL; p = p -> tm.next) { +# if defined(E2K) || defined(IA64) + GC_bool is_self = FALSE; +# endif + GC_stack_context_t crtn = p -> crtn; + + GC_ASSERT(THREAD_TABLE_INDEX(p -> id) == i); + if (KNOWN_FINISHED(p)) continue; + ++nthreads; + traced_stack_sect = crtn -> traced_stack_sect; + if (THREAD_EQUAL(p -> id, self)) { + GC_ASSERT((p -> flags & DO_BLOCKING) == 0); +# ifdef SPARC + lo = GC_save_regs_in_stack(); +# else + lo = GC_approx_sp(); +# ifdef IA64 + bs_hi = GC_save_regs_in_stack(); +# elif defined(E2K) + { + size_t stack_size; + + GC_ASSERT(NULL == crtn -> backing_store_end); + GET_PROCEDURE_STACK_LOCAL(crtn -> ps_ofs, + &bs_lo, &stack_size); + bs_hi = bs_lo + stack_size; + } +# endif +# endif + found_me = TRUE; +# if defined(E2K) || defined(IA64) + is_self = TRUE; +# endif + } else { + lo = (ptr_t)AO_load((volatile AO_t *)&(crtn -> stack_ptr)); +# ifdef IA64 + bs_hi = crtn -> backing_store_ptr; +# elif defined(E2K) + bs_lo = crtn -> backing_store_end; + bs_hi = crtn -> backing_store_ptr; +# endif + if (traced_stack_sect != NULL + && traced_stack_sect -> saved_stack_ptr == lo) { + /* If the thread has never been stopped since the recent */ + /* GC_call_with_gc_active invocation then skip the top */ + /* "stack section" as stack_ptr already points to. */ + traced_stack_sect = traced_stack_sect -> prev; + } + } + hi = crtn -> stack_end; +# ifdef IA64 + bs_lo = crtn -> backing_store_end; +# endif +# ifdef DEBUG_THREADS +# ifdef STACK_GROWS_UP + GC_log_printf("Stack for thread %p is (%p,%p]\n", + (void *)(p -> id), (void *)hi, (void *)lo); +# else + GC_log_printf("Stack for thread %p is [%p,%p)\n", + (void *)(p -> id), (void *)lo, (void *)hi); +# endif +# endif + if (NULL == lo) ABORT("GC_push_all_stacks: sp not set!"); + if (crtn -> altstack != NULL && (word)(crtn -> altstack) <= (word)lo + && (word)lo <= (word)(crtn -> altstack) + crtn -> altstack_size) { +# ifdef STACK_GROWS_UP + hi = crtn -> altstack; +# else + hi = crtn -> altstack + crtn -> altstack_size; +# endif + /* FIXME: Need to scan the normal stack too, but how ? */ + } +# ifdef STACKPTR_CORRECTOR_AVAILABLE + if (GC_sp_corrector != 0) + GC_sp_corrector((void **)&lo, (void *)(p -> id)); +# endif + GC_push_all_stack_sections(lo, hi, traced_stack_sect); +# ifdef STACK_GROWS_UP + total_size += lo - hi; +# else + total_size += hi - lo; /* lo <= hi */ +# endif +# ifdef NACL + /* Push reg_storage as roots, this will cover the reg context. */ + GC_push_all_stack((ptr_t)p -> reg_storage, + (ptr_t)(p -> reg_storage + NACL_GC_REG_STORAGE_SIZE)); + total_size += NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t); +# endif +# ifdef E2K + if ((GC_stop_count & THREAD_RESTARTED) != 0 +# ifdef GC_ENABLE_SUSPEND_THREAD + && (p -> ext_suspend_cnt & 1) == 0 +# endif + && !is_self && (p -> flags & DO_BLOCKING) == 0) + continue; /* procedure stack buffer has already been freed */ +# endif +# if defined(E2K) || defined(IA64) +# ifdef DEBUG_THREADS + GC_log_printf("Reg stack for thread %p is [%p,%p)\n", + (void *)(p -> id), (void *)bs_lo, (void *)bs_hi); +# endif + GC_ASSERT(bs_lo != NULL && bs_hi != NULL); + /* FIXME: This (if is_self) may add an unbounded number of */ + /* entries, and hence overflow the mark stack, which is bad. */ +# ifdef IA64 + GC_push_all_register_sections(bs_lo, bs_hi, is_self, + traced_stack_sect); +# else + if (is_self) { + GC_push_all_eager(bs_lo, bs_hi); + } else { + GC_push_all_stack(bs_lo, bs_hi); + } +# endif + total_size += bs_hi - bs_lo; /* bs_lo <= bs_hi */ +# endif + } + } + GC_VERBOSE_LOG_PRINTF("Pushed %d thread stacks\n", (int)nthreads); + if (!found_me && !GC_in_thread_creation) + ABORT("Collecting from unknown thread"); + GC_total_stacksize = total_size; +} + +#ifdef DEBUG_THREADS + /* There seems to be a very rare thread stopping problem. To help us */ + /* debug that, we save the ids of the stopping thread. */ + pthread_t GC_stopping_thread; + int GC_stopping_pid = 0; +#endif + +/* Suspend all threads that might still be running. Return the number */ +/* of suspend signals that were sent. */ +STATIC int GC_suspend_all(void) +{ + int n_live_threads = 0; + int i; +# ifndef NACL + GC_thread p; + pthread_t self = pthread_self(); + int result; + + GC_ASSERT((GC_stop_count & THREAD_RESTARTED) == 0); + GC_ASSERT(I_HOLD_LOCK()); + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != NULL; p = p -> tm.next) { + if (!THREAD_EQUAL(p -> id, self)) { + if ((p -> flags & (FINISHED | DO_BLOCKING)) != 0) continue; +# ifdef GC_ENABLE_SUSPEND_THREAD + if ((p -> ext_suspend_cnt & 1) != 0) continue; +# endif + if (AO_load(&(p -> last_stop_count)) == GC_stop_count) + continue; /* matters only if GC_retry_signals */ + n_live_threads++; +# ifdef DEBUG_THREADS + GC_log_printf("Sending suspend signal to %p\n", (void *)p->id); +# endif + + /* The synchronization between GC_dirty (based on */ + /* test-and-set) and the signal-based thread suspension */ + /* is performed in GC_stop_world because */ + /* GC_release_dirty_lock cannot be called before */ + /* acknowledging the thread is really suspended. */ + result = raise_signal(p, GC_sig_suspend); + switch (result) { + case ESRCH: + /* Not really there anymore. Possible? */ + n_live_threads--; + break; + case 0: + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, + (void *)(word)THREAD_SYSTEM_ID(p)); + /* Note: thread id might be truncated. */ + break; + default: + ABORT_ARG1("pthread_kill failed at suspend", + ": errcode= %d", result); + } + } + } + } + +# else /* NACL */ +# ifndef NACL_PARK_WAIT_USEC +# define NACL_PARK_WAIT_USEC 100 /* us */ +# endif + unsigned long num_sleeps = 0; + + GC_ASSERT(I_HOLD_LOCK()); +# ifdef DEBUG_THREADS + GC_log_printf("pthread_stop_world: number of threads: %d\n", + GC_nacl_num_gc_threads - 1); +# endif + GC_nacl_thread_parker = pthread_self(); + GC_nacl_park_threads_now = 1; + + if (GC_manual_vdb) + GC_acquire_dirty_lock(); + for (;;) { + int num_threads_parked = 0; + int num_used = 0; + + /* Check the 'parked' flag for each thread the GC knows about. */ + for (i = 0; i < MAX_NACL_GC_THREADS + && num_used < GC_nacl_num_gc_threads; i++) { + if (GC_nacl_thread_used[i] == 1) { + num_used++; + if (GC_nacl_thread_parked[i] == 1) { + num_threads_parked++; + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, (void *)(word)i); + } + } + } + /* -1 for the current thread. */ + if (num_threads_parked >= GC_nacl_num_gc_threads - 1) + break; +# ifdef DEBUG_THREADS + GC_log_printf("Sleep waiting for %d threads to park...\n", + GC_nacl_num_gc_threads - num_threads_parked - 1); +# endif + GC_usleep(NACL_PARK_WAIT_USEC); + if (++num_sleeps > (1000 * 1000) / NACL_PARK_WAIT_USEC) { + WARN("GC appears stalled waiting for %" WARN_PRIdPTR + " threads to park...\n", + GC_nacl_num_gc_threads - num_threads_parked - 1); + num_sleeps = 0; + } + } + if (GC_manual_vdb) + GC_release_dirty_lock(); +# endif /* NACL */ + return n_live_threads; +} + +GC_INNER void GC_stop_world(void) +{ +# if !defined(NACL) + int n_live_threads; +# endif + GC_ASSERT(I_HOLD_LOCK()); + /* Make sure all free list construction has stopped before we start. */ + /* No new construction can start, since a free list construction is */ + /* required to acquire and release the allocator lock before start. */ + + GC_ASSERT(GC_thr_initialized); +# ifdef DEBUG_THREADS + GC_stopping_thread = pthread_self(); + GC_stopping_pid = getpid(); + GC_log_printf("Stopping the world from %p\n", (void *)GC_stopping_thread); +# endif +# ifdef PARALLEL_MARK + if (GC_parallel) { + GC_acquire_mark_lock(); + GC_ASSERT(GC_fl_builder_count == 0); + /* We should have previously waited for it to become zero. */ + } +# endif /* PARALLEL_MARK */ + +# if defined(NACL) + (void)GC_suspend_all(); +# else + AO_store(&GC_stop_count, GC_stop_count + THREAD_RESTARTED); + /* Only concurrent reads are possible. */ + if (GC_manual_vdb) { + GC_acquire_dirty_lock(); + /* The write fault handler cannot be called if GC_manual_vdb */ + /* (thus double-locking should not occur in */ + /* async_set_pht_entry_from_index based on test-and-set). */ + } + n_live_threads = GC_suspend_all(); + if (GC_retry_signals) { + resend_lost_signals_retry(n_live_threads, GC_suspend_all); + } else { + suspend_restart_barrier(n_live_threads); + } + if (GC_manual_vdb) + GC_release_dirty_lock(); /* cannot be done in GC_suspend_all */ +# endif + +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_release_mark_lock(); +# endif +# ifdef DEBUG_THREADS + GC_log_printf("World stopped from %p\n", (void *)pthread_self()); + GC_stopping_thread = 0; +# endif +} + +#ifdef NACL +# if defined(__x86_64__) +# define NACL_STORE_REGS() \ + do { \ + __asm__ __volatile__ ("push %rbx"); \ + __asm__ __volatile__ ("push %rbp"); \ + __asm__ __volatile__ ("push %r12"); \ + __asm__ __volatile__ ("push %r13"); \ + __asm__ __volatile__ ("push %r14"); \ + __asm__ __volatile__ ("push %r15"); \ + __asm__ __volatile__ ("mov %%esp, %0" \ + : "=m" (GC_nacl_gc_thread_self -> crtn -> stack_ptr)); \ + BCOPY(GC_nacl_gc_thread_self -> crtn -> stack_ptr, \ + GC_nacl_gc_thread_self -> reg_storage, \ + NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t)); \ + __asm__ __volatile__ ("naclasp $48, %r15"); \ + } while (0) +# elif defined(__i386__) +# define NACL_STORE_REGS() \ + do { \ + __asm__ __volatile__ ("push %ebx"); \ + __asm__ __volatile__ ("push %ebp"); \ + __asm__ __volatile__ ("push %esi"); \ + __asm__ __volatile__ ("push %edi"); \ + __asm__ __volatile__ ("mov %%esp, %0" \ + : "=m" (GC_nacl_gc_thread_self -> crtn -> stack_ptr)); \ + BCOPY(GC_nacl_gc_thread_self -> crtn -> stack_ptr, \ + GC_nacl_gc_thread_self -> reg_storage, \ + NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t));\ + __asm__ __volatile__ ("add $16, %esp"); \ + } while (0) +# elif defined(__arm__) +# define NACL_STORE_REGS() \ + do { \ + __asm__ __volatile__ ("push {r4-r8,r10-r12,lr}"); \ + __asm__ __volatile__ ("mov r0, %0" \ + : : "r" (&GC_nacl_gc_thread_self -> crtn -> stack_ptr)); \ + __asm__ __volatile__ ("bic r0, r0, #0xc0000000"); \ + __asm__ __volatile__ ("str sp, [r0]"); \ + BCOPY(GC_nacl_gc_thread_self -> crtn -> stack_ptr, \ + GC_nacl_gc_thread_self -> reg_storage, \ + NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t)); \ + __asm__ __volatile__ ("add sp, sp, #40"); \ + __asm__ __volatile__ ("bic sp, sp, #0xc0000000"); \ + } while (0) +# else +# error TODO Please port NACL_STORE_REGS +# endif + + GC_API_OSCALL void nacl_pre_syscall_hook(void) + { + if (GC_nacl_thread_idx != -1) { + NACL_STORE_REGS(); + GC_nacl_gc_thread_self -> crtn -> stack_ptr = GC_approx_sp(); + GC_nacl_thread_parked[GC_nacl_thread_idx] = 1; + } + } + + GC_API_OSCALL void __nacl_suspend_thread_if_needed(void) + { + if (!GC_nacl_park_threads_now) return; + + /* Don't try to park the thread parker. */ + if (GC_nacl_thread_parker == pthread_self()) return; + + /* This can happen when a thread is created outside of the GC */ + /* system (wthread mostly). */ + if (GC_nacl_thread_idx < 0) return; + + /* If it was already 'parked', we're returning from a syscall, */ + /* so don't bother storing registers again, the GC has a set. */ + if (!GC_nacl_thread_parked[GC_nacl_thread_idx]) { + NACL_STORE_REGS(); + GC_nacl_gc_thread_self -> crtn -> stack_ptr = GC_approx_sp(); + } + GC_nacl_thread_parked[GC_nacl_thread_idx] = 1; + while (GC_nacl_park_threads_now) { + /* Just spin. */ + } + GC_nacl_thread_parked[GC_nacl_thread_idx] = 0; + + /* Clear out the reg storage for next suspend. */ + BZERO(GC_nacl_gc_thread_self -> reg_storage, + NACL_GC_REG_STORAGE_SIZE * sizeof(ptr_t)); + } + + GC_API_OSCALL void nacl_post_syscall_hook(void) + { + /* Calling __nacl_suspend_thread_if_needed right away should */ + /* guarantee we don't mutate the GC set. */ + __nacl_suspend_thread_if_needed(); + if (GC_nacl_thread_idx != -1) { + GC_nacl_thread_parked[GC_nacl_thread_idx] = 0; + } + } + + STATIC GC_bool GC_nacl_thread_parking_inited = FALSE; + STATIC pthread_mutex_t GC_nacl_thread_alloc_lock = PTHREAD_MUTEX_INITIALIZER; + + struct nacl_irt_blockhook { + int (*register_block_hooks)(void (*pre)(void), void (*post)(void)); + }; + + EXTERN_C_BEGIN + extern size_t nacl_interface_query(const char *interface_ident, + void *table, size_t tablesize); + EXTERN_C_END + + GC_INNER void GC_nacl_initialize_gc_thread(GC_thread me) + { + int i; + static struct nacl_irt_blockhook gc_hook; + + GC_ASSERT(NULL == GC_nacl_gc_thread_self); + GC_nacl_gc_thread_self = me; + pthread_mutex_lock(&GC_nacl_thread_alloc_lock); + if (!EXPECT(GC_nacl_thread_parking_inited, TRUE)) { + BZERO(GC_nacl_thread_parked, sizeof(GC_nacl_thread_parked)); + BZERO(GC_nacl_thread_used, sizeof(GC_nacl_thread_used)); + /* TODO: replace with public 'register hook' function when */ + /* available from glibc. */ + nacl_interface_query("nacl-irt-blockhook-0.1", + &gc_hook, sizeof(gc_hook)); + gc_hook.register_block_hooks(nacl_pre_syscall_hook, + nacl_post_syscall_hook); + GC_nacl_thread_parking_inited = TRUE; + } + GC_ASSERT(GC_nacl_num_gc_threads <= MAX_NACL_GC_THREADS); + for (i = 0; i < MAX_NACL_GC_THREADS; i++) { + if (GC_nacl_thread_used[i] == 0) { + GC_nacl_thread_used[i] = 1; + GC_nacl_thread_idx = i; + GC_nacl_num_gc_threads++; + break; + } + } + pthread_mutex_unlock(&GC_nacl_thread_alloc_lock); + } + + GC_INNER void GC_nacl_shutdown_gc_thread(void) + { + GC_ASSERT(GC_nacl_gc_thread_self != NULL); + pthread_mutex_lock(&GC_nacl_thread_alloc_lock); + GC_ASSERT(GC_nacl_thread_idx >= 0); + GC_ASSERT(GC_nacl_thread_idx < MAX_NACL_GC_THREADS); + GC_ASSERT(GC_nacl_thread_used[GC_nacl_thread_idx] != 0); + GC_nacl_thread_used[GC_nacl_thread_idx] = 0; + GC_nacl_thread_idx = -1; + GC_nacl_num_gc_threads--; + pthread_mutex_unlock(&GC_nacl_thread_alloc_lock); + GC_nacl_gc_thread_self = NULL; + } + +#else /* !NACL */ + + /* Restart all threads that were suspended by the collector. */ + /* Return the number of restart signals that were sent. */ + STATIC int GC_restart_all(void) + { + int n_live_threads = 0; + int i; + pthread_t self = pthread_self(); + GC_thread p; + int result; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT((GC_stop_count & THREAD_RESTARTED) != 0); + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != NULL; p = p -> tm.next) { + if (!THREAD_EQUAL(p -> id, self)) { + if ((p -> flags & (FINISHED | DO_BLOCKING)) != 0) continue; +# ifdef GC_ENABLE_SUSPEND_THREAD + if ((p -> ext_suspend_cnt & 1) != 0) continue; +# endif + if (GC_retry_signals + && AO_load(&(p -> last_stop_count)) == GC_stop_count) + continue; /* The thread has been restarted. */ + n_live_threads++; +# ifdef DEBUG_THREADS + GC_log_printf("Sending restart signal to %p\n", (void *)p->id); +# endif + result = raise_signal(p, GC_sig_thr_restart); + switch (result) { + case ESRCH: + /* Not really there anymore. Possible? */ + n_live_threads--; + break; + case 0: + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, + (void *)(word)THREAD_SYSTEM_ID(p)); + break; + default: + ABORT_ARG1("pthread_kill failed at resume", + ": errcode= %d", result); + } + } + } + } + return n_live_threads; + } +#endif /* !NACL */ + +GC_INNER void GC_start_world(void) +{ +# ifndef NACL + int n_live_threads; + + GC_ASSERT(I_HOLD_LOCK()); /* held continuously since the world stopped */ +# ifdef DEBUG_THREADS + GC_log_printf("World starting\n"); +# endif + AO_store_release(&GC_stop_count, GC_stop_count + THREAD_RESTARTED); + /* The updated value should now be visible to the */ + /* signal handler (note that pthread_kill is not on */ + /* the list of functions which synchronize memory). */ + n_live_threads = GC_restart_all(); + if (GC_retry_signals) { + resend_lost_signals_retry(n_live_threads, GC_restart_all); + } else { +# ifndef GC_NETBSD_THREADS_WORKAROUND + if (GC_sig_suspend == GC_sig_thr_restart) +# endif + { + suspend_restart_barrier(n_live_threads); + } + } +# ifdef DEBUG_THREADS + GC_log_printf("World started\n"); +# endif +# else /* NACL */ +# ifdef DEBUG_THREADS + GC_log_printf("World starting...\n"); +# endif + GC_nacl_park_threads_now = 0; + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, NULL); + /* TODO: Send event for every unsuspended thread. */ +# endif +} + +GC_INNER void GC_stop_init(void) +{ +# if !defined(NACL) + struct sigaction act; + char *str; + + if (SIGNAL_UNSET == GC_sig_suspend) + GC_sig_suspend = SIG_SUSPEND; + if (SIGNAL_UNSET == GC_sig_thr_restart) + GC_sig_thr_restart = SIG_THR_RESTART; + + if (sem_init(&GC_suspend_ack_sem, GC_SEM_INIT_PSHARED, 0) != 0) + ABORT("sem_init failed"); + GC_stop_count = THREAD_RESTARTED; /* i.e. the world is not stopped */ + + if (sigfillset(&act.sa_mask) != 0) { + ABORT("sigfillset failed"); + } +# ifdef GC_RTEMS_PTHREADS + if(sigprocmask(SIG_UNBLOCK, &act.sa_mask, NULL) != 0) { + ABORT("sigprocmask failed"); + } +# endif + GC_remove_allowed_signals(&act.sa_mask); + /* GC_sig_thr_restart is set in the resulting mask. */ + /* It is unmasked by the handler when necessary. */ + +# ifdef SA_RESTART + act.sa_flags = SA_RESTART; +# else + act.sa_flags = 0; +# endif +# ifdef SUSPEND_HANDLER_NO_CONTEXT + act.sa_handler = GC_suspend_handler; +# else + act.sa_flags |= SA_SIGINFO; + act.sa_sigaction = GC_suspend_sigaction; +# endif + /* act.sa_restorer is deprecated and should not be initialized. */ + if (sigaction(GC_sig_suspend, &act, NULL) != 0) { + ABORT("Cannot set SIG_SUSPEND handler"); + } + + if (GC_sig_suspend != GC_sig_thr_restart) { +# ifndef SUSPEND_HANDLER_NO_CONTEXT + act.sa_flags &= ~SA_SIGINFO; +# endif + act.sa_handler = GC_restart_handler; + if (sigaction(GC_sig_thr_restart, &act, NULL) != 0) + ABORT("Cannot set SIG_THR_RESTART handler"); + } else { + GC_COND_LOG_PRINTF("Using same signal for suspend and restart\n"); + } + + /* Initialize suspend_handler_mask (excluding GC_sig_thr_restart). */ + if (sigfillset(&suspend_handler_mask) != 0) ABORT("sigfillset failed"); + GC_remove_allowed_signals(&suspend_handler_mask); + if (sigdelset(&suspend_handler_mask, GC_sig_thr_restart) != 0) + ABORT("sigdelset failed"); + +# ifndef NO_RETRY_SIGNALS + /* Any platform could lose signals, so let's be conservative and */ + /* always enable signals retry logic. */ + GC_retry_signals = TRUE; +# endif + /* Override the default value of GC_retry_signals. */ + str = GETENV("GC_RETRY_SIGNALS"); + if (str != NULL) { + GC_retry_signals = *str != '0' || *(str + 1) != '\0'; + /* Do not retry if the environment variable is set to "0". */ + } + if (GC_retry_signals) { + GC_COND_LOG_PRINTF( + "Will retry suspend and restart signals if necessary\n"); + } + +# ifndef NO_SIGNALS_UNBLOCK_IN_MAIN + /* Explicitly unblock the signals once before new threads creation. */ + GC_unblock_gc_signals(); +# endif +# endif /* !NACL */ +} + +#endif /* PTHREAD_STOP_WORLD_IMPL */ + +#endif +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2008 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +/* + * Support code originally for LinuxThreads, the clone()-based kernel + * thread package for Linux which is included in libc6. + * + * This code no doubt makes some assumptions beyond what is + * guaranteed by the pthread standard, though it now does + * very little of that. It now also supports NPTL, and many + * other Posix thread implementations. We are trying to merge + * all flavors of pthread support code into this file. + */ + +#ifdef THREADS + +#ifdef GC_PTHREADS +# if defined(GC_DARWIN_THREADS) \ + || (defined(GC_WIN32_THREADS) && defined(EMULATE_PTHREAD_SEMAPHORE)) +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_DARWIN_SEMAPHORE_H +#define GC_DARWIN_SEMAPHORE_H + + + +#if !defined(GC_DARWIN_THREADS) && !defined(GC_WIN32_THREADS) +# error darwin_semaphore.h included for improper target +#endif + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/* This is a very simple semaphore implementation based on pthreads. */ +/* It is not async-signal safe. But this is not a problem because */ +/* signals are not used to suspend threads on the target. */ + +typedef struct { + pthread_mutex_t mutex; + pthread_cond_t cond; + int value; +} sem_t; + +GC_INLINE int sem_init(sem_t *sem, int pshared, int value) { + if (pshared != 0) { + errno = EPERM; /* unsupported */ + return -1; + } + sem->value = value; + if (pthread_mutex_init(&sem->mutex, NULL) != 0) + return -1; + if (pthread_cond_init(&sem->cond, NULL) != 0) { + (void)pthread_mutex_destroy(&sem->mutex); + return -1; + } + return 0; +} + +GC_INLINE int sem_post(sem_t *sem) { + if (pthread_mutex_lock(&sem->mutex) != 0) + return -1; + sem->value++; + if (pthread_cond_signal(&sem->cond) != 0) { + (void)pthread_mutex_unlock(&sem->mutex); + return -1; + } + return pthread_mutex_unlock(&sem->mutex) != 0 ? -1 : 0; +} + +GC_INLINE int sem_wait(sem_t *sem) { + if (pthread_mutex_lock(&sem->mutex) != 0) + return -1; + while (sem->value == 0) { + if (pthread_cond_wait(&sem->cond, &sem->mutex) != 0) { + (void)pthread_mutex_unlock(&sem->mutex); + return -1; + } + } + sem->value--; + return pthread_mutex_unlock(&sem->mutex) != 0 ? -1 : 0; +} + +GC_INLINE int sem_destroy(sem_t *sem) { + return pthread_cond_destroy(&sem->cond) != 0 + || pthread_mutex_destroy(&sem->mutex) != 0 ? -1 : 0; +} + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif + +# elif !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) +# include +# endif +# include +#endif /* GC_PTHREADS */ + +#ifndef GC_WIN32_THREADS +# include +# include +# if !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) +# if !defined(GC_RTEMS_PTHREADS) +# include +# endif +# include +# include +# include +# endif +# if defined(GC_EXPLICIT_SIGNALS_UNBLOCK) || !defined(GC_NO_PTHREAD_SIGMASK) \ + || (defined(GC_PTHREADS_PARAMARK) && !defined(NO_MARKER_SPECIAL_SIGMASK)) +# include +# endif +#endif /* !GC_WIN32_THREADS */ + +#ifdef E2K +# include +#endif + +#if defined(GC_DARWIN_THREADS) || defined(GC_FREEBSD_THREADS) +# include +#endif + +#if defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_THREADS) +# include +# include +#endif + +#if defined(GC_DGUX386_THREADS) +# include +# include + /* sem_t is an uint in DG/UX */ + typedef unsigned int sem_t; +#endif /* GC_DGUX386_THREADS */ + +#if defined(GC_PTHREADS) \ + && !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) + /* Undefine macros used to redirect pthread primitives. */ +# undef pthread_create +# ifndef GC_NO_PTHREAD_SIGMASK +# undef pthread_sigmask +# endif +# ifndef GC_NO_PTHREAD_CANCEL +# undef pthread_cancel +# endif +# ifdef GC_HAVE_PTHREAD_EXIT +# undef pthread_exit +# endif +# undef pthread_join +# undef pthread_detach +# if defined(GC_OSF1_THREADS) && defined(_PTHREAD_USE_MANGLED_NAMES_) \ + && !defined(_PTHREAD_USE_PTDNAM_) + /* Restore the original mangled names on Tru64 UNIX. */ +# define pthread_create __pthread_create +# define pthread_join __pthread_join +# define pthread_detach __pthread_detach +# ifndef GC_NO_PTHREAD_CANCEL +# define pthread_cancel __pthread_cancel +# endif +# ifdef GC_HAVE_PTHREAD_EXIT +# define pthread_exit __pthread_exit +# endif +# endif /* GC_OSF1_THREADS */ +#endif /* GC_PTHREADS */ + +#if !defined(GC_WIN32_THREADS) \ + && !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) + /* TODO: Enable GC_USE_DLOPEN_WRAP for Cygwin? */ + +# ifdef GC_USE_LD_WRAP +# define WRAP_FUNC(f) __wrap_##f +# define REAL_FUNC(f) __real_##f + int REAL_FUNC(pthread_create)(pthread_t *, + GC_PTHREAD_CREATE_CONST pthread_attr_t *, + void * (*start_routine)(void *), void *); + int REAL_FUNC(pthread_join)(pthread_t, void **); + int REAL_FUNC(pthread_detach)(pthread_t); +# ifndef GC_NO_PTHREAD_SIGMASK + int REAL_FUNC(pthread_sigmask)(int, const sigset_t *, sigset_t *); +# endif +# ifndef GC_NO_PTHREAD_CANCEL + int REAL_FUNC(pthread_cancel)(pthread_t); +# endif +# ifdef GC_HAVE_PTHREAD_EXIT + void REAL_FUNC(pthread_exit)(void *) GC_PTHREAD_EXIT_ATTRIBUTE; +# endif +# elif defined(GC_USE_DLOPEN_WRAP) +# include +# define WRAP_FUNC(f) f +# define REAL_FUNC(f) GC_real_##f + /* We define both GC_f and plain f to be the wrapped function. */ + /* In that way plain calls work, as do calls from files that */ + /* included gc.h, which redefined f to GC_f. */ + /* FIXME: Needs work for DARWIN and True64 (OSF1) */ + typedef int (*GC_pthread_create_t)(pthread_t *, + GC_PTHREAD_CREATE_CONST pthread_attr_t *, + void * (*)(void *), void *); + static GC_pthread_create_t REAL_FUNC(pthread_create); +# ifndef GC_NO_PTHREAD_SIGMASK + typedef int (*GC_pthread_sigmask_t)(int, const sigset_t *, sigset_t *); + static GC_pthread_sigmask_t REAL_FUNC(pthread_sigmask); +# endif + typedef int (*GC_pthread_join_t)(pthread_t, void **); + static GC_pthread_join_t REAL_FUNC(pthread_join); + typedef int (*GC_pthread_detach_t)(pthread_t); + static GC_pthread_detach_t REAL_FUNC(pthread_detach); +# ifndef GC_NO_PTHREAD_CANCEL + typedef int (*GC_pthread_cancel_t)(pthread_t); + static GC_pthread_cancel_t REAL_FUNC(pthread_cancel); +# endif +# ifdef GC_HAVE_PTHREAD_EXIT + typedef void (*GC_pthread_exit_t)(void *) GC_PTHREAD_EXIT_ATTRIBUTE; + static GC_pthread_exit_t REAL_FUNC(pthread_exit); +# endif +# else +# define WRAP_FUNC(f) GC_##f +# ifdef GC_DGUX386_THREADS +# define REAL_FUNC(f) __d10_##f +# else +# define REAL_FUNC(f) f +# endif +# endif /* !GC_USE_LD_WRAP && !GC_USE_DLOPEN_WRAP */ + +# if defined(GC_USE_LD_WRAP) || defined(GC_USE_DLOPEN_WRAP) + /* Define GC_ functions as aliases for the plain ones, which will */ + /* be intercepted. This allows files which include gc.h, and hence */ + /* generate references to the GC_ symbols, to see the right ones. */ + GC_API int GC_pthread_create(pthread_t *t, + GC_PTHREAD_CREATE_CONST pthread_attr_t *a, + void * (*fn)(void *), void *arg) + { + return pthread_create(t, a, fn, arg); + } + +# ifndef GC_NO_PTHREAD_SIGMASK + GC_API int GC_pthread_sigmask(int how, const sigset_t *mask, + sigset_t *old) + { + return pthread_sigmask(how, mask, old); + } +# endif /* !GC_NO_PTHREAD_SIGMASK */ + + GC_API int GC_pthread_join(pthread_t t, void **res) + { + return pthread_join(t, res); + } + + GC_API int GC_pthread_detach(pthread_t t) + { + return pthread_detach(t); + } + +# ifndef GC_NO_PTHREAD_CANCEL + GC_API int GC_pthread_cancel(pthread_t t) + { + return pthread_cancel(t); + } +# endif /* !GC_NO_PTHREAD_CANCEL */ + +# ifdef GC_HAVE_PTHREAD_EXIT + GC_API GC_PTHREAD_EXIT_ATTRIBUTE void GC_pthread_exit(void *retval) + { + pthread_exit(retval); + } +# endif +# endif /* GC_USE_LD_WRAP || GC_USE_DLOPEN_WRAP */ + +# ifdef GC_USE_DLOPEN_WRAP + STATIC GC_bool GC_syms_initialized = FALSE; + + STATIC void GC_init_real_syms(void) + { + void *dl_handle; + + GC_ASSERT(!GC_syms_initialized); +# ifdef RTLD_NEXT + dl_handle = RTLD_NEXT; +# else + dl_handle = dlopen("libpthread.so.0", RTLD_LAZY); + if (NULL == dl_handle) { + dl_handle = dlopen("libpthread.so", RTLD_LAZY); /* without ".0" */ + if (NULL == dl_handle) ABORT("Couldn't open libpthread"); + } +# endif + REAL_FUNC(pthread_create) = (GC_pthread_create_t)(word) + dlsym(dl_handle, "pthread_create"); +# ifdef RTLD_NEXT + if (REAL_FUNC(pthread_create) == 0) + ABORT("pthread_create not found" + " (probably -lgc is specified after -lpthread)"); +# endif +# ifndef GC_NO_PTHREAD_SIGMASK + REAL_FUNC(pthread_sigmask) = (GC_pthread_sigmask_t)(word) + dlsym(dl_handle, "pthread_sigmask"); +# endif + REAL_FUNC(pthread_join) = (GC_pthread_join_t)(word) + dlsym(dl_handle, "pthread_join"); + REAL_FUNC(pthread_detach) = (GC_pthread_detach_t)(word) + dlsym(dl_handle, "pthread_detach"); +# ifndef GC_NO_PTHREAD_CANCEL + REAL_FUNC(pthread_cancel) = (GC_pthread_cancel_t)(word) + dlsym(dl_handle, "pthread_cancel"); +# endif +# ifdef GC_HAVE_PTHREAD_EXIT + REAL_FUNC(pthread_exit) = (GC_pthread_exit_t)(word) + dlsym(dl_handle, "pthread_exit"); +# endif + GC_syms_initialized = TRUE; + } + +# define INIT_REAL_SYMS() if (EXPECT(GC_syms_initialized, TRUE)) {} \ + else GC_init_real_syms() +# else +# define INIT_REAL_SYMS() (void)0 +# endif /* !GC_USE_DLOPEN_WRAP */ + +#else +# define WRAP_FUNC(f) GC_##f +# define REAL_FUNC(f) f +# define INIT_REAL_SYMS() (void)0 +#endif /* GC_WIN32_THREADS */ + +#if defined(MPROTECT_VDB) && defined(DARWIN) + GC_INNER int GC_inner_pthread_create(pthread_t *t, + GC_PTHREAD_CREATE_CONST pthread_attr_t *a, + void *(*fn)(void *), void *arg) + { + INIT_REAL_SYMS(); + return REAL_FUNC(pthread_create)(t, a, fn, arg); + } +#endif + +#ifndef GC_ALWAYS_MULTITHREADED + GC_INNER GC_bool GC_need_to_lock = FALSE; +#endif + +#ifdef THREAD_LOCAL_ALLOC + /* We must explicitly mark ptrfree and gcj free lists, since the free */ + /* list links wouldn't otherwise be found. We also set them in the */ + /* normal free lists, since that involves touching less memory than */ + /* if we scanned them normally. */ + GC_INNER void GC_mark_thread_local_free_lists(void) + { + int i; + GC_thread p; + + for (i = 0; i < THREAD_TABLE_SZ; ++i) { + for (p = GC_threads[i]; p != NULL; p = p -> tm.next) { + if (!KNOWN_FINISHED(p)) + GC_mark_thread_local_fls_for(&p->tlfs); + } + } + } + +# if defined(GC_ASSERTIONS) + /* Check that all thread-local free-lists are completely marked. */ + /* Also check that thread-specific-data structures are marked. */ + void GC_check_tls(void) + { + int i; + GC_thread p; + + for (i = 0; i < THREAD_TABLE_SZ; ++i) { + for (p = GC_threads[i]; p != NULL; p = p -> tm.next) { + if (!KNOWN_FINISHED(p)) + GC_check_tls_for(&p->tlfs); + } + } +# if defined(USE_CUSTOM_SPECIFIC) + if (GC_thread_key != 0) + GC_check_tsd_marks(GC_thread_key); +# endif + } +# endif /* GC_ASSERTIONS */ +#endif /* THREAD_LOCAL_ALLOC */ + +#ifdef GC_WIN32_THREADS + /* A macro for functions and variables which should be accessible */ + /* from win32_threads.c but otherwise could be static. */ +# define GC_INNER_WIN32THREAD GC_INNER +#else +# define GC_INNER_WIN32THREAD STATIC +#endif + +#ifdef PARALLEL_MARK + +# if defined(GC_WIN32_THREADS) || defined(USE_PROC_FOR_LIBRARIES) \ + || (defined(IA64) && (defined(HAVE_PTHREAD_ATTR_GET_NP) \ + || defined(HAVE_PTHREAD_GETATTR_NP))) + GC_INNER_WIN32THREAD ptr_t GC_marker_sp[MAX_MARKERS - 1] = {0}; + /* The cold end of the stack */ + /* for markers. */ +# endif /* GC_WIN32_THREADS || USE_PROC_FOR_LIBRARIES */ + +# if defined(IA64) && defined(USE_PROC_FOR_LIBRARIES) + static ptr_t marker_bsp[MAX_MARKERS - 1] = {0}; +# endif + +# if defined(GC_DARWIN_THREADS) && !defined(GC_NO_THREADS_DISCOVERY) + static mach_port_t marker_mach_threads[MAX_MARKERS - 1] = {0}; + + /* Used only by GC_suspend_thread_list(). */ + GC_INNER GC_bool GC_is_mach_marker(thread_act_t thread) + { + int i; + for (i = 0; i < GC_markers_m1; i++) { + if (marker_mach_threads[i] == thread) + return TRUE; + } + return FALSE; + } +# endif /* GC_DARWIN_THREADS */ + +# ifdef HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG /* NetBSD */ + static void set_marker_thread_name(unsigned id) + { + int err = pthread_setname_np(pthread_self(), "GC-marker-%zu", + (void*)(size_t)id); + if (EXPECT(err != 0, FALSE)) + WARN("pthread_setname_np failed, errno= %" WARN_PRIdPTR "\n", + (signed_word)err); + } + +# elif defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID) \ + || defined(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID) \ + || defined(HAVE_PTHREAD_SET_NAME_NP) +# ifdef HAVE_PTHREAD_SET_NAME_NP +# include +# endif + static void set_marker_thread_name(unsigned id) + { + char name_buf[16]; /* pthread_setname_np may fail for longer names */ + int len = sizeof("GC-marker-") - 1; + + /* Compose the name manually as snprintf may be unavailable or */ + /* "%u directive output may be truncated" warning may occur. */ + BCOPY("GC-marker-", name_buf, len); + if (id >= 10) + name_buf[len++] = (char)('0' + (id / 10) % 10); + name_buf[len] = (char)('0' + id % 10); + name_buf[len + 1] = '\0'; + +# ifdef HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID /* iOS, OS X */ + (void)pthread_setname_np(name_buf); +# elif defined(HAVE_PTHREAD_SET_NAME_NP) /* OpenBSD */ + pthread_set_name_np(pthread_self(), name_buf); +# else /* Linux, Solaris, etc. */ + if (EXPECT(pthread_setname_np(pthread_self(), name_buf) != 0, FALSE)) + WARN("pthread_setname_np failed\n", 0); +# endif + } + +# elif defined(GC_WIN32_THREADS) && !defined(MSWINCE) + /* A pointer to SetThreadDescription() which is available since */ + /* Windows 10. The function prototype is in processthreadsapi.h. */ + static FARPROC setThreadDescription_fn; + + GC_INNER void GC_init_win32_thread_naming(HMODULE hK32) + { + if (hK32) + setThreadDescription_fn = GetProcAddress(hK32, "SetThreadDescription"); + } + + static void set_marker_thread_name(unsigned id) + { + WCHAR name_buf[16]; + int len = sizeof(L"GC-marker-") / sizeof(WCHAR) - 1; + HRESULT hr; + + if (!setThreadDescription_fn) return; /* missing SetThreadDescription */ + + /* Compose the name manually as swprintf may be unavailable. */ + BCOPY(L"GC-marker-", name_buf, len * sizeof(WCHAR)); + if (id >= 10) + name_buf[len++] = (WCHAR)('0' + (id / 10) % 10); + name_buf[len] = (WCHAR)('0' + id % 10); + name_buf[len + 1] = 0; + + /* Invoke SetThreadDescription(). Cast the function pointer to word */ + /* first to avoid "incompatible function types" compiler warning. */ + hr = (*(HRESULT (WINAPI *)(HANDLE, const WCHAR *)) + (GC_funcptr_uint)setThreadDescription_fn)(GetCurrentThread(), + name_buf); + if (hr < 0) + WARN("SetThreadDescription failed\n", 0); + } +# else +# define set_marker_thread_name(id) (void)(id) +# endif + + GC_INNER_WIN32THREAD +# ifdef GC_PTHREADS_PARAMARK + void *GC_mark_thread(void *id) +# elif defined(MSWINCE) + DWORD WINAPI GC_mark_thread(LPVOID id) +# else + unsigned __stdcall GC_mark_thread(void *id) +# endif + { + word my_mark_no = 0; + IF_CANCEL(int cancel_state;) + + if ((word)id == GC_WORD_MAX) return 0; /* to prevent a compiler warning */ + DISABLE_CANCEL(cancel_state); + /* Mark threads are not cancellable; they */ + /* should be invisible to client. */ + set_marker_thread_name((unsigned)(word)id); +# if defined(GC_WIN32_THREADS) || defined(USE_PROC_FOR_LIBRARIES) \ + || (defined(IA64) && (defined(HAVE_PTHREAD_ATTR_GET_NP) \ + || defined(HAVE_PTHREAD_GETATTR_NP))) + GC_marker_sp[(word)id] = GC_approx_sp(); +# endif +# if defined(IA64) && defined(USE_PROC_FOR_LIBRARIES) + marker_bsp[(word)id] = GC_save_regs_in_stack(); +# endif +# if defined(GC_DARWIN_THREADS) && !defined(GC_NO_THREADS_DISCOVERY) + marker_mach_threads[(word)id] = mach_thread_self(); +# endif +# if !defined(GC_PTHREADS_PARAMARK) + GC_marker_Id[(word)id] = thread_id_self(); +# endif + + /* Inform GC_start_mark_threads about completion of marker data init. */ + GC_acquire_mark_lock(); + if (0 == --GC_fl_builder_count) /* count may have a negative value */ + GC_notify_all_builder(); + + /* GC_mark_no is passed only to allow GC_help_marker to terminate */ + /* promptly. This is important if it were called from the signal */ + /* handler or from the allocator lock acquisition code. Under */ + /* Linux, it is not safe to call it from a signal handler, since it */ + /* uses mutexes and condition variables. Since it is called only */ + /* here, the argument is unnecessary. */ + for (;; ++my_mark_no) { + if (my_mark_no - GC_mark_no > (word)2) { + /* Resynchronize if we get far off, e.g. because GC_mark_no */ + /* wrapped. */ + my_mark_no = GC_mark_no; + } +# ifdef DEBUG_THREADS + GC_log_printf("Starting helper for mark number %lu (thread %u)\n", + (unsigned long)my_mark_no, (unsigned)(word)id); +# endif + GC_help_marker(my_mark_no); + } + } + + GC_INNER_WIN32THREAD int GC_available_markers_m1 = 0; + +#endif /* PARALLEL_MARK */ + +#ifdef GC_PTHREADS_PARAMARK + +# ifdef GLIBC_2_1_MUTEX_HACK + /* Ugly workaround for a linux threads bug in the final versions */ + /* of glibc 2.1. Pthread_mutex_trylock sets the mutex owner */ + /* field even when it fails to acquire the mutex. This causes */ + /* pthread_cond_wait to die. Should not be needed for glibc 2.2. */ + /* According to the man page, we should use */ + /* PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP, but that isn't actually */ + /* defined. */ + static pthread_mutex_t mark_mutex = + {0, 0, 0, PTHREAD_MUTEX_ERRORCHECK_NP, {0, 0}}; +# else + static pthread_mutex_t mark_mutex = PTHREAD_MUTEX_INITIALIZER; +# endif + +# ifdef CAN_HANDLE_FORK + static pthread_cond_t mark_cv; + /* initialized by GC_start_mark_threads_inner */ +# else + static pthread_cond_t mark_cv = PTHREAD_COND_INITIALIZER; +# endif + + GC_INNER void GC_start_mark_threads_inner(void) + { + int i; + pthread_attr_t attr; +# ifndef NO_MARKER_SPECIAL_SIGMASK + sigset_t set, oldset; +# endif + + GC_ASSERT(I_HOLD_LOCK()); + ASSERT_CANCEL_DISABLED(); + if (GC_available_markers_m1 <= 0 || GC_parallel) return; + /* Skip if parallel markers disabled or already started. */ + GC_wait_for_gc_completion(TRUE); + +# ifdef CAN_HANDLE_FORK + /* Initialize mark_cv (for the first time), or cleanup its value */ + /* after forking in the child process. All the marker threads in */ + /* the parent process were blocked on this variable at fork, so */ + /* pthread_cond_wait() malfunction (hang) is possible in the */ + /* child process without such a cleanup. */ + /* TODO: This is not portable, it is better to shortly unblock */ + /* all marker threads in the parent process at fork. */ + { + pthread_cond_t mark_cv_local = PTHREAD_COND_INITIALIZER; + BCOPY(&mark_cv_local, &mark_cv, sizeof(mark_cv)); + } +# endif + + GC_ASSERT(GC_fl_builder_count == 0); + INIT_REAL_SYMS(); /* for pthread_create */ + if (0 != pthread_attr_init(&attr)) ABORT("pthread_attr_init failed"); + if (0 != pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) + ABORT("pthread_attr_setdetachstate failed"); + +# ifdef DEFAULT_STACK_MAYBE_SMALL + /* Default stack size is usually too small: increase it. */ + /* Otherwise marker threads or GC may run out of space. */ + { + size_t old_size; + + if (pthread_attr_getstacksize(&attr, &old_size) != 0) + ABORT("pthread_attr_getstacksize failed"); + if (old_size < MIN_STACK_SIZE + && old_size != 0 /* stack size is known */) { + if (pthread_attr_setstacksize(&attr, MIN_STACK_SIZE) != 0) + ABORT("pthread_attr_setstacksize failed"); + } + } +# endif /* DEFAULT_STACK_MAYBE_SMALL */ + +# ifndef NO_MARKER_SPECIAL_SIGMASK + /* Apply special signal mask to GC marker threads, and don't drop */ + /* user defined signals by GC marker threads. */ + if (sigfillset(&set) != 0) + ABORT("sigfillset failed"); + +# ifdef SIGNAL_BASED_STOP_WORLD + /* These are used by GC to stop and restart the world. */ + if (sigdelset(&set, GC_get_suspend_signal()) != 0 + || sigdelset(&set, GC_get_thr_restart_signal()) != 0) + ABORT("sigdelset failed"); +# endif + + if (EXPECT(REAL_FUNC(pthread_sigmask)(SIG_BLOCK, + &set, &oldset) < 0, FALSE)) { + WARN("pthread_sigmask set failed, no markers started\n", 0); + GC_markers_m1 = 0; + (void)pthread_attr_destroy(&attr); + return; + } +# endif /* !NO_MARKER_SPECIAL_SIGMASK */ + + /* To have proper GC_parallel value in GC_help_marker. */ + GC_markers_m1 = GC_available_markers_m1; + + for (i = 0; i < GC_available_markers_m1; ++i) { + pthread_t new_thread; + +# ifdef GC_WIN32_THREADS + GC_marker_last_stack_min[i] = ADDR_LIMIT; +# endif + if (EXPECT(REAL_FUNC(pthread_create)(&new_thread, &attr, GC_mark_thread, + (void *)(word)i) != 0, FALSE)) { + WARN("Marker thread %" WARN_PRIdPTR " creation failed\n", + (signed_word)i); + /* Don't try to create other marker threads. */ + GC_markers_m1 = i; + break; + } + } + +# ifndef NO_MARKER_SPECIAL_SIGMASK + /* Restore previous signal mask. */ + if (EXPECT(REAL_FUNC(pthread_sigmask)(SIG_SETMASK, + &oldset, NULL) < 0, FALSE)) { + WARN("pthread_sigmask restore failed\n", 0); + } +# endif + + (void)pthread_attr_destroy(&attr); + GC_wait_for_markers_init(); + GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1); + } + +#endif /* GC_PTHREADS_PARAMARK */ + +/* A hash table to keep information about the registered threads. */ +/* Not used if GC_win32_dll_threads is set. */ +GC_INNER GC_thread GC_threads[THREAD_TABLE_SZ] = {0}; + +/* It may not be safe to allocate when we register the first thread. */ +/* Note that next and status fields are unused, but there might be some */ +/* other fields (crtn) to be pushed. */ +static struct GC_StackContext_Rep first_crtn; +static struct GC_Thread_Rep first_thread; + +/* A place to retain a pointer to an allocated object while a thread */ +/* registration is ongoing. Protected by the allocator lock. */ +static GC_stack_context_t saved_crtn = NULL; + +#ifdef GC_ASSERTIONS + GC_INNER GC_bool GC_thr_initialized = FALSE; +#endif + +void GC_push_thread_structures(void) +{ + GC_ASSERT(I_HOLD_LOCK()); +# if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS) + if (GC_win32_dll_threads) { + /* Unlike the other threads implementations, the thread table */ + /* here contains no pointers to the collectible heap (note also */ + /* that GC_PTHREADS is incompatible with DllMain-based thread */ + /* registration). Thus we have no private structures we need */ + /* to preserve. */ + } else +# endif + /* else */ { + GC_push_all(&GC_threads, (ptr_t)(&GC_threads) + sizeof(GC_threads)); + GC_ASSERT(NULL == first_thread.tm.next); +# ifdef GC_PTHREADS + GC_ASSERT(NULL == first_thread.status); +# endif + GC_PUSH_ALL_SYM(first_thread.crtn); + GC_PUSH_ALL_SYM(saved_crtn); + } +# if defined(THREAD_LOCAL_ALLOC) && defined(USE_CUSTOM_SPECIFIC) + GC_PUSH_ALL_SYM(GC_thread_key); +# endif +} + +#if defined(MPROTECT_VDB) && defined(GC_WIN32_THREADS) + GC_INNER void GC_win32_unprotect_thread(GC_thread t) + { + GC_ASSERT(I_HOLD_LOCK()); + if (!GC_win32_dll_threads && GC_auto_incremental) { + GC_stack_context_t crtn = t -> crtn; + + if (crtn != &first_crtn) { + GC_ASSERT(SMALL_OBJ(GC_size(crtn))); + GC_remove_protection(HBLKPTR(crtn), 1, FALSE); + } + if (t != &first_thread) { + GC_ASSERT(SMALL_OBJ(GC_size(t))); + GC_remove_protection(HBLKPTR(t), 1, FALSE); + } + } + } +#endif /* MPROTECT_VDB && GC_WIN32_THREADS */ + +#ifdef DEBUG_THREADS + STATIC int GC_count_threads(void) + { + int i; + int count = 0; + +# if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS) + if (GC_win32_dll_threads) return -1; /* not implemented */ +# endif + GC_ASSERT(I_HOLD_READER_LOCK()); + for (i = 0; i < THREAD_TABLE_SZ; ++i) { + GC_thread p; + + for (p = GC_threads[i]; p != NULL; p = p -> tm.next) { + if (!KNOWN_FINISHED(p)) + ++count; + } + } + return count; + } +#endif /* DEBUG_THREADS */ + +/* Add a thread to GC_threads. We assume it wasn't already there. */ +/* The id field is set by the caller. */ +GC_INNER_WIN32THREAD GC_thread GC_new_thread(thread_id_t self_id) +{ + int hv = THREAD_TABLE_INDEX(self_id); + GC_thread result; + + GC_ASSERT(I_HOLD_LOCK()); +# ifdef DEBUG_THREADS + GC_log_printf("Creating thread %p\n", (void *)(signed_word)self_id); + for (result = GC_threads[hv]; + result != NULL; result = result -> tm.next) + if (!THREAD_ID_EQUAL(result -> id, self_id)) { + GC_log_printf("Hash collision at GC_threads[%d]\n", hv); + break; + } +# endif + if (EXPECT(NULL == first_thread.crtn, FALSE)) { + result = &first_thread; + first_thread.crtn = &first_crtn; + GC_ASSERT(NULL == GC_threads[hv]); +# ifdef CPPCHECK + GC_noop1((unsigned char)first_thread.flags_pad[0]); +# if defined(THREAD_SANITIZER) && defined(SIGNAL_BASED_STOP_WORLD) + GC_noop1((unsigned char)first_crtn.dummy[0]); +# endif +# ifndef GC_NO_FINALIZATION + GC_noop1((unsigned char)first_crtn.fnlz_pad[0]); +# endif +# endif + } else { + GC_stack_context_t crtn; + + GC_ASSERT(!GC_win32_dll_threads); + GC_ASSERT(!GC_in_thread_creation); + GC_in_thread_creation = TRUE; /* OK to collect from unknown thread */ + crtn = (GC_stack_context_t)GC_INTERNAL_MALLOC( + sizeof(struct GC_StackContext_Rep), NORMAL); + + /* The current stack is not scanned until the thread is */ + /* registered, thus crtn pointer is to be retained in the */ + /* global data roots for a while (and pushed explicitly if */ + /* a collection occurs here). */ + GC_ASSERT(NULL == saved_crtn); + saved_crtn = crtn; + result = (GC_thread)GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), + NORMAL); + saved_crtn = NULL; /* no more collections till thread is registered */ + GC_in_thread_creation = FALSE; + if (NULL == crtn || NULL == result) + ABORT("Failed to allocate memory for thread registering"); + result -> crtn = crtn; + } + /* The id field is not set here. */ +# ifdef USE_TKILL_ON_ANDROID + result -> kernel_id = gettid(); +# endif + result -> tm.next = GC_threads[hv]; + GC_threads[hv] = result; +# ifdef NACL + GC_nacl_initialize_gc_thread(result); +# endif + GC_ASSERT(0 == result -> flags); + if (EXPECT(result != &first_thread, TRUE)) + GC_dirty(result); + return result; +} + +/* Delete a thread from GC_threads. We assume it is there. (The code */ +/* intentionally traps if it was not.) It is also safe to delete the */ +/* main thread. If GC_win32_dll_threads is set, it should be called */ +/* only from the thread being deleted. If a thread has been joined, */ +/* but we have not yet been notified, then there may be more than one */ +/* thread in the table with the same thread id - this is OK because we */ +/* delete a specific one. */ +GC_INNER_WIN32THREAD void GC_delete_thread(GC_thread t) +{ +# if defined(GC_WIN32_THREADS) && !defined(MSWINCE) + CloseHandle(t -> handle); +# endif +# if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS) + if (GC_win32_dll_threads) { + /* This is intended to be lock-free. It is either called */ + /* synchronously from the thread being deleted, or by the joining */ + /* thread. In this branch asynchronous changes to (*t) are */ + /* possible. Note that it is not allowed to call GC_printf (and */ + /* the friends) here, see GC_stop_world() in win32_threads.c for */ + /* the information. */ + t -> crtn -> stack_end = NULL; + t -> id = 0; + t -> flags = 0; /* !IS_SUSPENDED */ +# ifdef RETRY_GET_THREAD_CONTEXT + t -> context_sp = NULL; +# endif + AO_store_release(&(t -> tm.in_use), FALSE); + } else +# endif + /* else */ { + thread_id_t id = t -> id; + int hv = THREAD_TABLE_INDEX(id); + GC_thread p; + GC_thread prev = NULL; + + GC_ASSERT(I_HOLD_LOCK()); +# if defined(DEBUG_THREADS) && !defined(MSWINCE) \ + && (!defined(MSWIN32) || defined(CONSOLE_LOG)) + GC_log_printf("Deleting thread %p, n_threads= %d\n", + (void *)(signed_word)id, GC_count_threads()); +# endif + for (p = GC_threads[hv]; p != t; p = p -> tm.next) { + prev = p; + } + if (NULL == prev) { + GC_threads[hv] = p -> tm.next; + } else { + GC_ASSERT(prev != &first_thread); + prev -> tm.next = p -> tm.next; + GC_dirty(prev); + } + if (EXPECT(p != &first_thread, TRUE)) { +# ifdef GC_DARWIN_THREADS + mach_port_deallocate(mach_task_self(), p -> mach_thread); +# endif + GC_ASSERT(p -> crtn != &first_crtn); + GC_INTERNAL_FREE(p -> crtn); + GC_INTERNAL_FREE(p); + } + } +} + +/* Return a GC_thread corresponding to a given thread id, or */ +/* NULL if it is not there. Caller holds the allocator lock */ +/* at least in the reader mode or otherwise inhibits updates. */ +/* If there is more than one thread with the given id, we */ +/* return the most recent one. */ +GC_INNER GC_thread GC_lookup_thread(thread_id_t id) +{ + GC_thread p; + +# if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS) + if (GC_win32_dll_threads) + return GC_win32_dll_lookup_thread(id); +# endif + for (p = GC_threads[THREAD_TABLE_INDEX(id)]; + p != NULL; p = p -> tm.next) { + if (EXPECT(THREAD_ID_EQUAL(p -> id, id), TRUE)) break; + } + return p; +} + +/* Same as GC_self_thread_inner() but acquires the allocator lock (in */ +/* the reader mode). */ +STATIC GC_thread GC_self_thread(void) { + GC_thread p; + + READER_LOCK(); + p = GC_self_thread_inner(); + READER_UNLOCK(); + return p; +} + +#ifndef GC_NO_FINALIZATION + /* Called by GC_finalize() (in case of an allocation failure observed). */ + GC_INNER void GC_reset_finalizer_nested(void) + { + GC_ASSERT(I_HOLD_LOCK()); + GC_self_thread_inner() -> crtn -> finalizer_nested = 0; + } + + /* Checks and updates the thread-local level of finalizers recursion. */ + /* Returns NULL if GC_invoke_finalizers() should not be called by the */ + /* collector (to minimize the risk of a deep finalizers recursion), */ + /* otherwise returns a pointer to the thread-local finalizer_nested. */ + /* Called by GC_notify_or_invoke_finalizers() only. */ + GC_INNER unsigned char *GC_check_finalizer_nested(void) + { + GC_thread me; + GC_stack_context_t crtn; + unsigned nesting_level; + + GC_ASSERT(I_HOLD_LOCK()); + me = GC_self_thread_inner(); +# if defined(INCLUDE_LINUX_THREAD_DESCR) && defined(REDIRECT_MALLOC) + /* As noted in GC_pthread_start, an allocation may happen in */ + /* GC_get_stack_base, causing GC_notify_or_invoke_finalizers */ + /* to be called before the thread gets registered. */ + if (EXPECT(NULL == me, FALSE)) return NULL; +# endif + crtn = me -> crtn; + nesting_level = crtn -> finalizer_nested; + if (nesting_level) { + /* We are inside another GC_invoke_finalizers(). */ + /* Skip some implicitly-called GC_invoke_finalizers() */ + /* depending on the nesting (recursion) level. */ + if (++(crtn -> finalizer_skipped) < (1U << nesting_level)) + return NULL; + crtn -> finalizer_skipped = 0; + } + crtn -> finalizer_nested = (unsigned char)(nesting_level + 1); + return &(crtn -> finalizer_nested); + } +#endif /* !GC_NO_FINALIZATION */ + +#if defined(GC_ASSERTIONS) && defined(THREAD_LOCAL_ALLOC) + /* This is called from thread-local GC_malloc(). */ + GC_bool GC_is_thread_tsd_valid(void *tsd) + { + GC_thread me = GC_self_thread(); + + return (word)tsd >= (word)(&me->tlfs) + && (word)tsd < (word)(&me->tlfs) + sizeof(me->tlfs); + } +#endif /* GC_ASSERTIONS && THREAD_LOCAL_ALLOC */ + +GC_API int GC_CALL GC_thread_is_registered(void) +{ + /* TODO: Use GC_get_tlfs() instead. */ + GC_thread me = GC_self_thread(); + + return me != NULL && !KNOWN_FINISHED(me); +} + +GC_API void GC_CALL GC_register_altstack(void *normstack, + GC_word normstack_size, void *altstack, GC_word altstack_size) +{ +#ifdef GC_WIN32_THREADS + /* TODO: Implement */ + UNUSED_ARG(normstack); + UNUSED_ARG(normstack_size); + UNUSED_ARG(altstack); + UNUSED_ARG(altstack_size); +#else + GC_thread me; + GC_stack_context_t crtn; + + READER_LOCK(); + me = GC_self_thread_inner(); + if (EXPECT(NULL == me, FALSE)) { + /* We are called before GC_thr_init. */ + me = &first_thread; + } + crtn = me -> crtn; + crtn -> normstack = (ptr_t)normstack; + crtn -> normstack_size = normstack_size; + crtn -> altstack = (ptr_t)altstack; + crtn -> altstack_size = altstack_size; + READER_UNLOCK_RELEASE(); +#endif +} + +#ifdef USE_PROC_FOR_LIBRARIES + GC_INNER GC_bool GC_segment_is_thread_stack(ptr_t lo, ptr_t hi) + { + int i; + GC_thread p; + + GC_ASSERT(I_HOLD_READER_LOCK()); +# ifdef PARALLEL_MARK + for (i = 0; i < GC_markers_m1; ++i) { + if ((word)GC_marker_sp[i] > (word)lo + && (word)GC_marker_sp[i] < (word)hi) + return TRUE; +# ifdef IA64 + if ((word)marker_bsp[i] > (word)lo + && (word)marker_bsp[i] < (word)hi) + return TRUE; +# endif + } +# endif + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != NULL; p = p -> tm.next) { + GC_stack_context_t crtn = p -> crtn; + + if (crtn -> stack_end != NULL) { +# ifdef STACK_GROWS_UP + if ((word)crtn -> stack_end >= (word)lo + && (word)crtn -> stack_end < (word)hi) + return TRUE; +# else + if ((word)crtn -> stack_end > (word)lo + && (word)crtn -> stack_end <= (word)hi) + return TRUE; +# endif + } + } + } + return FALSE; + } +#endif /* USE_PROC_FOR_LIBRARIES */ + +#if (defined(HAVE_PTHREAD_ATTR_GET_NP) || defined(HAVE_PTHREAD_GETATTR_NP)) \ + && defined(IA64) + /* Find the largest stack base smaller than bound. May be used */ + /* to find the boundary between a register stack and adjacent */ + /* immediately preceding memory stack. */ + GC_INNER ptr_t GC_greatest_stack_base_below(ptr_t bound) + { + int i; + GC_thread p; + ptr_t result = 0; + + GC_ASSERT(I_HOLD_READER_LOCK()); +# ifdef PARALLEL_MARK + for (i = 0; i < GC_markers_m1; ++i) { + if ((word)GC_marker_sp[i] > (word)result + && (word)GC_marker_sp[i] < (word)bound) + result = GC_marker_sp[i]; + } +# endif + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != NULL; p = p -> tm.next) { + GC_stack_context_t crtn = p -> crtn; + + if ((word)(crtn -> stack_end) > (word)result + && (word)(crtn -> stack_end) < (word)bound) { + result = crtn -> stack_end; + } + } + } + return result; + } +#endif /* IA64 */ + +#ifndef STAT_READ +# define STAT_READ read + /* If read is wrapped, this may need to be redefined to call */ + /* the real one. */ +#endif + +#ifdef GC_HPUX_THREADS +# define GC_get_nprocs() pthread_num_processors_np() + +#elif defined(GC_OSF1_THREADS) || defined(GC_AIX_THREADS) \ + || defined(GC_HAIKU_THREADS) || defined(GC_SOLARIS_THREADS) \ + || defined(HURD) || defined(HOST_ANDROID) || defined(NACL) + GC_INLINE int GC_get_nprocs(void) + { + int nprocs = (int)sysconf(_SC_NPROCESSORS_ONLN); + return nprocs > 0 ? nprocs : 1; /* ignore error silently */ + } + +#elif defined(GC_IRIX_THREADS) + GC_INLINE int GC_get_nprocs(void) + { + int nprocs = (int)sysconf(_SC_NPROC_ONLN); + return nprocs > 0 ? nprocs : 1; /* ignore error silently */ + } + +#elif defined(GC_LINUX_THREADS) /* && !HOST_ANDROID && !NACL */ + /* Return the number of processors. */ + STATIC int GC_get_nprocs(void) + { + /* Should be "return sysconf(_SC_NPROCESSORS_ONLN);" but that */ + /* appears to be buggy in many cases. */ + /* We look for lines "cpu" in /proc/stat. */ +# define PROC_STAT_BUF_SZ ((1 + MAX_MARKERS) * 100) /* should be enough */ + /* No need to read the entire /proc/stat to get maximum cpu as */ + /* - the requested lines are located at the beginning of the file; */ + /* - the lines with cpu where N > MAX_MARKERS are not needed. */ + char stat_buf[PROC_STAT_BUF_SZ+1]; + int f; + int result, i, len; + + f = open("/proc/stat", O_RDONLY); + if (f < 0) { + WARN("Could not open /proc/stat\n", 0); + return 1; /* assume an uniprocessor */ + } + len = STAT_READ(f, stat_buf, sizeof(stat_buf)-1); + /* Unlikely that we need to retry because of an incomplete read here. */ + if (len < 0) { + WARN("Failed to read /proc/stat, errno= %" WARN_PRIdPTR "\n", + (signed_word)errno); + close(f); + return 1; + } + stat_buf[len] = '\0'; /* to avoid potential buffer overrun by atoi() */ + close(f); + + result = 1; + /* Some old kernels only have a single "cpu nnnn ..." */ + /* entry in /proc/stat. We identify those as */ + /* uniprocessors. */ + + for (i = 0; i < len - 4; ++i) { + if (stat_buf[i] == '\n' && stat_buf[i+1] == 'c' + && stat_buf[i+2] == 'p' && stat_buf[i+3] == 'u') { + int cpu_no = atoi(&stat_buf[i + 4]); + if (cpu_no >= result) + result = cpu_no + 1; + } + } + return result; + } + +#elif defined(GC_DGUX386_THREADS) + /* Return the number of processors, or i <= 0 if it can't be determined. */ + STATIC int GC_get_nprocs(void) + { + int numCpus; + struct dg_sys_info_pm_info pm_sysinfo; + int status = 0; + + status = dg_sys_info((long int *) &pm_sysinfo, + DG_SYS_INFO_PM_INFO_TYPE, DG_SYS_INFO_PM_CURRENT_VERSION); + if (status < 0) { + /* set -1 for error */ + numCpus = -1; + } else { + /* Active CPUs */ + numCpus = pm_sysinfo.idle_vp_count; + } + return numCpus; + } + +#elif defined(GC_DARWIN_THREADS) || defined(GC_FREEBSD_THREADS) \ + || defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_THREADS) + STATIC int GC_get_nprocs(void) + { + int mib[] = {CTL_HW,HW_NCPU}; + int res; + size_t len = sizeof(res); + + sysctl(mib, sizeof(mib)/sizeof(int), &res, &len, NULL, 0); + return res; + } + +#else + /* E.g., GC_RTEMS_PTHREADS */ +# define GC_get_nprocs() 1 /* not implemented */ +#endif /* !GC_LINUX_THREADS && !GC_DARWIN_THREADS && ... */ + +#if defined(ARM32) && defined(GC_LINUX_THREADS) && !defined(NACL) + /* Some buggy Linux/arm kernels show only non-sleeping CPUs in */ + /* /proc/stat (and /proc/cpuinfo), so another data system source is */ + /* tried first. Result <= 0 on error. */ + STATIC int GC_get_nprocs_present(void) + { + char stat_buf[16]; + int f; + int len; + + f = open("/sys/devices/system/cpu/present", O_RDONLY); + if (f < 0) + return -1; /* cannot open the file */ + + len = STAT_READ(f, stat_buf, sizeof(stat_buf)); + close(f); + + /* Recognized file format: "0\n" or "0-\n" */ + /* The file might probably contain a comma-separated list */ + /* but we do not need to handle it (just silently ignore). */ + if (len < 2 || stat_buf[0] != '0' || stat_buf[len - 1] != '\n') { + return 0; /* read error or unrecognized content */ + } else if (len == 2) { + return 1; /* an uniprocessor */ + } else if (stat_buf[1] != '-') { + return 0; /* unrecognized content */ + } + + stat_buf[len - 1] = '\0'; /* terminate the string */ + return atoi(&stat_buf[2]) + 1; /* skip "0-" and parse max_cpu_num */ + } +#endif /* ARM32 && GC_LINUX_THREADS && !NACL */ + +#if defined(CAN_HANDLE_FORK) && defined(THREAD_SANITIZER) + + + /* Workaround for TSan which does not notice that the allocator lock */ + /* is acquired in fork_prepare_proc(). */ + GC_ATTR_NO_SANITIZE_THREAD + static GC_bool collection_in_progress(void) + { + return GC_mark_state != MS_NONE; + } +#else +# define collection_in_progress() GC_collection_in_progress() +#endif + +/* We hold the allocator lock. Wait until an in-progress GC has */ +/* finished. Repeatedly releases the allocator lock in order to wait. */ +/* If wait_for_all is true, then we exit with the allocator lock held */ +/* and no collection is in progress; otherwise we just wait for the */ +/* current collection to finish. */ +GC_INNER void GC_wait_for_gc_completion(GC_bool wait_for_all) +{ +# if !defined(THREAD_SANITIZER) || !defined(CAN_CALL_ATFORK) + /* GC_lock_holder is accessed with the allocator lock held, so */ + /* there is no data race actually (unlike what's reported by TSan). */ + GC_ASSERT(I_HOLD_LOCK()); +# endif + ASSERT_CANCEL_DISABLED(); +# ifdef GC_DISABLE_INCREMENTAL + (void)wait_for_all; +# else + if (GC_incremental && collection_in_progress()) { + word old_gc_no = GC_gc_no; + + /* Make sure that no part of our stack is still on the mark */ + /* stack, since it's about to be unmapped. */ +# ifdef LINT2 + /* Note: do not transform this if-do-while construction into */ + /* a single while statement because it might cause some */ + /* static code analyzers to report a false positive (FP) */ + /* code defect about missing unlock after lock. */ +# endif + do { + ENTER_GC(); + GC_ASSERT(!GC_in_thread_creation); + GC_in_thread_creation = TRUE; + GC_collect_a_little_inner(1); + GC_in_thread_creation = FALSE; + EXIT_GC(); + + UNLOCK(); +# ifdef GC_WIN32_THREADS + Sleep(0); +# else + sched_yield(); +# endif + LOCK(); + } while (GC_incremental && collection_in_progress() + && (wait_for_all || old_gc_no == GC_gc_no)); + } +# endif +} + +#ifdef CAN_HANDLE_FORK + + /* Procedures called before and after a fork. The goal here is to */ + /* make it safe to call GC_malloc() in a forked child. It is unclear */ + /* that is attainable, since the single UNIX spec seems to imply that */ + /* one should only call async-signal-safe functions, and we probably */ + /* cannot quite guarantee that. But we give it our best shot. (That */ + /* same spec also implies that it is not safe to call the system */ + /* malloc between fork and exec. Thus we're doing no worse than it.) */ + + IF_CANCEL(static int fork_cancel_state;) + /* protected by the allocator lock */ + +# ifdef PARALLEL_MARK +# ifdef THREAD_SANITIZER +# if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK) + STATIC void GC_generic_lock(pthread_mutex_t *); +# endif + GC_ATTR_NO_SANITIZE_THREAD + static void wait_for_reclaim_atfork(void); +# else +# define wait_for_reclaim_atfork() GC_wait_for_reclaim() +# endif +# endif /* PARALLEL_MARK */ + + /* Prevent TSan false positive about the race during items removal */ + /* from GC_threads. (The race cannot happen since only one thread */ + /* survives in the child.) */ +# ifdef CAN_CALL_ATFORK + GC_ATTR_NO_SANITIZE_THREAD +# endif + static void store_to_threads_table(int hv, GC_thread me) + { + GC_threads[hv] = me; + } + + /* Remove all entries from the GC_threads table, except the one for */ + /* the current thread. We need to do this in the child process after */ + /* a fork(), since only the current thread survives in the child. */ + STATIC void GC_remove_all_threads_but_me(void) + { + int hv; + GC_thread me = NULL; + pthread_t self = pthread_self(); /* same as in parent */ +# ifndef GC_WIN32_THREADS +# define pthread_id id +# endif + + for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) { + GC_thread p, next; + + for (p = GC_threads[hv]; p != NULL; p = next) { + next = p -> tm.next; + if (THREAD_EQUAL(p -> pthread_id, self) + && me == NULL) { /* ignore dead threads with the same id */ + me = p; + p -> tm.next = NULL; + } else { +# ifdef THREAD_LOCAL_ALLOC + if (!KNOWN_FINISHED(p)) { + /* Cannot call GC_destroy_thread_local here. The free */ + /* lists may be in an inconsistent state (as thread p may */ + /* be updating one of the lists by GC_generic_malloc_many */ + /* or GC_FAST_MALLOC_GRANS when fork is invoked). */ + /* This should not be a problem because the lost elements */ + /* of the free lists will be collected during GC. */ + GC_remove_specific_after_fork(GC_thread_key, p -> pthread_id); + } +# endif + /* TODO: To avoid TSan hang (when updating GC_bytes_freed), */ + /* we just skip explicit freeing of GC_threads entries. */ +# if !defined(THREAD_SANITIZER) || !defined(CAN_CALL_ATFORK) + if (p != &first_thread) { + /* TODO: Should call mach_port_deallocate? */ + GC_ASSERT(p -> crtn != &first_crtn); + GC_INTERNAL_FREE(p -> crtn); + GC_INTERNAL_FREE(p); + } +# endif + } + } + store_to_threads_table(hv, NULL); + } + +# ifdef LINT2 + if (NULL == me) ABORT("Current thread is not found after fork"); +# else + GC_ASSERT(me != NULL); +# endif +# ifdef GC_WIN32_THREADS + /* Update Win32 thread id and handle. */ + me -> id = thread_id_self(); /* differs from that in parent */ +# ifndef MSWINCE + if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), + GetCurrentProcess(), (HANDLE *)&(me -> handle), + 0 /* dwDesiredAccess */, FALSE /* bInheritHandle */, + DUPLICATE_SAME_ACCESS)) + ABORT("DuplicateHandle failed"); +# endif +# endif +# ifdef GC_DARWIN_THREADS + /* Update thread Id after fork (it is OK to call */ + /* GC_destroy_thread_local and GC_free_inner */ + /* before update). */ + me -> mach_thread = mach_thread_self(); +# endif +# ifdef USE_TKILL_ON_ANDROID + me -> kernel_id = gettid(); +# endif + + /* Put "me" back to GC_threads. */ + store_to_threads_table(THREAD_TABLE_INDEX(me -> id), me); + +# if defined(THREAD_LOCAL_ALLOC) && !defined(USE_CUSTOM_SPECIFIC) + /* Some TLS implementations (e.g., on Cygwin) might be not */ + /* fork-friendly, so we re-assign thread-local pointer to 'tlfs' */ + /* for safety instead of the assertion check (again, it is OK to */ + /* call GC_destroy_thread_local and GC_free_inner before). */ + { + int res = GC_setspecific(GC_thread_key, &me->tlfs); + + if (COVERT_DATAFLOW(res) != 0) + ABORT("GC_setspecific failed (in child)"); + } +# endif +# undef pthread_id + } + + /* Called before a fork(). */ +# if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK) + /* GC_lock_holder is updated safely (no data race actually). */ + GC_ATTR_NO_SANITIZE_THREAD +# endif + static void fork_prepare_proc(void) + { + /* Acquire all relevant locks, so that after releasing the locks */ + /* the child will see a consistent state in which monitor */ + /* invariants hold. Unfortunately, we can't acquire libc locks */ + /* we might need, and there seems to be no guarantee that libc */ + /* must install a suitable fork handler. */ + /* Wait for an ongoing GC to finish, since we can't finish it in */ + /* the (one remaining thread in) the child. */ + + LOCK(); + DISABLE_CANCEL(fork_cancel_state); + /* Following waits may include cancellation points. */ +# ifdef PARALLEL_MARK + if (GC_parallel) + wait_for_reclaim_atfork(); +# endif + GC_wait_for_gc_completion(TRUE); +# ifdef PARALLEL_MARK + if (GC_parallel) { +# if defined(THREAD_SANITIZER) && defined(GC_ASSERTIONS) \ + && defined(CAN_CALL_ATFORK) + /* Prevent TSan false positive about the data race */ + /* when updating GC_mark_lock_holder. */ + GC_generic_lock(&mark_mutex); +# else + GC_acquire_mark_lock(); +# endif + } +# endif + GC_acquire_dirty_lock(); + } + + /* Called in parent after a fork() (even if the latter failed). */ +# if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK) + GC_ATTR_NO_SANITIZE_THREAD +# endif + static void fork_parent_proc(void) + { + GC_release_dirty_lock(); +# ifdef PARALLEL_MARK + if (GC_parallel) { +# if defined(THREAD_SANITIZER) && defined(GC_ASSERTIONS) \ + && defined(CAN_CALL_ATFORK) + /* To match that in fork_prepare_proc. */ + (void)pthread_mutex_unlock(&mark_mutex); +# else + GC_release_mark_lock(); +# endif + } +# endif + RESTORE_CANCEL(fork_cancel_state); + UNLOCK(); + } + + /* Called in child after a fork(). */ +# if defined(GC_ASSERTIONS) && defined(CAN_CALL_ATFORK) + GC_ATTR_NO_SANITIZE_THREAD +# endif + static void fork_child_proc(void) + { + GC_release_dirty_lock(); +# ifndef GC_DISABLE_INCREMENTAL + GC_dirty_update_child(); +# endif +# ifdef PARALLEL_MARK + if (GC_parallel) { +# if defined(THREAD_SANITIZER) && defined(GC_ASSERTIONS) \ + && defined(CAN_CALL_ATFORK) + (void)pthread_mutex_unlock(&mark_mutex); +# else + GC_release_mark_lock(); +# endif + /* Turn off parallel marking in the child, since we are probably */ + /* just going to exec, and we would have to restart mark threads. */ + GC_parallel = FALSE; + } +# ifdef THREAD_SANITIZER + /* TSan does not support threads creation in the child process. */ + GC_available_markers_m1 = 0; +# endif +# endif + /* Clean up the thread table, so that just our thread is left. */ + GC_remove_all_threads_but_me(); + RESTORE_CANCEL(fork_cancel_state); + UNLOCK(); + /* Even though after a fork the child only inherits the single */ + /* thread that called the fork(), if another thread in the parent */ + /* was attempting to lock the mutex while being held in */ + /* fork_child_prepare(), the mutex will be left in an inconsistent */ + /* state in the child after the UNLOCK. This is the case, at */ + /* least, in Mac OS X and leads to an unusable GC in the child */ + /* which will block when attempting to perform any GC operation */ + /* that acquires the allocation mutex. */ +# if defined(USE_PTHREAD_LOCKS) && !defined(GC_WIN32_THREADS) + GC_ASSERT(I_DONT_HOLD_LOCK()); + /* Reinitialize the mutex. It should be safe since we are */ + /* running this in the child which only inherits a single thread. */ + /* pthread_mutex_destroy() and pthread_rwlock_destroy() may */ + /* return EBUSY, which makes no sense, but that is the reason for */ + /* the need of the reinitialization. */ + /* Note: excluded for Cygwin as does not seem to be needed. */ +# ifdef USE_RWLOCK + (void)pthread_rwlock_destroy(&GC_allocate_ml); +# ifdef DARWIN + /* A workaround for pthread_rwlock_init() fail with EBUSY. */ + { + pthread_rwlock_t rwlock_local = PTHREAD_RWLOCK_INITIALIZER; + BCOPY(&rwlock_local, &GC_allocate_ml, sizeof(GC_allocate_ml)); + } +# else + if (pthread_rwlock_init(&GC_allocate_ml, NULL) != 0) + ABORT("pthread_rwlock_init failed (in child)"); +# endif +# else + (void)pthread_mutex_destroy(&GC_allocate_ml); + /* TODO: Probably some targets might need the default mutex */ + /* attribute to be passed instead of NULL. */ + if (0 != pthread_mutex_init(&GC_allocate_ml, NULL)) + ABORT("pthread_mutex_init failed (in child)"); +# endif +# endif + } + + /* Routines for fork handling by client (no-op if pthread_atfork works). */ + GC_API void GC_CALL GC_atfork_prepare(void) + { + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + if (GC_handle_fork <= 0) + fork_prepare_proc(); + } + + GC_API void GC_CALL GC_atfork_parent(void) + { + if (GC_handle_fork <= 0) + fork_parent_proc(); + } + + GC_API void GC_CALL GC_atfork_child(void) + { + if (GC_handle_fork <= 0) + fork_child_proc(); + } + + /* Prepare for forks if requested. */ + GC_INNER_WIN32THREAD void GC_setup_atfork(void) + { + if (GC_handle_fork) { +# ifdef CAN_CALL_ATFORK + if (pthread_atfork(fork_prepare_proc, fork_parent_proc, + fork_child_proc) == 0) { + /* Handlers successfully registered. */ + GC_handle_fork = 1; + } else +# endif + /* else */ if (GC_handle_fork != -1) + ABORT("pthread_atfork failed"); + } + } + +#endif /* CAN_HANDLE_FORK */ + +#ifdef INCLUDE_LINUX_THREAD_DESCR + __thread int GC_dummy_thread_local; +#endif + +#ifdef PARALLEL_MARK +# ifndef GC_WIN32_THREADS + static void setup_mark_lock(void); +# endif + + GC_INNER_WIN32THREAD unsigned GC_required_markers_cnt = 0; + /* The default value (0) means the number of */ + /* markers should be selected automatically. */ + + GC_API void GC_CALL GC_set_markers_count(unsigned markers) + { + GC_required_markers_cnt = markers < MAX_MARKERS ? markers : MAX_MARKERS; + } +#endif /* PARALLEL_MARK */ + +GC_INNER GC_bool GC_in_thread_creation = FALSE; + /* protected by the allocator lock */ + +GC_INNER_WIN32THREAD void GC_record_stack_base(GC_stack_context_t crtn, + const struct GC_stack_base *sb) +{ +# if !defined(GC_DARWIN_THREADS) && !defined(GC_WIN32_THREADS) + crtn -> stack_ptr = (ptr_t)(sb -> mem_base); +# endif + if ((crtn -> stack_end = (ptr_t)(sb -> mem_base)) == NULL) + ABORT("Bad stack base in GC_register_my_thread"); +# ifdef E2K + crtn -> ps_ofs = (size_t)(word)(sb -> reg_base); +# elif defined(IA64) + crtn -> backing_store_end = (ptr_t)(sb -> reg_base); +# elif defined(I386) && defined(GC_WIN32_THREADS) + crtn -> initial_stack_base = (ptr_t)(sb -> mem_base); +# endif +} + +#if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS) \ + || !defined(DONT_USE_ATEXIT) + GC_INNER_WIN32THREAD thread_id_t GC_main_thread_id; +#endif + +#ifndef DONT_USE_ATEXIT + GC_INNER GC_bool GC_is_main_thread(void) + { + GC_ASSERT(GC_thr_initialized); + return THREAD_ID_EQUAL(GC_main_thread_id, thread_id_self()); + } +#endif /* !DONT_USE_ATEXIT */ + +#ifndef GC_WIN32_THREADS + +STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb, + thread_id_t self_id) +{ + GC_thread me; + + GC_ASSERT(I_HOLD_LOCK()); + me = GC_new_thread(self_id); + me -> id = self_id; +# ifdef GC_DARWIN_THREADS + me -> mach_thread = mach_thread_self(); +# endif + GC_record_stack_base(me -> crtn, sb); + return me; +} + + STATIC int GC_nprocs = 1; + /* Number of processors. We may not have */ + /* access to all of them, but this is as good */ + /* a guess as any ... */ + +GC_INNER void GC_thr_init(void) +{ + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(!GC_thr_initialized); + GC_ASSERT((word)(&GC_threads) % sizeof(word) == 0); +# ifdef GC_ASSERTIONS + GC_thr_initialized = TRUE; +# endif +# ifdef CAN_HANDLE_FORK + GC_setup_atfork(); +# endif + +# ifdef INCLUDE_LINUX_THREAD_DESCR + /* Explicitly register the region including the address */ + /* of a thread local variable. This should include thread */ + /* locals for the main thread, except for those allocated */ + /* in response to dlopen calls. */ + { + ptr_t thread_local_addr = (ptr_t)(&GC_dummy_thread_local); + ptr_t main_thread_start, main_thread_end; + if (!GC_enclosing_writable_mapping(thread_local_addr, + &main_thread_start, &main_thread_end)) { + ABORT("Failed to find mapping for main thread thread locals"); + } else { + /* main_thread_start and main_thread_end are initialized. */ + GC_add_roots_inner(main_thread_start, main_thread_end, FALSE); + } + } +# endif + + /* Set GC_nprocs and GC_available_markers_m1. */ + { + char * nprocs_string = GETENV("GC_NPROCS"); + GC_nprocs = -1; + if (nprocs_string != NULL) GC_nprocs = atoi(nprocs_string); + } + if (GC_nprocs <= 0 +# if defined(ARM32) && defined(GC_LINUX_THREADS) && !defined(NACL) + && (GC_nprocs = GC_get_nprocs_present()) <= 1 + /* Workaround for some Linux/arm kernels */ +# endif + ) + { + GC_nprocs = GC_get_nprocs(); + } + if (GC_nprocs <= 0) { + WARN("GC_get_nprocs() returned %" WARN_PRIdPTR "\n", + (signed_word)GC_nprocs); + GC_nprocs = 2; /* assume dual-core */ +# ifdef PARALLEL_MARK + GC_available_markers_m1 = 0; /* but use only one marker */ +# endif + } else { +# ifdef PARALLEL_MARK + { + char * markers_string = GETENV("GC_MARKERS"); + int markers = GC_required_markers_cnt; + + if (markers_string != NULL) { + markers = atoi(markers_string); + if (markers <= 0 || markers > MAX_MARKERS) { + WARN("Too big or invalid number of mark threads: %" WARN_PRIdPTR + "; using maximum threads\n", (signed_word)markers); + markers = MAX_MARKERS; + } + } else if (0 == markers) { + /* Unless the client sets the desired number of */ + /* parallel markers, it is determined based on the */ + /* number of CPU cores. */ + markers = GC_nprocs; +# if defined(GC_MIN_MARKERS) && !defined(CPPCHECK) + /* This is primarily for targets without getenv(). */ + if (markers < GC_MIN_MARKERS) + markers = GC_MIN_MARKERS; +# endif + if (markers > MAX_MARKERS) + markers = MAX_MARKERS; /* silently limit the value */ + } + GC_available_markers_m1 = markers - 1; + } +# endif + } + GC_COND_LOG_PRINTF("Number of processors: %d\n", GC_nprocs); + +# if defined(BASE_ATOMIC_OPS_EMULATED) && defined(SIGNAL_BASED_STOP_WORLD) + /* Ensure the process is running on just one CPU core. */ + /* This is needed because the AO primitives emulated with */ + /* locks cannot be used inside signal handlers. */ + { + cpu_set_t mask; + int cpu_set_cnt = 0; + int cpu_lowest_set = 0; +# ifdef RANDOM_ONE_CPU_CORE + int cpu_highest_set = 0; +# endif + int i = GC_nprocs > 1 ? GC_nprocs : 2; /* check at least 2 cores */ + + if (sched_getaffinity(0 /* current process */, + sizeof(mask), &mask) == -1) + ABORT_ARG1("sched_getaffinity failed", ": errno= %d", errno); + while (i-- > 0) + if (CPU_ISSET(i, &mask)) { +# ifdef RANDOM_ONE_CPU_CORE + if (i + 1 != cpu_lowest_set) cpu_highest_set = i; +# endif + cpu_lowest_set = i; + cpu_set_cnt++; + } + if (0 == cpu_set_cnt) + ABORT("sched_getaffinity returned empty mask"); + if (cpu_set_cnt > 1) { +# ifdef RANDOM_ONE_CPU_CORE + if (cpu_lowest_set < cpu_highest_set) { + /* Pseudo-randomly adjust the bit to set among valid ones. */ + cpu_lowest_set += (unsigned)getpid() % + (cpu_highest_set - cpu_lowest_set + 1); + } +# endif + CPU_ZERO(&mask); + CPU_SET(cpu_lowest_set, &mask); /* select just one CPU */ + if (sched_setaffinity(0, sizeof(mask), &mask) == -1) + ABORT_ARG1("sched_setaffinity failed", ": errno= %d", errno); + WARN("CPU affinity mask is set to %p\n", (word)1 << cpu_lowest_set); + } + } +# endif /* BASE_ATOMIC_OPS_EMULATED */ + +# ifndef GC_DARWIN_THREADS + GC_stop_init(); +# endif + +# ifdef PARALLEL_MARK + if (GC_available_markers_m1 <= 0) { + /* Disable parallel marking. */ + GC_parallel = FALSE; + GC_COND_LOG_PRINTF( + "Single marker thread, turning off parallel marking\n"); + } else { + setup_mark_lock(); + } +# endif + + /* Add the initial thread, so we can stop it. */ + { + struct GC_stack_base sb; + GC_thread me; + thread_id_t self_id = thread_id_self(); + + sb.mem_base = GC_stackbottom; + GC_ASSERT(sb.mem_base != NULL); +# if defined(E2K) || defined(IA64) + sb.reg_base = GC_register_stackbottom; +# endif + GC_ASSERT(NULL == GC_self_thread_inner()); + me = GC_register_my_thread_inner(&sb, self_id); +# ifndef DONT_USE_ATEXIT + GC_main_thread_id = self_id; +# endif + me -> flags = DETACHED; + } +} + +#endif /* !GC_WIN32_THREADS */ + +/* Perform all initializations, including those that may require */ +/* allocation, e.g. initialize thread local free lists if used. */ +/* Must be called before a thread is created. */ +GC_INNER void GC_init_parallel(void) +{ +# ifdef THREAD_LOCAL_ALLOC + GC_thread me; + + GC_ASSERT(GC_is_initialized); + LOCK(); + me = GC_self_thread_inner(); + GC_init_thread_local(&me->tlfs); + UNLOCK(); +# endif +# if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS) + if (GC_win32_dll_threads) { + set_need_to_lock(); + /* Cannot intercept thread creation. Hence we don't know if */ + /* other threads exist. However, client is not allowed to */ + /* create other threads before collector initialization. */ + /* Thus it's OK not to lock before this. */ + } +# endif +} + +#if !defined(GC_NO_PTHREAD_SIGMASK) && defined(GC_PTHREADS) + GC_API int WRAP_FUNC(pthread_sigmask)(int how, const sigset_t *set, + sigset_t *oset) + { +# ifdef GC_WIN32_THREADS + /* pthreads-win32 does not support sigmask. */ + /* So, nothing required here... */ +# else + sigset_t fudged_set; + + INIT_REAL_SYMS(); + if (EXPECT(set != NULL, TRUE) + && (how == SIG_BLOCK || how == SIG_SETMASK)) { + int sig_suspend = GC_get_suspend_signal(); + + fudged_set = *set; + GC_ASSERT(sig_suspend >= 0); + if (sigdelset(&fudged_set, sig_suspend) != 0) + ABORT("sigdelset failed"); + set = &fudged_set; + } +# endif + return REAL_FUNC(pthread_sigmask)(how, set, oset); + } +#endif /* !GC_NO_PTHREAD_SIGMASK */ + +/* Wrapper for functions that are likely to block for an appreciable */ +/* length of time. */ + +#ifdef E2K + /* Cannot be defined as a function because the stack-allocated buffer */ + /* (pointed to by bs_lo) should be preserved till completion of */ + /* GC_do_blocking_inner (or GC_suspend_self_blocked). */ +# define do_blocking_enter(pTopOfStackUnset, me) \ + do { \ + ptr_t bs_lo; \ + size_t stack_size; \ + GC_stack_context_t crtn = (me) -> crtn; \ + \ + *(pTopOfStackUnset) = FALSE; \ + crtn -> stack_ptr = GC_approx_sp(); \ + GC_ASSERT(NULL == crtn -> backing_store_end); \ + GET_PROCEDURE_STACK_LOCAL(crtn -> ps_ofs, \ + &bs_lo, &stack_size); \ + crtn -> backing_store_end = bs_lo; \ + crtn -> backing_store_ptr = bs_lo + stack_size; \ + (me) -> flags |= DO_BLOCKING; \ + } while (0) + +#else /* !E2K */ + static void do_blocking_enter(GC_bool *pTopOfStackUnset, GC_thread me) + { +# if defined(SPARC) || defined(IA64) + ptr_t bs_hi = GC_save_regs_in_stack(); + /* TODO: regs saving already done by GC_with_callee_saves_pushed */ +# endif + GC_stack_context_t crtn = me -> crtn; + + GC_ASSERT(I_HOLD_READER_LOCK()); + GC_ASSERT((me -> flags & DO_BLOCKING) == 0); + *pTopOfStackUnset = FALSE; +# ifdef SPARC + crtn -> stack_ptr = bs_hi; +# else + crtn -> stack_ptr = GC_approx_sp(); +# endif +# if defined(GC_DARWIN_THREADS) && !defined(DARWIN_DONT_PARSE_STACK) + if (NULL == crtn -> topOfStack) { + /* GC_do_blocking_inner is not called recursively, */ + /* so topOfStack should be computed now. */ + *pTopOfStackUnset = TRUE; + crtn -> topOfStack = GC_FindTopOfStack(0); + } +# endif +# ifdef IA64 + crtn -> backing_store_ptr = bs_hi; +# endif + me -> flags |= DO_BLOCKING; + /* Save context here if we want to support precise stack marking. */ + } +#endif /* !E2K */ + +static void do_blocking_leave(GC_thread me, GC_bool topOfStackUnset) +{ + GC_ASSERT(I_HOLD_READER_LOCK()); + me -> flags &= (unsigned char)~DO_BLOCKING; +# ifdef E2K + { + GC_stack_context_t crtn = me -> crtn; + + GC_ASSERT(crtn -> backing_store_end != NULL); + crtn -> backing_store_ptr = NULL; + crtn -> backing_store_end = NULL; + } +# endif +# if defined(GC_DARWIN_THREADS) && !defined(DARWIN_DONT_PARSE_STACK) + if (topOfStackUnset) + me -> crtn -> topOfStack = NULL; /* make it unset again */ +# else + (void)topOfStackUnset; +# endif +} + +GC_INNER void GC_do_blocking_inner(ptr_t data, void *context) +{ + struct blocking_data *d = (struct blocking_data *)data; + GC_thread me; + GC_bool topOfStackUnset; + + UNUSED_ARG(context); + READER_LOCK(); + me = GC_self_thread_inner(); + do_blocking_enter(&topOfStackUnset, me); + READER_UNLOCK_RELEASE(); + + d -> client_data = (d -> fn)(d -> client_data); + + READER_LOCK(); /* This will block if the world is stopped. */ +# ifdef LINT2 + { +# ifdef GC_ASSERTIONS + GC_thread saved_me = me; +# endif + + /* The pointer to the GC thread descriptor should not be */ + /* changed while the thread is registered but a static */ + /* analysis tool might complain that this pointer value */ + /* (obtained in the first locked section) is unreliable in */ + /* the second locked section. */ + me = GC_self_thread_inner(); + GC_ASSERT(me == saved_me); + } +# endif +# if defined(GC_ENABLE_SUSPEND_THREAD) && defined(SIGNAL_BASED_STOP_WORLD) + /* Note: this code cannot be moved into do_blocking_leave() */ + /* otherwise there could be a static analysis tool warning */ + /* (false positive) about unlock without a matching lock. */ + while (EXPECT((me -> ext_suspend_cnt & 1) != 0, FALSE)) { + word suspend_cnt = (word)(me -> ext_suspend_cnt); + /* read suspend counter (number) before unlocking */ + + READER_UNLOCK_RELEASE(); + GC_suspend_self_inner(me, suspend_cnt); + READER_LOCK(); + } +# endif + do_blocking_leave(me, topOfStackUnset); + READER_UNLOCK_RELEASE(); +} + +#if defined(GC_ENABLE_SUSPEND_THREAD) && defined(SIGNAL_BASED_STOP_WORLD) + /* Similar to GC_do_blocking_inner() but assuming the allocator lock */ + /* is held and fn is GC_suspend_self_inner. */ + GC_INNER void GC_suspend_self_blocked(ptr_t thread_me, void *context) + { + GC_thread me = (GC_thread)thread_me; + GC_bool topOfStackUnset; + + UNUSED_ARG(context); + GC_ASSERT(I_HOLD_LOCK()); + /* The caller holds the allocator lock in the */ + /* exclusive mode, thus we require and restore */ + /* it to the same mode upon return from the */ + /* function. */ + do_blocking_enter(&topOfStackUnset, me); + while ((me -> ext_suspend_cnt & 1) != 0) { + word suspend_cnt = (word)(me -> ext_suspend_cnt); + + UNLOCK(); + GC_suspend_self_inner(me, suspend_cnt); + LOCK(); + } + do_blocking_leave(me, topOfStackUnset); + } +#endif /* GC_ENABLE_SUSPEND_THREAD */ + +GC_API void GC_CALL GC_set_stackbottom(void *gc_thread_handle, + const struct GC_stack_base *sb) +{ + GC_thread t = (GC_thread)gc_thread_handle; + GC_stack_context_t crtn; + + GC_ASSERT(sb -> mem_base != NULL); + if (!EXPECT(GC_is_initialized, TRUE)) { + GC_ASSERT(NULL == t); + /* Alter the stack bottom of the primordial thread. */ + GC_stackbottom = (char*)(sb -> mem_base); +# if defined(E2K) || defined(IA64) + GC_register_stackbottom = (ptr_t)(sb -> reg_base); +# endif + return; + } + + GC_ASSERT(I_HOLD_READER_LOCK()); + if (NULL == t) /* current thread? */ + t = GC_self_thread_inner(); + GC_ASSERT(!KNOWN_FINISHED(t)); + crtn = t -> crtn; + GC_ASSERT((t -> flags & DO_BLOCKING) == 0 + && NULL == crtn -> traced_stack_sect); /* for now */ + + crtn -> stack_end = (ptr_t)(sb -> mem_base); +# ifdef E2K + crtn -> ps_ofs = (size_t)(word)(sb -> reg_base); +# elif defined(IA64) + crtn -> backing_store_end = (ptr_t)(sb -> reg_base); +# endif +# ifdef GC_WIN32_THREADS + /* Reset the known minimum (hottest address in the stack). */ + crtn -> last_stack_min = ADDR_LIMIT; +# endif +} + +GC_API void * GC_CALL GC_get_my_stackbottom(struct GC_stack_base *sb) +{ + GC_thread me; + GC_stack_context_t crtn; + + READER_LOCK(); + me = GC_self_thread_inner(); + /* The thread is assumed to be registered. */ + crtn = me -> crtn; + sb -> mem_base = crtn -> stack_end; +# ifdef E2K + sb -> reg_base = (void *)(word)(crtn -> ps_ofs); +# elif defined(IA64) + sb -> reg_base = crtn -> backing_store_end; +# endif + READER_UNLOCK(); + return (void *)me; /* gc_thread_handle */ +} + +/* GC_call_with_gc_active() has the opposite to GC_do_blocking() */ +/* functionality. It might be called from a user function invoked by */ +/* GC_do_blocking() to temporarily back allow calling any GC function */ +/* and/or manipulating pointers to the garbage collected heap. */ +GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, void *client_data) +{ + struct GC_traced_stack_sect_s stacksect; + GC_thread me; + GC_stack_context_t crtn; + ptr_t stack_end; +# ifdef E2K + ptr_t saved_bs_ptr, saved_bs_end; + size_t saved_ps_ofs; +# endif + + READER_LOCK(); /* This will block if the world is stopped. */ + me = GC_self_thread_inner(); + crtn = me -> crtn; + + /* Adjust our stack bottom value (this could happen unless */ + /* GC_get_stack_base() was used which returned GC_SUCCESS). */ + stack_end = crtn -> stack_end; /* read of a volatile field */ + GC_ASSERT(stack_end != NULL); + if ((word)stack_end HOTTER_THAN (word)(&stacksect)) { + crtn -> stack_end = (ptr_t)(&stacksect); +# if defined(I386) && defined(GC_WIN32_THREADS) + crtn -> initial_stack_base = (ptr_t)(&stacksect); +# endif + } + + if ((me -> flags & DO_BLOCKING) == 0) { + /* We are not inside GC_do_blocking() - do nothing more. */ + READER_UNLOCK_RELEASE(); + /* Cast fn to a volatile type to prevent its call inlining. */ + client_data = (*(GC_fn_type volatile *)&fn)(client_data); + /* Prevent treating the above as a tail call. */ + GC_noop1(COVERT_DATAFLOW(&stacksect)); + return client_data; /* result */ + } + +# if defined(GC_ENABLE_SUSPEND_THREAD) && defined(SIGNAL_BASED_STOP_WORLD) + while (EXPECT((me -> ext_suspend_cnt & 1) != 0, FALSE)) { + word suspend_cnt = (word)(me -> ext_suspend_cnt); + READER_UNLOCK_RELEASE(); + GC_suspend_self_inner(me, suspend_cnt); + READER_LOCK(); + GC_ASSERT(me -> crtn == crtn); + } +# endif + + /* Setup new "stack section". */ + stacksect.saved_stack_ptr = crtn -> stack_ptr; +# ifdef E2K + GC_ASSERT(crtn -> backing_store_end != NULL); + { + unsigned long long sz_ull; + + GET_PROCEDURE_STACK_SIZE_INNER(&sz_ull); + saved_ps_ofs = crtn -> ps_ofs; + GC_ASSERT(saved_ps_ofs <= (size_t)sz_ull); + crtn -> ps_ofs = (size_t)sz_ull; + } + saved_bs_end = crtn -> backing_store_end; + saved_bs_ptr = crtn -> backing_store_ptr; + crtn -> backing_store_ptr = NULL; + crtn -> backing_store_end = NULL; +# elif defined(IA64) + /* This is the same as in GC_call_with_stack_base(). */ + stacksect.backing_store_end = GC_save_regs_in_stack(); + /* Unnecessarily flushes register stack, */ + /* but that probably doesn't hurt. */ + stacksect.saved_backing_store_ptr = crtn -> backing_store_ptr; +# endif + stacksect.prev = crtn -> traced_stack_sect; + me -> flags &= (unsigned char)~DO_BLOCKING; + crtn -> traced_stack_sect = &stacksect; + + READER_UNLOCK_RELEASE(); + client_data = (*(GC_fn_type volatile *)&fn)(client_data); + GC_ASSERT((me -> flags & DO_BLOCKING) == 0); + + /* Restore original "stack section". */ + READER_LOCK(); + GC_ASSERT(me -> crtn == crtn); + GC_ASSERT(crtn -> traced_stack_sect == &stacksect); +# ifdef CPPCHECK + GC_noop1((word)(crtn -> traced_stack_sect)); +# endif + crtn -> traced_stack_sect = stacksect.prev; +# ifdef E2K + GC_ASSERT(NULL == crtn -> backing_store_end); + crtn -> backing_store_end = saved_bs_end; + crtn -> backing_store_ptr = saved_bs_ptr; + crtn -> ps_ofs = saved_ps_ofs; +# elif defined(IA64) + crtn -> backing_store_ptr = stacksect.saved_backing_store_ptr; +# endif + me -> flags |= DO_BLOCKING; + crtn -> stack_ptr = stacksect.saved_stack_ptr; + READER_UNLOCK_RELEASE(); + return client_data; /* result */ +} + +STATIC void GC_unregister_my_thread_inner(GC_thread me) +{ + GC_ASSERT(I_HOLD_LOCK()); +# ifdef DEBUG_THREADS + GC_log_printf("Unregistering thread %p, gc_thread= %p, n_threads= %d\n", + (void *)(signed_word)(me -> id), (void *)me, + GC_count_threads()); +# endif + GC_ASSERT(!KNOWN_FINISHED(me)); +# if defined(THREAD_LOCAL_ALLOC) + GC_destroy_thread_local(&me->tlfs); +# endif +# ifdef NACL + GC_nacl_shutdown_gc_thread(); +# endif +# ifdef GC_PTHREADS +# if defined(GC_HAVE_PTHREAD_EXIT) || !defined(GC_NO_PTHREAD_CANCEL) + /* Handle DISABLED_GC flag which is set by the */ + /* intercepted pthread_cancel or pthread_exit. */ + if ((me -> flags & DISABLED_GC) != 0) { + GC_dont_gc--; + } +# endif + if ((me -> flags & DETACHED) == 0) { + me -> flags |= FINISHED; + } else +# endif + /* else */ { + GC_delete_thread(me); + } +# if defined(THREAD_LOCAL_ALLOC) + /* It is required to call remove_specific defined in specific.c. */ + GC_remove_specific(GC_thread_key); +# endif +} + +GC_API int GC_CALL GC_unregister_my_thread(void) +{ + GC_thread me; + IF_CANCEL(int cancel_state;) + + /* Client should not unregister the thread explicitly if it */ + /* is registered by DllMain, except for the main thread. */ +# if !defined(GC_NO_THREADS_DISCOVERY) && defined(GC_WIN32_THREADS) + GC_ASSERT(!GC_win32_dll_threads + || THREAD_ID_EQUAL(GC_main_thread_id, thread_id_self())); +# endif + + LOCK(); + DISABLE_CANCEL(cancel_state); + /* Wait for any GC that may be marking from our stack to */ + /* complete before we remove this thread. */ + GC_wait_for_gc_completion(FALSE); + me = GC_self_thread_inner(); +# ifdef DEBUG_THREADS + GC_log_printf( + "Called GC_unregister_my_thread on %p, gc_thread= %p\n", + (void *)(signed_word)thread_id_self(), (void *)me); +# endif + GC_ASSERT(THREAD_ID_EQUAL(me -> id, thread_id_self())); + GC_unregister_my_thread_inner(me); + RESTORE_CANCEL(cancel_state); + UNLOCK(); + return GC_SUCCESS; +} + +#if !defined(GC_NO_PTHREAD_CANCEL) && defined(GC_PTHREADS) + /* We should deal with the fact that apparently on Solaris and, */ + /* probably, on some Linux we can't collect while a thread is */ + /* exiting, since signals aren't handled properly. This currently */ + /* gives rise to deadlocks. The only workaround seen is to intercept */ + /* pthread_cancel() and pthread_exit(), and disable the collections */ + /* until the thread exit handler is called. That's ugly, because we */ + /* risk growing the heap unnecessarily. But it seems that we don't */ + /* really have an option in that the process is not in a fully */ + /* functional state while a thread is exiting. */ + GC_API int WRAP_FUNC(pthread_cancel)(pthread_t thread) + { +# ifdef CANCEL_SAFE + GC_thread t; +# endif + + INIT_REAL_SYMS(); +# ifdef CANCEL_SAFE + LOCK(); + t = GC_lookup_by_pthread(thread); + /* We test DISABLED_GC because pthread_exit could be called at */ + /* the same time. (If t is NULL then pthread_cancel should */ + /* return ESRCH.) */ + if (t != NULL && (t -> flags & DISABLED_GC) == 0) { + t -> flags |= DISABLED_GC; + GC_dont_gc++; + } + UNLOCK(); +# endif + return REAL_FUNC(pthread_cancel)(thread); + } +#endif /* !GC_NO_PTHREAD_CANCEL */ + +#ifdef GC_HAVE_PTHREAD_EXIT + GC_API GC_PTHREAD_EXIT_ATTRIBUTE void WRAP_FUNC(pthread_exit)(void *retval) + { + GC_thread me; + + INIT_REAL_SYMS(); + LOCK(); + me = GC_self_thread_inner(); + /* We test DISABLED_GC because someone else could call */ + /* pthread_cancel at the same time. */ + if (me != NULL && (me -> flags & DISABLED_GC) == 0) { + me -> flags |= DISABLED_GC; + GC_dont_gc++; + } + UNLOCK(); + + REAL_FUNC(pthread_exit)(retval); + } +#endif /* GC_HAVE_PTHREAD_EXIT */ + +GC_API void GC_CALL GC_allow_register_threads(void) +{ + /* Check GC is initialized and the current thread is registered. */ + GC_ASSERT(GC_self_thread() != NULL); + + INIT_REAL_SYMS(); /* to initialize symbols while single-threaded */ + GC_init_lib_bounds(); + GC_start_mark_threads(); + set_need_to_lock(); +} + +#if defined(PTHREAD_STOP_WORLD_IMPL) && !defined(NO_SIGNALS_UNBLOCK_IN_MAIN) \ + || defined(GC_EXPLICIT_SIGNALS_UNBLOCK) + /* Some targets (e.g., Solaris) might require this to be called when */ + /* doing thread registering from the thread destructor. */ + GC_INNER void GC_unblock_gc_signals(void) + { + sigset_t set; + + INIT_REAL_SYMS(); /* for pthread_sigmask */ + sigemptyset(&set); + sigaddset(&set, GC_get_suspend_signal()); + sigaddset(&set, GC_get_thr_restart_signal()); + if (REAL_FUNC(pthread_sigmask)(SIG_UNBLOCK, &set, NULL) != 0) + ABORT("pthread_sigmask failed"); + } +#endif /* PTHREAD_STOP_WORLD_IMPL || GC_EXPLICIT_SIGNALS_UNBLOCK */ + +GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *sb) +{ + GC_thread me; + + if (GC_need_to_lock == FALSE) + ABORT("Threads explicit registering is not previously enabled"); + + /* We lock here, since we want to wait for an ongoing GC. */ + LOCK(); + me = GC_self_thread_inner(); + if (EXPECT(NULL == me, TRUE)) { + me = GC_register_my_thread_inner(sb, thread_id_self()); +# ifdef GC_PTHREADS +# ifdef CPPCHECK + GC_noop1(me -> flags); +# endif + /* Treat as detached, since we do not need to worry about */ + /* pointer results. */ + me -> flags |= DETACHED; +# else + (void)me; +# endif + } else +# ifdef GC_PTHREADS + /* else */ if (KNOWN_FINISHED(me)) { + /* This code is executed when a thread is registered from the */ + /* client thread key destructor. */ +# ifdef NACL + GC_nacl_initialize_gc_thread(me); +# endif +# ifdef GC_DARWIN_THREADS + /* Reinitialize mach_thread to avoid thread_suspend fail */ + /* with MACH_SEND_INVALID_DEST error. */ + me -> mach_thread = mach_thread_self(); +# endif + GC_record_stack_base(me -> crtn, sb); + me -> flags &= (unsigned char)~FINISHED; /* but not DETACHED */ + } else +# endif + /* else */ { + UNLOCK(); + return GC_DUPLICATE; + } + +# ifdef THREAD_LOCAL_ALLOC + GC_init_thread_local(&me->tlfs); +# endif +# ifdef GC_EXPLICIT_SIGNALS_UNBLOCK + /* Since this could be executed from a thread destructor, */ + /* our signals might already be blocked. */ + GC_unblock_gc_signals(); +# endif +# if defined(GC_ENABLE_SUSPEND_THREAD) && defined(SIGNAL_BASED_STOP_WORLD) + if (EXPECT((me -> ext_suspend_cnt & 1) != 0, FALSE)) { + GC_with_callee_saves_pushed(GC_suspend_self_blocked, (ptr_t)me); + } +# endif + UNLOCK(); + return GC_SUCCESS; +} + +#if defined(GC_PTHREADS) \ + && !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) + + /* Called at thread exit. Never called for main thread. */ + /* That is OK, since it results in at most a tiny one-time */ + /* leak. And linuxthreads implementation does not reclaim */ + /* the primordial (main) thread resources or id anyway. */ + GC_INNER_PTHRSTART void GC_thread_exit_proc(void *arg) + { + GC_thread me = (GC_thread)arg; + IF_CANCEL(int cancel_state;) + +# ifdef DEBUG_THREADS + GC_log_printf("Called GC_thread_exit_proc on %p, gc_thread= %p\n", + (void *)(signed_word)(me -> id), (void *)me); +# endif + LOCK(); + DISABLE_CANCEL(cancel_state); + GC_wait_for_gc_completion(FALSE); + GC_unregister_my_thread_inner(me); + RESTORE_CANCEL(cancel_state); + UNLOCK(); + } + + GC_API int WRAP_FUNC(pthread_join)(pthread_t thread, void **retval) + { + int result; + GC_thread t; + + INIT_REAL_SYMS(); +# ifdef DEBUG_THREADS + GC_log_printf("thread %p is joining thread %p\n", + (void *)GC_PTHREAD_PTRVAL(pthread_self()), + (void *)GC_PTHREAD_PTRVAL(thread)); +# endif + + /* After the join, thread id may have been recycled. */ + READER_LOCK(); + t = (GC_thread)COVERT_DATAFLOW(GC_lookup_by_pthread(thread)); + /* This is guaranteed to be the intended one, since the thread id */ + /* cannot have been recycled by pthreads. */ + READER_UNLOCK(); + + result = REAL_FUNC(pthread_join)(thread, retval); +# if defined(GC_FREEBSD_THREADS) + /* On FreeBSD, the wrapped pthread_join() sometimes returns */ + /* (what appears to be) a spurious EINTR which caused the test */ + /* and real code to fail gratuitously. Having looked at system */ + /* pthread library source code, I see how such return code value */ + /* may be generated. In one path of the code, pthread_join just */ + /* returns the errno setting of the thread being joined - this */ + /* does not match the POSIX specification or the local man pages. */ + /* Thus, I have taken the liberty to catch this one spurious */ + /* return value. */ + if (EXPECT(result == EINTR, FALSE)) result = 0; +# endif + + if (EXPECT(0 == result, TRUE)) { + LOCK(); + /* Here the pthread id may have been recycled. Delete the thread */ + /* from GC_threads (unless it has been registered again from the */ + /* client thread key destructor). */ + if (KNOWN_FINISHED(t)) { + GC_delete_thread(t); + } + UNLOCK(); + } + +# ifdef DEBUG_THREADS + GC_log_printf("thread %p join with thread %p %s\n", + (void *)GC_PTHREAD_PTRVAL(pthread_self()), + (void *)GC_PTHREAD_PTRVAL(thread), + result != 0 ? "failed" : "succeeded"); +# endif + return result; + } + + GC_API int WRAP_FUNC(pthread_detach)(pthread_t thread) + { + int result; + GC_thread t; + + INIT_REAL_SYMS(); + READER_LOCK(); + t = (GC_thread)COVERT_DATAFLOW(GC_lookup_by_pthread(thread)); + READER_UNLOCK(); + result = REAL_FUNC(pthread_detach)(thread); + if (EXPECT(0 == result, TRUE)) { + LOCK(); + /* Here the pthread id may have been recycled. */ + if (KNOWN_FINISHED(t)) { + GC_delete_thread(t); + } else { + t -> flags |= DETACHED; + } + UNLOCK(); + } + return result; + } + + struct start_info { + void *(*start_routine)(void *); + void *arg; + sem_t registered; /* 1 ==> in our thread table, but */ + /* parent hasn't yet noticed. */ + unsigned char flags; + }; + + /* Called from GC_pthread_start_inner(). Defined in this file to */ + /* minimize the number of include files in pthread_start.c (because */ + /* sem_t and sem_post() are not used in that file directly). */ + GC_INNER_PTHRSTART GC_thread GC_start_rtn_prepare_thread( + void *(**pstart)(void *), + void **pstart_arg, + struct GC_stack_base *sb, void *arg) + { + struct start_info *psi = (struct start_info *)arg; + thread_id_t self_id = thread_id_self(); + GC_thread me; + +# ifdef DEBUG_THREADS + GC_log_printf("Starting thread %p, sp= %p\n", + (void *)GC_PTHREAD_PTRVAL(pthread_self()), (void *)&arg); +# endif + /* If a GC occurs before the thread is registered, that GC will */ + /* ignore this thread. That's fine, since it will block trying to */ + /* acquire the allocator lock, and won't yet hold interesting */ + /* pointers. */ + LOCK(); + /* We register the thread here instead of in the parent, so that */ + /* we don't need to hold the allocator lock during pthread_create. */ + me = GC_register_my_thread_inner(sb, self_id); + GC_ASSERT(me != &first_thread); + me -> flags = psi -> flags; +# ifdef GC_WIN32_THREADS + GC_win32_cache_self_pthread(self_id); +# endif +# ifdef THREAD_LOCAL_ALLOC + GC_init_thread_local(&me->tlfs); +# endif + UNLOCK(); + + *pstart = psi -> start_routine; + *pstart_arg = psi -> arg; +# if defined(DEBUG_THREADS) && defined(FUNCPTR_IS_WORD) + GC_log_printf("start_routine= %p\n", (void *)(word)(*pstart)); +# endif + sem_post(&(psi -> registered)); /* Last action on *psi; */ + /* OK to deallocate. */ + return me; + } + + STATIC void * GC_pthread_start(void * arg) + { +# ifdef INCLUDE_LINUX_THREAD_DESCR + struct GC_stack_base sb; + +# ifdef REDIRECT_MALLOC + /* GC_get_stack_base may call pthread_getattr_np, which can */ + /* unfortunately call realloc, which may allocate from an */ + /* unregistered thread. This is unpleasant, since it might */ + /* force heap growth (or, even, heap overflow). */ + GC_disable(); +# endif + if (GC_get_stack_base(&sb) != GC_SUCCESS) + ABORT("Failed to get thread stack base"); +# ifdef REDIRECT_MALLOC + GC_enable(); +# endif + return GC_pthread_start_inner(&sb, arg); +# else + return GC_call_with_stack_base(GC_pthread_start_inner, arg); +# endif + } + + GC_API int WRAP_FUNC(pthread_create)(pthread_t *new_thread, + GC_PTHREAD_CREATE_CONST pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg) + { + int result; + struct start_info si; + + GC_ASSERT(I_DONT_HOLD_LOCK()); + INIT_REAL_SYMS(); + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + GC_ASSERT(GC_thr_initialized); + + GC_init_lib_bounds(); + if (sem_init(&si.registered, GC_SEM_INIT_PSHARED, 0) != 0) + ABORT("sem_init failed"); + si.flags = 0; + si.start_routine = start_routine; + si.arg = arg; + + /* We resist the temptation to muck with the stack size here, */ + /* even if the default is unreasonably small. That is the client's */ + /* responsibility. */ +# ifdef GC_ASSERTIONS + { + size_t stack_size = 0; + if (NULL != attr) { + if (pthread_attr_getstacksize(attr, &stack_size) != 0) + ABORT("pthread_attr_getstacksize failed"); + } + if (0 == stack_size) { + pthread_attr_t my_attr; + + if (pthread_attr_init(&my_attr) != 0) + ABORT("pthread_attr_init failed"); + if (pthread_attr_getstacksize(&my_attr, &stack_size) != 0) + ABORT("pthread_attr_getstacksize failed"); + (void)pthread_attr_destroy(&my_attr); + } + /* On Solaris 10 and on Win32 with winpthreads, with the */ + /* default attr initialization, stack_size remains 0; fudge it. */ + if (EXPECT(0 == stack_size, FALSE)) { +# if !defined(SOLARIS) && !defined(GC_WIN32_PTHREADS) + WARN("Failed to get stack size for assertion checking\n", 0); +# endif + stack_size = 1000000; + } + GC_ASSERT(stack_size >= 65536); + /* Our threads may need to do some work for the GC. */ + /* Ridiculously small threads won't work, and they */ + /* probably wouldn't work anyway. */ + } +# endif + + if (attr != NULL) { + int detachstate; + + if (pthread_attr_getdetachstate(attr, &detachstate) != 0) + ABORT("pthread_attr_getdetachstate failed"); + if (PTHREAD_CREATE_DETACHED == detachstate) + si.flags |= DETACHED; + } + +# ifdef PARALLEL_MARK + if (EXPECT(!GC_parallel && GC_available_markers_m1 > 0, FALSE)) + GC_start_mark_threads(); +# endif +# ifdef DEBUG_THREADS + GC_log_printf("About to start new thread from thread %p\n", + (void *)GC_PTHREAD_PTRVAL(pthread_self())); +# endif + set_need_to_lock(); + result = REAL_FUNC(pthread_create)(new_thread, attr, + GC_pthread_start, &si); + + /* Wait until child has been added to the thread table. */ + /* This also ensures that we hold onto the stack-allocated si */ + /* until the child is done with it. */ + if (EXPECT(0 == result, TRUE)) { + IF_CANCEL(int cancel_state;) + + DISABLE_CANCEL(cancel_state); + /* pthread_create is not a cancellation point. */ + while (0 != sem_wait(&si.registered)) { +# if defined(GC_HAIKU_THREADS) + /* To workaround some bug in Haiku semaphores. */ + if (EACCES == errno) continue; +# endif + if (EINTR != errno) ABORT("sem_wait failed"); + } + RESTORE_CANCEL(cancel_state); + } + sem_destroy(&si.registered); + return result; + } + +#endif /* GC_PTHREADS && !SN_TARGET_ORBIS && !SN_TARGET_PSP2 */ + +#if ((defined(GC_PTHREADS_PARAMARK) || defined(USE_PTHREAD_LOCKS)) \ + && !defined(NO_PTHREAD_TRYLOCK)) || defined(USE_SPIN_LOCK) + /* Spend a few cycles in a way that can't introduce contention with */ + /* other threads. */ +# define GC_PAUSE_SPIN_CYCLES 10 + STATIC void GC_pause(void) + { + int i; + + for (i = 0; i < GC_PAUSE_SPIN_CYCLES; ++i) { + /* Something that's unlikely to be optimized away. */ +# if defined(AO_HAVE_compiler_barrier) \ + && !defined(BASE_ATOMIC_OPS_EMULATED) + AO_compiler_barrier(); +# else + GC_noop1(i); +# endif + } + } +#endif /* USE_SPIN_LOCK || !NO_PTHREAD_TRYLOCK */ + +#ifndef SPIN_MAX +# define SPIN_MAX 128 /* Maximum number of calls to GC_pause before */ + /* give up. */ +#endif + +#if (!defined(USE_SPIN_LOCK) && !defined(NO_PTHREAD_TRYLOCK) \ + && defined(USE_PTHREAD_LOCKS)) || defined(GC_PTHREADS_PARAMARK) + /* If we do not want to use the below spinlock implementation, either */ + /* because we don't have a GC_test_and_set implementation, or because */ + /* we don't want to risk sleeping, we can still try spinning on */ + /* pthread_mutex_trylock for a while. This appears to be very */ + /* beneficial in many cases. */ + /* I suspect that under high contention this is nearly always better */ + /* than the spin lock. But it is a bit slower on a uniprocessor. */ + /* Hence we still default to the spin lock. */ + /* This is also used to acquire the mark lock for the parallel */ + /* marker. */ + + /* Here we use a strict exponential backoff scheme. I don't know */ + /* whether that's better or worse than the above. We eventually */ + /* yield by calling pthread_mutex_lock(); it never makes sense to */ + /* explicitly sleep. */ + +# ifdef LOCK_STATS + /* Note that LOCK_STATS requires AO_HAVE_test_and_set. */ + volatile AO_t GC_spin_count = 0; + volatile AO_t GC_block_count = 0; + volatile AO_t GC_unlocked_count = 0; +# endif + + STATIC void GC_generic_lock(pthread_mutex_t * lock) + { +# ifndef NO_PTHREAD_TRYLOCK + unsigned pause_length = 1; + unsigned i; + + if (EXPECT(0 == pthread_mutex_trylock(lock), TRUE)) { +# ifdef LOCK_STATS + (void)AO_fetch_and_add1(&GC_unlocked_count); +# endif + return; + } + for (; pause_length <= SPIN_MAX; pause_length <<= 1) { + for (i = 0; i < pause_length; ++i) { + GC_pause(); + } + switch (pthread_mutex_trylock(lock)) { + case 0: +# ifdef LOCK_STATS + (void)AO_fetch_and_add1(&GC_spin_count); +# endif + return; + case EBUSY: + break; + default: + ABORT("Unexpected error from pthread_mutex_trylock"); + } + } +# endif /* !NO_PTHREAD_TRYLOCK */ +# ifdef LOCK_STATS + (void)AO_fetch_and_add1(&GC_block_count); +# endif + pthread_mutex_lock(lock); + } +#endif /* !USE_SPIN_LOCK || ... */ + +#if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) + GC_INNER volatile unsigned char GC_collecting = FALSE; + /* A hint that we are in the collector and */ + /* holding the allocator lock for an extended */ + /* period. */ + +# if defined(AO_HAVE_char_load) && !defined(BASE_ATOMIC_OPS_EMULATED) +# define is_collecting() ((GC_bool)AO_char_load(&GC_collecting)) +# else + /* GC_collecting is a hint, a potential data race between */ + /* GC_lock() and ENTER/EXIT_GC() is OK to ignore. */ +# define is_collecting() ((GC_bool)GC_collecting) +# endif +#endif /* GC_PTHREADS && !GC_WIN32_THREADS */ + +#ifdef GC_ASSERTIONS + GC_INNER unsigned long GC_lock_holder = NO_THREAD; +#endif + +#if defined(USE_SPIN_LOCK) + /* Reasonably fast spin locks. Basically the same implementation */ + /* as STL alloc.h. This isn't really the right way to do this. */ + /* but until the POSIX scheduling mess gets straightened out ... */ + + GC_INNER volatile AO_TS_t GC_allocate_lock = AO_TS_INITIALIZER; + +# define low_spin_max 30 /* spin cycles if we suspect uniprocessor */ +# define high_spin_max SPIN_MAX /* spin cycles for multiprocessor */ + + static volatile AO_t spin_max = low_spin_max; + static volatile AO_t last_spins = 0; + /* A potential data race between */ + /* threads invoking GC_lock which reads */ + /* and updates spin_max and last_spins */ + /* could be ignored because these */ + /* variables are hints only. */ + + GC_INNER void GC_lock(void) + { + unsigned my_spin_max; + unsigned my_last_spins; + unsigned i; + + if (EXPECT(AO_test_and_set_acquire(&GC_allocate_lock) + == AO_TS_CLEAR, TRUE)) { + return; + } + my_spin_max = (unsigned)AO_load(&spin_max); + my_last_spins = (unsigned)AO_load(&last_spins); + for (i = 0; i < my_spin_max; i++) { + if (is_collecting() || GC_nprocs == 1) + goto yield; + if (i < my_last_spins/2) { + GC_pause(); + continue; + } + if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_CLEAR) { + /* + * got it! + * Spinning worked. Thus we're probably not being scheduled + * against the other process with which we were contending. + * Thus it makes sense to spin longer the next time. + */ + AO_store(&last_spins, (AO_t)i); + AO_store(&spin_max, (AO_t)high_spin_max); + return; + } + } + /* We are probably being scheduled against the other process. Sleep. */ + AO_store(&spin_max, (AO_t)low_spin_max); + yield: + for (i = 0;; ++i) { + if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_CLEAR) { + return; + } +# define SLEEP_THRESHOLD 12 + /* Under Linux very short sleeps tend to wait until */ + /* the current time quantum expires. On old Linux */ + /* kernels nanosleep (<= 2 ms) just spins. */ + /* (Under 2.4, this happens only for real-time */ + /* processes.) We want to minimize both behaviors */ + /* here. */ + if (i < SLEEP_THRESHOLD) { + sched_yield(); + } else { + struct timespec ts; + + if (i > 24) i = 24; + /* Don't wait for more than about 15 ms, */ + /* even under extreme contention. */ + + ts.tv_sec = 0; + ts.tv_nsec = (unsigned32)1 << i; + nanosleep(&ts, 0); + } + } + } + +#elif defined(USE_PTHREAD_LOCKS) +# ifdef USE_RWLOCK + GC_INNER pthread_rwlock_t GC_allocate_ml = PTHREAD_RWLOCK_INITIALIZER; +# else + GC_INNER pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER; +# endif + +# ifndef NO_PTHREAD_TRYLOCK + GC_INNER void GC_lock(void) + { + if (1 == GC_nprocs || is_collecting()) { + pthread_mutex_lock(&GC_allocate_ml); + } else { + GC_generic_lock(&GC_allocate_ml); + } + } +# elif defined(GC_ASSERTIONS) + GC_INNER void GC_lock(void) + { +# ifdef USE_RWLOCK + (void)pthread_rwlock_wrlock(&GC_allocate_ml); /* exclusive */ +# else + pthread_mutex_lock(&GC_allocate_ml); +# endif + } +# endif /* NO_PTHREAD_TRYLOCK && GC_ASSERTIONS */ + +#endif /* !USE_SPIN_LOCK && USE_PTHREAD_LOCKS */ + +#ifdef GC_PTHREADS_PARAMARK + +# if defined(GC_ASSERTIONS) && defined(GC_WIN32_THREADS) \ + && !defined(USE_PTHREAD_LOCKS) +# define NUMERIC_THREAD_ID(id) (unsigned long)(word)GC_PTHREAD_PTRVAL(id) + /* Id not guaranteed to be unique. */ +# endif + +# ifdef GC_ASSERTIONS + STATIC unsigned long GC_mark_lock_holder = NO_THREAD; +# define SET_MARK_LOCK_HOLDER \ + (void)(GC_mark_lock_holder = NUMERIC_THREAD_ID(pthread_self())) +# define UNSET_MARK_LOCK_HOLDER \ + do { \ + GC_ASSERT(GC_mark_lock_holder \ + == NUMERIC_THREAD_ID(pthread_self())); \ + GC_mark_lock_holder = NO_THREAD; \ + } while (0) +# else +# define SET_MARK_LOCK_HOLDER (void)0 +# define UNSET_MARK_LOCK_HOLDER (void)0 +# endif /* !GC_ASSERTIONS */ + + static pthread_cond_t builder_cv = PTHREAD_COND_INITIALIZER; + +# ifndef GC_WIN32_THREADS + static void setup_mark_lock(void) + { +# ifdef GLIBC_2_19_TSX_BUG + pthread_mutexattr_t mattr; + int glibc_minor = -1; + int glibc_major = GC_parse_version(&glibc_minor, + gnu_get_libc_version()); + + if (glibc_major > 2 || (glibc_major == 2 && glibc_minor >= 19)) { + /* TODO: disable this workaround for glibc with fixed TSX */ + /* This disables lock elision to workaround a bug in glibc 2.19+ */ + if (0 != pthread_mutexattr_init(&mattr)) { + ABORT("pthread_mutexattr_init failed"); + } + if (0 != pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL)) { + ABORT("pthread_mutexattr_settype failed"); + } + if (0 != pthread_mutex_init(&mark_mutex, &mattr)) { + ABORT("pthread_mutex_init failed"); + } + (void)pthread_mutexattr_destroy(&mattr); + } +# endif + } +# endif /* !GC_WIN32_THREADS */ + + GC_INNER void GC_acquire_mark_lock(void) + { +# if defined(NUMERIC_THREAD_ID_UNIQUE) && !defined(THREAD_SANITIZER) + GC_ASSERT(GC_mark_lock_holder != NUMERIC_THREAD_ID(pthread_self())); +# endif + GC_generic_lock(&mark_mutex); + SET_MARK_LOCK_HOLDER; + } + + GC_INNER void GC_release_mark_lock(void) + { + UNSET_MARK_LOCK_HOLDER; + if (pthread_mutex_unlock(&mark_mutex) != 0) { + ABORT("pthread_mutex_unlock failed"); + } + } + + /* Collector must wait for a freelist builders for 2 reasons: */ + /* 1) Mark bits may still be getting examined without lock. */ + /* 2) Partial free lists referenced only by locals may not be scanned */ + /* correctly, e.g. if they contain "pointer-free" objects, since */ + /* the free-list link may be ignored. */ + STATIC void GC_wait_builder(void) + { + ASSERT_CANCEL_DISABLED(); + UNSET_MARK_LOCK_HOLDER; + if (pthread_cond_wait(&builder_cv, &mark_mutex) != 0) { + ABORT("pthread_cond_wait failed"); + } + GC_ASSERT(GC_mark_lock_holder == NO_THREAD); + SET_MARK_LOCK_HOLDER; + } + + GC_INNER void GC_wait_for_reclaim(void) + { + GC_acquire_mark_lock(); + while (GC_fl_builder_count > 0) { + GC_wait_builder(); + } + GC_release_mark_lock(); + } + +# if defined(CAN_HANDLE_FORK) && defined(THREAD_SANITIZER) + /* Identical to GC_wait_for_reclaim() but with the no_sanitize */ + /* attribute as a workaround for TSan which does not notice that */ + /* the allocator lock is acquired in fork_prepare_proc(). */ + GC_ATTR_NO_SANITIZE_THREAD + static void wait_for_reclaim_atfork(void) + { + GC_acquire_mark_lock(); + while (GC_fl_builder_count > 0) + GC_wait_builder(); + GC_release_mark_lock(); + } +# endif /* CAN_HANDLE_FORK && THREAD_SANITIZER */ + + GC_INNER void GC_notify_all_builder(void) + { + GC_ASSERT(GC_mark_lock_holder == NUMERIC_THREAD_ID(pthread_self())); + if (pthread_cond_broadcast(&builder_cv) != 0) { + ABORT("pthread_cond_broadcast failed"); + } + } + + GC_INNER void GC_wait_marker(void) + { + ASSERT_CANCEL_DISABLED(); + GC_ASSERT(GC_parallel); + UNSET_MARK_LOCK_HOLDER; + if (pthread_cond_wait(&mark_cv, &mark_mutex) != 0) { + ABORT("pthread_cond_wait failed"); + } + GC_ASSERT(GC_mark_lock_holder == NO_THREAD); + SET_MARK_LOCK_HOLDER; + } + + GC_INNER void GC_notify_all_marker(void) + { + GC_ASSERT(GC_parallel); + if (pthread_cond_broadcast(&mark_cv) != 0) { + ABORT("pthread_cond_broadcast failed"); + } + } + +#endif /* GC_PTHREADS_PARAMARK */ + +GC_INNER GC_on_thread_event_proc GC_on_thread_event = 0; + +GC_API void GC_CALL GC_set_on_thread_event(GC_on_thread_event_proc fn) +{ + /* fn may be 0 (means no event notifier). */ + LOCK(); + GC_on_thread_event = fn; + UNLOCK(); +} + +GC_API GC_on_thread_event_proc GC_CALL GC_get_on_thread_event(void) +{ + GC_on_thread_event_proc fn; + + READER_LOCK(); + fn = GC_on_thread_event; + READER_UNLOCK(); + return fn; +} + +#ifdef STACKPTR_CORRECTOR_AVAILABLE + GC_INNER GC_sp_corrector_proc GC_sp_corrector = 0; +#endif + +GC_API void GC_CALL GC_set_sp_corrector(GC_sp_corrector_proc fn) +{ +# ifdef STACKPTR_CORRECTOR_AVAILABLE + LOCK(); + GC_sp_corrector = fn; + UNLOCK(); +# else + UNUSED_ARG(fn); +# endif +} + +GC_API GC_sp_corrector_proc GC_CALL GC_get_sp_corrector(void) +{ +# ifdef STACKPTR_CORRECTOR_AVAILABLE + GC_sp_corrector_proc fn; + + READER_LOCK(); + fn = GC_sp_corrector; + READER_UNLOCK(); + return fn; +# else + return 0; /* unsupported */ +# endif +} + +#ifdef PTHREAD_REGISTER_CANCEL_WEAK_STUBS + /* Workaround "undefined reference" linkage errors on some targets. */ + EXTERN_C_BEGIN + extern void __pthread_register_cancel(void) __attribute__((__weak__)); + extern void __pthread_unregister_cancel(void) __attribute__((__weak__)); + EXTERN_C_END + + void __pthread_register_cancel(void) {} + void __pthread_unregister_cancel(void) {} +#endif + +#undef do_blocking_enter + +#endif /* THREADS */ + +/* + * Copyright (c) 2000 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + /* To determine type of tsd impl. */ + /* Includes private/specific.h */ + /* if needed. */ + +#if defined(USE_CUSTOM_SPECIFIC) + +static const tse invalid_tse = {INVALID_QTID, 0, 0, INVALID_THREADID}; + /* A thread-specific data entry which will never */ + /* appear valid to a reader. Used to fill in empty */ + /* cache entries to avoid a check for 0. */ + +GC_INNER int GC_key_create_inner(tsd ** key_ptr) +{ + int i; + int ret; + tsd * result; + + GC_ASSERT(I_HOLD_LOCK()); + /* A quick alignment check, since we need atomic stores */ + GC_ASSERT((word)(&invalid_tse.next) % sizeof(tse *) == 0); + result = (tsd *)MALLOC_CLEAR(sizeof(tsd)); + if (NULL == result) return ENOMEM; + ret = pthread_mutex_init(&result->lock, NULL); + if (ret != 0) return ret; + for (i = 0; i < TS_CACHE_SIZE; ++i) { + result -> cache[i] = (/* no const */ tse *)(word)(&invalid_tse); + } +# ifdef GC_ASSERTIONS + for (i = 0; i < TS_HASH_SIZE; ++i) { + GC_ASSERT(result -> hash[i].p == 0); + } +# endif + *key_ptr = result; + return 0; +} + +/* Set the thread-local value associated with the key. Should not */ +/* be used to overwrite a previously set value. */ +GC_INNER int GC_setspecific(tsd * key, void * value) +{ + pthread_t self = pthread_self(); + unsigned hash_val = HASH(self); + volatile tse * entry; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(self != INVALID_THREADID); + GC_dont_gc++; /* disable GC */ + entry = (volatile tse *)MALLOC_CLEAR(sizeof(tse)); + GC_dont_gc--; + if (EXPECT(NULL == entry, FALSE)) return ENOMEM; + + pthread_mutex_lock(&(key -> lock)); + entry -> next = key->hash[hash_val].p; +# ifdef GC_ASSERTIONS + { + tse *p; + + /* Ensure no existing entry. */ + for (p = entry -> next; p != NULL; p = p -> next) { + GC_ASSERT(!THREAD_EQUAL(p -> thread, self)); + } + } +# endif + entry -> thread = self; + entry -> value = TS_HIDE_VALUE(value); + GC_ASSERT(entry -> qtid == INVALID_QTID); + /* There can only be one writer at a time, but this needs to be */ + /* atomic with respect to concurrent readers. */ + AO_store_release(&key->hash[hash_val].ao, (AO_t)entry); + GC_dirty((/* no volatile */ void *)(word)entry); + GC_dirty(key->hash + hash_val); + if (pthread_mutex_unlock(&key->lock) != 0) + ABORT("pthread_mutex_unlock failed (setspecific)"); + return 0; +} + +/* Remove thread-specific data for a given thread. This function is */ +/* called at fork from the child process for all threads except for the */ +/* survived one. GC_remove_specific() should be called on thread exit. */ +GC_INNER void GC_remove_specific_after_fork(tsd * key, pthread_t t) +{ + unsigned hash_val = HASH(t); + tse *entry; + tse *prev = NULL; + +# ifdef CAN_HANDLE_FORK + /* Both GC_setspecific and GC_remove_specific should be called */ + /* with the allocator lock held to ensure the consistency of */ + /* the hash table in the forked child. */ + GC_ASSERT(I_HOLD_LOCK()); +# endif + pthread_mutex_lock(&(key -> lock)); + entry = key->hash[hash_val].p; + while (entry != NULL && !THREAD_EQUAL(entry->thread, t)) { + prev = entry; + entry = entry->next; + } + /* Invalidate qtid field, since qtids may be reused, and a later */ + /* cache lookup could otherwise find this entry. */ + if (entry != NULL) { + entry -> qtid = INVALID_QTID; + if (NULL == prev) { + key->hash[hash_val].p = entry->next; + GC_dirty(key->hash + hash_val); + } else { + prev->next = entry->next; + GC_dirty(prev); + } + /* Atomic! concurrent accesses still work. */ + /* They must, since readers don't lock. */ + /* We shouldn't need a volatile access here, */ + /* since both this and the preceding write */ + /* should become visible no later than */ + /* the pthread_mutex_unlock() call. */ + } + /* If we wanted to deallocate the entry, we'd first have to clear */ + /* any cache entries pointing to it. That probably requires */ + /* additional synchronization, since we can't prevent a concurrent */ + /* cache lookup, which should still be examining deallocated memory.*/ + /* This can only happen if the concurrent access is from another */ + /* thread, and hence has missed the cache, but still... */ +# ifdef LINT2 + GC_noop1((word)entry); +# endif + + /* With GC, we're done, since the pointers from the cache will */ + /* be overwritten, all local pointers to the entries will be */ + /* dropped, and the entry will then be reclaimed. */ + if (pthread_mutex_unlock(&key->lock) != 0) + ABORT("pthread_mutex_unlock failed (remove_specific after fork)"); +} + +/* Note that even the slow path doesn't lock. */ +GC_INNER void * GC_slow_getspecific(tsd * key, word qtid, + tse * volatile * cache_ptr) +{ + pthread_t self = pthread_self(); + tse *entry = key->hash[HASH(self)].p; + + GC_ASSERT(qtid != INVALID_QTID); + while (entry != NULL && !THREAD_EQUAL(entry->thread, self)) { + entry = entry -> next; + } + if (entry == NULL) return NULL; + /* Set cache_entry. */ + entry -> qtid = (AO_t)qtid; + /* It's safe to do this asynchronously. Either value */ + /* is safe, though may produce spurious misses. */ + /* We're replacing one qtid with another one for the */ + /* same thread. */ + *cache_ptr = entry; + /* Again this is safe since pointer assignments are */ + /* presumed atomic, and either pointer is valid. */ + return TS_REVEAL_PTR(entry -> value); +} + +#ifdef GC_ASSERTIONS + /* Check that that all elements of the data structure associated */ + /* with key are marked. */ + void GC_check_tsd_marks(tsd *key) + { + int i; + tse *p; + + if (!GC_is_marked(GC_base(key))) { + ABORT("Unmarked thread-specific-data table"); + } + for (i = 0; i < TS_HASH_SIZE; ++i) { + for (p = key->hash[i].p; p != 0; p = p -> next) { + if (!GC_is_marked(GC_base(p))) { + ABORT_ARG1("Unmarked thread-specific-data entry", + " at %p", (void *)p); + } + } + } + for (i = 0; i < TS_CACHE_SIZE; ++i) { + p = key -> cache[i]; + if (p != &invalid_tse && !GC_is_marked(GC_base(p))) { + ABORT_ARG1("Unmarked cached thread-specific-data entry", + " at %p", (void *)p); + } + } + } +#endif /* GC_ASSERTIONS */ + +#endif /* USE_CUSTOM_SPECIFIC */ + +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2008 by Hewlett-Packard Development Company. + * All rights reserved. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + + +#if defined(GC_WIN32_THREADS) + +/* The allocator lock definition. */ +#ifndef USE_PTHREAD_LOCKS +# ifdef USE_RWLOCK + GC_INNER SRWLOCK GC_allocate_ml; +# else + GC_INNER CRITICAL_SECTION GC_allocate_ml; +# endif +#endif /* !USE_PTHREAD_LOCKS */ + +#undef CreateThread +#undef ExitThread +#undef _beginthreadex +#undef _endthreadex + +#if !defined(GC_PTHREADS) && !defined(MSWINCE) +# include /* for errno, EAGAIN */ +# include /* For _beginthreadex, _endthreadex */ +#endif + +static ptr_t copy_ptr_regs(word *regs, const CONTEXT *pcontext); + +#ifndef GC_NO_THREADS_DISCOVERY + /* This code operates in two distinct modes, depending on */ + /* the setting of GC_win32_dll_threads. */ + /* If GC_win32_dll_threads is set, all threads in the process */ + /* are implicitly registered with the GC by DllMain. */ + /* No explicit registration is required, and attempts at */ + /* explicit registration are ignored. This mode is */ + /* very different from the Posix operation of the collector. */ + /* In this mode access to the thread table is lock-free. */ + /* Hence there is a static limit on the number of threads. */ + + /* GC_DISCOVER_TASK_THREADS should be used if DllMain-based */ + /* thread registration is required but it is impossible to */ + /* call GC_use_threads_discovery before other GC routines. */ + +# ifndef GC_DISCOVER_TASK_THREADS + /* GC_win32_dll_threads must be set (if needed) at the */ + /* application initialization time, i.e. before any */ + /* collector or thread calls. We make it a "dynamic" */ + /* option only to avoid multiple library versions. */ + GC_INNER GC_bool GC_win32_dll_threads = FALSE; +# endif +#else + /* If GC_win32_dll_threads is FALSE (or the collector is */ + /* built without GC_DLL defined), things operate in a way */ + /* that is very similar to Posix platforms, and new threads */ + /* must be registered with the collector, e.g. by using */ + /* preprocessor-based interception of the thread primitives. */ + /* In this case, we use a real data structure for the thread */ + /* table. Note that there is no equivalent of linker-based */ + /* call interception, since we don't have ELF-like */ + /* facilities. The Windows analog appears to be "API */ + /* hooking", which really seems to be a standard way to */ + /* do minor binary rewriting (?). I'd prefer not to have */ + /* the basic collector rely on such facilities, but an */ + /* optional package that intercepts thread calls this way */ + /* would probably be nice. */ +# undef MAX_THREADS +# define MAX_THREADS 1 /* dll_thread_table[] is always empty. */ +#endif /* GC_NO_THREADS_DISCOVERY */ + +/* We have two versions of the thread table. Which one */ +/* we use depends on whether GC_win32_dll_threads */ +/* is set. Note that before initialization, we don't */ +/* add any entries to either table, even if DllMain is */ +/* called. The main thread will be added on */ +/* initialization. */ + +/* GC_use_threads_discovery() is currently incompatible with pthreads */ +/* and WinCE. It might be possible to get DllMain-based thread */ +/* registration to work with Cygwin, but if you try it then you are on */ +/* your own. */ +GC_API void GC_CALL GC_use_threads_discovery(void) +{ +# ifdef GC_NO_THREADS_DISCOVERY + ABORT("GC DllMain-based thread registration unsupported"); +# else + /* Turn on GC_win32_dll_threads. */ + GC_ASSERT(!GC_is_initialized); + /* Note that GC_use_threads_discovery is expected to be called by */ + /* the client application (not from DllMain) at start-up. */ +# ifndef GC_DISCOVER_TASK_THREADS + GC_win32_dll_threads = TRUE; +# endif + GC_init(); +# ifdef CPPCHECK + GC_noop1((word)(GC_funcptr_uint)&GC_DllMain); +# endif +# endif +} + +#ifndef GC_NO_THREADS_DISCOVERY + /* We track thread attachments while the world is supposed to be */ + /* stopped. Unfortunately, we cannot stop them from starting, since */ + /* blocking in DllMain seems to cause the world to deadlock. Thus, */ + /* we have to recover if we notice this in the middle of marking. */ + STATIC volatile AO_t GC_attached_thread = FALSE; + + /* We assumed that volatile ==> memory ordering, at least among */ + /* volatiles. This code should consistently use atomic_ops. */ + STATIC volatile GC_bool GC_please_stop = FALSE; +#elif defined(GC_ASSERTIONS) + STATIC GC_bool GC_please_stop = FALSE; +#endif /* GC_NO_THREADS_DISCOVERY && GC_ASSERTIONS */ + +#if defined(WRAP_MARK_SOME) && !defined(GC_PTHREADS) + /* Return TRUE if an thread was attached since we last asked or */ + /* since GC_attached_thread was explicitly reset. */ + GC_INNER GC_bool GC_started_thread_while_stopped(void) + { +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { +# ifdef AO_HAVE_compare_and_swap_release + if (AO_compare_and_swap_release(&GC_attached_thread, TRUE, + FALSE /* stored */)) + return TRUE; +# else + AO_nop_full(); /* Prior heap reads need to complete earlier. */ + if (AO_load(&GC_attached_thread)) { + AO_store(&GC_attached_thread, FALSE); + return TRUE; + } +# endif + } +# endif + return FALSE; + } +#endif /* WRAP_MARK_SOME */ + +/* Thread table used if GC_win32_dll_threads is set. */ +/* This is a fixed size array. */ +/* Since we use runtime conditionals, both versions */ +/* are always defined. */ +# ifndef MAX_THREADS +# define MAX_THREADS 512 +# endif + +/* Things may get quite slow for large numbers of threads, */ +/* since we look them up with sequential search. */ +static volatile struct GC_Thread_Rep dll_thread_table[MAX_THREADS]; +#ifndef GC_NO_THREADS_DISCOVERY + static struct GC_StackContext_Rep dll_crtn_table[MAX_THREADS]; +#endif + +STATIC volatile LONG GC_max_thread_index = 0; + /* Largest index in dll_thread_table */ + /* that was ever used. */ + +/* This may be called from DllMain, and hence operates under unusual */ +/* constraints. In particular, it must be lock-free if */ +/* GC_win32_dll_threads is set. Always called from the thread being */ +/* added. If GC_win32_dll_threads is not set, we already hold the */ +/* allocator lock except possibly during single-threaded startup code. */ +/* Does not initialize thread local free lists. */ +GC_INNER GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb, + thread_id_t self_id) +{ + GC_thread me; + +# ifdef GC_NO_THREADS_DISCOVERY + GC_ASSERT(I_HOLD_LOCK()); +# endif + /* The following should be a no-op according to the Win32 */ + /* documentation. There is empirical evidence that it */ + /* isn't. - HB */ +# if defined(MPROTECT_VDB) && !defined(CYGWIN32) + if (GC_auto_incremental +# ifdef GWW_VDB + && !GC_gww_dirty_init() +# endif + ) + GC_set_write_fault_handler(); +# endif + +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + int i; + /* It appears to be unsafe to acquire a lock here, since this */ + /* code is apparently not preemptible on some systems. */ + /* (This is based on complaints, not on Microsoft's official */ + /* documentation, which says this should perform "only simple */ + /* initialization tasks".) */ + /* Hence we make do with nonblocking synchronization. */ + /* It has been claimed that DllMain is really only executed with */ + /* a particular system lock held, and thus careful use of locking */ + /* around code that doesn't call back into the system libraries */ + /* might be OK. But this has not been tested across all Win32 */ + /* variants. */ + for (i = 0; + InterlockedExchange(&dll_thread_table[i].tm.long_in_use, 1) != 0; + i++) { + /* Compare-and-swap would make this cleaner, but that's not */ + /* supported before Windows 98 and NT 4.0. In Windows 2000, */ + /* InterlockedExchange is supposed to be replaced by */ + /* InterlockedExchangePointer, but that's not really what I */ + /* want here. */ + /* FIXME: We should eventually declare Windows 95 dead and use */ + /* AO_ primitives here. */ + if (i == MAX_THREADS - 1) + ABORT("Too many threads"); + } + /* Update GC_max_thread_index if necessary. The following is */ + /* safe, and unlike CompareExchange-based solutions seems to work */ + /* on all Windows 95 and later platforms. Unfortunately, */ + /* GC_max_thread_index may be temporarily out of bounds, so */ + /* readers have to compensate. */ + while (i > GC_max_thread_index) { + InterlockedIncrement((LONG *)&GC_max_thread_index); + /* Cast away volatile for older versions of Win32 headers. */ + } + if (EXPECT(GC_max_thread_index >= MAX_THREADS, FALSE)) { + /* We overshot due to simultaneous increments. */ + /* Setting it to MAX_THREADS-1 is always safe. */ + GC_max_thread_index = MAX_THREADS - 1; + } + me = (GC_thread)(dll_thread_table + i); + me -> crtn = &dll_crtn_table[i]; + } else +# endif + /* else */ /* Not using DllMain */ { + me = GC_new_thread(self_id); + } +# ifdef GC_PTHREADS + me -> pthread_id = pthread_self(); +# endif +# ifndef MSWINCE + /* GetCurrentThread() returns a pseudohandle (a const value). */ + if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), + GetCurrentProcess(), + (HANDLE*)&(me -> handle), + 0 /* dwDesiredAccess */, FALSE /* bInheritHandle */, + DUPLICATE_SAME_ACCESS)) { + ABORT_ARG1("DuplicateHandle failed", + ": errcode= 0x%X", (unsigned)GetLastError()); + } +# endif +# if defined(WOW64_THREAD_CONTEXT_WORKAROUND) && defined(MSWINRT_FLAVOR) + /* Lookup TIB value via a call to NtCurrentTeb() on thread */ + /* registration rather than calling GetThreadSelectorEntry() which */ + /* is not available on UWP. */ + me -> tib = (PNT_TIB)NtCurrentTeb(); +# endif + me -> crtn -> last_stack_min = ADDR_LIMIT; + GC_record_stack_base(me -> crtn, sb); + /* Up until this point, GC_push_all_stacks considers this thread */ + /* invalid. */ + /* Up until this point, this entry is viewed as reserved but invalid */ + /* by GC_win32_dll_lookup_thread. */ + ((volatile struct GC_Thread_Rep *)me) -> id = self_id; +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + if (GC_please_stop) { + AO_store(&GC_attached_thread, TRUE); + AO_nop_full(); /* Later updates must become visible after this. */ + } + /* We'd like to wait here, but can't, since waiting in DllMain */ + /* provokes deadlocks. */ + /* Thus we force marking to be restarted instead. */ + } else +# endif + /* else */ { + GC_ASSERT(!GC_please_stop); + /* Otherwise both we and the thread stopping code would be */ + /* holding the allocator lock. */ + } + return me; +} + +/* + * GC_max_thread_index may temporarily be larger than MAX_THREADS. + * To avoid subscript errors, we check on access. + */ +GC_INLINE LONG GC_get_max_thread_index(void) +{ + LONG my_max = GC_max_thread_index; + if (EXPECT(my_max >= MAX_THREADS, FALSE)) return MAX_THREADS - 1; + return my_max; +} + +#ifndef GC_NO_THREADS_DISCOVERY + /* Search in dll_thread_table and return the GC_thread entity */ + /* corresponding to the given thread id. */ + /* May be called without a lock, but should be called in contexts in */ + /* which the requested thread cannot be asynchronously deleted, e.g. */ + /* from the thread itself. */ + GC_INNER GC_thread GC_win32_dll_lookup_thread(thread_id_t id) + { + int i; + LONG my_max = GC_get_max_thread_index(); + + GC_ASSERT(GC_win32_dll_threads); + for (i = 0; i <= my_max; i++) { + if (AO_load_acquire(&dll_thread_table[i].tm.in_use) + && dll_thread_table[i].id == id) + break; /* Must still be in use, since nobody else can */ + /* store our thread id. */ + } + return i <= my_max ? (GC_thread)(dll_thread_table + i) : NULL; + } +#endif /* !GC_NO_THREADS_DISCOVERY */ + +#ifdef GC_PTHREADS + /* A quick-and-dirty cache of the mapping between pthread_t */ + /* and Win32 thread id. */ +# define PTHREAD_MAP_SIZE 512 + thread_id_t GC_pthread_map_cache[PTHREAD_MAP_SIZE] = {0}; +# define PTHREAD_MAP_INDEX(pthread_id) \ + ((NUMERIC_THREAD_ID(pthread_id) >> 5) % PTHREAD_MAP_SIZE) + /* It appears pthread_t is really a pointer type ... */ +# define SET_PTHREAD_MAP_CACHE(pthread_id, win32_id) \ + (void)(GC_pthread_map_cache[PTHREAD_MAP_INDEX(pthread_id)] = (win32_id)) +# define GET_PTHREAD_MAP_CACHE(pthread_id) \ + GC_pthread_map_cache[PTHREAD_MAP_INDEX(pthread_id)] + + GC_INNER void GC_win32_cache_self_pthread(thread_id_t self_id) + { + pthread_t self = pthread_self(); + + GC_ASSERT(I_HOLD_LOCK()); + SET_PTHREAD_MAP_CACHE(self, self_id); + } + + /* Return a GC_thread corresponding to a given pthread_t, or */ + /* NULL if it is not there. We assume that this is only */ + /* called for pthread ids that have not yet terminated or are */ + /* still joinable, and cannot be terminated concurrently. */ + GC_INNER GC_thread GC_lookup_by_pthread(pthread_t thread) + { + /* TODO: search in dll_thread_table instead when DllMain-based */ + /* thread registration is made compatible with pthreads (and */ + /* turned on). */ + + thread_id_t id; + GC_thread p; + int hv; + + GC_ASSERT(I_HOLD_READER_LOCK()); + id = GET_PTHREAD_MAP_CACHE(thread); + /* We first try the cache. */ + for (p = GC_threads[THREAD_TABLE_INDEX(id)]; + p != NULL; p = p -> tm.next) { + if (EXPECT(THREAD_EQUAL(p -> pthread_id, thread), TRUE)) + return p; + } + + /* If that fails, we use a very slow approach. */ + for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) { + for (p = GC_threads[hv]; p != NULL; p = p -> tm.next) { + if (THREAD_EQUAL(p -> pthread_id, thread)) + return p; + } + } + return NULL; + } +#endif /* GC_PTHREADS */ + +#ifdef WOW64_THREAD_CONTEXT_WORKAROUND +# ifndef CONTEXT_EXCEPTION_ACTIVE +# define CONTEXT_EXCEPTION_ACTIVE 0x08000000 +# define CONTEXT_EXCEPTION_REQUEST 0x40000000 +# define CONTEXT_EXCEPTION_REPORTING 0x80000000 +# endif + static GC_bool isWow64; /* Is running 32-bit code on Win64? */ +# define GET_THREAD_CONTEXT_FLAGS (isWow64 \ + ? CONTEXT_INTEGER | CONTEXT_CONTROL \ + | CONTEXT_EXCEPTION_REQUEST | CONTEXT_SEGMENTS \ + : CONTEXT_INTEGER | CONTEXT_CONTROL) +#elif defined(I386) || defined(XMM_CANT_STORE_PTRS) +# define GET_THREAD_CONTEXT_FLAGS (CONTEXT_INTEGER | CONTEXT_CONTROL) +#else +# define GET_THREAD_CONTEXT_FLAGS (CONTEXT_INTEGER | CONTEXT_CONTROL \ + | CONTEXT_FLOATING_POINT) +#endif /* !WOW64_THREAD_CONTEXT_WORKAROUND && !I386 */ + +/* Suspend the given thread, if it's still active. */ +STATIC void GC_suspend(GC_thread t) +{ +# ifndef MSWINCE + DWORD exitCode; +# ifdef RETRY_GET_THREAD_CONTEXT + int retry_cnt; +# define MAX_SUSPEND_THREAD_RETRIES (1000 * 1000) +# endif +# endif + + GC_ASSERT(I_HOLD_LOCK()); +# if defined(DEBUG_THREADS) && !defined(MSWINCE) \ + && (!defined(MSWIN32) || defined(CONSOLE_LOG)) + GC_log_printf("Suspending 0x%x\n", (int)t->id); +# endif + GC_win32_unprotect_thread(t); + GC_acquire_dirty_lock(); + +# ifdef MSWINCE + /* SuspendThread() will fail if thread is running kernel code. */ + while (SuspendThread(THREAD_HANDLE(t)) == (DWORD)-1) { + GC_release_dirty_lock(); + Sleep(10); /* in millis */ + GC_acquire_dirty_lock(); + } +# elif defined(RETRY_GET_THREAD_CONTEXT) + for (retry_cnt = 0;;) { + /* Apparently the Windows 95 GetOpenFileName call creates */ + /* a thread that does not properly get cleaned up, and */ + /* SuspendThread on its descriptor may provoke a crash. */ + /* This reduces the probability of that event, though it still */ + /* appears there is a race here. */ + if (GetExitCodeThread(t -> handle, &exitCode) + && exitCode != STILL_ACTIVE) { + GC_release_dirty_lock(); +# ifdef GC_PTHREADS + t -> crtn -> stack_end = NULL; /* prevent stack from being pushed */ +# else + /* This breaks pthread_join on Cygwin, which is guaranteed to */ + /* only see user threads. */ + GC_delete_thread(t); +# endif + return; + } + + if (SuspendThread(t -> handle) != (DWORD)-1) { + CONTEXT context; + + context.ContextFlags = GET_THREAD_CONTEXT_FLAGS; + if (GetThreadContext(t -> handle, &context)) { + /* TODO: WoW64 extra workaround: if CONTEXT_EXCEPTION_ACTIVE */ + /* then Sleep(1) and retry. */ + t->context_sp = copy_ptr_regs(t->context_regs, &context); + break; /* success; the context pointer registers are saved */ + } + + /* Resume the thread, try to suspend it in a better location. */ + if (ResumeThread(t -> handle) == (DWORD)-1) + ABORT("ResumeThread failed in suspend loop"); + } + if (retry_cnt > 1) { + GC_release_dirty_lock(); + Sleep(0); /* yield */ + GC_acquire_dirty_lock(); + } + if (++retry_cnt >= MAX_SUSPEND_THREAD_RETRIES) + ABORT("SuspendThread loop failed"); /* something must be wrong */ + } +# else + if (GetExitCodeThread(t -> handle, &exitCode) + && exitCode != STILL_ACTIVE) { + GC_release_dirty_lock(); +# ifdef GC_PTHREADS + t -> crtn -> stack_end = NULL; /* prevent stack from being pushed */ +# else + GC_delete_thread(t); +# endif + return; + } + if (SuspendThread(t -> handle) == (DWORD)-1) + ABORT("SuspendThread failed"); +# endif + t -> flags |= IS_SUSPENDED; + GC_release_dirty_lock(); + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, THREAD_HANDLE(t)); +} + +#if defined(GC_ASSERTIONS) \ + && ((defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE)) + GC_INNER GC_bool GC_write_disabled = FALSE; + /* TRUE only if GC_stop_world() acquired GC_write_cs. */ +#endif + +GC_INNER void GC_stop_world(void) +{ + thread_id_t self_id = GetCurrentThreadId(); + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_thr_initialized); + + /* This code is the same as in pthread_stop_world.c */ +# ifdef PARALLEL_MARK + if (GC_parallel) { + GC_acquire_mark_lock(); + GC_ASSERT(GC_fl_builder_count == 0); + /* We should have previously waited for it to become zero. */ + } +# endif /* PARALLEL_MARK */ + +# if !defined(GC_NO_THREADS_DISCOVERY) || defined(GC_ASSERTIONS) + GC_please_stop = TRUE; +# endif +# if (defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE) + GC_ASSERT(!GC_write_disabled); + EnterCriticalSection(&GC_write_cs); + /* It's not allowed to call GC_printf() (and friends) here down to */ + /* LeaveCriticalSection (same applies recursively to GC_suspend, */ + /* GC_delete_thread, GC_get_max_thread_index, GC_size and */ + /* GC_remove_protection). */ +# ifdef GC_ASSERTIONS + GC_write_disabled = TRUE; +# endif +# endif +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + int i; + int my_max; + /* Any threads being created during this loop will end up setting */ + /* GC_attached_thread when they start. This will force marking */ + /* to restart. This is not ideal, but hopefully correct. */ + AO_store(&GC_attached_thread, FALSE); + my_max = (int)GC_get_max_thread_index(); + for (i = 0; i <= my_max; i++) { + GC_thread p = (GC_thread)(dll_thread_table + i); + + if (p -> crtn -> stack_end != NULL && (p -> flags & DO_BLOCKING) == 0 + && p -> id != self_id) { + GC_suspend(p); + } + } + } else +# endif + /* else */ { + GC_thread p; + int i; + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != NULL; p = p -> tm.next) + if (p -> crtn -> stack_end != NULL && p -> id != self_id + && (p -> flags & (FINISHED | DO_BLOCKING)) == 0) + GC_suspend(p); + } + } +# if (defined(MSWIN32) && !defined(CONSOLE_LOG)) || defined(MSWINCE) +# ifdef GC_ASSERTIONS + GC_write_disabled = FALSE; +# endif + LeaveCriticalSection(&GC_write_cs); +# endif +# ifdef PARALLEL_MARK + if (GC_parallel) + GC_release_mark_lock(); +# endif +} + +GC_INNER void GC_start_world(void) +{ +# ifdef GC_ASSERTIONS + thread_id_t self_id = GetCurrentThreadId(); +# endif + + GC_ASSERT(I_HOLD_LOCK()); + if (GC_win32_dll_threads) { + LONG my_max = GC_get_max_thread_index(); + int i; + + for (i = 0; i <= my_max; i++) { + GC_thread p = (GC_thread)(dll_thread_table + i); + + if ((p -> flags & IS_SUSPENDED) != 0) { +# ifdef DEBUG_THREADS + GC_log_printf("Resuming 0x%x\n", (int)p->id); +# endif + GC_ASSERT(p -> id != self_id + && *(/* no volatile */ ptr_t *) + (word)(&(p -> crtn -> stack_end)) != NULL); + if (ResumeThread(THREAD_HANDLE(p)) == (DWORD)-1) + ABORT("ResumeThread failed"); + p -> flags &= (unsigned char)~IS_SUSPENDED; + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, THREAD_HANDLE(p)); + } + /* Else thread is unregistered or not suspended. */ + } + } else { + GC_thread p; + int i; + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != NULL; p = p -> tm.next) { + if ((p -> flags & IS_SUSPENDED) != 0) { +# ifdef DEBUG_THREADS + GC_log_printf("Resuming 0x%x\n", (int)p->id); +# endif + GC_ASSERT(p -> id != self_id + && *(ptr_t *)&(p -> crtn -> stack_end) != NULL); + if (ResumeThread(THREAD_HANDLE(p)) == (DWORD)-1) + ABORT("ResumeThread failed"); + GC_win32_unprotect_thread(p); + p -> flags &= (unsigned char)~IS_SUSPENDED; + if (GC_on_thread_event) + GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, THREAD_HANDLE(p)); + } else { +# ifdef DEBUG_THREADS + GC_log_printf("Not resuming thread 0x%x as it is not suspended\n", + (int)p->id); +# endif + } + } + } + } +# if !defined(GC_NO_THREADS_DISCOVERY) || defined(GC_ASSERTIONS) + GC_please_stop = FALSE; +# endif +} + +#ifdef MSWINCE + /* The VirtualQuery calls below won't work properly on some old WinCE */ + /* versions, but since each stack is restricted to an aligned 64 KiB */ + /* region of virtual memory we can just take the next lowest multiple */ + /* of 64 KiB. The result of this macro must not be used as its */ + /* argument later and must not be used as the lower bound for sp */ + /* check (since the stack may be bigger than 64 KiB). */ +# define GC_wince_evaluate_stack_min(s) \ + (ptr_t)(((word)(s) - 1) & ~(word)0xFFFF) +#elif defined(GC_ASSERTIONS) +# define GC_dont_query_stack_min FALSE +#endif + +/* A cache holding the results of the recent VirtualQuery call. */ +/* Protected by the allocator lock. */ +static ptr_t last_address = 0; +static MEMORY_BASIC_INFORMATION last_info; + +/* Probe stack memory region (starting at "s") to find out its */ +/* lowest address (i.e. stack top). */ +/* S must be a mapped address inside the region, NOT the first */ +/* unmapped address. */ +STATIC ptr_t GC_get_stack_min(ptr_t s) +{ + ptr_t bottom; + + GC_ASSERT(I_HOLD_LOCK()); + if (s != last_address) { + VirtualQuery(s, &last_info, sizeof(last_info)); + last_address = s; + } + do { + bottom = (ptr_t)last_info.BaseAddress; + VirtualQuery(bottom - 1, &last_info, sizeof(last_info)); + last_address = bottom - 1; + } while ((last_info.Protect & PAGE_READWRITE) + && !(last_info.Protect & PAGE_GUARD)); + return bottom; +} + +/* Return true if the page at s has protections appropriate */ +/* for a stack page. */ +static GC_bool may_be_in_stack(ptr_t s) +{ + GC_ASSERT(I_HOLD_LOCK()); + if (s != last_address) { + VirtualQuery(s, &last_info, sizeof(last_info)); + last_address = s; + } + return (last_info.Protect & PAGE_READWRITE) + && !(last_info.Protect & PAGE_GUARD); +} + +/* Copy all registers that might point into the heap. Frame */ +/* pointer registers are included in case client code was */ +/* compiled with the 'omit frame pointer' optimization. */ +/* The context register values are stored to regs argument */ +/* which is expected to be of PUSHED_REGS_COUNT length exactly. */ +/* The functions returns the context stack pointer value. */ +static ptr_t copy_ptr_regs(word *regs, const CONTEXT *pcontext) { + ptr_t sp; + int cnt = 0; +# define context (*pcontext) +# define PUSH1(reg) (regs[cnt++] = (word)pcontext->reg) +# define PUSH2(r1,r2) (PUSH1(r1), PUSH1(r2)) +# define PUSH4(r1,r2,r3,r4) (PUSH2(r1,r2), PUSH2(r3,r4)) +# define PUSH8_LH(r1,r2,r3,r4) (PUSH4(r1.Low,r1.High,r2.Low,r2.High), \ + PUSH4(r3.Low,r3.High,r4.Low,r4.High)) +# if defined(I386) +# ifdef WOW64_THREAD_CONTEXT_WORKAROUND + PUSH2(ContextFlags, SegFs); /* cannot contain pointers */ +# endif + PUSH4(Edi,Esi,Ebx,Edx), PUSH2(Ecx,Eax), PUSH1(Ebp); + sp = (ptr_t)context.Esp; +# elif defined(X86_64) + PUSH4(Rax,Rcx,Rdx,Rbx); PUSH2(Rbp, Rsi); PUSH1(Rdi); + PUSH4(R8, R9, R10, R11); PUSH4(R12, R13, R14, R15); +# ifndef XMM_CANT_STORE_PTRS + PUSH8_LH(Xmm0, Xmm1, Xmm2, Xmm3); + PUSH8_LH(Xmm4, Xmm5, Xmm6, Xmm7); + PUSH8_LH(Xmm8, Xmm9, Xmm10, Xmm11); + PUSH8_LH(Xmm12, Xmm13, Xmm14, Xmm15); +# endif + sp = (ptr_t)context.Rsp; +# elif defined(ARM32) + PUSH4(R0,R1,R2,R3),PUSH4(R4,R5,R6,R7),PUSH4(R8,R9,R10,R11); + PUSH1(R12); + sp = (ptr_t)context.Sp; +# elif defined(AARCH64) + PUSH4(X0,X1,X2,X3),PUSH4(X4,X5,X6,X7),PUSH4(X8,X9,X10,X11); + PUSH4(X12,X13,X14,X15),PUSH4(X16,X17,X18,X19),PUSH4(X20,X21,X22,X23); + PUSH4(X24,X25,X26,X27),PUSH1(X28); + PUSH1(Lr); + sp = (ptr_t)context.Sp; +# elif defined(SHx) + PUSH4(R0,R1,R2,R3), PUSH4(R4,R5,R6,R7), PUSH4(R8,R9,R10,R11); + PUSH2(R12,R13), PUSH1(R14); + sp = (ptr_t)context.R15; +# elif defined(MIPS) + PUSH4(IntAt,IntV0,IntV1,IntA0), PUSH4(IntA1,IntA2,IntA3,IntT0); + PUSH4(IntT1,IntT2,IntT3,IntT4), PUSH4(IntT5,IntT6,IntT7,IntS0); + PUSH4(IntS1,IntS2,IntS3,IntS4), PUSH4(IntS5,IntS6,IntS7,IntT8); + PUSH4(IntT9,IntK0,IntK1,IntS8); + sp = (ptr_t)context.IntSp; +# elif defined(PPC) + PUSH4(Gpr0, Gpr3, Gpr4, Gpr5), PUSH4(Gpr6, Gpr7, Gpr8, Gpr9); + PUSH4(Gpr10,Gpr11,Gpr12,Gpr14), PUSH4(Gpr15,Gpr16,Gpr17,Gpr18); + PUSH4(Gpr19,Gpr20,Gpr21,Gpr22), PUSH4(Gpr23,Gpr24,Gpr25,Gpr26); + PUSH4(Gpr27,Gpr28,Gpr29,Gpr30), PUSH1(Gpr31); + sp = (ptr_t)context.Gpr1; +# elif defined(ALPHA) + PUSH4(IntV0,IntT0,IntT1,IntT2), PUSH4(IntT3,IntT4,IntT5,IntT6); + PUSH4(IntT7,IntS0,IntS1,IntS2), PUSH4(IntS3,IntS4,IntS5,IntFp); + PUSH4(IntA0,IntA1,IntA2,IntA3), PUSH4(IntA4,IntA5,IntT8,IntT9); + PUSH4(IntT10,IntT11,IntT12,IntAt); + sp = (ptr_t)context.IntSp; +# elif defined(CPPCHECK) + sp = (ptr_t)(word)cnt; /* to workaround "cnt not used" false positive */ +# else +# error Architecture is not supported +# endif +# undef context +# undef PUSH1 +# undef PUSH2 +# undef PUSH4 +# undef PUSH8_LH + GC_ASSERT(cnt == PUSHED_REGS_COUNT); + return sp; +} + +STATIC word GC_push_stack_for(GC_thread thread, thread_id_t self_id, + GC_bool *pfound_me) +{ + GC_bool is_self = FALSE; + ptr_t sp, stack_min; + GC_stack_context_t crtn = thread -> crtn; + ptr_t stack_end = crtn -> stack_end; + struct GC_traced_stack_sect_s *traced_stack_sect = crtn -> traced_stack_sect; + + GC_ASSERT(I_HOLD_LOCK()); + if (EXPECT(NULL == stack_end, FALSE)) return 0; + + if (thread -> id == self_id) { + GC_ASSERT((thread -> flags & DO_BLOCKING) == 0); + sp = GC_approx_sp(); + is_self = TRUE; + *pfound_me = TRUE; + } else if ((thread -> flags & DO_BLOCKING) != 0) { + /* Use saved sp value for blocked threads. */ + sp = crtn -> stack_ptr; + } else { +# ifdef RETRY_GET_THREAD_CONTEXT + /* We cache context when suspending the thread since it may */ + /* require looping. */ + word *regs = thread -> context_regs; + + if ((thread -> flags & IS_SUSPENDED) != 0) { + sp = thread -> context_sp; + } else +# else + word regs[PUSHED_REGS_COUNT]; +# endif + + /* else */ { + CONTEXT context; + + /* For unblocked threads call GetThreadContext(). */ + context.ContextFlags = GET_THREAD_CONTEXT_FLAGS; + if (GetThreadContext(THREAD_HANDLE(thread), &context)) { + sp = copy_ptr_regs(regs, &context); + } else { +# ifdef RETRY_GET_THREAD_CONTEXT + /* At least, try to use the stale context if saved. */ + sp = thread -> context_sp; + if (NULL == sp) { + /* Skip the current thread, anyway its stack will */ + /* be pushed when the world is stopped. */ + return 0; + } +# else + *(volatile ptr_t *)&sp = NULL; + /* to avoid "might be uninitialized" compiler warning */ + ABORT("GetThreadContext failed"); +# endif + } + } +# ifdef THREAD_LOCAL_ALLOC + GC_ASSERT((thread -> flags & IS_SUSPENDED) != 0 || !GC_world_stopped); +# endif + +# ifndef WOW64_THREAD_CONTEXT_WORKAROUND + GC_push_many_regs(regs, PUSHED_REGS_COUNT); +# else + GC_push_many_regs(regs + 2, PUSHED_REGS_COUNT - 2); + /* skip ContextFlags and SegFs */ + + /* WoW64 workaround. */ + if (isWow64) { + DWORD ContextFlags = (DWORD)regs[0]; + + if ((ContextFlags & CONTEXT_EXCEPTION_REPORTING) != 0 + && (ContextFlags & (CONTEXT_EXCEPTION_ACTIVE + /* | CONTEXT_SERVICE_ACTIVE */)) != 0) { + PNT_TIB tib; + +# ifdef MSWINRT_FLAVOR + tib = thread -> tib; +# else + WORD SegFs = (WORD)regs[1]; + LDT_ENTRY selector; + + if (!GetThreadSelectorEntry(THREAD_HANDLE(thread), SegFs, + &selector)) + ABORT("GetThreadSelectorEntry failed"); + tib = (PNT_TIB)(selector.BaseLow + | (selector.HighWord.Bits.BaseMid << 16) + | (selector.HighWord.Bits.BaseHi << 24)); +# endif +# ifdef DEBUG_THREADS + GC_log_printf("TIB stack limit/base: %p .. %p\n", + (void *)tib->StackLimit, (void *)tib->StackBase); +# endif + GC_ASSERT(!((word)stack_end COOLER_THAN (word)tib->StackBase)); + if (stack_end != crtn -> initial_stack_base + /* We are in a coroutine (old-style way of the support). */ + && ((word)stack_end <= (word)tib->StackLimit + || (word)tib->StackBase < (word)stack_end)) { + /* The coroutine stack is not within TIB stack. */ + WARN("GetThreadContext might return stale register values" + " including ESP= %p\n", sp); + /* TODO: Because of WoW64 bug, there is no guarantee that */ + /* sp really points to the stack top but, for now, we do */ + /* our best as the TIB stack limit/base cannot be used */ + /* while we are inside a coroutine. */ + } else { + /* GetThreadContext() might return stale register values, */ + /* so we scan the entire stack region (down to the stack */ + /* limit). There is no 100% guarantee that all the */ + /* registers are pushed but we do our best (the proper */ + /* solution would be to fix it inside Windows). */ + sp = (ptr_t)tib->StackLimit; + } + } /* else */ +# ifdef DEBUG_THREADS + else { + static GC_bool logged; + if (!logged + && (ContextFlags & CONTEXT_EXCEPTION_REPORTING) == 0) { + GC_log_printf("CONTEXT_EXCEPTION_REQUEST not supported\n"); + logged = TRUE; + } + } +# endif + } +# endif /* WOW64_THREAD_CONTEXT_WORKAROUND */ + } /* not current thread */ +# ifdef STACKPTR_CORRECTOR_AVAILABLE + if (GC_sp_corrector != 0) + GC_sp_corrector((void **)&sp, (void *)(thread -> pthread_id)); +# endif + + /* Set stack_min to the lowest address in the thread stack, */ + /* or to an address in the thread stack no larger than sp, */ + /* taking advantage of the old value to avoid slow traversals */ + /* of large stacks. */ + if (crtn -> last_stack_min == ADDR_LIMIT) { +# ifdef MSWINCE + if (GC_dont_query_stack_min) { + stack_min = GC_wince_evaluate_stack_min(traced_stack_sect != NULL ? + (ptr_t)traced_stack_sect : stack_end); + /* Keep last_stack_min value unmodified. */ + } else +# endif + /* else */ { + stack_min = GC_get_stack_min(traced_stack_sect != NULL ? + (ptr_t)traced_stack_sect : stack_end); + GC_win32_unprotect_thread(thread); + crtn -> last_stack_min = stack_min; + } + } else { + /* First, adjust the latest known minimum stack address if we */ + /* are inside GC_call_with_gc_active(). */ + if (traced_stack_sect != NULL && + (word)(crtn -> last_stack_min) > (word)traced_stack_sect) { + GC_win32_unprotect_thread(thread); + crtn -> last_stack_min = (ptr_t)traced_stack_sect; + } + + if ((word)sp < (word)stack_end + && (word)sp >= (word)(crtn -> last_stack_min)) { + stack_min = sp; + } else { + /* In the current thread it is always safe to use sp value. */ + if (may_be_in_stack(is_self && (word)sp < (word)(crtn -> last_stack_min) + ? sp : crtn -> last_stack_min)) { + stack_min = (ptr_t)last_info.BaseAddress; + /* Do not probe rest of the stack if sp is correct. */ + if ((word)sp < (word)stack_min || (word)sp >= (word)stack_end) + stack_min = GC_get_stack_min(crtn -> last_stack_min); + } else { + /* Stack shrunk? Is this possible? */ + stack_min = GC_get_stack_min(stack_end); + } + GC_win32_unprotect_thread(thread); + crtn -> last_stack_min = stack_min; + } + } + + GC_ASSERT(GC_dont_query_stack_min + || stack_min == GC_get_stack_min(stack_end) + || ((word)sp >= (word)stack_min + && (word)stack_min < (word)stack_end + && (word)stack_min > (word)GC_get_stack_min(stack_end))); + + if ((word)sp >= (word)stack_min && (word)sp < (word)stack_end) { +# ifdef DEBUG_THREADS + GC_log_printf("Pushing stack for 0x%x from sp %p to %p from 0x%x\n", + (int)(thread -> id), (void *)sp, (void *)stack_end, + (int)self_id); +# endif + GC_push_all_stack_sections(sp, stack_end, traced_stack_sect); + } else { + /* If not current thread then it is possible for sp to point to */ + /* the guarded (untouched yet) page just below the current */ + /* stack_min of the thread. */ + if (is_self || (word)sp >= (word)stack_end + || (word)(sp + GC_page_size) < (word)stack_min) + WARN("Thread stack pointer %p out of range, pushing everything\n", sp); +# ifdef DEBUG_THREADS + GC_log_printf("Pushing stack for 0x%x from (min) %p to %p from 0x%x\n", + (int)(thread -> id), (void *)stack_min, (void *)stack_end, + (int)self_id); +# endif + /* Push everything - ignore "traced stack section" data. */ + GC_push_all_stack(stack_min, stack_end); + } + return stack_end - sp; /* stack grows down */ +} + +/* Should do exactly the right thing if the world is stopped; should */ +/* not fail if it is not. */ +GC_INNER void GC_push_all_stacks(void) +{ + thread_id_t self_id = GetCurrentThreadId(); + GC_bool found_me = FALSE; +# ifndef SMALL_CONFIG + unsigned nthreads = 0; +# endif + word total_size = 0; + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(GC_thr_initialized); +# ifndef GC_NO_THREADS_DISCOVERY + if (GC_win32_dll_threads) { + int i; + LONG my_max = GC_get_max_thread_index(); + + for (i = 0; i <= my_max; i++) { + GC_thread p = (GC_thread)(dll_thread_table + i); + + if (p -> tm.in_use) { +# ifndef SMALL_CONFIG + ++nthreads; +# endif + total_size += GC_push_stack_for(p, self_id, &found_me); + } + } + } else +# endif + /* else */ { + int i; + for (i = 0; i < THREAD_TABLE_SZ; i++) { + GC_thread p; + + for (p = GC_threads[i]; p != NULL; p = p -> tm.next) { + GC_ASSERT(THREAD_TABLE_INDEX(p -> id) == i); + if (!KNOWN_FINISHED(p)) { +# ifndef SMALL_CONFIG + ++nthreads; +# endif + total_size += GC_push_stack_for(p, self_id, &found_me); + } + } + } + } +# ifndef SMALL_CONFIG + GC_VERBOSE_LOG_PRINTF("Pushed %d thread stacks%s\n", nthreads, + GC_win32_dll_threads ? + " based on DllMain thread tracking" : ""); +# endif + if (!found_me && !GC_in_thread_creation) + ABORT("Collecting from unknown thread"); + GC_total_stacksize = total_size; +} + +#ifdef PARALLEL_MARK + GC_INNER ptr_t GC_marker_last_stack_min[MAX_MARKERS - 1] = {0}; + /* Last known minimum (hottest) address */ + /* in stack (or ADDR_LIMIT if unset) */ + /* for markers. */ + +#endif /* PARALLEL_MARK */ + +/* Find stack with the lowest address which overlaps the */ +/* interval [start, limit). */ +/* Return stack bounds in *lo and *hi. If no such stack */ +/* is found, both *hi and *lo will be set to an address */ +/* higher than limit. */ +GC_INNER void GC_get_next_stack(char *start, char *limit, + char **lo, char **hi) +{ + int i; + char * current_min = ADDR_LIMIT; /* Least in-range stack base */ + ptr_t *plast_stack_min = NULL; /* Address of last_stack_min */ + /* field for thread corresponding */ + /* to current_min. */ + GC_thread thread = NULL; /* Either NULL or points to the */ + /* thread's hash table entry */ + /* containing *plast_stack_min. */ + + GC_ASSERT(I_HOLD_LOCK()); + /* First set current_min, ignoring limit. */ + if (GC_win32_dll_threads) { + LONG my_max = GC_get_max_thread_index(); + + for (i = 0; i <= my_max; i++) { + ptr_t stack_end = (ptr_t)dll_thread_table[i].crtn -> stack_end; + + if ((word)stack_end > (word)start + && (word)stack_end < (word)current_min) { + /* Update address of last_stack_min. */ + plast_stack_min = (ptr_t * /* no volatile */)(word)( + &(dll_thread_table[i].crtn -> last_stack_min)); + current_min = stack_end; +# ifdef CPPCHECK + /* To avoid a warning that thread is always null. */ + thread = (GC_thread)&dll_thread_table[i]; +# endif + } + } + } else { + for (i = 0; i < THREAD_TABLE_SZ; i++) { + GC_thread p; + + for (p = GC_threads[i]; p != NULL; p = p -> tm.next) { + GC_stack_context_t crtn = p -> crtn; + ptr_t stack_end = crtn -> stack_end; /* read of a volatile field */ + + if ((word)stack_end > (word)start + && (word)stack_end < (word)current_min) { + /* Update address of last_stack_min. */ + plast_stack_min = &(crtn -> last_stack_min); + thread = p; /* Remember current thread to unprotect. */ + current_min = stack_end; + } + } + } +# ifdef PARALLEL_MARK + for (i = 0; i < GC_markers_m1; ++i) { + ptr_t s = GC_marker_sp[i]; +# ifdef IA64 + /* FIXME: not implemented */ +# endif + if ((word)s > (word)start && (word)s < (word)current_min) { + GC_ASSERT(GC_marker_last_stack_min[i] != NULL); + plast_stack_min = &GC_marker_last_stack_min[i]; + current_min = s; + thread = NULL; /* Not a thread's hash table entry. */ + } + } +# endif + } + + *hi = current_min; + if (current_min == ADDR_LIMIT) { + *lo = ADDR_LIMIT; + return; + } + + GC_ASSERT((word)current_min > (word)start && plast_stack_min != NULL); +# ifdef MSWINCE + if (GC_dont_query_stack_min) { + *lo = GC_wince_evaluate_stack_min(current_min); + /* Keep last_stack_min value unmodified. */ + return; + } +# endif + + if ((word)current_min > (word)limit && !may_be_in_stack(limit)) { + /* Skip the rest since the memory region at limit address is */ + /* not a stack (so the lowest address of the found stack would */ + /* be above the limit value anyway). */ + *lo = ADDR_LIMIT; + return; + } + + /* Get the minimum address of the found stack by probing its memory */ + /* region starting from the recent known minimum (if set). */ + if (*plast_stack_min == ADDR_LIMIT + || !may_be_in_stack(*plast_stack_min)) { + /* Unsafe to start from last_stack_min value. */ + *lo = GC_get_stack_min(current_min); + } else { + /* Use the recent value to optimize search for min address. */ + *lo = GC_get_stack_min(*plast_stack_min); + } + + /* Remember current stack_min value. */ + if (thread != NULL) + GC_win32_unprotect_thread(thread); + *plast_stack_min = *lo; +} + +#if defined(PARALLEL_MARK) && !defined(GC_PTHREADS_PARAMARK) + +# ifndef MARK_THREAD_STACK_SIZE +# define MARK_THREAD_STACK_SIZE 0 /* default value */ +# endif + + STATIC HANDLE GC_marker_cv[MAX_MARKERS - 1] = {0}; + /* Events with manual reset (one for each */ + /* mark helper). */ + + GC_INNER thread_id_t GC_marker_Id[MAX_MARKERS - 1] = {0}; + /* This table is used for mapping helper */ + /* threads ID to mark helper index (linear */ + /* search is used since the mapping contains */ + /* only a few entries). */ + + /* mark_mutex_event, builder_cv, mark_cv are initialized in GC_thr_init */ + static HANDLE mark_mutex_event = (HANDLE)0; /* Event with auto-reset. */ + static HANDLE builder_cv = (HANDLE)0; /* Event with manual reset. */ + static HANDLE mark_cv = (HANDLE)0; /* Event with manual reset. */ + + GC_INNER void GC_start_mark_threads_inner(void) + { + int i; + + GC_ASSERT(I_HOLD_LOCK()); + ASSERT_CANCEL_DISABLED(); + if (GC_available_markers_m1 <= 0 || GC_parallel) return; + GC_wait_for_gc_completion(TRUE); + + GC_ASSERT(GC_fl_builder_count == 0); + /* Initialize GC_marker_cv[] fully before starting the */ + /* first helper thread. */ + GC_markers_m1 = GC_available_markers_m1; + for (i = 0; i < GC_markers_m1; ++i) { + if ((GC_marker_cv[i] = CreateEvent(NULL /* attrs */, + TRUE /* isManualReset */, + FALSE /* initialState */, + NULL /* name (A/W) */)) == (HANDLE)0) + ABORT("CreateEvent failed"); + } + + for (i = 0; i < GC_markers_m1; ++i) { +# if defined(MSWINCE) || defined(MSWIN_XBOX1) + HANDLE handle; + DWORD thread_id; + + GC_marker_last_stack_min[i] = ADDR_LIMIT; + /* There is no _beginthreadex() in WinCE. */ + handle = CreateThread(NULL /* lpsa */, + MARK_THREAD_STACK_SIZE /* ignored */, + GC_mark_thread, (LPVOID)(word)i, + 0 /* fdwCreate */, &thread_id); + if (EXPECT(NULL == handle, FALSE)) { + WARN("Marker thread %" WARN_PRIdPTR " creation failed\n", + (signed_word)i); + /* The most probable failure reason is "not enough memory". */ + /* Don't try to create other marker threads. */ + break; + } + /* It is safe to detach the thread. */ + CloseHandle(handle); +# else + GC_uintptr_t handle; + unsigned thread_id; + + GC_marker_last_stack_min[i] = ADDR_LIMIT; + handle = _beginthreadex(NULL /* security_attr */, + MARK_THREAD_STACK_SIZE, GC_mark_thread, + (void *)(word)i, 0 /* flags */, &thread_id); + if (EXPECT(!handle || handle == (GC_uintptr_t)-1L, FALSE)) { + WARN("Marker thread %" WARN_PRIdPTR " creation failed\n", + (signed_word)i); + /* Don't try to create other marker threads. */ + break; + } else {/* We may detach the thread (if handle is of HANDLE type) */ + /* CloseHandle((HANDLE)handle); */ + } +# endif + } + + /* Adjust GC_markers_m1 (and free unused resources) if failed. */ + while (GC_markers_m1 > i) { + GC_markers_m1--; + CloseHandle(GC_marker_cv[GC_markers_m1]); + } + GC_wait_for_markers_init(); + GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1); + if (EXPECT(0 == i, FALSE)) { + CloseHandle(mark_cv); + CloseHandle(builder_cv); + CloseHandle(mark_mutex_event); + } + } + +# ifdef GC_ASSERTIONS + STATIC unsigned long GC_mark_lock_holder = NO_THREAD; +# define SET_MARK_LOCK_HOLDER \ + (void)(GC_mark_lock_holder = GetCurrentThreadId()) +# define UNSET_MARK_LOCK_HOLDER \ + do { \ + GC_ASSERT(GC_mark_lock_holder == GetCurrentThreadId()); \ + GC_mark_lock_holder = NO_THREAD; \ + } while (0) +# else +# define SET_MARK_LOCK_HOLDER (void)0 +# define UNSET_MARK_LOCK_HOLDER (void)0 +# endif /* !GC_ASSERTIONS */ + + STATIC /* volatile */ LONG GC_mark_mutex_state = 0; + /* Mutex state: 0 - unlocked, */ + /* 1 - locked and no other waiters, */ + /* -1 - locked and waiters may exist. */ + /* Accessed by InterlockedExchange(). */ + +# ifdef LOCK_STATS + volatile AO_t GC_block_count = 0; + volatile AO_t GC_unlocked_count = 0; +# endif + + GC_INNER void GC_acquire_mark_lock(void) + { + GC_ASSERT(GC_mark_lock_holder != GetCurrentThreadId()); + if (EXPECT(InterlockedExchange(&GC_mark_mutex_state, + 1 /* locked */) != 0, FALSE)) { +# ifdef LOCK_STATS + (void)AO_fetch_and_add1(&GC_block_count); +# endif + /* Repeatedly reset the state and wait until we acquire the */ + /* mark lock. */ + while (InterlockedExchange(&GC_mark_mutex_state, + -1 /* locked_and_has_waiters */) != 0) { + if (WaitForSingleObject(mark_mutex_event, INFINITE) == WAIT_FAILED) + ABORT("WaitForSingleObject failed"); + } + } +# ifdef LOCK_STATS + else { + (void)AO_fetch_and_add1(&GC_unlocked_count); + } +# endif + + GC_ASSERT(GC_mark_lock_holder == NO_THREAD); + SET_MARK_LOCK_HOLDER; + } + + GC_INNER void GC_release_mark_lock(void) + { + UNSET_MARK_LOCK_HOLDER; + if (EXPECT(InterlockedExchange(&GC_mark_mutex_state, + 0 /* unlocked */) < 0, FALSE)) { + /* wake a waiter */ + if (SetEvent(mark_mutex_event) == FALSE) + ABORT("SetEvent failed"); + } + } + + /* In GC_wait_for_reclaim/GC_notify_all_builder() we emulate POSIX */ + /* cond_wait/cond_broadcast() primitives with WinAPI Event object */ + /* (working in "manual reset" mode). This works here because */ + /* GC_notify_all_builder() is always called holding the mark lock */ + /* and the checked condition (GC_fl_builder_count == 0) is the only */ + /* one for which broadcasting on builder_cv is performed. */ + + GC_INNER void GC_wait_for_reclaim(void) + { + GC_ASSERT(builder_cv != 0); + for (;;) { + GC_acquire_mark_lock(); + if (GC_fl_builder_count == 0) + break; + if (ResetEvent(builder_cv) == FALSE) + ABORT("ResetEvent failed"); + GC_release_mark_lock(); + if (WaitForSingleObject(builder_cv, INFINITE) == WAIT_FAILED) + ABORT("WaitForSingleObject failed"); + } + GC_release_mark_lock(); + } + + GC_INNER void GC_notify_all_builder(void) + { + GC_ASSERT(GC_mark_lock_holder == GetCurrentThreadId()); + GC_ASSERT(builder_cv != 0); + GC_ASSERT(GC_fl_builder_count == 0); + if (SetEvent(builder_cv) == FALSE) + ABORT("SetEvent failed"); + } + + /* mark_cv is used (for waiting) by a non-helper thread. */ + + GC_INNER void GC_wait_marker(void) + { + HANDLE event = mark_cv; + thread_id_t self_id = GetCurrentThreadId(); + int i = GC_markers_m1; + + while (i-- > 0) { + if (GC_marker_Id[i] == self_id) { + event = GC_marker_cv[i]; + break; + } + } + + if (ResetEvent(event) == FALSE) + ABORT("ResetEvent failed"); + GC_release_mark_lock(); + if (WaitForSingleObject(event, INFINITE) == WAIT_FAILED) + ABORT("WaitForSingleObject failed"); + GC_acquire_mark_lock(); + } + + GC_INNER void GC_notify_all_marker(void) + { + thread_id_t self_id = GetCurrentThreadId(); + int i = GC_markers_m1; + + while (i-- > 0) { + /* Notify every marker ignoring self (for efficiency). */ + if (SetEvent(GC_marker_Id[i] != self_id ? GC_marker_cv[i] : + mark_cv) == FALSE) + ABORT("SetEvent failed"); + } + } + +#endif /* PARALLEL_MARK && !GC_PTHREADS_PARAMARK */ + +/* We have no DllMain to take care of new threads. Thus, we */ +/* must properly intercept thread creation. */ + +struct win32_start_info { + LPTHREAD_START_ROUTINE start_routine; + LPVOID arg; +}; + +STATIC void *GC_CALLBACK GC_win32_start_inner(struct GC_stack_base *sb, + void *arg) +{ + void * ret; + LPTHREAD_START_ROUTINE start_routine = + ((struct win32_start_info *)arg) -> start_routine; + LPVOID start_arg = ((struct win32_start_info *)arg) -> arg; + + GC_ASSERT(!GC_win32_dll_threads); + GC_register_my_thread(sb); /* This waits for an in-progress GC. */ +# ifdef DEBUG_THREADS + GC_log_printf("thread 0x%lx starting...\n", (long)GetCurrentThreadId()); +# endif + GC_free(arg); + + /* Clear the thread entry even if we exit with an exception. */ + /* This is probably pointless, since an uncaught exception is */ + /* supposed to result in the process being killed. */ +# ifndef NO_SEH_AVAILABLE + ret = NULL; /* to avoid "might be uninitialized" compiler warning */ + __try +# endif + { + ret = (void *)(word)((*start_routine)(start_arg)); + } +# ifndef NO_SEH_AVAILABLE + __finally +# endif + { + (void)GC_unregister_my_thread(); + } + +# ifdef DEBUG_THREADS + GC_log_printf("thread 0x%lx returned from start routine\n", + (long)GetCurrentThreadId()); +# endif + return ret; +} + +STATIC DWORD WINAPI GC_win32_start(LPVOID arg) +{ + return (DWORD)(word)GC_call_with_stack_base(GC_win32_start_inner, arg); +} + +GC_API HANDLE WINAPI GC_CreateThread( + LPSECURITY_ATTRIBUTES lpThreadAttributes, + GC_WIN32_SIZE_T dwStackSize, + LPTHREAD_START_ROUTINE lpStartAddress, + LPVOID lpParameter, DWORD dwCreationFlags, + LPDWORD lpThreadId) +{ + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + GC_ASSERT(GC_thr_initialized); + /* Make sure GC is initialized (i.e. main thread is attached, */ + /* tls is initialized). This is redundant when */ + /* GC_win32_dll_threads is set by GC_use_threads_discovery(). */ + +# ifdef DEBUG_THREADS + GC_log_printf("About to create a thread from 0x%lx\n", + (long)GetCurrentThreadId()); +# endif + if (GC_win32_dll_threads) { + return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, + lpParameter, dwCreationFlags, lpThreadId); + } else { + struct win32_start_info *psi = + (struct win32_start_info *)GC_malloc_uncollectable( + sizeof(struct win32_start_info)); + /* Handed off to and deallocated by child thread. */ + HANDLE thread_h; + + if (EXPECT(NULL == psi, FALSE)) { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + /* set up thread arguments */ + psi -> start_routine = lpStartAddress; + psi -> arg = lpParameter; + GC_dirty(psi); + REACHABLE_AFTER_DIRTY(lpParameter); + +# ifdef PARALLEL_MARK + if (EXPECT(!GC_parallel && GC_available_markers_m1 > 0, FALSE)) + GC_start_mark_threads(); +# endif + set_need_to_lock(); + thread_h = CreateThread(lpThreadAttributes, dwStackSize, GC_win32_start, + psi, dwCreationFlags, lpThreadId); + if (EXPECT(0 == thread_h, FALSE)) GC_free(psi); + return thread_h; + } +} + +GC_API DECLSPEC_NORETURN void WINAPI GC_ExitThread(DWORD dwExitCode) +{ + if (!GC_win32_dll_threads) (void)GC_unregister_my_thread(); + ExitThread(dwExitCode); +} + +#if !defined(CYGWIN32) && !defined(MSWINCE) && !defined(MSWIN_XBOX1) \ + && !defined(NO_CRT) + GC_API GC_uintptr_t GC_CALL GC_beginthreadex( + void *security, unsigned stack_size, + unsigned (__stdcall *start_address)(void *), + void *arglist, unsigned initflag, + unsigned *thrdaddr) + { + if (!EXPECT(GC_is_initialized, TRUE)) GC_init(); + GC_ASSERT(GC_thr_initialized); +# ifdef DEBUG_THREADS + GC_log_printf("About to create a thread from 0x%lx\n", + (long)GetCurrentThreadId()); +# endif + + if (GC_win32_dll_threads) { + return _beginthreadex(security, stack_size, start_address, + arglist, initflag, thrdaddr); + } else { + GC_uintptr_t thread_h; + struct win32_start_info *psi = + (struct win32_start_info *)GC_malloc_uncollectable( + sizeof(struct win32_start_info)); + /* Handed off to and deallocated by child thread. */ + + if (EXPECT(NULL == psi, FALSE)) { + /* MSDN docs say _beginthreadex() returns 0 on error and sets */ + /* errno to either EAGAIN (too many threads) or EINVAL (the */ + /* argument is invalid or the stack size is incorrect), so we */ + /* set errno to EAGAIN on "not enough memory". */ + errno = EAGAIN; + return 0; + } + + /* set up thread arguments */ + psi -> start_routine = (LPTHREAD_START_ROUTINE)start_address; + psi -> arg = arglist; + GC_dirty(psi); + REACHABLE_AFTER_DIRTY(arglist); + +# ifdef PARALLEL_MARK + if (EXPECT(!GC_parallel && GC_available_markers_m1 > 0, FALSE)) + GC_start_mark_threads(); +# endif + set_need_to_lock(); + thread_h = _beginthreadex(security, stack_size, + (unsigned (__stdcall *)(void *))GC_win32_start, + psi, initflag, thrdaddr); + if (EXPECT(0 == thread_h, FALSE)) GC_free(psi); + return thread_h; + } + } + + GC_API void GC_CALL GC_endthreadex(unsigned retval) + { + if (!GC_win32_dll_threads) (void)GC_unregister_my_thread(); + _endthreadex(retval); + } +#endif /* !CYGWIN32 && !MSWINCE && !MSWIN_XBOX1 && !NO_CRT */ + +#ifdef GC_WINMAIN_REDIRECT + /* This might be useful on WinCE. Shouldn't be used with GC_DLL. */ + +# if defined(MSWINCE) && defined(UNDER_CE) +# define WINMAIN_LPTSTR LPWSTR +# else +# define WINMAIN_LPTSTR LPSTR +# endif + + /* This is defined in gc.h. */ +# undef WinMain + + /* Defined outside GC by an application. */ + int WINAPI GC_WinMain(HINSTANCE, HINSTANCE, WINMAIN_LPTSTR, int); + + typedef struct { + HINSTANCE hInstance; + HINSTANCE hPrevInstance; + WINMAIN_LPTSTR lpCmdLine; + int nShowCmd; + } main_thread_args; + + static DWORD WINAPI main_thread_start(LPVOID arg) + { + main_thread_args *main_args = (main_thread_args *)arg; + return (DWORD)GC_WinMain(main_args -> hInstance, + main_args -> hPrevInstance, + main_args -> lpCmdLine, main_args -> nShowCmd); + } + + STATIC void *GC_CALLBACK GC_waitForSingleObjectInfinite(void *handle) + { + return (void *)(word)WaitForSingleObject((HANDLE)handle, INFINITE); + } + +# ifndef WINMAIN_THREAD_STACK_SIZE +# define WINMAIN_THREAD_STACK_SIZE 0 /* default value */ +# endif + + int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + WINMAIN_LPTSTR lpCmdLine, int nShowCmd) + { + DWORD exit_code = 1; + + main_thread_args args = { + hInstance, hPrevInstance, lpCmdLine, nShowCmd + }; + HANDLE thread_h; + DWORD thread_id; + + /* initialize everything */ + GC_INIT(); + + /* start the main thread */ + thread_h = GC_CreateThread(NULL /* lpsa */, + WINMAIN_THREAD_STACK_SIZE /* ignored on WinCE */, + main_thread_start, &args, 0 /* fdwCreate */, + &thread_id); + if (NULL == thread_h) + ABORT("GC_CreateThread(main_thread) failed"); + + if ((DWORD)(word)GC_do_blocking(GC_waitForSingleObjectInfinite, + (void *)thread_h) == WAIT_FAILED) + ABORT("WaitForSingleObject(main_thread) failed"); + GetExitCodeThread(thread_h, &exit_code); + CloseHandle(thread_h); + +# ifdef MSWINCE + GC_deinit(); +# endif + return (int)exit_code; + } + +#endif /* GC_WINMAIN_REDIRECT */ + +#ifdef WOW64_THREAD_CONTEXT_WORKAROUND +# ifdef MSWINRT_FLAVOR + /* Available on WinRT but we have to declare it manually. */ + __declspec(dllimport) HMODULE WINAPI GetModuleHandleW(LPCWSTR); +# endif + + static GC_bool is_wow64_process(HMODULE hK32) + { + BOOL is_wow64; +# ifdef MSWINRT_FLAVOR + /* Try to use IsWow64Process2 as it handles different WoW64 cases. */ + HMODULE hWow64 = GetModuleHandleW(L"api-ms-win-core-wow64-l1-1-1.dll"); + + UNUSED_ARG(hK32); + if (hWow64) { + FARPROC pfn2 = GetProcAddress(hWow64, "IsWow64Process2"); + USHORT process_machine, native_machine; + + if (pfn2 + && (*(BOOL (WINAPI *)(HANDLE, USHORT *, USHORT *)) + (GC_funcptr_uint)pfn2)(GetCurrentProcess(), &process_machine, + &native_machine)) + return process_machine != native_machine; + } + if (IsWow64Process(GetCurrentProcess(), &is_wow64)) + return (GC_bool)is_wow64; +# else + if (hK32) { + FARPROC pfn = GetProcAddress(hK32, "IsWow64Process"); + + if (pfn + && (*(BOOL (WINAPI *)(HANDLE, BOOL *)) + (GC_funcptr_uint)pfn)(GetCurrentProcess(), &is_wow64)) + return (GC_bool)is_wow64; + } +# endif + return FALSE; /* IsWow64Process failed */ + } +#endif /* WOW64_THREAD_CONTEXT_WORKAROUND */ + +GC_INNER void GC_thr_init(void) +{ + struct GC_stack_base sb; + thread_id_t self_id = GetCurrentThreadId(); +# if (!defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID) && !defined(MSWINCE) \ + && defined(PARALLEL_MARK)) || defined(WOW64_THREAD_CONTEXT_WORKAROUND) + HMODULE hK32; +# ifdef MSWINRT_FLAVOR + MEMORY_BASIC_INFORMATION memInfo; + + if (VirtualQuery((void*)(word)GetProcAddress, &memInfo, sizeof(memInfo)) + != sizeof(memInfo)) + ABORT("Weird VirtualQuery result"); + hK32 = (HMODULE)memInfo.AllocationBase; +# else + hK32 = GetModuleHandle(TEXT("kernel32.dll")); +# endif +# endif + + GC_ASSERT(I_HOLD_LOCK()); + GC_ASSERT(!GC_thr_initialized); + GC_ASSERT((word)(&GC_threads) % sizeof(word) == 0); +# ifdef GC_ASSERTIONS + GC_thr_initialized = TRUE; +# endif +# if !defined(DONT_USE_ATEXIT) || !defined(GC_NO_THREADS_DISCOVERY) + GC_main_thread_id = self_id; +# endif +# ifdef CAN_HANDLE_FORK + GC_setup_atfork(); +# endif +# ifdef WOW64_THREAD_CONTEXT_WORKAROUND + /* Set isWow64 flag. */ + isWow64 = is_wow64_process(hK32); +# endif + /* Add the initial thread, so we can stop it. */ + sb.mem_base = GC_stackbottom; + GC_ASSERT(sb.mem_base != NULL); +# ifdef IA64 + sb.reg_base = GC_register_stackbottom; +# endif + +# if defined(PARALLEL_MARK) + { + char * markers_string = GETENV("GC_MARKERS"); + int markers = GC_required_markers_cnt; + + if (markers_string != NULL) { + markers = atoi(markers_string); + if (markers <= 0 || markers > MAX_MARKERS) { + WARN("Too big or invalid number of mark threads: %" WARN_PRIdPTR + "; using maximum threads\n", (signed_word)markers); + markers = MAX_MARKERS; + } + } else if (0 == markers) { + /* Unless the client sets the desired number of */ + /* parallel markers, it is determined based on the */ + /* number of CPU cores. */ +# ifdef MSWINCE + /* There is no GetProcessAffinityMask() in WinCE. */ + /* GC_sysinfo is already initialized. */ + markers = (int)GC_sysinfo.dwNumberOfProcessors; +# else +# ifdef _WIN64 + DWORD_PTR procMask = 0; + DWORD_PTR sysMask; +# else + DWORD procMask = 0; + DWORD sysMask; +# endif + int ncpu = 0; + if ( +# ifdef __cplusplus + GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask) +# else + /* Cast args to void* for compatibility with some old SDKs. */ + GetProcessAffinityMask(GetCurrentProcess(), + (void *)&procMask, (void *)&sysMask) +# endif + && procMask) { + do { + ncpu++; + } while ((procMask &= procMask - 1) != 0); + } + markers = ncpu; +# endif +# if defined(GC_MIN_MARKERS) && !defined(CPPCHECK) + /* This is primarily for testing on systems without getenv(). */ + if (markers < GC_MIN_MARKERS) + markers = GC_MIN_MARKERS; +# endif + if (markers > MAX_MARKERS) + markers = MAX_MARKERS; /* silently limit the value */ + } + GC_available_markers_m1 = markers - 1; + } + + /* Check whether parallel mode could be enabled. */ + if (GC_win32_dll_threads || GC_available_markers_m1 <= 0) { + /* Disable parallel marking. */ + GC_parallel = FALSE; + GC_COND_LOG_PRINTF( + "Single marker thread, turning off parallel marking\n"); + } else { +# ifndef GC_PTHREADS_PARAMARK + /* Initialize Win32 event objects for parallel marking. */ + mark_mutex_event = CreateEvent(NULL /* attrs */, + FALSE /* isManualReset */, + FALSE /* initialState */, NULL /* name */); + builder_cv = CreateEvent(NULL /* attrs */, + TRUE /* isManualReset */, + FALSE /* initialState */, NULL /* name */); + mark_cv = CreateEvent(NULL /* attrs */, TRUE /* isManualReset */, + FALSE /* initialState */, NULL /* name */); + if (mark_mutex_event == (HANDLE)0 || builder_cv == (HANDLE)0 + || mark_cv == (HANDLE)0) + ABORT("CreateEvent failed"); +# endif +# if !defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID) && !defined(MSWINCE) + GC_init_win32_thread_naming(hK32); +# endif + } +# endif /* PARALLEL_MARK */ + + GC_register_my_thread_inner(&sb, self_id); +} + +#ifndef GC_NO_THREADS_DISCOVERY + /* We avoid acquiring locks here, since this doesn't seem to be */ + /* preemptible. This may run with an uninitialized collector, in */ + /* which case we don't do much. This implies that no threads other */ + /* than the main one should be created with an uninitialized */ + /* collector. (The alternative of initializing the collector here */ + /* seems dangerous, since DllMain is limited in what it can do.) */ + +# ifdef GC_INSIDE_DLL + /* Export only if needed by client. */ + GC_API +# else +# define GC_DllMain DllMain +# endif + BOOL WINAPI GC_DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved) + { + thread_id_t self_id; + + UNUSED_ARG(inst); + UNUSED_ARG(reserved); + /* Note that GC_use_threads_discovery should be called by the */ + /* client application at start-up to activate automatic thread */ + /* registration (it is the default GC behavior); */ + /* to always have automatic thread registration turned on, the GC */ + /* should be compiled with -D GC_DISCOVER_TASK_THREADS. */ + if (!GC_win32_dll_threads && GC_is_initialized) return TRUE; + + switch (reason) { + case DLL_THREAD_ATTACH: /* invoked for threads other than main */ +# ifdef PARALLEL_MARK + /* Don't register marker threads. */ + if (GC_parallel) { + /* We could reach here only if GC is not initialized. */ + /* Because GC_thr_init() sets GC_parallel to off. */ + break; + } +# endif + /* FALLTHRU */ + case DLL_PROCESS_ATTACH: + /* This may run with the collector uninitialized. */ + self_id = GetCurrentThreadId(); + if (GC_is_initialized && GC_main_thread_id != self_id) { + struct GC_stack_base sb; + /* Don't lock here. */ +# ifdef GC_ASSERTIONS + int sb_result = +# endif + GC_get_stack_base(&sb); + GC_ASSERT(sb_result == GC_SUCCESS); + GC_register_my_thread_inner(&sb, self_id); + } /* o.w. we already did it during GC_thr_init, called by GC_init */ + break; + + case DLL_THREAD_DETACH: + /* We are hopefully running in the context of the exiting thread. */ + if (GC_win32_dll_threads) { + GC_thread t = GC_win32_dll_lookup_thread(GetCurrentThreadId()); + + if (EXPECT(t != NULL, TRUE)) GC_delete_thread(t); + } + break; + + case DLL_PROCESS_DETACH: + if (GC_win32_dll_threads) { + int i; + int my_max = (int)GC_get_max_thread_index(); + + for (i = 0; i <= my_max; ++i) { + if (AO_load(&(dll_thread_table[i].tm.in_use))) + GC_delete_thread((GC_thread)&dll_thread_table[i]); + } + GC_deinit(); + } + break; + } + return TRUE; + } +#endif /* !GC_NO_THREADS_DISCOVERY */ + +# ifndef GC_NO_THREAD_REDIRECTS + /* Restore thread calls redirection. */ +# define CreateThread GC_CreateThread +# define ExitThread GC_ExitThread +# undef _beginthreadex +# define _beginthreadex GC_beginthreadex +# undef _endthreadex +# define _endthreadex GC_endthreadex +# endif /* !GC_NO_THREAD_REDIRECTS */ + +#endif /* GC_WIN32_THREADS */ + + +#ifndef GC_PTHREAD_START_STANDALONE +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2010 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* We want to make sure that GC_thread_exit_proc() is unconditionally */ +/* invoked, even if the client is not compiled with -fexceptions, but */ +/* the GC is. The workaround is to put GC_pthread_start_inner() in its */ +/* own file (pthread_start.c), and undefine __EXCEPTIONS in the GCC */ +/* case at the top of the file. FIXME: it's still unclear whether this */ +/* will actually cause the exit handler to be invoked last when */ +/* thread_exit is called (and if -fexceptions is used). */ +#if !defined(DONT_UNDEF_EXCEPTIONS) && defined(__GNUC__) && defined(__linux__) + /* We undefine __EXCEPTIONS to avoid using GCC __cleanup__ attribute. */ + /* The current NPTL implementation of pthread_cleanup_push uses */ + /* __cleanup__ attribute when __EXCEPTIONS is defined (-fexceptions). */ + /* The stack unwinding and cleanup with __cleanup__ attributes work */ + /* correctly when everything is compiled with -fexceptions, but it is */ + /* not the requirement for this library clients to use -fexceptions */ + /* everywhere. With __EXCEPTIONS undefined, the cleanup routines are */ + /* registered with __pthread_register_cancel thus should work anyway. */ +# undef __EXCEPTIONS +#endif + + + +#if defined(GC_PTHREADS) \ + && !defined(SN_TARGET_ORBIS) && !defined(SN_TARGET_PSP2) + +/* Invoked from GC_pthread_start. */ +GC_INNER_PTHRSTART void *GC_CALLBACK GC_pthread_start_inner( + struct GC_stack_base *sb, void *arg) +{ + void * (*start)(void *); + void * start_arg; + void * result; + volatile GC_thread me = + GC_start_rtn_prepare_thread(&start, &start_arg, sb, arg); + +# ifndef NACL + pthread_cleanup_push(GC_thread_exit_proc, (void *)me); +# endif + result = (*start)(start_arg); +# if defined(DEBUG_THREADS) && !defined(GC_PTHREAD_START_STANDALONE) + GC_log_printf("Finishing thread %p\n", + (void *)GC_PTHREAD_PTRVAL(pthread_self())); +# endif + me -> status = result; + GC_end_stubborn_change(me); /* cannot use GC_dirty */ + /* Cleanup acquires the allocator lock, ensuring that we cannot exit */ + /* while a collection that thinks we are alive is trying to stop us. */ +# ifdef NACL + GC_thread_exit_proc((void *)me); +# else + pthread_cleanup_pop(1); +# endif + return result; +} + +#endif /* GC_PTHREADS */ + +#endif + +/* Restore pthread calls redirection (if altered in */ +/* pthread_stop_world.c, pthread_support.c or win32_threads.c). */ +/* This is only useful if directly included from application */ +/* (instead of linking gc). */ +#ifndef GC_NO_THREAD_REDIRECTS +# define GC_PTHREAD_REDIRECTS_ONLY +# include "gc/gc_pthread_redirects.h" +#endif + +/* The files from "extra" folder are not included. */ + diff --git a/thirdparty/libgc/include/gc.h b/thirdparty/libgc/include/gc.h new file mode 100644 index 0000000..568f422 --- /dev/null +++ b/thirdparty/libgc/include/gc.h @@ -0,0 +1,2 @@ +/* This file is installed for backward compatibility. */ +#include "gc/gc.h" diff --git a/thirdparty/libgc/include/gc/cord.h b/thirdparty/libgc/include/gc/cord.h new file mode 100644 index 0000000..00dcdb9 --- /dev/null +++ b/thirdparty/libgc/include/gc/cord.h @@ -0,0 +1,383 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * Cords are immutable character strings. A number of operations + * on long cords are much more efficient than their strings.h counterpart. + * In particular, concatenation takes constant time independent of the length + * of the arguments. (Cords are represented as trees, with internal + * nodes representing concatenation and leaves consisting of either C + * strings or a functional description of the string.) + * + * The following are reasonable applications of cords. They would perform + * unacceptably if C strings were used: + * - A compiler that produces assembly language output by repeatedly + * concatenating instructions onto a cord representing the output file. + * - A text editor that converts the input file to a cord, and then + * performs editing operations by producing a new cord representing + * the file after each character change (and keeping the old ones in an + * edit history). + * + * For optimal performance, cords should be built by + * concatenating short sections. + * This interface is designed for maximum compatibility with C strings. + * ASCII NUL characters may be embedded in cords using CORD_from_fn. + * This is handled correctly, but CORD_to_char_star will produce a string + * with embedded NULs when given such a cord. + * + * This interface is fairly big, largely for performance reasons. + * The most basic constants and functions: + * + * CORD - the type of a cord; + * CORD_EMPTY - empty cord; + * CORD_len(cord) - length of a cord; + * CORD_cat(cord1,cord2) - concatenation of two cords; + * CORD_substr(cord, start, len) - substring (or subcord); + * CORD_pos i; CORD_FOR(i, cord) { ... CORD_pos_fetch(i) ... } - + * examine each character in a cord (CORD_pos_fetch(i) is the char); + * CORD_fetch(int i) - Retrieve i'th character (slowly); + * CORD_cmp(cord1, cord2) - compare two cords; + * CORD_from_file(FILE * f) - turn a read-only file into a cord; + * CORD_to_char_star(cord) - convert to C string + * (non-NULL C constant strings are cords); + * CORD_printf (etc.) - cord version of printf (use %r for cords). + */ + +#ifndef CORD_H +#define CORD_H + +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + +#if defined(GC_DLL) && !defined(CORD_NOT_DLL) && !defined(CORD_API) + /* Same as for GC_API in gc_config_macros.h. */ +# ifdef CORD_BUILD +# if defined(__MINGW32__) && !defined(__cplusplus) || defined(__CEGCC__) +# define CORD_API __declspec(dllexport) +# elif defined(_MSC_VER) || defined(__DMC__) || defined(__BORLANDC__) \ + || defined(__CYGWIN__) || defined(__MINGW32__) \ + || defined(__WATCOMC__) +# define CORD_API extern __declspec(dllexport) +# elif defined(__GNUC__) && !defined(GC_NO_VISIBILITY) \ + && (__GNUC__ >= 4 || defined(GC_VISIBILITY_HIDDEN_SET)) + /* Only matters if used in conjunction with -fvisibility=hidden option. */ +# define CORD_API extern __attribute__((__visibility__("default"))) +# endif +# else +# if defined(__BORLANDC__) || defined(__CEGCC__) || defined(__CYGWIN__) \ + || defined(__DMC__) || defined(_MSC_VER) +# define CORD_API __declspec(dllimport) +# elif defined(__MINGW32__) || defined(__WATCOMC__) +# define CORD_API extern __declspec(dllimport) +# endif +# endif /* !CORD_BUILD */ +#endif /* GC_DLL */ + +#ifndef CORD_API +# define CORD_API extern +#endif + +/* Cords have type const char *. This is cheating quite a bit, and not */ +/* 100% portable. But it means that nonempty character string */ +/* constants may be used as cords directly, provided the string is */ +/* never modified in place. The empty cord is represented by, and */ +/* can be written as, 0. */ + +typedef const char * CORD; + +/* An empty cord is always represented as nil. */ +#define CORD_EMPTY 0 + +/* Is a nonempty cord represented as a C string? */ +#define CORD_IS_STRING(s) (*(s) != '\0') + +/* Concatenate two cords. If the arguments are C strings, they may */ +/* not be subsequently altered. */ +CORD_API CORD CORD_cat(CORD, CORD); + +/* Concatenate a cord and a C string with known length. Except for the */ +/* empty string case, this is a special case of CORD_cat. Since the */ +/* length is known, it can be faster. The string y is shared with the */ +/* resulting CORD. Hence it should not be altered by the caller. */ +CORD_API CORD CORD_cat_char_star(CORD /* x */, + const char * /* y */, size_t /* y_len */); + +/* Compute the length of a cord. */ +CORD_API size_t CORD_len(CORD); + +/* Cords may be represented by functions defining the i-th character. */ +typedef char (*CORD_fn)(size_t /* i */, void * /* client_data */); + +/* Turn a functional description into a cord. */ +CORD_API CORD CORD_from_fn(CORD_fn, void * /* client_data */, + size_t /* len */); + +/* Return the substring (subcord really) of x with length at most n, */ +/* starting at position i. (The initial character has position 0.) */ +CORD_API CORD CORD_substr(CORD, size_t /* i */, size_t /* n */); + +/* Return the argument, but rebalanced to allow more efficient */ +/* character retrieval, substring operations, and comparisons. */ +/* This is useful only for cords that were built using repeated */ +/* concatenation. Guarantees log time access to the result, unless */ +/* x was obtained through a large number of repeated substring ops */ +/* or the embedded functional descriptions take longer to evaluate. */ +/* May reallocate significant parts of the cord. The argument is not */ +/* modified; only the result is balanced. */ +CORD_API CORD CORD_balance(CORD); + +/* The following traverse a cord by applying a function to each */ +/* character. This is occasionally appropriate, especially where */ +/* speed is crucial. But, since C doesn't have nested functions, */ +/* clients of this sort of traversal are clumsy to write. Consider */ +/* the functions that operate on cord positions instead. */ + +/* Function to iteratively apply to individual characters in cord. */ +typedef int (*CORD_iter_fn)(char, void * /* client_data */); + +/* Function to apply to substrings of a cord. Each substring is a */ +/* a C character string, not a general cord. */ +typedef int (*CORD_batched_iter_fn)(const char *, void * /* client_data */); + +#define CORD_NO_FN ((CORD_batched_iter_fn)0) + +/* Apply f1 to each character in the cord, in ascending order, */ +/* starting at position i. If f2 is not CORD_NO_FN, then */ +/* multiple calls to f1 may be replaced by */ +/* a single call to f2. The parameter f2 is provided only to allow */ +/* some optimization by the client. This terminates when the right */ +/* end of this string is reached, or when f1 or f2 return != 0. In the */ +/* latter case CORD_iter returns != 0. Otherwise it returns 0. */ +/* The specified value of i must be < CORD_len(x). */ +CORD_API int CORD_iter5(CORD, size_t /* i */, CORD_iter_fn /* f1 */, + CORD_batched_iter_fn /* f2 */, + void * /* client_data */); + +/* A simpler version of CORD_iter5 that starts at 0, and without f2. */ +CORD_API int CORD_iter(CORD, CORD_iter_fn /* f1 */, void * /* client_data */); +#define CORD_iter(x, f1, cd) CORD_iter5(x, 0, f1, CORD_NO_FN, cd) + +/* Similar to CORD_iter5, but end-to-beginning. No provisions for */ +/* CORD_batched_iter_fn. */ +CORD_API int CORD_riter4(CORD, size_t /* i */, CORD_iter_fn /* f1 */, + void * /* client_data */); + +/* A simpler version of CORD_riter4 that starts at the end. */ +CORD_API int CORD_riter(CORD, CORD_iter_fn /* f1 */, void * /* client_data */); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +/* Functions that operate on cord positions. The easy way to traverse */ +/* cords. A cord position is logically a pair consisting of a cord */ +/* and an index into that cord. But it is much faster to retrieve a */ +/* character based on a position than on an index. Unfortunately, */ +/* positions are big (order of a few 100 bytes), so allocate them with */ +/* caution. */ +/* Things in cord_pos.h should be treated as opaque, except as */ +/* described below. Also, note that CORD_pos_fetch, CORD_next and */ +/* CORD_prev have both macro and function definitions. The former */ +/* may evaluate their argument more than once. */ +#include "cord_pos.h" + +/* + Visible definitions from above: + + typedef CORD_pos[1]; + + * Extract the cord from a position: + CORD CORD_pos_to_cord(CORD_pos p); + + * Extract the current index from a position: + size_t CORD_pos_to_index(CORD_pos p); + + * Fetch the character located at the given position: + char CORD_pos_fetch(CORD_pos p); + + * Initialize the position to refer to the given cord and index: + void CORD_set_pos(CORD_pos p, CORD x, size_t i); + + * Advance the position to the next character: + void CORD_next(CORD_pos p); + + * Move the position to the preceding character: + void CORD_prev(CORD_pos p); + + * Is the position valid, i.e. inside the cord? + int CORD_pos_valid(CORD_pos p); +*/ + +#ifdef __cplusplus + extern "C" { +#endif + +#define CORD_FOR(pos, cord) \ + for (CORD_set_pos(pos, cord, 0); CORD_pos_valid(pos); CORD_next(pos)) + +/* An out-of-memory handler to call. Zero value means do nothing */ +/* special, just abort. */ +#ifndef CORD_DONT_DECLARE_OOM_FN + CORD_API void (*CORD_oom_fn)(void); +#endif +#ifdef CORD_BUILD + /* no export */ void CORD__call_oom_fn(void); +#endif + +/* Dump the representation of x to stdout in an implementation defined */ +/* manner. Intended for debugging only. */ +CORD_API void CORD_dump(CORD); + +/* The following could easily be implemented by the client. They are */ +/* provided by the cord library for convenience. */ + +/* Concatenate a character to the end of a cord. */ +CORD_API CORD CORD_cat_char(CORD, char); + +/* Concatenate n cords. */ +CORD_API CORD CORD_catn(int /* n */, /* CORD */ ...); + +/* Return the character in CORD_substr(x, i, 1). */ +CORD_API char CORD_fetch(CORD /* x */, size_t /* i */); + +/* Return < 0, 0, or > 0, depending on whether x < y, x == y, x > y. */ +CORD_API int CORD_cmp(CORD /* x */, CORD /* y */); + +/* A generalization that takes both starting positions for the */ +/* comparison, and a limit on the number of characters to be compared. */ +CORD_API int CORD_ncmp(CORD /* x */, size_t /* x_start */, + CORD /* y */, size_t /* y_start */, size_t /* len */); + +/* Find the first occurrence of s in x at position start or later. */ +/* Return the position of the first character of s in x, or */ +/* CORD_NOT_FOUND if there is none. */ +CORD_API size_t CORD_str(CORD /* x */, size_t /* start */, CORD /* s */); + +/* Return a cord consisting of i copies of (possibly NUL) c. Dangerous */ +/* in conjunction with CORD_to_char_star. */ +/* The resulting representation takes constant space, independent of i. */ +CORD_API CORD CORD_chars(char /* c */, size_t /* i */); + +#define CORD_nul(i) CORD_chars('\0', (i)) + +/* Turn a file into cord. The file must be seekable. Its contents */ +/* must remain constant. The file may be accessed as an immediate */ +/* result of this call and/or as a result of subsequent accesses to */ +/* the cord. Short files are likely to be immediately read, but */ +/* long files are likely to be read on demand, possibly relying on */ +/* stdio for buffering. */ +/* We must have exclusive access to the descriptor f, i.e. we may */ +/* read it at any time, and expect the file pointer to be */ +/* where we left it. Normally this should be invoked as */ +/* CORD_from_file(fopen(...)). */ +/* CORD_from_file arranges to close the file descriptor when it is no */ +/* longer needed (e.g. when the result becomes inaccessible). */ +/* The file f must be such that ftell reflects the actual character */ +/* position in the file, i.e. the number of characters that can be */ +/* or were read with fread. On UNIX systems this is always true. */ +/* On Windows systems, f must be opened in binary mode. */ +CORD_API CORD CORD_from_file(FILE * /* f */); + +/* Equivalent to the above, except that the entire file will be read */ +/* and the file pointer will be closed immediately. */ +/* The binary mode restriction from above does not apply. */ +CORD_API CORD CORD_from_file_eager(FILE *); + +/* Equivalent to the above, except that the file will be read on */ +/* demand. The binary mode restriction applies. */ +CORD_API CORD CORD_from_file_lazy(FILE *); + +/* Turn a cord into a C string. The result shares no structure with */ +/* x, and is thus modifiable. */ +CORD_API char * CORD_to_char_star(CORD /* x */); + +/* Turn a C string into a CORD. The C string is copied, and so may */ +/* subsequently be modified. */ +CORD_API CORD CORD_from_char_star(const char *); + +/* Identical to the above, but the result may share structure with */ +/* the argument and is thus not modifiable. */ +CORD_API const char * CORD_to_const_char_star(CORD); + +/* Write a cord to a file, starting at the current position. */ +/* No trailing NULs are newlines are added. */ +/* Returns EOF if a write error occurs, 1 otherwise. */ +CORD_API int CORD_put(CORD, FILE *); + +/* "Not found" result for the following two functions. */ +#define CORD_NOT_FOUND ((size_t)(-1)) + +/* A vague analog of strchr. Returns the position (an integer, not */ +/* a pointer) of the first occurrence of (char) c inside x at position */ +/* i or later. The value i must be < CORD_len(x). */ +CORD_API size_t CORD_chr(CORD /* x */, size_t /* i */, int /* c */); + +/* A vague analog of strrchr. Returns index of the last occurrence */ +/* of (char) c inside x at position i or earlier. The value i */ +/* must be < CORD_len(x). */ +CORD_API size_t CORD_rchr(CORD /* x */, size_t /* i */, int /* c */); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +/* The following ones provide functionality similar to the ANSI C */ +/* functions with corresponding names, but with the following */ +/* additions and changes: */ +/* 1. A %r conversion specification specifies a CORD argument. Field */ +/* width, precision, etc. have the same semantics as for %s. */ +/* (Note that %c, %C, and %S were already taken.) */ +/* 2. The format string is represented as a CORD. */ +/* 3. CORD_sprintf and CORD_vsprintf assign the result through the 1st */ +/* argument. Unlike their ANSI C versions, there is no need to */ +/* guess the correct buffer size. */ +/* 4. Most of the conversions are implement through the native */ +/* vsprintf. Hence they are usually no faster, and */ +/* idiosyncrasies of the native printf are preserved. However, */ +/* CORD arguments to CORD_sprintf and CORD_vsprintf are NOT copied; */ +/* the result shares the original structure. This may make them */ +/* very efficient in some unusual applications. */ +/* The format string is copied. */ +/* All functions return the number of characters generated or -1 on */ +/* error. This complies with the ANSI standard, but is inconsistent */ +/* with some older implementations of sprintf. */ + +/* The implementation of these is probably less portable than the rest */ +/* of this package. */ + +#ifndef CORD_NO_IO + +#include + +# ifdef __cplusplus + extern "C" { +# endif + + CORD_API int CORD_sprintf(CORD * /* out */, CORD /* format */, ...); + CORD_API int CORD_vsprintf(CORD * /* out */, CORD /* format */, va_list); + CORD_API int CORD_fprintf(FILE *, CORD /* format */, ...); + CORD_API int CORD_vfprintf(FILE *, CORD /* format */, va_list); + CORD_API int CORD_printf(CORD /* format */, ...); + CORD_API int CORD_vprintf(CORD /* format */, va_list); + +# ifdef __cplusplus + } /* extern "C" */ +# endif + +#endif /* CORD_NO_IO */ + +#endif /* CORD_H */ diff --git a/thirdparty/libgc/include/gc/cord_pos.h b/thirdparty/libgc/include/gc/cord_pos.h new file mode 100644 index 0000000..4ea8ff4 --- /dev/null +++ b/thirdparty/libgc/include/gc/cord_pos.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* This should never be included directly; included only from cord.h. */ +#if !defined(CORD_POSITION_H) && defined(CORD_H) +#define CORD_POSITION_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* The representation of CORD_position. This is private to the */ +/* implementation, but the size is known to clients. Also */ +/* the implementation of some exported macros relies on it. */ +/* Don't use anything defined here and not in cord.h. */ + +#define CORD_MAX_DEPTH 48 + /* The maximum depth of a balanced cord + 1. */ + /* We do not let cords get deeper than this maximum. */ + +struct CORD_pe { + CORD pe_cord; + size_t pe_start_pos; +}; + +/* A structure describing an entry on the path from the root */ +/* to current position. */ +typedef struct CORD_Pos { + size_t cur_pos; + + int path_len; +# define CORD_POS_INVALID 0x55555555 + /* path_len == CORD_POS_INVALID <==> position invalid */ + + const char *cur_leaf; /* Current leaf, if it is a string. */ + /* If the current leaf is a function, */ + /* then this may point to function_buf */ + /* containing the next few characters. */ + /* Always points to a valid string */ + /* containing the current character */ + /* unless cur_end is 0. */ + size_t cur_start; /* Start position of cur_leaf. */ + size_t cur_end; /* Ending position of cur_leaf; */ + /* 0 if cur_leaf is invalid. */ + struct CORD_pe path[CORD_MAX_DEPTH + 1]; + /* path[path_len] is the leaf corresponding to cur_pos */ + /* path[0].pe_cord is the cord we point to. */ +# define CORD_FUNCTION_BUF_SZ 8 + char function_buf[CORD_FUNCTION_BUF_SZ]; + /* Space for next few chars */ + /* from function node. */ +} CORD_pos[1]; + +/* Extract the cord from a position. */ +CORD_API CORD CORD_pos_to_cord(CORD_pos); + +/* Extract the current index from a position. */ +CORD_API size_t CORD_pos_to_index(CORD_pos); + +/* Fetch the character located at the given position. */ +CORD_API char CORD_pos_fetch(CORD_pos); + +/* Initialize the position to refer to the given cord and index. */ +/* Note that this is the most expensive function on positions. */ +CORD_API void CORD_set_pos(CORD_pos, CORD, size_t /* index */); + +/* Advance the position to the next character. */ +/* p must be initialized and valid. */ +/* Invalidates p if past end. */ +CORD_API void CORD_next(CORD_pos /* p */); + +/* Move the position to the preceding character. */ +/* p must be initialized and valid. */ +/* Invalidates p if past beginning. */ +CORD_API void CORD_prev(CORD_pos /* p */); + +/* Is the position valid, i.e. inside the cord? */ +CORD_API int CORD_pos_valid(CORD_pos); + +CORD_API char CORD__pos_fetch(CORD_pos); +CORD_API void CORD__next(CORD_pos); +CORD_API void CORD__prev(CORD_pos); + +#define CORD_pos_fetch(p) \ + ((p)[0].cur_end != 0 ? \ + (p)[0].cur_leaf[(p)[0].cur_pos - (p)[0].cur_start] \ + : CORD__pos_fetch(p)) + +#define CORD_next(p) \ + ((p)[0].cur_pos + 1 < (p)[0].cur_end ? \ + (p)[0].cur_pos++ \ + : (CORD__next(p), 0U)) + +#define CORD_prev(p) \ + ((p)[0].cur_end != 0 && (p)[0].cur_pos > (p)[0].cur_start ? \ + (p)[0].cur_pos-- \ + : (CORD__prev(p), 0U)) + +#define CORD_pos_to_index(p) ((p)[0].cur_pos) + +#define CORD_pos_to_cord(p) ((p)[0].path[0].pe_cord) + +#define CORD_pos_valid(p) ((p)[0].path_len != CORD_POS_INVALID) + +/* Some grubby stuff for performance-critical friends: */ + +#define CORD_pos_chars_left(p) ((long)(p)[0].cur_end - (long)(p)[0].cur_pos) + /* Number of characters in cache. <= 0 ==> none */ + +#define CORD_pos_advance(p,n) ((p)[0].cur_pos += (n) - 1, CORD_next(p)) + /* Advance position by n characters; */ + /* 0 < n < CORD_pos_chars_left(p). */ + +#define CORD_pos_cur_char_addr(p) \ + ((p)[0].cur_leaf + ((p)[0].cur_pos - (p)[0].cur_start)) + /* Address of the current character in cache. */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif diff --git a/thirdparty/libgc/include/gc/ec.h b/thirdparty/libgc/include/gc/ec.h new file mode 100644 index 0000000..e7c97d4 --- /dev/null +++ b/thirdparty/libgc/include/gc/ec.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef EC_H +#define EC_H + +#ifndef CORD_H +# include "cord.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* Extensible cords are strings that may be destructively appended to. */ +/* They allow fast construction of cords from characters that are */ +/* being read from a stream. */ +/* + * A client might look like: + * + * { + * CORD_ec x; + * CORD result; + * char c; + * FILE *f; + * + * ... + * CORD_ec_init(x); + * while (...) { + * c = getc(f); + * ... + * CORD_ec_append(x, c); + * } + * result = CORD_balance(CORD_ec_to_cord(x)); + * + * If a C string is desired as the final result, the call to CORD_balance + * may be replaced by a call to CORD_to_char_star. + */ + +#ifndef CORD_BUFSZ +# define CORD_BUFSZ 128 +#endif + +/* This structure represents the concatenation of ec_cord with */ +/* ec_buf[0 .. ec_bufptr-ec_buf-1]. */ +typedef struct CORD_ec_struct { + CORD ec_cord; + char * ec_bufptr; + char ec_buf[CORD_BUFSZ+1]; +} CORD_ec[1]; + +/* Flush the buffer part of the extended cord into ec_cord. */ +CORD_API void CORD_ec_flush_buf(CORD_ec); + +/* Convert an extensible cord to a cord. */ +#define CORD_ec_to_cord(x) (CORD_ec_flush_buf(x), (x)[0].ec_cord) + +/* Initialize an extensible cord. */ +#define CORD_ec_init(x) \ + ((x)[0].ec_cord = 0, (void)((x)[0].ec_bufptr = (x)[0].ec_buf)) + +/* Append a character to an extensible cord. */ +#define CORD_ec_append(x, c) \ + ((void)((x)[0].ec_bufptr == (x)[0].ec_buf + CORD_BUFSZ \ + ? (CORD_ec_flush_buf(x), 0) : 0), \ + (void)(*(x)[0].ec_bufptr++ = (c))) + +/* Append a cord to an extensible cord. Structure remains shared with */ +/* original. */ +CORD_API void CORD_ec_append_cord(CORD_ec, CORD); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* EC_H */ diff --git a/thirdparty/libgc/include/gc/gc.h b/thirdparty/libgc/include/gc/gc.h new file mode 100644 index 0000000..14876cc --- /dev/null +++ b/thirdparty/libgc/include/gc/gc.h @@ -0,0 +1,2332 @@ +/* + * Copyright (c) 1988-1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2007 Free Software Foundation, Inc. + * Copyright (c) 2000-2011 by Hewlett-Packard Development Company. + * Copyright (c) 2009-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * Note that this defines a large number of tuning hooks, which can + * safely be ignored in nearly all cases. For normal use it suffices + * to call only GC_MALLOC and perhaps GC_REALLOC. + * For better performance, also look at GC_MALLOC_ATOMIC, and + * GC_enable_incremental. If you need an action to be performed + * immediately before an object is collected, look at GC_register_finalizer. + * Everything else is best ignored unless you encounter performance + * problems. + */ + +#ifndef GC_H +#define GC_H + +/* Help debug mixed up preprocessor symbols. */ +#if (defined(WIN64) && !defined(_WIN64)) && defined(_MSC_VER) +#pragma message("Warning: Expecting _WIN64 for x64 targets! Notice the leading underscore!") +#endif + +#include "gc_version.h" + /* Define version numbers here to allow test on build machine */ + /* for cross-builds. Note that this defines the header */ + /* version number, which may or may not match that of the */ + /* dynamic library. GC_get_version() can be used to obtain */ + /* the latter. */ + +#include "gc_config_macros.h" + +#ifdef __cplusplus + extern "C" { +#endif + +typedef void * GC_PTR; /* preserved only for backward compatibility */ + +/* Define word and signed word to be unsigned and signed types of the */ +/* size as char* or void*. */ +typedef GC_UNSIGNEDWORD GC_word; +typedef GC_SIGNEDWORD GC_signed_word; +#undef GC_SIGNEDWORD +#undef GC_UNSIGNEDWORD + +/* Get the GC library version. The returned value is a constant in the */ +/* form: ((version_major<<16) | (version_minor<<8) | version_micro). */ +GC_API GC_VERSION_VAL_T GC_CALL GC_get_version(void); + +/* Public read-only variables */ +/* The supplied getter functions are preferred for new code. */ + +GC_API GC_ATTR_DEPRECATED GC_word GC_gc_no; + /* Counter incremented per collection. */ + /* Includes empty GCs at startup. */ +GC_API GC_word GC_CALL GC_get_gc_no(void); + /* GC_get_gc_no() is unsynchronized, so */ + /* it requires GC_call_with_reader_lock() to */ + /* avoid data race on multiprocessors. */ + +#ifdef GC_THREADS + /* GC is parallelized for performance on multiprocessors. Set to */ + /* a non-zero value when client calls GC_start_mark_threads() */ + /* directly or starts the first non-main thread, provided the */ + /* collector is built with PARALLEL_MARK defined, and either */ + /* GC_MARKERS (or GC_NPROCS) environment variable is set to a value */ + /* bigger than 1, or multiple cores (processors) are available, or */ + /* the client calls GC_set_markers_count() before GC initialization. */ + /* After setting, GC_parallel value is equal to the number of marker */ + /* threads minus one (i.e. the number of existing parallel marker */ + /* threads excluding the initiating one). */ + GC_API GC_ATTR_DEPRECATED int GC_parallel; +#endif + +/* Return value of GC_parallel. Does not acquire the allocator lock. */ +GC_API int GC_CALL GC_get_parallel(void); + +/* Set the number of marker threads (including the initiating one) */ +/* to the desired value at start-up. Zero value means the collector */ +/* is to decide. If the correct non-zero value is passed, then later */ +/* GC_parallel will be set to the value minus one. Has no effect if */ +/* called after GC initialization. Does not itself cause creation of */ +/* the marker threads. Does not use any synchronization. */ +GC_API void GC_CALL GC_set_markers_count(unsigned); + +/* Public R/W variables */ +/* The supplied setter and getter functions are preferred for new code. */ + +typedef void * (GC_CALLBACK * GC_oom_func)(size_t /* bytes_requested */); +GC_API GC_ATTR_DEPRECATED GC_oom_func GC_oom_fn; + /* When there is insufficient memory to satisfy */ + /* an allocation request, we return */ + /* (*GC_oom_fn)(size). If it returns, it must */ + /* return either NULL or a valid pointer to */ + /* a previously allocated heap object. */ + /* By default, this just returns NULL. */ + /* GC_oom_fn must not be 0. Both the setter */ + /* and the getter acquire the allocator lock */ + /* (in the reader mode in case of the getter) */ + /* to avoid data race. */ +GC_API void GC_CALL GC_set_oom_fn(GC_oom_func) GC_ATTR_NONNULL(1); +GC_API GC_oom_func GC_CALL GC_get_oom_fn(void); + +typedef void (GC_CALLBACK * GC_on_heap_resize_proc)(GC_word /* new_size */); +GC_API GC_ATTR_DEPRECATED GC_on_heap_resize_proc GC_on_heap_resize; + /* Invoked when the heap grows or shrinks. */ + /* Called with the world stopped (and the */ + /* allocator lock held). May be 0. */ +GC_API void GC_CALL GC_set_on_heap_resize(GC_on_heap_resize_proc); +GC_API GC_on_heap_resize_proc GC_CALL GC_get_on_heap_resize(void); + /* Both the setter and the getter acquire the */ + /* allocator lock (in the reader mode in case */ + /* of the getter). */ + +typedef enum { + GC_EVENT_START /* COLLECTION */, + GC_EVENT_MARK_START, + GC_EVENT_MARK_END, + GC_EVENT_RECLAIM_START, + GC_EVENT_RECLAIM_END, + GC_EVENT_END /* COLLECTION */, + GC_EVENT_PRE_STOP_WORLD /* STOPWORLD_BEGIN */, + GC_EVENT_POST_STOP_WORLD /* STOPWORLD_END */, + GC_EVENT_PRE_START_WORLD /* STARTWORLD_BEGIN */, + GC_EVENT_POST_START_WORLD /* STARTWORLD_END */, + GC_EVENT_THREAD_SUSPENDED, + GC_EVENT_THREAD_UNSUSPENDED +} GC_EventType; + +typedef void (GC_CALLBACK * GC_on_collection_event_proc)(GC_EventType); + /* Invoked to indicate progress through the */ + /* collection process. Not used for thread */ + /* suspend/resume notifications. Called with */ + /* the allocator lock held (or, even, the world */ + /* stopped). May be 0 (means no notifier). */ +GC_API void GC_CALL GC_set_on_collection_event(GC_on_collection_event_proc); +GC_API GC_on_collection_event_proc GC_CALL GC_get_on_collection_event(void); + /* Both the setter and the getter acquire the */ + /* allocator lock (in the reader mode in case */ + /* of the getter). */ + +#if defined(GC_THREADS) || (defined(GC_BUILD) && defined(NN_PLATFORM_CTR)) + typedef void (GC_CALLBACK * GC_on_thread_event_proc)(GC_EventType, + void * /* thread_id */); + /* Invoked when a thread is suspended or */ + /* resumed during collection. Called with the */ + /* allocator lock held (and the world stopped */ + /* partially). May be 0 (means no notifier). */ + GC_API void GC_CALL GC_set_on_thread_event(GC_on_thread_event_proc); + GC_API GC_on_thread_event_proc GC_CALL GC_get_on_thread_event(void); + /* Both the setter and the getter acquire the */ + /* allocator lock (in the reader mode in case */ + /* of the getter). */ +#endif + +GC_API GC_ATTR_DEPRECATED int GC_find_leak; + /* Set to true to turn on the leak-finding mode */ + /* (do not actually garbage collect, but simply */ + /* report inaccessible memory that was not */ + /* deallocated with GC_FREE). Initial value */ + /* is determined by FIND_LEAK macro. */ + /* The value should not typically be modified */ + /* after GC initialization (and, thus, it does */ + /* not use or need synchronization). */ +GC_API void GC_CALL GC_set_find_leak(int); +GC_API int GC_CALL GC_get_find_leak(void); + +GC_API GC_ATTR_DEPRECATED int GC_all_interior_pointers; + /* Arrange for pointers to object interiors to */ + /* be recognized as valid. Typically should */ + /* not be changed after GC initialization (in */ + /* case of calling it after the GC is */ + /* initialized, the setter acquires the */ + /* allocator lock. The initial value depends */ + /* on whether the GC is built with */ + /* ALL_INTERIOR_POINTERS macro defined or not. */ + /* Unless DONT_ADD_BYTE_AT_END is defined, this */ + /* also affects whether sizes are increased by */ + /* at least a byte to allow "off the end" */ + /* pointer recognition (but the size is not */ + /* increased for uncollectible objects as well */ + /* as for ignore-off-page objects of at least */ + /* heap block size). Must be only 0 or 1. */ +GC_API void GC_CALL GC_set_all_interior_pointers(int); +GC_API int GC_CALL GC_get_all_interior_pointers(void); + +GC_API GC_ATTR_DEPRECATED int GC_finalize_on_demand; + /* If nonzero, finalizers will only be run in */ + /* response to an explicit GC_invoke_finalizers */ + /* call. The default is determined by whether */ + /* the FINALIZE_ON_DEMAND macro is defined */ + /* when the collector is built. The setter and */ + /* the getter are unsynchronized. */ +GC_API void GC_CALL GC_set_finalize_on_demand(int); +GC_API int GC_CALL GC_get_finalize_on_demand(void); + +GC_API GC_ATTR_DEPRECATED int GC_java_finalization; + /* Mark objects reachable from finalizable */ + /* objects in a separate post-pass. This makes */ + /* it a bit safer to use non-topologically- */ + /* ordered finalization. Default value is */ + /* determined by JAVA_FINALIZATION macro. */ + /* Enables GC_register_finalizer_unreachable to */ + /* work correctly. The setter and the getter */ + /* are unsynchronized. */ +GC_API void GC_CALL GC_set_java_finalization(int); +GC_API int GC_CALL GC_get_java_finalization(void); + +typedef void (GC_CALLBACK * GC_finalizer_notifier_proc)(void); +GC_API GC_ATTR_DEPRECATED GC_finalizer_notifier_proc GC_finalizer_notifier; + /* Invoked by the collector when there are */ + /* objects to be finalized. Invoked at most */ + /* once per GC cycle. Never invoked unless */ + /* GC_finalize_on_demand is set. */ + /* Typically this will notify a finalization */ + /* thread, which will call GC_invoke_finalizers */ + /* in response. May be 0 (means no notifier). */ + /* Both the setter and the getter acquire the */ + /* allocator lock (in the reader mode in case */ + /* of the getter). */ +GC_API void GC_CALL GC_set_finalizer_notifier(GC_finalizer_notifier_proc); +GC_API GC_finalizer_notifier_proc GC_CALL GC_get_finalizer_notifier(void); + +/* The functions called to report pointer checking errors. Called */ +/* without the allocator lock held. The default behavior is to fail */ +/* with the appropriate message which includes the pointers. The */ +/* functions (variables) must not be 0. Both the setters and the */ +/* getters are unsynchronized. */ +typedef void (GC_CALLBACK * GC_valid_ptr_print_proc_t)(void *); +typedef void (GC_CALLBACK * GC_same_obj_print_proc_t)(void * /* p */, + void * /* q */); +GC_API GC_ATTR_DEPRECATED GC_same_obj_print_proc_t GC_same_obj_print_proc; +GC_API GC_ATTR_DEPRECATED GC_valid_ptr_print_proc_t + GC_is_valid_displacement_print_proc; +GC_API GC_ATTR_DEPRECATED GC_valid_ptr_print_proc_t GC_is_visible_print_proc; +GC_API void GC_CALL GC_set_same_obj_print_proc(GC_same_obj_print_proc_t) + GC_ATTR_NONNULL(1); +GC_API GC_same_obj_print_proc_t GC_CALL GC_get_same_obj_print_proc(void); +GC_API void GC_CALL GC_set_is_valid_displacement_print_proc( + GC_valid_ptr_print_proc_t) GC_ATTR_NONNULL(1); +GC_API GC_valid_ptr_print_proc_t GC_CALL + GC_get_is_valid_displacement_print_proc(void); +GC_API void GC_CALL GC_set_is_visible_print_proc(GC_valid_ptr_print_proc_t) + GC_ATTR_NONNULL(1); +GC_API GC_valid_ptr_print_proc_t GC_CALL GC_get_is_visible_print_proc(void); + +GC_API +# ifndef GC_DONT_GC + GC_ATTR_DEPRECATED +# endif + int GC_dont_gc; /* != 0 ==> Do not collect. This overrides */ + /* explicit GC_gcollect() calls as well. */ + /* Used as a counter, so that nested enabling */ + /* and disabling work correctly. Should */ + /* normally be updated with GC_enable() and */ + /* GC_disable() calls. Direct assignment to */ + /* GC_dont_gc is deprecated. To check whether */ + /* GC is disabled, GC_is_disabled() is */ + /* preferred for new code. */ + +GC_API GC_ATTR_DEPRECATED int GC_dont_expand; + /* Do not expand the heap unless explicitly */ + /* requested or forced to. The setter and the */ + /* getter are unsynchronized. */ +GC_API void GC_CALL GC_set_dont_expand(int); +GC_API int GC_CALL GC_get_dont_expand(void); + +GC_API GC_ATTR_DEPRECATED int GC_use_entire_heap; + /* Causes the non-incremental collector to use the */ + /* entire heap before collecting. This sometimes */ + /* results in more large block fragmentation, since */ + /* very large blocks will tend to get broken up */ + /* during each GC cycle. It is likely to result in a */ + /* larger working set, but lower collection */ + /* frequencies, and hence fewer instructions executed */ + /* in the collector. */ + +GC_API GC_ATTR_DEPRECATED int GC_full_freq; + /* Number of partial collections between full */ + /* collections. Matters only if */ + /* GC_is_incremental_mode(). Full collections */ + /* are also triggered if the collector detects */ + /* a substantial increase in the number of the */ + /* in-use heap blocks. Values in the tens are */ + /* now perfectly reasonable, unlike for earlier */ + /* GC versions. The setter and the getter are */ + /* unsynchronized, so GC_call_with_alloc_lock() */ + /* (GC_call_with_reader_lock() in case of the */ + /* getter) is required to avoid data race (if */ + /* the value is modified after the GC is put */ + /* into the multi-threaded mode). */ +GC_API void GC_CALL GC_set_full_freq(int); +GC_API int GC_CALL GC_get_full_freq(void); + +GC_API GC_ATTR_DEPRECATED GC_word GC_non_gc_bytes; + /* Bytes not considered candidates for */ + /* collection. Used only to control scheduling */ + /* of collections. Updated by */ + /* GC_malloc_uncollectable and GC_free. */ + /* Wizards only. The setter and the getter are */ + /* unsynchronized, so GC_call_with_alloc_lock() */ + /* (GC_call_with_reader_lock() in case of the */ + /* getter) is required to avoid data race (if */ + /* the value is modified after the GC is put */ + /* into the multi-threaded mode). */ +GC_API void GC_CALL GC_set_non_gc_bytes(GC_word); +GC_API GC_word GC_CALL GC_get_non_gc_bytes(void); + +GC_API GC_ATTR_DEPRECATED int GC_no_dls; + /* Do not register dynamic library data */ + /* segments automatically. Also, if set by the */ + /* collector itself (during GC), this means */ + /* that such a registration is not supported. */ + /* Wizards only. Should be set only if the */ + /* application explicitly registers all roots. */ + /* (In some environments like Microsoft Windows */ + /* and Apple's Darwin, this may also prevent */ + /* registration of the main data segment as a */ + /* part of the root set.) The setter and the */ + /* getter are unsynchronized. */ +GC_API void GC_CALL GC_set_no_dls(int); +GC_API int GC_CALL GC_get_no_dls(void); + +GC_API GC_ATTR_DEPRECATED GC_word GC_free_space_divisor; + /* We try to make sure that we allocate at */ + /* least N/GC_free_space_divisor bytes between */ + /* collections, where N is twice the number */ + /* of traced bytes, plus the number of untraced */ + /* bytes (bytes in "atomic" objects), plus */ + /* a rough estimate of the root set size. */ + /* N approximates GC tracing work per GC. */ + /* The initial value is GC_FREE_SPACE_DIVISOR. */ + /* Increasing its value will use less space */ + /* but more collection time. Decreasing it */ + /* will appreciably decrease collection time */ + /* at the expense of space. */ + /* The setter and the getter are */ + /* unsynchronized, so GC_call_with_alloc_lock() */ + /* (GC_call_with_reader_lock() in case of the */ + /* getter) is required to avoid data race (if */ + /* the value is modified after the GC is put */ + /* into the multi-threaded mode). In GC v7.1 */ + /* and before, the setter returned the old */ + /* value. */ +GC_API void GC_CALL GC_set_free_space_divisor(GC_word); +GC_API GC_word GC_CALL GC_get_free_space_divisor(void); + +GC_API GC_ATTR_DEPRECATED GC_word GC_max_retries; + /* The maximum number of GCs attempted before */ + /* reporting out of memory after heap */ + /* expansion fails. */ + /* Initially 0. The setter and getter are */ + /* unsynchronized, so GC_call_with_alloc_lock() */ + /* (GC_call_with_reader_lock() in case of the */ + /* getter) is required to avoid data race (if */ + /* the value is modified after the GC is put */ + /* into the multi-threaded mode). */ +GC_API void GC_CALL GC_set_max_retries(GC_word); +GC_API GC_word GC_CALL GC_get_max_retries(void); + +GC_API GC_ATTR_DEPRECATED char *GC_stackbottom; + /* The cold end (bottom) of user stack. */ + /* May be set in the client prior to */ + /* calling any GC_ routines. This */ + /* avoids some overhead, and */ + /* potentially some signals that can */ + /* confuse debuggers. Otherwise the */ + /* collector attempts to set it */ + /* automatically. For multi-threaded */ + /* code, this is the cold end of the */ + /* stack for the primordial thread. */ + /* For multi-threaded code, altering */ + /* GC_stackbottom value directly after */ + /* GC initialization has no effect. */ + /* Portable clients should use */ + /* GC_set_stackbottom(), */ + /* GC_get_stack_base(), */ + /* GC_call_with_gc_active() and */ + /* GC_register_my_thread() instead. */ + +GC_API GC_ATTR_DEPRECATED int GC_dont_precollect; + /* Do not collect as part of GC */ + /* initialization. Should be set only */ + /* if the client wants a chance to */ + /* manually initialize the root set */ + /* before the first collection. */ + /* Interferes with blacklisting. */ + /* Wizards only. The setter and the */ + /* getter are unsynchronized (and no */ + /* external locking is needed since the */ + /* value is accessed at the GC */ + /* initialization only). */ +GC_API void GC_CALL GC_set_dont_precollect(int); +GC_API int GC_CALL GC_get_dont_precollect(void); + +#define GC_TIME_UNLIMITED 999999 +GC_API GC_ATTR_DEPRECATED unsigned long GC_time_limit; + /* If incremental collection is enabled, we try */ + /* to terminate collections after this many */ + /* milliseconds (plus the amount of nanoseconds */ + /* as given in the latest GC_set_time_limit_tv */ + /* call, if any). Not a hard time bound. */ + /* Setting this variable to GC_TIME_UNLIMITED */ + /* essentially disables incremental collection */ + /* (i.e. disables the "pause time exceeded" */ + /* tests) while leaving generational collection */ + /* enabled. The setter and the getter are */ + /* unsynchronized, so GC_call_with_alloc_lock() */ + /* (GC_call_with_reader_lock() in case of the */ + /* getter) is required to avoid data race (if */ + /* the value is modified after the GC is put */ + /* into the multi-threaded mode). The setter */ + /* does not update the value of the nanosecond */ + /* part of the time limit (it is zero unless */ + /* ever set by GC_set_time_limit_tv call). */ +GC_API void GC_CALL GC_set_time_limit(unsigned long); +GC_API unsigned long GC_CALL GC_get_time_limit(void); + +/* A portable type definition of time with a nanosecond precision. */ +struct GC_timeval_s { + unsigned long tv_ms; /* time in milliseconds */ + unsigned long tv_nsec;/* nanoseconds fraction (<1000000) */ +}; + +/* Public procedures */ + +/* Set/get the time limit of the incremental collections. This is */ +/* similar to GC_set_time_limit and GC_get_time_limit but the time is */ +/* provided with the nanosecond precision. The value of tv_nsec part */ +/* should be less than a million. If the value of tv_ms part is */ +/* GC_TIME_UNLIMITED then tv_nsec is ignored. Initially, the value of */ +/* tv_nsec part of the time limit is zero. The functions do not use */ +/* any synchronization. Defined only if the library has been compiled */ +/* without NO_CLOCK. */ +GC_API void GC_CALL GC_set_time_limit_tv(struct GC_timeval_s); +GC_API struct GC_timeval_s GC_CALL GC_get_time_limit_tv(void); + +/* Set/get the minimum value of the ratio of allocated bytes since GC */ +/* to the amount of finalizers created since that GC (value > */ +/* GC_bytes_allocd / (GC_fo_entries - last_fo_entries)) which triggers */ +/* the collection instead heap expansion. The value has no effect in */ +/* the GC incremental mode. The default value is 10000 unless */ +/* GC_ALLOCD_BYTES_PER_FINALIZER macro with a custom value is defined */ +/* to build libgc. The default value might be not the right choice for */ +/* clients where e.g. most objects have a finalizer. Zero value */ +/* effectively disables taking amount of finalizers in the decision */ +/* whether to collect or not. The functions do not use any */ +/* synchronization. */ +GC_API void GC_CALL GC_set_allocd_bytes_per_finalizer(GC_word); +GC_API GC_word GC_CALL GC_get_allocd_bytes_per_finalizer(void); + +/* Tell the collector to start various performance measurements. */ +/* Only the total time taken by full collections is calculated, as */ +/* of now. And, currently, there is no way to stop the measurements. */ +/* The function does not use any synchronization. Defined only if the */ +/* library has been compiled without NO_CLOCK. */ +GC_API void GC_CALL GC_start_performance_measurement(void); + +/* Get the total time of all full collections since the start of the */ +/* performance measurements. Includes time spent in the supplementary */ +/* actions like blacklists promotion, marks clearing, free lists */ +/* reconstruction and objects finalization. The measurement unit is a */ +/* millisecond. Note that the returned value wraps around on overflow. */ +/* The function does not use any synchronization. Defined only if the */ +/* library has been compiled without NO_CLOCK. */ +GC_API unsigned long GC_CALL GC_get_full_gc_total_time(void); + +/* Same as GC_get_full_gc_total_time but takes into account all mark */ +/* phases with the world stopped and nothing else. */ +GC_API unsigned long GC_CALL GC_get_stopped_mark_total_time(void); + +/* Set whether the GC will allocate executable memory pages or not. */ +/* A non-zero argument instructs the collector to allocate memory with */ +/* the executable flag on. Must be called before the collector is */ +/* initialized. May have no effect on some platforms. The default */ +/* value is controlled by NO_EXECUTE_PERMISSION macro (if present then */ +/* the flag is off). Portable clients should have */ +/* GC_set_pages_executable(1) call (before GC_INIT) provided they are */ +/* going to execute code on any of the GC-allocated memory objects. */ +GC_API void GC_CALL GC_set_pages_executable(int); + +/* Returns non-zero value if the GC is set to the allocate-executable */ +/* mode. The mode could be changed by GC_set_pages_executable (before */ +/* GC_INIT) unless the former has no effect on the platform. Does not */ +/* use or need synchronization. */ +GC_API int GC_CALL GC_get_pages_executable(void); + +/* The setter and the getter of the minimum value returned by the */ +/* internal min_bytes_allocd(). The value should not be zero; the */ +/* default value is one. Not synchronized. */ +GC_API void GC_CALL GC_set_min_bytes_allocd(size_t); +GC_API size_t GC_CALL GC_get_min_bytes_allocd(void); + +/* Set/get the size in pages of units operated by GC_collect_a_little. */ +/* The value should not be zero. Not synchronized. */ +GC_API void GC_CALL GC_set_rate(int); +GC_API int GC_CALL GC_get_rate(void); + +/* Set/get the maximum number of prior attempts at the world-stop */ +/* marking. Not synchronized. */ +GC_API void GC_CALL GC_set_max_prior_attempts(int); +GC_API int GC_CALL GC_get_max_prior_attempts(void); + +/* Control whether to disable algorithm deciding if a collection should */ +/* be started when we allocated enough to amortize GC. Both the setter */ +/* and the getter acquire the allocator lock (in the reader mode in */ +/* case of the getter) to avoid data race. */ +GC_API void GC_CALL GC_set_disable_automatic_collection(int); +GC_API int GC_CALL GC_get_disable_automatic_collection(void); + +/* Overrides the default handle-fork mode. Non-zero value means GC */ +/* should install proper pthread_atfork handlers. Has effect only if */ +/* called before GC_INIT. Clients should invoke GC_set_handle_fork */ +/* with non-zero argument if going to use fork with GC functions called */ +/* in the forked child. (Note that such client and atfork handlers */ +/* activities are not fully POSIX-compliant.) GC_set_handle_fork */ +/* instructs GC_init to setup GC fork handlers using pthread_atfork, */ +/* the latter might fail (or, even, absent on some targets) causing */ +/* abort at GC initialization. Issues with missing (or failed) */ +/* pthread_atfork() could be avoided by invocation */ +/* of GC_set_handle_fork(-1) at application start-up and surrounding */ +/* each fork() with the relevant GC_atfork_prepare/parent/child calls. */ +GC_API void GC_CALL GC_set_handle_fork(int); + +/* Routines to handle POSIX fork() manually (no-op if handled */ +/* automatically). GC_atfork_prepare should be called immediately */ +/* before fork(); GC_atfork_parent should be invoked just after fork in */ +/* the branch that corresponds to parent process (i.e., fork result is */ +/* non-zero); GC_atfork_child is to be called immediately in the child */ +/* branch (i.e., fork result is 0). Note that GC_atfork_child() call */ +/* should, of course, precede GC_start_mark_threads call (if any). */ +GC_API void GC_CALL GC_atfork_prepare(void); +GC_API void GC_CALL GC_atfork_parent(void); +GC_API void GC_CALL GC_atfork_child(void); + +/* Initialize the collector. Portable clients should call GC_INIT() */ +/* from the main program instead. */ +GC_API void GC_CALL GC_init(void); + +/* Return 1 (true) if the collector is initialized (or, at least, the */ +/* initialization is in progress), 0 otherwise. */ +GC_API int GC_CALL GC_is_init_called(void); + +/* Perform the collector shutdown. (E.g. dispose critical sections on */ +/* Win32 target.) A duplicate invocation is a no-op. GC_INIT should */ +/* not be called after the shutdown. See also GC_win32_free_heap(). */ +GC_API void GC_CALL GC_deinit(void); + +/* General purpose allocation routines, with roughly malloc calling */ +/* conv. The atomic versions promise that no relevant pointers are */ +/* contained in the object. The non-atomic versions guarantee that the */ +/* new object is cleared. GC_malloc_uncollectable allocates */ +/* an object that is scanned for pointers to collectible */ +/* objects, but is not itself collectible. The object is scanned even */ +/* if it does not appear to be reachable. GC_malloc_uncollectable and */ +/* GC_free called on the resulting object implicitly update */ +/* GC_non_gc_bytes appropriately. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc(size_t /* size_in_bytes */); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_atomic(size_t /* size_in_bytes */); +GC_API GC_ATTR_MALLOC char * GC_CALL GC_strdup(const char *); +GC_API GC_ATTR_MALLOC char * GC_CALL + GC_strndup(const char *, size_t) GC_ATTR_NONNULL(1); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_uncollectable(size_t /* size_in_bytes */); +GC_API GC_ATTR_DEPRECATED void * GC_CALL GC_malloc_stubborn(size_t); + +/* The routines that guarantee the requested alignment of the allocated */ +/* memory object. The align argument should be non-zero and a power */ +/* of two; GC_posix_memalign() also requires it to be not less than */ +/* size of a pointer. Note that posix_memalign() does not change value */ +/* of (*memptr) in case of failure (i.e. when the result is non-zero). */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(2) void * GC_CALL + GC_memalign(size_t /* align */, size_t /* lb */); +GC_API int GC_CALL GC_posix_memalign(void ** /* memptr */, size_t /* align */, + size_t /* lb */) GC_ATTR_NONNULL(1); +#ifndef GC_NO_VALLOC + GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_valloc(size_t /* lb */); + GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_pvalloc(size_t /* lb */); +#endif /* !GC_NO_VALLOC */ + +/* Explicitly deallocate an object. Dangerous if used incorrectly. */ +/* Requires a pointer to the base of an object. */ +/* An object should not be enabled for finalization (and it should not */ +/* contain registered disappearing links of any kind) when it is */ +/* explicitly deallocated. */ +/* GC_free(0) is a no-op, as required by ANSI C for free. */ +GC_API void GC_CALL GC_free(void *); + +/* The "stubborn" objects allocation is not supported anymore. Exists */ +/* only for the backward compatibility. */ +#define GC_MALLOC_STUBBORN(sz) GC_MALLOC(sz) +#define GC_NEW_STUBBORN(t) GC_NEW(t) +#define GC_CHANGE_STUBBORN(p) GC_change_stubborn(p) +GC_API GC_ATTR_DEPRECATED void GC_CALL GC_change_stubborn(const void *); + +/* Inform the collector that the object has been changed. */ +/* Only non-NULL pointer stores into the object are considered to be */ +/* changes. Matters only if the incremental collection is enabled in */ +/* the manual VDB mode (otherwise the function does nothing). */ +/* Should be followed typically by GC_reachable_here called for each */ +/* of the stored pointers. */ +GC_API void GC_CALL GC_end_stubborn_change(const void *) GC_ATTR_NONNULL(1); + +/* Return a pointer to the base (lowest address) of an object given */ +/* a pointer to a location within the object. */ +/* I.e., map an interior pointer to the corresponding base pointer. */ +/* Note that with debugging allocation, this returns a pointer to the */ +/* actual base of the object, i.e. the debug information, not to */ +/* the base of the user object. */ +/* Return 0 if displaced_pointer doesn't point to within a valid */ +/* object. */ +/* Note that a deallocated object in the garbage collected heap */ +/* may be considered valid, even if it has been deallocated with */ +/* GC_free. */ +GC_API void * GC_CALL GC_base(void * /* displaced_pointer */); + +/* Return 1 (true) if the argument points to somewhere in the GC heap, */ +/* 0 otherwise. Primary use is as a fast alternative to GC_base() to */ +/* check whether the given object is allocated by the collector or not. */ +/* It is assumed that the collector is already initialized. */ +GC_API int GC_CALL GC_is_heap_ptr(const void *); + +/* Given a pointer to the base of an object, return its size in bytes. */ +/* The returned size may be slightly larger than what was originally */ +/* requested. */ +GC_API size_t GC_CALL GC_size(const void * /* obj_addr */) GC_ATTR_NONNULL(1); + +/* For compatibility with C library. This is occasionally faster than */ +/* a malloc followed by a bcopy. But if you rely on that, either here */ +/* or with the standard C library, your code is broken. In my */ +/* opinion, it shouldn't have been invented, but now we're stuck. -HB */ +/* The resulting object has the same kind as the original. */ +/* It is an error to have changes enabled for the original object. */ +/* It does not change the content of the object from its beginning to */ +/* the minimum of old size and new_size_in_bytes; the content above in */ +/* case of object size growth is initialized to zero (not guaranteed */ +/* for atomic object type). The function follows ANSI conventions for */ +/* NULL old_object (i.e., equivalent to GC_malloc regardless of new */ +/* size). If new size is zero (and old_object is non-NULL) then the */ +/* call is equivalent to GC_free (and NULL is returned). If old_object */ +/* is non-NULL, it must have been returned by an earlier call to */ +/* GC_malloc* or GC_realloc. In case of the allocation failure, the */ +/* memory pointed by old_object is untouched (and not freed). */ +/* If the returned pointer is not the same as old_object and both of */ +/* them are non-NULL then old_object is freed. Returns either NULL (in */ +/* case of the allocation failure or zero new size) or pointer to the */ +/* allocated memory. */ +GC_API void * GC_CALL GC_realloc(void * /* old_object */, + size_t /* new_size_in_bytes */) + /* 'realloc' attr */ GC_ATTR_ALLOC_SIZE(2); + +/* Increase the heap size explicitly. Includes a GC_init() call. */ +/* Returns 0 on failure, 1 on success. */ +GC_API int GC_CALL GC_expand_hp(size_t /* number_of_bytes */); + +/* Limit the heap size to n bytes. Useful when you're debugging, */ +/* especially on systems that don't handle running out of memory well. */ +/* n == 0 ==> unbounded. This is the default. This setter function is */ +/* unsynchronized (so it might require GC_call_with_alloc_lock to avoid */ +/* data race). */ +GC_API void GC_CALL GC_set_max_heap_size(GC_word /* n */); + +/* Inform the collector that a certain section of statically allocated */ +/* memory contains no pointers to garbage collected memory. Thus it */ +/* need not be scanned. This is sometimes important if the application */ +/* maps large read/write files into the address space, which could be */ +/* mistaken for dynamic library data segments on some systems. */ +/* Both section start and end are not needed to be pointer-aligned. */ +GC_API void GC_CALL GC_exclude_static_roots(void * /* low_address */, + void * /* high_address_plus_1 */); + +/* Clear the number of entries in the exclusion table. Wizards only. */ +/* Should be called typically with the allocator lock held. */ +GC_API void GC_CALL GC_clear_exclusion_table(void); + +/* Clear the set of root segments. Wizards only. */ +GC_API void GC_CALL GC_clear_roots(void); + +/* Add a root segment. Wizards only. */ +/* May merge adjacent or overlapping segments if appropriate. */ +/* Both segment start and end are not needed to be pointer-aligned. */ +/* low_address must not be greater than high_address_plus_1. */ +GC_API void GC_CALL GC_add_roots(void * /* low_address */, + void * /* high_address_plus_1 */); + +/* Remove root segments located fully in the region. Wizards only. */ +GC_API void GC_CALL GC_remove_roots(void * /* low_address */, + void * /* high_address_plus_1 */); + +/* Add a displacement to the set of those considered valid by the */ +/* collector. GC_register_displacement(n) means that if p was returned */ +/* by GC_malloc, then (char *)p + n will be considered to be a valid */ +/* pointer to p. N must be small and less than the size of p. */ +/* (All pointers to the interior of objects from the stack are */ +/* considered valid in any case. This applies to heap objects and */ +/* static data.) */ +/* Preferably, this should be called before any other GC procedures. */ +/* Calling it later adds to the probability of excess memory */ +/* retention. */ +/* This is a no-op if the collector has recognition of */ +/* arbitrary interior pointers enabled, which is now the default. */ +GC_API void GC_CALL GC_register_displacement(size_t /* n */); + +/* The following version should be used if any debugging allocation is */ +/* being done. */ +GC_API void GC_CALL GC_debug_register_displacement(size_t /* n */); + +/* Explicitly trigger a full, world-stop collection. */ +GC_API void GC_CALL GC_gcollect(void); + +/* Same as above but ignores the default stop_func setting and tries to */ +/* unmap as much memory as possible (regardless of the corresponding */ +/* switch setting). The recommended usage: on receiving a system */ +/* low-memory event; before retrying a system call failed because of */ +/* the system is running out of resources. */ +GC_API void GC_CALL GC_gcollect_and_unmap(void); + +/* Trigger a full world-stopped collection. Abort the collection if */ +/* and when stop_func returns a nonzero value. Stop_func will be */ +/* called frequently, and should be reasonably fast. (stop_func is */ +/* called with the allocator lock held and the world might be stopped; */ +/* it's not allowed for stop_func to manipulate pointers to the garbage */ +/* collected heap or call most of GC functions.) This works even */ +/* if virtual dirty bits, and hence incremental collection is not */ +/* available for this architecture. Collections can be aborted faster */ +/* than normal pause times for incremental collection. However, */ +/* aborted collections do no useful work; the next collection needs */ +/* to start from the beginning. stop_func must not be 0. */ +/* GC_try_to_collect() returns 0 if the collection was aborted (or the */ +/* collections are disabled), 1 if it succeeded. */ +typedef int (GC_CALLBACK * GC_stop_func)(void); +GC_API int GC_CALL GC_try_to_collect(GC_stop_func /* stop_func */) + GC_ATTR_NONNULL(1); + +/* Set and get the default stop_func. The default stop_func is used by */ +/* GC_gcollect() and by implicitly triggered collections (except for */ +/* the case when handling out of memory). Must not be 0. Both the */ +/* setter and the getter acquire the allocator lock (in the reader mode */ +/* in case of the getter) to avoid data race. */ +GC_API void GC_CALL GC_set_stop_func(GC_stop_func /* stop_func */) + GC_ATTR_NONNULL(1); +GC_API GC_stop_func GC_CALL GC_get_stop_func(void); + +/* Return the number of bytes in the heap. Excludes collector private */ +/* data structures. Excludes the unmapped memory (returned to the OS). */ +/* Includes empty blocks and fragmentation loss. Includes some pages */ +/* that were allocated but never written. This is an unsynchronized */ +/* getter, so it should be called typically with the allocator lock */ +/* held, at least in the reader mode, to avoid data race on */ +/* multiprocessors (the alternative way is to use GC_get_prof_stats or */ +/* GC_get_heap_usage_safe API calls instead). */ +/* This getter remains lock-free (unsynchronized) for compatibility */ +/* reason since some existing clients call it from a GC callback */ +/* holding the allocator lock. (This API function and the following */ +/* four ones below were made thread-safe in GC v7.2alpha1 and */ +/* reverted back in v7.2alpha7 for the reason described.) */ +GC_API size_t GC_CALL GC_get_heap_size(void); + +/* Return a lower bound on the number of free bytes in the heap */ +/* (excluding the unmapped memory space). This is an unsynchronized */ +/* getter (see GC_get_heap_size comment regarding thread-safety). */ +GC_API size_t GC_CALL GC_get_free_bytes(void); + +/* Return the size (in bytes) of the unmapped memory (which is returned */ +/* to the OS but could be remapped back by the collector later unless */ +/* the OS runs out of system/virtual memory). This is an unsynchronized */ +/* getter (see GC_get_heap_size comment regarding thread-safety). */ +GC_API size_t GC_CALL GC_get_unmapped_bytes(void); + +/* Return the number of bytes allocated since the last collection. */ +/* This is an unsynchronized getter (see GC_get_heap_size comment */ +/* regarding thread-safety). */ +GC_API size_t GC_CALL GC_get_bytes_since_gc(void); + +/* Return the number of explicitly deallocated bytes of memory since */ +/* the recent collection. This is an unsynchronized getter. */ +GC_API size_t GC_CALL GC_get_expl_freed_bytes_since_gc(void); + +/* Return the total number of bytes allocated in this process. */ +/* Never decreases, except due to wrapping. This is an unsynchronized */ +/* getter (see GC_get_heap_size comment regarding thread-safety). */ +GC_API size_t GC_CALL GC_get_total_bytes(void); + +/* Return the total number of bytes obtained from OS. Includes the */ +/* unmapped memory. Never decreases. It is an unsynchronized getter. */ +GC_API size_t GC_CALL GC_get_obtained_from_os_bytes(void); + +/* Return the heap usage information. This is a thread-safe (atomic) */ +/* alternative for the five above getters. (This function acquires */ +/* the allocator lock in the reader mode, thus preventing data race and */ +/* returning the consistent result.) Passing NULL pointer is allowed */ +/* for any argument. Returned (filled in) values are of word type. */ +GC_API void GC_CALL GC_get_heap_usage_safe(GC_word * /* pheap_size */, + GC_word * /* pfree_bytes */, + GC_word * /* punmapped_bytes */, + GC_word * /* pbytes_since_gc */, + GC_word * /* ptotal_bytes */); + +/* Structure used to query GC statistics (profiling information). */ +/* More fields could be added in the future. To preserve compatibility */ +/* new fields should be added only to the end, and no deprecated fields */ +/* should be removed from. */ +struct GC_prof_stats_s { + GC_word heapsize_full; + /* Heap size in bytes (including the area unmapped to OS). */ + /* Same as GC_get_heap_size() + GC_get_unmapped_bytes(). */ + GC_word free_bytes_full; + /* Total bytes contained in free and unmapped blocks. */ + /* Same as GC_get_free_bytes() + GC_get_unmapped_bytes(). */ + GC_word unmapped_bytes; + /* Amount of memory unmapped to OS. Same as the value */ + /* returned by GC_get_unmapped_bytes(). */ + GC_word bytes_allocd_since_gc; + /* Number of bytes allocated since the recent collection. */ + /* Same as returned by GC_get_bytes_since_gc(). */ + GC_word allocd_bytes_before_gc; + /* Number of bytes allocated before the recent garbage */ + /* collection. The value may wrap. Same as the result of */ + /* GC_get_total_bytes() - GC_get_bytes_since_gc(). */ + GC_word non_gc_bytes; + /* Number of bytes not considered candidates for garbage */ + /* collection. Same as returned by GC_get_non_gc_bytes(). */ + GC_word gc_no; + /* Garbage collection cycle number. The value may wrap */ + /* (and could be -1). Same as returned by GC_get_gc_no(). */ + GC_word markers_m1; + /* Number of marker threads (excluding the initiating one). */ + /* Same as returned by GC_get_parallel (or 0 if the */ + /* collector is single-threaded). */ + GC_word bytes_reclaimed_since_gc; + /* Approximate number of reclaimed bytes after recent GC. */ + GC_word reclaimed_bytes_before_gc; + /* Approximate number of bytes reclaimed before the recent */ + /* garbage collection. The value may wrap. */ + GC_word expl_freed_bytes_since_gc; + /* Number of bytes freed explicitly since the recent GC. */ + /* Same as returned by GC_get_expl_freed_bytes_since_gc(). */ + GC_word obtained_from_os_bytes; + /* Total amount of memory obtained from OS, in bytes. */ +}; + +/* Atomically get GC statistics (various global counters). Clients */ +/* should pass the size of the buffer (of GC_prof_stats_s type) to fill */ +/* in the values - this is for interoperability between different GC */ +/* versions, an old client could have fewer fields, and vice versa, */ +/* client could use newer gc.h (with more entries declared in the */ +/* structure) than that of the linked libgc binary; in the latter case, */ +/* unsupported (unknown) fields are filled in with -1. Return the size */ +/* (in bytes) of the filled in part of the structure (excluding all */ +/* unknown fields, if any). */ +GC_API size_t GC_CALL GC_get_prof_stats(struct GC_prof_stats_s *, + size_t /* stats_sz */); +#ifdef GC_THREADS + /* Same as above but unsynchronized (i.e., not holding the allocator */ + /* lock). Clients should call it using GC_call_with_reader_lock() to */ + /* avoid data race on multiprocessors. */ + GC_API size_t GC_CALL GC_get_prof_stats_unsafe(struct GC_prof_stats_s *, + size_t /* stats_sz */); +#endif + +/* Get the element value (converted to bytes) at a given index of */ +/* size_map table which provides requested-to-actual allocation size */ +/* mapping. Assumes the collector is initialized. Returns -1 if the */ +/* index is out of size_map table bounds. Does not use synchronization, */ +/* thus clients should call it using GC_call_with_reader_lock() */ +/* typically to avoid data race on multiprocessors. */ +GC_API size_t GC_CALL GC_get_size_map_at(int i); + +/* Count total memory use (in bytes) by all allocated blocks. Acquires */ +/* the allocator lock in the reader mode. */ +GC_API size_t GC_CALL GC_get_memory_use(void); + +/* Disable garbage collection. Even GC_gcollect calls will be */ +/* ineffective. */ +GC_API void GC_CALL GC_disable(void); + +/* Return 1 (true) if the garbage collection is disabled (i.e., the */ +/* value of GC_dont_gc is non-zero), 0 otherwise. Does not acquire */ +/* the allocator lock. */ +GC_API int GC_CALL GC_is_disabled(void); + +/* Try to re-enable garbage collection. GC_disable() and GC_enable() */ +/* calls nest. Garbage collection is enabled if the number of calls to */ +/* both functions is equal. */ +GC_API void GC_CALL GC_enable(void); + +/* Select whether to use the manual VDB mode for the incremental */ +/* collection. Has no effect if called after enabling the incremental */ +/* collection. The default value is off unless the collector is */ +/* compiled with MANUAL_VDB defined. The manual VDB mode should be */ +/* used only if the client has the appropriate GC_END_STUBBORN_CHANGE */ +/* and GC_reachable_here (or, alternatively, GC_PTR_STORE_AND_DIRTY) */ +/* calls (to ensure proper write barriers). The setter and the getter */ +/* are not synchronized. */ +GC_API void GC_CALL GC_set_manual_vdb_allowed(int); +GC_API int GC_CALL GC_get_manual_vdb_allowed(void); + +/* The constants to represent available VDB techniques. */ +#define GC_VDB_NONE 0 /* means the incremental mode is unsupported */ +#define GC_VDB_MPROTECT 0x1 +#define GC_VDB_MANUAL 0x2 /* means GC_set_manual_vdb_allowed(1) has effect */ +#define GC_VDB_DEFAULT 0x4 /* means no other technique is usable */ +#define GC_VDB_GWW 0x8 +#define GC_VDB_PCR 0x10 +#define GC_VDB_PROC 0x20 +#define GC_VDB_SOFT 0x40 + +/* Get the list of available virtual dirty bits (VDB) techniques. */ +/* The returned value is a constant one, either GC_VDB_NONE, or one or */ +/* more of the above GC_VDB_* constants, or'ed together. May be called */ +/* before the collector is initialized. */ +GC_API unsigned GC_CALL GC_get_supported_vdbs(void); + +/* Enable incremental/generational collection. Not advisable unless */ +/* dirty bits are available or most heap objects are pointer-free */ +/* (atomic) or immutable. Don't use in leak finding mode. Ignored if */ +/* GC_dont_gc is non-zero. Only the generational piece of this is */ +/* functional if GC_time_limit is set to GC_TIME_UNLIMITED. Causes */ +/* thread-local variant of GC_gcj_malloc() to revert to locked */ +/* allocation. Must be called before any such GC_gcj_malloc() calls. */ +/* For best performance, should be called as early as possible. */ +/* On some platforms, calling it later may have adverse effects. */ +/* Safe to call before GC_INIT(). Includes a GC_init() call. */ +GC_API void GC_CALL GC_enable_incremental(void); + +/* Return 1 (true) if the incremental mode is on, 0 otherwise. */ +/* Does not acquire the allocator lock. */ +GC_API int GC_CALL GC_is_incremental_mode(void); + +/* An extended version of GC_is_incremental_mode() to return one of */ +/* GC_VDB_* constants designating which VDB technique is used exactly. */ +/* Does not acquire the allocator lock. */ +GC_API unsigned GC_CALL GC_get_actual_vdb(void); + +#define GC_PROTECTS_POINTER_HEAP 1 /* May protect non-atomic objects. */ +#define GC_PROTECTS_PTRFREE_HEAP 2 +#define GC_PROTECTS_STATIC_DATA 4 /* Currently never. */ +#define GC_PROTECTS_STACK 8 /* Probably impractical. */ + +#define GC_PROTECTS_NONE 0 + +/* Does incremental mode write-protect pages? Returns zero or */ +/* more of the above GC_PROTECTS_*, or'ed together. */ +/* The collector is assumed to be initialized before this call. */ +/* The result is not affected by GC_set_manual_vdb_allowed(). */ +/* Call of GC_enable_incremental() may change the result to */ +/* GC_PROTECTS_NONE if some implementation is chosen at runtime */ +/* not needing to write-protect the pages. */ +GC_API int GC_CALL GC_incremental_protection_needs(void); + +/* Force start of incremental collection. Acquires the allocator lock. */ +/* No-op unless GC incremental mode is on. */ +GC_API void GC_CALL GC_start_incremental_collection(void); + +/* Perform some garbage collection work, if appropriate. */ +/* Return 0 if there is no more work to be done (including the */ +/* case when garbage collection is not appropriate). */ +/* Typically performs an amount of work corresponding roughly */ +/* to marking from one page. May do more work if further */ +/* progress requires it, e.g. if incremental collection is */ +/* disabled. It is reasonable to call this in a wait loop */ +/* until it returns 0. If GC is disabled but the incremental */ +/* collection is already ongoing, then perform marking anyway */ +/* but not stopping the world (and without the reclaim phase). */ +GC_API int GC_CALL GC_collect_a_little(void); + +/* Allocate an object of size lb bytes. The client guarantees that as */ +/* long as the object is live, it will be referenced by a pointer that */ +/* points to somewhere within the first GC heap block (hblk) of the */ +/* object. (This should normally be declared volatile to prevent the */ +/* compiler from invalidating this assertion.) This routine is only */ +/* useful if a large array is being allocated. It reduces the chance */ +/* of accidentally retaining such an array as a result of scanning an */ +/* integer that happens to be an address inside the array. (Actually, */ +/* it reduces the chance of the allocator not finding space for such */ +/* an array, since it will try hard to avoid introducing such a false */ +/* reference.) On a SunOS 4.X or Windows system this is recommended */ +/* for arrays likely to be larger than 100 KB or so. For other systems,*/ +/* or if the collector is not configured to recognize all interior */ +/* pointers, the threshold is normally much higher. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_ignore_off_page(size_t /* lb */); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_atomic_ignore_off_page(size_t /* lb */); + +#ifdef GC_ADD_CALLER +# define GC_EXTRAS GC_RETURN_ADDR, __FILE__, __LINE__ +# define GC_EXTRA_PARAMS GC_word ra, const char * s, int i +#else +# define GC_EXTRAS __FILE__, __LINE__ +# define GC_EXTRA_PARAMS const char * s, int i +#endif + +/* The following is only defined if the library has been suitably */ +/* compiled: */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_atomic_uncollectable(size_t /* size_in_bytes */); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_malloc_atomic_uncollectable(size_t, GC_EXTRA_PARAMS); + +/* Debugging (annotated) allocation. GC_gcollect will check */ +/* objects allocated in this way for overwrites, etc. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_malloc(size_t /* size_in_bytes */, GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_malloc_atomic(size_t /* size_in_bytes */, GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC char * GC_CALL + GC_debug_strdup(const char *, GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC char * GC_CALL + GC_debug_strndup(const char *, size_t, GC_EXTRA_PARAMS) + GC_ATTR_NONNULL(1); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_malloc_uncollectable(size_t /* size_in_bytes */, + GC_EXTRA_PARAMS); +GC_API GC_ATTR_DEPRECATED void * GC_CALL + GC_debug_malloc_stubborn(size_t /* size_in_bytes */, GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_malloc_ignore_off_page(size_t /* size_in_bytes */, + GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_malloc_atomic_ignore_off_page(size_t /* size_in_bytes */, + GC_EXTRA_PARAMS); +GC_API void GC_CALL GC_debug_free(void *); +GC_API void * GC_CALL GC_debug_realloc(void * /* old_object */, + size_t /* new_size_in_bytes */, GC_EXTRA_PARAMS) + /* 'realloc' attr */ GC_ATTR_ALLOC_SIZE(2); +GC_API GC_ATTR_DEPRECATED void GC_CALL GC_debug_change_stubborn(const void *); +GC_API void GC_CALL GC_debug_end_stubborn_change(const void *) + GC_ATTR_NONNULL(1); + +/* Routines that allocate objects with debug information (like the */ +/* above), but just fill in dummy file and line number information. */ +/* Thus they can serve as drop-in malloc/realloc replacements. This */ +/* can be useful for two reasons: */ +/* 1) It allows the collector to be built with DBG_HDRS_ALL defined */ +/* even if some allocation calls come from 3rd party libraries */ +/* that can't be recompiled. */ +/* 2) On some platforms, the file and line information is redundant, */ +/* since it can be reconstructed from a stack trace. On such */ +/* platforms it may be more convenient not to recompile, e.g. for */ +/* leak detection. This can be accomplished by instructing the */ +/* linker to replace malloc/realloc with these. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_malloc_replacement(size_t /* size_in_bytes */); +GC_API /* 'realloc' attr */ GC_ATTR_ALLOC_SIZE(2) void * GC_CALL + GC_debug_realloc_replacement(void * /* object_addr */, + size_t /* size_in_bytes */); + +/* Convenient macros for disappearing links registration working both */ +/* for debug and non-debug allocated objects, and accepting interior */ +/* pointers to object. */ +#define GC_GENERAL_REGISTER_DISAPPEARING_LINK_SAFE(link, obj) \ + GC_general_register_disappearing_link(link, \ + GC_base((/* no const */ void *)(GC_word)(obj))) +#define GC_REGISTER_LONG_LINK_SAFE(link, obj) \ + GC_register_long_link(link, \ + GC_base((/* no const */ void *)(GC_word)(obj))) + +#ifdef GC_DEBUG_REPLACEMENT +# define GC_MALLOC(sz) GC_debug_malloc_replacement(sz) +# define GC_REALLOC(old, sz) GC_debug_realloc_replacement(old, sz) +#elif defined(GC_DEBUG) +# define GC_MALLOC(sz) GC_debug_malloc(sz, GC_EXTRAS) +# define GC_REALLOC(old, sz) GC_debug_realloc(old, sz, GC_EXTRAS) +#else +# define GC_MALLOC(sz) GC_malloc(sz) +# define GC_REALLOC(old, sz) GC_realloc(old, sz) +#endif /* !GC_DEBUG_REPLACEMENT && !GC_DEBUG */ + +#ifdef GC_DEBUG +# define GC_MALLOC_ATOMIC(sz) GC_debug_malloc_atomic(sz, GC_EXTRAS) +# define GC_STRDUP(s) GC_debug_strdup(s, GC_EXTRAS) +# define GC_STRNDUP(s, sz) GC_debug_strndup(s, sz, GC_EXTRAS) +# define GC_MALLOC_ATOMIC_UNCOLLECTABLE(sz) \ + GC_debug_malloc_atomic_uncollectable(sz, GC_EXTRAS) +# define GC_MALLOC_UNCOLLECTABLE(sz) \ + GC_debug_malloc_uncollectable(sz, GC_EXTRAS) +# define GC_MALLOC_IGNORE_OFF_PAGE(sz) \ + GC_debug_malloc_ignore_off_page(sz, GC_EXTRAS) +# define GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(sz) \ + GC_debug_malloc_atomic_ignore_off_page(sz, GC_EXTRAS) +# define GC_FREE(p) GC_debug_free(p) +# define GC_REGISTER_FINALIZER(p, f, d, of, od) \ + GC_debug_register_finalizer(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_IGNORE_SELF(p, f, d, of, od) \ + GC_debug_register_finalizer_ignore_self(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_NO_ORDER(p, f, d, of, od) \ + GC_debug_register_finalizer_no_order(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_UNREACHABLE(p, f, d, of, od) \ + GC_debug_register_finalizer_unreachable(p, f, d, of, od) +# define GC_TOGGLEREF_ADD(p, is_strong) GC_debug_toggleref_add(p, is_strong) +# define GC_END_STUBBORN_CHANGE(p) GC_debug_end_stubborn_change(p) +# define GC_PTR_STORE_AND_DIRTY(p, q) GC_debug_ptr_store_and_dirty(p, q) +# define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \ + GC_GENERAL_REGISTER_DISAPPEARING_LINK_SAFE(link, obj) +# define GC_REGISTER_LONG_LINK(link, obj) \ + GC_REGISTER_LONG_LINK_SAFE(link, obj) +# define GC_REGISTER_DISPLACEMENT(n) GC_debug_register_displacement(n) +#else +# define GC_MALLOC_ATOMIC(sz) GC_malloc_atomic(sz) +# define GC_STRDUP(s) GC_strdup(s) +# define GC_STRNDUP(s, sz) GC_strndup(s, sz) +# define GC_MALLOC_ATOMIC_UNCOLLECTABLE(sz) GC_malloc_atomic_uncollectable(sz) +# define GC_MALLOC_UNCOLLECTABLE(sz) GC_malloc_uncollectable(sz) +# define GC_MALLOC_IGNORE_OFF_PAGE(sz) \ + GC_malloc_ignore_off_page(sz) +# define GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(sz) \ + GC_malloc_atomic_ignore_off_page(sz) +# define GC_FREE(p) GC_free(p) +# define GC_REGISTER_FINALIZER(p, f, d, of, od) \ + GC_register_finalizer(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_IGNORE_SELF(p, f, d, of, od) \ + GC_register_finalizer_ignore_self(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_NO_ORDER(p, f, d, of, od) \ + GC_register_finalizer_no_order(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_UNREACHABLE(p, f, d, of, od) \ + GC_register_finalizer_unreachable(p, f, d, of, od) +# define GC_TOGGLEREF_ADD(p, is_strong) GC_toggleref_add(p, is_strong) +# define GC_END_STUBBORN_CHANGE(p) GC_end_stubborn_change(p) +# define GC_PTR_STORE_AND_DIRTY(p, q) GC_ptr_store_and_dirty(p, q) +# define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \ + GC_general_register_disappearing_link(link, obj) +# define GC_REGISTER_LONG_LINK(link, obj) \ + GC_register_long_link(link, obj) +# define GC_REGISTER_DISPLACEMENT(n) GC_register_displacement(n) +#endif /* !GC_DEBUG */ + +/* The following are included because they are often convenient, and */ +/* reduce the chance for a misspecified size argument. But calls may */ +/* expand to something syntactically incorrect if t is a complicated */ +/* type expression. Note that, unlike C++ new operator, these ones */ +/* may return NULL (if out of memory). */ +#define GC_NEW(t) ((t*)GC_MALLOC(sizeof(t))) +#define GC_NEW_ATOMIC(t) ((t*)GC_MALLOC_ATOMIC(sizeof(t))) +#define GC_NEW_UNCOLLECTABLE(t) ((t*)GC_MALLOC_UNCOLLECTABLE(sizeof(t))) + +#ifdef GC_REQUIRE_WCSDUP + /* This might be unavailable on some targets (or not needed). */ + /* wchar_t should be defined in stddef.h */ + GC_API GC_ATTR_MALLOC wchar_t * GC_CALL + GC_wcsdup(const wchar_t *) GC_ATTR_NONNULL(1); + GC_API GC_ATTR_MALLOC wchar_t * GC_CALL + GC_debug_wcsdup(const wchar_t *, GC_EXTRA_PARAMS) GC_ATTR_NONNULL(1); +# ifdef GC_DEBUG +# define GC_WCSDUP(s) GC_debug_wcsdup(s, GC_EXTRAS) +# else +# define GC_WCSDUP(s) GC_wcsdup(s) +# endif +#endif /* GC_REQUIRE_WCSDUP */ + +/* Finalization. Some of these primitives are grossly unsafe. */ +/* The idea is to make them both cheap, and sufficient to build */ +/* a safer layer, closer to Modula-3, Java, or PCedar finalization. */ +/* The interface represents my conclusions from a long discussion */ +/* with Alan Demers, Dan Greene, Carl Hauser, Barry Hayes, */ +/* Christian Jacobi, and Russ Atkinson. It's not perfect, and */ +/* probably nobody else agrees with it. Hans-J. Boehm 3/13/92 */ +typedef void (GC_CALLBACK * GC_finalization_proc)(void * /* obj */, + void * /* client_data */); + +GC_API void GC_CALL GC_register_finalizer(void * /* obj */, + GC_finalization_proc /* fn */, void * /* cd */, + GC_finalization_proc * /* ofn */, void ** /* ocd */) + GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_debug_register_finalizer(void * /* obj */, + GC_finalization_proc /* fn */, void * /* cd */, + GC_finalization_proc * /* ofn */, void ** /* ocd */) + GC_ATTR_NONNULL(1); + /* When obj is no longer accessible, invoke */ + /* (*fn)(obj, cd). If a and b are inaccessible, and */ + /* a points to b (after disappearing links have been */ + /* made to disappear), then only a will be */ + /* finalized. (If this does not create any new */ + /* pointers to b, then b will be finalized after the */ + /* next collection.) Any finalizable object that */ + /* is reachable from itself by following one or more */ + /* pointers will not be finalized (or collected). */ + /* Thus cycles involving finalizable objects should */ + /* be avoided, or broken by disappearing links. */ + /* All but the last finalizer registered for an object */ + /* is ignored. */ + /* No-op in the leak-finding mode. */ + /* Finalization may be removed by passing 0 as fn. */ + /* Finalizers are implicitly unregistered when they are */ + /* enqueued for finalization (i.e. become ready to be */ + /* finalized). */ + /* The old finalizer and client data are stored in */ + /* *ofn and *ocd. (ofn and/or ocd may be NULL. */ + /* The allocator lock is held while *ofn and *ocd are */ + /* updated. In case of error (no memory to register */ + /* new finalizer), *ofn and *ocd remain unchanged.) */ + /* Fn is never invoked on an accessible object, */ + /* provided hidden pointers are converted to real */ + /* pointers only if the allocator lock is held, at */ + /* least in the reader mode, and such conversions are */ + /* not performed by finalization routines. */ + /* If GC_register_finalizer is aborted as a result of */ + /* a signal, the object may be left with no */ + /* finalization, even if neither the old nor new */ + /* finalizer were NULL. */ + /* Obj should be the starting address of an object */ + /* allocated by GC_malloc or friends. Obj may also be */ + /* NULL or point to something outside GC heap (in this */ + /* case, fn is ignored, *ofn and *ocd are set to NULL). */ + /* Note that any garbage collectible object referenced */ + /* by cd will be considered accessible until the */ + /* finalizer is invoked. */ + +/* Another versions of the above follow. It ignores */ +/* self-cycles, i.e. pointers from a finalizable object to */ +/* itself. There is a stylistic argument that this is wrong, */ +/* but it's unavoidable for C++, since the compiler may */ +/* silently introduce these. It's also benign in that specific */ +/* case. And it helps if finalizable objects are split to */ +/* avoid cycles. */ +/* Note that cd will still be viewed as accessible, even if it */ +/* refers to the object itself. */ +GC_API void GC_CALL GC_register_finalizer_ignore_self(void * /* obj */, + GC_finalization_proc /* fn */, void * /* cd */, + GC_finalization_proc * /* ofn */, void ** /* ocd */) + GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_debug_register_finalizer_ignore_self(void * /* obj */, + GC_finalization_proc /* fn */, void * /* cd */, + GC_finalization_proc * /* ofn */, void ** /* ocd */) + GC_ATTR_NONNULL(1); + +/* Another version of the above. It ignores all cycles. */ +/* It should probably only be used by Java implementations. */ +/* Note that cd will still be viewed as accessible, even if it */ +/* refers to the object itself. */ +GC_API void GC_CALL GC_register_finalizer_no_order(void * /* obj */, + GC_finalization_proc /* fn */, void * /* cd */, + GC_finalization_proc * /* ofn */, void ** /* ocd */) + GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_debug_register_finalizer_no_order(void * /* obj */, + GC_finalization_proc /* fn */, void * /* cd */, + GC_finalization_proc * /* ofn */, void ** /* ocd */) + GC_ATTR_NONNULL(1); + +/* This is a special finalizer that is useful when an object's */ +/* finalizer must be run when the object is known to be no */ +/* longer reachable, not even from other finalizable objects. */ +/* It behaves like "normal" finalization, except that the */ +/* finalizer is not run while the object is reachable from */ +/* other objects specifying unordered finalization. */ +/* Effectively it allows an object referenced, possibly */ +/* indirectly, from an unordered finalizable object to override */ +/* the unordered finalization request. */ +/* This can be used in combination with finalizer_no_order so */ +/* as to release resources that must not be released while an */ +/* object can still be brought back to life by other */ +/* finalizers. */ +/* Only works if GC_java_finalization is set. Probably only */ +/* of interest when implementing a language that requires */ +/* unordered finalization (e.g. Java, C#). */ +GC_API void GC_CALL GC_register_finalizer_unreachable(void * /* obj */, + GC_finalization_proc /* fn */, void * /* cd */, + GC_finalization_proc * /* ofn */, void ** /* ocd */) + GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_debug_register_finalizer_unreachable(void * /* obj */, + GC_finalization_proc /* fn */, void * /* cd */, + GC_finalization_proc * /* ofn */, void ** /* ocd */) + GC_ATTR_NONNULL(1); + +#define GC_NO_MEMORY 2 /* Failure due to lack of memory. */ + +/* The following routine may be used to break cycles between */ +/* finalizable objects, thus causing cyclic finalizable */ +/* objects to be finalized in the correct order. Standard */ +/* use involves calling GC_register_disappearing_link(&p), */ +/* where p is a pointer that is not followed by finalization */ +/* code, and should not be considered in determining */ +/* finalization order. */ +GC_API int GC_CALL GC_register_disappearing_link(void ** /* link */) + GC_ATTR_NONNULL(1); + /* Link should point to a field of a heap allocated */ + /* object obj. *link will be cleared when obj is */ + /* found to be inaccessible. This happens BEFORE any */ + /* finalization code is invoked, and BEFORE any */ + /* decisions about finalization order are made. */ + /* This is useful in telling the finalizer that */ + /* some pointers are not essential for proper */ + /* finalization. This may avoid finalization cycles. */ + /* Note that obj may be resurrected by another */ + /* finalizer, and thus the clearing of *link may */ + /* be visible to non-finalization code. */ + /* There's an argument that an arbitrary action should */ + /* be allowed here, instead of just clearing a pointer. */ + /* But this causes problems if that action alters, or */ + /* examines connectivity. Returns GC_DUPLICATE if link */ + /* was already registered, GC_SUCCESS if registration */ + /* succeeded, GC_NO_MEMORY if it failed for lack of */ + /* memory, and GC_oom_fn did not handle the problem. */ + /* Only exists for backward compatibility. See below: */ + +GC_API int GC_CALL GC_general_register_disappearing_link(void ** /* link */, + const void * /* obj */) + GC_ATTR_NONNULL(1) GC_ATTR_NONNULL(2); + /* A slight generalization of the above. *link is */ + /* cleared when obj first becomes inaccessible. This */ + /* can be used to implement weak pointers easily and */ + /* safely. Typically link will point to a location */ + /* (in a GC-allocated object or not) holding */ + /* a disguised pointer to obj. (A pointer inside */ + /* an "atomic" object is effectively disguised.) */ + /* In this way, weak pointers are broken before any */ + /* object reachable from them gets finalized. */ + /* Each link may be registered only with one obj value, */ + /* i.e. all objects but the last one (link registered */ + /* with) are ignored. This was added after a long */ + /* email discussion with John Ellis. */ + /* link must be non-NULL (and be properly aligned). */ + /* obj must be a pointer to the first word of an object */ + /* allocated by GC_malloc or friends. A link */ + /* disappears when it is unregistered manually, or when */ + /* (*link) is cleared, or when the object containing */ + /* this link is garbage collected. It is unsafe to */ + /* explicitly deallocate the object containing link. */ + /* Explicit deallocation of obj may or may not cause */ + /* link to eventually be cleared. No-op in the */ + /* leak-finding mode. This function can be used to */ + /* implement certain types of weak pointers. Note, */ + /* however, this generally requires that the allocator */ + /* lock is held, at least in the reader mode (e.g. */ + /* using GC_call_with_reader_lock), when the disguised */ + /* pointer is accessed. Otherwise a strong pointer */ + /* could be recreated between the time the collector */ + /* decides to reclaim the object and the link is */ + /* cleared. Returns GC_SUCCESS if registration */ + /* succeeded (a new link is registered), GC_DUPLICATE */ + /* if link was already registered (with some object), */ + /* GC_NO_MEMORY if registration failed for lack of */ + /* memory (and GC_oom_fn did not handle the problem), */ + /* GC_UNIMPLEMENTED if GC_find_leak is true. */ + +GC_API int GC_CALL GC_move_disappearing_link(void ** /* link */, + void ** /* new_link */) + GC_ATTR_NONNULL(2); + /* Moves a link previously registered via */ + /* GC_general_register_disappearing_link (or */ + /* GC_register_disappearing_link). Does not change the */ + /* target object of the weak reference. Does not */ + /* change (*new_link) content. May be called with */ + /* new_link equal to link (to check whether link has */ + /* been registered). Returns GC_SUCCESS on success, */ + /* GC_DUPLICATE if there is already another */ + /* disappearing link at the new location (never */ + /* returned if new_link is equal to link), GC_NOT_FOUND */ + /* if no link is registered at the original location. */ + +GC_API int GC_CALL GC_unregister_disappearing_link(void ** /* link */); + /* Undoes a registration by either of the above two */ + /* routines. Returns 0 if link was not actually */ + /* registered (otherwise returns 1). */ + +GC_API int GC_CALL GC_register_long_link(void ** /* link */, + const void * /* obj */) + GC_ATTR_NONNULL(1) GC_ATTR_NONNULL(2); + /* Similar to GC_general_register_disappearing_link but */ + /* *link only gets cleared when obj becomes truly */ + /* inaccessible. An object becomes truly inaccessible */ + /* when it can no longer be resurrected from its */ + /* finalizer (e.g. by assigning itself to a pointer */ + /* traceable from root). This can be used to implement */ + /* long weak pointers easily and safely. */ + +GC_API int GC_CALL GC_move_long_link(void ** /* link */, + void ** /* new_link */) + GC_ATTR_NONNULL(2); + /* Similar to GC_move_disappearing_link but for a link */ + /* previously registered via GC_register_long_link. */ + +GC_API int GC_CALL GC_unregister_long_link(void ** /* link */); + /* Similar to GC_unregister_disappearing_link but for a */ + /* registration by either of the above two routines. */ + +/* Support of toggle-ref style of external memory management */ +/* without hooking up to the host retain/release machinery. */ +/* The idea of toggle-ref is that an external reference to */ +/* an object is kept and it can be either a strong or weak */ +/* reference; a weak reference is used when the external peer */ +/* has no interest in the object, and a strong otherwise. */ +typedef enum { + GC_TOGGLE_REF_DROP, + GC_TOGGLE_REF_STRONG, + GC_TOGGLE_REF_WEAK +} GC_ToggleRefStatus; + +/* The callback is to decide (return) the new state of a given */ +/* object. Invoked by the collector for all objects registered */ +/* for toggle-ref processing. Invoked with the allocator lock */ +/* held but the world is running. */ +typedef GC_ToggleRefStatus (GC_CALLBACK * GC_toggleref_func)(void * /* obj */); + +/* Set (register) a callback that decides the state of a given */ +/* object (by, probably, inspecting its native state). */ +/* The argument may be 0 (means no callback). Both the setter */ +/* and the getter acquire the allocator lock (in the reader */ +/* mode in case of the getter). */ +GC_API void GC_CALL GC_set_toggleref_func(GC_toggleref_func); +GC_API GC_toggleref_func GC_CALL GC_get_toggleref_func(void); + +/* Register a given object for toggle-ref processing. It will */ +/* be stored internally and the toggle-ref callback will be */ +/* invoked on the object until the callback returns */ +/* GC_TOGGLE_REF_DROP or the object is collected. If is_strong */ +/* is true, then the object is registered with a strong ref, */ +/* a weak one otherwise. Obj should be the starting address */ +/* of an object allocated by GC_malloc (GC_debug_malloc) or */ +/* friends. Returns GC_SUCCESS if registration succeeded (or */ +/* no callback is registered yet), GC_NO_MEMORY if it failed */ +/* for a lack of memory reason. */ +GC_API int GC_CALL GC_toggleref_add(void * /* obj */, int /* is_strong */) + GC_ATTR_NONNULL(1); +GC_API int GC_CALL GC_debug_toggleref_add(void * /* obj */, + int /* is_strong */) GC_ATTR_NONNULL(1); + +/* Finalizer callback support. Invoked by the collector (with */ +/* the allocator lock held) for each unreachable object */ +/* enqueued for finalization. */ +typedef void (GC_CALLBACK * GC_await_finalize_proc)(void * /* obj */); +GC_API void GC_CALL GC_set_await_finalize_proc(GC_await_finalize_proc); +GC_API GC_await_finalize_proc GC_CALL GC_get_await_finalize_proc(void); + /* Zero means no callback. The setter */ + /* and the getter acquire the allocator */ + /* lock too (in the reader mode in case */ + /* of the getter). */ + +/* Returns !=0 if GC_invoke_finalizers has something to do. */ +/* Does not use any synchronization. */ +GC_API int GC_CALL GC_should_invoke_finalizers(void); + +/* Set maximum amount of finalizers to run during a single */ +/* invocation of GC_invoke_finalizers. Zero means no limit. */ +/* Both the setter and the getter acquire the allocator lock */ +/* (in the reader mode in case of the getter). Note that */ +/* invocation of GC_finalize_all resets the maximum amount */ +/* value. */ +GC_API void GC_CALL GC_set_interrupt_finalizers(unsigned); +GC_API unsigned GC_CALL GC_get_interrupt_finalizers(void); + +GC_API int GC_CALL GC_invoke_finalizers(void); + /* Run finalizers for all objects that are ready to */ + /* be finalized. Return the number of finalizers */ + /* that were run. Normally this is also called */ + /* implicitly during some allocations. If */ + /* GC_finalize_on_demand is nonzero, it must be called */ + /* explicitly. */ + +/* Explicitly tell the collector that an object is reachable */ +/* at a particular program point. This prevents the argument */ +/* pointer from being optimized away, even it is otherwise no */ +/* longer needed. It should have no visible effect in the */ +/* absence of finalizers or disappearing links. But it may be */ +/* needed to prevent finalizers from running while the */ +/* associated external resource is still in use. */ +/* The function is sometimes called keep_alive in other */ +/* settings. */ +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) +# if defined(__e2k__) +# define GC_reachable_here(ptr) \ + __asm__ __volatile__ (" " : : "r"(ptr) : "memory") +# else +# define GC_reachable_here(ptr) \ + __asm__ __volatile__ (" " : : "X"(ptr) : "memory") +# endif +#elif defined(LINT2) +# define GC_reachable_here(ptr) GC_noop1(~(GC_word)(ptr)^(~(GC_word)0)) + /* The expression matches the one of COVERT_DATAFLOW(). */ +#else +# define GC_reachable_here(ptr) GC_noop1((GC_word)(ptr)) +#endif + +/* Make the argument appear live to compiler. Should be robust against */ +/* the whole program analysis. */ +GC_API void GC_CALL GC_noop1(GC_word); + +/* GC_set_warn_proc can be used to redirect or filter warning messages. */ +/* p may not be a NULL pointer. msg is printf format string (arg must */ +/* match the format). Both the setter and the getter acquire the */ +/* allocator lock (in the reader mode in case of the getter) to avoid */ +/* data race. In GC v7.1 and before, the setter returned the old */ +/* warn_proc value. */ +typedef void (GC_CALLBACK * GC_warn_proc)(char * /* msg */, + GC_word /* arg */); +GC_API void GC_CALL GC_set_warn_proc(GC_warn_proc /* p */) GC_ATTR_NONNULL(1); +/* GC_get_warn_proc returns the current warn_proc. */ +GC_API GC_warn_proc GC_CALL GC_get_warn_proc(void); + +/* GC_ignore_warn_proc may be used as an argument for GC_set_warn_proc */ +/* to suppress all warnings (unless statistics printing is turned on). */ +GC_API void GC_CALLBACK GC_ignore_warn_proc(char *, GC_word); + +/* Change file descriptor of GC log. Unavailable on some targets. */ +GC_API void GC_CALL GC_set_log_fd(int); + +/* abort_func is invoked on GC fatal aborts (just before OS-dependent */ +/* abort or exit(1) is called). Must be non-NULL. The default one */ +/* outputs msg to stderr provided msg is non-NULL. msg is NULL if */ +/* invoked before exit(1) otherwise msg is non-NULL (i.e., if invoked */ +/* before abort). Both the setter and the getter acquire the allocator */ +/* lock (in the reader mode in case of the getter). The setter does */ +/* not change abort_func if the library has been compiled with */ +/* SMALL_CONFIG defined. */ +typedef void (GC_CALLBACK * GC_abort_func)(const char * /* msg */); +GC_API void GC_CALL GC_set_abort_func(GC_abort_func) GC_ATTR_NONNULL(1); +GC_API GC_abort_func GC_CALL GC_get_abort_func(void); + +/* A portable way to abort the application because of not enough */ +/* memory. */ +GC_API void GC_CALL GC_abort_on_oom(void); + +/* The following is intended to be used by a higher level (e.g. */ +/* Java-like) finalization facility. It is expected that finalization */ +/* code will arrange for hidden pointers to disappear. Otherwise, */ +/* objects can be accessed after they have been collected. Should not */ +/* be used in the leak-finding mode. */ +/* Note that putting pointers in atomic objects or in non-pointer slots */ +/* of "typed" objects is equivalent to disguising them in this way, and */ +/* may have other advantages. Note also that some code relies on that */ +/* the least significant bit of the argument (including for NULL) is */ +/* inverted by these primitives. */ +/* Important: converting a hidden pointer to a real pointer requires */ +/* verifying that the object still exists; this should involve */ +/* acquiring the allocator lock, at least in the reader mode, to avoid */ +/* a race with the collector (e.g., one thread might fetch hidden link */ +/* value, while another thread might collect the relevant object and */ +/* reuse the free space for another object). */ +typedef GC_word GC_hidden_pointer; +#define GC_HIDE_POINTER(p) (~(GC_hidden_pointer)(p)) +#define GC_REVEAL_POINTER(p) ((void *)GC_HIDE_POINTER(p)) + +#if defined(I_HIDE_POINTERS) || defined(GC_I_HIDE_POINTERS) + /* This exists only for compatibility (the GC-prefixed symbols are */ + /* preferred for new code). */ +# define HIDE_POINTER(p) GC_HIDE_POINTER(p) +# define REVEAL_POINTER(p) GC_REVEAL_POINTER(p) +#endif + +/* A slightly modified version of GC_HIDE_POINTER which guarantees not */ +/* to "hide" NULL (i.e. passing zero argument gives zero result). This */ +/* might be useful in conjunction with GC_register_disappearing_link. */ +/* Note that unlike GC_HIDE_POINTER, inversion of the least significant */ +/* bit of the argument is not guaranteed. */ +#define GC_HIDE_NZ_POINTER(p) ((GC_hidden_pointer)(-(GC_signed_word)(p))) +#define GC_REVEAL_NZ_POINTER(p) ((void *)GC_HIDE_NZ_POINTER(p)) + +/* The routines to acquire/release the GC (allocator) lock. */ +/* The lock is not reentrant. GC_alloc_unlock() should not be called */ +/* unless the allocator lock is acquired by the current thread. */ +#ifdef GC_THREADS + GC_API void GC_CALL GC_alloc_lock(void); + GC_API void GC_CALL GC_alloc_unlock(void); +#else + /* No need for real locking if the client is single-threaded. */ +# define GC_alloc_lock() (void)0 +# define GC_alloc_unlock() (void)0 +#endif /* !GC_THREADS */ + +typedef void * (GC_CALLBACK * GC_fn_type)(void * /* client_data */); + +/* Execute given function with the allocator lock held (in the */ +/* exclusive mode). */ +GC_API void * GC_CALL GC_call_with_alloc_lock(GC_fn_type /* fn */, + void * /* client_data */) GC_ATTR_NONNULL(1); + +/* Execute given function with the allocator lock held in the reader */ +/* (shared) mode. The 3rd argument (release), if non-zero, indicates */ +/* that fn wrote some data that should be made visible to the thread */ +/* which acquires the allocator lock in the exclusive mode later. */ +#ifdef GC_THREADS + GC_API void * GC_CALL GC_call_with_reader_lock(GC_fn_type /* fn */, + void * /* client_data */, + int /* release */) GC_ATTR_NONNULL(1); +#else +# define GC_call_with_reader_lock(fn, cd, r) ((void)(r), (fn)(cd)) +#endif + +/* These routines are intended to explicitly notify the collector */ +/* of new threads. Often this is unnecessary because thread creation */ +/* is implicitly intercepted by the collector, using header-file */ +/* defines, or linker-based interception. In the long run the intent */ +/* is to always make redundant registration safe. In the short run, */ +/* this is being implemented a platform at a time. */ +/* The interface is complicated by the fact that we probably will not */ +/* ever be able to automatically determine the stack bottom for thread */ +/* stacks on all platforms. */ + +/* Structure representing the bottom (cold end) of a thread stack. */ +/* On most platforms this contains just a single address. */ +struct GC_stack_base { + void * mem_base; /* the bottom of the general-purpose stack */ +# if defined(__e2k__) \ + || defined(__ia64) || defined(__ia64__) || defined(_M_IA64) + void * reg_base; /* the bottom of the register stack */ +# endif +}; + +typedef void * (GC_CALLBACK * GC_stack_base_func)( + struct GC_stack_base * /* sb */, void * /* arg */); + +/* Call a function with a stack base structure corresponding to */ +/* somewhere in the GC_call_with_stack_base frame. This often can */ +/* be used to provide a sufficiently accurate stack bottom. And we */ +/* implement it everywhere. */ +GC_API void * GC_CALL GC_call_with_stack_base(GC_stack_base_func /* fn */, + void * /* arg */) GC_ATTR_NONNULL(1); + +#define GC_SUCCESS 0 +#define GC_DUPLICATE 1 /* Was already registered. */ +#define GC_NO_THREADS 2 /* No thread support in GC. */ + /* GC_NO_THREADS is not returned by any GC function anymore. */ +#define GC_UNIMPLEMENTED 3 /* Not yet implemented on this platform. */ +#define GC_NOT_FOUND 4 /* Requested link not found (returned */ + /* by GC_move_disappearing_link). */ + +/* Start the parallel marker threads, if available. Useful, e.g., */ +/* after POSIX fork in a child process (provided not followed by exec) */ +/* or in single-threaded clients (provided it is OK for the client to */ +/* perform marking in parallel). Acquires the allocator lock to avoid */ +/* a race. */ +GC_API void GC_CALL GC_start_mark_threads(void); + +#if defined(GC_DARWIN_THREADS) || defined(GC_WIN32_THREADS) + /* Use implicit thread registration and processing (via Win32 DllMain */ + /* or Darwin task_threads). Deprecated. Must be called before */ + /* GC_INIT() and other GC routines. Should be avoided if */ + /* GC_pthread_create, GC_beginthreadex (or GC_CreateThread), or */ + /* GC_register_my_thread could be called instead. */ + /* Includes a GC_init() call. Disables parallelized GC on Win32. */ + GC_API void GC_CALL GC_use_threads_discovery(void); +#endif + +#ifdef GC_THREADS + /* Suggest the GC to use the specific signal to suspend threads. */ + /* Has no effect after GC_init and on non-POSIX systems. */ + GC_API void GC_CALL GC_set_suspend_signal(int); + + /* Suggest the GC to use the specific signal to resume threads. */ + /* Has no effect after GC_init and on non-POSIX systems. */ + /* The same signal might be used for threads suspension and restart. */ + GC_API void GC_CALL GC_set_thr_restart_signal(int); + + /* Return the signal number (constant after initialization) used by */ + /* the GC to suspend threads on POSIX systems. Return -1 otherwise. */ + GC_API int GC_CALL GC_get_suspend_signal(void); + + /* Return the signal number (constant after initialization) used by */ + /* the garbage collector to restart (resume) threads on POSIX */ + /* systems. Return -1 otherwise. */ + GC_API int GC_CALL GC_get_thr_restart_signal(void); + + /* Explicitly enable GC_register_my_thread() invocation. */ + /* Done implicitly if a GC thread-creation function is called (or */ + /* implicit thread registration is activated, or the collector is */ + /* compiled with GC_ALWAYS_MULTITHREADED defined). Otherwise, it */ + /* must be called from the main (or any previously registered) thread */ + /* between the collector initialization and the first explicit */ + /* registering of a thread (it should be called as late as possible). */ + /* Includes a GC_start_mark_threads() call. */ + GC_API void GC_CALL GC_allow_register_threads(void); + + /* Register the current thread, with the indicated stack bottom, as */ + /* a new thread whose stack(s) should be traced by the GC. If it */ + /* is not implicitly called by the GC, this must be called before a */ + /* thread can allocate garbage collected memory, or assign pointers */ + /* to the garbage collected heap. Once registered, a thread will be */ + /* stopped during garbage collections. */ + /* This call must be previously enabled (see above). */ + /* This should never be called from the main thread, where it is */ + /* always done implicitly. This is normally done implicitly if GC_ */ + /* functions are called to create the thread, e.g. by including gc.h */ + /* (which redefines some system functions) before calling the system */ + /* thread creation function. Nonetheless, thread cleanup routines */ + /* (e.g., pthread key destructor) typically require manual thread */ + /* registering (and unregistering) if pointers to GC-allocated */ + /* objects are manipulated inside. */ + /* It is also always done implicitly on some platforms if */ + /* GC_use_threads_discovery() is called at start-up. Except for the */ + /* latter case, the explicit call is normally required for threads */ + /* created by third-party libraries. */ + /* A manually registered thread requires manual unregistering. */ + /* Returns GC_SUCCESS on success, GC_DUPLICATE if already registered. */ + GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *) + GC_ATTR_NONNULL(1); + + /* Return 1 (true) if the calling (current) thread is registered with */ + /* the garbage collector, 0 otherwise. Acquires the allocator lock */ + /* in the reader mode. If the thread is finished (e.g. running in */ + /* a destructor and not registered manually again), it is considered */ + /* as not registered. */ + GC_API int GC_CALL GC_thread_is_registered(void); + + /* Notify the collector about the stack and the alt-stack of the */ + /* current thread. normstack and normstack_size are used to */ + /* determine the "normal" stack boundaries when a thread is suspended */ + /* while it is on an alt-stack. Acquires the allocator lock in the */ + /* reader mode. */ + GC_API void GC_CALL GC_register_altstack(void * /* normstack */, + GC_word /* normstack_size */, + void * /* altstack */, + GC_word /* altstack_size */); + + /* Unregister the current thread. Only an explicitly registered */ + /* thread (i.e. for which GC_register_my_thread() returns GC_SUCCESS) */ + /* is allowed (and required) to call this function. (As a special */ + /* exception, it is also allowed to once unregister the main thread.) */ + /* The thread may no longer allocate garbage collected memory or */ + /* manipulate pointers to the garbage collected heap after making */ + /* this call. Specifically, if it wants to return or otherwise */ + /* communicate a pointer to the garbage-collected heap to another */ + /* thread, it must do this before calling GC_unregister_my_thread, */ + /* most probably by saving it in a global data structure. Must not */ + /* be called inside a GC callback function (except for */ + /* GC_call_with_stack_base() one). Always returns GC_SUCCESS. */ + GC_API int GC_CALL GC_unregister_my_thread(void); + + /* Stop/start the world explicitly. Not recommended for general use. */ + GC_API void GC_CALL GC_stop_world_external(void); + GC_API void GC_CALL GC_start_world_external(void); + + /* Provide a verifier/modifier of the stack pointer when pushing the */ + /* thread stacks. This might be useful for a crude integration */ + /* with certain coroutine implementations. (*sp_ptr) is the captured */ + /* stack pointer of the suspended thread with pthread_id (the latter */ + /* is actually of pthread_t type). The functionality is unsupported */ + /* on some targets (the getter always returns 0 in such a case). */ + /* Both the setter and the getter acquire the allocator lock (in the */ + /* reader mode in case of the getter). The client function (if */ + /* provided) is called with the allocator lock held and, might be, */ + /* with the world stopped. */ + typedef void (GC_CALLBACK * GC_sp_corrector_proc)(void ** /* sp_ptr */, + void * /* pthread_id */); + GC_API void GC_CALL GC_set_sp_corrector(GC_sp_corrector_proc); + GC_API GC_sp_corrector_proc GC_CALL GC_get_sp_corrector(void); +#endif /* GC_THREADS */ + +/* Wrapper for functions that are likely to block (or, at least, do not */ +/* allocate garbage collected memory and/or manipulate pointers to the */ +/* garbage collected heap) for an appreciable length of time. While fn */ +/* is running, the collector is said to be in the "inactive" state for */ +/* the current thread (this means that the thread is not suspended and */ +/* the thread's stack frames "belonging" to the functions in the */ +/* "inactive" state are not scanned during garbage collections). It is */ +/* assumed that the collector is already initialized and the current */ +/* thread is registered. It is allowed for fn to call */ +/* GC_call_with_gc_active() (even recursively), thus temporarily */ +/* toggling the collector's state back to "active". The latter */ +/* technique might be used to make stack scanning more precise (i.e. */ +/* scan only stack frames of functions that allocate garbage collected */ +/* memory and/or manipulate pointers to the garbage collected heap). */ +/* Acquires the allocator lock in the reader mode (but fn is called */ +/* not holding it). */ +GC_API void * GC_CALL GC_do_blocking(GC_fn_type /* fn */, + void * /* client_data */) GC_ATTR_NONNULL(1); + +/* Call a function switching to the "active" state of the collector for */ +/* the current thread (i.e. the user function is allowed to call any */ +/* GC function and/or manipulate pointers to the garbage collected */ +/* heap). GC_call_with_gc_active() has the functionality opposite to */ +/* GC_do_blocking() one. It is assumed that the collector is already */ +/* initialized and the current thread is registered. fn may toggle */ +/* the collector thread's state temporarily to "inactive" one by using */ +/* GC_do_blocking. GC_call_with_gc_active() often can be used to */ +/* provide a sufficiently accurate stack bottom. Acquires the */ +/* allocator lock in the reader mode (but fn is called not holding it). */ +GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type /* fn */, + void * /* client_data */) GC_ATTR_NONNULL(1); + +/* Attempt to fill in the GC_stack_base structure with the stack bottom */ +/* for this thread. This appears to be required to implement anything */ +/* like the JNI AttachCurrentThread in an environment in which new */ +/* threads are not automatically registered with the collector. */ +/* It is also unfortunately hard to implement well on many platforms. */ +/* Returns GC_SUCCESS or GC_UNIMPLEMENTED. Acquires the allocator lock */ +/* on some platforms. */ +GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *) + GC_ATTR_NONNULL(1); + +/* Fill in the GC_stack_base structure with the cold end (bottom) of */ +/* the stack of the current thread (or coroutine). */ +/* Unlike GC_get_stack_base, it retrieves the value stored in the */ +/* collector (which is initially set by the collector upon the thread */ +/* is started or registered manually but it could be later updated by */ +/* client using GC_set_stackbottom). Returns the GC-internal non-NULL */ +/* handle of the thread which could be passed to GC_set_stackbottom */ +/* later. It is assumed that the collector is already initialized and */ +/* the thread is registered. Acquires the allocator lock in the reader */ +/* mode. */ +GC_API void * GC_CALL GC_get_my_stackbottom(struct GC_stack_base *) + GC_ATTR_NONNULL(1); + +/* Set the cool end of the user (coroutine) stack of the specified */ +/* thread. The GC thread handle is either the one returned by */ +/* GC_get_my_stackbottom or NULL (the latter designates the current */ +/* thread). The caller should hold the allocator lock (e.g. using */ +/* GC_call_with_reader_lock with release argument set to 1), the reader */ +/* mode should be enough typically, at least for the collector itself */ +/* (the client is responsible to avoid data race between this and */ +/* GC_get_my_stackbottom functions if the client acquires the allocator */ +/* lock in the reader mode). Also, the function could be used for */ +/* setting GC_stackbottom value (the bottom of the primordial thread) */ +/* before the collector is initialized (the allocator lock is not */ +/* needed to be acquired at all in this case). */ +GC_API void GC_CALL GC_set_stackbottom(void * /* gc_thread_handle */, + const struct GC_stack_base *) + GC_ATTR_NONNULL(2); + +/* The following routines are primarily intended for use with a */ +/* preprocessor which inserts calls to check C pointer arithmetic. */ +/* They indicate failure by invoking the corresponding _print_proc. */ + +/* Checked pointer pre- and post- increment operations. Note that */ +/* the second argument is in units of bytes, not multiples of the */ +/* object size. This should either be invoked from a macro, or the */ +/* call should be automatically generated. */ +GC_API void * GC_CALL GC_pre_incr(void **, ptrdiff_t /* how_much */) + GC_ATTR_NONNULL(1); +GC_API void * GC_CALL GC_post_incr(void **, ptrdiff_t /* how_much */) + GC_ATTR_NONNULL(1); + +/* Check that p and q point to the same object. GC_same_obj_print_proc */ +/* is called (fail by default) if they do not. Succeeds, as well, if */ +/* neither p nor q points to the heap. (May succeed also if both p and */ +/* q point to between heap objects.) Returns the first argument. */ +/* (The returned value may be hard to use due to typing issues. But if */ +/* we had a suitable preprocessor...) We assume this is somewhat */ +/* performance critical (it should not be called by production code, */ +/* of course, but it can easily make even debugging intolerably slow). */ +GC_API void * GC_CALL GC_same_obj(void * /* p */, void * /* q */); + +/* Check that p is visible to the collector as a possibly pointer */ +/* containing location. If it is not, invoke GC_is_visible_print_proc */ +/* (fail by default). Always returns the argument. May erroneously */ +/* succeed in hard cases. The function is intended for debugging use */ +/* with untyped allocations. (The idea is that it should be possible, */ +/* though slow, to add such a call to all indirect pointer stores.) */ +/* Currently useless for the multi-threaded worlds. */ +GC_API void * GC_CALL GC_is_visible(void * /* p */); + +/* Check that if p is a pointer to a heap page, then it points to */ +/* a valid displacement within a heap object. If it is not, invoke */ +/* GC_is_valid_displacement_print_proc (fail by default). Always */ +/* returns the argument. Uninteresting with all-interior-pointers on. */ +/* Note that we do not acquire the allocator lock, since nothing */ +/* relevant about the header should change while we have a valid object */ +/* pointer to the block. */ +GC_API void * GC_CALL GC_is_valid_displacement(void * /* p */); + +/* Explicitly dump the GC state. This is most often called from the */ +/* debugger, or by setting the GC_DUMP_REGULARLY environment variable, */ +/* but it may be useful to call it from client code during debugging. */ +/* The current collection number is printed in the header of the dump. */ +/* Acquires the allocator lock in the reader mode to avoid data race. */ +/* Defined only if the library has been compiled without NO_DEBUGGING. */ +GC_API void GC_CALL GC_dump(void); + +/* The same as GC_dump but allows to specify the name of dump and does */ +/* not acquire the allocator lock. If name is non-NULL, it is printed */ +/* to help identifying individual dumps. Otherwise the current */ +/* collection number is used as the name. */ +/* Defined only if the library has been compiled without NO_DEBUGGING. */ +GC_API void GC_CALL GC_dump_named(const char * /* name */); + +/* Dump information about each block of every GC memory section. */ +/* Defined only if the library has been compiled without NO_DEBUGGING. */ +GC_API void GC_CALL GC_dump_regions(void); + +/* Dump information about every registered disappearing link and */ +/* finalizable object. */ +/* Defined only if the library has been compiled without NO_DEBUGGING. */ +GC_API void GC_CALL GC_dump_finalization(void); + +/* Safer, but slow, pointer addition. Probably useful mainly with */ +/* a preprocessor. Useful only for heap pointers. */ +/* Only the macros without trailing digits are meant to be used */ +/* by clients. These are designed to model the available C pointer */ +/* arithmetic expressions. */ +/* Even then, these are probably more useful as */ +/* documentation than as part of the API. */ +/* Note that GC_PTR_ADD evaluates the first argument more than once. */ +#if defined(GC_DEBUG) && (defined(__GNUC__) || defined(__clang__)) +# define GC_PTR_ADD3(x, n, type_of_result) \ + ((type_of_result)GC_same_obj((x)+(n), (x))) +# define GC_PRE_INCR3(x, n, type_of_result) \ + ((type_of_result)GC_pre_incr((void **)(&(x)), (n)*sizeof(*x))) +# define GC_POST_INCR3(x, n, type_of_result) \ + ((type_of_result)GC_post_incr((void **)(&(x)), (n)*sizeof(*x))) +# define GC_PTR_ADD(x, n) GC_PTR_ADD3(x, n, __typeof__(x)) +# define GC_PRE_INCR(x, n) GC_PRE_INCR3(x, n, __typeof__(x)) +# define GC_POST_INCR(x) GC_POST_INCR3(x, 1, __typeof__(x)) +# define GC_POST_DECR(x) GC_POST_INCR3(x, -1, __typeof__(x)) +#else /* !GC_DEBUG || !__GNUC__ */ + /* We can't do this right without typeof, which ANSI decided was not */ + /* sufficiently useful. Without it we resort to the non-debug version. */ + /* TODO: This should eventually support C++0x decltype. */ +# define GC_PTR_ADD(x, n) ((x)+(n)) +# define GC_PRE_INCR(x, n) ((x) += (n)) +# define GC_POST_INCR(x) ((x)++) +# define GC_POST_DECR(x) ((x)--) +#endif /* !GC_DEBUG || !__GNUC__ */ + +/* Safer assignment of a pointer to a non-stack location. */ +#ifdef GC_DEBUG +# define GC_PTR_STORE(p, q) \ + (*(void **)GC_is_visible((void *)(p)) = \ + GC_is_valid_displacement((void *)(q))) +#else +# define GC_PTR_STORE(p, q) (*(void **)(p) = (void *)(q)) +#endif + +/* GC_PTR_STORE_AND_DIRTY(p,q) is equivalent to GC_PTR_STORE(p,q) */ +/* followed by GC_END_STUBBORN_CHANGE(p) and GC_reachable_here(q) */ +/* (assuming p and q do not have side effects). */ +GC_API void GC_CALL GC_ptr_store_and_dirty(void * /* p */, + const void * /* q */); +GC_API void GC_CALL GC_debug_ptr_store_and_dirty(void * /* p */, + const void * /* q */); + +#ifdef GC_PTHREADS + /* For pthread support, we generally need to intercept a number of */ + /* thread library calls. We do that here by macro defining them. */ +# ifdef __cplusplus + } /* extern "C" */ +# endif +# include "gc_pthread_redirects.h" +# ifdef __cplusplus + extern "C" { +# endif +#endif + +/* This returns a list of objects, linked through their first word. */ +/* Its use can greatly reduce lock contention problems, since the */ +/* allocator lock can be acquired and released many fewer times. */ +GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_many(size_t /* lb */); +#define GC_NEXT(p) (*(void * *)(p)) /* Retrieve the next element */ + /* in returned list. */ + +/* A filter function to control the scanning of dynamic libraries. */ +/* If implemented, called by GC before registering a dynamic library */ +/* (discovered by GC) section as a static data root (called only as */ +/* a last reason not to register). The filename of the library, the */ +/* address and the length of the memory region (section) are passed. */ +/* This routine should return nonzero if that region should be scanned. */ +/* Always called with the allocator lock held. Depending on the */ +/* platform, might be called with the "world" stopped. */ +typedef int (GC_CALLBACK * GC_has_static_roots_func)( + const char * /* dlpi_name */, + void * /* section_start */, + size_t /* section_size */); + +/* Register a new callback (a user-supplied filter) to control the */ +/* scanning of dynamic libraries. Replaces any previously registered */ +/* callback. May be 0 (means no filtering). May be unused on some */ +/* platforms (if the filtering is unimplemented or inappropriate). */ +GC_API void GC_CALL GC_register_has_static_roots_callback( + GC_has_static_roots_func); + +#if !defined(CPPCHECK) && !defined(GC_WINDOWS_H_INCLUDED) && defined(WINAPI) + /* windows.h is included before gc.h */ +# define GC_WINDOWS_H_INCLUDED +#endif + +#if defined(GC_WIN32_THREADS) \ + && (!defined(GC_PTHREADS) || defined(GC_BUILD) \ + || defined(GC_WINDOWS_H_INCLUDED)) + /* Note: for Cygwin and pthreads-win32, this is skipped */ + /* unless windows.h is included before gc.h. */ + +# if (!defined(GC_NO_THREAD_DECLS) || defined(GC_BUILD)) \ + && !defined(GC_DONT_INCL_WINDOWS_H) + +# ifdef __cplusplus + } /* Including windows.h in an extern "C" context no longer works. */ +# endif + +# if !defined(_WIN32_WCE) && !defined(__CEGCC__) +# include /* For _beginthreadex, _endthreadex */ +# endif + +# if defined(GC_BUILD) || !defined(GC_DONT_INCLUDE_WINDOWS_H) +# include +# define GC_WINDOWS_H_INCLUDED +# endif + +# ifdef __cplusplus + extern "C" { +# endif + +# ifdef GC_UNDERSCORE_STDCALL + /* Explicitly prefix exported/imported WINAPI (__stdcall) symbols */ + /* with '_' (underscore). Might be useful if MinGW/x86 is used. */ +# define GC_CreateThread _GC_CreateThread +# define GC_ExitThread _GC_ExitThread +# endif + +# ifndef DECLSPEC_NORETURN + /* Typically defined in winnt.h. */ +# ifdef GC_WINDOWS_H_INCLUDED +# define DECLSPEC_NORETURN /* empty */ +# else +# define DECLSPEC_NORETURN __declspec(noreturn) +# endif +# endif + +# if !defined(_UINTPTR_T) && !defined(_UINTPTR_T_DEFINED) \ + && !defined(UINTPTR_MAX) + typedef GC_word GC_uintptr_t; +# else + typedef uintptr_t GC_uintptr_t; +# endif + +# ifdef _WIN64 +# define GC_WIN32_SIZE_T GC_uintptr_t +# elif defined(GC_WINDOWS_H_INCLUDED) +# define GC_WIN32_SIZE_T DWORD +# else +# define GC_WIN32_SIZE_T unsigned long +# endif + +# ifdef GC_INSIDE_DLL + /* Export GC DllMain to be invoked from client DllMain. */ +# ifdef GC_UNDERSCORE_STDCALL +# define GC_DllMain _GC_DllMain +# endif +# ifdef GC_WINDOWS_H_INCLUDED + GC_API BOOL WINAPI GC_DllMain(HINSTANCE /* inst */, + ULONG /* reason */, + LPVOID /* reserved */); +# else + GC_API int __stdcall GC_DllMain(void *, unsigned long, void *); +# endif +# endif /* GC_INSIDE_DLL */ + + /* All threads must be created using GC_CreateThread or */ + /* GC_beginthreadex, or must explicitly call GC_register_my_thread */ + /* (and call GC_unregister_my_thread before thread termination), so */ + /* that they will be recorded in the thread table. For backward */ + /* compatibility, it is possible to build the GC with GC_DLL */ + /* defined, and to call GC_use_threads_discovery. This implicitly */ + /* registers all created threads, but appears to be less robust. */ + /* Currently the collector expects all threads to fall through and */ + /* terminate normally, or call GC_endthreadex() or GC_ExitThread, */ + /* so that the thread is properly unregistered. */ +# ifdef GC_WINDOWS_H_INCLUDED + GC_API HANDLE WINAPI GC_CreateThread( + LPSECURITY_ATTRIBUTES /* lpThreadAttributes */, + GC_WIN32_SIZE_T /* dwStackSize */, + LPTHREAD_START_ROUTINE /* lpStartAddress */, + LPVOID /* lpParameter */, DWORD /* dwCreationFlags */, + LPDWORD /* lpThreadId */); + + GC_API DECLSPEC_NORETURN void WINAPI GC_ExitThread( + DWORD /* dwExitCode */); +# else + struct _SECURITY_ATTRIBUTES; + GC_API void *__stdcall GC_CreateThread(struct _SECURITY_ATTRIBUTES *, + GC_WIN32_SIZE_T, + unsigned long (__stdcall *)(void *), + void *, unsigned long, unsigned long *); + GC_API DECLSPEC_NORETURN void __stdcall GC_ExitThread(unsigned long); +# endif + +# if !defined(_WIN32_WCE) && !defined(__CEGCC__) + GC_API GC_uintptr_t GC_CALL GC_beginthreadex( + void * /* security */, unsigned /* stack_size */, + unsigned (__stdcall *)(void *), + void * /* arglist */, unsigned /* initflag */, + unsigned * /* thrdaddr */); + + /* Note: _endthreadex() is not currently marked as no-return in */ + /* VC++ and MinGW headers, so we don't mark it neither. */ + GC_API void GC_CALL GC_endthreadex(unsigned /* retval */); +# endif /* !_WIN32_WCE */ + +# endif /* !GC_NO_THREAD_DECLS */ + +# ifdef GC_WINMAIN_REDIRECT + /* The collector provides the real WinMain(), which starts a new */ + /* thread to call GC_WinMain() after initializing the GC. */ +# define WinMain GC_WinMain +# endif + + /* For compatibility only. */ +# define GC_use_DllMain GC_use_threads_discovery + +# ifndef GC_NO_THREAD_REDIRECTS +# define CreateThread GC_CreateThread +# define ExitThread GC_ExitThread +# undef _beginthreadex +# define _beginthreadex GC_beginthreadex +# undef _endthreadex +# define _endthreadex GC_endthreadex +/* #define _beginthread { > "Please use _beginthreadex instead of _beginthread" < } */ +# endif /* !GC_NO_THREAD_REDIRECTS */ + +#endif /* GC_WIN32_THREADS */ + +/* The setter and the getter for switching "unmap as much as possible" */ +/* mode on(1) and off(0). Has no effect unless unmapping is turned on. */ +/* Has no effect on implicitly-initiated garbage collections. Initial */ +/* value is controlled by GC_FORCE_UNMAP_ON_GCOLLECT. The setter and */ +/* the getter are unsynchronized. */ +GC_API void GC_CALL GC_set_force_unmap_on_gcollect(int); +GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void); + +/* Fully portable code should call GC_INIT() from the main program */ +/* before making any other GC_ calls. On most platforms this is a */ +/* no-op and the collector self-initializes. But a number of */ +/* platforms make that too hard. */ +/* A GC_INIT call is required if the collector is built with */ +/* THREAD_LOCAL_ALLOC defined and the initial allocation call is not */ +/* to GC_malloc() or GC_malloc_atomic(). */ + +#if defined(__CYGWIN32__) || defined(__CYGWIN__) + /* Similarly gnu-win32 DLLs need explicit initialization from the */ + /* main program, as does AIX. */ +# ifdef __x86_64__ + /* Cygwin/x64 does not add leading underscore to symbols anymore. */ + extern int __data_start__[], __data_end__[]; + extern int __bss_start__[], __bss_end__[]; +# define GC_DATASTART ((GC_word)__data_start__ < (GC_word)__bss_start__ \ + ? (void *)__data_start__ : (void *)__bss_start__) +# define GC_DATAEND ((GC_word)__data_end__ > (GC_word)__bss_end__ \ + ? (void *)__data_end__ : (void *)__bss_end__) +# else + extern int _data_start__[], _data_end__[], _bss_start__[], _bss_end__[]; +# define GC_DATASTART ((GC_word)_data_start__ < (GC_word)_bss_start__ \ + ? (void *)_data_start__ : (void *)_bss_start__) +# define GC_DATAEND ((GC_word)_data_end__ > (GC_word)_bss_end__ \ + ? (void *)_data_end__ : (void *)_bss_end__) +# endif /* !__x86_64__ */ +# define GC_INIT_CONF_ROOTS GC_add_roots(GC_DATASTART, GC_DATAEND); \ + GC_gcollect() /* For blacklisting. */ + /* Required at least if GC is in a DLL. And doesn't hurt. */ +#elif defined(_AIX) + extern int _data[], _end[]; +# define GC_DATASTART ((void *)_data) +# define GC_DATAEND ((void *)_end) +# define GC_INIT_CONF_ROOTS GC_add_roots(GC_DATASTART, GC_DATAEND) +#elif (defined(HOST_ANDROID) || defined(__ANDROID__)) \ + && defined(IGNORE_DYNAMIC_LOADING) + /* This is ugly but seems the only way to register data roots of the */ + /* client shared library if the GC dynamic loading support is off. */ +# pragma weak __dso_handle + extern int __dso_handle[]; + GC_API void * GC_CALL GC_find_limit(void * /* start */, int /* up */); +# define GC_INIT_CONF_ROOTS (void)(__dso_handle != 0 \ + ? (GC_add_roots(__dso_handle, \ + GC_find_limit(__dso_handle, \ + 1 /*up*/)), 0) : 0) +#else +# define GC_INIT_CONF_ROOTS /* empty */ +#endif + +#ifdef GC_DONT_EXPAND + /* Set GC_dont_expand to true at start-up. */ +# define GC_INIT_CONF_DONT_EXPAND GC_set_dont_expand(1) +#else +# define GC_INIT_CONF_DONT_EXPAND /* empty */ +#endif + +#ifdef GC_FORCE_UNMAP_ON_GCOLLECT + /* Turn on "unmap as much as possible on explicit GC" mode at start-up */ +# define GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT \ + GC_set_force_unmap_on_gcollect(1) +#else +# define GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT /* empty */ +#endif + +#ifdef GC_DONT_GC + /* This is for debugging only (useful if environment variables are */ + /* unsupported); cannot call GC_disable as goes before GC_init. */ +# define GC_INIT_CONF_MAX_RETRIES (void)(GC_dont_gc = 1) +#elif defined(GC_MAX_RETRIES) && !defined(CPPCHECK) + /* Set GC_max_retries to the desired value at start-up */ +# define GC_INIT_CONF_MAX_RETRIES GC_set_max_retries(GC_MAX_RETRIES) +#else +# define GC_INIT_CONF_MAX_RETRIES /* empty */ +#endif + +#if defined(GC_ALLOCD_BYTES_PER_FINALIZER) && !defined(CPPCHECK) + /* Set GC_allocd_bytes_per_finalizer to the desired value at start-up. */ +# define GC_INIT_CONF_ALLOCD_BYTES_PER_FINALIZER \ + GC_set_allocd_bytes_per_finalizer(GC_ALLOCD_BYTES_PER_FINALIZER) +#else +# define GC_INIT_CONF_ALLOCD_BYTES_PER_FINALIZER /* empty */ +#endif + +#if defined(GC_FREE_SPACE_DIVISOR) && !defined(CPPCHECK) + /* Set GC_free_space_divisor to the desired value at start-up */ +# define GC_INIT_CONF_FREE_SPACE_DIVISOR \ + GC_set_free_space_divisor(GC_FREE_SPACE_DIVISOR) +#else +# define GC_INIT_CONF_FREE_SPACE_DIVISOR /* empty */ +#endif + +#if defined(GC_FULL_FREQ) && !defined(CPPCHECK) + /* Set GC_full_freq to the desired value at start-up */ +# define GC_INIT_CONF_FULL_FREQ GC_set_full_freq(GC_FULL_FREQ) +#else +# define GC_INIT_CONF_FULL_FREQ /* empty */ +#endif + +#if defined(GC_TIME_LIMIT) && !defined(CPPCHECK) + /* Set GC_time_limit (in ms) to the desired value at start-up. */ +# define GC_INIT_CONF_TIME_LIMIT GC_set_time_limit(GC_TIME_LIMIT) +#else +# define GC_INIT_CONF_TIME_LIMIT /* empty */ +#endif + +#if defined(GC_MARKERS) && defined(GC_THREADS) && !defined(CPPCHECK) + /* Set the number of marker threads (including the initiating */ + /* one) to the desired value at start-up. */ +# define GC_INIT_CONF_MARKERS GC_set_markers_count(GC_MARKERS) +#else +# define GC_INIT_CONF_MARKERS /* empty */ +#endif + +#if defined(GC_SIG_SUSPEND) && defined(GC_THREADS) && !defined(CPPCHECK) +# define GC_INIT_CONF_SUSPEND_SIGNAL GC_set_suspend_signal(GC_SIG_SUSPEND) +#else +# define GC_INIT_CONF_SUSPEND_SIGNAL /* empty */ +#endif + +#if defined(GC_SIG_THR_RESTART) && defined(GC_THREADS) && !defined(CPPCHECK) +# define GC_INIT_CONF_THR_RESTART_SIGNAL \ + GC_set_thr_restart_signal(GC_SIG_THR_RESTART) +#else +# define GC_INIT_CONF_THR_RESTART_SIGNAL /* empty */ +#endif + +#if defined(GC_MAXIMUM_HEAP_SIZE) && !defined(CPPCHECK) + /* Limit the heap size to the desired value (useful for debugging). */ + /* The limit could be overridden either at the program start-up by */ + /* the similar environment variable or anytime later by the */ + /* corresponding API function call. */ +# define GC_INIT_CONF_MAXIMUM_HEAP_SIZE \ + GC_set_max_heap_size(GC_MAXIMUM_HEAP_SIZE) +#else +# define GC_INIT_CONF_MAXIMUM_HEAP_SIZE /* empty */ +#endif + +#ifdef GC_IGNORE_WARN + /* Turn off all warnings at start-up (after GC initialization) */ +# define GC_INIT_CONF_IGNORE_WARN GC_set_warn_proc(GC_ignore_warn_proc) +#else +# define GC_INIT_CONF_IGNORE_WARN /* empty */ +#endif + +#if defined(GC_INITIAL_HEAP_SIZE) && !defined(CPPCHECK) + /* Set heap size to the desired value at start-up */ +# define GC_INIT_CONF_INITIAL_HEAP_SIZE \ + { size_t heap_size = GC_get_heap_size(); \ + if (heap_size < (GC_INITIAL_HEAP_SIZE)) \ + (void)GC_expand_hp((GC_INITIAL_HEAP_SIZE) - heap_size); } +#else +# define GC_INIT_CONF_INITIAL_HEAP_SIZE /* empty */ +#endif + +/* Portable clients should call this at the program start-up. More */ +/* over, some platforms require this call to be done strictly from the */ +/* primordial thread. Multiple invocations are harmless. */ +#define GC_INIT() { GC_INIT_CONF_DONT_EXPAND; /* pre-init */ \ + GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT; \ + GC_INIT_CONF_MAX_RETRIES; \ + GC_INIT_CONF_ALLOCD_BYTES_PER_FINALIZER; \ + GC_INIT_CONF_FREE_SPACE_DIVISOR; \ + GC_INIT_CONF_FULL_FREQ; \ + GC_INIT_CONF_TIME_LIMIT; \ + GC_INIT_CONF_MARKERS; \ + GC_INIT_CONF_SUSPEND_SIGNAL; \ + GC_INIT_CONF_THR_RESTART_SIGNAL; \ + GC_INIT_CONF_MAXIMUM_HEAP_SIZE; \ + GC_init(); /* real GC initialization */ \ + GC_INIT_CONF_ROOTS; /* post-init */ \ + GC_INIT_CONF_IGNORE_WARN; \ + GC_INIT_CONF_INITIAL_HEAP_SIZE; } + +/* win32s may not free all resources on process exit. */ +/* This explicitly deallocates the heap. Defined only for Windows. */ +GC_API void GC_CALL GC_win32_free_heap(void); + +#if defined(__SYMBIAN32__) + void GC_init_global_static_roots(void); +#endif + +#if defined(_AMIGA) && !defined(GC_AMIGA_MAKINGLIB) + /* Allocation really goes through GC_amiga_allocwrapper_do. */ + void *GC_amiga_realloc(void *, size_t); +# define GC_realloc(a,b) GC_amiga_realloc(a,b) + void GC_amiga_set_toany(void (*)(void)); + extern int GC_amiga_free_space_divisor_inc; + extern void *(*GC_amiga_allocwrapper_do)(size_t, void *(GC_CALL *)(size_t)); +# define GC_malloc(a) \ + (*GC_amiga_allocwrapper_do)(a,GC_malloc) +# define GC_malloc_atomic(a) \ + (*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic) +# define GC_malloc_uncollectable(a) \ + (*GC_amiga_allocwrapper_do)(a,GC_malloc_uncollectable) +# define GC_malloc_atomic_uncollectable(a) \ + (*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_uncollectable) +# define GC_malloc_ignore_off_page(a) \ + (*GC_amiga_allocwrapper_do)(a,GC_malloc_ignore_off_page) +# define GC_malloc_atomic_ignore_off_page(a) \ + (*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_ignore_off_page) +#endif /* _AMIGA && !GC_AMIGA_MAKINGLIB */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_H */ diff --git a/thirdparty/libgc/include/gc/gc_allocator.h b/thirdparty/libgc/include/gc/gc_allocator.h new file mode 100644 index 0000000..250ead1 --- /dev/null +++ b/thirdparty/libgc/include/gc/gc_allocator.h @@ -0,0 +1,375 @@ +/* + * Copyright (c) 1996-1997 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * Copyright (c) 2002 + * Hewlett-Packard Company + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Hewlett-Packard Company makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + */ + +/* + * This implements standard-conforming allocators that interact with + * the garbage collector. Gc_allocator allocates garbage-collectible + * objects of type T. Traceable_allocator allocates objects that + * are not themselves garbage collected, but are scanned by the + * collector for pointers to collectible objects. Traceable_alloc + * should be used for explicitly managed STL containers that may + * point to collectible objects. + * + * This code was derived from an earlier version of the GNU C++ standard + * library, which itself was derived from the SGI STL implementation. + * + * Ignore-off-page allocator: George T. Talbot + */ + +#ifndef GC_ALLOCATOR_H +#define GC_ALLOCATOR_H + +#include "gc.h" + +#include // for placement new and bad_alloc + +#ifdef GC_NAMESPACE_ALLOCATOR +namespace boehmgc +{ +#endif + +#if !defined(GC_NO_MEMBER_TEMPLATES) && defined(_MSC_VER) && _MSC_VER <= 1200 + // MSVC++ 6.0 do not support member templates. +# define GC_NO_MEMBER_TEMPLATES +#endif + +#if defined(GC_NEW_ABORTS_ON_OOM) || defined(_LIBCPP_NO_EXCEPTIONS) +# define GC_ALLOCATOR_THROW_OR_ABORT() GC_abort_on_oom() +#else +# define GC_ALLOCATOR_THROW_OR_ABORT() throw std::bad_alloc() +#endif + +#if __cplusplus >= 201103L +# define GC_ALCTR_PTRDIFF_T std::ptrdiff_t +# define GC_ALCTR_SIZE_T std::size_t +#else +# define GC_ALCTR_PTRDIFF_T ptrdiff_t +# define GC_ALCTR_SIZE_T size_t +#endif + +// First some helpers to allow us to dispatch on whether or not a type +// is known to be pointer-free. These are private, except that the client +// may invoke the GC_DECLARE_PTRFREE macro. + +struct GC_true_type {}; +struct GC_false_type {}; + +template +struct GC_type_traits { + GC_false_type GC_is_ptr_free; +}; + +#define GC_DECLARE_PTRFREE(T) \ + template<> struct GC_type_traits { GC_true_type GC_is_ptr_free; } + +GC_DECLARE_PTRFREE(char); +GC_DECLARE_PTRFREE(signed char); +GC_DECLARE_PTRFREE(unsigned char); +GC_DECLARE_PTRFREE(signed short); +GC_DECLARE_PTRFREE(unsigned short); +GC_DECLARE_PTRFREE(signed int); +GC_DECLARE_PTRFREE(unsigned int); +GC_DECLARE_PTRFREE(signed long); +GC_DECLARE_PTRFREE(unsigned long); +GC_DECLARE_PTRFREE(float); +GC_DECLARE_PTRFREE(double); +GC_DECLARE_PTRFREE(long double); +// The client may want to add others. + +// In the following GC_Tp is GC_true_type if we are allocating a pointer-free +// object. +template +inline void * GC_selective_alloc(GC_ALCTR_SIZE_T n, GC_Tp, + bool ignore_off_page) { + void *obj = ignore_off_page ? GC_MALLOC_IGNORE_OFF_PAGE(n) : GC_MALLOC(n); + if (0 == obj) + GC_ALLOCATOR_THROW_OR_ABORT(); + return obj; +} + +#if !defined(__WATCOMC__) + // Note: template-id not supported in this context by Watcom compiler. + template <> + inline void * GC_selective_alloc(GC_ALCTR_SIZE_T n, + GC_true_type, + bool ignore_off_page) { + void *obj = ignore_off_page ? GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(n) + : GC_MALLOC_ATOMIC(n); + if (0 == obj) + GC_ALLOCATOR_THROW_OR_ABORT(); + return obj; + } +#endif + +// Now the public gc_allocator class. +template +class gc_allocator { +public: + typedef GC_ALCTR_SIZE_T size_type; + typedef GC_ALCTR_PTRDIFF_T difference_type; + typedef GC_Tp* pointer; + typedef const GC_Tp* const_pointer; + typedef GC_Tp& reference; + typedef const GC_Tp& const_reference; + typedef GC_Tp value_type; + + template struct rebind { + typedef gc_allocator other; + }; + + GC_CONSTEXPR gc_allocator() GC_NOEXCEPT {} + GC_CONSTEXPR gc_allocator(const gc_allocator&) GC_NOEXCEPT {} +# ifndef GC_NO_MEMBER_TEMPLATES + template GC_ATTR_EXPLICIT + GC_CONSTEXPR gc_allocator(const gc_allocator&) GC_NOEXCEPT {} +# endif + GC_CONSTEXPR ~gc_allocator() GC_NOEXCEPT {} + + GC_CONSTEXPR pointer address(reference GC_x) const { return &GC_x; } + GC_CONSTEXPR const_pointer address(const_reference GC_x) const { + return &GC_x; + } + + // GC_n is permitted to be 0. The C++ standard says nothing about what + // the return value is when GC_n == 0. + GC_CONSTEXPR GC_Tp* allocate(size_type GC_n, const void* = 0) { + GC_type_traits traits; + return static_cast(GC_selective_alloc(GC_n * sizeof(GC_Tp), + traits.GC_is_ptr_free, false)); + } + + GC_CONSTEXPR void deallocate(pointer __p, size_type /* GC_n */) GC_NOEXCEPT { + GC_FREE(__p); + } + + GC_CONSTEXPR size_type max_size() const GC_NOEXCEPT { + return static_cast(-1) / sizeof(GC_Tp); + } + + GC_CONSTEXPR void construct(pointer __p, const GC_Tp& __val) { + new(__p) GC_Tp(__val); + } + + GC_CONSTEXPR void destroy(pointer __p) { __p->~GC_Tp(); } +}; + +template<> +class gc_allocator { +public: + typedef GC_ALCTR_SIZE_T size_type; + typedef GC_ALCTR_PTRDIFF_T difference_type; + typedef void* pointer; + typedef const void* const_pointer; + typedef void value_type; + + template struct rebind { + typedef gc_allocator other; + }; +}; + +template +GC_CONSTEXPR inline bool operator==(const gc_allocator&, + const gc_allocator&) GC_NOEXCEPT { + return true; +} + +template +GC_CONSTEXPR inline bool operator!=(const gc_allocator&, + const gc_allocator&) GC_NOEXCEPT { + return false; +} + +// Now the public gc_allocator_ignore_off_page class. +template +class gc_allocator_ignore_off_page { +public: + typedef GC_ALCTR_SIZE_T size_type; + typedef GC_ALCTR_PTRDIFF_T difference_type; + typedef GC_Tp* pointer; + typedef const GC_Tp* const_pointer; + typedef GC_Tp& reference; + typedef const GC_Tp& const_reference; + typedef GC_Tp value_type; + + template struct rebind { + typedef gc_allocator_ignore_off_page other; + }; + + GC_CONSTEXPR gc_allocator_ignore_off_page() GC_NOEXCEPT {} + GC_CONSTEXPR gc_allocator_ignore_off_page( + const gc_allocator_ignore_off_page&) GC_NOEXCEPT {} +# ifndef GC_NO_MEMBER_TEMPLATES + template GC_ATTR_EXPLICIT + GC_CONSTEXPR gc_allocator_ignore_off_page( + const gc_allocator_ignore_off_page&) GC_NOEXCEPT {} +# endif + GC_CONSTEXPR ~gc_allocator_ignore_off_page() GC_NOEXCEPT {} + + GC_CONSTEXPR pointer address(reference GC_x) const { return &GC_x; } + GC_CONSTEXPR const_pointer address(const_reference GC_x) const { + return &GC_x; + } + + // GC_n is permitted to be 0. The C++ standard says nothing about what + // the return value is when GC_n == 0. + GC_CONSTEXPR GC_Tp* allocate(size_type GC_n, const void* = 0) { + GC_type_traits traits; + return static_cast(GC_selective_alloc(GC_n * sizeof(GC_Tp), + traits.GC_is_ptr_free, true)); + } + + GC_CONSTEXPR void deallocate(pointer __p, size_type /* GC_n */) GC_NOEXCEPT { + GC_FREE(__p); + } + + GC_CONSTEXPR size_type max_size() const GC_NOEXCEPT { + return static_cast(-1) / sizeof(GC_Tp); + } + + GC_CONSTEXPR void construct(pointer __p, const GC_Tp& __val) { + new(__p) GC_Tp(__val); + } + + GC_CONSTEXPR void destroy(pointer __p) { __p->~GC_Tp(); } +}; + +template<> +class gc_allocator_ignore_off_page { +public: + typedef GC_ALCTR_SIZE_T size_type; + typedef GC_ALCTR_PTRDIFF_T difference_type; + typedef void* pointer; + typedef const void* const_pointer; + typedef void value_type; + + template struct rebind { + typedef gc_allocator_ignore_off_page other; + }; +}; + +template +GC_CONSTEXPR inline bool operator==(const gc_allocator_ignore_off_page&, + const gc_allocator_ignore_off_page&) GC_NOEXCEPT { + return true; +} + +template +GC_CONSTEXPR inline bool operator!=(const gc_allocator_ignore_off_page&, + const gc_allocator_ignore_off_page&) GC_NOEXCEPT { + return false; +} + +// And the public traceable_allocator class. + +// Note that we currently do not specialize the pointer-free case, +// since a pointer-free traceable container does not make that much sense, +// though it could become an issue due to abstraction boundaries. + +template +class traceable_allocator { +public: + typedef GC_ALCTR_SIZE_T size_type; + typedef GC_ALCTR_PTRDIFF_T difference_type; + typedef GC_Tp* pointer; + typedef const GC_Tp* const_pointer; + typedef GC_Tp& reference; + typedef const GC_Tp& const_reference; + typedef GC_Tp value_type; + + template struct rebind { + typedef traceable_allocator other; + }; + + GC_CONSTEXPR traceable_allocator() GC_NOEXCEPT {} + GC_CONSTEXPR traceable_allocator(const traceable_allocator&) GC_NOEXCEPT {} +# ifndef GC_NO_MEMBER_TEMPLATES + template GC_ATTR_EXPLICIT + GC_CONSTEXPR traceable_allocator( + const traceable_allocator&) GC_NOEXCEPT {} +# endif + GC_CONSTEXPR ~traceable_allocator() GC_NOEXCEPT {} + + GC_CONSTEXPR pointer address(reference GC_x) const { return &GC_x; } + GC_CONSTEXPR const_pointer address(const_reference GC_x) const { + return &GC_x; + } + + // GC_n is permitted to be 0. The C++ standard says nothing about what + // the return value is when GC_n == 0. + GC_CONSTEXPR GC_Tp* allocate(size_type GC_n, const void* = 0) { + void * obj = GC_MALLOC_UNCOLLECTABLE(GC_n * sizeof(GC_Tp)); + if (0 == obj) + GC_ALLOCATOR_THROW_OR_ABORT(); + return static_cast(obj); + } + + GC_CONSTEXPR void deallocate(pointer __p, size_type /* GC_n */) GC_NOEXCEPT { + GC_FREE(__p); + } + + GC_CONSTEXPR size_type max_size() const GC_NOEXCEPT { + return static_cast(-1) / sizeof(GC_Tp); + } + + GC_CONSTEXPR void construct(pointer __p, const GC_Tp& __val) { + new(__p) GC_Tp(__val); + } + + GC_CONSTEXPR void destroy(pointer __p) { __p->~GC_Tp(); } +}; + +template<> +class traceable_allocator { +public: + typedef GC_ALCTR_SIZE_T size_type; + typedef GC_ALCTR_PTRDIFF_T difference_type; + typedef void* pointer; + typedef const void* const_pointer; + typedef void value_type; + + template struct rebind { + typedef traceable_allocator other; + }; +}; + +template +GC_CONSTEXPR inline bool operator==(const traceable_allocator&, + const traceable_allocator&) GC_NOEXCEPT { + return true; +} + +template +GC_CONSTEXPR inline bool operator!=(const traceable_allocator&, + const traceable_allocator&) GC_NOEXCEPT { + return false; +} + +#undef GC_ALCTR_PTRDIFF_T +#undef GC_ALCTR_SIZE_T + +#ifdef GC_NAMESPACE_ALLOCATOR +} +#endif + +#endif /* GC_ALLOCATOR_H */ diff --git a/thirdparty/libgc/include/gc/gc_backptr.h b/thirdparty/libgc/include/gc/gc_backptr.h new file mode 100644 index 0000000..204210f --- /dev/null +++ b/thirdparty/libgc/include/gc/gc_backptr.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* This is a simple API to implement pointer back tracing, i.e. */ +/* to answer questions such as "who is pointing to this" or */ +/* "why is this object being retained by the collector". */ +/* Most of these calls yield useful information on only after */ +/* a garbage collection. Usually the client will first force */ +/* a full collection and then gather information, preferably */ +/* before much intervening allocation. */ +/* The implementation of the interface is only about 99.9999% */ +/* correct. It is intended to be good enough for profiling, */ +/* but is not intended to be used with production code. */ +/* Results are likely to be much more useful if all allocation */ +/* is accomplished through the debugging allocators. */ + +/* + * The implementation idea is due to A. Demers. + */ + +#ifndef GC_BACKPTR_H +#define GC_BACKPTR_H + +#ifndef GC_H +# include "gc.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +typedef enum { + GC_UNREFERENCED, /* No reference info available. */ + GC_NO_SPACE, /* Dest not allocated with debug alloc. */ + GC_REFD_FROM_ROOT, /* Referenced directly by root *base_p. */ + GC_REFD_FROM_REG, /* Referenced from a register, i.e. */ + /* a root without an address. */ + GC_REFD_FROM_HEAP, /* Referenced from another heap obj. */ + GC_FINALIZER_REFD /* Finalizable and hence accessible. */ +} GC_ref_kind; + +/* Store information about the object referencing dest in *base_p */ +/* and *offset_p. */ +/* If multiple objects or roots point to dest, then the one */ +/* reported will be the last one used by the garbage collector to */ +/* trace the object. */ +/* If source is root, then *base_p = address and *offset_p = 0. */ +/* If source is heap object, then *base_p != 0, *offset_p = offset. */ +/* Dest can be any address within a heap object. */ +/* The allocator lock is not acquired by design (despite of the */ +/* possibility of a race); anyway the function should not be used */ +/* in production code. */ +GC_API GC_ref_kind GC_CALL GC_get_back_ptr_info(void * /* dest */, + void ** /* base_p */, size_t * /* offset_p */) + GC_ATTR_NONNULL(1); + +/* Generate a random heap address. The resulting address is */ +/* in the heap, but not necessarily inside a valid object. */ +/* The caller should hold the allocator lock. */ +GC_API void * GC_CALL GC_generate_random_heap_address(void); + +/* Generate a random address inside a valid marked heap object. */ +/* The caller should hold the allocator lock. */ +GC_API void * GC_CALL GC_generate_random_valid_address(void); + +/* Force a garbage collection and generate a backtrace from a */ +/* random heap address. */ +/* This uses the GC logging mechanism (GC_printf) to produce */ +/* output. It can often be called from a debugger. */ +GC_API void GC_CALL GC_generate_random_backtrace(void); + +/* Print a backtrace from a specific address. Used by the */ +/* above. The client should call GC_gcollect() immediately */ +/* before invocation. */ +GC_API void GC_CALL GC_print_backtrace(void *) GC_ATTR_NONNULL(1); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_BACKPTR_H */ diff --git a/thirdparty/libgc/include/gc/gc_config_macros.h b/thirdparty/libgc/include/gc/gc_config_macros.h new file mode 100644 index 0000000..3285d0c --- /dev/null +++ b/thirdparty/libgc/include/gc/gc_config_macros.h @@ -0,0 +1,487 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * Copyright (c) 2008-2020 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* This should never be included directly; it is included only from gc.h. */ +/* We separate it only to make gc.h more suitable as documentation. */ +#if defined(GC_H) + +/* Convenient internal macro to test version of gcc. */ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +# define GC_GNUC_PREREQ(major, minor) \ + ((__GNUC__ << 8) + __GNUC_MINOR__ >= ((major) << 8) + (minor)) +#else +# define GC_GNUC_PREREQ(major, minor) 0 /* FALSE */ +#endif + +/* A macro to define integer types of a pointer size. There seems to */ +/* be no way to do this even semi-portably. The following is probably */ +/* no better/worse than almost anything else. */ +/* The ANSI standard suggests that size_t and ptrdiff_t might be */ +/* better choices. But those had incorrect definitions on some older */ +/* systems; notably "typedef int size_t" is wrong. */ +#ifdef _WIN64 +# if defined(__int64) && !defined(CPPCHECK) +# define GC_SIGNEDWORD __int64 +# else +# define GC_SIGNEDWORD long long +# endif +#else +# define GC_SIGNEDWORD long +#endif +#define GC_UNSIGNEDWORD unsigned GC_SIGNEDWORD + +/* The return type of GC_get_version(). A 32-bit unsigned integer */ +/* or longer. */ +# define GC_VERSION_VAL_T unsigned + +/* Some tests for old macros. These violate our namespace rules and */ +/* will disappear shortly. Use the GC_ names. */ +#if defined(SOLARIS_THREADS) || defined(_SOLARIS_THREADS) \ + || defined(_SOLARIS_PTHREADS) || defined(GC_SOLARIS_PTHREADS) + /* We no longer support old style Solaris threads. */ + /* GC_SOLARIS_THREADS now means pthreads. */ +# ifndef GC_SOLARIS_THREADS +# define GC_SOLARIS_THREADS +# endif +#endif +#if defined(IRIX_THREADS) +# define GC_IRIX_THREADS +#endif +#if defined(DGUX_THREADS) && !defined(GC_DGUX386_THREADS) +# define GC_DGUX386_THREADS +#endif +#if defined(AIX_THREADS) +# define GC_AIX_THREADS +#endif +#if defined(HPUX_THREADS) +# define GC_HPUX_THREADS +#endif +#if defined(OSF1_THREADS) +# define GC_OSF1_THREADS +#endif +#if defined(LINUX_THREADS) +# define GC_LINUX_THREADS +#endif +#if defined(WIN32_THREADS) +# define GC_WIN32_THREADS +#endif +#if defined(RTEMS_THREADS) +# define GC_RTEMS_PTHREADS +#endif +#if defined(USE_LD_WRAP) +# define GC_USE_LD_WRAP +#endif + +#if defined(GC_WIN32_PTHREADS) && !defined(GC_WIN32_THREADS) + /* Using pthreads-win32 library (or other Win32 implementation). */ +# define GC_WIN32_THREADS +#endif + +#if defined(GC_AIX_THREADS) || defined(GC_DARWIN_THREADS) \ + || defined(GC_DGUX386_THREADS) || defined(GC_FREEBSD_THREADS) \ + || defined(GC_HPUX_THREADS) \ + || defined(GC_IRIX_THREADS) || defined(GC_LINUX_THREADS) \ + || defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_THREADS) \ + || defined(GC_OSF1_THREADS) || defined(GC_SOLARIS_THREADS) \ + || defined(GC_WIN32_THREADS) || defined(GC_RTEMS_PTHREADS) +# ifndef GC_THREADS +# define GC_THREADS +# endif +#elif defined(GC_THREADS) +# if defined(__linux__) +# define GC_LINUX_THREADS +# elif defined(__OpenBSD__) +# define GC_OPENBSD_THREADS +# elif defined(_PA_RISC1_1) || defined(_PA_RISC2_0) || defined(hppa) \ + || defined(__HPPA) || (defined(__ia64) && defined(_HPUX_SOURCE)) +# define GC_HPUX_THREADS +# elif defined(__HAIKU__) +# define GC_HAIKU_THREADS +# elif (defined(__DragonFly__) || defined(__FreeBSD_kernel__) \ + || defined(__FreeBSD__)) && !defined(GC_NO_FREEBSD) +# define GC_FREEBSD_THREADS +# elif defined(__NetBSD__) +# define GC_NETBSD_THREADS +# elif defined(__alpha) || defined(__alpha__) /* && !Linux && !xBSD */ +# define GC_OSF1_THREADS +# elif (defined(mips) || defined(__mips) || defined(_mips)) \ + && !(defined(nec_ews) || defined(_nec_ews) \ + || defined(ultrix) || defined(__ultrix)) +# define GC_IRIX_THREADS +# elif defined(__sparc) /* && !Linux */ \ + || ((defined(sun) || defined(__sun)) \ + && (defined(i386) || defined(__i386__) \ + || defined(__amd64) || defined(__amd64__))) +# define GC_SOLARIS_THREADS +# elif defined(__APPLE__) && defined(__MACH__) +# define GC_DARWIN_THREADS +# endif +# if defined(DGUX) && (defined(i386) || defined(__i386__)) +# define GC_DGUX386_THREADS +# endif +# if defined(_AIX) +# define GC_AIX_THREADS +# endif +# if (defined(_WIN32) || defined(_MSC_VER) || defined(__BORLANDC__) \ + || defined(__CYGWIN32__) || defined(__CYGWIN__) || defined(__CEGCC__) \ + || defined(_WIN32_WCE) || defined(__MINGW32__)) \ + && !defined(GC_WIN32_THREADS) + /* Either posix or native Win32 threads. */ +# define GC_WIN32_THREADS +# endif +# if defined(__rtems__) && (defined(i386) || defined(__i386__)) +# define GC_RTEMS_PTHREADS +# endif +#endif /* GC_THREADS */ + +#undef GC_PTHREADS +#if (!defined(GC_WIN32_THREADS) || defined(GC_WIN32_PTHREADS) \ + || defined(__CYGWIN32__) || defined(__CYGWIN__)) && defined(GC_THREADS) \ + && !defined(NN_PLATFORM_CTR) && !defined(NN_BUILD_TARGET_PLATFORM_NX) + /* Posix threads. */ +# define GC_PTHREADS +#endif + +#if !defined(_PTHREADS) && defined(GC_NETBSD_THREADS) +# define _PTHREADS +#endif + +#if defined(GC_DGUX386_THREADS) && !defined(_POSIX4A_DRAFT10_SOURCE) +# define _POSIX4A_DRAFT10_SOURCE 1 +#endif + +#if !defined(_REENTRANT) && defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) + /* Better late than never. This fails if system headers that depend */ + /* on this were previously included. */ +# define _REENTRANT 1 +#endif + +#define __GC +#if !defined(_WIN32_WCE) || defined(__GNUC__) +# include +# if defined(__MINGW32__) && !defined(_WIN32_WCE) +# include + /* We mention uintptr_t. */ + /* Perhaps this should be included in pure msft environments */ + /* as well? */ +# endif +#else /* _WIN32_WCE */ + /* Yet more kludges for WinCE. */ +# include /* size_t is defined here */ +# ifndef _PTRDIFF_T_DEFINED + /* ptrdiff_t is not defined */ +# define _PTRDIFF_T_DEFINED + typedef long ptrdiff_t; +# endif +#endif /* _WIN32_WCE */ + +#if !defined(GC_NOT_DLL) && !defined(GC_DLL) \ + && ((defined(_DLL) && !defined(__GNUC__)) \ + || (defined(DLL_EXPORT) && defined(GC_BUILD))) +# define GC_DLL +#endif + +#if defined(GC_DLL) && !defined(GC_API) + +# if defined(__CEGCC__) +# if defined(GC_BUILD) +# define GC_API __declspec(dllexport) +# else +# define GC_API __declspec(dllimport) +# endif + +# elif defined(__MINGW32__) +# if defined(__cplusplus) && defined(GC_BUILD) +# define GC_API extern __declspec(dllexport) +# elif defined(GC_BUILD) || defined(__MINGW32_DELAY_LOAD__) +# define GC_API __declspec(dllexport) +# else +# define GC_API extern __declspec(dllimport) +# endif + +# elif defined(_MSC_VER) || defined(__DMC__) || defined(__BORLANDC__) \ + || defined(__CYGWIN__) +# ifdef GC_BUILD +# define GC_API extern __declspec(dllexport) +# else +# define GC_API __declspec(dllimport) +# endif + +# elif defined(__WATCOMC__) +# ifdef GC_BUILD +# define GC_API extern __declspec(dllexport) +# else +# define GC_API extern __declspec(dllimport) +# endif + +# elif defined(__SYMBIAN32__) +# ifdef GC_BUILD +# define GC_API extern EXPORT_C +# else +# define GC_API extern IMPORT_C +# endif + +# elif defined(__GNUC__) + /* Only matters if used in conjunction with -fvisibility=hidden option. */ +# if defined(GC_BUILD) && !defined(GC_NO_VISIBILITY) \ + && (GC_GNUC_PREREQ(4, 0) || defined(GC_VISIBILITY_HIDDEN_SET)) +# define GC_API extern __attribute__((__visibility__("default"))) +# endif +# endif +#endif /* GC_DLL */ + +#ifndef GC_API +# define GC_API extern +#endif + +#ifndef GC_CALL +# define GC_CALL +#endif + +#ifndef GC_CALLBACK +# define GC_CALLBACK GC_CALL +#endif + +#ifndef GC_ATTR_MALLOC + /* 'malloc' attribute should be used for all malloc-like functions */ + /* (to tell the compiler that a function may be treated as if any */ + /* non-NULL pointer it returns cannot alias any other pointer valid */ + /* when the function returns). If the client code violates this rule */ + /* by using custom GC_oom_func then define GC_OOM_FUNC_RETURNS_ALIAS. */ +# ifdef GC_OOM_FUNC_RETURNS_ALIAS +# define GC_ATTR_MALLOC /* empty */ +# elif GC_GNUC_PREREQ(3, 1) +# define GC_ATTR_MALLOC __attribute__((__malloc__)) +# elif defined(_MSC_VER) && (_MSC_VER >= 1900) && !defined(__EDG__) +# define GC_ATTR_MALLOC \ + __declspec(allocator) __declspec(noalias) __declspec(restrict) +# elif defined(_MSC_VER) && _MSC_VER >= 1400 +# define GC_ATTR_MALLOC __declspec(noalias) __declspec(restrict) +# else +# define GC_ATTR_MALLOC +# endif +#endif + +#ifndef GC_ATTR_ALLOC_SIZE + /* 'alloc_size' attribute improves __builtin_object_size correctness. */ +# undef GC_ATTR_CALLOC_SIZE +# ifdef __clang__ +# if __has_attribute(__alloc_size__) +# define GC_ATTR_ALLOC_SIZE(argnum) __attribute__((__alloc_size__(argnum))) +# define GC_ATTR_CALLOC_SIZE(n, s) __attribute__((__alloc_size__(n, s))) +# else +# define GC_ATTR_ALLOC_SIZE(argnum) /* empty */ +# endif +# elif GC_GNUC_PREREQ(4, 3) && !defined(__ICC) +# define GC_ATTR_ALLOC_SIZE(argnum) __attribute__((__alloc_size__(argnum))) +# define GC_ATTR_CALLOC_SIZE(n, s) __attribute__((__alloc_size__(n, s))) +# else +# define GC_ATTR_ALLOC_SIZE(argnum) /* empty */ +# endif +#endif + +#ifndef GC_ATTR_CALLOC_SIZE +# define GC_ATTR_CALLOC_SIZE(n, s) /* empty */ +#endif + +#ifndef GC_ATTR_NONNULL +# if GC_GNUC_PREREQ(4, 0) +# define GC_ATTR_NONNULL(argnum) __attribute__((__nonnull__(argnum))) +# else +# define GC_ATTR_NONNULL(argnum) /* empty */ +# endif +#endif + +#ifndef GC_ATTR_CONST +# if GC_GNUC_PREREQ(4, 0) +# define GC_ATTR_CONST __attribute__((__const__)) +# else +# define GC_ATTR_CONST /* empty */ +# endif +#endif + +#ifndef GC_ATTR_DEPRECATED +# ifdef GC_BUILD +# undef GC_ATTR_DEPRECATED +# define GC_ATTR_DEPRECATED /* empty */ +# elif GC_GNUC_PREREQ(4, 0) +# define GC_ATTR_DEPRECATED __attribute__((__deprecated__)) +# elif defined(_MSC_VER) && _MSC_VER >= 1200 +# define GC_ATTR_DEPRECATED __declspec(deprecated) +# else +# define GC_ATTR_DEPRECATED /* empty */ +# endif +#endif + +#if defined(__sgi) && !defined(__GNUC__) && _COMPILER_VERSION >= 720 +# define GC_ADD_CALLER +# define GC_RETURN_ADDR (GC_word)__return_address +#endif + +#if defined(__linux__) || defined(__GLIBC__) +# if !defined(__native_client__) +# include +# endif +# if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1 || __GLIBC__ > 2) \ + && !defined(__ia64__) \ + && !defined(GC_MISSING_EXECINFO_H) \ + && !defined(GC_HAVE_BUILTIN_BACKTRACE) +# define GC_HAVE_BUILTIN_BACKTRACE +# endif +# if defined(__i386__) || defined(__amd64__) || defined(__x86_64__) +# define GC_CAN_SAVE_CALL_STACKS +# endif +#endif /* GLIBC */ + +#if defined(_MSC_VER) && _MSC_VER >= 1200 /* version 12.0+ (MSVC 6.0+) */ \ + && !defined(_M_ARM) && !defined(_M_ARM64) \ + && !defined(_AMD64_) && !defined(_M_X64) && !defined(_WIN32_WCE) \ + && !defined(GC_HAVE_NO_BUILTIN_BACKTRACE) \ + && !defined(GC_HAVE_BUILTIN_BACKTRACE) +# define GC_HAVE_BUILTIN_BACKTRACE +#endif + +#if defined(GC_HAVE_BUILTIN_BACKTRACE) && !defined(GC_CAN_SAVE_CALL_STACKS) +# define GC_CAN_SAVE_CALL_STACKS +#endif + +#if defined(__sparc__) +# define GC_CAN_SAVE_CALL_STACKS +#endif + +/* If we're on a platform on which we can't save call stacks, but */ +/* gcc is normally used, we go ahead and define GC_ADD_CALLER. */ +/* We make this decision independent of whether gcc is actually being */ +/* used, in order to keep the interface consistent, and allow mixing */ +/* of compilers. */ +/* This may also be desirable if it is possible but expensive to */ +/* retrieve the call chain. */ +#if (defined(__linux__) || defined(__DragonFly__) || defined(__FreeBSD__) \ + || defined(__FreeBSD_kernel__) || defined(__HAIKU__) \ + || defined(__NetBSD__) || defined(__OpenBSD__) \ + || defined(HOST_ANDROID) || defined(__ANDROID__)) \ + && !defined(GC_CAN_SAVE_CALL_STACKS) +# define GC_ADD_CALLER +# if GC_GNUC_PREREQ(2, 95) + /* gcc knows how to retrieve return address, but we don't know */ + /* how to generate call stacks. */ +# define GC_RETURN_ADDR (GC_word)__builtin_return_address(0) +# if GC_GNUC_PREREQ(4, 0) && (defined(__i386__) || defined(__amd64__) \ + || defined(__x86_64__) /* and probably others... */) \ + && !defined(GC_NO_RETURN_ADDR_PARENT) +# define GC_HAVE_RETURN_ADDR_PARENT +# define GC_RETURN_ADDR_PARENT \ + (GC_word)__builtin_extract_return_addr(__builtin_return_address(1)) + /* Note: a compiler might complain that calling */ + /* __builtin_return_address with a nonzero argument is unsafe. */ +# endif +# else + /* Just pass 0 for gcc compatibility. */ +# define GC_RETURN_ADDR 0 +# endif +#endif /* !GC_CAN_SAVE_CALL_STACKS */ + +#ifdef GC_PTHREADS + +# if (defined(GC_DARWIN_THREADS) || defined(GC_WIN32_PTHREADS) \ + || defined(__native_client__) || defined(GC_RTEMS_PTHREADS)) \ + && !defined(GC_NO_DLOPEN) + /* Either there is no dlopen() or we do not need to intercept it. */ +# define GC_NO_DLOPEN +# endif + +# if (defined(GC_DARWIN_THREADS) || defined(GC_WIN32_PTHREADS) \ + || defined(__native_client__)) \ + && !defined(GC_NO_PTHREAD_SIGMASK) + /* Either there is no pthread_sigmask() or no need to intercept it. */ +# define GC_NO_PTHREAD_SIGMASK +# endif + +# if defined(__native_client__) + /* At present, NaCl pthread_create() prototype does not have */ + /* "const" for its "attr" argument; also, NaCl pthread_exit() one */ + /* does not have "noreturn" attribute. */ +# ifndef GC_PTHREAD_CREATE_CONST +# define GC_PTHREAD_CREATE_CONST /* empty */ +# endif +# ifndef GC_HAVE_PTHREAD_EXIT +# define GC_HAVE_PTHREAD_EXIT +# define GC_PTHREAD_EXIT_ATTRIBUTE /* empty */ +# endif +# endif + +# if !defined(GC_HAVE_PTHREAD_EXIT) \ + && !defined(HOST_ANDROID) && !defined(__ANDROID__) \ + && (defined(GC_LINUX_THREADS) || defined(GC_SOLARIS_THREADS)) +# define GC_HAVE_PTHREAD_EXIT + /* Intercept pthread_exit on Linux and Solaris. */ +# if GC_GNUC_PREREQ(2, 7) +# define GC_PTHREAD_EXIT_ATTRIBUTE __attribute__((__noreturn__)) +# elif defined(__NORETURN) /* used in Solaris */ +# define GC_PTHREAD_EXIT_ATTRIBUTE __NORETURN +# else +# define GC_PTHREAD_EXIT_ATTRIBUTE /* empty */ +# endif +# endif + +# if (!defined(GC_HAVE_PTHREAD_EXIT) || defined(__native_client__)) \ + && !defined(GC_NO_PTHREAD_CANCEL) + /* Either there is no pthread_cancel() or no need to intercept it. */ +# define GC_NO_PTHREAD_CANCEL +# endif + +#endif /* GC_PTHREADS */ + +#ifdef __cplusplus + +#ifndef GC_ATTR_EXPLICIT +# if __cplusplus >= 201103L && !defined(__clang__) || _MSVC_LANG >= 201103L \ + || defined(CPPCHECK) +# define GC_ATTR_EXPLICIT explicit +# else +# define GC_ATTR_EXPLICIT /* empty */ +# endif +#endif + +#ifndef GC_NOEXCEPT +# if defined(__DMC__) || (defined(__BORLANDC__) \ + && (defined(_RWSTD_NO_EXCEPTIONS) || defined(_RWSTD_NO_EX_SPEC))) \ + || (defined(_MSC_VER) && defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS) \ + || (defined(__WATCOMC__) && !defined(_CPPUNWIND)) +# define GC_NOEXCEPT /* empty */ +# ifndef GC_NEW_ABORTS_ON_OOM +# define GC_NEW_ABORTS_ON_OOM +# endif +# elif __cplusplus >= 201103L || _MSVC_LANG >= 201103L +# define GC_NOEXCEPT noexcept +# else +# define GC_NOEXCEPT throw() +# endif +#endif + +#ifndef GC_CONSTEXPR +# if __cplusplus >= 202002L +# define GC_CONSTEXPR constexpr +# else +# define GC_CONSTEXPR /* empty */ +# endif +#endif + +#endif /* __cplusplus */ + +#endif diff --git a/thirdparty/libgc/include/gc/gc_disclaim.h b/thirdparty/libgc/include/gc/gc_disclaim.h new file mode 100644 index 0000000..aa3930e --- /dev/null +++ b/thirdparty/libgc/include/gc/gc_disclaim.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2007-2011 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_DISCLAIM_H +#define GC_DISCLAIM_H + +#include "gc.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/* This API is defined only if the library has been suitably compiled */ +/* (i.e. with ENABLE_DISCLAIM defined). */ + +/* Prepare the object kind used by GC_finalized_malloc. Call it from */ +/* your initialization code or, at least, at some point before using */ +/* finalized allocations. The function is thread-safe. */ +GC_API void GC_CALL GC_init_finalized_malloc(void); + +/* Type of a disclaim callback. Called with the allocator lock held. */ +typedef int (GC_CALLBACK * GC_disclaim_proc)(void * /* obj */); + +/* Register proc to be called on each object (of given kind) ready to */ +/* be reclaimed. If proc() returns non-zero, the collector will not */ +/* reclaim the object on this GC cycle (proc() should not try to */ +/* resurrect the object otherwise); objects reachable from proc() */ +/* (including the referred closure object) will be protected from */ +/* collection if mark_from_all is non-zero, but at the expense that */ +/* long chains of objects will take many cycles to reclaim. Any call */ +/* to GC_free() deallocates the object (pointed by the argument) */ +/* without inquiring proc(). Acquires the allocator lock. No-op in */ +/* the leak-finding mode. */ +GC_API void GC_CALL GC_register_disclaim_proc(int /* kind */, + GC_disclaim_proc /* proc */, + int /* mark_from_all */); + +/* The finalizer closure used by GC_finalized_malloc. */ +struct GC_finalizer_closure { + GC_finalization_proc proc; + void *cd; +}; + +/* Allocate an object which is to be finalized by the given closure. */ +/* This uses a dedicated object kind with a disclaim procedure, and is */ +/* more efficient than GC_register_finalizer and friends. */ +/* GC_init_finalized_malloc must be called before using this. */ +/* The collector will reclaim the object during this GC cycle (thus, */ +/* fc->proc() should not try to resurrect the object). The other */ +/* objects reachable from fc->proc (including the closure object in */ +/* case it is a heap-allocated one) will be protected from collection. */ +/* Note that GC_size (applied to such allocated object) returns a value */ +/* slightly bigger than the specified allocation size, and that GC_base */ +/* result points to a word prior to the start of the allocated object. */ +/* The disclaim procedure is not invoked in the leak-finding mode. */ +/* There is no debugging version of this allocation API. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_finalized_malloc(size_t /* size */, + const struct GC_finalizer_closure * /* fc */) GC_ATTR_NONNULL(2); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif diff --git a/thirdparty/libgc/include/gc/gc_gcj.h b/thirdparty/libgc/include/gc/gc_gcj.h new file mode 100644 index 0000000..4fda157 --- /dev/null +++ b/thirdparty/libgc/include/gc/gc_gcj.h @@ -0,0 +1,110 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright 1999 by Hewlett-Packard Company. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* This file assumes the collector has been compiled with GC_GCJ_SUPPORT. */ + +/* + * We allocate objects whose first word contains a pointer to a struct + * describing the object type. This struct contains a garbage collector mark + * descriptor at offset MARK_DESCR_OFFSET. Alternatively, the objects + * may be marked by the mark procedure passed to GC_init_gcj_malloc_mp. + */ + +#ifndef GC_GCJ_H +#define GC_GCJ_H + + /* Gcj keeps GC descriptor as second word of vtable. This */ + /* probably needs to be adjusted for other clients. */ + /* We currently assume that this offset is such that: */ + /* - all objects of this kind are large enough to have */ + /* a value at that offset, and */ + /* - it is not zero. */ + /* These assumptions allow objects on the free list to be */ + /* marked normally. */ + +#ifndef GC_H +# include "gc.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* This function must be called before the gcj allocators are invoked. */ +/* mp_index and mp are the index and mark proc (see gc_mark.h), */ +/* respectively, for the allocated objects. mp will be used to build */ +/* the descriptor for objects allocated through the debugging */ +/* interface; it will be invoked on all such objects with an */ +/* "environment" value of 1. The client may choose to use the same */ +/* mark proc for some of its generated mark descriptors. */ +/* In that case, it should use a different "environment" value to */ +/* detect the presence or absence of the debug header. */ +/* mp is really of type GC_mark_proc, as defined in gc_mark.h; we do */ +/* not want to include that here for namespace pollution reasons. */ +/* Passing in mp_index here instead of having GC_init_gcj_malloc() */ +/* internally call GC_new_proc() is quite ugly, but in typical usage */ +/* scenarios a compiler also has to know about mp_index, so */ +/* generating it dynamically is not acceptable. The mp_index will */ +/* typically be an integer less than RESERVED_MARK_PROCS, so that it */ +/* does not collide with indices allocated by GC_new_proc. If the */ +/* application needs no other reserved indices, zero */ +/* (GC_GCJ_RESERVED_MARK_PROC_INDEX in gc_mark.h) is an obvious choice. */ +/* Deprecated, portable clients should include gc_mark.h and use */ +/* GC_init_gcj_malloc_mp() instead. */ +GC_API GC_ATTR_DEPRECATED void GC_CALL GC_init_gcj_malloc(int /* mp_index */, + void * /* mp */); + +/* Allocate an object, clear it, and store the pointer to the type */ +/* structure (vtable in gcj). This adds a byte at the end of the */ +/* object if GC_malloc() would. In case of out of memory, GC_oom_fn() */ +/* is called and its result is returned. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_gcj_malloc(size_t /* lb */, + void * /* ptr_to_struct_containing_descr */); + +/* The debug versions allocate such that the specified mark proc */ +/* is always invoked. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_gcj_malloc(size_t /* lb */, + void * /* ptr_to_struct_containing_descr */, + GC_EXTRA_PARAMS); + +/* Similar to GC_gcj_malloc, but assumes that a pointer to near the */ +/* beginning (i.e. within the first heap block) of the allocated object */ +/* is always maintained. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_gcj_malloc_ignore_off_page(size_t /* lb */, + void * /* ptr_to_struct_containing_descr */); + +/* The kind numbers of normal and debug gcj objects. */ +/* Useful only for debug support, we hope. */ +GC_API int GC_gcj_kind; + +GC_API int GC_gcj_debug_kind; + +#ifdef GC_DEBUG +# define GC_GCJ_MALLOC(s,d) GC_debug_gcj_malloc(s,d,GC_EXTRAS) +# define GC_GCJ_MALLOC_IGNORE_OFF_PAGE(s,d) GC_debug_gcj_malloc(s,d,GC_EXTRAS) +#else +# define GC_GCJ_MALLOC(s,d) GC_gcj_malloc(s,d) +# define GC_GCJ_MALLOC_IGNORE_OFF_PAGE(s,d) GC_gcj_malloc_ignore_off_page(s,d) +#endif + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_GCJ_H */ diff --git a/thirdparty/libgc/include/gc/gc_inline.h b/thirdparty/libgc/include/gc/gc_inline.h new file mode 100644 index 0000000..9e559ec --- /dev/null +++ b/thirdparty/libgc/include/gc/gc_inline.h @@ -0,0 +1,220 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright (c) 2005 Hewlett-Packard Development Company, L.P. + * Copyright (c) 2008-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_INLINE_H +#define GC_INLINE_H + +/* WARNING: */ +/* Note that for these routines, it is the clients responsibility to */ +/* add the extra byte at the end to deal with one-past-the-end pointers.*/ +/* In the standard collector configuration, the collector assumes that */ +/* such a byte has been added, and hence does not trace the last word */ +/* in the resulting object. */ +/* This is not an issue if the collector is compiled with */ +/* DONT_ADD_BYTE_AT_END, or if GC_all_interior_pointers is not set. */ +/* This interface is most useful for compilers that generate C. */ +/* It is also used internally for thread-local allocation. */ +/* Manual use is hereby discouraged. */ +/* Clients should include atomic_ops.h (or similar) before this header. */ +/* There is no debugging version of this allocation API. */ + +#include "gc.h" +#include "gc_tiny_fl.h" + +#if GC_GNUC_PREREQ(3, 0) || defined(__clang__) +# define GC_EXPECT(expr, outcome) __builtin_expect(expr, outcome) + /* Equivalent to (expr), but predict that usually (expr)==outcome. */ +#else +# define GC_EXPECT(expr, outcome) (expr) +#endif + +#ifndef GC_ASSERT +# ifdef NDEBUG +# define GC_ASSERT(expr) /* empty */ +# else +# include +# define GC_ASSERT(expr) assert(expr) +# endif +#endif + +#ifndef GC_PREFETCH_FOR_WRITE +# if (GC_GNUC_PREREQ(3, 0) || defined(__clang__)) \ + && !defined(GC_NO_PREFETCH_FOR_WRITE) +# define GC_PREFETCH_FOR_WRITE(x) __builtin_prefetch((x), 1 /* write */) +# elif defined(_MSC_VER) && !defined(GC_NO_PREFETCH_FOR_WRITE) \ + && (defined(_M_IX86) || defined(_M_X64)) && !defined(_CHPE_ONLY_) \ + && (_MSC_VER >= 1900) /* VS 2015+ */ +# include +# define GC_PREFETCH_FOR_WRITE(x) _m_prefetchw(x) + /* TODO: Support also _M_ARM (__prefetchw). */ +# else +# define GC_PREFETCH_FOR_WRITE(x) (void)0 +# endif +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* Object kinds (exposed to public). */ +#define GC_I_PTRFREE 0 +#define GC_I_NORMAL 1 + +/* Store a pointer to a list of newly allocated objects of kind k and */ +/* size lb in *result. The caller must make sure that *result is */ +/* traced even if objects are ptrfree. */ +GC_API void GC_CALL GC_generic_malloc_many(size_t /* lb */, int /* k */, + void ** /* result */); + +/* Generalized version of GC_malloc and GC_malloc_atomic. */ +/* Uses appropriately the thread-local (if available) or the global */ +/* free-list of the specified kind. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_kind(size_t /* lb */, int /* k */); + +#ifdef GC_THREADS + /* Same as above but uses only the global free-list. */ + GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_kind_global(size_t /* lb */, int /* k */); +#else +# define GC_malloc_kind_global GC_malloc_kind +#endif + +/* An internal macro to update the free list pointer atomically (if */ +/* the AO primitives are available) to avoid race with the marker. */ +#if defined(GC_THREADS) && defined(AO_HAVE_store) +# define GC_FAST_M_AO_STORE(my_fl, next) \ + AO_store((volatile AO_t *)(my_fl), (AO_t)(next)) +#else +# define GC_FAST_M_AO_STORE(my_fl, next) (void)(*(my_fl) = (next)) +#endif + +/* The ultimately general inline allocation macro. Allocate an object */ +/* of size granules, putting the resulting pointer in result. Tiny_fl */ +/* is a "tiny" free list array, which will be used first, if the size */ +/* is appropriate. If granules argument is too large, we allocate with */ +/* default_expr instead. If we need to refill the free list, we use */ +/* GC_generic_malloc_many with the indicated kind. */ +/* Tiny_fl should be an array of GC_TINY_FREELISTS void * pointers. */ +/* If num_direct is nonzero, and the individual free list pointers */ +/* are initialized to (void *)1, then we allocate num_direct granules */ +/* directly using generic_malloc before putting multiple objects into */ +/* the tiny_fl entry. If num_direct is zero, then the free lists may */ +/* also be initialized to (void *)0. */ +/* Note that we use the zeroth free list to hold objects 1 granule in */ +/* size that are used to satisfy size 0 allocation requests. */ +/* We rely on much of this hopefully getting optimized away in the */ +/* case of num_direct is 0. Particularly, if granules argument is */ +/* constant, this should generate a small amount of code. */ +# define GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, num_direct, \ + kind, default_expr, init) \ + do { \ + if (GC_EXPECT((granules) >= GC_TINY_FREELISTS, 0)) { \ + result = (default_expr); \ + } else { \ + void **my_fl = (tiny_fl) + (granules); \ + void *my_entry = *my_fl; \ + void *next; \ + \ + for (;;) { \ + if (GC_EXPECT((GC_word)my_entry \ + > (num_direct) + GC_TINY_FREELISTS + 1, 1)) { \ + next = *(void **)(my_entry); \ + result = my_entry; \ + GC_FAST_M_AO_STORE(my_fl, next); \ + init; \ + GC_PREFETCH_FOR_WRITE(next); \ + if ((kind) != GC_I_PTRFREE) { \ + GC_end_stubborn_change(my_fl); \ + GC_reachable_here(next); \ + } \ + GC_ASSERT(GC_size(result) >= (granules)*GC_GRANULE_BYTES); \ + GC_ASSERT((kind) == GC_I_PTRFREE \ + || ((GC_word *)result)[1] == 0); \ + break; \ + } \ + /* Entry contains counter or NULL */ \ + if ((GC_signed_word)my_entry - (GC_signed_word)(num_direct) <= 0 \ + /* (GC_word)my_entry <= (num_direct) */ \ + && my_entry != 0 /* NULL */) { \ + /* Small counter value, not NULL */ \ + GC_FAST_M_AO_STORE(my_fl, (char *)my_entry \ + + (granules) + 1); \ + result = (default_expr); \ + break; \ + } else { \ + /* Large counter or NULL */ \ + GC_generic_malloc_many((granules) == 0 ? GC_GRANULE_BYTES : \ + GC_RAW_BYTES_FROM_INDEX(granules), \ + kind, my_fl); \ + my_entry = *my_fl; \ + if (my_entry == 0) { \ + result = (*GC_get_oom_fn())((granules)*GC_GRANULE_BYTES); \ + break; \ + } \ + } \ + } \ + } \ + } while (0) + +# define GC_WORDS_TO_WHOLE_GRANULES(n) \ + GC_WORDS_TO_GRANULES((n) + GC_GRANULE_WORDS - 1) + +/* Allocate n words (not bytes). The pointer is stored to result. */ +/* Note: this should really only be used if GC_all_interior_pointers is */ +/* not set, or DONT_ADD_BYTE_AT_END is set; see above. */ +/* Does not acquire the allocator lock. The caller is responsible for */ +/* supplying a cleared tiny_fl free list array. For single-threaded */ +/* applications, this may be a global array. */ +# define GC_MALLOC_WORDS_KIND(result, n, tiny_fl, kind, init) \ + do { \ + size_t granules = GC_WORDS_TO_WHOLE_GRANULES(n); \ + GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, 0, kind, \ + GC_malloc_kind(granules*GC_GRANULE_BYTES, kind), \ + init); \ + } while (0) + +# define GC_MALLOC_WORDS(result, n, tiny_fl) \ + GC_MALLOC_WORDS_KIND(result, n, tiny_fl, GC_I_NORMAL, \ + *(void **)(result) = 0) + +# define GC_MALLOC_ATOMIC_WORDS(result, n, tiny_fl) \ + GC_MALLOC_WORDS_KIND(result, n, tiny_fl, GC_I_PTRFREE, (void)0) + +/* And once more for two word initialized objects: */ +# define GC_CONS(result, first, second, tiny_fl) \ + do { \ + void *l = (void *)(first); \ + void *r = (void *)(second); \ + GC_MALLOC_WORDS_KIND(result, 2, tiny_fl, GC_I_NORMAL, (void)0); \ + if ((result) != 0 /* NULL */) { \ + *(void **)(result) = l; \ + GC_ptr_store_and_dirty((void **)(result) + 1, r); \ + GC_reachable_here(l); \ + } \ + } while (0) + +/* Print address of each object in the free list. The caller should */ +/* hold the allocator lock at least in the reader mode. Defined only */ +/* if the library has been compiled without NO_DEBUGGING. */ +GC_API void GC_CALL GC_print_free_list(int /* kind */, + size_t /* sz_in_granules */); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* !GC_INLINE_H */ diff --git a/thirdparty/libgc/include/gc/gc_mark.h b/thirdparty/libgc/include/gc/gc_mark.h new file mode 100644 index 0000000..093dd01 --- /dev/null +++ b/thirdparty/libgc/include/gc/gc_mark.h @@ -0,0 +1,417 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 2001 by Hewlett-Packard Company. All rights reserved. + * Copyright (c) 2009-2022 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + +/* + * This contains interfaces to the GC marker that are likely to be useful to + * clients that provide detailed heap layout information to the collector. + * This interface should not be used by normal C or C++ clients. + * It will be useful to runtimes for other languages. + * + * This is an experts-only interface! There are many ways to break the + * collector in subtle ways by using this functionality. + */ +#ifndef GC_MARK_H +#define GC_MARK_H + +#ifndef GC_H +# include "gc.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +#define GC_PROC_BYTES 100 + +#if defined(GC_BUILD) || defined(NOT_GCBUILD) + struct GC_ms_entry; + struct GC_hblk_s; +#else + struct GC_ms_entry { void *opaque; }; + struct GC_hblk_s { void *opaque; }; +#endif + +/* A client supplied mark procedure. Returns new mark stack pointer. */ +/* Primary effect should be to push new entries on the mark stack. */ +/* Mark stack pointer values are passed and returned explicitly. */ +/* Global variables describing mark stack are not necessarily valid. */ +/* (This usually saves a few cycles by keeping things in registers.) */ +/* Assumed to scan about GC_PROC_BYTES on average. If it needs to do */ +/* much more work than that, it should do it in smaller pieces by */ +/* pushing itself back on the mark stack. */ +/* Note that it should always do some work (defined as marking some */ +/* objects) before pushing more than one entry on the mark stack. */ +/* This is required to ensure termination in the event of mark stack */ +/* overflows. */ +/* This procedure is always called with at least one empty entry on the */ +/* mark stack. */ +/* Currently we require that mark procedures look for pointers in a */ +/* subset of the places the conservative marker would. It must be safe */ +/* to invoke the normal mark procedure instead. */ +/* WARNING: Such a mark procedure may be invoked on an unused object */ +/* residing on a free list. Such objects are cleared, except for a */ +/* free list link field in the first word. Thus mark procedures may */ +/* not count on the presence of a type descriptor, and must handle this */ +/* case correctly somehow. Also, a mark procedure should be prepared */ +/* to be executed concurrently from the marker threads (the later ones */ +/* are created only if the client has called GC_start_mark_threads() */ +/* or started a user thread previously). */ +typedef struct GC_ms_entry * (GC_CALLBACK * GC_mark_proc)(GC_word * /* addr */, + struct GC_ms_entry * /* mark_stack_ptr */, + struct GC_ms_entry * /* mark_stack_limit */, + GC_word /* env */); + +#define GC_LOG_MAX_MARK_PROCS 6 +#define GC_MAX_MARK_PROCS (1 << GC_LOG_MAX_MARK_PROCS) + +/* In a few cases it's necessary to assign statically known indices to */ +/* certain mark procs. Thus we reserve a few for well known clients. */ +/* (This is necessary if mark descriptors are compiler generated.) */ +#define GC_RESERVED_MARK_PROCS 8 +#define GC_GCJ_RESERVED_MARK_PROC_INDEX 0 + +/* Object descriptors on mark stack or in objects. Low order two */ +/* bits are tags distinguishing among the following 4 possibilities */ +/* for the rest (high order) bits. */ +#define GC_DS_TAG_BITS 2 +#define GC_DS_TAGS ((1U << GC_DS_TAG_BITS) - 1) +#define GC_DS_LENGTH 0 /* The entire word is a length in bytes that */ + /* must be a multiple of 4. */ +#define GC_DS_BITMAP 1 /* The high order bits are describing pointer */ + /* fields. The most significant bit is set if */ + /* the first word is a pointer. */ + /* (This unconventional ordering sometimes */ + /* makes the marker slightly faster.) */ + /* Zeroes indicate definite nonpointers. Ones */ + /* indicate possible pointers. */ + /* Only usable if pointers are word aligned. */ +#define GC_DS_PROC 2 + /* The objects referenced by this object can be */ + /* pushed on the mark stack by invoking */ + /* PROC(descr). ENV(descr) is passed as the */ + /* last argument. */ +#define GC_MAKE_PROC(proc_index, env) \ + ((((((GC_word)(env)) << GC_LOG_MAX_MARK_PROCS) \ + | (unsigned)(proc_index)) << GC_DS_TAG_BITS) \ + | (GC_word)GC_DS_PROC) +#define GC_DS_PER_OBJECT 3 /* The real descriptor is at the */ + /* byte displacement from the beginning of the */ + /* object given by descr & ~GC_DS_TAGS. */ + /* If the descriptor is negative, the real */ + /* descriptor is at (*) - */ + /* (descr&~GC_DS_TAGS) - GC_INDIR_PER_OBJ_BIAS */ + /* The latter alternative can be used if each */ + /* object contains a type descriptor in the */ + /* first word. */ + /* Note that in the multi-threaded environments */ + /* per-object descriptors must be located in */ + /* either the first two or last two words of */ + /* the object, since only those are guaranteed */ + /* to be cleared while the allocator lock is */ + /* held. */ +#define GC_INDIR_PER_OBJ_BIAS 0x10 + +GC_API void * GC_least_plausible_heap_addr; +GC_API void * GC_greatest_plausible_heap_addr; + /* Bounds on the heap. Guaranteed to be valid. */ + /* Likely to include future heap expansion. */ + /* Hence usually includes not-yet-mapped */ + /* memory, or might overlap with other data */ + /* roots. The address of any heap object is */ + /* larger than GC_least_plausible_heap_addr and */ + /* less than GC_greatest_plausible_heap_addr. */ + +/* Specify the pointer mask. Works only if the collector is built with */ +/* DYNAMIC_POINTER_MASK macro defined. These primitives are normally */ +/* needed only to support systems that use high-order pointer tags. */ +/* The setter is expected to be called, if needed, before the GC */ +/* initialization or, at least, before the first object is allocated. */ +/* Both the setter and the getter are unsynchronized. */ +GC_API void GC_CALL GC_set_pointer_mask(GC_word); +GC_API GC_word GC_CALL GC_get_pointer_mask(void); + +/* Similar to GC_set/get_pointer_mask but for the pointer shift. */ +/* The value should be less than the size of word, in bits. Applied */ +/* after the mask. */ +GC_API void GC_CALL GC_set_pointer_shift(unsigned); +GC_API unsigned GC_CALL GC_get_pointer_shift(void); + +/* Handle nested references in a custom mark procedure. */ +/* Check if obj is a valid object. If so, ensure that it is marked. */ +/* If it was not previously marked, push its contents onto the mark */ +/* stack for future scanning. The object will then be scanned using */ +/* its mark descriptor. */ +/* Returns the new mark stack pointer. */ +/* Handles mark stack overflows correctly. */ +/* Since this marks first, it makes progress even if there are mark */ +/* stack overflows. */ +/* Src is the address of the pointer to obj, which is used only */ +/* for back pointer-based heap debugging. */ +/* It is strongly recommended that most objects be handled without mark */ +/* procedures, e.g. with bitmap descriptors, and that mark procedures */ +/* be reserved for exceptional cases. That will ensure that */ +/* performance of this call is not extremely performance critical. */ +/* (Otherwise we would need to inline GC_mark_and_push completely, */ +/* which would tie the client code to a fixed collector version.) */ +/* Note that mark procedures should explicitly call FIXUP_POINTER() */ +/* if required. */ +GC_API struct GC_ms_entry * GC_CALL GC_mark_and_push(void * /* obj */, + struct GC_ms_entry * /* mark_stack_ptr */, + struct GC_ms_entry * /* mark_stack_limit */, + void ** /* src */); + +#define GC_MARK_AND_PUSH(obj, msp, lim, src) \ + ((GC_word)(obj) > (GC_word)GC_least_plausible_heap_addr \ + && (GC_word)(obj) < (GC_word)GC_greatest_plausible_heap_addr ? \ + GC_mark_and_push(obj, msp, lim, src) : (msp)) + +/* The size of the header added to objects allocated through the */ +/* GC_debug routines. Defined as a function so that client mark */ +/* procedures do not need to be recompiled for the collector library */ +/* version changes. */ +GC_API GC_ATTR_CONST size_t GC_CALL GC_get_debug_header_size(void); +#define GC_USR_PTR_FROM_BASE(p) \ + ((void *)((char *)(p) + GC_get_debug_header_size())) + +/* The same but defined as a variable. Exists only for the backward */ +/* compatibility. Some compilers do not accept "const" together with */ +/* deprecated or dllimport attributes, so the symbol is exported as */ +/* a non-constant one. */ +GC_API GC_ATTR_DEPRECATED +# ifdef GC_BUILD + const +# endif + size_t GC_debug_header_size; + +/* Return the heap block size. Each heap block is devoted to a single */ +/* size and kind of object. */ +GC_API GC_ATTR_CONST size_t GC_CALL GC_get_hblk_size(void); + +/* Same as GC_walk_hblk_fn but with index of the free list. */ +typedef void (GC_CALLBACK * GC_walk_free_blk_fn)(struct GC_hblk_s *, + int /* index */, + GC_word /* client_data */); + +/* Apply fn to each completely empty heap block. It is the */ +/* responsibility of the caller to avoid data race during the function */ +/* execution (e.g. by acquiring the allocator lock at least in the */ +/* reader mode). */ +GC_API void GC_CALL GC_iterate_free_hblks(GC_walk_free_blk_fn, + GC_word /* client_data */) GC_ATTR_NONNULL(1); + +typedef void (GC_CALLBACK * GC_walk_hblk_fn)(struct GC_hblk_s *, + GC_word /* client_data */); + +/* Apply fn to each allocated heap block. It is the responsibility */ +/* of the caller to avoid data race during the function execution (e.g. */ +/* by acquiring the allocator lock at least in the reader mode). */ +GC_API void GC_CALL GC_apply_to_all_blocks(GC_walk_hblk_fn, + GC_word /* client_data */) GC_ATTR_NONNULL(1); + +/* If there are likely to be false references to a block starting at h */ +/* of the indicated length, then return the next plausible starting */ +/* location for h that might avoid these false references. Otherwise */ +/* NULL is returned. Assumes the allocator lock is held at least in */ +/* the reader mode but no assertion about it by design. */ +GC_API struct GC_hblk_s *GC_CALL GC_is_black_listed(struct GC_hblk_s *, + GC_word /* len */); + +/* Return the number of set mark bits for the heap block where object */ +/* p is located. Defined only if the library has been compiled */ +/* without NO_DEBUGGING. */ +GC_API unsigned GC_CALL GC_count_set_marks_in_hblk(const void * /* p */); + +/* And some routines to support creation of new "kinds", e.g. with */ +/* custom mark procedures, by language runtimes. */ +/* The _inner versions assume the caller holds the allocator lock. */ + +/* Return a new free list array. */ +GC_API void ** GC_CALL GC_new_free_list(void); +GC_API void ** GC_CALL GC_new_free_list_inner(void); + +/* Return a new kind, as specified. */ +GC_API unsigned GC_CALL GC_new_kind(void ** /* free_list */, + GC_word /* mark_descriptor_template */, + int /* add_size_to_descriptor */, + int /* clear_new_objects */) GC_ATTR_NONNULL(1); + /* The last two parameters must be zero or one. */ +GC_API unsigned GC_CALL GC_new_kind_inner(void ** /* free_list */, + GC_word /* mark_descriptor_template */, + int /* add_size_to_descriptor */, + int /* clear_new_objects */) GC_ATTR_NONNULL(1); + +/* Return a new mark procedure identifier, suitable for use as */ +/* the first argument in GC_MAKE_PROC. */ +GC_API unsigned GC_CALL GC_new_proc(GC_mark_proc); +GC_API unsigned GC_CALL GC_new_proc_inner(GC_mark_proc); + +/* Similar to GC_init_gcj_malloc() described in gc_gcj.h but with the */ +/* proper types of the arguments. */ +/* Defined only if the library has been compiled with GC_GCJ_SUPPORT. */ +GC_API void GC_CALL GC_init_gcj_malloc_mp(unsigned /* mp_index */, + GC_mark_proc /* mp */); + +/* Allocate an object of a given kind. By default, there are only */ +/* a few kinds: composite (pointerful), atomic, uncollectible, etc. */ +/* We claim it is possible for clever client code that understands the */ +/* GC internals to add more, e.g. to communicate object layout */ +/* information to the collector. Note that in the multi-threaded */ +/* contexts, this is usually unsafe for kinds that have the descriptor */ +/* in the object itself, since there is otherwise a window in which */ +/* the descriptor is not correct. Even in the single-threaded case, */ +/* we need to be sure that cleared objects on a free list don't */ +/* cause a GC crash if they are accidentally traced. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL GC_generic_malloc( + size_t /* lb */, + int /* knd */); + +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_generic_malloc_ignore_off_page( + size_t /* lb */, int /* knd */); + /* As above, but pointers to past the */ + /* first hblk of the resulting object */ + /* are ignored. */ + +/* Generalized version of GC_malloc_[atomic_]uncollectable. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_generic_malloc_uncollectable( + size_t /* lb */, int /* knd */); + +/* Same as above but primary for allocating an object of the same kind */ +/* as an existing one (kind obtained by GC_get_kind_and_size). */ +/* Not suitable for GCJ and typed-malloc kinds. */ +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_generic_or_special_malloc( + size_t /* size */, int /* knd */); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_debug_generic_or_special_malloc( + size_t /* size */, int /* knd */, + GC_EXTRA_PARAMS); + +#ifdef GC_DEBUG +# define GC_GENERIC_OR_SPECIAL_MALLOC(sz, knd) \ + GC_debug_generic_or_special_malloc(sz, knd, GC_EXTRAS) +#else +# define GC_GENERIC_OR_SPECIAL_MALLOC(sz, knd) \ + GC_generic_or_special_malloc(sz, knd) +#endif /* !GC_DEBUG */ + +/* Similar to GC_size but returns object kind. Size is returned too */ +/* if psize is not NULL. */ +GC_API int GC_CALL GC_get_kind_and_size(const void *, size_t * /* psize */) + GC_ATTR_NONNULL(1); + +typedef void (GC_CALLBACK * GC_describe_type_fn)(void * /* p */, + char * /* out_buf */); + /* A procedure which */ + /* produces a human-readable */ + /* description of the "type" of object */ + /* p into the buffer out_buf of length */ + /* GC_TYPE_DESCR_LEN. This is used by */ + /* the debug support when printing */ + /* objects. */ + /* These functions should be as robust */ + /* as possible, though we do avoid */ + /* invoking them on objects on the */ + /* global free list. */ +#define GC_TYPE_DESCR_LEN 40 + +GC_API void GC_CALL GC_register_describe_type_fn(int /* kind */, + GC_describe_type_fn); + /* Register a describe_type function */ + /* to be used when printing objects */ + /* of a particular kind. */ + +/* Clear some of the inaccessible part of the stack. Returns its */ +/* argument, so it can be used in a tail call position, hence clearing */ +/* another frame. Argument may be NULL. */ +GC_API void * GC_CALL GC_clear_stack(void *); + +/* Set and get the client notifier on collections. The client function */ +/* is called at the start of every full GC (called with the allocator */ +/* lock held). May be 0. This is a really tricky interface to use */ +/* correctly. Unless you really understand the collector internals, */ +/* the callback should not, directly or indirectly, make any GC_ or */ +/* potentially blocking calls. In particular, it is not safe to */ +/* allocate memory using the garbage collector from within the callback */ +/* function. Both the setter and the getter acquire the allocator */ +/* lock (in the reader mode in case of the getter). */ +typedef void (GC_CALLBACK * GC_start_callback_proc)(void); +GC_API void GC_CALL GC_set_start_callback(GC_start_callback_proc); +GC_API GC_start_callback_proc GC_CALL GC_get_start_callback(void); + +/* Slow/general mark bit manipulation. The caller should hold the */ +/* allocator lock (the reader mode is enough in case of GC_is_marked). */ +/* GC_is_marked returns 1 (true) or 0. The argument should be the real */ +/* address of an object (i.e. the address of the debug header if there */ +/* is one). */ +GC_API int GC_CALL GC_is_marked(const void *) GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_clear_mark_bit(const void *) GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_set_mark_bit(const void *) GC_ATTR_NONNULL(1); + +/* Push everything in the given range onto the mark stack. */ +/* (GC_push_conditional pushes either all or only dirty pages depending */ +/* on the third argument.) GC_push_all_eager also ensures that stack */ +/* is scanned immediately, not just scheduled for scanning. */ +GC_API void GC_CALL GC_push_all(void * /* bottom */, void * /* top */); +GC_API void GC_CALL GC_push_all_eager(void * /* bottom */, void * /* top */); +GC_API void GC_CALL GC_push_conditional(void * /* bottom */, void * /* top */, + int /* bool all */); +GC_API void GC_CALL GC_push_finalizer_structures(void); + +/* Set and get the client push-other-roots procedure. A client */ +/* supplied procedure should also call the original procedure. */ +/* Note that both the setter and the getter require some external */ +/* synchronization to avoid data race. */ +typedef void (GC_CALLBACK * GC_push_other_roots_proc)(void); +GC_API void GC_CALL GC_set_push_other_roots(GC_push_other_roots_proc); +GC_API GC_push_other_roots_proc GC_CALL GC_get_push_other_roots(void); + +/* Walk the GC heap visiting all reachable objects. Assume the caller */ +/* holds the allocator lock at least in the reader mode. Object base */ +/* pointer, object size and client custom data are passed to the */ +/* callback (holding the allocator lock in the same mode as the caller */ +/* does). */ +typedef void (GC_CALLBACK * GC_reachable_object_proc)(void * /* obj */, + size_t /* bytes */, + void * /* client_data */); +GC_API void GC_CALL GC_enumerate_reachable_objects_inner( + GC_reachable_object_proc, + void * /* client_data */) GC_ATTR_NONNULL(1); + +/* Is the given address in one of the temporary static root sections? */ +/* Acquires the allocator lock in the reader mode. */ +GC_API int GC_CALL GC_is_tmp_root(void *); + +GC_API void GC_CALL GC_print_trace(GC_word /* gc_no */); +GC_API void GC_CALL GC_print_trace_inner(GC_word /* gc_no */); + +/* Set the client for when mark stack is empty. A client can use */ +/* this callback to process (un)marked objects and push additional */ +/* work onto the stack. Useful for implementing ephemerons. Both the */ +/* setter and the getter acquire the allocator lock (in the reader mode */ +/* in case of the getter). */ +typedef struct GC_ms_entry * (GC_CALLBACK * GC_on_mark_stack_empty_proc)( + struct GC_ms_entry * /* mark_stack_ptr */, + struct GC_ms_entry * /* mark_stack_limit */); +GC_API void GC_CALL GC_set_on_mark_stack_empty(GC_on_mark_stack_empty_proc); +GC_API GC_on_mark_stack_empty_proc GC_CALL GC_get_on_mark_stack_empty(void); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_MARK_H */ diff --git a/thirdparty/libgc/include/gc/gc_pthread_redirects.h b/thirdparty/libgc/include/gc/gc_pthread_redirects.h new file mode 100644 index 0000000..05190d4 --- /dev/null +++ b/thirdparty/libgc/include/gc/gc_pthread_redirects.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2010 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* Our pthread support normally needs to intercept a number of thread */ +/* calls. We arrange to do that here, if appropriate. */ + +#ifndef GC_PTHREAD_REDIRECTS_H +#define GC_PTHREAD_REDIRECTS_H + +/* Included from gc.h only. Included only if GC_PTHREADS. */ +#if defined(GC_H) && defined(GC_PTHREADS) + +/* We need to intercept calls to many of the threads primitives, so */ +/* that we can locate thread stacks and stop the world. */ +/* Note also that the collector cannot always see thread specific data. */ +/* Thread specific data should generally consist of pointers to */ +/* uncollectible objects (allocated with GC_malloc_uncollectable, */ +/* not the system malloc), which are deallocated using the destructor */ +/* facility in thr_keycreate. Alternatively, keep a redundant pointer */ +/* to thread specific data on the thread stack. */ + +#ifndef GC_PTHREAD_REDIRECTS_ONLY + +# include +# ifndef GC_NO_DLOPEN +# include +# endif +# ifndef GC_NO_PTHREAD_SIGMASK +# include /* needed anyway for proper redirection */ +# endif + +# ifdef __cplusplus + extern "C" { +# endif + +# ifndef GC_SUSPEND_THREAD_ID +# define GC_SUSPEND_THREAD_ID pthread_t +# endif + +# ifndef GC_NO_DLOPEN + GC_API void *GC_dlopen(const char * /* path */, int /* mode */); +# endif /* !GC_NO_DLOPEN */ + +# ifndef GC_NO_PTHREAD_SIGMASK +# if defined(GC_PTHREAD_SIGMASK_NEEDED) \ + || defined(GC_HAVE_PTHREAD_SIGMASK) || defined(_BSD_SOURCE) \ + || defined(_GNU_SOURCE) || defined(_NETBSD_SOURCE) \ + || (_POSIX_C_SOURCE >= 199506L) || (_XOPEN_SOURCE >= 500) \ + || (__POSIX_VISIBLE >= 199506) /* xBSD internal macro */ + GC_API int GC_pthread_sigmask(int /* how */, const sigset_t *, + sigset_t * /* oset */); +# else +# define GC_NO_PTHREAD_SIGMASK +# endif +# endif /* !GC_NO_PTHREAD_SIGMASK */ + +# ifndef GC_PTHREAD_CREATE_CONST + /* This is used for pthread_create() only. */ +# define GC_PTHREAD_CREATE_CONST const +# endif + + GC_API int GC_pthread_create(pthread_t *, + GC_PTHREAD_CREATE_CONST pthread_attr_t *, + void *(*)(void *), void * /* arg */); + GC_API int GC_pthread_join(pthread_t, void ** /* retval */); + GC_API int GC_pthread_detach(pthread_t); + +# ifndef GC_NO_PTHREAD_CANCEL + GC_API int GC_pthread_cancel(pthread_t); +# endif + +# if defined(GC_HAVE_PTHREAD_EXIT) && !defined(GC_PTHREAD_EXIT_DECLARED) +# define GC_PTHREAD_EXIT_DECLARED + GC_API void GC_pthread_exit(void *) GC_PTHREAD_EXIT_ATTRIBUTE; +# endif + +# ifdef __cplusplus + } /* extern "C" */ +# endif + +#endif /* !GC_PTHREAD_REDIRECTS_ONLY */ + +#if !defined(GC_NO_THREAD_REDIRECTS) && !defined(GC_USE_LD_WRAP) + /* Unless the compiler supports #pragma extern_prefix, the Tru64 */ + /* UNIX pthread.h redefines some POSIX thread functions to use */ + /* mangled names. Anyway, it's safe to undef them before redefining. */ +# undef pthread_create +# undef pthread_join +# undef pthread_detach +# define pthread_create GC_pthread_create +# define pthread_join GC_pthread_join +# define pthread_detach GC_pthread_detach + +# ifndef GC_NO_PTHREAD_SIGMASK +# undef pthread_sigmask +# define pthread_sigmask GC_pthread_sigmask +# endif +# ifndef GC_NO_DLOPEN +# undef dlopen +# define dlopen GC_dlopen +# endif +# ifndef GC_NO_PTHREAD_CANCEL +# undef pthread_cancel +# define pthread_cancel GC_pthread_cancel +# endif +# ifdef GC_HAVE_PTHREAD_EXIT +# undef pthread_exit +# define pthread_exit GC_pthread_exit +# endif +#endif /* !GC_NO_THREAD_REDIRECTS */ + +#endif /* GC_PTHREADS */ + +#endif /* GC_PTHREAD_REDIRECTS_H */ diff --git a/thirdparty/libgc/include/gc/gc_tiny_fl.h b/thirdparty/libgc/include/gc/gc_tiny_fl.h new file mode 100644 index 0000000..af2e368 --- /dev/null +++ b/thirdparty/libgc/include/gc/gc_tiny_fl.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 1999-2005 Hewlett-Packard Development Company, L.P. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_TINY_FL_H +#define GC_TINY_FL_H + +/* Constants and data structures for "tiny" free lists. These are */ +/* used for thread-local allocation or in-lined allocators. */ +/* Each global free list also essentially starts with one of these. */ +/* However, global free lists are known to the GC. "Tiny" free lists */ +/* are basically private to the client. Their contents are viewed as */ +/* "in use" and marked accordingly by the core of the GC. */ +/* Note that inlined code might know about the layout of these and the */ +/* constants involved. Thus any change here may invalidate clients, */ +/* and such changes should be avoided. Hence we keep this as simple */ +/* as possible. */ + +/* We always set GC_GRANULE_BYTES to twice the length of a pointer. */ +/* This means that all allocation requests are rounded up to the next */ +/* multiple of 16 on 64-bit architectures or 8 on 32-bit architectures. */ +/* This appears to be a reasonable compromise between fragmentation */ +/* overhead and space usage for mark bits (usually mark bytes). */ +/* On many 64-bit architectures some memory references require 16-byte */ +/* alignment, making this necessary anyway. For a few 32-bit */ +/* architectures (e.g. x86), we may also need 16-byte alignment for */ +/* certain memory references. But currently that does not seem to be */ +/* the default for all conventional malloc implementations, so we */ +/* ignore that problem. */ +/* It would always be safe, and often useful, to be able to allocate */ +/* very small objects with smaller alignment. But that would cost us */ +/* mark bit space, so we no longer do so. */ + +/* GC_GRANULE_BYTES should not be overridden in any instances of the GC */ +/* library that may be shared between applications, since it affects */ +/* the binary interface to the library. */ +#ifndef GC_GRANULE_BYTES +# define GC_GRANULE_WORDS 2 +# if defined(__LP64__) || defined (_LP64) || defined(_WIN64) \ + || defined(__alpha__) || defined(__arch64__) \ + || defined(__powerpc64__) || defined(__s390x__) \ + || (defined(__x86_64__) && !defined(__ILP32__)) +# define GC_GRANULE_BYTES (GC_GRANULE_WORDS * 8 /* sizeof(void*) */) +# else +# define GC_GRANULE_BYTES (GC_GRANULE_WORDS * 4 /* sizeof(void*) */) +# endif +#endif /* !GC_GRANULE_BYTES */ + +#if GC_GRANULE_WORDS == 2 +# define GC_WORDS_TO_GRANULES(n) ((n)>>1) +#else +# define GC_WORDS_TO_GRANULES(n) ((n)*sizeof(void *)/GC_GRANULE_BYTES) +#endif + +/* A "tiny" free list header contains GC_TINY_FREELISTS pointers to */ +/* singly linked lists of objects of different sizes, the i-th one */ +/* containing objects i granules in size. Note that there is a list */ +/* of size zero objects. */ +#ifndef GC_TINY_FREELISTS +# if GC_GRANULE_BYTES == 16 +# define GC_TINY_FREELISTS 25 +# else +# define GC_TINY_FREELISTS 33 /* Up to and including 256 bytes */ +# endif +#endif /* !GC_TINY_FREELISTS */ + +/* The i-th free list corresponds to size i*GC_GRANULE_BYTES */ +/* Internally to the collector, the index can be computed with */ +/* ALLOC_REQUEST_GRANS(). Externally, we don't know whether */ +/* DONT_ADD_BYTE_AT_END is set, but the client should know. */ + +/* Convert a free list index to the actual size of objects */ +/* on that list, including extra space we added. Not an */ +/* inverse of the above. */ +#define GC_RAW_BYTES_FROM_INDEX(i) ((i) * GC_GRANULE_BYTES) + +#endif /* GC_TINY_FL_H */ diff --git a/thirdparty/libgc/include/gc/gc_typed.h b/thirdparty/libgc/include/gc/gc_typed.h new file mode 100644 index 0000000..837861d --- /dev/null +++ b/thirdparty/libgc/include/gc/gc_typed.h @@ -0,0 +1,157 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright 1996 Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * Some simple primitives for allocation with explicit type information. + * Facilities for dynamic type inference may be added later. + * Should be used only for extremely performance critical applications, + * or if conservative collector leakage is otherwise a problem (unlikely). + * Note that this is implemented completely separately from the rest + * of the collector, and is not linked in unless referenced. + * This does not currently support GC_DEBUG in any interesting way. + */ + +#ifndef GC_TYPED_H +#define GC_TYPED_H + +#ifndef GC_H +# include "gc.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +typedef GC_word * GC_bitmap; + /* The least significant bit of the first word is one if */ + /* the first word in the object may be a pointer. */ + +#define GC_WORDSZ (8 * sizeof(GC_word)) +#define GC_get_bit(bm, index) /* index should be of unsigned type */ \ + (((bm)[(index) / GC_WORDSZ] >> ((index) % GC_WORDSZ)) & 1) +#define GC_set_bit(bm, index) /* index should be of unsigned type */ \ + ((bm)[(index) / GC_WORDSZ] |= (GC_word)1 << ((index) % GC_WORDSZ)) +#define GC_WORD_OFFSET(t, f) (offsetof(t,f) / sizeof(GC_word)) +#define GC_WORD_LEN(t) (sizeof(t) / sizeof(GC_word)) +#define GC_BITMAP_SIZE(t) ((GC_WORD_LEN(t) + GC_WORDSZ - 1) / GC_WORDSZ) + +typedef GC_word GC_descr; + +GC_API GC_descr GC_CALL GC_make_descriptor(const GC_word * /* GC_bitmap bm */, + size_t /* len (number_of_bits_in_bitmap) */); + /* Return a type descriptor for the object whose layout */ + /* is described by the argument. */ + /* The least significant bit of the first word is one */ + /* if the first word in the object may be a pointer. */ + /* The second argument specifies the number of */ + /* meaningful bits in the bitmap. The actual object */ + /* may be larger (but not smaller). Any additional */ + /* words in the object are assumed not to contain */ + /* pointers. */ + /* Returns a conservative approximation in the */ + /* (unlikely) case of insufficient memory to build */ + /* the descriptor. Calls to GC_make_descriptor */ + /* may consume some amount of a finite resource. This */ + /* is intended to be called once per type, not once */ + /* per allocation. */ + +/* It is possible to generate a descriptor for a C type T with */ +/* word aligned pointer fields f1, f2, ... as follows: */ +/* */ +/* GC_descr T_descr; */ +/* GC_word T_bitmap[GC_BITMAP_SIZE(T)] = {0}; */ +/* GC_set_bit(T_bitmap, GC_WORD_OFFSET(T,f1)); */ +/* GC_set_bit(T_bitmap, GC_WORD_OFFSET(T,f2)); */ +/* ... */ +/* T_descr = GC_make_descriptor(T_bitmap, GC_WORD_LEN(T)); */ + +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_explicitly_typed(size_t /* size_in_bytes */, + GC_descr /* d */); + /* Allocate an object whose layout is described by d. */ + /* The size may NOT be less than the number of */ + /* meaningful bits in the bitmap of d multiplied by */ + /* sizeof GC_word. The returned object is cleared. */ + /* The returned object may NOT be passed to GC_realloc. */ + +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL + GC_malloc_explicitly_typed_ignore_off_page(size_t /* size_in_bytes */, + GC_descr /* d */); + +GC_API GC_ATTR_MALLOC GC_ATTR_CALLOC_SIZE(1, 2) void * GC_CALL + GC_calloc_explicitly_typed(size_t /* nelements */, + size_t /* element_size_in_bytes */, + GC_descr /* d */); + /* Allocate an array of nelements elements, each of the */ + /* given size, and with the given descriptor. */ + /* The element size must be a multiple of the byte */ + /* alignment required for pointers. E.g. on a 32-bit */ + /* machine with 16-bit aligned pointers, size_in_bytes */ + /* must be a multiple of 2. The element size may NOT */ + /* be less than the number of meaningful bits in the */ + /* bitmap of d multiplied by sizeof GC_word. */ + /* Returned object is cleared. */ + +#define GC_CALLOC_TYPED_DESCR_WORDS 8 + +#ifdef GC_BUILD + struct GC_calloc_typed_descr_s; +#else + struct GC_calloc_typed_descr_s { + GC_word opaque[GC_CALLOC_TYPED_DESCR_WORDS]; + }; +#endif + +GC_API int GC_CALL GC_calloc_prepare_explicitly_typed( + struct GC_calloc_typed_descr_s * /* pctd */, + size_t /* sizeof_ctd */, size_t /* nelements */, + size_t /* element_size_in_bytes */, GC_descr); + /* This is same as GC_calloc_explicitly_typed but more optimal */ + /* in terms of the performance and memory usage if the client */ + /* needs to allocate multiple typed object arrays with the */ + /* same layout and number of elements. The client should call */ + /* it to be prepared for the subsequent allocations by */ + /* GC_calloc_do_explicitly_typed, one or many. The result of */ + /* the preparation is stored to *pctd, even in case of */ + /* a failure. The prepared structure could be just dropped */ + /* when no longer needed. Returns 0 on failure, 1 on success; */ + /* the result could be ignored (as it is also stored to *pctd */ + /* and checked later by GC_calloc_do_explicitly_typed). */ + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_calloc_do_explicitly_typed( + const struct GC_calloc_typed_descr_s * /* pctd */, + size_t /* sizeof_ctd */); + /* The actual object allocation for the prepared description. */ + +#ifdef GC_DEBUG +# define GC_MALLOC_EXPLICITLY_TYPED(bytes, d) ((void)(d), GC_MALLOC(bytes)) +# define GC_MALLOC_EXPLICITLY_TYPED_IGNORE_OFF_PAGE(bytes, d) \ + GC_MALLOC_EXPLICITLY_TYPED(bytes, d) +# define GC_CALLOC_EXPLICITLY_TYPED(n, bytes, d) \ + ((void)(d), GC_MALLOC((n) * (bytes))) +#else +# define GC_MALLOC_EXPLICITLY_TYPED(bytes, d) \ + GC_malloc_explicitly_typed(bytes, d) +# define GC_MALLOC_EXPLICITLY_TYPED_IGNORE_OFF_PAGE(bytes, d) \ + GC_malloc_explicitly_typed_ignore_off_page(bytes, d) +# define GC_CALLOC_EXPLICITLY_TYPED(n, bytes, d) \ + GC_calloc_explicitly_typed(n, bytes, d) +#endif + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_TYPED_H */ diff --git a/thirdparty/libgc/include/gc/gc_version.h b/thirdparty/libgc/include/gc/gc_version.h new file mode 100644 index 0000000..0afe91b --- /dev/null +++ b/thirdparty/libgc/include/gc/gc_version.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* This should never be included directly; it is included only from gc.h. */ +#if defined(GC_H) + +/* The policy regarding version numbers: development code has odd */ +/* "minor" number (and "micro" part is 0); when development is finished */ +/* and a release is prepared, "minor" number is incremented (keeping */ +/* "micro" number still zero), whenever a defect is fixed a new release */ +/* is prepared incrementing "micro" part to odd value (the most stable */ +/* release has the biggest "micro" number). */ + +/* The version here should match that in configure/configure.ac */ +/* Eventually this one may become unnecessary. For now we need */ +/* it to keep the old-style build process working. */ +#define GC_TMP_VERSION_MAJOR 8 +#define GC_TMP_VERSION_MINOR 3 +#define GC_TMP_VERSION_MICRO 0 /* 8.3.0 */ + +#ifdef GC_VERSION_MAJOR +# if GC_TMP_VERSION_MAJOR != GC_VERSION_MAJOR \ + || GC_TMP_VERSION_MINOR != GC_VERSION_MINOR \ + || GC_TMP_VERSION_MICRO != GC_VERSION_MICRO +# error Inconsistent version info. Check README.md, include/gc_version.h and configure.ac. +# endif +#else +# define GC_VERSION_MAJOR GC_TMP_VERSION_MAJOR +# define GC_VERSION_MINOR GC_TMP_VERSION_MINOR +# define GC_VERSION_MICRO GC_TMP_VERSION_MICRO +#endif /* !GC_VERSION_MAJOR */ + +#endif diff --git a/thirdparty/libgc/include/gc/javaxfc.h b/thirdparty/libgc/include/gc/javaxfc.h new file mode 100644 index 0000000..d31c727 --- /dev/null +++ b/thirdparty/libgc/include/gc/javaxfc.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_JAVAXFC_H +#define GC_JAVAXFC_H + +#ifndef GC_H +# include "gc.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* + * Invoke all remaining finalizers that haven't yet been run. (Since the + * notifier is not called, this should be called from a separate thread.) + * This function is needed for strict compliance with the Java standard, + * which can make the runtime guarantee that all finalizers are run. + * This is problematic for several reasons: + * 1) It means that finalizers, and all methods called by them, + * must be prepared to deal with objects that have been finalized in + * spite of the fact that they are still referenced by statically + * allocated pointer variables. + * 2) It may mean that we get stuck in an infinite loop running + * finalizers which create new finalizable objects, though that's + * probably unlikely. + * Thus this is not recommended for general use. + * Acquires the allocator lock (to enqueue all finalizers). + */ +GC_API void GC_CALL GC_finalize_all(void); + +#ifdef GC_THREADS + /* External thread suspension support. No thread suspension count */ + /* (so a thread which has been suspended numerous times will be */ + /* resumed with the very first call to GC_resume_thread). */ + /* Acquires the allocator lock. Thread should be registered in GC. */ + /* Unimplemented on some platforms. Not recommended for general use. */ +# ifndef GC_SUSPEND_THREAD_ID +# define GC_SUSPEND_THREAD_ID void* +# endif + GC_API void GC_CALL GC_suspend_thread(GC_SUSPEND_THREAD_ID); + GC_API void GC_CALL GC_resume_thread(GC_SUSPEND_THREAD_ID); + + /* Is the given thread suspended externally? The result is either */ + /* 1 (true) or 0. Acquires the allocator lock in the reader mode. */ + /* Note: returns false if the thread is not registered in GC. */ + /* Unimplemented on some platforms (same as GC_suspend_thread). */ + GC_API int GC_CALL GC_is_thread_suspended(GC_SUSPEND_THREAD_ID); +#endif /* GC_THREADS */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* GC_JAVAXFC_H */ diff --git a/thirdparty/libgc/include/gc/leak_detector.h b/thirdparty/libgc/include/gc/leak_detector.h new file mode 100644 index 0000000..84008de --- /dev/null +++ b/thirdparty/libgc/include/gc/leak_detector.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2000-2011 by Hewlett-Packard Development Company. + * All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_LEAK_DETECTOR_H +#define GC_LEAK_DETECTOR_H + +/* Include this header file (e.g., via gcc --include directive) */ +/* to turn libgc into a leak detector. */ + +#ifndef GC_DEBUG +# define GC_DEBUG +#endif +#include "gc.h" + +#ifndef GC_DONT_INCLUDE_STDLIB + /* We ensure stdlib.h and string.h are included before */ + /* redirecting malloc() and the accompanying functions. */ +# include +# include +#endif + +#undef malloc +#define malloc(n) GC_MALLOC(n) +#undef calloc +#define calloc(m,n) GC_MALLOC((m)*(n)) +#undef free +#define free(p) GC_FREE(p) +#undef realloc +#define realloc(p,n) GC_REALLOC(p,n) +#undef reallocarray +#define reallocarray(p,m,n) GC_REALLOC(p,(m)*(n)) + +#undef strdup +#define strdup(s) GC_STRDUP(s) +#undef strndup +#define strndup(s,n) GC_STRNDUP(s,n) + +#ifdef GC_REQUIRE_WCSDUP + /* The collector should be built with GC_REQUIRE_WCSDUP */ + /* defined as well to redirect wcsdup(). */ +# include +# undef wcsdup +# define wcsdup(s) GC_WCSDUP(s) +#endif + +/* The following routines for the aligned objects allocation */ +/* (aligned_alloc, valloc, etc.) do not have their debugging */ +/* counterparts. Note that free() called for such objects */ +/* may output a warning that the pointer has no debugging info. */ + +#undef aligned_alloc +#define aligned_alloc(a,n) GC_memalign(a,n) /* identical to memalign */ +#undef memalign +#define memalign(a,n) GC_memalign(a,n) +#undef posix_memalign +#define posix_memalign(p,a,n) GC_posix_memalign(p,a,n) + +#undef _aligned_malloc +#define _aligned_malloc(n,a) GC_memalign(a,n) /* reverse args order */ +#undef _aligned_free +#define _aligned_free(p) GC_free(p) /* non-debug */ + +#ifndef GC_NO_VALLOC +# undef valloc +# define valloc(n) GC_valloc(n) +# undef pvalloc +# define pvalloc(n) GC_pvalloc(n) /* obsolete */ +#endif /* !GC_NO_VALLOC */ + +#ifndef CHECK_LEAKS +# define CHECK_LEAKS() GC_gcollect() + /* Note 1: CHECK_LEAKS does not have GC prefix (preserved for */ + /* backward compatibility). */ + /* Note 2: GC_gcollect() is also called automatically in the */ + /* leak-finding mode at program exit. */ +#endif + +#endif /* GC_LEAK_DETECTOR_H */ diff --git a/thirdparty/miniz/ChangeLog.md b/thirdparty/miniz/ChangeLog.md new file mode 100644 index 0000000..ec99111 --- /dev/null +++ b/thirdparty/miniz/ChangeLog.md @@ -0,0 +1,239 @@ +## Changelog + +### 3.0.2 + + - Fix buffer overrun in mz_utf8z_to_widechar on Windows + +### 3.0.1 + + - Fix compilation error with MINIZ_USE_UNALIGNED_LOADS_AND_STORES=1 + +### 3.0.0 + + - Reduce memory usage for inflate. This changes `struct tinfl_decompressor_tag` and therefore requires a major version bump (breaks ABI compatibility) + - Add padding to structures so it continues to work if features differ. This also changes some structures + - Use _ftelli64, _fseeki64 and stat with MinGW32 and OpenWatcom + - Fix varios warnings with OpenWatcom compiler + - Avoid using unaligned memory access in UBSan builds + - Set MINIZ_LITTLE_ENDIAN only if not set + - Add MINIZ_NO_DEFLATE_APIS and MINIZ_NO_INFLATE_APIS + - Fix use of uninitialized memory in tinfl_decompress_mem_to_callback() + - Use wfopen on windows + - Use _wstat64 instead _stat64 on windows + - Use level_and_flags after MZ_DEFAULT_COMPRESSION has been handled + - Improve endianess detection + - Don't use unaligned stores and loads per default + - Fix function declaration if MINIZ_NO_STDIO is used + - Fix MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 not being set + - Remove total files check (its 32-bit uint) + - tinfl_decompress: avoid NULL ptr arithmetic UB + - miniz_zip: fix mz_zip_reader_extract_to_heap to read correct sizes + - Eliminate 64-bit operations on 32-bit machines + - Disable treating warnings as error with MSVC + - Disable building shared lib via CMake by default + - Fixed alignment problems on MacOS + - Fixed get error string for MZ_ZIP_TOTAL_ERRORS + - Write correct FLEVEL 2-bit value in zlib header + - miniz.pc.in: fix include path not containing the "miniz" suffix + - Fix compatibility with FreeBSD + - pkg-config tweaks + - Fix integer overflow in header corruption check + - Fix some warnings + - tdefl_compress_normal: Avoid NULL ptr arithmetic UB + - replace use of stdint.h types with mz_ variants + + +### 2.2.0 + + - Fix examples with amalgamation + - Modified cmake script to support shared library mode and find_package + - Fix for misleading doc comment on `mz_zip_reader_init_cfile` function + - Add include location tolerance and stop forcing `_GNU_SOURCE` + - Fix: mz_zip_reader_locate_file_v2 returns an mz_bool + - Fix large file system checks + - Add #elif to enable an external mz_crc32() to be linked in + - Write with dynamic size (size of file/data to be added not known before adding) + - Added uncompress2 for zlib compatibility + - Add support for building as a Meson subproject + - Added OSSFuzz support; Integrate with CIFuzz + - Add pkg-config file + - Fixed use-of-uninitialized value msan error when copying dist bytes with no output bytes written. + - mz_zip_validate_file(): fix memory leak on errors + - Fixed MSAN use-of-uninitialized in tinfl_decompress when invalid dist is decoded. In this instance dist was 31 which s_dist_base translates as 0 + - Add flag to set (compressed) size in local file header + - avoid use of uninitialized value in tdefl_record_literal + +### 2.1.0 + + - More instances of memcpy instead of cast and use memcpy per default + - Remove inline for c90 support + - New function to read files via callback functions when adding them + - Fix out of bounds read while reading Zip64 extended information + - guard memcpy when n == 0 because buffer may be NULL + - Implement inflateReset() function + - Move comp/decomp alloc/free prototypes under guarding #ifndef MZ_NO_MALLOC + - Fix large file support under Windows + - Don't warn if _LARGEFILE64_SOURCE is not defined to 1 + - Fixes for MSVC warnings + - Remove check that path of file added to archive contains ':' or '\' + - Add !defined check on MINIZ_USE_ALIGNED_LOADS_AND_STORES + +### 2.0.8 + + - Remove unimplemented functions (mz_zip_locate_file and mz_zip_locate_file_v2) + - Add license, changelog, readme and example files to release zip + - Fix heap overflow to user buffer in tinfl_status tinfl_decompress + - Fix corrupt archive if uncompressed file smaller than 4 byte and the file is added by mz_zip_writer_add_mem* + +### 2.0.7 + + - Removed need in C++ compiler in cmake build + - Fixed a lot of uninitialized value errors found with Valgrind by memsetting m_dict to 0 in tdefl_init + - Fix resource leak in mz_zip_reader_init_file_v2 + - Fix assert with mz_zip_writer_add_mem* w/MZ_DEFAULT_COMPRESSION + - cmake build: install library and headers + - Remove _LARGEFILE64_SOURCE requirement from apple defines for large files + +### 2.0.6 + + - Improve MZ_ZIP_FLAG_WRITE_ZIP64 documentation + - Remove check for cur_archive_file_ofs > UINT_MAX because cur_archive_file_ofs is not used after this point + - Add cmake debug configuration + - Fix PNG height when creating png files + - Add "iterative" file extraction method based on mz_zip_reader_extract_to_callback. + - Option to use memcpy for unaligned data access + - Define processor/arch macros as zero if not set to one + +### 2.0.4/2.0.5 + + - Fix compilation with the various omission compile definitions + +### 2.0.3 + +- Fix GCC/clang compile warnings +- Added callback for periodic flushes (for ZIP file streaming) +- Use UTF-8 for file names in ZIP files per default + +### 2.0.2 + +- Fix source backwards compatibility with 1.x +- Fix a ZIP bit not being set correctly + +### 2.0.1 + +- Added some tests +- Added CI +- Make source code ANSI C compatible + +### 2.0.0 beta + +- Matthew Sitton merged miniz 1.x to Rich Geldreich's vogl ZIP64 changes. Miniz is now licensed as MIT since the vogl code base is MIT licensed +- Miniz is now split into several files +- Miniz does now not seek backwards when creating ZIP files. That is the ZIP files can be streamed +- Miniz automatically switches to the ZIP64 format when the created ZIP files goes over ZIP file limits +- Similar to [SQLite](https://www.sqlite.org/amalgamation.html) the Miniz source code is amalgamated into one miniz.c/miniz.h pair in a build step (amalgamate.sh). Please use miniz.c/miniz.h in your projects +- Miniz 2 is only source back-compatible with miniz 1.x. It breaks binary compatibility because structures changed + +### v1.16 BETA Oct 19, 2013 + +Still testing, this release is downloadable from [here](http://www.tenacioussoftware.com/miniz_v116_beta_r1.7z). Two key inflator-only robustness and streaming related changes. Also merged in tdefl_compressor_alloc(), tdefl_compressor_free() helpers to make script bindings easier for rustyzip. I would greatly appreciate any help with testing or any feedback. + +The inflator in raw (non-zlib) mode is now usable on gzip or similar streams that have a bunch of bytes following the raw deflate data (problem discovered by rustyzip author williamw520). This version should never read beyond the last byte of the raw deflate data independent of how many bytes you pass into the input buffer. + +The inflator now has a new failure status TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS (-4). Previously, if the inflator was starved of bytes and could not make progress (because the input buffer was empty and the caller did not set the TINFL_FLAG_HAS_MORE_INPUT flag - say on truncated or corrupted compressed data stream) it would append all 0's to the input and try to soldier on. This is scary behavior if the caller didn't know when to stop accepting output (because it didn't know how much uncompressed data was expected, or didn't enforce a sane maximum). v1.16 will instead return TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS immediately if it needs 1 or more bytes to make progress, the input buf is empty, and the caller has indicated that no more input is available. This is a "soft" failure, so you can call the inflator again with more input and it will try to continue, or you can give up and fail. This could be very useful in network streaming scenarios. + +- The inflator coroutine func. is subtle and complex so I'm being cautious about this release. I would greatly appreciate any help with testing or any feedback. + I feel good about these changes, and they've been through several hours of automated testing, but they will probably not fix anything for the majority of prev. users so I'm + going to mark this release as beta for a few weeks and continue testing it at work/home on various things. +- The inflator in raw (non-zlib) mode is now usable on gzip or similar data streams that have a bunch of bytes following the raw deflate data (problem discovered by rustyzip author williamw520). + This version should *never* read beyond the last byte of the raw deflate data independent of how many bytes you pass into the input buffer. This issue was caused by the various Huffman bitbuffer lookahead optimizations, and + would not be an issue if the caller knew and enforced the precise size of the raw compressed data *or* if the compressed data was in zlib format (i.e. always followed by the byte aligned zlib adler32). + So in other words, you can now call the inflator on deflate streams that are followed by arbitrary amounts of data and it's guaranteed that decompression will stop exactly on the last byte. +- The inflator now has a new failure status: TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS (-4). Previously, if the inflator was starved of bytes and could not make progress (because the input buffer was empty and the + caller did not set the TINFL_FLAG_HAS_MORE_INPUT flag - say on truncated or corrupted compressed data stream) it would append all 0's to the input and try to soldier on. + This is scary, because in the worst case, I believe it was possible for the prev. inflator to start outputting large amounts of literal data. If the caller didn't know when to stop accepting output + (because it didn't know how much uncompressed data was expected, or didn't enforce a sane maximum) it could continue forever. v1.16 cannot fall into this failure mode, instead it'll return + TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS immediately if it needs 1 or more bytes to make progress, the input buf is empty, and the caller has indicated that no more input is available. This is a "soft" + failure, so you can call the inflator again with more input and it will try to continue, or you can give up and fail. This could be very useful in network streaming scenarios. +- Added documentation to all the tinfl return status codes, fixed miniz_tester so it accepts double minus params for Linux, tweaked example1.c, added a simple "follower bytes" test to miniz_tester.cpp. +### v1.15 r4 STABLE - Oct 13, 2013 + +Merged over a few very minor bug fixes that I fixed in the zip64 branch. This is downloadable from [here](http://code.google.com/p/miniz/downloads/list) and also in SVN head (as of 10/19/13). + + +### v1.15 - Oct. 13, 2013 + +Interim bugfix release while I work on the next major release with zip64 and streaming compression/decompression support. Fixed the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com), which could cause the locate files func to not find files when this flag was specified. Also fixed a bug in mz_zip_reader_extract_to_mem_no_alloc() with user provided read buffers (thanks kymoon). I also merged lots of compiler fixes from various github repo branches and Google Code issue reports. I finally added cmake support (only tested under for Linux so far), compiled and tested with clang v3.3 and gcc 4.6 (under Linux), added defl_write_image_to_png_file_in_memory_ex() (supports Y flipping for OpenGL use, real-time compression), added a new PNG example (example6.c - Mandelbrot), and I added 64-bit file I/O support (stat64(), etc.) for glibc. + +- Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug + would only have occurred in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place() + (which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag). +- Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size +- Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries. + Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice). +- Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes +- mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed +- Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6. +- Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti +- Merged MZ_FORCEINLINE fix from hdeanclark +- Fix include before config #ifdef, thanks emil.brink +- Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can + set it to 1 for real-time compression). +- Merged in some compiler fixes from paulharris's github repro. +- Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3. +- Added example6.c, which dumps an image of the mandelbrot set to a PNG file. +- Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. +- In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled +- In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch + +### v1.14 - May 20, 2012 + +(SVN Only) Minor tweaks to get miniz.c compiling with the Tiny C Compiler, added #ifndef MINIZ_NO_TIME guards around utime.h includes. Adding mz_free() function, so the caller can free heap blocks returned by miniz using whatever heap functions it has been configured to use, MSVC specific fixes to use "safe" variants of several functions (localtime_s, fopen_s, freopen_s). + +MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include (thanks fermtect). + +Compiler specific fixes, some from fermtect. I upgraded to TDM GCC 4.6.1 and now static __forceinline is giving it fits, so I'm changing all usage of __forceinline to MZ_FORCEINLINE and forcing gcc to use __attribute__((__always_inline__)) (and MSVC to use __forceinline). Also various fixes from fermtect for MinGW32: added #include , 64-bit ftell/fseek fixes. + +### v1.13 - May 19, 2012 + +From jason@cornsyrup.org and kelwert@mtu.edu - Most importantly, fixed mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bits. Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. Other stuff: + +Eliminated a bunch of warnings when compiling with GCC 32-bit/64. Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). + +Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) + +Fix ftell() usage in a few of the examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). Fix fail logic handling in mz_zip_add_mem_to_archive_file_in_place() so it always calls mz_zip_writer_finalize_archive() and mz_zip_writer_end(), even if the file add fails. + +- From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. +- Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. +- Eliminated a bunch of warnings when compiling with GCC 32-bit/64. +- Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly +"Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). +- Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. +- Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. +- Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. +- Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) +- Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). + +### v1.12 - 4/12/12 + +More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's. +level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson for the feedback/bug report. + +### v1.11 - 5/28/11 + +Added statement from unlicense.org + +### v1.10 - 5/27/11 + +- Substantial compressor optimizations: +- Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). +- Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. +- Refactored the compression code for better readability and maintainability. +- Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large drop in throughput on some files). + +### v1.09 - 5/15/11 + +Initial stable release. + + diff --git a/thirdparty/miniz/examples/example1.c b/thirdparty/miniz/examples/example1.c new file mode 100644 index 0000000..d6e33fa --- /dev/null +++ b/thirdparty/miniz/examples/example1.c @@ -0,0 +1,105 @@ +// example1.c - Demonstrates miniz.c's compress() and uncompress() functions (same as zlib's). +// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. +#include +#include "miniz.h" +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint; + +// The string to compress. +static const char *s_pStr = "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ + "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ + "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ + "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ + "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ + "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ + "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."; + +int main(int argc, char *argv[]) +{ + uint step = 0; + int cmp_status; + uLong src_len = (uLong)strlen(s_pStr); + uLong cmp_len = compressBound(src_len); + uLong uncomp_len = src_len; + uint8 *pCmp, *pUncomp; + uint total_succeeded = 0; + (void)argc, (void)argv; + + printf("miniz.c version: %s\n", MZ_VERSION); + + do + { + // Allocate buffers to hold compressed and uncompressed data. + pCmp = (mz_uint8 *)malloc((size_t)cmp_len); + pUncomp = (mz_uint8 *)malloc((size_t)src_len); + if ((!pCmp) || (!pUncomp)) + { + printf("Out of memory!\n"); + return EXIT_FAILURE; + } + + // Compress the string. + cmp_status = compress(pCmp, &cmp_len, (const unsigned char *)s_pStr, src_len); + if (cmp_status != Z_OK) + { + printf("compress() failed!\n"); + free(pCmp); + free(pUncomp); + return EXIT_FAILURE; + } + + printf("Compressed from %u to %u bytes\n", (mz_uint32)src_len, (mz_uint32)cmp_len); + + if (step) + { + // Purposely corrupt the compressed data if fuzzy testing (this is a very crude fuzzy test). + uint n = 1 + (rand() % 3); + while (n--) + { + uint i = rand() % cmp_len; + pCmp[i] ^= (rand() & 0xFF); + } + } + + // Decompress. + cmp_status = uncompress(pUncomp, &uncomp_len, pCmp, cmp_len); + total_succeeded += (cmp_status == Z_OK); + + if (step) + { + printf("Simple fuzzy test: step %u total_succeeded: %u\n", step, total_succeeded); + } + else + { + if (cmp_status != Z_OK) + { + printf("uncompress failed!\n"); + free(pCmp); + free(pUncomp); + return EXIT_FAILURE; + } + + printf("Decompressed from %u to %u bytes\n", (mz_uint32)cmp_len, (mz_uint32)uncomp_len); + + // Ensure uncompress() returned the expected data. + if ((uncomp_len != src_len) || (memcmp(pUncomp, s_pStr, (size_t)src_len))) + { + printf("Decompression failed!\n"); + free(pCmp); + free(pUncomp); + return EXIT_FAILURE; + } + } + + free(pCmp); + free(pUncomp); + + step++; + + // Keep on fuzzy testing if there's a non-empty command line. + } while (argc >= 2); + + printf("Success.\n"); + return EXIT_SUCCESS; +} diff --git a/thirdparty/miniz/examples/example2.c b/thirdparty/miniz/examples/example2.c new file mode 100644 index 0000000..03d2409 --- /dev/null +++ b/thirdparty/miniz/examples/example2.c @@ -0,0 +1,164 @@ +// example2.c - Simple demonstration of miniz.c's ZIP archive API's. +// Note this test deletes the test archive file "__mz_example2_test__.zip" in the current directory, then creates a new one with test data. +// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. + +#if defined(__GNUC__) + // Ensure we get the 64-bit variants of the CRT's file I/O calls + #ifndef _FILE_OFFSET_BITS + #define _FILE_OFFSET_BITS 64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE 1 + #endif +#endif + +#include +#include "miniz.h" + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint; + +// The string to compress. +static const char *s_pTest_str = + "MISSION CONTROL I wouldn't worry too much about the computer. First of all, there is still a chance that he is right, despite your tests, and" \ + "if it should happen again, we suggest eliminating this possibility by allowing the unit to remain in place and seeing whether or not it" \ + "actually fails. If the computer should turn out to be wrong, the situation is still not alarming. The type of obsessional error he may be" \ + "guilty of is not unknown among the latest generation of HAL 9000 computers. It has almost always revolved around a single detail, such as" \ + "the one you have described, and it has never interfered with the integrity or reliability of the computer's performance in other areas." \ + "No one is certain of the cause of this kind of malfunctioning. It may be over-programming, but it could also be any number of reasons. In any" \ + "event, it is somewhat analogous to human neurotic behavior. Does this answer your query? Zero-five-three-Zero, MC, transmission concluded."; + +static const char *s_pComment = "This is a comment"; + +int main(int argc, char *argv[]) +{ + int i, sort_iter; + mz_bool status; + size_t uncomp_size; + mz_zip_archive zip_archive; + void *p; + const int N = 50; + char data[2048]; + char archive_filename[64]; + static const char *s_Test_archive_filename = "__mz_example2_test__.zip"; + + assert((strlen(s_pTest_str) + 64) < sizeof(data)); + + printf("miniz.c version: %s\n", MZ_VERSION); + + (void)argc, (void)argv; + + // Delete the test archive, so it doesn't keep growing as we run this test + remove(s_Test_archive_filename); + + // Append a bunch of text files to the test archive + for (i = (N - 1); i >= 0; --i) + { + sprintf(archive_filename, "%u.txt", i); + sprintf(data, "%u %s %u", (N - 1) - i, s_pTest_str, i); + + // Add a new file to the archive. Note this is an IN-PLACE operation, so if it fails your archive is probably hosed (its central directory may not be complete) but it should be recoverable using zip -F or -FF. So use caution with this guy. + // A more robust way to add a file to an archive would be to read it into memory, perform the operation, then write a new archive out to a temp file and then delete/rename the files. + // Or, write a new archive to disk to a temp file, then delete/rename the files. For this test this API is fine. + status = mz_zip_add_mem_to_archive_file_in_place(s_Test_archive_filename, archive_filename, data, strlen(data) + 1, s_pComment, (uint16)strlen(s_pComment), MZ_BEST_COMPRESSION); + if (!status) + { + printf("mz_zip_add_mem_to_archive_file_in_place failed!\n"); + return EXIT_FAILURE; + } + } + + // Add a directory entry for testing + status = mz_zip_add_mem_to_archive_file_in_place(s_Test_archive_filename, "directory/", NULL, 0, "no comment", (uint16)strlen("no comment"), MZ_BEST_COMPRESSION); + if (!status) + { + printf("mz_zip_add_mem_to_archive_file_in_place failed!\n"); + return EXIT_FAILURE; + } + + // Now try to open the archive. + memset(&zip_archive, 0, sizeof(zip_archive)); + + status = mz_zip_reader_init_file(&zip_archive, s_Test_archive_filename, 0); + if (!status) + { + printf("mz_zip_reader_init_file() failed!\n"); + return EXIT_FAILURE; + } + + // Get and print information about each file in the archive. + for (i = 0; i < (int)mz_zip_reader_get_num_files(&zip_archive); i++) + { + mz_zip_archive_file_stat file_stat; + if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) + { + printf("mz_zip_reader_file_stat() failed!\n"); + mz_zip_reader_end(&zip_archive); + return EXIT_FAILURE; + } + + printf("Filename: \"%s\", Comment: \"%s\", Uncompressed size: %u, Compressed size: %u, Is Dir: %u\n", file_stat.m_filename, file_stat.m_comment, (uint)file_stat.m_uncomp_size, (uint)file_stat.m_comp_size, mz_zip_reader_is_file_a_directory(&zip_archive, i)); + + if (!strcmp(file_stat.m_filename, "directory/")) + { + if (!mz_zip_reader_is_file_a_directory(&zip_archive, i)) + { + printf("mz_zip_reader_is_file_a_directory() didn't return the expected results!\n"); + mz_zip_reader_end(&zip_archive); + return EXIT_FAILURE; + } + } + } + + // Close the archive, freeing any resources it was using + mz_zip_reader_end(&zip_archive); + + // Now verify the compressed data + for (sort_iter = 0; sort_iter < 2; sort_iter++) + { + memset(&zip_archive, 0, sizeof(zip_archive)); + status = mz_zip_reader_init_file(&zip_archive, s_Test_archive_filename, sort_iter ? MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY : 0); + if (!status) + { + printf("mz_zip_reader_init_file() failed!\n"); + return EXIT_FAILURE; + } + + for (i = 0; i < N; i++) + { + sprintf(archive_filename, "%u.txt", i); + sprintf(data, "%u %s %u", (N - 1) - i, s_pTest_str, i); + + // Try to extract all the files to the heap. + p = mz_zip_reader_extract_file_to_heap(&zip_archive, archive_filename, &uncomp_size, 0); + if (!p) + { + printf("mz_zip_reader_extract_file_to_heap() failed!\n"); + mz_zip_reader_end(&zip_archive); + return EXIT_FAILURE; + } + + // Make sure the extraction really succeeded. + if ((uncomp_size != (strlen(data) + 1)) || (memcmp(p, data, strlen(data)))) + { + printf("mz_zip_reader_extract_file_to_heap() failed to extract the proper data\n"); + mz_free(p); + mz_zip_reader_end(&zip_archive); + return EXIT_FAILURE; + } + + printf("Successfully extracted file \"%s\", size %u\n", archive_filename, (uint)uncomp_size); + printf("File data: \"%s\"\n", (const char *)p); + + // We're done. + mz_free(p); + } + + // Close the archive, freeing any resources it was using + mz_zip_reader_end(&zip_archive); + } + + printf("Success.\n"); + return EXIT_SUCCESS; +} diff --git a/thirdparty/miniz/examples/example3.c b/thirdparty/miniz/examples/example3.c new file mode 100644 index 0000000..a2c6846 --- /dev/null +++ b/thirdparty/miniz/examples/example3.c @@ -0,0 +1,269 @@ +// example3.c - Demonstrates how to use miniz.c's deflate() and inflate() functions for simple file compression. +// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. +// For simplicity, this example is limited to files smaller than 4GB, but this is not a limitation of miniz.c. +#include +#include +#include "miniz.h" + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint; + +#define my_max(a,b) (((a) > (b)) ? (a) : (b)) +#define my_min(a,b) (((a) < (b)) ? (a) : (b)) + +#define BUF_SIZE (1024 * 1024) +static uint8 s_inbuf[BUF_SIZE]; +static uint8 s_outbuf[BUF_SIZE]; + +int main(int argc, char *argv[]) +{ + const char *pMode; + FILE *pInfile, *pOutfile; + uint infile_size; + int level = Z_BEST_COMPRESSION; + z_stream stream; + int p = 1; + const char *pSrc_filename; + const char *pDst_filename; + long file_loc; + + printf("miniz.c version: %s\n", MZ_VERSION); + + if (argc < 4) + { + printf("Usage: example3 [options] [mode:c or d] infile outfile\n"); + printf("\nModes:\n"); + printf("c - Compresses file infile to a zlib stream in file outfile\n"); + printf("d - Decompress zlib stream in file infile to file outfile\n"); + printf("\nOptions:\n"); + printf("-l[0-10] - Compression level, higher values are slower.\n"); + return EXIT_FAILURE; + } + + while ((p < argc) && (argv[p][0] == '-')) + { + switch (argv[p][1]) + { + case 'l': + { + level = atoi(&argv[1][2]); + if ((level < 0) || (level > 10)) + { + printf("Invalid level!\n"); + return EXIT_FAILURE; + } + break; + } + default: + { + printf("Invalid option: %s\n", argv[p]); + return EXIT_FAILURE; + } + } + p++; + } + + if ((argc - p) < 3) + { + printf("Must specify mode, input filename, and output filename after options!\n"); + return EXIT_FAILURE; + } + else if ((argc - p) > 3) + { + printf("Too many filenames!\n"); + return EXIT_FAILURE; + } + + pMode = argv[p++]; + if (!strchr("cCdD", pMode[0])) + { + printf("Invalid mode!\n"); + return EXIT_FAILURE; + } + + pSrc_filename = argv[p++]; + pDst_filename = argv[p++]; + + printf("Mode: %c, Level: %u\nInput File: \"%s\"\nOutput File: \"%s\"\n", pMode[0], level, pSrc_filename, pDst_filename); + + // Open input file. + pInfile = fopen(pSrc_filename, "rb"); + if (!pInfile) + { + printf("Failed opening input file!\n"); + return EXIT_FAILURE; + } + + // Determine input file's size. + fseek(pInfile, 0, SEEK_END); + file_loc = ftell(pInfile); + fseek(pInfile, 0, SEEK_SET); + + if ((file_loc < 0) || ((mz_uint64)file_loc > INT_MAX)) + { + // This is not a limitation of miniz or tinfl, but this example. + printf("File is too large to be processed by this example.\n"); + return EXIT_FAILURE; + } + + infile_size = (uint)file_loc; + + // Open output file. + pOutfile = fopen(pDst_filename, "wb"); + if (!pOutfile) + { + printf("Failed opening output file!\n"); + return EXIT_FAILURE; + } + + printf("Input file size: %u\n", infile_size); + + // Init the z_stream + memset(&stream, 0, sizeof(stream)); + stream.next_in = s_inbuf; + stream.avail_in = 0; + stream.next_out = s_outbuf; + stream.avail_out = BUF_SIZE; + + if ((pMode[0] == 'c') || (pMode[0] == 'C')) + { + // Compression. + uint infile_remaining = infile_size; + + if (deflateInit(&stream, level) != Z_OK) + { + printf("deflateInit() failed!\n"); + return EXIT_FAILURE; + } + + for ( ; ; ) + { + int status; + if (!stream.avail_in) + { + // Input buffer is empty, so read more bytes from input file. + uint n = my_min(BUF_SIZE, infile_remaining); + + if (fread(s_inbuf, 1, n, pInfile) != n) + { + printf("Failed reading from input file!\n"); + return EXIT_FAILURE; + } + + stream.next_in = s_inbuf; + stream.avail_in = n; + + infile_remaining -= n; + //printf("Input bytes remaining: %u\n", infile_remaining); + } + + status = deflate(&stream, infile_remaining ? Z_NO_FLUSH : Z_FINISH); + + if ((status == Z_STREAM_END) || (!stream.avail_out)) + { + // Output buffer is full, or compression is done, so write buffer to output file. + uint n = BUF_SIZE - stream.avail_out; + if (fwrite(s_outbuf, 1, n, pOutfile) != n) + { + printf("Failed writing to output file!\n"); + return EXIT_FAILURE; + } + stream.next_out = s_outbuf; + stream.avail_out = BUF_SIZE; + } + + if (status == Z_STREAM_END) + break; + else if (status != Z_OK) + { + printf("deflate() failed with status %i!\n", status); + return EXIT_FAILURE; + } + } + + if (deflateEnd(&stream) != Z_OK) + { + printf("deflateEnd() failed!\n"); + return EXIT_FAILURE; + } + } + else if ((pMode[0] == 'd') || (pMode[0] == 'D')) + { + // Decompression. + uint infile_remaining = infile_size; + + if (inflateInit(&stream)) + { + printf("inflateInit() failed!\n"); + return EXIT_FAILURE; + } + + for ( ; ; ) + { + int status; + if (!stream.avail_in) + { + // Input buffer is empty, so read more bytes from input file. + uint n = my_min(BUF_SIZE, infile_remaining); + + if (fread(s_inbuf, 1, n, pInfile) != n) + { + printf("Failed reading from input file!\n"); + return EXIT_FAILURE; + } + + stream.next_in = s_inbuf; + stream.avail_in = n; + + infile_remaining -= n; + } + + status = inflate(&stream, Z_SYNC_FLUSH); + + if ((status == Z_STREAM_END) || (!stream.avail_out)) + { + // Output buffer is full, or decompression is done, so write buffer to output file. + uint n = BUF_SIZE - stream.avail_out; + if (fwrite(s_outbuf, 1, n, pOutfile) != n) + { + printf("Failed writing to output file!\n"); + return EXIT_FAILURE; + } + stream.next_out = s_outbuf; + stream.avail_out = BUF_SIZE; + } + + if (status == Z_STREAM_END) + break; + else if (status != Z_OK) + { + printf("inflate() failed with status %i!\n", status); + return EXIT_FAILURE; + } + } + + if (inflateEnd(&stream) != Z_OK) + { + printf("inflateEnd() failed!\n"); + return EXIT_FAILURE; + } + } + else + { + printf("Invalid mode!\n"); + return EXIT_FAILURE; + } + + fclose(pInfile); + if (EOF == fclose(pOutfile)) + { + printf("Failed writing to output file!\n"); + return EXIT_FAILURE; + } + + printf("Total input bytes: %u\n", (mz_uint32)stream.total_in); + printf("Total output bytes: %u\n", (mz_uint32)stream.total_out); + printf("Success.\n"); + return EXIT_SUCCESS; +} diff --git a/thirdparty/miniz/examples/example4.c b/thirdparty/miniz/examples/example4.c new file mode 100644 index 0000000..ac49e7f --- /dev/null +++ b/thirdparty/miniz/examples/example4.c @@ -0,0 +1,102 @@ +// example4.c - Uses tinfl.c to decompress a zlib stream in memory to an output file +// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. +#include "miniz.h" +#include +#include + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint; + +#define my_max(a,b) (((a) > (b)) ? (a) : (b)) +#define my_min(a,b) (((a) < (b)) ? (a) : (b)) + +static int tinfl_put_buf_func(const void* pBuf, int len, void *pUser) +{ + return len == (int)fwrite(pBuf, 1, len, (FILE*)pUser); +} + +int main(int argc, char *argv[]) +{ + int status; + FILE *pInfile, *pOutfile; + uint infile_size, outfile_size; + size_t in_buf_size; + uint8 *pCmp_data; + long file_loc; + + if (argc != 3) + { + printf("Usage: example4 infile outfile\n"); + printf("Decompresses zlib stream in file infile to file outfile.\n"); + printf("Input file must be able to fit entirely in memory.\n"); + printf("example3 can be used to create compressed zlib streams.\n"); + return EXIT_FAILURE; + } + + // Open input file. + pInfile = fopen(argv[1], "rb"); + if (!pInfile) + { + printf("Failed opening input file!\n"); + return EXIT_FAILURE; + } + + // Determine input file's size. + fseek(pInfile, 0, SEEK_END); + file_loc = ftell(pInfile); + fseek(pInfile, 0, SEEK_SET); + + if ((file_loc < 0) || ((mz_uint64)file_loc > INT_MAX)) + { + // This is not a limitation of miniz or tinfl, but this example. + printf("File is too large to be processed by this example.\n"); + return EXIT_FAILURE; + } + + infile_size = (uint)file_loc; + + pCmp_data = (uint8 *)malloc(infile_size); + if (!pCmp_data) + { + printf("Out of memory!\n"); + return EXIT_FAILURE; + } + if (fread(pCmp_data, 1, infile_size, pInfile) != infile_size) + { + printf("Failed reading input file!\n"); + return EXIT_FAILURE; + } + + // Open output file. + pOutfile = fopen(argv[2], "wb"); + if (!pOutfile) + { + printf("Failed opening output file!\n"); + return EXIT_FAILURE; + } + + printf("Input file size: %u\n", infile_size); + + in_buf_size = infile_size; + status = tinfl_decompress_mem_to_callback(pCmp_data, &in_buf_size, tinfl_put_buf_func, pOutfile, TINFL_FLAG_PARSE_ZLIB_HEADER); + if (!status) + { + printf("tinfl_decompress_mem_to_callback() failed with status %i!\n", status); + return EXIT_FAILURE; + } + + outfile_size = ftell(pOutfile); + + fclose(pInfile); + if (EOF == fclose(pOutfile)) + { + printf("Failed writing to output file!\n"); + return EXIT_FAILURE; + } + + printf("Total input bytes: %u\n", (uint)in_buf_size); + printf("Total output bytes: %u\n", outfile_size); + printf("Success.\n"); + return EXIT_SUCCESS; +} diff --git a/thirdparty/miniz/examples/example5.c b/thirdparty/miniz/examples/example5.c new file mode 100644 index 0000000..2e47199 --- /dev/null +++ b/thirdparty/miniz/examples/example5.c @@ -0,0 +1,327 @@ +// example5.c - Demonstrates how to use miniz.c's low-level tdefl_compress() and tinfl_inflate() API's for simple file to file compression/decompression. +// The low-level API's are the fastest, make no use of dynamic memory allocation, and are the most flexible functions exposed by miniz.c. +// Public domain, April 11 2012, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. +// For simplicity, this example is limited to files smaller than 4GB, but this is not a limitation of miniz.c. + +// Purposely disable a whole bunch of stuff this low-level example doesn't use. +#define MINIZ_NO_STDIO +#define MINIZ_NO_ARCHIVE_APIS +#define MINIZ_NO_TIME +#define MINIZ_NO_ZLIB_APIS +#define MINIZ_NO_MALLOC +#include "miniz.h" + +// Now include stdio.h because this test uses fopen(), etc. (but we still don't want miniz.c's stdio stuff, for testing). +#include +#include + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint; + +#define my_max(a,b) (((a) > (b)) ? (a) : (b)) +#define my_min(a,b) (((a) < (b)) ? (a) : (b)) + +// IN_BUF_SIZE is the size of the file read buffer. +// IN_BUF_SIZE must be >= 1 +#define IN_BUF_SIZE (1024*512) +static uint8 s_inbuf[IN_BUF_SIZE]; + +// COMP_OUT_BUF_SIZE is the size of the output buffer used during compression. +// COMP_OUT_BUF_SIZE must be >= 1 and <= OUT_BUF_SIZE +#define COMP_OUT_BUF_SIZE (1024*512) + +// OUT_BUF_SIZE is the size of the output buffer used during decompression. +// OUT_BUF_SIZE must be a power of 2 >= TINFL_LZ_DICT_SIZE (because the low-level decompressor not only writes, but reads from the output buffer as it decompresses) +//#define OUT_BUF_SIZE (TINFL_LZ_DICT_SIZE) +#define OUT_BUF_SIZE (1024*512) +static uint8 s_outbuf[OUT_BUF_SIZE]; + +// tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k). +// This example makes it a global vs. putting it on the stack, of course in real-world usage you'll probably malloc() or new it. +tdefl_compressor g_deflator; + +int main(int argc, char *argv[]) +{ + const char *pMode; + FILE *pInfile, *pOutfile; + uint infile_size; + int level = 9; + int p = 1; + const char *pSrc_filename; + const char *pDst_filename; + const void *next_in = s_inbuf; + size_t avail_in = 0; + void *next_out = s_outbuf; + size_t avail_out = OUT_BUF_SIZE; + size_t total_in = 0, total_out = 0; + long file_loc; + + assert(COMP_OUT_BUF_SIZE <= OUT_BUF_SIZE); + + printf("miniz.c example5 (demonstrates tinfl/tdefl)\n"); + + if (argc < 4) + { + printf("File to file compression/decompression using the low-level tinfl/tdefl API's.\n"); + printf("Usage: example5 [options] [mode:c or d] infile outfile\n"); + printf("\nModes:\n"); + printf("c - Compresses file infile to a zlib stream in file outfile\n"); + printf("d - Decompress zlib stream in file infile to file outfile\n"); + printf("\nOptions:\n"); + printf("-l[0-10] - Compression level, higher values are slower, 0 is none.\n"); + return EXIT_FAILURE; + } + + while ((p < argc) && (argv[p][0] == '-')) + { + switch (argv[p][1]) + { + case 'l': + { + level = atoi(&argv[1][2]); + if ((level < 0) || (level > 10)) + { + printf("Invalid level!\n"); + return EXIT_FAILURE; + } + break; + } + default: + { + printf("Invalid option: %s\n", argv[p]); + return EXIT_FAILURE; + } + } + p++; + } + + if ((argc - p) < 3) + { + printf("Must specify mode, input filename, and output filename after options!\n"); + return EXIT_FAILURE; + } + else if ((argc - p) > 3) + { + printf("Too many filenames!\n"); + return EXIT_FAILURE; + } + + pMode = argv[p++]; + if (!strchr("cCdD", pMode[0])) + { + printf("Invalid mode!\n"); + return EXIT_FAILURE; + } + + pSrc_filename = argv[p++]; + pDst_filename = argv[p++]; + + printf("Mode: %c, Level: %u\nInput File: \"%s\"\nOutput File: \"%s\"\n", pMode[0], level, pSrc_filename, pDst_filename); + + // Open input file. + pInfile = fopen(pSrc_filename, "rb"); + if (!pInfile) + { + printf("Failed opening input file!\n"); + return EXIT_FAILURE; + } + + // Determine input file's size. + fseek(pInfile, 0, SEEK_END); + file_loc = ftell(pInfile); + fseek(pInfile, 0, SEEK_SET); + + if ((file_loc < 0) || ((mz_uint64)file_loc > INT_MAX)) + { + // This is not a limitation of miniz or tinfl, but this example. + printf("File is too large to be processed by this example.\n"); + return EXIT_FAILURE; + } + + infile_size = (uint)file_loc; + + // Open output file. + pOutfile = fopen(pDst_filename, "wb"); + if (!pOutfile) + { + printf("Failed opening output file!\n"); + return EXIT_FAILURE; + } + + printf("Input file size: %u\n", infile_size); + + if ((pMode[0] == 'c') || (pMode[0] == 'C')) + { + // The number of dictionary probes to use at each compression level (0-10). 0=implies fastest/minimal possible probing. + static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + + tdefl_status status; + uint infile_remaining = infile_size; + + // create tdefl() compatible flags (we have to compose the low-level flags ourselves, or use tdefl_create_comp_flags_from_zip_params() but that means MINIZ_NO_ZLIB_APIS can't be defined). + mz_uint comp_flags = TDEFL_WRITE_ZLIB_HEADER | s_tdefl_num_probes[MZ_MIN(10, level)] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (!level) + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + + // Initialize the low-level compressor. + status = tdefl_init(&g_deflator, NULL, NULL, comp_flags); + if (status != TDEFL_STATUS_OKAY) + { + printf("tdefl_init() failed!\n"); + return EXIT_FAILURE; + } + + avail_out = COMP_OUT_BUF_SIZE; + + // Compression. + for ( ; ; ) + { + size_t in_bytes, out_bytes; + + if (!avail_in) + { + // Input buffer is empty, so read more bytes from input file. + uint n = my_min(IN_BUF_SIZE, infile_remaining); + + if (fread(s_inbuf, 1, n, pInfile) != n) + { + printf("Failed reading from input file!\n"); + return EXIT_FAILURE; + } + + next_in = s_inbuf; + avail_in = n; + + infile_remaining -= n; + //printf("Input bytes remaining: %u\n", infile_remaining); + } + + in_bytes = avail_in; + out_bytes = avail_out; + // Compress as much of the input as possible (or all of it) to the output buffer. + status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, infile_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); + + next_in = (const char *)next_in + in_bytes; + avail_in -= in_bytes; + total_in += in_bytes; + + next_out = (char *)next_out + out_bytes; + avail_out -= out_bytes; + total_out += out_bytes; + + if ((status != TDEFL_STATUS_OKAY) || (!avail_out)) + { + // Output buffer is full, or compression is done or failed, so write buffer to output file. + uint n = COMP_OUT_BUF_SIZE - (uint)avail_out; + if (fwrite(s_outbuf, 1, n, pOutfile) != n) + { + printf("Failed writing to output file!\n"); + return EXIT_FAILURE; + } + next_out = s_outbuf; + avail_out = COMP_OUT_BUF_SIZE; + } + + if (status == TDEFL_STATUS_DONE) + { + // Compression completed successfully. + break; + } + else if (status != TDEFL_STATUS_OKAY) + { + // Compression somehow failed. + printf("tdefl_compress() failed with status %i!\n", status); + return EXIT_FAILURE; + } + } + } + else if ((pMode[0] == 'd') || (pMode[0] == 'D')) + { + // Decompression. + uint infile_remaining = infile_size; + + tinfl_decompressor inflator; + tinfl_init(&inflator); + + for ( ; ; ) + { + size_t in_bytes, out_bytes; + tinfl_status status; + if (!avail_in) + { + // Input buffer is empty, so read more bytes from input file. + uint n = my_min(IN_BUF_SIZE, infile_remaining); + + if (fread(s_inbuf, 1, n, pInfile) != n) + { + printf("Failed reading from input file!\n"); + return EXIT_FAILURE; + } + + next_in = s_inbuf; + avail_in = n; + + infile_remaining -= n; + } + + in_bytes = avail_in; + out_bytes = avail_out; + status = tinfl_decompress(&inflator, (const mz_uint8 *)next_in, &in_bytes, s_outbuf, (mz_uint8 *)next_out, &out_bytes, (infile_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0) | TINFL_FLAG_PARSE_ZLIB_HEADER); + + avail_in -= in_bytes; + next_in = (const mz_uint8 *)next_in + in_bytes; + total_in += in_bytes; + + avail_out -= out_bytes; + next_out = (mz_uint8 *)next_out + out_bytes; + total_out += out_bytes; + + if ((status <= TINFL_STATUS_DONE) || (!avail_out)) + { + // Output buffer is full, or decompression is done, so write buffer to output file. + uint n = OUT_BUF_SIZE - (uint)avail_out; + if (fwrite(s_outbuf, 1, n, pOutfile) != n) + { + printf("Failed writing to output file!\n"); + return EXIT_FAILURE; + } + next_out = s_outbuf; + avail_out = OUT_BUF_SIZE; + } + + // If status is <= TINFL_STATUS_DONE then either decompression is done or something went wrong. + if (status <= TINFL_STATUS_DONE) + { + if (status == TINFL_STATUS_DONE) + { + // Decompression completed successfully. + break; + } + else + { + // Decompression failed. + printf("tinfl_decompress() failed with status %i!\n", status); + return EXIT_FAILURE; + } + } + } + } + else + { + printf("Invalid mode!\n"); + return EXIT_FAILURE; + } + + fclose(pInfile); + if (EOF == fclose(pOutfile)) + { + printf("Failed writing to output file!\n"); + return EXIT_FAILURE; + } + + printf("Total input bytes: %u\n", (mz_uint32)total_in); + printf("Total output bytes: %u\n", (mz_uint32)total_out); + printf("Success.\n"); + return EXIT_SUCCESS; +} diff --git a/thirdparty/miniz/examples/example6.c b/thirdparty/miniz/examples/example6.c new file mode 100644 index 0000000..5eeb962 --- /dev/null +++ b/thirdparty/miniz/examples/example6.c @@ -0,0 +1,166 @@ +// example6.c - Demonstrates how to miniz's PNG writer func +// Public domain, April 11 2012, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. +// Mandlebrot set code from http://rosettacode.org/wiki/Mandelbrot_set#C +// Must link this example against libm on Linux. + +// Purposely disable a whole bunch of stuff this low-level example doesn't use. +#define MINIZ_NO_STDIO +#define MINIZ_NO_TIME +#define MINIZ_NO_ZLIB_APIS +#include "miniz.h" + +// Now include stdio.h because this test uses fopen(), etc. (but we still don't want miniz.c's stdio stuff, for testing). +#include +#include +#include + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint; + +typedef struct +{ + uint8 r, g, b; +} rgb_t; + +static void hsv_to_rgb(int hue, int min, int max, rgb_t *p) +{ + const int invert = 0; + const int saturation = 1; + const int color_rotate = 0; + + if (min == max) max = min + 1; + if (invert) hue = max - (hue - min); + if (!saturation) { + p->r = p->g = p->b = 255 * (max - hue) / (max - min); + return; + } else { + const double h_dbl = fmod(color_rotate + 1e-4 + 4.0 * (hue - min) / (max - min), 6); + const double c_dbl = 255 * saturation; + const double X_dbl = c_dbl * (1 - fabs(fmod(h_dbl, 2) - 1)); + const int h = (int)h_dbl; + const int c = (int)c_dbl; + const int X = (int)X_dbl; + + p->r = p->g = p->b = 0; + + switch(h) { + case 0: p->r = c; p->g = X; return; + case 1: p->r = X; p->g = c; return; + case 2: p->g = c; p->b = X; return; + case 3: p->g = X; p->b = c; return; + case 4: p->r = X; p->b = c; return; + default:p->r = c; p->b = X; + } + } +} + +int main(int argc, char *argv[]) +{ + // Image resolution + const int iXmax = 4096; + const int iYmax = 4096; + + // Output filename + static const char *pFilename = "mandelbrot.png"; + + int iX, iY; + const double CxMin = -2.5; + const double CxMax = 1.5; + const double CyMin = -2.0; + const double CyMax = 2.0; + + double PixelWidth = (CxMax - CxMin) / iXmax; + double PixelHeight = (CyMax - CyMin) / iYmax; + + // Z=Zx+Zy*i ; Z0 = 0 + double Zx, Zy; + double Zx2, Zy2; // Zx2=Zx*Zx; Zy2=Zy*Zy + + int Iteration; + const int IterationMax = 200; + + // bail-out value , radius of circle + const double EscapeRadius = 2; + double ER2=EscapeRadius * EscapeRadius; + + uint8 *pImage = (uint8 *)malloc(iXmax * 3 * iYmax); + + // world ( double) coordinate = parameter plane + double Cx,Cy; + + int MinIter = 9999, MaxIter = 0; + + (void)argc, (void)argv; + + for(iY = 0; iY < iYmax; iY++) + { + Cy = CyMin + iY * PixelHeight; + if (fabs(Cy) < PixelHeight/2) + Cy = 0.0; // Main antenna + + for(iX = 0; iX < iXmax; iX++) + { + uint8 *color = pImage + (iX * 3) + (iY * iXmax * 3); + + Cx = CxMin + iX * PixelWidth; + + // initial value of orbit = critical point Z= 0 + Zx = 0.0; + Zy = 0.0; + Zx2 = Zx * Zx; + Zy2 = Zy * Zy; + + for (Iteration=0;Iteration> 8; + color[2] = 0; + + if (Iteration < MinIter) + MinIter = Iteration; + if (Iteration > MaxIter) + MaxIter = Iteration; + } + } + + for(iY = 0; iY < iYmax; iY++) + { + for(iX = 0; iX < iXmax; iX++) + { + uint8 *color = (uint8 *)(pImage + (iX * 3) + (iY * iXmax * 3)); + + uint Iterations = color[0] | (color[1] << 8U); + + hsv_to_rgb((int)Iterations, MinIter, MaxIter, (rgb_t *)color); + } + } + + // Now write the PNG image. + { + size_t png_data_size = 0; + void *pPNG_data = tdefl_write_image_to_png_file_in_memory_ex(pImage, iXmax, iYmax, 3, &png_data_size, 6, MZ_FALSE); + if (!pPNG_data) + fprintf(stderr, "tdefl_write_image_to_png_file_in_memory_ex() failed!\n"); + else + { + FILE *pFile = fopen(pFilename, "wb"); + fwrite(pPNG_data, 1, png_data_size, pFile); + fclose(pFile); + printf("Wrote %s\n", pFilename); + } + + // mz_free() is by default just an alias to free() internally, but if you've overridden miniz's allocation funcs you'll probably need to call mz_free(). + mz_free(pPNG_data); + } + + free(pImage); + + return EXIT_SUCCESS; +} diff --git a/thirdparty/miniz/miniz.c b/thirdparty/miniz/miniz.c new file mode 100644 index 0000000..53ab38b --- /dev/null +++ b/thirdparty/miniz/miniz.c @@ -0,0 +1,7835 @@ +#include "miniz.h" +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API's */ + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) +{ + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); + size_t block_len = buf_len % 5552; + if (!ptr) + return MZ_ADLER32_INIT; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + return (s2 << 16) + s1; +} + +/* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */ +#if 0 + mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) + { + static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + mz_uint32 crcu32 = (mz_uint32)crc; + if (!ptr) + return MZ_CRC32_INIT; + crcu32 = ~crcu32; + while (buf_len--) + { + mz_uint8 b = *ptr++; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; + } + return ~crcu32; + } +#elif defined(USE_EXTERNAL_MZCRC) +/* If USE_EXTERNAL_CRC is defined, an external module will export the + * mz_crc32() symbol for us to use, e.g. an SSE-accelerated version. + * Depending on the impl, it may be necessary to ~ the input/output crc values. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len); +#else +/* Faster, but larger CPU cache footprint. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +{ + static const mz_uint32 s_crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, + 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, + 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, + 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, + 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, + 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, + 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, + 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, + 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, + 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, + 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, + 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, + 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, + 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, + 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, + 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, + 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, + 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, + 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, + 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, + 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, + 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, + 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, + 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, + 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; + const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; + + while (buf_len >= 4) + { + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; + pByte_buf += 4; + buf_len -= 4; + } + + while (buf_len) + { + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + ++pByte_buf; + --buf_len; + } + + return ~crc32; +} +#endif + +void mz_free(void *p) +{ + MZ_FREE(p); +} + +MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) +{ + (void)opaque, (void)items, (void)size; + return MZ_MALLOC(items * size); +} +MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address) +{ + (void)opaque, (void)address; + MZ_FREE(address); +} +MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) +{ + (void)opaque, (void)address, (void)items, (void)size; + return MZ_REALLOC(address, items * size); +} + +const char *mz_version(void) +{ + return MZ_VERSION; +} + +#ifndef MINIZ_NO_ZLIB_APIS + +#ifndef MINIZ_NO_DEFLATE_APIS + +int mz_deflateInit(mz_streamp pStream, int level) +{ + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); +} + +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) +{ + tdefl_compressor *pComp; + mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) + return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) + { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; +} + +int mz_deflateReset(mz_streamp pStream) +{ + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) + return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); + return MZ_OK; +} + +int mz_deflate(mz_streamp pStream, int flush) +{ + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) + return MZ_STREAM_ERROR; + if (!pStream->avail_out) + return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; + orig_total_out = pStream->total_out; + for (;;) + { + tdefl_status defl_status; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) + { + mz_status = MZ_STREAM_ERROR; + break; + } + else if (defl_status == TDEFL_STATUS_DONE) + { + mz_status = MZ_STREAM_END; + break; + } + else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) + { + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; /* Can't make forward progress without some input. + */ + } + } + return mz_status; +} + +int mz_deflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) +{ + (void)pStream; + /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */ + return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +} + +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) +{ + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((mz_uint64)(source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) + return status; + + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } + + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); +} + +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); +} + +mz_ulong mz_compressBound(mz_ulong source_len) +{ + return mz_deflateBound(NULL, source_len); +} + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS + +typedef struct +{ + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; + int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +int mz_inflateInit2(mz_streamp pStream, int window_bits) +{ + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); + if (!pDecomp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +int mz_inflateInit(mz_streamp pStream) +{ + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); +} + +int mz_inflateReset(mz_streamp pStream) +{ + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + + pDecomp = (inflate_state *)pStream->state; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + /* pDecomp->m_window_bits = window_bits */; + + return MZ_OK; +} + +int mz_inflate(mz_streamp pStream, int flush) +{ + inflate_state *pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) + return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + + pState = (inflate_state *)pStream->state; + if (pState->m_window_bits > 0) + decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; + pState->m_first_call = 0; + if (pState->m_last_status < 0) + return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) + { + /* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */ + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) + { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + /* flush != MZ_FINISH then we must assume there's more input. */ + if (flush != MZ_FINISH) + decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) + { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; + } + + for (;;) + { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */ + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */ + else if (flush == MZ_FINISH) + { + /* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */ + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */ + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } + else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) + break; + } + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} + +int mz_inflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} +int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len) +{ + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((mz_uint64)(*pSource_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)*pSource_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; + + status = mz_inflate(&stream, MZ_FINISH); + *pSource_len = *pSource_len - stream.avail_in; + if (status != MZ_STREAM_END) + { + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; + } + *pDest_len = stream.total_out; + + return mz_inflateEnd(&stream); +} + +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_uncompress2(pDest, pDest_len, pSource, &source_len); +} + +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +const char *mz_error(int err) +{ + static struct + { + int m_err; + const char *m_pDesc; + } s_error_descs[] = + { + { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } + }; + mz_uint i; + for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) + if (s_error_descs[i].m_err == err) + return s_error_descs[i].m_pDesc; + return NULL; +} + +#endif /*MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + + +#ifndef MINIZ_NO_DEFLATE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Compression (independent from all decompression API's) */ + +/* Purposely making these tables static for faster init and thread safety. */ +static const mz_uint16 s_tdefl_len_sym[256] = + { + 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, + 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285 + }; + +static const mz_uint8 s_tdefl_len_extra[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0 + }; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = + { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 + }; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = + { + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7 + }; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = + { + 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = + { + 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13 + }; + +/* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */ +typedef struct +{ + mz_uint16 m_key, m_sym_index; +} tdefl_sym_freq; +static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1) +{ + mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; + tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; + MZ_CLEAR_ARR(hist); + for (i = 0; i < num_syms; i++) + { + mz_uint freq = pSyms0[i].m_key; + hist[freq & 0xFF]++; + hist[256 + ((freq >> 8) & 0xFF)]++; + } + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) + total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) + { + const mz_uint32 *pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) + { + offsets[i] = cur_ofs; + cur_ofs += pHist[i]; + } + for (i = 0; i < num_syms; i++) + pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + { + tdefl_sym_freq *t = pCur_syms; + pCur_syms = pNew_syms; + pNew_syms = t; + } + } + return pCur_syms; +} + +/* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */ +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) +{ + int root, leaf, next, avbl, used, dpth; + if (n == 0) + return; + else if (n == 1) + { + A[0].m_key = 1; + return; + } + A[0].m_key += A[1].m_key; + root = 0; + leaf = 2; + for (next = 1; next < n - 1; next++) + { + if (leaf >= n || A[root].m_key < A[leaf].m_key) + { + A[next].m_key = A[root].m_key; + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = A[leaf++].m_key; + if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) + { + A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); + } + A[n - 2].m_key = 0; + for (next = n - 3; next >= 0; next--) + A[next].m_key = A[A[next].m_key].m_key + 1; + avbl = 1; + used = dpth = 0; + root = n - 2; + next = n - 1; + while (avbl > 0) + { + while (root >= 0 && (int)A[root].m_key == dpth) + { + used++; + root--; + } + while (avbl > used) + { + A[next--].m_key = (mz_uint16)(dpth); + avbl--; + } + avbl = 2 * used; + dpth++; + used = 0; + } +} + +/* Limits canonical Huffman code table's max code size. */ +enum +{ + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 +}; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) +{ + int i; + mz_uint32 total = 0; + if (code_list_len <= 1) + return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) + pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) + total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) + { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) + if (pNum_codes[i]) + { + pNum_codes[i]--; + pNum_codes[i + 1] += 2; + break; + } + total--; + } +} + +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) +{ + int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; + mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; + MZ_CLEAR_ARR(num_codes); + if (static_table) + { + for (i = 0; i < table_len; i++) + num_codes[d->m_huff_code_sizes[table_num][i]]++; + } + else + { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + for (i = 0; i < table_len; i++) + if (pSym_count[i]) + { + syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; + syms0[num_used_syms++].m_sym_index = (mz_uint16)i; + } + + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); + tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); + + for (i = 0; i < num_used_syms; i++) + num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); + + MZ_CLEAR_ARR(d->m_huff_code_sizes[table_num]); + MZ_CLEAR_ARR(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) + d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; + for (j = 0, i = 2; i <= code_size_limit; i++) + next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) + { + mz_uint rev_code = 0, code, code_size; + if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) + continue; + code = next_code[code_size]++; + for (l = code_size; l > 0; l--, code >>= 1) + rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) \ + do \ + { \ + mz_uint bits = b; \ + mz_uint len = l; \ + MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); \ + d->m_bits_in += len; \ + while (d->m_bits_in >= 8) \ + { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ + } \ + MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() \ + { \ + if (rle_repeat_count) \ + { \ + if (rle_repeat_count < 3) \ + { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) \ + packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } \ + else \ + { \ + d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 16; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ + } \ + rle_repeat_count = 0; \ + } \ + } + +#define TDEFL_RLE_ZERO_CODE_SIZE() \ + { \ + if (rle_z_count) \ + { \ + if (rle_z_count < 3) \ + { \ + d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ + while (rle_z_count--) \ + packed_code_sizes[num_packed_code_sizes++] = 0; \ + } \ + else if (rle_z_count <= 10) \ + { \ + d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 17; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ + } \ + else \ + { \ + d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 18; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ + } \ + rle_z_count = 0; \ + } \ + } + +static const mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + +static void tdefl_start_dynamic_block(tdefl_compressor *d) +{ + int num_lit_codes, num_dist_codes, num_bit_lengths; + mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; + mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; + + d->m_huff_count[0][256] = 1; + + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) + if (d->m_huff_code_sizes[0][num_lit_codes - 1]) + break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) + if (d->m_huff_code_sizes[1][num_dist_codes - 1]) + break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; + num_packed_code_sizes = 0; + rle_z_count = 0; + rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) + { + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); + packed_code_sizes[num_packed_code_sizes++] = code_size; + } + else if (++rle_repeat_count == 6) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + } + prev_code_size = code_size; + } + if (rle_repeat_count) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); + + TDEFL_PUT_BITS(2, 2); + + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + + for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) + if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) + break; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); + TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + for (i = 0; (int)i < num_bit_lengths; i++) + TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); + + for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;) + { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; + MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) + TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); + } +} + +static void tdefl_start_static_block(tdefl_compressor *d) +{ + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; + + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) \ + { \ + bit_buffer |= (((mz_uint64)(b)) << bits_in); \ + bits_in += (l); \ + } + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + + if (flags & 1) + { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0]; + mz_uint match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + /* This sequence coaxes MSVC into using cmov's vs. jmp's. */ + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + } + + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; + + memcpy(pOutput_buf, &bit_buffer, sizeof(mz_uint64)); + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } + +#undef TDEFL_PUT_BITS_FAST + + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; + + while (bits_in) + { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) + { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + if (match_dist < 512) + { + sym = s_tdefl_small_dist_sym[match_dist]; + num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } + else + { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; + num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS */ + +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) +{ + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} + +static const mz_uint s_tdefl_num_probes[11]; + +static int tdefl_flush_block(tdefl_compressor *d, int flush) +{ + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; + mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; + + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; + + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; + + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); + + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) + { + const mz_uint8 cmf = 0x78; + mz_uint8 flg, flevel = 3; + mz_uint header, i, mz_un = sizeof(s_tdefl_num_probes) / sizeof(mz_uint); + + /* Determine compression level by reversing the process in tdefl_create_comp_flags_from_zip_params() */ + for (i = 0; i < mz_un; i++) + if (s_tdefl_num_probes[i] == (d->m_flags & 0xFFF)) break; + + if (i < 2) + flevel = 0; + else if (i < 6) + flevel = 1; + else if (i == 6) + flevel = 2; + + header = cmf << 8 | (flevel << 6); + header += 31 - (header % 31); + flg = header & 0xFF; + + TDEFL_PUT_BITS(cmf, 8); + TDEFL_PUT_BITS(flg, 8); + } + + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); + + pSaved_output_buf = d->m_pOutput_buf; + saved_bit_buf = d->m_bit_buffer; + saved_bits_in = d->m_bits_in; + + if (!use_raw_block) + comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); + + /* If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. */ + if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) + { + mz_uint i; + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) + { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) + { + TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + } + } + /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */ + else if (!comp_block_succeeded) + { + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } + + if (flush) + { + if (flush == TDEFL_FINISH) + { + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) + { + mz_uint i, a = d->m_adler32; + for (i = 0; i < 4; i++) + { + TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); + a <<= 8; + } + } + } + else + { + mz_uint i, z = 0; + TDEFL_PUT_BITS(0, 3); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, z ^= 0xFFFF) + { + TDEFL_PUT_BITS(z & 0xFFFF, 16); + } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; + d->m_total_lz_bytes = 0; + d->m_block_index++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) + { + if (d->m_pPut_buf_func) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } + else if (pOutput_buf_start == d->m_output_buf) + { + int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) + { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } + else + { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; +} + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +static mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +#else +#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p) +#define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p) +#endif +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s); + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + q = (const mz_uint16 *)(d->m_dict + probe_pos); + if (TDEFL_READ_UNALIGNED_WORD2(q) != s01) + continue; + p = s; + probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + if (!probe_len) + { + *pMatch_dist = dist; + *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); + break; + } + else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) + break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } +} +#else +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + p = s; + q = d->m_dict + probe_pos; + for (probe_len = 0; probe_len < max_match_len; probe_len++) + if (*p++ != *q++) + break; + if (probe_len > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = probe_len) == max_match_len) + return; + c0 = d->m_dict[pos + match_len]; + c1 = d->m_dict[pos + match_len - 1]; + } + } +} +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static mz_uint32 TDEFL_READ_UNALIGNED_WORD32(const mz_uint8* p) +{ + mz_uint32 ret; + memcpy(&ret, p, sizeof(mz_uint32)); + return ret; +} +#else +#define TDEFL_READ_UNALIGNED_WORD32(p) *(const mz_uint32 *)(p) +#endif +static mz_bool tdefl_compress_fast(tdefl_compressor *d) +{ + /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */ + mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) + { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; + + while (num_bytes_to_process) + { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; + } + + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) + break; + + while (lookahead_size >= 4) + { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8 *pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = TDEFL_READ_UNALIGNED_WORD32(pCur_dict) & 0xFFFFFF; + mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((TDEFL_READ_UNALIGNED_WORD32(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) + { + const mz_uint16 *p = (const mz_uint16 *)pCur_dict; + const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); + mz_uint32 probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); + if (!probe_len) + cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) + { + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + else + { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + + cur_match_dist--; + + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); +#ifdef MINIZ_UNALIGNED_USE_MEMCPY + memcpy(&pLZ_code_buf[1], &cur_match_dist, sizeof(cur_match_dist)); +#else + *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; +#endif + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; + } + } + else + { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + + while (lookahead_size) + { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + } + + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + return MZ_TRUE; +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + +static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) +{ + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + d->m_huff_count[0][lit]++; +} + +static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) +{ + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); + d->m_pLZ_code_buf += 3; + + *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; +} + +static mz_bool tdefl_compress_normal(tdefl_compressor *d) +{ + const mz_uint8 *pSrc = d->m_pSrc; + size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) + { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */ + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) + { + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; + mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc ? pSrc + num_bytes_to_process : NULL; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) + { + mz_uint8 c = *pSrc++; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + ins_pos++; + } + } + else + { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) + { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; + + /* Simple lazy/greedy parsing state machine. */ + len_to_move = 1; + cur_match_dist = 0; + cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); + cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) + { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) + { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + cur_match_len = 0; + while (cur_match_len < d->m_lookahead_size) + { + if (d->m_dict[cur_pos + cur_match_len] != c) + break; + cur_match_len++; + } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) + cur_match_len = 0; + else + cur_match_dist = 1; + } + } + else + { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) + { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) + { + if (cur_match_len > d->m_saved_match_len) + { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[cur_pos]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + } + else + { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; + d->m_saved_match_len = 0; + } + } + else if (!cur_match_dist) + tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + /* Move the lookahead forward by len_to_move bytes. */ + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE); + /* Check if it's time to flush the current LZ codes to the internal output buffer. */ + if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) + { + int n; + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } + + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} + +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) +{ + if (d->m_pIn_buf_size) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + } + + if (d->m_pOut_buf_size) + { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } + + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) +{ + if (!d) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } + + d->m_pIn_buf = pIn_buf; + d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; + d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); + d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf)) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); + } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); + + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) + { + if (!tdefl_compress_fast(d)) + return d->m_prev_return_status; + } + else +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) + d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); + + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) + { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) + { + MZ_CLEAR_ARR(d->m_hash); + MZ_CLEAR_ARR(d->m_next); + d->m_dict_size = 0; + } + } + + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); +} + +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) +{ + MZ_ASSERT(d->m_pPut_buf_func); + return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +} + +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + d->m_pPut_buf_func = pPut_buf_func; + d->m_pPut_buf_user = pPut_buf_user; + d->m_flags = (mz_uint)(flags); + d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; + d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_ARR(d->m_hash); + d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; + d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + *d->m_pLZ_flags = 0; + d->m_num_flags_left = 8; + d->m_pOutput_buf = d->m_output_buf; + d->m_pOutput_buf_end = d->m_output_buf; + d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; + d->m_adler32 = 1; + d->m_pIn_buf = NULL; + d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; + d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; + d->m_pSrc = NULL; + d->m_src_buf_left = 0; + d->m_out_buf_ofs = 0; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_ARR(d->m_dict); + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) +{ + return d->m_prev_return_status; +} + +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) +{ + return d->m_adler32; +} + +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + tdefl_compressor *pComp; + mz_bool succeeded; + if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) + return MZ_FALSE; + pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + if (!pComp) + return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); + succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); + MZ_FREE(pComp); + return succeeded; +} + +typedef struct +{ + size_t m_size, m_capacity; + mz_uint8 *m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; + +static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) +{ + tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) + { + size_t new_capacity = p->m_capacity; + mz_uint8 *pNew_buf; + if (!p->m_expandable) + return MZ_FALSE; + do + { + new_capacity = MZ_MAX(128U, new_capacity << 1U); + } while (new_size > new_capacity); + pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); + if (!pNew_buf) + return MZ_FALSE; + p->m_pBuf = pNew_buf; + p->m_capacity = new_capacity; + } + memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); + p->m_size = new_size; + return MZ_TRUE; +} + +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) + return MZ_FALSE; + else + *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return NULL; + *pOut_len = out_buf.m_size; + return out_buf.m_pBuf; +} + +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) + return 0; + out_buf.m_pBuf = (mz_uint8 *)pOut_buf; + out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return 0; + return out_buf.m_size; +} + +static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + +/* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */ +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) +{ + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) + comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) + comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) + comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) + comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) + comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) */ +#endif + +/* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at + http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. + This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */ +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) +{ + /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */ + static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + tdefl_output_buffer out_buf; + int i, bpl = w * num_chans, y, z; + mz_uint32 c; + *pLen_out = 0; + if (!pComp) + return NULL; + MZ_CLEAR_OBJ(out_buf); + out_buf.m_expandable = MZ_TRUE; + out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); + if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) + { + MZ_FREE(pComp); + return NULL; + } + /* write dummy header */ + for (z = 41; z; --z) + tdefl_output_buffer_putter(&z, 1, &out_buf); + /* compress image data */ + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) + { + tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); + tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); + } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) + { + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + /* write real header */ + *pLen_out = out_buf.m_size - 41; + { + static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 }; + mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, + 0x0a, 0x1a, 0x0a, 0x00, 0x00, + 0x00, 0x0d, 0x49, 0x48, 0x44, + 0x52, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x44, 0x41, + 0x54 }; + pnghdr[18] = (mz_uint8)(w >> 8); + pnghdr[19] = (mz_uint8)w; + pnghdr[22] = (mz_uint8)(h >> 8); + pnghdr[23] = (mz_uint8)h; + pnghdr[25] = chans[num_chans]; + pnghdr[33] = (mz_uint8)(*pLen_out >> 24); + pnghdr[34] = (mz_uint8)(*pLen_out >> 16); + pnghdr[35] = (mz_uint8)(*pLen_out >> 8); + pnghdr[36] = (mz_uint8)*pLen_out; + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); + for (i = 0; i < 4; ++i, c <<= 8) + ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + /* write footer (IDAT CRC-32, followed by IEND chunk) */ + if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) + { + *pLen_out = 0; + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); + for (i = 0; i < 4; ++i, c <<= 8) + (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); + /* compute final size of file, grab compressed data buffer and return */ + *pLen_out += 57; + MZ_FREE(pComp); + return out_buf.m_pBuf; +} +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +{ + /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */ + return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); +} + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ +/* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +tdefl_compressor *tdefl_compressor_alloc(void) +{ + return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); +} + +void tdefl_compressor_free(tdefl_compressor *pComp) +{ + MZ_FREE(pComp); +} +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + /************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + + +#ifndef MINIZ_NO_INFLATE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Decompression (completely independent from all compression API's) */ + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN \ + switch (r->m_state) \ + { \ + case 0: +#define TINFL_CR_RETURN(state_index, result) \ + do \ + { \ + status = result; \ + r->m_state = state_index; \ + goto common_exit; \ + case state_index:; \ + } \ + MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) \ + do \ + { \ + for (;;) \ + { \ + TINFL_CR_RETURN(state_index, result); \ + } \ + } \ + MZ_MACRO_END +#define TINFL_CR_FINISH } + +#define TINFL_GET_BYTE(state_index, c) \ + do \ + { \ + while (pIn_buf_cur >= pIn_buf_end) \ + { \ + TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \ + } \ + c = *pIn_buf_cur++; \ + } \ + MZ_MACRO_END + +#define TINFL_NEED_BITS(state_index, n) \ + do \ + { \ + mz_uint c; \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < (mz_uint)(n)) +#define TINFL_SKIP_BITS(state_index, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END +#define TINFL_GET_BITS(state_index, b, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + b = bit_buf & ((1 << (n)) - 1); \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END + +/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */ +/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */ +/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */ +/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ +#define TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree) \ + do \ + { \ + temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) \ + { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } \ + else if (num_bits > TINFL_FAST_LOOKUP_BITS) \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); \ + if (temp >= 0) \ + break; \ + } \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < 15); + +/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */ +/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */ +/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */ +/* The slow path is only executed at the very end of the input buffer. */ +/* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */ +/* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */ +#define TINFL_HUFF_DECODE(state_index, sym, pLookUp, pTree) \ + do \ + { \ + int temp; \ + mz_uint code_len, c; \ + if (num_bits < 15) \ + { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) \ + { \ + TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree); \ + } \ + else \ + { \ + bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ + pIn_buf_cur += 2; \ + num_bits += 16; \ + } \ + } \ + if ((temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + code_len = temp >> 9, temp &= 511; \ + else \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while (temp < 0); \ + } \ + sym = temp; \ + bit_buf >>= code_len; \ + num_bits -= code_len; \ + } \ + MZ_MACRO_END + +static void tinfl_clear_tree(tinfl_decompressor *r) +{ + if (r->m_type == 0) + MZ_CLEAR_ARR(r->m_tree_0); + else if (r->m_type == 1) + MZ_CLEAR_ARR(r->m_tree_1); + else + MZ_CLEAR_ARR(r->m_tree_2); +} + +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) +{ + static const mz_uint16 s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; + static const mz_uint8 s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; + static const mz_uint16 s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; + static const mz_uint8 s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; + static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + static const mz_uint16 s_min_table_sizes[3] = { 257, 1, 4 }; + + mz_int16 *pTrees[3]; + mz_uint8 *pCode_sizes[3]; + + tinfl_status status = TINFL_STATUS_FAILED; + mz_uint32 num_bits, dist, counter, num_extra; + tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next ? pOut_buf_next + *pOut_buf_size : NULL; + size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; + + /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */ + if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) + { + *pIn_buf_size = *pOut_buf_size = 0; + return TINFL_STATUS_BAD_PARAM; + } + + pTrees[0] = r->m_tree_0; + pTrees[1] = r->m_tree_1; + pTrees[2] = r->m_tree_2; + pCode_sizes[0] = r->m_code_size_0; + pCode_sizes[1] = r->m_code_size_1; + pCode_sizes[2] = r->m_code_size_2; + + num_bits = r->m_num_bits; + bit_buf = r->m_bit_buf; + dist = r->m_dist; + counter = r->m_counter; + num_extra = r->m_num_extra; + dist_from_out_buf_start = r->m_dist_from_out_buf_start; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; + r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_GET_BYTE(1, r->m_zhdr0); + TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)((size_t)1 << (8U + (r->m_zhdr0 >> 4))))); + if (counter) + { + TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); + } + } + + do + { + TINFL_GET_BITS(3, r->m_final, 3); + r->m_type = r->m_final >> 1; + if (r->m_type == 0) + { + TINFL_SKIP_BITS(5, num_bits & 7); + for (counter = 0; counter < 4; ++counter) + { + if (num_bits) + TINFL_GET_BITS(6, r->m_raw_header[counter], 8); + else + TINFL_GET_BYTE(7, r->m_raw_header[counter]); + } + if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) + { + TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); + } + while ((counter) && (num_bits)) + { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) + { + size_t n; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); + } + while (pIn_buf_cur >= pIn_buf_end) + { + TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); + pIn_buf_cur += n; + pOut_buf_cur += n; + counter -= (mz_uint)n; + } + } + else if (r->m_type == 3) + { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } + else + { + if (r->m_type == 1) + { + mz_uint8 *p = r->m_code_size_0; + mz_uint i; + r->m_table_sizes[0] = 288; + r->m_table_sizes[1] = 32; + TINFL_MEMSET(r->m_code_size_1, 5, 32); + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + } + else + { + for (counter = 0; counter < 3; counter++) + { + TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); + r->m_table_sizes[counter] += s_min_table_sizes[counter]; + } + MZ_CLEAR_ARR(r->m_code_size_2); + for (counter = 0; counter < r->m_table_sizes[2]; counter++) + { + mz_uint s; + TINFL_GET_BITS(14, s, 3); + r->m_code_size_2[s_length_dezigzag[counter]] = (mz_uint8)s; + } + r->m_table_sizes[2] = 19; + } + for (; (int)r->m_type >= 0; r->m_type--) + { + int tree_next, tree_cur; + mz_int16 *pLookUp; + mz_int16 *pTree; + mz_uint8 *pCode_size; + mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; + pLookUp = r->m_look_up[r->m_type]; + pTree = pTrees[r->m_type]; + pCode_size = pCode_sizes[r->m_type]; + MZ_CLEAR_ARR(total_syms); + TINFL_MEMSET(pLookUp, 0, sizeof(r->m_look_up[0])); + tinfl_clear_tree(r); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) + total_syms[pCode_size[i]]++; + used_syms = 0, total = 0; + next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) + { + used_syms += total_syms[i]; + next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); + } + if ((65536 != total) && (used_syms > 1)) + { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) + { + mz_uint rev_code = 0, l, cur_code, code_size = pCode_size[sym_index]; + if (!code_size) + continue; + cur_code = next_code[code_size]++; + for (l = code_size; l > 0; l--, cur_code >>= 1) + rev_code = (rev_code << 1) | (cur_code & 1); + if (code_size <= TINFL_FAST_LOOKUP_BITS) + { + mz_int16 k = (mz_int16)((code_size << 9) | sym_index); + while (rev_code < TINFL_FAST_LOOKUP_SIZE) + { + pLookUp[rev_code] = k; + rev_code += (1 << code_size); + } + continue; + } + if (0 == (tree_cur = pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) + { + pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) + { + tree_cur -= ((rev_code >>= 1) & 1); + if (!pTree[-tree_cur - 1]) + { + pTree[-tree_cur - 1] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + else + tree_cur = pTree[-tree_cur - 1]; + } + tree_cur -= ((rev_code >>= 1) & 1); + pTree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) + { + for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) + { + mz_uint s; + TINFL_HUFF_DECODE(16, dist, r->m_look_up[2], r->m_tree_2); + if (dist < 16) + { + r->m_len_codes[counter++] = (mz_uint8)dist; + continue; + } + if ((dist == 16) && (!counter)) + { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; + TINFL_GET_BITS(18, s, num_extra); + s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); + counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) + { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + TINFL_MEMCPY(r->m_code_size_0, r->m_len_codes, r->m_table_sizes[0]); + TINFL_MEMCPY(r->m_code_size_1, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); + } + } + for (;;) + { + mz_uint8 *pSrc; + for (;;) + { + if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) + { + TINFL_HUFF_DECODE(23, counter, r->m_look_up[0], r->m_tree_0); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)counter; + } + else + { + int sym2; + mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + if (num_bits < 30) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 4; + num_bits += 32; + } +#else + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + counter = sym2; + bit_buf >>= code_len; + num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + bit_buf >>= code_len; + num_bits -= code_len; + + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) + { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) + break; + + num_extra = s_length_extra[counter - 257]; + counter = s_length_base[counter - 257]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(25, extra_bits, num_extra); + counter += extra_bits; + } + + TINFL_HUFF_DECODE(26, dist, r->m_look_up[1], r->m_tree_1); + num_extra = s_dist_extra[dist]; + dist = s_dist_base[dist]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(27, extra_bits, num_extra); + dist += extra_bits; + } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist == 0 || dist > dist_from_out_buf_start || dist_from_out_buf_start == 0) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) + { + while (counter--) + { + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) + { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do + { +#ifdef MINIZ_UNALIGNED_USE_MEMCPY + memcpy(pOut_buf_cur, pSrc, sizeof(mz_uint32)*2); +#else + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; +#endif + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) + { + if (counter) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + while(counter>2) + { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; + pSrc += 3; + counter -= 3; + } + if (counter > 0) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + + /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */ + TINFL_SKIP_BITS(32, num_bits & 7); + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + bit_buf &= ~(~(tinfl_bit_buf_t)0 << num_bits); + MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ + + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + for (counter = 0; counter < 4; ++counter) + { + mz_uint s; + if (num_bits) + TINFL_GET_BITS(41, s, 8); + else + TINFL_GET_BYTE(42, s); + r->m_z_adler32 = (r->m_z_adler32 << 8) | s; + } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); + + TINFL_CR_FINISH + +common_exit: + /* As long as we aren't telling the caller that we NEED more input to make forward progress: */ + /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */ + if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS)) + { + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + } + r->m_num_bits = num_bits; + r->m_bit_buf = bit_buf & ~(~(tinfl_bit_buf_t)0 << num_bits); + r->m_dist = dist; + r->m_counter = counter; + r->m_num_extra = num_extra; + r->m_dist_from_out_buf_start = dist_from_out_buf_start; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; + *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) + { + const mz_uint8 *ptr = pOut_buf_next; + size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; + size_t block_len = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + r->m_check_adler32 = (s2 << 16) + s1; + if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) + status = TINFL_STATUS_ADLER32_MISMATCH; + } + return status; +} + +/* Higher level helper functions. */ +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tinfl_decompressor decomp; + void *pBuf = NULL, *pNew_buf; + size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for (;;) + { + size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) + break; + new_out_buf_capacity = out_buf_capacity * 2; + if (new_out_buf_capacity < 128) + new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + pBuf = pNew_buf; + out_buf_capacity = new_out_buf_capacity; + } + return pBuf; +} + +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tinfl_decompressor decomp; + tinfl_status status; + tinfl_init(&decomp); + status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; +} + +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + int result = 0; + tinfl_decompressor decomp; + mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); + size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) + return TINFL_STATUS_FAILED; + memset(pDict,0,TINFL_LZ_DICT_SIZE); + tinfl_init(&decomp); + for (;;) + { + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) + { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); + } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; +} + +#ifndef MINIZ_NO_MALLOC +tinfl_decompressor *tinfl_decompressor_alloc(void) +{ + tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); + if (pDecomp) + tinfl_init(pDecomp); + return pDecomp; +} + +void tinfl_decompressor_free(tinfl_decompressor *pDecomp) +{ + MZ_FREE(pDecomp); +} +#endif + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + /************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * Copyright 2016 Martin Raiber + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- .ZIP archive reading */ + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include + +#if defined(_MSC_VER) || defined(__MINGW64__) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include + +static WCHAR* mz_utf8z_to_widechar(const char* str) +{ + int reqChars = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); + WCHAR* wStr = (WCHAR*)malloc(reqChars * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, str, -1, wStr, reqChars); + return wStr; +} + +static FILE *mz_fopen(const char *pFilename, const char *pMode) +{ + WCHAR* wFilename = mz_utf8z_to_widechar(pFilename); + WCHAR* wMode = mz_utf8z_to_widechar(pMode); + FILE* pFile = NULL; + errno_t err = _wfopen_s(&pFile, wFilename, wMode); + free(wFilename); + free(wMode); + return err ? NULL : pFile; +} + +static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) +{ + WCHAR* wPath = mz_utf8z_to_widechar(pPath); + WCHAR* wMode = mz_utf8z_to_widechar(pMode); + FILE* pFile = NULL; + errno_t err = _wfreopen_s(&pFile, wPath, wMode, pStream); + free(wPath); + free(wMode); + return err ? NULL : pFile; +} + +static int mz_stat64(const char *path, struct __stat64 *buffer) +{ + WCHAR* wPath = mz_utf8z_to_widechar(path); + int res = _wstat64(wPath, buffer); + free(wPath); + return res; +} + +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN mz_fopen +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 _ftelli64 +#define MZ_FSEEK64 _fseeki64 +#define MZ_FILE_STAT_STRUCT _stat64 +#define MZ_FILE_STAT mz_stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN mz_freopen +#define MZ_DELETE_FILE remove + +#elif defined(__MINGW32__) || defined(__WATCOMC__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 _ftelli64 +#define MZ_FSEEK64 _fseeki64 +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove + +#elif defined(__TINYC__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove + +#elif defined(__USE_LARGEFILE64) /* gcc, clang */ +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen64(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT stat64 +#define MZ_FILE_STAT stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen64(p, m, s) +#define MZ_DELETE_FILE remove + +#elif defined(__APPLE__) || defined(__FreeBSD__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen(p, m, s) +#define MZ_DELETE_FILE remove + +#else +#pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#ifdef __STRICT_ANSI__ +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#else +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#endif +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#endif /* #ifdef _MSC_VER */ +#endif /* #ifdef MINIZ_NO_STDIO */ + +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +/* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. */ +enum +{ + /* ZIP archive identifiers and record sizes */ + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, + MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + + /* ZIP64 archive identifier and record sizes */ + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, + MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, + MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, + MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, + MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, + + /* Central directory header record offsets */ + MZ_ZIP_CDH_SIG_OFS = 0, + MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, + MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, + MZ_ZIP_CDH_BIT_FLAG_OFS = 8, + MZ_ZIP_CDH_METHOD_OFS = 10, + MZ_ZIP_CDH_FILE_TIME_OFS = 12, + MZ_ZIP_CDH_FILE_DATE_OFS = 14, + MZ_ZIP_CDH_CRC32_OFS = 16, + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, + MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, + MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, + MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, + MZ_ZIP_CDH_DISK_START_OFS = 34, + MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, + MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, + + /* Local directory header offsets */ + MZ_ZIP_LDH_SIG_OFS = 0, + MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, + MZ_ZIP_LDH_BIT_FLAG_OFS = 6, + MZ_ZIP_LDH_METHOD_OFS = 8, + MZ_ZIP_LDH_FILE_TIME_OFS = 10, + MZ_ZIP_LDH_FILE_DATE_OFS = 12, + MZ_ZIP_LDH_CRC32_OFS = 14, + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, + MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, + + /* End of central directory offsets */ + MZ_ZIP_ECDH_SIG_OFS = 0, + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, + MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, + MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, + MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, + + /* ZIP64 End of central directory locator offsets */ + MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ + + /* ZIP64 End of central directory header offsets */ + MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ + MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, + MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 +}; + +typedef struct +{ + void *m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; + +struct mz_zip_internal_state_tag +{ + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; + + /* The flags passed in when the archive is initially opened. */ + mz_uint32 m_init_flags; + + /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ + mz_bool m_zip64; + + /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.) */ + mz_bool m_zip64_has_extended_info_fields; + + /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */ + MZ_FILE *m_pFile; + mz_uint64 m_file_archive_start_ofs; + + void *m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size + +#if defined(DEBUG) || defined(_DEBUG) +static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index) +{ + MZ_ASSERT(index < pArray->m_size); + return index; +} +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)] +#else +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] +#endif + +static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size) +{ + memset(pArray, 0, sizeof(mz_zip_array)); + pArray->m_element_size = element_size; +} + +static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) +{ + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} + +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) +{ + void *pNew_p; + size_t new_capacity = min_new_capacity; + MZ_ASSERT(pArray->m_element_size); + if (pArray->m_capacity >= min_new_capacity) + return MZ_TRUE; + if (growing) + { + new_capacity = MZ_MAX(1, pArray->m_capacity); + while (new_capacity < min_new_capacity) + new_capacity *= 2; + } + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) + return MZ_FALSE; + pArray->m_p = pNew_p; + pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) +{ + if (new_capacity > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) + return MZ_FALSE; + } + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) +{ + if (new_size > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) + return MZ_FALSE; + } + pArray->m_size = new_size; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) +{ + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) +{ + size_t orig_size = pArray->m_size; + if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) + return MZ_FALSE; + if (n > 0) + memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; + tm.tm_mon = ((dos_date >> 5) & 15) - 1; + tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; + tm.tm_min = (dos_time >> 5) & 63; + tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); +} + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ +#ifdef _MSC_VER + struct tm tm_struct; + struct tm *tm = &tm_struct; + errno_t err = localtime_s(tm, &time); + if (err) + { + *pDOS_date = 0; + *pDOS_time = 0; + return; + } +#else + struct tm *tm = localtime(&time); +#endif /* #ifdef _MSC_VER */ + + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); + *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +} +#endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifndef MINIZ_NO_STDIO +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime) +{ + struct MZ_FILE_STAT_STRUCT file_stat; + + /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ + if (MZ_FILE_STAT(pFilename, &file_stat) != 0) + return MZ_FALSE; + + *pTime = file_stat.st_mtime; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ + +static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time) +{ + struct utimbuf t; + + memset(&t, 0, sizeof(t)); + t.actime = access_time; + t.modtime = modified_time; + + return !utime(pFilename, &t); +} +#endif /* #ifndef MINIZ_NO_STDIO */ +#endif /* #ifndef MINIZ_NO_TIME */ + +static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + if (pZip) + pZip->m_last_error = err_num; + return MZ_FALSE; +} + +static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags) +{ + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + pZip->m_last_error = MZ_ZIP_NO_ERROR; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + pZip->m_pState->m_init_flags = flags; + pZip->m_pState->m_zip64 = MZ_FALSE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} + +#define MZ_SWAP_UINT32(a, b) \ + do \ + { \ + mz_uint32 t = a; \ + a = b; \ + b = t; \ + } \ + MZ_MACRO_END + +/* Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) */ +static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices; + mz_uint32 start, end; + const mz_uint32 size = pZip->m_total_files; + + if (size <= 1U) + return; + + pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + + start = (size - 2U) >> 1U; + for (;;) + { + mz_uint64 child, root = start; + for (;;) + { + if ((child = (root << 1U) + 1U) >= size) + break; + child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + if (!start) + break; + start--; + } + + end = size - 1; + while (end > 0) + { + mz_uint64 child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for (;;) + { + if ((child = (root << 1U) + 1U) >= end) + break; + child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + end--; + } +} + +static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs) +{ + mz_int64 cur_file_ofs; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + + /* Basic sanity checks - reject files which are too small */ + if (pZip->m_archive_size < record_size) + return MZ_FALSE; + + /* Find the record by scanning the file from the end towards the beginning. */ + cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for (;;) + { + int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) + return MZ_FALSE; + + for (i = n - 4; i >= 0; --i) + { + mz_uint s = MZ_READ_LE32(pBuf + i); + if (s == record_sig) + { + if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) + break; + } + } + + if (i >= 0) + { + cur_file_ofs += i; + break; + } + + /* Give up if we've searched the entire file, or we've gone back "too far" (~64kb) */ + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) + return MZ_FALSE; + + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); + } + + *pOfs = cur_file_ofs; + return MZ_TRUE; +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags) +{ + mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; + mz_uint64 cdir_ofs = 0; + mz_int64 cur_file_ofs = 0; + const mz_uint8 *p; + + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); + mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; + + mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32; + + mz_uint64 zip64_end_of_central_dir_ofs = 0; + + /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. */ + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) + return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); + + /* Read and verify the end of central directory record. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + { + if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) + { + zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); + if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) + { + pZip->m_pState->m_zip64 = MZ_TRUE; + } + } + } + } + } + + pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); + cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + + if (pZip->m_pState->m_zip64) + { + mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); + mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); + mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); + mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); + + if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (zip64_total_num_of_disks != 1U) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + /* Check for miniz's practical limits */ + if (zip64_cdir_total_entries > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; + + if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; + + /* Check for miniz's current practical limits (sorry, this should be enough for millions of files) */ + if (zip64_size_of_central_directory > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + cdir_size = (mz_uint32)zip64_size_of_central_directory; + + num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); + + cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); + + cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); + } + + if (pZip->m_total_files != cdir_entries_on_this_disk) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (cdir_size < (mz_uint64)pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) + { + mz_uint i, n; + /* Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices. */ + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (sort_central_dir) + { + if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + /* Now create an index into the central directory file records, do some basic sanity checking on each record */ + p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) + { + mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; + mz_uint64 comp_size, decomp_size, local_header_ofs; + + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + + if (sort_central_dir) + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && + (ext_data_size) && + (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = ext_data_size; + + if (extra_size_remaining) + { + const mz_uint8 *pExtra_data; + void* buf = NULL; + + if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size > n) + { + buf = MZ_MALLOC(ext_data_size); + if(buf==NULL) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size, buf, ext_data_size) != ext_data_size) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (mz_uint8*)buf; + } + else + { + pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; + } + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin). */ + pZip->m_pState->m_zip64 = MZ_TRUE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + + MZ_FREE(buf); + } + } + + /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */ + if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) + { + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (comp_size != MZ_UINT32_MAX) + { + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + n -= total_header_size; + p += total_header_size; + } + } + + if (sort_central_dir) + mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +void mz_zip_zero_struct(mz_zip_archive *pZip) +{ + if (pZip) + MZ_CLEAR_PTR(pZip); +} + +static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_bool status = MZ_TRUE; + + if (!pZip) + return MZ_FALSE; + + if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; + + return MZ_FALSE; + } + + if (pZip->m_pState) + { + mz_zip_internal_state *pState = pZip->m_pState; + pZip->m_pState = NULL; + + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; + status = MZ_FALSE; + } + } + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + } + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return status; +} + +mz_bool mz_zip_reader_end(mz_zip_archive *pZip) +{ + return mz_zip_reader_end_internal(pZip, MZ_TRUE); +} +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags) +{ + if ((!pZip) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_archive_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags) +{ + if (!pMem) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pNeeds_keepalive = NULL; + +#ifdef __cplusplus + pZip->m_pState->m_pMem = const_cast(pMem); +#else + pZip->m_pState->m_pMem = (void *)pMem; +#endif + + pZip->m_pState->m_mem_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) +{ + return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); +} + +mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) +{ + mz_uint64 file_size; + MZ_FILE *pFile; + + if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + file_size = archive_size; + if (!file_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + } + + file_size = MZ_FTELL64(pFile); + } + + /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ + + if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) +{ + mz_uint64 cur_file_ofs; + + if ((!pZip) || (!pFile)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + cur_file_ofs = MZ_FTELL64(pFile); + + if (!archive_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + + archive_size = MZ_FTELL64(pFile) - cur_file_ofs; + + if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = archive_size; + pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index) +{ + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) + return NULL; + return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); +} + +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint m_bit_flag; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; +} + +mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint bit_flag; + mz_uint method; + + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); + bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + + if ((method != 0) && (method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + return MZ_FALSE; + } + + if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + return MZ_FALSE; + } + + if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint filename_len, attribute_mapping_id, external_attr; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) + { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') + return MZ_TRUE; + } + + /* Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. */ + /* Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. */ + /* FIXME: Remove this check? Is it necessary - we already check the filename. */ + attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; + (void)attribute_mapping_id; + + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) + { + return MZ_TRUE; + } + + return MZ_FALSE; +} + +static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data) +{ + mz_uint n; + const mz_uint8 *p = pCentral_dir_header; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_FALSE; + + if ((!p) || (!pStat)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Extract fields from the central directory record. */ + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + /* Copy as much of the filename and comment as possible. */ + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); + pStat->m_comment[n] = '\0'; + + /* Set some flags for convienance */ + pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); + pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); + pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); + + /* See if we need to read any zip64 extended information fields. */ + /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them). */ + if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if (extra_size_remaining) + { + const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2; + mz_uint32 field_data_remaining = field_data_size; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_TRUE; + + if (pStat->m_uncomp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_uncomp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_comp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_comp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_local_header_ofs == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + } + } + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) +{ + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) + return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) + return MZ_FALSE; + return MZ_TRUE; +} + +static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); +} + +static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const mz_uint32 size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); + + if (pIndex) + *pIndex = 0; + + if (size) + { + /* yes I could use uint32_t's, but then we would have to add some special case checks in the loop, argh, and */ + /* honestly the major expense here on 32-bit CPU's will still be the filename compare */ + mz_int64 l = 0, h = (mz_int64)size - 1; + + while (l <= h) + { + mz_int64 m = l + ((h - l) >> 1); + mz_uint32 file_index = pIndices[(mz_uint32)m]; + + int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); + if (!comp) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) +{ + mz_uint32 index; + if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index)) + return -1; + else + return (int)index; +} + +mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) +{ + mz_uint file_index; + size_t name_len, comment_len; + + if (pIndex) + *pIndex = 0; + + if ((!pZip) || (!pZip->m_pState) || (!pName)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* See if we can use a binary search */ + if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && + (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && + ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) + { + return mz_zip_locate_file_binary_search(pZip, pName, pIndex); + } + + /* Locate the entry by scanning the entire central directory */ + name_len = strlen(pName); + if (name_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + comment_len = pComment ? strlen(pComment) : 0; + if (comment_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + for (file_index = 0; file_index < pZip->m_total_files; file_index++) + { + const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) + continue; + if (comment_len) + { + mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); + const char *pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) + { + int ofs = filename_len - 1; + do + { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) + break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; + filename_len -= ofs; + } + if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags))) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +static +mz_bool mz_zip_reader_extract_to_mem_no_alloc1(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size, const mz_zip_archive_file_stat *st) +{ + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + tinfl_decompressor inflator; + + if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (st) { + file_stat = *st; + } else + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Ensure supplied output buffer is large enough. */ + needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (buf_size < needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); + + /* Read and parse the local directory entry. */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) + { + if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + } +#endif + + return MZ_TRUE; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) + { + /* Read directly from the archive in memory. */ + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else if (pUser_read_buf) + { + /* Use a user provided read buffer. */ + if (!user_read_buf_size) + return MZ_FALSE; + pRead_buf = (mz_uint8 *)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + else + { + /* Temporarily allocate a read buffer. */ + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do + { + /* The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above */ + size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL); +} + +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, NULL, 0, NULL); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); +} + +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + mz_uint64 alloc_size; + void *pBuf; + + if (pSize) + *pSize = 0; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return NULL; + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + return NULL; + } + + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + if (!mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, (size_t)alloc_size, flags, NULL, 0, &file_stat)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) + *pSize = (size_t)alloc_size; + return pBuf; +} + +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + { + if (pSize) + *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + int status = TINFL_STATUS_DONE; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint file_crc32 = MZ_CRC32_INIT; +#endif + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf = NULL; + void *pWrite_buf = NULL; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir) */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pState->m_pMem) + { + if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + } + else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); +#endif + } + + cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + while (comp_remaining) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); + } +#endif + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + } + } + } + else + { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + status = TINFL_STATUS_FAILED; + } + else + { + do + { + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) + { + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); +#endif + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + } + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (file_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if (!pZip->m_pState->m_pMem) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + if (pWrite_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_reader_extract_iter_state *pState; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + /* Argument sanity check */ + if ((!pZip) || (!pZip->m_pState)) + return NULL; + + /* Allocate an iterator status structure */ + pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); + if (!pState) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + /* Fetch file details */ + if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Encryption and patch files are not supported. */ + if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Init state - save args */ + pState->pZip = pZip; + pState->flags = flags; + + /* Init state - reset variables to defaults */ + pState->status = TINFL_STATUS_DONE; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + pState->file_crc32 = MZ_CRC32_INIT; +#endif + pState->read_buf_ofs = 0; + pState->out_buf_ofs = 0; + pState->pRead_buf = NULL; + pState->pWrite_buf = NULL; + pState->out_blk_remain = 0; + + /* Read and parse the local directory entry. */ + pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs; + pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + else + { + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, therefore intermediate read buffer required */ + pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + else + { + /* Decompression not required - we will be reading directly into user buffer, no temp buf required */ + pState->read_buf_size = 0; + } + pState->read_buf_avail = 0; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, init decompressor */ + tinfl_init( &pState->inflator ); + + /* Allocate write buffer */ + if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + if (pState->pRead_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + + return pState; +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_uint32 file_index; + + /* Locate file index by name */ + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return NULL; + + /* Construct iterator */ + return mz_zip_reader_extract_iter_new(pZip, file_index, flags); +} + +size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size) +{ + size_t copied_to_caller = 0; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) + return 0; + + if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data, calc amount to return. */ + copied_to_caller = (size_t)MZ_MIN( buf_size, pState->comp_remaining ); + + /* Zip is in memory....or requires reading from a file? */ + if (pState->pZip->m_pState->m_pMem) + { + /* Copy data to caller's buffer */ + memcpy( pvBuf, pState->pRead_buf, copied_to_caller ); + pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller; + } + else + { + /* Read directly into caller's buffer */ + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller) + { + /* Failed to read all that was asked for, flag failure and alert user */ + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + copied_to_caller = 0; + } + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Compute CRC if not returning compressed data only */ + if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller); +#endif + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += copied_to_caller; + pState->out_buf_ofs += copied_to_caller; + pState->comp_remaining -= copied_to_caller; + } + else + { + do + { + /* Calc ptr to write buffer - given current output pos and block size */ + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + /* Calc max output size - given current output pos and block size */ + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + if (!pState->out_blk_remain) + { + /* Read more data from file if none available (and reading from file) */ + if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) + { + /* Calc read size */ + pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += pState->read_buf_avail; + pState->comp_remaining -= pState->read_buf_avail; + pState->read_buf_ofs = 0; + } + + /* Perform decompression */ + in_buf_size = (size_t)pState->read_buf_avail; + pState->status = tinfl_decompress(&pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + pState->read_buf_avail -= in_buf_size; + pState->read_buf_ofs += in_buf_size; + + /* Update current output block size remaining */ + pState->out_blk_remain = out_buf_size; + } + + if (pState->out_blk_remain) + { + /* Calc amount to return. */ + size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain ); + + /* Copy data to caller's buffer */ + memcpy( (mz_uint8*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Perform CRC */ + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); +#endif + + /* Decrement data consumed from block */ + pState->out_blk_remain -= to_copy; + + /* Inc output offset, while performing sanity check */ + if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Increment counter of data copied to caller */ + copied_to_caller += to_copy; + } + } while ( (copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT)) ); + } + + /* Return how many bytes were copied into user buffer */ + return copied_to_caller; +} + +mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState) +{ + int status; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) + return MZ_FALSE; + + /* Was decompression completed and requested? */ + if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + pState->status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (pState->file_crc32 != pState->file_stat.m_crc32) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + } +#endif + } + + /* Free buffers */ + if (!pState->pZip->m_pState->m_pMem) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); + if (pState->pWrite_buf) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); + + /* Save status */ + status = pState->status; + + /* Free context */ + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); + + return status == TINFL_STATUS_DONE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +{ + (void)ofs; + + return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) +{ + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE *pFile; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + + if (MZ_FCLOSE(pFile) == EOF) + { + if (status) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + + status = MZ_FALSE; + } + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + if (status) + mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); +#endif + + return status; +} + +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} + +mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); +} + +mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_uint32 *p = (mz_uint32 *)pOpaque; + (void)file_ofs; + *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n); + return n; +} + +mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + mz_zip_internal_state *pState; + const mz_uint8 *pCentral_dir_header; + mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint64 local_header_ofs = 0; + mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_uint32 uncomp_crc32 = MZ_CRC32_INIT; + mz_bool has_data_descriptor; + mz_uint32 local_header_bit_flags; + + mz_zip_array file_data_array; + mz_zip_array_init(&file_data_array, 1); + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (file_index > pZip->m_total_files) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + pCentral_dir_header = mz_zip_get_cdh(pZip, file_index); + + if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_is_encrypted) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports stored and deflate. */ + if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + if (!file_stat.m_is_supported) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + /* Read and parse the local directory entry. */ + local_header_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS); + local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + has_data_descriptor = (local_header_bit_flags & 8) != 0; + + if (local_header_filename_len != strlen(file_stat.m_filename)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (!mz_zip_array_resize(pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE)) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + goto handle_failure; + } + + if (local_header_filename_len) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + /* I've seen 1 archive that had the same pathname, but used backslashes in the local dir and forward slashes in the central dir. Do we care about this? For now, this case will fail validation. */ + if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) + { + mz_uint32 extra_size_remaining = local_header_extra_len; + const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p; + + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + /* TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! (big_descriptor.zip) */ + /* I've seen zips in the wild with the data descriptor bit set, but proper local header values and bogus data descriptors */ + if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32)) + { + mz_uint8 descriptor_buf[32]; + mz_bool has_id; + const mz_uint8 *pSrc; + mz_uint32 file_crc32; + mz_uint64 comp_size = 0, uncomp_size = 0; + + mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4; + + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf; + + file_crc32 = MZ_READ_LE32(pSrc); + + if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64)); + } + else + { + comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32)); + } + + if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + else + { + if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + mz_zip_array_clear(pZip, &file_data_array); + + if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0) + { + if (!mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0)) + return MZ_FALSE; + + /* 1 more check to be sure, although the extract checks too. */ + if (uncomp_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + return MZ_FALSE; + } + } + + return MZ_TRUE; + +handle_failure: + mz_zip_array_clear(pZip, &file_data_array); + return MZ_FALSE; +} + +mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) +{ + mz_zip_internal_state *pState; + mz_uint32 i; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Basic sanity checks */ + if (!pState->m_zip64) + { + if (pZip->m_total_files > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (pZip->m_archive_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + else + { + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + for (i = 0; i < pZip->m_total_files; i++) + { + if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags) + { + mz_uint32 found_index; + mz_zip_archive_file_stat stat; + + if (!mz_zip_reader_file_stat(pZip, i, &stat)) + return MZ_FALSE; + + if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index)) + return MZ_FALSE; + + /* This check can fail if there are duplicate filenames in the archive (which we don't check for when writing - that's up to the user) */ + if (found_index != i) + return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + } + + if (!mz_zip_validate_file(pZip, i, flags)) + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if ((!pMem) || (!size)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_mem(&zip, pMem, size, flags)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) + *pErr = actual_err; + + return success; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if (!pFilename) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) + *pErr = actual_err; + + return success; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +/* ------------------- .ZIP archive writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); +} +static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); + p[2] = (mz_uint8)(v >> 16); + p[3] = (mz_uint8)(v >> 24); +} +static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) +{ + mz_write_le32(p, (mz_uint32)v); + mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); +} + +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) +#define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v)) + +static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); + + if (!n) + return 0; + + /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */ + if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + return 0; + } + + if (new_size > pState->m_mem_capacity) + { + void *pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); + + while (new_capacity < new_size) + new_capacity *= 2; + + if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return 0; + } + + pState->m_pMem = pNew_block; + pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; +} + +static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_zip_internal_state *pState; + mz_bool status = MZ_TRUE; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + status = MZ_FALSE; + } + } + + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; +} + +mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags) +{ + mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; + + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + { + if (!pZip->m_pRead) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (pZip->m_file_offset_alignment) + { + /* Ensure user specified file offset alignment is a power of 2. */ + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + + pZip->m_pState->m_zip64 = zip64; + pZip->m_pState->m_zip64_has_extended_info_fields = zip64; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) +{ + return mz_zip_writer_init_v2(pZip, existing_size, 0); +} + +mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags) +{ + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_mem_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; + + if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) + { + if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) + { + mz_zip_writer_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) +{ + return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + return 0; + } + + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) +{ + return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); +} + +mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags) +{ + MZ_FILE *pFile; + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + + pZip->m_pState->m_pFile = pFile; + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + + if (size_to_reserve_at_beginning) + { + mz_uint64 cur_ofs = 0; + char buf[4096]; + + MZ_CLEAR_ARR(buf); + + do + { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_ofs += n; + size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags) +{ + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, 0, flags)) + return MZ_FALSE; + + pZip->m_pState->m_pFile = pFile; + pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_zip_internal_state *pState; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) + { + /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?) */ + if (!pZip->m_pState->m_zip64) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* No sense in trying to write to an archive that's already at the support max size */ + if (pZip->m_pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + pState = pZip->m_pState; + + if (pState->m_pFile) + { +#ifdef MINIZ_NO_STDIO + (void)pFilename; + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +#else + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (!pFilename) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable. */ + if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) + { + /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. */ + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + } + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; +#endif /* #ifdef MINIZ_NO_STDIO */ + } + else if (pState->m_pMem) + { + /* Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. */ + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + } + /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ + else if (!pZip->m_pWrite) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Start writing new files at the archive's current central directory location. */ + /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer. */ + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_central_directory_file_ofs = 0; + + /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ + /* Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow. */ + /* TODO: We could easily maintain the sorted central directory offsets. */ + mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) +{ + return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); +} + +/* TODO: pArchive_name is a terrible name here! */ +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) +{ + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); +} + +typedef struct +{ + mz_zip_archive *m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; + +static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser) +{ + mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) + return MZ_FALSE; + + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; +} + +#define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) +#define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) +static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs) +{ + mz_uint8 *pDst = pBuf; + mz_uint32 field_size = 0; + + MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + MZ_WRITE_LE16(pDst + 2, 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) + { + MZ_WRITE_LE64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pComp_size) + { + MZ_WRITE_LE64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + MZ_WRITE_LE64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + MZ_WRITE_LE16(pBuf + 2, field_size); + + return (mz_uint32)(pDst - pBuf); +} + +static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, + mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, + const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes, + const char *user_extra_data, mz_uint user_extra_data_len) +{ + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + + if (!pZip->m_pState->m_zip64) + { + if (local_header_ofs > 0xFFFFFFFF) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, (mz_uint16)(extra_size + user_extra_data_len), comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) + { + /* Try to resize the central directory array back into its original state. */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) +{ + /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. */ + if (*pArchive_name == '/') + return MZ_FALSE; + + /* Making sure the name does not contain drive letters or DOS style backward slashes is the responsibility of the program using miniz*/ + + return MZ_TRUE; +} + +static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) +{ + mz_uint32 n; + if (!pZip->m_file_offset_alignment) + return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); +} + +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) +{ + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) + { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_file_ofs += s; + n -= s; + } + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) +{ + return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0); +} + +mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, + mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor *pComp = NULL; + mz_bool store_data_uncompressed; + mz_zip_internal_state *pState; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_uint16 bit_flags = 0; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + level = level_and_flags & 0xF; + store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + if (((mz_uint64)buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +#ifndef MINIZ_NO_TIME + if (last_modified != NULL) + { + mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); + } + else + { + MZ_TIME_T cur_time; + time(&cur_time); + mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif /* #ifndef MINIZ_NO_TIME */ + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) + { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len + + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) + { + /* Set DOS Subdirectory attribute bit. */ + ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; + + /* Subdirectories cannot contain data. */ + if ((buf_size) || (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) */ + if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if ((!store_data_uncompressed) && (buf_size)) + { + if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + cur_archive_file_ofs += num_alignment_padding_bytes; + + MZ_CLEAR_ARR(local_dir_header); + + if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + method = MZ_DEFLATED; + } + + if (pState->m_zip64) + { + if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + + if (pExtra_data != NULL) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (store_data_uncompressed) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + } + else if (buf_size) + { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; + + if (uncomp_size) + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, + comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 gen_flags; + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; + mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_zip_internal_state *pState; + mz_uint64 file_ofs = 0, cur_archive_header_file_ofs; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + + gen_flags = (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) ? 0 : MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if ((!pState->m_zip64) && (max_size > MZ_UINT32_MAX)) + { + /* Source file is too large for non-zip64 */ + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + pState->m_zip64 = MZ_TRUE; + } + + /* We could support this, but why? */ + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + } + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 + + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + +#ifndef MINIZ_NO_TIME + if (pFile_time) + { + mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); + } +#endif + + if (max_size <= 3) + level = 0; + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_archive_file_ofs; + + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + if (max_size && level) + { + method = MZ_DEFLATED; + } + + MZ_CLEAR_ARR(local_dir_header); + if (pState->m_zip64) + { + if (max_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, + (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + else + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, NULL, + NULL, + (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (max_size) + { + void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) + { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!level) + { + while (1) + { + size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); + if (n == 0) + break; + + if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + file_ofs += n; + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + cur_archive_file_ofs += n; + } + uncomp_size = file_ofs; + comp_size = uncomp_size; + } + else + { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + } + + for (;;) + { + tdefl_status status; + tdefl_flush flush = TDEFL_NO_FLUSH; + + size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); + if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + break; + } + + file_ofs += n; + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + + if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) + flush = TDEFL_FULL_FLUSH; + + if (n == 0) + flush = TDEFL_FINISH; + + status = tdefl_compress_buffer(pComp, pRead_buf, n, flush); + if (status == TDEFL_STATUS_DONE) + { + result = MZ_TRUE; + break; + } + else if (status != TDEFL_STATUS_OKAY) + { + mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + break; + } + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + + if (!result) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return MZ_FALSE; + } + + uncomp_size = file_ofs; + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + } + + if (!(level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE)) + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) + { + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, + (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), + (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : uncomp_size, + (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : comp_size, + uncomp_crc32, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + cur_archive_header_file_ofs = local_dir_header_ofs; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + if (pExtra_data != NULL) + { + cur_archive_header_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_header_file_ofs += archive_name_size; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_header_file_ofs += extra_size; + } + } + + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, comment_size, + uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO + +static size_t mz_file_read_func_stdio(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + MZ_FILE *pSrc_file = (MZ_FILE *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pSrc_file); + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pSrc_file, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + + return MZ_FREAD(pBuf, 1, n, pSrc_file); +} + +mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + return mz_zip_writer_add_read_buf_callback(pZip, pArchive_name, mz_file_read_func_stdio, pSrc_file, max_size, pFile_time, pComment, comment_size, level_and_flags, + user_extra_data, user_extra_data_len, user_extra_data_central, user_extra_data_central_len); +} + +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + MZ_FILE *pSrc_file = NULL; + mz_uint64 uncomp_size = 0; + MZ_TIME_T file_modified_time; + MZ_TIME_T *pFile_time = NULL; + mz_bool status; + + memset(&file_modified_time, 0, sizeof(file_modified_time)); + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + pFile_time = &file_modified_time; + if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); +#endif + + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + + status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, NULL, 0, NULL, 0); + + MZ_FCLOSE(pSrc_file); + + return status; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, mz_uint32 ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) +{ + /* + 64 should be enough for any new zip64 data */ + if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE); + + if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start)) + { + mz_uint8 new_ext_block[64]; + mz_uint8 *pDst = new_ext_block; + mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + mz_write_le16(pDst + sizeof(mz_uint16), 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) + { + mz_write_le64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + } + + if (pComp_size) + { + mz_write_le64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + mz_write_le64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + } + + if (pDisk_start) + { + mz_write_le32(pDst, *pDisk_start); + pDst += sizeof(mz_uint32); + } + + mz_write_le16(new_ext_block + sizeof(mz_uint16), (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2)); + + if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if ((pExt) && (ext_len)) + { + mz_uint32 extra_size_remaining = ext_len; + const mz_uint8 *pExtra_data = pExt; + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + return MZ_TRUE; +} + +/* TODO: This func is now pretty freakin complex due to zip64, split it up? */ +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index) +{ + mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size; + mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state *pState; + void *pBuf; + const mz_uint8 *pSrc_central_header; + mz_zip_archive_file_stat src_file_stat; + mz_uint32 src_filename_len, src_comment_len, src_ext_len; + mz_uint32 local_header_filename_size, local_header_extra_len; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Don't support copying files from zip64 archives to non-zip64, even though in some cases this is possible */ + if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Get pointer to the source central dir header and crack it */ + if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS); + src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); + src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS); + src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len; + + /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 fudge factor in case we need to add more extra data) */ + if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + if (!pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + /* TODO: Our zip64 support still has some 32-bit limits that may not be worth fixing. */ + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + + if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL)) + return MZ_FALSE; + + cur_src_file_ofs = src_file_stat.m_local_header_ofs; + cur_dst_file_ofs = pZip->m_archive_size; + + /* Read the source archive's local dir header */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Compute the total size we need to copy (filename+extra data+compressed data) */ + local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size; + + /* Try to find a zip64 extended information field */ + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) + { + mz_zip_array file_data_array; + const mz_uint8 *pExtra_data; + mz_uint32 extra_size_remaining = local_header_extra_len; + + mz_zip_array_init(&file_data_array, 1); + if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE)) + { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (const mz_uint8 *)file_data_array.m_p; + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + + mz_zip_array_clear(pZip, &file_data_array); + } + + if (!pState->m_zip64) + { + /* Try to detect if the new archive will most likely wind up too big and bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which could be present, +64 is a fudge factor). */ + /* We also check when the archive is finalized so this doesn't need to be perfect. */ + mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) + + pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64; + + if (approx_new_archive_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + /* Write dest archive padding */ + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) + return MZ_FALSE; + + cur_dst_file_ofs += num_alignment_padding_bytes; + + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + /* The original zip's local header+ext block doesn't change, even with zip64, so we can just copy it over to the dest zip */ + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Copy over the source archive bytes to the dest archive, also ensure we have enough buf space to handle optional data descriptor */ + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining))))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + while (src_archive_bytes_remaining) + { + n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + cur_src_file_ofs += n; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_dst_file_ofs += n; + + src_archive_bytes_remaining -= n; + } + + /* Now deal with the optional data descriptor */ + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) + { + /* Copy data descriptor */ + if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + /* src is zip64, dest must be zip64 */ + + /* name uint32_t's */ + /* id 1 (optional in zip64?) */ + /* crc 1 */ + /* comp_size 2 */ + /* uncomp_size 2 */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5); + } + else + { + /* src is NOT zip64 */ + mz_bool has_id; + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + + if (pZip->m_pState->m_zip64) + { + /* dest is zip64, so upgrade the data descriptor */ + const mz_uint8 *pSrc_descriptor = (const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0); + const mz_uint32 src_crc32 = MZ_READ_LE32(pSrc_descriptor); + const mz_uint64 src_comp_size = MZ_READ_LE32(pSrc_descriptor + sizeof(mz_uint32)); + const mz_uint64 src_uncomp_size = MZ_READ_LE32(pSrc_descriptor + 2*sizeof(mz_uint32)); + + mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); + mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size); + + n = sizeof(mz_uint32) * 6; + } + else + { + /* dest is NOT zip64, just copy it as-is */ + n = sizeof(mz_uint32) * (has_id ? 4 : 3); + } + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + + /* Finally, add the new central dir header */ + orig_central_dir_size = pState->m_central_dir.m_size; + + memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + + if (pState->m_zip64) + { + /* This is the painful part: We need to write a new central dir header + ext block with updated zip64 fields, and ensure the old fields (if any) are not included. */ + const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len; + mz_zip_array new_ext_block; + + mz_zip_array_init(&new_ext_block, sizeof(mz_uint8)); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX); + + if (!mz_zip_writer_update_zip64_extension_block(&new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return MZ_FALSE; + } + + MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + mz_zip_array_clear(pZip, &new_ext_block); + } + else + { + /* sanity checks */ + if (cur_dst_file_ofs > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (local_dir_header_ofs >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + } + + /* This shouldn't trigger unless we screwed up during the initial sanity checks */ + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + { + /* TODO: Support central dirs >= 32-bits in size */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + } + + n = (mz_uint32)orig_central_dir_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[256]; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) + { + if ((mz_uint64)pState->m_central_dir.m_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) + { + /* Write central directory */ + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += central_dir_size; + } + + if (pState->m_zip64) + { + /* Write zip64 end of central directory header */ + mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; + + MZ_CLEAR_ARR(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; + + /* Write zip64 end of central directory locator */ + MZ_CLEAR_ARR(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; + } + + /* Write end of central directory record */ + MZ_CLEAR_ARR(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize) +{ + if ((!ppBuf) || (!pSize)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + *ppBuf = NULL; + *pSize = 0; + + if ((!pZip) || (!pZip->m_pState)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_pWrite != mz_zip_heap_write_func) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_finalize_archive(pZip)) + return MZ_FALSE; + + *ppBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_end(mz_zip_archive *pZip) +{ + return mz_zip_writer_end_internal(pZip, MZ_TRUE); +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL); +} + +mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr) +{ + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + mz_zip_zero_struct(&zip_archive); + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_FILENAME; + return MZ_FALSE; + } + + /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten. */ + /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) + { + /* Create a new archive. */ + if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + created_new_archive = MZ_TRUE; + } + else + { + /* Append to an existing archive. */ + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + + mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); + + return MZ_FALSE; + } + } + + status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); + actual_err = zip_archive.m_last_error; + + /* Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) */ + if (!mz_zip_writer_finalize_archive(&zip_archive)) + { + if (!actual_err) + actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; + } + + if (!mz_zip_writer_end_internal(&zip_archive, status)) + { + if (!actual_err) + actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; + } + + if ((!status) && (created_new_archive)) + { + /* It's a new archive and something went wrong, so just delete it. */ + int ignoredStatus = MZ_DELETE_FILE(pZip_filename); + (void)ignoredStatus; + } + + if (pErr) + *pErr = actual_err; + + return status; +} + +void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr) +{ + mz_uint32 file_index; + mz_zip_archive zip_archive; + void *p = NULL; + + if (pSize) + *pSize = 0; + + if ((!pZip_filename) || (!pArchive_name)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + + return NULL; + } + + mz_zip_zero_struct(&zip_archive); + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + + return NULL; + } + + if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) + { + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); + } + + mz_zip_reader_end_internal(&zip_archive, p != NULL); + + if (pErr) + *pErr = zip_archive.m_last_error; + + return p; +} + +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +{ + return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL); +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* ------------------- Misc utils */ + +mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; +} + +mz_zip_type mz_zip_get_type(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; +} + +mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + mz_zip_error prev_err; + + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = err_num; + return prev_err; +} + +mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip) +{ + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + return pZip->m_last_error; +} + +mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip) +{ + return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); +} + +mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip) +{ + mz_zip_error prev_err; + + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = MZ_ZIP_NO_ERROR; + return prev_err; +} + +const char *mz_zip_get_error_string(mz_zip_error mz_err) +{ + switch (mz_err) + { + case MZ_ZIP_NO_ERROR: + return "no error"; + case MZ_ZIP_UNDEFINED_ERROR: + return "undefined error"; + case MZ_ZIP_TOO_MANY_FILES: + return "too many files"; + case MZ_ZIP_FILE_TOO_LARGE: + return "file too large"; + case MZ_ZIP_UNSUPPORTED_METHOD: + return "unsupported method"; + case MZ_ZIP_UNSUPPORTED_ENCRYPTION: + return "unsupported encryption"; + case MZ_ZIP_UNSUPPORTED_FEATURE: + return "unsupported feature"; + case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: + return "failed finding central directory"; + case MZ_ZIP_NOT_AN_ARCHIVE: + return "not a ZIP archive"; + case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: + return "invalid header or archive is corrupted"; + case MZ_ZIP_UNSUPPORTED_MULTIDISK: + return "unsupported multidisk archive"; + case MZ_ZIP_DECOMPRESSION_FAILED: + return "decompression failed or archive is corrupted"; + case MZ_ZIP_COMPRESSION_FAILED: + return "compression failed"; + case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: + return "unexpected decompressed size"; + case MZ_ZIP_CRC_CHECK_FAILED: + return "CRC-32 check failed"; + case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: + return "unsupported central directory size"; + case MZ_ZIP_ALLOC_FAILED: + return "allocation failed"; + case MZ_ZIP_FILE_OPEN_FAILED: + return "file open failed"; + case MZ_ZIP_FILE_CREATE_FAILED: + return "file create failed"; + case MZ_ZIP_FILE_WRITE_FAILED: + return "file write failed"; + case MZ_ZIP_FILE_READ_FAILED: + return "file read failed"; + case MZ_ZIP_FILE_CLOSE_FAILED: + return "file close failed"; + case MZ_ZIP_FILE_SEEK_FAILED: + return "file seek failed"; + case MZ_ZIP_FILE_STAT_FAILED: + return "file stat failed"; + case MZ_ZIP_INVALID_PARAMETER: + return "invalid parameter"; + case MZ_ZIP_INVALID_FILENAME: + return "invalid filename"; + case MZ_ZIP_BUF_TOO_SMALL: + return "buffer too small"; + case MZ_ZIP_INTERNAL_ERROR: + return "internal error"; + case MZ_ZIP_FILE_NOT_FOUND: + return "file not found"; + case MZ_ZIP_ARCHIVE_TOO_LARGE: + return "archive is too large"; + case MZ_ZIP_VALIDATION_FAILED: + return "validation failed"; + case MZ_ZIP_WRITE_CALLBACK_FAILED: + return "write callback failed"; + case MZ_ZIP_TOTAL_ERRORS: + return "total errors"; + default: + break; + } + + return "unknown error"; +} + +/* Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 extended information extra field, argh. */ +mz_bool mz_zip_is_zip64(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return MZ_FALSE; + + return pZip->m_pState->m_zip64; +} + +size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + + return pZip->m_pState->m_central_dir.m_size; +} + +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_total_files : 0; +} + +mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip) +{ + if (!pZip) + return 0; + return pZip->m_archive_size; +} + +mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_file_archive_start_ofs; +} + +MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_pFile; +} + +size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); +} + +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + if (filename_buf_size) + pFilename[0] = '\0'; + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return 0; + } + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) + { + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; + } + return n + 1; +} + +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +{ + return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); +} + +mz_bool mz_zip_end(mz_zip_archive *pZip) +{ + if (!pZip) + return MZ_FALSE; + + if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) + return mz_zip_reader_end(pZip); +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) + return mz_zip_writer_end(pZip); +#endif + + return MZ_FALSE; +} + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ diff --git a/thirdparty/miniz/miniz.h b/thirdparty/miniz/miniz.h new file mode 100644 index 0000000..9fcfffc --- /dev/null +++ b/thirdparty/miniz/miniz.h @@ -0,0 +1,1422 @@ +#ifndef MINIZ_EXPORT +#define MINIZ_EXPORT +#endif +/* miniz.c 3.0.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich , last updated Oct. 13, 2013 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Low-level Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory + block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateReset/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. + Supports raw deflate streams or standard zlib streams with adler-32 checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is to call this function: + + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, + const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 + + * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz + uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files + (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). +*/ +#pragma once + + + +/* Defines to completely disable specific portions of miniz.c: + If all macros here are defined the only functionality remaining will be CRC-32 and adler-32. */ + +/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ +/*#define MINIZ_NO_STDIO */ + +/* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */ +/* get/set file times, and the C run-time funcs that get/set times won't be called. */ +/* The current downside is the times written to your archives will be from 1979. */ +/*#define MINIZ_NO_TIME */ + +/* Define MINIZ_NO_DEFLATE_APIS to disable all compression API's. */ +/*#define MINIZ_NO_DEFLATE_APIS */ + +/* Define MINIZ_NO_INFLATE_APIS to disable all decompression API's. */ +/*#define MINIZ_NO_INFLATE_APIS */ + +/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_APIS */ + +/* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */ +/*#define MINIZ_NO_ZLIB_APIS */ + +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ +/*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. + Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc + callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user + functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ +/*#define MINIZ_NO_MALLOC */ + +#ifdef MINIZ_NO_INFLATE_APIS +#define MINIZ_NO_ARCHIVE_APIS +#endif + +#ifdef MINIZ_NO_DEFLATE_APIS +#define MINIZ_NO_ARCHIVE_WRITING_APIS +#endif + +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) +/* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ +#define MINIZ_NO_TIME +#endif + +#include + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) +#include +#endif + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ +#define MINIZ_X86_OR_X64_CPU 1 +#else +#define MINIZ_X86_OR_X64_CPU 0 +#endif + +/* Set MINIZ_LITTLE_ENDIAN only if not set */ +#if !defined(MINIZ_LITTLE_ENDIAN) +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif + +#else + +#if MINIZ_X86_OR_X64_CPU +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif + +#endif +#endif + +/* Using unaligned loads and stores causes errors when using UBSan */ +#if defined(__has_feature) +#if __has_feature(undefined_behavior_sanitizer) +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif + +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ +#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) +#if MINIZ_X86_OR_X64_CPU +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#define MINIZ_UNALIGNED_USE_MEMCPY +#else +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */ +#define MINIZ_HAS_64BIT_REGISTERS 1 +#else +#define MINIZ_HAS_64BIT_REGISTERS 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API Definitions. */ + +/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ +typedef unsigned long mz_ulong; + +/* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ +MINIZ_EXPORT void mz_free(void *p); + +#define MZ_ADLER32_INIT (1) +/* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ +MINIZ_EXPORT mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +/* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ +MINIZ_EXPORT mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +/* Compression strategies. */ +enum +{ + MZ_DEFAULT_STRATEGY = 0, + MZ_FILTERED = 1, + MZ_HUFFMAN_ONLY = 2, + MZ_RLE = 3, + MZ_FIXED = 4 +}; + +/* Method */ +#define MZ_DEFLATED 8 + +/* Heap allocation callbacks. +Note that mz_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. */ +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +/* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ +enum +{ + MZ_NO_COMPRESSION = 0, + MZ_BEST_SPEED = 1, + MZ_BEST_COMPRESSION = 9, + MZ_UBER_COMPRESSION = 10, + MZ_DEFAULT_LEVEL = 6, + MZ_DEFAULT_COMPRESSION = -1 +}; + +#define MZ_VERSION "11.0.2" +#define MZ_VERNUM 0xB002 +#define MZ_VER_MAJOR 11 +#define MZ_VER_MINOR 2 +#define MZ_VER_REVISION 0 +#define MZ_VER_SUBREVISION 0 + +#ifndef MINIZ_NO_ZLIB_APIS + +/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */ +enum +{ + MZ_NO_FLUSH = 0, + MZ_PARTIAL_FLUSH = 1, + MZ_SYNC_FLUSH = 2, + MZ_FULL_FLUSH = 3, + MZ_FINISH = 4, + MZ_BLOCK = 5 +}; + +/* Return status codes. MZ_PARAM_ERROR is non-standard. */ +enum +{ + MZ_OK = 0, + MZ_STREAM_END = 1, + MZ_NEED_DICT = 2, + MZ_ERRNO = -1, + MZ_STREAM_ERROR = -2, + MZ_DATA_ERROR = -3, + MZ_MEM_ERROR = -4, + MZ_BUF_ERROR = -5, + MZ_VERSION_ERROR = -6, + MZ_PARAM_ERROR = -10000 +}; + +/* Window bits */ +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +/* Compression/decompression stream struct. */ +typedef struct mz_stream_s +{ + const unsigned char *next_in; /* pointer to next byte to read */ + unsigned int avail_in; /* number of bytes available at next_in */ + mz_ulong total_in; /* total number of bytes consumed so far */ + + unsigned char *next_out; /* pointer to next byte to write */ + unsigned int avail_out; /* number of bytes that can be written to next_out */ + mz_ulong total_out; /* total number of bytes produced so far */ + + char *msg; /* error msg (unused) */ + struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ + + mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ + mz_free_func zfree; /* optional heap free function (defaults to free) */ + void *opaque; /* heap alloc function user pointer */ + + int data_type; /* data_type (unused) */ + mz_ulong adler; /* adler32 of the source or uncompressed data */ + mz_ulong reserved; /* not used */ +} mz_stream; + +typedef mz_stream *mz_streamp; + +/* Returns the version string of miniz.c. */ +MINIZ_EXPORT const char *mz_version(void); + +#ifndef MINIZ_NO_DEFLATE_APIS + +/* mz_deflateInit() initializes a compressor with default options: */ +/* Parameters: */ +/* pStream must point to an initialized mz_stream struct. */ +/* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ +/* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */ +/* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if the input parameters are bogus. */ +/* MZ_MEM_ERROR on out of memory. */ +MINIZ_EXPORT int mz_deflateInit(mz_streamp pStream, int level); + +/* mz_deflateInit2() is like mz_deflate(), except with more control: */ +/* Additional parameters: */ +/* method must be MZ_DEFLATED */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ +/* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ +MINIZ_EXPORT int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ +MINIZ_EXPORT int mz_deflateReset(mz_streamp pStream); + +/* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */ +/* Return values: */ +/* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */ +/* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ +MINIZ_EXPORT int mz_deflate(mz_streamp pStream, int flush); + +/* mz_deflateEnd() deinitializes a compressor: */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +MINIZ_EXPORT int mz_deflateEnd(mz_streamp pStream); + +/* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ +MINIZ_EXPORT mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +/* Single-call compression functions mz_compress() and mz_compress2(): */ +/* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ +MINIZ_EXPORT int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ +MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len); + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS + +/* Initializes a decompressor. */ +MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream); + +/* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ +MINIZ_EXPORT int mz_inflateInit2(mz_streamp pStream, int window_bits); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */ +MINIZ_EXPORT int mz_inflateReset(mz_streamp pStream); + +/* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ +/* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */ +/* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */ +/* Return values: */ +/* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */ +/* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_DATA_ERROR if the deflate stream is invalid. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ +/* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ +MINIZ_EXPORT int mz_inflate(mz_streamp pStream, int flush); + +/* Deinitializes a decompressor. */ +MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream); + +/* Single-call decompression. */ +/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ +MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len); +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +/* Returns a string description of the specified error code, or NULL if the error code is invalid. */ +MINIZ_EXPORT const char *mz_error(int err); + +/* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef mz_ulong uLong; +typedef Byte Bytef; +typedef uInt uIntf; +typedef char charf; +typedef int intf; +typedef void *voidpf; +typedef uLong uLongf; +typedef void *voidp; +typedef void *const voidpc; +#define Z_NULL 0 +#define Z_NO_FLUSH MZ_NO_FLUSH +#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH +#define Z_SYNC_FLUSH MZ_SYNC_FLUSH +#define Z_FULL_FLUSH MZ_FULL_FLUSH +#define Z_FINISH MZ_FINISH +#define Z_BLOCK MZ_BLOCK +#define Z_OK MZ_OK +#define Z_STREAM_END MZ_STREAM_END +#define Z_NEED_DICT MZ_NEED_DICT +#define Z_ERRNO MZ_ERRNO +#define Z_STREAM_ERROR MZ_STREAM_ERROR +#define Z_DATA_ERROR MZ_DATA_ERROR +#define Z_MEM_ERROR MZ_MEM_ERROR +#define Z_BUF_ERROR MZ_BUF_ERROR +#define Z_VERSION_ERROR MZ_VERSION_ERROR +#define Z_PARAM_ERROR MZ_PARAM_ERROR +#define Z_NO_COMPRESSION MZ_NO_COMPRESSION +#define Z_BEST_SPEED MZ_BEST_SPEED +#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION +#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION +#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY +#define Z_FILTERED MZ_FILTERED +#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY +#define Z_RLE MZ_RLE +#define Z_FIXED MZ_FIXED +#define Z_DEFLATED MZ_DEFLATED +#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS +#define alloc_func mz_alloc_func +#define free_func mz_free_func +#define internal_state mz_internal_state +#define z_stream mz_stream + +#ifndef MINIZ_NO_DEFLATE_APIS +#define deflateInit mz_deflateInit +#define deflateInit2 mz_deflateInit2 +#define deflateReset mz_deflateReset +#define deflate mz_deflate +#define deflateEnd mz_deflateEnd +#define deflateBound mz_deflateBound +#define compress mz_compress +#define compress2 mz_compress2 +#define compressBound mz_compressBound +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS +#define inflateInit mz_inflateInit +#define inflateInit2 mz_inflateInit2 +#define inflateReset mz_inflateReset +#define inflate mz_inflate +#define inflateEnd mz_inflateEnd +#define uncompress mz_uncompress +#define uncompress2 mz_uncompress2 +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +#define crc32 mz_crc32 +#define adler32 mz_adler32 +#define MAX_WBITS 15 +#define MAX_MEM_LEVEL 9 +#define zError mz_error +#define ZLIB_VERSION MZ_VERSION +#define ZLIB_VERNUM MZ_VERNUM +#define ZLIB_VER_MAJOR MZ_VER_MAJOR +#define ZLIB_VER_MINOR MZ_VER_MINOR +#define ZLIB_VER_REVISION MZ_VER_REVISION +#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION +#define zlibVersion mz_version +#define zlib_version mz_version() +#endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +#endif /* MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif + + + + + +#pragma once +#include +#include +#include +#include + + + +/* ------------------- Types and macros */ +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef int64_t mz_int64; +typedef uint64_t mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */ +#ifdef _MSC_VER +#define MZ_MACRO_END while (0, 0) +#else +#define MZ_MACRO_END while (0) +#endif + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include +#define MZ_FILE FILE +#endif /* #ifdef MINIZ_NO_STDIO */ + +#ifdef MINIZ_NO_TIME +typedef struct mz_dummy_time_t_tag +{ + mz_uint32 m_dummy1; + mz_uint32 m_dummy2; +} mz_dummy_time_t; +#define MZ_TIME_T mz_dummy_time_t +#else +#define MZ_TIME_T time_t +#endif + +#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC +#define MZ_MALLOC(x) NULL +#define MZ_FREE(x) (void)x, ((void)0) +#define MZ_REALLOC(p, x) NULL +#else +#define MZ_MALLOC(x) malloc(x) +#define MZ_FREE(x) free(x) +#define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) +#define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj)) +#define MZ_CLEAR_PTR(obj) memset((obj), 0, sizeof(*obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) +#define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else +#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) +#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif + +#define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U)) + +#ifdef _MSC_VER +#define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) +#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) +#else +#define MZ_FORCEINLINE inline +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); +extern MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address); +extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); + +#define MZ_UINT16_MAX (0xFFFFU) +#define MZ_UINT32_MAX (0xFFFFFFFFU) + +#ifdef __cplusplus +} +#endif + #pragma once + + +#ifndef MINIZ_NO_DEFLATE_APIS + +#ifdef __cplusplus +extern "C" { +#endif +/* ------------------- Low-level Compression API Definitions */ + +/* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */ +#define TDEFL_LESS_MEMORY 0 + +/* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */ +/* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */ +enum +{ + TDEFL_HUFFMAN_ONLY = 0, + TDEFL_DEFAULT_MAX_PROBES = 128, + TDEFL_MAX_PROBES_MASK = 0xFFF +}; + +/* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */ +/* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */ +/* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */ +/* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */ +/* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ +/* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ +/* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ +/* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ +/* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +/* High level compression functions: */ +/* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ +/* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must free() the returned block when it's no longer needed. */ +MINIZ_EXPORT void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ +/* Returns 0 on failure. */ +MINIZ_EXPORT size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* Compresses an image to a compressed PNG file in memory. */ +/* On entry: */ +/* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */ +/* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */ +/* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ +/* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pLen_out will be set to the size of the PNG image file. */ +/* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +/* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); + +/* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ +MINIZ_EXPORT mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +enum +{ + TDEFL_MAX_HUFF_TABLES = 3, + TDEFL_MAX_HUFF_SYMBOLS_0 = 288, + TDEFL_MAX_HUFF_SYMBOLS_1 = 32, + TDEFL_MAX_HUFF_SYMBOLS_2 = 19, + TDEFL_LZ_DICT_SIZE = 32768, + TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, + TDEFL_MIN_MATCH_LEN = 3, + TDEFL_MAX_MATCH_LEN = 258 +}; + +/* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */ +#if TDEFL_LESS_MEMORY +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 12, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#else +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 15, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#endif + +/* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */ +typedef enum { + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1 +} tdefl_status; + +/* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ +typedef enum { + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +/* tdefl's compression state structure. */ +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +/* Initializes the compressor. */ +/* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */ +/* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ +/* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ +/* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ +MINIZ_EXPORT tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +/* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ +MINIZ_EXPORT tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + +/* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ +/* tdefl_compress_buffer() always consumes the entire input buffer. */ +MINIZ_EXPORT tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +MINIZ_EXPORT tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +MINIZ_EXPORT mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +/* Create tdefl_compress() flags given zlib-style compression parameters. */ +/* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ +/* window_bits may be -15 (raw deflate) or 15 (zlib) */ +/* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ +MINIZ_EXPORT mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tdefl_compressor structure in C so that */ +/* non-C language bindings to tdefl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +MINIZ_EXPORT tdefl_compressor *tdefl_compressor_alloc(void); +MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + #pragma once + +/* ------------------- Low-level Decompression API Definitions */ + +#ifndef MINIZ_NO_INFLATE_APIS + +#ifdef __cplusplus +extern "C" { +#endif +/* Decompression flags used by tinfl_decompress(). */ +/* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */ +/* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */ +/* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */ +/* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */ +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +/* High level decompression functions: */ +/* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */ +/* On return: */ +/* Function returns a pointer to the decompressed data, or NULL on failure. */ +/* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must call mz_free() on the returned block when it's no longer needed. */ +MINIZ_EXPORT void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ +/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +MINIZ_EXPORT size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ +/* Returns 1 on success or 0 on failure. */ +typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); +MINIZ_EXPORT int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; +typedef struct tinfl_decompressor_tag tinfl_decompressor; + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tinfl_decompressor structure in C so that */ +/* non-C language bindings to tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +MINIZ_EXPORT tinfl_decompressor *tinfl_decompressor_alloc(void); +MINIZ_EXPORT void tinfl_decompressor_free(tinfl_decompressor *pDecomp); +#endif + +/* Max size of LZ dictionary. */ +#define TINFL_LZ_DICT_SIZE 32768 + +/* Return status. */ +typedef enum { + /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ + /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ + /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ + TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, + + /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ + TINFL_STATUS_BAD_PARAM = -3, + + /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ + TINFL_STATUS_ADLER32_MISMATCH = -2, + + /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ + TINFL_STATUS_FAILED = -1, + + /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ + + /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ + /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ + TINFL_STATUS_DONE = 0, + + /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ + /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ + /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + + /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ + /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ + /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ + /* so I may need to add some code to address this. */ + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +/* Initializes the decompressor to its initial state. */ +#define tinfl_init(r) \ + do \ + { \ + (r)->m_state = 0; \ + } \ + MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ +/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ +MINIZ_EXPORT tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); + +/* Internal/private bits follow. */ +enum +{ + TINFL_MAX_HUFF_TABLES = 3, + TINFL_MAX_HUFF_SYMBOLS_0 = 288, + TINFL_MAX_HUFF_SYMBOLS_1 = 32, + TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, + TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +#if MINIZ_HAS_64BIT_REGISTERS +#define TINFL_USE_64BIT_BITBUF 1 +#else +#define TINFL_USE_64BIT_BITBUF 0 +#endif + +#if TINFL_USE_64BIT_BITBUF +typedef mz_uint64 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (64) +#else +typedef mz_uint32 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + mz_int16 m_look_up[TINFL_MAX_HUFF_TABLES][TINFL_FAST_LOOKUP_SIZE]; + mz_int16 m_tree_0[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; + mz_int16 m_tree_1[TINFL_MAX_HUFF_SYMBOLS_1 * 2]; + mz_int16 m_tree_2[TINFL_MAX_HUFF_SYMBOLS_2 * 2]; + mz_uint8 m_code_size_0[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_uint8 m_code_size_1[TINFL_MAX_HUFF_SYMBOLS_1]; + mz_uint8 m_code_size_2[TINFL_MAX_HUFF_SYMBOLS_2]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +#pragma once + + +/* ------------------- ZIP archive reading/writing */ + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ + MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 +}; + +typedef struct +{ + /* Central directory file index. */ + mz_uint32 m_file_index; + + /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ + mz_uint64 m_central_dir_ofs; + + /* These fields are copied directly from the zip's central dir. */ + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; + + /* CRC-32 of uncompressed data. */ + mz_uint32 m_crc32; + + /* File's compressed size. */ + mz_uint64 m_comp_size; + + /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ + mz_uint64 m_uncomp_size; + + /* Zip internal and external file attributes. */ + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + + /* Entry's local header file offset in bytes. */ + mz_uint64 m_local_header_ofs; + + /* Size of comment in bytes. */ + mz_uint32 m_comment_size; + + /* MZ_TRUE if the entry appears to be a directory. */ + mz_bool m_is_directory; + + /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ + mz_bool m_is_encrypted; + + /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ + mz_bool m_is_supported; + + /* Filename. If string ends in '/' it's a subdirectory entry. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + + /* Comment field. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; + +#ifdef MINIZ_NO_TIME + MZ_TIME_T m_padding; +#else + MZ_TIME_T m_time; +#endif +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); +typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum { + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef enum { + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, + MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ + MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ + MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ + MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, + MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000, + /*After adding a compressed file, seek back + to local file header and set the correct sizes*/ + MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE = 0x20000 +} mz_zip_flags; + +typedef enum { + MZ_ZIP_TYPE_INVALID = 0, + MZ_ZIP_TYPE_USER, + MZ_ZIP_TYPE_MEMORY, + MZ_ZIP_TYPE_HEAP, + MZ_ZIP_TYPE_FILE, + MZ_ZIP_TYPE_CFILE, + MZ_ZIP_TOTAL_TYPES +} mz_zip_type; + +/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ +typedef enum { + MZ_ZIP_NO_ERROR = 0, + MZ_ZIP_UNDEFINED_ERROR, + MZ_ZIP_TOO_MANY_FILES, + MZ_ZIP_FILE_TOO_LARGE, + MZ_ZIP_UNSUPPORTED_METHOD, + MZ_ZIP_UNSUPPORTED_ENCRYPTION, + MZ_ZIP_UNSUPPORTED_FEATURE, + MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, + MZ_ZIP_NOT_AN_ARCHIVE, + MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, + MZ_ZIP_UNSUPPORTED_MULTIDISK, + MZ_ZIP_DECOMPRESSION_FAILED, + MZ_ZIP_COMPRESSION_FAILED, + MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, + MZ_ZIP_CRC_CHECK_FAILED, + MZ_ZIP_UNSUPPORTED_CDIR_SIZE, + MZ_ZIP_ALLOC_FAILED, + MZ_ZIP_FILE_OPEN_FAILED, + MZ_ZIP_FILE_CREATE_FAILED, + MZ_ZIP_FILE_WRITE_FAILED, + MZ_ZIP_FILE_READ_FAILED, + MZ_ZIP_FILE_CLOSE_FAILED, + MZ_ZIP_FILE_SEEK_FAILED, + MZ_ZIP_FILE_STAT_FAILED, + MZ_ZIP_INVALID_PARAMETER, + MZ_ZIP_INVALID_FILENAME, + MZ_ZIP_BUF_TOO_SMALL, + MZ_ZIP_INTERNAL_ERROR, + MZ_ZIP_FILE_NOT_FOUND, + MZ_ZIP_ARCHIVE_TOO_LARGE, + MZ_ZIP_VALIDATION_FAILED, + MZ_ZIP_WRITE_CALLBACK_FAILED, + MZ_ZIP_TOTAL_ERRORS +} mz_zip_error; + +typedef struct +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + + /* We only support up to UINT32_MAX files in zip64 mode. */ + mz_uint32 m_total_files; + mz_zip_mode m_zip_mode; + mz_zip_type m_zip_type; + mz_zip_error m_last_error; + + mz_uint64 m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + mz_file_needs_keepalive m_pNeeds_keepalive; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef struct +{ + mz_zip_archive *pZip; + mz_uint flags; + + int status; + + mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + void *pWrite_buf; + + size_t out_blk_remain; + + tinfl_decompressor inflator; + +#ifdef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint padding; +#else + mz_uint file_crc32; +#endif + +} mz_zip_reader_extract_iter_state; + +/* -------- ZIP reading */ + +/* Inits a ZIP archive reader. */ +/* These functions read and validate the archive's central directory. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); + +MINIZ_EXPORT mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +/* Read a archive from a disk file. */ +/* file_start_ofs is the file offset where the archive actually begins, or 0. */ +/* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); + +/* Read an archive from an already opened FILE, beginning at the current file position. */ +/* The archive is assumed to be archive_size bytes long. If archive_size is 0, then the entire rest of the file is assumed to contain the archive. */ +/* The FILE will NOT be closed when mz_zip_reader_end() is called. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); +#endif + +/* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ +MINIZ_EXPORT mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +/* -------- ZIP reading or writing */ + +/* Clears a mz_zip_archive struct to all zeros. */ +/* Important: This must be done before passing the struct to any mz_zip functions. */ +MINIZ_EXPORT void mz_zip_zero_struct(mz_zip_archive *pZip); + +MINIZ_EXPORT mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); + +/* Returns the total number of files in the archive. */ +MINIZ_EXPORT mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +MINIZ_EXPORT mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); +MINIZ_EXPORT mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); +MINIZ_EXPORT MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); + +/* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ +MINIZ_EXPORT size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); + +/* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ +/* Note that the m_last_error functionality is not thread safe. */ +MINIZ_EXPORT mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); +MINIZ_EXPORT mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT const char *mz_zip_get_error_string(mz_zip_error mz_err); + +/* MZ_TRUE if the archive file entry is a directory entry. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the file is encrypted/strong encrypted. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); + +/* Retrieves the filename of an archive file entry. */ +/* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ +MINIZ_EXPORT mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +/* Attempts to locates a file in the archive's central directory. */ +/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ +/* Returns -1 if the file cannot be found. */ +MINIZ_EXPORT int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); + +/* Returns detailed information about an archive file entry. */ +MINIZ_EXPORT mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +/* MZ_TRUE if the file is in zip64 format. */ +/* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ +MINIZ_EXPORT mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); + +/* Returns the total central directory size in bytes. */ +/* The current max supported size is <= MZ_UINT32_MAX. */ +MINIZ_EXPORT size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); + +/* Extracts a archive file to a memory buffer using no memory allocation. */ +/* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +/* Extracts a archive file to a memory buffer. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +/* Extracts a archive file to a dynamically allocated heap buffer. */ +/* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ +/* Returns NULL and sets the last error on failure. */ +MINIZ_EXPORT void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +MINIZ_EXPORT void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +/* Extracts a archive file using a callback function to output the file's data. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + +/* Extract a file iteratively */ +MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); +MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); +MINIZ_EXPORT size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); + +#ifndef MINIZ_NO_STDIO +/* Extracts a archive file to a disk file and sets its last accessed and modified times. */ +/* This function only extracts files, not archive directory records. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); + +/* Extracts a archive file starting at the current position in the destination FILE stream. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); +#endif + +#if 0 +/* TODO */ + typedef void *mz_zip_streaming_extract_state_ptr; + mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + mz_uint64 mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_uint64 mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, mz_uint64 new_ofs); + size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); + mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); +#endif + +/* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */ +/* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ +MINIZ_EXPORT mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + +/* Validates an entire archive by calling mz_zip_validate_file() on each file. */ +MINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); + +/* Misc utils/helpers, valid for ZIP reading or writing */ +MINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); +#ifndef MINIZ_NO_STDIO +MINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); +#endif + +/* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ +MINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip); + +/* -------- ZIP writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +/* Inits a ZIP archive writer. */ +/*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ +/*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ +MINIZ_EXPORT mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +MINIZ_EXPORT mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); + +MINIZ_EXPORT mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); +MINIZ_EXPORT mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +MINIZ_EXPORT mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +MINIZ_EXPORT mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); +#endif + +/* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ +/* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ +/* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */ +/* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ +/* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ +/* the archive is finalized the file's central directory will be hosed. */ +MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); +MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); + +/* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ +/* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ +/* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); + +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + +/* Adds the contents of a file to an archive. This function also records the disk file's modified time into the archive. */ +/* File data is supplied via a read callback function. User mz_zip_writer_add_(c)file to add a file directly.*/ +MINIZ_EXPORT mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + + +#ifndef MINIZ_NO_STDIO +/* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); +#endif + +/* Adds a file to an archive by fully cloning the data from another archive. */ +/* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); + +/* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ +/* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ +/* An archive must be manually finalized by calling this function for it to be valid. */ +MINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); + +/* Finalizes a heap archive, returning a pointer to the heap block and its size. */ +/* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ +MINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); + +/* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ +/* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ +MINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +/* -------- Misc. high-level helper functions: */ + +/* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ +/* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +/* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ +MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); + +#ifndef MINIZ_NO_STDIO +/* Reads a single file from an archive into a heap block. */ +/* If pComment is not NULL, only the file with the specified comment will be extracted. */ +/* Returns NULL on failure. */ +MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); +MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); +#endif + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifdef __cplusplus +} +#endif + +#endif /* MINIZ_NO_ARCHIVE_APIS */ diff --git a/thirdparty/miniz/readme.md b/thirdparty/miniz/readme.md new file mode 100644 index 0000000..9734435 --- /dev/null +++ b/thirdparty/miniz/readme.md @@ -0,0 +1,46 @@ +## Miniz + +Miniz is a lossless, high performance data compression library in a single source file that implements the zlib (RFC 1950) and Deflate (RFC 1951) compressed data format specification standards. It supports the most commonly used functions exported by the zlib library, but is a completely independent implementation so zlib's licensing requirements do not apply. Miniz also contains simple to use functions for writing .PNG format image files and reading/writing/appending .ZIP format archives. Miniz's compression speed has been tuned to be comparable to zlib's, and it also has a specialized real-time compressor function designed to compare well against fastlz/minilzo. + +## Usage + +Releases are available at the [releases page](https://github.com/richgel999/miniz/releases) as a pair of `miniz.c`/`miniz.h` files which can be simply added to a project. To create this file pair the different source and header files are [amalgamated](https://www.sqlite.org/amalgamation.html) during build. Alternatively use as cmake or meson module (or build system of your choice). + +## Features + +* MIT licensed +* A portable, single source and header file library written in plain C. Tested with GCC, clang and Visual Studio. +* Easily tuned and trimmed down by defines +* A drop-in replacement for zlib's most used API's (tested in several open source projects that use zlib, such as libpng and libzip). +* Fills a single threaded performance vs. compression ratio gap between several popular real-time compressors and zlib. For example, at level 1, miniz.c compresses around 5-9% better than minilzo, but is approx. 35% slower. At levels 2-9, miniz.c is designed to compare favorably against zlib's ratio and speed. See the miniz performance comparison page for example timings. +* Not a block based compressor: miniz.c fully supports stream based processing using a coroutine-style implementation. The zlib-style API functions can be called a single byte at a time if that's all you've got. +* Easy to use. The low-level compressor (tdefl) and decompressor (tinfl) have simple state structs which can be saved/restored as needed with simple memcpy's. The low-level codec API's don't use the heap in any way. +* Entire inflater (including optional zlib header parsing and Adler-32 checking) is implemented in a single function as a coroutine, which is separately available in a small (~550 line) source file: miniz_tinfl.c +* A fairly complete (but totally optional) set of .ZIP archive manipulation and extraction API's. The archive functionality is intended to solve common problems encountered in embedded, mobile, or game development situations. (The archive API's are purposely just powerful enough to write an entire archiver given a bit of additional higher-level logic.) + +## Building miniz - Using vcpkg + +You can download and install miniz using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: + + git clone https://github.com/Microsoft/vcpkg.git + cd vcpkg + ./bootstrap-vcpkg.sh + ./vcpkg integrate install + ./vcpkg install miniz + +The miniz port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. + +## Known Problems + +* No support for encrypted archives. Not sure how useful this stuff is in practice. +* Minimal documentation. The assumption is that the user is already familiar with the basic zlib API. I need to write an API wiki - for now I've tried to place key comments before each enum/API, and I've included 6 examples that demonstrate how to use the module's major features. + +## Special Thanks + +Thanks to Alex Evans for the PNG writer function. Also, thanks to Paul Holden and Thorsten Scheuermann for feedback and testing, Matt Pritchard for all his encouragement, and Sean Barrett's various public domain libraries for inspiration (and encouraging me to write miniz.c in C, which was much more enjoyable and less painful than I thought it would be considering I've been programming in C++ for so long). + +Thanks to Bruce Dawson for reporting a problem with the level_and_flags archive API parameter (which is fixed in v1.12) and general feedback, and Janez Zemva for indirectly encouraging me into writing more examples. + +## Patents + +I was recently asked if miniz avoids patent issues. miniz purposely uses the same core algorithms as the ones used by zlib. The compressor uses vanilla hash chaining as described [here](https://datatracker.ietf.org/doc/html/rfc1951#section-4). Also see the [gzip FAQ](https://web.archive.org/web/20160308045258/http://www.gzip.org/#faq11). In my opinion, if miniz falls prey to a patent attack then zlib/gzip are likely to be at serious risk too. diff --git a/thirdparty/regexp/regexp.c b/thirdparty/regexp/regexp.c new file mode 100644 index 0000000..f0ec373 --- /dev/null +++ b/thirdparty/regexp/regexp.c @@ -0,0 +1,1176 @@ +#ifndef REGEXP_MALLOC +#define REGEXP_MALLOC malloc +#endif +#ifndef REGEXP_FREE +#define REGEXP_FREE free +#endif + +#include +#include +#include +#include +#include + +#include "regexp.h" + +#define nelem(a) (sizeof (a) / sizeof (a)[0]) + +typedef unsigned int Rune; + +static int isalpharune(Rune c) +{ + /* TODO: Add unicode support */ + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); +} + +static Rune toupperrune(Rune c) +{ + /* TODO: Add unicode support */ + if (c >= 'a' && c <= 'z') + return c - 'a' + 'A'; + return c; +} + +static int chartorune(Rune *r, const char *s) +{ + /* TODO: Add UTF-8 decoding */ + *r = *s; + return 1; +} + +#define REPINF 255 +#define MAXSUB REG_MAXSUB +#define MAXPROG (32 << 10) + +typedef struct Reclass Reclass; +typedef struct Renode Renode; +typedef struct Reinst Reinst; +typedef struct Rethread Rethread; + +struct Reclass { + Rune *end; + Rune spans[64]; +}; + +struct Reprog { + Reinst *start, *end; + int flags; + unsigned int nsub; + Reclass cclass[16]; +}; + +static struct { + Reprog *prog; + Renode *pstart, *pend; + + const char *source; + unsigned int ncclass; + unsigned int nsub; + Renode *sub[MAXSUB]; + + int lookahead; + Rune yychar; + Reclass *yycc; + int yymin, yymax; + + const char *error; + jmp_buf kaboom; +} g; + +static void die(const char *message) +{ + g.error = message; + longjmp(g.kaboom, 1); +} + +static Rune canon(Rune c) +{ + Rune u = toupperrune(c); + if (c >= 128 && u < 128) + return c; + return u; +} + +/* Scan */ + +enum { + L_CHAR = 256, + L_CCLASS, /* character class */ + L_NCCLASS, /* negative character class */ + L_NC, /* "(?:" no capture */ + L_PLA, /* "(?=" positive lookahead */ + L_NLA, /* "(?!" negative lookahead */ + L_WORD, /* "\b" word boundary */ + L_NWORD, /* "\B" non-word boundary */ + L_REF, /* "\1" back-reference */ + L_COUNT /* {M,N} */ +}; + +static int hex(int c) +{ + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 0xA; + if (c >= 'A' && c <= 'F') return c - 'A' + 0xA; + die("invalid escape sequence"); + return 0; +} + +static int dec(int c) +{ + if (c >= '0' && c <= '9') return c - '0'; + die("invalid quantifier"); + return 0; +} + +#define ESCAPES "BbDdSsWw^$\\.*+?()[]{}|0123456789" + +static int isunicodeletter(int c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || isalpharune(c); +} + +static int nextrune(void) +{ + g.source += chartorune(&g.yychar, g.source); + if (g.yychar == '\\') { + g.source += chartorune(&g.yychar, g.source); + switch (g.yychar) { + case 0: die("unterminated escape sequence"); break; + case 'f': g.yychar = '\f'; return 0; + case 'n': g.yychar = '\n'; return 0; + case 'r': g.yychar = '\r'; return 0; + case 't': g.yychar = '\t'; return 0; + case 'v': g.yychar = '\v'; return 0; + case 'c': + g.yychar = (*g.source++) & 31; + return 0; + case 'x': + g.yychar = hex(*g.source++) << 4; + g.yychar += hex(*g.source++); + if (g.yychar == 0) { + g.yychar = '0'; + return 1; + } + return 0; + case 'u': + g.yychar = hex(*g.source++) << 12; + g.yychar += hex(*g.source++) << 8; + g.yychar += hex(*g.source++) << 4; + g.yychar += hex(*g.source++); + if (g.yychar == 0) { + g.yychar = '0'; + return 1; + } + return 0; + } + if (strchr(ESCAPES, g.yychar)) + return 1; + if (isunicodeletter(g.yychar) || g.yychar == '_') /* check identity escape */ + die("invalid escape character"); + return 0; + } + return 0; +} + +static int lexcount(void) +{ + g.yychar = *g.source++; + + g.yymin = dec(g.yychar); + g.yychar = *g.source++; + while (g.yychar != ',' && g.yychar != '}') { + g.yymin = g.yymin * 10 + dec(g.yychar); + g.yychar = *g.source++; + } + if (g.yymin >= REPINF) + die("numeric overflow"); + + if (g.yychar == ',') { + g.yychar = *g.source++; + if (g.yychar == '}') { + g.yymax = REPINF; + } else { + g.yymax = dec(g.yychar); + g.yychar = *g.source++; + while (g.yychar != '}') { + g.yymax = g.yymax * 10 + dec(g.yychar); + g.yychar = *g.source++; + } + if (g.yymax >= REPINF) + die("numeric overflow"); + } + } else { + g.yymax = g.yymin; + } + + return L_COUNT; +} + +static void newcclass(void) +{ + if (g.ncclass >= nelem(g.prog->cclass)) + die("too many character classes"); + g.yycc = g.prog->cclass + g.ncclass++; + g.yycc->end = g.yycc->spans; +} + +static void addrange(Rune a, Rune b) +{ + if (a > b) + die("invalid character class range"); + if (g.yycc->end + 2 == g.yycc->spans + nelem(g.yycc->spans)) + die("too many character class ranges"); + *g.yycc->end++ = a; + *g.yycc->end++ = b; +} + +static void addranges_d(void) +{ + addrange('0', '9'); +} + +static void addranges_D(void) +{ + addrange(0, '0'-1); + addrange('9'+1, 0xFFFF); +} + +static void addranges_s(void) +{ + addrange(0x9, 0x9); + addrange(0xA, 0xD); + addrange(0x20, 0x20); + addrange(0xA0, 0xA0); + addrange(0x2028, 0x2029); + addrange(0xFEFF, 0xFEFF); +} + +static void addranges_S(void) +{ + addrange(0, 0x9-1); + addrange(0x9+1, 0xA-1); + addrange(0xD+1, 0x20-1); + addrange(0x20+1, 0xA0-1); + addrange(0xA0+1, 0x2028-1); + addrange(0x2029+1, 0xFEFF-1); + addrange(0xFEFF+1, 0xFFFF); +} + +static void addranges_w(void) +{ + addrange('0', '9'); + addrange('A', 'Z'); + addrange('_', '_'); + addrange('a', 'z'); +} + +static void addranges_W(void) +{ + addrange(0, '0'-1); + addrange('9'+1, 'A'-1); + addrange('Z'+1, '_'-1); + addrange('_'+1, 'a'-1); + addrange('z'+1, 0xFFFF); +} + +static int lexclass(void) +{ + int type = L_CCLASS; + int quoted, havesave, havedash; + Rune save = 0; + + newcclass(); + + quoted = nextrune(); + if (!quoted && g.yychar == '^') { + type = L_NCCLASS; + quoted = nextrune(); + } + + havesave = havedash = 0; + for (;;) { + if (g.yychar == 0) + die("unterminated character class"); + if (!quoted && g.yychar == ']') + break; + + if (!quoted && g.yychar == '-') { + if (havesave) { + if (havedash) { + addrange(save, '-'); + havesave = havedash = 0; + } else { + havedash = 1; + } + } else { + save = '-'; + havesave = 1; + } + } else if (quoted && strchr("DSWdsw", g.yychar)) { + if (havesave) { + addrange(save, save); + if (havedash) + addrange('-', '-'); + } + switch (g.yychar) { + case 'd': addranges_d(); break; + case 's': addranges_s(); break; + case 'w': addranges_w(); break; + case 'D': addranges_D(); break; + case 'S': addranges_S(); break; + case 'W': addranges_W(); break; + } + havesave = havedash = 0; + } else { + if (quoted) { + if (g.yychar == 'b') + g.yychar = '\b'; + else if (g.yychar == '0') + g.yychar = 0; + /* else identity escape */ + } + if (havesave) { + if (havedash) { + addrange(save, g.yychar); + havesave = havedash = 0; + } else { + addrange(save, save); + save = g.yychar; + } + } else { + save = g.yychar; + havesave = 1; + } + } + + quoted = nextrune(); + } + + if (havesave) { + addrange(save, save); + if (havedash) + addrange('-', '-'); + } + + return type; +} + +static int lex(void) +{ + int quoted = nextrune(); + if (quoted) { + switch (g.yychar) { + case 'b': return L_WORD; + case 'B': return L_NWORD; + case 'd': newcclass(); addranges_d(); return L_CCLASS; + case 's': newcclass(); addranges_s(); return L_CCLASS; + case 'w': newcclass(); addranges_w(); return L_CCLASS; + case 'D': newcclass(); addranges_d(); return L_NCCLASS; + case 'S': newcclass(); addranges_s(); return L_NCCLASS; + case 'W': newcclass(); addranges_w(); return L_NCCLASS; + case '0': g.yychar = 0; return L_CHAR; + } + if (g.yychar >= '0' && g.yychar <= '9') { + g.yychar -= '0'; + if (*g.source >= '0' && *g.source <= '9') + g.yychar = g.yychar * 10 + *g.source++ - '0'; + return L_REF; + } + return L_CHAR; + } + + switch (g.yychar) { + case 0: + case '$': case ')': case '*': case '+': + case '.': case '?': case '^': case '|': + return g.yychar; + } + + if (g.yychar == '{') + return lexcount(); + if (g.yychar == '[') + return lexclass(); + if (g.yychar == '(') { + if (g.source[0] == '?') { + if (g.source[1] == ':') { + g.source += 2; + return L_NC; + } + if (g.source[1] == '=') { + g.source += 2; + return L_PLA; + } + if (g.source[1] == '!') { + g.source += 2; + return L_NLA; + } + } + return '('; + } + + return L_CHAR; +} + +/* Parse */ + +enum { + P_CAT, P_ALT, P_REP, + P_BOL, P_EOL, P_WORD, P_NWORD, + P_PAR, P_PLA, P_NLA, + P_ANY, P_CHAR, P_CCLASS, P_NCCLASS, + P_REF +}; + +struct Renode { + unsigned char type; + unsigned char ng, m, n; + Rune c; + Reclass *cc; + Renode *x; + Renode *y; +}; + +static Renode *newnode(int type) +{ + Renode *node = g.pend++; + node->type = type; + node->cc = NULL; + node->c = 0; + node->ng = 0; + node->m = 0; + node->n = 0; + node->x = node->y = NULL; + return node; +} + +static int empty(Renode *node) +{ + if (!node) return 1; + switch (node->type) { + default: return 1; + case P_CAT: return empty(node->x) && empty(node->y); + case P_ALT: return empty(node->x) || empty(node->y); + case P_REP: return empty(node->x) || node->m == 0; + case P_PAR: return empty(node->x); + case P_REF: return empty(node->x); + case P_ANY: case P_CHAR: case P_CCLASS: case P_NCCLASS: return 0; + } +} + +static Renode *newrep(Renode *atom, int ng, int min, int max) +{ + Renode *rep = newnode(P_REP); + if (max == REPINF && empty(atom)) + die("infinite loop matching the empty string"); + rep->ng = ng; + rep->m = min; + rep->n = max; + rep->x = atom; + return rep; +} + +static void next(void) +{ + g.lookahead = lex(); +} + +static int accept(int t) +{ + if (g.lookahead == t) { + next(); + return 1; + } + return 0; +} + +static Renode *parsealt(void); + +static Renode *parseatom(void) +{ + Renode *atom; + if (g.lookahead == L_CHAR) { + atom = newnode(P_CHAR); + atom->c = g.yychar; + next(); + return atom; + } + if (g.lookahead == L_CCLASS) { + atom = newnode(P_CCLASS); + atom->cc = g.yycc; + next(); + return atom; + } + if (g.lookahead == L_NCCLASS) { + atom = newnode(P_NCCLASS); + atom->cc = g.yycc; + next(); + return atom; + } + if (g.lookahead == L_REF) { + atom = newnode(P_REF); + if (g.yychar == 0 || g.yychar > g.nsub || !g.sub[g.yychar]) + die("invalid back-reference"); + atom->n = g.yychar; + atom->x = g.sub[g.yychar]; + next(); + return atom; + } + if (accept('.')) + return newnode(P_ANY); + if (accept('(')) { + atom = newnode(P_PAR); + if (g.nsub == MAXSUB) + die("too many captures"); + atom->n = g.nsub++; + atom->x = parsealt(); + g.sub[atom->n] = atom; + if (!accept(')')) + die("unmatched '('"); + return atom; + } + if (accept(L_NC)) { + atom = parsealt(); + if (!accept(')')) + die("unmatched '('"); + return atom; + } + if (accept(L_PLA)) { + atom = newnode(P_PLA); + atom->x = parsealt(); + if (!accept(')')) + die("unmatched '('"); + return atom; + } + if (accept(L_NLA)) { + atom = newnode(P_NLA); + atom->x = parsealt(); + if (!accept(')')) + die("unmatched '('"); + return atom; + } + die("syntax error"); + return NULL; +} + +static Renode *parserep(void) +{ + Renode *atom; + + if (accept('^')) return newnode(P_BOL); + if (accept('$')) return newnode(P_EOL); + if (accept(L_WORD)) return newnode(P_WORD); + if (accept(L_NWORD)) return newnode(P_NWORD); + + atom = parseatom(); + if (g.lookahead == L_COUNT) { + int min = g.yymin, max = g.yymax; + next(); + if (max < min) + die("invalid quantifier"); + return newrep(atom, accept('?'), min, max); + } + if (accept('*')) return newrep(atom, accept('?'), 0, REPINF); + if (accept('+')) return newrep(atom, accept('?'), 1, REPINF); + if (accept('?')) return newrep(atom, accept('?'), 0, 1); + return atom; +} + +static Renode *parsecat(void) +{ + Renode *cat, *head, **tail; + if (g.lookahead && g.lookahead != '|' && g.lookahead != ')') { + /* Build a right-leaning tree by splicing in new 'cat' at the tail. */ + head = parserep(); + tail = &head; + while (g.lookahead && g.lookahead != '|' && g.lookahead != ')') { + cat = newnode(P_CAT); + cat->x = *tail; + cat->y = parserep(); + *tail = cat; + tail = &cat->y; + } + return head; + } + return NULL; +} + +static Renode *parsealt(void) +{ + Renode *alt, *x; + alt = parsecat(); + while (accept('|')) { + x = alt; + alt = newnode(P_ALT); + alt->x = x; + alt->y = parsecat(); + } + return alt; +} + +/* Compile */ + +enum { + I_END, I_JUMP, I_SPLIT, I_PLA, I_NLA, + I_ANYNL, I_ANY, I_CHAR, I_CCLASS, I_NCCLASS, I_REF, + I_BOL, I_EOL, I_WORD, I_NWORD, + I_LPAR, I_RPAR +}; + +struct Reinst { + unsigned char opcode; + unsigned char n; + Rune c; + Reclass *cc; + Reinst *x; + Reinst *y; +}; + +static unsigned int count(Renode *node) +{ + unsigned int min, max, n; + if (!node) return 0; + switch (node->type) { + default: return 1; + case P_CAT: return count(node->x) + count(node->y); + case P_ALT: return count(node->x) + count(node->y) + 2; + case P_REP: + min = node->m; + max = node->n; + if (min == max) n = count(node->x) * min; + else if (max < REPINF) n = count(node->x) * max + (max - min); + else n = count(node->x) * (min + 1) + 2; + if (n > MAXPROG) die("program too large"); + return n; + case P_PAR: return count(node->x) + 2; + case P_PLA: return count(node->x) + 2; + case P_NLA: return count(node->x) + 2; + } +} + +static Reinst *emit(Reprog *prog, int opcode) +{ + Reinst *inst = prog->end++; + inst->opcode = opcode; + inst->n = 0; + inst->c = 0; + inst->cc = NULL; + inst->x = inst->y = NULL; + return inst; +} + +static void compile(Reprog *prog, Renode *node) +{ + Reinst *inst, *split, *jump; + unsigned int i; + + if (!node) + return; + +loop: + switch (node->type) { + case P_CAT: + compile(prog, node->x); + node = node->y; + goto loop; + + case P_ALT: + split = emit(prog, I_SPLIT); + compile(prog, node->x); + jump = emit(prog, I_JUMP); + compile(prog, node->y); + split->x = split + 1; + split->y = jump + 1; + jump->x = prog->end; + break; + + case P_REP: + for (i = 0; i < node->m; ++i) { + inst = prog->end; + compile(prog, node->x); + } + if (node->m == node->n) + break; + if (node->n < REPINF) { + for (i = node->m; i < node->n; ++i) { + split = emit(prog, I_SPLIT); + compile(prog, node->x); + if (node->ng) { + split->y = split + 1; + split->x = prog->end; + } else { + split->x = split + 1; + split->y = prog->end; + } + } + } else if (node->m == 0) { + split = emit(prog, I_SPLIT); + compile(prog, node->x); + jump = emit(prog, I_JUMP); + if (node->ng) { + split->y = split + 1; + split->x = prog->end; + } else { + split->x = split + 1; + split->y = prog->end; + } + jump->x = split; + } else { + split = emit(prog, I_SPLIT); + if (node->ng) { + split->y = inst; + split->x = prog->end; + } else { + split->x = inst; + split->y = prog->end; + } + } + break; + + case P_BOL: emit(prog, I_BOL); break; + case P_EOL: emit(prog, I_EOL); break; + case P_WORD: emit(prog, I_WORD); break; + case P_NWORD: emit(prog, I_NWORD); break; + + case P_PAR: + inst = emit(prog, I_LPAR); + inst->n = node->n; + compile(prog, node->x); + inst = emit(prog, I_RPAR); + inst->n = node->n; + break; + case P_PLA: + split = emit(prog, I_PLA); + compile(prog, node->x); + emit(prog, I_END); + split->x = split + 1; + split->y = prog->end; + break; + case P_NLA: + split = emit(prog, I_NLA); + compile(prog, node->x); + emit(prog, I_END); + split->x = split + 1; + split->y = prog->end; + break; + + case P_ANY: + emit(prog, I_ANY); + break; + case P_CHAR: + inst = emit(prog, I_CHAR); + inst->c = (prog->flags & REG_ICASE) ? canon(node->c) : node->c; + break; + case P_CCLASS: + inst = emit(prog, I_CCLASS); + inst->cc = node->cc; + break; + case P_NCCLASS: + inst = emit(prog, I_NCCLASS); + inst->cc = node->cc; + break; + case P_REF: + inst = emit(prog, I_REF); + inst->n = node->n; + break; + } +} + +#ifdef TEST +static void dumpnode(Renode *node) +{ + Rune *p; + if (!node) { printf("Empty"); return; } + switch (node->type) { + case P_CAT: printf("Cat("); dumpnode(node->x); printf(", "); dumpnode(node->y); printf(")"); break; + case P_ALT: printf("Alt("); dumpnode(node->x); printf(", "); dumpnode(node->y); printf(")"); break; + case P_REP: + printf(node->ng ? "NgRep(%d,%d," : "Rep(%d,%d,", node->m, node->n); + dumpnode(node->x); + printf(")"); + break; + case P_BOL: printf("Bol"); break; + case P_EOL: printf("Eol"); break; + case P_WORD: printf("Word"); break; + case P_NWORD: printf("NotWord"); break; + case P_PAR: printf("Par(%d,", node->n); dumpnode(node->x); printf(")"); break; + case P_PLA: printf("PLA("); dumpnode(node->x); printf(")"); break; + case P_NLA: printf("NLA("); dumpnode(node->x); printf(")"); break; + case P_ANY: printf("Any"); break; + case P_CHAR: printf("Char(%c)", node->c); break; + case P_CCLASS: + printf("Class("); + for (p = node->cc->spans; p < node->cc->end; p += 2) printf("%02X-%02X,", p[0], p[1]); + printf(")"); + break; + case P_NCCLASS: + printf("NotClass("); + for (p = node->cc->spans; p < node->cc->end; p += 2) printf("%02X-%02X,", p[0], p[1]); + printf(")"); + break; + case P_REF: printf("Ref(%d)", node->n); break; + } +} + +static void dumpprog(Reprog *prog) +{ + Reinst *inst; + int i; + for (i = 0, inst = prog->start; inst < prog->end; ++i, ++inst) { + printf("% 5d: ", i); + switch (inst->opcode) { + case I_END: puts("end"); break; + case I_JUMP: printf("jump %d\n", (int)(inst->x - prog->start)); break; + case I_SPLIT: printf("split %d %d\n", (int)(inst->x - prog->start), (int)(inst->y - prog->start)); break; + case I_PLA: printf("pla %d %d\n", (int)(inst->x - prog->start), (int)(inst->y - prog->start)); break; + case I_NLA: printf("nla %d %d\n", (int)(inst->x - prog->start), (int)(inst->y - prog->start)); break; + case I_ANY: puts("any"); break; + case I_ANYNL: puts("anynl"); break; + case I_CHAR: printf(inst->c >= 32 && inst->c < 127 ? "char '%c'\n" : "char U+%04X\n", inst->c); break; + case I_CCLASS: puts("cclass"); break; + case I_NCCLASS: puts("ncclass"); break; + case I_REF: printf("ref %d\n", inst->n); break; + case I_BOL: puts("bol"); break; + case I_EOL: puts("eol"); break; + case I_WORD: puts("word"); break; + case I_NWORD: puts("nword"); break; + case I_LPAR: printf("lpar %d\n", inst->n); break; + case I_RPAR: printf("rpar %d\n", inst->n); break; + } + } +} +#endif + +Reprog *regcomp(const char *pattern, int cflags, const char **errorp) +{ + Renode *node; + Reinst *split, *jump; + int i, n; + + g.pstart = NULL; + g.prog = NULL; + + if (setjmp(g.kaboom)) { + if (errorp) *errorp = g.error; + REGEXP_FREE(g.pstart); + REGEXP_FREE(g.prog); + return NULL; + } + + g.prog = REGEXP_MALLOC(sizeof (Reprog)); + if (!g.prog) + die("cannot allocate regular expression"); + n = strlen(pattern) * 2; + if (n > 0) { + g.pstart = g.pend = REGEXP_MALLOC(sizeof (Renode) * n); + if (!g.pstart) + die("cannot allocate regular expression parse list"); + } + + g.source = pattern; + g.ncclass = 0; + g.nsub = 1; + for (i = 0; i < MAXSUB; ++i) + g.sub[i] = 0; + + g.prog->flags = cflags; + + next(); + node = parsealt(); + if (g.lookahead == ')') + die("unmatched ')'"); + if (g.lookahead != 0) + die("syntax error"); + +#ifdef TEST + dumpnode(node); + putchar('\n'); +#endif + + n = 6 + count(node); + if (n < 0 || n > MAXPROG) + die("program too large"); + + g.prog->nsub = g.nsub; + g.prog->start = g.prog->end = REGEXP_MALLOC(n * sizeof (Reinst)); + + split = emit(g.prog, I_SPLIT); + split->x = split + 3; + split->y = split + 1; + emit(g.prog, I_ANYNL); + jump = emit(g.prog, I_JUMP); + jump->x = split; + emit(g.prog, I_LPAR); + compile(g.prog, node); + emit(g.prog, I_RPAR); + emit(g.prog, I_END); + +#ifdef TEST + dumpprog(g.prog); +#endif + + REGEXP_FREE(g.pstart); + + if (errorp) *errorp = NULL; + return g.prog; +} + +void regREGEXP_FREE(Reprog *prog) +{ + if (prog) { + REGEXP_FREE(prog->start); + REGEXP_FREE(prog); + } +} + +/* Match */ + +static int isnewline(int c) +{ + return c == 0xA || c == 0xD || c == 0x2028 || c == 0x2029; +} + +static int iswordchar(int c) +{ + return c == '_' || + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9'); +} + +static int incclass(Reclass *cc, Rune c) +{ + Rune *p; + for (p = cc->spans; p < cc->end; p += 2) + if (p[0] <= c && c <= p[1]) + return 1; + return 0; +} + +static int incclasscanon(Reclass *cc, Rune c) +{ + Rune *p, r; + for (p = cc->spans; p < cc->end; p += 2) + for (r = p[0]; r <= p[1]; ++r) + if (c == canon(r)) + return 1; + return 0; +} + +static int strncmpcanon(const char *a, const char *b, unsigned int n) +{ + Rune ra, rb; + int c; + while (n--) { + if (!*a) return -1; + if (!*b) return 1; + a += chartorune(&ra, a); + b += chartorune(&rb, b); + c = canon(ra) - canon(rb); + if (c) + return c; + } + return 0; +} + +static int match(Reinst *pc, const char *sp, const char *bol, int flags, Resub *out) +{ + Resub scratch; + int i; + Rune c; + + for (;;) { + switch (pc->opcode) { + case I_END: + return 1; + case I_JUMP: + pc = pc->x; + break; + case I_SPLIT: + scratch = *out; + if (match(pc->x, sp, bol, flags, &scratch)) { + *out = scratch; + return 1; + } + pc = pc->y; + break; + + case I_PLA: + if (!match(pc->x, sp, bol, flags, out)) + return 0; + pc = pc->y; + break; + case I_NLA: + scratch = *out; + if (match(pc->x, sp, bol, flags, &scratch)) + return 0; + pc = pc->y; + break; + + case I_ANYNL: + sp += chartorune(&c, sp); + if (c == 0) + return 0; + pc = pc + 1; + break; + case I_ANY: + sp += chartorune(&c, sp); + if (c == 0) + return 0; + if (isnewline(c)) + return 0; + pc = pc + 1; + break; + case I_CHAR: + sp += chartorune(&c, sp); + if (c == 0) + return 0; + if (flags & REG_ICASE) + c = canon(c); + if (c != pc->c) + return 0; + pc = pc + 1; + break; + case I_CCLASS: + sp += chartorune(&c, sp); + if (c == 0) + return 0; + if (flags & REG_ICASE) { + if (!incclasscanon(pc->cc, canon(c))) + return 0; + } else { + if (!incclass(pc->cc, c)) + return 0; + } + pc = pc + 1; + break; + case I_NCCLASS: + sp += chartorune(&c, sp); + if (c == 0) + return 0; + if (flags & REG_ICASE) { + if (incclasscanon(pc->cc, canon(c))) + return 0; + } else { + if (incclass(pc->cc, c)) + return 0; + } + pc = pc + 1; + break; + case I_REF: + i = out->sub[pc->n].ep - out->sub[pc->n].sp; + if (flags & REG_ICASE) { + if (strncmpcanon(sp, out->sub[pc->n].sp, i)) + return 0; + } else { + if (strncmp(sp, out->sub[pc->n].sp, i)) + return 0; + } + if (i > 0) + sp += i; + pc = pc + 1; + break; + + case I_BOL: + if (sp == bol && !(flags & REG_NOTBOL)) { + pc = pc + 1; + break; + } + if (flags & REG_NEWLINE) { + if (sp > bol && isnewline(sp[-1])) { + pc = pc + 1; + break; + } + } + return 0; + case I_EOL: + if (*sp == 0) { + pc = pc + 1; + break; + } + if (flags & REG_NEWLINE) { + if (isnewline(*sp)) { + pc = pc + 1; + break; + } + } + return 0; + case I_WORD: + i = sp > bol && iswordchar(sp[-1]); + i ^= iswordchar(sp[0]); + if (!i) + return 0; + pc = pc + 1; + break; + case I_NWORD: + i = sp > bol && iswordchar(sp[-1]); + i ^= iswordchar(sp[0]); + if (i) + return 0; + pc = pc + 1; + break; + + case I_LPAR: + out->sub[pc->n].sp = sp; + pc = pc + 1; + break; + case I_RPAR: + out->sub[pc->n].ep = sp; + pc = pc + 1; + break; + default: + return 0; + } + } +} + +int regexec(Reprog *prog, const char *sp, Resub *sub, int eflags) +{ + Resub scratch; + int i; + + if (!sub) + sub = &scratch; + + sub->nsub = prog->nsub; + for (i = 0; i < MAXSUB; ++i) + sub->sub[i].sp = sub->sub[i].ep = NULL; + + return !match(prog->start, sp, sp, prog->flags | eflags, sub); +} + +#ifdef TEST +int main(int argc, char **argv) +{ + const char *error; + const char *s; + Reprog *p; + Resub m; + unsigned int i; + + if (argc > 1) { + p = regcomp(argv[1], 0, &error); + if (!p) { + fprintf(stderr, "regcomp: %s\n", error); + return 1; + } + + if (argc > 2) { + s = argv[2]; + printf("nsub = %d\n", p->nsub); + if (!regexec(p, s, &m, 0)) { + for (i = 0; i < m.nsub; ++i) { + int n = m.sub[i].ep - m.sub[i].sp; + if (n > 0) + printf("match %d: s=%d e=%d n=%d '%.*s'\n", i, (int)(m.sub[i].sp - s), (int)(m.sub[i].ep - s), n, n, m.sub[i].sp); + else + printf("match %d: n=0 ''\n", i); + } + } else { + printf("no match\n"); + } + } + } + + return 0; +} +#endif \ No newline at end of file diff --git a/thirdparty/regexp/regexp.h b/thirdparty/regexp/regexp.h new file mode 100644 index 0000000..374e684 --- /dev/null +++ b/thirdparty/regexp/regexp.h @@ -0,0 +1,31 @@ +#ifndef regexp_h +#define regexp_h + +typedef struct Reprog Reprog; +typedef struct Resub Resub; + +Reprog *regcomp(const char *pattern, int cflags, const char **errorp); +int regexec(Reprog *prog, const char *string, Resub *sub, int eflags); +void regfree(Reprog *prog); + +enum { + /* regcomp flags */ + REG_ICASE = 1, + REG_NEWLINE = 2, + + /* regexec flags */ + REG_NOTBOL = 4, + + /* limits */ + REG_MAXSUB = 10 +}; + +struct Resub { + unsigned int nsub; + struct { + const char *sp; + const char *ep; + } sub[REG_MAXSUB]; +}; + +#endif \ No newline at end of file diff --git a/thirdparty/sokol/.editorconfig b/thirdparty/sokol/.editorconfig new file mode 100644 index 0000000..2e85449 --- /dev/null +++ b/thirdparty/sokol/.editorconfig @@ -0,0 +1,9 @@ +root=true +[**] +indent_style=space +indent_size=4 +trim_trailing_whitespace=true +insert_final_newline=true + +[*.yml] +indent_size=2 diff --git a/thirdparty/sokol/.github/workflows/gen_bindings.yml b/thirdparty/sokol/.github/workflows/gen_bindings.yml new file mode 100644 index 0000000..73586bd --- /dev/null +++ b/thirdparty/sokol/.github/workflows/gen_bindings.yml @@ -0,0 +1,405 @@ +name: Bindings + +on: [push, pull_request] + +jobs: + test-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - name: test_win + run: | + cd tests + test_win.cmd + shell: cmd + + test-mac: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + - uses: seanmiddleditch/gha-setup-ninja@master + - name: test_macos + run: | + cd tests + ./test_macos.sh + + test-linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: seanmiddleditch/gha-setup-ninja@master + - name: prepare + run: | + sudo apt-get update + sudo apt-get install libgl1-mesa-dev libegl1-mesa-dev mesa-common-dev xorg-dev libasound-dev + - name: test_linux + run: | + cd tests + ./test_linux.sh + + gen-bindings: + needs: [ test-windows, test-mac, test-linux ] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/checkout@v4 + with: + repository: floooh/sokol-zig + path: bindgen/sokol-zig + - uses: actions/checkout@v4 + with: + repository: floooh/sokol-nim + path: bindgen/sokol-nim + - uses: actions/checkout@v4 + with: + repository: floooh/sokol-odin + path: bindgen/sokol-odin + - uses: actions/checkout@v4 + with: + repository: floooh/sokol-rust + path: bindgen/sokol-rust + - uses: actions/checkout@v4 + with: + repository: kassane/sokol-d + path: bindgen/sokol-d + - name: generate + run: | + cd bindgen + python3 gen_all.py + - name: upload-zig-artifact + uses: actions/upload-artifact@v4 + with: + name: ignore-me-zig + retention-days: 1 + path: bindgen/sokol-zig/src/sokol + - name: upload-nim-artifact + uses: actions/upload-artifact@v4 + with: + name: ignore-me-nim + retention-days: 1 + path: bindgen/sokol-nim/src/sokol + - name: upload-odin-artifact + uses: actions/upload-artifact@v4 + with: + name: ignore-me-odin + retention-days: 1 + path: | + bindgen/sokol-odin/sokol + bindgen/sokol-odin/c + - name: upload-rust-artifact + uses: actions/upload-artifact@v4 + with: + name: ignore-me-rust + retention-days: 1 + path: bindgen/sokol-rust/src + - name: upload-d-artifact + uses: actions/upload-artifact@v3 + with: + name: ignore-me-d + retention-days: 1 + path: bindgen/sokol-d/src/sokol + + test-zig: + needs: gen-bindings + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{matrix.os}} + steps: + - uses: actions/checkout@v4 + with: + repository: floooh/sokol-zig + - uses: goto-bus-stop/setup-zig@v2 + - uses: actions/download-artifact@v4 + with: + name: ignore-me-zig + path: src/sokol + - name: prepare + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install libgl1-mesa-dev libegl1-mesa-dev mesa-common-dev xorg-dev libasound-dev + - name: build + run: zig build + + test-nim: + needs: gen-bindings + strategy: + fail-fast: false + matrix: + #os: [ubuntu-latest, macos-latest, windows-latest] + os: [ubuntu-latest, macos-latest ] + runs-on: ${{matrix.os}} + steps: + - uses: jiro4989/setup-nim-action@v1 + with: + nim-version: '2.x' + repo-token: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/checkout@v4 + with: + repository: floooh/sokol-nim + - uses: actions/download-artifact@v4 + with: + name: ignore-me-nim + path: src/sokol + - if: runner.os == 'Linux' + name: prepare + run: | + sudo apt-get update + sudo apt-get install libgl1-mesa-dev libegl1-mesa-dev mesa-common-dev xorg-dev libasound-dev + - name: build + run: | + nimble install -Y + nimble install glm -Y + nimble build_all + + test-odin: + needs: gen-bindings + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{matrix.os}} + steps: + - uses: actions/checkout@v4 + with: + repository: floooh/sokol-odin + - uses: actions/download-artifact@v4 + with: + name: ignore-me-odin + # NOTE: see https://github.com/floooh/sokol-odin/blob/main/.github/workflows/main.yml + - uses: ilammy/msvc-dev-cmd@v1 + - if: runner.os == 'Linux' + name: prepare-linux + run: | + sudo apt-get update + sudo apt-get install libglu1-mesa-dev mesa-common-dev xorg-dev libasound-dev + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + echo "/usr/lib/llvm-17/bin" >> $GITHUB_PATH + curl -L https://github.com/odin-lang/Odin/releases/download/dev-2024-04/odin-ubuntu-amd64-dev-2024-04.zip --output odin.zip + unzip odin.zip + unzip dist.zip + rm -r ./dist/examples + mv ./dist/* ./ + chmod a+x ./odin + cd sokol + chmod a+x ./build_clibs_linux.sh + ./build_clibs_linux.sh + cd .. + - if: runner.os == 'macOS' + name: prepare-macos + run: | + brew install llvm@17 + curl -L https://github.com/odin-lang/Odin/releases/download/dev-2024-04/odin-macos-amd64-dev-2024-04.zip --output odin.zip + unzip odin.zip + unzip dist.zip + rm -r ./dist/examples + mv ./dist/* ./ + chmod a+x ./odin + cd sokol + chmod a+x ./build_clibs_macos.sh + ./build_clibs_macos.sh + cd .. + - if: runner.os == 'Windows' + name: prepare-windows + shell: cmd + run: | + curl -L https://github.com/odin-lang/Odin/releases/download/dev-2024-04/odin-windows-amd64-dev-2024-04.zip --output odin.zip + unzip odin.zip + cd sokol + build_clibs_windows.cmd + cd .. + - name: build + run: | + ./odin build examples/clear -debug + ./odin build examples/triangle -debug + ./odin build examples/quad -debug + ./odin build examples/bufferoffsets -debug + ./odin build examples/cube -debug + ./odin build examples/noninterleaved -debug + ./odin build examples/texcube -debug + ./odin build examples/shapes -debug + ./odin build examples/offscreen -debug + ./odin build examples/instancing -debug + ./odin build examples/mrt -debug + ./odin build examples/blend -debug + ./odin build examples/debugtext -debug + ./odin build examples/debugtext-print -debug + ./odin build examples/debugtext-userfont -debug + ./odin build examples/saudio -debug + ./odin build examples/sgl -debug + ./odin build examples/sgl-points -debug + ./odin build examples/sgl-context -debug + + test-rust: + needs: gen-bindings + env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse + strategy: + fail-fast: false + matrix: + # os: [ubuntu-latest, macos-latest, windows-latest] + os: [ubuntu-latest, windows-latest] + runs-on: ${{matrix.os}} + steps: + - uses: actions/checkout@v4 + with: + repository: floooh/sokol-rust + - uses: actions/download-artifact@v4 + with: + name: ignore-me-rust + path: src + - uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + - name: prepare-linux + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install libglu1-mesa-dev mesa-common-dev xorg-dev libasound-dev + - name: build + run: | + cargo --version + cargo build --examples --verbose + + test-d: + needs: gen-bindings + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{matrix.os}} + steps: + - uses: actions/checkout@v4 + with: + repository: kassane/sokol-d + - uses: goto-bus-stop/setup-zig@v2 + with: + version: 0.12.0 + - uses: dlang-community/setup-dlang@v1 + with: + compiler: ldc-master + - uses: actions/download-artifact@v3 + with: + name: ignore-me-d + path: src/sokol + - name: prepare + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install libgl1-mesa-dev libegl1-mesa-dev mesa-common-dev xorg-dev libasound-dev + - name: build + run: zig build --summary all + + # only deploy the bindings for commits on the main branch + deploy-zig: + needs: test-zig + if: github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + repository: floooh/sokol-zig + ssh-key: ${{ secrets.GHACTIONS_ZIG_PUSH }} + - uses: actions/download-artifact@v4 + with: + name: ignore-me-zig + path: src/sokol + - name: "commit and push" + run: | + git config user.email "none" + git config user.name "GH Action" + git add -A + git diff-index --quiet HEAD || git commit -m "updated (https://github.com/floooh/sokol/commit/${{ github.sha }})" + git push + + deploy-nim: + needs: test-nim + if: github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + repository: floooh/sokol-nim + ssh-key: ${{ secrets.GHACTIONS_NIM_PUSH }} + - uses: actions/download-artifact@v4 + with: + name: ignore-me-nim + path: src/sokol + - name: "commit and push" + run: | + git config user.email "none" + git config user.name "GH Action" + git add -A + git diff-index --quiet HEAD || git commit -m "updated (https://github.com/floooh/sokol/commit/${{ github.sha }})" + git push + + deploy-odin: + needs: test-odin + if: github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + repository: floooh/sokol-odin + ssh-key: ${{ secrets.GHACTIONS_ODIN_PUSH }} + - uses: actions/download-artifact@v4 + with: + name: ignore-me-odin + - name: "commit and push" + run: | + git config user.email "none" + git config user.name "GH Action" + git add -A + git diff-index --quiet HEAD || git commit -m "updated (https://github.com/floooh/sokol/commit/${{ github.sha }})" + git push + + deploy-rust: + needs: test-rust + env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse + if: github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + repository: floooh/sokol-rust + ssh-key: ${{ secrets.GHACTIONS_RUST_PUSH }} + - uses: actions/download-artifact@v4 + with: + name: ignore-me-rust + path: src + - uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + - name: "cargo fmt" + run: cargo fmt + - name: "commit and push" + run: | + git config user.email "none" + git config user.name "GH Action" + git status -vv + git add -A + git diff-index --quiet HEAD || git commit -m "updated (https://github.com/floooh/sokol/commit/${{ github.sha }})" + git push + + deploy-d: + needs: test-d + if: github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + repository: kassane/sokol-d + ssh-key: ${{ secrets.GHACTIONS_D_PUSH }} + - uses: actions/download-artifact@v3 + with: + name: ignore-me-d + path: src/sokol + - name: "commit and push" + run: | + git config user.email "none" + git config user.name "GH Action" + git add -A + git diff-index --quiet HEAD || git commit -m "updated (https://github.com/floooh/sokol/commit/${{ github.sha }})" + git push diff --git a/thirdparty/sokol/.github/workflows/main.yml b/thirdparty/sokol/.github/workflows/main.yml new file mode 100644 index 0000000..4d7ddf3 --- /dev/null +++ b/thirdparty/sokol/.github/workflows/main.yml @@ -0,0 +1,65 @@ +name: "Build & Test" + +on: [push, pull_request] + +jobs: + windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - name: test_win + run: | + cd tests + test_win.cmd + shell: cmd + mac: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + - uses: seanmiddleditch/gha-setup-ninja@master + - name: test_macos + run: | + cd tests + ./test_macos.sh + ios: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + - name: test_ios + run: | + cd tests + ./test_ios.sh + linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: seanmiddleditch/gha-setup-ninja@master + - name: prepare + run: | + sudo apt-get update + sudo apt-get install libgl1-mesa-dev libegl1-mesa-dev mesa-common-dev xorg-dev libasound-dev + - name: test_linux + run: | + cd tests + ./test_linux.sh + emscripten: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: seanmiddleditch/gha-setup-ninja@master + - name: test_emscripten + run: | + cd tests + ./test_emscripten.sh + android: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: seanmiddleditch/gha-setup-ninja@master + - uses: actions/setup-java@v1 + with: + java-version: '8' + - name: test_android + run: | + cd tests + ./test_android.sh diff --git a/thirdparty/sokol/.gitignore b/thirdparty/sokol/.gitignore new file mode 100644 index 0000000..58627d6 --- /dev/null +++ b/thirdparty/sokol/.gitignore @@ -0,0 +1,7 @@ +.vscode/ +build/ +#>fips +# this area is managed by fips, do not edit +.fips-* +*.pyc +# NOTE: if you use sokol_gfx.h and sokol_app.h together, make sure to update both. This is +because the pixel format enum in sokol_gfx.h has been shuffled around a bit, and as a result, some internal +pixel format constants in sokol_app.h had to move too! + +- sokol_gfx.h: some minor new features (non-breaking): + - the struct `sg_pixel_format` has two new items: + - `bool compressed`: true if this is a hardware-compressed pixel format + - `int bytes_per_pixel`: as the name says, with the caveat that this is + zero for compressed pixel formats (because the smallest element in compressed formats is a block, not a pixel) + - two previously private helper functions have been exposed to help with size computations + for texture data, these may be useful when preparing image data for consumption by `sg_make_image()` + and `sg_update_image()`: + - `int sg_query_row_pitch(sg_pixel_format fmt, int width, int row_align_bytes)`: + Computes the number of bytes in a texture row for a given pixel format. A 'row' has + different meanings for uncompressed vs compressed formats: For uncompressed pixel + formats, a row is a single line of pixels, while for compressed formats, a row is + a line of 'compression blocks'. `width` is always in pixels. + - `int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes)`: + Computes number of bytes in a texture surface (e.g. a single mipmap) for a given + pixel format. `width` and `height` are always in pixels. + + The `row_align_bytes` parameter is for added flexibility. For image data that goes into + the `sg_make_image()` or `sg_update_image()` functions this should generally be 1, because these + functions take tightly packed image data as input no matter what alignment restrictions + exist in the backend 3D APIs. +- Related issue: https://github.com/floooh/sokol/issues/946, and PR: https://github.com/floooh/sokol/pull/962 + +#### 03-Jan-2024 + +- sokol_nuklear.h: `snk_handle_event()` now returns a bool to indicate whether the + event was handled by Nuklear (this allows an application to skip its own event + handling if Nuklear already handled the event). Issue link: https://github.com/floooh/sokol/issues/958, + fixed in PR: https://github.com/floooh/sokol/pull/959. Many thanks to @adamrt for the PR! + +#### 02-Jan-2024 + +Happy New Year! A couple of input-related changes in the sokol_app.h Emscripten backend: + +- Mouse and touch events now bubble up to the HTML document instead of being consumed, in some scenarios this + allows better integration with the surrounding web page. To prevent event bubbling, + call `sapp_consume_event()` from within the sokol_app.h event callback function. +- **NOTE**: wheel/scroll events behave as before and are always consumed. This prevents + an ugly "scroll bumping" effect when a wheel event bubbles up on a page where + scrolling shouldn't be possible. +- The hidden HTML text input field hack for text input on mobile browsers has been + removed. This idea never really worked across all browsers, and it actually + interfered with Dear ImGui text input fields because the hidden HTML text field + generated focus-in/out events which confused the Dear ImGui input handling code. + +Those changes fix a couple of problem when trying to integrate sokol_app.h applications +into VSCode webview panels, see: https://marketplace.visualstudio.com/items?itemName=floooh.vscode-kcide + +Related PR: https://github.com/floooh/sokol/pull/939 + +#### 10-Nov-2023 + +A small change in the sokol_gfx.h GL backend on Windows only: + +PR https://github.com/floooh/sokol/pull/839 has been merged, in debug mode this creates +the GL context with WGL_CONTEXT_DEBUG_BIT_ARB. Thanks to @castano for the PR! + +#### 06-Nov-2023 + +A bugfix in the sokol_gfx.h D3D11 backend, and some related cleanup when creating depth-stencil +render target images and resource views: + +- fixed: render target images with format SG_PIXELFORMAT_DEPTH_STENCIL triggered a validation + error because the pixel format capabilities code marked them as non-renderable. Now + the SG_PIXELFORMAT_DEPTH_STENCIL pixel format is properly reported as renderable. +- the DXGIFormats for SG_PIXELFORMAT_DEPTH_STENCIL images are now as follows: + - D3D11 texture object: DXGI_FORMAT_R24G8_TYPELESS + - D3D11 shader-resource-view object: DXGI_FORMAT_R24_UNORM_X8_TYPELESS + - D3D11 depth-stencil-view object: DXGI_FORMAT_D24_UNORM_S8_UINT + +Related PR: https://github.com/floooh/sokol/pull/937 + +#### 30-Oct-2023 + +Some sokol_gfx.h backend-specific updates and tweaks (very minor chance that this is breaking if you are injecting textures into the D3D11 backend). + +- a new set of public API functions to access the native backend 3D-API resource objects of + sokol-gfx resource objects: + + ``` + sg_[api]_[type]_info sg_[api]_query_[type]_info(sg_[type]) + ``` + ...where `[api]` is any of `[gl, d3d11, mtl, wgpu]` and `[type]` is any of `[buffer, image, sampler, shader, pipeline, pass]`. + + This is mainly useful when mixing native 3D-API code with sokol-gfx code. + + See issue https://github.com/floooh/sokol/issues/931 for details. + +- WebGPU backend: `sg_make_image()` will no longer automatically create a WebGPU texture-view object when injecting a WebGPU texture object, instead +this must now be explicitly provided. + +- D3D11 backend: `sg_make_image()` will no longer automatically create a +shader-resource-view object when injecting a D3D11 texture object, and +vice versa, a texture object will no longer be looked up from an injected +shader-resource-view object (e.g. the injection rules are now more straightforward and explicit). See issue https://github.com/floooh/sokol/issues/930 for details. + +For the detailed changes, see PR https://github.com/floooh/sokol/pull/932. + +#### 27-Oct-2023 + +Fix broken render-to-mipmap in the sokol_gfx.h GL backend. + +There was a subtle bug / "feature gap" lurking in sokol_gfx.h GL backend: trying +to render to any mipmap except the top-level mipmap resulted in a black screen +because of an incomplete-framebuffer error. This is fixed now. The changes in detail: + +- creating a texture in the GL backend now sets the GL_TEXTURE_MAX_LEVEL property + (this is the fix to make everything work) +- the framebuffer completeness check in the GL backend now has more detailed error logging +- in the validation layer, the requirement that a sampler that's used with a + single-mipmap-texture must use `.mipmap_filter = SG_FILTER_NONE` has been + relaxed (a later update will remove SG_FILTER_NONE entirely since it's not needed anymore + and the concept of a "none" mipmap filter only exists in GL and Metal, but not D3D, WebGPU + and Vulkan) + +Ticket: https://github.com/floooh/sokol/issues/923 + +PR: https://github.com/floooh/sokol/pull/924 + +There's also a new render-to-mipmap sample which covers to close this 'feature gap': + +https://floooh.github.io/sokol-html5/miprender-sapp.html + +A couple of similar samples will follow over the next few days +(rendering to texture array layers and 3d texture slices). + +#### 26-Oct-2023 + +- sokol_app.h gl: fix a regression introduced in https://github.com/floooh/sokol/pull/916 + which could select the wrong framebuffer pixel format and break rendering + on some GL drivers (in my case: an older Intel GPU). + + If you are using the GL backend on Windows, please make sure to upgrade! + +#### 23-Oct-2023 + +- sokol_app.h gl: some further startup optimizations in the WGL code path + via PR https://github.com/floooh/sokol/pull/916 + +#### 21-Oct-2023 + +The major topic of this update is the 'finalized' WebGPU support in sokol_gfx.h and sokol_app.h. + +- WebGPU samples are hosted here: + + https://floooh.github.io/sokol-webgpu/ + +- WebGL2 samples remain hosted here: + + https://floooh.github.io/sokol-html5/ + +- Please read the following blog post as introduction: + + https://floooh.github.io/2023/10/16/sokol-webgpu.html + +- ...and the changelog and updated documentation in the sokol-shdc repository: + + https://github.com/floooh/sokol-tools + +- You'll also need to update the sokol-shdc binaries: + + https://github.com/floooh/sokol-tools-bin + +- Please also read the following new or updated sections in the embedded sokol_gfx.h header documentation: + + - `ON SHADER CREATION` + - `ON SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT AND SG_SAMPLERTYPE_NONFILTERING` + - `WEBGPU CAVEATS` + + Please do this especially when using any of the following texture pixel formats, as you will most likely encounter new validation layer errors: + + - `SG_PIXELFORMAT_R32F` + - `SG_PIXELFORMAT_RG32F` + - `SG_PIXELFORMAT_RGBA32F` + +- There is a tiny breaking change in the sokol_gfx.h API (only requires action when not using sokol-shdc): + + - the following `sg_sampler_type` enum items have been renamed to better match their WebGPU counterparts: + - SG_SAMPLERTYPE_SAMPLE => SG_SAMPLERTYPE_FILTERING + - SG_SAMPLERTYPE_COMPARE => SG_SAMPLERTYPE_COMPARISON + + - the enum `sg_image_sample_type` gained a new item: + - SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT + + - the enum `sg_sampler_type` gained a new item: + - SG_SAMPLERTYPE_NONFILTERING + +- The sokol_gfx.h struct `sg_desc` has two new items: + - `.wgpu_bindgroups_cache_size` - must be power-of-2, default: 1024 + - `.wgpu_disable_bindgroups_cache` - default: false + +- sokol_gfx.h gained the following new public API functions to query per-frame information: + - `sg_frame_stats sg_query_frame_stats()` + - `void sg_enable_frame_stats(void)` + - `void sg_disable_frame_stats(void)` + - `bool sg_frame_stats_enabled(void)` + + Frame statistics gathering is enabled after startup, but can be temporarily + disabled and enabled again via `sg_disable_frame_stats()` and `sg_enable_frame_stats`. + +- The sokol_gfx.h validation layer has new validation checks in `sg_make_shader()` + regarding image/sampler pair compatibility (WebGPU is particularly strict about + this stuff). + +- In sokol_app.h, the old wip WebGPU device and swapchain setup code is now implemented + in pure C code (previously this was a mix of Javascript and C). + +- Also note that sokol_app.h currently only supports WebGPU in the Emscripten backend. + If you want to use sokol_gfx.h with the WebGPU backend in a native scenario, you'll have + to use a different window system glue library (like GLFW). The sokol-samples directory + has a handful of examples for using sokol_gfx.h + Dawn + GLFW. + +- The following headers have been made compatible with the sokol_gfx.h WebGPU backend + (mainly by embedding WGSL shader code): + - sokol_debugtext.h + - sokol_fontstash.h + - sokol_gl.h + - sokol_spine.h + - sokol_imgui.h (also required some more changes for embedding `unfilterable-float` + textures, since these now require separate shader and pipeline objects) + - sokol_nuklear.h (works in WebGPU, but doesn't contain the work from sokol_imgui.h + to support `unfilterable-float` user textures) + +- sokol_gfx_imgui.h gained a new function `sg_imgui_draw_menu()` which renders a + menu panel to show/hide all debug windows. Previously this had to be done + outside the header. + +- sokol_gfx_imgui.h gained a new 'frame stats' window, which allows to peak into + sokol_gfx.h frame-rendering internals. This basically visualizes the struct + `sg_frame_stats` returned by the new sokol_gfx.h function `sg_query_frame_stats()`. + +- The sokol-samples repository gained 3 new samples: + - cubemap-jpeg-sapp.c (load a cubemap from separate JPEG files) + - cubemaprt-sapp.c (render into cubemap faces - this demo actually existed a while but wasn't "official" so far) + - drawcallperf-sapp.c (a sample to explore the performance overhead of sg_apply_bindings, sg_apply_uniforms and sg_draw) + +#### 03-Oct-2023 + +- sokol_app.h win/gl: PR https://github.com/floooh/sokol/pull/886 has been merged, this makes + GL context initialization on Windows slightly more efficient. Many thanks to @dtrebilco! + +#### 25-Sep-2023 + +- The allocator callback functions in all headers that support custom allocators have been renamed + from `alloc` and `free` to `alloc_fn` and `free_fn`, this is because the symbol `free` is quite + likely to collide with a preprocessor macro of the same name if the standard C allocator is + replaced with a custom allocator. + + This is a breaking change only if you've been providing your own allocator functions to + the sokol headers. + + See issue https://github.com/floooh/sokol/issues/903 and PR https://github.com/floooh/sokol/pull/908 + for details. + +#### 23-Sep-2023 + +- sokol_gfx.h gl: Allow to inject an external GL framebuffer id into the sokol-gfx default + pass. See PR https://github.com/floooh/sokol/pull/899 and issue https://github.com/floooh/sokol/issues/892 + for details. Many thanks to @danielchasehooper for the discussion and PR! + + Further down the road I want to make the whole topic more flexible while at the same time + simplifying the sokol-gfx API, see here: https://github.com/floooh/sokol/issues/904 + +#### 22-Sep-2023 + +- sokol_gfx.h: Fixed a Metal validation error on Intel Macs when creating textures (Intel Macs + have unified memory, but don't support textures in shared storage mode). This was a regression + in the image/sampler split update in mid-July 2023. Fixes issue https://github.com/floooh/sokol/issues/905 + via PR https://github.com/floooh/sokol/pull/907. + +#### 19-Sep-2023 + +- sokol_fetch.h: fixed a minor issue where a request that was cancelled before it was dispatched + had an incomplete response state set in the response callback (the `finished`, `failed` and + `error_code` fields were not set). This fixes issue https://github.com/floooh/sokol/issues/882 + via PR https://github.com/floooh/sokol/pull/898 + +#### 18-Sep-2023 + +- PR https://github.com/floooh/sokol/pull/893 has been merged, this fixes a minor issue + in the GL backend when using an injected texture as framebuffer attachment. +- Issue https://github.com/floooh/sokol/issues/884 has been fixed via PR https://github.com/floooh/sokol/pull/894, + this adds missing error code paths in the Metal backend when Metal object creation fails. +- Clarified `sapp_run()` behaviour in the sokol_app.h documentation header (search for `OPTIONAL: DON'T HIJACK main()`) +- sokol_args.h now fully supports "key-only args", see issue https://github.com/floooh/sokol/issues/876 for details, + fixed via PR https://github.com/floooh/sokol/pull/896 + +#### 17-Sep-2023 + +- The sokol-gfx Metal backend now adds debug labels to Metal resource objects and + also passes through the `sg_push/pop_debug_group()` calls. If you use the push/pop + debug group calls, please be aware of the following limitations: + + - a push inside a render pass must have an associated pop inside the same render pass + - a push outside any render pass must have an associated pop outside any render pass + - Metal will ignore any push/pop calls outside render passes (this is because in Metal + these are MTLCommandEncoder methods) + + Associated issue: https://github.com/floooh/sokol/issues/889, and PR: https://github.com/floooh/sokol/pull/890. + +#### 09-Sep-2023 + +- a small PR has been merged which fixes a redundant glBindFramebuffer() in the GLES3 backend + in `sg_end_pass()` (see: https://github.com/floooh/sokol/pull/878), many thanks to @danielchasehooper + for catching that issue! +- sokol_imgui.h has been fixed for cimgui 1.89.9 (see https://github.com/floooh/sokol/issues/879) + +#### 28-Aug-2023 + +**sokol_gfx.h metal**: A new attempt at fixing a rare Metal validation layer +error about MTKView swapchain resource lifetimes. See PR https://github.com/floooh/sokol/pull/873 +for details. + +#### 26-Jul-2023 + +**sokol_nuklear.h**: The same image+sampler support has been added as in sokol_imgui.h +three days ago: + +- a new object type `snk_image_t` which wraps a sokol-gfx image and sampler + under a common handle +- new functions: + - snk_make_image() + - snk_destroy_image() + - snk_query_image_desc() + - snk_image_from_nkhandle() +- the function snk_nkhandle() now takes an snk_image_t handle instead of an sg_image handle +- the nuklear.h header needs to be included before the declaration (not just the implementation), + this was already required before, but now you get a proper error message if the include is missing +- the 'standard' logging- and error-reporting callback has been added as in the other sokol headers + (don't forget to add a logging callback in snk_setup(), otherwise sokol-nuklear will be silent) +- since sokol-nuklear now needs to allocate memory, an allocator can now be provided to the + snk_setup() call (otherwise malloc/free will be used) + +Please also read the new documentation section `ON USER-PROVIDED IMAGES AND SAMPLERS` +in sokol_nuklear.h, and also check out the (rewritten) sample: + +https://floooh.github.io/sokol-html5/nuklear-images-sapp.html + +Associated PR: https://github.com/floooh/sokol/pull/862 + +#### 23-Jul-2023 + +**sokol_imgui.h**: Add proper support for injecting user-provided sokol-gfx +images and samplers into Dear ImGui UIs. With the introduction of separate +sampler objects in sokol_gfx.h there's a temporary feature regression in +sokol_imgui.h and sokol_nuklear.h in that user provided images had to use a +shared sampler that's hardwired into the respective headers. This update fixes +this problem for sokol_imgui.h, with a similar fix for sokol_nuklear.h coming +up next. + +The sokol_imgui.h changes in detail are: + +- a new object type `simgui_image_t` which wraps a sokol-gfx image and sampler + object under a common handle +- two new function `simgui_make_image()` and `simgui_destroy_image()` to + create and destroy such a new `simgui_image_t` object. +- the existing function `simgui_imtextureid()` has been changed to take + an `simgui_image_t` +- sokol_imgui.h now also uses the same error-handling and logging callback + as the other sokol headers (this was needed because creating an `simgui_image_t` + object may fail because the object pool is exhausted) - don't forget + to provide a logging callback (for instance via sokol_log.h), otherwise + sokol_imgui.h will be entirely silent in case of errors. + +Please also read the new documentation section `ON USER-PROVIDED IMAGES AND SAMPLERS` +in sokol_imgui.h, and also check out the new sample: + +https://floooh.github.io/sokol-html5/imgui-images-sapp.html + +Associated PR: https://github.com/floooh/sokol/pull/861 + +#### 16-Jul-2023 + +**BREAKING CHANGES** + +The main topic of this update is to separate sampler state from image state in +sokol_gfx.h which became possible after GLES2 support had been removed from +sokol_gfx.h. + +This also causes some 'collateral changes' in shader authoring and +other sokol headers, but there was opportunity to fill a few feature gaps +in sokol_gfx.h as well: + +- it's now possible to sample depth textures in shaders both with regular + samplers, and with 'comparison samplers' (which is mainly useful for shadow mapping) +- it's now possible to create render passes without color attachments for + 'depth-only' rendering + +See the new [shadows-depthtex-sapp](https://floooh.github.io/sokol-html5/shadows-depthtex-sapp.html) sample which demonstrates both features. + +> NOTE: all related projects have a git tag `pre-separate-samplers` in case you are not ready yet to make the switch + +> NOTE 2: if you use sokol-gfx with the sokol-shdc shader compiler, you'll also need +> to update the sokol-shdc binaries from https://github.com/floooh/sokol-tools-bin + +##### **sokol_gfx.h** + +- texture sampler state has been removed from `sg_image_desc`, instead you now + need to create separate sampler objects: + + ```c + sg_sampler smp = sg_make_sampler(&(sg_sampler_desc){ + .min_filter = SG_FILTER_LINEAR, + .mag_filter = SG_FILTER_LINEAR, + .wrap_u = SG_WRAP_CLAMP_TO_EDGE, + .wrap_v = SG_WRAP_CLAMP_TO_EDGE + }); + ``` + +- texture filtering is now described by 3 separate filters: + - min_filter = SG_FILTER_NEAREST | SG_FILTER_LINEAR + - mag_filter = SG_FILTER_NEAREST | SG_FILTER_LINEAR + - mipmap_filter = SG_FILTER_NONE | SG_FILTER_NEAREST | SG_FILTER_LINEAR + + ...this basically switches from the esoteric GL convention to a convention + that's used by all other 3D APIs. There's still a limitation that's caused by + GL though: a sampler which is going to be used with an image that has a + `mipmap_count = 1` requires that `.mipmap_filter = SG_FILTER_NONE`. + +- another new sampler state in `sg_sampler_desc` is `sg_compare_func compare;`, + this allows to create 'comparison samplers' for shadow mapping + +- when calling `sg_apply_bindings()` the struct `sg_bindings` now has changed + to also include sampler objects, note that there is no 1:1 relationship + between images and samplers required: + + ```c + sg_apply_bindings(&(sg_bindings){ + .vertex_buffers[0] = vbuf, + .fs = { + .images = { + [SLOT_tex0] = img0, + [SLOT_tex1] = img1, + [SLOT_tex2] = img2, + }, + .samplers[SLOT_smp] = smp, + } + }); + ``` + +- if you use sokol-shdc, you need to rewrite your shaders from 'OpenGL GLSL style' (with + combined image samplers) to 'Vulkan GLSL style' (with separate textures and samplers): + + E.g. the old GL-style shader with combined image samplers: + + ```glsl + uniform sampler2D tex; + + void main() { + frag_color = texture(tex, uv); + } + ``` + ...now needs to look like this: + + ```glsl + uniform texture2D tex; + uniform sampler smp; + + void main() { + frag_color = texture(sampler2D(tex, smp), uv); + } + ``` + + sokol-shdc will now throw an error if it encounters an 'old' shader using combined + image-samplers, this helps you to catch all places where a rewrite to separate + texture and sampler objects is required. + +- If you *don't* use sokol-shdc and instead provide your own backend-specific + shaders, you need to provide more shader interface reflection info about the texture + and sampler usage in a shader when calling `sg_make_shader`. + Please see the new documentation block `ON SHADER CREATION` in sokol_gfx.h for more details! + + Also refer to the updated 3D-backend-specific samples here: + + - for GL: https://github.com/floooh/sokol-samples/tree/master/glfw + - for GLES3: https://github.com/floooh/sokol-samples/tree/master/html5 + - for D3D11: https://github.com/floooh/sokol-samples/tree/master/d3d11 + - for Metal: https://github.com/floooh/sokol-samples/tree/master/metal + +- it's now possible to create `sg_pass` objects without color attachments to + enable depth-only rendering, see the new sample [shadows-depthtex-sapp](https://floooh.github.io/sokol-html5/shadows-depthtex-sapp.html) for details, + specifically be aware of the caveat that a depth-only-compatible `sg_pipeline` object + needs to 'deactivate' the first color target by setting its pixel format + to `NONE`: + + ```c + sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){ + ... + .colors[0].pixel_format = SG_PIXELFORMAT_NONE, + ... + }); + ``` + +- the following struct names have been changed to be more in line with related + struct names, this also makes those names similar to WebGPU types: + + - `sg_buffer_layout_desc` => `sg_vertex_buffer_layout_state` + - `sg_vertex_attr_desc` => `sg_vertex_attr_state` + - `sg_layout_desc` => `sg_vertex_layout_state` + - `sg_color_state` => `sg_color_target_state` + +- bugfixes and under-the-hood changes + - `sg_begin_pass()` used the wrong framebuffer size when rendering to a mip-level != 0 + - the Metal backend code started to use the `if (@available(...))` statement + to check for runtime-availability of macOS/iOS API features + - **NOTE:** this change (`if (@available(...))`) caused linking problems in + the Zig and Rust bindings on GH Actions (missing symbol + `___isPlatformVersionAtLeast`) which I could not reproduce locally on my + M1 Mac. On Zig this could be fixed by moving to the latest zig-0.11.0-dev + version, but for Rust this still needs to be fixed). + - on macOS the Metal backend now creates resources in Shared resource storage mode if + supported by the device + - on iOS the Metal backend now supports clamp-to-border-color if possible (depends on + iOS version and GPU family) + +##### **sokol_gl.h** + +- The function `sgl_texture(sg_image img)` has been changed to accept a sampler + object to `sgl_texture(sg_image img, sg_sampler smp)`. Passing an invalid image handle + will use the builtin default (white) texture, and passing an invalid sampler + handle will use the builtin default sampler. + +##### **sokol_shape.h** + +- Some sokol-shape functions have been renamed to match renamed structs in sokol-gfx: + + - `sshape_buffer_layout_desc()` => `sshape_vertex_buffer_layout_state()` + - `sshape_position_attr_desc()` => `sshape_position_vertex_attr_state()` + - `sshape_normal_attr_desc()` => `sshape_normal_vertex_attr_state()` + - `sshape_texcoord_attr_desc()` => `sshape_texcoord_vertex_attr_state()` + - `sshape_color_attr_desc()` => `sshape_color_vertex_attr_state()` + +##### **sokol_spine.h** + +- A sokol-spine atlas object now allocates both an `sg_image` and `sg_sampler` handle + and expects the user code to initialize those handles to complete image and + sampler objects. Check the updated sokol-spine samples here for more details: + + https://github.com/floooh/sokol-samples/tree/master/sapp + +##### **sokol_imgui.h** + +- sokol_imgui.h has a new public function to create an ImTextureID handle from + an `sg_image` handle which can be used like this: + + ```c + ImTextureID tex_id = simgui_imtextureid(img); + ``` + + Note that sokol-imgui currently doesn't currently allow to pass user-provided `sg_sampler` + object with the user-provided image. + +##### **sokol_nuklear.h** + +- similar to sokol_imgui.h, there's a new public function `snk_nkhandle()` + which creates a Nuklear handle from a sokol-gfx image handle which can be + used like this to create a Nuklear image handle: + + ```c + nk_image nki = nk_image_handle(snk_nkhandle(img)); + ``` + + As with sokol_imgui.h, it's currently not possible to pass a user-provided `sg_sampler` + object with the image. + + +#### 20-May-2023 + +Some minor event-related cleanup in sokol_app.h and a touchscreen fix in sokol_imgui.h + +- in the event `SAPP_EVENTTYPE_FILESDROPPED`: + - the `sapp_event.modifier` field now contains the active modifier keys + at the time of the file drop operations on the platforms macOS, Emscripten + and Win32 (on Linux I haven't figured out how this might work with the + Xlib API) + - on macOS, the `sapp_event.mouse_x/y` fields now contain the window-relative + mouse position where the drop happened (this already worked as expected on + the other desktop platforms) + - on macOS and Linux, the `sapp_event.mouse_dx/dy` fields are now set to zero + (this already was the case on Emscripten and Win32) +- in the events `SAPP_EVENTTYPE_MOUSE_ENTER` and `SAPP_EVENTTYPE_MOUSE_LEAVE`: + - the `sapp_event.mouse_dx/dy` fields are now set to zero, previously this + could be a very big value on some desktop platforms + +Many thanks to @castano for the initial PR (https://github.com/floooh/sokol/pull/830)! + +- In sokol_imgui.h, the new io.AddMouseSourceEvent() function in Dear ImGui 1.89.5 + is called to differentiate between mouse- and touch-events, this makes ui tabs + work with a single tap (previously a double-tap on the tab was needed). The code + won't break if the ImGui version is older (in this case the function simply isn't called) + + +#### 19-May-2023 + +**BREAKING CHANGES**_ in sokol_gfx.h: Render passes are now more 'harmonized' +with Metal and WebGPU by exposing a 'store action', and making MSAA resolve attachments +explicit. The changes in detail: + + - A new documentation section `ON RENDER PASSES` has been added to sokol_gfx.h, this + gives a much more detailed overview of the new render pass behaviour than this + changelog, please make sure to give it a read - especially when you are using + MSAA offscreen render passes in your code. + - `sg_action` has been renamed to `sg_load_action`. + - A new enum `sg_store_action` has been added. + - In `sg_pass_action`: + - `.action` has been renamed to `.load_action`. + - `.value` has been renamed to `.clear_value`. + - A new field `.store_action` has been added. + - An `sg_image` object with a sample count > 1 no longer contains a second implicit + texture for the msaa-resolve operation. + - When creating a pass object, there's now an array of `sg_image` objects + called `resolve_attachments[]`. When a resolve attachment image is set, the + color attachment at the same slot index must be an image with a sample count > + 1, and an 'msaa-resolve' operation from the color attachment into the + resolve attachment will take place in `sg_end_pass()`. + - Pass attachments are now more flexible (there were a couple of gaps where specific + image types were not allowed as pass attachments, especially for the depth-stencil- + attachment - but this hadn't actually been checked by the validation layer). + - Some gaps in the validation layer around images and passes have been tightened up, + those usually don't work in one backend or another, but have been ignored so far + in the validation layer, mainly: + - MSAA images must have num_mipmaps = 1. + - 3D images cannot have a sample_count > 1. + - 3D images cannot have depth or depth-stencil image formats. + - It's not allowed to bind MSAA images as texture. + - It's not allowed to bind depth or depth-stencil images as texture. + - (I'll see if I can relax some of those restrictions after the WebGPU backend release) + - **A lot** of new tests have been added to cover validation layer checks when creating + image and pass objects. + + Next up: WebGPU! + +#### 30-Apr-2023 + +GLES2/WebGL1 support has been removed from the sokol headers (now that + all browsers support WebGL2, and WebGPU is around the corner I feel like it's finally + time to ditch GLES2. + + This is a breaking API change in sokol_gfx.h and sokol_app.h. + + Common changes across all headers: + - (breaking change) the `SOKOL_GLES2` config define is no longer accepted and will cause a compile error + (use `SOKOL_GLES3` instead) + - (breaking change) on Emscripten use the linker option `-s USE_WEBGL2=1` + - any embedded GLES shaders have been updated from glsl100 to glsl300es (but glsl100 shaders + still work fine with the GLES3 backend) + + Changes in sokol_gfx.h: + - (breaking change) the following `sg_features` members have been removed (because those features + are no longer optional, but guaranteed across all backends): + - `sg_features.instancing` + - `sg_features.multiple_render_targets` + - `sg_features.msaa_render_targets` + - `sg_features.imagetype_3d` + - `sg_features.imagetype_array` + - (breaking change) the struct `sg_gl_context_desc` and its embedded instance `sg_desc.gl` have been removed + - `sg_image` objects with `SG_PIXELFORMAT_DEPTH` or `SG_PIXELFORMAT_DEPTH_STENCIL` with + a `sample_count == 1` are now regular textures in the GL backend (this is not true + for MSAA depth textures unfortunately, those are still GL render buffer objects) + - in the GL backend, `SG_PIXELFORMAT_DEPTH` now resolves to `GL_DEPTH_COMPONENT32F` (same + as in the other backends), previously it was `GL_DEPTH_COMPONENT16` + - in `sg_begin_pass()`, the GL backend now only uses the new `glClearBuffer*` functions, the + old GLES2 clear functions have been removed + - in `sg_end_pass()`, the GLES3 backend now invalidates MSAA render buffers after they have + been resolved (via `glInvalidateFramebuffer`) - more control over this will come soon-ish + when this ticket is implemented: https://github.com/floooh/sokol/issues/816 + - the instanced rendering functions are no longer wrapped in C macros in the GL backend + + Changes in sokol_app.h: + - (breaking) the config item `sapp_desc.gl_force_gles2` has been removed + - (breaking) the function `sapp_gles2()` has been removed + - any fallback logic from GLES3 to GLES2 has been removed (in the Emscripten, Android and + iOS backends) + +- **20-Feb-2023**: sokol_gfx.h has a new set of functions to get a 'best-effort' + desc struct with the creation parameters of a specific resource object: + + ```c + sg_buffer_desc sg_query_buffer_desc(sg_buffer buf); + sg_image_desc sg_query_image_desc(sg_image img); + sg_shader_desc sg_query_shader_desc(sg_shader shd); + sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip); + sg_pass_desc sg_query_pass_desc(sg_pass pass); + ``` + + The returned structs will *not* be an exact copy of the desc struct that + was used for creation the resource object, instead: + + - references to external data (like buffer and image content or + shader sources) will be zeroed + - any attributes that have not been kept around internally after + creation will be zeroed (the ```sg_shader_desc``` struct is most + affected by this, the other structs are fairly complete). + + Calling the functions with an invalid or dangling resource handle + will return a completely zeroed struct (thus it may make sense + to first check the resource state via ```sg_query_*_state()```) + + Nevertheless, those functions may be useful to get a partially filled out + 'creation blueprint' for creating similar resources without the need + to keep and pass around the original desc structs. + + >MINOR BREAKING CHANGE: the struct members ```sg_image_info.width``` and + ```sg_image_info.height``` have been removed, this information is now + returned by ```sg_query_image_desc()```. + + PR: https://github.com/floooh/sokol/pull/796, fixes: https://github.com/floooh/sokol/issues/568 + +- **17-Feb-2023**: sokol_app.h on macOS now has a proper fix for the problem + that macOS doesn't send key-up events while the Cmd key is held down. + Previously this was handled through a workaround of immediately sending a + key-up event after its key-down event if the Cmd key is currently held down + to prevent a 'stuck key'. The proper fix is now to install an "event monitor" + callback (many thanks to GLFW for finding and implementing the solution). + Unfortunately there's no such solution for the Emscripten code path, which + also don't send a key-up event while Cmd is pressed on macOS (the workaround + there to send a key-up event right on key-down while Cmd is held down to + prevent a stuck key is still in place) For more details, see: + https://github.com/floooh/sokol/issues/794 + +- **15-Feb-2023**: A fix in the sokol_gfx.h GL backend: due to a bug in the + state cache, the GL backend could only bind a total of + SG_MAX_SHADERSTAGE_IMAGES (= 12) when it actually should be twice that amount + (12 per shader stage). Note however that the total amount of texture bindings + is still internally limited by the GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS + runtime variable (~~currently this is not exposed in sg_limits though~~). Many + thanks to @allcreater for PR https://github.com/floooh/sokol/pull/787. + PS: sg_limits now exposes GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS as + ```sg_limits.gl_max_combined_texture_image_units```, and the + value can also be inspected via the debug UI in sokol_gfx_imgui.h. + +- **13-Feb-2023**: The way logging works has been completely revamped in + the sokol headers. UWP support has been removed from sokol_audio.h + and sokol_app.h (this also means that the sokol headers no longer contain + any C++ code). + + **REQUIRED ACTION**: Since the sokol headers are now completely silent + without a logging callback (explanation below), it is highly recommended + to use the standard logging callback provided by the new header ```sokol_log.h```. + For instance for sokol_gfx.h it looks like this: + + ```c + #include "sokol_log.h" + //... + sg_setup(&(sg_desc){ + //... + .logger.func = slog_func, + }); + ``` + + All sokol samples have been updated to use sokol_log.h for logging. + + The former logging callback is now a combined + logging- and error-reporting callback, and more information is available + to the logging function: + - a 'tag string' which identifies the sokol headers, this string + is identical with the API prefix (e.g. "sg" for sokol_gfx.h, + "sapp" for sokol_app.h etc...) + - a numeric log level: 0=panic, 1=error, 2=warning, 3=info + - a numeric 'log item id' (think of it as error code, but since + not only errors are reported I called it a log item id) + - a human readable error message + - a source file line number where the log item was reported + - the file path of the sokol header + + Log level ```panic``` is special in that it terminates execution inside + the log function. When a sokol header issues a panic log message, it means + that the problem is so big that execution can not continue. By default, + the sokol headers and the standard log function in sokol_log.h call + ```abort()``` when a panic log message is issued. + + In debug mode (NDEBUG not defined, or SOKOL_DEBUG defined), a log message + (in this case from sokol_spine.h) will look like this: + + ``` + [sspine][error][id:12] /Users/floh/projects/sokol/util/sokol_spine.h:3472:0: + SKELETON_DESC_NO_ATLAS: no atlas object provided in sspine_skeleton_desc.atlas + ``` + The information can be 'parsed' like this: + - ```[sspine]```: it's a message from sokol_spine.h + - ```[error]```: it's an error + - ```[id:12]```: the numeric log item id (associated with ```SKELETON_DESC_NO_ATLAS``` below) + - source file path and line number in a compiler-specific format - in some IDEs and terminals + this is a clickable link + - the line below is the human readable log item id and message + + In release mode (NDEBUG is defined and SOKOL_DEBUG is not defined), log messages + are drastically reduced (the reason is to not bloat the executable with all the extra string data): + + ``` + [sspine][error][id:12][line:3472] + ``` + ...this reduced information still gives all the necessary information to identify the location and type of error. + + A custom logging function must adhere to a few rules: + + - must be re-entrant because it might be called from different threads + - must treat **all** provided string pointers as optional (can be null) + - don't store the string pointers, copy the string data instead + - must not return for log level panic + + A new header ```sokol_log.h``` has been added to provide a standard logging callback implementation + which provides logging output on all platforms to stderr and/or platform specific logging + facilities. ```sokol_log.h``` only uses fputs() and platform specific logging function instead + of fprintf() to preserve some executable size. + + **QUESTION**: Why are the sokol headers now silent, unless a logging callback is installed? + This is mainly because a standard logging function which does something meaningful on all + platforms (including Windows and Android) isn't trivial. E.g. printing to stderr is not + enough. It's better to move that stuff into a centralized place in a separate header, + but since the core sokol headers must not (statically) depend on other sokol headers + the only solution that made sense was to provide a standard logging function which must + be 'registered' as a callback. + +- **26-Jan-2023**: Work on SRGB support in sokol_gfx.h has started, but + this requires more effort to be really usable. For now, only a new + pixel format has been added: SG_PIXELFORMAT_SRGB8A8 (see https://github.com/floooh/sokol/pull/758, + many thanks to @allcreater). The sokol-gfx GL backend has a temporary + workaround to align behaviour with D3D11 and Metal: automatic SRGB conversion + is enabled for offscreen render passes, but disabled for the default + framebuffer. A proper fix will require separate work on sokol_app.h to + support an SRGB default framebuffer and communicate to sokol-gfx + whether the default framebuffer is SRGB enabled or not. + +- **24-Jan-2023**: sokol_gfx.h Metal: A minor inconsistency has been fixed in + the validation layer and an assert for the function ```sg_apply_uniforms()``` + which checks the size of the incoming data against the uniform block size. + The validation layer and Metal backend did a ```<=``` test while the D3D11 + and GL backends checked for an exact size match. Both the validation layer + and the Metal backend now also check for an exact match. Thanks to @nmr8acme + for noticing the issue and providing a PR! (https://github.com/floooh/sokol/pull/776) + +- **23-Jan-2023**: A couple more sokol_audio.h updates: + - an AAudio backend has been added for Android, and made the default. This + means you now need to link with ```aaudio``` instead of ```OpenSLES``` when + using sokol_audio.h on Android. The OpenSLES backend code still exists (for + now), but must be explicitly selected by compiling the sokol_audio.h + implementation with the define ```SAUDIO_ANDROID_SLES``` (e.g. there is + no runtime fallback from AAudio to OpenSLES). AAudio is fully supported + since Android 8.1. Many thanks to @oviano for the initial AAudio PR + (https://github.com/floooh/sokol/pull/484) + - in the WebAudio backend, WebAudio is now properly activated on the first + input action again on Chrome for Android (at some point activating WebAudio + via a ```touchstart``` event stopped working and had to be moved to the + ```touchend``` event, see https://github.com/floooh/sokol/issues/701) + - audio backend initialization on iOS and macOS is now a bit more fault-tolerant, + errors during initialization now properly set sokol_audio.h to 'silent mode' + instead of asserting (or in release mode ignoring the error) + - ...and some minor general code cleanup things in sokol_audio.h: backend-specific + functions now generally have a matching prefix (like ```_saudio_alsa_...()```) + for better searchability + +- **16-Jan-2023**: + - sokol_audio.h android: https://github.com/floooh/sokol/pull/747 has been merged + which adds a couple more error checks at OpenSLES startup. + - sokol_gfx.h: support for half-float vertex formats has been added via + PR https://github.com/floooh/sokol/pull/745 + - sokol_imgui.h: fixes for Dear ImGui 1.89 deprecations (via PR https://github.com/floooh/sokol/pull/761) + +- **15-Jan-2023**: two bugfixes in sokol_app.h and sokol_gfx.h: + - sokol_app.h x11: Mouse button events now always return valid mouse + coordinates, also when no mouse movement happened yet + (fixes https://github.com/floooh/sokol/issues/770) + - sokol_gfx.h gl: The GL context is now configured with + GL_UNPACK_ALIGNMENT = 1, this should bring texture creation and updating + behaviour in line with the other backends for tightly packed texture + data that doesn't have a row-pitch with a multiple of 4 + (fixes https://github.com/floooh/sokol/issues/767) + +- **14-Jan-2023**: sokol_app.h x11: a drag'n'drop related bugfix, the + XdndFinished reply event was sent with the wrong window handle which + confused some apps where the drag operation originated + (see https://github.com/floooh/sokol/pull/765#issuecomment-1382750611) + +- **16-Dec-2022**: In the sokol_gfx.h Metal backend: A fix for a Metal + validation layer error which I just discovered yesterday (seems to be new in + macOS 13). When the validation layer is active, and the application window + becomes fully obscured, the validation layer throws an error after a short + time (for details see: https://github.com/floooh/sokol/issues/762). + The reason appears to be that sokol_gfx.h creates a command buffer with + 'unretained references' (e.g. the command buffer doesn't manage the + lifetime of resources used by the commands stored in the buffer). This + seems to clash with MTKView's and/or CAMetalLayer's expectations. I fixed + this now by creating a second command buffer with 'retained references', + which only holds the ```presentDrawable``` command. That way, regular + draw commands don't have the refcounting overhead (because they're stored + in an unretained-references cmdbuffer), while the drawable surface is + still properly lifetime managed (because it's used in a separate command + buffer with retained references). + +- **15-Dec-2022**: A small but important update in sokol_imgui.h which fixes + touch input handling on mobile devices. Many thanks to GitHub user @Xadiant + for the bug investigation and [PR](https://github.com/floooh/sokol/pull/760). + +- **25-Nov-2022**: Some code cleanup around resource creation and destruction in sokol_gfx.h: + - It's now safe to call the destroy, uninit and dealloc functions in any + resource state, in general, the functions will do the right thing without + assertions getting in the way (there are however new log warnings in some + cases though, such as attempting to call an ```sg_dealloc_*()``` function on + a resource object that's not in ALLOC state) + - A related **minor breaking change**: the ```sg_uninit_*()``` functions now return + void instead of bool, this is because ```sg_dealloc_*()``` no longer asserts + when called in the wrong resource state + - Related internal code cleanup in the backend-agnostic resource creation + and cleanup code, better or more consistent function names, etc... + - The validation layer can now be disabled in debug mode with a runtime + flag during setup: ```sg_desc.disable_validation```. This is mainly useful + for test code. + - Creating a pass object with invalid image objects now no longer asserts, + but instead results in a pass object in FAILED state. In debug mode, + the validation layer will still stop at this problem though (it's mostly + an 'undefined API behaviour' fix in release mode). + - Calling ```sg_shutdown()``` with existing resources in ALLOC state will + no longer print a log message about an 'active context mismatch'. + - A new header documentation blurb about the two-step resource creation + and destruction functions (search for RESOURCE CREATION AND DESTRUCTION IN DETAIL) + +- **16-Nov-2022**: Render layer support has been added to sokol_debugtext.h, + same general changes as in sokol_gl.h with two new functions: + sdtx_layer(layer_id) to select the layer to record text into, and + sdtx_draw_layer(layer_id) to draw the recorded text in that layer inside a + sokol-gfx render pass. The new sample [debugtext-layers-sapp](https://floooh.github.io/sokol-html5/debugtext-layers-sapp) demonstrates the feature together with + sokol-gl. + + +- **11-Nov-2022**: sokol_gl.h has 2 new public API functions which enable + layered rendering: sgl_layer(), sgl_draw_layer() (technically it's three + functions: there's also sgl_context_draw_layer(), but that's just a variant of + sgl_draw_layer()). This allows to 'interleave' sokol-gl rendering + with other render operations. The [spine-layers-sapp](https://floooh.github.io/sokol-html5/spine-layers-sapp.html) + sample has been updated to use multiple sokol-gl layers. + +- **09-Nov-2022**: sokol_gfx.h now allows to add 'commit listeners', these + are callback functions which are called from inside sg_commit(). This is + mainly useful for libraries which build on top of sokol-gfx to be notified + about the start/end point of a frame, which in turn may simplify the public + API, or the internal implementation, because the library no longer needs to + 'guess' when a new frame starts. + + For more details, search for 'COMMIT LISTENERS' in the sokol_gfx.h header. + + This also results in a minor breaking change in sokol_spine.h: The function + ```sspine_new_frame()``` has been removed and replaced with an internal commit + listener. + + Likewise, sokol_gl.h now uses a commit listener in the implementation, but + without changing the public API (the feature will be important for an upcoming + sokol-gl feature to support rendering layers, and for this a 'new-frame-function' + would have been needed). + +- **05-Nov-2022** A breaking change in sokol_fetch.h, and a minor change in + sokol_app.h which should only break for very few users: + - An ```sfetch_range_t``` ptr/size pair struct has been added to sokol_fetch.h, + and discrete ptr/size pairs have been replaced with sfetch_range_t + items. This affects the structs ```sfetch_request_t``` and ```sfetch_response_t```, + and the function ```sfetch_bind_buffer()```. + - The required changes in ```sfetch_response_t``` might be a bit non-obviois: To + access the fetched data, previous ```.buffer_ptr``` and ```.fetched_size``` + was used. The fetched data is now accessible through an ```sfetch_range_t data``` + item (```data.ptr``` and ```data.size```). The old ```.fetched_offset``` item + has been renamed to ```.data_offset``` to better conform with the new naming. + - The last two occurrences of discrete ptr/size pairs in sokol_app.h now have also + been replaced with ```sapp_range_t``` items, this only affects the structs + ```sapp_html5_fetch_request``` and ```sapp_html5_fetch_response```. + +- **03-Nov-2022** The language bindings generation has been updated for Zig 0.10.0, + and clang-14 (there was a minor change in the JSON ast-dump format). + Many thanks to GitHub user @kcbanner for the Zig PR! + +- **02-Nov-2022** A new header sokol_spine.h (in the util dir), this is a + renderer and 'handle wrapper' around the spine-c runtime (Spine is a popular 2D + character anim system: http://esotericsoftware.com/). This turned out a much bigger + rabbit-hole than I initially expected, but the effort is justified by being a + experimentation testbed for a couple of things I want to add to other sokol + headers (for instance cleaned up handle pool code, a new logging- and error-reporting + system, render layers which will be useful for sokol_gl.h and sokol_debugtext.h). + +- **22-Oct-2022** All sokol headers now allow to override logging with a + callback function (installed in the setup call) instead of defining a SOKOL_LOG + macro. Overriding SOKOL_LOG still works as default fallback, but this is no + longer documented, consider this deprecated. Many thanks to GitHub user + @Manuzor for the PR (see https://github.com/floooh/sokol/pull/721 for details) + +- **21-Oct-2022** RGB9E5 pixel format support in sokol_gfx.h and a GLES2 related + bugfix in the sokol_app.h Android backend: + - sokol_gfx.h now supports RGB9E5 textures (3*9 bit RGB + 5 bit shared exponent), + this works in all backends except GLES2 and WebGL1 (use ```sg_query_pixelformat()``` + to check for runtime support). Many thanks to GitHub user @allcreater for the PR! + - a bugfix in the sokol_app.h Android backend: when forcing a GLES2 context via + sapp_desc.gl_force_gles2, the Android backend correctly created a GLES2 context, + but then didn't communicate this through the function ```sapp_gles2()``` (which + still returned false in this case). This caused the sokol_gfx.h GL backend to + use the GLES3 code path instead GLES2 (which surprisingly seemed to have worked + fine, at least for the sokol samples which force GLES2). + +- **19-Oct-2022** Some fixes in the embedded Javascript code blocks (via EM_JS) + in sokol_app.h, sokol_args.h, sokol_audio.h and sokol_fetch.h: + - the JS code has been 'modernized' (e.g. const and let instead of var, + ```() => { ... }``` instead of ```function () { ... }``` for callbacks) + - false positives in the Closure static analysis have been suppressed + via inline hints + +- **16-Oct-2022** The Odin bindings generator and the generated bindings have + been simplified (the Odin binding now don't have separate wrapper functions). + Requires the latest Odin release. Also note: On M1 Macs I'm currently seeing + what looks like an ABI problem (in functions which pass color values to the C + side as uint8_t, the colors come out wrong). This also happened with the + previous binding version, so it looks like a regression in Odin. Might be + related to this recent bugfix (which I haven't tested yet): + https://github.com/odin-lang/Odin/issues/2121 Many thanks to @thePHTest for the + PR! (https://github.com/floooh/sokol/pull/719) + +- **15-Oct-2022** + - fixes for Emscripten 3.1.24: the sokol headers now use the new + **EM_JS_DEPS()** macro to declare 'indirect dependencies on JS library functions'. + This is a (much more robust) follow-up fix to the Emscripten related fixes from 10-Sep-2022. + The new Emscripten SDK also displays a couple of Javascript "static analyzer" warnings + by the Closure compiler (used in release mode to optimize and minify the generated + JS code). I fixed a couple of those warnings, but some warnings persist (all of them + false positives). Not sure yet if these can be fixed or need to be suppressed, but + that's for another time. + - the webkitAudioContext() fallback in sokol_audio.h's Emscripten backend + has been removed (only AudioContext is supported now), the fallback also + triggered a Closure warning, so it probably never worked as intended anyway. + - I also had to undo an older workaround in sokol_app.h on iOS (https://github.com/floooh/sokol/issues/645) + because this is now triggering a Metal validation layer error (https://github.com/floooh/sokol/issues/726). + The original case is no longer reproducible, so undoing the old workaround seems to + be a quick fix. Eventually I want to get rid of MTKView though, and go down to + CAMetalLayer. + +- **08-Oct-2022** sokol_app.h Android backend: the ```sapp_touchpoint``` struct + now has a new item ```sapp_android_tooltype android_tooltype;```. This exposes the + result of the Android NDK function ```AMotionEvent_getToolType()```. + Many thanks to @Wertzui123 for the initial PR (https://github.com/floooh/sokol/pull/717). + +- **25-Sep-2022**: sokol_app.h on Linux now optionally supports EGL instead of + GLX for the window system glue code and can create a GLES2 or GLES3 context + instead of a 'desktop GL' context. + To get EGL+GLES2/GLES3, just define SOKOL_GLES2 or SOKOL_GLES3 to compile the + implementation. To get EGL+GL, define SOKOL_GLCORE *and* SOKOL_FORCE_EGL. + By default, defining just SOKOL_GLCORE uses GLX for the window system glue + (just as before). Many thanks to GH user @billzez for the PR! + +- **10-Sep-2022**: sokol_app.h and sokol_args.h has been fixed for Emscripten 3.21, those headers + used the Emscripten Javascript helper function ```ccall()``` which is now part of the + 'legacy runtime' and causes linker errors. Instead of ```ccall()``` sokol_app.h and sokol_args.h + now drop down to a lower level set of Emscripten JS helper functions (which hopefully won't + go away anytime soon). + +- **05-Aug-2022**: New officially supported and automatically updated language bindings for Odin: + https://github.com/floooh/sokol-odin (also see [gen_odin.py](https://github.com/floooh/sokol/blob/master/bindgen/gen_odin.py)) + +- **10-Jul-2022**: New features in sokol_app.h and sokol_imgui.h: + - In sokol_app.h it's now possible to set a mouse cursor type from a number of predefined + types via the new function ```sapp_set_mouse_cursor(sapp_mouse_cursor cursor)```. The + available cursor types are compatible with GLFW and Dear ImGui. Supported platforms + are: macOS, linux, win32, uwp and web. + - ```sapp_show_mouse(bool shown)``` now also works on the web platform. + - In sokol_app.h, the poorly defined 'user cursor' feature has been removed (```sapp_desc.user_cursor``` + and ```SAPP_EVENTTYPE_UPDATE_CURSOR```). This was a hack to allow changing the mouse cursor and + only worked on Win32 and macOS (with different behaviour). Since setting the cursor type + is now 'properly supported, this hack was removed. + - sokol_imgui.h will now set the cursor type via ```sapp_set_mouse_cursor()```. This can be + disabled with the new ```simgui_desc_t``` item ```disable_set_mouse_cursor```. + - sokol_imgui.h now automatically enables resizing windows from edges (not just the bottom-right corner), + this behaviour can be disabled with the new ```simgui_desc_t``` item ```disable_windows_resize_from_edges```. + - sokol_imgui.h can now optionally write to the alpha channel (useful if you want to render the UI + into a separate render target, which is later composed onto the default framebuffer). The feature + is enabled with the new ```simgui_desc_t``` item ```write_alpha_channel```. + + Many thanks to **@tomc1998** for the initial [Linux/X11 mouse cursor type PR](https://github.com/floooh/sokol/pull/678) and + **@luigi-rosso** for the [sokol_imgui.h alpha channel PR](https://github.com/floooh/sokol/pull/687)! + +- **03-Jul-2022**: A new sokol_gfx.h function ```bool sg_query_buffer_will_overflow(sg_buffer buf, size_t size)``` +which allows to check if a call to ```sg_append_buffer()``` would overflow the buffer. This +is an alternative to the ```sg_query_buffer_overflow()``` function which only reports +the overflow after the fact. Many thanks to @RandyGaul for the PR! + +- **29-Jun-2022**: In sokol_app.h with the D3D11 backend, if SOKOL_DEBUG is +defined, and the D3D11 device creation fails, there's now a fallback code +path which tries to create the device again without the D3D11_CREATE_DEVICE_DEBUG +flag. Turns out the D3D11 debug support may suddenly stop working (just happened +to me, indicated by the Win10 "Graphics Tool" feature being silently uninstalled +and failing to install when asked to do so). This fix at least allows sokol_app.h +applications compiled in debug mode to run, even if the D3D11 debug layer doesn't +work. + +- **29-May-2022**: The code generation scripts for the +[sokol-nim](https://github.com/floooh/sokol-nim) language bindings have been +revised and updated, many thanks to Gustav Olsson for the PR! (I'm planning to +spend a few more days integrating the bindings generation with GitHub Actions, +so that it's easier to publish new bindings after updates to the sokol headers). + +- **26-May-2022**: The GL backend in sokol_app.h now allows to override the GL + context version via two new items in the ```sapp_desc``` struct: + ```sapp_desc.gl_major_version``` and ```sapp_desc.gl_minor_version```. The + default GL context version remains at 3.2. Overriding the GL version might make + sense if you're not using sokol_app.h together with sokol_gfx.h, or otherwise + want to call GL functions directly. Note that this only works for the + 'desktop GL' backends (Windows, Linux and macOS), but not for the GLES backends + (Android, iOS, web). Furthermore, on macOS only the GL versions 3.2 and 4.1 + are available (plus the special config major=1 minor=0 creates an + NSOpenGLProfileVersionLegacy context). In general: use at your risk :) Many + thanks to GitHub user @pplux for the PR! + +- **15-May-2022**: The way internal memory allocation can be overridden with + your own functions has been changed from global macros to callbacks + provided in the API setup call. For instance in sokol_gfx.h: + + ```c + void* my_malloc(size_t size, void* userdata) { + (void)userdata; // unused + return malloc(size); + } + + void my_free(void* ptr, void* userdata) { + (void)userdata; // unused + free(ptr); + } + + //... + sg_setup(&(sg_desc){ + //... + .allocator = { + .alloc = my_malloc, + .free = my_free, + .user_data = ..., + } + }); + ``` + + sokol_gfx.h will now call ```my_malloc()``` and ```my_free()``` whenever it needs + to allocate or free memory (note however that allocations inside OS + functions or 3rd party libraries are not affected). + + If no override functions are provided, the standard library functions ```malloc()``` and ```free()``` + will be used, just as before. + + This change breaks source compatibility in the following headers: + + - **sokol_fontstash.h**: the function signature of ```sfons_create()``` has changed, + this now takes a pointer to a new ```sfons_desc_t``` struct instead of + individual parameters. + - **sokol_gfx_imgui.h** (NOT sokol_imgui.h!): likewise, the function signature of + ```sg_imgui_init()``` has changed, this now takes an additional parameter + which is a pointer to a new ```sg_imgui_desc_t``` struct. + + All affected headers also have a preprocessor check for the outdated + macros ```SOKOL_MALLOC```, ```SOKOL_CALLOC``` and ```SOKOL_FREE``` and throw + a compilation error if those macros are detected. + + (if configuration through macros is still desired this could be added back in + the future, but I figured that the new way is more flexible in most situations). + + The header sokol_memtrack.h and the sample [restart-sapp](https://floooh.github.io/sokol-html5/restart-sapp.html) have been updated accordingly. + + Also search for ```MEMORY ALLOCATION OVERRIDE``` in the header documentation block + for more details. + +- **14-May-2022**: added a helper function ```simgui_map_keycode()``` to + sokol_imgui.h to map sokol_app.h keycodes (```sapp_keycode```, + ```SAPP_KEYCODE_*```) to Dear ImGui keycodes (```ImGuiKey```, ```ImGuiKey_*```). + If you're using Dear ImGui function to check for key input, you'll need to + update the code like this: + + - Old: + ```cpp + ImGui::IsKeyPressed(SAPP_KEYCODE_A); + ``` + - New: + ```cpp + ImGui::IsKeyPressed(simgui_map_keycode(SAPP_KEYCODE_A)); + ``` + + This was basically 'fallout' from rewriting the input system in sokol_imgui.h + to the new evented IO system in Dear ImGui. + +- **08-Feb-2022**: sokol_imgui.h has been updated for Dear ImGui 1.87: + - sokol_imgui.h's input code has been rewritten to use the new evented IO + system and extended virtual key codes in Dear ImGui + - on non-Emscripten platforms, mouse buttons are no longer "cancelled" when + the mouse leaves the window (since the native desktop platforms + automatically capture the mouse when mouse buttons are pressed, but mouse + capture is not supported in the sokol_app.h Emscripten backend) + +- **28-Jan-2022**: some window size behaviour changes in sokol_app.h. + - Asking for a default-sized window (via sapp_desc.width/height = 0) now + behaves a bit differently on desktop platforms. Previously this set the + window size to 640x480, now a default window covers more screen area: + - on Windows CW_USEDEFAULT will be used for the size + - on macOS and Linux, the window size will be 4/5 of the + display size + - no behaviour changes on other platforms + - On Windows and Linux, the window is now centered (in a later update, + more control over the initial window position, and new functions for + positioning and sizing might be provided) + - On Windows, when toggling between windowed and fullscreen, the + window position and size will now be restored (on other platforms + this already happened automatically through the window system) + - On all desktop platforms if an application starts in fullscreen and + then is toggled back to windowed, the window will now be of the + expected size (provided in sapp_desc.width/height) + +- **20-Jan-2022**: + - sokol_audio.h: A compatibility fix in the sokol_audio.h WASAPI backend (Windows): On + some configs the IAudioClient::Initialize() call could fail because + of a mismatch between the requested number of channels and speaker config. + See [#614](https://github.com/floooh/sokol/issues/614) for details. + - sokol_app.h D3D11/DXGI: Fix an (uncritical) COM interface leak warning for IDXGIAdapter and + IDXGIFactory at shutdown, introduced with the recent disabling of Alt-Enter. + +- **18-Jan-2022**: + - sokol_app.h now has per-monitor DPI support on Windows and macOS: when + the application window is moved to a monitor with different DPI, the values + returned by sapp_dpi_scale(), sapp_width() and sapp_height() will update + accordingly (only if the application requested high-dpi rendering with + ```sapp_desc.high_dpi=true```, otherwise the dpi scale value remains + fixed at 1.0f). The application will receive an SAPP_EVENTTYPE_RESIZED event + if the default framebuffer size has changed because of a DPI change. + On Windows this feature requires Win10 version 1703 or later (aka the + 'Creators Update'), older Windows version simply behave as before. + Many thank to @tjachmann for the initial PR with the Windows implementation! + - sokol_app.h: DPI scale computation on macOS is now more robust using the + NSScreen.backingScaleFactor value + - sokol_app.h: the new frame timing code in sokol_app.h now detects if the display + refresh rate changes and adjusts itself accordingly (for instance if the + window is moved between displays with different refresh rate) + - sokol_app.h D3D11/DXGI: during window movement and resize, the frame is now + presented with DXGI_PRESENT_DO_NOT_WAIT, this fixes some window system + stuttering issues on Win10 configs with recent NVIDIA drivers. + - sokol_app.h D3D11/DXGI: the application will no longer appear to freeze for + 0.5 seconds when the title bar is grabbed with the mouse for movement, but + then not moving the mouse. + - sokol_app.h D3D11/DXGI: DXGI's automatic windowed/fullscreen switching via + Alt-Enter has been disabled, because this switched to 'real' fullscreen mode, + while sokol_app.h's fullscreen mode uses a borderless window. Use the + programmatic fullscreen/window switching via ```sapp_toggle_fullscreen()``` + instead. + - **BREAKING CHANGE** in sokol_imgui.h: because the applications' DPI scale + can now change at any time, the DPI scale value is now communicated to + sokol_imgui.h in the ```simgui_new_frame()``` function. This has been + changed to accept a pointer to a new ```simgui_frame_desc_t``` struct. + With C99, change the simgui_new_frame() call as follows (if also using + sokol_app.h): + ```c + simgui_new_frame(&(simgui_frame_desc_t){ + .width = sapp_width(), + .height = sapp_height(), + .delta_time = sapp_frame_duration(), + .dpi_scale = sapp_dpi_scale() + }); + ``` + On C++ this works: + ```c++ + simgui_new_frame({ sapp_width(), sapp_height(), sapp_frame_duration(), sapp_dpi_scale() }); + ``` + ...or in C++20: + ```c++ + simgui_new_frame({ + .width = sapp_width(), + .height = sapp_height(), + .delta_time = sapp_frame_duration(), + .dpi_scale = sapp_dpi_scale() + }); + ``` + - **KNOWN ISSUE**: the recent change in sokol-audio's WASAPI backend to directly consume + float samples doesn't appear to work on some configs (see [#614](https://github.com/floooh/sokol/issues/614)), + investigation is underway + +- **15-Jan-2022**: + - A bugfix in the GL backend for uniform arrays using the 'native' uniform block layout. + The bug was a regression in the recent 'uniform data handling' update. See + [PR #611](https://github.com/floooh/sokol/pull/611) for details, and this [new sample/test](https://github.com/floooh/sokol-samples/blob/master/glfw/uniformarrays-glfw.c). + Many thanks to @nmr8acme for the PR! + +- **08-Jan-2022**: some enhancements and cleanup to uniform data handling in sokol_gfx.h + and the sokol-shdc shader compiler: + - *IMPORTANT*: when updating sokol_gfx.h (and you're using the sokol-shdc shader compiler), + don't forget to update the sokol-shdc binaries too! + - The GLSL uniform types int, ivec2, ivec3 and + ivec4 can now be used in shader code, those are exposed to the GL + backends with the new ```sg_uniform_type``` items + ```SG_UNIFORM_TYPE_INT[2,3,4]```. + - A new enum ```sg_uniform_layout```, currently with the values SG_UNIFORMLAYOUT_NATIVE + and SG_UNIFORMLAYOUT_STD140. The enum is used in ```sg_shader_uniform_block_desc``` + as a 'packing rule hint', so that the GL backend can properly locate the offset + of uniform block members. The default (SG_UNIFORMLAYOUT_NATIVE) keeps the same + behaviour, so existing code shouldn't need to be changed. With the packing + rule SG_UNIFORMLAYOUT_STD140 the uniform block interior is expected to be + laid out according to the OpenGL std140 packing rule. + - Note that the SG_UNIFORMLAYOUT_STD140 only allows a subset of the actual std140 + packing rule: arrays are only allowed for the types vec4, int4 and mat4. + This is because the uniform data must still be compatible with + ```glUniform()``` calls in the GL backends (which have different + 'interior alignment' for arrays). + - The sokol-shdc compiler supports the new uniform types and will annotate the + code-generated sg_shader_desc structs with SG_UNIFORMLAYOUT_STD140, + and there are new errors to make sure that uniform blocks are compatible + with all sokol_gfx.h backends. + - Likewise, sokol_gfx.h has tighter validation for the ```sg_shader_uniform_block``` + desc struct, but only when the GL backend is used (in general, the interior + layout of uniform blocks is only relevant for GL backends, on all other backends + sokol_gfx.h just passes the uniform data as an opaque block to the shader) + For more details see: + - [new sections in the sokol_gfx.h documentation](https://github.com/floooh/sokol/blob/ba64add0b67cac16fc86fb6b64d1da5f67e80c0f/sokol_gfx.h#L343-L450) + - [documentation of ```sg_uniform_layout```](https://github.com/floooh/sokol/blob/ba64add0b67cac16fc86fb6b64d1da5f67e80c0f/sokol_gfx.h#L1322-L1355) + - [enhanced sokol-shdc documentation](https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md#glsl-uniform-blocks-and-c-structs) + - [a new sample 'uniformtypes-sapp'](https://floooh.github.io/sokol-html5/uniformtypes-sapp.html) + + PS: and an unrelated change: the frame latency on Win32+D3D11 has been slightly improved + via IDXGIDevice1::SetMaximumFrameLatency() + +- **27-Dec-2021**: sokol_app.h frame timing improvements: + - A new function ```double sapp_frame_duration(void)``` which returns the frame + duration in seconds, averaged over the last 256 frames to smooth out + jittering spikes. If available, this uses platform/backend specific + functions of the swapchain API: + - On Windows: DXGI's GetFrameStatistics().SyncQPCTime. + - On Emscripten: the timestamp provided by the RAF callback, this will + still be clamped and jittered on some browsers, but averaged over + a number of frames yields a pretty accurate approximation + of the actual frame duration. + - On Metal, ```MTLDrawable addPresentedHandler + presentedTime``` + doesn't appear to function correctly on macOS Monterey and/or M1 Macs, so + instead mach_absolute_time() is called at the start of the MTKView + frame callback. + - In all other situations, the same timing method is used as + in sokol_time.h. + - On macOS and iOS, sokol_app.h now queries the maximum display refresh rate + of the main display and uses this as base to compute the preferred frame + rate (by multiplying with ```sapp_desc.swap_interval```), previously the + preferred frame rate was hardwired to ```60 * swap_interval```. This means + that native macOS and iOS applications may now run at 120Hz instead of + 60Hz depending on the device (I realize that this isn't ideal, there + will probably be a different way to hint the preferred interval at + which the frame callback is called, which would also support disabling + vsync and probably also adaptive vsync). + +- **19-Dec-2021**: some sokol_audio.h changes: + - on Windows, sokol_audio.h no longer converts audio samples + from float to int16_t, but instead configures WASAPI to directly accept + float samples. Many thanks to GitHub user iOrange for the PR! + - sokol_audio.h has a new public function ```saudio_suspended()``` which + returns true if the audio device/context is currently in suspended mode. + On all backends except WebAudio this always returns false. This allows + to show a visual hint to the user that audio is muted until the first + input event is received. + +- **18-Dec-2021**: the sokol_gfx.h ```sg_draw()``` function now uses the currently applied + pipeline object to decide if the GL or D3D11 backend's instanced drawing function + should be called instead of the ```num_instances``` argument. This fixes a + bug on some WebGL configs when instanced rendering is configured + but ```sg_draw()``` is called with an instance count of 1. + +- **18-Nov-2021**: sokol_gl.h has a new function to control the point size for + point list rendering: ```void sgl_point_size(float size)```. Note that on D3D11 + the point size is currently ignored (since D3D11 doesn't support a point size at + all, the feature will need to be emulated in sokol_gl.h when the D3D11 backend is active). + Also note that points cannot currently be textured, only colored. + +- **08-Oct-2021**: texture compression support in sokol_gfx.h has been revisited: + - tighter validation checks on texture creation: + - content data validation now also happens in ```sg_make_image()``` (previously only in ```sg_update_image()```) + - validate that compressed textures are immutable + - separate "no data" validation checks for immutable vs dynamic/stream textures + - provided data size for creating or updating textures must match the expected surface sizes exactly + - fix PVRTC row and surface pitch computation according to the GL PVRTC extension spec + - better adhere to Metal documentation for the ```MTLTexture.replaceRegion``` parameters (when bytesPerImage is expected to be zero or not) + +- **02-Sep-2021**: some minor non-breaking additions: + - sokol_app.h: new events FOCUSED and UNFOCUSED to indicate that the + window has gained or lost the focused state (Win32: WM_SETFOCUS/WM_KILLFOCUS, + macOS: windowDidBecomeKey/windowDidResignKey, X11: FocusIn/FocusOut, + HTML5: focus/blur). + - sokol_app.h Emscripten backend: the input event keycode is now extracted + from the HTML5 code string which yields the actual unmapped virtual key code. + +- **21-Aug-2021**: some minor API tweaks in sokol_gl.h and sokol_debugtext.h, + one of them breaking (still minor though): + - sokol_gl.h has a new function ```sgl_default_context()``` which returns the + default context handle, it's the same as the global constant SGL_DEFAULT_CONTEXT, + but wrapping this in a function is better for language bindings + - ...and a similar function in sokol_debugtext.h: ```sdtx_default_context()``` + - The sokol_gl.h function ```sgl_default_pipeline()``` has been renamed to + ```sgl_load_default_pipeline()```. This fits better with the related + function ```sgl_load_pipeline()``` and doesn't 'semantically clash' + with the new function sgl_default_context(). The sgl_default_pipeline() + function is rarely used, so it's quite unlikely that this change breaks + your code. + +- **19-Aug-2021**: sokol_gl.h gained rendering context support, this allows + sokol-gl to render into different sokol-gfx render passes. No changes are + needed for existing sokol-gl code. Check the updated + [header documentation](https://github.com/floooh/sokol/blob/master/util/sokol_gl.h) + and the new sample + [sgl-context-sapp](https://floooh.github.io/sokol-html5/sgl-context-sapp.html) + for details! + +- **21-Jun-2021**: A new utility header sokol_color.h has been added, which adds + sokol_gfx.h-compatible named color constants and a handful initial utility + functions. See the [header documentation](https://github.com/floooh/sokol/blob/master/util/sokol_color.h) + for details. Many thanks to Stuart Adams (@nyalloc) for contributing the header! + +- **12-Apr-2021**: Minor new feature in sokol_app.h: mouse buttons are now + also reported as modifier flags in most input events (similar to the + Ctrl-, Alt-, Shift- and Super-key modifiers). This lets you quickly check + what mouse buttons are currently pressed in any input event without having + to keep track of pressed mouse buttons yourself. This is implemented in the following + sokol_app.h backends: Win32, UWP, Emscripten, X11 and macOS. Example + code is in the [events-sapp.cc](https://floooh.github.io/sokol-html5/events-sapp.html) sample + +- **10-Apr-2021**: followup fixes from yesterday: custom icon support on macOS + has been added (since macOS has no regular window icons, the dock icon is + updated instead), and a bugfix in the internal helper which select the + best matching candidate image (this actually always selected the first + candidate image) + +- **09-Apr-2021**: sokol_app.h now allows to programmatically set the window + icon in the Win32, X11 and HTML5 backends. Search for "WINDOW ICON SUPPORT" + in sokol_app.h for documentation, and see the new + [icon sample](https://floooh.github.io/sokol-html5/icon-sapp.html) for example code. + +- **01-Apr-2021**: some fixes in sokol_app.h's iOS backend: + - In the iOS Metal backend, high-dpi vs low-dpi works again. Some time + ago (around iOS 12.x) MTKView started to ignore the contentScaleFactor + property, which lead to sokol_app.h always setting up a HighDPI + framebuffer even when sapp_desc.high_dpi wasn't set. The fix is to set + the MTKView's drawableSize explicitly now. + - The iOS GL backend didn't support MSAA multisampling so far, this has + been fixed now, but only one MSAA mode (4x) is available, which will be + selected when sapp_desc.sample_count is greater than 1. + +- **31-Mar-2021**: sokol_audio.h on macOS no longer includes system framework + headers (AudioToolbox/AudioToolbox.h), instead the necessary declarations + are embedded directly in sokol_audio.h (to get the old behaviour and + force inclusion of AudioToolbox/AudioToolbox.h, define + ```SAUDIO_OSX_USE_SYSTEM_HEADERS``` before including the sokol_audio.h + implementation). This "fix" is both an experiment and an immediate workaround + for a current issue in Zig's HEAD version (what will eventually become + zig 0.8.0). See this issue for details: https://github.com/ziglang/zig/issues/8360). + The experiment is basically to see whether this approach generally makes sense + (replacing system headers with embedded declarations, so that the sokol headers + only depend on C standard library headers). This approach might + simplify cross-compilation and integration with other languages than C and C++. + +- **20-Mar-2021**: The Windows-specific OpenGL loader, and the platform-specific +GL header includes have been moved from sokol_app.h to sokol_gfx.h. This means: + - In general, the sokol_gfx.h implementation can now simply be included + without having to include other headers which provide the GL API declarations + first (e.g. when sokol_gfx.h is used without sokol_app.h, you don't need to + use a GL loader, or include the system-specific GL headers yourself). + - When sokol_gfx.h is used together with sokol_app.h, the include order + for the implementations doesn't matter anymore (until now, the sokol_app.h + implementation had to be included before the sokol_gfx.h implementation). + - The only "downside" (not really a downside) is that sokol_gfx.h now has + platform detection ifdefs to include the correct GL headers for a given + platform. Until now this problem was "delegated" to the library user. + - The old macro **SOKOL_WIN32_NO_GL_LOADER** has been removed, and replaced + with a more general **SOKOL_EXTERNAL_GL_LOADER**. Define this before + including the sokol_gfx.h implementation if you are using your own GL + loader or provide the GL API declarations in any other way. In this case, + sokol_gfx.h will not include any platform GL headers, and the embedded + Win32 GL loader will be disabled. + +- **22-Feb-2021**: Mouse input latency in sokol_app.h's macOS backend has been + quite significantly reduced, please see the detailed explanation [in this + PR](https://github.com/floooh/sokol/pull/483). Many thanks to @randrew for + the PR! + +- **19-Feb-2021**: sokol_app.h learned some Windows-specific config options +to redirect stdout/stderr to the parent terminal or a separate console +window, and allow outputting UTF-8 encoded text. For details, search for +"WINDOWS CONSOLE OUTPUT" in +[sokol_app.h](https://github.com/floooh/sokol/blob/master/sokol_app.h). Many +thanks to @garettbass for the initial PR! + +- **17-Feb-2021**: When compiled for iOS, the sokol_audio.h CoreAudio backend now +uses the **AVAudioSession** class to activate and deactivate audio output as needed. +This fixes sokol_audio.h for iPhones (so far, sokol_audio.h accidentally only worked +for iPads). Please see [this issue](https://github.com/floooh/sokol/issues/431) for details. +A somewhat unfortunate side effect of this fix is that sokol_audio.h must now be compiled +as Objective-C when targeting iOS, also note that a new framework must be linked: ```AVFoundation```. +Many thanks to @oviano for providing the PR! + +- **14-Feb-2021**: The Dear ImGui rendering backend in [sokol_imgui.h](https://github.com/floooh/sokol/blob/master/util/sokol_imgui.h) has been rewritten to only do a single +buffer-update per frame each for vertex- and index-data. This addresses performance-problems +with sg_append_buffer() in the GL backend on some platforms (see [this issue](https://github.com/floooh/sokol/issues/399) for details. + +- **13-Feb-2021**: A new utility header [sokol_nuklear.h](https://github.com/floooh/sokol/blob/master/util/sokol_nuklear.h) +has been added which implements a rendering backend for [Nuklear](https://github.com/Immediate-Mode-UI/Nuklear) +on top of sokol_gfx.h. Also see the new sample [nuklear-sapp](https://floooh.github.io/sokol-html5/nuklear-sapp.html). +Many thanks to **@wmerrifield** for the PR! + +- **10-Feb-2021**: The breaking API-update has been merged (mainly sokol_gfx.h). +Please see [this blogpost](https://floooh.github.io/2021/02/07/sokol-api-overhaul.html) +and the updates [sokol samples](https://floooh.github.io/sokol-html5/) for details. +I also created a git tag named 'pre-feb2021-api-changes' which captures the previous +state in all related projects. Please also update the [sokol-tools-bin](https://github.com/floooh/sokol-tools-bin) if you're using the sokol-shdc shader compiler. + +- **07-Feb-2021**: A PSA about upcoming breaking changes in (mainly) sokol_gfx.h: https://floooh.github.io/2021/02/07/sokol-api-overhaul.html + +- **20-Dec-2020**: A couple of minor breaking changes in the sokol_gfx.h and +sokol_app.h APIs as preparation for the upcoming automatic language binding +generation: + - in **sokol_gfx.h** nested unions have been removed: + - **sg_image_desc.depth/.layers** has been renamed to **.num_slices** + - **sg_attachment_desc.face/.layer/.slice** has been unified to **.slice** + - in **sokol_app.h** the return value of **sapp_run()** has been changed from + **int** to **void** (the function always returned zero anyway) + + Non-breaking (or at most potentially breaking) changes: + - expressions in enums have been replaced with integer literals (e.g. (1<<2) becomes 4) + - the value of **SAPP_MOUSEBUTTON_INVALID** has been changed from -1 to 0x100 + + For more information about the upcoming automatic language-bindings generation [see this bog post](https://floooh.github.io/2020/08/23/sokol-bindgen.html) + +- **02-Dec-2020**: sokol_gfx.h has a couple new public API functions for +destroying resources in two steps: + - sg_uninit_buffer + sg_dealloc_buffer + - sg_uninit_image + sg_dealloc_image + - sg_uninit_shader + sg_dealloc_shader + - sg_uninit_pipeline + sg_dealloc_pipeline + - sg_uninit_pass + sg_dealloc_pass + + Calling both functions in this order is identical with calling the + traditional sg_destroy_xxx() functions. See this PR for more details: + https://github.com/floooh/sokol/pull/435. Many thanks to @oviano for the + PR! + +- **28-Nov-2020**: In addition to the generic SOKOL_API_DECL and SOKOL_IMPL +defines there are now header-specific versions SOKOL_xxx_API_DECL and +SOKOL_xxx_IMPL (for instance SOKOL_GFX_API_DECL and SOKOL_GFX_IMPL). The +original motivation for splitting the SOKOL_API_DECL defines up is described +here: https://github.com/floooh/sokol/issues/428). The same change for +SOKOL_IMPL also finally unifies the approach used in the utility headers (in +the ```util``` subdirectory), which exclusively used the SOKOL_xxx_IMPL +pattern with the core headers which exclusively used SOKOL_IMPL before (all +headers accept both patterns now). Many thanks to @iboB for providing the +API_DECL PR! + +- **17-Nov-2020**: A new utility header **sokol_shape.h** to generate + vertices+indices for simple shapes (plane, box, sphere, cylinder and torus), + which seamlessly plug into the sokol_gfx.h resource creation functions. As + with most new utility headers, the initial functionality is a bit bare bones + and the public API shouldn't be considered stable yet. Check the sokol-samples + webpage for new and updates samples: https://floooh.github.io/sokol-html5/ + +- **08-Nov-2020** PSA: It appears that RenderDoc v1.10 chokes on the new + D3D11/DXGI swapchain code from 10-Oct-2020 in sokol_app.h. The current + RenderDoc Nightly Build works, so I guess in v1.11 everything will be fine. + +- **03-Nov-2020**: sokol_app.h: the missing drag'n'drop support for HTML5/WASM + has been added. This adds two platform-specific functions + ```sapp_html5_get_dropped_file_size()``` and + ```sapp_html5_fetch_dropped_file()```. Please read the documentation + section in sokol_app.h under 'DRAG AND DROP SUPPORT' for additional + details and example code. Also consult the source code of the new + ```droptest-sapp``` sample for an example of how to load the content + of dropped files on the web and native platforms: + + https://floooh.github.io/sokol-html5/droptest-sapp.html + + +- **27-Oct-2020**: I committed a bugfix for a longstanding WebGL canvas id versus + css-selector confusion in the emscripten/WASM backend code in sokol_app.h. + I think the fix should not require any changes in your code (because if + you'd be using a canvas name different from the default "canvas" it wouldn't + have worked before anyway). See this bug for details: https://github.com/floooh/sokol/issues/407 + +- **22-Oct-2020**: sokol_app.h now has file drag'n'drop support on Win32, + macOS and Linux. WASM/HTML5 support will be added soon-ish. This will + work a bit differently because of security-related restrictions in the + HTML5 drag'n'drop API, but more on that later. For documentation, + search for 'DRAG AND DROP SUPPORT' in [sokol_app.h](https://github.com/floooh/sokol/blob/master/sokol_app.h). + + Check out [events-sapp.c](https://github.com/floooh/sokol-samples/blob/master/sapp/events-sapp.cc) + for a simple usage example (I will also add a more real-world example to my + chips emulators once the WASM/HTML5 implementation is ready). + + Many thanks for @prime31 and @hb3p8 for the initial PRs and valuable feature + discussions! + +- **10-Oct-2020**: Improvements to the sokol_app.h Win32+D3D11 and UWP+D3D11 swapchain code: + - In the Win32+D3D11 backend and when running on Win10, + ```DXGI_SWAP_EFFECT_FLIP_DISCARD``` is now used. This gets rid of a + deprecation warning in the debugger console and also should allow slightly + more efficient swaps in some situations. When running on Win7 or Win8, the + traditional ```DXGI_SWAP_EFFECT_DISCARD``` is used. + - The UWP backend now supports MSAA multisampling (the required fixes for + this are the same as in the Win32 backend with the new swap effect: a + separate MSAA texture and render-target-view is created where + rendering goes into, and this MSAA texture is resolved into the actual + swapchain surface before presentation). + +- **07-Oct-2020**: + A fix in the ALSA/Linux backend initialization in sokol_audio.h: Previously, + initialization would fail if ALSA can't allocate the exact requested + buffer size. Instead sokol_audio.h let's now pick ALSA a suitable buffer + size. Also better log messages in the ALSA initialization code if something + goes wrong. Unfortunately I'm not able to reproduce the buffer allocation + problem on my Linux machine. Details are in this issue: https://github.com/floooh/sokol/issues/400 + + **NARRATOR**: the fix didn't work. + +- **02-Oct-2020**: + The sokol_app.h Win32 backend can now render while moving and resizing + the window. NOTE that resizing the swapchain buffers (and receiving + SAPP_EVENTTYPE_RESIZED events) is deferred until the resizing finished. + Resizing the swapchain buffers each frame created a substantial temporary + memory spike of up to several hundred MBytes. I need to figure out a better + swapchain resizing strategy. + +- **30-Sep-2020**: + sokol_audio.h now works on UWP, thanks again to Alberto Fustinoni + (@albertofustinoni) for the PR! + +- **26-Sep-2020**: + sokol_app.h gained a new function sapp_set_window_title() to change + the window title on Windows, macOS and Linux. Many thanks to + @medvednikov for the initial PR! + +- **23-Sep-2020**: + sokol_app.h now has initial UWP support using the C++/WinRT set of APIs. + Currently this requires "bleeding edge" tools: A recent VS2019 version, + and a very recent Windows SDK version (at least version 10.0.19041.0). + Furthermore the sokol_app.h implementation must be compiled as C++17 + (this is a requirement of the C++/WinRT headers). Note that the Win32 + backend will remain the primary and recommended backend on Windows. The UWP + backend should only be used when the Win32 backend is not an option. + The [sokol-samples](https://github.com/floooh/sokol-samples) project + has two new build configs ```sapp-uwp-vstudio-debug``` and + ```sapp-uwp-vstudio-release``` to build the sokol-app samples for UWP. + + Many thanks to Alberto Fustinoni (@albertofustinoni) for providing + the initial PR! + + (also NOTE: UWP-related fixes in other sokol headers will follow) + +- **22-Sep-2020**: + A small fix in sokol_app.h's Win32 backend: when a mouse button is pressed, + mouse input is now 'captured' by calling SetCapture(), and when the last + mouse button is released, ReleaseCapture() is called. This also provides + mouse events outside the window area as long as a mouse button is pressed, + which is useful for windowed UI applicactions (this is not the same as the + more 'rigorous' and explicit pointer-lock feature which is more useful for + camera-controls) + +- **31-Aug-2020**: + Internal change: The D3D11/DXGI backend code in sokol_gfx.h and sokol_app.h + now use the D3D11 and DXGI C++-APIs when the implementation is compiled as + C++, and the C-APIs when the implementation is compiled as C (before, the C + API was also used when the implementation is compiled as C++). The new + behaviour is useful when another header *must* use the D3D11/DXGI C++ APIs + but should be included in the same compilation unit as sokol_gfx.h an + sokol_app.h (for example see this PR: + https://github.com/floooh/sokol/pull/351). + +- **24-Aug-2020**: + The backend-specific callback functions that are provided to sokol_gfx.h + in the ```sg_setup()``` initialization call now have alternative + versions which accept a userdata-pointer argument. The userdata-free functions + still exist, so no changes are required for existing code. + +- **02-Aug-2020**: + - sokol_app.h now has a mouse-lock feature (aka pointer-lock) via two + new functions ```void sapp_lock_mouse(bool lock)``` and ```bool sapp_mouse_locked(void)```. + For documentation, please search for 'MOUSE LOCK' in sokol_app.h. + The sokol-app samples [events-sapp](https://floooh.github.io/sokol-html5/events-sapp.html) + and [cgltf-sapp](https://floooh.github.io/sokol-html5/cgltf-sapp.html) have been + updated to demonstrate the feature. + - sokol_app.h Linux: mouse pointer visibility (via ```void sapp_show_mouse(bool show)```) + has been implemented for Linux/X11 + - sokol_app.h WASM: mouse wheel scroll deltas are now 'normalized' between + the different scroll modes (pixels, lines, pages). See this issue: + https://github.com/floooh/sokol/issues/339. Many thanks to @bqqbarbhg for + investigating the issue and providing a solution! + - sokol_app.h now has [better documentation](https://github.com/floooh/sokol/blob/89a3bb8da0a2df843d6cc60a270ddc69f9aa69d6/sokol_app.h#L70) + what system libraries must be linked on the various platforms (and on Linux two additional libraries must be + linked now: Xcursor and Xi) + +- **22-Jul-2020**: **PLEASE NOTE** cmake 3.18 breaks some of sokol samples when + compiling with the Visual Studio toolchain because some C files now actually + compile as C++ for some reason (see: + https://twitter.com/FlohOfWoe/status/1285996526117040128). Until this is + fixed, or I have come up with a workaround, please use an older cmake version + to build the sokol samples with the Visual Studio compiler. + + (Update: I have added a workaround to fips: https://github.com/floooh/fips/commit/89997b8ebdca6fc9455a5cfe6145eecaa017df49 + which fixes the issue at least for fips projects) + +- **14-Jul-2020**: + - sapp_mouse_shown() has been implemented for macOS (thanks to @slmjkdbtl for + providing the initial PR!) + - On macOS, the lower-level functions CGDisplayShowCursor and CGDisplayHideCursor + are now used instead of the NSCursor class. This is in preparation for the + 'pointer lock' feature which will also use CGDisplay* functions. + - Calling ```sapp_show_mouse(bool visible)``` no longer 'stacks' (e.g. there's + no 'hidden counter' underneath anymore, instead calling ```sapp_show_mouse(true)``` + will always show the cursor and ```sapp_show_mouse(false)``` will always + hide it. This is a different behaviour than the underlying Win32 and + macOS functions ShowCursor() and CGDisplaShow/HideCursor() + - The mouse show/hide behaviour can now be tested in the ```events-sapp``` sample + (so far this only works on Windows and macOS). + +- **13-Jul-2020**: + - On macOS and iOS, sokol_app.h and sokol_gfx.h can now be compiled with + ARC (Automatic Reference Counting) **disabled** (previously ARC had to be + enabled). + - Compiling with ARC enabled is still supported but with a little caveat: + if you're compiling sokol_app.h or sokol_gfx.h in ObjC mode (not ObjC++ + mode) *AND* ARC is enabled, then the Xcode version must be more recent + than before (the language feature ```__has_feature(objc_arc_fields)``` + must be supported, which I think has been added in Xcode 10.2, I couldn't + find this mentioned in any Xcode release notes though). Compiling with + ARC disabled should also work on older Xcode versions though. + - Various internal code cleanup things: + - sokol_app.h had the same 'structural cleanup' as sokol_gfx.h in + January, all internal state (including ObjC id's) has been merged into + a single big state structure. Backend specific struct declarations + have been moved closer together in the header, and + backend-specific structures and functions have been named more + consistently for better 'searchability' + - The 'mini GL' loader in the sokol_app.h Win32+WGL backend has been + rewritten to use X-Macros (less redundant lines of code) + - All macOS and iOS code has been revised and cleaned up + - On macOS a workaround for a (what looks like) post-Catalina + NSOpenGLView issue has been added: if the sokol_app.h window doesn't + fit on screen (and was thus 'clamped' by Cocoa) *AND* the + content-size was not set to native Retina resolution, the initial + content size was reported as if it was in Retina resolution. This + caused an empty screen to be rendered in the imgui-sapp demo. The + workaround is to hook into the NSOpenGLView reshape event at which + point the reported content size is correct. + - On macOS and iOS, the various 'view delegate' objects have been + removed, and rendering happens instead in the subclasses of MTKView, + GLKView and NSOpenGLView. + - On macOS and iOS, there's now proper cleanup code in the + applicationWillTerminate callback (although note that on iOS this + function isn't guaranteed to be called, because an application can + also simply be killed by the operating system. + +- **22-Jun-2020**: The X11/GLX backend in sokol_app.h now has (soft-)fullscreen +support, bringing the feature on par with Windows and macOS. Many thanks to +@medvednikov for the PR! + +- **20-Jun-2020**: Some work to better support older DX10-level GPUs in the +sokol_gfx.h D3D11 backend: + - sg_make_shader() now by default compiles HLSL shader code as shader model 4.0 + (previously shader model 5.0 which caused problems with some older + Intel GPUs still in use, see this issue: https://github.com/floooh/sokol/issues/179) + - A new string item ```const char* d3d11_target``` in ```sg_shader_stage_desc``` now allows + to pass in the D3D shader model for compiling shaders. This defaults to + "vs_4_0" for the vertex shader stage and "ps_4_0" for the fragment shader stage. + The minimal DX shader model for use with the sokol_gfx.h D3D11 backend is + shader model 4.0, because that's the first shader model supporting + constant buffers. + - The *sokol-shdc* shader compiler tool has a new output option ```hlsl4``` + to generate HLSL4 source code and shader model 4.0 byte code. + - All embedded D3D shader byte code in the sokol utility headers has been + changed from shader model 5.0 to 4.0 + + If you are using sokol_gfx.h with sokol-shdc, please update both at the same time + to avoid compilation errors caused by the new ```sg_shader_stage_desc.d3d11_target``` + item. The sg_shader_desc initialization code in sokol-shdc has now been made more + robust to prevent similar problems in the future. + +- **14-Jun-2020**: I have added a very simple utility header ```sokol_memtrack.h``` +which allows to track memory allocations in sokol headers (number and overall +size of allocations) by overriding the macros SOKOL_MALLOC, SOKOL_CALLOC and +SOKOL_FREE. Simply include ```sokol_memtrack.h``` before the other sokol +header implementation includes to enable memory tracking in those headers +(but remember that the sokol_memtrack.h implementation must only be included +once in the whole project, so this only works when all other sokol header +implementations are included in the same compilation unit). + +- **06-Jun-2020**: Some optimizations in the sokol_gfx.h GL backend to avoid + redundant GL calls in two areas: in the sg_begin_pass() calls when not + clearing the color- and depth-stencil-attachments, and in sg_apply_bindings() + when binding textures. Everything should behave exactly as before, but if + you notice any problems in those areas, please file a bug. Many thanks to + @edubart for the PRs! + +- **01-Jun-2020**: sokol_app.h now allows to toggle to and from fullscreen +programmatically and to query the current fullscreen state via 2 new +functions: ```sapp_toggle_fullscreen()``` and ```sapp_is_fullscreen()```. +Currently this is only implemented for Windows and macOS (not Linux). +Thanks to @mattiasljungstrom for getting the feature started and providing +the Win32 implementation! + +- **28-May-2020**: a small quality-of-life improvement for C++ coders: when the +sokol headers are included into C++, all public API functions which take a +pointer to a struct now have a C++ overload which instead takes a const-ref. +This allows to move the struct initialization right into the function call +just like in C99. For instance, in C99 one can write: + ```c + sg_buffer buf = sg_make_buffer(&(sg_buffer_desc){ + .size = sizeof(vertices), + .type = SG_BUFFERTYPE_VERTEXBUFFER, + .content = vertices + }); + ``` + In C++ it isn't possible to take the address of an 'adhoc-initialized' + struct like this, but with the new reference-wrapper functions (and C++20 + designated initialization) this should work now: + ```cpp + sg_buffer buf = sg_make_buffer({ + .size = sizeof(vertices), + .type = SG_BUFFERTYPE_VERTEXBUFFER, + .content = vertices + }); + ``` + Many thanks to @garettbass for providing the PR! + + +- **27-May-2020**: a new utility header [sokol_debugtext.h](https://github.com/floooh/sokol/blob/master/util/sokol_debugtext.h) +for rendering simple ASCII text using vintage home computer fonts via sokol_gfx.h + +- **13-May-2020**: a new function in sokol_time.h to round a measured frame time +to common display refresh rates: ```stm_round_to_common_refresh_rate()```. +See the header documentation for the motivation behind this function. + +- **02-May-2020**: sokol_app.h: the 'programmatic quit' behaviour on the +web-platform is now more in line with other platforms: calling +```sapp_quit()``` will invoke the cleanup callback function, perform +platform-specific cleanup (like unregistering JS event handlers), and finally +exit the frame loop. In typical scenarios this isn't very useful (because +usually the user will simply close the tab, which doesn't allow to run +cleanup code), but it's useful for situations where the same +code needs to run repeatedly on a web page. Many thanks to @caiiiycuk +for providing the PR! + +- **30-Apr-2020**: experimental WebGPU backend and a minor breaking change: + - sokol_gfx.h: a new WebGPU backend, expect frequent breakage for a while + because the WebGPU API is still in flux + - a new header sokol_glue.h, with interop helper functions when specific combinations + of sokol headers are used together + - changes in the way sokol_gfx.h is initialized via a new layout of the + sg_desc structure + - sokol_gfx.h: a new ```sg_sampler_type``` enum which is required for + shader creation to tell the WebGPU backend about the sampler data types + (float, signed int, or unsigned int) used in the shader + - sokol_app.h: a handful new functions to query default framebuffer attributes (color- and + depth-buffer pixel formats, and MSAA sample count) + - sokol_app.h: WebGPU device and swapchain initialization (currently only + in the emscripten code path) + - [sokol-shdc](https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md) has + been updated with WebGPU support (currently outputs SPIRV bytecode), and to output the new + ```sg_sampler_type``` enum in ```sg_shader_image_desc``` + - [sokol-samples](https://github.com/floooh/sokol-samples/) has a new set of + backend-specific WebGPU samples, and the other samples have been updated + for the new sokol-gfx initialization + - ```pre-webgpu``` tags have been added to the [sokol](https://github.com/floooh/sokol/releases/tag/pre-webgpu), [sokol-samples](https://github.com/floooh/sokol-samples/releases/tag/pre-webgpu), [sokol-tools](https://github.com/floooh/sokol-tools/releases/tag/pre-webgpu) + and [sokol-tools-bin](https://github.com/floooh/sokol-tools-bin/releases/tag/pre-webgpu) github repositories (in case you need to continue working with + the older versions) + - please see this [blog post](https://floooh.github.io/2020/04/26/sokol-spring-2020-update.html) + for more details + +- **05-Apr-2020**: A bugfix in sokol_gl.h, the (fairly recent) optimization for + merging draw calls contained a bug that could be triggered in an "empty" + sgl_begin/sgl_end pair (with no vertices recorded inbetween). This could + lead to the following draw call being rendered with the wrong uniform data. + +- **30-Jan-2020**: Some cleanup in sokol_gfx.h in the backend implementation code, + internal data structures and documentation comments. The public + API hasn't changed, so the change should be completely invisible + from the outside. + +- **02-Dec-2019**: Initial clipboard support in sokol_app.h for Windows, macOS + and HTML5. This allows to read and write UTF-8 encoded strings from and + to the target platform's shared clipboard. + + A 'real-world' example usage is in the [Visual6502 Remix project](https://github.com/floooh/v6502r). + + Unfortunately clipboard support on the HTML5 platform comes with a lot of + platform-specific caveats which can't be solved in sokol_app.h alone + because of the restrictions the web platform puts on clipboard access and + different behaviours and support levels of the various HTML5 clipboard + APIs. I'm not really happy with the current HTML5 clipboard + implementation. It sorta works, but it sure ain't pretty :) + + Maybe the situation will improve in a few years when all browsers agree + on and support the new [permission-based clipboard + API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API). + + For documentation of the clipboard feature, search for CLIPBOARD SUPPORT + in sokol_app.h + +- **08-Sep-2019**: sokol_gfx.h now supports clamp-to-border texture sampling: + - the enum ```sg_wrap``` has a new member ```SG_WRAP_CLAMP_TO_BORDER``` + - there's a new enum ```sg_border_color``` + - the struct ```sg_image_desc``` has a new member ```sg_border_color border_color``` + - new feature flag in ```sg_features```: ```image_clamp_to_border``` + + Note the following caveats: + + - clamp-to-border is only supported on a subset of platforms, support can + be checked at runtime via ```sg_query_features().image_clamp_to_border``` + (D3D11, desktop-GL and macOS-Metal support clamp-to-border, + all other platforms don't) + - there are three hardwired border colors: transparent-black, + opaque-black and opaque-white (modern 3D APIs have moved away from + a freely programmable border color) + - if clamp-to-border is not supported, sampling will fall back to + clamp-to-edge without a validation warning + + Many thanks to @martincohen for suggesting the feature and providing the initial +D3D11 implementation! + +- **31-Aug-2019**: The header **sokol_gfx_cimgui.h** has been merged into +[**sokol_gfx_imgui.h**](https://github.com/floooh/sokol/blob/master/util/sokol_gfx_imgui.h). +Same idea as merging sokol_cimgui.h into sokol_imgui.h, the implementation +is now "bilingual", and can either be included into a C++ file or into a C file. +When included into a C++ file, the Dear ImGui C++ API will be called directly, +otherwise the C API bindings via cimgui.h + +- **28-Aug-2019**: The header **sokol_cimgui.h** has been merged into +[**sokol_imgui.h**](https://github.com/floooh/sokol/blob/master/util/sokol_imgui.h). +The sokol_cimgui.h header had been created to implement Dear ImGui UIs from +pure C applications, instead of having to fall back to C++ just for the UI +code. However, there was a lot of code duplication between sokol_imgui.h and +sokol_cimgui.h, so that it made more sense to merge the two headers. The C vs +C++ code path will be selected automatically: When the implementation of +sokol_imgui.h is included into a C++ source file, the Dear ImGui C++ API will +be used. Otherwise, when the implementation is included into a C source file, +the C API via cimgui.h + +- **27-Aug-2019**: [**sokol_audio.h**](https://github.com/floooh/sokol/blob/master/sokol_audio.h) + now has an OpenSLES backend for Android. Many thanks to Sepehr Taghdisian (@septag) + for the PR! + +- **26-Aug-2019**: new utility header for text rendering, and fixes in sokol_gl.h: + - a new utility header [**sokol_fontstash.h**](https://github.com/floooh/sokol/blob/master/util/sokol_fontstash.h) + which implements a renderer for [fontstash.h](https://github.com/memononen/fontstash) + on top of sokol_gl.h + - **sokol_gl.h** updates: + - Optimization: If no relevant state between two begin/end pairs has + changed, draw commands will be merged into a single sokol-gfx draw + call. This is especially useful for text- and sprite-rendering (previously, + each begin/end pair would always result in one draw call). + - Bugfix: When calling sgl_disable_texture() the previously active + texture would still remain active which could lead to rendering + artefacts. This has been fixed. + - Feature: It's now possible to provide a custom shader in the + 'desc' argument of *sgl_make_pipeline()*, as long as the shader + is "compatible" with sokol_gl.h, see the sokol_fontstash.h + header for an example. This feature isn't "advertised" in the + sokol_gl.h documentation because it's a bit brittle (for instance + if sokol_gl.h updates uniform block structures, custom shaders + would break), but it may still come in handy in some situations. + +- **20-Aug-2019**: sokol_gfx.h has a couple new query functions to inspect the + default values of resource-creation desc structures: + + ```c + sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc); + sg_image_desc sg_query_image_defaults(const sg_image_desc* desc); + sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc); + sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc); + sg_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc); + ``` + These functions take a pointer to a resource creation desc struct that + may contain zero-initialized values (to indicate default values) and + return a new struct where the zero-init values have been replaced with + concrete values. This is useful to inspect the actual creation attributes + of a resource. + +- **18-Aug-2019**: + - Pixelformat and runtime capabilities modernization in sokol_gfx.h (breaking changes): + - The list of pixel formats supported in sokol_gfx.h has been modernized, + many new formats are available, and some formats have been removed. The + supported pixel formats are now identical with what WebGPU provides, + minus the SRGB formats (if SRGB conversion is needed it should be done + in the pixel shader) + - The pixel format list is now more "orthogonal": + - one, two or four color components (R, RG, RGBA) + - 8-, 16- or 32-bit component width + - unsigned-normalized (no postfix), signed-normalized (SN postfix), + unsigned-integer (UI postfix) and signed-integer (SI postfix) + and float (F postfix) component types. + - special pixel formats BGRA8 (default render target format on + Metal and D3D11), RGB10A2 and RG11B10F + - DXT compressed formats replaced with BC1 to BC7 (BC1 to BC3 + are identical to the old DXT pixel formats) + - packed 16-bit formats (like RGBA4) have been removed + - packed 24-bit formats (RGB8) have been removed + - Use the new function ```sg_query_pixelformat()``` to get detailed + runtime capability information about a pixelformat (for instance + whether it is supported at all, can be used as render target etc...). + - Use the new function ```sg_query_limits()``` to query "numeric limits" + like maximum texture dimensions for different texture types. + - The enumeration ```sg_feature``` and the function ```sg_query_feature()``` + has been replaced with the new function ```sg_query_features()```, which + returns a struct ```sg_features``` (this contains a bool for each + optional feature). + - The default pixelformat for render target images and pipeline objects + now depends on the backend: + - for GL backends, the default pixelformat stays the same: RGBA8 + - for the Metal and D3D11 backends, the default pixelformat for + render target images is now BGRA8 (the reason is because + MTKView's pixelformat was always BGRA8 but this was "hidden" + through an internal hack, and a BGRA swapchain is more efficient + than RGBA in D3D11/DXGI) + - Because of the above RGBA/BGRA change, you may see pixelformat validation + errors in existing code if the code assumes that a render target image is + always created with a default pixelformat of RGBA8. + - Changes in sokol_app.h: + - The D3D11 backend now creates the DXGI swapchain with BGRA8 pixelformat + (previously: RGBA8), this allows more efficient presentation in some + situations since no format-conversion-blit needs to happen. + +- **18-Jul-2019**: + - sokol_fetch.h has been fixed and can be used again :) + +- **11-Jul-2019**: + - Don't use sokol_fetch.h for now, the current version assumes that + it is possible to obtain the content size of a file from the + HTTP server without downloading the entire file first. Turns out + that's not possible with vanilla HTTP when the web server serves + files compressed (in that case the Content-Length is the _compressed_ + size, yet JS/WASM only has access to the uncompressed data). + Long story short, I need to go back to the drawing board :) + +- **06-Jul-2019**: + - new header [sokol_fetch.h](https://github.com/floooh/sokol/blob/master/sokol_fetch.h) for asynchronously loading data. + - make sure to carefully read the embedded documentation + for making the best use of the header + - two new samples: [simple PNG file loadng with stb_image.h](https://floooh.github.io/sokol-html5/loadpng-sapp.html) and [MPEG1 streaming with pl_mpeg.h](https://floooh.github.io/sokol-html5/plmpeg-sapp.html) + - sokol_gfx.h: increased SG_MAX_SHADERSTAGE_BUFFERS configuration + constant from 4 to 8. + +- **10-Jun-2019**: sokol_app.h now has proper "application quit handling": + - a pending quit can be intercepted, for instance to show a "Really Quit?" dialog box + - application code can now initiate a "soft quit" (interceptable) or + "hard quit" (not interceptable) + - on the web platform, the standard "Leave Site?" dialog box implemented + by browsers can be shown when the user leaves the site + - Android and iOS currently don't have any of those features (since the + operating system may decide to terminate mobile applications at any time + anyway, if similar features are added they will most likely have + similar limitations as the web platform) + For details, search for 'APPLICATION QUIT' in the sokol_app.h documentation + header: https://github.com/floooh/sokol/blob/master/sokol_app.h + + The [imgui-highdpi-sapp](https://github.com/floooh/sokol-samples/tree/master/sapp) + contains sample code for all new quit-related features. + +- **08-Jun-2019**: some new stuff in sokol_app.h: + - the ```sapp_event``` struct has a new field ```bool key_repeat``` + which is true when a keyboard event is a key-repeat (for the + event types ```SAPP_EVENTTYPE_KEY_DOWN``` and ```SAPP_EVENTTYPE_CHAR```). + Many thanks to [Scott Lembcke](https://github.com/slembcke) for + the pull request! + - a new function to poll the internal frame counter: + ```uint64_t sapp_frame_count(void)```, previously the frame counter + was only available via ```sapp_event```. + - also check out the new [event-inspector sample](https://floooh.github.io/sokol-html5/wasm/events-sapp.html) + +- **04-Jun-2019**: All sokol headers now recognize a config-define ```SOKOL_DLL``` + if sokol should be compiled into a DLL (when used with ```SOKOL_IMPL```) + or used as a DLL. On Windows, this will prepend the public function declarations + with ```__declspec(dllexport)``` or ```__declspec(dllimport)```. + +- **31-May-2019**: if you're working with emscripten and fips, please note the + following changes: + + https://github.com/floooh/fips#public-service-announcements + +- **27-May-2019**: some D3D11 updates: + - The shader-cross-compiler can now generate D3D bytecode when + running on Windows, see the [sokol-shdc docs](https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md) for more +details. + - sokol_gfx.h no longer needs to be compiled with a + SOKOL_D3D11_SHADER_COMPILER define to enable shader compilation in the + D3D11 backend. Instead, the D3D shader compiler DLL (d3dcompiler_47.dll) + will be loaded on-demand when the first HLSL shader needs to be compiled. + If an application only uses D3D shader byte code, the compiler DLL won't + be loaded into the process. + +- **24-May-2019** The shader-cross-compiler can now generate Metal byte code +for macOS or iOS when the build is running on macOS. This is enabled +automatically with the fips-integration files in [sokol-tools-bin](https://github.com/floooh/sokol-tools-bin), +see the [sokol-shdc docs](https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md) for more +details. + +- **16-May-2019** two new utility headers: *sokol_cimgui.h* and *sokol_gfx_cimgui.h*, +those are the same as their counterparts sokol_imgui.h and sokol_gfx_imgui.h, but +use the [cimgui](https://github.com/cimgui/cimgui) C-API for Dear ImGui. This +is useful if you don't want to - or cannot - use C++ for creating Dear ImGui UIs. + + Many thanks to @prime31 for contributing those! + + sokol_cimgui.h [is used +here](https://floooh.github.io/sokol-html5/wasm/cimgui-sapp.html), and +sokol_gfx_cimgui.h is used for the [debugging UI +here](https://floooh.github.io/sokol-html5/wasm/sgl-microui-sapp-ui.html) + +- **15-May-2019** there's now an optional shader-cross-compiler solution for +sokol_gfx.h: [see here for details](https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md). +This is "V1.0" with two notable features missing: + + - an include-file feature for GLSL shaders + - compilation to Metal- and D3D-bytecode (currently + only source-code generation is supported) + + The [sokol-app samples](https://floooh.github.io/sokol-html5/) have been + ported to the new shader-cross-compilation, follow the ```src``` and + ```glsl``` links on the specific sample webpages to see the C- and GLSL- + source-code. + +- **02-May-2019** sokol_gfx.h has a new function ```sg_query_backend()```, this +will return an enum ```sg_backend``` identifying the backend sokol-gfx is +currently running on, which is one of the following values: + + - SG_BACKEND_GLCORE33 + - SG_BACKEND_GLES2 + - SG_BACKEND_GLES3 + - SG_BACKEND_D3D11 + - SG_BACKEND_METAL_MACOS + - SG_BACKEND_METAL_IOS + + When compiled with SOKOL_GLES3, sg_query_backend() may return SG_BACKEND_GLES2 + when the runtime platform doesn't support GLES3/WebGL2 and had to fallback + to GLES2/WebGL2. + + When compiled with SOKOL_METAL, sg_query_backend() will return SG_BACKEND_METAL_MACOS + when the compile target is macOS, and SG_BACKEND_METAL_IOS when the target is iOS. + +- **26-Apr-2019** Small but breaking change in **sokol_gfx.h** how the vertex +layout definition in sg_pipeline_desc works: + + Vertex component names and semantics (needed by the GLES2 and D3D11 backends) have moved from ```sg_pipeline_desc``` into ```sg_shader_desc```. + + This may seem like a rather pointless small detail to change, especially + for breaking existing code, but the whole thing will make a bit more + sense when the new shader-cross-compiler will be integrated which I'm + currently working on (here: https://github.com/floooh/sokol-tools). + + While working on getting reflection data out of the shaders (e.g. what + uniform blocks and textures the shader uses), it occurred to me that + vertex-attribute-names and -semantics are actually part of the reflection + info and belong to the shader, not to the vertex layout in the pipeline + object (which only describes how the incoming vertex data maps to + vertex-component **slots**. Instead of (optionally) mapping this + association through a name, the pipeline's vertex layout is now always + strictly defined in terms of numeric 'bind slots' for **all** sokol_gfx.h + backends. For 3D APIs where the vertex component slot isn't explicitly + defined in the shader language (GLES2/WebGL, D3D11, and optionally + GLES3/GL), the shader merely offers a lookup table how vertex-layout + slot-indices map to names/semantics (and the underlying 3D API than maps + those names back to slot indices, which shows that Metal and GL made the + right choice defining the slots right in the shader). + + Here's how the code changes (taken from the triangle-sapp.c sample): + + **OLD**: + ```c + /* create a shader */ + sg_shader shd = sg_make_shader(&(sg_shader_desc){ + .vs.source = vs_src, + .fs.source = fs_src, + }); + + /* create a pipeline object (default render states are fine for triangle) */ + pip = sg_make_pipeline(&(sg_pipeline_desc){ + /* if the vertex layout doesn't have gaps, don't need to provide strides and offsets */ + .shader = shd, + .layout = { + .attrs = { + [0] = { .name="position", .sem_name="POS", .format=SG_VERTEXFORMAT_FLOAT3 }, + [1] = { .name="color0", .sem_name="COLOR", .format=SG_VERTEXFORMAT_FLOAT4 } + } + }, + }); + ``` + + **NEW**: + ```c + /* create a shader */ + sg_shader shd = sg_make_shader(&(sg_shader_desc){ + .attrs = { + [0] = { .name="position", .sem_name="POS" }, + [1] = { .name="color0", .sem_name="COLOR" } + }, + .vs.source = vs_src, + .fs.source = fs_src, + }); + + /* create a pipeline object (default render states are fine for triangle) */ + pip = sg_make_pipeline(&(sg_pipeline_desc){ + /* if the vertex layout doesn't have gaps, don't need to provide strides and offsets */ + .shader = shd, + .layout = { + .attrs = { + [0].format=SG_VERTEXFORMAT_FLOAT3, + [1].format=SG_VERTEXFORMAT_FLOAT4 + } + }, + }); + ``` + + ```sg_shader_desc``` has a new embedded struct ```attrs``` which + contains a vertex attribute _name_ (for GLES2/WebGL) and + _sem_name/sem_index_ (for D3D11). For the Metal backend this struct is + ignored completely, and for GLES3/GL it is optional, and not required + when the vertex shader inputs are annotated with ```layout(location=N)```. + + The remaining attribute description members in ```sg_pipeline_desc``` are: + - **.format**: the format of input vertex data (this can be different + from the vertex shader's inputs when data is extended during + vertex fetch (e.g. input can be vec3 while the vertex shader + expects vec4) + - **.offset**: optional offset of the vertex component data (not needed + when the input vertex has no gaps between the components) + - **.buffer**: the vertex buffer bind slot if the vertex data is coming + from different buffers + + Also check out the various samples: + + - for GLSL (explicit slots via ```layout(location=N)```): https://github.com/floooh/sokol-samples/tree/master/glfw + - for D3D11 (semantic names/indices): https://github.com/floooh/sokol-samples/tree/master/d3d11 + - for GLES2: (vertex attribute names): https://github.com/floooh/sokol-samples/tree/master/html5 + - for Metal: (explicit slots): https://github.com/floooh/sokol-samples/tree/master/metal + - ...and all of the above combined: https://github.com/floooh/sokol-samples/tree/master/sapp + +- **19-Apr-2019** I have replaced the rather inflexible render-state handling +in **sokol_gl.h** with a *pipeline stack* (like the GL matrix stack, but with +pipeline-state-objects), along with a couple of other minor API tweaks. + + These are the new pipeline-stack functions: + ```c + sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc* desc); + void sgl_destroy_pipeline(sgl_pipeline pip); + void sgl_default_pipeline(void); + void sgl_load_pipeline(sgl_pipeline pip); + void sgl_push_pipeline(void); + void sgl_pop_pipeline(void); + ``` + + A pipeline object is created just like in sokol_gfx.h, but without shaders, + vertex layout, pixel formats, primitive-type and sample count (these details + are filled in by the ```sgl_make_pipeline()``` wrapper function. For instance + to create a pipeline object for additive transparency: + + ```c + sgl_pipeline additive_pip = sgl_make_pipeline(&(sg_pipeline_desc){ + .blend = { + .enabled = true, + .src_factor_rgb = SG_BLENDFACTOR_ONE, + .dst_factor_rgb = SG_BLENDFACTOR_ONE + } + }); + ``` + + And to render with this, simply call ```sgl_load_pipeline()```: + + ```c + sgl_load_pipeline(additive_pip); + sgl_begin_triangles(); + ... + sgl_end(); + ``` + + Or to preserve and restore the previously active pipeline state: + + ```c + sgl_push_pipeline(); + sgl_load_pipeline(additive_pip); + sgl_begin_quads(); + ... + sgl_end(); + sgl_pop_pipeline(); + ``` + + You can also load the 'default pipeline' explicitly on the top of the + pipeline stack with ```sgl_default_pipeline()```. + + The other API change is: + + - ```sgl_state_texture(bool b)``` has been replaced with ```sgl_enable_texture()``` + and ```sgl_disable_texture()``` + + The code samples have been updated accordingly: + + - [sgl-sapp.c](https://github.com/floooh/sokol-samples/blob/master/sapp/sgl-sapp.c) + - [sgl-lines-sapp.c](https://github.com/floooh/sokol-samples/blob/master/sapp/sgl-lines-sapp.c) + - [sgl-microui-sapp.c](https://github.com/floooh/sokol-samples/blob/master/sapp/sgl-microui-sapp.c) + +- **01-Apr-2019** (not an April Fool's joke): There's a new **sokol_gl.h** +util header which implements an 'OpenGL-1.x-in-spirit' rendering API on top +of sokol_gfx.h (vertex specification via begin/end, and a matrix stack). This is +only a small subset of OpenGL 1.x, mainly intended for debug-visualization or +simple tasks like 2D UI rendering. As always, sample code is in the +[sokol-samples](https://github.com/floooh/sokol-samples) project. + +- **15-Mar-2019**: various Dear ImGui related changes: + - there's a new utility header sokol_imgui.h with a simple drop-in + renderer for Dear ImGui on top of sokol_gfx.h and sokol_app.h + (sokol_app.h is optional, and only used for input handling) + - the sokol_gfx_imgui.h debug inspection header no longer + depends on internal data structures and functions of sokol_gfx.h, as such + it is now a normal *utility header* and has been moved to the *utils* + directory + - the implementation macro for sokol_gfx_imgui.h has been changed + from SOKOL_IMPL to SOKOL_GFX_IMGUI_IMPL (so when you suddenly get + unresolved linker errors, that's the reason) + - all headers now have two preprocessor defines for the declaration + and implementation (for instance in sokol_gfx.h: SOKOL_GFX_INCLUDED + and SOKOL_GFX_IMPL_INCLUDED) these are checked in the utility-headers + to provide useful error message when dependent headers are missing + +- **05-Mar-2019**: sokol_gfx.h now has a 'trace hook' API, and I have started +implementing optional debug-inspection-UI headers on top of Dear ImGui: + - sokol_gfx.h has a new function *sg_install_trace_hooks()*, this allows + you to install a callback function for each public sokol_gfx.h function + (and a couple of error callbacks). For more details, search for "TRACE HOOKS" + in sokol_gfx.h + - when creating sokol_gfx.h resources, you can now set a 'debug label' + in the desc structure, this is ignored by sokol_gfx.h itself, but is + useful for debuggers or profilers hooking in via the new trace hooks + - likewise, two new functions *sg_push_debug_group()* and *sg_pop_debug_group()* + can be used to group related drawing functions under a name, this + is also ignored by sokol_gfx.h itself and only useful when hooking + into the API calls + - I have started a new 'subproject' in the 'imgui' directory, this will + contain a slowly growing set of optional debug-inspection-UI headers + which allow to peek under the hood of the Sokol headers. The UIs are + implemented with [Dear ImGui](https://github.com/ocornut/imgui). Again, + see the README in the 'imgui' directory and the headers in there + for details, and check out the live demos on the [Sokol Sample Webpage](https://floooh.github.io/sokol-html5/) + (click on the little UI buttons in the top right corner of each thumbnail) + +- **21-Feb-2019**: sokol_app.h and sokol_audio.h now have an alternative +set of callbacks with user_data arguments. This is useful if you don't +want or cannot store your own application state in global variables. +See the header documentation in sokol_app.h and sokol_audio.h for details, +and check out the samples *sapp/noentry-sapp.c* and *sapp/modplay-sapp.c* +in https://github.com/floooh/sokol-samples + +- **19-Feb-2019**: sokol_app.h now has an alternative mode where it doesn't +"hijack" the platform's main() function. Search for SOKOL_NO_ENTRY in +sokol_app.h for details and documentation. + +- **26-Jan-2019**: sokol_app.h now has an Android backend contributed by + [Gustav Olsson](https://github.com/gustavolsson)! + See the [sokol-samples readme](https://github.com/floooh/sokol-samples/blob/master/README.md) + for build instructions. + +- **21-Jan-2019**: sokol_gfx.h - pool-slot-generation-counters and a dummy backend: + - Resource pool slots now have a generation-counter for the resource-id + unique-tag, instead of a single counter for the whole pool. This allows + to create many more unique handles. + - sokol_gfx.h now has a dummy backend, activated by defining SOKOL_DUMMY_BACKEND + (instead of SOKOL_METAL, SOKOL_D3D11, ...), this allows to write + 'headless' tests (and there's now a sokol-gfx-test in the sokol-samples + repository which mainly tests the resource pool system) + +- **12-Jan-2019**: sokol_gfx.h - setting the pipeline state and resource +bindings now happens in separate calls, specifically: + - *sg_apply_draw_state()* has been replaced with *sg_apply_pipeline()* and + *sg_apply_bindings()* + - the *sg_draw_state* struct has been replaced with *sg_bindings* + - *sg_apply_uniform_block()* has been renamed to *sg_apply_uniforms()* + +All existing code will still work. See [this blog +post](https://floooh.github.io/2019/01/12/sokol-apply-pipeline.html) for +details. + +- **29-Oct-2018**: + - sokol_gfx.h has a new function **sg_append_buffer()** which allows to + append new data to a buffer multiple times per frame and interleave this + with draw calls. This basically implements the + D3D11_MAP_WRITE_NO_OVERWRITE update strategy for buffer objects. For + example usage, see the updated Dear ImGui samples in the [sokol_samples + repo](https://github.com/floooh/sokol-samples) + - the GL state cache in sokol_gfx.h handles buffers bindings in a + more robust way, previously it might have happened that the + buffer binding gets confused when creating buffers or updating + buffer contents in the render loop + +- **17-Oct-2018**: sokol_args.h added + +- **26-Sep-2018**: sokol_audio.h ready for prime time :) + +- **11-May-2018**: sokol_gfx.h now autodetects iOS vs MacOS in the Metal +backend during compilation using the standard define TARGET_OS_IPHONE defined +in the TargetConditionals.h system header, please replace the old +backend-selection defines SOKOL_METAL_MACOS and SOKOL_METAL_IOS with +**SOKOL_METAL** + +- **20-Apr-2018**: 3 new context-switching functions have been added +to make it possible to use sokol together with applications that +use multiple GL contexts. On D3D11 and Metal, the functions are currently +empty. See the new section ```WORKING WITH CONTEXTS``` in the sokol_gfx.h +header documentation, and the new sample [multiwindow-glfw](https://github.com/floooh/sokol-samples/blob/master/glfw/multiwindow-glfw.c) + +- **31-Jan-2018**: The vertex layout declaration in sg\_pipeline\_desc had +some fairly subtle flaws and has been changed to work like Metal or Vulkan. +The gist is that the vertex-buffer-layout properties (vertex stride, +vertex-step-rate and -step-function for instancing) is now defined in a +separate array from the vertex attributes. This removes some brittle backend +code which tries to guess the right vertex attribute slot if no attribute +names are given, and which was wrong for shader-code-generation pipelines +which reorder the vertex attributes (I stumbled over this when porting the +Oryol Gfx module over to sokol-gfx). Some code samples: + +```c +// a complete vertex layout declaration with a single input buffer +// with two vertex attributes +sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){ + .layout = { + .buffers = { + [0] = { + .stride = 20, + .step_func = SG_VERTEXSTEP_PER_VERTEX, + .step_rate = 1 + } + }, + .attrs = { + [0] = { + .name = "pos", + .offset = 0, + .format = SG_VERTEXFORMAT_FLOAT3, + .buffer_index = 0 + }, + [1] = { + .name = "uv", + .offset = 12, + .format = SG_VERTEXFORMAT_FLOAT2, + .buffer_index = 0 + } + } + }, + ... +}); + +// if the vertex layout has no gaps, we can get rid of the strides and offsets: +sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){ + .layout = { + .buffers = { + [0] = { + .step_func = SG_VERTEXSTEP_PER_VERTEX, + .step_rate=1 + } + }, + .attrs = { + [0] = { + .name = "pos", + .format = SG_VERTEXFORMAT_FLOAT3, + .buffer_index = 0 + }, + [1] = { + .name = "uv", + .format = SG_VERTEXFORMAT_FLOAT2, + .buffer_index = 0 + } + } + }, + ... +}); + +// we can also get rid of the other default-values, which leaves buffers[0] +// as all-defaults, so it can disappear completely: +sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){ + .layout = { + .attrs = { + [0] = { .name = "pos", .format = SG_VERTEXFORMAT_FLOAT3 }, + [1] = { .name = "uv", .format = SG_VERTEXFORMAT_FLOAT2 } + } + }, + ... +}); + +// and finally on GL3.3 and Metal and we don't need the attribute names +// (on D3D11, a semantic name and index must be provided though) +sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){ + .layout = { + .attrs = { + [0] = { .format = SG_VERTEXFORMAT_FLOAT3 }, + [1] = { .format = SG_VERTEXFORMAT_FLOAT2 } + } + }, + ... +}); +``` + +Please check the sample code in https://github.com/floooh/sokol-samples for +more examples! + +Enjoy! diff --git a/thirdparty/sokol/README.md b/thirdparty/sokol/README.md new file mode 100644 index 0000000..dcceedc --- /dev/null +++ b/thirdparty/sokol/README.md @@ -0,0 +1,419 @@ +

+

Simple + STB-style + cross-platform libraries for C and C++, written in C.

+

+ +# Sokol + +[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**19-Jun-2024**: bugfix in the sokol_gfx.h d3d11 backend when updating 3d textures + +[![Build](/../../actions/workflows/main.yml/badge.svg)](/../../actions/workflows/main.yml) [![Bindings](/../../actions/workflows/gen_bindings.yml/badge.svg)](/../../actions/workflows/gen_bindings.yml) [![build](https://github.com/floooh/sokol-zig/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-zig/actions/workflows/main.yml) [![build](https://github.com/floooh/sokol-nim/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-nim/actions/workflows/main.yml) [![Odin](https://github.com/floooh/sokol-odin/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-odin/actions/workflows/main.yml)[![Rust](https://github.com/floooh/sokol-rust/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-rust/actions/workflows/main.yml)[![Dlang](https://github.com/kassane/sokol-d/actions/workflows/build.yml/badge.svg)](https://github.com/kassane/sokol-d/actions/workflows/build.yml) + +## Examples and Related Projects + +- [Live Samples](https://floooh.github.io/sokol-html5/index.html) via WASM ([source](https://github.com/floooh/sokol-samples)) + +- [Doom Shareware](https://floooh.github.io/doom-sokol/) ported to the Sokol headers ([source](https://github.com/floooh/doom-sokol)) + +- [Everybody Wants to Crank the World](https://aras-p.github.io/demo-pd-cranktheworld/) demo by +Aras Pranckevičius, PC/web port via sokol ([source](https://github.com/aras-p/demo-pd-cranktheworld)). + +- [sokol_gp.h](https://github.com/edubart/sokol_gp) a 2D shape drawing library on top of sokol_gfx.h + +- [LearnOpenGL examples ported to sokol-gfx](https://zeromake.github.io/learnopengl-examples/) ([git repo](https://github.com/zeromake/learnopengl-examples)) + +- [Dear ImGui starterkit](https://github.com/floooh/cimgui-sokol-starterkit) a self-contained starterkit for writing Dear ImGui apps in C. + +- [qoiview](https://github.com/floooh/qoiview) a basic viewer for the new QOI image file format + +- [Tiny 8-bit emulators](https://floooh.github.io/tiny8bit/) + +- A 'single-file' [Pacman clone in C99](https://github.com/floooh/pacman.c/), also available in [Zig](https://github.com/floooh/pacman.zig/) + +- [Solar Storm](https://store.steampowered.com/app/2754920/Solar_Storm/), a turn-based scifi artillery game built with Odin and Sokol, released on Steam. + +- [Spanking Runners (Samogonki)](https://store.steampowered.com/app/2599800/Spanking_Runners/), arcade racing in a bright and unusual world, released on Steam. + +- [MEG-4](https://bztsrc.gitlab.io/meg4) a virtual fantasy console emulator in C89, ported to sokol + +- A [Minigolf game](https://mgerdes.github.io/minigolf.html) ([source](https://github.com/mgerdes/minigolf)). + +- [hIghQube](https://github.com/RuiVarela/hIghQube) A game demo that used sokol rendering extensively + +- [Senos](https://github.com/RuiVarela/Senos) A music app that uses sokol as backend + +- ['Dealer's Dungeon'](https://dealers-dungeon.com/demo/) ([lower graphics quality](https://dealers-dungeon.com/demo/?q=3), +[source](https://github.com/bqqbarbhg/spear)) + +- [Command line tools](https://github.com/floooh/sokol-tools) (shader compiler) + +- [How to build without a build system](https://github.com/floooh/sokol-samples#how-to-build-without-a-build-system): +useful details for integrating the Sokol headers into your own project with your favourite C/C++ build system + +## Core libraries + +- [**sokol\_gfx.h**](https://github.com/floooh/sokol/blob/master/sokol_gfx.h): 3D-API wrapper (GL/GLES3/WebGL2 + Metal + D3D11 + WebGPU) +- [**sokol\_app.h**](https://github.com/floooh/sokol/blob/master/sokol_app.h): app framework wrapper (entry + window + 3D-context + input) +- [**sokol\_time.h**](https://github.com/floooh/sokol/blob/master/sokol_time.h): time measurement +- [**sokol\_audio.h**](https://github.com/floooh/sokol/blob/master/sokol_audio.h): minimal buffer-streaming audio playback +- [**sokol\_fetch.h**](https://github.com/floooh/sokol/blob/master/sokol_fetch.h): asynchronous data streaming from HTTP and local filesystem +- [**sokol\_args.h**](https://github.com/floooh/sokol/blob/master/sokol_args.h): unified cmdline/URL arg parser for web and native apps +- [**sokol\_log.h**](https://github.com/floooh/sokol/blob/master/sokol_log.h): provides a standard logging callback for the other sokol headers + +## Utility libraries + +- [**sokol\_imgui.h**](https://github.com/floooh/sokol/blob/master/util/sokol_imgui.h): sokol_gfx.h rendering backend for [Dear ImGui](https://github.com/ocornut/imgui) +- [**sokol\_nuklear.h**](https://github.com/floooh/sokol/blob/master/util/sokol_nuklear.h): sokol_gfx.h rendering backend for [Nuklear](https://github.com/Immediate-Mode-UI/Nuklear) +- [**sokol\_gl.h**](https://github.com/floooh/sokol/blob/master/util/sokol_gl.h): OpenGL 1.x style immediate-mode rendering API on top of sokol_gfx.h +- [**sokol\_fontstash.h**](https://github.com/floooh/sokol/blob/master/util/sokol_fontstash.h): sokol_gl.h rendering backend for [fontstash](https://github.com/memononen/fontstash) +- [**sokol\_gfx\_imgui.h**](https://github.com/floooh/sokol/blob/master/util/sokol_gfx_imgui.h): debug-inspection UI for sokol_gfx.h (implemented with Dear ImGui) +- [**sokol\_debugtext.h**](https://github.com/floooh/sokol/blob/master/util/sokol_debugtext.h): a simple text renderer using vintage home computer fonts +- [**sokol\_memtrack.h**](https://github.com/floooh/sokol/blob/master/util/sokol_memtrack.h): easily track memory allocations in sokol headers +- [**sokol\_shape.h**](https://github.com/floooh/sokol/blob/master/util/sokol_shape.h): generate simple shapes and plug them into sokol-gfx resource creation structs +- [**sokol\_color.h**](https://github.com/floooh/sokol/blob/master/util/sokol_color.h): X11 style color constants and functions for creating sg_color objects +- [**sokol\_spine.h**](https://github.com/floooh/sokol/blob/master/util/sokol_spine.h): a sokol-style wrapper around the Spine C runtime (http://en.esotericsoftware.com/spine-in-depth) + +## 'Official' Language Bindings + +These are automatically updated on changes to the C headers: + +- [sokol-zig](https://github.com/floooh/sokol-zig) +- [sokol-odin](https://github.com/floooh/sokol-odin) +- [sokol-nim](https://github.com/floooh/sokol-nim) +- [sokol-rust](https://github.com/floooh/sokol-rust) +- [sokol-d](https://github.com/kassane/sokol-d) + +## Notes + +WebAssembly is a 'first-class citizen', one important motivation for the +Sokol headers is to provide a collection of cross-platform APIs with a +minimal footprint on the web platform while still being useful. + +The core headers are standalone and can be used independently from each other. + +### Why C: + +- easier integration with other languages +- easier integration into other projects +- adds only minimal size overhead to executables + +A blog post with more background info: [A Tour of sokol_gfx.h](http://floooh.github.io/2017/07/29/sokol-gfx-tour.html) + +# sokol_gfx.h: + +- simple, modern wrapper around GLES3/WebGL2, GL3.3, D3D11, Metal, and WebGPU +- buffers, images, shaders, pipeline-state-objects and render-passes +- does *not* handle window creation or 3D API context initialization +- does *not* provide shader dialect cross-translation (**BUT** there's now an 'official' shader-cross-compiler solution which +seamlessly integrates with sokol_gfx.h and IDEs: [see here for details](https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md) + +# sokol_app.h + +A minimal cross-platform application-wrapper library: + +- unified application entry +- single window or canvas for 3D rendering +- 3D context initialization +- event-based keyboard, mouse and touch input +- supported platforms: Win32, MacOS, Linux (X11), iOS, WASM, Android, UWP +- supported 3D-APIs: GL3.3 (GLX/WGL), Metal, D3D11, GLES3/WebGL2 + +The vanilla Hello-Triangle using sokol_gfx.h, sokol_app.h and the +sokol-shdc shader compiler (shader code not shown): + +```c +#include "sokol_app.h" +#include "sokol_gfx.h" +#include "sokol_log.h" +#include "sokol_glue.h" +#include "triangle-sapp.glsl.h" + +static struct { + sg_pipeline pip; + sg_bindings bind; + sg_pass_action pass_action; +} state; + +static void init(void) { + sg_setup(&(sg_desc){ + .environment = sglue_environment(), + .logger.func = slog_func, + }); + + float vertices[] = { + 0.0f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f + }; + state.bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){ + .data = SG_RANGE(vertices), + }); + + state.pip = sg_make_pipeline(&(sg_pipeline_desc){ + .shader = sg_make_shader(triangle_shader_desc(sg_query_backend())), + .layout = { + .attrs = { + [ATTR_vs_position].format = SG_VERTEXFORMAT_FLOAT3, + [ATTR_vs_color0].format = SG_VERTEXFORMAT_FLOAT4 + } + }, + }); + + state.pass_action = (sg_pass_action) { + .colors[0] = { .load_action=SG_LOADACTION_CLEAR, .clear_value={0.0f, 0.0f, 0.0f, 1.0f } } + }; +} + +void frame(void) { + sg_begin_pass(&(sg_pass){ .action = state.pass_action, .swapchain = sglue_swapchain() }); + sg_apply_pipeline(state.pip); + sg_apply_bindings(&state.bind); + sg_draw(0, 3, 1); + sg_end_pass(); + sg_commit(); +} + +void cleanup(void) { + sg_shutdown(); +} + +sapp_desc sokol_main(int argc, char* argv[]) { + (void)argc; (void)argv; + return (sapp_desc){ + .init_cb = init, + .frame_cb = frame, + .cleanup_cb = cleanup, + .width = 640, + .height = 480, + .window_title = "Triangle", + .icon.sokol_default = true, + .logger.func = slog_func, + }; +} +``` + +# sokol_audio.h + +A minimal audio-streaming API: + +- you provide a mono- or stereo-stream of 32-bit float samples which sokol_audio.h forwards into platform-specific backends +- two ways to provide the data: + 1. directly fill backend audio buffer from your callback function running in the audio thread + 2. alternatively push small packets of audio data from your main loop, + or a separate thread created by you +- platform backends: + - Windows: WASAPI + - macOS/iOS: CoreAudio + - Linux: ALSA + - emscripten: WebAudio + ScriptProcessorNode (doesn't use the emscripten-provided OpenAL or SDL Audio wrappers) + +A simple mono square-wave generator using the callback model: + +```c +// the sample callback, running in audio thread +static void stream_cb(float* buffer, int num_frames, int num_channels) { + assert(1 == num_channels); + static uint32_t count = 0; + for (int i = 0; i < num_frames; i++) { + buffer[i] = (count++ & (1<<3)) ? 0.5f : -0.5f; + } +} + +int main() { + // init sokol-audio with default params + saudio_setup(&(saudio_desc){ + .stream_cb = stream_cb, + .logger.func = slog_func, + }); + + // run main loop + ... + + // shutdown sokol-audio + saudio_shutdown(); + return 0; +``` + +The same code using the push-model + +```c +#define BUF_SIZE (32) +int main() { + // init sokol-audio with default params, no callback + saudio_setup(&(saudio_desc){ + .logger.func = slog_func, + }); + assert(saudio_channels() == 1); + + // a small intermediate buffer so we don't need to push + // individual samples, which would be quite inefficient + float buf[BUF_SIZE]; + int buf_pos = 0; + uint32_t count = 0; + + // push samples from main loop + bool done = false; + while (!done) { + // generate and push audio samples... + int num_frames = saudio_expect(); + for (int i = 0; i < num_frames; i++) { + // simple square wave generator + buf[buf_pos++] = (count++ & (1<<3)) ? 0.5f : -0.5f; + if (buf_pos == BUF_SIZE) { + buf_pos = 0; + saudio_push(buf, BUF_SIZE); + } + } + // handle other per-frame stuff... + ... + } + + // shutdown sokol-audio + saudio_shutdown(); + return 0; +} +``` + +# sokol_fetch.h + +Load entire files, or stream data asynchronously over HTTP (emscripten/wasm) +or the local filesystem (all native platforms). + +Simple C99 example loading a file into a static buffer: + +```c +#include "sokol_fetch.h" +#include "sokol_log.h" + +static void response_callback(const sfetch_response*); + +#define MAX_FILE_SIZE (1024*1024) +static uint8_t buffer[MAX_FILE_SIZE]; + +// application init +static void init(void) { + ... + // setup sokol-fetch with default config: + sfetch_setup(&(sfetch_desc_t){ .logger.func = slog_func }); + + // start loading a file into a statically allocated buffer: + sfetch_send(&(sfetch_request_t){ + .path = "hello_world.txt", + .callback = response_callback + .buffer_ptr = buffer, + .buffer_size = sizeof(buffer) + }); +} + +// per frame... +static void frame(void) { + ... + // need to call sfetch_dowork() once per frame to 'turn the gears': + sfetch_dowork(); + ... +} + +// the response callback is where the interesting stuff happens: +static void response_callback(const sfetch_response_t* response) { + if (response->fetched) { + // data has been loaded into the provided buffer, do something + // with the data... + const void* data = response->buffer_ptr; + uint64_t data_size = response->fetched_size; + } + // the finished flag is set both on success and failure + if (response->failed) { + // oops, something went wrong + switch (response->error_code) { + SFETCH_ERROR_FILE_NOT_FOUND: ... + SFETCH_ERROR_BUFFER_TOO_SMALL: ... + ... + } + } +} + +// application shutdown +static void shutdown(void) { + ... + sfetch_shutdown(); + ... +} +``` + +# sokol_time.h: + +Simple cross-platform time measurement: + +```c +#include "sokol_time.h" +... +/* initialize sokol_time */ +stm_setup(); + +/* take start timestamp */ +uint64_t start = stm_now(); + +...some code to measure... + +/* compute elapsed time */ +uint64_t elapsed = stm_since(start); + +/* convert to time units */ +double seconds = stm_sec(elapsed); +double milliseconds = stm_ms(elapsed); +double microseconds = stm_us(elapsed); +double nanoseconds = stm_ns(elapsed); + +/* difference between 2 time stamps */ +uint64_t start = stm_now(); +... +uint64_t end = stm_now(); +uint64_t elapsed = stm_diff(end, start); + +/* compute a 'lap time' (e.g. for fps) */ +uint64_t last_time = 0; +while (!done) { + ...render something... + double frame_time_ms = stm_ms(stm_laptime(&last_time)); +} +``` + +# sokol_args.h + +Unified argument parsing for web and native apps. Uses argc/argv on native +platforms and the URL query string on the web. + +Example URL with one arg: + +https://floooh.github.io/tiny8bit/kc85.html?type=kc85_4 + +The same as command line app: + +> kc85 type=kc85_4 + +Parsed like this: + +```c +#include "sokol_args.h" + +int main(int argc, char* argv[]) { + sargs_setup(&(sargs_desc){ .argc=argc, .argv=argv }); + if (sargs_exists("type")) { + if (sargs_equals("type", "kc85_4")) { + // start as KC85/4 + } + else if (sargs_equals("type", "kc85_3")) { + // start as KC85/3 + } + else { + // start as KC85/2 + } + } + sargs_shutdown(); + return 0; +} +``` + +See the sokol_args.h header for a more complete documentation, and the [Tiny +Emulators](https://floooh.github.io/tiny8bit/) for more interesting usage examples. diff --git a/thirdparty/sokol/assets/logo_full_large.png b/thirdparty/sokol/assets/logo_full_large.png new file mode 100644 index 0000000..cd3c48a Binary files /dev/null and b/thirdparty/sokol/assets/logo_full_large.png differ diff --git a/thirdparty/sokol/assets/logo_full_small.png b/thirdparty/sokol/assets/logo_full_small.png new file mode 100644 index 0000000..865b960 Binary files /dev/null and b/thirdparty/sokol/assets/logo_full_small.png differ diff --git a/thirdparty/sokol/assets/logo_s_large.png b/thirdparty/sokol/assets/logo_s_large.png new file mode 100644 index 0000000..091229b Binary files /dev/null and b/thirdparty/sokol/assets/logo_s_large.png differ diff --git a/thirdparty/sokol/assets/logo_s_small.png b/thirdparty/sokol/assets/logo_s_small.png new file mode 100644 index 0000000..c5d098c Binary files /dev/null and b/thirdparty/sokol/assets/logo_s_small.png differ diff --git a/thirdparty/sokol/bindgen/.gitignore b/thirdparty/sokol/bindgen/.gitignore new file mode 100644 index 0000000..8dccea1 --- /dev/null +++ b/thirdparty/sokol/bindgen/.gitignore @@ -0,0 +1,9 @@ +*.json +*.nim +*.zig +__pycache__/ +sokol-nim/ +sokol-zig/ +sokol-odin/ +sokol-rust/ +sokol-d/ diff --git a/thirdparty/sokol/bindgen/README.md b/thirdparty/sokol/bindgen/README.md new file mode 100644 index 0000000..aef1ec1 --- /dev/null +++ b/thirdparty/sokol/bindgen/README.md @@ -0,0 +1,40 @@ +## Language Binding Generation Scripts + +> REMINDER: we can pass `-fparse-all-comments` to the clang ast-dump command line which adds the following node types to the ast-dump.json: FullComment, ParagraphComment, TextComment. This might allow us to preserve comments in the language bindings (might be useful as part of a bigger change to make sokol header comments autodoc and Intellisense-friendly) + +### Zig + +First make sure that clang and python3 are in the path: + +``` +> clang --version +> python3 --version +``` + +...on Windows I simply install those with scoop: + +``` +> scoop install llvm +> scoop install python +``` + +To update the Zig bindings: + +``` +> cd sokol/bindgen +> git clone https://github.com/floooh/sokol-zig +> git clone https://github.com/floooh/sokol-nim +> git clone https://github.com/floooh/sokol-odin +> git clone https://github.com/floooh/sokol-rust +> python3 gen_all.py +``` + +Test and run samples: + +``` +> cd sokol/bindgen/sokol-zig +> zig build run-clear +> zig build run-triangle +> zig build run-cube +... +``` diff --git a/thirdparty/sokol/bindgen/gen_all.py b/thirdparty/sokol/bindgen/gen_all.py new file mode 100644 index 0000000..3b29383 --- /dev/null +++ b/thirdparty/sokol/bindgen/gen_all.py @@ -0,0 +1,48 @@ +import os, gen_nim, gen_zig, gen_odin, gen_rust, gen_d + +tasks = [ + [ '../sokol_log.h', 'slog_', [] ], + [ '../sokol_gfx.h', 'sg_', [] ], + [ '../sokol_app.h', 'sapp_', [] ], + [ '../sokol_glue.h', 'sglue_', ['sg_'] ], + [ '../sokol_time.h', 'stm_', [] ], + [ '../sokol_audio.h', 'saudio_', [] ], + [ '../util/sokol_gl.h', 'sgl_', ['sg_'] ], + [ '../util/sokol_debugtext.h', 'sdtx_', ['sg_'] ], + [ '../util/sokol_shape.h', 'sshape_', ['sg_'] ], +] + +# Odin +gen_odin.prepare() +for task in tasks: + [c_header_path, main_prefix, dep_prefixes] = task + gen_odin.gen(c_header_path, main_prefix, dep_prefixes) + +# Nim +gen_nim.prepare() +for task in tasks: + [c_header_path, main_prefix, dep_prefixes] = task + gen_nim.gen(c_header_path, main_prefix, dep_prefixes) + +# Zig +zig_tasks = [ + *tasks, + [ '../sokol_fetch.h', 'sfetch_', [] ], + [ '../util/sokol_imgui.h', 'simgui_', ['sg_', 'sapp_'] ], +] +gen_zig.prepare() +for task in zig_tasks: + [c_header_path, main_prefix, dep_prefixes] = task + gen_zig.gen(c_header_path, main_prefix, dep_prefixes) + +# D +gen_d.prepare() +for task in tasks: + [c_header_path, main_prefix, dep_prefixes] = task + gen_d.gen(c_header_path, main_prefix, dep_prefixes) + +# Rust +gen_rust.prepare() +for task in tasks: + [c_header_path, main_prefix, dep_prefixes] = task + gen_rust.gen(c_header_path, main_prefix, dep_prefixes) diff --git a/thirdparty/sokol/bindgen/gen_d.py b/thirdparty/sokol/bindgen/gen_d.py new file mode 100644 index 0000000..31caf09 --- /dev/null +++ b/thirdparty/sokol/bindgen/gen_d.py @@ -0,0 +1,514 @@ +#------------------------------------------------------------------------------- +# Generate D bindings. +# +# D coding style: +# - types are PascalCase +# - functions are camelCase +# - otherwise snake_case +#------------------------------------------------------------------------------- +import gen_ir +import os +import shutil +import sys + +import gen_util as util + +module_names = { + 'slog_': 'log', + 'sg_': 'gfx', + 'sapp_': 'app', + 'stm_': 'time', + 'saudio_': 'audio', + 'sgl_': 'gl', + 'sdtx_': 'debugtext', + 'sshape_': 'shape', + 'sglue_': 'glue', + 'sfetch_': 'fetch', + 'simgui_': 'imgui', +} + +c_source_paths = { + 'slog_': 'sokol-d/src/sokol/c/sokol_log.c', + 'sg_': 'sokol-d/src/sokol/c/sokol_gfx.c', + 'sapp_': 'sokol-d/src/sokol/c/sokol_app.c', + 'stm_': 'sokol-d/src/sokol/c/sokol_time.c', + 'saudio_': 'sokol-d/src/sokol/c/sokol_audio.c', + 'sgl_': 'sokol-d/src/sokol/c/sokol_gl.c', + 'sdtx_': 'sokol-d/src/sokol/c/sokol_debugtext.c', + 'sshape_': 'sokol-d/src/sokol/c/sokol_shape.c', + 'sglue_': 'sokol-d/src/sokol/c/sokol_glue.c', + 'sfetch_': 'sokol-d/src/sokol/c/sokol_fetch.c', + 'simgui_': 'sokol-d/src/sokol/c/sokol_imgui.c', +} + +ignores = [ + 'sdtx_printf', + 'sdtx_vprintf', +] + +# functions that need to be exposed as 'raw' C callbacks without a Dlang wrapper function +c_callbacks = [ + 'slog_func' +] + +# NOTE: syntax for function results: "func_name.RESULT" +overrides = { + 'ref': '_ref', + 'sgl_error': 'sgl_get_error', # 'error' is reserved in Dlang + 'sgl_deg': 'sgl_as_degrees', + 'sgl_rad': 'sgl_as_radians', + 'sg_context_desc.color_format': 'int', + 'sg_context_desc.depth_format': 'int', + 'sg_apply_uniforms.ub_index': 'uint32_t', + 'sg_draw.base_element': 'uint32_t', + 'sg_draw.num_elements': 'uint32_t', + 'sg_draw.num_instances': 'uint32_t', + 'sshape_element_range_t.base_element': 'uint32_t', + 'sshape_element_range_t.num_elements': 'uint32_t', + 'sdtx_font.font_index': 'uint32_t', + 'SGL_NO_ERROR': 'SGL_ERROR_NO_ERROR', + 'sfetch_continue': 'continue_fetching', # 'continue' is reserved in D +} + +prim_types = { + "int": "int", + "bool": "bool", + "char": "char", + "int8_t": "byte", + "uint8_t": "ubyte", + "int16_t": "short", + "uint16_t": "ushort", + "int32_t": "int", + "uint32_t": "uint", + "int64_t": "long", + "uint64_t": "ulong", + "float": "float", + "double": "double", + "uintptr_t": "ulong", + "intptr_t": "long", + "size_t": "size_t", +} + +prim_defaults = { + 'int': '0', + 'bool': 'false', + 'int8_t': '0', + 'uint8_t': '0', + 'int16_t': '0', + 'uint16_t': '0', + 'int32_t': '0', + 'uint32_t': '0', + 'int64_t': '0', + 'uint64_t': '0', + 'float': '0.0f', + 'double': '0.0', + 'uintptr_t': '0', + 'intptr_t': '0', + 'size_t': '0' +} + +struct_types = [] +enum_types = [] +enum_items = {} +out_lines = '' + +def reset_globals(): + global struct_types + global enum_types + global enum_items + global out_lines + struct_types = [] + enum_types = [] + enum_items = {} + out_lines = '' + +def l(s): + global out_lines + out_lines += s + '\n' + +def as_d_prim_type(s): + return prim_types[s] + +# prefix_bla_blub(_t) => (dep.)BlaBlub +def as_d_struct_type(s, prefix): + parts = s.lower().split('_') + outp = '' if s.startswith(prefix) else f'{parts[0]}.' + for part in parts[1:]: + # ignore '_t' type postfix + if (part != 't'): + outp += part.capitalize() + return outp + +# prefix_bla_blub(_t) => (dep.)BlaBlub +def as_d_enum_type(s, prefix): + parts = s.lower().split('_') + outp = '' if s.startswith(prefix) else f'{parts[0]}.' + for part in parts[1:]: + if (part != 't'): + outp += part.capitalize() + return outp + +def check_override(name, default=None): + if name in overrides: + return overrides[name] + elif default is None: + return name + else: + return default + +def check_ignore(name): + return name in ignores + +# PREFIX_ENUM_BLA => Bla, _PREFIX_ENUM_BLA => Bla +def as_enum_item_name(s): + outp = s.lstrip('_') + parts = outp.split('_')[2:] + outp = '_'.join(parts) + outp = outp.capitalize() + if outp[0].isdigit(): + outp = '_' + outp.capitalize() + return outp + +def enum_default_item(enum_name): + return enum_items[enum_name][0] + +def is_prim_type(s): + return s in prim_types + +def is_struct_type(s): + return s in struct_types + +def is_enum_type(s): + return s in enum_types + +def is_const_prim_ptr(s): + for prim_type in prim_types: + if s == f"const {prim_type} *": + return True + return False + +def is_prim_ptr(s): + for prim_type in prim_types: + if s == f"{prim_type} *": + return True + return False + +def is_const_struct_ptr(s): + for struct_type in struct_types: + if s == f"const {struct_type} *": + return True + return False + +def is_struct_ptr(s): + for struct_type in struct_types: + if s == f"{struct_type} *": + return True + return False + +def type_default_value(s): + return prim_defaults[s] + +def as_c_arg_type(arg_type, prefix): + if arg_type == "void": + return "void" + elif is_prim_type(arg_type): + return as_d_prim_type(arg_type) + elif is_struct_type(arg_type): + return as_d_struct_type(arg_type, prefix) + elif is_enum_type(arg_type): + return as_d_enum_type(arg_type, prefix) + elif util.is_void_ptr(arg_type): + return "void*" + elif util.is_const_void_ptr(arg_type): + return "const(void)*" + elif util.is_string_ptr(arg_type): + return "const(char)*" + elif is_const_struct_ptr(arg_type): + return f"const {as_d_struct_type(util.extract_ptr_type(arg_type), prefix)} *" + elif is_prim_ptr(arg_type): + return f"{as_d_prim_type(util.extract_ptr_type(arg_type))} *" + elif is_const_prim_ptr(arg_type): + return f"const {as_d_prim_type(util.extract_ptr_type(arg_type))} *" + else: + sys.exit(f"Error as_c_arg_type(): {arg_type}") + +def as_d_arg_type(arg_prefix, arg_type, prefix): + # NOTE: if arg_prefix is None, the result is used as return value + pre = "" if arg_prefix is None else arg_prefix + if arg_type == "void": + if arg_prefix is None: + return "void" + else: + return "" + elif is_prim_type(arg_type): + return as_d_prim_type(arg_type) + pre + elif is_struct_type(arg_type): + return as_d_struct_type(arg_type, prefix) + pre + elif is_enum_type(arg_type): + return as_d_enum_type(arg_type, prefix) + pre + elif util.is_void_ptr(arg_type): + return "scope void*" + pre + elif util.is_const_void_ptr(arg_type): + return "scope const(void)*" + pre + elif util.is_string_ptr(arg_type): + return "scope const(char)*" + pre + elif is_struct_ptr(arg_type): + return f"scope ref {as_d_struct_type(util.extract_ptr_type(arg_type), prefix)}" + pre + elif is_const_struct_ptr(arg_type): + return f"scope ref {as_d_struct_type(util.extract_ptr_type(arg_type), prefix)}" + pre + elif is_prim_ptr(arg_type): + return f"scope {as_d_prim_type(util.extract_ptr_type(arg_type))} *" + pre + elif is_const_prim_ptr(arg_type): + return f"scope const {as_d_prim_type(util.extract_ptr_type(arg_type))} *" + pre + else: + sys.exit(f"ERROR as_d_arg_type(): {arg_type}") + +def is_d_string(d_type): + return d_type == "string" + +# get C-style arguments of a function pointer as string +def funcptr_args_c(field_type, prefix): + tokens = field_type[field_type.index('(*)')+4:-1].split(',') + s = "" + for token in tokens: + arg_type = token.strip() + if s != "": + s += ", " + c_arg = as_c_arg_type(arg_type, prefix) + if c_arg == "void": + return "" + else: + s += c_arg + return s + +# get C-style result of a function pointer as string +def funcptr_result_c(field_type): + res_type = field_type[:field_type.index('(*)')].strip() + if res_type == 'void': + return 'void' + elif is_prim_type(res_type): + return as_d_prim_type(res_type) + elif util.is_const_void_ptr(res_type): + return 'const(void)*' + elif util.is_void_ptr(res_type): + return 'void*' + else: + sys.exit(f"ERROR funcptr_result_c(): {field_type}") + +def funcdecl_args_c(decl, prefix): + s = "" + func_name = decl['name'] + for param_decl in decl['params']: + if s != "": + s += ", " + param_name = param_decl['name'] + param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type']) + s += as_c_arg_type(param_type, prefix) + return s + +def funcdecl_args_d(decl, prefix): + s = "" + func_name = decl['name'] + for param_decl in decl['params']: + if s != "": + s += ", " + param_name = param_decl['name'] + param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type']) + s += f"{as_d_arg_type(f' {param_name}', param_type, prefix)}" + return s + +def funcdecl_result_c(decl, prefix): + func_name = decl['name'] + decl_type = decl['type'] + result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip()) + return as_c_arg_type(result_type, prefix) + +def funcdecl_result_d(decl, prefix): + func_name = decl['name'] + decl_type = decl['type'] + result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip()) + d_res_type = as_d_arg_type(None, result_type, prefix) + if is_d_string(d_res_type): + d_res_type = "string" + return d_res_type + +def gen_struct(decl, prefix): + struct_name = check_override(decl['name']) + d_type = as_d_struct_type(struct_name, prefix) + l(f"extern(C)\nstruct {d_type} {{") + for field in decl['fields']: + field_name = check_override(field['name']) + field_type = check_override(f'{struct_name}.{field_name}', default=field['type']) + if is_prim_type(field_type): + l(f" {as_d_prim_type(field_type)} {field_name} = {type_default_value(field_type)};") + elif is_struct_type(field_type): + l(f" {as_d_struct_type(field_type, prefix)} {field_name};") + elif is_enum_type(field_type): + l(f" {as_d_enum_type(field_type, prefix)} {field_name};") + elif util.is_string_ptr(field_type): + l(f" const(char)* {field_name} = null;") + elif util.is_const_void_ptr(field_type): + l(f" const(void)* {field_name} = null;") + elif util.is_void_ptr(field_type): + l(f" void* {field_name} = null;") + elif is_const_prim_ptr(field_type): + l(f" const {as_d_prim_type(util.extract_ptr_type(field_type))} = null;") + elif util.is_func_ptr(field_type): + l(f" extern(C) {funcptr_result_c(field_type)} function({funcptr_args_c(field_type, prefix)}) {field_name} = null;") + elif util.is_1d_array_type(field_type): + array_type = util.extract_array_type(field_type) + array_sizes = util.extract_array_sizes(field_type) + if is_prim_type(array_type) or is_struct_type(array_type): + if is_prim_type(array_type): + d_type = as_d_prim_type(array_type) + def_val = type_default_value(array_type) + elif is_struct_type(array_type): + d_type = as_d_struct_type(array_type, prefix) + def_val = '' + elif is_enum_type(array_type): + d_type = as_d_enum_type(array_type, prefix) + def_val = '' + else: + sys.exit(f"ERROR gen_struct is_1d_array_type: {array_type}") + t0 = f"{d_type}[{array_sizes[0]}]" + t1 = f"{d_type}[]" + if def_val != '': + l(f" {t0} {field_name} = {def_val};") + else: + l(f" {t0} {field_name};") + elif util.is_const_void_ptr(array_type): + l(f" const(void)*[{array_sizes[0]}] {field_name} = null;") + else: + sys.exit(f"ERROR gen_struct: array {field_name}: {field_type} => {array_type} [{array_sizes[0]}]") + elif util.is_2d_array_type(field_type): + array_type = util.extract_array_type(field_type) + array_sizes = util.extract_array_sizes(field_type) + if is_prim_type(array_type): + d_type = as_d_prim_type(array_type) + def_val = type_default_value(array_type) + elif is_struct_type(array_type): + d_type = as_d_struct_type(array_type, prefix) + def_val = '' + else: + sys.exit(f"ERROR gen_struct is_2d_array_type: {array_type}") + t0 = f"{d_type}[{array_sizes[0]}][{array_sizes[1]}]" + if def_val != '': + l(f" {t0} {field_name} = {def_val};") + else: + l(f" {t0} {field_name};") + else: + sys.exit(f"ERROR gen_struct: {field_type} {field_name};") + l("}") + +def gen_consts(decl, prefix): + for item in decl['items']: + item_name = check_override(item['name']) + l(f"enum {util.as_lower_snake_case(item_name, prefix)} = {item['value']};") + +def gen_enum(decl, prefix): + enum_name = check_override(decl['name']) + l(f"enum {as_d_enum_type(enum_name, prefix)} {{") + for item in decl['items']: + item_name = as_enum_item_name(check_override(item['name'])) + if item_name != "Force_u32": + if 'value' in item: + l(f" {item_name} = {item['value']},") + else: + l(f" {item_name},") + l("}") + +def gen_func_c(decl, prefix): + l(f"extern(C) {funcdecl_result_c(decl, prefix)} {decl['name']}({funcdecl_args_c(decl, prefix)}) @system @nogc nothrow;") + +def gen_func_d(decl, prefix): + c_func_name = decl['name'] + d_func_name = util.as_lower_camel_case(check_override(decl['name']), prefix) + if c_func_name in c_callbacks: + # a simple forwarded C callback function + l(f"alias {d_func_name} = {c_func_name};") + else: + d_res_type = funcdecl_result_d(decl, prefix) + l(f"{d_res_type} {d_func_name}({funcdecl_args_d(decl, prefix)}) @trusted @nogc nothrow {{") + if d_res_type != 'void': + s = f" return {c_func_name}(" + else: + s = f" {c_func_name}(" + for i, param_decl in enumerate(decl['params']): + if i > 0: + s += ", " + arg_name = param_decl['name'] + arg_type = param_decl['type'] + if is_const_struct_ptr(arg_type): + s += f"&{arg_name}" + elif util.is_string_ptr(arg_type): + s += f"{arg_name}" + else: + s += arg_name + if is_d_string(d_res_type): + s += ")" + s += ");" + l(s) + l("}") + +def pre_parse(inp): + global struct_types + global enum_types + for decl in inp['decls']: + kind = decl['kind'] + if kind == 'struct': + struct_types.append(decl['name']) + elif kind == 'enum': + enum_name = decl['name'] + enum_types.append(enum_name) + enum_items[enum_name] = [] + for item in decl['items']: + enum_items[enum_name].append(as_enum_item_name(item['name'])) + +def gen_imports(inp, dep_prefixes): + for dep_prefix in dep_prefixes: + dep_module_name = module_names[dep_prefix] + l(f'import {dep_prefix[:-1]} = sokol.{dep_module_name};') + l('') + +def gen_module(inp, dep_prefixes): + l('// machine generated, do not edit') + l('') + l(f'module sokol.{inp["module"]};') + gen_imports(inp, dep_prefixes) + pre_parse(inp) + prefix = inp['prefix'] + for decl in inp['decls']: + if not decl['is_dep']: + kind = decl['kind'] + if kind == 'consts': + gen_consts(decl, prefix) + elif not check_ignore(decl['name']): + if kind == 'struct': + gen_struct(decl, prefix) + elif kind == 'enum': + gen_enum(decl, prefix) + elif kind == 'func': + gen_func_c(decl, prefix) + gen_func_d(decl, prefix) + +def prepare(): + print('=== Generating d bindings:') + if not os.path.isdir('sokol-d/src/sokol'): + os.makedirs('sokol-d/src/sokol') + if not os.path.isdir('sokol-d/src/sokol/c'): + os.makedirs('sokol-d/src/sokol/c') + +def gen(c_header_path, c_prefix, dep_c_prefixes): + if not c_prefix in module_names: + print(f' >> warning: skipping generation for {c_prefix} prefix...') + return + module_name = module_names[c_prefix] + c_source_path = c_source_paths[c_prefix] + print(f' {c_header_path} => {module_name}') + reset_globals() + shutil.copyfile(c_header_path, f'sokol-d/src/sokol/c/{os.path.basename(c_header_path)}') + ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes) + gen_module(ir, dep_c_prefixes) + output_path = f"sokol-d/src/sokol/{ir['module']}.d" + with open(output_path, 'w', newline='\n') as f_outp: + f_outp.write(out_lines) diff --git a/thirdparty/sokol/bindgen/gen_ir.py b/thirdparty/sokol/bindgen/gen_ir.py new file mode 100644 index 0000000..0089ae2 --- /dev/null +++ b/thirdparty/sokol/bindgen/gen_ir.py @@ -0,0 +1,124 @@ +#------------------------------------------------------------------------------- +# Generate an intermediate representation of a clang AST dump. +#------------------------------------------------------------------------------- +import json, sys, subprocess + +def is_api_decl(decl, prefix): + if 'name' in decl: + return decl['name'].startswith(prefix) + elif decl['kind'] == 'EnumDecl': + # an anonymous enum, check if the items start with the prefix + return decl['inner'][0]['name'].lower().startswith(prefix) + else: + return False + +def is_dep_decl(decl, dep_prefixes): + for prefix in dep_prefixes: + if is_api_decl(decl, prefix): + return True + return False + +def dep_prefix(decl, dep_prefixes): + for prefix in dep_prefixes: + if is_api_decl(decl, prefix): + return prefix + return None + +def filter_types(str): + return str.replace('_Bool', 'bool') + +def parse_struct(decl): + outp = {} + outp['kind'] = 'struct' + outp['name'] = decl['name'] + outp['fields'] = [] + for item_decl in decl['inner']: + if item_decl['kind'] != 'FieldDecl': + sys.exit(f"ERROR: Structs must only contain simple fields ({decl['name']})") + item = {} + if 'name' in item_decl: + item['name'] = item_decl['name'] + item['type'] = filter_types(item_decl['type']['qualType']) + outp['fields'].append(item) + return outp + +def parse_enum(decl): + outp = {} + if 'name' in decl: + outp['kind'] = 'enum' + outp['name'] = decl['name'] + needs_value = False + else: + outp['kind'] = 'consts' + needs_value = True + outp['items'] = [] + for item_decl in decl['inner']: + if item_decl['kind'] == 'EnumConstantDecl': + item = {} + item['name'] = item_decl['name'] + if 'inner' in item_decl: + const_expr = item_decl['inner'][0] + if const_expr['kind'] != 'ConstantExpr': + sys.exit(f"ERROR: Enum values must be a ConstantExpr ({item_decl['name']}), is '{const_expr['kind']}'") + if const_expr['valueCategory'] != 'rvalue' and const_expr['valueCategory'] != 'prvalue': + sys.exit(f"ERROR: Enum value ConstantExpr must be 'rvalue' or 'prvalue' ({item_decl['name']}), is '{const_expr['valueCategory']}'") + if not ((len(const_expr['inner']) == 1) and (const_expr['inner'][0]['kind'] == 'IntegerLiteral')): + sys.exit(f"ERROR: Enum value ConstantExpr must have exactly one IntegerLiteral ({item_decl['name']})") + item['value'] = const_expr['inner'][0]['value'] + if needs_value and 'value' not in item: + sys.exit(f"ERROR: anonymous enum items require an explicit value") + outp['items'].append(item) + return outp + +def parse_func(decl): + outp = {} + outp['kind'] = 'func' + outp['name'] = decl['name'] + outp['type'] = filter_types(decl['type']['qualType']) + outp['params'] = [] + if 'inner' in decl: + for param in decl['inner']: + if param['kind'] != 'ParmVarDecl': + print(f" >> warning: ignoring func {decl['name']} (unsupported parameter type)") + return None + outp_param = {} + outp_param['name'] = param['name'] + outp_param['type'] = filter_types(param['type']['qualType']) + outp['params'].append(outp_param) + return outp + +def parse_decl(decl): + kind = decl['kind'] + if kind == 'RecordDecl': + return parse_struct(decl) + elif kind == 'EnumDecl': + return parse_enum(decl) + elif kind == 'FunctionDecl': + return parse_func(decl) + else: + return None + +def clang(csrc_path): + cmd = ['clang', '-Xclang', '-ast-dump=json', '-c' ] + cmd.append(csrc_path) + return subprocess.check_output(cmd) + +def gen(header_path, source_path, module, main_prefix, dep_prefixes): + ast = clang(source_path) + inp = json.loads(ast) + outp = {} + outp['module'] = module + outp['prefix'] = main_prefix + outp['dep_prefixes'] = dep_prefixes + outp['decls'] = [] + for decl in inp['inner']: + is_dep = is_dep_decl(decl, dep_prefixes) + if is_api_decl(decl, main_prefix) or is_dep: + outp_decl = parse_decl(decl) + if outp_decl is not None: + outp_decl['is_dep'] = is_dep + outp_decl['dep_prefix'] = dep_prefix(decl, dep_prefixes) + outp['decls'].append(outp_decl) + with open(f'{module}.json', 'w') as f: + f.write(json.dumps(outp, indent=2)); + return outp diff --git a/thirdparty/sokol/bindgen/gen_nim.py b/thirdparty/sokol/bindgen/gen_nim.py new file mode 100644 index 0000000..0468dfb --- /dev/null +++ b/thirdparty/sokol/bindgen/gen_nim.py @@ -0,0 +1,613 @@ +#------------------------------------------------------------------------------- +# Generate Nim bindings +# +# Nim coding style: +# - type identifiers are PascalCase, everything else is camelCase +# - reference: https://nim-lang.org/docs/nep1.html +#------------------------------------------------------------------------------- +import gen_ir +import gen_util as util +import os, shutil, sys + +module_names = { + 'slog_': 'log', + 'sg_': 'gfx', + 'sapp_': 'app', + 'stm_': 'time', + 'saudio_': 'audio', + 'sgl_': 'gl', + 'sdtx_': 'debugtext', + 'sshape_': 'shape', + 'sglue_': 'glue', +} + +c_source_paths = { + 'slog_': 'sokol-nim/src/sokol/c/sokol_log.c', + 'sg_': 'sokol-nim/src/sokol/c/sokol_gfx.c', + 'sapp_': 'sokol-nim/src/sokol/c/sokol_app.c', + 'stm_': 'sokol-nim/src/sokol/c/sokol_time.c', + 'saudio_': 'sokol-nim/src/sokol/c/sokol_audio.c', + 'sgl_': 'sokol-nim/src/sokol/c/sokol_gl.c', + 'sdtx_': 'sokol-nim/src/sokol/c/sokol_debugtext.c', + 'sshape_': 'sokol-nim/src/sokol/c/sokol_shape.c', + 'sglue_': 'sokol-nim/src/sokol/c/sokol_glue.c', +} + +c_callbacks = [ + 'slog_func', +] + +ignores = [ + 'sdtx_printf', + 'sdtx_vprintf', +] + +overrides = { + 'sgl_error': 'sgl_get_error', + 'sgl_deg': 'sgl_as_degrees', + 'sgl_rad': 'sgl_as_radians', + 'SGL_NO_ERROR': 'SGL_ERROR_NO_ERROR', + 'SG_BUFFERTYPE_VERTEXBUFFER': 'SG_BUFFERTYPE_VERTEX_BUFFER', + 'SG_BUFFERTYPE_INDEXBUFFER': 'SG_BUFFERTYPE_INDEX_BUFFER', + 'SG_ACTION_DONTCARE': 'SG_ACTION_DONT_CARE', + 'ptr': 'addr', # range ptr + 'func': 'fn', + 'slog_func': 'fn', +} + +enumPrefixOverrides = { + # sokol_gfx.h + 'LOADACTION': 'loadAction', + 'STOREACTION': 'storeAction', + 'PIXELFORMAT': 'pixelFormat', + 'RESOURCESTATE': 'resourceState', + 'BUFFERTYPE': 'bufferType', + 'INDEXTYPE': 'indexType', + 'IMAGETYPE': 'imageType', + 'SAMPLERTYPE': 'samplerType', + 'CUBEFACE': 'cubeFace', + 'SHADERSTAGE': 'shaderStage', + 'PRIMITIVETYPE': 'primitiveType', + 'BORDERCOLOR': 'borderColor', + 'VERTEXFORMAT': 'vertexFormat', + 'VERTEXSTEP': 'vertexStep', + 'UNIFORMTYPE': 'uniformType', + 'UNIFORMLAYOUT': 'uniformLayout', + 'CULLMODE': 'cullMode', + 'FACEWINDING': 'faceWinding', + 'COMPAREFUNC': 'compareFunc', + 'STENCILOP': 'stencilOp', + 'BLENDFACTOR': 'blendFactor', + 'BLENDOP': 'blendOp', + 'COLORMASK': 'colorMask', + # sokol_app.h + 'EVENTTYPE': 'eventType', + 'KEYCODE': 'keyCode', + 'MOUSEBUTTON': 'mouseButton', +} + +prim_types = { + 'int': 'int32', + 'bool': 'bool', + 'char': 'char', + 'int8_t': 'int8', + 'uint8_t': 'uint8', + 'int16_t': 'int16', + 'uint16_t': 'uint16', + 'int32_t': 'int32', + 'uint32_t': 'uint32', + 'int64_t': 'int64', + 'uint64_t': 'uint64', + 'float': 'float32', + 'double': 'float64', + 'uintptr_t': 'uint', + 'intptr_t': 'int', + 'size_t': 'int', # not a bug, Nim's sizeof() returns int +} + +prim_defaults = { + 'int': '0', + 'bool': 'false', + 'int8_t': '0', + 'uint8_t': '0', + 'int16_t': '0', + 'uint16_t': '0', + 'int32_t': '0', + 'uint32_t': '0', + 'int64_t': '0', + 'uint64_t': '0', + 'float': '0.0f', + 'double': '0.0', + 'uintptr_t': '0', + 'intptr_t': '0', + 'size_t': '0' +} + +common_prim_types = """ +array +untyped typed void +bool byte char +int int8 int16 int32 int64 +uint uint8 uint16 uint32 uint64 +float float32 float64 +string +cchar cint csize_t +cfloat cdouble +cstring +pointer +""".split() + +keywords = """ +addr and as asm +bind block break +case cast concept const continue converter +defer discard distinct div do +elif else end enum except export +finally for from func +if import in include interface is isnot iterator +let +macro method mixin mod +nil not notin +object of or out +proc ptr +raise ref return +shl shr static +template try tuple type +using +var +when while +xor +yield +""".split() + common_prim_types + +struct_types = [] +enum_types = [] +out_lines = '' + +def reset_globals(): + global struct_types + global enum_types + global out_lines + struct_types = [] + enum_types = [] + out_lines = '' + +def l(s): + global out_lines + out_lines += s + '\n' + +def as_nim_prim_type(s): + return prim_types[s] + +# prefix_bla_blub(_t) => (dep.)BlaBlub +def as_nim_type_name(s, prefix): + parts = s.lower().split('_') + dep = parts[0] + '_' + outp = '' + if not s.startswith(prefix) and dep in module_names: + outp = module_names[dep] + '.' + for part in parts[1:]: + # ignore '_t' type postfix + if (part != 't'): + outp += part.capitalize() + return outp + +def check_override(name, default=None): + if name in overrides: + return overrides[name] + elif default is None: + return name + else: + return default + +def check_ignore(name): + return name in ignores + +def is_power_of_two(val): + return val == 0 or val & (val - 1) == 0 + +def wrap_keywords(s): + if s in keywords: + return f'`{s}`' + else: + return s + +# prefix_bla_blub => blaBlub +def as_camel_case(s, prefix, wrap=True): + outp = s.lower() + if outp.startswith(prefix): + outp = outp[len(prefix):] + parts = outp.lstrip('_').split('_') + outp = parts[0] + for part in parts[1:]: + outp += part.capitalize() + if wrap: + outp = wrap_keywords(outp) + return outp + +# PREFIX_ENUM_BLA_BLO => blaBlo +def as_enum_item_name(s, wrap=True): + outp = s.lstrip('_') + parts = outp.split('_')[1:] + if parts[0] in enumPrefixOverrides: + parts[0] = enumPrefixOverrides[parts[0]] + else: + parts[0] = parts[0].lower() + outp = parts[0] + for part in parts[1:]: + outp += part.capitalize() + if wrap: + outp = wrap_keywords(outp) + return outp + +def is_prim_type(s): + return s in prim_types + +def is_struct_type(s): + return s in struct_types + +def is_enum_type(s): + return s in enum_types + +def is_const_prim_ptr(s): + for prim_type in prim_types: + if s == f"const {prim_type} *": + return True + return False + +def is_prim_ptr(s): + for prim_type in prim_types: + if s == f"{prim_type} *": + return True + return False + +def is_const_struct_ptr(s): + for struct_type in struct_types: + if s == f"const {struct_type} *": + return True + return False + +def type_default_value(s): + return prim_defaults[s] + +def funcptr_args(field_type, prefix): + tokens = field_type[field_type.index('(*)')+4:-1].split(',') + s = "" + n = 0 + for token in tokens: + n += 1 + arg_ctype = token.strip() + if s != "": + s += ", " + arg_nimtype = as_nim_type(arg_ctype, prefix) + if arg_nimtype == "": + return "" # fun(void) + s += f"a{n}:{arg_nimtype}" + if s == "a1:void": + s = "" + return s + +def funcptr_result(field_type, prefix): + ctype = field_type[:field_type.index('(*)')].strip() + return as_nim_type(ctype, prefix) + +def as_nim_type(ctype, prefix, struct_ptr_as_value=False): + if ctype == "void": + return "" + elif is_prim_type(ctype): + return as_nim_prim_type(ctype) + elif is_struct_type(ctype): + return as_nim_type_name(ctype, prefix) + elif is_enum_type(ctype): + return as_nim_type_name(ctype, prefix) + elif util.is_string_ptr(ctype): + return "cstring" + elif util.is_void_ptr(ctype) or util.is_const_void_ptr(ctype): + return "pointer" + elif is_const_struct_ptr(ctype): + nim_type = as_nim_type(util.extract_ptr_type(ctype), prefix) + if struct_ptr_as_value: + return f"{nim_type}" + else: + return f"ptr {nim_type}" + elif is_prim_ptr(ctype) or is_const_prim_ptr(ctype): + return f"ptr {as_nim_type(util.extract_ptr_type(ctype), prefix)}" + elif util.is_func_ptr(ctype): + args = funcptr_args(ctype, prefix) + res = funcptr_result(ctype, prefix) + if res != "": + res = ":" + res + return f"proc({args}){res} {{.cdecl.}}" + elif util.is_1d_array_type(ctype): + array_ctype = util.extract_array_type(ctype) + array_sizes = util.extract_array_sizes(ctype) + return f'array[{array_sizes[0]}, {as_nim_type(array_ctype, prefix)}]' + elif util.is_2d_array_type(ctype): + array_ctype = util.extract_array_type(ctype) + array_sizes = util.extract_array_sizes(ctype) + return f'array[{array_sizes[0]}, array[{array_sizes[1]}, {as_nim_type(array_ctype, prefix)}]]' + else: + sys.exit(f"ERROR as_nim_type: {ctype}") + +def as_nim_struct_name(struct_decl, prefix): + struct_name = check_override(struct_decl['name']) + nim_type = f'{as_nim_type_name(struct_name, prefix)}' + return nim_type + +def as_nim_field_name(field_decl, prefix, check_private=True): + field_name = as_camel_case(check_override(field_decl['name']), prefix) + if check_private: + is_private = field_decl['name'].startswith('_') + if not is_private: + field_name += "*" + return field_name + +def as_nim_field_type(struct_decl, field_decl, prefix): + return as_nim_type(check_override(f"{struct_decl['name']}.{field_decl['name']}", default=field_decl['type']), prefix) + +def gen_struct(decl, prefix): + l(f"type {as_nim_struct_name(decl, prefix)}* = object") + for field in decl['fields']: + l(f" {as_nim_field_name(field, prefix)}:{as_nim_field_type(decl, field, prefix)}") + l("") + +def gen_consts(decl, prefix): + l("const") + for item in decl['items']: + item_name = check_override(item['name']) + l(f" {as_camel_case(item_name, prefix)}* = {item['value']}") + l("") + +def gen_enum(decl, prefix): + item_names_by_value = {} + value = -1 + has_explicit_values = False + for item in decl['items']: + item_name = check_override(item['name']) + if item_name.endswith("_NUM") or item_name.endswith("_FORCE_U32"): + continue + else: + if 'value' in item: + has_explicit_values = True + value = int(item['value']) + else: + value += 1 + item_names_by_value[value] = as_enum_item_name(item_name) + enum_name_nim = as_nim_type_name(decl['name'], prefix) + l('type') + l(f" {enum_name_nim}* {{.size:sizeof(int32).}} = enum") + if has_explicit_values: + # Nim requires explicit enum values to be declared in ascending order + for value in sorted(item_names_by_value): + name = item_names_by_value[value] + l(f" {name} = {value},") + else: + for name in item_names_by_value.values(): + l(f" {name},") + l("") + +# returns C prototype compatible function args (with pointers) +def funcdecl_args_c(decl, prefix): + s = "" + func_name = decl['name'] + for param_decl in decl['params']: + if s != "": + s += ", " + arg_name = param_decl['name'] + arg_type = check_override(f'{func_name}.{arg_name}', default=param_decl['type']) + s += f"{as_camel_case(arg_name, prefix)}:{as_nim_type(arg_type, prefix)}" + return s + +# returns Nim function args (pass structs by value) +def funcdecl_args_nim(decl, prefix): + s = "" + func_name = decl['name'] + for param_decl in decl['params']: + if s != "": + s += ", " + arg_name = param_decl['name'] + arg_type = check_override(f'{func_name}.{arg_name}', default=param_decl['type']) + s += f"{as_camel_case(arg_name, prefix)}:{as_nim_type(arg_type, prefix, struct_ptr_as_value=True)}" + return s + +def funcdecl_result(decl, prefix): + func_name = decl['name'] + decl_type = decl['type'] + result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip()) + nim_res_type = as_nim_type(result_type, prefix) + if nim_res_type == "": + nim_res_type = "void" + return nim_res_type + +def gen_func_nim(decl, prefix): + c_func_name = decl['name'] + nim_func_name = as_camel_case(check_override(c_func_name), prefix, wrap=False) + nim_res_type = funcdecl_result(decl, prefix) + if c_func_name in c_callbacks: + l(f"proc {nim_func_name}*({funcdecl_args_c(decl, prefix)}):{nim_res_type} {{.cdecl, importc:\"{c_func_name}\".}}") + else: + l(f"proc c_{nim_func_name}({funcdecl_args_c(decl, prefix)}):{nim_res_type} {{.cdecl, importc:\"{c_func_name}\".}}") + l(f"proc {wrap_keywords(nim_func_name)}*({funcdecl_args_nim(decl, prefix)}):{nim_res_type} =") + s = f" c_{nim_func_name}(" + for i, param_decl in enumerate(decl['params']): + if i > 0: + s += ", " + arg_name = param_decl['name'] + arg_type = param_decl['type'] + if is_const_struct_ptr(arg_type): + s += f"addr({arg_name})" + else: + s += arg_name + s += ")" + l(s) + l("") + +def gen_array_converters(decl, prefix): + for field in decl['fields']: + if util.is_array_type(field['type']): + array_type = util.extract_array_type(field['type']) + array_sizes = util.extract_array_sizes(field['type']) + struct_name = as_nim_struct_name(decl, prefix) + field_name = as_nim_field_name(field, prefix, check_private=False) + array_base_type = as_nim_type(array_type, prefix) + if util.is_1d_array_type(field['type']): + n = array_sizes[0] + l(f'converter to{struct_name}{field_name}*[N:static[int]](items: array[N, {array_base_type}]): array[{n}, {array_base_type}] =') + l(f' static: assert(N <= {n})') + l(f' for index,item in items.pairs: result[index]=item') + l('') + elif util.is_2d_array_type(field['type']): + x = array_sizes[1] + y = array_sizes[0] + l(f'converter to{struct_name}{field_name}*[Y:static[int], X:static[int]](items: array[Y, array[X, {array_base_type}]]): array[{y}, array[{x}, {array_base_type}]] =') + l(f' static: assert(X <= {x})') + l(f' static: assert(Y <= {y})') + l(f' for indexY,itemY in items.pairs:') + l(f' for indexX, itemX in itemY.pairs:') + l(f' result[indexY][indexX] = itemX') + l('') + else: + sys.exit('Unsupported converter array dimension (> 2)!') + +def pre_parse(inp): + global struct_types + global enum_types + for decl in inp['decls']: + kind = decl['kind'] + if kind == 'struct': + struct_types.append(decl['name']) + elif kind == 'enum': + enum_name = decl['name'] + enum_types.append(enum_name) + +def gen_imports(inp, dep_prefixes): + for dep_prefix in dep_prefixes: + dep_module_name = module_names[dep_prefix] + l(f'import {dep_module_name}') + l('') + +def gen_extra(inp): + if inp['prefix'] in ['sg_']: + # FIXME: remove when sokol-shdc has been integrated! + l('when defined gl:') + l(' const gl* = true') + l(' const d3d11* = false') + l(' const metal* = false') + l('elif defined windows:') + l(' const gl* = false') + l(' const d3d11* = true') + l(' const metal* = false') + l('elif defined macosx:') + l(' const gl* = false') + l(' const d3d11* = false') + l(' const metal* = true') + l('elif defined linux:') + l(' const gl* = true') + l(' const d3d11* = false') + l(' const metal* = false') + l('else:') + l(' error("unsupported platform")') + l('') + if inp['prefix'] in ['sg_', 'sapp_']: + l('when defined windows:') + l(' when not defined vcc:') + l(' {.passl:"-lkernel32 -luser32 -lshell32 -lgdi32".}') + l(' when defined gl:') + l(' {.passc:"-DSOKOL_GLCORE".}') + l(' else:') + l(' {.passc:"-DSOKOL_D3D11".}') + l(' when not defined vcc:') + l(' {.passl:"-ld3d11 -ldxgi".}') + l('elif defined macosx:') + l(' {.passc:"-x objective-c".}') + l(' {.passl:"-framework Cocoa -framework QuartzCore".}') + l(' when defined gl:') + l(' {.passc:"-DSOKOL_GLCORE".}') + l(' {.passl:"-framework OpenGL".}') + l(' else:') + l(' {.passc:"-DSOKOL_METAL".}') + l(' {.passl:"-framework Metal -framework MetalKit".}') + l('elif defined linux:') + l(' {.passc:"-DSOKOL_GLCORE".}') + l(' {.passl:"-lX11 -lXi -lXcursor -lGL -lm -ldl -lpthread".}') + l('else:') + l(' error("unsupported platform")') + l('') + if inp['prefix'] in ['saudio_']: + l('when defined windows:') + l(' when not defined vcc:') + l(' {.passl:"-lkernel32 -lole32".}') + l('elif defined macosx:') + l(' {.passl:"-framework AudioToolbox".}') + l('elif defined linux:') + l(' {.passl:"-lasound -lm -lpthread".}') + l('else:') + l(' error("unsupported platform")') + l('') + if inp['prefix'] in ['sg_']: + l('## Convert a 4-element tuple of numbers to a gfx.Color') + l('converter toColor*[R:SomeNumber,G:SomeNumber,B:SomeNumber,A:SomeNumber](rgba: tuple [r:R,g:G,b:B,a:A]):Color =') + l(' Color(r:rgba.r.float32, g:rgba.g.float32, b:rgba.b.float32, a:rgba.a.float32)') + l('') + l('## Convert a 3-element tuple of numbers to a gfx.Color') + l('converter toColor*[R:SomeNumber,G:SomeNumber,B:SomeNumber](rgba: tuple [r:R,g:G,b:B]):Color =') + l(' Color(r:rgba.r.float32, g:rgba.g.float32, b:rgba.b.float32, a:1.float32)') + l('') + # NOTE: this simplistic to_Range() converter has various issues, some of them dangerous: + # - doesn't work as expected for slice types + # - it's very easy to create a range that points to invalid memory + # (so far observed for stack-allocated structs <= 16 bytes) + #if inp['prefix'] in ['sg_', 'sdtx_', 'sshape_']: + # l('# helper function to convert "anything" into a Range') + # l('converter to_Range*[T](source: T): Range =') + # l(' Range(addr: source.addr, size: source.sizeof.uint)') + # l('') + c_source_path = '/'.join(c_source_paths[inp['prefix']].split('/')[3:]) + l('{.passc:"-DSOKOL_NIM_IMPL".}') + l('when defined(release):') + l(' {.passc:"-DNDEBUG".}') + l(f'{{.compile:"{c_source_path}".}}') + +def gen_module(inp, dep_prefixes): + l('## machine generated, do not edit') + l('') + gen_imports(inp, dep_prefixes) + pre_parse(inp) + prefix = inp['prefix'] + for decl in inp['decls']: + if not decl['is_dep']: + kind = decl['kind'] + if kind == 'consts': + gen_consts(decl, prefix) + elif not check_ignore(decl['name']): + if kind == 'struct': + gen_struct(decl, prefix) + gen_array_converters(decl, prefix) + elif kind == 'enum': + gen_enum(decl, prefix) + elif kind == 'func': + gen_func_nim(decl, prefix) + gen_extra(inp) + +def prepare(): + print('=== Generating Nim bindings:') + if not os.path.isdir('sokol-nim/src/sokol'): + os.makedirs('sokol-nim/src/sokol') + if not os.path.isdir('sokol-nim/src/sokol/c'): + os.makedirs('sokol-nim/src/sokol/c') + +def gen(c_header_path, c_prefix, dep_c_prefixes): + if not c_prefix in module_names: + print(f' >> warning: skipping generation for {c_prefix} prefix...') + return + global out_lines + module_name = module_names[c_prefix] + c_source_path = c_source_paths[c_prefix] + print(f' {c_header_path} => {module_name}') + reset_globals() + shutil.copyfile(c_header_path, f'sokol-nim/src/sokol/c/{os.path.basename(c_header_path)}') + ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes) + gen_module(ir, dep_c_prefixes) + output_path = f"sokol-nim/src/sokol/{ir['module']}.nim" + with open(output_path, 'w', newline='\n') as f_outp: + f_outp.write(out_lines) diff --git a/thirdparty/sokol/bindgen/gen_odin.py b/thirdparty/sokol/bindgen/gen_odin.py new file mode 100644 index 0000000..d2bc55d --- /dev/null +++ b/thirdparty/sokol/bindgen/gen_odin.py @@ -0,0 +1,535 @@ +#------------------------------------------------------------------------------- +# gen_odin.py +# +# Generate Odin bindings. +#------------------------------------------------------------------------------- +import gen_ir +import gen_util as util +import os, shutil, sys + +bindings_root = 'sokol-odin' +c_root = f'{bindings_root}/sokol/c' +module_root = f'{bindings_root}/sokol' + +module_names = { + 'slog_': 'log', + 'sg_': 'gfx', + 'sapp_': 'app', + 'stm_': 'time', + 'saudio_': 'audio', + 'sgl_': 'gl', + 'sdtx_': 'debugtext', + 'sshape_': 'shape', + 'sglue_': 'glue', +} + +system_libs = { + 'sg_': { + 'windows': { + 'd3d11': "", + 'gl': "", + }, + 'macos': { + 'metal': '"system:Cocoa.framework","system:QuartzCore.framework","system:Metal.framework","system:MetalKit.framework"', + 'gl': '"system:Cocoa.framework","system:QuartzCore.framework","system:OpenGL.framework"' + }, + 'linux': { + 'gl': '"system:GL", "system:dl", "system:pthread"' + } + }, + 'sapp_': { + 'windows': { + 'd3d11': '', + 'gl': '', + }, + 'macos': { + 'metal': '"system:Cocoa.framework","system:QuartzCore.framework","system:Metal.framework","system:MetalKit.framework"', + 'gl': '"system:Cocoa.framework","system:QuartzCore.framework","system:OpenGL.framework"', + }, + 'linux': { + 'gl': '"system:X11", "system:Xi", "system:Xcursor", "system:GL", "system:dl", "system:pthread"' + } + }, + 'saudio_': { + 'windows': { + 'd3d11': '', + 'gl': '', + }, + 'macos': { + 'metal': '"system:AudioToolbox.framework"', + 'gl': '"system:AudioToolbox.framework"', + }, + 'linux': { + 'gl': '"system:asound", "system:dl", "system:pthread"', + } + } +} + +c_source_names = { + 'slog_': 'sokol_log.c', + 'sg_': 'sokol_gfx.c', + 'sapp_': 'sokol_app.c', + 'sapp_sg': 'sokol_glue.c', + 'stm_': 'sokol_time.c', + 'saudio_': 'sokol_audio.c', + 'sgl_': 'sokol_gl.c', + 'sdtx_': 'sokol_debugtext.c', + 'sshape_': 'sokol_shape.c', + 'sglue_': 'sokol_glue.c', +} + +ignores = [ + 'sdtx_printf', + 'sdtx_vprintf', + 'sg_install_trace_hooks', + 'sg_trace_hooks', +] + +# NOTE: syntax for function results: "func_name.RESULT" +overrides = { + 'context': 'ctx', # reserved keyword + 'SGL_NO_ERROR': 'SGL_ERROR_NO_ERROR', +} + +prim_types = { + 'int': 'c.int', + 'bool': 'bool', + 'char': 'u8', + 'int8_t': 'i8', + 'uint8_t': 'u8', + 'int16_t': 'i16', + 'uint16_t': 'u16', + 'int32_t': 'i32', + 'uint32_t': 'u32', + 'int64_t': 'i64', + 'uint64_t': 'u64', + 'float': 'f32', + 'double': 'f64', + 'uintptr_t': 'u64', + 'intptr_t': 'i64', + 'size_t': 'u64' +} + +prim_defaults = { + 'int': '0', + 'bool': 'false', + 'int8_t': '0', + 'uint8_t': '0', + 'int16_t': '0', + 'uint16_t': '0', + 'int32_t': '0', + 'uint32_t': '0', + 'int64_t': '0', + 'uint64_t': '0', + 'float': '0.0', + 'double': '0.0', + 'uintptr_t': '0', + 'intptr_t': '0', + 'size_t': '0' +} + +struct_types = [] +enum_types = [] +enum_items = {} +out_lines = '' + +def reset_globals(): + global struct_types + global enum_types + global enum_items + global out_lines + struct_types = [] + enum_types = [] + enum_items = {} + out_lines = '' + +def l(s): + global out_lines + out_lines += s + '\n' + +def check_override(name, default=None): + if name in overrides: + return overrides[name] + elif default is None: + return name + else: + return default + +def check_ignore(name): + return name in ignores + +# PREFIX_BLA_BLUB to BLA_BLUB, prefix_bla_blub to bla_blub +def as_snake_case(s, prefix): + outp = s + if outp.lower().startswith(prefix): + outp = outp[len(prefix):] + return outp + +def get_odin_module_path(c_prefix): + return f'{module_root}/{module_names[c_prefix]}' + +def get_csource_path(c_prefix): + return f'{c_root}/{c_source_names[c_prefix]}' + +def make_odin_module_directory(c_prefix): + path = get_odin_module_path(c_prefix) + if not os.path.isdir(path): + os.makedirs(path) + +def as_prim_type(s): + return prim_types[s] + +# prefix_bla_blub(_t) => (dep.)Bla_Blub +def as_struct_or_enum_type(s, prefix): + parts = s.lower().split('_') + outp = '' if s.startswith(prefix) else f'{parts[0]}.' + for part in parts[1:]: + # ignore '_t' type postfix + if (part != 't'): + outp += part.capitalize() + outp += '_' + outp = outp[:-1] + return outp + +# PREFIX_ENUM_BLA_BLUB => BLA_BLUB, _PREFIX_ENUM_BLA_BLUB => BLA_BLUB +def as_enum_item_name(s): + outp = s.lstrip('_') + parts = outp.split('_')[2:] + outp = '_'.join(parts) + if outp[0].isdigit(): + outp = '_' + outp + return outp + +def enum_default_item(enum_name): + return enum_items[enum_name][0] + +def is_prim_type(s): + return s in prim_types + +def is_int_type(s): + return s == "int" + +def is_struct_type(s): + return s in struct_types + +def is_enum_type(s): + return s in enum_types + +def is_const_prim_ptr(s): + for prim_type in prim_types: + if s == f"const {prim_type} *": + return True + return False + +def is_prim_ptr(s): + for prim_type in prim_types: + if s == f"{prim_type} *": + return True + return False + +def is_const_struct_ptr(s): + for struct_type in struct_types: + if s == f"const {struct_type} *": + return True + return False + +def type_default_value(s): + return prim_defaults[s] + +def map_type(type, prefix, sub_type): + if sub_type not in ['c_arg', 'odin_arg', 'struct_field']: + sys.exit(f"Error: map_type(): unknown sub_type '{sub_type}") + if type == "void": + return "" + elif is_prim_type(type): + if sub_type == 'odin_arg': + # for Odin args, maps C int (32-bit) to Odin int (pointer-sized), + # and the C bool type to Odin's bool type + if type == 'int' or type == 'uint32_t': + return 'int' + elif type == 'bool': + return 'bool' + return as_prim_type(type) + elif is_struct_type(type): + return as_struct_or_enum_type(type, prefix) + elif is_enum_type(type): + return as_struct_or_enum_type(type, prefix) + elif util.is_void_ptr(type): + return "rawptr" + elif util.is_const_void_ptr(type): + return "rawptr" + elif util.is_string_ptr(type): + return "cstring" + elif is_const_struct_ptr(type): + # pass Odin struct args by value, not by pointer + if sub_type == 'odin_arg': + return f"{as_struct_or_enum_type(util.extract_ptr_type(type), prefix)}" + else: + return f"^{as_struct_or_enum_type(util.extract_ptr_type(type), prefix)}" + elif is_prim_ptr(type): + return f"^{as_prim_type(util.extract_ptr_type(type))}" + elif is_const_prim_ptr(type): + return f"^{as_prim_type(util.extract_ptr_type(type))}" + elif util.is_1d_array_type(type): + array_type = util.extract_array_type(type) + array_sizes = util.extract_array_sizes(type) + return f"[{array_sizes[0]}]{map_type(array_type, prefix, sub_type)}" + elif util.is_2d_array_type(type): + array_type = util.extract_array_type(type) + array_sizes = util.extract_array_sizes(type) + return f"[{array_sizes[0]}][{array_sizes[1]}]{map_type(array_type, prefix, sub_type)}" + elif util.is_func_ptr(type): + res_type = funcptr_result_c(type, prefix) + res_str = '' if res_type == '' else f' -> {res_type}' + return f'proc "c" ({funcptr_args_c(type, prefix)}){res_str}' + else: + sys.exit(f"Error map_type(): unknown type '{type}'") + +def funcdecl_args_c(decl, prefix): + s = '' + func_name = decl['name'] + for param_decl in decl['params']: + if s != '': + s += ', ' + param_name = param_decl['name'] + param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type']) + if is_const_struct_ptr(param_type): + s += f"#by_ptr {param_name}: {map_type(param_type, prefix, 'odin_arg')}" + elif is_int_type(param_type): + s += f"#any_int {param_name}: {map_type(param_type, prefix, 'c_arg')}" + else: + s += f"{param_name}: {map_type(param_type, prefix, 'c_arg')}" + return s + +def funcptr_args_c(field_type, prefix): + tokens = field_type[field_type.index('(*)')+4:-1].split(',') + s = '' + arg_index = 0 + for token in tokens: + arg_type = token.strip() + if s != '': + s += ', ' + c_arg = map_type(arg_type, prefix, 'c_arg') + if c_arg == '': + return '' + else: + s += f'a{arg_index}: {c_arg}' + arg_index += 1 + return s + +def funcptr_result_c(field_type, prefix): + res_type = field_type[:field_type.index('(*)')].strip() + return map_type(res_type, prefix, 'c_arg') + +def funcdecl_result_c(decl, prefix): + func_name = decl['name'] + decl_type = decl['type'] + res_c_type = decl_type[:decl_type.index('(')].strip() + return map_type(check_override(f'{func_name}.RESULT', default=res_c_type), prefix, 'c_arg') + +def get_system_libs(module, platform, backend): + if module in system_libs: + if platform in system_libs[module]: + if backend in system_libs[module][platform]: + libs = system_libs[module][platform][backend] + if libs != '': + return f", {libs}" + return '' + +def gen_c_imports(inp, c_prefix, prefix): + module_name = inp["module"] + clib_prefix = f'sokol_{module_name}' + clib_import = f'{clib_prefix}_clib' + windows_d3d11_libs = get_system_libs(prefix, 'windows', 'd3d11') + windows_gl_libs = get_system_libs(prefix, 'windows', 'gl') + macos_metal_libs = get_system_libs(prefix, 'macos', 'metal') + macos_gl_libs = get_system_libs(prefix, 'macos', 'gl') + linux_gl_libs = get_system_libs(prefix, 'linux', 'gl') + l( 'import "core:c"') + l( '') + l( 'SOKOL_DEBUG :: #config(SOKOL_DEBUG, ODIN_DEBUG)') + l( '') + l(f'DEBUG :: #config(SOKOL_{module_name.upper()}_DEBUG, SOKOL_DEBUG)') + l( 'USE_GL :: #config(SOKOL_USE_GL, false)') + l( 'USE_DLL :: #config(SOKOL_DLL, false)') + l( '') + l( 'when ODIN_OS == .Windows {') + l( ' when USE_DLL {') + l( ' when USE_GL {') + l(f' when DEBUG {{ foreign import {clib_import} {{ "../sokol_dll_windows_x64_gl_debug.lib"{windows_gl_libs} }} }}') + l(f' else {{ foreign import {clib_import} {{ "../sokol_dll_windows_x64_gl_release.lib"{windows_gl_libs} }} }}') + l( ' } else {') + l(f' when DEBUG {{ foreign import {clib_import} {{ "../sokol_dll_windows_x64_d3d11_debug.lib"{windows_d3d11_libs} }} }}') + l(f' else {{ foreign import {clib_import} {{ "../sokol_dll_windows_x64_d3d11_release.lib"{windows_d3d11_libs} }} }}') + l( ' }') + l( ' } else {') + l( ' when USE_GL {') + l(f' when DEBUG {{ foreign import {clib_import} {{ "{clib_prefix}_windows_x64_gl_debug.lib"{windows_gl_libs} }} }}') + l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_windows_x64_gl_release.lib"{windows_gl_libs} }} }}') + l( ' } else {') + l(f' when DEBUG {{ foreign import {clib_import} {{ "{clib_prefix}_windows_x64_d3d11_debug.lib"{windows_d3d11_libs} }} }}') + l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_windows_x64_d3d11_release.lib"{windows_d3d11_libs} }} }}') + l( ' }') + l( ' }') + l( '} else when ODIN_OS == .Darwin {') + l( ' when USE_DLL {') + l(f' when USE_GL && ODIN_ARCH == .arm64 && DEBUG {{ foreign import {clib_import} {{ "../dylib/sokol_dylib_macos_arm64_gl_debug.dylib" }} }}') + l(f' else when USE_GL && ODIN_ARCH == .arm64 && !DEBUG {{ foreign import {clib_import} {{ "../dylib/sokol_dylib_macos_arm64_gl_release.dylib" }} }}') + l(f' else when USE_GL && ODIN_ARCH == .amd64 && DEBUG {{ foreign import {clib_import} {{ "../dylib/sokol_dylib_macos_x64_gl_debug.dylib" }} }}') + l(f' else when USE_GL && ODIN_ARCH == .amd64 && !DEBUG {{ foreign import {clib_import} {{ "../dylib/sokol_dylib_macos_x64_gl_release.dylib" }} }}') + l(f' else when !USE_GL && ODIN_ARCH == .arm64 && DEBUG {{ foreign import {clib_import} {{ "../dylib/sokol_dylib_macos_arm64_metal_debug.dylib" }} }}') + l(f' else when !USE_GL && ODIN_ARCH == .arm64 && !DEBUG {{ foreign import {clib_import} {{ "../dylib/sokol_dylib_macos_arm64_metal_release.dylib" }} }}') + l(f' else when !USE_GL && ODIN_ARCH == .amd64 && DEBUG {{ foreign import {clib_import} {{ "../dylib/sokol_dylib_macos_x64_metal_debug.dylib" }} }}') + l(f' else when !USE_GL && ODIN_ARCH == .amd64 && !DEBUG {{ foreign import {clib_import} {{ "../dylib/sokol_dylib_macos_x64_metal_release.dylib" }} }}') + l( ' } else {') + l( ' when USE_GL {') + l( ' when ODIN_ARCH == .arm64 {') + l(f' when DEBUG {{ foreign import {clib_import} {{ "{clib_prefix}_macos_arm64_gl_debug.a"{macos_gl_libs} }} }}') + l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_macos_arm64_gl_release.a"{macos_gl_libs} }} }}') + l( ' } else {') + l(f' when DEBUG {{ foreign import {clib_import} {{ "{clib_prefix}_macos_x64_gl_debug.a"{macos_gl_libs} }} }}') + l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_macos_x64_gl_release.a"{macos_gl_libs} }} }}') + l( ' }') + l( ' } else {') + l( ' when ODIN_ARCH == .arm64 {') + l(f' when DEBUG {{ foreign import {clib_import} {{ "{clib_prefix}_macos_arm64_metal_debug.a"{macos_metal_libs} }} }}') + l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_macos_arm64_metal_release.a"{macos_metal_libs} }} }}') + l( ' } else {') + l(f' when DEBUG {{ foreign import {clib_import} {{ "{clib_prefix}_macos_x64_metal_debug.a"{macos_metal_libs} }} }}') + l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_macos_x64_metal_release.a"{macos_metal_libs} }} }}') + l( ' }') + l( ' }') + l( ' }') + l( '} else when ODIN_OS == .Linux {') + l(f' when DEBUG {{ foreign import {clib_import} {{ "{clib_prefix}_linux_x64_gl_debug.a"{linux_gl_libs} }} }}') + l(f' else {{ foreign import {clib_import} {{ "{clib_prefix}_linux_x64_gl_release.a"{linux_gl_libs} }} }}') + l( '} else {') + l( ' #panic("This OS is currently not supported")') + l( '}') + l( '') + + # Need to special case sapp_sg to avoid Odin's context keyword + if c_prefix == "sapp_sg": + l(f'@(default_calling_convention="c")') + else: + l(f'@(default_calling_convention="c", link_prefix="{c_prefix}")') + l(f"foreign {clib_import} {{") + prefix = inp['prefix'] + for decl in inp['decls']: + if decl['kind'] == 'func' and not decl['is_dep'] and not check_ignore(decl['name']): + args = funcdecl_args_c(decl, prefix) + res_type = funcdecl_result_c(decl, prefix) + res_str = '' if res_type == '' else f'-> {res_type}' + # Need to special case sapp_sg to avoid Odin's context keyword + if c_prefix == "sapp_sg": + l(f' @(link_name="{decl["name"]}")') + l(f" {check_override(as_snake_case(decl['name'], c_prefix))} :: proc({args}) {res_str} ---") + else: + l(f" {as_snake_case(decl['name'], c_prefix)} :: proc({args}) {res_str} ---") + l('}') + l('') + +def gen_consts(decl, prefix): + for item in decl['items']: + item_name = check_override(item['name']) + l(f"{as_snake_case(item_name, prefix)} :: {item['value']}") + l('') + +def gen_struct(decl, prefix): + c_struct_name = check_override(decl['name']) + struct_name = as_struct_or_enum_type(c_struct_name, prefix) + l(f'{struct_name} :: struct {{') + for field in decl['fields']: + field_name = check_override(field['name']) + field_type = map_type(check_override(f'{c_struct_name}.{field_name}', default=field['type']), prefix, 'struct_field') + # any field name starting with _ is considered private + if field_name.startswith('_'): + l(f' _ : {field_type},') + else: + l(f' {field_name} : {field_type},') + l('}') + l('') + +def gen_enum(decl, prefix): + enum_name = check_override(decl['name']) + l(f'{as_struct_or_enum_type(enum_name, prefix)} :: enum i32 {{') + for item in decl['items']: + item_name = as_enum_item_name(check_override(item['name'])) + if item_name != 'FORCE_U32' and item_name != 'NUM': + if 'value' in item: + l(f" {item_name} = {item['value']},") + else: + l(f" {item_name},") + l('}') + l('') + +def gen_imports(dep_prefixes): + for dep_prefix in dep_prefixes: + dep_module_name = module_names[dep_prefix] + l(f'import {dep_prefix[:-1]} "../{dep_module_name}"') + l('') + +def gen_helpers(inp): + if inp['prefix'] == 'sdtx_': + l('import "core:fmt"') + l('import "core:strings"') + l('printf :: proc(s: string, args: ..any) {') + l(' fstr := fmt.tprintf(s, ..args)') + l(' putr(strings.unsafe_string_to_cstring(fstr), len(fstr))') + l('}') + +def gen_module(inp, c_prefix, dep_prefixes): + pre_parse(inp) + l('// machine generated, do not edit') + l('') + l(f"package sokol_{inp['module']}") + gen_imports(dep_prefixes) + gen_helpers(inp) + prefix = inp['prefix'] + gen_c_imports(inp, c_prefix, prefix) + for decl in inp['decls']: + if not decl['is_dep']: + kind = decl['kind'] + if kind == 'consts': + gen_consts(decl, prefix) + elif not check_ignore(decl['name']): + if kind == 'struct': + gen_struct(decl, prefix) + elif kind == 'enum': + gen_enum(decl, prefix) + +def pre_parse(inp): + global struct_types + global enum_types + for decl in inp['decls']: + kind = decl['kind'] + if kind == 'struct': + struct_types.append(decl['name']) + elif kind == 'enum': + enum_name = decl['name'] + enum_types.append(enum_name) + enum_items[enum_name] = [] + for item in decl['items']: + enum_items[enum_name].append(as_enum_item_name(item['name'])) + +def prepare(): + print('=== Generating Odin bindings:') + if not os.path.isdir(module_root): + os.makedirs(module_root) + if not os.path.isdir(c_root): + os.makedirs(c_root) + +def gen(c_header_path, c_prefix, dep_c_prefixes): + if not c_prefix in module_names: + print(f' >> warning: skipping generation for {c_prefix} prefix...') + return + reset_globals() + make_odin_module_directory(c_prefix) + print(f' {c_header_path} => {module_names[c_prefix]}') + shutil.copyfile(c_header_path, f'{c_root}/{os.path.basename(c_header_path)}') + csource_path = get_csource_path(c_prefix) + module_name = module_names[c_prefix] + ir = gen_ir.gen(c_header_path, csource_path, module_name, c_prefix, dep_c_prefixes) + gen_module(ir, c_prefix, dep_c_prefixes) + with open(f"{module_root}/{ir['module']}/{ir['module']}.odin", 'w', newline='\n') as f_outp: + f_outp.write(out_lines) diff --git a/thirdparty/sokol/bindgen/gen_rust.py b/thirdparty/sokol/bindgen/gen_rust.py new file mode 100644 index 0000000..59a2b67 --- /dev/null +++ b/thirdparty/sokol/bindgen/gen_rust.py @@ -0,0 +1,893 @@ +# ------------------------------------------------------------------------------- +# Generate Rust bindings. +# +# Rust coding style: +# - types are PascalCase +# - otherwise snake_case +# ------------------------------------------------------------------------------- +import gen_ir +import os, shutil, sys + +import gen_util as util + +module_names = { + "slog_": "log", + "sg_": "gfx", + "sapp_": "app", + "stm_": "time", + "saudio_": "audio", + "sgl_": "gl", + "sdtx_": "debugtext", + "sshape_": "shape", + "simgui_": "imgui", + "sglue_": "glue", +} + +module_requires_rust_feature = { + module_names["simgui_"]: "imgui", +} + +c_source_paths = { + "slog_": "sokol-rust/src/sokol/c/sokol_log.c", + "sg_": "sokol-rust/src/sokol/c/sokol_gfx.c", + "sapp_": "sokol-rust/src/sokol/c/sokol_app.c", + "stm_": "sokol-rust/src/sokol/c/sokol_time.c", + "saudio_": "sokol-rust/src/sokol/c/sokol_audio.c", + "sgl_": "sokol-rust/src/sokol/c/sokol_gl.c", + "sdtx_": "sokol-rust/src/sokol/c/sokol_debugtext.c", + "sshape_": "sokol-rust/src/sokol/c/sokol_shape.c", + "simgui_": "sokol-rust/src/sokol/c/sokol_imgui.c", + "sglue_": "sokol-rust/src/sokol/c/sokol_glue.c", +} + +ignores = [ + "sdtx_printf", + "sdtx_vprintf", + "simgui_add_key_event", + # "sg_install_trace_hooks", + # "sg_trace_hooks", +] + +range_struct_name = "Range" + +# functions that need to be exposed as 'raw' C callbacks without a Rust wrapper function +c_callbacks = ["slog_func"] + +# NOTE: syntax for function results: "func_name.RESULT" +overrides = { + "type": "_type", + "ref": "_ref", + + "sg_apply_uniforms.ub_index": "uintptr_t", + "sg_draw.base_element": "uintptr_t", + "sg_draw.num_elements": "uintptr_t", + "sg_draw.num_instances": "uintptr_t", + "sshape_element_range_t.base_element": "uintptr_t", + "sshape_element_range_t.num_elements": "uintptr_t", + "sdtx_font.font_index": "uintptr_t", + + "sdtx_move": "sdtx_move_cursor", + "sdtx_move_x": "sdtx_move_cursor_x", + "sdtx_move_y": "sdtx_move_cursor_y", + + "sg_image_type::SG_IMAGETYPE_2D": "SG_IMAGEYPE_DIM2", + "sg_image_type::SG_IMAGETYPE_3D": "SG_IMAGETYPE_DIM3", + + "sapp_keycode::SAPP_KEYCODE_0": "SAPP_KEYCODE_NUM0", + "sapp_keycode::SAPP_KEYCODE_1": "SAPP_KEYCODE_NUM1", + "sapp_keycode::SAPP_KEYCODE_2": "SAPP_KEYCODE_NUM2", + "sapp_keycode::SAPP_KEYCODE_3": "SAPP_KEYCODE_NUM3", + "sapp_keycode::SAPP_KEYCODE_4": "SAPP_KEYCODE_NUM4", + "sapp_keycode::SAPP_KEYCODE_5": "SAPP_KEYCODE_NUM5", + "sapp_keycode::SAPP_KEYCODE_6": "SAPP_KEYCODE_NUM6", + "sapp_keycode::SAPP_KEYCODE_7": "SAPP_KEYCODE_NUM7", + "sapp_keycode::SAPP_KEYCODE_8": "SAPP_KEYCODE_NUM8", + "sapp_keycode::SAPP_KEYCODE_9": "SAPP_KEYCODE_NUM9", + + # "sgl_error": "sgl_get_error", # 'error' is reserved in zig + # "sgl_deg": "sgl_as_degrees", + # "sgl_rad": "sgl_as_radians", + # "sg_context_desc.color_format": "int", + # "SGL_NO_ERROR": "SGL_ERROR_NO_ERROR", + # "sg_context_desc.depth_format": "int", +} + +prim_types = { + "int": "i32", + "bool": "bool", + "char": "core::ffi::c_char", + "int8_t": "i8", + "uint8_t": "u8", + "int16_t": "i16", + "uint16_t": "u16", + "int32_t": "i32", + "uint32_t": "u32", + "int64_t": "i64", + "uint64_t": "u64", + "float": "f32", + "double": "f64", + "uintptr_t": "usize", + "intptr_t": "isize", + "size_t": "usize", +} + +prim_defaults = { + "int": "0", + "bool": "false", + "int8_t": "0", + "uint8_t": "0", + "int16_t": "0", + "uint16_t": "0", + "int32_t": "0", + "uint32_t": "0", + "int64_t": "0", + "uint64_t": "0", + "float": "0.0", + "double": "0.0", + "uintptr_t": "0", + "intptr_t": "0", + "size_t": "0", + "char": "0", +} + +special_constant_types = { + "SG_INVALID_ID": "u32", + "SAPP_MODIFIER_SHIFT": "u32", + "SAPP_MODIFIER_CTRL": "u32", + "SAPP_MODIFIER_ALT": "u32", + "SAPP_MODIFIER_SUPER": "u32", + "SAPP_MODIFIER_LMB": "u32", + "SAPP_MODIFIER_RMB": "u32", + "SAPP_MODIFIER_MMB": "u32", +} + +struct_types = [] +enum_types = [] +enum_items = {} +out_lines = "" + + +def reset_globals(): + global struct_types + global enum_types + global enum_items + global out_lines + struct_types = [] + enum_types = [] + enum_items = {} + out_lines = "" + + +def l(s): + global out_lines + out_lines += s + "\n" + + +def as_rust_prim_type(s): + return prim_types[s] + + +def as_upper_snake_case(s, prefix): + outp = s.lower() + if outp.startswith(prefix): + outp = outp[len(prefix):] + return outp.upper() + + +# prefix_bla_blub(_t) => (dep::)BlaBlub +def as_rust_struct_type(s, prefix): + parts = s.lower().split("_") + outp = "" if s.startswith(prefix) else f"{parts[0]}::" + for part in parts[1:]: + # ignore '_t' type postfix + if part != "t": + outp += part.capitalize() + return outp + + +# prefix_bla_blub(_t) => (dep::)BlaBlub +def as_rust_enum_type(s, prefix): + parts = s.lower().split("_") + outp = "" if s.startswith(prefix) else f"{parts[0]}::" + for part in parts[1:]: + # ignore '_t' type postfix + if part != "t": + outp += part.capitalize() + return outp + + +def check_override(name, default=None): + if name in overrides: + return overrides[name] + elif default is None: + return name + else: + return default + + +def check_ignore(name): + return name in ignores + + +# PREFIX_ENUM_BLA_BLA => BlaBla, _PREFIX_ENUM_BLA_BLA => BlaBla +def as_enum_item_name(s): + parts = s.lstrip("_").split("_") + outp = "" + for i, part in enumerate(parts[2:]): + # TODO: What to do with enum fields starting with numbers? + outp += part.capitalize() + + return outp + + +def enum_default_item(enum_name): + return enum_items[enum_name][0] + + +def is_prim_type(s): + return s in prim_types + + +def is_struct_type(s): + return s in struct_types + + +def is_enum_type(s): + return s in enum_types + + +def is_const_prim_ptr(s): + for prim_type in prim_types: + if s == f"const {prim_type} *": + return True + return False + + +def is_prim_ptr(s): + for prim_type in prim_types: + if s == f"{prim_type} *": + return True + return False + + +def is_const_struct_ptr(s): + for struct_type in struct_types: + if s == f"const {struct_type} *": + return True + return False + + +def is_struct_ptr(s): + for struct_type in struct_types: + if s == f"{struct_type} *": + return True + return False + + +def type_default_value(s): + return prim_defaults[s] + + +def as_c_arg_type(arg_prefix, arg_type, prefix): + # NOTE: if arg_prefix is None, the result is used as return value + pre = "" if arg_prefix is None else arg_prefix + + if arg_type == "void": + return "" + elif is_prim_type(arg_type): + return pre + as_rust_prim_type(arg_type) + elif is_struct_type(arg_type): + return pre + as_rust_struct_type(arg_type, prefix) + elif is_enum_type(arg_type): + return pre + as_rust_enum_type(arg_type, prefix) + elif util.is_void_ptr(arg_type): + return pre + "*mut core::ffi::c_void" + elif util.is_const_void_ptr(arg_type): + return pre + "*const core::ffi::c_void" + elif util.is_string_ptr(arg_type): + return pre + "*const core::ffi::c_char" + elif is_const_struct_ptr(arg_type): + return pre + f"*const {as_rust_struct_type(util.extract_ptr_type(arg_type), prefix)}" + elif is_struct_ptr(arg_type): + return pre + f"*mut {as_rust_struct_type(util.extract_ptr_type(arg_type), prefix)}" + elif is_prim_ptr(arg_type): + return pre + f"*mut {as_rust_prim_type(util.extract_ptr_type(arg_type))}" + elif is_const_prim_ptr(arg_type): + return pre + f"*const {as_rust_prim_type(util.extract_ptr_type(arg_type))}" + else: + sys.exit(f"ERROR as_c_arg_type(): {arg_type}") + + +def as_rust_arg_type(arg_prefix, arg_type, prefix): + # NOTE: if arg_prefix is None, the result is used as return value + pre = "" if arg_prefix is None else arg_prefix + + if arg_type == "void": + return "" + elif is_prim_type(arg_type): + return pre + as_rust_prim_type(arg_type) + elif is_struct_type(arg_type): + return pre + as_rust_struct_type(arg_type, prefix) + elif is_enum_type(arg_type): + return pre + as_rust_enum_type(arg_type, prefix) + elif util.is_void_ptr(arg_type): + return pre + "*mut core::ffi::c_void" + elif util.is_const_void_ptr(arg_type): + return pre + "*const core::ffi::c_void" + elif util.is_string_ptr(arg_type): + return pre + "&str" + elif is_const_struct_ptr(arg_type): + return pre + f"&{as_rust_struct_type(util.extract_ptr_type(arg_type), prefix)}" + elif is_struct_ptr(arg_type): + return pre + f"&mut {as_rust_struct_type(util.extract_ptr_type(arg_type), prefix)}" + elif is_prim_ptr(arg_type): + return pre + f"&mut {as_rust_prim_type(util.extract_ptr_type(arg_type))}" + elif is_const_prim_ptr(arg_type): + return pre + f"&{as_rust_prim_type(util.extract_ptr_type(arg_type))}" + else: + sys.exit(f"ERROR as_rust_arg_type(): {arg_type}") + + +def is_rust_string(rust_type): + return rust_type == "&str" or rust_type == " -> &'static str" + + +# get C-style arguments of a function pointer as string +def funcptr_args_c(field_type, prefix): + tokens = field_type[field_type.index("(*)") + 4: -1].split(",") + s = "" + for token in tokens: + arg_type = token.strip() + if s != "": + s += ", " + c_arg = as_c_arg_type(None, arg_type, prefix) + if c_arg == "void": + return "" + else: + s += c_arg + return s + + +# get C-style result of a function pointer as string +def funcptr_result_c(field_type): + res_type = field_type[: field_type.index("(*)")].strip() + if res_type == "void": + return "" + elif is_prim_type(res_type): + return f" -> {as_rust_prim_type(res_type)}" + elif util.is_const_void_ptr(res_type): + return " -> *const core::ffi::c_void" + elif util.is_void_ptr(res_type): + return " -> *mut core::ffi::c_void" + else: + sys.exit(f"ERROR funcptr_result_c(): {field_type}") + + +def funcdecl_args_c(decl, prefix): + s = "" + func_name = decl["name"] + for param_decl in decl["params"]: + if s != "": + s += ", " + param_name = param_decl["name"] + param_type = check_override( + f"{func_name}.{param_name}", default=param_decl["type"] + ) + s += f"{as_c_arg_type(f'{param_name}: ', param_type, prefix)}" + return s + + +def funcdecl_args_rust(decl, prefix): + s = "" + func_name = decl["name"] + for param_decl in decl["params"]: + if s != "": + s += ", " + param_name = param_decl["name"] + param_type = check_override( + f"{func_name}.{param_name}", default=param_decl["type"] + ) + s += f"{as_rust_arg_type(f'{param_name}: ', param_type, prefix)}" + return s + + +def funcdecl_result_c(decl, prefix): + func_name = decl["name"] + decl_type = decl["type"] + result_type = check_override( + f"{func_name}.RESULT", default=decl_type[: decl_type.index("(")].strip() + ) + + it = as_c_arg_type(None, result_type, prefix) + if it == "()" or it == "": + return "" + else: + return f" -> {it}" + + +def funcdecl_result_rust(decl, prefix): + func_name = decl["name"] + decl_type = decl["type"] + result_type = check_override( + f"{func_name}.RESULT", default=decl_type[: decl_type.index("(")].strip() + ) + rust_res_type = as_rust_arg_type(None, result_type, prefix) + + if is_rust_string(rust_res_type): + rust_res_type = "&'static str" + + if rust_res_type == "": + return "" + else: + return f" -> {rust_res_type }" + + +def gen_struct(decl, prefix): + struct_name = check_override(decl["name"]) + rust_type = as_rust_struct_type(struct_name, prefix) + rust_struct_type = rust_type + + struct_lines = [] + default_lines = [] + + for field in decl["fields"]: + field_name = check_override(field["name"]) + field_type = check_override( + f"{struct_name}.{field_name}", default=field["type"] + ) + + if is_prim_type(field_type): + struct_lines.append( + f"pub {field_name}: {as_rust_prim_type(field_type)}" + ) + default_lines.append( + f"{field_name}: {type_default_value(field_type)}" + ) + elif is_struct_type(field_type): + struct_lines.append( + f"pub {field_name}: {as_rust_struct_type(field_type, prefix)}" + ) + default_lines.append( + f"{field_name}: {as_rust_struct_type(field_type, prefix)}::new()" + ) + elif is_enum_type(field_type): + struct_lines.append( + f"pub {field_name}: {as_rust_enum_type(field_type, prefix)}" + ) + default_lines.append( + f"{field_name}: {as_rust_enum_type(field_type, prefix)}::new()" + ) + elif util.is_string_ptr(field_type): + struct_lines.append( + f"pub {field_name}: *const core::ffi::c_char" + ) + default_lines.append( + f"{field_name}: core::ptr::null()" + ) + elif util.is_const_void_ptr(field_type): + struct_lines.append( + f"pub {field_name}: *const core::ffi::c_void" + ) + default_lines.append( + f"{field_name}: core::ptr::null()" + ) + elif util.is_void_ptr(field_type): + struct_lines.append( + f"pub {field_name}: *mut core::ffi::c_void" + ) + default_lines.append( + f"{field_name}: core::ptr::null_mut()" + ) + elif is_const_prim_ptr(field_type): + struct_lines.append( + f"pub {field_name}: *const {as_rust_prim_type(util.extract_ptr_type(field_type))}" + ) + default_lines.append( + f"{field_name}: core::ptr::null()" + ) + elif is_prim_ptr(field_type): + struct_lines.append( + f"pub {field_name}: *mut {as_rust_prim_type(util.extract_ptr_type(field_type))}" + ) + default_lines.append( + f"{field_name}: core::ptr::null_mut()" + ) + elif is_const_struct_ptr(field_type): + struct_lines.append( + f"pub {field_name}: *const {as_rust_struct_type(util.extract_ptr_type(field_type), prefix)}" + ) + default_lines.append( + f"{field_name}: core::ptr::null()" + ) + elif is_struct_ptr(field_type): + struct_lines.append( + f"pub {field_name}: *mut {as_rust_struct_type(util.extract_ptr_type(field_type), prefix)}" + ) + default_lines.append( + f"{field_name}: core::ptr::null_mut()" + ) + elif util.is_func_ptr(field_type): + struct_lines.append( + f"pub {field_name}: Option" + ) + default_lines.append( + f"{field_name}: None" + ) + elif util.is_1d_array_type(field_type): + array_type = util.extract_array_type(field_type) + array_sizes = util.extract_array_sizes(field_type) + if is_prim_type(array_type) or is_struct_type(array_type): + if is_prim_type(array_type): + rust_type = as_rust_prim_type(array_type) + def_val = type_default_value(array_type) + elif is_struct_type(array_type) or is_enum_type(array_type): + rust_type = as_rust_struct_type(array_type, prefix) + def_val = f"{rust_type}::new()" + else: + sys.exit(f"ERROR gen_struct is_1d_array_type: {array_type}") + t0 = f"[{rust_type}; {array_sizes[0]}]" + # t1 = f"&{rust_type}" + struct_lines.append( + f"pub {field_name}: {t0}" + ) + default_lines.append( + f"{field_name}: [{def_val}; {array_sizes[0]}]" + ) + elif util.is_const_void_ptr(array_type): + struct_lines.append( + f"pub {field_name}: [*const core::ffi::c_void; {array_sizes[0]}]" + ) + default_lines.append( + f"{field_name}: [core::ptr::null(); {array_sizes[0]}]" + ) + else: + sys.exit( + f"ERROR gen_struct: array {field_name}: {field_type} => [{array_type}: {array_sizes[0]}]" + ) + elif util.is_2d_array_type(field_type): + array_type = util.extract_array_type(field_type) + array_sizes = util.extract_array_sizes(field_type) + + if is_prim_type(array_type): + rust_type = as_rust_prim_type(array_type) + def_val = type_default_value(array_type) + elif is_struct_type(array_type): + rust_type = as_rust_struct_type(array_type, prefix) + def_val = f"{rust_type}::new()" + else: + sys.exit(f"ERROR gen_struct is_2d_array_type: {array_type}") + + struct_lines.append( + f"pub {field_name}: [[{rust_type}; {array_sizes[1]}]; {array_sizes[0]}]" + ) + default_lines.append( + f"{field_name}: [[{def_val}; {array_sizes[1]}]; {array_sizes[0]}]" + ) + else: + sys.exit(f"ERROR gen_struct: {field_name}: {field_type};") + + # + # TODO: Is this the best way to have zero-initialization with support for constants? + # core::mem::zeroed() cleaner? + # + + l("#[repr(C)]") + l("#[derive(Copy, Clone, Debug)]") + l(f"pub struct {rust_struct_type} {{") + for line in struct_lines: + l(f" {line},") + l("}") + + l(f"impl {rust_struct_type} {{") + l(" pub const fn new() -> Self {") + l(" Self {") + for line in default_lines: + l(f" {line},") + l(" }") + l(" }") + l("}") + + l(f"impl Default for {rust_struct_type} {{") + l(" fn default() -> Self {") + l(" Self::new()") + l(" }") + l("}") + + +def gen_consts(decl, prefix): + for item in decl["items"]: + # + # TODO: What type should these constants have? Currently giving all `usize` + # unless specifically overridden by `special_constant_types` + # + + item_name = check_override(item["name"]) + if item_name in special_constant_types: + special_type = special_constant_types[item_name] + l(f"pub const {as_upper_snake_case(item_name, prefix)}: {special_type} = {item['value']};") + else: + l(f"pub const {as_upper_snake_case(item_name, prefix)}: usize = {item['value']};") + + +def gen_enum(decl, prefix): + enum_name = check_override(decl["name"]) + + names = [ + as_enum_item_name(check_override(f"{decl['name']}::{item['name']}", item['name'])) for item in decl["items"] + ] + + is_u32 = False + for name in names: + if name == "ForceU32": + is_u32 = True + break + + l("#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]") + if is_u32: + l("#[repr(u32)]") + else: + l("#[repr(i32)]") + + rust_enum_name = as_rust_enum_type(enum_name, prefix) + + l(f"pub enum {rust_enum_name} {{") + for item_name, item in zip(names, decl["items"]): + if item_name != "ForceU32": + if "value" in item: + l(f" {item_name} = {item['value']},") + else: + l(f" {item_name},") + l("}") + + default_item = enum_default_item(enum_name) + l(f"impl {rust_enum_name} {{") + l(" pub const fn new() -> Self {") + l(f" Self::{default_item}") + l(" }") + l("}") + + l(f"impl Default for {rust_enum_name} {{") + l(" fn default() -> Self {") + l(f" Self::{default_item}") + l(" }") + l("}") + + +def gen_func_c(decl, prefix): + l("pub extern \"C\" {") + l(f" fn {decl['name']}({funcdecl_args_c(decl, prefix)}){funcdecl_result_c(decl, prefix)};") + l("}") + + +def gen_c_funcs(funcs): + l("pub mod ffi {") + l(" #![allow(unused_imports)]") + l(" use super::*;") + l(" extern \"C\" {") + for decl, prefix in funcs: + l(f" pub fn {decl['name']}({funcdecl_args_c(decl, prefix)}){funcdecl_result_c(decl, prefix)};") + l(" }") + l("}") + + +def gen_rust_funcs(funcs): + for decl, prefix in funcs: + gen_func_rust(decl, prefix) + + +def gen_func_rust(decl, prefix): + c_func_name = decl["name"] + rust_func_name = util.as_lower_snake_case(check_override(decl["name"]), prefix) + rust_res_type = funcdecl_result_rust(decl, prefix) + + if c_func_name in c_callbacks: + c_res_type = funcdecl_result_c(decl, prefix) + l("#[inline]") + l(f'pub extern "C" fn {c_func_name}({funcdecl_args_c(decl, prefix)}){c_res_type} {{') + l(" unsafe {") + s = f" ffi::{c_func_name}(" + for i, param_decl in enumerate(decl["params"]): + if i > 0: + s += ", " + arg_name = param_decl["name"] + s += arg_name + s += ")" + l(s) + l(" }") + l("}") + else: + l("#[inline]") + l(f"pub fn {rust_func_name}({funcdecl_args_rust(decl, prefix)}){rust_res_type} {{") + for i, param_decl in enumerate(decl["params"]): + arg_name = param_decl["name"] + arg_type = param_decl["type"] + if util.is_string_ptr(arg_type): + l(f" let tmp_{i} = std::ffi::CString::new({arg_name}).unwrap();") + + l(" unsafe {") + if is_rust_string(rust_res_type): + # special case: convert C string to rust string slice + s = f" c_char_ptr_to_rust_str(ffi::{c_func_name}(" + else: + s = f" ffi::{c_func_name}(" + + for i, param_decl in enumerate(decl["params"]): + if i > 0: + s += ", " + arg_name = param_decl["name"] + arg_type = param_decl["type"] + + if util.is_string_ptr(arg_type): + s += f"tmp_{i}.as_ptr()" + else: + s += arg_name + + if is_rust_string(rust_res_type): + s += ")" + s += ")" + l(s) + l(" }") + l("}") + + +def pre_parse(inp): + global struct_types + global enum_types + for decl in inp["decls"]: + kind = decl["kind"] + if kind == "struct": + struct_types.append(decl["name"]) + elif kind == "enum": + enum_name = decl["name"] + enum_types.append(enum_name) + enum_items[enum_name] = [] + for item in decl["items"]: + enum_items[enum_name].append(as_enum_item_name(item["name"])) + +def gen_imports(inp, dep_prefixes): + for dep_prefix in dep_prefixes: + dep_module_name = module_names[dep_prefix] + # l(f'const {dep_prefix[:-1]} = @import("{dep_module_name}.rs");') + l(f'use crate::{dep_module_name} as {dep_prefix[:-1]};') + l("") + + +def gen_helpers(inp): + l("/// Helper function to convert a C string to a Rust string slice") + l("#[inline]") + l("fn c_char_ptr_to_rust_str(c_char_ptr: *const core::ffi::c_char) -> &'static str {") + l(" let c_str = unsafe { core::ffi::CStr::from_ptr(c_char_ptr) };") + l(" c_str.to_str().expect(\"c_char_ptr contained invalid Utf8 Data\")") + l("}") + l("") + + if inp['prefix'] in ['sg_', 'sdtx_', 'sshape_', 'sapp_']: + l("/// Helper function to cast a Rust slice into a sokol Range") + l(f"pub fn slice_as_range(data: &[T]) -> {range_struct_name} {{") + l(f" {range_struct_name} {{ size: std::mem::size_of_val(data), ptr: data.as_ptr() as *const _ }}") + l("}") + l("/// Helper function to cast a Rust reference into a sokol Range") + l(f"pub fn value_as_range(value: &T) -> {range_struct_name} {{") + l(f" {range_struct_name} {{ size: std::mem::size_of::(), ptr: value as *const T as *const _ }}") + l("}") + l("") + l(f"impl From<&[T]> for {range_struct_name} {{") + l(" #[inline]") + l(" fn from(data: &[T]) -> Self {") + l(" slice_as_range(data)") + l(" }") + l("}") + l(f"impl From<&T> for {range_struct_name} {{") + l(" #[inline]") + l(" fn from(value: &T) -> Self {") + l(" value_as_range(value)") + l(" }") + l("}") + l("") + + # if inp["prefix"] == "sdtx_": + # l("/// std.fmt compatible Writer") + # l("pub const Writer = struct {") + # l(" pub const Error = error { };") + # l(" pub fn writeAll(self: Writer, bytes: []const u8) Error!void {") + # l(" _ = self;") + # l(" for (bytes) |byte| {") + # l(" putc(byte);") + # l(" }") + # l(" }") + # l(" pub fn writeByteNTimes(self: Writer, byte: u8, n: u64) Error!void {") + # l(" _ = self;") + # l(" var i: u64 = 0;") + # l(" while (i < n): (i += 1) {") + # l(" putc(byte);") + # l(" }") + # l(" }") + # l("};") + # l("// std.fmt-style formatted print") + # l("pub fn print(comptime fmt: anytype, args: anytype) void {") + # l(" var writer: Writer = .{};") + # l(' @import("std").fmt.format(writer, fmt, args) catch {};') + # l("}") + # l("") + + +def gen_module(inp, dep_prefixes): + module = inp['module'] + if module in module_requires_rust_feature: + feature = module_requires_rust_feature[module] + l(f"//! To use this module, enable the feature \"{feature}\"") + + l("// machine generated, do not edit") + l("") + + + l("#![allow(dead_code)]") + l("#![allow(unused_imports)]") + l("") + gen_imports(inp, dep_prefixes) + gen_helpers(inp) + pre_parse(inp) + prefix = inp["prefix"] + + funcs = [] + + for decl in inp["decls"]: + # + # HACK: gen_ir.py accidentally marks all sg_imgui_ declarations as is_dep since sg_imgui + # depends on sg_a but also starts with sg_... Fix gen_ir.py to remove this hack + # + dep_hack = False + if module == "gfx_imgui": + dep_hack = "name" in decl and decl["name"].startswith("sg_imgui_") + + if not decl["is_dep"] or dep_hack: + kind = decl["kind"] + if kind == "consts": + gen_consts(decl, prefix) + elif not check_ignore(decl["name"]): + if kind == "struct": + gen_struct(decl, prefix) + elif kind == "enum": + gen_enum(decl, prefix) + elif kind == "func": + funcs.append((decl, prefix)) + + gen_c_funcs(funcs) + gen_rust_funcs(funcs) + + +def prepare(): + print("=== Generating Rust bindings:") + if not os.path.isdir("sokol-rust/src/sokol"): + os.makedirs("sokol-rust/src/sokol") + if not os.path.isdir("sokol-rust/src/sokol/c"): + os.makedirs("sokol-rust/src/sokol/c") + + with open("sokol-rust/src/lib.rs", "w", newline="\n") as f_outp: + f_outp.write("//! Automatically generated sokol bindings for Rust\n\n") + + +def gen(c_header_path, c_prefix, dep_c_prefixes): + if c_prefix not in module_names: + print(f' >> warning: skipping generation for {c_prefix} prefix...') + return + + module_name = module_names[c_prefix] + c_source_path = c_source_paths[c_prefix] + print(f' {c_header_path} => {module_name}') + reset_globals() + c_path_in_project = f'sokol-rust/src/sokol/c/{os.path.basename(c_header_path)}' + shutil.copyfile(c_header_path, c_path_in_project) + ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes) + gen_module(ir, dep_c_prefixes) + output_path = f"sokol-rust/src/{ir['module']}.rs" + with open(output_path, 'w', newline='\n') as f_outp: + f_outp.write(out_lines) + + with open("sokol-rust/src/lib.rs", "a", newline="\n") as f_outp: + module = ir['module'] + if module in module_requires_rust_feature: + feature = module_requires_rust_feature[module] + f_outp.write(f"/// Enable feature \"{feature}\" to use\n") + f_outp.write(f"#[cfg(feature=\"{feature}\")]\n") + f_outp.write(f"pub mod {module};\n") diff --git a/thirdparty/sokol/bindgen/gen_util.py b/thirdparty/sokol/bindgen/gen_util.py new file mode 100644 index 0000000..a76f4b2 --- /dev/null +++ b/thirdparty/sokol/bindgen/gen_util.py @@ -0,0 +1,57 @@ +# common utility functions for all bindings generators +import re + +re_1d_array = re.compile("^(?:const )?\w*\s*\*?\[\d*\]$") +re_2d_array = re.compile("^(?:const )?\w*\s*\*?\[\d*\]\[\d*\]$") + +def is_1d_array_type(s): + return re_1d_array.match(s) is not None + +def is_2d_array_type(s): + return re_2d_array.match(s) is not None + +def is_array_type(s): + return is_1d_array_type(s) or is_2d_array_type(s) + +def extract_array_type(s): + return s[:s.index('[')].strip() + +def extract_array_sizes(s): + return s[s.index('['):].replace('[', ' ').replace(']', ' ').split() + +def is_string_ptr(s): + return s == "const char *" + +def is_const_void_ptr(s): + return s == "const void *" + +def is_void_ptr(s): + return s == "void *" + +def is_func_ptr(s): + return '(*)' in s + +def extract_ptr_type(s): + tokens = s.split() + if tokens[0] == 'const': + return tokens[1] + else: + return tokens[0] + +# PREFIX_BLA_BLUB to bla_blub +def as_lower_snake_case(s, prefix): + outp = s.lower() + if outp.startswith(prefix): + outp = outp[len(prefix):] + return outp + +# prefix_bla_blub => blaBlub, PREFIX_BLA_BLUB => blaBlub +def as_lower_camel_case(s, prefix): + outp = s.lower() + if outp.startswith(prefix): + outp = outp[len(prefix):] + parts = outp.split('_') + outp = parts[0] + for part in parts[1:]: + outp += part.capitalize() + return outp diff --git a/thirdparty/sokol/bindgen/gen_zig.py b/thirdparty/sokol/bindgen/gen_zig.py new file mode 100644 index 0000000..4b63eeb --- /dev/null +++ b/thirdparty/sokol/bindgen/gen_zig.py @@ -0,0 +1,558 @@ +#------------------------------------------------------------------------------- +# Generate Zig bindings. +# +# Zig coding style: +# - types are PascalCase +# - functions are camelCase +# - otherwise snake_case +#------------------------------------------------------------------------------- +import gen_ir +import os, shutil, sys + +import gen_util as util + +module_names = { + 'slog_': 'log', + 'sg_': 'gfx', + 'sapp_': 'app', + 'stm_': 'time', + 'saudio_': 'audio', + 'sgl_': 'gl', + 'sdtx_': 'debugtext', + 'sshape_': 'shape', + 'sglue_': 'glue', + 'sfetch_': 'fetch', + 'simgui_': 'imgui', +} + +c_source_paths = { + 'slog_': 'sokol-zig/src/sokol/c/sokol_log.c', + 'sg_': 'sokol-zig/src/sokol/c/sokol_gfx.c', + 'sapp_': 'sokol-zig/src/sokol/c/sokol_app.c', + 'stm_': 'sokol-zig/src/sokol/c/sokol_time.c', + 'saudio_': 'sokol-zig/src/sokol/c/sokol_audio.c', + 'sgl_': 'sokol-zig/src/sokol/c/sokol_gl.c', + 'sdtx_': 'sokol-zig/src/sokol/c/sokol_debugtext.c', + 'sshape_': 'sokol-zig/src/sokol/c/sokol_shape.c', + 'sglue_': 'sokol-zig/src/sokol/c/sokol_glue.c', + 'sfetch_': 'sokol-zig/src/sokol/c/sokol_fetch.c', + 'simgui_': 'sokol-zig/src/sokol/c/sokol_imgui.c', +} + +ignores = [ + 'sdtx_printf', + 'sdtx_vprintf', + 'sg_install_trace_hooks', + 'sg_trace_hooks', +] + +# functions that need to be exposed as 'raw' C callbacks without a Zig wrapper function +c_callbacks = [ + 'slog_func' +] + +# NOTE: syntax for function results: "func_name.RESULT" +overrides = { + 'sgl_error': 'sgl_get_error', # 'error' is reserved in Zig + 'sgl_deg': 'sgl_as_degrees', + 'sgl_rad': 'sgl_as_radians', + 'sg_apply_uniforms.ub_index': 'uint32_t', + 'sg_draw.base_element': 'uint32_t', + 'sg_draw.num_elements': 'uint32_t', + 'sg_draw.num_instances': 'uint32_t', + 'sshape_element_range_t.base_element': 'uint32_t', + 'sshape_element_range_t.num_elements': 'uint32_t', + 'sdtx_font.font_index': 'uint32_t', + 'SGL_NO_ERROR': 'SGL_ERROR_NO_ERROR', + 'sfetch_continue': 'continue_fetching', # 'continue' is reserved in Zig + 'sfetch_desc': 'sfetch_get_desc' # 'desc' shadowed by earlier definiton +} + +prim_types = { + 'int': 'i32', + 'bool': 'bool', + 'char': 'u8', + 'int8_t': 'i8', + 'uint8_t': 'u8', + 'int16_t': 'i16', + 'uint16_t': 'u16', + 'int32_t': 'i32', + 'uint32_t': 'u32', + 'int64_t': 'i64', + 'uint64_t': 'u64', + 'float': 'f32', + 'double': 'f64', + 'uintptr_t': 'usize', + 'intptr_t': 'isize', + 'size_t': 'usize' +} + +prim_defaults = { + 'int': '0', + 'bool': 'false', + 'int8_t': '0', + 'uint8_t': '0', + 'int16_t': '0', + 'uint16_t': '0', + 'int32_t': '0', + 'uint32_t': '0', + 'int64_t': '0', + 'uint64_t': '0', + 'float': '0.0', + 'double': '0.0', + 'uintptr_t': '0', + 'intptr_t': '0', + 'size_t': '0' +} + + +struct_types = [] +enum_types = [] +enum_items = {} +out_lines = '' + +def reset_globals(): + global struct_types + global enum_types + global enum_items + global out_lines + struct_types = [] + enum_types = [] + enum_items = {} + out_lines = '' + +def l(s): + global out_lines + out_lines += s + '\n' + +def as_zig_prim_type(s): + return prim_types[s] + +# prefix_bla_blub(_t) => (dep.)BlaBlub +def as_zig_struct_type(s, prefix): + parts = s.lower().split('_') + outp = '' if s.startswith(prefix) else f'{parts[0]}.' + for part in parts[1:]: + # ignore '_t' type postfix + if (part != 't'): + outp += part.capitalize() + return outp + +# prefix_bla_blub(_t) => (dep.)BlaBlub +def as_zig_enum_type(s, prefix): + parts = s.lower().split('_') + outp = '' if s.startswith(prefix) else f'{parts[0]}.' + for part in parts[1:]: + if (part != 't'): + outp += part.capitalize() + return outp + +def check_override(name, default=None): + if name in overrides: + return overrides[name] + elif default is None: + return name + else: + return default + +def check_ignore(name): + return name in ignores + +# PREFIX_ENUM_BLA => Bla, _PREFIX_ENUM_BLA => Bla +def as_enum_item_name(s): + outp = s.lstrip('_') + parts = outp.split('_')[2:] + outp = '_'.join(parts) + if outp[0].isdigit(): + outp = '_' + outp + return outp + +def enum_default_item(enum_name): + return enum_items[enum_name][0] + +def is_prim_type(s): + return s in prim_types + +def is_struct_type(s): + return s in struct_types + +def is_enum_type(s): + return s in enum_types + +def is_const_prim_ptr(s): + for prim_type in prim_types: + if s == f"const {prim_type} *": + return True + return False + +def is_prim_ptr(s): + for prim_type in prim_types: + if s == f"{prim_type} *": + return True + return False + +def is_const_struct_ptr(s): + for struct_type in struct_types: + if s == f"const {struct_type} *": + return True + return False + +def type_default_value(s): + return prim_defaults[s] + +def as_c_arg_type(arg_type, prefix): + if arg_type == "void": + return "void" + elif is_prim_type(arg_type): + return as_zig_prim_type(arg_type) + elif is_struct_type(arg_type): + return as_zig_struct_type(arg_type, prefix) + elif is_enum_type(arg_type): + return as_zig_enum_type(arg_type, prefix) + elif util.is_void_ptr(arg_type): + return "?*anyopaque" + elif util.is_const_void_ptr(arg_type): + return "?*const anyopaque" + elif util.is_string_ptr(arg_type): + return "[*c]const u8" + elif is_const_struct_ptr(arg_type): + return f"[*c]const {as_zig_struct_type(util.extract_ptr_type(arg_type), prefix)}" + elif is_prim_ptr(arg_type): + return f"[*c]{as_zig_prim_type(util.extract_ptr_type(arg_type))}" + elif is_const_prim_ptr(arg_type): + return f"[*c]const {as_zig_prim_type(util.extract_ptr_type(arg_type))}" + else: + sys.exit(f"Error as_c_arg_type(): {arg_type}") + +def as_zig_arg_type(arg_prefix, arg_type, prefix): + # NOTE: if arg_prefix is None, the result is used as return value + pre = "" if arg_prefix is None else arg_prefix + if arg_type == "void": + if arg_prefix is None: + return "void" + else: + return "" + elif is_prim_type(arg_type): + return pre + as_zig_prim_type(arg_type) + elif is_struct_type(arg_type): + return pre + as_zig_struct_type(arg_type, prefix) + elif is_enum_type(arg_type): + return pre + as_zig_enum_type(arg_type, prefix) + elif util.is_void_ptr(arg_type): + return pre + "?*anyopaque" + elif util.is_const_void_ptr(arg_type): + return pre + "?*const anyopaque" + elif util.is_string_ptr(arg_type): + return pre + "[:0]const u8" + elif is_const_struct_ptr(arg_type): + # not a bug, pass const structs by value + return pre + f"{as_zig_struct_type(util.extract_ptr_type(arg_type), prefix)}" + elif is_prim_ptr(arg_type): + return pre + f"*{as_zig_prim_type(util.extract_ptr_type(arg_type))}" + elif is_const_prim_ptr(arg_type): + return pre + f"*const {as_zig_prim_type(util.extract_ptr_type(arg_type))}" + else: + sys.exit(f"ERROR as_zig_arg_type(): {arg_type}") + +def is_zig_string(zig_type): + return zig_type == "[:0]const u8" + +# get C-style arguments of a function pointer as string +def funcptr_args_c(field_type, prefix): + tokens = field_type[field_type.index('(*)')+4:-1].split(',') + s = "" + for token in tokens: + arg_type = token.strip() + if s != "": + s += ", " + c_arg = as_c_arg_type(arg_type, prefix) + if c_arg == "void": + return "" + else: + s += c_arg + return s + +# get C-style result of a function pointer as string +def funcptr_result_c(field_type): + res_type = field_type[:field_type.index('(*)')].strip() + if res_type == 'void': + return 'void' + elif is_prim_type(res_type): + return as_zig_prim_type(res_type) + elif util.is_const_void_ptr(res_type): + return '?*const anyopaque' + elif util.is_void_ptr(res_type): + return '?*anyopaque' + else: + sys.exit(f"ERROR funcptr_result_c(): {field_type}") + +def funcdecl_args_c(decl, prefix): + s = "" + func_name = decl['name'] + for param_decl in decl['params']: + if s != "": + s += ", " + param_name = param_decl['name'] + param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type']) + s += as_c_arg_type(param_type, prefix) + return s + +def funcdecl_args_zig(decl, prefix): + s = "" + func_name = decl['name'] + for param_decl in decl['params']: + if s != "": + s += ", " + param_name = param_decl['name'] + param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type']) + s += f"{as_zig_arg_type(f'{param_name}: ', param_type, prefix)}" + return s + +def funcdecl_result_c(decl, prefix): + func_name = decl['name'] + decl_type = decl['type'] + result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip()) + return as_c_arg_type(result_type, prefix) + +def funcdecl_result_zig(decl, prefix): + func_name = decl['name'] + decl_type = decl['type'] + result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip()) + zig_res_type = as_zig_arg_type(None, result_type, prefix) + return zig_res_type + +def gen_struct(decl, prefix): + struct_name = check_override(decl['name']) + zig_type = as_zig_struct_type(struct_name, prefix) + l(f"pub const {zig_type} = extern struct {{") + for field in decl['fields']: + field_name = check_override(field['name']) + field_type = check_override(f'{struct_name}.{field_name}', default=field['type']) + if is_prim_type(field_type): + l(f" {field_name}: {as_zig_prim_type(field_type)} = {type_default_value(field_type)},") + elif is_struct_type(field_type): + l(f" {field_name}: {as_zig_struct_type(field_type, prefix)} = .{{}},") + elif is_enum_type(field_type): + l(f" {field_name}: {as_zig_enum_type(field_type, prefix)} = .{enum_default_item(field_type)},") + elif util.is_string_ptr(field_type): + l(f" {field_name}: [*c]const u8 = null,") + elif util.is_const_void_ptr(field_type): + l(f" {field_name}: ?*const anyopaque = null,") + elif util.is_void_ptr(field_type): + l(f" {field_name}: ?*anyopaque = null,") + elif is_const_prim_ptr(field_type): + l(f" {field_name}: ?[*]const {as_zig_prim_type(util.extract_ptr_type(field_type))} = null,") + elif util.is_func_ptr(field_type): + l(f" {field_name}: ?*const fn ({funcptr_args_c(field_type, prefix)}) callconv(.C) {funcptr_result_c(field_type)} = null,") + elif util.is_1d_array_type(field_type): + array_type = util.extract_array_type(field_type) + array_sizes = util.extract_array_sizes(field_type) + if is_prim_type(array_type) or is_struct_type(array_type): + if is_prim_type(array_type): + zig_type = as_zig_prim_type(array_type) + def_val = type_default_value(array_type) + elif is_struct_type(array_type): + zig_type = as_zig_struct_type(array_type, prefix) + def_val = '.{}' + elif is_enum_type(array_type): + zig_type = as_zig_enum_type(array_type, prefix) + def_val = '.{}' + else: + sys.exit(f"ERROR gen_struct is_1d_array_type: {array_type}") + t0 = f"[{array_sizes[0]}]{zig_type}" + t1 = f"[_]{zig_type}" + l(f" {field_name}: {t0} = {t1}{{{def_val}}} ** {array_sizes[0]},") + elif util.is_const_void_ptr(array_type): + l(f" {field_name}: [{array_sizes[0]}]?*const anyopaque = [_]?*const anyopaque{{null}} ** {array_sizes[0]},") + else: + sys.exit(f"ERROR gen_struct: array {field_name}: {field_type} => {array_type} [{array_sizes[0]}]") + elif util.is_2d_array_type(field_type): + array_type = util.extract_array_type(field_type) + array_sizes = util.extract_array_sizes(field_type) + if is_prim_type(array_type): + zig_type = as_zig_prim_type(array_type) + def_val = type_default_value(array_type) + elif is_struct_type(array_type): + zig_type = as_zig_struct_type(array_type, prefix) + def_val = ".{}" + else: + sys.exit(f"ERROR gen_struct is_2d_array_type: {array_type}") + t0 = f"[{array_sizes[0]}][{array_sizes[1]}]{zig_type}" + l(f" {field_name}: {t0} = [_][{array_sizes[1]}]{zig_type}{{[_]{zig_type}{{{def_val}}} ** {array_sizes[1]}}} ** {array_sizes[0]},") + else: + sys.exit(f"ERROR gen_struct: {field_name}: {field_type};") + l("};") + +def gen_consts(decl, prefix): + for item in decl['items']: + item_name = check_override(item['name']) + l(f"pub const {util.as_lower_snake_case(item_name, prefix)} = {item['value']};") + +def gen_enum(decl, prefix): + enum_name = check_override(decl['name']) + l(f"pub const {as_zig_enum_type(enum_name, prefix)} = enum(i32) {{") + for item in decl['items']: + item_name = as_enum_item_name(check_override(item['name'])) + if item_name != "FORCE_U32": + if 'value' in item: + l(f" {item_name} = {item['value']},") + else: + l(f" {item_name},") + l("};") + +def gen_func_c(decl, prefix): + l(f"pub extern fn {decl['name']}({funcdecl_args_c(decl, prefix)}) {funcdecl_result_c(decl, prefix)};") + +def gen_func_zig(decl, prefix): + c_func_name = decl['name'] + zig_func_name = util.as_lower_camel_case(check_override(decl['name']), prefix) + if c_func_name in c_callbacks: + # a simple forwarded C callback function + l(f"pub const {zig_func_name} = {c_func_name};") + else: + zig_res_type = funcdecl_result_zig(decl, prefix) + l(f"pub fn {zig_func_name}({funcdecl_args_zig(decl, prefix)}) {zig_res_type} {{") + if is_zig_string(zig_res_type): + # special case: convert C string to Zig string slice + s = f" return cStrToZig({c_func_name}(" + elif zig_res_type != 'void': + s = f" return {c_func_name}(" + else: + s = f" {c_func_name}(" + for i, param_decl in enumerate(decl['params']): + if i > 0: + s += ", " + arg_name = param_decl['name'] + arg_type = param_decl['type'] + if is_const_struct_ptr(arg_type): + s += f"&{arg_name}" + elif util.is_string_ptr(arg_type): + s += f"@ptrCast({arg_name})" + else: + s += arg_name + if is_zig_string(zig_res_type): + s += ")" + s += ");" + l(s) + l("}") + +def pre_parse(inp): + global struct_types + global enum_types + for decl in inp['decls']: + kind = decl['kind'] + if kind == 'struct': + struct_types.append(decl['name']) + elif kind == 'enum': + enum_name = decl['name'] + enum_types.append(enum_name) + enum_items[enum_name] = [] + for item in decl['items']: + enum_items[enum_name].append(as_enum_item_name(item['name'])) + +def gen_imports(inp, dep_prefixes): + l('const builtin = @import("builtin");') + for dep_prefix in dep_prefixes: + dep_module_name = module_names[dep_prefix] + l(f'const {dep_prefix[:-1]} = @import("{dep_module_name}.zig");') + l('') + +def gen_helpers(inp): + l('// helper function to convert a C string to a Zig string slice') + l('fn cStrToZig(c_str: [*c]const u8) [:0]const u8 {') + l(' return @import("std").mem.span(c_str);') + l('}') + if inp['prefix'] in ['sg_', 'sdtx_', 'sshape_', 'sfetch_']: + l('// helper function to convert "anything" to a Range struct') + l('pub fn asRange(val: anytype) Range {') + l(' const type_info = @typeInfo(@TypeOf(val));') + l(' switch (type_info) {') + l(' .Pointer => {') + l(' switch (type_info.Pointer.size) {') + l(' .One => return .{ .ptr = val, .size = @sizeOf(type_info.Pointer.child) },') + l(' .Slice => return .{ .ptr = val.ptr, .size = @sizeOf(type_info.Pointer.child) * val.len },') + l(' else => @compileError("FIXME: Pointer type!"),') + l(' }') + l(' },') + l(' .Struct, .Array => {') + l(' @compileError("Structs and arrays must be passed as pointers to asRange");') + l(' },') + l(' else => {') + l(' @compileError("Cannot convert to range!");') + l(' },') + l(' }') + l('}') + l('') + if inp['prefix'] == 'sdtx_': + l('// std.fmt compatible Writer') + l('pub const Writer = struct {') + l(' pub const Error = error{};') + l(' pub fn writeAll(self: Writer, bytes: []const u8) Error!void {') + l(' _ = self;') + l(' for (bytes) |byte| {') + l(' putc(byte);') + l(' }') + l(' }') + l(' pub fn writeByteNTimes(self: Writer, byte: u8, n: usize) Error!void {') + l(' _ = self;') + l(' var i: u64 = 0;') + l(' while (i < n) : (i += 1) {') + l(' putc(byte);') + l(' }') + l(' }') + l(' pub fn writeBytesNTimes(self: Writer, bytes: []const u8, n: usize) Error!void {') + l(' var i: usize = 0;') + l(' while (i < n) : (i += 1) {') + l(' try self.writeAll(bytes);') + l(' }') + l(' }') + l('};') + l('// std.fmt-style formatted print') + l('pub fn print(comptime fmt: anytype, args: anytype) void {') + l(' const writer: Writer = .{};') + l(' @import("std").fmt.format(writer, fmt, args) catch {};') + l('}') + l('') + +def gen_module(inp, dep_prefixes): + l('// machine generated, do not edit') + l('') + gen_imports(inp, dep_prefixes) + gen_helpers(inp) + pre_parse(inp) + prefix = inp['prefix'] + for decl in inp['decls']: + if not decl['is_dep']: + kind = decl['kind'] + if kind == 'consts': + gen_consts(decl, prefix) + elif not check_ignore(decl['name']): + if kind == 'struct': + gen_struct(decl, prefix) + elif kind == 'enum': + gen_enum(decl, prefix) + elif kind == 'func': + gen_func_c(decl, prefix) + gen_func_zig(decl, prefix) + +def prepare(): + print('=== Generating Zig bindings:') + if not os.path.isdir('sokol-zig/src/sokol'): + os.makedirs('sokol-zig/src/sokol') + if not os.path.isdir('sokol-zig/src/sokol/c'): + os.makedirs('sokol-zig/src/sokol/c') + +def gen(c_header_path, c_prefix, dep_c_prefixes): + if not c_prefix in module_names: + print(f' >> warning: skipping generation for {c_prefix} prefix...') + return + module_name = module_names[c_prefix] + c_source_path = c_source_paths[c_prefix] + print(f' {c_header_path} => {module_name}') + reset_globals() + shutil.copyfile(c_header_path, f'sokol-zig/src/sokol/c/{os.path.basename(c_header_path)}') + ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes) + gen_module(ir, dep_c_prefixes) + output_path = f"sokol-zig/src/sokol/{ir['module']}.zig" + with open(output_path, 'w', newline='\n') as f_outp: + f_outp.write(out_lines) diff --git a/thirdparty/sokol/fips.yml b/thirdparty/sokol/fips.yml new file mode 100644 index 0000000..372d8fd --- /dev/null +++ b/thirdparty/sokol/fips.yml @@ -0,0 +1,2 @@ +exports: + header-dirs: [ ".", "util" ] diff --git a/thirdparty/sokol/sokol_app.h b/thirdparty/sokol/sokol_app.h new file mode 100644 index 0000000..df3c1f2 --- /dev/null +++ b/thirdparty/sokol/sokol_app.h @@ -0,0 +1,11819 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_APP_IMPL) +#define SOKOL_APP_IMPL +#endif +#ifndef SOKOL_APP_INCLUDED +/* + sokol_app.h -- cross-platform application wrapper + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_APP_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + In the same place define one of the following to select the 3D-API + which should be initialized by sokol_app.h (this must also match + the backend selected for sokol_gfx.h if both are used in the same + project): + + #define SOKOL_GLCORE + #define SOKOL_GLES3 + #define SOKOL_D3D11 + #define SOKOL_METAL + #define SOKOL_WGPU + #define SOKOL_NOAPI + + Optionally provide the following defines with your own implementations: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + SOKOL_WIN32_FORCE_MAIN - define this on Win32 to use a main() entry point instead of WinMain + SOKOL_NO_ENTRY - define this if sokol_app.h shouldn't "hijack" the main() function + SOKOL_APP_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_APP_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + Optionally define the following to force debug checks and validations + even in release mode: + + SOKOL_DEBUG - by default this is defined if _DEBUG is defined + + If sokol_app.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_APP_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + On Linux, SOKOL_GLCORE can use either GLX or EGL. + GLX is default, set SOKOL_FORCE_EGL to override. + + For example code, see https://github.com/floooh/sokol-samples/tree/master/sapp + + Portions of the Windows and Linux GL initialization, event-, icon- etc... code + have been taken from GLFW (http://www.glfw.org/) + + iOS onscreen keyboard support 'inspired' by libgdx. + + Link with the following system libraries: + + - on macOS with Metal: Cocoa, QuartzCore, Metal, MetalKit + - on macOS with GL: Cocoa, QuartzCore, OpenGL + - on iOS with Metal: Foundation, UIKit, Metal, MetalKit + - on iOS with GL: Foundation, UIKit, OpenGLES, GLKit + - on Linux with EGL: X11, Xi, Xcursor, EGL, GL (or GLESv2), dl, pthread, m(?) + - on Linux with GLX: X11, Xi, Xcursor, GL, dl, pthread, m(?) + - on Android: GLESv3, EGL, log, android + - on Windows with the MSVC or Clang toolchains: no action needed, libs are defined in-source via pragma-comment-lib + - on Windows with MINGW/MSYS2 gcc: compile with '-mwin32' so that _WIN32 is defined + - link with the following libs: -lkernel32 -luser32 -lshell32 + - additionally with the GL backend: -lgdi32 + - additionally with the D3D11 backend: -ld3d11 -ldxgi + + On Linux, you also need to use the -pthread compiler and linker option, otherwise weird + things will happen, see here for details: https://github.com/floooh/sokol/issues/376 + + On macOS and iOS, the implementation must be compiled as Objective-C. + + FEATURE OVERVIEW + ================ + sokol_app.h provides a minimalistic cross-platform API which + implements the 'application-wrapper' parts of a 3D application: + + - a common application entry function + - creates a window and 3D-API context/device with a 'default framebuffer' + - makes the rendered frame visible + - provides keyboard-, mouse- and low-level touch-events + - platforms: MacOS, iOS, HTML5, Win32, Linux/RaspberryPi, Android + - 3D-APIs: Metal, D3D11, GL3.2, GLES3, WebGL, WebGL2, NOAPI + + FEATURE/PLATFORM MATRIX + ======================= + | Windows | macOS | Linux | iOS | Android | HTML5 + --------------------+---------+-------+-------+-------+---------+-------- + gl 3.x | YES | YES | YES | --- | --- | --- + gles3/webgl2 | --- | --- | YES(2)| YES | YES | YES + metal | --- | YES | --- | YES | --- | --- + d3d11 | YES | --- | --- | --- | --- | --- + noapi | YES | TODO | TODO | --- | TODO | --- + KEY_DOWN | YES | YES | YES | SOME | TODO | YES + KEY_UP | YES | YES | YES | SOME | TODO | YES + CHAR | YES | YES | YES | YES | TODO | YES + MOUSE_DOWN | YES | YES | YES | --- | --- | YES + MOUSE_UP | YES | YES | YES | --- | --- | YES + MOUSE_SCROLL | YES | YES | YES | --- | --- | YES + MOUSE_MOVE | YES | YES | YES | --- | --- | YES + MOUSE_ENTER | YES | YES | YES | --- | --- | YES + MOUSE_LEAVE | YES | YES | YES | --- | --- | YES + TOUCHES_BEGAN | --- | --- | --- | YES | YES | YES + TOUCHES_MOVED | --- | --- | --- | YES | YES | YES + TOUCHES_ENDED | --- | --- | --- | YES | YES | YES + TOUCHES_CANCELLED | --- | --- | --- | YES | YES | YES + RESIZED | YES | YES | YES | YES | YES | YES + ICONIFIED | YES | YES | YES | --- | --- | --- + RESTORED | YES | YES | YES | --- | --- | --- + FOCUSED | YES | YES | YES | --- | --- | YES + UNFOCUSED | YES | YES | YES | --- | --- | YES + SUSPENDED | --- | --- | --- | YES | YES | TODO + RESUMED | --- | --- | --- | YES | YES | TODO + QUIT_REQUESTED | YES | YES | YES | --- | --- | YES + IME | TODO | TODO? | TODO | ??? | TODO | ??? + key repeat flag | YES | YES | YES | --- | --- | YES + windowed | YES | YES | YES | --- | --- | YES + fullscreen | YES | YES | YES | YES | YES | --- + mouse hide | YES | YES | YES | --- | --- | YES + mouse lock | YES | YES | YES | --- | --- | YES + set cursor type | YES | YES | YES | --- | --- | YES + screen keyboard | --- | --- | --- | YES | TODO | YES + swap interval | YES | YES | YES | YES | TODO | YES + high-dpi | YES | YES | TODO | YES | YES | YES + clipboard | YES | YES | TODO | --- | --- | YES + MSAA | YES | YES | YES | YES | YES | YES + drag'n'drop | YES | YES | YES | --- | --- | YES + window icon | YES | YES(1)| YES | --- | --- | YES + + (1) macOS has no regular window icons, instead the dock icon is changed + (2) supported with EGL only (not GLX) + + STEP BY STEP + ============ + --- Add a sokol_main() function to your code which returns a sapp_desc structure + with initialization parameters and callback function pointers. This + function is called very early, usually at the start of the + platform's entry function (e.g. main or WinMain). You should do as + little as possible here, since the rest of your code might be called + from another thread (this depends on the platform): + + sapp_desc sokol_main(int argc, char* argv[]) { + return (sapp_desc) { + .width = 640, + .height = 480, + .init_cb = my_init_func, + .frame_cb = my_frame_func, + .cleanup_cb = my_cleanup_func, + .event_cb = my_event_func, + ... + }; + } + + To get any logging output in case of errors you need to provide a log + callback. The easiest way is via sokol_log.h: + + #include "sokol_log.h" + + sapp_desc sokol_main(int argc, char* argv[]) { + return (sapp_desc) { + ... + .logger.func = slog_func, + }; + } + + There are many more setup parameters, but these are the most important. + For a complete list search for the sapp_desc structure declaration + below. + + DO NOT call any sokol-app function from inside sokol_main(), since + sokol-app will not be initialized at this point. + + The .width and .height parameters are the preferred size of the 3D + rendering canvas. The actual size may differ from this depending on + platform and other circumstances. Also the canvas size may change at + any time (for instance when the user resizes the application window, + or rotates the mobile device). You can just keep .width and .height + zero-initialized to open a default-sized window (what "default-size" + exactly means is platform-specific, but usually it's a size that covers + most of, but not all, of the display). + + All provided function callbacks will be called from the same thread, + but this may be different from the thread where sokol_main() was called. + + .init_cb (void (*)(void)) + This function is called once after the application window, + 3D rendering context and swap chain have been created. The + function takes no arguments and has no return value. + .frame_cb (void (*)(void)) + This is the per-frame callback, which is usually called 60 + times per second. This is where your application would update + most of its state and perform all rendering. + .cleanup_cb (void (*)(void)) + The cleanup callback is called once right before the application + quits. + .event_cb (void (*)(const sapp_event* event)) + The event callback is mainly for input handling, but is also + used to communicate other types of events to the application. Keep the + event_cb struct member zero-initialized if your application doesn't require + event handling. + + As you can see, those 'standard callbacks' don't have a user_data + argument, so any data that needs to be preserved between callbacks + must live in global variables. If keeping state in global variables + is not an option, there's an alternative set of callbacks with + an additional user_data pointer argument: + + .user_data (void*) + The user-data argument for the callbacks below + .init_userdata_cb (void (*)(void* user_data)) + .frame_userdata_cb (void (*)(void* user_data)) + .cleanup_userdata_cb (void (*)(void* user_data)) + .event_userdata_cb (void(*)(const sapp_event* event, void* user_data)) + + The function sapp_userdata() can be used to query the user_data + pointer provided in the sapp_desc struct. + + You can also call sapp_query_desc() to get a copy of the + original sapp_desc structure. + + NOTE that there's also an alternative compile mode where sokol_app.h + doesn't "hijack" the main() function. Search below for SOKOL_NO_ENTRY. + + --- Implement the initialization callback function (init_cb), this is called + once after the rendering surface, 3D API and swap chain have been + initialized by sokol_app. All sokol-app functions can be called + from inside the initialization callback, the most useful functions + at this point are: + + int sapp_width(void) + int sapp_height(void) + Returns the current width and height of the default framebuffer in pixels, + this may change from one frame to the next, and it may be different + from the initial size provided in the sapp_desc struct. + + float sapp_widthf(void) + float sapp_heightf(void) + These are alternatives to sapp_width() and sapp_height() which return + the default framebuffer size as float values instead of integer. This + may help to prevent casting back and forth between int and float + in more strongly typed languages than C and C++. + + double sapp_frame_duration(void) + Returns the frame duration in seconds averaged over a number of + frames to smooth out any jittering spikes. + + int sapp_color_format(void) + int sapp_depth_format(void) + The color and depth-stencil pixelformats of the default framebuffer, + as integer values which are compatible with sokol-gfx's + sg_pixel_format enum (so that they can be plugged directly in places + where sg_pixel_format is expected). Possible values are: + + 23 == SG_PIXELFORMAT_RGBA8 + 28 == SG_PIXELFORMAT_BGRA8 + 42 == SG_PIXELFORMAT_DEPTH + 43 == SG_PIXELFORMAT_DEPTH_STENCIL + + int sapp_sample_count(void) + Return the MSAA sample count of the default framebuffer. + + const void* sapp_metal_get_device(void) + const void* sapp_metal_get_current_drawable(void) + const void* sapp_metal_get_depth_stencil_texture(void) + const void* sapp_metal_get_msaa_color_texture(void) + If the Metal backend has been selected, these functions return pointers + to various Metal API objects required for rendering, otherwise + they return a null pointer. These void pointers are actually + Objective-C ids converted with a (ARC) __bridge cast so that + the ids can be tunnel through C code. Also note that the returned + pointers to the renderpass-descriptor and drawable may change from one + frame to the next, only the Metal device object is guaranteed to + stay the same. + + const void* sapp_macos_get_window(void) + On macOS, get the NSWindow object pointer, otherwise a null pointer. + Before being used as Objective-C object, the void* must be converted + back with a (ARC) __bridge cast. + + const void* sapp_ios_get_window(void) + On iOS, get the UIWindow object pointer, otherwise a null pointer. + Before being used as Objective-C object, the void* must be converted + back with a (ARC) __bridge cast. + + const void* sapp_d3d11_get_device(void) + const void* sapp_d3d11_get_device_context(void) + const void* sapp_d3d11_get_render_view(void) + const void* sapp_d3d11_get_resolve_view(void); + const void* sapp_d3d11_get_depth_stencil_view(void) + Similar to the sapp_metal_* functions, the sapp_d3d11_* functions + return pointers to D3D11 API objects required for rendering, + only if the D3D11 backend has been selected. Otherwise they + return a null pointer. Note that the returned pointers to the + render-target-view and depth-stencil-view may change from one + frame to the next! + + const void* sapp_win32_get_hwnd(void) + On Windows, get the window's HWND, otherwise a null pointer. The + HWND has been cast to a void pointer in order to be tunneled + through code which doesn't include Windows.h. + + const void* sapp_wgpu_get_device(void) + const void* sapp_wgpu_get_render_view(void) + const void* sapp_wgpu_get_resolve_view(void) + const void* sapp_wgpu_get_depth_stencil_view(void) + These are the WebGPU-specific functions to get the WebGPU + objects and values required for rendering. If sokol_app.h + is not compiled with SOKOL_WGPU, these functions return null. + + uint32_t sapp_gl_get_framebuffer(void) + This returns the 'default framebuffer' of the GL context. + Typically this will be zero. + + int sapp_gl_get_major_version(void) + int sapp_gl_get_minor_version(void) + Returns the major and minor version of the GL context + (only for SOKOL_GLCORE, all other backends return zero here, including SOKOL_GLES3) + + const void* sapp_android_get_native_activity(void); + On Android, get the native activity ANativeActivity pointer, otherwise + a null pointer. + + --- Implement the frame-callback function, this function will be called + on the same thread as the init callback, but might be on a different + thread than the sokol_main() function. Note that the size of + the rendering framebuffer might have changed since the frame callback + was called last. Call the functions sapp_width() and sapp_height() + each frame to get the current size. + + --- Optionally implement the event-callback to handle input events. + sokol-app provides the following type of input events: + - a 'virtual key' was pressed down or released + - a single text character was entered (provided as UTF-32 code point) + - a mouse button was pressed down or released (left, right, middle) + - mouse-wheel or 2D scrolling events + - the mouse was moved + - the mouse has entered or left the application window boundaries + - low-level, portable multi-touch events (began, moved, ended, cancelled) + - the application window was resized, iconified or restored + - the application was suspended or restored (on mobile platforms) + - the user or application code has asked to quit the application + - a string was pasted to the system clipboard + - one or more files have been dropped onto the application window + + To explicitly 'consume' an event and prevent that the event is + forwarded for further handling to the operating system, call + sapp_consume_event() from inside the event handler (NOTE that + this behaviour is currently only implemented for some HTML5 + events, support for other platforms and event types will + be added as needed, please open a GitHub ticket and/or provide + a PR if needed). + + NOTE: Do *not* call any 3D API rendering functions in the event + callback function, since the 3D API context may not be active when the + event callback is called (it may work on some platforms and 3D APIs, + but not others, and the exact behaviour may change between + sokol-app versions). + + --- Implement the cleanup-callback function, this is called once + after the user quits the application (see the section + "APPLICATION QUIT" for detailed information on quitting + behaviour, and how to intercept a pending quit - for instance to show a + "Really Quit?" dialog box). Note that the cleanup-callback isn't + guaranteed to be called on the web and mobile platforms. + + MOUSE CURSOR TYPE AND VISIBILITY + ================================ + You can show and hide the mouse cursor with + + void sapp_show_mouse(bool show) + + And to get the current shown status: + + bool sapp_mouse_shown(void) + + NOTE that hiding the mouse cursor is different and independent from + the MOUSE/POINTER LOCK feature which will also hide the mouse pointer when + active (MOUSE LOCK is described below). + + To change the mouse cursor to one of several predefined types, call + the function: + + void sapp_set_mouse_cursor(sapp_mouse_cursor cursor) + + Setting the default mouse cursor SAPP_MOUSECURSOR_DEFAULT will restore + the standard look. + + To get the currently active mouse cursor type, call: + + sapp_mouse_cursor sapp_get_mouse_cursor(void) + + MOUSE LOCK (AKA POINTER LOCK, AKA MOUSE CAPTURE) + ================================================ + In normal mouse mode, no mouse movement events are reported when the + mouse leaves the windows client area or hits the screen border (whether + it's one or the other depends on the platform), and the mouse move events + (SAPP_EVENTTYPE_MOUSE_MOVE) contain absolute mouse positions in + framebuffer pixels in the sapp_event items mouse_x and mouse_y, and + relative movement in framebuffer pixels in the sapp_event items mouse_dx + and mouse_dy. + + To get continuous mouse movement (also when the mouse leaves the window + client area or hits the screen border), activate mouse-lock mode + by calling: + + sapp_lock_mouse(true) + + When mouse lock is activated, the mouse pointer is hidden, the + reported absolute mouse position (sapp_event.mouse_x/y) appears + frozen, and the relative mouse movement in sapp_event.mouse_dx/dy + no longer has a direct relation to framebuffer pixels but instead + uses "raw mouse input" (what "raw mouse input" exactly means also + differs by platform). + + To deactivate mouse lock and return to normal mouse mode, call + + sapp_lock_mouse(false) + + And finally, to check if mouse lock is currently active, call + + if (sapp_mouse_locked()) { ... } + + On native platforms, the sapp_lock_mouse() and sapp_mouse_locked() + functions work as expected (mouse lock is activated or deactivated + immediately when sapp_lock_mouse() is called, and sapp_mouse_locked() + also immediately returns the new state after sapp_lock_mouse() + is called. + + On the web platform, sapp_lock_mouse() and sapp_mouse_locked() behave + differently, as dictated by the limitations of the HTML5 Pointer Lock API: + + - sapp_lock_mouse(true) can be called at any time, but it will + only take effect in a 'short-lived input event handler of a specific + type', meaning when one of the following events happens: + - SAPP_EVENTTYPE_MOUSE_DOWN + - SAPP_EVENTTYPE_MOUSE_UP + - SAPP_EVENTTYPE_MOUSE_SCROLL + - SAPP_EVENTTYPE_KEY_UP + - SAPP_EVENTTYPE_KEY_DOWN + - The mouse lock/unlock action on the web platform is asynchronous, + this means that sapp_mouse_locked() won't immediately return + the new status after calling sapp_lock_mouse(), instead the + reported status will only change when the pointer lock has actually + been activated or deactivated in the browser. + - On the web, mouse lock can be deactivated by the user at any time + by pressing the Esc key. When this happens, sokol_app.h behaves + the same as if sapp_lock_mouse(false) is called. + + For things like camera manipulation it's most straightforward to lock + and unlock the mouse right from the sokol_app.h event handler, for + instance the following code enters and leaves mouse lock when the + left mouse button is pressed and released, and then uses the relative + movement information to manipulate a camera (taken from the + cgltf-sapp.c sample in the sokol-samples repository + at https://github.com/floooh/sokol-samples): + + static void input(const sapp_event* ev) { + switch (ev->type) { + case SAPP_EVENTTYPE_MOUSE_DOWN: + if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) { + sapp_lock_mouse(true); + } + break; + + case SAPP_EVENTTYPE_MOUSE_UP: + if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) { + sapp_lock_mouse(false); + } + break; + + case SAPP_EVENTTYPE_MOUSE_MOVE: + if (sapp_mouse_locked()) { + cam_orbit(&state.camera, ev->mouse_dx * 0.25f, ev->mouse_dy * 0.25f); + } + break; + + default: + break; + } + } + + CLIPBOARD SUPPORT + ================= + Applications can send and receive UTF-8 encoded text data from and to the + system clipboard. By default, clipboard support is disabled and + must be enabled at startup via the following sapp_desc struct + members: + + sapp_desc.enable_clipboard - set to true to enable clipboard support + sapp_desc.clipboard_size - size of the internal clipboard buffer in bytes + + Enabling the clipboard will dynamically allocate a clipboard buffer + for UTF-8 encoded text data of the requested size in bytes, the default + size is 8 KBytes. Strings that don't fit into the clipboard buffer + (including the terminating zero) will be silently clipped, so it's + important that you provide a big enough clipboard size for your + use case. + + To send data to the clipboard, call sapp_set_clipboard_string() with + a pointer to an UTF-8 encoded, null-terminated C-string. + + NOTE that on the HTML5 platform, sapp_set_clipboard_string() must be + called from inside a 'short-lived event handler', and there are a few + other HTML5-specific caveats to workaround. You'll basically have to + tinker until it works in all browsers :/ (maybe the situation will + improve when all browsers agree on and implement the new + HTML5 navigator.clipboard API). + + To get data from the clipboard, check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED + event in your event handler function, and then call sapp_get_clipboard_string() + to obtain the pasted UTF-8 encoded text. + + NOTE that behaviour of sapp_get_clipboard_string() is slightly different + depending on platform: + + - on the HTML5 platform, the internal clipboard buffer will only be updated + right before the SAPP_EVENTTYPE_CLIPBOARD_PASTED event is sent, + and sapp_get_clipboard_string() will simply return the current content + of the clipboard buffer + - on 'native' platforms, the call to sapp_get_clipboard_string() will + update the internal clipboard buffer with the most recent data + from the system clipboard + + Portable code should check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED event, + and then call sapp_get_clipboard_string() right in the event handler. + + The SAPP_EVENTTYPE_CLIPBOARD_PASTED event will be generated by sokol-app + as follows: + + - on macOS: when the Cmd+V key is pressed down + - on HTML5: when the browser sends a 'paste' event to the global 'window' object + - on all other platforms: when the Ctrl+V key is pressed down + + DRAG AND DROP SUPPORT + ===================== + PLEASE NOTE: the drag'n'drop feature works differently on WASM/HTML5 + and on the native desktop platforms (Win32, Linux and macOS) because + of security-related restrictions in the HTML5 drag'n'drop API. The + WASM/HTML5 specifics are described at the end of this documentation + section: + + Like clipboard support, drag'n'drop support must be explicitly enabled + at startup in the sapp_desc struct. + + sapp_desc sokol_main(void) { + return (sapp_desc) { + .enable_dragndrop = true, // default is false + ... + }; + } + + You can also adjust the maximum number of files that are accepted + in a drop operation, and the maximum path length in bytes if needed: + + sapp_desc sokol_main(void) { + return (sapp_desc) { + .enable_dragndrop = true, // default is false + .max_dropped_files = 8, // default is 1 + .max_dropped_file_path_length = 8192, // in bytes, default is 2048 + ... + }; + } + + When drag'n'drop is enabled, the event callback will be invoked with an + event of type SAPP_EVENTTYPE_FILES_DROPPED whenever the user drops files on + the application window. + + After the SAPP_EVENTTYPE_FILES_DROPPED is received, you can query the + number of dropped files, and their absolute paths by calling separate + functions: + + void on_event(const sapp_event* ev) { + if (ev->type == SAPP_EVENTTYPE_FILES_DROPPED) { + + // the mouse position where the drop happened + float x = ev->mouse_x; + float y = ev->mouse_y; + + // get the number of files and their paths like this: + const int num_dropped_files = sapp_get_num_dropped_files(); + for (int i = 0; i < num_dropped_files; i++) { + const char* path = sapp_get_dropped_file_path(i); + ... + } + } + } + + The returned file paths are UTF-8 encoded strings. + + You can call sapp_get_num_dropped_files() and sapp_get_dropped_file_path() + anywhere, also outside the event handler callback, but be aware that the + file path strings will be overwritten with the next drop operation. + + In any case, sapp_get_dropped_file_path() will never return a null pointer, + instead an empty string "" will be returned if the drag'n'drop feature + hasn't been enabled, the last drop-operation failed, or the file path index + is out of range. + + Drag'n'drop caveats: + + - if more files are dropped in a single drop-action + than sapp_desc.max_dropped_files, the additional + files will be silently ignored + - if any of the file paths is longer than + sapp_desc.max_dropped_file_path_length (in number of bytes, after UTF-8 + encoding) the entire drop operation will be silently ignored (this + needs some sort of error feedback in the future) + - no mouse positions are reported while the drag is in + process, this may change in the future + + Drag'n'drop on HTML5/WASM: + + The HTML5 drag'n'drop API doesn't return file paths, but instead + black-box 'file objects' which must be used to load the content + of dropped files. This is the reason why sokol_app.h adds two + HTML5-specific functions to the drag'n'drop API: + + uint32_t sapp_html5_get_dropped_file_size(int index) + Returns the size in bytes of a dropped file. + + void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request) + Asynchronously loads the content of a dropped file into a + provided memory buffer (which must be big enough to hold + the file content) + + To start loading the first dropped file after an SAPP_EVENTTYPE_FILES_DROPPED + event is received: + + sapp_html5_fetch_dropped_file(&(sapp_html5_fetch_request){ + .dropped_file_index = 0, + .callback = fetch_cb + .buffer = { + .ptr = buf, + .size = sizeof(buf) + }, + .user_data = ... + }); + + Make sure that the memory pointed to by 'buf' stays valid until the + callback function is called! + + As result of the asynchronous loading operation (no matter if succeeded or + failed) the 'fetch_cb' function will be called: + + void fetch_cb(const sapp_html5_fetch_response* response) { + // IMPORTANT: check if the loading operation actually succeeded: + if (response->succeeded) { + // the size of the loaded file: + const size_t num_bytes = response->data.size; + // and the pointer to the data (same as 'buf' in the fetch-call): + const void* ptr = response->data.ptr; + } + else { + // on error check the error code: + switch (response->error_code) { + case SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL: + ... + break; + case SAPP_HTML5_FETCH_ERROR_OTHER: + ... + break; + } + } + } + + Check the droptest-sapp example for a real-world example which works + both on native platforms and the web: + + https://github.com/floooh/sokol-samples/blob/master/sapp/droptest-sapp.c + + HIGH-DPI RENDERING + ================== + You can set the sapp_desc.high_dpi flag during initialization to request + a full-resolution framebuffer on HighDPI displays. The default behaviour + is sapp_desc.high_dpi=false, this means that the application will + render to a lower-resolution framebuffer on HighDPI displays and the + rendered content will be upscaled by the window system composer. + + In a HighDPI scenario, you still request the same window size during + sokol_main(), but the framebuffer sizes returned by sapp_width() + and sapp_height() will be scaled up according to the DPI scaling + ratio. + + Note that on some platforms the DPI scaling factor may change at any + time (for instance when a window is moved from a high-dpi display + to a low-dpi display). + + To query the current DPI scaling factor, call the function: + + float sapp_dpi_scale(void); + + For instance on a Retina Mac, returning the following sapp_desc + struct from sokol_main(): + + sapp_desc sokol_main(void) { + return (sapp_desc) { + .width = 640, + .height = 480, + .high_dpi = true, + ... + }; + } + + ...the functions the functions sapp_width(), sapp_height() + and sapp_dpi_scale() will return the following values: + + sapp_width: 1280 + sapp_height: 960 + sapp_dpi_scale: 2.0 + + If the high_dpi flag is false, or you're not running on a Retina display, + the values would be: + + sapp_width: 640 + sapp_height: 480 + sapp_dpi_scale: 1.0 + + If the window is moved from the Retina display to a low-dpi external display, + the values would change as follows: + + sapp_width: 1280 => 640 + sapp_height: 960 => 480 + sapp_dpi_scale: 2.0 => 1.0 + + Currently there is no event associated with a DPI change, but an + SAPP_EVENTTYPE_RESIZED will be sent as a side effect of the + framebuffer size changing. + + Per-monitor DPI is currently supported on macOS and Windows. + + APPLICATION QUIT + ================ + Without special quit handling, a sokol_app.h application will quit + 'gracefully' when the user clicks the window close-button unless a + platform's application model prevents this (e.g. on web or mobile). + 'Graceful exit' means that the application-provided cleanup callback will + be called before the application quits. + + On native desktop platforms sokol_app.h provides more control over the + application-quit-process. It's possible to initiate a 'programmatic quit' + from the application code, and a quit initiated by the application user can + be intercepted (for instance to show a custom dialog box). + + This 'programmatic quit protocol' is implemented through 3 functions + and 1 event: + + - sapp_quit(): This function simply quits the application without + giving the user a chance to intervene. Usually this might + be called when the user clicks the 'Ok' button in a 'Really Quit?' + dialog box + - sapp_request_quit(): Calling sapp_request_quit() will send the + event SAPP_EVENTTYPE_QUIT_REQUESTED to the applications event handler + callback, giving the user code a chance to intervene and cancel the + pending quit process (for instance to show a 'Really Quit?' dialog + box). If the event handler callback does nothing, the application + will be quit as usual. To prevent this, call the function + sapp_cancel_quit() from inside the event handler. + - sapp_cancel_quit(): Cancels a pending quit request, either initiated + by the user clicking the window close button, or programmatically + by calling sapp_request_quit(). The only place where calling this + function makes sense is from inside the event handler callback when + the SAPP_EVENTTYPE_QUIT_REQUESTED event has been received. + - SAPP_EVENTTYPE_QUIT_REQUESTED: this event is sent when the user + clicks the window's close button or application code calls the + sapp_request_quit() function. The event handler callback code can handle + this event by calling sapp_cancel_quit() to cancel the quit. + If the event is ignored, the application will quit as usual. + + On the web platform, the quit behaviour differs from native platforms, + because of web-specific restrictions: + + A `programmatic quit` initiated by calling sapp_quit() or + sapp_request_quit() will work as described above: the cleanup callback is + called, platform-specific cleanup is performed (on the web + this means that JS event handlers are unregistered), and then + the request-animation-loop will be exited. However that's all. The + web page itself will continue to exist (e.g. it's not possible to + programmatically close the browser tab). + + On the web it's also not possible to run custom code when the user + closes a browser tab, so it's not possible to prevent this with a + fancy custom dialog box. + + Instead the standard "Leave Site?" dialog box can be activated (or + deactivated) with the following function: + + sapp_html5_ask_leave_site(bool ask); + + The initial state of the associated internal flag can be provided + at startup via sapp_desc.html5_ask_leave_site. + + This feature should only be used sparingly in critical situations - for + instance when the user would loose data - since popping up modal dialog + boxes is considered quite rude in the web world. Note that there's no way + to customize the content of this dialog box or run any code as a result + of the user's decision. Also note that the user must have interacted with + the site before the dialog box will appear. These are all security measures + to prevent fishing. + + The Dear ImGui HighDPI sample contains example code of how to + implement a 'Really Quit?' dialog box with Dear ImGui (native desktop + platforms only), and for showing the hardwired "Leave Site?" dialog box + when running on the web platform: + + https://floooh.github.io/sokol-html5/wasm/imgui-highdpi-sapp.html + + FULLSCREEN + ========== + If the sapp_desc.fullscreen flag is true, sokol-app will try to create + a fullscreen window on platforms with a 'proper' window system + (mobile devices will always use fullscreen). The implementation details + depend on the target platform, in general sokol-app will use a + 'soft approach' which doesn't interfere too much with the platform's + window system (for instance borderless fullscreen window instead of + a 'real' fullscreen mode). Such details might change over time + as sokol-app is adapted for different needs. + + The most important effect of fullscreen mode to keep in mind is that + the requested canvas width and height will be ignored for the initial + window size, calling sapp_width() and sapp_height() will instead return + the resolution of the fullscreen canvas (however the provided size + might still be used for the non-fullscreen window, in case the user can + switch back from fullscreen- to windowed-mode). + + To toggle fullscreen mode programmatically, call sapp_toggle_fullscreen(). + + To check if the application window is currently in fullscreen mode, + call sapp_is_fullscreen(). + + WINDOW ICON SUPPORT + =================== + Some sokol_app.h backends allow to change the window icon programmatically: + + - on Win32: the small icon in the window's title bar, and the + bigger icon in the task bar + - on Linux: highly dependent on the used window manager, but usually + the window's title bar icon and/or the task bar icon + - on HTML5: the favicon shown in the page's browser tab + + NOTE that it is not possible to set the actual application icon which is + displayed by the operating system on the desktop or 'home screen'. Those + icons must be provided 'traditionally' through operating-system-specific + resources which are associated with the application (sokol_app.h might + later support setting the window icon from platform specific resource data + though). + + There are two ways to set the window icon: + + - at application start in the sokol_main() function by initializing + the sapp_desc.icon nested struct + - or later by calling the function sapp_set_icon() + + As a convenient shortcut, sokol_app.h comes with a builtin default-icon + (a rainbow-colored 'S', which at least looks a bit better than the Windows + default icon for applications), which can be activated like this: + + At startup in sokol_main(): + + sapp_desc sokol_main(...) { + return (sapp_desc){ + ... + icon.sokol_default = true + }; + } + + Or later by calling: + + sapp_set_icon(&(sapp_icon_desc){ .sokol_default = true }); + + NOTE that a completely zero-initialized sapp_icon_desc struct will not + update the window icon in any way. This is an 'escape hatch' so that you + can handle the window icon update yourself (or if you do this already, + sokol_app.h won't get in your way, in this case just leave the + sapp_desc.icon struct zero-initialized). + + Providing your own icon images works exactly like in GLFW (down to the + data format): + + You provide one or more 'candidate images' in different sizes, and the + sokol_app.h platform backends pick the best match for the specific backend + and icon type. + + For each candidate image, you need to provide: + + - the width in pixels + - the height in pixels + - and the actual pixel data in RGBA8 pixel format (e.g. 0xFFCC8844 + on a little-endian CPU means: alpha=0xFF, blue=0xCC, green=0x88, red=0x44) + + For instance, if you have 3 candidate images (small, medium, big) of + sizes 16x16, 32x32 and 64x64 the corresponding sapp_icon_desc struct is setup + like this: + + // the actual pixel data (RGBA8, origin top-left) + const uint32_t small[16][16] = { ... }; + const uint32_t medium[32][32] = { ... }; + const uint32_t big[64][64] = { ... }; + + const sapp_icon_desc icon_desc = { + .images = { + { .width = 16, .height = 16, .pixels = SAPP_RANGE(small) }, + { .width = 32, .height = 32, .pixels = SAPP_RANGE(medium) }, + // ...or without the SAPP_RANGE helper macro: + { .width = 64, .height = 64, .pixels = { .ptr=big, .size=sizeof(big) } } + } + }; + + An sapp_icon_desc struct initialized like this can then either be applied + at application start in sokol_main: + + sapp_desc sokol_main(...) { + return (sapp_desc){ + ... + icon = icon_desc + }; + } + + ...or later by calling sapp_set_icon(): + + sapp_set_icon(&icon_desc); + + Some window icon caveats: + + - once the window icon has been updated, there's no way to go back to + the platform's default icon, this is because some platforms (Linux + and HTML5) don't switch the icon visual back to the default even if + the custom icon is deleted or removed + - on HTML5, if the sokol_app.h icon doesn't show up in the browser + tab, check that there's no traditional favicon 'link' element + is defined in the page's index.html, sokol_app.h will only + append a new favicon link element, but not delete any manually + defined favicon in the page + + For an example and test of the window icon feature, check out the + 'icon-sapp' sample on the sokol-samples git repository. + + ONSCREEN KEYBOARD + ================= + On some platforms which don't provide a physical keyboard, sokol-app + can display the platform's integrated onscreen keyboard for text + input. To request that the onscreen keyboard is shown, call + + sapp_show_keyboard(true); + + Likewise, to hide the keyboard call: + + sapp_show_keyboard(false); + + Note that onscreen keyboard functionality is no longer supported + on the browser platform (the previous hacks and workarounds to make browser + keyboards work for on web applications that don't use HTML UIs + never really worked across browsers). + + INPUT EVENT BUBBLING ON THE WEB PLATFORM + ======================================== + By default, input event bubbling on the web platform is configured in + a way that makes the most sense for 'full-canvas' apps that cover the + entire browser client window area: + + - mouse, touch and wheel events do not bubble up, this prevents various + ugly side events, like: + - HTML text overlays being selected on double- or triple-click into + the canvas + - 'scroll bumping' even when the canvas covers the entire client area + - key_up/down events for 'character keys' *do* bubble up (otherwise + the browser will not generate UNICODE character events) + - all other key events *do not* bubble up by default (this prevents side effects + like F1 opening help, or F7 starting 'caret browsing') + - character events do no bubble up (although I haven't noticed any side effects + otherwise) + + Event bubbling can be enabled for input event categories during initialization + in the sapp_desc struct: + + sapp_desc sokol_main(int argc, char* argv[]) { + return (sapp_desc){ + //... + .html5_bubble_mouse_events = true, + .html5_bubble_touch_events = true, + .html5_bubble_wheel_events = true, + .html5_bubble_key_events = true, + .html5_bubble_char_events = true, + }; + } + + This basically opens the floodgates lets *all* input events bubble up to the browser. + To prevent individual events from bubbling, call sapp_consume_event() from within + the sokol_app.h event callback. + + OPTIONAL: DON'T HIJACK main() (#define SOKOL_NO_ENTRY) + ====================================================== + NOTE: SOKOL_NO_ENTRY and sapp_run() is currently not supported on Android. + + In its default configuration, sokol_app.h "hijacks" the platform's + standard main() function. This was done because different platforms + have different entry point conventions which are not compatible with + C's main() (for instance WinMain on Windows has completely different + arguments). However, this "main hijacking" posed a problem for + usage scenarios like integrating sokol_app.h with other languages than + C or C++, so an alternative SOKOL_NO_ENTRY mode has been added + in which the user code provides the platform's main function: + + - define SOKOL_NO_ENTRY before including the sokol_app.h implementation + - do *not* provide a sokol_main() function + - instead provide the standard main() function of the platform + - from the main function, call the function ```sapp_run()``` which + takes a pointer to an ```sapp_desc``` structure. + - from here on```sapp_run()``` takes over control and calls the provided + init-, frame-, event- and cleanup-callbacks just like in the default model. + + sapp_run() behaves differently across platforms: + + - on some platforms, sapp_run() will return when the application quits + - on other platforms, sapp_run() will never return, even when the + application quits (the operating system is free to simply terminate + the application at any time) + - on Emscripten specifically, sapp_run() will return immediately while + the frame callback keeps being called + + This different behaviour of sapp_run() essentially means that there shouldn't + be any code *after* sapp_run(), because that may either never be called, or in + case of Emscripten will be called at an unexpected time (at application start). + + An application also should not depend on the cleanup-callback being called + when cross-platform compatibility is required. + + Since sapp_run() returns immediately on Emscripten you shouldn't activate + the 'EXIT_RUNTIME' linker option (this is disabled by default when compiling + for the browser target), since the C/C++ exit runtime would be called immediately at + application start, causing any global objects to be destroyed and global + variables to be zeroed. + + WINDOWS CONSOLE OUTPUT + ====================== + On Windows, regular windowed applications don't show any stdout/stderr text + output, which can be a bit of a hassle for printf() debugging or generally + logging text to the console. Also, console output by default uses a local + codepage setting and thus international UTF-8 encoded text is printed + as garbage. + + To help with these issues, sokol_app.h can be configured at startup + via the following Windows-specific sapp_desc flags: + + sapp_desc.win32_console_utf8 (default: false) + When set to true, the output console codepage will be switched + to UTF-8 (and restored to the original codepage on exit) + + sapp_desc.win32_console_attach (default: false) + When set to true, stdout and stderr will be attached to the + console of the parent process (if the parent process actually + has a console). This means that if the application was started + in a command line window, stdout and stderr output will be printed + to the terminal, just like a regular command line program. But if + the application is started via double-click, it will behave like + a regular UI application, and stdout/stderr will not be visible. + + sapp_desc.win32_console_create (default: false) + When set to true, a new console window will be created and + stdout/stderr will be redirected to that console window. It + doesn't matter if the application is started from the command + line or via double-click. + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + sapp_desc sokol_main(int argc, char* argv[]) { + return (sapp_desc){ + // ... + .allocator = { + .alloc_fn = my_alloc, + .free_fn = my_free, + .user_data = ..., + } + }; + } + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_app.h + itself though, not any allocations in OS libraries. + + + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + sapp_desc sokol_main(int argc, char* argv[]) { + return (sapp_desc) { + ... + .logger.func = slog_func, + }; + } + + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'sapp' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SAPP_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_app.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-app like this: + + sapp_desc sokol_main(int argc, char* argv[]) { + return (sapp_desc) { + ... + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }; + } + + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. + + + TEMP NOTE DUMP + ============== + - sapp_desc needs a bool whether to initialize depth-stencil surface + - the Android implementation calls cleanup_cb() and destroys the egl context in onDestroy + at the latest but should do it earlier, in onStop, as an app is "killable" after onStop + on Android Honeycomb and later (it can't be done at the moment as the app may be started + again after onStop and the sokol lifecycle does not yet handle context teardown/bringup) + + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_APP_INCLUDED (1) +#include // size_t +#include +#include + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_APP_API_DECL) +#define SOKOL_APP_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_APP_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_APP_IMPL) +#define SOKOL_APP_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_APP_API_DECL __declspec(dllimport) +#else +#define SOKOL_APP_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* misc constants */ +enum { + SAPP_MAX_TOUCHPOINTS = 8, + SAPP_MAX_MOUSEBUTTONS = 3, + SAPP_MAX_KEYCODES = 512, + SAPP_MAX_ICONIMAGES = 8, +}; + +/* + sapp_event_type + + The type of event that's passed to the event handler callback + in the sapp_event.type field. These are not just "traditional" + input events, but also notify the application about state changes + or other user-invoked actions. +*/ +typedef enum sapp_event_type { + SAPP_EVENTTYPE_INVALID, + SAPP_EVENTTYPE_KEY_DOWN, + SAPP_EVENTTYPE_KEY_UP, + SAPP_EVENTTYPE_CHAR, + SAPP_EVENTTYPE_MOUSE_DOWN, + SAPP_EVENTTYPE_MOUSE_UP, + SAPP_EVENTTYPE_MOUSE_SCROLL, + SAPP_EVENTTYPE_MOUSE_MOVE, + SAPP_EVENTTYPE_MOUSE_ENTER, + SAPP_EVENTTYPE_MOUSE_LEAVE, + SAPP_EVENTTYPE_TOUCHES_BEGAN, + SAPP_EVENTTYPE_TOUCHES_MOVED, + SAPP_EVENTTYPE_TOUCHES_ENDED, + SAPP_EVENTTYPE_TOUCHES_CANCELLED, + SAPP_EVENTTYPE_RESIZED, + SAPP_EVENTTYPE_ICONIFIED, + SAPP_EVENTTYPE_RESTORED, + SAPP_EVENTTYPE_FOCUSED, + SAPP_EVENTTYPE_UNFOCUSED, + SAPP_EVENTTYPE_SUSPENDED, + SAPP_EVENTTYPE_RESUMED, + SAPP_EVENTTYPE_QUIT_REQUESTED, + SAPP_EVENTTYPE_CLIPBOARD_PASTED, + SAPP_EVENTTYPE_FILES_DROPPED, + _SAPP_EVENTTYPE_NUM, + _SAPP_EVENTTYPE_FORCE_U32 = 0x7FFFFFFF +} sapp_event_type; + +/* + sapp_keycode + + The 'virtual keycode' of a KEY_DOWN or KEY_UP event in the + struct field sapp_event.key_code. + + Note that the keycode values are identical with GLFW. +*/ +typedef enum sapp_keycode { + SAPP_KEYCODE_INVALID = 0, + SAPP_KEYCODE_SPACE = 32, + SAPP_KEYCODE_APOSTROPHE = 39, /* ' */ + SAPP_KEYCODE_COMMA = 44, /* , */ + SAPP_KEYCODE_MINUS = 45, /* - */ + SAPP_KEYCODE_PERIOD = 46, /* . */ + SAPP_KEYCODE_SLASH = 47, /* / */ + SAPP_KEYCODE_0 = 48, + SAPP_KEYCODE_1 = 49, + SAPP_KEYCODE_2 = 50, + SAPP_KEYCODE_3 = 51, + SAPP_KEYCODE_4 = 52, + SAPP_KEYCODE_5 = 53, + SAPP_KEYCODE_6 = 54, + SAPP_KEYCODE_7 = 55, + SAPP_KEYCODE_8 = 56, + SAPP_KEYCODE_9 = 57, + SAPP_KEYCODE_SEMICOLON = 59, /* ; */ + SAPP_KEYCODE_EQUAL = 61, /* = */ + SAPP_KEYCODE_A = 65, + SAPP_KEYCODE_B = 66, + SAPP_KEYCODE_C = 67, + SAPP_KEYCODE_D = 68, + SAPP_KEYCODE_E = 69, + SAPP_KEYCODE_F = 70, + SAPP_KEYCODE_G = 71, + SAPP_KEYCODE_H = 72, + SAPP_KEYCODE_I = 73, + SAPP_KEYCODE_J = 74, + SAPP_KEYCODE_K = 75, + SAPP_KEYCODE_L = 76, + SAPP_KEYCODE_M = 77, + SAPP_KEYCODE_N = 78, + SAPP_KEYCODE_O = 79, + SAPP_KEYCODE_P = 80, + SAPP_KEYCODE_Q = 81, + SAPP_KEYCODE_R = 82, + SAPP_KEYCODE_S = 83, + SAPP_KEYCODE_T = 84, + SAPP_KEYCODE_U = 85, + SAPP_KEYCODE_V = 86, + SAPP_KEYCODE_W = 87, + SAPP_KEYCODE_X = 88, + SAPP_KEYCODE_Y = 89, + SAPP_KEYCODE_Z = 90, + SAPP_KEYCODE_LEFT_BRACKET = 91, /* [ */ + SAPP_KEYCODE_BACKSLASH = 92, /* \ */ + SAPP_KEYCODE_RIGHT_BRACKET = 93, /* ] */ + SAPP_KEYCODE_GRAVE_ACCENT = 96, /* ` */ + SAPP_KEYCODE_WORLD_1 = 161, /* non-US #1 */ + SAPP_KEYCODE_WORLD_2 = 162, /* non-US #2 */ + SAPP_KEYCODE_ESCAPE = 256, + SAPP_KEYCODE_ENTER = 257, + SAPP_KEYCODE_TAB = 258, + SAPP_KEYCODE_BACKSPACE = 259, + SAPP_KEYCODE_INSERT = 260, + SAPP_KEYCODE_DELETE = 261, + SAPP_KEYCODE_RIGHT = 262, + SAPP_KEYCODE_LEFT = 263, + SAPP_KEYCODE_DOWN = 264, + SAPP_KEYCODE_UP = 265, + SAPP_KEYCODE_PAGE_UP = 266, + SAPP_KEYCODE_PAGE_DOWN = 267, + SAPP_KEYCODE_HOME = 268, + SAPP_KEYCODE_END = 269, + SAPP_KEYCODE_CAPS_LOCK = 280, + SAPP_KEYCODE_SCROLL_LOCK = 281, + SAPP_KEYCODE_NUM_LOCK = 282, + SAPP_KEYCODE_PRINT_SCREEN = 283, + SAPP_KEYCODE_PAUSE = 284, + SAPP_KEYCODE_F1 = 290, + SAPP_KEYCODE_F2 = 291, + SAPP_KEYCODE_F3 = 292, + SAPP_KEYCODE_F4 = 293, + SAPP_KEYCODE_F5 = 294, + SAPP_KEYCODE_F6 = 295, + SAPP_KEYCODE_F7 = 296, + SAPP_KEYCODE_F8 = 297, + SAPP_KEYCODE_F9 = 298, + SAPP_KEYCODE_F10 = 299, + SAPP_KEYCODE_F11 = 300, + SAPP_KEYCODE_F12 = 301, + SAPP_KEYCODE_F13 = 302, + SAPP_KEYCODE_F14 = 303, + SAPP_KEYCODE_F15 = 304, + SAPP_KEYCODE_F16 = 305, + SAPP_KEYCODE_F17 = 306, + SAPP_KEYCODE_F18 = 307, + SAPP_KEYCODE_F19 = 308, + SAPP_KEYCODE_F20 = 309, + SAPP_KEYCODE_F21 = 310, + SAPP_KEYCODE_F22 = 311, + SAPP_KEYCODE_F23 = 312, + SAPP_KEYCODE_F24 = 313, + SAPP_KEYCODE_F25 = 314, + SAPP_KEYCODE_KP_0 = 320, + SAPP_KEYCODE_KP_1 = 321, + SAPP_KEYCODE_KP_2 = 322, + SAPP_KEYCODE_KP_3 = 323, + SAPP_KEYCODE_KP_4 = 324, + SAPP_KEYCODE_KP_5 = 325, + SAPP_KEYCODE_KP_6 = 326, + SAPP_KEYCODE_KP_7 = 327, + SAPP_KEYCODE_KP_8 = 328, + SAPP_KEYCODE_KP_9 = 329, + SAPP_KEYCODE_KP_DECIMAL = 330, + SAPP_KEYCODE_KP_DIVIDE = 331, + SAPP_KEYCODE_KP_MULTIPLY = 332, + SAPP_KEYCODE_KP_SUBTRACT = 333, + SAPP_KEYCODE_KP_ADD = 334, + SAPP_KEYCODE_KP_ENTER = 335, + SAPP_KEYCODE_KP_EQUAL = 336, + SAPP_KEYCODE_LEFT_SHIFT = 340, + SAPP_KEYCODE_LEFT_CONTROL = 341, + SAPP_KEYCODE_LEFT_ALT = 342, + SAPP_KEYCODE_LEFT_SUPER = 343, + SAPP_KEYCODE_RIGHT_SHIFT = 344, + SAPP_KEYCODE_RIGHT_CONTROL = 345, + SAPP_KEYCODE_RIGHT_ALT = 346, + SAPP_KEYCODE_RIGHT_SUPER = 347, + SAPP_KEYCODE_MENU = 348, +} sapp_keycode; + +/* + Android specific 'tool type' enum for touch events. This lets the + application check what type of input device was used for + touch events. + + NOTE: the values must remain in sync with the corresponding + Android SDK type, so don't change those. + + See https://developer.android.com/reference/android/view/MotionEvent#TOOL_TYPE_UNKNOWN +*/ +typedef enum sapp_android_tooltype { + SAPP_ANDROIDTOOLTYPE_UNKNOWN = 0, // TOOL_TYPE_UNKNOWN + SAPP_ANDROIDTOOLTYPE_FINGER = 1, // TOOL_TYPE_FINGER + SAPP_ANDROIDTOOLTYPE_STYLUS = 2, // TOOL_TYPE_STYLUS + SAPP_ANDROIDTOOLTYPE_MOUSE = 3, // TOOL_TYPE_MOUSE +} sapp_android_tooltype; + +/* + sapp_touchpoint + + Describes a single touchpoint in a multitouch event (TOUCHES_BEGAN, + TOUCHES_MOVED, TOUCHES_ENDED). + + Touch points are stored in the nested array sapp_event.touches[], + and the number of touches is stored in sapp_event.num_touches. +*/ +typedef struct sapp_touchpoint { + uintptr_t identifier; + float pos_x; + float pos_y; + sapp_android_tooltype android_tooltype; // only valid on Android + bool changed; +} sapp_touchpoint; + +/* + sapp_mousebutton + + The currently pressed mouse button in the events MOUSE_DOWN + and MOUSE_UP, stored in the struct field sapp_event.mouse_button. +*/ +typedef enum sapp_mousebutton { + SAPP_MOUSEBUTTON_LEFT = 0x0, + SAPP_MOUSEBUTTON_RIGHT = 0x1, + SAPP_MOUSEBUTTON_MIDDLE = 0x2, + SAPP_MOUSEBUTTON_INVALID = 0x100, +} sapp_mousebutton; + +/* + These are currently pressed modifier keys (and mouse buttons) which are + passed in the event struct field sapp_event.modifiers. +*/ +enum { + SAPP_MODIFIER_SHIFT = 0x1, // left or right shift key + SAPP_MODIFIER_CTRL = 0x2, // left or right control key + SAPP_MODIFIER_ALT = 0x4, // left or right alt key + SAPP_MODIFIER_SUPER = 0x8, // left or right 'super' key + SAPP_MODIFIER_LMB = 0x100, // left mouse button + SAPP_MODIFIER_RMB = 0x200, // right mouse button + SAPP_MODIFIER_MMB = 0x400, // middle mouse button +}; + +/* + sapp_event + + This is an all-in-one event struct passed to the event handler + user callback function. Note that it depends on the event + type what struct fields actually contain useful values, so you + should first check the event type before reading other struct + fields. +*/ +typedef struct sapp_event { + uint64_t frame_count; // current frame counter, always valid, useful for checking if two events were issued in the same frame + sapp_event_type type; // the event type, always valid + sapp_keycode key_code; // the virtual key code, only valid in KEY_UP, KEY_DOWN + uint32_t char_code; // the UTF-32 character code, only valid in CHAR events + bool key_repeat; // true if this is a key-repeat event, valid in KEY_UP, KEY_DOWN and CHAR + uint32_t modifiers; // current modifier keys, valid in all key-, char- and mouse-events + sapp_mousebutton mouse_button; // mouse button that was pressed or released, valid in MOUSE_DOWN, MOUSE_UP + float mouse_x; // current horizontal mouse position in pixels, always valid except during mouse lock + float mouse_y; // current vertical mouse position in pixels, always valid except during mouse lock + float mouse_dx; // relative horizontal mouse movement since last frame, always valid + float mouse_dy; // relative vertical mouse movement since last frame, always valid + float scroll_x; // horizontal mouse wheel scroll distance, valid in MOUSE_SCROLL events + float scroll_y; // vertical mouse wheel scroll distance, valid in MOUSE_SCROLL events + int num_touches; // number of valid items in the touches[] array + sapp_touchpoint touches[SAPP_MAX_TOUCHPOINTS]; // current touch points, valid in TOUCHES_BEGIN, TOUCHES_MOVED, TOUCHES_ENDED + int window_width; // current window- and framebuffer sizes in pixels, always valid + int window_height; + int framebuffer_width; // = window_width * dpi_scale + int framebuffer_height; // = window_height * dpi_scale +} sapp_event; + +/* + sg_range + + A general pointer/size-pair struct and constructor macros for passing binary blobs + into sokol_app.h. +*/ +typedef struct sapp_range { + const void* ptr; + size_t size; +} sapp_range; +// disabling this for every includer isn't great, but the warnings are also quite pointless +#if defined(_MSC_VER) +#pragma warning(disable:4221) /* /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y' */ +#pragma warning(disable:4204) /* VS2015: nonstandard extension used: non-constant aggregate initializer */ +#endif +#if defined(__cplusplus) +#define SAPP_RANGE(x) sapp_range{ &x, sizeof(x) } +#else +#define SAPP_RANGE(x) (sapp_range){ &x, sizeof(x) } +#endif + +/* + sapp_image_desc + + This is used to describe image data to sokol_app.h (at first, window + icons, later maybe cursor images). + + Note that the actual image pixel format depends on the use case: + + - window icon pixels are RGBA8 +*/ +typedef struct sapp_image_desc { + int width; + int height; + sapp_range pixels; +} sapp_image_desc; + +/* + sapp_icon_desc + + An icon description structure for use in sapp_desc.icon and + sapp_set_icon(). + + When setting a custom image, the application can provide a number of + candidates differing in size, and sokol_app.h will pick the image(s) + closest to the size expected by the platform's window system. + + To set sokol-app's default icon, set .sokol_default to true. + + Otherwise provide candidate images of different sizes in the + images[] array. + + If both the sokol_default flag is set to true, any image candidates + will be ignored and the sokol_app.h default icon will be set. +*/ +typedef struct sapp_icon_desc { + bool sokol_default; + sapp_image_desc images[SAPP_MAX_ICONIMAGES]; +} sapp_icon_desc; + +/* + sapp_allocator + + Used in sapp_desc to provide custom memory-alloc and -free functions + to sokol_app.h. If memory management should be overridden, both the + alloc_fn and free_fn function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct sapp_allocator { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} sapp_allocator; + +/* + sapp_log_item + + Log items are defined via X-Macros and expanded to an enum + 'sapp_log_item', and in debug mode to corresponding + human readable error messages. +*/ +#define _SAPP_LOG_ITEMS \ + _SAPP_LOGITEM_XMACRO(OK, "Ok") \ + _SAPP_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SAPP_LOGITEM_XMACRO(MACOS_INVALID_NSOPENGL_PROFILE, "macos: invalid NSOpenGLProfile (valid choices are 1.0, 3.2 and 4.1)") \ + _SAPP_LOGITEM_XMACRO(WIN32_LOAD_OPENGL32_DLL_FAILED, "failed loading opengl32.dll") \ + _SAPP_LOGITEM_XMACRO(WIN32_CREATE_HELPER_WINDOW_FAILED, "failed to create helper window") \ + _SAPP_LOGITEM_XMACRO(WIN32_HELPER_WINDOW_GETDC_FAILED, "failed to get helper window DC") \ + _SAPP_LOGITEM_XMACRO(WIN32_DUMMY_CONTEXT_SET_PIXELFORMAT_FAILED, "failed to set pixel format for dummy GL context") \ + _SAPP_LOGITEM_XMACRO(WIN32_CREATE_DUMMY_CONTEXT_FAILED, "failed to create dummy GL context") \ + _SAPP_LOGITEM_XMACRO(WIN32_DUMMY_CONTEXT_MAKE_CURRENT_FAILED, "failed to make dummy GL context current") \ + _SAPP_LOGITEM_XMACRO(WIN32_GET_PIXELFORMAT_ATTRIB_FAILED, "failed to get WGL pixel format attribute") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_FIND_PIXELFORMAT_FAILED, "failed to find matching WGL pixel format") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_DESCRIBE_PIXELFORMAT_FAILED, "failed to get pixel format descriptor") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_SET_PIXELFORMAT_FAILED, "failed to set selected pixel format") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_ARB_CREATE_CONTEXT_REQUIRED, "ARB_create_context required") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_ARB_CREATE_CONTEXT_PROFILE_REQUIRED, "ARB_create_context_profile required") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_OPENGL_3_2_NOT_SUPPORTED, "OpenGL 3.2 not supported by GL driver (ERROR_INVALID_VERSION_ARB)") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_OPENGL_PROFILE_NOT_SUPPORTED, "requested OpenGL profile not support by GL driver (ERROR_INVALID_PROFILE_ARB)") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_INCOMPATIBLE_DEVICE_CONTEXT, "CreateContextAttribsARB failed with ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_CREATE_CONTEXT_ATTRIBS_FAILED_OTHER, "CreateContextAttribsARB failed for other reason") \ + _SAPP_LOGITEM_XMACRO(WIN32_D3D11_CREATE_DEVICE_AND_SWAPCHAIN_WITH_DEBUG_FAILED, "D3D11CreateDeviceAndSwapChain() with D3D11_CREATE_DEVICE_DEBUG failed, retrying without debug flag.") \ + _SAPP_LOGITEM_XMACRO(WIN32_D3D11_GET_IDXGIFACTORY_FAILED, "could not obtain IDXGIFactory object") \ + _SAPP_LOGITEM_XMACRO(WIN32_D3D11_GET_IDXGIADAPTER_FAILED, "could not obtain IDXGIAdapter object") \ + _SAPP_LOGITEM_XMACRO(WIN32_D3D11_QUERY_INTERFACE_IDXGIDEVICE1_FAILED, "could not obtain IDXGIDevice1 interface") \ + _SAPP_LOGITEM_XMACRO(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_LOCK, "RegisterRawInputDevices() failed (on mouse lock)") \ + _SAPP_LOGITEM_XMACRO(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_UNLOCK, "RegisterRawInputDevices() failed (on mouse unlock)") \ + _SAPP_LOGITEM_XMACRO(WIN32_GET_RAW_INPUT_DATA_FAILED, "GetRawInputData() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_LOAD_LIBGL_FAILED, "failed to load libGL") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_LOAD_ENTRY_POINTS_FAILED, "failed to load GLX entry points") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_EXTENSION_NOT_FOUND, "GLX extension not found") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_QUERY_VERSION_FAILED, "failed to query GLX version") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_VERSION_TOO_LOW, "GLX version too low (need at least 1.3)") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_NO_GLXFBCONFIGS, "glXGetFBConfigs() returned no configs") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG, "failed to find a suitable GLXFBConfig") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_GET_VISUAL_FROM_FBCONFIG_FAILED, "glXGetVisualFromFBConfig failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_REQUIRED_EXTENSIONS_MISSING, "GLX extensions ARB_create_context and ARB_create_context_profile missing") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_CREATE_CONTEXT_FAILED, "Failed to create GL context via glXCreateContextAttribsARB") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_CREATE_WINDOW_FAILED, "glXCreateWindow() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_X11_CREATE_WINDOW_FAILED, "XCreateWindow() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_BIND_OPENGL_API_FAILED, "eglBindAPI(EGL_OPENGL_API) failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_BIND_OPENGL_ES_API_FAILED, "eglBindAPI(EGL_OPENGL_ES_API) failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_GET_DISPLAY_FAILED, "eglGetDisplay() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_INITIALIZE_FAILED, "eglInitialize() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_NO_CONFIGS, "eglChooseConfig() returned no configs") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_NO_NATIVE_VISUAL, "eglGetConfigAttrib() for EGL_NATIVE_VISUAL_ID failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_GET_VISUAL_INFO_FAILED, "XGetVisualInfo() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_CREATE_WINDOW_SURFACE_FAILED, "eglCreateWindowSurface() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_CREATE_CONTEXT_FAILED, "eglCreateContext() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_MAKE_CURRENT_FAILED, "eglMakeCurrent() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_X11_OPEN_DISPLAY_FAILED, "XOpenDisplay() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_X11_QUERY_SYSTEM_DPI_FAILED, "failed to query system dpi value, assuming default 96.0") \ + _SAPP_LOGITEM_XMACRO(LINUX_X11_DROPPED_FILE_URI_WRONG_SCHEME, "dropped file URL doesn't start with 'file://'") \ + _SAPP_LOGITEM_XMACRO(ANDROID_UNSUPPORTED_INPUT_EVENT_INPUT_CB, "unsupported input event encountered in _sapp_android_input_cb()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_UNSUPPORTED_INPUT_EVENT_MAIN_CB, "unsupported input event encountered in _sapp_android_main_cb()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_READ_MSG_FAILED, "failed to read message in _sapp_android_main_cb()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_WRITE_MSG_FAILED, "failed to write message in _sapp_android_msg") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_CREATE, "MSG_CREATE") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_RESUME, "MSG_RESUME") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_PAUSE, "MSG_PAUSE") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_FOCUS, "MSG_FOCUS") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_NO_FOCUS, "MSG_NO_FOCUS") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_SET_NATIVE_WINDOW, "MSG_SET_NATIVE_WINDOW") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_SET_INPUT_QUEUE, "MSG_SET_INPUT_QUEUE") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_DESTROY, "MSG_DESTROY") \ + _SAPP_LOGITEM_XMACRO(ANDROID_UNKNOWN_MSG, "unknown msg type received") \ + _SAPP_LOGITEM_XMACRO(ANDROID_LOOP_THREAD_STARTED, "loop thread started") \ + _SAPP_LOGITEM_XMACRO(ANDROID_LOOP_THREAD_DONE, "loop thread done") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONSTART, "NativeActivity onStart()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONRESUME, "NativeActivity onResume") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONSAVEINSTANCESTATE, "NativeActivity onSaveInstanceState") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONWINDOWFOCUSCHANGED, "NativeActivity onWindowFocusChanged") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONPAUSE, "NativeActivity onPause") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONSTOP, "NativeActivity onStop()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWCREATED, "NativeActivity onNativeWindowCreated") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWDESTROYED, "NativeActivity onNativeWindowDestroyed") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUECREATED, "NativeActivity onInputQueueCreated") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUEDESTROYED, "NativeActivity onInputQueueDestroyed") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONCONFIGURATIONCHANGED, "NativeActivity onConfigurationChanged") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONLOWMEMORY, "NativeActivity onLowMemory") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONDESTROY, "NativeActivity onDestroy") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_DONE, "NativeActivity done") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONCREATE, "NativeActivity onCreate") \ + _SAPP_LOGITEM_XMACRO(ANDROID_CREATE_THREAD_PIPE_FAILED, "failed to create thread pipe") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS, "NativeActivity successfully created") \ + _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_SURFACE_FAILED, "wgpu: failed to create surface for swapchain") \ + _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_SWAPCHAIN_FAILED, "wgpu: failed to create swapchain object") \ + _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_TEXTURE_FAILED, "wgpu: failed to create depth-stencil texture for swapchain") \ + _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_VIEW_FAILED, "wgpu: failed to create view object for swapchain depth-stencil texture") \ + _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_MSAA_TEXTURE_FAILED, "wgpu: failed to create msaa texture for swapchain") \ + _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_MSAA_VIEW_FAILED, "wgpu: failed to create view object for swapchain msaa texture") \ + _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_DEVICE_STATUS_ERROR, "wgpu: requesting device failed with status 'error'") \ + _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_DEVICE_STATUS_UNKNOWN, "wgpu: requesting device failed with status 'unknown'") \ + _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_ADAPTER_STATUS_UNAVAILABLE, "wgpu: requesting adapter failed with 'unavailable'") \ + _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_ADAPTER_STATUS_ERROR, "wgpu: requesting adapter failed with status 'error'") \ + _SAPP_LOGITEM_XMACRO(WGPU_REQUEST_ADAPTER_STATUS_UNKNOWN, "wgpu: requesting adapter failed with status 'unknown'") \ + _SAPP_LOGITEM_XMACRO(WGPU_CREATE_INSTANCE_FAILED, "wgpu: failed to create instance") \ + _SAPP_LOGITEM_XMACRO(IMAGE_DATA_SIZE_MISMATCH, "image data size mismatch (must be width*height*4 bytes)") \ + _SAPP_LOGITEM_XMACRO(DROPPED_FILE_PATH_TOO_LONG, "dropped file path too long (sapp_desc.max_dropped_filed_path_length)") \ + _SAPP_LOGITEM_XMACRO(CLIPBOARD_STRING_TOO_BIG, "clipboard string didn't fit into clipboard buffer") \ + +#define _SAPP_LOGITEM_XMACRO(item,msg) SAPP_LOGITEM_##item, +typedef enum sapp_log_item { + _SAPP_LOG_ITEMS +} sapp_log_item; +#undef _SAPP_LOGITEM_XMACRO + +/* + sapp_logger + + Used in sapp_desc to provide a logging function. Please be aware that + without logging function, sokol-app will be completely silent, e.g. it will + not report errors or warnings. For maximum error verbosity, compile in + debug mode (e.g. NDEBUG *not* defined) and install a logger (for instance + the standard logging function from sokol_log.h). +*/ +typedef struct sapp_logger { + void (*func)( + const char* tag, // always "sapp" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SAPP_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_app.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} sapp_logger; + +typedef struct sapp_desc { + void (*init_cb)(void); // these are the user-provided callbacks without user data + void (*frame_cb)(void); + void (*cleanup_cb)(void); + void (*event_cb)(const sapp_event*); + + void* user_data; // these are the user-provided callbacks with user data + void (*init_userdata_cb)(void*); + void (*frame_userdata_cb)(void*); + void (*cleanup_userdata_cb)(void*); + void (*event_userdata_cb)(const sapp_event*, void*); + + int width; // the preferred width of the window / canvas + int height; // the preferred height of the window / canvas + int sample_count; // MSAA sample count + int swap_interval; // the preferred swap interval (ignored on some platforms) + bool high_dpi; // whether the rendering canvas is full-resolution on HighDPI displays + bool fullscreen; // whether the window should be created in fullscreen mode + bool alpha; // whether the framebuffer should have an alpha channel (ignored on some platforms) + const char* window_title; // the window title as UTF-8 encoded string + bool enable_clipboard; // enable clipboard access, default is false + int clipboard_size; // max size of clipboard content in bytes + bool enable_dragndrop; // enable file dropping (drag'n'drop), default is false + int max_dropped_files; // max number of dropped files to process (default: 1) + int max_dropped_file_path_length; // max length in bytes of a dropped UTF-8 file path (default: 2048) + sapp_icon_desc icon; // the initial window icon to set + sapp_allocator allocator; // optional memory allocation overrides (default: malloc/free) + sapp_logger logger; // logging callback override (default: NO LOGGING!) + + // backend-specific options + int gl_major_version; // override GL major and minor version (the default GL version is 3.2) + int gl_minor_version; + bool win32_console_utf8; // if true, set the output console codepage to UTF-8 + bool win32_console_create; // if true, attach stdout/stderr to a new console window + bool win32_console_attach; // if true, attach stdout/stderr to parent process + const char* html5_canvas_name; // the name (id) of the HTML5 canvas element, default is "canvas" + bool html5_canvas_resize; // if true, the HTML5 canvas size is set to sapp_desc.width/height, otherwise canvas size is tracked + bool html5_preserve_drawing_buffer; // HTML5 only: whether to preserve default framebuffer content between frames + bool html5_premultiplied_alpha; // HTML5 only: whether the rendered pixels use premultiplied alpha convention + bool html5_ask_leave_site; // initial state of the internal html5_ask_leave_site flag (see sapp_html5_ask_leave_site()) + bool html5_bubble_mouse_events; // if true, mouse events will bubble up to the web page + bool html5_bubble_touch_events; // same for touch events + bool html5_bubble_wheel_events; // same for wheel events + bool html5_bubble_key_events; // if true, bubble up *all* key events to browser, not just key events that represent characters + bool html5_bubble_char_events; // if true, bubble up character events to browser + bool html5_use_emsc_set_main_loop; // if true, use emscripten_set_main_loop() instead of emscripten_request_animation_frame_loop() + bool html5_emsc_set_main_loop_simulate_infinite_loop; // this will be passed as the simulate_infinite_loop arg to emscripten_set_main_loop() + bool ios_keyboard_resizes_canvas; // if true, showing the iOS keyboard shrinks the canvas +} sapp_desc; + +/* HTML5 specific: request and response structs for + asynchronously loading dropped-file content. +*/ +typedef enum sapp_html5_fetch_error { + SAPP_HTML5_FETCH_ERROR_NO_ERROR, + SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL, + SAPP_HTML5_FETCH_ERROR_OTHER, +} sapp_html5_fetch_error; + +typedef struct sapp_html5_fetch_response { + bool succeeded; // true if the loading operation has succeeded + sapp_html5_fetch_error error_code; + int file_index; // index of the dropped file (0..sapp_get_num_dropped_filed()-1) + sapp_range data; // pointer and size of the fetched data (data.ptr == buffer.ptr, data.size <= buffer.size) + sapp_range buffer; // the user-provided buffer ptr/size pair (buffer.ptr == data.ptr, buffer.size >= data.size) + void* user_data; // user-provided user data pointer +} sapp_html5_fetch_response; + +typedef struct sapp_html5_fetch_request { + int dropped_file_index; // 0..sapp_get_num_dropped_files()-1 + void (*callback)(const sapp_html5_fetch_response*); // response callback function pointer (required) + sapp_range buffer; // ptr/size of a memory buffer to load the data into + void* user_data; // optional userdata pointer +} sapp_html5_fetch_request; + +/* + sapp_mouse_cursor + + Predefined cursor image definitions, set with sapp_set_mouse_cursor(sapp_mouse_cursor cursor) +*/ +typedef enum sapp_mouse_cursor { + SAPP_MOUSECURSOR_DEFAULT = 0, // equivalent with system default cursor + SAPP_MOUSECURSOR_ARROW, + SAPP_MOUSECURSOR_IBEAM, + SAPP_MOUSECURSOR_CROSSHAIR, + SAPP_MOUSECURSOR_POINTING_HAND, + SAPP_MOUSECURSOR_RESIZE_EW, + SAPP_MOUSECURSOR_RESIZE_NS, + SAPP_MOUSECURSOR_RESIZE_NWSE, + SAPP_MOUSECURSOR_RESIZE_NESW, + SAPP_MOUSECURSOR_RESIZE_ALL, + SAPP_MOUSECURSOR_NOT_ALLOWED, + _SAPP_MOUSECURSOR_NUM, +} sapp_mouse_cursor; + +/* user-provided functions */ +extern sapp_desc sokol_main(int argc, char* argv[]); + +/* returns true after sokol-app has been initialized */ +SOKOL_APP_API_DECL bool sapp_isvalid(void); +/* returns the current framebuffer width in pixels */ +SOKOL_APP_API_DECL int sapp_width(void); +/* same as sapp_width(), but returns float */ +SOKOL_APP_API_DECL float sapp_widthf(void); +/* returns the current framebuffer height in pixels */ +SOKOL_APP_API_DECL int sapp_height(void); +/* same as sapp_height(), but returns float */ +SOKOL_APP_API_DECL float sapp_heightf(void); +/* get default framebuffer color pixel format */ +SOKOL_APP_API_DECL int sapp_color_format(void); +/* get default framebuffer depth pixel format */ +SOKOL_APP_API_DECL int sapp_depth_format(void); +/* get default framebuffer sample count */ +SOKOL_APP_API_DECL int sapp_sample_count(void); +/* returns true when high_dpi was requested and actually running in a high-dpi scenario */ +SOKOL_APP_API_DECL bool sapp_high_dpi(void); +/* returns the dpi scaling factor (window pixels to framebuffer pixels) */ +SOKOL_APP_API_DECL float sapp_dpi_scale(void); +/* show or hide the mobile device onscreen keyboard */ +SOKOL_APP_API_DECL void sapp_show_keyboard(bool show); +/* return true if the mobile device onscreen keyboard is currently shown */ +SOKOL_APP_API_DECL bool sapp_keyboard_shown(void); +/* query fullscreen mode */ +SOKOL_APP_API_DECL bool sapp_is_fullscreen(void); +/* toggle fullscreen mode */ +SOKOL_APP_API_DECL void sapp_toggle_fullscreen(void); +/* show or hide the mouse cursor */ +SOKOL_APP_API_DECL void sapp_show_mouse(bool show); +/* show or hide the mouse cursor */ +SOKOL_APP_API_DECL bool sapp_mouse_shown(void); +/* enable/disable mouse-pointer-lock mode */ +SOKOL_APP_API_DECL void sapp_lock_mouse(bool lock); +/* return true if in mouse-pointer-lock mode (this may toggle a few frames later) */ +SOKOL_APP_API_DECL bool sapp_mouse_locked(void); +/* set mouse cursor type */ +SOKOL_APP_API_DECL void sapp_set_mouse_cursor(sapp_mouse_cursor cursor); +/* get current mouse cursor type */ +SOKOL_APP_API_DECL sapp_mouse_cursor sapp_get_mouse_cursor(void); +/* return the userdata pointer optionally provided in sapp_desc */ +SOKOL_APP_API_DECL void* sapp_userdata(void); +/* return a copy of the sapp_desc structure */ +SOKOL_APP_API_DECL sapp_desc sapp_query_desc(void); +/* initiate a "soft quit" (sends SAPP_EVENTTYPE_QUIT_REQUESTED) */ +SOKOL_APP_API_DECL void sapp_request_quit(void); +/* cancel a pending quit (when SAPP_EVENTTYPE_QUIT_REQUESTED has been received) */ +SOKOL_APP_API_DECL void sapp_cancel_quit(void); +/* initiate a "hard quit" (quit application without sending SAPP_EVENTTYPE_QUIT_REQUESTED) */ +SOKOL_APP_API_DECL void sapp_quit(void); +/* call from inside event callback to consume the current event (don't forward to platform) */ +SOKOL_APP_API_DECL void sapp_consume_event(void); +/* get the current frame counter (for comparison with sapp_event.frame_count) */ +SOKOL_APP_API_DECL uint64_t sapp_frame_count(void); +/* get an averaged/smoothed frame duration in seconds */ +SOKOL_APP_API_DECL double sapp_frame_duration(void); +/* write string into clipboard */ +SOKOL_APP_API_DECL void sapp_set_clipboard_string(const char* str); +/* read string from clipboard (usually during SAPP_EVENTTYPE_CLIPBOARD_PASTED) */ +SOKOL_APP_API_DECL const char* sapp_get_clipboard_string(void); +/* set the window title (only on desktop platforms) */ +SOKOL_APP_API_DECL void sapp_set_window_title(const char* str); +/* set the window icon (only on Windows and Linux) */ +SOKOL_APP_API_DECL void sapp_set_icon(const sapp_icon_desc* icon_desc); +/* gets the total number of dropped files (after an SAPP_EVENTTYPE_FILES_DROPPED event) */ +SOKOL_APP_API_DECL int sapp_get_num_dropped_files(void); +/* gets the dropped file paths */ +SOKOL_APP_API_DECL const char* sapp_get_dropped_file_path(int index); + +/* special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub) */ +SOKOL_APP_API_DECL void sapp_run(const sapp_desc* desc); + +/* EGL: get EGLDisplay object */ +SOKOL_APP_API_DECL const void* sapp_egl_get_display(void); +/* EGL: get EGLContext object */ +SOKOL_APP_API_DECL const void* sapp_egl_get_context(void); + +/* HTML5: enable or disable the hardwired "Leave Site?" dialog box */ +SOKOL_APP_API_DECL void sapp_html5_ask_leave_site(bool ask); +/* HTML5: get byte size of a dropped file */ +SOKOL_APP_API_DECL uint32_t sapp_html5_get_dropped_file_size(int index); +/* HTML5: asynchronously load the content of a dropped file */ +SOKOL_APP_API_DECL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request); + +/* Metal: get bridged pointer to Metal device object */ +SOKOL_APP_API_DECL const void* sapp_metal_get_device(void); +/* Metal: get bridged pointer to MTKView's current drawable of type CAMetalDrawable */ +SOKOL_APP_API_DECL const void* sapp_metal_get_current_drawable(void); +/* Metal: get bridged pointer to MTKView's depth-stencil texture of type MTLTexture */ +SOKOL_APP_API_DECL const void* sapp_metal_get_depth_stencil_texture(void); +/* Metal: get bridged pointer to MTKView's msaa-color-texture of type MTLTexture (may be null) */ +SOKOL_APP_API_DECL const void* sapp_metal_get_msaa_color_texture(void); +/* macOS: get bridged pointer to macOS NSWindow */ +SOKOL_APP_API_DECL const void* sapp_macos_get_window(void); +/* iOS: get bridged pointer to iOS UIWindow */ +SOKOL_APP_API_DECL const void* sapp_ios_get_window(void); + +/* D3D11: get pointer to ID3D11Device object */ +SOKOL_APP_API_DECL const void* sapp_d3d11_get_device(void); +/* D3D11: get pointer to ID3D11DeviceContext object */ +SOKOL_APP_API_DECL const void* sapp_d3d11_get_device_context(void); +/* D3D11: get pointer to IDXGISwapChain object */ +SOKOL_APP_API_DECL const void* sapp_d3d11_get_swap_chain(void); +/* D3D11: get pointer to ID3D11RenderTargetView object for rendering */ +SOKOL_APP_API_DECL const void* sapp_d3d11_get_render_view(void); +/* D3D11: get pointer ID3D11RenderTargetView object for msaa-resolve (may return null) */ +SOKOL_APP_API_DECL const void* sapp_d3d11_get_resolve_view(void); +/* D3D11: get pointer ID3D11DepthStencilView */ +SOKOL_APP_API_DECL const void* sapp_d3d11_get_depth_stencil_view(void); +/* Win32: get the HWND window handle */ +SOKOL_APP_API_DECL const void* sapp_win32_get_hwnd(void); + +/* WebGPU: get WGPUDevice handle */ +SOKOL_APP_API_DECL const void* sapp_wgpu_get_device(void); +/* WebGPU: get swapchain's WGPUTextureView handle for rendering */ +SOKOL_APP_API_DECL const void* sapp_wgpu_get_render_view(void); +/* WebGPU: get swapchain's MSAA-resolve WGPUTextureView (may return null) */ +SOKOL_APP_API_DECL const void* sapp_wgpu_get_resolve_view(void); +/* WebGPU: get swapchain's WGPUTextureView for the depth-stencil surface */ +SOKOL_APP_API_DECL const void* sapp_wgpu_get_depth_stencil_view(void); + +/* GL: get framebuffer object */ +SOKOL_APP_API_DECL uint32_t sapp_gl_get_framebuffer(void); +/* GL: get major version (only valid for desktop GL) */ +SOKOL_APP_API_DECL int sapp_gl_get_major_version(void); +/* GL: get minor version (only valid for desktop GL) */ +SOKOL_APP_API_DECL int sapp_gl_get_minor_version(void); + +/* Android: get native activity handle */ +SOKOL_APP_API_DECL const void* sapp_android_get_native_activity(void); + +#ifdef __cplusplus +} /* extern "C" */ + +/* reference-based equivalents for C++ */ +inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } + +#endif + +// this WinRT specific hack is required when wWinMain is in a static library +#if defined(_MSC_VER) && defined(UNICODE) +#include +#if defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#pragma comment(linker, "/include:wWinMain") +#endif +#endif + +#endif // SOKOL_APP_INCLUDED + +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation +#ifdef SOKOL_APP_IMPL +#define SOKOL_APP_IMPL_INCLUDED (1) + +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sapp_desc.allocator to override memory allocation functions" +#endif + +#include // malloc, free +#include // memset +#include // size_t +#include // roundf + +// helper macros +#define _sapp_def(val, def) (((val) == 0) ? (def) : (val)) +#define _sapp_absf(a) (((a)<0.0f)?-(a):(a)) + +#define _SAPP_MAX_TITLE_LENGTH (128) +#define _SAPP_FALLBACK_DEFAULT_WINDOW_WIDTH (640) +#define _SAPP_FALLBACK_DEFAULT_WINDOW_HEIGHT (480) +// NOTE: the pixel format values *must* be compatible with sg_pixel_format +#define _SAPP_PIXELFORMAT_RGBA8 (23) +#define _SAPP_PIXELFORMAT_BGRA8 (28) +#define _SAPP_PIXELFORMAT_DEPTH (43) +#define _SAPP_PIXELFORMAT_DEPTH_STENCIL (44) + +// check if the config defines are alright +#if defined(__APPLE__) + // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting + #if !defined(__cplusplus) + #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields) + #error "sokol_app.h requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)" + #endif + #endif + #define _SAPP_APPLE (1) + #include + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + /* MacOS */ + #define _SAPP_MACOS (1) + #if !defined(SOKOL_METAL) && !defined(SOKOL_GLCORE) + #error("sokol_app.h: unknown 3D API selected for MacOS, must be SOKOL_METAL or SOKOL_GLCORE") + #endif + #else + /* iOS or iOS Simulator */ + #define _SAPP_IOS (1) + #if !defined(SOKOL_METAL) && !defined(SOKOL_GLES3) + #error("sokol_app.h: unknown 3D API selected for iOS, must be SOKOL_METAL or SOKOL_GLES3") + #endif + #endif +#elif defined(__EMSCRIPTEN__) + /* emscripten (asm.js or wasm) */ + #define _SAPP_EMSCRIPTEN (1) + #if !defined(SOKOL_GLES3) && !defined(SOKOL_WGPU) + #error("sokol_app.h: unknown 3D API selected for emscripten, must be SOKOL_GLES3 or SOKOL_WGPU") + #endif +#elif defined(_WIN32) + /* Windows (D3D11 or GL) */ + #define _SAPP_WIN32 (1) + #if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE) && !defined(SOKOL_NOAPI) + #error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11, SOKOL_GLCORE or SOKOL_NOAPI") + #endif +#elif defined(__ANDROID__) + /* Android */ + #define _SAPP_ANDROID (1) + #if !defined(SOKOL_GLES3) + #error("sokol_app.h: unknown 3D API selected for Android, must be SOKOL_GLES3") + #endif + #if defined(SOKOL_NO_ENTRY) + #error("sokol_app.h: SOKOL_NO_ENTRY is not supported on Android") + #endif +#elif defined(__linux__) || defined(__unix__) + /* Linux */ + #define _SAPP_LINUX (1) + #if defined(SOKOL_GLCORE) + #if !defined(SOKOL_FORCE_EGL) + #define _SAPP_GLX (1) + #endif + #define GL_GLEXT_PROTOTYPES + #include + #elif defined(SOKOL_GLES3) + #include + #include + #else + #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE, SOKOL_GLES3") + #endif +#else +#error "sokol_app.h: Unknown platform" +#endif + +#if defined(SOKOL_GLCORE) || defined(SOKOL_GLES3) + #define _SAPP_ANY_GL (1) +#endif + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef SOKOL_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#endif + +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif +#ifndef _SOKOL_UNUSED + #define _SOKOL_UNUSED(x) (void)(x) +#endif + +#if defined(_SAPP_APPLE) + #if defined(SOKOL_METAL) + #import + #import + #endif + #if defined(_SAPP_MACOS) + #if defined(_SAPP_ANY_GL) + #ifndef GL_SILENCE_DEPRECATION + #define GL_SILENCE_DEPRECATION + #endif + #include + #include + #endif + #elif defined(_SAPP_IOS) + #import + #if defined(_SAPP_ANY_GL) + #import + #include + #endif + #endif + #include + #include +#elif defined(_SAPP_EMSCRIPTEN) + #if defined(SOKOL_WGPU) + #include + #endif + #if defined(SOKOL_GLES3) + #include + #endif + #include + #include +#elif defined(_SAPP_WIN32) + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ + #pragma warning(disable:4204) /* nonstandard extension used: non-constant aggregate initializer */ + #pragma warning(disable:4054) /* 'type cast': from function pointer */ + #pragma warning(disable:4055) /* 'type cast': from data pointer */ + #pragma warning(disable:4505) /* unreferenced local function has been removed */ + #pragma warning(disable:4115) /* /W4: 'ID3D11ModuleInstance': named type definition in parentheses (in d3d11.h) */ + #endif + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #include + #include + #if !defined(SOKOL_NO_ENTRY) // if SOKOL_NO_ENTRY is defined, it's the applications' responsibility to use the right subsystem + #if defined(SOKOL_WIN32_FORCE_MAIN) + #pragma comment (linker, "/subsystem:console") + #else + #pragma comment (linker, "/subsystem:windows") + #endif + #endif + #include /* freopen_s() */ + #include /* wcslen() */ + + #pragma comment (lib, "kernel32") + #pragma comment (lib, "user32") + #pragma comment (lib, "shell32") /* CommandLineToArgvW, DragQueryFileW, DragFinished */ + #pragma comment (lib, "gdi32") + #if defined(SOKOL_D3D11) + #pragma comment (lib, "dxgi") + #pragma comment (lib, "d3d11") + #endif + + #if defined(SOKOL_D3D11) + #ifndef D3D11_NO_HELPERS + #define D3D11_NO_HELPERS + #endif + #include + #include + // DXGI_SWAP_EFFECT_FLIP_DISCARD is only defined in newer Windows SDKs, so don't depend on it + #define _SAPP_DXGI_SWAP_EFFECT_FLIP_DISCARD (4) + #endif + #ifndef WM_MOUSEHWHEEL /* see https://github.com/floooh/sokol/issues/138 */ + #define WM_MOUSEHWHEEL (0x020E) + #endif + #ifndef WM_DPICHANGED + #define WM_DPICHANGED (0x02E0) + #endif +#elif defined(_SAPP_ANDROID) + #include + #include + #include + #include + #include + #include + #include +#elif defined(_SAPP_LINUX) + #define GL_GLEXT_PROTOTYPES + #include + #include + #include + #include + #include + #include + #include + #include + #include /* XC_* font cursors */ + #include /* CARD32 */ + #if !defined(_SAPP_GLX) + #include + #endif + #include /* dlopen, dlsym, dlclose */ + #include /* LONG_MAX */ + #include /* only used a linker-guard, search for _sapp_linux_run() and see first comment */ + #include +#endif + +#if defined(_SAPP_APPLE) + // this is ARC compatible + #if defined(__cplusplus) + #define _SAPP_CLEAR_ARC_STRUCT(type, item) { item = type(); } + #else + #define _SAPP_CLEAR_ARC_STRUCT(type, item) { item = (type) { 0 }; } + #endif +#else + #define _SAPP_CLEAR_ARC_STRUCT(type, item) { _sapp_clear(&item, sizeof(item)); } +#endif + + +// ███████ ██████ █████ ███ ███ ███████ ████████ ██ ███ ███ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ████ ████ ██ ████ ██ ██ +// █████ ██████ ███████ ██ ████ ██ █████ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ████ ██████ +// +// >>frame timing +#define _SAPP_RING_NUM_SLOTS (256) +typedef struct { + int head; + int tail; + double buf[_SAPP_RING_NUM_SLOTS]; +} _sapp_ring_t; + +_SOKOL_PRIVATE int _sapp_ring_idx(int i) { + return i % _SAPP_RING_NUM_SLOTS; +} + +_SOKOL_PRIVATE void _sapp_ring_init(_sapp_ring_t* ring) { + ring->head = 0; + ring->tail = 0; +} + +_SOKOL_PRIVATE bool _sapp_ring_full(_sapp_ring_t* ring) { + return _sapp_ring_idx(ring->head + 1) == ring->tail; +} + +_SOKOL_PRIVATE bool _sapp_ring_empty(_sapp_ring_t* ring) { + return ring->head == ring->tail; +} + +_SOKOL_PRIVATE int _sapp_ring_count(_sapp_ring_t* ring) { + int count; + if (ring->head >= ring->tail) { + count = ring->head - ring->tail; + } + else { + count = (ring->head + _SAPP_RING_NUM_SLOTS) - ring->tail; + } + SOKOL_ASSERT((count >= 0) && (count < _SAPP_RING_NUM_SLOTS)); + return count; +} + +_SOKOL_PRIVATE void _sapp_ring_enqueue(_sapp_ring_t* ring, double val) { + SOKOL_ASSERT(!_sapp_ring_full(ring)); + ring->buf[ring->head] = val; + ring->head = _sapp_ring_idx(ring->head + 1); +} + +_SOKOL_PRIVATE double _sapp_ring_dequeue(_sapp_ring_t* ring) { + SOKOL_ASSERT(!_sapp_ring_empty(ring)); + double val = ring->buf[ring->tail]; + ring->tail = _sapp_ring_idx(ring->tail + 1); + return val; +} + +/* + NOTE: + + Q: Why not use CAMetalDrawable.presentedTime on macOS and iOS? + A: The value appears to be highly unstable during the first few + seconds, sometimes several frames are dropped in sequence, or + switch between 120 and 60 Hz for a few frames. Simply measuring + and averaging the frame time yielded a more stable frame duration. + Maybe switching to CVDisplayLink would yield better results. + Until then just measure the time. +*/ +typedef struct { + #if defined(_SAPP_APPLE) + struct { + mach_timebase_info_data_t timebase; + uint64_t start; + } mach; + #elif defined(_SAPP_EMSCRIPTEN) + // empty + #elif defined(_SAPP_WIN32) + struct { + LARGE_INTEGER freq; + LARGE_INTEGER start; + } win; + #else // Linux, Android, ... + #ifdef CLOCK_MONOTONIC + #define _SAPP_CLOCK_MONOTONIC CLOCK_MONOTONIC + #else + // on some embedded platforms, CLOCK_MONOTONIC isn't defined + #define _SAPP_CLOCK_MONOTONIC (1) + #endif + struct { + uint64_t start; + } posix; + #endif +} _sapp_timestamp_t; + +_SOKOL_PRIVATE int64_t _sapp_int64_muldiv(int64_t value, int64_t numer, int64_t denom) { + int64_t q = value / denom; + int64_t r = value % denom; + return q * numer + r * numer / denom; +} + +_SOKOL_PRIVATE void _sapp_timestamp_init(_sapp_timestamp_t* ts) { + #if defined(_SAPP_APPLE) + mach_timebase_info(&ts->mach.timebase); + ts->mach.start = mach_absolute_time(); + #elif defined(_SAPP_EMSCRIPTEN) + (void)ts; + #elif defined(_SAPP_WIN32) + QueryPerformanceFrequency(&ts->win.freq); + QueryPerformanceCounter(&ts->win.start); + #else + struct timespec tspec; + clock_gettime(_SAPP_CLOCK_MONOTONIC, &tspec); + ts->posix.start = (uint64_t)tspec.tv_sec*1000000000 + (uint64_t)tspec.tv_nsec; + #endif +} + +_SOKOL_PRIVATE double _sapp_timestamp_now(_sapp_timestamp_t* ts) { + #if defined(_SAPP_APPLE) + const uint64_t traw = mach_absolute_time() - ts->mach.start; + const uint64_t now = (uint64_t) _sapp_int64_muldiv((int64_t)traw, (int64_t)ts->mach.timebase.numer, (int64_t)ts->mach.timebase.denom); + return (double)now / 1000000000.0; + #elif defined(_SAPP_EMSCRIPTEN) + (void)ts; + SOKOL_ASSERT(false); + return 0.0; + #elif defined(_SAPP_WIN32) + LARGE_INTEGER qpc; + QueryPerformanceCounter(&qpc); + const uint64_t now = (uint64_t)_sapp_int64_muldiv(qpc.QuadPart - ts->win.start.QuadPart, 1000000000, ts->win.freq.QuadPart); + return (double)now / 1000000000.0; + #else + struct timespec tspec; + clock_gettime(_SAPP_CLOCK_MONOTONIC, &tspec); + const uint64_t now = ((uint64_t)tspec.tv_sec*1000000000 + (uint64_t)tspec.tv_nsec) - ts->posix.start; + return (double)now / 1000000000.0; + #endif +} + +typedef struct { + double last; + double accum; + double avg; + int spike_count; + int num; + _sapp_timestamp_t timestamp; + _sapp_ring_t ring; +} _sapp_timing_t; + +_SOKOL_PRIVATE void _sapp_timing_reset(_sapp_timing_t* t) { + t->last = 0.0; + t->accum = 0.0; + t->spike_count = 0; + t->num = 0; + _sapp_ring_init(&t->ring); +} + +_SOKOL_PRIVATE void _sapp_timing_init(_sapp_timing_t* t) { + t->avg = 1.0 / 60.0; // dummy value until first actual value is available + _sapp_timing_reset(t); + _sapp_timestamp_init(&t->timestamp); +} + +_SOKOL_PRIVATE void _sapp_timing_put(_sapp_timing_t* t, double dur) { + // arbitrary upper limit to ignore outliers (e.g. during window resizing, or debugging) + double min_dur = 0.0; + double max_dur = 0.1; + // if we have enough samples for a useful average, use a much tighter 'valid window' + if (_sapp_ring_full(&t->ring)) { + min_dur = t->avg * 0.8; + max_dur = t->avg * 1.2; + } + if ((dur < min_dur) || (dur > max_dur)) { + t->spike_count++; + // if there have been many spikes in a row, the display refresh rate + // might have changed, so a timing reset is needed + if (t->spike_count > 20) { + _sapp_timing_reset(t); + } + return; + } + if (_sapp_ring_full(&t->ring)) { + double old_val = _sapp_ring_dequeue(&t->ring); + t->accum -= old_val; + t->num -= 1; + } + _sapp_ring_enqueue(&t->ring, dur); + t->accum += dur; + t->num += 1; + SOKOL_ASSERT(t->num > 0); + t->avg = t->accum / t->num; + t->spike_count = 0; +} + +_SOKOL_PRIVATE void _sapp_timing_discontinuity(_sapp_timing_t* t) { + t->last = 0.0; +} + +_SOKOL_PRIVATE void _sapp_timing_measure(_sapp_timing_t* t) { + const double now = _sapp_timestamp_now(&t->timestamp); + if (t->last > 0.0) { + double dur = now - t->last; + _sapp_timing_put(t, dur); + } + t->last = now; +} + +_SOKOL_PRIVATE void _sapp_timing_external(_sapp_timing_t* t, double now) { + if (t->last > 0.0) { + double dur = now - t->last; + _sapp_timing_put(t, dur); + } + t->last = now; +} + +_SOKOL_PRIVATE double _sapp_timing_get_avg(_sapp_timing_t* t) { + return t->avg; +} + +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >> structs +#if defined(_SAPP_MACOS) +@interface _sapp_macos_app_delegate : NSObject +@end +@interface _sapp_macos_window : NSWindow +@end +@interface _sapp_macos_window_delegate : NSObject +@end +#if defined(SOKOL_METAL) + @interface _sapp_macos_view : MTKView + @end +#elif defined(SOKOL_GLCORE) + @interface _sapp_macos_view : NSOpenGLView + - (void)timerFired:(id)sender; + @end +#endif // SOKOL_GLCORE + +typedef struct { + uint32_t flags_changed_store; + uint8_t mouse_buttons; + NSWindow* window; + NSTrackingArea* tracking_area; + id keyup_monitor; + _sapp_macos_app_delegate* app_dlg; + _sapp_macos_window_delegate* win_dlg; + _sapp_macos_view* view; + NSCursor* cursors[_SAPP_MOUSECURSOR_NUM]; + #if defined(SOKOL_METAL) + id mtl_device; + #endif +} _sapp_macos_t; + +#endif // _SAPP_MACOS + +#if defined(_SAPP_IOS) + +@interface _sapp_app_delegate : NSObject +@end +@interface _sapp_textfield_dlg : NSObject +- (void)keyboardWasShown:(NSNotification*)notif; +- (void)keyboardWillBeHidden:(NSNotification*)notif; +- (void)keyboardDidChangeFrame:(NSNotification*)notif; +@end +#if defined(SOKOL_METAL) + @interface _sapp_ios_view : MTKView; + @end +#else + @interface _sapp_ios_view : GLKView + @end +#endif + +typedef struct { + UIWindow* window; + _sapp_ios_view* view; + UITextField* textfield; + _sapp_textfield_dlg* textfield_dlg; + #if defined(SOKOL_METAL) + UIViewController* view_ctrl; + id mtl_device; + #else + GLKViewController* view_ctrl; + EAGLContext* eagl_ctx; + #endif + bool suspended; +} _sapp_ios_t; + +#endif // _SAPP_IOS + +#if defined(_SAPP_EMSCRIPTEN) + +#if defined(SOKOL_WGPU) +typedef struct { + WGPUInstance instance; + WGPUAdapter adapter; + WGPUDevice device; + WGPUTextureFormat render_format; + WGPUSurface surface; + WGPUSwapChain swapchain; + WGPUTexture msaa_tex; + WGPUTextureView msaa_view; + WGPUTexture depth_stencil_tex; + WGPUTextureView depth_stencil_view; + WGPUTextureView swapchain_view; + bool async_init_done; +} _sapp_wgpu_t; +#endif + +typedef struct { + bool mouse_lock_requested; + uint16_t mouse_buttons; +} _sapp_emsc_t; +#endif // _SAPP_EMSCRIPTEN + +#if defined(SOKOL_D3D11) && defined(_SAPP_WIN32) +typedef struct { + ID3D11Device* device; + ID3D11DeviceContext* device_context; + ID3D11Texture2D* rt; + ID3D11RenderTargetView* rtv; + ID3D11Texture2D* msaa_rt; + ID3D11RenderTargetView* msaa_rtv; + ID3D11Texture2D* ds; + ID3D11DepthStencilView* dsv; + DXGI_SWAP_CHAIN_DESC swap_chain_desc; + IDXGISwapChain* swap_chain; + IDXGIDevice1* dxgi_device; + bool use_dxgi_frame_stats; + UINT sync_refresh_count; +} _sapp_d3d11_t; +#endif + +#if defined(_SAPP_WIN32) + +#ifndef DPI_ENUMS_DECLARED +typedef enum PROCESS_DPI_AWARENESS +{ + PROCESS_DPI_UNAWARE = 0, + PROCESS_SYSTEM_DPI_AWARE = 1, + PROCESS_PER_MONITOR_DPI_AWARE = 2 +} PROCESS_DPI_AWARENESS; +typedef enum MONITOR_DPI_TYPE { + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI +} MONITOR_DPI_TYPE; +#endif // DPI_ENUMS_DECLARED + +typedef struct { + bool aware; + float content_scale; + float window_scale; + float mouse_scale; +} _sapp_win32_dpi_t; + +typedef struct { + HWND hwnd; + HMONITOR hmonitor; + HDC dc; + HICON big_icon; + HICON small_icon; + HCURSOR cursors[_SAPP_MOUSECURSOR_NUM]; + UINT orig_codepage; + LONG mouse_locked_x, mouse_locked_y; + RECT stored_window_rect; // used to restore window pos/size when toggling fullscreen => windowed + bool is_win10_or_greater; + bool in_create_window; + bool iconified; + bool mouse_tracked; + uint8_t mouse_capture_mask; + _sapp_win32_dpi_t dpi; + bool raw_input_mousepos_valid; + LONG raw_input_mousepos_x; + LONG raw_input_mousepos_y; + uint8_t raw_input_data[256]; +} _sapp_win32_t; + +#if defined(SOKOL_GLCORE) +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_TYPE_RGBA_ARB 0x202b +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NO_ACCELERATION_ARB 0x2025 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_ALPHA_BITS_ARB 0x201b +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_SAMPLES_ARB 0x2042 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define ERROR_INVALID_VERSION_ARB 0x2095 +#define ERROR_INVALID_PROFILE_ARB 0x2096 +#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 +typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int); +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*); +typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void); +typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC); +typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC,HGLRC,const int*); +typedef HGLRC (WINAPI * PFN_wglCreateContext)(HDC); +typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC); +typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR); +typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void); +typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC); + +typedef struct { + HINSTANCE opengl32; + HGLRC gl_ctx; + PFN_wglCreateContext CreateContext; + PFN_wglDeleteContext DeleteContext; + PFN_wglGetProcAddress GetProcAddress; + PFN_wglGetCurrentDC GetCurrentDC; + PFN_wglMakeCurrent MakeCurrent; + PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT; + PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB; + PFNWGLGETEXTENSIONSSTRINGEXTPROC GetExtensionsStringEXT; + PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB; + PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; + // special case glGetIntegerv + void (WINAPI *GetIntegerv)(uint32_t pname, int32_t* data); + bool ext_swap_control; + bool arb_multisample; + bool arb_pixel_format; + bool arb_create_context; + bool arb_create_context_profile; + HWND msg_hwnd; + HDC msg_dc; +} _sapp_wgl_t; +#endif // SOKOL_GLCORE + +#endif // _SAPP_WIN32 + +#if defined(_SAPP_ANDROID) +typedef enum { + _SOKOL_ANDROID_MSG_CREATE, + _SOKOL_ANDROID_MSG_RESUME, + _SOKOL_ANDROID_MSG_PAUSE, + _SOKOL_ANDROID_MSG_FOCUS, + _SOKOL_ANDROID_MSG_NO_FOCUS, + _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW, + _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE, + _SOKOL_ANDROID_MSG_DESTROY, +} _sapp_android_msg_t; + +typedef struct { + pthread_t thread; + pthread_mutex_t mutex; + pthread_cond_t cond; + int read_from_main_fd; + int write_from_main_fd; +} _sapp_android_pt_t; + +typedef struct { + ANativeWindow* window; + AInputQueue* input; +} _sapp_android_resources_t; + +typedef struct { + ANativeActivity* activity; + _sapp_android_pt_t pt; + _sapp_android_resources_t pending; + _sapp_android_resources_t current; + ALooper* looper; + bool is_thread_started; + bool is_thread_stopping; + bool is_thread_stopped; + bool has_created; + bool has_resumed; + bool has_focus; + EGLConfig config; + EGLDisplay display; + EGLContext context; + EGLSurface surface; +} _sapp_android_t; + +#endif // _SAPP_ANDROID + +#if defined(_SAPP_LINUX) + +#define _SAPP_X11_XDND_VERSION (5) + +#define GLX_VENDOR 1 +#define GLX_RGBA_BIT 0x00000001 +#define GLX_WINDOW_BIT 0x00000001 +#define GLX_DRAWABLE_TYPE 0x8010 +#define GLX_RENDER_TYPE 0x8011 +#define GLX_DOUBLEBUFFER 5 +#define GLX_RED_SIZE 8 +#define GLX_GREEN_SIZE 9 +#define GLX_BLUE_SIZE 10 +#define GLX_ALPHA_SIZE 11 +#define GLX_DEPTH_SIZE 12 +#define GLX_STENCIL_SIZE 13 +#define GLX_SAMPLES 0x186a1 +#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define GLX_CONTEXT_FLAGS_ARB 0x2094 + +typedef XID GLXWindow; +typedef XID GLXDrawable; +typedef struct __GLXFBConfig* GLXFBConfig; +typedef struct __GLXcontext* GLXContext; +typedef void (*__GLXextproc)(void); + +typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*); +typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int); +typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*); +typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*); +typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext); +typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext); +typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable); +typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int); +typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*); +typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const char *procName); +typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int); +typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig); +typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*); +typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow); + +typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); +typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); + +typedef struct { + bool available; + int major_opcode; + int event_base; + int error_base; + int major; + int minor; +} _sapp_xi_t; + +typedef struct { + int version; + Window source; + Atom format; + Atom XdndAware; + Atom XdndEnter; + Atom XdndPosition; + Atom XdndStatus; + Atom XdndActionCopy; + Atom XdndDrop; + Atom XdndFinished; + Atom XdndSelection; + Atom XdndTypeList; + Atom text_uri_list; +} _sapp_xdnd_t; + +typedef struct { + uint8_t mouse_buttons; + Display* display; + int screen; + Window root; + Colormap colormap; + Window window; + Cursor hidden_cursor; + Cursor cursors[_SAPP_MOUSECURSOR_NUM]; + int window_state; + float dpi; + unsigned char error_code; + Atom UTF8_STRING; + Atom WM_PROTOCOLS; + Atom WM_DELETE_WINDOW; + Atom WM_STATE; + Atom NET_WM_NAME; + Atom NET_WM_ICON_NAME; + Atom NET_WM_ICON; + Atom NET_WM_STATE; + Atom NET_WM_STATE_FULLSCREEN; + _sapp_xi_t xi; + _sapp_xdnd_t xdnd; +} _sapp_x11_t; + +#if defined(_SAPP_GLX) + +typedef struct { + void* libgl; + int major; + int minor; + int event_base; + int error_base; + GLXContext ctx; + GLXWindow window; + + // GLX 1.3 functions + PFNGLXGETFBCONFIGSPROC GetFBConfigs; + PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib; + PFNGLXGETCLIENTSTRINGPROC GetClientString; + PFNGLXQUERYEXTENSIONPROC QueryExtension; + PFNGLXQUERYVERSIONPROC QueryVersion; + PFNGLXDESTROYCONTEXTPROC DestroyContext; + PFNGLXMAKECURRENTPROC MakeCurrent; + PFNGLXSWAPBUFFERSPROC SwapBuffers; + PFNGLXQUERYEXTENSIONSSTRINGPROC QueryExtensionsString; + PFNGLXGETVISUALFROMFBCONFIGPROC GetVisualFromFBConfig; + PFNGLXCREATEWINDOWPROC CreateWindow; + PFNGLXDESTROYWINDOWPROC DestroyWindow; + + // GLX 1.4 and extension functions + PFNGLXGETPROCADDRESSPROC GetProcAddress; + PFNGLXGETPROCADDRESSPROC GetProcAddressARB; + PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT; + PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA; + PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; + + // special case glGetIntegerv + void (*GetIntegerv)(uint32_t pname, int32_t* data); + + // extension availability + bool EXT_swap_control; + bool MESA_swap_control; + bool ARB_multisample; + bool ARB_create_context; + bool ARB_create_context_profile; +} _sapp_glx_t; + +#else + +typedef struct { + EGLDisplay display; + EGLContext context; + EGLSurface surface; +} _sapp_egl_t; + +#endif // _SAPP_GLX +#endif // _SAPP_LINUX + +#if defined(_SAPP_ANY_GL) +typedef struct { + uint32_t framebuffer; +} _sapp_gl_t; +#endif + +typedef struct { + bool enabled; + int buf_size; + char* buffer; +} _sapp_clipboard_t; + +typedef struct { + bool enabled; + int max_files; + int max_path_length; + int num_files; + int buf_size; + char* buffer; +} _sapp_drop_t; + +typedef struct { + float x, y; + float dx, dy; + bool shown; + bool locked; + bool pos_valid; + sapp_mouse_cursor current_cursor; +} _sapp_mouse_t; + +typedef struct { + sapp_desc desc; + bool valid; + bool fullscreen; + bool first_frame; + bool init_called; + bool cleanup_called; + bool quit_requested; + bool quit_ordered; + bool event_consumed; + bool html5_ask_leave_site; + bool onscreen_keyboard_shown; + int window_width; + int window_height; + int framebuffer_width; + int framebuffer_height; + int sample_count; + int swap_interval; + float dpi_scale; + uint64_t frame_count; + _sapp_timing_t timing; + sapp_event event; + _sapp_mouse_t mouse; + _sapp_clipboard_t clipboard; + _sapp_drop_t drop; + sapp_icon_desc default_icon_desc; + uint32_t* default_icon_pixels; + #if defined(_SAPP_MACOS) + _sapp_macos_t macos; + #elif defined(_SAPP_IOS) + _sapp_ios_t ios; + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_t emsc; + #if defined(SOKOL_WGPU) + _sapp_wgpu_t wgpu; + #endif + #elif defined(_SAPP_WIN32) + _sapp_win32_t win32; + #if defined(SOKOL_D3D11) + _sapp_d3d11_t d3d11; + #elif defined(SOKOL_GLCORE) + _sapp_wgl_t wgl; + #endif + #elif defined(_SAPP_ANDROID) + _sapp_android_t android; + #elif defined(_SAPP_LINUX) + _sapp_x11_t x11; + #if defined(_SAPP_GLX) + _sapp_glx_t glx; + #else + _sapp_egl_t egl; + #endif + #endif + #if defined(_SAPP_ANY_GL) + _sapp_gl_t gl; + #endif + char html5_canvas_selector[_SAPP_MAX_TITLE_LENGTH]; + char window_title[_SAPP_MAX_TITLE_LENGTH]; // UTF-8 + wchar_t window_title_wide[_SAPP_MAX_TITLE_LENGTH]; // UTF-32 or UCS-2 */ + sapp_keycode keycodes[SAPP_MAX_KEYCODES]; +} _sapp_t; +static _sapp_t _sapp; + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SAPP_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sapp_log_messages[] = { + _SAPP_LOG_ITEMS +}; +#undef _SAPP_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SAPP_PANIC(code) _sapp_log(SAPP_LOGITEM_ ##code, 0, 0, __LINE__) +#define _SAPP_ERROR(code) _sapp_log(SAPP_LOGITEM_ ##code, 1, 0, __LINE__) +#define _SAPP_WARN(code) _sapp_log(SAPP_LOGITEM_ ##code, 2, 0, __LINE__) +#define _SAPP_INFO(code) _sapp_log(SAPP_LOGITEM_ ##code, 3, 0, __LINE__) + +static void _sapp_log(sapp_log_item log_item, uint32_t log_level, const char* msg, uint32_t line_nr) { + if (_sapp.desc.logger.func) { + const char* filename = 0; + #if defined(SOKOL_DEBUG) + filename = __FILE__; + if (0 == msg) { + msg = _sapp_log_messages[log_item]; + } + #endif + _sapp.desc.logger.func("sapp", log_level, log_item, msg, line_nr, filename, _sapp.desc.logger.user_data); + } + else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory +_SOKOL_PRIVATE void _sapp_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +_SOKOL_PRIVATE void* _sapp_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_sapp.desc.allocator.alloc_fn) { + ptr = _sapp.desc.allocator.alloc_fn(size, _sapp.desc.allocator.user_data); + } else { + ptr = malloc(size); + } + if (0 == ptr) { + _SAPP_PANIC(MALLOC_FAILED); + } + return ptr; +} + +_SOKOL_PRIVATE void* _sapp_malloc_clear(size_t size) { + void* ptr = _sapp_malloc(size); + _sapp_clear(ptr, size); + return ptr; +} + +_SOKOL_PRIVATE void _sapp_free(void* ptr) { + if (_sapp.desc.allocator.free_fn) { + _sapp.desc.allocator.free_fn(ptr, _sapp.desc.allocator.user_data); + } + else { + free(ptr); + } +} + +// ██ ██ ███████ ██ ██████ ███████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ █████ ██ ██████ █████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ +// +// >>helpers +_SOKOL_PRIVATE void _sapp_call_init(void) { + if (_sapp.desc.init_cb) { + _sapp.desc.init_cb(); + } + else if (_sapp.desc.init_userdata_cb) { + _sapp.desc.init_userdata_cb(_sapp.desc.user_data); + } + _sapp.init_called = true; +} + +_SOKOL_PRIVATE void _sapp_call_frame(void) { + if (_sapp.init_called && !_sapp.cleanup_called) { + if (_sapp.desc.frame_cb) { + _sapp.desc.frame_cb(); + } + else if (_sapp.desc.frame_userdata_cb) { + _sapp.desc.frame_userdata_cb(_sapp.desc.user_data); + } + } +} + +_SOKOL_PRIVATE void _sapp_call_cleanup(void) { + if (!_sapp.cleanup_called) { + if (_sapp.desc.cleanup_cb) { + _sapp.desc.cleanup_cb(); + } + else if (_sapp.desc.cleanup_userdata_cb) { + _sapp.desc.cleanup_userdata_cb(_sapp.desc.user_data); + } + _sapp.cleanup_called = true; + } +} + +_SOKOL_PRIVATE bool _sapp_call_event(const sapp_event* e) { + if (!_sapp.cleanup_called) { + if (_sapp.desc.event_cb) { + _sapp.desc.event_cb(e); + } + else if (_sapp.desc.event_userdata_cb) { + _sapp.desc.event_userdata_cb(e, _sapp.desc.user_data); + } + } + if (_sapp.event_consumed) { + _sapp.event_consumed = false; + return true; + } + else { + return false; + } +} + +_SOKOL_PRIVATE char* _sapp_dropped_file_path_ptr(int index) { + SOKOL_ASSERT(_sapp.drop.buffer); + SOKOL_ASSERT((index >= 0) && (index <= _sapp.drop.max_files)); + int offset = index * _sapp.drop.max_path_length; + SOKOL_ASSERT(offset < _sapp.drop.buf_size); + return &_sapp.drop.buffer[offset]; +} + +/* Copy a string into a fixed size buffer with guaranteed zero- + termination. + + Return false if the string didn't fit into the buffer and had to be clamped. + + FIXME: Currently UTF-8 strings might become invalid if the string + is clamped, because the last zero-byte might be written into + the middle of a multi-byte sequence. +*/ +_SOKOL_PRIVATE bool _sapp_strcpy(const char* src, char* dst, int max_len) { + SOKOL_ASSERT(src && dst && (max_len > 0)); + char* const end = &(dst[max_len-1]); + char c = 0; + for (int i = 0; i < max_len; i++) { + c = *src; + if (c != 0) { + src++; + } + *dst++ = c; + } + /* truncated? */ + if (c != 0) { + *end = 0; + return false; + } + else { + return true; + } +} + +_SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* desc) { + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + sapp_desc res = *desc; + res.sample_count = _sapp_def(res.sample_count, 1); + res.swap_interval = _sapp_def(res.swap_interval, 1); + // NOTE: can't patch the default for gl_major_version and gl_minor_version + // independently, because a desired version 4.0 would be patched to 4.2 + // (or expressed differently: zero is a valid value for gl_minor_version + // and can't be used to indicate 'default') + if (0 == res.gl_major_version) { + #if defined(_SAPP_APPLE) + res.gl_major_version = 4; + res.gl_minor_version = 1; + #else + res.gl_major_version = 4; + res.gl_minor_version = 3; + #endif + } + res.html5_canvas_name = _sapp_def(res.html5_canvas_name, "canvas"); + res.clipboard_size = _sapp_def(res.clipboard_size, 8192); + res.max_dropped_files = _sapp_def(res.max_dropped_files, 1); + res.max_dropped_file_path_length = _sapp_def(res.max_dropped_file_path_length, 2048); + res.window_title = _sapp_def(res.window_title, "sokol_app"); + return res; +} + +_SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->width >= 0); + SOKOL_ASSERT(desc->height >= 0); + SOKOL_ASSERT(desc->sample_count >= 0); + SOKOL_ASSERT(desc->swap_interval >= 0); + SOKOL_ASSERT(desc->clipboard_size >= 0); + SOKOL_ASSERT(desc->max_dropped_files >= 0); + SOKOL_ASSERT(desc->max_dropped_file_path_length >= 0); + _SAPP_CLEAR_ARC_STRUCT(_sapp_t, _sapp); + _sapp.desc = _sapp_desc_defaults(desc); + _sapp.first_frame = true; + // NOTE: _sapp.desc.width/height may be 0! Platform backends need to deal with this + _sapp.window_width = _sapp.desc.width; + _sapp.window_height = _sapp.desc.height; + _sapp.framebuffer_width = _sapp.window_width; + _sapp.framebuffer_height = _sapp.window_height; + _sapp.sample_count = _sapp.desc.sample_count; + _sapp.swap_interval = _sapp.desc.swap_interval; + _sapp.html5_canvas_selector[0] = '#'; + _sapp_strcpy(_sapp.desc.html5_canvas_name, &_sapp.html5_canvas_selector[1], sizeof(_sapp.html5_canvas_selector) - 1); + _sapp.desc.html5_canvas_name = &_sapp.html5_canvas_selector[1]; + _sapp.html5_ask_leave_site = _sapp.desc.html5_ask_leave_site; + _sapp.clipboard.enabled = _sapp.desc.enable_clipboard; + if (_sapp.clipboard.enabled) { + _sapp.clipboard.buf_size = _sapp.desc.clipboard_size; + _sapp.clipboard.buffer = (char*) _sapp_malloc_clear((size_t)_sapp.clipboard.buf_size); + } + _sapp.drop.enabled = _sapp.desc.enable_dragndrop; + if (_sapp.drop.enabled) { + _sapp.drop.max_files = _sapp.desc.max_dropped_files; + _sapp.drop.max_path_length = _sapp.desc.max_dropped_file_path_length; + _sapp.drop.buf_size = _sapp.drop.max_files * _sapp.drop.max_path_length; + _sapp.drop.buffer = (char*) _sapp_malloc_clear((size_t)_sapp.drop.buf_size); + } + _sapp_strcpy(_sapp.desc.window_title, _sapp.window_title, sizeof(_sapp.window_title)); + _sapp.desc.window_title = _sapp.window_title; + _sapp.dpi_scale = 1.0f; + _sapp.fullscreen = _sapp.desc.fullscreen; + _sapp.mouse.shown = true; + _sapp_timing_init(&_sapp.timing); +} + +_SOKOL_PRIVATE void _sapp_discard_state(void) { + if (_sapp.clipboard.enabled) { + SOKOL_ASSERT(_sapp.clipboard.buffer); + _sapp_free((void*)_sapp.clipboard.buffer); + } + if (_sapp.drop.enabled) { + SOKOL_ASSERT(_sapp.drop.buffer); + _sapp_free((void*)_sapp.drop.buffer); + } + if (_sapp.default_icon_pixels) { + _sapp_free((void*)_sapp.default_icon_pixels); + } + _SAPP_CLEAR_ARC_STRUCT(_sapp_t, _sapp); +} + +_SOKOL_PRIVATE void _sapp_init_event(sapp_event_type type) { + _sapp_clear(&_sapp.event, sizeof(_sapp.event)); + _sapp.event.type = type; + _sapp.event.frame_count = _sapp.frame_count; + _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; + _sapp.event.window_width = _sapp.window_width; + _sapp.event.window_height = _sapp.window_height; + _sapp.event.framebuffer_width = _sapp.framebuffer_width; + _sapp.event.framebuffer_height = _sapp.framebuffer_height; + _sapp.event.mouse_x = _sapp.mouse.x; + _sapp.event.mouse_y = _sapp.mouse.y; + _sapp.event.mouse_dx = _sapp.mouse.dx; + _sapp.event.mouse_dy = _sapp.mouse.dy; +} + +_SOKOL_PRIVATE bool _sapp_events_enabled(void) { + /* only send events when an event callback is set, and the init function was called */ + return (_sapp.desc.event_cb || _sapp.desc.event_userdata_cb) && _sapp.init_called; +} + +_SOKOL_PRIVATE sapp_keycode _sapp_translate_key(int scan_code) { + if ((scan_code >= 0) && (scan_code < SAPP_MAX_KEYCODES)) { + return _sapp.keycodes[scan_code]; + } + else { + return SAPP_KEYCODE_INVALID; + } +} + +_SOKOL_PRIVATE void _sapp_clear_drop_buffer(void) { + if (_sapp.drop.enabled) { + SOKOL_ASSERT(_sapp.drop.buffer); + _sapp_clear(_sapp.drop.buffer, (size_t)_sapp.drop.buf_size); + } +} + +_SOKOL_PRIVATE void _sapp_frame(void) { + if (_sapp.first_frame) { + _sapp.first_frame = false; + _sapp_call_init(); + } + _sapp_call_frame(); + _sapp.frame_count++; +} + +_SOKOL_PRIVATE bool _sapp_image_validate(const sapp_image_desc* desc) { + SOKOL_ASSERT(desc->width > 0); + SOKOL_ASSERT(desc->height > 0); + SOKOL_ASSERT(desc->pixels.ptr != 0); + SOKOL_ASSERT(desc->pixels.size > 0); + const size_t wh_size = (size_t)(desc->width * desc->height) * sizeof(uint32_t); + if (wh_size != desc->pixels.size) { + _SAPP_ERROR(IMAGE_DATA_SIZE_MISMATCH); + return false; + } + return true; +} + +_SOKOL_PRIVATE int _sapp_image_bestmatch(const sapp_image_desc image_descs[], int num_images, int width, int height) { + int least_diff = 0x7FFFFFFF; + int least_index = 0; + for (int i = 0; i < num_images; i++) { + int diff = (image_descs[i].width * image_descs[i].height) - (width * height); + if (diff < 0) { + diff = -diff; + } + if (diff < least_diff) { + least_diff = diff; + least_index = i; + } + } + return least_index; +} + +_SOKOL_PRIVATE int _sapp_icon_num_images(const sapp_icon_desc* desc) { + int index = 0; + for (; index < SAPP_MAX_ICONIMAGES; index++) { + if (0 == desc->images[index].pixels.ptr) { + break; + } + } + return index; +} + +_SOKOL_PRIVATE bool _sapp_validate_icon_desc(const sapp_icon_desc* desc, int num_images) { + SOKOL_ASSERT(num_images <= SAPP_MAX_ICONIMAGES); + for (int i = 0; i < num_images; i++) { + const sapp_image_desc* img_desc = &desc->images[i]; + if (!_sapp_image_validate(img_desc)) { + return false; + } + } + return true; +} + +_SOKOL_PRIVATE void _sapp_setup_default_icon(void) { + SOKOL_ASSERT(0 == _sapp.default_icon_pixels); + + const int num_icons = 3; + const int icon_sizes[3] = { 16, 32, 64 }; // must be multiple of 8! + + // allocate a pixel buffer for all icon pixels + int all_num_pixels = 0; + for (int i = 0; i < num_icons; i++) { + all_num_pixels += icon_sizes[i] * icon_sizes[i]; + } + _sapp.default_icon_pixels = (uint32_t*) _sapp_malloc_clear((size_t)all_num_pixels * sizeof(uint32_t)); + + // initialize default_icon_desc struct + uint32_t* dst = _sapp.default_icon_pixels; + const uint32_t* dst_end = dst + all_num_pixels; + (void)dst_end; // silence unused warning in release mode + for (int i = 0; i < num_icons; i++) { + const int dim = (int) icon_sizes[i]; + const int num_pixels = dim * dim; + sapp_image_desc* img_desc = &_sapp.default_icon_desc.images[i]; + img_desc->width = dim; + img_desc->height = dim; + img_desc->pixels.ptr = dst; + img_desc->pixels.size = (size_t)num_pixels * sizeof(uint32_t); + dst += num_pixels; + } + SOKOL_ASSERT(dst == dst_end); + + // Amstrad CPC font 'S' + const uint8_t tile[8] = { + 0x3C, + 0x66, + 0x60, + 0x3C, + 0x06, + 0x66, + 0x3C, + 0x00, + }; + // rainbow colors + const uint32_t colors[8] = { + 0xFF4370FF, + 0xFF26A7FF, + 0xFF58EEFF, + 0xFF57E1D4, + 0xFF65CC9C, + 0xFF6ABB66, + 0xFFF5A542, + 0xFFC2577E, + }; + dst = _sapp.default_icon_pixels; + const uint32_t blank = 0x00FFFFFF; + const uint32_t shadow = 0xFF000000; + for (int i = 0; i < num_icons; i++) { + const int dim = icon_sizes[i]; + SOKOL_ASSERT((dim % 8) == 0); + const int scale = dim / 8; + for (int ty = 0, y = 0; ty < 8; ty++) { + const uint32_t color = colors[ty]; + for (int sy = 0; sy < scale; sy++, y++) { + uint8_t bits = tile[ty]; + for (int tx = 0, x = 0; tx < 8; tx++, bits<<=1) { + uint32_t pixel = (0 == (bits & 0x80)) ? blank : color; + for (int sx = 0; sx < scale; sx++, x++) { + SOKOL_ASSERT(dst < dst_end); + *dst++ = pixel; + } + } + } + } + } + SOKOL_ASSERT(dst == dst_end); + + // right shadow + dst = _sapp.default_icon_pixels; + for (int i = 0; i < num_icons; i++) { + const int dim = icon_sizes[i]; + for (int y = 0; y < dim; y++) { + uint32_t prev_color = blank; + for (int x = 0; x < dim; x++) { + const int dst_index = y * dim + x; + const uint32_t cur_color = dst[dst_index]; + if ((cur_color == blank) && (prev_color != blank)) { + dst[dst_index] = shadow; + } + prev_color = cur_color; + } + } + dst += dim * dim; + } + SOKOL_ASSERT(dst == dst_end); + + // bottom shadow + dst = _sapp.default_icon_pixels; + for (int i = 0; i < num_icons; i++) { + const int dim = icon_sizes[i]; + for (int x = 0; x < dim; x++) { + uint32_t prev_color = blank; + for (int y = 0; y < dim; y++) { + const int dst_index = y * dim + x; + const uint32_t cur_color = dst[dst_index]; + if ((cur_color == blank) && (prev_color != blank)) { + dst[dst_index] = shadow; + } + prev_color = cur_color; + } + } + dst += dim * dim; + } + SOKOL_ASSERT(dst == dst_end); +} + +// █████ ██████ ██████ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██ █████ +// ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ +// +// >>apple +#if defined(_SAPP_APPLE) + +#if __has_feature(objc_arc) +#define _SAPP_OBJC_RELEASE(obj) { obj = nil; } +#else +#define _SAPP_OBJC_RELEASE(obj) { [obj release]; obj = nil; } +#endif + +// ███ ███ █████ ██████ ██████ ███████ +// ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ ███████ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██████ ██████ ███████ +// +// >>macos +#if defined(_SAPP_MACOS) + +_SOKOL_PRIVATE void _sapp_macos_init_keytable(void) { + _sapp.keycodes[0x1D] = SAPP_KEYCODE_0; + _sapp.keycodes[0x12] = SAPP_KEYCODE_1; + _sapp.keycodes[0x13] = SAPP_KEYCODE_2; + _sapp.keycodes[0x14] = SAPP_KEYCODE_3; + _sapp.keycodes[0x15] = SAPP_KEYCODE_4; + _sapp.keycodes[0x17] = SAPP_KEYCODE_5; + _sapp.keycodes[0x16] = SAPP_KEYCODE_6; + _sapp.keycodes[0x1A] = SAPP_KEYCODE_7; + _sapp.keycodes[0x1C] = SAPP_KEYCODE_8; + _sapp.keycodes[0x19] = SAPP_KEYCODE_9; + _sapp.keycodes[0x00] = SAPP_KEYCODE_A; + _sapp.keycodes[0x0B] = SAPP_KEYCODE_B; + _sapp.keycodes[0x08] = SAPP_KEYCODE_C; + _sapp.keycodes[0x02] = SAPP_KEYCODE_D; + _sapp.keycodes[0x0E] = SAPP_KEYCODE_E; + _sapp.keycodes[0x03] = SAPP_KEYCODE_F; + _sapp.keycodes[0x05] = SAPP_KEYCODE_G; + _sapp.keycodes[0x04] = SAPP_KEYCODE_H; + _sapp.keycodes[0x22] = SAPP_KEYCODE_I; + _sapp.keycodes[0x26] = SAPP_KEYCODE_J; + _sapp.keycodes[0x28] = SAPP_KEYCODE_K; + _sapp.keycodes[0x25] = SAPP_KEYCODE_L; + _sapp.keycodes[0x2E] = SAPP_KEYCODE_M; + _sapp.keycodes[0x2D] = SAPP_KEYCODE_N; + _sapp.keycodes[0x1F] = SAPP_KEYCODE_O; + _sapp.keycodes[0x23] = SAPP_KEYCODE_P; + _sapp.keycodes[0x0C] = SAPP_KEYCODE_Q; + _sapp.keycodes[0x0F] = SAPP_KEYCODE_R; + _sapp.keycodes[0x01] = SAPP_KEYCODE_S; + _sapp.keycodes[0x11] = SAPP_KEYCODE_T; + _sapp.keycodes[0x20] = SAPP_KEYCODE_U; + _sapp.keycodes[0x09] = SAPP_KEYCODE_V; + _sapp.keycodes[0x0D] = SAPP_KEYCODE_W; + _sapp.keycodes[0x07] = SAPP_KEYCODE_X; + _sapp.keycodes[0x10] = SAPP_KEYCODE_Y; + _sapp.keycodes[0x06] = SAPP_KEYCODE_Z; + _sapp.keycodes[0x27] = SAPP_KEYCODE_APOSTROPHE; + _sapp.keycodes[0x2A] = SAPP_KEYCODE_BACKSLASH; + _sapp.keycodes[0x2B] = SAPP_KEYCODE_COMMA; + _sapp.keycodes[0x18] = SAPP_KEYCODE_EQUAL; + _sapp.keycodes[0x32] = SAPP_KEYCODE_GRAVE_ACCENT; + _sapp.keycodes[0x21] = SAPP_KEYCODE_LEFT_BRACKET; + _sapp.keycodes[0x1B] = SAPP_KEYCODE_MINUS; + _sapp.keycodes[0x2F] = SAPP_KEYCODE_PERIOD; + _sapp.keycodes[0x1E] = SAPP_KEYCODE_RIGHT_BRACKET; + _sapp.keycodes[0x29] = SAPP_KEYCODE_SEMICOLON; + _sapp.keycodes[0x2C] = SAPP_KEYCODE_SLASH; + _sapp.keycodes[0x0A] = SAPP_KEYCODE_WORLD_1; + _sapp.keycodes[0x33] = SAPP_KEYCODE_BACKSPACE; + _sapp.keycodes[0x39] = SAPP_KEYCODE_CAPS_LOCK; + _sapp.keycodes[0x75] = SAPP_KEYCODE_DELETE; + _sapp.keycodes[0x7D] = SAPP_KEYCODE_DOWN; + _sapp.keycodes[0x77] = SAPP_KEYCODE_END; + _sapp.keycodes[0x24] = SAPP_KEYCODE_ENTER; + _sapp.keycodes[0x35] = SAPP_KEYCODE_ESCAPE; + _sapp.keycodes[0x7A] = SAPP_KEYCODE_F1; + _sapp.keycodes[0x78] = SAPP_KEYCODE_F2; + _sapp.keycodes[0x63] = SAPP_KEYCODE_F3; + _sapp.keycodes[0x76] = SAPP_KEYCODE_F4; + _sapp.keycodes[0x60] = SAPP_KEYCODE_F5; + _sapp.keycodes[0x61] = SAPP_KEYCODE_F6; + _sapp.keycodes[0x62] = SAPP_KEYCODE_F7; + _sapp.keycodes[0x64] = SAPP_KEYCODE_F8; + _sapp.keycodes[0x65] = SAPP_KEYCODE_F9; + _sapp.keycodes[0x6D] = SAPP_KEYCODE_F10; + _sapp.keycodes[0x67] = SAPP_KEYCODE_F11; + _sapp.keycodes[0x6F] = SAPP_KEYCODE_F12; + _sapp.keycodes[0x69] = SAPP_KEYCODE_F13; + _sapp.keycodes[0x6B] = SAPP_KEYCODE_F14; + _sapp.keycodes[0x71] = SAPP_KEYCODE_F15; + _sapp.keycodes[0x6A] = SAPP_KEYCODE_F16; + _sapp.keycodes[0x40] = SAPP_KEYCODE_F17; + _sapp.keycodes[0x4F] = SAPP_KEYCODE_F18; + _sapp.keycodes[0x50] = SAPP_KEYCODE_F19; + _sapp.keycodes[0x5A] = SAPP_KEYCODE_F20; + _sapp.keycodes[0x73] = SAPP_KEYCODE_HOME; + _sapp.keycodes[0x72] = SAPP_KEYCODE_INSERT; + _sapp.keycodes[0x7B] = SAPP_KEYCODE_LEFT; + _sapp.keycodes[0x3A] = SAPP_KEYCODE_LEFT_ALT; + _sapp.keycodes[0x3B] = SAPP_KEYCODE_LEFT_CONTROL; + _sapp.keycodes[0x38] = SAPP_KEYCODE_LEFT_SHIFT; + _sapp.keycodes[0x37] = SAPP_KEYCODE_LEFT_SUPER; + _sapp.keycodes[0x6E] = SAPP_KEYCODE_MENU; + _sapp.keycodes[0x47] = SAPP_KEYCODE_NUM_LOCK; + _sapp.keycodes[0x79] = SAPP_KEYCODE_PAGE_DOWN; + _sapp.keycodes[0x74] = SAPP_KEYCODE_PAGE_UP; + _sapp.keycodes[0x7C] = SAPP_KEYCODE_RIGHT; + _sapp.keycodes[0x3D] = SAPP_KEYCODE_RIGHT_ALT; + _sapp.keycodes[0x3E] = SAPP_KEYCODE_RIGHT_CONTROL; + _sapp.keycodes[0x3C] = SAPP_KEYCODE_RIGHT_SHIFT; + _sapp.keycodes[0x36] = SAPP_KEYCODE_RIGHT_SUPER; + _sapp.keycodes[0x31] = SAPP_KEYCODE_SPACE; + _sapp.keycodes[0x30] = SAPP_KEYCODE_TAB; + _sapp.keycodes[0x7E] = SAPP_KEYCODE_UP; + _sapp.keycodes[0x52] = SAPP_KEYCODE_KP_0; + _sapp.keycodes[0x53] = SAPP_KEYCODE_KP_1; + _sapp.keycodes[0x54] = SAPP_KEYCODE_KP_2; + _sapp.keycodes[0x55] = SAPP_KEYCODE_KP_3; + _sapp.keycodes[0x56] = SAPP_KEYCODE_KP_4; + _sapp.keycodes[0x57] = SAPP_KEYCODE_KP_5; + _sapp.keycodes[0x58] = SAPP_KEYCODE_KP_6; + _sapp.keycodes[0x59] = SAPP_KEYCODE_KP_7; + _sapp.keycodes[0x5B] = SAPP_KEYCODE_KP_8; + _sapp.keycodes[0x5C] = SAPP_KEYCODE_KP_9; + _sapp.keycodes[0x45] = SAPP_KEYCODE_KP_ADD; + _sapp.keycodes[0x41] = SAPP_KEYCODE_KP_DECIMAL; + _sapp.keycodes[0x4B] = SAPP_KEYCODE_KP_DIVIDE; + _sapp.keycodes[0x4C] = SAPP_KEYCODE_KP_ENTER; + _sapp.keycodes[0x51] = SAPP_KEYCODE_KP_EQUAL; + _sapp.keycodes[0x43] = SAPP_KEYCODE_KP_MULTIPLY; + _sapp.keycodes[0x4E] = SAPP_KEYCODE_KP_SUBTRACT; +} + +_SOKOL_PRIVATE void _sapp_macos_discard_state(void) { + // NOTE: it's safe to call [release] on a nil object + if (_sapp.macos.keyup_monitor != nil) { + [NSEvent removeMonitor:_sapp.macos.keyup_monitor]; + // NOTE: removeMonitor also releases the object + _sapp.macos.keyup_monitor = nil; + } + _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area); + _SAPP_OBJC_RELEASE(_sapp.macos.app_dlg); + _SAPP_OBJC_RELEASE(_sapp.macos.win_dlg); + _SAPP_OBJC_RELEASE(_sapp.macos.view); + #if defined(SOKOL_METAL) + _SAPP_OBJC_RELEASE(_sapp.macos.mtl_device); + #endif + _SAPP_OBJC_RELEASE(_sapp.macos.window); +} + +// undocumented methods for creating cursors (see GLFW 3.4 and imgui_impl_osx.mm) +@interface NSCursor() ++ (id)_windowResizeNorthWestSouthEastCursor; ++ (id)_windowResizeNorthEastSouthWestCursor; ++ (id)_windowResizeNorthSouthCursor; ++ (id)_windowResizeEastWestCursor; +@end + +_SOKOL_PRIVATE void _sapp_macos_init_cursors(void) { + _sapp.macos.cursors[SAPP_MOUSECURSOR_DEFAULT] = nil; // not a bug + _sapp.macos.cursors[SAPP_MOUSECURSOR_ARROW] = [NSCursor arrowCursor]; + _sapp.macos.cursors[SAPP_MOUSECURSOR_IBEAM] = [NSCursor IBeamCursor]; + _sapp.macos.cursors[SAPP_MOUSECURSOR_CROSSHAIR] = [NSCursor crosshairCursor]; + _sapp.macos.cursors[SAPP_MOUSECURSOR_POINTING_HAND] = [NSCursor pointingHandCursor]; + _sapp.macos.cursors[SAPP_MOUSECURSOR_RESIZE_EW] = [NSCursor respondsToSelector:@selector(_windowResizeEastWestCursor)] ? [NSCursor _windowResizeEastWestCursor] : [NSCursor resizeLeftRightCursor]; + _sapp.macos.cursors[SAPP_MOUSECURSOR_RESIZE_NS] = [NSCursor respondsToSelector:@selector(_windowResizeNorthSouthCursor)] ? [NSCursor _windowResizeNorthSouthCursor] : [NSCursor resizeUpDownCursor]; + _sapp.macos.cursors[SAPP_MOUSECURSOR_RESIZE_NWSE] = [NSCursor respondsToSelector:@selector(_windowResizeNorthWestSouthEastCursor)] ? [NSCursor _windowResizeNorthWestSouthEastCursor] : [NSCursor closedHandCursor]; + _sapp.macos.cursors[SAPP_MOUSECURSOR_RESIZE_NESW] = [NSCursor respondsToSelector:@selector(_windowResizeNorthEastSouthWestCursor)] ? [NSCursor _windowResizeNorthEastSouthWestCursor] : [NSCursor closedHandCursor]; + _sapp.macos.cursors[SAPP_MOUSECURSOR_RESIZE_ALL] = [NSCursor closedHandCursor]; + _sapp.macos.cursors[SAPP_MOUSECURSOR_NOT_ALLOWED] = [NSCursor operationNotAllowedCursor]; +} + +_SOKOL_PRIVATE void _sapp_macos_run(const sapp_desc* desc) { + _sapp_init_state(desc); + _sapp_macos_init_keytable(); + [NSApplication sharedApplication]; + + // set the application dock icon as early as possible, otherwise + // the dummy icon will be visible for a short time + sapp_set_icon(&_sapp.desc.icon); + _sapp.macos.app_dlg = [[_sapp_macos_app_delegate alloc] init]; + NSApp.delegate = _sapp.macos.app_dlg; + + // workaround for "no key-up sent while Cmd is pressed" taken from GLFW: + NSEvent* (^keyup_monitor)(NSEvent*) = ^NSEvent* (NSEvent* event) { + if ([event modifierFlags] & NSEventModifierFlagCommand) { + [[NSApp keyWindow] sendEvent:event]; + } + return event; + }; + _sapp.macos.keyup_monitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp handler:keyup_monitor]; + + [NSApp run]; + // NOTE: [NSApp run] never returns, instead cleanup code + // must be put into applicationWillTerminate +} + +/* MacOS entry function */ +#if !defined(SOKOL_NO_ENTRY) +int main(int argc, char* argv[]) { + sapp_desc desc = sokol_main(argc, argv); + _sapp_macos_run(&desc); + return 0; +} +#endif /* SOKOL_NO_ENTRY */ + +_SOKOL_PRIVATE uint32_t _sapp_macos_mods(NSEvent* ev) { + const NSEventModifierFlags f = (ev == nil) ? NSEvent.modifierFlags : ev.modifierFlags; + const NSUInteger b = NSEvent.pressedMouseButtons; + uint32_t m = 0; + if (f & NSEventModifierFlagShift) { + m |= SAPP_MODIFIER_SHIFT; + } + if (f & NSEventModifierFlagControl) { + m |= SAPP_MODIFIER_CTRL; + } + if (f & NSEventModifierFlagOption) { + m |= SAPP_MODIFIER_ALT; + } + if (f & NSEventModifierFlagCommand) { + m |= SAPP_MODIFIER_SUPER; + } + if (0 != (b & (1<<0))) { + m |= SAPP_MODIFIER_LMB; + } + if (0 != (b & (1<<1))) { + m |= SAPP_MODIFIER_RMB; + } + if (0 != (b & (1<<2))) { + m |= SAPP_MODIFIER_MMB; + } + return m; +} + +_SOKOL_PRIVATE void _sapp_macos_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mod) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp.event.mouse_button = btn; + _sapp.event.modifiers = mod; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_macos_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mod) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp.event.key_code = key; + _sapp.event.key_repeat = repeat; + _sapp.event.modifiers = mod; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_macos_app_event(sapp_event_type type) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } +} + +/* NOTE: unlike the iOS version of this function, the macOS version + can dynamically update the DPI scaling factor when a window is moved + between HighDPI / LowDPI screens. +*/ +_SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) { + if (_sapp.desc.high_dpi) { + _sapp.dpi_scale = [_sapp.macos.window screen].backingScaleFactor; + } + else { + _sapp.dpi_scale = 1.0f; + } + _sapp.macos.view.layer.contentsScale = _sapp.dpi_scale; // NOTE: needed because we set layerContentsPlacement to a non-scaling value in windowWillStartLiveResize. + const NSRect bounds = [_sapp.macos.view bounds]; + _sapp.window_width = (int)roundf(bounds.size.width); + _sapp.window_height = (int)roundf(bounds.size.height); + #if defined(SOKOL_METAL) + _sapp.framebuffer_width = (int)roundf(bounds.size.width * _sapp.dpi_scale); + _sapp.framebuffer_height = (int)roundf(bounds.size.height * _sapp.dpi_scale); + const CGSize fb_size = _sapp.macos.view.drawableSize; + const int cur_fb_width = (int)roundf(fb_size.width); + const int cur_fb_height = (int)roundf(fb_size.height); + const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || + (_sapp.framebuffer_height != cur_fb_height); + #elif defined(SOKOL_GLCORE) + const int cur_fb_width = (int)roundf(bounds.size.width * _sapp.dpi_scale); + const int cur_fb_height = (int)roundf(bounds.size.height * _sapp.dpi_scale); + const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || + (_sapp.framebuffer_height != cur_fb_height); + _sapp.framebuffer_width = cur_fb_width; + _sapp.framebuffer_height = cur_fb_height; + #endif + if (_sapp.framebuffer_width == 0) { + _sapp.framebuffer_width = 1; + } + if (_sapp.framebuffer_height == 0) { + _sapp.framebuffer_height = 1; + } + if (_sapp.window_width == 0) { + _sapp.window_width = 1; + } + if (_sapp.window_height == 0) { + _sapp.window_height = 1; + } + if (dim_changed) { + #if defined(SOKOL_METAL) + CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height }; + _sapp.macos.view.drawableSize = drawable_size; + #else + // nothing to do for GL? + #endif + if (!_sapp.first_frame) { + _sapp_macos_app_event(SAPP_EVENTTYPE_RESIZED); + } + } +} + +_SOKOL_PRIVATE void _sapp_macos_toggle_fullscreen(void) { + /* NOTE: the _sapp.fullscreen flag is also notified by the + windowDidEnterFullscreen / windowDidExitFullscreen + event handlers + */ + _sapp.fullscreen = !_sapp.fullscreen; + [_sapp.macos.window toggleFullScreen:nil]; +} + +_SOKOL_PRIVATE void _sapp_macos_set_clipboard_string(const char* str) { + @autoreleasepool { + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil]; + [pasteboard setString:@(str) forType:NSPasteboardTypeString]; + } +} + +_SOKOL_PRIVATE const char* _sapp_macos_get_clipboard_string(void) { + SOKOL_ASSERT(_sapp.clipboard.buffer); + @autoreleasepool { + _sapp.clipboard.buffer[0] = 0; + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + if (![[pasteboard types] containsObject:NSPasteboardTypeString]) { + return _sapp.clipboard.buffer; + } + NSString* str = [pasteboard stringForType:NSPasteboardTypeString]; + if (!str) { + return _sapp.clipboard.buffer; + } + _sapp_strcpy([str UTF8String], _sapp.clipboard.buffer, _sapp.clipboard.buf_size); + } + return _sapp.clipboard.buffer; +} + +_SOKOL_PRIVATE void _sapp_macos_update_window_title(void) { + [_sapp.macos.window setTitle: [NSString stringWithUTF8String:_sapp.window_title]]; +} + +_SOKOL_PRIVATE void _sapp_macos_mouse_update_from_nspoint(NSPoint mouse_pos, bool clear_dxdy) { + if (!_sapp.mouse.locked) { + float new_x = mouse_pos.x * _sapp.dpi_scale; + float new_y = _sapp.framebuffer_height - (mouse_pos.y * _sapp.dpi_scale) - 1; + if (clear_dxdy) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + } + else if (_sapp.mouse.pos_valid) { + // don't update dx/dy in the very first update + _sapp.mouse.dx = new_x - _sapp.mouse.x; + _sapp.mouse.dy = new_y - _sapp.mouse.y; + } + _sapp.mouse.x = new_x; + _sapp.mouse.y = new_y; + _sapp.mouse.pos_valid = true; + } +} + +_SOKOL_PRIVATE void _sapp_macos_mouse_update_from_nsevent(NSEvent* event, bool clear_dxdy) { + _sapp_macos_mouse_update_from_nspoint(event.locationInWindow, clear_dxdy); +} + +_SOKOL_PRIVATE void _sapp_macos_show_mouse(bool visible) { + /* NOTE: this function is only called when the mouse visibility actually changes */ + if (visible) { + CGDisplayShowCursor(kCGDirectMainDisplay); + } + else { + CGDisplayHideCursor(kCGDirectMainDisplay); + } +} + +_SOKOL_PRIVATE void _sapp_macos_lock_mouse(bool lock) { + if (lock == _sapp.mouse.locked) { + return; + } + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + _sapp.mouse.locked = lock; + /* + NOTE that this code doesn't warp the mouse cursor to the window + center as everybody else does it. This lead to a spike in the + *second* mouse-moved event after the warp happened. The + mouse centering doesn't seem to be required (mouse-moved events + are reported correctly even when the cursor is at an edge of the screen). + + NOTE also that the hide/show of the mouse cursor should properly + stack with calls to sapp_show_mouse() + */ + if (_sapp.mouse.locked) { + CGAssociateMouseAndMouseCursorPosition(NO); + [NSCursor hide]; + } + else { + [NSCursor unhide]; + CGAssociateMouseAndMouseCursorPosition(YES); + } +} + +_SOKOL_PRIVATE void _sapp_macos_update_cursor(sapp_mouse_cursor cursor, bool shown) { + // show/hide cursor only if visibility status has changed (required because show/hide stacks) + if (shown != _sapp.mouse.shown) { + if (shown) { + [NSCursor unhide]; + } + else { + [NSCursor hide]; + } + } + // update cursor type + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + if (_sapp.macos.cursors[cursor]) { + [_sapp.macos.cursors[cursor] set]; + } + else { + [[NSCursor arrowCursor] set]; + } +} + +_SOKOL_PRIVATE void _sapp_macos_set_icon(const sapp_icon_desc* icon_desc, int num_images) { + NSDockTile* dock_tile = NSApp.dockTile; + const int wanted_width = (int) dock_tile.size.width; + const int wanted_height = (int) dock_tile.size.height; + const int img_index = _sapp_image_bestmatch(icon_desc->images, num_images, wanted_width, wanted_height); + const sapp_image_desc* img_desc = &icon_desc->images[img_index]; + + CGColorSpaceRef cg_color_space = CGColorSpaceCreateDeviceRGB(); + CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)img_desc->pixels.ptr, (CFIndex)img_desc->pixels.size); + CGDataProviderRef cg_data_provider = CGDataProviderCreateWithCFData(cf_data); + CGImageRef cg_img = CGImageCreate( + (size_t)img_desc->width, // width + (size_t)img_desc->height, // height + 8, // bitsPerComponent + 32, // bitsPerPixel + (size_t)img_desc->width * 4,// bytesPerRow + cg_color_space, // space + kCGImageAlphaLast | kCGImageByteOrderDefault, // bitmapInfo + cg_data_provider, // provider + NULL, // decode + false, // shouldInterpolate + kCGRenderingIntentDefault); + CFRelease(cf_data); + CGDataProviderRelease(cg_data_provider); + CGColorSpaceRelease(cg_color_space); + + NSImage* ns_image = [[NSImage alloc] initWithCGImage:cg_img size:dock_tile.size]; + dock_tile.contentView = [NSImageView imageViewWithImage:ns_image]; + [dock_tile display]; + _SAPP_OBJC_RELEASE(ns_image); + CGImageRelease(cg_img); +} + +_SOKOL_PRIVATE void _sapp_macos_frame(void) { + _sapp_frame(); + if (_sapp.quit_requested || _sapp.quit_ordered) { + [_sapp.macos.window performClose:nil]; + } +} + +@implementation _sapp_macos_app_delegate +- (void)applicationDidFinishLaunching:(NSNotification*)aNotification { + _SOKOL_UNUSED(aNotification); + _sapp_macos_init_cursors(); + if ((_sapp.window_width == 0) || (_sapp.window_height == 0)) { + // use 4/5 of screen size as default size + NSRect screen_rect = NSScreen.mainScreen.frame; + if (_sapp.window_width == 0) { + _sapp.window_width = (int)roundf((screen_rect.size.width * 4.0f) / 5.0f); + } + if (_sapp.window_height == 0) { + _sapp.window_height = (int)roundf((screen_rect.size.height * 4.0f) / 5.0f); + } + } + const NSUInteger style = + NSWindowStyleMaskTitled | + NSWindowStyleMaskClosable | + NSWindowStyleMaskMiniaturizable | + NSWindowStyleMaskResizable; + NSRect window_rect = NSMakeRect(0, 0, _sapp.window_width, _sapp.window_height); + _sapp.macos.window = [[_sapp_macos_window alloc] + initWithContentRect:window_rect + styleMask:style + backing:NSBackingStoreBuffered + defer:NO]; + _sapp.macos.window.releasedWhenClosed = NO; // this is necessary for proper cleanup in applicationWillTerminate + _sapp.macos.window.title = [NSString stringWithUTF8String:_sapp.window_title]; + _sapp.macos.window.acceptsMouseMovedEvents = YES; + _sapp.macos.window.restorable = YES; + + _sapp.macos.win_dlg = [[_sapp_macos_window_delegate alloc] init]; + _sapp.macos.window.delegate = _sapp.macos.win_dlg; + #if defined(SOKOL_METAL) + NSInteger max_fps = 60; + #if (__MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) + if (@available(macOS 12.0, *)) { + max_fps = [NSScreen.mainScreen maximumFramesPerSecond]; + } + #endif + _sapp.macos.mtl_device = MTLCreateSystemDefaultDevice(); + _sapp.macos.view = [[_sapp_macos_view alloc] init]; + [_sapp.macos.view updateTrackingAreas]; + _sapp.macos.view.preferredFramesPerSecond = max_fps / _sapp.swap_interval; + _sapp.macos.view.device = _sapp.macos.mtl_device; + _sapp.macos.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm; + _sapp.macos.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; + _sapp.macos.view.sampleCount = (NSUInteger) _sapp.sample_count; + _sapp.macos.view.autoResizeDrawable = false; + _sapp.macos.window.contentView = _sapp.macos.view; + [_sapp.macos.window makeFirstResponder:_sapp.macos.view]; + _sapp.macos.view.layer.magnificationFilter = kCAFilterNearest; + #elif defined(SOKOL_GLCORE) + NSOpenGLPixelFormatAttribute attrs[32]; + int i = 0; + attrs[i++] = NSOpenGLPFAAccelerated; + attrs[i++] = NSOpenGLPFADoubleBuffer; + attrs[i++] = NSOpenGLPFAOpenGLProfile; + const int glVersion = _sapp.desc.gl_major_version * 10 + _sapp.desc.gl_minor_version; + switch(glVersion) { + case 10: attrs[i++] = NSOpenGLProfileVersionLegacy; break; + case 32: attrs[i++] = NSOpenGLProfileVersion3_2Core; break; + case 41: attrs[i++] = NSOpenGLProfileVersion4_1Core; break; + default: + _SAPP_PANIC(MACOS_INVALID_NSOPENGL_PROFILE); + } + attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24; + attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8; + attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 24; + attrs[i++] = NSOpenGLPFAStencilSize; attrs[i++] = 8; + if (_sapp.sample_count > 1) { + attrs[i++] = NSOpenGLPFAMultisample; + attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 1; + attrs[i++] = NSOpenGLPFASamples; attrs[i++] = (NSOpenGLPixelFormatAttribute)_sapp.sample_count; + } + else { + attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 0; + } + attrs[i++] = 0; + NSOpenGLPixelFormat* glpixelformat_obj = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; + SOKOL_ASSERT(glpixelformat_obj != nil); + + _sapp.macos.view = [[_sapp_macos_view alloc] + initWithFrame:window_rect + pixelFormat:glpixelformat_obj]; + _SAPP_OBJC_RELEASE(glpixelformat_obj); + [_sapp.macos.view updateTrackingAreas]; + if (_sapp.desc.high_dpi) { + [_sapp.macos.view setWantsBestResolutionOpenGLSurface:YES]; + } + else { + [_sapp.macos.view setWantsBestResolutionOpenGLSurface:NO]; + } + + _sapp.macos.window.contentView = _sapp.macos.view; + [_sapp.macos.window makeFirstResponder:_sapp.macos.view]; + + NSTimer* timer_obj = [NSTimer timerWithTimeInterval:0.001 + target:_sapp.macos.view + selector:@selector(timerFired:) + userInfo:nil + repeats:YES]; + [[NSRunLoop currentRunLoop] addTimer:timer_obj forMode:NSDefaultRunLoopMode]; + timer_obj = nil; + #endif + [_sapp.macos.window center]; + _sapp.valid = true; + if (_sapp.fullscreen) { + /* ^^^ on GL, this already toggles a rendered frame, so set the valid flag before */ + [_sapp.macos.window toggleFullScreen:self]; + } + NSApp.activationPolicy = NSApplicationActivationPolicyRegular; + [NSApp activateIgnoringOtherApps:YES]; + [_sapp.macos.window makeKeyAndOrderFront:nil]; + _sapp_macos_update_dimensions(); + [NSEvent setMouseCoalescingEnabled:NO]; + + // workaround for window not being focused during a long init callback + // for details see: https://github.com/floooh/sokol/pull/982 + // also see: https://gitlab.gnome.org/GNOME/gtk/-/issues/2342 + NSEvent *focusevent = [NSEvent otherEventWithType:NSEventTypeAppKitDefined + location:NSZeroPoint + modifierFlags:0x40 + timestamp:0 + windowNumber:0 + context:nil + subtype:NSEventSubtypeApplicationActivated + data1:0 + data2:0]; + [NSApp postEvent:focusevent atStart:YES]; +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender { + _SOKOL_UNUSED(sender); + return YES; +} + +- (void)applicationWillTerminate:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_call_cleanup(); + _sapp_macos_discard_state(); + _sapp_discard_state(); +} +@end + +@implementation _sapp_macos_window_delegate +- (BOOL)windowShouldClose:(id)sender { + _SOKOL_UNUSED(sender); + /* only give user-code a chance to intervene when sapp_quit() wasn't already called */ + if (!_sapp.quit_ordered) { + /* if window should be closed and event handling is enabled, give user code + a chance to intervene via sapp_cancel_quit() + */ + _sapp.quit_requested = true; + _sapp_macos_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); + /* user code hasn't intervened, quit the app */ + if (_sapp.quit_requested) { + _sapp.quit_ordered = true; + } + } + if (_sapp.quit_ordered) { + return YES; + } + else { + return NO; + } +} + +#if defined(SOKOL_METAL) +- (void)windowWillStartLiveResize:(NSNotification *)notification { + // Work around the MTKView resizing glitch by "anchoring" the layer to the window corner opposite + // to the currently manipulated corner (or edge). This prevents the content stretching back and + // forth during resizing. This is a workaround for this issue: https://github.com/floooh/sokol/issues/700 + // Can be removed if/when migrating to CAMetalLayer: https://github.com/floooh/sokol/issues/727 + bool resizing_from_left = _sapp.mouse.x < _sapp.window_width/2; + bool resizing_from_top = _sapp.mouse.y < _sapp.window_height/2; + NSViewLayerContentsPlacement placement; + if (resizing_from_left) { + placement = resizing_from_top ? NSViewLayerContentsPlacementBottomRight : NSViewLayerContentsPlacementTopRight; + } else { + placement = resizing_from_top ? NSViewLayerContentsPlacementBottomLeft : NSViewLayerContentsPlacementTopLeft; + } + _sapp.macos.view.layerContentsPlacement = placement; +} +#endif + +- (void)windowDidResize:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_macos_update_dimensions(); +} + +- (void)windowDidChangeScreen:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_timing_reset(&_sapp.timing); + _sapp_macos_update_dimensions(); +} + +- (void)windowDidMiniaturize:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_macos_app_event(SAPP_EVENTTYPE_ICONIFIED); +} + +- (void)windowDidDeminiaturize:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_macos_app_event(SAPP_EVENTTYPE_RESTORED); +} + +- (void)windowDidBecomeKey:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_macos_app_event(SAPP_EVENTTYPE_FOCUSED); +} + +- (void)windowDidResignKey:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_macos_app_event(SAPP_EVENTTYPE_UNFOCUSED); +} + +- (void)windowDidEnterFullScreen:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp.fullscreen = true; +} + +- (void)windowDidExitFullScreen:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp.fullscreen = false; +} +@end + +@implementation _sapp_macos_window +- (instancetype)initWithContentRect:(NSRect)contentRect + styleMask:(NSWindowStyleMask)style + backing:(NSBackingStoreType)backingStoreType + defer:(BOOL)flag { + if (self = [super initWithContentRect:contentRect styleMask:style backing:backingStoreType defer:flag]) { + #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 + [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]]; + #endif + } + return self; +} + +- (NSDragOperation)draggingEntered:(id)sender { + return NSDragOperationCopy; +} + +- (NSDragOperation)draggingUpdated:(id)sender { + return NSDragOperationCopy; +} + +- (BOOL)performDragOperation:(id)sender { + #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 + NSPasteboard *pboard = [sender draggingPasteboard]; + if ([pboard.types containsObject:NSPasteboardTypeFileURL]) { + _sapp_clear_drop_buffer(); + _sapp.drop.num_files = ((int)pboard.pasteboardItems.count > _sapp.drop.max_files) ? _sapp.drop.max_files : (int)pboard.pasteboardItems.count; + bool drop_failed = false; + for (int i = 0; i < _sapp.drop.num_files; i++) { + NSURL *fileUrl = [NSURL fileURLWithPath:[pboard.pasteboardItems[(NSUInteger)i] stringForType:NSPasteboardTypeFileURL]]; + if (!_sapp_strcpy(fileUrl.standardizedURL.path.UTF8String, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); + drop_failed = true; + break; + } + } + if (!drop_failed) { + if (_sapp_events_enabled()) { + _sapp_macos_mouse_update_from_nspoint(sender.draggingLocation, true); + _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp.event.modifiers = _sapp_macos_mods(nil); + _sapp_call_event(&_sapp.event); + } + } + else { + _sapp_clear_drop_buffer(); + _sapp.drop.num_files = 0; + } + return YES; + } + #endif + return NO; +} +@end + +@implementation _sapp_macos_view +#if defined(SOKOL_GLCORE) +- (void)timerFired:(id)sender { + _SOKOL_UNUSED(sender); + [self setNeedsDisplay:YES]; +} +- (void)prepareOpenGL { + [super prepareOpenGL]; + GLint swapInt = 1; + NSOpenGLContext* ctx = [_sapp.macos.view openGLContext]; + [ctx setValues:&swapInt forParameter:NSOpenGLContextParameterSwapInterval]; + [ctx makeCurrentContext]; +} +#endif + +_SOKOL_PRIVATE void _sapp_macos_poll_input_events(void) { + /* + + NOTE: late event polling temporarily out-commented to check if this + causes infrequent and almost impossible to reproduce problems with the + window close events, see: + https://github.com/floooh/sokol/pull/483#issuecomment-805148815 + + + const NSEventMask mask = NSEventMaskLeftMouseDown | + NSEventMaskLeftMouseUp| + NSEventMaskRightMouseDown | + NSEventMaskRightMouseUp | + NSEventMaskMouseMoved | + NSEventMaskLeftMouseDragged | + NSEventMaskRightMouseDragged | + NSEventMaskMouseEntered | + NSEventMaskMouseExited | + NSEventMaskKeyDown | + NSEventMaskKeyUp | + NSEventMaskCursorUpdate | + NSEventMaskScrollWheel | + NSEventMaskTabletPoint | + NSEventMaskTabletProximity | + NSEventMaskOtherMouseDown | + NSEventMaskOtherMouseUp | + NSEventMaskOtherMouseDragged | + NSEventMaskPressure | + NSEventMaskDirectTouch; + @autoreleasepool { + for (;;) { + // NOTE: using NSDefaultRunLoopMode here causes stuttering in the GL backend, + // see: https://github.com/floooh/sokol/issues/486 + NSEvent* event = [NSApp nextEventMatchingMask:mask untilDate:nil inMode:NSEventTrackingRunLoopMode dequeue:YES]; + if (event == nil) { + break; + } + [NSApp sendEvent:event]; + } + } + */ +} + +- (void)drawRect:(NSRect)rect { + _SOKOL_UNUSED(rect); + #if defined(_SAPP_ANY_GL) + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer); + #endif + _sapp_timing_measure(&_sapp.timing); + /* Catch any last-moment input events */ + _sapp_macos_poll_input_events(); + @autoreleasepool { + _sapp_macos_frame(); + } + #if defined(_SAPP_ANY_GL) + [[_sapp.macos.view openGLContext] flushBuffer]; + #endif +} + +- (BOOL)isOpaque { + return YES; +} +- (BOOL)canBecomeKeyView { + return YES; +} +- (BOOL)acceptsFirstResponder { + return YES; +} +- (void)updateTrackingAreas { + if (_sapp.macos.tracking_area != nil) { + [self removeTrackingArea:_sapp.macos.tracking_area]; + _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area); + } + const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | + NSTrackingActiveInKeyWindow | + NSTrackingEnabledDuringMouseDrag | + NSTrackingCursorUpdate | + NSTrackingInVisibleRect | + NSTrackingAssumeInside; + _sapp.macos.tracking_area = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil]; + [self addTrackingArea:_sapp.macos.tracking_area]; + [super updateTrackingAreas]; +} + +// helper function to make GL context active +static void _sapp_gl_make_current(void) { + #if defined(SOKOL_GLCORE) + [[_sapp.macos.view openGLContext] makeCurrentContext]; + #endif +} + +- (void)mouseEntered:(NSEvent*)event { + _sapp_gl_make_current(); + _sapp_macos_mouse_update_from_nsevent(event, true); + /* don't send mouse enter/leave while dragging (so that it behaves the same as + on Windows while SetCapture is active + */ + if (0 == _sapp.macos.mouse_buttons) { + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event)); + } +} +- (void)mouseExited:(NSEvent*)event { + _sapp_gl_make_current(); + _sapp_macos_mouse_update_from_nsevent(event, true); + if (0 == _sapp.macos.mouse_buttons) { + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event)); + } +} +- (void)mouseDown:(NSEvent*)event { + _sapp_gl_make_current(); + _sapp_macos_mouse_update_from_nsevent(event, false); + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mods(event)); + _sapp.macos.mouse_buttons |= (1< 0.0f) || (_sapp_absf(dy) > 0.0f)) { + _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + _sapp.event.modifiers = _sapp_macos_mods(event); + _sapp.event.scroll_x = dx; + _sapp.event.scroll_y = dy; + _sapp_call_event(&_sapp.event); + } + } +} +- (void)keyDown:(NSEvent*)event { + if (_sapp_events_enabled()) { + _sapp_gl_make_current(); + const uint32_t mods = _sapp_macos_mods(event); + const sapp_keycode key_code = _sapp_translate_key(event.keyCode); + _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_DOWN, key_code, event.isARepeat, mods); + const NSString* chars = event.characters; + const NSUInteger len = chars.length; + if (len > 0) { + _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp.event.modifiers = mods; + for (NSUInteger i = 0; i < len; i++) { + const unichar codepoint = [chars characterAtIndex:i]; + if ((codepoint & 0xFF00) == 0xF700) { + continue; + } + _sapp.event.char_code = codepoint; + _sapp.event.key_repeat = event.isARepeat; + _sapp_call_event(&_sapp.event); + } + } + /* if this is a Cmd+V (paste), also send a CLIPBOARD_PASTE event */ + if (_sapp.clipboard.enabled && (mods == SAPP_MODIFIER_SUPER) && (key_code == SAPP_KEYCODE_V)) { + _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); + _sapp_call_event(&_sapp.event); + } + } +} +- (void)keyUp:(NSEvent*)event { + _sapp_gl_make_current(); + _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP, + _sapp_translate_key(event.keyCode), + event.isARepeat, + _sapp_macos_mods(event)); +} +- (void)flagsChanged:(NSEvent*)event { + const uint32_t old_f = _sapp.macos.flags_changed_store; + const uint32_t new_f = (uint32_t)event.modifierFlags; + _sapp.macos.flags_changed_store = new_f; + sapp_keycode key_code = SAPP_KEYCODE_INVALID; + bool down = false; + if ((new_f ^ old_f) & NSEventModifierFlagShift) { + key_code = SAPP_KEYCODE_LEFT_SHIFT; + down = 0 != (new_f & NSEventModifierFlagShift); + } + if ((new_f ^ old_f) & NSEventModifierFlagControl) { + key_code = SAPP_KEYCODE_LEFT_CONTROL; + down = 0 != (new_f & NSEventModifierFlagControl); + } + if ((new_f ^ old_f) & NSEventModifierFlagOption) { + key_code = SAPP_KEYCODE_LEFT_ALT; + down = 0 != (new_f & NSEventModifierFlagOption); + } + if ((new_f ^ old_f) & NSEventModifierFlagCommand) { + key_code = SAPP_KEYCODE_LEFT_SUPER; + down = 0 != (new_f & NSEventModifierFlagCommand); + } + if (key_code != SAPP_KEYCODE_INVALID) { + _sapp_macos_key_event(down ? SAPP_EVENTTYPE_KEY_DOWN : SAPP_EVENTTYPE_KEY_UP, + key_code, + false, + _sapp_macos_mods(event)); + } +} +@end + +#endif // macOS + +// ██ ██████ ███████ +// ██ ██ ██ ██ +// ██ ██ ██ ███████ +// ██ ██ ██ ██ +// ██ ██████ ███████ +// +// >>ios +#if defined(_SAPP_IOS) + +_SOKOL_PRIVATE void _sapp_ios_discard_state(void) { + // NOTE: it's safe to call [release] on a nil object + _SAPP_OBJC_RELEASE(_sapp.ios.textfield_dlg); + _SAPP_OBJC_RELEASE(_sapp.ios.textfield); + #if defined(SOKOL_METAL) + _SAPP_OBJC_RELEASE(_sapp.ios.view_ctrl); + _SAPP_OBJC_RELEASE(_sapp.ios.mtl_device); + #else + _SAPP_OBJC_RELEASE(_sapp.ios.view_ctrl); + _SAPP_OBJC_RELEASE(_sapp.ios.eagl_ctx); + #endif + _SAPP_OBJC_RELEASE(_sapp.ios.view); + _SAPP_OBJC_RELEASE(_sapp.ios.window); +} + +_SOKOL_PRIVATE void _sapp_ios_run(const sapp_desc* desc) { + _sapp_init_state(desc); + static int argc = 1; + static char* argv[] = { (char*)"sokol_app" }; + UIApplicationMain(argc, argv, nil, NSStringFromClass([_sapp_app_delegate class])); +} + +/* iOS entry function */ +#if !defined(SOKOL_NO_ENTRY) +int main(int argc, char* argv[]) { + sapp_desc desc = sokol_main(argc, argv); + _sapp_ios_run(&desc); + return 0; +} +#endif /* SOKOL_NO_ENTRY */ + +_SOKOL_PRIVATE void _sapp_ios_app_event(sapp_event_type type) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_ios_touch_event(sapp_event_type type, NSSet* touches, UIEvent* event) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + NSEnumerator* enumerator = event.allTouches.objectEnumerator; + UITouch* ios_touch; + while ((ios_touch = [enumerator nextObject])) { + if ((_sapp.event.num_touches + 1) < SAPP_MAX_TOUCHPOINTS) { + CGPoint ios_pos = [ios_touch locationInView:_sapp.ios.view]; + sapp_touchpoint* cur_point = &_sapp.event.touches[_sapp.event.num_touches++]; + cur_point->identifier = (uintptr_t) ios_touch; + cur_point->pos_x = ios_pos.x * _sapp.dpi_scale; + cur_point->pos_y = ios_pos.y * _sapp.dpi_scale; + cur_point->changed = [touches containsObject:ios_touch]; + } + } + if (_sapp.event.num_touches > 0) { + _sapp_call_event(&_sapp.event); + } + } +} + +_SOKOL_PRIVATE void _sapp_ios_update_dimensions(void) { + CGRect screen_rect = UIScreen.mainScreen.bounds; + _sapp.framebuffer_width = (int)roundf(screen_rect.size.width * _sapp.dpi_scale); + _sapp.framebuffer_height = (int)roundf(screen_rect.size.height * _sapp.dpi_scale); + _sapp.window_width = (int)roundf(screen_rect.size.width); + _sapp.window_height = (int)roundf(screen_rect.size.height); + int cur_fb_width, cur_fb_height; + #if defined(SOKOL_METAL) + const CGSize fb_size = _sapp.ios.view.drawableSize; + cur_fb_width = (int)roundf(fb_size.width); + cur_fb_height = (int)roundf(fb_size.height); + #else + cur_fb_width = (int)roundf(_sapp.ios.view.drawableWidth); + cur_fb_height = (int)roundf(_sapp.ios.view.drawableHeight); + #endif + const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || + (_sapp.framebuffer_height != cur_fb_height); + if (dim_changed) { + #if defined(SOKOL_METAL) + const CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height }; + _sapp.ios.view.drawableSize = drawable_size; + #else + // nothing to do here, GLKView correctly respects the view's contentScaleFactor + #endif + if (!_sapp.first_frame) { + _sapp_ios_app_event(SAPP_EVENTTYPE_RESIZED); + } + } +} + +_SOKOL_PRIVATE void _sapp_ios_frame(void) { + _sapp_ios_update_dimensions(); + _sapp_frame(); +} + +_SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { + /* if not happened yet, create an invisible text field */ + if (nil == _sapp.ios.textfield) { + _sapp.ios.textfield_dlg = [[_sapp_textfield_dlg alloc] init]; + _sapp.ios.textfield = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 100, 50)]; + _sapp.ios.textfield.keyboardType = UIKeyboardTypeDefault; + _sapp.ios.textfield.returnKeyType = UIReturnKeyDefault; + _sapp.ios.textfield.autocapitalizationType = UITextAutocapitalizationTypeNone; + _sapp.ios.textfield.autocorrectionType = UITextAutocorrectionTypeNo; + _sapp.ios.textfield.spellCheckingType = UITextSpellCheckingTypeNo; + _sapp.ios.textfield.hidden = YES; + _sapp.ios.textfield.text = @"x"; + _sapp.ios.textfield.delegate = _sapp.ios.textfield_dlg; + [_sapp.ios.view_ctrl.view addSubview:_sapp.ios.textfield]; + + [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg + selector:@selector(keyboardWasShown:) + name:UIKeyboardDidShowNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg + selector:@selector(keyboardWillBeHidden:) + name:UIKeyboardWillHideNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg + selector:@selector(keyboardDidChangeFrame:) + name:UIKeyboardDidChangeFrameNotification object:nil]; + } + if (shown) { + /* setting the text field as first responder brings up the onscreen keyboard */ + [_sapp.ios.textfield becomeFirstResponder]; + } + else { + [_sapp.ios.textfield resignFirstResponder]; + } +} + +@implementation _sapp_app_delegate +- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + CGRect screen_rect = UIScreen.mainScreen.bounds; + _sapp.ios.window = [[UIWindow alloc] initWithFrame:screen_rect]; + _sapp.window_width = (int)roundf(screen_rect.size.width); + _sapp.window_height = (int)roundf(screen_rect.size.height); + if (_sapp.desc.high_dpi) { + _sapp.dpi_scale = (float) UIScreen.mainScreen.nativeScale; + } + else { + _sapp.dpi_scale = 1.0f; + } + _sapp.framebuffer_width = (int)roundf(_sapp.window_width * _sapp.dpi_scale); + _sapp.framebuffer_height = (int)roundf(_sapp.window_height * _sapp.dpi_scale); + NSInteger max_fps = UIScreen.mainScreen.maximumFramesPerSecond; + #if defined(SOKOL_METAL) + _sapp.ios.mtl_device = MTLCreateSystemDefaultDevice(); + _sapp.ios.view = [[_sapp_ios_view alloc] init]; + _sapp.ios.view.preferredFramesPerSecond = max_fps / _sapp.swap_interval; + _sapp.ios.view.device = _sapp.ios.mtl_device; + _sapp.ios.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm; + _sapp.ios.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; + _sapp.ios.view.sampleCount = (NSUInteger)_sapp.sample_count; + /* NOTE: iOS MTKView seems to ignore thew view's contentScaleFactor + and automatically renders at Retina resolution. We'll disable + autoResize and instead do the resizing in _sapp_ios_update_dimensions() + */ + _sapp.ios.view.autoResizeDrawable = false; + _sapp.ios.view.userInteractionEnabled = YES; + _sapp.ios.view.multipleTouchEnabled = YES; + _sapp.ios.view_ctrl = [[UIViewController alloc] init]; + _sapp.ios.view_ctrl.modalPresentationStyle = UIModalPresentationFullScreen; + _sapp.ios.view_ctrl.view = _sapp.ios.view; + _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl; + #else + _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; + _sapp.ios.view = [[_sapp_ios_view alloc] initWithFrame:screen_rect]; + _sapp.ios.view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; + _sapp.ios.view.drawableDepthFormat = GLKViewDrawableDepthFormat24; + _sapp.ios.view.drawableStencilFormat = GLKViewDrawableStencilFormatNone; + GLKViewDrawableMultisample msaa = _sapp.sample_count > 1 ? GLKViewDrawableMultisample4X : GLKViewDrawableMultisampleNone; + _sapp.ios.view.drawableMultisample = msaa; + _sapp.ios.view.context = _sapp.ios.eagl_ctx; + _sapp.ios.view.enableSetNeedsDisplay = NO; + _sapp.ios.view.userInteractionEnabled = YES; + _sapp.ios.view.multipleTouchEnabled = YES; + // on GLKView, contentScaleFactor appears to work just fine! + if (_sapp.desc.high_dpi) { + _sapp.ios.view.contentScaleFactor = _sapp.dpi_scale; + } + else { + _sapp.ios.view.contentScaleFactor = 1.0; + } + _sapp.ios.view_ctrl = [[GLKViewController alloc] init]; + _sapp.ios.view_ctrl.view = _sapp.ios.view; + _sapp.ios.view_ctrl.preferredFramesPerSecond = max_fps / _sapp.swap_interval; + _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl; + #endif + [_sapp.ios.window makeKeyAndVisible]; + + _sapp.valid = true; + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application { + if (!_sapp.ios.suspended) { + _sapp.ios.suspended = true; + _sapp_ios_app_event(SAPP_EVENTTYPE_SUSPENDED); + } +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + if (_sapp.ios.suspended) { + _sapp.ios.suspended = false; + _sapp_ios_app_event(SAPP_EVENTTYPE_RESUMED); + } +} + +/* NOTE: this method will rarely ever be called, iOS application + which are terminated by the user are usually killed via signal 9 + by the operating system. +*/ +- (void)applicationWillTerminate:(UIApplication *)application { + _SOKOL_UNUSED(application); + _sapp_call_cleanup(); + _sapp_ios_discard_state(); + _sapp_discard_state(); +} +@end + +@implementation _sapp_textfield_dlg +- (void)keyboardWasShown:(NSNotification*)notif { + _sapp.onscreen_keyboard_shown = true; + /* query the keyboard's size, and modify the content view's size */ + if (_sapp.desc.ios_keyboard_resizes_canvas) { + NSDictionary* info = notif.userInfo; + CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; + CGRect view_frame = UIScreen.mainScreen.bounds; + view_frame.size.height -= kbd_h; + _sapp.ios.view.frame = view_frame; + } +} +- (void)keyboardWillBeHidden:(NSNotification*)notif { + _sapp.onscreen_keyboard_shown = false; + if (_sapp.desc.ios_keyboard_resizes_canvas) { + _sapp.ios.view.frame = UIScreen.mainScreen.bounds; + } +} +- (void)keyboardDidChangeFrame:(NSNotification*)notif { + /* this is for the case when the screen rotation changes while the keyboard is open */ + if (_sapp.onscreen_keyboard_shown && _sapp.desc.ios_keyboard_resizes_canvas) { + NSDictionary* info = notif.userInfo; + CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; + CGRect view_frame = UIScreen.mainScreen.bounds; + view_frame.size.height -= kbd_h; + _sapp.ios.view.frame = view_frame; + } +} +- (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string { + if (_sapp_events_enabled()) { + const NSUInteger len = string.length; + if (len > 0) { + for (NSUInteger i = 0; i < len; i++) { + unichar c = [string characterAtIndex:i]; + if (c >= 32) { + /* ignore surrogates for now */ + if ((c < 0xD800) || (c > 0xDFFF)) { + _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp.event.char_code = c; + _sapp_call_event(&_sapp.event); + } + } + if (c <= 32) { + sapp_keycode k = SAPP_KEYCODE_INVALID; + switch (c) { + case 10: k = SAPP_KEYCODE_ENTER; break; + case 32: k = SAPP_KEYCODE_SPACE; break; + default: break; + } + if (k != SAPP_KEYCODE_INVALID) { + _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN); + _sapp.event.key_code = k; + _sapp_call_event(&_sapp.event); + _sapp_init_event(SAPP_EVENTTYPE_KEY_UP); + _sapp.event.key_code = k; + _sapp_call_event(&_sapp.event); + } + } + } + } + else { + /* this was a backspace */ + _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN); + _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE; + _sapp_call_event(&_sapp.event); + _sapp_init_event(SAPP_EVENTTYPE_KEY_UP); + _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE; + _sapp_call_event(&_sapp.event); + } + } + return NO; +} +@end + +@implementation _sapp_ios_view +- (void)drawRect:(CGRect)rect { + _SOKOL_UNUSED(rect); + #if defined(_SAPP_ANY_GL) + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer); + #endif + _sapp_timing_measure(&_sapp.timing); + @autoreleasepool { + _sapp_ios_frame(); + } +} +- (BOOL)isOpaque { + return YES; +} +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event { + _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_BEGAN, touches, event); +} +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent*)event { + _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_MOVED, touches, event); +} +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent*)event { + _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_ENDED, touches, event); +} +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent*)event { + _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_CANCELLED, touches, event); +} +@end +#endif /* TARGET_OS_IPHONE */ + +#endif /* _SAPP_APPLE */ + +// ███████ ███ ███ ███████ ██████ ██████ ██ ██████ ████████ ███████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// █████ ██ ████ ██ ███████ ██ ██████ ██ ██████ ██ █████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ███████ ██████ ██ ██ ██ ██ ██ ███████ ██ ████ +// +// >>emscripten +#if defined(_SAPP_EMSCRIPTEN) + +#if defined(EM_JS_DEPS) +EM_JS_DEPS(sokol_app, "$withStackSave,$stringToUTF8OnStack"); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*_sapp_html5_fetch_callback) (const sapp_html5_fetch_response*); + +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_onpaste(const char* str) { + if (_sapp.clipboard.enabled) { + _sapp_strcpy(str, _sapp.clipboard.buffer, _sapp.clipboard.buf_size); + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); + _sapp_call_event(&_sapp.event); + } + } +} + +/* https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload */ +EMSCRIPTEN_KEEPALIVE int _sapp_html5_get_ask_leave_site(void) { + return _sapp.html5_ask_leave_site ? 1 : 0; +} + +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_begin_drop(int num) { + if (!_sapp.drop.enabled) { + return; + } + if (num < 0) { + num = 0; + } + if (num > _sapp.drop.max_files) { + num = _sapp.drop.max_files; + } + _sapp.drop.num_files = num; + _sapp_clear_drop_buffer(); +} + +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_drop(int i, const char* name) { + /* NOTE: name is only the filename part, not a path */ + if (!_sapp.drop.enabled) { + return; + } + if (0 == name) { + return; + } + SOKOL_ASSERT(_sapp.drop.num_files <= _sapp.drop.max_files); + if ((i < 0) || (i >= _sapp.drop.num_files)) { + return; + } + if (!_sapp_strcpy(name, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); + _sapp.drop.num_files = 0; + } +} + +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_end_drop(int x, int y, int mods) { + if (!_sapp.drop.enabled) { + return; + } + if (0 == _sapp.drop.num_files) { + /* there was an error copying the filenames */ + _sapp_clear_drop_buffer(); + return; + + } + if (_sapp_events_enabled()) { + _sapp.mouse.x = (float)x * _sapp.dpi_scale; + _sapp.mouse.y = (float)y * _sapp.dpi_scale; + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + // see sapp_js_add_dragndrop_listeners for mods constants + if (mods & 1) { _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; } + if (mods & 2) { _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; } + if (mods & 4) { _sapp.event.modifiers |= SAPP_MODIFIER_ALT; } + if (mods & 8) { _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; } + _sapp_call_event(&_sapp.event); + } +} + +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_invoke_fetch_cb(int index, int success, int error_code, _sapp_html5_fetch_callback callback, uint32_t fetched_size, void* buf_ptr, uint32_t buf_size, void* user_data) { + sapp_html5_fetch_response response; + _sapp_clear(&response, sizeof(response)); + response.succeeded = (0 != success); + response.error_code = (sapp_html5_fetch_error) error_code; + response.file_index = index; + response.data.ptr = buf_ptr; + response.data.size = fetched_size; + response.buffer.ptr = buf_ptr; + response.buffer.size = buf_size; + response.user_data = user_data; + callback(&response); +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +EM_JS(void, sapp_js_add_beforeunload_listener, (void), { + Module.sokol_beforeunload = (event) => { + if (__sapp_html5_get_ask_leave_site() != 0) { + event.preventDefault(); + event.returnValue = ' '; + } + }; + window.addEventListener('beforeunload', Module.sokol_beforeunload); +}); + +EM_JS(void, sapp_js_remove_beforeunload_listener, (void), { + window.removeEventListener('beforeunload', Module.sokol_beforeunload); +}); + +EM_JS(void, sapp_js_add_clipboard_listener, (void), { + Module.sokol_paste = (event) => { + const pasted_str = event.clipboardData.getData('text'); + withStackSave(() => { + const cstr = stringToUTF8OnStack(pasted_str); + __sapp_emsc_onpaste(cstr); + }); + }; + window.addEventListener('paste', Module.sokol_paste); +}); + +EM_JS(void, sapp_js_remove_clipboard_listener, (void), { + window.removeEventListener('paste', Module.sokol_paste); +}); + +EM_JS(void, sapp_js_write_clipboard, (const char* c_str), { + const str = UTF8ToString(c_str); + const ta = document.createElement('textarea'); + ta.setAttribute('autocomplete', 'off'); + ta.setAttribute('autocorrect', 'off'); + ta.setAttribute('autocapitalize', 'off'); + ta.setAttribute('spellcheck', 'false'); + ta.style.left = -100 + 'px'; + ta.style.top = -100 + 'px'; + ta.style.height = 1; + ta.style.width = 1; + ta.value = str; + document.body.appendChild(ta); + ta.select(); + document.execCommand('copy'); + document.body.removeChild(ta); +}); + +_SOKOL_PRIVATE void _sapp_emsc_set_clipboard_string(const char* str) { + sapp_js_write_clipboard(str); +} + +EM_JS(void, sapp_js_add_dragndrop_listeners, (const char* canvas_name_cstr), { + Module.sokol_drop_files = []; + const canvas_name = UTF8ToString(canvas_name_cstr); + const canvas = document.getElementById(canvas_name); + Module.sokol_dragenter = (event) => { + event.stopPropagation(); + event.preventDefault(); + }; + Module.sokol_dragleave = (event) => { + event.stopPropagation(); + event.preventDefault(); + }; + Module.sokol_dragover = (event) => { + event.stopPropagation(); + event.preventDefault(); + }; + Module.sokol_drop = (event) => { + event.stopPropagation(); + event.preventDefault(); + const files = event.dataTransfer.files; + Module.sokol_dropped_files = files; + __sapp_emsc_begin_drop(files.length); + for (let i = 0; i < files.length; i++) { + withStackSave(() => { + const cstr = stringToUTF8OnStack(files[i].name); + __sapp_emsc_drop(i, cstr); + }); + } + let mods = 0; + if (event.shiftKey) { mods |= 1; } + if (event.ctrlKey) { mods |= 2; } + if (event.altKey) { mods |= 4; } + if (event.metaKey) { mods |= 8; } + // FIXME? see computation of targetX/targetY in emscripten via getClientBoundingRect + __sapp_emsc_end_drop(event.clientX, event.clientY, mods); + }; + canvas.addEventListener('dragenter', Module.sokol_dragenter, false); + canvas.addEventListener('dragleave', Module.sokol_dragleave, false); + canvas.addEventListener('dragover', Module.sokol_dragover, false); + canvas.addEventListener('drop', Module.sokol_drop, false); +}); + +EM_JS(uint32_t, sapp_js_dropped_file_size, (int index), { + \x2F\x2A\x2A @suppress {missingProperties} \x2A\x2F + const files = Module.sokol_dropped_files; + if ((index < 0) || (index >= files.length)) { + return 0; + } + else { + return files[index].size; + } +}); + +EM_JS(void, sapp_js_fetch_dropped_file, (int index, _sapp_html5_fetch_callback callback, void* buf_ptr, uint32_t buf_size, void* user_data), { + const reader = new FileReader(); + reader.onload = (loadEvent) => { + const content = loadEvent.target.result; + if (content.byteLength > buf_size) { + // SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL + __sapp_emsc_invoke_fetch_cb(index, 0, 1, callback, 0, buf_ptr, buf_size, user_data); + } + else { + HEAPU8.set(new Uint8Array(content), buf_ptr); + __sapp_emsc_invoke_fetch_cb(index, 1, 0, callback, content.byteLength, buf_ptr, buf_size, user_data); + } + }; + reader.onerror = () => { + // SAPP_HTML5_FETCH_ERROR_OTHER + __sapp_emsc_invoke_fetch_cb(index, 0, 2, callback, 0, buf_ptr, buf_size, user_data); + }; + \x2F\x2A\x2A @suppress {missingProperties} \x2A\x2F + const files = Module.sokol_dropped_files; + reader.readAsArrayBuffer(files[index]); +}); + +EM_JS(void, sapp_js_remove_dragndrop_listeners, (const char* canvas_name_cstr), { + const canvas_name = UTF8ToString(canvas_name_cstr); + const canvas = document.getElementById(canvas_name); + canvas.removeEventListener('dragenter', Module.sokol_dragenter); + canvas.removeEventListener('dragleave', Module.sokol_dragleave); + canvas.removeEventListener('dragover', Module.sokol_dragover); + canvas.removeEventListener('drop', Module.sokol_drop); +}); + +EM_JS(void, sapp_js_init, (const char* c_str_target), { + // lookup and store canvas object by name + const target_str = UTF8ToString(c_str_target); + Module.sapp_emsc_target = document.getElementById(target_str); + if (!Module.sapp_emsc_target) { + console.log("sokol_app.h: invalid target:" + target_str); + } + if (!Module.sapp_emsc_target.requestPointerLock) { + console.log("sokol_app.h: target doesn't support requestPointerLock:" + target_str); + } +}); + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockchange_cb(int emsc_type, const EmscriptenPointerlockChangeEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(emsc_type); + _SOKOL_UNUSED(user_data); + _sapp.mouse.locked = emsc_event->isActive; + return EM_TRUE; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockerror_cb(int emsc_type, const void* reserved, void* user_data) { + _SOKOL_UNUSED(emsc_type); + _SOKOL_UNUSED(reserved); + _SOKOL_UNUSED(user_data); + _sapp.mouse.locked = false; + _sapp.emsc.mouse_lock_requested = false; + return true; +} + +EM_JS(void, sapp_js_request_pointerlock, (void), { + if (Module.sapp_emsc_target) { + if (Module.sapp_emsc_target.requestPointerLock) { + Module.sapp_emsc_target.requestPointerLock(); + } + } +}); + +EM_JS(void, sapp_js_exit_pointerlock, (void), { + if (document.exitPointerLock) { + document.exitPointerLock(); + } +}); + +_SOKOL_PRIVATE void _sapp_emsc_lock_mouse(bool lock) { + if (lock) { + /* request mouse-lock during event handler invocation (see _sapp_emsc_update_mouse_lock_state) */ + _sapp.emsc.mouse_lock_requested = true; + } + else { + /* NOTE: the _sapp.mouse_locked state will be set in the pointerlockchange callback */ + _sapp.emsc.mouse_lock_requested = false; + sapp_js_exit_pointerlock(); + } +} + +/* called from inside event handlers to check if mouse lock had been requested, + and if yes, actually enter mouse lock. +*/ +_SOKOL_PRIVATE void _sapp_emsc_update_mouse_lock_state(void) { + if (_sapp.emsc.mouse_lock_requested) { + _sapp.emsc.mouse_lock_requested = false; + sapp_js_request_pointerlock(); + } +} + +// set mouse cursor type +EM_JS(void, sapp_js_set_cursor, (int cursor_type, int shown), { + if (Module.sapp_emsc_target) { + let cursor; + if (shown === 0) { + cursor = "none"; + } + else switch (cursor_type) { + case 0: cursor = "auto"; break; // SAPP_MOUSECURSOR_DEFAULT + case 1: cursor = "default"; break; // SAPP_MOUSECURSOR_ARROW + case 2: cursor = "text"; break; // SAPP_MOUSECURSOR_IBEAM + case 3: cursor = "crosshair"; break; // SAPP_MOUSECURSOR_CROSSHAIR + case 4: cursor = "pointer"; break; // SAPP_MOUSECURSOR_POINTING_HAND + case 5: cursor = "ew-resize"; break; // SAPP_MOUSECURSOR_RESIZE_EW + case 6: cursor = "ns-resize"; break; // SAPP_MOUSECURSOR_RESIZE_NS + case 7: cursor = "nwse-resize"; break; // SAPP_MOUSECURSOR_RESIZE_NWSE + case 8: cursor = "nesw-resize"; break; // SAPP_MOUSECURSOR_RESIZE_NESW + case 9: cursor = "all-scroll"; break; // SAPP_MOUSECURSOR_RESIZE_ALL + case 10: cursor = "not-allowed"; break; // SAPP_MOUSECURSOR_NOT_ALLOWED + default: cursor = "auto"; break; + } + Module.sapp_emsc_target.style.cursor = cursor; + } +}); + +_SOKOL_PRIVATE void _sapp_emsc_update_cursor(sapp_mouse_cursor cursor, bool shown) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + sapp_js_set_cursor((int)cursor, shown ? 1 : 0); +} + +/* JS helper functions to update browser tab favicon */ +EM_JS(void, sapp_js_clear_favicon, (void), { + const link = document.getElementById('sokol-app-favicon'); + if (link) { + document.head.removeChild(link); + } +}); + +EM_JS(void, sapp_js_set_favicon, (int w, int h, const uint8_t* pixels), { + const canvas = document.createElement('canvas'); + canvas.width = w; + canvas.height = h; + const ctx = canvas.getContext('2d'); + const img_data = ctx.createImageData(w, h); + img_data.data.set(HEAPU8.subarray(pixels, pixels + w*h*4)); + ctx.putImageData(img_data, 0, 0); + const new_link = document.createElement('link'); + new_link.id = 'sokol-app-favicon'; + new_link.rel = 'shortcut icon'; + new_link.href = canvas.toDataURL(); + document.head.appendChild(new_link); +}); + +_SOKOL_PRIVATE void _sapp_emsc_set_icon(const sapp_icon_desc* icon_desc, int num_images) { + SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES)); + sapp_js_clear_favicon(); + // find the best matching image candidate for 16x16 pixels + int img_index = _sapp_image_bestmatch(icon_desc->images, num_images, 16, 16); + const sapp_image_desc* img_desc = &icon_desc->images[img_index]; + sapp_js_set_favicon(img_desc->width, img_desc->height, (const uint8_t*) img_desc->pixels.ptr); +} + +_SOKOL_PRIVATE uint32_t _sapp_emsc_mouse_button_mods(uint16_t buttons) { + uint32_t m = 0; + if (0 != (buttons & (1<<0))) { m |= SAPP_MODIFIER_LMB; } + if (0 != (buttons & (1<<1))) { m |= SAPP_MODIFIER_RMB; } // not a bug + if (0 != (buttons & (1<<2))) { m |= SAPP_MODIFIER_MMB; } // not a bug + return m; +} + +_SOKOL_PRIVATE uint32_t _sapp_emsc_mouse_event_mods(const EmscriptenMouseEvent* ev) { + uint32_t m = 0; + if (ev->ctrlKey) { m |= SAPP_MODIFIER_CTRL; } + if (ev->shiftKey) { m |= SAPP_MODIFIER_SHIFT; } + if (ev->altKey) { m |= SAPP_MODIFIER_ALT; } + if (ev->metaKey) { m |= SAPP_MODIFIER_SUPER; } + m |= _sapp_emsc_mouse_button_mods(_sapp.emsc.mouse_buttons); + return m; +} + +_SOKOL_PRIVATE uint32_t _sapp_emsc_key_event_mods(const EmscriptenKeyboardEvent* ev) { + uint32_t m = 0; + if (ev->ctrlKey) { m |= SAPP_MODIFIER_CTRL; } + if (ev->shiftKey) { m |= SAPP_MODIFIER_SHIFT; } + if (ev->altKey) { m |= SAPP_MODIFIER_ALT; } + if (ev->metaKey) { m |= SAPP_MODIFIER_SUPER; } + m |= _sapp_emsc_mouse_button_mods(_sapp.emsc.mouse_buttons); + return m; +} + +_SOKOL_PRIVATE uint32_t _sapp_emsc_touch_event_mods(const EmscriptenTouchEvent* ev) { + uint32_t m = 0; + if (ev->ctrlKey) { m |= SAPP_MODIFIER_CTRL; } + if (ev->shiftKey) { m |= SAPP_MODIFIER_SHIFT; } + if (ev->altKey) { m |= SAPP_MODIFIER_ALT; } + if (ev->metaKey) { m |= SAPP_MODIFIER_SUPER; } + m |= _sapp_emsc_mouse_button_mods(_sapp.emsc.mouse_buttons); + return m; +} + +#if defined(SOKOL_WGPU) +_SOKOL_PRIVATE void _sapp_emsc_wgpu_size_changed(void); +#endif + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenUiEvent* ui_event, void* user_data) { + _SOKOL_UNUSED(event_type); + _SOKOL_UNUSED(user_data); + double w, h; + emscripten_get_element_css_size(_sapp.html5_canvas_selector, &w, &h); + /* The above method might report zero when toggling HTML5 fullscreen, + in that case use the window's inner width reported by the + emscripten event. This works ok when toggling *into* fullscreen + but doesn't properly restore the previous canvas size when switching + back from fullscreen. + + In general, due to the HTML5's fullscreen API's flaky nature it is + recommended to use 'soft fullscreen' (stretching the WebGL canvas + over the browser windows client rect) with a CSS definition like this: + + position: absolute; + top: 0px; + left: 0px; + margin: 0px; + border: 0; + width: 100%; + height: 100%; + overflow: hidden; + display: block; + */ + if (w < 1.0) { + w = ui_event->windowInnerWidth; + } + else { + _sapp.window_width = (int)roundf(w); + } + if (h < 1.0) { + h = ui_event->windowInnerHeight; + } + else { + _sapp.window_height = (int)roundf(h); + } + if (_sapp.desc.high_dpi) { + _sapp.dpi_scale = emscripten_get_device_pixel_ratio(); + } + _sapp.framebuffer_width = (int)roundf(w * _sapp.dpi_scale); + _sapp.framebuffer_height = (int)roundf(h * _sapp.dpi_scale); + SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0)); + emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height); + #if defined(SOKOL_WGPU) + // on WebGPU: recreate size-dependent rendering surfaces + _sapp_emsc_wgpu_size_changed(); + #endif + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_RESIZED); + _sapp_call_event(&_sapp.event); + } + return true; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(user_data); + bool consume_event = !_sapp.desc.html5_bubble_mouse_events; + _sapp.emsc.mouse_buttons = emsc_event->buttons; + if (_sapp.mouse.locked) { + _sapp.mouse.dx = (float) emsc_event->movementX; + _sapp.mouse.dy = (float) emsc_event->movementY; + } else { + float new_x = emsc_event->targetX * _sapp.dpi_scale; + float new_y = emsc_event->targetY * _sapp.dpi_scale; + if (_sapp.mouse.pos_valid) { + _sapp.mouse.dx = new_x - _sapp.mouse.x; + _sapp.mouse.dy = new_y - _sapp.mouse.y; + } + _sapp.mouse.x = new_x; + _sapp.mouse.y = new_y; + _sapp.mouse.pos_valid = true; + } + if (_sapp_events_enabled() && (emsc_event->button >= 0) && (emsc_event->button < SAPP_MAX_MOUSEBUTTONS)) { + sapp_event_type type; + bool is_button_event = false; + bool clear_dxdy = false; + switch (emsc_type) { + case EMSCRIPTEN_EVENT_MOUSEDOWN: + type = SAPP_EVENTTYPE_MOUSE_DOWN; + is_button_event = true; + break; + case EMSCRIPTEN_EVENT_MOUSEUP: + type = SAPP_EVENTTYPE_MOUSE_UP; + is_button_event = true; + break; + case EMSCRIPTEN_EVENT_MOUSEMOVE: + type = SAPP_EVENTTYPE_MOUSE_MOVE; + break; + case EMSCRIPTEN_EVENT_MOUSEENTER: + type = SAPP_EVENTTYPE_MOUSE_ENTER; + clear_dxdy = true; + break; + case EMSCRIPTEN_EVENT_MOUSELEAVE: + type = SAPP_EVENTTYPE_MOUSE_LEAVE; + clear_dxdy = true; + break; + default: + type = SAPP_EVENTTYPE_INVALID; + break; + } + if (clear_dxdy) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + } + if (type != SAPP_EVENTTYPE_INVALID) { + _sapp_init_event(type); + _sapp.event.modifiers = _sapp_emsc_mouse_event_mods(emsc_event); + if (is_button_event) { + switch (emsc_event->button) { + case 0: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_LEFT; break; + case 1: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_MIDDLE; break; + case 2: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_RIGHT; break; + default: _sapp.event.mouse_button = (sapp_mousebutton)emsc_event->button; break; + } + } else { + _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; + } + consume_event |= _sapp_call_event(&_sapp.event); + } + // mouse lock can only be activated in mouse button events (not in move, enter or leave) + if (is_button_event) { + _sapp_emsc_update_mouse_lock_state(); + } + } + return consume_event; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_wheel_cb(int emsc_type, const EmscriptenWheelEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(emsc_type); + _SOKOL_UNUSED(user_data); + bool consume_event = !_sapp.desc.html5_bubble_wheel_events; + _sapp.emsc.mouse_buttons = emsc_event->mouse.buttons; + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + _sapp.event.modifiers = _sapp_emsc_mouse_event_mods(&emsc_event->mouse); + /* see https://github.com/floooh/sokol/issues/339 */ + float scale; + switch (emsc_event->deltaMode) { + case DOM_DELTA_PIXEL: scale = -0.04f; break; + case DOM_DELTA_LINE: scale = -1.33f; break; + case DOM_DELTA_PAGE: scale = -10.0f; break; // FIXME: this is a guess + default: scale = -0.1f; break; // shouldn't happen + } + _sapp.event.scroll_x = scale * (float)emsc_event->deltaX; + _sapp.event.scroll_y = scale * (float)emsc_event->deltaY; + consume_event |= _sapp_call_event(&_sapp.event); + } + _sapp_emsc_update_mouse_lock_state(); + return consume_event; +} + +static struct { + const char* str; + sapp_keycode code; +} _sapp_emsc_keymap[] = { + { "Backspace", SAPP_KEYCODE_BACKSPACE }, + { "Tab", SAPP_KEYCODE_TAB }, + { "Enter", SAPP_KEYCODE_ENTER }, + { "ShiftLeft", SAPP_KEYCODE_LEFT_SHIFT }, + { "ShiftRight", SAPP_KEYCODE_RIGHT_SHIFT }, + { "ControlLeft", SAPP_KEYCODE_LEFT_CONTROL }, + { "ControlRight", SAPP_KEYCODE_RIGHT_CONTROL }, + { "AltLeft", SAPP_KEYCODE_LEFT_ALT }, + { "AltRight", SAPP_KEYCODE_RIGHT_ALT }, + { "Pause", SAPP_KEYCODE_PAUSE }, + { "CapsLock", SAPP_KEYCODE_CAPS_LOCK }, + { "Escape", SAPP_KEYCODE_ESCAPE }, + { "Space", SAPP_KEYCODE_SPACE }, + { "PageUp", SAPP_KEYCODE_PAGE_UP }, + { "PageDown", SAPP_KEYCODE_PAGE_DOWN }, + { "End", SAPP_KEYCODE_END }, + { "Home", SAPP_KEYCODE_HOME }, + { "ArrowLeft", SAPP_KEYCODE_LEFT }, + { "ArrowUp", SAPP_KEYCODE_UP }, + { "ArrowRight", SAPP_KEYCODE_RIGHT }, + { "ArrowDown", SAPP_KEYCODE_DOWN }, + { "PrintScreen", SAPP_KEYCODE_PRINT_SCREEN }, + { "Insert", SAPP_KEYCODE_INSERT }, + { "Delete", SAPP_KEYCODE_DELETE }, + { "Digit0", SAPP_KEYCODE_0 }, + { "Digit1", SAPP_KEYCODE_1 }, + { "Digit2", SAPP_KEYCODE_2 }, + { "Digit3", SAPP_KEYCODE_3 }, + { "Digit4", SAPP_KEYCODE_4 }, + { "Digit5", SAPP_KEYCODE_5 }, + { "Digit6", SAPP_KEYCODE_6 }, + { "Digit7", SAPP_KEYCODE_7 }, + { "Digit8", SAPP_KEYCODE_8 }, + { "Digit9", SAPP_KEYCODE_9 }, + { "KeyA", SAPP_KEYCODE_A }, + { "KeyB", SAPP_KEYCODE_B }, + { "KeyC", SAPP_KEYCODE_C }, + { "KeyD", SAPP_KEYCODE_D }, + { "KeyE", SAPP_KEYCODE_E }, + { "KeyF", SAPP_KEYCODE_F }, + { "KeyG", SAPP_KEYCODE_G }, + { "KeyH", SAPP_KEYCODE_H }, + { "KeyI", SAPP_KEYCODE_I }, + { "KeyJ", SAPP_KEYCODE_J }, + { "KeyK", SAPP_KEYCODE_K }, + { "KeyL", SAPP_KEYCODE_L }, + { "KeyM", SAPP_KEYCODE_M }, + { "KeyN", SAPP_KEYCODE_N }, + { "KeyO", SAPP_KEYCODE_O }, + { "KeyP", SAPP_KEYCODE_P }, + { "KeyQ", SAPP_KEYCODE_Q }, + { "KeyR", SAPP_KEYCODE_R }, + { "KeyS", SAPP_KEYCODE_S }, + { "KeyT", SAPP_KEYCODE_T }, + { "KeyU", SAPP_KEYCODE_U }, + { "KeyV", SAPP_KEYCODE_V }, + { "KeyW", SAPP_KEYCODE_W }, + { "KeyX", SAPP_KEYCODE_X }, + { "KeyY", SAPP_KEYCODE_Y }, + { "KeyZ", SAPP_KEYCODE_Z }, + { "MetaLeft", SAPP_KEYCODE_LEFT_SUPER }, + { "MetaRight", SAPP_KEYCODE_RIGHT_SUPER }, + { "Numpad0", SAPP_KEYCODE_KP_0 }, + { "Numpad1", SAPP_KEYCODE_KP_1 }, + { "Numpad2", SAPP_KEYCODE_KP_2 }, + { "Numpad3", SAPP_KEYCODE_KP_3 }, + { "Numpad4", SAPP_KEYCODE_KP_4 }, + { "Numpad5", SAPP_KEYCODE_KP_5 }, + { "Numpad6", SAPP_KEYCODE_KP_6 }, + { "Numpad7", SAPP_KEYCODE_KP_7 }, + { "Numpad8", SAPP_KEYCODE_KP_8 }, + { "Numpad9", SAPP_KEYCODE_KP_9 }, + { "NumpadMultiply", SAPP_KEYCODE_KP_MULTIPLY }, + { "NumpadAdd", SAPP_KEYCODE_KP_ADD }, + { "NumpadSubtract", SAPP_KEYCODE_KP_SUBTRACT }, + { "NumpadDecimal", SAPP_KEYCODE_KP_DECIMAL }, + { "NumpadDivide", SAPP_KEYCODE_KP_DIVIDE }, + { "F1", SAPP_KEYCODE_F1 }, + { "F2", SAPP_KEYCODE_F2 }, + { "F3", SAPP_KEYCODE_F3 }, + { "F4", SAPP_KEYCODE_F4 }, + { "F5", SAPP_KEYCODE_F5 }, + { "F6", SAPP_KEYCODE_F6 }, + { "F7", SAPP_KEYCODE_F7 }, + { "F8", SAPP_KEYCODE_F8 }, + { "F9", SAPP_KEYCODE_F9 }, + { "F10", SAPP_KEYCODE_F10 }, + { "F11", SAPP_KEYCODE_F11 }, + { "F12", SAPP_KEYCODE_F12 }, + { "NumLock", SAPP_KEYCODE_NUM_LOCK }, + { "ScrollLock", SAPP_KEYCODE_SCROLL_LOCK }, + { "Semicolon", SAPP_KEYCODE_SEMICOLON }, + { "Equal", SAPP_KEYCODE_EQUAL }, + { "Comma", SAPP_KEYCODE_COMMA }, + { "Minus", SAPP_KEYCODE_MINUS }, + { "Period", SAPP_KEYCODE_PERIOD }, + { "Slash", SAPP_KEYCODE_SLASH }, + { "Backquote", SAPP_KEYCODE_GRAVE_ACCENT }, + { "BracketLeft", SAPP_KEYCODE_LEFT_BRACKET }, + { "Backslash", SAPP_KEYCODE_BACKSLASH }, + { "BracketRight", SAPP_KEYCODE_RIGHT_BRACKET }, + { "Quote", SAPP_KEYCODE_GRAVE_ACCENT }, // FIXME: ??? + { 0, SAPP_KEYCODE_INVALID }, +}; + +_SOKOL_PRIVATE sapp_keycode _sapp_emsc_translate_key(const char* str) { + int i = 0; + const char* keystr; + while (( keystr = _sapp_emsc_keymap[i].str )) { + if (0 == strcmp(str, keystr)) { + return _sapp_emsc_keymap[i].code; + } + i += 1; + } + return SAPP_KEYCODE_INVALID; +} + +// returns true if the key code is a 'character key', this is used to decide +// if a key event needs to bubble up to create a char event +_SOKOL_PRIVATE bool _sapp_emsc_is_char_key(sapp_keycode key_code) { + return key_code < SAPP_KEYCODE_WORLD_1; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboardEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(user_data); + bool consume_event = false; + if (_sapp_events_enabled()) { + sapp_event_type type; + switch (emsc_type) { + case EMSCRIPTEN_EVENT_KEYDOWN: + type = SAPP_EVENTTYPE_KEY_DOWN; + break; + case EMSCRIPTEN_EVENT_KEYUP: + type = SAPP_EVENTTYPE_KEY_UP; + break; + case EMSCRIPTEN_EVENT_KEYPRESS: + type = SAPP_EVENTTYPE_CHAR; + break; + default: + type = SAPP_EVENTTYPE_INVALID; + break; + } + if (type != SAPP_EVENTTYPE_INVALID) { + bool send_keyup_followup = false; + _sapp_init_event(type); + _sapp.event.key_repeat = emsc_event->repeat; + _sapp.event.modifiers = _sapp_emsc_key_event_mods(emsc_event); + if (type == SAPP_EVENTTYPE_CHAR) { + // NOTE: charCode doesn't appear to be supported on Android Chrome + _sapp.event.char_code = emsc_event->charCode; + consume_event |= !_sapp.desc.html5_bubble_char_events; + } else { + if (0 != emsc_event->code[0]) { + // This code path is for desktop browsers which send untranslated 'physical' key code strings + // (which is what we actually want for key events) + _sapp.event.key_code = _sapp_emsc_translate_key(emsc_event->code); + } else { + // This code path is for mobile browsers which only send localized key code + // strings. Note that the translation will only work for a small subset + // of localization-agnostic keys (like Enter, arrow keys, etc...), but + // regular alpha-numeric keys will all result in an SAPP_KEYCODE_INVALID) + _sapp.event.key_code = _sapp_emsc_translate_key(emsc_event->key); + } + + // Special hack for macOS: if the Super key is pressed, macOS doesn't + // send keyUp events. As a workaround, to prevent keys from + // "sticking", we'll send a keyup event following a keydown + // when the SUPER key is pressed + if ((type == SAPP_EVENTTYPE_KEY_DOWN) && + (_sapp.event.key_code != SAPP_KEYCODE_LEFT_SUPER) && + (_sapp.event.key_code != SAPP_KEYCODE_RIGHT_SUPER) && + (_sapp.event.modifiers & SAPP_MODIFIER_SUPER)) + { + send_keyup_followup = true; + } + + // 'character key events' will always need to bubble up, otherwise the browser + // wouldn't be able to generate character events. + if (!_sapp_emsc_is_char_key(_sapp.event.key_code)) { + consume_event |= !_sapp.desc.html5_bubble_key_events; + } + } + consume_event |= _sapp_call_event(&_sapp.event); + if (send_keyup_followup) { + _sapp.event.type = SAPP_EVENTTYPE_KEY_UP; + consume_event |= _sapp_call_event(&_sapp.event); + } + } + } + _sapp_emsc_update_mouse_lock_state(); + return consume_event; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_touch_cb(int emsc_type, const EmscriptenTouchEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(user_data); + bool consume_event = !_sapp.desc.html5_bubble_touch_events; + if (_sapp_events_enabled()) { + sapp_event_type type; + switch (emsc_type) { + case EMSCRIPTEN_EVENT_TOUCHSTART: + type = SAPP_EVENTTYPE_TOUCHES_BEGAN; + break; + case EMSCRIPTEN_EVENT_TOUCHMOVE: + type = SAPP_EVENTTYPE_TOUCHES_MOVED; + break; + case EMSCRIPTEN_EVENT_TOUCHEND: + type = SAPP_EVENTTYPE_TOUCHES_ENDED; + break; + case EMSCRIPTEN_EVENT_TOUCHCANCEL: + type = SAPP_EVENTTYPE_TOUCHES_CANCELLED; + break; + default: + type = SAPP_EVENTTYPE_INVALID; + break; + } + if (type != SAPP_EVENTTYPE_INVALID) { + _sapp_init_event(type); + _sapp.event.modifiers = _sapp_emsc_touch_event_mods(emsc_event); + _sapp.event.num_touches = emsc_event->numTouches; + if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) { + _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS; + } + for (int i = 0; i < _sapp.event.num_touches; i++) { + const EmscriptenTouchPoint* src = &emsc_event->touches[i]; + sapp_touchpoint* dst = &_sapp.event.touches[i]; + dst->identifier = (uintptr_t)src->identifier; + dst->pos_x = src->targetX * _sapp.dpi_scale; + dst->pos_y = src->targetY * _sapp.dpi_scale; + dst->changed = src->isChanged; + } + consume_event |= _sapp_call_event(&_sapp.event); + } + } + return consume_event; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_focus_cb(int emsc_type, const EmscriptenFocusEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(emsc_type); + _SOKOL_UNUSED(emsc_event); + _SOKOL_UNUSED(user_data); + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_FOCUSED); + _sapp_call_event(&_sapp.event); + } + return true; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_blur_cb(int emsc_type, const EmscriptenFocusEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(emsc_type); + _SOKOL_UNUSED(emsc_event); + _SOKOL_UNUSED(user_data); + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_UNFOCUSED); + _sapp_call_event(&_sapp.event); + } + return true; +} + +#if defined(SOKOL_GLES3) +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_webgl_context_cb(int emsc_type, const void* reserved, void* user_data) { + _SOKOL_UNUSED(reserved); + _SOKOL_UNUSED(user_data); + sapp_event_type type; + switch (emsc_type) { + case EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST: type = SAPP_EVENTTYPE_SUSPENDED; break; + case EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED: type = SAPP_EVENTTYPE_RESUMED; break; + default: type = SAPP_EVENTTYPE_INVALID; break; + } + if (_sapp_events_enabled() && (SAPP_EVENTTYPE_INVALID != type)) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } + return true; +} + +_SOKOL_PRIVATE void _sapp_emsc_webgl_init(void) { + EmscriptenWebGLContextAttributes attrs; + emscripten_webgl_init_context_attributes(&attrs); + attrs.alpha = _sapp.desc.alpha; + attrs.depth = true; + attrs.stencil = true; + attrs.antialias = _sapp.sample_count > 1; + attrs.premultipliedAlpha = _sapp.desc.html5_premultiplied_alpha; + attrs.preserveDrawingBuffer = _sapp.desc.html5_preserve_drawing_buffer; + attrs.enableExtensionsByDefault = true; + attrs.majorVersion = 2; + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(_sapp.html5_canvas_selector, &attrs); + // FIXME: error message? + emscripten_webgl_make_context_current(ctx); + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer); + + // FIXME: remove PVRTC support here and in sokol-gfx at some point + // some WebGL extension are not enabled automatically by emscripten + emscripten_webgl_enable_extension(ctx, "WEBKIT_WEBGL_compressed_texture_pvrtc"); +} +#endif + +#if defined(SOKOL_WGPU) + +_SOKOL_PRIVATE void _sapp_emsc_wgpu_create_swapchain(void) { + SOKOL_ASSERT(_sapp.wgpu.instance); + SOKOL_ASSERT(_sapp.wgpu.device); + SOKOL_ASSERT(0 == _sapp.wgpu.surface); + SOKOL_ASSERT(0 == _sapp.wgpu.swapchain); + SOKOL_ASSERT(0 == _sapp.wgpu.msaa_tex); + SOKOL_ASSERT(0 == _sapp.wgpu.msaa_view); + SOKOL_ASSERT(0 == _sapp.wgpu.depth_stencil_tex); + SOKOL_ASSERT(0 == _sapp.wgpu.depth_stencil_view); + SOKOL_ASSERT(0 == _sapp.wgpu.swapchain_view); + + WGPUSurfaceDescriptorFromCanvasHTMLSelector canvas_desc; + _sapp_clear(&canvas_desc, sizeof(canvas_desc)); + canvas_desc.chain.sType = WGPUSType_SurfaceDescriptorFromCanvasHTMLSelector; + canvas_desc.selector = _sapp.html5_canvas_selector; + WGPUSurfaceDescriptor surf_desc; + _sapp_clear(&surf_desc, sizeof(surf_desc)); + surf_desc.nextInChain = &canvas_desc.chain; + _sapp.wgpu.surface = wgpuInstanceCreateSurface(_sapp.wgpu.instance, &surf_desc); + if (0 == _sapp.wgpu.surface) { + _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_SURFACE_FAILED); + } + _sapp.wgpu.render_format = wgpuSurfaceGetPreferredFormat(_sapp.wgpu.surface, _sapp.wgpu.adapter); + + WGPUSwapChainDescriptor sc_desc; + _sapp_clear(&sc_desc, sizeof(sc_desc)); + sc_desc.usage = WGPUTextureUsage_RenderAttachment; + sc_desc.format = _sapp.wgpu.render_format; + sc_desc.width = (uint32_t)_sapp.framebuffer_width; + sc_desc.height = (uint32_t)_sapp.framebuffer_height; + sc_desc.presentMode = WGPUPresentMode_Fifo; + _sapp.wgpu.swapchain = wgpuDeviceCreateSwapChain(_sapp.wgpu.device, _sapp.wgpu.surface, &sc_desc); + if (0 == _sapp.wgpu.swapchain) { + _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_SWAPCHAIN_FAILED); + } + + WGPUTextureDescriptor ds_desc; + _sapp_clear(&ds_desc, sizeof(ds_desc)); + ds_desc.usage = WGPUTextureUsage_RenderAttachment; + ds_desc.dimension = WGPUTextureDimension_2D; + ds_desc.size.width = (uint32_t)_sapp.framebuffer_width; + ds_desc.size.height = (uint32_t)_sapp.framebuffer_height; + ds_desc.size.depthOrArrayLayers = 1; + ds_desc.format = WGPUTextureFormat_Depth32FloatStencil8; + ds_desc.mipLevelCount = 1; + ds_desc.sampleCount = (uint32_t)_sapp.sample_count; + _sapp.wgpu.depth_stencil_tex = wgpuDeviceCreateTexture(_sapp.wgpu.device, &ds_desc); + if (0 == _sapp.wgpu.depth_stencil_tex) { + _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_TEXTURE_FAILED); + } + _sapp.wgpu.depth_stencil_view = wgpuTextureCreateView(_sapp.wgpu.depth_stencil_tex, 0); + if (0 == _sapp.wgpu.depth_stencil_view) { + _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_VIEW_FAILED); + } + + if (_sapp.sample_count > 1) { + WGPUTextureDescriptor msaa_desc; + _sapp_clear(&msaa_desc, sizeof(msaa_desc)); + msaa_desc.usage = WGPUTextureUsage_RenderAttachment; + msaa_desc.dimension = WGPUTextureDimension_2D; + msaa_desc.size.width = (uint32_t)_sapp.framebuffer_width; + msaa_desc.size.height = (uint32_t)_sapp.framebuffer_height; + msaa_desc.size.depthOrArrayLayers = 1; + msaa_desc.format = _sapp.wgpu.render_format; + msaa_desc.mipLevelCount = 1; + msaa_desc.sampleCount = (uint32_t)_sapp.sample_count; + _sapp.wgpu.msaa_tex = wgpuDeviceCreateTexture(_sapp.wgpu.device, &msaa_desc); + if (0 == _sapp.wgpu.msaa_tex) { + _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_MSAA_TEXTURE_FAILED); + } + _sapp.wgpu.msaa_view = wgpuTextureCreateView(_sapp.wgpu.msaa_tex, 0); + if (0 == _sapp.wgpu.msaa_view) { + _SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_MSAA_VIEW_FAILED); + } + } +} + +_SOKOL_PRIVATE void _sapp_emsc_wgpu_discard_swapchain(void) { + if (_sapp.wgpu.msaa_view) { + wgpuTextureViewRelease(_sapp.wgpu.msaa_view); + _sapp.wgpu.msaa_view = 0; + } + if (_sapp.wgpu.msaa_tex) { + wgpuTextureRelease(_sapp.wgpu.msaa_tex); + _sapp.wgpu.msaa_tex = 0; + } + if (_sapp.wgpu.depth_stencil_view) { + wgpuTextureViewRelease(_sapp.wgpu.depth_stencil_view); + _sapp.wgpu.depth_stencil_view = 0; + } + if (_sapp.wgpu.depth_stencil_tex) { + wgpuTextureRelease(_sapp.wgpu.depth_stencil_tex); + _sapp.wgpu.depth_stencil_tex = 0; + } + if (_sapp.wgpu.swapchain) { + wgpuSwapChainRelease(_sapp.wgpu.swapchain); + _sapp.wgpu.swapchain = 0; + } + if (_sapp.wgpu.surface) { + wgpuSurfaceRelease(_sapp.wgpu.surface); + _sapp.wgpu.surface = 0; + } +} + +_SOKOL_PRIVATE void _sapp_emsc_wgpu_size_changed(void) { + _sapp_emsc_wgpu_discard_swapchain(); + _sapp_emsc_wgpu_create_swapchain(); +} + +_SOKOL_PRIVATE void _sapp_emsc_wgpu_request_device_cb(WGPURequestDeviceStatus status, WGPUDevice device, const char* msg, void* userdata) { + _SOKOL_UNUSED(msg); + _SOKOL_UNUSED(userdata); + SOKOL_ASSERT(!_sapp.wgpu.async_init_done); + if (status != WGPURequestDeviceStatus_Success) { + if (status == WGPURequestDeviceStatus_Error) { + _SAPP_PANIC(WGPU_REQUEST_DEVICE_STATUS_ERROR); + } else { + _SAPP_PANIC(WGPU_REQUEST_DEVICE_STATUS_UNKNOWN); + } + } + SOKOL_ASSERT(device); + _sapp.wgpu.device = device; + _sapp_emsc_wgpu_create_swapchain(); + _sapp.wgpu.async_init_done = true; +} + +_SOKOL_PRIVATE void _sapp_emsc_wgpu_request_adapter_cb(WGPURequestAdapterStatus status, WGPUAdapter adapter, const char* msg, void* userdata) { + _SOKOL_UNUSED(msg); + _SOKOL_UNUSED(userdata); + if (status != WGPURequestAdapterStatus_Success) { + switch (status) { + case WGPURequestAdapterStatus_Unavailable: _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_UNAVAILABLE); break; + case WGPURequestAdapterStatus_Error: _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_ERROR); break; + default: _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_UNKNOWN); break; + } + } + SOKOL_ASSERT(adapter); + _sapp.wgpu.adapter = adapter; + size_t cur_feature_index = 1; + #define _SAPP_WGPU_MAX_REQUESTED_FEATURES (8) + WGPUFeatureName requiredFeatures[_SAPP_WGPU_MAX_REQUESTED_FEATURES] = { + WGPUFeatureName_Depth32FloatStencil8, + }; + // check for optional features we're interested in + if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_TextureCompressionBC)) { + SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES); + requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionBC; + } + if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_TextureCompressionETC2)) { + SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES); + requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionETC2; + } + if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_TextureCompressionASTC)) { + SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES); + requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionASTC; + } + if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_Float32Filterable)) { + SOKOL_ASSERT(cur_feature_index < _SAPP_WGPU_MAX_REQUESTED_FEATURES); + requiredFeatures[cur_feature_index++] = WGPUFeatureName_Float32Filterable; + } + #undef _SAPP_WGPU_MAX_REQUESTED_FEATURES + + WGPUDeviceDescriptor dev_desc; + _sapp_clear(&dev_desc, sizeof(dev_desc)); + dev_desc.requiredFeatureCount = cur_feature_index; + dev_desc.requiredFeatures = requiredFeatures, + wgpuAdapterRequestDevice(adapter, &dev_desc, _sapp_emsc_wgpu_request_device_cb, 0); +} + +_SOKOL_PRIVATE void _sapp_emsc_wgpu_init(void) { + SOKOL_ASSERT(0 == _sapp.wgpu.instance); + SOKOL_ASSERT(!_sapp.wgpu.async_init_done); + _sapp.wgpu.instance = wgpuCreateInstance(0); + if (0 == _sapp.wgpu.instance) { + _SAPP_PANIC(WGPU_CREATE_INSTANCE_FAILED); + } + // FIXME: power preference? + wgpuInstanceRequestAdapter(_sapp.wgpu.instance, 0, _sapp_emsc_wgpu_request_adapter_cb, 0); +} + +_SOKOL_PRIVATE void _sapp_emsc_wgpu_frame(void) { + if (_sapp.wgpu.async_init_done) { + _sapp.wgpu.swapchain_view = wgpuSwapChainGetCurrentTextureView(_sapp.wgpu.swapchain); + _sapp_frame(); + wgpuTextureViewRelease(_sapp.wgpu.swapchain_view); + _sapp.wgpu.swapchain_view = 0; + } +} +#endif // SOKOL_WGPU + +_SOKOL_PRIVATE void _sapp_emsc_register_eventhandlers(void) { + // NOTE: HTML canvas doesn't receive input focus, this is why key event handlers are added + // to the window object (this could be worked around by adding a "tab index" to the + // canvas) + emscripten_set_mousedown_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_mouseup_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_mousemove_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_mouseenter_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_mouseleave_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_wheel_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_wheel_cb); + emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); + emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); + emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); + emscripten_set_touchstart_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); + emscripten_set_touchmove_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); + emscripten_set_touchend_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); + emscripten_set_touchcancel_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); + emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockchange_cb); + emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockerror_cb); + emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_focus_cb); + emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_blur_cb); + sapp_js_add_beforeunload_listener(); + if (_sapp.clipboard.enabled) { + sapp_js_add_clipboard_listener(); + } + if (_sapp.drop.enabled) { + sapp_js_add_dragndrop_listeners(&_sapp.html5_canvas_selector[1]); + } + #if defined(SOKOL_GLES3) + emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb); + emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb); + #endif +} + +_SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers(void) { + emscripten_set_mousedown_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_mouseup_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_mousemove_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_mouseenter_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_mouseleave_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_wheel_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + emscripten_set_touchstart_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_touchmove_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_touchend_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_touchcancel_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0); + emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0); + emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + if (!_sapp.desc.html5_canvas_resize) { + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + } + sapp_js_remove_beforeunload_listener(); + if (_sapp.clipboard.enabled) { + sapp_js_remove_clipboard_listener(); + } + if (_sapp.drop.enabled) { + sapp_js_remove_dragndrop_listeners(&_sapp.html5_canvas_selector[1]); + } + #if defined(SOKOL_GLES3) + emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, 0); + #endif +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame_animation_loop(double time, void* userData) { + _SOKOL_UNUSED(userData); + _sapp_timing_external(&_sapp.timing, time / 1000.0); + + #if defined(SOKOL_WGPU) + _sapp_emsc_wgpu_frame(); + #else + _sapp_frame(); + #endif + + // quit-handling + if (_sapp.quit_requested) { + _sapp_init_event(SAPP_EVENTTYPE_QUIT_REQUESTED); + _sapp_call_event(&_sapp.event); + if (_sapp.quit_requested) { + _sapp.quit_ordered = true; + } + } + if (_sapp.quit_ordered) { + _sapp_emsc_unregister_eventhandlers(); + _sapp_call_cleanup(); + _sapp_discard_state(); + return EM_FALSE; + } + return EM_TRUE; +} + +_SOKOL_PRIVATE void _sapp_emsc_frame_main_loop(void) { + const double time = emscripten_performance_now(); + if (!_sapp_emsc_frame_animation_loop(time, 0)) { + emscripten_cancel_main_loop(); + } +} + +_SOKOL_PRIVATE void _sapp_emsc_run(const sapp_desc* desc) { + _sapp_init_state(desc); + sapp_js_init(&_sapp.html5_canvas_selector[1]); + double w, h; + if (_sapp.desc.html5_canvas_resize) { + w = (double) _sapp_def(_sapp.desc.width, _SAPP_FALLBACK_DEFAULT_WINDOW_WIDTH); + h = (double) _sapp_def(_sapp.desc.height, _SAPP_FALLBACK_DEFAULT_WINDOW_HEIGHT); + } + else { + emscripten_get_element_css_size(_sapp.html5_canvas_selector, &w, &h); + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, false, _sapp_emsc_size_changed); + } + if (_sapp.desc.high_dpi) { + _sapp.dpi_scale = emscripten_get_device_pixel_ratio(); + } + _sapp.window_width = (int)roundf(w); + _sapp.window_height = (int)roundf(h); + _sapp.framebuffer_width = (int)roundf(w * _sapp.dpi_scale); + _sapp.framebuffer_height = (int)roundf(h * _sapp.dpi_scale); + emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height); + #if defined(SOKOL_GLES3) + _sapp_emsc_webgl_init(); + #elif defined(SOKOL_WGPU) + _sapp_emsc_wgpu_init(); + #endif + _sapp.valid = true; + _sapp_emsc_register_eventhandlers(); + sapp_set_icon(&desc->icon); + + // start the frame loop + if (_sapp.desc.html5_use_emsc_set_main_loop) { + emscripten_set_main_loop(_sapp_emsc_frame_main_loop, 0, _sapp.desc.html5_emsc_set_main_loop_simulate_infinite_loop); + } else { + emscripten_request_animation_frame_loop(_sapp_emsc_frame_animation_loop, 0); + } + // NOT A BUG: do not call _sapp_discard_state() here, instead this is + // called in _sapp_emsc_frame() when the application is ordered to quit +} + +#if !defined(SOKOL_NO_ENTRY) +int main(int argc, char* argv[]) { + sapp_desc desc = sokol_main(argc, argv); + _sapp_emsc_run(&desc); + return 0; +} +#endif /* SOKOL_NO_ENTRY */ +#endif /* _SAPP_EMSCRIPTEN */ + +// ██████ ██ ██ ██ ███████ ██ ██████ ███████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ███ ██ ███████ █████ ██ ██████ █████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ███████ ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ +// +// >>gl helpers +#if defined(SOKOL_GLCORE) +typedef struct { + int red_bits; + int green_bits; + int blue_bits; + int alpha_bits; + int depth_bits; + int stencil_bits; + int samples; + bool doublebuffer; + uintptr_t handle; +} _sapp_gl_fbconfig; + +_SOKOL_PRIVATE void _sapp_gl_init_fbconfig(_sapp_gl_fbconfig* fbconfig) { + _sapp_clear(fbconfig, sizeof(_sapp_gl_fbconfig)); + /* -1 means "don't care" */ + fbconfig->red_bits = -1; + fbconfig->green_bits = -1; + fbconfig->blue_bits = -1; + fbconfig->alpha_bits = -1; + fbconfig->depth_bits = -1; + fbconfig->stencil_bits = -1; + fbconfig->samples = -1; +} + +typedef struct { + int least_missing; + int least_color_diff; + int least_extra_diff; + bool best_match; +} _sapp_gl_fbselect; + +_SOKOL_PRIVATE void _sapp_gl_init_fbselect(_sapp_gl_fbselect* fbselect) { + _sapp_clear(fbselect, sizeof(_sapp_gl_fbselect)); + fbselect->least_missing = 1000000; + fbselect->least_color_diff = 10000000; + fbselect->least_extra_diff = 10000000; + fbselect->best_match = false; +} + +// NOTE: this is used only in the WGL code path +_SOKOL_PRIVATE bool _sapp_gl_select_fbconfig(_sapp_gl_fbselect* fbselect, const _sapp_gl_fbconfig* desired, const _sapp_gl_fbconfig* current) { + int missing = 0; + if (desired->doublebuffer != current->doublebuffer) { + return false; + } + + if ((desired->alpha_bits > 0) && (current->alpha_bits == 0)) { + missing++; + } + if ((desired->depth_bits > 0) && (current->depth_bits == 0)) { + missing++; + } + if ((desired->stencil_bits > 0) && (current->stencil_bits == 0)) { + missing++; + } + if ((desired->samples > 0) && (current->samples == 0)) { + /* Technically, several multisampling buffers could be + involved, but that's a lower level implementation detail and + not important to us here, so we count them as one + */ + missing++; + } + + /* These polynomials make many small channel size differences matter + less than one large channel size difference + Calculate color channel size difference value + */ + int color_diff = 0; + if (desired->red_bits != -1) { + color_diff += (desired->red_bits - current->red_bits) * (desired->red_bits - current->red_bits); + } + if (desired->green_bits != -1) { + color_diff += (desired->green_bits - current->green_bits) * (desired->green_bits - current->green_bits); + } + if (desired->blue_bits != -1) { + color_diff += (desired->blue_bits - current->blue_bits) * (desired->blue_bits - current->blue_bits); + } + + /* Calculate non-color channel size difference value */ + int extra_diff = 0; + if (desired->alpha_bits != -1) { + extra_diff += (desired->alpha_bits - current->alpha_bits) * (desired->alpha_bits - current->alpha_bits); + } + if (desired->depth_bits != -1) { + extra_diff += (desired->depth_bits - current->depth_bits) * (desired->depth_bits - current->depth_bits); + } + if (desired->stencil_bits != -1) { + extra_diff += (desired->stencil_bits - current->stencil_bits) * (desired->stencil_bits - current->stencil_bits); + } + if (desired->samples != -1) { + extra_diff += (desired->samples - current->samples) * (desired->samples - current->samples); + } + + /* Figure out if the current one is better than the best one found so far + Least number of missing buffers is the most important heuristic, + then color buffer size match and lastly size match for other buffers + */ + bool new_closest = false; + if (missing < fbselect->least_missing) { + new_closest = true; + } else if (missing == fbselect->least_missing) { + if ((color_diff < fbselect->least_color_diff) || + ((color_diff == fbselect->least_color_diff) && (extra_diff < fbselect->least_extra_diff))) + { + new_closest = true; + } + } + if (new_closest) { + fbselect->least_missing = missing; + fbselect->least_color_diff = color_diff; + fbselect->least_extra_diff = extra_diff; + fbselect->best_match = (missing | color_diff | extra_diff) == 0; + } + return new_closest; +} + +// NOTE: this is used only in the GLX code path +_SOKOL_PRIVATE const _sapp_gl_fbconfig* _sapp_gl_choose_fbconfig(const _sapp_gl_fbconfig* desired, const _sapp_gl_fbconfig* alternatives, int count) { + int missing, least_missing = 1000000; + int color_diff, least_color_diff = 10000000; + int extra_diff, least_extra_diff = 10000000; + const _sapp_gl_fbconfig* current; + const _sapp_gl_fbconfig* closest = 0; + for (int i = 0; i < count; i++) { + current = alternatives + i; + if (desired->doublebuffer != current->doublebuffer) { + continue; + } + missing = 0; + if (desired->alpha_bits > 0 && current->alpha_bits == 0) { + missing++; + } + if (desired->depth_bits > 0 && current->depth_bits == 0) { + missing++; + } + if (desired->stencil_bits > 0 && current->stencil_bits == 0) { + missing++; + } + if (desired->samples > 0 && current->samples == 0) { + /* Technically, several multisampling buffers could be + involved, but that's a lower level implementation detail and + not important to us here, so we count them as one + */ + missing++; + } + + /* These polynomials make many small channel size differences matter + less than one large channel size difference + Calculate color channel size difference value + */ + color_diff = 0; + if (desired->red_bits != -1) { + color_diff += (desired->red_bits - current->red_bits) * (desired->red_bits - current->red_bits); + } + if (desired->green_bits != -1) { + color_diff += (desired->green_bits - current->green_bits) * (desired->green_bits - current->green_bits); + } + if (desired->blue_bits != -1) { + color_diff += (desired->blue_bits - current->blue_bits) * (desired->blue_bits - current->blue_bits); + } + + /* Calculate non-color channel size difference value */ + extra_diff = 0; + if (desired->alpha_bits != -1) { + extra_diff += (desired->alpha_bits - current->alpha_bits) * (desired->alpha_bits - current->alpha_bits); + } + if (desired->depth_bits != -1) { + extra_diff += (desired->depth_bits - current->depth_bits) * (desired->depth_bits - current->depth_bits); + } + if (desired->stencil_bits != -1) { + extra_diff += (desired->stencil_bits - current->stencil_bits) * (desired->stencil_bits - current->stencil_bits); + } + if (desired->samples != -1) { + extra_diff += (desired->samples - current->samples) * (desired->samples - current->samples); + } + + /* Figure out if the current one is better than the best one found so far + Least number of missing buffers is the most important heuristic, + then color buffer size match and lastly size match for other buffers + */ + if (missing < least_missing) { + closest = current; + } + else if (missing == least_missing) { + if ((color_diff < least_color_diff) || + (color_diff == least_color_diff && extra_diff < least_extra_diff)) + { + closest = current; + } + } + if (current == closest) { + least_missing = missing; + least_color_diff = color_diff; + least_extra_diff = extra_diff; + } + } + return closest; +} +#endif + +// ██ ██ ██ ███ ██ ██████ ██████ ██ ██ ███████ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ █ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ █ ██ ███████ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██ ██ +// ███ ███ ██ ██ ████ ██████ ██████ ███ ███ ███████ +// +// >>windows +#if defined(_SAPP_WIN32) +_SOKOL_PRIVATE bool _sapp_win32_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) { + SOKOL_ASSERT(src && dst && (dst_num_bytes > 1)); + _sapp_clear(dst, (size_t)dst_num_bytes); + const int dst_chars = dst_num_bytes / (int)sizeof(wchar_t); + const int dst_needed = MultiByteToWideChar(CP_UTF8, 0, src, -1, 0, 0); + if ((dst_needed > 0) && (dst_needed < dst_chars)) { + MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, dst_chars); + return true; + } + else { + /* input string doesn't fit into destination buffer */ + return false; + } +} + +_SOKOL_PRIVATE void _sapp_win32_app_event(sapp_event_type type) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_win32_init_keytable(void) { + /* same as GLFW */ + _sapp.keycodes[0x00B] = SAPP_KEYCODE_0; + _sapp.keycodes[0x002] = SAPP_KEYCODE_1; + _sapp.keycodes[0x003] = SAPP_KEYCODE_2; + _sapp.keycodes[0x004] = SAPP_KEYCODE_3; + _sapp.keycodes[0x005] = SAPP_KEYCODE_4; + _sapp.keycodes[0x006] = SAPP_KEYCODE_5; + _sapp.keycodes[0x007] = SAPP_KEYCODE_6; + _sapp.keycodes[0x008] = SAPP_KEYCODE_7; + _sapp.keycodes[0x009] = SAPP_KEYCODE_8; + _sapp.keycodes[0x00A] = SAPP_KEYCODE_9; + _sapp.keycodes[0x01E] = SAPP_KEYCODE_A; + _sapp.keycodes[0x030] = SAPP_KEYCODE_B; + _sapp.keycodes[0x02E] = SAPP_KEYCODE_C; + _sapp.keycodes[0x020] = SAPP_KEYCODE_D; + _sapp.keycodes[0x012] = SAPP_KEYCODE_E; + _sapp.keycodes[0x021] = SAPP_KEYCODE_F; + _sapp.keycodes[0x022] = SAPP_KEYCODE_G; + _sapp.keycodes[0x023] = SAPP_KEYCODE_H; + _sapp.keycodes[0x017] = SAPP_KEYCODE_I; + _sapp.keycodes[0x024] = SAPP_KEYCODE_J; + _sapp.keycodes[0x025] = SAPP_KEYCODE_K; + _sapp.keycodes[0x026] = SAPP_KEYCODE_L; + _sapp.keycodes[0x032] = SAPP_KEYCODE_M; + _sapp.keycodes[0x031] = SAPP_KEYCODE_N; + _sapp.keycodes[0x018] = SAPP_KEYCODE_O; + _sapp.keycodes[0x019] = SAPP_KEYCODE_P; + _sapp.keycodes[0x010] = SAPP_KEYCODE_Q; + _sapp.keycodes[0x013] = SAPP_KEYCODE_R; + _sapp.keycodes[0x01F] = SAPP_KEYCODE_S; + _sapp.keycodes[0x014] = SAPP_KEYCODE_T; + _sapp.keycodes[0x016] = SAPP_KEYCODE_U; + _sapp.keycodes[0x02F] = SAPP_KEYCODE_V; + _sapp.keycodes[0x011] = SAPP_KEYCODE_W; + _sapp.keycodes[0x02D] = SAPP_KEYCODE_X; + _sapp.keycodes[0x015] = SAPP_KEYCODE_Y; + _sapp.keycodes[0x02C] = SAPP_KEYCODE_Z; + _sapp.keycodes[0x028] = SAPP_KEYCODE_APOSTROPHE; + _sapp.keycodes[0x02B] = SAPP_KEYCODE_BACKSLASH; + _sapp.keycodes[0x033] = SAPP_KEYCODE_COMMA; + _sapp.keycodes[0x00D] = SAPP_KEYCODE_EQUAL; + _sapp.keycodes[0x029] = SAPP_KEYCODE_GRAVE_ACCENT; + _sapp.keycodes[0x01A] = SAPP_KEYCODE_LEFT_BRACKET; + _sapp.keycodes[0x00C] = SAPP_KEYCODE_MINUS; + _sapp.keycodes[0x034] = SAPP_KEYCODE_PERIOD; + _sapp.keycodes[0x01B] = SAPP_KEYCODE_RIGHT_BRACKET; + _sapp.keycodes[0x027] = SAPP_KEYCODE_SEMICOLON; + _sapp.keycodes[0x035] = SAPP_KEYCODE_SLASH; + _sapp.keycodes[0x056] = SAPP_KEYCODE_WORLD_2; + _sapp.keycodes[0x00E] = SAPP_KEYCODE_BACKSPACE; + _sapp.keycodes[0x153] = SAPP_KEYCODE_DELETE; + _sapp.keycodes[0x14F] = SAPP_KEYCODE_END; + _sapp.keycodes[0x01C] = SAPP_KEYCODE_ENTER; + _sapp.keycodes[0x001] = SAPP_KEYCODE_ESCAPE; + _sapp.keycodes[0x147] = SAPP_KEYCODE_HOME; + _sapp.keycodes[0x152] = SAPP_KEYCODE_INSERT; + _sapp.keycodes[0x15D] = SAPP_KEYCODE_MENU; + _sapp.keycodes[0x151] = SAPP_KEYCODE_PAGE_DOWN; + _sapp.keycodes[0x149] = SAPP_KEYCODE_PAGE_UP; + _sapp.keycodes[0x045] = SAPP_KEYCODE_PAUSE; + _sapp.keycodes[0x146] = SAPP_KEYCODE_PAUSE; + _sapp.keycodes[0x039] = SAPP_KEYCODE_SPACE; + _sapp.keycodes[0x00F] = SAPP_KEYCODE_TAB; + _sapp.keycodes[0x03A] = SAPP_KEYCODE_CAPS_LOCK; + _sapp.keycodes[0x145] = SAPP_KEYCODE_NUM_LOCK; + _sapp.keycodes[0x046] = SAPP_KEYCODE_SCROLL_LOCK; + _sapp.keycodes[0x03B] = SAPP_KEYCODE_F1; + _sapp.keycodes[0x03C] = SAPP_KEYCODE_F2; + _sapp.keycodes[0x03D] = SAPP_KEYCODE_F3; + _sapp.keycodes[0x03E] = SAPP_KEYCODE_F4; + _sapp.keycodes[0x03F] = SAPP_KEYCODE_F5; + _sapp.keycodes[0x040] = SAPP_KEYCODE_F6; + _sapp.keycodes[0x041] = SAPP_KEYCODE_F7; + _sapp.keycodes[0x042] = SAPP_KEYCODE_F8; + _sapp.keycodes[0x043] = SAPP_KEYCODE_F9; + _sapp.keycodes[0x044] = SAPP_KEYCODE_F10; + _sapp.keycodes[0x057] = SAPP_KEYCODE_F11; + _sapp.keycodes[0x058] = SAPP_KEYCODE_F12; + _sapp.keycodes[0x064] = SAPP_KEYCODE_F13; + _sapp.keycodes[0x065] = SAPP_KEYCODE_F14; + _sapp.keycodes[0x066] = SAPP_KEYCODE_F15; + _sapp.keycodes[0x067] = SAPP_KEYCODE_F16; + _sapp.keycodes[0x068] = SAPP_KEYCODE_F17; + _sapp.keycodes[0x069] = SAPP_KEYCODE_F18; + _sapp.keycodes[0x06A] = SAPP_KEYCODE_F19; + _sapp.keycodes[0x06B] = SAPP_KEYCODE_F20; + _sapp.keycodes[0x06C] = SAPP_KEYCODE_F21; + _sapp.keycodes[0x06D] = SAPP_KEYCODE_F22; + _sapp.keycodes[0x06E] = SAPP_KEYCODE_F23; + _sapp.keycodes[0x076] = SAPP_KEYCODE_F24; + _sapp.keycodes[0x038] = SAPP_KEYCODE_LEFT_ALT; + _sapp.keycodes[0x01D] = SAPP_KEYCODE_LEFT_CONTROL; + _sapp.keycodes[0x02A] = SAPP_KEYCODE_LEFT_SHIFT; + _sapp.keycodes[0x15B] = SAPP_KEYCODE_LEFT_SUPER; + _sapp.keycodes[0x137] = SAPP_KEYCODE_PRINT_SCREEN; + _sapp.keycodes[0x138] = SAPP_KEYCODE_RIGHT_ALT; + _sapp.keycodes[0x11D] = SAPP_KEYCODE_RIGHT_CONTROL; + _sapp.keycodes[0x036] = SAPP_KEYCODE_RIGHT_SHIFT; + _sapp.keycodes[0x15C] = SAPP_KEYCODE_RIGHT_SUPER; + _sapp.keycodes[0x150] = SAPP_KEYCODE_DOWN; + _sapp.keycodes[0x14B] = SAPP_KEYCODE_LEFT; + _sapp.keycodes[0x14D] = SAPP_KEYCODE_RIGHT; + _sapp.keycodes[0x148] = SAPP_KEYCODE_UP; + _sapp.keycodes[0x052] = SAPP_KEYCODE_KP_0; + _sapp.keycodes[0x04F] = SAPP_KEYCODE_KP_1; + _sapp.keycodes[0x050] = SAPP_KEYCODE_KP_2; + _sapp.keycodes[0x051] = SAPP_KEYCODE_KP_3; + _sapp.keycodes[0x04B] = SAPP_KEYCODE_KP_4; + _sapp.keycodes[0x04C] = SAPP_KEYCODE_KP_5; + _sapp.keycodes[0x04D] = SAPP_KEYCODE_KP_6; + _sapp.keycodes[0x047] = SAPP_KEYCODE_KP_7; + _sapp.keycodes[0x048] = SAPP_KEYCODE_KP_8; + _sapp.keycodes[0x049] = SAPP_KEYCODE_KP_9; + _sapp.keycodes[0x04E] = SAPP_KEYCODE_KP_ADD; + _sapp.keycodes[0x053] = SAPP_KEYCODE_KP_DECIMAL; + _sapp.keycodes[0x135] = SAPP_KEYCODE_KP_DIVIDE; + _sapp.keycodes[0x11C] = SAPP_KEYCODE_KP_ENTER; + _sapp.keycodes[0x037] = SAPP_KEYCODE_KP_MULTIPLY; + _sapp.keycodes[0x04A] = SAPP_KEYCODE_KP_SUBTRACT; +} +#endif // _SAPP_WIN32 + +#if defined(_SAPP_WIN32) + +#if defined(SOKOL_D3D11) + +#if defined(__cplusplus) +#define _sapp_d3d11_Release(self) (self)->Release() +#define _sapp_win32_refiid(iid) iid +#else +#define _sapp_d3d11_Release(self) (self)->lpVtbl->Release(self) +#define _sapp_win32_refiid(iid) &iid +#endif + +#define _SAPP_SAFE_RELEASE(obj) if (obj) { _sapp_d3d11_Release(obj); obj=0; } + + +static const IID _sapp_IID_ID3D11Texture2D = { 0x6f15aaf2,0xd208,0x4e89, {0x9a,0xb4,0x48,0x95,0x35,0xd3,0x4f,0x9c} }; +static const IID _sapp_IID_IDXGIDevice1 = { 0x77db970f,0x6276,0x48ba, {0xba,0x28,0x07,0x01,0x43,0xb4,0x39,0x2c} }; +static const IID _sapp_IID_IDXGIFactory = { 0x7b7166ec,0x21c7,0x44ae, {0xb2,0x1a,0xc9,0xae,0x32,0x1a,0xe3,0x69} }; + +static inline HRESULT _sapp_dxgi_GetBuffer(IDXGISwapChain* self, UINT Buffer, REFIID riid, void** ppSurface) { + #if defined(__cplusplus) + return self->GetBuffer(Buffer, riid, ppSurface); + #else + return self->lpVtbl->GetBuffer(self, Buffer, riid, ppSurface); + #endif +} + +static inline HRESULT _sapp_d3d11_QueryInterface(ID3D11Device* self, REFIID riid, void** ppvObject) { + #if defined(__cplusplus) + return self->QueryInterface(riid, ppvObject); + #else + return self->lpVtbl->QueryInterface(self, riid, ppvObject); + #endif +} + +static inline HRESULT _sapp_d3d11_CreateRenderTargetView(ID3D11Device* self, ID3D11Resource *pResource, const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, ID3D11RenderTargetView** ppRTView) { + #if defined(__cplusplus) + return self->CreateRenderTargetView(pResource, pDesc, ppRTView); + #else + return self->lpVtbl->CreateRenderTargetView(self, pResource, pDesc, ppRTView); + #endif +} + +static inline HRESULT _sapp_d3d11_CreateTexture2D(ID3D11Device* self, const D3D11_TEXTURE2D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D** ppTexture2D) { + #if defined(__cplusplus) + return self->CreateTexture2D(pDesc, pInitialData, ppTexture2D); + #else + return self->lpVtbl->CreateTexture2D(self, pDesc, pInitialData, ppTexture2D); + #endif +} + +static inline HRESULT _sapp_d3d11_CreateDepthStencilView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D11DepthStencilView** ppDepthStencilView) { + #if defined(__cplusplus) + return self->CreateDepthStencilView(pResource, pDesc, ppDepthStencilView); + #else + return self->lpVtbl->CreateDepthStencilView(self, pResource, pDesc, ppDepthStencilView); + #endif +} + +static inline HRESULT _sapp_dxgi_ResizeBuffers(IDXGISwapChain* self, UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags) { + #if defined(__cplusplus) + return self->ResizeBuffers(BufferCount, Width, Height, NewFormat, SwapChainFlags); + #else + return self->lpVtbl->ResizeBuffers(self, BufferCount, Width, Height, NewFormat, SwapChainFlags); + #endif +} + +static inline HRESULT _sapp_dxgi_Present(IDXGISwapChain* self, UINT SyncInterval, UINT Flags) { + #if defined(__cplusplus) + return self->Present(SyncInterval, Flags); + #else + return self->lpVtbl->Present(self, SyncInterval, Flags); + #endif +} + +static inline HRESULT _sapp_dxgi_GetFrameStatistics(IDXGISwapChain* self, DXGI_FRAME_STATISTICS* pStats) { + #if defined(__cplusplus) + return self->GetFrameStatistics(pStats); + #else + return self->lpVtbl->GetFrameStatistics(self, pStats); + #endif +} + +static inline HRESULT _sapp_dxgi_SetMaximumFrameLatency(IDXGIDevice1* self, UINT MaxLatency) { + #if defined(__cplusplus) + return self->SetMaximumFrameLatency(MaxLatency); + #else + return self->lpVtbl->SetMaximumFrameLatency(self, MaxLatency); + #endif +} + +static inline HRESULT _sapp_dxgi_GetAdapter(IDXGIDevice1* self, IDXGIAdapter** pAdapter) { + #if defined(__cplusplus) + return self->GetAdapter(pAdapter); + #else + return self->lpVtbl->GetAdapter(self, pAdapter); + #endif +} + +static inline HRESULT _sapp_dxgi_GetParent(IDXGIObject* self, REFIID riid, void** ppParent) { + #if defined(__cplusplus) + return self->GetParent(riid, ppParent); + #else + return self->lpVtbl->GetParent(self, riid, ppParent); + #endif +} + +static inline HRESULT _sapp_dxgi_MakeWindowAssociation(IDXGIFactory* self, HWND WindowHandle, UINT Flags) { + #if defined(__cplusplus) + return self->MakeWindowAssociation(WindowHandle, Flags); + #else + return self->lpVtbl->MakeWindowAssociation(self, WindowHandle, Flags); + #endif +} + +_SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { + DXGI_SWAP_CHAIN_DESC* sc_desc = &_sapp.d3d11.swap_chain_desc; + sc_desc->BufferDesc.Width = (UINT)_sapp.framebuffer_width; + sc_desc->BufferDesc.Height = (UINT)_sapp.framebuffer_height; + sc_desc->BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + sc_desc->BufferDesc.RefreshRate.Numerator = 60; + sc_desc->BufferDesc.RefreshRate.Denominator = 1; + sc_desc->OutputWindow = _sapp.win32.hwnd; + sc_desc->Windowed = true; + if (_sapp.win32.is_win10_or_greater) { + sc_desc->BufferCount = 2; + sc_desc->SwapEffect = (DXGI_SWAP_EFFECT) _SAPP_DXGI_SWAP_EFFECT_FLIP_DISCARD; + _sapp.d3d11.use_dxgi_frame_stats = true; + } + else { + sc_desc->BufferCount = 1; + sc_desc->SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + _sapp.d3d11.use_dxgi_frame_stats = false; + } + sc_desc->SampleDesc.Count = 1; + sc_desc->SampleDesc.Quality = 0; + sc_desc->BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + UINT create_flags = D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_BGRA_SUPPORT; + #if defined(SOKOL_DEBUG) + create_flags |= D3D11_CREATE_DEVICE_DEBUG; + #endif + D3D_FEATURE_LEVEL feature_level; + HRESULT hr = D3D11CreateDeviceAndSwapChain( + NULL, /* pAdapter (use default) */ + D3D_DRIVER_TYPE_HARDWARE, /* DriverType */ + NULL, /* Software */ + create_flags, /* Flags */ + NULL, /* pFeatureLevels */ + 0, /* FeatureLevels */ + D3D11_SDK_VERSION, /* SDKVersion */ + sc_desc, /* pSwapChainDesc */ + &_sapp.d3d11.swap_chain, /* ppSwapChain */ + &_sapp.d3d11.device, /* ppDevice */ + &feature_level, /* pFeatureLevel */ + &_sapp.d3d11.device_context); /* ppImmediateContext */ + _SOKOL_UNUSED(hr); + #if defined(SOKOL_DEBUG) + if (!SUCCEEDED(hr)) { + // if initialization with D3D11_CREATE_DEVICE_DEBUG fails, this could be because the + // 'D3D11 debug layer' stopped working, indicated by the error message: + // === + // D3D11CreateDevice: Flags (0x2) were specified which require the D3D11 SDK Layers for Windows 10, but they are not present on the system. + // These flags must be removed, or the Windows 10 SDK must be installed. + // Flags include: D3D11_CREATE_DEVICE_DEBUG + // === + // + // ...just retry with the DEBUG flag switched off + _SAPP_ERROR(WIN32_D3D11_CREATE_DEVICE_AND_SWAPCHAIN_WITH_DEBUG_FAILED); + create_flags &= ~D3D11_CREATE_DEVICE_DEBUG; + hr = D3D11CreateDeviceAndSwapChain( + NULL, /* pAdapter (use default) */ + D3D_DRIVER_TYPE_HARDWARE, /* DriverType */ + NULL, /* Software */ + create_flags, /* Flags */ + NULL, /* pFeatureLevels */ + 0, /* FeatureLevels */ + D3D11_SDK_VERSION, /* SDKVersion */ + sc_desc, /* pSwapChainDesc */ + &_sapp.d3d11.swap_chain, /* ppSwapChain */ + &_sapp.d3d11.device, /* ppDevice */ + &feature_level, /* pFeatureLevel */ + &_sapp.d3d11.device_context); /* ppImmediateContext */ + } + #endif + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.swap_chain && _sapp.d3d11.device && _sapp.d3d11.device_context); + + // minimize frame latency, disable Alt-Enter + hr = _sapp_d3d11_QueryInterface(_sapp.d3d11.device, _sapp_win32_refiid(_sapp_IID_IDXGIDevice1), (void**)&_sapp.d3d11.dxgi_device); + if (SUCCEEDED(hr) && _sapp.d3d11.dxgi_device) { + _sapp_dxgi_SetMaximumFrameLatency(_sapp.d3d11.dxgi_device, 1); + IDXGIAdapter* dxgi_adapter = 0; + hr = _sapp_dxgi_GetAdapter(_sapp.d3d11.dxgi_device, &dxgi_adapter); + if (SUCCEEDED(hr) && dxgi_adapter) { + IDXGIFactory* dxgi_factory = 0; + hr = _sapp_dxgi_GetParent((IDXGIObject*)dxgi_adapter, _sapp_win32_refiid(_sapp_IID_IDXGIFactory), (void**)&dxgi_factory); + if (SUCCEEDED(hr)) { + _sapp_dxgi_MakeWindowAssociation(dxgi_factory, _sapp.win32.hwnd, DXGI_MWA_NO_ALT_ENTER|DXGI_MWA_NO_PRINT_SCREEN); + _SAPP_SAFE_RELEASE(dxgi_factory); + } + else { + _SAPP_ERROR(WIN32_D3D11_GET_IDXGIFACTORY_FAILED); + } + _SAPP_SAFE_RELEASE(dxgi_adapter); + } + else { + _SAPP_ERROR(WIN32_D3D11_GET_IDXGIADAPTER_FAILED); + } + } + else { + _SAPP_PANIC(WIN32_D3D11_QUERY_INTERFACE_IDXGIDEVICE1_FAILED); + } +} + +_SOKOL_PRIVATE void _sapp_d3d11_destroy_device_and_swapchain(void) { + _SAPP_SAFE_RELEASE(_sapp.d3d11.swap_chain); + _SAPP_SAFE_RELEASE(_sapp.d3d11.dxgi_device); + _SAPP_SAFE_RELEASE(_sapp.d3d11.device_context); + _SAPP_SAFE_RELEASE(_sapp.d3d11.device); +} + +_SOKOL_PRIVATE void _sapp_d3d11_create_default_render_target(void) { + SOKOL_ASSERT(0 == _sapp.d3d11.rt); + SOKOL_ASSERT(0 == _sapp.d3d11.rtv); + SOKOL_ASSERT(0 == _sapp.d3d11.msaa_rt); + SOKOL_ASSERT(0 == _sapp.d3d11.msaa_rtv); + SOKOL_ASSERT(0 == _sapp.d3d11.ds); + SOKOL_ASSERT(0 == _sapp.d3d11.dsv); + + HRESULT hr; + + /* view for the swapchain-created framebuffer */ + hr = _sapp_dxgi_GetBuffer(_sapp.d3d11.swap_chain, 0, _sapp_win32_refiid(_sapp_IID_ID3D11Texture2D), (void**)&_sapp.d3d11.rt); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rt); + hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.rt, NULL, &_sapp.d3d11.rtv); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rtv); + + /* common desc for MSAA and depth-stencil texture */ + D3D11_TEXTURE2D_DESC tex_desc; + _sapp_clear(&tex_desc, sizeof(tex_desc)); + tex_desc.Width = (UINT)_sapp.framebuffer_width; + tex_desc.Height = (UINT)_sapp.framebuffer_height; + tex_desc.MipLevels = 1; + tex_desc.ArraySize = 1; + tex_desc.Usage = D3D11_USAGE_DEFAULT; + tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; + tex_desc.SampleDesc.Count = (UINT) _sapp.sample_count; + tex_desc.SampleDesc.Quality = (UINT) (_sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0); + + /* create MSAA texture and view if antialiasing requested */ + if (_sapp.sample_count > 1) { + tex_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &_sapp.d3d11.msaa_rt); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.msaa_rt); + hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.msaa_rt, NULL, &_sapp.d3d11.msaa_rtv); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.msaa_rtv); + } + + /* texture and view for the depth-stencil-surface */ + tex_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; + tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &_sapp.d3d11.ds); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.ds); + hr = _sapp_d3d11_CreateDepthStencilView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.ds, NULL, &_sapp.d3d11.dsv); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.dsv); +} + +_SOKOL_PRIVATE void _sapp_d3d11_destroy_default_render_target(void) { + _SAPP_SAFE_RELEASE(_sapp.d3d11.rt); + _SAPP_SAFE_RELEASE(_sapp.d3d11.rtv); + _SAPP_SAFE_RELEASE(_sapp.d3d11.msaa_rt); + _SAPP_SAFE_RELEASE(_sapp.d3d11.msaa_rtv); + _SAPP_SAFE_RELEASE(_sapp.d3d11.ds); + _SAPP_SAFE_RELEASE(_sapp.d3d11.dsv); +} + +_SOKOL_PRIVATE void _sapp_d3d11_resize_default_render_target(void) { + if (_sapp.d3d11.swap_chain) { + _sapp_d3d11_destroy_default_render_target(); + _sapp_dxgi_ResizeBuffers(_sapp.d3d11.swap_chain, _sapp.d3d11.swap_chain_desc.BufferCount, (UINT)_sapp.framebuffer_width, (UINT)_sapp.framebuffer_height, DXGI_FORMAT_B8G8R8A8_UNORM, 0); + _sapp_d3d11_create_default_render_target(); + } +} + +_SOKOL_PRIVATE void _sapp_d3d11_present(bool do_not_wait) { + UINT flags = 0; + if (_sapp.win32.is_win10_or_greater && do_not_wait) { + /* this hack/workaround somewhat improves window-movement and -sizing + responsiveness when rendering is controlled via WM_TIMER during window + move and resize on NVIDIA cards on Win10 with recent drivers. + */ + flags = DXGI_PRESENT_DO_NOT_WAIT; + } + _sapp_dxgi_Present(_sapp.d3d11.swap_chain, (UINT)_sapp.swap_interval, flags); +} + +#endif /* SOKOL_D3D11 */ + +#if defined(SOKOL_GLCORE) +_SOKOL_PRIVATE void _sapp_wgl_init(void) { + _sapp.wgl.opengl32 = LoadLibraryA("opengl32.dll"); + if (!_sapp.wgl.opengl32) { + _SAPP_PANIC(WIN32_LOAD_OPENGL32_DLL_FAILED); + } + SOKOL_ASSERT(_sapp.wgl.opengl32); + _sapp.wgl.CreateContext = (PFN_wglCreateContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglCreateContext"); + SOKOL_ASSERT(_sapp.wgl.CreateContext); + _sapp.wgl.DeleteContext = (PFN_wglDeleteContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglDeleteContext"); + SOKOL_ASSERT(_sapp.wgl.DeleteContext); + _sapp.wgl.GetProcAddress = (PFN_wglGetProcAddress)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglGetProcAddress"); + SOKOL_ASSERT(_sapp.wgl.GetProcAddress); + _sapp.wgl.GetCurrentDC = (PFN_wglGetCurrentDC)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglGetCurrentDC"); + SOKOL_ASSERT(_sapp.wgl.GetCurrentDC); + _sapp.wgl.MakeCurrent = (PFN_wglMakeCurrent)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglMakeCurrent"); + SOKOL_ASSERT(_sapp.wgl.MakeCurrent); + _sapp.wgl.GetIntegerv = (void(WINAPI*)(uint32_t, int32_t*)) GetProcAddress(_sapp.wgl.opengl32, "glGetIntegerv"); + SOKOL_ASSERT(_sapp.wgl.GetIntegerv); + + _sapp.wgl.msg_hwnd = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, + L"SOKOLAPP", + L"sokol-app message window", + WS_CLIPSIBLINGS|WS_CLIPCHILDREN, + 0, 0, 1, 1, + NULL, NULL, + GetModuleHandleW(NULL), + NULL); + if (!_sapp.wgl.msg_hwnd) { + _SAPP_PANIC(WIN32_CREATE_HELPER_WINDOW_FAILED); + } + SOKOL_ASSERT(_sapp.wgl.msg_hwnd); + ShowWindow(_sapp.wgl.msg_hwnd, SW_HIDE); + MSG msg; + while (PeekMessageW(&msg, _sapp.wgl.msg_hwnd, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + _sapp.wgl.msg_dc = GetDC(_sapp.wgl.msg_hwnd); + if (!_sapp.wgl.msg_dc) { + _SAPP_PANIC(WIN32_HELPER_WINDOW_GETDC_FAILED); + } +} + +_SOKOL_PRIVATE void _sapp_wgl_shutdown(void) { + SOKOL_ASSERT(_sapp.wgl.opengl32 && _sapp.wgl.msg_hwnd); + DestroyWindow(_sapp.wgl.msg_hwnd); _sapp.wgl.msg_hwnd = 0; + FreeLibrary(_sapp.wgl.opengl32); _sapp.wgl.opengl32 = 0; +} + +_SOKOL_PRIVATE bool _sapp_wgl_has_ext(const char* ext, const char* extensions) { + SOKOL_ASSERT(ext && extensions); + const char* start = extensions; + while (true) { + const char* where = strstr(start, ext); + if (!where) { + return false; + } + const char* terminator = where + strlen(ext); + if ((where == start) || (*(where - 1) == ' ')) { + if (*terminator == ' ' || *terminator == '\0') { + break; + } + } + start = terminator; + } + return true; +} + +_SOKOL_PRIVATE bool _sapp_wgl_ext_supported(const char* ext) { + SOKOL_ASSERT(ext); + if (_sapp.wgl.GetExtensionsStringEXT) { + const char* extensions = _sapp.wgl.GetExtensionsStringEXT(); + if (extensions) { + if (_sapp_wgl_has_ext(ext, extensions)) { + return true; + } + } + } + if (_sapp.wgl.GetExtensionsStringARB) { + const char* extensions = _sapp.wgl.GetExtensionsStringARB(_sapp.wgl.GetCurrentDC()); + if (extensions) { + if (_sapp_wgl_has_ext(ext, extensions)) { + return true; + } + } + } + return false; +} + +_SOKOL_PRIVATE void _sapp_wgl_load_extensions(void) { + SOKOL_ASSERT(_sapp.wgl.msg_dc); + PIXELFORMATDESCRIPTOR pfd; + _sapp_clear(&pfd, sizeof(pfd)); + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + if (!SetPixelFormat(_sapp.wgl.msg_dc, ChoosePixelFormat(_sapp.wgl.msg_dc, &pfd), &pfd)) { + _SAPP_PANIC(WIN32_DUMMY_CONTEXT_SET_PIXELFORMAT_FAILED); + } + HGLRC rc = _sapp.wgl.CreateContext(_sapp.wgl.msg_dc); + if (!rc) { + _SAPP_PANIC(WIN32_CREATE_DUMMY_CONTEXT_FAILED); + } + if (!_sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, rc)) { + _SAPP_PANIC(WIN32_DUMMY_CONTEXT_MAKE_CURRENT_FAILED); + } + _sapp.wgl.GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringEXT"); + _sapp.wgl.GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringARB"); + _sapp.wgl.CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)(void*) _sapp.wgl.GetProcAddress("wglCreateContextAttribsARB"); + _sapp.wgl.SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)(void*) _sapp.wgl.GetProcAddress("wglSwapIntervalEXT"); + _sapp.wgl.GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(void*) _sapp.wgl.GetProcAddress("wglGetPixelFormatAttribivARB"); + _sapp.wgl.arb_multisample = _sapp_wgl_ext_supported("WGL_ARB_multisample"); + _sapp.wgl.arb_create_context = _sapp_wgl_ext_supported("WGL_ARB_create_context"); + _sapp.wgl.arb_create_context_profile = _sapp_wgl_ext_supported("WGL_ARB_create_context_profile"); + _sapp.wgl.ext_swap_control = _sapp_wgl_ext_supported("WGL_EXT_swap_control"); + _sapp.wgl.arb_pixel_format = _sapp_wgl_ext_supported("WGL_ARB_pixel_format"); + _sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, 0); + _sapp.wgl.DeleteContext(rc); +} + +_SOKOL_PRIVATE int _sapp_wgl_attrib(int pixel_format, int attrib) { + SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); + int value = 0; + if (!_sapp.wgl.GetPixelFormatAttribivARB(_sapp.win32.dc, pixel_format, 0, 1, &attrib, &value)) { + _SAPP_PANIC(WIN32_GET_PIXELFORMAT_ATTRIB_FAILED); + } + return value; +} + +_SOKOL_PRIVATE void _sapp_wgl_attribiv(int pixel_format, int num_attribs, const int* attribs, int* results) { + SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); + if (!_sapp.wgl.GetPixelFormatAttribivARB(_sapp.win32.dc, pixel_format, 0, num_attribs, attribs, results)) { + _SAPP_PANIC(WIN32_GET_PIXELFORMAT_ATTRIB_FAILED); + } +} + +_SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) { + SOKOL_ASSERT(_sapp.win32.dc); + SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); + + #define _sapp_wgl_num_query_tags (12) + const int query_tags[_sapp_wgl_num_query_tags] = { + WGL_SUPPORT_OPENGL_ARB, + WGL_DRAW_TO_WINDOW_ARB, + WGL_PIXEL_TYPE_ARB, + WGL_ACCELERATION_ARB, + WGL_DOUBLE_BUFFER_ARB, + WGL_RED_BITS_ARB, + WGL_GREEN_BITS_ARB, + WGL_BLUE_BITS_ARB, + WGL_ALPHA_BITS_ARB, + WGL_DEPTH_BITS_ARB, + WGL_STENCIL_BITS_ARB, + WGL_SAMPLES_ARB, + }; + const int result_support_opengl_index = 0; + const int result_draw_to_window_index = 1; + const int result_pixel_type_index = 2; + const int result_acceleration_index = 3; + const int result_double_buffer_index = 4; + const int result_red_bits_index = 5; + const int result_green_bits_index = 6; + const int result_blue_bits_index = 7; + const int result_alpha_bits_index = 8; + const int result_depth_bits_index = 9; + const int result_stencil_bits_index = 10; + const int result_samples_index = 11; + + int query_results[_sapp_wgl_num_query_tags] = {0}; + // Drop the last item if multisample extension is not supported. + // If in future querying with multiple extensions, will have to shuffle index values to have active extensions on the end. + int query_count = _sapp_wgl_num_query_tags; + if (!_sapp.wgl.arb_multisample) { + query_count = _sapp_wgl_num_query_tags - 1; + } + + int native_count = _sapp_wgl_attrib(1, WGL_NUMBER_PIXEL_FORMATS_ARB); + + _sapp_gl_fbconfig desired; + _sapp_gl_init_fbconfig(&desired); + desired.red_bits = 8; + desired.green_bits = 8; + desired.blue_bits = 8; + desired.alpha_bits = 8; + desired.depth_bits = 24; + desired.stencil_bits = 8; + desired.doublebuffer = true; + desired.samples = (_sapp.sample_count > 1) ? _sapp.sample_count : 0; + + int pixel_format = 0; + + _sapp_gl_fbselect fbselect; + _sapp_gl_init_fbselect(&fbselect); + for (int i = 0; i < native_count; i++) { + const int n = i + 1; + _sapp_wgl_attribiv(n, query_count, query_tags, query_results); + + if (query_results[result_support_opengl_index] == 0 + || query_results[result_draw_to_window_index] == 0 + || query_results[result_pixel_type_index] != WGL_TYPE_RGBA_ARB + || query_results[result_acceleration_index] == WGL_NO_ACCELERATION_ARB) + { + continue; + } + + _sapp_gl_fbconfig u; + _sapp_clear(&u, sizeof(u)); + u.red_bits = query_results[result_red_bits_index]; + u.green_bits = query_results[result_green_bits_index]; + u.blue_bits = query_results[result_blue_bits_index]; + u.alpha_bits = query_results[result_alpha_bits_index]; + u.depth_bits = query_results[result_depth_bits_index]; + u.stencil_bits = query_results[result_stencil_bits_index]; + u.doublebuffer = 0 != query_results[result_double_buffer_index]; + u.samples = query_results[result_samples_index]; // NOTE: If arb_multisample is not supported - just takes the default 0 + + // Test if this pixel format is better than the previous one + if (_sapp_gl_select_fbconfig(&fbselect, &desired, &u)) { + pixel_format = (uintptr_t)n; + + // Early exit if matching as good as possible + if (fbselect.best_match) { + break; + } + } + } + + return pixel_format; +} + +_SOKOL_PRIVATE void _sapp_wgl_create_context(void) { + int pixel_format = _sapp_wgl_find_pixel_format(); + if (0 == pixel_format) { + _SAPP_PANIC(WIN32_WGL_FIND_PIXELFORMAT_FAILED); + } + PIXELFORMATDESCRIPTOR pfd; + if (!DescribePixelFormat(_sapp.win32.dc, pixel_format, sizeof(pfd), &pfd)) { + _SAPP_PANIC(WIN32_WGL_DESCRIBE_PIXELFORMAT_FAILED); + } + if (!SetPixelFormat(_sapp.win32.dc, pixel_format, &pfd)) { + _SAPP_PANIC(WIN32_WGL_SET_PIXELFORMAT_FAILED); + } + if (!_sapp.wgl.arb_create_context) { + _SAPP_PANIC(WIN32_WGL_ARB_CREATE_CONTEXT_REQUIRED); + } + if (!_sapp.wgl.arb_create_context_profile) { + _SAPP_PANIC(WIN32_WGL_ARB_CREATE_CONTEXT_PROFILE_REQUIRED); + } + const int attrs[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, _sapp.desc.gl_major_version, + WGL_CONTEXT_MINOR_VERSION_ARB, _sapp.desc.gl_minor_version, +#if defined(SOKOL_DEBUG) + WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | WGL_CONTEXT_DEBUG_BIT_ARB, +#else + WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, +#endif + WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + 0, 0 + }; + _sapp.wgl.gl_ctx = _sapp.wgl.CreateContextAttribsARB(_sapp.win32.dc, 0, attrs); + if (!_sapp.wgl.gl_ctx) { + const DWORD err = GetLastError(); + if (err == (0xc0070000 | ERROR_INVALID_VERSION_ARB)) { + _SAPP_PANIC(WIN32_WGL_OPENGL_3_2_NOT_SUPPORTED); + } + else if (err == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) { + _SAPP_PANIC(WIN32_WGL_OPENGL_PROFILE_NOT_SUPPORTED); + } + else if (err == (0xc0070000 | ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB)) { + _SAPP_PANIC(WIN32_WGL_INCOMPATIBLE_DEVICE_CONTEXT); + } + else { + _SAPP_PANIC(WIN32_WGL_CREATE_CONTEXT_ATTRIBS_FAILED_OTHER); + } + } + _sapp.wgl.MakeCurrent(_sapp.win32.dc, _sapp.wgl.gl_ctx); + if (_sapp.wgl.ext_swap_control) { + /* FIXME: DwmIsCompositionEnabled() (see GLFW) */ + _sapp.wgl.SwapIntervalEXT(_sapp.swap_interval); + } + const uint32_t gl_framebuffer_binding = 0x8CA6; + _sapp.wgl.GetIntegerv(gl_framebuffer_binding, (int32_t*)&_sapp.gl.framebuffer); +} + +_SOKOL_PRIVATE void _sapp_wgl_destroy_context(void) { + SOKOL_ASSERT(_sapp.wgl.gl_ctx); + _sapp.wgl.DeleteContext(_sapp.wgl.gl_ctx); + _sapp.wgl.gl_ctx = 0; +} + +_SOKOL_PRIVATE void _sapp_wgl_swap_buffers(void) { + SOKOL_ASSERT(_sapp.win32.dc); + /* FIXME: DwmIsCompositionEnabled? (see GLFW) */ + SwapBuffers(_sapp.win32.dc); +} +#endif /* SOKOL_GLCORE */ + +_SOKOL_PRIVATE bool _sapp_win32_wide_to_utf8(const wchar_t* src, char* dst, int dst_num_bytes) { + SOKOL_ASSERT(src && dst && (dst_num_bytes > 1)); + _sapp_clear(dst, (size_t)dst_num_bytes); + const int bytes_needed = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL); + if (bytes_needed <= dst_num_bytes) { + WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, dst_num_bytes, NULL, NULL); + return true; + } + else { + return false; + } +} + +/* updates current window and framebuffer size from the window's client rect, returns true if size has changed */ +_SOKOL_PRIVATE bool _sapp_win32_update_dimensions(void) { + RECT rect; + if (GetClientRect(_sapp.win32.hwnd, &rect)) { + float window_width = (float)(rect.right - rect.left) / _sapp.win32.dpi.window_scale; + float window_height = (float)(rect.bottom - rect.top) / _sapp.win32.dpi.window_scale; + _sapp.window_width = (int)roundf(window_width); + _sapp.window_height = (int)roundf(window_height); + int fb_width = (int)roundf(window_width * _sapp.win32.dpi.content_scale); + int fb_height = (int)roundf(window_height * _sapp.win32.dpi.content_scale); + /* prevent a framebuffer size of 0 when window is minimized */ + if (0 == fb_width) { + fb_width = 1; + } + if (0 == fb_height) { + fb_height = 1; + } + if ((fb_width != _sapp.framebuffer_width) || (fb_height != _sapp.framebuffer_height)) { + _sapp.framebuffer_width = fb_width; + _sapp.framebuffer_height = fb_height; + return true; + } + } + else { + _sapp.window_width = _sapp.window_height = 1; + _sapp.framebuffer_width = _sapp.framebuffer_height = 1; + } + return false; +} + +_SOKOL_PRIVATE void _sapp_win32_set_fullscreen(bool fullscreen, UINT swp_flags) { + HMONITOR monitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONEAREST); + MONITORINFO minfo; + _sapp_clear(&minfo, sizeof(minfo)); + minfo.cbSize = sizeof(MONITORINFO); + GetMonitorInfo(monitor, &minfo); + const RECT mr = minfo.rcMonitor; + const int monitor_w = mr.right - mr.left; + const int monitor_h = mr.bottom - mr.top; + + const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + DWORD win_style; + RECT rect = { 0, 0, 0, 0 }; + + _sapp.fullscreen = fullscreen; + if (!_sapp.fullscreen) { + win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; + rect = _sapp.win32.stored_window_rect; + } + else { + GetWindowRect(_sapp.win32.hwnd, &_sapp.win32.stored_window_rect); + win_style = WS_POPUP | WS_SYSMENU | WS_VISIBLE; + rect.left = mr.left; + rect.top = mr.top; + rect.right = rect.left + monitor_w; + rect.bottom = rect.top + monitor_h; + AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style); + } + const int win_w = rect.right - rect.left; + const int win_h = rect.bottom - rect.top; + const int win_x = rect.left; + const int win_y = rect.top; + SetWindowLongPtr(_sapp.win32.hwnd, GWL_STYLE, win_style); + SetWindowPos(_sapp.win32.hwnd, HWND_TOP, win_x, win_y, win_w, win_h, swp_flags | SWP_FRAMECHANGED); +} + +_SOKOL_PRIVATE void _sapp_win32_toggle_fullscreen(void) { + _sapp_win32_set_fullscreen(!_sapp.fullscreen, SWP_SHOWWINDOW); +} + +_SOKOL_PRIVATE void _sapp_win32_init_cursor(sapp_mouse_cursor cursor) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + // NOTE: the OCR_* constants are only defined if OEMRESOURCE is defined + // before windows.h is included, but we can't guarantee that because + // the sokol_app.h implementation may be included with other implementations + // in the same compilation unit + int id = 0; + switch (cursor) { + case SAPP_MOUSECURSOR_ARROW: id = 32512; break; // OCR_NORMAL + case SAPP_MOUSECURSOR_IBEAM: id = 32513; break; // OCR_IBEAM + case SAPP_MOUSECURSOR_CROSSHAIR: id = 32515; break; // OCR_CROSS + case SAPP_MOUSECURSOR_POINTING_HAND: id = 32649; break; // OCR_HAND + case SAPP_MOUSECURSOR_RESIZE_EW: id = 32644; break; // OCR_SIZEWE + case SAPP_MOUSECURSOR_RESIZE_NS: id = 32645; break; // OCR_SIZENS + case SAPP_MOUSECURSOR_RESIZE_NWSE: id = 32642; break; // OCR_SIZENWSE + case SAPP_MOUSECURSOR_RESIZE_NESW: id = 32643; break; // OCR_SIZENESW + case SAPP_MOUSECURSOR_RESIZE_ALL: id = 32646; break; // OCR_SIZEALL + case SAPP_MOUSECURSOR_NOT_ALLOWED: id = 32648; break; // OCR_NO + default: break; + } + if (id != 0) { + _sapp.win32.cursors[cursor] = (HCURSOR)LoadImageW(NULL, MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE|LR_SHARED); + } + // fallback: default cursor + if (0 == _sapp.win32.cursors[cursor]) { + // 32512 => IDC_ARROW + _sapp.win32.cursors[cursor] = LoadCursorW(NULL, MAKEINTRESOURCEW(32512)); + } + SOKOL_ASSERT(0 != _sapp.win32.cursors[cursor]); +} + +_SOKOL_PRIVATE void _sapp_win32_init_cursors(void) { + for (int i = 0; i < _SAPP_MOUSECURSOR_NUM; i++) { + _sapp_win32_init_cursor((sapp_mouse_cursor)i); + } +} + +_SOKOL_PRIVATE bool _sapp_win32_cursor_in_content_area(void) { + POINT pos; + if (!GetCursorPos(&pos)) { + return false; + } + if (WindowFromPoint(pos) != _sapp.win32.hwnd) { + return false; + } + RECT area; + GetClientRect(_sapp.win32.hwnd, &area); + ClientToScreen(_sapp.win32.hwnd, (POINT*)&area.left); + ClientToScreen(_sapp.win32.hwnd, (POINT*)&area.right); + return PtInRect(&area, pos) == TRUE; +} + +_SOKOL_PRIVATE void _sapp_win32_update_cursor(sapp_mouse_cursor cursor, bool shown, bool skip_area_test) { + // NOTE: when called from WM_SETCURSOR, the area test would be redundant + if (!skip_area_test) { + if (!_sapp_win32_cursor_in_content_area()) { + return; + } + } + if (!shown) { + SetCursor(NULL); + } + else { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + SOKOL_ASSERT(0 != _sapp.win32.cursors[cursor]); + SetCursor(_sapp.win32.cursors[cursor]); + } +} + +_SOKOL_PRIVATE void _sapp_win32_capture_mouse(uint8_t btn_mask) { + if (0 == _sapp.win32.mouse_capture_mask) { + SetCapture(_sapp.win32.hwnd); + } + _sapp.win32.mouse_capture_mask |= btn_mask; +} + +_SOKOL_PRIVATE void _sapp_win32_release_mouse(uint8_t btn_mask) { + if (0 != _sapp.win32.mouse_capture_mask) { + _sapp.win32.mouse_capture_mask &= ~btn_mask; + if (0 == _sapp.win32.mouse_capture_mask) { + ReleaseCapture(); + } + } +} + +_SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { + if (lock == _sapp.mouse.locked) { + return; + } + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + _sapp.mouse.locked = lock; + _sapp_win32_release_mouse(0xFF); + if (_sapp.mouse.locked) { + /* store the current mouse position, so it can be restored when unlocked */ + POINT pos; + BOOL res = GetCursorPos(&pos); + SOKOL_ASSERT(res); _SOKOL_UNUSED(res); + _sapp.win32.mouse_locked_x = pos.x; + _sapp.win32.mouse_locked_y = pos.y; + + /* while the mouse is locked, make the mouse cursor invisible and + confine the mouse movement to a small rectangle inside our window + (so that we don't miss any mouse up events) + */ + RECT client_rect = { + _sapp.win32.mouse_locked_x, + _sapp.win32.mouse_locked_y, + _sapp.win32.mouse_locked_x, + _sapp.win32.mouse_locked_y + }; + ClipCursor(&client_rect); + + /* make the mouse cursor invisible, this will stack with sapp_show_mouse() */ + ShowCursor(FALSE); + + /* enable raw input for mouse, starts sending WM_INPUT messages to WinProc (see GLFW) */ + const RAWINPUTDEVICE rid = { + 0x01, // usUsagePage: HID_USAGE_PAGE_GENERIC + 0x02, // usUsage: HID_USAGE_GENERIC_MOUSE + 0, // dwFlags + _sapp.win32.hwnd // hwndTarget + }; + if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { + _SAPP_ERROR(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_LOCK); + } + /* in case the raw mouse device only supports absolute position reporting, + we need to skip the dx/dy compution for the first WM_INPUT event + */ + _sapp.win32.raw_input_mousepos_valid = false; + } + else { + /* disable raw input for mouse */ + const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL }; + if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { + _SAPP_ERROR(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_UNLOCK); + } + + /* let the mouse roam freely again */ + ClipCursor(NULL); + ShowCursor(TRUE); + + /* restore the 'pre-locked' mouse position */ + BOOL res = SetCursorPos(_sapp.win32.mouse_locked_x, _sapp.win32.mouse_locked_y); + SOKOL_ASSERT(res); _SOKOL_UNUSED(res); + } +} + +_SOKOL_PRIVATE bool _sapp_win32_update_monitor(void) { + const HMONITOR cur_monitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONULL); + if (cur_monitor != _sapp.win32.hmonitor) { + _sapp.win32.hmonitor = cur_monitor; + return true; + } + else { + return false; + } +} + +_SOKOL_PRIVATE uint32_t _sapp_win32_mods(void) { + uint32_t mods = 0; + if (GetKeyState(VK_SHIFT) & (1<<15)) { + mods |= SAPP_MODIFIER_SHIFT; + } + if (GetKeyState(VK_CONTROL) & (1<<15)) { + mods |= SAPP_MODIFIER_CTRL; + } + if (GetKeyState(VK_MENU) & (1<<15)) { + mods |= SAPP_MODIFIER_ALT; + } + if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & (1<<15)) { + mods |= SAPP_MODIFIER_SUPER; + } + const bool swapped = (TRUE == GetSystemMetrics(SM_SWAPBUTTON)); + if (GetAsyncKeyState(VK_LBUTTON)) { + mods |= swapped ? SAPP_MODIFIER_RMB : SAPP_MODIFIER_LMB; + } + if (GetAsyncKeyState(VK_RBUTTON)) { + mods |= swapped ? SAPP_MODIFIER_LMB : SAPP_MODIFIER_RMB; + } + if (GetAsyncKeyState(VK_MBUTTON)) { + mods |= SAPP_MODIFIER_MMB; + } + return mods; +} + +_SOKOL_PRIVATE void _sapp_win32_mouse_update(LPARAM lParam) { + if (!_sapp.mouse.locked) { + const float new_x = (float)GET_X_LPARAM(lParam) * _sapp.win32.dpi.mouse_scale; + const float new_y = (float)GET_Y_LPARAM(lParam) * _sapp.win32.dpi.mouse_scale; + if (_sapp.mouse.pos_valid) { + // don't update dx/dy in the very first event + _sapp.mouse.dx = new_x - _sapp.mouse.x; + _sapp.mouse.dy = new_y - _sapp.mouse.y; + } + _sapp.mouse.x = new_x; + _sapp.mouse.y = new_y; + _sapp.mouse.pos_valid = true; + } +} + +_SOKOL_PRIVATE void _sapp_win32_mouse_event(sapp_event_type type, sapp_mousebutton btn) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp.event.modifiers = _sapp_win32_mods(); + _sapp.event.mouse_button = btn; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_win32_scroll_event(float x, float y) { + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + _sapp.event.modifiers = _sapp_win32_mods(); + _sapp.event.scroll_x = -x / 30.0f; + _sapp.event.scroll_y = y / 30.0f; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_win32_key_event(sapp_event_type type, int vk, bool repeat) { + if (_sapp_events_enabled() && (vk < SAPP_MAX_KEYCODES)) { + _sapp_init_event(type); + _sapp.event.modifiers = _sapp_win32_mods(); + _sapp.event.key_code = _sapp.keycodes[vk]; + _sapp.event.key_repeat = repeat; + _sapp_call_event(&_sapp.event); + /* check if a CLIPBOARD_PASTED event must be sent too */ + if (_sapp.clipboard.enabled && + (type == SAPP_EVENTTYPE_KEY_DOWN) && + (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) && + (_sapp.event.key_code == SAPP_KEYCODE_V)) + { + _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); + _sapp_call_event(&_sapp.event); + } + } +} + +_SOKOL_PRIVATE void _sapp_win32_char_event(uint32_t c, bool repeat) { + if (_sapp_events_enabled() && (c >= 32)) { + _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp.event.modifiers = _sapp_win32_mods(); + _sapp.event.char_code = c; + _sapp.event.key_repeat = repeat; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_win32_dpi_changed(HWND hWnd, LPRECT proposed_win_rect) { + /* called on WM_DPICHANGED, which will only be sent to the application + if sapp_desc.high_dpi is true and the Windows version is recent enough + to support DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 + */ + SOKOL_ASSERT(_sapp.desc.high_dpi); + HINSTANCE user32 = LoadLibraryA("user32.dll"); + if (!user32) { + return; + } + typedef UINT(WINAPI * GETDPIFORWINDOW_T)(HWND hwnd); + GETDPIFORWINDOW_T fn_getdpiforwindow = (GETDPIFORWINDOW_T)(void*)GetProcAddress(user32, "GetDpiForWindow"); + if (fn_getdpiforwindow) { + UINT dpix = fn_getdpiforwindow(_sapp.win32.hwnd); + // NOTE: for high-dpi apps, mouse_scale remains one + _sapp.win32.dpi.window_scale = (float)dpix / 96.0f; + _sapp.win32.dpi.content_scale = _sapp.win32.dpi.window_scale; + _sapp.dpi_scale = _sapp.win32.dpi.window_scale; + SetWindowPos(hWnd, 0, + proposed_win_rect->left, + proposed_win_rect->top, + proposed_win_rect->right - proposed_win_rect->left, + proposed_win_rect->bottom - proposed_win_rect->top, + SWP_NOZORDER | SWP_NOACTIVATE); + } + FreeLibrary(user32); +} + +_SOKOL_PRIVATE void _sapp_win32_files_dropped(HDROP hdrop) { + if (!_sapp.drop.enabled) { + return; + } + _sapp_clear_drop_buffer(); + bool drop_failed = false; + const int count = (int) DragQueryFileW(hdrop, 0xffffffff, NULL, 0); + _sapp.drop.num_files = (count > _sapp.drop.max_files) ? _sapp.drop.max_files : count; + for (UINT i = 0; i < (UINT)_sapp.drop.num_files; i++) { + const UINT num_chars = DragQueryFileW(hdrop, i, NULL, 0) + 1; + WCHAR* buffer = (WCHAR*) _sapp_malloc_clear(num_chars * sizeof(WCHAR)); + DragQueryFileW(hdrop, i, buffer, num_chars); + if (!_sapp_win32_wide_to_utf8(buffer, _sapp_dropped_file_path_ptr((int)i), _sapp.drop.max_path_length)) { + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); + drop_failed = true; + } + _sapp_free(buffer); + } + DragFinish(hdrop); + if (!drop_failed) { + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp.event.modifiers = _sapp_win32_mods(); + _sapp_call_event(&_sapp.event); + } + } + else { + _sapp_clear_drop_buffer(); + _sapp.drop.num_files = 0; + } +} + +_SOKOL_PRIVATE void _sapp_win32_timing_measure(void) { + #if defined(SOKOL_D3D11) + // on D3D11, use the more precise DXGI timestamp + if (_sapp.d3d11.use_dxgi_frame_stats) { + DXGI_FRAME_STATISTICS dxgi_stats; + _sapp_clear(&dxgi_stats, sizeof(dxgi_stats)); + HRESULT hr = _sapp_dxgi_GetFrameStatistics(_sapp.d3d11.swap_chain, &dxgi_stats); + if (SUCCEEDED(hr)) { + if (dxgi_stats.SyncRefreshCount != _sapp.d3d11.sync_refresh_count) { + if ((_sapp.d3d11.sync_refresh_count + 1) != dxgi_stats.SyncRefreshCount) { + _sapp_timing_discontinuity(&_sapp.timing); + } + _sapp.d3d11.sync_refresh_count = dxgi_stats.SyncRefreshCount; + LARGE_INTEGER qpc = dxgi_stats.SyncQPCTime; + const uint64_t now = (uint64_t)_sapp_int64_muldiv(qpc.QuadPart - _sapp.timing.timestamp.win.start.QuadPart, 1000000000, _sapp.timing.timestamp.win.freq.QuadPart); + _sapp_timing_external(&_sapp.timing, (double)now / 1000000000.0); + } + return; + } + } + // fallback if swap model isn't "flip-discard" or GetFrameStatistics failed for another reason + _sapp_timing_measure(&_sapp.timing); + #endif + #if defined(SOKOL_GLCORE) + _sapp_timing_measure(&_sapp.timing); + #endif + #if defined(SOKOL_NOAPI) + _sapp_timing_measure(&_sapp.timing); + #endif +} + +_SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (!_sapp.win32.in_create_window) { + switch (uMsg) { + case WM_CLOSE: + /* only give user a chance to intervene when sapp_quit() wasn't already called */ + if (!_sapp.quit_ordered) { + /* if window should be closed and event handling is enabled, give user code + a change to intervene via sapp_cancel_quit() + */ + _sapp.quit_requested = true; + _sapp_win32_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); + /* if user code hasn't intervened, quit the app */ + if (_sapp.quit_requested) { + _sapp.quit_ordered = true; + } + } + if (_sapp.quit_ordered) { + PostQuitMessage(0); + } + return 0; + case WM_SYSCOMMAND: + switch (wParam & 0xFFF0) { + case SC_SCREENSAVE: + case SC_MONITORPOWER: + if (_sapp.fullscreen) { + /* disable screen saver and blanking in fullscreen mode */ + return 0; + } + break; + case SC_KEYMENU: + /* user trying to access menu via ALT */ + return 0; + } + break; + case WM_ERASEBKGND: + return 1; + case WM_SIZE: + { + const bool iconified = wParam == SIZE_MINIMIZED; + if (iconified != _sapp.win32.iconified) { + _sapp.win32.iconified = iconified; + if (iconified) { + _sapp_win32_app_event(SAPP_EVENTTYPE_ICONIFIED); + } + else { + _sapp_win32_app_event(SAPP_EVENTTYPE_RESTORED); + } + } + } + break; + case WM_SETFOCUS: + _sapp_win32_app_event(SAPP_EVENTTYPE_FOCUSED); + break; + case WM_KILLFOCUS: + /* if focus is lost for any reason, and we're in mouse locked mode, disable mouse lock */ + if (_sapp.mouse.locked) { + _sapp_win32_lock_mouse(false); + } + _sapp_win32_app_event(SAPP_EVENTTYPE_UNFOCUSED); + break; + case WM_SETCURSOR: + if (LOWORD(lParam) == HTCLIENT) { + _sapp_win32_update_cursor(_sapp.mouse.current_cursor, _sapp.mouse.shown, true); + return TRUE; + } + break; + case WM_DPICHANGED: + { + /* Update window's DPI and size if its moved to another monitor with a different DPI + Only sent if DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 is used. + */ + _sapp_win32_dpi_changed(hWnd, (LPRECT)lParam); + break; + } + case WM_LBUTTONDOWN: + _sapp_win32_mouse_update(lParam); + _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT); + _sapp_win32_capture_mouse(1<data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { + /* mouse only reports absolute position + NOTE: This code is untested and will most likely behave wrong in Remote Desktop sessions. + (such remote desktop sessions are setting the MOUSE_MOVE_ABSOLUTE flag). + See: https://github.com/floooh/sokol/issues/806 and + https://github.com/microsoft/DirectXTK/commit/ef56b63f3739381e451f7a5a5bd2c9779d2a7555) + */ + LONG new_x = raw_mouse_data->data.mouse.lLastX; + LONG new_y = raw_mouse_data->data.mouse.lLastY; + if (_sapp.win32.raw_input_mousepos_valid) { + _sapp.mouse.dx = (float) (new_x - _sapp.win32.raw_input_mousepos_x); + _sapp.mouse.dy = (float) (new_y - _sapp.win32.raw_input_mousepos_y); + } + _sapp.win32.raw_input_mousepos_x = new_x; + _sapp.win32.raw_input_mousepos_y = new_y; + _sapp.win32.raw_input_mousepos_valid = true; + } + else { + /* mouse reports movement delta (this seems to be the common case) */ + _sapp.mouse.dx = (float) raw_mouse_data->data.mouse.lLastX; + _sapp.mouse.dy = (float) raw_mouse_data->data.mouse.lLastY; + } + _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID); + } + break; + + case WM_MOUSELEAVE: + if (!_sapp.mouse.locked) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + _sapp.win32.mouse_tracked = false; + _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID); + } + break; + case WM_MOUSEWHEEL: + _sapp_win32_scroll_event(0.0f, (float)((SHORT)HIWORD(wParam))); + break; + case WM_MOUSEHWHEEL: + _sapp_win32_scroll_event((float)((SHORT)HIWORD(wParam)), 0.0f); + break; + case WM_CHAR: + _sapp_win32_char_event((uint32_t)wParam, !!(lParam&0x40000000)); + break; + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + _sapp_win32_key_event(SAPP_EVENTTYPE_KEY_DOWN, (int)(HIWORD(lParam)&0x1FF), !!(lParam&0x40000000)); + break; + case WM_KEYUP: + case WM_SYSKEYUP: + _sapp_win32_key_event(SAPP_EVENTTYPE_KEY_UP, (int)(HIWORD(lParam)&0x1FF), false); + break; + case WM_ENTERSIZEMOVE: + SetTimer(_sapp.win32.hwnd, 1, USER_TIMER_MINIMUM, NULL); + break; + case WM_EXITSIZEMOVE: + KillTimer(_sapp.win32.hwnd, 1); + break; + case WM_TIMER: + _sapp_win32_timing_measure(); + _sapp_frame(); + #if defined(SOKOL_D3D11) + // present with DXGI_PRESENT_DO_NOT_WAIT + _sapp_d3d11_present(true); + #endif + #if defined(SOKOL_GLCORE) + _sapp_wgl_swap_buffers(); + #endif + /* NOTE: resizing the swap-chain during resize leads to a substantial + memory spike (hundreds of megabytes for a few seconds). + + if (_sapp_win32_update_dimensions()) { + #if defined(SOKOL_D3D11) + _sapp_d3d11_resize_default_render_target(); + #endif + _sapp_win32_app_event(SAPP_EVENTTYPE_RESIZED); + } + */ + break; + case WM_NCLBUTTONDOWN: + /* workaround for half-second pause when starting to move window + see: https://gamedev.net/forums/topic/672094-keeping-things-moving-during-win32-moveresize-events/5254386/ + */ + if (SendMessage(_sapp.win32.hwnd, WM_NCHITTEST, wParam, lParam) == HTCAPTION) { + POINT point; + GetCursorPos(&point); + ScreenToClient(_sapp.win32.hwnd, &point); + PostMessage(_sapp.win32.hwnd, WM_MOUSEMOVE, 0, ((uint32_t)point.x)|(((uint32_t)point.y) << 16)); + } + break; + case WM_DROPFILES: + _sapp_win32_files_dropped((HDROP)wParam); + break; + case WM_DISPLAYCHANGE: + // refresh rate might have changed + _sapp_timing_reset(&_sapp.timing); + break; + + default: + break; + } + } + return DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + +_SOKOL_PRIVATE void _sapp_win32_create_window(void) { + WNDCLASSW wndclassw; + _sapp_clear(&wndclassw, sizeof(wndclassw)); + wndclassw.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wndclassw.lpfnWndProc = (WNDPROC) _sapp_win32_wndproc; + wndclassw.hInstance = GetModuleHandleW(NULL); + wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclassw.hIcon = LoadIcon(NULL, IDI_WINLOGO); + wndclassw.lpszClassName = L"SOKOLAPP"; + RegisterClassW(&wndclassw); + + /* NOTE: regardless whether fullscreen is requested or not, a regular + windowed-mode window will always be created first (however in hidden + mode, so that no windowed-mode window pops up before the fullscreen window) + */ + const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + RECT rect = { 0, 0, 0, 0 }; + DWORD win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; + rect.right = (int) ((float)_sapp.window_width * _sapp.win32.dpi.window_scale); + rect.bottom = (int) ((float)_sapp.window_height * _sapp.win32.dpi.window_scale); + const bool use_default_width = 0 == _sapp.window_width; + const bool use_default_height = 0 == _sapp.window_height; + AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style); + const int win_width = rect.right - rect.left; + const int win_height = rect.bottom - rect.top; + _sapp.win32.in_create_window = true; + _sapp.win32.hwnd = CreateWindowExW( + win_ex_style, // dwExStyle + L"SOKOLAPP", // lpClassName + _sapp.window_title_wide, // lpWindowName + win_style, // dwStyle + CW_USEDEFAULT, // X + SW_HIDE, // Y (NOTE: CW_USEDEFAULT is not used for position here, but internally calls ShowWindow! + use_default_width ? CW_USEDEFAULT : win_width, // nWidth + use_default_height ? CW_USEDEFAULT : win_height, // nHeight (NOTE: if width is CW_USEDEFAULT, height is actually ignored) + NULL, // hWndParent + NULL, // hMenu + GetModuleHandle(NULL), // hInstance + NULL); // lParam + _sapp.win32.in_create_window = false; + _sapp.win32.dc = GetDC(_sapp.win32.hwnd); + _sapp.win32.hmonitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONULL); + SOKOL_ASSERT(_sapp.win32.dc); + + /* this will get the actual windowed-mode window size, if fullscreen + is requested, the set_fullscreen function will then capture the + current window rectangle, which then might be used later to + restore the window position when switching back to windowed + */ + _sapp_win32_update_dimensions(); + if (_sapp.fullscreen) { + _sapp_win32_set_fullscreen(_sapp.fullscreen, SWP_HIDEWINDOW); + _sapp_win32_update_dimensions(); + } + ShowWindow(_sapp.win32.hwnd, SW_SHOW); + DragAcceptFiles(_sapp.win32.hwnd, 1); +} + +_SOKOL_PRIVATE void _sapp_win32_destroy_window(void) { + DestroyWindow(_sapp.win32.hwnd); _sapp.win32.hwnd = 0; + UnregisterClassW(L"SOKOLAPP", GetModuleHandleW(NULL)); +} + +_SOKOL_PRIVATE void _sapp_win32_destroy_icons(void) { + if (_sapp.win32.big_icon) { + DestroyIcon(_sapp.win32.big_icon); + _sapp.win32.big_icon = 0; + } + if (_sapp.win32.small_icon) { + DestroyIcon(_sapp.win32.small_icon); + _sapp.win32.small_icon = 0; + } +} + +_SOKOL_PRIVATE void _sapp_win32_init_console(void) { + if (_sapp.desc.win32_console_create || _sapp.desc.win32_console_attach) { + BOOL con_valid = FALSE; + if (_sapp.desc.win32_console_create) { + con_valid = AllocConsole(); + } + else if (_sapp.desc.win32_console_attach) { + con_valid = AttachConsole(ATTACH_PARENT_PROCESS); + } + if (con_valid) { + FILE* res_fp = 0; + errno_t err; + err = freopen_s(&res_fp, "CON", "w", stdout); + (void)err; + err = freopen_s(&res_fp, "CON", "w", stderr); + (void)err; + } + } + if (_sapp.desc.win32_console_utf8) { + _sapp.win32.orig_codepage = GetConsoleOutputCP(); + SetConsoleOutputCP(CP_UTF8); + } +} + +_SOKOL_PRIVATE void _sapp_win32_restore_console(void) { + if (_sapp.desc.win32_console_utf8) { + SetConsoleOutputCP(_sapp.win32.orig_codepage); + } +} + +_SOKOL_PRIVATE void _sapp_win32_init_dpi(void) { + + DECLARE_HANDLE(DPI_AWARENESS_CONTEXT_T); + typedef BOOL(WINAPI * SETPROCESSDPIAWARE_T)(void); + typedef bool (WINAPI * SETPROCESSDPIAWARENESSCONTEXT_T)(DPI_AWARENESS_CONTEXT_T); // since Windows 10, version 1703 + typedef HRESULT(WINAPI * SETPROCESSDPIAWARENESS_T)(PROCESS_DPI_AWARENESS); + typedef HRESULT(WINAPI * GETDPIFORMONITOR_T)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); + + SETPROCESSDPIAWARE_T fn_setprocessdpiaware = 0; + SETPROCESSDPIAWARENESS_T fn_setprocessdpiawareness = 0; + GETDPIFORMONITOR_T fn_getdpiformonitor = 0; + SETPROCESSDPIAWARENESSCONTEXT_T fn_setprocessdpiawarenesscontext =0; + + HINSTANCE user32 = LoadLibraryA("user32.dll"); + if (user32) { + fn_setprocessdpiaware = (SETPROCESSDPIAWARE_T)(void*) GetProcAddress(user32, "SetProcessDPIAware"); + fn_setprocessdpiawarenesscontext = (SETPROCESSDPIAWARENESSCONTEXT_T)(void*) GetProcAddress(user32, "SetProcessDpiAwarenessContext"); + } + HINSTANCE shcore = LoadLibraryA("shcore.dll"); + if (shcore) { + fn_setprocessdpiawareness = (SETPROCESSDPIAWARENESS_T)(void*) GetProcAddress(shcore, "SetProcessDpiAwareness"); + fn_getdpiformonitor = (GETDPIFORMONITOR_T)(void*) GetProcAddress(shcore, "GetDpiForMonitor"); + } + /* + NOTE on SetProcessDpiAware() vs SetProcessDpiAwareness() vs SetProcessDpiAwarenessContext(): + + These are different attempts to get DPI handling on Windows right, from oldest + to newest. SetProcessDpiAwarenessContext() is required for the new + DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 method. + */ + if (fn_setprocessdpiawareness) { + if (_sapp.desc.high_dpi) { + /* app requests HighDPI rendering, first try the Win10 Creator Update per-monitor-dpi awareness, + if that fails, fall back to system-dpi-awareness + */ + _sapp.win32.dpi.aware = true; + DPI_AWARENESS_CONTEXT_T per_monitor_aware_v2 = (DPI_AWARENESS_CONTEXT_T)-4; + if (!(fn_setprocessdpiawarenesscontext && fn_setprocessdpiawarenesscontext(per_monitor_aware_v2))) { + // fallback to system-dpi-aware + fn_setprocessdpiawareness(PROCESS_SYSTEM_DPI_AWARE); + } + } + else { + /* if the app didn't request HighDPI rendering, let Windows do the upscaling */ + _sapp.win32.dpi.aware = false; + fn_setprocessdpiawareness(PROCESS_DPI_UNAWARE); + } + } + else if (fn_setprocessdpiaware) { + // fallback for Windows 7 + _sapp.win32.dpi.aware = true; + fn_setprocessdpiaware(); + } + /* get dpi scale factor for main monitor */ + if (fn_getdpiformonitor && _sapp.win32.dpi.aware) { + POINT pt = { 1, 1 }; + HMONITOR hm = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + UINT dpix, dpiy; + HRESULT hr = fn_getdpiformonitor(hm, MDT_EFFECTIVE_DPI, &dpix, &dpiy); + _SOKOL_UNUSED(hr); + SOKOL_ASSERT(SUCCEEDED(hr)); + /* clamp window scale to an integer factor */ + _sapp.win32.dpi.window_scale = (float)dpix / 96.0f; + } + else { + _sapp.win32.dpi.window_scale = 1.0f; + } + if (_sapp.desc.high_dpi) { + _sapp.win32.dpi.content_scale = _sapp.win32.dpi.window_scale; + _sapp.win32.dpi.mouse_scale = 1.0f; + } + else { + _sapp.win32.dpi.content_scale = 1.0f; + _sapp.win32.dpi.mouse_scale = 1.0f / _sapp.win32.dpi.window_scale; + } + _sapp.dpi_scale = _sapp.win32.dpi.content_scale; + if (user32) { + FreeLibrary(user32); + } + if (shcore) { + FreeLibrary(shcore); + } +} + +_SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(const char* str) { + SOKOL_ASSERT(str); + SOKOL_ASSERT(_sapp.win32.hwnd); + SOKOL_ASSERT(_sapp.clipboard.enabled && (_sapp.clipboard.buf_size > 0)); + + if (!OpenClipboard(_sapp.win32.hwnd)) { + return false; + } + + HANDLE object = 0; + wchar_t* wchar_buf = 0; + + const SIZE_T wchar_buf_size = (SIZE_T)_sapp.clipboard.buf_size * sizeof(wchar_t); + object = GlobalAlloc(GMEM_MOVEABLE, wchar_buf_size); + if (NULL == object) { + goto error; + } + wchar_buf = (wchar_t*) GlobalLock(object); + if (NULL == wchar_buf) { + goto error; + } + if (!_sapp_win32_utf8_to_wide(str, wchar_buf, (int)wchar_buf_size)) { + goto error; + } + GlobalUnlock(object); + wchar_buf = 0; + EmptyClipboard(); + // NOTE: when successful, SetClipboardData() takes ownership of memory object! + if (NULL == SetClipboardData(CF_UNICODETEXT, object)) { + goto error; + } + CloseClipboard(); + return true; + +error: + if (wchar_buf) { + GlobalUnlock(object); + } + if (object) { + GlobalFree(object); + } + CloseClipboard(); + return false; +} + +_SOKOL_PRIVATE const char* _sapp_win32_get_clipboard_string(void) { + SOKOL_ASSERT(_sapp.clipboard.enabled && _sapp.clipboard.buffer); + SOKOL_ASSERT(_sapp.win32.hwnd); + if (!OpenClipboard(_sapp.win32.hwnd)) { + /* silently ignore any errors and just return the current + content of the local clipboard buffer + */ + return _sapp.clipboard.buffer; + } + HANDLE object = GetClipboardData(CF_UNICODETEXT); + if (!object) { + CloseClipboard(); + return _sapp.clipboard.buffer; + } + const wchar_t* wchar_buf = (const wchar_t*) GlobalLock(object); + if (!wchar_buf) { + CloseClipboard(); + return _sapp.clipboard.buffer; + } + if (!_sapp_win32_wide_to_utf8(wchar_buf, _sapp.clipboard.buffer, _sapp.clipboard.buf_size)) { + _SAPP_ERROR(CLIPBOARD_STRING_TOO_BIG); + } + GlobalUnlock(object); + CloseClipboard(); + return _sapp.clipboard.buffer; +} + +_SOKOL_PRIVATE void _sapp_win32_update_window_title(void) { + _sapp_win32_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); + SetWindowTextW(_sapp.win32.hwnd, _sapp.window_title_wide); +} + +_SOKOL_PRIVATE HICON _sapp_win32_create_icon_from_image(const sapp_image_desc* desc) { + BITMAPV5HEADER bi; + _sapp_clear(&bi, sizeof(bi)); + bi.bV5Size = sizeof(bi); + bi.bV5Width = desc->width; + bi.bV5Height = -desc->height; // NOTE the '-' here to indicate that origin is top-left + bi.bV5Planes = 1; + bi.bV5BitCount = 32; + bi.bV5Compression = BI_BITFIELDS; + bi.bV5RedMask = 0x00FF0000; + bi.bV5GreenMask = 0x0000FF00; + bi.bV5BlueMask = 0x000000FF; + bi.bV5AlphaMask = 0xFF000000; + + uint8_t* target = 0; + const uint8_t* source = (const uint8_t*)desc->pixels.ptr; + + HDC dc = GetDC(NULL); + HBITMAP color = CreateDIBSection(dc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&target, NULL, (DWORD)0); + ReleaseDC(NULL, dc); + if (0 == color) { + return NULL; + } + SOKOL_ASSERT(target); + + HBITMAP mask = CreateBitmap(desc->width, desc->height, 1, 1, NULL); + if (0 == mask) { + DeleteObject(color); + return NULL; + } + + for (int i = 0; i < (desc->width*desc->height); i++) { + target[0] = source[2]; + target[1] = source[1]; + target[2] = source[0]; + target[3] = source[3]; + target += 4; + source += 4; + } + + ICONINFO icon_info; + _sapp_clear(&icon_info, sizeof(icon_info)); + icon_info.fIcon = true; + icon_info.xHotspot = 0; + icon_info.yHotspot = 0; + icon_info.hbmMask = mask; + icon_info.hbmColor = color; + HICON icon_handle = CreateIconIndirect(&icon_info); + DeleteObject(color); + DeleteObject(mask); + + return icon_handle; +} + +_SOKOL_PRIVATE void _sapp_win32_set_icon(const sapp_icon_desc* icon_desc, int num_images) { + SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES)); + + int big_img_index = _sapp_image_bestmatch(icon_desc->images, num_images, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON)); + int sml_img_index = _sapp_image_bestmatch(icon_desc->images, num_images, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); + HICON big_icon = _sapp_win32_create_icon_from_image(&icon_desc->images[big_img_index]); + HICON sml_icon = _sapp_win32_create_icon_from_image(&icon_desc->images[sml_img_index]); + + // if icon creation or lookup has failed for some reason, leave the currently set icon untouched + if (0 != big_icon) { + SendMessage(_sapp.win32.hwnd, WM_SETICON, ICON_BIG, (LPARAM) big_icon); + if (0 != _sapp.win32.big_icon) { + DestroyIcon(_sapp.win32.big_icon); + } + _sapp.win32.big_icon = big_icon; + } + if (0 != sml_icon) { + SendMessage(_sapp.win32.hwnd, WM_SETICON, ICON_SMALL, (LPARAM) sml_icon); + if (0 != _sapp.win32.small_icon) { + DestroyIcon(_sapp.win32.small_icon); + } + _sapp.win32.small_icon = sml_icon; + } +} + +/* don't laugh, but this seems to be the easiest and most robust + way to check if we're running on Win10 + + From: https://github.com/videolan/vlc/blob/232fb13b0d6110c4d1b683cde24cf9a7f2c5c2ea/modules/video_output/win32/d3d11_swapchain.c#L263 +*/ +_SOKOL_PRIVATE bool _sapp_win32_is_win10_or_greater(void) { + HMODULE h = GetModuleHandleW(L"kernel32.dll"); + if (NULL != h) { + return (NULL != GetProcAddress(h, "GetSystemCpuSetInformation")); + } + else { + return false; + } +} + +_SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { + _sapp_init_state(desc); + _sapp_win32_init_console(); + _sapp.win32.is_win10_or_greater = _sapp_win32_is_win10_or_greater(); + _sapp_win32_init_keytable(); + _sapp_win32_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); + _sapp_win32_init_dpi(); + _sapp_win32_init_cursors(); + _sapp_win32_create_window(); + sapp_set_icon(&desc->icon); + #if defined(SOKOL_D3D11) + _sapp_d3d11_create_device_and_swapchain(); + _sapp_d3d11_create_default_render_target(); + #endif + #if defined(SOKOL_GLCORE) + _sapp_wgl_init(); + _sapp_wgl_load_extensions(); + _sapp_wgl_create_context(); + #endif + _sapp.valid = true; + + bool done = false; + while (!(done || _sapp.quit_ordered)) { + _sapp_win32_timing_measure(); + MSG msg; + while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { + if (WM_QUIT == msg.message) { + done = true; + continue; + } + else { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + _sapp_frame(); + #if defined(SOKOL_D3D11) + _sapp_d3d11_present(false); + if (IsIconic(_sapp.win32.hwnd)) { + Sleep((DWORD)(16 * _sapp.swap_interval)); + } + #endif + #if defined(SOKOL_GLCORE) + _sapp_wgl_swap_buffers(); + #endif + /* check for window resized, this cannot happen in WM_SIZE as it explodes memory usage */ + if (_sapp_win32_update_dimensions()) { + #if defined(SOKOL_D3D11) + _sapp_d3d11_resize_default_render_target(); + #endif + _sapp_win32_app_event(SAPP_EVENTTYPE_RESIZED); + } + /* check if the window monitor has changed, need to reset timing because + the new monitor might have a different refresh rate + */ + if (_sapp_win32_update_monitor()) { + _sapp_timing_reset(&_sapp.timing); + } + if (_sapp.quit_requested) { + PostMessage(_sapp.win32.hwnd, WM_CLOSE, 0, 0); + } + } + _sapp_call_cleanup(); + + #if defined(SOKOL_D3D11) + _sapp_d3d11_destroy_default_render_target(); + _sapp_d3d11_destroy_device_and_swapchain(); + #else + _sapp_wgl_destroy_context(); + _sapp_wgl_shutdown(); + #endif + _sapp_win32_destroy_window(); + _sapp_win32_destroy_icons(); + _sapp_win32_restore_console(); + _sapp_discard_state(); +} + +_SOKOL_PRIVATE char** _sapp_win32_command_line_to_utf8_argv(LPWSTR w_command_line, int* o_argc) { + int argc = 0; + char** argv = 0; + char* args; + + LPWSTR* w_argv = CommandLineToArgvW(w_command_line, &argc); + if (w_argv == NULL) { + // FIXME: chicken egg problem, can't report errors before sokol_main() is called! + } else { + size_t size = wcslen(w_command_line) * 4; + argv = (char**) _sapp_malloc_clear(((size_t)argc + 1) * sizeof(char*) + size); + SOKOL_ASSERT(argv); + args = (char*) &argv[argc + 1]; + int n; + for (int i = 0; i < argc; ++i) { + n = WideCharToMultiByte(CP_UTF8, 0, w_argv[i], -1, args, (int)size, NULL, NULL); + if (n == 0) { + // FIXME: chicken egg problem, can't report errors before sokol_main() is called! + break; + } + argv[i] = args; + size -= (size_t)n; + args += n; + } + LocalFree(w_argv); + } + *o_argc = argc; + return argv; +} + +#if !defined(SOKOL_NO_ENTRY) +#if defined(SOKOL_WIN32_FORCE_MAIN) +int main(int argc, char* argv[]) { + sapp_desc desc = sokol_main(argc, argv); + _sapp_win32_run(&desc); + return 0; +} +#else +int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { + _SOKOL_UNUSED(hInstance); + _SOKOL_UNUSED(hPrevInstance); + _SOKOL_UNUSED(lpCmdLine); + _SOKOL_UNUSED(nCmdShow); + int argc_utf8 = 0; + char** argv_utf8 = _sapp_win32_command_line_to_utf8_argv(GetCommandLineW(), &argc_utf8); + sapp_desc desc = sokol_main(argc_utf8, argv_utf8); + _sapp_win32_run(&desc); + _sapp_free(argv_utf8); + return 0; +} +#endif /* SOKOL_WIN32_FORCE_MAIN */ +#endif /* SOKOL_NO_ENTRY */ + +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#endif /* _SAPP_WIN32 */ + +// █████ ███ ██ ██████ ██████ ██████ ██ ██████ +// ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██ ██ ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ████ ██████ ██ ██ ██████ ██ ██████ +// +// >>android +#if defined(_SAPP_ANDROID) + +/* android loop thread */ +_SOKOL_PRIVATE bool _sapp_android_init_egl(void) { + SOKOL_ASSERT(_sapp.android.display == EGL_NO_DISPLAY); + SOKOL_ASSERT(_sapp.android.context == EGL_NO_CONTEXT); + + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (display == EGL_NO_DISPLAY) { + return false; + } + if (eglInitialize(display, NULL, NULL) == EGL_FALSE) { + return false; + } + EGLint alpha_size = _sapp.desc.alpha ? 8 : 0; + const EGLint cfg_attributes[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, alpha_size, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, 0, + EGL_NONE, + }; + EGLConfig available_cfgs[32]; + EGLint cfg_count; + eglChooseConfig(display, cfg_attributes, available_cfgs, 32, &cfg_count); + SOKOL_ASSERT(cfg_count > 0); + SOKOL_ASSERT(cfg_count <= 32); + + /* find config with 8-bit rgb buffer if available, ndk sample does not trust egl spec */ + EGLConfig config; + bool exact_cfg_found = false; + for (int i = 0; i < cfg_count; ++i) { + EGLConfig c = available_cfgs[i]; + EGLint r, g, b, a, d; + if (eglGetConfigAttrib(display, c, EGL_RED_SIZE, &r) == EGL_TRUE && + eglGetConfigAttrib(display, c, EGL_GREEN_SIZE, &g) == EGL_TRUE && + eglGetConfigAttrib(display, c, EGL_BLUE_SIZE, &b) == EGL_TRUE && + eglGetConfigAttrib(display, c, EGL_ALPHA_SIZE, &a) == EGL_TRUE && + eglGetConfigAttrib(display, c, EGL_DEPTH_SIZE, &d) == EGL_TRUE && + r == 8 && g == 8 && b == 8 && (alpha_size == 0 || a == alpha_size) && d == 16) { + exact_cfg_found = true; + config = c; + break; + } + } + if (!exact_cfg_found) { + config = available_cfgs[0]; + } + + EGLint ctx_attributes[] = { + EGL_CONTEXT_CLIENT_VERSION, 3, + EGL_NONE, + }; + EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctx_attributes); + if (context == EGL_NO_CONTEXT) { + return false; + } + + _sapp.android.config = config; + _sapp.android.display = display; + _sapp.android.context = context; + return true; +} + +_SOKOL_PRIVATE void _sapp_android_cleanup_egl(void) { + if (_sapp.android.display != EGL_NO_DISPLAY) { + eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (_sapp.android.surface != EGL_NO_SURFACE) { + eglDestroySurface(_sapp.android.display, _sapp.android.surface); + _sapp.android.surface = EGL_NO_SURFACE; + } + if (_sapp.android.context != EGL_NO_CONTEXT) { + eglDestroyContext(_sapp.android.display, _sapp.android.context); + _sapp.android.context = EGL_NO_CONTEXT; + } + eglTerminate(_sapp.android.display); + _sapp.android.display = EGL_NO_DISPLAY; + } +} + +_SOKOL_PRIVATE bool _sapp_android_init_egl_surface(ANativeWindow* window) { + SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY); + SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT); + SOKOL_ASSERT(_sapp.android.surface == EGL_NO_SURFACE); + SOKOL_ASSERT(window); + + /* TODO: set window flags */ + /* ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_KEEP_SCREEN_ON, 0); */ + + /* create egl surface and make it current */ + EGLSurface surface = eglCreateWindowSurface(_sapp.android.display, _sapp.android.config, window, NULL); + if (surface == EGL_NO_SURFACE) { + return false; + } + if (eglMakeCurrent(_sapp.android.display, surface, surface, _sapp.android.context) == EGL_FALSE) { + return false; + } + _sapp.android.surface = surface; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer); + return true; +} + +_SOKOL_PRIVATE void _sapp_android_cleanup_egl_surface(void) { + if (_sapp.android.display == EGL_NO_DISPLAY) { + return; + } + eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (_sapp.android.surface != EGL_NO_SURFACE) { + eglDestroySurface(_sapp.android.display, _sapp.android.surface); + _sapp.android.surface = EGL_NO_SURFACE; + } +} + +_SOKOL_PRIVATE void _sapp_android_app_event(sapp_event_type type) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool force_update) { + SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY); + SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT); + SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE); + SOKOL_ASSERT(window); + + const int32_t win_w = ANativeWindow_getWidth(window); + const int32_t win_h = ANativeWindow_getHeight(window); + SOKOL_ASSERT(win_w >= 0 && win_h >= 0); + const bool win_changed = (win_w != _sapp.window_width) || (win_h != _sapp.window_height); + _sapp.window_width = win_w; + _sapp.window_height = win_h; + if (win_changed || force_update) { + if (!_sapp.desc.high_dpi) { + const int32_t buf_w = win_w / 2; + const int32_t buf_h = win_h / 2; + EGLint format; + EGLBoolean egl_result = eglGetConfigAttrib(_sapp.android.display, _sapp.android.config, EGL_NATIVE_VISUAL_ID, &format); + SOKOL_ASSERT(egl_result == EGL_TRUE); _SOKOL_UNUSED(egl_result); + /* NOTE: calling ANativeWindow_setBuffersGeometry() with the same dimensions + as the ANativeWindow size results in weird display artefacts, that's + why it's only called when the buffer geometry is different from + the window size + */ + int32_t result = ANativeWindow_setBuffersGeometry(window, buf_w, buf_h, format); + SOKOL_ASSERT(result == 0); _SOKOL_UNUSED(result); + } + } + + /* query surface size */ + EGLint fb_w, fb_h; + EGLBoolean egl_result_w = eglQuerySurface(_sapp.android.display, _sapp.android.surface, EGL_WIDTH, &fb_w); + EGLBoolean egl_result_h = eglQuerySurface(_sapp.android.display, _sapp.android.surface, EGL_HEIGHT, &fb_h); + SOKOL_ASSERT(egl_result_w == EGL_TRUE); _SOKOL_UNUSED(egl_result_w); + SOKOL_ASSERT(egl_result_h == EGL_TRUE); _SOKOL_UNUSED(egl_result_h); + const bool fb_changed = (fb_w != _sapp.framebuffer_width) || (fb_h != _sapp.framebuffer_height); + _sapp.framebuffer_width = fb_w; + _sapp.framebuffer_height = fb_h; + _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width; + if (win_changed || fb_changed || force_update) { + if (!_sapp.first_frame) { + _sapp_android_app_event(SAPP_EVENTTYPE_RESIZED); + } + } +} + +_SOKOL_PRIVATE void _sapp_android_cleanup(void) { + if (_sapp.android.surface != EGL_NO_SURFACE) { + /* egl context is bound, cleanup gracefully */ + if (_sapp.init_called && !_sapp.cleanup_called) { + _sapp_call_cleanup(); + } + } + /* always try to cleanup by destroying egl context */ + _sapp_android_cleanup_egl(); +} + +_SOKOL_PRIVATE void _sapp_android_shutdown(void) { + /* try to cleanup while we still have a surface and can call cleanup_cb() */ + _sapp_android_cleanup(); + /* request exit */ + ANativeActivity_finish(_sapp.android.activity); +} + +_SOKOL_PRIVATE void _sapp_android_frame(void) { + SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY); + SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT); + SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE); + _sapp_timing_measure(&_sapp.timing); + _sapp_android_update_dimensions(_sapp.android.current.window, false); + _sapp_frame(); + eglSwapBuffers(_sapp.android.display, _sapp.android.surface); +} + +_SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) { + if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_MOTION) { + return false; + } + if (!_sapp_events_enabled()) { + return false; + } + int32_t action_idx = AMotionEvent_getAction(e); + int32_t action = action_idx & AMOTION_EVENT_ACTION_MASK; + sapp_event_type type = SAPP_EVENTTYPE_INVALID; + switch (action) { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_POINTER_DOWN: + type = SAPP_EVENTTYPE_TOUCHES_BEGAN; + break; + case AMOTION_EVENT_ACTION_MOVE: + type = SAPP_EVENTTYPE_TOUCHES_MOVED; + break; + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_POINTER_UP: + type = SAPP_EVENTTYPE_TOUCHES_ENDED; + break; + case AMOTION_EVENT_ACTION_CANCEL: + type = SAPP_EVENTTYPE_TOUCHES_CANCELLED; + break; + default: + break; + } + if (type == SAPP_EVENTTYPE_INVALID) { + return false; + } + int32_t idx = action_idx >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + _sapp_init_event(type); + _sapp.event.num_touches = (int)AMotionEvent_getPointerCount(e); + if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) { + _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS; + } + for (int32_t i = 0; i < _sapp.event.num_touches; i++) { + sapp_touchpoint* dst = &_sapp.event.touches[i]; + dst->identifier = (uintptr_t)AMotionEvent_getPointerId(e, (size_t)i); + dst->pos_x = (AMotionEvent_getX(e, (size_t)i) / _sapp.window_width) * _sapp.framebuffer_width; + dst->pos_y = (AMotionEvent_getY(e, (size_t)i) / _sapp.window_height) * _sapp.framebuffer_height; + dst->android_tooltype = (sapp_android_tooltype) AMotionEvent_getToolType(e, (size_t)i); + if (action == AMOTION_EVENT_ACTION_POINTER_DOWN || + action == AMOTION_EVENT_ACTION_POINTER_UP) { + dst->changed = (i == idx); + } else { + dst->changed = true; + } + } + _sapp_call_event(&_sapp.event); + return true; +} + +_SOKOL_PRIVATE bool _sapp_android_key_event(const AInputEvent* e) { + if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_KEY) { + return false; + } + if (AKeyEvent_getKeyCode(e) == AKEYCODE_BACK) { + /* FIXME: this should be hooked into a "really quit?" mechanism + so the app can ask the user for confirmation, this is currently + generally missing in sokol_app.h + */ + _sapp_android_shutdown(); + return true; + } + return false; +} + +_SOKOL_PRIVATE int _sapp_android_input_cb(int fd, int events, void* data) { + _SOKOL_UNUSED(fd); + _SOKOL_UNUSED(data); + if ((events & ALOOPER_EVENT_INPUT) == 0) { + _SAPP_ERROR(ANDROID_UNSUPPORTED_INPUT_EVENT_INPUT_CB); + return 1; + } + SOKOL_ASSERT(_sapp.android.current.input); + AInputEvent* event = NULL; + while (AInputQueue_getEvent(_sapp.android.current.input, &event) >= 0) { + if (AInputQueue_preDispatchEvent(_sapp.android.current.input, event) != 0) { + continue; + } + int32_t handled = 0; + if (_sapp_android_touch_event(event) || _sapp_android_key_event(event)) { + handled = 1; + } + AInputQueue_finishEvent(_sapp.android.current.input, event, handled); + } + return 1; +} + +_SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { + _SOKOL_UNUSED(data); + if ((events & ALOOPER_EVENT_INPUT) == 0) { + _SAPP_ERROR(ANDROID_UNSUPPORTED_INPUT_EVENT_MAIN_CB); + return 1; + } + + _sapp_android_msg_t msg; + if (read(fd, &msg, sizeof(msg)) != sizeof(msg)) { + _SAPP_ERROR(ANDROID_READ_MSG_FAILED); + return 1; + } + + pthread_mutex_lock(&_sapp.android.pt.mutex); + switch (msg) { + case _SOKOL_ANDROID_MSG_CREATE: + { + _SAPP_INFO(ANDROID_MSG_CREATE); + SOKOL_ASSERT(!_sapp.valid); + bool result = _sapp_android_init_egl(); + SOKOL_ASSERT(result); _SOKOL_UNUSED(result); + _sapp.valid = true; + _sapp.android.has_created = true; + } + break; + case _SOKOL_ANDROID_MSG_RESUME: + _SAPP_INFO(ANDROID_MSG_RESUME); + _sapp.android.has_resumed = true; + _sapp_android_app_event(SAPP_EVENTTYPE_RESUMED); + break; + case _SOKOL_ANDROID_MSG_PAUSE: + _SAPP_INFO(ANDROID_MSG_PAUSE); + _sapp.android.has_resumed = false; + _sapp_android_app_event(SAPP_EVENTTYPE_SUSPENDED); + break; + case _SOKOL_ANDROID_MSG_FOCUS: + _SAPP_INFO(ANDROID_MSG_FOCUS); + _sapp.android.has_focus = true; + break; + case _SOKOL_ANDROID_MSG_NO_FOCUS: + _SAPP_INFO(ANDROID_MSG_NO_FOCUS); + _sapp.android.has_focus = false; + break; + case _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW: + _SAPP_INFO(ANDROID_MSG_SET_NATIVE_WINDOW); + if (_sapp.android.current.window != _sapp.android.pending.window) { + if (_sapp.android.current.window != NULL) { + _sapp_android_cleanup_egl_surface(); + } + if (_sapp.android.pending.window != NULL) { + if (_sapp_android_init_egl_surface(_sapp.android.pending.window)) { + _sapp_android_update_dimensions(_sapp.android.pending.window, true); + } else { + _sapp_android_shutdown(); + } + } + } + _sapp.android.current.window = _sapp.android.pending.window; + break; + case _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE: + _SAPP_INFO(ANDROID_MSG_SET_INPUT_QUEUE); + if (_sapp.android.current.input != _sapp.android.pending.input) { + if (_sapp.android.current.input != NULL) { + AInputQueue_detachLooper(_sapp.android.current.input); + } + if (_sapp.android.pending.input != NULL) { + AInputQueue_attachLooper( + _sapp.android.pending.input, + _sapp.android.looper, + ALOOPER_POLL_CALLBACK, + _sapp_android_input_cb, + NULL); /* data */ + } + } + _sapp.android.current.input = _sapp.android.pending.input; + break; + case _SOKOL_ANDROID_MSG_DESTROY: + _SAPP_INFO(ANDROID_MSG_DESTROY); + _sapp_android_cleanup(); + _sapp.valid = false; + _sapp.android.is_thread_stopping = true; + break; + default: + _SAPP_WARN(ANDROID_UNKNOWN_MSG); + break; + } + pthread_cond_broadcast(&_sapp.android.pt.cond); /* signal "received" */ + pthread_mutex_unlock(&_sapp.android.pt.mutex); + return 1; +} + +_SOKOL_PRIVATE bool _sapp_android_should_update(void) { + bool is_in_front = _sapp.android.has_resumed && _sapp.android.has_focus; + bool has_surface = _sapp.android.surface != EGL_NO_SURFACE; + return is_in_front && has_surface; +} + +_SOKOL_PRIVATE void _sapp_android_show_keyboard(bool shown) { + SOKOL_ASSERT(_sapp.valid); + /* This seems to be broken in the NDK, but there is (a very cumbersome) workaround... */ + if (shown) { + ANativeActivity_showSoftInput(_sapp.android.activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED); + } else { + ANativeActivity_hideSoftInput(_sapp.android.activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS); + } +} + +_SOKOL_PRIVATE void* _sapp_android_loop(void* arg) { + _SOKOL_UNUSED(arg); + _SAPP_INFO(ANDROID_LOOP_THREAD_STARTED); + + _sapp.android.looper = ALooper_prepare(0 /* or ALOOPER_PREPARE_ALLOW_NON_CALLBACKS*/); + ALooper_addFd(_sapp.android.looper, + _sapp.android.pt.read_from_main_fd, + ALOOPER_POLL_CALLBACK, + ALOOPER_EVENT_INPUT, + _sapp_android_main_cb, + NULL); /* data */ + + /* signal start to main thread */ + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp.android.is_thread_started = true; + pthread_cond_broadcast(&_sapp.android.pt.cond); + pthread_mutex_unlock(&_sapp.android.pt.mutex); + + /* main loop */ + while (!_sapp.android.is_thread_stopping) { + /* sokol frame */ + if (_sapp_android_should_update()) { + _sapp_android_frame(); + } + + /* process all events (or stop early if app is requested to quit) */ + bool process_events = true; + while (process_events && !_sapp.android.is_thread_stopping) { + bool block_until_event = !_sapp.android.is_thread_stopping && !_sapp_android_should_update(); + process_events = ALooper_pollOnce(block_until_event ? -1 : 0, NULL, NULL, NULL) == ALOOPER_POLL_CALLBACK; + } + } + + /* cleanup thread */ + if (_sapp.android.current.input != NULL) { + AInputQueue_detachLooper(_sapp.android.current.input); + } + + /* the following causes heap corruption on exit, why?? + ALooper_removeFd(_sapp.android.looper, _sapp.android.pt.read_from_main_fd); + ALooper_release(_sapp.android.looper);*/ + + /* signal "destroyed" */ + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp.android.is_thread_stopped = true; + pthread_cond_broadcast(&_sapp.android.pt.cond); + pthread_mutex_unlock(&_sapp.android.pt.mutex); + + _SAPP_INFO(ANDROID_LOOP_THREAD_DONE); + return NULL; +} + +/* android main/ui thread */ +_SOKOL_PRIVATE void _sapp_android_msg(_sapp_android_msg_t msg) { + if (write(_sapp.android.pt.write_from_main_fd, &msg, sizeof(msg)) != sizeof(msg)) { + _SAPP_ERROR(ANDROID_WRITE_MSG_FAILED); + } +} + +_SOKOL_PRIVATE void _sapp_android_on_start(ANativeActivity* activity) { + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONSTART); +} + +_SOKOL_PRIVATE void _sapp_android_on_resume(ANativeActivity* activity) { + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONRESUME); + _sapp_android_msg(_SOKOL_ANDROID_MSG_RESUME); +} + +_SOKOL_PRIVATE void* _sapp_android_on_save_instance_state(ANativeActivity* activity, size_t* out_size) { + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONSAVEINSTANCESTATE); + *out_size = 0; + return NULL; +} + +_SOKOL_PRIVATE void _sapp_android_on_window_focus_changed(ANativeActivity* activity, int has_focus) { + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONWINDOWFOCUSCHANGED); + if (has_focus) { + _sapp_android_msg(_SOKOL_ANDROID_MSG_FOCUS); + } else { + _sapp_android_msg(_SOKOL_ANDROID_MSG_NO_FOCUS); + } +} + +_SOKOL_PRIVATE void _sapp_android_on_pause(ANativeActivity* activity) { + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONPAUSE); + _sapp_android_msg(_SOKOL_ANDROID_MSG_PAUSE); +} + +_SOKOL_PRIVATE void _sapp_android_on_stop(ANativeActivity* activity) { + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONSTOP); +} + +_SOKOL_PRIVATE void _sapp_android_msg_set_native_window(ANativeWindow* window) { + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp.android.pending.window = window; + _sapp_android_msg(_SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW); + while (_sapp.android.current.window != window) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); + } + pthread_mutex_unlock(&_sapp.android.pt.mutex); +} + +_SOKOL_PRIVATE void _sapp_android_on_native_window_created(ANativeActivity* activity, ANativeWindow* window) { + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWCREATED); + _sapp_android_msg_set_native_window(window); +} + +_SOKOL_PRIVATE void _sapp_android_on_native_window_destroyed(ANativeActivity* activity, ANativeWindow* window) { + _SOKOL_UNUSED(activity); + _SOKOL_UNUSED(window); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWDESTROYED); + _sapp_android_msg_set_native_window(NULL); +} + +_SOKOL_PRIVATE void _sapp_android_msg_set_input_queue(AInputQueue* input) { + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp.android.pending.input = input; + _sapp_android_msg(_SOKOL_ANDROID_MSG_SET_INPUT_QUEUE); + while (_sapp.android.current.input != input) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); + } + pthread_mutex_unlock(&_sapp.android.pt.mutex); +} + +_SOKOL_PRIVATE void _sapp_android_on_input_queue_created(ANativeActivity* activity, AInputQueue* queue) { + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUECREATED); + _sapp_android_msg_set_input_queue(queue); +} + +_SOKOL_PRIVATE void _sapp_android_on_input_queue_destroyed(ANativeActivity* activity, AInputQueue* queue) { + _SOKOL_UNUSED(activity); + _SOKOL_UNUSED(queue); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUEDESTROYED); + _sapp_android_msg_set_input_queue(NULL); +} + +_SOKOL_PRIVATE void _sapp_android_on_config_changed(ANativeActivity* activity) { + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONCONFIGURATIONCHANGED); + /* see android:configChanges in manifest */ +} + +_SOKOL_PRIVATE void _sapp_android_on_low_memory(ANativeActivity* activity) { + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONLOWMEMORY); +} + +_SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) { + /* + * For some reason even an empty app using nativeactivity.h will crash (WIN DEATH) + * on my device (Moto X 2nd gen) when the app is removed from the task view + * (TaskStackView: onTaskViewDismissed). + * + * However, if ANativeActivity_finish() is explicitly called from for example + * _sapp_android_on_stop(), the crash disappears. Is this a bug in NativeActivity? + */ + _SOKOL_UNUSED(activity); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONDESTROY); + + /* send destroy msg */ + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp_android_msg(_SOKOL_ANDROID_MSG_DESTROY); + while (!_sapp.android.is_thread_stopped) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); + } + pthread_mutex_unlock(&_sapp.android.pt.mutex); + + /* clean up main thread */ + pthread_cond_destroy(&_sapp.android.pt.cond); + pthread_mutex_destroy(&_sapp.android.pt.mutex); + + close(_sapp.android.pt.read_from_main_fd); + close(_sapp.android.pt.write_from_main_fd); + + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_DONE); + + /* this is a bit naughty, but causes a clean restart of the app (static globals are reset) */ + exit(0); +} + +JNIEXPORT +void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size_t saved_state_size) { + _SOKOL_UNUSED(saved_state); + _SOKOL_UNUSED(saved_state_size); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONCREATE); + + // the NativeActity pointer needs to be available inside sokol_main() + // (see https://github.com/floooh/sokol/issues/708), however _sapp_init_state() + // will clear the global _sapp_t struct, so we need to initialize the native + // activity pointer twice, once before sokol_main() and once after _sapp_init_state() + _sapp_clear(&_sapp, sizeof(_sapp)); + _sapp.android.activity = activity; + sapp_desc desc = sokol_main(0, NULL); + _sapp_init_state(&desc); + _sapp.android.activity = activity; + + int pipe_fd[2]; + if (pipe(pipe_fd) != 0) { + _SAPP_ERROR(ANDROID_CREATE_THREAD_PIPE_FAILED); + return; + } + _sapp.android.pt.read_from_main_fd = pipe_fd[0]; + _sapp.android.pt.write_from_main_fd = pipe_fd[1]; + + pthread_mutex_init(&_sapp.android.pt.mutex, NULL); + pthread_cond_init(&_sapp.android.pt.cond, NULL); + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&_sapp.android.pt.thread, &attr, _sapp_android_loop, 0); + pthread_attr_destroy(&attr); + + /* wait until main loop has started */ + pthread_mutex_lock(&_sapp.android.pt.mutex); + while (!_sapp.android.is_thread_started) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); + } + pthread_mutex_unlock(&_sapp.android.pt.mutex); + + /* send create msg */ + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp_android_msg(_SOKOL_ANDROID_MSG_CREATE); + while (!_sapp.android.has_created) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); + } + pthread_mutex_unlock(&_sapp.android.pt.mutex); + + /* register for callbacks */ + activity->callbacks->onStart = _sapp_android_on_start; + activity->callbacks->onResume = _sapp_android_on_resume; + activity->callbacks->onSaveInstanceState = _sapp_android_on_save_instance_state; + activity->callbacks->onWindowFocusChanged = _sapp_android_on_window_focus_changed; + activity->callbacks->onPause = _sapp_android_on_pause; + activity->callbacks->onStop = _sapp_android_on_stop; + activity->callbacks->onDestroy = _sapp_android_on_destroy; + activity->callbacks->onNativeWindowCreated = _sapp_android_on_native_window_created; + /* activity->callbacks->onNativeWindowResized = _sapp_android_on_native_window_resized; */ + /* activity->callbacks->onNativeWindowRedrawNeeded = _sapp_android_on_native_window_redraw_needed; */ + activity->callbacks->onNativeWindowDestroyed = _sapp_android_on_native_window_destroyed; + activity->callbacks->onInputQueueCreated = _sapp_android_on_input_queue_created; + activity->callbacks->onInputQueueDestroyed = _sapp_android_on_input_queue_destroyed; + /* activity->callbacks->onContentRectChanged = _sapp_android_on_content_rect_changed; */ + activity->callbacks->onConfigurationChanged = _sapp_android_on_config_changed; + activity->callbacks->onLowMemory = _sapp_android_on_low_memory; + + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS); + + /* NOT A BUG: do NOT call sapp_discard_state() */ +} + +#endif /* _SAPP_ANDROID */ + +// ██ ██ ███ ██ ██ ██ ██ ██ +// ██ ██ ████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ████ ██████ ██ ██ +// +// >>linux +#if defined(_SAPP_LINUX) + +/* see GLFW's xkb_unicode.c */ +static const struct _sapp_x11_codepair { + uint16_t keysym; + uint16_t ucs; +} _sapp_x11_keysymtab[] = { + { 0x01a1, 0x0104 }, + { 0x01a2, 0x02d8 }, + { 0x01a3, 0x0141 }, + { 0x01a5, 0x013d }, + { 0x01a6, 0x015a }, + { 0x01a9, 0x0160 }, + { 0x01aa, 0x015e }, + { 0x01ab, 0x0164 }, + { 0x01ac, 0x0179 }, + { 0x01ae, 0x017d }, + { 0x01af, 0x017b }, + { 0x01b1, 0x0105 }, + { 0x01b2, 0x02db }, + { 0x01b3, 0x0142 }, + { 0x01b5, 0x013e }, + { 0x01b6, 0x015b }, + { 0x01b7, 0x02c7 }, + { 0x01b9, 0x0161 }, + { 0x01ba, 0x015f }, + { 0x01bb, 0x0165 }, + { 0x01bc, 0x017a }, + { 0x01bd, 0x02dd }, + { 0x01be, 0x017e }, + { 0x01bf, 0x017c }, + { 0x01c0, 0x0154 }, + { 0x01c3, 0x0102 }, + { 0x01c5, 0x0139 }, + { 0x01c6, 0x0106 }, + { 0x01c8, 0x010c }, + { 0x01ca, 0x0118 }, + { 0x01cc, 0x011a }, + { 0x01cf, 0x010e }, + { 0x01d0, 0x0110 }, + { 0x01d1, 0x0143 }, + { 0x01d2, 0x0147 }, + { 0x01d5, 0x0150 }, + { 0x01d8, 0x0158 }, + { 0x01d9, 0x016e }, + { 0x01db, 0x0170 }, + { 0x01de, 0x0162 }, + { 0x01e0, 0x0155 }, + { 0x01e3, 0x0103 }, + { 0x01e5, 0x013a }, + { 0x01e6, 0x0107 }, + { 0x01e8, 0x010d }, + { 0x01ea, 0x0119 }, + { 0x01ec, 0x011b }, + { 0x01ef, 0x010f }, + { 0x01f0, 0x0111 }, + { 0x01f1, 0x0144 }, + { 0x01f2, 0x0148 }, + { 0x01f5, 0x0151 }, + { 0x01f8, 0x0159 }, + { 0x01f9, 0x016f }, + { 0x01fb, 0x0171 }, + { 0x01fe, 0x0163 }, + { 0x01ff, 0x02d9 }, + { 0x02a1, 0x0126 }, + { 0x02a6, 0x0124 }, + { 0x02a9, 0x0130 }, + { 0x02ab, 0x011e }, + { 0x02ac, 0x0134 }, + { 0x02b1, 0x0127 }, + { 0x02b6, 0x0125 }, + { 0x02b9, 0x0131 }, + { 0x02bb, 0x011f }, + { 0x02bc, 0x0135 }, + { 0x02c5, 0x010a }, + { 0x02c6, 0x0108 }, + { 0x02d5, 0x0120 }, + { 0x02d8, 0x011c }, + { 0x02dd, 0x016c }, + { 0x02de, 0x015c }, + { 0x02e5, 0x010b }, + { 0x02e6, 0x0109 }, + { 0x02f5, 0x0121 }, + { 0x02f8, 0x011d }, + { 0x02fd, 0x016d }, + { 0x02fe, 0x015d }, + { 0x03a2, 0x0138 }, + { 0x03a3, 0x0156 }, + { 0x03a5, 0x0128 }, + { 0x03a6, 0x013b }, + { 0x03aa, 0x0112 }, + { 0x03ab, 0x0122 }, + { 0x03ac, 0x0166 }, + { 0x03b3, 0x0157 }, + { 0x03b5, 0x0129 }, + { 0x03b6, 0x013c }, + { 0x03ba, 0x0113 }, + { 0x03bb, 0x0123 }, + { 0x03bc, 0x0167 }, + { 0x03bd, 0x014a }, + { 0x03bf, 0x014b }, + { 0x03c0, 0x0100 }, + { 0x03c7, 0x012e }, + { 0x03cc, 0x0116 }, + { 0x03cf, 0x012a }, + { 0x03d1, 0x0145 }, + { 0x03d2, 0x014c }, + { 0x03d3, 0x0136 }, + { 0x03d9, 0x0172 }, + { 0x03dd, 0x0168 }, + { 0x03de, 0x016a }, + { 0x03e0, 0x0101 }, + { 0x03e7, 0x012f }, + { 0x03ec, 0x0117 }, + { 0x03ef, 0x012b }, + { 0x03f1, 0x0146 }, + { 0x03f2, 0x014d }, + { 0x03f3, 0x0137 }, + { 0x03f9, 0x0173 }, + { 0x03fd, 0x0169 }, + { 0x03fe, 0x016b }, + { 0x047e, 0x203e }, + { 0x04a1, 0x3002 }, + { 0x04a2, 0x300c }, + { 0x04a3, 0x300d }, + { 0x04a4, 0x3001 }, + { 0x04a5, 0x30fb }, + { 0x04a6, 0x30f2 }, + { 0x04a7, 0x30a1 }, + { 0x04a8, 0x30a3 }, + { 0x04a9, 0x30a5 }, + { 0x04aa, 0x30a7 }, + { 0x04ab, 0x30a9 }, + { 0x04ac, 0x30e3 }, + { 0x04ad, 0x30e5 }, + { 0x04ae, 0x30e7 }, + { 0x04af, 0x30c3 }, + { 0x04b0, 0x30fc }, + { 0x04b1, 0x30a2 }, + { 0x04b2, 0x30a4 }, + { 0x04b3, 0x30a6 }, + { 0x04b4, 0x30a8 }, + { 0x04b5, 0x30aa }, + { 0x04b6, 0x30ab }, + { 0x04b7, 0x30ad }, + { 0x04b8, 0x30af }, + { 0x04b9, 0x30b1 }, + { 0x04ba, 0x30b3 }, + { 0x04bb, 0x30b5 }, + { 0x04bc, 0x30b7 }, + { 0x04bd, 0x30b9 }, + { 0x04be, 0x30bb }, + { 0x04bf, 0x30bd }, + { 0x04c0, 0x30bf }, + { 0x04c1, 0x30c1 }, + { 0x04c2, 0x30c4 }, + { 0x04c3, 0x30c6 }, + { 0x04c4, 0x30c8 }, + { 0x04c5, 0x30ca }, + { 0x04c6, 0x30cb }, + { 0x04c7, 0x30cc }, + { 0x04c8, 0x30cd }, + { 0x04c9, 0x30ce }, + { 0x04ca, 0x30cf }, + { 0x04cb, 0x30d2 }, + { 0x04cc, 0x30d5 }, + { 0x04cd, 0x30d8 }, + { 0x04ce, 0x30db }, + { 0x04cf, 0x30de }, + { 0x04d0, 0x30df }, + { 0x04d1, 0x30e0 }, + { 0x04d2, 0x30e1 }, + { 0x04d3, 0x30e2 }, + { 0x04d4, 0x30e4 }, + { 0x04d5, 0x30e6 }, + { 0x04d6, 0x30e8 }, + { 0x04d7, 0x30e9 }, + { 0x04d8, 0x30ea }, + { 0x04d9, 0x30eb }, + { 0x04da, 0x30ec }, + { 0x04db, 0x30ed }, + { 0x04dc, 0x30ef }, + { 0x04dd, 0x30f3 }, + { 0x04de, 0x309b }, + { 0x04df, 0x309c }, + { 0x05ac, 0x060c }, + { 0x05bb, 0x061b }, + { 0x05bf, 0x061f }, + { 0x05c1, 0x0621 }, + { 0x05c2, 0x0622 }, + { 0x05c3, 0x0623 }, + { 0x05c4, 0x0624 }, + { 0x05c5, 0x0625 }, + { 0x05c6, 0x0626 }, + { 0x05c7, 0x0627 }, + { 0x05c8, 0x0628 }, + { 0x05c9, 0x0629 }, + { 0x05ca, 0x062a }, + { 0x05cb, 0x062b }, + { 0x05cc, 0x062c }, + { 0x05cd, 0x062d }, + { 0x05ce, 0x062e }, + { 0x05cf, 0x062f }, + { 0x05d0, 0x0630 }, + { 0x05d1, 0x0631 }, + { 0x05d2, 0x0632 }, + { 0x05d3, 0x0633 }, + { 0x05d4, 0x0634 }, + { 0x05d5, 0x0635 }, + { 0x05d6, 0x0636 }, + { 0x05d7, 0x0637 }, + { 0x05d8, 0x0638 }, + { 0x05d9, 0x0639 }, + { 0x05da, 0x063a }, + { 0x05e0, 0x0640 }, + { 0x05e1, 0x0641 }, + { 0x05e2, 0x0642 }, + { 0x05e3, 0x0643 }, + { 0x05e4, 0x0644 }, + { 0x05e5, 0x0645 }, + { 0x05e6, 0x0646 }, + { 0x05e7, 0x0647 }, + { 0x05e8, 0x0648 }, + { 0x05e9, 0x0649 }, + { 0x05ea, 0x064a }, + { 0x05eb, 0x064b }, + { 0x05ec, 0x064c }, + { 0x05ed, 0x064d }, + { 0x05ee, 0x064e }, + { 0x05ef, 0x064f }, + { 0x05f0, 0x0650 }, + { 0x05f1, 0x0651 }, + { 0x05f2, 0x0652 }, + { 0x06a1, 0x0452 }, + { 0x06a2, 0x0453 }, + { 0x06a3, 0x0451 }, + { 0x06a4, 0x0454 }, + { 0x06a5, 0x0455 }, + { 0x06a6, 0x0456 }, + { 0x06a7, 0x0457 }, + { 0x06a8, 0x0458 }, + { 0x06a9, 0x0459 }, + { 0x06aa, 0x045a }, + { 0x06ab, 0x045b }, + { 0x06ac, 0x045c }, + { 0x06ae, 0x045e }, + { 0x06af, 0x045f }, + { 0x06b0, 0x2116 }, + { 0x06b1, 0x0402 }, + { 0x06b2, 0x0403 }, + { 0x06b3, 0x0401 }, + { 0x06b4, 0x0404 }, + { 0x06b5, 0x0405 }, + { 0x06b6, 0x0406 }, + { 0x06b7, 0x0407 }, + { 0x06b8, 0x0408 }, + { 0x06b9, 0x0409 }, + { 0x06ba, 0x040a }, + { 0x06bb, 0x040b }, + { 0x06bc, 0x040c }, + { 0x06be, 0x040e }, + { 0x06bf, 0x040f }, + { 0x06c0, 0x044e }, + { 0x06c1, 0x0430 }, + { 0x06c2, 0x0431 }, + { 0x06c3, 0x0446 }, + { 0x06c4, 0x0434 }, + { 0x06c5, 0x0435 }, + { 0x06c6, 0x0444 }, + { 0x06c7, 0x0433 }, + { 0x06c8, 0x0445 }, + { 0x06c9, 0x0438 }, + { 0x06ca, 0x0439 }, + { 0x06cb, 0x043a }, + { 0x06cc, 0x043b }, + { 0x06cd, 0x043c }, + { 0x06ce, 0x043d }, + { 0x06cf, 0x043e }, + { 0x06d0, 0x043f }, + { 0x06d1, 0x044f }, + { 0x06d2, 0x0440 }, + { 0x06d3, 0x0441 }, + { 0x06d4, 0x0442 }, + { 0x06d5, 0x0443 }, + { 0x06d6, 0x0436 }, + { 0x06d7, 0x0432 }, + { 0x06d8, 0x044c }, + { 0x06d9, 0x044b }, + { 0x06da, 0x0437 }, + { 0x06db, 0x0448 }, + { 0x06dc, 0x044d }, + { 0x06dd, 0x0449 }, + { 0x06de, 0x0447 }, + { 0x06df, 0x044a }, + { 0x06e0, 0x042e }, + { 0x06e1, 0x0410 }, + { 0x06e2, 0x0411 }, + { 0x06e3, 0x0426 }, + { 0x06e4, 0x0414 }, + { 0x06e5, 0x0415 }, + { 0x06e6, 0x0424 }, + { 0x06e7, 0x0413 }, + { 0x06e8, 0x0425 }, + { 0x06e9, 0x0418 }, + { 0x06ea, 0x0419 }, + { 0x06eb, 0x041a }, + { 0x06ec, 0x041b }, + { 0x06ed, 0x041c }, + { 0x06ee, 0x041d }, + { 0x06ef, 0x041e }, + { 0x06f0, 0x041f }, + { 0x06f1, 0x042f }, + { 0x06f2, 0x0420 }, + { 0x06f3, 0x0421 }, + { 0x06f4, 0x0422 }, + { 0x06f5, 0x0423 }, + { 0x06f6, 0x0416 }, + { 0x06f7, 0x0412 }, + { 0x06f8, 0x042c }, + { 0x06f9, 0x042b }, + { 0x06fa, 0x0417 }, + { 0x06fb, 0x0428 }, + { 0x06fc, 0x042d }, + { 0x06fd, 0x0429 }, + { 0x06fe, 0x0427 }, + { 0x06ff, 0x042a }, + { 0x07a1, 0x0386 }, + { 0x07a2, 0x0388 }, + { 0x07a3, 0x0389 }, + { 0x07a4, 0x038a }, + { 0x07a5, 0x03aa }, + { 0x07a7, 0x038c }, + { 0x07a8, 0x038e }, + { 0x07a9, 0x03ab }, + { 0x07ab, 0x038f }, + { 0x07ae, 0x0385 }, + { 0x07af, 0x2015 }, + { 0x07b1, 0x03ac }, + { 0x07b2, 0x03ad }, + { 0x07b3, 0x03ae }, + { 0x07b4, 0x03af }, + { 0x07b5, 0x03ca }, + { 0x07b6, 0x0390 }, + { 0x07b7, 0x03cc }, + { 0x07b8, 0x03cd }, + { 0x07b9, 0x03cb }, + { 0x07ba, 0x03b0 }, + { 0x07bb, 0x03ce }, + { 0x07c1, 0x0391 }, + { 0x07c2, 0x0392 }, + { 0x07c3, 0x0393 }, + { 0x07c4, 0x0394 }, + { 0x07c5, 0x0395 }, + { 0x07c6, 0x0396 }, + { 0x07c7, 0x0397 }, + { 0x07c8, 0x0398 }, + { 0x07c9, 0x0399 }, + { 0x07ca, 0x039a }, + { 0x07cb, 0x039b }, + { 0x07cc, 0x039c }, + { 0x07cd, 0x039d }, + { 0x07ce, 0x039e }, + { 0x07cf, 0x039f }, + { 0x07d0, 0x03a0 }, + { 0x07d1, 0x03a1 }, + { 0x07d2, 0x03a3 }, + { 0x07d4, 0x03a4 }, + { 0x07d5, 0x03a5 }, + { 0x07d6, 0x03a6 }, + { 0x07d7, 0x03a7 }, + { 0x07d8, 0x03a8 }, + { 0x07d9, 0x03a9 }, + { 0x07e1, 0x03b1 }, + { 0x07e2, 0x03b2 }, + { 0x07e3, 0x03b3 }, + { 0x07e4, 0x03b4 }, + { 0x07e5, 0x03b5 }, + { 0x07e6, 0x03b6 }, + { 0x07e7, 0x03b7 }, + { 0x07e8, 0x03b8 }, + { 0x07e9, 0x03b9 }, + { 0x07ea, 0x03ba }, + { 0x07eb, 0x03bb }, + { 0x07ec, 0x03bc }, + { 0x07ed, 0x03bd }, + { 0x07ee, 0x03be }, + { 0x07ef, 0x03bf }, + { 0x07f0, 0x03c0 }, + { 0x07f1, 0x03c1 }, + { 0x07f2, 0x03c3 }, + { 0x07f3, 0x03c2 }, + { 0x07f4, 0x03c4 }, + { 0x07f5, 0x03c5 }, + { 0x07f6, 0x03c6 }, + { 0x07f7, 0x03c7 }, + { 0x07f8, 0x03c8 }, + { 0x07f9, 0x03c9 }, + { 0x08a1, 0x23b7 }, + { 0x08a2, 0x250c }, + { 0x08a3, 0x2500 }, + { 0x08a4, 0x2320 }, + { 0x08a5, 0x2321 }, + { 0x08a6, 0x2502 }, + { 0x08a7, 0x23a1 }, + { 0x08a8, 0x23a3 }, + { 0x08a9, 0x23a4 }, + { 0x08aa, 0x23a6 }, + { 0x08ab, 0x239b }, + { 0x08ac, 0x239d }, + { 0x08ad, 0x239e }, + { 0x08ae, 0x23a0 }, + { 0x08af, 0x23a8 }, + { 0x08b0, 0x23ac }, + { 0x08bc, 0x2264 }, + { 0x08bd, 0x2260 }, + { 0x08be, 0x2265 }, + { 0x08bf, 0x222b }, + { 0x08c0, 0x2234 }, + { 0x08c1, 0x221d }, + { 0x08c2, 0x221e }, + { 0x08c5, 0x2207 }, + { 0x08c8, 0x223c }, + { 0x08c9, 0x2243 }, + { 0x08cd, 0x21d4 }, + { 0x08ce, 0x21d2 }, + { 0x08cf, 0x2261 }, + { 0x08d6, 0x221a }, + { 0x08da, 0x2282 }, + { 0x08db, 0x2283 }, + { 0x08dc, 0x2229 }, + { 0x08dd, 0x222a }, + { 0x08de, 0x2227 }, + { 0x08df, 0x2228 }, + { 0x08ef, 0x2202 }, + { 0x08f6, 0x0192 }, + { 0x08fb, 0x2190 }, + { 0x08fc, 0x2191 }, + { 0x08fd, 0x2192 }, + { 0x08fe, 0x2193 }, + { 0x09e0, 0x25c6 }, + { 0x09e1, 0x2592 }, + { 0x09e2, 0x2409 }, + { 0x09e3, 0x240c }, + { 0x09e4, 0x240d }, + { 0x09e5, 0x240a }, + { 0x09e8, 0x2424 }, + { 0x09e9, 0x240b }, + { 0x09ea, 0x2518 }, + { 0x09eb, 0x2510 }, + { 0x09ec, 0x250c }, + { 0x09ed, 0x2514 }, + { 0x09ee, 0x253c }, + { 0x09ef, 0x23ba }, + { 0x09f0, 0x23bb }, + { 0x09f1, 0x2500 }, + { 0x09f2, 0x23bc }, + { 0x09f3, 0x23bd }, + { 0x09f4, 0x251c }, + { 0x09f5, 0x2524 }, + { 0x09f6, 0x2534 }, + { 0x09f7, 0x252c }, + { 0x09f8, 0x2502 }, + { 0x0aa1, 0x2003 }, + { 0x0aa2, 0x2002 }, + { 0x0aa3, 0x2004 }, + { 0x0aa4, 0x2005 }, + { 0x0aa5, 0x2007 }, + { 0x0aa6, 0x2008 }, + { 0x0aa7, 0x2009 }, + { 0x0aa8, 0x200a }, + { 0x0aa9, 0x2014 }, + { 0x0aaa, 0x2013 }, + { 0x0aae, 0x2026 }, + { 0x0aaf, 0x2025 }, + { 0x0ab0, 0x2153 }, + { 0x0ab1, 0x2154 }, + { 0x0ab2, 0x2155 }, + { 0x0ab3, 0x2156 }, + { 0x0ab4, 0x2157 }, + { 0x0ab5, 0x2158 }, + { 0x0ab6, 0x2159 }, + { 0x0ab7, 0x215a }, + { 0x0ab8, 0x2105 }, + { 0x0abb, 0x2012 }, + { 0x0abc, 0x2329 }, + { 0x0abe, 0x232a }, + { 0x0ac3, 0x215b }, + { 0x0ac4, 0x215c }, + { 0x0ac5, 0x215d }, + { 0x0ac6, 0x215e }, + { 0x0ac9, 0x2122 }, + { 0x0aca, 0x2613 }, + { 0x0acc, 0x25c1 }, + { 0x0acd, 0x25b7 }, + { 0x0ace, 0x25cb }, + { 0x0acf, 0x25af }, + { 0x0ad0, 0x2018 }, + { 0x0ad1, 0x2019 }, + { 0x0ad2, 0x201c }, + { 0x0ad3, 0x201d }, + { 0x0ad4, 0x211e }, + { 0x0ad6, 0x2032 }, + { 0x0ad7, 0x2033 }, + { 0x0ad9, 0x271d }, + { 0x0adb, 0x25ac }, + { 0x0adc, 0x25c0 }, + { 0x0add, 0x25b6 }, + { 0x0ade, 0x25cf }, + { 0x0adf, 0x25ae }, + { 0x0ae0, 0x25e6 }, + { 0x0ae1, 0x25ab }, + { 0x0ae2, 0x25ad }, + { 0x0ae3, 0x25b3 }, + { 0x0ae4, 0x25bd }, + { 0x0ae5, 0x2606 }, + { 0x0ae6, 0x2022 }, + { 0x0ae7, 0x25aa }, + { 0x0ae8, 0x25b2 }, + { 0x0ae9, 0x25bc }, + { 0x0aea, 0x261c }, + { 0x0aeb, 0x261e }, + { 0x0aec, 0x2663 }, + { 0x0aed, 0x2666 }, + { 0x0aee, 0x2665 }, + { 0x0af0, 0x2720 }, + { 0x0af1, 0x2020 }, + { 0x0af2, 0x2021 }, + { 0x0af3, 0x2713 }, + { 0x0af4, 0x2717 }, + { 0x0af5, 0x266f }, + { 0x0af6, 0x266d }, + { 0x0af7, 0x2642 }, + { 0x0af8, 0x2640 }, + { 0x0af9, 0x260e }, + { 0x0afa, 0x2315 }, + { 0x0afb, 0x2117 }, + { 0x0afc, 0x2038 }, + { 0x0afd, 0x201a }, + { 0x0afe, 0x201e }, + { 0x0ba3, 0x003c }, + { 0x0ba6, 0x003e }, + { 0x0ba8, 0x2228 }, + { 0x0ba9, 0x2227 }, + { 0x0bc0, 0x00af }, + { 0x0bc2, 0x22a5 }, + { 0x0bc3, 0x2229 }, + { 0x0bc4, 0x230a }, + { 0x0bc6, 0x005f }, + { 0x0bca, 0x2218 }, + { 0x0bcc, 0x2395 }, + { 0x0bce, 0x22a4 }, + { 0x0bcf, 0x25cb }, + { 0x0bd3, 0x2308 }, + { 0x0bd6, 0x222a }, + { 0x0bd8, 0x2283 }, + { 0x0bda, 0x2282 }, + { 0x0bdc, 0x22a2 }, + { 0x0bfc, 0x22a3 }, + { 0x0cdf, 0x2017 }, + { 0x0ce0, 0x05d0 }, + { 0x0ce1, 0x05d1 }, + { 0x0ce2, 0x05d2 }, + { 0x0ce3, 0x05d3 }, + { 0x0ce4, 0x05d4 }, + { 0x0ce5, 0x05d5 }, + { 0x0ce6, 0x05d6 }, + { 0x0ce7, 0x05d7 }, + { 0x0ce8, 0x05d8 }, + { 0x0ce9, 0x05d9 }, + { 0x0cea, 0x05da }, + { 0x0ceb, 0x05db }, + { 0x0cec, 0x05dc }, + { 0x0ced, 0x05dd }, + { 0x0cee, 0x05de }, + { 0x0cef, 0x05df }, + { 0x0cf0, 0x05e0 }, + { 0x0cf1, 0x05e1 }, + { 0x0cf2, 0x05e2 }, + { 0x0cf3, 0x05e3 }, + { 0x0cf4, 0x05e4 }, + { 0x0cf5, 0x05e5 }, + { 0x0cf6, 0x05e6 }, + { 0x0cf7, 0x05e7 }, + { 0x0cf8, 0x05e8 }, + { 0x0cf9, 0x05e9 }, + { 0x0cfa, 0x05ea }, + { 0x0da1, 0x0e01 }, + { 0x0da2, 0x0e02 }, + { 0x0da3, 0x0e03 }, + { 0x0da4, 0x0e04 }, + { 0x0da5, 0x0e05 }, + { 0x0da6, 0x0e06 }, + { 0x0da7, 0x0e07 }, + { 0x0da8, 0x0e08 }, + { 0x0da9, 0x0e09 }, + { 0x0daa, 0x0e0a }, + { 0x0dab, 0x0e0b }, + { 0x0dac, 0x0e0c }, + { 0x0dad, 0x0e0d }, + { 0x0dae, 0x0e0e }, + { 0x0daf, 0x0e0f }, + { 0x0db0, 0x0e10 }, + { 0x0db1, 0x0e11 }, + { 0x0db2, 0x0e12 }, + { 0x0db3, 0x0e13 }, + { 0x0db4, 0x0e14 }, + { 0x0db5, 0x0e15 }, + { 0x0db6, 0x0e16 }, + { 0x0db7, 0x0e17 }, + { 0x0db8, 0x0e18 }, + { 0x0db9, 0x0e19 }, + { 0x0dba, 0x0e1a }, + { 0x0dbb, 0x0e1b }, + { 0x0dbc, 0x0e1c }, + { 0x0dbd, 0x0e1d }, + { 0x0dbe, 0x0e1e }, + { 0x0dbf, 0x0e1f }, + { 0x0dc0, 0x0e20 }, + { 0x0dc1, 0x0e21 }, + { 0x0dc2, 0x0e22 }, + { 0x0dc3, 0x0e23 }, + { 0x0dc4, 0x0e24 }, + { 0x0dc5, 0x0e25 }, + { 0x0dc6, 0x0e26 }, + { 0x0dc7, 0x0e27 }, + { 0x0dc8, 0x0e28 }, + { 0x0dc9, 0x0e29 }, + { 0x0dca, 0x0e2a }, + { 0x0dcb, 0x0e2b }, + { 0x0dcc, 0x0e2c }, + { 0x0dcd, 0x0e2d }, + { 0x0dce, 0x0e2e }, + { 0x0dcf, 0x0e2f }, + { 0x0dd0, 0x0e30 }, + { 0x0dd1, 0x0e31 }, + { 0x0dd2, 0x0e32 }, + { 0x0dd3, 0x0e33 }, + { 0x0dd4, 0x0e34 }, + { 0x0dd5, 0x0e35 }, + { 0x0dd6, 0x0e36 }, + { 0x0dd7, 0x0e37 }, + { 0x0dd8, 0x0e38 }, + { 0x0dd9, 0x0e39 }, + { 0x0dda, 0x0e3a }, + { 0x0ddf, 0x0e3f }, + { 0x0de0, 0x0e40 }, + { 0x0de1, 0x0e41 }, + { 0x0de2, 0x0e42 }, + { 0x0de3, 0x0e43 }, + { 0x0de4, 0x0e44 }, + { 0x0de5, 0x0e45 }, + { 0x0de6, 0x0e46 }, + { 0x0de7, 0x0e47 }, + { 0x0de8, 0x0e48 }, + { 0x0de9, 0x0e49 }, + { 0x0dea, 0x0e4a }, + { 0x0deb, 0x0e4b }, + { 0x0dec, 0x0e4c }, + { 0x0ded, 0x0e4d }, + { 0x0df0, 0x0e50 }, + { 0x0df1, 0x0e51 }, + { 0x0df2, 0x0e52 }, + { 0x0df3, 0x0e53 }, + { 0x0df4, 0x0e54 }, + { 0x0df5, 0x0e55 }, + { 0x0df6, 0x0e56 }, + { 0x0df7, 0x0e57 }, + { 0x0df8, 0x0e58 }, + { 0x0df9, 0x0e59 }, + { 0x0ea1, 0x3131 }, + { 0x0ea2, 0x3132 }, + { 0x0ea3, 0x3133 }, + { 0x0ea4, 0x3134 }, + { 0x0ea5, 0x3135 }, + { 0x0ea6, 0x3136 }, + { 0x0ea7, 0x3137 }, + { 0x0ea8, 0x3138 }, + { 0x0ea9, 0x3139 }, + { 0x0eaa, 0x313a }, + { 0x0eab, 0x313b }, + { 0x0eac, 0x313c }, + { 0x0ead, 0x313d }, + { 0x0eae, 0x313e }, + { 0x0eaf, 0x313f }, + { 0x0eb0, 0x3140 }, + { 0x0eb1, 0x3141 }, + { 0x0eb2, 0x3142 }, + { 0x0eb3, 0x3143 }, + { 0x0eb4, 0x3144 }, + { 0x0eb5, 0x3145 }, + { 0x0eb6, 0x3146 }, + { 0x0eb7, 0x3147 }, + { 0x0eb8, 0x3148 }, + { 0x0eb9, 0x3149 }, + { 0x0eba, 0x314a }, + { 0x0ebb, 0x314b }, + { 0x0ebc, 0x314c }, + { 0x0ebd, 0x314d }, + { 0x0ebe, 0x314e }, + { 0x0ebf, 0x314f }, + { 0x0ec0, 0x3150 }, + { 0x0ec1, 0x3151 }, + { 0x0ec2, 0x3152 }, + { 0x0ec3, 0x3153 }, + { 0x0ec4, 0x3154 }, + { 0x0ec5, 0x3155 }, + { 0x0ec6, 0x3156 }, + { 0x0ec7, 0x3157 }, + { 0x0ec8, 0x3158 }, + { 0x0ec9, 0x3159 }, + { 0x0eca, 0x315a }, + { 0x0ecb, 0x315b }, + { 0x0ecc, 0x315c }, + { 0x0ecd, 0x315d }, + { 0x0ece, 0x315e }, + { 0x0ecf, 0x315f }, + { 0x0ed0, 0x3160 }, + { 0x0ed1, 0x3161 }, + { 0x0ed2, 0x3162 }, + { 0x0ed3, 0x3163 }, + { 0x0ed4, 0x11a8 }, + { 0x0ed5, 0x11a9 }, + { 0x0ed6, 0x11aa }, + { 0x0ed7, 0x11ab }, + { 0x0ed8, 0x11ac }, + { 0x0ed9, 0x11ad }, + { 0x0eda, 0x11ae }, + { 0x0edb, 0x11af }, + { 0x0edc, 0x11b0 }, + { 0x0edd, 0x11b1 }, + { 0x0ede, 0x11b2 }, + { 0x0edf, 0x11b3 }, + { 0x0ee0, 0x11b4 }, + { 0x0ee1, 0x11b5 }, + { 0x0ee2, 0x11b6 }, + { 0x0ee3, 0x11b7 }, + { 0x0ee4, 0x11b8 }, + { 0x0ee5, 0x11b9 }, + { 0x0ee6, 0x11ba }, + { 0x0ee7, 0x11bb }, + { 0x0ee8, 0x11bc }, + { 0x0ee9, 0x11bd }, + { 0x0eea, 0x11be }, + { 0x0eeb, 0x11bf }, + { 0x0eec, 0x11c0 }, + { 0x0eed, 0x11c1 }, + { 0x0eee, 0x11c2 }, + { 0x0eef, 0x316d }, + { 0x0ef0, 0x3171 }, + { 0x0ef1, 0x3178 }, + { 0x0ef2, 0x317f }, + { 0x0ef3, 0x3181 }, + { 0x0ef4, 0x3184 }, + { 0x0ef5, 0x3186 }, + { 0x0ef6, 0x318d }, + { 0x0ef7, 0x318e }, + { 0x0ef8, 0x11eb }, + { 0x0ef9, 0x11f0 }, + { 0x0efa, 0x11f9 }, + { 0x0eff, 0x20a9 }, + { 0x13a4, 0x20ac }, + { 0x13bc, 0x0152 }, + { 0x13bd, 0x0153 }, + { 0x13be, 0x0178 }, + { 0x20ac, 0x20ac }, + { 0xfe50, '`' }, + { 0xfe51, 0x00b4 }, + { 0xfe52, '^' }, + { 0xfe53, '~' }, + { 0xfe54, 0x00af }, + { 0xfe55, 0x02d8 }, + { 0xfe56, 0x02d9 }, + { 0xfe57, 0x00a8 }, + { 0xfe58, 0x02da }, + { 0xfe59, 0x02dd }, + { 0xfe5a, 0x02c7 }, + { 0xfe5b, 0x00b8 }, + { 0xfe5c, 0x02db }, + { 0xfe5d, 0x037a }, + { 0xfe5e, 0x309b }, + { 0xfe5f, 0x309c }, + { 0xfe63, '/' }, + { 0xfe64, 0x02bc }, + { 0xfe65, 0x02bd }, + { 0xfe66, 0x02f5 }, + { 0xfe67, 0x02f3 }, + { 0xfe68, 0x02cd }, + { 0xfe69, 0xa788 }, + { 0xfe6a, 0x02f7 }, + { 0xfe6e, ',' }, + { 0xfe6f, 0x00a4 }, + { 0xfe80, 'a' }, /* XK_dead_a */ + { 0xfe81, 'A' }, /* XK_dead_A */ + { 0xfe82, 'e' }, /* XK_dead_e */ + { 0xfe83, 'E' }, /* XK_dead_E */ + { 0xfe84, 'i' }, /* XK_dead_i */ + { 0xfe85, 'I' }, /* XK_dead_I */ + { 0xfe86, 'o' }, /* XK_dead_o */ + { 0xfe87, 'O' }, /* XK_dead_O */ + { 0xfe88, 'u' }, /* XK_dead_u */ + { 0xfe89, 'U' }, /* XK_dead_U */ + { 0xfe8a, 0x0259 }, + { 0xfe8b, 0x018f }, + { 0xfe8c, 0x00b5 }, + { 0xfe90, '_' }, + { 0xfe91, 0x02c8 }, + { 0xfe92, 0x02cc }, + { 0xff80 /*XKB_KEY_KP_Space*/, ' ' }, + { 0xff95 /*XKB_KEY_KP_7*/, 0x0037 }, + { 0xff96 /*XKB_KEY_KP_4*/, 0x0034 }, + { 0xff97 /*XKB_KEY_KP_8*/, 0x0038 }, + { 0xff98 /*XKB_KEY_KP_6*/, 0x0036 }, + { 0xff99 /*XKB_KEY_KP_2*/, 0x0032 }, + { 0xff9a /*XKB_KEY_KP_9*/, 0x0039 }, + { 0xff9b /*XKB_KEY_KP_3*/, 0x0033 }, + { 0xff9c /*XKB_KEY_KP_1*/, 0x0031 }, + { 0xff9d /*XKB_KEY_KP_5*/, 0x0035 }, + { 0xff9e /*XKB_KEY_KP_0*/, 0x0030 }, + { 0xffaa /*XKB_KEY_KP_Multiply*/, '*' }, + { 0xffab /*XKB_KEY_KP_Add*/, '+' }, + { 0xffac /*XKB_KEY_KP_Separator*/, ',' }, + { 0xffad /*XKB_KEY_KP_Subtract*/, '-' }, + { 0xffae /*XKB_KEY_KP_Decimal*/, '.' }, + { 0xffaf /*XKB_KEY_KP_Divide*/, '/' }, + { 0xffb0 /*XKB_KEY_KP_0*/, 0x0030 }, + { 0xffb1 /*XKB_KEY_KP_1*/, 0x0031 }, + { 0xffb2 /*XKB_KEY_KP_2*/, 0x0032 }, + { 0xffb3 /*XKB_KEY_KP_3*/, 0x0033 }, + { 0xffb4 /*XKB_KEY_KP_4*/, 0x0034 }, + { 0xffb5 /*XKB_KEY_KP_5*/, 0x0035 }, + { 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 }, + { 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 }, + { 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 }, + { 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 }, + { 0xffbd /*XKB_KEY_KP_Equal*/, '=' } +}; + +_SOKOL_PRIVATE int _sapp_x11_error_handler(Display* display, XErrorEvent* event) { + _SOKOL_UNUSED(display); + _sapp.x11.error_code = event->error_code; + return 0; +} + +_SOKOL_PRIVATE void _sapp_x11_grab_error_handler(void) { + _sapp.x11.error_code = Success; + XSetErrorHandler(_sapp_x11_error_handler); +} + +_SOKOL_PRIVATE void _sapp_x11_release_error_handler(void) { + XSync(_sapp.x11.display, False); + XSetErrorHandler(NULL); +} + +_SOKOL_PRIVATE void _sapp_x11_init_extensions(void) { + _sapp.x11.UTF8_STRING = XInternAtom(_sapp.x11.display, "UTF8_STRING", False); + _sapp.x11.WM_PROTOCOLS = XInternAtom(_sapp.x11.display, "WM_PROTOCOLS", False); + _sapp.x11.WM_DELETE_WINDOW = XInternAtom(_sapp.x11.display, "WM_DELETE_WINDOW", False); + _sapp.x11.WM_STATE = XInternAtom(_sapp.x11.display, "WM_STATE", False); + _sapp.x11.NET_WM_NAME = XInternAtom(_sapp.x11.display, "_NET_WM_NAME", False); + _sapp.x11.NET_WM_ICON_NAME = XInternAtom(_sapp.x11.display, "_NET_WM_ICON_NAME", False); + _sapp.x11.NET_WM_ICON = XInternAtom(_sapp.x11.display, "_NET_WM_ICON", False); + _sapp.x11.NET_WM_STATE = XInternAtom(_sapp.x11.display, "_NET_WM_STATE", False); + _sapp.x11.NET_WM_STATE_FULLSCREEN = XInternAtom(_sapp.x11.display, "_NET_WM_STATE_FULLSCREEN", False); + if (_sapp.drop.enabled) { + _sapp.x11.xdnd.XdndAware = XInternAtom(_sapp.x11.display, "XdndAware", False); + _sapp.x11.xdnd.XdndEnter = XInternAtom(_sapp.x11.display, "XdndEnter", False); + _sapp.x11.xdnd.XdndPosition = XInternAtom(_sapp.x11.display, "XdndPosition", False); + _sapp.x11.xdnd.XdndStatus = XInternAtom(_sapp.x11.display, "XdndStatus", False); + _sapp.x11.xdnd.XdndActionCopy = XInternAtom(_sapp.x11.display, "XdndActionCopy", False); + _sapp.x11.xdnd.XdndDrop = XInternAtom(_sapp.x11.display, "XdndDrop", False); + _sapp.x11.xdnd.XdndFinished = XInternAtom(_sapp.x11.display, "XdndFinished", False); + _sapp.x11.xdnd.XdndSelection = XInternAtom(_sapp.x11.display, "XdndSelection", False); + _sapp.x11.xdnd.XdndTypeList = XInternAtom(_sapp.x11.display, "XdndTypeList", False); + _sapp.x11.xdnd.text_uri_list = XInternAtom(_sapp.x11.display, "text/uri-list", False); + } + + /* check Xi extension for raw mouse input */ + if (XQueryExtension(_sapp.x11.display, "XInputExtension", &_sapp.x11.xi.major_opcode, &_sapp.x11.xi.event_base, &_sapp.x11.xi.error_base)) { + _sapp.x11.xi.major = 2; + _sapp.x11.xi.minor = 0; + if (XIQueryVersion(_sapp.x11.display, &_sapp.x11.xi.major, &_sapp.x11.xi.minor) == Success) { + _sapp.x11.xi.available = true; + } + } +} + +_SOKOL_PRIVATE void _sapp_x11_query_system_dpi(void) { + /* from GLFW: + + NOTE: Default to the display-wide DPI as we don't currently have a policy + for which monitor a window is considered to be on + + _sapp.x11.dpi = DisplayWidth(_sapp.x11.display, _sapp.x11.screen) * + 25.4f / DisplayWidthMM(_sapp.x11.display, _sapp.x11.screen); + + NOTE: Basing the scale on Xft.dpi where available should provide the most + consistent user experience (matches Qt, Gtk, etc), although not + always the most accurate one + */ + bool dpi_ok = false; + char* rms = XResourceManagerString(_sapp.x11.display); + if (rms) { + XrmDatabase db = XrmGetStringDatabase(rms); + if (db) { + XrmValue value; + char* type = NULL; + if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value)) { + if (type && strcmp(type, "String") == 0) { + _sapp.x11.dpi = atof(value.addr); + dpi_ok = true; + } + } + XrmDestroyDatabase(db); + } + } + // fallback if querying DPI had failed: assume the standard DPI 96.0f + if (!dpi_ok) { + _sapp.x11.dpi = 96.0f; + _SAPP_WARN(LINUX_X11_QUERY_SYSTEM_DPI_FAILED); + } +} + +#if defined(_SAPP_GLX) + +_SOKOL_PRIVATE bool _sapp_glx_has_ext(const char* ext, const char* extensions) { + SOKOL_ASSERT(ext); + const char* start = extensions; + while (true) { + const char* where = strstr(start, ext); + if (!where) { + return false; + } + const char* terminator = where + strlen(ext); + if ((where == start) || (*(where - 1) == ' ')) { + if (*terminator == ' ' || *terminator == '\0') { + break; + } + } + start = terminator; + } + return true; +} + +_SOKOL_PRIVATE bool _sapp_glx_extsupported(const char* ext, const char* extensions) { + if (extensions) { + return _sapp_glx_has_ext(ext, extensions); + } + else { + return false; + } +} + +_SOKOL_PRIVATE void* _sapp_glx_getprocaddr(const char* procname) +{ + if (_sapp.glx.GetProcAddress) { + return (void*) _sapp.glx.GetProcAddress(procname); + } + else if (_sapp.glx.GetProcAddressARB) { + return (void*) _sapp.glx.GetProcAddressARB(procname); + } + else { + return dlsym(_sapp.glx.libgl, procname); + } +} + +_SOKOL_PRIVATE void _sapp_glx_init(void) { + const char* sonames[] = { "libGL.so.1", "libGL.so", 0 }; + for (int i = 0; sonames[i]; i++) { + _sapp.glx.libgl = dlopen(sonames[i], RTLD_LAZY|RTLD_GLOBAL); + if (_sapp.glx.libgl) { + break; + } + } + if (!_sapp.glx.libgl) { + _SAPP_PANIC(LINUX_GLX_LOAD_LIBGL_FAILED); + } + _sapp.glx.GetFBConfigs = (PFNGLXGETFBCONFIGSPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigs"); + _sapp.glx.GetFBConfigAttrib = (PFNGLXGETFBCONFIGATTRIBPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigAttrib"); + _sapp.glx.GetClientString = (PFNGLXGETCLIENTSTRINGPROC) dlsym(_sapp.glx.libgl, "glXGetClientString"); + _sapp.glx.QueryExtension = (PFNGLXQUERYEXTENSIONPROC) dlsym(_sapp.glx.libgl, "glXQueryExtension"); + _sapp.glx.QueryVersion = (PFNGLXQUERYVERSIONPROC) dlsym(_sapp.glx.libgl, "glXQueryVersion"); + _sapp.glx.DestroyContext = (PFNGLXDESTROYCONTEXTPROC) dlsym(_sapp.glx.libgl, "glXDestroyContext"); + _sapp.glx.MakeCurrent = (PFNGLXMAKECURRENTPROC) dlsym(_sapp.glx.libgl, "glXMakeCurrent"); + _sapp.glx.SwapBuffers = (PFNGLXSWAPBUFFERSPROC) dlsym(_sapp.glx.libgl, "glXSwapBuffers"); + _sapp.glx.QueryExtensionsString = (PFNGLXQUERYEXTENSIONSSTRINGPROC) dlsym(_sapp.glx.libgl, "glXQueryExtensionsString"); + _sapp.glx.CreateWindow = (PFNGLXCREATEWINDOWPROC) dlsym(_sapp.glx.libgl, "glXCreateWindow"); + _sapp.glx.DestroyWindow = (PFNGLXDESTROYWINDOWPROC) dlsym(_sapp.glx.libgl, "glXDestroyWindow"); + _sapp.glx.GetProcAddress = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp.glx.libgl, "glXGetProcAddress"); + _sapp.glx.GetProcAddressARB = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp.glx.libgl, "glXGetProcAddressARB"); + _sapp.glx.GetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC) dlsym(_sapp.glx.libgl, "glXGetVisualFromFBConfig"); + if (!_sapp.glx.GetFBConfigs || + !_sapp.glx.GetFBConfigAttrib || + !_sapp.glx.GetClientString || + !_sapp.glx.QueryExtension || + !_sapp.glx.QueryVersion || + !_sapp.glx.DestroyContext || + !_sapp.glx.MakeCurrent || + !_sapp.glx.SwapBuffers || + !_sapp.glx.QueryExtensionsString || + !_sapp.glx.CreateWindow || + !_sapp.glx.DestroyWindow || + !_sapp.glx.GetProcAddress || + !_sapp.glx.GetProcAddressARB || + !_sapp.glx.GetVisualFromFBConfig) + { + _SAPP_PANIC(LINUX_GLX_LOAD_ENTRY_POINTS_FAILED); + } + + if (!_sapp.glx.QueryExtension(_sapp.x11.display, &_sapp.glx.error_base, &_sapp.glx.event_base)) { + _SAPP_PANIC(LINUX_GLX_EXTENSION_NOT_FOUND); + } + if (!_sapp.glx.QueryVersion(_sapp.x11.display, &_sapp.glx.major, &_sapp.glx.minor)) { + _SAPP_PANIC(LINUX_GLX_QUERY_VERSION_FAILED); + } + if (_sapp.glx.major == 1 && _sapp.glx.minor < 3) { + _SAPP_PANIC(LINUX_GLX_VERSION_TOO_LOW); + } + const char* exts = _sapp.glx.QueryExtensionsString(_sapp.x11.display, _sapp.x11.screen); + if (_sapp_glx_extsupported("GLX_EXT_swap_control", exts)) { + _sapp.glx.SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) _sapp_glx_getprocaddr("glXSwapIntervalEXT"); + _sapp.glx.EXT_swap_control = 0 != _sapp.glx.SwapIntervalEXT; + } + if (_sapp_glx_extsupported("GLX_MESA_swap_control", exts)) { + _sapp.glx.SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) _sapp_glx_getprocaddr("glXSwapIntervalMESA"); + _sapp.glx.MESA_swap_control = 0 != _sapp.glx.SwapIntervalMESA; + } + _sapp.glx.ARB_multisample = _sapp_glx_extsupported("GLX_ARB_multisample", exts); + if (_sapp_glx_extsupported("GLX_ARB_create_context", exts)) { + _sapp.glx.CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) _sapp_glx_getprocaddr("glXCreateContextAttribsARB"); + _sapp.glx.ARB_create_context = 0 != _sapp.glx.CreateContextAttribsARB; + } + _sapp.glx.ARB_create_context_profile = _sapp_glx_extsupported("GLX_ARB_create_context_profile", exts); +} + +_SOKOL_PRIVATE int _sapp_glx_attrib(GLXFBConfig fbconfig, int attrib) { + int value; + _sapp.glx.GetFBConfigAttrib(_sapp.x11.display, fbconfig, attrib, &value); + return value; +} + +_SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig(void) { + GLXFBConfig* native_configs; + _sapp_gl_fbconfig* usable_configs; + const _sapp_gl_fbconfig* closest; + int i, native_count, usable_count; + const char* vendor; + bool trust_window_bit = true; + + /* HACK: This is a (hopefully temporary) workaround for Chromium + (VirtualBox GL) not setting the window bit on any GLXFBConfigs + */ + vendor = _sapp.glx.GetClientString(_sapp.x11.display, GLX_VENDOR); + if (vendor && strcmp(vendor, "Chromium") == 0) { + trust_window_bit = false; + } + + native_configs = _sapp.glx.GetFBConfigs(_sapp.x11.display, _sapp.x11.screen, &native_count); + if (!native_configs || !native_count) { + _SAPP_PANIC(LINUX_GLX_NO_GLXFBCONFIGS); + } + + usable_configs = (_sapp_gl_fbconfig*) _sapp_malloc_clear((size_t)native_count * sizeof(_sapp_gl_fbconfig)); + usable_count = 0; + for (i = 0; i < native_count; i++) { + const GLXFBConfig n = native_configs[i]; + _sapp_gl_fbconfig* u = usable_configs + usable_count; + _sapp_gl_init_fbconfig(u); + + /* Only consider RGBA GLXFBConfigs */ + if (0 == (_sapp_glx_attrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT)) { + continue; + } + /* Only consider window GLXFBConfigs */ + if (0 == (_sapp_glx_attrib(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT)) { + if (trust_window_bit) { + continue; + } + } + u->red_bits = _sapp_glx_attrib(n, GLX_RED_SIZE); + u->green_bits = _sapp_glx_attrib(n, GLX_GREEN_SIZE); + u->blue_bits = _sapp_glx_attrib(n, GLX_BLUE_SIZE); + u->alpha_bits = _sapp_glx_attrib(n, GLX_ALPHA_SIZE); + u->depth_bits = _sapp_glx_attrib(n, GLX_DEPTH_SIZE); + u->stencil_bits = _sapp_glx_attrib(n, GLX_STENCIL_SIZE); + if (_sapp_glx_attrib(n, GLX_DOUBLEBUFFER)) { + u->doublebuffer = true; + } + if (_sapp.glx.ARB_multisample) { + u->samples = _sapp_glx_attrib(n, GLX_SAMPLES); + } + u->handle = (uintptr_t) n; + usable_count++; + } + _sapp_gl_fbconfig desired; + _sapp_gl_init_fbconfig(&desired); + desired.red_bits = 8; + desired.green_bits = 8; + desired.blue_bits = 8; + desired.alpha_bits = 8; + desired.depth_bits = 24; + desired.stencil_bits = 8; + desired.doublebuffer = true; + desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0; + closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count); + GLXFBConfig result = 0; + if (closest) { + result = (GLXFBConfig) closest->handle; + } + XFree(native_configs); + _sapp_free(usable_configs); + return result; +} + +_SOKOL_PRIVATE void _sapp_glx_choose_visual(Visual** visual, int* depth) { + GLXFBConfig native = _sapp_glx_choosefbconfig(); + if (0 == native) { + _SAPP_PANIC(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG); + } + XVisualInfo* result = _sapp.glx.GetVisualFromFBConfig(_sapp.x11.display, native); + if (!result) { + _SAPP_PANIC(LINUX_GLX_GET_VISUAL_FROM_FBCONFIG_FAILED); + } + *visual = result->visual; + *depth = result->depth; + XFree(result); +} + +_SOKOL_PRIVATE void _sapp_glx_make_current(void) { + _sapp.glx.MakeCurrent(_sapp.x11.display, _sapp.glx.window, _sapp.glx.ctx); + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer); +} + +_SOKOL_PRIVATE void _sapp_glx_create_context(void) { + GLXFBConfig native = _sapp_glx_choosefbconfig(); + if (0 == native){ + _SAPP_PANIC(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG); + } + if (!(_sapp.glx.ARB_create_context && _sapp.glx.ARB_create_context_profile)) { + _SAPP_PANIC(LINUX_GLX_REQUIRED_EXTENSIONS_MISSING); + } + _sapp_x11_grab_error_handler(); + const int attribs[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, _sapp.desc.gl_major_version, + GLX_CONTEXT_MINOR_VERSION_ARB, _sapp.desc.gl_minor_version, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + 0, 0 + }; + _sapp.glx.ctx = _sapp.glx.CreateContextAttribsARB(_sapp.x11.display, native, NULL, True, attribs); + if (!_sapp.glx.ctx) { + _SAPP_PANIC(LINUX_GLX_CREATE_CONTEXT_FAILED); + } + _sapp_x11_release_error_handler(); + _sapp.glx.window = _sapp.glx.CreateWindow(_sapp.x11.display, native, _sapp.x11.window, NULL); + if (!_sapp.glx.window) { + _SAPP_PANIC(LINUX_GLX_CREATE_WINDOW_FAILED); + } + _sapp_glx_make_current(); +} + +_SOKOL_PRIVATE void _sapp_glx_destroy_context(void) { + if (_sapp.glx.window) { + _sapp.glx.DestroyWindow(_sapp.x11.display, _sapp.glx.window); + _sapp.glx.window = 0; + } + if (_sapp.glx.ctx) { + _sapp.glx.DestroyContext(_sapp.x11.display, _sapp.glx.ctx); + _sapp.glx.ctx = 0; + } +} + +_SOKOL_PRIVATE void _sapp_glx_swap_buffers(void) { + _sapp.glx.SwapBuffers(_sapp.x11.display, _sapp.glx.window); +} + +_SOKOL_PRIVATE void _sapp_glx_swapinterval(int interval) { + if (_sapp.glx.EXT_swap_control) { + _sapp.glx.SwapIntervalEXT(_sapp.x11.display, _sapp.glx.window, interval); + } + else if (_sapp.glx.MESA_swap_control) { + _sapp.glx.SwapIntervalMESA(interval); + } +} + +#endif /* _SAPP_GLX */ + +_SOKOL_PRIVATE void _sapp_x11_send_event(Atom type, int a, int b, int c, int d, int e) { + XEvent event; + _sapp_clear(&event, sizeof(event)); + + event.type = ClientMessage; + event.xclient.window = _sapp.x11.window; + event.xclient.format = 32; + event.xclient.message_type = type; + event.xclient.data.l[0] = a; + event.xclient.data.l[1] = b; + event.xclient.data.l[2] = c; + event.xclient.data.l[3] = d; + event.xclient.data.l[4] = e; + + XSendEvent(_sapp.x11.display, _sapp.x11.root, + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &event); +} + +_SOKOL_PRIVATE void _sapp_x11_query_window_size(void) { + XWindowAttributes attribs; + XGetWindowAttributes(_sapp.x11.display, _sapp.x11.window, &attribs); + _sapp.window_width = attribs.width; + _sapp.window_height = attribs.height; + _sapp.framebuffer_width = _sapp.window_width; + _sapp.framebuffer_height = _sapp.window_height; +} + +_SOKOL_PRIVATE void _sapp_x11_set_fullscreen(bool enable) { + /* NOTE: this function must be called after XMapWindow (which happens in _sapp_x11_show_window()) */ + if (_sapp.x11.NET_WM_STATE && _sapp.x11.NET_WM_STATE_FULLSCREEN) { + if (enable) { + const int _NET_WM_STATE_ADD = 1; + _sapp_x11_send_event(_sapp.x11.NET_WM_STATE, + _NET_WM_STATE_ADD, + _sapp.x11.NET_WM_STATE_FULLSCREEN, + 0, 1, 0); + } + else { + const int _NET_WM_STATE_REMOVE = 0; + _sapp_x11_send_event(_sapp.x11.NET_WM_STATE, + _NET_WM_STATE_REMOVE, + _sapp.x11.NET_WM_STATE_FULLSCREEN, + 0, 1, 0); + } + } + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE void _sapp_x11_create_hidden_cursor(void) { + SOKOL_ASSERT(0 == _sapp.x11.hidden_cursor); + const int w = 16; + const int h = 16; + XcursorImage* img = XcursorImageCreate(w, h); + SOKOL_ASSERT(img && (img->width == 16) && (img->height == 16) && img->pixels); + img->xhot = 0; + img->yhot = 0; + const size_t num_bytes = (size_t)(w * h) * sizeof(XcursorPixel); + _sapp_clear(img->pixels, num_bytes); + _sapp.x11.hidden_cursor = XcursorImageLoadCursor(_sapp.x11.display, img); + XcursorImageDestroy(img); +} + + _SOKOL_PRIVATE void _sapp_x11_create_standard_cursor(sapp_mouse_cursor cursor, const char* name, const char* theme, int size, uint32_t fallback_native) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + SOKOL_ASSERT(_sapp.x11.display); + if (theme) { + XcursorImage* img = XcursorLibraryLoadImage(name, theme, size); + if (img) { + _sapp.x11.cursors[cursor] = XcursorImageLoadCursor(_sapp.x11.display, img); + XcursorImageDestroy(img); + } + } + if (0 == _sapp.x11.cursors[cursor]) { + _sapp.x11.cursors[cursor] = XCreateFontCursor(_sapp.x11.display, fallback_native); + } +} + +_SOKOL_PRIVATE void _sapp_x11_create_cursors(void) { + SOKOL_ASSERT(_sapp.x11.display); + const char* cursor_theme = XcursorGetTheme(_sapp.x11.display); + const int size = XcursorGetDefaultSize(_sapp.x11.display); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_ARROW, "default", cursor_theme, size, XC_left_ptr); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_IBEAM, "text", cursor_theme, size, XC_xterm); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_CROSSHAIR, "crosshair", cursor_theme, size, XC_crosshair); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_POINTING_HAND, "pointer", cursor_theme, size, XC_hand2); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_EW, "ew-resize", cursor_theme, size, XC_sb_h_double_arrow); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_NS, "ns-resize", cursor_theme, size, XC_sb_v_double_arrow); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_NWSE, "nwse-resize", cursor_theme, size, 0); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_NESW, "nesw-resize", cursor_theme, size, 0); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_RESIZE_ALL, "all-scroll", cursor_theme, size, XC_fleur); + _sapp_x11_create_standard_cursor(SAPP_MOUSECURSOR_NOT_ALLOWED, "no-allowed", cursor_theme, size, 0); + _sapp_x11_create_hidden_cursor(); +} + +_SOKOL_PRIVATE void _sapp_x11_destroy_cursors(void) { + SOKOL_ASSERT(_sapp.x11.display); + if (_sapp.x11.hidden_cursor) { + XFreeCursor(_sapp.x11.display, _sapp.x11.hidden_cursor); + _sapp.x11.hidden_cursor = 0; + } + for (int i = 0; i < _SAPP_MOUSECURSOR_NUM; i++) { + if (_sapp.x11.cursors[i]) { + XFreeCursor(_sapp.x11.display, _sapp.x11.cursors[i]); + _sapp.x11.cursors[i] = 0; + } + } +} + +_SOKOL_PRIVATE void _sapp_x11_toggle_fullscreen(void) { + _sapp.fullscreen = !_sapp.fullscreen; + _sapp_x11_set_fullscreen(_sapp.fullscreen); + _sapp_x11_query_window_size(); +} + +_SOKOL_PRIVATE void _sapp_x11_update_cursor(sapp_mouse_cursor cursor, bool shown) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + if (shown) { + if (_sapp.x11.cursors[cursor]) { + XDefineCursor(_sapp.x11.display, _sapp.x11.window, _sapp.x11.cursors[cursor]); + } + else { + XUndefineCursor(_sapp.x11.display, _sapp.x11.window); + } + } + else { + XDefineCursor(_sapp.x11.display, _sapp.x11.window, _sapp.x11.hidden_cursor); + } + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE void _sapp_x11_lock_mouse(bool lock) { + if (lock == _sapp.mouse.locked) { + return; + } + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + _sapp.mouse.locked = lock; + if (_sapp.mouse.locked) { + if (_sapp.x11.xi.available) { + XIEventMask em; + unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; // XIMaskLen is a macro + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + XISetMask(mask, XI_RawMotion); + XISelectEvents(_sapp.x11.display, _sapp.x11.root, &em, 1); + } + XGrabPointer(_sapp.x11.display, // display + _sapp.x11.window, // grab_window + True, // owner_events + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, // event_mask + GrabModeAsync, // pointer_mode + GrabModeAsync, // keyboard_mode + _sapp.x11.window, // confine_to + _sapp.x11.hidden_cursor, // cursor + CurrentTime); // time + } + else { + if (_sapp.x11.xi.available) { + XIEventMask em; + unsigned char mask[] = { 0 }; + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + XISelectEvents(_sapp.x11.display, _sapp.x11.root, &em, 1); + } + XWarpPointer(_sapp.x11.display, None, _sapp.x11.window, 0, 0, 0, 0, (int) _sapp.mouse.x, _sapp.mouse.y); + XUngrabPointer(_sapp.x11.display, CurrentTime); + } + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE void _sapp_x11_update_window_title(void) { + Xutf8SetWMProperties(_sapp.x11.display, + _sapp.x11.window, + _sapp.window_title, _sapp.window_title, + NULL, 0, NULL, NULL, NULL); + XChangeProperty(_sapp.x11.display, _sapp.x11.window, + _sapp.x11.NET_WM_NAME, _sapp.x11.UTF8_STRING, 8, + PropModeReplace, + (unsigned char*)_sapp.window_title, + strlen(_sapp.window_title)); + XChangeProperty(_sapp.x11.display, _sapp.x11.window, + _sapp.x11.NET_WM_ICON_NAME, _sapp.x11.UTF8_STRING, 8, + PropModeReplace, + (unsigned char*)_sapp.window_title, + strlen(_sapp.window_title)); + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE void _sapp_x11_set_icon(const sapp_icon_desc* icon_desc, int num_images) { + SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES)); + int long_count = 0; + for (int i = 0; i < num_images; i++) { + const sapp_image_desc* img_desc = &icon_desc->images[i]; + long_count += 2 + (img_desc->width * img_desc->height); + } + long* icon_data = (long*) _sapp_malloc_clear((size_t)long_count * sizeof(long)); + SOKOL_ASSERT(icon_data); + long* dst = icon_data; + for (int img_index = 0; img_index < num_images; img_index++) { + const sapp_image_desc* img_desc = &icon_desc->images[img_index]; + const uint8_t* src = (const uint8_t*) img_desc->pixels.ptr; + *dst++ = img_desc->width; + *dst++ = img_desc->height; + const int num_pixels = img_desc->width * img_desc->height; + for (int pixel_index = 0; pixel_index < num_pixels; pixel_index++) { + *dst++ = ((long)(src[pixel_index * 4 + 0]) << 16) | + ((long)(src[pixel_index * 4 + 1]) << 8) | + ((long)(src[pixel_index * 4 + 2]) << 0) | + ((long)(src[pixel_index * 4 + 3]) << 24); + } + } + XChangeProperty(_sapp.x11.display, _sapp.x11.window, + _sapp.x11.NET_WM_ICON, + XA_CARDINAL, 32, + PropModeReplace, + (unsigned char*)icon_data, + long_count); + _sapp_free(icon_data); + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { + _sapp.x11.colormap = XCreateColormap(_sapp.x11.display, _sapp.x11.root, visual, AllocNone); + XSetWindowAttributes wa; + _sapp_clear(&wa, sizeof(wa)); + const uint32_t wamask = CWBorderPixel | CWColormap | CWEventMask; + wa.colormap = _sapp.x11.colormap; + wa.border_pixel = 0; + wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | + PointerMotionMask | ButtonPressMask | ButtonReleaseMask | + ExposureMask | FocusChangeMask | VisibilityChangeMask | + EnterWindowMask | LeaveWindowMask | PropertyChangeMask; + + int display_width = DisplayWidth(_sapp.x11.display, _sapp.x11.screen); + int display_height = DisplayHeight(_sapp.x11.display, _sapp.x11.screen); + int window_width = _sapp.window_width; + int window_height = _sapp.window_height; + if (0 == window_width) { + window_width = (display_width * 4) / 5; + } + if (0 == window_height) { + window_height = (display_height * 4) / 5; + } + int window_xpos = (display_width - window_width) / 2; + int window_ypos = (display_height - window_height) / 2; + if (window_xpos < 0) { + window_xpos = 0; + } + if (window_ypos < 0) { + window_ypos = 0; + } + _sapp_x11_grab_error_handler(); + _sapp.x11.window = XCreateWindow(_sapp.x11.display, + _sapp.x11.root, + window_xpos, + window_ypos, + (uint32_t)window_width, + (uint32_t)window_height, + 0, /* border width */ + depth, /* color depth */ + InputOutput, + visual, + wamask, + &wa); + _sapp_x11_release_error_handler(); + if (!_sapp.x11.window) { + _SAPP_PANIC(LINUX_X11_CREATE_WINDOW_FAILED); + } + Atom protocols[] = { + _sapp.x11.WM_DELETE_WINDOW + }; + XSetWMProtocols(_sapp.x11.display, _sapp.x11.window, protocols, 1); + + XSizeHints* hints = XAllocSizeHints(); + hints->flags = (PWinGravity | PPosition | PSize); + hints->win_gravity = StaticGravity; + hints->x = window_xpos; + hints->y = window_ypos; + hints->width = window_width; + hints->height = window_height; + XSetWMNormalHints(_sapp.x11.display, _sapp.x11.window, hints); + XFree(hints); + + /* announce support for drag'n'drop */ + if (_sapp.drop.enabled) { + const Atom version = _SAPP_X11_XDND_VERSION; + XChangeProperty(_sapp.x11.display, _sapp.x11.window, _sapp.x11.xdnd.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*) &version, 1); + } + _sapp_x11_update_window_title(); + _sapp_x11_query_window_size(); +} + +_SOKOL_PRIVATE void _sapp_x11_destroy_window(void) { + if (_sapp.x11.window) { + XUnmapWindow(_sapp.x11.display, _sapp.x11.window); + XDestroyWindow(_sapp.x11.display, _sapp.x11.window); + _sapp.x11.window = 0; + } + if (_sapp.x11.colormap) { + XFreeColormap(_sapp.x11.display, _sapp.x11.colormap); + _sapp.x11.colormap = 0; + } + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE bool _sapp_x11_window_visible(void) { + XWindowAttributes wa; + XGetWindowAttributes(_sapp.x11.display, _sapp.x11.window, &wa); + return wa.map_state == IsViewable; +} + +_SOKOL_PRIVATE void _sapp_x11_show_window(void) { + if (!_sapp_x11_window_visible()) { + XMapWindow(_sapp.x11.display, _sapp.x11.window); + XRaiseWindow(_sapp.x11.display, _sapp.x11.window); + XFlush(_sapp.x11.display); + } +} + +_SOKOL_PRIVATE void _sapp_x11_hide_window(void) { + XUnmapWindow(_sapp.x11.display, _sapp.x11.window); + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE unsigned long _sapp_x11_get_window_property(Window window, Atom property, Atom type, unsigned char** value) { + Atom actualType; + int actualFormat; + unsigned long itemCount, bytesAfter; + XGetWindowProperty(_sapp.x11.display, + window, + property, + 0, + LONG_MAX, + False, + type, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + value); + return itemCount; +} + +_SOKOL_PRIVATE int _sapp_x11_get_window_state(void) { + int result = WithdrawnState; + struct { + CARD32 state; + Window icon; + } *state = NULL; + + if (_sapp_x11_get_window_property(_sapp.x11.window, _sapp.x11.WM_STATE, _sapp.x11.WM_STATE, (unsigned char**)&state) >= 2) { + result = (int)state->state; + } + if (state) { + XFree(state); + } + return result; +} + +_SOKOL_PRIVATE uint32_t _sapp_x11_key_modifier_bit(sapp_keycode key) { + switch (key) { + case SAPP_KEYCODE_LEFT_SHIFT: + case SAPP_KEYCODE_RIGHT_SHIFT: + return SAPP_MODIFIER_SHIFT; + case SAPP_KEYCODE_LEFT_CONTROL: + case SAPP_KEYCODE_RIGHT_CONTROL: + return SAPP_MODIFIER_CTRL; + case SAPP_KEYCODE_LEFT_ALT: + case SAPP_KEYCODE_RIGHT_ALT: + return SAPP_MODIFIER_ALT; + case SAPP_KEYCODE_LEFT_SUPER: + case SAPP_KEYCODE_RIGHT_SUPER: + return SAPP_MODIFIER_SUPER; + default: + return 0; + } +} + +_SOKOL_PRIVATE uint32_t _sapp_x11_button_modifier_bit(sapp_mousebutton btn) { + switch (btn) { + case SAPP_MOUSEBUTTON_LEFT: return SAPP_MODIFIER_LMB; + case SAPP_MOUSEBUTTON_RIGHT: return SAPP_MODIFIER_RMB; + case SAPP_MOUSEBUTTON_MIDDLE: return SAPP_MODIFIER_MMB; + default: return 0; + } +} + +_SOKOL_PRIVATE uint32_t _sapp_x11_mods(uint32_t x11_mods) { + uint32_t mods = 0; + if (x11_mods & ShiftMask) { + mods |= SAPP_MODIFIER_SHIFT; + } + if (x11_mods & ControlMask) { + mods |= SAPP_MODIFIER_CTRL; + } + if (x11_mods & Mod1Mask) { + mods |= SAPP_MODIFIER_ALT; + } + if (x11_mods & Mod4Mask) { + mods |= SAPP_MODIFIER_SUPER; + } + if (x11_mods & Button1Mask) { + mods |= SAPP_MODIFIER_LMB; + } + if (x11_mods & Button2Mask) { + mods |= SAPP_MODIFIER_MMB; + } + if (x11_mods & Button3Mask) { + mods |= SAPP_MODIFIER_RMB; + } + return mods; +} + +_SOKOL_PRIVATE void _sapp_x11_app_event(sapp_event_type type) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE sapp_mousebutton _sapp_x11_translate_button(const XEvent* event) { + switch (event->xbutton.button) { + case Button1: return SAPP_MOUSEBUTTON_LEFT; + case Button2: return SAPP_MOUSEBUTTON_MIDDLE; + case Button3: return SAPP_MOUSEBUTTON_RIGHT; + default: return SAPP_MOUSEBUTTON_INVALID; + } +} + +_SOKOL_PRIVATE void _sapp_x11_mouse_update(int x, int y, bool clear_dxdy) { + if (!_sapp.mouse.locked) { + const float new_x = (float) x; + const float new_y = (float) y; + if (clear_dxdy) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + } else if (_sapp.mouse.pos_valid) { + _sapp.mouse.dx = new_x - _sapp.mouse.x; + _sapp.mouse.dy = new_y - _sapp.mouse.y; + } + _sapp.mouse.x = new_x; + _sapp.mouse.y = new_y; + _sapp.mouse.pos_valid = true; + } +} + +_SOKOL_PRIVATE void _sapp_x11_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mods) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp.event.mouse_button = btn; + _sapp.event.modifiers = mods; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_x11_scroll_event(float x, float y, uint32_t mods) { + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + _sapp.event.modifiers = mods; + _sapp.event.scroll_x = x; + _sapp.event.scroll_y = y; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_x11_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mods) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp.event.key_code = key; + _sapp.event.key_repeat = repeat; + _sapp.event.modifiers = mods; + _sapp_call_event(&_sapp.event); + /* check if a CLIPBOARD_PASTED event must be sent too */ + if (_sapp.clipboard.enabled && + (type == SAPP_EVENTTYPE_KEY_DOWN) && + (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) && + (_sapp.event.key_code == SAPP_KEYCODE_V)) + { + _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); + _sapp_call_event(&_sapp.event); + } + } +} + +_SOKOL_PRIVATE void _sapp_x11_char_event(uint32_t chr, bool repeat, uint32_t mods) { + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp.event.char_code = chr; + _sapp.event.key_repeat = repeat; + _sapp.event.modifiers = mods; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE sapp_keycode _sapp_x11_translate_key(int scancode) { + int dummy; + KeySym* keysyms = XGetKeyboardMapping(_sapp.x11.display, scancode, 1, &dummy); + SOKOL_ASSERT(keysyms); + KeySym keysym = keysyms[0]; + XFree(keysyms); + switch (keysym) { + case XK_Escape: return SAPP_KEYCODE_ESCAPE; + case XK_Tab: return SAPP_KEYCODE_TAB; + case XK_Shift_L: return SAPP_KEYCODE_LEFT_SHIFT; + case XK_Shift_R: return SAPP_KEYCODE_RIGHT_SHIFT; + case XK_Control_L: return SAPP_KEYCODE_LEFT_CONTROL; + case XK_Control_R: return SAPP_KEYCODE_RIGHT_CONTROL; + case XK_Meta_L: + case XK_Alt_L: return SAPP_KEYCODE_LEFT_ALT; + case XK_Mode_switch: /* Mapped to Alt_R on many keyboards */ + case XK_ISO_Level3_Shift: /* AltGr on at least some machines */ + case XK_Meta_R: + case XK_Alt_R: return SAPP_KEYCODE_RIGHT_ALT; + case XK_Super_L: return SAPP_KEYCODE_LEFT_SUPER; + case XK_Super_R: return SAPP_KEYCODE_RIGHT_SUPER; + case XK_Menu: return SAPP_KEYCODE_MENU; + case XK_Num_Lock: return SAPP_KEYCODE_NUM_LOCK; + case XK_Caps_Lock: return SAPP_KEYCODE_CAPS_LOCK; + case XK_Print: return SAPP_KEYCODE_PRINT_SCREEN; + case XK_Scroll_Lock: return SAPP_KEYCODE_SCROLL_LOCK; + case XK_Pause: return SAPP_KEYCODE_PAUSE; + case XK_Delete: return SAPP_KEYCODE_DELETE; + case XK_BackSpace: return SAPP_KEYCODE_BACKSPACE; + case XK_Return: return SAPP_KEYCODE_ENTER; + case XK_Home: return SAPP_KEYCODE_HOME; + case XK_End: return SAPP_KEYCODE_END; + case XK_Page_Up: return SAPP_KEYCODE_PAGE_UP; + case XK_Page_Down: return SAPP_KEYCODE_PAGE_DOWN; + case XK_Insert: return SAPP_KEYCODE_INSERT; + case XK_Left: return SAPP_KEYCODE_LEFT; + case XK_Right: return SAPP_KEYCODE_RIGHT; + case XK_Down: return SAPP_KEYCODE_DOWN; + case XK_Up: return SAPP_KEYCODE_UP; + case XK_F1: return SAPP_KEYCODE_F1; + case XK_F2: return SAPP_KEYCODE_F2; + case XK_F3: return SAPP_KEYCODE_F3; + case XK_F4: return SAPP_KEYCODE_F4; + case XK_F5: return SAPP_KEYCODE_F5; + case XK_F6: return SAPP_KEYCODE_F6; + case XK_F7: return SAPP_KEYCODE_F7; + case XK_F8: return SAPP_KEYCODE_F8; + case XK_F9: return SAPP_KEYCODE_F9; + case XK_F10: return SAPP_KEYCODE_F10; + case XK_F11: return SAPP_KEYCODE_F11; + case XK_F12: return SAPP_KEYCODE_F12; + case XK_F13: return SAPP_KEYCODE_F13; + case XK_F14: return SAPP_KEYCODE_F14; + case XK_F15: return SAPP_KEYCODE_F15; + case XK_F16: return SAPP_KEYCODE_F16; + case XK_F17: return SAPP_KEYCODE_F17; + case XK_F18: return SAPP_KEYCODE_F18; + case XK_F19: return SAPP_KEYCODE_F19; + case XK_F20: return SAPP_KEYCODE_F20; + case XK_F21: return SAPP_KEYCODE_F21; + case XK_F22: return SAPP_KEYCODE_F22; + case XK_F23: return SAPP_KEYCODE_F23; + case XK_F24: return SAPP_KEYCODE_F24; + case XK_F25: return SAPP_KEYCODE_F25; + + case XK_KP_Divide: return SAPP_KEYCODE_KP_DIVIDE; + case XK_KP_Multiply: return SAPP_KEYCODE_KP_MULTIPLY; + case XK_KP_Subtract: return SAPP_KEYCODE_KP_SUBTRACT; + case XK_KP_Add: return SAPP_KEYCODE_KP_ADD; + + case XK_KP_Insert: return SAPP_KEYCODE_KP_0; + case XK_KP_End: return SAPP_KEYCODE_KP_1; + case XK_KP_Down: return SAPP_KEYCODE_KP_2; + case XK_KP_Page_Down: return SAPP_KEYCODE_KP_3; + case XK_KP_Left: return SAPP_KEYCODE_KP_4; + case XK_KP_Begin: return SAPP_KEYCODE_KP_5; + case XK_KP_Right: return SAPP_KEYCODE_KP_6; + case XK_KP_Home: return SAPP_KEYCODE_KP_7; + case XK_KP_Up: return SAPP_KEYCODE_KP_8; + case XK_KP_Page_Up: return SAPP_KEYCODE_KP_9; + case XK_KP_Delete: return SAPP_KEYCODE_KP_DECIMAL; + case XK_KP_Equal: return SAPP_KEYCODE_KP_EQUAL; + case XK_KP_Enter: return SAPP_KEYCODE_KP_ENTER; + + case XK_a: return SAPP_KEYCODE_A; + case XK_b: return SAPP_KEYCODE_B; + case XK_c: return SAPP_KEYCODE_C; + case XK_d: return SAPP_KEYCODE_D; + case XK_e: return SAPP_KEYCODE_E; + case XK_f: return SAPP_KEYCODE_F; + case XK_g: return SAPP_KEYCODE_G; + case XK_h: return SAPP_KEYCODE_H; + case XK_i: return SAPP_KEYCODE_I; + case XK_j: return SAPP_KEYCODE_J; + case XK_k: return SAPP_KEYCODE_K; + case XK_l: return SAPP_KEYCODE_L; + case XK_m: return SAPP_KEYCODE_M; + case XK_n: return SAPP_KEYCODE_N; + case XK_o: return SAPP_KEYCODE_O; + case XK_p: return SAPP_KEYCODE_P; + case XK_q: return SAPP_KEYCODE_Q; + case XK_r: return SAPP_KEYCODE_R; + case XK_s: return SAPP_KEYCODE_S; + case XK_t: return SAPP_KEYCODE_T; + case XK_u: return SAPP_KEYCODE_U; + case XK_v: return SAPP_KEYCODE_V; + case XK_w: return SAPP_KEYCODE_W; + case XK_x: return SAPP_KEYCODE_X; + case XK_y: return SAPP_KEYCODE_Y; + case XK_z: return SAPP_KEYCODE_Z; + case XK_1: return SAPP_KEYCODE_1; + case XK_2: return SAPP_KEYCODE_2; + case XK_3: return SAPP_KEYCODE_3; + case XK_4: return SAPP_KEYCODE_4; + case XK_5: return SAPP_KEYCODE_5; + case XK_6: return SAPP_KEYCODE_6; + case XK_7: return SAPP_KEYCODE_7; + case XK_8: return SAPP_KEYCODE_8; + case XK_9: return SAPP_KEYCODE_9; + case XK_0: return SAPP_KEYCODE_0; + case XK_space: return SAPP_KEYCODE_SPACE; + case XK_minus: return SAPP_KEYCODE_MINUS; + case XK_equal: return SAPP_KEYCODE_EQUAL; + case XK_bracketleft: return SAPP_KEYCODE_LEFT_BRACKET; + case XK_bracketright: return SAPP_KEYCODE_RIGHT_BRACKET; + case XK_backslash: return SAPP_KEYCODE_BACKSLASH; + case XK_semicolon: return SAPP_KEYCODE_SEMICOLON; + case XK_apostrophe: return SAPP_KEYCODE_APOSTROPHE; + case XK_grave: return SAPP_KEYCODE_GRAVE_ACCENT; + case XK_comma: return SAPP_KEYCODE_COMMA; + case XK_period: return SAPP_KEYCODE_PERIOD; + case XK_slash: return SAPP_KEYCODE_SLASH; + case XK_less: return SAPP_KEYCODE_WORLD_1; /* At least in some layouts... */ + default: return SAPP_KEYCODE_INVALID; + } +} + +_SOKOL_PRIVATE int32_t _sapp_x11_keysym_to_unicode(KeySym keysym) { + int min = 0; + int max = sizeof(_sapp_x11_keysymtab) / sizeof(struct _sapp_x11_codepair) - 1; + int mid; + + /* First check for Latin-1 characters (1:1 mapping) */ + if ((keysym >= 0x0020 && keysym <= 0x007e) || + (keysym >= 0x00a0 && keysym <= 0x00ff)) + { + return keysym; + } + + /* Also check for directly encoded 24-bit UCS characters */ + if ((keysym & 0xff000000) == 0x01000000) { + return keysym & 0x00ffffff; + } + + /* Binary search in table */ + while (max >= min) { + mid = (min + max) / 2; + if (_sapp_x11_keysymtab[mid].keysym < keysym) { + min = mid + 1; + } + else if (_sapp_x11_keysymtab[mid].keysym > keysym) { + max = mid - 1; + } + else { + return _sapp_x11_keysymtab[mid].ucs; + } + } + + /* No matching Unicode value found */ + return -1; +} + +_SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { + SOKOL_ASSERT(src); + SOKOL_ASSERT(_sapp.drop.buffer); + + _sapp_clear_drop_buffer(); + _sapp.drop.num_files = 0; + + /* + src is (potentially percent-encoded) string made of one or multiple paths + separated by \r\n, each path starting with 'file://' + */ + bool err = false; + int src_count = 0; + char src_chr = 0; + char* dst_ptr = _sapp.drop.buffer; + const char* dst_end_ptr = dst_ptr + (_sapp.drop.max_path_length - 1); // room for terminating 0 + while (0 != (src_chr = *src++)) { + src_count++; + char dst_chr = 0; + /* check leading 'file://' */ + if (src_count <= 7) { + if (((src_count == 1) && (src_chr != 'f')) || + ((src_count == 2) && (src_chr != 'i')) || + ((src_count == 3) && (src_chr != 'l')) || + ((src_count == 4) && (src_chr != 'e')) || + ((src_count == 5) && (src_chr != ':')) || + ((src_count == 6) && (src_chr != '/')) || + ((src_count == 7) && (src_chr != '/'))) + { + _SAPP_ERROR(LINUX_X11_DROPPED_FILE_URI_WRONG_SCHEME); + err = true; + break; + } + } + else if (src_chr == '\r') { + // skip + } + else if (src_chr == '\n') { + src_count = 0; + _sapp.drop.num_files++; + // too many files is not an error + if (_sapp.drop.num_files >= _sapp.drop.max_files) { + break; + } + dst_ptr = _sapp.drop.buffer + _sapp.drop.num_files * _sapp.drop.max_path_length; + dst_end_ptr = dst_ptr + (_sapp.drop.max_path_length - 1); + } + else if ((src_chr == '%') && src[0] && src[1]) { + // a percent-encoded byte (most likely UTF-8 multibyte sequence) + const char digits[3] = { src[0], src[1], 0 }; + src += 2; + dst_chr = (char) strtol(digits, 0, 16); + } + else { + dst_chr = src_chr; + } + if (dst_chr) { + // dst_end_ptr already has adjustment for terminating zero + if (dst_ptr < dst_end_ptr) { + *dst_ptr++ = dst_chr; + } + else { + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); + err = true; + break; + } + } + } + if (err) { + _sapp_clear_drop_buffer(); + _sapp.drop.num_files = 0; + return false; + } + else { + return true; + } +} + +// XLib manual says keycodes are in the range [8, 255] inclusive. +// https://tronche.com/gui/x/xlib/input/keyboard-encoding.html +static bool _sapp_x11_keycodes[256]; + +_SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { + Bool filtered = XFilterEvent(event, None); + switch (event->type) { + case GenericEvent: + if (_sapp.mouse.locked && _sapp.x11.xi.available) { + if (event->xcookie.extension == _sapp.x11.xi.major_opcode) { + if (XGetEventData(_sapp.x11.display, &event->xcookie)) { + if (event->xcookie.evtype == XI_RawMotion) { + XIRawEvent* re = (XIRawEvent*) event->xcookie.data; + if (re->valuators.mask_len) { + const double* values = re->raw_values; + if (XIMaskIsSet(re->valuators.mask, 0)) { + _sapp.mouse.dx = (float) *values; + values++; + } + if (XIMaskIsSet(re->valuators.mask, 1)) { + _sapp.mouse.dy = (float) *values; + } + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state)); + } + } + XFreeEventData(_sapp.x11.display, &event->xcookie); + } + } + } + break; + case FocusIn: + // NOTE: ignoring NotifyGrab and NotifyUngrab is same behaviour as GLFW + if ((event->xfocus.mode != NotifyGrab) && (event->xfocus.mode != NotifyUngrab)) { + _sapp_x11_app_event(SAPP_EVENTTYPE_FOCUSED); + } + break; + case FocusOut: + /* if focus is lost for any reason, and we're in mouse locked mode, disable mouse lock */ + if (_sapp.mouse.locked) { + _sapp_x11_lock_mouse(false); + } + // NOTE: ignoring NotifyGrab and NotifyUngrab is same behaviour as GLFW + if ((event->xfocus.mode != NotifyGrab) && (event->xfocus.mode != NotifyUngrab)) { + _sapp_x11_app_event(SAPP_EVENTTYPE_UNFOCUSED); + } + break; + case KeyPress: + { + int keycode = (int)event->xkey.keycode; + const sapp_keycode key = _sapp_x11_translate_key(keycode); + bool repeat = _sapp_x11_keycodes[keycode & 0xFF]; + _sapp_x11_keycodes[keycode & 0xFF] = true; + uint32_t mods = _sapp_x11_mods(event->xkey.state); + // X11 doesn't set modifier bit on key down, so emulate that + mods |= _sapp_x11_key_modifier_bit(key); + if (key != SAPP_KEYCODE_INVALID) { + _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_DOWN, key, repeat, mods); + } + KeySym keysym; + XLookupString(&event->xkey, NULL, 0, &keysym, NULL); + int32_t chr = _sapp_x11_keysym_to_unicode(keysym); + if (chr > 0) { + _sapp_x11_char_event((uint32_t)chr, repeat, mods); + } + } + break; + case KeyRelease: + { + int keycode = (int)event->xkey.keycode; + const sapp_keycode key = _sapp_x11_translate_key(keycode); + _sapp_x11_keycodes[keycode & 0xFF] = false; + if (key != SAPP_KEYCODE_INVALID) { + uint32_t mods = _sapp_x11_mods(event->xkey.state); + // X11 doesn't clear modifier bit on key up, so emulate that + mods &= ~_sapp_x11_key_modifier_bit(key); + _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_UP, key, false, mods); + } + } + break; + case ButtonPress: + { + _sapp_x11_mouse_update(event->xbutton.x, event->xbutton.y, false); + const sapp_mousebutton btn = _sapp_x11_translate_button(event); + uint32_t mods = _sapp_x11_mods(event->xbutton.state); + // X11 doesn't set modifier bit on button down, so emulate that + mods |= _sapp_x11_button_modifier_bit(btn); + if (btn != SAPP_MOUSEBUTTON_INVALID) { + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, btn, mods); + _sapp.x11.mouse_buttons |= (1 << btn); + } + else { + /* might be a scroll event */ + switch (event->xbutton.button) { + case 4: _sapp_x11_scroll_event(0.0f, 1.0f, mods); break; + case 5: _sapp_x11_scroll_event(0.0f, -1.0f, mods); break; + case 6: _sapp_x11_scroll_event(1.0f, 0.0f, mods); break; + case 7: _sapp_x11_scroll_event(-1.0f, 0.0f, mods); break; + } + } + } + break; + case ButtonRelease: + { + _sapp_x11_mouse_update(event->xbutton.x, event->xbutton.y, false); + const sapp_mousebutton btn = _sapp_x11_translate_button(event); + if (btn != SAPP_MOUSEBUTTON_INVALID) { + uint32_t mods = _sapp_x11_mods(event->xbutton.state); + // X11 doesn't clear modifier bit on button up, so emulate that + mods &= ~_sapp_x11_button_modifier_bit(btn); + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, btn, mods); + _sapp.x11.mouse_buttons &= ~(1 << btn); + } + } + break; + case EnterNotify: + /* don't send enter/leave events while mouse button held down */ + if (0 == _sapp.x11.mouse_buttons) { + _sapp_x11_mouse_update(event->xcrossing.x, event->xcrossing.y, true); + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state)); + } + break; + case LeaveNotify: + if (0 == _sapp.x11.mouse_buttons) { + _sapp_x11_mouse_update(event->xcrossing.x, event->xcrossing.y, true); + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state)); + } + break; + case MotionNotify: + if (!_sapp.mouse.locked) { + _sapp_x11_mouse_update(event->xmotion.x, event->xmotion.y, false); + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state)); + } + break; + case ConfigureNotify: + if ((event->xconfigure.width != _sapp.window_width) || (event->xconfigure.height != _sapp.window_height)) { + _sapp.window_width = event->xconfigure.width; + _sapp.window_height = event->xconfigure.height; + _sapp.framebuffer_width = _sapp.window_width; + _sapp.framebuffer_height = _sapp.window_height; + _sapp_x11_app_event(SAPP_EVENTTYPE_RESIZED); + } + break; + case PropertyNotify: + if (event->xproperty.state == PropertyNewValue) { + if (event->xproperty.atom == _sapp.x11.WM_STATE) { + const int state = _sapp_x11_get_window_state(); + if (state != _sapp.x11.window_state) { + _sapp.x11.window_state = state; + if (state == IconicState) { + _sapp_x11_app_event(SAPP_EVENTTYPE_ICONIFIED); + } + else if (state == NormalState) { + _sapp_x11_app_event(SAPP_EVENTTYPE_RESTORED); + } + } + } + } + break; + case ClientMessage: + if (filtered) { + return; + } + if (event->xclient.message_type == _sapp.x11.WM_PROTOCOLS) { + const Atom protocol = (Atom)event->xclient.data.l[0]; + if (protocol == _sapp.x11.WM_DELETE_WINDOW) { + _sapp.quit_requested = true; + } + } + else if (event->xclient.message_type == _sapp.x11.xdnd.XdndEnter) { + const bool is_list = 0 != (event->xclient.data.l[1] & 1); + _sapp.x11.xdnd.source = (Window)event->xclient.data.l[0]; + _sapp.x11.xdnd.version = event->xclient.data.l[1] >> 24; + _sapp.x11.xdnd.format = None; + if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { + return; + } + uint32_t count = 0; + Atom* formats = 0; + if (is_list) { + count = _sapp_x11_get_window_property(_sapp.x11.xdnd.source, _sapp.x11.xdnd.XdndTypeList, XA_ATOM, (unsigned char**)&formats); + } + else { + count = 3; + formats = (Atom*) event->xclient.data.l + 2; + } + for (uint32_t i = 0; i < count; i++) { + if (formats[i] == _sapp.x11.xdnd.text_uri_list) { + _sapp.x11.xdnd.format = _sapp.x11.xdnd.text_uri_list; + break; + } + } + if (is_list && formats) { + XFree(formats); + } + } + else if (event->xclient.message_type == _sapp.x11.xdnd.XdndDrop) { + if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { + return; + } + Time time = CurrentTime; + if (_sapp.x11.xdnd.format) { + if (_sapp.x11.xdnd.version >= 1) { + time = (Time)event->xclient.data.l[2]; + } + XConvertSelection(_sapp.x11.display, + _sapp.x11.xdnd.XdndSelection, + _sapp.x11.xdnd.format, + _sapp.x11.xdnd.XdndSelection, + _sapp.x11.window, + time); + } + else if (_sapp.x11.xdnd.version >= 2) { + XEvent reply; + _sapp_clear(&reply, sizeof(reply)); + reply.type = ClientMessage; + reply.xclient.window = _sapp.x11.xdnd.source; + reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished; + reply.xclient.format = 32; + reply.xclient.data.l[0] = (long)_sapp.x11.window; + reply.xclient.data.l[1] = 0; // drag was rejected + reply.xclient.data.l[2] = None; + XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); + XFlush(_sapp.x11.display); + } + } + else if (event->xclient.message_type == _sapp.x11.xdnd.XdndPosition) { + /* drag operation has moved over the window + FIXME: we could track the mouse position here, but + this isn't implemented on other platforms either so far + */ + if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { + return; + } + XEvent reply; + _sapp_clear(&reply, sizeof(reply)); + reply.type = ClientMessage; + reply.xclient.window = _sapp.x11.xdnd.source; + reply.xclient.message_type = _sapp.x11.xdnd.XdndStatus; + reply.xclient.format = 32; + reply.xclient.data.l[0] = (long)_sapp.x11.window; + if (_sapp.x11.xdnd.format) { + /* reply that we are ready to copy the dragged data */ + reply.xclient.data.l[1] = 1; // accept with no rectangle + if (_sapp.x11.xdnd.version >= 2) { + reply.xclient.data.l[4] = (long)_sapp.x11.xdnd.XdndActionCopy; + } + } + XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); + XFlush(_sapp.x11.display); + } + break; + case SelectionNotify: + if (event->xselection.property == _sapp.x11.xdnd.XdndSelection) { + char* data = 0; + uint32_t result = _sapp_x11_get_window_property(event->xselection.requestor, + event->xselection.property, + event->xselection.target, + (unsigned char**) &data); + if (_sapp.drop.enabled && result) { + if (_sapp_x11_parse_dropped_files_list(data)) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + if (_sapp_events_enabled()) { + // FIXME: Figure out how to get modifier key state here. + // The XSelection event has no 'state' item, and + // XQueryKeymap() always returns a zeroed array. + _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp_call_event(&_sapp.event); + } + } + } + if (_sapp.x11.xdnd.version >= 2) { + XEvent reply; + _sapp_clear(&reply, sizeof(reply)); + reply.type = ClientMessage; + reply.xclient.window = _sapp.x11.xdnd.source; + reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished; + reply.xclient.format = 32; + reply.xclient.data.l[0] = (long)_sapp.x11.window; + reply.xclient.data.l[1] = result; + reply.xclient.data.l[2] = (long)_sapp.x11.xdnd.XdndActionCopy; + XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); + XFlush(_sapp.x11.display); + } + } + break; + case DestroyNotify: + break; + } +} + +#if !defined(_SAPP_GLX) + +_SOKOL_PRIVATE void _sapp_egl_init(void) { +#if defined(SOKOL_GLCORE) + if (!eglBindAPI(EGL_OPENGL_API)) { + _SAPP_PANIC(LINUX_EGL_BIND_OPENGL_API_FAILED); + } +#else + if (!eglBindAPI(EGL_OPENGL_ES_API)) { + _SAPP_PANIC(LINUX_EGL_BIND_OPENGL_ES_API_FAILED); + } +#endif + + _sapp.egl.display = eglGetDisplay((EGLNativeDisplayType)_sapp.x11.display); + if (EGL_NO_DISPLAY == _sapp.egl.display) { + _SAPP_PANIC(LINUX_EGL_GET_DISPLAY_FAILED); + } + + EGLint major, minor; + if (!eglInitialize(_sapp.egl.display, &major, &minor)) { + _SAPP_PANIC(LINUX_EGL_INITIALIZE_FAILED); + } + + EGLint sample_count = _sapp.desc.sample_count > 1 ? _sapp.desc.sample_count : 0; + EGLint alpha_size = _sapp.desc.alpha ? 8 : 0; + const EGLint config_attrs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + #if defined(SOKOL_GLCORE) + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + #elif defined(SOKOL_GLES3) + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, + #endif + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, alpha_size, + EGL_DEPTH_SIZE, 24, + EGL_STENCIL_SIZE, 8, + EGL_SAMPLE_BUFFERS, _sapp.desc.sample_count > 1 ? 1 : 0, + EGL_SAMPLES, sample_count, + EGL_NONE, + }; + + EGLConfig egl_configs[32]; + EGLint config_count; + if (!eglChooseConfig(_sapp.egl.display, config_attrs, egl_configs, 32, &config_count) || config_count == 0) { + _SAPP_PANIC(LINUX_EGL_NO_CONFIGS); + } + + EGLConfig config = egl_configs[0]; + for (int i = 0; i < config_count; ++i) { + EGLConfig c = egl_configs[i]; + EGLint r, g, b, a, d, s, n; + if (eglGetConfigAttrib(_sapp.egl.display, c, EGL_RED_SIZE, &r) && + eglGetConfigAttrib(_sapp.egl.display, c, EGL_GREEN_SIZE, &g) && + eglGetConfigAttrib(_sapp.egl.display, c, EGL_BLUE_SIZE, &b) && + eglGetConfigAttrib(_sapp.egl.display, c, EGL_ALPHA_SIZE, &a) && + eglGetConfigAttrib(_sapp.egl.display, c, EGL_DEPTH_SIZE, &d) && + eglGetConfigAttrib(_sapp.egl.display, c, EGL_STENCIL_SIZE, &s) && + eglGetConfigAttrib(_sapp.egl.display, c, EGL_SAMPLES, &n) && + (r == 8) && (g == 8) && (b == 8) && (a == alpha_size) && (d == 24) && (s == 8) && (n == sample_count)) { + config = c; + break; + } + } + + EGLint visual_id; + if (!eglGetConfigAttrib(_sapp.egl.display, config, EGL_NATIVE_VISUAL_ID, &visual_id)) { + _SAPP_PANIC(LINUX_EGL_NO_NATIVE_VISUAL); + } + + XVisualInfo visual_info_template; + _sapp_clear(&visual_info_template, sizeof(visual_info_template)); + visual_info_template.visualid = (VisualID)visual_id; + + int num_visuals; + XVisualInfo* visual_info = XGetVisualInfo(_sapp.x11.display, VisualIDMask, &visual_info_template, &num_visuals); + if (!visual_info) { + _SAPP_PANIC(LINUX_EGL_GET_VISUAL_INFO_FAILED); + } + + _sapp_x11_create_window(visual_info->visual, visual_info->depth); + XFree(visual_info); + + _sapp.egl.surface = eglCreateWindowSurface(_sapp.egl.display, config, (EGLNativeWindowType)_sapp.x11.window, NULL); + if (EGL_NO_SURFACE == _sapp.egl.surface) { + _SAPP_PANIC(LINUX_EGL_CREATE_WINDOW_SURFACE_FAILED); + } + + EGLint ctx_attrs[] = { + #if defined(SOKOL_GLCORE) + EGL_CONTEXT_MAJOR_VERSION, _sapp.desc.gl_major_version, + EGL_CONTEXT_MINOR_VERSION, _sapp.desc.gl_minor_version, + EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + #elif defined(SOKOL_GLES3) + EGL_CONTEXT_CLIENT_VERSION, 3, + #endif + EGL_NONE, + }; + + _sapp.egl.context = eglCreateContext(_sapp.egl.display, config, EGL_NO_CONTEXT, ctx_attrs); + if (EGL_NO_CONTEXT == _sapp.egl.context) { + _SAPP_PANIC(LINUX_EGL_CREATE_CONTEXT_FAILED); + } + + if (!eglMakeCurrent(_sapp.egl.display, _sapp.egl.surface, _sapp.egl.surface, _sapp.egl.context)) { + _SAPP_PANIC(LINUX_EGL_MAKE_CURRENT_FAILED); + } + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_sapp.gl.framebuffer); + + eglSwapInterval(_sapp.egl.display, _sapp.swap_interval); +} + +_SOKOL_PRIVATE void _sapp_egl_destroy(void) { + if (_sapp.egl.display != EGL_NO_DISPLAY) { + eglMakeCurrent(_sapp.egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (_sapp.egl.context != EGL_NO_CONTEXT) { + eglDestroyContext(_sapp.egl.display, _sapp.egl.context); + _sapp.egl.context = EGL_NO_CONTEXT; + } + + if (_sapp.egl.surface != EGL_NO_SURFACE) { + eglDestroySurface(_sapp.egl.display, _sapp.egl.surface); + _sapp.egl.surface = EGL_NO_SURFACE; + } + + eglTerminate(_sapp.egl.display); + _sapp.egl.display = EGL_NO_DISPLAY; + } +} + +#endif /* _SAPP_GLX */ + +_SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { + /* The following lines are here to trigger a linker error instead of an + obscure runtime error if the user has forgotten to add -pthread to + the compiler or linker options. They have no other purpose. + */ + pthread_attr_t pthread_attr; + pthread_attr_init(&pthread_attr); + pthread_attr_destroy(&pthread_attr); + + _sapp_init_state(desc); + _sapp.x11.window_state = NormalState; + + XInitThreads(); + XrmInitialize(); + _sapp.x11.display = XOpenDisplay(NULL); + if (!_sapp.x11.display) { + _SAPP_PANIC(LINUX_X11_OPEN_DISPLAY_FAILED); + } + _sapp.x11.screen = DefaultScreen(_sapp.x11.display); + _sapp.x11.root = DefaultRootWindow(_sapp.x11.display); + XkbSetDetectableAutoRepeat(_sapp.x11.display, true, NULL); + _sapp_x11_query_system_dpi(); + _sapp.dpi_scale = _sapp.x11.dpi / 96.0f; + _sapp_x11_init_extensions(); + _sapp_x11_create_cursors(); +#if defined(_SAPP_GLX) + _sapp_glx_init(); + Visual* visual = 0; + int depth = 0; + _sapp_glx_choose_visual(&visual, &depth); + _sapp_x11_create_window(visual, depth); + _sapp_glx_create_context(); + _sapp_glx_swapinterval(_sapp.swap_interval); +#else + _sapp_egl_init(); +#endif + sapp_set_icon(&desc->icon); + _sapp.valid = true; + _sapp_x11_show_window(); + if (_sapp.fullscreen) { + _sapp_x11_set_fullscreen(true); + } + + XFlush(_sapp.x11.display); + while (!_sapp.quit_ordered) { + _sapp_timing_measure(&_sapp.timing); + int count = XPending(_sapp.x11.display); + while (count--) { + XEvent event; + XNextEvent(_sapp.x11.display, &event); + _sapp_x11_process_event(&event); + } + _sapp_frame(); +#if defined(_SAPP_GLX) + _sapp_glx_swap_buffers(); +#else + eglSwapBuffers(_sapp.egl.display, _sapp.egl.surface); +#endif + XFlush(_sapp.x11.display); + /* handle quit-requested, either from window or from sapp_request_quit() */ + if (_sapp.quit_requested && !_sapp.quit_ordered) { + /* give user code a chance to intervene */ + _sapp_x11_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); + /* if user code hasn't intervened, quit the app */ + if (_sapp.quit_requested) { + _sapp.quit_ordered = true; + } + } + } + _sapp_call_cleanup(); +#if defined(_SAPP_GLX) + _sapp_glx_destroy_context(); +#else + _sapp_egl_destroy(); +#endif + _sapp_x11_destroy_window(); + _sapp_x11_destroy_cursors(); + XCloseDisplay(_sapp.x11.display); + _sapp_discard_state(); +} + +#if !defined(SOKOL_NO_ENTRY) +int main(int argc, char* argv[]) { + sapp_desc desc = sokol_main(argc, argv); + _sapp_linux_run(&desc); + return 0; +} +#endif /* SOKOL_NO_ENTRY */ +#endif /* _SAPP_LINUX */ + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +#if defined(SOKOL_NO_ENTRY) +SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) { + SOKOL_ASSERT(desc); + #if defined(_SAPP_MACOS) + _sapp_macos_run(desc); + #elif defined(_SAPP_IOS) + _sapp_ios_run(desc); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_run(desc); + #elif defined(_SAPP_WIN32) + _sapp_win32_run(desc); + #elif defined(_SAPP_LINUX) + _sapp_linux_run(desc); + #else + #error "sapp_run() not supported on this platform" + #endif +} + +/* this is just a stub so the linker doesn't complain */ +sapp_desc sokol_main(int argc, char* argv[]) { + _SOKOL_UNUSED(argc); + _SOKOL_UNUSED(argv); + sapp_desc desc; + _sapp_clear(&desc, sizeof(desc)); + return desc; +} +#else +/* likewise, in normal mode, sapp_run() is just an empty stub */ +SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) { + _SOKOL_UNUSED(desc); +} +#endif + +SOKOL_API_IMPL bool sapp_isvalid(void) { + return _sapp.valid; +} + +SOKOL_API_IMPL void* sapp_userdata(void) { + return _sapp.desc.user_data; +} + +SOKOL_API_IMPL sapp_desc sapp_query_desc(void) { + return _sapp.desc; +} + +SOKOL_API_IMPL uint64_t sapp_frame_count(void) { + return _sapp.frame_count; +} + +SOKOL_API_IMPL double sapp_frame_duration(void) { + return _sapp_timing_get_avg(&_sapp.timing); +} + +SOKOL_API_IMPL int sapp_width(void) { + return (_sapp.framebuffer_width > 0) ? _sapp.framebuffer_width : 1; +} + +SOKOL_API_IMPL float sapp_widthf(void) { + return (float)sapp_width(); +} + +SOKOL_API_IMPL int sapp_height(void) { + return (_sapp.framebuffer_height > 0) ? _sapp.framebuffer_height : 1; +} + +SOKOL_API_IMPL float sapp_heightf(void) { + return (float)sapp_height(); +} + +SOKOL_API_IMPL int sapp_color_format(void) { + #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) + switch (_sapp.wgpu.render_format) { + case WGPUTextureFormat_RGBA8Unorm: + return _SAPP_PIXELFORMAT_RGBA8; + case WGPUTextureFormat_BGRA8Unorm: + return _SAPP_PIXELFORMAT_BGRA8; + default: + SOKOL_UNREACHABLE; + return 0; + } + #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) + return _SAPP_PIXELFORMAT_BGRA8; + #else + return _SAPP_PIXELFORMAT_RGBA8; + #endif +} + +SOKOL_API_IMPL int sapp_depth_format(void) { + return _SAPP_PIXELFORMAT_DEPTH_STENCIL; +} + +SOKOL_API_IMPL int sapp_sample_count(void) { + return _sapp.sample_count; +} + +SOKOL_API_IMPL bool sapp_high_dpi(void) { + return _sapp.desc.high_dpi && (_sapp.dpi_scale >= 1.5f); +} + +SOKOL_API_IMPL float sapp_dpi_scale(void) { + return _sapp.dpi_scale; +} + +SOKOL_API_IMPL const void* sapp_egl_get_display(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_ANDROID) + return _sapp.android.display; + #elif defined(_SAPP_LINUX) && !defined(_SAPP_GLX) + return _sapp.egl.display; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_egl_get_context(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_ANDROID) + return _sapp.android.context; + #elif defined(_SAPP_LINUX) && !defined(_SAPP_GLX) + return _sapp.egl.context; + #else + return 0; + #endif +} + +SOKOL_API_IMPL void sapp_show_keyboard(bool show) { + #if defined(_SAPP_IOS) + _sapp_ios_show_keyboard(show); + #elif defined(_SAPP_ANDROID) + _sapp_android_show_keyboard(show); + #else + _SOKOL_UNUSED(show); + #endif +} + +SOKOL_API_IMPL bool sapp_keyboard_shown(void) { + return _sapp.onscreen_keyboard_shown; +} + +SOKOL_API_IMPL bool sapp_is_fullscreen(void) { + return _sapp.fullscreen; +} + +SOKOL_API_IMPL void sapp_toggle_fullscreen(void) { + #if defined(_SAPP_MACOS) + _sapp_macos_toggle_fullscreen(); + #elif defined(_SAPP_WIN32) + _sapp_win32_toggle_fullscreen(); + #elif defined(_SAPP_LINUX) + _sapp_x11_toggle_fullscreen(); + #endif +} + +/* NOTE that sapp_show_mouse() does not "stack" like the Win32 or macOS API functions! */ +SOKOL_API_IMPL void sapp_show_mouse(bool show) { + if (_sapp.mouse.shown != show) { + #if defined(_SAPP_MACOS) + _sapp_macos_update_cursor(_sapp.mouse.current_cursor, show); + #elif defined(_SAPP_WIN32) + _sapp_win32_update_cursor(_sapp.mouse.current_cursor, show, false); + #elif defined(_SAPP_LINUX) + _sapp_x11_update_cursor(_sapp.mouse.current_cursor, show); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_update_cursor(_sapp.mouse.current_cursor, show); + #endif + _sapp.mouse.shown = show; + } +} + +SOKOL_API_IMPL bool sapp_mouse_shown(void) { + return _sapp.mouse.shown; +} + +SOKOL_API_IMPL void sapp_lock_mouse(bool lock) { + #if defined(_SAPP_MACOS) + _sapp_macos_lock_mouse(lock); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_lock_mouse(lock); + #elif defined(_SAPP_WIN32) + _sapp_win32_lock_mouse(lock); + #elif defined(_SAPP_LINUX) + _sapp_x11_lock_mouse(lock); + #else + _sapp.mouse.locked = lock; + #endif +} + +SOKOL_API_IMPL bool sapp_mouse_locked(void) { + return _sapp.mouse.locked; +} + +SOKOL_API_IMPL void sapp_set_mouse_cursor(sapp_mouse_cursor cursor) { + SOKOL_ASSERT((cursor >= 0) && (cursor < _SAPP_MOUSECURSOR_NUM)); + if (_sapp.mouse.current_cursor != cursor) { + #if defined(_SAPP_MACOS) + _sapp_macos_update_cursor(cursor, _sapp.mouse.shown); + #elif defined(_SAPP_WIN32) + _sapp_win32_update_cursor(cursor, _sapp.mouse.shown, false); + #elif defined(_SAPP_LINUX) + _sapp_x11_update_cursor(cursor, _sapp.mouse.shown); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_update_cursor(cursor, _sapp.mouse.shown); + #endif + _sapp.mouse.current_cursor = cursor; + } +} + +SOKOL_API_IMPL sapp_mouse_cursor sapp_get_mouse_cursor(void) { + return _sapp.mouse.current_cursor; +} + +SOKOL_API_IMPL void sapp_request_quit(void) { + _sapp.quit_requested = true; +} + +SOKOL_API_IMPL void sapp_cancel_quit(void) { + _sapp.quit_requested = false; +} + +SOKOL_API_IMPL void sapp_quit(void) { + _sapp.quit_ordered = true; +} + +SOKOL_API_IMPL void sapp_consume_event(void) { + _sapp.event_consumed = true; +} + +/* NOTE: on HTML5, sapp_set_clipboard_string() must be called from within event handler! */ +SOKOL_API_IMPL void sapp_set_clipboard_string(const char* str) { + if (!_sapp.clipboard.enabled) { + return; + } + SOKOL_ASSERT(str); + #if defined(_SAPP_MACOS) + _sapp_macos_set_clipboard_string(str); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_set_clipboard_string(str); + #elif defined(_SAPP_WIN32) + _sapp_win32_set_clipboard_string(str); + #else + /* not implemented */ + #endif + _sapp_strcpy(str, _sapp.clipboard.buffer, _sapp.clipboard.buf_size); +} + +SOKOL_API_IMPL const char* sapp_get_clipboard_string(void) { + if (!_sapp.clipboard.enabled) { + return ""; + } + #if defined(_SAPP_MACOS) + return _sapp_macos_get_clipboard_string(); + #elif defined(_SAPP_EMSCRIPTEN) + return _sapp.clipboard.buffer; + #elif defined(_SAPP_WIN32) + return _sapp_win32_get_clipboard_string(); + #else + /* not implemented */ + return _sapp.clipboard.buffer; + #endif +} + +SOKOL_API_IMPL void sapp_set_window_title(const char* title) { + SOKOL_ASSERT(title); + _sapp_strcpy(title, _sapp.window_title, sizeof(_sapp.window_title)); + #if defined(_SAPP_MACOS) + _sapp_macos_update_window_title(); + #elif defined(_SAPP_WIN32) + _sapp_win32_update_window_title(); + #elif defined(_SAPP_LINUX) + _sapp_x11_update_window_title(); + #endif +} + +SOKOL_API_IMPL void sapp_set_icon(const sapp_icon_desc* desc) { + SOKOL_ASSERT(desc); + if (desc->sokol_default) { + if (0 == _sapp.default_icon_pixels) { + _sapp_setup_default_icon(); + } + SOKOL_ASSERT(0 != _sapp.default_icon_pixels); + desc = &_sapp.default_icon_desc; + } + const int num_images = _sapp_icon_num_images(desc); + if (num_images == 0) { + return; + } + SOKOL_ASSERT((num_images > 0) && (num_images <= SAPP_MAX_ICONIMAGES)); + if (!_sapp_validate_icon_desc(desc, num_images)) { + return; + } + #if defined(_SAPP_MACOS) + _sapp_macos_set_icon(desc, num_images); + #elif defined(_SAPP_WIN32) + _sapp_win32_set_icon(desc, num_images); + #elif defined(_SAPP_LINUX) + _sapp_x11_set_icon(desc, num_images); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_set_icon(desc, num_images); + #endif +} + +SOKOL_API_IMPL int sapp_get_num_dropped_files(void) { + SOKOL_ASSERT(_sapp.drop.enabled); + return _sapp.drop.num_files; +} + +SOKOL_API_IMPL const char* sapp_get_dropped_file_path(int index) { + SOKOL_ASSERT(_sapp.drop.enabled); + SOKOL_ASSERT((index >= 0) && (index < _sapp.drop.num_files)); + SOKOL_ASSERT(_sapp.drop.buffer); + if (!_sapp.drop.enabled) { + return ""; + } + if ((index < 0) || (index >= _sapp.drop.max_files)) { + return ""; + } + return (const char*) _sapp_dropped_file_path_ptr(index); +} + +SOKOL_API_IMPL uint32_t sapp_html5_get_dropped_file_size(int index) { + SOKOL_ASSERT(_sapp.drop.enabled); + SOKOL_ASSERT((index >= 0) && (index < _sapp.drop.num_files)); + #if defined(_SAPP_EMSCRIPTEN) + if (!_sapp.drop.enabled) { + return 0; + } + return sapp_js_dropped_file_size(index); + #else + (void)index; + return 0; + #endif +} + +SOKOL_API_IMPL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request) { + SOKOL_ASSERT(_sapp.drop.enabled); + SOKOL_ASSERT(request); + SOKOL_ASSERT(request->callback); + SOKOL_ASSERT(request->buffer.ptr); + SOKOL_ASSERT(request->buffer.size > 0); + #if defined(_SAPP_EMSCRIPTEN) + const int index = request->dropped_file_index; + sapp_html5_fetch_error error_code = SAPP_HTML5_FETCH_ERROR_NO_ERROR; + if ((index < 0) || (index >= _sapp.drop.num_files)) { + error_code = SAPP_HTML5_FETCH_ERROR_OTHER; + } + if (sapp_html5_get_dropped_file_size(index) > request->buffer.size) { + error_code = SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL; + } + if (SAPP_HTML5_FETCH_ERROR_NO_ERROR != error_code) { + _sapp_emsc_invoke_fetch_cb(index, + false, // success + (int)error_code, + request->callback, + 0, // fetched_size + (void*)request->buffer.ptr, + request->buffer.size, + request->user_data); + } + else { + sapp_js_fetch_dropped_file(index, + request->callback, + (void*)request->buffer.ptr, + request->buffer.size, + request->user_data); + } + #else + (void)request; + #endif +} + +SOKOL_API_IMPL const void* sapp_metal_get_device(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_METAL) + #if defined(_SAPP_MACOS) + const void* obj = (__bridge const void*) _sapp.macos.mtl_device; + #else + const void* obj = (__bridge const void*) _sapp.ios.mtl_device; + #endif + SOKOL_ASSERT(obj); + return obj; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_metal_get_current_drawable(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_METAL) + #if defined(_SAPP_MACOS) + const void* obj = (__bridge const void*) [_sapp.macos.view currentDrawable]; + #else + const void* obj = (__bridge const void*) [_sapp.ios.view currentDrawable]; + #endif + SOKOL_ASSERT(obj); + return obj; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_metal_get_depth_stencil_texture(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_METAL) + #if defined(_SAPP_MACOS) + const void* obj = (__bridge const void*) [_sapp.macos.view depthStencilTexture]; + #else + const void* obj = (__bridge const void*) [_sapp.ios.view depthStencilTexture]; + #endif + return obj; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_metal_get_msaa_color_texture(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_METAL) + #if defined(_SAPP_MACOS) + const void* obj = (__bridge const void*) [_sapp.macos.view multisampleColorTexture]; + #else + const void* obj = (__bridge const void*) [_sapp.ios.view multisampleColorTexture]; + #endif + return obj; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_macos_get_window(void) { + #if defined(_SAPP_MACOS) + const void* obj = (__bridge const void*) _sapp.macos.window; + SOKOL_ASSERT(obj); + return obj; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_ios_get_window(void) { + #if defined(_SAPP_IOS) + const void* obj = (__bridge const void*) _sapp.ios.window; + SOKOL_ASSERT(obj); + return obj; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_d3d11_get_device(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_D3D11) + return _sapp.d3d11.device; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_d3d11_get_device_context(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_D3D11) + return _sapp.d3d11.device_context; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_d3d11_get_swap_chain(void) { + SOKOL_ASSERT(_sapp.valid); +#if defined(SOKOL_D3D11) + return _sapp.d3d11.swap_chain; +#else + return 0; +#endif +} + +SOKOL_API_IMPL const void* sapp_d3d11_get_render_view(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_D3D11) + if (_sapp.sample_count > 1) { + SOKOL_ASSERT(_sapp.d3d11.msaa_rtv); + return _sapp.d3d11.msaa_rtv; + } else { + SOKOL_ASSERT(_sapp.d3d11.rtv); + return _sapp.d3d11.rtv; + } + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_d3d11_get_resolve_view(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_D3D11) + if (_sapp.sample_count > 1) { + SOKOL_ASSERT(_sapp.d3d11.rtv); + return _sapp.d3d11.rtv; + } else { + return 0; + } + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_d3d11_get_depth_stencil_view(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_D3D11) + return _sapp.d3d11.dsv; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_win32_get_hwnd(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_WIN32) + return _sapp.win32.hwnd; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_wgpu_get_device(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) + return (const void*) _sapp.wgpu.device; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_wgpu_get_render_view(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) + if (_sapp.sample_count > 1) { + SOKOL_ASSERT(_sapp.wgpu.msaa_view); + return (const void*) _sapp.wgpu.msaa_view; + } else { + SOKOL_ASSERT(_sapp.wgpu.swapchain_view); + return (const void*) _sapp.wgpu.swapchain_view; + } + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_wgpu_get_resolve_view(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) + if (_sapp.sample_count > 1) { + SOKOL_ASSERT(_sapp.wgpu.swapchain_view); + return (const void*) _sapp.wgpu.swapchain_view; + } else { + return 0; + } + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_wgpu_get_depth_stencil_view(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) + return (const void*) _sapp.wgpu.depth_stencil_view; + #else + return 0; + #endif +} + +SOKOL_API_IMPL uint32_t sapp_gl_get_framebuffer(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_ANY_GL) + return _sapp.gl.framebuffer; + #else + return 0; + #endif +} + +SOKOL_API_IMPL int sapp_gl_get_major_version(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_GLCORE) + return _sapp.desc.gl_major_version; + #else + return 0; + #endif +} + +SOKOL_API_IMPL int sapp_gl_get_minor_version(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_GLCORE) + return _sapp.desc.gl_minor_version; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_android_get_native_activity(void) { + // NOTE: _sapp.valid is not asserted here because sapp_android_get_native_activity() + // needs to be callable from within sokol_main() (see: https://github.com/floooh/sokol/issues/708) + #if defined(_SAPP_ANDROID) + return (void*)_sapp.android.activity; + #else + return 0; + #endif +} + +SOKOL_API_IMPL void sapp_html5_ask_leave_site(bool ask) { + _sapp.html5_ask_leave_site = ask; +} + +#endif /* SOKOL_APP_IMPL */ diff --git a/thirdparty/sokol/sokol_args.h b/thirdparty/sokol/sokol_args.h new file mode 100644 index 0000000..b5b4d47 --- /dev/null +++ b/thirdparty/sokol/sokol_args.h @@ -0,0 +1,867 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_ARGS_IMPL) +#define SOKOL_ARGS_IMPL +#endif +#ifndef SOKOL_ARGS_INCLUDED +/* + sokol_args.h -- cross-platform key/value arg-parsing for web and native + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_ARGS_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + Optionally provide the following defines with your own implementations: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_ARGS_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_ARGS_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + If sokol_args.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_ARGS_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + OVERVIEW + ======== + sokol_args.h provides a simple unified argument parsing API for WebAssembly and + native apps. + + When running as a WebAssembly app, arguments are taken from the page URL: + + https://floooh.github.io/tiny8bit/kc85.html?type=kc85_3&mod=m022&snapshot=kc85/jungle.kcc + + The same arguments provided to a command line app: + + kc85 type=kc85_3 mod=m022 snapshot=kc85/jungle.kcc + + You can also use standalone keys without value: + + https://floooh.github.io/tiny8bit/kc85.html?bla&blub + + On the command line: + + kc85 bla blub + + Such value-less keys are reported as the value being an empty string, but they + can be tested with `sapp_exists("bla")` or `sapp_boolean("blub")`. + + ARGUMENT FORMATTING + =================== + On the web platform, arguments must be formatted as a valid URL query string + with 'percent encoding' used for special characters. + + Strings are expected to be UTF-8 encoded (although sokol_args.h doesn't + contain any special UTF-8 handling). See below on how to obtain + UTF-8 encoded argc/argv values on Windows when using WinMain() as + entry point. + + On native platforms the following rules must be followed: + + Arguments have the general form + + key=value + + or + + key + + When a key has no value, the value will be assigned an empty string. + + Key/value pairs are separated by 'whitespace', valid whitespace + characters are space and tab. + + Whitespace characters in front and after the separating '=' character + are ignored: + + key = value + + ...is the same as + + key=value + + The 'key' string must be a simple string without escape sequences or whitespace. + + The 'value' string can be quoted, and quoted value strings can contain + whitespace: + + key = 'single-quoted value' + key = "double-quoted value" + + Single-quoted value strings can contain double quotes, and vice-versa: + + key = 'single-quoted value "can contain double-quotes"' + key = "double-quoted value 'can contain single-quotes'" + + Note that correct quoting can be tricky on some shells, since command + shells may remove quotes, unless they're escaped. + + Value strings can contain a small selection of escape sequences: + + \n - newline + \r - carriage return + \t - tab + \\ - escaped backslash + + (more escape codes may be added in the future). + + CODE EXAMPLE + ============ + + int main(int argc, char* argv[]) { + // initialize sokol_args with default parameters + sargs_setup(&(sargs_desc){ + .argc = argc, + .argv = argv + }); + + // check if a key exists... + if (sargs_exists("bla")) { + ... + } + + // get value string for key, if not found, return empty string "" + const char* val0 = sargs_value("bla"); + + // get value string for key, or default string if key not found + const char* val1 = sargs_value_def("bla", "default_value"); + + // check if a key matches expected value + if (sargs_equals("type", "kc85_4")) { + ... + } + + // check if a key's value is "true", "yes" or "on" or if this is a standalone key + if (sargs_boolean("joystick_enabled")) { + ... + } + + // iterate over keys and values + for (int i = 0; i < sargs_num_args(); i++) { + printf("key: %s, value: %s\n", sargs_key_at(i), sargs_value_at(i)); + } + + // lookup argument index by key string, will return -1 if key + // is not found, sargs_key_at() and sargs_value_at() will return + // an empty string for invalid indices + int index = sargs_find("bla"); + printf("key: %s, value: %s\n", sargs_key_at(index), sargs_value_at(index)); + + // shutdown sokol-args + sargs_shutdown(); + } + + WINMAIN AND ARGC / ARGV + ======================= + On Windows with WinMain() based apps, getting UTF8-encoded command line + arguments is a bit more complicated: + + First call GetCommandLineW(), this returns the entire command line + as UTF-16 string. Then call CommandLineToArgvW(), this parses the + command line string into the usual argc/argv format (but in UTF-16). + Finally convert the UTF-16 strings in argv[] into UTF-8 via + WideCharToMultiByte(). + + See the function _sapp_win32_command_line_to_utf8_argv() in sokol_app.h + for example code how to do this (if you're using sokol_app.h, it will + already convert the command line arguments to UTF-8 for you of course, + so you can plug them directly into sokol_app.h). + + API DOCUMENTATION + ================= + void sargs_setup(const sargs_desc* desc) + Initialize sokol_args, desc contains the following configuration + parameters: + int argc - the main function's argc parameter + char** argv - the main function's argv parameter + int max_args - max number of key/value pairs, default is 16 + int buf_size - size of the internal string buffer, default is 16384 + + Note that on the web, argc and argv will be ignored and the arguments + will be taken from the page URL instead. + + sargs_setup() will allocate 2 memory chunks: one for keeping track + of the key/value args of size 'max_args*8', and a string buffer + of size 'buf_size'. + + void sargs_shutdown(void) + Shutdown sokol-args and free any allocated memory. + + bool sargs_isvalid(void) + Return true between sargs_setup() and sargs_shutdown() + + bool sargs_exists(const char* key) + Test if an argument exists by its key name. + + const char* sargs_value(const char* key) + Return value associated with key. Returns an empty string ("") if the + key doesn't exist, or if the key doesn't have a value. + + const char* sargs_value_def(const char* key, const char* default) + Return value associated with key, or the provided default value if the + key doesn't exist, or this is a value-less key. + + bool sargs_equals(const char* key, const char* val); + Return true if the value associated with key matches + the 'val' argument. + + bool sargs_boolean(const char* key) + Return true if the value string of 'key' is one of 'true', 'yes', 'on', + or this is a key without value. + + int sargs_find(const char* key) + Find argument by key name and return its index, or -1 if not found. + + int sargs_num_args(void) + Return number of key/value pairs. + + const char* sargs_key_at(int index) + Return the key name of argument at index. Returns empty string if + is index is outside range. + + const char* sargs_value_at(int index) + Return the value of argument at index. Returns empty string + if the key at index has no value, or the index is out-of-range. + + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + sargs_setup(&(sargs_desc){ + // ... + .allocator = { + .alloc_fn = my_alloc, + .free_fn = my_free, + .user_data = ..., + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_args.h + itself though, not any allocations in OS libraries. + + TODO + ==== + - parsing errors? + + LICENSE + ======= + + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_ARGS_INCLUDED (1) +#include +#include +#include // size_t + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_ARGS_API_DECL) +#define SOKOL_ARGS_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_ARGS_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_ARGS_IMPL) +#define SOKOL_ARGS_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_ARGS_API_DECL __declspec(dllimport) +#else +#define SOKOL_ARGS_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + sargs_allocator + + Used in sargs_desc to provide custom memory-alloc and -free functions + to sokol_args.h. If memory management should be overridden, both the + alloc_fn and free_fn function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct sargs_allocator { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} sargs_allocator; + +typedef struct sargs_desc { + int argc; + char** argv; + int max_args; + int buf_size; + sargs_allocator allocator; +} sargs_desc; + +/* setup sokol-args */ +SOKOL_ARGS_API_DECL void sargs_setup(const sargs_desc* desc); +/* shutdown sokol-args */ +SOKOL_ARGS_API_DECL void sargs_shutdown(void); +/* true between sargs_setup() and sargs_shutdown() */ +SOKOL_ARGS_API_DECL bool sargs_isvalid(void); +/* test if an argument exists by key name */ +SOKOL_ARGS_API_DECL bool sargs_exists(const char* key); +/* get value by key name, return empty string if key doesn't exist or an existing key has no value */ +SOKOL_ARGS_API_DECL const char* sargs_value(const char* key); +/* get value by key name, return provided default if key doesn't exist or has no value */ +SOKOL_ARGS_API_DECL const char* sargs_value_def(const char* key, const char* def); +/* return true if val arg matches the value associated with key */ +SOKOL_ARGS_API_DECL bool sargs_equals(const char* key, const char* val); +/* return true if key's value is "true", "yes", "on" or an existing key has no value */ +SOKOL_ARGS_API_DECL bool sargs_boolean(const char* key); +/* get index of arg by key name, return -1 if not exists */ +SOKOL_ARGS_API_DECL int sargs_find(const char* key); +/* get number of parsed arguments */ +SOKOL_ARGS_API_DECL int sargs_num_args(void); +/* get key name of argument at index, or empty string */ +SOKOL_ARGS_API_DECL const char* sargs_key_at(int index); +/* get value string of argument at index, or empty string */ +SOKOL_ARGS_API_DECL const char* sargs_value_at(int index); + +#ifdef __cplusplus +} /* extern "C" */ + +/* reference-based equivalents for c++ */ +inline void sargs_setup(const sargs_desc& desc) { return sargs_setup(&desc); } + +#endif +#endif // SOKOL_ARGS_INCLUDED + +/*--- IMPLEMENTATION ---------------------------------------------------------*/ +#ifdef SOKOL_ARGS_IMPL +#define SOKOL_ARGS_IMPL_INCLUDED (1) + +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sargs_desc.allocator to override memory allocation functions" +#endif + +#include // memset, strcmp +#include // malloc, free + +#if defined(__EMSCRIPTEN__) +#include +#endif + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif + +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif + +#define _sargs_def(val, def) (((val) == 0) ? (def) : (val)) + +#define _SARGS_MAX_ARGS_DEF (16) +#define _SARGS_BUF_SIZE_DEF (16*1024) + +/* parser state */ +#define _SARGS_EXPECT_KEY (1<<0) +#define _SARGS_EXPECT_SEP (1<<1) +#define _SARGS_EXPECT_VAL (1<<2) +#define _SARGS_PARSING_KEY (1<<3) +#define _SARGS_PARSING_VAL (1<<4) +#define _SARGS_ERROR (1<<5) + +/* a key/value pair struct */ +typedef struct { + int key; /* index to start of key string in buf */ + int val; /* index to start of value string in buf */ +} _sargs_kvp_t; + +/* sokol-args state */ +typedef struct { + int max_args; /* number of key/value pairs in args array */ + int num_args; /* number of valid items in args array */ + _sargs_kvp_t* args; /* key/value pair array */ + int buf_size; /* size of buffer in bytes */ + int buf_pos; /* current buffer position */ + char* buf; /* character buffer, first char is reserved and zero for 'empty string' */ + bool valid; + uint32_t parse_state; + char quote; /* current quote char, 0 if not in a quote */ + bool in_escape; /* currently in an escape sequence */ + sargs_allocator allocator; +} _sargs_state_t; +static _sargs_state_t _sargs; + +/*== PRIVATE IMPLEMENTATION FUNCTIONS ========================================*/ +_SOKOL_PRIVATE void _sargs_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +_SOKOL_PRIVATE void* _sargs_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_sargs.allocator.alloc_fn) { + ptr = _sargs.allocator.alloc_fn(size, _sargs.allocator.user_data); + } else { + ptr = malloc(size); + } + SOKOL_ASSERT(ptr); + return ptr; +} + +_SOKOL_PRIVATE void* _sargs_malloc_clear(size_t size) { + void* ptr = _sargs_malloc(size); + _sargs_clear(ptr, size); + return ptr; +} + +_SOKOL_PRIVATE void _sargs_free(void* ptr) { + if (_sargs.allocator.free_fn) { + _sargs.allocator.free_fn(ptr, _sargs.allocator.user_data); + } else { + free(ptr); + } +} + +_SOKOL_PRIVATE void _sargs_putc(char c) { + if ((_sargs.buf_pos+2) < _sargs.buf_size) { + _sargs.buf[_sargs.buf_pos++] = c; + } +} + +_SOKOL_PRIVATE const char* _sargs_str(int index) { + SOKOL_ASSERT((index >= 0) && (index < _sargs.buf_size)); + return &_sargs.buf[index]; +} + +/*-- argument parser functions ------------------*/ +_SOKOL_PRIVATE void _sargs_expect_key(void) { + _sargs.parse_state = _SARGS_EXPECT_KEY; +} + +_SOKOL_PRIVATE bool _sargs_key_expected(void) { + return 0 != (_sargs.parse_state & _SARGS_EXPECT_KEY); +} + +_SOKOL_PRIVATE void _sargs_expect_val(void) { + _sargs.parse_state = _SARGS_EXPECT_VAL; +} + +_SOKOL_PRIVATE bool _sargs_val_expected(void) { + return 0 != (_sargs.parse_state & _SARGS_EXPECT_VAL); +} + +_SOKOL_PRIVATE void _sargs_expect_sep_or_key(void) { + _sargs.parse_state = _SARGS_EXPECT_SEP | _SARGS_EXPECT_KEY; +} + +_SOKOL_PRIVATE bool _sargs_any_expected(void) { + return 0 != (_sargs.parse_state & (_SARGS_EXPECT_KEY | _SARGS_EXPECT_VAL | _SARGS_EXPECT_SEP)); +} + +_SOKOL_PRIVATE bool _sargs_is_separator(char c) { + return c == '='; +} + +_SOKOL_PRIVATE bool _sargs_is_quote(char c) { + if (0 == _sargs.quote) { + return (c == '\'') || (c == '"'); + } + else { + return c == _sargs.quote; + } +} + +_SOKOL_PRIVATE void _sargs_begin_quote(char c) { + _sargs.quote = c; +} + +_SOKOL_PRIVATE void _sargs_end_quote(void) { + _sargs.quote = 0; +} + +_SOKOL_PRIVATE bool _sargs_in_quotes(void) { + return 0 != _sargs.quote; +} + +_SOKOL_PRIVATE bool _sargs_is_whitespace(char c) { + return !_sargs_in_quotes() && ((c == ' ') || (c == '\t')); +} + +_SOKOL_PRIVATE void _sargs_start_key(void) { + SOKOL_ASSERT((_sargs.num_args >= 0) && (_sargs.num_args < _sargs.max_args)); + _sargs.parse_state = _SARGS_PARSING_KEY; + _sargs.args[_sargs.num_args].key = _sargs.buf_pos; +} + +_SOKOL_PRIVATE void _sargs_end_key(void) { + SOKOL_ASSERT((_sargs.num_args >= 0) && (_sargs.num_args < _sargs.max_args)); + _sargs_putc(0); + // declare val as empty string in case this is a key-only arg + _sargs.args[_sargs.num_args].val = _sargs.buf_pos - 1; + _sargs.num_args++; + _sargs.parse_state = 0; +} + +_SOKOL_PRIVATE bool _sargs_parsing_key(void) { + return 0 != (_sargs.parse_state & _SARGS_PARSING_KEY); +} + +_SOKOL_PRIVATE void _sargs_start_val(void) { + SOKOL_ASSERT((_sargs.num_args > 0) && (_sargs.num_args <= _sargs.max_args)); + _sargs.parse_state = _SARGS_PARSING_VAL; + _sargs.args[_sargs.num_args - 1].val = _sargs.buf_pos; +} + +_SOKOL_PRIVATE void _sargs_end_val(void) { + _sargs_putc(0); + _sargs.parse_state = 0; +} + +_SOKOL_PRIVATE bool _sargs_is_escape(char c) { + return '\\' == c; +} + +_SOKOL_PRIVATE void _sargs_start_escape(void) { + _sargs.in_escape = true; +} + +_SOKOL_PRIVATE bool _sargs_in_escape(void) { + return _sargs.in_escape; +} + +_SOKOL_PRIVATE char _sargs_escape(char c) { + switch (c) { + case 'n': return '\n'; + case 't': return '\t'; + case 'r': return '\r'; + case '\\': return '\\'; + default: return c; + } +} + +_SOKOL_PRIVATE void _sargs_end_escape(void) { + _sargs.in_escape = false; +} + +_SOKOL_PRIVATE bool _sargs_parsing_val(void) { + return 0 != (_sargs.parse_state & _SARGS_PARSING_VAL); +} + +_SOKOL_PRIVATE bool _sargs_parse_carg(const char* src) { + char c; + while (0 != (c = *src++)) { + if (_sargs_in_escape()) { + c = _sargs_escape(c); + _sargs_end_escape(); + } + else if (_sargs_is_escape(c)) { + _sargs_start_escape(); + continue; + } + if (_sargs_any_expected()) { + if (!_sargs_is_whitespace(c)) { + /* start of key, value or separator */ + if (_sargs_is_separator(c)) { + /* skip separator and expect value */ + _sargs_expect_val(); + continue; + } + else if (_sargs_key_expected()) { + /* start of new key */ + _sargs_start_key(); + } + else if (_sargs_val_expected()) { + /* start of value */ + if (_sargs_is_quote(c)) { + _sargs_begin_quote(c); + continue; + } + _sargs_start_val(); + } + } + else { + /* skip white space */ + continue; + } + } + else if (_sargs_parsing_key()) { + if (_sargs_is_whitespace(c) || _sargs_is_separator(c)) { + /* end of key string */ + _sargs_end_key(); + if (_sargs_is_separator(c)) { + _sargs_expect_val(); + } + else { + _sargs_expect_sep_or_key(); + } + continue; + } + } + else if (_sargs_parsing_val()) { + if (_sargs_in_quotes()) { + /* when in quotes, whitespace is a normal character + and a matching quote ends the value string + */ + if (_sargs_is_quote(c)) { + _sargs_end_quote(); + _sargs_end_val(); + _sargs_expect_key(); + continue; + } + } + else if (_sargs_is_whitespace(c)) { + /* end of value string (no quotes) */ + _sargs_end_val(); + _sargs_expect_key(); + continue; + } + } + _sargs_putc(c); + } + if (_sargs_parsing_key()) { + _sargs_end_key(); + _sargs_expect_sep_or_key(); + } + else if (_sargs_parsing_val() && !_sargs_in_quotes()) { + _sargs_end_val(); + _sargs_expect_key(); + } + return true; +} + +_SOKOL_PRIVATE bool _sargs_parse_cargs(int argc, const char** argv) { + _sargs_expect_key(); + bool retval = true; + for (int i = 1; i < argc; i++) { + retval &= _sargs_parse_carg(argv[i]); + } + _sargs.parse_state = 0; + return retval; +} + +/*-- EMSCRIPTEN IMPLEMENTATION -----------------------------------------------*/ +#if defined(__EMSCRIPTEN__) + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(EM_JS_DEPS) +EM_JS_DEPS(sokol_audio, "$withStackSave,$stringToUTF8OnStack"); +#endif + +EMSCRIPTEN_KEEPALIVE void _sargs_add_kvp(const char* key, const char* val) { + SOKOL_ASSERT(_sargs.valid && key && val); + if (_sargs.num_args >= _sargs.max_args) { + return; + } + + /* copy key string */ + char c; + _sargs.args[_sargs.num_args].key = _sargs.buf_pos; + const char* ptr = key; + while (0 != (c = *ptr++)) { + _sargs_putc(c); + } + _sargs_putc(0); + + /* copy value string */ + _sargs.args[_sargs.num_args].val = _sargs.buf_pos; + ptr = val; + while (0 != (c = *ptr++)) { + _sargs_putc(c); + } + _sargs_putc(0); + + _sargs.num_args++; +} +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* JS function to extract arguments from the page URL */ +EM_JS(void, sargs_js_parse_url, (void), { + const params = new URLSearchParams(window.location.search).entries(); + for (let p = params.next(); !p.done; p = params.next()) { + const key = p.value[0]; + const val = p.value[1]; + withStackSave(() => { + const key_cstr = stringToUTF8OnStack(key); + const val_cstr = stringToUTF8OnStack(val); + __sargs_add_kvp(key_cstr, val_cstr) + }); + } +}); + +#endif /* EMSCRIPTEN */ + +/*== PUBLIC IMPLEMENTATION FUNCTIONS =========================================*/ +SOKOL_API_IMPL void sargs_setup(const sargs_desc* desc) { + SOKOL_ASSERT(desc); + _sargs_clear(&_sargs, sizeof(_sargs)); + _sargs.max_args = _sargs_def(desc->max_args, _SARGS_MAX_ARGS_DEF); + _sargs.buf_size = _sargs_def(desc->buf_size, _SARGS_BUF_SIZE_DEF); + SOKOL_ASSERT(_sargs.buf_size > 8); + _sargs.args = (_sargs_kvp_t*) _sargs_malloc_clear((size_t)_sargs.max_args * sizeof(_sargs_kvp_t)); + _sargs.buf = (char*) _sargs_malloc_clear((size_t)_sargs.buf_size * sizeof(char)); + /* the first character in buf is reserved and always zero, this is the 'empty string' */ + _sargs.buf_pos = 1; + _sargs.allocator = desc->allocator; + _sargs.valid = true; + + /* parse argc/argv */ + _sargs_parse_cargs(desc->argc, (const char**) desc->argv); + + #if defined(__EMSCRIPTEN__) + /* on emscripten, also parse the page URL*/ + sargs_js_parse_url(); + #endif +} + +SOKOL_API_IMPL void sargs_shutdown(void) { + SOKOL_ASSERT(_sargs.valid); + if (_sargs.args) { + _sargs_free(_sargs.args); + _sargs.args = 0; + } + if (_sargs.buf) { + _sargs_free(_sargs.buf); + _sargs.buf = 0; + } + _sargs.valid = false; +} + +SOKOL_API_IMPL bool sargs_isvalid(void) { + return _sargs.valid; +} + +SOKOL_API_IMPL int sargs_find(const char* key) { + SOKOL_ASSERT(_sargs.valid && key); + for (int i = 0; i < _sargs.num_args; i++) { + if (0 == strcmp(_sargs_str(_sargs.args[i].key), key)) { + return i; + } + } + return -1; +} + +SOKOL_API_IMPL int sargs_num_args(void) { + SOKOL_ASSERT(_sargs.valid); + return _sargs.num_args; +} + +SOKOL_API_IMPL const char* sargs_key_at(int index) { + SOKOL_ASSERT(_sargs.valid); + if ((index >= 0) && (index < _sargs.num_args)) { + return _sargs_str(_sargs.args[index].key); + } + else { + /* index 0 is always the empty string */ + return _sargs_str(0); + } +} + +SOKOL_API_IMPL const char* sargs_value_at(int index) { + SOKOL_ASSERT(_sargs.valid); + if ((index >= 0) && (index < _sargs.num_args)) { + return _sargs_str(_sargs.args[index].val); + } + else { + /* index 0 is always the empty string */ + return _sargs_str(0); + } +} + +SOKOL_API_IMPL bool sargs_exists(const char* key) { + SOKOL_ASSERT(_sargs.valid && key); + return -1 != sargs_find(key); +} + +SOKOL_API_IMPL const char* sargs_value(const char* key) { + SOKOL_ASSERT(_sargs.valid && key); + return sargs_value_at(sargs_find(key)); +} + +SOKOL_API_IMPL const char* sargs_value_def(const char* key, const char* def) { + SOKOL_ASSERT(_sargs.valid && key && def); + int arg_index = sargs_find(key); + if (-1 != arg_index) { + const char* res = sargs_value_at(arg_index); + SOKOL_ASSERT(res); + if (res[0] == 0) { + return def; + } else { + return res; + } + } + else { + return def; + } +} + +SOKOL_API_IMPL bool sargs_equals(const char* key, const char* val) { + SOKOL_ASSERT(_sargs.valid && key && val); + return 0 == strcmp(sargs_value(key), val); +} + +SOKOL_API_IMPL bool sargs_boolean(const char* key) { + if (sargs_exists(key)) { + const char* val = sargs_value(key); + return (0 == strcmp("true", val)) || + (0 == strcmp("yes", val)) || + (0 == strcmp("on", val)) || + (0 == strcmp("", val)); + } else { + return false; + } +} + +#endif /* SOKOL_ARGS_IMPL */ diff --git a/thirdparty/sokol/sokol_audio.h b/thirdparty/sokol/sokol_audio.h new file mode 100644 index 0000000..529d1e4 --- /dev/null +++ b/thirdparty/sokol/sokol_audio.h @@ -0,0 +1,2598 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_AUDIO_IMPL) +#define SOKOL_AUDIO_IMPL +#endif +#ifndef SOKOL_AUDIO_INCLUDED +/* + sokol_audio.h -- cross-platform audio-streaming API + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_AUDIO_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + Optionally provide the following defines with your own implementations: + + SOKOL_DUMMY_BACKEND - use a dummy backend + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_AUDIO_API_DECL- public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_AUDIO_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + SAUDIO_RING_MAX_SLOTS - max number of slots in the push-audio ring buffer (default 1024) + SAUDIO_OSX_USE_SYSTEM_HEADERS - define this to force inclusion of system headers on + macOS instead of using embedded CoreAudio declarations + SAUDIO_ANDROID_AAUDIO - on Android, select the AAudio backend (default) + SAUDIO_ANDROID_SLES - on Android, select the OpenSLES backend + + If sokol_audio.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_AUDIO_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + Link with the following libraries: + + - on macOS: AudioToolbox + - on iOS: AudioToolbox, AVFoundation + - on FreeBSD: asound + - on Linux: asound + - on Android: link with OpenSLES or aaudio + - on Windows with MSVC or Clang toolchain: no action needed, libs are defined in-source via pragma-comment-lib + - on Windows with MINGW/MSYS2 gcc: compile with '-mwin32' and link with -lole32 + + FEATURE OVERVIEW + ================ + You provide a mono- or stereo-stream of 32-bit float samples, which + Sokol Audio feeds into platform-specific audio backends: + + - Windows: WASAPI + - Linux: ALSA + - FreeBSD: ALSA + - macOS: CoreAudio + - iOS: CoreAudio+AVAudioSession + - emscripten: WebAudio with ScriptProcessorNode + - Android: AAudio (default) or OpenSLES, select at build time + + Sokol Audio will not do any buffer mixing or volume control, if you have + multiple independent input streams of sample data you need to perform the + mixing yourself before forwarding the data to Sokol Audio. + + There are two mutually exclusive ways to provide the sample data: + + 1. Callback model: You provide a callback function, which will be called + when Sokol Audio needs new samples. On all platforms except emscripten, + this function is called from a separate thread. + 2. Push model: Your code pushes small blocks of sample data from your + main loop or a thread you created. The pushed data is stored in + a ring buffer where it is pulled by the backend code when + needed. + + The callback model is preferred because it is the most direct way to + feed sample data into the audio backends and also has less moving parts + (there is no ring buffer between your code and the audio backend). + + Sometimes it is not possible to generate the audio stream directly in a + callback function running in a separate thread, for such cases Sokol Audio + provides the push-model as a convenience. + + SOKOL AUDIO, SOLOUD AND MINIAUDIO + ================================= + The WASAPI, ALSA, OpenSLES and CoreAudio backend code has been taken from the + SoLoud library (with some modifications, so any bugs in there are most + likely my fault). If you need a more fully-featured audio solution, check + out SoLoud, it's excellent: + + https://github.com/jarikomppa/soloud + + Another alternative which feature-wise is somewhere inbetween SoLoud and + sokol-audio might be MiniAudio: + + https://github.com/mackron/miniaudio + + GLOSSARY + ======== + - stream buffer: + The internal audio data buffer, usually provided by the backend API. The + size of the stream buffer defines the base latency, smaller buffers have + lower latency but may cause audio glitches. Bigger buffers reduce or + eliminate glitches, but have a higher base latency. + + - stream callback: + Optional callback function which is called by Sokol Audio when it + needs new samples. On Windows, macOS/iOS and Linux, this is called in + a separate thread, on WebAudio, this is called per-frame in the + browser thread. + + - channel: + A discrete track of audio data, currently 1-channel (mono) and + 2-channel (stereo) is supported and tested. + + - sample: + The magnitude of an audio signal on one channel at a given time. In + Sokol Audio, samples are 32-bit float numbers in the range -1.0 to + +1.0. + + - frame: + The tightly packed set of samples for all channels at a given time. + For mono 1 frame is 1 sample. For stereo, 1 frame is 2 samples. + + - packet: + In Sokol Audio, a small chunk of audio data that is moved from the + main thread to the audio streaming thread in order to decouple the + rate at which the main thread provides new audio data, and the + streaming thread consuming audio data. + + WORKING WITH SOKOL AUDIO + ======================== + First call saudio_setup() with your preferred audio playback options. + In most cases you can stick with the default values, these provide + a good balance between low-latency and glitch-free playback + on all audio backends. + + You should always provide a logging callback to be aware of any + warnings and errors. The easiest way is to use sokol_log.h for this: + + #include "sokol_log.h" + // ... + saudio_setup(&(saudio_desc){ + .logger = { + .func = slog_func, + } + }); + + If you want to use the callback-model, you need to provide a stream + callback function either in saudio_desc.stream_cb or saudio_desc.stream_userdata_cb, + otherwise keep both function pointers zero-initialized. + + Use push model and default playback parameters: + + saudio_setup(&(saudio_desc){ .logger.func = slog_func }); + + Use stream callback model and default playback parameters: + + saudio_setup(&(saudio_desc){ + .stream_cb = my_stream_callback + .logger.func = slog_func, + }); + + The standard stream callback doesn't have a user data argument, if you want + that, use the alternative stream_userdata_cb and also set the user_data pointer: + + saudio_setup(&(saudio_desc){ + .stream_userdata_cb = my_stream_callback, + .user_data = &my_data + .logger.func = slog_func, + }); + + The following playback parameters can be provided through the + saudio_desc struct: + + General parameters (both for stream-callback and push-model): + + int sample_rate -- the sample rate in Hz, default: 44100 + int num_channels -- number of channels, default: 1 (mono) + int buffer_frames -- number of frames in streaming buffer, default: 2048 + + The stream callback prototype (either with or without userdata): + + void (*stream_cb)(float* buffer, int num_frames, int num_channels) + void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data) + Function pointer to the user-provide stream callback. + + Push-model parameters: + + int packet_frames -- number of frames in a packet, default: 128 + int num_packets -- number of packets in ring buffer, default: 64 + + The sample_rate and num_channels parameters are only hints for the audio + backend, it isn't guaranteed that those are the values used for actual + playback. + + To get the actual parameters, call the following functions after + saudio_setup(): + + int saudio_sample_rate(void) + int saudio_channels(void); + + It's unlikely that the number of channels will be different than requested, + but a different sample rate isn't uncommon. + + (NOTE: there's an yet unsolved issue when an audio backend might switch + to a different sample rate when switching output devices, for instance + plugging in a bluetooth headset, this case is currently not handled in + Sokol Audio). + + You can check if audio initialization was successful with + saudio_isvalid(). If backend initialization failed for some reason + (for instance when there's no audio device in the machine), this + will return false. Not checking for success won't do any harm, all + Sokol Audio function will silently fail when called after initialization + has failed, so apart from missing audio output, nothing bad will happen. + + Before your application exits, you should call + + saudio_shutdown(); + + This stops the audio thread (on Linux, Windows and macOS/iOS) and + properly shuts down the audio backend. + + THE STREAM CALLBACK MODEL + ========================= + To use Sokol Audio in stream-callback-mode, provide a callback function + like this in the saudio_desc struct when calling saudio_setup(): + + void stream_cb(float* buffer, int num_frames, int num_channels) { + ... + } + + Or the alternative version with a user-data argument: + + void stream_userdata_cb(float* buffer, int num_frames, int num_channels, void* user_data) { + my_data_t* my_data = (my_data_t*) user_data; + ... + } + + The job of the callback function is to fill the *buffer* with 32-bit + float sample values. + + To output silence, fill the buffer with zeros: + + void stream_cb(float* buffer, int num_frames, int num_channels) { + const int num_samples = num_frames * num_channels; + for (int i = 0; i < num_samples; i++) { + buffer[i] = 0.0f; + } + } + + For stereo output (num_channels == 2), the samples for the left + and right channel are interleaved: + + void stream_cb(float* buffer, int num_frames, int num_channels) { + assert(2 == num_channels); + for (int i = 0; i < num_frames; i++) { + buffer[2*i + 0] = ...; // left channel + buffer[2*i + 1] = ...; // right channel + } + } + + Please keep in mind that the stream callback function is running in a + separate thread, if you need to share data with the main thread you need + to take care yourself to make the access to the shared data thread-safe! + + THE PUSH MODEL + ============== + To use the push-model for providing audio data, simply don't set (keep + zero-initialized) the stream_cb field in the saudio_desc struct when + calling saudio_setup(). + + To provide sample data with the push model, call the saudio_push() + function at regular intervals (for instance once per frame). You can + call the saudio_expect() function to ask Sokol Audio how much room is + in the ring buffer, but if you provide a continuous stream of data + at the right sample rate, saudio_expect() isn't required (it's a simple + way to sync/throttle your sample generation code with the playback + rate though). + + With saudio_push() you may need to maintain your own intermediate sample + buffer, since pushing individual sample values isn't very efficient. + The following example is from the MOD player sample in + sokol-samples (https://github.com/floooh/sokol-samples): + + const int num_frames = saudio_expect(); + if (num_frames > 0) { + const int num_samples = num_frames * saudio_channels(); + read_samples(flt_buf, num_samples); + saudio_push(flt_buf, num_frames); + } + + Another option is to ignore saudio_expect(), and just push samples as they + are generated in small batches. In this case you *need* to generate the + samples at the right sample rate: + + The following example is taken from the Tiny Emulators project + (https://github.com/floooh/chips-test), this is for mono playback, + so (num_samples == num_frames): + + // tick the sound generator + if (ay38910_tick(&sys->psg)) { + // new sample is ready + sys->sample_buffer[sys->sample_pos++] = sys->psg.sample; + if (sys->sample_pos == sys->num_samples) { + // new sample packet is ready + saudio_push(sys->sample_buffer, sys->num_samples); + sys->sample_pos = 0; + } + } + + THE WEBAUDIO BACKEND + ==================== + The WebAudio backend is currently using a ScriptProcessorNode callback to + feed the sample data into WebAudio. ScriptProcessorNode has been + deprecated for a while because it is running from the main thread, with + the default initialization parameters it works 'pretty well' though. + Ultimately Sokol Audio will use Audio Worklets, but this requires a few + more things to fall into place (Audio Worklets implemented everywhere, + SharedArrayBuffers enabled again, and I need to figure out a 'low-cost' + solution in terms of implementation effort, since Audio Worklets are + a lot more complex than ScriptProcessorNode if the audio data needs to come + from the main thread). + + The WebAudio backend is automatically selected when compiling for + emscripten (__EMSCRIPTEN__ define exists). + + https://developers.google.com/web/updates/2017/12/audio-worklet + https://developers.google.com/web/updates/2018/06/audio-worklet-design-pattern + + "Blob URLs": https://www.html5rocks.com/en/tutorials/workers/basics/ + + Also see: https://blog.paul.cx/post/a-wait-free-spsc-ringbuffer-for-the-web/ + + THE COREAUDIO BACKEND + ===================== + The CoreAudio backend is selected on macOS and iOS (__APPLE__ is defined). + Since the CoreAudio API is implemented in C (not Objective-C) on macOS the + implementation part of Sokol Audio can be included into a C source file. + + However on iOS, Sokol Audio must be compiled as Objective-C due to it's + reliance on the AVAudioSession object. The iOS code path support both + being compiled with or without ARC (Automatic Reference Counting). + + For thread synchronisation, the CoreAudio backend will use the + pthread_mutex_* functions. + + The incoming floating point samples will be directly forwarded to + CoreAudio without further conversion. + + macOS and iOS applications that use Sokol Audio need to link with + the AudioToolbox framework. + + THE WASAPI BACKEND + ================== + The WASAPI backend is automatically selected when compiling on Windows + (_WIN32 is defined). + + For thread synchronisation a Win32 critical section is used. + + WASAPI may use a different size for its own streaming buffer then requested, + so the base latency may be slightly bigger. The current backend implementation + converts the incoming floating point sample values to signed 16-bit + integers. + + The required Windows system DLLs are linked with #pragma comment(lib, ...), + so you shouldn't need to add additional linker libs in the build process + (otherwise this is a bug which should be fixed in sokol_audio.h). + + THE ALSA BACKEND + ================ + The ALSA backend is automatically selected when compiling on Linux + ('linux' is defined). + + For thread synchronisation, the pthread_mutex_* functions are used. + + Samples are directly forwarded to ALSA in 32-bit float format, no + further conversion is taking place. + + You need to link with the 'asound' library, and the + header must be present (usually both are installed with some sort + of ALSA development package). + + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + saudio_setup(&(saudio_desc){ + // ... + .allocator = { + .alloc_fn = my_alloc, + .free_fn = my_free, + .user_data = ..., + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_audio.h + itself though, not any allocations in OS libraries. + + Memory allocation will only happen on the same thread where saudio_setup() + was called, so you don't need to worry about thread-safety. + + + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + saudio_setup(&(saudio_desc){ .logger.func = slog_func }); + + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'saudio' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SAUDIO_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_audio.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-audio like this: + + saudio_setup(&(saudio_desc){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); + + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. + + LICENSE + ======= + + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_AUDIO_INCLUDED (1) +#include // size_t +#include +#include + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_AUDIO_API_DECL) +#define SOKOL_AUDIO_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_AUDIO_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_AUDIO_IMPL) +#define SOKOL_AUDIO_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_AUDIO_API_DECL __declspec(dllimport) +#else +#define SOKOL_AUDIO_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + saudio_log_item + + Log items are defined via X-Macros, and expanded to an + enum 'saudio_log_item', and in debug mode only, + corresponding strings. + + Used as parameter in the logging callback. +*/ +#define _SAUDIO_LOG_ITEMS \ + _SAUDIO_LOGITEM_XMACRO(OK, "Ok") \ + _SAUDIO_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_SND_PCM_OPEN_FAILED, "snd_pcm_open() failed") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_FLOAT_SAMPLES_NOT_SUPPORTED, "floating point sample format not supported") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_REQUESTED_BUFFER_SIZE_NOT_SUPPORTED, "requested buffer size not supported") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_REQUESTED_CHANNEL_COUNT_NOT_SUPPORTED, "requested channel count not supported") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_SND_PCM_HW_PARAMS_SET_RATE_NEAR_FAILED, "snd_pcm_hw_params_set_rate_near() failed") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_SND_PCM_HW_PARAMS_FAILED, "snd_pcm_hw_params() failed") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_PTHREAD_CREATE_FAILED, "pthread_create() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_CREATE_EVENT_FAILED, "CreateEvent() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_CREATE_DEVICE_ENUMERATOR_FAILED, "CoCreateInstance() for IMMDeviceEnumerator failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_GET_DEFAULT_AUDIO_ENDPOINT_FAILED, "IMMDeviceEnumerator.GetDefaultAudioEndpoint() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_DEVICE_ACTIVATE_FAILED, "IMMDevice.Activate() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_AUDIO_CLIENT_INITIALIZE_FAILED, "IAudioClient.Initialize() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_AUDIO_CLIENT_GET_BUFFER_SIZE_FAILED, "IAudioClient.GetBufferSize() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_AUDIO_CLIENT_GET_SERVICE_FAILED, "IAudioClient.GetService() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_AUDIO_CLIENT_SET_EVENT_HANDLE_FAILED, "IAudioClient.SetEventHandle() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_CREATE_THREAD_FAILED, "CreateThread() failed") \ + _SAUDIO_LOGITEM_XMACRO(AAUDIO_STREAMBUILDER_OPEN_STREAM_FAILED, "AAudioStreamBuilder_openStream() failed") \ + _SAUDIO_LOGITEM_XMACRO(AAUDIO_PTHREAD_CREATE_FAILED, "pthread_create() failed after AAUDIO_ERROR_DISCONNECTED") \ + _SAUDIO_LOGITEM_XMACRO(AAUDIO_RESTARTING_STREAM_AFTER_ERROR, "restarting AAudio stream after error") \ + _SAUDIO_LOGITEM_XMACRO(USING_AAUDIO_BACKEND, "using AAudio backend") \ + _SAUDIO_LOGITEM_XMACRO(AAUDIO_CREATE_STREAMBUILDER_FAILED, "AAudio_createStreamBuilder() failed") \ + _SAUDIO_LOGITEM_XMACRO(USING_SLES_BACKEND, "using OpenSLES backend") \ + _SAUDIO_LOGITEM_XMACRO(SLES_CREATE_ENGINE_FAILED, "slCreateEngine() failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_ENGINE_GET_ENGINE_INTERFACE_FAILED, "GetInterface() for SL_IID_ENGINE failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_CREATE_OUTPUT_MIX_FAILED, "CreateOutputMix() failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_MIXER_GET_VOLUME_INTERFACE_FAILED, "GetInterface() for SL_IID_VOLUME failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_ENGINE_CREATE_AUDIO_PLAYER_FAILED, "CreateAudioPlayer() failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_PLAYER_GET_PLAY_INTERFACE_FAILED, "GetInterface() for SL_IID_PLAY failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_PLAYER_GET_VOLUME_INTERFACE_FAILED, "GetInterface() for SL_IID_VOLUME failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_PLAYER_GET_BUFFERQUEUE_INTERFACE_FAILED, "GetInterface() for SL_IID_ANDROIDSIMPLEBUFFERQUEUE failed") \ + _SAUDIO_LOGITEM_XMACRO(COREAUDIO_NEW_OUTPUT_FAILED, "AudioQueueNewOutput() failed") \ + _SAUDIO_LOGITEM_XMACRO(COREAUDIO_ALLOCATE_BUFFER_FAILED, "AudioQueueAllocateBuffer() failed") \ + _SAUDIO_LOGITEM_XMACRO(COREAUDIO_START_FAILED, "AudioQueueStart() failed") \ + _SAUDIO_LOGITEM_XMACRO(BACKEND_BUFFER_SIZE_ISNT_MULTIPLE_OF_PACKET_SIZE, "backend buffer size isn't multiple of packet size") \ + +#define _SAUDIO_LOGITEM_XMACRO(item,msg) SAUDIO_LOGITEM_##item, +typedef enum saudio_log_item { + _SAUDIO_LOG_ITEMS +} saudio_log_item; +#undef _SAUDIO_LOGITEM_XMACRO + +/* + saudio_logger + + Used in saudio_desc to provide a custom logging and error reporting + callback to sokol-audio. +*/ +typedef struct saudio_logger { + void (*func)( + const char* tag, // always "saudio" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SAUDIO_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_audio.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} saudio_logger; + +/* + saudio_allocator + + Used in saudio_desc to provide custom memory-alloc and -free functions + to sokol_audio.h. If memory management should be overridden, both the + alloc_fn and free_fn function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct saudio_allocator { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} saudio_allocator; + +typedef struct saudio_desc { + int sample_rate; // requested sample rate + int num_channels; // number of channels, default: 1 (mono) + int buffer_frames; // number of frames in streaming buffer + int packet_frames; // number of frames in a packet + int num_packets; // number of packets in packet queue + void (*stream_cb)(float* buffer, int num_frames, int num_channels); // optional streaming callback (no user data) + void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data); //... and with user data + void* user_data; // optional user data argument for stream_userdata_cb + saudio_allocator allocator; // optional allocation override functions + saudio_logger logger; // optional logging function (default: NO LOGGING!) +} saudio_desc; + +/* setup sokol-audio */ +SOKOL_AUDIO_API_DECL void saudio_setup(const saudio_desc* desc); +/* shutdown sokol-audio */ +SOKOL_AUDIO_API_DECL void saudio_shutdown(void); +/* true after setup if audio backend was successfully initialized */ +SOKOL_AUDIO_API_DECL bool saudio_isvalid(void); +/* return the saudio_desc.user_data pointer */ +SOKOL_AUDIO_API_DECL void* saudio_userdata(void); +/* return a copy of the original saudio_desc struct */ +SOKOL_AUDIO_API_DECL saudio_desc saudio_query_desc(void); +/* actual sample rate */ +SOKOL_AUDIO_API_DECL int saudio_sample_rate(void); +/* return actual backend buffer size in number of frames */ +SOKOL_AUDIO_API_DECL int saudio_buffer_frames(void); +/* actual number of channels */ +SOKOL_AUDIO_API_DECL int saudio_channels(void); +/* return true if audio context is currently suspended (only in WebAudio backend, all other backends return false) */ +SOKOL_AUDIO_API_DECL bool saudio_suspended(void); +/* get current number of frames to fill packet queue */ +SOKOL_AUDIO_API_DECL int saudio_expect(void); +/* push sample frames from main thread, returns number of frames actually pushed */ +SOKOL_AUDIO_API_DECL int saudio_push(const float* frames, int num_frames); + +#ifdef __cplusplus +} /* extern "C" */ + +/* reference-based equivalents for c++ */ +inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc); } + +#endif +#endif // SOKOL_AUDIO_INCLUDED + +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation +#ifdef SOKOL_AUDIO_IMPL +#define SOKOL_AUDIO_IMPL_INCLUDED (1) + +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use saudio_desc.allocator to override memory allocation functions" +#endif + +#include // alloc, free +#include // memset, memcpy +#include // size_t + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif + +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif + +#ifndef _SOKOL_UNUSED + #define _SOKOL_UNUSED(x) (void)(x) +#endif + +// platform detection defines +#if defined(SOKOL_DUMMY_BACKEND) + // nothing +#elif defined(__APPLE__) + #define _SAUDIO_APPLE (1) + #include + #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE + #define _SAUDIO_IOS (1) + #else + #define _SAUDIO_MACOS (1) + #endif +#elif defined(__EMSCRIPTEN__) + #define _SAUDIO_EMSCRIPTEN (1) +#elif defined(_WIN32) + #define _SAUDIO_WINDOWS (1) + #include + #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) + #error "sokol_audio.h no longer supports UWP" + #endif +#elif defined(__ANDROID__) + #define _SAUDIO_ANDROID (1) + #if !defined(SAUDIO_ANDROID_SLES) && !defined(SAUDIO_ANDROID_AAUDIO) + #define SAUDIO_ANDROID_AAUDIO (1) + #endif +#elif defined(__linux__) || defined(__unix__) + #define _SAUDIO_LINUX (1) +#else +#error "sokol_audio.h: Unknown platform" +#endif + +// platform-specific headers and definitions +#if defined(SOKOL_DUMMY_BACKEND) + #define _SAUDIO_NOTHREADS (1) +#elif defined(_SAUDIO_WINDOWS) + #define _SAUDIO_WINTHREADS (1) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #include + #pragma comment (lib, "kernel32") + #pragma comment (lib, "ole32") + #ifndef CINTERFACE + #define CINTERFACE + #endif + #ifndef COBJMACROS + #define COBJMACROS + #endif + #ifndef CONST_VTABLE + #define CONST_VTABLE + #endif + #include + #include + static const IID _saudio_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, {0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2} }; + static const IID _saudio_IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35, {0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6} }; + static const CLSID _saudio_CLSID_IMMDeviceEnumerator = { 0xbcde0395, 0xe52f, 0x467c, {0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e} }; + static const IID _saudio_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483, {0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2} }; + static const IID _saudio_IID_Devinterface_Audio_Render = { 0xe6327cad, 0xdcec, 0x4949, {0xae, 0x8a, 0x99, 0x1e, 0x97, 0x6a, 0x79, 0xd2} }; + static const IID _saudio_IID_IActivateAudioInterface_Completion_Handler = { 0x94ea2b94, 0xe9cc, 0x49e0, {0xc0, 0xff, 0xee, 0x64, 0xca, 0x8f, 0x5b, 0x90} }; + static const GUID _saudio_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} }; + #if defined(__cplusplus) + #define _SOKOL_AUDIO_WIN32COM_ID(x) (x) + #else + #define _SOKOL_AUDIO_WIN32COM_ID(x) (&x) + #endif + /* fix for Visual Studio 2015 SDKs */ + #ifndef AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM + #define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 + #endif + #ifndef AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY + #define AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000 + #endif + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4505) /* unreferenced local function has been removed */ + #endif +#elif defined(_SAUDIO_APPLE) + #define _SAUDIO_PTHREADS (1) + #include + #if defined(_SAUDIO_IOS) + // always use system headers on iOS (for now at least) + #if !defined(SAUDIO_OSX_USE_SYSTEM_HEADERS) + #define SAUDIO_OSX_USE_SYSTEM_HEADERS (1) + #endif + #if !defined(__cplusplus) + #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields) + #error "sokol_audio.h on iOS requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)" + #endif + #endif + #include + #include + #else + #if defined(SAUDIO_OSX_USE_SYSTEM_HEADERS) + #include + #endif + #endif +#elif defined(_SAUDIO_ANDROID) + #define _SAUDIO_PTHREADS (1) + #include + #if defined(SAUDIO_ANDROID_SLES) + #include "SLES/OpenSLES_Android.h" + #elif defined(SAUDIO_ANDROID_AAUDIO) + #include "aaudio/AAudio.h" + #endif +#elif defined(_SAUDIO_LINUX) + #if !defined(__FreeBSD__) + #include + #endif + #define _SAUDIO_PTHREADS (1) + #include + #define ALSA_PCM_NEW_HW_PARAMS_API + #include +#elif defined(__EMSCRIPTEN__) + #define _SAUDIO_NOTHREADS (1) + #include +#endif + +#define _saudio_def(val, def) (((val) == 0) ? (def) : (val)) +#define _saudio_def_flt(val, def) (((val) == 0.0f) ? (def) : (val)) + +#define _SAUDIO_DEFAULT_SAMPLE_RATE (44100) +#define _SAUDIO_DEFAULT_BUFFER_FRAMES (2048) +#define _SAUDIO_DEFAULT_PACKET_FRAMES (128) +#define _SAUDIO_DEFAULT_NUM_PACKETS ((_SAUDIO_DEFAULT_BUFFER_FRAMES/_SAUDIO_DEFAULT_PACKET_FRAMES)*4) + +#ifndef SAUDIO_RING_MAX_SLOTS +#define SAUDIO_RING_MAX_SLOTS (1024) +#endif + +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs +#if defined(_SAUDIO_PTHREADS) + +typedef struct { + pthread_mutex_t mutex; +} _saudio_mutex_t; + +#elif defined(_SAUDIO_WINTHREADS) + +typedef struct { + CRITICAL_SECTION critsec; +} _saudio_mutex_t; + +#elif defined(_SAUDIO_NOTHREADS) + +typedef struct { + int dummy_mutex; +} _saudio_mutex_t; + +#endif + +#if defined(SOKOL_DUMMY_BACKEND) + +typedef struct { + int dummy; +} _saudio_dummy_backend_t; + +#elif defined(_SAUDIO_APPLE) + +#if defined(SAUDIO_OSX_USE_SYSTEM_HEADERS) + +typedef AudioQueueRef _saudio_AudioQueueRef; +typedef AudioQueueBufferRef _saudio_AudioQueueBufferRef; +typedef AudioStreamBasicDescription _saudio_AudioStreamBasicDescription; +typedef OSStatus _saudio_OSStatus; + +#define _saudio_kAudioFormatLinearPCM (kAudioFormatLinearPCM) +#define _saudio_kLinearPCMFormatFlagIsFloat (kLinearPCMFormatFlagIsFloat) +#define _saudio_kAudioFormatFlagIsPacked (kAudioFormatFlagIsPacked) + +#else +#ifdef __cplusplus +extern "C" { +#endif + +// embedded AudioToolbox declarations +typedef uint32_t _saudio_AudioFormatID; +typedef uint32_t _saudio_AudioFormatFlags; +typedef int32_t _saudio_OSStatus; +typedef uint32_t _saudio_SMPTETimeType; +typedef uint32_t _saudio_SMPTETimeFlags; +typedef uint32_t _saudio_AudioTimeStampFlags; +typedef void* _saudio_CFRunLoopRef; +typedef void* _saudio_CFStringRef; +typedef void* _saudio_AudioQueueRef; + +#define _saudio_kAudioFormatLinearPCM ('lpcm') +#define _saudio_kLinearPCMFormatFlagIsFloat (1U << 0) +#define _saudio_kAudioFormatFlagIsPacked (1U << 3) + +typedef struct _saudio_AudioStreamBasicDescription { + double mSampleRate; + _saudio_AudioFormatID mFormatID; + _saudio_AudioFormatFlags mFormatFlags; + uint32_t mBytesPerPacket; + uint32_t mFramesPerPacket; + uint32_t mBytesPerFrame; + uint32_t mChannelsPerFrame; + uint32_t mBitsPerChannel; + uint32_t mReserved; +} _saudio_AudioStreamBasicDescription; + +typedef struct _saudio_AudioStreamPacketDescription { + int64_t mStartOffset; + uint32_t mVariableFramesInPacket; + uint32_t mDataByteSize; +} _saudio_AudioStreamPacketDescription; + +typedef struct _saudio_SMPTETime { + int16_t mSubframes; + int16_t mSubframeDivisor; + uint32_t mCounter; + _saudio_SMPTETimeType mType; + _saudio_SMPTETimeFlags mFlags; + int16_t mHours; + int16_t mMinutes; + int16_t mSeconds; + int16_t mFrames; +} _saudio_SMPTETime; + +typedef struct _saudio_AudioTimeStamp { + double mSampleTime; + uint64_t mHostTime; + double mRateScalar; + uint64_t mWordClockTime; + _saudio_SMPTETime mSMPTETime; + _saudio_AudioTimeStampFlags mFlags; + uint32_t mReserved; +} _saudio_AudioTimeStamp; + +typedef struct _saudio_AudioQueueBuffer { + const uint32_t mAudioDataBytesCapacity; + void* const mAudioData; + uint32_t mAudioDataByteSize; + void * mUserData; + const uint32_t mPacketDescriptionCapacity; + _saudio_AudioStreamPacketDescription* const mPacketDescriptions; + uint32_t mPacketDescriptionCount; +} _saudio_AudioQueueBuffer; +typedef _saudio_AudioQueueBuffer* _saudio_AudioQueueBufferRef; + +typedef void (*_saudio_AudioQueueOutputCallback)(void* user_data, _saudio_AudioQueueRef inAQ, _saudio_AudioQueueBufferRef inBuffer); + +extern _saudio_OSStatus AudioQueueNewOutput(const _saudio_AudioStreamBasicDescription* inFormat, _saudio_AudioQueueOutputCallback inCallbackProc, void* inUserData, _saudio_CFRunLoopRef inCallbackRunLoop, _saudio_CFStringRef inCallbackRunLoopMode, uint32_t inFlags, _saudio_AudioQueueRef* outAQ); +extern _saudio_OSStatus AudioQueueDispose(_saudio_AudioQueueRef inAQ, bool inImmediate); +extern _saudio_OSStatus AudioQueueAllocateBuffer(_saudio_AudioQueueRef inAQ, uint32_t inBufferByteSize, _saudio_AudioQueueBufferRef* outBuffer); +extern _saudio_OSStatus AudioQueueEnqueueBuffer(_saudio_AudioQueueRef inAQ, _saudio_AudioQueueBufferRef inBuffer, uint32_t inNumPacketDescs, const _saudio_AudioStreamPacketDescription* inPacketDescs); +extern _saudio_OSStatus AudioQueueStart(_saudio_AudioQueueRef inAQ, const _saudio_AudioTimeStamp * inStartTime); +extern _saudio_OSStatus AudioQueueStop(_saudio_AudioQueueRef inAQ, bool inImmediate); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SAUDIO_OSX_USE_SYSTEM_HEADERS + +typedef struct { + _saudio_AudioQueueRef ca_audio_queue; + #if defined(_SAUDIO_IOS) + id ca_interruption_handler; + #endif +} _saudio_apple_backend_t; + +#elif defined(_SAUDIO_LINUX) + +typedef struct { + snd_pcm_t* device; + float* buffer; + int buffer_byte_size; + int buffer_frames; + pthread_t thread; + bool thread_stop; +} _saudio_alsa_backend_t; + +#elif defined(SAUDIO_ANDROID_SLES) + +#define SAUDIO_SLES_NUM_BUFFERS (2) + +typedef struct { + pthread_mutex_t mutex; + pthread_cond_t cond; + int count; +} _saudio_sles_semaphore_t; + +typedef struct { + SLObjectItf engine_obj; + SLEngineItf engine; + SLObjectItf output_mix_obj; + SLVolumeItf output_mix_vol; + SLDataLocator_OutputMix out_locator; + SLDataSink dst_data_sink; + SLObjectItf player_obj; + SLPlayItf player; + SLVolumeItf player_vol; + SLAndroidSimpleBufferQueueItf player_buffer_queue; + + int16_t* output_buffers[SAUDIO_SLES_NUM_BUFFERS]; + float* src_buffer; + int active_buffer; + _saudio_sles_semaphore_t buffer_sem; + pthread_t thread; + volatile int thread_stop; + SLDataLocator_AndroidSimpleBufferQueue in_locator; +} _saudio_sles_backend_t; + +#elif defined(SAUDIO_ANDROID_AAUDIO) + +typedef struct { + AAudioStreamBuilder* builder; + AAudioStream* stream; + pthread_t thread; + pthread_mutex_t mutex; +} _saudio_aaudio_backend_t; + +#elif defined(_SAUDIO_WINDOWS) + +typedef struct { + HANDLE thread_handle; + HANDLE buffer_end_event; + bool stop; + UINT32 dst_buffer_frames; + int src_buffer_frames; + int src_buffer_byte_size; + int src_buffer_pos; + float* src_buffer; +} _saudio_wasapi_thread_data_t; + +typedef struct { + IMMDeviceEnumerator* device_enumerator; + IMMDevice* device; + IAudioClient* audio_client; + IAudioRenderClient* render_client; + _saudio_wasapi_thread_data_t thread; +} _saudio_wasapi_backend_t; + +#elif defined(_SAUDIO_EMSCRIPTEN) + +typedef struct { + uint8_t* buffer; +} _saudio_web_backend_t; + +#else +#error "unknown platform" +#endif + +#if defined(SOKOL_DUMMY_BACKEND) +typedef _saudio_dummy_backend_t _saudio_backend_t; +#elif defined(_SAUDIO_APPLE) +typedef _saudio_apple_backend_t _saudio_backend_t; +#elif defined(_SAUDIO_EMSCRIPTEN) +typedef _saudio_web_backend_t _saudio_backend_t; +#elif defined(_SAUDIO_WINDOWS) +typedef _saudio_wasapi_backend_t _saudio_backend_t; +#elif defined(SAUDIO_ANDROID_SLES) +typedef _saudio_sles_backend_t _saudio_backend_t; +#elif defined(SAUDIO_ANDROID_AAUDIO) +typedef _saudio_aaudio_backend_t _saudio_backend_t; +#elif defined(_SAUDIO_LINUX) +typedef _saudio_alsa_backend_t _saudio_backend_t; +#endif + +/* a ringbuffer structure */ +typedef struct { + int head; // next slot to write to + int tail; // next slot to read from + int num; // number of slots in queue + int queue[SAUDIO_RING_MAX_SLOTS]; +} _saudio_ring_t; + +/* a packet FIFO structure */ +typedef struct { + bool valid; + int packet_size; /* size of a single packets in bytes(!) */ + int num_packets; /* number of packet in fifo */ + uint8_t* base_ptr; /* packet memory chunk base pointer (dynamically allocated) */ + int cur_packet; /* current write-packet */ + int cur_offset; /* current byte-offset into current write packet */ + _saudio_mutex_t mutex; /* mutex for thread-safe access */ + _saudio_ring_t read_queue; /* buffers with data, ready to be streamed */ + _saudio_ring_t write_queue; /* empty buffers, ready to be pushed to */ +} _saudio_fifo_t; + +/* sokol-audio state */ +typedef struct { + bool valid; + void (*stream_cb)(float* buffer, int num_frames, int num_channels); + void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data); + void* user_data; + int sample_rate; /* sample rate */ + int buffer_frames; /* number of frames in streaming buffer */ + int bytes_per_frame; /* filled by backend */ + int packet_frames; /* number of frames in a packet */ + int num_packets; /* number of packets in packet queue */ + int num_channels; /* actual number of channels */ + saudio_desc desc; + _saudio_fifo_t fifo; + _saudio_backend_t backend; +} _saudio_state_t; + +_SOKOL_PRIVATE _saudio_state_t _saudio; + +_SOKOL_PRIVATE bool _saudio_has_callback(void) { + return (_saudio.stream_cb || _saudio.stream_userdata_cb); +} + +_SOKOL_PRIVATE void _saudio_stream_callback(float* buffer, int num_frames, int num_channels) { + if (_saudio.stream_cb) { + _saudio.stream_cb(buffer, num_frames, num_channels); + } + else if (_saudio.stream_userdata_cb) { + _saudio.stream_userdata_cb(buffer, num_frames, num_channels, _saudio.user_data); + } +} + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SAUDIO_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _saudio_log_messages[] = { + _SAUDIO_LOG_ITEMS +}; +#undef _SAUDIO_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SAUDIO_PANIC(code) _saudio_log(SAUDIO_LOGITEM_ ##code, 0, __LINE__) +#define _SAUDIO_ERROR(code) _saudio_log(SAUDIO_LOGITEM_ ##code, 1, __LINE__) +#define _SAUDIO_WARN(code) _saudio_log(SAUDIO_LOGITEM_ ##code, 2, __LINE__) +#define _SAUDIO_INFO(code) _saudio_log(SAUDIO_LOGITEM_ ##code, 3, __LINE__) + +static void _saudio_log(saudio_log_item log_item, uint32_t log_level, uint32_t line_nr) { + if (_saudio.desc.logger.func) { + #if defined(SOKOL_DEBUG) + const char* filename = __FILE__; + const char* message = _saudio_log_messages[log_item]; + #else + const char* filename = 0; + const char* message = 0; + #endif + _saudio.desc.logger.func("saudio", log_level, log_item, message, line_nr, filename, _saudio.desc.logger.user_data); + } + else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory +_SOKOL_PRIVATE void _saudio_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +_SOKOL_PRIVATE void* _saudio_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_saudio.desc.allocator.alloc_fn) { + ptr = _saudio.desc.allocator.alloc_fn(size, _saudio.desc.allocator.user_data); + } else { + ptr = malloc(size); + } + if (0 == ptr) { + _SAUDIO_PANIC(MALLOC_FAILED); + } + return ptr; +} + +_SOKOL_PRIVATE void* _saudio_malloc_clear(size_t size) { + void* ptr = _saudio_malloc(size); + _saudio_clear(ptr, size); + return ptr; +} + +_SOKOL_PRIVATE void _saudio_free(void* ptr) { + if (_saudio.desc.allocator.free_fn) { + _saudio.desc.allocator.free_fn(ptr, _saudio.desc.allocator.user_data); + } else { + free(ptr); + } +} + +// ███ ███ ██ ██ ████████ ███████ ██ ██ +// ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ ██ ██ ██ █████ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██████ ██ ███████ ██ ██ +// +// >>mutex +#if defined(_SAUDIO_NOTHREADS) + +_SOKOL_PRIVATE void _saudio_mutex_init(_saudio_mutex_t* m) { (void)m; } +_SOKOL_PRIVATE void _saudio_mutex_destroy(_saudio_mutex_t* m) { (void)m; } +_SOKOL_PRIVATE void _saudio_mutex_lock(_saudio_mutex_t* m) { (void)m; } +_SOKOL_PRIVATE void _saudio_mutex_unlock(_saudio_mutex_t* m) { (void)m; } + +#elif defined(_SAUDIO_PTHREADS) + +_SOKOL_PRIVATE void _saudio_mutex_init(_saudio_mutex_t* m) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutex_init(&m->mutex, &attr); +} + +_SOKOL_PRIVATE void _saudio_mutex_destroy(_saudio_mutex_t* m) { + pthread_mutex_destroy(&m->mutex); +} + +_SOKOL_PRIVATE void _saudio_mutex_lock(_saudio_mutex_t* m) { + pthread_mutex_lock(&m->mutex); +} + +_SOKOL_PRIVATE void _saudio_mutex_unlock(_saudio_mutex_t* m) { + pthread_mutex_unlock(&m->mutex); +} + +#elif defined(_SAUDIO_WINTHREADS) + +_SOKOL_PRIVATE void _saudio_mutex_init(_saudio_mutex_t* m) { + InitializeCriticalSection(&m->critsec); +} + +_SOKOL_PRIVATE void _saudio_mutex_destroy(_saudio_mutex_t* m) { + DeleteCriticalSection(&m->critsec); +} + +_SOKOL_PRIVATE void _saudio_mutex_lock(_saudio_mutex_t* m) { + EnterCriticalSection(&m->critsec); +} + +_SOKOL_PRIVATE void _saudio_mutex_unlock(_saudio_mutex_t* m) { + LeaveCriticalSection(&m->critsec); +} +#else +#error "sokol_audio.h: unknown platform!" +#endif + +// ██████ ██ ███ ██ ██████ ██████ ██ ██ ███████ ███████ ███████ ██████ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ ███ ██████ ██ ██ █████ █████ █████ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ████ ██████ ██████ ██████ ██ ██ ███████ ██ ██ +// +// >>ringbuffer +_SOKOL_PRIVATE int _saudio_ring_idx(_saudio_ring_t* ring, int i) { + return (i % ring->num); +} + +_SOKOL_PRIVATE void _saudio_ring_init(_saudio_ring_t* ring, int num_slots) { + SOKOL_ASSERT((num_slots + 1) <= SAUDIO_RING_MAX_SLOTS); + ring->head = 0; + ring->tail = 0; + /* one slot reserved to detect 'full' vs 'empty' */ + ring->num = num_slots + 1; +} + +_SOKOL_PRIVATE bool _saudio_ring_full(_saudio_ring_t* ring) { + return _saudio_ring_idx(ring, ring->head + 1) == ring->tail; +} + +_SOKOL_PRIVATE bool _saudio_ring_empty(_saudio_ring_t* ring) { + return ring->head == ring->tail; +} + +_SOKOL_PRIVATE int _saudio_ring_count(_saudio_ring_t* ring) { + int count; + if (ring->head >= ring->tail) { + count = ring->head - ring->tail; + } + else { + count = (ring->head + ring->num) - ring->tail; + } + SOKOL_ASSERT(count < ring->num); + return count; +} + +_SOKOL_PRIVATE void _saudio_ring_enqueue(_saudio_ring_t* ring, int val) { + SOKOL_ASSERT(!_saudio_ring_full(ring)); + ring->queue[ring->head] = val; + ring->head = _saudio_ring_idx(ring, ring->head + 1); +} + +_SOKOL_PRIVATE int _saudio_ring_dequeue(_saudio_ring_t* ring) { + SOKOL_ASSERT(!_saudio_ring_empty(ring)); + int val = ring->queue[ring->tail]; + ring->tail = _saudio_ring_idx(ring, ring->tail + 1); + return val; +} + +// ███████ ██ ███████ ██████ +// ██ ██ ██ ██ ██ +// █████ ██ █████ ██ ██ +// ██ ██ ██ ██ ██ +// ██ ██ ██ ██████ +// +// >>fifo +_SOKOL_PRIVATE void _saudio_fifo_init_mutex(_saudio_fifo_t* fifo) { + /* this must be called before initializing both the backend and the fifo itself! */ + _saudio_mutex_init(&fifo->mutex); +} + +_SOKOL_PRIVATE void _saudio_fifo_destroy_mutex(_saudio_fifo_t* fifo) { + _saudio_mutex_destroy(&fifo->mutex); +} + +_SOKOL_PRIVATE void _saudio_fifo_init(_saudio_fifo_t* fifo, int packet_size, int num_packets) { + /* NOTE: there's a chicken-egg situation during the init phase where the + streaming thread must be started before the fifo is actually initialized, + thus the fifo init must already be protected from access by the fifo_read() func. + */ + _saudio_mutex_lock(&fifo->mutex); + SOKOL_ASSERT((packet_size > 0) && (num_packets > 0)); + fifo->packet_size = packet_size; + fifo->num_packets = num_packets; + fifo->base_ptr = (uint8_t*) _saudio_malloc((size_t)(packet_size * num_packets)); + fifo->cur_packet = -1; + fifo->cur_offset = 0; + _saudio_ring_init(&fifo->read_queue, num_packets); + _saudio_ring_init(&fifo->write_queue, num_packets); + for (int i = 0; i < num_packets; i++) { + _saudio_ring_enqueue(&fifo->write_queue, i); + } + SOKOL_ASSERT(_saudio_ring_full(&fifo->write_queue)); + SOKOL_ASSERT(_saudio_ring_count(&fifo->write_queue) == num_packets); + SOKOL_ASSERT(_saudio_ring_empty(&fifo->read_queue)); + SOKOL_ASSERT(_saudio_ring_count(&fifo->read_queue) == 0); + fifo->valid = true; + _saudio_mutex_unlock(&fifo->mutex); +} + +_SOKOL_PRIVATE void _saudio_fifo_shutdown(_saudio_fifo_t* fifo) { + SOKOL_ASSERT(fifo->base_ptr); + _saudio_free(fifo->base_ptr); + fifo->base_ptr = 0; + fifo->valid = false; +} + +_SOKOL_PRIVATE int _saudio_fifo_writable_bytes(_saudio_fifo_t* fifo) { + _saudio_mutex_lock(&fifo->mutex); + int num_bytes = (_saudio_ring_count(&fifo->write_queue) * fifo->packet_size); + if (fifo->cur_packet != -1) { + num_bytes += fifo->packet_size - fifo->cur_offset; + } + _saudio_mutex_unlock(&fifo->mutex); + SOKOL_ASSERT((num_bytes >= 0) && (num_bytes <= (fifo->num_packets * fifo->packet_size))); + return num_bytes; +} + +/* write new data to the write queue, this is called from main thread */ +_SOKOL_PRIVATE int _saudio_fifo_write(_saudio_fifo_t* fifo, const uint8_t* ptr, int num_bytes) { + /* returns the number of bytes written, this will be smaller then requested + if the write queue runs full + */ + int all_to_copy = num_bytes; + while (all_to_copy > 0) { + /* need to grab a new packet? */ + if (fifo->cur_packet == -1) { + _saudio_mutex_lock(&fifo->mutex); + if (!_saudio_ring_empty(&fifo->write_queue)) { + fifo->cur_packet = _saudio_ring_dequeue(&fifo->write_queue); + } + _saudio_mutex_unlock(&fifo->mutex); + SOKOL_ASSERT(fifo->cur_offset == 0); + } + /* append data to current write packet */ + if (fifo->cur_packet != -1) { + int to_copy = all_to_copy; + const int max_copy = fifo->packet_size - fifo->cur_offset; + if (to_copy > max_copy) { + to_copy = max_copy; + } + uint8_t* dst = fifo->base_ptr + fifo->cur_packet * fifo->packet_size + fifo->cur_offset; + memcpy(dst, ptr, (size_t)to_copy); + ptr += to_copy; + fifo->cur_offset += to_copy; + all_to_copy -= to_copy; + SOKOL_ASSERT(fifo->cur_offset <= fifo->packet_size); + SOKOL_ASSERT(all_to_copy >= 0); + } + else { + /* early out if we're starving */ + int bytes_copied = num_bytes - all_to_copy; + SOKOL_ASSERT((bytes_copied >= 0) && (bytes_copied < num_bytes)); + return bytes_copied; + } + /* if write packet is full, push to read queue */ + if (fifo->cur_offset == fifo->packet_size) { + _saudio_mutex_lock(&fifo->mutex); + _saudio_ring_enqueue(&fifo->read_queue, fifo->cur_packet); + _saudio_mutex_unlock(&fifo->mutex); + fifo->cur_packet = -1; + fifo->cur_offset = 0; + } + } + SOKOL_ASSERT(all_to_copy == 0); + return num_bytes; +} + +/* read queued data, this is called form the stream callback (maybe separate thread) */ +_SOKOL_PRIVATE int _saudio_fifo_read(_saudio_fifo_t* fifo, uint8_t* ptr, int num_bytes) { + /* NOTE: fifo_read might be called before the fifo is properly initialized */ + _saudio_mutex_lock(&fifo->mutex); + int num_bytes_copied = 0; + if (fifo->valid) { + SOKOL_ASSERT(0 == (num_bytes % fifo->packet_size)); + SOKOL_ASSERT(num_bytes <= (fifo->packet_size * fifo->num_packets)); + const int num_packets_needed = num_bytes / fifo->packet_size; + uint8_t* dst = ptr; + /* either pull a full buffer worth of data, or nothing */ + if (_saudio_ring_count(&fifo->read_queue) >= num_packets_needed) { + for (int i = 0; i < num_packets_needed; i++) { + int packet_index = _saudio_ring_dequeue(&fifo->read_queue); + _saudio_ring_enqueue(&fifo->write_queue, packet_index); + const uint8_t* src = fifo->base_ptr + packet_index * fifo->packet_size; + memcpy(dst, src, (size_t)fifo->packet_size); + dst += fifo->packet_size; + num_bytes_copied += fifo->packet_size; + } + SOKOL_ASSERT(num_bytes == num_bytes_copied); + } + } + _saudio_mutex_unlock(&fifo->mutex); + return num_bytes_copied; +} + +// ██████ ██ ██ ███ ███ ███ ███ ██ ██ +// ██ ██ ██ ██ ████ ████ ████ ████ ██ ██ +// ██ ██ ██ ██ ██ ████ ██ ██ ████ ██ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ██ ██ ██ ██ +// +// >>dummy +#if defined(SOKOL_DUMMY_BACKEND) +_SOKOL_PRIVATE bool _saudio_dummy_backend_init(void) { + _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); + return true; +}; +_SOKOL_PRIVATE void _saudio_dummy_backend_shutdown(void) { }; + +// █████ ██ ███████ █████ +// ██ ██ ██ ██ ██ ██ +// ███████ ██ ███████ ███████ +// ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██ ██ +// +// >>alsa +#elif defined(_SAUDIO_LINUX) + +/* the streaming callback runs in a separate thread */ +_SOKOL_PRIVATE void* _saudio_alsa_cb(void* param) { + _SOKOL_UNUSED(param); + while (!_saudio.backend.thread_stop) { + /* snd_pcm_writei() will be blocking until it needs data */ + int write_res = snd_pcm_writei(_saudio.backend.device, _saudio.backend.buffer, (snd_pcm_uframes_t)_saudio.backend.buffer_frames); + if (write_res < 0) { + /* underrun occurred */ + snd_pcm_prepare(_saudio.backend.device); + } + else { + /* fill the streaming buffer with new data */ + if (_saudio_has_callback()) { + _saudio_stream_callback(_saudio.backend.buffer, _saudio.backend.buffer_frames, _saudio.num_channels); + } + else { + if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.buffer, _saudio.backend.buffer_byte_size)) { + /* not enough read data available, fill the entire buffer with silence */ + _saudio_clear(_saudio.backend.buffer, (size_t)_saudio.backend.buffer_byte_size); + } + } + } + } + return 0; +} + +_SOKOL_PRIVATE bool _saudio_alsa_backend_init(void) { + int dir; uint32_t rate; + int rc = snd_pcm_open(&_saudio.backend.device, "default", SND_PCM_STREAM_PLAYBACK, 0); + if (rc < 0) { + _SAUDIO_ERROR(ALSA_SND_PCM_OPEN_FAILED); + return false; + } + + /* configuration works by restricting the 'configuration space' step + by step, we require all parameters except the sample rate to + match perfectly + */ + snd_pcm_hw_params_t* params = 0; + snd_pcm_hw_params_alloca(¶ms); + snd_pcm_hw_params_any(_saudio.backend.device, params); + snd_pcm_hw_params_set_access(_saudio.backend.device, params, SND_PCM_ACCESS_RW_INTERLEAVED); + if (0 > snd_pcm_hw_params_set_format(_saudio.backend.device, params, SND_PCM_FORMAT_FLOAT_LE)) { + _SAUDIO_ERROR(ALSA_FLOAT_SAMPLES_NOT_SUPPORTED); + goto error; + } + if (0 > snd_pcm_hw_params_set_buffer_size(_saudio.backend.device, params, (snd_pcm_uframes_t)_saudio.buffer_frames)) { + _SAUDIO_ERROR(ALSA_REQUESTED_BUFFER_SIZE_NOT_SUPPORTED); + goto error; + } + if (0 > snd_pcm_hw_params_set_channels(_saudio.backend.device, params, (uint32_t)_saudio.num_channels)) { + _SAUDIO_ERROR(ALSA_REQUESTED_CHANNEL_COUNT_NOT_SUPPORTED); + goto error; + } + /* let ALSA pick a nearby sampling rate */ + rate = (uint32_t) _saudio.sample_rate; + dir = 0; + if (0 > snd_pcm_hw_params_set_rate_near(_saudio.backend.device, params, &rate, &dir)) { + _SAUDIO_ERROR(ALSA_SND_PCM_HW_PARAMS_SET_RATE_NEAR_FAILED); + goto error; + } + if (0 > snd_pcm_hw_params(_saudio.backend.device, params)) { + _SAUDIO_ERROR(ALSA_SND_PCM_HW_PARAMS_FAILED); + goto error; + } + + /* read back actual sample rate and channels */ + _saudio.sample_rate = (int)rate; + _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); + + /* allocate the streaming buffer */ + _saudio.backend.buffer_byte_size = _saudio.buffer_frames * _saudio.bytes_per_frame; + _saudio.backend.buffer_frames = _saudio.buffer_frames; + _saudio.backend.buffer = (float*) _saudio_malloc_clear((size_t)_saudio.backend.buffer_byte_size); + + /* create the buffer-streaming start thread */ + if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_alsa_cb, 0)) { + _SAUDIO_ERROR(ALSA_PTHREAD_CREATE_FAILED); + goto error; + } + + return true; +error: + if (_saudio.backend.device) { + snd_pcm_close(_saudio.backend.device); + _saudio.backend.device = 0; + } + return false; +}; + +_SOKOL_PRIVATE void _saudio_alsa_backend_shutdown(void) { + SOKOL_ASSERT(_saudio.backend.device); + _saudio.backend.thread_stop = true; + pthread_join(_saudio.backend.thread, 0); + snd_pcm_drain(_saudio.backend.device); + snd_pcm_close(_saudio.backend.device); + _saudio_free(_saudio.backend.buffer); +}; + +// ██ ██ █████ ███████ █████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ █ ██ ███████ ███████ ███████ ██████ ██ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ +// ███ ███ ██ ██ ███████ ██ ██ ██ ██ +// +// >>wasapi +#elif defined(_SAUDIO_WINDOWS) + +/* fill intermediate buffer with new data and reset buffer_pos */ +_SOKOL_PRIVATE void _saudio_wasapi_fill_buffer(void) { + if (_saudio_has_callback()) { + _saudio_stream_callback(_saudio.backend.thread.src_buffer, _saudio.backend.thread.src_buffer_frames, _saudio.num_channels); + } + else { + if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.thread.src_buffer, _saudio.backend.thread.src_buffer_byte_size)) { + /* not enough read data available, fill the entire buffer with silence */ + _saudio_clear(_saudio.backend.thread.src_buffer, (size_t)_saudio.backend.thread.src_buffer_byte_size); + } + } +} + +_SOKOL_PRIVATE int _saudio_wasapi_min(int a, int b) { + return (a < b) ? a : b; +} + +_SOKOL_PRIVATE void _saudio_wasapi_submit_buffer(int num_frames) { + BYTE* wasapi_buffer = 0; + if (FAILED(IAudioRenderClient_GetBuffer(_saudio.backend.render_client, num_frames, &wasapi_buffer))) { + return; + } + SOKOL_ASSERT(wasapi_buffer); + + /* copy samples to WASAPI buffer, refill source buffer if needed */ + int num_remaining_samples = num_frames * _saudio.num_channels; + int buffer_pos = _saudio.backend.thread.src_buffer_pos; + const int buffer_size_in_samples = _saudio.backend.thread.src_buffer_byte_size / (int)sizeof(float); + float* dst = (float*)wasapi_buffer; + const float* dst_end = dst + num_remaining_samples; + _SOKOL_UNUSED(dst_end); // suppress unused warning in release mode + const float* src = _saudio.backend.thread.src_buffer; + + while (num_remaining_samples > 0) { + if (0 == buffer_pos) { + _saudio_wasapi_fill_buffer(); + } + const int samples_to_copy = _saudio_wasapi_min(num_remaining_samples, buffer_size_in_samples - buffer_pos); + SOKOL_ASSERT((buffer_pos + samples_to_copy) <= buffer_size_in_samples); + SOKOL_ASSERT((dst + samples_to_copy) <= dst_end); + memcpy(dst, &src[buffer_pos], (size_t)samples_to_copy * sizeof(float)); + num_remaining_samples -= samples_to_copy; + SOKOL_ASSERT(num_remaining_samples >= 0); + buffer_pos += samples_to_copy; + dst += samples_to_copy; + + SOKOL_ASSERT(buffer_pos <= buffer_size_in_samples); + if (buffer_pos == buffer_size_in_samples) { + buffer_pos = 0; + } + } + _saudio.backend.thread.src_buffer_pos = buffer_pos; + IAudioRenderClient_ReleaseBuffer(_saudio.backend.render_client, num_frames, 0); +} + +_SOKOL_PRIVATE DWORD WINAPI _saudio_wasapi_thread_fn(LPVOID param) { + (void)param; + _saudio_wasapi_submit_buffer(_saudio.backend.thread.src_buffer_frames); + IAudioClient_Start(_saudio.backend.audio_client); + while (!_saudio.backend.thread.stop) { + WaitForSingleObject(_saudio.backend.thread.buffer_end_event, INFINITE); + UINT32 padding = 0; + if (FAILED(IAudioClient_GetCurrentPadding(_saudio.backend.audio_client, &padding))) { + continue; + } + SOKOL_ASSERT(_saudio.backend.thread.dst_buffer_frames >= padding); + int num_frames = (int)_saudio.backend.thread.dst_buffer_frames - (int)padding; + if (num_frames > 0) { + _saudio_wasapi_submit_buffer(num_frames); + } + } + return 0; +} + +_SOKOL_PRIVATE void _saudio_wasapi_release(void) { + if (_saudio.backend.thread.src_buffer) { + _saudio_free(_saudio.backend.thread.src_buffer); + _saudio.backend.thread.src_buffer = 0; + } + if (_saudio.backend.render_client) { + IAudioRenderClient_Release(_saudio.backend.render_client); + _saudio.backend.render_client = 0; + } + if (_saudio.backend.audio_client) { + IAudioClient_Release(_saudio.backend.audio_client); + _saudio.backend.audio_client = 0; + } + if (_saudio.backend.device) { + IMMDevice_Release(_saudio.backend.device); + _saudio.backend.device = 0; + } + if (_saudio.backend.device_enumerator) { + IMMDeviceEnumerator_Release(_saudio.backend.device_enumerator); + _saudio.backend.device_enumerator = 0; + } + if (0 != _saudio.backend.thread.buffer_end_event) { + CloseHandle(_saudio.backend.thread.buffer_end_event); + _saudio.backend.thread.buffer_end_event = 0; + } +} + +_SOKOL_PRIVATE bool _saudio_wasapi_backend_init(void) { + REFERENCE_TIME dur; + /* CoInitializeEx could have been called elsewhere already, in which + case the function returns with S_FALSE (thus it does not make much + sense to check the result) + */ + HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED); + _SOKOL_UNUSED(hr); + _saudio.backend.thread.buffer_end_event = CreateEvent(0, FALSE, FALSE, 0); + if (0 == _saudio.backend.thread.buffer_end_event) { + _SAUDIO_ERROR(WASAPI_CREATE_EVENT_FAILED); + goto error; + } + if (FAILED(CoCreateInstance(_SOKOL_AUDIO_WIN32COM_ID(_saudio_CLSID_IMMDeviceEnumerator), + 0, CLSCTX_ALL, + _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IMMDeviceEnumerator), + (void**)&_saudio.backend.device_enumerator))) + { + _SAUDIO_ERROR(WASAPI_CREATE_DEVICE_ENUMERATOR_FAILED); + goto error; + } + if (FAILED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(_saudio.backend.device_enumerator, + eRender, eConsole, + &_saudio.backend.device))) + { + _SAUDIO_ERROR(WASAPI_GET_DEFAULT_AUDIO_ENDPOINT_FAILED); + goto error; + } + if (FAILED(IMMDevice_Activate(_saudio.backend.device, + _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioClient), + CLSCTX_ALL, 0, + (void**)&_saudio.backend.audio_client))) + { + _SAUDIO_ERROR(WASAPI_DEVICE_ACTIVATE_FAILED); + goto error; + } + + WAVEFORMATEXTENSIBLE fmtex; + _saudio_clear(&fmtex, sizeof(fmtex)); + fmtex.Format.nChannels = (WORD)_saudio.num_channels; + fmtex.Format.nSamplesPerSec = (DWORD)_saudio.sample_rate; + fmtex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + fmtex.Format.wBitsPerSample = 32; + fmtex.Format.nBlockAlign = (fmtex.Format.nChannels * fmtex.Format.wBitsPerSample) / 8; + fmtex.Format.nAvgBytesPerSec = fmtex.Format.nSamplesPerSec * fmtex.Format.nBlockAlign; + fmtex.Format.cbSize = 22; /* WORD + DWORD + GUID */ + fmtex.Samples.wValidBitsPerSample = 32; + if (_saudio.num_channels == 1) { + fmtex.dwChannelMask = SPEAKER_FRONT_CENTER; + } + else { + fmtex.dwChannelMask = SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT; + } + fmtex.SubFormat = _saudio_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + dur = (REFERENCE_TIME) + (((double)_saudio.buffer_frames) / (((double)_saudio.sample_rate) * (1.0/10000000.0))); + if (FAILED(IAudioClient_Initialize(_saudio.backend.audio_client, + AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK|AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM|AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY, + dur, 0, (WAVEFORMATEX*)&fmtex, 0))) + { + _SAUDIO_ERROR(WASAPI_AUDIO_CLIENT_INITIALIZE_FAILED); + goto error; + } + if (FAILED(IAudioClient_GetBufferSize(_saudio.backend.audio_client, &_saudio.backend.thread.dst_buffer_frames))) { + _SAUDIO_ERROR(WASAPI_AUDIO_CLIENT_GET_BUFFER_SIZE_FAILED); + goto error; + } + if (FAILED(IAudioClient_GetService(_saudio.backend.audio_client, + _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioRenderClient), + (void**)&_saudio.backend.render_client))) + { + _SAUDIO_ERROR(WASAPI_AUDIO_CLIENT_GET_SERVICE_FAILED); + goto error; + } + if (FAILED(IAudioClient_SetEventHandle(_saudio.backend.audio_client, _saudio.backend.thread.buffer_end_event))) { + _SAUDIO_ERROR(WASAPI_AUDIO_CLIENT_SET_EVENT_HANDLE_FAILED); + goto error; + } + _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); + _saudio.backend.thread.src_buffer_frames = _saudio.buffer_frames; + _saudio.backend.thread.src_buffer_byte_size = _saudio.backend.thread.src_buffer_frames * _saudio.bytes_per_frame; + + /* allocate an intermediate buffer for sample format conversion */ + _saudio.backend.thread.src_buffer = (float*) _saudio_malloc((size_t)_saudio.backend.thread.src_buffer_byte_size); + + /* create streaming thread */ + _saudio.backend.thread.thread_handle = CreateThread(NULL, 0, _saudio_wasapi_thread_fn, 0, 0, 0); + if (0 == _saudio.backend.thread.thread_handle) { + _SAUDIO_ERROR(WASAPI_CREATE_THREAD_FAILED); + goto error; + } + return true; +error: + _saudio_wasapi_release(); + return false; +} + +_SOKOL_PRIVATE void _saudio_wasapi_backend_shutdown(void) { + if (_saudio.backend.thread.thread_handle) { + _saudio.backend.thread.stop = true; + SetEvent(_saudio.backend.thread.buffer_end_event); + WaitForSingleObject(_saudio.backend.thread.thread_handle, INFINITE); + CloseHandle(_saudio.backend.thread.thread_handle); + _saudio.backend.thread.thread_handle = 0; + } + if (_saudio.backend.audio_client) { + IAudioClient_Stop(_saudio.backend.audio_client); + } + _saudio_wasapi_release(); + CoUninitialize(); +} + +// ██ ██ ███████ ██████ █████ ██ ██ ██████ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ █ ██ █████ ██████ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███ ███ ███████ ██████ ██ ██ ██████ ██████ ██ ██████ +// +// >>webaudio +#elif defined(_SAUDIO_EMSCRIPTEN) + +#ifdef __cplusplus +extern "C" { +#endif + +EMSCRIPTEN_KEEPALIVE int _saudio_emsc_pull(int num_frames) { + SOKOL_ASSERT(_saudio.backend.buffer); + if (num_frames == _saudio.buffer_frames) { + if (_saudio_has_callback()) { + _saudio_stream_callback((float*)_saudio.backend.buffer, num_frames, _saudio.num_channels); + } + else { + const int num_bytes = num_frames * _saudio.bytes_per_frame; + if (0 == _saudio_fifo_read(&_saudio.fifo, _saudio.backend.buffer, num_bytes)) { + /* not enough read data available, fill the entire buffer with silence */ + _saudio_clear(_saudio.backend.buffer, (size_t)num_bytes); + } + } + int res = (int) _saudio.backend.buffer; + return res; + } + else { + return 0; + } +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* setup the WebAudio context and attach a ScriptProcessorNode */ +EM_JS(int, saudio_js_init, (int sample_rate, int num_channels, int buffer_size), { + Module._saudio_context = null; + Module._saudio_node = null; + if (typeof AudioContext !== 'undefined') { + Module._saudio_context = new AudioContext({ + sampleRate: sample_rate, + latencyHint: 'interactive', + }); + } + else { + Module._saudio_context = null; + console.log('sokol_audio.h: no WebAudio support'); + } + if (Module._saudio_context) { + console.log('sokol_audio.h: sample rate ', Module._saudio_context.sampleRate); + Module._saudio_node = Module._saudio_context.createScriptProcessor(buffer_size, 0, num_channels); + Module._saudio_node.onaudioprocess = (event) => { + const num_frames = event.outputBuffer.length; + const ptr = __saudio_emsc_pull(num_frames); + if (ptr) { + const num_channels = event.outputBuffer.numberOfChannels; + for (let chn = 0; chn < num_channels; chn++) { + const chan = event.outputBuffer.getChannelData(chn); + for (let i = 0; i < num_frames; i++) { + chan[i] = HEAPF32[(ptr>>2) + ((num_channels*i)+chn)] + } + } + } + }; + Module._saudio_node.connect(Module._saudio_context.destination); + + // in some browsers, WebAudio needs to be activated on a user action + const resume_webaudio = () => { + if (Module._saudio_context) { + if (Module._saudio_context.state === 'suspended') { + Module._saudio_context.resume(); + } + } + }; + document.addEventListener('click', resume_webaudio, {once:true}); + document.addEventListener('touchend', resume_webaudio, {once:true}); + document.addEventListener('keydown', resume_webaudio, {once:true}); + return 1; + } + else { + return 0; + } +}); + +/* shutdown the WebAudioContext and ScriptProcessorNode */ +EM_JS(void, saudio_js_shutdown, (void), { + \x2F\x2A\x2A @suppress {missingProperties} \x2A\x2F + const ctx = Module._saudio_context; + if (ctx !== null) { + if (Module._saudio_node) { + Module._saudio_node.disconnect(); + } + ctx.close(); + Module._saudio_context = null; + Module._saudio_node = null; + } +}); + +/* get the actual sample rate back from the WebAudio context */ +EM_JS(int, saudio_js_sample_rate, (void), { + if (Module._saudio_context) { + return Module._saudio_context.sampleRate; + } + else { + return 0; + } +}); + +/* get the actual buffer size in number of frames */ +EM_JS(int, saudio_js_buffer_frames, (void), { + if (Module._saudio_node) { + return Module._saudio_node.bufferSize; + } + else { + return 0; + } +}); + +/* return 1 if the WebAudio context is currently suspended, else 0 */ +EM_JS(int, saudio_js_suspended, (void), { + if (Module._saudio_context) { + if (Module._saudio_context.state === 'suspended') { + return 1; + } + else { + return 0; + } + } +}); + +_SOKOL_PRIVATE bool _saudio_webaudio_backend_init(void) { + if (saudio_js_init(_saudio.sample_rate, _saudio.num_channels, _saudio.buffer_frames)) { + _saudio.bytes_per_frame = (int)sizeof(float) * _saudio.num_channels; + _saudio.sample_rate = saudio_js_sample_rate(); + _saudio.buffer_frames = saudio_js_buffer_frames(); + const size_t buf_size = (size_t) (_saudio.buffer_frames * _saudio.bytes_per_frame); + _saudio.backend.buffer = (uint8_t*) _saudio_malloc(buf_size); + return true; + } + else { + return false; + } +} + +_SOKOL_PRIVATE void _saudio_webaudio_backend_shutdown(void) { + saudio_js_shutdown(); + if (_saudio.backend.buffer) { + _saudio_free(_saudio.backend.buffer); + _saudio.backend.buffer = 0; + } +} + +// █████ █████ ██ ██ ██████ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██████ ██████ ██ ██████ +// +// >>aaudio +#elif defined(SAUDIO_ANDROID_AAUDIO) + +_SOKOL_PRIVATE aaudio_data_callback_result_t _saudio_aaudio_data_callback(AAudioStream* stream, void* user_data, void* audio_data, int32_t num_frames) { + _SOKOL_UNUSED(user_data); + _SOKOL_UNUSED(stream); + if (_saudio_has_callback()) { + _saudio_stream_callback((float*)audio_data, (int)num_frames, _saudio.num_channels); + } + else { + uint8_t* ptr = (uint8_t*)audio_data; + int num_bytes = _saudio.bytes_per_frame * num_frames; + if (0 == _saudio_fifo_read(&_saudio.fifo, ptr, num_bytes)) { + // not enough read data available, fill the entire buffer with silence + memset(ptr, 0, (size_t)num_bytes); + } + } + return AAUDIO_CALLBACK_RESULT_CONTINUE; +} + +_SOKOL_PRIVATE bool _saudio_aaudio_start_stream(void) { + if (AAudioStreamBuilder_openStream(_saudio.backend.builder, &_saudio.backend.stream) != AAUDIO_OK) { + _SAUDIO_ERROR(AAUDIO_STREAMBUILDER_OPEN_STREAM_FAILED); + return false; + } + AAudioStream_requestStart(_saudio.backend.stream); + return true; +} + +_SOKOL_PRIVATE void _saudio_aaudio_stop_stream(void) { + if (_saudio.backend.stream) { + AAudioStream_requestStop(_saudio.backend.stream); + AAudioStream_close(_saudio.backend.stream); + _saudio.backend.stream = 0; + } +} + +_SOKOL_PRIVATE void* _saudio_aaudio_restart_stream_thread_fn(void* param) { + _SOKOL_UNUSED(param); + _SAUDIO_WARN(AAUDIO_RESTARTING_STREAM_AFTER_ERROR); + pthread_mutex_lock(&_saudio.backend.mutex); + _saudio_aaudio_stop_stream(); + _saudio_aaudio_start_stream(); + pthread_mutex_unlock(&_saudio.backend.mutex); + return 0; +} + +_SOKOL_PRIVATE void _saudio_aaudio_error_callback(AAudioStream* stream, void* user_data, aaudio_result_t error) { + _SOKOL_UNUSED(stream); + _SOKOL_UNUSED(user_data); + if (error == AAUDIO_ERROR_DISCONNECTED) { + if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_aaudio_restart_stream_thread_fn, 0)) { + _SAUDIO_ERROR(AAUDIO_PTHREAD_CREATE_FAILED); + } + } +} + +_SOKOL_PRIVATE void _saudio_aaudio_backend_shutdown(void) { + pthread_mutex_lock(&_saudio.backend.mutex); + _saudio_aaudio_stop_stream(); + pthread_mutex_unlock(&_saudio.backend.mutex); + if (_saudio.backend.builder) { + AAudioStreamBuilder_delete(_saudio.backend.builder); + _saudio.backend.builder = 0; + } + pthread_mutex_destroy(&_saudio.backend.mutex); +} + +_SOKOL_PRIVATE bool _saudio_aaudio_backend_init(void) { + _SAUDIO_INFO(USING_AAUDIO_BACKEND); + + _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutex_init(&_saudio.backend.mutex, &attr); + + if (AAudio_createStreamBuilder(&_saudio.backend.builder) != AAUDIO_OK) { + _SAUDIO_ERROR(AAUDIO_CREATE_STREAMBUILDER_FAILED); + _saudio_aaudio_backend_shutdown(); + return false; + } + + AAudioStreamBuilder_setFormat(_saudio.backend.builder, AAUDIO_FORMAT_PCM_FLOAT); + AAudioStreamBuilder_setSampleRate(_saudio.backend.builder, _saudio.sample_rate); + AAudioStreamBuilder_setChannelCount(_saudio.backend.builder, _saudio.num_channels); + AAudioStreamBuilder_setBufferCapacityInFrames(_saudio.backend.builder, _saudio.buffer_frames * 2); + AAudioStreamBuilder_setFramesPerDataCallback(_saudio.backend.builder, _saudio.buffer_frames); + AAudioStreamBuilder_setDataCallback(_saudio.backend.builder, _saudio_aaudio_data_callback, 0); + AAudioStreamBuilder_setErrorCallback(_saudio.backend.builder, _saudio_aaudio_error_callback, 0); + + if (!_saudio_aaudio_start_stream()) { + _saudio_aaudio_backend_shutdown(); + return false; + } + + return true; +} + +// ██████ ██████ ███████ ███ ██ ███████ ██ ███████ ███████ +// ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ +// ██ ██ ██████ █████ ██ ██ ██ ███████ ██ █████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ███████ ██ ████ ███████ ███████ ███████ ███████ +// +// >>opensles +// >>sles +#elif defined(SAUDIO_ANDROID_SLES) + +_SOKOL_PRIVATE void _saudio_sles_semaphore_init(_saudio_sles_semaphore_t* sem) { + sem->count = 0; + int r = pthread_mutex_init(&sem->mutex, NULL); + SOKOL_ASSERT(r == 0); + r = pthread_cond_init(&sem->cond, NULL); + SOKOL_ASSERT(r == 0); + (void)(r); +} + +_SOKOL_PRIVATE void _saudio_sles_semaphore_destroy(_saudio_sles_semaphore_t* sem) { + pthread_cond_destroy(&sem->cond); + pthread_mutex_destroy(&sem->mutex); +} + +_SOKOL_PRIVATE void _saudio_sles_semaphore_post(_saudio_sles_semaphore_t* sem, int count) { + int r = pthread_mutex_lock(&sem->mutex); + SOKOL_ASSERT(r == 0); + for (int ii = 0; ii < count; ii++) { + r = pthread_cond_signal(&sem->cond); + SOKOL_ASSERT(r == 0); + } + sem->count += count; + r = pthread_mutex_unlock(&sem->mutex); + SOKOL_ASSERT(r == 0); + (void)(r); +} + +_SOKOL_PRIVATE bool _saudio_sles_semaphore_wait(_saudio_sles_semaphore_t* sem) { + int r = pthread_mutex_lock(&sem->mutex); + SOKOL_ASSERT(r == 0); + while (r == 0 && sem->count <= 0) { + r = pthread_cond_wait(&sem->cond, &sem->mutex); + } + bool ok = (r == 0); + if (ok) { + --sem->count; + } + r = pthread_mutex_unlock(&sem->mutex); + (void)(r); + return ok; +} + +/* fill intermediate buffer with new data and reset buffer_pos */ +_SOKOL_PRIVATE void _saudio_sles_fill_buffer(void) { + int src_buffer_frames = _saudio.buffer_frames; + if (_saudio_has_callback()) { + _saudio_stream_callback(_saudio.backend.src_buffer, src_buffer_frames, _saudio.num_channels); + } + else { + const int src_buffer_byte_size = src_buffer_frames * _saudio.num_channels * (int)sizeof(float); + if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.src_buffer, src_buffer_byte_size)) { + /* not enough read data available, fill the entire buffer with silence */ + _saudio_clear(_saudio.backend.src_buffer, (size_t)src_buffer_byte_size); + } + } +} + +_SOKOL_PRIVATE void SLAPIENTRY _saudio_sles_play_cb(SLPlayItf player, void *context, SLuint32 event) { + _SOKOL_UNUSED(context); + _SOKOL_UNUSED(player); + if (event & SL_PLAYEVENT_HEADATEND) { + _saudio_sles_semaphore_post(&_saudio.backend.buffer_sem, 1); + } +} + +_SOKOL_PRIVATE void* _saudio_sles_thread_fn(void* param) { + _SOKOL_UNUSED(param); + while (!_saudio.backend.thread_stop) { + /* get next output buffer, advance, next buffer. */ + int16_t* out_buffer = _saudio.backend.output_buffers[_saudio.backend.active_buffer]; + _saudio.backend.active_buffer = (_saudio.backend.active_buffer + 1) % SAUDIO_SLES_NUM_BUFFERS; + int16_t* next_buffer = _saudio.backend.output_buffers[_saudio.backend.active_buffer]; + + /* queue this buffer */ + const int buffer_size_bytes = _saudio.buffer_frames * _saudio.num_channels * (int)sizeof(short); + (*_saudio.backend.player_buffer_queue)->Enqueue(_saudio.backend.player_buffer_queue, out_buffer, (SLuint32)buffer_size_bytes); + + /* fill the next buffer */ + _saudio_sles_fill_buffer(); + const int num_samples = _saudio.num_channels * _saudio.buffer_frames; + for (int i = 0; i < num_samples; ++i) { + next_buffer[i] = (int16_t) (_saudio.backend.src_buffer[i] * 0x7FFF); + } + + _saudio_sles_semaphore_wait(&_saudio.backend.buffer_sem); + } + + return 0; +} + +_SOKOL_PRIVATE void _saudio_sles_backend_shutdown(void) { + _saudio.backend.thread_stop = 1; + pthread_join(_saudio.backend.thread, 0); + + if (_saudio.backend.player_obj) { + (*_saudio.backend.player_obj)->Destroy(_saudio.backend.player_obj); + } + + if (_saudio.backend.output_mix_obj) { + (*_saudio.backend.output_mix_obj)->Destroy(_saudio.backend.output_mix_obj); + } + + if (_saudio.backend.engine_obj) { + (*_saudio.backend.engine_obj)->Destroy(_saudio.backend.engine_obj); + } + + for (int i = 0; i < SAUDIO_SLES_NUM_BUFFERS; i++) { + _saudio_free(_saudio.backend.output_buffers[i]); + } + _saudio_free(_saudio.backend.src_buffer); +} + +_SOKOL_PRIVATE bool _saudio_sles_backend_init(void) { + _SAUDIO_INFO(USING_SLES_BACKEND); + + _saudio.bytes_per_frame = (int)sizeof(float) * _saudio.num_channels; + + for (int i = 0; i < SAUDIO_SLES_NUM_BUFFERS; ++i) { + const int buffer_size_bytes = (int)sizeof(int16_t) * _saudio.num_channels * _saudio.buffer_frames; + _saudio.backend.output_buffers[i] = (int16_t*) _saudio_malloc_clear((size_t)buffer_size_bytes); + } + + { + const int buffer_size_bytes = _saudio.bytes_per_frame * _saudio.buffer_frames; + _saudio.backend.src_buffer = (float*) _saudio_malloc_clear((size_t)buffer_size_bytes); + } + + /* Create engine */ + const SLEngineOption opts[] = { { SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE } }; + if (slCreateEngine(&_saudio.backend.engine_obj, 1, opts, 0, NULL, NULL ) != SL_RESULT_SUCCESS) { + _SAUDIO_ERROR(SLES_CREATE_ENGINE_FAILED); + _saudio_sles_backend_shutdown(); + return false; + } + + (*_saudio.backend.engine_obj)->Realize(_saudio.backend.engine_obj, SL_BOOLEAN_FALSE); + if ((*_saudio.backend.engine_obj)->GetInterface(_saudio.backend.engine_obj, SL_IID_ENGINE, &_saudio.backend.engine) != SL_RESULT_SUCCESS) { + _SAUDIO_ERROR(SLES_ENGINE_GET_ENGINE_INTERFACE_FAILED); + _saudio_sles_backend_shutdown(); + return false; + } + + /* Create output mix. */ + { + const SLInterfaceID ids[] = { SL_IID_VOLUME }; + const SLboolean req[] = { SL_BOOLEAN_FALSE }; + + if ((*_saudio.backend.engine)->CreateOutputMix(_saudio.backend.engine, &_saudio.backend.output_mix_obj, 1, ids, req) != SL_RESULT_SUCCESS) { + _SAUDIO_ERROR(SLES_CREATE_OUTPUT_MIX_FAILED); + _saudio_sles_backend_shutdown(); + return false; + } + (*_saudio.backend.output_mix_obj)->Realize(_saudio.backend.output_mix_obj, SL_BOOLEAN_FALSE); + + if ((*_saudio.backend.output_mix_obj)->GetInterface(_saudio.backend.output_mix_obj, SL_IID_VOLUME, &_saudio.backend.output_mix_vol) != SL_RESULT_SUCCESS) { + _SAUDIO_WARN(SLES_MIXER_GET_VOLUME_INTERFACE_FAILED); + } + } + + /* android buffer queue */ + _saudio.backend.in_locator.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; + _saudio.backend.in_locator.numBuffers = SAUDIO_SLES_NUM_BUFFERS; + + /* data format */ + SLDataFormat_PCM format; + format.formatType = SL_DATAFORMAT_PCM; + format.numChannels = (SLuint32)_saudio.num_channels; + format.samplesPerSec = (SLuint32) (_saudio.sample_rate * 1000); + format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; + format.containerSize = 16; + format.endianness = SL_BYTEORDER_LITTLEENDIAN; + + if (_saudio.num_channels == 2) { + format.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + } else { + format.channelMask = SL_SPEAKER_FRONT_CENTER; + } + + SLDataSource src; + src.pLocator = &_saudio.backend.in_locator; + src.pFormat = &format; + + /* Output mix. */ + _saudio.backend.out_locator.locatorType = SL_DATALOCATOR_OUTPUTMIX; + _saudio.backend.out_locator.outputMix = _saudio.backend.output_mix_obj; + + _saudio.backend.dst_data_sink.pLocator = &_saudio.backend.out_locator; + _saudio.backend.dst_data_sink.pFormat = NULL; + + /* setup player */ + { + const SLInterfaceID ids[] = { SL_IID_VOLUME, SL_IID_ANDROIDSIMPLEBUFFERQUEUE }; + const SLboolean req[] = { SL_BOOLEAN_FALSE, SL_BOOLEAN_TRUE }; + + if ((*_saudio.backend.engine)->CreateAudioPlayer(_saudio.backend.engine, &_saudio.backend.player_obj, &src, &_saudio.backend.dst_data_sink, sizeof(ids) / sizeof(ids[0]), ids, req) != SL_RESULT_SUCCESS) + { + _SAUDIO_ERROR(SLES_ENGINE_CREATE_AUDIO_PLAYER_FAILED); + _saudio_sles_backend_shutdown(); + return false; + } + (*_saudio.backend.player_obj)->Realize(_saudio.backend.player_obj, SL_BOOLEAN_FALSE); + + if ((*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_PLAY, &_saudio.backend.player) != SL_RESULT_SUCCESS) { + _SAUDIO_ERROR(SLES_PLAYER_GET_PLAY_INTERFACE_FAILED); + _saudio_sles_backend_shutdown(); + return false; + } + if ((*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_VOLUME, &_saudio.backend.player_vol) != SL_RESULT_SUCCESS) { + _SAUDIO_ERROR(SLES_PLAYER_GET_VOLUME_INTERFACE_FAILED); + } + if ((*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &_saudio.backend.player_buffer_queue) != SL_RESULT_SUCCESS) { + _SAUDIO_ERROR(SLES_PLAYER_GET_BUFFERQUEUE_INTERFACE_FAILED); + _saudio_sles_backend_shutdown(); + return false; + } + } + + /* begin */ + { + const int buffer_size_bytes = (int)sizeof(int16_t) * _saudio.num_channels * _saudio.buffer_frames; + (*_saudio.backend.player_buffer_queue)->Enqueue(_saudio.backend.player_buffer_queue, _saudio.backend.output_buffers[0], (SLuint32)buffer_size_bytes); + _saudio.backend.active_buffer = (_saudio.backend.active_buffer + 1) % SAUDIO_SLES_NUM_BUFFERS; + + (*_saudio.backend.player)->RegisterCallback(_saudio.backend.player, _saudio_sles_play_cb, NULL); + (*_saudio.backend.player)->SetCallbackEventsMask(_saudio.backend.player, SL_PLAYEVENT_HEADATEND); + (*_saudio.backend.player)->SetPlayState(_saudio.backend.player, SL_PLAYSTATE_PLAYING); + } + + /* create the buffer-streaming start thread */ + if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_sles_thread_fn, 0)) { + _saudio_sles_backend_shutdown(); + return false; + } + + return true; +} + +// ██████ ██████ ██████ ███████ █████ ██ ██ ██████ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██████ █████ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ██ ███████ ██ ██ ██████ ██████ ██ ██████ +// +// >>coreaudio +#elif defined(_SAUDIO_APPLE) + +#if defined(_SAUDIO_IOS) +#if __has_feature(objc_arc) +#define _SAUDIO_OBJC_RELEASE(obj) { obj = nil; } +#else +#define _SAUDIO_OBJC_RELEASE(obj) { [obj release]; obj = nil; } +#endif + +@interface _saudio_interruption_handler : NSObject { } +@end + +@implementation _saudio_interruption_handler +-(id)init { + self = [super init]; + AVAudioSession* session = [AVAudioSession sharedInstance]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:session]; + return self; +} + +-(void)dealloc { + [self remove_handler]; + #if !__has_feature(objc_arc) + [super dealloc]; + #endif +} + +-(void)remove_handler { + [[NSNotificationCenter defaultCenter] removeObserver:self name:@"AVAudioSessionInterruptionNotification" object:nil]; +} + +-(void)handle_interruption:(NSNotification*)notification { + AVAudioSession* session = [AVAudioSession sharedInstance]; + SOKOL_ASSERT(session); + NSDictionary* dict = notification.userInfo; + SOKOL_ASSERT(dict); + NSInteger type = [[dict valueForKey:AVAudioSessionInterruptionTypeKey] integerValue]; + switch (type) { + case AVAudioSessionInterruptionTypeBegan: + if (_saudio.backend.ca_audio_queue) { + AudioQueuePause(_saudio.backend.ca_audio_queue); + } + [session setActive:false error:nil]; + break; + case AVAudioSessionInterruptionTypeEnded: + [session setActive:true error:nil]; + if (_saudio.backend.ca_audio_queue) { + AudioQueueStart(_saudio.backend.ca_audio_queue, NULL); + } + break; + default: + break; + } +} +@end +#endif // _SAUDIO_IOS + +/* NOTE: the buffer data callback is called on a separate thread! */ +_SOKOL_PRIVATE void _saudio_coreaudio_callback(void* user_data, _saudio_AudioQueueRef queue, _saudio_AudioQueueBufferRef buffer) { + _SOKOL_UNUSED(user_data); + if (_saudio_has_callback()) { + const int num_frames = (int)buffer->mAudioDataByteSize / _saudio.bytes_per_frame; + const int num_channels = _saudio.num_channels; + _saudio_stream_callback((float*)buffer->mAudioData, num_frames, num_channels); + } + else { + uint8_t* ptr = (uint8_t*)buffer->mAudioData; + int num_bytes = (int) buffer->mAudioDataByteSize; + if (0 == _saudio_fifo_read(&_saudio.fifo, ptr, num_bytes)) { + /* not enough read data available, fill the entire buffer with silence */ + _saudio_clear(ptr, (size_t)num_bytes); + } + } + AudioQueueEnqueueBuffer(queue, buffer, 0, NULL); +} + +_SOKOL_PRIVATE void _saudio_coreaudio_backend_shutdown(void) { + if (_saudio.backend.ca_audio_queue) { + AudioQueueStop(_saudio.backend.ca_audio_queue, true); + AudioQueueDispose(_saudio.backend.ca_audio_queue, false); + _saudio.backend.ca_audio_queue = 0; + } + #if defined(_SAUDIO_IOS) + /* remove interruption handler */ + if (_saudio.backend.ca_interruption_handler != nil) { + [_saudio.backend.ca_interruption_handler remove_handler]; + _SAUDIO_OBJC_RELEASE(_saudio.backend.ca_interruption_handler); + } + /* deactivate audio session */ + AVAudioSession* session = [AVAudioSession sharedInstance]; + SOKOL_ASSERT(session); + [session setActive:false error:nil];; + #endif // _SAUDIO_IOS +} + +_SOKOL_PRIVATE bool _saudio_coreaudio_backend_init(void) { + SOKOL_ASSERT(0 == _saudio.backend.ca_audio_queue); + + #if defined(_SAUDIO_IOS) + /* activate audio session */ + AVAudioSession* session = [AVAudioSession sharedInstance]; + SOKOL_ASSERT(session != nil); + [session setCategory: AVAudioSessionCategoryPlayback error:nil]; + [session setActive:true error:nil]; + + /* create interruption handler */ + _saudio.backend.ca_interruption_handler = [[_saudio_interruption_handler alloc] init]; + #endif + + /* create an audio queue with fp32 samples */ + _saudio_AudioStreamBasicDescription fmt; + _saudio_clear(&fmt, sizeof(fmt)); + fmt.mSampleRate = (double) _saudio.sample_rate; + fmt.mFormatID = _saudio_kAudioFormatLinearPCM; + fmt.mFormatFlags = _saudio_kLinearPCMFormatFlagIsFloat | _saudio_kAudioFormatFlagIsPacked; + fmt.mFramesPerPacket = 1; + fmt.mChannelsPerFrame = (uint32_t) _saudio.num_channels; + fmt.mBytesPerFrame = (uint32_t)sizeof(float) * (uint32_t)_saudio.num_channels; + fmt.mBytesPerPacket = fmt.mBytesPerFrame; + fmt.mBitsPerChannel = 32; + _saudio_OSStatus res = AudioQueueNewOutput(&fmt, _saudio_coreaudio_callback, 0, NULL, NULL, 0, &_saudio.backend.ca_audio_queue); + if (0 != res) { + _SAUDIO_ERROR(COREAUDIO_NEW_OUTPUT_FAILED); + return false; + } + SOKOL_ASSERT(_saudio.backend.ca_audio_queue); + + /* create 2 audio buffers */ + for (int i = 0; i < 2; i++) { + _saudio_AudioQueueBufferRef buf = NULL; + const uint32_t buf_byte_size = (uint32_t)_saudio.buffer_frames * fmt.mBytesPerFrame; + res = AudioQueueAllocateBuffer(_saudio.backend.ca_audio_queue, buf_byte_size, &buf); + if (0 != res) { + _SAUDIO_ERROR(COREAUDIO_ALLOCATE_BUFFER_FAILED); + _saudio_coreaudio_backend_shutdown(); + return false; + } + buf->mAudioDataByteSize = buf_byte_size; + _saudio_clear(buf->mAudioData, buf->mAudioDataByteSize); + AudioQueueEnqueueBuffer(_saudio.backend.ca_audio_queue, buf, 0, NULL); + } + + /* init or modify actual playback parameters */ + _saudio.bytes_per_frame = (int)fmt.mBytesPerFrame; + + /* ...and start playback */ + res = AudioQueueStart(_saudio.backend.ca_audio_queue, NULL); + if (0 != res) { + _SAUDIO_ERROR(COREAUDIO_START_FAILED); + _saudio_coreaudio_backend_shutdown(); + return false; + } + return true; +} + +#else +#error "unsupported platform" +#endif + +bool _saudio_backend_init(void) { + #if defined(SOKOL_DUMMY_BACKEND) + return _saudio_dummy_backend_init(); + #elif defined(_SAUDIO_LINUX) + return _saudio_alsa_backend_init(); + #elif defined(_SAUDIO_WINDOWS) + return _saudio_wasapi_backend_init(); + #elif defined(_SAUDIO_EMSCRIPTEN) + return _saudio_webaudio_backend_init(); + #elif defined(SAUDIO_ANDROID_AAUDIO) + return _saudio_aaudio_backend_init(); + #elif defined(SAUDIO_ANDROID_SLES) + return _saudio_sles_backend_init(); + #elif defined(_SAUDIO_APPLE) + return _saudio_coreaudio_backend_init(); + #else + #error "unknown platform" + #endif +} + +void _saudio_backend_shutdown(void) { + #if defined(SOKOL_DUMMY_BACKEND) + _saudio_dummy_backend_shutdown(); + #elif defined(_SAUDIO_LINUX) + _saudio_alsa_backend_shutdown(); + #elif defined(_SAUDIO_WINDOWS) + _saudio_wasapi_backend_shutdown(); + #elif defined(_SAUDIO_EMSCRIPTEN) + _saudio_webaudio_backend_shutdown(); + #elif defined(SAUDIO_ANDROID_AAUDIO) + _saudio_aaudio_backend_shutdown(); + #elif defined(SAUDIO_ANDROID_SLES) + _saudio_sles_backend_shutdown(); + #elif defined(_SAUDIO_APPLE) + return _saudio_coreaudio_backend_shutdown(); + #else + #error "unknown platform" + #endif +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) { + SOKOL_ASSERT(!_saudio.valid); + SOKOL_ASSERT(desc); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + _saudio_clear(&_saudio, sizeof(_saudio)); + _saudio.desc = *desc; + _saudio.stream_cb = desc->stream_cb; + _saudio.stream_userdata_cb = desc->stream_userdata_cb; + _saudio.user_data = desc->user_data; + _saudio.sample_rate = _saudio_def(_saudio.desc.sample_rate, _SAUDIO_DEFAULT_SAMPLE_RATE); + _saudio.buffer_frames = _saudio_def(_saudio.desc.buffer_frames, _SAUDIO_DEFAULT_BUFFER_FRAMES); + _saudio.packet_frames = _saudio_def(_saudio.desc.packet_frames, _SAUDIO_DEFAULT_PACKET_FRAMES); + _saudio.num_packets = _saudio_def(_saudio.desc.num_packets, _SAUDIO_DEFAULT_NUM_PACKETS); + _saudio.num_channels = _saudio_def(_saudio.desc.num_channels, 1); + _saudio_fifo_init_mutex(&_saudio.fifo); + if (_saudio_backend_init()) { + /* the backend might not support the requested exact buffer size, + make sure the actual buffer size is still a multiple of + the requested packet size + */ + if (0 != (_saudio.buffer_frames % _saudio.packet_frames)) { + _SAUDIO_ERROR(BACKEND_BUFFER_SIZE_ISNT_MULTIPLE_OF_PACKET_SIZE); + _saudio_backend_shutdown(); + return; + } + SOKOL_ASSERT(_saudio.bytes_per_frame > 0); + _saudio_fifo_init(&_saudio.fifo, _saudio.packet_frames * _saudio.bytes_per_frame, _saudio.num_packets); + _saudio.valid = true; + } + else { + _saudio_fifo_destroy_mutex(&_saudio.fifo); + } +} + +SOKOL_API_IMPL void saudio_shutdown(void) { + if (_saudio.valid) { + _saudio_backend_shutdown(); + _saudio_fifo_shutdown(&_saudio.fifo); + _saudio_fifo_destroy_mutex(&_saudio.fifo); + _saudio.valid = false; + } +} + +SOKOL_API_IMPL bool saudio_isvalid(void) { + return _saudio.valid; +} + +SOKOL_API_IMPL void* saudio_userdata(void) { + return _saudio.desc.user_data; +} + +SOKOL_API_IMPL saudio_desc saudio_query_desc(void) { + return _saudio.desc; +} + +SOKOL_API_IMPL int saudio_sample_rate(void) { + return _saudio.sample_rate; +} + +SOKOL_API_IMPL int saudio_buffer_frames(void) { + return _saudio.buffer_frames; +} + +SOKOL_API_IMPL int saudio_channels(void) { + return _saudio.num_channels; +} + +SOKOL_API_IMPL bool saudio_suspended(void) { + #if defined(_SAUDIO_EMSCRIPTEN) + if (_saudio.valid) { + return 1 == saudio_js_suspended(); + } + else { + return false; + } + #else + return false; + #endif +} + +SOKOL_API_IMPL int saudio_expect(void) { + if (_saudio.valid) { + const int num_frames = _saudio_fifo_writable_bytes(&_saudio.fifo) / _saudio.bytes_per_frame; + return num_frames; + } + else { + return 0; + } +} + +SOKOL_API_IMPL int saudio_push(const float* frames, int num_frames) { + SOKOL_ASSERT(frames && (num_frames > 0)); + if (_saudio.valid) { + const int num_bytes = num_frames * _saudio.bytes_per_frame; + const int num_written = _saudio_fifo_write(&_saudio.fifo, (const uint8_t*)frames, num_bytes); + return num_written / _saudio.bytes_per_frame; + } + else { + return 0; + } +} + +#undef _saudio_def +#undef _saudio_def_flt + +#if defined(_SAUDIO_WINDOWS) +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#endif + +#endif /* SOKOL_AUDIO_IMPL */ diff --git a/thirdparty/sokol/sokol_fetch.h b/thirdparty/sokol/sokol_fetch.h new file mode 100644 index 0000000..2cc4931 --- /dev/null +++ b/thirdparty/sokol/sokol_fetch.h @@ -0,0 +1,2819 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_FETCH_IMPL) +#define SOKOL_FETCH_IMPL +#endif +#ifndef SOKOL_FETCH_INCLUDED +/* + sokol_fetch.h -- asynchronous data loading/streaming + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_FETCH_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + Optionally provide the following defines with your own implementations: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + SOKOL_FETCH_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_FETCH_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + SFETCH_MAX_PATH - max length of UTF-8 filesystem path / URL (default: 1024 bytes) + SFETCH_MAX_USERDATA_UINT64 - max size of embedded userdata in number of uint64_t, userdata + will be copied into an 8-byte aligned memory region associated + with each in-flight request, default value is 16 (== 128 bytes) + SFETCH_MAX_CHANNELS - max number of IO channels (default is 16, also see sfetch_desc_t.num_channels) + + If sokol_fetch.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_FETCH_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + NOTE: The following documentation talks a lot about "IO threads". Actual + threads are only used on platforms where threads are available. The web + version (emscripten/wasm) doesn't use POSIX-style threads, but instead + asynchronous Javascript calls chained together by callbacks. The actual + source code differences between the two approaches have been kept to + a minimum though. + + FEATURE OVERVIEW + ================ + + - Asynchronously load complete files, or stream files incrementally via + HTTP (on web platform), or the local file system (on native platforms) + + - Request / response-callback model, user code sends a request + to initiate a file-load, sokol_fetch.h calls the response callback + on the same thread when data is ready or user-code needs + to respond otherwise + + - Not limited to the main-thread or a single thread: A sokol-fetch + "context" can live on any thread, and multiple contexts + can operate side-by-side on different threads. + + - Memory management for data buffers is under full control of user code. + sokol_fetch.h won't allocate memory after it has been setup. + + - Automatic rate-limiting guarantees that only a maximum number of + requests is processed at any one time, allowing a zero-allocation + model, where all data is streamed into fixed-size, pre-allocated + buffers. + + - Active Requests can be paused, continued and cancelled from anywhere + in the user-thread which sent this request. + + + TL;DR EXAMPLE CODE + ================== + This is the most-simple example code to load a single data file with a + known maximum size: + + (1) initialize sokol-fetch with default parameters (but NOTE that the + default setup parameters provide a safe-but-slow "serialized" + operation). In order to see any logging output in case or errors + you should always provide a logging function + (such as 'slog_func' from sokol_log.h): + + sfetch_setup(&(sfetch_desc_t){ .logger.func = slog_func }); + + (2) send a fetch-request to load a file from the current directory + into a buffer big enough to hold the entire file content: + + static uint8_t buf[MAX_FILE_SIZE]; + + sfetch_send(&(sfetch_request_t){ + .path = "my_file.txt", + .callback = response_callback, + .buffer = { + .ptr = buf, + .size = sizeof(buf) + } + }); + + If 'buf' is a value (e.g. an array or struct item), the .buffer item can + be initialized with the SFETCH_RANGE() helper macro: + + sfetch_send(&(sfetch_request_t){ + .path = "my_file.txt", + .callback = response_callback, + .buffer = SFETCH_RANGE(buf) + }); + + (3) write a 'response-callback' function, this will be called whenever + the user-code must respond to state changes of the request + (most importantly when data has been loaded): + + void response_callback(const sfetch_response_t* response) { + if (response->fetched) { + // data has been loaded, and is available via the + // sfetch_range_t struct item 'data': + const void* ptr = response->data.ptr; + size_t num_bytes = response->data.size; + } + if (response->finished) { + // the 'finished'-flag is the catch-all flag for when the request + // is finished, no matter if loading was successful or failed, + // so any cleanup-work should happen here... + ... + if (response->failed) { + // 'failed' is true in (addition to 'finished') if something + // went wrong (file doesn't exist, or less bytes could be + // read from the file than expected) + } + } + } + + (4) pump the sokol-fetch message queues, and invoke response callbacks + by calling: + + sfetch_dowork(); + + In an event-driven app this should be called in the event loop. If you + use sokol-app this would be in your frame_cb function. + + (5) finally, call sfetch_shutdown() at the end of the application: + + There's many other loading-scenarios, for instance one doesn't have to + provide a buffer upfront, this can also happen in the response callback. + + Or it's possible to stream huge files into small fixed-size buffer, + complete with pausing and continuing the download. + + It's also possible to improve the 'pipeline throughput' by fetching + multiple files in parallel, but at the same time limit the maximum + number of requests that can be 'in-flight'. + + For how this all works, please read the following documentation sections :) + + + API DOCUMENTATION + ================= + + void sfetch_setup(const sfetch_desc_t* desc) + -------------------------------------------- + First call sfetch_setup(const sfetch_desc_t*) on any thread before calling + any other sokol-fetch functions on the same thread. + + sfetch_setup() takes a pointer to an sfetch_desc_t struct with setup + parameters. Parameters which should use their default values must + be zero-initialized: + + - max_requests (uint32_t): + The maximum number of requests that can be alive at any time, the + default is 128. + + - num_channels (uint32_t): + The number of "IO channels" used to parallelize and prioritize + requests, the default is 1. + + - num_lanes (uint32_t): + The number of "lanes" on a single channel. Each request which is + currently 'inflight' on a channel occupies one lane until the + request is finished. This is used for automatic rate-limiting + (search below for CHANNELS AND LANES for more details). The + default number of lanes is 1. + + For example, to setup sokol-fetch for max 1024 active requests, 4 channels, + and 8 lanes per channel in C99: + + sfetch_setup(&(sfetch_desc_t){ + .max_requests = 1024, + .num_channels = 4, + .num_lanes = 8 + }); + + sfetch_setup() is the only place where sokol-fetch will allocate memory. + + NOTE that the default setup parameters of 1 channel and 1 lane per channel + has a very poor 'pipeline throughput' since this essentially serializes + IO requests (a new request will only be processed when the last one has + finished), and since each request needs at least one roundtrip between + the user- and IO-thread the throughput will be at most one request per + frame. Search for LATENCY AND THROUGHPUT below for more information on + how to increase throughput. + + NOTE that you can call sfetch_setup() on multiple threads, each thread + will get its own thread-local sokol-fetch instance, which will work + independently from sokol-fetch instances on other threads. + + void sfetch_shutdown(void) + -------------------------- + Call sfetch_shutdown() at the end of the application to stop any + IO threads and free all memory that was allocated in sfetch_setup(). + + sfetch_handle_t sfetch_send(const sfetch_request_t* request) + ------------------------------------------------------------ + Call sfetch_send() to start loading data, the function takes a pointer to an + sfetch_request_t struct with request parameters and returns a + sfetch_handle_t identifying the request for later calls. At least + a path/URL and callback must be provided: + + sfetch_handle_t h = sfetch_send(&(sfetch_request_t){ + .path = "my_file.txt", + .callback = my_response_callback + }); + + sfetch_send() will return an invalid handle if no request can be allocated + from the internal pool because all available request items are 'in-flight'. + + The sfetch_request_t struct contains the following parameters (optional + parameters that are not provided must be zero-initialized): + + - path (const char*, required) + Pointer to an UTF-8 encoded C string describing the filesystem + path or HTTP URL. The string will be copied into an internal data + structure, and passed "as is" (apart from any required + encoding-conversions) to fopen(), CreateFileW() or + XMLHttpRequest. The maximum length of the string is defined by + the SFETCH_MAX_PATH configuration define, the default is 1024 bytes + including the 0-terminator byte. + + - callback (sfetch_callback_t, required) + Pointer to a response-callback function which is called when the + request needs "user code attention". Search below for REQUEST + STATES AND THE RESPONSE CALLBACK for detailed information about + handling responses in the response callback. + + - channel (uint32_t, optional) + Index of the IO channel where the request should be processed. + Channels are used to parallelize and prioritize requests relative + to each other. Search below for CHANNELS AND LANES for more + information. The default channel is 0. + + - chunk_size (uint32_t, optional) + The chunk_size member is used for streaming data incrementally + in small chunks. After 'chunk_size' bytes have been loaded into + to the streaming buffer, the response callback will be called + with the buffer containing the fetched data for the current chunk. + If chunk_size is 0 (the default), than the whole file will be loaded. + Please search below for CHUNK SIZE AND HTTP COMPRESSION for + important information how streaming works if the web server + is serving compressed data. + + - buffer (sfetch_range_t) + This is a optional pointer/size pair describing a chunk of memory where + data will be loaded into (if no buffer is provided upfront, this + must happen in the response callback). If a buffer is provided, + it must be big enough to either hold the entire file (if chunk_size + is zero), or the *uncompressed* data for one downloaded chunk + (if chunk_size is > 0). + + - user_data (sfetch_range_t) + The user_data ptr/size range struct describe an optional POD blob + (plain-old-data) associated with the request which will be copied(!) + into an internal memory block. The maximum default size of this + memory block is 128 bytes (but can be overridden by defining + SFETCH_MAX_USERDATA_UINT64 before including the notification, note + that this define is in "number of uint64_t", not number of bytes). + The user-data block is 8-byte aligned, and will be copied via + memcpy() (so don't put any C++ "smart members" in there). + + NOTE that request handles are strictly thread-local and only unique + within the thread the handle was created on, and all function calls + involving a request handle must happen on that same thread. + + bool sfetch_handle_valid(sfetch_handle_t request) + ------------------------------------------------- + This checks if the provided request handle is valid, and is associated with + a currently active request. It will return false if: + + - sfetch_send() returned an invalid handle because it couldn't allocate + a new request from the internal request pool (because they're all + in flight) + - the request associated with the handle is no longer alive (because + it either finished successfully, or the request failed for some + reason) + + void sfetch_dowork(void) + ------------------------ + Call sfetch_dowork(void) in regular intervals (for instance once per frame) + on the same thread as sfetch_setup() to "turn the gears". If you are sending + requests but never hear back from them in the response callback function, then + the most likely reason is that you forgot to add the call to sfetch_dowork() + in the per-frame function. + + sfetch_dowork() roughly performs the following work: + + - any new requests that have been sent with sfetch_send() since the + last call to sfetch_dowork() will be dispatched to their IO channels + and assigned a free lane. If all lanes on that channel are occupied + by requests 'in flight', incoming requests must wait until + a lane becomes available + + - for all new requests which have been enqueued on a channel which + don't already have a buffer assigned the response callback will be + called with (response->dispatched == true) so that the response + callback can inspect the dynamically assigned lane and bind a buffer + to the request (search below for CHANNELS AND LANE for more info) + + - a state transition from "user side" to "IO thread side" happens for + each new request that has been dispatched to a channel. + + - requests dispatched to a channel are either forwarded into that + channel's worker thread (on native platforms), or cause an HTTP + request to be sent via an asynchronous XMLHttpRequest (on the web + platform) + + - for all requests which have finished their current IO operation a + state transition from "IO thread side" to "user side" happens, + and the response callback is called so that the fetched data + can be processed. + + - requests which are completely finished (either because the entire + file content has been loaded, or they are in the FAILED state) are + freed (this just changes their state in the 'request pool', no actual + memory is freed) + + - requests which are not yet finished are fed back into the + 'incoming' queue of their channel, and the cycle starts again, this + only happens for requests which perform data streaming (not load + the entire file at once). + + void sfetch_cancel(sfetch_handle_t request) + ------------------------------------------- + This cancels a request in the next sfetch_dowork() call and invokes the + response callback with (response.failed == true) and (response.finished + == true) to give user-code a chance to do any cleanup work for the + request. If sfetch_cancel() is called for a request that is no longer + alive, nothing bad will happen (the call will simply do nothing). + + void sfetch_pause(sfetch_handle_t request) + ------------------------------------------ + This pauses an active request in the next sfetch_dowork() call and puts + it into the PAUSED state. For all requests in PAUSED state, the response + callback will be called in each call to sfetch_dowork() to give user-code + a chance to CONTINUE the request (by calling sfetch_continue()). Pausing + a request makes sense for dynamic rate-limiting in streaming scenarios + (like video/audio streaming with a fixed number of streaming buffers. As + soon as all available buffers are filled with download data, downloading + more data must be prevented to allow video/audio playback to catch up and + free up empty buffers for new download data. + + void sfetch_continue(sfetch_handle_t request) + --------------------------------------------- + Continues a paused request, counterpart to the sfetch_pause() function. + + void sfetch_bind_buffer(sfetch_handle_t request, sfetch_range_t buffer) + ---------------------------------------------------------------------------------------- + This "binds" a new buffer (as pointer/size pair) to an active request. The + function *must* be called from inside the response-callback, and there + must not already be another buffer bound. + + void* sfetch_unbind_buffer(sfetch_handle_t request) + --------------------------------------------------- + This removes the current buffer binding from the request and returns + a pointer to the previous buffer (useful if the buffer was dynamically + allocated and it must be freed). + + sfetch_unbind_buffer() *must* be called from inside the response callback. + + The usual code sequence to bind a different buffer in the response + callback might look like this: + + void response_callback(const sfetch_response_t* response) { + if (response.fetched) { + ... + // switch to a different buffer (in the FETCHED state it is + // guaranteed that the request has a buffer, otherwise it + // would have gone into the FAILED state + void* old_buf_ptr = sfetch_unbind_buffer(response.handle); + free(old_buf_ptr); + void* new_buf_ptr = malloc(new_buf_size); + sfetch_bind_buffer(response.handle, new_buf_ptr, new_buf_size); + } + if (response.finished) { + // unbind and free the currently associated buffer, + // the buffer pointer could be null if the request has failed + // NOTE that it is legal to call free() with a nullptr, + // this happens if the request failed to open its file + // and never goes into the OPENED state + void* buf_ptr = sfetch_unbind_buffer(response.handle); + free(buf_ptr); + } + } + + sfetch_desc_t sfetch_desc(void) + ------------------------------- + sfetch_desc() returns a copy of the sfetch_desc_t struct passed to + sfetch_setup(), with zero-initialized values replaced with + their default values. + + int sfetch_max_userdata_bytes(void) + ----------------------------------- + This returns the value of the SFETCH_MAX_USERDATA_UINT64 config + define, but in number of bytes (so SFETCH_MAX_USERDATA_UINT64*8). + + int sfetch_max_path(void) + ------------------------- + Returns the value of the SFETCH_MAX_PATH config define. + + + REQUEST STATES AND THE RESPONSE CALLBACK + ======================================== + A request goes through a number of states during its lifetime. Depending + on the current state of a request, it will be 'owned' either by the + "user-thread" (where the request was sent) or an IO thread. + + You can think of a request as "ping-ponging" between the IO thread and + user thread, any actual IO work is done on the IO thread, while + invocations of the response-callback happen on the user-thread. + + All state transitions and callback invocations happen inside the + sfetch_dowork() function. + + An active request goes through the following states: + + ALLOCATED (user-thread) + + The request has been allocated in sfetch_send() and is + waiting to be dispatched into its IO channel. When this + happens, the request will transition into the DISPATCHED state. + + DISPATCHED (IO thread) + + The request has been dispatched into its IO channel, and a + lane has been assigned to the request. + + If a buffer was provided in sfetch_send() the request will + immediately transition into the FETCHING state and start loading + data into the buffer. + + If no buffer was provided in sfetch_send(), the response + callback will be called with (response->dispatched == true), + so that the response callback can bind a buffer to the + request. Binding the buffer in the response callback makes + sense if the buffer isn't dynamically allocated, but instead + a pre-allocated buffer must be selected from the request's + channel and lane. + + Note that it isn't possible to get a file size in the response callback + which would help with allocating a buffer of the right size, this is + because it isn't possible in HTTP to query the file size before the + entire file is downloaded (...when the web server serves files compressed). + + If opening the file failed, the request will transition into + the FAILED state with the error code SFETCH_ERROR_FILE_NOT_FOUND. + + FETCHING (IO thread) + + While a request is in the FETCHING state, data will be loaded into + the user-provided buffer. + + If no buffer was provided, the request will go into the FAILED + state with the error code SFETCH_ERROR_NO_BUFFER. + + If a buffer was provided, but it is too small to contain the + fetched data, the request will go into the FAILED state with + error code SFETCH_ERROR_BUFFER_TOO_SMALL. + + If less data can be read from the file than expected, the request + will go into the FAILED state with error code SFETCH_ERROR_UNEXPECTED_EOF. + + If loading data into the provided buffer works as expected, the + request will go into the FETCHED state. + + FETCHED (user thread) + + The request goes into the FETCHED state either when the entire file + has been loaded into the provided buffer (when request.chunk_size == 0), + or a chunk has been loaded (and optionally decompressed) into the + buffer (when request.chunk_size > 0). + + The response callback will be called so that the user-code can + process the loaded data using the following sfetch_response_t struct members: + + - data.ptr: pointer to the start of fetched data + - data.size: the number of bytes in the provided buffer + - data_offset: the byte offset of the loaded data chunk in the + overall file (this is only set to a non-zero value in a streaming + scenario) + + Once all file data has been loaded, the 'finished' flag will be set + in the response callback's sfetch_response_t argument. + + After the user callback returns, and all file data has been loaded + (response.finished flag is set) the request has reached its end-of-life + and will recycled. + + Otherwise, if there's still data to load (because streaming was + requested by providing a non-zero request.chunk_size), the request + will switch back to the FETCHING state to load the next chunk of data. + + Note that it is ok to associate a different buffer or buffer-size + with the request by calling sfetch_bind_buffer() in the response-callback. + + To check in the response callback for the FETCHED state, and + independently whether the request is finished: + + void response_callback(const sfetch_response_t* response) { + if (response->fetched) { + // request is in FETCHED state, the loaded data is available + // in .data.ptr, and the number of bytes that have been + // loaded in .data.size: + const void* data = response->data.ptr; + size_t num_bytes = response->data.size; + } + if (response->finished) { + // the finished flag is set either when all data + // has been loaded, the request has been cancelled, + // or the file operation has failed, this is where + // any required per-request cleanup work should happen + } + } + + + FAILED (user thread) + + A request will transition into the FAILED state in the following situations: + + - if the file doesn't exist or couldn't be opened for other + reasons (SFETCH_ERROR_FILE_NOT_FOUND) + - if no buffer is associated with the request in the FETCHING state + (SFETCH_ERROR_NO_BUFFER) + - if the provided buffer is too small to hold the entire file + (if request.chunk_size == 0), or the (potentially decompressed) + partial data chunk (SFETCH_ERROR_BUFFER_TOO_SMALL) + - if less bytes could be read from the file then expected + (SFETCH_ERROR_UNEXPECTED_EOF) + - if a request has been cancelled via sfetch_cancel() + (SFETCH_ERROR_CANCELLED) + + The response callback will be called once after a request goes into + the FAILED state, with the 'response->finished' and + 'response->failed' flags set to true. + + This gives the user-code a chance to cleanup any resources associated + with the request. + + To check for the failed state in the response callback: + + void response_callback(const sfetch_response_t* response) { + if (response->failed) { + // specifically check for the failed state... + } + // or you can do a catch-all check via the finished-flag: + if (response->finished) { + if (response->failed) { + // if more detailed error handling is needed: + switch (response->error_code) { + ... + } + } + } + } + + PAUSED (user thread) + + A request will transition into the PAUSED state after user-code + calls the function sfetch_pause() on the request's handle. Usually + this happens from within the response-callback in streaming scenarios + when the data streaming needs to wait for a data decoder (like + a video/audio player) to catch up. + + While a request is in PAUSED state, the response-callback will be + called in each sfetch_dowork(), so that the user-code can either + continue the request by calling sfetch_continue(), or cancel + the request by calling sfetch_cancel(). + + When calling sfetch_continue() on a paused request, the request will + transition into the FETCHING state. Otherwise if sfetch_cancel() is + called, the request will switch into the FAILED state. + + To check for the PAUSED state in the response callback: + + void response_callback(const sfetch_response_t* response) { + if (response->paused) { + // we can check here whether the request should + // continue to load data: + if (should_continue(response->handle)) { + sfetch_continue(response->handle); + } + } + } + + + CHUNK SIZE AND HTTP COMPRESSION + =============================== + TL;DR: for streaming scenarios, the provided chunk-size must be smaller + than the provided buffer-size because the web server may decide to + serve the data compressed and the chunk-size must be given in 'compressed + bytes' while the buffer receives 'uncompressed bytes'. It's not possible + in HTTP to query the uncompressed size for a compressed download until + that download has finished. + + With vanilla HTTP, it is not possible to query the actual size of a file + without downloading the entire file first (the Content-Length response + header only provides the compressed size). Furthermore, for HTTP + range-requests, the range is given on the compressed data, not the + uncompressed data. So if the web server decides to server the data + compressed, the content-length and range-request parameters don't + correspond to the uncompressed data that's arriving in the sokol-fetch + buffers, and there's no way from JS or WASM to either force uncompressed + downloads (e.g. by setting the Accept-Encoding field), or access the + compressed data. + + This has some implications for sokol_fetch.h, most notably that buffers + can't be provided in the exactly right size, because that size can't + be queried from HTTP before the data is actually downloaded. + + When downloading whole files at once, it is basically expected that you + know the maximum files size upfront through other means (for instance + through a separate meta-data-file which contains the file sizes and + other meta-data for each file that needs to be loaded). + + For streaming downloads the situation is a bit more complicated. These + use HTTP range-requests, and those ranges are defined on the (potentially) + compressed data which the JS/WASM side doesn't have access to. However, + the JS/WASM side only ever sees the uncompressed data, and it's not possible + to query the uncompressed size of a range request before that range request + has finished. + + If the provided buffer is too small to contain the uncompressed data, + the request will fail with error code SFETCH_ERROR_BUFFER_TOO_SMALL. + + + CHANNELS AND LANES + ================== + Channels and lanes are (somewhat artificial) concepts to manage + parallelization, prioritization and rate-limiting. + + Channels can be used to parallelize message processing for better 'pipeline + throughput', and to prioritize requests: user-code could reserve one + channel for streaming downloads which need to run in parallel to other + requests, another channel for "regular" downloads and yet another + high-priority channel which would only be used for small files which need + to start loading immediately. + + Each channel comes with its own IO thread and message queues for pumping + messages in and out of the thread. The channel where a request is + processed is selected manually when sending a message: + + sfetch_send(&(sfetch_request_t){ + .path = "my_file.txt", + .callback = my_response_callback, + .channel = 2 + }); + + The number of channels is configured at startup in sfetch_setup() and + cannot be changed afterwards. + + Channels are completely separate from each other, and a request will + never "hop" from one channel to another. + + Each channel consists of a fixed number of "lanes" for automatic rate + limiting: + + When a request is sent to a channel via sfetch_send(), a "free lane" will + be picked and assigned to the request. The request will occupy this lane + for its entire life time (also while it is paused). If all lanes of a + channel are currently occupied, new requests will need to wait until a + lane becomes unoccupied. + + Since the number of channels and lanes is known upfront, it is guaranteed + that there will never be more than "num_channels * num_lanes" requests + in flight at any one time. + + This guarantee eliminates unexpected load- and memory-spikes when + many requests are sent in very short time, and it allows to pre-allocate + a fixed number of memory buffers which can be reused for the entire + "lifetime" of a sokol-fetch context. + + In the most simple scenario - when a maximum file size is known - buffers + can be statically allocated like this: + + uint8_t buffer[NUM_CHANNELS][NUM_LANES][MAX_FILE_SIZE]; + + Then in the user callback pick a buffer by channel and lane, + and associate it with the request like this: + + void response_callback(const sfetch_response_t* response) { + if (response->dispatched) { + void* ptr = buffer[response->channel][response->lane]; + sfetch_bind_buffer(response->handle, ptr, MAX_FILE_SIZE); + } + ... + } + + + NOTES ON OPTIMIZING PIPELINE LATENCY AND THROUGHPUT + =================================================== + With the default configuration of 1 channel and 1 lane per channel, + sokol_fetch.h will appear to have a shockingly bad loading performance + if several files are loaded. + + This has two reasons: + + (1) all parallelization when loading data has been disabled. A new + request will only be processed, when the last request has finished. + + (2) every invocation of the response-callback adds one frame of latency + to the request, because callbacks will only be called from within + sfetch_dowork() + + sokol-fetch takes a few shortcuts to improve step (2) and reduce + the 'inherent latency' of a request: + + - if a buffer is provided upfront, the response-callback won't be + called in the DISPATCHED state, but start right with the FETCHED state + where data has already been loaded into the buffer + + - there is no separate CLOSED state where the callback is invoked + separately when loading has finished (or the request has failed), + instead the finished and failed flags will be set as part of + the last FETCHED invocation + + This means providing a big-enough buffer to fit the entire file is the + best case, the response callback will only be called once, ideally in + the next frame (or two calls to sfetch_dowork()). + + If no buffer is provided upfront, one frame of latency is added because + the response callback needs to be invoked in the DISPATCHED state so that + the user code can bind a buffer. + + This means the best case for a request without an upfront-provided + buffer is 2 frames (or 3 calls to sfetch_dowork()). + + That's about what can be done to improve the latency for a single request, + but the really important step is to improve overall throughput. If you + need to load thousands of files you don't want that to be completely + serialized. + + The most important action to increase throughput is to increase the + number of lanes per channel. This defines how many requests can be + 'in flight' on a single channel at the same time. The guiding decision + factor for how many lanes you can "afford" is the memory size you want + to set aside for buffers. Each lane needs its own buffer so that + the data loaded for one request doesn't scribble over the data + loaded for another request. + + Here's a simple example of sending 4 requests without upfront buffer + on a channel with 1, 2 and 4 lanes, each line is one frame: + + 1 LANE (8 frames): + Lane 0: + ------------- + REQ 0 DISPATCHED + REQ 0 FETCHED + REQ 1 DISPATCHED + REQ 1 FETCHED + REQ 2 DISPATCHED + REQ 2 FETCHED + REQ 3 DISPATCHED + REQ 3 FETCHED + + Note how the request don't overlap, so they can all use the same buffer. + + 2 LANES (4 frames): + Lane 0: Lane 1: + ------------------------------------ + REQ 0 DISPATCHED REQ 1 DISPATCHED + REQ 0 FETCHED REQ 1 FETCHED + REQ 2 DISPATCHED REQ 3 DISPATCHED + REQ 2 FETCHED REQ 3 FETCHED + + This reduces the overall time to 4 frames, but now you need 2 buffers so + that requests don't scribble over each other. + + 4 LANES (2 frames): + Lane 0: Lane 1: Lane 2: Lane 3: + ---------------------------------------------------------------------------- + REQ 0 DISPATCHED REQ 1 DISPATCHED REQ 2 DISPATCHED REQ 3 DISPATCHED + REQ 0 FETCHED REQ 1 FETCHED REQ 2 FETCHED REQ 3 FETCHED + + Now we're down to the same 'best-case' latency as sending a single + request. + + Apart from the memory requirements for the streaming buffers (which is + under your control), you can be generous with the number of lanes, + they don't add any processing overhead. + + The last option for tweaking latency and throughput is channels. Each + channel works independently from other channels, so while one + channel is busy working through a large number of requests (or one + very long streaming download), you can set aside a high-priority channel + for requests that need to start as soon as possible. + + On platforms with threading support, each channel runs on its own + thread, but this is mainly an implementation detail to work around + the blocking traditional file IO functions, not for performance reasons. + + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + sfetch_setup(&(sfetch_desc_t){ + // ... + .allocator = { + .alloc_fn = my_alloc, + .free_fn = my_free, + .user_data = ..., + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_fetch.h + itself though, not any allocations in OS libraries. + + Memory allocation will only happen on the same thread where sfetch_setup() + was called, so you don't need to worry about thread-safety. + + + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call, + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + sfetch_setup(&(sfetch_desc_t){ + // ... + .logger.func = slog_func + }); + + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'sfetch' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SFETCH_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_fetch.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-fetch like this: + + sfetch_setup(&(sfetch_desc_t){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); + + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. + + + FUTURE PLANS / V2.0 IDEA DUMP + ============================= + - An optional polling API (as alternative to callback API) + - Move buffer-management into the API? The "manual management" + can be quite tricky especially for dynamic allocation scenarios, + API support for buffer management would simplify cases like + preventing that requests scribble over each other's buffers, or + an automatic garbage collection for dynamically allocated buffers, + or automatically falling back to dynamic allocation if static + buffers aren't big enough. + - Pluggable request handlers to load data from other "sources" + (especially HTTP downloads on native platforms via e.g. libcurl + would be useful) + - I'm currently not happy how the user-data block is handled, this + should getting and updating the user-data should be wrapped by + API functions (similar to bind/unbind buffer) + + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2019 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_FETCH_INCLUDED (1) +#include // size_t +#include +#include + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_FETCH_API_DECL) +#define SOKOL_FETCH_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_FETCH_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_FETCH_IMPL) +#define SOKOL_FETCH_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_FETCH_API_DECL __declspec(dllimport) +#else +#define SOKOL_FETCH_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + sfetch_log_item_t + + Log items are defined via X-Macros, and expanded to an + enum 'sfetch_log_item', and in debug mode only, + corresponding strings. + + Used as parameter in the logging callback. +*/ +#define _SFETCH_LOG_ITEMS \ + _SFETCH_LOGITEM_XMACRO(OK, "Ok") \ + _SFETCH_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SFETCH_LOGITEM_XMACRO(FILE_PATH_UTF8_DECODING_FAILED, "failed converting file path from UTF8 to wide") \ + _SFETCH_LOGITEM_XMACRO(SEND_QUEUE_FULL, "send queue full (adjust via sfetch_desc_t.max_requests)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_CHANNEL_INDEX_TOO_BIG, "channel index too big (adjust via sfetch_desc_t.num_channels)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_PATH_IS_NULL, "file path is nullptr (sfetch_request_t.path)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_PATH_TOO_LONG, "file path is too long (SFETCH_MAX_PATH)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_CALLBACK_MISSING, "no callback provided (sfetch_request_t.callback)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_CHUNK_SIZE_GREATER_BUFFER_SIZE, "chunk size is greater buffer size (sfetch_request_t.chunk_size vs .buffer.size)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_USERDATA_PTR_IS_SET_BUT_USERDATA_SIZE_IS_NULL, "user data ptr is set but user data size is null (sfetch_request_t.user_data.ptr vs .size)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_USERDATA_PTR_IS_NULL_BUT_USERDATA_SIZE_IS_NOT, "user data ptr is null but size is not (sfetch_request_t.user_data.ptr vs .size)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_USERDATA_SIZE_TOO_BIG, "user data size too big (see SFETCH_MAX_USERDATA_UINT64)") \ + _SFETCH_LOGITEM_XMACRO(CLAMPING_NUM_CHANNELS_TO_MAX_CHANNELS, "clamping num channels to SFETCH_MAX_CHANNELS") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_POOL_EXHAUSTED, "request pool exhausted (tweak via sfetch_desc_t.max_requests)") \ + +#define _SFETCH_LOGITEM_XMACRO(item,msg) SFETCH_LOGITEM_##item, +typedef enum sfetch_log_item_t { + _SFETCH_LOG_ITEMS +} sfetch_log_item_t; +#undef _SFETCH_LOGITEM_XMACRO + +/* + sfetch_logger_t + + Used in sfetch_desc_t to provide a custom logging and error reporting + callback to sokol-fetch. +*/ +typedef struct sfetch_logger_t { + void (*func)( + const char* tag, // always "sfetch" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SFETCH_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_fetch.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} sfetch_logger_t; + +/* + sfetch_range_t + + A pointer-size pair struct to pass memory ranges into and out of sokol-fetch. + When initialized from a value type (array or struct) you can use the + SFETCH_RANGE() helper macro to build an sfetch_range_t struct. +*/ +typedef struct sfetch_range_t { + const void* ptr; + size_t size; +} sfetch_range_t; + +// disabling this for every includer isn't great, but the warnings are also quite pointless +#if defined(_MSC_VER) +#pragma warning(disable:4221) // /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y' +#pragma warning(disable:4204) // VS2015: nonstandard extension used: non-constant aggregate initializer +#endif +#if defined(__cplusplus) +#define SFETCH_RANGE(x) sfetch_range_t{ &x, sizeof(x) } +#else +#define SFETCH_RANGE(x) (sfetch_range_t){ &x, sizeof(x) } +#endif + +/* + sfetch_allocator_t + + Used in sfetch_desc_t to provide custom memory-alloc and -free functions + to sokol_fetch.h. If memory management should be overridden, both the + alloc and free function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct sfetch_allocator_t { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} sfetch_allocator_t; + +/* configuration values for sfetch_setup() */ +typedef struct sfetch_desc_t { + uint32_t max_requests; // max number of active requests across all channels (default: 128) + uint32_t num_channels; // number of channels to fetch requests in parallel (default: 1) + uint32_t num_lanes; // max number of requests active on the same channel (default: 1) + sfetch_allocator_t allocator; // optional memory allocation overrides (default: malloc/free) + sfetch_logger_t logger; // optional log function overrides (default: NO LOGGING!) +} sfetch_desc_t; + +/* a request handle to identify an active fetch request, returned by sfetch_send() */ +typedef struct sfetch_handle_t { uint32_t id; } sfetch_handle_t; + +/* error codes */ +typedef enum sfetch_error_t { + SFETCH_ERROR_NO_ERROR, + SFETCH_ERROR_FILE_NOT_FOUND, + SFETCH_ERROR_NO_BUFFER, + SFETCH_ERROR_BUFFER_TOO_SMALL, + SFETCH_ERROR_UNEXPECTED_EOF, + SFETCH_ERROR_INVALID_HTTP_STATUS, + SFETCH_ERROR_CANCELLED +} sfetch_error_t; + +/* the response struct passed to the response callback */ +typedef struct sfetch_response_t { + sfetch_handle_t handle; // request handle this response belongs to + bool dispatched; // true when request is in DISPATCHED state (lane has been assigned) + bool fetched; // true when request is in FETCHED state (fetched data is available) + bool paused; // request is currently in paused state + bool finished; // this is the last response for this request + bool failed; // request has failed (always set together with 'finished') + bool cancelled; // request was cancelled (always set together with 'finished') + sfetch_error_t error_code; // more detailed error code when failed is true + uint32_t channel; // the channel which processes this request + uint32_t lane; // the lane this request occupies on its channel + const char* path; // the original filesystem path of the request + void* user_data; // pointer to read/write user-data area + uint32_t data_offset; // current offset of fetched data chunk in the overall file data + sfetch_range_t data; // the fetched data as ptr/size pair (data.ptr == buffer.ptr, data.size <= buffer.size) + sfetch_range_t buffer; // the user-provided buffer which holds the fetched data +} sfetch_response_t; + +/* request parameters passed to sfetch_send() */ +typedef struct sfetch_request_t { + uint32_t channel; // index of channel this request is assigned to (default: 0) + const char* path; // filesystem path or HTTP URL (required) + void (*callback) (const sfetch_response_t*); // response callback function pointer (required) + uint32_t chunk_size; // number of bytes to load per stream-block (optional) + sfetch_range_t buffer; // a memory buffer where the data will be loaded into (optional) + sfetch_range_t user_data; // ptr/size of a POD user data block which will be memcpy'd (optional) +} sfetch_request_t; + +/* setup sokol-fetch (can be called on multiple threads) */ +SOKOL_FETCH_API_DECL void sfetch_setup(const sfetch_desc_t* desc); +/* discard a sokol-fetch context */ +SOKOL_FETCH_API_DECL void sfetch_shutdown(void); +/* return true if sokol-fetch has been setup */ +SOKOL_FETCH_API_DECL bool sfetch_valid(void); +/* get the desc struct that was passed to sfetch_setup() */ +SOKOL_FETCH_API_DECL sfetch_desc_t sfetch_desc(void); +/* return the max userdata size in number of bytes (SFETCH_MAX_USERDATA_UINT64 * sizeof(uint64_t)) */ +SOKOL_FETCH_API_DECL int sfetch_max_userdata_bytes(void); +/* return the value of the SFETCH_MAX_PATH implementation config value */ +SOKOL_FETCH_API_DECL int sfetch_max_path(void); + +/* send a fetch-request, get handle to request back */ +SOKOL_FETCH_API_DECL sfetch_handle_t sfetch_send(const sfetch_request_t* request); +/* return true if a handle is valid *and* the request is alive */ +SOKOL_FETCH_API_DECL bool sfetch_handle_valid(sfetch_handle_t h); +/* do per-frame work, moves requests into and out of IO threads, and invokes response-callbacks */ +SOKOL_FETCH_API_DECL void sfetch_dowork(void); + +/* bind a data buffer to a request (request must not currently have a buffer bound, must be called from response callback */ +SOKOL_FETCH_API_DECL void sfetch_bind_buffer(sfetch_handle_t h, sfetch_range_t buffer); +/* clear the 'buffer binding' of a request, returns previous buffer pointer (can be 0), must be called from response callback */ +SOKOL_FETCH_API_DECL void* sfetch_unbind_buffer(sfetch_handle_t h); +/* cancel a request that's in flight (will call response callback with .cancelled + .finished) */ +SOKOL_FETCH_API_DECL void sfetch_cancel(sfetch_handle_t h); +/* pause a request (will call response callback each frame with .paused) */ +SOKOL_FETCH_API_DECL void sfetch_pause(sfetch_handle_t h); +/* continue a paused request */ +SOKOL_FETCH_API_DECL void sfetch_continue(sfetch_handle_t h); + +#ifdef __cplusplus +} /* extern "C" */ + +/* reference-based equivalents for c++ */ +inline void sfetch_setup(const sfetch_desc_t& desc) { return sfetch_setup(&desc); } +inline sfetch_handle_t sfetch_send(const sfetch_request_t& request) { return sfetch_send(&request); } + +#endif +#endif // SOKOL_FETCH_INCLUDED + +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation +#ifdef SOKOL_FETCH_IMPL +#define SOKOL_FETCH_IMPL_INCLUDED (1) + +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sfetch_desc_t.allocator to override memory allocation functions" +#endif + +#include /* malloc, free */ +#include /* memset, memcpy */ + +#ifndef SFETCH_MAX_PATH +#define SFETCH_MAX_PATH (1024) +#endif +#ifndef SFETCH_MAX_USERDATA_UINT64 +#define SFETCH_MAX_USERDATA_UINT64 (16) +#endif +#ifndef SFETCH_MAX_CHANNELS +#define SFETCH_MAX_CHANNELS (16) +#endif + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif + +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif + +#ifndef _SOKOL_UNUSED + #define _SOKOL_UNUSED(x) (void)(x) +#endif + +#if defined(__EMSCRIPTEN__) + #include + #define _SFETCH_PLATFORM_EMSCRIPTEN (1) + #define _SFETCH_PLATFORM_WINDOWS (0) + #define _SFETCH_PLATFORM_POSIX (0) + #define _SFETCH_HAS_THREADS (0) +#elif defined(_WIN32) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #define _SFETCH_PLATFORM_WINDOWS (1) + #define _SFETCH_PLATFORM_EMSCRIPTEN (0) + #define _SFETCH_PLATFORM_POSIX (0) + #define _SFETCH_HAS_THREADS (1) +#else + #include + #include /* fopen, fread, fseek, fclose */ + #define _SFETCH_PLATFORM_POSIX (1) + #define _SFETCH_PLATFORM_EMSCRIPTEN (0) + #define _SFETCH_PLATFORM_WINDOWS (0) + #define _SFETCH_HAS_THREADS (1) +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4724) // potential mod by 0 +#endif + +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs +typedef struct _sfetch_path_t { + char buf[SFETCH_MAX_PATH]; +} _sfetch_path_t; + +/* a thread with incoming and outgoing message queue syncing */ +#if _SFETCH_PLATFORM_POSIX +typedef struct { + pthread_t thread; + pthread_cond_t incoming_cond; + pthread_mutex_t incoming_mutex; + pthread_mutex_t outgoing_mutex; + pthread_mutex_t running_mutex; + pthread_mutex_t stop_mutex; + bool stop_requested; + bool valid; +} _sfetch_thread_t; +#elif _SFETCH_PLATFORM_WINDOWS +typedef struct { + HANDLE thread; + HANDLE incoming_event; + CRITICAL_SECTION incoming_critsec; + CRITICAL_SECTION outgoing_critsec; + CRITICAL_SECTION running_critsec; + CRITICAL_SECTION stop_critsec; + bool stop_requested; + bool valid; +} _sfetch_thread_t; +#endif + +/* file handle abstraction */ +#if _SFETCH_PLATFORM_POSIX +typedef FILE* _sfetch_file_handle_t; +#define _SFETCH_INVALID_FILE_HANDLE (0) +typedef void*(*_sfetch_thread_func_t)(void*); +#elif _SFETCH_PLATFORM_WINDOWS +typedef HANDLE _sfetch_file_handle_t; +#define _SFETCH_INVALID_FILE_HANDLE (INVALID_HANDLE_VALUE) +typedef LPTHREAD_START_ROUTINE _sfetch_thread_func_t; +#endif + +/* user-side per-request state */ +typedef struct { + bool pause; /* switch item to PAUSED state if true */ + bool cont; /* switch item back to FETCHING if true */ + bool cancel; /* cancel the request, switch into FAILED state */ + /* transfer IO => user thread */ + uint32_t fetched_offset; /* number of bytes fetched so far */ + uint32_t fetched_size; /* size of last fetched chunk */ + sfetch_error_t error_code; + bool finished; + /* user thread only */ + size_t user_data_size; + uint64_t user_data[SFETCH_MAX_USERDATA_UINT64]; +} _sfetch_item_user_t; + +/* thread-side per-request state */ +typedef struct { + /* transfer IO => user thread */ + uint32_t fetched_offset; + uint32_t fetched_size; + sfetch_error_t error_code; + bool failed; + bool finished; + /* IO thread only */ + #if _SFETCH_PLATFORM_EMSCRIPTEN + uint32_t http_range_offset; + #else + _sfetch_file_handle_t file_handle; + #endif + uint32_t content_size; +} _sfetch_item_thread_t; + +/* a request goes through the following states, ping-ponging between IO and user thread */ +typedef enum _sfetch_state_t { + _SFETCH_STATE_INITIAL, /* internal: request has just been initialized */ + _SFETCH_STATE_ALLOCATED, /* internal: request has been allocated from internal pool */ + _SFETCH_STATE_DISPATCHED, /* user thread: request has been dispatched to its IO channel */ + _SFETCH_STATE_FETCHING, /* IO thread: waiting for data to be fetched */ + _SFETCH_STATE_FETCHED, /* user thread: fetched data available */ + _SFETCH_STATE_PAUSED, /* user thread: request has been paused via sfetch_pause() */ + _SFETCH_STATE_FAILED, /* user thread: follow state or FETCHING if something went wrong */ +} _sfetch_state_t; + +/* an internal request item */ +#define _SFETCH_INVALID_LANE (0xFFFFFFFF) +typedef struct { + sfetch_handle_t handle; + _sfetch_state_t state; + uint32_t channel; + uint32_t lane; + uint32_t chunk_size; + void (*callback) (const sfetch_response_t*); + sfetch_range_t buffer; + + /* updated by IO-thread, off-limits to user thread */ + _sfetch_item_thread_t thread; + + /* accessible by user-thread, off-limits to IO thread */ + _sfetch_item_user_t user; + + /* big stuff at the end */ + _sfetch_path_t path; +} _sfetch_item_t; + +/* a pool of internal per-request items */ +typedef struct { + uint32_t size; + uint32_t free_top; + _sfetch_item_t* items; + uint32_t* free_slots; + uint32_t* gen_ctrs; + bool valid; +} _sfetch_pool_t; + +/* a ringbuffer for pool-slot ids */ +typedef struct { + uint32_t head; + uint32_t tail; + uint32_t num; + uint32_t* buf; +} _sfetch_ring_t; + +/* an IO channel with its own IO thread */ +struct _sfetch_t; +typedef struct { + struct _sfetch_t* ctx; // back-pointer to thread-local _sfetch state pointer, since this isn't accessible from the IO threads + _sfetch_ring_t free_lanes; + _sfetch_ring_t user_sent; + _sfetch_ring_t user_incoming; + _sfetch_ring_t user_outgoing; + #if _SFETCH_HAS_THREADS + _sfetch_ring_t thread_incoming; + _sfetch_ring_t thread_outgoing; + _sfetch_thread_t thread; + #endif + void (*request_handler)(struct _sfetch_t* ctx, uint32_t slot_id); + bool valid; +} _sfetch_channel_t; + +/* the sfetch global state */ +typedef struct _sfetch_t { + bool setup; + bool valid; + bool in_callback; + sfetch_desc_t desc; + _sfetch_pool_t pool; + _sfetch_channel_t chn[SFETCH_MAX_CHANNELS]; +} _sfetch_t; +#if _SFETCH_HAS_THREADS +#if defined(_MSC_VER) +static __declspec(thread) _sfetch_t* _sfetch; +#else +static __thread _sfetch_t* _sfetch; +#endif +#else +static _sfetch_t* _sfetch; +#endif +#define _sfetch_def(val, def) (((val) == 0) ? (def) : (val)) + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SFETCH_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sfetch_log_messages[] = { + _SFETCH_LOG_ITEMS +}; +#undef _SFETCH_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SFETCH_PANIC(code) _sfetch_log(SFETCH_LOGITEM_ ##code, 0, __LINE__) +#define _SFETCH_ERROR(code) _sfetch_log(SFETCH_LOGITEM_ ##code, 1, __LINE__) +#define _SFETCH_WARN(code) _sfetch_log(SFETCH_LOGITEM_ ##code, 2, __LINE__) +#define _SFETCH_INFO(code) _sfetch_log(SFETCH_LOGITEM_ ##code, 3, __LINE__) + +static void _sfetch_log(sfetch_log_item_t log_item, uint32_t log_level, uint32_t line_nr) { + if (_sfetch->desc.logger.func) { + #if defined(SOKOL_DEBUG) + const char* filename = __FILE__; + const char* message = _sfetch_log_messages[log_item]; + #else + const char* filename = 0; + const char* message = 0; + #endif + _sfetch->desc.logger.func("sfetch", log_level, log_item, message, line_nr, filename, _sfetch->desc.logger.user_data); + } + else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory +_SOKOL_PRIVATE void _sfetch_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +_SOKOL_PRIVATE void* _sfetch_malloc_with_allocator(const sfetch_allocator_t* allocator, size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (allocator->alloc_fn) { + ptr = allocator->alloc_fn(size, allocator->user_data); + } else { + ptr = malloc(size); + } + if (0 == ptr) { + _SFETCH_PANIC(MALLOC_FAILED); + } + return ptr; +} + +_SOKOL_PRIVATE void* _sfetch_malloc(size_t size) { + return _sfetch_malloc_with_allocator(&_sfetch->desc.allocator, size); +} + +_SOKOL_PRIVATE void* _sfetch_malloc_clear(size_t size) { + void* ptr = _sfetch_malloc(size); + _sfetch_clear(ptr, size); + return ptr; +} + +_SOKOL_PRIVATE void _sfetch_free(void* ptr) { + if (_sfetch->desc.allocator.free_fn) { + _sfetch->desc.allocator.free_fn(ptr, _sfetch->desc.allocator.user_data); + } else { + free(ptr); + } +} + +_SOKOL_PRIVATE _sfetch_t* _sfetch_ctx(void) { + return _sfetch; +} + +_SOKOL_PRIVATE void _sfetch_path_copy(_sfetch_path_t* dst, const char* src) { + SOKOL_ASSERT(dst); + if (src && (strlen(src) < SFETCH_MAX_PATH)) { + #if defined(_MSC_VER) + strncpy_s(dst->buf, SFETCH_MAX_PATH, src, (SFETCH_MAX_PATH-1)); + #else + strncpy(dst->buf, src, SFETCH_MAX_PATH); + #endif + dst->buf[SFETCH_MAX_PATH-1] = 0; + } + else { + _sfetch_clear(dst->buf, SFETCH_MAX_PATH); + } +} + +_SOKOL_PRIVATE _sfetch_path_t _sfetch_path_make(const char* str) { + _sfetch_path_t res; + _sfetch_path_copy(&res, str); + return res; +} + +// ███ ███ ███████ ███████ ███████ █████ ██████ ███████ ██████ ██ ██ ███████ ██ ██ ███████ +// ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ███████ ███████ ███████ ██ ███ █████ ██ ██ ██ ██ █████ ██ ██ █████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ▄▄ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ███████ ██ ██ ██████ ███████ ██████ ██████ ███████ ██████ ███████ +// ▀▀ +// >>message queue +_SOKOL_PRIVATE uint32_t _sfetch_ring_wrap(const _sfetch_ring_t* rb, uint32_t i) { + return i % rb->num; +} + +_SOKOL_PRIVATE void _sfetch_ring_discard(_sfetch_ring_t* rb) { + SOKOL_ASSERT(rb); + if (rb->buf) { + _sfetch_free(rb->buf); + rb->buf = 0; + } + rb->head = 0; + rb->tail = 0; + rb->num = 0; +} + +_SOKOL_PRIVATE bool _sfetch_ring_init(_sfetch_ring_t* rb, uint32_t num_slots) { + SOKOL_ASSERT(rb && (num_slots > 0)); + SOKOL_ASSERT(0 == rb->buf); + rb->head = 0; + rb->tail = 0; + /* one slot reserved to detect full vs empty */ + rb->num = num_slots + 1; + const size_t queue_size = rb->num * sizeof(sfetch_handle_t); + rb->buf = (uint32_t*) _sfetch_malloc_clear(queue_size); + if (rb->buf) { + return true; + } + else { + _sfetch_ring_discard(rb); + return false; + } +} + +_SOKOL_PRIVATE bool _sfetch_ring_full(const _sfetch_ring_t* rb) { + SOKOL_ASSERT(rb && rb->buf); + return _sfetch_ring_wrap(rb, rb->head + 1) == rb->tail; +} + +_SOKOL_PRIVATE bool _sfetch_ring_empty(const _sfetch_ring_t* rb) { + SOKOL_ASSERT(rb && rb->buf); + return rb->head == rb->tail; +} + +_SOKOL_PRIVATE uint32_t _sfetch_ring_count(const _sfetch_ring_t* rb) { + SOKOL_ASSERT(rb && rb->buf); + uint32_t count; + if (rb->head >= rb->tail) { + count = rb->head - rb->tail; + } + else { + count = (rb->head + rb->num) - rb->tail; + } + SOKOL_ASSERT(count < rb->num); + return count; +} + +_SOKOL_PRIVATE void _sfetch_ring_enqueue(_sfetch_ring_t* rb, uint32_t slot_id) { + SOKOL_ASSERT(rb && rb->buf); + SOKOL_ASSERT(!_sfetch_ring_full(rb)); + SOKOL_ASSERT(rb->head < rb->num); + rb->buf[rb->head] = slot_id; + rb->head = _sfetch_ring_wrap(rb, rb->head + 1); +} + +_SOKOL_PRIVATE uint32_t _sfetch_ring_dequeue(_sfetch_ring_t* rb) { + SOKOL_ASSERT(rb && rb->buf); + SOKOL_ASSERT(!_sfetch_ring_empty(rb)); + SOKOL_ASSERT(rb->tail < rb->num); + uint32_t slot_id = rb->buf[rb->tail]; + rb->tail = _sfetch_ring_wrap(rb, rb->tail + 1); + return slot_id; +} + +_SOKOL_PRIVATE uint32_t _sfetch_ring_peek(const _sfetch_ring_t* rb, uint32_t index) { + SOKOL_ASSERT(rb && rb->buf); + SOKOL_ASSERT(!_sfetch_ring_empty(rb)); + SOKOL_ASSERT(index < _sfetch_ring_count(rb)); + uint32_t rb_index = _sfetch_ring_wrap(rb, rb->tail + index); + return rb->buf[rb_index]; +} + +// ██████ ███████ ██████ ██ ██ ███████ ███████ ████████ ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ █████ ██ ██ ██ ██ █████ ███████ ██ ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ▄▄ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██████ ██████ ███████ ███████ ██ ██ ██████ ██████ ███████ +// ▀▀ +// >>request pool +_SOKOL_PRIVATE uint32_t _sfetch_make_id(uint32_t index, uint32_t gen_ctr) { + return (gen_ctr<<16) | (index & 0xFFFF); +} + +_SOKOL_PRIVATE sfetch_handle_t _sfetch_make_handle(uint32_t slot_id) { + sfetch_handle_t h; + h.id = slot_id; + return h; +} + +_SOKOL_PRIVATE uint32_t _sfetch_slot_index(uint32_t slot_id) { + return slot_id & 0xFFFF; +} + +_SOKOL_PRIVATE void _sfetch_item_init(_sfetch_item_t* item, uint32_t slot_id, const sfetch_request_t* request) { + SOKOL_ASSERT(item && (0 == item->handle.id)); + SOKOL_ASSERT(request && request->path); + _sfetch_clear(item, sizeof(_sfetch_item_t)); + item->handle.id = slot_id; + item->state = _SFETCH_STATE_INITIAL; + item->channel = request->channel; + item->chunk_size = request->chunk_size; + item->lane = _SFETCH_INVALID_LANE; + item->callback = request->callback; + item->buffer = request->buffer; + item->path = _sfetch_path_make(request->path); + #if !_SFETCH_PLATFORM_EMSCRIPTEN + item->thread.file_handle = _SFETCH_INVALID_FILE_HANDLE; + #endif + if (request->user_data.ptr && + (request->user_data.size > 0) && + (request->user_data.size <= (SFETCH_MAX_USERDATA_UINT64*8))) + { + item->user.user_data_size = request->user_data.size; + memcpy(item->user.user_data, request->user_data.ptr, request->user_data.size); + } +} + +_SOKOL_PRIVATE void _sfetch_item_discard(_sfetch_item_t* item) { + SOKOL_ASSERT(item && (0 != item->handle.id)); + _sfetch_clear(item, sizeof(_sfetch_item_t)); +} + +_SOKOL_PRIVATE void _sfetch_pool_discard(_sfetch_pool_t* pool) { + SOKOL_ASSERT(pool); + if (pool->free_slots) { + _sfetch_free(pool->free_slots); + pool->free_slots = 0; + } + if (pool->gen_ctrs) { + _sfetch_free(pool->gen_ctrs); + pool->gen_ctrs = 0; + } + if (pool->items) { + _sfetch_free(pool->items); + pool->items = 0; + } + pool->size = 0; + pool->free_top = 0; + pool->valid = false; +} + +_SOKOL_PRIVATE bool _sfetch_pool_init(_sfetch_pool_t* pool, uint32_t num_items) { + SOKOL_ASSERT(pool && (num_items > 0) && (num_items < ((1<<16)-1))); + SOKOL_ASSERT(0 == pool->items); + /* NOTE: item slot 0 is reserved for the special "invalid" item index 0*/ + pool->size = num_items + 1; + pool->free_top = 0; + const size_t items_size = pool->size * sizeof(_sfetch_item_t); + pool->items = (_sfetch_item_t*) _sfetch_malloc_clear(items_size); + /* generation counters indexable by pool slot index, slot 0 is reserved */ + const size_t gen_ctrs_size = sizeof(uint32_t) * pool->size; + pool->gen_ctrs = (uint32_t*) _sfetch_malloc_clear(gen_ctrs_size); + SOKOL_ASSERT(pool->gen_ctrs); + /* NOTE: it's not a bug to only reserve num_items here */ + const size_t free_slots_size = num_items * sizeof(int); + pool->free_slots = (uint32_t*) _sfetch_malloc_clear(free_slots_size); + if (pool->items && pool->free_slots) { + /* never allocate the 0-th item, this is the reserved 'invalid item' */ + for (uint32_t i = pool->size - 1; i >= 1; i--) { + pool->free_slots[pool->free_top++] = i; + } + pool->valid = true; + } + else { + /* allocation error */ + _sfetch_pool_discard(pool); + } + return pool->valid; +} + +_SOKOL_PRIVATE uint32_t _sfetch_pool_item_alloc(_sfetch_pool_t* pool, const sfetch_request_t* request) { + SOKOL_ASSERT(pool && pool->valid); + if (pool->free_top > 0) { + uint32_t slot_index = pool->free_slots[--pool->free_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + uint32_t slot_id = _sfetch_make_id(slot_index, ++pool->gen_ctrs[slot_index]); + _sfetch_item_init(&pool->items[slot_index], slot_id, request); + pool->items[slot_index].state = _SFETCH_STATE_ALLOCATED; + return slot_id; + } + else { + /* pool exhausted, return the 'invalid handle' */ + return _sfetch_make_id(0, 0); + } +} + +_SOKOL_PRIVATE void _sfetch_pool_item_free(_sfetch_pool_t* pool, uint32_t slot_id) { + SOKOL_ASSERT(pool && pool->valid); + uint32_t slot_index = _sfetch_slot_index(slot_id); + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + SOKOL_ASSERT(pool->items[slot_index].handle.id == slot_id); + #if defined(SOKOL_DEBUG) + /* debug check against double-free */ + for (uint32_t i = 0; i < pool->free_top; i++) { + SOKOL_ASSERT(pool->free_slots[i] != slot_index); + } + #endif + _sfetch_item_discard(&pool->items[slot_index]); + pool->free_slots[pool->free_top++] = slot_index; + SOKOL_ASSERT(pool->free_top <= (pool->size - 1)); +} + +/* return pointer to item by handle without matching id check */ +_SOKOL_PRIVATE _sfetch_item_t* _sfetch_pool_item_at(_sfetch_pool_t* pool, uint32_t slot_id) { + SOKOL_ASSERT(pool && pool->valid); + uint32_t slot_index = _sfetch_slot_index(slot_id); + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return &pool->items[slot_index]; +} + +/* return pointer to item by handle with matching id check */ +_SOKOL_PRIVATE _sfetch_item_t* _sfetch_pool_item_lookup(_sfetch_pool_t* pool, uint32_t slot_id) { + SOKOL_ASSERT(pool && pool->valid); + if (0 != slot_id) { + _sfetch_item_t* item = _sfetch_pool_item_at(pool, slot_id); + if (item->handle.id == slot_id) { + return item; + } + } + return 0; +} + +// ██████ ██████ ███████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ███████ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ███████ ██ ██ ██ +// +// >>posix +#if _SFETCH_PLATFORM_POSIX +_SOKOL_PRIVATE _sfetch_file_handle_t _sfetch_file_open(const _sfetch_path_t* path) { + return fopen(path->buf, "rb"); +} + +_SOKOL_PRIVATE void _sfetch_file_close(_sfetch_file_handle_t h) { + fclose(h); +} + +_SOKOL_PRIVATE bool _sfetch_file_handle_valid(_sfetch_file_handle_t h) { + return h != _SFETCH_INVALID_FILE_HANDLE; +} + +_SOKOL_PRIVATE uint32_t _sfetch_file_size(_sfetch_file_handle_t h) { + fseek(h, 0, SEEK_END); + return (uint32_t) ftell(h); +} + +_SOKOL_PRIVATE bool _sfetch_file_read(_sfetch_file_handle_t h, uint32_t offset, uint32_t num_bytes, void* ptr) { + fseek(h, (long)offset, SEEK_SET); + return num_bytes == fread(ptr, 1, num_bytes, h); +} + +_SOKOL_PRIVATE bool _sfetch_thread_init(_sfetch_thread_t* thread, _sfetch_thread_func_t thread_func, void* thread_arg) { + SOKOL_ASSERT(thread && !thread->valid && !thread->stop_requested); + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutex_init(&thread->incoming_mutex, &attr); + pthread_mutexattr_destroy(&attr); + + pthread_mutexattr_init(&attr); + pthread_mutex_init(&thread->outgoing_mutex, &attr); + pthread_mutexattr_destroy(&attr); + + pthread_mutexattr_init(&attr); + pthread_mutex_init(&thread->running_mutex, &attr); + pthread_mutexattr_destroy(&attr); + + pthread_mutexattr_init(&attr); + pthread_mutex_init(&thread->stop_mutex, &attr); + pthread_mutexattr_destroy(&attr); + + pthread_condattr_t cond_attr; + pthread_condattr_init(&cond_attr); + pthread_cond_init(&thread->incoming_cond, &cond_attr); + pthread_condattr_destroy(&cond_attr); + + /* FIXME: in debug mode, the threads should be named */ + pthread_mutex_lock(&thread->running_mutex); + int res = pthread_create(&thread->thread, 0, thread_func, thread_arg); + thread->valid = (0 == res); + pthread_mutex_unlock(&thread->running_mutex); + return thread->valid; +} + +_SOKOL_PRIVATE void _sfetch_thread_request_stop(_sfetch_thread_t* thread) { + pthread_mutex_lock(&thread->stop_mutex); + thread->stop_requested = true; + pthread_mutex_unlock(&thread->stop_mutex); +} + +_SOKOL_PRIVATE bool _sfetch_thread_stop_requested(_sfetch_thread_t* thread) { + pthread_mutex_lock(&thread->stop_mutex); + bool stop_requested = thread->stop_requested; + pthread_mutex_unlock(&thread->stop_mutex); + return stop_requested; +} + +_SOKOL_PRIVATE void _sfetch_thread_join(_sfetch_thread_t* thread) { + SOKOL_ASSERT(thread); + if (thread->valid) { + pthread_mutex_lock(&thread->incoming_mutex); + _sfetch_thread_request_stop(thread); + pthread_cond_signal(&thread->incoming_cond); + pthread_mutex_unlock(&thread->incoming_mutex); + pthread_join(thread->thread, 0); + thread->valid = false; + } + pthread_mutex_destroy(&thread->stop_mutex); + pthread_mutex_destroy(&thread->running_mutex); + pthread_mutex_destroy(&thread->incoming_mutex); + pthread_mutex_destroy(&thread->outgoing_mutex); + pthread_cond_destroy(&thread->incoming_cond); +} + +/* called when the thread-func is entered, this blocks the thread func until + the _sfetch_thread_t object is fully initialized +*/ +_SOKOL_PRIVATE void _sfetch_thread_entered(_sfetch_thread_t* thread) { + pthread_mutex_lock(&thread->running_mutex); +} + +/* called by the thread-func right before it is left */ +_SOKOL_PRIVATE void _sfetch_thread_leaving(_sfetch_thread_t* thread) { + pthread_mutex_unlock(&thread->running_mutex); +} + +_SOKOL_PRIVATE void _sfetch_thread_enqueue_incoming(_sfetch_thread_t* thread, _sfetch_ring_t* incoming, _sfetch_ring_t* src) { + /* called from user thread */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(incoming && incoming->buf); + SOKOL_ASSERT(src && src->buf); + if (!_sfetch_ring_empty(src)) { + pthread_mutex_lock(&thread->incoming_mutex); + while (!_sfetch_ring_full(incoming) && !_sfetch_ring_empty(src)) { + _sfetch_ring_enqueue(incoming, _sfetch_ring_dequeue(src)); + } + pthread_cond_signal(&thread->incoming_cond); + pthread_mutex_unlock(&thread->incoming_mutex); + } +} + +_SOKOL_PRIVATE uint32_t _sfetch_thread_dequeue_incoming(_sfetch_thread_t* thread, _sfetch_ring_t* incoming) { + /* called from thread function */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(incoming && incoming->buf); + pthread_mutex_lock(&thread->incoming_mutex); + while (_sfetch_ring_empty(incoming) && !thread->stop_requested) { + pthread_cond_wait(&thread->incoming_cond, &thread->incoming_mutex); + } + uint32_t item = 0; + if (!thread->stop_requested) { + item = _sfetch_ring_dequeue(incoming); + } + pthread_mutex_unlock(&thread->incoming_mutex); + return item; +} + +_SOKOL_PRIVATE bool _sfetch_thread_enqueue_outgoing(_sfetch_thread_t* thread, _sfetch_ring_t* outgoing, uint32_t item) { + /* called from thread function */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(outgoing && outgoing->buf); + SOKOL_ASSERT(0 != item); + pthread_mutex_lock(&thread->outgoing_mutex); + bool result = false; + if (!_sfetch_ring_full(outgoing)) { + _sfetch_ring_enqueue(outgoing, item); + } + pthread_mutex_unlock(&thread->outgoing_mutex); + return result; +} + +_SOKOL_PRIVATE void _sfetch_thread_dequeue_outgoing(_sfetch_thread_t* thread, _sfetch_ring_t* outgoing, _sfetch_ring_t* dst) { + /* called from user thread */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(outgoing && outgoing->buf); + SOKOL_ASSERT(dst && dst->buf); + pthread_mutex_lock(&thread->outgoing_mutex); + while (!_sfetch_ring_full(dst) && !_sfetch_ring_empty(outgoing)) { + _sfetch_ring_enqueue(dst, _sfetch_ring_dequeue(outgoing)); + } + pthread_mutex_unlock(&thread->outgoing_mutex); +} +#endif /* _SFETCH_PLATFORM_POSIX */ + +// ██ ██ ██ ███ ██ ██████ ██████ ██ ██ ███████ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ █ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ █ ██ ███████ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██ ██ +// ███ ███ ██ ██ ████ ██████ ██████ ███ ███ ███████ +// +// >>windows +#if _SFETCH_PLATFORM_WINDOWS +_SOKOL_PRIVATE bool _sfetch_win32_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) { + SOKOL_ASSERT(src && dst && (dst_num_bytes > 1)); + _sfetch_clear(dst, (size_t)dst_num_bytes); + const int dst_chars = dst_num_bytes / (int)sizeof(wchar_t); + const int dst_needed = MultiByteToWideChar(CP_UTF8, 0, src, -1, 0, 0); + if ((dst_needed > 0) && (dst_needed < dst_chars)) { + MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, dst_chars); + return true; + } + else { + /* input string doesn't fit into destination buffer */ + return false; + } +} + +_SOKOL_PRIVATE _sfetch_file_handle_t _sfetch_file_open(const _sfetch_path_t* path) { + wchar_t w_path[SFETCH_MAX_PATH]; + if (!_sfetch_win32_utf8_to_wide(path->buf, w_path, sizeof(w_path))) { + _SFETCH_ERROR(FILE_PATH_UTF8_DECODING_FAILED); + return 0; + } + _sfetch_file_handle_t h = CreateFileW( + w_path, /* lpFileName */ + GENERIC_READ, /* dwDesiredAccess */ + FILE_SHARE_READ, /* dwShareMode */ + NULL, /* lpSecurityAttributes */ + OPEN_EXISTING, /* dwCreationDisposition */ + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, /* dwFlagsAndAttributes */ + NULL); /* hTemplateFile */ + return h; +} + +_SOKOL_PRIVATE void _sfetch_file_close(_sfetch_file_handle_t h) { + CloseHandle(h); +} + +_SOKOL_PRIVATE bool _sfetch_file_handle_valid(_sfetch_file_handle_t h) { + return h != _SFETCH_INVALID_FILE_HANDLE; +} + +_SOKOL_PRIVATE uint32_t _sfetch_file_size(_sfetch_file_handle_t h) { + return GetFileSize(h, NULL); +} + +_SOKOL_PRIVATE bool _sfetch_file_read(_sfetch_file_handle_t h, uint32_t offset, uint32_t num_bytes, void* ptr) { + LARGE_INTEGER offset_li; + offset_li.QuadPart = offset; + BOOL seek_res = SetFilePointerEx(h, offset_li, NULL, FILE_BEGIN); + if (seek_res) { + DWORD bytes_read = 0; + BOOL read_res = ReadFile(h, ptr, (DWORD)num_bytes, &bytes_read, NULL); + return read_res && (bytes_read == num_bytes); + } + else { + return false; + } +} + +_SOKOL_PRIVATE bool _sfetch_thread_init(_sfetch_thread_t* thread, _sfetch_thread_func_t thread_func, void* thread_arg) { + SOKOL_ASSERT(thread && !thread->valid && !thread->stop_requested); + + thread->incoming_event = CreateEventA(NULL, FALSE, FALSE, NULL); + SOKOL_ASSERT(NULL != thread->incoming_event); + InitializeCriticalSection(&thread->incoming_critsec); + InitializeCriticalSection(&thread->outgoing_critsec); + InitializeCriticalSection(&thread->running_critsec); + InitializeCriticalSection(&thread->stop_critsec); + + EnterCriticalSection(&thread->running_critsec); + const SIZE_T stack_size = 512 * 1024; + thread->thread = CreateThread(NULL, stack_size, thread_func, thread_arg, 0, NULL); + thread->valid = (NULL != thread->thread); + LeaveCriticalSection(&thread->running_critsec); + return thread->valid; +} + +_SOKOL_PRIVATE void _sfetch_thread_request_stop(_sfetch_thread_t* thread) { + EnterCriticalSection(&thread->stop_critsec); + thread->stop_requested = true; + LeaveCriticalSection(&thread->stop_critsec); +} + +_SOKOL_PRIVATE bool _sfetch_thread_stop_requested(_sfetch_thread_t* thread) { + EnterCriticalSection(&thread->stop_critsec); + bool stop_requested = thread->stop_requested; + LeaveCriticalSection(&thread->stop_critsec); + return stop_requested; +} + +_SOKOL_PRIVATE void _sfetch_thread_join(_sfetch_thread_t* thread) { + if (thread->valid) { + EnterCriticalSection(&thread->incoming_critsec); + _sfetch_thread_request_stop(thread); + BOOL set_event_res = SetEvent(thread->incoming_event); + _SOKOL_UNUSED(set_event_res); + SOKOL_ASSERT(set_event_res); + LeaveCriticalSection(&thread->incoming_critsec); + WaitForSingleObject(thread->thread, INFINITE); + CloseHandle(thread->thread); + thread->valid = false; + } + CloseHandle(thread->incoming_event); + DeleteCriticalSection(&thread->stop_critsec); + DeleteCriticalSection(&thread->running_critsec); + DeleteCriticalSection(&thread->outgoing_critsec); + DeleteCriticalSection(&thread->incoming_critsec); +} + +_SOKOL_PRIVATE void _sfetch_thread_entered(_sfetch_thread_t* thread) { + EnterCriticalSection(&thread->running_critsec); +} + +/* called by the thread-func right before it is left */ +_SOKOL_PRIVATE void _sfetch_thread_leaving(_sfetch_thread_t* thread) { + LeaveCriticalSection(&thread->running_critsec); +} + +_SOKOL_PRIVATE void _sfetch_thread_enqueue_incoming(_sfetch_thread_t* thread, _sfetch_ring_t* incoming, _sfetch_ring_t* src) { + /* called from user thread */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(incoming && incoming->buf); + SOKOL_ASSERT(src && src->buf); + if (!_sfetch_ring_empty(src)) { + EnterCriticalSection(&thread->incoming_critsec); + while (!_sfetch_ring_full(incoming) && !_sfetch_ring_empty(src)) { + _sfetch_ring_enqueue(incoming, _sfetch_ring_dequeue(src)); + } + LeaveCriticalSection(&thread->incoming_critsec); + BOOL set_event_res = SetEvent(thread->incoming_event); + _SOKOL_UNUSED(set_event_res); + SOKOL_ASSERT(set_event_res); + } +} + +_SOKOL_PRIVATE uint32_t _sfetch_thread_dequeue_incoming(_sfetch_thread_t* thread, _sfetch_ring_t* incoming) { + /* called from thread function */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(incoming && incoming->buf); + EnterCriticalSection(&thread->incoming_critsec); + while (_sfetch_ring_empty(incoming) && !thread->stop_requested) { + LeaveCriticalSection(&thread->incoming_critsec); + WaitForSingleObject(thread->incoming_event, INFINITE); + EnterCriticalSection(&thread->incoming_critsec); + } + uint32_t item = 0; + if (!thread->stop_requested) { + item = _sfetch_ring_dequeue(incoming); + } + LeaveCriticalSection(&thread->incoming_critsec); + return item; +} + +_SOKOL_PRIVATE bool _sfetch_thread_enqueue_outgoing(_sfetch_thread_t* thread, _sfetch_ring_t* outgoing, uint32_t item) { + /* called from thread function */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(outgoing && outgoing->buf); + EnterCriticalSection(&thread->outgoing_critsec); + bool result = false; + if (!_sfetch_ring_full(outgoing)) { + _sfetch_ring_enqueue(outgoing, item); + } + LeaveCriticalSection(&thread->outgoing_critsec); + return result; +} + +_SOKOL_PRIVATE void _sfetch_thread_dequeue_outgoing(_sfetch_thread_t* thread, _sfetch_ring_t* outgoing, _sfetch_ring_t* dst) { + /* called from user thread */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(outgoing && outgoing->buf); + SOKOL_ASSERT(dst && dst->buf); + EnterCriticalSection(&thread->outgoing_critsec); + while (!_sfetch_ring_full(dst) && !_sfetch_ring_empty(outgoing)) { + _sfetch_ring_enqueue(dst, _sfetch_ring_dequeue(outgoing)); + } + LeaveCriticalSection(&thread->outgoing_critsec); +} +#endif /* _SFETCH_PLATFORM_WINDOWS */ + +// ██████ ██ ██ █████ ███ ██ ███ ██ ███████ ██ ███████ +// ██ ██ ██ ██ ██ ████ ██ ████ ██ ██ ██ ██ +// ██ ███████ ███████ ██ ██ ██ ██ ██ ██ █████ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ ████ ██ ████ ███████ ███████ ███████ +// +// >>channels + +/* per-channel request handler for native platforms accessing the local filesystem */ +#if _SFETCH_HAS_THREADS +_SOKOL_PRIVATE void _sfetch_request_handler(_sfetch_t* ctx, uint32_t slot_id) { + _sfetch_state_t state; + _sfetch_path_t* path; + _sfetch_item_thread_t* thread; + sfetch_range_t* buffer; + uint32_t chunk_size; + { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (!item) { + return; + } + state = item->state; + SOKOL_ASSERT((state == _SFETCH_STATE_FETCHING) || + (state == _SFETCH_STATE_PAUSED) || + (state == _SFETCH_STATE_FAILED)); + path = &item->path; + thread = &item->thread; + buffer = &item->buffer; + chunk_size = item->chunk_size; + } + if (thread->failed) { + return; + } + if (state == _SFETCH_STATE_FETCHING) { + if ((buffer->ptr == 0) || (buffer->size == 0)) { + thread->error_code = SFETCH_ERROR_NO_BUFFER; + thread->failed = true; + } + else { + /* open file if not happened yet */ + if (!_sfetch_file_handle_valid(thread->file_handle)) { + SOKOL_ASSERT(path->buf[0]); + SOKOL_ASSERT(thread->fetched_offset == 0); + SOKOL_ASSERT(thread->fetched_size == 0); + thread->file_handle = _sfetch_file_open(path); + if (_sfetch_file_handle_valid(thread->file_handle)) { + thread->content_size = _sfetch_file_size(thread->file_handle); + } + else { + thread->error_code = SFETCH_ERROR_FILE_NOT_FOUND; + thread->failed = true; + } + } + if (!thread->failed) { + uint32_t read_offset = 0; + uint32_t bytes_to_read = 0; + if (chunk_size == 0) { + /* load entire file */ + if (thread->content_size <= buffer->size) { + bytes_to_read = thread->content_size; + read_offset = 0; + } + else { + /* provided buffer to small to fit entire file */ + thread->error_code = SFETCH_ERROR_BUFFER_TOO_SMALL; + thread->failed = true; + } + } + else { + if (chunk_size <= buffer->size) { + bytes_to_read = chunk_size; + read_offset = thread->fetched_offset; + if ((read_offset + bytes_to_read) > thread->content_size) { + bytes_to_read = thread->content_size - read_offset; + } + } + else { + /* provided buffer to small to fit next chunk */ + thread->error_code = SFETCH_ERROR_BUFFER_TOO_SMALL; + thread->failed = true; + } + } + if (!thread->failed) { + if (_sfetch_file_read(thread->file_handle, read_offset, bytes_to_read, (void*)buffer->ptr)) { + thread->fetched_size = bytes_to_read; + thread->fetched_offset += bytes_to_read; + } + else { + thread->error_code = SFETCH_ERROR_UNEXPECTED_EOF; + thread->failed = true; + } + } + } + } + SOKOL_ASSERT(thread->fetched_offset <= thread->content_size); + if (thread->failed || (thread->fetched_offset == thread->content_size)) { + if (_sfetch_file_handle_valid(thread->file_handle)) { + _sfetch_file_close(thread->file_handle); + thread->file_handle = _SFETCH_INVALID_FILE_HANDLE; + } + thread->finished = true; + } + } + /* ignore items in PAUSED or FAILED state */ +} + +#if _SFETCH_PLATFORM_WINDOWS +_SOKOL_PRIVATE DWORD WINAPI _sfetch_channel_thread_func(LPVOID arg) { +#else +_SOKOL_PRIVATE void* _sfetch_channel_thread_func(void* arg) { +#endif + _sfetch_channel_t* chn = (_sfetch_channel_t*) arg; + _sfetch_thread_entered(&chn->thread); + while (!_sfetch_thread_stop_requested(&chn->thread)) { + /* block until work arrives */ + uint32_t slot_id = _sfetch_thread_dequeue_incoming(&chn->thread, &chn->thread_incoming); + /* slot_id will be invalid if the thread was woken up to join */ + if (!_sfetch_thread_stop_requested(&chn->thread)) { + SOKOL_ASSERT(0 != slot_id); + chn->request_handler(chn->ctx, slot_id); + SOKOL_ASSERT(!_sfetch_ring_full(&chn->thread_outgoing)); + _sfetch_thread_enqueue_outgoing(&chn->thread, &chn->thread_outgoing, slot_id); + } + } + _sfetch_thread_leaving(&chn->thread); + return 0; +} +#endif /* _SFETCH_HAS_THREADS */ + +#if _SFETCH_PLATFORM_EMSCRIPTEN +EM_JS(void, sfetch_js_send_head_request, (uint32_t slot_id, const char* path_cstr), { + const path_str = UTF8ToString(path_cstr); + const req = new XMLHttpRequest(); + req.open('HEAD', path_str); + req.onreadystatechange = function() { + if (req.readyState == XMLHttpRequest.DONE) { + if (req.status == 200) { + const content_length = req.getResponseHeader('Content-Length'); + __sfetch_emsc_head_response(slot_id, content_length); + } + else { + __sfetch_emsc_failed_http_status(slot_id, req.status); + } + } + }; + req.send(); +}); + +/* if bytes_to_read != 0, a range-request will be sent, otherwise a normal request */ +EM_JS(void, sfetch_js_send_get_request, (uint32_t slot_id, const char* path_cstr, uint32_t offset, uint32_t bytes_to_read, void* buf_ptr, uint32_t buf_size), { + const path_str = UTF8ToString(path_cstr); + const req = new XMLHttpRequest(); + req.open('GET', path_str); + req.responseType = 'arraybuffer'; + const need_range_request = (bytes_to_read > 0); + if (need_range_request) { + req.setRequestHeader('Range', 'bytes='+offset+'-'+(offset+bytes_to_read-1)); + } + req.onreadystatechange = function() { + if (req.readyState == XMLHttpRequest.DONE) { + if ((req.status == 206) || ((req.status == 200) && !need_range_request)) { + const u8_array = new Uint8Array(\x2F\x2A\x2A @type {!ArrayBuffer} \x2A\x2F (req.response)); + const content_fetched_size = u8_array.length; + if (content_fetched_size <= buf_size) { + HEAPU8.set(u8_array, buf_ptr); + __sfetch_emsc_get_response(slot_id, bytes_to_read, content_fetched_size); + } + else { + __sfetch_emsc_failed_buffer_too_small(slot_id); + } + } + else { + __sfetch_emsc_failed_http_status(slot_id, req.status); + } + } + }; + req.send(); +}); + +/*=== emscripten specific C helper functions =================================*/ +#ifdef __cplusplus +extern "C" { +#endif +void _sfetch_emsc_send_get_request(uint32_t slot_id, _sfetch_item_t* item) { + if ((item->buffer.ptr == 0) || (item->buffer.size == 0)) { + item->thread.error_code = SFETCH_ERROR_NO_BUFFER; + item->thread.failed = true; + } + else { + uint32_t offset = 0; + uint32_t bytes_to_read = 0; + if (item->chunk_size > 0) { + /* send HTTP range request */ + SOKOL_ASSERT(item->thread.content_size > 0); + SOKOL_ASSERT(item->thread.http_range_offset < item->thread.content_size); + bytes_to_read = item->thread.content_size - item->thread.http_range_offset; + if (bytes_to_read > item->chunk_size) { + bytes_to_read = item->chunk_size; + } + SOKOL_ASSERT(bytes_to_read > 0); + offset = item->thread.http_range_offset; + } + sfetch_js_send_get_request(slot_id, item->path.buf, offset, bytes_to_read, (void*)item->buffer.ptr, item->buffer.size); + } +} + +/* called by JS when an initial HEAD request finished successfully (only when streaming chunks) */ +EMSCRIPTEN_KEEPALIVE void _sfetch_emsc_head_response(uint32_t slot_id, uint32_t content_length) { + _sfetch_t* ctx = _sfetch_ctx(); + if (ctx && ctx->valid) { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (item) { + SOKOL_ASSERT(item->buffer.ptr && (item->buffer.size > 0)); + item->thread.content_size = content_length; + _sfetch_emsc_send_get_request(slot_id, item); + } + } +} + +/* called by JS when a followup GET request finished successfully */ +EMSCRIPTEN_KEEPALIVE void _sfetch_emsc_get_response(uint32_t slot_id, uint32_t range_fetched_size, uint32_t content_fetched_size) { + _sfetch_t* ctx = _sfetch_ctx(); + if (ctx && ctx->valid) { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (item) { + item->thread.fetched_size = content_fetched_size; + item->thread.fetched_offset += content_fetched_size; + item->thread.http_range_offset += range_fetched_size; + if (item->chunk_size == 0) { + item->thread.finished = true; + } + else if (item->thread.http_range_offset >= item->thread.content_size) { + item->thread.finished = true; + } + _sfetch_ring_enqueue(&ctx->chn[item->channel].user_outgoing, slot_id); + } + } +} + +/* called by JS when an error occurred */ +EMSCRIPTEN_KEEPALIVE void _sfetch_emsc_failed_http_status(uint32_t slot_id, uint32_t http_status) { + _sfetch_t* ctx = _sfetch_ctx(); + if (ctx && ctx->valid) { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (item) { + if (http_status == 404) { + item->thread.error_code = SFETCH_ERROR_FILE_NOT_FOUND; + } + else { + item->thread.error_code = SFETCH_ERROR_INVALID_HTTP_STATUS; + } + item->thread.failed = true; + item->thread.finished = true; + _sfetch_ring_enqueue(&ctx->chn[item->channel].user_outgoing, slot_id); + } + } +} + +EMSCRIPTEN_KEEPALIVE void _sfetch_emsc_failed_buffer_too_small(uint32_t slot_id) { + _sfetch_t* ctx = _sfetch_ctx(); + if (ctx && ctx->valid) { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (item) { + item->thread.error_code = SFETCH_ERROR_BUFFER_TOO_SMALL; + item->thread.failed = true; + item->thread.finished = true; + _sfetch_ring_enqueue(&ctx->chn[item->channel].user_outgoing, slot_id); + } + } +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +_SOKOL_PRIVATE void _sfetch_request_handler(_sfetch_t* ctx, uint32_t slot_id) { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (!item) { + return; + } + if (item->state == _SFETCH_STATE_FETCHING) { + if ((item->chunk_size > 0) && (item->thread.content_size == 0)) { + /* if streaming download is requested, and the content-length isn't known + yet, need to send a HEAD request first + */ + sfetch_js_send_head_request(slot_id, item->path.buf); + } + else { + /* otherwise, this is either a request to load the entire file, or + to load the next streaming chunk + */ + _sfetch_emsc_send_get_request(slot_id, item); + } + } + else { + /* just move all other items (e.g. paused or cancelled) + into the outgoing queue, so they won't get lost + */ + _sfetch_ring_enqueue(&ctx->chn[item->channel].user_outgoing, slot_id); + } + if (item->thread.failed) { + item->thread.finished = true; + } +} +#endif /* _SFETCH_PLATFORM_EMSCRIPTEN */ + +_SOKOL_PRIVATE void _sfetch_channel_discard(_sfetch_channel_t* chn) { + SOKOL_ASSERT(chn); + #if _SFETCH_HAS_THREADS + if (chn->valid) { + _sfetch_thread_join(&chn->thread); + } + _sfetch_ring_discard(&chn->thread_incoming); + _sfetch_ring_discard(&chn->thread_outgoing); + #endif + _sfetch_ring_discard(&chn->free_lanes); + _sfetch_ring_discard(&chn->user_sent); + _sfetch_ring_discard(&chn->user_incoming); + _sfetch_ring_discard(&chn->user_outgoing); + _sfetch_ring_discard(&chn->free_lanes); + chn->valid = false; +} + +_SOKOL_PRIVATE bool _sfetch_channel_init(_sfetch_channel_t* chn, _sfetch_t* ctx, uint32_t num_items, uint32_t num_lanes, void (*request_handler)(_sfetch_t* ctx, uint32_t)) { + SOKOL_ASSERT(chn && (num_items > 0) && request_handler); + SOKOL_ASSERT(!chn->valid); + bool valid = true; + chn->request_handler = request_handler; + chn->ctx = ctx; + valid &= _sfetch_ring_init(&chn->free_lanes, num_lanes); + for (uint32_t lane = 0; lane < num_lanes; lane++) { + _sfetch_ring_enqueue(&chn->free_lanes, lane); + } + valid &= _sfetch_ring_init(&chn->user_sent, num_items); + valid &= _sfetch_ring_init(&chn->user_incoming, num_lanes); + valid &= _sfetch_ring_init(&chn->user_outgoing, num_lanes); + #if _SFETCH_HAS_THREADS + valid &= _sfetch_ring_init(&chn->thread_incoming, num_lanes); + valid &= _sfetch_ring_init(&chn->thread_outgoing, num_lanes); + #endif + if (valid) { + chn->valid = true; + #if _SFETCH_HAS_THREADS + _sfetch_thread_init(&chn->thread, _sfetch_channel_thread_func, chn); + #endif + return true; + } + else { + _sfetch_channel_discard(chn); + return false; + } +} + +/* put a request into the channels sent-queue, this is where all new requests + are stored until a lane becomes free. +*/ +_SOKOL_PRIVATE bool _sfetch_channel_send(_sfetch_channel_t* chn, uint32_t slot_id) { + SOKOL_ASSERT(chn && chn->valid); + if (!_sfetch_ring_full(&chn->user_sent)) { + _sfetch_ring_enqueue(&chn->user_sent, slot_id); + return true; + } + else { + _SFETCH_ERROR(SEND_QUEUE_FULL); + return false; + } +} + +_SOKOL_PRIVATE void _sfetch_invoke_response_callback(_sfetch_item_t* item) { + sfetch_response_t response; + _sfetch_clear(&response, sizeof(response)); + response.handle = item->handle; + response.dispatched = (item->state == _SFETCH_STATE_DISPATCHED); + response.fetched = (item->state == _SFETCH_STATE_FETCHED); + response.paused = (item->state == _SFETCH_STATE_PAUSED); + response.finished = item->user.finished; + response.failed = (item->state == _SFETCH_STATE_FAILED); + response.cancelled = item->user.cancel; + response.error_code = item->user.error_code; + response.channel = item->channel; + response.lane = item->lane; + response.path = item->path.buf; + response.user_data = item->user.user_data; + response.data_offset = item->user.fetched_offset - item->user.fetched_size; + response.data.ptr = item->buffer.ptr; + response.data.size = item->user.fetched_size; + response.buffer = item->buffer; + item->callback(&response); +} + +_SOKOL_PRIVATE void _sfetch_cancel_item(_sfetch_item_t* item) { + item->state = _SFETCH_STATE_FAILED; + item->user.finished = true; + item->user.error_code = SFETCH_ERROR_CANCELLED; +} + +/* per-frame channel stuff: move requests in and out of the IO threads, call response callbacks */ +_SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_t* pool) { + + /* move items from sent- to incoming-queue permitting free lanes */ + const uint32_t num_sent = _sfetch_ring_count(&chn->user_sent); + const uint32_t avail_lanes = _sfetch_ring_count(&chn->free_lanes); + const uint32_t num_move = (num_sent < avail_lanes) ? num_sent : avail_lanes; + for (uint32_t i = 0; i < num_move; i++) { + const uint32_t slot_id = _sfetch_ring_dequeue(&chn->user_sent); + _sfetch_item_t* item = _sfetch_pool_item_lookup(pool, slot_id); + SOKOL_ASSERT(item); + SOKOL_ASSERT(item->state == _SFETCH_STATE_ALLOCATED); + // if the item was cancelled early, kick it out immediately + if (item->user.cancel) { + _sfetch_cancel_item(item); + _sfetch_invoke_response_callback(item); + _sfetch_pool_item_free(pool, slot_id); + continue; + } + item->state = _SFETCH_STATE_DISPATCHED; + item->lane = _sfetch_ring_dequeue(&chn->free_lanes); + // if no buffer provided yet, invoke response callback to do so + if (0 == item->buffer.ptr) { + _sfetch_invoke_response_callback(item); + } + _sfetch_ring_enqueue(&chn->user_incoming, slot_id); + } + + /* prepare incoming items for being moved into the IO thread */ + const uint32_t num_incoming = _sfetch_ring_count(&chn->user_incoming); + for (uint32_t i = 0; i < num_incoming; i++) { + const uint32_t slot_id = _sfetch_ring_peek(&chn->user_incoming, i); + _sfetch_item_t* item = _sfetch_pool_item_lookup(pool, slot_id); + SOKOL_ASSERT(item); + SOKOL_ASSERT(item->state != _SFETCH_STATE_INITIAL); + SOKOL_ASSERT(item->state != _SFETCH_STATE_FETCHING); + /* transfer input params from user- to thread-data */ + if (item->user.pause) { + item->state = _SFETCH_STATE_PAUSED; + item->user.pause = false; + } + if (item->user.cont) { + if (item->state == _SFETCH_STATE_PAUSED) { + item->state = _SFETCH_STATE_FETCHED; + } + item->user.cont = false; + } + if (item->user.cancel) { + _sfetch_cancel_item(item); + } + switch (item->state) { + case _SFETCH_STATE_DISPATCHED: + case _SFETCH_STATE_FETCHED: + item->state = _SFETCH_STATE_FETCHING; + break; + default: break; + } + } + + #if _SFETCH_HAS_THREADS + /* move new items into the IO threads and processed items out of IO threads */ + _sfetch_thread_enqueue_incoming(&chn->thread, &chn->thread_incoming, &chn->user_incoming); + _sfetch_thread_dequeue_outgoing(&chn->thread, &chn->thread_outgoing, &chn->user_outgoing); + #else + /* without threading just directly dequeue items from the user_incoming queue and + call the request handler, the user_outgoing queue will be filled as the + asynchronous HTTP requests sent by the request handler are completed + */ + while (!_sfetch_ring_empty(&chn->user_incoming)) { + uint32_t slot_id = _sfetch_ring_dequeue(&chn->user_incoming); + _sfetch_request_handler(chn->ctx, slot_id); + } + #endif + + /* drain the outgoing queue, prepare items for invoking the response + callback, and finally call the response callback, free finished items + */ + while (!_sfetch_ring_empty(&chn->user_outgoing)) { + const uint32_t slot_id = _sfetch_ring_dequeue(&chn->user_outgoing); + SOKOL_ASSERT(slot_id); + _sfetch_item_t* item = _sfetch_pool_item_lookup(pool, slot_id); + SOKOL_ASSERT(item && item->callback); + SOKOL_ASSERT(item->state != _SFETCH_STATE_INITIAL); + SOKOL_ASSERT(item->state != _SFETCH_STATE_ALLOCATED); + SOKOL_ASSERT(item->state != _SFETCH_STATE_DISPATCHED); + SOKOL_ASSERT(item->state != _SFETCH_STATE_FETCHED); + /* transfer output params from thread- to user-data */ + item->user.fetched_offset = item->thread.fetched_offset; + item->user.fetched_size = item->thread.fetched_size; + if (item->user.cancel) { + _sfetch_cancel_item(item); + } + else { + item->user.error_code = item->thread.error_code; + } + if (item->thread.finished) { + item->user.finished = true; + } + /* state transition */ + if (item->thread.failed) { + item->state = _SFETCH_STATE_FAILED; + } + else if (item->state == _SFETCH_STATE_FETCHING) { + item->state = _SFETCH_STATE_FETCHED; + } + _sfetch_invoke_response_callback(item); + + /* when the request is finished, free the lane for another request, + otherwise feed it back into the incoming queue + */ + if (item->user.finished) { + _sfetch_ring_enqueue(&chn->free_lanes, item->lane); + _sfetch_pool_item_free(pool, slot_id); + } + else { + _sfetch_ring_enqueue(&chn->user_incoming, slot_id); + } + } +} + +_SOKOL_PRIVATE bool _sfetch_validate_request(_sfetch_t* ctx, const sfetch_request_t* req) { + if (req->channel >= ctx->desc.num_channels) { + _SFETCH_ERROR(REQUEST_CHANNEL_INDEX_TOO_BIG); + return false; + } + if (!req->path) { + _SFETCH_ERROR(REQUEST_PATH_IS_NULL); + return false; + } + if (strlen(req->path) >= (SFETCH_MAX_PATH-1)) { + _SFETCH_ERROR(REQUEST_PATH_TOO_LONG); + return false; + } + if (!req->callback) { + _SFETCH_ERROR(REQUEST_CALLBACK_MISSING); + return false; + } + if (req->chunk_size > req->buffer.size) { + _SFETCH_ERROR(REQUEST_CHUNK_SIZE_GREATER_BUFFER_SIZE); + return false; + } + if (req->user_data.ptr && (req->user_data.size == 0)) { + _SFETCH_ERROR(REQUEST_USERDATA_PTR_IS_SET_BUT_USERDATA_SIZE_IS_NULL); + return false; + } + if (!req->user_data.ptr && (req->user_data.size > 0)) { + _SFETCH_ERROR(REQUEST_USERDATA_PTR_IS_NULL_BUT_USERDATA_SIZE_IS_NOT); + return false; + } + if (req->user_data.size > SFETCH_MAX_USERDATA_UINT64 * sizeof(uint64_t)) { + _SFETCH_ERROR(REQUEST_USERDATA_SIZE_TOO_BIG); + return false; + } + return true; +} + +_SOKOL_PRIVATE sfetch_desc_t _sfetch_desc_defaults(const sfetch_desc_t* desc) { + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + sfetch_desc_t res = *desc; + res.max_requests = _sfetch_def(desc->max_requests, 128); + res.num_channels = _sfetch_def(desc->num_channels, 1); + res.num_lanes = _sfetch_def(desc->num_lanes, 1); + return res; +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void sfetch_setup(const sfetch_desc_t* desc_) { + SOKOL_ASSERT(desc_); + SOKOL_ASSERT(0 == _sfetch); + + sfetch_desc_t desc = _sfetch_desc_defaults(desc_); + _sfetch = (_sfetch_t*) _sfetch_malloc_with_allocator(&desc.allocator, sizeof(_sfetch_t)); + SOKOL_ASSERT(_sfetch); + _sfetch_t* ctx = _sfetch_ctx(); + _sfetch_clear(ctx, sizeof(_sfetch_t)); + ctx->desc = desc; + ctx->setup = true; + ctx->valid = true; + + /* replace zero-init items with default values */ + if (ctx->desc.num_channels > SFETCH_MAX_CHANNELS) { + ctx->desc.num_channels = SFETCH_MAX_CHANNELS; + _SFETCH_WARN(CLAMPING_NUM_CHANNELS_TO_MAX_CHANNELS); + } + + /* setup the global request item pool */ + ctx->valid &= _sfetch_pool_init(&ctx->pool, ctx->desc.max_requests); + + /* setup IO channels (one thread per channel) */ + for (uint32_t i = 0; i < ctx->desc.num_channels; i++) { + ctx->valid &= _sfetch_channel_init(&ctx->chn[i], ctx, ctx->desc.max_requests, ctx->desc.num_lanes, _sfetch_request_handler); + } +} + +SOKOL_API_IMPL void sfetch_shutdown(void) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->setup); + ctx->valid = false; + /* IO threads must be shutdown first */ + for (uint32_t i = 0; i < ctx->desc.num_channels; i++) { + if (ctx->chn[i].valid) { + _sfetch_channel_discard(&ctx->chn[i]); + } + } + _sfetch_pool_discard(&ctx->pool); + ctx->setup = false; + _sfetch_free(ctx); + _sfetch = 0; +} + +SOKOL_API_IMPL bool sfetch_valid(void) { + _sfetch_t* ctx = _sfetch_ctx(); + return ctx && ctx->valid; +} + +SOKOL_API_IMPL sfetch_desc_t sfetch_desc(void) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + return ctx->desc; +} + +SOKOL_API_IMPL int sfetch_max_userdata_bytes(void) { + return SFETCH_MAX_USERDATA_UINT64 * 8; +} + +SOKOL_API_IMPL int sfetch_max_path(void) { + return SFETCH_MAX_PATH; +} + +SOKOL_API_IMPL bool sfetch_handle_valid(sfetch_handle_t h) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + /* shortcut invalid handle */ + if (h.id == 0) { + return false; + } + return 0 != _sfetch_pool_item_lookup(&ctx->pool, h.id); +} + +SOKOL_API_IMPL sfetch_handle_t sfetch_send(const sfetch_request_t* request) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->setup); + + const sfetch_handle_t invalid_handle = _sfetch_make_handle(0); + if (!ctx->valid) { + return invalid_handle; + } + if (!_sfetch_validate_request(ctx, request)) { + return invalid_handle; + } + SOKOL_ASSERT(request->channel < ctx->desc.num_channels); + + uint32_t slot_id = _sfetch_pool_item_alloc(&ctx->pool, request); + if (0 == slot_id) { + _SFETCH_WARN(REQUEST_POOL_EXHAUSTED); + return invalid_handle; + } + if (!_sfetch_channel_send(&ctx->chn[request->channel], slot_id)) { + /* send failed because the channels sent-queue overflowed */ + _sfetch_pool_item_free(&ctx->pool, slot_id); + return invalid_handle; + } + return _sfetch_make_handle(slot_id); +} + +SOKOL_API_IMPL void sfetch_dowork(void) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->setup); + if (!ctx->valid) { + return; + } + /* we're pumping each channel 2x so that unfinished request items coming out the + IO threads can be moved back into the IO-thread immediately without + having to wait a frame + */ + ctx->in_callback = true; + for (int pass = 0; pass < 2; pass++) { + for (uint32_t chn_index = 0; chn_index < ctx->desc.num_channels; chn_index++) { + _sfetch_channel_dowork(&ctx->chn[chn_index], &ctx->pool); + } + } + ctx->in_callback = false; +} + +SOKOL_API_IMPL void sfetch_bind_buffer(sfetch_handle_t h, sfetch_range_t buffer) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + SOKOL_ASSERT(ctx->in_callback); + SOKOL_ASSERT(buffer.ptr && (buffer.size > 0)); + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, h.id); + if (item) { + SOKOL_ASSERT((0 == item->buffer.ptr) && (0 == item->buffer.size)); + item->buffer = buffer; + } +} + +SOKOL_API_IMPL void* sfetch_unbind_buffer(sfetch_handle_t h) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + SOKOL_ASSERT(ctx->in_callback); + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, h.id); + if (item) { + void* prev_buf_ptr = (void*)item->buffer.ptr; + item->buffer.ptr = 0; + item->buffer.size = 0; + return prev_buf_ptr; + } + else { + return 0; + } +} + +SOKOL_API_IMPL void sfetch_pause(sfetch_handle_t h) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, h.id); + if (item) { + item->user.pause = true; + item->user.cont = false; + } +} + +SOKOL_API_IMPL void sfetch_continue(sfetch_handle_t h) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, h.id); + if (item) { + item->user.cont = true; + item->user.pause = false; + } +} + +SOKOL_API_IMPL void sfetch_cancel(sfetch_handle_t h) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, h.id); + if (item) { + item->user.cont = false; + item->user.pause = false; + item->user.cancel = true; + } +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif /* SOKOL_FETCH_IMPL */ diff --git a/thirdparty/sokol/sokol_gfx.h b/thirdparty/sokol/sokol_gfx.h new file mode 100644 index 0000000..58a9352 --- /dev/null +++ b/thirdparty/sokol/sokol_gfx.h @@ -0,0 +1,19454 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_GFX_IMPL) +#define SOKOL_GFX_IMPL +#endif +#ifndef SOKOL_GFX_INCLUDED +/* + sokol_gfx.h -- simple 3D API wrapper + + Project URL: https://github.com/floooh/sokol + + Example code: https://github.com/floooh/sokol-samples + + Do this: + #define SOKOL_IMPL or + #define SOKOL_GFX_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + In the same place define one of the following to select the rendering + backend: + #define SOKOL_GLCORE + #define SOKOL_GLES3 + #define SOKOL_D3D11 + #define SOKOL_METAL + #define SOKOL_WGPU + #define SOKOL_DUMMY_BACKEND + + I.e. for the desktop GL it should look like this: + + #include ... + #include ... + #define SOKOL_IMPL + #define SOKOL_GLCORE + #include "sokol_gfx.h" + + The dummy backend replaces the platform-specific backend code with empty + stub functions. This is useful for writing tests that need to run on the + command line. + + Optionally provide the following defines with your own implementations: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + SOKOL_GFX_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_GFX_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + SOKOL_TRACE_HOOKS - enable trace hook callbacks (search below for TRACE HOOKS) + SOKOL_EXTERNAL_GL_LOADER - indicates that you're using your own GL loader, in this case + sokol_gfx.h will not include any platform GL headers and disable + the integrated Win32 GL loader + + If sokol_gfx.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_GFX_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + If you want to compile without deprecated structs and functions, + define: + + SOKOL_NO_DEPRECATED + + Optionally define the following to force debug checks and validations + even in release mode: + + SOKOL_DEBUG - by default this is defined if _DEBUG is defined + + sokol_gfx DOES NOT: + =================== + - create a window, swapchain or the 3D-API context/device, you must do this + before sokol_gfx is initialized, and pass any required information + (like 3D device pointers) to the sokol_gfx initialization call + + - present the rendered frame, how this is done exactly usually depends + on how the window and 3D-API context/device was created + + - provide a unified shader language, instead 3D-API-specific shader + source-code or shader-bytecode must be provided (for the "official" + offline shader cross-compiler, see here: + https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md) + + + STEP BY STEP + ============ + --- to initialize sokol_gfx, after creating a window and a 3D-API + context/device, call: + + sg_setup(const sg_desc*) + + Depending on the selected 3D backend, sokol-gfx requires some + information, like a device pointer, default swapchain pixel formats + and so on. If you are using sokol_app.h for the window system + glue, you can use a helper function provided in the sokol_glue.h + header: + + #include "sokol_gfx.h" + #include "sokol_app.h" + #include "sokol_glue.h" + //... + sg_setup(&(sg_desc){ + .environment = sglue_environment(), + }); + + To get any logging output for errors and from the validation layer, you + need to provide a logging callback. Easiest way is through sokol_log.h: + + #include "sokol_log.h" + //... + sg_setup(&(sg_desc){ + //... + .logger.func = slog_func, + }); + + --- create resource objects (at least buffers, shaders and pipelines, + and optionally images, samplers and render-pass-attachments): + + sg_buffer sg_make_buffer(const sg_buffer_desc*) + sg_image sg_make_image(const sg_image_desc*) + sg_sampler sg_make_sampler(const sg_sampler_desc*) + sg_shader sg_make_shader(const sg_shader_desc*) + sg_pipeline sg_make_pipeline(const sg_pipeline_desc*) + sg_attachments sg_make_attachments(const sg_attachments_desc*) + + --- start a render pass: + + sg_begin_pass(const sg_pass* pass); + + Typically, passes render into an externally provided swapchain which + presents the rendering result on the display. Such a 'swapchain pass' + is started like this: + + sg_begin_pass(&(sg_pass){ .action = { ... }, .swapchain = sglue_swapchain() }) + + ...where .action is an sg_pass_action struct containing actions to be performed + at the start and end of a render pass (such as clearing the render surfaces to + a specific color), and .swapchain is an sg_swapchain + struct all the required information to render into the swapchain's surfaces. + + To start an 'offscreen pass' into sokol-gfx image objects, an sg_attachment + object handle is required instead of an sg_swapchain struct. An offscreen + pass is started like this (assuming attachments is an sg_attachments handle): + + sg_begin_pass(&(sg_pass){ .action = { ... }, .attachments = attachments }); + + --- set the render pipeline state for the next draw call with: + + sg_apply_pipeline(sg_pipeline pip) + + --- fill an sg_bindings struct with the resource bindings for the next + draw call (0..N vertex buffers, 0 or 1 index buffer, 0..N image-objects, + samplers and storage-buffers), and call: + + sg_apply_bindings(const sg_bindings* bindings) + + to update the resource bindings + + --- optionally update shader uniform data with: + + sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data) + + Read the section 'UNIFORM DATA LAYOUT' to learn about the expected memory layout + of the uniform data passed into sg_apply_uniforms(). + + --- kick off a draw call with: + + sg_draw(int base_element, int num_elements, int num_instances) + + The sg_draw() function unifies all the different ways to render primitives + in a single call (indexed vs non-indexed rendering, and instanced vs non-instanced + rendering). In case of indexed rendering, base_element and num_element specify + indices in the currently bound index buffer. In case of non-indexed rendering + base_element and num_elements specify vertices in the currently bound + vertex-buffer(s). To perform instanced rendering, the rendering pipeline + must be setup for instancing (see sg_pipeline_desc below), a separate vertex buffer + containing per-instance data must be bound, and the num_instances parameter + must be > 1. + + --- finish the current rendering pass with: + + sg_end_pass() + + --- when done with the current frame, call + + sg_commit() + + --- at the end of your program, shutdown sokol_gfx with: + + sg_shutdown() + + --- if you need to destroy resources before sg_shutdown(), call: + + sg_destroy_buffer(sg_buffer buf) + sg_destroy_image(sg_image img) + sg_destroy_sampler(sg_sampler smp) + sg_destroy_shader(sg_shader shd) + sg_destroy_pipeline(sg_pipeline pip) + sg_destroy_attachments(sg_attachments atts) + + --- to set a new viewport rectangle, call + + sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) + + ...or if you want to specify the viewport rectangle with float values: + + sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left) + + --- to set a new scissor rect, call: + + sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) + + ...or with float values: + + sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left) + + Both sg_apply_viewport() and sg_apply_scissor_rect() must be called + inside a rendering pass + + Note that sg_begin_default_pass() and sg_begin_pass() will reset both the + viewport and scissor rectangles to cover the entire framebuffer. + + --- to update (overwrite) the content of buffer and image resources, call: + + sg_update_buffer(sg_buffer buf, const sg_range* data) + sg_update_image(sg_image img, const sg_image_data* data) + + Buffers and images to be updated must have been created with + SG_USAGE_DYNAMIC or SG_USAGE_STREAM + + Only one update per frame is allowed for buffer and image resources when + using the sg_update_*() functions. The rationale is to have a simple + countermeasure to avoid the CPU scribbling over data the GPU is currently + using, or the CPU having to wait for the GPU + + Buffer and image updates can be partial, as long as a rendering + operation only references the valid (updated) data in the + buffer or image. + + --- to append a chunk of data to a buffer resource, call: + + int sg_append_buffer(sg_buffer buf, const sg_range* data) + + The difference to sg_update_buffer() is that sg_append_buffer() + can be called multiple times per frame to append new data to the + buffer piece by piece, optionally interleaved with draw calls referencing + the previously written data. + + sg_append_buffer() returns a byte offset to the start of the + written data, this offset can be assigned to + sg_bindings.vertex_buffer_offsets[n] or + sg_bindings.index_buffer_offset + + Code example: + + for (...) { + const void* data = ...; + const int num_bytes = ...; + int offset = sg_append_buffer(buf, &(sg_range) { .ptr=data, .size=num_bytes }); + bindings.vertex_buffer_offsets[0] = offset; + sg_apply_pipeline(pip); + sg_apply_bindings(&bindings); + sg_apply_uniforms(...); + sg_draw(...); + } + + A buffer to be used with sg_append_buffer() must have been created + with SG_USAGE_DYNAMIC or SG_USAGE_STREAM. + + If the application appends more data to the buffer then fits into + the buffer, the buffer will go into the "overflow" state for the + rest of the frame. + + Any draw calls attempting to render an overflown buffer will be + silently dropped (in debug mode this will also result in a + validation error). + + You can also check manually if a buffer is in overflow-state by calling + + bool sg_query_buffer_overflow(sg_buffer buf) + + You can manually check to see if an overflow would occur before adding + any data to a buffer by calling + + bool sg_query_buffer_will_overflow(sg_buffer buf, size_t size) + + NOTE: Due to restrictions in underlying 3D-APIs, appended chunks of + data will be 4-byte aligned in the destination buffer. This means + that there will be gaps in index buffers containing 16-bit indices + when the number of indices in a call to sg_append_buffer() is + odd. This isn't a problem when each call to sg_append_buffer() + is associated with one draw call, but will be problematic when + a single indexed draw call spans several appended chunks of indices. + + --- to check at runtime for optional features, limits and pixelformat support, + call: + + sg_features sg_query_features() + sg_limits sg_query_limits() + sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt) + + --- if you need to call into the underlying 3D-API directly, you must call: + + sg_reset_state_cache() + + ...before calling sokol_gfx functions again + + --- you can inspect the original sg_desc structure handed to sg_setup() + by calling sg_query_desc(). This will return an sg_desc struct with + the default values patched in instead of any zero-initialized values + + --- you can get a desc struct matching the creation attributes of a + specific resource object via: + + sg_buffer_desc sg_query_buffer_desc(sg_buffer buf) + sg_image_desc sg_query_image_desc(sg_image img) + sg_sampler_desc sg_query_sampler_desc(sg_sampler smp) + sg_shader_desc sq_query_shader_desc(sg_shader shd) + sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip) + sg_attachments_desc sg_query_attachments_desc(sg_attachments atts) + + ...but NOTE that the returned desc structs may be incomplete, only + creation attributes that are kept around internally after resource + creation will be filled in, and in some cases (like shaders) that's + very little. Any missing attributes will be set to zero. The returned + desc structs might still be useful as partial blueprint for creating + similar resources if filled up with the missing attributes. + + Calling the query-desc functions on an invalid resource will return + completely zeroed structs (it makes sense to check the resource state + with sg_query_*_state() first) + + --- you can query the default resource creation parameters through the functions + + sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) + sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) + sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc) + sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) + sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc) + sg_attachments_desc sg_query_attachments_defaults(const sg_attachments_desc* desc) + + These functions take a pointer to a desc structure which may contain + zero-initialized items for default values. These zero-init values + will be replaced with their concrete values in the returned desc + struct. + + --- you can inspect various internal resource runtime values via: + + sg_buffer_info sg_query_buffer_info(sg_buffer buf) + sg_image_info sg_query_image_info(sg_image img) + sg_sampler_info sg_query_sampler_info(sg_sampler smp) + sg_shader_info sg_query_shader_info(sg_shader shd) + sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip) + sg_attachments_info sg_query_attachments_info(sg_attachments atts) + + ...please note that the returned info-structs are tied quite closely + to sokol_gfx.h internals, and may change more often than other + public API functions and structs. + + --- you can query frame stats and control stats collection via: + + sg_query_frame_stats() + sg_enable_frame_stats() + sg_disable_frame_stats() + sg_frame_stats_enabled() + + --- you can ask at runtime what backend sokol_gfx.h has been compiled for: + + sg_backend sg_query_backend(void) + + --- call the following helper functions to compute the number of + bytes in a texture row or surface for a specific pixel format. + These functions might be helpful when preparing image data for consumption + by sg_make_image() or sg_update_image(): + + int sg_query_row_pitch(sg_pixel_format fmt, int width, int int row_align_bytes); + int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes); + + Width and height are generally in number pixels, but note that 'row' has different meaning + for uncompressed vs compressed pixel formats: for uncompressed formats, a row is identical + with a single line if pixels, while in compressed formats, one row is a line of *compression blocks*. + + This is why calling sg_query_surface_pitch() for a compressed pixel format and height + N, N+1, N+2, ... may return the same result. + + The row_align_bytes parammeter is for added flexibility. For image data that goes into + the sg_make_image() or sg_update_image() this should generally be 1, because these + functions take tightly packed image data as input no matter what alignment restrictions + exist in the backend 3D APIs. + + ON INITIALIZATION: + ================== + When calling sg_setup(), a pointer to an sg_desc struct must be provided + which contains initialization options. These options provide two types + of information to sokol-gfx: + + (1) upper bounds and limits needed to allocate various internal + data structures: + - the max number of resources of each type that can + be alive at the same time, this is used for allocating + internal pools + - the max overall size of uniform data that can be + updated per frame, including a worst-case alignment + per uniform update (this worst-case alignment is 256 bytes) + - the max size of all dynamic resource updates (sg_update_buffer, + sg_append_buffer and sg_update_image) per frame + Not all of those limit values are used by all backends, but it is + good practice to provide them none-the-less. + + (2) 3D backend "environment information" in a nested sg_environment struct: + - pointers to backend-specific context- or device-objects (for instance + the D3D11, WebGPU or Metal device objects) + - defaults for external swapchain pixel formats and sample counts, + these will be used as default values in image and pipeline objects, + and the sg_swapchain struct passed into sg_begin_pass() + Usually you provide a complete sg_environment struct through + a helper function, as an example look at the sglue_environment() + function in the sokol_glue.h header. + + See the documentation block of the sg_desc struct below for more information. + + + ON RENDER PASSES + ================ + Relevant samples: + - https://floooh.github.io/sokol-html5/offscreen-sapp.html + - https://floooh.github.io/sokol-html5/offscreen-msaa-sapp.html + - https://floooh.github.io/sokol-html5/mrt-sapp.html + - https://floooh.github.io/sokol-html5/mrt-pixelformats-sapp.html + + A render pass groups rendering commands into a set of render target images + (called 'pass attachments'). Render target images can be used in subsequent + passes as textures (it is invalid to use the same image both as render target + and as texture in the same pass). + + The following sokol-gfx functions must only be called inside a render pass: + + sg_apply_viewport(f) + sg_apply_scissor_rect(f) + sg_apply_pipeline + sg_apply_bindings + sg_apply_uniforms + sg_draw + + A frame must have at least one 'swapchain render pass' which renders into an + externally provided swapchain provided as an sg_swapchain struct to the + sg_begin_pass() function. The sg_swapchain struct must contain the + following information: + + - the color pixel-format of the swapchain's render surface + - an optional depth/stencil pixel format if the swapchain + has a depth/stencil buffer + - an optional sample-count for MSAA rendering + - NOTE: the above three values can be zero-initialized, in that + case the defaults from the sg_environment struct will be used that + had been passed to the sg_setup() function. + - a number of backend specific objects: + - GL/GLES3: just a GL framebuffer handle + - D3D11: + - an ID3D11RenderTargetView for the rendering surface + - if MSAA is used, an ID3D11RenderTargetView as + MSAA resolve-target + - an optional ID3D11DepthStencilView for the + depth/stencil buffer + - WebGPU + - a WGPUTextureView object for the rendering surface + - if MSAA is used, a WGPUTextureView object as MSAA resolve target + - an optional WGPUTextureView for the + - Metal (NOTE that the roles of provided surfaces is slightly + different in Metal than in D3D11 or WebGPU, notably, the + CAMetalDrawable is either rendered to directly, or serves + as MSAA resolve target): + - a CAMetalDrawable object which is either rendered + into directly, or in case of MSAA rendering, serves + as MSAA-resolve-target + - if MSAA is used, an multisampled MTLTexture where + rendering goes into + - an optional MTLTexture for the depth/stencil buffer + + It's recommended that you create a helper function which returns an + initialized sg_swapchain struct by value. This can then be directly plugged + into the sg_begin_pass function like this: + + sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain() }); + + As an example for such a helper function check out the function sglue_swapchain() + in the sokol_glue.h header. + + For offscreen render passes, the render target images used in a render pass + are baked into an immutable sg_attachments object. + + For a simple offscreen scenario with one color-, one depth-stencil-render + target and without multisampling, creating an attachment object looks like this: + + First create two render target images, one with a color pixel format, + and one with the depth- or depth-stencil pixel format. Both images + must have the same dimensions: + + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .sample_count = 1, + }); + const sg_image depth_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_DEPTH, + .sample_count = 1, + }); + + NOTE: when creating render target images, have in mind that some default values + are aligned with the default environment attributes in the sg_environment struct + that was passed into the sg_setup() call: + + - the default value for sg_image_desc.pixel_format is taken from + sg_environment.defaults.color_format + - the default value for sg_image_desc.sample_count is taken from + sg_environment.defaults.sample_count + - the default value for sg_image_desc.num_mipmaps is always 1 + + Next create an attachments object: + + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0].image = color_img, + .depth_stencil.image = depth_img, + }); + + This attachments object is then passed into the sg_begin_pass() function + in place of the swapchain struct: + + sg_begin_pass(&(sg_pass){ .attachments = atts }); + + Swapchain and offscreen passes form dependency trees each with a swapchain + pass at the root, offscreen passes as nodes, and render target images as + dependencies between passes. + + sg_pass_action structs are used to define actions that should happen at the + start and end of rendering passes (such as clearing pass attachments to a + specific color or depth-value, or performing an MSAA resolve operation at + the end of a pass). + + A typical sg_pass_action object which clears the color attachment to black + might look like this: + + const sg_pass_action = { + .colors[0] = { + .load_action = SG_LOADACTION_CLEAR, + .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f } + } + }; + + This omits the defaults for the color attachment store action, and + the depth-stencil-attachments actions. The same pass action with the + defaults explicitly filled in would look like this: + + const sg_pass_action pass_action = { + .colors[0] = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_STORE, + .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f } + }, + .depth = = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_DONTCARE, + .clear_value = 1.0f, + }, + .stencil = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_DONTCARE, + .clear_value = 0 + } + }; + + With the sg_pass object and sg_pass_action struct in place everything + is ready now for the actual render pass: + + Using such this prepared sg_pass_action in a swapchain pass looks like + this: + + sg_begin_pass(&(sg_pass){ + .action = pass_action, + .swapchain = sglue_swapchain() + }); + ... + sg_end_pass(); + + ...of alternatively in one offscreen pass: + + sg_begin_pass(&(sg_pass){ + .action = pass_action, + .attachments = attachments, + }); + ... + sg_end_pass(); + + Offscreen rendering can also go into a mipmap, or a slice/face of + a cube-, array- or 3d-image (which some restrictions, for instance + it's not possible to create a 3D image with a depth/stencil pixel format, + these exceptions are generally caught by the sokol-gfx validation layer). + + The mipmap/slice selection happens at attachments creation time, for instance + to render into mipmap 2 of slice 3 of an array texture: + + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { + .image = color_img, + .mip_level = 2, + .slice = 3, + }, + .depth_stencil.image = depth_img, + }); + + If MSAA offscreen rendering is desired, the multi-sample rendering result + must be 'resolved' into a separate 'resolve image', before that image can + be used as texture. + + NOTE: currently multisample-images cannot be bound as textures. + + Creating a simple attachments object for multisampled rendering requires + 3 attachment images: the color attachment image which has a sample + count > 1, a resolve attachment image of the same size and pixel format + but a sample count == 1, and a depth/stencil attachment image with + the same size and sample count as the color attachment image: + + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .sample_count = 4, + }); + const sg_image resolve_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .sample_count = 1, + }); + const sg_image depth_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_DEPTH, + .sample_count = 4, + }); + + ...create the attachments object: + + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0].image = color_img, + .resolves[0].image = resolve_img, + .depth_stencil.image = depth_img, + }); + + If an attachments object defines a resolve image in a specific resolve attachment slot, + an 'msaa resolve operation' will happen in sg_end_pass(). + + In this scenario, the content of the MSAA color attachment doesn't need to be + preserved (since it's only needed inside sg_end_pass for the msaa-resolve), so + the .store_action should be set to "don't care": + + const sg_pass_action = { + .colors[0] = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_DONTCARE, + .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f } + } + }; + + The actual render pass looks as usual: + + sg_begin_pass(&(sg_pass){ .action = pass_action, .attachments = atts }); + ... + sg_end_pass(); + + ...after sg_end_pass() the only difference to the non-msaa scenario is that the + rendering result which is going to be used as texture in a followup pass is + in 'resolve_img', not in 'color_img' (in fact, trying to bind color_img as a + texture would result in a validation error). + + + ON SHADER CREATION + ================== + sokol-gfx doesn't come with an integrated shader cross-compiler, instead + backend-specific shader sources or binary blobs need to be provided when + creating a shader object, along with information about the shader resource + binding interface needed in the sokol-gfx validation layer and to properly + bind shader resources on the CPU-side to be consumable by the GPU-side. + + The easiest way to provide all this shader creation data is to use the + sokol-shdc shader compiler tool to compile shaders from a common + GLSL syntax into backend-specific sources or binary blobs, along with + shader interface information and uniform blocks mapped to C structs. + + To create a shader using a C header which has been code-generated by sokol-shdc: + + // include the C header code-generated by sokol-shdc: + #include "myshader.glsl.h" + ... + + // create shader using a code-generated helper function from the C header: + sg_shader shd = sg_make_shader(myshader_shader_desc(sg_query_backend())); + + The samples in the 'sapp' subdirectory of the sokol-samples project + also use the sokol-shdc approach: + + https://github.com/floooh/sokol-samples/tree/master/sapp + + If you're planning to use sokol-shdc, you can stop reading here, instead + continue with the sokol-shdc documentation: + + https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md + + To create shaders with backend-specific shader code or binary blobs, + the sg_make_shader() function requires the following information: + + - Shader code or shader binary blobs for the vertex- and fragment- shader-stage: + - for the desktop GL backend, source code can be provided in '#version 410' or + '#version 430', version 430 is required for storage buffer support, but note + that this is not available on macOS + - for the GLES3 backend, source code must be provided in '#version 300 es' syntax + - for the D3D11 backend, shaders can be provided as source or binary blobs, the + source code should be in HLSL4.0 (for best compatibility) or alternatively + in HLSL5.0 syntax (other versions may work but are not tested), NOTE: when + shader source code is provided for the D3D11 backend, sokol-gfx will dynamically + load 'd3dcompiler_47.dll' + - for the Metal backends, shaders can be provided as source or binary blobs, the + MSL version should be in 'metal-1.1' (other versions may work but are not tested) + - for the WebGPU backend, shader must be provided as WGSL source code + - optionally the following shader-code related attributes can be provided: + - an entry function name (only on D3D11 or Metal, but not OpenGL) + - on D3D11 only, a compilation target (default is "vs_4_0" and "ps_4_0") + + - Depending on backend, information about the input vertex attributes used by the + vertex shader: + - Metal: no information needed since vertex attributes are always bound + by their attribute location defined in the shader via '[[attribute(N)]]' + - WebGPU: no information needed since vertex attributes are always + bound by their attribute location defined in the shader via `@location(N)` + - GLSL: vertex attribute names can be optionally provided, in that case their + location will be looked up by name, otherwise, the vertex attribute location + can be defined with 'layout(location = N)', PLEASE NOTE that the name-lookup method + may be removed at some point + - D3D11: a 'semantic name' and 'semantic index' must be provided for each vertex + attribute, e.g. if the vertex attribute is defined as 'TEXCOORD1' in the shader, + the semantic name would be 'TEXCOORD', and the semantic index would be '1' + + - Information about each uniform block used in the shader: + - The size of the uniform block in number of bytes. + - A memory layout hint (currently 'native' or 'std140') where 'native' defines a + backend-specific memory layout which shouldn't be used for cross-platform code. + Only std140 guarantees a backend-agnostic memory layout. + - For GLSL only: a description of the internal uniform block layout, which maps + member types and their offsets on the CPU side to uniform variable names + in the GLSL shader + - please also NOTE the documentation sections about UNIFORM DATA LAYOUT + and CROSS-BACKEND COMMON UNIFORM DATA LAYOUT below! + + - A description of each storage buffer used in the shader: + - a boolean 'readonly' flag, note that currently only + readonly storage buffers are supported + - note that storage buffers are not supported on all backends + and platforms + + - A description of each texture/image used in the shader: + - the expected image type: + - SG_IMAGETYPE_2D + - SG_IMAGETYPE_CUBE + - SG_IMAGETYPE_3D + - SG_IMAGETYPE_ARRAY + - the expected 'image sample type': + - SG_IMAGESAMPLETYPE_FLOAT + - SG_IMAGESAMPLETYPE_DEPTH + - SG_IMAGESAMPLETYPE_SINT + - SG_IMAGESAMPLETYPE_UINT + - SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT + - a flag whether the texture is expected to be multisampled + (currently it's not supported to fetch data from multisampled + textures in shaders, but this is planned for a later time) + + - A description of each texture sampler used in the shader: + - SG_SAMPLERTYPE_FILTERING, + - SG_SAMPLERTYPE_NONFILTERING, + - SG_SAMPLERTYPE_COMPARISON, + + - An array of 'image-sampler-pairs' used by the shader to sample textures, + for D3D11, Metal and WebGPU this is used for validation purposes to check + whether the texture and sampler are compatible with each other (especially + WebGPU is very picky about combining the correct + texture-sample-type with the correct sampler-type). For GLSL an + additional 'combined-image-sampler name' must be provided because 'OpenGL + style GLSL' cannot handle separate texture and sampler objects, but still + groups them into a traditional GLSL 'sampler object'. + + Compatibility rules for image-sample-type vs sampler-type are as follows: + + - SG_IMAGESAMPLETYPE_FLOAT => (SG_SAMPLERTYPE_FILTERING or SG_SAMPLERTYPE_NONFILTERING) + - SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT => SG_SAMPLERTYPE_NONFILTERING + - SG_IMAGESAMPLETYPE_SINT => SG_SAMPLERTYPE_NONFILTERING + - SG_IMAGESAMPLETYPE_UINT => SG_SAMPLERTYPE_NONFILTERING + - SG_IMAGESAMPLETYPE_DEPTH => SG_SAMPLERTYPE_COMPARISON + + For example code of how to create backend-specific shader objects, + please refer to the following samples: + + - for D3D11: https://github.com/floooh/sokol-samples/tree/master/d3d11 + - for Metal: https://github.com/floooh/sokol-samples/tree/master/metal + - for OpenGL: https://github.com/floooh/sokol-samples/tree/master/glfw + - for GLES3: https://github.com/floooh/sokol-samples/tree/master/html5 + - for WebGPI: https://github.com/floooh/sokol-samples/tree/master/wgpu + + + ON SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT AND SG_SAMPLERTYPE_NONFILTERING + ======================================================================== + The WebGPU backend introduces the concept of 'unfilterable-float' textures, + which can only be combined with 'nonfiltering' samplers (this is a restriction + specific to WebGPU, but since the same sokol-gfx code should work across + all backend, the sokol-gfx validation layer also enforces this restriction + - the alternative would be undefined behaviour in some backend APIs on + some devices). + + The background is that some mobile devices (most notably iOS devices) can + not perform linear filtering when sampling textures with certain pixel + formats, most notable the 32F formats: + + - SG_PIXELFORMAT_R32F + - SG_PIXELFORMAT_RG32F + - SG_PIXELFORMAT_RGBA32F + + The information of whether a shader is going to be used with such an + unfilterable-float texture must already be provided in the sg_shader_desc + struct when creating the shader (see the above section "ON SHADER CREATION"). + + If you are using the sokol-shdc shader compiler, the information whether a + texture/sampler binding expects an 'unfilterable-float/nonfiltering' + texture/sampler combination cannot be inferred from the shader source + alone, you'll need to provide this hint via annotation-tags. For instance + here is an example from the ozz-skin-sapp.c sample shader which samples an + RGBA32F texture with skinning matrices in the vertex shader: + + ```glsl + @image_sample_type joint_tex unfilterable_float + uniform texture2D joint_tex; + @sampler_type smp nonfiltering + uniform sampler smp; + ``` + + This will result in SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT and + SG_SAMPLERTYPE_NONFILTERING being written to the code-generated + sg_shader_desc struct. + + + UNIFORM DATA LAYOUT: + ==================== + NOTE: if you use the sokol-shdc shader compiler tool, you don't need to worry + about the following details. + + The data that's passed into the sg_apply_uniforms() function must adhere to + specific layout rules so that the GPU shader finds the uniform block + items at the right offset. + + For the D3D11 and Metal backends, sokol-gfx only cares about the size of uniform + blocks, but not about the internal layout. The data will just be copied into + a uniform/constant buffer in a single operation and it's up you to arrange the + CPU-side layout so that it matches the GPU side layout. This also means that with + the D3D11 and Metal backends you are not limited to a 'cross-platform' subset + of uniform variable types. + + If you ever only use one of the D3D11, Metal *or* WebGPU backend, you can stop reading here. + + For the GL backends, the internal layout of uniform blocks matters though, + and you are limited to a small number of uniform variable types. This is + because sokol-gfx must be able to locate the uniform block members in order + to upload them to the GPU with glUniformXXX() calls. + + To describe the uniform block layout to sokol-gfx, the following information + must be passed to the sg_make_shader() call in the sg_shader_desc struct: + + - a hint about the used packing rule (either SG_UNIFORMLAYOUT_NATIVE or + SG_UNIFORMLAYOUT_STD140) + - a list of the uniform block members types in the correct order they + appear on the CPU side + + For example if the GLSL shader has the following uniform declarations: + + uniform mat4 mvp; + uniform vec2 offset0; + uniform vec2 offset1; + uniform vec2 offset2; + + ...and on the CPU side, there's a similar C struct: + + typedef struct { + float mvp[16]; + float offset0[2]; + float offset1[2]; + float offset2[2]; + } params_t; + + ...the uniform block description in the sg_shader_desc must look like this: + + sg_shader_desc desc = { + .vs.uniform_blocks[0] = { + .size = sizeof(params_t), + .layout = SG_UNIFORMLAYOUT_NATIVE, // this is the default and can be omitted + .uniforms = { + // order must be the same as in 'params_t': + [0] = { .name = "mvp", .type = SG_UNIFORMTYPE_MAT4 }, + [1] = { .name = "offset0", .type = SG_UNIFORMTYPE_VEC2 }, + [2] = { .name = "offset1", .type = SG_UNIFORMTYPE_VEC2 }, + [3] = { .name = "offset2", .type = SG_UNIFORMTYPE_VEC2 }, + } + } + }; + + With this information sokol-gfx can now compute the correct offsets of the data items + within the uniform block struct. + + The SG_UNIFORMLAYOUT_NATIVE packing rule works fine if only the GL backends are used, + but for proper D3D11/Metal/GL a subset of the std140 layout must be used which is + described in the next section: + + + CROSS-BACKEND COMMON UNIFORM DATA LAYOUT + ======================================== + For cross-platform / cross-3D-backend code it is important that the same uniform block + layout on the CPU side can be used for all sokol-gfx backends. To achieve this, + a common subset of the std140 layout must be used: + + - The uniform block layout hint in sg_shader_desc must be explicitly set to + SG_UNIFORMLAYOUT_STD140. + - Only the following GLSL uniform types can be used (with their associated sokol-gfx enums): + - float => SG_UNIFORMTYPE_FLOAT + - vec2 => SG_UNIFORMTYPE_FLOAT2 + - vec3 => SG_UNIFORMTYPE_FLOAT3 + - vec4 => SG_UNIFORMTYPE_FLOAT4 + - int => SG_UNIFORMTYPE_INT + - ivec2 => SG_UNIFORMTYPE_INT2 + - ivec3 => SG_UNIFORMTYPE_INT3 + - ivec4 => SG_UNIFORMTYPE_INT4 + - mat4 => SG_UNIFORMTYPE_MAT4 + - Alignment for those types must be as follows (in bytes): + - float => 4 + - vec2 => 8 + - vec3 => 16 + - vec4 => 16 + - int => 4 + - ivec2 => 8 + - ivec3 => 16 + - ivec4 => 16 + - mat4 => 16 + - Arrays are only allowed for the following types: vec4, int4, mat4. + + Note that the HLSL cbuffer layout rules are slightly different from the + std140 layout rules, this means that the cbuffer declarations in HLSL code + must be tweaked so that the layout is compatible with std140. + + The by far easiest way to tackle the common uniform block layout problem is + to use the sokol-shdc shader cross-compiler tool! + + ON STORAGE BUFFERS + ================== + Storage buffers can be used to pass large amounts of random access structured + data fromt the CPU side to the shaders. They are similar to data textures, but are + more convenient to use both on the CPU and shader side since they can be accessed + in shaders as as a 1-dimensional array of struct items. + + Storage buffers are *NOT* supported on the following platform/backend combos: + + - macOS+GL (because storage buffers require GL 4.3, while macOS only goes up to GL 4.1) + - all GLES3 platforms (WebGL2, iOS, Android - with the option that support on + Android may be added at a later point) + + Currently only 'readonly' storage buffers are supported (meaning it's not possible + to write to storage buffers from shaders). + + To use storage buffers, the following steps are required: + + - write a shader which uses storage buffers (also see the example links below) + - create one or more storage buffers via sg_make_buffer() with the + buffer type SG_BUFFERTYPE_STORAGEBUFFER + - when creating a shader via sg_make_shader(), populate the sg_shader_desc + struct with binding info (when using sokol-shdc, this step will be taken care + of automatically) + - which storage buffer bind slots on the vertex- and fragment-stage + are occupied + - whether the storage buffer on that bind slot is readonly (this is currently required + to be true) + - when calling sg_apply_bindings(), apply the matching bind slots with the previously + created storage buffers + - ...and that's it. + + For more details, see the following backend-agnostic sokol samples: + + - simple vertex pulling from a storage buffer: + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/vertexpull-sapp.c + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/vertexpull-sapp.glsl + - instanced rendering via storage buffers (vertex- and instance-pulling): + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-pull-sapp.c + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-pull-sapp.glsl + - storage buffers both on the vertex- and fragment-stage: + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/sbuftex-sapp.c + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/sbuftex-sapp.glsl + - the Ozz animation sample rewritten to pull all rendering data from storage buffers: + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/ozz-storagebuffer-sapp.cc + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/ozz-storagebuffer-sapp.glsl + + ...also see the following backend-specific vertex pulling samples (those also don't use sokol-shdc): + + - D3D11: https://github.com/floooh/sokol-samples/blob/master/d3d11/vertexpulling-d3d11.c + - desktop GL: https://github.com/floooh/sokol-samples/blob/master/glfw/vertexpulling-glfw.c + - Metal: https://github.com/floooh/sokol-samples/blob/master/metal/vertexpulling-metal.c + - WebGPU: https://github.com/floooh/sokol-samples/blob/master/wgpu/vertexpulling-wgpu.c + + Storage buffer shader authoring caveats when using sokol-shdc: + + - declare a storage buffer interface block with `readonly buffer [name] { ... }` + - do NOT annotate storage buffers with `layout(...)`, sokol-shdc will take care of that + - declare a struct which describes a single array item in the storage buffer interface block + - only put a single flexible array member into the storage buffer interface block + + E.g. a complete example in 'sokol-shdc GLSL': + + ```glsl + // declare a struct: + struct sb_vertex { + vec3 pos; + vec4 color; + } + // declare a buffer interface block with a single flexible struct array: + readonly buffer vertices { + sb_vertex vtx[]; + } + // in the shader function, access the storage buffer like this: + void main() { + vec3 pos = vtx[gl_VertexIndex].pos; + ... + } + ``` + + Backend-specific storage-buffer caveats (not relevant when using sokol-shdc): + + D3D11: + - storage buffers are created as 'raw' Byte Address Buffers + (https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-intro#raw-views-of-buffers) + - in HLSL, use a ByteAddressBuffer to access the buffer content + (https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-byteaddressbuffer) + - in D3D11, storage buffers and textures share the same bind slots, sokol-gfx reserves + shader resource slots 0..15 for textures and 16..23 for storage buffers. + - e.g. in HLSL, storage buffer bindings start at register(t16) no matter the shader stage + + Metal: + - in Metal there is no internal difference between vertex-, uniform- and + storage-buffers, all are bound to the same 'buffer bind slots' with the + following reserved ranges: + - vertex shader stage: + - uniform buffers (internal): slots 0..3 + - vertex buffers: slots 4..11 + - storage buffers: slots 12..19 + - fragment shader stage: + - uniform buffers (internal): slots 0..3 + - storage buffers: slots 4..11 + - this means in MSL, storage buffer bindings start at [[buffer(12)]] in the vertex + shaders, and at [[buffer(4)]] in fragment shaders + + GL: + - the GL backend doesn't use name-lookup to find storage buffer bindings, this + means you must annotate buffers with `layout(std430, binding=N)` in GLSL + - ...where N is 0..7 in the vertex shader, and 8..15 in the fragment shader + + WebGPU: + - in WGSL, use the following bind locations for the various shader resource types: + - vertex shader stage: + - textures `@group(1) @binding(0..15)` + - samplers `@group(1) @binding(16..31)` + - storage buffers `@group(1) @binding(32..47)` + - fragment shader stage: + - textures `@group(1) @binding(48..63)` + - samplers `@group(1) @binding(64..79)` + - storage buffers `@group(1) @binding(80..95)` + + TRACE HOOKS: + ============ + sokol_gfx.h optionally allows to install "trace hook" callbacks for + each public API functions. When a public API function is called, and + a trace hook callback has been installed for this function, the + callback will be invoked with the parameters and result of the function. + This is useful for things like debugging- and profiling-tools, or + keeping track of resource creation and destruction. + + To use the trace hook feature: + + --- Define SOKOL_TRACE_HOOKS before including the implementation. + + --- Setup an sg_trace_hooks structure with your callback function + pointers (keep all function pointers you're not interested + in zero-initialized), optionally set the user_data member + in the sg_trace_hooks struct. + + --- Install the trace hooks by calling sg_install_trace_hooks(), + the return value of this function is another sg_trace_hooks + struct which contains the previously set of trace hooks. + You should keep this struct around, and call those previous + functions pointers from your own trace callbacks for proper + chaining. + + As an example of how trace hooks are used, have a look at the + imgui/sokol_gfx_imgui.h header which implements a realtime + debugging UI for sokol_gfx.h on top of Dear ImGui. + + + A NOTE ON PORTABLE PACKED VERTEX FORMATS: + ========================================= + There are two things to consider when using packed + vertex formats like UBYTE4, SHORT2, etc which need to work + across all backends: + + - D3D11 can only convert *normalized* vertex formats to + floating point during vertex fetch, normalized formats + have a trailing 'N', and are "normalized" to a range + -1.0..+1.0 (for the signed formats) or 0.0..1.0 (for the + unsigned formats): + + - SG_VERTEXFORMAT_BYTE4N + - SG_VERTEXFORMAT_UBYTE4N + - SG_VERTEXFORMAT_SHORT2N + - SG_VERTEXFORMAT_USHORT2N + - SG_VERTEXFORMAT_SHORT4N + - SG_VERTEXFORMAT_USHORT4N + + D3D11 will not convert *non-normalized* vertex formats to floating point + vertex shader inputs, those can only be uses with the *ivecn* vertex shader + input types when D3D11 is used as backend (GL and Metal can use both formats) + + - SG_VERTEXFORMAT_BYTE4, + - SG_VERTEXFORMAT_UBYTE4 + - SG_VERTEXFORMAT_SHORT2 + - SG_VERTEXFORMAT_SHORT4 + + For a vertex input layout which works on all platforms, only use the following + vertex formats, and if needed "expand" the normalized vertex shader + inputs in the vertex shader by multiplying with 127.0, 255.0, 32767.0 or + 65535.0: + + - SG_VERTEXFORMAT_FLOAT, + - SG_VERTEXFORMAT_FLOAT2, + - SG_VERTEXFORMAT_FLOAT3, + - SG_VERTEXFORMAT_FLOAT4, + - SG_VERTEXFORMAT_BYTE4N, + - SG_VERTEXFORMAT_UBYTE4N, + - SG_VERTEXFORMAT_SHORT2N, + - SG_VERTEXFORMAT_USHORT2N + - SG_VERTEXFORMAT_SHORT4N, + - SG_VERTEXFORMAT_USHORT4N + - SG_VERTEXFORMAT_UINT10_N2 + - SG_VERTEXFORMAT_HALF2 + - SG_VERTEXFORMAT_HALF4 + + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + sg_setup(&(sg_desc){ + // ... + .allocator = { + .alloc_fn = my_alloc, + .free_fn = my_free, + .user_data = ..., + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_gfx.h + itself though, not any allocations in OS libraries. + + + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + sg_setup(&(sg_desc){ .logger.func = slog_func }); + + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'sg' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SG_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_gfx.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-gfx like this: + + sg_setup(&(sg_desc){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); + + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. + + + COMMIT LISTENERS + ================ + It's possible to hook callback functions into sokol-gfx which are called from + inside sg_commit() in unspecified order. This is mainly useful for libraries + that build on top of sokol_gfx.h to be notified about the end/start of a frame. + + To add a commit listener, call: + + static void my_commit_listener(void* user_data) { + ... + } + + bool success = sg_add_commit_listener((sg_commit_listener){ + .func = my_commit_listener, + .user_data = ..., + }); + + The function returns false if the internal array of commit listeners is full, + or the same commit listener had already been added. + + If the function returns true, my_commit_listener() will be called each frame + from inside sg_commit(). + + By default, 1024 distinct commit listeners can be added, but this number + can be tweaked in the sg_setup() call: + + sg_setup(&(sg_desc){ + .max_commit_listeners = 2048, + }); + + An sg_commit_listener item is equal to another if both the function + pointer and user_data field are equal. + + To remove a commit listener: + + bool success = sg_remove_commit_listener((sg_commit_listener){ + .func = my_commit_listener, + .user_data = ..., + }); + + ...where the .func and .user_data field are equal to a previous + sg_add_commit_listener() call. The function returns true if the commit + listener item was found and removed, and false otherwise. + + + RESOURCE CREATION AND DESTRUCTION IN DETAIL + =========================================== + The 'vanilla' way to create resource objects is with the 'make functions': + + sg_buffer sg_make_buffer(const sg_buffer_desc* desc) + sg_image sg_make_image(const sg_image_desc* desc) + sg_sampler sg_make_sampler(const sg_sampler_desc* desc) + sg_shader sg_make_shader(const sg_shader_desc* desc) + sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) + sg_attachments sg_make_attachments(const sg_attachments_desc* desc) + + This will result in one of three cases: + + 1. The returned handle is invalid. This happens when there are no more + free slots in the resource pool for this resource type. An invalid + handle is associated with the INVALID resource state, for instance: + + sg_buffer buf = sg_make_buffer(...) + if (sg_query_buffer_state(buf) == SG_RESOURCESTATE_INVALID) { + // buffer pool is exhausted + } + + 2. The returned handle is valid, but creating the underlying resource + has failed for some reason. This results in a resource object in the + FAILED state. The reason *why* resource creation has failed differ + by resource type. Look for log messages with more details. A failed + resource state can be checked with: + + sg_buffer buf = sg_make_buffer(...) + if (sg_query_buffer_state(buf) == SG_RESOURCESTATE_FAILED) { + // creating the resource has failed + } + + 3. And finally, if everything goes right, the returned resource is + in resource state VALID and ready to use. This can be checked + with: + + sg_buffer buf = sg_make_buffer(...) + if (sg_query_buffer_state(buf) == SG_RESOURCESTATE_VALID) { + // creating the resource has failed + } + + When calling the 'make functions', the created resource goes through a number + of states: + + - INITIAL: the resource slot associated with the new resource is currently + free (technically, there is no resource yet, just an empty pool slot) + - ALLOC: a handle for the new resource has been allocated, this just means + a pool slot has been reserved. + - VALID or FAILED: in VALID state any 3D API backend resource objects have + been successfully created, otherwise if anything went wrong, the resource + will be in FAILED state. + + Sometimes it makes sense to first grab a handle, but initialize the + underlying resource at a later time. For instance when loading data + asynchronously from a slow data source, you may know what buffers and + textures are needed at an early stage of the loading process, but actually + loading the buffer or texture content can only be completed at a later time. + + For such situations, sokol-gfx resource objects can be created in two steps. + You can allocate a handle upfront with one of the 'alloc functions': + + sg_buffer sg_alloc_buffer(void) + sg_image sg_alloc_image(void) + sg_sampler sg_alloc_sampler(void) + sg_shader sg_alloc_shader(void) + sg_pipeline sg_alloc_pipeline(void) + sg_attachments sg_alloc_attachments(void) + + This will return a handle with the underlying resource object in the + ALLOC state: + + sg_image img = sg_alloc_image(); + if (sg_query_image_state(img) == SG_RESOURCESTATE_ALLOC) { + // allocating an image handle has succeeded, otherwise + // the image pool is full + } + + Such an 'incomplete' handle can be used in most sokol-gfx rendering functions + without doing any harm, sokol-gfx will simply skip any rendering operation + that involve resources which are not in VALID state. + + At a later time (for instance once the texture has completed loading + asynchronously), the resource creation can be completed by calling one of + the 'init functions', those functions take an existing resource handle and + 'desc struct': + + void sg_init_buffer(sg_buffer buf, const sg_buffer_desc* desc) + void sg_init_image(sg_image img, const sg_image_desc* desc) + void sg_init_sampler(sg_sampler smp, const sg_sampler_desc* desc) + void sg_init_shader(sg_shader shd, const sg_shader_desc* desc) + void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc* desc) + void sg_init_attachments(sg_attachments atts, const sg_attachments_desc* desc) + + The init functions expect a resource in ALLOC state, and after the function + returns, the resource will be either in VALID or FAILED state. Calling + an 'alloc function' followed by the matching 'init function' is fully + equivalent with calling the 'make function' alone. + + Destruction can also happen as a two-step process. The 'uninit functions' + will put a resource object from the VALID or FAILED state back into the + ALLOC state: + + void sg_uninit_buffer(sg_buffer buf) + void sg_uninit_image(sg_image img) + void sg_uninit_sampler(sg_sampler smp) + void sg_uninit_shader(sg_shader shd) + void sg_uninit_pipeline(sg_pipeline pip) + void sg_uninit_attachments(sg_attachments pass) + + Calling the 'uninit functions' with a resource that is not in the VALID or + FAILED state is a no-op. + + To finally free the pool slot for recycling call the 'dealloc functions': + + void sg_dealloc_buffer(sg_buffer buf) + void sg_dealloc_image(sg_image img) + void sg_dealloc_sampler(sg_sampler smp) + void sg_dealloc_shader(sg_shader shd) + void sg_dealloc_pipeline(sg_pipeline pip) + void sg_dealloc_attachments(sg_attachments atts) + + Calling the 'dealloc functions' on a resource that's not in ALLOC state is + a no-op, but will generate a warning log message. + + Calling an 'uninit function' and 'dealloc function' in sequence is equivalent + with calling the associated 'destroy function': + + void sg_destroy_buffer(sg_buffer buf) + void sg_destroy_image(sg_image img) + void sg_destroy_sampler(sg_sampler smp) + void sg_destroy_shader(sg_shader shd) + void sg_destroy_pipeline(sg_pipeline pip) + void sg_destroy_attachments(sg_attachments atts) + + The 'destroy functions' can be called on resources in any state and generally + do the right thing (for instance if the resource is in ALLOC state, the destroy + function will be equivalent to the 'dealloc function' and skip the 'uninit part'). + + And finally to close the circle, the 'fail functions' can be called to manually + put a resource in ALLOC state into the FAILED state: + + sg_fail_buffer(sg_buffer buf) + sg_fail_image(sg_image img) + sg_fail_sampler(sg_sampler smp) + sg_fail_shader(sg_shader shd) + sg_fail_pipeline(sg_pipeline pip) + sg_fail_attachments(sg_attachments atts) + + This is recommended if anything went wrong outside of sokol-gfx during asynchronous + resource setup (for instance a file loading operation failed). In this case, + the 'fail function' should be called instead of the 'init function'. + + Calling a 'fail function' on a resource that's not in ALLOC state is a no-op, + but will generate a warning log message. + + NOTE: that two-step resource creation usually only makes sense for buffers + and images, but not for samplers, shaders, pipelines or attachments. Most notably, trying + to create a pipeline object with a shader that's not in VALID state will + trigger a validation layer error, or if the validation layer is disabled, + result in a pipeline object in FAILED state. Same when trying to create + an attachments object with invalid image objects. + + + WEBGPU CAVEATS + ============== + For a general overview and design notes of the WebGPU backend see: + + https://floooh.github.io/2023/10/16/sokol-webgpu.html + + In general, don't expect an automatic speedup when switching from the WebGL2 + backend to the WebGPU backend. Some WebGPU functions currently actually + have a higher CPU overhead than similar WebGL2 functions, leading to the + paradoxical situation that some WebGPU code may be slower than similar WebGL2 + code. + + - when writing WGSL shader code by hand, a specific bind-slot convention + must be used: + + All uniform block structs must use `@group(0)`, with up to + 4 uniform blocks per shader stage. + - Vertex shader uniform block bindings must start at `@group(0) @binding(0)` + - Fragment shader uniform blocks bindings must start at `@group(0) @binding(4)` + + All textures and samplers must use `@group(1)` and start at specific + offsets depending on resource type and shader stage. + - Vertex shader textures must start at `@group(1) @binding(0)` + - Vertex shader samplers must start at `@group(1) @binding(16)` + - Vertex shader storage buffers must start at `@group(1) @binding(32)` + - Fragment shader textures must start at `@group(1) @binding(48)` + - Fragment shader samplers must start at `@group(1) @binding(64)` + - Fragment shader storage buffers must start at `@group(1) @binding(80)` + + Note that the actual number of allowed per-stage texture- and sampler-bindings + in sokol-gfx is currently lower than the above ranges (currently only up to + 12 textures, 8 samplers and 8 storage buffers are allowed per shader stage). + + If you use sokol-shdc to generate WGSL shader code, you don't need to worry + about the above binding convention since sokol-shdc assigns bind slots + automatically. + + - The sokol-gfx WebGPU backend uses the sg_desc.uniform_buffer_size item + to allocate a single per-frame uniform buffer which must be big enough + to hold all data written by sg_apply_uniforms() during a single frame, + including a worst-case 256-byte alignment (e.g. each sg_apply_uniform + call will cost 256 bytes of uniform buffer size). The default size + is 4 MB, which is enough for 16384 sg_apply_uniform() calls per + frame (assuming the uniform data 'payload' is less than 256 bytes + per call). These rules are the same as for the Metal backend, so if + you are already using the Metal backend you'll be fine. + + - sg_apply_bindings(): the sokol-gfx WebGPU backend implements a bindgroup + cache to prevent excessive creation and destruction of BindGroup objects + when calling sg_apply_bindings(). The number of slots in the bindgroups + cache is defined in sg_desc.wgpu_bindgroups_cache_size when calling + sg_setup. The cache size must be a power-of-2 number, with the default being + 1024. The bindgroups cache behaviour can be observed by calling the new + function sg_query_frame_stats(), where the following struct items are + of interest: + + .wgpu.num_bindgroup_cache_hits + .wgpu.num_bindgroup_cache_misses + .wgpu.num_bindgroup_cache_collisions + .wgpu.num_bindgroup_cache_vs_hash_key_mismatch + + The value to pay attention to is `.wgpu.num_bindgroup_cache_collisions`, + if this number if consistently higher than a few percent of the + .wgpu.num_set_bindgroup value, it might be a good idea to bump the + bindgroups cache size to the next power-of-2. + + - sg_apply_viewport(): WebGPU currently has a unique restriction that viewport + rectangles must be contained entirely within the framebuffer. As a shitty + workaround sokol_gfx.h will clip incoming viewport rectangles against + the framebuffer, but this will distort the clipspace-to-screenspace mapping. + There's no proper way to handle this inside sokol_gfx.h, this must be fixed + in a future WebGPU update. + + - The sokol shader compiler generally adds `diagnostic(off, derivative_uniformity);` + into the WGSL output. Currently only the Chrome WebGPU implementation seems + to recognize this. + + - The vertex format SG_VERTEXFORMAT_UINT10_N2 is currently not supported because + WebGPU lacks a matching vertex format (this is currently being worked on though, + as soon as the vertex format shows up in webgpu.h, sokol_gfx.h will add support. + + - Likewise, the following sokol-gfx vertex formats are not supported in WebGPU: + R16, R16SN, RG16, RG16SN, RGBA16, RGBA16SN and all PVRTC compressed format. + Unlike unsupported vertex formats, unsupported pixel formats can be queried + in cross-backend code via sg_query_pixel_format() though. + + - The Emscripten WebGPU shim currently doesn't support the Closure minification + post-link-step (e.g. currently the emcc argument '--closure 1' or '--closure 2' + will generate broken Javascript code. + + - sokol-gfx requires the WebGPU device feature `depth32float-stencil8` to be enabled + (this should be widely supported) + + - sokol-gfx expects that the WebGPU device feature `float32-filterable` to *not* be + enabled (since this would exclude all iOS devices) + + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_GFX_INCLUDED (1) +#include // size_t +#include +#include + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_GFX_API_DECL) +#define SOKOL_GFX_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_GFX_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GFX_IMPL) +#define SOKOL_GFX_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_GFX_API_DECL __declspec(dllimport) +#else +#define SOKOL_GFX_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + Resource id typedefs: + + sg_buffer: vertex- and index-buffers + sg_image: images used as textures and render targets + sg_sampler sampler object describing how a texture is sampled in a shader + sg_shader: vertex- and fragment-shaders and shader interface information + sg_pipeline: associated shader and vertex-layouts, and render states + sg_attachments: a baked collection of render pass attachment images + + Instead of pointers, resource creation functions return a 32-bit + number which uniquely identifies the resource object. + + The 32-bit resource id is split into a 16-bit pool index in the lower bits, + and a 16-bit 'generation counter' in the upper bits. The index allows fast + pool lookups, and combined with the generation-counter it allows to detect + 'dangling accesses' (trying to use an object which no longer exists, and + its pool slot has been reused for a new object) + + The resource ids are wrapped into a strongly-typed struct so that + trying to pass an incompatible resource id is a compile error. +*/ +typedef struct sg_buffer { uint32_t id; } sg_buffer; +typedef struct sg_image { uint32_t id; } sg_image; +typedef struct sg_sampler { uint32_t id; } sg_sampler; +typedef struct sg_shader { uint32_t id; } sg_shader; +typedef struct sg_pipeline { uint32_t id; } sg_pipeline; +typedef struct sg_attachments { uint32_t id; } sg_attachments; + +/* + sg_range is a pointer-size-pair struct used to pass memory blobs into + sokol-gfx. When initialized from a value type (array or struct), you can + use the SG_RANGE() macro to build an sg_range struct. For functions which + take either a sg_range pointer, or a (C++) sg_range reference, use the + SG_RANGE_REF macro as a solution which compiles both in C and C++. +*/ +typedef struct sg_range { + const void* ptr; + size_t size; +} sg_range; + +// disabling this for every includer isn't great, but the warnings are also quite pointless +#if defined(_MSC_VER) +#pragma warning(disable:4221) // /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y' +#pragma warning(disable:4204) // VS2015: nonstandard extension used: non-constant aggregate initializer +#endif +#if defined(__cplusplus) +#define SG_RANGE(x) sg_range{ &x, sizeof(x) } +#define SG_RANGE_REF(x) sg_range{ &x, sizeof(x) } +#else +#define SG_RANGE(x) (sg_range){ &x, sizeof(x) } +#define SG_RANGE_REF(x) &(sg_range){ &x, sizeof(x) } +#endif + +// various compile-time constants +enum { + SG_INVALID_ID = 0, + SG_NUM_SHADER_STAGES = 2, + SG_NUM_INFLIGHT_FRAMES = 2, + SG_MAX_COLOR_ATTACHMENTS = 4, + SG_MAX_VERTEX_BUFFERS = 8, + SG_MAX_SHADERSTAGE_IMAGES = 12, + SG_MAX_SHADERSTAGE_SAMPLERS = 8, + SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS = 12, + SG_MAX_SHADERSTAGE_STORAGEBUFFERS = 8, + SG_MAX_SHADERSTAGE_UBS = 4, + SG_MAX_UB_MEMBERS = 16, + SG_MAX_VERTEX_ATTRIBUTES = 16, + SG_MAX_MIPMAPS = 16, + SG_MAX_TEXTUREARRAY_LAYERS = 128 +}; + +/* + sg_color + + An RGBA color value. +*/ +typedef struct sg_color { float r, g, b, a; } sg_color; + +/* + sg_backend + + The active 3D-API backend, use the function sg_query_backend() + to get the currently active backend. +*/ +typedef enum sg_backend { + SG_BACKEND_GLCORE, + SG_BACKEND_GLES3, + SG_BACKEND_D3D11, + SG_BACKEND_METAL_IOS, + SG_BACKEND_METAL_MACOS, + SG_BACKEND_METAL_SIMULATOR, + SG_BACKEND_WGPU, + SG_BACKEND_DUMMY, +} sg_backend; + +/* + sg_pixel_format + + sokol_gfx.h basically uses the same pixel formats as WebGPU, since these + are supported on most newer GPUs. + + A pixelformat name consist of three parts: + + - components (R, RG, RGB or RGBA) + - bit width per component (8, 16 or 32) + - component data type: + - unsigned normalized (no postfix) + - signed normalized (SN postfix) + - unsigned integer (UI postfix) + - signed integer (SI postfix) + - float (F postfix) + + Not all pixel formats can be used for everything, call sg_query_pixelformat() + to inspect the capabilities of a given pixelformat. The function returns + an sg_pixelformat_info struct with the following members: + + - sample: the pixelformat can be sampled as texture at least with + nearest filtering + - filter: the pixelformat can be samples as texture with linear + filtering + - render: the pixelformat can be used for render targets + - blend: blending is supported when using the pixelformat for + render targets + - msaa: multisample-antialiasing is supported when using the + pixelformat for render targets + - depth: the pixelformat can be used for depth-stencil attachments + - compressed: this is a block-compressed format + - bytes_per_pixel: the numbers of bytes in a pixel (0 for compressed formats) + + The default pixel format for texture images is SG_PIXELFORMAT_RGBA8. + + The default pixel format for render target images is platform-dependent + and taken from the sg_environment struct passed into sg_setup(). Typically + the default formats are: + + - for the Metal, D3D11 and WebGPU backends: SG_PIXELFORMAT_BGRA8 + - for GL backends: SG_PIXELFORMAT_RGBA8 +*/ +typedef enum sg_pixel_format { + _SG_PIXELFORMAT_DEFAULT, // value 0 reserved for default-init + SG_PIXELFORMAT_NONE, + + SG_PIXELFORMAT_R8, + SG_PIXELFORMAT_R8SN, + SG_PIXELFORMAT_R8UI, + SG_PIXELFORMAT_R8SI, + + SG_PIXELFORMAT_R16, + SG_PIXELFORMAT_R16SN, + SG_PIXELFORMAT_R16UI, + SG_PIXELFORMAT_R16SI, + SG_PIXELFORMAT_R16F, + SG_PIXELFORMAT_RG8, + SG_PIXELFORMAT_RG8SN, + SG_PIXELFORMAT_RG8UI, + SG_PIXELFORMAT_RG8SI, + + SG_PIXELFORMAT_R32UI, + SG_PIXELFORMAT_R32SI, + SG_PIXELFORMAT_R32F, + SG_PIXELFORMAT_RG16, + SG_PIXELFORMAT_RG16SN, + SG_PIXELFORMAT_RG16UI, + SG_PIXELFORMAT_RG16SI, + SG_PIXELFORMAT_RG16F, + SG_PIXELFORMAT_RGBA8, + SG_PIXELFORMAT_SRGB8A8, + SG_PIXELFORMAT_RGBA8SN, + SG_PIXELFORMAT_RGBA8UI, + SG_PIXELFORMAT_RGBA8SI, + SG_PIXELFORMAT_BGRA8, + SG_PIXELFORMAT_RGB10A2, + SG_PIXELFORMAT_RG11B10F, + SG_PIXELFORMAT_RGB9E5, + + SG_PIXELFORMAT_RG32UI, + SG_PIXELFORMAT_RG32SI, + SG_PIXELFORMAT_RG32F, + SG_PIXELFORMAT_RGBA16, + SG_PIXELFORMAT_RGBA16SN, + SG_PIXELFORMAT_RGBA16UI, + SG_PIXELFORMAT_RGBA16SI, + SG_PIXELFORMAT_RGBA16F, + + SG_PIXELFORMAT_RGBA32UI, + SG_PIXELFORMAT_RGBA32SI, + SG_PIXELFORMAT_RGBA32F, + + // NOTE: when adding/removing pixel formats before DEPTH, also update sokol_app.h/_SAPP_PIXELFORMAT_* + SG_PIXELFORMAT_DEPTH, + SG_PIXELFORMAT_DEPTH_STENCIL, + + // NOTE: don't put any new compressed format in front of here + SG_PIXELFORMAT_BC1_RGBA, + SG_PIXELFORMAT_BC2_RGBA, + SG_PIXELFORMAT_BC3_RGBA, + SG_PIXELFORMAT_BC3_SRGBA, + SG_PIXELFORMAT_BC4_R, + SG_PIXELFORMAT_BC4_RSN, + SG_PIXELFORMAT_BC5_RG, + SG_PIXELFORMAT_BC5_RGSN, + SG_PIXELFORMAT_BC6H_RGBF, + SG_PIXELFORMAT_BC6H_RGBUF, + SG_PIXELFORMAT_BC7_RGBA, + SG_PIXELFORMAT_BC7_SRGBA, + SG_PIXELFORMAT_PVRTC_RGB_2BPP, // FIXME: deprecated + SG_PIXELFORMAT_PVRTC_RGB_4BPP, // FIXME: deprecated + SG_PIXELFORMAT_PVRTC_RGBA_2BPP, // FIXME: deprecated + SG_PIXELFORMAT_PVRTC_RGBA_4BPP, // FIXME: deprecated + SG_PIXELFORMAT_ETC2_RGB8, + SG_PIXELFORMAT_ETC2_SRGB8, + SG_PIXELFORMAT_ETC2_RGB8A1, + SG_PIXELFORMAT_ETC2_RGBA8, + SG_PIXELFORMAT_ETC2_SRGB8A8, + SG_PIXELFORMAT_EAC_R11, + SG_PIXELFORMAT_EAC_R11SN, + SG_PIXELFORMAT_EAC_RG11, + SG_PIXELFORMAT_EAC_RG11SN, + + SG_PIXELFORMAT_ASTC_4x4_RGBA, + SG_PIXELFORMAT_ASTC_4x4_SRGBA, + + _SG_PIXELFORMAT_NUM, + _SG_PIXELFORMAT_FORCE_U32 = 0x7FFFFFFF +} sg_pixel_format; + +/* + Runtime information about a pixel format, returned + by sg_query_pixelformat(). +*/ +typedef struct sg_pixelformat_info { + bool sample; // pixel format can be sampled in shaders at least with nearest filtering + bool filter; // pixel format can be sampled with linear filtering + bool render; // pixel format can be used as render target + bool blend; // alpha-blending is supported + bool msaa; // pixel format can be used as MSAA render target + bool depth; // pixel format is a depth format + bool compressed; // true if this is a hardware-compressed format + int bytes_per_pixel; // NOTE: this is 0 for compressed formats, use sg_query_row_pitch() / sg_query_surface_pitch() as alternative +} sg_pixelformat_info; + +/* + Runtime information about available optional features, + returned by sg_query_features() +*/ +typedef struct sg_features { + bool origin_top_left; // framebuffer and texture origin is in top left corner + bool image_clamp_to_border; // border color and clamp-to-border UV-wrap mode is supported + bool mrt_independent_blend_state; // multiple-render-target rendering can use per-render-target blend state + bool mrt_independent_write_mask; // multiple-render-target rendering can use per-render-target color write masks + bool storage_buffer; // storage buffers are supported +} sg_features; + +/* + Runtime information about resource limits, returned by sg_query_limit() +*/ +typedef struct sg_limits { + int max_image_size_2d; // max width/height of SG_IMAGETYPE_2D images + int max_image_size_cube; // max width/height of SG_IMAGETYPE_CUBE images + int max_image_size_3d; // max width/height/depth of SG_IMAGETYPE_3D images + int max_image_size_array; // max width/height of SG_IMAGETYPE_ARRAY images + int max_image_array_layers; // max number of layers in SG_IMAGETYPE_ARRAY images + int max_vertex_attrs; // max number of vertex attributes, clamped to SG_MAX_VERTEX_ATTRIBUTES + int gl_max_vertex_uniform_components; // <= GL_MAX_VERTEX_UNIFORM_COMPONENTS (only on GL backends) + int gl_max_combined_texture_image_units; // <= GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS (only on GL backends) +} sg_limits; + +/* + sg_resource_state + + The current state of a resource in its resource pool. + Resources start in the INITIAL state, which means the + pool slot is unoccupied and can be allocated. When a resource is + created, first an id is allocated, and the resource pool slot + is set to state ALLOC. After allocation, the resource is + initialized, which may result in the VALID or FAILED state. The + reason why allocation and initialization are separate is because + some resource types (e.g. buffers and images) might be asynchronously + initialized by the user application. If a resource which is not + in the VALID state is attempted to be used for rendering, rendering + operations will silently be dropped. + + The special INVALID state is returned in sg_query_xxx_state() if no + resource object exists for the provided resource id. +*/ +typedef enum sg_resource_state { + SG_RESOURCESTATE_INITIAL, + SG_RESOURCESTATE_ALLOC, + SG_RESOURCESTATE_VALID, + SG_RESOURCESTATE_FAILED, + SG_RESOURCESTATE_INVALID, + _SG_RESOURCESTATE_FORCE_U32 = 0x7FFFFFFF +} sg_resource_state; + +/* + sg_usage + + A resource usage hint describing the update strategy of + buffers and images. This is used in the sg_buffer_desc.usage + and sg_image_desc.usage members when creating buffers + and images: + + SG_USAGE_IMMUTABLE: the resource will never be updated with + new data, instead the content of the + resource must be provided on creation + SG_USAGE_DYNAMIC: the resource will be updated infrequently + with new data (this could range from "once + after creation", to "quite often but not + every frame") + SG_USAGE_STREAM: the resource will be updated each frame + with new content + + The rendering backends use this hint to prevent that the + CPU needs to wait for the GPU when attempting to update + a resource that might be currently accessed by the GPU. + + Resource content is updated with the functions sg_update_buffer() or + sg_append_buffer() for buffer objects, and sg_update_image() for image + objects. For the sg_update_*() functions, only one update is allowed per + frame and resource object, while sg_append_buffer() can be called + multiple times per frame on the same buffer. The application must update + all data required for rendering (this means that the update data can be + smaller than the resource size, if only a part of the overall resource + size is used for rendering, you only need to make sure that the data that + *is* used is valid). + + The default usage is SG_USAGE_IMMUTABLE. +*/ +typedef enum sg_usage { + _SG_USAGE_DEFAULT, // value 0 reserved for default-init + SG_USAGE_IMMUTABLE, + SG_USAGE_DYNAMIC, + SG_USAGE_STREAM, + _SG_USAGE_NUM, + _SG_USAGE_FORCE_U32 = 0x7FFFFFFF +} sg_usage; + +/* + sg_buffer_type + + Indicates whether a buffer will be bound as vertex-, + index- or storage-buffer. + + Used in the sg_buffer_desc.type member when creating a buffer. + + The default value is SG_BUFFERTYPE_VERTEXBUFFER. +*/ +typedef enum sg_buffer_type { + _SG_BUFFERTYPE_DEFAULT, // value 0 reserved for default-init + SG_BUFFERTYPE_VERTEXBUFFER, + SG_BUFFERTYPE_INDEXBUFFER, + SG_BUFFERTYPE_STORAGEBUFFER, + _SG_BUFFERTYPE_NUM, + _SG_BUFFERTYPE_FORCE_U32 = 0x7FFFFFFF +} sg_buffer_type; + +/* + sg_index_type + + Indicates whether indexed rendering (fetching vertex-indices from an + index buffer) is used, and if yes, the index data type (16- or 32-bits). + This is used in the sg_pipeline_desc.index_type member when creating a + pipeline object. + + The default index type is SG_INDEXTYPE_NONE. +*/ +typedef enum sg_index_type { + _SG_INDEXTYPE_DEFAULT, // value 0 reserved for default-init + SG_INDEXTYPE_NONE, + SG_INDEXTYPE_UINT16, + SG_INDEXTYPE_UINT32, + _SG_INDEXTYPE_NUM, + _SG_INDEXTYPE_FORCE_U32 = 0x7FFFFFFF +} sg_index_type; + +/* + sg_image_type + + Indicates the basic type of an image object (2D-texture, cubemap, + 3D-texture or 2D-array-texture). Used in the sg_image_desc.type member when + creating an image, and in sg_shader_image_desc to describe a sampled texture + in the shader (both must match and will be checked in the validation layer + when calling sg_apply_bindings). + + The default image type when creating an image is SG_IMAGETYPE_2D. +*/ +typedef enum sg_image_type { + _SG_IMAGETYPE_DEFAULT, // value 0 reserved for default-init + SG_IMAGETYPE_2D, + SG_IMAGETYPE_CUBE, + SG_IMAGETYPE_3D, + SG_IMAGETYPE_ARRAY, + _SG_IMAGETYPE_NUM, + _SG_IMAGETYPE_FORCE_U32 = 0x7FFFFFFF +} sg_image_type; + +/* + sg_image_sample_type + + The basic data type of a texture sample as expected by a shader. + Must be provided in sg_shader_image_desc and used by the validation + layer in sg_apply_bindings() to check if the provided image object + is compatible with what the shader expects. Apart from the sokol-gfx + validation layer, WebGPU is the only backend API which actually requires + matching texture and sampler type to be provided upfront for validation + (other 3D APIs treat texture/sampler type mismatches as undefined behaviour). + + NOTE that the following texture pixel formats require the use + of SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT, combined with a sampler + of type SG_SAMPLERTYPE_NONFILTERING: + + - SG_PIXELFORMAT_R32F + - SG_PIXELFORMAT_RG32F + - SG_PIXELFORMAT_RGBA32F + + (when using sokol-shdc, also check out the meta tags `@image_sample_type` + and `@sampler_type`) +*/ +typedef enum sg_image_sample_type { + _SG_IMAGESAMPLETYPE_DEFAULT, // value 0 reserved for default-init + SG_IMAGESAMPLETYPE_FLOAT, + SG_IMAGESAMPLETYPE_DEPTH, + SG_IMAGESAMPLETYPE_SINT, + SG_IMAGESAMPLETYPE_UINT, + SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT, + _SG_IMAGESAMPLETYPE_NUM, + _SG_IMAGESAMPLETYPE_FORCE_U32 = 0x7FFFFFFF +} sg_image_sample_type; + +/* + sg_sampler_type + + The basic type of a texture sampler (sampling vs comparison) as + defined in a shader. Must be provided in sg_shader_sampler_desc. + + sg_image_sample_type and sg_sampler_type for a texture/sampler + pair must be compatible with each other, specifically only + the following pairs are allowed: + + - SG_IMAGESAMPLETYPE_FLOAT => (SG_SAMPLERTYPE_FILTERING or SG_SAMPLERTYPE_NONFILTERING) + - SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT => SG_SAMPLERTYPE_NONFILTERING + - SG_IMAGESAMPLETYPE_SINT => SG_SAMPLERTYPE_NONFILTERING + - SG_IMAGESAMPLETYPE_UINT => SG_SAMPLERTYPE_NONFILTERING + - SG_IMAGESAMPLETYPE_DEPTH => SG_SAMPLERTYPE_COMPARISON +*/ +typedef enum sg_sampler_type { + _SG_SAMPLERTYPE_DEFAULT, + SG_SAMPLERTYPE_FILTERING, + SG_SAMPLERTYPE_NONFILTERING, + SG_SAMPLERTYPE_COMPARISON, + _SG_SAMPLERTYPE_NUM, + _SG_SAMPLERTYPE_FORCE_U32, +} sg_sampler_type; + +/* + sg_cube_face + + The cubemap faces. Use these as indices in the sg_image_desc.content + array. +*/ +typedef enum sg_cube_face { + SG_CUBEFACE_POS_X, + SG_CUBEFACE_NEG_X, + SG_CUBEFACE_POS_Y, + SG_CUBEFACE_NEG_Y, + SG_CUBEFACE_POS_Z, + SG_CUBEFACE_NEG_Z, + SG_CUBEFACE_NUM, + _SG_CUBEFACE_FORCE_U32 = 0x7FFFFFFF +} sg_cube_face; + +/* + sg_shader_stage + + There are 2 shader stages: vertex- and fragment-shader-stage. + Each shader stage + + - SG_MAX_SHADERSTAGE_UBS slots for applying uniform data + - SG_MAX_SHADERSTAGE_IMAGES slots for images used as textures + - SG_MAX_SHADERSTAGE_SAMPLERS slots for texture samplers + - SG_MAX_SHADERSTAGE_STORAGEBUFFERS slots for storage buffer bindings +*/ +typedef enum sg_shader_stage { + SG_SHADERSTAGE_VS, + SG_SHADERSTAGE_FS, + _SG_SHADERSTAGE_FORCE_U32 = 0x7FFFFFFF +} sg_shader_stage; + +/* + sg_primitive_type + + This is the common subset of 3D primitive types supported across all 3D + APIs. This is used in the sg_pipeline_desc.primitive_type member when + creating a pipeline object. + + The default primitive type is SG_PRIMITIVETYPE_TRIANGLES. +*/ +typedef enum sg_primitive_type { + _SG_PRIMITIVETYPE_DEFAULT, // value 0 reserved for default-init + SG_PRIMITIVETYPE_POINTS, + SG_PRIMITIVETYPE_LINES, + SG_PRIMITIVETYPE_LINE_STRIP, + SG_PRIMITIVETYPE_TRIANGLES, + SG_PRIMITIVETYPE_TRIANGLE_STRIP, + _SG_PRIMITIVETYPE_NUM, + _SG_PRIMITIVETYPE_FORCE_U32 = 0x7FFFFFFF +} sg_primitive_type; + +/* + sg_filter + + The filtering mode when sampling a texture image. This is + used in the sg_sampler_desc.min_filter, sg_sampler_desc.mag_filter + and sg_sampler_desc.mipmap_filter members when creating a sampler object. + + For min_filter and mag_filter the default is SG_FILTER_NEAREST. + + For mipmap_filter the default is SG_FILTER_NONE. +*/ +typedef enum sg_filter { + _SG_FILTER_DEFAULT, // value 0 reserved for default-init + SG_FILTER_NONE, // FIXME: deprecated + SG_FILTER_NEAREST, + SG_FILTER_LINEAR, + _SG_FILTER_NUM, + _SG_FILTER_FORCE_U32 = 0x7FFFFFFF +} sg_filter; + +/* + sg_wrap + + The texture coordinates wrapping mode when sampling a texture + image. This is used in the sg_image_desc.wrap_u, .wrap_v + and .wrap_w members when creating an image. + + The default wrap mode is SG_WRAP_REPEAT. + + NOTE: SG_WRAP_CLAMP_TO_BORDER is not supported on all backends + and platforms. To check for support, call sg_query_features() + and check the "clamp_to_border" boolean in the returned + sg_features struct. + + Platforms which don't support SG_WRAP_CLAMP_TO_BORDER will silently fall back + to SG_WRAP_CLAMP_TO_EDGE without a validation error. +*/ +typedef enum sg_wrap { + _SG_WRAP_DEFAULT, // value 0 reserved for default-init + SG_WRAP_REPEAT, + SG_WRAP_CLAMP_TO_EDGE, + SG_WRAP_CLAMP_TO_BORDER, + SG_WRAP_MIRRORED_REPEAT, + _SG_WRAP_NUM, + _SG_WRAP_FORCE_U32 = 0x7FFFFFFF +} sg_wrap; + +/* + sg_border_color + + The border color to use when sampling a texture, and the UV wrap + mode is SG_WRAP_CLAMP_TO_BORDER. + + The default border color is SG_BORDERCOLOR_OPAQUE_BLACK +*/ +typedef enum sg_border_color { + _SG_BORDERCOLOR_DEFAULT, // value 0 reserved for default-init + SG_BORDERCOLOR_TRANSPARENT_BLACK, + SG_BORDERCOLOR_OPAQUE_BLACK, + SG_BORDERCOLOR_OPAQUE_WHITE, + _SG_BORDERCOLOR_NUM, + _SG_BORDERCOLOR_FORCE_U32 = 0x7FFFFFFF +} sg_border_color; + +/* + sg_vertex_format + + The data type of a vertex component. This is used to describe + the layout of vertex data when creating a pipeline object. +*/ +typedef enum sg_vertex_format { + SG_VERTEXFORMAT_INVALID, + SG_VERTEXFORMAT_FLOAT, + SG_VERTEXFORMAT_FLOAT2, + SG_VERTEXFORMAT_FLOAT3, + SG_VERTEXFORMAT_FLOAT4, + SG_VERTEXFORMAT_BYTE4, + SG_VERTEXFORMAT_BYTE4N, + SG_VERTEXFORMAT_UBYTE4, + SG_VERTEXFORMAT_UBYTE4N, + SG_VERTEXFORMAT_SHORT2, + SG_VERTEXFORMAT_SHORT2N, + SG_VERTEXFORMAT_USHORT2N, + SG_VERTEXFORMAT_SHORT4, + SG_VERTEXFORMAT_SHORT4N, + SG_VERTEXFORMAT_USHORT4N, + SG_VERTEXFORMAT_UINT10_N2, + SG_VERTEXFORMAT_HALF2, + SG_VERTEXFORMAT_HALF4, + _SG_VERTEXFORMAT_NUM, + _SG_VERTEXFORMAT_FORCE_U32 = 0x7FFFFFFF +} sg_vertex_format; + +/* + sg_vertex_step + + Defines whether the input pointer of a vertex input stream is advanced + 'per vertex' or 'per instance'. The default step-func is + SG_VERTEXSTEP_PER_VERTEX. SG_VERTEXSTEP_PER_INSTANCE is used with + instanced-rendering. + + The vertex-step is part of the vertex-layout definition + when creating pipeline objects. +*/ +typedef enum sg_vertex_step { + _SG_VERTEXSTEP_DEFAULT, // value 0 reserved for default-init + SG_VERTEXSTEP_PER_VERTEX, + SG_VERTEXSTEP_PER_INSTANCE, + _SG_VERTEXSTEP_NUM, + _SG_VERTEXSTEP_FORCE_U32 = 0x7FFFFFFF +} sg_vertex_step; + +/* + sg_uniform_type + + The data type of a uniform block member. This is used to + describe the internal layout of uniform blocks when creating + a shader object. +*/ +typedef enum sg_uniform_type { + SG_UNIFORMTYPE_INVALID, + SG_UNIFORMTYPE_FLOAT, + SG_UNIFORMTYPE_FLOAT2, + SG_UNIFORMTYPE_FLOAT3, + SG_UNIFORMTYPE_FLOAT4, + SG_UNIFORMTYPE_INT, + SG_UNIFORMTYPE_INT2, + SG_UNIFORMTYPE_INT3, + SG_UNIFORMTYPE_INT4, + SG_UNIFORMTYPE_MAT4, + _SG_UNIFORMTYPE_NUM, + _SG_UNIFORMTYPE_FORCE_U32 = 0x7FFFFFFF +} sg_uniform_type; + +/* + sg_uniform_layout + + A hint for the interior memory layout of uniform blocks. This is + only really relevant for the GL backend where the internal layout + of uniform blocks must be known to sokol-gfx. For all other backends the + internal memory layout of uniform blocks doesn't matter, sokol-gfx + will just pass uniform data as a single memory blob to the + 3D backend. + + SG_UNIFORMLAYOUT_NATIVE (default) + Native layout means that a 'backend-native' memory layout + is used. For the GL backend this means that uniforms + are packed tightly in memory (e.g. there are no padding + bytes). + + SG_UNIFORMLAYOUT_STD140 + The memory layout is a subset of std140. Arrays are only + allowed for the FLOAT4, INT4 and MAT4. Alignment is as + is as follows: + + FLOAT, INT: 4 byte alignment + FLOAT2, INT2: 8 byte alignment + FLOAT3, INT3: 16 byte alignment(!) + FLOAT4, INT4: 16 byte alignment + MAT4: 16 byte alignment + FLOAT4[], INT4[]: 16 byte alignment + + The overall size of the uniform block must be a multiple + of 16. + + For more information search for 'UNIFORM DATA LAYOUT' in the documentation block + at the start of the header. +*/ +typedef enum sg_uniform_layout { + _SG_UNIFORMLAYOUT_DEFAULT, // value 0 reserved for default-init + SG_UNIFORMLAYOUT_NATIVE, // default: layout depends on currently active backend + SG_UNIFORMLAYOUT_STD140, // std140: memory layout according to std140 + _SG_UNIFORMLAYOUT_NUM, + _SG_UNIFORMLAYOUT_FORCE_U32 = 0x7FFFFFFF +} sg_uniform_layout; + +/* + sg_cull_mode + + The face-culling mode, this is used in the + sg_pipeline_desc.cull_mode member when creating a + pipeline object. + + The default cull mode is SG_CULLMODE_NONE +*/ +typedef enum sg_cull_mode { + _SG_CULLMODE_DEFAULT, // value 0 reserved for default-init + SG_CULLMODE_NONE, + SG_CULLMODE_FRONT, + SG_CULLMODE_BACK, + _SG_CULLMODE_NUM, + _SG_CULLMODE_FORCE_U32 = 0x7FFFFFFF +} sg_cull_mode; + +/* + sg_face_winding + + The vertex-winding rule that determines a front-facing primitive. This + is used in the member sg_pipeline_desc.face_winding + when creating a pipeline object. + + The default winding is SG_FACEWINDING_CW (clockwise) +*/ +typedef enum sg_face_winding { + _SG_FACEWINDING_DEFAULT, // value 0 reserved for default-init + SG_FACEWINDING_CCW, + SG_FACEWINDING_CW, + _SG_FACEWINDING_NUM, + _SG_FACEWINDING_FORCE_U32 = 0x7FFFFFFF +} sg_face_winding; + +/* + sg_compare_func + + The compare-function for configuring depth- and stencil-ref tests + in pipeline objects, and for texture samplers which perform a comparison + instead of regular sampling operation. + + sg_pipeline_desc + .depth + .compare + .stencil + .front.compare + .back.compar + + sg_sampler_desc + .compare + + The default compare func for depth- and stencil-tests is + SG_COMPAREFUNC_ALWAYS. + + The default compare func for sampler is SG_COMPAREFUNC_NEVER. +*/ +typedef enum sg_compare_func { + _SG_COMPAREFUNC_DEFAULT, // value 0 reserved for default-init + SG_COMPAREFUNC_NEVER, + SG_COMPAREFUNC_LESS, + SG_COMPAREFUNC_EQUAL, + SG_COMPAREFUNC_LESS_EQUAL, + SG_COMPAREFUNC_GREATER, + SG_COMPAREFUNC_NOT_EQUAL, + SG_COMPAREFUNC_GREATER_EQUAL, + SG_COMPAREFUNC_ALWAYS, + _SG_COMPAREFUNC_NUM, + _SG_COMPAREFUNC_FORCE_U32 = 0x7FFFFFFF +} sg_compare_func; + +/* + sg_stencil_op + + The operation performed on a currently stored stencil-value when a + comparison test passes or fails. This is used when creating a pipeline + object in the members: + + sg_pipeline_desc + .stencil + .front + .fail_op + .depth_fail_op + .pass_op + .back + .fail_op + .depth_fail_op + .pass_op + + The default value is SG_STENCILOP_KEEP. +*/ +typedef enum sg_stencil_op { + _SG_STENCILOP_DEFAULT, // value 0 reserved for default-init + SG_STENCILOP_KEEP, + SG_STENCILOP_ZERO, + SG_STENCILOP_REPLACE, + SG_STENCILOP_INCR_CLAMP, + SG_STENCILOP_DECR_CLAMP, + SG_STENCILOP_INVERT, + SG_STENCILOP_INCR_WRAP, + SG_STENCILOP_DECR_WRAP, + _SG_STENCILOP_NUM, + _SG_STENCILOP_FORCE_U32 = 0x7FFFFFFF +} sg_stencil_op; + +/* + sg_blend_factor + + The source and destination factors in blending operations. + This is used in the following members when creating a pipeline object: + + sg_pipeline_desc + .colors[i] + .blend + .src_factor_rgb + .dst_factor_rgb + .src_factor_alpha + .dst_factor_alpha + + The default value is SG_BLENDFACTOR_ONE for source + factors, and SG_BLENDFACTOR_ZERO for destination factors. +*/ +typedef enum sg_blend_factor { + _SG_BLENDFACTOR_DEFAULT, // value 0 reserved for default-init + SG_BLENDFACTOR_ZERO, + SG_BLENDFACTOR_ONE, + SG_BLENDFACTOR_SRC_COLOR, + SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR, + SG_BLENDFACTOR_SRC_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, + SG_BLENDFACTOR_DST_COLOR, + SG_BLENDFACTOR_ONE_MINUS_DST_COLOR, + SG_BLENDFACTOR_DST_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA, + SG_BLENDFACTOR_SRC_ALPHA_SATURATED, + SG_BLENDFACTOR_BLEND_COLOR, + SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR, + SG_BLENDFACTOR_BLEND_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA, + _SG_BLENDFACTOR_NUM, + _SG_BLENDFACTOR_FORCE_U32 = 0x7FFFFFFF +} sg_blend_factor; + +/* + sg_blend_op + + Describes how the source and destination values are combined in the + fragment blending operation. It is used in the following members when + creating a pipeline object: + + sg_pipeline_desc + .colors[i] + .blend + .op_rgb + .op_alpha + + The default value is SG_BLENDOP_ADD. +*/ +typedef enum sg_blend_op { + _SG_BLENDOP_DEFAULT, // value 0 reserved for default-init + SG_BLENDOP_ADD, + SG_BLENDOP_SUBTRACT, + SG_BLENDOP_REVERSE_SUBTRACT, + _SG_BLENDOP_NUM, + _SG_BLENDOP_FORCE_U32 = 0x7FFFFFFF +} sg_blend_op; + +/* + sg_color_mask + + Selects the active color channels when writing a fragment color to the + framebuffer. This is used in the members + sg_pipeline_desc.colors[i].write_mask when creating a pipeline object. + + The default colormask is SG_COLORMASK_RGBA (write all colors channels) + + NOTE: since the color mask value 0 is reserved for the default value + (SG_COLORMASK_RGBA), use SG_COLORMASK_NONE if all color channels + should be disabled. +*/ +typedef enum sg_color_mask { + _SG_COLORMASK_DEFAULT = 0, // value 0 reserved for default-init + SG_COLORMASK_NONE = 0x10, // special value for 'all channels disabled + SG_COLORMASK_R = 0x1, + SG_COLORMASK_G = 0x2, + SG_COLORMASK_RG = 0x3, + SG_COLORMASK_B = 0x4, + SG_COLORMASK_RB = 0x5, + SG_COLORMASK_GB = 0x6, + SG_COLORMASK_RGB = 0x7, + SG_COLORMASK_A = 0x8, + SG_COLORMASK_RA = 0x9, + SG_COLORMASK_GA = 0xA, + SG_COLORMASK_RGA = 0xB, + SG_COLORMASK_BA = 0xC, + SG_COLORMASK_RBA = 0xD, + SG_COLORMASK_GBA = 0xE, + SG_COLORMASK_RGBA = 0xF, + _SG_COLORMASK_FORCE_U32 = 0x7FFFFFFF +} sg_color_mask; + +/* + sg_load_action + + Defines the load action that should be performed at the start of a render pass: + + SG_LOADACTION_CLEAR: clear the render target + SG_LOADACTION_LOAD: load the previous content of the render target + SG_LOADACTION_DONTCARE: leave the render target in an undefined state + + This is used in the sg_pass_action structure. + + The default load action for all pass attachments is SG_LOADACTION_CLEAR, + with the values rgba = { 0.5f, 0.5f, 0.5f, 1.0f }, depth=1.0f and stencil=0. + + If you want to override the default behaviour, it is important to not + only set the clear color, but the 'action' field as well (as long as this + is _SG_LOADACTION_DEFAULT, the value fields will be ignored). +*/ +typedef enum sg_load_action { + _SG_LOADACTION_DEFAULT, + SG_LOADACTION_CLEAR, + SG_LOADACTION_LOAD, + SG_LOADACTION_DONTCARE, + _SG_LOADACTION_FORCE_U32 = 0x7FFFFFFF +} sg_load_action; + +/* + sg_store_action + + Defines the store action that be performed at the end of a render pass: + + SG_STOREACTION_STORE: store the rendered content to the color attachment image + SG_STOREACTION_DONTCARE: allows the GPU to discard the rendered content +*/ +typedef enum sg_store_action { + _SG_STOREACTION_DEFAULT, + SG_STOREACTION_STORE, + SG_STOREACTION_DONTCARE, + _SG_STOREACTION_FORCE_U32 = 0x7FFFFFFF +} sg_store_action; + + +/* + sg_pass_action + + The sg_pass_action struct defines the actions to be performed + at the start and end of a render pass. + + - at the start of the pass: whether the render targets should be cleared, + loaded with their previous content, or start in an undefined state + - for clear operations: the clear value (color, depth, or stencil values) + - at the end of the pass: whether the rendering result should be + stored back into the render target or discarded +*/ +typedef struct sg_color_attachment_action { + sg_load_action load_action; // default: SG_LOADACTION_CLEAR + sg_store_action store_action; // default: SG_STOREACTION_STORE + sg_color clear_value; // default: { 0.5f, 0.5f, 0.5f, 1.0f } +} sg_color_attachment_action; + +typedef struct sg_depth_attachment_action { + sg_load_action load_action; // default: SG_LOADACTION_CLEAR + sg_store_action store_action; // default: SG_STOREACTION_DONTCARE + float clear_value; // default: 1.0 +} sg_depth_attachment_action; + +typedef struct sg_stencil_attachment_action { + sg_load_action load_action; // default: SG_LOADACTION_CLEAR + sg_store_action store_action; // default: SG_STOREACTION_DONTCARE + uint8_t clear_value; // default: 0 +} sg_stencil_attachment_action; + +typedef struct sg_pass_action { + sg_color_attachment_action colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_depth_attachment_action depth; + sg_stencil_attachment_action stencil; +} sg_pass_action; + +/* + sg_swapchain + + Used in sg_begin_pass() to provide details about an external swapchain + (pixel formats, sample count and backend-API specific render surface objects). + + The following information must be provided: + + - the width and height of the swapchain surfaces in number of pixels, + - the pixel format of the render- and optional msaa-resolve-surface + - the pixel format of the optional depth- or depth-stencil-surface + - the MSAA sample count for the render and depth-stencil surface + + If the pixel formats and MSAA sample counts are left zero-initialized, + their defaults are taken from the sg_environment struct provided in the + sg_setup() call. + + The width and height *must* be > 0. + + Additionally the following backend API specific objects must be passed in + as 'type erased' void pointers: + + GL: on all GL backends, a GL framebuffer object must be provided. This + can be zero for the default framebuffer. + + D3D11: + - an ID3D11RenderTargetView for the rendering surface, without + MSAA rendering this surface will also be displayed + - an optional ID3D11DepthStencilView for the depth- or depth/stencil + buffer surface + - when MSAA rendering is used, another ID3D11RenderTargetView + which serves as MSAA resolve target and will be displayed + + WebGPU (same as D3D11, except different types) + - a WGPUTextureView for the rendering surface, without + MSAA rendering this surface will also be displayed + - an optional WGPUTextureView for the depth- or depth/stencil + buffer surface + - when MSAA rendering is used, another WGPUTextureView + which serves as MSAA resolve target and will be displayed + + Metal (NOTE that the rolves of provided surfaces is slightly different + than on D3D11 or WebGPU in case of MSAA vs non-MSAA rendering): + + - A current CAMetalDrawable (NOT an MTLDrawable!) which will be presented. + This will either be rendered to directly (if no MSAA is used), or serve + as MSAA-resolve target. + - an optional MTLTexture for the depth- or depth-stencil buffer + - an optional multisampled MTLTexture which serves as intermediate + rendering surface which will then be resolved into the + CAMetalDrawable. + + NOTE that for Metal you must use an ObjC __bridge cast to + properly tunnel the ObjC object handle through a C void*, e.g.: + + swapchain.metal.current_drawable = (__bridge const void*) [mtkView currentDrawable]; + + On all other backends you shouldn't need to mess with the reference count. + + It's a good practice to write a helper function which returns an initialized + sg_swapchain structs, which can then be plugged directly into + sg_pass.swapchain. Look at the function sglue_swapchain() in the sokol_glue.h + as an example. +*/ +typedef struct sg_metal_swapchain { + const void* current_drawable; // CAMetalDrawable (NOT MTLDrawable!!!) + const void* depth_stencil_texture; // MTLTexture + const void* msaa_color_texture; // MTLTexture +} sg_metal_swapchain; + +typedef struct sg_d3d11_swapchain { + const void* render_view; // ID3D11RenderTargetView + const void* resolve_view; // ID3D11RenderTargetView + const void* depth_stencil_view; // ID3D11DepthStencilView +} sg_d3d11_swapchain; + +typedef struct sg_wgpu_swapchain { + const void* render_view; // WGPUTextureView + const void* resolve_view; // WGPUTextureView + const void* depth_stencil_view; // WGPUTextureView +} sg_wgpu_swapchain; + +typedef struct sg_gl_swapchain { + uint32_t framebuffer; // GL framebuffer object +} sg_gl_swapchain; + +typedef struct sg_swapchain { + int width; + int height; + int sample_count; + sg_pixel_format color_format; + sg_pixel_format depth_format; + sg_metal_swapchain metal; + sg_d3d11_swapchain d3d11; + sg_wgpu_swapchain wgpu; + sg_gl_swapchain gl; +} sg_swapchain; + +/* + sg_pass + + The sg_pass structure is passed as argument into the sg_begin_pass() + function. + + For an offscreen rendering pass, an sg_pass_action struct and sg_attachments + object must be provided, and for swapchain passes, and sg_pass_action and + an sg_swapchain struct. It is an error to provide both an sg_attachments + handle and an initialized sg_swapchain struct in the same sg_begin_pass(). + + An sg_begin_pass() call for an offscreen pass would look like this (where + `attachments` is an sg_attachments handle): + + sg_begin_pass(&(sg_pass){ + .action = { ... }, + .attachments = attachments, + }); + + ...and a swapchain render pass would look like this (using the sokol_glue.h + helper function sglue_swapchain() which gets the swapchain properties from + sokol_app.h): + + sg_begin_pass(&(sg_pass){ + .action = { ... }, + .swapchain = sglue_swapchain(), + }); + + You can also omit the .action object to get default pass action behaviour + (clear to color=grey, depth=1 and stencil=0). +*/ +typedef struct sg_pass { + uint32_t _start_canary; + sg_pass_action action; + sg_attachments attachments; + sg_swapchain swapchain; + const char* label; + uint32_t _end_canary; +} sg_pass; + +/* + sg_bindings + + The sg_bindings structure defines the resource binding slots + of the sokol_gfx render pipeline, used as argument to the + sg_apply_bindings() function. + + A resource binding struct contains: + + - 1..N vertex buffers + - 0..N vertex buffer offsets + - 0..1 index buffers + - 0..1 index buffer offsets + - 0..N vertex shader stage images + - 0..N vertex shader stage samplers + - 0..N vertex shader storage buffers + - 0..N fragment shader stage images + - 0..N fragment shader stage samplers + - 0..N fragment shader storage buffers + + For the max number of bindings, see the constant definitions: + + - SG_MAX_VERTEX_BUFFERS + - SG_MAX_SHADERSTAGE_IMAGES + - SG_MAX_SHADERSTAGE_SAMPLERS + - SG_MAX_SHADERSTAGE_STORAGEBUFFERS + + The optional buffer offsets can be used to put different unrelated + chunks of vertex- and/or index-data into the same buffer objects. +*/ +typedef struct sg_stage_bindings { + sg_image images[SG_MAX_SHADERSTAGE_IMAGES]; + sg_sampler samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; + sg_buffer storage_buffers[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; +} sg_stage_bindings; + +typedef struct sg_bindings { + uint32_t _start_canary; + sg_buffer vertex_buffers[SG_MAX_VERTEX_BUFFERS]; + int vertex_buffer_offsets[SG_MAX_VERTEX_BUFFERS]; + sg_buffer index_buffer; + int index_buffer_offset; + sg_stage_bindings vs; + sg_stage_bindings fs; + uint32_t _end_canary; +} sg_bindings; + +/* + sg_buffer_desc + + Creation parameters for sg_buffer objects, used in the + sg_make_buffer() call. + + The default configuration is: + + .size: 0 (*must* be >0 for buffers without data) + .type: SG_BUFFERTYPE_VERTEXBUFFER + .usage: SG_USAGE_IMMUTABLE + .data.ptr 0 (*must* be valid for immutable buffers) + .data.size 0 (*must* be > 0 for immutable buffers) + .label 0 (optional string label) + + For immutable buffers which are initialized with initial data, + keep the .size item zero-initialized, and set the size together with the + pointer to the initial data in the .data item. + + For mutable buffers without initial data, keep the .data item + zero-initialized, and set the buffer size in the .size item instead. + + You can also set both size values, but currently both size values must + be identical (this may change in the future when the dynamic resource + management may become more flexible). + + ADVANCED TOPIC: Injecting native 3D-API buffers: + + The following struct members allow to inject your own GL, Metal + or D3D11 buffers into sokol_gfx: + + .gl_buffers[SG_NUM_INFLIGHT_FRAMES] + .mtl_buffers[SG_NUM_INFLIGHT_FRAMES] + .d3d11_buffer + + You must still provide all other struct items except the .data item, and + these must match the creation parameters of the native buffers you + provide. For SG_USAGE_IMMUTABLE, only provide a single native 3D-API + buffer, otherwise you need to provide SG_NUM_INFLIGHT_FRAMES buffers + (only for GL and Metal, not D3D11). Providing multiple buffers for GL and + Metal is necessary because sokol_gfx will rotate through them when + calling sg_update_buffer() to prevent lock-stalls. + + Note that it is expected that immutable injected buffer have already been + initialized with content, and the .content member must be 0! + + Also you need to call sg_reset_state_cache() after calling native 3D-API + functions, and before calling any sokol_gfx function. +*/ +typedef struct sg_buffer_desc { + uint32_t _start_canary; + size_t size; + sg_buffer_type type; + sg_usage usage; + sg_range data; + const char* label; + // optionally inject backend-specific resources + uint32_t gl_buffers[SG_NUM_INFLIGHT_FRAMES]; + const void* mtl_buffers[SG_NUM_INFLIGHT_FRAMES]; + const void* d3d11_buffer; + const void* wgpu_buffer; + uint32_t _end_canary; +} sg_buffer_desc; + +/* + sg_image_data + + Defines the content of an image through a 2D array of sg_range structs. + The first array dimension is the cubemap face, and the second array + dimension the mipmap level. +*/ +typedef struct sg_image_data { + sg_range subimage[SG_CUBEFACE_NUM][SG_MAX_MIPMAPS]; +} sg_image_data; + +/* + sg_image_desc + + Creation parameters for sg_image objects, used in the sg_make_image() call. + + The default configuration is: + + .type: SG_IMAGETYPE_2D + .render_target: false + .width 0 (must be set to >0) + .height 0 (must be set to >0) + .num_slices 1 (3D textures: depth; array textures: number of layers) + .num_mipmaps: 1 + .usage: SG_USAGE_IMMUTABLE + .pixel_format: SG_PIXELFORMAT_RGBA8 for textures, or sg_desc.environment.defaults.color_format for render targets + .sample_count: 1 for textures, or sg_desc.environment.defaults.sample_count for render targets + .data an sg_image_data struct to define the initial content + .label 0 (optional string label for trace hooks) + + Q: Why is the default sample_count for render targets identical with the + "default sample count" from sg_desc.environment.defaults.sample_count? + + A: So that it matches the default sample count in pipeline objects. Even + though it is a bit strange/confusing that offscreen render targets by default + get the same sample count as 'default swapchains', but it's better that + an offscreen render target created with default parameters matches + a pipeline object created with default parameters. + + NOTE: + + Images with usage SG_USAGE_IMMUTABLE must be fully initialized by + providing a valid .data member which points to initialization data. + + ADVANCED TOPIC: Injecting native 3D-API textures: + + The following struct members allow to inject your own GL, Metal or D3D11 + textures into sokol_gfx: + + .gl_textures[SG_NUM_INFLIGHT_FRAMES] + .mtl_textures[SG_NUM_INFLIGHT_FRAMES] + .d3d11_texture + .d3d11_shader_resource_view + .wgpu_texture + .wgpu_texture_view + + For GL, you can also specify the texture target or leave it empty to use + the default texture target for the image type (GL_TEXTURE_2D for + SG_IMAGETYPE_2D etc) + + For D3D11 and WebGPU, either only provide a texture, or both a texture and + shader-resource-view / texture-view object. If you want to use access the + injected texture in a shader you *must* provide a shader-resource-view. + + The same rules apply as for injecting native buffers (see sg_buffer_desc + documentation for more details). +*/ +typedef struct sg_image_desc { + uint32_t _start_canary; + sg_image_type type; + bool render_target; + int width; + int height; + int num_slices; + int num_mipmaps; + sg_usage usage; + sg_pixel_format pixel_format; + int sample_count; + sg_image_data data; + const char* label; + // optionally inject backend-specific resources + uint32_t gl_textures[SG_NUM_INFLIGHT_FRAMES]; + uint32_t gl_texture_target; + const void* mtl_textures[SG_NUM_INFLIGHT_FRAMES]; + const void* d3d11_texture; + const void* d3d11_shader_resource_view; + const void* wgpu_texture; + const void* wgpu_texture_view; + uint32_t _end_canary; +} sg_image_desc; + +/* + sg_sampler_desc + + Creation parameters for sg_sampler objects, used in the sg_make_sampler() call + + .min_filter: SG_FILTER_NEAREST + .mag_filter: SG_FILTER_NEAREST + .mipmap_filter SG_FILTER_NONE + .wrap_u: SG_WRAP_REPEAT + .wrap_v: SG_WRAP_REPEAT + .wrap_w: SG_WRAP_REPEAT (only SG_IMAGETYPE_3D) + .min_lod 0.0f + .max_lod FLT_MAX + .border_color SG_BORDERCOLOR_OPAQUE_BLACK + .compare SG_COMPAREFUNC_NEVER + .max_anisotropy 1 (must be 1..16) + +*/ +typedef struct sg_sampler_desc { + uint32_t _start_canary; + sg_filter min_filter; + sg_filter mag_filter; + sg_filter mipmap_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + float min_lod; + float max_lod; + sg_border_color border_color; + sg_compare_func compare; + uint32_t max_anisotropy; + const char* label; + // optionally inject backend-specific resources + uint32_t gl_sampler; + const void* mtl_sampler; + const void* d3d11_sampler; + const void* wgpu_sampler; + uint32_t _end_canary; +} sg_sampler_desc; + +/* + sg_shader_desc + + The structure sg_shader_desc defines all creation parameters for shader + programs, used as input to the sg_make_shader() function: + + - reflection information for vertex attributes (vertex shader inputs): + - vertex attribute name (only optionally used by GLES3 and GL) + - a semantic name and index (required for D3D11) + - for each shader-stage (vertex and fragment): + - the shader source or bytecode + - an optional entry function name + - an optional compile target (only for D3D11 when source is provided, + defaults are "vs_4_0" and "ps_4_0") + - reflection info for each uniform block used by the shader stage: + - the size of the uniform block in bytes + - a memory layout hint (native vs std140, only required for GL backends) + - reflection info for each uniform block member (only required for GL backends): + - member name + - member type (SG_UNIFORMTYPE_xxx) + - if the member is an array, the number of array items + - reflection info for textures used in the shader stage: + - the image type (SG_IMAGETYPE_xxx) + - the image-sample type (SG_IMAGESAMPLETYPE_xxx, default is SG_IMAGESAMPLETYPE_FLOAT) + - whether the shader expects a multisampled texture + - reflection info for samplers used in the shader stage: + - the sampler type (SG_SAMPLERTYPE_xxx) + - reflection info for each image-sampler-pair used by the shader: + - the texture slot of the involved texture + - the sampler slot of the involved sampler + - for GLSL only: the name of the combined image-sampler object + - reflection info for each storage-buffer used by the shader: + - whether the storage buffer is readonly (currently this + must be true) + + For all GL backends, shader source-code must be provided. For D3D11 and Metal, + either shader source-code or byte-code can be provided. + + For D3D11, if source code is provided, the d3dcompiler_47.dll will be loaded + on demand. If this fails, shader creation will fail. When compiling HLSL + source code, you can provide an optional target string via + sg_shader_stage_desc.d3d11_target, the default target is "vs_4_0" for the + vertex shader stage and "ps_4_0" for the pixel shader stage. +*/ +typedef struct sg_shader_attr_desc { + const char* name; // GLSL vertex attribute name (optional) + const char* sem_name; // HLSL semantic name + int sem_index; // HLSL semantic index +} sg_shader_attr_desc; + +typedef struct sg_shader_uniform_desc { + const char* name; + sg_uniform_type type; + int array_count; +} sg_shader_uniform_desc; + +typedef struct sg_shader_uniform_block_desc { + size_t size; + sg_uniform_layout layout; + sg_shader_uniform_desc uniforms[SG_MAX_UB_MEMBERS]; +} sg_shader_uniform_block_desc; + +typedef struct sg_shader_storage_buffer_desc { + bool used; + bool readonly; +} sg_shader_storage_buffer_desc; + +typedef struct sg_shader_image_desc { + bool used; + bool multisampled; + sg_image_type image_type; + sg_image_sample_type sample_type; +} sg_shader_image_desc; + +typedef struct sg_shader_sampler_desc { + bool used; + sg_sampler_type sampler_type; +} sg_shader_sampler_desc; + +typedef struct sg_shader_image_sampler_pair_desc { + bool used; + int image_slot; + int sampler_slot; + const char* glsl_name; +} sg_shader_image_sampler_pair_desc; + +typedef struct sg_shader_stage_desc { + const char* source; + sg_range bytecode; + const char* entry; + const char* d3d11_target; + sg_shader_uniform_block_desc uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + sg_shader_storage_buffer_desc storage_buffers[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; + sg_shader_image_desc images[SG_MAX_SHADERSTAGE_IMAGES]; + sg_shader_sampler_desc samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; + sg_shader_image_sampler_pair_desc image_sampler_pairs[SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS]; +} sg_shader_stage_desc; + +typedef struct sg_shader_desc { + uint32_t _start_canary; + sg_shader_attr_desc attrs[SG_MAX_VERTEX_ATTRIBUTES]; + sg_shader_stage_desc vs; + sg_shader_stage_desc fs; + const char* label; + uint32_t _end_canary; +} sg_shader_desc; + +/* + sg_pipeline_desc + + The sg_pipeline_desc struct defines all creation parameters for an + sg_pipeline object, used as argument to the sg_make_pipeline() function: + + - the vertex layout for all input vertex buffers + - a shader object + - the 3D primitive type (points, lines, triangles, ...) + - the index type (none, 16- or 32-bit) + - all the fixed-function-pipeline state (depth-, stencil-, blend-state, etc...) + + If the vertex data has no gaps between vertex components, you can omit + the .layout.buffers[].stride and layout.attrs[].offset items (leave them + default-initialized to 0), sokol-gfx will then compute the offsets and + strides from the vertex component formats (.layout.attrs[].format). + Please note that ALL vertex attribute offsets must be 0 in order for the + automatic offset computation to kick in. + + The default configuration is as follows: + + .shader: 0 (must be initialized with a valid sg_shader id!) + .layout: + .buffers[]: vertex buffer layouts + .stride: 0 (if no stride is given it will be computed) + .step_func SG_VERTEXSTEP_PER_VERTEX + .step_rate 1 + .attrs[]: vertex attribute declarations + .buffer_index 0 the vertex buffer bind slot + .offset 0 (offsets can be omitted if the vertex layout has no gaps) + .format SG_VERTEXFORMAT_INVALID (must be initialized!) + .depth: + .pixel_format: sg_desc.context.depth_format + .compare: SG_COMPAREFUNC_ALWAYS + .write_enabled: false + .bias: 0.0f + .bias_slope_scale: 0.0f + .bias_clamp: 0.0f + .stencil: + .enabled: false + .front/back: + .compare: SG_COMPAREFUNC_ALWAYS + .fail_op: SG_STENCILOP_KEEP + .depth_fail_op: SG_STENCILOP_KEEP + .pass_op: SG_STENCILOP_KEEP + .read_mask: 0 + .write_mask: 0 + .ref: 0 + .color_count 1 + .colors[0..color_count] + .pixel_format sg_desc.context.color_format + .write_mask: SG_COLORMASK_RGBA + .blend: + .enabled: false + .src_factor_rgb: SG_BLENDFACTOR_ONE + .dst_factor_rgb: SG_BLENDFACTOR_ZERO + .op_rgb: SG_BLENDOP_ADD + .src_factor_alpha: SG_BLENDFACTOR_ONE + .dst_factor_alpha: SG_BLENDFACTOR_ZERO + .op_alpha: SG_BLENDOP_ADD + .primitive_type: SG_PRIMITIVETYPE_TRIANGLES + .index_type: SG_INDEXTYPE_NONE + .cull_mode: SG_CULLMODE_NONE + .face_winding: SG_FACEWINDING_CW + .sample_count: sg_desc.context.sample_count + .blend_color: (sg_color) { 0.0f, 0.0f, 0.0f, 0.0f } + .alpha_to_coverage_enabled: false + .label 0 (optional string label for trace hooks) +*/ +typedef struct sg_vertex_buffer_layout_state { + int stride; + sg_vertex_step step_func; + int step_rate; +} sg_vertex_buffer_layout_state; + +typedef struct sg_vertex_attr_state { + int buffer_index; + int offset; + sg_vertex_format format; +} sg_vertex_attr_state; + +typedef struct sg_vertex_layout_state { + sg_vertex_buffer_layout_state buffers[SG_MAX_VERTEX_BUFFERS]; + sg_vertex_attr_state attrs[SG_MAX_VERTEX_ATTRIBUTES]; +} sg_vertex_layout_state; + +typedef struct sg_stencil_face_state { + sg_compare_func compare; + sg_stencil_op fail_op; + sg_stencil_op depth_fail_op; + sg_stencil_op pass_op; +} sg_stencil_face_state; + +typedef struct sg_stencil_state { + bool enabled; + sg_stencil_face_state front; + sg_stencil_face_state back; + uint8_t read_mask; + uint8_t write_mask; + uint8_t ref; +} sg_stencil_state; + +typedef struct sg_depth_state { + sg_pixel_format pixel_format; + sg_compare_func compare; + bool write_enabled; + float bias; + float bias_slope_scale; + float bias_clamp; +} sg_depth_state; + +typedef struct sg_blend_state { + bool enabled; + sg_blend_factor src_factor_rgb; + sg_blend_factor dst_factor_rgb; + sg_blend_op op_rgb; + sg_blend_factor src_factor_alpha; + sg_blend_factor dst_factor_alpha; + sg_blend_op op_alpha; +} sg_blend_state; + +typedef struct sg_color_target_state { + sg_pixel_format pixel_format; + sg_color_mask write_mask; + sg_blend_state blend; +} sg_color_target_state; + +typedef struct sg_pipeline_desc { + uint32_t _start_canary; + sg_shader shader; + sg_vertex_layout_state layout; + sg_depth_state depth; + sg_stencil_state stencil; + int color_count; + sg_color_target_state colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_primitive_type primitive_type; + sg_index_type index_type; + sg_cull_mode cull_mode; + sg_face_winding face_winding; + int sample_count; + sg_color blend_color; + bool alpha_to_coverage_enabled; + const char* label; + uint32_t _end_canary; +} sg_pipeline_desc; + +/* + sg_attachments_desc + + Creation parameters for an sg_attachments object, used as argument to the + sg_make_attachments() function. + + An attachments object bundles 0..4 color attachments, 0..4 msaa-resolve + attachments, and none or one depth-stencil attachmente for use + in a render pass. At least one color attachment or one depth-stencil + attachment must be provided (no color attachment and a depth-stencil + attachment is useful for a depth-only render pass). + + Each attachment definition consists of an image object, and two additional indices + describing which subimage the pass will render into: one mipmap index, and if the image + is a cubemap, array-texture or 3D-texture, the face-index, array-layer or + depth-slice. + + All attachments must have the same width and height. + + All color attachments and the depth-stencil attachment must have the + same sample count. + + If a resolve attachment is set, an MSAA-resolve operation from the + associated color attachment image into the resolve attachment image will take + place in the sg_end_pass() function. In this case, the color attachment + must have a (sample_count>1), and the resolve attachment a + (sample_count==1). The resolve attachment also must have the same pixel + format as the color attachment. + + NOTE that MSAA depth-stencil attachments cannot be msaa-resolved! +*/ +typedef struct sg_attachment_desc { + sg_image image; + int mip_level; + int slice; // cube texture: face; array texture: layer; 3D texture: slice +} sg_attachment_desc; + +typedef struct sg_attachments_desc { + uint32_t _start_canary; + sg_attachment_desc colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_attachment_desc resolves[SG_MAX_COLOR_ATTACHMENTS]; + sg_attachment_desc depth_stencil; + const char* label; + uint32_t _end_canary; +} sg_attachments_desc; + +/* + sg_trace_hooks + + Installable callback functions to keep track of the sokol-gfx calls, + this is useful for debugging, or keeping track of resource creation + and destruction. + + Trace hooks are installed with sg_install_trace_hooks(), this returns + another sg_trace_hooks struct with the previous set of + trace hook function pointers. These should be invoked by the + new trace hooks to form a proper call chain. +*/ +typedef struct sg_trace_hooks { + void* user_data; + void (*reset_state_cache)(void* user_data); + void (*make_buffer)(const sg_buffer_desc* desc, sg_buffer result, void* user_data); + void (*make_image)(const sg_image_desc* desc, sg_image result, void* user_data); + void (*make_sampler)(const sg_sampler_desc* desc, sg_sampler result, void* user_data); + void (*make_shader)(const sg_shader_desc* desc, sg_shader result, void* user_data); + void (*make_pipeline)(const sg_pipeline_desc* desc, sg_pipeline result, void* user_data); + void (*make_attachments)(const sg_attachments_desc* desc, sg_attachments result, void* user_data); + void (*destroy_buffer)(sg_buffer buf, void* user_data); + void (*destroy_image)(sg_image img, void* user_data); + void (*destroy_sampler)(sg_sampler smp, void* user_data); + void (*destroy_shader)(sg_shader shd, void* user_data); + void (*destroy_pipeline)(sg_pipeline pip, void* user_data); + void (*destroy_attachments)(sg_attachments atts, void* user_data); + void (*update_buffer)(sg_buffer buf, const sg_range* data, void* user_data); + void (*update_image)(sg_image img, const sg_image_data* data, void* user_data); + void (*append_buffer)(sg_buffer buf, const sg_range* data, int result, void* user_data); + void (*begin_pass)(const sg_pass* pass, void* user_data); + void (*apply_viewport)(int x, int y, int width, int height, bool origin_top_left, void* user_data); + void (*apply_scissor_rect)(int x, int y, int width, int height, bool origin_top_left, void* user_data); + void (*apply_pipeline)(sg_pipeline pip, void* user_data); + void (*apply_bindings)(const sg_bindings* bindings, void* user_data); + void (*apply_uniforms)(sg_shader_stage stage, int ub_index, const sg_range* data, void* user_data); + void (*draw)(int base_element, int num_elements, int num_instances, void* user_data); + void (*end_pass)(void* user_data); + void (*commit)(void* user_data); + void (*alloc_buffer)(sg_buffer result, void* user_data); + void (*alloc_image)(sg_image result, void* user_data); + void (*alloc_sampler)(sg_sampler result, void* user_data); + void (*alloc_shader)(sg_shader result, void* user_data); + void (*alloc_pipeline)(sg_pipeline result, void* user_data); + void (*alloc_attachments)(sg_attachments result, void* user_data); + void (*dealloc_buffer)(sg_buffer buf_id, void* user_data); + void (*dealloc_image)(sg_image img_id, void* user_data); + void (*dealloc_sampler)(sg_sampler smp_id, void* user_data); + void (*dealloc_shader)(sg_shader shd_id, void* user_data); + void (*dealloc_pipeline)(sg_pipeline pip_id, void* user_data); + void (*dealloc_attachments)(sg_attachments atts_id, void* user_data); + void (*init_buffer)(sg_buffer buf_id, const sg_buffer_desc* desc, void* user_data); + void (*init_image)(sg_image img_id, const sg_image_desc* desc, void* user_data); + void (*init_sampler)(sg_sampler smp_id, const sg_sampler_desc* desc, void* user_data); + void (*init_shader)(sg_shader shd_id, const sg_shader_desc* desc, void* user_data); + void (*init_pipeline)(sg_pipeline pip_id, const sg_pipeline_desc* desc, void* user_data); + void (*init_attachments)(sg_attachments atts_id, const sg_attachments_desc* desc, void* user_data); + void (*uninit_buffer)(sg_buffer buf_id, void* user_data); + void (*uninit_image)(sg_image img_id, void* user_data); + void (*uninit_sampler)(sg_sampler smp_id, void* user_data); + void (*uninit_shader)(sg_shader shd_id, void* user_data); + void (*uninit_pipeline)(sg_pipeline pip_id, void* user_data); + void (*uninit_attachments)(sg_attachments atts_id, void* user_data); + void (*fail_buffer)(sg_buffer buf_id, void* user_data); + void (*fail_image)(sg_image img_id, void* user_data); + void (*fail_sampler)(sg_sampler smp_id, void* user_data); + void (*fail_shader)(sg_shader shd_id, void* user_data); + void (*fail_pipeline)(sg_pipeline pip_id, void* user_data); + void (*fail_attachments)(sg_attachments atts_id, void* user_data); + void (*push_debug_group)(const char* name, void* user_data); + void (*pop_debug_group)(void* user_data); +} sg_trace_hooks; + +/* + sg_buffer_info + sg_image_info + sg_sampler_info + sg_shader_info + sg_pipeline_info + sg_attachments_info + + These structs contain various internal resource attributes which + might be useful for debug-inspection. Please don't rely on the + actual content of those structs too much, as they are quite closely + tied to sokol_gfx.h internals and may change more frequently than + the other public API elements. + + The *_info structs are used as the return values of the following functions: + + sg_query_buffer_info() + sg_query_image_info() + sg_query_sampler_info() + sg_query_shader_info() + sg_query_pipeline_info() + sg_query_pass_info() +*/ +typedef struct sg_slot_info { + sg_resource_state state; // the current state of this resource slot + uint32_t res_id; // type-neutral resource if (e.g. sg_buffer.id) +} sg_slot_info; + +typedef struct sg_buffer_info { + sg_slot_info slot; // resource pool slot info + uint32_t update_frame_index; // frame index of last sg_update_buffer() + uint32_t append_frame_index; // frame index of last sg_append_buffer() + int append_pos; // current position in buffer for sg_append_buffer() + bool append_overflow; // is buffer in overflow state (due to sg_append_buffer) + int num_slots; // number of renaming-slots for dynamically updated buffers + int active_slot; // currently active write-slot for dynamically updated buffers +} sg_buffer_info; + +typedef struct sg_image_info { + sg_slot_info slot; // resource pool slot info + uint32_t upd_frame_index; // frame index of last sg_update_image() + int num_slots; // number of renaming-slots for dynamically updated images + int active_slot; // currently active write-slot for dynamically updated images +} sg_image_info; + +typedef struct sg_sampler_info { + sg_slot_info slot; // resource pool slot info +} sg_sampler_info; + +typedef struct sg_shader_info { + sg_slot_info slot; // resource pool slot info +} sg_shader_info; + +typedef struct sg_pipeline_info { + sg_slot_info slot; // resource pool slot info +} sg_pipeline_info; + +typedef struct sg_attachments_info { + sg_slot_info slot; // resource pool slot info +} sg_attachments_info; + +/* + sg_frame_stats + + Allows to track generic and backend-specific stats about a + render frame. Obtained by calling sg_query_frame_stats(). The returned + struct contains information about the *previous* frame. +*/ +typedef struct sg_frame_stats_gl { + uint32_t num_bind_buffer; + uint32_t num_active_texture; + uint32_t num_bind_texture; + uint32_t num_bind_sampler; + uint32_t num_use_program; + uint32_t num_render_state; + uint32_t num_vertex_attrib_pointer; + uint32_t num_vertex_attrib_divisor; + uint32_t num_enable_vertex_attrib_array; + uint32_t num_disable_vertex_attrib_array; + uint32_t num_uniform; +} sg_frame_stats_gl; + +typedef struct sg_frame_stats_d3d11_pass { + uint32_t num_om_set_render_targets; + uint32_t num_clear_render_target_view; + uint32_t num_clear_depth_stencil_view; + uint32_t num_resolve_subresource; +} sg_frame_stats_d3d11_pass; + +typedef struct sg_frame_stats_d3d11_pipeline { + uint32_t num_rs_set_state; + uint32_t num_om_set_depth_stencil_state; + uint32_t num_om_set_blend_state; + uint32_t num_ia_set_primitive_topology; + uint32_t num_ia_set_input_layout; + uint32_t num_vs_set_shader; + uint32_t num_vs_set_constant_buffers; + uint32_t num_ps_set_shader; + uint32_t num_ps_set_constant_buffers; +} sg_frame_stats_d3d11_pipeline; + +typedef struct sg_frame_stats_d3d11_bindings { + uint32_t num_ia_set_vertex_buffers; + uint32_t num_ia_set_index_buffer; + uint32_t num_vs_set_shader_resources; + uint32_t num_ps_set_shader_resources; + uint32_t num_vs_set_samplers; + uint32_t num_ps_set_samplers; +} sg_frame_stats_d3d11_bindings; + +typedef struct sg_frame_stats_d3d11_uniforms { + uint32_t num_update_subresource; +} sg_frame_stats_d3d11_uniforms; + +typedef struct sg_frame_stats_d3d11_draw { + uint32_t num_draw_indexed_instanced; + uint32_t num_draw_indexed; + uint32_t num_draw_instanced; + uint32_t num_draw; +} sg_frame_stats_d3d11_draw; + +typedef struct sg_frame_stats_d3d11 { + sg_frame_stats_d3d11_pass pass; + sg_frame_stats_d3d11_pipeline pipeline; + sg_frame_stats_d3d11_bindings bindings; + sg_frame_stats_d3d11_uniforms uniforms; + sg_frame_stats_d3d11_draw draw; + uint32_t num_map; + uint32_t num_unmap; +} sg_frame_stats_d3d11; + +typedef struct sg_frame_stats_metal_idpool { + uint32_t num_added; + uint32_t num_released; + uint32_t num_garbage_collected; +} sg_frame_stats_metal_idpool; + +typedef struct sg_frame_stats_metal_pipeline { + uint32_t num_set_blend_color; + uint32_t num_set_cull_mode; + uint32_t num_set_front_facing_winding; + uint32_t num_set_stencil_reference_value; + uint32_t num_set_depth_bias; + uint32_t num_set_render_pipeline_state; + uint32_t num_set_depth_stencil_state; +} sg_frame_stats_metal_pipeline; + +typedef struct sg_frame_stats_metal_bindings { + uint32_t num_set_vertex_buffer; + uint32_t num_set_vertex_texture; + uint32_t num_set_vertex_sampler_state; + uint32_t num_set_fragment_buffer; + uint32_t num_set_fragment_texture; + uint32_t num_set_fragment_sampler_state; +} sg_frame_stats_metal_bindings; + +typedef struct sg_frame_stats_metal_uniforms { + uint32_t num_set_vertex_buffer_offset; + uint32_t num_set_fragment_buffer_offset; +} sg_frame_stats_metal_uniforms; + +typedef struct sg_frame_stats_metal { + sg_frame_stats_metal_idpool idpool; + sg_frame_stats_metal_pipeline pipeline; + sg_frame_stats_metal_bindings bindings; + sg_frame_stats_metal_uniforms uniforms; +} sg_frame_stats_metal; + +typedef struct sg_frame_stats_wgpu_uniforms { + uint32_t num_set_bindgroup; + uint32_t size_write_buffer; +} sg_frame_stats_wgpu_uniforms; + +typedef struct sg_frame_stats_wgpu_bindings { + uint32_t num_set_vertex_buffer; + uint32_t num_skip_redundant_vertex_buffer; + uint32_t num_set_index_buffer; + uint32_t num_skip_redundant_index_buffer; + uint32_t num_create_bindgroup; + uint32_t num_discard_bindgroup; + uint32_t num_set_bindgroup; + uint32_t num_skip_redundant_bindgroup; + uint32_t num_bindgroup_cache_hits; + uint32_t num_bindgroup_cache_misses; + uint32_t num_bindgroup_cache_collisions; + uint32_t num_bindgroup_cache_hash_vs_key_mismatch; +} sg_frame_stats_wgpu_bindings; + +typedef struct sg_frame_stats_wgpu { + sg_frame_stats_wgpu_uniforms uniforms; + sg_frame_stats_wgpu_bindings bindings; +} sg_frame_stats_wgpu; + +typedef struct sg_frame_stats { + uint32_t frame_index; // current frame counter, starts at 0 + + uint32_t num_passes; + uint32_t num_apply_viewport; + uint32_t num_apply_scissor_rect; + uint32_t num_apply_pipeline; + uint32_t num_apply_bindings; + uint32_t num_apply_uniforms; + uint32_t num_draw; + uint32_t num_update_buffer; + uint32_t num_append_buffer; + uint32_t num_update_image; + + uint32_t size_apply_uniforms; + uint32_t size_update_buffer; + uint32_t size_append_buffer; + uint32_t size_update_image; + + sg_frame_stats_gl gl; + sg_frame_stats_d3d11 d3d11; + sg_frame_stats_metal metal; + sg_frame_stats_wgpu wgpu; +} sg_frame_stats; + +/* + sg_log_item + + An enum with a unique item for each log message, warning, error + and validation layer message. +*/ +#define _SG_LOG_ITEMS \ + _SG_LOGITEM_XMACRO(OK, "Ok") \ + _SG_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SG_LOGITEM_XMACRO(GL_TEXTURE_FORMAT_NOT_SUPPORTED, "pixel format not supported for texture (gl)") \ + _SG_LOGITEM_XMACRO(GL_3D_TEXTURES_NOT_SUPPORTED, "3d textures not supported (gl)") \ + _SG_LOGITEM_XMACRO(GL_ARRAY_TEXTURES_NOT_SUPPORTED, "array textures not supported (gl)") \ + _SG_LOGITEM_XMACRO(GL_SHADER_COMPILATION_FAILED, "shader compilation failed (gl)") \ + _SG_LOGITEM_XMACRO(GL_SHADER_LINKING_FAILED, "shader linking failed (gl)") \ + _SG_LOGITEM_XMACRO(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER, "vertex attribute not found in shader (gl)") \ + _SG_LOGITEM_XMACRO(GL_TEXTURE_NAME_NOT_FOUND_IN_SHADER, "texture name not found in shader (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_UNDEFINED, "framebuffer completeness check failed with GL_FRAMEBUFFER_UNDEFINED (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_INCOMPLETE_ATTACHMENT, "framebuffer completeness check failed with GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MISSING_ATTACHMENT, "framebuffer completeness check failed with GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_UNSUPPORTED, "framebuffer completeness check failed with GL_FRAMEBUFFER_UNSUPPORTED (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MULTISAMPLE, "framebuffer completeness check failed with GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_UNKNOWN, "framebuffer completeness check failed (unknown reason) (gl)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_BUFFER_FAILED, "CreateBuffer() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_BUFFER_SRV_FAILED, "CreateShaderResourceView() failed for storage buffer (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for depth-stencil texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_TEXTURE_FAILED, "CreateTexture2D() failed for depth-stencil texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for 2d-, cube- or array-texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_TEXTURE_FAILED, "CreateTexture2D() failed for 2d-, cube- or array-texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_SRV_FAILED, "CreateShaderResourceView() failed for 2d-, cube- or array-texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_3D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for 3D texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_3D_TEXTURE_FAILED, "CreateTexture3D() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_3D_SRV_FAILED, "CreateShaderResourceView() failed for 3d texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_MSAA_TEXTURE_FAILED, "CreateTexture2D() failed for MSAA render target texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_SAMPLER_STATE_FAILED, "CreateSamplerState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_LOAD_D3DCOMPILER_47_DLL_FAILED, "loading d3dcompiler_47.dll failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_SHADER_COMPILATION_FAILED, "shader compilation failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_SHADER_COMPILATION_OUTPUT, "") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_CONSTANT_BUFFER_FAILED, "CreateBuffer() failed for uniform constant buffer (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_INPUT_LAYOUT_FAILED, "CreateInputLayout() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_RASTERIZER_STATE_FAILED, "CreateRasterizerState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_STENCIL_STATE_FAILED, "CreateDepthStencilState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_BLEND_STATE_FAILED, "CreateBlendState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_RTV_FAILED, "CreateRenderTargetView() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DSV_FAILED, "CreateDepthStencilView() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_UPDATE_BUFFER_FAILED, "Map() failed when updating buffer (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_APPEND_BUFFER_FAILED, "Map() failed when appending to buffer (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_UPDATE_IMAGE_FAILED, "Map() failed when updating image (d3d11)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_BUFFER_FAILED, "failed to create buffer object (metal)") \ + _SG_LOGITEM_XMACRO(METAL_TEXTURE_FORMAT_NOT_SUPPORTED, "pixel format not supported for texture (metal)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_TEXTURE_FAILED, "failed to create texture object (metal)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_SAMPLER_FAILED, "failed to create sampler object (metal)") \ + _SG_LOGITEM_XMACRO(METAL_SHADER_COMPILATION_FAILED, "shader compilation failed (metal)") \ + _SG_LOGITEM_XMACRO(METAL_SHADER_CREATION_FAILED, "shader creation failed (metal)") \ + _SG_LOGITEM_XMACRO(METAL_SHADER_COMPILATION_OUTPUT, "") \ + _SG_LOGITEM_XMACRO(METAL_VERTEX_SHADER_ENTRY_NOT_FOUND, "vertex shader entry function not found (metal)") \ + _SG_LOGITEM_XMACRO(METAL_FRAGMENT_SHADER_ENTRY_NOT_FOUND, "fragment shader entry not found (metal)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_RPS_FAILED, "failed to create render pipeline state (metal)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_RPS_OUTPUT, "") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_DSS_FAILED, "failed to create depth stencil state (metal)") \ + _SG_LOGITEM_XMACRO(WGPU_BINDGROUPS_POOL_EXHAUSTED, "bindgroups pool exhausted (increase sg_desc.bindgroups_cache_size) (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_BINDGROUPSCACHE_SIZE_GREATER_ONE, "sg_desc.wgpu_bindgroups_cache_size must be > 1 (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_BINDGROUPSCACHE_SIZE_POW2, "sg_desc.wgpu_bindgroups_cache_size must be a power of 2 (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_CREATEBINDGROUP_FAILED, "wgpuDeviceCreateBindGroup failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_BUFFER_FAILED, "wgpuDeviceCreateBuffer() failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_TEXTURE_FAILED, "wgpuDeviceCreateTexture() failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_TEXTURE_VIEW_FAILED, "wgpuTextureCreateView() failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_SAMPLER_FAILED, "wgpuDeviceCreateSampler() failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_SHADER_MODULE_FAILED, "wgpuDeviceCreateShaderModule() failed") \ + _SG_LOGITEM_XMACRO(WGPU_SHADER_TOO_MANY_IMAGES, "shader uses too many sampled images on shader stage (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_SHADER_TOO_MANY_SAMPLERS, "shader uses too many samplers on shader stage (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_SHADER_TOO_MANY_STORAGEBUFFERS, "shader uses too many storage buffer bindings on shader stage (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_SHADER_CREATE_BINDGROUP_LAYOUT_FAILED, "wgpuDeviceCreateBindGroupLayout() for shader stage failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_PIPELINE_LAYOUT_FAILED, "wgpuDeviceCreatePipelineLayout() failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_RENDER_PIPELINE_FAILED, "wgpuDeviceCreateRenderPipeline() failed") \ + _SG_LOGITEM_XMACRO(WGPU_ATTACHMENTS_CREATE_TEXTURE_VIEW_FAILED, "wgpuTextureCreateView() failed in create attachments") \ + _SG_LOGITEM_XMACRO(IDENTICAL_COMMIT_LISTENER, "attempting to add identical commit listener") \ + _SG_LOGITEM_XMACRO(COMMIT_LISTENER_ARRAY_FULL, "commit listener array full") \ + _SG_LOGITEM_XMACRO(TRACE_HOOKS_NOT_ENABLED, "sg_install_trace_hooks() called, but SG_TRACE_HOOKS is not defined") \ + _SG_LOGITEM_XMACRO(DEALLOC_BUFFER_INVALID_STATE, "sg_dealloc_buffer(): buffer must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(DEALLOC_IMAGE_INVALID_STATE, "sg_dealloc_image(): image must be in alloc state") \ + _SG_LOGITEM_XMACRO(DEALLOC_SAMPLER_INVALID_STATE, "sg_dealloc_sampler(): sampler must be in alloc state") \ + _SG_LOGITEM_XMACRO(DEALLOC_SHADER_INVALID_STATE, "sg_dealloc_shader(): shader must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(DEALLOC_PIPELINE_INVALID_STATE, "sg_dealloc_pipeline(): pipeline must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(DEALLOC_ATTACHMENTS_INVALID_STATE, "sg_dealloc_attachments(): attachments must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_BUFFER_INVALID_STATE, "sg_init_buffer(): buffer must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_IMAGE_INVALID_STATE, "sg_init_image(): image must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_SAMPLER_INVALID_STATE, "sg_init_sampler(): sampler must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_SHADER_INVALID_STATE, "sg_init_shader(): shader must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_PIPELINE_INVALID_STATE, "sg_init_pipeline(): pipeline must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_ATTACHMENTS_INVALID_STATE, "sg_init_attachments(): pass must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(UNINIT_BUFFER_INVALID_STATE, "sg_uninit_buffer(): buffer must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_IMAGE_INVALID_STATE, "sg_uninit_image(): image must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_SAMPLER_INVALID_STATE, "sg_uninit_sampler(): sampler must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_SHADER_INVALID_STATE, "sg_uninit_shader(): shader must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_PIPELINE_INVALID_STATE, "sg_uninit_pipeline(): pipeline must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_ATTACHMENTS_INVALID_STATE, "sg_uninit_attachments(): attachments must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(FAIL_BUFFER_INVALID_STATE, "sg_fail_buffer(): buffer must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_IMAGE_INVALID_STATE, "sg_fail_image(): image must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_SAMPLER_INVALID_STATE, "sg_fail_sampler(): sampler must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_SHADER_INVALID_STATE, "sg_fail_shader(): shader must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_PIPELINE_INVALID_STATE, "sg_fail_pipeline(): pipeline must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_ATTACHMENTS_INVALID_STATE, "sg_fail_attachments(): attachments must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(BUFFER_POOL_EXHAUSTED, "buffer pool exhausted") \ + _SG_LOGITEM_XMACRO(IMAGE_POOL_EXHAUSTED, "image pool exhausted") \ + _SG_LOGITEM_XMACRO(SAMPLER_POOL_EXHAUSTED, "sampler pool exhausted") \ + _SG_LOGITEM_XMACRO(SHADER_POOL_EXHAUSTED, "shader pool exhausted") \ + _SG_LOGITEM_XMACRO(PIPELINE_POOL_EXHAUSTED, "pipeline pool exhausted") \ + _SG_LOGITEM_XMACRO(PASS_POOL_EXHAUSTED, "pass pool exhausted") \ + _SG_LOGITEM_XMACRO(BEGINPASS_ATTACHMENT_INVALID, "sg_begin_pass: an attachment was provided that no longer exists") \ + _SG_LOGITEM_XMACRO(DRAW_WITHOUT_BINDINGS, "attempting to draw without resource bindings") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_CANARY, "sg_buffer_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_SIZE, "sg_buffer_desc.size and .data.size cannot both be 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_DATA, "immutable buffers must be initialized with data (sg_buffer_desc.data.ptr and sg_buffer_desc.data.size)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_DATA_SIZE, "immutable buffer data size differs from buffer size") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_NO_DATA, "dynamic/stream usage buffers cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_STORAGEBUFFER_SUPPORTED, "storage buffers not supported by the backend 3D API (requires OpenGL >= 4.3)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_STORAGEBUFFER_SIZE_MULTIPLE_4, "size of storage buffers must be a multiple of 4") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDATA_NODATA, "sg_image_data: no data (.ptr and/or .size is zero)") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDATA_DATA_SIZE, "sg_image_data: data size doesn't match expected surface size") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_CANARY, "sg_image_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_WIDTH, "sg_image_desc.width must be > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_HEIGHT, "sg_image_desc.height must be > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_PIXELFORMAT, "invalid pixel format for render-target image") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT, "invalid pixel format for non-render-target image") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT, "non-render-target images cannot be multisampled") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT, "MSAA not supported for this pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_NUM_MIPMAPS, "MSAA images must have num_mipmaps == 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_3D_IMAGE, "3D images cannot have a sample_count > 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE, "3D images cannot have a depth/stencil image format") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_IMMUTABLE, "render target images must be SG_USAGE_IMMUTABLE") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_NO_DATA, "render target images cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_INJECTED_NO_DATA, "images with injected textures cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA, "dynamic/stream images cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE, "compressed images must be immutable") \ + _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_CANARY, "sg_sampler_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_MINFILTER_NONE, "sg_sampler_desc.min_filter cannot be SG_FILTER_NONE") \ + _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_MAGFILTER_NONE, "sg_sampler_desc.mag_filter cannot be SG_FILTER_NONE") \ + _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING, "sg_sampler_desc.max_anisotropy > 1 requires min/mag/mipmap_filter to be SG_FILTER_LINEAR") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_CANARY, "sg_shader_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SOURCE, "shader source code required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_BYTECODE, "shader byte code required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE, "shader source or byte code required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_BYTECODE_SIZE, "shader byte code length (in bytes) required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_UBS, "shader uniform blocks must occupy continuous slots") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS, "uniform block members must occupy continuous slots") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_UB_MEMBERS, "GL backend requires uniform block member declarations") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_MEMBER_NAME, "uniform block member name missing") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_SIZE_MISMATCH, "size of uniform block members doesn't match uniform block size") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_ARRAY_COUNT, "uniform array count must be >= 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE, "uniform arrays only allowed for FLOAT4, INT4, MAT4 in std140 layout") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_STORAGEBUFFERS, "shader stage storage buffers must occupy continuous slots (sg_shader_desc.vs|fs.storage_buffers[])") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_STORAGEBUFFER_READONLY, "shader stage storage buffers must be readonly (sg_shader_desc.vs|fs.storage_buffers[].readonly)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_IMAGES, "shader stage images must occupy continuous slots (sg_shader_desc.vs|fs.images[])") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_SAMPLERS, "shader stage samplers must occupy continuous slots (sg_shader_desc.vs|fs.samplers[])") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_IMAGE_SLOT_OUT_OF_RANGE, "shader stage: image-sampler-pair image slot index is out of range (sg_shader_desc.vs|fs.image_sampler_pairs[].image_slot)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_SAMPLER_SLOT_OUT_OF_RANGE, "shader stage: image-sampler-pair image slot index is out of range (sg_shader_desc.vs|fs.image_sampler_pairs[].sampler_slot)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_NAME_REQUIRED_FOR_GL, "shader stage: image-sampler-pairs must be named in GL (sg_shader_desc.vs|fs.image_sampler_pairs[].name)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_NAME_BUT_NOT_USED, "shader stage: image-sampler-pair has name but .used field not true") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_IMAGE_BUT_NOT_USED, "shader stage: image-sampler-pair has .image_slot != 0 but .used field not true") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_SAMPLER_BUT_NOT_USED, "shader stage: image-sampler-pair .sampler_slot != 0 but .used field not true") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NONFILTERING_SAMPLER_REQUIRED, "shader stage: image sample type UNFILTERABLE_FLOAT, UINT, SINT can only be used with NONFILTERING sampler") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_COMPARISON_SAMPLER_REQUIRED, "shader stage: image sample type DEPTH can only be used with COMPARISON sampler") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS, "shader stage: one or more images are note referenced by (sg_shader_desc.vs|fs.image_sampler_pairs[].image_slot)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS, "shader stage: one or more samplers are not referenced by image-sampler-pairs (sg_shader_desc.vs|fs.image_sampler_pairs[].sampler_slot)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_IMAGE_SAMPLER_PAIRS, "shader stage image-sampler-pairs must occupy continuous slots (sg_shader_desc.vs|fs.image_samplers[])") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG, "vertex attribute name/semantic string too long (max len 16)") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_CANARY, "sg_pipeline_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_SHADER, "sg_pipeline_desc.shader missing or invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_NO_CONT_ATTRS, "sg_pipeline_desc.layout.attrs is not continuous") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4, "sg_pipeline_desc.layout.buffers[].stride must be multiple of 4") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_ATTR_SEMANTICS, "D3D11 missing vertex attribute semantics in shader") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_CANARY, "sg_attachments_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_NO_ATTACHMENTS, "sg_attachments_desc no color or depth-stencil attachments") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_NO_CONT_COLOR_ATTS, "color attachments must occupy continuous slots") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_IMAGE, "pass attachment image is not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_MIPLEVEL, "pass attachment mip level is bigger than image has mipmaps") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_FACE, "pass attachment image is cubemap, but face index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_LAYER, "pass attachment image is array texture, but layer index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_SLICE, "pass attachment image is 3d texture, but slice value is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_IMAGE_NO_RT, "pass attachment image must be have render_target=true") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_COLOR_INV_PIXELFORMAT, "pass color-attachment images must be renderable color pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_INV_PIXELFORMAT, "pass depth-attachment image must be depth or depth-stencil pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_IMAGE_SIZES, "all pass attachments must have the same size") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_IMAGE_SAMPLE_COUNTS, "all pass attachments must have the same sample count") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_COLOR_IMAGE_MSAA, "pass resolve attachments must have a color attachment image with sample count > 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE, "pass resolve attachment image not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_SAMPLE_COUNT, "pass resolve attachment image sample count must be 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_MIPLEVEL, "pass resolve attachment mip level is bigger than image has mipmaps") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_FACE, "pass resolve attachment is cubemap, but face index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_LAYER, "pass resolve attachment is array texture, but layer index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_SLICE, "pass resolve attachment is 3d texture, but slice value is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_NO_RT, "pass resolve attachment image must have render_target=true") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_SIZES, "pass resolve attachment size must match color attachment image size") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_FORMAT, "pass resolve attachment pixel format must match color attachment pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE, "pass depth attachment image is not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_MIPLEVEL, "pass depth attachment mip level is bigger than image has mipmaps") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_FACE, "pass depth attachment image is cubemap, but face index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_LAYER, "pass depth attachment image is array texture, but layer index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_SLICE, "pass depth attachment image is 3d texture, but slice value is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_NO_RT, "pass depth attachment image must be have render_target=true") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SIZES, "pass depth attachment image size must match color attachment image size") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SAMPLE_COUNT, "pass depth attachment sample count must match color attachment sample count") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_CANARY, "sg_begin_pass: pass struct not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_ATTACHMENTS_EXISTS, "sg_begin_pass: attachments object no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_ATTACHMENTS_VALID, "sg_begin_pass: attachments object not in resource state VALID") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLOR_ATTACHMENT_IMAGE, "sg_begin_pass: one or more color attachment images are not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVE_ATTACHMENT_IMAGE, "sg_begin_pass: one or more resolve attachment images are not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCIL_ATTACHMENT_IMAGE, "sg_begin_pass: one or more depth-stencil attachment images are not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH, "sg_begin_pass: expected pass.swapchain.width > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH_NOTSET, "sg_begin_pass: expected pass.swapchain.width == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT, "sg_begin_pass: expected pass.swapchain.height > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT_NOTSET, "sg_begin_pass: expected pass.swapchain.height == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT, "sg_begin_pass: expected pass.swapchain.sample_count > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT_NOTSET, "sg_begin_pass: expected pass.swapchain.sample_count == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT, "sg_begin_pass: expected pass.swapchain.color_format to be valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT_NOTSET, "sg_begin_pass: expected pass.swapchain.color_format to be unset") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_DEPTHFORMAT_NOTSET, "sg_begin_pass: expected pass.swapchain.depth_format to be unset") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE, "sg_begin_pass: expected pass.swapchain.metal.current_drawable != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE_NOTSET, "sg_begin_pass: expected pass.swapchain.metal.current_drawable == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE, "sg_begin_pass: expected pass.swapchain.metal.depth_stencil_texture != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE_NOTSET, "sg_begin_pass: expected pass.swapchain.metal.depth_stencil_texture == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE, "sg_begin_pass: expected pass.swapchain.metal.msaa_color_texture != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE_NOTSET, "sg_begin_pass: expected pass.swapchain.metal.msaa_color_texture == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW, "sg_begin_pass: expected pass.swapchain.d3d11.render_view != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.d3d11.render_view == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW, "sg_begin_pass: expected pass.swapchain.d3d11.resolve_view != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.d3d11.resolve_view == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW, "sg_begin_pass: expected pass.swapchain.d3d11.depth_stencil_view != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.d3d11.depth_stencil_view == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW, "sg_begin_pass: expected pass.swapchain.wgpu.render_view != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.wgpu.render_view == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW, "sg_begin_pass: expected pass.swapchain.wgpu.resolve_view != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.wgpu.resolve_view == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW, "sg_begin_pass: expected pass.swapchain.wgpu.depth_stencil_view != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.wgpu.depth_stencil_view == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_GL_EXPECT_FRAMEBUFFER_NOTSET, "sg_begin_pass: expected pass.swapchain.gl.framebuffer == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_VALID_ID, "sg_apply_pipeline: invalid pipeline id provided") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_EXISTS, "sg_apply_pipeline: pipeline object no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_VALID, "sg_apply_pipeline: pipeline object not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_SHADER_EXISTS, "sg_apply_pipeline: shader object no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_SHADER_VALID, "sg_apply_pipeline: shader object not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_CURPASS_ATTACHMENTS_EXISTS, "sg_apply_pipeline: current pass attachments no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_CURPASS_ATTACHMENTS_VALID, "sg_apply_pipeline: current pass attachments not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_ATT_COUNT, "sg_apply_pipeline: number of pipeline color attachments doesn't match number of pass color attachments") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_COLOR_FORMAT, "sg_apply_pipeline: pipeline color attachment pixel format doesn't match pass color attachment pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_DEPTH_FORMAT, "sg_apply_pipeline: pipeline depth pixel_format doesn't match pass depth attachment pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_SAMPLE_COUNT, "sg_apply_pipeline: pipeline MSAA sample count doesn't match render pass attachment sample count") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE, "sg_apply_bindings: must be called after sg_apply_pipeline") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE_EXISTS, "sg_apply_bindings: currently applied pipeline object no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE_VALID, "sg_apply_bindings: currently applied pipeline object not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VBS, "sg_apply_bindings: number of vertex buffers doesn't match number of pipeline vertex layouts") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VB_EXISTS, "sg_apply_bindings: vertex buffer no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VB_TYPE, "sg_apply_bindings: buffer in vertex buffer slot is not a SG_BUFFERTYPE_VERTEXBUFFER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VB_OVERFLOW, "sg_apply_bindings: buffer in vertex buffer slot is overflown") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_NO_IB, "sg_apply_bindings: pipeline object defines indexed rendering, but no index buffer provided") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB, "sg_apply_bindings: pipeline object defines non-indexed rendering, but index buffer provided") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_EXISTS, "sg_apply_bindings: index buffer no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_TYPE, "sg_apply_bindings: buffer in index buffer slot is not a SG_BUFFERTYPE_INDEXBUFFER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_OVERFLOW, "sg_apply_bindings: buffer in index buffer slot is overflown") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_IMAGE_BINDING, "sg_apply_bindings: image binding on vertex stage is missing or the image handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMG_EXISTS, "sg_apply_bindings: image bound to vertex stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMAGE_TYPE_MISMATCH, "sg_apply_bindings: type of image bound to vertex stage doesn't match shader desc") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMAGE_MSAA, "sg_apply_bindings: cannot bind image with sample_count>1 to vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_FILTERABLE_IMAGE, "sg_apply_bindings: filterable image expected on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_DEPTH_IMAGE, "sg_apply_bindings: depth image expected on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_IMAGE_BINDING, "sg_apply_bindings: unexpected image binding on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_SAMPLER_BINDING, "sg_apply_bindings: sampler binding on vertex stage is missing or the sampler handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_COMPARISON on vertex stage but sampler has SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_FILTERING or SG_SAMPLERTYPE_NONFILTERING on vertex stage but sampler doesn't have SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_NONFILTERING_SAMPLER, "sg_apply_bindings: shader expected SG_SAMPLERTYPE_NONFILTERING on vertex stage, but sampler has SG_FILTER_LINEAR filters") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_BINDING, "sg_apply_bindings: unexpected sampler binding on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_SMP_EXISTS, "sg_apply_bindings: sampler bound to vertex stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_STORAGEBUFFER_BINDING, "sg_apply_bindings: storage buffer binding on vertex stage is missing or the buffer handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_STORAGEBUFFER_EXISTS, "sg_apply_bindings: storage buffer bound to vertex stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_STORAGEBUFFER_BINDING_BUFFERTYPE, "sg_apply_bindings: buffer bound to vertex stage storage buffer slot is not of type storage buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_STORAGEBUFFER_BINDING, "sg_apply_bindings: unexpected storage buffer binding on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_IMAGE_BINDING, "sg_apply_bindings: image binding on fragment stage is missing or the image handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMG_EXISTS, "sg_apply_bindings: image bound to fragment stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMAGE_TYPE_MISMATCH, "sg_apply_bindings: type of image bound to fragment stage doesn't match shader desc") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMAGE_MSAA, "sg_apply_bindings: cannot bind image with sample_count>1 to fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_FILTERABLE_IMAGE, "sg_apply_bindings: filterable image expected on fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_DEPTH_IMAGE, "sg_apply_bindings: depth image expected on fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_IMAGE_BINDING, "sg_apply_bindings: unexpected image binding on fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_SAMPLER_BINDING, "sg_apply_bindings: sampler binding on fragment stage is missing or the sampler handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_COMPARISON on fragment stage but sampler has SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_FILTERING on fragment stage but sampler doesn't have SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_NONFILTERING_SAMPLER, "sg_apply_bindings: shader expected SG_SAMPLERTYPE_NONFILTERING on fragment stage, but sampler has SG_FILTER_LINEAR filters") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_BINDING, "sg_apply_bindings: unexpected sampler binding on fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_SMP_EXISTS, "sg_apply_bindings: sampler bound to fragment stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_STORAGEBUFFER_BINDING, "sg_apply_bindings: storage buffer binding on fragment stage is missing or the buffer handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_STORAGEBUFFER_EXISTS, "sg_apply_bindings: storage buffer bound to fragment stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_STORAGEBUFFER_BINDING_BUFFERTYPE, "sg_apply_bindings: buffer bound to frahment stage storage buffer slot is not of type storage buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_STORAGEBUFFER_BINDING, "sg_apply_bindings: unexpected storage buffer binding on fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_AUB_NO_PIPELINE, "sg_apply_uniforms: must be called after sg_apply_pipeline()") \ + _SG_LOGITEM_XMACRO(VALIDATE_AUB_NO_UB_AT_SLOT, "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot") \ + _SG_LOGITEM_XMACRO(VALIDATE_AUB_SIZE, "sg_apply_uniforms: data size doesn't match declared uniform block size") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_USAGE, "sg_update_buffer: cannot update immutable buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_SIZE, "sg_update_buffer: update size is bigger than buffer size") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_ONCE, "sg_update_buffer: only one update allowed per buffer and frame") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_APPEND, "sg_update_buffer: cannot call sg_update_buffer and sg_append_buffer in same frame") \ + _SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_USAGE, "sg_append_buffer: cannot append to immutable buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_SIZE, "sg_append_buffer: overall appended size is bigger than buffer size") \ + _SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_UPDATE, "sg_append_buffer: cannot call sg_append_buffer and sg_update_buffer in same frame") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDIMG_USAGE, "sg_update_image: cannot update immutable image") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDIMG_ONCE, "sg_update_image: only one update allowed per image and frame") \ + _SG_LOGITEM_XMACRO(VALIDATION_FAILED, "validation layer checks failed") \ + +#define _SG_LOGITEM_XMACRO(item,msg) SG_LOGITEM_##item, +typedef enum sg_log_item { + _SG_LOG_ITEMS +} sg_log_item; +#undef _SG_LOGITEM_XMACRO + +/* + sg_desc + + The sg_desc struct contains configuration values for sokol_gfx, + it is used as parameter to the sg_setup() call. + + The default configuration is: + + .buffer_pool_size 128 + .image_pool_size 128 + .sampler_pool_size 64 + .shader_pool_size 32 + .pipeline_pool_size 64 + .pass_pool_size 16 + .uniform_buffer_size 4 MB (4*1024*1024) + .max_commit_listeners 1024 + .disable_validation false + .mtl_force_managed_storage_mode false + .wgpu_disable_bindgroups_cache false + .wgpu_bindgroups_cache_size 1024 + + .allocator.alloc_fn 0 (in this case, malloc() will be called) + .allocator.free_fn 0 (in this case, free() will be called) + .allocator.user_data 0 + + .environment.defaults.color_format: default value depends on selected backend: + all GL backends: SG_PIXELFORMAT_RGBA8 + Metal and D3D11: SG_PIXELFORMAT_BGRA8 + WebGPU: *no default* (must be queried from WebGPU swapchain object) + .environment.defaults.depth_format: SG_PIXELFORMAT_DEPTH_STENCIL + .environment.defaults.sample_count: 1 + + Metal specific: + (NOTE: All Objective-C object references are transferred through + a bridged (const void*) to sokol_gfx, which will use a unretained + bridged cast (__bridged id) to retrieve the Objective-C + references back. Since the bridge cast is unretained, the caller + must hold a strong reference to the Objective-C object for the + duration of the sokol_gfx call! + + .mtl_force_managed_storage_mode + when enabled, Metal buffers and texture resources are created in managed storage + mode, otherwise sokol-gfx will decide whether to create buffers and + textures in managed or shared storage mode (this is mainly a debugging option) + .mtl_use_command_buffer_with_retained_references + when true, the sokol-gfx Metal backend will use Metal command buffers which + bump the reference count of resource objects as long as they are inflight, + this is slower than the default command-buffer-with-unretained-references + method, this may be a workaround when confronted with lifetime validation + errors from the Metal validation layer until a proper fix has been implemented + .environment.metal.device + a pointer to the MTLDevice object + + D3D11 specific: + .environment.d3d11.device + a pointer to the ID3D11Device object, this must have been created + before sg_setup() is called + .environment.d3d11.device_context + a pointer to the ID3D11DeviceContext object + + WebGPU specific: + .wgpu_disable_bindgroups_cache + When this is true, the WebGPU backend will create and immediately + release a BindGroup object in the sg_apply_bindings() call, only + use this for debugging purposes. + .wgpu_bindgroups_cache_size + The size of the bindgroups cache for re-using BindGroup objects + between sg_apply_bindings() calls. The smaller the cache size, + the more likely are cache slot collisions which will cause + a BindGroups object to be destroyed and a new one created. + Use the information returned by sg_query_stats() to check + if this is a frequent occurrence, and increase the cache size as + needed (the default is 1024). + NOTE: wgpu_bindgroups_cache_size must be a power-of-2 number! + .environment.wgpu.device + a WGPUDevice handle + + When using sokol_gfx.h and sokol_app.h together, consider using the + helper function sglue_environment() in the sokol_glue.h header to + initialize the sg_desc.environment nested struct. sglue_environment() returns + a completely initialized sg_environment struct with information + provided by sokol_app.h. +*/ +typedef struct sg_environment_defaults { + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; +} sg_environment_defaults; + +typedef struct sg_metal_environment { + const void* device; +} sg_metal_environment; + +typedef struct sg_d3d11_environment { + const void* device; + const void* device_context; +} sg_d3d11_environment; + +typedef struct sg_wgpu_environment { + const void* device; +} sg_wgpu_environment; + +typedef struct sg_environment { + sg_environment_defaults defaults; + sg_metal_environment metal; + sg_d3d11_environment d3d11; + sg_wgpu_environment wgpu; +} sg_environment; + +/* + sg_commit_listener + + Used with function sg_add_commit_listener() to add a callback + which will be called in sg_commit(). This is useful for libraries + building on top of sokol-gfx to be notified about when a frame + ends (instead of having to guess, or add a manual 'new-frame' + function. +*/ +typedef struct sg_commit_listener { + void (*func)(void* user_data); + void* user_data; +} sg_commit_listener; + +/* + sg_allocator + + Used in sg_desc to provide custom memory-alloc and -free functions + to sokol_gfx.h. If memory management should be overridden, both the + alloc_fn and free_fn function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct sg_allocator { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} sg_allocator; + +/* + sg_logger + + Used in sg_desc to provide a logging function. Please be aware + that without logging function, sokol-gfx will be completely + silent, e.g. it will not report errors, warnings and + validation layer messages. For maximum error verbosity, + compile in debug mode (e.g. NDEBUG *not* defined) and provide a + compatible logger function in the sg_setup() call + (for instance the standard logging function from sokol_log.h). +*/ +typedef struct sg_logger { + void (*func)( + const char* tag, // always "sg" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SG_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_gfx.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} sg_logger; + +typedef struct sg_desc { + uint32_t _start_canary; + int buffer_pool_size; + int image_pool_size; + int sampler_pool_size; + int shader_pool_size; + int pipeline_pool_size; + int attachments_pool_size; + int uniform_buffer_size; + int max_commit_listeners; + bool disable_validation; // disable validation layer even in debug mode, useful for tests + bool mtl_force_managed_storage_mode; // for debugging: use Metal managed storage mode for resources even with UMA + bool mtl_use_command_buffer_with_retained_references; // Metal: use a managed MTLCommandBuffer which ref-counts used resources + bool wgpu_disable_bindgroups_cache; // set to true to disable the WebGPU backend BindGroup cache + int wgpu_bindgroups_cache_size; // number of slots in the WebGPU bindgroup cache (must be 2^N) + sg_allocator allocator; + sg_logger logger; // optional log function override + sg_environment environment; + uint32_t _end_canary; +} sg_desc; + +// setup and misc functions +SOKOL_GFX_API_DECL void sg_setup(const sg_desc* desc); +SOKOL_GFX_API_DECL void sg_shutdown(void); +SOKOL_GFX_API_DECL bool sg_isvalid(void); +SOKOL_GFX_API_DECL void sg_reset_state_cache(void); +SOKOL_GFX_API_DECL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks); +SOKOL_GFX_API_DECL void sg_push_debug_group(const char* name); +SOKOL_GFX_API_DECL void sg_pop_debug_group(void); +SOKOL_GFX_API_DECL bool sg_add_commit_listener(sg_commit_listener listener); +SOKOL_GFX_API_DECL bool sg_remove_commit_listener(sg_commit_listener listener); + +// resource creation, destruction and updating +SOKOL_GFX_API_DECL sg_buffer sg_make_buffer(const sg_buffer_desc* desc); +SOKOL_GFX_API_DECL sg_image sg_make_image(const sg_image_desc* desc); +SOKOL_GFX_API_DECL sg_sampler sg_make_sampler(const sg_sampler_desc* desc); +SOKOL_GFX_API_DECL sg_shader sg_make_shader(const sg_shader_desc* desc); +SOKOL_GFX_API_DECL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc); +SOKOL_GFX_API_DECL sg_attachments sg_make_attachments(const sg_attachments_desc* desc); +SOKOL_GFX_API_DECL void sg_destroy_buffer(sg_buffer buf); +SOKOL_GFX_API_DECL void sg_destroy_image(sg_image img); +SOKOL_GFX_API_DECL void sg_destroy_sampler(sg_sampler smp); +SOKOL_GFX_API_DECL void sg_destroy_shader(sg_shader shd); +SOKOL_GFX_API_DECL void sg_destroy_pipeline(sg_pipeline pip); +SOKOL_GFX_API_DECL void sg_destroy_attachments(sg_attachments atts); +SOKOL_GFX_API_DECL void sg_update_buffer(sg_buffer buf, const sg_range* data); +SOKOL_GFX_API_DECL void sg_update_image(sg_image img, const sg_image_data* data); +SOKOL_GFX_API_DECL int sg_append_buffer(sg_buffer buf, const sg_range* data); +SOKOL_GFX_API_DECL bool sg_query_buffer_overflow(sg_buffer buf); +SOKOL_GFX_API_DECL bool sg_query_buffer_will_overflow(sg_buffer buf, size_t size); + +// rendering functions +SOKOL_GFX_API_DECL void sg_begin_pass(const sg_pass* pass); +SOKOL_GFX_API_DECL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left); +SOKOL_GFX_API_DECL void sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left); +SOKOL_GFX_API_DECL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left); +SOKOL_GFX_API_DECL void sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left); +SOKOL_GFX_API_DECL void sg_apply_pipeline(sg_pipeline pip); +SOKOL_GFX_API_DECL void sg_apply_bindings(const sg_bindings* bindings); +SOKOL_GFX_API_DECL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data); +SOKOL_GFX_API_DECL void sg_draw(int base_element, int num_elements, int num_instances); +SOKOL_GFX_API_DECL void sg_end_pass(void); +SOKOL_GFX_API_DECL void sg_commit(void); + +// getting information +SOKOL_GFX_API_DECL sg_desc sg_query_desc(void); +SOKOL_GFX_API_DECL sg_backend sg_query_backend(void); +SOKOL_GFX_API_DECL sg_features sg_query_features(void); +SOKOL_GFX_API_DECL sg_limits sg_query_limits(void); +SOKOL_GFX_API_DECL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt); +SOKOL_GFX_API_DECL int sg_query_row_pitch(sg_pixel_format fmt, int width, int row_align_bytes); +SOKOL_GFX_API_DECL int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes); +// get current state of a resource (INITIAL, ALLOC, VALID, FAILED, INVALID) +SOKOL_GFX_API_DECL sg_resource_state sg_query_buffer_state(sg_buffer buf); +SOKOL_GFX_API_DECL sg_resource_state sg_query_image_state(sg_image img); +SOKOL_GFX_API_DECL sg_resource_state sg_query_sampler_state(sg_sampler smp); +SOKOL_GFX_API_DECL sg_resource_state sg_query_shader_state(sg_shader shd); +SOKOL_GFX_API_DECL sg_resource_state sg_query_pipeline_state(sg_pipeline pip); +SOKOL_GFX_API_DECL sg_resource_state sg_query_attachments_state(sg_attachments atts); +// get runtime information about a resource +SOKOL_GFX_API_DECL sg_buffer_info sg_query_buffer_info(sg_buffer buf); +SOKOL_GFX_API_DECL sg_image_info sg_query_image_info(sg_image img); +SOKOL_GFX_API_DECL sg_sampler_info sg_query_sampler_info(sg_sampler smp); +SOKOL_GFX_API_DECL sg_shader_info sg_query_shader_info(sg_shader shd); +SOKOL_GFX_API_DECL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip); +SOKOL_GFX_API_DECL sg_attachments_info sg_query_attachments_info(sg_attachments atts); +// get desc structs matching a specific resource (NOTE that not all creation attributes may be provided) +SOKOL_GFX_API_DECL sg_buffer_desc sg_query_buffer_desc(sg_buffer buf); +SOKOL_GFX_API_DECL sg_image_desc sg_query_image_desc(sg_image img); +SOKOL_GFX_API_DECL sg_sampler_desc sg_query_sampler_desc(sg_sampler smp); +SOKOL_GFX_API_DECL sg_shader_desc sg_query_shader_desc(sg_shader shd); +SOKOL_GFX_API_DECL sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip); +SOKOL_GFX_API_DECL sg_attachments_desc sg_query_attachments_desc(sg_attachments atts); +// get resource creation desc struct with their default values replaced +SOKOL_GFX_API_DECL sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc); +SOKOL_GFX_API_DECL sg_image_desc sg_query_image_defaults(const sg_image_desc* desc); +SOKOL_GFX_API_DECL sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc); +SOKOL_GFX_API_DECL sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc); +SOKOL_GFX_API_DECL sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc); +SOKOL_GFX_API_DECL sg_attachments_desc sg_query_attachments_defaults(const sg_attachments_desc* desc); + +// separate resource allocation and initialization (for async setup) +SOKOL_GFX_API_DECL sg_buffer sg_alloc_buffer(void); +SOKOL_GFX_API_DECL sg_image sg_alloc_image(void); +SOKOL_GFX_API_DECL sg_sampler sg_alloc_sampler(void); +SOKOL_GFX_API_DECL sg_shader sg_alloc_shader(void); +SOKOL_GFX_API_DECL sg_pipeline sg_alloc_pipeline(void); +SOKOL_GFX_API_DECL sg_attachments sg_alloc_attachments(void); +SOKOL_GFX_API_DECL void sg_dealloc_buffer(sg_buffer buf); +SOKOL_GFX_API_DECL void sg_dealloc_image(sg_image img); +SOKOL_GFX_API_DECL void sg_dealloc_sampler(sg_sampler smp); +SOKOL_GFX_API_DECL void sg_dealloc_shader(sg_shader shd); +SOKOL_GFX_API_DECL void sg_dealloc_pipeline(sg_pipeline pip); +SOKOL_GFX_API_DECL void sg_dealloc_attachments(sg_attachments attachments); +SOKOL_GFX_API_DECL void sg_init_buffer(sg_buffer buf, const sg_buffer_desc* desc); +SOKOL_GFX_API_DECL void sg_init_image(sg_image img, const sg_image_desc* desc); +SOKOL_GFX_API_DECL void sg_init_sampler(sg_sampler smg, const sg_sampler_desc* desc); +SOKOL_GFX_API_DECL void sg_init_shader(sg_shader shd, const sg_shader_desc* desc); +SOKOL_GFX_API_DECL void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc* desc); +SOKOL_GFX_API_DECL void sg_init_attachments(sg_attachments attachments, const sg_attachments_desc* desc); +SOKOL_GFX_API_DECL void sg_uninit_buffer(sg_buffer buf); +SOKOL_GFX_API_DECL void sg_uninit_image(sg_image img); +SOKOL_GFX_API_DECL void sg_uninit_sampler(sg_sampler smp); +SOKOL_GFX_API_DECL void sg_uninit_shader(sg_shader shd); +SOKOL_GFX_API_DECL void sg_uninit_pipeline(sg_pipeline pip); +SOKOL_GFX_API_DECL void sg_uninit_attachments(sg_attachments atts); +SOKOL_GFX_API_DECL void sg_fail_buffer(sg_buffer buf); +SOKOL_GFX_API_DECL void sg_fail_image(sg_image img); +SOKOL_GFX_API_DECL void sg_fail_sampler(sg_sampler smp); +SOKOL_GFX_API_DECL void sg_fail_shader(sg_shader shd); +SOKOL_GFX_API_DECL void sg_fail_pipeline(sg_pipeline pip); +SOKOL_GFX_API_DECL void sg_fail_attachments(sg_attachments atts); + +// frame stats +SOKOL_GFX_API_DECL void sg_enable_frame_stats(void); +SOKOL_GFX_API_DECL void sg_disable_frame_stats(void); +SOKOL_GFX_API_DECL bool sg_frame_stats_enabled(void); +SOKOL_GFX_API_DECL sg_frame_stats sg_query_frame_stats(void); + +/* Backend-specific structs and functions, these may come in handy for mixing + sokol-gfx rendering with 'native backend' rendering functions. + + This group of functions will be expanded as needed. +*/ + +typedef struct sg_d3d11_buffer_info { + const void* buf; // ID3D11Buffer* +} sg_d3d11_buffer_info; + +typedef struct sg_d3d11_image_info { + const void* tex2d; // ID3D11Texture2D* + const void* tex3d; // ID3D11Texture3D* + const void* res; // ID3D11Resource* (either tex2d or tex3d) + const void* srv; // ID3D11ShaderResourceView* +} sg_d3d11_image_info; + +typedef struct sg_d3d11_sampler_info { + const void* smp; // ID3D11SamplerState* +} sg_d3d11_sampler_info; + +typedef struct sg_d3d11_shader_info { + const void* vs_cbufs[SG_MAX_SHADERSTAGE_UBS]; // ID3D11Buffer* (vertex stage constant buffers) + const void* fs_cbufs[SG_MAX_SHADERSTAGE_UBS]; // ID3D11Buffer* (fragment stage constant buffers) + const void* vs; // ID3D11VertexShader* + const void* fs; // ID3D11PixelShader* +} sg_d3d11_shader_info; + +typedef struct sg_d3d11_pipeline_info { + const void* il; // ID3D11InputLayout* + const void* rs; // ID3D11RasterizerState* + const void* dss; // ID3D11DepthStencilState* + const void* bs; // ID3D11BlendState* +} sg_d3d11_pipeline_info; + +typedef struct sg_d3d11_attachments_info { + const void* color_rtv[SG_MAX_COLOR_ATTACHMENTS]; // ID3D11RenderTargetView + const void* resolve_rtv[SG_MAX_COLOR_ATTACHMENTS]; // ID3D11RenderTargetView + const void* dsv; // ID3D11DepthStencilView +} sg_d3d11_attachments_info; + +typedef struct sg_mtl_buffer_info { + const void* buf[SG_NUM_INFLIGHT_FRAMES]; // id + int active_slot; +} sg_mtl_buffer_info; + +typedef struct sg_mtl_image_info { + const void* tex[SG_NUM_INFLIGHT_FRAMES]; // id + int active_slot; +} sg_mtl_image_info; + +typedef struct sg_mtl_sampler_info { + const void* smp; // id +} sg_mtl_sampler_info; + +typedef struct sg_mtl_shader_info { + const void* vs_lib; // id + const void* fs_lib; // id + const void* vs_func; // id + const void* fs_func; // id +} sg_mtl_shader_info; + +typedef struct sg_mtl_pipeline_info { + const void* rps; // id + const void* dss; // id +} sg_mtl_pipeline_info; + +typedef struct sg_wgpu_buffer_info { + const void* buf; // WGPUBuffer +} sg_wgpu_buffer_info; + +typedef struct sg_wgpu_image_info { + const void* tex; // WGPUTexture + const void* view; // WGPUTextureView +} sg_wgpu_image_info; + +typedef struct sg_wgpu_sampler_info { + const void* smp; // WGPUSampler +} sg_wgpu_sampler_info; + +typedef struct sg_wgpu_shader_info { + const void* vs_mod; // WGPUShaderModule + const void* fs_mod; // WGPUShaderModule + const void* bgl; // WGPUBindGroupLayout; +} sg_wgpu_shader_info; + +typedef struct sg_wgpu_pipeline_info { + const void* pip; // WGPURenderPipeline +} sg_wgpu_pipeline_info; + +typedef struct sg_wgpu_attachments_info { + const void* color_view[SG_MAX_COLOR_ATTACHMENTS]; // WGPUTextureView + const void* resolve_view[SG_MAX_COLOR_ATTACHMENTS]; // WGPUTextureView + const void* ds_view; // WGPUTextureView +} sg_wgpu_attachments_info; + +typedef struct sg_gl_buffer_info { + uint32_t buf[SG_NUM_INFLIGHT_FRAMES]; + int active_slot; +} sg_gl_buffer_info; + +typedef struct sg_gl_image_info { + uint32_t tex[SG_NUM_INFLIGHT_FRAMES]; + uint32_t tex_target; + uint32_t msaa_render_buffer; + int active_slot; +} sg_gl_image_info; + +typedef struct sg_gl_sampler_info { + uint32_t smp; +} sg_gl_sampler_info; + +typedef struct sg_gl_shader_info { + uint32_t prog; +} sg_gl_shader_info; + +typedef struct sg_gl_attachments_info { + uint32_t framebuffer; + uint32_t msaa_resolve_framebuffer[SG_MAX_COLOR_ATTACHMENTS]; +} sg_gl_attachments_info; + +// D3D11: return ID3D11Device +SOKOL_GFX_API_DECL const void* sg_d3d11_device(void); +// D3D11: return ID3D11DeviceContext +SOKOL_GFX_API_DECL const void* sg_d3d11_device_context(void); +// D3D11: get internal buffer resource objects +SOKOL_GFX_API_DECL sg_d3d11_buffer_info sg_d3d11_query_buffer_info(sg_buffer buf); +// D3D11: get internal image resource objects +SOKOL_GFX_API_DECL sg_d3d11_image_info sg_d3d11_query_image_info(sg_image img); +// D3D11: get internal sampler resource objects +SOKOL_GFX_API_DECL sg_d3d11_sampler_info sg_d3d11_query_sampler_info(sg_sampler smp); +// D3D11: get internal shader resource objects +SOKOL_GFX_API_DECL sg_d3d11_shader_info sg_d3d11_query_shader_info(sg_shader shd); +// D3D11: get internal pipeline resource objects +SOKOL_GFX_API_DECL sg_d3d11_pipeline_info sg_d3d11_query_pipeline_info(sg_pipeline pip); +// D3D11: get internal pass resource objects +SOKOL_GFX_API_DECL sg_d3d11_attachments_info sg_d3d11_query_attachments_info(sg_attachments atts); + +// Metal: return __bridge-casted MTLDevice +SOKOL_GFX_API_DECL const void* sg_mtl_device(void); +// Metal: return __bridge-casted MTLRenderCommandEncoder in current pass (or zero if outside pass) +SOKOL_GFX_API_DECL const void* sg_mtl_render_command_encoder(void); +// Metal: get internal __bridge-casted buffer resource objects +SOKOL_GFX_API_DECL sg_mtl_buffer_info sg_mtl_query_buffer_info(sg_buffer buf); +// Metal: get internal __bridge-casted image resource objects +SOKOL_GFX_API_DECL sg_mtl_image_info sg_mtl_query_image_info(sg_image img); +// Metal: get internal __bridge-casted sampler resource objects +SOKOL_GFX_API_DECL sg_mtl_sampler_info sg_mtl_query_sampler_info(sg_sampler smp); +// Metal: get internal __bridge-casted shader resource objects +SOKOL_GFX_API_DECL sg_mtl_shader_info sg_mtl_query_shader_info(sg_shader shd); +// Metal: get internal __bridge-casted pipeline resource objects +SOKOL_GFX_API_DECL sg_mtl_pipeline_info sg_mtl_query_pipeline_info(sg_pipeline pip); + +// WebGPU: return WGPUDevice object +SOKOL_GFX_API_DECL const void* sg_wgpu_device(void); +// WebGPU: return WGPUQueue object +SOKOL_GFX_API_DECL const void* sg_wgpu_queue(void); +// WebGPU: return this frame's WGPUCommandEncoder +SOKOL_GFX_API_DECL const void* sg_wgpu_command_encoder(void); +// WebGPU: return WGPURenderPassEncoder of current pass +SOKOL_GFX_API_DECL const void* sg_wgpu_render_pass_encoder(void); +// WebGPU: get internal buffer resource objects +SOKOL_GFX_API_DECL sg_wgpu_buffer_info sg_wgpu_query_buffer_info(sg_buffer buf); +// WebGPU: get internal image resource objects +SOKOL_GFX_API_DECL sg_wgpu_image_info sg_wgpu_query_image_info(sg_image img); +// WebGPU: get internal sampler resource objects +SOKOL_GFX_API_DECL sg_wgpu_sampler_info sg_wgpu_query_sampler_info(sg_sampler smp); +// WebGPU: get internal shader resource objects +SOKOL_GFX_API_DECL sg_wgpu_shader_info sg_wgpu_query_shader_info(sg_shader shd); +// WebGPU: get internal pipeline resource objects +SOKOL_GFX_API_DECL sg_wgpu_pipeline_info sg_wgpu_query_pipeline_info(sg_pipeline pip); +// WebGPU: get internal pass resource objects +SOKOL_GFX_API_DECL sg_wgpu_attachments_info sg_wgpu_query_attachments_info(sg_attachments atts); + +// GL: get internal buffer resource objects +SOKOL_GFX_API_DECL sg_gl_buffer_info sg_gl_query_buffer_info(sg_buffer buf); +// GL: get internal image resource objects +SOKOL_GFX_API_DECL sg_gl_image_info sg_gl_query_image_info(sg_image img); +// GL: get internal sampler resource objects +SOKOL_GFX_API_DECL sg_gl_sampler_info sg_gl_query_sampler_info(sg_sampler smp); +// GL: get internal shader resource objects +SOKOL_GFX_API_DECL sg_gl_shader_info sg_gl_query_shader_info(sg_shader shd); +// GL: get internal pass resource objects +SOKOL_GFX_API_DECL sg_gl_attachments_info sg_gl_query_attachments_info(sg_attachments atts); + +#ifdef __cplusplus +} // extern "C" + +// reference-based equivalents for c++ +inline void sg_setup(const sg_desc& desc) { return sg_setup(&desc); } + +inline sg_buffer sg_make_buffer(const sg_buffer_desc& desc) { return sg_make_buffer(&desc); } +inline sg_image sg_make_image(const sg_image_desc& desc) { return sg_make_image(&desc); } +inline sg_sampler sg_make_sampler(const sg_sampler_desc& desc) { return sg_make_sampler(&desc); } +inline sg_shader sg_make_shader(const sg_shader_desc& desc) { return sg_make_shader(&desc); } +inline sg_pipeline sg_make_pipeline(const sg_pipeline_desc& desc) { return sg_make_pipeline(&desc); } +inline sg_attachments sg_make_attachments(const sg_attachments_desc& desc) { return sg_make_attachments(&desc); } +inline void sg_update_image(sg_image img, const sg_image_data& data) { return sg_update_image(img, &data); } + +inline void sg_begin_pass(const sg_pass& pass) { return sg_begin_pass(&pass); } +inline void sg_apply_bindings(const sg_bindings& bindings) { return sg_apply_bindings(&bindings); } +inline void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range& data) { return sg_apply_uniforms(stage, ub_index, &data); } + +inline sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc& desc) { return sg_query_buffer_defaults(&desc); } +inline sg_image_desc sg_query_image_defaults(const sg_image_desc& desc) { return sg_query_image_defaults(&desc); } +inline sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc& desc) { return sg_query_sampler_defaults(&desc); } +inline sg_shader_desc sg_query_shader_defaults(const sg_shader_desc& desc) { return sg_query_shader_defaults(&desc); } +inline sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc& desc) { return sg_query_pipeline_defaults(&desc); } +inline sg_attachments_desc sg_query_attachments_defaults(const sg_attachments_desc& desc) { return sg_query_attachments_defaults(&desc); } + +inline void sg_init_buffer(sg_buffer buf, const sg_buffer_desc& desc) { return sg_init_buffer(buf, &desc); } +inline void sg_init_image(sg_image img, const sg_image_desc& desc) { return sg_init_image(img, &desc); } +inline void sg_init_sampler(sg_sampler smp, const sg_sampler_desc& desc) { return sg_init_sampler(smp, &desc); } +inline void sg_init_shader(sg_shader shd, const sg_shader_desc& desc) { return sg_init_shader(shd, &desc); } +inline void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc& desc) { return sg_init_pipeline(pip, &desc); } +inline void sg_init_attachments(sg_attachments atts, const sg_attachments_desc& desc) { return sg_init_attachments(atts, &desc); } + +inline void sg_update_buffer(sg_buffer buf_id, const sg_range& data) { return sg_update_buffer(buf_id, &data); } +inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_append_buffer(buf_id, &data); } +#endif +#endif // SOKOL_GFX_INCLUDED + +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation +#ifdef SOKOL_GFX_IMPL +#define SOKOL_GFX_IMPL_INCLUDED (1) + +#if !(defined(SOKOL_GLCORE)||defined(SOKOL_GLES3)||defined(SOKOL_D3D11)||defined(SOKOL_METAL)||defined(SOKOL_WGPU)||defined(SOKOL_DUMMY_BACKEND)) +#error "Please select a backend with SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND" +#endif +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sg_desc.allocator to override memory allocation functions" +#endif + +#include // malloc, free +#include // memset +#include // FLT_MAX + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef SOKOL_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#endif + +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif + +#ifndef _SOKOL_UNUSED + #define _SOKOL_UNUSED(x) (void)(x) +#endif + +#if defined(SOKOL_TRACE_HOOKS) +#define _SG_TRACE_ARGS(fn, ...) if (_sg.hooks.fn) { _sg.hooks.fn(__VA_ARGS__, _sg.hooks.user_data); } +#define _SG_TRACE_NOARGS(fn) if (_sg.hooks.fn) { _sg.hooks.fn(_sg.hooks.user_data); } +#else +#define _SG_TRACE_ARGS(fn, ...) +#define _SG_TRACE_NOARGS(fn) +#endif + +// default clear values +#ifndef SG_DEFAULT_CLEAR_RED +#define SG_DEFAULT_CLEAR_RED (0.5f) +#endif +#ifndef SG_DEFAULT_CLEAR_GREEN +#define SG_DEFAULT_CLEAR_GREEN (0.5f) +#endif +#ifndef SG_DEFAULT_CLEAR_BLUE +#define SG_DEFAULT_CLEAR_BLUE (0.5f) +#endif +#ifndef SG_DEFAULT_CLEAR_ALPHA +#define SG_DEFAULT_CLEAR_ALPHA (1.0f) +#endif +#ifndef SG_DEFAULT_CLEAR_DEPTH +#define SG_DEFAULT_CLEAR_DEPTH (1.0f) +#endif +#ifndef SG_DEFAULT_CLEAR_STENCIL +#define SG_DEFAULT_CLEAR_STENCIL (0) +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4115) // named type definition in parentheses +#pragma warning(disable:4505) // unreferenced local function has been removed +#pragma warning(disable:4201) // nonstandard extension used: nameless struct/union (needed by d3d11.h) +#pragma warning(disable:4054) // 'type cast': from function pointer +#pragma warning(disable:4055) // 'type cast': from data pointer +#endif + +#if defined(SOKOL_D3D11) + #ifndef D3D11_NO_HELPERS + #define D3D11_NO_HELPERS + #endif + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #include + #ifdef _MSC_VER + #pragma comment (lib, "kernel32") + #pragma comment (lib, "user32") + #pragma comment (lib, "dxgi") + #pragma comment (lib, "d3d11") + #endif +#elif defined(SOKOL_METAL) + // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting + #if !defined(__cplusplus) + #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields) + #error "sokol_gfx.h requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)" + #endif + #endif + #include + #include + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + #define _SG_TARGET_MACOS (1) + #else + #define _SG_TARGET_IOS (1) + #if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR + #define _SG_TARGET_IOS_SIMULATOR (1) + #endif + #endif + #import + #import // needed for CAMetalDrawable +#elif defined(SOKOL_WGPU) + #include + #if defined(__EMSCRIPTEN__) + #include + #endif +#elif defined(SOKOL_GLCORE) || defined(SOKOL_GLES3) + #define _SOKOL_ANY_GL (1) + + // include platform specific GL headers (or on Win32: use an embedded GL loader) + #if !defined(SOKOL_EXTERNAL_GL_LOADER) + #if defined(_WIN32) + #if defined(SOKOL_GLCORE) && !defined(SOKOL_EXTERNAL_GL_LOADER) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #define _SOKOL_USE_WIN32_GL_LOADER (1) + #pragma comment (lib, "kernel32") // GetProcAddress() + #endif + #elif defined(__APPLE__) + #include + #ifndef GL_SILENCE_DEPRECATION + #define GL_SILENCE_DEPRECATION + #endif + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + #include + #else + #include + #include + #endif + #elif defined(__EMSCRIPTEN__) || defined(__ANDROID__) + #if defined(SOKOL_GLES3) + #include + #endif + #elif defined(__linux__) || defined(__unix__) + #if defined(SOKOL_GLCORE) + #define GL_GLEXT_PROTOTYPES + #include + #else + #include + #include + #endif + #endif + #endif + + // optional GL loader definitions (only on Win32) + #if defined(_SOKOL_USE_WIN32_GL_LOADER) + #define __gl_h_ 1 + #define __gl32_h_ 1 + #define __gl31_h_ 1 + #define __GL_H__ 1 + #define __glext_h_ 1 + #define __GLEXT_H_ 1 + #define __gltypes_h_ 1 + #define __glcorearb_h_ 1 + #define __gl_glcorearb_h_ 1 + #define GL_APIENTRY APIENTRY + + typedef unsigned int GLenum; + typedef unsigned int GLuint; + typedef int GLsizei; + typedef char GLchar; + typedef ptrdiff_t GLintptr; + typedef ptrdiff_t GLsizeiptr; + typedef double GLclampd; + typedef unsigned short GLushort; + typedef unsigned char GLubyte; + typedef unsigned char GLboolean; + typedef uint64_t GLuint64; + typedef double GLdouble; + typedef unsigned short GLhalf; + typedef float GLclampf; + typedef unsigned int GLbitfield; + typedef signed char GLbyte; + typedef short GLshort; + typedef void GLvoid; + typedef int64_t GLint64; + typedef float GLfloat; + typedef int GLint; + #define GL_INT_2_10_10_10_REV 0x8D9F + #define GL_R32F 0x822E + #define GL_PROGRAM_POINT_SIZE 0x8642 + #define GL_DEPTH_ATTACHMENT 0x8D00 + #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A + #define GL_COLOR_ATTACHMENT2 0x8CE2 + #define GL_COLOR_ATTACHMENT0 0x8CE0 + #define GL_R16F 0x822D + #define GL_COLOR_ATTACHMENT22 0x8CF6 + #define GL_DRAW_FRAMEBUFFER 0x8CA9 + #define GL_FRAMEBUFFER_COMPLETE 0x8CD5 + #define GL_NUM_EXTENSIONS 0x821D + #define GL_INFO_LOG_LENGTH 0x8B84 + #define GL_VERTEX_SHADER 0x8B31 + #define GL_INCR 0x1E02 + #define GL_DYNAMIC_DRAW 0x88E8 + #define GL_STATIC_DRAW 0x88E4 + #define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 + #define GL_TEXTURE_CUBE_MAP 0x8513 + #define GL_FUNC_SUBTRACT 0x800A + #define GL_FUNC_REVERSE_SUBTRACT 0x800B + #define GL_CONSTANT_COLOR 0x8001 + #define GL_DECR_WRAP 0x8508 + #define GL_R8 0x8229 + #define GL_LINEAR_MIPMAP_LINEAR 0x2703 + #define GL_ELEMENT_ARRAY_BUFFER 0x8893 + #define GL_SHORT 0x1402 + #define GL_DEPTH_TEST 0x0B71 + #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 + #define GL_LINK_STATUS 0x8B82 + #define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 + #define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E + #define GL_RGBA16F 0x881A + #define GL_CONSTANT_ALPHA 0x8003 + #define GL_READ_FRAMEBUFFER 0x8CA8 + #define GL_TEXTURE0 0x84C0 + #define GL_TEXTURE_MIN_LOD 0x813A + #define GL_CLAMP_TO_EDGE 0x812F + #define GL_UNSIGNED_SHORT_5_6_5 0x8363 + #define GL_TEXTURE_WRAP_R 0x8072 + #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 + #define GL_NEAREST_MIPMAP_NEAREST 0x2700 + #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 + #define GL_SRC_ALPHA_SATURATE 0x0308 + #define GL_STREAM_DRAW 0x88E0 + #define GL_ONE 1 + #define GL_NEAREST_MIPMAP_LINEAR 0x2702 + #define GL_RGB10_A2 0x8059 + #define GL_RGBA8 0x8058 + #define GL_SRGB8_ALPHA8 0x8C43 + #define GL_COLOR_ATTACHMENT1 0x8CE1 + #define GL_RGBA4 0x8056 + #define GL_RGB8 0x8051 + #define GL_ARRAY_BUFFER 0x8892 + #define GL_STENCIL 0x1802 + #define GL_TEXTURE_2D 0x0DE1 + #define GL_DEPTH 0x1801 + #define GL_FRONT 0x0404 + #define GL_STENCIL_BUFFER_BIT 0x00000400 + #define GL_REPEAT 0x2901 + #define GL_RGBA 0x1908 + #define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 + #define GL_DECR 0x1E03 + #define GL_FRAGMENT_SHADER 0x8B30 + #define GL_FLOAT 0x1406 + #define GL_TEXTURE_MAX_LOD 0x813B + #define GL_DEPTH_COMPONENT 0x1902 + #define GL_ONE_MINUS_DST_ALPHA 0x0305 + #define GL_COLOR 0x1800 + #define GL_TEXTURE_2D_ARRAY 0x8C1A + #define GL_TRIANGLES 0x0004 + #define GL_UNSIGNED_BYTE 0x1401 + #define GL_TEXTURE_MAG_FILTER 0x2800 + #define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 + #define GL_NONE 0 + #define GL_SRC_COLOR 0x0300 + #define GL_BYTE 0x1400 + #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A + #define GL_LINE_STRIP 0x0003 + #define GL_TEXTURE_3D 0x806F + #define GL_CW 0x0900 + #define GL_LINEAR 0x2601 + #define GL_RENDERBUFFER 0x8D41 + #define GL_GEQUAL 0x0206 + #define GL_COLOR_BUFFER_BIT 0x00004000 + #define GL_RGBA32F 0x8814 + #define GL_BLEND 0x0BE2 + #define GL_ONE_MINUS_SRC_ALPHA 0x0303 + #define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 + #define GL_TEXTURE_WRAP_T 0x2803 + #define GL_TEXTURE_WRAP_S 0x2802 + #define GL_TEXTURE_MIN_FILTER 0x2801 + #define GL_LINEAR_MIPMAP_NEAREST 0x2701 + #define GL_EXTENSIONS 0x1F03 + #define GL_NO_ERROR 0 + #define GL_REPLACE 0x1E01 + #define GL_KEEP 0x1E00 + #define GL_CCW 0x0901 + #define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 + #define GL_RGB 0x1907 + #define GL_TRIANGLE_STRIP 0x0005 + #define GL_FALSE 0 + #define GL_ZERO 0 + #define GL_CULL_FACE 0x0B44 + #define GL_INVERT 0x150A + #define GL_INT 0x1404 + #define GL_UNSIGNED_INT 0x1405 + #define GL_UNSIGNED_SHORT 0x1403 + #define GL_NEAREST 0x2600 + #define GL_SCISSOR_TEST 0x0C11 + #define GL_LEQUAL 0x0203 + #define GL_STENCIL_TEST 0x0B90 + #define GL_DITHER 0x0BD0 + #define GL_DEPTH_COMPONENT32F 0x8CAC + #define GL_EQUAL 0x0202 + #define GL_FRAMEBUFFER 0x8D40 + #define GL_RGB5 0x8050 + #define GL_LINES 0x0001 + #define GL_DEPTH_BUFFER_BIT 0x00000100 + #define GL_SRC_ALPHA 0x0302 + #define GL_INCR_WRAP 0x8507 + #define GL_LESS 0x0201 + #define GL_MULTISAMPLE 0x809D + #define GL_FRAMEBUFFER_BINDING 0x8CA6 + #define GL_BACK 0x0405 + #define GL_ALWAYS 0x0207 + #define GL_FUNC_ADD 0x8006 + #define GL_ONE_MINUS_DST_COLOR 0x0307 + #define GL_NOTEQUAL 0x0205 + #define GL_DST_COLOR 0x0306 + #define GL_COMPILE_STATUS 0x8B81 + #define GL_RED 0x1903 + #define GL_COLOR_ATTACHMENT3 0x8CE3 + #define GL_DST_ALPHA 0x0304 + #define GL_RGB5_A1 0x8057 + #define GL_GREATER 0x0204 + #define GL_POLYGON_OFFSET_FILL 0x8037 + #define GL_TRUE 1 + #define GL_NEVER 0x0200 + #define GL_POINTS 0x0000 + #define GL_ONE_MINUS_SRC_COLOR 0x0301 + #define GL_MIRRORED_REPEAT 0x8370 + #define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D + #define GL_R11F_G11F_B10F 0x8C3A + #define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B + #define GL_RGB9_E5 0x8C3D + #define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E + #define GL_RGBA32UI 0x8D70 + #define GL_RGB32UI 0x8D71 + #define GL_RGBA16UI 0x8D76 + #define GL_RGB16UI 0x8D77 + #define GL_RGBA8UI 0x8D7C + #define GL_RGB8UI 0x8D7D + #define GL_RGBA32I 0x8D82 + #define GL_RGB32I 0x8D83 + #define GL_RGBA16I 0x8D88 + #define GL_RGB16I 0x8D89 + #define GL_RGBA8I 0x8D8E + #define GL_RGB8I 0x8D8F + #define GL_RED_INTEGER 0x8D94 + #define GL_RG 0x8227 + #define GL_RG_INTEGER 0x8228 + #define GL_R8 0x8229 + #define GL_R16 0x822A + #define GL_RG8 0x822B + #define GL_RG16 0x822C + #define GL_R16F 0x822D + #define GL_R32F 0x822E + #define GL_RG16F 0x822F + #define GL_RG32F 0x8230 + #define GL_R8I 0x8231 + #define GL_R8UI 0x8232 + #define GL_R16I 0x8233 + #define GL_R16UI 0x8234 + #define GL_R32I 0x8235 + #define GL_R32UI 0x8236 + #define GL_RG8I 0x8237 + #define GL_RG8UI 0x8238 + #define GL_RG16I 0x8239 + #define GL_RG16UI 0x823A + #define GL_RG32I 0x823B + #define GL_RG32UI 0x823C + #define GL_RGBA_INTEGER 0x8D99 + #define GL_R8_SNORM 0x8F94 + #define GL_RG8_SNORM 0x8F95 + #define GL_RGB8_SNORM 0x8F96 + #define GL_RGBA8_SNORM 0x8F97 + #define GL_R16_SNORM 0x8F98 + #define GL_RG16_SNORM 0x8F99 + #define GL_RGB16_SNORM 0x8F9A + #define GL_RGBA16_SNORM 0x8F9B + #define GL_RGBA16 0x805B + #define GL_MAX_TEXTURE_SIZE 0x0D33 + #define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C + #define GL_MAX_3D_TEXTURE_SIZE 0x8073 + #define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF + #define GL_MAX_VERTEX_ATTRIBS 0x8869 + #define GL_CLAMP_TO_BORDER 0x812D + #define GL_TEXTURE_BORDER_COLOR 0x1004 + #define GL_CURRENT_PROGRAM 0x8B8D + #define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A + #define GL_UNPACK_ALIGNMENT 0x0CF5 + #define GL_FRAMEBUFFER_SRGB 0x8DB9 + #define GL_TEXTURE_COMPARE_MODE 0x884C + #define GL_TEXTURE_COMPARE_FUNC 0x884D + #define GL_COMPARE_REF_TO_TEXTURE 0x884E + #define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F + #define GL_TEXTURE_MAX_LEVEL 0x813D + #define GL_FRAMEBUFFER_UNDEFINED 0x8219 + #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 + #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 + #define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD + #define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 + #define GL_MAJOR_VERSION 0x821B + #define GL_MINOR_VERSION 0x821C + #endif + + #ifndef GL_UNSIGNED_INT_2_10_10_10_REV + #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 + #endif + #ifndef GL_UNSIGNED_INT_24_8 + #define GL_UNSIGNED_INT_24_8 0x84FA + #endif + #ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE + #endif + #ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF + #endif + #ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 + #endif + #ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 + #endif + #ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 + #endif + #ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT + #define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F + #endif + #ifndef GL_COMPRESSED_RED_RGTC1 + #define GL_COMPRESSED_RED_RGTC1 0x8DBB + #endif + #ifndef GL_COMPRESSED_SIGNED_RED_RGTC1 + #define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC + #endif + #ifndef GL_COMPRESSED_RED_GREEN_RGTC2 + #define GL_COMPRESSED_RED_GREEN_RGTC2 0x8DBD + #endif + #ifndef GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2 + #define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2 0x8DBE + #endif + #ifndef GL_COMPRESSED_RGBA_BPTC_UNORM_ARB + #define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C + #endif + #ifndef GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB + #define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D + #endif + #ifndef GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB + #define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E + #endif + #ifndef GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB + #define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F + #endif + #ifndef GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG + #define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 + #endif + #ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG + #define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 + #endif + #ifndef GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG + #define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 + #endif + #ifndef GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG + #define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 + #endif + #ifndef GL_COMPRESSED_RGB8_ETC2 + #define GL_COMPRESSED_RGB8_ETC2 0x9274 + #endif + #ifndef GL_COMPRESSED_SRGB8_ETC2 + #define GL_COMPRESSED_SRGB8_ETC2 0x9275 + #endif + #ifndef GL_COMPRESSED_RGBA8_ETC2_EAC + #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 + #endif + #ifndef GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC + #define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 + #endif + #ifndef GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 + #define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 + #endif + #ifndef GL_COMPRESSED_R11_EAC + #define GL_COMPRESSED_R11_EAC 0x9270 + #endif + #ifndef GL_COMPRESSED_SIGNED_R11_EAC + #define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 + #endif + #ifndef GL_COMPRESSED_RG11_EAC + #define GL_COMPRESSED_RG11_EAC 0x9272 + #endif + #ifndef GL_COMPRESSED_SIGNED_RG11_EAC + #define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 + #endif + #ifndef GL_COMPRESSED_RGBA_ASTC_4x4_KHR + #define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 + #endif + #ifndef GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR + #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 + #endif + #ifndef GL_DEPTH24_STENCIL8 + #define GL_DEPTH24_STENCIL8 0x88F0 + #endif + #ifndef GL_HALF_FLOAT + #define GL_HALF_FLOAT 0x140B + #endif + #ifndef GL_DEPTH_STENCIL + #define GL_DEPTH_STENCIL 0x84F9 + #endif + #ifndef GL_LUMINANCE + #define GL_LUMINANCE 0x1909 + #endif + #ifndef _SG_GL_CHECK_ERROR + #define _SG_GL_CHECK_ERROR() { SOKOL_ASSERT(glGetError() == GL_NO_ERROR); } + #endif +#endif + +#if defined(SOKOL_GLES3) + // on WebGL2, GL_FRAMEBUFFER_UNDEFINED technically doesn't exist (it is defined + // in the Emscripten headers, but may not exist in other WebGL2 shims) + // see: https://github.com/floooh/sokol/pull/933 + #ifndef GL_FRAMEBUFFER_UNDEFINED + #define GL_FRAMEBUFFER_UNDEFINED 0x8219 + #endif +#endif + +// make some GL constants generally available to simplify compilation, +// use of those constants will be filtered by runtime flags +#ifndef GL_SHADER_STORAGE_BUFFER +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#endif + +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs +// resource pool slots +typedef struct { + uint32_t id; + sg_resource_state state; +} _sg_slot_t; + +// resource pool housekeeping struct +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _sg_pool_t; + +_SOKOL_PRIVATE void _sg_init_pool(_sg_pool_t* pool, int num); +_SOKOL_PRIVATE void _sg_discard_pool(_sg_pool_t* pool); +_SOKOL_PRIVATE int _sg_pool_alloc_index(_sg_pool_t* pool); +_SOKOL_PRIVATE void _sg_pool_free_index(_sg_pool_t* pool, int slot_index); +_SOKOL_PRIVATE void _sg_reset_slot(_sg_slot_t* slot); +_SOKOL_PRIVATE uint32_t _sg_slot_alloc(_sg_pool_t* pool, _sg_slot_t* slot, int slot_index); +_SOKOL_PRIVATE int _sg_slot_index(uint32_t id); + +// constants +enum { + _SG_STRING_SIZE = 32, + _SG_SLOT_SHIFT = 16, + _SG_SLOT_MASK = (1<<_SG_SLOT_SHIFT)-1, + _SG_MAX_POOL_SIZE = (1<<_SG_SLOT_SHIFT), + _SG_DEFAULT_BUFFER_POOL_SIZE = 128, + _SG_DEFAULT_IMAGE_POOL_SIZE = 128, + _SG_DEFAULT_SAMPLER_POOL_SIZE = 64, + _SG_DEFAULT_SHADER_POOL_SIZE = 32, + _SG_DEFAULT_PIPELINE_POOL_SIZE = 64, + _SG_DEFAULT_ATTACHMENTS_POOL_SIZE = 16, + _SG_DEFAULT_UB_SIZE = 4 * 1024 * 1024, + _SG_DEFAULT_MAX_COMMIT_LISTENERS = 1024, + _SG_DEFAULT_WGPU_BINDGROUP_CACHE_SIZE = 1024, +}; + +// fixed-size string +typedef struct { + char buf[_SG_STRING_SIZE]; +} _sg_str_t; + +// helper macros +#define _sg_def(val, def) (((val) == 0) ? (def) : (val)) +#define _sg_def_flt(val, def) (((val) == 0.0f) ? (def) : (val)) +#define _sg_min(a,b) (((a)<(b))?(a):(b)) +#define _sg_max(a,b) (((a)>(b))?(a):(b)) +#define _sg_clamp(v,v0,v1) (((v)<(v0))?(v0):(((v)>(v1))?(v1):(v))) +#define _sg_fequal(val,cmp,delta) ((((val)-(cmp))> -(delta))&&(((val)-(cmp))<(delta))) +#define _sg_ispow2(val) ((val&(val-1))==0) +#define _sg_stats_add(key,val) {if(_sg.stats_enabled){ _sg.stats.key+=val;}} + +_SOKOL_PRIVATE void* _sg_malloc_clear(size_t size); +_SOKOL_PRIVATE void _sg_free(void* ptr); +_SOKOL_PRIVATE void _sg_clear(void* ptr, size_t size); + +typedef struct { + int size; + int append_pos; + bool append_overflow; + uint32_t update_frame_index; + uint32_t append_frame_index; + int num_slots; + int active_slot; + sg_buffer_type type; + sg_usage usage; +} _sg_buffer_common_t; + +_SOKOL_PRIVATE void _sg_buffer_common_init(_sg_buffer_common_t* cmn, const sg_buffer_desc* desc) { + cmn->size = (int)desc->size; + cmn->append_pos = 0; + cmn->append_overflow = false; + cmn->update_frame_index = 0; + cmn->append_frame_index = 0; + cmn->num_slots = (desc->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + cmn->active_slot = 0; + cmn->type = desc->type; + cmn->usage = desc->usage; +} + +typedef struct { + uint32_t upd_frame_index; + int num_slots; + int active_slot; + sg_image_type type; + bool render_target; + int width; + int height; + int num_slices; + int num_mipmaps; + sg_usage usage; + sg_pixel_format pixel_format; + int sample_count; +} _sg_image_common_t; + +_SOKOL_PRIVATE void _sg_image_common_init(_sg_image_common_t* cmn, const sg_image_desc* desc) { + cmn->upd_frame_index = 0; + cmn->num_slots = (desc->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + cmn->active_slot = 0; + cmn->type = desc->type; + cmn->render_target = desc->render_target; + cmn->width = desc->width; + cmn->height = desc->height; + cmn->num_slices = desc->num_slices; + cmn->num_mipmaps = desc->num_mipmaps; + cmn->usage = desc->usage; + cmn->pixel_format = desc->pixel_format; + cmn->sample_count = desc->sample_count; +} + +typedef struct { + sg_filter min_filter; + sg_filter mag_filter; + sg_filter mipmap_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + float min_lod; + float max_lod; + sg_border_color border_color; + sg_compare_func compare; + uint32_t max_anisotropy; +} _sg_sampler_common_t; + +_SOKOL_PRIVATE void _sg_sampler_common_init(_sg_sampler_common_t* cmn, const sg_sampler_desc* desc) { + cmn->min_filter = desc->min_filter; + cmn->mag_filter = desc->mag_filter; + cmn->mipmap_filter = desc->mipmap_filter; + cmn->wrap_u = desc->wrap_u; + cmn->wrap_v = desc->wrap_v; + cmn->wrap_w = desc->wrap_w; + cmn->min_lod = desc->min_lod; + cmn->max_lod = desc->max_lod; + cmn->border_color = desc->border_color; + cmn->compare = desc->compare; + cmn->max_anisotropy = desc->max_anisotropy; +} + +typedef struct { + size_t size; +} _sg_shader_uniform_block_t; + +typedef struct { + bool used; + bool readonly; +} _sg_shader_storage_buffer_t; + +typedef struct { + sg_image_type image_type; + sg_image_sample_type sample_type; + bool multisampled; +} _sg_shader_image_t; + +typedef struct { + sg_sampler_type sampler_type; +} _sg_shader_sampler_t; + +// combined image sampler mappings, only needed on GL +typedef struct { + int image_slot; + int sampler_slot; +} _sg_shader_image_sampler_t; + +typedef struct { + int num_uniform_blocks; + int num_storage_buffers; + int num_images; + int num_samplers; + int num_image_samplers; + _sg_shader_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + _sg_shader_storage_buffer_t storage_buffers[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; + _sg_shader_image_t images[SG_MAX_SHADERSTAGE_IMAGES]; + _sg_shader_sampler_t samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; + _sg_shader_image_sampler_t image_samplers[SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS]; +} _sg_shader_stage_t; + +typedef struct { + _sg_shader_stage_t stage[SG_NUM_SHADER_STAGES]; +} _sg_shader_common_t; + +_SOKOL_PRIVATE void _sg_shader_common_init(_sg_shader_common_t* cmn, const sg_shader_desc* desc) { + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; + _sg_shader_stage_t* stage = &cmn->stage[stage_index]; + SOKOL_ASSERT(stage->num_uniform_blocks == 0); + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + if (0 == ub_desc->size) { + break; + } + stage->uniform_blocks[ub_index].size = ub_desc->size; + stage->num_uniform_blocks++; + } + SOKOL_ASSERT(stage->num_images == 0); + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (!img_desc->used) { + break; + } + stage->images[img_index].multisampled = img_desc->multisampled; + stage->images[img_index].image_type = img_desc->image_type; + stage->images[img_index].sample_type = img_desc->sample_type; + stage->num_images++; + } + SOKOL_ASSERT(stage->num_samplers == 0); + for (int smp_index = 0; smp_index < SG_MAX_SHADERSTAGE_SAMPLERS; smp_index++) { + const sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_index]; + if (!smp_desc->used) { + break; + } + stage->samplers[smp_index].sampler_type = smp_desc->sampler_type; + stage->num_samplers++; + } + SOKOL_ASSERT(stage->num_image_samplers == 0); + for (int img_smp_index = 0; img_smp_index < SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS; img_smp_index++) { + const sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_index]; + if (!img_smp_desc->used) { + break; + } + SOKOL_ASSERT((img_smp_desc->image_slot >= 0) && (img_smp_desc->image_slot < stage->num_images)); + stage->image_samplers[img_smp_index].image_slot = img_smp_desc->image_slot; + SOKOL_ASSERT((img_smp_desc->sampler_slot >= 0) && (img_smp_desc->sampler_slot < stage->num_samplers)); + stage->image_samplers[img_smp_index].sampler_slot = img_smp_desc->sampler_slot; + stage->num_image_samplers++; + } + SOKOL_ASSERT(stage->num_storage_buffers == 0); + for (int sbuf_index = 0; sbuf_index < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; sbuf_index++) { + const sg_shader_storage_buffer_desc* sbuf_desc = &stage_desc->storage_buffers[sbuf_index]; + if (!sbuf_desc->used) { + break; + } + stage->storage_buffers[sbuf_index].used = sbuf_desc->used; + stage->storage_buffers[sbuf_index].readonly = sbuf_desc->readonly; + stage->num_storage_buffers++; + } + } +} + +typedef struct { + bool vertex_buffer_layout_active[SG_MAX_VERTEX_BUFFERS]; + bool use_instanced_draw; + sg_shader shader_id; + sg_vertex_layout_state layout; + sg_depth_state depth; + sg_stencil_state stencil; + int color_count; + sg_color_target_state colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_primitive_type primitive_type; + sg_index_type index_type; + sg_cull_mode cull_mode; + sg_face_winding face_winding; + int sample_count; + sg_color blend_color; + bool alpha_to_coverage_enabled; +} _sg_pipeline_common_t; + +_SOKOL_PRIVATE void _sg_pipeline_common_init(_sg_pipeline_common_t* cmn, const sg_pipeline_desc* desc) { + SOKOL_ASSERT((desc->color_count >= 0) && (desc->color_count <= SG_MAX_COLOR_ATTACHMENTS)); + for (int i = 0; i < SG_MAX_VERTEX_BUFFERS; i++) { + cmn->vertex_buffer_layout_active[i] = false; + } + cmn->use_instanced_draw = false; + cmn->shader_id = desc->shader; + cmn->layout = desc->layout; + cmn->depth = desc->depth; + cmn->stencil = desc->stencil; + cmn->color_count = desc->color_count; + for (int i = 0; i < desc->color_count; i++) { + cmn->colors[i] = desc->colors[i]; + } + cmn->primitive_type = desc->primitive_type; + cmn->index_type = desc->index_type; + cmn->cull_mode = desc->cull_mode; + cmn->face_winding = desc->face_winding; + cmn->sample_count = desc->sample_count; + cmn->blend_color = desc->blend_color; + cmn->alpha_to_coverage_enabled = desc->alpha_to_coverage_enabled; +} + +typedef struct { + sg_image image_id; + int mip_level; + int slice; +} _sg_attachment_common_t; + +typedef struct { + int width; + int height; + int num_colors; + _sg_attachment_common_t colors[SG_MAX_COLOR_ATTACHMENTS]; + _sg_attachment_common_t resolves[SG_MAX_COLOR_ATTACHMENTS]; + _sg_attachment_common_t depth_stencil; +} _sg_attachments_common_t; + +_SOKOL_PRIVATE void _sg_attachment_common_init(_sg_attachment_common_t* cmn, const sg_attachment_desc* desc) { + cmn->image_id = desc->image; + cmn->mip_level = desc->mip_level; + cmn->slice = desc->slice; +} + +_SOKOL_PRIVATE void _sg_attachments_common_init(_sg_attachments_common_t* cmn, const sg_attachments_desc* desc, int width, int height) { + SOKOL_ASSERT((width > 0) && (height > 0)); + cmn->width = width; + cmn->height = height; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (desc->colors[i].image.id != SG_INVALID_ID) { + cmn->num_colors++; + _sg_attachment_common_init(&cmn->colors[i], &desc->colors[i]); + _sg_attachment_common_init(&cmn->resolves[i], &desc->resolves[i]); + } + } + if (desc->depth_stencil.image.id != SG_INVALID_ID) { + _sg_attachment_common_init(&cmn->depth_stencil, &desc->depth_stencil); + } +} + +#if defined(SOKOL_DUMMY_BACKEND) +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; +} _sg_dummy_buffer_t; +typedef _sg_dummy_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; +} _sg_dummy_image_t; +typedef _sg_dummy_image_t _sg_image_t; + +typedef struct { + _sg_slot_t slot; + _sg_sampler_common_t cmn; +} _sg_dummy_sampler_t; +typedef _sg_dummy_sampler_t _sg_sampler_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; +} _sg_dummy_shader_t; +typedef _sg_dummy_shader_t _sg_shader_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_t* shader; + _sg_pipeline_common_t cmn; +} _sg_dummy_pipeline_t; +typedef _sg_dummy_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; +} _sg_dummy_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_attachments_common_t cmn; + struct { + _sg_dummy_attachment_t colors[SG_MAX_COLOR_ATTACHMENTS]; + _sg_dummy_attachment_t resolves[SG_MAX_COLOR_ATTACHMENTS]; + _sg_dummy_attachment_t depth_stencil; + } dmy; +} _sg_dummy_attachments_t; +typedef _sg_dummy_attachments_t _sg_attachments_t; + +#elif defined(_SOKOL_ANY_GL) + +#define _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE (SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS * SG_NUM_SHADER_STAGES) +#define _SG_GL_STORAGEBUFFER_STAGE_INDEX_PITCH (SG_MAX_SHADERSTAGE_STORAGEBUFFERS) + +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + GLuint buf[SG_NUM_INFLIGHT_FRAMES]; + bool injected; // if true, external buffers were injected with sg_buffer_desc.gl_buffers + } gl; +} _sg_gl_buffer_t; +typedef _sg_gl_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + GLenum target; + GLuint msaa_render_buffer; + GLuint tex[SG_NUM_INFLIGHT_FRAMES]; + bool injected; // if true, external textures were injected with sg_image_desc.gl_textures + } gl; +} _sg_gl_image_t; +typedef _sg_gl_image_t _sg_image_t; + +typedef struct { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + GLuint smp; + bool injected; // true if external sampler was injects in sg_sampler_desc.gl_sampler + } gl; +} _sg_gl_sampler_t; +typedef _sg_gl_sampler_t _sg_sampler_t; + +typedef struct { + GLint gl_loc; + sg_uniform_type type; + uint16_t count; + uint16_t offset; +} _sg_gl_uniform_t; + +typedef struct { + int num_uniforms; + _sg_gl_uniform_t uniforms[SG_MAX_UB_MEMBERS]; +} _sg_gl_uniform_block_t; + +typedef struct { + int gl_tex_slot; +} _sg_gl_shader_image_sampler_t; + +typedef struct { + _sg_str_t name; +} _sg_gl_shader_attr_t; + +typedef struct { + _sg_gl_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + _sg_gl_shader_image_sampler_t image_samplers[SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS]; +} _sg_gl_shader_stage_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + GLuint prog; + _sg_gl_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + _sg_gl_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + } gl; +} _sg_gl_shader_t; +typedef _sg_gl_shader_t _sg_shader_t; + +typedef struct { + int8_t vb_index; // -1 if attr is not enabled + int8_t divisor; // -1 if not initialized + uint8_t stride; + uint8_t size; + uint8_t normalized; + int offset; + GLenum type; +} _sg_gl_attr_t; + +typedef struct { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + _sg_shader_t* shader; + struct { + _sg_gl_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + sg_depth_state depth; + sg_stencil_state stencil; + sg_primitive_type primitive_type; + sg_blend_state blend; + sg_color_mask color_write_mask[SG_MAX_COLOR_ATTACHMENTS]; + sg_cull_mode cull_mode; + sg_face_winding face_winding; + int sample_count; + bool alpha_to_coverage_enabled; + } gl; +} _sg_gl_pipeline_t; +typedef _sg_gl_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; +} _sg_gl_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_attachments_common_t cmn; + struct { + GLuint fb; + _sg_gl_attachment_t colors[SG_MAX_COLOR_ATTACHMENTS]; + _sg_gl_attachment_t resolves[SG_MAX_COLOR_ATTACHMENTS]; + _sg_gl_attachment_t depth_stencil; + GLuint msaa_resolve_framebuffer[SG_MAX_COLOR_ATTACHMENTS]; + } gl; +} _sg_gl_attachments_t; +typedef _sg_gl_attachments_t _sg_attachments_t; + +typedef struct { + _sg_gl_attr_t gl_attr; + GLuint gl_vbuf; +} _sg_gl_cache_attr_t; + +typedef struct { + GLenum target; + GLuint texture; + GLuint sampler; +} _sg_gl_cache_texture_sampler_bind_slot; + +typedef struct { + sg_depth_state depth; + sg_stencil_state stencil; + sg_blend_state blend; + sg_color_mask color_write_mask[SG_MAX_COLOR_ATTACHMENTS]; + sg_cull_mode cull_mode; + sg_face_winding face_winding; + bool polygon_offset_enabled; + int sample_count; + sg_color blend_color; + bool alpha_to_coverage_enabled; + _sg_gl_cache_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + GLuint vertex_buffer; + GLuint index_buffer; + GLuint storage_buffer; // general bind point + GLuint stage_storage_buffers[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; + GLuint stored_vertex_buffer; + GLuint stored_index_buffer; + GLuint stored_storage_buffer; + GLuint prog; + _sg_gl_cache_texture_sampler_bind_slot texture_samplers[_SG_GL_TEXTURE_SAMPLER_CACHE_SIZE]; + _sg_gl_cache_texture_sampler_bind_slot stored_texture_sampler; + int cur_ib_offset; + GLenum cur_primitive_type; + GLenum cur_index_type; + GLenum cur_active_texture; + _sg_pipeline_t* cur_pipeline; + sg_pipeline cur_pipeline_id; +} _sg_gl_state_cache_t; + +typedef struct { + bool valid; + GLuint vao; + _sg_gl_state_cache_t cache; + bool ext_anisotropic; + GLint max_anisotropy; + sg_store_action color_store_actions[SG_MAX_COLOR_ATTACHMENTS]; + sg_store_action depth_store_action; + sg_store_action stencil_store_action; + #if _SOKOL_USE_WIN32_GL_LOADER + HINSTANCE opengl32_dll; + #endif +} _sg_gl_backend_t; + +#elif defined(SOKOL_D3D11) + +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + ID3D11Buffer* buf; + ID3D11ShaderResourceView* srv; + } d3d11; +} _sg_d3d11_buffer_t; +typedef _sg_d3d11_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + DXGI_FORMAT format; + ID3D11Texture2D* tex2d; + ID3D11Texture3D* tex3d; + ID3D11Resource* res; // either tex2d or tex3d + ID3D11ShaderResourceView* srv; + } d3d11; +} _sg_d3d11_image_t; +typedef _sg_d3d11_image_t _sg_image_t; + +typedef struct { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + ID3D11SamplerState* smp; + } d3d11; +} _sg_d3d11_sampler_t; +typedef _sg_d3d11_sampler_t _sg_sampler_t; + +typedef struct { + _sg_str_t sem_name; + int sem_index; +} _sg_d3d11_shader_attr_t; + +typedef struct { + ID3D11Buffer* cbufs[SG_MAX_SHADERSTAGE_UBS]; +} _sg_d3d11_shader_stage_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + _sg_d3d11_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + _sg_d3d11_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + ID3D11VertexShader* vs; + ID3D11PixelShader* fs; + void* vs_blob; + size_t vs_blob_length; + } d3d11; +} _sg_d3d11_shader_t; +typedef _sg_d3d11_shader_t _sg_shader_t; + +typedef struct { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + _sg_shader_t* shader; + struct { + UINT stencil_ref; + UINT vb_strides[SG_MAX_VERTEX_BUFFERS]; + D3D_PRIMITIVE_TOPOLOGY topology; + DXGI_FORMAT index_format; + ID3D11InputLayout* il; + ID3D11RasterizerState* rs; + ID3D11DepthStencilState* dss; + ID3D11BlendState* bs; + } d3d11; +} _sg_d3d11_pipeline_t; +typedef _sg_d3d11_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; + union { + ID3D11RenderTargetView* rtv; + ID3D11DepthStencilView* dsv; + } view; +} _sg_d3d11_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_attachments_common_t cmn; + struct { + _sg_d3d11_attachment_t colors[SG_MAX_COLOR_ATTACHMENTS]; + _sg_d3d11_attachment_t resolves[SG_MAX_COLOR_ATTACHMENTS]; + _sg_d3d11_attachment_t depth_stencil; + } d3d11; +} _sg_d3d11_attachments_t; +typedef _sg_d3d11_attachments_t _sg_attachments_t; + +typedef struct { + bool valid; + ID3D11Device* dev; + ID3D11DeviceContext* ctx; + bool use_indexed_draw; + bool use_instanced_draw; + _sg_pipeline_t* cur_pipeline; + sg_pipeline cur_pipeline_id; + struct { + ID3D11RenderTargetView* render_view; + ID3D11RenderTargetView* resolve_view; + } cur_pass; + // on-demand loaded d3dcompiler_47.dll handles + HINSTANCE d3dcompiler_dll; + bool d3dcompiler_dll_load_failed; + pD3DCompile D3DCompile_func; + // global subresourcedata array for texture updates + D3D11_SUBRESOURCE_DATA subres_data[SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS]; +} _sg_d3d11_backend_t; + +#elif defined(SOKOL_METAL) + +#if defined(_SG_TARGET_MACOS) || defined(_SG_TARGET_IOS_SIMULATOR) +#define _SG_MTL_UB_ALIGN (256) +#else +#define _SG_MTL_UB_ALIGN (16) +#endif +#define _SG_MTL_INVALID_SLOT_INDEX (0) + +typedef struct { + uint32_t frame_index; // frame index at which it is safe to release this resource + int slot_index; +} _sg_mtl_release_item_t; + +typedef struct { + NSMutableArray* pool; + int num_slots; + int free_queue_top; + int* free_queue; + int release_queue_front; + int release_queue_back; + _sg_mtl_release_item_t* release_queue; +} _sg_mtl_idpool_t; + +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + int buf[SG_NUM_INFLIGHT_FRAMES]; // index into _sg_mtl_pool + } mtl; +} _sg_mtl_buffer_t; +typedef _sg_mtl_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + int tex[SG_NUM_INFLIGHT_FRAMES]; + } mtl; +} _sg_mtl_image_t; +typedef _sg_mtl_image_t _sg_image_t; + +typedef struct { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + int sampler_state; + } mtl; +} _sg_mtl_sampler_t; +typedef _sg_mtl_sampler_t _sg_sampler_t; + +typedef struct { + int mtl_lib; + int mtl_func; +} _sg_mtl_shader_stage_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + _sg_mtl_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + } mtl; +} _sg_mtl_shader_t; +typedef _sg_mtl_shader_t _sg_shader_t; + +typedef struct { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + _sg_shader_t* shader; + struct { + MTLPrimitiveType prim_type; + int index_size; + MTLIndexType index_type; + MTLCullMode cull_mode; + MTLWinding winding; + uint32_t stencil_ref; + int rps; + int dss; + } mtl; +} _sg_mtl_pipeline_t; +typedef _sg_mtl_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; +} _sg_mtl_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_attachments_common_t cmn; + struct { + _sg_mtl_attachment_t colors[SG_MAX_COLOR_ATTACHMENTS]; + _sg_mtl_attachment_t resolves[SG_MAX_COLOR_ATTACHMENTS]; + _sg_mtl_attachment_t depth_stencil; + } mtl; +} _sg_mtl_attachments_t; +typedef _sg_mtl_attachments_t _sg_attachments_t; + +// resource binding state cache +typedef struct { + const _sg_pipeline_t* cur_pipeline; + sg_pipeline cur_pipeline_id; + const _sg_buffer_t* cur_indexbuffer; + sg_buffer cur_indexbuffer_id; + int cur_indexbuffer_offset; + int cur_vertexbuffer_offsets[SG_MAX_VERTEX_BUFFERS]; + sg_buffer cur_vertexbuffer_ids[SG_MAX_VERTEX_BUFFERS]; + sg_image cur_vs_image_ids[SG_MAX_SHADERSTAGE_IMAGES]; + sg_image cur_fs_image_ids[SG_MAX_SHADERSTAGE_IMAGES]; + sg_sampler cur_vs_sampler_ids[SG_MAX_SHADERSTAGE_SAMPLERS]; + sg_sampler cur_fs_sampler_ids[SG_MAX_SHADERSTAGE_SAMPLERS]; + sg_buffer cur_vs_storagebuffer_ids[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; + sg_buffer cur_fs_storagebuffer_ids[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; +} _sg_mtl_state_cache_t; + +typedef struct { + bool valid; + bool use_shared_storage_mode; + uint32_t cur_frame_rotate_index; + int ub_size; + int cur_ub_offset; + uint8_t* cur_ub_base_ptr; + _sg_mtl_state_cache_t state_cache; + _sg_mtl_idpool_t idpool; + dispatch_semaphore_t sem; + id device; + id cmd_queue; + id cmd_buffer; + id cmd_encoder; + id cur_drawable; + id uniform_buffers[SG_NUM_INFLIGHT_FRAMES]; +} _sg_mtl_backend_t; + +#elif defined(SOKOL_WGPU) + +#define _SG_WGPU_ROWPITCH_ALIGN (256) +#define _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE (1<<16) // also see WGPULimits.maxUniformBufferBindingSize +#define _SG_WGPU_NUM_BINDGROUPS (2) // 0: uniforms, 1: images and sampler on both shader stages +#define _SG_WGPU_UNIFORM_BINDGROUP_INDEX (0) +#define _SG_WGPU_IMAGE_SAMPLER_BINDGROUP_INDEX (1) +#define _SG_WGPU_MAX_BINDGROUP_ENTRIES (SG_NUM_SHADER_STAGES * (SG_MAX_SHADERSTAGE_IMAGES + SG_MAX_SHADERSTAGE_SAMPLERS + SG_MAX_SHADERSTAGE_STORAGEBUFFERS)) + +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + WGPUBuffer buf; + } wgpu; +} _sg_wgpu_buffer_t; +typedef _sg_wgpu_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + WGPUTexture tex; + WGPUTextureView view; + } wgpu; +} _sg_wgpu_image_t; +typedef _sg_wgpu_image_t _sg_image_t; + +typedef struct { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + WGPUSampler smp; + } wgpu; +} _sg_wgpu_sampler_t; +typedef _sg_wgpu_sampler_t _sg_sampler_t; + +typedef struct { + WGPUShaderModule module; + _sg_str_t entry; +} _sg_wgpu_shader_stage_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + _sg_wgpu_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + WGPUBindGroupLayout bind_group_layout; + } wgpu; +} _sg_wgpu_shader_t; +typedef _sg_wgpu_shader_t _sg_shader_t; + +typedef struct { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + _sg_shader_t* shader; + struct { + WGPURenderPipeline pip; + WGPUColor blend_color; + } wgpu; +} _sg_wgpu_pipeline_t; +typedef _sg_wgpu_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; + WGPUTextureView view; +} _sg_wgpu_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_attachments_common_t cmn; + struct { + _sg_wgpu_attachment_t colors[SG_MAX_COLOR_ATTACHMENTS]; + _sg_wgpu_attachment_t resolves[SG_MAX_COLOR_ATTACHMENTS]; + _sg_wgpu_attachment_t depth_stencil; + } wgpu; +} _sg_wgpu_attachments_t; +typedef _sg_wgpu_attachments_t _sg_attachments_t; + +// a pool of per-frame uniform buffers +typedef struct { + uint32_t num_bytes; + uint32_t offset; // current offset into buf + uint8_t* staging; // intermediate buffer for uniform data updates + WGPUBuffer buf; // the GPU-side uniform buffer + struct { + WGPUBindGroupLayout group_layout; + WGPUBindGroup group; + uint32_t offsets[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + } bind; +} _sg_wgpu_uniform_buffer_t; + +typedef struct { + uint32_t id; +} _sg_wgpu_bindgroup_handle_t; + +#define _SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS (1 + _SG_WGPU_MAX_BINDGROUP_ENTRIES) +typedef struct { + uint64_t hash; + uint32_t items[_SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS]; +} _sg_wgpu_bindgroups_cache_key_t; + +typedef struct { + uint32_t num; // must be 2^n + uint32_t index_mask; // mask to turn hash into valid index + _sg_wgpu_bindgroup_handle_t* items; +} _sg_wgpu_bindgroups_cache_t; + +typedef struct { + _sg_slot_t slot; + WGPUBindGroup bindgroup; + _sg_wgpu_bindgroups_cache_key_t key; +} _sg_wgpu_bindgroup_t; + +typedef struct { + _sg_pool_t pool; + _sg_wgpu_bindgroup_t* bindgroups; +} _sg_wgpu_bindgroups_pool_t; + +typedef struct { + struct { + sg_buffer buffer; + int offset; + } vbs[SG_MAX_VERTEX_BUFFERS]; + struct { + sg_buffer buffer; + int offset; + } ib; + _sg_wgpu_bindgroup_handle_t bg; +} _sg_wgpu_bindings_cache_t; + +// the WGPU backend state +typedef struct { + bool valid; + bool use_indexed_draw; + WGPUDevice dev; + WGPUSupportedLimits limits; + WGPUQueue queue; + WGPUCommandEncoder cmd_enc; + WGPURenderPassEncoder pass_enc; + WGPUBindGroup empty_bind_group; + const _sg_pipeline_t* cur_pipeline; + sg_pipeline cur_pipeline_id; + _sg_wgpu_uniform_buffer_t uniform; + _sg_wgpu_bindings_cache_t bindings_cache; + _sg_wgpu_bindgroups_cache_t bindgroups_cache; + _sg_wgpu_bindgroups_pool_t bindgroups_pool; +} _sg_wgpu_backend_t; +#endif + +// POOL STRUCTS + +// this *MUST* remain 0 +#define _SG_INVALID_SLOT_INDEX (0) + +typedef struct { + _sg_pool_t buffer_pool; + _sg_pool_t image_pool; + _sg_pool_t sampler_pool; + _sg_pool_t shader_pool; + _sg_pool_t pipeline_pool; + _sg_pool_t attachments_pool; + _sg_buffer_t* buffers; + _sg_image_t* images; + _sg_sampler_t* samplers; + _sg_shader_t* shaders; + _sg_pipeline_t* pipelines; + _sg_attachments_t* attachments; +} _sg_pools_t; + +typedef struct { + int num; // number of allocated commit listener items + int upper; // the current upper index (no valid items past this point) + sg_commit_listener* items; +} _sg_commit_listeners_t; + +// resolved resource bindings struct +typedef struct { + _sg_pipeline_t* pip; + int num_vbs; + int num_vs_imgs; + int num_vs_smps; + int num_vs_sbufs; + int num_fs_imgs; + int num_fs_smps; + int num_fs_sbufs; + int vb_offsets[SG_MAX_VERTEX_BUFFERS]; + int ib_offset; + _sg_buffer_t* vbs[SG_MAX_VERTEX_BUFFERS]; + _sg_buffer_t* ib; + _sg_image_t* vs_imgs[SG_MAX_SHADERSTAGE_IMAGES]; + _sg_sampler_t* vs_smps[SG_MAX_SHADERSTAGE_SAMPLERS]; + _sg_buffer_t* vs_sbufs[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; + _sg_image_t* fs_imgs[SG_MAX_SHADERSTAGE_IMAGES]; + _sg_sampler_t* fs_smps[SG_MAX_SHADERSTAGE_SAMPLERS]; + _sg_buffer_t* fs_sbufs[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; +} _sg_bindings_t; + +typedef struct { + bool sample; + bool filter; + bool render; + bool blend; + bool msaa; + bool depth; +} _sg_pixelformat_info_t; + +typedef struct { + bool valid; + sg_desc desc; // original desc with default values patched in + uint32_t frame_index; + struct { + bool valid; + bool in_pass; + sg_attachments atts_id; // SG_INVALID_ID in a swapchain pass + _sg_attachments_t* atts; // 0 in a swapchain pass + int width; + int height; + struct { + sg_pixel_format color_fmt; + sg_pixel_format depth_fmt; + int sample_count; + } swapchain; + } cur_pass; + sg_pipeline cur_pipeline; + bool next_draw_valid; + #if defined(SOKOL_DEBUG) + sg_log_item validate_error; + #endif + _sg_pools_t pools; + sg_backend backend; + sg_features features; + sg_limits limits; + _sg_pixelformat_info_t formats[_SG_PIXELFORMAT_NUM]; + bool stats_enabled; + sg_frame_stats stats; + sg_frame_stats prev_stats; + #if defined(_SOKOL_ANY_GL) + _sg_gl_backend_t gl; + #elif defined(SOKOL_METAL) + _sg_mtl_backend_t mtl; + #elif defined(SOKOL_D3D11) + _sg_d3d11_backend_t d3d11; + #elif defined(SOKOL_WGPU) + _sg_wgpu_backend_t wgpu; + #endif + #if defined(SOKOL_TRACE_HOOKS) + sg_trace_hooks hooks; + #endif + _sg_commit_listeners_t commit_listeners; +} _sg_state_t; +static _sg_state_t _sg; + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SG_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sg_log_messages[] = { + _SG_LOG_ITEMS +}; +#undef _SG_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SG_PANIC(code) _sg_log(SG_LOGITEM_ ##code, 0, 0, __LINE__) +#define _SG_ERROR(code) _sg_log(SG_LOGITEM_ ##code, 1, 0, __LINE__) +#define _SG_WARN(code) _sg_log(SG_LOGITEM_ ##code, 2, 0, __LINE__) +#define _SG_INFO(code) _sg_log(SG_LOGITEM_ ##code, 3, 0, __LINE__) +#define _SG_LOGMSG(code,msg) _sg_log(SG_LOGITEM_ ##code, 3, msg, __LINE__) +#define _SG_VALIDATE(cond,code) if (!(cond)){ _sg.validate_error = SG_LOGITEM_ ##code; _sg_log(SG_LOGITEM_ ##code, 1, 0, __LINE__); } + +static void _sg_log(sg_log_item log_item, uint32_t log_level, const char* msg, uint32_t line_nr) { + if (_sg.desc.logger.func) { + const char* filename = 0; + #if defined(SOKOL_DEBUG) + filename = __FILE__; + if (0 == msg) { + msg = _sg_log_messages[log_item]; + } + #endif + _sg.desc.logger.func("sg", log_level, log_item, msg, line_nr, filename, _sg.desc.logger.user_data); + } else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory + +// a helper macro to clear a struct with potentially ARC'ed ObjC references +#if defined(SOKOL_METAL) + #if defined(__cplusplus) + #define _SG_CLEAR_ARC_STRUCT(type, item) { item = type(); } + #else + #define _SG_CLEAR_ARC_STRUCT(type, item) { item = (type) { 0 }; } + #endif +#else + #define _SG_CLEAR_ARC_STRUCT(type, item) { _sg_clear(&item, sizeof(item)); } +#endif + +_SOKOL_PRIVATE void _sg_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +_SOKOL_PRIVATE void* _sg_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_sg.desc.allocator.alloc_fn) { + ptr = _sg.desc.allocator.alloc_fn(size, _sg.desc.allocator.user_data); + } else { + ptr = malloc(size); + } + if (0 == ptr) { + _SG_PANIC(MALLOC_FAILED); + } + return ptr; +} + +_SOKOL_PRIVATE void* _sg_malloc_clear(size_t size) { + void* ptr = _sg_malloc(size); + _sg_clear(ptr, size); + return ptr; +} + +_SOKOL_PRIVATE void _sg_free(void* ptr) { + if (_sg.desc.allocator.free_fn) { + _sg.desc.allocator.free_fn(ptr, _sg.desc.allocator.user_data); + } else { + free(ptr); + } +} + +_SOKOL_PRIVATE bool _sg_strempty(const _sg_str_t* str) { + return 0 == str->buf[0]; +} + +_SOKOL_PRIVATE const char* _sg_strptr(const _sg_str_t* str) { + return &str->buf[0]; +} + +_SOKOL_PRIVATE void _sg_strcpy(_sg_str_t* dst, const char* src) { + SOKOL_ASSERT(dst); + if (src) { + #if defined(_MSC_VER) + strncpy_s(dst->buf, _SG_STRING_SIZE, src, (_SG_STRING_SIZE-1)); + #else + strncpy(dst->buf, src, _SG_STRING_SIZE); + #endif + dst->buf[_SG_STRING_SIZE-1] = 0; + } else { + _sg_clear(dst->buf, _SG_STRING_SIZE); + } +} + +// ██ ██ ███████ ██ ██████ ███████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ █████ ██ ██████ █████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ +// +// >>helpers +_SOKOL_PRIVATE uint32_t _sg_align_u32(uint32_t val, uint32_t align) { + SOKOL_ASSERT((align > 0) && ((align & (align - 1)) == 0)); + return (val + (align - 1)) & ~(align - 1); +} + +typedef struct { int x, y, w, h; } _sg_recti_t; + +_SOKOL_PRIVATE _sg_recti_t _sg_clipi(int x, int y, int w, int h, int clip_width, int clip_height) { + x = _sg_min(_sg_max(0, x), clip_width-1); + y = _sg_min(_sg_max(0, y), clip_height-1); + if ((x + w) > clip_width) { + w = clip_width - x; + } + if ((y + h) > clip_height) { + h = clip_height - y; + } + w = _sg_max(w, 1); + h = _sg_max(h, 1); + const _sg_recti_t res = { x, y, w, h }; + return res; +} + +_SOKOL_PRIVATE int _sg_vertexformat_bytesize(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return 4; + case SG_VERTEXFORMAT_FLOAT2: return 8; + case SG_VERTEXFORMAT_FLOAT3: return 12; + case SG_VERTEXFORMAT_FLOAT4: return 16; + case SG_VERTEXFORMAT_BYTE4: return 4; + case SG_VERTEXFORMAT_BYTE4N: return 4; + case SG_VERTEXFORMAT_UBYTE4: return 4; + case SG_VERTEXFORMAT_UBYTE4N: return 4; + case SG_VERTEXFORMAT_SHORT2: return 4; + case SG_VERTEXFORMAT_SHORT2N: return 4; + case SG_VERTEXFORMAT_USHORT2N: return 4; + case SG_VERTEXFORMAT_SHORT4: return 8; + case SG_VERTEXFORMAT_SHORT4N: return 8; + case SG_VERTEXFORMAT_USHORT4N: return 8; + case SG_VERTEXFORMAT_UINT10_N2: return 4; + case SG_VERTEXFORMAT_HALF2: return 4; + case SG_VERTEXFORMAT_HALF4: return 8; + case SG_VERTEXFORMAT_INVALID: return 0; + default: + SOKOL_UNREACHABLE; + return -1; + } +} + +_SOKOL_PRIVATE uint32_t _sg_uniform_alignment(sg_uniform_type type, int array_count, sg_uniform_layout ub_layout) { + if (ub_layout == SG_UNIFORMLAYOUT_NATIVE) { + return 1; + } else { + SOKOL_ASSERT(array_count > 0); + if (array_count == 1) { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_INT: + return 4; + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_INT2: + return 8; + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT3: + case SG_UNIFORMTYPE_INT4: + return 16; + case SG_UNIFORMTYPE_MAT4: + return 16; + default: + SOKOL_UNREACHABLE; + return 1; + } + } else { + return 16; + } + } +} + +_SOKOL_PRIVATE uint32_t _sg_uniform_size(sg_uniform_type type, int array_count, sg_uniform_layout ub_layout) { + SOKOL_ASSERT(array_count > 0); + if (array_count == 1) { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_INT: + return 4; + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_INT2: + return 8; + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_INT3: + return 12; + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT4: + return 16; + case SG_UNIFORMTYPE_MAT4: + return 64; + default: + SOKOL_UNREACHABLE; + return 0; + } + } else { + if (ub_layout == SG_UNIFORMLAYOUT_NATIVE) { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_INT: + return 4 * (uint32_t)array_count; + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_INT2: + return 8 * (uint32_t)array_count; + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_INT3: + return 12 * (uint32_t)array_count; + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT4: + return 16 * (uint32_t)array_count; + case SG_UNIFORMTYPE_MAT4: + return 64 * (uint32_t)array_count; + default: + SOKOL_UNREACHABLE; + return 0; + } + } else { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT: + case SG_UNIFORMTYPE_INT2: + case SG_UNIFORMTYPE_INT3: + case SG_UNIFORMTYPE_INT4: + return 16 * (uint32_t)array_count; + case SG_UNIFORMTYPE_MAT4: + return 64 * (uint32_t)array_count; + default: + SOKOL_UNREACHABLE; + return 0; + } + } + } +} + +_SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_BC1_RGBA: + case SG_PIXELFORMAT_BC2_RGBA: + case SG_PIXELFORMAT_BC3_RGBA: + case SG_PIXELFORMAT_BC3_SRGBA: + case SG_PIXELFORMAT_BC4_R: + case SG_PIXELFORMAT_BC4_RSN: + case SG_PIXELFORMAT_BC5_RG: + case SG_PIXELFORMAT_BC5_RGSN: + case SG_PIXELFORMAT_BC6H_RGBF: + case SG_PIXELFORMAT_BC6H_RGBUF: + case SG_PIXELFORMAT_BC7_RGBA: + case SG_PIXELFORMAT_BC7_SRGBA: + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_SRGB8: + case SG_PIXELFORMAT_ETC2_RGB8A1: + case SG_PIXELFORMAT_ETC2_RGBA8: + case SG_PIXELFORMAT_ETC2_SRGB8A8: + case SG_PIXELFORMAT_EAC_R11: + case SG_PIXELFORMAT_EAC_R11SN: + case SG_PIXELFORMAT_EAC_RG11: + case SG_PIXELFORMAT_EAC_RG11SN: + case SG_PIXELFORMAT_ASTC_4x4_RGBA: + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: + return true; + default: + return false; + } +} + +_SOKOL_PRIVATE bool _sg_is_valid_rendertarget_color_format(sg_pixel_format fmt) { + const int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM)); + return _sg.formats[fmt_index].render && !_sg.formats[fmt_index].depth; +} + +_SOKOL_PRIVATE bool _sg_is_valid_rendertarget_depth_format(sg_pixel_format fmt) { + const int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM)); + return _sg.formats[fmt_index].render && _sg.formats[fmt_index].depth; +} + +_SOKOL_PRIVATE bool _sg_is_depth_or_depth_stencil_format(sg_pixel_format fmt) { + return (SG_PIXELFORMAT_DEPTH == fmt) || (SG_PIXELFORMAT_DEPTH_STENCIL == fmt); +} + +_SOKOL_PRIVATE bool _sg_is_depth_stencil_format(sg_pixel_format fmt) { + return (SG_PIXELFORMAT_DEPTH_STENCIL == fmt); +} + +_SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: + case SG_PIXELFORMAT_R8SN: + case SG_PIXELFORMAT_R8UI: + case SG_PIXELFORMAT_R8SI: + return 1; + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_R16UI: + case SG_PIXELFORMAT_R16SI: + case SG_PIXELFORMAT_R16F: + case SG_PIXELFORMAT_RG8: + case SG_PIXELFORMAT_RG8SN: + case SG_PIXELFORMAT_RG8UI: + case SG_PIXELFORMAT_RG8SI: + return 2; + case SG_PIXELFORMAT_R32UI: + case SG_PIXELFORMAT_R32SI: + case SG_PIXELFORMAT_R32F: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RG16UI: + case SG_PIXELFORMAT_RG16SI: + case SG_PIXELFORMAT_RG16F: + case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_SRGB8A8: + case SG_PIXELFORMAT_RGBA8SN: + case SG_PIXELFORMAT_RGBA8UI: + case SG_PIXELFORMAT_RGBA8SI: + case SG_PIXELFORMAT_BGRA8: + case SG_PIXELFORMAT_RGB10A2: + case SG_PIXELFORMAT_RG11B10F: + case SG_PIXELFORMAT_RGB9E5: + return 4; + case SG_PIXELFORMAT_RG32UI: + case SG_PIXELFORMAT_RG32SI: + case SG_PIXELFORMAT_RG32F: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_RGBA16UI: + case SG_PIXELFORMAT_RGBA16SI: + case SG_PIXELFORMAT_RGBA16F: + return 8; + case SG_PIXELFORMAT_RGBA32UI: + case SG_PIXELFORMAT_RGBA32SI: + case SG_PIXELFORMAT_RGBA32F: + return 16; + case SG_PIXELFORMAT_DEPTH: + case SG_PIXELFORMAT_DEPTH_STENCIL: + return 4; + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE int _sg_roundup(int val, int round_to) { + return (val+(round_to-1)) & ~(round_to-1); +} + +_SOKOL_PRIVATE uint32_t _sg_roundup_u32(uint32_t val, uint32_t round_to) { + return (val+(round_to-1)) & ~(round_to-1); +} + +_SOKOL_PRIVATE uint64_t _sg_roundup_u64(uint64_t val, uint64_t round_to) { + return (val+(round_to-1)) & ~(round_to-1); +} + +_SOKOL_PRIVATE bool _sg_multiple_u64(uint64_t val, uint64_t of) { + return (val & (of-1)) == 0; +} + +/* return row pitch for an image + + see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp + + For the special PVRTC pitch computation, see: + GL extension requirement (https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt) + + Quote: + + 6) How is the imageSize argument calculated for the CompressedTexImage2D + and CompressedTexSubImage2D functions. + + Resolution: For PVRTC 4BPP formats the imageSize is calculated as: + ( max(width, 8) * max(height, 8) * 4 + 7) / 8 + For PVRTC 2BPP formats the imageSize is calculated as: + ( max(width, 16) * max(height, 8) * 2 + 7) / 8 +*/ +_SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width, int row_align) { + int pitch; + switch (fmt) { + case SG_PIXELFORMAT_BC1_RGBA: + case SG_PIXELFORMAT_BC4_R: + case SG_PIXELFORMAT_BC4_RSN: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_SRGB8: + case SG_PIXELFORMAT_ETC2_RGB8A1: + case SG_PIXELFORMAT_EAC_R11: + case SG_PIXELFORMAT_EAC_R11SN: + pitch = ((width + 3) / 4) * 8; + pitch = pitch < 8 ? 8 : pitch; + break; + case SG_PIXELFORMAT_BC2_RGBA: + case SG_PIXELFORMAT_BC3_RGBA: + case SG_PIXELFORMAT_BC3_SRGBA: + case SG_PIXELFORMAT_BC5_RG: + case SG_PIXELFORMAT_BC5_RGSN: + case SG_PIXELFORMAT_BC6H_RGBF: + case SG_PIXELFORMAT_BC6H_RGBUF: + case SG_PIXELFORMAT_BC7_RGBA: + case SG_PIXELFORMAT_BC7_SRGBA: + case SG_PIXELFORMAT_ETC2_RGBA8: + case SG_PIXELFORMAT_ETC2_SRGB8A8: + case SG_PIXELFORMAT_EAC_RG11: + case SG_PIXELFORMAT_EAC_RG11SN: + case SG_PIXELFORMAT_ASTC_4x4_RGBA: + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: + pitch = ((width + 3) / 4) * 16; + pitch = pitch < 16 ? 16 : pitch; + break; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + pitch = (_sg_max(width, 8) * 4 + 7) / 8; + break; + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + pitch = (_sg_max(width, 16) * 2 + 7) / 8; + break; + default: + pitch = width * _sg_pixelformat_bytesize(fmt); + break; + } + pitch = _sg_roundup(pitch, row_align); + return pitch; +} + +// compute the number of rows in a surface depending on pixel format +_SOKOL_PRIVATE int _sg_num_rows(sg_pixel_format fmt, int height) { + int num_rows; + switch (fmt) { + case SG_PIXELFORMAT_BC1_RGBA: + case SG_PIXELFORMAT_BC4_R: + case SG_PIXELFORMAT_BC4_RSN: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_SRGB8: + case SG_PIXELFORMAT_ETC2_RGB8A1: + case SG_PIXELFORMAT_ETC2_RGBA8: + case SG_PIXELFORMAT_ETC2_SRGB8A8: + case SG_PIXELFORMAT_EAC_R11: + case SG_PIXELFORMAT_EAC_R11SN: + case SG_PIXELFORMAT_EAC_RG11: + case SG_PIXELFORMAT_EAC_RG11SN: + case SG_PIXELFORMAT_BC2_RGBA: + case SG_PIXELFORMAT_BC3_RGBA: + case SG_PIXELFORMAT_BC3_SRGBA: + case SG_PIXELFORMAT_BC5_RG: + case SG_PIXELFORMAT_BC5_RGSN: + case SG_PIXELFORMAT_BC6H_RGBF: + case SG_PIXELFORMAT_BC6H_RGBUF: + case SG_PIXELFORMAT_BC7_RGBA: + case SG_PIXELFORMAT_BC7_SRGBA: + case SG_PIXELFORMAT_ASTC_4x4_RGBA: + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: + num_rows = ((height + 3) / 4); + break; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + /* NOTE: this is most likely not correct because it ignores any + PVCRTC block size, but multiplied with _sg_row_pitch() + it gives the correct surface pitch. + + See: https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt + */ + num_rows = ((_sg_max(height, 8) + 7) / 8) * 8; + break; + default: + num_rows = height; + break; + } + if (num_rows < 1) { + num_rows = 1; + } + return num_rows; +} + +// return size of a mipmap level +_SOKOL_PRIVATE int _sg_miplevel_dim(int base_dim, int mip_level) { + return _sg_max(base_dim >> mip_level, 1); +} + +/* return pitch of a 2D subimage / texture slice + see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp +*/ +_SOKOL_PRIVATE int _sg_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align) { + int num_rows = _sg_num_rows(fmt, height); + return num_rows * _sg_row_pitch(fmt, width, row_align); +} + +// capability table pixel format helper functions +_SOKOL_PRIVATE void _sg_pixelformat_all(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->blend = true; + pfi->render = true; + pfi->msaa = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_s(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sf(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->filter = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sr(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->render = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sfr(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->render = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_srmd(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->render = true; + pfi->msaa = true; + pfi->depth = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_srm(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->render = true; + pfi->msaa = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sfrm(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->render = true; + pfi->msaa = true; +} +_SOKOL_PRIVATE void _sg_pixelformat_sbrm(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->blend = true; + pfi->render = true; + pfi->msaa = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sbr(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->blend = true; + pfi->render = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sfbr(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->blend = true; + pfi->render = true; +} + +_SOKOL_PRIVATE sg_pass_action _sg_pass_action_defaults(const sg_pass_action* action) { + SOKOL_ASSERT(action); + sg_pass_action res = *action; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (res.colors[i].load_action == _SG_LOADACTION_DEFAULT) { + res.colors[i].load_action = SG_LOADACTION_CLEAR; + res.colors[i].clear_value.r = SG_DEFAULT_CLEAR_RED; + res.colors[i].clear_value.g = SG_DEFAULT_CLEAR_GREEN; + res.colors[i].clear_value.b = SG_DEFAULT_CLEAR_BLUE; + res.colors[i].clear_value.a = SG_DEFAULT_CLEAR_ALPHA; + } + if (res.colors[i].store_action == _SG_STOREACTION_DEFAULT) { + res.colors[i].store_action = SG_STOREACTION_STORE; + } + } + if (res.depth.load_action == _SG_LOADACTION_DEFAULT) { + res.depth.load_action = SG_LOADACTION_CLEAR; + res.depth.clear_value = SG_DEFAULT_CLEAR_DEPTH; + } + if (res.depth.store_action == _SG_STOREACTION_DEFAULT) { + res.depth.store_action = SG_STOREACTION_DONTCARE; + } + if (res.stencil.load_action == _SG_LOADACTION_DEFAULT) { + res.stencil.load_action = SG_LOADACTION_CLEAR; + res.stencil.clear_value = SG_DEFAULT_CLEAR_STENCIL; + } + if (res.stencil.store_action == _SG_STOREACTION_DEFAULT) { + res.stencil.store_action = SG_STOREACTION_DONTCARE; + } + return res; +} + +// ██████ ██ ██ ███ ███ ███ ███ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ████ ████ ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ ██ ██ ██ ████ ██ ██ ████ ██ ████ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ██ ██ ██ ██ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>dummy backend +#if defined(SOKOL_DUMMY_BACKEND) + +_SOKOL_PRIVATE void _sg_dummy_setup_backend(const sg_desc* desc) { + SOKOL_ASSERT(desc); + _SOKOL_UNUSED(desc); + _sg.backend = SG_BACKEND_DUMMY; + for (int i = SG_PIXELFORMAT_R8; i < SG_PIXELFORMAT_BC1_RGBA; i++) { + _sg.formats[i].sample = true; + _sg.formats[i].filter = true; + _sg.formats[i].render = true; + _sg.formats[i].blend = true; + _sg.formats[i].msaa = true; + } + _sg.formats[SG_PIXELFORMAT_DEPTH].depth = true; + _sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL].depth = true; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_backend(void) { + // empty +} + +_SOKOL_PRIVATE void _sg_dummy_reset_state_cache(void) { + // empty +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + _SOKOL_UNUSED(buf); + _SOKOL_UNUSED(desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + _SOKOL_UNUSED(buf); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + _SOKOL_UNUSED(img); + _SOKOL_UNUSED(desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + _SOKOL_UNUSED(img); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + _SOKOL_UNUSED(smp); + _SOKOL_UNUSED(desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + _SOKOL_UNUSED(smp); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + _SOKOL_UNUSED(shd); + _SOKOL_UNUSED(desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _SOKOL_UNUSED(shd); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && desc); + pip->shader = shd; + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + pip->cmn.vertex_buffer_layout_active[a_state->buffer_index] = true; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _SOKOL_UNUSED(pip); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_attachments(_sg_attachments_t* atts, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_attachments_desc* desc) { + SOKOL_ASSERT(atts && desc); + SOKOL_ASSERT(color_images && resolve_images); + + for (int i = 0; i < atts->cmn.num_colors; i++) { + const sg_attachment_desc* color_desc = &desc->colors[i]; + _SOKOL_UNUSED(color_desc); + SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == atts->dmy.colors[i].image); + SOKOL_ASSERT(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); + atts->dmy.colors[i].image = color_images[i]; + + const sg_attachment_desc* resolve_desc = &desc->resolves[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == atts->dmy.resolves[i].image); + SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); + SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); + atts->dmy.resolves[i].image = resolve_images[i]; + } + } + + SOKOL_ASSERT(0 == atts->dmy.depth_stencil.image); + const sg_attachment_desc* ds_desc = &desc->depth_stencil; + if (ds_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(ds_img && (ds_img->slot.id == ds_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_img->cmn.pixel_format)); + atts->dmy.depth_stencil.image = ds_img; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + _SOKOL_UNUSED(atts); +} + +_SOKOL_PRIVATE _sg_image_t* _sg_dummy_attachments_color_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->dmy.colors[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_dummy_attachments_resolve_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->dmy.resolves[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_dummy_attachments_ds_image(const _sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + return atts->dmy.depth_stencil.image; +} + +_SOKOL_PRIVATE void _sg_dummy_begin_pass(const sg_pass* pass) { + SOKOL_ASSERT(pass); + _SOKOL_UNUSED(pass); +} + +_SOKOL_PRIVATE void _sg_dummy_end_pass(void) { + // empty +} + +_SOKOL_PRIVATE void _sg_dummy_commit(void) { + // empty +} + +_SOKOL_PRIVATE void _sg_dummy_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + _SOKOL_UNUSED(x); + _SOKOL_UNUSED(y); + _SOKOL_UNUSED(w); + _SOKOL_UNUSED(h); + _SOKOL_UNUSED(origin_top_left); +} + +_SOKOL_PRIVATE void _sg_dummy_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + _SOKOL_UNUSED(x); + _SOKOL_UNUSED(y); + _SOKOL_UNUSED(w); + _SOKOL_UNUSED(h); + _SOKOL_UNUSED(origin_top_left); +} + +_SOKOL_PRIVATE void _sg_dummy_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _SOKOL_UNUSED(pip); +} + +_SOKOL_PRIVATE bool _sg_dummy_apply_bindings(_sg_bindings_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); + _SOKOL_UNUSED(bnd); + return true; +} + +_SOKOL_PRIVATE void _sg_dummy_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + _SOKOL_UNUSED(stage_index); + _SOKOL_UNUSED(ub_index); + _SOKOL_UNUSED(data); +} + +_SOKOL_PRIVATE void _sg_dummy_draw(int base_element, int num_elements, int num_instances) { + _SOKOL_UNUSED(base_element); + _SOKOL_UNUSED(num_elements); + _SOKOL_UNUSED(num_instances); +} + +_SOKOL_PRIVATE void _sg_dummy_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + _SOKOL_UNUSED(data); + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } +} + +_SOKOL_PRIVATE bool _sg_dummy_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + _SOKOL_UNUSED(data); + if (new_frame) { + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + } + return true; +} + +_SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + _SOKOL_UNUSED(data); + if (++img->cmn.active_slot >= img->cmn.num_slots) { + img->cmn.active_slot = 0; + } +} + +// ██████ ██████ ███████ ███ ██ ██████ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ ██████ █████ ██ ██ ██ ██ ███ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ███████ ██ ████ ██████ ███████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>opengl backend +#elif defined(_SOKOL_ANY_GL) + +// optional GL loader for win32 +#if defined(_SOKOL_USE_WIN32_GL_LOADER) + +#ifndef SG_GL_FUNCS_EXT +#define SG_GL_FUNCS_EXT +#endif + +// X Macro list of GL function names and signatures +#define _SG_GL_FUNCS \ + SG_GL_FUNCS_EXT \ + _SG_XMACRO(glBindVertexArray, void, (GLuint array)) \ + _SG_XMACRO(glFramebufferTextureLayer, void, (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer)) \ + _SG_XMACRO(glGenFramebuffers, void, (GLsizei n, GLuint * framebuffers)) \ + _SG_XMACRO(glBindFramebuffer, void, (GLenum target, GLuint framebuffer)) \ + _SG_XMACRO(glBindRenderbuffer, void, (GLenum target, GLuint renderbuffer)) \ + _SG_XMACRO(glGetStringi, const GLubyte *, (GLenum name, GLuint index)) \ + _SG_XMACRO(glClearBufferfi, void, (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil)) \ + _SG_XMACRO(glClearBufferfv, void, (GLenum buffer, GLint drawbuffer, const GLfloat * value)) \ + _SG_XMACRO(glClearBufferuiv, void, (GLenum buffer, GLint drawbuffer, const GLuint * value)) \ + _SG_XMACRO(glClearBufferiv, void, (GLenum buffer, GLint drawbuffer, const GLint * value)) \ + _SG_XMACRO(glDeleteRenderbuffers, void, (GLsizei n, const GLuint * renderbuffers)) \ + _SG_XMACRO(glUniform1fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SG_XMACRO(glUniform2fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SG_XMACRO(glUniform3fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SG_XMACRO(glUniform4fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SG_XMACRO(glUniform1iv, void, (GLint location, GLsizei count, const GLint * value)) \ + _SG_XMACRO(glUniform2iv, void, (GLint location, GLsizei count, const GLint * value)) \ + _SG_XMACRO(glUniform3iv, void, (GLint location, GLsizei count, const GLint * value)) \ + _SG_XMACRO(glUniform4iv, void, (GLint location, GLsizei count, const GLint * value)) \ + _SG_XMACRO(glUniformMatrix4fv, void, (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)) \ + _SG_XMACRO(glUseProgram, void, (GLuint program)) \ + _SG_XMACRO(glShaderSource, void, (GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length)) \ + _SG_XMACRO(glLinkProgram, void, (GLuint program)) \ + _SG_XMACRO(glGetUniformLocation, GLint, (GLuint program, const GLchar * name)) \ + _SG_XMACRO(glGetShaderiv, void, (GLuint shader, GLenum pname, GLint * params)) \ + _SG_XMACRO(glGetProgramInfoLog, void, (GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \ + _SG_XMACRO(glGetAttribLocation, GLint, (GLuint program, const GLchar * name)) \ + _SG_XMACRO(glDisableVertexAttribArray, void, (GLuint index)) \ + _SG_XMACRO(glDeleteShader, void, (GLuint shader)) \ + _SG_XMACRO(glDeleteProgram, void, (GLuint program)) \ + _SG_XMACRO(glCompileShader, void, (GLuint shader)) \ + _SG_XMACRO(glStencilFuncSeparate, void, (GLenum face, GLenum func, GLint ref, GLuint mask)) \ + _SG_XMACRO(glStencilOpSeparate, void, (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)) \ + _SG_XMACRO(glRenderbufferStorageMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)) \ + _SG_XMACRO(glDrawBuffers, void, (GLsizei n, const GLenum * bufs)) \ + _SG_XMACRO(glVertexAttribDivisor, void, (GLuint index, GLuint divisor)) \ + _SG_XMACRO(glBufferSubData, void, (GLenum target, GLintptr offset, GLsizeiptr size, const void * data)) \ + _SG_XMACRO(glGenBuffers, void, (GLsizei n, GLuint * buffers)) \ + _SG_XMACRO(glCheckFramebufferStatus, GLenum, (GLenum target)) \ + _SG_XMACRO(glFramebufferRenderbuffer, void, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)) \ + _SG_XMACRO(glCompressedTexImage2D, void, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data)) \ + _SG_XMACRO(glCompressedTexImage3D, void, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data)) \ + _SG_XMACRO(glActiveTexture, void, (GLenum texture)) \ + _SG_XMACRO(glTexSubImage3D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels)) \ + _SG_XMACRO(glRenderbufferStorage, void, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height)) \ + _SG_XMACRO(glGenTextures, void, (GLsizei n, GLuint * textures)) \ + _SG_XMACRO(glPolygonOffset, void, (GLfloat factor, GLfloat units)) \ + _SG_XMACRO(glDrawElements, void, (GLenum mode, GLsizei count, GLenum type, const void * indices)) \ + _SG_XMACRO(glDeleteFramebuffers, void, (GLsizei n, const GLuint * framebuffers)) \ + _SG_XMACRO(glBlendEquationSeparate, void, (GLenum modeRGB, GLenum modeAlpha)) \ + _SG_XMACRO(glDeleteTextures, void, (GLsizei n, const GLuint * textures)) \ + _SG_XMACRO(glGetProgramiv, void, (GLuint program, GLenum pname, GLint * params)) \ + _SG_XMACRO(glBindTexture, void, (GLenum target, GLuint texture)) \ + _SG_XMACRO(glTexImage3D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels)) \ + _SG_XMACRO(glCreateShader, GLuint, (GLenum type)) \ + _SG_XMACRO(glTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels)) \ + _SG_XMACRO(glFramebufferTexture2D, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) \ + _SG_XMACRO(glCreateProgram, GLuint, (void)) \ + _SG_XMACRO(glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ + _SG_XMACRO(glDeleteBuffers, void, (GLsizei n, const GLuint * buffers)) \ + _SG_XMACRO(glDrawArrays, void, (GLenum mode, GLint first, GLsizei count)) \ + _SG_XMACRO(glDrawElementsInstanced, void, (GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount)) \ + _SG_XMACRO(glVertexAttribPointer, void, (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer)) \ + _SG_XMACRO(glUniform1i, void, (GLint location, GLint v0)) \ + _SG_XMACRO(glDisable, void, (GLenum cap)) \ + _SG_XMACRO(glColorMask, void, (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) \ + _SG_XMACRO(glColorMaski, void, (GLuint buf, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) \ + _SG_XMACRO(glBindBuffer, void, (GLenum target, GLuint buffer)) \ + _SG_XMACRO(glDeleteVertexArrays, void, (GLsizei n, const GLuint * arrays)) \ + _SG_XMACRO(glDepthMask, void, (GLboolean flag)) \ + _SG_XMACRO(glDrawArraysInstanced, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount)) \ + _SG_XMACRO(glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ + _SG_XMACRO(glGenRenderbuffers, void, (GLsizei n, GLuint * renderbuffers)) \ + _SG_XMACRO(glBufferData, void, (GLenum target, GLsizeiptr size, const void * data, GLenum usage)) \ + _SG_XMACRO(glBlendFuncSeparate, void, (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha)) \ + _SG_XMACRO(glTexParameteri, void, (GLenum target, GLenum pname, GLint param)) \ + _SG_XMACRO(glGetIntegerv, void, (GLenum pname, GLint * data)) \ + _SG_XMACRO(glEnable, void, (GLenum cap)) \ + _SG_XMACRO(glBlitFramebuffer, void, (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)) \ + _SG_XMACRO(glStencilMask, void, (GLuint mask)) \ + _SG_XMACRO(glAttachShader, void, (GLuint program, GLuint shader)) \ + _SG_XMACRO(glGetError, GLenum, (void)) \ + _SG_XMACRO(glBlendColor, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) \ + _SG_XMACRO(glTexParameterf, void, (GLenum target, GLenum pname, GLfloat param)) \ + _SG_XMACRO(glTexParameterfv, void, (GLenum target, GLenum pname, const GLfloat* params)) \ + _SG_XMACRO(glGetShaderInfoLog, void, (GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \ + _SG_XMACRO(glDepthFunc, void, (GLenum func)) \ + _SG_XMACRO(glStencilOp , void, (GLenum fail, GLenum zfail, GLenum zpass)) \ + _SG_XMACRO(glStencilFunc, void, (GLenum func, GLint ref, GLuint mask)) \ + _SG_XMACRO(glEnableVertexAttribArray, void, (GLuint index)) \ + _SG_XMACRO(glBlendFunc, void, (GLenum sfactor, GLenum dfactor)) \ + _SG_XMACRO(glReadBuffer, void, (GLenum src)) \ + _SG_XMACRO(glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels)) \ + _SG_XMACRO(glGenVertexArrays, void, (GLsizei n, GLuint * arrays)) \ + _SG_XMACRO(glFrontFace, void, (GLenum mode)) \ + _SG_XMACRO(glCullFace, void, (GLenum mode)) \ + _SG_XMACRO(glPixelStorei, void, (GLenum pname, GLint param)) \ + _SG_XMACRO(glBindSampler, void, (GLuint unit, GLuint sampler)) \ + _SG_XMACRO(glGenSamplers, void, (GLsizei n, GLuint* samplers)) \ + _SG_XMACRO(glSamplerParameteri, void, (GLuint sampler, GLenum pname, GLint param)) \ + _SG_XMACRO(glSamplerParameterf, void, (GLuint sampler, GLenum pname, GLfloat param)) \ + _SG_XMACRO(glSamplerParameterfv, void, (GLuint sampler, GLenum pname, const GLfloat* params)) \ + _SG_XMACRO(glDeleteSamplers, void, (GLsizei n, const GLuint* samplers)) \ + _SG_XMACRO(glBindBufferBase, void, (GLenum target, GLuint index, GLuint buffer)) + +// generate GL function pointer typedefs +#define _SG_XMACRO(name, ret, args) typedef ret (GL_APIENTRY* PFN_ ## name) args; +_SG_GL_FUNCS +#undef _SG_XMACRO + +// generate GL function pointers +#define _SG_XMACRO(name, ret, args) static PFN_ ## name name; +_SG_GL_FUNCS +#undef _SG_XMACRO + +// helper function to lookup GL functions in GL DLL +typedef PROC (WINAPI * _sg_wglGetProcAddress)(LPCSTR); +_SOKOL_PRIVATE void* _sg_gl_getprocaddr(const char* name, _sg_wglGetProcAddress wgl_getprocaddress) { + void* proc_addr = (void*) wgl_getprocaddress(name); + if (0 == proc_addr) { + proc_addr = (void*) GetProcAddress(_sg.gl.opengl32_dll, name); + } + SOKOL_ASSERT(proc_addr); + return proc_addr; +} + +// populate GL function pointers +_SOKOL_PRIVATE void _sg_gl_load_opengl(void) { + SOKOL_ASSERT(0 == _sg.gl.opengl32_dll); + _sg.gl.opengl32_dll = LoadLibraryA("opengl32.dll"); + SOKOL_ASSERT(_sg.gl.opengl32_dll); + _sg_wglGetProcAddress wgl_getprocaddress = (_sg_wglGetProcAddress) GetProcAddress(_sg.gl.opengl32_dll, "wglGetProcAddress"); + SOKOL_ASSERT(wgl_getprocaddress); + #define _SG_XMACRO(name, ret, args) name = (PFN_ ## name) _sg_gl_getprocaddr(#name, wgl_getprocaddress); + _SG_GL_FUNCS + #undef _SG_XMACRO +} + +_SOKOL_PRIVATE void _sg_gl_unload_opengl(void) { + SOKOL_ASSERT(_sg.gl.opengl32_dll); + FreeLibrary(_sg.gl.opengl32_dll); + _sg.gl.opengl32_dll = 0; +} +#endif // _SOKOL_USE_WIN32_GL_LOADER + +//-- type translation ---------------------------------------------------------- +_SOKOL_PRIVATE GLenum _sg_gl_buffer_target(sg_buffer_type t) { + switch (t) { + case SG_BUFFERTYPE_VERTEXBUFFER: return GL_ARRAY_BUFFER; + case SG_BUFFERTYPE_INDEXBUFFER: return GL_ELEMENT_ARRAY_BUFFER; + case SG_BUFFERTYPE_STORAGEBUFFER: return GL_SHADER_STORAGE_BUFFER; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_texture_target(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return GL_TEXTURE_2D; + case SG_IMAGETYPE_CUBE: return GL_TEXTURE_CUBE_MAP; + case SG_IMAGETYPE_3D: return GL_TEXTURE_3D; + case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_ARRAY; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_usage(sg_usage u) { + switch (u) { + case SG_USAGE_IMMUTABLE: return GL_STATIC_DRAW; + case SG_USAGE_DYNAMIC: return GL_DYNAMIC_DRAW; + case SG_USAGE_STREAM: return GL_STREAM_DRAW; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_shader_stage(sg_shader_stage stage) { + switch (stage) { + case SG_SHADERSTAGE_VS: return GL_VERTEX_SHADER; + case SG_SHADERSTAGE_FS: return GL_FRAGMENT_SHADER; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLint _sg_gl_vertexformat_size(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return 1; + case SG_VERTEXFORMAT_FLOAT2: return 2; + case SG_VERTEXFORMAT_FLOAT3: return 3; + case SG_VERTEXFORMAT_FLOAT4: return 4; + case SG_VERTEXFORMAT_BYTE4: return 4; + case SG_VERTEXFORMAT_BYTE4N: return 4; + case SG_VERTEXFORMAT_UBYTE4: return 4; + case SG_VERTEXFORMAT_UBYTE4N: return 4; + case SG_VERTEXFORMAT_SHORT2: return 2; + case SG_VERTEXFORMAT_SHORT2N: return 2; + case SG_VERTEXFORMAT_USHORT2N: return 2; + case SG_VERTEXFORMAT_SHORT4: return 4; + case SG_VERTEXFORMAT_SHORT4N: return 4; + case SG_VERTEXFORMAT_USHORT4N: return 4; + case SG_VERTEXFORMAT_UINT10_N2: return 4; + case SG_VERTEXFORMAT_HALF2: return 2; + case SG_VERTEXFORMAT_HALF4: return 4; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_vertexformat_type(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: + case SG_VERTEXFORMAT_FLOAT2: + case SG_VERTEXFORMAT_FLOAT3: + case SG_VERTEXFORMAT_FLOAT4: + return GL_FLOAT; + case SG_VERTEXFORMAT_BYTE4: + case SG_VERTEXFORMAT_BYTE4N: + return GL_BYTE; + case SG_VERTEXFORMAT_UBYTE4: + case SG_VERTEXFORMAT_UBYTE4N: + return GL_UNSIGNED_BYTE; + case SG_VERTEXFORMAT_SHORT2: + case SG_VERTEXFORMAT_SHORT2N: + case SG_VERTEXFORMAT_SHORT4: + case SG_VERTEXFORMAT_SHORT4N: + return GL_SHORT; + case SG_VERTEXFORMAT_USHORT2N: + case SG_VERTEXFORMAT_USHORT4N: + return GL_UNSIGNED_SHORT; + case SG_VERTEXFORMAT_UINT10_N2: + return GL_UNSIGNED_INT_2_10_10_10_REV; + case SG_VERTEXFORMAT_HALF2: + case SG_VERTEXFORMAT_HALF4: + return GL_HALF_FLOAT; + default: + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLboolean _sg_gl_vertexformat_normalized(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_BYTE4N: + case SG_VERTEXFORMAT_UBYTE4N: + case SG_VERTEXFORMAT_SHORT2N: + case SG_VERTEXFORMAT_USHORT2N: + case SG_VERTEXFORMAT_SHORT4N: + case SG_VERTEXFORMAT_USHORT4N: + case SG_VERTEXFORMAT_UINT10_N2: + return GL_TRUE; + default: + return GL_FALSE; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_primitive_type(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return GL_POINTS; + case SG_PRIMITIVETYPE_LINES: return GL_LINES; + case SG_PRIMITIVETYPE_LINE_STRIP: return GL_LINE_STRIP; + case SG_PRIMITIVETYPE_TRIANGLES: return GL_TRIANGLES; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_index_type(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_NONE: return 0; + case SG_INDEXTYPE_UINT16: return GL_UNSIGNED_SHORT; + case SG_INDEXTYPE_UINT32: return GL_UNSIGNED_INT; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_compare_func(sg_compare_func cmp) { + switch (cmp) { + case SG_COMPAREFUNC_NEVER: return GL_NEVER; + case SG_COMPAREFUNC_LESS: return GL_LESS; + case SG_COMPAREFUNC_EQUAL: return GL_EQUAL; + case SG_COMPAREFUNC_LESS_EQUAL: return GL_LEQUAL; + case SG_COMPAREFUNC_GREATER: return GL_GREATER; + case SG_COMPAREFUNC_NOT_EQUAL: return GL_NOTEQUAL; + case SG_COMPAREFUNC_GREATER_EQUAL: return GL_GEQUAL; + case SG_COMPAREFUNC_ALWAYS: return GL_ALWAYS; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return GL_KEEP; + case SG_STENCILOP_ZERO: return GL_ZERO; + case SG_STENCILOP_REPLACE: return GL_REPLACE; + case SG_STENCILOP_INCR_CLAMP: return GL_INCR; + case SG_STENCILOP_DECR_CLAMP: return GL_DECR; + case SG_STENCILOP_INVERT: return GL_INVERT; + case SG_STENCILOP_INCR_WRAP: return GL_INCR_WRAP; + case SG_STENCILOP_DECR_WRAP: return GL_DECR_WRAP; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return GL_ZERO; + case SG_BLENDFACTOR_ONE: return GL_ONE; + case SG_BLENDFACTOR_SRC_COLOR: return GL_SRC_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR; + case SG_BLENDFACTOR_SRC_ALPHA: return GL_SRC_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA; + case SG_BLENDFACTOR_DST_COLOR: return GL_DST_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return GL_ONE_MINUS_DST_COLOR; + case SG_BLENDFACTOR_DST_ALPHA: return GL_DST_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return GL_SRC_ALPHA_SATURATE; + case SG_BLENDFACTOR_BLEND_COLOR: return GL_CONSTANT_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return GL_ONE_MINUS_CONSTANT_COLOR; + case SG_BLENDFACTOR_BLEND_ALPHA: return GL_CONSTANT_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return GL_ONE_MINUS_CONSTANT_ALPHA; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return GL_FUNC_ADD; + case SG_BLENDOP_SUBTRACT: return GL_FUNC_SUBTRACT; + case SG_BLENDOP_REVERSE_SUBTRACT: return GL_FUNC_REVERSE_SUBTRACT; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_min_filter(sg_filter min_f, sg_filter mipmap_f) { + if (min_f == SG_FILTER_NEAREST) { + switch (mipmap_f) { + case SG_FILTER_NONE: return GL_NEAREST; + case SG_FILTER_NEAREST: return GL_NEAREST_MIPMAP_NEAREST; + case SG_FILTER_LINEAR: return GL_NEAREST_MIPMAP_LINEAR; + default: SOKOL_UNREACHABLE; return (GLenum)0; + } + } else if (min_f == SG_FILTER_LINEAR) { + switch (mipmap_f) { + case SG_FILTER_NONE: return GL_LINEAR; + case SG_FILTER_NEAREST: return GL_LINEAR_MIPMAP_NEAREST; + case SG_FILTER_LINEAR: return GL_LINEAR_MIPMAP_LINEAR; + default: SOKOL_UNREACHABLE; return (GLenum)0; + } + } else { + SOKOL_UNREACHABLE; return (GLenum)0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_mag_filter(sg_filter mag_f) { + if (mag_f == SG_FILTER_NEAREST) { + return GL_NEAREST; + } else { + return GL_LINEAR; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_wrap(sg_wrap w) { + switch (w) { + case SG_WRAP_CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE; + #if defined(SOKOL_GLCORE) + case SG_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_BORDER; + #else + case SG_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_EDGE; + #endif + case SG_WRAP_REPEAT: return GL_REPEAT; + case SG_WRAP_MIRRORED_REPEAT: return GL_MIRRORED_REPEAT; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_teximage_type(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: + case SG_PIXELFORMAT_R8UI: + case SG_PIXELFORMAT_RG8: + case SG_PIXELFORMAT_RG8UI: + case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_SRGB8A8: + case SG_PIXELFORMAT_RGBA8UI: + case SG_PIXELFORMAT_BGRA8: + return GL_UNSIGNED_BYTE; + case SG_PIXELFORMAT_R8SN: + case SG_PIXELFORMAT_R8SI: + case SG_PIXELFORMAT_RG8SN: + case SG_PIXELFORMAT_RG8SI: + case SG_PIXELFORMAT_RGBA8SN: + case SG_PIXELFORMAT_RGBA8SI: + return GL_BYTE; + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16UI: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16UI: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16UI: + return GL_UNSIGNED_SHORT; + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_R16SI: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RG16SI: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_RGBA16SI: + return GL_SHORT; + case SG_PIXELFORMAT_R16F: + case SG_PIXELFORMAT_RG16F: + case SG_PIXELFORMAT_RGBA16F: + return GL_HALF_FLOAT; + case SG_PIXELFORMAT_R32UI: + case SG_PIXELFORMAT_RG32UI: + case SG_PIXELFORMAT_RGBA32UI: + return GL_UNSIGNED_INT; + case SG_PIXELFORMAT_R32SI: + case SG_PIXELFORMAT_RG32SI: + case SG_PIXELFORMAT_RGBA32SI: + return GL_INT; + case SG_PIXELFORMAT_R32F: + case SG_PIXELFORMAT_RG32F: + case SG_PIXELFORMAT_RGBA32F: + return GL_FLOAT; + case SG_PIXELFORMAT_RGB10A2: + return GL_UNSIGNED_INT_2_10_10_10_REV; + case SG_PIXELFORMAT_RG11B10F: + return GL_UNSIGNED_INT_10F_11F_11F_REV; + case SG_PIXELFORMAT_RGB9E5: + return GL_UNSIGNED_INT_5_9_9_9_REV; + case SG_PIXELFORMAT_DEPTH: + return GL_FLOAT; + case SG_PIXELFORMAT_DEPTH_STENCIL: + return GL_UNSIGNED_INT_24_8; + default: + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: + case SG_PIXELFORMAT_R8SN: + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_R16F: + case SG_PIXELFORMAT_R32F: + return GL_RED; + case SG_PIXELFORMAT_R8UI: + case SG_PIXELFORMAT_R8SI: + case SG_PIXELFORMAT_R16UI: + case SG_PIXELFORMAT_R16SI: + case SG_PIXELFORMAT_R32UI: + case SG_PIXELFORMAT_R32SI: + return GL_RED_INTEGER; + case SG_PIXELFORMAT_RG8: + case SG_PIXELFORMAT_RG8SN: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RG16F: + case SG_PIXELFORMAT_RG32F: + return GL_RG; + case SG_PIXELFORMAT_RG8UI: + case SG_PIXELFORMAT_RG8SI: + case SG_PIXELFORMAT_RG16UI: + case SG_PIXELFORMAT_RG16SI: + case SG_PIXELFORMAT_RG32UI: + case SG_PIXELFORMAT_RG32SI: + return GL_RG_INTEGER; + case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_SRGB8A8: + case SG_PIXELFORMAT_RGBA8SN: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_RGBA16F: + case SG_PIXELFORMAT_RGBA32F: + case SG_PIXELFORMAT_RGB10A2: + return GL_RGBA; + case SG_PIXELFORMAT_RGBA8UI: + case SG_PIXELFORMAT_RGBA8SI: + case SG_PIXELFORMAT_RGBA16UI: + case SG_PIXELFORMAT_RGBA16SI: + case SG_PIXELFORMAT_RGBA32UI: + case SG_PIXELFORMAT_RGBA32SI: + return GL_RGBA_INTEGER; + case SG_PIXELFORMAT_RG11B10F: + case SG_PIXELFORMAT_RGB9E5: + return GL_RGB; + case SG_PIXELFORMAT_DEPTH: + return GL_DEPTH_COMPONENT; + case SG_PIXELFORMAT_DEPTH_STENCIL: + return GL_DEPTH_STENCIL; + case SG_PIXELFORMAT_BC1_RGBA: + return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case SG_PIXELFORMAT_BC2_RGBA: + return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case SG_PIXELFORMAT_BC3_RGBA: + return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC3_SRGBA: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC4_R: + return GL_COMPRESSED_RED_RGTC1; + case SG_PIXELFORMAT_BC4_RSN: + return GL_COMPRESSED_SIGNED_RED_RGTC1; + case SG_PIXELFORMAT_BC5_RG: + return GL_COMPRESSED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC5_RGSN: + return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC6H_RGBF: + return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC6H_RGBUF: + return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC7_RGBA: + return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_BC7_SRGBA: + return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_ETC2_RGB8: + return GL_COMPRESSED_RGB8_ETC2; + case SG_PIXELFORMAT_ETC2_SRGB8: + return GL_COMPRESSED_SRGB8_ETC2; + case SG_PIXELFORMAT_ETC2_RGB8A1: + return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + case SG_PIXELFORMAT_ETC2_RGBA8: + return GL_COMPRESSED_RGBA8_ETC2_EAC; + case SG_PIXELFORMAT_ETC2_SRGB8A8: + return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; + case SG_PIXELFORMAT_EAC_R11: + return GL_COMPRESSED_R11_EAC; + case SG_PIXELFORMAT_EAC_R11SN: + return GL_COMPRESSED_SIGNED_R11_EAC; + case SG_PIXELFORMAT_EAC_RG11: + return GL_COMPRESSED_RG11_EAC; + case SG_PIXELFORMAT_EAC_RG11SN: + return GL_COMPRESSED_SIGNED_RG11_EAC; + case SG_PIXELFORMAT_ASTC_4x4_RGBA: + return GL_COMPRESSED_RGBA_ASTC_4x4_KHR; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: + return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR; + default: + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_teximage_internal_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: return GL_R8; + case SG_PIXELFORMAT_R8SN: return GL_R8_SNORM; + case SG_PIXELFORMAT_R8UI: return GL_R8UI; + case SG_PIXELFORMAT_R8SI: return GL_R8I; + #if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_R16: return GL_R16; + case SG_PIXELFORMAT_R16SN: return GL_R16_SNORM; + #endif + case SG_PIXELFORMAT_R16UI: return GL_R16UI; + case SG_PIXELFORMAT_R16SI: return GL_R16I; + case SG_PIXELFORMAT_R16F: return GL_R16F; + case SG_PIXELFORMAT_RG8: return GL_RG8; + case SG_PIXELFORMAT_RG8SN: return GL_RG8_SNORM; + case SG_PIXELFORMAT_RG8UI: return GL_RG8UI; + case SG_PIXELFORMAT_RG8SI: return GL_RG8I; + case SG_PIXELFORMAT_R32UI: return GL_R32UI; + case SG_PIXELFORMAT_R32SI: return GL_R32I; + case SG_PIXELFORMAT_R32F: return GL_R32F; + #if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_RG16: return GL_RG16; + case SG_PIXELFORMAT_RG16SN: return GL_RG16_SNORM; + #endif + case SG_PIXELFORMAT_RG16UI: return GL_RG16UI; + case SG_PIXELFORMAT_RG16SI: return GL_RG16I; + case SG_PIXELFORMAT_RG16F: return GL_RG16F; + case SG_PIXELFORMAT_RGBA8: return GL_RGBA8; + case SG_PIXELFORMAT_SRGB8A8: return GL_SRGB8_ALPHA8; + case SG_PIXELFORMAT_RGBA8SN: return GL_RGBA8_SNORM; + case SG_PIXELFORMAT_RGBA8UI: return GL_RGBA8UI; + case SG_PIXELFORMAT_RGBA8SI: return GL_RGBA8I; + case SG_PIXELFORMAT_RGB10A2: return GL_RGB10_A2; + case SG_PIXELFORMAT_RG11B10F: return GL_R11F_G11F_B10F; + case SG_PIXELFORMAT_RGB9E5: return GL_RGB9_E5; + case SG_PIXELFORMAT_RG32UI: return GL_RG32UI; + case SG_PIXELFORMAT_RG32SI: return GL_RG32I; + case SG_PIXELFORMAT_RG32F: return GL_RG32F; + #if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_RGBA16: return GL_RGBA16; + case SG_PIXELFORMAT_RGBA16SN: return GL_RGBA16_SNORM; + #endif + case SG_PIXELFORMAT_RGBA16UI: return GL_RGBA16UI; + case SG_PIXELFORMAT_RGBA16SI: return GL_RGBA16I; + case SG_PIXELFORMAT_RGBA16F: return GL_RGBA16F; + case SG_PIXELFORMAT_RGBA32UI: return GL_RGBA32UI; + case SG_PIXELFORMAT_RGBA32SI: return GL_RGBA32I; + case SG_PIXELFORMAT_RGBA32F: return GL_RGBA32F; + case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT32F; + case SG_PIXELFORMAT_DEPTH_STENCIL: return GL_DEPTH24_STENCIL8; + case SG_PIXELFORMAT_BC1_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case SG_PIXELFORMAT_BC2_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case SG_PIXELFORMAT_BC3_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC3_SRGBA: return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC4_R: return GL_COMPRESSED_RED_RGTC1; + case SG_PIXELFORMAT_BC4_RSN: return GL_COMPRESSED_SIGNED_RED_RGTC1; + case SG_PIXELFORMAT_BC5_RG: return GL_COMPRESSED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC5_RGSN: return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC6H_RGBF: return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC6H_RGBUF: return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC7_RGBA: return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_BC7_SRGBA: return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_ETC2_RGB8: return GL_COMPRESSED_RGB8_ETC2; + case SG_PIXELFORMAT_ETC2_SRGB8: return GL_COMPRESSED_SRGB8_ETC2; + case SG_PIXELFORMAT_ETC2_RGB8A1: return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + case SG_PIXELFORMAT_ETC2_RGBA8: return GL_COMPRESSED_RGBA8_ETC2_EAC; + case SG_PIXELFORMAT_ETC2_SRGB8A8: return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; + case SG_PIXELFORMAT_EAC_R11: return GL_COMPRESSED_R11_EAC; + case SG_PIXELFORMAT_EAC_R11SN: return GL_COMPRESSED_SIGNED_R11_EAC; + case SG_PIXELFORMAT_EAC_RG11: return GL_COMPRESSED_RG11_EAC; + case SG_PIXELFORMAT_EAC_RG11SN: return GL_COMPRESSED_SIGNED_RG11_EAC; + case SG_PIXELFORMAT_ASTC_4x4_RGBA: return GL_COMPRESSED_RGBA_ASTC_4x4_KHR; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_cubeface_target(int face_index) { + switch (face_index) { + case 0: return GL_TEXTURE_CUBE_MAP_POSITIVE_X; + case 1: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; + case 2: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; + case 3: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; + case 4: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; + case 5: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; + default: SOKOL_UNREACHABLE; return 0; + } +} + +// see: https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml +_SOKOL_PRIVATE void _sg_gl_init_pixelformats(bool has_bgra) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); + #if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); + #if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_SRGB8A8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + if (has_bgra) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); + } + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGB9E5]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + #if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); +} + +// FIXME: OES_half_float_blend +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_half_float(bool has_colorbuffer_half_float) { + if (has_colorbuffer_half_float) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } else { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_float(bool has_colorbuffer_float, bool has_texture_float_linear, bool has_float_blend) { + if (has_texture_float_linear) { + if (has_colorbuffer_float) { + if (has_float_blend) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } else { + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + } else { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + } else { + if (has_colorbuffer_float) { + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } else { + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + } +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_s3tc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_SRGBA]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_rgtc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_bptc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_SRGBA]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_pvrtc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_2BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_4BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_2BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_4BPP]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_etc2(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_astc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_SRGBA]); + } + +_SOKOL_PRIVATE void _sg_gl_init_limits(void) { + _SG_GL_CHECK_ERROR(); + GLint gl_int; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_size_2d = gl_int; + _sg.limits.max_image_size_array = gl_int; + glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_size_cube = gl_int; + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &gl_int); + _SG_GL_CHECK_ERROR(); + if (gl_int > SG_MAX_VERTEX_ATTRIBUTES) { + gl_int = SG_MAX_VERTEX_ATTRIBUTES; + } + _sg.limits.max_vertex_attrs = gl_int; + glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.gl_max_vertex_uniform_components = gl_int; + glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_size_3d = gl_int; + glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_array_layers = gl_int; + if (_sg.gl.ext_anisotropic) { + glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.gl.max_anisotropy = gl_int; + } else { + _sg.gl.max_anisotropy = 1; + } + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.gl_max_combined_texture_image_units = gl_int; +} + +#if defined(SOKOL_GLCORE) +_SOKOL_PRIVATE void _sg_gl_init_caps_glcore(void) { + _sg.backend = SG_BACKEND_GLCORE; + + GLint major_version = 0; + GLint minor_version = 0; + glGetIntegerv(GL_MAJOR_VERSION, &major_version); + glGetIntegerv(GL_MINOR_VERSION, &minor_version); + const int version = major_version * 100 + minor_version * 10; + _sg.features.origin_top_left = false; + _sg.features.image_clamp_to_border = true; + _sg.features.mrt_independent_blend_state = false; + _sg.features.mrt_independent_write_mask = true; + _sg.features.storage_buffer = version >= 430; + + // scan extensions + bool has_s3tc = false; // BC1..BC3 + bool has_rgtc = false; // BC4 and BC5 + bool has_bptc = false; // BC6H and BC7 + bool has_pvrtc = false; + bool has_etc2 = false; + bool has_astc = false; + GLint num_ext = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_ext); + for (int i = 0; i < num_ext; i++) { + const char* ext = (const char*) glGetStringi(GL_EXTENSIONS, (GLuint)i); + if (ext) { + if (strstr(ext, "_texture_compression_s3tc")) { + has_s3tc = true; + } else if (strstr(ext, "_texture_compression_rgtc")) { + has_rgtc = true; + } else if (strstr(ext, "_texture_compression_bptc")) { + has_bptc = true; + } else if (strstr(ext, "_texture_compression_pvrtc")) { + has_pvrtc = true; + } else if (strstr(ext, "_ES3_compatibility")) { + has_etc2 = true; + } else if (strstr(ext, "_texture_filter_anisotropic")) { + _sg.gl.ext_anisotropic = true; + } else if (strstr(ext, "_texture_compression_astc_ldr")) { + has_astc = true; + } + } + } + + // limits + _sg_gl_init_limits(); + + // pixel formats + const bool has_bgra = false; // not a bug + const bool has_colorbuffer_float = true; + const bool has_colorbuffer_half_float = true; + const bool has_texture_float_linear = true; // FIXME??? + const bool has_float_blend = true; + _sg_gl_init_pixelformats(has_bgra); + _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); + _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float); + if (has_s3tc) { + _sg_gl_init_pixelformats_s3tc(); + } + if (has_rgtc) { + _sg_gl_init_pixelformats_rgtc(); + } + if (has_bptc) { + _sg_gl_init_pixelformats_bptc(); + } + if (has_pvrtc) { + _sg_gl_init_pixelformats_pvrtc(); + } + if (has_etc2) { + _sg_gl_init_pixelformats_etc2(); + } + if (has_astc) { + _sg_gl_init_pixelformats_astc(); + } +} +#endif + +#if defined(SOKOL_GLES3) +_SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { + _sg.backend = SG_BACKEND_GLES3; + + _sg.features.origin_top_left = false; + _sg.features.image_clamp_to_border = false; + _sg.features.mrt_independent_blend_state = false; + _sg.features.mrt_independent_write_mask = false; + _sg.features.storage_buffer = false; + + bool has_s3tc = false; // BC1..BC3 + bool has_rgtc = false; // BC4 and BC5 + bool has_bptc = false; // BC6H and BC7 + bool has_pvrtc = false; + #if defined(__EMSCRIPTEN__) + bool has_etc2 = false; + #else + bool has_etc2 = true; + #endif + bool has_astc = false; + bool has_colorbuffer_float = false; + bool has_colorbuffer_half_float = false; + bool has_texture_float_linear = false; + bool has_float_blend = false; + GLint num_ext = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_ext); + for (int i = 0; i < num_ext; i++) { + const char* ext = (const char*) glGetStringi(GL_EXTENSIONS, (GLuint)i); + if (ext) { + if (strstr(ext, "_texture_compression_s3tc")) { + has_s3tc = true; + } else if (strstr(ext, "_compressed_texture_s3tc")) { + has_s3tc = true; + } else if (strstr(ext, "_texture_compression_rgtc")) { + has_rgtc = true; + } else if (strstr(ext, "_texture_compression_bptc")) { + has_bptc = true; + } else if (strstr(ext, "_texture_compression_pvrtc")) { + has_pvrtc = true; + } else if (strstr(ext, "_compressed_texture_pvrtc")) { + has_pvrtc = true; + } else if (strstr(ext, "_compressed_texture_etc")) { + has_etc2 = true; + } else if (strstr(ext, "_compressed_texture_astc")) { + has_astc = true; + } else if (strstr(ext, "_color_buffer_float")) { + has_colorbuffer_float = true; + } else if (strstr(ext, "_color_buffer_half_float")) { + has_colorbuffer_half_float = true; + } else if (strstr(ext, "_texture_float_linear")) { + has_texture_float_linear = true; + } else if (strstr(ext, "_float_blend")) { + has_float_blend = true; + } else if (strstr(ext, "_texture_filter_anisotropic")) { + _sg.gl.ext_anisotropic = true; + } + } + } + + /* on WebGL2, color_buffer_float also includes 16-bit formats + see: https://developer.mozilla.org/en-US/docs/Web/API/EXT_color_buffer_float + */ + #if defined(__EMSCRIPTEN__) + if (!has_colorbuffer_half_float && has_colorbuffer_float) { + has_colorbuffer_half_float = has_colorbuffer_float; + } + #endif + + // limits + _sg_gl_init_limits(); + + // pixel formats + const bool has_bgra = false; // not a bug + _sg_gl_init_pixelformats(has_bgra); + _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); + _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float); + if (has_s3tc) { + _sg_gl_init_pixelformats_s3tc(); + } + if (has_rgtc) { + _sg_gl_init_pixelformats_rgtc(); + } + if (has_bptc) { + _sg_gl_init_pixelformats_bptc(); + } + if (has_pvrtc) { + _sg_gl_init_pixelformats_pvrtc(); + } + if (has_etc2) { + _sg_gl_init_pixelformats_etc2(); + } + if (has_astc) { + _sg_gl_init_pixelformats_astc(); + } +} +#endif + +//-- state cache implementation ------------------------------------------------ +_SOKOL_PRIVATE GLuint _sg_gl_storagebuffer_bind_index(int stage, int slot) { + SOKOL_ASSERT((stage >= 0) && (stage < SG_NUM_SHADER_STAGES)); + SOKOL_ASSERT((slot >= 0) && (slot < SG_MAX_SHADERSTAGE_STORAGEBUFFERS)); + return (GLuint) (stage * _SG_GL_STORAGEBUFFER_STAGE_INDEX_PITCH + slot); +} + +_SOKOL_PRIVATE void _sg_gl_cache_clear_buffer_bindings(bool force) { + if (force || (_sg.gl.cache.vertex_buffer != 0)) { + glBindBuffer(GL_ARRAY_BUFFER, 0); + _sg.gl.cache.vertex_buffer = 0; + _sg_stats_add(gl.num_bind_buffer, 1); + } + if (force || (_sg.gl.cache.index_buffer != 0)) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + _sg.gl.cache.index_buffer = 0; + _sg_stats_add(gl.num_bind_buffer, 1); + } + if (force || (_sg.gl.cache.storage_buffer != 0)) { + if (_sg.features.storage_buffer) { + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + } + _sg.gl.cache.storage_buffer = 0; + _sg_stats_add(gl.num_bind_buffer, 1); + } + for (int stage = 0; stage < SG_NUM_SHADER_STAGES; stage++) { + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { + if (force || (_sg.gl.cache.stage_storage_buffers[stage][i] != 0)) { + const GLuint bind_index = _sg_gl_storagebuffer_bind_index(stage, i); + if (_sg.features.storage_buffer) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, bind_index, 0); + } + _sg.gl.cache.stage_storage_buffers[stage][i] = 0; + _sg_stats_add(gl.num_bind_buffer, 1); + } + } + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_bind_buffer(GLenum target, GLuint buffer) { + SOKOL_ASSERT((GL_ARRAY_BUFFER == target) || (GL_ELEMENT_ARRAY_BUFFER == target) || (GL_SHADER_STORAGE_BUFFER == target)); + if (target == GL_ARRAY_BUFFER) { + if (_sg.gl.cache.vertex_buffer != buffer) { + _sg.gl.cache.vertex_buffer = buffer; + glBindBuffer(target, buffer); + _sg_stats_add(gl.num_bind_buffer, 1); + } + } else if (target == GL_ELEMENT_ARRAY_BUFFER) { + if (_sg.gl.cache.index_buffer != buffer) { + _sg.gl.cache.index_buffer = buffer; + glBindBuffer(target, buffer); + _sg_stats_add(gl.num_bind_buffer, 1); + } + } else if (target == GL_SHADER_STORAGE_BUFFER) { + if (_sg.gl.cache.storage_buffer != buffer) { + _sg.gl.cache.storage_buffer = buffer; + if (_sg.features.storage_buffer) { + glBindBuffer(target, buffer); + } + _sg_stats_add(gl.num_bind_buffer, 1); + } + } else { + SOKOL_UNREACHABLE; + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_bind_storage_buffer(int stage, int slot, GLuint buffer) { + SOKOL_ASSERT((stage >= 0) && (stage < SG_NUM_SHADER_STAGES)); + SOKOL_ASSERT((slot >= 0) && (slot < SG_MAX_SHADERSTAGE_STORAGEBUFFERS)); + if (_sg.gl.cache.stage_storage_buffers[stage][slot] != buffer) { + _sg.gl.cache.stage_storage_buffers[stage][slot] = buffer; + _sg.gl.cache.storage_buffer = buffer; // not a bug + GLuint bind_index = _sg_gl_storagebuffer_bind_index(stage, slot); + if (_sg.features.storage_buffer) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, bind_index, buffer); + } + _sg_stats_add(gl.num_bind_buffer, 1); + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_store_buffer_binding(GLenum target) { + if (target == GL_ARRAY_BUFFER) { + _sg.gl.cache.stored_vertex_buffer = _sg.gl.cache.vertex_buffer; + } else if (target == GL_ELEMENT_ARRAY_BUFFER) { + _sg.gl.cache.stored_index_buffer = _sg.gl.cache.index_buffer; + } else if (target == GL_SHADER_STORAGE_BUFFER) { + _sg.gl.cache.stored_storage_buffer = _sg.gl.cache.storage_buffer; + } else { + SOKOL_UNREACHABLE; + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_restore_buffer_binding(GLenum target) { + if (target == GL_ARRAY_BUFFER) { + if (_sg.gl.cache.stored_vertex_buffer != 0) { + // we only care about restoring valid ids + _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_vertex_buffer); + _sg.gl.cache.stored_vertex_buffer = 0; + } + } else if (target == GL_ELEMENT_ARRAY_BUFFER) { + if (_sg.gl.cache.stored_index_buffer != 0) { + // we only care about restoring valid ids + _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_index_buffer); + _sg.gl.cache.stored_index_buffer = 0; + } + } else if (target == GL_SHADER_STORAGE_BUFFER) { + if (_sg.gl.cache.stored_storage_buffer != 0) { + // we only care about restoring valid ids + _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_storage_buffer); + _sg.gl.cache.stored_storage_buffer = 0; + } + } else { + SOKOL_UNREACHABLE; + } +} + +// called when _sg_gl_discard_buffer() +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_buffer(GLuint buf) { + if (buf == _sg.gl.cache.vertex_buffer) { + _sg.gl.cache.vertex_buffer = 0; + glBindBuffer(GL_ARRAY_BUFFER, 0); + _sg_stats_add(gl.num_bind_buffer, 1); + } + if (buf == _sg.gl.cache.index_buffer) { + _sg.gl.cache.index_buffer = 0; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + _sg_stats_add(gl.num_bind_buffer, 1); + } + if (buf == _sg.gl.cache.storage_buffer) { + _sg.gl.cache.storage_buffer = 0; + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + _sg_stats_add(gl.num_bind_buffer, 1); + } + for (int stage = 0; stage < SG_NUM_SHADER_STAGES; stage++) { + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { + if (buf == _sg.gl.cache.stage_storage_buffers[stage][i]) { + _sg.gl.cache.stage_storage_buffers[stage][i] = 0; + _sg.gl.cache.storage_buffer = 0; // not a bug! + const GLuint bind_index = _sg_gl_storagebuffer_bind_index(stage, i); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, bind_index, 0); + _sg_stats_add(gl.num_bind_buffer, 1); + } + } + } + if (buf == _sg.gl.cache.stored_vertex_buffer) { + _sg.gl.cache.stored_vertex_buffer = 0; + } + if (buf == _sg.gl.cache.stored_index_buffer) { + _sg.gl.cache.stored_index_buffer = 0; + } + if (buf == _sg.gl.cache.stored_storage_buffer) { + _sg.gl.cache.stored_storage_buffer = 0; + } + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + if (buf == _sg.gl.cache.attrs[i].gl_vbuf) { + _sg.gl.cache.attrs[i].gl_vbuf = 0; + } + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_active_texture(GLenum texture) { + _SG_GL_CHECK_ERROR(); + if (_sg.gl.cache.cur_active_texture != texture) { + _sg.gl.cache.cur_active_texture = texture; + glActiveTexture(texture); + _sg_stats_add(gl.num_active_texture, 1); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_cache_clear_texture_sampler_bindings(bool force) { + _SG_GL_CHECK_ERROR(); + for (int i = 0; (i < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE) && (i < _sg.limits.gl_max_combined_texture_image_units); i++) { + if (force || (_sg.gl.cache.texture_samplers[i].texture != 0)) { + GLenum gl_texture_unit = (GLenum) (GL_TEXTURE0 + i); + glActiveTexture(gl_texture_unit); + _sg_stats_add(gl.num_active_texture, 1); + glBindTexture(GL_TEXTURE_2D, 0); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + glBindTexture(GL_TEXTURE_3D, 0); + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); + _sg_stats_add(gl.num_bind_texture, 4); + glBindSampler((GLuint)i, 0); + _sg_stats_add(gl.num_bind_sampler, 1); + _sg.gl.cache.texture_samplers[i].target = 0; + _sg.gl.cache.texture_samplers[i].texture = 0; + _sg.gl.cache.texture_samplers[i].sampler = 0; + _sg.gl.cache.cur_active_texture = gl_texture_unit; + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_cache_bind_texture_sampler(int slot_index, GLenum target, GLuint texture, GLuint sampler) { + /* it's valid to call this function with target=0 and/or texture=0 + target=0 will unbind the previous binding, texture=0 will clear + the new binding + */ + SOKOL_ASSERT((slot_index >= 0) && (slot_index < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE)); + if (slot_index >= _sg.limits.gl_max_combined_texture_image_units) { + return; + } + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_texture_sampler_bind_slot* slot = &_sg.gl.cache.texture_samplers[slot_index]; + if ((slot->target != target) || (slot->texture != texture) || (slot->sampler != sampler)) { + _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + slot_index)); + // if the target has changed, clear the previous binding on that target + if ((target != slot->target) && (slot->target != 0)) { + glBindTexture(slot->target, 0); + _SG_GL_CHECK_ERROR(); + _sg_stats_add(gl.num_bind_texture, 1); + } + // apply new binding (can be 0 to unbind) + if (target != 0) { + glBindTexture(target, texture); + _SG_GL_CHECK_ERROR(); + _sg_stats_add(gl.num_bind_texture, 1); + } + // apply new sampler (can be 0 to unbind) + glBindSampler((GLuint)slot_index, sampler); + _SG_GL_CHECK_ERROR(); + _sg_stats_add(gl.num_bind_sampler, 1); + + slot->target = target; + slot->texture = texture; + slot->sampler = sampler; + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_store_texture_sampler_binding(int slot_index) { + SOKOL_ASSERT((slot_index >= 0) && (slot_index < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE)); + _sg.gl.cache.stored_texture_sampler = _sg.gl.cache.texture_samplers[slot_index]; +} + +_SOKOL_PRIVATE void _sg_gl_cache_restore_texture_sampler_binding(int slot_index) { + SOKOL_ASSERT((slot_index >= 0) && (slot_index < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE)); + _sg_gl_cache_texture_sampler_bind_slot* slot = &_sg.gl.cache.stored_texture_sampler; + if (slot->texture != 0) { + // we only care about restoring valid ids + SOKOL_ASSERT(slot->target != 0); + _sg_gl_cache_bind_texture_sampler(slot_index, slot->target, slot->texture, slot->sampler); + slot->target = 0; + slot->texture = 0; + slot->sampler = 0; + } +} + +// called from _sg_gl_discard_texture() and _sg_gl_discard_sampler() +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_texture_sampler(GLuint tex, GLuint smp) { + _SG_GL_CHECK_ERROR(); + for (int i = 0; i < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE; i++) { + _sg_gl_cache_texture_sampler_bind_slot* slot = &_sg.gl.cache.texture_samplers[i]; + if ((0 != slot->target) && ((tex == slot->texture) || (smp == slot->sampler))) { + _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + i)); + glBindTexture(slot->target, 0); + _SG_GL_CHECK_ERROR(); + _sg_stats_add(gl.num_bind_texture, 1); + glBindSampler((GLuint)i, 0); + _SG_GL_CHECK_ERROR(); + _sg_stats_add(gl.num_bind_sampler, 1); + slot->target = 0; + slot->texture = 0; + slot->sampler = 0; + } + } + if ((tex == _sg.gl.cache.stored_texture_sampler.texture) || (smp == _sg.gl.cache.stored_texture_sampler.sampler)) { + _sg.gl.cache.stored_texture_sampler.target = 0; + _sg.gl.cache.stored_texture_sampler.texture = 0; + _sg.gl.cache.stored_texture_sampler.sampler = 0; + } +} + +// called from _sg_gl_discard_shader() +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_program(GLuint prog) { + if (prog == _sg.gl.cache.prog) { + _sg.gl.cache.prog = 0; + glUseProgram(0); + _sg_stats_add(gl.num_use_program, 1); + } +} + +// called from _sg_gl_discard_pipeline() +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_pipeline(_sg_pipeline_t* pip) { + if (pip == _sg.gl.cache.cur_pipeline) { + _sg.gl.cache.cur_pipeline = 0; + _sg.gl.cache.cur_pipeline_id.id = SG_INVALID_ID; + } +} + +_SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { + _SG_GL_CHECK_ERROR(); + glBindVertexArray(_sg.gl.vao); + _SG_GL_CHECK_ERROR(); + _sg_clear(&_sg.gl.cache, sizeof(_sg.gl.cache)); + _sg_gl_cache_clear_buffer_bindings(true); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_clear_texture_sampler_bindings(true); + _SG_GL_CHECK_ERROR(); + for (int i = 0; i < _sg.limits.max_vertex_attrs; i++) { + _sg_gl_attr_t* attr = &_sg.gl.cache.attrs[i].gl_attr; + attr->vb_index = -1; + attr->divisor = -1; + glDisableVertexAttribArray((GLuint)i); + _SG_GL_CHECK_ERROR(); + _sg_stats_add(gl.num_disable_vertex_attrib_array, 1); + } + _sg.gl.cache.cur_primitive_type = GL_TRIANGLES; + + // shader program + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&_sg.gl.cache.prog); + _SG_GL_CHECK_ERROR(); + + // depth and stencil state + _sg.gl.cache.depth.compare = SG_COMPAREFUNC_ALWAYS; + _sg.gl.cache.stencil.front.compare = SG_COMPAREFUNC_ALWAYS; + _sg.gl.cache.stencil.front.fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.front.depth_fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.front.pass_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.back.compare = SG_COMPAREFUNC_ALWAYS; + _sg.gl.cache.stencil.back.fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.back.depth_fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.back.pass_op = SG_STENCILOP_KEEP; + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_ALWAYS); + glDepthMask(GL_FALSE); + glDisable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 0, 0); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilMask(0); + _sg_stats_add(gl.num_render_state, 7); + + // blend state + _sg.gl.cache.blend.src_factor_rgb = SG_BLENDFACTOR_ONE; + _sg.gl.cache.blend.dst_factor_rgb = SG_BLENDFACTOR_ZERO; + _sg.gl.cache.blend.op_rgb = SG_BLENDOP_ADD; + _sg.gl.cache.blend.src_factor_alpha = SG_BLENDFACTOR_ONE; + _sg.gl.cache.blend.dst_factor_alpha = SG_BLENDFACTOR_ZERO; + _sg.gl.cache.blend.op_alpha = SG_BLENDOP_ADD; + glDisable(GL_BLEND); + glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO); + glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); + glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); + _sg_stats_add(gl.num_render_state, 4); + + // standalone state + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.gl.cache.color_write_mask[i] = SG_COLORMASK_RGBA; + } + _sg.gl.cache.cull_mode = SG_CULLMODE_NONE; + _sg.gl.cache.face_winding = SG_FACEWINDING_CW; + _sg.gl.cache.sample_count = 1; + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glPolygonOffset(0.0f, 0.0f); + glDisable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_CULL_FACE); + glFrontFace(GL_CW); + glCullFace(GL_BACK); + glEnable(GL_SCISSOR_TEST); + glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + glEnable(GL_DITHER); + glDisable(GL_POLYGON_OFFSET_FILL); + _sg_stats_add(gl.num_render_state, 10); + #if defined(SOKOL_GLCORE) + glEnable(GL_MULTISAMPLE); + glEnable(GL_PROGRAM_POINT_SIZE); + _sg_stats_add(gl.num_render_state, 2); + #endif +} + +_SOKOL_PRIVATE void _sg_gl_setup_backend(const sg_desc* desc) { + _SOKOL_UNUSED(desc); + + // assumes that _sg.gl is already zero-initialized + _sg.gl.valid = true; + + #if defined(_SOKOL_USE_WIN32_GL_LOADER) + _sg_gl_load_opengl(); + #endif + + // clear initial GL error state + #if defined(SOKOL_DEBUG) + while (glGetError() != GL_NO_ERROR); + #endif + #if defined(SOKOL_GLCORE) + _sg_gl_init_caps_glcore(); + #elif defined(SOKOL_GLES3) + _sg_gl_init_caps_gles3(); + #endif + + glGenVertexArrays(1, &_sg.gl.vao); + glBindVertexArray(_sg.gl.vao); + _SG_GL_CHECK_ERROR(); + // incoming texture data is generally expected to be packed tightly + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + #if defined(SOKOL_GLCORE) + // enable seamless cubemap sampling (only desktop GL) + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + #endif + _sg_gl_reset_state_cache(); +} + +_SOKOL_PRIVATE void _sg_gl_discard_backend(void) { + SOKOL_ASSERT(_sg.gl.valid); + if (_sg.gl.vao) { + glDeleteVertexArrays(1, &_sg.gl.vao); + } + #if defined(_SOKOL_USE_WIN32_GL_LOADER) + _sg_gl_unload_opengl(); + #endif + _sg.gl.valid = false; +} + +//-- GL backend resource creation and destruction ------------------------------ +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + _SG_GL_CHECK_ERROR(); + buf->gl.injected = (0 != desc->gl_buffers[0]); + const GLenum gl_target = _sg_gl_buffer_target(buf->cmn.type); + const GLenum gl_usage = _sg_gl_usage(buf->cmn.usage); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + GLuint gl_buf = 0; + if (buf->gl.injected) { + SOKOL_ASSERT(desc->gl_buffers[slot]); + gl_buf = desc->gl_buffers[slot]; + } else { + glGenBuffers(1, &gl_buf); + SOKOL_ASSERT(gl_buf); + _sg_gl_cache_store_buffer_binding(gl_target); + _sg_gl_cache_bind_buffer(gl_target, gl_buf); + glBufferData(gl_target, buf->cmn.size, 0, gl_usage); + if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { + SOKOL_ASSERT(desc->data.ptr); + glBufferSubData(gl_target, 0, buf->cmn.size, desc->data.ptr); + } + _sg_gl_cache_restore_buffer_binding(gl_target); + } + buf->gl.buf[slot] = gl_buf; + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_discard_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + _SG_GL_CHECK_ERROR(); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + if (buf->gl.buf[slot]) { + _sg_gl_cache_invalidate_buffer(buf->gl.buf[slot]); + if (!buf->gl.injected) { + glDeleteBuffers(1, &buf->gl.buf[slot]); + } + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE bool _sg_gl_supported_texture_format(sg_pixel_format fmt) { + const int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index > SG_PIXELFORMAT_NONE) && (fmt_index < _SG_PIXELFORMAT_NUM)); + return _sg.formats[fmt_index].sample; +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + _SG_GL_CHECK_ERROR(); + img->gl.injected = (0 != desc->gl_textures[0]); + + // check if texture format is support + if (!_sg_gl_supported_texture_format(img->cmn.pixel_format)) { + _SG_ERROR(GL_TEXTURE_FORMAT_NOT_SUPPORTED); + return SG_RESOURCESTATE_FAILED; + } + const GLenum gl_internal_format = _sg_gl_teximage_internal_format(img->cmn.pixel_format); + + // if this is a MSAA render target, a render buffer object will be created instead of a regulat texture + // (since GLES3 has no multisampled texture objects) + if (img->cmn.render_target && (img->cmn.sample_count > 1)) { + glGenRenderbuffers(1, &img->gl.msaa_render_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, img->gl.msaa_render_buffer); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->cmn.sample_count, gl_internal_format, img->cmn.width, img->cmn.height); + } else if (img->gl.injected) { + img->gl.target = _sg_gl_texture_target(img->cmn.type); + // inject externally GL textures + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + SOKOL_ASSERT(desc->gl_textures[slot]); + img->gl.tex[slot] = desc->gl_textures[slot]; + } + if (desc->gl_texture_target) { + img->gl.target = (GLenum)desc->gl_texture_target; + } + } else { + // create our own GL texture(s) + img->gl.target = _sg_gl_texture_target(img->cmn.type); + const GLenum gl_format = _sg_gl_teximage_format(img->cmn.pixel_format); + const bool is_compressed = _sg_is_compressed_pixel_format(img->cmn.pixel_format); + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + glGenTextures(1, &img->gl.tex[slot]); + SOKOL_ASSERT(img->gl.tex[slot]); + _sg_gl_cache_store_texture_sampler_binding(0); + _sg_gl_cache_bind_texture_sampler(0, img->gl.target, img->gl.tex[slot], 0); + glTexParameteri(img->gl.target, GL_TEXTURE_MAX_LEVEL, img->cmn.num_mipmaps - 1); + const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1; + int data_index = 0; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, data_index++) { + GLenum gl_img_target = img->gl.target; + if (SG_IMAGETYPE_CUBE == img->cmn.type) { + gl_img_target = _sg_gl_cubeface_target(face_index); + } + const GLvoid* data_ptr = desc->data.subimage[face_index][mip_index].ptr; + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { + if (is_compressed) { + const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; + glCompressedTexImage2D(gl_img_target, mip_index, gl_internal_format, + mip_width, mip_height, 0, data_size, data_ptr); + } else { + const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); + glTexImage2D(gl_img_target, mip_index, (GLint)gl_internal_format, + mip_width, mip_height, 0, gl_format, gl_type, data_ptr); + } + } else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) { + int mip_depth = img->cmn.num_slices; + if (SG_IMAGETYPE_3D == img->cmn.type) { + mip_depth = _sg_miplevel_dim(mip_depth, mip_index); + } + if (is_compressed) { + const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; + glCompressedTexImage3D(gl_img_target, mip_index, gl_internal_format, + mip_width, mip_height, mip_depth, 0, data_size, data_ptr); + } else { + const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); + glTexImage3D(gl_img_target, mip_index, (GLint)gl_internal_format, + mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr); + } + } + } + } + _sg_gl_cache_restore_texture_sampler_binding(0); + } + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_discard_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + _SG_GL_CHECK_ERROR(); + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + if (img->gl.tex[slot]) { + _sg_gl_cache_invalidate_texture_sampler(img->gl.tex[slot], 0); + if (!img->gl.injected) { + glDeleteTextures(1, &img->gl.tex[slot]); + } + } + } + if (img->gl.msaa_render_buffer) { + glDeleteRenderbuffers(1, &img->gl.msaa_render_buffer); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + _SG_GL_CHECK_ERROR(); + smp->gl.injected = (0 != desc->gl_sampler); + if (smp->gl.injected) { + smp->gl.smp = (GLuint) desc->gl_sampler; + } else { + glGenSamplers(1, &smp->gl.smp); + SOKOL_ASSERT(smp->gl.smp); + + const GLenum gl_min_filter = _sg_gl_min_filter(smp->cmn.min_filter, smp->cmn.mipmap_filter); + const GLenum gl_mag_filter = _sg_gl_mag_filter(smp->cmn.mag_filter); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_MIN_FILTER, (GLint)gl_min_filter); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_MAG_FILTER, (GLint)gl_mag_filter); + // GL spec has strange defaults for mipmap min/max lod: -1000 to +1000 + const float min_lod = _sg_clamp(desc->min_lod, 0.0f, 1000.0f); + const float max_lod = _sg_clamp(desc->max_lod, 0.0f, 1000.0f); + glSamplerParameterf(smp->gl.smp, GL_TEXTURE_MIN_LOD, min_lod); + glSamplerParameterf(smp->gl.smp, GL_TEXTURE_MAX_LOD, max_lod); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_S, (GLint)_sg_gl_wrap(smp->cmn.wrap_u)); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_T, (GLint)_sg_gl_wrap(smp->cmn.wrap_v)); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_R, (GLint)_sg_gl_wrap(smp->cmn.wrap_w)); + #if defined(SOKOL_GLCORE) + float border[4]; + switch (smp->cmn.border_color) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: + border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 0.0f; + break; + case SG_BORDERCOLOR_OPAQUE_WHITE: + border[0] = 1.0f; border[1] = 1.0f; border[2] = 1.0f; border[3] = 1.0f; + break; + default: + border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 1.0f; + break; + } + glSamplerParameterfv(smp->gl.smp, GL_TEXTURE_BORDER_COLOR, border); + #endif + if (smp->cmn.compare != SG_COMPAREFUNC_NEVER) { + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_COMPARE_FUNC, (GLint)_sg_gl_compare_func(smp->cmn.compare)); + } else { + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_COMPARE_MODE, GL_NONE); + } + if (_sg.gl.ext_anisotropic && (smp->cmn.max_anisotropy > 1)) { + GLint max_aniso = (GLint) smp->cmn.max_anisotropy; + if (max_aniso > _sg.gl.max_anisotropy) { + max_aniso = _sg.gl.max_anisotropy; + } + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso); + } + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_invalidate_texture_sampler(0, smp->gl.smp); + if (!smp->gl.injected) { + glDeleteSamplers(1, &smp->gl.smp); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE GLuint _sg_gl_compile_shader(sg_shader_stage stage, const char* src) { + SOKOL_ASSERT(src); + _SG_GL_CHECK_ERROR(); + GLuint gl_shd = glCreateShader(_sg_gl_shader_stage(stage)); + glShaderSource(gl_shd, 1, &src, 0); + glCompileShader(gl_shd); + GLint compile_status = 0; + glGetShaderiv(gl_shd, GL_COMPILE_STATUS, &compile_status); + if (!compile_status) { + // compilation failed, log error and delete shader + GLint log_len = 0; + glGetShaderiv(gl_shd, GL_INFO_LOG_LENGTH, &log_len); + if (log_len > 0) { + GLchar* log_buf = (GLchar*) _sg_malloc((size_t)log_len); + glGetShaderInfoLog(gl_shd, log_len, &log_len, log_buf); + _SG_ERROR(GL_SHADER_COMPILATION_FAILED); + _SG_LOGMSG(GL_SHADER_COMPILATION_FAILED, log_buf); + _sg_free(log_buf); + } + glDeleteShader(gl_shd); + gl_shd = 0; + } + _SG_GL_CHECK_ERROR(); + return gl_shd; +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(!shd->gl.prog); + _SG_GL_CHECK_ERROR(); + + // copy the optional vertex attribute names over + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + _sg_strcpy(&shd->gl.attrs[i].name, desc->attrs[i].name); + } + + GLuint gl_vs = _sg_gl_compile_shader(SG_SHADERSTAGE_VS, desc->vs.source); + GLuint gl_fs = _sg_gl_compile_shader(SG_SHADERSTAGE_FS, desc->fs.source); + if (!(gl_vs && gl_fs)) { + return SG_RESOURCESTATE_FAILED; + } + GLuint gl_prog = glCreateProgram(); + glAttachShader(gl_prog, gl_vs); + glAttachShader(gl_prog, gl_fs); + glLinkProgram(gl_prog); + glDeleteShader(gl_vs); + glDeleteShader(gl_fs); + _SG_GL_CHECK_ERROR(); + + GLint link_status; + glGetProgramiv(gl_prog, GL_LINK_STATUS, &link_status); + if (!link_status) { + GLint log_len = 0; + glGetProgramiv(gl_prog, GL_INFO_LOG_LENGTH, &log_len); + if (log_len > 0) { + GLchar* log_buf = (GLchar*) _sg_malloc((size_t)log_len); + glGetProgramInfoLog(gl_prog, log_len, &log_len, log_buf); + _SG_ERROR(GL_SHADER_LINKING_FAILED); + _SG_LOGMSG(GL_SHADER_LINKING_FAILED, log_buf); + _sg_free(log_buf); + } + glDeleteProgram(gl_prog); + return SG_RESOURCESTATE_FAILED; + } + shd->gl.prog = gl_prog; + + // resolve uniforms + _SG_GL_CHECK_ERROR(); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &desc->vs : &desc->fs; + const _sg_shader_stage_t* stage = &shd->cmn.stage[stage_index]; + _sg_gl_shader_stage_t* gl_stage = &shd->gl.stage[stage_index]; + for (int ub_index = 0; ub_index < stage->num_uniform_blocks; ub_index++) { + const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + SOKOL_ASSERT(ub_desc->size > 0); + _sg_gl_uniform_block_t* ub = &gl_stage->uniform_blocks[ub_index]; + SOKOL_ASSERT(ub->num_uniforms == 0); + uint32_t cur_uniform_offset = 0; + for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { + const sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; + if (u_desc->type == SG_UNIFORMTYPE_INVALID) { + break; + } + const uint32_t u_align = _sg_uniform_alignment(u_desc->type, u_desc->array_count, ub_desc->layout); + const uint32_t u_size = _sg_uniform_size(u_desc->type, u_desc->array_count, ub_desc->layout); + cur_uniform_offset = _sg_align_u32(cur_uniform_offset, u_align); + _sg_gl_uniform_t* u = &ub->uniforms[u_index]; + u->type = u_desc->type; + u->count = (uint16_t) u_desc->array_count; + u->offset = (uint16_t) cur_uniform_offset; + cur_uniform_offset += u_size; + if (u_desc->name) { + u->gl_loc = glGetUniformLocation(gl_prog, u_desc->name); + } else { + u->gl_loc = u_index; + } + ub->num_uniforms++; + } + if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { + cur_uniform_offset = _sg_align_u32(cur_uniform_offset, 16); + } + SOKOL_ASSERT(ub_desc->size == (size_t)cur_uniform_offset); + _SOKOL_UNUSED(cur_uniform_offset); + } + } + + // resolve combined image samplers + _SG_GL_CHECK_ERROR(); + GLuint cur_prog = 0; + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&cur_prog); + glUseProgram(gl_prog); + int gl_tex_slot = 0; + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &desc->vs : &desc->fs; + const _sg_shader_stage_t* stage = &shd->cmn.stage[stage_index]; + _sg_gl_shader_stage_t* gl_stage = &shd->gl.stage[stage_index]; + for (int img_smp_index = 0; img_smp_index < stage->num_image_samplers; img_smp_index++) { + const sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_index]; + _sg_gl_shader_image_sampler_t* gl_img_smp = &gl_stage->image_samplers[img_smp_index]; + SOKOL_ASSERT(img_smp_desc->glsl_name); + GLint gl_loc = glGetUniformLocation(gl_prog, img_smp_desc->glsl_name); + if (gl_loc != -1) { + gl_img_smp->gl_tex_slot = gl_tex_slot++; + glUniform1i(gl_loc, gl_img_smp->gl_tex_slot); + } else { + gl_img_smp->gl_tex_slot = -1; + _SG_ERROR(GL_TEXTURE_NAME_NOT_FOUND_IN_SHADER); + _SG_LOGMSG(GL_TEXTURE_NAME_NOT_FOUND_IN_SHADER, img_smp_desc->glsl_name); + } + } + } + // it's legal to call glUseProgram with 0 + glUseProgram(cur_prog); + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_discard_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _SG_GL_CHECK_ERROR(); + if (shd->gl.prog) { + _sg_gl_cache_invalidate_program(shd->gl.prog); + glDeleteProgram(shd->gl.prog); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT((pip->shader == 0) && (pip->cmn.shader_id.id != SG_INVALID_ID)); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + SOKOL_ASSERT(shd->gl.prog); + pip->shader = shd; + pip->gl.primitive_type = desc->primitive_type; + pip->gl.depth = desc->depth; + pip->gl.stencil = desc->stencil; + // FIXME: blend color and write mask per draw-buffer-attachment (requires GL4) + pip->gl.blend = desc->colors[0].blend; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + pip->gl.color_write_mask[i] = desc->colors[i].write_mask; + } + pip->gl.cull_mode = desc->cull_mode; + pip->gl.face_winding = desc->face_winding; + pip->gl.sample_count = desc->sample_count; + pip->gl.alpha_to_coverage_enabled = desc->alpha_to_coverage_enabled; + + // resolve vertex attributes + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + pip->gl.attrs[attr_index].vb_index = -1; + } + for (int attr_index = 0; attr_index < _sg.limits.max_vertex_attrs; attr_index++) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[a_state->buffer_index]; + const sg_vertex_step step_func = l_state->step_func; + const int step_rate = l_state->step_rate; + GLint attr_loc = attr_index; + if (!_sg_strempty(&shd->gl.attrs[attr_index].name)) { + attr_loc = glGetAttribLocation(pip->shader->gl.prog, _sg_strptr(&shd->gl.attrs[attr_index].name)); + } + SOKOL_ASSERT(attr_loc < (GLint)_sg.limits.max_vertex_attrs); + if (attr_loc != -1) { + _sg_gl_attr_t* gl_attr = &pip->gl.attrs[attr_loc]; + SOKOL_ASSERT(gl_attr->vb_index == -1); + gl_attr->vb_index = (int8_t) a_state->buffer_index; + if (step_func == SG_VERTEXSTEP_PER_VERTEX) { + gl_attr->divisor = 0; + } else { + gl_attr->divisor = (int8_t) step_rate; + pip->cmn.use_instanced_draw = true; + } + SOKOL_ASSERT(l_state->stride > 0); + gl_attr->stride = (uint8_t) l_state->stride; + gl_attr->offset = a_state->offset; + gl_attr->size = (uint8_t) _sg_gl_vertexformat_size(a_state->format); + gl_attr->type = _sg_gl_vertexformat_type(a_state->format); + gl_attr->normalized = _sg_gl_vertexformat_normalized(a_state->format); + pip->cmn.vertex_buffer_layout_active[a_state->buffer_index] = true; + } else { + _SG_ERROR(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER); + _SG_LOGMSG(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER, _sg_strptr(&shd->gl.attrs[attr_index].name)); + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_discard_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _sg_gl_cache_invalidate_pipeline(pip); +} + +_SOKOL_PRIVATE void _sg_gl_fb_attach_texture(const _sg_gl_attachment_t* gl_att, const _sg_attachment_common_t* cmn_att, GLenum gl_att_type) { + const _sg_image_t* img = gl_att->image; + SOKOL_ASSERT(img); + const GLuint gl_tex = img->gl.tex[0]; + SOKOL_ASSERT(gl_tex); + const GLuint gl_target = img->gl.target; + SOKOL_ASSERT(gl_target); + const int mip_level = cmn_att->mip_level; + const int slice = cmn_att->slice; + switch (img->cmn.type) { + case SG_IMAGETYPE_2D: + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, gl_target, gl_tex, mip_level); + break; + case SG_IMAGETYPE_CUBE: + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, _sg_gl_cubeface_target(slice), gl_tex, mip_level); + break; + default: + glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_att_type, gl_tex, mip_level, slice); + break; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_depth_stencil_attachment_type(const _sg_gl_attachment_t* ds_att) { + const _sg_image_t* img = ds_att->image; + SOKOL_ASSERT(img); + if (_sg_is_depth_stencil_format(img->cmn.pixel_format)) { + return GL_DEPTH_STENCIL_ATTACHMENT; + } else { + return GL_DEPTH_ATTACHMENT; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_attachments(_sg_attachments_t* atts, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_image, const sg_attachments_desc* desc) { + SOKOL_ASSERT(atts && desc); + SOKOL_ASSERT(color_images && resolve_images); + _SG_GL_CHECK_ERROR(); + + // copy image pointers + for (int i = 0; i < atts->cmn.num_colors; i++) { + const sg_attachment_desc* color_desc = &desc->colors[i]; + _SOKOL_UNUSED(color_desc); + SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == atts->gl.colors[i].image); + SOKOL_ASSERT(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); + atts->gl.colors[i].image = color_images[i]; + + const sg_attachment_desc* resolve_desc = &desc->resolves[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == atts->gl.resolves[i].image); + SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); + SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); + atts->gl.resolves[i].image = resolve_images[i]; + } + } + SOKOL_ASSERT(0 == atts->gl.depth_stencil.image); + const sg_attachment_desc* ds_desc = &desc->depth_stencil; + if (ds_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(ds_image && (ds_image->slot.id == ds_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_image->cmn.pixel_format)); + atts->gl.depth_stencil.image = ds_image; + } + + // store current framebuffer binding (restored at end of function) + GLuint gl_orig_fb; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&gl_orig_fb); + + // create a framebuffer object + glGenFramebuffers(1, &atts->gl.fb); + glBindFramebuffer(GL_FRAMEBUFFER, atts->gl.fb); + + // attach color attachments to framebuffer + for (int i = 0; i < atts->cmn.num_colors; i++) { + const _sg_image_t* color_img = atts->gl.colors[i].image; + SOKOL_ASSERT(color_img); + const GLuint gl_msaa_render_buffer = color_img->gl.msaa_render_buffer; + if (gl_msaa_render_buffer) { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, (GLenum)(GL_COLOR_ATTACHMENT0+i), GL_RENDERBUFFER, gl_msaa_render_buffer); + } else { + const GLenum gl_att_type = (GLenum)(GL_COLOR_ATTACHMENT0 + i); + _sg_gl_fb_attach_texture(&atts->gl.colors[i], &atts->cmn.colors[i], gl_att_type); + } + } + // attach depth-stencil attachment + if (atts->gl.depth_stencil.image) { + const GLenum gl_att = _sg_gl_depth_stencil_attachment_type(&atts->gl.depth_stencil); + const _sg_image_t* ds_img = atts->gl.depth_stencil.image; + const GLuint gl_msaa_render_buffer = ds_img->gl.msaa_render_buffer; + if (gl_msaa_render_buffer) { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, gl_att, GL_RENDERBUFFER, gl_msaa_render_buffer); + } else { + const GLenum gl_att_type = _sg_gl_depth_stencil_attachment_type(&atts->gl.depth_stencil); + _sg_gl_fb_attach_texture(&atts->gl.depth_stencil, &atts->cmn.depth_stencil, gl_att_type); + } + } + + // check if framebuffer is complete + { + const GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (fb_status != GL_FRAMEBUFFER_COMPLETE) { + switch (fb_status) { + case GL_FRAMEBUFFER_UNDEFINED: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNDEFINED); + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_ATTACHMENT); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MISSING_ATTACHMENT); + break; + case GL_FRAMEBUFFER_UNSUPPORTED: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNSUPPORTED); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MULTISAMPLE); + break; + default: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNKNOWN); + break; + } + return SG_RESOURCESTATE_FAILED; + } + } + + // setup color attachments for the framebuffer + static const GLenum gl_draw_bufs[SG_MAX_COLOR_ATTACHMENTS] = { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3 + }; + glDrawBuffers(atts->cmn.num_colors, gl_draw_bufs); + + // create MSAA resolve framebuffers if necessary + for (int i = 0; i < atts->cmn.num_colors; i++) { + _sg_gl_attachment_t* gl_resolve_att = &atts->gl.resolves[i]; + if (gl_resolve_att->image) { + _sg_attachment_common_t* cmn_resolve_att = &atts->cmn.resolves[i]; + SOKOL_ASSERT(0 == atts->gl.msaa_resolve_framebuffer[i]); + glGenFramebuffers(1, &atts->gl.msaa_resolve_framebuffer[i]); + glBindFramebuffer(GL_FRAMEBUFFER, atts->gl.msaa_resolve_framebuffer[i]); + _sg_gl_fb_attach_texture(gl_resolve_att, cmn_resolve_att, GL_COLOR_ATTACHMENT0); + // check if framebuffer is complete + const GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (fb_status != GL_FRAMEBUFFER_COMPLETE) { + switch (fb_status) { + case GL_FRAMEBUFFER_UNDEFINED: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNDEFINED); + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_ATTACHMENT); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MISSING_ATTACHMENT); + break; + case GL_FRAMEBUFFER_UNSUPPORTED: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNSUPPORTED); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MULTISAMPLE); + break; + default: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNKNOWN); + break; + } + return SG_RESOURCESTATE_FAILED; + } + // setup color attachments for the framebuffer + glDrawBuffers(1, &gl_draw_bufs[0]); + } + } + + // restore original framebuffer binding + glBindFramebuffer(GL_FRAMEBUFFER, gl_orig_fb); + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_discard_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + _SG_GL_CHECK_ERROR(); + if (0 != atts->gl.fb) { + glDeleteFramebuffers(1, &atts->gl.fb); + } + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (atts->gl.msaa_resolve_framebuffer[i]) { + glDeleteFramebuffers(1, &atts->gl.msaa_resolve_framebuffer[i]); + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE _sg_image_t* _sg_gl_attachments_color_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->gl.colors[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_gl_attachments_resolve_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->gl.resolves[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_gl_attachments_ds_image(const _sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + return atts->gl.depth_stencil.image; +} + +_SOKOL_PRIVATE void _sg_gl_begin_pass(const sg_pass* pass) { + // FIXME: what if a texture used as render target is still bound, should we + // unbind all currently bound textures in begin pass? + SOKOL_ASSERT(pass); + _SG_GL_CHECK_ERROR(); + const _sg_attachments_t* atts = _sg.cur_pass.atts; + const sg_swapchain* swapchain = &pass->swapchain; + const sg_pass_action* action = &pass->action; + + // bind the render pass framebuffer + // + // FIXME: Disabling SRGB conversion for the default framebuffer is + // a crude hack to make behaviour for sRGB render target textures + // identical with the Metal and D3D11 swapchains created by sokol-app. + // + // This will need a cleaner solution (e.g. allowing to configure + // sokol_app.h with an sRGB or RGB framebuffer. + if (atts) { + // offscreen pass + SOKOL_ASSERT(atts->gl.fb); + #if defined(SOKOL_GLCORE) + glEnable(GL_FRAMEBUFFER_SRGB); + #endif + glBindFramebuffer(GL_FRAMEBUFFER, atts->gl.fb); + } else { + // default pass + #if defined(SOKOL_GLCORE) + glDisable(GL_FRAMEBUFFER_SRGB); + #endif + // NOTE: on some platforms, the default framebuffer of a context + // is null, so we can't actually assert here that the + // framebuffer has been provided + glBindFramebuffer(GL_FRAMEBUFFER, swapchain->gl.framebuffer); + } + glViewport(0, 0, _sg.cur_pass.width, _sg.cur_pass.height); + glScissor(0, 0, _sg.cur_pass.width, _sg.cur_pass.height); + + // number of color attachments + const int num_color_atts = atts ? atts->cmn.num_colors : 1; + + // clear color and depth-stencil attachments if needed + bool clear_any_color = false; + for (int i = 0; i < num_color_atts; i++) { + if (SG_LOADACTION_CLEAR == action->colors[i].load_action) { + clear_any_color = true; + break; + } + } + const bool clear_depth = (action->depth.load_action == SG_LOADACTION_CLEAR); + const bool clear_stencil = (action->stencil.load_action == SG_LOADACTION_CLEAR); + + bool need_pip_cache_flush = false; + if (clear_any_color) { + bool need_color_mask_flush = false; + // NOTE: not a bug to iterate over all possible color attachments + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (SG_COLORMASK_RGBA != _sg.gl.cache.color_write_mask[i]) { + need_pip_cache_flush = true; + need_color_mask_flush = true; + _sg.gl.cache.color_write_mask[i] = SG_COLORMASK_RGBA; + } + } + if (need_color_mask_flush) { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + } + if (clear_depth) { + if (!_sg.gl.cache.depth.write_enabled) { + need_pip_cache_flush = true; + _sg.gl.cache.depth.write_enabled = true; + glDepthMask(GL_TRUE); + } + if (_sg.gl.cache.depth.compare != SG_COMPAREFUNC_ALWAYS) { + need_pip_cache_flush = true; + _sg.gl.cache.depth.compare = SG_COMPAREFUNC_ALWAYS; + glDepthFunc(GL_ALWAYS); + } + } + if (clear_stencil) { + if (_sg.gl.cache.stencil.write_mask != 0xFF) { + need_pip_cache_flush = true; + _sg.gl.cache.stencil.write_mask = 0xFF; + glStencilMask(0xFF); + } + } + if (need_pip_cache_flush) { + // we messed with the state cache directly, need to clear cached + // pipeline to force re-evaluation in next sg_apply_pipeline() + _sg.gl.cache.cur_pipeline = 0; + _sg.gl.cache.cur_pipeline_id.id = SG_INVALID_ID; + } + for (int i = 0; i < num_color_atts; i++) { + if (action->colors[i].load_action == SG_LOADACTION_CLEAR) { + glClearBufferfv(GL_COLOR, i, &action->colors[i].clear_value.r); + } + } + if ((atts == 0) || (atts->gl.depth_stencil.image)) { + if (clear_depth && clear_stencil) { + glClearBufferfi(GL_DEPTH_STENCIL, 0, action->depth.clear_value, action->stencil.clear_value); + } else if (clear_depth) { + glClearBufferfv(GL_DEPTH, 0, &action->depth.clear_value); + } else if (clear_stencil) { + GLint val = (GLint) action->stencil.clear_value; + glClearBufferiv(GL_STENCIL, 0, &val); + } + } + // keep store actions for end-pass + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.gl.color_store_actions[i] = action->colors[i].store_action; + } + _sg.gl.depth_store_action = action->depth.store_action; + _sg.gl.stencil_store_action = action->stencil.store_action; + + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_end_pass(void) { + _SG_GL_CHECK_ERROR(); + + if (_sg.cur_pass.atts) { + const _sg_attachments_t* atts = _sg.cur_pass.atts; + SOKOL_ASSERT(atts->slot.id == _sg.cur_pass.atts_id.id); + bool fb_read_bound = false; + bool fb_draw_bound = false; + const int num_color_atts = atts->cmn.num_colors; + for (int i = 0; i < num_color_atts; i++) { + // perform MSAA resolve if needed + if (atts->gl.msaa_resolve_framebuffer[i] != 0) { + if (!fb_read_bound) { + SOKOL_ASSERT(atts->gl.fb); + glBindFramebuffer(GL_READ_FRAMEBUFFER, atts->gl.fb); + fb_read_bound = true; + } + const int w = atts->gl.colors[i].image->cmn.width; + const int h = atts->gl.colors[i].image->cmn.height; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, atts->gl.msaa_resolve_framebuffer[i]); + glReadBuffer((GLenum)(GL_COLOR_ATTACHMENT0 + i)); + glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); + fb_draw_bound = true; + } + } + + // invalidate framebuffers + _SOKOL_UNUSED(fb_draw_bound); + #if defined(SOKOL_GLES3) + // need to restore framebuffer binding before invalidate if the MSAA resolve had changed the binding + if (fb_draw_bound) { + glBindFramebuffer(GL_FRAMEBUFFER, atts->gl.fb); + } + GLenum invalidate_atts[SG_MAX_COLOR_ATTACHMENTS + 2] = { 0 }; + int att_index = 0; + for (int i = 0; i < num_color_atts; i++) { + if (_sg.gl.color_store_actions[i] == SG_STOREACTION_DONTCARE) { + invalidate_atts[att_index++] = (GLenum)(GL_COLOR_ATTACHMENT0 + i); + } + } + if ((_sg.gl.depth_store_action == SG_STOREACTION_DONTCARE) && (_sg.cur_pass.atts->cmn.depth_stencil.image_id.id != SG_INVALID_ID)) { + invalidate_atts[att_index++] = GL_DEPTH_ATTACHMENT; + } + if ((_sg.gl.stencil_store_action == SG_STOREACTION_DONTCARE) && (_sg.cur_pass.atts->cmn.depth_stencil.image_id.id != SG_INVALID_ID)) { + invalidate_atts[att_index++] = GL_STENCIL_ATTACHMENT; + } + if (att_index > 0) { + glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, att_index, invalidate_atts); + } + #endif + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + y = origin_top_left ? (_sg.cur_pass.height - (y+h)) : y; + glViewport(x, y, w, h); +} + +_SOKOL_PRIVATE void _sg_gl_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + y = origin_top_left ? (_sg.cur_pass.height - (y+h)) : y; + glScissor(x, y, w, h); +} + +_SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + _SG_GL_CHECK_ERROR(); + if ((_sg.gl.cache.cur_pipeline != pip) || (_sg.gl.cache.cur_pipeline_id.id != pip->slot.id)) { + _sg.gl.cache.cur_pipeline = pip; + _sg.gl.cache.cur_pipeline_id.id = pip->slot.id; + _sg.gl.cache.cur_primitive_type = _sg_gl_primitive_type(pip->gl.primitive_type); + _sg.gl.cache.cur_index_type = _sg_gl_index_type(pip->cmn.index_type); + + // update depth state + { + const sg_depth_state* state_ds = &pip->gl.depth; + sg_depth_state* cache_ds = &_sg.gl.cache.depth; + if (state_ds->compare != cache_ds->compare) { + cache_ds->compare = state_ds->compare; + glDepthFunc(_sg_gl_compare_func(state_ds->compare)); + _sg_stats_add(gl.num_render_state, 1); + } + if (state_ds->write_enabled != cache_ds->write_enabled) { + cache_ds->write_enabled = state_ds->write_enabled; + glDepthMask(state_ds->write_enabled); + _sg_stats_add(gl.num_render_state, 1); + } + if (!_sg_fequal(state_ds->bias, cache_ds->bias, 0.000001f) || + !_sg_fequal(state_ds->bias_slope_scale, cache_ds->bias_slope_scale, 0.000001f)) + { + /* according to ANGLE's D3D11 backend: + D3D11 SlopeScaledDepthBias ==> GL polygonOffsetFactor + D3D11 DepthBias ==> GL polygonOffsetUnits + DepthBiasClamp has no meaning on GL + */ + cache_ds->bias = state_ds->bias; + cache_ds->bias_slope_scale = state_ds->bias_slope_scale; + glPolygonOffset(state_ds->bias_slope_scale, state_ds->bias); + _sg_stats_add(gl.num_render_state, 1); + bool po_enabled = true; + if (_sg_fequal(state_ds->bias, 0.0f, 0.000001f) && + _sg_fequal(state_ds->bias_slope_scale, 0.0f, 0.000001f)) + { + po_enabled = false; + } + if (po_enabled != _sg.gl.cache.polygon_offset_enabled) { + _sg.gl.cache.polygon_offset_enabled = po_enabled; + if (po_enabled) { + glEnable(GL_POLYGON_OFFSET_FILL); + } else { + glDisable(GL_POLYGON_OFFSET_FILL); + } + _sg_stats_add(gl.num_render_state, 1); + } + } + } + + // update stencil state + { + const sg_stencil_state* state_ss = &pip->gl.stencil; + sg_stencil_state* cache_ss = &_sg.gl.cache.stencil; + if (state_ss->enabled != cache_ss->enabled) { + cache_ss->enabled = state_ss->enabled; + if (state_ss->enabled) { + glEnable(GL_STENCIL_TEST); + } else { + glDisable(GL_STENCIL_TEST); + } + _sg_stats_add(gl.num_render_state, 1); + } + if (state_ss->write_mask != cache_ss->write_mask) { + cache_ss->write_mask = state_ss->write_mask; + glStencilMask(state_ss->write_mask); + _sg_stats_add(gl.num_render_state, 1); + } + for (int i = 0; i < 2; i++) { + const sg_stencil_face_state* state_sfs = (i==0)? &state_ss->front : &state_ss->back; + sg_stencil_face_state* cache_sfs = (i==0)? &cache_ss->front : &cache_ss->back; + GLenum gl_face = (i==0)? GL_FRONT : GL_BACK; + if ((state_sfs->compare != cache_sfs->compare) || + (state_ss->read_mask != cache_ss->read_mask) || + (state_ss->ref != cache_ss->ref)) + { + cache_sfs->compare = state_sfs->compare; + glStencilFuncSeparate(gl_face, + _sg_gl_compare_func(state_sfs->compare), + state_ss->ref, + state_ss->read_mask); + _sg_stats_add(gl.num_render_state, 1); + } + if ((state_sfs->fail_op != cache_sfs->fail_op) || + (state_sfs->depth_fail_op != cache_sfs->depth_fail_op) || + (state_sfs->pass_op != cache_sfs->pass_op)) + { + cache_sfs->fail_op = state_sfs->fail_op; + cache_sfs->depth_fail_op = state_sfs->depth_fail_op; + cache_sfs->pass_op = state_sfs->pass_op; + glStencilOpSeparate(gl_face, + _sg_gl_stencil_op(state_sfs->fail_op), + _sg_gl_stencil_op(state_sfs->depth_fail_op), + _sg_gl_stencil_op(state_sfs->pass_op)); + _sg_stats_add(gl.num_render_state, 1); + } + } + cache_ss->read_mask = state_ss->read_mask; + cache_ss->ref = state_ss->ref; + } + + if (pip->cmn.color_count > 0) { + // update blend state + // FIXME: separate blend state per color attachment not support, needs GL4 + const sg_blend_state* state_bs = &pip->gl.blend; + sg_blend_state* cache_bs = &_sg.gl.cache.blend; + if (state_bs->enabled != cache_bs->enabled) { + cache_bs->enabled = state_bs->enabled; + if (state_bs->enabled) { + glEnable(GL_BLEND); + } else { + glDisable(GL_BLEND); + } + _sg_stats_add(gl.num_render_state, 1); + } + if ((state_bs->src_factor_rgb != cache_bs->src_factor_rgb) || + (state_bs->dst_factor_rgb != cache_bs->dst_factor_rgb) || + (state_bs->src_factor_alpha != cache_bs->src_factor_alpha) || + (state_bs->dst_factor_alpha != cache_bs->dst_factor_alpha)) + { + cache_bs->src_factor_rgb = state_bs->src_factor_rgb; + cache_bs->dst_factor_rgb = state_bs->dst_factor_rgb; + cache_bs->src_factor_alpha = state_bs->src_factor_alpha; + cache_bs->dst_factor_alpha = state_bs->dst_factor_alpha; + glBlendFuncSeparate(_sg_gl_blend_factor(state_bs->src_factor_rgb), + _sg_gl_blend_factor(state_bs->dst_factor_rgb), + _sg_gl_blend_factor(state_bs->src_factor_alpha), + _sg_gl_blend_factor(state_bs->dst_factor_alpha)); + _sg_stats_add(gl.num_render_state, 1); + } + if ((state_bs->op_rgb != cache_bs->op_rgb) || (state_bs->op_alpha != cache_bs->op_alpha)) { + cache_bs->op_rgb = state_bs->op_rgb; + cache_bs->op_alpha = state_bs->op_alpha; + glBlendEquationSeparate(_sg_gl_blend_op(state_bs->op_rgb), _sg_gl_blend_op(state_bs->op_alpha)); + _sg_stats_add(gl.num_render_state, 1); + } + + // standalone color target state + for (GLuint i = 0; i < (GLuint)pip->cmn.color_count; i++) { + if (pip->gl.color_write_mask[i] != _sg.gl.cache.color_write_mask[i]) { + const sg_color_mask cm = pip->gl.color_write_mask[i]; + _sg.gl.cache.color_write_mask[i] = cm; + #ifdef SOKOL_GLCORE + glColorMaski(i, + (cm & SG_COLORMASK_R) != 0, + (cm & SG_COLORMASK_G) != 0, + (cm & SG_COLORMASK_B) != 0, + (cm & SG_COLORMASK_A) != 0); + #else + if (0 == i) { + glColorMask((cm & SG_COLORMASK_R) != 0, + (cm & SG_COLORMASK_G) != 0, + (cm & SG_COLORMASK_B) != 0, + (cm & SG_COLORMASK_A) != 0); + } + #endif + _sg_stats_add(gl.num_render_state, 1); + } + } + + if (!_sg_fequal(pip->cmn.blend_color.r, _sg.gl.cache.blend_color.r, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.g, _sg.gl.cache.blend_color.g, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.b, _sg.gl.cache.blend_color.b, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.a, _sg.gl.cache.blend_color.a, 0.0001f)) + { + sg_color c = pip->cmn.blend_color; + _sg.gl.cache.blend_color = c; + glBlendColor(c.r, c.g, c.b, c.a); + _sg_stats_add(gl.num_render_state, 1); + } + } // pip->cmn.color_count > 0 + + if (pip->gl.cull_mode != _sg.gl.cache.cull_mode) { + _sg.gl.cache.cull_mode = pip->gl.cull_mode; + if (SG_CULLMODE_NONE == pip->gl.cull_mode) { + glDisable(GL_CULL_FACE); + _sg_stats_add(gl.num_render_state, 1); + } else { + glEnable(GL_CULL_FACE); + GLenum gl_mode = (SG_CULLMODE_FRONT == pip->gl.cull_mode) ? GL_FRONT : GL_BACK; + glCullFace(gl_mode); + _sg_stats_add(gl.num_render_state, 2); + } + } + if (pip->gl.face_winding != _sg.gl.cache.face_winding) { + _sg.gl.cache.face_winding = pip->gl.face_winding; + GLenum gl_winding = (SG_FACEWINDING_CW == pip->gl.face_winding) ? GL_CW : GL_CCW; + glFrontFace(gl_winding); + _sg_stats_add(gl.num_render_state, 1); + } + if (pip->gl.alpha_to_coverage_enabled != _sg.gl.cache.alpha_to_coverage_enabled) { + _sg.gl.cache.alpha_to_coverage_enabled = pip->gl.alpha_to_coverage_enabled; + if (pip->gl.alpha_to_coverage_enabled) { + glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); + } else { + glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + } + _sg_stats_add(gl.num_render_state, 1); + } + #ifdef SOKOL_GLCORE + if (pip->gl.sample_count != _sg.gl.cache.sample_count) { + _sg.gl.cache.sample_count = pip->gl.sample_count; + if (pip->gl.sample_count > 1) { + glEnable(GL_MULTISAMPLE); + } else { + glDisable(GL_MULTISAMPLE); + } + _sg_stats_add(gl.num_render_state, 1); + } + #endif + + // bind shader program + if (pip->shader->gl.prog != _sg.gl.cache.prog) { + _sg.gl.cache.prog = pip->shader->gl.prog; + glUseProgram(pip->shader->gl.prog); + _sg_stats_add(gl.num_use_program, 1); + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE bool _sg_gl_apply_bindings(_sg_bindings_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); + _SG_GL_CHECK_ERROR(); + + // bind combined image-samplers + _SG_GL_CHECK_ERROR(); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const _sg_shader_stage_t* stage = &bnd->pip->shader->cmn.stage[stage_index]; + const _sg_gl_shader_stage_t* gl_stage = &bnd->pip->shader->gl.stage[stage_index]; + _sg_image_t** imgs = (stage_index == SG_SHADERSTAGE_VS) ? bnd->vs_imgs : bnd->fs_imgs; + _sg_sampler_t** smps = (stage_index == SG_SHADERSTAGE_VS) ? bnd->vs_smps : bnd->fs_smps; + const int num_imgs = (stage_index == SG_SHADERSTAGE_VS) ? bnd->num_vs_imgs : bnd->num_fs_imgs; + const int num_smps = (stage_index == SG_SHADERSTAGE_VS) ? bnd->num_vs_smps : bnd->num_fs_smps; + SOKOL_ASSERT(num_imgs == stage->num_images); _SOKOL_UNUSED(num_imgs); + SOKOL_ASSERT(num_smps == stage->num_samplers); _SOKOL_UNUSED(num_smps); + for (int img_smp_index = 0; img_smp_index < stage->num_image_samplers; img_smp_index++) { + const int gl_tex_slot = gl_stage->image_samplers[img_smp_index].gl_tex_slot; + if (gl_tex_slot != -1) { + const int img_index = stage->image_samplers[img_smp_index].image_slot; + const int smp_index = stage->image_samplers[img_smp_index].sampler_slot; + SOKOL_ASSERT(img_index < num_imgs); + SOKOL_ASSERT(smp_index < num_smps); + _sg_image_t* img = imgs[img_index]; + _sg_sampler_t* smp = smps[smp_index]; + const GLenum gl_tgt = img->gl.target; + const GLuint gl_tex = img->gl.tex[img->cmn.active_slot]; + const GLuint gl_smp = smp->gl.smp; + _sg_gl_cache_bind_texture_sampler(gl_tex_slot, gl_tgt, gl_tex, gl_smp); + } + } + } + _SG_GL_CHECK_ERROR(); + + // bind storage buffers + for (int slot = 0; slot < bnd->num_vs_sbufs; slot++) { + _sg_buffer_t* sb = bnd->vs_sbufs[slot]; + GLuint gl_sb = sb->gl.buf[sb->cmn.active_slot]; + _sg_gl_cache_bind_storage_buffer(SG_SHADERSTAGE_VS, slot, gl_sb); + } + for (int slot = 0; slot < bnd->num_fs_sbufs; slot++) { + _sg_buffer_t* sb = bnd->fs_sbufs[slot]; + GLuint gl_sb = sb->gl.buf[sb->cmn.active_slot]; + _sg_gl_cache_bind_storage_buffer(SG_SHADERSTAGE_FS, slot, gl_sb); + } + + // index buffer (can be 0) + const GLuint gl_ib = bnd->ib ? bnd->ib->gl.buf[bnd->ib->cmn.active_slot] : 0; + _sg_gl_cache_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, gl_ib); + _sg.gl.cache.cur_ib_offset = bnd->ib_offset; + + // vertex attributes + for (GLuint attr_index = 0; attr_index < (GLuint)_sg.limits.max_vertex_attrs; attr_index++) { + _sg_gl_attr_t* attr = &bnd->pip->gl.attrs[attr_index]; + _sg_gl_cache_attr_t* cache_attr = &_sg.gl.cache.attrs[attr_index]; + bool cache_attr_dirty = false; + int vb_offset = 0; + GLuint gl_vb = 0; + if (attr->vb_index >= 0) { + // attribute is enabled + SOKOL_ASSERT(attr->vb_index < bnd->num_vbs); + _sg_buffer_t* vb = bnd->vbs[attr->vb_index]; + SOKOL_ASSERT(vb); + gl_vb = vb->gl.buf[vb->cmn.active_slot]; + vb_offset = bnd->vb_offsets[attr->vb_index] + attr->offset; + if ((gl_vb != cache_attr->gl_vbuf) || + (attr->size != cache_attr->gl_attr.size) || + (attr->type != cache_attr->gl_attr.type) || + (attr->normalized != cache_attr->gl_attr.normalized) || + (attr->stride != cache_attr->gl_attr.stride) || + (vb_offset != cache_attr->gl_attr.offset) || + (cache_attr->gl_attr.divisor != attr->divisor)) + { + _sg_gl_cache_bind_buffer(GL_ARRAY_BUFFER, gl_vb); + glVertexAttribPointer(attr_index, attr->size, attr->type, attr->normalized, attr->stride, (const GLvoid*)(GLintptr)vb_offset); + _sg_stats_add(gl.num_vertex_attrib_pointer, 1); + glVertexAttribDivisor(attr_index, (GLuint)attr->divisor); + _sg_stats_add(gl.num_vertex_attrib_divisor, 1); + cache_attr_dirty = true; + } + if (cache_attr->gl_attr.vb_index == -1) { + glEnableVertexAttribArray(attr_index); + _sg_stats_add(gl.num_enable_vertex_attrib_array, 1); + cache_attr_dirty = true; + } + } else { + // attribute is disabled + if (cache_attr->gl_attr.vb_index != -1) { + glDisableVertexAttribArray(attr_index); + _sg_stats_add(gl.num_disable_vertex_attrib_array, 1); + cache_attr_dirty = true; + } + } + if (cache_attr_dirty) { + cache_attr->gl_attr = *attr; + cache_attr->gl_attr.offset = vb_offset; + cache_attr->gl_vbuf = gl_vb; + } + } + _SG_GL_CHECK_ERROR(); + return true; +} + +_SOKOL_PRIVATE void _sg_gl_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->slot.id == _sg.gl.cache.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->slot.id == _sg.gl.cache.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks > ub_index); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size == data->size); + const _sg_gl_shader_stage_t* gl_stage = &_sg.gl.cache.cur_pipeline->shader->gl.stage[stage_index]; + const _sg_gl_uniform_block_t* gl_ub = &gl_stage->uniform_blocks[ub_index]; + for (int u_index = 0; u_index < gl_ub->num_uniforms; u_index++) { + const _sg_gl_uniform_t* u = &gl_ub->uniforms[u_index]; + SOKOL_ASSERT(u->type != SG_UNIFORMTYPE_INVALID); + if (u->gl_loc == -1) { + continue; + } + _sg_stats_add(gl.num_uniform, 1); + GLfloat* fptr = (GLfloat*) (((uint8_t*)data->ptr) + u->offset); + GLint* iptr = (GLint*) (((uint8_t*)data->ptr) + u->offset); + switch (u->type) { + case SG_UNIFORMTYPE_INVALID: + break; + case SG_UNIFORMTYPE_FLOAT: + glUniform1fv(u->gl_loc, u->count, fptr); + break; + case SG_UNIFORMTYPE_FLOAT2: + glUniform2fv(u->gl_loc, u->count, fptr); + break; + case SG_UNIFORMTYPE_FLOAT3: + glUniform3fv(u->gl_loc, u->count, fptr); + break; + case SG_UNIFORMTYPE_FLOAT4: + glUniform4fv(u->gl_loc, u->count, fptr); + break; + case SG_UNIFORMTYPE_INT: + glUniform1iv(u->gl_loc, u->count, iptr); + break; + case SG_UNIFORMTYPE_INT2: + glUniform2iv(u->gl_loc, u->count, iptr); + break; + case SG_UNIFORMTYPE_INT3: + glUniform3iv(u->gl_loc, u->count, iptr); + break; + case SG_UNIFORMTYPE_INT4: + glUniform4iv(u->gl_loc, u->count, iptr); + break; + case SG_UNIFORMTYPE_MAT4: + glUniformMatrix4fv(u->gl_loc, u->count, GL_FALSE, fptr); + break; + default: + SOKOL_UNREACHABLE; + break; + } + } +} + +_SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline); + const GLenum i_type = _sg.gl.cache.cur_index_type; + const GLenum p_type = _sg.gl.cache.cur_primitive_type; + const bool use_instanced_draw = (num_instances > 1) || (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw); + if (0 != i_type) { + // indexed rendering + const int i_size = (i_type == GL_UNSIGNED_SHORT) ? 2 : 4; + const int ib_offset = _sg.gl.cache.cur_ib_offset; + const GLvoid* indices = (const GLvoid*)(GLintptr)(base_element*i_size+ib_offset); + if (use_instanced_draw) { + glDrawElementsInstanced(p_type, num_elements, i_type, indices, num_instances); + } else { + glDrawElements(p_type, num_elements, i_type, indices); + } + } else { + // non-indexed rendering + if (use_instanced_draw) { + glDrawArraysInstanced(p_type, base_element, num_elements, num_instances); + } else { + glDrawArrays(p_type, base_element, num_elements); + } + } +} + +_SOKOL_PRIVATE void _sg_gl_commit(void) { + // "soft" clear bindings (only those that are actually bound) + _sg_gl_cache_clear_buffer_bindings(false); + _sg_gl_cache_clear_texture_sampler_bindings(false); +} + +_SOKOL_PRIVATE void _sg_gl_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + // only one update per buffer per frame allowed + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + GLenum gl_tgt = _sg_gl_buffer_target(buf->cmn.type); + SOKOL_ASSERT(buf->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); + GLuint gl_buf = buf->gl.buf[buf->cmn.active_slot]; + SOKOL_ASSERT(gl_buf); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_store_buffer_binding(gl_tgt); + _sg_gl_cache_bind_buffer(gl_tgt, gl_buf); + glBufferSubData(gl_tgt, 0, (GLsizeiptr)data->size, data->ptr); + _sg_gl_cache_restore_buffer_binding(gl_tgt); + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + if (new_frame) { + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + } + GLenum gl_tgt = _sg_gl_buffer_target(buf->cmn.type); + SOKOL_ASSERT(buf->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); + GLuint gl_buf = buf->gl.buf[buf->cmn.active_slot]; + SOKOL_ASSERT(gl_buf); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_store_buffer_binding(gl_tgt); + _sg_gl_cache_bind_buffer(gl_tgt, gl_buf); + glBufferSubData(gl_tgt, buf->cmn.append_pos, (GLsizeiptr)data->size, data->ptr); + _sg_gl_cache_restore_buffer_binding(gl_tgt); + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + // only one update per image per frame allowed + if (++img->cmn.active_slot >= img->cmn.num_slots) { + img->cmn.active_slot = 0; + } + SOKOL_ASSERT(img->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); + SOKOL_ASSERT(0 != img->gl.tex[img->cmn.active_slot]); + _sg_gl_cache_store_texture_sampler_binding(0); + _sg_gl_cache_bind_texture_sampler(0, img->gl.target, img->gl.tex[img->cmn.active_slot], 0); + const GLenum gl_img_format = _sg_gl_teximage_format(img->cmn.pixel_format); + const GLenum gl_img_type = _sg_gl_teximage_type(img->cmn.pixel_format); + const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1; + const int num_mips = img->cmn.num_mipmaps; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < num_mips; mip_index++) { + GLenum gl_img_target = img->gl.target; + if (SG_IMAGETYPE_CUBE == img->cmn.type) { + gl_img_target = _sg_gl_cubeface_target(face_index); + } + const GLvoid* data_ptr = data->subimage[face_index][mip_index].ptr; + int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { + glTexSubImage2D(gl_img_target, mip_index, + 0, 0, + mip_width, mip_height, + gl_img_format, gl_img_type, + data_ptr); + } else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) { + int mip_depth = img->cmn.num_slices; + if (SG_IMAGETYPE_3D == img->cmn.type) { + mip_depth = _sg_miplevel_dim(img->cmn.num_slices, mip_index); + } + glTexSubImage3D(gl_img_target, mip_index, + 0, 0, 0, + mip_width, mip_height, mip_depth, + gl_img_format, gl_img_type, + data_ptr); + + } + } + } + _sg_gl_cache_restore_texture_sampler_binding(0); +} + +// ██████ ██████ ██████ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ███ ███ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ █████ ██ ██ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██████ ██ ██ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>d3d11 backend +#elif defined(SOKOL_D3D11) + +#define _SG_D3D11_MAX_SHADERSTAGE_SRVS (32) +#define _SG_D3D11_SHADERSTAGE_IMAGE_SRV_OFFSET (0) +#define _SG_D3D11_SHADERSTAGE_BUFFER_SRV_OFFSET (16) + +#if defined(__cplusplus) +#define _sg_d3d11_AddRef(self) (self)->AddRef() +#else +#define _sg_d3d11_AddRef(self) (self)->lpVtbl->AddRef(self) +#endif + +#if defined(__cplusplus) +#define _sg_d3d11_Release(self) (self)->Release() +#else +#define _sg_d3d11_Release(self) (self)->lpVtbl->Release(self) +#endif + +// NOTE: This needs to be a macro since we can't use the polymorphism in C. It's called on many kinds of resources. +// NOTE: Based on microsoft docs, it's fine to call this with pData=NULL if DataSize is also zero. +#if defined(__cplusplus) +#define _sg_d3d11_SetPrivateData(self, guid, DataSize, pData) (self)->SetPrivateData(guid, DataSize, pData) +#else +#define _sg_d3d11_SetPrivateData(self, guid, DataSize, pData) (self)->lpVtbl->SetPrivateData(self, guid, DataSize, pData) +#endif + +#if defined(__cplusplus) +#define _sg_win32_refguid(guid) guid +#else +#define _sg_win32_refguid(guid) &guid +#endif + +static const GUID _sg_d3d11_WKPDID_D3DDebugObjectName = { 0x429b8c22,0x9188,0x4b0c, {0x87,0x42,0xac,0xb0,0xbf,0x85,0xc2,0x00} }; + +#if defined(SOKOL_DEBUG) +#define _sg_d3d11_setlabel(self, label) _sg_d3d11_SetPrivateData(self, _sg_win32_refguid(_sg_d3d11_WKPDID_D3DDebugObjectName), label ? (UINT)strlen(label) : 0, label) +#else +#define _sg_d3d11_setlabel(self, label) +#endif + + +//-- D3D11 C/C++ wrappers ------------------------------------------------------ +static inline HRESULT _sg_d3d11_CheckFormatSupport(ID3D11Device* self, DXGI_FORMAT Format, UINT* pFormatSupport) { + #if defined(__cplusplus) + return self->CheckFormatSupport(Format, pFormatSupport); + #else + return self->lpVtbl->CheckFormatSupport(self, Format, pFormatSupport); + #endif +} + +static inline void _sg_d3d11_OMSetRenderTargets(ID3D11DeviceContext* self, UINT NumViews, ID3D11RenderTargetView* const* ppRenderTargetViews, ID3D11DepthStencilView *pDepthStencilView) { + #if defined(__cplusplus) + self->OMSetRenderTargets(NumViews, ppRenderTargetViews, pDepthStencilView); + #else + self->lpVtbl->OMSetRenderTargets(self, NumViews, ppRenderTargetViews, pDepthStencilView); + #endif +} + +static inline void _sg_d3d11_RSSetState(ID3D11DeviceContext* self, ID3D11RasterizerState* pRasterizerState) { + #if defined(__cplusplus) + self->RSSetState(pRasterizerState); + #else + self->lpVtbl->RSSetState(self, pRasterizerState); + #endif +} + +static inline void _sg_d3d11_OMSetDepthStencilState(ID3D11DeviceContext* self, ID3D11DepthStencilState* pDepthStencilState, UINT StencilRef) { + #if defined(__cplusplus) + self->OMSetDepthStencilState(pDepthStencilState, StencilRef); + #else + self->lpVtbl->OMSetDepthStencilState(self, pDepthStencilState, StencilRef); + #endif +} + +static inline void _sg_d3d11_OMSetBlendState(ID3D11DeviceContext* self, ID3D11BlendState* pBlendState, const FLOAT BlendFactor[4], UINT SampleMask) { + #if defined(__cplusplus) + self->OMSetBlendState(pBlendState, BlendFactor, SampleMask); + #else + self->lpVtbl->OMSetBlendState(self, pBlendState, BlendFactor, SampleMask); + #endif +} + +static inline void _sg_d3d11_IASetVertexBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppVertexBuffers, const UINT* pStrides, const UINT* pOffsets) { + #if defined(__cplusplus) + self->IASetVertexBuffers(StartSlot, NumBuffers, ppVertexBuffers, pStrides, pOffsets); + #else + self->lpVtbl->IASetVertexBuffers(self, StartSlot, NumBuffers, ppVertexBuffers, pStrides, pOffsets); + #endif +} + +static inline void _sg_d3d11_IASetIndexBuffer(ID3D11DeviceContext* self, ID3D11Buffer* pIndexBuffer, DXGI_FORMAT Format, UINT Offset) { + #if defined(__cplusplus) + self->IASetIndexBuffer(pIndexBuffer, Format, Offset); + #else + self->lpVtbl->IASetIndexBuffer(self, pIndexBuffer, Format, Offset); + #endif +} + +static inline void _sg_d3d11_IASetInputLayout(ID3D11DeviceContext* self, ID3D11InputLayout* pInputLayout) { + #if defined(__cplusplus) + self->IASetInputLayout(pInputLayout); + #else + self->lpVtbl->IASetInputLayout(self, pInputLayout); + #endif +} + +static inline void _sg_d3d11_VSSetShader(ID3D11DeviceContext* self, ID3D11VertexShader* pVertexShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { + #if defined(__cplusplus) + self->VSSetShader(pVertexShader, ppClassInstances, NumClassInstances); + #else + self->lpVtbl->VSSetShader(self, pVertexShader, ppClassInstances, NumClassInstances); + #endif +} + +static inline void _sg_d3d11_PSSetShader(ID3D11DeviceContext* self, ID3D11PixelShader* pPixelShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { + #if defined(__cplusplus) + self->PSSetShader(pPixelShader, ppClassInstances, NumClassInstances); + #else + self->lpVtbl->PSSetShader(self, pPixelShader, ppClassInstances, NumClassInstances); + #endif +} + +static inline void _sg_d3d11_VSSetConstantBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { + #if defined(__cplusplus) + self->VSSetConstantBuffers(StartSlot, NumBuffers, ppConstantBuffers); + #else + self->lpVtbl->VSSetConstantBuffers(self, StartSlot, NumBuffers, ppConstantBuffers); + #endif +} + +static inline void _sg_d3d11_PSSetConstantBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { + #if defined(__cplusplus) + self->PSSetConstantBuffers(StartSlot, NumBuffers, ppConstantBuffers); + #else + self->lpVtbl->PSSetConstantBuffers(self, StartSlot, NumBuffers, ppConstantBuffers); + #endif +} + +static inline void _sg_d3d11_VSSetShaderResources(ID3D11DeviceContext* self, UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { + #if defined(__cplusplus) + self->VSSetShaderResources(StartSlot, NumViews, ppShaderResourceViews); + #else + self->lpVtbl->VSSetShaderResources(self, StartSlot, NumViews, ppShaderResourceViews); + #endif +} + +static inline void _sg_d3d11_PSSetShaderResources(ID3D11DeviceContext* self, UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { + #if defined(__cplusplus) + self->PSSetShaderResources(StartSlot, NumViews, ppShaderResourceViews); + #else + self->lpVtbl->PSSetShaderResources(self, StartSlot, NumViews, ppShaderResourceViews); + #endif +} + +static inline void _sg_d3d11_VSSetSamplers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { + #if defined(__cplusplus) + self->VSSetSamplers(StartSlot, NumSamplers, ppSamplers); + #else + self->lpVtbl->VSSetSamplers(self, StartSlot, NumSamplers, ppSamplers); + #endif +} + +static inline void _sg_d3d11_PSSetSamplers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { + #if defined(__cplusplus) + self->PSSetSamplers(StartSlot, NumSamplers, ppSamplers); + #else + self->lpVtbl->PSSetSamplers(self, StartSlot, NumSamplers, ppSamplers); + #endif +} + +static inline HRESULT _sg_d3d11_CreateBuffer(ID3D11Device* self, const D3D11_BUFFER_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Buffer** ppBuffer) { + #if defined(__cplusplus) + return self->CreateBuffer(pDesc, pInitialData, ppBuffer); + #else + return self->lpVtbl->CreateBuffer(self, pDesc, pInitialData, ppBuffer); + #endif +} + +static inline HRESULT _sg_d3d11_CreateTexture2D(ID3D11Device* self, const D3D11_TEXTURE2D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D** ppTexture2D) { + #if defined(__cplusplus) + return self->CreateTexture2D(pDesc, pInitialData, ppTexture2D); + #else + return self->lpVtbl->CreateTexture2D(self, pDesc, pInitialData, ppTexture2D); + #endif +} + +static inline HRESULT _sg_d3d11_CreateShaderResourceView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, ID3D11ShaderResourceView** ppSRView) { + #if defined(__cplusplus) + return self->CreateShaderResourceView(pResource, pDesc, ppSRView); + #else + return self->lpVtbl->CreateShaderResourceView(self, pResource, pDesc, ppSRView); + #endif +} + +static inline void _sg_d3d11_GetResource(ID3D11View* self, ID3D11Resource** ppResource) { + #if defined(__cplusplus) + self->GetResource(ppResource); + #else + self->lpVtbl->GetResource(self, ppResource); + #endif +} + +static inline HRESULT _sg_d3d11_CreateTexture3D(ID3D11Device* self, const D3D11_TEXTURE3D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture3D** ppTexture3D) { + #if defined(__cplusplus) + return self->CreateTexture3D(pDesc, pInitialData, ppTexture3D); + #else + return self->lpVtbl->CreateTexture3D(self, pDesc, pInitialData, ppTexture3D); + #endif +} + +static inline HRESULT _sg_d3d11_CreateSamplerState(ID3D11Device* self, const D3D11_SAMPLER_DESC* pSamplerDesc, ID3D11SamplerState** ppSamplerState) { + #if defined(__cplusplus) + return self->CreateSamplerState(pSamplerDesc, ppSamplerState); + #else + return self->lpVtbl->CreateSamplerState(self, pSamplerDesc, ppSamplerState); + #endif +} + +static inline LPVOID _sg_d3d11_GetBufferPointer(ID3D10Blob* self) { + #if defined(__cplusplus) + return self->GetBufferPointer(); + #else + return self->lpVtbl->GetBufferPointer(self); + #endif +} + +static inline SIZE_T _sg_d3d11_GetBufferSize(ID3D10Blob* self) { + #if defined(__cplusplus) + return self->GetBufferSize(); + #else + return self->lpVtbl->GetBufferSize(self); + #endif +} + +static inline HRESULT _sg_d3d11_CreateVertexShader(ID3D11Device* self, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11VertexShader** ppVertexShader) { + #if defined(__cplusplus) + return self->CreateVertexShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppVertexShader); + #else + return self->lpVtbl->CreateVertexShader(self, pShaderBytecode, BytecodeLength, pClassLinkage, ppVertexShader); + #endif +} + +static inline HRESULT _sg_d3d11_CreatePixelShader(ID3D11Device* self, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11PixelShader** ppPixelShader) { + #if defined(__cplusplus) + return self->CreatePixelShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppPixelShader); + #else + return self->lpVtbl->CreatePixelShader(self, pShaderBytecode, BytecodeLength, pClassLinkage, ppPixelShader); + #endif +} + +static inline HRESULT _sg_d3d11_CreateInputLayout(ID3D11Device* self, const D3D11_INPUT_ELEMENT_DESC* pInputElementDescs, UINT NumElements, const void* pShaderBytecodeWithInputSignature, SIZE_T BytecodeLength, ID3D11InputLayout **ppInputLayout) { + #if defined(__cplusplus) + return self->CreateInputLayout(pInputElementDescs, NumElements, pShaderBytecodeWithInputSignature, BytecodeLength, ppInputLayout); + #else + return self->lpVtbl->CreateInputLayout(self, pInputElementDescs, NumElements, pShaderBytecodeWithInputSignature, BytecodeLength, ppInputLayout); + #endif +} + +static inline HRESULT _sg_d3d11_CreateRasterizerState(ID3D11Device* self, const D3D11_RASTERIZER_DESC* pRasterizerDesc, ID3D11RasterizerState** ppRasterizerState) { + #if defined(__cplusplus) + return self->CreateRasterizerState(pRasterizerDesc, ppRasterizerState); + #else + return self->lpVtbl->CreateRasterizerState(self, pRasterizerDesc, ppRasterizerState); + #endif +} + +static inline HRESULT _sg_d3d11_CreateDepthStencilState(ID3D11Device* self, const D3D11_DEPTH_STENCIL_DESC* pDepthStencilDesc, ID3D11DepthStencilState** ppDepthStencilState) { + #if defined(__cplusplus) + return self->CreateDepthStencilState(pDepthStencilDesc, ppDepthStencilState); + #else + return self->lpVtbl->CreateDepthStencilState(self, pDepthStencilDesc, ppDepthStencilState); + #endif +} + +static inline HRESULT _sg_d3d11_CreateBlendState(ID3D11Device* self, const D3D11_BLEND_DESC* pBlendStateDesc, ID3D11BlendState** ppBlendState) { + #if defined(__cplusplus) + return self->CreateBlendState(pBlendStateDesc, ppBlendState); + #else + return self->lpVtbl->CreateBlendState(self, pBlendStateDesc, ppBlendState); + #endif +} + +static inline HRESULT _sg_d3d11_CreateRenderTargetView(ID3D11Device* self, ID3D11Resource *pResource, const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, ID3D11RenderTargetView** ppRTView) { + #if defined(__cplusplus) + return self->CreateRenderTargetView(pResource, pDesc, ppRTView); + #else + return self->lpVtbl->CreateRenderTargetView(self, pResource, pDesc, ppRTView); + #endif +} + +static inline HRESULT _sg_d3d11_CreateDepthStencilView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D11DepthStencilView** ppDepthStencilView) { + #if defined(__cplusplus) + return self->CreateDepthStencilView(pResource, pDesc, ppDepthStencilView); + #else + return self->lpVtbl->CreateDepthStencilView(self, pResource, pDesc, ppDepthStencilView); + #endif +} + +static inline void _sg_d3d11_RSSetViewports(ID3D11DeviceContext* self, UINT NumViewports, const D3D11_VIEWPORT* pViewports) { + #if defined(__cplusplus) + self->RSSetViewports(NumViewports, pViewports); + #else + self->lpVtbl->RSSetViewports(self, NumViewports, pViewports); + #endif +} + +static inline void _sg_d3d11_RSSetScissorRects(ID3D11DeviceContext* self, UINT NumRects, const D3D11_RECT* pRects) { + #if defined(__cplusplus) + self->RSSetScissorRects(NumRects, pRects); + #else + self->lpVtbl->RSSetScissorRects(self, NumRects, pRects); + #endif +} + +static inline void _sg_d3d11_ClearRenderTargetView(ID3D11DeviceContext* self, ID3D11RenderTargetView* pRenderTargetView, const FLOAT ColorRGBA[4]) { + #if defined(__cplusplus) + self->ClearRenderTargetView(pRenderTargetView, ColorRGBA); + #else + self->lpVtbl->ClearRenderTargetView(self, pRenderTargetView, ColorRGBA); + #endif +} + +static inline void _sg_d3d11_ClearDepthStencilView(ID3D11DeviceContext* self, ID3D11DepthStencilView* pDepthStencilView, UINT ClearFlags, FLOAT Depth, UINT8 Stencil) { + #if defined(__cplusplus) + self->ClearDepthStencilView(pDepthStencilView, ClearFlags, Depth, Stencil); + #else + self->lpVtbl->ClearDepthStencilView(self, pDepthStencilView, ClearFlags, Depth, Stencil); + #endif +} + +static inline void _sg_d3d11_ResolveSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, ID3D11Resource* pSrcResource, UINT SrcSubresource, DXGI_FORMAT Format) { + #if defined(__cplusplus) + self->ResolveSubresource(pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format); + #else + self->lpVtbl->ResolveSubresource(self, pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format); + #endif +} + +static inline void _sg_d3d11_IASetPrimitiveTopology(ID3D11DeviceContext* self, D3D11_PRIMITIVE_TOPOLOGY Topology) { + #if defined(__cplusplus) + self->IASetPrimitiveTopology(Topology); + #else + self->lpVtbl->IASetPrimitiveTopology(self, Topology); + #endif +} + +static inline void _sg_d3d11_UpdateSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch) { + #if defined(__cplusplus) + self->UpdateSubresource(pDstResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch); + #else + self->lpVtbl->UpdateSubresource(self, pDstResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch); + #endif +} + +static inline void _sg_d3d11_DrawIndexed(ID3D11DeviceContext* self, UINT IndexCount, UINT StartIndexLocation, INT BaseVertexLocation) { + #if defined(__cplusplus) + self->DrawIndexed(IndexCount, StartIndexLocation, BaseVertexLocation); + #else + self->lpVtbl->DrawIndexed(self, IndexCount, StartIndexLocation, BaseVertexLocation); + #endif +} + +static inline void _sg_d3d11_DrawIndexedInstanced(ID3D11DeviceContext* self, UINT IndexCountPerInstance, UINT InstanceCount, UINT StartIndexLocation, INT BaseVertexLocation, UINT StartInstanceLocation) { + #if defined(__cplusplus) + self->DrawIndexedInstanced(IndexCountPerInstance, InstanceCount, StartIndexLocation, BaseVertexLocation, StartInstanceLocation); + #else + self->lpVtbl->DrawIndexedInstanced(self, IndexCountPerInstance, InstanceCount, StartIndexLocation, BaseVertexLocation, StartInstanceLocation); + #endif +} + +static inline void _sg_d3d11_Draw(ID3D11DeviceContext* self, UINT VertexCount, UINT StartVertexLocation) { + #if defined(__cplusplus) + self->Draw(VertexCount, StartVertexLocation); + #else + self->lpVtbl->Draw(self, VertexCount, StartVertexLocation); + #endif +} + +static inline void _sg_d3d11_DrawInstanced(ID3D11DeviceContext* self, UINT VertexCountPerInstance, UINT InstanceCount, UINT StartVertexLocation, UINT StartInstanceLocation) { + #if defined(__cplusplus) + self->DrawInstanced(VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation); + #else + self->lpVtbl->DrawInstanced(self, VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation); + #endif +} + +static inline HRESULT _sg_d3d11_Map(ID3D11DeviceContext* self, ID3D11Resource* pResource, UINT Subresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource) { + #if defined(__cplusplus) + return self->Map(pResource, Subresource, MapType, MapFlags, pMappedResource); + #else + return self->lpVtbl->Map(self, pResource, Subresource, MapType, MapFlags, pMappedResource); + #endif +} + +static inline void _sg_d3d11_Unmap(ID3D11DeviceContext* self, ID3D11Resource* pResource, UINT Subresource) { + #if defined(__cplusplus) + self->Unmap(pResource, Subresource); + #else + self->lpVtbl->Unmap(self, pResource, Subresource); + #endif +} + +static inline void _sg_d3d11_ClearState(ID3D11DeviceContext* self) { + #if defined(__cplusplus) + self->ClearState(); + #else + self->lpVtbl->ClearState(self); + #endif +} + +//-- enum translation functions ------------------------------------------------ +_SOKOL_PRIVATE D3D11_USAGE _sg_d3d11_usage(sg_usage usg) { + switch (usg) { + case SG_USAGE_IMMUTABLE: + return D3D11_USAGE_IMMUTABLE; + case SG_USAGE_DYNAMIC: + case SG_USAGE_STREAM: + return D3D11_USAGE_DYNAMIC; + default: + SOKOL_UNREACHABLE; + return (D3D11_USAGE) 0; + } +} + +_SOKOL_PRIVATE UINT _sg_d3d11_buffer_bind_flags(sg_buffer_type t) { + switch (t) { + case SG_BUFFERTYPE_VERTEXBUFFER: + return D3D11_BIND_VERTEX_BUFFER; + case SG_BUFFERTYPE_INDEXBUFFER: + return D3D11_BIND_INDEX_BUFFER; + case SG_BUFFERTYPE_STORAGEBUFFER: + // FIXME: for compute shaders we'd want UNORDERED_ACCESS? + return D3D11_BIND_SHADER_RESOURCE; + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE UINT _sg_d3d11_buffer_misc_flags(sg_buffer_type t) { + switch (t) { + case SG_BUFFERTYPE_VERTEXBUFFER: + case SG_BUFFERTYPE_INDEXBUFFER: + return 0; + case SG_BUFFERTYPE_STORAGEBUFFER: + return D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS; + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE UINT _sg_d3d11_cpu_access_flags(sg_usage usg) { + switch (usg) { + case SG_USAGE_IMMUTABLE: + return 0; + case SG_USAGE_DYNAMIC: + case SG_USAGE_STREAM: + return D3D11_CPU_ACCESS_WRITE; + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_texture_pixel_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: return DXGI_FORMAT_R8_UNORM; + case SG_PIXELFORMAT_R8SN: return DXGI_FORMAT_R8_SNORM; + case SG_PIXELFORMAT_R8UI: return DXGI_FORMAT_R8_UINT; + case SG_PIXELFORMAT_R8SI: return DXGI_FORMAT_R8_SINT; + case SG_PIXELFORMAT_R16: return DXGI_FORMAT_R16_UNORM; + case SG_PIXELFORMAT_R16SN: return DXGI_FORMAT_R16_SNORM; + case SG_PIXELFORMAT_R16UI: return DXGI_FORMAT_R16_UINT; + case SG_PIXELFORMAT_R16SI: return DXGI_FORMAT_R16_SINT; + case SG_PIXELFORMAT_R16F: return DXGI_FORMAT_R16_FLOAT; + case SG_PIXELFORMAT_RG8: return DXGI_FORMAT_R8G8_UNORM; + case SG_PIXELFORMAT_RG8SN: return DXGI_FORMAT_R8G8_SNORM; + case SG_PIXELFORMAT_RG8UI: return DXGI_FORMAT_R8G8_UINT; + case SG_PIXELFORMAT_RG8SI: return DXGI_FORMAT_R8G8_SINT; + case SG_PIXELFORMAT_R32UI: return DXGI_FORMAT_R32_UINT; + case SG_PIXELFORMAT_R32SI: return DXGI_FORMAT_R32_SINT; + case SG_PIXELFORMAT_R32F: return DXGI_FORMAT_R32_FLOAT; + case SG_PIXELFORMAT_RG16: return DXGI_FORMAT_R16G16_UNORM; + case SG_PIXELFORMAT_RG16SN: return DXGI_FORMAT_R16G16_SNORM; + case SG_PIXELFORMAT_RG16UI: return DXGI_FORMAT_R16G16_UINT; + case SG_PIXELFORMAT_RG16SI: return DXGI_FORMAT_R16G16_SINT; + case SG_PIXELFORMAT_RG16F: return DXGI_FORMAT_R16G16_FLOAT; + case SG_PIXELFORMAT_RGBA8: return DXGI_FORMAT_R8G8B8A8_UNORM; + case SG_PIXELFORMAT_SRGB8A8: return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + case SG_PIXELFORMAT_RGBA8SN: return DXGI_FORMAT_R8G8B8A8_SNORM; + case SG_PIXELFORMAT_RGBA8UI: return DXGI_FORMAT_R8G8B8A8_UINT; + case SG_PIXELFORMAT_RGBA8SI: return DXGI_FORMAT_R8G8B8A8_SINT; + case SG_PIXELFORMAT_BGRA8: return DXGI_FORMAT_B8G8R8A8_UNORM; + case SG_PIXELFORMAT_RGB10A2: return DXGI_FORMAT_R10G10B10A2_UNORM; + case SG_PIXELFORMAT_RG11B10F: return DXGI_FORMAT_R11G11B10_FLOAT; + case SG_PIXELFORMAT_RGB9E5: return DXGI_FORMAT_R9G9B9E5_SHAREDEXP; + case SG_PIXELFORMAT_RG32UI: return DXGI_FORMAT_R32G32_UINT; + case SG_PIXELFORMAT_RG32SI: return DXGI_FORMAT_R32G32_SINT; + case SG_PIXELFORMAT_RG32F: return DXGI_FORMAT_R32G32_FLOAT; + case SG_PIXELFORMAT_RGBA16: return DXGI_FORMAT_R16G16B16A16_UNORM; + case SG_PIXELFORMAT_RGBA16SN: return DXGI_FORMAT_R16G16B16A16_SNORM; + case SG_PIXELFORMAT_RGBA16UI: return DXGI_FORMAT_R16G16B16A16_UINT; + case SG_PIXELFORMAT_RGBA16SI: return DXGI_FORMAT_R16G16B16A16_SINT; + case SG_PIXELFORMAT_RGBA16F: return DXGI_FORMAT_R16G16B16A16_FLOAT; + case SG_PIXELFORMAT_RGBA32UI: return DXGI_FORMAT_R32G32B32A32_UINT; + case SG_PIXELFORMAT_RGBA32SI: return DXGI_FORMAT_R32G32B32A32_SINT; + case SG_PIXELFORMAT_RGBA32F: return DXGI_FORMAT_R32G32B32A32_FLOAT; + case SG_PIXELFORMAT_DEPTH: return DXGI_FORMAT_R32_TYPELESS; + case SG_PIXELFORMAT_DEPTH_STENCIL: return DXGI_FORMAT_R24G8_TYPELESS; + case SG_PIXELFORMAT_BC1_RGBA: return DXGI_FORMAT_BC1_UNORM; + case SG_PIXELFORMAT_BC2_RGBA: return DXGI_FORMAT_BC2_UNORM; + case SG_PIXELFORMAT_BC3_RGBA: return DXGI_FORMAT_BC3_UNORM; + case SG_PIXELFORMAT_BC3_SRGBA: return DXGI_FORMAT_BC3_UNORM_SRGB; + case SG_PIXELFORMAT_BC4_R: return DXGI_FORMAT_BC4_UNORM; + case SG_PIXELFORMAT_BC4_RSN: return DXGI_FORMAT_BC4_SNORM; + case SG_PIXELFORMAT_BC5_RG: return DXGI_FORMAT_BC5_UNORM; + case SG_PIXELFORMAT_BC5_RGSN: return DXGI_FORMAT_BC5_SNORM; + case SG_PIXELFORMAT_BC6H_RGBF: return DXGI_FORMAT_BC6H_SF16; + case SG_PIXELFORMAT_BC6H_RGBUF: return DXGI_FORMAT_BC6H_UF16; + case SG_PIXELFORMAT_BC7_RGBA: return DXGI_FORMAT_BC7_UNORM; + case SG_PIXELFORMAT_BC7_SRGBA: return DXGI_FORMAT_BC7_UNORM_SRGB; + default: return DXGI_FORMAT_UNKNOWN; + }; +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_srv_pixel_format(sg_pixel_format fmt) { + if (fmt == SG_PIXELFORMAT_DEPTH) { + return DXGI_FORMAT_R32_FLOAT; + } else if (fmt == SG_PIXELFORMAT_DEPTH_STENCIL) { + return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; + } else { + return _sg_d3d11_texture_pixel_format(fmt); + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_dsv_pixel_format(sg_pixel_format fmt) { + if (fmt == SG_PIXELFORMAT_DEPTH) { + return DXGI_FORMAT_D32_FLOAT; + } else if (fmt == SG_PIXELFORMAT_DEPTH_STENCIL) { + return DXGI_FORMAT_D24_UNORM_S8_UINT; + } else { + return _sg_d3d11_texture_pixel_format(fmt); + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_rtv_pixel_format(sg_pixel_format fmt) { + if (fmt == SG_PIXELFORMAT_DEPTH) { + return DXGI_FORMAT_R32_FLOAT; + } else if (fmt == SG_PIXELFORMAT_DEPTH_STENCIL) { + return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; + } else { + return _sg_d3d11_texture_pixel_format(fmt); + } +} + +_SOKOL_PRIVATE D3D11_PRIMITIVE_TOPOLOGY _sg_d3d11_primitive_topology(sg_primitive_type prim_type) { + switch (prim_type) { + case SG_PRIMITIVETYPE_POINTS: return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; + case SG_PRIMITIVETYPE_LINES: return D3D11_PRIMITIVE_TOPOLOGY_LINELIST; + case SG_PRIMITIVETYPE_LINE_STRIP: return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP; + case SG_PRIMITIVETYPE_TRIANGLES: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; + default: SOKOL_UNREACHABLE; return (D3D11_PRIMITIVE_TOPOLOGY) 0; + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_index_format(sg_index_type index_type) { + switch (index_type) { + case SG_INDEXTYPE_NONE: return DXGI_FORMAT_UNKNOWN; + case SG_INDEXTYPE_UINT16: return DXGI_FORMAT_R16_UINT; + case SG_INDEXTYPE_UINT32: return DXGI_FORMAT_R32_UINT; + default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0; + } +} + +_SOKOL_PRIVATE D3D11_FILTER _sg_d3d11_filter(sg_filter min_f, sg_filter mag_f, sg_filter mipmap_f, bool comparison, uint32_t max_anisotropy) { + uint32_t d3d11_filter = 0; + if (max_anisotropy > 1) { + // D3D11_FILTER_ANISOTROPIC = 0x55, + d3d11_filter |= 0x55; + } else { + // D3D11_FILTER_MIN_MAG_MIP_POINT = 0, + // D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR = 0x1, + // D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT = 0x4, + // D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR = 0x5, + // D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT = 0x10, + // D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 0x11, + // D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT = 0x14, + // D3D11_FILTER_MIN_MAG_MIP_LINEAR = 0x15, + if (mipmap_f == SG_FILTER_LINEAR) { + d3d11_filter |= 0x01; + } + if (mag_f == SG_FILTER_LINEAR) { + d3d11_filter |= 0x04; + } + if (min_f == SG_FILTER_LINEAR) { + d3d11_filter |= 0x10; + } + } + // D3D11_FILTER_COMPARISON_MIN_MAG_MIP_POINT = 0x80, + // D3D11_FILTER_COMPARISON_MIN_MAG_POINT_MIP_LINEAR = 0x81, + // D3D11_FILTER_COMPARISON_MIN_POINT_MAG_LINEAR_MIP_POINT = 0x84, + // D3D11_FILTER_COMPARISON_MIN_POINT_MAG_MIP_LINEAR = 0x85, + // D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_MIP_POINT = 0x90, + // D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 0x91, + // D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT = 0x94, + // D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR = 0x95, + // D3D11_FILTER_COMPARISON_ANISOTROPIC = 0xd5, + if (comparison) { + d3d11_filter |= 0x80; + } + return (D3D11_FILTER)d3d11_filter; +} + +_SOKOL_PRIVATE D3D11_TEXTURE_ADDRESS_MODE _sg_d3d11_address_mode(sg_wrap m) { + switch (m) { + case SG_WRAP_REPEAT: return D3D11_TEXTURE_ADDRESS_WRAP; + case SG_WRAP_CLAMP_TO_EDGE: return D3D11_TEXTURE_ADDRESS_CLAMP; + case SG_WRAP_CLAMP_TO_BORDER: return D3D11_TEXTURE_ADDRESS_BORDER; + case SG_WRAP_MIRRORED_REPEAT: return D3D11_TEXTURE_ADDRESS_MIRROR; + default: SOKOL_UNREACHABLE; return (D3D11_TEXTURE_ADDRESS_MODE) 0; + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_vertex_format(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return DXGI_FORMAT_R32_FLOAT; + case SG_VERTEXFORMAT_FLOAT2: return DXGI_FORMAT_R32G32_FLOAT; + case SG_VERTEXFORMAT_FLOAT3: return DXGI_FORMAT_R32G32B32_FLOAT; + case SG_VERTEXFORMAT_FLOAT4: return DXGI_FORMAT_R32G32B32A32_FLOAT; + case SG_VERTEXFORMAT_BYTE4: return DXGI_FORMAT_R8G8B8A8_SINT; + case SG_VERTEXFORMAT_BYTE4N: return DXGI_FORMAT_R8G8B8A8_SNORM; + case SG_VERTEXFORMAT_UBYTE4: return DXGI_FORMAT_R8G8B8A8_UINT; + case SG_VERTEXFORMAT_UBYTE4N: return DXGI_FORMAT_R8G8B8A8_UNORM; + case SG_VERTEXFORMAT_SHORT2: return DXGI_FORMAT_R16G16_SINT; + case SG_VERTEXFORMAT_SHORT2N: return DXGI_FORMAT_R16G16_SNORM; + case SG_VERTEXFORMAT_USHORT2N: return DXGI_FORMAT_R16G16_UNORM; + case SG_VERTEXFORMAT_SHORT4: return DXGI_FORMAT_R16G16B16A16_SINT; + case SG_VERTEXFORMAT_SHORT4N: return DXGI_FORMAT_R16G16B16A16_SNORM; + case SG_VERTEXFORMAT_USHORT4N: return DXGI_FORMAT_R16G16B16A16_UNORM; + case SG_VERTEXFORMAT_UINT10_N2: return DXGI_FORMAT_R10G10B10A2_UNORM; + case SG_VERTEXFORMAT_HALF2: return DXGI_FORMAT_R16G16_FLOAT; + case SG_VERTEXFORMAT_HALF4: return DXGI_FORMAT_R16G16B16A16_FLOAT; + default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0; + } +} + +_SOKOL_PRIVATE D3D11_INPUT_CLASSIFICATION _sg_d3d11_input_classification(sg_vertex_step step) { + switch (step) { + case SG_VERTEXSTEP_PER_VERTEX: return D3D11_INPUT_PER_VERTEX_DATA; + case SG_VERTEXSTEP_PER_INSTANCE: return D3D11_INPUT_PER_INSTANCE_DATA; + default: SOKOL_UNREACHABLE; return (D3D11_INPUT_CLASSIFICATION) 0; + } +} + +_SOKOL_PRIVATE D3D11_CULL_MODE _sg_d3d11_cull_mode(sg_cull_mode m) { + switch (m) { + case SG_CULLMODE_NONE: return D3D11_CULL_NONE; + case SG_CULLMODE_FRONT: return D3D11_CULL_FRONT; + case SG_CULLMODE_BACK: return D3D11_CULL_BACK; + default: SOKOL_UNREACHABLE; return (D3D11_CULL_MODE) 0; + } +} + +_SOKOL_PRIVATE D3D11_COMPARISON_FUNC _sg_d3d11_compare_func(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return D3D11_COMPARISON_NEVER; + case SG_COMPAREFUNC_LESS: return D3D11_COMPARISON_LESS; + case SG_COMPAREFUNC_EQUAL: return D3D11_COMPARISON_EQUAL; + case SG_COMPAREFUNC_LESS_EQUAL: return D3D11_COMPARISON_LESS_EQUAL; + case SG_COMPAREFUNC_GREATER: return D3D11_COMPARISON_GREATER; + case SG_COMPAREFUNC_NOT_EQUAL: return D3D11_COMPARISON_NOT_EQUAL; + case SG_COMPAREFUNC_GREATER_EQUAL: return D3D11_COMPARISON_GREATER_EQUAL; + case SG_COMPAREFUNC_ALWAYS: return D3D11_COMPARISON_ALWAYS; + default: SOKOL_UNREACHABLE; return (D3D11_COMPARISON_FUNC) 0; + } +} + +_SOKOL_PRIVATE D3D11_STENCIL_OP _sg_d3d11_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return D3D11_STENCIL_OP_KEEP; + case SG_STENCILOP_ZERO: return D3D11_STENCIL_OP_ZERO; + case SG_STENCILOP_REPLACE: return D3D11_STENCIL_OP_REPLACE; + case SG_STENCILOP_INCR_CLAMP: return D3D11_STENCIL_OP_INCR_SAT; + case SG_STENCILOP_DECR_CLAMP: return D3D11_STENCIL_OP_DECR_SAT; + case SG_STENCILOP_INVERT: return D3D11_STENCIL_OP_INVERT; + case SG_STENCILOP_INCR_WRAP: return D3D11_STENCIL_OP_INCR; + case SG_STENCILOP_DECR_WRAP: return D3D11_STENCIL_OP_DECR; + default: SOKOL_UNREACHABLE; return (D3D11_STENCIL_OP) 0; + } +} + +_SOKOL_PRIVATE D3D11_BLEND _sg_d3d11_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return D3D11_BLEND_ZERO; + case SG_BLENDFACTOR_ONE: return D3D11_BLEND_ONE; + case SG_BLENDFACTOR_SRC_COLOR: return D3D11_BLEND_SRC_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return D3D11_BLEND_INV_SRC_COLOR; + case SG_BLENDFACTOR_SRC_ALPHA: return D3D11_BLEND_SRC_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return D3D11_BLEND_INV_SRC_ALPHA; + case SG_BLENDFACTOR_DST_COLOR: return D3D11_BLEND_DEST_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return D3D11_BLEND_INV_DEST_COLOR; + case SG_BLENDFACTOR_DST_ALPHA: return D3D11_BLEND_DEST_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return D3D11_BLEND_INV_DEST_ALPHA; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return D3D11_BLEND_SRC_ALPHA_SAT; + case SG_BLENDFACTOR_BLEND_COLOR: return D3D11_BLEND_BLEND_FACTOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return D3D11_BLEND_INV_BLEND_FACTOR; + case SG_BLENDFACTOR_BLEND_ALPHA: return D3D11_BLEND_BLEND_FACTOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return D3D11_BLEND_INV_BLEND_FACTOR; + default: SOKOL_UNREACHABLE; return (D3D11_BLEND) 0; + } +} + +_SOKOL_PRIVATE D3D11_BLEND_OP _sg_d3d11_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return D3D11_BLEND_OP_ADD; + case SG_BLENDOP_SUBTRACT: return D3D11_BLEND_OP_SUBTRACT; + case SG_BLENDOP_REVERSE_SUBTRACT: return D3D11_BLEND_OP_REV_SUBTRACT; + default: SOKOL_UNREACHABLE; return (D3D11_BLEND_OP) 0; + } +} + +_SOKOL_PRIVATE UINT8 _sg_d3d11_color_write_mask(sg_color_mask m) { + UINT8 res = 0; + if (m & SG_COLORMASK_R) { + res |= D3D11_COLOR_WRITE_ENABLE_RED; + } + if (m & SG_COLORMASK_G) { + res |= D3D11_COLOR_WRITE_ENABLE_GREEN; + } + if (m & SG_COLORMASK_B) { + res |= D3D11_COLOR_WRITE_ENABLE_BLUE; + } + if (m & SG_COLORMASK_A) { + res |= D3D11_COLOR_WRITE_ENABLE_ALPHA; + } + return res; +} + +_SOKOL_PRIVATE UINT _sg_d3d11_dxgi_fmt_caps(DXGI_FORMAT dxgi_fmt) { + UINT dxgi_fmt_caps = 0; + if (dxgi_fmt != DXGI_FORMAT_UNKNOWN) { + HRESULT hr = _sg_d3d11_CheckFormatSupport(_sg.d3d11.dev, dxgi_fmt, &dxgi_fmt_caps); + SOKOL_ASSERT(SUCCEEDED(hr) || (E_FAIL == hr)); + if (!SUCCEEDED(hr)) { + dxgi_fmt_caps = 0; + } + } + return dxgi_fmt_caps; +} + +// see: https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-limits#resource-limits-for-feature-level-11-hardware +_SOKOL_PRIVATE void _sg_d3d11_init_caps(void) { + _sg.backend = SG_BACKEND_D3D11; + + _sg.features.origin_top_left = true; + _sg.features.image_clamp_to_border = true; + _sg.features.mrt_independent_blend_state = true; + _sg.features.mrt_independent_write_mask = true; + _sg.features.storage_buffer = true; + + _sg.limits.max_image_size_2d = 16 * 1024; + _sg.limits.max_image_size_cube = 16 * 1024; + _sg.limits.max_image_size_3d = 2 * 1024; + _sg.limits.max_image_size_array = 16 * 1024; + _sg.limits.max_image_array_layers = 2 * 1024; + _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; + + // see: https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_format_support + for (int fmt = (SG_PIXELFORMAT_NONE+1); fmt < _SG_PIXELFORMAT_NUM; fmt++) { + const UINT srv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_srv_pixel_format((sg_pixel_format)fmt)); + const UINT rtv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_rtv_pixel_format((sg_pixel_format)fmt)); + const UINT dsv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_dsv_pixel_format((sg_pixel_format)fmt)); + _sg_pixelformat_info_t* info = &_sg.formats[fmt]; + const bool render = 0 != (rtv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_RENDER_TARGET); + const bool depth = 0 != (dsv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL); + info->sample = 0 != (srv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_TEXTURE2D); + info->filter = 0 != (srv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_SHADER_SAMPLE); + info->render = render || depth; + info->blend = 0 != (rtv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_BLENDABLE); + info->msaa = 0 != (rtv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET); + info->depth = depth; + } +} + +_SOKOL_PRIVATE void _sg_d3d11_setup_backend(const sg_desc* desc) { + // assume _sg.d3d11 already is zero-initialized + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->environment.d3d11.device); + SOKOL_ASSERT(desc->environment.d3d11.device_context); + _sg.d3d11.valid = true; + _sg.d3d11.dev = (ID3D11Device*) desc->environment.d3d11.device; + _sg.d3d11.ctx = (ID3D11DeviceContext*) desc->environment.d3d11.device_context; + _sg_d3d11_init_caps(); +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_backend(void) { + SOKOL_ASSERT(_sg.d3d11.valid); + _sg.d3d11.valid = false; +} + +_SOKOL_PRIVATE void _sg_d3d11_clear_state(void) { + // clear all the device context state, so that resource refs don't keep stuck in the d3d device context + _sg_d3d11_ClearState(_sg.d3d11.ctx); +} + +_SOKOL_PRIVATE void _sg_d3d11_reset_state_cache(void) { + // just clear the d3d11 device context state + _sg_d3d11_clear_state(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + SOKOL_ASSERT(!buf->d3d11.buf); + const bool injected = (0 != desc->d3d11_buffer); + if (injected) { + buf->d3d11.buf = (ID3D11Buffer*) desc->d3d11_buffer; + _sg_d3d11_AddRef(buf->d3d11.buf); + // FIXME: for storage buffers also need to inject resource view + } else { + D3D11_BUFFER_DESC d3d11_buf_desc; + _sg_clear(&d3d11_buf_desc, sizeof(d3d11_buf_desc)); + d3d11_buf_desc.ByteWidth = (UINT)buf->cmn.size; + d3d11_buf_desc.Usage = _sg_d3d11_usage(buf->cmn.usage); + d3d11_buf_desc.BindFlags = _sg_d3d11_buffer_bind_flags(buf->cmn.type); + d3d11_buf_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(buf->cmn.usage); + d3d11_buf_desc.MiscFlags = _sg_d3d11_buffer_misc_flags(buf->cmn.type); + D3D11_SUBRESOURCE_DATA* init_data_ptr = 0; + D3D11_SUBRESOURCE_DATA init_data; + _sg_clear(&init_data, sizeof(init_data)); + if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { + SOKOL_ASSERT(desc->data.ptr); + init_data.pSysMem = desc->data.ptr; + init_data_ptr = &init_data; + } + HRESULT hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &d3d11_buf_desc, init_data_ptr, &buf->d3d11.buf); + if (!(SUCCEEDED(hr) && buf->d3d11.buf)) { + _SG_ERROR(D3D11_CREATE_BUFFER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + + // for storage buffers need to create a view object + if (buf->cmn.type == SG_BUFFERTYPE_STORAGEBUFFER) { + // FIXME: currently only shader-resource-view, in future also UAV + // storage buffer size must be multiple of 4 + SOKOL_ASSERT(_sg_multiple_u64(buf->cmn.size, 4)); + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = DXGI_FORMAT_R32_TYPELESS; + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX; + d3d11_srv_desc.BufferEx.FirstElement = 0; + d3d11_srv_desc.BufferEx.NumElements = buf->cmn.size / 4; + d3d11_srv_desc.BufferEx.Flags = D3D11_BUFFEREX_SRV_FLAG_RAW; + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)buf->d3d11.buf, &d3d11_srv_desc, &buf->d3d11.srv); + if (!(SUCCEEDED(hr) && buf->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_BUFFER_SRV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + _sg_d3d11_setlabel(buf->d3d11.buf, desc->label); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + if (buf->d3d11.buf) { + _sg_d3d11_Release(buf->d3d11.buf); + } + if (buf->d3d11.srv) { + _sg_d3d11_Release(buf->d3d11.srv); + } +} + +_SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_image_data* data) { + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices:1; + int subres_index = 0; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int slice_index = 0; slice_index < num_slices; slice_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) { + SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); + D3D11_SUBRESOURCE_DATA* subres_data = &_sg.d3d11.subres_data[subres_index]; + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + const sg_range* subimg_data = &(data->subimage[face_index][mip_index]); + const size_t slice_size = subimg_data->size / (size_t)num_slices; + const size_t slice_offset = slice_size * (size_t)slice_index; + const uint8_t* ptr = (const uint8_t*) subimg_data->ptr; + subres_data->pSysMem = ptr + slice_offset; + subres_data->SysMemPitch = (UINT)_sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + if (img->cmn.type == SG_IMAGETYPE_3D) { + // FIXME? const int mip_depth = _sg_miplevel_dim(img->depth, mip_index); + subres_data->SysMemSlicePitch = (UINT)_sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + } else { + subres_data->SysMemSlicePitch = 0; + } + } + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + SOKOL_ASSERT((0 == img->d3d11.tex2d) && (0 == img->d3d11.tex3d) && (0 == img->d3d11.res) && (0 == img->d3d11.srv)); + HRESULT hr; + + const bool injected = (0 != desc->d3d11_texture); + const bool msaa = (img->cmn.sample_count > 1); + img->d3d11.format = _sg_d3d11_texture_pixel_format(img->cmn.pixel_format); + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { + _SG_ERROR(D3D11_CREATE_2D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT); + return SG_RESOURCESTATE_FAILED; + } + + // prepare initial content pointers + D3D11_SUBRESOURCE_DATA* init_data = 0; + if (!injected && (img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { + _sg_d3d11_fill_subres_data(img, &desc->data); + init_data = _sg.d3d11.subres_data; + } + if (img->cmn.type != SG_IMAGETYPE_3D) { + // 2D-, cube- or array-texture + // first check for injected texture and/or resource view + if (injected) { + img->d3d11.tex2d = (ID3D11Texture2D*) desc->d3d11_texture; + _sg_d3d11_AddRef(img->d3d11.tex2d); + img->d3d11.srv = (ID3D11ShaderResourceView*) desc->d3d11_shader_resource_view; + if (img->d3d11.srv) { + _sg_d3d11_AddRef(img->d3d11.srv); + } + } else { + // if not injected, create 2D texture + D3D11_TEXTURE2D_DESC d3d11_tex_desc; + _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); + d3d11_tex_desc.Width = (UINT)img->cmn.width; + d3d11_tex_desc.Height = (UINT)img->cmn.height; + d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; + switch (img->cmn.type) { + case SG_IMAGETYPE_ARRAY: d3d11_tex_desc.ArraySize = (UINT)img->cmn.num_slices; break; + case SG_IMAGETYPE_CUBE: d3d11_tex_desc.ArraySize = 6; break; + default: d3d11_tex_desc.ArraySize = 1; break; + } + d3d11_tex_desc.Format = img->d3d11.format; + if (img->cmn.render_target) { + d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; + if (_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)) { + d3d11_tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + } else { + d3d11_tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; + } + if (!msaa) { + d3d11_tex_desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE; + } + d3d11_tex_desc.CPUAccessFlags = 0; + } else { + d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); + d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); + } + d3d11_tex_desc.SampleDesc.Count = (UINT)img->cmn.sample_count; + d3d11_tex_desc.SampleDesc.Quality = (UINT) (msaa ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0); + d3d11_tex_desc.MiscFlags = (img->cmn.type == SG_IMAGETYPE_CUBE) ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; + + hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex2d); + if (!(SUCCEEDED(hr) && img->d3d11.tex2d)) { + _SG_ERROR(D3D11_CREATE_2D_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(img->d3d11.tex2d, desc->label); + + // create shader-resource-view for 2D texture + // FIXME: currently we don't support setting MSAA texture as shader resource + if (!msaa) { + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = _sg_d3d11_srv_pixel_format(img->cmn.pixel_format); + switch (img->cmn.type) { + case SG_IMAGETYPE_2D: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + d3d11_srv_desc.Texture2D.MipLevels = (UINT)img->cmn.num_mipmaps; + break; + case SG_IMAGETYPE_CUBE: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + d3d11_srv_desc.TextureCube.MipLevels = (UINT)img->cmn.num_mipmaps; + break; + case SG_IMAGETYPE_ARRAY: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + d3d11_srv_desc.Texture2DArray.MipLevels = (UINT)img->cmn.num_mipmaps; + d3d11_srv_desc.Texture2DArray.ArraySize = (UINT)img->cmn.num_slices; + break; + default: + SOKOL_UNREACHABLE; break; + } + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex2d, &d3d11_srv_desc, &img->d3d11.srv); + if (!(SUCCEEDED(hr) && img->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_2D_SRV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(img->d3d11.srv, desc->label); + } + } + SOKOL_ASSERT(img->d3d11.tex2d); + img->d3d11.res = (ID3D11Resource*)img->d3d11.tex2d; + _sg_d3d11_AddRef(img->d3d11.res); + } else { + // 3D texture - same procedure, first check if injected, than create non-injected + if (injected) { + img->d3d11.tex3d = (ID3D11Texture3D*) desc->d3d11_texture; + _sg_d3d11_AddRef(img->d3d11.tex3d); + img->d3d11.srv = (ID3D11ShaderResourceView*) desc->d3d11_shader_resource_view; + if (img->d3d11.srv) { + _sg_d3d11_AddRef(img->d3d11.srv); + } + } else { + // not injected, create 3d texture + D3D11_TEXTURE3D_DESC d3d11_tex_desc; + _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); + d3d11_tex_desc.Width = (UINT)img->cmn.width; + d3d11_tex_desc.Height = (UINT)img->cmn.height; + d3d11_tex_desc.Depth = (UINT)img->cmn.num_slices; + d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; + d3d11_tex_desc.Format = img->d3d11.format; + if (img->cmn.render_target) { + d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; + d3d11_tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; + d3d11_tex_desc.CPUAccessFlags = 0; + } else { + d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); + d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); + } + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { + _SG_ERROR(D3D11_CREATE_3D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT); + return SG_RESOURCESTATE_FAILED; + } + hr = _sg_d3d11_CreateTexture3D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex3d); + if (!(SUCCEEDED(hr) && img->d3d11.tex3d)) { + _SG_ERROR(D3D11_CREATE_3D_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(img->d3d11.tex3d, desc->label); + + // create shader-resource-view for 3D texture + if (!msaa) { + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = _sg_d3d11_srv_pixel_format(img->cmn.pixel_format); + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + d3d11_srv_desc.Texture3D.MipLevels = (UINT)img->cmn.num_mipmaps; + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex3d, &d3d11_srv_desc, &img->d3d11.srv); + if (!(SUCCEEDED(hr) && img->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_3D_SRV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(img->d3d11.srv, desc->label); + } + } + SOKOL_ASSERT(img->d3d11.tex3d); + img->d3d11.res = (ID3D11Resource*)img->d3d11.tex3d; + _sg_d3d11_AddRef(img->d3d11.res); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + if (img->d3d11.tex2d) { + _sg_d3d11_Release(img->d3d11.tex2d); + } + if (img->d3d11.tex3d) { + _sg_d3d11_Release(img->d3d11.tex3d); + } + if (img->d3d11.res) { + _sg_d3d11_Release(img->d3d11.res); + } + if (img->d3d11.srv) { + _sg_d3d11_Release(img->d3d11.srv); + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + SOKOL_ASSERT(0 == smp->d3d11.smp); + const bool injected = (0 != desc->d3d11_sampler); + if (injected) { + smp->d3d11.smp = (ID3D11SamplerState*)desc->d3d11_sampler; + _sg_d3d11_AddRef(smp->d3d11.smp); + } else { + D3D11_SAMPLER_DESC d3d11_smp_desc; + _sg_clear(&d3d11_smp_desc, sizeof(d3d11_smp_desc)); + d3d11_smp_desc.Filter = _sg_d3d11_filter(desc->min_filter, desc->mag_filter, desc->mipmap_filter, desc->compare != SG_COMPAREFUNC_NEVER, desc->max_anisotropy); + d3d11_smp_desc.AddressU = _sg_d3d11_address_mode(desc->wrap_u); + d3d11_smp_desc.AddressV = _sg_d3d11_address_mode(desc->wrap_v); + d3d11_smp_desc.AddressW = _sg_d3d11_address_mode(desc->wrap_w); + d3d11_smp_desc.MipLODBias = 0.0f; // FIXME? + switch (desc->border_color) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: + // all 0.0f + break; + case SG_BORDERCOLOR_OPAQUE_WHITE: + for (int i = 0; i < 4; i++) { + d3d11_smp_desc.BorderColor[i] = 1.0f; + } + break; + default: + // opaque black + d3d11_smp_desc.BorderColor[3] = 1.0f; + break; + } + d3d11_smp_desc.MaxAnisotropy = desc->max_anisotropy; + d3d11_smp_desc.ComparisonFunc = _sg_d3d11_compare_func(desc->compare); + d3d11_smp_desc.MinLOD = desc->min_lod; + d3d11_smp_desc.MaxLOD = desc->max_lod; + HRESULT hr = _sg_d3d11_CreateSamplerState(_sg.d3d11.dev, &d3d11_smp_desc, &smp->d3d11.smp); + if (!(SUCCEEDED(hr) && smp->d3d11.smp)) { + _SG_ERROR(D3D11_CREATE_SAMPLER_STATE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(smp->d3d11.smp, desc->label); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + if (smp->d3d11.smp) { + _sg_d3d11_Release(smp->d3d11.smp); + } +} + +_SOKOL_PRIVATE bool _sg_d3d11_load_d3dcompiler_dll(void) { + if ((0 == _sg.d3d11.d3dcompiler_dll) && !_sg.d3d11.d3dcompiler_dll_load_failed) { + _sg.d3d11.d3dcompiler_dll = LoadLibraryA("d3dcompiler_47.dll"); + if (0 == _sg.d3d11.d3dcompiler_dll) { + // don't attempt to load missing DLL in the future + _SG_ERROR(D3D11_LOAD_D3DCOMPILER_47_DLL_FAILED); + _sg.d3d11.d3dcompiler_dll_load_failed = true; + return false; + } + // look up function pointers + _sg.d3d11.D3DCompile_func = (pD3DCompile)(void*) GetProcAddress(_sg.d3d11.d3dcompiler_dll, "D3DCompile"); + SOKOL_ASSERT(_sg.d3d11.D3DCompile_func); + } + return 0 != _sg.d3d11.d3dcompiler_dll; +} + +_SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_stage_desc* stage_desc) { + if (!_sg_d3d11_load_d3dcompiler_dll()) { + return NULL; + } + SOKOL_ASSERT(stage_desc->d3d11_target); + ID3DBlob* output = NULL; + ID3DBlob* errors_or_warnings = NULL; + HRESULT hr = _sg.d3d11.D3DCompile_func( + stage_desc->source, // pSrcData + strlen(stage_desc->source), // SrcDataSize + NULL, // pSourceName + NULL, // pDefines + NULL, // pInclude + stage_desc->entry ? stage_desc->entry : "main", // pEntryPoint + stage_desc->d3d11_target, // pTarget + D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR | D3DCOMPILE_OPTIMIZATION_LEVEL3, // Flags1 + 0, // Flags2 + &output, // ppCode + &errors_or_warnings); // ppErrorMsgs + if (FAILED(hr)) { + _SG_ERROR(D3D11_SHADER_COMPILATION_FAILED); + } + if (errors_or_warnings) { + _SG_WARN(D3D11_SHADER_COMPILATION_OUTPUT); + _SG_LOGMSG(D3D11_SHADER_COMPILATION_OUTPUT, (LPCSTR)_sg_d3d11_GetBufferPointer(errors_or_warnings)); + _sg_d3d11_Release(errors_or_warnings); errors_or_warnings = NULL; + } + if (FAILED(hr)) { + // just in case, usually output is NULL here + if (output) { + _sg_d3d11_Release(output); + output = NULL; + } + } + return output; +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(!shd->d3d11.vs && !shd->d3d11.fs && !shd->d3d11.vs_blob); + HRESULT hr; + + // copy vertex attribute semantic names and indices + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + _sg_strcpy(&shd->d3d11.attrs[i].sem_name, desc->attrs[i].sem_name); + shd->d3d11.attrs[i].sem_index = desc->attrs[i].sem_index; + } + + // shader stage uniform blocks and image slots + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; + _sg_d3d11_shader_stage_t* d3d11_stage = &shd->d3d11.stage[stage_index]; + for (int ub_index = 0; ub_index < cmn_stage->num_uniform_blocks; ub_index++) { + const _sg_shader_uniform_block_t* ub = &cmn_stage->uniform_blocks[ub_index]; + + // create a D3D constant buffer for each uniform block + SOKOL_ASSERT(0 == d3d11_stage->cbufs[ub_index]); + D3D11_BUFFER_DESC cb_desc; + _sg_clear(&cb_desc, sizeof(cb_desc)); + cb_desc.ByteWidth = (UINT)_sg_roundup((int)ub->size, 16); + cb_desc.Usage = D3D11_USAGE_DEFAULT; + cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &cb_desc, NULL, &d3d11_stage->cbufs[ub_index]); + if (!(SUCCEEDED(hr) && d3d11_stage->cbufs[ub_index])) { + _SG_ERROR(D3D11_CREATE_CONSTANT_BUFFER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(d3d11_stage->cbufs[ub_index], desc->label); + } + } + + const void* vs_ptr = 0, *fs_ptr = 0; + SIZE_T vs_length = 0, fs_length = 0; + ID3DBlob* vs_blob = 0, *fs_blob = 0; + if (desc->vs.bytecode.ptr && desc->fs.bytecode.ptr) { + // create from shader byte code + vs_ptr = desc->vs.bytecode.ptr; + fs_ptr = desc->fs.bytecode.ptr; + vs_length = desc->vs.bytecode.size; + fs_length = desc->fs.bytecode.size; + } else { + // compile from shader source code + vs_blob = _sg_d3d11_compile_shader(&desc->vs); + fs_blob = _sg_d3d11_compile_shader(&desc->fs); + if (vs_blob && fs_blob) { + vs_ptr = _sg_d3d11_GetBufferPointer(vs_blob); + vs_length = _sg_d3d11_GetBufferSize(vs_blob); + fs_ptr = _sg_d3d11_GetBufferPointer(fs_blob); + fs_length = _sg_d3d11_GetBufferSize(fs_blob); + } + } + sg_resource_state result = SG_RESOURCESTATE_FAILED; + if (vs_ptr && fs_ptr && (vs_length > 0) && (fs_length > 0)) { + // create the D3D vertex- and pixel-shader objects + hr = _sg_d3d11_CreateVertexShader(_sg.d3d11.dev, vs_ptr, vs_length, NULL, &shd->d3d11.vs); + bool vs_succeeded = SUCCEEDED(hr) && shd->d3d11.vs; + hr = _sg_d3d11_CreatePixelShader(_sg.d3d11.dev, fs_ptr, fs_length, NULL, &shd->d3d11.fs); + bool fs_succeeded = SUCCEEDED(hr) && shd->d3d11.fs; + + // need to store the vertex shader byte code, this is needed later in sg_create_pipeline + if (vs_succeeded && fs_succeeded) { + shd->d3d11.vs_blob_length = vs_length; + shd->d3d11.vs_blob = _sg_malloc((size_t)vs_length); + SOKOL_ASSERT(shd->d3d11.vs_blob); + memcpy(shd->d3d11.vs_blob, vs_ptr, vs_length); + result = SG_RESOURCESTATE_VALID; + _sg_d3d11_setlabel(shd->d3d11.vs, desc->label); + _sg_d3d11_setlabel(shd->d3d11.fs, desc->label); + } + } + if (vs_blob) { + _sg_d3d11_Release(vs_blob); vs_blob = 0; + } + if (fs_blob) { + _sg_d3d11_Release(fs_blob); fs_blob = 0; + } + return result; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + if (shd->d3d11.vs) { + _sg_d3d11_Release(shd->d3d11.vs); + } + if (shd->d3d11.fs) { + _sg_d3d11_Release(shd->d3d11.fs); + } + if (shd->d3d11.vs_blob) { + _sg_free(shd->d3d11.vs_blob); + } + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; + _sg_d3d11_shader_stage_t* d3d11_stage = &shd->d3d11.stage[stage_index]; + for (int ub_index = 0; ub_index < cmn_stage->num_uniform_blocks; ub_index++) { + if (d3d11_stage->cbufs[ub_index]) { + _sg_d3d11_Release(d3d11_stage->cbufs[ub_index]); + } + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(shd->d3d11.vs_blob && shd->d3d11.vs_blob_length > 0); + SOKOL_ASSERT(!pip->d3d11.il && !pip->d3d11.rs && !pip->d3d11.dss && !pip->d3d11.bs); + + pip->shader = shd; + pip->d3d11.index_format = _sg_d3d11_index_format(pip->cmn.index_type); + pip->d3d11.topology = _sg_d3d11_primitive_topology(desc->primitive_type); + pip->d3d11.stencil_ref = desc->stencil.ref; + + // create input layout object + HRESULT hr; + D3D11_INPUT_ELEMENT_DESC d3d11_comps[SG_MAX_VERTEX_ATTRIBUTES]; + _sg_clear(d3d11_comps, sizeof(d3d11_comps)); + int attr_index = 0; + for (; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[a_state->buffer_index]; + const sg_vertex_step step_func = l_state->step_func; + const int step_rate = l_state->step_rate; + D3D11_INPUT_ELEMENT_DESC* d3d11_comp = &d3d11_comps[attr_index]; + d3d11_comp->SemanticName = _sg_strptr(&shd->d3d11.attrs[attr_index].sem_name); + d3d11_comp->SemanticIndex = (UINT)shd->d3d11.attrs[attr_index].sem_index; + d3d11_comp->Format = _sg_d3d11_vertex_format(a_state->format); + d3d11_comp->InputSlot = (UINT)a_state->buffer_index; + d3d11_comp->AlignedByteOffset = (UINT)a_state->offset; + d3d11_comp->InputSlotClass = _sg_d3d11_input_classification(step_func); + if (SG_VERTEXSTEP_PER_INSTANCE == step_func) { + d3d11_comp->InstanceDataStepRate = (UINT)step_rate; + pip->cmn.use_instanced_draw = true; + } + pip->cmn.vertex_buffer_layout_active[a_state->buffer_index] = true; + } + for (int layout_index = 0; layout_index < SG_MAX_VERTEX_BUFFERS; layout_index++) { + if (pip->cmn.vertex_buffer_layout_active[layout_index]) { + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[layout_index]; + SOKOL_ASSERT(l_state->stride > 0); + pip->d3d11.vb_strides[layout_index] = (UINT)l_state->stride; + } else { + pip->d3d11.vb_strides[layout_index] = 0; + } + } + if (attr_index > 0) { + hr = _sg_d3d11_CreateInputLayout(_sg.d3d11.dev, + d3d11_comps, // pInputElementDesc + (UINT)attr_index, // NumElements + shd->d3d11.vs_blob, // pShaderByteCodeWithInputSignature + shd->d3d11.vs_blob_length, // BytecodeLength + &pip->d3d11.il); + if (!(SUCCEEDED(hr) && pip->d3d11.il)) { + _SG_ERROR(D3D11_CREATE_INPUT_LAYOUT_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(pip->d3d11.il, desc->label); + } + + // create rasterizer state + D3D11_RASTERIZER_DESC rs_desc; + _sg_clear(&rs_desc, sizeof(rs_desc)); + rs_desc.FillMode = D3D11_FILL_SOLID; + rs_desc.CullMode = _sg_d3d11_cull_mode(desc->cull_mode); + rs_desc.FrontCounterClockwise = desc->face_winding == SG_FACEWINDING_CCW; + rs_desc.DepthBias = (INT) pip->cmn.depth.bias; + rs_desc.DepthBiasClamp = pip->cmn.depth.bias_clamp; + rs_desc.SlopeScaledDepthBias = pip->cmn.depth.bias_slope_scale; + rs_desc.DepthClipEnable = TRUE; + rs_desc.ScissorEnable = TRUE; + rs_desc.MultisampleEnable = desc->sample_count > 1; + rs_desc.AntialiasedLineEnable = FALSE; + hr = _sg_d3d11_CreateRasterizerState(_sg.d3d11.dev, &rs_desc, &pip->d3d11.rs); + if (!(SUCCEEDED(hr) && pip->d3d11.rs)) { + _SG_ERROR(D3D11_CREATE_RASTERIZER_STATE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(pip->d3d11.rs, desc->label); + + // create depth-stencil state + D3D11_DEPTH_STENCIL_DESC dss_desc; + _sg_clear(&dss_desc, sizeof(dss_desc)); + dss_desc.DepthEnable = TRUE; + dss_desc.DepthWriteMask = desc->depth.write_enabled ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + dss_desc.DepthFunc = _sg_d3d11_compare_func(desc->depth.compare); + dss_desc.StencilEnable = desc->stencil.enabled; + dss_desc.StencilReadMask = desc->stencil.read_mask; + dss_desc.StencilWriteMask = desc->stencil.write_mask; + const sg_stencil_face_state* sf = &desc->stencil.front; + dss_desc.FrontFace.StencilFailOp = _sg_d3d11_stencil_op(sf->fail_op); + dss_desc.FrontFace.StencilDepthFailOp = _sg_d3d11_stencil_op(sf->depth_fail_op); + dss_desc.FrontFace.StencilPassOp = _sg_d3d11_stencil_op(sf->pass_op); + dss_desc.FrontFace.StencilFunc = _sg_d3d11_compare_func(sf->compare); + const sg_stencil_face_state* sb = &desc->stencil.back; + dss_desc.BackFace.StencilFailOp = _sg_d3d11_stencil_op(sb->fail_op); + dss_desc.BackFace.StencilDepthFailOp = _sg_d3d11_stencil_op(sb->depth_fail_op); + dss_desc.BackFace.StencilPassOp = _sg_d3d11_stencil_op(sb->pass_op); + dss_desc.BackFace.StencilFunc = _sg_d3d11_compare_func(sb->compare); + hr = _sg_d3d11_CreateDepthStencilState(_sg.d3d11.dev, &dss_desc, &pip->d3d11.dss); + if (!(SUCCEEDED(hr) && pip->d3d11.dss)) { + _SG_ERROR(D3D11_CREATE_DEPTH_STENCIL_STATE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(pip->d3d11.dss, desc->label); + + // create blend state + D3D11_BLEND_DESC bs_desc; + _sg_clear(&bs_desc, sizeof(bs_desc)); + bs_desc.AlphaToCoverageEnable = desc->alpha_to_coverage_enabled; + bs_desc.IndependentBlendEnable = TRUE; + { + int i = 0; + for (i = 0; i < desc->color_count; i++) { + const sg_blend_state* src = &desc->colors[i].blend; + D3D11_RENDER_TARGET_BLEND_DESC* dst = &bs_desc.RenderTarget[i]; + dst->BlendEnable = src->enabled; + dst->SrcBlend = _sg_d3d11_blend_factor(src->src_factor_rgb); + dst->DestBlend = _sg_d3d11_blend_factor(src->dst_factor_rgb); + dst->BlendOp = _sg_d3d11_blend_op(src->op_rgb); + dst->SrcBlendAlpha = _sg_d3d11_blend_factor(src->src_factor_alpha); + dst->DestBlendAlpha = _sg_d3d11_blend_factor(src->dst_factor_alpha); + dst->BlendOpAlpha = _sg_d3d11_blend_op(src->op_alpha); + dst->RenderTargetWriteMask = _sg_d3d11_color_write_mask(desc->colors[i].write_mask); + } + for (; i < 8; i++) { + D3D11_RENDER_TARGET_BLEND_DESC* dst = &bs_desc.RenderTarget[i]; + dst->BlendEnable = FALSE; + dst->SrcBlend = dst->SrcBlendAlpha = D3D11_BLEND_ONE; + dst->DestBlend = dst->DestBlendAlpha = D3D11_BLEND_ZERO; + dst->BlendOp = dst->BlendOpAlpha = D3D11_BLEND_OP_ADD; + dst->RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + } + } + hr = _sg_d3d11_CreateBlendState(_sg.d3d11.dev, &bs_desc, &pip->d3d11.bs); + if (!(SUCCEEDED(hr) && pip->d3d11.bs)) { + _SG_ERROR(D3D11_CREATE_BLEND_STATE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(pip->d3d11.bs, desc->label); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + if (pip == _sg.d3d11.cur_pipeline) { + _sg.d3d11.cur_pipeline = 0; + _sg.d3d11.cur_pipeline_id.id = SG_INVALID_ID; + } + if (pip->d3d11.il) { + _sg_d3d11_Release(pip->d3d11.il); + } + if (pip->d3d11.rs) { + _sg_d3d11_Release(pip->d3d11.rs); + } + if (pip->d3d11.dss) { + _sg_d3d11_Release(pip->d3d11.dss); + } + if (pip->d3d11.bs) { + _sg_d3d11_Release(pip->d3d11.bs); + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_attachments(_sg_attachments_t* atts, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_attachments_desc* desc) { + SOKOL_ASSERT(atts && desc); + SOKOL_ASSERT(color_images && resolve_images); + SOKOL_ASSERT(_sg.d3d11.dev); + + // copy image pointers + for (int i = 0; i < atts->cmn.num_colors; i++) { + const sg_attachment_desc* color_desc = &desc->colors[i]; + _SOKOL_UNUSED(color_desc); + SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == atts->d3d11.colors[i].image); + SOKOL_ASSERT(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); + atts->d3d11.colors[i].image = color_images[i]; + + const sg_attachment_desc* resolve_desc = &desc->resolves[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == atts->d3d11.resolves[i].image); + SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); + SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); + atts->d3d11.resolves[i].image = resolve_images[i]; + } + } + SOKOL_ASSERT(0 == atts->d3d11.depth_stencil.image); + const sg_attachment_desc* ds_desc = &desc->depth_stencil; + if (ds_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(ds_img && (ds_img->slot.id == ds_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_img->cmn.pixel_format)); + atts->d3d11.depth_stencil.image = ds_img; + } + + // create render-target views + for (int i = 0; i < atts->cmn.num_colors; i++) { + const _sg_attachment_common_t* cmn_color_att = &atts->cmn.colors[i]; + const _sg_image_t* color_img = color_images[i]; + SOKOL_ASSERT(0 == atts->d3d11.colors[i].view.rtv); + const bool msaa = color_img->cmn.sample_count > 1; + D3D11_RENDER_TARGET_VIEW_DESC d3d11_rtv_desc; + _sg_clear(&d3d11_rtv_desc, sizeof(d3d11_rtv_desc)); + d3d11_rtv_desc.Format = _sg_d3d11_rtv_pixel_format(color_img->cmn.pixel_format); + if (color_img->cmn.type == SG_IMAGETYPE_2D) { + if (msaa) { + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; + } else { + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + d3d11_rtv_desc.Texture2D.MipSlice = (UINT)cmn_color_att->mip_level; + } + } else if ((color_img->cmn.type == SG_IMAGETYPE_CUBE) || (color_img->cmn.type == SG_IMAGETYPE_ARRAY)) { + if (msaa) { + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY; + d3d11_rtv_desc.Texture2DMSArray.FirstArraySlice = (UINT)cmn_color_att->slice; + d3d11_rtv_desc.Texture2DMSArray.ArraySize = 1; + } else { + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + d3d11_rtv_desc.Texture2DArray.MipSlice = (UINT)cmn_color_att->mip_level; + d3d11_rtv_desc.Texture2DArray.FirstArraySlice = (UINT)cmn_color_att->slice; + d3d11_rtv_desc.Texture2DArray.ArraySize = 1; + } + } else { + SOKOL_ASSERT(color_img->cmn.type == SG_IMAGETYPE_3D); + SOKOL_ASSERT(!msaa); + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; + d3d11_rtv_desc.Texture3D.MipSlice = (UINT)cmn_color_att->mip_level; + d3d11_rtv_desc.Texture3D.FirstWSlice = (UINT)cmn_color_att->slice; + d3d11_rtv_desc.Texture3D.WSize = 1; + } + SOKOL_ASSERT(color_img->d3d11.res); + HRESULT hr = _sg_d3d11_CreateRenderTargetView(_sg.d3d11.dev, color_img->d3d11.res, &d3d11_rtv_desc, &atts->d3d11.colors[i].view.rtv); + if (!(SUCCEEDED(hr) && atts->d3d11.colors[i].view.rtv)) { + _SG_ERROR(D3D11_CREATE_RTV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(atts->d3d11.colors[i].view.rtv, desc->label); + } + SOKOL_ASSERT(0 == atts->d3d11.depth_stencil.view.dsv); + if (ds_desc->image.id != SG_INVALID_ID) { + const _sg_attachment_common_t* cmn_ds_att = &atts->cmn.depth_stencil; + const bool msaa = ds_img->cmn.sample_count > 1; + D3D11_DEPTH_STENCIL_VIEW_DESC d3d11_dsv_desc; + _sg_clear(&d3d11_dsv_desc, sizeof(d3d11_dsv_desc)); + d3d11_dsv_desc.Format = _sg_d3d11_dsv_pixel_format(ds_img->cmn.pixel_format); + SOKOL_ASSERT(ds_img && ds_img->cmn.type != SG_IMAGETYPE_3D); + if (ds_img->cmn.type == SG_IMAGETYPE_2D) { + if (msaa) { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; + } else { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + d3d11_dsv_desc.Texture2D.MipSlice = (UINT)cmn_ds_att->mip_level; + } + } else if ((ds_img->cmn.type == SG_IMAGETYPE_CUBE) || (ds_img->cmn.type == SG_IMAGETYPE_ARRAY)) { + if (msaa) { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY; + d3d11_dsv_desc.Texture2DMSArray.FirstArraySlice = (UINT)cmn_ds_att->slice; + d3d11_dsv_desc.Texture2DMSArray.ArraySize = 1; + } else { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY; + d3d11_dsv_desc.Texture2DArray.MipSlice = (UINT)cmn_ds_att->mip_level; + d3d11_dsv_desc.Texture2DArray.FirstArraySlice = (UINT)cmn_ds_att->slice; + d3d11_dsv_desc.Texture2DArray.ArraySize = 1; + } + } + SOKOL_ASSERT(ds_img->d3d11.res); + HRESULT hr = _sg_d3d11_CreateDepthStencilView(_sg.d3d11.dev, ds_img->d3d11.res, &d3d11_dsv_desc, &atts->d3d11.depth_stencil.view.dsv); + if (!(SUCCEEDED(hr) && atts->d3d11.depth_stencil.view.dsv)) { + _SG_ERROR(D3D11_CREATE_DSV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(atts->d3d11.depth_stencil.view.dsv, desc->label); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (atts->d3d11.colors[i].view.rtv) { + _sg_d3d11_Release(atts->d3d11.colors[i].view.rtv); + } + if (atts->d3d11.resolves[i].view.rtv) { + _sg_d3d11_Release(atts->d3d11.resolves[i].view.rtv); + } + } + if (atts->d3d11.depth_stencil.view.dsv) { + _sg_d3d11_Release(atts->d3d11.depth_stencil.view.dsv); + } +} + +_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_attachments_color_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->d3d11.colors[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_attachments_resolve_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->d3d11.resolves[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_attachments_ds_image(const _sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + return atts->d3d11.depth_stencil.image; +} + +_SOKOL_PRIVATE void _sg_d3d11_begin_pass(const sg_pass* pass) { + SOKOL_ASSERT(pass); + + const _sg_attachments_t* atts = _sg.cur_pass.atts; + const sg_swapchain* swapchain = &pass->swapchain; + const sg_pass_action* action = &pass->action; + + int num_rtvs = 0; + ID3D11RenderTargetView* rtvs[SG_MAX_COLOR_ATTACHMENTS] = { 0 }; + ID3D11DepthStencilView* dsv = 0; + _sg.d3d11.cur_pass.render_view = 0; + _sg.d3d11.cur_pass.resolve_view = 0; + if (atts) { + num_rtvs = atts->cmn.num_colors; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + rtvs[i] = atts->d3d11.colors[i].view.rtv; + } + dsv = atts->d3d11.depth_stencil.view.dsv; + } else { + // NOTE: depth-stencil-view is optional + SOKOL_ASSERT(swapchain->d3d11.render_view); + num_rtvs = 1; + rtvs[0] = (ID3D11RenderTargetView*) swapchain->d3d11.render_view; + dsv = (ID3D11DepthStencilView*) swapchain->d3d11.depth_stencil_view; + _sg.d3d11.cur_pass.render_view = (ID3D11RenderTargetView*) swapchain->d3d11.render_view; + _sg.d3d11.cur_pass.resolve_view = (ID3D11RenderTargetView*) swapchain->d3d11.resolve_view; + } + // apply the render-target- and depth-stencil-views + _sg_d3d11_OMSetRenderTargets(_sg.d3d11.ctx, SG_MAX_COLOR_ATTACHMENTS, rtvs, dsv); + _sg_stats_add(d3d11.pass.num_om_set_render_targets, 1); + + // set viewport and scissor rect to cover whole screen + D3D11_VIEWPORT vp; + _sg_clear(&vp, sizeof(vp)); + vp.Width = (FLOAT) _sg.cur_pass.width; + vp.Height = (FLOAT) _sg.cur_pass.height; + vp.MaxDepth = 1.0f; + _sg_d3d11_RSSetViewports(_sg.d3d11.ctx, 1, &vp); + D3D11_RECT rect; + rect.left = 0; + rect.top = 0; + rect.right = _sg.cur_pass.width; + rect.bottom = _sg.cur_pass.height; + _sg_d3d11_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); + + // perform clear action + for (int i = 0; i < num_rtvs; i++) { + if (action->colors[i].load_action == SG_LOADACTION_CLEAR) { + _sg_d3d11_ClearRenderTargetView(_sg.d3d11.ctx, rtvs[i], (float*)&action->colors[i].clear_value); + _sg_stats_add(d3d11.pass.num_clear_render_target_view, 1); + } + } + UINT ds_flags = 0; + if (action->depth.load_action == SG_LOADACTION_CLEAR) { + ds_flags |= D3D11_CLEAR_DEPTH; + } + if (action->stencil.load_action == SG_LOADACTION_CLEAR) { + ds_flags |= D3D11_CLEAR_STENCIL; + } + if ((0 != ds_flags) && dsv) { + _sg_d3d11_ClearDepthStencilView(_sg.d3d11.ctx, dsv, ds_flags, action->depth.clear_value, action->stencil.clear_value); + _sg_stats_add(d3d11.pass.num_clear_depth_stencil_view, 1); + } +} + +// D3D11CalcSubresource only exists for C++ +_SOKOL_PRIVATE UINT _sg_d3d11_calcsubresource(UINT mip_slice, UINT array_slice, UINT mip_levels) { + return mip_slice + array_slice * mip_levels; +} + +_SOKOL_PRIVATE void _sg_d3d11_end_pass(void) { + SOKOL_ASSERT(_sg.d3d11.ctx); + + // need to resolve MSAA render attachments into texture? + if (_sg.cur_pass.atts_id.id != SG_INVALID_ID) { + // ...for offscreen pass... + SOKOL_ASSERT(_sg.cur_pass.atts && _sg.cur_pass.atts->slot.id == _sg.cur_pass.atts_id.id); + for (int i = 0; i < _sg.cur_pass.atts->cmn.num_colors; i++) { + const _sg_image_t* resolve_img = _sg.cur_pass.atts->d3d11.resolves[i].image; + if (resolve_img) { + const _sg_image_t* color_img = _sg.cur_pass.atts->d3d11.colors[i].image; + const _sg_attachment_common_t* cmn_color_att = &_sg.cur_pass.atts->cmn.colors[i]; + const _sg_attachment_common_t* cmn_resolve_att = &_sg.cur_pass.atts->cmn.resolves[i]; + SOKOL_ASSERT(resolve_img->slot.id == cmn_resolve_att->image_id.id); + SOKOL_ASSERT(color_img && (color_img->slot.id == cmn_color_att->image_id.id)); + SOKOL_ASSERT(color_img->cmn.sample_count > 1); + SOKOL_ASSERT(resolve_img->cmn.sample_count == 1); + const UINT src_subres = _sg_d3d11_calcsubresource( + (UINT)cmn_color_att->mip_level, + (UINT)cmn_color_att->slice, + (UINT)color_img->cmn.num_mipmaps); + const UINT dst_subres = _sg_d3d11_calcsubresource( + (UINT)cmn_resolve_att->mip_level, + (UINT)cmn_resolve_att->slice, + (UINT)resolve_img->cmn.num_mipmaps); + _sg_d3d11_ResolveSubresource(_sg.d3d11.ctx, + resolve_img->d3d11.res, + dst_subres, + color_img->d3d11.res, + src_subres, + color_img->d3d11.format); + _sg_stats_add(d3d11.pass.num_resolve_subresource, 1); + } + } + } else { + // ...for swapchain pass... + if (_sg.d3d11.cur_pass.resolve_view) { + SOKOL_ASSERT(_sg.d3d11.cur_pass.render_view); + SOKOL_ASSERT(_sg.cur_pass.swapchain.sample_count > 1); + SOKOL_ASSERT(_sg.cur_pass.swapchain.color_fmt > SG_PIXELFORMAT_NONE); + ID3D11Resource* d3d11_render_res = 0; + ID3D11Resource* d3d11_resolve_res = 0; + _sg_d3d11_GetResource((ID3D11View*)_sg.d3d11.cur_pass.render_view, &d3d11_render_res); + _sg_d3d11_GetResource((ID3D11View*)_sg.d3d11.cur_pass.resolve_view, &d3d11_resolve_res); + SOKOL_ASSERT(d3d11_render_res); + SOKOL_ASSERT(d3d11_resolve_res); + const sg_pixel_format color_fmt = _sg.cur_pass.swapchain.color_fmt; + _sg_d3d11_ResolveSubresource(_sg.d3d11.ctx, d3d11_resolve_res, 0, d3d11_render_res, 0, _sg_d3d11_rtv_pixel_format(color_fmt)); + _sg_d3d11_Release(d3d11_render_res); + _sg_d3d11_Release(d3d11_resolve_res); + _sg_stats_add(d3d11.pass.num_resolve_subresource, 1); + } + } + _sg.d3d11.cur_pass.render_view = 0; + _sg.d3d11.cur_pass.resolve_view = 0; + _sg.d3d11.cur_pipeline = 0; + _sg.d3d11.cur_pipeline_id.id = SG_INVALID_ID; + _sg_d3d11_clear_state(); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.d3d11.ctx); + D3D11_VIEWPORT vp; + vp.TopLeftX = (FLOAT) x; + vp.TopLeftY = (FLOAT) (origin_top_left ? y : (_sg.cur_pass.height - (y + h))); + vp.Width = (FLOAT) w; + vp.Height = (FLOAT) h; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + _sg_d3d11_RSSetViewports(_sg.d3d11.ctx, 1, &vp); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.d3d11.ctx); + D3D11_RECT rect; + rect.left = x; + rect.top = (origin_top_left ? y : (_sg.cur_pass.height - (y + h))); + rect.right = x + w; + rect.bottom = origin_top_left ? (y + h) : (_sg.cur_pass.height - y); + _sg_d3d11_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(pip->d3d11.rs && pip->d3d11.bs && pip->d3d11.dss); + + _sg.d3d11.cur_pipeline = pip; + _sg.d3d11.cur_pipeline_id.id = pip->slot.id; + _sg.d3d11.use_indexed_draw = (pip->d3d11.index_format != DXGI_FORMAT_UNKNOWN); + _sg.d3d11.use_instanced_draw = pip->cmn.use_instanced_draw; + + _sg_d3d11_RSSetState(_sg.d3d11.ctx, pip->d3d11.rs); + _sg_d3d11_OMSetDepthStencilState(_sg.d3d11.ctx, pip->d3d11.dss, pip->d3d11.stencil_ref); + _sg_d3d11_OMSetBlendState(_sg.d3d11.ctx, pip->d3d11.bs, (float*)&pip->cmn.blend_color, 0xFFFFFFFF); + _sg_d3d11_IASetPrimitiveTopology(_sg.d3d11.ctx, pip->d3d11.topology); + _sg_d3d11_IASetInputLayout(_sg.d3d11.ctx, pip->d3d11.il); + _sg_d3d11_VSSetShader(_sg.d3d11.ctx, pip->shader->d3d11.vs, NULL, 0); + _sg_d3d11_VSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->d3d11.stage[SG_SHADERSTAGE_VS].cbufs); + _sg_d3d11_PSSetShader(_sg.d3d11.ctx, pip->shader->d3d11.fs, NULL, 0); + _sg_d3d11_PSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->d3d11.stage[SG_SHADERSTAGE_FS].cbufs); + _sg_stats_add(d3d11.pipeline.num_rs_set_state, 1); + _sg_stats_add(d3d11.pipeline.num_om_set_depth_stencil_state, 1); + _sg_stats_add(d3d11.pipeline.num_om_set_blend_state, 1); + _sg_stats_add(d3d11.pipeline.num_ia_set_primitive_topology, 1); + _sg_stats_add(d3d11.pipeline.num_ia_set_input_layout, 1); + _sg_stats_add(d3d11.pipeline.num_vs_set_shader, 1); + _sg_stats_add(d3d11.pipeline.num_vs_set_constant_buffers, 1); + _sg_stats_add(d3d11.pipeline.num_ps_set_shader, 1); + _sg_stats_add(d3d11.pipeline.num_ps_set_constant_buffers, 1); +} + +_SOKOL_PRIVATE bool _sg_d3d11_apply_bindings(_sg_bindings_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); + SOKOL_ASSERT(_sg.d3d11.ctx); + + // gather all the D3D11 resources into arrays + ID3D11Buffer* d3d11_ib = bnd->ib ? bnd->ib->d3d11.buf : 0; + ID3D11Buffer* d3d11_vbs[SG_MAX_VERTEX_BUFFERS] = {0}; + UINT d3d11_vb_offsets[SG_MAX_VERTEX_BUFFERS] = {0}; + ID3D11ShaderResourceView* d3d11_vs_srvs[_SG_D3D11_MAX_SHADERSTAGE_SRVS] = {0}; + ID3D11ShaderResourceView* d3d11_fs_srvs[_SG_D3D11_MAX_SHADERSTAGE_SRVS] = {0}; + ID3D11SamplerState* d3d11_vs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = {0}; + ID3D11SamplerState* d3d11_fs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = {0}; + for (int i = 0; i < bnd->num_vbs; i++) { + SOKOL_ASSERT(bnd->vbs[i]->d3d11.buf); + d3d11_vbs[i] = bnd->vbs[i]->d3d11.buf; + d3d11_vb_offsets[i] = (UINT)bnd->vb_offsets[i]; + } + for (int i = 0; i < bnd->num_vs_imgs; i++) { + SOKOL_ASSERT(bnd->vs_imgs[i]->d3d11.srv); + d3d11_vs_srvs[_SG_D3D11_SHADERSTAGE_IMAGE_SRV_OFFSET + i] = bnd->vs_imgs[i]->d3d11.srv; + } + for (int i = 0; i < bnd->num_vs_sbufs; i++) { + SOKOL_ASSERT(bnd->vs_sbufs[i]->d3d11.srv); + d3d11_vs_srvs[_SG_D3D11_SHADERSTAGE_BUFFER_SRV_OFFSET + i] = bnd->vs_sbufs[i]->d3d11.srv; + } + for (int i = 0; i < bnd->num_fs_imgs; i++) { + SOKOL_ASSERT(bnd->fs_imgs[i]->d3d11.srv); + d3d11_fs_srvs[_SG_D3D11_SHADERSTAGE_IMAGE_SRV_OFFSET + i] = bnd->fs_imgs[i]->d3d11.srv; + } + for (int i = 0; i < bnd->num_fs_sbufs; i++) { + SOKOL_ASSERT(bnd->fs_sbufs[i]->d3d11.srv); + d3d11_fs_srvs[_SG_D3D11_SHADERSTAGE_BUFFER_SRV_OFFSET + i] = bnd->fs_sbufs[i]->d3d11.srv; + } + for (int i = 0; i < bnd->num_vs_smps; i++) { + SOKOL_ASSERT(bnd->vs_smps[i]->d3d11.smp); + d3d11_vs_smps[i] = bnd->vs_smps[i]->d3d11.smp; + } + for (int i = 0; i < bnd->num_fs_smps; i++) { + SOKOL_ASSERT(bnd->fs_smps[i]->d3d11.smp); + d3d11_fs_smps[i] = bnd->fs_smps[i]->d3d11.smp; + } + _sg_d3d11_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_VERTEX_BUFFERS, d3d11_vbs, bnd->pip->d3d11.vb_strides, d3d11_vb_offsets); + _sg_d3d11_IASetIndexBuffer(_sg.d3d11.ctx, d3d11_ib, bnd->pip->d3d11.index_format, (UINT)bnd->ib_offset); + _sg_d3d11_VSSetShaderResources(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_SHADERSTAGE_SRVS, d3d11_vs_srvs); + _sg_d3d11_PSSetShaderResources(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_SHADERSTAGE_SRVS, d3d11_fs_srvs); + _sg_d3d11_VSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_SAMPLERS, d3d11_vs_smps); + _sg_d3d11_PSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_SAMPLERS, d3d11_fs_smps); + _sg_stats_add(d3d11.bindings.num_ia_set_vertex_buffers, 1); + _sg_stats_add(d3d11.bindings.num_ia_set_index_buffer, 1); + _sg_stats_add(d3d11.bindings.num_vs_set_shader_resources, 1); + _sg_stats_add(d3d11.bindings.num_ps_set_shader_resources, 1); + _sg_stats_add(d3d11.bindings.num_vs_set_samplers, 1); + _sg_stats_add(d3d11.bindings.num_ps_set_samplers, 1); + return true; +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(_sg.d3d11.cur_pipeline && _sg.d3d11.cur_pipeline->slot.id == _sg.d3d11.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.d3d11.cur_pipeline->shader && _sg.d3d11.cur_pipeline->shader->slot.id == _sg.d3d11.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(ub_index < _sg.d3d11.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); + SOKOL_ASSERT(data->size == _sg.d3d11.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); + ID3D11Buffer* cb = _sg.d3d11.cur_pipeline->shader->d3d11.stage[stage_index].cbufs[ub_index]; + SOKOL_ASSERT(cb); + _sg_d3d11_UpdateSubresource(_sg.d3d11.ctx, (ID3D11Resource*)cb, 0, NULL, data->ptr, 0, 0); + _sg_stats_add(d3d11.uniforms.num_update_subresource, 1); +} + +_SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_instances) { + const bool use_instanced_draw = (num_instances > 1) || (_sg.d3d11.use_instanced_draw); + if (_sg.d3d11.use_indexed_draw) { + if (use_instanced_draw) { + _sg_d3d11_DrawIndexedInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0, 0); + _sg_stats_add(d3d11.draw.num_draw_indexed_instanced, 1); + } else { + _sg_d3d11_DrawIndexed(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element, 0); + _sg_stats_add(d3d11.draw.num_draw_indexed, 1); + } + } else { + if (use_instanced_draw) { + _sg_d3d11_DrawInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0); + _sg_stats_add(d3d11.draw.num_draw_instanced, 1); + } else { + _sg_d3d11_Draw(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element); + _sg_stats_add(d3d11.draw.num_draw, 1); + } + } +} + +_SOKOL_PRIVATE void _sg_d3d11_commit(void) { + // empty +} + +_SOKOL_PRIVATE void _sg_d3d11_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(buf->d3d11.buf); + D3D11_MAPPED_SUBRESOURCE d3d11_msr; + HRESULT hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); + _sg_stats_add(d3d11.num_map, 1); + if (SUCCEEDED(hr)) { + memcpy(d3d11_msr.pData, data->ptr, data->size); + _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); + _sg_stats_add(d3d11.num_unmap, 1); + } else { + _SG_ERROR(D3D11_MAP_FOR_UPDATE_BUFFER_FAILED); + } +} + +_SOKOL_PRIVATE void _sg_d3d11_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(buf->d3d11.buf); + D3D11_MAP map_type = new_frame ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE_NO_OVERWRITE; + D3D11_MAPPED_SUBRESOURCE d3d11_msr; + HRESULT hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, map_type, 0, &d3d11_msr); + _sg_stats_add(d3d11.num_map, 1); + if (SUCCEEDED(hr)) { + uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData + buf->cmn.append_pos; + memcpy(dst_ptr, data->ptr, data->size); + _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); + _sg_stats_add(d3d11.num_unmap, 1); + } else { + _SG_ERROR(D3D11_MAP_FOR_APPEND_BUFFER_FAILED); + } +} + +// see: https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-subresources +// also see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-d3d11calcsubresource +_SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(img->d3d11.res); + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices:1; + const int num_depth_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? img->cmn.num_slices:1; + UINT subres_index = 0; + HRESULT hr; + D3D11_MAPPED_SUBRESOURCE d3d11_msr; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int slice_index = 0; slice_index < num_slices; slice_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) { + SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + const int src_row_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + const int src_depth_pitch = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + const sg_range* subimg_data = &(data->subimage[face_index][mip_index]); + const size_t slice_size = subimg_data->size / (size_t)num_slices; + SOKOL_ASSERT(slice_size == (size_t)(src_depth_pitch * num_depth_slices)); + const size_t slice_offset = slice_size * (size_t)slice_index; + const uint8_t* slice_ptr = ((const uint8_t*)subimg_data->ptr) + slice_offset; + hr = _sg_d3d11_Map(_sg.d3d11.ctx, img->d3d11.res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); + _sg_stats_add(d3d11.num_map, 1); + if (SUCCEEDED(hr)) { + const uint8_t* src_ptr = slice_ptr; + uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData; + for (int depth_index = 0; depth_index < num_depth_slices; depth_index++) { + if (src_row_pitch == (int)d3d11_msr.RowPitch) { + const size_t copy_size = slice_size / (size_t)num_depth_slices; + SOKOL_ASSERT((copy_size * (size_t)num_depth_slices) == slice_size); + memcpy(dst_ptr, src_ptr, copy_size); + } else { + SOKOL_ASSERT(src_row_pitch < (int)d3d11_msr.RowPitch); + const uint8_t* src_row_ptr = src_ptr; + uint8_t* dst_row_ptr = dst_ptr; + for (int row_index = 0; row_index < mip_height; row_index++) { + memcpy(dst_row_ptr, src_row_ptr, (size_t)src_row_pitch); + src_row_ptr += src_row_pitch; + dst_row_ptr += d3d11_msr.RowPitch; + } + } + src_ptr += src_depth_pitch; + dst_ptr += d3d11_msr.DepthPitch; + } + _sg_d3d11_Unmap(_sg.d3d11.ctx, img->d3d11.res, subres_index); + _sg_stats_add(d3d11.num_unmap, 1); + } else { + _SG_ERROR(D3D11_MAP_FOR_UPDATE_IMAGE_FAILED); + } + } + } + } +} + +// ███ ███ ███████ ████████ █████ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ████ ██ █████ ██ ███████ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██ ███████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>metal backend +#elif defined(SOKOL_METAL) + +#if __has_feature(objc_arc) +#define _SG_OBJC_RETAIN(obj) { } +#define _SG_OBJC_RELEASE(obj) { obj = nil; } +#else +#define _SG_OBJC_RETAIN(obj) { [obj retain]; } +#define _SG_OBJC_RELEASE(obj) { [obj release]; obj = nil; } +#endif + +//-- enum translation functions ------------------------------------------------ +_SOKOL_PRIVATE MTLLoadAction _sg_mtl_load_action(sg_load_action a) { + switch (a) { + case SG_LOADACTION_CLEAR: return MTLLoadActionClear; + case SG_LOADACTION_LOAD: return MTLLoadActionLoad; + case SG_LOADACTION_DONTCARE: return MTLLoadActionDontCare; + default: SOKOL_UNREACHABLE; return (MTLLoadAction)0; + } +} + +_SOKOL_PRIVATE MTLStoreAction _sg_mtl_store_action(sg_store_action a, bool resolve) { + switch (a) { + case SG_STOREACTION_STORE: + if (resolve) { + return MTLStoreActionStoreAndMultisampleResolve; + } else { + return MTLStoreActionStore; + } + break; + case SG_STOREACTION_DONTCARE: + if (resolve) { + return MTLStoreActionMultisampleResolve; + } else { + return MTLStoreActionDontCare; + } + break; + default: SOKOL_UNREACHABLE; return (MTLStoreAction)0; + } +} + +_SOKOL_PRIVATE MTLResourceOptions _sg_mtl_resource_options_storage_mode_managed_or_shared(void) { + #if defined(_SG_TARGET_MACOS) + if (_sg.mtl.use_shared_storage_mode) { + return MTLResourceStorageModeShared; + } else { + return MTLResourceStorageModeManaged; + } + #else + // MTLResourceStorageModeManaged is not even defined on iOS SDK + return MTLResourceStorageModeShared; + #endif +} + +_SOKOL_PRIVATE MTLResourceOptions _sg_mtl_buffer_resource_options(sg_usage usg) { + switch (usg) { + case SG_USAGE_IMMUTABLE: + return _sg_mtl_resource_options_storage_mode_managed_or_shared(); + case SG_USAGE_DYNAMIC: + case SG_USAGE_STREAM: + return MTLResourceCPUCacheModeWriteCombined | _sg_mtl_resource_options_storage_mode_managed_or_shared(); + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE MTLVertexStepFunction _sg_mtl_step_function(sg_vertex_step step) { + switch (step) { + case SG_VERTEXSTEP_PER_VERTEX: return MTLVertexStepFunctionPerVertex; + case SG_VERTEXSTEP_PER_INSTANCE: return MTLVertexStepFunctionPerInstance; + default: SOKOL_UNREACHABLE; return (MTLVertexStepFunction)0; + } +} + +_SOKOL_PRIVATE MTLVertexFormat _sg_mtl_vertex_format(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return MTLVertexFormatFloat; + case SG_VERTEXFORMAT_FLOAT2: return MTLVertexFormatFloat2; + case SG_VERTEXFORMAT_FLOAT3: return MTLVertexFormatFloat3; + case SG_VERTEXFORMAT_FLOAT4: return MTLVertexFormatFloat4; + case SG_VERTEXFORMAT_BYTE4: return MTLVertexFormatChar4; + case SG_VERTEXFORMAT_BYTE4N: return MTLVertexFormatChar4Normalized; + case SG_VERTEXFORMAT_UBYTE4: return MTLVertexFormatUChar4; + case SG_VERTEXFORMAT_UBYTE4N: return MTLVertexFormatUChar4Normalized; + case SG_VERTEXFORMAT_SHORT2: return MTLVertexFormatShort2; + case SG_VERTEXFORMAT_SHORT2N: return MTLVertexFormatShort2Normalized; + case SG_VERTEXFORMAT_USHORT2N: return MTLVertexFormatUShort2Normalized; + case SG_VERTEXFORMAT_SHORT4: return MTLVertexFormatShort4; + case SG_VERTEXFORMAT_SHORT4N: return MTLVertexFormatShort4Normalized; + case SG_VERTEXFORMAT_USHORT4N: return MTLVertexFormatUShort4Normalized; + case SG_VERTEXFORMAT_UINT10_N2: return MTLVertexFormatUInt1010102Normalized; + case SG_VERTEXFORMAT_HALF2: return MTLVertexFormatHalf2; + case SG_VERTEXFORMAT_HALF4: return MTLVertexFormatHalf4; + default: SOKOL_UNREACHABLE; return (MTLVertexFormat)0; + } +} + +_SOKOL_PRIVATE MTLPrimitiveType _sg_mtl_primitive_type(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return MTLPrimitiveTypePoint; + case SG_PRIMITIVETYPE_LINES: return MTLPrimitiveTypeLine; + case SG_PRIMITIVETYPE_LINE_STRIP: return MTLPrimitiveTypeLineStrip; + case SG_PRIMITIVETYPE_TRIANGLES: return MTLPrimitiveTypeTriangle; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return MTLPrimitiveTypeTriangleStrip; + default: SOKOL_UNREACHABLE; return (MTLPrimitiveType)0; + } +} + +_SOKOL_PRIVATE MTLPixelFormat _sg_mtl_pixel_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: return MTLPixelFormatR8Unorm; + case SG_PIXELFORMAT_R8SN: return MTLPixelFormatR8Snorm; + case SG_PIXELFORMAT_R8UI: return MTLPixelFormatR8Uint; + case SG_PIXELFORMAT_R8SI: return MTLPixelFormatR8Sint; + case SG_PIXELFORMAT_R16: return MTLPixelFormatR16Unorm; + case SG_PIXELFORMAT_R16SN: return MTLPixelFormatR16Snorm; + case SG_PIXELFORMAT_R16UI: return MTLPixelFormatR16Uint; + case SG_PIXELFORMAT_R16SI: return MTLPixelFormatR16Sint; + case SG_PIXELFORMAT_R16F: return MTLPixelFormatR16Float; + case SG_PIXELFORMAT_RG8: return MTLPixelFormatRG8Unorm; + case SG_PIXELFORMAT_RG8SN: return MTLPixelFormatRG8Snorm; + case SG_PIXELFORMAT_RG8UI: return MTLPixelFormatRG8Uint; + case SG_PIXELFORMAT_RG8SI: return MTLPixelFormatRG8Sint; + case SG_PIXELFORMAT_R32UI: return MTLPixelFormatR32Uint; + case SG_PIXELFORMAT_R32SI: return MTLPixelFormatR32Sint; + case SG_PIXELFORMAT_R32F: return MTLPixelFormatR32Float; + case SG_PIXELFORMAT_RG16: return MTLPixelFormatRG16Unorm; + case SG_PIXELFORMAT_RG16SN: return MTLPixelFormatRG16Snorm; + case SG_PIXELFORMAT_RG16UI: return MTLPixelFormatRG16Uint; + case SG_PIXELFORMAT_RG16SI: return MTLPixelFormatRG16Sint; + case SG_PIXELFORMAT_RG16F: return MTLPixelFormatRG16Float; + case SG_PIXELFORMAT_RGBA8: return MTLPixelFormatRGBA8Unorm; + case SG_PIXELFORMAT_SRGB8A8: return MTLPixelFormatRGBA8Unorm_sRGB; + case SG_PIXELFORMAT_RGBA8SN: return MTLPixelFormatRGBA8Snorm; + case SG_PIXELFORMAT_RGBA8UI: return MTLPixelFormatRGBA8Uint; + case SG_PIXELFORMAT_RGBA8SI: return MTLPixelFormatRGBA8Sint; + case SG_PIXELFORMAT_BGRA8: return MTLPixelFormatBGRA8Unorm; + case SG_PIXELFORMAT_RGB10A2: return MTLPixelFormatRGB10A2Unorm; + case SG_PIXELFORMAT_RG11B10F: return MTLPixelFormatRG11B10Float; + case SG_PIXELFORMAT_RGB9E5: return MTLPixelFormatRGB9E5Float; + case SG_PIXELFORMAT_RG32UI: return MTLPixelFormatRG32Uint; + case SG_PIXELFORMAT_RG32SI: return MTLPixelFormatRG32Sint; + case SG_PIXELFORMAT_RG32F: return MTLPixelFormatRG32Float; + case SG_PIXELFORMAT_RGBA16: return MTLPixelFormatRGBA16Unorm; + case SG_PIXELFORMAT_RGBA16SN: return MTLPixelFormatRGBA16Snorm; + case SG_PIXELFORMAT_RGBA16UI: return MTLPixelFormatRGBA16Uint; + case SG_PIXELFORMAT_RGBA16SI: return MTLPixelFormatRGBA16Sint; + case SG_PIXELFORMAT_RGBA16F: return MTLPixelFormatRGBA16Float; + case SG_PIXELFORMAT_RGBA32UI: return MTLPixelFormatRGBA32Uint; + case SG_PIXELFORMAT_RGBA32SI: return MTLPixelFormatRGBA32Sint; + case SG_PIXELFORMAT_RGBA32F: return MTLPixelFormatRGBA32Float; + case SG_PIXELFORMAT_DEPTH: return MTLPixelFormatDepth32Float; + case SG_PIXELFORMAT_DEPTH_STENCIL: return MTLPixelFormatDepth32Float_Stencil8; + #if defined(_SG_TARGET_MACOS) + case SG_PIXELFORMAT_BC1_RGBA: return MTLPixelFormatBC1_RGBA; + case SG_PIXELFORMAT_BC2_RGBA: return MTLPixelFormatBC2_RGBA; + case SG_PIXELFORMAT_BC3_RGBA: return MTLPixelFormatBC3_RGBA; + case SG_PIXELFORMAT_BC3_SRGBA: return MTLPixelFormatBC3_RGBA_sRGB; + case SG_PIXELFORMAT_BC4_R: return MTLPixelFormatBC4_RUnorm; + case SG_PIXELFORMAT_BC4_RSN: return MTLPixelFormatBC4_RSnorm; + case SG_PIXELFORMAT_BC5_RG: return MTLPixelFormatBC5_RGUnorm; + case SG_PIXELFORMAT_BC5_RGSN: return MTLPixelFormatBC5_RGSnorm; + case SG_PIXELFORMAT_BC6H_RGBF: return MTLPixelFormatBC6H_RGBFloat; + case SG_PIXELFORMAT_BC6H_RGBUF: return MTLPixelFormatBC6H_RGBUfloat; + case SG_PIXELFORMAT_BC7_RGBA: return MTLPixelFormatBC7_RGBAUnorm; + case SG_PIXELFORMAT_BC7_SRGBA: return MTLPixelFormatBC7_RGBAUnorm_sRGB; + #else + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: return MTLPixelFormatPVRTC_RGB_2BPP; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: return MTLPixelFormatPVRTC_RGB_4BPP; + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: return MTLPixelFormatPVRTC_RGBA_2BPP; + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: return MTLPixelFormatPVRTC_RGBA_4BPP; + case SG_PIXELFORMAT_ETC2_RGB8: return MTLPixelFormatETC2_RGB8; + case SG_PIXELFORMAT_ETC2_SRGB8: return MTLPixelFormatETC2_RGB8_sRGB; + case SG_PIXELFORMAT_ETC2_RGB8A1: return MTLPixelFormatETC2_RGB8A1; + case SG_PIXELFORMAT_ETC2_RGBA8: return MTLPixelFormatEAC_RGBA8; + case SG_PIXELFORMAT_ETC2_SRGB8A8: return MTLPixelFormatEAC_RGBA8_sRGB; + case SG_PIXELFORMAT_EAC_R11: return MTLPixelFormatEAC_R11Unorm; + case SG_PIXELFORMAT_EAC_R11SN: return MTLPixelFormatEAC_R11Snorm; + case SG_PIXELFORMAT_EAC_RG11: return MTLPixelFormatEAC_RG11Unorm; + case SG_PIXELFORMAT_EAC_RG11SN: return MTLPixelFormatEAC_RG11Snorm; + case SG_PIXELFORMAT_ASTC_4x4_RGBA: return MTLPixelFormatASTC_4x4_LDR; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return MTLPixelFormatASTC_4x4_sRGB; + #endif + default: return MTLPixelFormatInvalid; + } +} + +_SOKOL_PRIVATE MTLColorWriteMask _sg_mtl_color_write_mask(sg_color_mask m) { + MTLColorWriteMask mtl_mask = MTLColorWriteMaskNone; + if (m & SG_COLORMASK_R) { + mtl_mask |= MTLColorWriteMaskRed; + } + if (m & SG_COLORMASK_G) { + mtl_mask |= MTLColorWriteMaskGreen; + } + if (m & SG_COLORMASK_B) { + mtl_mask |= MTLColorWriteMaskBlue; + } + if (m & SG_COLORMASK_A) { + mtl_mask |= MTLColorWriteMaskAlpha; + } + return mtl_mask; +} + +_SOKOL_PRIVATE MTLBlendOperation _sg_mtl_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return MTLBlendOperationAdd; + case SG_BLENDOP_SUBTRACT: return MTLBlendOperationSubtract; + case SG_BLENDOP_REVERSE_SUBTRACT: return MTLBlendOperationReverseSubtract; + default: SOKOL_UNREACHABLE; return (MTLBlendOperation)0; + } +} + +_SOKOL_PRIVATE MTLBlendFactor _sg_mtl_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return MTLBlendFactorZero; + case SG_BLENDFACTOR_ONE: return MTLBlendFactorOne; + case SG_BLENDFACTOR_SRC_COLOR: return MTLBlendFactorSourceColor; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return MTLBlendFactorOneMinusSourceColor; + case SG_BLENDFACTOR_SRC_ALPHA: return MTLBlendFactorSourceAlpha; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return MTLBlendFactorOneMinusSourceAlpha; + case SG_BLENDFACTOR_DST_COLOR: return MTLBlendFactorDestinationColor; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return MTLBlendFactorOneMinusDestinationColor; + case SG_BLENDFACTOR_DST_ALPHA: return MTLBlendFactorDestinationAlpha; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return MTLBlendFactorOneMinusDestinationAlpha; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return MTLBlendFactorSourceAlphaSaturated; + case SG_BLENDFACTOR_BLEND_COLOR: return MTLBlendFactorBlendColor; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return MTLBlendFactorOneMinusBlendColor; + case SG_BLENDFACTOR_BLEND_ALPHA: return MTLBlendFactorBlendAlpha; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return MTLBlendFactorOneMinusBlendAlpha; + default: SOKOL_UNREACHABLE; return (MTLBlendFactor)0; + } +} + +_SOKOL_PRIVATE MTLCompareFunction _sg_mtl_compare_func(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return MTLCompareFunctionNever; + case SG_COMPAREFUNC_LESS: return MTLCompareFunctionLess; + case SG_COMPAREFUNC_EQUAL: return MTLCompareFunctionEqual; + case SG_COMPAREFUNC_LESS_EQUAL: return MTLCompareFunctionLessEqual; + case SG_COMPAREFUNC_GREATER: return MTLCompareFunctionGreater; + case SG_COMPAREFUNC_NOT_EQUAL: return MTLCompareFunctionNotEqual; + case SG_COMPAREFUNC_GREATER_EQUAL: return MTLCompareFunctionGreaterEqual; + case SG_COMPAREFUNC_ALWAYS: return MTLCompareFunctionAlways; + default: SOKOL_UNREACHABLE; return (MTLCompareFunction)0; + } +} + +_SOKOL_PRIVATE MTLStencilOperation _sg_mtl_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return MTLStencilOperationKeep; + case SG_STENCILOP_ZERO: return MTLStencilOperationZero; + case SG_STENCILOP_REPLACE: return MTLStencilOperationReplace; + case SG_STENCILOP_INCR_CLAMP: return MTLStencilOperationIncrementClamp; + case SG_STENCILOP_DECR_CLAMP: return MTLStencilOperationDecrementClamp; + case SG_STENCILOP_INVERT: return MTLStencilOperationInvert; + case SG_STENCILOP_INCR_WRAP: return MTLStencilOperationIncrementWrap; + case SG_STENCILOP_DECR_WRAP: return MTLStencilOperationDecrementWrap; + default: SOKOL_UNREACHABLE; return (MTLStencilOperation)0; + } +} + +_SOKOL_PRIVATE MTLCullMode _sg_mtl_cull_mode(sg_cull_mode m) { + switch (m) { + case SG_CULLMODE_NONE: return MTLCullModeNone; + case SG_CULLMODE_FRONT: return MTLCullModeFront; + case SG_CULLMODE_BACK: return MTLCullModeBack; + default: SOKOL_UNREACHABLE; return (MTLCullMode)0; + } +} + +_SOKOL_PRIVATE MTLWinding _sg_mtl_winding(sg_face_winding w) { + switch (w) { + case SG_FACEWINDING_CW: return MTLWindingClockwise; + case SG_FACEWINDING_CCW: return MTLWindingCounterClockwise; + default: SOKOL_UNREACHABLE; return (MTLWinding)0; + } +} + +_SOKOL_PRIVATE MTLIndexType _sg_mtl_index_type(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_UINT16: return MTLIndexTypeUInt16; + case SG_INDEXTYPE_UINT32: return MTLIndexTypeUInt32; + default: SOKOL_UNREACHABLE; return (MTLIndexType)0; + } +} + +_SOKOL_PRIVATE int _sg_mtl_index_size(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_NONE: return 0; + case SG_INDEXTYPE_UINT16: return 2; + case SG_INDEXTYPE_UINT32: return 4; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE MTLTextureType _sg_mtl_texture_type(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return MTLTextureType2D; + case SG_IMAGETYPE_CUBE: return MTLTextureTypeCube; + case SG_IMAGETYPE_3D: return MTLTextureType3D; + case SG_IMAGETYPE_ARRAY: return MTLTextureType2DArray; + default: SOKOL_UNREACHABLE; return (MTLTextureType)0; + } +} + +_SOKOL_PRIVATE bool _sg_mtl_is_pvrtc(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + return true; + default: + return false; + } +} + +_SOKOL_PRIVATE MTLSamplerAddressMode _sg_mtl_address_mode(sg_wrap w) { + if (_sg.features.image_clamp_to_border) { + if (@available(macOS 12.0, iOS 14.0, *)) { + // border color feature available + switch (w) { + case SG_WRAP_REPEAT: return MTLSamplerAddressModeRepeat; + case SG_WRAP_CLAMP_TO_EDGE: return MTLSamplerAddressModeClampToEdge; + case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToBorderColor; + case SG_WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat; + default: SOKOL_UNREACHABLE; return (MTLSamplerAddressMode)0; + } + } + } + // fallthrough: clamp to border no supported + switch (w) { + case SG_WRAP_REPEAT: return MTLSamplerAddressModeRepeat; + case SG_WRAP_CLAMP_TO_EDGE: return MTLSamplerAddressModeClampToEdge; + case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToEdge; + case SG_WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat; + default: SOKOL_UNREACHABLE; return (MTLSamplerAddressMode)0; + } +} + +_SOKOL_PRIVATE API_AVAILABLE(ios(14.0), macos(12.0)) MTLSamplerBorderColor _sg_mtl_border_color(sg_border_color c) { + switch (c) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: return MTLSamplerBorderColorTransparentBlack; + case SG_BORDERCOLOR_OPAQUE_BLACK: return MTLSamplerBorderColorOpaqueBlack; + case SG_BORDERCOLOR_OPAQUE_WHITE: return MTLSamplerBorderColorOpaqueWhite; + default: SOKOL_UNREACHABLE; return (MTLSamplerBorderColor)0; + } +} + +_SOKOL_PRIVATE MTLSamplerMinMagFilter _sg_mtl_minmag_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + return MTLSamplerMinMagFilterNearest; + case SG_FILTER_LINEAR: + return MTLSamplerMinMagFilterLinear; + default: + SOKOL_UNREACHABLE; return (MTLSamplerMinMagFilter)0; + } +} + +_SOKOL_PRIVATE MTLSamplerMipFilter _sg_mtl_mipmap_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NONE: + return MTLSamplerMipFilterNotMipmapped; + case SG_FILTER_NEAREST: + return MTLSamplerMipFilterNearest; + case SG_FILTER_LINEAR: + return MTLSamplerMipFilterLinear; + default: + SOKOL_UNREACHABLE; return (MTLSamplerMipFilter)0; + } +} + +//-- a pool for all Metal resource objects, with deferred release queue --------- +_SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) { + _sg.mtl.idpool.num_slots = 2 * + ( + 2 * desc->buffer_pool_size + + 4 * desc->image_pool_size + + 1 * desc->sampler_pool_size + + 4 * desc->shader_pool_size + + 2 * desc->pipeline_pool_size + + desc->attachments_pool_size + + 128 + ); + _sg.mtl.idpool.pool = [NSMutableArray arrayWithCapacity:(NSUInteger)_sg.mtl.idpool.num_slots]; + _SG_OBJC_RETAIN(_sg.mtl.idpool.pool); + NSNull* null = [NSNull null]; + for (int i = 0; i < _sg.mtl.idpool.num_slots; i++) { + [_sg.mtl.idpool.pool addObject:null]; + } + SOKOL_ASSERT([_sg.mtl.idpool.pool count] == (NSUInteger)_sg.mtl.idpool.num_slots); + // a queue of currently free slot indices + _sg.mtl.idpool.free_queue_top = 0; + _sg.mtl.idpool.free_queue = (int*)_sg_malloc_clear((size_t)_sg.mtl.idpool.num_slots * sizeof(int)); + // pool slot 0 is reserved! + for (int i = _sg.mtl.idpool.num_slots-1; i >= 1; i--) { + _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = i; + } + // a circular queue which holds release items (frame index when a resource is to be released, and the resource's pool index + _sg.mtl.idpool.release_queue_front = 0; + _sg.mtl.idpool.release_queue_back = 0; + _sg.mtl.idpool.release_queue = (_sg_mtl_release_item_t*)_sg_malloc_clear((size_t)_sg.mtl.idpool.num_slots * sizeof(_sg_mtl_release_item_t)); + for (int i = 0; i < _sg.mtl.idpool.num_slots; i++) { + _sg.mtl.idpool.release_queue[i].frame_index = 0; + _sg.mtl.idpool.release_queue[i].slot_index = _SG_MTL_INVALID_SLOT_INDEX; + } +} + +_SOKOL_PRIVATE void _sg_mtl_destroy_pool(void) { + _sg_free(_sg.mtl.idpool.release_queue); _sg.mtl.idpool.release_queue = 0; + _sg_free(_sg.mtl.idpool.free_queue); _sg.mtl.idpool.free_queue = 0; + _SG_OBJC_RELEASE(_sg.mtl.idpool.pool); +} + +// get a new free resource pool slot +_SOKOL_PRIVATE int _sg_mtl_alloc_pool_slot(void) { + SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top > 0); + const int slot_index = _sg.mtl.idpool.free_queue[--_sg.mtl.idpool.free_queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + return slot_index; +} + +// put a free resource pool slot back into the free-queue +_SOKOL_PRIVATE void _sg_mtl_free_pool_slot(int slot_index) { + SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top < _sg.mtl.idpool.num_slots); + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = slot_index; +} + +// add an MTLResource to the pool, return pool index or 0 if input was 'nil' +_SOKOL_PRIVATE int _sg_mtl_add_resource(id res) { + if (nil == res) { + return _SG_MTL_INVALID_SLOT_INDEX; + } + _sg_stats_add(metal.idpool.num_added, 1); + const int slot_index = _sg_mtl_alloc_pool_slot(); + // NOTE: the NSMutableArray will take ownership of its items + SOKOL_ASSERT([NSNull null] == _sg.mtl.idpool.pool[(NSUInteger)slot_index]); + _sg.mtl.idpool.pool[(NSUInteger)slot_index] = res; + return slot_index; +} + +/* mark an MTLResource for release, this will put the resource into the + deferred-release queue, and the resource will then be released N frames later, + the special pool index 0 will be ignored (this means that a nil + value was provided to _sg_mtl_add_resource() +*/ +_SOKOL_PRIVATE void _sg_mtl_release_resource(uint32_t frame_index, int slot_index) { + if (slot_index == _SG_MTL_INVALID_SLOT_INDEX) { + return; + } + _sg_stats_add(metal.idpool.num_released, 1); + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + SOKOL_ASSERT([NSNull null] != _sg.mtl.idpool.pool[(NSUInteger)slot_index]); + int release_index = _sg.mtl.idpool.release_queue_front++; + if (_sg.mtl.idpool.release_queue_front >= _sg.mtl.idpool.num_slots) { + // wrap-around + _sg.mtl.idpool.release_queue_front = 0; + } + // release queue full? + SOKOL_ASSERT(_sg.mtl.idpool.release_queue_front != _sg.mtl.idpool.release_queue_back); + SOKOL_ASSERT(0 == _sg.mtl.idpool.release_queue[release_index].frame_index); + const uint32_t safe_to_release_frame_index = frame_index + SG_NUM_INFLIGHT_FRAMES + 1; + _sg.mtl.idpool.release_queue[release_index].frame_index = safe_to_release_frame_index; + _sg.mtl.idpool.release_queue[release_index].slot_index = slot_index; +} + +// run garbage-collection pass on all resources in the release-queue +_SOKOL_PRIVATE void _sg_mtl_garbage_collect(uint32_t frame_index) { + while (_sg.mtl.idpool.release_queue_back != _sg.mtl.idpool.release_queue_front) { + if (frame_index < _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index) { + // don't need to check further, release-items past this are too young + break; + } + _sg_stats_add(metal.idpool.num_garbage_collected, 1); + // safe to release this resource + const int slot_index = _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index; + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + // note: the NSMutableArray takes ownership of its items, assigning an NSNull object will + // release the object, no matter if using ARC or not + SOKOL_ASSERT(_sg.mtl.idpool.pool[(NSUInteger)slot_index] != [NSNull null]); + _sg.mtl.idpool.pool[(NSUInteger)slot_index] = [NSNull null]; + // put the now free pool index back on the free queue + _sg_mtl_free_pool_slot(slot_index); + // reset the release queue slot and advance the back index + _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index = 0; + _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index = _SG_MTL_INVALID_SLOT_INDEX; + _sg.mtl.idpool.release_queue_back++; + if (_sg.mtl.idpool.release_queue_back >= _sg.mtl.idpool.num_slots) { + // wrap-around + _sg.mtl.idpool.release_queue_back = 0; + } + } +} + +_SOKOL_PRIVATE id _sg_mtl_id(int slot_index) { + return _sg.mtl.idpool.pool[(NSUInteger)slot_index]; +} + +_SOKOL_PRIVATE void _sg_mtl_clear_state_cache(void) { + _sg_clear(&_sg.mtl.state_cache, sizeof(_sg.mtl.state_cache)); +} + +// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf +_SOKOL_PRIVATE void _sg_mtl_init_caps(void) { + #if defined(_SG_TARGET_MACOS) + _sg.backend = SG_BACKEND_METAL_MACOS; + #elif defined(_SG_TARGET_IOS) + #if defined(_SG_TARGET_IOS_SIMULATOR) + _sg.backend = SG_BACKEND_METAL_SIMULATOR; + #else + _sg.backend = SG_BACKEND_METAL_IOS; + #endif + #endif + _sg.features.origin_top_left = true; + _sg.features.mrt_independent_blend_state = true; + _sg.features.mrt_independent_write_mask = true; + _sg.features.storage_buffer = true; + + _sg.features.image_clamp_to_border = false; + #if (MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) || (__IPHONE_OS_VERSION_MAX_ALLOWED >= 140000) + if (@available(macOS 12.0, iOS 14.0, *)) { + _sg.features.image_clamp_to_border = [_sg.mtl.device supportsFamily:MTLGPUFamilyApple7] + || [_sg.mtl.device supportsFamily:MTLGPUFamilyMac2]; + #if (MAC_OS_X_VERSION_MAX_ALLOWED >= 130000) || (__IPHONE_OS_VERSION_MAX_ALLOWED >= 160000) + if (!_sg.features.image_clamp_to_border) { + if (@available(macOS 13.0, iOS 16.0, *)) { + _sg.features.image_clamp_to_border = [_sg.mtl.device supportsFamily:MTLGPUFamilyMetal3]; + } + } + #endif + } + #endif + + #if defined(_SG_TARGET_MACOS) + _sg.limits.max_image_size_2d = 16 * 1024; + _sg.limits.max_image_size_cube = 16 * 1024; + _sg.limits.max_image_size_3d = 2 * 1024; + _sg.limits.max_image_size_array = 16 * 1024; + _sg.limits.max_image_array_layers = 2 * 1024; + #else + // FIXME: newer iOS devices support 16k textures + _sg.limits.max_image_size_2d = 8 * 1024; + _sg.limits.max_image_size_cube = 8 * 1024; + _sg.limits.max_image_size_3d = 2 * 1024; + _sg.limits.max_image_size_array = 8 * 1024; + _sg.limits.max_image_array_layers = 2 * 1024; + #endif + _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; + + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16SN]); + #else + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_R16]); + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_R16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R32F]); + #else + _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_R32F]); + #endif + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]); + #else + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RG16]); + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RG16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_SRGB8A8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGB9E5]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + #else + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB9E5]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + #endif + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); + #else + _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RG32F]); + #endif + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); + #else + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RGBA16]); + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + #else + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + #endif + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_SRGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_SRGBA]); + #else + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_2BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_4BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_2BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_4BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_SRGBA]); + + #endif +} + +//-- main Metal backend state and functions ------------------------------------ +_SOKOL_PRIVATE void _sg_mtl_setup_backend(const sg_desc* desc) { + // assume already zero-initialized + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->environment.metal.device); + SOKOL_ASSERT(desc->uniform_buffer_size > 0); + _sg_mtl_init_pool(desc); + _sg_mtl_clear_state_cache(); + _sg.mtl.valid = true; + _sg.mtl.ub_size = desc->uniform_buffer_size; + _sg.mtl.sem = dispatch_semaphore_create(SG_NUM_INFLIGHT_FRAMES); + _sg.mtl.device = (__bridge id) desc->environment.metal.device; + _sg.mtl.cmd_queue = [_sg.mtl.device newCommandQueue]; + + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + _sg.mtl.uniform_buffers[i] = [_sg.mtl.device + newBufferWithLength:(NSUInteger)_sg.mtl.ub_size + options:MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeShared + ]; + #if defined(SOKOL_DEBUG) + _sg.mtl.uniform_buffers[i].label = [NSString stringWithFormat:@"sg-uniform-buffer.%d", i]; + #endif + } + + if (desc->mtl_force_managed_storage_mode) { + _sg.mtl.use_shared_storage_mode = false; + } else if (@available(macOS 10.15, iOS 13.0, *)) { + // on Intel Macs, always use managed resources even though the + // device says it supports unified memory (because of texture restrictions) + const bool is_apple_gpu = [_sg.mtl.device supportsFamily:MTLGPUFamilyApple1]; + if (!is_apple_gpu) { + _sg.mtl.use_shared_storage_mode = false; + } else { + _sg.mtl.use_shared_storage_mode = true; + } + } else { + #if defined(_SG_TARGET_MACOS) + _sg.mtl.use_shared_storage_mode = false; + #else + _sg.mtl.use_shared_storage_mode = true; + #endif + } + _sg_mtl_init_caps(); +} + +_SOKOL_PRIVATE void _sg_mtl_discard_backend(void) { + SOKOL_ASSERT(_sg.mtl.valid); + // wait for the last frame to finish + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER); + } + // semaphore must be "relinquished" before destruction + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + dispatch_semaphore_signal(_sg.mtl.sem); + } + _sg_mtl_garbage_collect(_sg.frame_index + SG_NUM_INFLIGHT_FRAMES + 2); + _sg_mtl_destroy_pool(); + _sg.mtl.valid = false; + + _SG_OBJC_RELEASE(_sg.mtl.sem); + _SG_OBJC_RELEASE(_sg.mtl.device); + _SG_OBJC_RELEASE(_sg.mtl.cmd_queue); + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + _SG_OBJC_RELEASE(_sg.mtl.uniform_buffers[i]); + } + // NOTE: MTLCommandBuffer and MTLRenderCommandEncoder are auto-released + _sg.mtl.cmd_buffer = nil; + _sg.mtl.cmd_encoder = nil; +} + +_SOKOL_PRIVATE void _sg_mtl_bind_uniform_buffers(void) { + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + for (int slot = 0; slot < SG_MAX_SHADERSTAGE_UBS; slot++) { + [_sg.mtl.cmd_encoder + setVertexBuffer:_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] + offset:0 + atIndex:(NSUInteger)slot]; + [_sg.mtl.cmd_encoder + setFragmentBuffer:_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] + offset:0 + atIndex:(NSUInteger)slot]; + } +} + +_SOKOL_PRIVATE void _sg_mtl_reset_state_cache(void) { + _sg_mtl_clear_state_cache(); + // need to restore the uniform buffer binding (normally happens in _sg_mtl_begin_pass() + if (nil != _sg.mtl.cmd_encoder) { + _sg_mtl_bind_uniform_buffers(); + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + const bool injected = (0 != desc->mtl_buffers[0]); + MTLResourceOptions mtl_options = _sg_mtl_buffer_resource_options(buf->cmn.usage); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + id mtl_buf; + if (injected) { + SOKOL_ASSERT(desc->mtl_buffers[slot]); + mtl_buf = (__bridge id) desc->mtl_buffers[slot]; + } else { + if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { + SOKOL_ASSERT(desc->data.ptr); + mtl_buf = [_sg.mtl.device newBufferWithBytes:desc->data.ptr length:(NSUInteger)buf->cmn.size options:mtl_options]; + } else { + mtl_buf = [_sg.mtl.device newBufferWithLength:(NSUInteger)buf->cmn.size options:mtl_options]; + } + if (nil == mtl_buf) { + _SG_ERROR(METAL_CREATE_BUFFER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + #if defined(SOKOL_DEBUG) + if (desc->label) { + mtl_buf.label = [NSString stringWithFormat:@"%s.%d", desc->label, slot]; + } + #endif + buf->mtl.buf[slot] = _sg_mtl_add_resource(mtl_buf); + _SG_OBJC_RELEASE(mtl_buf); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_discard_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + // it's valid to call release resource with '0' + _sg_mtl_release_resource(_sg.frame_index, buf->mtl.buf[slot]); + } +} + +_SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unretained id mtl_tex, const sg_image_data* data) { + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices : 1; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { + SOKOL_ASSERT(data->subimage[face_index][mip_index].ptr); + SOKOL_ASSERT(data->subimage[face_index][mip_index].size > 0); + const uint8_t* data_ptr = (const uint8_t*)data->subimage[face_index][mip_index].ptr; + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + // special case PVRTC formats: bytePerRow and bytesPerImage must be 0 + int bytes_per_row = 0; + int bytes_per_slice = 0; + if (!_sg_mtl_is_pvrtc(img->cmn.pixel_format)) { + bytes_per_row = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + } + /* bytesPerImage special case: https://developer.apple.com/documentation/metal/mtltexture/1515679-replaceregion + + "Supply a nonzero value only when you copy data to a MTLTextureType3D type texture" + */ + MTLRegion region; + int bytes_per_image; + if (img->cmn.type == SG_IMAGETYPE_3D) { + const int mip_depth = _sg_miplevel_dim(img->cmn.num_slices, mip_index); + region = MTLRegionMake3D(0, 0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height, (NSUInteger)mip_depth); + bytes_per_image = bytes_per_slice; + // FIXME: apparently the minimal bytes_per_image size for 3D texture is 4 KByte... somehow need to handle this + } else { + region = MTLRegionMake2D(0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height); + bytes_per_image = 0; + } + + for (int slice_index = 0; slice_index < num_slices; slice_index++) { + const int mtl_slice_index = (img->cmn.type == SG_IMAGETYPE_CUBE) ? face_index : slice_index; + const int slice_offset = slice_index * bytes_per_slice; + SOKOL_ASSERT((slice_offset + bytes_per_slice) <= (int)data->subimage[face_index][mip_index].size); + [mtl_tex replaceRegion:region + mipmapLevel:(NSUInteger)mip_index + slice:(NSUInteger)mtl_slice_index + withBytes:data_ptr + slice_offset + bytesPerRow:(NSUInteger)bytes_per_row + bytesPerImage:(NSUInteger)bytes_per_image]; + } + } + } +} + +// initialize MTLTextureDescriptor with common attributes +_SOKOL_PRIVATE bool _sg_mtl_init_texdesc_common(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { + mtl_desc.textureType = _sg_mtl_texture_type(img->cmn.type); + mtl_desc.pixelFormat = _sg_mtl_pixel_format(img->cmn.pixel_format); + if (MTLPixelFormatInvalid == mtl_desc.pixelFormat) { + _SG_ERROR(METAL_TEXTURE_FORMAT_NOT_SUPPORTED); + return false; + } + mtl_desc.width = (NSUInteger)img->cmn.width; + mtl_desc.height = (NSUInteger)img->cmn.height; + if (SG_IMAGETYPE_3D == img->cmn.type) { + mtl_desc.depth = (NSUInteger)img->cmn.num_slices; + } else { + mtl_desc.depth = 1; + } + mtl_desc.mipmapLevelCount = (NSUInteger)img->cmn.num_mipmaps; + if (SG_IMAGETYPE_ARRAY == img->cmn.type) { + mtl_desc.arrayLength = (NSUInteger)img->cmn.num_slices; + } else { + mtl_desc.arrayLength = 1; + } + mtl_desc.usage = MTLTextureUsageShaderRead; + MTLResourceOptions res_options = 0; + if (img->cmn.usage != SG_USAGE_IMMUTABLE) { + res_options |= MTLResourceCPUCacheModeWriteCombined; + } + res_options |= _sg_mtl_resource_options_storage_mode_managed_or_shared(); + mtl_desc.resourceOptions = res_options; + return true; +} + +// initialize MTLTextureDescriptor with rendertarget attributes +_SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { + SOKOL_ASSERT(img->cmn.render_target); + _SOKOL_UNUSED(img); + mtl_desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget; + mtl_desc.resourceOptions = MTLResourceStorageModePrivate; +} + +// initialize MTLTextureDescriptor with MSAA attributes +_SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt_msaa(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { + SOKOL_ASSERT(img->cmn.sample_count > 1); + mtl_desc.usage = MTLTextureUsageRenderTarget; + mtl_desc.resourceOptions = MTLResourceStorageModePrivate; + mtl_desc.textureType = MTLTextureType2DMultisample; + mtl_desc.sampleCount = (NSUInteger)img->cmn.sample_count; +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + const bool injected = (0 != desc->mtl_textures[0]); + + // first initialize all Metal resource pool slots to 'empty' + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + img->mtl.tex[i] = _sg_mtl_add_resource(nil); + } + + // initialize a Metal texture descriptor + MTLTextureDescriptor* mtl_desc = [[MTLTextureDescriptor alloc] init]; + if (!_sg_mtl_init_texdesc_common(mtl_desc, img)) { + _SG_OBJC_RELEASE(mtl_desc); + return SG_RESOURCESTATE_FAILED; + } + if (img->cmn.render_target) { + if (img->cmn.sample_count > 1) { + _sg_mtl_init_texdesc_rt_msaa(mtl_desc, img); + } else { + _sg_mtl_init_texdesc_rt(mtl_desc, img); + } + } + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + id mtl_tex; + if (injected) { + SOKOL_ASSERT(desc->mtl_textures[slot]); + mtl_tex = (__bridge id) desc->mtl_textures[slot]; + } else { + mtl_tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; + if (nil == mtl_tex) { + _SG_OBJC_RELEASE(mtl_desc); + _SG_ERROR(METAL_CREATE_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + if ((img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { + _sg_mtl_copy_image_data(img, mtl_tex, &desc->data); + } + } + #if defined(SOKOL_DEBUG) + if (desc->label) { + mtl_tex.label = [NSString stringWithFormat:@"%s.%d", desc->label, slot]; + } + #endif + img->mtl.tex[slot] = _sg_mtl_add_resource(mtl_tex); + _SG_OBJC_RELEASE(mtl_tex); + } + _SG_OBJC_RELEASE(mtl_desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_discard_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + // it's valid to call release resource with a 'null resource' + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + _sg_mtl_release_resource(_sg.frame_index, img->mtl.tex[slot]); + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + id mtl_smp; + const bool injected = (0 != desc->mtl_sampler); + if (injected) { + SOKOL_ASSERT(desc->mtl_sampler); + mtl_smp = (__bridge id) desc->mtl_sampler; + } else { + MTLSamplerDescriptor* mtl_desc = [[MTLSamplerDescriptor alloc] init]; + mtl_desc.sAddressMode = _sg_mtl_address_mode(desc->wrap_u); + mtl_desc.tAddressMode = _sg_mtl_address_mode(desc->wrap_v); + mtl_desc.rAddressMode = _sg_mtl_address_mode(desc->wrap_w); + if (_sg.features.image_clamp_to_border) { + if (@available(macOS 12.0, iOS 14.0, *)) { + mtl_desc.borderColor = _sg_mtl_border_color(desc->border_color); + } + } + mtl_desc.minFilter = _sg_mtl_minmag_filter(desc->min_filter); + mtl_desc.magFilter = _sg_mtl_minmag_filter(desc->mag_filter); + mtl_desc.mipFilter = _sg_mtl_mipmap_filter(desc->mipmap_filter); + mtl_desc.lodMinClamp = desc->min_lod; + mtl_desc.lodMaxClamp = desc->max_lod; + // FIXME: lodAverage? + mtl_desc.maxAnisotropy = desc->max_anisotropy; + mtl_desc.normalizedCoordinates = YES; + mtl_desc.compareFunction = _sg_mtl_compare_func(desc->compare); + #if defined(SOKOL_DEBUG) + if (desc->label) { + mtl_desc.label = [NSString stringWithUTF8String:desc->label]; + } + #endif + mtl_smp = [_sg.mtl.device newSamplerStateWithDescriptor:mtl_desc]; + _SG_OBJC_RELEASE(mtl_desc); + if (nil == mtl_smp) { + _SG_ERROR(METAL_CREATE_SAMPLER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + smp->mtl.sampler_state = _sg_mtl_add_resource(mtl_smp); + _SG_OBJC_RELEASE(mtl_smp); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + // it's valid to call release resource with a 'null resource' + _sg_mtl_release_resource(_sg.frame_index, smp->mtl.sampler_state); +} + +_SOKOL_PRIVATE id _sg_mtl_compile_library(const char* src) { + NSError* err = NULL; + id lib = [_sg.mtl.device + newLibraryWithSource:[NSString stringWithUTF8String:src] + options:nil + error:&err + ]; + if (err) { + _SG_ERROR(METAL_SHADER_COMPILATION_FAILED); + _SG_LOGMSG(METAL_SHADER_COMPILATION_OUTPUT, [err.localizedDescription UTF8String]); + } + return lib; +} + +_SOKOL_PRIVATE id _sg_mtl_library_from_bytecode(const void* ptr, size_t num_bytes) { + NSError* err = NULL; + dispatch_data_t lib_data = dispatch_data_create(ptr, num_bytes, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + id lib = [_sg.mtl.device newLibraryWithData:lib_data error:&err]; + if (err) { + _SG_ERROR(METAL_SHADER_CREATION_FAILED); + _SG_LOGMSG(METAL_SHADER_COMPILATION_OUTPUT, [err.localizedDescription UTF8String]); + } + _SG_OBJC_RELEASE(lib_data); + return lib; +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + + // create metal library objects and lookup entry functions + id vs_lib = nil; + id fs_lib = nil; + id vs_func = nil; + id fs_func = nil; + const char* vs_entry = desc->vs.entry; + const char* fs_entry = desc->fs.entry; + if (desc->vs.bytecode.ptr && desc->fs.bytecode.ptr) { + // separate byte code provided + vs_lib = _sg_mtl_library_from_bytecode(desc->vs.bytecode.ptr, desc->vs.bytecode.size); + fs_lib = _sg_mtl_library_from_bytecode(desc->fs.bytecode.ptr, desc->fs.bytecode.size); + if ((nil == vs_lib) || (nil == fs_lib)) { + goto failed; + } + vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; + fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; + } else if (desc->vs.source && desc->fs.source) { + // separate sources provided + vs_lib = _sg_mtl_compile_library(desc->vs.source); + fs_lib = _sg_mtl_compile_library(desc->fs.source); + if ((nil == vs_lib) || (nil == fs_lib)) { + goto failed; + } + vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; + fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; + } else { + goto failed; + } + if (nil == vs_func) { + _SG_ERROR(METAL_VERTEX_SHADER_ENTRY_NOT_FOUND); + goto failed; + } + if (nil == fs_func) { + _SG_ERROR(METAL_FRAGMENT_SHADER_ENTRY_NOT_FOUND); + goto failed; + } + #if defined(SOKOL_DEBUG) + if (desc->label) { + vs_lib.label = [NSString stringWithFormat:@"%s.vs", desc->label]; + fs_lib.label = [NSString stringWithFormat:@"%s.fs", desc->label]; + } + #endif + // it is legal to call _sg_mtl_add_resource with a nil value, this will return a special 0xFFFFFFFF index + shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib = _sg_mtl_add_resource(vs_lib); + _SG_OBJC_RELEASE(vs_lib); + shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib = _sg_mtl_add_resource(fs_lib); + _SG_OBJC_RELEASE(fs_lib); + shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func = _sg_mtl_add_resource(vs_func); + _SG_OBJC_RELEASE(vs_func); + shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func = _sg_mtl_add_resource(fs_func); + _SG_OBJC_RELEASE(fs_func); + return SG_RESOURCESTATE_VALID; +failed: + if (vs_lib != nil) { + _SG_OBJC_RELEASE(vs_lib); + } + if (fs_lib != nil) { + _SG_OBJC_RELEASE(fs_lib); + } + if (vs_func != nil) { + _SG_OBJC_RELEASE(vs_func); + } + if (fs_func != nil) { + _SG_OBJC_RELEASE(fs_func); + } + return SG_RESOURCESTATE_FAILED; +} + +_SOKOL_PRIVATE void _sg_mtl_discard_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + // it is valid to call _sg_mtl_release_resource with a 'null resource' + _sg_mtl_release_resource(_sg.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func); + _sg_mtl_release_resource(_sg.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib); + _sg_mtl_release_resource(_sg.frame_index, shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func); + _sg_mtl_release_resource(_sg.frame_index, shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib); +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + + pip->shader = shd; + + sg_primitive_type prim_type = desc->primitive_type; + pip->mtl.prim_type = _sg_mtl_primitive_type(prim_type); + pip->mtl.index_size = _sg_mtl_index_size(pip->cmn.index_type); + if (SG_INDEXTYPE_NONE != pip->cmn.index_type) { + pip->mtl.index_type = _sg_mtl_index_type(pip->cmn.index_type); + } + pip->mtl.cull_mode = _sg_mtl_cull_mode(desc->cull_mode); + pip->mtl.winding = _sg_mtl_winding(desc->face_winding); + pip->mtl.stencil_ref = desc->stencil.ref; + + // create vertex-descriptor + MTLVertexDescriptor* vtx_desc = [MTLVertexDescriptor vertexDescriptor]; + for (NSUInteger attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + vtx_desc.attributes[attr_index].format = _sg_mtl_vertex_format(a_state->format); + vtx_desc.attributes[attr_index].offset = (NSUInteger)a_state->offset; + vtx_desc.attributes[attr_index].bufferIndex = (NSUInteger)(a_state->buffer_index + SG_MAX_SHADERSTAGE_UBS); + pip->cmn.vertex_buffer_layout_active[a_state->buffer_index] = true; + } + for (NSUInteger layout_index = 0; layout_index < SG_MAX_VERTEX_BUFFERS; layout_index++) { + if (pip->cmn.vertex_buffer_layout_active[layout_index]) { + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[layout_index]; + const NSUInteger mtl_vb_slot = layout_index + SG_MAX_SHADERSTAGE_UBS; + SOKOL_ASSERT(l_state->stride > 0); + vtx_desc.layouts[mtl_vb_slot].stride = (NSUInteger)l_state->stride; + vtx_desc.layouts[mtl_vb_slot].stepFunction = _sg_mtl_step_function(l_state->step_func); + vtx_desc.layouts[mtl_vb_slot].stepRate = (NSUInteger)l_state->step_rate; + if (SG_VERTEXSTEP_PER_INSTANCE == l_state->step_func) { + // NOTE: not actually used in _sg_mtl_draw() + pip->cmn.use_instanced_draw = true; + } + } + } + + // render-pipeline descriptor + MTLRenderPipelineDescriptor* rp_desc = [[MTLRenderPipelineDescriptor alloc] init]; + rp_desc.vertexDescriptor = vtx_desc; + SOKOL_ASSERT(shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); + rp_desc.vertexFunction = _sg_mtl_id(shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func); + SOKOL_ASSERT(shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); + rp_desc.fragmentFunction = _sg_mtl_id(shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func); + rp_desc.rasterSampleCount = (NSUInteger)desc->sample_count; + rp_desc.alphaToCoverageEnabled = desc->alpha_to_coverage_enabled; + rp_desc.alphaToOneEnabled = NO; + rp_desc.rasterizationEnabled = YES; + rp_desc.depthAttachmentPixelFormat = _sg_mtl_pixel_format(desc->depth.pixel_format); + if (desc->depth.pixel_format == SG_PIXELFORMAT_DEPTH_STENCIL) { + rp_desc.stencilAttachmentPixelFormat = _sg_mtl_pixel_format(desc->depth.pixel_format); + } + if (@available(macOS 10.13, iOS 11.0, *)) { + for (NSUInteger i = 0; i < (SG_MAX_SHADERSTAGE_UBS+SG_MAX_VERTEX_BUFFERS); i++) { + rp_desc.vertexBuffers[i].mutability = MTLMutabilityImmutable; + } + for (NSUInteger i = 0; i < SG_MAX_SHADERSTAGE_UBS; i++) { + rp_desc.fragmentBuffers[i].mutability = MTLMutabilityImmutable; + } + } + for (NSUInteger i = 0; i < (NSUInteger)desc->color_count; i++) { + SOKOL_ASSERT(i < SG_MAX_COLOR_ATTACHMENTS); + const sg_color_target_state* cs = &desc->colors[i]; + rp_desc.colorAttachments[i].pixelFormat = _sg_mtl_pixel_format(cs->pixel_format); + rp_desc.colorAttachments[i].writeMask = _sg_mtl_color_write_mask(cs->write_mask); + rp_desc.colorAttachments[i].blendingEnabled = cs->blend.enabled; + rp_desc.colorAttachments[i].alphaBlendOperation = _sg_mtl_blend_op(cs->blend.op_alpha); + rp_desc.colorAttachments[i].rgbBlendOperation = _sg_mtl_blend_op(cs->blend.op_rgb); + rp_desc.colorAttachments[i].destinationAlphaBlendFactor = _sg_mtl_blend_factor(cs->blend.dst_factor_alpha); + rp_desc.colorAttachments[i].destinationRGBBlendFactor = _sg_mtl_blend_factor(cs->blend.dst_factor_rgb); + rp_desc.colorAttachments[i].sourceAlphaBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_alpha); + rp_desc.colorAttachments[i].sourceRGBBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_rgb); + } + #if defined(SOKOL_DEBUG) + if (desc->label) { + rp_desc.label = [NSString stringWithFormat:@"%s", desc->label]; + } + #endif + NSError* err = NULL; + id mtl_rps = [_sg.mtl.device newRenderPipelineStateWithDescriptor:rp_desc error:&err]; + _SG_OBJC_RELEASE(rp_desc); + if (nil == mtl_rps) { + SOKOL_ASSERT(err); + _SG_ERROR(METAL_CREATE_RPS_FAILED); + _SG_LOGMSG(METAL_CREATE_RPS_OUTPUT, [err.localizedDescription UTF8String]); + return SG_RESOURCESTATE_FAILED; + } + pip->mtl.rps = _sg_mtl_add_resource(mtl_rps); + _SG_OBJC_RELEASE(mtl_rps); + + // depth-stencil-state + MTLDepthStencilDescriptor* ds_desc = [[MTLDepthStencilDescriptor alloc] init]; + ds_desc.depthCompareFunction = _sg_mtl_compare_func(desc->depth.compare); + ds_desc.depthWriteEnabled = desc->depth.write_enabled; + if (desc->stencil.enabled) { + const sg_stencil_face_state* sb = &desc->stencil.back; + ds_desc.backFaceStencil = [[MTLStencilDescriptor alloc] init]; + ds_desc.backFaceStencil.stencilFailureOperation = _sg_mtl_stencil_op(sb->fail_op); + ds_desc.backFaceStencil.depthFailureOperation = _sg_mtl_stencil_op(sb->depth_fail_op); + ds_desc.backFaceStencil.depthStencilPassOperation = _sg_mtl_stencil_op(sb->pass_op); + ds_desc.backFaceStencil.stencilCompareFunction = _sg_mtl_compare_func(sb->compare); + ds_desc.backFaceStencil.readMask = desc->stencil.read_mask; + ds_desc.backFaceStencil.writeMask = desc->stencil.write_mask; + const sg_stencil_face_state* sf = &desc->stencil.front; + ds_desc.frontFaceStencil = [[MTLStencilDescriptor alloc] init]; + ds_desc.frontFaceStencil.stencilFailureOperation = _sg_mtl_stencil_op(sf->fail_op); + ds_desc.frontFaceStencil.depthFailureOperation = _sg_mtl_stencil_op(sf->depth_fail_op); + ds_desc.frontFaceStencil.depthStencilPassOperation = _sg_mtl_stencil_op(sf->pass_op); + ds_desc.frontFaceStencil.stencilCompareFunction = _sg_mtl_compare_func(sf->compare); + ds_desc.frontFaceStencil.readMask = desc->stencil.read_mask; + ds_desc.frontFaceStencil.writeMask = desc->stencil.write_mask; + } + #if defined(SOKOL_DEBUG) + if (desc->label) { + ds_desc.label = [NSString stringWithFormat:@"%s.dss", desc->label]; + } + #endif + id mtl_dss = [_sg.mtl.device newDepthStencilStateWithDescriptor:ds_desc]; + _SG_OBJC_RELEASE(ds_desc); + if (nil == mtl_dss) { + _SG_ERROR(METAL_CREATE_DSS_FAILED); + return SG_RESOURCESTATE_FAILED; + } + pip->mtl.dss = _sg_mtl_add_resource(mtl_dss); + _SG_OBJC_RELEASE(mtl_dss); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_discard_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + // it's valid to call release resource with a 'null resource' + _sg_mtl_release_resource(_sg.frame_index, pip->mtl.rps); + _sg_mtl_release_resource(_sg.frame_index, pip->mtl.dss); +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_attachments(_sg_attachments_t* atts, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_attachments_desc* desc) { + SOKOL_ASSERT(atts && desc); + SOKOL_ASSERT(color_images && resolve_images); + + // copy image pointers + for (int i = 0; i < atts->cmn.num_colors; i++) { + const sg_attachment_desc* color_desc = &desc->colors[i]; + _SOKOL_UNUSED(color_desc); + SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == atts->mtl.colors[i].image); + SOKOL_ASSERT(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); + atts->mtl.colors[i].image = color_images[i]; + + const sg_attachment_desc* resolve_desc = &desc->resolves[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == atts->mtl.resolves[i].image); + SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); + SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); + atts->mtl.resolves[i].image = resolve_images[i]; + } + } + SOKOL_ASSERT(0 == atts->mtl.depth_stencil.image); + const sg_attachment_desc* ds_desc = &desc->depth_stencil; + if (ds_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(ds_img && (ds_img->slot.id == ds_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_img->cmn.pixel_format)); + atts->mtl.depth_stencil.image = ds_img; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_discard_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + _SOKOL_UNUSED(atts); +} + +_SOKOL_PRIVATE _sg_image_t* _sg_mtl_attachments_color_image(const _sg_attachments_t* atts, int index) { + // NOTE: may return null + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->mtl.colors[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_mtl_attachments_resolve_image(const _sg_attachments_t* atts, int index) { + // NOTE: may return null + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->mtl.resolves[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_mtl_attachments_ds_image(const _sg_attachments_t* atts) { + // NOTE: may return null + SOKOL_ASSERT(atts); + return atts->mtl.depth_stencil.image; +} + +_SOKOL_PRIVATE void _sg_mtl_begin_pass(const sg_pass* pass) { + SOKOL_ASSERT(pass); + SOKOL_ASSERT(_sg.mtl.cmd_queue); + SOKOL_ASSERT(nil == _sg.mtl.cmd_encoder); + SOKOL_ASSERT(nil == _sg.mtl.cur_drawable); + _sg_mtl_clear_state_cache(); + + const _sg_attachments_t* atts = _sg.cur_pass.atts; + const sg_swapchain* swapchain = &pass->swapchain; + const sg_pass_action* action = &pass->action; + + /* + if this is the first pass in the frame, create command buffers + + NOTE: we're creating two command buffers here, one with unretained references + for storing the regular commands, and one with retained references for + storing the presentDrawable call (this needs to hold on the drawable until + presentation has happened - and the easiest way to do this is to let the + command buffer manage the lifetime of the drawable). + + Also see: https://github.com/floooh/sokol/issues/762 + */ + if (nil == _sg.mtl.cmd_buffer) { + // block until the oldest frame in flight has finished + dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER); + if (_sg.desc.mtl_use_command_buffer_with_retained_references) { + _sg.mtl.cmd_buffer = [_sg.mtl.cmd_queue commandBuffer]; + } else { + _sg.mtl.cmd_buffer = [_sg.mtl.cmd_queue commandBufferWithUnretainedReferences]; + } + [_sg.mtl.cmd_buffer enqueue]; + [_sg.mtl.cmd_buffer addCompletedHandler:^(id cmd_buf) { + // NOTE: this code is called on a different thread! + _SOKOL_UNUSED(cmd_buf); + dispatch_semaphore_signal(_sg.mtl.sem); + }]; + } + + // if this is first pass in frame, get uniform buffer base pointer + if (0 == _sg.mtl.cur_ub_base_ptr) { + _sg.mtl.cur_ub_base_ptr = (uint8_t*)[_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] contents]; + } + + MTLRenderPassDescriptor* pass_desc = [MTLRenderPassDescriptor renderPassDescriptor]; + SOKOL_ASSERT(pass_desc); + if (atts) { + // setup pass descriptor for offscreen rendering + SOKOL_ASSERT(atts->slot.state == SG_RESOURCESTATE_VALID); + for (NSUInteger i = 0; i < (NSUInteger)atts->cmn.num_colors; i++) { + const _sg_attachment_common_t* cmn_color_att = &atts->cmn.colors[i]; + const _sg_mtl_attachment_t* mtl_color_att = &atts->mtl.colors[i]; + const _sg_image_t* color_att_img = mtl_color_att->image; + const _sg_attachment_common_t* cmn_resolve_att = &atts->cmn.resolves[i]; + const _sg_mtl_attachment_t* mtl_resolve_att = &atts->mtl.resolves[i]; + const _sg_image_t* resolve_att_img = mtl_resolve_att->image; + SOKOL_ASSERT(color_att_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(color_att_img->slot.id == cmn_color_att->image_id.id); + SOKOL_ASSERT(color_att_img->mtl.tex[color_att_img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.colorAttachments[i].loadAction = _sg_mtl_load_action(action->colors[i].load_action); + pass_desc.colorAttachments[i].storeAction = _sg_mtl_store_action(action->colors[i].store_action, resolve_att_img != 0); + sg_color c = action->colors[i].clear_value; + pass_desc.colorAttachments[i].clearColor = MTLClearColorMake(c.r, c.g, c.b, c.a); + pass_desc.colorAttachments[i].texture = _sg_mtl_id(color_att_img->mtl.tex[color_att_img->cmn.active_slot]); + pass_desc.colorAttachments[i].level = (NSUInteger)cmn_color_att->mip_level; + switch (color_att_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.colorAttachments[i].slice = (NSUInteger)cmn_color_att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.colorAttachments[i].depthPlane = (NSUInteger)cmn_color_att->slice; + break; + default: break; + } + if (resolve_att_img) { + SOKOL_ASSERT(resolve_att_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(resolve_att_img->slot.id == cmn_resolve_att->image_id.id); + SOKOL_ASSERT(resolve_att_img->mtl.tex[resolve_att_img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.colorAttachments[i].resolveTexture = _sg_mtl_id(resolve_att_img->mtl.tex[resolve_att_img->cmn.active_slot]); + pass_desc.colorAttachments[i].resolveLevel = (NSUInteger)cmn_resolve_att->mip_level; + switch (resolve_att_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.colorAttachments[i].resolveSlice = (NSUInteger)cmn_resolve_att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.colorAttachments[i].resolveDepthPlane = (NSUInteger)cmn_resolve_att->slice; + break; + default: break; + } + } + } + const _sg_image_t* ds_att_img = atts->mtl.depth_stencil.image; + if (0 != ds_att_img) { + SOKOL_ASSERT(ds_att_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(ds_att_img->slot.id == atts->cmn.depth_stencil.image_id.id); + SOKOL_ASSERT(ds_att_img->mtl.tex[ds_att_img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.depthAttachment.texture = _sg_mtl_id(ds_att_img->mtl.tex[ds_att_img->cmn.active_slot]); + pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.load_action); + pass_desc.depthAttachment.storeAction = _sg_mtl_store_action(action->depth.store_action, false); + pass_desc.depthAttachment.clearDepth = action->depth.clear_value; + const _sg_attachment_common_t* cmn_ds_att = &atts->cmn.depth_stencil; + switch (ds_att_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.depthAttachment.slice = (NSUInteger)cmn_ds_att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.depthAttachment.resolveDepthPlane = (NSUInteger)cmn_ds_att->slice; + break; + default: break; + } + if (_sg_is_depth_stencil_format(ds_att_img->cmn.pixel_format)) { + pass_desc.stencilAttachment.texture = _sg_mtl_id(ds_att_img->mtl.tex[ds_att_img->cmn.active_slot]); + pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.load_action); + pass_desc.stencilAttachment.storeAction = _sg_mtl_store_action(action->depth.store_action, false); + pass_desc.stencilAttachment.clearStencil = action->stencil.clear_value; + switch (ds_att_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.stencilAttachment.slice = (NSUInteger)cmn_ds_att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.stencilAttachment.resolveDepthPlane = (NSUInteger)cmn_ds_att->slice; + break; + default: break; + } + } + } + } else { + // setup pass descriptor for swapchain rendering + // + // NOTE: at least in macOS Sonoma this no longer seems to be the case, the + // current drawable is also valid in a minimized window + // === + // an MTKView current_drawable will not be valid if window is minimized, don't do any rendering in this case + if (0 == swapchain->metal.current_drawable) { + _sg.cur_pass.valid = false; + return; + } + // pin the swapchain resources into memory so that they outlive their command buffer + // (this is necessary because the command buffer doesn't retain references) + int pass_desc_ref = _sg_mtl_add_resource(pass_desc); + _sg_mtl_release_resource(_sg.frame_index, pass_desc_ref); + + _sg.mtl.cur_drawable = (__bridge id) swapchain->metal.current_drawable; + if (swapchain->sample_count > 1) { + // multi-sampling: render into msaa texture, resolve into drawable texture + id msaa_tex = (__bridge id) swapchain->metal.msaa_color_texture; + SOKOL_ASSERT(msaa_tex != nil); + pass_desc.colorAttachments[0].texture = msaa_tex; + pass_desc.colorAttachments[0].resolveTexture = _sg.mtl.cur_drawable.texture; + pass_desc.colorAttachments[0].storeAction = MTLStoreActionMultisampleResolve; + } else { + // non-msaa: render into current_drawable + pass_desc.colorAttachments[0].texture = _sg.mtl.cur_drawable.texture; + pass_desc.colorAttachments[0].storeAction = MTLStoreActionStore; + } + pass_desc.colorAttachments[0].loadAction = _sg_mtl_load_action(action->colors[0].load_action); + const sg_color c = action->colors[0].clear_value; + pass_desc.colorAttachments[0].clearColor = MTLClearColorMake(c.r, c.g, c.b, c.a); + + // optional depth-stencil texture + if (swapchain->metal.depth_stencil_texture) { + id ds_tex = (__bridge id) swapchain->metal.depth_stencil_texture; + SOKOL_ASSERT(ds_tex != nil); + pass_desc.depthAttachment.texture = ds_tex; + pass_desc.depthAttachment.storeAction = MTLStoreActionDontCare; + pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.load_action); + pass_desc.depthAttachment.clearDepth = action->depth.clear_value; + if (_sg_is_depth_stencil_format(swapchain->depth_format)) { + pass_desc.stencilAttachment.texture = ds_tex; + pass_desc.stencilAttachment.storeAction = MTLStoreActionDontCare; + pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.load_action); + pass_desc.stencilAttachment.clearStencil = action->stencil.clear_value; + } + } + } + + // NOTE: at least in macOS Sonoma, the following is no longer the case, a valid + // render command encoder is also returned in a minimized window + // === + // create a render command encoder, this might return nil if window is minimized + _sg.mtl.cmd_encoder = [_sg.mtl.cmd_buffer renderCommandEncoderWithDescriptor:pass_desc]; + if (nil == _sg.mtl.cmd_encoder) { + _sg.cur_pass.valid = false; + return; + } + + #if defined(SOKOL_DEBUG) + if (pass->label) { + _sg.mtl.cmd_encoder.label = [NSString stringWithUTF8String:pass->label]; + } + #endif + + // bind the global uniform buffer, this only happens once per pass + _sg_mtl_bind_uniform_buffers(); +} + +_SOKOL_PRIVATE void _sg_mtl_end_pass(void) { + if (nil != _sg.mtl.cmd_encoder) { + [_sg.mtl.cmd_encoder endEncoding]; + // NOTE: MTLRenderCommandEncoder is autoreleased + _sg.mtl.cmd_encoder = nil; + } + // if this is a swapchain pass, present the drawable + if (nil != _sg.mtl.cur_drawable) { + [_sg.mtl.cmd_buffer presentDrawable:_sg.mtl.cur_drawable]; + _sg.mtl.cur_drawable = nil; + } +} + +_SOKOL_PRIVATE void _sg_mtl_commit(void) { + SOKOL_ASSERT(nil == _sg.mtl.cmd_encoder); + SOKOL_ASSERT(nil != _sg.mtl.cmd_buffer); + + // commit the frame's command buffer + [_sg.mtl.cmd_buffer commit]; + + // garbage-collect resources pending for release + _sg_mtl_garbage_collect(_sg.frame_index); + + // rotate uniform buffer slot + if (++_sg.mtl.cur_frame_rotate_index >= SG_NUM_INFLIGHT_FRAMES) { + _sg.mtl.cur_frame_rotate_index = 0; + } + _sg.mtl.cur_ub_offset = 0; + _sg.mtl.cur_ub_base_ptr = 0; + // NOTE: MTLCommandBuffer is autoreleased + _sg.mtl.cmd_buffer = nil; +} + +_SOKOL_PRIVATE void _sg_mtl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + SOKOL_ASSERT(_sg.cur_pass.height > 0); + MTLViewport vp; + vp.originX = (double) x; + vp.originY = (double) (origin_top_left ? y : (_sg.cur_pass.height - (y + h))); + vp.width = (double) w; + vp.height = (double) h; + vp.znear = 0.0; + vp.zfar = 1.0; + [_sg.mtl.cmd_encoder setViewport:vp]; +} + +_SOKOL_PRIVATE void _sg_mtl_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + SOKOL_ASSERT(_sg.cur_pass.width > 0); + SOKOL_ASSERT(_sg.cur_pass.height > 0); + // clip against framebuffer rect + const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.cur_pass.width, _sg.cur_pass.height); + MTLScissorRect r; + r.x = (NSUInteger)clip.x; + r.y = (NSUInteger) (origin_top_left ? clip.y : (_sg.cur_pass.height - (clip.y + clip.h))); + r.width = (NSUInteger)clip.w; + r.height = (NSUInteger)clip.h; + [_sg.mtl.cmd_encoder setScissorRect:r]; +} + +_SOKOL_PRIVATE void _sg_mtl_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + + if (_sg.mtl.state_cache.cur_pipeline_id.id != pip->slot.id) { + _sg.mtl.state_cache.cur_pipeline = pip; + _sg.mtl.state_cache.cur_pipeline_id.id = pip->slot.id; + sg_color c = pip->cmn.blend_color; + [_sg.mtl.cmd_encoder setBlendColorRed:c.r green:c.g blue:c.b alpha:c.a]; + _sg_stats_add(metal.pipeline.num_set_blend_color, 1); + [_sg.mtl.cmd_encoder setCullMode:pip->mtl.cull_mode]; + _sg_stats_add(metal.pipeline.num_set_cull_mode, 1); + [_sg.mtl.cmd_encoder setFrontFacingWinding:pip->mtl.winding]; + _sg_stats_add(metal.pipeline.num_set_front_facing_winding, 1); + [_sg.mtl.cmd_encoder setStencilReferenceValue:pip->mtl.stencil_ref]; + _sg_stats_add(metal.pipeline.num_set_stencil_reference_value, 1); + [_sg.mtl.cmd_encoder setDepthBias:pip->cmn.depth.bias slopeScale:pip->cmn.depth.bias_slope_scale clamp:pip->cmn.depth.bias_clamp]; + _sg_stats_add(metal.pipeline.num_set_depth_bias, 1); + SOKOL_ASSERT(pip->mtl.rps != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setRenderPipelineState:_sg_mtl_id(pip->mtl.rps)]; + _sg_stats_add(metal.pipeline.num_set_render_pipeline_state, 1); + SOKOL_ASSERT(pip->mtl.dss != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setDepthStencilState:_sg_mtl_id(pip->mtl.dss)]; + _sg_stats_add(metal.pipeline.num_set_depth_stencil_state, 1); + } +} + +_SOKOL_PRIVATE bool _sg_mtl_apply_bindings(_sg_bindings_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + + // store index buffer binding, this will be needed later in sg_draw() + _sg.mtl.state_cache.cur_indexbuffer = bnd->ib; + _sg.mtl.state_cache.cur_indexbuffer_offset = bnd->ib_offset; + if (bnd->ib) { + SOKOL_ASSERT(bnd->pip->cmn.index_type != SG_INDEXTYPE_NONE); + _sg.mtl.state_cache.cur_indexbuffer_id.id = bnd->ib->slot.id; + } else { + SOKOL_ASSERT(bnd->pip->cmn.index_type == SG_INDEXTYPE_NONE); + _sg.mtl.state_cache.cur_indexbuffer_id.id = SG_INVALID_ID; + } + + // apply vertex buffers + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_vbs; slot++) { + const _sg_buffer_t* vb = bnd->vbs[slot]; + const int vb_offset = bnd->vb_offsets[slot]; + if ((_sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id != vb->slot.id) || + (_sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] != vb_offset)) + { + _sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] = vb_offset; + const NSUInteger mtl_slot = SG_MAX_SHADERSTAGE_UBS + slot; + if (_sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id != vb->slot.id) { + _sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id = vb->slot.id; + SOKOL_ASSERT(vb->mtl.buf[vb->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setVertexBuffer:_sg_mtl_id(vb->mtl.buf[vb->cmn.active_slot]) + offset:(NSUInteger)vb_offset + atIndex:mtl_slot]; + } else { + [_sg.mtl.cmd_encoder setVertexBufferOffset:(NSUInteger)vb_offset atIndex:mtl_slot]; + } + _sg_stats_add(metal.bindings.num_set_vertex_buffer, 1); + } + } + + // apply vertex stage images + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_vs_imgs; slot++) { + const _sg_image_t* img = bnd->vs_imgs[slot]; + if (_sg.mtl.state_cache.cur_vs_image_ids[slot].id != img->slot.id) { + _sg.mtl.state_cache.cur_vs_image_ids[slot].id = img->slot.id; + SOKOL_ASSERT(img->mtl.tex[img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setVertexTexture:_sg_mtl_id(img->mtl.tex[img->cmn.active_slot]) atIndex:slot]; + _sg_stats_add(metal.bindings.num_set_vertex_texture, 1); + } + } + + // apply vertex stage samplers + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_vs_smps; slot++) { + const _sg_sampler_t* smp = bnd->vs_smps[slot]; + if (_sg.mtl.state_cache.cur_vs_sampler_ids[slot].id != smp->slot.id) { + _sg.mtl.state_cache.cur_vs_sampler_ids[slot].id = smp->slot.id; + SOKOL_ASSERT(smp->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setVertexSamplerState:_sg_mtl_id(smp->mtl.sampler_state) atIndex:slot]; + _sg_stats_add(metal.bindings.num_set_vertex_sampler_state, 1); + } + } + + // apply vertex stage storage buffers + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_vs_sbufs; slot++) { + const _sg_buffer_t* sbuf = bnd->vs_sbufs[slot]; + if (_sg.mtl.state_cache.cur_vs_storagebuffer_ids[slot].id != sbuf->slot.id) { + _sg.mtl.state_cache.cur_vs_storagebuffer_ids[slot].id = sbuf->slot.id; + SOKOL_ASSERT(sbuf->mtl.buf[sbuf->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + const NSUInteger mtl_slot = SG_MAX_SHADERSTAGE_UBS + SG_MAX_VERTEX_BUFFERS + slot; + [_sg.mtl.cmd_encoder setVertexBuffer:_sg_mtl_id(sbuf->mtl.buf[sbuf->cmn.active_slot]) offset:0 atIndex:mtl_slot]; + _sg_stats_add(metal.bindings.num_set_vertex_buffer, 1); + } + } + + // apply fragment stage images + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_fs_imgs; slot++) { + const _sg_image_t* img = bnd->fs_imgs[slot]; + if (_sg.mtl.state_cache.cur_fs_image_ids[slot].id != img->slot.id) { + _sg.mtl.state_cache.cur_fs_image_ids[slot].id = img->slot.id; + SOKOL_ASSERT(img->mtl.tex[img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setFragmentTexture:_sg_mtl_id(img->mtl.tex[img->cmn.active_slot]) atIndex:slot]; + _sg_stats_add(metal.bindings.num_set_fragment_texture, 1); + } + } + + // apply fragment stage samplers + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_fs_smps; slot++) { + const _sg_sampler_t* smp = bnd->fs_smps[slot]; + if (_sg.mtl.state_cache.cur_fs_sampler_ids[slot].id != smp->slot.id) { + _sg.mtl.state_cache.cur_fs_sampler_ids[slot].id = smp->slot.id; + SOKOL_ASSERT(smp->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setFragmentSamplerState:_sg_mtl_id(smp->mtl.sampler_state) atIndex:slot]; + _sg_stats_add(metal.bindings.num_set_fragment_sampler_state, 1); + } + } + + // apply fragment stage storage buffers + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_fs_sbufs; slot++) { + const _sg_buffer_t* sbuf = bnd->fs_sbufs[slot]; + if (_sg.mtl.state_cache.cur_fs_storagebuffer_ids[slot].id != sbuf->slot.id) { + _sg.mtl.state_cache.cur_fs_storagebuffer_ids[slot].id = sbuf->slot.id; + SOKOL_ASSERT(sbuf->mtl.buf[sbuf->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + const NSUInteger mtl_slot = SG_MAX_SHADERSTAGE_UBS + slot; + [_sg.mtl.cmd_encoder setFragmentBuffer:_sg_mtl_id(sbuf->mtl.buf[sbuf->cmn.active_slot]) offset:0 atIndex:mtl_slot]; + _sg_stats_add(metal.bindings.num_set_fragment_buffer, 1); + } + } + + return true; +} + +_SOKOL_PRIVATE void _sg_mtl_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + SOKOL_ASSERT(((size_t)_sg.mtl.cur_ub_offset + data->size) <= (size_t)_sg.mtl.ub_size); + SOKOL_ASSERT((_sg.mtl.cur_ub_offset & (_SG_MTL_UB_ALIGN-1)) == 0); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline && _sg.mtl.state_cache.cur_pipeline->shader); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->slot.id == _sg.mtl.state_cache.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->shader->slot.id == _sg.mtl.state_cache.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(ub_index < _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); + SOKOL_ASSERT(data->size == _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); + + // copy to global uniform buffer, record offset into cmd encoder, and advance offset + uint8_t* dst = &_sg.mtl.cur_ub_base_ptr[_sg.mtl.cur_ub_offset]; + memcpy(dst, data->ptr, data->size); + if (stage_index == SG_SHADERSTAGE_VS) { + [_sg.mtl.cmd_encoder setVertexBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:(NSUInteger)ub_index]; + _sg_stats_add(metal.uniforms.num_set_vertex_buffer_offset, 1); + } else { + [_sg.mtl.cmd_encoder setFragmentBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:(NSUInteger)ub_index]; + _sg_stats_add(metal.uniforms.num_set_fragment_buffer_offset, 1); + } + _sg.mtl.cur_ub_offset = _sg_roundup(_sg.mtl.cur_ub_offset + (int)data->size, _SG_MTL_UB_ALIGN); +} + +_SOKOL_PRIVATE void _sg_mtl_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline && (_sg.mtl.state_cache.cur_pipeline->slot.id == _sg.mtl.state_cache.cur_pipeline_id.id)); + if (SG_INDEXTYPE_NONE != _sg.mtl.state_cache.cur_pipeline->cmn.index_type) { + // indexed rendering + SOKOL_ASSERT(_sg.mtl.state_cache.cur_indexbuffer && (_sg.mtl.state_cache.cur_indexbuffer->slot.id == _sg.mtl.state_cache.cur_indexbuffer_id.id)); + const _sg_buffer_t* ib = _sg.mtl.state_cache.cur_indexbuffer; + SOKOL_ASSERT(ib->mtl.buf[ib->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + const NSUInteger index_buffer_offset = (NSUInteger) (_sg.mtl.state_cache.cur_indexbuffer_offset + base_element * _sg.mtl.state_cache.cur_pipeline->mtl.index_size); + [_sg.mtl.cmd_encoder drawIndexedPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl.prim_type + indexCount:(NSUInteger)num_elements + indexType:_sg.mtl.state_cache.cur_pipeline->mtl.index_type + indexBuffer:_sg_mtl_id(ib->mtl.buf[ib->cmn.active_slot]) + indexBufferOffset:index_buffer_offset + instanceCount:(NSUInteger)num_instances]; + } else { + // non-indexed rendering + [_sg.mtl.cmd_encoder drawPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl.prim_type + vertexStart:(NSUInteger)base_element + vertexCount:(NSUInteger)num_elements + instanceCount:(NSUInteger)num_instances]; + } +} + +_SOKOL_PRIVATE void _sg_mtl_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + __unsafe_unretained id mtl_buf = _sg_mtl_id(buf->mtl.buf[buf->cmn.active_slot]); + void* dst_ptr = [mtl_buf contents]; + memcpy(dst_ptr, data->ptr, data->size); + #if defined(_SG_TARGET_MACOS) + if (_sg_mtl_resource_options_storage_mode_managed_or_shared() == MTLResourceStorageModeManaged) { + [mtl_buf didModifyRange:NSMakeRange(0, data->size)]; + } + #endif +} + +_SOKOL_PRIVATE void _sg_mtl_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + if (new_frame) { + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + } + __unsafe_unretained id mtl_buf = _sg_mtl_id(buf->mtl.buf[buf->cmn.active_slot]); + uint8_t* dst_ptr = (uint8_t*) [mtl_buf contents]; + dst_ptr += buf->cmn.append_pos; + memcpy(dst_ptr, data->ptr, data->size); + #if defined(_SG_TARGET_MACOS) + if (_sg_mtl_resource_options_storage_mode_managed_or_shared() == MTLResourceStorageModeManaged) { + [mtl_buf didModifyRange:NSMakeRange((NSUInteger)buf->cmn.append_pos, (NSUInteger)data->size)]; + } + #endif +} + +_SOKOL_PRIVATE void _sg_mtl_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + if (++img->cmn.active_slot >= img->cmn.num_slots) { + img->cmn.active_slot = 0; + } + __unsafe_unretained id mtl_tex = _sg_mtl_id(img->mtl.tex[img->cmn.active_slot]); + _sg_mtl_copy_image_data(img, mtl_tex, data); +} + +_SOKOL_PRIVATE void _sg_mtl_push_debug_group(const char* name) { + SOKOL_ASSERT(name); + if (_sg.mtl.cmd_encoder) { + [_sg.mtl.cmd_encoder pushDebugGroup:[NSString stringWithUTF8String:name]]; + } +} + +_SOKOL_PRIVATE void _sg_mtl_pop_debug_group(void) { + if (_sg.mtl.cmd_encoder) { + [_sg.mtl.cmd_encoder popDebugGroup]; + } +} + +// ██ ██ ███████ ██████ ██████ ██████ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ █ ██ █████ ██████ ██ ███ ██████ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███ ███ ███████ ██████ ██████ ██ ██████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>webgpu +// >>wgpu +#elif defined(SOKOL_WGPU) + +_SOKOL_PRIVATE WGPUBufferUsageFlags _sg_wgpu_buffer_usage(sg_buffer_type t, sg_usage u) { + WGPUBufferUsageFlags res = 0; + if (SG_BUFFERTYPE_VERTEXBUFFER == t) { + res = WGPUBufferUsage_Vertex; + } else if (SG_BUFFERTYPE_STORAGEBUFFER == t) { + res = WGPUBufferUsage_Storage; + } else { + res = WGPUBufferUsage_Index; + } + if (SG_USAGE_IMMUTABLE != u) { + res |= WGPUBufferUsage_CopyDst; + } + return res; +} + +_SOKOL_PRIVATE WGPULoadOp _sg_wgpu_load_op(WGPUTextureView view, sg_load_action a) { + if (0 == view) { + return WGPULoadOp_Undefined; + } else switch (a) { + case SG_LOADACTION_CLEAR: + case SG_LOADACTION_DONTCARE: + return WGPULoadOp_Clear; + case SG_LOADACTION_LOAD: + return WGPULoadOp_Load; + default: + SOKOL_UNREACHABLE; + return WGPULoadOp_Force32; + } +} + +_SOKOL_PRIVATE WGPUStoreOp _sg_wgpu_store_op(WGPUTextureView view, sg_store_action a) { + if (0 == view) { + return WGPUStoreOp_Undefined; + } else switch (a) { + case SG_STOREACTION_STORE: + return WGPUStoreOp_Store; + case SG_STOREACTION_DONTCARE: + return WGPUStoreOp_Discard; + default: + SOKOL_UNREACHABLE; + return WGPUStoreOp_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureViewDimension _sg_wgpu_texture_view_dimension(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return WGPUTextureViewDimension_2D; + case SG_IMAGETYPE_CUBE: return WGPUTextureViewDimension_Cube; + case SG_IMAGETYPE_3D: return WGPUTextureViewDimension_3D; + case SG_IMAGETYPE_ARRAY: return WGPUTextureViewDimension_2DArray; + default: SOKOL_UNREACHABLE; return WGPUTextureViewDimension_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureDimension _sg_wgpu_texture_dimension(sg_image_type t) { + if (SG_IMAGETYPE_3D == t) { + return WGPUTextureDimension_3D; + } else { + return WGPUTextureDimension_2D; + } +} + +_SOKOL_PRIVATE WGPUTextureSampleType _sg_wgpu_texture_sample_type(sg_image_sample_type t) { + switch (t) { + case SG_IMAGESAMPLETYPE_FLOAT: return WGPUTextureSampleType_Float; + case SG_IMAGESAMPLETYPE_DEPTH: return WGPUTextureSampleType_Depth; + case SG_IMAGESAMPLETYPE_SINT: return WGPUTextureSampleType_Sint; + case SG_IMAGESAMPLETYPE_UINT: return WGPUTextureSampleType_Uint; + case SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT: return WGPUTextureSampleType_UnfilterableFloat; + default: SOKOL_UNREACHABLE; return WGPUTextureSampleType_Force32; + } +} + +_SOKOL_PRIVATE WGPUSamplerBindingType _sg_wgpu_sampler_binding_type(sg_sampler_type t) { + switch (t) { + case SG_SAMPLERTYPE_FILTERING: return WGPUSamplerBindingType_Filtering; + case SG_SAMPLERTYPE_COMPARISON: return WGPUSamplerBindingType_Comparison; + case SG_SAMPLERTYPE_NONFILTERING: return WGPUSamplerBindingType_NonFiltering; + default: SOKOL_UNREACHABLE; return WGPUSamplerBindingType_Force32; + } +} + +_SOKOL_PRIVATE WGPUAddressMode _sg_wgpu_sampler_address_mode(sg_wrap m) { + switch (m) { + case SG_WRAP_REPEAT: + return WGPUAddressMode_Repeat; + case SG_WRAP_CLAMP_TO_EDGE: + case SG_WRAP_CLAMP_TO_BORDER: + return WGPUAddressMode_ClampToEdge; + case SG_WRAP_MIRRORED_REPEAT: + return WGPUAddressMode_MirrorRepeat; + default: + SOKOL_UNREACHABLE; + return WGPUAddressMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_minmag_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + return WGPUFilterMode_Nearest; + case SG_FILTER_LINEAR: + return WGPUFilterMode_Linear; + default: + SOKOL_UNREACHABLE; + return WGPUFilterMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUMipmapFilterMode _sg_wgpu_sampler_mipmap_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NONE: + case SG_FILTER_NEAREST: + return WGPUMipmapFilterMode_Nearest; + case SG_FILTER_LINEAR: + return WGPUMipmapFilterMode_Linear; + default: + SOKOL_UNREACHABLE; + return WGPUMipmapFilterMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUIndexFormat _sg_wgpu_indexformat(sg_index_type t) { + // NOTE: there's no WGPUIndexFormat_None + return (t == SG_INDEXTYPE_UINT16) ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32; +} + +_SOKOL_PRIVATE WGPUIndexFormat _sg_wgpu_stripindexformat(sg_primitive_type prim_type, sg_index_type idx_type) { + if (idx_type == SG_INDEXTYPE_NONE) { + return WGPUIndexFormat_Undefined; + } else if ((prim_type == SG_PRIMITIVETYPE_LINE_STRIP) || (prim_type == SG_PRIMITIVETYPE_TRIANGLE_STRIP)) { + return _sg_wgpu_indexformat(idx_type); + } else { + return WGPUIndexFormat_Undefined; + } +} + +_SOKOL_PRIVATE WGPUVertexStepMode _sg_wgpu_stepmode(sg_vertex_step s) { + return (s == SG_VERTEXSTEP_PER_VERTEX) ? WGPUVertexStepMode_Vertex : WGPUVertexStepMode_Instance; +} + +_SOKOL_PRIVATE WGPUVertexFormat _sg_wgpu_vertexformat(sg_vertex_format f) { + switch (f) { + case SG_VERTEXFORMAT_FLOAT: return WGPUVertexFormat_Float32; + case SG_VERTEXFORMAT_FLOAT2: return WGPUVertexFormat_Float32x2; + case SG_VERTEXFORMAT_FLOAT3: return WGPUVertexFormat_Float32x3; + case SG_VERTEXFORMAT_FLOAT4: return WGPUVertexFormat_Float32x4; + case SG_VERTEXFORMAT_BYTE4: return WGPUVertexFormat_Sint8x4; + case SG_VERTEXFORMAT_BYTE4N: return WGPUVertexFormat_Snorm8x4; + case SG_VERTEXFORMAT_UBYTE4: return WGPUVertexFormat_Uint8x4; + case SG_VERTEXFORMAT_UBYTE4N: return WGPUVertexFormat_Unorm8x4; + case SG_VERTEXFORMAT_SHORT2: return WGPUVertexFormat_Sint16x2; + case SG_VERTEXFORMAT_SHORT2N: return WGPUVertexFormat_Snorm16x2; + case SG_VERTEXFORMAT_USHORT2N: return WGPUVertexFormat_Unorm16x2; + case SG_VERTEXFORMAT_SHORT4: return WGPUVertexFormat_Sint16x4; + case SG_VERTEXFORMAT_SHORT4N: return WGPUVertexFormat_Snorm16x4; + case SG_VERTEXFORMAT_USHORT4N: return WGPUVertexFormat_Unorm16x4; + case SG_VERTEXFORMAT_HALF2: return WGPUVertexFormat_Float16x2; + case SG_VERTEXFORMAT_HALF4: return WGPUVertexFormat_Float16x4; + // FIXME! UINT10_N2 (see https://github.com/gpuweb/gpuweb/issues/4275) + case SG_VERTEXFORMAT_UINT10_N2: return WGPUVertexFormat_Undefined; + default: + SOKOL_UNREACHABLE; + return WGPUVertexFormat_Force32; + } +} + +_SOKOL_PRIVATE WGPUPrimitiveTopology _sg_wgpu_topology(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return WGPUPrimitiveTopology_PointList; + case SG_PRIMITIVETYPE_LINES: return WGPUPrimitiveTopology_LineList; + case SG_PRIMITIVETYPE_LINE_STRIP: return WGPUPrimitiveTopology_LineStrip; + case SG_PRIMITIVETYPE_TRIANGLES: return WGPUPrimitiveTopology_TriangleList; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return WGPUPrimitiveTopology_TriangleStrip; + default: + SOKOL_UNREACHABLE; + return WGPUPrimitiveTopology_Force32; + } +} + +_SOKOL_PRIVATE WGPUFrontFace _sg_wgpu_frontface(sg_face_winding fw) { + return (fw == SG_FACEWINDING_CCW) ? WGPUFrontFace_CCW : WGPUFrontFace_CW; +} + +_SOKOL_PRIVATE WGPUCullMode _sg_wgpu_cullmode(sg_cull_mode cm) { + switch (cm) { + case SG_CULLMODE_NONE: return WGPUCullMode_None; + case SG_CULLMODE_FRONT: return WGPUCullMode_Front; + case SG_CULLMODE_BACK: return WGPUCullMode_Back; + default: + SOKOL_UNREACHABLE; + return WGPUCullMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) { + switch (p) { + case SG_PIXELFORMAT_NONE: return WGPUTextureFormat_Undefined; + case SG_PIXELFORMAT_R8: return WGPUTextureFormat_R8Unorm; + case SG_PIXELFORMAT_R8SN: return WGPUTextureFormat_R8Snorm; + case SG_PIXELFORMAT_R8UI: return WGPUTextureFormat_R8Uint; + case SG_PIXELFORMAT_R8SI: return WGPUTextureFormat_R8Sint; + case SG_PIXELFORMAT_R16UI: return WGPUTextureFormat_R16Uint; + case SG_PIXELFORMAT_R16SI: return WGPUTextureFormat_R16Sint; + case SG_PIXELFORMAT_R16F: return WGPUTextureFormat_R16Float; + case SG_PIXELFORMAT_RG8: return WGPUTextureFormat_RG8Unorm; + case SG_PIXELFORMAT_RG8SN: return WGPUTextureFormat_RG8Snorm; + case SG_PIXELFORMAT_RG8UI: return WGPUTextureFormat_RG8Uint; + case SG_PIXELFORMAT_RG8SI: return WGPUTextureFormat_RG8Sint; + case SG_PIXELFORMAT_R32UI: return WGPUTextureFormat_R32Uint; + case SG_PIXELFORMAT_R32SI: return WGPUTextureFormat_R32Sint; + case SG_PIXELFORMAT_R32F: return WGPUTextureFormat_R32Float; + case SG_PIXELFORMAT_RG16UI: return WGPUTextureFormat_RG16Uint; + case SG_PIXELFORMAT_RG16SI: return WGPUTextureFormat_RG16Sint; + case SG_PIXELFORMAT_RG16F: return WGPUTextureFormat_RG16Float; + case SG_PIXELFORMAT_RGBA8: return WGPUTextureFormat_RGBA8Unorm; + case SG_PIXELFORMAT_SRGB8A8: return WGPUTextureFormat_RGBA8UnormSrgb; + case SG_PIXELFORMAT_RGBA8SN: return WGPUTextureFormat_RGBA8Snorm; + case SG_PIXELFORMAT_RGBA8UI: return WGPUTextureFormat_RGBA8Uint; + case SG_PIXELFORMAT_RGBA8SI: return WGPUTextureFormat_RGBA8Sint; + case SG_PIXELFORMAT_BGRA8: return WGPUTextureFormat_BGRA8Unorm; + case SG_PIXELFORMAT_RGB10A2: return WGPUTextureFormat_RGB10A2Unorm; + case SG_PIXELFORMAT_RG11B10F: return WGPUTextureFormat_RG11B10Ufloat; + case SG_PIXELFORMAT_RG32UI: return WGPUTextureFormat_RG32Uint; + case SG_PIXELFORMAT_RG32SI: return WGPUTextureFormat_RG32Sint; + case SG_PIXELFORMAT_RG32F: return WGPUTextureFormat_RG32Float; + case SG_PIXELFORMAT_RGBA16UI: return WGPUTextureFormat_RGBA16Uint; + case SG_PIXELFORMAT_RGBA16SI: return WGPUTextureFormat_RGBA16Sint; + case SG_PIXELFORMAT_RGBA16F: return WGPUTextureFormat_RGBA16Float; + case SG_PIXELFORMAT_RGBA32UI: return WGPUTextureFormat_RGBA32Uint; + case SG_PIXELFORMAT_RGBA32SI: return WGPUTextureFormat_RGBA32Sint; + case SG_PIXELFORMAT_RGBA32F: return WGPUTextureFormat_RGBA32Float; + case SG_PIXELFORMAT_DEPTH: return WGPUTextureFormat_Depth32Float; + case SG_PIXELFORMAT_DEPTH_STENCIL: return WGPUTextureFormat_Depth32FloatStencil8; + case SG_PIXELFORMAT_BC1_RGBA: return WGPUTextureFormat_BC1RGBAUnorm; + case SG_PIXELFORMAT_BC2_RGBA: return WGPUTextureFormat_BC2RGBAUnorm; + case SG_PIXELFORMAT_BC3_RGBA: return WGPUTextureFormat_BC3RGBAUnorm; + case SG_PIXELFORMAT_BC3_SRGBA: return WGPUTextureFormat_BC3RGBAUnormSrgb; + case SG_PIXELFORMAT_BC4_R: return WGPUTextureFormat_BC4RUnorm; + case SG_PIXELFORMAT_BC4_RSN: return WGPUTextureFormat_BC4RSnorm; + case SG_PIXELFORMAT_BC5_RG: return WGPUTextureFormat_BC5RGUnorm; + case SG_PIXELFORMAT_BC5_RGSN: return WGPUTextureFormat_BC5RGSnorm; + case SG_PIXELFORMAT_BC6H_RGBF: return WGPUTextureFormat_BC6HRGBFloat; + case SG_PIXELFORMAT_BC6H_RGBUF: return WGPUTextureFormat_BC6HRGBUfloat; + case SG_PIXELFORMAT_BC7_RGBA: return WGPUTextureFormat_BC7RGBAUnorm; + case SG_PIXELFORMAT_BC7_SRGBA: return WGPUTextureFormat_BC7RGBAUnormSrgb; + case SG_PIXELFORMAT_ETC2_RGB8: return WGPUTextureFormat_ETC2RGB8Unorm; + case SG_PIXELFORMAT_ETC2_RGB8A1: return WGPUTextureFormat_ETC2RGB8A1Unorm; + case SG_PIXELFORMAT_ETC2_RGBA8: return WGPUTextureFormat_ETC2RGBA8Unorm; + case SG_PIXELFORMAT_ETC2_SRGB8: return WGPUTextureFormat_ETC2RGB8UnormSrgb; + case SG_PIXELFORMAT_ETC2_SRGB8A8: return WGPUTextureFormat_ETC2RGBA8UnormSrgb; + case SG_PIXELFORMAT_EAC_R11: return WGPUTextureFormat_EACR11Unorm; + case SG_PIXELFORMAT_EAC_R11SN: return WGPUTextureFormat_EACR11Snorm; + case SG_PIXELFORMAT_EAC_RG11: return WGPUTextureFormat_EACRG11Unorm; + case SG_PIXELFORMAT_EAC_RG11SN: return WGPUTextureFormat_EACRG11Snorm; + case SG_PIXELFORMAT_RGB9E5: return WGPUTextureFormat_RGB9E5Ufloat; + case SG_PIXELFORMAT_ASTC_4x4_RGBA: return WGPUTextureFormat_ASTC4x4Unorm; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return WGPUTextureFormat_ASTC4x4UnormSrgb; + // NOT SUPPORTED + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + return WGPUTextureFormat_Undefined; + + default: + SOKOL_UNREACHABLE; + return WGPUTextureFormat_Force32; + } +} + +_SOKOL_PRIVATE WGPUCompareFunction _sg_wgpu_comparefunc(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return WGPUCompareFunction_Never; + case SG_COMPAREFUNC_LESS: return WGPUCompareFunction_Less; + case SG_COMPAREFUNC_EQUAL: return WGPUCompareFunction_Equal; + case SG_COMPAREFUNC_LESS_EQUAL: return WGPUCompareFunction_LessEqual; + case SG_COMPAREFUNC_GREATER: return WGPUCompareFunction_Greater; + case SG_COMPAREFUNC_NOT_EQUAL: return WGPUCompareFunction_NotEqual; + case SG_COMPAREFUNC_GREATER_EQUAL: return WGPUCompareFunction_GreaterEqual; + case SG_COMPAREFUNC_ALWAYS: return WGPUCompareFunction_Always; + default: + SOKOL_UNREACHABLE; + return WGPUCompareFunction_Force32; + } +} + +_SOKOL_PRIVATE WGPUStencilOperation _sg_wgpu_stencilop(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return WGPUStencilOperation_Keep; + case SG_STENCILOP_ZERO: return WGPUStencilOperation_Zero; + case SG_STENCILOP_REPLACE: return WGPUStencilOperation_Replace; + case SG_STENCILOP_INCR_CLAMP: return WGPUStencilOperation_IncrementClamp; + case SG_STENCILOP_DECR_CLAMP: return WGPUStencilOperation_DecrementClamp; + case SG_STENCILOP_INVERT: return WGPUStencilOperation_Invert; + case SG_STENCILOP_INCR_WRAP: return WGPUStencilOperation_IncrementWrap; + case SG_STENCILOP_DECR_WRAP: return WGPUStencilOperation_DecrementWrap; + default: + SOKOL_UNREACHABLE; + return WGPUStencilOperation_Force32; + } +} + +_SOKOL_PRIVATE WGPUBlendOperation _sg_wgpu_blendop(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return WGPUBlendOperation_Add; + case SG_BLENDOP_SUBTRACT: return WGPUBlendOperation_Subtract; + case SG_BLENDOP_REVERSE_SUBTRACT: return WGPUBlendOperation_ReverseSubtract; + default: + SOKOL_UNREACHABLE; + return WGPUBlendOperation_Force32; + } +} + +_SOKOL_PRIVATE WGPUBlendFactor _sg_wgpu_blendfactor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return WGPUBlendFactor_Zero; + case SG_BLENDFACTOR_ONE: return WGPUBlendFactor_One; + case SG_BLENDFACTOR_SRC_COLOR: return WGPUBlendFactor_Src; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return WGPUBlendFactor_OneMinusSrc; + case SG_BLENDFACTOR_SRC_ALPHA: return WGPUBlendFactor_SrcAlpha; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return WGPUBlendFactor_OneMinusSrcAlpha; + case SG_BLENDFACTOR_DST_COLOR: return WGPUBlendFactor_Dst; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return WGPUBlendFactor_OneMinusDst; + case SG_BLENDFACTOR_DST_ALPHA: return WGPUBlendFactor_DstAlpha; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return WGPUBlendFactor_OneMinusDstAlpha; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return WGPUBlendFactor_SrcAlphaSaturated; + case SG_BLENDFACTOR_BLEND_COLOR: return WGPUBlendFactor_Constant; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return WGPUBlendFactor_OneMinusConstant; + // FIXME: separate blend alpha value not supported? + case SG_BLENDFACTOR_BLEND_ALPHA: return WGPUBlendFactor_Constant; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return WGPUBlendFactor_OneMinusConstant; + default: + SOKOL_UNREACHABLE; + return WGPUBlendFactor_Force32; + } +} + +_SOKOL_PRIVATE WGPUColorWriteMaskFlags _sg_wgpu_colorwritemask(uint8_t m) { + WGPUColorWriteMaskFlags res = 0; + if (0 != (m & SG_COLORMASK_R)) { + res |= WGPUColorWriteMask_Red; + } + if (0 != (m & SG_COLORMASK_G)) { + res |= WGPUColorWriteMask_Green; + } + if (0 != (m & SG_COLORMASK_B)) { + res |= WGPUColorWriteMask_Blue; + } + if (0 != (m & SG_COLORMASK_A)) { + res |= WGPUColorWriteMask_Alpha; + } + return res; +} + +// image/sampler binding on wgpu follows this convention: +// +// - all images and sampler are in @group(1) +// - vertex stage images start at @binding(0) +// - vertex stage samplers start at @binding(16) +// - vertex stage storage buffers start at @binding(32) +// - fragment stage images start at @binding(48) +// - fragment stage samplers start at @binding(64) +// - fragment stage storage buffers start at @binding(80) +// +_SOKOL_PRIVATE uint32_t _sg_wgpu_image_binding(sg_shader_stage stage, int img_slot) { + SOKOL_ASSERT((img_slot >= 0) && (img_slot < 16)); + if (SG_SHADERSTAGE_VS == stage) { + return 0 + (uint32_t)img_slot; + } else { + return 48 + (uint32_t)img_slot; + } +} + +_SOKOL_PRIVATE uint32_t _sg_wgpu_sampler_binding(sg_shader_stage stage, int smp_slot) { + SOKOL_ASSERT((smp_slot >= 0) && (smp_slot < 16)); + if (SG_SHADERSTAGE_VS == stage) { + return 16 + (uint32_t)smp_slot; + } else { + return 64 + (uint32_t)smp_slot; + } +} + +_SOKOL_PRIVATE uint32_t _sg_wgpu_storagebuffer_binding(sg_shader_stage stage, int sbuf_slot) { + SOKOL_ASSERT((sbuf_slot >= 0) && (sbuf_slot < 16)); + if (SG_SHADERSTAGE_VS == stage) { + return 32 + (uint32_t)sbuf_slot; + } else { + return 80 + (uint32_t)sbuf_slot; + } +} + +_SOKOL_PRIVATE WGPUShaderStage _sg_wgpu_shader_stage(sg_shader_stage stage) { + switch (stage) { + case SG_SHADERSTAGE_VS: return WGPUShaderStage_Vertex; + case SG_SHADERSTAGE_FS: return WGPUShaderStage_Fragment; + default: SOKOL_UNREACHABLE; return WGPUShaderStage_None; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { + _sg.backend = SG_BACKEND_WGPU; + _sg.features.origin_top_left = true; + _sg.features.image_clamp_to_border = false; + _sg.features.mrt_independent_blend_state = true; + _sg.features.mrt_independent_write_mask = true; + _sg.features.storage_buffer = true; + + wgpuDeviceGetLimits(_sg.wgpu.dev, &_sg.wgpu.limits); + + const WGPULimits* l = &_sg.wgpu.limits.limits; + _sg.limits.max_image_size_2d = (int) l->maxTextureDimension2D; + _sg.limits.max_image_size_cube = (int) l->maxTextureDimension2D; // not a bug, see: https://github.com/gpuweb/gpuweb/issues/1327 + _sg.limits.max_image_size_3d = (int) l->maxTextureDimension3D; + _sg.limits.max_image_size_array = (int) l->maxTextureDimension2D; + _sg.limits.max_image_array_layers = (int) l->maxTextureArrayLayers; + _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; + + // NOTE: no WGPUTextureFormat_R16Unorm + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_SRGB8A8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG8SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + + // FIXME: can be made renderable via extension + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); + + // NOTE: msaa rendering is possible in WebGPU, but no resolve + // which is a combination that's not currently supported in sokol-gfx + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R8UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG8UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG16SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + + if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_Float32Filterable)) { + _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } else { + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); + + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGB9E5]); + + if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_TextureCompressionBC)) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_SRGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_SRGBA]); + } + if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_TextureCompressionETC2)) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]); + } + + if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_TextureCompressionASTC)) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_SRGBA]); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_uniform_buffer_init(const sg_desc* desc) { + SOKOL_ASSERT(0 == _sg.wgpu.uniform.staging); + SOKOL_ASSERT(0 == _sg.wgpu.uniform.buf); + SOKOL_ASSERT(0 == _sg.wgpu.uniform.bind.group_layout); + SOKOL_ASSERT(0 == _sg.wgpu.uniform.bind.group); + + // Add the max-uniform-update size (64 KB) to the requested buffer size, + // this is to prevent validation errors in the WebGPU implementation + // if the entire buffer size is used per frame. 64 KB is the allowed + // max uniform update size on NVIDIA + // + // FIXME: is this still needed? + _sg.wgpu.uniform.num_bytes = (uint32_t)(desc->uniform_buffer_size + _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE); + _sg.wgpu.uniform.staging = (uint8_t*)_sg_malloc(_sg.wgpu.uniform.num_bytes); + + WGPUBufferDescriptor ub_desc; + _sg_clear(&ub_desc, sizeof(ub_desc)); + ub_desc.size = _sg.wgpu.uniform.num_bytes; + ub_desc.usage = WGPUBufferUsage_Uniform|WGPUBufferUsage_CopyDst; + _sg.wgpu.uniform.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &ub_desc); + SOKOL_ASSERT(_sg.wgpu.uniform.buf); + + WGPUBindGroupLayoutEntry ub_bgle_desc[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + _sg_clear(ub_bgle_desc, sizeof(ub_bgle_desc)); + for (uint32_t stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + WGPUShaderStage vis = (stage_index == SG_SHADERSTAGE_VS) ? WGPUShaderStage_Vertex : WGPUShaderStage_Fragment; + for (uint32_t ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + uint32_t bind_index = stage_index * SG_MAX_SHADERSTAGE_UBS + ub_index; + ub_bgle_desc[stage_index][ub_index].binding = bind_index; + ub_bgle_desc[stage_index][ub_index].visibility = vis; + ub_bgle_desc[stage_index][ub_index].buffer.type = WGPUBufferBindingType_Uniform; + ub_bgle_desc[stage_index][ub_index].buffer.hasDynamicOffset = true; + } + } + + WGPUBindGroupLayoutDescriptor ub_bgl_desc; + _sg_clear(&ub_bgl_desc, sizeof(ub_bgl_desc)); + ub_bgl_desc.entryCount = SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS; + ub_bgl_desc.entries = &ub_bgle_desc[0][0]; + _sg.wgpu.uniform.bind.group_layout = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &ub_bgl_desc); + SOKOL_ASSERT(_sg.wgpu.uniform.bind.group_layout); + + WGPUBindGroupEntry ub_bge[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + _sg_clear(ub_bge, sizeof(ub_bge)); + for (uint32_t stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + for (uint32_t ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + uint32_t bind_index = stage_index * SG_MAX_SHADERSTAGE_UBS + ub_index; + ub_bge[stage_index][ub_index].binding = bind_index; + ub_bge[stage_index][ub_index].buffer = _sg.wgpu.uniform.buf; + ub_bge[stage_index][ub_index].size = _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE; + } + } + WGPUBindGroupDescriptor bg_desc; + _sg_clear(&bg_desc, sizeof(bg_desc)); + bg_desc.layout = _sg.wgpu.uniform.bind.group_layout; + bg_desc.entryCount = SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS; + bg_desc.entries = &ub_bge[0][0]; + _sg.wgpu.uniform.bind.group = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); + SOKOL_ASSERT(_sg.wgpu.uniform.bind.group); +} + +_SOKOL_PRIVATE void _sg_wgpu_uniform_buffer_discard(void) { + if (_sg.wgpu.uniform.buf) { + wgpuBufferRelease(_sg.wgpu.uniform.buf); + _sg.wgpu.uniform.buf = 0; + } + if (_sg.wgpu.uniform.bind.group) { + wgpuBindGroupRelease(_sg.wgpu.uniform.bind.group); + _sg.wgpu.uniform.bind.group = 0; + } + if (_sg.wgpu.uniform.bind.group_layout) { + wgpuBindGroupLayoutRelease(_sg.wgpu.uniform.bind.group_layout); + _sg.wgpu.uniform.bind.group_layout = 0; + } + if (_sg.wgpu.uniform.staging) { + _sg_free(_sg.wgpu.uniform.staging); + _sg.wgpu.uniform.staging = 0; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_uniform_buffer_on_begin_pass(void) { + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, + 0, // groupIndex 0 is reserved for uniform buffers + _sg.wgpu.uniform.bind.group, + SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS, + &_sg.wgpu.uniform.bind.offsets[0][0]); +} + +_SOKOL_PRIVATE void _sg_wgpu_uniform_buffer_on_commit(void) { + wgpuQueueWriteBuffer(_sg.wgpu.queue, _sg.wgpu.uniform.buf, 0, _sg.wgpu.uniform.staging, _sg.wgpu.uniform.offset); + _sg_stats_add(wgpu.uniforms.size_write_buffer, _sg.wgpu.uniform.offset); + _sg.wgpu.uniform.offset = 0; + _sg_clear(&_sg.wgpu.uniform.bind.offsets[0][0], sizeof(_sg.wgpu.uniform.bind.offsets)); +} + +_SOKOL_PRIVATE void _sg_wgpu_bindgroups_pool_init(const sg_desc* desc) { + SOKOL_ASSERT((desc->wgpu_bindgroups_cache_size > 0) && (desc->wgpu_bindgroups_cache_size < _SG_MAX_POOL_SIZE)); + _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool; + SOKOL_ASSERT(0 == p->bindgroups); + const int pool_size = desc->wgpu_bindgroups_cache_size; + _sg_init_pool(&p->pool, pool_size); + size_t pool_byte_size = sizeof(_sg_wgpu_bindgroup_t) * (size_t)p->pool.size; + p->bindgroups = (_sg_wgpu_bindgroup_t*) _sg_malloc_clear(pool_byte_size); +} + +_SOKOL_PRIVATE void _sg_wgpu_bindgroups_pool_discard(void) { + _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool; + SOKOL_ASSERT(p->bindgroups); + _sg_free(p->bindgroups); p->bindgroups = 0; + _sg_discard_pool(&p->pool); +} + +_SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_bindgroup_at(uint32_t bg_id) { + SOKOL_ASSERT(SG_INVALID_ID != bg_id); + _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool; + int slot_index = _sg_slot_index(bg_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->pool.size)); + return &p->bindgroups[slot_index]; +} + +_SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_lookup_bindgroup(uint32_t bg_id) { + if (SG_INVALID_ID != bg_id) { + _sg_wgpu_bindgroup_t* bg = _sg_wgpu_bindgroup_at(bg_id); + if (bg->slot.id == bg_id) { + return bg; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_wgpu_bindgroup_handle_t _sg_wgpu_alloc_bindgroup(void) { + _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool; + _sg_wgpu_bindgroup_handle_t res; + int slot_index = _sg_pool_alloc_index(&p->pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&p->pool, &p->bindgroups[slot_index].slot, slot_index); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(WGPU_BINDGROUPS_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE void _sg_wgpu_dealloc_bindgroup(_sg_wgpu_bindgroup_t* bg) { + SOKOL_ASSERT(bg && (bg->slot.state == SG_RESOURCESTATE_ALLOC) && (bg->slot.id != SG_INVALID_ID)); + _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool; + _sg_pool_free_index(&p->pool, _sg_slot_index(bg->slot.id)); + _sg_reset_slot(&bg->slot); +} + +_SOKOL_PRIVATE void _sg_wgpu_reset_bindgroup_to_alloc_state(_sg_wgpu_bindgroup_t* bg) { + SOKOL_ASSERT(bg); + _sg_slot_t slot = bg->slot; + _sg_clear(bg, sizeof(_sg_wgpu_bindgroup_t)); + bg->slot = slot; + bg->slot.state = SG_RESOURCESTATE_ALLOC; +} + +// MurmurHash64B (see: https://github.com/aappleby/smhasher/blob/61a0530f28277f2e850bfc39600ce61d02b518de/src/MurmurHash2.cpp#L142) +_SOKOL_PRIVATE uint64_t _sg_wgpu_hash(const void* key, int len, uint64_t seed) { + const uint32_t m = 0x5bd1e995; + const int r = 24; + uint32_t h1 = (uint32_t)seed ^ (uint32_t)len; + uint32_t h2 = (uint32_t)(seed >> 32); + const uint32_t * data = (const uint32_t *)key; + while (len >= 8) { + uint32_t k1 = *data++; + k1 *= m; k1 ^= k1 >> r; k1 *= m; + h1 *= m; h1 ^= k1; + len -= 4; + uint32_t k2 = *data++; + k2 *= m; k2 ^= k2 >> r; k2 *= m; + h2 *= m; h2 ^= k2; + len -= 4; + } + if (len >= 4) { + uint32_t k1 = *data++; + k1 *= m; k1 ^= k1 >> r; k1 *= m; + h1 *= m; h1 ^= k1; + len -= 4; + } + switch(len) { + case 3: h2 ^= (uint32_t)(((unsigned char*)data)[2] << 16); + case 2: h2 ^= (uint32_t)(((unsigned char*)data)[1] << 8); + case 1: h2 ^= ((unsigned char*)data)[0]; + h2 *= m; + }; + h1 ^= h2 >> 18; h1 *= m; + h2 ^= h1 >> 22; h2 *= m; + h1 ^= h2 >> 17; h1 *= m; + h2 ^= h1 >> 19; h2 *= m; + uint64_t h = h1; + h = (h << 32) | h2; + return h; +} + +_SOKOL_PRIVATE void _sg_wgpu_init_bindgroups_cache_key(_sg_wgpu_bindgroups_cache_key_t* key, const _sg_bindings_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); + SOKOL_ASSERT(bnd->num_vs_imgs <= SG_MAX_SHADERSTAGE_IMAGES); + SOKOL_ASSERT(bnd->num_vs_smps <= SG_MAX_SHADERSTAGE_SAMPLERS); + SOKOL_ASSERT(bnd->num_vs_sbufs <= SG_MAX_SHADERSTAGE_STORAGEBUFFERS); + SOKOL_ASSERT(bnd->num_fs_imgs <= SG_MAX_SHADERSTAGE_IMAGES); + SOKOL_ASSERT(bnd->num_fs_smps <= SG_MAX_SHADERSTAGE_SAMPLERS); + SOKOL_ASSERT(bnd->num_fs_sbufs <= SG_MAX_SHADERSTAGE_STORAGEBUFFERS); + + _sg_clear(key->items, sizeof(key->items)); + key->items[0] = bnd->pip->slot.id; + const int vs_imgs_offset = 1; + const int vs_smps_offset = vs_imgs_offset + SG_MAX_SHADERSTAGE_IMAGES; + const int vs_sbufs_offset = vs_smps_offset + SG_MAX_SHADERSTAGE_SAMPLERS; + const int fs_imgs_offset = vs_sbufs_offset + SG_MAX_SHADERSTAGE_STORAGEBUFFERS; + const int fs_smps_offset = fs_imgs_offset + SG_MAX_SHADERSTAGE_IMAGES; + const int fs_sbufs_offset = fs_smps_offset + SG_MAX_SHADERSTAGE_SAMPLERS; + SOKOL_ASSERT((fs_sbufs_offset + SG_MAX_SHADERSTAGE_STORAGEBUFFERS) == _SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS); + for (int i = 0; i < bnd->num_vs_imgs; i++) { + SOKOL_ASSERT(bnd->vs_imgs[i]); + key->items[vs_imgs_offset + i] = bnd->vs_imgs[i]->slot.id; + } + for (int i = 0; i < bnd->num_vs_smps; i++) { + SOKOL_ASSERT(bnd->vs_smps[i]); + key->items[vs_smps_offset + i] = bnd->vs_smps[i]->slot.id; + } + for (int i = 0; i < bnd->num_vs_sbufs; i++) { + SOKOL_ASSERT(bnd->vs_sbufs[i]); + key->items[vs_sbufs_offset + i] = bnd->vs_sbufs[i]->slot.id; + } + for (int i = 0; i < bnd->num_fs_imgs; i++) { + SOKOL_ASSERT(bnd->fs_imgs[i]); + key->items[fs_imgs_offset + i] = bnd->fs_imgs[i]->slot.id; + } + for (int i = 0; i < bnd->num_fs_smps; i++) { + SOKOL_ASSERT(bnd->fs_smps[i]); + key->items[fs_smps_offset + i] = bnd->fs_smps[i]->slot.id; + } + for (int i = 0; i < bnd->num_fs_sbufs; i++) { + SOKOL_ASSERT(bnd->fs_sbufs[i]); + key->items[fs_sbufs_offset + i] = bnd->fs_sbufs[i]->slot.id; + } + key->hash = _sg_wgpu_hash(&key->items, (int)sizeof(key->items), 0x1234567887654321); +} + +_SOKOL_PRIVATE bool _sg_wgpu_compare_bindgroups_cache_key(_sg_wgpu_bindgroups_cache_key_t* k0, _sg_wgpu_bindgroups_cache_key_t* k1) { + SOKOL_ASSERT(k0 && k1); + if (k0->hash != k1->hash) { + return false; + } + if (memcmp(&k0->items, &k1->items, sizeof(k0->items)) != 0) { + _sg_stats_add(wgpu.bindings.num_bindgroup_cache_hash_vs_key_mismatch, 1); + return false; + } + return true; +} + +_SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_create_bindgroup(_sg_bindings_t* bnd) { + SOKOL_ASSERT(_sg.wgpu.dev); + SOKOL_ASSERT(bnd->pip); + SOKOL_ASSERT(bnd->pip->shader && (bnd->pip->cmn.shader_id.id == bnd->pip->shader->slot.id)); + _sg_stats_add(wgpu.bindings.num_create_bindgroup, 1); + _sg_wgpu_bindgroup_handle_t bg_id = _sg_wgpu_alloc_bindgroup(); + if (bg_id.id == SG_INVALID_ID) { + return 0; + } + _sg_wgpu_bindgroup_t* bg = _sg_wgpu_bindgroup_at(bg_id.id); + SOKOL_ASSERT(bg && (bg->slot.state == SG_RESOURCESTATE_ALLOC)); + + // create wgpu bindgroup object + WGPUBindGroupLayout bgl = bnd->pip->shader->wgpu.bind_group_layout; + SOKOL_ASSERT(bgl); + WGPUBindGroupEntry wgpu_entries[_SG_WGPU_MAX_BINDGROUP_ENTRIES]; + _sg_clear(&wgpu_entries, sizeof(wgpu_entries)); + int bge_index = 0; + for (int i = 0; i < bnd->num_vs_imgs; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_image_binding(SG_SHADERSTAGE_VS, i); + wgpu_entry->textureView = bnd->vs_imgs[i]->wgpu.view; + } + for (int i = 0; i < bnd->num_vs_smps; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_sampler_binding(SG_SHADERSTAGE_VS, i); + wgpu_entry->sampler = bnd->vs_smps[i]->wgpu.smp; + } + for (int i = 0; i < bnd->num_vs_sbufs; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_storagebuffer_binding(SG_SHADERSTAGE_VS, i); + wgpu_entry->buffer = bnd->vs_sbufs[i]->wgpu.buf; + wgpu_entry->size = (uint64_t) bnd->vs_sbufs[i]->cmn.size; + } + for (int i = 0; i < bnd->num_fs_imgs; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_image_binding(SG_SHADERSTAGE_FS, i); + wgpu_entry->textureView = bnd->fs_imgs[i]->wgpu.view; + } + for (int i = 0; i < bnd->num_fs_smps; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_sampler_binding(SG_SHADERSTAGE_FS, i); + wgpu_entry->sampler = bnd->fs_smps[i]->wgpu.smp; + } + for (int i = 0; i < bnd->num_fs_sbufs; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_storagebuffer_binding(SG_SHADERSTAGE_FS, i); + wgpu_entry->buffer = bnd->fs_sbufs[i]->wgpu.buf; + wgpu_entry->size = (uint64_t) bnd->fs_sbufs[i]->cmn.size; + } + WGPUBindGroupDescriptor bg_desc; + _sg_clear(&bg_desc, sizeof(bg_desc)); + bg_desc.layout = bgl; + bg_desc.entryCount = (size_t)bge_index; + bg_desc.entries = &wgpu_entries[0]; + bg->bindgroup = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); + if (bg->bindgroup == 0) { + _SG_ERROR(WGPU_CREATEBINDGROUP_FAILED); + bg->slot.state = SG_RESOURCESTATE_FAILED; + return bg; + } + + _sg_wgpu_init_bindgroups_cache_key(&bg->key, bnd); + + bg->slot.state = SG_RESOURCESTATE_VALID; + return bg; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_bindgroup(_sg_wgpu_bindgroup_t* bg) { + SOKOL_ASSERT(bg); + _sg_stats_add(wgpu.bindings.num_discard_bindgroup, 1); + if (bg->slot.state == SG_RESOURCESTATE_VALID) { + if (bg->bindgroup) { + wgpuBindGroupRelease(bg->bindgroup); + bg->bindgroup = 0; + } + _sg_wgpu_reset_bindgroup_to_alloc_state(bg); + SOKOL_ASSERT(bg->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (bg->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_wgpu_dealloc_bindgroup(bg); + SOKOL_ASSERT(bg->slot.state == SG_RESOURCESTATE_INITIAL); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_all_bindgroups(void) { + _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool; + for (int i = 0; i < p->pool.size; i++) { + sg_resource_state state = p->bindgroups[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_wgpu_discard_bindgroup(&p->bindgroups[i]); + } + } +} + +_SOKOL_PRIVATE void _sg_wgpu_bindgroups_cache_init(const sg_desc* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.num == 0); + SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.index_mask == 0); + SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.items == 0); + const int num = desc->wgpu_bindgroups_cache_size; + if (num <= 1) { + _SG_PANIC(WGPU_BINDGROUPSCACHE_SIZE_GREATER_ONE); + } + if (!_sg_ispow2(num)) { + _SG_PANIC(WGPU_BINDGROUPSCACHE_SIZE_POW2); + } + _sg.wgpu.bindgroups_cache.num = (uint32_t)desc->wgpu_bindgroups_cache_size; + _sg.wgpu.bindgroups_cache.index_mask = _sg.wgpu.bindgroups_cache.num - 1; + size_t size_in_bytes = sizeof(_sg_wgpu_bindgroup_handle_t) * (size_t)num; + _sg.wgpu.bindgroups_cache.items = (_sg_wgpu_bindgroup_handle_t*)_sg_malloc_clear(size_in_bytes); +} + +_SOKOL_PRIVATE void _sg_wgpu_bindgroups_cache_discard(void) { + if (_sg.wgpu.bindgroups_cache.items) { + _sg_free(_sg.wgpu.bindgroups_cache.items); + _sg.wgpu.bindgroups_cache.items = 0; + } + _sg.wgpu.bindgroups_cache.num = 0; + _sg.wgpu.bindgroups_cache.index_mask = 0; +} + +_SOKOL_PRIVATE void _sg_wgpu_bindgroups_cache_set(uint64_t hash, uint32_t bg_id) { + uint32_t index = hash & _sg.wgpu.bindgroups_cache.index_mask; + SOKOL_ASSERT(index < _sg.wgpu.bindgroups_cache.num); + SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.items); + _sg.wgpu.bindgroups_cache.items[index].id = bg_id; +} + +_SOKOL_PRIVATE uint32_t _sg_wgpu_bindgroups_cache_get(uint64_t hash) { + uint32_t index = hash & _sg.wgpu.bindgroups_cache.index_mask; + SOKOL_ASSERT(index < _sg.wgpu.bindgroups_cache.num); + SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.items); + return _sg.wgpu.bindgroups_cache.items[index].id; +} + +_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_clear(void) { + memset(&_sg.wgpu.bindings_cache, 0, sizeof(_sg.wgpu.bindings_cache)); +} + +_SOKOL_PRIVATE bool _sg_wgpu_bindings_cache_vb_dirty(int index, const _sg_buffer_t* vb, int offset) { + SOKOL_ASSERT((index >= 0) && (index < SG_MAX_VERTEX_BUFFERS)); + if (vb) { + return (_sg.wgpu.bindings_cache.vbs[index].buffer.id != vb->slot.id) + || (_sg.wgpu.bindings_cache.vbs[index].offset != offset); + } else { + return _sg.wgpu.bindings_cache.vbs[index].buffer.id != SG_INVALID_ID; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_vb_update(int index, const _sg_buffer_t* vb, int offset) { + SOKOL_ASSERT((index >= 0) && (index < SG_MAX_VERTEX_BUFFERS)); + if (vb) { + _sg.wgpu.bindings_cache.vbs[index].buffer.id = vb->slot.id; + _sg.wgpu.bindings_cache.vbs[index].offset = offset; + } else { + _sg.wgpu.bindings_cache.vbs[index].buffer.id = SG_INVALID_ID; + _sg.wgpu.bindings_cache.vbs[index].offset = 0; + } +} + +_SOKOL_PRIVATE bool _sg_wgpu_bindings_cache_ib_dirty(const _sg_buffer_t* ib, int offset) { + if (ib) { + return (_sg.wgpu.bindings_cache.ib.buffer.id != ib->slot.id) + || (_sg.wgpu.bindings_cache.ib.offset != offset); + } else { + return _sg.wgpu.bindings_cache.ib.buffer.id != SG_INVALID_ID; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_ib_update(const _sg_buffer_t* ib, int offset) { + if (ib) { + _sg.wgpu.bindings_cache.ib.buffer.id = ib->slot.id; + _sg.wgpu.bindings_cache.ib.offset = offset; + } else { + _sg.wgpu.bindings_cache.ib.buffer.id = SG_INVALID_ID; + _sg.wgpu.bindings_cache.ib.offset = 0; + } +} + +_SOKOL_PRIVATE bool _sg_wgpu_bindings_cache_bg_dirty(const _sg_wgpu_bindgroup_t* bg) { + if (bg) { + return _sg.wgpu.bindings_cache.bg.id != bg->slot.id; + } else { + return _sg.wgpu.bindings_cache.bg.id != SG_INVALID_ID; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_bg_update(const _sg_wgpu_bindgroup_t* bg) { + if (bg) { + _sg.wgpu.bindings_cache.bg.id = bg->slot.id; + } else { + _sg.wgpu.bindings_cache.bg.id = SG_INVALID_ID; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_set_bindings_bindgroup(_sg_wgpu_bindgroup_t* bg) { + if (_sg_wgpu_bindings_cache_bg_dirty(bg)) { + _sg_wgpu_bindings_cache_bg_update(bg); + _sg_stats_add(wgpu.bindings.num_set_bindgroup, 1); + if (bg) { + SOKOL_ASSERT(bg->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(bg->bindgroup); + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, _SG_WGPU_IMAGE_SAMPLER_BINDGROUP_INDEX, bg->bindgroup, 0, 0); + } else { + // a nullptr bindgroup means setting the empty bindgroup + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, _SG_WGPU_IMAGE_SAMPLER_BINDGROUP_INDEX, _sg.wgpu.empty_bind_group, 0, 0); + } + } else { + _sg_stats_add(wgpu.bindings.num_skip_redundant_bindgroup, 1); + } +} + +_SOKOL_PRIVATE bool _sg_wgpu_apply_bindgroup(_sg_bindings_t* bnd) { + if ((bnd->num_vs_imgs + bnd->num_vs_smps + bnd->num_vs_sbufs + bnd->num_fs_imgs + bnd->num_fs_smps + bnd->num_fs_sbufs) > 0) { + if (!_sg.desc.wgpu_disable_bindgroups_cache) { + _sg_wgpu_bindgroup_t* bg = 0; + _sg_wgpu_bindgroups_cache_key_t key; + _sg_wgpu_init_bindgroups_cache_key(&key, bnd); + uint32_t bg_id = _sg_wgpu_bindgroups_cache_get(key.hash); + if (bg_id != SG_INVALID_ID) { + // potential cache hit + bg = _sg_wgpu_lookup_bindgroup(bg_id); + SOKOL_ASSERT(bg && (bg->slot.state == SG_RESOURCESTATE_VALID)); + if (!_sg_wgpu_compare_bindgroups_cache_key(&key, &bg->key)) { + // cache collision, need to delete cached bindgroup + _sg_stats_add(wgpu.bindings.num_bindgroup_cache_collisions, 1); + _sg_wgpu_discard_bindgroup(bg); + _sg_wgpu_bindgroups_cache_set(key.hash, SG_INVALID_ID); + bg = 0; + } else { + _sg_stats_add(wgpu.bindings.num_bindgroup_cache_hits, 1); + } + } else { + _sg_stats_add(wgpu.bindings.num_bindgroup_cache_misses, 1); + } + if (bg == 0) { + // either no cache entry yet, or cache collision, create new bindgroup and store in cache + bg = _sg_wgpu_create_bindgroup(bnd); + _sg_wgpu_bindgroups_cache_set(key.hash, bg->slot.id); + } + if (bg && bg->slot.state == SG_RESOURCESTATE_VALID) { + _sg_wgpu_set_bindings_bindgroup(bg); + } else { + return false; + } + } else { + // bindgroups cache disabled, create and destroy bindgroup on the fly (expensive!) + _sg_wgpu_bindgroup_t* bg = _sg_wgpu_create_bindgroup(bnd); + if (bg) { + if (bg->slot.state == SG_RESOURCESTATE_VALID) { + _sg_wgpu_set_bindings_bindgroup(bg); + } + _sg_wgpu_discard_bindgroup(bg); + } else { + return false; + } + } + } else { + _sg_wgpu_set_bindings_bindgroup(0); + } + return true; +} + +_SOKOL_PRIVATE bool _sg_wgpu_apply_index_buffer(_sg_bindings_t* bnd) { + if (_sg_wgpu_bindings_cache_ib_dirty(bnd->ib, bnd->ib_offset)) { + _sg_wgpu_bindings_cache_ib_update(bnd->ib, bnd->ib_offset); + if (bnd->ib) { + const WGPUIndexFormat format = _sg_wgpu_indexformat(bnd->pip->cmn.index_type); + const uint64_t buf_size = (uint64_t)bnd->ib->cmn.size; + const uint64_t offset = (uint64_t)bnd->ib_offset; + SOKOL_ASSERT(buf_size > offset); + const uint64_t max_bytes = buf_size - offset; + wgpuRenderPassEncoderSetIndexBuffer(_sg.wgpu.pass_enc, bnd->ib->wgpu.buf, format, offset, max_bytes); + _sg_stats_add(wgpu.bindings.num_set_index_buffer, 1); + } + // FIXME: else-path should actually set a null index buffer (this was just recently implemented in WebGPU) + } else { + _sg_stats_add(wgpu.bindings.num_skip_redundant_index_buffer, 1); + } + return true; +} + +_SOKOL_PRIVATE bool _sg_wgpu_apply_vertex_buffers(_sg_bindings_t* bnd) { + for (int slot = 0; slot < bnd->num_vbs; slot++) { + if (_sg_wgpu_bindings_cache_vb_dirty(slot, bnd->vbs[slot], bnd->vb_offsets[slot])) { + _sg_wgpu_bindings_cache_vb_update(slot, bnd->vbs[slot], bnd->vb_offsets[slot]); + const uint64_t buf_size = (uint64_t)bnd->vbs[slot]->cmn.size; + const uint64_t offset = (uint64_t)bnd->vb_offsets[slot]; + SOKOL_ASSERT(buf_size > offset); + const uint64_t max_bytes = buf_size - offset; + wgpuRenderPassEncoderSetVertexBuffer(_sg.wgpu.pass_enc, (uint32_t)slot, bnd->vbs[slot]->wgpu.buf, offset, max_bytes); + _sg_stats_add(wgpu.bindings.num_set_vertex_buffer, 1); + } else { + _sg_stats_add(wgpu.bindings.num_skip_redundant_vertex_buffer, 1); + } + } + // FIXME: remaining vb slots should actually set a null vertex buffer (this was just recently implemented in WebGPU) + return true; +} + +_SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->environment.wgpu.device); + SOKOL_ASSERT(desc->uniform_buffer_size > 0); + _sg.backend = SG_BACKEND_WGPU; + _sg.wgpu.valid = true; + _sg.wgpu.dev = (WGPUDevice) desc->environment.wgpu.device; + _sg.wgpu.queue = wgpuDeviceGetQueue(_sg.wgpu.dev); + SOKOL_ASSERT(_sg.wgpu.queue); + + _sg_wgpu_init_caps(); + _sg_wgpu_uniform_buffer_init(desc); + _sg_wgpu_bindgroups_pool_init(desc); + _sg_wgpu_bindgroups_cache_init(desc); + _sg_wgpu_bindings_cache_clear(); + + // create an empty bind group for shader stages without bound images + // FIXME: once WebGPU supports setting null objects, this can be removed + WGPUBindGroupLayoutDescriptor bgl_desc; + _sg_clear(&bgl_desc, sizeof(bgl_desc)); + WGPUBindGroupLayout empty_bgl = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &bgl_desc); + SOKOL_ASSERT(empty_bgl); + WGPUBindGroupDescriptor bg_desc; + _sg_clear(&bg_desc, sizeof(bg_desc)); + bg_desc.layout = empty_bgl; + _sg.wgpu.empty_bind_group = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); + SOKOL_ASSERT(_sg.wgpu.empty_bind_group); + wgpuBindGroupLayoutRelease(empty_bgl); + + // create initial per-frame command encoder + WGPUCommandEncoderDescriptor cmd_enc_desc; + _sg_clear(&cmd_enc_desc, sizeof(cmd_enc_desc)); + _sg.wgpu.cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); + SOKOL_ASSERT(_sg.wgpu.cmd_enc); +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_backend(void) { + SOKOL_ASSERT(_sg.wgpu.valid); + SOKOL_ASSERT(_sg.wgpu.cmd_enc); + _sg.wgpu.valid = false; + _sg_wgpu_discard_all_bindgroups(); + _sg_wgpu_bindgroups_cache_discard(); + _sg_wgpu_bindgroups_pool_discard(); + _sg_wgpu_uniform_buffer_discard(); + wgpuBindGroupRelease(_sg.wgpu.empty_bind_group); _sg.wgpu.empty_bind_group = 0; + wgpuCommandEncoderRelease(_sg.wgpu.cmd_enc); _sg.wgpu.cmd_enc = 0; + wgpuQueueRelease(_sg.wgpu.queue); _sg.wgpu.queue = 0; +} + +_SOKOL_PRIVATE void _sg_wgpu_reset_state_cache(void) { + _sg_wgpu_bindings_cache_clear(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + const bool injected = (0 != desc->wgpu_buffer); + if (injected) { + buf->wgpu.buf = (WGPUBuffer) desc->wgpu_buffer; + wgpuBufferReference(buf->wgpu.buf); + } else { + // buffer mapping size must be multiple of 4, so round up buffer size (only a problem + // with index buffers containing odd number of indices) + const uint64_t wgpu_buf_size = _sg_roundup_u64((uint64_t)buf->cmn.size, 4); + const bool map_at_creation = (SG_USAGE_IMMUTABLE == buf->cmn.usage); + + WGPUBufferDescriptor wgpu_buf_desc; + _sg_clear(&wgpu_buf_desc, sizeof(wgpu_buf_desc)); + wgpu_buf_desc.usage = _sg_wgpu_buffer_usage(buf->cmn.type, buf->cmn.usage); + wgpu_buf_desc.size = wgpu_buf_size; + wgpu_buf_desc.mappedAtCreation = map_at_creation; + wgpu_buf_desc.label = desc->label; + buf->wgpu.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &wgpu_buf_desc); + if (0 == buf->wgpu.buf) { + _SG_ERROR(WGPU_CREATE_BUFFER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + if (map_at_creation) { + SOKOL_ASSERT(desc->data.ptr && (desc->data.size > 0)); + SOKOL_ASSERT(desc->data.size <= (size_t)buf->cmn.size); + // FIXME: inefficient on WASM + void* ptr = wgpuBufferGetMappedRange(buf->wgpu.buf, 0, wgpu_buf_size); + SOKOL_ASSERT(ptr); + memcpy(ptr, desc->data.ptr, desc->data.size); + wgpuBufferUnmap(buf->wgpu.buf); + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + if (buf->wgpu.buf) { + wgpuBufferDestroy(buf->wgpu.buf); + wgpuBufferRelease(buf->wgpu.buf); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_copy_buffer_data(const _sg_buffer_t* buf, uint64_t offset, const sg_range* data) { + SOKOL_ASSERT((offset + data->size) <= (size_t)buf->cmn.size); + // WebGPU's write-buffer requires the size to be a multiple of four, so we may need to split the copy + // operation into two writeBuffer calls + uint64_t clamped_size = data->size & ~3UL; + uint64_t extra_size = data->size & 3UL; + SOKOL_ASSERT(extra_size < 4); + wgpuQueueWriteBuffer(_sg.wgpu.queue, buf->wgpu.buf, offset, data->ptr, clamped_size); + if (extra_size > 0) { + const uint64_t extra_src_offset = clamped_size; + const uint64_t extra_dst_offset = offset + clamped_size; + uint8_t extra_data[4] = { 0 }; + uint8_t* extra_src_ptr = ((uint8_t*)data->ptr) + extra_src_offset; + for (size_t i = 0; i < extra_size; i++) { + extra_data[i] = extra_src_ptr[i]; + } + wgpuQueueWriteBuffer(_sg.wgpu.queue, buf->wgpu.buf, extra_dst_offset, extra_src_ptr, 4); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_copy_image_data(const _sg_image_t* img, WGPUTexture wgpu_tex, const sg_image_data* data) { + WGPUTextureDataLayout wgpu_layout; + _sg_clear(&wgpu_layout, sizeof(wgpu_layout)); + WGPUImageCopyTexture wgpu_copy_tex; + _sg_clear(&wgpu_copy_tex, sizeof(wgpu_copy_tex)); + wgpu_copy_tex.texture = wgpu_tex; + wgpu_copy_tex.aspect = WGPUTextureAspect_All; + WGPUExtent3D wgpu_extent; + _sg_clear(&wgpu_extent, sizeof(wgpu_extent)); + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6 : 1; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { + wgpu_copy_tex.mipLevel = (uint32_t)mip_index; + wgpu_copy_tex.origin.z = (uint32_t)face_index; + int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + int mip_slices; + switch (img->cmn.type) { + case SG_IMAGETYPE_CUBE: + mip_slices = 1; + break; + case SG_IMAGETYPE_3D: + mip_slices = _sg_miplevel_dim(img->cmn.num_slices, mip_index); + break; + default: + mip_slices = img->cmn.num_slices; + break; + } + const int row_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + const int num_rows = _sg_num_rows(img->cmn.pixel_format, mip_height); + if (_sg_is_compressed_pixel_format(img->cmn.pixel_format)) { + mip_width = _sg_roundup(mip_width, 4); + mip_height = _sg_roundup(mip_height, 4); + } + wgpu_layout.offset = 0; + wgpu_layout.bytesPerRow = (uint32_t)row_pitch; + wgpu_layout.rowsPerImage = (uint32_t)num_rows; + wgpu_extent.width = (uint32_t)mip_width; + wgpu_extent.height = (uint32_t)mip_height; + wgpu_extent.depthOrArrayLayers = (uint32_t)mip_slices; + const sg_range* mip_data = &data->subimage[face_index][mip_index]; + wgpuQueueWriteTexture(_sg.wgpu.queue, &wgpu_copy_tex, mip_data->ptr, mip_data->size, &wgpu_layout, &wgpu_extent); + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + const bool injected = (0 != desc->wgpu_texture); + if (injected) { + img->wgpu.tex = (WGPUTexture)desc->wgpu_texture; + wgpuTextureReference(img->wgpu.tex); + img->wgpu.view = (WGPUTextureView)desc->wgpu_texture_view; + if (img->wgpu.view) { + wgpuTextureViewReference(img->wgpu.view); + } + } else { + WGPUTextureDescriptor wgpu_tex_desc; + _sg_clear(&wgpu_tex_desc, sizeof(wgpu_tex_desc)); + wgpu_tex_desc.label = desc->label; + wgpu_tex_desc.usage = WGPUTextureUsage_TextureBinding|WGPUTextureUsage_CopyDst; + if (desc->render_target) { + wgpu_tex_desc.usage |= WGPUTextureUsage_RenderAttachment; + } + wgpu_tex_desc.dimension = _sg_wgpu_texture_dimension(img->cmn.type); + wgpu_tex_desc.size.width = (uint32_t) img->cmn.width; + wgpu_tex_desc.size.height = (uint32_t) img->cmn.height; + if (desc->type == SG_IMAGETYPE_CUBE) { + wgpu_tex_desc.size.depthOrArrayLayers = 6; + } else { + wgpu_tex_desc.size.depthOrArrayLayers = (uint32_t) img->cmn.num_slices; + } + wgpu_tex_desc.format = _sg_wgpu_textureformat(img->cmn.pixel_format); + wgpu_tex_desc.mipLevelCount = (uint32_t) img->cmn.num_mipmaps; + wgpu_tex_desc.sampleCount = (uint32_t) img->cmn.sample_count; + img->wgpu.tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); + if (0 == img->wgpu.tex) { + _SG_ERROR(WGPU_CREATE_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + if ((img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { + _sg_wgpu_copy_image_data(img, img->wgpu.tex, &desc->data); + } + WGPUTextureViewDescriptor wgpu_texview_desc; + _sg_clear(&wgpu_texview_desc, sizeof(wgpu_texview_desc)); + wgpu_texview_desc.label = desc->label; + wgpu_texview_desc.dimension = _sg_wgpu_texture_view_dimension(img->cmn.type); + wgpu_texview_desc.mipLevelCount = (uint32_t)img->cmn.num_mipmaps; + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + wgpu_texview_desc.arrayLayerCount = 6; + } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + wgpu_texview_desc.arrayLayerCount = (uint32_t)img->cmn.num_slices; + } else { + wgpu_texview_desc.arrayLayerCount = 1; + } + if (_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)) { + wgpu_texview_desc.aspect = WGPUTextureAspect_DepthOnly; + } else { + wgpu_texview_desc.aspect = WGPUTextureAspect_All; + } + img->wgpu.view = wgpuTextureCreateView(img->wgpu.tex, &wgpu_texview_desc); + if (0 == img->wgpu.view) { + _SG_ERROR(WGPU_CREATE_TEXTURE_VIEW_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + if (img->wgpu.view) { + wgpuTextureViewRelease(img->wgpu.view); + img->wgpu.view = 0; + } + if (img->wgpu.tex) { + wgpuTextureDestroy(img->wgpu.tex); + wgpuTextureRelease(img->wgpu.tex); + img->wgpu.tex = 0; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + SOKOL_ASSERT(_sg.wgpu.dev); + const bool injected = (0 != desc->wgpu_sampler); + if (injected) { + smp->wgpu.smp = (WGPUSampler) desc->wgpu_sampler; + wgpuSamplerReference(smp->wgpu.smp); + } else { + WGPUSamplerDescriptor wgpu_desc; + _sg_clear(&wgpu_desc, sizeof(wgpu_desc)); + wgpu_desc.label = desc->label; + wgpu_desc.addressModeU = _sg_wgpu_sampler_address_mode(desc->wrap_u); + wgpu_desc.addressModeV = _sg_wgpu_sampler_address_mode(desc->wrap_v); + wgpu_desc.addressModeW = _sg_wgpu_sampler_address_mode(desc->wrap_w); + wgpu_desc.magFilter = _sg_wgpu_sampler_minmag_filter(desc->mag_filter); + wgpu_desc.minFilter = _sg_wgpu_sampler_minmag_filter(desc->min_filter); + wgpu_desc.mipmapFilter = _sg_wgpu_sampler_mipmap_filter(desc->mipmap_filter); + wgpu_desc.lodMinClamp = desc->min_lod; + wgpu_desc.lodMaxClamp = desc->max_lod; + wgpu_desc.compare = _sg_wgpu_comparefunc(desc->compare); + if (wgpu_desc.compare == WGPUCompareFunction_Never) { + wgpu_desc.compare = WGPUCompareFunction_Undefined; + } + wgpu_desc.maxAnisotropy = (uint16_t)desc->max_anisotropy; + smp->wgpu.smp = wgpuDeviceCreateSampler(_sg.wgpu.dev, &wgpu_desc); + if (0 == smp->wgpu.smp) { + _SG_ERROR(WGPU_CREATE_SAMPLER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + if (smp->wgpu.smp) { + wgpuSamplerRelease(smp->wgpu.smp); + smp->wgpu.smp = 0; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(desc->vs.source && desc->fs.source); + + WGPUBindGroupLayoutEntry wgpu_bgl_entries[_SG_WGPU_MAX_BINDGROUP_ENTRIES]; + _sg_clear(wgpu_bgl_entries, sizeof(wgpu_bgl_entries)); + int bgl_index = 0; + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; + + _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; + _sg_wgpu_shader_stage_t* wgpu_stage = &shd->wgpu.stage[stage_index]; + _sg_strcpy(&wgpu_stage->entry, stage_desc->entry); + + WGPUShaderModuleWGSLDescriptor wgpu_shdmod_wgsl_desc; + _sg_clear(&wgpu_shdmod_wgsl_desc, sizeof(wgpu_shdmod_wgsl_desc)); + wgpu_shdmod_wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor; + wgpu_shdmod_wgsl_desc.code = stage_desc->source; + + WGPUShaderModuleDescriptor wgpu_shdmod_desc; + _sg_clear(&wgpu_shdmod_desc, sizeof(wgpu_shdmod_desc)); + wgpu_shdmod_desc.nextInChain = &wgpu_shdmod_wgsl_desc.chain; + wgpu_shdmod_desc.label = desc->label; + + wgpu_stage->module = wgpuDeviceCreateShaderModule(_sg.wgpu.dev, &wgpu_shdmod_desc); + if (0 == wgpu_stage->module) { + _SG_ERROR(WGPU_CREATE_SHADER_MODULE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + + const int num_images = cmn_stage->num_images; + if (num_images > (int)_sg.wgpu.limits.limits.maxSampledTexturesPerShaderStage) { + _SG_ERROR(WGPU_SHADER_TOO_MANY_IMAGES); + return SG_RESOURCESTATE_FAILED; + } + const int num_samplers = cmn_stage->num_samplers; + if (num_samplers > (int)_sg.wgpu.limits.limits.maxSamplersPerShaderStage) { + _SG_ERROR(WGPU_SHADER_TOO_MANY_SAMPLERS); + return SG_RESOURCESTATE_FAILED; + } + const int num_sbufs = cmn_stage->num_storage_buffers; + if (num_sbufs > (int)_sg.wgpu.limits.limits.maxStorageBuffersPerShaderStage) { + _SG_ERROR(WGPU_SHADER_TOO_MANY_STORAGEBUFFERS); + return SG_RESOURCESTATE_FAILED; + } + for (int img_index = 0; img_index < num_images; img_index++) { + SOKOL_ASSERT(bgl_index < _SG_WGPU_MAX_BINDGROUP_ENTRIES); + WGPUBindGroupLayoutEntry* wgpu_bgl_entry = &wgpu_bgl_entries[bgl_index++]; + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + wgpu_bgl_entry->binding = _sg_wgpu_image_binding((sg_shader_stage)stage_index, img_index); + wgpu_bgl_entry->visibility = _sg_wgpu_shader_stage((sg_shader_stage)stage_index); + wgpu_bgl_entry->texture.viewDimension = _sg_wgpu_texture_view_dimension(img_desc->image_type); + wgpu_bgl_entry->texture.sampleType = _sg_wgpu_texture_sample_type(img_desc->sample_type); + wgpu_bgl_entry->texture.multisampled = img_desc->multisampled; + } + for (int smp_index = 0; smp_index < num_samplers; smp_index++) { + SOKOL_ASSERT(bgl_index < _SG_WGPU_MAX_BINDGROUP_ENTRIES); + WGPUBindGroupLayoutEntry* wgpu_bgl_entry = &wgpu_bgl_entries[bgl_index++]; + const sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_index]; + wgpu_bgl_entry->binding = _sg_wgpu_sampler_binding((sg_shader_stage)stage_index, smp_index); + wgpu_bgl_entry->visibility = _sg_wgpu_shader_stage((sg_shader_stage)stage_index); + wgpu_bgl_entry->sampler.type = _sg_wgpu_sampler_binding_type(smp_desc->sampler_type); + } + for (int sbuf_index = 0; sbuf_index < num_sbufs; sbuf_index++) { + SOKOL_ASSERT(bgl_index < _SG_WGPU_MAX_BINDGROUP_ENTRIES); + WGPUBindGroupLayoutEntry* wgpu_bgl_entry = &wgpu_bgl_entries[bgl_index++]; + const sg_shader_storage_buffer_desc* sbuf_desc = &stage_desc->storage_buffers[sbuf_index]; + wgpu_bgl_entry->binding = _sg_wgpu_storagebuffer_binding((sg_shader_stage)stage_index, sbuf_index); + wgpu_bgl_entry->visibility = _sg_wgpu_shader_stage((sg_shader_stage)stage_index); + wgpu_bgl_entry->buffer.type = sbuf_desc->readonly ? WGPUBufferBindingType_ReadOnlyStorage : WGPUBufferBindingType_Storage; + } + } + + WGPUBindGroupLayoutDescriptor wgpu_bgl_desc; + _sg_clear(&wgpu_bgl_desc, sizeof(wgpu_bgl_desc)); + wgpu_bgl_desc.entryCount = (size_t)bgl_index; + wgpu_bgl_desc.entries = &wgpu_bgl_entries[0]; + shd->wgpu.bind_group_layout = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &wgpu_bgl_desc); + if (shd->wgpu.bind_group_layout == 0) { + _SG_ERROR(WGPU_SHADER_CREATE_BINDGROUP_LAYOUT_FAILED); + return SG_RESOURCESTATE_FAILED; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + if (shd->wgpu.bind_group_layout) { + wgpuBindGroupLayoutRelease(shd->wgpu.bind_group_layout); + shd->wgpu.bind_group_layout = 0; + } + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + _sg_wgpu_shader_stage_t* wgpu_stage = &shd->wgpu.stage[stage_index]; + if (wgpu_stage->module) { + wgpuShaderModuleRelease(wgpu_stage->module); + wgpu_stage->module = 0; + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + SOKOL_ASSERT(shd->wgpu.bind_group_layout); + pip->shader = shd; + + pip->wgpu.blend_color.r = (double) desc->blend_color.r; + pip->wgpu.blend_color.g = (double) desc->blend_color.g; + pip->wgpu.blend_color.b = (double) desc->blend_color.b; + pip->wgpu.blend_color.a = (double) desc->blend_color.a; + + // - @group(0) for uniform blocks + // - @group(1) for all image and sampler resources + WGPUBindGroupLayout wgpu_bgl[_SG_WGPU_NUM_BINDGROUPS]; + _sg_clear(&wgpu_bgl, sizeof(wgpu_bgl)); + wgpu_bgl[_SG_WGPU_UNIFORM_BINDGROUP_INDEX] = _sg.wgpu.uniform.bind.group_layout; + wgpu_bgl[_SG_WGPU_IMAGE_SAMPLER_BINDGROUP_INDEX] = shd->wgpu.bind_group_layout; + WGPUPipelineLayoutDescriptor wgpu_pl_desc; + _sg_clear(&wgpu_pl_desc, sizeof(wgpu_pl_desc)); + wgpu_pl_desc.bindGroupLayoutCount = _SG_WGPU_NUM_BINDGROUPS; + wgpu_pl_desc.bindGroupLayouts = &wgpu_bgl[0]; + const WGPUPipelineLayout wgpu_pip_layout = wgpuDeviceCreatePipelineLayout(_sg.wgpu.dev, &wgpu_pl_desc); + if (0 == wgpu_pip_layout) { + _SG_ERROR(WGPU_CREATE_PIPELINE_LAYOUT_FAILED); + return SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT(wgpu_pip_layout); + + WGPUVertexBufferLayout wgpu_vb_layouts[SG_MAX_VERTEX_BUFFERS]; + _sg_clear(wgpu_vb_layouts, sizeof(wgpu_vb_layouts)); + WGPUVertexAttribute wgpu_vtx_attrs[SG_MAX_VERTEX_BUFFERS][SG_MAX_VERTEX_ATTRIBUTES]; + _sg_clear(wgpu_vtx_attrs, sizeof(wgpu_vtx_attrs)); + int wgpu_vb_num = 0; + for (int vb_idx = 0; vb_idx < SG_MAX_VERTEX_BUFFERS; vb_idx++, wgpu_vb_num++) { + const sg_vertex_buffer_layout_state* vbl_state = &desc->layout.buffers[vb_idx]; + if (0 == vbl_state->stride) { + break; + } + wgpu_vb_layouts[vb_idx].arrayStride = (uint64_t)vbl_state->stride; + wgpu_vb_layouts[vb_idx].stepMode = _sg_wgpu_stepmode(vbl_state->step_func); + wgpu_vb_layouts[vb_idx].attributes = &wgpu_vtx_attrs[vb_idx][0]; + } + for (int va_idx = 0; va_idx < SG_MAX_VERTEX_ATTRIBUTES; va_idx++) { + const sg_vertex_attr_state* va_state = &desc->layout.attrs[va_idx]; + if (SG_VERTEXFORMAT_INVALID == va_state->format) { + break; + } + const int vb_idx = va_state->buffer_index; + SOKOL_ASSERT(vb_idx < SG_MAX_VERTEX_BUFFERS); + pip->cmn.vertex_buffer_layout_active[vb_idx] = true; + const size_t wgpu_attr_idx = wgpu_vb_layouts[vb_idx].attributeCount; + wgpu_vb_layouts[vb_idx].attributeCount += 1; + wgpu_vtx_attrs[vb_idx][wgpu_attr_idx].format = _sg_wgpu_vertexformat(va_state->format); + wgpu_vtx_attrs[vb_idx][wgpu_attr_idx].offset = (uint64_t)va_state->offset; + wgpu_vtx_attrs[vb_idx][wgpu_attr_idx].shaderLocation = (uint32_t)va_idx; + } + + WGPURenderPipelineDescriptor wgpu_pip_desc; + _sg_clear(&wgpu_pip_desc, sizeof(wgpu_pip_desc)); + WGPUDepthStencilState wgpu_ds_state; + _sg_clear(&wgpu_ds_state, sizeof(wgpu_ds_state)); + WGPUFragmentState wgpu_frag_state; + _sg_clear(&wgpu_frag_state, sizeof(wgpu_frag_state)); + WGPUColorTargetState wgpu_ctgt_state[SG_MAX_COLOR_ATTACHMENTS]; + _sg_clear(&wgpu_ctgt_state, sizeof(wgpu_ctgt_state)); + WGPUBlendState wgpu_blend_state[SG_MAX_COLOR_ATTACHMENTS]; + _sg_clear(&wgpu_blend_state, sizeof(wgpu_blend_state)); + wgpu_pip_desc.label = desc->label; + wgpu_pip_desc.layout = wgpu_pip_layout; + wgpu_pip_desc.vertex.module = shd->wgpu.stage[SG_SHADERSTAGE_VS].module; + wgpu_pip_desc.vertex.entryPoint = shd->wgpu.stage[SG_SHADERSTAGE_VS].entry.buf; + wgpu_pip_desc.vertex.bufferCount = (size_t)wgpu_vb_num; + wgpu_pip_desc.vertex.buffers = &wgpu_vb_layouts[0]; + wgpu_pip_desc.primitive.topology = _sg_wgpu_topology(desc->primitive_type); + wgpu_pip_desc.primitive.stripIndexFormat = _sg_wgpu_stripindexformat(desc->primitive_type, desc->index_type); + wgpu_pip_desc.primitive.frontFace = _sg_wgpu_frontface(desc->face_winding); + wgpu_pip_desc.primitive.cullMode = _sg_wgpu_cullmode(desc->cull_mode); + if (SG_PIXELFORMAT_NONE != desc->depth.pixel_format) { + wgpu_ds_state.format = _sg_wgpu_textureformat(desc->depth.pixel_format); + wgpu_ds_state.depthWriteEnabled = desc->depth.write_enabled; + wgpu_ds_state.depthCompare = _sg_wgpu_comparefunc(desc->depth.compare); + wgpu_ds_state.stencilFront.compare = _sg_wgpu_comparefunc(desc->stencil.front.compare); + wgpu_ds_state.stencilFront.failOp = _sg_wgpu_stencilop(desc->stencil.front.fail_op); + wgpu_ds_state.stencilFront.depthFailOp = _sg_wgpu_stencilop(desc->stencil.front.depth_fail_op); + wgpu_ds_state.stencilFront.passOp = _sg_wgpu_stencilop(desc->stencil.front.pass_op); + wgpu_ds_state.stencilBack.compare = _sg_wgpu_comparefunc(desc->stencil.back.compare); + wgpu_ds_state.stencilBack.failOp = _sg_wgpu_stencilop(desc->stencil.back.fail_op); + wgpu_ds_state.stencilBack.depthFailOp = _sg_wgpu_stencilop(desc->stencil.back.depth_fail_op); + wgpu_ds_state.stencilBack.passOp = _sg_wgpu_stencilop(desc->stencil.back.pass_op); + wgpu_ds_state.stencilReadMask = desc->stencil.read_mask; + wgpu_ds_state.stencilWriteMask = desc->stencil.write_mask; + wgpu_ds_state.depthBias = (int32_t)desc->depth.bias; + wgpu_ds_state.depthBiasSlopeScale = desc->depth.bias_slope_scale; + wgpu_ds_state.depthBiasClamp = desc->depth.bias_clamp; + wgpu_pip_desc.depthStencil = &wgpu_ds_state; + } + wgpu_pip_desc.multisample.count = (uint32_t)desc->sample_count; + wgpu_pip_desc.multisample.mask = 0xFFFFFFFF; + wgpu_pip_desc.multisample.alphaToCoverageEnabled = desc->alpha_to_coverage_enabled; + if (desc->color_count > 0) { + wgpu_frag_state.module = shd->wgpu.stage[SG_SHADERSTAGE_FS].module; + wgpu_frag_state.entryPoint = shd->wgpu.stage[SG_SHADERSTAGE_FS].entry.buf; + wgpu_frag_state.targetCount = (size_t)desc->color_count; + wgpu_frag_state.targets = &wgpu_ctgt_state[0]; + for (int i = 0; i < desc->color_count; i++) { + SOKOL_ASSERT(i < SG_MAX_COLOR_ATTACHMENTS); + wgpu_ctgt_state[i].format = _sg_wgpu_textureformat(desc->colors[i].pixel_format); + wgpu_ctgt_state[i].writeMask = _sg_wgpu_colorwritemask(desc->colors[i].write_mask); + if (desc->colors[i].blend.enabled) { + wgpu_ctgt_state[i].blend = &wgpu_blend_state[i]; + wgpu_blend_state[i].color.operation = _sg_wgpu_blendop(desc->colors[i].blend.op_rgb); + wgpu_blend_state[i].color.srcFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.src_factor_rgb); + wgpu_blend_state[i].color.dstFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.dst_factor_rgb); + wgpu_blend_state[i].alpha.operation = _sg_wgpu_blendop(desc->colors[i].blend.op_alpha); + wgpu_blend_state[i].alpha.srcFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.src_factor_alpha); + wgpu_blend_state[i].alpha.dstFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.dst_factor_alpha); + } + } + wgpu_pip_desc.fragment = &wgpu_frag_state; + } + pip->wgpu.pip = wgpuDeviceCreateRenderPipeline(_sg.wgpu.dev, &wgpu_pip_desc); + wgpuPipelineLayoutRelease(wgpu_pip_layout); + if (0 == pip->wgpu.pip) { + _SG_ERROR(WGPU_CREATE_RENDER_PIPELINE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + if (pip == _sg.wgpu.cur_pipeline) { + _sg.wgpu.cur_pipeline = 0; + _sg.wgpu.cur_pipeline_id.id = SG_INVALID_ID; + } + if (pip->wgpu.pip) { + wgpuRenderPipelineRelease(pip->wgpu.pip); + pip->wgpu.pip = 0; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_attachments(_sg_attachments_t* atts, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_attachments_desc* desc) { + SOKOL_ASSERT(atts && desc); + SOKOL_ASSERT(color_images && resolve_images); + + // copy image pointers and create renderable wgpu texture views + for (int i = 0; i < atts->cmn.num_colors; i++) { + const sg_attachment_desc* color_desc = &desc->colors[i]; + _SOKOL_UNUSED(color_desc); + SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == atts->wgpu.colors[i].image); + SOKOL_ASSERT(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); + SOKOL_ASSERT(color_images[i]->wgpu.tex); + atts->wgpu.colors[i].image = color_images[i]; + + WGPUTextureViewDescriptor wgpu_color_view_desc; + _sg_clear(&wgpu_color_view_desc, sizeof(wgpu_color_view_desc)); + wgpu_color_view_desc.baseMipLevel = (uint32_t) color_desc->mip_level; + wgpu_color_view_desc.mipLevelCount = 1; + wgpu_color_view_desc.baseArrayLayer = (uint32_t) color_desc->slice; + wgpu_color_view_desc.arrayLayerCount = 1; + atts->wgpu.colors[i].view = wgpuTextureCreateView(color_images[i]->wgpu.tex, &wgpu_color_view_desc); + if (0 == atts->wgpu.colors[i].view) { + _SG_ERROR(WGPU_ATTACHMENTS_CREATE_TEXTURE_VIEW_FAILED); + return SG_RESOURCESTATE_FAILED; + } + + const sg_attachment_desc* resolve_desc = &desc->resolves[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == atts->wgpu.resolves[i].image); + SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); + SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); + SOKOL_ASSERT(resolve_images[i]->wgpu.tex); + atts->wgpu.resolves[i].image = resolve_images[i]; + + WGPUTextureViewDescriptor wgpu_resolve_view_desc; + _sg_clear(&wgpu_resolve_view_desc, sizeof(wgpu_resolve_view_desc)); + wgpu_resolve_view_desc.baseMipLevel = (uint32_t) resolve_desc->mip_level; + wgpu_resolve_view_desc.mipLevelCount = 1; + wgpu_resolve_view_desc.baseArrayLayer = (uint32_t) resolve_desc->slice; + wgpu_resolve_view_desc.arrayLayerCount = 1; + atts->wgpu.resolves[i].view = wgpuTextureCreateView(resolve_images[i]->wgpu.tex, &wgpu_resolve_view_desc); + if (0 == atts->wgpu.resolves[i].view) { + _SG_ERROR(WGPU_ATTACHMENTS_CREATE_TEXTURE_VIEW_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + } + SOKOL_ASSERT(0 == atts->wgpu.depth_stencil.image); + const sg_attachment_desc* ds_desc = &desc->depth_stencil; + if (ds_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(ds_img && (ds_img->slot.id == ds_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_img->cmn.pixel_format)); + SOKOL_ASSERT(ds_img->wgpu.tex); + atts->wgpu.depth_stencil.image = ds_img; + + WGPUTextureViewDescriptor wgpu_ds_view_desc; + _sg_clear(&wgpu_ds_view_desc, sizeof(wgpu_ds_view_desc)); + wgpu_ds_view_desc.baseMipLevel = (uint32_t) ds_desc->mip_level; + wgpu_ds_view_desc.mipLevelCount = 1; + wgpu_ds_view_desc.baseArrayLayer = (uint32_t) ds_desc->slice; + wgpu_ds_view_desc.arrayLayerCount = 1; + atts->wgpu.depth_stencil.view = wgpuTextureCreateView(ds_img->wgpu.tex, &wgpu_ds_view_desc); + if (0 == atts->wgpu.depth_stencil.view) { + _SG_ERROR(WGPU_ATTACHMENTS_CREATE_TEXTURE_VIEW_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + for (int i = 0; i < atts->cmn.num_colors; i++) { + if (atts->wgpu.colors[i].view) { + wgpuTextureViewRelease(atts->wgpu.colors[i].view); + atts->wgpu.colors[i].view = 0; + } + if (atts->wgpu.resolves[i].view) { + wgpuTextureViewRelease(atts->wgpu.resolves[i].view); + atts->wgpu.resolves[i].view = 0; + } + } + if (atts->wgpu.depth_stencil.view) { + wgpuTextureViewRelease(atts->wgpu.depth_stencil.view); + atts->wgpu.depth_stencil.view = 0; + } +} + +_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_attachments_color_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + // NOTE: may return null + return atts->wgpu.colors[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_attachments_resolve_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + // NOTE: may return null + return atts->wgpu.resolves[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_attachments_ds_image(const _sg_attachments_t* atts) { + // NOTE: may return null + SOKOL_ASSERT(atts); + return atts->wgpu.depth_stencil.image; +} + +_SOKOL_PRIVATE void _sg_wgpu_init_color_att(WGPURenderPassColorAttachment* wgpu_att, const sg_color_attachment_action* action, WGPUTextureView color_view, WGPUTextureView resolve_view) { + wgpu_att->depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; + wgpu_att->view = color_view; + wgpu_att->resolveTarget = resolve_view; + wgpu_att->loadOp = _sg_wgpu_load_op(color_view, action->load_action); + wgpu_att->storeOp = _sg_wgpu_store_op(color_view, action->store_action); + wgpu_att->clearValue.r = action->clear_value.r; + wgpu_att->clearValue.g = action->clear_value.g; + wgpu_att->clearValue.b = action->clear_value.b; + wgpu_att->clearValue.a = action->clear_value.a; +} + +_SOKOL_PRIVATE void _sg_wgpu_init_ds_att(WGPURenderPassDepthStencilAttachment* wgpu_att, const sg_pass_action* action, sg_pixel_format fmt, WGPUTextureView view) { + wgpu_att->view = view; + wgpu_att->depthLoadOp = _sg_wgpu_load_op(view, action->depth.load_action); + wgpu_att->depthStoreOp = _sg_wgpu_store_op(view, action->depth.store_action); + wgpu_att->depthClearValue = action->depth.clear_value; + wgpu_att->depthReadOnly = false; + if (_sg_is_depth_stencil_format(fmt)) { + wgpu_att->stencilLoadOp = _sg_wgpu_load_op(view, action->stencil.load_action); + wgpu_att->stencilStoreOp = _sg_wgpu_store_op(view, action->stencil.store_action); + } else { + wgpu_att->stencilLoadOp = WGPULoadOp_Undefined; + wgpu_att->stencilStoreOp = WGPUStoreOp_Undefined; + } + wgpu_att->stencilClearValue = action->stencil.clear_value; + wgpu_att->stencilReadOnly = false; +} + +_SOKOL_PRIVATE void _sg_wgpu_begin_pass(const sg_pass* pass) { + SOKOL_ASSERT(pass); + SOKOL_ASSERT(_sg.wgpu.cmd_enc); + SOKOL_ASSERT(_sg.wgpu.dev); + + const _sg_attachments_t* atts = _sg.cur_pass.atts; + const sg_swapchain* swapchain = &pass->swapchain; + const sg_pass_action* action = &pass->action; + + _sg.wgpu.cur_pipeline = 0; + _sg.wgpu.cur_pipeline_id.id = SG_INVALID_ID; + + WGPURenderPassDescriptor wgpu_pass_desc; + WGPURenderPassColorAttachment wgpu_color_att[SG_MAX_COLOR_ATTACHMENTS]; + WGPURenderPassDepthStencilAttachment wgpu_ds_att; + _sg_clear(&wgpu_pass_desc, sizeof(wgpu_pass_desc)); + _sg_clear(&wgpu_color_att, sizeof(wgpu_color_att)); + _sg_clear(&wgpu_ds_att, sizeof(wgpu_ds_att)); + wgpu_pass_desc.label = pass->label; + if (atts) { + SOKOL_ASSERT(atts->slot.state == SG_RESOURCESTATE_VALID); + for (int i = 0; i < atts->cmn.num_colors; i++) { + _sg_wgpu_init_color_att(&wgpu_color_att[i], &action->colors[i], atts->wgpu.colors[i].view, atts->wgpu.resolves[i].view); + } + wgpu_pass_desc.colorAttachmentCount = (size_t)atts->cmn.num_colors; + wgpu_pass_desc.colorAttachments = &wgpu_color_att[0]; + if (atts->wgpu.depth_stencil.image) { + _sg_wgpu_init_ds_att(&wgpu_ds_att, action, atts->wgpu.depth_stencil.image->cmn.pixel_format, atts->wgpu.depth_stencil.view); + wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att; + } + } else { + WGPUTextureView wgpu_color_view = (WGPUTextureView) swapchain->wgpu.render_view; + WGPUTextureView wgpu_resolve_view = (WGPUTextureView) swapchain->wgpu.resolve_view; + WGPUTextureView wgpu_depth_stencil_view = (WGPUTextureView) swapchain->wgpu.depth_stencil_view; + _sg_wgpu_init_color_att(&wgpu_color_att[0], &action->colors[0], wgpu_color_view, wgpu_resolve_view); + wgpu_pass_desc.colorAttachmentCount = 1; + wgpu_pass_desc.colorAttachments = &wgpu_color_att[0]; + if (wgpu_depth_stencil_view) { + SOKOL_ASSERT(swapchain->depth_format > SG_PIXELFORMAT_NONE); + _sg_wgpu_init_ds_att(&wgpu_ds_att, action, swapchain->depth_format, wgpu_depth_stencil_view); + wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att; + } + } + _sg.wgpu.pass_enc = wgpuCommandEncoderBeginRenderPass(_sg.wgpu.cmd_enc, &wgpu_pass_desc); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + + // clear bindings cache and apply an empty image-sampler bindgroup + _sg_wgpu_bindings_cache_clear(); + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, _SG_WGPU_IMAGE_SAMPLER_BINDGROUP_INDEX, _sg.wgpu.empty_bind_group, 0, 0); + _sg_stats_add(wgpu.bindings.num_set_bindgroup, 1); + + // initial uniform buffer binding (required even if no uniforms are set in the frame) + _sg_wgpu_uniform_buffer_on_begin_pass(); +} + +_SOKOL_PRIVATE void _sg_wgpu_end_pass(void) { + if (_sg.wgpu.pass_enc) { + wgpuRenderPassEncoderEnd(_sg.wgpu.pass_enc); + wgpuRenderPassEncoderRelease(_sg.wgpu.pass_enc); + _sg.wgpu.pass_enc = 0; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_commit(void) { + SOKOL_ASSERT(_sg.wgpu.cmd_enc); + + _sg_wgpu_uniform_buffer_on_commit(); + + WGPUCommandBufferDescriptor cmd_buf_desc; + _sg_clear(&cmd_buf_desc, sizeof(cmd_buf_desc)); + WGPUCommandBuffer wgpu_cmd_buf = wgpuCommandEncoderFinish(_sg.wgpu.cmd_enc, &cmd_buf_desc); + SOKOL_ASSERT(wgpu_cmd_buf); + wgpuCommandEncoderRelease(_sg.wgpu.cmd_enc); + _sg.wgpu.cmd_enc = 0; + + wgpuQueueSubmit(_sg.wgpu.queue, 1, &wgpu_cmd_buf); + wgpuCommandBufferRelease(wgpu_cmd_buf); + + // create a new render-command-encoder for next frame + WGPUCommandEncoderDescriptor cmd_enc_desc; + _sg_clear(&cmd_enc_desc, sizeof(cmd_enc_desc)); + _sg.wgpu.cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.wgpu.pass_enc); + // FIXME FIXME FIXME: CLIPPING THE VIEWPORT HERE IS WRONG!!! + // (but currently required because WebGPU insists that the viewport rectangle must be + // fully contained inside the framebuffer, but this doesn't make any sense, and also + // isn't required by the backend APIs) + const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.cur_pass.width, _sg.cur_pass.height); + float xf = (float) clip.x; + float yf = (float) (origin_top_left ? clip.y : (_sg.cur_pass.height - (clip.y + clip.h))); + float wf = (float) clip.w; + float hf = (float) clip.h; + wgpuRenderPassEncoderSetViewport(_sg.wgpu.pass_enc, xf, yf, wf, hf, 0.0f, 1.0f); +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.wgpu.pass_enc); + const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.cur_pass.width, _sg.cur_pass.height); + uint32_t sx = (uint32_t) clip.x; + uint32_t sy = (uint32_t) (origin_top_left ? clip.y : (_sg.cur_pass.height - (clip.y + clip.h))); + uint32_t sw = (uint32_t) clip.w; + uint32_t sh = (uint32_t) clip.h; + wgpuRenderPassEncoderSetScissorRect(_sg.wgpu.pass_enc, sx, sy, sw, sh); +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->wgpu.pip); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + _sg.wgpu.use_indexed_draw = (pip->cmn.index_type != SG_INDEXTYPE_NONE); + _sg.wgpu.cur_pipeline = pip; + _sg.wgpu.cur_pipeline_id.id = pip->slot.id; + wgpuRenderPassEncoderSetPipeline(_sg.wgpu.pass_enc, pip->wgpu.pip); + wgpuRenderPassEncoderSetBlendConstant(_sg.wgpu.pass_enc, &pip->wgpu.blend_color); + wgpuRenderPassEncoderSetStencilReference(_sg.wgpu.pass_enc, pip->cmn.stencil.ref); +} + +_SOKOL_PRIVATE bool _sg_wgpu_apply_bindings(_sg_bindings_t* bnd) { + SOKOL_ASSERT(_sg.wgpu.pass_enc); + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip->shader && (bnd->pip->cmn.shader_id.id == bnd->pip->shader->slot.id)); + bool retval = true; + retval &= _sg_wgpu_apply_index_buffer(bnd); + retval &= _sg_wgpu_apply_vertex_buffers(bnd); + retval &= _sg_wgpu_apply_bindgroup(bnd); + return retval; +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + const uint32_t alignment = _sg.wgpu.limits.limits.minUniformBufferOffsetAlignment; + SOKOL_ASSERT(_sg.wgpu.pass_enc); + SOKOL_ASSERT(_sg.wgpu.uniform.staging); + SOKOL_ASSERT((_sg.wgpu.uniform.offset + data->size) <= _sg.wgpu.uniform.num_bytes); + SOKOL_ASSERT((_sg.wgpu.uniform.offset & (alignment - 1)) == 0); + SOKOL_ASSERT(_sg.wgpu.cur_pipeline && _sg.wgpu.cur_pipeline->shader); + SOKOL_ASSERT(_sg.wgpu.cur_pipeline->slot.id == _sg.wgpu.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.wgpu.cur_pipeline->shader->slot.id == _sg.wgpu.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(ub_index < _sg.wgpu.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); + SOKOL_ASSERT(data->size <= _sg.wgpu.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); + SOKOL_ASSERT(data->size <= _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE); + + _sg_stats_add(wgpu.uniforms.num_set_bindgroup, 1); + memcpy(_sg.wgpu.uniform.staging + _sg.wgpu.uniform.offset, data->ptr, data->size); + _sg.wgpu.uniform.bind.offsets[stage_index][ub_index] = _sg.wgpu.uniform.offset; + _sg.wgpu.uniform.offset = _sg_roundup_u32(_sg.wgpu.uniform.offset + (uint32_t)data->size, alignment); + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, + _SG_WGPU_UNIFORM_BINDGROUP_INDEX, + _sg.wgpu.uniform.bind.group, + SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS, + &_sg.wgpu.uniform.bind.offsets[0][0]); +} + +_SOKOL_PRIVATE void _sg_wgpu_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.wgpu.pass_enc); + SOKOL_ASSERT(_sg.wgpu.cur_pipeline && (_sg.wgpu.cur_pipeline->slot.id == _sg.wgpu.cur_pipeline_id.id)); + if (SG_INDEXTYPE_NONE != _sg.wgpu.cur_pipeline->cmn.index_type) { + wgpuRenderPassEncoderDrawIndexed(_sg.wgpu.pass_enc, (uint32_t)num_elements, (uint32_t)num_instances, (uint32_t)base_element, 0, 0); + } else { + wgpuRenderPassEncoderDraw(_sg.wgpu.pass_enc, (uint32_t)num_elements, (uint32_t)num_instances, (uint32_t)base_element, 0); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + SOKOL_ASSERT(buf); + _sg_wgpu_copy_buffer_data(buf, 0, data); +} + +_SOKOL_PRIVATE void _sg_wgpu_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + _SOKOL_UNUSED(new_frame); + _sg_wgpu_copy_buffer_data(buf, (uint64_t)buf->cmn.append_pos, data); +} + +_SOKOL_PRIVATE void _sg_wgpu_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + _sg_wgpu_copy_image_data(img, img->wgpu.tex, data); +} +#endif + +// ██████ ███████ ███ ██ ███████ ██████ ██ ██████ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ███ █████ ██ ██ ██ █████ ██████ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ███████ ██ ████ ███████ ██ ██ ██ ██████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>generic backend +static inline void _sg_setup_backend(const sg_desc* desc) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_setup_backend(desc); + #elif defined(SOKOL_METAL) + _sg_mtl_setup_backend(desc); + #elif defined(SOKOL_D3D11) + _sg_d3d11_setup_backend(desc); + #elif defined(SOKOL_WGPU) + _sg_wgpu_setup_backend(desc); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_setup_backend(desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_backend(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_backend(); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_backend(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_backend(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_backend(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_backend(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_reset_state_cache(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_reset_state_cache(); + #elif defined(SOKOL_METAL) + _sg_mtl_reset_state_cache(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_reset_state_cache(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_reset_state_cache(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_reset_state_cache(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_buffer(buf, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_buffer(buf, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_buffer(buf, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_buffer(buf, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_buffer(buf, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_buffer(_sg_buffer_t* buf) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_buffer(buf); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_buffer(buf); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_buffer(buf); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_buffer(buf); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_buffer(buf); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_image(img, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_image(img, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_image(img, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_image(img, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_image(img, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_image(_sg_image_t* img) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_image(img); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_image(img); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_image(img); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_image(img); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_image(img); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_sampler(smp, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_sampler(smp, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_sampler(smp, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_sampler(smp, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_sampler(smp, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_sampler(_sg_sampler_t* smp) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_sampler(smp); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_sampler(smp); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_sampler(smp); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_sampler(smp); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_sampler(smp); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_shader(shd, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_shader(shd, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_shader(shd, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_shader(shd, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_shader(shd, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_shader(_sg_shader_t* shd) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_shader(shd); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_shader(shd); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_shader(shd); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_shader(shd); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_shader(shd); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_pipeline(pip, shd, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_pipeline(pip, shd, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_pipeline(pip, shd, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_pipeline(pip, shd, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_pipeline(pip, shd, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_pipeline(_sg_pipeline_t* pip) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_pipeline(pip); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_pipeline(pip); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_pipeline(pip); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_pipeline(pip); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_pipeline(pip); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_attachments(_sg_attachments_t* atts, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_image, const sg_attachments_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_attachments(atts, color_images, resolve_images, ds_image, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_attachments(atts, color_images, resolve_images, ds_image, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_attachments(atts, color_images, resolve_images, ds_image, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_attachments(atts, color_images, resolve_images, ds_image, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_attachments(atts, color_images, resolve_images, ds_image, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_attachments(_sg_attachments_t* atts) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_attachments(atts); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_attachments(atts); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_attachments(atts); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_discard_attachments(atts); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_attachments(atts); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline _sg_image_t* _sg_attachments_color_image(const _sg_attachments_t* atts, int index) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_attachments_color_image(atts, index); + #elif defined(SOKOL_METAL) + return _sg_mtl_attachments_color_image(atts, index); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_attachments_color_image(atts, index); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_attachments_color_image(atts, index); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_attachments_color_image(atts, index); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline _sg_image_t* _sg_attachments_resolve_image(const _sg_attachments_t* atts, int index) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_attachments_resolve_image(atts, index); + #elif defined(SOKOL_METAL) + return _sg_mtl_attachments_resolve_image(atts, index); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_attachments_resolve_image(atts, index); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_attachments_resolve_image(atts, index); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_attachments_resolve_image(atts, index); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline _sg_image_t* _sg_attachments_ds_image(const _sg_attachments_t* atts) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_attachments_ds_image(atts); + #elif defined(SOKOL_METAL) + return _sg_mtl_attachments_ds_image(atts); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_attachments_ds_image(atts); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_attachments_ds_image(atts); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_attachments_ds_image(atts); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_begin_pass(const sg_pass* pass) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_begin_pass(pass); + #elif defined(SOKOL_METAL) + _sg_mtl_begin_pass(pass); + #elif defined(SOKOL_D3D11) + _sg_d3d11_begin_pass(pass); + #elif defined(SOKOL_WGPU) + _sg_wgpu_begin_pass(pass); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_begin_pass(pass); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_end_pass(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_end_pass(); + #elif defined(SOKOL_METAL) + _sg_mtl_end_pass(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_end_pass(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_end_pass(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_end_pass(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_viewport(x, y, w, h, origin_top_left); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_scissor_rect(x, y, w, h, origin_top_left); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_pipeline(_sg_pipeline_t* pip) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_pipeline(pip); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_pipeline(pip); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_pipeline(pip); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_pipeline(pip); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_pipeline(pip); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline bool _sg_apply_bindings(_sg_bindings_t* bnd) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_apply_bindings(bnd); + #elif defined(SOKOL_METAL) + return _sg_mtl_apply_bindings(bnd); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_apply_bindings(bnd); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_apply_bindings(bnd); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_apply_bindings(bnd); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_uniforms(stage_index, ub_index, data); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_uniforms(stage_index, ub_index, data); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_uniforms(stage_index, ub_index, data); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_uniforms(stage_index, ub_index, data); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_uniforms(stage_index, ub_index, data); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_draw(int base_element, int num_elements, int num_instances) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_draw(base_element, num_elements, num_instances); + #elif defined(SOKOL_METAL) + _sg_mtl_draw(base_element, num_elements, num_instances); + #elif defined(SOKOL_D3D11) + _sg_d3d11_draw(base_element, num_elements, num_instances); + #elif defined(SOKOL_WGPU) + _sg_wgpu_draw(base_element, num_elements, num_instances); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_draw(base_element, num_elements, num_instances); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_commit(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_commit(); + #elif defined(SOKOL_METAL) + _sg_mtl_commit(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_commit(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_commit(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_commit(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_update_buffer(buf, data); + #elif defined(SOKOL_METAL) + _sg_mtl_update_buffer(buf, data); + #elif defined(SOKOL_D3D11) + _sg_d3d11_update_buffer(buf, data); + #elif defined(SOKOL_WGPU) + _sg_wgpu_update_buffer(buf, data); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_update_buffer(buf, data); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_append_buffer(buf, data, new_frame); + #elif defined(SOKOL_METAL) + _sg_mtl_append_buffer(buf, data, new_frame); + #elif defined(SOKOL_D3D11) + _sg_d3d11_append_buffer(buf, data, new_frame); + #elif defined(SOKOL_WGPU) + _sg_wgpu_append_buffer(buf, data, new_frame); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_append_buffer(buf, data, new_frame); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_update_image(_sg_image_t* img, const sg_image_data* data) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_update_image(img, data); + #elif defined(SOKOL_METAL) + _sg_mtl_update_image(img, data); + #elif defined(SOKOL_D3D11) + _sg_d3d11_update_image(img, data); + #elif defined(SOKOL_WGPU) + _sg_wgpu_update_image(img, data); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_update_image(img, data); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_push_debug_group(const char* name) { + #if defined(SOKOL_METAL) + _sg_mtl_push_debug_group(name); + #else + _SOKOL_UNUSED(name); + #endif +} + +static inline void _sg_pop_debug_group(void) { + #if defined(SOKOL_METAL) + _sg_mtl_pop_debug_group(); + #endif +} + +// ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ +// +// >>pool +_SOKOL_PRIVATE void _sg_init_pool(_sg_pool_t* pool, int num) { + SOKOL_ASSERT(pool && (num >= 1)); + // slot 0 is reserved for the 'invalid id', so bump the pool size by 1 + pool->size = num + 1; + pool->queue_top = 0; + // generation counters indexable by pool slot index, slot 0 is reserved + size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size; + pool->gen_ctrs = (uint32_t*)_sg_malloc_clear(gen_ctrs_size); + // it's not a bug to only reserve 'num' here + pool->free_queue = (int*) _sg_malloc_clear(sizeof(int) * (size_t)num); + // never allocate the zero-th pool item since the invalid id is 0 + for (int i = pool->size-1; i >= 1; i--) { + pool->free_queue[pool->queue_top++] = i; + } +} + +_SOKOL_PRIVATE void _sg_discard_pool(_sg_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + _sg_free(pool->free_queue); + pool->free_queue = 0; + SOKOL_ASSERT(pool->gen_ctrs); + _sg_free(pool->gen_ctrs); + pool->gen_ctrs = 0; + pool->size = 0; + pool->queue_top = 0; +} + +_SOKOL_PRIVATE int _sg_pool_alloc_index(_sg_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } else { + // pool exhausted + return _SG_INVALID_SLOT_INDEX; + } +} + +_SOKOL_PRIVATE void _sg_pool_free_index(_sg_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_ASSERT(pool->queue_top < pool->size); + #ifdef SOKOL_DEBUG + // debug check against double-free + for (int i = 0; i < pool->queue_top; i++) { + SOKOL_ASSERT(pool->free_queue[i] != slot_index); + } + #endif + pool->free_queue[pool->queue_top++] = slot_index; + SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); +} + +_SOKOL_PRIVATE void _sg_reset_slot(_sg_slot_t* slot) { + SOKOL_ASSERT(slot); + _sg_clear(slot, sizeof(_sg_slot_t)); +} + +_SOKOL_PRIVATE void _sg_reset_buffer_to_alloc_state(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + _sg_slot_t slot = buf->slot; + _sg_clear(buf, sizeof(*buf)); + buf->slot = slot; + buf->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_image_to_alloc_state(_sg_image_t* img) { + SOKOL_ASSERT(img); + _sg_slot_t slot = img->slot; + _sg_clear(img, sizeof(*img)); + img->slot = slot; + img->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_sampler_to_alloc_state(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + _sg_slot_t slot = smp->slot; + _sg_clear(smp, sizeof(*smp)); + smp->slot = slot; + smp->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_shader_to_alloc_state(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _sg_slot_t slot = shd->slot; + _sg_clear(shd, sizeof(*shd)); + shd->slot = slot; + shd->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_pipeline_to_alloc_state(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _sg_slot_t slot = pip->slot; + _sg_clear(pip, sizeof(*pip)); + pip->slot = slot; + pip->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_attachments_to_alloc_state(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + _sg_slot_t slot = atts->slot; + _sg_clear(atts, sizeof(*atts)); + atts->slot = slot; + atts->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_setup_pools(_sg_pools_t* p, const sg_desc* desc) { + SOKOL_ASSERT(p); + SOKOL_ASSERT(desc); + // note: the pools here will have an additional item, since slot 0 is reserved + SOKOL_ASSERT((desc->buffer_pool_size > 0) && (desc->buffer_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->buffer_pool, desc->buffer_pool_size); + size_t buffer_pool_byte_size = sizeof(_sg_buffer_t) * (size_t)p->buffer_pool.size; + p->buffers = (_sg_buffer_t*) _sg_malloc_clear(buffer_pool_byte_size); + + SOKOL_ASSERT((desc->image_pool_size > 0) && (desc->image_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->image_pool, desc->image_pool_size); + size_t image_pool_byte_size = sizeof(_sg_image_t) * (size_t)p->image_pool.size; + p->images = (_sg_image_t*) _sg_malloc_clear(image_pool_byte_size); + + SOKOL_ASSERT((desc->sampler_pool_size > 0) && (desc->sampler_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->sampler_pool, desc->sampler_pool_size); + size_t sampler_pool_byte_size = sizeof(_sg_sampler_t) * (size_t)p->sampler_pool.size; + p->samplers = (_sg_sampler_t*) _sg_malloc_clear(sampler_pool_byte_size); + + SOKOL_ASSERT((desc->shader_pool_size > 0) && (desc->shader_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->shader_pool, desc->shader_pool_size); + size_t shader_pool_byte_size = sizeof(_sg_shader_t) * (size_t)p->shader_pool.size; + p->shaders = (_sg_shader_t*) _sg_malloc_clear(shader_pool_byte_size); + + SOKOL_ASSERT((desc->pipeline_pool_size > 0) && (desc->pipeline_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->pipeline_pool, desc->pipeline_pool_size); + size_t pipeline_pool_byte_size = sizeof(_sg_pipeline_t) * (size_t)p->pipeline_pool.size; + p->pipelines = (_sg_pipeline_t*) _sg_malloc_clear(pipeline_pool_byte_size); + + SOKOL_ASSERT((desc->attachments_pool_size > 0) && (desc->attachments_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->attachments_pool, desc->attachments_pool_size); + size_t attachments_pool_byte_size = sizeof(_sg_attachments_t) * (size_t)p->attachments_pool.size; + p->attachments = (_sg_attachments_t*) _sg_malloc_clear(attachments_pool_byte_size); +} + +_SOKOL_PRIVATE void _sg_discard_pools(_sg_pools_t* p) { + SOKOL_ASSERT(p); + _sg_free(p->attachments); p->attachments = 0; + _sg_free(p->pipelines); p->pipelines = 0; + _sg_free(p->shaders); p->shaders = 0; + _sg_free(p->samplers); p->samplers = 0; + _sg_free(p->images); p->images = 0; + _sg_free(p->buffers); p->buffers = 0; + _sg_discard_pool(&p->attachments_pool); + _sg_discard_pool(&p->pipeline_pool); + _sg_discard_pool(&p->shader_pool); + _sg_discard_pool(&p->sampler_pool); + _sg_discard_pool(&p->image_pool); + _sg_discard_pool(&p->buffer_pool); +} + +/* allocate the slot at slot_index: + - bump the slot's generation counter + - create a resource id from the generation counter and slot index + - set the slot's id to this id + - set the slot's state to ALLOC + - return the resource id +*/ +_SOKOL_PRIVATE uint32_t _sg_slot_alloc(_sg_pool_t* pool, _sg_slot_t* slot, int slot_index) { + /* FIXME: add handling for an overflowing generation counter, + for now, just overflow (another option is to disable + the slot) + */ + SOKOL_ASSERT(pool && pool->gen_ctrs); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(slot->id == SG_INVALID_ID); + SOKOL_ASSERT(slot->state == SG_RESOURCESTATE_INITIAL); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + slot->id = (ctr<<_SG_SLOT_SHIFT)|(slot_index & _SG_SLOT_MASK); + slot->state = SG_RESOURCESTATE_ALLOC; + return slot->id; +} + +// extract slot index from id +_SOKOL_PRIVATE int _sg_slot_index(uint32_t id) { + int slot_index = (int) (id & _SG_SLOT_MASK); + SOKOL_ASSERT(_SG_INVALID_SLOT_INDEX != slot_index); + return slot_index; +} + +// returns pointer to resource by id without matching id check +_SOKOL_PRIVATE _sg_buffer_t* _sg_buffer_at(const _sg_pools_t* p, uint32_t buf_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != buf_id)); + int slot_index = _sg_slot_index(buf_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->buffer_pool.size)); + return &p->buffers[slot_index]; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_image_at(const _sg_pools_t* p, uint32_t img_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != img_id)); + int slot_index = _sg_slot_index(img_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->image_pool.size)); + return &p->images[slot_index]; +} + +_SOKOL_PRIVATE _sg_sampler_t* _sg_sampler_at(const _sg_pools_t* p, uint32_t smp_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != smp_id)); + int slot_index = _sg_slot_index(smp_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->sampler_pool.size)); + return &p->samplers[slot_index]; +} + +_SOKOL_PRIVATE _sg_shader_t* _sg_shader_at(const _sg_pools_t* p, uint32_t shd_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != shd_id)); + int slot_index = _sg_slot_index(shd_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->shader_pool.size)); + return &p->shaders[slot_index]; +} + +_SOKOL_PRIVATE _sg_pipeline_t* _sg_pipeline_at(const _sg_pools_t* p, uint32_t pip_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != pip_id)); + int slot_index = _sg_slot_index(pip_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->pipeline_pool.size)); + return &p->pipelines[slot_index]; +} + +_SOKOL_PRIVATE _sg_attachments_t* _sg_attachments_at(const _sg_pools_t* p, uint32_t atts_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != atts_id)); + int slot_index = _sg_slot_index(atts_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->attachments_pool.size)); + return &p->attachments[slot_index]; +} + +// returns pointer to resource with matching id check, may return 0 +_SOKOL_PRIVATE _sg_buffer_t* _sg_lookup_buffer(const _sg_pools_t* p, uint32_t buf_id) { + if (SG_INVALID_ID != buf_id) { + _sg_buffer_t* buf = _sg_buffer_at(p, buf_id); + if (buf->slot.id == buf_id) { + return buf; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_lookup_image(const _sg_pools_t* p, uint32_t img_id) { + if (SG_INVALID_ID != img_id) { + _sg_image_t* img = _sg_image_at(p, img_id); + if (img->slot.id == img_id) { + return img; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_sampler_t* _sg_lookup_sampler(const _sg_pools_t* p, uint32_t smp_id) { + if (SG_INVALID_ID != smp_id) { + _sg_sampler_t* smp = _sg_sampler_at(p, smp_id); + if (smp->slot.id == smp_id) { + return smp; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_shader_t* _sg_lookup_shader(const _sg_pools_t* p, uint32_t shd_id) { + SOKOL_ASSERT(p); + if (SG_INVALID_ID != shd_id) { + _sg_shader_t* shd = _sg_shader_at(p, shd_id); + if (shd->slot.id == shd_id) { + return shd; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_pipeline_t* _sg_lookup_pipeline(const _sg_pools_t* p, uint32_t pip_id) { + SOKOL_ASSERT(p); + if (SG_INVALID_ID != pip_id) { + _sg_pipeline_t* pip = _sg_pipeline_at(p, pip_id); + if (pip->slot.id == pip_id) { + return pip; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_attachments_t* _sg_lookup_attachments(const _sg_pools_t* p, uint32_t atts_id) { + SOKOL_ASSERT(p); + if (SG_INVALID_ID != atts_id) { + _sg_attachments_t* atts = _sg_attachments_at(p, atts_id); + if (atts->slot.id == atts_id) { + return atts; + } + } + return 0; +} + +_SOKOL_PRIVATE void _sg_discard_all_resources(_sg_pools_t* p) { + /* this is a bit dumb since it loops over all pool slots to + find the occupied slots, on the other hand it is only ever + executed at shutdown + NOTE: ONLY EXECUTE THIS AT SHUTDOWN + ...because the free queues will not be reset + and the resource slots not be cleared! + */ + for (int i = 1; i < p->buffer_pool.size; i++) { + sg_resource_state state = p->buffers[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_buffer(&p->buffers[i]); + } + } + for (int i = 1; i < p->image_pool.size; i++) { + sg_resource_state state = p->images[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_image(&p->images[i]); + } + } + for (int i = 1; i < p->sampler_pool.size; i++) { + sg_resource_state state = p->samplers[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_sampler(&p->samplers[i]); + } + } + for (int i = 1; i < p->shader_pool.size; i++) { + sg_resource_state state = p->shaders[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_shader(&p->shaders[i]); + } + } + for (int i = 1; i < p->pipeline_pool.size; i++) { + sg_resource_state state = p->pipelines[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_pipeline(&p->pipelines[i]); + } + } + for (int i = 1; i < p->attachments_pool.size; i++) { + sg_resource_state state = p->attachments[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_attachments(&p->attachments[i]); + } + } +} + +// ██ ██ █████ ██ ██ ██████ █████ ████████ ██ ██████ ███ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ███████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ████ ██ ██ ███████ ██ ██████ ██ ██ ██ ██ ██████ ██ ████ +// +// >>validation +#if defined(SOKOL_DEBUG) +_SOKOL_PRIVATE void _sg_validate_begin(void) { + _sg.validate_error = SG_LOGITEM_OK; +} + +_SOKOL_PRIVATE bool _sg_validate_end(void) { + if (_sg.validate_error != SG_LOGITEM_OK) { + #if !defined(SOKOL_VALIDATE_NON_FATAL) + _SG_PANIC(VALIDATION_FAILED); + return false; + #else + return false; + #endif + } else { + return true; + } +} +#endif + +_SOKOL_PRIVATE bool _sg_validate_buffer_desc(const sg_buffer_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_BUFFERDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_BUFFERDESC_CANARY); + _SG_VALIDATE(desc->size > 0, VALIDATE_BUFFERDESC_SIZE); + bool injected = (0 != desc->gl_buffers[0]) || + (0 != desc->mtl_buffers[0]) || + (0 != desc->d3d11_buffer) || + (0 != desc->wgpu_buffer); + if (!injected && (desc->usage == SG_USAGE_IMMUTABLE)) { + _SG_VALIDATE((0 != desc->data.ptr) && (desc->data.size > 0), VALIDATE_BUFFERDESC_DATA); + _SG_VALIDATE(desc->size == desc->data.size, VALIDATE_BUFFERDESC_DATA_SIZE); + } else { + _SG_VALIDATE(0 == desc->data.ptr, VALIDATE_BUFFERDESC_NO_DATA); + } + if (desc->type == SG_BUFFERTYPE_STORAGEBUFFER) { + _SG_VALIDATE(_sg.features.storage_buffer, VALIDATE_BUFFERDESC_STORAGEBUFFER_SUPPORTED); + _SG_VALIDATE(_sg_multiple_u64(desc->size, 4), VALIDATE_BUFFERDESC_STORAGEBUFFER_SIZE_MULTIPLE_4); + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE void _sg_validate_image_data(const sg_image_data* data, sg_pixel_format fmt, int width, int height, int num_faces, int num_mips, int num_slices) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(data); + _SOKOL_UNUSED(fmt); + _SOKOL_UNUSED(width); + _SOKOL_UNUSED(height); + _SOKOL_UNUSED(num_faces); + _SOKOL_UNUSED(num_mips); + _SOKOL_UNUSED(num_slices); + #else + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < num_mips; mip_index++) { + const bool has_data = data->subimage[face_index][mip_index].ptr != 0; + const bool has_size = data->subimage[face_index][mip_index].size > 0; + _SG_VALIDATE(has_data && has_size, VALIDATE_IMAGEDATA_NODATA); + const int mip_width = _sg_miplevel_dim(width, mip_index); + const int mip_height = _sg_miplevel_dim(height, mip_index); + const int bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, 1); + const int expected_size = bytes_per_slice * num_slices; + _SG_VALIDATE(expected_size == (int)data->subimage[face_index][mip_index].size, VALIDATE_IMAGEDATA_DATA_SIZE); + } + } + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_IMAGEDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_IMAGEDESC_CANARY); + _SG_VALIDATE(desc->width > 0, VALIDATE_IMAGEDESC_WIDTH); + _SG_VALIDATE(desc->height > 0, VALIDATE_IMAGEDESC_HEIGHT); + const sg_pixel_format fmt = desc->pixel_format; + const sg_usage usage = desc->usage; + const bool injected = (0 != desc->gl_textures[0]) || + (0 != desc->mtl_textures[0]) || + (0 != desc->d3d11_texture) || + (0 != desc->wgpu_texture); + if (_sg_is_depth_or_depth_stencil_format(fmt)) { + _SG_VALIDATE(desc->type != SG_IMAGETYPE_3D, VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE); + } + if (desc->render_target) { + SOKOL_ASSERT(((int)fmt >= 0) && ((int)fmt < _SG_PIXELFORMAT_NUM)); + _SG_VALIDATE(_sg.formats[fmt].render, VALIDATE_IMAGEDESC_RT_PIXELFORMAT); + _SG_VALIDATE(usage == SG_USAGE_IMMUTABLE, VALIDATE_IMAGEDESC_RT_IMMUTABLE); + _SG_VALIDATE(desc->data.subimage[0][0].ptr==0, VALIDATE_IMAGEDESC_RT_NO_DATA); + if (desc->sample_count > 1) { + _SG_VALIDATE(_sg.formats[fmt].msaa, VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT); + _SG_VALIDATE(desc->num_mipmaps == 1, VALIDATE_IMAGEDESC_MSAA_NUM_MIPMAPS); + _SG_VALIDATE(desc->type != SG_IMAGETYPE_3D, VALIDATE_IMAGEDESC_MSAA_3D_IMAGE); + } + } else { + _SG_VALIDATE(desc->sample_count == 1, VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT); + const bool valid_nonrt_fmt = !_sg_is_valid_rendertarget_depth_format(fmt); + _SG_VALIDATE(valid_nonrt_fmt, VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT); + const bool is_compressed = _sg_is_compressed_pixel_format(desc->pixel_format); + const bool is_immutable = (usage == SG_USAGE_IMMUTABLE); + if (is_compressed) { + _SG_VALIDATE(is_immutable, VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE); + } + if (!injected && is_immutable) { + // image desc must have valid data + _sg_validate_image_data(&desc->data, + desc->pixel_format, + desc->width, + desc->height, + (desc->type == SG_IMAGETYPE_CUBE) ? 6 : 1, + desc->num_mipmaps, + desc->num_slices); + } else { + // image desc must not have data + for (int face_index = 0; face_index < SG_CUBEFACE_NUM; face_index++) { + for (int mip_index = 0; mip_index < SG_MAX_MIPMAPS; mip_index++) { + const bool no_data = 0 == desc->data.subimage[face_index][mip_index].ptr; + const bool no_size = 0 == desc->data.subimage[face_index][mip_index].size; + if (injected) { + _SG_VALIDATE(no_data && no_size, VALIDATE_IMAGEDESC_INJECTED_NO_DATA); + } + if (!is_immutable) { + _SG_VALIDATE(no_data && no_size, VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA); + } + } + } + } + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_sampler_desc(const sg_sampler_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_SAMPLERDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_SAMPLERDESC_CANARY); + _SG_VALIDATE(desc->min_filter != SG_FILTER_NONE, VALIDATE_SAMPLERDESC_MINFILTER_NONE); + _SG_VALIDATE(desc->mag_filter != SG_FILTER_NONE, VALIDATE_SAMPLERDESC_MAGFILTER_NONE); + // restriction from WebGPU: when anisotropy > 1, all filters must be linear + if (desc->max_anisotropy > 1) { + _SG_VALIDATE((desc->min_filter == SG_FILTER_LINEAR) + && (desc->mag_filter == SG_FILTER_LINEAR) + && (desc->mipmap_filter == SG_FILTER_LINEAR), + VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING); + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_SHADERDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_SHADERDESC_CANARY); + #if defined(SOKOL_GLCORE) || defined(SOKOL_GLES3) || defined(SOKOL_WGPU) + // on GL or WebGPU, must provide shader source code + _SG_VALIDATE(0 != desc->vs.source, VALIDATE_SHADERDESC_SOURCE); + _SG_VALIDATE(0 != desc->fs.source, VALIDATE_SHADERDESC_SOURCE); + #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) + // on Metal or D3D11, must provide shader source code or byte code + _SG_VALIDATE((0 != desc->vs.source)||(0 != desc->vs.bytecode.ptr), VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); + _SG_VALIDATE((0 != desc->fs.source)||(0 != desc->fs.bytecode.ptr), VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); + #else + // Dummy Backend, don't require source or bytecode + #endif + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + if (desc->attrs[i].name) { + _SG_VALIDATE(strlen(desc->attrs[i].name) < _SG_STRING_SIZE, VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); + } + if (desc->attrs[i].sem_name) { + _SG_VALIDATE(strlen(desc->attrs[i].sem_name) < _SG_STRING_SIZE, VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); + } + } + // if shader byte code, the size must also be provided + if (0 != desc->vs.bytecode.ptr) { + _SG_VALIDATE(desc->vs.bytecode.size > 0, VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + } + if (0 != desc->fs.bytecode.ptr) { + _SG_VALIDATE(desc->fs.bytecode.size > 0, VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + } + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == 0)? &desc->vs : &desc->fs; + bool uniform_blocks_continuous = true; + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + if (ub_desc->size > 0) { + _SG_VALIDATE(uniform_blocks_continuous, VALIDATE_SHADERDESC_NO_CONT_UBS); + #if defined(_SOKOL_ANY_GL) + bool uniforms_continuous = true; + uint32_t uniform_offset = 0; + int num_uniforms = 0; + for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { + const sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; + if (u_desc->type != SG_UNIFORMTYPE_INVALID) { + _SG_VALIDATE(uniforms_continuous, VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS); + #if defined(SOKOL_GLES3) + _SG_VALIDATE(0 != u_desc->name, VALIDATE_SHADERDESC_UB_MEMBER_NAME); + #endif + const int array_count = u_desc->array_count; + _SG_VALIDATE(array_count > 0, VALIDATE_SHADERDESC_UB_ARRAY_COUNT); + const uint32_t u_align = _sg_uniform_alignment(u_desc->type, array_count, ub_desc->layout); + const uint32_t u_size = _sg_uniform_size(u_desc->type, array_count, ub_desc->layout); + uniform_offset = _sg_align_u32(uniform_offset, u_align); + uniform_offset += u_size; + num_uniforms++; + // with std140, arrays are only allowed for FLOAT4, INT4, MAT4 + if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { + if (array_count > 1) { + _SG_VALIDATE((u_desc->type == SG_UNIFORMTYPE_FLOAT4) || (u_desc->type == SG_UNIFORMTYPE_INT4) || (u_desc->type == SG_UNIFORMTYPE_MAT4), VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE); + } + } + } else { + uniforms_continuous = false; + } + } + if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { + uniform_offset = _sg_align_u32(uniform_offset, 16); + } + _SG_VALIDATE((size_t)uniform_offset == ub_desc->size, VALIDATE_SHADERDESC_UB_SIZE_MISMATCH); + _SG_VALIDATE(num_uniforms > 0, VALIDATE_SHADERDESC_NO_UB_MEMBERS); + #endif + } else { + uniform_blocks_continuous = false; + } + } + bool storage_buffers_continuous = true; + for (int sbuf_index = 0; sbuf_index < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; sbuf_index++) { + const sg_shader_storage_buffer_desc* sbuf_desc = &stage_desc->storage_buffers[sbuf_index]; + if (sbuf_desc->used) { + _SG_VALIDATE(storage_buffers_continuous, VALIDATE_SHADERDESC_NO_CONT_STORAGEBUFFERS); + _SG_VALIDATE(sbuf_desc->readonly, VALIDATE_SHADERDESC_STORAGEBUFFER_READONLY); + } else { + storage_buffers_continuous = false; + } + } + bool images_continuous = true; + int num_images = 0; + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (img_desc->used) { + _SG_VALIDATE(images_continuous, VALIDATE_SHADERDESC_NO_CONT_IMAGES); + num_images++; + } else { + images_continuous = false; + } + } + bool samplers_continuous = true; + int num_samplers = 0; + for (int smp_index = 0; smp_index < SG_MAX_SHADERSTAGE_SAMPLERS; smp_index++) { + const sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_index]; + if (smp_desc->used) { + _SG_VALIDATE(samplers_continuous, VALIDATE_SHADERDESC_NO_CONT_SAMPLERS); + num_samplers++; + } else { + samplers_continuous = false; + } + } + bool image_samplers_continuous = true; + int num_image_samplers = 0; + for (int img_smp_index = 0; img_smp_index < SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS; img_smp_index++) { + const sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_index]; + if (img_smp_desc->used) { + _SG_VALIDATE(image_samplers_continuous, VALIDATE_SHADERDESC_NO_CONT_IMAGE_SAMPLER_PAIRS); + num_image_samplers++; + const bool img_slot_in_range = (img_smp_desc->image_slot >= 0) && (img_smp_desc->image_slot < SG_MAX_SHADERSTAGE_IMAGES); + const bool smp_slot_in_range = (img_smp_desc->sampler_slot >= 0) && (img_smp_desc->sampler_slot < SG_MAX_SHADERSTAGE_SAMPLERS); + _SG_VALIDATE(img_slot_in_range && (img_smp_desc->image_slot < num_images), VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_IMAGE_SLOT_OUT_OF_RANGE); + _SG_VALIDATE(smp_slot_in_range && (img_smp_desc->sampler_slot < num_samplers), VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_IMAGE_SLOT_OUT_OF_RANGE); + #if defined(_SOKOL_ANY_GL) + _SG_VALIDATE(img_smp_desc->glsl_name != 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_NAME_REQUIRED_FOR_GL); + #endif + if (img_slot_in_range && smp_slot_in_range) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_smp_desc->image_slot]; + const sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[img_smp_desc->sampler_slot]; + const bool needs_nonfiltering = (img_desc->sample_type == SG_IMAGESAMPLETYPE_UINT) + || (img_desc->sample_type == SG_IMAGESAMPLETYPE_SINT) + || (img_desc->sample_type == SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT); + const bool needs_comparison = img_desc->sample_type == SG_IMAGESAMPLETYPE_DEPTH; + if (needs_nonfiltering) { + _SG_VALIDATE(needs_nonfiltering && (smp_desc->sampler_type == SG_SAMPLERTYPE_NONFILTERING), VALIDATE_SHADERDESC_NONFILTERING_SAMPLER_REQUIRED); + } + if (needs_comparison) { + _SG_VALIDATE(needs_comparison && (smp_desc->sampler_type == SG_SAMPLERTYPE_COMPARISON), VALIDATE_SHADERDESC_COMPARISON_SAMPLER_REQUIRED); + } + } + } else { + _SG_VALIDATE(img_smp_desc->glsl_name == 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_NAME_BUT_NOT_USED); + _SG_VALIDATE(img_smp_desc->image_slot == 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_IMAGE_BUT_NOT_USED); + _SG_VALIDATE(img_smp_desc->sampler_slot == 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_SAMPLER_BUT_NOT_USED); + image_samplers_continuous = false; + } + } + // each image and sampler must be referenced by an image sampler + const uint32_t expected_img_slot_mask = (uint32_t)((1 << num_images) - 1); + const uint32_t expected_smp_slot_mask = (uint32_t)((1 << num_samplers) - 1); + uint32_t actual_img_slot_mask = 0; + uint32_t actual_smp_slot_mask = 0; + for (int img_smp_index = 0; img_smp_index < num_image_samplers; img_smp_index++) { + const sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_index]; + actual_img_slot_mask |= (1 << ((uint32_t)img_smp_desc->image_slot & 31)); + actual_smp_slot_mask |= (1 << ((uint32_t)img_smp_desc->sampler_slot & 31)); + } + _SG_VALIDATE(expected_img_slot_mask == actual_img_slot_mask, VALIDATE_SHADERDESC_IMAGE_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS); + _SG_VALIDATE(expected_smp_slot_mask == actual_smp_slot_mask, VALIDATE_SHADERDESC_SAMPLER_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS); + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_PIPELINEDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_PIPELINEDESC_CANARY); + _SG_VALIDATE(desc->shader.id != SG_INVALID_ID, VALIDATE_PIPELINEDESC_SHADER); + for (int buf_index = 0; buf_index < SG_MAX_VERTEX_BUFFERS; buf_index++) { + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[buf_index]; + if (l_state->stride == 0) { + continue; + } + _SG_VALIDATE(_sg_multiple_u64((uint64_t)l_state->stride, 4), VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); + } + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); + _SG_VALIDATE(0 != shd, VALIDATE_PIPELINEDESC_SHADER); + if (shd) { + _SG_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_PIPELINEDESC_SHADER); + bool attrs_cont = true; + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + attrs_cont = false; + continue; + } + _SG_VALIDATE(attrs_cont, VALIDATE_PIPELINEDESC_NO_CONT_ATTRS); + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + #if defined(SOKOL_D3D11) + // on D3D11, semantic names (and semantic indices) must be provided + _SG_VALIDATE(!_sg_strempty(&shd->d3d11.attrs[attr_index].sem_name), VALIDATE_PIPELINEDESC_ATTR_SEMANTICS); + #endif + } + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_attachments_desc(const sg_attachments_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_ATTACHMENTSDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_ATTACHMENTSDESC_CANARY); + bool atts_cont = true; + int color_width = -1, color_height = -1, color_sample_count = -1; + bool has_color_atts = false; + for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { + const sg_attachment_desc* att = &desc->colors[att_index]; + if (att->image.id == SG_INVALID_ID) { + atts_cont = false; + continue; + } + _SG_VALIDATE(atts_cont, VALIDATE_ATTACHMENTSDESC_NO_CONT_COLOR_ATTS); + has_color_atts = true; + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); + _SG_VALIDATE(img, VALIDATE_ATTACHMENTSDESC_IMAGE); + if (0 != img) { + _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ATTACHMENTSDESC_IMAGE); + _SG_VALIDATE(img->cmn.render_target, VALIDATE_ATTACHMENTSDESC_IMAGE_NO_RT); + _SG_VALIDATE(att->mip_level < img->cmn.num_mipmaps, VALIDATE_ATTACHMENTSDESC_MIPLEVEL); + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + _SG_VALIDATE(att->slice < 6, VALIDATE_ATTACHMENTSDESC_FACE); + } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_ATTACHMENTSDESC_LAYER); + } else if (img->cmn.type == SG_IMAGETYPE_3D) { + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_ATTACHMENTSDESC_SLICE); + } + if (att_index == 0) { + color_width = _sg_miplevel_dim(img->cmn.width, att->mip_level); + color_height = _sg_miplevel_dim(img->cmn.height, att->mip_level); + color_sample_count = img->cmn.sample_count; + } else { + _SG_VALIDATE(color_width == _sg_miplevel_dim(img->cmn.width, att->mip_level), VALIDATE_ATTACHMENTSDESC_IMAGE_SIZES); + _SG_VALIDATE(color_height == _sg_miplevel_dim(img->cmn.height, att->mip_level), VALIDATE_ATTACHMENTSDESC_IMAGE_SIZES); + _SG_VALIDATE(color_sample_count == img->cmn.sample_count, VALIDATE_ATTACHMENTSDESC_IMAGE_SAMPLE_COUNTS); + } + _SG_VALIDATE(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format), VALIDATE_ATTACHMENTSDESC_COLOR_INV_PIXELFORMAT); + + // check resolve attachment + const sg_attachment_desc* res_att = &desc->resolves[att_index]; + if (res_att->image.id != SG_INVALID_ID) { + // associated color attachment must be MSAA + _SG_VALIDATE(img->cmn.sample_count > 1, VALIDATE_ATTACHMENTSDESC_RESOLVE_COLOR_IMAGE_MSAA); + const _sg_image_t* res_img = _sg_lookup_image(&_sg.pools, res_att->image.id); + _SG_VALIDATE(res_img, VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE); + if (res_img != 0) { + _SG_VALIDATE(res_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE); + _SG_VALIDATE(res_img->cmn.render_target, VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_NO_RT); + _SG_VALIDATE(res_img->cmn.sample_count == 1, VALIDATE_ATTACHMENTSDESC_RESOLVE_SAMPLE_COUNT); + _SG_VALIDATE(res_att->mip_level < res_img->cmn.num_mipmaps, VALIDATE_ATTACHMENTSDESC_RESOLVE_MIPLEVEL); + if (res_img->cmn.type == SG_IMAGETYPE_CUBE) { + _SG_VALIDATE(res_att->slice < 6, VALIDATE_ATTACHMENTSDESC_RESOLVE_FACE); + } else if (res_img->cmn.type == SG_IMAGETYPE_ARRAY) { + _SG_VALIDATE(res_att->slice < res_img->cmn.num_slices, VALIDATE_ATTACHMENTSDESC_RESOLVE_LAYER); + } else if (res_img->cmn.type == SG_IMAGETYPE_3D) { + _SG_VALIDATE(res_att->slice < res_img->cmn.num_slices, VALIDATE_ATTACHMENTSDESC_RESOLVE_SLICE); + } + _SG_VALIDATE(img->cmn.pixel_format == res_img->cmn.pixel_format, VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_FORMAT); + _SG_VALIDATE(color_width == _sg_miplevel_dim(res_img->cmn.width, res_att->mip_level), VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_SIZES); + _SG_VALIDATE(color_height == _sg_miplevel_dim(res_img->cmn.height, res_att->mip_level), VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_SIZES); + } + } + } + } + bool has_depth_stencil_att = false; + if (desc->depth_stencil.image.id != SG_INVALID_ID) { + const sg_attachment_desc* att = &desc->depth_stencil; + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); + _SG_VALIDATE(img, VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE); + has_depth_stencil_att = true; + if (img) { + _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE); + _SG_VALIDATE(att->mip_level < img->cmn.num_mipmaps, VALIDATE_ATTACHMENTSDESC_DEPTH_MIPLEVEL); + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + _SG_VALIDATE(att->slice < 6, VALIDATE_ATTACHMENTSDESC_DEPTH_FACE); + } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_ATTACHMENTSDESC_DEPTH_LAYER); + } else if (img->cmn.type == SG_IMAGETYPE_3D) { + // NOTE: this can't actually happen because of VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_ATTACHMENTSDESC_DEPTH_SLICE); + } + _SG_VALIDATE(img->cmn.render_target, VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_NO_RT); + _SG_VALIDATE((color_width == -1) || (color_width == _sg_miplevel_dim(img->cmn.width, att->mip_level)), VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SIZES); + _SG_VALIDATE((color_height == -1) || (color_height == _sg_miplevel_dim(img->cmn.height, att->mip_level)), VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SIZES); + _SG_VALIDATE((color_sample_count == -1) || (color_sample_count == img->cmn.sample_count), VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SAMPLE_COUNT); + _SG_VALIDATE(_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format), VALIDATE_ATTACHMENTSDESC_DEPTH_INV_PIXELFORMAT); + } + } + _SG_VALIDATE(has_color_atts || has_depth_stencil_att, VALIDATE_ATTACHMENTSDESC_NO_ATTACHMENTS); + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_begin_pass(const sg_pass* pass) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(pass); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + _sg_validate_begin(); + _SG_VALIDATE(pass->_start_canary == 0, VALIDATE_BEGINPASS_CANARY); + _SG_VALIDATE(pass->_end_canary == 0, VALIDATE_BEGINPASS_CANARY); + if (pass->attachments.id == SG_INVALID_ID) { + // this is a swapchain pass + _SG_VALIDATE(pass->swapchain.width > 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH); + _SG_VALIDATE(pass->swapchain.height > 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT); + _SG_VALIDATE(pass->swapchain.sample_count > 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT); + _SG_VALIDATE(pass->swapchain.color_format > SG_PIXELFORMAT_NONE, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT); + // NOTE: depth buffer is optional, so depth_format is allowed to be invalid + // NOTE: the GL framebuffer handle may actually be 0 + #if defined(SOKOL_METAL) + _SG_VALIDATE(pass->swapchain.metal.current_drawable != 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE); + if (pass->swapchain.depth_format == SG_PIXELFORMAT_NONE) { + _SG_VALIDATE(pass->swapchain.metal.depth_stencil_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE_NOTSET); + } else { + _SG_VALIDATE(pass->swapchain.metal.depth_stencil_texture != 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE); + } + if (pass->swapchain.sample_count > 1) { + _SG_VALIDATE(pass->swapchain.metal.msaa_color_texture != 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE); + } else { + _SG_VALIDATE(pass->swapchain.metal.msaa_color_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE_NOTSET); + } + #elif defined(SOKOL_D3D11) + _SG_VALIDATE(pass->swapchain.d3d11.render_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW); + if (pass->swapchain.depth_format == SG_PIXELFORMAT_NONE) { + _SG_VALIDATE(pass->swapchain.d3d11.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW_NOTSET); + } else { + _SG_VALIDATE(pass->swapchain.d3d11.depth_stencil_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW); + } + if (pass->swapchain.sample_count > 1) { + _SG_VALIDATE(pass->swapchain.d3d11.resolve_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW); + } else { + _SG_VALIDATE(pass->swapchain.d3d11.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW_NOTSET); + } + #elif defined(SOKOL_WGPU) + _SG_VALIDATE(pass->swapchain.wgpu.render_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW); + if (pass->swapchain.depth_format == SG_PIXELFORMAT_NONE) { + _SG_VALIDATE(pass->swapchain.wgpu.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW_NOTSET); + } else { + _SG_VALIDATE(pass->swapchain.wgpu.depth_stencil_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW); + } + if (pass->swapchain.sample_count > 1) { + _SG_VALIDATE(pass->swapchain.wgpu.resolve_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW); + } else { + _SG_VALIDATE(pass->swapchain.wgpu.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW_NOTSET); + } + #endif + } else { + // this is an 'offscreen pass' + const _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, pass->attachments.id); + if (atts) { + _SG_VALIDATE(atts->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_ATTACHMENTS_VALID); + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + const _sg_attachment_common_t* color_att = &atts->cmn.colors[i]; + const _sg_image_t* color_img = _sg_attachments_color_image(atts, i); + if (color_img) { + _SG_VALIDATE(color_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_COLOR_ATTACHMENT_IMAGE); + _SG_VALIDATE(color_img->slot.id == color_att->image_id.id, VALIDATE_BEGINPASS_COLOR_ATTACHMENT_IMAGE); + } + const _sg_attachment_common_t* resolve_att = &atts->cmn.resolves[i]; + const _sg_image_t* resolve_img = _sg_attachments_resolve_image(atts, i); + if (resolve_img) { + _SG_VALIDATE(resolve_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_RESOLVE_ATTACHMENT_IMAGE); + _SG_VALIDATE(resolve_img->slot.id == resolve_att->image_id.id, VALIDATE_BEGINPASS_RESOLVE_ATTACHMENT_IMAGE); + } + } + const _sg_image_t* ds_img = _sg_attachments_ds_image(atts); + if (ds_img) { + const _sg_attachment_common_t* att = &atts->cmn.depth_stencil; + _SG_VALIDATE(ds_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_DEPTHSTENCIL_ATTACHMENT_IMAGE); + _SG_VALIDATE(ds_img->slot.id == att->image_id.id, VALIDATE_BEGINPASS_DEPTHSTENCIL_ATTACHMENT_IMAGE); + } + } else { + _SG_VALIDATE(atts != 0, VALIDATE_BEGINPASS_ATTACHMENTS_EXISTS); + } + // swapchain params must be all zero! + _SG_VALIDATE(pass->swapchain.width == 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH_NOTSET); + _SG_VALIDATE(pass->swapchain.height == 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT_NOTSET); + _SG_VALIDATE(pass->swapchain.sample_count == 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT_NOTSET); + _SG_VALIDATE(pass->swapchain.color_format == _SG_PIXELFORMAT_DEFAULT, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT_NOTSET); + _SG_VALIDATE(pass->swapchain.depth_format == _SG_PIXELFORMAT_DEFAULT, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_DEPTHFORMAT_NOTSET); + #if defined(SOKOL_METAL) + _SG_VALIDATE(pass->swapchain.metal.current_drawable == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE_NOTSET); + _SG_VALIDATE(pass->swapchain.metal.depth_stencil_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE_NOTSET); + _SG_VALIDATE(pass->swapchain.metal.msaa_color_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE_NOTSET); + #elif defined(SOKOL_D3D11) + _SG_VALIDATE(pass->swapchain.d3d11.render_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW_NOTSET); + _SG_VALIDATE(pass->swapchain.d3d11.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW_NOTSET); + _SG_VALIDATE(pass->swapchain.d3d11.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW_NOTSET); + #elif defined(SOKOL_WGPU) + _SG_VALIDATE(pass->swapchain.wgpu.render_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW_NOTSET); + _SG_VALIDATE(pass->swapchain.wgpu.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW_NOTSET); + _SG_VALIDATE(pass->swapchain.wgpu.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW_NOTSET); + #elif defined(_SOKOL_ANY_GL) + _SG_VALIDATE(pass->swapchain.gl.framebuffer == 0, VALIDATE_BEGINPASS_SWAPCHAIN_GL_EXPECT_FRAMEBUFFER_NOTSET); + #endif + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_pipeline(sg_pipeline pip_id) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(pip_id); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + _sg_validate_begin(); + // the pipeline object must be alive and valid + _SG_VALIDATE(pip_id.id != SG_INVALID_ID, VALIDATE_APIP_PIPELINE_VALID_ID); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + _SG_VALIDATE(pip != 0, VALIDATE_APIP_PIPELINE_EXISTS); + if (!pip) { + return _sg_validate_end(); + } + _SG_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_PIPELINE_VALID); + // the pipeline's shader must be alive and valid + SOKOL_ASSERT(pip->shader); + _SG_VALIDATE(pip->shader->slot.id == pip->cmn.shader_id.id, VALIDATE_APIP_SHADER_EXISTS); + _SG_VALIDATE(pip->shader->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_SHADER_VALID); + // check that pipeline attributes match current pass attributes + if (_sg.cur_pass.atts_id.id != SG_INVALID_ID) { + // an offscreen pass + const _sg_attachments_t* atts = _sg.cur_pass.atts; + SOKOL_ASSERT(atts); + _SG_VALIDATE(atts->slot.id == _sg.cur_pass.atts_id.id, VALIDATE_APIP_CURPASS_ATTACHMENTS_EXISTS); + _SG_VALIDATE(atts->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_CURPASS_ATTACHMENTS_VALID); + + _SG_VALIDATE(pip->cmn.color_count == atts->cmn.num_colors, VALIDATE_APIP_ATT_COUNT); + for (int i = 0; i < pip->cmn.color_count; i++) { + const _sg_image_t* att_img = _sg_attachments_color_image(atts, i); + _SG_VALIDATE(pip->cmn.colors[i].pixel_format == att_img->cmn.pixel_format, VALIDATE_APIP_COLOR_FORMAT); + _SG_VALIDATE(pip->cmn.sample_count == att_img->cmn.sample_count, VALIDATE_APIP_SAMPLE_COUNT); + } + const _sg_image_t* att_dsimg = _sg_attachments_ds_image(atts); + if (att_dsimg) { + _SG_VALIDATE(pip->cmn.depth.pixel_format == att_dsimg->cmn.pixel_format, VALIDATE_APIP_DEPTH_FORMAT); + } else { + _SG_VALIDATE(pip->cmn.depth.pixel_format == SG_PIXELFORMAT_NONE, VALIDATE_APIP_DEPTH_FORMAT); + } + } else { + // default pass + _SG_VALIDATE(pip->cmn.color_count == 1, VALIDATE_APIP_ATT_COUNT); + _SG_VALIDATE(pip->cmn.colors[0].pixel_format == _sg.cur_pass.swapchain.color_fmt, VALIDATE_APIP_COLOR_FORMAT); + _SG_VALIDATE(pip->cmn.depth.pixel_format == _sg.cur_pass.swapchain.depth_fmt, VALIDATE_APIP_DEPTH_FORMAT); + _SG_VALIDATE(pip->cmn.sample_count == _sg.cur_pass.swapchain.sample_count, VALIDATE_APIP_SAMPLE_COUNT); + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(bindings); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + _sg_validate_begin(); + + // a pipeline object must have been applied + _SG_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, VALIDATE_ABND_PIPELINE); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); + _SG_VALIDATE(pip != 0, VALIDATE_ABND_PIPELINE_EXISTS); + if (!pip) { + return _sg_validate_end(); + } + _SG_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ABND_PIPELINE_VALID); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + + // has expected vertex buffers, and vertex buffers still exist + for (int i = 0; i < SG_MAX_VERTEX_BUFFERS; i++) { + if (bindings->vertex_buffers[i].id != SG_INVALID_ID) { + _SG_VALIDATE(pip->cmn.vertex_buffer_layout_active[i], VALIDATE_ABND_VBS); + // buffers in vertex-buffer-slots must be of type SG_BUFFERTYPE_VERTEXBUFFER + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); + _SG_VALIDATE(buf != 0, VALIDATE_ABND_VB_EXISTS); + if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { + _SG_VALIDATE(SG_BUFFERTYPE_VERTEXBUFFER == buf->cmn.type, VALIDATE_ABND_VB_TYPE); + _SG_VALIDATE(!buf->cmn.append_overflow, VALIDATE_ABND_VB_OVERFLOW); + } + } else { + // vertex buffer provided in a slot which has no vertex layout in pipeline + _SG_VALIDATE(!pip->cmn.vertex_buffer_layout_active[i], VALIDATE_ABND_VBS); + } + } + + // index buffer expected or not, and index buffer still exists + if (pip->cmn.index_type == SG_INDEXTYPE_NONE) { + // pipeline defines non-indexed rendering, but index buffer provided + _SG_VALIDATE(bindings->index_buffer.id == SG_INVALID_ID, VALIDATE_ABND_IB); + } else { + // pipeline defines indexed rendering, but no index buffer provided + _SG_VALIDATE(bindings->index_buffer.id != SG_INVALID_ID, VALIDATE_ABND_NO_IB); + } + if (bindings->index_buffer.id != SG_INVALID_ID) { + // buffer in index-buffer-slot must be of type SG_BUFFERTYPE_INDEXBUFFER + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); + _SG_VALIDATE(buf != 0, VALIDATE_ABND_IB_EXISTS); + if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { + _SG_VALIDATE(SG_BUFFERTYPE_INDEXBUFFER == buf->cmn.type, VALIDATE_ABND_IB_TYPE); + _SG_VALIDATE(!buf->cmn.append_overflow, VALIDATE_ABND_IB_OVERFLOW); + } + } + + // has expected vertex shader images + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; + if (stage->images[i].image_type != _SG_IMAGETYPE_DEFAULT) { + _SG_VALIDATE(bindings->vs.images[i].id != SG_INVALID_ID, VALIDATE_ABND_VS_EXPECTED_IMAGE_BINDING); + if (bindings->vs.images[i].id != SG_INVALID_ID) { + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->vs.images[i].id); + _SG_VALIDATE(img != 0, VALIDATE_ABND_VS_IMG_EXISTS); + if (img && img->slot.state == SG_RESOURCESTATE_VALID) { + _SG_VALIDATE(img->cmn.type == stage->images[i].image_type, VALIDATE_ABND_VS_IMAGE_TYPE_MISMATCH); + _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_ABND_VS_IMAGE_MSAA); + const _sg_pixelformat_info_t* info = &_sg.formats[img->cmn.pixel_format]; + switch (stage->images[i].sample_type) { + case SG_IMAGESAMPLETYPE_FLOAT: + _SG_VALIDATE(info->filter, VALIDATE_ABND_VS_EXPECTED_FILTERABLE_IMAGE); + break; + case SG_IMAGESAMPLETYPE_DEPTH: + _SG_VALIDATE(info->depth, VALIDATE_ABND_VS_EXPECTED_DEPTH_IMAGE); + break; + default: + break; + } + } + } + } else { + _SG_VALIDATE(bindings->vs.images[i].id == SG_INVALID_ID, VALIDATE_ABND_VS_UNEXPECTED_IMAGE_BINDING); + } + } + + // has expected vertex shader image samplers + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; + if (stage->samplers[i].sampler_type != _SG_SAMPLERTYPE_DEFAULT) { + _SG_VALIDATE(bindings->vs.samplers[i].id != SG_INVALID_ID, VALIDATE_ABND_VS_EXPECTED_SAMPLER_BINDING); + if (bindings->vs.samplers[i].id != SG_INVALID_ID) { + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, bindings->vs.samplers[i].id); + _SG_VALIDATE(smp != 0, VALIDATE_ABND_VS_SMP_EXISTS); + if (smp) { + if (stage->samplers[i].sampler_type == SG_SAMPLERTYPE_COMPARISON) { + _SG_VALIDATE(smp->cmn.compare != SG_COMPAREFUNC_NEVER, VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_COMPARE_NEVER); + } else { + _SG_VALIDATE(smp->cmn.compare == SG_COMPAREFUNC_NEVER, VALIDATE_ABND_VS_EXPECTED_SAMPLER_COMPARE_NEVER); + } + if (stage->samplers[i].sampler_type == SG_SAMPLERTYPE_NONFILTERING) { + const bool nonfiltering = (smp->cmn.min_filter != SG_FILTER_LINEAR) + && (smp->cmn.mag_filter != SG_FILTER_LINEAR) + && (smp->cmn.mipmap_filter != SG_FILTER_LINEAR); + _SG_VALIDATE(nonfiltering, VALIDATE_ABND_VS_EXPECTED_NONFILTERING_SAMPLER); + } + } + } + } else { + _SG_VALIDATE(bindings->vs.samplers[i].id == SG_INVALID_ID, VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_BINDING); + } + } + + // has expected vertex shader storage buffers + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; + if (stage->storage_buffers[i].used) { + _SG_VALIDATE(bindings->vs.storage_buffers[i].id != SG_INVALID_ID, VALIDATE_ABND_VS_EXPECTED_STORAGEBUFFER_BINDING); + if (bindings->vs.storage_buffers[i].id != SG_INVALID_ID) { + const _sg_buffer_t* sbuf = _sg_lookup_buffer(&_sg.pools, bindings->vs.storage_buffers[i].id); + _SG_VALIDATE(sbuf != 0, VALIDATE_ABND_VS_STORAGEBUFFER_EXISTS); + if (sbuf) { + _SG_VALIDATE(sbuf->cmn.type == SG_BUFFERTYPE_STORAGEBUFFER, VALIDATE_ABND_VS_STORAGEBUFFER_BINDING_BUFFERTYPE); + } + } + } else { + _SG_VALIDATE(bindings->vs.storage_buffers[i].id == SG_INVALID_ID, VALIDATE_ABND_VS_UNEXPECTED_STORAGEBUFFER_BINDING); + } + } + + // has expected fragment shader images + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; + if (stage->images[i].image_type != _SG_IMAGETYPE_DEFAULT) { + _SG_VALIDATE(bindings->fs.images[i].id != SG_INVALID_ID, VALIDATE_ABND_FS_EXPECTED_IMAGE_BINDING); + if (bindings->fs.images[i].id != SG_INVALID_ID) { + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->fs.images[i].id); + _SG_VALIDATE(img != 0, VALIDATE_ABND_FS_IMG_EXISTS); + if (img && img->slot.state == SG_RESOURCESTATE_VALID) { + _SG_VALIDATE(img->cmn.type == stage->images[i].image_type, VALIDATE_ABND_FS_IMAGE_TYPE_MISMATCH); + _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_ABND_FS_IMAGE_MSAA); + const _sg_pixelformat_info_t* info = &_sg.formats[img->cmn.pixel_format]; + switch (stage->images[i].sample_type) { + case SG_IMAGESAMPLETYPE_FLOAT: + _SG_VALIDATE(info->filter, VALIDATE_ABND_FS_EXPECTED_FILTERABLE_IMAGE); + break; + case SG_IMAGESAMPLETYPE_DEPTH: + _SG_VALIDATE(info->depth, VALIDATE_ABND_FS_EXPECTED_DEPTH_IMAGE); + break; + default: + break; + } + } + } + } else { + _SG_VALIDATE(bindings->fs.images[i].id == SG_INVALID_ID, VALIDATE_ABND_FS_UNEXPECTED_IMAGE_BINDING); + } + } + + // has expected fragment shader samplers + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; + if (stage->samplers[i].sampler_type != _SG_SAMPLERTYPE_DEFAULT) { + _SG_VALIDATE(bindings->fs.samplers[i].id != SG_INVALID_ID, VALIDATE_ABND_FS_EXPECTED_SAMPLER_BINDING); + if (bindings->fs.samplers[i].id != SG_INVALID_ID) { + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, bindings->fs.samplers[i].id); + _SG_VALIDATE(smp != 0, VALIDATE_ABND_FS_SMP_EXISTS); + if (smp) { + if (stage->samplers[i].sampler_type == SG_SAMPLERTYPE_COMPARISON) { + _SG_VALIDATE(smp->cmn.compare != SG_COMPAREFUNC_NEVER, VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_COMPARE_NEVER); + } else { + _SG_VALIDATE(smp->cmn.compare == SG_COMPAREFUNC_NEVER, VALIDATE_ABND_FS_EXPECTED_SAMPLER_COMPARE_NEVER); + } + if (stage->samplers[i].sampler_type == SG_SAMPLERTYPE_NONFILTERING) { + const bool nonfiltering = (smp->cmn.min_filter != SG_FILTER_LINEAR) + && (smp->cmn.mag_filter != SG_FILTER_LINEAR) + && (smp->cmn.mipmap_filter != SG_FILTER_LINEAR); + _SG_VALIDATE(nonfiltering, VALIDATE_ABND_FS_EXPECTED_NONFILTERING_SAMPLER); + } + } + } + } else { + _SG_VALIDATE(bindings->fs.samplers[i].id == SG_INVALID_ID, VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_BINDING); + } + } + + // has expected fragment shader storage buffers + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; + if (stage->storage_buffers[i].used) { + _SG_VALIDATE(bindings->fs.storage_buffers[i].id != SG_INVALID_ID, VALIDATE_ABND_FS_EXPECTED_STORAGEBUFFER_BINDING); + if (bindings->fs.storage_buffers[i].id != SG_INVALID_ID) { + const _sg_buffer_t* sbuf = _sg_lookup_buffer(&_sg.pools, bindings->fs.storage_buffers[i].id); + _SG_VALIDATE(sbuf != 0, VALIDATE_ABND_FS_STORAGEBUFFER_EXISTS); + if (sbuf) { + _SG_VALIDATE(sbuf->cmn.type == SG_BUFFERTYPE_STORAGEBUFFER, VALIDATE_ABND_FS_STORAGEBUFFER_BINDING_BUFFERTYPE); + } + } + } else { + _SG_VALIDATE(bindings->fs.storage_buffers[i].id == SG_INVALID_ID, VALIDATE_ABND_FS_UNEXPECTED_STORAGEBUFFER_BINDING); + } + } + + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(stage_index); + _SOKOL_UNUSED(ub_index); + _SOKOL_UNUSED(data); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT((stage_index == SG_SHADERSTAGE_VS) || (stage_index == SG_SHADERSTAGE_FS)); + SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); + _sg_validate_begin(); + _SG_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, VALIDATE_AUB_NO_PIPELINE); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); + SOKOL_ASSERT(pip && (pip->slot.id == _sg.cur_pipeline.id)); + SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->cmn.shader_id.id)); + + // check that there is a uniform block at 'stage' and 'ub_index' + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[stage_index]; + _SG_VALIDATE(ub_index < stage->num_uniform_blocks, VALIDATE_AUB_NO_UB_AT_SLOT); + + // check that the provided data size matches the uniform block size + _SG_VALIDATE(data->size == stage->uniform_blocks[ub_index].size, VALIDATE_AUB_SIZE); + + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_update_buffer(const _sg_buffer_t* buf, const sg_range* data) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(buf); + _SOKOL_UNUSED(data); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(buf && data && data->ptr); + _sg_validate_begin(); + _SG_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, VALIDATE_UPDATEBUF_USAGE); + _SG_VALIDATE(buf->cmn.size >= (int)data->size, VALIDATE_UPDATEBUF_SIZE); + _SG_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, VALIDATE_UPDATEBUF_ONCE); + _SG_VALIDATE(buf->cmn.append_frame_index != _sg.frame_index, VALIDATE_UPDATEBUF_APPEND); + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_append_buffer(const _sg_buffer_t* buf, const sg_range* data) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(buf); + _SOKOL_UNUSED(data); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(buf && data && data->ptr); + _sg_validate_begin(); + _SG_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, VALIDATE_APPENDBUF_USAGE); + _SG_VALIDATE(buf->cmn.size >= (buf->cmn.append_pos + (int)data->size), VALIDATE_APPENDBUF_SIZE); + _SG_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, VALIDATE_APPENDBUF_UPDATE); + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_update_image(const _sg_image_t* img, const sg_image_data* data) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(img); + _SOKOL_UNUSED(data); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(img && data); + _sg_validate_begin(); + _SG_VALIDATE(img->cmn.usage != SG_USAGE_IMMUTABLE, VALIDATE_UPDIMG_USAGE); + _SG_VALIDATE(img->cmn.upd_frame_index != _sg.frame_index, VALIDATE_UPDIMG_ONCE); + _sg_validate_image_data(data, + img->cmn.pixel_format, + img->cmn.width, + img->cmn.height, + (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6 : 1, + img->cmn.num_mipmaps, + img->cmn.num_slices); + return _sg_validate_end(); + #endif +} + +// ██████ ███████ ███████ ██████ ██ ██ ██████ ██████ ███████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ █████ ███████ ██ ██ ██ ██ ██████ ██ █████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██████ ██████ ██ ██ ██████ ███████ ███████ +// +// >>resources +_SOKOL_PRIVATE sg_buffer_desc _sg_buffer_desc_defaults(const sg_buffer_desc* desc) { + sg_buffer_desc def = *desc; + def.type = _sg_def(def.type, SG_BUFFERTYPE_VERTEXBUFFER); + def.usage = _sg_def(def.usage, SG_USAGE_IMMUTABLE); + if (def.size == 0) { + def.size = def.data.size; + } else if (def.data.size == 0) { + def.data.size = def.size; + } + return def; +} + +_SOKOL_PRIVATE sg_image_desc _sg_image_desc_defaults(const sg_image_desc* desc) { + sg_image_desc def = *desc; + def.type = _sg_def(def.type, SG_IMAGETYPE_2D); + def.num_slices = _sg_def(def.num_slices, 1); + def.num_mipmaps = _sg_def(def.num_mipmaps, 1); + def.usage = _sg_def(def.usage, SG_USAGE_IMMUTABLE); + if (desc->render_target) { + def.pixel_format = _sg_def(def.pixel_format, _sg.desc.environment.defaults.color_format); + def.sample_count = _sg_def(def.sample_count, _sg.desc.environment.defaults.sample_count); + } else { + def.pixel_format = _sg_def(def.pixel_format, SG_PIXELFORMAT_RGBA8); + def.sample_count = _sg_def(def.sample_count, 1); + } + return def; +} + +_SOKOL_PRIVATE sg_sampler_desc _sg_sampler_desc_defaults(const sg_sampler_desc* desc) { + sg_sampler_desc def = *desc; + def.min_filter = _sg_def(def.min_filter, SG_FILTER_NEAREST); + def.mag_filter = _sg_def(def.mag_filter, SG_FILTER_NEAREST); + def.mipmap_filter = _sg_def(def.mipmap_filter, SG_FILTER_NONE); + def.wrap_u = _sg_def(def.wrap_u, SG_WRAP_REPEAT); + def.wrap_v = _sg_def(def.wrap_v, SG_WRAP_REPEAT); + def.wrap_w = _sg_def(def.wrap_w, SG_WRAP_REPEAT); + def.max_lod = _sg_def_flt(def.max_lod, FLT_MAX); + def.border_color = _sg_def(def.border_color, SG_BORDERCOLOR_OPAQUE_BLACK); + def.compare = _sg_def(def.compare, SG_COMPAREFUNC_NEVER); + def.max_anisotropy = _sg_def(def.max_anisotropy, 1); + return def; +} + +_SOKOL_PRIVATE sg_shader_desc _sg_shader_desc_defaults(const sg_shader_desc* desc) { + sg_shader_desc def = *desc; + #if defined(SOKOL_METAL) + def.vs.entry = _sg_def(def.vs.entry, "_main"); + def.fs.entry = _sg_def(def.fs.entry, "_main"); + #else + def.vs.entry = _sg_def(def.vs.entry, "main"); + def.fs.entry = _sg_def(def.fs.entry, "main"); + #endif + #if defined(SOKOL_D3D11) + if (def.vs.source) { + def.vs.d3d11_target = _sg_def(def.vs.d3d11_target, "vs_4_0"); + } + if (def.fs.source) { + def.fs.d3d11_target = _sg_def(def.fs.d3d11_target, "ps_4_0"); + } + #endif + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &def.vs : &def.fs; + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + if (0 == ub_desc->size) { + break; + } + ub_desc->layout = _sg_def(ub_desc->layout, SG_UNIFORMLAYOUT_NATIVE); + for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { + sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; + if (u_desc->type == SG_UNIFORMTYPE_INVALID) { + break; + } + u_desc->array_count = _sg_def(u_desc->array_count, 1); + } + } + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (!img_desc->used) { + break; + } + img_desc->image_type = _sg_def(img_desc->image_type, SG_IMAGETYPE_2D); + img_desc->sample_type = _sg_def(img_desc->sample_type, SG_IMAGESAMPLETYPE_FLOAT); + } + for (int smp_index = 0; smp_index < SG_MAX_SHADERSTAGE_SAMPLERS; smp_index++) { + sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_index]; + if (!smp_desc->used) { + break; + } + smp_desc->sampler_type = _sg_def(smp_desc->sampler_type, SG_SAMPLERTYPE_FILTERING); + } + } + return def; +} + +_SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_desc* desc) { + sg_pipeline_desc def = *desc; + + def.primitive_type = _sg_def(def.primitive_type, SG_PRIMITIVETYPE_TRIANGLES); + def.index_type = _sg_def(def.index_type, SG_INDEXTYPE_NONE); + def.cull_mode = _sg_def(def.cull_mode, SG_CULLMODE_NONE); + def.face_winding = _sg_def(def.face_winding, SG_FACEWINDING_CW); + def.sample_count = _sg_def(def.sample_count, _sg.desc.environment.defaults.sample_count); + + def.stencil.front.compare = _sg_def(def.stencil.front.compare, SG_COMPAREFUNC_ALWAYS); + def.stencil.front.fail_op = _sg_def(def.stencil.front.fail_op, SG_STENCILOP_KEEP); + def.stencil.front.depth_fail_op = _sg_def(def.stencil.front.depth_fail_op, SG_STENCILOP_KEEP); + def.stencil.front.pass_op = _sg_def(def.stencil.front.pass_op, SG_STENCILOP_KEEP); + def.stencil.back.compare = _sg_def(def.stencil.back.compare, SG_COMPAREFUNC_ALWAYS); + def.stencil.back.fail_op = _sg_def(def.stencil.back.fail_op, SG_STENCILOP_KEEP); + def.stencil.back.depth_fail_op = _sg_def(def.stencil.back.depth_fail_op, SG_STENCILOP_KEEP); + def.stencil.back.pass_op = _sg_def(def.stencil.back.pass_op, SG_STENCILOP_KEEP); + + def.depth.compare = _sg_def(def.depth.compare, SG_COMPAREFUNC_ALWAYS); + def.depth.pixel_format = _sg_def(def.depth.pixel_format, _sg.desc.environment.defaults.depth_format); + if (def.colors[0].pixel_format == SG_PIXELFORMAT_NONE) { + // special case depth-only rendering, enforce a color count of 0 + def.color_count = 0; + } else { + def.color_count = _sg_def(def.color_count, 1); + } + if (def.color_count > SG_MAX_COLOR_ATTACHMENTS) { + def.color_count = SG_MAX_COLOR_ATTACHMENTS; + } + for (int i = 0; i < def.color_count; i++) { + sg_color_target_state* cs = &def.colors[i]; + cs->pixel_format = _sg_def(cs->pixel_format, _sg.desc.environment.defaults.color_format); + cs->write_mask = _sg_def(cs->write_mask, SG_COLORMASK_RGBA); + sg_blend_state* bs = &def.colors[i].blend; + bs->src_factor_rgb = _sg_def(bs->src_factor_rgb, SG_BLENDFACTOR_ONE); + bs->dst_factor_rgb = _sg_def(bs->dst_factor_rgb, SG_BLENDFACTOR_ZERO); + bs->op_rgb = _sg_def(bs->op_rgb, SG_BLENDOP_ADD); + bs->src_factor_alpha = _sg_def(bs->src_factor_alpha, SG_BLENDFACTOR_ONE); + bs->dst_factor_alpha = _sg_def(bs->dst_factor_alpha, SG_BLENDFACTOR_ZERO); + bs->op_alpha = _sg_def(bs->op_alpha, SG_BLENDOP_ADD); + } + + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + sg_vertex_attr_state* a_state = &def.layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + sg_vertex_buffer_layout_state* l_state = &def.layout.buffers[a_state->buffer_index]; + l_state->step_func = _sg_def(l_state->step_func, SG_VERTEXSTEP_PER_VERTEX); + l_state->step_rate = _sg_def(l_state->step_rate, 1); + } + + // resolve vertex layout strides and offsets + int auto_offset[SG_MAX_VERTEX_BUFFERS]; + _sg_clear(auto_offset, sizeof(auto_offset)); + bool use_auto_offset = true; + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + // to use computed offsets, *all* attr offsets must be 0 + if (def.layout.attrs[attr_index].offset != 0) { + use_auto_offset = false; + } + } + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + sg_vertex_attr_state* a_state = &def.layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + if (use_auto_offset) { + a_state->offset = auto_offset[a_state->buffer_index]; + } + auto_offset[a_state->buffer_index] += _sg_vertexformat_bytesize(a_state->format); + } + // compute vertex strides if needed + for (int buf_index = 0; buf_index < SG_MAX_VERTEX_BUFFERS; buf_index++) { + sg_vertex_buffer_layout_state* l_state = &def.layout.buffers[buf_index]; + if (l_state->stride == 0) { + l_state->stride = auto_offset[buf_index]; + } + } + + return def; +} + +_SOKOL_PRIVATE sg_attachments_desc _sg_attachments_desc_defaults(const sg_attachments_desc* desc) { + sg_attachments_desc def = *desc; + return def; +} + +_SOKOL_PRIVATE sg_buffer _sg_alloc_buffer(void) { + sg_buffer res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.buffer_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.buffer_pool, &_sg.pools.buffers[slot_index].slot, slot_index); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(BUFFER_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE sg_image _sg_alloc_image(void) { + sg_image res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.image_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.image_pool, &_sg.pools.images[slot_index].slot, slot_index); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(IMAGE_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE sg_sampler _sg_alloc_sampler(void) { + sg_sampler res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.sampler_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.sampler_pool, &_sg.pools.samplers[slot_index].slot, slot_index); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(SAMPLER_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE sg_shader _sg_alloc_shader(void) { + sg_shader res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.shader_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.shader_pool, &_sg.pools.shaders[slot_index].slot, slot_index); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(SHADER_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE sg_pipeline _sg_alloc_pipeline(void) { + sg_pipeline res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.pipeline_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id =_sg_slot_alloc(&_sg.pools.pipeline_pool, &_sg.pools.pipelines[slot_index].slot, slot_index); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(PIPELINE_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE sg_attachments _sg_alloc_attachments(void) { + sg_attachments res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.attachments_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.attachments_pool, &_sg.pools.attachments[slot_index].slot, slot_index); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(PASS_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE void _sg_dealloc_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf && (buf->slot.state == SG_RESOURCESTATE_ALLOC) && (buf->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.buffer_pool, _sg_slot_index(buf->slot.id)); + _sg_reset_slot(&buf->slot); +} + +_SOKOL_PRIVATE void _sg_dealloc_image(_sg_image_t* img) { + SOKOL_ASSERT(img && (img->slot.state == SG_RESOURCESTATE_ALLOC) && (img->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.image_pool, _sg_slot_index(img->slot.id)); + _sg_reset_slot(&img->slot); +} + +_SOKOL_PRIVATE void _sg_dealloc_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp && (smp->slot.state == SG_RESOURCESTATE_ALLOC) && (smp->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.sampler_pool, _sg_slot_index(smp->slot.id)); + _sg_reset_slot(&smp->slot); +} + +_SOKOL_PRIVATE void _sg_dealloc_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd && (shd->slot.state == SG_RESOURCESTATE_ALLOC) && (shd->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.shader_pool, _sg_slot_index(shd->slot.id)); + _sg_reset_slot(&shd->slot); +} + +_SOKOL_PRIVATE void _sg_dealloc_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip && (pip->slot.state == SG_RESOURCESTATE_ALLOC) && (pip->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.pipeline_pool, _sg_slot_index(pip->slot.id)); + _sg_reset_slot(&pip->slot); +} + +_SOKOL_PRIVATE void _sg_dealloc_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts && (atts->slot.state == SG_RESOURCESTATE_ALLOC) && (atts->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.attachments_pool, _sg_slot_index(atts->slot.id)); + _sg_reset_slot(&atts->slot); +} + +_SOKOL_PRIVATE void _sg_init_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && (buf->slot.state == SG_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + if (_sg_validate_buffer_desc(desc)) { + _sg_buffer_common_init(&buf->cmn, desc); + buf->slot.state = _sg_create_buffer(buf, desc); + } else { + buf->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID)||(buf->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && (img->slot.state == SG_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + if (_sg_validate_image_desc(desc)) { + _sg_image_common_init(&img->cmn, desc); + img->slot.state = _sg_create_image(img, desc); + } else { + img->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID)||(img->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && (smp->slot.state == SG_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + if (_sg_validate_sampler_desc(desc)) { + _sg_sampler_common_init(&smp->cmn, desc); + smp->slot.state = _sg_create_sampler(smp, desc); + } else { + smp->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((smp->slot.state == SG_RESOURCESTATE_VALID)||(smp->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && (shd->slot.state == SG_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + if (_sg_validate_shader_desc(desc)) { + _sg_shader_common_init(&shd->cmn, desc); + shd->slot.state = _sg_create_shader(shd, desc); + } else { + shd->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID)||(shd->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && (pip->slot.state == SG_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + if (_sg_validate_pipeline_desc(desc)) { + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); + if (shd && (shd->slot.state == SG_RESOURCESTATE_VALID)) { + _sg_pipeline_common_init(&pip->cmn, desc); + pip->slot.state = _sg_create_pipeline(pip, shd, desc); + } else { + pip->slot.state = SG_RESOURCESTATE_FAILED; + } + } else { + pip->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID)||(pip->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_attachments(_sg_attachments_t* atts, const sg_attachments_desc* desc) { + SOKOL_ASSERT(atts && atts->slot.state == SG_RESOURCESTATE_ALLOC); + SOKOL_ASSERT(desc); + if (_sg_validate_attachments_desc(desc)) { + // lookup pass attachment image pointers + _sg_image_t* color_images[SG_MAX_COLOR_ATTACHMENTS] = { 0 }; + _sg_image_t* resolve_images[SG_MAX_COLOR_ATTACHMENTS] = { 0 }; + _sg_image_t* ds_image = 0; + // NOTE: validation already checked that all surfaces are same width/height + int width = 0; + int height = 0; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (desc->colors[i].image.id) { + color_images[i] = _sg_lookup_image(&_sg.pools, desc->colors[i].image.id); + if (!(color_images[i] && color_images[i]->slot.state == SG_RESOURCESTATE_VALID)) { + atts->slot.state = SG_RESOURCESTATE_FAILED; + return; + } + const int mip_level = desc->colors[i].mip_level; + width = _sg_miplevel_dim(color_images[i]->cmn.width, mip_level); + height = _sg_miplevel_dim(color_images[i]->cmn.height, mip_level); + } + if (desc->resolves[i].image.id) { + resolve_images[i] = _sg_lookup_image(&_sg.pools, desc->resolves[i].image.id); + if (!(resolve_images[i] && resolve_images[i]->slot.state == SG_RESOURCESTATE_VALID)) { + atts->slot.state = SG_RESOURCESTATE_FAILED; + return; + } + } + } + if (desc->depth_stencil.image.id) { + ds_image = _sg_lookup_image(&_sg.pools, desc->depth_stencil.image.id); + if (!(ds_image && ds_image->slot.state == SG_RESOURCESTATE_VALID)) { + atts->slot.state = SG_RESOURCESTATE_FAILED; + return; + } + const int mip_level = desc->depth_stencil.mip_level; + width = _sg_miplevel_dim(ds_image->cmn.width, mip_level); + height = _sg_miplevel_dim(ds_image->cmn.height, mip_level); + } + _sg_attachments_common_init(&atts->cmn, desc, width, height); + atts->slot.state = _sg_create_attachments(atts, color_images, resolve_images, ds_image, desc); + } else { + atts->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((atts->slot.state == SG_RESOURCESTATE_VALID)||(atts->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_uninit_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf && ((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED))); + _sg_discard_buffer(buf); + _sg_reset_buffer_to_alloc_state(buf); +} + +_SOKOL_PRIVATE void _sg_uninit_image(_sg_image_t* img) { + SOKOL_ASSERT(img && ((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED))); + _sg_discard_image(img); + _sg_reset_image_to_alloc_state(img); +} + +_SOKOL_PRIVATE void _sg_uninit_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp && ((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED))); + _sg_discard_sampler(smp); + _sg_reset_sampler_to_alloc_state(smp); +} + +_SOKOL_PRIVATE void _sg_uninit_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd && ((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED))); + _sg_discard_shader(shd); + _sg_reset_shader_to_alloc_state(shd); +} + +_SOKOL_PRIVATE void _sg_uninit_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip && ((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED))); + _sg_discard_pipeline(pip); + _sg_reset_pipeline_to_alloc_state(pip); +} + +_SOKOL_PRIVATE void _sg_uninit_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts && ((atts->slot.state == SG_RESOURCESTATE_VALID) || (atts->slot.state == SG_RESOURCESTATE_FAILED))); + _sg_discard_attachments(atts); + _sg_reset_attachments_to_alloc_state(atts); +} + +_SOKOL_PRIVATE void _sg_setup_commit_listeners(const sg_desc* desc) { + SOKOL_ASSERT(desc->max_commit_listeners > 0); + SOKOL_ASSERT(0 == _sg.commit_listeners.items); + SOKOL_ASSERT(0 == _sg.commit_listeners.num); + SOKOL_ASSERT(0 == _sg.commit_listeners.upper); + _sg.commit_listeners.num = desc->max_commit_listeners; + const size_t size = (size_t)_sg.commit_listeners.num * sizeof(sg_commit_listener); + _sg.commit_listeners.items = (sg_commit_listener*)_sg_malloc_clear(size); +} + +_SOKOL_PRIVATE void _sg_discard_commit_listeners(void) { + SOKOL_ASSERT(0 != _sg.commit_listeners.items); + _sg_free(_sg.commit_listeners.items); + _sg.commit_listeners.items = 0; +} + +_SOKOL_PRIVATE void _sg_notify_commit_listeners(void) { + SOKOL_ASSERT(_sg.commit_listeners.items); + for (int i = 0; i < _sg.commit_listeners.upper; i++) { + const sg_commit_listener* listener = &_sg.commit_listeners.items[i]; + if (listener->func) { + listener->func(listener->user_data); + } + } +} + +_SOKOL_PRIVATE bool _sg_add_commit_listener(const sg_commit_listener* new_listener) { + SOKOL_ASSERT(new_listener && new_listener->func); + SOKOL_ASSERT(_sg.commit_listeners.items); + // first check if the listener hadn't been added already + for (int i = 0; i < _sg.commit_listeners.upper; i++) { + const sg_commit_listener* slot = &_sg.commit_listeners.items[i]; + if ((slot->func == new_listener->func) && (slot->user_data == new_listener->user_data)) { + _SG_ERROR(IDENTICAL_COMMIT_LISTENER); + return false; + } + } + // first try to plug a hole + sg_commit_listener* slot = 0; + for (int i = 0; i < _sg.commit_listeners.upper; i++) { + if (_sg.commit_listeners.items[i].func == 0) { + slot = &_sg.commit_listeners.items[i]; + break; + } + } + if (!slot) { + // append to end + if (_sg.commit_listeners.upper < _sg.commit_listeners.num) { + slot = &_sg.commit_listeners.items[_sg.commit_listeners.upper++]; + } + } + if (!slot) { + _SG_ERROR(COMMIT_LISTENER_ARRAY_FULL); + return false; + } + *slot = *new_listener; + return true; +} + +_SOKOL_PRIVATE bool _sg_remove_commit_listener(const sg_commit_listener* listener) { + SOKOL_ASSERT(listener && listener->func); + SOKOL_ASSERT(_sg.commit_listeners.items); + for (int i = 0; i < _sg.commit_listeners.upper; i++) { + sg_commit_listener* slot = &_sg.commit_listeners.items[i]; + // both the function pointer and user data must match! + if ((slot->func == listener->func) && (slot->user_data == listener->user_data)) { + slot->func = 0; + slot->user_data = 0; + // NOTE: since _sg_add_commit_listener() already catches duplicates, + // we don't need to worry about them here + return true; + } + } + return false; +} + +_SOKOL_PRIVATE sg_desc _sg_desc_defaults(const sg_desc* desc) { + /* + NOTE: on WebGPU, the default color pixel format MUST be provided, + cannot be a default compile-time constant. + */ + sg_desc res = *desc; + #if defined(SOKOL_WGPU) + SOKOL_ASSERT(SG_PIXELFORMAT_NONE < res.environment.defaults.color_format); + #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) + res.environment.defaults.color_format = _sg_def(res.environment.defaults.color_format, SG_PIXELFORMAT_BGRA8); + #else + res.environment.defaults.color_format = _sg_def(res.environment.defaults.color_format, SG_PIXELFORMAT_RGBA8); + #endif + res.environment.defaults.depth_format = _sg_def(res.environment.defaults.depth_format, SG_PIXELFORMAT_DEPTH_STENCIL); + res.environment.defaults.sample_count = _sg_def(res.environment.defaults.sample_count, 1); + res.buffer_pool_size = _sg_def(res.buffer_pool_size, _SG_DEFAULT_BUFFER_POOL_SIZE); + res.image_pool_size = _sg_def(res.image_pool_size, _SG_DEFAULT_IMAGE_POOL_SIZE); + res.sampler_pool_size = _sg_def(res.sampler_pool_size, _SG_DEFAULT_SAMPLER_POOL_SIZE); + res.shader_pool_size = _sg_def(res.shader_pool_size, _SG_DEFAULT_SHADER_POOL_SIZE); + res.pipeline_pool_size = _sg_def(res.pipeline_pool_size, _SG_DEFAULT_PIPELINE_POOL_SIZE); + res.attachments_pool_size = _sg_def(res.attachments_pool_size, _SG_DEFAULT_ATTACHMENTS_POOL_SIZE); + res.uniform_buffer_size = _sg_def(res.uniform_buffer_size, _SG_DEFAULT_UB_SIZE); + res.max_commit_listeners = _sg_def(res.max_commit_listeners, _SG_DEFAULT_MAX_COMMIT_LISTENERS); + res.wgpu_bindgroups_cache_size = _sg_def(res.wgpu_bindgroups_cache_size, _SG_DEFAULT_WGPU_BINDGROUP_CACHE_SIZE); + return res; +} + +_SOKOL_PRIVATE sg_pass _sg_pass_defaults(const sg_pass* pass) { + sg_pass res = *pass; + if (res.attachments.id == SG_INVALID_ID) { + // this is a swapchain-pass + res.swapchain.sample_count = _sg_def(res.swapchain.sample_count, _sg.desc.environment.defaults.sample_count); + res.swapchain.color_format = _sg_def(res.swapchain.color_format, _sg.desc.environment.defaults.color_format); + res.swapchain.depth_format = _sg_def(res.swapchain.depth_format, _sg.desc.environment.defaults.depth_format); + } + res.action = _sg_pass_action_defaults(&res.action); + return res; +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void sg_setup(const sg_desc* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0)); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + _SG_CLEAR_ARC_STRUCT(_sg_state_t, _sg); + _sg.desc = _sg_desc_defaults(desc); + _sg_setup_pools(&_sg.pools, &_sg.desc); + _sg_setup_commit_listeners(&_sg.desc); + _sg.frame_index = 1; + _sg.stats_enabled = true; + _sg_setup_backend(&_sg.desc); + _sg.valid = true; +} + +SOKOL_API_IMPL void sg_shutdown(void) { + _sg_discard_all_resources(&_sg.pools); + _sg_discard_backend(); + _sg_discard_commit_listeners(); + _sg_discard_pools(&_sg.pools); + _SG_CLEAR_ARC_STRUCT(_sg_state_t, _sg); +} + +SOKOL_API_IMPL bool sg_isvalid(void) { + return _sg.valid; +} + +SOKOL_API_IMPL sg_desc sg_query_desc(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.desc; +} + +SOKOL_API_IMPL sg_backend sg_query_backend(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.backend; +} + +SOKOL_API_IMPL sg_features sg_query_features(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.features; +} + +SOKOL_API_IMPL sg_limits sg_query_limits(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.limits; +} + +SOKOL_API_IMPL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt) { + SOKOL_ASSERT(_sg.valid); + int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index > SG_PIXELFORMAT_NONE) && (fmt_index < _SG_PIXELFORMAT_NUM)); + const _sg_pixelformat_info_t* src = &_sg.formats[fmt_index]; + sg_pixelformat_info res; + _sg_clear(&res, sizeof(res)); + res.sample = src->sample; + res.filter = src->filter; + res.render = src->render; + res.blend = src->blend; + res.msaa = src->msaa; + res.depth = src->depth; + res.compressed = _sg_is_compressed_pixel_format(fmt); + if (!res.compressed) { + res.bytes_per_pixel = _sg_pixelformat_bytesize(fmt); + } + return res; +} + +SOKOL_API_IMPL int sg_query_row_pitch(sg_pixel_format fmt, int width, int row_align_bytes) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(width > 0); + SOKOL_ASSERT((row_align_bytes > 0) && _sg_ispow2(row_align_bytes)); + SOKOL_ASSERT(((int)fmt > SG_PIXELFORMAT_NONE) && ((int)fmt < _SG_PIXELFORMAT_NUM)); + return _sg_row_pitch(fmt, width, row_align_bytes); +} + +SOKOL_API_IMPL int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT((width > 0) && (height > 0)); + SOKOL_ASSERT((row_align_bytes > 0) && _sg_ispow2(row_align_bytes)); + SOKOL_ASSERT(((int)fmt > SG_PIXELFORMAT_NONE) && ((int)fmt < _SG_PIXELFORMAT_NUM)); + return _sg_surface_pitch(fmt, width, height, row_align_bytes); +} + +SOKOL_API_IMPL sg_frame_stats sg_query_frame_stats(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.prev_stats; +} + +SOKOL_API_IMPL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(trace_hooks); + _SOKOL_UNUSED(trace_hooks); + #if defined(SOKOL_TRACE_HOOKS) + sg_trace_hooks old_hooks = _sg.hooks; + _sg.hooks = *trace_hooks; + #else + static sg_trace_hooks old_hooks; + _SG_WARN(TRACE_HOOKS_NOT_ENABLED); + #endif + return old_hooks; +} + +SOKOL_API_IMPL sg_buffer sg_alloc_buffer(void) { + SOKOL_ASSERT(_sg.valid); + sg_buffer res = _sg_alloc_buffer(); + _SG_TRACE_ARGS(alloc_buffer, res); + return res; +} + +SOKOL_API_IMPL sg_image sg_alloc_image(void) { + SOKOL_ASSERT(_sg.valid); + sg_image res = _sg_alloc_image(); + _SG_TRACE_ARGS(alloc_image, res); + return res; +} + +SOKOL_API_IMPL sg_sampler sg_alloc_sampler(void) { + SOKOL_ASSERT(_sg.valid); + sg_sampler res = _sg_alloc_sampler(); + _SG_TRACE_ARGS(alloc_sampler, res); + return res; +} + +SOKOL_API_IMPL sg_shader sg_alloc_shader(void) { + SOKOL_ASSERT(_sg.valid); + sg_shader res = _sg_alloc_shader(); + _SG_TRACE_ARGS(alloc_shader, res); + return res; +} + +SOKOL_API_IMPL sg_pipeline sg_alloc_pipeline(void) { + SOKOL_ASSERT(_sg.valid); + sg_pipeline res = _sg_alloc_pipeline(); + _SG_TRACE_ARGS(alloc_pipeline, res); + return res; +} + +SOKOL_API_IMPL sg_attachments sg_alloc_attachments(void) { + SOKOL_ASSERT(_sg.valid); + sg_attachments res = _sg_alloc_attachments(); + _SG_TRACE_ARGS(alloc_attachments, res); + return res; +} + +SOKOL_API_IMPL void sg_dealloc_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_buffer(buf); + } else { + _SG_ERROR(DEALLOC_BUFFER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(dealloc_buffer, buf_id); +} + +SOKOL_API_IMPL void sg_dealloc_image(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + if (img->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_image(img); + } else { + _SG_ERROR(DEALLOC_IMAGE_INVALID_STATE); + } + } + _SG_TRACE_ARGS(dealloc_image, img_id); +} + +SOKOL_API_IMPL void sg_dealloc_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_sampler(smp); + } else { + _SG_ERROR(DEALLOC_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(dealloc_sampler, smp_id); +} + +SOKOL_API_IMPL void sg_dealloc_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_shader(shd); + } else { + _SG_ERROR(DEALLOC_SHADER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(dealloc_shader, shd_id); +} + +SOKOL_API_IMPL void sg_dealloc_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_pipeline(pip); + } else { + _SG_ERROR(DEALLOC_PIPELINE_INVALID_STATE); + } + } + _SG_TRACE_ARGS(dealloc_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_dealloc_attachments(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + if (atts->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_attachments(atts); + } else { + _SG_ERROR(DEALLOC_ATTACHMENTS_INVALID_STATE); + } + } + _SG_TRACE_ARGS(dealloc_attachments, atts_id); +} + +SOKOL_API_IMPL void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_buffer_desc desc_def = _sg_buffer_desc_defaults(desc); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_buffer(buf, &desc_def); + SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)); + } else { + _SG_ERROR(INIT_BUFFER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(init_buffer, buf_id, &desc_def); +} + +SOKOL_API_IMPL void sg_init_image(sg_image img_id, const sg_image_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_image_desc desc_def = _sg_image_desc_defaults(desc); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + if (img->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_image(img, &desc_def); + SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)); + } else { + _SG_ERROR(INIT_IMAGE_INVALID_STATE); + } + } + _SG_TRACE_ARGS(init_image, img_id, &desc_def); +} + +SOKOL_API_IMPL void sg_init_sampler(sg_sampler smp_id, const sg_sampler_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_sampler_desc desc_def = _sg_sampler_desc_defaults(desc); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_sampler(smp, &desc_def); + SOKOL_ASSERT((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)); + } else { + _SG_ERROR(INIT_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(init_sampler, smp_id, &desc_def); +} + +SOKOL_API_IMPL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_shader_desc desc_def = _sg_shader_desc_defaults(desc); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_shader(shd, &desc_def); + SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)); + } else { + _SG_ERROR(INIT_SHADER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(init_shader, shd_id, &desc_def); +} + +SOKOL_API_IMPL void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_pipeline_desc desc_def = _sg_pipeline_desc_defaults(desc); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_pipeline(pip, &desc_def); + SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)); + } else { + _SG_ERROR(INIT_PIPELINE_INVALID_STATE); + } + } + _SG_TRACE_ARGS(init_pipeline, pip_id, &desc_def); +} + +SOKOL_API_IMPL void sg_init_attachments(sg_attachments atts_id, const sg_attachments_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_attachments_desc desc_def = _sg_attachments_desc_defaults(desc); + _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + if (atts->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_attachments(atts, &desc_def); + SOKOL_ASSERT((atts->slot.state == SG_RESOURCESTATE_VALID) || (atts->slot.state == SG_RESOURCESTATE_FAILED)); + } else { + _SG_ERROR(INIT_ATTACHMENTS_INVALID_STATE); + } + } + _SG_TRACE_ARGS(init_attachments, atts_id, &desc_def); +} + +SOKOL_API_IMPL void sg_uninit_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + if ((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_buffer(buf); + SOKOL_ASSERT(buf->slot.state == SG_RESOURCESTATE_ALLOC); + } else { + _SG_ERROR(UNINIT_BUFFER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_buffer, buf_id); +} + +SOKOL_API_IMPL void sg_uninit_image(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + if ((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_image(img); + SOKOL_ASSERT(img->slot.state == SG_RESOURCESTATE_ALLOC); + } else { + _SG_ERROR(UNINIT_IMAGE_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_image, img_id); +} + +SOKOL_API_IMPL void sg_uninit_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if ((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_sampler(smp); + SOKOL_ASSERT(smp->slot.state == SG_RESOURCESTATE_ALLOC); + } else { + _SG_ERROR(UNINIT_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_sampler, smp_id); +} + +SOKOL_API_IMPL void sg_uninit_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + if ((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_shader(shd); + SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_ALLOC); + } else { + _SG_ERROR(UNINIT_SHADER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_shader, shd_id); +} + +SOKOL_API_IMPL void sg_uninit_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if ((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_pipeline(pip); + SOKOL_ASSERT(pip->slot.state == SG_RESOURCESTATE_ALLOC); + } else { + _SG_ERROR(UNINIT_PIPELINE_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_uninit_attachments(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + if ((atts->slot.state == SG_RESOURCESTATE_VALID) || (atts->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_attachments(atts); + SOKOL_ASSERT(atts->slot.state == SG_RESOURCESTATE_ALLOC); + } else { + _SG_ERROR(UNINIT_ATTACHMENTS_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_attachments, atts_id); +} + +SOKOL_API_IMPL void sg_fail_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { + buf->slot.state = SG_RESOURCESTATE_FAILED; + } else { + _SG_ERROR(FAIL_BUFFER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(fail_buffer, buf_id); +} + +SOKOL_API_IMPL void sg_fail_image(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + if (img->slot.state == SG_RESOURCESTATE_ALLOC) { + img->slot.state = SG_RESOURCESTATE_FAILED; + } else { + _SG_ERROR(FAIL_IMAGE_INVALID_STATE); + } + } + _SG_TRACE_ARGS(fail_image, img_id); +} + +SOKOL_API_IMPL void sg_fail_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + smp->slot.state = SG_RESOURCESTATE_FAILED; + } else { + _SG_ERROR(FAIL_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(fail_sampler, smp_id); +} + +SOKOL_API_IMPL void sg_fail_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { + shd->slot.state = SG_RESOURCESTATE_FAILED; + } else { + _SG_ERROR(FAIL_SHADER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(fail_shader, shd_id); +} + +SOKOL_API_IMPL void sg_fail_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { + pip->slot.state = SG_RESOURCESTATE_FAILED; + } else { + _SG_ERROR(FAIL_PIPELINE_INVALID_STATE); + } + } + _SG_TRACE_ARGS(fail_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_fail_attachments(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + if (atts->slot.state == SG_RESOURCESTATE_ALLOC) { + atts->slot.state = SG_RESOURCESTATE_FAILED; + } else { + _SG_ERROR(FAIL_ATTACHMENTS_INVALID_STATE); + } + } + _SG_TRACE_ARGS(fail_attachments, atts_id); +} + +SOKOL_API_IMPL sg_resource_state sg_query_buffer_state(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + sg_resource_state res = buf ? buf->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_image_state(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + sg_resource_state res = img ? img->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_sampler_state(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + sg_resource_state res = smp ? smp->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_shader_state(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + sg_resource_state res = shd ? shd->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_pipeline_state(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + sg_resource_state res = pip ? pip->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_attachments_state(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + sg_resource_state res = atts ? atts->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_buffer sg_make_buffer(const sg_buffer_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_buffer_desc desc_def = _sg_buffer_desc_defaults(desc); + sg_buffer buf_id = _sg_alloc_buffer(); + if (buf_id.id != SG_INVALID_ID) { + _sg_buffer_t* buf = _sg_buffer_at(&_sg.pools, buf_id.id); + SOKOL_ASSERT(buf && (buf->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_buffer(buf, &desc_def); + SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)); + } + _SG_TRACE_ARGS(make_buffer, &desc_def, buf_id); + return buf_id; +} + +SOKOL_API_IMPL sg_image sg_make_image(const sg_image_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_image_desc desc_def = _sg_image_desc_defaults(desc); + sg_image img_id = _sg_alloc_image(); + if (img_id.id != SG_INVALID_ID) { + _sg_image_t* img = _sg_image_at(&_sg.pools, img_id.id); + SOKOL_ASSERT(img && (img->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_image(img, &desc_def); + SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)); + } + _SG_TRACE_ARGS(make_image, &desc_def, img_id); + return img_id; +} + +SOKOL_API_IMPL sg_sampler sg_make_sampler(const sg_sampler_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_sampler_desc desc_def = _sg_sampler_desc_defaults(desc); + sg_sampler smp_id = _sg_alloc_sampler(); + if (smp_id.id != SG_INVALID_ID) { + _sg_sampler_t* smp = _sg_sampler_at(&_sg.pools, smp_id.id); + SOKOL_ASSERT(smp && (smp->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_sampler(smp, &desc_def); + SOKOL_ASSERT((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)); + } + _SG_TRACE_ARGS(make_sampler, &desc_def, smp_id); + return smp_id; +} + +SOKOL_API_IMPL sg_shader sg_make_shader(const sg_shader_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_shader_desc desc_def = _sg_shader_desc_defaults(desc); + sg_shader shd_id = _sg_alloc_shader(); + if (shd_id.id != SG_INVALID_ID) { + _sg_shader_t* shd = _sg_shader_at(&_sg.pools, shd_id.id); + SOKOL_ASSERT(shd && (shd->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_shader(shd, &desc_def); + SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)); + } + _SG_TRACE_ARGS(make_shader, &desc_def, shd_id); + return shd_id; +} + +SOKOL_API_IMPL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_pipeline_desc desc_def = _sg_pipeline_desc_defaults(desc); + sg_pipeline pip_id = _sg_alloc_pipeline(); + if (pip_id.id != SG_INVALID_ID) { + _sg_pipeline_t* pip = _sg_pipeline_at(&_sg.pools, pip_id.id); + SOKOL_ASSERT(pip && (pip->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_pipeline(pip, &desc_def); + SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)); + } + _SG_TRACE_ARGS(make_pipeline, &desc_def, pip_id); + return pip_id; +} + +SOKOL_API_IMPL sg_attachments sg_make_attachments(const sg_attachments_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_attachments_desc desc_def = _sg_attachments_desc_defaults(desc); + sg_attachments atts_id = _sg_alloc_attachments(); + if (atts_id.id != SG_INVALID_ID) { + _sg_attachments_t* atts = _sg_attachments_at(&_sg.pools, atts_id.id); + SOKOL_ASSERT(atts && (atts->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_attachments(atts, &desc_def); + SOKOL_ASSERT((atts->slot.state == SG_RESOURCESTATE_VALID) || (atts->slot.state == SG_RESOURCESTATE_FAILED)); + } + _SG_TRACE_ARGS(make_attachments, &desc_def, atts_id); + return atts_id; +} + +SOKOL_API_IMPL void sg_destroy_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_buffer, buf_id); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + if ((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_buffer(buf); + SOKOL_ASSERT(buf->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_buffer(buf); + SOKOL_ASSERT(buf->slot.state == SG_RESOURCESTATE_INITIAL); + } + } +} + +SOKOL_API_IMPL void sg_destroy_image(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_image, img_id); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + if ((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_image(img); + SOKOL_ASSERT(img->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (img->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_image(img); + SOKOL_ASSERT(img->slot.state == SG_RESOURCESTATE_INITIAL); + } + } +} + +SOKOL_API_IMPL void sg_destroy_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_sampler, smp_id); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if ((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_sampler(smp); + SOKOL_ASSERT(smp->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_sampler(smp); + SOKOL_ASSERT(smp->slot.state == SG_RESOURCESTATE_INITIAL); + } + } +} + +SOKOL_API_IMPL void sg_destroy_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_shader, shd_id); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + if ((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_shader(shd); + SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_shader(shd); + SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_INITIAL); + } + } +} + +SOKOL_API_IMPL void sg_destroy_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_pipeline, pip_id); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if ((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_pipeline(pip); + SOKOL_ASSERT(pip->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_pipeline(pip); + SOKOL_ASSERT(pip->slot.state == SG_RESOURCESTATE_INITIAL); + } + } +} + +SOKOL_API_IMPL void sg_destroy_attachments(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_attachments, atts_id); + _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + if ((atts->slot.state == SG_RESOURCESTATE_VALID) || (atts->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_attachments(atts); + SOKOL_ASSERT(atts->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (atts->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_attachments(atts); + SOKOL_ASSERT(atts->slot.state == SG_RESOURCESTATE_INITIAL); + } + } +} + +SOKOL_API_IMPL void sg_begin_pass(const sg_pass* pass) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(!_sg.cur_pass.valid); + SOKOL_ASSERT(!_sg.cur_pass.in_pass); + SOKOL_ASSERT(pass); + SOKOL_ASSERT((pass->_start_canary == 0) && (pass->_end_canary == 0)); + const sg_pass pass_def = _sg_pass_defaults(pass); + if (!_sg_validate_begin_pass(&pass_def)) { + return; + } + if (pass_def.attachments.id != SG_INVALID_ID) { + // an offscreen pass + SOKOL_ASSERT(_sg.cur_pass.atts == 0); + _sg.cur_pass.atts = _sg_lookup_attachments(&_sg.pools, pass_def.attachments.id); + if (0 == _sg.cur_pass.atts) { + _SG_ERROR(BEGINPASS_ATTACHMENT_INVALID); + return; + } + _sg.cur_pass.atts_id = pass_def.attachments; + _sg.cur_pass.width = _sg.cur_pass.atts->cmn.width; + _sg.cur_pass.height = _sg.cur_pass.atts->cmn.height; + } else { + // a swapchain pass + SOKOL_ASSERT(pass_def.swapchain.width > 0); + SOKOL_ASSERT(pass_def.swapchain.height > 0); + SOKOL_ASSERT(pass_def.swapchain.color_format > SG_PIXELFORMAT_NONE); + SOKOL_ASSERT(pass_def.swapchain.sample_count > 0); + _sg.cur_pass.width = pass_def.swapchain.width; + _sg.cur_pass.height = pass_def.swapchain.height; + _sg.cur_pass.swapchain.color_fmt = pass_def.swapchain.color_format; + _sg.cur_pass.swapchain.depth_fmt = pass_def.swapchain.depth_format; + _sg.cur_pass.swapchain.sample_count = pass_def.swapchain.sample_count; + } + _sg.cur_pass.valid = true; // may be overruled by backend begin-pass functions + _sg.cur_pass.in_pass = true; + _sg_begin_pass(&pass_def); + _SG_TRACE_ARGS(begin_pass, &pass_def); +} + +SOKOL_API_IMPL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); + _sg_stats_add(num_apply_viewport, 1); + if (!_sg.cur_pass.valid) { + return; + } + _sg_apply_viewport(x, y, width, height, origin_top_left); + _SG_TRACE_ARGS(apply_viewport, x, y, width, height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left) { + sg_apply_viewport((int)x, (int)y, (int)width, (int)height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); + _sg_stats_add(num_apply_scissor_rect, 1); + if (!_sg.cur_pass.valid) { + return; + } + _sg_apply_scissor_rect(x, y, width, height, origin_top_left); + _SG_TRACE_ARGS(apply_scissor_rect, x, y, width, height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left) { + sg_apply_scissor_rect((int)x, (int)y, (int)width, (int)height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); + _sg_stats_add(num_apply_pipeline, 1); + if (!_sg_validate_apply_pipeline(pip_id)) { + _sg.next_draw_valid = false; + return; + } + if (!_sg.cur_pass.valid) { + return; + } + _sg.cur_pipeline = pip_id; + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + SOKOL_ASSERT(pip); + _sg.next_draw_valid = (SG_RESOURCESTATE_VALID == pip->slot.state); + SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->cmn.shader_id.id)); + _sg_apply_pipeline(pip); + _SG_TRACE_ARGS(apply_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); + SOKOL_ASSERT(bindings); + SOKOL_ASSERT((bindings->_start_canary == 0) && (bindings->_end_canary==0)); + _sg_stats_add(num_apply_bindings, 1); + if (!_sg_validate_apply_bindings(bindings)) { + _sg.next_draw_valid = false; + return; + } + if (!_sg.cur_pass.valid) { + return; + } + + _sg_bindings_t bnd; + _sg_clear(&bnd, sizeof(bnd)); + bnd.pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); + if (0 == bnd.pip) { + _sg.next_draw_valid = false; + } + + for (int i = 0; i < SG_MAX_VERTEX_BUFFERS; i++, bnd.num_vbs++) { + if (bindings->vertex_buffers[i].id) { + bnd.vbs[i] = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); + bnd.vb_offsets[i] = bindings->vertex_buffer_offsets[i]; + if (bnd.vbs[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.vbs[i]->slot.state); + _sg.next_draw_valid &= !bnd.vbs[i]->cmn.append_overflow; + } else { + _sg.next_draw_valid = false; + } + } else { + break; + } + } + + if (bindings->index_buffer.id) { + bnd.ib = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); + bnd.ib_offset = bindings->index_buffer_offset; + if (bnd.ib) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.ib->slot.state); + _sg.next_draw_valid &= !bnd.ib->cmn.append_overflow; + } else { + _sg.next_draw_valid = false; + } + } + + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, bnd.num_vs_imgs++) { + if (bindings->vs.images[i].id) { + bnd.vs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->vs.images[i].id); + if (bnd.vs_imgs[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.vs_imgs[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } + } else { + break; + } + } + + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++, bnd.num_vs_smps++) { + if (bindings->vs.samplers[i].id) { + bnd.vs_smps[i] = _sg_lookup_sampler(&_sg.pools, bindings->vs.samplers[i].id); + if (bnd.vs_smps[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.vs_smps[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } + } else { + break; + } + } + + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++, bnd.num_vs_sbufs++) { + if (bindings->vs.storage_buffers[i].id) { + bnd.vs_sbufs[i] = _sg_lookup_buffer(&_sg.pools, bindings->vs.storage_buffers[i].id); + if (bnd.vs_sbufs[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.vs_sbufs[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } + } else { + break; + } + } + + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, bnd.num_fs_imgs++) { + if (bindings->fs.images[i].id) { + bnd.fs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->fs.images[i].id); + if (bnd.fs_imgs[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.fs_imgs[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } + } else { + break; + } + } + + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++, bnd.num_fs_smps++) { + if (bindings->fs.samplers[i].id) { + bnd.fs_smps[i] = _sg_lookup_sampler(&_sg.pools, bindings->fs.samplers[i].id); + if (bnd.fs_smps[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.fs_smps[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } + } else { + break; + } + } + + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++, bnd.num_fs_sbufs++) { + if (bindings->fs.storage_buffers[i].id) { + bnd.fs_sbufs[i] = _sg_lookup_buffer(&_sg.pools, bindings->fs.storage_buffers[i].id); + if (bnd.fs_sbufs[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.fs_sbufs[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } + } else { + break; + } + } + if (_sg.next_draw_valid) { + _sg.next_draw_valid &= _sg_apply_bindings(&bnd); + _SG_TRACE_ARGS(apply_bindings, bindings); + } +} + +SOKOL_API_IMPL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); + SOKOL_ASSERT((stage == SG_SHADERSTAGE_VS) || (stage == SG_SHADERSTAGE_FS)); + SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + _sg_stats_add(num_apply_uniforms, 1); + _sg_stats_add(size_apply_uniforms, (uint32_t)data->size); + if (!_sg_validate_apply_uniforms(stage, ub_index, data)) { + _sg.next_draw_valid = false; + return; + } + if (!_sg.cur_pass.valid) { + return; + } + if (!_sg.next_draw_valid) { + return; + } + _sg_apply_uniforms(stage, ub_index, data); + _SG_TRACE_ARGS(apply_uniforms, stage, ub_index, data); +} + +SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); + SOKOL_ASSERT(base_element >= 0); + SOKOL_ASSERT(num_elements >= 0); + SOKOL_ASSERT(num_instances >= 0); + _sg_stats_add(num_draw, 1); + if (!_sg.cur_pass.valid) { + return; + } + if (!_sg.next_draw_valid) { + return; + } + /* attempting to draw with zero elements or instances is not technically an + error, but might be handled as an error in the backend API (e.g. on Metal) + */ + if ((0 == num_elements) || (0 == num_instances)) { + return; + } + _sg_draw(base_element, num_elements, num_instances); + _SG_TRACE_ARGS(draw, base_element, num_elements, num_instances); +} + +SOKOL_API_IMPL void sg_end_pass(void) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); + _sg_stats_add(num_passes, 1); + // NOTE: don't exit early if !_sg.cur_pass.valid + _sg_end_pass(); + _sg.cur_pipeline.id = SG_INVALID_ID; + _sg_clear(&_sg.cur_pass, sizeof(_sg.cur_pass)); + _SG_TRACE_NOARGS(end_pass); +} + +SOKOL_API_IMPL void sg_commit(void) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(!_sg.cur_pass.valid); + SOKOL_ASSERT(!_sg.cur_pass.in_pass); + _sg_commit(); + _sg.stats.frame_index = _sg.frame_index; + _sg.prev_stats = _sg.stats; + _sg_clear(&_sg.stats, sizeof(_sg.stats)); + _sg_notify_commit_listeners(); + _SG_TRACE_NOARGS(commit); + _sg.frame_index++; +} + +SOKOL_API_IMPL void sg_reset_state_cache(void) { + SOKOL_ASSERT(_sg.valid); + _sg_reset_state_cache(); + _SG_TRACE_NOARGS(reset_state_cache); +} + +SOKOL_API_IMPL void sg_update_buffer(sg_buffer buf_id, const sg_range* data) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + _sg_stats_add(num_update_buffer, 1); + _sg_stats_add(size_update_buffer, (uint32_t)data->size); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if ((data->size > 0) && buf && (buf->slot.state == SG_RESOURCESTATE_VALID)) { + if (_sg_validate_update_buffer(buf, data)) { + SOKOL_ASSERT(data->size <= (size_t)buf->cmn.size); + // only one update allowed per buffer and frame + SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index); + // update and append on same buffer in same frame not allowed + SOKOL_ASSERT(buf->cmn.append_frame_index != _sg.frame_index); + _sg_update_buffer(buf, data); + buf->cmn.update_frame_index = _sg.frame_index; + } + } + _SG_TRACE_ARGS(update_buffer, buf_id, data); +} + +SOKOL_API_IMPL int sg_append_buffer(sg_buffer buf_id, const sg_range* data) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(data && data->ptr); + _sg_stats_add(num_append_buffer, 1); + _sg_stats_add(size_append_buffer, (uint32_t)data->size); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + int result; + if (buf) { + // rewind append cursor in a new frame + if (buf->cmn.append_frame_index != _sg.frame_index) { + buf->cmn.append_pos = 0; + buf->cmn.append_overflow = false; + } + if (((size_t)buf->cmn.append_pos + data->size) > (size_t)buf->cmn.size) { + buf->cmn.append_overflow = true; + } + const int start_pos = buf->cmn.append_pos; + // NOTE: the multiple-of-4 requirement for the buffer offset is coming + // from WebGPU, but we want identical behaviour between backends + SOKOL_ASSERT(_sg_multiple_u64((uint64_t)start_pos, 4)); + if (buf->slot.state == SG_RESOURCESTATE_VALID) { + if (_sg_validate_append_buffer(buf, data)) { + if (!buf->cmn.append_overflow && (data->size > 0)) { + // update and append on same buffer in same frame not allowed + SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index); + _sg_append_buffer(buf, data, buf->cmn.append_frame_index != _sg.frame_index); + buf->cmn.append_pos += (int) _sg_roundup_u64(data->size, 4); + buf->cmn.append_frame_index = _sg.frame_index; + } + } + } + result = start_pos; + } else { + // FIXME: should we return -1 here? + result = 0; + } + _SG_TRACE_ARGS(append_buffer, buf_id, data, result); + return result; +} + +SOKOL_API_IMPL bool sg_query_buffer_overflow(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + bool result = buf ? buf->cmn.append_overflow : false; + return result; +} + +SOKOL_API_IMPL bool sg_query_buffer_will_overflow(sg_buffer buf_id, size_t size) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + bool result = false; + if (buf) { + int append_pos = buf->cmn.append_pos; + // rewind append cursor in a new frame + if (buf->cmn.append_frame_index != _sg.frame_index) { + append_pos = 0; + } + if ((append_pos + _sg_roundup((int)size, 4)) > buf->cmn.size) { + result = true; + } + } + return result; +} + +SOKOL_API_IMPL void sg_update_image(sg_image img_id, const sg_image_data* data) { + SOKOL_ASSERT(_sg.valid); + _sg_stats_add(num_update_image, 1); + for (int face_index = 0; face_index < SG_CUBEFACE_NUM; face_index++) { + for (int mip_index = 0; mip_index < SG_MAX_MIPMAPS; mip_index++) { + if (data->subimage[face_index][mip_index].size == 0) { + break; + } + _sg_stats_add(size_update_image, (uint32_t)data->subimage[face_index][mip_index].size); + } + } + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img && img->slot.state == SG_RESOURCESTATE_VALID) { + if (_sg_validate_update_image(img, data)) { + SOKOL_ASSERT(img->cmn.upd_frame_index != _sg.frame_index); + _sg_update_image(img, data); + img->cmn.upd_frame_index = _sg.frame_index; + } + } + _SG_TRACE_ARGS(update_image, img_id, data); +} + +SOKOL_API_IMPL void sg_push_debug_group(const char* name) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(name); + _sg_push_debug_group(name); + _SG_TRACE_ARGS(push_debug_group, name); +} + +SOKOL_API_IMPL void sg_pop_debug_group(void) { + SOKOL_ASSERT(_sg.valid); + _sg_pop_debug_group(); + _SG_TRACE_NOARGS(pop_debug_group); +} + +SOKOL_API_IMPL bool sg_add_commit_listener(sg_commit_listener listener) { + SOKOL_ASSERT(_sg.valid); + return _sg_add_commit_listener(&listener); +} + +SOKOL_API_IMPL bool sg_remove_commit_listener(sg_commit_listener listener) { + SOKOL_ASSERT(_sg.valid); + return _sg_remove_commit_listener(&listener); +} + +SOKOL_API_IMPL void sg_enable_frame_stats(void) { + SOKOL_ASSERT(_sg.valid); + _sg.stats_enabled = true; +} + +SOKOL_API_IMPL void sg_disable_frame_stats(void) { + SOKOL_ASSERT(_sg.valid); + _sg.stats_enabled = false; +} + +SOKOL_API_IMPL bool sg_frame_stats_enabled(void) { + return _sg.stats_enabled; +} + +SOKOL_API_IMPL sg_buffer_info sg_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_buffer_info info; + _sg_clear(&info, sizeof(info)); + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + info.slot.state = buf->slot.state; + info.slot.res_id = buf->slot.id; + info.update_frame_index = buf->cmn.update_frame_index; + info.append_frame_index = buf->cmn.append_frame_index; + info.append_pos = buf->cmn.append_pos; + info.append_overflow = buf->cmn.append_overflow; + #if defined(SOKOL_D3D11) + info.num_slots = 1; + info.active_slot = 0; + #else + info.num_slots = buf->cmn.num_slots; + info.active_slot = buf->cmn.active_slot; + #endif + } + return info; +} + +SOKOL_API_IMPL sg_image_info sg_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_image_info info; + _sg_clear(&info, sizeof(info)); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + info.slot.state = img->slot.state; + info.slot.res_id = img->slot.id; + info.upd_frame_index = img->cmn.upd_frame_index; + #if defined(SOKOL_D3D11) + info.num_slots = 1; + info.active_slot = 0; + #else + info.num_slots = img->cmn.num_slots; + info.active_slot = img->cmn.active_slot; + #endif + } + return info; +} + +SOKOL_API_IMPL sg_sampler_info sg_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_sampler_info info; + _sg_clear(&info, sizeof(info)); + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + info.slot.state = smp->slot.state; + info.slot.res_id = smp->slot.id; + } + return info; +} + +SOKOL_API_IMPL sg_shader_info sg_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_shader_info info; + _sg_clear(&info, sizeof(info)); + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + info.slot.state = shd->slot.state; + info.slot.res_id = shd->slot.id; + } + return info; +} + +SOKOL_API_IMPL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + sg_pipeline_info info; + _sg_clear(&info, sizeof(info)); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + info.slot.state = pip->slot.state; + info.slot.res_id = pip->slot.id; + } + return info; +} + +SOKOL_API_IMPL sg_attachments_info sg_query_attachments_info(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + sg_attachments_info info; + _sg_clear(&info, sizeof(info)); + const _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + info.slot.state = atts->slot.state; + info.slot.res_id = atts->slot.id; + } + return info; +} + +SOKOL_API_IMPL sg_buffer_desc sg_query_buffer_desc(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_buffer_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + desc.size = (size_t)buf->cmn.size; + desc.type = buf->cmn.type; + desc.usage = buf->cmn.usage; + } + return desc; +} + +SOKOL_API_IMPL sg_image_desc sg_query_image_desc(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_image_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + desc.type = img->cmn.type; + desc.render_target = img->cmn.render_target; + desc.width = img->cmn.width; + desc.height = img->cmn.height; + desc.num_slices = img->cmn.num_slices; + desc.num_mipmaps = img->cmn.num_mipmaps; + desc.usage = img->cmn.usage; + desc.pixel_format = img->cmn.pixel_format; + desc.sample_count = img->cmn.sample_count; + } + return desc; +} + +SOKOL_API_IMPL sg_sampler_desc sg_query_sampler_desc(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_sampler_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + desc.min_filter = smp->cmn.min_filter; + desc.mag_filter = smp->cmn.mag_filter; + desc.mipmap_filter = smp->cmn.mipmap_filter; + desc.wrap_u = smp->cmn.wrap_u; + desc.wrap_v = smp->cmn.wrap_v; + desc.wrap_w = smp->cmn.wrap_w; + desc.min_lod = smp->cmn.min_lod; + desc.max_lod = smp->cmn.max_lod; + desc.border_color = smp->cmn.border_color; + desc.compare = smp->cmn.compare; + desc.max_anisotropy = smp->cmn.max_anisotropy; + } + return desc; +} + +SOKOL_API_IMPL sg_shader_desc sg_query_shader_desc(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_shader_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + for (int stage_idx = 0; stage_idx < SG_NUM_SHADER_STAGES; stage_idx++) { + sg_shader_stage_desc* stage_desc = (stage_idx == 0) ? &desc.vs : &desc.fs; + const _sg_shader_stage_t* stage = &shd->cmn.stage[stage_idx]; + for (int ub_idx = 0; ub_idx < stage->num_uniform_blocks; ub_idx++) { + sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_idx]; + const _sg_shader_uniform_block_t* ub = &stage->uniform_blocks[ub_idx]; + ub_desc->size = ub->size; + } + for (int img_idx = 0; img_idx < stage->num_images; img_idx++) { + sg_shader_image_desc* img_desc = &stage_desc->images[img_idx]; + const _sg_shader_image_t* img = &stage->images[img_idx]; + img_desc->used = true; + img_desc->image_type = img->image_type; + img_desc->sample_type = img->sample_type; + img_desc->multisampled = img->multisampled; + } + for (int smp_idx = 0; smp_idx < stage->num_samplers; smp_idx++) { + sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_idx]; + const _sg_shader_sampler_t* smp = &stage->samplers[smp_idx]; + smp_desc->used = true; + smp_desc->sampler_type = smp->sampler_type; + } + for (int img_smp_idx = 0; img_smp_idx < stage->num_image_samplers; img_smp_idx++) { + sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_idx]; + const _sg_shader_image_sampler_t* img_smp = &stage->image_samplers[img_smp_idx]; + img_smp_desc->used = true; + img_smp_desc->image_slot = img_smp->image_slot; + img_smp_desc->sampler_slot = img_smp->sampler_slot; + img_smp_desc->glsl_name = 0; + } + } + } + return desc; +} + +SOKOL_API_IMPL sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + sg_pipeline_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + desc.shader = pip->cmn.shader_id; + desc.layout = pip->cmn.layout; + desc.depth = pip->cmn.depth; + desc.stencil = pip->cmn.stencil; + desc.color_count = pip->cmn.color_count; + for (int i = 0; i < pip->cmn.color_count; i++) { + desc.colors[i] = pip->cmn.colors[i]; + } + desc.primitive_type = pip->cmn.primitive_type; + desc.index_type = pip->cmn.index_type; + desc.cull_mode = pip->cmn.cull_mode; + desc.face_winding = pip->cmn.face_winding; + desc.sample_count = pip->cmn.sample_count; + desc.blend_color = pip->cmn.blend_color; + desc.alpha_to_coverage_enabled = pip->cmn.alpha_to_coverage_enabled; + } + return desc; +} + +SOKOL_API_IMPL sg_attachments_desc sg_query_attachments_desc(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + sg_attachments_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + for (int i = 0; i < atts->cmn.num_colors; i++) { + desc.colors[i].image = atts->cmn.colors[i].image_id; + desc.colors[i].mip_level = atts->cmn.colors[i].mip_level; + desc.colors[i].slice = atts->cmn.colors[i].slice; + } + desc.depth_stencil.image = atts->cmn.depth_stencil.image_id; + desc.depth_stencil.mip_level = atts->cmn.depth_stencil.mip_level; + desc.depth_stencil.slice = atts->cmn.depth_stencil.slice; + } + return desc; +} + +SOKOL_API_IMPL sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_buffer_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_image_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_sampler_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_shader_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_pipeline_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_attachments_desc sg_query_attachments_defaults(const sg_attachments_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_attachments_desc_defaults(desc); +} + +SOKOL_API_IMPL const void* sg_d3d11_device(void) { + #if defined(SOKOL_D3D11) + return (const void*) _sg.d3d11.dev; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_d3d11_device_context(void) { + #if defined(SOKOL_D3D11) + return (const void*) _sg.d3d11.ctx; + #else + return 0; + #endif +} + +SOKOL_API_IMPL sg_d3d11_buffer_info sg_d3d11_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_buffer_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + res.buf = (const void*) buf->d3d11.buf; + } + #else + _SOKOL_UNUSED(buf_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_image_info sg_d3d11_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_image_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + res.tex2d = (const void*) img->d3d11.tex2d; + res.tex3d = (const void*) img->d3d11.tex3d; + res.res = (const void*) img->d3d11.res; + res.srv = (const void*) img->d3d11.srv; + } + #else + _SOKOL_UNUSED(img_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_sampler_info sg_d3d11_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_sampler_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + res.smp = (const void*) smp->d3d11.smp; + } + #else + _SOKOL_UNUSED(smp_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_shader_info sg_d3d11_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_shader_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + for (int i = 0; i < SG_MAX_SHADERSTAGE_UBS; i++) { + res.vs_cbufs[i] = (const void*) shd->d3d11.stage[SG_SHADERSTAGE_VS].cbufs[i]; + res.fs_cbufs[i] = (const void*) shd->d3d11.stage[SG_SHADERSTAGE_FS].cbufs[i]; + } + res.vs = (const void*) shd->d3d11.vs; + res.fs = (const void*) shd->d3d11.fs; + } + #else + _SOKOL_UNUSED(shd_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_pipeline_info sg_d3d11_query_pipeline_info(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_pipeline_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + res.il = (const void*) pip->d3d11.il; + res.rs = (const void*) pip->d3d11.rs; + res.dss = (const void*) pip->d3d11.dss; + res.bs = (const void*) pip->d3d11.bs; + } + #else + _SOKOL_UNUSED(pip_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_attachments_info sg_d3d11_query_attachments_info(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_attachments_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + res.color_rtv[i] = (const void*) atts->d3d11.colors[i].view.rtv; + res.resolve_rtv[i] = (const void*) atts->d3d11.resolves[i].view.rtv; + } + res.dsv = (const void*) atts->d3d11.depth_stencil.view.dsv; + } + #else + _SOKOL_UNUSED(atts_id); + #endif + return res; +} + +SOKOL_API_IMPL const void* sg_mtl_device(void) { + #if defined(SOKOL_METAL) + if (nil != _sg.mtl.device) { + return (__bridge const void*) _sg.mtl.device; + } else { + return 0; + } + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_mtl_render_command_encoder(void) { + #if defined(SOKOL_METAL) + if (nil != _sg.mtl.cmd_encoder) { + return (__bridge const void*) _sg.mtl.cmd_encoder; + } else { + return 0; + } + #else + return 0; + #endif +} + +SOKOL_API_IMPL sg_mtl_buffer_info sg_mtl_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_mtl_buffer_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + if (buf->mtl.buf[i] != 0) { + res.buf[i] = (__bridge void*) _sg_mtl_id(buf->mtl.buf[i]); + } + } + res.active_slot = buf->cmn.active_slot; + } + #else + _SOKOL_UNUSED(buf_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_mtl_image_info sg_mtl_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_mtl_image_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + if (img->mtl.tex[i] != 0) { + res.tex[i] = (__bridge void*) _sg_mtl_id(img->mtl.tex[i]); + } + } + res.active_slot = img->cmn.active_slot; + } + #else + _SOKOL_UNUSED(img_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_mtl_sampler_info sg_mtl_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_mtl_sampler_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if (smp->mtl.sampler_state != 0) { + res.smp = (__bridge void*) _sg_mtl_id(smp->mtl.sampler_state); + } + } + #else + _SOKOL_UNUSED(smp_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_mtl_shader_info sg_mtl_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_mtl_shader_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + const int vs_lib = shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib; + const int vs_func = shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func; + const int fs_lib = shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib; + const int fs_func = shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func; + if (vs_lib != 0) { + res.vs_lib = (__bridge void*) _sg_mtl_id(vs_lib); + } + if (fs_lib != 0) { + res.fs_lib = (__bridge void*) _sg_mtl_id(fs_lib); + } + if (vs_func != 0) { + res.vs_func = (__bridge void*) _sg_mtl_id(vs_func); + } + if (fs_func != 0) { + res.fs_func = (__bridge void*) _sg_mtl_id(fs_func); + } + } + #else + _SOKOL_UNUSED(shd_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_mtl_pipeline_info sg_mtl_query_pipeline_info(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + sg_mtl_pipeline_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if (pip->mtl.rps != 0) { + res.rps = (__bridge void*) _sg_mtl_id(pip->mtl.rps); + } + if (pip->mtl.dss != 0) { + res.dss = (__bridge void*) _sg_mtl_id(pip->mtl.dss); + } + } + #else + _SOKOL_UNUSED(pip_id); + #endif + return res; +} + +SOKOL_API_IMPL const void* sg_wgpu_device(void) { + #if defined(SOKOL_WGPU) + return (const void*) _sg.wgpu.dev; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_wgpu_queue(void) { + #if defined(SOKOL_WGPU) + return (const void*) _sg.wgpu.queue; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_wgpu_command_encoder(void) { + #if defined(SOKOL_WGPU) + return (const void*) _sg.wgpu.cmd_enc; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_wgpu_render_pass_encoder(void) { + #if defined(SOKOL_WGPU) + return (const void*) _sg.wgpu.pass_enc; + #else + return 0; + #endif +} + +SOKOL_API_IMPL sg_wgpu_buffer_info sg_wgpu_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_buffer_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + res.buf = (const void*) buf->wgpu.buf; + } + #else + _SOKOL_UNUSED(buf_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_image_info sg_wgpu_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_image_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + res.tex = (const void*) img->wgpu.tex; + res.view = (const void*) img->wgpu.view; + } + #else + _SOKOL_UNUSED(img_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_sampler_info sg_wgpu_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_sampler_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + res.smp = (const void*) smp->wgpu.smp; + } + #else + _SOKOL_UNUSED(smp_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_shader_info sg_wgpu_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_shader_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + res.vs_mod = (const void*) shd->wgpu.stage[SG_SHADERSTAGE_VS].module; + res.fs_mod = (const void*) shd->wgpu.stage[SG_SHADERSTAGE_FS].module; + res.bgl = (const void*) shd->wgpu.bind_group_layout; + } + #else + _SOKOL_UNUSED(shd_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_pipeline_info sg_wgpu_query_pipeline_info(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_pipeline_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + res.pip = (const void*) pip->wgpu.pip; + } + #else + _SOKOL_UNUSED(pip_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_attachments_info sg_wgpu_query_attachments_info(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_attachments_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + res.color_view[i] = (const void*) atts->wgpu.colors[i].view; + res.resolve_view[i] = (const void*) atts->wgpu.resolves[i].view; + } + res.ds_view = (const void*) atts->wgpu.depth_stencil.view; + } + #else + _SOKOL_UNUSED(atts_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_buffer_info sg_gl_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_gl_buffer_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + res.buf[i] = buf->gl.buf[i]; + } + res.active_slot = buf->cmn.active_slot; + } + #else + _SOKOL_UNUSED(buf_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_image_info sg_gl_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_gl_image_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + res.tex[i] = img->gl.tex[i]; + } + res.tex_target = img->gl.target; + res.msaa_render_buffer = img->gl.msaa_render_buffer; + res.active_slot = img->cmn.active_slot; + } + #else + _SOKOL_UNUSED(img_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_sampler_info sg_gl_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_gl_sampler_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + res.smp = smp->gl.smp; + } + #else + _SOKOL_UNUSED(smp_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_shader_info sg_gl_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_gl_shader_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + res.prog = shd->gl.prog; + } + #else + _SOKOL_UNUSED(shd_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_attachments_info sg_gl_query_attachments_info(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + sg_gl_attachments_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + res.framebuffer = atts->gl.fb; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + res.msaa_resolve_framebuffer[i] = atts->gl.msaa_resolve_framebuffer[i]; + } + } + #else + _SOKOL_UNUSED(atts_id); + #endif + return res; +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif // SOKOL_GFX_IMPL diff --git a/thirdparty/sokol/sokol_glue.h b/thirdparty/sokol/sokol_glue.h new file mode 100644 index 0000000..a715b17 --- /dev/null +++ b/thirdparty/sokol/sokol_glue.h @@ -0,0 +1,162 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_GLUE_IMPL) +#define SOKOL_GLUE_IMPL +#endif +#ifndef SOKOL_GLUE_INCLUDED +/* + sokol_glue.h -- glue helper functions for sokol headers + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_GLUE_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + ...optionally provide the following macros to override defaults: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_GLUE_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_GLUE_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + If sokol_glue.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_GLUE_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + OVERVIEW + ======== + sokol_glue.h provides glue helper functions between sokol_gfx.h and sokol_app.h, + so that sokol_gfx.h doesn't need to depend on sokol_app.h but can be + used with different window system glue libraries. + + PROVIDED FUNCTIONS + ================== + + sg_environment sglue_environment(void) + + Returns an sg_environment struct initialized by calling sokol_app.h + functions. Use this in the sg_setup() call like this: + + sg_setup(&(sg_desc){ + .environment = sglue_environment(), + ... + }); + + sg_swapchain sglue_swapchain(void) + + Returns an sg_swapchain struct initialized by calling sokol_app.h + functions. Use this in sg_begin_pass() for a 'swapchain pass' like + this: + + sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain(), ... }); + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_GLUE_INCLUDED + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_GLUE_API_DECL) +#define SOKOL_GLUE_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_GLUE_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GLUE_IMPL) +#define SOKOL_GLUE_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_GLUE_API_DECL __declspec(dllimport) +#else +#define SOKOL_GLUE_API_DECL extern +#endif +#endif + +#ifndef SOKOL_GFX_INCLUDED +#error "Please include sokol_gfx.h before sokol_glue.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +SOKOL_GLUE_API_DECL sg_environment sglue_environment(void); +SOKOL_GLUE_API_DECL sg_swapchain sglue_swapchain(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* SOKOL_GLUE_INCLUDED */ + +/*-- IMPLEMENTATION ----------------------------------------------------------*/ +#ifdef SOKOL_GLUE_IMPL +#define SOKOL_GLUE_IMPL_INCLUDED (1) +#include /* memset */ + +#ifndef SOKOL_APP_INCLUDED +#error "Please include sokol_app.h before the sokol_glue.h implementation" +#endif + +#ifndef SOKOL_API_IMPL +#define SOKOL_API_IMPL +#endif + + +SOKOL_API_IMPL sg_environment sglue_environment(void) { + sg_environment env; + memset(&env, 0, sizeof(env)); + env.defaults.color_format = (sg_pixel_format) sapp_color_format(); + env.defaults.depth_format = (sg_pixel_format) sapp_depth_format(); + env.defaults.sample_count = sapp_sample_count(); + env.metal.device = sapp_metal_get_device(); + env.d3d11.device = sapp_d3d11_get_device(); + env.d3d11.device_context = sapp_d3d11_get_device_context(); + env.wgpu.device = sapp_wgpu_get_device(); + return env; +} + +SOKOL_API_IMPL sg_swapchain sglue_swapchain(void) { + sg_swapchain swapchain; + memset(&swapchain, 0, sizeof(swapchain)); + swapchain.width = sapp_width(); + swapchain.height = sapp_height(); + swapchain.sample_count = sapp_sample_count(); + swapchain.color_format = (sg_pixel_format)sapp_color_format(); + swapchain.depth_format = (sg_pixel_format)sapp_depth_format(); + swapchain.metal.current_drawable = sapp_metal_get_current_drawable(); + swapchain.metal.depth_stencil_texture = sapp_metal_get_depth_stencil_texture(); + swapchain.metal.msaa_color_texture = sapp_metal_get_msaa_color_texture(); + swapchain.d3d11.render_view = sapp_d3d11_get_render_view(); + swapchain.d3d11.resolve_view = sapp_d3d11_get_resolve_view(); + swapchain.d3d11.depth_stencil_view = sapp_d3d11_get_depth_stencil_view(); + swapchain.wgpu.render_view = sapp_wgpu_get_render_view(); + swapchain.wgpu.resolve_view = sapp_wgpu_get_resolve_view(); + swapchain.wgpu.depth_stencil_view = sapp_wgpu_get_depth_stencil_view(); + swapchain.gl.framebuffer = sapp_gl_get_framebuffer(); + return swapchain; +} + +#endif /* SOKOL_GLUE_IMPL */ diff --git a/thirdparty/sokol/sokol_log.h b/thirdparty/sokol/sokol_log.h new file mode 100644 index 0000000..58ff30b --- /dev/null +++ b/thirdparty/sokol/sokol_log.h @@ -0,0 +1,343 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_LOG_IMPL) +#define SOKOL_LOG_IMPL +#endif +#ifndef SOKOL_LOG_INCLUDED +/* + sokol_log.h -- common logging callback for sokol headers + + Project URL: https://github.com/floooh/sokol + + Example code: https://github.com/floooh/sokol-samples + + Do this: + #define SOKOL_IMPL or + #define SOKOL_LOG_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + Optionally provide the following defines when building the implementation: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + SOKOL_LOG_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_GFX_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + Optionally define the following for verbose output: + + SOKOL_DEBUG - by default this is defined if _DEBUG is defined + + + OVERVIEW + ======== + sokol_log.h provides a default logging callback for other sokol headers. + + To use the default log callback, just include sokol_log.h and provide + a function pointer to the 'slog_func' function when setting up the + sokol library: + + For instance with sokol_audio.h: + + #include "sokol_log.h" + ... + saudio_setup(&(saudio_desc){ .logger.func = slog_func }); + + Logging output goes to stderr and/or a platform specific logging subsystem + (which means that in some scenarios you might see logging messages duplicated): + + - Windows: stderr + OutputDebugStringA() + - macOS/iOS/Linux: stderr + syslog() + - Emscripten: console.info()/warn()/error() + - Android: __android_log_write() + + On Windows with sokol_app.h also note the runtime config items to make + stdout/stderr output visible on the console for WinMain() applications + via sapp_desc.win32_console_attach or sapp_desc.win32_console_create, + however when running in a debugger on Windows, the logging output should + show up on the debug output UI panel. + + In debug mode, a log message might look like this: + + [sspine][error][id:12] /Users/floh/projects/sokol/util/sokol_spine.h:3472:0: + SKELETON_DESC_NO_ATLAS: no atlas object provided in sspine_skeleton_desc.atlas + + The source path and line number is formatted like compiler errors, in some IDEs (like VSCode) + such error messages are clickable. + + In release mode, logging is less verbose as to not bloat the executable with string data, but you still get + enough information to identify the type and location of an error: + + [sspine][error][id:12][line:3472] + + RULES FOR WRITING YOUR OWN LOGGING FUNCTION + =========================================== + - must be re-entrant because it might be called from different threads + - must treat **all** provided string pointers as optional (can be null) + - don't store the string pointers, copy the string data instead + - must not return for log level panic + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2023 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_LOG_INCLUDED (1) +#include + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_LOG_API_DECL) +#define SOKOL_LOG_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_LOG_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_LOG_IMPL) +#define SOKOL_LOG_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_LOG_API_DECL __declspec(dllimport) +#else +#define SOKOL_LOG_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + Plug this function into the 'logger.func' struct item when initializing any of the sokol + headers. For instance for sokol_audio.h it would loom like this: + + saudio_setup(&(saudio_desc){ + .logger = { + .func = slog_func + } + }); +*/ +SOKOL_LOG_API_DECL void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data); + +#ifdef __cplusplus +} // extern "C" +#endif +#endif // SOKOL_LOG_INCLUDED + +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation +#ifdef SOKOL_LOG_IMPL +#define SOKOL_LOG_IMPL_INCLUDED (1) + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif + +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif + +#ifndef _SOKOL_UNUSED + #define _SOKOL_UNUSED(x) (void)(x) +#endif + +// platform detection +#if defined(__APPLE__) + #define _SLOG_APPLE (1) +#elif defined(__EMSCRIPTEN__) + #define _SLOG_EMSCRIPTEN (1) +#elif defined(_WIN32) + #define _SLOG_WINDOWS (1) +#elif defined(__ANDROID__) + #define _SLOG_ANDROID (1) +#elif defined(__linux__) || defined(__unix__) + #define _SLOG_LINUX (1) +#else +#error "sokol_log.h: unknown platform" +#endif + +#include // abort +#include // fputs +#include // size_t + +#if defined(_SLOG_EMSCRIPTEN) +#include +#elif defined(_SLOG_WINDOWS) +#ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX + #define NOMINMAX +#endif +#include +#elif defined(_SLOG_ANDROID) +#include +#elif defined(_SLOG_LINUX) || defined(_SLOG_APPLE) +#include +#endif + +// size of line buffer (on stack!) in bytes including terminating zero +#define _SLOG_LINE_LENGTH (512) + +_SOKOL_PRIVATE char* _slog_append(const char* str, char* dst, char* end) { + if (str) { + char c; + while (((c = *str++) != 0) && (dst < (end - 1))) { + *dst++ = c; + } + } + *dst = 0; + return dst; +} + +_SOKOL_PRIVATE char* _slog_itoa(uint32_t x, char* buf, size_t buf_size) { + const size_t max_digits_and_null = 11; + if (buf_size < max_digits_and_null) { + return 0; + } + char* p = buf + max_digits_and_null; + *--p = 0; + do { + *--p = '0' + (x % 10); + x /= 10; + } while (x != 0); + return p; +} + +#if defined(_SLOG_EMSCRIPTEN) +EM_JS(void, slog_js_log, (uint32_t level, const char* c_str), { + const str = UTF8ToString(c_str); + switch (level) { + case 0: console.error(str); break; + case 1: console.error(str); break; + case 2: console.warn(str); break; + default: console.info(str); break; + } +}); +#endif + +SOKOL_API_IMPL void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data) { + _SOKOL_UNUSED(user_data); + + const char* log_level_str; + switch (log_level) { + case 0: log_level_str = "panic"; break; + case 1: log_level_str = "error"; break; + case 2: log_level_str = "warning"; break; + default: log_level_str = "info"; break; + } + + // build log output line + char line_buf[_SLOG_LINE_LENGTH]; + char* str = line_buf; + char* end = line_buf + sizeof(line_buf); + char num_buf[32]; + if (tag) { + str = _slog_append("[", str, end); + str = _slog_append(tag, str, end); + str = _slog_append("]", str, end); + } + str = _slog_append("[", str, end); + str = _slog_append(log_level_str, str, end); + str = _slog_append("]", str, end); + str = _slog_append("[id:", str, end); + str = _slog_append(_slog_itoa(log_item, num_buf, sizeof(num_buf)), str, end); + str = _slog_append("]", str, end); + // if a filename is provided, build a clickable log message that's compatible with compiler error messages + if (filename) { + str = _slog_append(" ", str, end); + #if defined(_MSC_VER) + // MSVC compiler error format + str = _slog_append(filename, str, end); + str = _slog_append("(", str, end); + str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); + str = _slog_append("): ", str, end); + #else + // gcc/clang compiler error format + str = _slog_append(filename, str, end); + str = _slog_append(":", str, end); + str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); + str = _slog_append(":0: ", str, end); + #endif + } + else { + str = _slog_append("[line:", str, end); + str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); + str = _slog_append("] ", str, end); + } + if (message) { + str = _slog_append("\n\t", str, end); + str = _slog_append(message, str, end); + } + str = _slog_append("\n\n", str, end); + if (0 == log_level) { + str = _slog_append("ABORTING because of [panic]\n", str, end); + (void)str; + } + + // print to stderr? + #if defined(_SLOG_LINUX) || defined(_SLOG_WINDOWS) || defined(_SLOG_APPLE) + fputs(line_buf, stderr); + #endif + + // platform specific logging calls + #if defined(_SLOG_WINDOWS) + OutputDebugStringA(line_buf); + #elif defined(_SLOG_ANDROID) + int prio; + switch (log_level) { + case 0: prio = ANDROID_LOG_FATAL; break; + case 1: prio = ANDROID_LOG_ERROR; break; + case 2: prio = ANDROID_LOG_WARN; break; + default: prio = ANDROID_LOG_INFO; break; + } + __android_log_write(prio, "SOKOL", line_buf); + #elif defined(_SLOG_EMSCRIPTEN) + slog_js_log(log_level, line_buf); + #elif defined(_SLOG_LINUX) || defined(_SLOG_APPLE) + int prio; + switch (log_level) { + case 0: prio = LOG_CRIT; break; + case 1: prio = LOG_ERR; break; + case 2: prio = LOG_WARNING; break; + default: prio = LOG_INFO; break; + } + syslog(prio, "%s", line_buf); + #endif + if (0 == log_level) { + abort(); + } +} +#endif // SOKOL_LOG_IMPL diff --git a/thirdparty/sokol/sokol_time.h b/thirdparty/sokol/sokol_time.h new file mode 100644 index 0000000..fd766d8 --- /dev/null +++ b/thirdparty/sokol/sokol_time.h @@ -0,0 +1,319 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_TIME_IMPL) +#define SOKOL_TIME_IMPL +#endif +#ifndef SOKOL_TIME_INCLUDED +/* + sokol_time.h -- simple cross-platform time measurement + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_TIME_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + Optionally provide the following defines with your own implementations: + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_TIME_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_TIME_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + If sokol_time.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_TIME_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + void stm_setup(); + Call once before any other functions to initialize sokol_time + (this calls for instance QueryPerformanceFrequency on Windows) + + uint64_t stm_now(); + Get current point in time in unspecified 'ticks'. The value that + is returned has no relation to the 'wall-clock' time and is + not in a specific time unit, it is only useful to compute + time differences. + + uint64_t stm_diff(uint64_t new, uint64_t old); + Computes the time difference between new and old. This will always + return a positive, non-zero value. + + uint64_t stm_since(uint64_t start); + Takes the current time, and returns the elapsed time since start + (this is a shortcut for "stm_diff(stm_now(), start)") + + uint64_t stm_laptime(uint64_t* last_time); + This is useful for measuring frame time and other recurring + events. It takes the current time, returns the time difference + to the value in last_time, and stores the current time in + last_time for the next call. If the value in last_time is 0, + the return value will be zero (this usually happens on the + very first call). + + uint64_t stm_round_to_common_refresh_rate(uint64_t duration) + This oddly named function takes a measured frame time and + returns the closest "nearby" common display refresh rate frame duration + in ticks. If the input duration isn't close to any common display + refresh rate, the input duration will be returned unchanged as a fallback. + The main purpose of this function is to remove jitter/inaccuracies from + measured frame times, and instead use the display refresh rate as + frame duration. + NOTE: for more robust frame timing, consider using the + sokol_app.h function sapp_frame_duration() + + Use the following functions to convert a duration in ticks into + useful time units: + + double stm_sec(uint64_t ticks); + double stm_ms(uint64_t ticks); + double stm_us(uint64_t ticks); + double stm_ns(uint64_t ticks); + Converts a tick value into seconds, milliseconds, microseconds + or nanoseconds. Note that not all platforms will have nanosecond + or even microsecond precision. + + Uses the following time measurement functions under the hood: + + Windows: QueryPerformanceFrequency() / QueryPerformanceCounter() + MacOS/iOS: mach_absolute_time() + emscripten: emscripten_get_now() + Linux+others: clock_gettime(CLOCK_MONOTONIC) + + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_TIME_INCLUDED (1) +#include + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_TIME_API_DECL) +#define SOKOL_TIME_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_TIME_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_TIME_IMPL) +#define SOKOL_TIME_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_TIME_API_DECL __declspec(dllimport) +#else +#define SOKOL_TIME_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +SOKOL_TIME_API_DECL void stm_setup(void); +SOKOL_TIME_API_DECL uint64_t stm_now(void); +SOKOL_TIME_API_DECL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks); +SOKOL_TIME_API_DECL uint64_t stm_since(uint64_t start_ticks); +SOKOL_TIME_API_DECL uint64_t stm_laptime(uint64_t* last_time); +SOKOL_TIME_API_DECL uint64_t stm_round_to_common_refresh_rate(uint64_t frame_ticks); +SOKOL_TIME_API_DECL double stm_sec(uint64_t ticks); +SOKOL_TIME_API_DECL double stm_ms(uint64_t ticks); +SOKOL_TIME_API_DECL double stm_us(uint64_t ticks); +SOKOL_TIME_API_DECL double stm_ns(uint64_t ticks); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif // SOKOL_TIME_INCLUDED + +/*-- IMPLEMENTATION ----------------------------------------------------------*/ +#ifdef SOKOL_TIME_IMPL +#define SOKOL_TIME_IMPL_INCLUDED (1) +#include /* memset */ + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif + +#if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +typedef struct { + uint32_t initialized; + LARGE_INTEGER freq; + LARGE_INTEGER start; +} _stm_state_t; +#elif defined(__APPLE__) && defined(__MACH__) +#include +typedef struct { + uint32_t initialized; + mach_timebase_info_data_t timebase; + uint64_t start; +} _stm_state_t; +#elif defined(__EMSCRIPTEN__) +#include +typedef struct { + uint32_t initialized; + double start; +} _stm_state_t; +#else /* anything else, this will need more care for non-Linux platforms */ +#ifdef ESP8266 +// On the ESP8266, clock_gettime ignores the first argument and CLOCK_MONOTONIC isn't defined +#define CLOCK_MONOTONIC 0 +#endif +#include +typedef struct { + uint32_t initialized; + uint64_t start; +} _stm_state_t; +#endif +static _stm_state_t _stm; + +/* prevent 64-bit overflow when computing relative timestamp + see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3 +*/ +#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) +_SOKOL_PRIVATE int64_t _stm_int64_muldiv(int64_t value, int64_t numer, int64_t denom) { + int64_t q = value / denom; + int64_t r = value % denom; + return q * numer + r * numer / denom; +} +#endif + +SOKOL_API_IMPL void stm_setup(void) { + memset(&_stm, 0, sizeof(_stm)); + _stm.initialized = 0xABCDABCD; + #if defined(_WIN32) + QueryPerformanceFrequency(&_stm.freq); + QueryPerformanceCounter(&_stm.start); + #elif defined(__APPLE__) && defined(__MACH__) + mach_timebase_info(&_stm.timebase); + _stm.start = mach_absolute_time(); + #elif defined(__EMSCRIPTEN__) + _stm.start = emscripten_get_now(); + #else + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + _stm.start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec; + #endif +} + +SOKOL_API_IMPL uint64_t stm_now(void) { + SOKOL_ASSERT(_stm.initialized == 0xABCDABCD); + uint64_t now; + #if defined(_WIN32) + LARGE_INTEGER qpc_t; + QueryPerformanceCounter(&qpc_t); + now = (uint64_t) _stm_int64_muldiv(qpc_t.QuadPart - _stm.start.QuadPart, 1000000000, _stm.freq.QuadPart); + #elif defined(__APPLE__) && defined(__MACH__) + const uint64_t mach_now = mach_absolute_time() - _stm.start; + now = (uint64_t) _stm_int64_muldiv((int64_t)mach_now, (int64_t)_stm.timebase.numer, (int64_t)_stm.timebase.denom); + #elif defined(__EMSCRIPTEN__) + double js_now = emscripten_get_now() - _stm.start; + now = (uint64_t) (js_now * 1000000.0); + #else + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + now = ((uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec) - _stm.start; + #endif + return now; +} + +SOKOL_API_IMPL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks) { + if (new_ticks > old_ticks) { + return new_ticks - old_ticks; + } + else { + return 1; + } +} + +SOKOL_API_IMPL uint64_t stm_since(uint64_t start_ticks) { + return stm_diff(stm_now(), start_ticks); +} + +SOKOL_API_IMPL uint64_t stm_laptime(uint64_t* last_time) { + SOKOL_ASSERT(last_time); + uint64_t dt = 0; + uint64_t now = stm_now(); + if (0 != *last_time) { + dt = stm_diff(now, *last_time); + } + *last_time = now; + return dt; +} + +// first number is frame duration in ns, second number is tolerance in ns, +// the resulting min/max values must not overlap! +static const uint64_t _stm_refresh_rates[][2] = { + { 16666667, 1000000 }, // 60 Hz: 16.6667 +- 1ms + { 13888889, 250000 }, // 72 Hz: 13.8889 +- 0.25ms + { 13333333, 250000 }, // 75 Hz: 13.3333 +- 0.25ms + { 11764706, 250000 }, // 85 Hz: 11.7647 +- 0.25 + { 11111111, 250000 }, // 90 Hz: 11.1111 +- 0.25ms + { 10000000, 500000 }, // 100 Hz: 10.0000 +- 0.5ms + { 8333333, 500000 }, // 120 Hz: 8.3333 +- 0.5ms + { 6944445, 500000 }, // 144 Hz: 6.9445 +- 0.5ms + { 4166667, 1000000 }, // 240 Hz: 4.1666 +- 1ms + { 0, 0 }, // keep the last element always at zero +}; + +SOKOL_API_IMPL uint64_t stm_round_to_common_refresh_rate(uint64_t ticks) { + uint64_t ns; + int i = 0; + while (0 != (ns = _stm_refresh_rates[i][0])) { + uint64_t tol = _stm_refresh_rates[i][1]; + if ((ticks > (ns - tol)) && (ticks < (ns + tol))) { + return ns; + } + i++; + } + // fallthrough: didn't fit into any buckets + return ticks; +} + +SOKOL_API_IMPL double stm_sec(uint64_t ticks) { + return (double)ticks / 1000000000.0; +} + +SOKOL_API_IMPL double stm_ms(uint64_t ticks) { + return (double)ticks / 1000000.0; +} + +SOKOL_API_IMPL double stm_us(uint64_t ticks) { + return (double)ticks / 1000.0; +} + +SOKOL_API_IMPL double stm_ns(uint64_t ticks) { + return (double)ticks; +} +#endif /* SOKOL_TIME_IMPL */ + diff --git a/thirdparty/sokol/tests/.gitignore b/thirdparty/sokol/tests/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/thirdparty/sokol/tests/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/thirdparty/sokol/tests/CMakeLists.txt b/thirdparty/sokol/tests/CMakeLists.txt new file mode 100644 index 0000000..9c7d3cb --- /dev/null +++ b/thirdparty/sokol/tests/CMakeLists.txt @@ -0,0 +1,164 @@ +cmake_minimum_required(VERSION 3.20) +project(sokol-test) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 11) + +# SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU, SOKOL_DUMMY +set(SOKOL_BACKEND "SOKOL_DUMMY_BACKEND" CACHE STRING "Select 3D backend API") +set_property(CACHE SOKOL_BACKEND PROPERTY STRINGS SOKOL_GLCORE SOKOL_METAL SOKOL_D3D11 SOKOL_DUMMY_BACKEND) +option(SOKOL_FORCE_EGL "Force EGL with GLCORE backend" OFF) +option(SOKOL_FORCE_SLES "Force SLES in sokol-audio Android backend" OFF) +option(USE_ARC "Enable/disable ARC" OFF) +option(USE_ANALYZER "Enable/disable clang analyzer" OFF) + +if (CMAKE_SYSTEM_NAME STREQUAL Emscripten) + set(EMSCRIPTEN 1) +elseif (CMAKE_SYSTEM_NAME STREQUAL iOS) + set(OSX_IOS 1) +elseif (CMAKE_SYSTEM_NAME STREQUAL Android) + set(ANDROID 1) +elseif (CMAKE_SYSTEM_NAME STREQUAL Linux) + set(LINUX 1) +elseif (CMAKE_SYSTEM_NAME STREQUAL Darwin) + set(OSX_MACOS 1) +elseif (CMAKE_SYSTEM_NAME STREQUAL Windows) + set(WINDOWS 1) +else() + message(FATAL_ERROR "Unrecognized CMAKE_SYSTEM_NAME") +endif() + +message(">> CMAKE_CXX_COMPILER_ID: ${CMAKE_CXX_COMPILER_ID}") +message(">> SOKOL_BACKEND: ${SOKOL_BACKEND}") +message(">> SOKOL_FORCE_EGL: ${SOKOL_FORCE_EGL}") +message(">> SOKOL_FORCE_SLES: ${SOKOL_FORCE_SLES}") +if (OSX_IOS OR OSX_MACOS) + if (USE_ARC) + message(">> ObjC ARC ENABLED") + else() + message(">> ObjC ARC DISABLED") + endif() +endif() +message(">> BUILD_TYPE: ${CMAKE_BUILD_TYPE}") +message(">> TOOLCHAIN: ${CMAKE_TOOLCHAIN_FILE}") + +set(c_flags) +set(cxx_flags) +set(link_flags) +set(system_libs) + +if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + set(c_flags ${c_flags} /W4 /WX /D_CRT_SECURE_NO_WARNINGS) + set(cxx_flags ${cxx_flags} /W4 /WX /EHsc /D_CRT_SECURE_NO_WARNINGS) +else() + set(c_flags ${c_flags} -Wall -Wextra -Werror -Wsign-conversion -Wstrict-prototypes) + set(cxx_flags ${cxx_flags} -Wall -Wextra -Werror -Wsign-conversion -fno-rtti -fno-exceptions) + if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(c_flags ${c_flags} -Wno-missing-field-initializers) + set(cxx_flags ${cxx_flags} -Wno-missing-field-initializers) + endif() + if (USE_ANALYZER) + # FIXME: consider using clang-tidy via CMAKE_CXX_CLANG_TIDY: https://ortogonal.github.io/cmake-clang-tidy/ + # with the default settings this spams the output with irrelevant C++ coding style warnings in 3rd party libs though + message(">> Configuring for static code analysis") + set(c_flags ${c_flags} --analyze -Xanalyzer -analyzer-opt-analyze-headers) + set(cxx_flags ${cxx_flags} --analyze -Xanalyzer -analyzer-opt-analyze-headers) + set(link_flags ${link_flags} --analyze -Wno-unused-command-line-argument) + endif() +endif() + +if (EMSCRIPTEN) + set(CMAKE_EXECUTABLE_SUFFIX ".html") + set(link_flags ${link_flags} -sNO_FILESYSTEM=1 -sASSERTIONS=0 -sMALLOC=emmalloc -sINITIAL_MEMORY=33554432 --closure=1) + if (SOKOL_BACKEND STREQUAL SOKOL_WGPU) + set(link_flags ${link_flags} -sUSE_WEBGPU=1) + else() + set(link_flags ${link_flags} -sUSE_WEBGL2=1) + endif() +elseif (OSX_IOS) + set(exe_type MACOSX_BUNDLE) + if (USE_ARC) + set(c_flags ${c_flags} -fobjc-arc) + set(cxx_flags ${cxx_flags} -fobjc-arc) + endif() + set(system_libs ${system_libs} "-framework Foundation" "-framework UIKit" "-framework AudioToolbox" "-framework AVFoundation") + if (SOKOL_BACKEND STREQUAL SOKOL_METAL) + set(system_libs ${system_libs} "-framework Metal" "-framework MetalKit") + else() + set(system_libs ${system_libs} "-framework OpenGLES" "-framework GLKit") + endif() +elseif (ANDROID) + if (SOKOL_FORCE_SLES) + set(system_libs ${system_libs} GLESv3 EGL OpenSLES log android) + else() + set(system_libs ${system_libs} GLESv3 EGL aaudio log android) + endif() +elseif (LINUX) + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) + if ((SOKOL_BACKEND STREQUAL SOKOL_GLES3) OR SOKOL_FORCE_EGL) + set(system_libs ${system_libs} X11 Xi Xcursor EGL GL asound dl m Threads::Threads) + else() + set(system_libs ${system_libs} X11 Xi Xcursor GL asound dl m Threads::Threads) + endif() +elseif (OSX_MACOS) + set(exe_type MACOSX_BUNDLE) + if (USE_ARC) + set(c_flags ${c_flags} -fobjc-arc) + set(cxx_flags ${cxx_flags} -fobjc-arc) + endif() + set(system_libs ${system_libs} "-framework QuartzCore" "-framework Cocoa" "-framework AudioToolbox") + if (SOKOL_BACKEND STREQUAL SOKOL_METAL) + set(system_libs ${system_libs} "-framework MetalKit" "-framework Metal") + else() + set(system_libs ${system_libs} "-framework OpenGL") + endif() +elseif (WINDOWS) + set(exe_type WIN32) +endif() + +macro(configure_common target) + if (SOKOL_FORCE_EGL) + target_compile_definitions(${target} PRIVATE SOKOL_FORCE_EGL) + endif() + if (SOKOL_FORCE_SLES) + target_compile_definitions(${target} PRIVATE SAUDIO_ANDROID_SLES) + endif() + target_compile_definitions(${target} PRIVATE ${SOKOL_BACKEND}) + target_link_options(${target} PRIVATE ${link_flags}) + target_link_libraries(${target} PRIVATE ${system_libs}) + target_include_directories(${target} PRIVATE ../.. ../../util) + target_include_directories(${target} PRIVATE ../ext) +endmacro() + +macro(configure_osx_properties target) + if (OSX_IOS) + target_compile_definitions(${target} PRIVATE GLES_SILENCE_DEPRECATION) + endif() + set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${target}") + set_target_properties(${target} PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "${target}") + set_target_properties(${target} PROPERTIES MACOSX_BUNDLE_PRODUCT_NAME "${target}") + set_target_properties(${target} PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "${target}") +endmacro() + +macro(configure_c target) + configure_common(${target}) + target_compile_options(${target} PRIVATE ${c_flags}) + if (OSX_MACOS OR OSX_IOS) + target_compile_options(${target} PRIVATE -x objective-c) + configure_osx_properties(${target}) + endif() +endmacro() + +macro(configure_cxx target) + configure_common(${target}) + target_compile_options(${target} PRIVATE ${cxx_flags}) + if (OSX_MACOS OR OSX_IOS) + target_compile_options(${target} PRIVATE -x objective-c++) + configure_osx_properties(${target}) + endif() +endmacro() + +add_subdirectory(ext) +add_subdirectory(compile) +add_subdirectory(functional) diff --git a/thirdparty/sokol/tests/CMakePresets.json b/thirdparty/sokol/tests/CMakePresets.json new file mode 100644 index 0000000..1894497 --- /dev/null +++ b/thirdparty/sokol/tests/CMakePresets.json @@ -0,0 +1,720 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 21, + "patch": 0 + }, + "configurePresets": [ + { + "name": "macos_gl_debug", + "generator": "Ninja", + "binaryDir": "build/macos_gl_debug", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLCORE", + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "macos_gl_release", + "generator": "Ninja", + "binaryDir": "build/macos_gl_release", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLCORE", + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "macos_gl_analyze", + "generator": "Ninja", + "binaryDir": "build/macos_gl_analyze", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLCORE", + "CMAKE_BUILD_TYPE": "Debug", + "USE_ANALYZER": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "macos_metal_debug", + "generator": "Ninja", + "binaryDir": "build/macos_metal_debug", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_METAL", + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "macos_metal_release", + "generator": "Ninja", + "binaryDir": "build/macos_metal_release", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_METAL", + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "macos_metal_analyze", + "generator": "Ninja", + "binaryDir": "build/macos_metal_analyze", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_METAL", + "CMAKE_BUILD_TYPE": "Debug", + "USE_ANALYZER": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "macos_arc_gl_debug", + "generator": "Ninja", + "binaryDir": "build/macos_arc_gl_debug", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLCORE", + "USE_ARC": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "macos_arc_gl_release", + "generator": "Ninja", + "binaryDir": "build/macos_arc_gl_release", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLCORE", + "USE_ARC": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "macos_arc_gl_analyze", + "generator": "Ninja", + "binaryDir": "build/macos_arc_gl_analyze", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLCORE", + "USE_ARC": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_BUILD_TYPE": "Debug", + "USE_ANALYZER": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "macos_arc_metal_debug", + "generator": "Ninja", + "binaryDir": "build/macos_arc_metal_debug", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_METAL", + "USE_ARC": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "macos_arc_metal_release", + "generator": "Ninja", + "binaryDir": "build/macos_arc_metal_release", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_METAL", + "USE_ARC": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "macos_arc_metal_analyze", + "generator": "Ninja", + "binaryDir": "build/macos_arc_metal_analyze", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_METAL", + "USE_ARC": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_BUILD_TYPE": "Debug", + "USE_ANALYZER": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "ios_gl", + "generator": "Xcode", + "binaryDir": "build/ios_gl", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLES3", + "CMAKE_SYSTEM_NAME": "iOS" + } + }, + { + "name": "ios_gl_analyze", + "generator": "Ninja", + "binaryDir": "build/ios_gl_analyze", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLES3", + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_SYSTEM_NAME": "iOS", + "USE_ANALYZER": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "ios_metal", + "generator": "Xcode", + "binaryDir": "build/ios_metal", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_METAL", + "CMAKE_SYSTEM_NAME": "iOS" + } + }, + { + "name": "ios_metal_analyze", + "generator": "Ninja", + "binaryDir": "build/ios_metal_analyze", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_METAL", + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_SYSTEM_NAME": "iOS", + "USE_ANALYZER": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "ios_arc_gl", + "generator": "Xcode", + "binaryDir": "build/ios_arc_gl", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLES3", + "USE_ARC": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_SYSTEM_NAME": "iOS" + } + }, + { + "name": "ios_arc_gl_analyze", + "generator": "Ninja", + "binaryDir": "build/ios_arc_gl_analyze", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLES3", + "USE_ARC": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_SYSTEM_NAME": "iOS", + "USE_ANALYZER": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "ios_arc_metal", + "generator": "Xcode", + "binaryDir": "build/ios_arc_metal", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_METAL", + "USE_ARC": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_SYSTEM_NAME": "iOS" + } + }, + { + "name": "ios_arc_metal_analyze", + "generator": "Ninja", + "binaryDir": "build/ios_arc_metal_analyze", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_METAL", + "USE_ARC": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_SYSTEM_NAME": "iOS", + "USE_ANALYZER": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "linux_gl_debug", + "generator": "Ninja", + "binaryDir": "build/linux_gl_debug", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLCORE", + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "linux_gl_release", + "generator": "Ninja", + "binaryDir": "build/linux_gl_release", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLCORE", + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "linux_gl_analyze", + "generator": "Ninja", + "binaryDir": "build/linux_gl_analyze", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLCORE", + "CMAKE_BUILD_TYPE": "Debug", + "USE_ANALYZER": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "linux_gles3_debug", + "generator": "Ninja", + "binaryDir": "build/linux_gles3_debug", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLES3", + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "linux_gles3_release", + "generator": "Ninja", + "binaryDir": "build/linux_gles3_release", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLES3", + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "linux_gles3_analyze", + "generator": "Ninja", + "binaryDir": "build/linux_gles3_analyze", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLES3", + "CMAKE_BUILD_TYPE": "Debug", + "USE_ANALYZER": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "linux_gl_egl_debug", + "generator": "Ninja", + "binaryDir": "build/linux_gl_egl_debug", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLCORE", + "SOKOL_FORCE_EGL": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "linux_gl_egl_release", + "generator": "Ninja", + "binaryDir": "build/linux_gl_egl_release", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLCORE", + "SOKOL_FORCE_EGL": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "emsc_webgl2_debug", + "generator": "Ninja", + "binaryDir": "build/emsc_webgl2_debug", + "toolchainFile": "build/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLES3", + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "emsc_webgl2_release", + "generator": "Ninja", + "binaryDir": "build/emsc_webgl2_release", + "toolchainFile": "build/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLES3", + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "emsc_wgpu_debug", + "generator": "Ninja", + "binaryDir": "build/emsc_wgpu_debug", + "toolchainFile": "build/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_WGPU", + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "emsc_wgpu_release", + "generator": "Ninja", + "binaryDir": "build/emsc_wgpu_release", + "toolchainFile": "build/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_WGPU", + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "android_debug", + "generator": "Ninja", + "binaryDir": "build/android_debug", + "toolchainFile": "build/android_sdk/ndk-bundle/build/cmake/android.toolchain.cmake", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLES3", + "ANDROID_ABI": "armeabi-v7a", + "ANDROID_PLATFORM": "android-28", + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "android_release", + "generator": "Ninja", + "binaryDir": "build/android_release", + "toolchainFile": "build/android_sdk/ndk-bundle/build/cmake/android.toolchain.cmake", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLES3", + "ANDROID_ABI": "armeabi-v7a", + "ANDROID_PLATFORM": "android-28", + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "android_sles_debug", + "generator": "Ninja", + "binaryDir": "build/android_sles_debug", + "toolchainFile": "build/android_sdk/ndk-bundle/build/cmake/android.toolchain.cmake", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLES3", + "ANDROID_ABI": "armeabi-v7a", + "ANDROID_PLATFORM": "android-28", + "CMAKE_BUILD_TYPE": "Debug", + "SOKOL_FORCE_SLES": { + "type": "BOOL", + "value": "ON" + } + } + }, + { + "name": "android_sles_release", + "generator": "Ninja", + "binaryDir": "build/android_sles_release", + "toolchainFile": "build/android_sdk/ndk-bundle/build/cmake/android.toolchain.cmake", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLES3", + "ANDROID_ABI": "armeabi-v7a", + "ANDROID_PLATFORM": "android-28", + "CMAKE_BUILD_TYPE": "Release", + "SOKOL_FORCE_SLES": { + "type": "BOOL", + "value": "ON" + } + } + }, + { + "name": "win_gl", + "binaryDir": "build/win_gl", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLCORE" + } + }, + { + "name": "win_gl_analyze", + "generator": "Ninja", + "binaryDir": "build/win_gl_analyze", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_GLCORE", + "CMAKE_BUILD_TYPE": "Debug", + "USE_ANALYZER": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "win_d3d11", + "binaryDir": "build/win_d3d11", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_D3D11" + } + }, + { + "name": "win_d3d11_analyze", + "generator": "Ninja", + "binaryDir": "build/win_d3d11_analyze", + "cacheVariables": { + "SOKOL_BACKEND": "SOKOL_D3D11", + "CMAKE_BUILD_TYPE": "Debug", + "USE_ANALYZER": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++" + } + } + ], + "buildPresets": [ + { + "name": "macos_gl_debug", + "configurePreset": "macos_gl_debug" + }, + { + "name": "macos_gl_release", + "configurePreset": "macos_gl_release" + }, + { + "name": "macos_gl_analyze", + "configurePreset": "macos_gl_analyze" + }, + { + "name": "macos_metal_debug", + "configurePreset": "macos_metal_debug" + }, + { + "name": "macos_metal_release", + "configurePreset": "macos_metal_release" + }, + { + "name": "macos_metal_analyze", + "configurePreset": "macos_metal_analyze" + }, + { + "name": "macos_arc_gl_debug", + "configurePreset": "macos_arc_gl_debug" + }, + { + "name": "macos_arc_gl_release", + "configurePreset": "macos_arc_gl_release" + }, + { + "name": "macos_arc_gl_analyze", + "configurePreset": "macos_arc_gl_analyze" + }, + { + "name": "macos_arc_metal_debug", + "configurePreset": "macos_arc_metal_debug" + }, + { + "name": "macos_arc_metal_release", + "configurePreset": "macos_arc_metal_release" + }, + { + "name": "macos_arc_metal_analyze", + "configurePreset": "macos_arc_metal_analyze" + }, + { + "name": "ios_gl_debug", + "configurePreset": "ios_gl", + "configuration": "Debug", + "nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ] + }, + { + "name": "ios_gl_release", + "configurePreset": "ios_gl", + "configuration": "Release", + "nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ] + }, + { + "name": "ios_gl_analyze", + "configurePreset": "ios_gl_analyze" + }, + { + "name": "ios_metal_debug", + "configurePreset": "ios_metal", + "configuration": "Debug", + "nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ] + }, + { + "name": "ios_metal_release", + "configurePreset": "ios_metal", + "configuration": "Release", + "nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ] + }, + { + "name": "ios_metal_analyze", + "configurePreset": "ios_metal_analyze" + }, + { + "name": "ios_arc_gl_debug", + "configurePreset": "ios_arc_gl", + "configuration": "Debug", + "nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ] + }, + { + "name": "ios_arc_gl_release", + "configurePreset": "ios_arc_gl", + "configuration": "Release", + "nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ] + }, + { + "name": "ios_arc_gl_analyze", + "configurePreset": "ios_arc_gl_analyze" + }, + { + "name": "ios_arc_metal_debug", + "configurePreset": "ios_arc_metal", + "configuration": "Debug", + "nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ] + }, + { + "name": "ios_arc_metal_release", + "configurePreset": "ios_arc_metal", + "configuration": "Release", + "nativeToolOptions": [ "CODE_SIGN_IDENTITY=\"\"", "CODE_SIGNING_REQUIRED=NO", "CODE_SIGNING_ALLOWED=NO" ] + }, + { + "name": "ios_arc_metal_analyze", + "configurePreset": "ios_arc_metal_analyze" + }, + { + "name": "linux_gl_debug", + "configurePreset": "linux_gl_debug" + }, + { + "name": "linux_gl_release", + "configurePreset": "linux_gl_release" + }, + { + "name": "linux_gl_analyze", + "configurePreset": "linux_gl_analyze" + }, + { + "name": "linux_gles3_debug", + "configurePreset": "linux_gles3_debug" + }, + { + "name": "linux_gles3_release", + "configurePreset": "linux_gles3_release" + }, + { + "name": "linux_gles3_analyze", + "configurePreset": "linux_gles3_analyze" + }, + { + "name": "linux_gl_egl_debug", + "configurePreset": "linux_gl_egl_debug" + }, + { + "name": "linux_gl_egl_release", + "configurePreset": "linux_gl_egl_release" + }, + { + "name": "emsc_webgl2_debug", + "configurePreset": "emsc_webgl2_debug" + }, + { + "name": "emsc_webgl2_release", + "configurePreset": "emsc_webgl2_release" + }, + { + "name": "emsc_wgpu_debug", + "configurePreset": "emsc_wgpu_debug" + }, + { + "name": "emsc_wgpu_release", + "configurePreset": "emsc_wgpu_release" + }, + { + "name": "android_debug", + "configurePreset": "android_debug" + }, + { + "name": "android_release", + "configurePreset": "android_release" + }, + { + "name": "android_sles_debug", + "configurePreset": "android_sles_debug" + }, + { + "name": "android_sles_release", + "configurePreset": "android_sles_release" + }, + { + "name": "win_gl_debug", + "configurePreset": "win_gl", + "configuration": "Debug" + }, + { + "name": "win_gl_release", + "configurePreset": "win_gl", + "configuration": "Release" + }, + { + "name": "win_gl_analyze", + "configurePreset": "win_gl_analyze" + }, + { + "name": "win_d3d11_debug", + "configurePreset": "win_d3d11", + "configuration": "Debug" + }, + { + "name": "win_d3d11_release", + "configurePreset": "win_d3d11", + "configuration": "Release" + }, + { + "name": "win_d3d11_analyze", + "configurePreset": "win_d3d11_analyze" + } + ] +} diff --git a/thirdparty/sokol/tests/analyze_ios.sh b/thirdparty/sokol/tests/analyze_ios.sh new file mode 100644 index 0000000..5b3f474 --- /dev/null +++ b/thirdparty/sokol/tests/analyze_ios.sh @@ -0,0 +1,6 @@ +set -e +source test_common.sh +build ios_gl_analyze ios_gl_analyze +build ios_metal_analyze ios_metal_analyze +build ios_arc_gl_analyze ios_arc_gl_analyze +build ios_arc_metal_analyze ios_arc_metal_analyze diff --git a/thirdparty/sokol/tests/analyze_linux.sh b/thirdparty/sokol/tests/analyze_linux.sh new file mode 100644 index 0000000..87099b9 --- /dev/null +++ b/thirdparty/sokol/tests/analyze_linux.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -e +source test_common.sh + +build linux_gl_analyze linux_gl_analyze +build linux_gles3_analyze linux_gles3_analyze diff --git a/thirdparty/sokol/tests/analyze_macos.sh b/thirdparty/sokol/tests/analyze_macos.sh new file mode 100644 index 0000000..3134070 --- /dev/null +++ b/thirdparty/sokol/tests/analyze_macos.sh @@ -0,0 +1,8 @@ +set -e +source test_common.sh + +build macos_gl_analyze macos_gl_analyze +build macos_metal_analyze macos_metal_analyze + +build macos_arc_gl_analyze macos_arc_gl_analyze +build macos_arc_metal_analyze macos_arc_metal_analyze diff --git a/thirdparty/sokol/tests/analyze_win.cmd b/thirdparty/sokol/tests/analyze_win.cmd new file mode 100644 index 0000000..39aa798 --- /dev/null +++ b/thirdparty/sokol/tests/analyze_win.cmd @@ -0,0 +1,4 @@ +cmake --preset win_gl_analyze || exit /b 10 +cmake --build --preset win_gl_analyze || exit /b 10 +cmake --preset win_d3d11_analyze || exit /b 10 +cmake --build --preset win_d3d11_analyze || exit /b 10 diff --git a/thirdparty/sokol/tests/compile/CMakeLists.txt b/thirdparty/sokol/tests/compile/CMakeLists.txt new file mode 100644 index 0000000..e664555 --- /dev/null +++ b/thirdparty/sokol/tests/compile/CMakeLists.txt @@ -0,0 +1,58 @@ +set(c_sources + sokol_app.c + sokol_glue.c + sokol_gfx.c + sokol_time.c + sokol_args.c + sokol_audio.c + sokol_debugtext.c + sokol_gl.c + sokol_fontstash.c + sokol_imgui.c + sokol_gfx_imgui.c + sokol_shape.c + sokol_nuklear.c + sokol_color.c + sokol_spine.c + sokol_log.c + sokol_main.c) +if (NOT ANDROID) + set(c_sources ${c_sources} sokol_fetch.c) +endif() + +set(cxx_sources + sokol_app.cc + sokol_glue.cc + sokol_gfx.cc + sokol_time.cc + sokol_args.cc + sokol_audio.cc + sokol_debugtext.cc + sokol_gl.cc + sokol_fontstash.cc + sokol_imgui.cc + sokol_gfx_imgui.cc + sokol_shape.cc + sokol_color.cc + sokol_spine.cc + sokol_log.cc + sokol_main.cc) +if (NOT ANDROID) + set(cxx_sources ${cxx_sources} sokol_fetch.cc) +endif() + +if (ANDROID) + add_library(sokol-compiletest-c SHARED ${c_sources}) +else() + add_executable(sokol-compiletest-c ${exe_type} sokol_app.c sokol_glue.c ${c_sources}) +endif() +target_link_libraries(sokol-compiletest-c PUBLIC cimgui nuklear spine) +configure_c(sokol-compiletest-c) + +if (ANDROID) + add_library(sokol-compiletest-cxx SHARED ${cxx_sources}) +else() + add_executable(sokol-compiletest-cxx ${exe_type} ${cxx_sources}) +endif() +target_link_libraries(sokol-compiletest-cxx PUBLIC imgui nuklear spine) +configure_cxx(sokol-compiletest-cxx) diff --git a/thirdparty/sokol/tests/compile/sokol_app.c b/thirdparty/sokol/tests/compile/sokol_app.c new file mode 100644 index 0000000..33304cf --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_app.c @@ -0,0 +1,6 @@ +#define SOKOL_IMPL +#include "sokol_app.h" + +void use_app_impl(void) { + sapp_run(&(sapp_desc){0}); +} diff --git a/thirdparty/sokol/tests/compile/sokol_app.cc b/thirdparty/sokol/tests/compile/sokol_app.cc new file mode 100644 index 0000000..49c254e --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_app.cc @@ -0,0 +1,6 @@ +#define SOKOL_IMPL +#include "sokol_app.h" + +void use_app_impl() { + sapp_run({ }); +} diff --git a/thirdparty/sokol/tests/compile/sokol_args.c b/thirdparty/sokol/tests/compile/sokol_args.c new file mode 100644 index 0000000..4005283 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_args.c @@ -0,0 +1,6 @@ +#define SOKOL_IMPL +#include "sokol_args.h" + +void use_args_impl(void) { + sargs_setup(&(sargs_desc){0}); +} diff --git a/thirdparty/sokol/tests/compile/sokol_args.cc b/thirdparty/sokol/tests/compile/sokol_args.cc new file mode 100644 index 0000000..7e31d46 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_args.cc @@ -0,0 +1,6 @@ +#define SOKOL_IMPL +#include "sokol_args.h" + +void use_args_impl() { + sargs_setup({}); +} diff --git a/thirdparty/sokol/tests/compile/sokol_audio.c b/thirdparty/sokol/tests/compile/sokol_audio.c new file mode 100644 index 0000000..1dc12f2 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_audio.c @@ -0,0 +1,6 @@ +#define SOKOL_IMPL +#include "sokol_audio.h" + +void use_audio_impl(void) { + saudio_setup(&(saudio_desc){0}); +} diff --git a/thirdparty/sokol/tests/compile/sokol_audio.cc b/thirdparty/sokol/tests/compile/sokol_audio.cc new file mode 100644 index 0000000..4205d51 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_audio.cc @@ -0,0 +1,6 @@ +#define SOKOL_IMPL +#include "sokol_audio.h" + +void use_audio_impl() { + saudio_setup({}); +} diff --git a/thirdparty/sokol/tests/compile/sokol_color.c b/thirdparty/sokol/tests/compile/sokol_color.c new file mode 100644 index 0000000..ea3a354 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_color.c @@ -0,0 +1,8 @@ +#include "sokol_gfx.h" +#define SOKOL_IMPL +#include "sokol_color.h" + +void use_color_impl(void) { + sg_color c = sg_make_color_4b(255, 0, 0, 255); + (void)c; +} diff --git a/thirdparty/sokol/tests/compile/sokol_color.cc b/thirdparty/sokol/tests/compile/sokol_color.cc new file mode 100644 index 0000000..ccb6d2b --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_color.cc @@ -0,0 +1,9 @@ +#include "sokol_gfx.h" +#define SOKOL_IMPL +#include "sokol_color.h" + +void use_color_impl(void) { + sg_color c = sg_make_color(255, 0, 0, 255); + (void)c; +} + diff --git a/thirdparty/sokol/tests/compile/sokol_debugtext.c b/thirdparty/sokol/tests/compile/sokol_debugtext.c new file mode 100644 index 0000000..1d22c63 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_debugtext.c @@ -0,0 +1,7 @@ +#include "sokol_gfx.h" +#define SOKOL_IMPL +#include "sokol_debugtext.h" + +void use_debugtext_impl(void) { + sdtx_setup(&(sdtx_desc_t){0}); +} diff --git a/thirdparty/sokol/tests/compile/sokol_debugtext.cc b/thirdparty/sokol/tests/compile/sokol_debugtext.cc new file mode 100644 index 0000000..6889405 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_debugtext.cc @@ -0,0 +1,7 @@ +#include "sokol_gfx.h" +#define SOKOL_IMPL +#include "sokol_debugtext.h" + +void use_debugtext_impl() { + sdtx_setup({}); +} diff --git a/thirdparty/sokol/tests/compile/sokol_fetch.c b/thirdparty/sokol/tests/compile/sokol_fetch.c new file mode 100644 index 0000000..2509056 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_fetch.c @@ -0,0 +1,6 @@ +#define SOKOL_IMPL +#include "sokol_fetch.h" + +void use_fetch_impl(void) { + sfetch_setup(&(sfetch_desc_t){0}); +} diff --git a/thirdparty/sokol/tests/compile/sokol_fetch.cc b/thirdparty/sokol/tests/compile/sokol_fetch.cc new file mode 100644 index 0000000..69cc228 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_fetch.cc @@ -0,0 +1,6 @@ +#define SOKOL_IMPL +#include "sokol_fetch.h" + +void use_fetch_impl() { + sfetch_setup({}); +} diff --git a/thirdparty/sokol/tests/compile/sokol_fontstash.c b/thirdparty/sokol/tests/compile/sokol_fontstash.c new file mode 100644 index 0000000..0232de8 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_fontstash.c @@ -0,0 +1,21 @@ +#include "sokol_gfx.h" +#include "sokol_gl.h" + +#define FONTSTASH_IMPLEMENTATION +#if defined(_MSC_VER ) +#pragma warning(disable:4996) // strncpy use in fontstash.h +#endif +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wsign-conversion" +#endif +#include // malloc/free +#include "fontstash.h" +#define SOKOL_IMPL +#include "sokol_fontstash.h" + +void use_fontstash_impl(void) { + FONScontext* ctx = sfons_create(&(sfons_desc_t){ 0 }); + sfons_destroy(ctx); +} diff --git a/thirdparty/sokol/tests/compile/sokol_fontstash.cc b/thirdparty/sokol/tests/compile/sokol_fontstash.cc new file mode 100644 index 0000000..96d7c31 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_fontstash.cc @@ -0,0 +1,23 @@ +#include "sokol_gfx.h" +#include "sokol_gl.h" + +#define FONTSTASH_IMPLEMENTATION +#if defined(_MSC_VER ) +#pragma warning(disable:4996) // strncpy use in fontstash.h +#pragma warning(disable:4505) // unreferenced local function has been removed +#pragma warning(disable:4100) // unreferenced formal parameter +#endif +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wsign-conversion" +#endif +#include // malloc/free +#include "fontstash.h" +#define SOKOL_IMPL +#include "sokol_fontstash.h" + +void use_fontstash_impl() { + const sfons_desc_t desc = { }; + FONScontext* ctx = sfons_create(&desc); + sfons_destroy(ctx); +} diff --git a/thirdparty/sokol/tests/compile/sokol_gfx.c b/thirdparty/sokol/tests/compile/sokol_gfx.c new file mode 100644 index 0000000..8926310 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_gfx.c @@ -0,0 +1,6 @@ +#define SOKOL_IMPL +#include "sokol_gfx.h" + +void use_gfx_impl(void) { + sg_setup(&(sg_desc){0}); +} diff --git a/thirdparty/sokol/tests/compile/sokol_gfx.cc b/thirdparty/sokol/tests/compile/sokol_gfx.cc new file mode 100644 index 0000000..58f6592 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_gfx.cc @@ -0,0 +1,6 @@ +#define SOKOL_IMPL +#include "sokol_gfx.h" + +void use_gfx_impl() { + sg_setup({}); +} diff --git a/thirdparty/sokol/tests/compile/sokol_gfx_imgui.c b/thirdparty/sokol/tests/compile/sokol_gfx_imgui.c new file mode 100644 index 0000000..5b59501 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_gfx_imgui.c @@ -0,0 +1,17 @@ +#include "sokol_app.h" +#include "sokol_gfx.h" +#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS +#if defined(_MSC_VER ) +#pragma warning(disable:4201) // nonstandard extension used: nameless struct/union +#pragma warning(disable:4214) // nonstandard extension used: bit field types other than int +#endif +#include "cimgui/cimgui.h" +#include "sokol_imgui.h" +#define SOKOL_IMPL +#include "sokol_gfx_imgui.h" + +void use_gfx_imgui_impl(void) { + sgimgui_t ctx = {0}; + sgimgui_init(&ctx, &(sgimgui_desc_t){0}); + sgimgui_discard(&ctx); +} diff --git a/thirdparty/sokol/tests/compile/sokol_gfx_imgui.cc b/thirdparty/sokol/tests/compile/sokol_gfx_imgui.cc new file mode 100644 index 0000000..2e64dbc --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_gfx_imgui.cc @@ -0,0 +1,12 @@ +#include "sokol_app.h" +#include "sokol_gfx.h" +#include "imgui.h" +#include "sokol_imgui.h" +#define SOKOL_IMPL +#include "sokol_gfx_imgui.h" + +void use_gfx_imgui_impl() { + sgimgui_t ctx = {}; + sgimgui_init(&ctx, { }); + sgimgui_discard(&ctx); +} diff --git a/thirdparty/sokol/tests/compile/sokol_gl.c b/thirdparty/sokol/tests/compile/sokol_gl.c new file mode 100644 index 0000000..6dbeea1 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_gl.c @@ -0,0 +1,7 @@ +#include "sokol_gfx.h" +#define SOKOL_IMPL +#include "sokol_gl.h" + +void use_gl_impl(void) { + sgl_setup(&(sgl_desc_t){0}); +} diff --git a/thirdparty/sokol/tests/compile/sokol_gl.cc b/thirdparty/sokol/tests/compile/sokol_gl.cc new file mode 100644 index 0000000..a4177cb --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_gl.cc @@ -0,0 +1,7 @@ +#include "sokol_gfx.h" +#define SOKOL_IMPL +#include "sokol_gl.h" + +void use_gl_impl() { + sgl_setup({}); +} diff --git a/thirdparty/sokol/tests/compile/sokol_glue.c b/thirdparty/sokol/tests/compile/sokol_glue.c new file mode 100644 index 0000000..114b6e2 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_glue.c @@ -0,0 +1,9 @@ +#include "sokol_app.h" +#include "sokol_gfx.h" +#define SOKOL_IMPL +#include "sokol_glue.h" + +void use_glue_impl(void) { + const sg_environment env = sglue_environment(); + (void)env; +} diff --git a/thirdparty/sokol/tests/compile/sokol_glue.cc b/thirdparty/sokol/tests/compile/sokol_glue.cc new file mode 100644 index 0000000..4aaf21c --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_glue.cc @@ -0,0 +1,9 @@ +#include "sokol_app.h" +#include "sokol_gfx.h" +#define SOKOL_IMPL +#include "sokol_glue.h" + +void use_glue_impl() { + const sg_environment ctx = sglue_environment(); + (void)ctx; +} diff --git a/thirdparty/sokol/tests/compile/sokol_imgui.c b/thirdparty/sokol/tests/compile/sokol_imgui.c new file mode 100644 index 0000000..90f0bb7 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_imgui.c @@ -0,0 +1,18 @@ +#include "sokol_app.h" +#include "sokol_gfx.h" +#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS +#if defined(_MSC_VER ) +#pragma warning(disable:4201) // nonstandard extension used: nameless struct/union +#pragma warning(disable:4214) // nonstandard extension used: bit field types other than int +#endif +#include "cimgui/cimgui.h" +#define SOKOL_IMPL +#if defined(SOKOL_DUMMY_BACKEND) +#define SOKOL_IMGUI_NO_SOKOL_APP +#endif +#include "sokol_imgui.h" + +void use_imgui_impl(void) { + simgui_setup(&(simgui_desc_t){0}); +} + diff --git a/thirdparty/sokol/tests/compile/sokol_imgui.cc b/thirdparty/sokol/tests/compile/sokol_imgui.cc new file mode 100644 index 0000000..5a6a32f --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_imgui.cc @@ -0,0 +1,12 @@ +#include "sokol_app.h" +#include "sokol_gfx.h" +#include "imgui.h" +#define SOKOL_IMPL +#if defined(SOKOL_DUMMY_BACKEND) +#define SOKOL_IMGUI_NO_SOKOL_APP +#endif +#include "sokol_imgui.h" + +void use_imgui_impl() { + simgui_setup({}); +} diff --git a/thirdparty/sokol/tests/compile/sokol_log.c b/thirdparty/sokol/tests/compile/sokol_log.c new file mode 100644 index 0000000..ac66ec5 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_log.c @@ -0,0 +1,6 @@ +#define SOKOL_IMPL +#include "sokol_log.h" + +void use_sokol_log(void) { + slog_func("bla", 1, 123, "123", 42, "bla.c", 0); +} diff --git a/thirdparty/sokol/tests/compile/sokol_log.cc b/thirdparty/sokol/tests/compile/sokol_log.cc new file mode 100644 index 0000000..ac66ec5 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_log.cc @@ -0,0 +1,6 @@ +#define SOKOL_IMPL +#include "sokol_log.h" + +void use_sokol_log(void) { + slog_func("bla", 1, 123, "123", 42, "bla.c", 0); +} diff --git a/thirdparty/sokol/tests/compile/sokol_main.c b/thirdparty/sokol/tests/compile/sokol_main.c new file mode 100644 index 0000000..d1e34b6 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_main.c @@ -0,0 +1,13 @@ +#include "sokol_app.h" + +#if defined(SOKOL_DUMMY_BACKEND) +int main() { + return 0; +} +#else +sapp_desc sokol_main(int argc, char* argv[]) { + (void)argc; + (void)argv; + return (sapp_desc){0}; +} +#endif diff --git a/thirdparty/sokol/tests/compile/sokol_main.cc b/thirdparty/sokol/tests/compile/sokol_main.cc new file mode 100644 index 0000000..c27ed3e --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_main.cc @@ -0,0 +1,7 @@ +#include "sokol_app.h" + +sapp_desc sokol_main(int argc, char* argv[]) { + (void)argc; + (void)argv; + return { }; +} diff --git a/thirdparty/sokol/tests/compile/sokol_nuklear.c b/thirdparty/sokol/tests/compile/sokol_nuklear.c new file mode 100644 index 0000000..e5311b4 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_nuklear.c @@ -0,0 +1,22 @@ +#include "sokol_app.h" +#include "sokol_gfx.h" + +// include nuklear.h before the sokol_nuklear.h implementation +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_INCLUDE_STANDARD_VARARGS +#include "nuklear.h" + +#define SOKOL_IMPL +#if defined(SOKOL_DUMMY_BACKEND) +#define SOKOL_NUKLEAR_NO_SOKOL_APP +#endif +#include "sokol_nuklear.h" + +void use_nuklear_impl(void) { + snk_setup(&(snk_desc_t){0}); +} diff --git a/thirdparty/sokol/tests/compile/sokol_shape.c b/thirdparty/sokol/tests/compile/sokol_shape.c new file mode 100644 index 0000000..72c48b6 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_shape.c @@ -0,0 +1,7 @@ +#include "sokol_gfx.h" +#define SOKOL_IMPL +#include "sokol_shape.h" + +void use_shape_impl(void) { + sshape_plane_sizes(10); +} diff --git a/thirdparty/sokol/tests/compile/sokol_shape.cc b/thirdparty/sokol/tests/compile/sokol_shape.cc new file mode 100644 index 0000000..0013ea6 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_shape.cc @@ -0,0 +1,7 @@ +#include "sokol_gfx.h" +#define SOKOL_IMPL +#include "sokol_shape.h" + +void use_shape_impl() { + sshape_plane_sizes(10); +} diff --git a/thirdparty/sokol/tests/compile/sokol_spine.c b/thirdparty/sokol/tests/compile/sokol_spine.c new file mode 100644 index 0000000..4669003 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_spine.c @@ -0,0 +1,8 @@ +#include "sokol_gfx.h" +#define SOKOL_IMPL +#include "spine/spine.h" +#include "sokol_spine.h" + +void use_sspine_impl(void) { + sspine_setup(&(sspine_desc){0}); +} diff --git a/thirdparty/sokol/tests/compile/sokol_spine.cc b/thirdparty/sokol/tests/compile/sokol_spine.cc new file mode 100644 index 0000000..e484dbe --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_spine.cc @@ -0,0 +1,9 @@ +#include "sokol_gfx.h" +#define SOKOL_IMPL +#include "spine/spine.h" +#include "sokol_spine.h" + +void use_sspine_impl(void) { + const sspine_desc desc = {}; + sspine_setup(&desc); +} diff --git a/thirdparty/sokol/tests/compile/sokol_time.c b/thirdparty/sokol/tests/compile/sokol_time.c new file mode 100644 index 0000000..416e500 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_time.c @@ -0,0 +1,6 @@ +#define SOKOL_IMPL +#include "sokol_time.h" + +void use_time_impl(void) { + stm_setup(); +} diff --git a/thirdparty/sokol/tests/compile/sokol_time.cc b/thirdparty/sokol/tests/compile/sokol_time.cc new file mode 100644 index 0000000..f0c19c9 --- /dev/null +++ b/thirdparty/sokol/tests/compile/sokol_time.cc @@ -0,0 +1,7 @@ +#define SOKOL_IMPL +#include "sokol_time.h" + +void use_time_impl() { + stm_setup(); +} + diff --git a/thirdparty/sokol/tests/ext/CMakeLists.txt b/thirdparty/sokol/tests/ext/CMakeLists.txt new file mode 100644 index 0000000..6fa78cb --- /dev/null +++ b/thirdparty/sokol/tests/ext/CMakeLists.txt @@ -0,0 +1,97 @@ +# external dependencies + +# NOTE FetchContent is so frigging slow that we just run git directly +set(cimgui_dir ${CMAKE_BINARY_DIR}/../_deps/cimgui) +set(spineruntimes_dir ${CMAKE_BINARY_DIR}/../_deps/spineruntimes) + +if (IS_DIRECTORY ${cimgui_dir}) + message("### ${cimgui_dir} exists...") +else() + message("### Fetching cimgui to ${cimgui_dir} (this may take a while...)") + execute_process(COMMAND git clone --depth=1 --recursive https://github.com/fips-libs/fips-cimgui ${cimgui_dir}) +endif() +if (IS_DIRECTORY ${spineruntimes_dir}) + message("### ${spineruntimes_dir} exists...") +else() + message("### Fetching spine runtimes to ${spineruntimes_dir} (this may take a while...)") + execute_process(COMMAND git clone --depth=1 --branch 4.1 --recursive https://github.com/EsotericSoftware/spine-runtimes ${spineruntimes_dir}) +endif() + +add_library(cimgui + ${cimgui_dir}/cimgui/cimgui.cpp + ${cimgui_dir}/cimgui/imgui/imgui.cpp + ${cimgui_dir}/cimgui/imgui/imgui_demo.cpp + ${cimgui_dir}/cimgui/imgui/imgui_draw.cpp + ${cimgui_dir}/cimgui/imgui/imgui_tables.cpp + ${cimgui_dir}/cimgui/imgui/imgui_widgets.cpp) +target_include_directories(cimgui SYSTEM PUBLIC ${cimgui_dir}) + +add_library(imgui + ${cimgui_dir}/cimgui/imgui/imgui.cpp + ${cimgui_dir}/cimgui/imgui/imgui_demo.cpp + ${cimgui_dir}/cimgui/imgui/imgui_draw.cpp + ${cimgui_dir}/cimgui/imgui/imgui_tables.cpp + ${cimgui_dir}/cimgui/imgui/imgui_widgets.cpp) +target_include_directories(imgui SYSTEM PUBLIC ${cimgui_dir}/cimgui/imgui) + +add_library(spine + ${spineruntimes_dir}/spine-c/spine-c/src/spine/Animation.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/AnimationState.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/AnimationStateData.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/Array.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/Atlas.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/AtlasAttachmentLoader.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/Attachment.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/AttachmentLoader.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/Bone.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/BoneData.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/BoundingBoxAttachment.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/ClippingAttachment.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/Color.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/Debug.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/Event.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/EventData.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/IkConstraint.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/IkConstraintData.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/Json.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/Json.h + ${spineruntimes_dir}/spine-c/spine-c/src/spine/MeshAttachment.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/PathAttachment.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/PathConstraint.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/PathConstraintData.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/PointAttachment.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/RegionAttachment.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/Sequence.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/Skeleton.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/SkeletonBinary.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/SkeletonBounds.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/SkeletonClipping.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/SkeletonData.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/SkeletonJson.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/Skin.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/Slot.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/SlotData.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/TransformConstraint.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/TransformConstraintData.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/Triangulator.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/VertexAttachment.c + ${spineruntimes_dir}/spine-c/spine-c/src/spine/extension.c) +target_include_directories(spine SYSTEM PUBLIC ${spineruntimes_dir}/spine-c/spine-c/include) +if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + target_compile_options(spine PRIVATE /wd4267 /wd4244) # conversion from 'x' to 'y' possible loss of data +endif() +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(spine PRIVATE -Wno-shorten-64-to-32) +endif() + +file(COPY ${spineruntimes_dir}/examples/spineboy/export/spineboy-pro.json DESTINATION ${CMAKE_BINARY_DIR}) +file(COPY ${spineruntimes_dir}/examples/spineboy/export/spineboy-pro.skel DESTINATION ${CMAKE_BINARY_DIR}) +file(COPY ${spineruntimes_dir}/examples/spineboy/export/spineboy.atlas DESTINATION ${CMAKE_BINARY_DIR}) +file(COPY ${spineruntimes_dir}/examples/spineboy/export/spineboy.png DESTINATION ${CMAKE_BINARY_DIR}) + +file(COPY ${spineruntimes_dir}/examples/spineboy/export/spineboy-pro.json DESTINATION ${CMAKE_BINARY_DIR}/Debug) +file(COPY ${spineruntimes_dir}/examples/spineboy/export/spineboy-pro.skel DESTINATION ${CMAKE_BINARY_DIR}/Debug) +file(COPY ${spineruntimes_dir}/examples/spineboy/export/spineboy.atlas DESTINATION ${CMAKE_BINARY_DIR}/Debug) +file(COPY ${spineruntimes_dir}/examples/spineboy/export/spineboy.png DESTINATION ${CMAKE_BINARY_DIR}/Debug) + +add_library(nuklear nuklear.c) diff --git a/thirdparty/sokol/tests/ext/fontstash.h b/thirdparty/sokol/tests/ext/fontstash.h new file mode 100644 index 0000000..a09e6f1 --- /dev/null +++ b/thirdparty/sokol/tests/ext/fontstash.h @@ -0,0 +1,1714 @@ +// +// NOTE sokol: all IO functions have been removed +// +// Copyright (c) 2009-2013 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#ifndef FONS_H +#define FONS_H + +#ifdef __cplusplus +extern "C" { +#endif + +// To make the implementation private to the file that generates the implementation +#ifdef FONS_STATIC +#define FONS_DEF static +#else +#define FONS_DEF extern +#endif + +#define FONS_INVALID -1 + +enum FONSflags { + FONS_ZERO_TOPLEFT = 1, + FONS_ZERO_BOTTOMLEFT = 2, +}; + +enum FONSalign { + // Horizontal align + FONS_ALIGN_LEFT = 1<<0, // Default + FONS_ALIGN_CENTER = 1<<1, + FONS_ALIGN_RIGHT = 1<<2, + // Vertical align + FONS_ALIGN_TOP = 1<<3, + FONS_ALIGN_MIDDLE = 1<<4, + FONS_ALIGN_BOTTOM = 1<<5, + FONS_ALIGN_BASELINE = 1<<6, // Default +}; + +enum FONSerrorCode { + // Font atlas is full. + FONS_ATLAS_FULL = 1, + // Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE. + FONS_SCRATCH_FULL = 2, + // Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES. + FONS_STATES_OVERFLOW = 3, + // Trying to pop too many states fonsPopState(). + FONS_STATES_UNDERFLOW = 4, +}; + +struct FONSparams { + int width, height; + unsigned char flags; + void* userPtr; + int (*renderCreate)(void* uptr, int width, int height); + int (*renderResize)(void* uptr, int width, int height); + void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data); + void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts); + void (*renderDelete)(void* uptr); +}; +typedef struct FONSparams FONSparams; + +struct FONSquad +{ + float x0,y0,s0,t0; + float x1,y1,s1,t1; +}; +typedef struct FONSquad FONSquad; + +struct FONStextIter { + float x, y, nextx, nexty, scale, spacing; + unsigned int codepoint; + short isize, iblur; + struct FONSfont* font; + int prevGlyphIndex; + const char* str; + const char* next; + const char* end; + unsigned int utf8state; +}; +typedef struct FONStextIter FONStextIter; + +typedef struct FONScontext FONScontext; + +// Contructor and destructor. +FONS_DEF FONScontext* fonsCreateInternal(FONSparams* params); +FONS_DEF void fonsDeleteInternal(FONScontext* s); + +FONS_DEF void fonsSetErrorCallback(FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr); +// Returns current atlas size. +FONS_DEF void fonsGetAtlasSize(FONScontext* s, int* width, int* height); +// Expands the atlas size. +FONS_DEF int fonsExpandAtlas(FONScontext* s, int width, int height); +// Resets the whole stash. +FONS_DEF int fonsResetAtlas(FONScontext* stash, int width, int height); + +// Add fonts +FONS_DEF int fonsGetFontByName(FONScontext* s, const char* name); +FONS_DEF int fonsAddFallbackFont(FONScontext* stash, int base, int fallback); + +// State handling +FONS_DEF void fonsPushState(FONScontext* s); +FONS_DEF void fonsPopState(FONScontext* s); +FONS_DEF void fonsClearState(FONScontext* s); + +// State setting +FONS_DEF void fonsSetSize(FONScontext* s, float size); +FONS_DEF void fonsSetColor(FONScontext* s, unsigned int color); +FONS_DEF void fonsSetSpacing(FONScontext* s, float spacing); +FONS_DEF void fonsSetBlur(FONScontext* s, float blur); +FONS_DEF void fonsSetAlign(FONScontext* s, int align); +FONS_DEF void fonsSetFont(FONScontext* s, int font); + +// Draw text +FONS_DEF float fonsDrawText(FONScontext* s, float x, float y, const char* string, const char* end); + +// Measure text +FONS_DEF float fonsTextBounds(FONScontext* s, float x, float y, const char* string, const char* end, float* bounds); +FONS_DEF void fonsLineBounds(FONScontext* s, float y, float* miny, float* maxy); +FONS_DEF void fonsVertMetrics(FONScontext* s, float* ascender, float* descender, float* lineh); + +// Text iterator +FONS_DEF int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end); +FONS_DEF int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, struct FONSquad* quad); + +// Pull texture changes +FONS_DEF const unsigned char* fonsGetTextureData(FONScontext* stash, int* width, int* height); +FONS_DEF int fonsValidateTexture(FONScontext* s, int* dirty); + +// Draws the stash texture for debugging +FONS_DEF void fonsDrawDebug(FONScontext* s, float x, float y); + +#ifdef __cplusplus +} +#endif + +#endif // FONS_H + + +#ifdef FONTSTASH_IMPLEMENTATION + +#define FONS_NOTUSED(v) (void)sizeof(v) + +#ifdef FONS_USE_FREETYPE + +#include +#include FT_FREETYPE_H +#include FT_ADVANCES_H +#include + +struct FONSttFontImpl { + FT_Face font; +}; +typedef struct FONSttFontImpl FONSttFontImpl; + +static FT_Library ftLibrary; + +static int fons__tt_init() +{ + FT_Error ftError; + FONS_NOTUSED(context); + ftError = FT_Init_FreeType(&ftLibrary); + return ftError == 0; +} + +static int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize) +{ + FT_Error ftError; + FONS_NOTUSED(context); + + //font->font.userdata = stash; + ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, 0, &font->font); + return ftError == 0; +} + +static void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap) +{ + *ascent = font->font->ascender; + *descent = font->font->descender; + *lineGap = font->font->height - (*ascent - *descent); +} + +static float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) +{ + return size / (font->font->ascender - font->font->descender); +} + +static int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) +{ + return FT_Get_Char_Index(font->font, codepoint); +} + +static int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale, + int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1) +{ + FT_Error ftError; + FT_GlyphSlot ftGlyph; + FT_Fixed advFixed; + FONS_NOTUSED(scale); + + ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender))); + if (ftError) return 0; + ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER); + if (ftError) return 0; + ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, &advFixed); + if (ftError) return 0; + ftGlyph = font->font->glyph; + *advance = (int)advFixed; + *lsb = (int)ftGlyph->metrics.horiBearingX; + *x0 = ftGlyph->bitmap_left; + *x1 = *x0 + ftGlyph->bitmap.width; + *y0 = -ftGlyph->bitmap_top; + *y1 = *y0 + ftGlyph->bitmap.rows; + return 1; +} + +static void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride, + float scaleX, float scaleY, int glyph) +{ + FT_GlyphSlot ftGlyph = font->font->glyph; + int ftGlyphOffset = 0; + int x, y; + FONS_NOTUSED(outWidth); + FONS_NOTUSED(outHeight); + FONS_NOTUSED(scaleX); + FONS_NOTUSED(scaleY); + FONS_NOTUSED(glyph); // glyph has already been loaded by fons__tt_buildGlyphBitmap + + for ( y = 0; y < ftGlyph->bitmap.rows; y++ ) { + for ( x = 0; x < ftGlyph->bitmap.width; x++ ) { + output[(y * outStride) + x] = ftGlyph->bitmap.buffer[ftGlyphOffset++]; + } + } +} + +static int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) +{ + FT_Vector ftKerning; + FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning); + return (int)((ftKerning.x + 32) >> 6); // Round up and convert to integer +} + +#else + +#define STB_TRUETYPE_IMPLEMENTATION +#define STBTT_STATIC +static void* fons__tmpalloc(size_t size, void* up); +static void fons__tmpfree(void* ptr, void* up); +#define STBTT_malloc(x,u) fons__tmpalloc(x,u) +#define STBTT_free(x,u) fons__tmpfree(x,u) +#include "stb_truetype.h" + +struct FONSttFontImpl { + stbtt_fontinfo font; +}; +typedef struct FONSttFontImpl FONSttFontImpl; + +static int fons__tt_init(FONScontext *context) +{ + FONS_NOTUSED(context); + return 1; +} + +static int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize) +{ + int stbError; + FONS_NOTUSED(dataSize); + + font->font.userdata = context; + stbError = stbtt_InitFont(&font->font, data, 0); + return stbError; +} + +static void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap) +{ + stbtt_GetFontVMetrics(&font->font, ascent, descent, lineGap); +} + +static float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) +{ + return stbtt_ScaleForPixelHeight(&font->font, size); +} + +static int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) +{ + return stbtt_FindGlyphIndex(&font->font, codepoint); +} + +static int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale, + int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1) +{ + FONS_NOTUSED(size); + stbtt_GetGlyphHMetrics(&font->font, glyph, advance, lsb); + stbtt_GetGlyphBitmapBox(&font->font, glyph, scale, scale, x0, y0, x1, y1); + return 1; +} + +static void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride, + float scaleX, float scaleY, int glyph) +{ + stbtt_MakeGlyphBitmap(&font->font, output, outWidth, outHeight, outStride, scaleX, scaleY, glyph); +} + +static int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) +{ + return stbtt_GetGlyphKernAdvance(&font->font, glyph1, glyph2); +} + +#endif + +#ifndef FONS_SCRATCH_BUF_SIZE +# define FONS_SCRATCH_BUF_SIZE 64000 +#endif +#ifndef FONS_HASH_LUT_SIZE +# define FONS_HASH_LUT_SIZE 256 +#endif +#ifndef FONS_INIT_FONTS +# define FONS_INIT_FONTS 4 +#endif +#ifndef FONS_INIT_GLYPHS +# define FONS_INIT_GLYPHS 256 +#endif +#ifndef FONS_INIT_ATLAS_NODES +# define FONS_INIT_ATLAS_NODES 256 +#endif +#ifndef FONS_VERTEX_COUNT +# define FONS_VERTEX_COUNT 1024 +#endif +#ifndef FONS_MAX_STATES +# define FONS_MAX_STATES 20 +#endif +#ifndef FONS_MAX_FALLBACKS +# define FONS_MAX_FALLBACKS 20 +#endif + +static unsigned int fons__hashint(unsigned int a) +{ + a += ~(a<<15); + a ^= (a>>10); + a += (a<<3); + a ^= (a>>6); + a += ~(a<<11); + a ^= (a>>16); + return a; +} + +static int fons__mini(int a, int b) +{ + return a < b ? a : b; +} + +static int fons__maxi(int a, int b) +{ + return a > b ? a : b; +} + +struct FONSglyph +{ + unsigned int codepoint; + int index; + int next; + short size, blur; + short x0,y0,x1,y1; + short xadv,xoff,yoff; +}; +typedef struct FONSglyph FONSglyph; + +struct FONSfont +{ + FONSttFontImpl font; + char name[64]; + unsigned char* data; + int dataSize; + unsigned char freeData; + float ascender; + float descender; + float lineh; + FONSglyph* glyphs; + int cglyphs; + int nglyphs; + int lut[FONS_HASH_LUT_SIZE]; + int fallbacks[FONS_MAX_FALLBACKS]; + int nfallbacks; +}; +typedef struct FONSfont FONSfont; + +struct FONSstate +{ + int font; + int align; + float size; + unsigned int color; + float blur; + float spacing; +}; +typedef struct FONSstate FONSstate; + +struct FONSatlasNode { + short x, y, width; +}; +typedef struct FONSatlasNode FONSatlasNode; + +struct FONSatlas +{ + int width, height; + FONSatlasNode* nodes; + int nnodes; + int cnodes; +}; +typedef struct FONSatlas FONSatlas; + +struct FONScontext +{ + FONSparams params; + float itw,ith; + unsigned char* texData; + int dirtyRect[4]; + FONSfont** fonts; + FONSatlas* atlas; + int cfonts; + int nfonts; + float verts[FONS_VERTEX_COUNT*2]; + float tcoords[FONS_VERTEX_COUNT*2]; + unsigned int colors[FONS_VERTEX_COUNT]; + int nverts; + unsigned char* scratch; + int nscratch; + FONSstate states[FONS_MAX_STATES]; + int nstates; + void (*handleError)(void* uptr, int error, int val); + void* errorUptr; +}; + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +static void* fons__tmpalloc(size_t size, void* up) +{ + unsigned char* ptr; + FONScontext* stash = (FONScontext*)up; + + // 16-byte align the returned pointer + size = (size + 0xf) & ~0xf; + + if (stash->nscratch+(int)size > FONS_SCRATCH_BUF_SIZE) { + if (stash->handleError) + stash->handleError(stash->errorUptr, FONS_SCRATCH_FULL, stash->nscratch+(int)size); + return NULL; + } + ptr = stash->scratch + stash->nscratch; + stash->nscratch += (int)size; + return ptr; +} + +static void fons__tmpfree(void* ptr, void* up) +{ + (void)ptr; + (void)up; + // empty +} + +#endif // STB_TRUETYPE_IMPLEMENTATION + +// Copyright (c) 2008-2010 Bjoern Hoehrmann +// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. + +#define FONS_UTF8_ACCEPT 0 +#define FONS_UTF8_REJECT 12 + +static unsigned int fons__decutf8(unsigned int* state, unsigned int* codep, unsigned int byte) +{ + static const unsigned char utf8d[] = { + // The first part of the table maps bytes to character classes that + // to reduce the size of the transition table and create bitmasks. + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + + // The second part is a transition table that maps a combination + // of a state of the automaton and a character class to a state. + 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, + 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, + 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, + 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, + 12,36,12,12,12,12,12,12,12,12,12,12, + }; + + unsigned int type = utf8d[byte]; + + *codep = (*state != FONS_UTF8_ACCEPT) ? + (byte & 0x3fu) | (*codep << 6) : + (0xff >> type) & (byte); + + *state = utf8d[256 + *state + type]; + return *state; +} + +// Atlas based on Skyline Bin Packer by Jukka Jylänki + +static void fons__deleteAtlas(FONSatlas* atlas) +{ + if (atlas == NULL) return; + if (atlas->nodes != NULL) free(atlas->nodes); + free(atlas); +} + +static FONSatlas* fons__allocAtlas(int w, int h, int nnodes) +{ + FONSatlas* atlas = NULL; + + // Allocate memory for the font stash. + atlas = (FONSatlas*)malloc(sizeof(FONSatlas)); + if (atlas == NULL) goto error; + memset(atlas, 0, sizeof(FONSatlas)); + + atlas->width = w; + atlas->height = h; + + // Allocate space for skyline nodes + atlas->nodes = (FONSatlasNode*)malloc(sizeof(FONSatlasNode) * nnodes); + if (atlas->nodes == NULL) goto error; + memset(atlas->nodes, 0, sizeof(FONSatlasNode) * nnodes); + atlas->nnodes = 0; + atlas->cnodes = nnodes; + + // Init root node. + atlas->nodes[0].x = 0; + atlas->nodes[0].y = 0; + atlas->nodes[0].width = (short)w; + atlas->nnodes++; + + return atlas; + +error: + if (atlas) fons__deleteAtlas(atlas); + return NULL; +} + +static int fons__atlasInsertNode(FONSatlas* atlas, int idx, int x, int y, int w) +{ + int i; + // Insert node + if (atlas->nnodes+1 > atlas->cnodes) { + atlas->cnodes = atlas->cnodes == 0 ? 8 : atlas->cnodes * 2; + atlas->nodes = (FONSatlasNode*)realloc(atlas->nodes, sizeof(FONSatlasNode) * atlas->cnodes); + if (atlas->nodes == NULL) + return 0; + } + for (i = atlas->nnodes; i > idx; i--) + atlas->nodes[i] = atlas->nodes[i-1]; + atlas->nodes[idx].x = (short)x; + atlas->nodes[idx].y = (short)y; + atlas->nodes[idx].width = (short)w; + atlas->nnodes++; + + return 1; +} + +static void fons__atlasRemoveNode(FONSatlas* atlas, int idx) +{ + int i; + if (atlas->nnodes == 0) return; + for (i = idx; i < atlas->nnodes-1; i++) + atlas->nodes[i] = atlas->nodes[i+1]; + atlas->nnodes--; +} + +static void fons__atlasExpand(FONSatlas* atlas, int w, int h) +{ + // Insert node for empty space + if (w > atlas->width) + fons__atlasInsertNode(atlas, atlas->nnodes, atlas->width, 0, w - atlas->width); + atlas->width = w; + atlas->height = h; +} + +static void fons__atlasReset(FONSatlas* atlas, int w, int h) +{ + atlas->width = w; + atlas->height = h; + atlas->nnodes = 0; + + // Init root node. + atlas->nodes[0].x = 0; + atlas->nodes[0].y = 0; + atlas->nodes[0].width = (short)w; + atlas->nnodes++; +} + +static int fons__atlasAddSkylineLevel(FONSatlas* atlas, int idx, int x, int y, int w, int h) +{ + int i; + + // Insert new node + if (fons__atlasInsertNode(atlas, idx, x, y+h, w) == 0) + return 0; + + // Delete skyline segments that fall under the shadow of the new segment. + for (i = idx+1; i < atlas->nnodes; i++) { + if (atlas->nodes[i].x < atlas->nodes[i-1].x + atlas->nodes[i-1].width) { + int shrink = atlas->nodes[i-1].x + atlas->nodes[i-1].width - atlas->nodes[i].x; + atlas->nodes[i].x += (short)shrink; + atlas->nodes[i].width -= (short)shrink; + if (atlas->nodes[i].width <= 0) { + fons__atlasRemoveNode(atlas, i); + i--; + } else { + break; + } + } else { + break; + } + } + + // Merge same height skyline segments that are next to each other. + for (i = 0; i < atlas->nnodes-1; i++) { + if (atlas->nodes[i].y == atlas->nodes[i+1].y) { + atlas->nodes[i].width += atlas->nodes[i+1].width; + fons__atlasRemoveNode(atlas, i+1); + i--; + } + } + + return 1; +} + +static int fons__atlasRectFits(FONSatlas* atlas, int i, int w, int h) +{ + // Checks if there is enough space at the location of skyline span 'i', + // and return the max height of all skyline spans under that at that location, + // (think tetris block being dropped at that position). Or -1 if no space found. + int x = atlas->nodes[i].x; + int y = atlas->nodes[i].y; + int spaceLeft; + if (x + w > atlas->width) + return -1; + spaceLeft = w; + while (spaceLeft > 0) { + if (i == atlas->nnodes) return -1; + y = fons__maxi(y, atlas->nodes[i].y); + if (y + h > atlas->height) return -1; + spaceLeft -= atlas->nodes[i].width; + ++i; + } + return y; +} + +static int fons__atlasAddRect(FONSatlas* atlas, int rw, int rh, int* rx, int* ry) +{ + int besth = atlas->height, bestw = atlas->width, besti = -1; + int bestx = -1, besty = -1, i; + + // Bottom left fit heuristic. + for (i = 0; i < atlas->nnodes; i++) { + int y = fons__atlasRectFits(atlas, i, rw, rh); + if (y != -1) { + if (y + rh < besth || (y + rh == besth && atlas->nodes[i].width < bestw)) { + besti = i; + bestw = atlas->nodes[i].width; + besth = y + rh; + bestx = atlas->nodes[i].x; + besty = y; + } + } + } + + if (besti == -1) + return 0; + + // Perform the actual packing. + if (fons__atlasAddSkylineLevel(atlas, besti, bestx, besty, rw, rh) == 0) + return 0; + + *rx = bestx; + *ry = besty; + + return 1; +} + +static void fons__addWhiteRect(FONScontext* stash, int w, int h) +{ + int x, y, gx, gy; + unsigned char* dst; + if (fons__atlasAddRect(stash->atlas, w, h, &gx, &gy) == 0) + return; + + // Rasterize + dst = &stash->texData[gx + gy * stash->params.width]; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) + dst[x] = 0xff; + dst += stash->params.width; + } + + stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], gx); + stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], gy); + stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], gx+w); + stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], gy+h); +} + +FONScontext* fonsCreateInternal(FONSparams* params) +{ + FONScontext* stash = NULL; + + // Allocate memory for the font stash. + stash = (FONScontext*)malloc(sizeof(FONScontext)); + if (stash == NULL) goto error; + memset(stash, 0, sizeof(FONScontext)); + + stash->params = *params; + + // Allocate scratch buffer. + stash->scratch = (unsigned char*)malloc(FONS_SCRATCH_BUF_SIZE); + if (stash->scratch == NULL) goto error; + + // Initialize implementation library + if (!fons__tt_init(stash)) goto error; + + if (stash->params.renderCreate != NULL) { + if (stash->params.renderCreate(stash->params.userPtr, stash->params.width, stash->params.height) == 0) + goto error; + } + + stash->atlas = fons__allocAtlas(stash->params.width, stash->params.height, FONS_INIT_ATLAS_NODES); + if (stash->atlas == NULL) goto error; + + // Allocate space for fonts. + stash->fonts = (FONSfont**)malloc(sizeof(FONSfont*) * FONS_INIT_FONTS); + if (stash->fonts == NULL) goto error; + memset(stash->fonts, 0, sizeof(FONSfont*) * FONS_INIT_FONTS); + stash->cfonts = FONS_INIT_FONTS; + stash->nfonts = 0; + + // Create texture for the cache. + stash->itw = 1.0f/stash->params.width; + stash->ith = 1.0f/stash->params.height; + stash->texData = (unsigned char*)malloc(stash->params.width * stash->params.height); + if (stash->texData == NULL) goto error; + memset(stash->texData, 0, stash->params.width * stash->params.height); + + stash->dirtyRect[0] = stash->params.width; + stash->dirtyRect[1] = stash->params.height; + stash->dirtyRect[2] = 0; + stash->dirtyRect[3] = 0; + + // Add white rect at 0,0 for debug drawing. + fons__addWhiteRect(stash, 2,2); + + fonsPushState(stash); + fonsClearState(stash); + + return stash; + +error: + fonsDeleteInternal(stash); + return NULL; +} + +static FONSstate* fons__getState(FONScontext* stash) +{ + return &stash->states[stash->nstates-1]; +} + +int fonsAddFallbackFont(FONScontext* stash, int base, int fallback) +{ + FONSfont* baseFont = stash->fonts[base]; + if (baseFont->nfallbacks < FONS_MAX_FALLBACKS) { + baseFont->fallbacks[baseFont->nfallbacks++] = fallback; + return 1; + } + return 0; +} + +void fonsSetSize(FONScontext* stash, float size) +{ + fons__getState(stash)->size = size; +} + +void fonsSetColor(FONScontext* stash, unsigned int color) +{ + fons__getState(stash)->color = color; +} + +void fonsSetSpacing(FONScontext* stash, float spacing) +{ + fons__getState(stash)->spacing = spacing; +} + +void fonsSetBlur(FONScontext* stash, float blur) +{ + fons__getState(stash)->blur = blur; +} + +void fonsSetAlign(FONScontext* stash, int align) +{ + fons__getState(stash)->align = align; +} + +void fonsSetFont(FONScontext* stash, int font) +{ + fons__getState(stash)->font = font; +} + +void fonsPushState(FONScontext* stash) +{ + if (stash->nstates >= FONS_MAX_STATES) { + if (stash->handleError) + stash->handleError(stash->errorUptr, FONS_STATES_OVERFLOW, 0); + return; + } + if (stash->nstates > 0) + memcpy(&stash->states[stash->nstates], &stash->states[stash->nstates-1], sizeof(FONSstate)); + stash->nstates++; +} + +void fonsPopState(FONScontext* stash) +{ + if (stash->nstates <= 1) { + if (stash->handleError) + stash->handleError(stash->errorUptr, FONS_STATES_UNDERFLOW, 0); + return; + } + stash->nstates--; +} + +void fonsClearState(FONScontext* stash) +{ + FONSstate* state = fons__getState(stash); + state->size = 12.0f; + state->color = 0xffffffff; + state->font = 0; + state->blur = 0; + state->spacing = 0; + state->align = FONS_ALIGN_LEFT | FONS_ALIGN_BASELINE; +} + +static void fons__freeFont(FONSfont* font) +{ + if (font == NULL) return; + if (font->glyphs) free(font->glyphs); + if (font->freeData && font->data) free(font->data); + free(font); +} + +static int fons__allocFont(FONScontext* stash) +{ + FONSfont* font = NULL; + if (stash->nfonts+1 > stash->cfonts) { + stash->cfonts = stash->cfonts == 0 ? 8 : stash->cfonts * 2; + stash->fonts = (FONSfont**)realloc(stash->fonts, sizeof(FONSfont*) * stash->cfonts); + if (stash->fonts == NULL) + return -1; + } + font = (FONSfont*)malloc(sizeof(FONSfont)); + if (font == NULL) goto error; + memset(font, 0, sizeof(FONSfont)); + + font->glyphs = (FONSglyph*)malloc(sizeof(FONSglyph) * FONS_INIT_GLYPHS); + if (font->glyphs == NULL) goto error; + font->cglyphs = FONS_INIT_GLYPHS; + font->nglyphs = 0; + + stash->fonts[stash->nfonts++] = font; + return stash->nfonts-1; + +error: + fons__freeFont(font); + + return FONS_INVALID; +} + +int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData) +{ + int i, ascent, descent, fh, lineGap; + FONSfont* font; + + int idx = fons__allocFont(stash); + if (idx == FONS_INVALID) + return FONS_INVALID; + + font = stash->fonts[idx]; + + strncpy(font->name, name, sizeof(font->name)); + font->name[sizeof(font->name)-1] = '\0'; + + // Init hash lookup. + for (i = 0; i < FONS_HASH_LUT_SIZE; ++i) + font->lut[i] = -1; + + // Read in the font data. + font->dataSize = dataSize; + font->data = data; + font->freeData = (unsigned char)freeData; + + // Init font + stash->nscratch = 0; + if (!fons__tt_loadFont(stash, &font->font, data, dataSize)) goto error; + + // Store normalized line height. The real line height is got + // by multiplying the lineh by font size. + fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap); + fh = ascent - descent; + font->ascender = (float)ascent / (float)fh; + font->descender = (float)descent / (float)fh; + font->lineh = (float)(fh + lineGap) / (float)fh; + + return idx; + +error: + fons__freeFont(font); + stash->nfonts--; + return FONS_INVALID; +} + +int fonsGetFontByName(FONScontext* s, const char* name) +{ + int i; + for (i = 0; i < s->nfonts; i++) { + if (strcmp(s->fonts[i]->name, name) == 0) + return i; + } + return FONS_INVALID; +} + + +static FONSglyph* fons__allocGlyph(FONSfont* font) +{ + if (font->nglyphs+1 > font->cglyphs) { + font->cglyphs = font->cglyphs == 0 ? 8 : font->cglyphs * 2; + font->glyphs = (FONSglyph*)realloc(font->glyphs, sizeof(FONSglyph) * font->cglyphs); + if (font->glyphs == NULL) return NULL; + } + font->nglyphs++; + return &font->glyphs[font->nglyphs-1]; +} + + +// Based on Exponential blur, Jani Huhtanen, 2006 + +#define APREC 16 +#define ZPREC 7 + +static void fons__blurCols(unsigned char* dst, int w, int h, int dstStride, int alpha) +{ + int x, y; + for (y = 0; y < h; y++) { + int z = 0; // force zero border + for (x = 1; x < w; x++) { + z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC; + dst[x] = (unsigned char)(z >> ZPREC); + } + dst[w-1] = 0; // force zero border + z = 0; + for (x = w-2; x >= 0; x--) { + z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC; + dst[x] = (unsigned char)(z >> ZPREC); + } + dst[0] = 0; // force zero border + dst += dstStride; + } +} + +static void fons__blurRows(unsigned char* dst, int w, int h, int dstStride, int alpha) +{ + int x, y; + for (x = 0; x < w; x++) { + int z = 0; // force zero border + for (y = dstStride; y < h*dstStride; y += dstStride) { + z += (alpha * (((int)(dst[y]) << ZPREC) - z)) >> APREC; + dst[y] = (unsigned char)(z >> ZPREC); + } + dst[(h-1)*dstStride] = 0; // force zero border + z = 0; + for (y = (h-2)*dstStride; y >= 0; y -= dstStride) { + z += (alpha * (((int)(dst[y]) << ZPREC) - z)) >> APREC; + dst[y] = (unsigned char)(z >> ZPREC); + } + dst[0] = 0; // force zero border + dst++; + } +} + + +static void fons__blur(FONScontext* stash, unsigned char* dst, int w, int h, int dstStride, int blur) +{ + int alpha; + float sigma; + (void)stash; + + if (blur < 1) + return; + // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity) + sigma = (float)blur * 0.57735f; // 1 / sqrt(3) + alpha = (int)((1< 20) iblur = 20; + pad = iblur+2; + + // Reset allocator. + stash->nscratch = 0; + + // Find code point and size. + h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1); + i = font->lut[h]; + while (i != -1) { + if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur) + return &font->glyphs[i]; + i = font->glyphs[i].next; + } + + // Could not find glyph, create it. + g = fons__tt_getGlyphIndex(&font->font, codepoint); + // Try to find the glyph in fallback fonts. + if (g == 0) { + for (i = 0; i < font->nfallbacks; ++i) { + FONSfont* fallbackFont = stash->fonts[font->fallbacks[i]]; + int fallbackIndex = fons__tt_getGlyphIndex(&fallbackFont->font, codepoint); + if (fallbackIndex != 0) { + g = fallbackIndex; + renderFont = fallbackFont; + break; + } + } + // It is possible that we did not find a fallback glyph. + // In that case the glyph index 'g' is 0, and we'll proceed below and cache empty glyph. + } + scale = fons__tt_getPixelHeightScale(&renderFont->font, size); + fons__tt_buildGlyphBitmap(&renderFont->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1); + gw = x1-x0 + pad*2; + gh = y1-y0 + pad*2; + + // Find free spot for the rect in the atlas + added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); + if (added == 0 && stash->handleError != NULL) { + // Atlas is full, let the user to resize the atlas (or not), and try again. + stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0); + added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); + } + if (added == 0) return NULL; + + // Init glyph. + glyph = fons__allocGlyph(font); + glyph->codepoint = codepoint; + glyph->size = isize; + glyph->blur = iblur; + glyph->index = g; + glyph->x0 = (short)gx; + glyph->y0 = (short)gy; + glyph->x1 = (short)(glyph->x0+gw); + glyph->y1 = (short)(glyph->y0+gh); + glyph->xadv = (short)(scale * advance * 10.0f); + glyph->xoff = (short)(x0 - pad); + glyph->yoff = (short)(y0 - pad); + glyph->next = 0; + + // Insert char to hash lookup. + glyph->next = font->lut[h]; + font->lut[h] = font->nglyphs-1; + + // Rasterize + dst = &stash->texData[(glyph->x0+pad) + (glyph->y0+pad) * stash->params.width]; + fons__tt_renderGlyphBitmap(&renderFont->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale,scale, g); + + // Make sure there is one pixel empty border. + dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; + for (y = 0; y < gh; y++) { + dst[y*stash->params.width] = 0; + dst[gw-1 + y*stash->params.width] = 0; + } + for (x = 0; x < gw; x++) { + dst[x] = 0; + dst[x + (gh-1)*stash->params.width] = 0; + } + + // Debug code to color the glyph background +/* unsigned char* fdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; + for (y = 0; y < gh; y++) { + for (x = 0; x < gw; x++) { + int a = (int)fdst[x+y*stash->params.width] + 20; + if (a > 255) a = 255; + fdst[x+y*stash->params.width] = a; + } + }*/ + + // Blur + if (iblur > 0) { + stash->nscratch = 0; + bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; + fons__blur(stash, bdst, gw,gh, stash->params.width, iblur); + } + + stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0); + stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], glyph->y0); + stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], glyph->x1); + stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], glyph->y1); + + return glyph; +} + +static void fons__getQuad(FONScontext* stash, FONSfont* font, + int prevGlyphIndex, FONSglyph* glyph, + float scale, float spacing, float* x, float* y, FONSquad* q) +{ + float rx,ry,xoff,yoff,x0,y0,x1,y1; + + if (prevGlyphIndex != -1) { + float adv = fons__tt_getGlyphKernAdvance(&font->font, prevGlyphIndex, glyph->index) * scale; + *x += (int)(adv + spacing + 0.5f); + } + + // Each glyph has 2px border to allow good interpolation, + // one pixel to prevent leaking, and one to allow good interpolation for rendering. + // Inset the texture region by one pixel for correct interpolation. + xoff = (short)(glyph->xoff+1); + yoff = (short)(glyph->yoff+1); + x0 = (float)(glyph->x0+1); + y0 = (float)(glyph->y0+1); + x1 = (float)(glyph->x1-1); + y1 = (float)(glyph->y1-1); + + if (stash->params.flags & FONS_ZERO_TOPLEFT) { + rx = (float)(int)(*x + xoff); + ry = (float)(int)(*y + yoff); + + q->x0 = rx; + q->y0 = ry; + q->x1 = rx + x1 - x0; + q->y1 = ry + y1 - y0; + + q->s0 = x0 * stash->itw; + q->t0 = y0 * stash->ith; + q->s1 = x1 * stash->itw; + q->t1 = y1 * stash->ith; + } else { + rx = (float)(int)(*x + xoff); + ry = (float)(int)(*y - yoff); + + q->x0 = rx; + q->y0 = ry; + q->x1 = rx + x1 - x0; + q->y1 = ry - y1 + y0; + + q->s0 = x0 * stash->itw; + q->t0 = y0 * stash->ith; + q->s1 = x1 * stash->itw; + q->t1 = y1 * stash->ith; + } + + *x += (int)(glyph->xadv / 10.0f + 0.5f); +} + +static void fons__flush(FONScontext* stash) +{ + // Flush texture + if (stash->dirtyRect[0] < stash->dirtyRect[2] && stash->dirtyRect[1] < stash->dirtyRect[3]) { + if (stash->params.renderUpdate != NULL) + stash->params.renderUpdate(stash->params.userPtr, stash->dirtyRect, stash->texData); + // Reset dirty rect + stash->dirtyRect[0] = stash->params.width; + stash->dirtyRect[1] = stash->params.height; + stash->dirtyRect[2] = 0; + stash->dirtyRect[3] = 0; + } + + // Flush triangles + if (stash->nverts > 0) { + if (stash->params.renderDraw != NULL) + stash->params.renderDraw(stash->params.userPtr, stash->verts, stash->tcoords, stash->colors, stash->nverts); + stash->nverts = 0; + } +} + +static __inline void fons__vertex(FONScontext* stash, float x, float y, float s, float t, unsigned int c) +{ + stash->verts[stash->nverts*2+0] = x; + stash->verts[stash->nverts*2+1] = y; + stash->tcoords[stash->nverts*2+0] = s; + stash->tcoords[stash->nverts*2+1] = t; + stash->colors[stash->nverts] = c; + stash->nverts++; +} + +static float fons__getVertAlign(FONScontext* stash, FONSfont* font, int align, short isize) +{ + if (stash->params.flags & FONS_ZERO_TOPLEFT) { + if (align & FONS_ALIGN_TOP) { + return font->ascender * (float)isize/10.0f; + } else if (align & FONS_ALIGN_MIDDLE) { + return (font->ascender + font->descender) / 2.0f * (float)isize/10.0f; + } else if (align & FONS_ALIGN_BASELINE) { + return 0.0f; + } else if (align & FONS_ALIGN_BOTTOM) { + return font->descender * (float)isize/10.0f; + } + } else { + if (align & FONS_ALIGN_TOP) { + return -font->ascender * (float)isize/10.0f; + } else if (align & FONS_ALIGN_MIDDLE) { + return -(font->ascender + font->descender) / 2.0f * (float)isize/10.0f; + } else if (align & FONS_ALIGN_BASELINE) { + return 0.0f; + } else if (align & FONS_ALIGN_BOTTOM) { + return -font->descender * (float)isize/10.0f; + } + } + return 0.0; +} + +FONS_DEF float fonsDrawText(FONScontext* stash, + float x, float y, + const char* str, const char* end) +{ + FONSstate* state = fons__getState(stash); + unsigned int codepoint; + unsigned int utf8state = 0; + FONSglyph* glyph = NULL; + FONSquad q; + int prevGlyphIndex = -1; + short isize = (short)(state->size*10.0f); + short iblur = (short)state->blur; + float scale; + FONSfont* font; + float width; + + if (stash == NULL) return x; + if (state->font < 0 || state->font >= stash->nfonts) return x; + font = stash->fonts[state->font]; + if (font->data == NULL) return x; + + scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f); + + if (end == NULL) + end = str + strlen(str); + + // Align horizontally + if (state->align & FONS_ALIGN_LEFT) { + // empty + } else if (state->align & FONS_ALIGN_RIGHT) { + width = fonsTextBounds(stash, x,y, str, end, NULL); + x -= width; + } else if (state->align & FONS_ALIGN_CENTER) { + width = fonsTextBounds(stash, x,y, str, end, NULL); + x -= width * 0.5f; + } + // Align vertically. + y += fons__getVertAlign(stash, font, state->align, isize); + + for (; str != end; ++str) { + if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) + continue; + glyph = fons__getGlyph(stash, font, codepoint, isize, iblur); + if (glyph != NULL) { + fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); + + if (stash->nverts+6 > FONS_VERTEX_COUNT) + fons__flush(stash); + + fons__vertex(stash, q.x0, q.y0, q.s0, q.t0, state->color); + fons__vertex(stash, q.x1, q.y1, q.s1, q.t1, state->color); + fons__vertex(stash, q.x1, q.y0, q.s1, q.t0, state->color); + + fons__vertex(stash, q.x0, q.y0, q.s0, q.t0, state->color); + fons__vertex(stash, q.x0, q.y1, q.s0, q.t1, state->color); + fons__vertex(stash, q.x1, q.y1, q.s1, q.t1, state->color); + } + prevGlyphIndex = glyph != NULL ? glyph->index : -1; + } + fons__flush(stash); + + return x; +} + +FONS_DEF int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, + float x, float y, const char* str, const char* end) +{ + FONSstate* state = fons__getState(stash); + float width; + + memset(iter, 0, sizeof(*iter)); + + if (stash == NULL) return 0; + if (state->font < 0 || state->font >= stash->nfonts) return 0; + iter->font = stash->fonts[state->font]; + if (iter->font->data == NULL) return 0; + + iter->isize = (short)(state->size*10.0f); + iter->iblur = (short)state->blur; + iter->scale = fons__tt_getPixelHeightScale(&iter->font->font, (float)iter->isize/10.0f); + + // Align horizontally + if (state->align & FONS_ALIGN_LEFT) { + // empty + } else if (state->align & FONS_ALIGN_RIGHT) { + width = fonsTextBounds(stash, x,y, str, end, NULL); + x -= width; + } else if (state->align & FONS_ALIGN_CENTER) { + width = fonsTextBounds(stash, x,y, str, end, NULL); + x -= width * 0.5f; + } + // Align vertically. + y += fons__getVertAlign(stash, iter->font, state->align, iter->isize); + + if (end == NULL) + end = str + strlen(str); + + iter->x = iter->nextx = x; + iter->y = iter->nexty = y; + iter->spacing = state->spacing; + iter->str = str; + iter->next = str; + iter->end = end; + iter->codepoint = 0; + iter->prevGlyphIndex = -1; + + return 1; +} + +FONS_DEF int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, FONSquad* quad) +{ + FONSglyph* glyph = NULL; + const char* str = iter->next; + iter->str = iter->next; + + if (str == iter->end) + return 0; + + for (; str != iter->end; str++) { + if (fons__decutf8(&iter->utf8state, &iter->codepoint, *(const unsigned char*)str)) + continue; + str++; + // Get glyph and quad + iter->x = iter->nextx; + iter->y = iter->nexty; + glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur); + if (glyph != NULL) + fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad); + iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1; + break; + } + iter->next = str; + + return 1; +} + +FONS_DEF void fonsDrawDebug(FONScontext* stash, float x, float y) +{ + int i; + int w = stash->params.width; + int h = stash->params.height; + float u = w == 0 ? 0 : (1.0f / w); + float v = h == 0 ? 0 : (1.0f / h); + + if (stash->nverts+6+6 > FONS_VERTEX_COUNT) + fons__flush(stash); + + // Draw background + fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff); + fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff); + fons__vertex(stash, x+w, y+0, u, v, 0x0fffffff); + + fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff); + fons__vertex(stash, x+0, y+h, u, v, 0x0fffffff); + fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff); + + // Draw texture + fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff); + fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff); + fons__vertex(stash, x+w, y+0, 1, 0, 0xffffffff); + + fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff); + fons__vertex(stash, x+0, y+h, 0, 1, 0xffffffff); + fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff); + + // Drawbug draw atlas + for (i = 0; i < stash->atlas->nnodes; i++) { + FONSatlasNode* n = &stash->atlas->nodes[i]; + + if (stash->nverts+6 > FONS_VERTEX_COUNT) + fons__flush(stash); + + fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc00000ff); + fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc00000ff); + fons__vertex(stash, x+n->x+n->width, y+n->y+0, u, v, 0xc00000ff); + + fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc00000ff); + fons__vertex(stash, x+n->x+0, y+n->y+1, u, v, 0xc00000ff); + fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc00000ff); + } + + fons__flush(stash); +} + +FONS_DEF float fonsTextBounds(FONScontext* stash, + float x, float y, + const char* str, const char* end, + float* bounds) +{ + FONSstate* state = fons__getState(stash); + unsigned int codepoint; + unsigned int utf8state = 0; + FONSquad q; + FONSglyph* glyph = NULL; + int prevGlyphIndex = -1; + short isize = (short)(state->size*10.0f); + short iblur = (short)state->blur; + float scale; + FONSfont* font; + float startx, advance; + float minx, miny, maxx, maxy; + + if (stash == NULL) return 0; + if (state->font < 0 || state->font >= stash->nfonts) return 0; + font = stash->fonts[state->font]; + if (font->data == NULL) return 0; + + scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f); + + // Align vertically. + y += fons__getVertAlign(stash, font, state->align, isize); + + minx = maxx = x; + miny = maxy = y; + startx = x; + + if (end == NULL) + end = str + strlen(str); + + for (; str != end; ++str) { + if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) + continue; + glyph = fons__getGlyph(stash, font, codepoint, isize, iblur); + if (glyph != NULL) { + fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); + if (q.x0 < minx) minx = q.x0; + if (q.x1 > maxx) maxx = q.x1; + if (stash->params.flags & FONS_ZERO_TOPLEFT) { + if (q.y0 < miny) miny = q.y0; + if (q.y1 > maxy) maxy = q.y1; + } else { + if (q.y1 < miny) miny = q.y1; + if (q.y0 > maxy) maxy = q.y0; + } + } + prevGlyphIndex = glyph != NULL ? glyph->index : -1; + } + + advance = x - startx; + + // Align horizontally + if (state->align & FONS_ALIGN_LEFT) { + // empty + } else if (state->align & FONS_ALIGN_RIGHT) { + minx -= advance; + maxx -= advance; + } else if (state->align & FONS_ALIGN_CENTER) { + minx -= advance * 0.5f; + maxx -= advance * 0.5f; + } + + if (bounds) { + bounds[0] = minx; + bounds[1] = miny; + bounds[2] = maxx; + bounds[3] = maxy; + } + + return advance; +} + +FONS_DEF void fonsVertMetrics(FONScontext* stash, + float* ascender, float* descender, float* lineh) +{ + FONSfont* font; + FONSstate* state = fons__getState(stash); + short isize; + + if (stash == NULL) return; + if (state->font < 0 || state->font >= stash->nfonts) return; + font = stash->fonts[state->font]; + isize = (short)(state->size*10.0f); + if (font->data == NULL) return; + + if (ascender) + *ascender = font->ascender*isize/10.0f; + if (descender) + *descender = font->descender*isize/10.0f; + if (lineh) + *lineh = font->lineh*isize/10.0f; +} + +FONS_DEF void fonsLineBounds(FONScontext* stash, float y, float* miny, float* maxy) +{ + FONSfont* font; + FONSstate* state = fons__getState(stash); + short isize; + + if (stash == NULL) return; + if (state->font < 0 || state->font >= stash->nfonts) return; + font = stash->fonts[state->font]; + isize = (short)(state->size*10.0f); + if (font->data == NULL) return; + + y += fons__getVertAlign(stash, font, state->align, isize); + + if (stash->params.flags & FONS_ZERO_TOPLEFT) { + *miny = y - font->ascender * (float)isize/10.0f; + *maxy = *miny + font->lineh*isize/10.0f; + } else { + *maxy = y + font->descender * (float)isize/10.0f; + *miny = *maxy - font->lineh*isize/10.0f; + } +} + +FONS_DEF const unsigned char* fonsGetTextureData(FONScontext* stash, int* width, int* height) +{ + if (width != NULL) + *width = stash->params.width; + if (height != NULL) + *height = stash->params.height; + return stash->texData; +} + +FONS_DEF int fonsValidateTexture(FONScontext* stash, int* dirty) +{ + if (stash->dirtyRect[0] < stash->dirtyRect[2] && stash->dirtyRect[1] < stash->dirtyRect[3]) { + dirty[0] = stash->dirtyRect[0]; + dirty[1] = stash->dirtyRect[1]; + dirty[2] = stash->dirtyRect[2]; + dirty[3] = stash->dirtyRect[3]; + // Reset dirty rect + stash->dirtyRect[0] = stash->params.width; + stash->dirtyRect[1] = stash->params.height; + stash->dirtyRect[2] = 0; + stash->dirtyRect[3] = 0; + return 1; + } + return 0; +} + +FONS_DEF void fonsDeleteInternal(FONScontext* stash) +{ + int i; + if (stash == NULL) return; + + if (stash->params.renderDelete) + stash->params.renderDelete(stash->params.userPtr); + + for (i = 0; i < stash->nfonts; ++i) + fons__freeFont(stash->fonts[i]); + + if (stash->atlas) fons__deleteAtlas(stash->atlas); + if (stash->fonts) free(stash->fonts); + if (stash->texData) free(stash->texData); + if (stash->scratch) free(stash->scratch); + free(stash); +} + +FONS_DEF void fonsSetErrorCallback(FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr) +{ + if (stash == NULL) return; + stash->handleError = callback; + stash->errorUptr = uptr; +} + +FONS_DEF void fonsGetAtlasSize(FONScontext* stash, int* width, int* height) +{ + if (stash == NULL) return; + *width = stash->params.width; + *height = stash->params.height; +} + +FONS_DEF int fonsExpandAtlas(FONScontext* stash, int width, int height) +{ + int i, maxy = 0; + unsigned char* data = NULL; + if (stash == NULL) return 0; + + width = fons__maxi(width, stash->params.width); + height = fons__maxi(height, stash->params.height); + + if (width == stash->params.width && height == stash->params.height) + return 1; + + // Flush pending glyphs. + fons__flush(stash); + + // Create new texture + if (stash->params.renderResize != NULL) { + if (stash->params.renderResize(stash->params.userPtr, width, height) == 0) + return 0; + } + // Copy old texture data over. + data = (unsigned char*)malloc(width * height); + if (data == NULL) + return 0; + for (i = 0; i < stash->params.height; i++) { + unsigned char* dst = &data[i*width]; + unsigned char* src = &stash->texData[i*stash->params.width]; + memcpy(dst, src, stash->params.width); + if (width > stash->params.width) + memset(dst+stash->params.width, 0, width - stash->params.width); + } + if (height > stash->params.height) + memset(&data[stash->params.height * width], 0, (height - stash->params.height) * width); + + free(stash->texData); + stash->texData = data; + + // Increase atlas size + fons__atlasExpand(stash->atlas, width, height); + + // Add existing data as dirty. + for (i = 0; i < stash->atlas->nnodes; i++) + maxy = fons__maxi(maxy, stash->atlas->nodes[i].y); + stash->dirtyRect[0] = 0; + stash->dirtyRect[1] = 0; + stash->dirtyRect[2] = stash->params.width; + stash->dirtyRect[3] = maxy; + + stash->params.width = width; + stash->params.height = height; + stash->itw = 1.0f/stash->params.width; + stash->ith = 1.0f/stash->params.height; + + return 1; +} + +FONS_DEF int fonsResetAtlas(FONScontext* stash, int width, int height) +{ + int i, j; + if (stash == NULL) return 0; + + // Flush pending glyphs. + fons__flush(stash); + + // Create new texture + if (stash->params.renderResize != NULL) { + if (stash->params.renderResize(stash->params.userPtr, width, height) == 0) + return 0; + } + + // Reset atlas + fons__atlasReset(stash->atlas, width, height); + + // Clear texture data. + stash->texData = (unsigned char*)realloc(stash->texData, width * height); + if (stash->texData == NULL) return 0; + memset(stash->texData, 0, width * height); + + // Reset dirty rect + stash->dirtyRect[0] = width; + stash->dirtyRect[1] = height; + stash->dirtyRect[2] = 0; + stash->dirtyRect[3] = 0; + + // Reset cached glyphs + for (i = 0; i < stash->nfonts; i++) { + FONSfont* font = stash->fonts[i]; + font->nglyphs = 0; + for (j = 0; j < FONS_HASH_LUT_SIZE; j++) + font->lut[j] = -1; + } + + stash->params.width = width; + stash->params.height = height; + stash->itw = 1.0f/stash->params.width; + stash->ith = 1.0f/stash->params.height; + + // Add white rect at 0,0 for debug drawing. + fons__addWhiteRect(stash, 2,2); + + return 1; +} + +#endif // FONTSTASH_IMPLEMENTATION diff --git a/thirdparty/sokol/tests/ext/nuklear.c b/thirdparty/sokol/tests/ext/nuklear.c new file mode 100644 index 0000000..52712f3 --- /dev/null +++ b/thirdparty/sokol/tests/ext/nuklear.c @@ -0,0 +1,33 @@ +// include nuklear.h before the sokol_nuklear.h implementation +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_IMPLEMENTATION + +#if defined(__clang__) +#pragma GCC diagnostic ignored "-Wunknown-warning-option" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wnull-pointer-subtraction" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#endif +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4996) // sprintf,fopen,localtime: This function or variable may be unsafe +#pragma warning(disable:4127) // conditional expression is constant +#pragma warning(disable:4100) // unreferenced formal parameter +#pragma warning(disable:4701) // potentially uninitialized local variable used +#endif +#include "nuklear.h" +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/thirdparty/sokol/tests/ext/nuklear.h b/thirdparty/sokol/tests/ext/nuklear.h new file mode 100644 index 0000000..6855f6f --- /dev/null +++ b/thirdparty/sokol/tests/ext/nuklear.h @@ -0,0 +1,29499 @@ +/* +/// # Nuklear +/// ![](https://cloud.githubusercontent.com/assets/8057201/11761525/ae06f0ca-a0c6-11e5-819d-5610b25f6ef4.gif) +/// +/// ## Contents +/// 1. About section +/// 2. Highlights section +/// 3. Features section +/// 4. Usage section +/// 1. Flags section +/// 2. Constants section +/// 3. Dependencies section +/// 5. Example section +/// 6. API section +/// 1. Context section +/// 2. Input section +/// 3. Drawing section +/// 4. Window section +/// 5. Layouting section +/// 6. Groups section +/// 7. Tree section +/// 8. Properties section +/// 7. License section +/// 8. Changelog section +/// 9. Gallery section +/// 10. Credits section +/// +/// ## About +/// This is a minimal state immediate mode graphical user interface toolkit +/// written in ANSI C and licensed under public domain. It was designed as a simple +/// embeddable user interface for application and does not have any dependencies, +/// a default renderbackend or OS window and input handling but instead provides a very modular +/// library approach by using simple input state for input and draw +/// commands describing primitive shapes as output. So instead of providing a +/// layered library that tries to abstract over a number of platform and +/// render backends it only focuses on the actual UI. +/// +/// ## Highlights +/// - Graphical user interface toolkit +/// - Single header library +/// - Written in C89 (a.k.a. ANSI C or ISO C90) +/// - Small codebase (~18kLOC) +/// - Focus on portability, efficiency and simplicity +/// - No dependencies (not even the standard library if not wanted) +/// - Fully skinnable and customizable +/// - Low memory footprint with total memory control if needed or wanted +/// - UTF-8 support +/// - No global or hidden state +/// - Customizable library modules (you can compile and use only what you need) +/// - Optional font baker and vertex buffer output +/// +/// ## Features +/// - Absolutely no platform dependent code +/// - Memory management control ranging from/to +/// - Ease of use by allocating everything from standard library +/// - Control every byte of memory inside the library +/// - Font handling control ranging from/to +/// - Use your own font implementation for everything +/// - Use this libraries internal font baking and handling API +/// - Drawing output control ranging from/to +/// - Simple shapes for more high level APIs which already have drawing capabilities +/// - Hardware accessible anti-aliased vertex buffer output +/// - Customizable colors and properties ranging from/to +/// - Simple changes to color by filling a simple color table +/// - Complete control with ability to use skinning to decorate widgets +/// - Bendable UI library with widget ranging from/to +/// - Basic widgets like buttons, checkboxes, slider, ... +/// - Advanced widget like abstract comboboxes, contextual menus,... +/// - Compile time configuration to only compile what you need +/// - Subset which can be used if you do not want to link or use the standard library +/// - Can be easily modified to only update on user input instead of frame updates +/// +/// ## Usage +/// This library is self contained in one single header file and can be used either +/// in header only mode or in implementation mode. The header only mode is used +/// by default when included and allows including this header in other headers +/// and does not contain the actual implementation.

+/// +/// The implementation mode requires to define the preprocessor macro +/// NK_IMPLEMENTATION in *one* .c/.cpp file before #includeing this file, e.g.: +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~C +/// #define NK_IMPLEMENTATION +/// #include "nuklear.h" +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Also optionally define the symbols listed in the section "OPTIONAL DEFINES" +/// below in header and implementation mode if you want to use additional functionality +/// or need more control over the library. +/// +/// !!! WARNING +/// Every time nuklear is included define the same compiler flags. This very important not doing so could lead to compiler errors or even worse stack corruptions. +/// +/// ### Flags +/// Flag | Description +/// --------------------------------|------------------------------------------ +/// NK_PRIVATE | If defined declares all functions as static, so they can only be accessed inside the file that contains the implementation +/// NK_INCLUDE_FIXED_TYPES | If defined it will include header `` for fixed sized types otherwise nuklear tries to select the correct type. If that fails it will throw a compiler error and you have to select the correct types yourself. +/// NK_INCLUDE_DEFAULT_ALLOCATOR | If defined it will include header `` and provide additional functions to use this library without caring for memory allocation control and therefore ease memory management. +/// NK_INCLUDE_STANDARD_IO | If defined it will include header `` and provide additional functions depending on file loading. +/// NK_INCLUDE_STANDARD_VARARGS | If defined it will include header and provide additional functions depending on file loading. +/// NK_INCLUDE_STANDARD_BOOL | If defined it will include header `` for nk_bool otherwise nuklear defines nk_bool as int. +/// NK_INCLUDE_VERTEX_BUFFER_OUTPUT | Defining this adds a vertex draw command list backend to this library, which allows you to convert queue commands into vertex draw commands. This is mainly if you need a hardware accessible format for OpenGL, DirectX, Vulkan, Metal,... +/// NK_INCLUDE_FONT_BAKING | Defining this adds `stb_truetype` and `stb_rect_pack` implementation to this library and provides font baking and rendering. If you already have font handling or do not want to use this font handler you don't have to define it. +/// NK_INCLUDE_DEFAULT_FONT | Defining this adds the default font: ProggyClean.ttf into this library which can be loaded into a font atlas and allows using this library without having a truetype font +/// NK_INCLUDE_COMMAND_USERDATA | Defining this adds a userdata pointer into each command. Can be useful for example if you want to provide custom shaders depending on the used widget. Can be combined with the style structures. +/// NK_BUTTON_TRIGGER_ON_RELEASE | Different platforms require button clicks occurring either on buttons being pressed (up to down) or released (down to up). By default this library will react on buttons being pressed, but if you define this it will only trigger if a button is released. +/// NK_ZERO_COMMAND_MEMORY | Defining this will zero out memory for each drawing command added to a drawing queue (inside nk_command_buffer_push). Zeroing command memory is very useful for fast checking (using memcmp) if command buffers are equal and avoid drawing frames when nothing on screen has changed since previous frame. +/// NK_UINT_DRAW_INDEX | Defining this will set the size of vertex index elements when using NK_VERTEX_BUFFER_OUTPUT to 32bit instead of the default of 16bit +/// NK_KEYSTATE_BASED_INPUT | Define this if your backend uses key state for each frame rather than key press/release events +/// +/// !!! WARNING +/// The following flags will pull in the standard C library: +/// - NK_INCLUDE_DEFAULT_ALLOCATOR +/// - NK_INCLUDE_STANDARD_IO +/// - NK_INCLUDE_STANDARD_VARARGS +/// +/// !!! WARNING +/// The following flags if defined need to be defined for both header and implementation: +/// - NK_INCLUDE_FIXED_TYPES +/// - NK_INCLUDE_DEFAULT_ALLOCATOR +/// - NK_INCLUDE_STANDARD_VARARGS +/// - NK_INCLUDE_STANDARD_BOOL +/// - NK_INCLUDE_VERTEX_BUFFER_OUTPUT +/// - NK_INCLUDE_FONT_BAKING +/// - NK_INCLUDE_DEFAULT_FONT +/// - NK_INCLUDE_STANDARD_VARARGS +/// - NK_INCLUDE_COMMAND_USERDATA +/// - NK_UINT_DRAW_INDEX +/// +/// ### Constants +/// Define | Description +/// --------------------------------|--------------------------------------- +/// NK_BUFFER_DEFAULT_INITIAL_SIZE | Initial buffer size allocated by all buffers while using the default allocator functions included by defining NK_INCLUDE_DEFAULT_ALLOCATOR. If you don't want to allocate the default 4k memory then redefine it. +/// NK_MAX_NUMBER_BUFFER | Maximum buffer size for the conversion buffer between float and string Under normal circumstances this should be more than sufficient. +/// NK_INPUT_MAX | Defines the max number of bytes which can be added as text input in one frame. Under normal circumstances this should be more than sufficient. +/// +/// !!! WARNING +/// The following constants if defined need to be defined for both header and implementation: +/// - NK_MAX_NUMBER_BUFFER +/// - NK_BUFFER_DEFAULT_INITIAL_SIZE +/// - NK_INPUT_MAX +/// +/// ### Dependencies +/// Function | Description +/// ------------|--------------------------------------------------------------- +/// NK_ASSERT | If you don't define this, nuklear will use with assert(). +/// NK_MEMSET | You can define this to 'memset' or your own memset implementation replacement. If not nuklear will use its own version. +/// NK_MEMCPY | You can define this to 'memcpy' or your own memcpy implementation replacement. If not nuklear will use its own version. +/// NK_SQRT | You can define this to 'sqrt' or your own sqrt implementation replacement. If not nuklear will use its own slow and not highly accurate version. +/// NK_SIN | You can define this to 'sinf' or your own sine implementation replacement. If not nuklear will use its own approximation implementation. +/// NK_COS | You can define this to 'cosf' or your own cosine implementation replacement. If not nuklear will use its own approximation implementation. +/// NK_STRTOD | You can define this to `strtod` or your own string to double conversion implementation replacement. If not defined nuklear will use its own imprecise and possibly unsafe version (does not handle nan or infinity!). +/// NK_DTOA | You can define this to `dtoa` or your own double to string conversion implementation replacement. If not defined nuklear will use its own imprecise and possibly unsafe version (does not handle nan or infinity!). +/// NK_VSNPRINTF| If you define `NK_INCLUDE_STANDARD_VARARGS` as well as `NK_INCLUDE_STANDARD_IO` and want to be safe define this to `vsnprintf` on compilers supporting later versions of C or C++. By default nuklear will check for your stdlib version in C as well as compiler version in C++. if `vsnprintf` is available it will define it to `vsnprintf` directly. If not defined and if you have older versions of C or C++ it will be defined to `vsprintf` which is unsafe. +/// +/// !!! WARNING +/// The following dependencies will pull in the standard C library if not redefined: +/// - NK_ASSERT +/// +/// !!! WARNING +/// The following dependencies if defined need to be defined for both header and implementation: +/// - NK_ASSERT +/// +/// !!! WARNING +/// The following dependencies if defined need to be defined only for the implementation part: +/// - NK_MEMSET +/// - NK_MEMCPY +/// - NK_SQRT +/// - NK_SIN +/// - NK_COS +/// - NK_STRTOD +/// - NK_DTOA +/// - NK_VSNPRINTF +/// +/// ## Example +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// // init gui state +/// enum {EASY, HARD}; +/// static int op = EASY; +/// static float value = 0.6f; +/// static int i = 20; +/// struct nk_context ctx; +/// +/// nk_init_fixed(&ctx, calloc(1, MAX_MEMORY), MAX_MEMORY, &font); +/// if (nk_begin(&ctx, "Show", nk_rect(50, 50, 220, 220), +/// NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_CLOSABLE)) { +/// // fixed widget pixel width +/// nk_layout_row_static(&ctx, 30, 80, 1); +/// if (nk_button_label(&ctx, "button")) { +/// // event handling +/// } +/// +/// // fixed widget window ratio width +/// nk_layout_row_dynamic(&ctx, 30, 2); +/// if (nk_option_label(&ctx, "easy", op == EASY)) op = EASY; +/// if (nk_option_label(&ctx, "hard", op == HARD)) op = HARD; +/// +/// // custom widget pixel width +/// nk_layout_row_begin(&ctx, NK_STATIC, 30, 2); +/// { +/// nk_layout_row_push(&ctx, 50); +/// nk_label(&ctx, "Volume:", NK_TEXT_LEFT); +/// nk_layout_row_push(&ctx, 110); +/// nk_slider_float(&ctx, 0, &value, 1.0f, 0.1f); +/// } +/// nk_layout_row_end(&ctx); +/// } +/// nk_end(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// ![](https://cloud.githubusercontent.com/assets/8057201/10187981/584ecd68-675c-11e5-897c-822ef534a876.png) +/// +/// ## API +/// +*/ +#ifndef NK_SINGLE_FILE + #define NK_SINGLE_FILE +#endif + +#ifndef NK_NUKLEAR_H_ +#define NK_NUKLEAR_H_ + +#ifdef __cplusplus +extern "C" { +#endif +/* + * ============================================================== + * + * CONSTANTS + * + * =============================================================== + */ +#define NK_UNDEFINED (-1.0f) +#define NK_UTF_INVALID 0xFFFD /* internal invalid utf8 rune */ +#define NK_UTF_SIZE 4 /* describes the number of bytes a glyph consists of*/ +#ifndef NK_INPUT_MAX + #define NK_INPUT_MAX 16 +#endif +#ifndef NK_MAX_NUMBER_BUFFER + #define NK_MAX_NUMBER_BUFFER 64 +#endif +#ifndef NK_SCROLLBAR_HIDING_TIMEOUT + #define NK_SCROLLBAR_HIDING_TIMEOUT 4.0f +#endif +/* + * ============================================================== + * + * HELPER + * + * =============================================================== + */ +#ifndef NK_API + #ifdef NK_PRIVATE + #if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199409L)) + #define NK_API static inline + #elif defined(__cplusplus) + #define NK_API static inline + #else + #define NK_API static + #endif + #else + #define NK_API extern + #endif +#endif +#ifndef NK_LIB + #ifdef NK_SINGLE_FILE + #define NK_LIB static + #else + #define NK_LIB extern + #endif +#endif + +#define NK_INTERN static +#define NK_STORAGE static +#define NK_GLOBAL static + +#define NK_FLAG(x) (1 << (x)) +#define NK_STRINGIFY(x) #x +#define NK_MACRO_STRINGIFY(x) NK_STRINGIFY(x) +#define NK_STRING_JOIN_IMMEDIATE(arg1, arg2) arg1 ## arg2 +#define NK_STRING_JOIN_DELAY(arg1, arg2) NK_STRING_JOIN_IMMEDIATE(arg1, arg2) +#define NK_STRING_JOIN(arg1, arg2) NK_STRING_JOIN_DELAY(arg1, arg2) + +#ifdef _MSC_VER + #define NK_UNIQUE_NAME(name) NK_STRING_JOIN(name,__COUNTER__) +#else + #define NK_UNIQUE_NAME(name) NK_STRING_JOIN(name,__LINE__) +#endif + +#ifndef NK_STATIC_ASSERT + #define NK_STATIC_ASSERT(exp) typedef char NK_UNIQUE_NAME(_dummy_array)[(exp)?1:-1] +#endif + +#ifndef NK_FILE_LINE +#ifdef _MSC_VER + #define NK_FILE_LINE __FILE__ ":" NK_MACRO_STRINGIFY(__COUNTER__) +#else + #define NK_FILE_LINE __FILE__ ":" NK_MACRO_STRINGIFY(__LINE__) +#endif +#endif + +#define NK_MIN(a,b) ((a) < (b) ? (a) : (b)) +#define NK_MAX(a,b) ((a) < (b) ? (b) : (a)) +#define NK_CLAMP(i,v,x) (NK_MAX(NK_MIN(v,x), i)) + +#ifdef NK_INCLUDE_STANDARD_VARARGS + #include + #if defined(_MSC_VER) && (_MSC_VER >= 1600) /* VS 2010 and above */ + #include + #define NK_PRINTF_FORMAT_STRING _Printf_format_string_ + #else + #define NK_PRINTF_FORMAT_STRING + #endif + #if defined(__GNUC__) + #define NK_PRINTF_VARARG_FUNC(fmtargnumber) __attribute__((format(__printf__, fmtargnumber, fmtargnumber+1))) + #define NK_PRINTF_VALIST_FUNC(fmtargnumber) __attribute__((format(__printf__, fmtargnumber, 0))) + #else + #define NK_PRINTF_VARARG_FUNC(fmtargnumber) + #define NK_PRINTF_VALIST_FUNC(fmtargnumber) + #endif +#endif + +/* + * =============================================================== + * + * BASIC + * + * =============================================================== + */ +#ifdef NK_INCLUDE_FIXED_TYPES + #include + #define NK_INT8 int8_t + #define NK_UINT8 uint8_t + #define NK_INT16 int16_t + #define NK_UINT16 uint16_t + #define NK_INT32 int32_t + #define NK_UINT32 uint32_t + #define NK_SIZE_TYPE uintptr_t + #define NK_POINTER_TYPE uintptr_t +#else + #ifndef NK_INT8 + #define NK_INT8 signed char + #endif + #ifndef NK_UINT8 + #define NK_UINT8 unsigned char + #endif + #ifndef NK_INT16 + #define NK_INT16 signed short + #endif + #ifndef NK_UINT16 + #define NK_UINT16 unsigned short + #endif + #ifndef NK_INT32 + #if defined(_MSC_VER) + #define NK_INT32 __int32 + #else + #define NK_INT32 signed int + #endif + #endif + #ifndef NK_UINT32 + #if defined(_MSC_VER) + #define NK_UINT32 unsigned __int32 + #else + #define NK_UINT32 unsigned int + #endif + #endif + #ifndef NK_SIZE_TYPE + #if defined(_WIN64) && defined(_MSC_VER) + #define NK_SIZE_TYPE unsigned __int64 + #elif (defined(_WIN32) || defined(WIN32)) && defined(_MSC_VER) + #define NK_SIZE_TYPE unsigned __int32 + #elif defined(__GNUC__) || defined(__clang__) + #if defined(__x86_64__) || defined(__ppc64__) + #define NK_SIZE_TYPE unsigned long + #else + #define NK_SIZE_TYPE unsigned int + #endif + #else + #define NK_SIZE_TYPE unsigned long + #endif + #endif + #ifndef NK_POINTER_TYPE + #if defined(_WIN64) && defined(_MSC_VER) + #define NK_POINTER_TYPE unsigned __int64 + #elif (defined(_WIN32) || defined(WIN32)) && defined(_MSC_VER) + #define NK_POINTER_TYPE unsigned __int32 + #elif defined(__GNUC__) || defined(__clang__) + #if defined(__x86_64__) || defined(__ppc64__) + #define NK_POINTER_TYPE unsigned long + #else + #define NK_POINTER_TYPE unsigned int + #endif + #else + #define NK_POINTER_TYPE unsigned long + #endif + #endif +#endif + +#ifndef NK_BOOL + #ifdef NK_INCLUDE_STANDARD_BOOL + #include + #define NK_BOOL bool + #else + #define NK_BOOL int /* could be char, use int for drop-in replacement backwards compatibility */ + #endif +#endif + +typedef NK_INT8 nk_char; +typedef NK_UINT8 nk_uchar; +typedef NK_UINT8 nk_byte; +typedef NK_INT16 nk_short; +typedef NK_UINT16 nk_ushort; +typedef NK_INT32 nk_int; +typedef NK_UINT32 nk_uint; +typedef NK_SIZE_TYPE nk_size; +typedef NK_POINTER_TYPE nk_ptr; +typedef NK_BOOL nk_bool; + +typedef nk_uint nk_hash; +typedef nk_uint nk_flags; +typedef nk_uint nk_rune; + +/* Make sure correct type size: + * This will fire with a negative subscript error if the type sizes + * are set incorrectly by the compiler, and compile out if not */ +NK_STATIC_ASSERT(sizeof(nk_short) == 2); +NK_STATIC_ASSERT(sizeof(nk_ushort) == 2); +NK_STATIC_ASSERT(sizeof(nk_uint) == 4); +NK_STATIC_ASSERT(sizeof(nk_int) == 4); +NK_STATIC_ASSERT(sizeof(nk_byte) == 1); +NK_STATIC_ASSERT(sizeof(nk_flags) >= 4); +NK_STATIC_ASSERT(sizeof(nk_rune) >= 4); +NK_STATIC_ASSERT(sizeof(nk_size) >= sizeof(void*)); +NK_STATIC_ASSERT(sizeof(nk_ptr) >= sizeof(void*)); +#ifdef NK_INCLUDE_STANDARD_BOOL +NK_STATIC_ASSERT(sizeof(nk_bool) == sizeof(bool)); +#else +NK_STATIC_ASSERT(sizeof(nk_bool) >= 2); +#endif + +/* ============================================================================ + * + * API + * + * =========================================================================== */ +struct nk_buffer; +struct nk_allocator; +struct nk_command_buffer; +struct nk_draw_command; +struct nk_convert_config; +struct nk_style_item; +struct nk_text_edit; +struct nk_draw_list; +struct nk_user_font; +struct nk_panel; +struct nk_context; +struct nk_draw_vertex_layout_element; +struct nk_style_button; +struct nk_style_toggle; +struct nk_style_selectable; +struct nk_style_slide; +struct nk_style_progress; +struct nk_style_scrollbar; +struct nk_style_edit; +struct nk_style_property; +struct nk_style_chart; +struct nk_style_combo; +struct nk_style_tab; +struct nk_style_window_header; +struct nk_style_window; + +enum {nk_false, nk_true}; +struct nk_color {nk_byte r,g,b,a;}; +struct nk_colorf {float r,g,b,a;}; +struct nk_vec2 {float x,y;}; +struct nk_vec2i {short x, y;}; +struct nk_rect {float x,y,w,h;}; +struct nk_recti {short x,y,w,h;}; +typedef char nk_glyph[NK_UTF_SIZE]; +typedef union {void *ptr; int id;} nk_handle; +struct nk_image {nk_handle handle;unsigned short w,h;unsigned short region[4];}; +struct nk_cursor {struct nk_image img; struct nk_vec2 size, offset;}; +struct nk_scroll {nk_uint x, y;}; + +enum nk_heading {NK_UP, NK_RIGHT, NK_DOWN, NK_LEFT}; +enum nk_button_behavior {NK_BUTTON_DEFAULT, NK_BUTTON_REPEATER}; +enum nk_modify {NK_FIXED = nk_false, NK_MODIFIABLE = nk_true}; +enum nk_orientation {NK_VERTICAL, NK_HORIZONTAL}; +enum nk_collapse_states {NK_MINIMIZED = nk_false, NK_MAXIMIZED = nk_true}; +enum nk_show_states {NK_HIDDEN = nk_false, NK_SHOWN = nk_true}; +enum nk_chart_type {NK_CHART_LINES, NK_CHART_COLUMN, NK_CHART_MAX}; +enum nk_chart_event {NK_CHART_HOVERING = 0x01, NK_CHART_CLICKED = 0x02}; +enum nk_color_format {NK_RGB, NK_RGBA}; +enum nk_popup_type {NK_POPUP_STATIC, NK_POPUP_DYNAMIC}; +enum nk_layout_format {NK_DYNAMIC, NK_STATIC}; +enum nk_tree_type {NK_TREE_NODE, NK_TREE_TAB}; + +typedef void*(*nk_plugin_alloc)(nk_handle, void *old, nk_size); +typedef void (*nk_plugin_free)(nk_handle, void *old); +typedef nk_bool(*nk_plugin_filter)(const struct nk_text_edit*, nk_rune unicode); +typedef void(*nk_plugin_paste)(nk_handle, struct nk_text_edit*); +typedef void(*nk_plugin_copy)(nk_handle, const char*, int len); + +struct nk_allocator { + nk_handle userdata; + nk_plugin_alloc alloc; + nk_plugin_free free; +}; +enum nk_symbol_type { + NK_SYMBOL_NONE, + NK_SYMBOL_X, + NK_SYMBOL_UNDERSCORE, + NK_SYMBOL_CIRCLE_SOLID, + NK_SYMBOL_CIRCLE_OUTLINE, + NK_SYMBOL_RECT_SOLID, + NK_SYMBOL_RECT_OUTLINE, + NK_SYMBOL_TRIANGLE_UP, + NK_SYMBOL_TRIANGLE_DOWN, + NK_SYMBOL_TRIANGLE_LEFT, + NK_SYMBOL_TRIANGLE_RIGHT, + NK_SYMBOL_PLUS, + NK_SYMBOL_MINUS, + NK_SYMBOL_MAX +}; +/* ============================================================================= + * + * CONTEXT + * + * =============================================================================*/ +/*/// ### Context +/// Contexts are the main entry point and the majestro of nuklear and contain all required state. +/// They are used for window, memory, input, style, stack, commands and time management and need +/// to be passed into all nuklear GUI specific functions. +/// +/// #### Usage +/// To use a context it first has to be initialized which can be achieved by calling +/// one of either `nk_init_default`, `nk_init_fixed`, `nk_init`, `nk_init_custom`. +/// Each takes in a font handle and a specific way of handling memory. Memory control +/// hereby ranges from standard library to just specifying a fixed sized block of memory +/// which nuklear has to manage itself from. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_context ctx; +/// nk_init_xxx(&ctx, ...); +/// while (1) { +/// // [...] +/// nk_clear(&ctx); +/// } +/// nk_free(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// #### Reference +/// Function | Description +/// --------------------|------------------------------------------------------- +/// __nk_init_default__ | Initializes context with standard library memory allocation (malloc,free) +/// __nk_init_fixed__ | Initializes context from single fixed size memory block +/// __nk_init__ | Initializes context with memory allocator callbacks for alloc and free +/// __nk_init_custom__ | Initializes context from two buffers. One for draw commands the other for window/panel/table allocations +/// __nk_clear__ | Called at the end of the frame to reset and prepare the context for the next frame +/// __nk_free__ | Shutdown and free all memory allocated inside the context +/// __nk_set_user_data__| Utility function to pass user data to draw command + */ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +/*/// #### nk_init_default +/// Initializes a `nk_context` struct with a default standard library allocator. +/// Should be used if you don't want to be bothered with memory management in nuklear. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_init_default(struct nk_context *ctx, const struct nk_user_font *font); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|--------------------------------------------------------------- +/// __ctx__ | Must point to an either stack or heap allocated `nk_context` struct +/// __font__ | Must point to a previously initialized font handle for more info look at font documentation +/// +/// Returns either `false(0)` on failure or `true(1)` on success. +/// +*/ +NK_API nk_bool nk_init_default(struct nk_context*, const struct nk_user_font*); +#endif +/*/// #### nk_init_fixed +/// Initializes a `nk_context` struct from single fixed size memory block +/// Should be used if you want complete control over nuklear's memory management. +/// Especially recommended for system with little memory or systems with virtual memory. +/// For the later case you can just allocate for example 16MB of virtual memory +/// and only the required amount of memory will actually be committed. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_init_fixed(struct nk_context *ctx, void *memory, nk_size size, const struct nk_user_font *font); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// !!! Warning +/// make sure the passed memory block is aligned correctly for `nk_draw_commands`. +/// +/// Parameter | Description +/// ------------|-------------------------------------------------------------- +/// __ctx__ | Must point to an either stack or heap allocated `nk_context` struct +/// __memory__ | Must point to a previously allocated memory block +/// __size__ | Must contain the total size of __memory__ +/// __font__ | Must point to a previously initialized font handle for more info look at font documentation +/// +/// Returns either `false(0)` on failure or `true(1)` on success. +*/ +NK_API nk_bool nk_init_fixed(struct nk_context*, void *memory, nk_size size, const struct nk_user_font*); +/*/// #### nk_init +/// Initializes a `nk_context` struct with memory allocation callbacks for nuklear to allocate +/// memory from. Used internally for `nk_init_default` and provides a kitchen sink allocation +/// interface to nuklear. Can be useful for cases like monitoring memory consumption. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_init(struct nk_context *ctx, struct nk_allocator *alloc, const struct nk_user_font *font); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|--------------------------------------------------------------- +/// __ctx__ | Must point to an either stack or heap allocated `nk_context` struct +/// __alloc__ | Must point to a previously allocated memory allocator +/// __font__ | Must point to a previously initialized font handle for more info look at font documentation +/// +/// Returns either `false(0)` on failure or `true(1)` on success. +*/ +NK_API nk_bool nk_init(struct nk_context*, struct nk_allocator*, const struct nk_user_font*); +/*/// #### nk_init_custom +/// Initializes a `nk_context` struct from two different either fixed or growing +/// buffers. The first buffer is for allocating draw commands while the second buffer is +/// used for allocating windows, panels and state tables. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_init_custom(struct nk_context *ctx, struct nk_buffer *cmds, struct nk_buffer *pool, const struct nk_user_font *font); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|--------------------------------------------------------------- +/// __ctx__ | Must point to an either stack or heap allocated `nk_context` struct +/// __cmds__ | Must point to a previously initialized memory buffer either fixed or dynamic to store draw commands into +/// __pool__ | Must point to a previously initialized memory buffer either fixed or dynamic to store windows, panels and tables +/// __font__ | Must point to a previously initialized font handle for more info look at font documentation +/// +/// Returns either `false(0)` on failure or `true(1)` on success. +*/ +NK_API nk_bool nk_init_custom(struct nk_context*, struct nk_buffer *cmds, struct nk_buffer *pool, const struct nk_user_font*); +/*/// #### nk_clear +/// Resets the context state at the end of the frame. This includes mostly +/// garbage collector tasks like removing windows or table not called and therefore +/// used anymore. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_clear(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +*/ +NK_API void nk_clear(struct nk_context*); +/*/// #### nk_free +/// Frees all memory allocated by nuklear. Not needed if context was +/// initialized with `nk_init_fixed`. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_free(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +*/ +NK_API void nk_free(struct nk_context*); +#ifdef NK_INCLUDE_COMMAND_USERDATA +/*/// #### nk_set_user_data +/// Sets the currently passed userdata passed down into each draw command. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_set_user_data(struct nk_context *ctx, nk_handle data); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|-------------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +/// __data__ | Handle with either pointer or index to be passed into every draw commands +*/ +NK_API void nk_set_user_data(struct nk_context*, nk_handle handle); +#endif +/* ============================================================================= + * + * INPUT + * + * =============================================================================*/ +/*/// ### Input +/// The input API is responsible for holding the current input state composed of +/// mouse, key and text input states. +/// It is worth noting that no direct OS or window handling is done in nuklear. +/// Instead all input state has to be provided by platform specific code. This on one hand +/// expects more work from the user and complicates usage but on the other hand +/// provides simple abstraction over a big number of platforms, libraries and other +/// already provided functionality. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// nk_input_begin(&ctx); +/// while (GetEvent(&evt)) { +/// if (evt.type == MOUSE_MOVE) +/// nk_input_motion(&ctx, evt.motion.x, evt.motion.y); +/// else if (evt.type == [...]) { +/// // [...] +/// } +/// } nk_input_end(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// #### Usage +/// Input state needs to be provided to nuklear by first calling `nk_input_begin` +/// which resets internal state like delta mouse position and button transistions. +/// After `nk_input_begin` all current input state needs to be provided. This includes +/// mouse motion, button and key pressed and released, text input and scrolling. +/// Both event- or state-based input handling are supported by this API +/// and should work without problems. Finally after all input state has been +/// mirrored `nk_input_end` needs to be called to finish input process. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_context ctx; +/// nk_init_xxx(&ctx, ...); +/// while (1) { +/// Event evt; +/// nk_input_begin(&ctx); +/// while (GetEvent(&evt)) { +/// if (evt.type == MOUSE_MOVE) +/// nk_input_motion(&ctx, evt.motion.x, evt.motion.y); +/// else if (evt.type == [...]) { +/// // [...] +/// } +/// } +/// nk_input_end(&ctx); +/// // [...] +/// nk_clear(&ctx); +/// } nk_free(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// #### Reference +/// Function | Description +/// --------------------|------------------------------------------------------- +/// __nk_input_begin__ | Begins the input mirroring process. Needs to be called before all other `nk_input_xxx` calls +/// __nk_input_motion__ | Mirrors mouse cursor position +/// __nk_input_key__ | Mirrors key state with either pressed or released +/// __nk_input_button__ | Mirrors mouse button state with either pressed or released +/// __nk_input_scroll__ | Mirrors mouse scroll values +/// __nk_input_char__ | Adds a single ASCII text character into an internal text buffer +/// __nk_input_glyph__ | Adds a single multi-byte UTF-8 character into an internal text buffer +/// __nk_input_unicode__| Adds a single unicode rune into an internal text buffer +/// __nk_input_end__ | Ends the input mirroring process by calculating state changes. Don't call any `nk_input_xxx` function referenced above after this call +*/ +enum nk_keys { + NK_KEY_NONE, + NK_KEY_SHIFT, + NK_KEY_CTRL, + NK_KEY_DEL, + NK_KEY_ENTER, + NK_KEY_TAB, + NK_KEY_BACKSPACE, + NK_KEY_COPY, + NK_KEY_CUT, + NK_KEY_PASTE, + NK_KEY_UP, + NK_KEY_DOWN, + NK_KEY_LEFT, + NK_KEY_RIGHT, + /* Shortcuts: text field */ + NK_KEY_TEXT_INSERT_MODE, + NK_KEY_TEXT_REPLACE_MODE, + NK_KEY_TEXT_RESET_MODE, + NK_KEY_TEXT_LINE_START, + NK_KEY_TEXT_LINE_END, + NK_KEY_TEXT_START, + NK_KEY_TEXT_END, + NK_KEY_TEXT_UNDO, + NK_KEY_TEXT_REDO, + NK_KEY_TEXT_SELECT_ALL, + NK_KEY_TEXT_WORD_LEFT, + NK_KEY_TEXT_WORD_RIGHT, + /* Shortcuts: scrollbar */ + NK_KEY_SCROLL_START, + NK_KEY_SCROLL_END, + NK_KEY_SCROLL_DOWN, + NK_KEY_SCROLL_UP, + NK_KEY_MAX +}; +enum nk_buttons { + NK_BUTTON_LEFT, + NK_BUTTON_MIDDLE, + NK_BUTTON_RIGHT, + NK_BUTTON_DOUBLE, + NK_BUTTON_MAX +}; +/*/// #### nk_input_begin +/// Begins the input mirroring process by resetting text, scroll +/// mouse, previous mouse position and movement as well as key state transitions, +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_input_begin(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +*/ +NK_API void nk_input_begin(struct nk_context*); +/*/// #### nk_input_motion +/// Mirrors current mouse position to nuklear +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_input_motion(struct nk_context *ctx, int x, int y); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +/// __x__ | Must hold an integer describing the current mouse cursor x-position +/// __y__ | Must hold an integer describing the current mouse cursor y-position +*/ +NK_API void nk_input_motion(struct nk_context*, int x, int y); +/*/// #### nk_input_key +/// Mirrors the state of a specific key to nuklear +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_input_key(struct nk_context*, enum nk_keys key, nk_bool down); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +/// __key__ | Must be any value specified in enum `nk_keys` that needs to be mirrored +/// __down__ | Must be 0 for key is up and 1 for key is down +*/ +NK_API void nk_input_key(struct nk_context*, enum nk_keys, nk_bool down); +/*/// #### nk_input_button +/// Mirrors the state of a specific mouse button to nuklear +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_input_button(struct nk_context *ctx, enum nk_buttons btn, int x, int y, nk_bool down); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +/// __btn__ | Must be any value specified in enum `nk_buttons` that needs to be mirrored +/// __x__ | Must contain an integer describing mouse cursor x-position on click up/down +/// __y__ | Must contain an integer describing mouse cursor y-position on click up/down +/// __down__ | Must be 0 for key is up and 1 for key is down +*/ +NK_API void nk_input_button(struct nk_context*, enum nk_buttons, int x, int y, nk_bool down); +/*/// #### nk_input_scroll +/// Copies the last mouse scroll value to nuklear. Is generally +/// a scroll value. So does not have to come from mouse and could also originate +/// TODO finish this sentence +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_input_scroll(struct nk_context *ctx, struct nk_vec2 val); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +/// __val__ | vector with both X- as well as Y-scroll value +*/ +NK_API void nk_input_scroll(struct nk_context*, struct nk_vec2 val); +/*/// #### nk_input_char +/// Copies a single ASCII character into an internal text buffer +/// This is basically a helper function to quickly push ASCII characters into +/// nuklear. +/// +/// !!! Note +/// Stores up to NK_INPUT_MAX bytes between `nk_input_begin` and `nk_input_end`. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_input_char(struct nk_context *ctx, char c); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +/// __c__ | Must be a single ASCII character preferable one that can be printed +*/ +NK_API void nk_input_char(struct nk_context*, char); +/*/// #### nk_input_glyph +/// Converts an encoded unicode rune into UTF-8 and copies the result into an +/// internal text buffer. +/// +/// !!! Note +/// Stores up to NK_INPUT_MAX bytes between `nk_input_begin` and `nk_input_end`. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_input_glyph(struct nk_context *ctx, const nk_glyph g); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +/// __g__ | UTF-32 unicode codepoint +*/ +NK_API void nk_input_glyph(struct nk_context*, const nk_glyph); +/*/// #### nk_input_unicode +/// Converts a unicode rune into UTF-8 and copies the result +/// into an internal text buffer. +/// !!! Note +/// Stores up to NK_INPUT_MAX bytes between `nk_input_begin` and `nk_input_end`. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_input_unicode(struct nk_context*, nk_rune rune); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +/// __rune__ | UTF-32 unicode codepoint +*/ +NK_API void nk_input_unicode(struct nk_context*, nk_rune); +/*/// #### nk_input_end +/// End the input mirroring process by resetting mouse grabbing +/// state to ensure the mouse cursor is not grabbed indefinitely. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_input_end(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +*/ +NK_API void nk_input_end(struct nk_context*); +/* ============================================================================= + * + * DRAWING + * + * =============================================================================*/ +/*/// ### Drawing +/// This library was designed to be render backend agnostic so it does +/// not draw anything to screen directly. Instead all drawn shapes, widgets +/// are made of, are buffered into memory and make up a command queue. +/// Each frame therefore fills the command buffer with draw commands +/// that then need to be executed by the user and his own render backend. +/// After that the command buffer needs to be cleared and a new frame can be +/// started. It is probably important to note that the command buffer is the main +/// drawing API and the optional vertex buffer API only takes this format and +/// converts it into a hardware accessible format. +/// +/// #### Usage +/// To draw all draw commands accumulated over a frame you need your own render +/// backend able to draw a number of 2D primitives. This includes at least +/// filled and stroked rectangles, circles, text, lines, triangles and scissors. +/// As soon as this criterion is met you can iterate over each draw command +/// and execute each draw command in a interpreter like fashion: +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// const struct nk_command *cmd = 0; +/// nk_foreach(cmd, &ctx) { +/// switch (cmd->type) { +/// case NK_COMMAND_LINE: +/// your_draw_line_function(...) +/// break; +/// case NK_COMMAND_RECT +/// your_draw_rect_function(...) +/// break; +/// case //...: +/// //[...] +/// } +/// } +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// In program flow context draw commands need to be executed after input has been +/// gathered and the complete UI with windows and their contained widgets have +/// been executed and before calling `nk_clear` which frees all previously +/// allocated draw commands. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_context ctx; +/// nk_init_xxx(&ctx, ...); +/// while (1) { +/// Event evt; +/// nk_input_begin(&ctx); +/// while (GetEvent(&evt)) { +/// if (evt.type == MOUSE_MOVE) +/// nk_input_motion(&ctx, evt.motion.x, evt.motion.y); +/// else if (evt.type == [...]) { +/// [...] +/// } +/// } +/// nk_input_end(&ctx); +/// // +/// // [...] +/// // +/// const struct nk_command *cmd = 0; +/// nk_foreach(cmd, &ctx) { +/// switch (cmd->type) { +/// case NK_COMMAND_LINE: +/// your_draw_line_function(...) +/// break; +/// case NK_COMMAND_RECT +/// your_draw_rect_function(...) +/// break; +/// case ...: +/// // [...] +/// } +/// nk_clear(&ctx); +/// } +/// nk_free(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// You probably noticed that you have to draw all of the UI each frame which is +/// quite wasteful. While the actual UI updating loop is quite fast rendering +/// without actually needing it is not. So there are multiple things you could do. +/// +/// First is only update on input. This of course is only an option if your +/// application only depends on the UI and does not require any outside calculations. +/// If you actually only update on input make sure to update the UI two times each +/// frame and call `nk_clear` directly after the first pass and only draw in +/// the second pass. In addition it is recommended to also add additional timers +/// to make sure the UI is not drawn more than a fixed number of frames per second. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_context ctx; +/// nk_init_xxx(&ctx, ...); +/// while (1) { +/// // [...wait for input ] +/// // [...do two UI passes ...] +/// do_ui(...) +/// nk_clear(&ctx); +/// do_ui(...) +/// // +/// // draw +/// const struct nk_command *cmd = 0; +/// nk_foreach(cmd, &ctx) { +/// switch (cmd->type) { +/// case NK_COMMAND_LINE: +/// your_draw_line_function(...) +/// break; +/// case NK_COMMAND_RECT +/// your_draw_rect_function(...) +/// break; +/// case ...: +/// //[...] +/// } +/// nk_clear(&ctx); +/// } +/// nk_free(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// The second probably more applicable trick is to only draw if anything changed. +/// It is not really useful for applications with continuous draw loop but +/// quite useful for desktop applications. To actually get nuklear to only +/// draw on changes you first have to define `NK_ZERO_COMMAND_MEMORY` and +/// allocate a memory buffer that will store each unique drawing output. +/// After each frame you compare the draw command memory inside the library +/// with your allocated buffer by memcmp. If memcmp detects differences +/// you have to copy the command buffer into the allocated buffer +/// and then draw like usual (this example uses fixed memory but you could +/// use dynamically allocated memory). +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// //[... other defines ...] +/// #define NK_ZERO_COMMAND_MEMORY +/// #include "nuklear.h" +/// // +/// // setup context +/// struct nk_context ctx; +/// void *last = calloc(1,64*1024); +/// void *buf = calloc(1,64*1024); +/// nk_init_fixed(&ctx, buf, 64*1024); +/// // +/// // loop +/// while (1) { +/// // [...input...] +/// // [...ui...] +/// void *cmds = nk_buffer_memory(&ctx.memory); +/// if (memcmp(cmds, last, ctx.memory.allocated)) { +/// memcpy(last,cmds,ctx.memory.allocated); +/// const struct nk_command *cmd = 0; +/// nk_foreach(cmd, &ctx) { +/// switch (cmd->type) { +/// case NK_COMMAND_LINE: +/// your_draw_line_function(...) +/// break; +/// case NK_COMMAND_RECT +/// your_draw_rect_function(...) +/// break; +/// case ...: +/// // [...] +/// } +/// } +/// } +/// nk_clear(&ctx); +/// } +/// nk_free(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Finally while using draw commands makes sense for higher abstracted platforms like +/// X11 and Win32 or drawing libraries it is often desirable to use graphics +/// hardware directly. Therefore it is possible to just define +/// `NK_INCLUDE_VERTEX_BUFFER_OUTPUT` which includes optional vertex output. +/// To access the vertex output you first have to convert all draw commands into +/// vertexes by calling `nk_convert` which takes in your preferred vertex format. +/// After successfully converting all draw commands just iterate over and execute all +/// vertex draw commands: +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// // fill configuration +/// struct your_vertex +/// { +/// float pos[2]; // important to keep it to 2 floats +/// float uv[2]; +/// unsigned char col[4]; +/// }; +/// struct nk_convert_config cfg = {}; +/// static const struct nk_draw_vertex_layout_element vertex_layout[] = { +/// {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct your_vertex, pos)}, +/// {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct your_vertex, uv)}, +/// {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct your_vertex, col)}, +/// {NK_VERTEX_LAYOUT_END} +/// }; +/// cfg.shape_AA = NK_ANTI_ALIASING_ON; +/// cfg.line_AA = NK_ANTI_ALIASING_ON; +/// cfg.vertex_layout = vertex_layout; +/// cfg.vertex_size = sizeof(struct your_vertex); +/// cfg.vertex_alignment = NK_ALIGNOF(struct your_vertex); +/// cfg.circle_segment_count = 22; +/// cfg.curve_segment_count = 22; +/// cfg.arc_segment_count = 22; +/// cfg.global_alpha = 1.0f; +/// cfg.null = dev->null; +/// // +/// // setup buffers and convert +/// struct nk_buffer cmds, verts, idx; +/// nk_buffer_init_default(&cmds); +/// nk_buffer_init_default(&verts); +/// nk_buffer_init_default(&idx); +/// nk_convert(&ctx, &cmds, &verts, &idx, &cfg); +/// // +/// // draw +/// nk_draw_foreach(cmd, &ctx, &cmds) { +/// if (!cmd->elem_count) continue; +/// //[...] +/// } +/// nk_buffer_free(&cms); +/// nk_buffer_free(&verts); +/// nk_buffer_free(&idx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// #### Reference +/// Function | Description +/// --------------------|------------------------------------------------------- +/// __nk__begin__ | Returns the first draw command in the context draw command list to be drawn +/// __nk__next__ | Increments the draw command iterator to the next command inside the context draw command list +/// __nk_foreach__ | Iterates over each draw command inside the context draw command list +/// __nk_convert__ | Converts from the abstract draw commands list into a hardware accessible vertex format +/// __nk_draw_begin__ | Returns the first vertex command in the context vertex draw list to be executed +/// __nk__draw_next__ | Increments the vertex command iterator to the next command inside the context vertex command list +/// __nk__draw_end__ | Returns the end of the vertex draw list +/// __nk_draw_foreach__ | Iterates over each vertex draw command inside the vertex draw list +*/ +enum nk_anti_aliasing {NK_ANTI_ALIASING_OFF, NK_ANTI_ALIASING_ON}; +enum nk_convert_result { + NK_CONVERT_SUCCESS = 0, + NK_CONVERT_INVALID_PARAM = 1, + NK_CONVERT_COMMAND_BUFFER_FULL = NK_FLAG(1), + NK_CONVERT_VERTEX_BUFFER_FULL = NK_FLAG(2), + NK_CONVERT_ELEMENT_BUFFER_FULL = NK_FLAG(3) +}; +struct nk_draw_null_texture { + nk_handle texture; /* texture handle to a texture with a white pixel */ + struct nk_vec2 uv; /* coordinates to a white pixel in the texture */ +}; +struct nk_convert_config { + float global_alpha; /* global alpha value */ + enum nk_anti_aliasing line_AA; /* line anti-aliasing flag can be turned off if you are tight on memory */ + enum nk_anti_aliasing shape_AA; /* shape anti-aliasing flag can be turned off if you are tight on memory */ + unsigned circle_segment_count; /* number of segments used for circles: default to 22 */ + unsigned arc_segment_count; /* number of segments used for arcs: default to 22 */ + unsigned curve_segment_count; /* number of segments used for curves: default to 22 */ + struct nk_draw_null_texture null; /* handle to texture with a white pixel for shape drawing */ + const struct nk_draw_vertex_layout_element *vertex_layout; /* describes the vertex output format and packing */ + nk_size vertex_size; /* sizeof one vertex for vertex packing */ + nk_size vertex_alignment; /* vertex alignment: Can be obtained by NK_ALIGNOF */ +}; +/*/// #### nk__begin +/// Returns a draw command list iterator to iterate all draw +/// commands accumulated over one frame. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// const struct nk_command* nk__begin(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | must point to an previously initialized `nk_context` struct at the end of a frame +/// +/// Returns draw command pointer pointing to the first command inside the draw command list +*/ +NK_API const struct nk_command* nk__begin(struct nk_context*); +/*/// #### nk__next +/// Returns draw command pointer pointing to the next command inside the draw command list +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// const struct nk_command* nk__next(struct nk_context*, const struct nk_command*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame +/// __cmd__ | Must point to an previously a draw command either returned by `nk__begin` or `nk__next` +/// +/// Returns draw command pointer pointing to the next command inside the draw command list +*/ +NK_API const struct nk_command* nk__next(struct nk_context*, const struct nk_command*); +/*/// #### nk_foreach +/// Iterates over each draw command inside the context draw command list +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// #define nk_foreach(c, ctx) +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame +/// __cmd__ | Command pointer initialized to NULL +/// +/// Iterates over each draw command inside the context draw command list +*/ +#define nk_foreach(c, ctx) for((c) = nk__begin(ctx); (c) != 0; (c) = nk__next(ctx,c)) +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +/*/// #### nk_convert +/// Converts all internal draw commands into vertex draw commands and fills +/// three buffers with vertexes, vertex draw commands and vertex indices. The vertex format +/// as well as some other configuration values have to be configured by filling out a +/// `nk_convert_config` struct. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// nk_flags nk_convert(struct nk_context *ctx, struct nk_buffer *cmds, +/// struct nk_buffer *vertices, struct nk_buffer *elements, const struct nk_convert_config*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame +/// __cmds__ | Must point to a previously initialized buffer to hold converted vertex draw commands +/// __vertices__| Must point to a previously initialized buffer to hold all produced vertices +/// __elements__| Must point to a previously initialized buffer to hold all produced vertex indices +/// __config__ | Must point to a filled out `nk_config` struct to configure the conversion process +/// +/// Returns one of enum nk_convert_result error codes +/// +/// Parameter | Description +/// --------------------------------|----------------------------------------------------------- +/// NK_CONVERT_SUCCESS | Signals a successful draw command to vertex buffer conversion +/// NK_CONVERT_INVALID_PARAM | An invalid argument was passed in the function call +/// NK_CONVERT_COMMAND_BUFFER_FULL | The provided buffer for storing draw commands is full or failed to allocate more memory +/// NK_CONVERT_VERTEX_BUFFER_FULL | The provided buffer for storing vertices is full or failed to allocate more memory +/// NK_CONVERT_ELEMENT_BUFFER_FULL | The provided buffer for storing indicies is full or failed to allocate more memory +*/ +NK_API nk_flags nk_convert(struct nk_context*, struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements, const struct nk_convert_config*); +/*/// #### nk__draw_begin +/// Returns a draw vertex command buffer iterator to iterate over the vertex draw command buffer +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// const struct nk_draw_command* nk__draw_begin(const struct nk_context*, const struct nk_buffer*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame +/// __buf__ | Must point to an previously by `nk_convert` filled out vertex draw command buffer +/// +/// Returns vertex draw command pointer pointing to the first command inside the vertex draw command buffer +*/ +NK_API const struct nk_draw_command* nk__draw_begin(const struct nk_context*, const struct nk_buffer*); +/*/// #### nk__draw_end +/// Returns the vertex draw command at the end of the vertex draw command buffer +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// const struct nk_draw_command* nk__draw_end(const struct nk_context *ctx, const struct nk_buffer *buf); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame +/// __buf__ | Must point to an previously by `nk_convert` filled out vertex draw command buffer +/// +/// Returns vertex draw command pointer pointing to the end of the last vertex draw command inside the vertex draw command buffer +*/ +NK_API const struct nk_draw_command* nk__draw_end(const struct nk_context*, const struct nk_buffer*); +/*/// #### nk__draw_next +/// Increments the vertex draw command buffer iterator +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// const struct nk_draw_command* nk__draw_next(const struct nk_draw_command*, const struct nk_buffer*, const struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __cmd__ | Must point to an previously either by `nk__draw_begin` or `nk__draw_next` returned vertex draw command +/// __buf__ | Must point to an previously by `nk_convert` filled out vertex draw command buffer +/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame +/// +/// Returns vertex draw command pointer pointing to the end of the last vertex draw command inside the vertex draw command buffer +*/ +NK_API const struct nk_draw_command* nk__draw_next(const struct nk_draw_command*, const struct nk_buffer*, const struct nk_context*); +/*/// #### nk_draw_foreach +/// Iterates over each vertex draw command inside a vertex draw command buffer +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// #define nk_draw_foreach(cmd,ctx, b) +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __cmd__ | `nk_draw_command`iterator set to NULL +/// __buf__ | Must point to an previously by `nk_convert` filled out vertex draw command buffer +/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame +*/ +#define nk_draw_foreach(cmd,ctx, b) for((cmd)=nk__draw_begin(ctx, b); (cmd)!=0; (cmd)=nk__draw_next(cmd, b, ctx)) +#endif +/* ============================================================================= + * + * WINDOW + * + * ============================================================================= +/// ### Window +/// Windows are the main persistent state used inside nuklear and are life time +/// controlled by simply "retouching" (i.e. calling) each window each frame. +/// All widgets inside nuklear can only be added inside the function pair `nk_begin_xxx` +/// and `nk_end`. Calling any widgets outside these two functions will result in an +/// assert in debug or no state change in release mode.

+/// +/// Each window holds frame persistent state like position, size, flags, state tables, +/// and some garbage collected internal persistent widget state. Each window +/// is linked into a window stack list which determines the drawing and overlapping +/// order. The topmost window thereby is the currently active window.

+/// +/// To change window position inside the stack occurs either automatically by +/// user input by being clicked on or programmatically by calling `nk_window_focus`. +/// Windows by default are visible unless explicitly being defined with flag +/// `NK_WINDOW_HIDDEN`, the user clicked the close button on windows with flag +/// `NK_WINDOW_CLOSABLE` or if a window was explicitly hidden by calling +/// `nk_window_show`. To explicitly close and destroy a window call `nk_window_close`.

+/// +/// #### Usage +/// To create and keep a window you have to call one of the two `nk_begin_xxx` +/// functions to start window declarations and `nk_end` at the end. Furthermore it +/// is recommended to check the return value of `nk_begin_xxx` and only process +/// widgets inside the window if the value is not 0. Either way you have to call +/// `nk_end` at the end of window declarations. Furthermore, do not attempt to +/// nest `nk_begin_xxx` calls which will hopefully result in an assert or if not +/// in a segmentation fault. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// if (nk_begin_xxx(...) { +/// // [... widgets ...] +/// } +/// nk_end(ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// In the grand concept window and widget declarations need to occur after input +/// handling and before drawing to screen. Not doing so can result in higher +/// latency or at worst invalid behavior. Furthermore make sure that `nk_clear` +/// is called at the end of the frame. While nuklear's default platform backends +/// already call `nk_clear` for you if you write your own backend not calling +/// `nk_clear` can cause asserts or even worse undefined behavior. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_context ctx; +/// nk_init_xxx(&ctx, ...); +/// while (1) { +/// Event evt; +/// nk_input_begin(&ctx); +/// while (GetEvent(&evt)) { +/// if (evt.type == MOUSE_MOVE) +/// nk_input_motion(&ctx, evt.motion.x, evt.motion.y); +/// else if (evt.type == [...]) { +/// nk_input_xxx(...); +/// } +/// } +/// nk_input_end(&ctx); +/// +/// if (nk_begin_xxx(...) { +/// //[...] +/// } +/// nk_end(ctx); +/// +/// const struct nk_command *cmd = 0; +/// nk_foreach(cmd, &ctx) { +/// case NK_COMMAND_LINE: +/// your_draw_line_function(...) +/// break; +/// case NK_COMMAND_RECT +/// your_draw_rect_function(...) +/// break; +/// case //...: +/// //[...] +/// } +/// nk_clear(&ctx); +/// } +/// nk_free(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// #### Reference +/// Function | Description +/// ------------------------------------|---------------------------------------- +/// nk_begin | Starts a new window; needs to be called every frame for every window (unless hidden) or otherwise the window gets removed +/// nk_begin_titled | Extended window start with separated title and identifier to allow multiple windows with same name but not title +/// nk_end | Needs to be called at the end of the window building process to process scaling, scrollbars and general cleanup +// +/// nk_window_find | Finds and returns the window with give name +/// nk_window_get_bounds | Returns a rectangle with screen position and size of the currently processed window. +/// nk_window_get_position | Returns the position of the currently processed window +/// nk_window_get_size | Returns the size with width and height of the currently processed window +/// nk_window_get_width | Returns the width of the currently processed window +/// nk_window_get_height | Returns the height of the currently processed window +/// nk_window_get_panel | Returns the underlying panel which contains all processing state of the current window +/// nk_window_get_content_region | Returns the position and size of the currently visible and non-clipped space inside the currently processed window +/// nk_window_get_content_region_min | Returns the upper rectangle position of the currently visible and non-clipped space inside the currently processed window +/// nk_window_get_content_region_max | Returns the upper rectangle position of the currently visible and non-clipped space inside the currently processed window +/// nk_window_get_content_region_size | Returns the size of the currently visible and non-clipped space inside the currently processed window +/// nk_window_get_canvas | Returns the draw command buffer. Can be used to draw custom widgets +/// nk_window_get_scroll | Gets the scroll offset of the current window +/// nk_window_has_focus | Returns if the currently processed window is currently active +/// nk_window_is_collapsed | Returns if the window with given name is currently minimized/collapsed +/// nk_window_is_closed | Returns if the currently processed window was closed +/// nk_window_is_hidden | Returns if the currently processed window was hidden +/// nk_window_is_active | Same as nk_window_has_focus for some reason +/// nk_window_is_hovered | Returns if the currently processed window is currently being hovered by mouse +/// nk_window_is_any_hovered | Return if any window currently hovered +/// nk_item_is_any_active | Returns if any window or widgets is currently hovered or active +// +/// nk_window_set_bounds | Updates position and size of the currently processed window +/// nk_window_set_position | Updates position of the currently process window +/// nk_window_set_size | Updates the size of the currently processed window +/// nk_window_set_focus | Set the currently processed window as active window +/// nk_window_set_scroll | Sets the scroll offset of the current window +// +/// nk_window_close | Closes the window with given window name which deletes the window at the end of the frame +/// nk_window_collapse | Collapses the window with given window name +/// nk_window_collapse_if | Collapses the window with given window name if the given condition was met +/// nk_window_show | Hides a visible or reshows a hidden window +/// nk_window_show_if | Hides/shows a window depending on condition +*/ +/* +/// #### nk_panel_flags +/// Flag | Description +/// ----------------------------|---------------------------------------- +/// NK_WINDOW_BORDER | Draws a border around the window to visually separate window from the background +/// NK_WINDOW_MOVABLE | The movable flag indicates that a window can be moved by user input or by dragging the window header +/// NK_WINDOW_SCALABLE | The scalable flag indicates that a window can be scaled by user input by dragging a scaler icon at the button of the window +/// NK_WINDOW_CLOSABLE | Adds a closable icon into the header +/// NK_WINDOW_MINIMIZABLE | Adds a minimize icon into the header +/// NK_WINDOW_NO_SCROLLBAR | Removes the scrollbar from the window +/// NK_WINDOW_TITLE | Forces a header at the top at the window showing the title +/// NK_WINDOW_SCROLL_AUTO_HIDE | Automatically hides the window scrollbar if no user interaction: also requires delta time in `nk_context` to be set each frame +/// NK_WINDOW_BACKGROUND | Always keep window in the background +/// NK_WINDOW_SCALE_LEFT | Puts window scaler in the left-bottom corner instead right-bottom +/// NK_WINDOW_NO_INPUT | Prevents window of scaling, moving or getting focus +/// +/// #### nk_collapse_states +/// State | Description +/// ----------------|----------------------------------------------------------- +/// __NK_MINIMIZED__| UI section is collased and not visibile until maximized +/// __NK_MAXIMIZED__| UI section is extended and visibile until minimized +///

+*/ +enum nk_panel_flags { + NK_WINDOW_BORDER = NK_FLAG(0), + NK_WINDOW_MOVABLE = NK_FLAG(1), + NK_WINDOW_SCALABLE = NK_FLAG(2), + NK_WINDOW_CLOSABLE = NK_FLAG(3), + NK_WINDOW_MINIMIZABLE = NK_FLAG(4), + NK_WINDOW_NO_SCROLLBAR = NK_FLAG(5), + NK_WINDOW_TITLE = NK_FLAG(6), + NK_WINDOW_SCROLL_AUTO_HIDE = NK_FLAG(7), + NK_WINDOW_BACKGROUND = NK_FLAG(8), + NK_WINDOW_SCALE_LEFT = NK_FLAG(9), + NK_WINDOW_NO_INPUT = NK_FLAG(10) +}; +/*/// #### nk_begin +/// Starts a new window; needs to be called every frame for every +/// window (unless hidden) or otherwise the window gets removed +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_begin(struct nk_context *ctx, const char *title, struct nk_rect bounds, nk_flags flags); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __title__ | Window title and identifier. Needs to be persistent over frames to identify the window +/// __bounds__ | Initial position and window size. However if you do not define `NK_WINDOW_SCALABLE` or `NK_WINDOW_MOVABLE` you can set window position and size every frame +/// __flags__ | Window flags defined in the nk_panel_flags section with a number of different window behaviors +/// +/// Returns `true(1)` if the window can be filled up with widgets from this point +/// until `nk_end` or `false(0)` otherwise for example if minimized +*/ +NK_API nk_bool nk_begin(struct nk_context *ctx, const char *title, struct nk_rect bounds, nk_flags flags); +/*/// #### nk_begin_titled +/// Extended window start with separated title and identifier to allow multiple +/// windows with same title but not name +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_begin_titled(struct nk_context *ctx, const char *name, const char *title, struct nk_rect bounds, nk_flags flags); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Window identifier. Needs to be persistent over frames to identify the window +/// __title__ | Window title displayed inside header if flag `NK_WINDOW_TITLE` or either `NK_WINDOW_CLOSABLE` or `NK_WINDOW_MINIMIZED` was set +/// __bounds__ | Initial position and window size. However if you do not define `NK_WINDOW_SCALABLE` or `NK_WINDOW_MOVABLE` you can set window position and size every frame +/// __flags__ | Window flags defined in the nk_panel_flags section with a number of different window behaviors +/// +/// Returns `true(1)` if the window can be filled up with widgets from this point +/// until `nk_end` or `false(0)` otherwise for example if minimized +*/ +NK_API nk_bool nk_begin_titled(struct nk_context *ctx, const char *name, const char *title, struct nk_rect bounds, nk_flags flags); +/*/// #### nk_end +/// Needs to be called at the end of the window building process to process scaling, scrollbars and general cleanup. +/// All widget calls after this functions will result in asserts or no state changes +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_end(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +*/ +NK_API void nk_end(struct nk_context *ctx); +/*/// #### nk_window_find +/// Finds and returns a window from passed name +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_window *nk_window_find(struct nk_context *ctx, const char *name); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Window identifier +/// +/// Returns a `nk_window` struct pointing to the identified window or NULL if +/// no window with the given name was found +*/ +NK_API struct nk_window *nk_window_find(struct nk_context *ctx, const char *name); +/*/// #### nk_window_get_bounds +/// Returns a rectangle with screen position and size of the currently processed window +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_rect nk_window_get_bounds(const struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns a `nk_rect` struct with window upper left window position and size +*/ +NK_API struct nk_rect nk_window_get_bounds(const struct nk_context *ctx); +/*/// #### nk_window_get_position +/// Returns the position of the currently processed window. +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_vec2 nk_window_get_position(const struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns a `nk_vec2` struct with window upper left position +*/ +NK_API struct nk_vec2 nk_window_get_position(const struct nk_context *ctx); +/*/// #### nk_window_get_size +/// Returns the size with width and height of the currently processed window. +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_vec2 nk_window_get_size(const struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns a `nk_vec2` struct with window width and height +*/ +NK_API struct nk_vec2 nk_window_get_size(const struct nk_context*); +/*/// #### nk_window_get_width +/// Returns the width of the currently processed window. +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// float nk_window_get_width(const struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns the current window width +*/ +NK_API float nk_window_get_width(const struct nk_context*); +/*/// #### nk_window_get_height +/// Returns the height of the currently processed window. +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// float nk_window_get_height(const struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns the current window height +*/ +NK_API float nk_window_get_height(const struct nk_context*); +/*/// #### nk_window_get_panel +/// Returns the underlying panel which contains all processing state of the current window. +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// !!! WARNING +/// Do not keep the returned panel pointer around, it is only valid until `nk_end` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_panel* nk_window_get_panel(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns a pointer to window internal `nk_panel` state. +*/ +NK_API struct nk_panel* nk_window_get_panel(struct nk_context*); +/*/// #### nk_window_get_content_region +/// Returns the position and size of the currently visible and non-clipped space +/// inside the currently processed window. +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_rect nk_window_get_content_region(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns `nk_rect` struct with screen position and size (no scrollbar offset) +/// of the visible space inside the current window +*/ +NK_API struct nk_rect nk_window_get_content_region(struct nk_context*); +/*/// #### nk_window_get_content_region_min +/// Returns the upper left position of the currently visible and non-clipped +/// space inside the currently processed window. +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_vec2 nk_window_get_content_region_min(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// returns `nk_vec2` struct with upper left screen position (no scrollbar offset) +/// of the visible space inside the current window +*/ +NK_API struct nk_vec2 nk_window_get_content_region_min(struct nk_context*); +/*/// #### nk_window_get_content_region_max +/// Returns the lower right screen position of the currently visible and +/// non-clipped space inside the currently processed window. +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_vec2 nk_window_get_content_region_max(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns `nk_vec2` struct with lower right screen position (no scrollbar offset) +/// of the visible space inside the current window +*/ +NK_API struct nk_vec2 nk_window_get_content_region_max(struct nk_context*); +/*/// #### nk_window_get_content_region_size +/// Returns the size of the currently visible and non-clipped space inside the +/// currently processed window +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_vec2 nk_window_get_content_region_size(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns `nk_vec2` struct with size the visible space inside the current window +*/ +NK_API struct nk_vec2 nk_window_get_content_region_size(struct nk_context*); +/*/// #### nk_window_get_canvas +/// Returns the draw command buffer. Can be used to draw custom widgets +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// !!! WARNING +/// Do not keep the returned command buffer pointer around it is only valid until `nk_end` +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_command_buffer* nk_window_get_canvas(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns a pointer to window internal `nk_command_buffer` struct used as +/// drawing canvas. Can be used to do custom drawing. +*/ +NK_API struct nk_command_buffer* nk_window_get_canvas(struct nk_context*); +/*/// #### nk_window_get_scroll +/// Gets the scroll offset for the current window +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_get_scroll(struct nk_context *ctx, nk_uint *offset_x, nk_uint *offset_y); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// -------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __offset_x__ | A pointer to the x offset output (or NULL to ignore) +/// __offset_y__ | A pointer to the y offset output (or NULL to ignore) +*/ +NK_API void nk_window_get_scroll(struct nk_context*, nk_uint *offset_x, nk_uint *offset_y); +/*/// #### nk_window_has_focus +/// Returns if the currently processed window is currently active +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_window_has_focus(const struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns `false(0)` if current window is not active or `true(1)` if it is +*/ +NK_API nk_bool nk_window_has_focus(const struct nk_context*); +/*/// #### nk_window_is_hovered +/// Return if the current window is being hovered +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_window_is_hovered(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns `true(1)` if current window is hovered or `false(0)` otherwise +*/ +NK_API nk_bool nk_window_is_hovered(struct nk_context*); +/*/// #### nk_window_is_collapsed +/// Returns if the window with given name is currently minimized/collapsed +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_window_is_collapsed(struct nk_context *ctx, const char *name); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of window you want to check if it is collapsed +/// +/// Returns `true(1)` if current window is minimized and `false(0)` if window not +/// found or is not minimized +*/ +NK_API nk_bool nk_window_is_collapsed(struct nk_context *ctx, const char *name); +/*/// #### nk_window_is_closed +/// Returns if the window with given name was closed by calling `nk_close` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_window_is_closed(struct nk_context *ctx, const char *name); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of window you want to check if it is closed +/// +/// Returns `true(1)` if current window was closed or `false(0)` window not found or not closed +*/ +NK_API nk_bool nk_window_is_closed(struct nk_context*, const char*); +/*/// #### nk_window_is_hidden +/// Returns if the window with given name is hidden +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_window_is_hidden(struct nk_context *ctx, const char *name); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of window you want to check if it is hidden +/// +/// Returns `true(1)` if current window is hidden or `false(0)` window not found or visible +*/ +NK_API nk_bool nk_window_is_hidden(struct nk_context*, const char*); +/*/// #### nk_window_is_active +/// Same as nk_window_has_focus for some reason +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_window_is_active(struct nk_context *ctx, const char *name); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of window you want to check if it is active +/// +/// Returns `true(1)` if current window is active or `false(0)` window not found or not active +*/ +NK_API nk_bool nk_window_is_active(struct nk_context*, const char*); +/*/// #### nk_window_is_any_hovered +/// Returns if the any window is being hovered +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_window_is_any_hovered(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns `true(1)` if any window is hovered or `false(0)` otherwise +*/ +NK_API nk_bool nk_window_is_any_hovered(struct nk_context*); +/*/// #### nk_item_is_any_active +/// Returns if the any window is being hovered or any widget is currently active. +/// Can be used to decide if input should be processed by UI or your specific input handling. +/// Example could be UI and 3D camera to move inside a 3D space. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_item_is_any_active(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns `true(1)` if any window is hovered or any item is active or `false(0)` otherwise +*/ +NK_API nk_bool nk_item_is_any_active(struct nk_context*); +/*/// #### nk_window_set_bounds +/// Updates position and size of window with passed in name +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_set_bounds(struct nk_context*, const char *name, struct nk_rect bounds); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of the window to modify both position and size +/// __bounds__ | Must point to a `nk_rect` struct with the new position and size +*/ +NK_API void nk_window_set_bounds(struct nk_context*, const char *name, struct nk_rect bounds); +/*/// #### nk_window_set_position +/// Updates position of window with passed name +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_set_position(struct nk_context*, const char *name, struct nk_vec2 pos); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of the window to modify both position +/// __pos__ | Must point to a `nk_vec2` struct with the new position +*/ +NK_API void nk_window_set_position(struct nk_context*, const char *name, struct nk_vec2 pos); +/*/// #### nk_window_set_size +/// Updates size of window with passed in name +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_set_size(struct nk_context*, const char *name, struct nk_vec2); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of the window to modify both window size +/// __size__ | Must point to a `nk_vec2` struct with new window size +*/ +NK_API void nk_window_set_size(struct nk_context*, const char *name, struct nk_vec2); +/*/// #### nk_window_set_focus +/// Sets the window with given name as active +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_set_focus(struct nk_context*, const char *name); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of the window to set focus on +*/ +NK_API void nk_window_set_focus(struct nk_context*, const char *name); +/*/// #### nk_window_set_scroll +/// Sets the scroll offset for the current window +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_set_scroll(struct nk_context *ctx, nk_uint offset_x, nk_uint offset_y); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// -------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __offset_x__ | The x offset to scroll to +/// __offset_y__ | The y offset to scroll to +*/ +NK_API void nk_window_set_scroll(struct nk_context*, nk_uint offset_x, nk_uint offset_y); +/*/// #### nk_window_close +/// Closes a window and marks it for being freed at the end of the frame +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_close(struct nk_context *ctx, const char *name); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of the window to close +*/ +NK_API void nk_window_close(struct nk_context *ctx, const char *name); +/*/// #### nk_window_collapse +/// Updates collapse state of a window with given name +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_collapse(struct nk_context*, const char *name, enum nk_collapse_states state); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of the window to close +/// __state__ | value out of nk_collapse_states section +*/ +NK_API void nk_window_collapse(struct nk_context*, const char *name, enum nk_collapse_states state); +/*/// #### nk_window_collapse_if +/// Updates collapse state of a window with given name if given condition is met +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_collapse_if(struct nk_context*, const char *name, enum nk_collapse_states, int cond); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of the window to either collapse or maximize +/// __state__ | value out of nk_collapse_states section the window should be put into +/// __cond__ | condition that has to be met to actually commit the collapse state change +*/ +NK_API void nk_window_collapse_if(struct nk_context*, const char *name, enum nk_collapse_states, int cond); +/*/// #### nk_window_show +/// updates visibility state of a window with given name +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_show(struct nk_context*, const char *name, enum nk_show_states); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of the window to either collapse or maximize +/// __state__ | state with either visible or hidden to modify the window with +*/ +NK_API void nk_window_show(struct nk_context*, const char *name, enum nk_show_states); +/*/// #### nk_window_show_if +/// Updates visibility state of a window with given name if a given condition is met +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_show_if(struct nk_context*, const char *name, enum nk_show_states, int cond); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of the window to either hide or show +/// __state__ | state with either visible or hidden to modify the window with +/// __cond__ | condition that has to be met to actually commit the visbility state change +*/ +NK_API void nk_window_show_if(struct nk_context*, const char *name, enum nk_show_states, int cond); +/* ============================================================================= + * + * LAYOUT + * + * ============================================================================= +/// ### Layouting +/// Layouting in general describes placing widget inside a window with position and size. +/// While in this particular implementation there are five different APIs for layouting +/// each with different trade offs between control and ease of use.

+/// +/// All layouting methods in this library are based around the concept of a row. +/// A row has a height the window content grows by and a number of columns and each +/// layouting method specifies how each widget is placed inside the row. +/// After a row has been allocated by calling a layouting functions and then +/// filled with widgets will advance an internal pointer over the allocated row.

+/// +/// To actually define a layout you just call the appropriate layouting function +/// and each subsequent widget call will place the widget as specified. Important +/// here is that if you define more widgets then columns defined inside the layout +/// functions it will allocate the next row without you having to make another layouting

+/// call. +/// +/// Biggest limitation with using all these APIs outside the `nk_layout_space_xxx` API +/// is that you have to define the row height for each. However the row height +/// often depends on the height of the font.

+/// +/// To fix that internally nuklear uses a minimum row height that is set to the +/// height plus padding of currently active font and overwrites the row height +/// value if zero.

+/// +/// If you manually want to change the minimum row height then +/// use nk_layout_set_min_row_height, and use nk_layout_reset_min_row_height to +/// reset it back to be derived from font height.

+/// +/// Also if you change the font in nuklear it will automatically change the minimum +/// row height for you and. This means if you change the font but still want +/// a minimum row height smaller than the font you have to repush your value.

+/// +/// For actually more advanced UI I would even recommend using the `nk_layout_space_xxx` +/// layouting method in combination with a cassowary constraint solver (there are +/// some versions on github with permissive license model) to take over all control over widget +/// layouting yourself. However for quick and dirty layouting using all the other layouting +/// functions should be fine. +/// +/// #### Usage +/// 1. __nk_layout_row_dynamic__

+/// The easiest layouting function is `nk_layout_row_dynamic`. It provides each +/// widgets with same horizontal space inside the row and dynamically grows +/// if the owning window grows in width. So the number of columns dictates +/// the size of each widget dynamically by formula: +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// widget_width = (window_width - padding - spacing) * (1/colum_count) +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Just like all other layouting APIs if you define more widget than columns this +/// library will allocate a new row and keep all layouting parameters previously +/// defined. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// if (nk_begin_xxx(...) { +/// // first row with height: 30 composed of two widgets +/// nk_layout_row_dynamic(&ctx, 30, 2); +/// nk_widget(...); +/// nk_widget(...); +/// // +/// // second row with same parameter as defined above +/// nk_widget(...); +/// nk_widget(...); +/// // +/// // third row uses 0 for height which will use auto layouting +/// nk_layout_row_dynamic(&ctx, 0, 2); +/// nk_widget(...); +/// nk_widget(...); +/// } +/// nk_end(...); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// 2. __nk_layout_row_static__

+/// Another easy layouting function is `nk_layout_row_static`. It provides each +/// widget with same horizontal pixel width inside the row and does not grow +/// if the owning window scales smaller or bigger. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// if (nk_begin_xxx(...) { +/// // first row with height: 30 composed of two widgets with width: 80 +/// nk_layout_row_static(&ctx, 30, 80, 2); +/// nk_widget(...); +/// nk_widget(...); +/// // +/// // second row with same parameter as defined above +/// nk_widget(...); +/// nk_widget(...); +/// // +/// // third row uses 0 for height which will use auto layouting +/// nk_layout_row_static(&ctx, 0, 80, 2); +/// nk_widget(...); +/// nk_widget(...); +/// } +/// nk_end(...); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// 3. __nk_layout_row_xxx__

+/// A little bit more advanced layouting API are functions `nk_layout_row_begin`, +/// `nk_layout_row_push` and `nk_layout_row_end`. They allow to directly +/// specify each column pixel or window ratio in a row. It supports either +/// directly setting per column pixel width or widget window ratio but not +/// both. Furthermore it is a immediate mode API so each value is directly +/// pushed before calling a widget. Therefore the layout is not automatically +/// repeating like the last two layouting functions. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// if (nk_begin_xxx(...) { +/// // first row with height: 25 composed of two widgets with width 60 and 40 +/// nk_layout_row_begin(ctx, NK_STATIC, 25, 2); +/// nk_layout_row_push(ctx, 60); +/// nk_widget(...); +/// nk_layout_row_push(ctx, 40); +/// nk_widget(...); +/// nk_layout_row_end(ctx); +/// // +/// // second row with height: 25 composed of two widgets with window ratio 0.25 and 0.75 +/// nk_layout_row_begin(ctx, NK_DYNAMIC, 25, 2); +/// nk_layout_row_push(ctx, 0.25f); +/// nk_widget(...); +/// nk_layout_row_push(ctx, 0.75f); +/// nk_widget(...); +/// nk_layout_row_end(ctx); +/// // +/// // third row with auto generated height: composed of two widgets with window ratio 0.25 and 0.75 +/// nk_layout_row_begin(ctx, NK_DYNAMIC, 0, 2); +/// nk_layout_row_push(ctx, 0.25f); +/// nk_widget(...); +/// nk_layout_row_push(ctx, 0.75f); +/// nk_widget(...); +/// nk_layout_row_end(ctx); +/// } +/// nk_end(...); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// 4. __nk_layout_row__

+/// The array counterpart to API nk_layout_row_xxx is the single nk_layout_row +/// functions. Instead of pushing either pixel or window ratio for every widget +/// it allows to define it by array. The trade of for less control is that +/// `nk_layout_row` is automatically repeating. Otherwise the behavior is the +/// same. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// if (nk_begin_xxx(...) { +/// // two rows with height: 30 composed of two widgets with width 60 and 40 +/// const float size[] = {60,40}; +/// nk_layout_row(ctx, NK_STATIC, 30, 2, ratio); +/// nk_widget(...); +/// nk_widget(...); +/// nk_widget(...); +/// nk_widget(...); +/// // +/// // two rows with height: 30 composed of two widgets with window ratio 0.25 and 0.75 +/// const float ratio[] = {0.25, 0.75}; +/// nk_layout_row(ctx, NK_DYNAMIC, 30, 2, ratio); +/// nk_widget(...); +/// nk_widget(...); +/// nk_widget(...); +/// nk_widget(...); +/// // +/// // two rows with auto generated height composed of two widgets with window ratio 0.25 and 0.75 +/// const float ratio[] = {0.25, 0.75}; +/// nk_layout_row(ctx, NK_DYNAMIC, 30, 2, ratio); +/// nk_widget(...); +/// nk_widget(...); +/// nk_widget(...); +/// nk_widget(...); +/// } +/// nk_end(...); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// 5. __nk_layout_row_template_xxx__

+/// The most complex and second most flexible API is a simplified flexbox version without +/// line wrapping and weights for dynamic widgets. It is an immediate mode API but +/// unlike `nk_layout_row_xxx` it has auto repeat behavior and needs to be called +/// before calling the templated widgets. +/// The row template layout has three different per widget size specifier. The first +/// one is the `nk_layout_row_template_push_static` with fixed widget pixel width. +/// They do not grow if the row grows and will always stay the same. +/// The second size specifier is `nk_layout_row_template_push_variable` +/// which defines a minimum widget size but it also can grow if more space is available +/// not taken by other widgets. +/// Finally there are dynamic widgets with `nk_layout_row_template_push_dynamic` +/// which are completely flexible and unlike variable widgets can even shrink +/// to zero if not enough space is provided. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// if (nk_begin_xxx(...) { +/// // two rows with height: 30 composed of three widgets +/// nk_layout_row_template_begin(ctx, 30); +/// nk_layout_row_template_push_dynamic(ctx); +/// nk_layout_row_template_push_variable(ctx, 80); +/// nk_layout_row_template_push_static(ctx, 80); +/// nk_layout_row_template_end(ctx); +/// // +/// // first row +/// nk_widget(...); // dynamic widget can go to zero if not enough space +/// nk_widget(...); // variable widget with min 80 pixel but can grow bigger if enough space +/// nk_widget(...); // static widget with fixed 80 pixel width +/// // +/// // second row same layout +/// nk_widget(...); +/// nk_widget(...); +/// nk_widget(...); +/// } +/// nk_end(...); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// 6. __nk_layout_space_xxx__

+/// Finally the most flexible API directly allows you to place widgets inside the +/// window. The space layout API is an immediate mode API which does not support +/// row auto repeat and directly sets position and size of a widget. Position +/// and size hereby can be either specified as ratio of allocated space or +/// allocated space local position and pixel size. Since this API is quite +/// powerful there are a number of utility functions to get the available space +/// and convert between local allocated space and screen space. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// if (nk_begin_xxx(...) { +/// // static row with height: 500 (you can set column count to INT_MAX if you don't want to be bothered) +/// nk_layout_space_begin(ctx, NK_STATIC, 500, INT_MAX); +/// nk_layout_space_push(ctx, nk_rect(0,0,150,200)); +/// nk_widget(...); +/// nk_layout_space_push(ctx, nk_rect(200,200,100,200)); +/// nk_widget(...); +/// nk_layout_space_end(ctx); +/// // +/// // dynamic row with height: 500 (you can set column count to INT_MAX if you don't want to be bothered) +/// nk_layout_space_begin(ctx, NK_DYNAMIC, 500, INT_MAX); +/// nk_layout_space_push(ctx, nk_rect(0.5,0.5,0.1,0.1)); +/// nk_widget(...); +/// nk_layout_space_push(ctx, nk_rect(0.7,0.6,0.1,0.1)); +/// nk_widget(...); +/// } +/// nk_end(...); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// #### Reference +/// Function | Description +/// ----------------------------------------|------------------------------------ +/// nk_layout_set_min_row_height | Set the currently used minimum row height to a specified value +/// nk_layout_reset_min_row_height | Resets the currently used minimum row height to font height +/// nk_layout_widget_bounds | Calculates current width a static layout row can fit inside a window +/// nk_layout_ratio_from_pixel | Utility functions to calculate window ratio from pixel size +// +/// nk_layout_row_dynamic | Current layout is divided into n same sized growing columns +/// nk_layout_row_static | Current layout is divided into n same fixed sized columns +/// nk_layout_row_begin | Starts a new row with given height and number of columns +/// nk_layout_row_push | Pushes another column with given size or window ratio +/// nk_layout_row_end | Finished previously started row +/// nk_layout_row | Specifies row columns in array as either window ratio or size +// +/// nk_layout_row_template_begin | Begins the row template declaration +/// nk_layout_row_template_push_dynamic | Adds a dynamic column that dynamically grows and can go to zero if not enough space +/// nk_layout_row_template_push_variable | Adds a variable column that dynamically grows but does not shrink below specified pixel width +/// nk_layout_row_template_push_static | Adds a static column that does not grow and will always have the same size +/// nk_layout_row_template_end | Marks the end of the row template +// +/// nk_layout_space_begin | Begins a new layouting space that allows to specify each widgets position and size +/// nk_layout_space_push | Pushes position and size of the next widget in own coordinate space either as pixel or ratio +/// nk_layout_space_end | Marks the end of the layouting space +// +/// nk_layout_space_bounds | Callable after nk_layout_space_begin and returns total space allocated +/// nk_layout_space_to_screen | Converts vector from nk_layout_space coordinate space into screen space +/// nk_layout_space_to_local | Converts vector from screen space into nk_layout_space coordinates +/// nk_layout_space_rect_to_screen | Converts rectangle from nk_layout_space coordinate space into screen space +/// nk_layout_space_rect_to_local | Converts rectangle from screen space into nk_layout_space coordinates +*/ +/*/// #### nk_layout_set_min_row_height +/// Sets the currently used minimum row height. +/// !!! WARNING +/// The passed height needs to include both your preferred row height +/// as well as padding. No internal padding is added. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_set_min_row_height(struct nk_context*, float height); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __height__ | New minimum row height to be used for auto generating the row height +*/ +NK_API void nk_layout_set_min_row_height(struct nk_context*, float height); +/*/// #### nk_layout_reset_min_row_height +/// Reset the currently used minimum row height back to `font_height + text_padding + padding` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_reset_min_row_height(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +*/ +NK_API void nk_layout_reset_min_row_height(struct nk_context*); +/*/// #### nk_layout_widget_bounds +/// Returns the width of the next row allocate by one of the layouting functions +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_rect nk_layout_widget_bounds(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// +/// Return `nk_rect` with both position and size of the next row +*/ +NK_API struct nk_rect nk_layout_widget_bounds(struct nk_context*); +/*/// #### nk_layout_ratio_from_pixel +/// Utility functions to calculate window ratio from pixel size +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// float nk_layout_ratio_from_pixel(struct nk_context*, float pixel_width); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __pixel__ | Pixel_width to convert to window ratio +/// +/// Returns `nk_rect` with both position and size of the next row +*/ +NK_API float nk_layout_ratio_from_pixel(struct nk_context*, float pixel_width); +/*/// #### nk_layout_row_dynamic +/// Sets current row layout to share horizontal space +/// between @cols number of widgets evenly. Once called all subsequent widget +/// calls greater than @cols will allocate a new row with same layout. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_dynamic(struct nk_context *ctx, float height, int cols); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __height__ | Holds height of each widget in row or zero for auto layouting +/// __columns__ | Number of widget inside row +*/ +NK_API void nk_layout_row_dynamic(struct nk_context *ctx, float height, int cols); +/*/// #### nk_layout_row_static +/// Sets current row layout to fill @cols number of widgets +/// in row with same @item_width horizontal size. Once called all subsequent widget +/// calls greater than @cols will allocate a new row with same layout. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_static(struct nk_context *ctx, float height, int item_width, int cols); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __height__ | Holds height of each widget in row or zero for auto layouting +/// __width__ | Holds pixel width of each widget in the row +/// __columns__ | Number of widget inside row +*/ +NK_API void nk_layout_row_static(struct nk_context *ctx, float height, int item_width, int cols); +/*/// #### nk_layout_row_begin +/// Starts a new dynamic or fixed row with given height and columns. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_begin(struct nk_context *ctx, enum nk_layout_format fmt, float row_height, int cols); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __fmt__ | either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns +/// __height__ | holds height of each widget in row or zero for auto layouting +/// __columns__ | Number of widget inside row +*/ +NK_API void nk_layout_row_begin(struct nk_context *ctx, enum nk_layout_format fmt, float row_height, int cols); +/*/// #### nk_layout_row_push +/// Specifies either window ratio or width of a single column +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_push(struct nk_context*, float value); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __value__ | either a window ratio or fixed width depending on @fmt in previous `nk_layout_row_begin` call +*/ +NK_API void nk_layout_row_push(struct nk_context*, float value); +/*/// #### nk_layout_row_end +/// Finished previously started row +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_end(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +*/ +NK_API void nk_layout_row_end(struct nk_context*); +/*/// #### nk_layout_row +/// Specifies row columns in array as either window ratio or size +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row(struct nk_context*, enum nk_layout_format, float height, int cols, const float *ratio); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __fmt__ | Either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns +/// __height__ | Holds height of each widget in row or zero for auto layouting +/// __columns__ | Number of widget inside row +*/ +NK_API void nk_layout_row(struct nk_context*, enum nk_layout_format, float height, int cols, const float *ratio); +/*/// #### nk_layout_row_template_begin +/// Begins the row template declaration +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_template_begin(struct nk_context*, float row_height); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __height__ | Holds height of each widget in row or zero for auto layouting +*/ +NK_API void nk_layout_row_template_begin(struct nk_context*, float row_height); +/*/// #### nk_layout_row_template_push_dynamic +/// Adds a dynamic column that dynamically grows and can go to zero if not enough space +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_template_push_dynamic(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __height__ | Holds height of each widget in row or zero for auto layouting +*/ +NK_API void nk_layout_row_template_push_dynamic(struct nk_context*); +/*/// #### nk_layout_row_template_push_variable +/// Adds a variable column that dynamically grows but does not shrink below specified pixel width +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_template_push_variable(struct nk_context*, float min_width); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __width__ | Holds the minimum pixel width the next column must always be +*/ +NK_API void nk_layout_row_template_push_variable(struct nk_context*, float min_width); +/*/// #### nk_layout_row_template_push_static +/// Adds a static column that does not grow and will always have the same size +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_template_push_static(struct nk_context*, float width); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __width__ | Holds the absolute pixel width value the next column must be +*/ +NK_API void nk_layout_row_template_push_static(struct nk_context*, float width); +/*/// #### nk_layout_row_template_end +/// Marks the end of the row template +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_template_end(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +*/ +NK_API void nk_layout_row_template_end(struct nk_context*); +/*/// #### nk_layout_space_begin +/// Begins a new layouting space that allows to specify each widgets position and size. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_space_begin(struct nk_context*, enum nk_layout_format, float height, int widget_count); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __fmt__ | Either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns +/// __height__ | Holds height of each widget in row or zero for auto layouting +/// __columns__ | Number of widgets inside row +*/ +NK_API void nk_layout_space_begin(struct nk_context*, enum nk_layout_format, float height, int widget_count); +/*/// #### nk_layout_space_push +/// Pushes position and size of the next widget in own coordinate space either as pixel or ratio +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_space_push(struct nk_context *ctx, struct nk_rect bounds); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` +/// __bounds__ | Position and size in laoyut space local coordinates +*/ +NK_API void nk_layout_space_push(struct nk_context*, struct nk_rect bounds); +/*/// #### nk_layout_space_end +/// Marks the end of the layout space +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_space_end(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` +*/ +NK_API void nk_layout_space_end(struct nk_context*); +/*/// #### nk_layout_space_bounds +/// Utility function to calculate total space allocated for `nk_layout_space` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_rect nk_layout_space_bounds(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` +/// +/// Returns `nk_rect` holding the total space allocated +*/ +NK_API struct nk_rect nk_layout_space_bounds(struct nk_context*); +/*/// #### nk_layout_space_to_screen +/// Converts vector from nk_layout_space coordinate space into screen space +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_vec2 nk_layout_space_to_screen(struct nk_context*, struct nk_vec2); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` +/// __vec__ | Position to convert from layout space into screen coordinate space +/// +/// Returns transformed `nk_vec2` in screen space coordinates +*/ +NK_API struct nk_vec2 nk_layout_space_to_screen(struct nk_context*, struct nk_vec2); +/*/// #### nk_layout_space_to_local +/// Converts vector from layout space into screen space +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_vec2 nk_layout_space_to_local(struct nk_context*, struct nk_vec2); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` +/// __vec__ | Position to convert from screen space into layout coordinate space +/// +/// Returns transformed `nk_vec2` in layout space coordinates +*/ +NK_API struct nk_vec2 nk_layout_space_to_local(struct nk_context*, struct nk_vec2); +/*/// #### nk_layout_space_rect_to_screen +/// Converts rectangle from screen space into layout space +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_rect nk_layout_space_rect_to_screen(struct nk_context*, struct nk_rect); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` +/// __bounds__ | Rectangle to convert from layout space into screen space +/// +/// Returns transformed `nk_rect` in screen space coordinates +*/ +NK_API struct nk_rect nk_layout_space_rect_to_screen(struct nk_context*, struct nk_rect); +/*/// #### nk_layout_space_rect_to_local +/// Converts rectangle from layout space into screen space +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_rect nk_layout_space_rect_to_local(struct nk_context*, struct nk_rect); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` +/// __bounds__ | Rectangle to convert from layout space into screen space +/// +/// Returns transformed `nk_rect` in layout space coordinates +*/ +NK_API struct nk_rect nk_layout_space_rect_to_local(struct nk_context*, struct nk_rect); +/* ============================================================================= + * + * GROUP + * + * ============================================================================= +/// ### Groups +/// Groups are basically windows inside windows. They allow to subdivide space +/// in a window to layout widgets as a group. Almost all more complex widget +/// layouting requirements can be solved using groups and basic layouting +/// fuctionality. Groups just like windows are identified by an unique name and +/// internally keep track of scrollbar offsets by default. However additional +/// versions are provided to directly manage the scrollbar. +/// +/// #### Usage +/// To create a group you have to call one of the three `nk_group_begin_xxx` +/// functions to start group declarations and `nk_group_end` at the end. Furthermore it +/// is required to check the return value of `nk_group_begin_xxx` and only process +/// widgets inside the window if the value is not 0. +/// Nesting groups is possible and even encouraged since many layouting schemes +/// can only be achieved by nesting. Groups, unlike windows, need `nk_group_end` +/// to be only called if the corosponding `nk_group_begin_xxx` call does not return 0: +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// if (nk_group_begin_xxx(ctx, ...) { +/// // [... widgets ...] +/// nk_group_end(ctx); +/// } +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// In the grand concept groups can be called after starting a window +/// with `nk_begin_xxx` and before calling `nk_end`: +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_context ctx; +/// nk_init_xxx(&ctx, ...); +/// while (1) { +/// // Input +/// Event evt; +/// nk_input_begin(&ctx); +/// while (GetEvent(&evt)) { +/// if (evt.type == MOUSE_MOVE) +/// nk_input_motion(&ctx, evt.motion.x, evt.motion.y); +/// else if (evt.type == [...]) { +/// nk_input_xxx(...); +/// } +/// } +/// nk_input_end(&ctx); +/// // +/// // Window +/// if (nk_begin_xxx(...) { +/// // [...widgets...] +/// nk_layout_row_dynamic(...); +/// if (nk_group_begin_xxx(ctx, ...) { +/// //[... widgets ...] +/// nk_group_end(ctx); +/// } +/// } +/// nk_end(ctx); +/// // +/// // Draw +/// const struct nk_command *cmd = 0; +/// nk_foreach(cmd, &ctx) { +/// switch (cmd->type) { +/// case NK_COMMAND_LINE: +/// your_draw_line_function(...) +/// break; +/// case NK_COMMAND_RECT +/// your_draw_rect_function(...) +/// break; +/// case ...: +/// // [...] +/// } +/// nk_clear(&ctx); +/// } +/// nk_free(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// #### Reference +/// Function | Description +/// --------------------------------|------------------------------------------- +/// nk_group_begin | Start a new group with internal scrollbar handling +/// nk_group_begin_titled | Start a new group with separeted name and title and internal scrollbar handling +/// nk_group_end | Ends a group. Should only be called if nk_group_begin returned non-zero +/// nk_group_scrolled_offset_begin | Start a new group with manual separated handling of scrollbar x- and y-offset +/// nk_group_scrolled_begin | Start a new group with manual scrollbar handling +/// nk_group_scrolled_end | Ends a group with manual scrollbar handling. Should only be called if nk_group_begin returned non-zero +/// nk_group_get_scroll | Gets the scroll offset for the given group +/// nk_group_set_scroll | Sets the scroll offset for the given group +*/ +/*/// #### nk_group_begin +/// Starts a new widget group. Requires a previous layouting function to specify a pos/size. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_group_begin(struct nk_context*, const char *title, nk_flags); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __title__ | Must be an unique identifier for this group that is also used for the group header +/// __flags__ | Window flags defined in the nk_panel_flags section with a number of different group behaviors +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +NK_API nk_bool nk_group_begin(struct nk_context*, const char *title, nk_flags); +/*/// #### nk_group_begin_titled +/// Starts a new widget group. Requires a previous layouting function to specify a pos/size. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_group_begin_titled(struct nk_context*, const char *name, const char *title, nk_flags); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __id__ | Must be an unique identifier for this group +/// __title__ | Group header title +/// __flags__ | Window flags defined in the nk_panel_flags section with a number of different group behaviors +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +NK_API nk_bool nk_group_begin_titled(struct nk_context*, const char *name, const char *title, nk_flags); +/*/// #### nk_group_end +/// Ends a widget group +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_group_end(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +*/ +NK_API void nk_group_end(struct nk_context*); +/*/// #### nk_group_scrolled_offset_begin +/// starts a new widget group. requires a previous layouting function to specify +/// a size. Does not keep track of scrollbar. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_group_scrolled_offset_begin(struct nk_context*, nk_uint *x_offset, nk_uint *y_offset, const char *title, nk_flags flags); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __x_offset__| Scrollbar x-offset to offset all widgets inside the group horizontally. +/// __y_offset__| Scrollbar y-offset to offset all widgets inside the group vertically +/// __title__ | Window unique group title used to both identify and display in the group header +/// __flags__ | Window flags from the nk_panel_flags section +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +NK_API nk_bool nk_group_scrolled_offset_begin(struct nk_context*, nk_uint *x_offset, nk_uint *y_offset, const char *title, nk_flags flags); +/*/// #### nk_group_scrolled_begin +/// Starts a new widget group. requires a previous +/// layouting function to specify a size. Does not keep track of scrollbar. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_group_scrolled_begin(struct nk_context*, struct nk_scroll *off, const char *title, nk_flags); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __off__ | Both x- and y- scroll offset. Allows for manual scrollbar control +/// __title__ | Window unique group title used to both identify and display in the group header +/// __flags__ | Window flags from nk_panel_flags section +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +NK_API nk_bool nk_group_scrolled_begin(struct nk_context*, struct nk_scroll *off, const char *title, nk_flags); +/*/// #### nk_group_scrolled_end +/// Ends a widget group after calling nk_group_scrolled_offset_begin or nk_group_scrolled_begin. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_group_scrolled_end(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +*/ +NK_API void nk_group_scrolled_end(struct nk_context*); +/*/// #### nk_group_get_scroll +/// Gets the scroll position of the given group. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_group_get_scroll(struct nk_context*, const char *id, nk_uint *x_offset, nk_uint *y_offset); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// -------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __id__ | The id of the group to get the scroll position of +/// __x_offset__ | A pointer to the x offset output (or NULL to ignore) +/// __y_offset__ | A pointer to the y offset output (or NULL to ignore) +*/ +NK_API void nk_group_get_scroll(struct nk_context*, const char *id, nk_uint *x_offset, nk_uint *y_offset); +/*/// #### nk_group_set_scroll +/// Sets the scroll position of the given group. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_group_set_scroll(struct nk_context*, const char *id, nk_uint x_offset, nk_uint y_offset); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// -------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __id__ | The id of the group to scroll +/// __x_offset__ | The x offset to scroll to +/// __y_offset__ | The y offset to scroll to +*/ +NK_API void nk_group_set_scroll(struct nk_context*, const char *id, nk_uint x_offset, nk_uint y_offset); +/* ============================================================================= + * + * TREE + * + * ============================================================================= +/// ### Tree +/// Trees represent two different concept. First the concept of a collapsable +/// UI section that can be either in a hidden or visibile state. They allow the UI +/// user to selectively minimize the current set of visible UI to comprehend. +/// The second concept are tree widgets for visual UI representation of trees.

+/// +/// Trees thereby can be nested for tree representations and multiple nested +/// collapsable UI sections. All trees are started by calling of the +/// `nk_tree_xxx_push_tree` functions and ended by calling one of the +/// `nk_tree_xxx_pop_xxx()` functions. Each starting functions takes a title label +/// and optionally an image to be displayed and the initial collapse state from +/// the nk_collapse_states section.

+/// +/// The runtime state of the tree is either stored outside the library by the caller +/// or inside which requires a unique ID. The unique ID can either be generated +/// automatically from `__FILE__` and `__LINE__` with function `nk_tree_push`, +/// by `__FILE__` and a user provided ID generated for example by loop index with +/// function `nk_tree_push_id` or completely provided from outside by user with +/// function `nk_tree_push_hashed`. +/// +/// #### Usage +/// To create a tree you have to call one of the seven `nk_tree_xxx_push_xxx` +/// functions to start a collapsable UI section and `nk_tree_xxx_pop` to mark the +/// end. +/// Each starting function will either return `false(0)` if the tree is collapsed +/// or hidden and therefore does not need to be filled with content or `true(1)` +/// if visible and required to be filled. +/// +/// !!! Note +/// The tree header does not require and layouting function and instead +/// calculates a auto height based on the currently used font size +/// +/// The tree ending functions only need to be called if the tree content is +/// actually visible. So make sure the tree push function is guarded by `if` +/// and the pop call is only taken if the tree is visible. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// if (nk_tree_push(ctx, NK_TREE_TAB, "Tree", NK_MINIMIZED)) { +/// nk_layout_row_dynamic(...); +/// nk_widget(...); +/// nk_tree_pop(ctx); +/// } +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// #### Reference +/// Function | Description +/// ----------------------------|------------------------------------------- +/// nk_tree_push | Start a collapsable UI section with internal state management +/// nk_tree_push_id | Start a collapsable UI section with internal state management callable in a look +/// nk_tree_push_hashed | Start a collapsable UI section with internal state management with full control over internal unique ID use to store state +/// nk_tree_image_push | Start a collapsable UI section with image and label header +/// nk_tree_image_push_id | Start a collapsable UI section with image and label header and internal state management callable in a look +/// nk_tree_image_push_hashed | Start a collapsable UI section with image and label header and internal state management with full control over internal unique ID use to store state +/// nk_tree_pop | Ends a collapsable UI section +// +/// nk_tree_state_push | Start a collapsable UI section with external state management +/// nk_tree_state_image_push | Start a collapsable UI section with image and label header and external state management +/// nk_tree_state_pop | Ends a collapsabale UI section +/// +/// #### nk_tree_type +/// Flag | Description +/// ----------------|---------------------------------------- +/// NK_TREE_NODE | Highlighted tree header to mark a collapsable UI section +/// NK_TREE_TAB | Non-highighted tree header closer to tree representations +*/ +/*/// #### nk_tree_push +/// Starts a collapsable UI section with internal state management +/// !!! WARNING +/// To keep track of the runtime tree collapsable state this function uses +/// defines `__FILE__` and `__LINE__` to generate a unique ID. If you want +/// to call this function in a loop please use `nk_tree_push_id` or +/// `nk_tree_push_hashed` instead. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// #define nk_tree_push(ctx, type, title, state) +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node +/// __title__ | Label printed in the tree header +/// __state__ | Initial tree state value out of nk_collapse_states +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +#define nk_tree_push(ctx, type, title, state) nk_tree_push_hashed(ctx, type, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),__LINE__) +/*/// #### nk_tree_push_id +/// Starts a collapsable UI section with internal state management callable in a look +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// #define nk_tree_push_id(ctx, type, title, state, id) +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node +/// __title__ | Label printed in the tree header +/// __state__ | Initial tree state value out of nk_collapse_states +/// __id__ | Loop counter index if this function is called in a loop +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +#define nk_tree_push_id(ctx, type, title, state, id) nk_tree_push_hashed(ctx, type, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),id) +/*/// #### nk_tree_push_hashed +/// Start a collapsable UI section with internal state management with full +/// control over internal unique ID used to store state +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_tree_push_hashed(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node +/// __title__ | Label printed in the tree header +/// __state__ | Initial tree state value out of nk_collapse_states +/// __hash__ | Memory block or string to generate the ID from +/// __len__ | Size of passed memory block or string in __hash__ +/// __seed__ | Seeding value if this function is called in a loop or default to `0` +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +NK_API nk_bool nk_tree_push_hashed(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed); +/*/// #### nk_tree_image_push +/// Start a collapsable UI section with image and label header +/// !!! WARNING +/// To keep track of the runtime tree collapsable state this function uses +/// defines `__FILE__` and `__LINE__` to generate a unique ID. If you want +/// to call this function in a loop please use `nk_tree_image_push_id` or +/// `nk_tree_image_push_hashed` instead. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// #define nk_tree_image_push(ctx, type, img, title, state) +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node +/// __img__ | Image to display inside the header on the left of the label +/// __title__ | Label printed in the tree header +/// __state__ | Initial tree state value out of nk_collapse_states +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +#define nk_tree_image_push(ctx, type, img, title, state) nk_tree_image_push_hashed(ctx, type, img, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),__LINE__) +/*/// #### nk_tree_image_push_id +/// Start a collapsable UI section with image and label header and internal state +/// management callable in a look +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// #define nk_tree_image_push_id(ctx, type, img, title, state, id) +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node +/// __img__ | Image to display inside the header on the left of the label +/// __title__ | Label printed in the tree header +/// __state__ | Initial tree state value out of nk_collapse_states +/// __id__ | Loop counter index if this function is called in a loop +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +#define nk_tree_image_push_id(ctx, type, img, title, state, id) nk_tree_image_push_hashed(ctx, type, img, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),id) +/*/// #### nk_tree_image_push_hashed +/// Start a collapsable UI section with internal state management with full +/// control over internal unique ID used to store state +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_tree_image_push_hashed(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node +/// __img__ | Image to display inside the header on the left of the label +/// __title__ | Label printed in the tree header +/// __state__ | Initial tree state value out of nk_collapse_states +/// __hash__ | Memory block or string to generate the ID from +/// __len__ | Size of passed memory block or string in __hash__ +/// __seed__ | Seeding value if this function is called in a loop or default to `0` +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +NK_API nk_bool nk_tree_image_push_hashed(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed); +/*/// #### nk_tree_pop +/// Ends a collapsabale UI section +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_tree_pop(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling `nk_tree_xxx_push_xxx` +*/ +NK_API void nk_tree_pop(struct nk_context*); +/*/// #### nk_tree_state_push +/// Start a collapsable UI section with external state management +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_tree_state_push(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states *state); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling `nk_tree_xxx_push_xxx` +/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node +/// __title__ | Label printed in the tree header +/// __state__ | Persistent state to update +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +NK_API nk_bool nk_tree_state_push(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states *state); +/*/// #### nk_tree_state_image_push +/// Start a collapsable UI section with image and label header and external state management +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_tree_state_image_push(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states *state); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling `nk_tree_xxx_push_xxx` +/// __img__ | Image to display inside the header on the left of the label +/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node +/// __title__ | Label printed in the tree header +/// __state__ | Persistent state to update +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +NK_API nk_bool nk_tree_state_image_push(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states *state); +/*/// #### nk_tree_state_pop +/// Ends a collapsabale UI section +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_tree_state_pop(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling `nk_tree_xxx_push_xxx` +*/ +NK_API void nk_tree_state_pop(struct nk_context*); + +#define nk_tree_element_push(ctx, type, title, state, sel) nk_tree_element_push_hashed(ctx, type, title, state, sel, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),__LINE__) +#define nk_tree_element_push_id(ctx, type, title, state, sel, id) nk_tree_element_push_hashed(ctx, type, title, state, sel, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),id) +NK_API nk_bool nk_tree_element_push_hashed(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states initial_state, nk_bool *selected, const char *hash, int len, int seed); +NK_API nk_bool nk_tree_element_image_push_hashed(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states initial_state, nk_bool *selected, const char *hash, int len,int seed); +NK_API void nk_tree_element_pop(struct nk_context*); + +/* ============================================================================= + * + * LIST VIEW + * + * ============================================================================= */ +struct nk_list_view { +/* public: */ + int begin, end, count; +/* private: */ + int total_height; + struct nk_context *ctx; + nk_uint *scroll_pointer; + nk_uint scroll_value; +}; +NK_API nk_bool nk_list_view_begin(struct nk_context*, struct nk_list_view *out, const char *id, nk_flags, int row_height, int row_count); +NK_API void nk_list_view_end(struct nk_list_view*); +/* ============================================================================= + * + * WIDGET + * + * ============================================================================= */ +enum nk_widget_layout_states { + NK_WIDGET_INVALID, /* The widget cannot be seen and is completely out of view */ + NK_WIDGET_VALID, /* The widget is completely inside the window and can be updated and drawn */ + NK_WIDGET_ROM /* The widget is partially visible and cannot be updated */ +}; +enum nk_widget_states { + NK_WIDGET_STATE_MODIFIED = NK_FLAG(1), + NK_WIDGET_STATE_INACTIVE = NK_FLAG(2), /* widget is neither active nor hovered */ + NK_WIDGET_STATE_ENTERED = NK_FLAG(3), /* widget has been hovered on the current frame */ + NK_WIDGET_STATE_HOVER = NK_FLAG(4), /* widget is being hovered */ + NK_WIDGET_STATE_ACTIVED = NK_FLAG(5),/* widget is currently activated */ + NK_WIDGET_STATE_LEFT = NK_FLAG(6), /* widget is from this frame on not hovered anymore */ + NK_WIDGET_STATE_HOVERED = NK_WIDGET_STATE_HOVER|NK_WIDGET_STATE_MODIFIED, /* widget is being hovered */ + NK_WIDGET_STATE_ACTIVE = NK_WIDGET_STATE_ACTIVED|NK_WIDGET_STATE_MODIFIED /* widget is currently activated */ +}; +NK_API enum nk_widget_layout_states nk_widget(struct nk_rect*, const struct nk_context*); +NK_API enum nk_widget_layout_states nk_widget_fitting(struct nk_rect*, struct nk_context*, struct nk_vec2); +NK_API struct nk_rect nk_widget_bounds(struct nk_context*); +NK_API struct nk_vec2 nk_widget_position(struct nk_context*); +NK_API struct nk_vec2 nk_widget_size(struct nk_context*); +NK_API float nk_widget_width(struct nk_context*); +NK_API float nk_widget_height(struct nk_context*); +NK_API nk_bool nk_widget_is_hovered(struct nk_context*); +NK_API nk_bool nk_widget_is_mouse_clicked(struct nk_context*, enum nk_buttons); +NK_API nk_bool nk_widget_has_mouse_click_down(struct nk_context*, enum nk_buttons, nk_bool down); +NK_API void nk_spacing(struct nk_context*, int cols); +/* ============================================================================= + * + * TEXT + * + * ============================================================================= */ +enum nk_text_align { + NK_TEXT_ALIGN_LEFT = 0x01, + NK_TEXT_ALIGN_CENTERED = 0x02, + NK_TEXT_ALIGN_RIGHT = 0x04, + NK_TEXT_ALIGN_TOP = 0x08, + NK_TEXT_ALIGN_MIDDLE = 0x10, + NK_TEXT_ALIGN_BOTTOM = 0x20 +}; +enum nk_text_alignment { + NK_TEXT_LEFT = NK_TEXT_ALIGN_MIDDLE|NK_TEXT_ALIGN_LEFT, + NK_TEXT_CENTERED = NK_TEXT_ALIGN_MIDDLE|NK_TEXT_ALIGN_CENTERED, + NK_TEXT_RIGHT = NK_TEXT_ALIGN_MIDDLE|NK_TEXT_ALIGN_RIGHT +}; +NK_API void nk_text(struct nk_context*, const char*, int, nk_flags); +NK_API void nk_text_colored(struct nk_context*, const char*, int, nk_flags, struct nk_color); +NK_API void nk_text_wrap(struct nk_context*, const char*, int); +NK_API void nk_text_wrap_colored(struct nk_context*, const char*, int, struct nk_color); +NK_API void nk_label(struct nk_context*, const char*, nk_flags align); +NK_API void nk_label_colored(struct nk_context*, const char*, nk_flags align, struct nk_color); +NK_API void nk_label_wrap(struct nk_context*, const char*); +NK_API void nk_label_colored_wrap(struct nk_context*, const char*, struct nk_color); +NK_API void nk_image(struct nk_context*, struct nk_image); +NK_API void nk_image_color(struct nk_context*, struct nk_image, struct nk_color); +#ifdef NK_INCLUDE_STANDARD_VARARGS +NK_API void nk_labelf(struct nk_context*, nk_flags, NK_PRINTF_FORMAT_STRING const char*, ...) NK_PRINTF_VARARG_FUNC(3); +NK_API void nk_labelf_colored(struct nk_context*, nk_flags, struct nk_color, NK_PRINTF_FORMAT_STRING const char*,...) NK_PRINTF_VARARG_FUNC(4); +NK_API void nk_labelf_wrap(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*,...) NK_PRINTF_VARARG_FUNC(2); +NK_API void nk_labelf_colored_wrap(struct nk_context*, struct nk_color, NK_PRINTF_FORMAT_STRING const char*,...) NK_PRINTF_VARARG_FUNC(3); +NK_API void nk_labelfv(struct nk_context*, nk_flags, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(3); +NK_API void nk_labelfv_colored(struct nk_context*, nk_flags, struct nk_color, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(4); +NK_API void nk_labelfv_wrap(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(2); +NK_API void nk_labelfv_colored_wrap(struct nk_context*, struct nk_color, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(3); +NK_API void nk_value_bool(struct nk_context*, const char *prefix, int); +NK_API void nk_value_int(struct nk_context*, const char *prefix, int); +NK_API void nk_value_uint(struct nk_context*, const char *prefix, unsigned int); +NK_API void nk_value_float(struct nk_context*, const char *prefix, float); +NK_API void nk_value_color_byte(struct nk_context*, const char *prefix, struct nk_color); +NK_API void nk_value_color_float(struct nk_context*, const char *prefix, struct nk_color); +NK_API void nk_value_color_hex(struct nk_context*, const char *prefix, struct nk_color); +#endif +/* ============================================================================= + * + * BUTTON + * + * ============================================================================= */ +NK_API nk_bool nk_button_text(struct nk_context*, const char *title, int len); +NK_API nk_bool nk_button_label(struct nk_context*, const char *title); +NK_API nk_bool nk_button_color(struct nk_context*, struct nk_color); +NK_API nk_bool nk_button_symbol(struct nk_context*, enum nk_symbol_type); +NK_API nk_bool nk_button_image(struct nk_context*, struct nk_image img); +NK_API nk_bool nk_button_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags text_alignment); +NK_API nk_bool nk_button_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); +NK_API nk_bool nk_button_image_label(struct nk_context*, struct nk_image img, const char*, nk_flags text_alignment); +NK_API nk_bool nk_button_image_text(struct nk_context*, struct nk_image img, const char*, int, nk_flags alignment); +NK_API nk_bool nk_button_text_styled(struct nk_context*, const struct nk_style_button*, const char *title, int len); +NK_API nk_bool nk_button_label_styled(struct nk_context*, const struct nk_style_button*, const char *title); +NK_API nk_bool nk_button_symbol_styled(struct nk_context*, const struct nk_style_button*, enum nk_symbol_type); +NK_API nk_bool nk_button_image_styled(struct nk_context*, const struct nk_style_button*, struct nk_image img); +NK_API nk_bool nk_button_symbol_text_styled(struct nk_context*,const struct nk_style_button*, enum nk_symbol_type, const char*, int, nk_flags alignment); +NK_API nk_bool nk_button_symbol_label_styled(struct nk_context *ctx, const struct nk_style_button *style, enum nk_symbol_type symbol, const char *title, nk_flags align); +NK_API nk_bool nk_button_image_label_styled(struct nk_context*,const struct nk_style_button*, struct nk_image img, const char*, nk_flags text_alignment); +NK_API nk_bool nk_button_image_text_styled(struct nk_context*,const struct nk_style_button*, struct nk_image img, const char*, int, nk_flags alignment); +NK_API void nk_button_set_behavior(struct nk_context*, enum nk_button_behavior); +NK_API nk_bool nk_button_push_behavior(struct nk_context*, enum nk_button_behavior); +NK_API nk_bool nk_button_pop_behavior(struct nk_context*); +/* ============================================================================= + * + * CHECKBOX + * + * ============================================================================= */ +NK_API nk_bool nk_check_label(struct nk_context*, const char*, nk_bool active); +NK_API nk_bool nk_check_text(struct nk_context*, const char*, int, nk_bool active); +NK_API unsigned nk_check_flags_label(struct nk_context*, const char*, unsigned int flags, unsigned int value); +NK_API unsigned nk_check_flags_text(struct nk_context*, const char*, int, unsigned int flags, unsigned int value); +NK_API nk_bool nk_checkbox_label(struct nk_context*, const char*, nk_bool *active); +NK_API nk_bool nk_checkbox_text(struct nk_context*, const char*, int, nk_bool *active); +NK_API nk_bool nk_checkbox_flags_label(struct nk_context*, const char*, unsigned int *flags, unsigned int value); +NK_API nk_bool nk_checkbox_flags_text(struct nk_context*, const char*, int, unsigned int *flags, unsigned int value); +/* ============================================================================= + * + * RADIO BUTTON + * + * ============================================================================= */ +NK_API nk_bool nk_radio_label(struct nk_context*, const char*, nk_bool *active); +NK_API nk_bool nk_radio_text(struct nk_context*, const char*, int, nk_bool *active); +NK_API nk_bool nk_option_label(struct nk_context*, const char*, nk_bool active); +NK_API nk_bool nk_option_text(struct nk_context*, const char*, int, nk_bool active); +/* ============================================================================= + * + * SELECTABLE + * + * ============================================================================= */ +NK_API nk_bool nk_selectable_label(struct nk_context*, const char*, nk_flags align, nk_bool *value); +NK_API nk_bool nk_selectable_text(struct nk_context*, const char*, int, nk_flags align, nk_bool *value); +NK_API nk_bool nk_selectable_image_label(struct nk_context*,struct nk_image, const char*, nk_flags align, nk_bool *value); +NK_API nk_bool nk_selectable_image_text(struct nk_context*,struct nk_image, const char*, int, nk_flags align, nk_bool *value); +NK_API nk_bool nk_selectable_symbol_label(struct nk_context*,enum nk_symbol_type, const char*, nk_flags align, nk_bool *value); +NK_API nk_bool nk_selectable_symbol_text(struct nk_context*,enum nk_symbol_type, const char*, int, nk_flags align, nk_bool *value); + +NK_API nk_bool nk_select_label(struct nk_context*, const char*, nk_flags align, nk_bool value); +NK_API nk_bool nk_select_text(struct nk_context*, const char*, int, nk_flags align, nk_bool value); +NK_API nk_bool nk_select_image_label(struct nk_context*, struct nk_image,const char*, nk_flags align, nk_bool value); +NK_API nk_bool nk_select_image_text(struct nk_context*, struct nk_image,const char*, int, nk_flags align, nk_bool value); +NK_API nk_bool nk_select_symbol_label(struct nk_context*,enum nk_symbol_type, const char*, nk_flags align, nk_bool value); +NK_API nk_bool nk_select_symbol_text(struct nk_context*,enum nk_symbol_type, const char*, int, nk_flags align, nk_bool value); + +/* ============================================================================= + * + * SLIDER + * + * ============================================================================= */ +NK_API float nk_slide_float(struct nk_context*, float min, float val, float max, float step); +NK_API int nk_slide_int(struct nk_context*, int min, int val, int max, int step); +NK_API nk_bool nk_slider_float(struct nk_context*, float min, float *val, float max, float step); +NK_API nk_bool nk_slider_int(struct nk_context*, int min, int *val, int max, int step); +/* ============================================================================= + * + * PROGRESSBAR + * + * ============================================================================= */ +NK_API nk_bool nk_progress(struct nk_context*, nk_size *cur, nk_size max, nk_bool modifyable); +NK_API nk_size nk_prog(struct nk_context*, nk_size cur, nk_size max, nk_bool modifyable); + +/* ============================================================================= + * + * COLOR PICKER + * + * ============================================================================= */ +NK_API struct nk_colorf nk_color_picker(struct nk_context*, struct nk_colorf, enum nk_color_format); +NK_API nk_bool nk_color_pick(struct nk_context*, struct nk_colorf*, enum nk_color_format); +/* ============================================================================= + * + * PROPERTIES + * + * ============================================================================= +/// ### Properties +/// Properties are the main value modification widgets in Nuklear. Changing a value +/// can be achieved by dragging, adding/removing incremental steps on button click +/// or by directly typing a number. +/// +/// #### Usage +/// Each property requires a unique name for identifaction that is also used for +/// displaying a label. If you want to use the same name multiple times make sure +/// add a '#' before your name. The '#' will not be shown but will generate a +/// unique ID. Each propery also takes in a minimum and maximum value. If you want +/// to make use of the complete number range of a type just use the provided +/// type limits from `limits.h`. For example `INT_MIN` and `INT_MAX` for +/// `nk_property_int` and `nk_propertyi`. In additional each property takes in +/// a increment value that will be added or subtracted if either the increment +/// decrement button is clicked. Finally there is a value for increment per pixel +/// dragged that is added or subtracted from the value. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int value = 0; +/// struct nk_context ctx; +/// nk_init_xxx(&ctx, ...); +/// while (1) { +/// // Input +/// Event evt; +/// nk_input_begin(&ctx); +/// while (GetEvent(&evt)) { +/// if (evt.type == MOUSE_MOVE) +/// nk_input_motion(&ctx, evt.motion.x, evt.motion.y); +/// else if (evt.type == [...]) { +/// nk_input_xxx(...); +/// } +/// } +/// nk_input_end(&ctx); +/// // +/// // Window +/// if (nk_begin_xxx(...) { +/// // Property +/// nk_layout_row_dynamic(...); +/// nk_property_int(ctx, "ID", INT_MIN, &value, INT_MAX, 1, 1); +/// } +/// nk_end(ctx); +/// // +/// // Draw +/// const struct nk_command *cmd = 0; +/// nk_foreach(cmd, &ctx) { +/// switch (cmd->type) { +/// case NK_COMMAND_LINE: +/// your_draw_line_function(...) +/// break; +/// case NK_COMMAND_RECT +/// your_draw_rect_function(...) +/// break; +/// case ...: +/// // [...] +/// } +/// nk_clear(&ctx); +/// } +/// nk_free(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// #### Reference +/// Function | Description +/// --------------------|------------------------------------------- +/// nk_property_int | Integer property directly modifing a passed in value +/// nk_property_float | Float property directly modifing a passed in value +/// nk_property_double | Double property directly modifing a passed in value +/// nk_propertyi | Integer property returning the modified int value +/// nk_propertyf | Float property returning the modified float value +/// nk_propertyd | Double property returning the modified double value +/// +*/ +/*/// #### nk_property_int +/// Integer property directly modifing a passed in value +/// !!! WARNING +/// To generate a unique property ID using the same label make sure to insert +/// a `#` at the beginning. It will not be shown but guarantees correct behavior. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_property_int(struct nk_context *ctx, const char *name, int min, int *val, int max, int step, float inc_per_pixel); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// --------------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function +/// __name__ | String used both as a label as well as a unique identifier +/// __min__ | Minimum value not allowed to be underflown +/// __val__ | Integer pointer to be modified +/// __max__ | Maximum value not allowed to be overflown +/// __step__ | Increment added and subtracted on increment and decrement button +/// __inc_per_pixel__ | Value per pixel added or subtracted on dragging +*/ +NK_API void nk_property_int(struct nk_context*, const char *name, int min, int *val, int max, int step, float inc_per_pixel); +/*/// #### nk_property_float +/// Float property directly modifing a passed in value +/// !!! WARNING +/// To generate a unique property ID using the same label make sure to insert +/// a `#` at the beginning. It will not be shown but guarantees correct behavior. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_property_float(struct nk_context *ctx, const char *name, float min, float *val, float max, float step, float inc_per_pixel); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// --------------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function +/// __name__ | String used both as a label as well as a unique identifier +/// __min__ | Minimum value not allowed to be underflown +/// __val__ | Float pointer to be modified +/// __max__ | Maximum value not allowed to be overflown +/// __step__ | Increment added and subtracted on increment and decrement button +/// __inc_per_pixel__ | Value per pixel added or subtracted on dragging +*/ +NK_API void nk_property_float(struct nk_context*, const char *name, float min, float *val, float max, float step, float inc_per_pixel); +/*/// #### nk_property_double +/// Double property directly modifing a passed in value +/// !!! WARNING +/// To generate a unique property ID using the same label make sure to insert +/// a `#` at the beginning. It will not be shown but guarantees correct behavior. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_property_double(struct nk_context *ctx, const char *name, double min, double *val, double max, double step, double inc_per_pixel); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// --------------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function +/// __name__ | String used both as a label as well as a unique identifier +/// __min__ | Minimum value not allowed to be underflown +/// __val__ | Double pointer to be modified +/// __max__ | Maximum value not allowed to be overflown +/// __step__ | Increment added and subtracted on increment and decrement button +/// __inc_per_pixel__ | Value per pixel added or subtracted on dragging +*/ +NK_API void nk_property_double(struct nk_context*, const char *name, double min, double *val, double max, double step, float inc_per_pixel); +/*/// #### nk_propertyi +/// Integer property modifing a passed in value and returning the new value +/// !!! WARNING +/// To generate a unique property ID using the same label make sure to insert +/// a `#` at the beginning. It will not be shown but guarantees correct behavior. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_propertyi(struct nk_context *ctx, const char *name, int min, int val, int max, int step, float inc_per_pixel); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// --------------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function +/// __name__ | String used both as a label as well as a unique identifier +/// __min__ | Minimum value not allowed to be underflown +/// __val__ | Current integer value to be modified and returned +/// __max__ | Maximum value not allowed to be overflown +/// __step__ | Increment added and subtracted on increment and decrement button +/// __inc_per_pixel__ | Value per pixel added or subtracted on dragging +/// +/// Returns the new modified integer value +*/ +NK_API nk_bool nk_propertyi(struct nk_context*, const char *name, int min, int val, int max, int step, float inc_per_pixel); +/*/// #### nk_propertyf +/// Float property modifing a passed in value and returning the new value +/// !!! WARNING +/// To generate a unique property ID using the same label make sure to insert +/// a `#` at the beginning. It will not be shown but guarantees correct behavior. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// float nk_propertyf(struct nk_context *ctx, const char *name, float min, float val, float max, float step, float inc_per_pixel); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// --------------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function +/// __name__ | String used both as a label as well as a unique identifier +/// __min__ | Minimum value not allowed to be underflown +/// __val__ | Current float value to be modified and returned +/// __max__ | Maximum value not allowed to be overflown +/// __step__ | Increment added and subtracted on increment and decrement button +/// __inc_per_pixel__ | Value per pixel added or subtracted on dragging +/// +/// Returns the new modified float value +*/ +NK_API float nk_propertyf(struct nk_context*, const char *name, float min, float val, float max, float step, float inc_per_pixel); +/*/// #### nk_propertyd +/// Float property modifing a passed in value and returning the new value +/// !!! WARNING +/// To generate a unique property ID using the same label make sure to insert +/// a `#` at the beginning. It will not be shown but guarantees correct behavior. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// float nk_propertyd(struct nk_context *ctx, const char *name, double min, double val, double max, double step, double inc_per_pixel); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// --------------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function +/// __name__ | String used both as a label as well as a unique identifier +/// __min__ | Minimum value not allowed to be underflown +/// __val__ | Current double value to be modified and returned +/// __max__ | Maximum value not allowed to be overflown +/// __step__ | Increment added and subtracted on increment and decrement button +/// __inc_per_pixel__ | Value per pixel added or subtracted on dragging +/// +/// Returns the new modified double value +*/ +NK_API double nk_propertyd(struct nk_context*, const char *name, double min, double val, double max, double step, float inc_per_pixel); +/* ============================================================================= + * + * TEXT EDIT + * + * ============================================================================= */ +enum nk_edit_flags { + NK_EDIT_DEFAULT = 0, + NK_EDIT_READ_ONLY = NK_FLAG(0), + NK_EDIT_AUTO_SELECT = NK_FLAG(1), + NK_EDIT_SIG_ENTER = NK_FLAG(2), + NK_EDIT_ALLOW_TAB = NK_FLAG(3), + NK_EDIT_NO_CURSOR = NK_FLAG(4), + NK_EDIT_SELECTABLE = NK_FLAG(5), + NK_EDIT_CLIPBOARD = NK_FLAG(6), + NK_EDIT_CTRL_ENTER_NEWLINE = NK_FLAG(7), + NK_EDIT_NO_HORIZONTAL_SCROLL = NK_FLAG(8), + NK_EDIT_ALWAYS_INSERT_MODE = NK_FLAG(9), + NK_EDIT_MULTILINE = NK_FLAG(10), + NK_EDIT_GOTO_END_ON_ACTIVATE = NK_FLAG(11) +}; +enum nk_edit_types { + NK_EDIT_SIMPLE = NK_EDIT_ALWAYS_INSERT_MODE, + NK_EDIT_FIELD = NK_EDIT_SIMPLE|NK_EDIT_SELECTABLE|NK_EDIT_CLIPBOARD, + NK_EDIT_BOX = NK_EDIT_ALWAYS_INSERT_MODE| NK_EDIT_SELECTABLE| NK_EDIT_MULTILINE|NK_EDIT_ALLOW_TAB|NK_EDIT_CLIPBOARD, + NK_EDIT_EDITOR = NK_EDIT_SELECTABLE|NK_EDIT_MULTILINE|NK_EDIT_ALLOW_TAB| NK_EDIT_CLIPBOARD +}; +enum nk_edit_events { + NK_EDIT_ACTIVE = NK_FLAG(0), /* edit widget is currently being modified */ + NK_EDIT_INACTIVE = NK_FLAG(1), /* edit widget is not active and is not being modified */ + NK_EDIT_ACTIVATED = NK_FLAG(2), /* edit widget went from state inactive to state active */ + NK_EDIT_DEACTIVATED = NK_FLAG(3), /* edit widget went from state active to state inactive */ + NK_EDIT_COMMITED = NK_FLAG(4) /* edit widget has received an enter and lost focus */ +}; +NK_API nk_flags nk_edit_string(struct nk_context*, nk_flags, char *buffer, int *len, int max, nk_plugin_filter); +NK_API nk_flags nk_edit_string_zero_terminated(struct nk_context*, nk_flags, char *buffer, int max, nk_plugin_filter); +NK_API nk_flags nk_edit_buffer(struct nk_context*, nk_flags, struct nk_text_edit*, nk_plugin_filter); +NK_API void nk_edit_focus(struct nk_context*, nk_flags flags); +NK_API void nk_edit_unfocus(struct nk_context*); +/* ============================================================================= + * + * CHART + * + * ============================================================================= */ +NK_API nk_bool nk_chart_begin(struct nk_context*, enum nk_chart_type, int num, float min, float max); +NK_API nk_bool nk_chart_begin_colored(struct nk_context*, enum nk_chart_type, struct nk_color, struct nk_color active, int num, float min, float max); +NK_API void nk_chart_add_slot(struct nk_context *ctx, const enum nk_chart_type, int count, float min_value, float max_value); +NK_API void nk_chart_add_slot_colored(struct nk_context *ctx, const enum nk_chart_type, struct nk_color, struct nk_color active, int count, float min_value, float max_value); +NK_API nk_flags nk_chart_push(struct nk_context*, float); +NK_API nk_flags nk_chart_push_slot(struct nk_context*, float, int); +NK_API void nk_chart_end(struct nk_context*); +NK_API void nk_plot(struct nk_context*, enum nk_chart_type, const float *values, int count, int offset); +NK_API void nk_plot_function(struct nk_context*, enum nk_chart_type, void *userdata, float(*value_getter)(void* user, int index), int count, int offset); +/* ============================================================================= + * + * POPUP + * + * ============================================================================= */ +NK_API nk_bool nk_popup_begin(struct nk_context*, enum nk_popup_type, const char*, nk_flags, struct nk_rect bounds); +NK_API void nk_popup_close(struct nk_context*); +NK_API void nk_popup_end(struct nk_context*); +NK_API void nk_popup_get_scroll(struct nk_context*, nk_uint *offset_x, nk_uint *offset_y); +NK_API void nk_popup_set_scroll(struct nk_context*, nk_uint offset_x, nk_uint offset_y); +/* ============================================================================= + * + * COMBOBOX + * + * ============================================================================= */ +NK_API int nk_combo(struct nk_context*, const char **items, int count, int selected, int item_height, struct nk_vec2 size); +NK_API int nk_combo_separator(struct nk_context*, const char *items_separated_by_separator, int separator, int selected, int count, int item_height, struct nk_vec2 size); +NK_API int nk_combo_string(struct nk_context*, const char *items_separated_by_zeros, int selected, int count, int item_height, struct nk_vec2 size); +NK_API int nk_combo_callback(struct nk_context*, void(*item_getter)(void*, int, const char**), void *userdata, int selected, int count, int item_height, struct nk_vec2 size); +NK_API void nk_combobox(struct nk_context*, const char **items, int count, int *selected, int item_height, struct nk_vec2 size); +NK_API void nk_combobox_string(struct nk_context*, const char *items_separated_by_zeros, int *selected, int count, int item_height, struct nk_vec2 size); +NK_API void nk_combobox_separator(struct nk_context*, const char *items_separated_by_separator, int separator, int *selected, int count, int item_height, struct nk_vec2 size); +NK_API void nk_combobox_callback(struct nk_context*, void(*item_getter)(void*, int, const char**), void*, int *selected, int count, int item_height, struct nk_vec2 size); +/* ============================================================================= + * + * ABSTRACT COMBOBOX + * + * ============================================================================= */ +NK_API nk_bool nk_combo_begin_text(struct nk_context*, const char *selected, int, struct nk_vec2 size); +NK_API nk_bool nk_combo_begin_label(struct nk_context*, const char *selected, struct nk_vec2 size); +NK_API nk_bool nk_combo_begin_color(struct nk_context*, struct nk_color color, struct nk_vec2 size); +NK_API nk_bool nk_combo_begin_symbol(struct nk_context*, enum nk_symbol_type, struct nk_vec2 size); +NK_API nk_bool nk_combo_begin_symbol_label(struct nk_context*, const char *selected, enum nk_symbol_type, struct nk_vec2 size); +NK_API nk_bool nk_combo_begin_symbol_text(struct nk_context*, const char *selected, int, enum nk_symbol_type, struct nk_vec2 size); +NK_API nk_bool nk_combo_begin_image(struct nk_context*, struct nk_image img, struct nk_vec2 size); +NK_API nk_bool nk_combo_begin_image_label(struct nk_context*, const char *selected, struct nk_image, struct nk_vec2 size); +NK_API nk_bool nk_combo_begin_image_text(struct nk_context*, const char *selected, int, struct nk_image, struct nk_vec2 size); +NK_API nk_bool nk_combo_item_label(struct nk_context*, const char*, nk_flags alignment); +NK_API nk_bool nk_combo_item_text(struct nk_context*, const char*,int, nk_flags alignment); +NK_API nk_bool nk_combo_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment); +NK_API nk_bool nk_combo_item_image_text(struct nk_context*, struct nk_image, const char*, int,nk_flags alignment); +NK_API nk_bool nk_combo_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment); +NK_API nk_bool nk_combo_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); +NK_API void nk_combo_close(struct nk_context*); +NK_API void nk_combo_end(struct nk_context*); +/* ============================================================================= + * + * CONTEXTUAL + * + * ============================================================================= */ +NK_API nk_bool nk_contextual_begin(struct nk_context*, nk_flags, struct nk_vec2, struct nk_rect trigger_bounds); +NK_API nk_bool nk_contextual_item_text(struct nk_context*, const char*, int,nk_flags align); +NK_API nk_bool nk_contextual_item_label(struct nk_context*, const char*, nk_flags align); +NK_API nk_bool nk_contextual_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment); +NK_API nk_bool nk_contextual_item_image_text(struct nk_context*, struct nk_image, const char*, int len, nk_flags alignment); +NK_API nk_bool nk_contextual_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment); +NK_API nk_bool nk_contextual_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); +NK_API void nk_contextual_close(struct nk_context*); +NK_API void nk_contextual_end(struct nk_context*); +/* ============================================================================= + * + * TOOLTIP + * + * ============================================================================= */ +NK_API void nk_tooltip(struct nk_context*, const char*); +#ifdef NK_INCLUDE_STANDARD_VARARGS +NK_API void nk_tooltipf(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, ...) NK_PRINTF_VARARG_FUNC(2); +NK_API void nk_tooltipfv(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(2); +#endif +NK_API nk_bool nk_tooltip_begin(struct nk_context*, float width); +NK_API void nk_tooltip_end(struct nk_context*); +/* ============================================================================= + * + * MENU + * + * ============================================================================= */ +NK_API void nk_menubar_begin(struct nk_context*); +NK_API void nk_menubar_end(struct nk_context*); +NK_API nk_bool nk_menu_begin_text(struct nk_context*, const char* title, int title_len, nk_flags align, struct nk_vec2 size); +NK_API nk_bool nk_menu_begin_label(struct nk_context*, const char*, nk_flags align, struct nk_vec2 size); +NK_API nk_bool nk_menu_begin_image(struct nk_context*, const char*, struct nk_image, struct nk_vec2 size); +NK_API nk_bool nk_menu_begin_image_text(struct nk_context*, const char*, int,nk_flags align,struct nk_image, struct nk_vec2 size); +NK_API nk_bool nk_menu_begin_image_label(struct nk_context*, const char*, nk_flags align,struct nk_image, struct nk_vec2 size); +NK_API nk_bool nk_menu_begin_symbol(struct nk_context*, const char*, enum nk_symbol_type, struct nk_vec2 size); +NK_API nk_bool nk_menu_begin_symbol_text(struct nk_context*, const char*, int,nk_flags align,enum nk_symbol_type, struct nk_vec2 size); +NK_API nk_bool nk_menu_begin_symbol_label(struct nk_context*, const char*, nk_flags align,enum nk_symbol_type, struct nk_vec2 size); +NK_API nk_bool nk_menu_item_text(struct nk_context*, const char*, int,nk_flags align); +NK_API nk_bool nk_menu_item_label(struct nk_context*, const char*, nk_flags alignment); +NK_API nk_bool nk_menu_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment); +NK_API nk_bool nk_menu_item_image_text(struct nk_context*, struct nk_image, const char*, int len, nk_flags alignment); +NK_API nk_bool nk_menu_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); +NK_API nk_bool nk_menu_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment); +NK_API void nk_menu_close(struct nk_context*); +NK_API void nk_menu_end(struct nk_context*); +/* ============================================================================= + * + * STYLE + * + * ============================================================================= */ +enum nk_style_colors { + NK_COLOR_TEXT, + NK_COLOR_WINDOW, + NK_COLOR_HEADER, + NK_COLOR_BORDER, + NK_COLOR_BUTTON, + NK_COLOR_BUTTON_HOVER, + NK_COLOR_BUTTON_ACTIVE, + NK_COLOR_TOGGLE, + NK_COLOR_TOGGLE_HOVER, + NK_COLOR_TOGGLE_CURSOR, + NK_COLOR_SELECT, + NK_COLOR_SELECT_ACTIVE, + NK_COLOR_SLIDER, + NK_COLOR_SLIDER_CURSOR, + NK_COLOR_SLIDER_CURSOR_HOVER, + NK_COLOR_SLIDER_CURSOR_ACTIVE, + NK_COLOR_PROPERTY, + NK_COLOR_EDIT, + NK_COLOR_EDIT_CURSOR, + NK_COLOR_COMBO, + NK_COLOR_CHART, + NK_COLOR_CHART_COLOR, + NK_COLOR_CHART_COLOR_HIGHLIGHT, + NK_COLOR_SCROLLBAR, + NK_COLOR_SCROLLBAR_CURSOR, + NK_COLOR_SCROLLBAR_CURSOR_HOVER, + NK_COLOR_SCROLLBAR_CURSOR_ACTIVE, + NK_COLOR_TAB_HEADER, + NK_COLOR_COUNT +}; +enum nk_style_cursor { + NK_CURSOR_ARROW, + NK_CURSOR_TEXT, + NK_CURSOR_MOVE, + NK_CURSOR_RESIZE_VERTICAL, + NK_CURSOR_RESIZE_HORIZONTAL, + NK_CURSOR_RESIZE_TOP_LEFT_DOWN_RIGHT, + NK_CURSOR_RESIZE_TOP_RIGHT_DOWN_LEFT, + NK_CURSOR_COUNT +}; +NK_API void nk_style_default(struct nk_context*); +NK_API void nk_style_from_table(struct nk_context*, const struct nk_color*); +NK_API void nk_style_load_cursor(struct nk_context*, enum nk_style_cursor, const struct nk_cursor*); +NK_API void nk_style_load_all_cursors(struct nk_context*, struct nk_cursor*); +NK_API const char* nk_style_get_color_by_name(enum nk_style_colors); +NK_API void nk_style_set_font(struct nk_context*, const struct nk_user_font*); +NK_API nk_bool nk_style_set_cursor(struct nk_context*, enum nk_style_cursor); +NK_API void nk_style_show_cursor(struct nk_context*); +NK_API void nk_style_hide_cursor(struct nk_context*); + +NK_API nk_bool nk_style_push_font(struct nk_context*, const struct nk_user_font*); +NK_API nk_bool nk_style_push_float(struct nk_context*, float*, float); +NK_API nk_bool nk_style_push_vec2(struct nk_context*, struct nk_vec2*, struct nk_vec2); +NK_API nk_bool nk_style_push_style_item(struct nk_context*, struct nk_style_item*, struct nk_style_item); +NK_API nk_bool nk_style_push_flags(struct nk_context*, nk_flags*, nk_flags); +NK_API nk_bool nk_style_push_color(struct nk_context*, struct nk_color*, struct nk_color); + +NK_API nk_bool nk_style_pop_font(struct nk_context*); +NK_API nk_bool nk_style_pop_float(struct nk_context*); +NK_API nk_bool nk_style_pop_vec2(struct nk_context*); +NK_API nk_bool nk_style_pop_style_item(struct nk_context*); +NK_API nk_bool nk_style_pop_flags(struct nk_context*); +NK_API nk_bool nk_style_pop_color(struct nk_context*); +/* ============================================================================= + * + * COLOR + * + * ============================================================================= */ +NK_API struct nk_color nk_rgb(int r, int g, int b); +NK_API struct nk_color nk_rgb_iv(const int *rgb); +NK_API struct nk_color nk_rgb_bv(const nk_byte* rgb); +NK_API struct nk_color nk_rgb_f(float r, float g, float b); +NK_API struct nk_color nk_rgb_fv(const float *rgb); +NK_API struct nk_color nk_rgb_cf(struct nk_colorf c); +NK_API struct nk_color nk_rgb_hex(const char *rgb); + +NK_API struct nk_color nk_rgba(int r, int g, int b, int a); +NK_API struct nk_color nk_rgba_u32(nk_uint); +NK_API struct nk_color nk_rgba_iv(const int *rgba); +NK_API struct nk_color nk_rgba_bv(const nk_byte *rgba); +NK_API struct nk_color nk_rgba_f(float r, float g, float b, float a); +NK_API struct nk_color nk_rgba_fv(const float *rgba); +NK_API struct nk_color nk_rgba_cf(struct nk_colorf c); +NK_API struct nk_color nk_rgba_hex(const char *rgb); + +NK_API struct nk_colorf nk_hsva_colorf(float h, float s, float v, float a); +NK_API struct nk_colorf nk_hsva_colorfv(float *c); +NK_API void nk_colorf_hsva_f(float *out_h, float *out_s, float *out_v, float *out_a, struct nk_colorf in); +NK_API void nk_colorf_hsva_fv(float *hsva, struct nk_colorf in); + +NK_API struct nk_color nk_hsv(int h, int s, int v); +NK_API struct nk_color nk_hsv_iv(const int *hsv); +NK_API struct nk_color nk_hsv_bv(const nk_byte *hsv); +NK_API struct nk_color nk_hsv_f(float h, float s, float v); +NK_API struct nk_color nk_hsv_fv(const float *hsv); + +NK_API struct nk_color nk_hsva(int h, int s, int v, int a); +NK_API struct nk_color nk_hsva_iv(const int *hsva); +NK_API struct nk_color nk_hsva_bv(const nk_byte *hsva); +NK_API struct nk_color nk_hsva_f(float h, float s, float v, float a); +NK_API struct nk_color nk_hsva_fv(const float *hsva); + +/* color (conversion nuklear --> user) */ +NK_API void nk_color_f(float *r, float *g, float *b, float *a, struct nk_color); +NK_API void nk_color_fv(float *rgba_out, struct nk_color); +NK_API struct nk_colorf nk_color_cf(struct nk_color); +NK_API void nk_color_d(double *r, double *g, double *b, double *a, struct nk_color); +NK_API void nk_color_dv(double *rgba_out, struct nk_color); + +NK_API nk_uint nk_color_u32(struct nk_color); +NK_API void nk_color_hex_rgba(char *output, struct nk_color); +NK_API void nk_color_hex_rgb(char *output, struct nk_color); + +NK_API void nk_color_hsv_i(int *out_h, int *out_s, int *out_v, struct nk_color); +NK_API void nk_color_hsv_b(nk_byte *out_h, nk_byte *out_s, nk_byte *out_v, struct nk_color); +NK_API void nk_color_hsv_iv(int *hsv_out, struct nk_color); +NK_API void nk_color_hsv_bv(nk_byte *hsv_out, struct nk_color); +NK_API void nk_color_hsv_f(float *out_h, float *out_s, float *out_v, struct nk_color); +NK_API void nk_color_hsv_fv(float *hsv_out, struct nk_color); + +NK_API void nk_color_hsva_i(int *h, int *s, int *v, int *a, struct nk_color); +NK_API void nk_color_hsva_b(nk_byte *h, nk_byte *s, nk_byte *v, nk_byte *a, struct nk_color); +NK_API void nk_color_hsva_iv(int *hsva_out, struct nk_color); +NK_API void nk_color_hsva_bv(nk_byte *hsva_out, struct nk_color); +NK_API void nk_color_hsva_f(float *out_h, float *out_s, float *out_v, float *out_a, struct nk_color); +NK_API void nk_color_hsva_fv(float *hsva_out, struct nk_color); +/* ============================================================================= + * + * IMAGE + * + * ============================================================================= */ +NK_API nk_handle nk_handle_ptr(void*); +NK_API nk_handle nk_handle_id(int); +NK_API struct nk_image nk_image_handle(nk_handle); +NK_API struct nk_image nk_image_ptr(void*); +NK_API struct nk_image nk_image_id(int); +NK_API nk_bool nk_image_is_subimage(const struct nk_image* img); +NK_API struct nk_image nk_subimage_ptr(void*, unsigned short w, unsigned short h, struct nk_rect sub_region); +NK_API struct nk_image nk_subimage_id(int, unsigned short w, unsigned short h, struct nk_rect sub_region); +NK_API struct nk_image nk_subimage_handle(nk_handle, unsigned short w, unsigned short h, struct nk_rect sub_region); +/* ============================================================================= + * + * MATH + * + * ============================================================================= */ +NK_API nk_hash nk_murmur_hash(const void *key, int len, nk_hash seed); +NK_API void nk_triangle_from_direction(struct nk_vec2 *result, struct nk_rect r, float pad_x, float pad_y, enum nk_heading); + +NK_API struct nk_vec2 nk_vec2(float x, float y); +NK_API struct nk_vec2 nk_vec2i(int x, int y); +NK_API struct nk_vec2 nk_vec2v(const float *xy); +NK_API struct nk_vec2 nk_vec2iv(const int *xy); + +NK_API struct nk_rect nk_get_null_rect(void); +NK_API struct nk_rect nk_rect(float x, float y, float w, float h); +NK_API struct nk_rect nk_recti(int x, int y, int w, int h); +NK_API struct nk_rect nk_recta(struct nk_vec2 pos, struct nk_vec2 size); +NK_API struct nk_rect nk_rectv(const float *xywh); +NK_API struct nk_rect nk_rectiv(const int *xywh); +NK_API struct nk_vec2 nk_rect_pos(struct nk_rect); +NK_API struct nk_vec2 nk_rect_size(struct nk_rect); +/* ============================================================================= + * + * STRING + * + * ============================================================================= */ +NK_API int nk_strlen(const char *str); +NK_API int nk_stricmp(const char *s1, const char *s2); +NK_API int nk_stricmpn(const char *s1, const char *s2, int n); +NK_API int nk_strtoi(const char *str, const char **endptr); +NK_API float nk_strtof(const char *str, const char **endptr); +#ifndef NK_STRTOD +#define NK_STRTOD nk_strtod +NK_API double nk_strtod(const char *str, const char **endptr); +#endif +NK_API int nk_strfilter(const char *text, const char *regexp); +NK_API int nk_strmatch_fuzzy_string(char const *str, char const *pattern, int *out_score); +NK_API int nk_strmatch_fuzzy_text(const char *txt, int txt_len, const char *pattern, int *out_score); +/* ============================================================================= + * + * UTF-8 + * + * ============================================================================= */ +NK_API int nk_utf_decode(const char*, nk_rune*, int); +NK_API int nk_utf_encode(nk_rune, char*, int); +NK_API int nk_utf_len(const char*, int byte_len); +NK_API const char* nk_utf_at(const char *buffer, int length, int index, nk_rune *unicode, int *len); +/* =============================================================== + * + * FONT + * + * ===============================================================*/ +/* Font handling in this library was designed to be quite customizable and lets + you decide what you want to use and what you want to provide. There are three + different ways to use the font atlas. The first two will use your font + handling scheme and only requires essential data to run nuklear. The next + slightly more advanced features is font handling with vertex buffer output. + Finally the most complex API wise is using nuklear's font baking API. + + 1.) Using your own implementation without vertex buffer output + -------------------------------------------------------------- + So first up the easiest way to do font handling is by just providing a + `nk_user_font` struct which only requires the height in pixel of the used + font and a callback to calculate the width of a string. This way of handling + fonts is best fitted for using the normal draw shape command API where you + do all the text drawing yourself and the library does not require any kind + of deeper knowledge about which font handling mechanism you use. + IMPORTANT: the `nk_user_font` pointer provided to nuklear has to persist + over the complete life time! I know this sucks but it is currently the only + way to switch between fonts. + + float your_text_width_calculation(nk_handle handle, float height, const char *text, int len) + { + your_font_type *type = handle.ptr; + float text_width = ...; + return text_width; + } + + struct nk_user_font font; + font.userdata.ptr = &your_font_class_or_struct; + font.height = your_font_height; + font.width = your_text_width_calculation; + + struct nk_context ctx; + nk_init_default(&ctx, &font); + + 2.) Using your own implementation with vertex buffer output + -------------------------------------------------------------- + While the first approach works fine if you don't want to use the optional + vertex buffer output it is not enough if you do. To get font handling working + for these cases you have to provide two additional parameters inside the + `nk_user_font`. First a texture atlas handle used to draw text as subimages + of a bigger font atlas texture and a callback to query a character's glyph + information (offset, size, ...). So it is still possible to provide your own + font and use the vertex buffer output. + + float your_text_width_calculation(nk_handle handle, float height, const char *text, int len) + { + your_font_type *type = handle.ptr; + float text_width = ...; + return text_width; + } + void query_your_font_glyph(nk_handle handle, float font_height, struct nk_user_font_glyph *glyph, nk_rune codepoint, nk_rune next_codepoint) + { + your_font_type *type = handle.ptr; + glyph.width = ...; + glyph.height = ...; + glyph.xadvance = ...; + glyph.uv[0].x = ...; + glyph.uv[0].y = ...; + glyph.uv[1].x = ...; + glyph.uv[1].y = ...; + glyph.offset.x = ...; + glyph.offset.y = ...; + } + + struct nk_user_font font; + font.userdata.ptr = &your_font_class_or_struct; + font.height = your_font_height; + font.width = your_text_width_calculation; + font.query = query_your_font_glyph; + font.texture.id = your_font_texture; + + struct nk_context ctx; + nk_init_default(&ctx, &font); + + 3.) Nuklear font baker + ------------------------------------ + The final approach if you do not have a font handling functionality or don't + want to use it in this library is by using the optional font baker. + The font baker APIs can be used to create a font plus font atlas texture + and can be used with or without the vertex buffer output. + + It still uses the `nk_user_font` struct and the two different approaches + previously stated still work. The font baker is not located inside + `nk_context` like all other systems since it can be understood as more of + an extension to nuklear and does not really depend on any `nk_context` state. + + Font baker need to be initialized first by one of the nk_font_atlas_init_xxx + functions. If you don't care about memory just call the default version + `nk_font_atlas_init_default` which will allocate all memory from the standard library. + If you want to control memory allocation but you don't care if the allocated + memory is temporary and therefore can be freed directly after the baking process + is over or permanent you can call `nk_font_atlas_init`. + + After successfully initializing the font baker you can add Truetype(.ttf) fonts from + different sources like memory or from file by calling one of the `nk_font_atlas_add_xxx`. + functions. Adding font will permanently store each font, font config and ttf memory block(!) + inside the font atlas and allows to reuse the font atlas. If you don't want to reuse + the font baker by for example adding additional fonts you can call + `nk_font_atlas_cleanup` after the baking process is over (after calling nk_font_atlas_end). + + As soon as you added all fonts you wanted you can now start the baking process + for every selected glyph to image by calling `nk_font_atlas_bake`. + The baking process returns image memory, width and height which can be used to + either create your own image object or upload it to any graphics library. + No matter which case you finally have to call `nk_font_atlas_end` which + will free all temporary memory including the font atlas image so make sure + you created our texture beforehand. `nk_font_atlas_end` requires a handle + to your font texture or object and optionally fills a `struct nk_draw_null_texture` + which can be used for the optional vertex output. If you don't want it just + set the argument to `NULL`. + + At this point you are done and if you don't want to reuse the font atlas you + can call `nk_font_atlas_cleanup` to free all truetype blobs and configuration + memory. Finally if you don't use the font atlas and any of it's fonts anymore + you need to call `nk_font_atlas_clear` to free all memory still being used. + + struct nk_font_atlas atlas; + nk_font_atlas_init_default(&atlas); + nk_font_atlas_begin(&atlas); + nk_font *font = nk_font_atlas_add_from_file(&atlas, "Path/To/Your/TTF_Font.ttf", 13, 0); + nk_font *font2 = nk_font_atlas_add_from_file(&atlas, "Path/To/Your/TTF_Font2.ttf", 16, 0); + const void* img = nk_font_atlas_bake(&atlas, &img_width, &img_height, NK_FONT_ATLAS_RGBA32); + nk_font_atlas_end(&atlas, nk_handle_id(texture), 0); + + struct nk_context ctx; + nk_init_default(&ctx, &font->handle); + while (1) { + + } + nk_font_atlas_clear(&atlas); + + The font baker API is probably the most complex API inside this library and + I would suggest reading some of my examples `example/` to get a grip on how + to use the font atlas. There are a number of details I left out. For example + how to merge fonts, configure a font with `nk_font_config` to use other languages, + use another texture coordinate format and a lot more: + + struct nk_font_config cfg = nk_font_config(font_pixel_height); + cfg.merge_mode = nk_false or nk_true; + cfg.range = nk_font_korean_glyph_ranges(); + cfg.coord_type = NK_COORD_PIXEL; + nk_font *font = nk_font_atlas_add_from_file(&atlas, "Path/To/Your/TTF_Font.ttf", 13, &cfg); + +*/ +struct nk_user_font_glyph; +typedef float(*nk_text_width_f)(nk_handle, float h, const char*, int len); +typedef void(*nk_query_font_glyph_f)(nk_handle handle, float font_height, + struct nk_user_font_glyph *glyph, + nk_rune codepoint, nk_rune next_codepoint); + +#if defined(NK_INCLUDE_VERTEX_BUFFER_OUTPUT) || defined(NK_INCLUDE_SOFTWARE_FONT) +struct nk_user_font_glyph { + struct nk_vec2 uv[2]; + /* texture coordinates */ + struct nk_vec2 offset; + /* offset between top left and glyph */ + float width, height; + /* size of the glyph */ + float xadvance; + /* offset to the next glyph */ +}; +#endif + +struct nk_user_font { + nk_handle userdata; + /* user provided font handle */ + float height; + /* max height of the font */ + nk_text_width_f width; + /* font string width in pixel callback */ +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + nk_query_font_glyph_f query; + /* font glyph callback to query drawing info */ + nk_handle texture; + /* texture handle to the used font atlas or texture */ +#endif +}; + +#ifdef NK_INCLUDE_FONT_BAKING +enum nk_font_coord_type { + NK_COORD_UV, /* texture coordinates inside font glyphs are clamped between 0-1 */ + NK_COORD_PIXEL /* texture coordinates inside font glyphs are in absolute pixel */ +}; + +struct nk_font; +struct nk_baked_font { + float height; + /* height of the font */ + float ascent, descent; + /* font glyphs ascent and descent */ + nk_rune glyph_offset; + /* glyph array offset inside the font glyph baking output array */ + nk_rune glyph_count; + /* number of glyphs of this font inside the glyph baking array output */ + const nk_rune *ranges; + /* font codepoint ranges as pairs of (from/to) and 0 as last element */ +}; + +struct nk_font_config { + struct nk_font_config *next; + /* NOTE: only used internally */ + void *ttf_blob; + /* pointer to loaded TTF file memory block. + * NOTE: not needed for nk_font_atlas_add_from_memory and nk_font_atlas_add_from_file. */ + nk_size ttf_size; + /* size of the loaded TTF file memory block + * NOTE: not needed for nk_font_atlas_add_from_memory and nk_font_atlas_add_from_file. */ + + unsigned char ttf_data_owned_by_atlas; + /* used inside font atlas: default to: 0*/ + unsigned char merge_mode; + /* merges this font into the last font */ + unsigned char pixel_snap; + /* align every character to pixel boundary (if true set oversample (1,1)) */ + unsigned char oversample_v, oversample_h; + /* rasterize at hight quality for sub-pixel position */ + unsigned char padding[3]; + + float size; + /* baked pixel height of the font */ + enum nk_font_coord_type coord_type; + /* texture coordinate format with either pixel or UV coordinates */ + struct nk_vec2 spacing; + /* extra pixel spacing between glyphs */ + const nk_rune *range; + /* list of unicode ranges (2 values per range, zero terminated) */ + struct nk_baked_font *font; + /* font to setup in the baking process: NOTE: not needed for font atlas */ + nk_rune fallback_glyph; + /* fallback glyph to use if a given rune is not found */ + struct nk_font_config *n; + struct nk_font_config *p; +}; + +struct nk_font_glyph { + nk_rune codepoint; + float xadvance; + float x0, y0, x1, y1, w, h; + float u0, v0, u1, v1; +}; + +struct nk_font { + struct nk_font *next; + struct nk_user_font handle; + struct nk_baked_font info; + float scale; + struct nk_font_glyph *glyphs; + const struct nk_font_glyph *fallback; + nk_rune fallback_codepoint; + nk_handle texture; + struct nk_font_config *config; +}; + +enum nk_font_atlas_format { + NK_FONT_ATLAS_ALPHA8, + NK_FONT_ATLAS_RGBA32 +}; + +struct nk_font_atlas { + void *pixel; + int tex_width; + int tex_height; + + struct nk_allocator permanent; + struct nk_allocator temporary; + + struct nk_recti custom; + struct nk_cursor cursors[NK_CURSOR_COUNT]; + + int glyph_count; + struct nk_font_glyph *glyphs; + struct nk_font *default_font; + struct nk_font *fonts; + struct nk_font_config *config; + int font_num; +}; + +/* some language glyph codepoint ranges */ +NK_API const nk_rune *nk_font_default_glyph_ranges(void); +NK_API const nk_rune *nk_font_chinese_glyph_ranges(void); +NK_API const nk_rune *nk_font_cyrillic_glyph_ranges(void); +NK_API const nk_rune *nk_font_korean_glyph_ranges(void); + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void nk_font_atlas_init_default(struct nk_font_atlas*); +#endif +NK_API void nk_font_atlas_init(struct nk_font_atlas*, struct nk_allocator*); +NK_API void nk_font_atlas_init_custom(struct nk_font_atlas*, struct nk_allocator *persistent, struct nk_allocator *transient); +NK_API void nk_font_atlas_begin(struct nk_font_atlas*); +NK_API struct nk_font_config nk_font_config(float pixel_height); +NK_API struct nk_font *nk_font_atlas_add(struct nk_font_atlas*, const struct nk_font_config*); +#ifdef NK_INCLUDE_DEFAULT_FONT +NK_API struct nk_font* nk_font_atlas_add_default(struct nk_font_atlas*, float height, const struct nk_font_config*); +#endif +NK_API struct nk_font* nk_font_atlas_add_from_memory(struct nk_font_atlas *atlas, void *memory, nk_size size, float height, const struct nk_font_config *config); +#ifdef NK_INCLUDE_STANDARD_IO +NK_API struct nk_font* nk_font_atlas_add_from_file(struct nk_font_atlas *atlas, const char *file_path, float height, const struct nk_font_config*); +#endif +NK_API struct nk_font *nk_font_atlas_add_compressed(struct nk_font_atlas*, void *memory, nk_size size, float height, const struct nk_font_config*); +NK_API struct nk_font* nk_font_atlas_add_compressed_base85(struct nk_font_atlas*, const char *data, float height, const struct nk_font_config *config); +NK_API const void* nk_font_atlas_bake(struct nk_font_atlas*, int *width, int *height, enum nk_font_atlas_format); +NK_API void nk_font_atlas_end(struct nk_font_atlas*, nk_handle tex, struct nk_draw_null_texture*); +NK_API const struct nk_font_glyph* nk_font_find_glyph(struct nk_font*, nk_rune unicode); +NK_API void nk_font_atlas_cleanup(struct nk_font_atlas *atlas); +NK_API void nk_font_atlas_clear(struct nk_font_atlas*); + +#endif + +/* ============================================================== + * + * MEMORY BUFFER + * + * ===============================================================*/ +/* A basic (double)-buffer with linear allocation and resetting as only + freeing policy. The buffer's main purpose is to control all memory management + inside the GUI toolkit and still leave memory control as much as possible in + the hand of the user while also making sure the library is easy to use if + not as much control is needed. + In general all memory inside this library can be provided from the user in + three different ways. + + The first way and the one providing most control is by just passing a fixed + size memory block. In this case all control lies in the hand of the user + since he can exactly control where the memory comes from and how much memory + the library should consume. Of course using the fixed size API removes the + ability to automatically resize a buffer if not enough memory is provided so + you have to take over the resizing. While being a fixed sized buffer sounds + quite limiting, it is very effective in this library since the actual memory + consumption is quite stable and has a fixed upper bound for a lot of cases. + + If you don't want to think about how much memory the library should allocate + at all time or have a very dynamic UI with unpredictable memory consumption + habits but still want control over memory allocation you can use the dynamic + allocator based API. The allocator consists of two callbacks for allocating + and freeing memory and optional userdata so you can plugin your own allocator. + + The final and easiest way can be used by defining + NK_INCLUDE_DEFAULT_ALLOCATOR which uses the standard library memory + allocation functions malloc and free and takes over complete control over + memory in this library. +*/ +struct nk_memory_status { + void *memory; + unsigned int type; + nk_size size; + nk_size allocated; + nk_size needed; + nk_size calls; +}; + +enum nk_allocation_type { + NK_BUFFER_FIXED, + NK_BUFFER_DYNAMIC +}; + +enum nk_buffer_allocation_type { + NK_BUFFER_FRONT, + NK_BUFFER_BACK, + NK_BUFFER_MAX +}; + +struct nk_buffer_marker { + nk_bool active; + nk_size offset; +}; + +struct nk_memory {void *ptr;nk_size size;}; +struct nk_buffer { + struct nk_buffer_marker marker[NK_BUFFER_MAX]; + /* buffer marker to free a buffer to a certain offset */ + struct nk_allocator pool; + /* allocator callback for dynamic buffers */ + enum nk_allocation_type type; + /* memory management type */ + struct nk_memory memory; + /* memory and size of the current memory block */ + float grow_factor; + /* growing factor for dynamic memory management */ + nk_size allocated; + /* total amount of memory allocated */ + nk_size needed; + /* totally consumed memory given that enough memory is present */ + nk_size calls; + /* number of allocation calls */ + nk_size size; + /* current size of the buffer */ +}; + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void nk_buffer_init_default(struct nk_buffer*); +#endif +NK_API void nk_buffer_init(struct nk_buffer*, const struct nk_allocator*, nk_size size); +NK_API void nk_buffer_init_fixed(struct nk_buffer*, void *memory, nk_size size); +NK_API void nk_buffer_info(struct nk_memory_status*, struct nk_buffer*); +NK_API void nk_buffer_push(struct nk_buffer*, enum nk_buffer_allocation_type type, const void *memory, nk_size size, nk_size align); +NK_API void nk_buffer_mark(struct nk_buffer*, enum nk_buffer_allocation_type type); +NK_API void nk_buffer_reset(struct nk_buffer*, enum nk_buffer_allocation_type type); +NK_API void nk_buffer_clear(struct nk_buffer*); +NK_API void nk_buffer_free(struct nk_buffer*); +NK_API void *nk_buffer_memory(struct nk_buffer*); +NK_API const void *nk_buffer_memory_const(const struct nk_buffer*); +NK_API nk_size nk_buffer_total(struct nk_buffer*); + +/* ============================================================== + * + * STRING + * + * ===============================================================*/ +/* Basic string buffer which is only used in context with the text editor + * to manage and manipulate dynamic or fixed size string content. This is _NOT_ + * the default string handling method. The only instance you should have any contact + * with this API is if you interact with an `nk_text_edit` object inside one of the + * copy and paste functions and even there only for more advanced cases. */ +struct nk_str { + struct nk_buffer buffer; + int len; /* in codepoints/runes/glyphs */ +}; + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void nk_str_init_default(struct nk_str*); +#endif +NK_API void nk_str_init(struct nk_str*, const struct nk_allocator*, nk_size size); +NK_API void nk_str_init_fixed(struct nk_str*, void *memory, nk_size size); +NK_API void nk_str_clear(struct nk_str*); +NK_API void nk_str_free(struct nk_str*); + +NK_API int nk_str_append_text_char(struct nk_str*, const char*, int); +NK_API int nk_str_append_str_char(struct nk_str*, const char*); +NK_API int nk_str_append_text_utf8(struct nk_str*, const char*, int); +NK_API int nk_str_append_str_utf8(struct nk_str*, const char*); +NK_API int nk_str_append_text_runes(struct nk_str*, const nk_rune*, int); +NK_API int nk_str_append_str_runes(struct nk_str*, const nk_rune*); + +NK_API int nk_str_insert_at_char(struct nk_str*, int pos, const char*, int); +NK_API int nk_str_insert_at_rune(struct nk_str*, int pos, const char*, int); + +NK_API int nk_str_insert_text_char(struct nk_str*, int pos, const char*, int); +NK_API int nk_str_insert_str_char(struct nk_str*, int pos, const char*); +NK_API int nk_str_insert_text_utf8(struct nk_str*, int pos, const char*, int); +NK_API int nk_str_insert_str_utf8(struct nk_str*, int pos, const char*); +NK_API int nk_str_insert_text_runes(struct nk_str*, int pos, const nk_rune*, int); +NK_API int nk_str_insert_str_runes(struct nk_str*, int pos, const nk_rune*); + +NK_API void nk_str_remove_chars(struct nk_str*, int len); +NK_API void nk_str_remove_runes(struct nk_str *str, int len); +NK_API void nk_str_delete_chars(struct nk_str*, int pos, int len); +NK_API void nk_str_delete_runes(struct nk_str*, int pos, int len); + +NK_API char *nk_str_at_char(struct nk_str*, int pos); +NK_API char *nk_str_at_rune(struct nk_str*, int pos, nk_rune *unicode, int *len); +NK_API nk_rune nk_str_rune_at(const struct nk_str*, int pos); +NK_API const char *nk_str_at_char_const(const struct nk_str*, int pos); +NK_API const char *nk_str_at_const(const struct nk_str*, int pos, nk_rune *unicode, int *len); + +NK_API char *nk_str_get(struct nk_str*); +NK_API const char *nk_str_get_const(const struct nk_str*); +NK_API int nk_str_len(struct nk_str*); +NK_API int nk_str_len_char(struct nk_str*); + +/*=============================================================== + * + * TEXT EDITOR + * + * ===============================================================*/ +/* Editing text in this library is handled by either `nk_edit_string` or + * `nk_edit_buffer`. But like almost everything in this library there are multiple + * ways of doing it and a balance between control and ease of use with memory + * as well as functionality controlled by flags. + * + * This library generally allows three different levels of memory control: + * First of is the most basic way of just providing a simple char array with + * string length. This method is probably the easiest way of handling simple + * user text input. Main upside is complete control over memory while the biggest + * downside in comparison with the other two approaches is missing undo/redo. + * + * For UIs that require undo/redo the second way was created. It is based on + * a fixed size nk_text_edit struct, which has an internal undo/redo stack. + * This is mainly useful if you want something more like a text editor but don't want + * to have a dynamically growing buffer. + * + * The final way is using a dynamically growing nk_text_edit struct, which + * has both a default version if you don't care where memory comes from and an + * allocator version if you do. While the text editor is quite powerful for its + * complexity I would not recommend editing gigabytes of data with it. + * It is rather designed for uses cases which make sense for a GUI library not for + * an full blown text editor. + */ +#ifndef NK_TEXTEDIT_UNDOSTATECOUNT +#define NK_TEXTEDIT_UNDOSTATECOUNT 99 +#endif + +#ifndef NK_TEXTEDIT_UNDOCHARCOUNT +#define NK_TEXTEDIT_UNDOCHARCOUNT 999 +#endif + +struct nk_text_edit; +struct nk_clipboard { + nk_handle userdata; + nk_plugin_paste paste; + nk_plugin_copy copy; +}; + +struct nk_text_undo_record { + int where; + short insert_length; + short delete_length; + short char_storage; +}; + +struct nk_text_undo_state { + struct nk_text_undo_record undo_rec[NK_TEXTEDIT_UNDOSTATECOUNT]; + nk_rune undo_char[NK_TEXTEDIT_UNDOCHARCOUNT]; + short undo_point; + short redo_point; + short undo_char_point; + short redo_char_point; +}; + +enum nk_text_edit_type { + NK_TEXT_EDIT_SINGLE_LINE, + NK_TEXT_EDIT_MULTI_LINE +}; + +enum nk_text_edit_mode { + NK_TEXT_EDIT_MODE_VIEW, + NK_TEXT_EDIT_MODE_INSERT, + NK_TEXT_EDIT_MODE_REPLACE +}; + +struct nk_text_edit { + struct nk_clipboard clip; + struct nk_str string; + nk_plugin_filter filter; + struct nk_vec2 scrollbar; + + int cursor; + int select_start; + int select_end; + unsigned char mode; + unsigned char cursor_at_end_of_line; + unsigned char initialized; + unsigned char has_preferred_x; + unsigned char single_line; + unsigned char active; + unsigned char padding1; + float preferred_x; + struct nk_text_undo_state undo; +}; + +/* filter function */ +NK_API nk_bool nk_filter_default(const struct nk_text_edit*, nk_rune unicode); +NK_API nk_bool nk_filter_ascii(const struct nk_text_edit*, nk_rune unicode); +NK_API nk_bool nk_filter_float(const struct nk_text_edit*, nk_rune unicode); +NK_API nk_bool nk_filter_decimal(const struct nk_text_edit*, nk_rune unicode); +NK_API nk_bool nk_filter_hex(const struct nk_text_edit*, nk_rune unicode); +NK_API nk_bool nk_filter_oct(const struct nk_text_edit*, nk_rune unicode); +NK_API nk_bool nk_filter_binary(const struct nk_text_edit*, nk_rune unicode); + +/* text editor */ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void nk_textedit_init_default(struct nk_text_edit*); +#endif +NK_API void nk_textedit_init(struct nk_text_edit*, struct nk_allocator*, nk_size size); +NK_API void nk_textedit_init_fixed(struct nk_text_edit*, void *memory, nk_size size); +NK_API void nk_textedit_free(struct nk_text_edit*); +NK_API void nk_textedit_text(struct nk_text_edit*, const char*, int total_len); +NK_API void nk_textedit_delete(struct nk_text_edit*, int where, int len); +NK_API void nk_textedit_delete_selection(struct nk_text_edit*); +NK_API void nk_textedit_select_all(struct nk_text_edit*); +NK_API nk_bool nk_textedit_cut(struct nk_text_edit*); +NK_API nk_bool nk_textedit_paste(struct nk_text_edit*, char const*, int len); +NK_API void nk_textedit_undo(struct nk_text_edit*); +NK_API void nk_textedit_redo(struct nk_text_edit*); + +/* =============================================================== + * + * DRAWING + * + * ===============================================================*/ +/* This library was designed to be render backend agnostic so it does + not draw anything to screen. Instead all drawn shapes, widgets + are made of, are buffered into memory and make up a command queue. + Each frame therefore fills the command buffer with draw commands + that then need to be executed by the user and his own render backend. + After that the command buffer needs to be cleared and a new frame can be + started. It is probably important to note that the command buffer is the main + drawing API and the optional vertex buffer API only takes this format and + converts it into a hardware accessible format. + + To use the command queue to draw your own widgets you can access the + command buffer of each window by calling `nk_window_get_canvas` after + previously having called `nk_begin`: + + void draw_red_rectangle_widget(struct nk_context *ctx) + { + struct nk_command_buffer *canvas; + struct nk_input *input = &ctx->input; + canvas = nk_window_get_canvas(ctx); + + struct nk_rect space; + enum nk_widget_layout_states state; + state = nk_widget(&space, ctx); + if (!state) return; + + if (state != NK_WIDGET_ROM) + update_your_widget_by_user_input(...); + nk_fill_rect(canvas, space, 0, nk_rgb(255,0,0)); + } + + if (nk_begin(...)) { + nk_layout_row_dynamic(ctx, 25, 1); + draw_red_rectangle_widget(ctx); + } + nk_end(..) + + Important to know if you want to create your own widgets is the `nk_widget` + call. It allocates space on the panel reserved for this widget to be used, + but also returns the state of the widget space. If your widget is not seen and does + not have to be updated it is '0' and you can just return. If it only has + to be drawn the state will be `NK_WIDGET_ROM` otherwise you can do both + update and draw your widget. The reason for separating is to only draw and + update what is actually necessary which is crucial for performance. +*/ +enum nk_command_type { + NK_COMMAND_NOP, + NK_COMMAND_SCISSOR, + NK_COMMAND_LINE, + NK_COMMAND_CURVE, + NK_COMMAND_RECT, + NK_COMMAND_RECT_FILLED, + NK_COMMAND_RECT_MULTI_COLOR, + NK_COMMAND_CIRCLE, + NK_COMMAND_CIRCLE_FILLED, + NK_COMMAND_ARC, + NK_COMMAND_ARC_FILLED, + NK_COMMAND_TRIANGLE, + NK_COMMAND_TRIANGLE_FILLED, + NK_COMMAND_POLYGON, + NK_COMMAND_POLYGON_FILLED, + NK_COMMAND_POLYLINE, + NK_COMMAND_TEXT, + NK_COMMAND_IMAGE, + NK_COMMAND_CUSTOM +}; + +/* command base and header of every command inside the buffer */ +struct nk_command { + enum nk_command_type type; + nk_size next; +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_handle userdata; +#endif +}; + +struct nk_command_scissor { + struct nk_command header; + short x, y; + unsigned short w, h; +}; + +struct nk_command_line { + struct nk_command header; + unsigned short line_thickness; + struct nk_vec2i begin; + struct nk_vec2i end; + struct nk_color color; +}; + +struct nk_command_curve { + struct nk_command header; + unsigned short line_thickness; + struct nk_vec2i begin; + struct nk_vec2i end; + struct nk_vec2i ctrl[2]; + struct nk_color color; +}; + +struct nk_command_rect { + struct nk_command header; + unsigned short rounding; + unsigned short line_thickness; + short x, y; + unsigned short w, h; + struct nk_color color; +}; + +struct nk_command_rect_filled { + struct nk_command header; + unsigned short rounding; + short x, y; + unsigned short w, h; + struct nk_color color; +}; + +struct nk_command_rect_multi_color { + struct nk_command header; + short x, y; + unsigned short w, h; + struct nk_color left; + struct nk_color top; + struct nk_color bottom; + struct nk_color right; +}; + +struct nk_command_triangle { + struct nk_command header; + unsigned short line_thickness; + struct nk_vec2i a; + struct nk_vec2i b; + struct nk_vec2i c; + struct nk_color color; +}; + +struct nk_command_triangle_filled { + struct nk_command header; + struct nk_vec2i a; + struct nk_vec2i b; + struct nk_vec2i c; + struct nk_color color; +}; + +struct nk_command_circle { + struct nk_command header; + short x, y; + unsigned short line_thickness; + unsigned short w, h; + struct nk_color color; +}; + +struct nk_command_circle_filled { + struct nk_command header; + short x, y; + unsigned short w, h; + struct nk_color color; +}; + +struct nk_command_arc { + struct nk_command header; + short cx, cy; + unsigned short r; + unsigned short line_thickness; + float a[2]; + struct nk_color color; +}; + +struct nk_command_arc_filled { + struct nk_command header; + short cx, cy; + unsigned short r; + float a[2]; + struct nk_color color; +}; + +struct nk_command_polygon { + struct nk_command header; + struct nk_color color; + unsigned short line_thickness; + unsigned short point_count; + struct nk_vec2i points[1]; +}; + +struct nk_command_polygon_filled { + struct nk_command header; + struct nk_color color; + unsigned short point_count; + struct nk_vec2i points[1]; +}; + +struct nk_command_polyline { + struct nk_command header; + struct nk_color color; + unsigned short line_thickness; + unsigned short point_count; + struct nk_vec2i points[1]; +}; + +struct nk_command_image { + struct nk_command header; + short x, y; + unsigned short w, h; + struct nk_image img; + struct nk_color col; +}; + +typedef void (*nk_command_custom_callback)(void *canvas, short x,short y, + unsigned short w, unsigned short h, nk_handle callback_data); +struct nk_command_custom { + struct nk_command header; + short x, y; + unsigned short w, h; + nk_handle callback_data; + nk_command_custom_callback callback; +}; + +struct nk_command_text { + struct nk_command header; + const struct nk_user_font *font; + struct nk_color background; + struct nk_color foreground; + short x, y; + unsigned short w, h; + float height; + int length; + char string[1]; +}; + +enum nk_command_clipping { + NK_CLIPPING_OFF = nk_false, + NK_CLIPPING_ON = nk_true +}; + +struct nk_command_buffer { + struct nk_buffer *base; + struct nk_rect clip; + int use_clipping; + nk_handle userdata; + nk_size begin, end, last; +}; + +/* shape outlines */ +NK_API void nk_stroke_line(struct nk_command_buffer *b, float x0, float y0, float x1, float y1, float line_thickness, struct nk_color); +NK_API void nk_stroke_curve(struct nk_command_buffer*, float, float, float, float, float, float, float, float, float line_thickness, struct nk_color); +NK_API void nk_stroke_rect(struct nk_command_buffer*, struct nk_rect, float rounding, float line_thickness, struct nk_color); +NK_API void nk_stroke_circle(struct nk_command_buffer*, struct nk_rect, float line_thickness, struct nk_color); +NK_API void nk_stroke_arc(struct nk_command_buffer*, float cx, float cy, float radius, float a_min, float a_max, float line_thickness, struct nk_color); +NK_API void nk_stroke_triangle(struct nk_command_buffer*, float, float, float, float, float, float, float line_thichness, struct nk_color); +NK_API void nk_stroke_polyline(struct nk_command_buffer*, float *points, int point_count, float line_thickness, struct nk_color col); +NK_API void nk_stroke_polygon(struct nk_command_buffer*, float*, int point_count, float line_thickness, struct nk_color); + +/* filled shades */ +NK_API void nk_fill_rect(struct nk_command_buffer*, struct nk_rect, float rounding, struct nk_color); +NK_API void nk_fill_rect_multi_color(struct nk_command_buffer*, struct nk_rect, struct nk_color left, struct nk_color top, struct nk_color right, struct nk_color bottom); +NK_API void nk_fill_circle(struct nk_command_buffer*, struct nk_rect, struct nk_color); +NK_API void nk_fill_arc(struct nk_command_buffer*, float cx, float cy, float radius, float a_min, float a_max, struct nk_color); +NK_API void nk_fill_triangle(struct nk_command_buffer*, float x0, float y0, float x1, float y1, float x2, float y2, struct nk_color); +NK_API void nk_fill_polygon(struct nk_command_buffer*, float*, int point_count, struct nk_color); + +/* misc */ +NK_API void nk_draw_image(struct nk_command_buffer*, struct nk_rect, const struct nk_image*, struct nk_color); +NK_API void nk_draw_text(struct nk_command_buffer*, struct nk_rect, const char *text, int len, const struct nk_user_font*, struct nk_color, struct nk_color); +NK_API void nk_push_scissor(struct nk_command_buffer*, struct nk_rect); +NK_API void nk_push_custom(struct nk_command_buffer*, struct nk_rect, nk_command_custom_callback, nk_handle usr); + +/* =============================================================== + * + * INPUT + * + * ===============================================================*/ +struct nk_mouse_button { + nk_bool down; + unsigned int clicked; + struct nk_vec2 clicked_pos; +}; +struct nk_mouse { + struct nk_mouse_button buttons[NK_BUTTON_MAX]; + struct nk_vec2 pos; + struct nk_vec2 prev; + struct nk_vec2 delta; + struct nk_vec2 scroll_delta; + unsigned char grab; + unsigned char grabbed; + unsigned char ungrab; +}; + +struct nk_key { + nk_bool down; + unsigned int clicked; +}; +struct nk_keyboard { + struct nk_key keys[NK_KEY_MAX]; + char text[NK_INPUT_MAX]; + int text_len; +}; + +struct nk_input { + struct nk_keyboard keyboard; + struct nk_mouse mouse; +}; + +NK_API nk_bool nk_input_has_mouse_click(const struct nk_input*, enum nk_buttons); +NK_API nk_bool nk_input_has_mouse_click_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect); +NK_API nk_bool nk_input_has_mouse_click_down_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect, nk_bool down); +NK_API nk_bool nk_input_is_mouse_click_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect); +NK_API nk_bool nk_input_is_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, struct nk_rect b, nk_bool down); +NK_API nk_bool nk_input_any_mouse_click_in_rect(const struct nk_input*, struct nk_rect); +NK_API nk_bool nk_input_is_mouse_prev_hovering_rect(const struct nk_input*, struct nk_rect); +NK_API nk_bool nk_input_is_mouse_hovering_rect(const struct nk_input*, struct nk_rect); +NK_API nk_bool nk_input_mouse_clicked(const struct nk_input*, enum nk_buttons, struct nk_rect); +NK_API nk_bool nk_input_is_mouse_down(const struct nk_input*, enum nk_buttons); +NK_API nk_bool nk_input_is_mouse_pressed(const struct nk_input*, enum nk_buttons); +NK_API nk_bool nk_input_is_mouse_released(const struct nk_input*, enum nk_buttons); +NK_API nk_bool nk_input_is_key_pressed(const struct nk_input*, enum nk_keys); +NK_API nk_bool nk_input_is_key_released(const struct nk_input*, enum nk_keys); +NK_API nk_bool nk_input_is_key_down(const struct nk_input*, enum nk_keys); + +/* =============================================================== + * + * DRAW LIST + * + * ===============================================================*/ +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +/* The optional vertex buffer draw list provides a 2D drawing context + with antialiasing functionality which takes basic filled or outlined shapes + or a path and outputs vertexes, elements and draw commands. + The actual draw list API is not required to be used directly while using this + library since converting the default library draw command output is done by + just calling `nk_convert` but I decided to still make this library accessible + since it can be useful. + + The draw list is based on a path buffering and polygon and polyline + rendering API which allows a lot of ways to draw 2D content to screen. + In fact it is probably more powerful than needed but allows even more crazy + things than this library provides by default. +*/ +#ifdef NK_UINT_DRAW_INDEX +typedef nk_uint nk_draw_index; +#else +typedef nk_ushort nk_draw_index; +#endif +enum nk_draw_list_stroke { + NK_STROKE_OPEN = nk_false, + /* build up path has no connection back to the beginning */ + NK_STROKE_CLOSED = nk_true + /* build up path has a connection back to the beginning */ +}; + +enum nk_draw_vertex_layout_attribute { + NK_VERTEX_POSITION, + NK_VERTEX_COLOR, + NK_VERTEX_TEXCOORD, + NK_VERTEX_ATTRIBUTE_COUNT +}; + +enum nk_draw_vertex_layout_format { + NK_FORMAT_SCHAR, + NK_FORMAT_SSHORT, + NK_FORMAT_SINT, + NK_FORMAT_UCHAR, + NK_FORMAT_USHORT, + NK_FORMAT_UINT, + NK_FORMAT_FLOAT, + NK_FORMAT_DOUBLE, + +NK_FORMAT_COLOR_BEGIN, + NK_FORMAT_R8G8B8 = NK_FORMAT_COLOR_BEGIN, + NK_FORMAT_R16G15B16, + NK_FORMAT_R32G32B32, + + NK_FORMAT_R8G8B8A8, + NK_FORMAT_B8G8R8A8, + NK_FORMAT_R16G15B16A16, + NK_FORMAT_R32G32B32A32, + NK_FORMAT_R32G32B32A32_FLOAT, + NK_FORMAT_R32G32B32A32_DOUBLE, + + NK_FORMAT_RGB32, + NK_FORMAT_RGBA32, +NK_FORMAT_COLOR_END = NK_FORMAT_RGBA32, + NK_FORMAT_COUNT +}; + +#define NK_VERTEX_LAYOUT_END NK_VERTEX_ATTRIBUTE_COUNT,NK_FORMAT_COUNT,0 +struct nk_draw_vertex_layout_element { + enum nk_draw_vertex_layout_attribute attribute; + enum nk_draw_vertex_layout_format format; + nk_size offset; +}; + +struct nk_draw_command { + unsigned int elem_count; + /* number of elements in the current draw batch */ + struct nk_rect clip_rect; + /* current screen clipping rectangle */ + nk_handle texture; + /* current texture to set */ +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_handle userdata; +#endif +}; + +struct nk_draw_list { + struct nk_rect clip_rect; + struct nk_vec2 circle_vtx[12]; + struct nk_convert_config config; + + struct nk_buffer *buffer; + struct nk_buffer *vertices; + struct nk_buffer *elements; + + unsigned int element_count; + unsigned int vertex_count; + unsigned int cmd_count; + nk_size cmd_offset; + + unsigned int path_count; + unsigned int path_offset; + + enum nk_anti_aliasing line_AA; + enum nk_anti_aliasing shape_AA; + +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_handle userdata; +#endif +}; + +/* draw list */ +NK_API void nk_draw_list_init(struct nk_draw_list*); +NK_API void nk_draw_list_setup(struct nk_draw_list*, const struct nk_convert_config*, struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements, enum nk_anti_aliasing line_aa,enum nk_anti_aliasing shape_aa); + +/* drawing */ +#define nk_draw_list_foreach(cmd, can, b) for((cmd)=nk__draw_list_begin(can, b); (cmd)!=0; (cmd)=nk__draw_list_next(cmd, b, can)) +NK_API const struct nk_draw_command* nk__draw_list_begin(const struct nk_draw_list*, const struct nk_buffer*); +NK_API const struct nk_draw_command* nk__draw_list_next(const struct nk_draw_command*, const struct nk_buffer*, const struct nk_draw_list*); +NK_API const struct nk_draw_command* nk__draw_list_end(const struct nk_draw_list*, const struct nk_buffer*); + +/* path */ +NK_API void nk_draw_list_path_clear(struct nk_draw_list*); +NK_API void nk_draw_list_path_line_to(struct nk_draw_list*, struct nk_vec2 pos); +NK_API void nk_draw_list_path_arc_to_fast(struct nk_draw_list*, struct nk_vec2 center, float radius, int a_min, int a_max); +NK_API void nk_draw_list_path_arc_to(struct nk_draw_list*, struct nk_vec2 center, float radius, float a_min, float a_max, unsigned int segments); +NK_API void nk_draw_list_path_rect_to(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, float rounding); +NK_API void nk_draw_list_path_curve_to(struct nk_draw_list*, struct nk_vec2 p2, struct nk_vec2 p3, struct nk_vec2 p4, unsigned int num_segments); +NK_API void nk_draw_list_path_fill(struct nk_draw_list*, struct nk_color); +NK_API void nk_draw_list_path_stroke(struct nk_draw_list*, struct nk_color, enum nk_draw_list_stroke closed, float thickness); + +/* stroke */ +NK_API void nk_draw_list_stroke_line(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, struct nk_color, float thickness); +NK_API void nk_draw_list_stroke_rect(struct nk_draw_list*, struct nk_rect rect, struct nk_color, float rounding, float thickness); +NK_API void nk_draw_list_stroke_triangle(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, struct nk_vec2 c, struct nk_color, float thickness); +NK_API void nk_draw_list_stroke_circle(struct nk_draw_list*, struct nk_vec2 center, float radius, struct nk_color, unsigned int segs, float thickness); +NK_API void nk_draw_list_stroke_curve(struct nk_draw_list*, struct nk_vec2 p0, struct nk_vec2 cp0, struct nk_vec2 cp1, struct nk_vec2 p1, struct nk_color, unsigned int segments, float thickness); +NK_API void nk_draw_list_stroke_poly_line(struct nk_draw_list*, const struct nk_vec2 *pnts, const unsigned int cnt, struct nk_color, enum nk_draw_list_stroke, float thickness, enum nk_anti_aliasing); + +/* fill */ +NK_API void nk_draw_list_fill_rect(struct nk_draw_list*, struct nk_rect rect, struct nk_color, float rounding); +NK_API void nk_draw_list_fill_rect_multi_color(struct nk_draw_list*, struct nk_rect rect, struct nk_color left, struct nk_color top, struct nk_color right, struct nk_color bottom); +NK_API void nk_draw_list_fill_triangle(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, struct nk_vec2 c, struct nk_color); +NK_API void nk_draw_list_fill_circle(struct nk_draw_list*, struct nk_vec2 center, float radius, struct nk_color col, unsigned int segs); +NK_API void nk_draw_list_fill_poly_convex(struct nk_draw_list*, const struct nk_vec2 *points, const unsigned int count, struct nk_color, enum nk_anti_aliasing); + +/* misc */ +NK_API void nk_draw_list_add_image(struct nk_draw_list*, struct nk_image texture, struct nk_rect rect, struct nk_color); +NK_API void nk_draw_list_add_text(struct nk_draw_list*, const struct nk_user_font*, struct nk_rect, const char *text, int len, float font_height, struct nk_color); +#ifdef NK_INCLUDE_COMMAND_USERDATA +NK_API void nk_draw_list_push_userdata(struct nk_draw_list*, nk_handle userdata); +#endif + +#endif + +/* =============================================================== + * + * GUI + * + * ===============================================================*/ +enum nk_style_item_type { + NK_STYLE_ITEM_COLOR, + NK_STYLE_ITEM_IMAGE +}; + +union nk_style_item_data { + struct nk_image image; + struct nk_color color; +}; + +struct nk_style_item { + enum nk_style_item_type type; + union nk_style_item_data data; +}; + +struct nk_style_text { + struct nk_color color; + struct nk_vec2 padding; +}; + +struct nk_style_button { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* text */ + struct nk_color text_background; + struct nk_color text_normal; + struct nk_color text_hover; + struct nk_color text_active; + nk_flags text_alignment; + + /* properties */ + float border; + float rounding; + struct nk_vec2 padding; + struct nk_vec2 image_padding; + struct nk_vec2 touch_padding; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle userdata); + void(*draw_end)(struct nk_command_buffer*, nk_handle userdata); +}; + +struct nk_style_toggle { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* cursor */ + struct nk_style_item cursor_normal; + struct nk_style_item cursor_hover; + + /* text */ + struct nk_color text_normal; + struct nk_color text_hover; + struct nk_color text_active; + struct nk_color text_background; + nk_flags text_alignment; + + /* properties */ + struct nk_vec2 padding; + struct nk_vec2 touch_padding; + float spacing; + float border; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_selectable { + /* background (inactive) */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item pressed; + + /* background (active) */ + struct nk_style_item normal_active; + struct nk_style_item hover_active; + struct nk_style_item pressed_active; + + /* text color (inactive) */ + struct nk_color text_normal; + struct nk_color text_hover; + struct nk_color text_pressed; + + /* text color (active) */ + struct nk_color text_normal_active; + struct nk_color text_hover_active; + struct nk_color text_pressed_active; + struct nk_color text_background; + nk_flags text_alignment; + + /* properties */ + float rounding; + struct nk_vec2 padding; + struct nk_vec2 touch_padding; + struct nk_vec2 image_padding; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_slider { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* background bar */ + struct nk_color bar_normal; + struct nk_color bar_hover; + struct nk_color bar_active; + struct nk_color bar_filled; + + /* cursor */ + struct nk_style_item cursor_normal; + struct nk_style_item cursor_hover; + struct nk_style_item cursor_active; + + /* properties */ + float border; + float rounding; + float bar_height; + struct nk_vec2 padding; + struct nk_vec2 spacing; + struct nk_vec2 cursor_size; + + /* optional buttons */ + int show_buttons; + struct nk_style_button inc_button; + struct nk_style_button dec_button; + enum nk_symbol_type inc_symbol; + enum nk_symbol_type dec_symbol; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_progress { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* cursor */ + struct nk_style_item cursor_normal; + struct nk_style_item cursor_hover; + struct nk_style_item cursor_active; + struct nk_color cursor_border_color; + + /* properties */ + float rounding; + float border; + float cursor_border; + float cursor_rounding; + struct nk_vec2 padding; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_scrollbar { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* cursor */ + struct nk_style_item cursor_normal; + struct nk_style_item cursor_hover; + struct nk_style_item cursor_active; + struct nk_color cursor_border_color; + + /* properties */ + float border; + float rounding; + float border_cursor; + float rounding_cursor; + struct nk_vec2 padding; + + /* optional buttons */ + int show_buttons; + struct nk_style_button inc_button; + struct nk_style_button dec_button; + enum nk_symbol_type inc_symbol; + enum nk_symbol_type dec_symbol; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_edit { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + struct nk_style_scrollbar scrollbar; + + /* cursor */ + struct nk_color cursor_normal; + struct nk_color cursor_hover; + struct nk_color cursor_text_normal; + struct nk_color cursor_text_hover; + + /* text (unselected) */ + struct nk_color text_normal; + struct nk_color text_hover; + struct nk_color text_active; + + /* text (selected) */ + struct nk_color selected_normal; + struct nk_color selected_hover; + struct nk_color selected_text_normal; + struct nk_color selected_text_hover; + + /* properties */ + float border; + float rounding; + float cursor_size; + struct nk_vec2 scrollbar_size; + struct nk_vec2 padding; + float row_padding; +}; + +struct nk_style_property { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* text */ + struct nk_color label_normal; + struct nk_color label_hover; + struct nk_color label_active; + + /* symbols */ + enum nk_symbol_type sym_left; + enum nk_symbol_type sym_right; + + /* properties */ + float border; + float rounding; + struct nk_vec2 padding; + + struct nk_style_edit edit; + struct nk_style_button inc_button; + struct nk_style_button dec_button; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_chart { + /* colors */ + struct nk_style_item background; + struct nk_color border_color; + struct nk_color selected_color; + struct nk_color color; + + /* properties */ + float border; + float rounding; + struct nk_vec2 padding; +}; + +struct nk_style_combo { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* label */ + struct nk_color label_normal; + struct nk_color label_hover; + struct nk_color label_active; + + /* symbol */ + struct nk_color symbol_normal; + struct nk_color symbol_hover; + struct nk_color symbol_active; + + /* button */ + struct nk_style_button button; + enum nk_symbol_type sym_normal; + enum nk_symbol_type sym_hover; + enum nk_symbol_type sym_active; + + /* properties */ + float border; + float rounding; + struct nk_vec2 content_padding; + struct nk_vec2 button_padding; + struct nk_vec2 spacing; +}; + +struct nk_style_tab { + /* background */ + struct nk_style_item background; + struct nk_color border_color; + struct nk_color text; + + /* button */ + struct nk_style_button tab_maximize_button; + struct nk_style_button tab_minimize_button; + struct nk_style_button node_maximize_button; + struct nk_style_button node_minimize_button; + enum nk_symbol_type sym_minimize; + enum nk_symbol_type sym_maximize; + + /* properties */ + float border; + float rounding; + float indent; + struct nk_vec2 padding; + struct nk_vec2 spacing; +}; + +enum nk_style_header_align { + NK_HEADER_LEFT, + NK_HEADER_RIGHT +}; +struct nk_style_window_header { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + + /* button */ + struct nk_style_button close_button; + struct nk_style_button minimize_button; + enum nk_symbol_type close_symbol; + enum nk_symbol_type minimize_symbol; + enum nk_symbol_type maximize_symbol; + + /* title */ + struct nk_color label_normal; + struct nk_color label_hover; + struct nk_color label_active; + + /* properties */ + enum nk_style_header_align align; + struct nk_vec2 padding; + struct nk_vec2 label_padding; + struct nk_vec2 spacing; +}; + +struct nk_style_window { + struct nk_style_window_header header; + struct nk_style_item fixed_background; + struct nk_color background; + + struct nk_color border_color; + struct nk_color popup_border_color; + struct nk_color combo_border_color; + struct nk_color contextual_border_color; + struct nk_color menu_border_color; + struct nk_color group_border_color; + struct nk_color tooltip_border_color; + struct nk_style_item scaler; + + float border; + float combo_border; + float contextual_border; + float menu_border; + float group_border; + float tooltip_border; + float popup_border; + float min_row_height_padding; + + float rounding; + struct nk_vec2 spacing; + struct nk_vec2 scrollbar_size; + struct nk_vec2 min_size; + + struct nk_vec2 padding; + struct nk_vec2 group_padding; + struct nk_vec2 popup_padding; + struct nk_vec2 combo_padding; + struct nk_vec2 contextual_padding; + struct nk_vec2 menu_padding; + struct nk_vec2 tooltip_padding; +}; + +struct nk_style { + const struct nk_user_font *font; + const struct nk_cursor *cursors[NK_CURSOR_COUNT]; + const struct nk_cursor *cursor_active; + struct nk_cursor *cursor_last; + int cursor_visible; + + struct nk_style_text text; + struct nk_style_button button; + struct nk_style_button contextual_button; + struct nk_style_button menu_button; + struct nk_style_toggle option; + struct nk_style_toggle checkbox; + struct nk_style_selectable selectable; + struct nk_style_slider slider; + struct nk_style_progress progress; + struct nk_style_property property; + struct nk_style_edit edit; + struct nk_style_chart chart; + struct nk_style_scrollbar scrollh; + struct nk_style_scrollbar scrollv; + struct nk_style_tab tab; + struct nk_style_combo combo; + struct nk_style_window window; +}; + +NK_API struct nk_style_item nk_style_item_image(struct nk_image img); +NK_API struct nk_style_item nk_style_item_color(struct nk_color); +NK_API struct nk_style_item nk_style_item_hide(void); + +/*============================================================== + * PANEL + * =============================================================*/ +#ifndef NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS +#define NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS 16 +#endif +#ifndef NK_CHART_MAX_SLOT +#define NK_CHART_MAX_SLOT 4 +#endif + +enum nk_panel_type { + NK_PANEL_NONE = 0, + NK_PANEL_WINDOW = NK_FLAG(0), + NK_PANEL_GROUP = NK_FLAG(1), + NK_PANEL_POPUP = NK_FLAG(2), + NK_PANEL_CONTEXTUAL = NK_FLAG(4), + NK_PANEL_COMBO = NK_FLAG(5), + NK_PANEL_MENU = NK_FLAG(6), + NK_PANEL_TOOLTIP = NK_FLAG(7) +}; +enum nk_panel_set { + NK_PANEL_SET_NONBLOCK = NK_PANEL_CONTEXTUAL|NK_PANEL_COMBO|NK_PANEL_MENU|NK_PANEL_TOOLTIP, + NK_PANEL_SET_POPUP = NK_PANEL_SET_NONBLOCK|NK_PANEL_POPUP, + NK_PANEL_SET_SUB = NK_PANEL_SET_POPUP|NK_PANEL_GROUP +}; + +struct nk_chart_slot { + enum nk_chart_type type; + struct nk_color color; + struct nk_color highlight; + float min, max, range; + int count; + struct nk_vec2 last; + int index; +}; + +struct nk_chart { + int slot; + float x, y, w, h; + struct nk_chart_slot slots[NK_CHART_MAX_SLOT]; +}; + +enum nk_panel_row_layout_type { + NK_LAYOUT_DYNAMIC_FIXED = 0, + NK_LAYOUT_DYNAMIC_ROW, + NK_LAYOUT_DYNAMIC_FREE, + NK_LAYOUT_DYNAMIC, + NK_LAYOUT_STATIC_FIXED, + NK_LAYOUT_STATIC_ROW, + NK_LAYOUT_STATIC_FREE, + NK_LAYOUT_STATIC, + NK_LAYOUT_TEMPLATE, + NK_LAYOUT_COUNT +}; +struct nk_row_layout { + enum nk_panel_row_layout_type type; + int index; + float height; + float min_height; + int columns; + const float *ratio; + float item_width; + float item_height; + float item_offset; + float filled; + struct nk_rect item; + int tree_depth; + float templates[NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS]; +}; + +struct nk_popup_buffer { + nk_size begin; + nk_size parent; + nk_size last; + nk_size end; + nk_bool active; +}; + +struct nk_menu_state { + float x, y, w, h; + struct nk_scroll offset; +}; + +struct nk_panel { + enum nk_panel_type type; + nk_flags flags; + struct nk_rect bounds; + nk_uint *offset_x; + nk_uint *offset_y; + float at_x, at_y, max_x; + float footer_height; + float header_height; + float border; + unsigned int has_scrolling; + struct nk_rect clip; + struct nk_menu_state menu; + struct nk_row_layout row; + struct nk_chart chart; + struct nk_command_buffer *buffer; + struct nk_panel *parent; +}; + +/*============================================================== + * WINDOW + * =============================================================*/ +#ifndef NK_WINDOW_MAX_NAME +#define NK_WINDOW_MAX_NAME 64 +#endif + +struct nk_table; +enum nk_window_flags { + NK_WINDOW_PRIVATE = NK_FLAG(11), + NK_WINDOW_DYNAMIC = NK_WINDOW_PRIVATE, + /* special window type growing up in height while being filled to a certain maximum height */ + NK_WINDOW_ROM = NK_FLAG(12), + /* sets window widgets into a read only mode and does not allow input changes */ + NK_WINDOW_NOT_INTERACTIVE = NK_WINDOW_ROM|NK_WINDOW_NO_INPUT, + /* prevents all interaction caused by input to either window or widgets inside */ + NK_WINDOW_HIDDEN = NK_FLAG(13), + /* Hides window and stops any window interaction and drawing */ + NK_WINDOW_CLOSED = NK_FLAG(14), + /* Directly closes and frees the window at the end of the frame */ + NK_WINDOW_MINIMIZED = NK_FLAG(15), + /* marks the window as minimized */ + NK_WINDOW_REMOVE_ROM = NK_FLAG(16) + /* Removes read only mode at the end of the window */ +}; + +struct nk_popup_state { + struct nk_window *win; + enum nk_panel_type type; + struct nk_popup_buffer buf; + nk_hash name; + nk_bool active; + unsigned combo_count; + unsigned con_count, con_old; + unsigned active_con; + struct nk_rect header; +}; + +struct nk_edit_state { + nk_hash name; + unsigned int seq; + unsigned int old; + int active, prev; + int cursor; + int sel_start; + int sel_end; + struct nk_scroll scrollbar; + unsigned char mode; + unsigned char single_line; +}; + +struct nk_property_state { + int active, prev; + char buffer[NK_MAX_NUMBER_BUFFER]; + int length; + int cursor; + int select_start; + int select_end; + nk_hash name; + unsigned int seq; + unsigned int old; + int state; +}; + +struct nk_window { + unsigned int seq; + nk_hash name; + char name_string[NK_WINDOW_MAX_NAME]; + nk_flags flags; + + struct nk_rect bounds; + struct nk_scroll scrollbar; + struct nk_command_buffer buffer; + struct nk_panel *layout; + float scrollbar_hiding_timer; + + /* persistent widget state */ + struct nk_property_state property; + struct nk_popup_state popup; + struct nk_edit_state edit; + unsigned int scrolled; + + struct nk_table *tables; + unsigned int table_count; + + /* window list hooks */ + struct nk_window *next; + struct nk_window *prev; + struct nk_window *parent; +}; + +/*============================================================== + * STACK + * =============================================================*/ +/* The style modifier stack can be used to temporarily change a + * property inside `nk_style`. For example if you want a special + * red button you can temporarily push the old button color onto a stack + * draw the button with a red color and then you just pop the old color + * back from the stack: + * + * nk_style_push_style_item(ctx, &ctx->style.button.normal, nk_style_item_color(nk_rgb(255,0,0))); + * nk_style_push_style_item(ctx, &ctx->style.button.hover, nk_style_item_color(nk_rgb(255,0,0))); + * nk_style_push_style_item(ctx, &ctx->style.button.active, nk_style_item_color(nk_rgb(255,0,0))); + * nk_style_push_vec2(ctx, &cx->style.button.padding, nk_vec2(2,2)); + * + * nk_button(...); + * + * nk_style_pop_style_item(ctx); + * nk_style_pop_style_item(ctx); + * nk_style_pop_style_item(ctx); + * nk_style_pop_vec2(ctx); + * + * Nuklear has a stack for style_items, float properties, vector properties, + * flags, colors, fonts and for button_behavior. Each has it's own fixed size stack + * which can be changed at compile time. + */ +#ifndef NK_BUTTON_BEHAVIOR_STACK_SIZE +#define NK_BUTTON_BEHAVIOR_STACK_SIZE 8 +#endif + +#ifndef NK_FONT_STACK_SIZE +#define NK_FONT_STACK_SIZE 8 +#endif + +#ifndef NK_STYLE_ITEM_STACK_SIZE +#define NK_STYLE_ITEM_STACK_SIZE 16 +#endif + +#ifndef NK_FLOAT_STACK_SIZE +#define NK_FLOAT_STACK_SIZE 32 +#endif + +#ifndef NK_VECTOR_STACK_SIZE +#define NK_VECTOR_STACK_SIZE 16 +#endif + +#ifndef NK_FLAGS_STACK_SIZE +#define NK_FLAGS_STACK_SIZE 32 +#endif + +#ifndef NK_COLOR_STACK_SIZE +#define NK_COLOR_STACK_SIZE 32 +#endif + +#define NK_CONFIGURATION_STACK_TYPE(prefix, name, type)\ + struct nk_config_stack_##name##_element {\ + prefix##_##type *address;\ + prefix##_##type old_value;\ + } +#define NK_CONFIG_STACK(type,size)\ + struct nk_config_stack_##type {\ + int head;\ + struct nk_config_stack_##type##_element elements[size];\ + } + +#define nk_float float +NK_CONFIGURATION_STACK_TYPE(struct nk, style_item, style_item); +NK_CONFIGURATION_STACK_TYPE(nk ,float, float); +NK_CONFIGURATION_STACK_TYPE(struct nk, vec2, vec2); +NK_CONFIGURATION_STACK_TYPE(nk ,flags, flags); +NK_CONFIGURATION_STACK_TYPE(struct nk, color, color); +NK_CONFIGURATION_STACK_TYPE(const struct nk, user_font, user_font*); +NK_CONFIGURATION_STACK_TYPE(enum nk, button_behavior, button_behavior); + +NK_CONFIG_STACK(style_item, NK_STYLE_ITEM_STACK_SIZE); +NK_CONFIG_STACK(float, NK_FLOAT_STACK_SIZE); +NK_CONFIG_STACK(vec2, NK_VECTOR_STACK_SIZE); +NK_CONFIG_STACK(flags, NK_FLAGS_STACK_SIZE); +NK_CONFIG_STACK(color, NK_COLOR_STACK_SIZE); +NK_CONFIG_STACK(user_font, NK_FONT_STACK_SIZE); +NK_CONFIG_STACK(button_behavior, NK_BUTTON_BEHAVIOR_STACK_SIZE); + +struct nk_configuration_stacks { + struct nk_config_stack_style_item style_items; + struct nk_config_stack_float floats; + struct nk_config_stack_vec2 vectors; + struct nk_config_stack_flags flags; + struct nk_config_stack_color colors; + struct nk_config_stack_user_font fonts; + struct nk_config_stack_button_behavior button_behaviors; +}; + +/*============================================================== + * CONTEXT + * =============================================================*/ +#define NK_VALUE_PAGE_CAPACITY \ + (((NK_MAX(sizeof(struct nk_window),sizeof(struct nk_panel)) / sizeof(nk_uint))) / 2) + +struct nk_table { + unsigned int seq; + unsigned int size; + nk_hash keys[NK_VALUE_PAGE_CAPACITY]; + nk_uint values[NK_VALUE_PAGE_CAPACITY]; + struct nk_table *next, *prev; +}; + +union nk_page_data { + struct nk_table tbl; + struct nk_panel pan; + struct nk_window win; +}; + +struct nk_page_element { + union nk_page_data data; + struct nk_page_element *next; + struct nk_page_element *prev; +}; + +struct nk_page { + unsigned int size; + struct nk_page *next; + struct nk_page_element win[1]; +}; + +struct nk_pool { + struct nk_allocator alloc; + enum nk_allocation_type type; + unsigned int page_count; + struct nk_page *pages; + struct nk_page_element *freelist; + unsigned capacity; + nk_size size; + nk_size cap; +}; + +struct nk_context { +/* public: can be accessed freely */ + struct nk_input input; + struct nk_style style; + struct nk_buffer memory; + struct nk_clipboard clip; + nk_flags last_widget_state; + enum nk_button_behavior button_behavior; + struct nk_configuration_stacks stacks; + float delta_time_seconds; + +/* private: + should only be accessed if you + know what you are doing */ +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + struct nk_draw_list draw_list; +#endif +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_handle userdata; +#endif + /* text editor objects are quite big because of an internal + * undo/redo stack. Therefore it does not make sense to have one for + * each window for temporary use cases, so I only provide *one* instance + * for all windows. This works because the content is cleared anyway */ + struct nk_text_edit text_edit; + /* draw buffer used for overlay drawing operation like cursor */ + struct nk_command_buffer overlay; + + /* windows */ + int build; + int use_pool; + struct nk_pool pool; + struct nk_window *begin; + struct nk_window *end; + struct nk_window *active; + struct nk_window *current; + struct nk_page_element *freelist; + unsigned int count; + unsigned int seq; +}; + +/* ============================================================== + * MATH + * =============================================================== */ +#define NK_PI 3.141592654f +#define NK_UTF_INVALID 0xFFFD +#define NK_MAX_FLOAT_PRECISION 2 + +#define NK_UNUSED(x) ((void)(x)) +#define NK_SATURATE(x) (NK_MAX(0, NK_MIN(1.0f, x))) +#define NK_LEN(a) (sizeof(a)/sizeof(a)[0]) +#define NK_ABS(a) (((a) < 0) ? -(a) : (a)) +#define NK_BETWEEN(x, a, b) ((a) <= (x) && (x) < (b)) +#define NK_INBOX(px, py, x, y, w, h)\ + (NK_BETWEEN(px,x,x+w) && NK_BETWEEN(py,y,y+h)) +#define NK_INTERSECT(x0, y0, w0, h0, x1, y1, w1, h1) \ + ((x1 < (x0 + w0)) && (x0 < (x1 + w1)) && \ + (y1 < (y0 + h0)) && (y0 < (y1 + h1))) +#define NK_CONTAINS(x, y, w, h, bx, by, bw, bh)\ + (NK_INBOX(x,y, bx, by, bw, bh) && NK_INBOX(x+w,y+h, bx, by, bw, bh)) + +#define nk_vec2_sub(a, b) nk_vec2((a).x - (b).x, (a).y - (b).y) +#define nk_vec2_add(a, b) nk_vec2((a).x + (b).x, (a).y + (b).y) +#define nk_vec2_len_sqr(a) ((a).x*(a).x+(a).y*(a).y) +#define nk_vec2_muls(a, t) nk_vec2((a).x * (t), (a).y * (t)) + +#define nk_ptr_add(t, p, i) ((t*)((void*)((nk_byte*)(p) + (i)))) +#define nk_ptr_add_const(t, p, i) ((const t*)((const void*)((const nk_byte*)(p) + (i)))) +#define nk_zero_struct(s) nk_zero(&s, sizeof(s)) + +/* ============================================================== + * ALIGNMENT + * =============================================================== */ +/* Pointer to Integer type conversion for pointer alignment */ +#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC*/ +# define NK_UINT_TO_PTR(x) ((void*)(__PTRDIFF_TYPE__)(x)) +# define NK_PTR_TO_UINT(x) ((nk_size)(__PTRDIFF_TYPE__)(x)) +#elif !defined(__GNUC__) /* works for compilers other than LLVM */ +# define NK_UINT_TO_PTR(x) ((void*)&((char*)0)[x]) +# define NK_PTR_TO_UINT(x) ((nk_size)(((char*)x)-(char*)0)) +#elif defined(NK_USE_FIXED_TYPES) /* used if we have */ +# define NK_UINT_TO_PTR(x) ((void*)(uintptr_t)(x)) +# define NK_PTR_TO_UINT(x) ((uintptr_t)(x)) +#else /* generates warning but works */ +# define NK_UINT_TO_PTR(x) ((void*)(x)) +# define NK_PTR_TO_UINT(x) ((nk_size)(x)) +#endif + +#define NK_ALIGN_PTR(x, mask)\ + (NK_UINT_TO_PTR((NK_PTR_TO_UINT((nk_byte*)(x) + (mask-1)) & ~(mask-1)))) +#define NK_ALIGN_PTR_BACK(x, mask)\ + (NK_UINT_TO_PTR((NK_PTR_TO_UINT((nk_byte*)(x)) & ~(mask-1)))) + +#define NK_OFFSETOF(st,m) ((nk_ptr)&(((st*)0)->m)) +#define NK_CONTAINER_OF(ptr,type,member)\ + (type*)((void*)((char*)(1 ? (ptr): &((type*)0)->member) - NK_OFFSETOF(type, member))) + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +template struct nk_alignof; +template struct nk_helper{enum {value = size_diff};}; +template struct nk_helper{enum {value = nk_alignof::value};}; +template struct nk_alignof{struct Big {T x; char c;}; enum { + diff = sizeof(Big) - sizeof(T), value = nk_helper::value};}; +#define NK_ALIGNOF(t) (nk_alignof::value) +#elif defined(_MSC_VER) +#define NK_ALIGNOF(t) (__alignof(t)) +#else +#define NK_ALIGNOF(t) ((char*)(&((struct {char c; t _h;}*)0)->_h) - (char*)0) +#endif + +#endif /* NK_NUKLEAR_H_ */ + +#ifdef NK_IMPLEMENTATION + +#ifndef NK_INTERNAL_H +#define NK_INTERNAL_H + +#ifndef NK_POOL_DEFAULT_CAPACITY +#define NK_POOL_DEFAULT_CAPACITY 16 +#endif + +#ifndef NK_DEFAULT_COMMAND_BUFFER_SIZE +#define NK_DEFAULT_COMMAND_BUFFER_SIZE (4*1024) +#endif + +#ifndef NK_BUFFER_DEFAULT_INITIAL_SIZE +#define NK_BUFFER_DEFAULT_INITIAL_SIZE (4*1024) +#endif + +/* standard library headers */ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +#include /* malloc, free */ +#endif +#ifdef NK_INCLUDE_STANDARD_IO +#include /* fopen, fclose,... */ +#endif +#ifdef NK_INCLUDE_STANDARD_VARARGS +#include /* valist, va_start, va_end, ... */ +#endif +#ifndef NK_ASSERT +#include +#define NK_ASSERT(expr) assert(expr) +#endif + +#define NK_DEFAULT (-1) + +#ifndef NK_VSNPRINTF +/* If your compiler does support `vsnprintf` I would highly recommend + * defining this to vsnprintf instead since `vsprintf` is basically + * unbelievable unsafe and should *NEVER* be used. But I have to support + * it since C89 only provides this unsafe version. */ + #if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) ||\ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + (defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)) ||\ + (defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)) ||\ + defined(_ISOC99_SOURCE) || defined(_BSD_SOURCE) + #define NK_VSNPRINTF(s,n,f,a) vsnprintf(s,n,f,a) + #else + #define NK_VSNPRINTF(s,n,f,a) vsprintf(s,f,a) + #endif +#endif + +#define NK_SCHAR_MIN (-127) +#define NK_SCHAR_MAX 127 +#define NK_UCHAR_MIN 0 +#define NK_UCHAR_MAX 256 +#define NK_SSHORT_MIN (-32767) +#define NK_SSHORT_MAX 32767 +#define NK_USHORT_MIN 0 +#define NK_USHORT_MAX 65535 +#define NK_SINT_MIN (-2147483647) +#define NK_SINT_MAX 2147483647 +#define NK_UINT_MIN 0 +#define NK_UINT_MAX 4294967295u + +/* Make sure correct type size: + * This will fire with a negative subscript error if the type sizes + * are set incorrectly by the compiler, and compile out if not */ +NK_STATIC_ASSERT(sizeof(nk_size) >= sizeof(void*)); +NK_STATIC_ASSERT(sizeof(nk_ptr) == sizeof(void*)); +NK_STATIC_ASSERT(sizeof(nk_flags) >= 4); +NK_STATIC_ASSERT(sizeof(nk_rune) >= 4); +NK_STATIC_ASSERT(sizeof(nk_ushort) == 2); +NK_STATIC_ASSERT(sizeof(nk_short) == 2); +NK_STATIC_ASSERT(sizeof(nk_uint) == 4); +NK_STATIC_ASSERT(sizeof(nk_int) == 4); +NK_STATIC_ASSERT(sizeof(nk_byte) == 1); +#ifdef NK_INCLUDE_STANDARD_BOOL +NK_STATIC_ASSERT(sizeof(nk_bool) == sizeof(bool)); +#else +NK_STATIC_ASSERT(sizeof(nk_bool) == 4); +#endif + +NK_GLOBAL const struct nk_rect nk_null_rect = {-8192.0f, -8192.0f, 16384, 16384}; +#define NK_FLOAT_PRECISION 0.00000000000001 + +NK_GLOBAL const struct nk_color nk_red = {255,0,0,255}; +NK_GLOBAL const struct nk_color nk_green = {0,255,0,255}; +NK_GLOBAL const struct nk_color nk_blue = {0,0,255,255}; +NK_GLOBAL const struct nk_color nk_white = {255,255,255,255}; +NK_GLOBAL const struct nk_color nk_black = {0,0,0,255}; +NK_GLOBAL const struct nk_color nk_yellow = {255,255,0,255}; + +/* widget */ +#define nk_widget_state_reset(s)\ + if ((*(s)) & NK_WIDGET_STATE_MODIFIED)\ + (*(s)) = NK_WIDGET_STATE_INACTIVE|NK_WIDGET_STATE_MODIFIED;\ + else (*(s)) = NK_WIDGET_STATE_INACTIVE; + +/* math */ +NK_LIB float nk_inv_sqrt(float n); +#ifndef NK_SIN +NK_LIB float nk_sin(float x); +#endif +#ifndef NK_COS +NK_LIB float nk_cos(float x); +#endif +NK_LIB nk_uint nk_round_up_pow2(nk_uint v); +NK_LIB struct nk_rect nk_shrink_rect(struct nk_rect r, float amount); +NK_LIB struct nk_rect nk_pad_rect(struct nk_rect r, struct nk_vec2 pad); +NK_LIB void nk_unify(struct nk_rect *clip, const struct nk_rect *a, float x0, float y0, float x1, float y1); +NK_LIB double nk_pow(double x, int n); +NK_LIB int nk_ifloord(double x); +NK_LIB int nk_ifloorf(float x); +NK_LIB int nk_iceilf(float x); +NK_LIB int nk_log10(double n); + +/* util */ +enum {NK_DO_NOT_STOP_ON_NEW_LINE, NK_STOP_ON_NEW_LINE}; +NK_LIB nk_bool nk_is_lower(int c); +NK_LIB nk_bool nk_is_upper(int c); +NK_LIB int nk_to_upper(int c); +NK_LIB int nk_to_lower(int c); + +#ifndef NK_MEMCPY +NK_LIB void* nk_memcopy(void *dst, const void *src, nk_size n); +#endif +#ifndef NK_MEMSET +NK_LIB void nk_memset(void *ptr, int c0, nk_size size); +#endif +NK_LIB void nk_zero(void *ptr, nk_size size); +NK_LIB char *nk_itoa(char *s, long n); +NK_LIB int nk_string_float_limit(char *string, int prec); +#ifndef NK_DTOA +NK_LIB char *nk_dtoa(char *s, double n); +#endif +NK_LIB int nk_text_clamp(const struct nk_user_font *font, const char *text, int text_len, float space, int *glyphs, float *text_width, nk_rune *sep_list, int sep_count); +NK_LIB struct nk_vec2 nk_text_calculate_text_bounds(const struct nk_user_font *font, const char *begin, int byte_len, float row_height, const char **remaining, struct nk_vec2 *out_offset, int *glyphs, int op); +#ifdef NK_INCLUDE_STANDARD_VARARGS +NK_LIB int nk_strfmt(char *buf, int buf_size, const char *fmt, va_list args); +#endif +#ifdef NK_INCLUDE_STANDARD_IO +NK_LIB char *nk_file_load(const char* path, nk_size* siz, struct nk_allocator *alloc); +#endif + +/* buffer */ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_LIB void* nk_malloc(nk_handle unused, void *old,nk_size size); +NK_LIB void nk_mfree(nk_handle unused, void *ptr); +#endif +NK_LIB void* nk_buffer_align(void *unaligned, nk_size align, nk_size *alignment, enum nk_buffer_allocation_type type); +NK_LIB void* nk_buffer_alloc(struct nk_buffer *b, enum nk_buffer_allocation_type type, nk_size size, nk_size align); +NK_LIB void* nk_buffer_realloc(struct nk_buffer *b, nk_size capacity, nk_size *size); + +/* draw */ +NK_LIB void nk_command_buffer_init(struct nk_command_buffer *cb, struct nk_buffer *b, enum nk_command_clipping clip); +NK_LIB void nk_command_buffer_reset(struct nk_command_buffer *b); +NK_LIB void* nk_command_buffer_push(struct nk_command_buffer* b, enum nk_command_type t, nk_size size); +NK_LIB void nk_draw_symbol(struct nk_command_buffer *out, enum nk_symbol_type type, struct nk_rect content, struct nk_color background, struct nk_color foreground, float border_width, const struct nk_user_font *font); + +/* buffering */ +NK_LIB void nk_start_buffer(struct nk_context *ctx, struct nk_command_buffer *b); +NK_LIB void nk_start(struct nk_context *ctx, struct nk_window *win); +NK_LIB void nk_start_popup(struct nk_context *ctx, struct nk_window *win); +NK_LIB void nk_finish_popup(struct nk_context *ctx, struct nk_window*); +NK_LIB void nk_finish_buffer(struct nk_context *ctx, struct nk_command_buffer *b); +NK_LIB void nk_finish(struct nk_context *ctx, struct nk_window *w); +NK_LIB void nk_build(struct nk_context *ctx); + +/* text editor */ +NK_LIB void nk_textedit_clear_state(struct nk_text_edit *state, enum nk_text_edit_type type, nk_plugin_filter filter); +NK_LIB void nk_textedit_click(struct nk_text_edit *state, float x, float y, const struct nk_user_font *font, float row_height); +NK_LIB void nk_textedit_drag(struct nk_text_edit *state, float x, float y, const struct nk_user_font *font, float row_height); +NK_LIB void nk_textedit_key(struct nk_text_edit *state, enum nk_keys key, int shift_mod, const struct nk_user_font *font, float row_height); + +/* window */ +enum nk_window_insert_location { + NK_INSERT_BACK, /* inserts window into the back of list (front of screen) */ + NK_INSERT_FRONT /* inserts window into the front of list (back of screen) */ +}; +NK_LIB void *nk_create_window(struct nk_context *ctx); +NK_LIB void nk_remove_window(struct nk_context*, struct nk_window*); +NK_LIB void nk_free_window(struct nk_context *ctx, struct nk_window *win); +NK_LIB struct nk_window *nk_find_window(struct nk_context *ctx, nk_hash hash, const char *name); +NK_LIB void nk_insert_window(struct nk_context *ctx, struct nk_window *win, enum nk_window_insert_location loc); + +/* pool */ +NK_LIB void nk_pool_init(struct nk_pool *pool, struct nk_allocator *alloc, unsigned int capacity); +NK_LIB void nk_pool_free(struct nk_pool *pool); +NK_LIB void nk_pool_init_fixed(struct nk_pool *pool, void *memory, nk_size size); +NK_LIB struct nk_page_element *nk_pool_alloc(struct nk_pool *pool); + +/* page-element */ +NK_LIB struct nk_page_element* nk_create_page_element(struct nk_context *ctx); +NK_LIB void nk_link_page_element_into_freelist(struct nk_context *ctx, struct nk_page_element *elem); +NK_LIB void nk_free_page_element(struct nk_context *ctx, struct nk_page_element *elem); + +/* table */ +NK_LIB struct nk_table* nk_create_table(struct nk_context *ctx); +NK_LIB void nk_remove_table(struct nk_window *win, struct nk_table *tbl); +NK_LIB void nk_free_table(struct nk_context *ctx, struct nk_table *tbl); +NK_LIB void nk_push_table(struct nk_window *win, struct nk_table *tbl); +NK_LIB nk_uint *nk_add_value(struct nk_context *ctx, struct nk_window *win, nk_hash name, nk_uint value); +NK_LIB nk_uint *nk_find_value(struct nk_window *win, nk_hash name); + +/* panel */ +NK_LIB void *nk_create_panel(struct nk_context *ctx); +NK_LIB void nk_free_panel(struct nk_context*, struct nk_panel *pan); +NK_LIB nk_bool nk_panel_has_header(nk_flags flags, const char *title); +NK_LIB struct nk_vec2 nk_panel_get_padding(const struct nk_style *style, enum nk_panel_type type); +NK_LIB float nk_panel_get_border(const struct nk_style *style, nk_flags flags, enum nk_panel_type type); +NK_LIB struct nk_color nk_panel_get_border_color(const struct nk_style *style, enum nk_panel_type type); +NK_LIB nk_bool nk_panel_is_sub(enum nk_panel_type type); +NK_LIB nk_bool nk_panel_is_nonblock(enum nk_panel_type type); +NK_LIB nk_bool nk_panel_begin(struct nk_context *ctx, const char *title, enum nk_panel_type panel_type); +NK_LIB void nk_panel_end(struct nk_context *ctx); + +/* layout */ +NK_LIB float nk_layout_row_calculate_usable_space(const struct nk_style *style, enum nk_panel_type type, float total_space, int columns); +NK_LIB void nk_panel_layout(const struct nk_context *ctx, struct nk_window *win, float height, int cols); +NK_LIB void nk_row_layout(struct nk_context *ctx, enum nk_layout_format fmt, float height, int cols, int width); +NK_LIB void nk_panel_alloc_row(const struct nk_context *ctx, struct nk_window *win); +NK_LIB void nk_layout_widget_space(struct nk_rect *bounds, const struct nk_context *ctx, struct nk_window *win, int modify); +NK_LIB void nk_panel_alloc_space(struct nk_rect *bounds, const struct nk_context *ctx); +NK_LIB void nk_layout_peek(struct nk_rect *bounds, struct nk_context *ctx); + +/* popup */ +NK_LIB nk_bool nk_nonblock_begin(struct nk_context *ctx, nk_flags flags, struct nk_rect body, struct nk_rect header, enum nk_panel_type panel_type); + +/* text */ +struct nk_text { + struct nk_vec2 padding; + struct nk_color background; + struct nk_color text; +}; +NK_LIB void nk_widget_text(struct nk_command_buffer *o, struct nk_rect b, const char *string, int len, const struct nk_text *t, nk_flags a, const struct nk_user_font *f); +NK_LIB void nk_widget_text_wrap(struct nk_command_buffer *o, struct nk_rect b, const char *string, int len, const struct nk_text *t, const struct nk_user_font *f); + +/* button */ +NK_LIB nk_bool nk_button_behavior(nk_flags *state, struct nk_rect r, const struct nk_input *i, enum nk_button_behavior behavior); +NK_LIB const struct nk_style_item* nk_draw_button(struct nk_command_buffer *out, const struct nk_rect *bounds, nk_flags state, const struct nk_style_button *style); +NK_LIB nk_bool nk_do_button(nk_flags *state, struct nk_command_buffer *out, struct nk_rect r, const struct nk_style_button *style, const struct nk_input *in, enum nk_button_behavior behavior, struct nk_rect *content); +NK_LIB void nk_draw_button_text(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state, const struct nk_style_button *style, const char *txt, int len, nk_flags text_alignment, const struct nk_user_font *font); +NK_LIB nk_bool nk_do_button_text(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, const char *string, int len, nk_flags align, enum nk_button_behavior behavior, const struct nk_style_button *style, const struct nk_input *in, const struct nk_user_font *font); +NK_LIB void nk_draw_button_symbol(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state, const struct nk_style_button *style, enum nk_symbol_type type, const struct nk_user_font *font); +NK_LIB nk_bool nk_do_button_symbol(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, enum nk_symbol_type symbol, enum nk_button_behavior behavior, const struct nk_style_button *style, const struct nk_input *in, const struct nk_user_font *font); +NK_LIB void nk_draw_button_image(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state, const struct nk_style_button *style, const struct nk_image *img); +NK_LIB nk_bool nk_do_button_image(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, struct nk_image img, enum nk_button_behavior b, const struct nk_style_button *style, const struct nk_input *in); +NK_LIB void nk_draw_button_text_symbol(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *label, const struct nk_rect *symbol, nk_flags state, const struct nk_style_button *style, const char *str, int len, enum nk_symbol_type type, const struct nk_user_font *font); +NK_LIB nk_bool nk_do_button_text_symbol(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, enum nk_symbol_type symbol, const char *str, int len, nk_flags align, enum nk_button_behavior behavior, const struct nk_style_button *style, const struct nk_user_font *font, const struct nk_input *in); +NK_LIB void nk_draw_button_text_image(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *label, const struct nk_rect *image, nk_flags state, const struct nk_style_button *style, const char *str, int len, const struct nk_user_font *font, const struct nk_image *img); +NK_LIB nk_bool nk_do_button_text_image(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, struct nk_image img, const char* str, int len, nk_flags align, enum nk_button_behavior behavior, const struct nk_style_button *style, const struct nk_user_font *font, const struct nk_input *in); + +/* toggle */ +enum nk_toggle_type { + NK_TOGGLE_CHECK, + NK_TOGGLE_OPTION +}; +NK_LIB nk_bool nk_toggle_behavior(const struct nk_input *in, struct nk_rect select, nk_flags *state, nk_bool active); +NK_LIB void nk_draw_checkbox(struct nk_command_buffer *out, nk_flags state, const struct nk_style_toggle *style, nk_bool active, const struct nk_rect *label, const struct nk_rect *selector, const struct nk_rect *cursors, const char *string, int len, const struct nk_user_font *font); +NK_LIB void nk_draw_option(struct nk_command_buffer *out, nk_flags state, const struct nk_style_toggle *style, nk_bool active, const struct nk_rect *label, const struct nk_rect *selector, const struct nk_rect *cursors, const char *string, int len, const struct nk_user_font *font); +NK_LIB nk_bool nk_do_toggle(nk_flags *state, struct nk_command_buffer *out, struct nk_rect r, nk_bool *active, const char *str, int len, enum nk_toggle_type type, const struct nk_style_toggle *style, const struct nk_input *in, const struct nk_user_font *font); + +/* progress */ +NK_LIB nk_size nk_progress_behavior(nk_flags *state, struct nk_input *in, struct nk_rect r, struct nk_rect cursor, nk_size max, nk_size value, nk_bool modifiable); +NK_LIB void nk_draw_progress(struct nk_command_buffer *out, nk_flags state, const struct nk_style_progress *style, const struct nk_rect *bounds, const struct nk_rect *scursor, nk_size value, nk_size max); +NK_LIB nk_size nk_do_progress(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, nk_size value, nk_size max, nk_bool modifiable, const struct nk_style_progress *style, struct nk_input *in); + +/* slider */ +NK_LIB float nk_slider_behavior(nk_flags *state, struct nk_rect *logical_cursor, struct nk_rect *visual_cursor, struct nk_input *in, struct nk_rect bounds, float slider_min, float slider_max, float slider_value, float slider_step, float slider_steps); +NK_LIB void nk_draw_slider(struct nk_command_buffer *out, nk_flags state, const struct nk_style_slider *style, const struct nk_rect *bounds, const struct nk_rect *visual_cursor, float min, float value, float max); +NK_LIB float nk_do_slider(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, float min, float val, float max, float step, const struct nk_style_slider *style, struct nk_input *in, const struct nk_user_font *font); + +/* scrollbar */ +NK_LIB float nk_scrollbar_behavior(nk_flags *state, struct nk_input *in, int has_scrolling, const struct nk_rect *scroll, const struct nk_rect *cursor, const struct nk_rect *empty0, const struct nk_rect *empty1, float scroll_offset, float target, float scroll_step, enum nk_orientation o); +NK_LIB void nk_draw_scrollbar(struct nk_command_buffer *out, nk_flags state, const struct nk_style_scrollbar *style, const struct nk_rect *bounds, const struct nk_rect *scroll); +NK_LIB float nk_do_scrollbarv(nk_flags *state, struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, float offset, float target, float step, float button_pixel_inc, const struct nk_style_scrollbar *style, struct nk_input *in, const struct nk_user_font *font); +NK_LIB float nk_do_scrollbarh(nk_flags *state, struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, float offset, float target, float step, float button_pixel_inc, const struct nk_style_scrollbar *style, struct nk_input *in, const struct nk_user_font *font); + +/* selectable */ +NK_LIB void nk_draw_selectable(struct nk_command_buffer *out, nk_flags state, const struct nk_style_selectable *style, nk_bool active, const struct nk_rect *bounds, const struct nk_rect *icon, const struct nk_image *img, enum nk_symbol_type sym, const char *string, int len, nk_flags align, const struct nk_user_font *font); +NK_LIB nk_bool nk_do_selectable(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, const char *str, int len, nk_flags align, nk_bool *value, const struct nk_style_selectable *style, const struct nk_input *in, const struct nk_user_font *font); +NK_LIB nk_bool nk_do_selectable_image(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, const char *str, int len, nk_flags align, nk_bool *value, const struct nk_image *img, const struct nk_style_selectable *style, const struct nk_input *in, const struct nk_user_font *font); + +/* edit */ +NK_LIB void nk_edit_draw_text(struct nk_command_buffer *out, const struct nk_style_edit *style, float pos_x, float pos_y, float x_offset, const char *text, int byte_len, float row_height, const struct nk_user_font *font, struct nk_color background, struct nk_color foreground, nk_bool is_selected); +NK_LIB nk_flags nk_do_edit(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, nk_flags flags, nk_plugin_filter filter, struct nk_text_edit *edit, const struct nk_style_edit *style, struct nk_input *in, const struct nk_user_font *font); + +/* color-picker */ +NK_LIB nk_bool nk_color_picker_behavior(nk_flags *state, const struct nk_rect *bounds, const struct nk_rect *matrix, const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, struct nk_colorf *color, const struct nk_input *in); +NK_LIB void nk_draw_color_picker(struct nk_command_buffer *o, const struct nk_rect *matrix, const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, struct nk_colorf col); +NK_LIB nk_bool nk_do_color_picker(nk_flags *state, struct nk_command_buffer *out, struct nk_colorf *col, enum nk_color_format fmt, struct nk_rect bounds, struct nk_vec2 padding, const struct nk_input *in, const struct nk_user_font *font); + +/* property */ +enum nk_property_status { + NK_PROPERTY_DEFAULT, + NK_PROPERTY_EDIT, + NK_PROPERTY_DRAG +}; +enum nk_property_filter { + NK_FILTER_INT, + NK_FILTER_FLOAT +}; +enum nk_property_kind { + NK_PROPERTY_INT, + NK_PROPERTY_FLOAT, + NK_PROPERTY_DOUBLE +}; +union nk_property { + int i; + float f; + double d; +}; +struct nk_property_variant { + enum nk_property_kind kind; + union nk_property value; + union nk_property min_value; + union nk_property max_value; + union nk_property step; +}; +NK_LIB struct nk_property_variant nk_property_variant_int(int value, int min_value, int max_value, int step); +NK_LIB struct nk_property_variant nk_property_variant_float(float value, float min_value, float max_value, float step); +NK_LIB struct nk_property_variant nk_property_variant_double(double value, double min_value, double max_value, double step); + +NK_LIB void nk_drag_behavior(nk_flags *state, const struct nk_input *in, struct nk_rect drag, struct nk_property_variant *variant, float inc_per_pixel); +NK_LIB void nk_property_behavior(nk_flags *ws, const struct nk_input *in, struct nk_rect property, struct nk_rect label, struct nk_rect edit, struct nk_rect empty, int *state, struct nk_property_variant *variant, float inc_per_pixel); +NK_LIB void nk_draw_property(struct nk_command_buffer *out, const struct nk_style_property *style, const struct nk_rect *bounds, const struct nk_rect *label, nk_flags state, const char *name, int len, const struct nk_user_font *font); +NK_LIB void nk_do_property(nk_flags *ws, struct nk_command_buffer *out, struct nk_rect property, const char *name, struct nk_property_variant *variant, float inc_per_pixel, char *buffer, int *len, int *state, int *cursor, int *select_begin, int *select_end, const struct nk_style_property *style, enum nk_property_filter filter, struct nk_input *in, const struct nk_user_font *font, struct nk_text_edit *text_edit, enum nk_button_behavior behavior); +NK_LIB void nk_property(struct nk_context *ctx, const char *name, struct nk_property_variant *variant, float inc_per_pixel, const enum nk_property_filter filter); + +#ifdef NK_INCLUDE_FONT_BAKING + +#define STB_RECT_PACK_IMPLEMENTATION +#define STB_TRUETYPE_IMPLEMENTATION + +/* Allow consumer to define own STBTT_malloc/STBTT_free, and use the font atlas' allocator otherwise */ +#ifndef STBTT_malloc +static void* +nk_stbtt_malloc(nk_size size, void *user_data) { + struct nk_allocator *alloc = (struct nk_allocator *) user_data; + return alloc->alloc(alloc->userdata, 0, size); +} + +static void +nk_stbtt_free(void *ptr, void *user_data) { + struct nk_allocator *alloc = (struct nk_allocator *) user_data; + alloc->free(alloc->userdata, ptr); +} + +#define STBTT_malloc(x,u) nk_stbtt_malloc(x,u) +#define STBTT_free(x,u) nk_stbtt_free(x,u) + +#endif /* STBTT_malloc */ + +#endif /* NK_INCLUDE_FONT_BAKING */ + +#endif + + + + + +/* =============================================================== + * + * MATH + * + * ===============================================================*/ +/* Since nuklear is supposed to work on all systems providing floating point + math without any dependencies I also had to implement my own math functions + for sqrt, sin and cos. Since the actual highly accurate implementations for + the standard library functions are quite complex and I do not need high + precision for my use cases I use approximations. + + Sqrt + ---- + For square root nuklear uses the famous fast inverse square root: + https://en.wikipedia.org/wiki/Fast_inverse_square_root with + slightly tweaked magic constant. While on today's hardware it is + probably not faster it is still fast and accurate enough for + nuklear's use cases. IMPORTANT: this requires float format IEEE 754 + + Sine/Cosine + ----------- + All constants inside both function are generated Remez's minimax + approximations for value range 0...2*PI. The reason why I decided to + approximate exactly that range is that nuklear only needs sine and + cosine to generate circles which only requires that exact range. + In addition I used Remez instead of Taylor for additional precision: + www.lolengine.net/blog/2011/12/21/better-function-approximations. + + The tool I used to generate constants for both sine and cosine + (it can actually approximate a lot more functions) can be + found here: www.lolengine.net/wiki/oss/lolremez +*/ +NK_LIB float +nk_inv_sqrt(float n) +{ + float x2; + const float threehalfs = 1.5f; + union {nk_uint i; float f;} conv = {0}; + conv.f = n; + x2 = n * 0.5f; + conv.i = 0x5f375A84 - (conv.i >> 1); + conv.f = conv.f * (threehalfs - (x2 * conv.f * conv.f)); + return conv.f; +} +#ifndef NK_SIN +#define NK_SIN nk_sin +NK_LIB float +nk_sin(float x) +{ + NK_STORAGE const float a0 = +1.91059300966915117e-31f; + NK_STORAGE const float a1 = +1.00086760103908896f; + NK_STORAGE const float a2 = -1.21276126894734565e-2f; + NK_STORAGE const float a3 = -1.38078780785773762e-1f; + NK_STORAGE const float a4 = -2.67353392911981221e-2f; + NK_STORAGE const float a5 = +2.08026600266304389e-2f; + NK_STORAGE const float a6 = -3.03996055049204407e-3f; + NK_STORAGE const float a7 = +1.38235642404333740e-4f; + return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*a7)))))); +} +#endif +#ifndef NK_COS +#define NK_COS nk_cos +NK_LIB float +nk_cos(float x) +{ + /* New implementation. Also generated using lolremez. */ + /* Old version significantly deviated from expected results. */ + NK_STORAGE const float a0 = 9.9995999154986614e-1f; + NK_STORAGE const float a1 = 1.2548995793001028e-3f; + NK_STORAGE const float a2 = -5.0648546280678015e-1f; + NK_STORAGE const float a3 = 1.2942246466519995e-2f; + NK_STORAGE const float a4 = 2.8668384702547972e-2f; + NK_STORAGE const float a5 = 7.3726485210586547e-3f; + NK_STORAGE const float a6 = -3.8510875386947414e-3f; + NK_STORAGE const float a7 = 4.7196604604366623e-4f; + NK_STORAGE const float a8 = -1.8776444013090451e-5f; + return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*(a7 + x*a8))))))); +} +#endif +NK_LIB nk_uint +nk_round_up_pow2(nk_uint v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} +NK_LIB double +nk_pow(double x, int n) +{ + /* check the sign of n */ + double r = 1; + int plus = n >= 0; + n = (plus) ? n : -n; + while (n > 0) { + if ((n & 1) == 1) + r *= x; + n /= 2; + x *= x; + } + return plus ? r : 1.0 / r; +} +NK_LIB int +nk_ifloord(double x) +{ + x = (double)((int)x - ((x < 0.0) ? 1 : 0)); + return (int)x; +} +NK_LIB int +nk_ifloorf(float x) +{ + x = (float)((int)x - ((x < 0.0f) ? 1 : 0)); + return (int)x; +} +NK_LIB int +nk_iceilf(float x) +{ + if (x >= 0) { + int i = (int)x; + return (x > i) ? i+1: i; + } else { + int t = (int)x; + float r = x - (float)t; + return (r > 0.0f) ? t+1: t; + } +} +NK_LIB int +nk_log10(double n) +{ + int neg; + int ret; + int exp = 0; + + neg = (n < 0) ? 1 : 0; + ret = (neg) ? (int)-n : (int)n; + while ((ret / 10) > 0) { + ret /= 10; + exp++; + } + if (neg) exp = -exp; + return exp; +} +NK_API struct nk_rect +nk_get_null_rect(void) +{ + return nk_null_rect; +} +NK_API struct nk_rect +nk_rect(float x, float y, float w, float h) +{ + struct nk_rect r; + r.x = x; r.y = y; + r.w = w; r.h = h; + return r; +} +NK_API struct nk_rect +nk_recti(int x, int y, int w, int h) +{ + struct nk_rect r; + r.x = (float)x; + r.y = (float)y; + r.w = (float)w; + r.h = (float)h; + return r; +} +NK_API struct nk_rect +nk_recta(struct nk_vec2 pos, struct nk_vec2 size) +{ + return nk_rect(pos.x, pos.y, size.x, size.y); +} +NK_API struct nk_rect +nk_rectv(const float *r) +{ + return nk_rect(r[0], r[1], r[2], r[3]); +} +NK_API struct nk_rect +nk_rectiv(const int *r) +{ + return nk_recti(r[0], r[1], r[2], r[3]); +} +NK_API struct nk_vec2 +nk_rect_pos(struct nk_rect r) +{ + struct nk_vec2 ret; + ret.x = r.x; ret.y = r.y; + return ret; +} +NK_API struct nk_vec2 +nk_rect_size(struct nk_rect r) +{ + struct nk_vec2 ret; + ret.x = r.w; ret.y = r.h; + return ret; +} +NK_LIB struct nk_rect +nk_shrink_rect(struct nk_rect r, float amount) +{ + struct nk_rect res; + r.w = NK_MAX(r.w, 2 * amount); + r.h = NK_MAX(r.h, 2 * amount); + res.x = r.x + amount; + res.y = r.y + amount; + res.w = r.w - 2 * amount; + res.h = r.h - 2 * amount; + return res; +} +NK_LIB struct nk_rect +nk_pad_rect(struct nk_rect r, struct nk_vec2 pad) +{ + r.w = NK_MAX(r.w, 2 * pad.x); + r.h = NK_MAX(r.h, 2 * pad.y); + r.x += pad.x; r.y += pad.y; + r.w -= 2 * pad.x; + r.h -= 2 * pad.y; + return r; +} +NK_API struct nk_vec2 +nk_vec2(float x, float y) +{ + struct nk_vec2 ret; + ret.x = x; ret.y = y; + return ret; +} +NK_API struct nk_vec2 +nk_vec2i(int x, int y) +{ + struct nk_vec2 ret; + ret.x = (float)x; + ret.y = (float)y; + return ret; +} +NK_API struct nk_vec2 +nk_vec2v(const float *v) +{ + return nk_vec2(v[0], v[1]); +} +NK_API struct nk_vec2 +nk_vec2iv(const int *v) +{ + return nk_vec2i(v[0], v[1]); +} +NK_LIB void +nk_unify(struct nk_rect *clip, const struct nk_rect *a, float x0, float y0, + float x1, float y1) +{ + NK_ASSERT(a); + NK_ASSERT(clip); + clip->x = NK_MAX(a->x, x0); + clip->y = NK_MAX(a->y, y0); + clip->w = NK_MIN(a->x + a->w, x1) - clip->x; + clip->h = NK_MIN(a->y + a->h, y1) - clip->y; + clip->w = NK_MAX(0, clip->w); + clip->h = NK_MAX(0, clip->h); +} + +NK_API void +nk_triangle_from_direction(struct nk_vec2 *result, struct nk_rect r, + float pad_x, float pad_y, enum nk_heading direction) +{ + float w_half, h_half; + NK_ASSERT(result); + + r.w = NK_MAX(2 * pad_x, r.w); + r.h = NK_MAX(2 * pad_y, r.h); + r.w = r.w - 2 * pad_x; + r.h = r.h - 2 * pad_y; + + r.x = r.x + pad_x; + r.y = r.y + pad_y; + + w_half = r.w / 2.0f; + h_half = r.h / 2.0f; + + if (direction == NK_UP) { + result[0] = nk_vec2(r.x + w_half, r.y); + result[1] = nk_vec2(r.x + r.w, r.y + r.h); + result[2] = nk_vec2(r.x, r.y + r.h); + } else if (direction == NK_RIGHT) { + result[0] = nk_vec2(r.x, r.y); + result[1] = nk_vec2(r.x + r.w, r.y + h_half); + result[2] = nk_vec2(r.x, r.y + r.h); + } else if (direction == NK_DOWN) { + result[0] = nk_vec2(r.x, r.y); + result[1] = nk_vec2(r.x + r.w, r.y); + result[2] = nk_vec2(r.x + w_half, r.y + r.h); + } else { + result[0] = nk_vec2(r.x, r.y + h_half); + result[1] = nk_vec2(r.x + r.w, r.y); + result[2] = nk_vec2(r.x + r.w, r.y + r.h); + } +} + + + + + +/* =============================================================== + * + * UTIL + * + * ===============================================================*/ +NK_INTERN int nk_str_match_here(const char *regexp, const char *text); +NK_INTERN int nk_str_match_star(int c, const char *regexp, const char *text); +NK_LIB nk_bool nk_is_lower(int c) {return (c >= 'a' && c <= 'z') || (c >= 0xE0 && c <= 0xFF);} +NK_LIB nk_bool nk_is_upper(int c){return (c >= 'A' && c <= 'Z') || (c >= 0xC0 && c <= 0xDF);} +NK_LIB int nk_to_upper(int c) {return (c >= 'a' && c <= 'z') ? (c - ('a' - 'A')) : c;} +NK_LIB int nk_to_lower(int c) {return (c >= 'A' && c <= 'Z') ? (c - ('a' + 'A')) : c;} + +#ifndef NK_MEMCPY +#define NK_MEMCPY nk_memcopy +NK_LIB void* +nk_memcopy(void *dst0, const void *src0, nk_size length) +{ + nk_ptr t; + char *dst = (char*)dst0; + const char *src = (const char*)src0; + if (length == 0 || dst == src) + goto done; + + #define nk_word int + #define nk_wsize sizeof(nk_word) + #define nk_wmask (nk_wsize-1) + #define NK_TLOOP(s) if (t) NK_TLOOP1(s) + #define NK_TLOOP1(s) do { s; } while (--t) + + if (dst < src) { + t = (nk_ptr)src; /* only need low bits */ + if ((t | (nk_ptr)dst) & nk_wmask) { + if ((t ^ (nk_ptr)dst) & nk_wmask || length < nk_wsize) + t = length; + else + t = nk_wsize - (t & nk_wmask); + length -= t; + NK_TLOOP1(*dst++ = *src++); + } + t = length / nk_wsize; + NK_TLOOP(*(nk_word*)(void*)dst = *(const nk_word*)(const void*)src; + src += nk_wsize; dst += nk_wsize); + t = length & nk_wmask; + NK_TLOOP(*dst++ = *src++); + } else { + src += length; + dst += length; + t = (nk_ptr)src; + if ((t | (nk_ptr)dst) & nk_wmask) { + if ((t ^ (nk_ptr)dst) & nk_wmask || length <= nk_wsize) + t = length; + else + t &= nk_wmask; + length -= t; + NK_TLOOP1(*--dst = *--src); + } + t = length / nk_wsize; + NK_TLOOP(src -= nk_wsize; dst -= nk_wsize; + *(nk_word*)(void*)dst = *(const nk_word*)(const void*)src); + t = length & nk_wmask; + NK_TLOOP(*--dst = *--src); + } + #undef nk_word + #undef nk_wsize + #undef nk_wmask + #undef NK_TLOOP + #undef NK_TLOOP1 +done: + return (dst0); +} +#endif +#ifndef NK_MEMSET +#define NK_MEMSET nk_memset +NK_LIB void +nk_memset(void *ptr, int c0, nk_size size) +{ + #define nk_word unsigned + #define nk_wsize sizeof(nk_word) + #define nk_wmask (nk_wsize - 1) + nk_byte *dst = (nk_byte*)ptr; + unsigned c = 0; + nk_size t = 0; + + if ((c = (nk_byte)c0) != 0) { + c = (c << 8) | c; /* at least 16-bits */ + if (sizeof(unsigned int) > 2) + c = (c << 16) | c; /* at least 32-bits*/ + } + + /* too small of a word count */ + dst = (nk_byte*)ptr; + if (size < 3 * nk_wsize) { + while (size--) *dst++ = (nk_byte)c0; + return; + } + + /* align destination */ + if ((t = NK_PTR_TO_UINT(dst) & nk_wmask) != 0) { + t = nk_wsize -t; + size -= t; + do { + *dst++ = (nk_byte)c0; + } while (--t != 0); + } + + /* fill word */ + t = size / nk_wsize; + do { + *(nk_word*)((void*)dst) = c; + dst += nk_wsize; + } while (--t != 0); + + /* fill trailing bytes */ + t = (size & nk_wmask); + if (t != 0) { + do { + *dst++ = (nk_byte)c0; + } while (--t != 0); + } + + #undef nk_word + #undef nk_wsize + #undef nk_wmask +} +#endif +NK_LIB void +nk_zero(void *ptr, nk_size size) +{ + NK_ASSERT(ptr); + NK_MEMSET(ptr, 0, size); +} +NK_API int +nk_strlen(const char *str) +{ + int siz = 0; + NK_ASSERT(str); + while (str && *str++ != '\0') siz++; + return siz; +} +NK_API int +nk_strtoi(const char *str, const char **endptr) +{ + int neg = 1; + const char *p = str; + int value = 0; + + NK_ASSERT(str); + if (!str) return 0; + + /* skip whitespace */ + while (*p == ' ') p++; + if (*p == '-') { + neg = -1; + p++; + } + while (*p && *p >= '0' && *p <= '9') { + value = value * 10 + (int) (*p - '0'); + p++; + } + if (endptr) + *endptr = p; + return neg*value; +} +NK_API double +nk_strtod(const char *str, const char **endptr) +{ + double m; + double neg = 1.0; + const char *p = str; + double value = 0; + double number = 0; + + NK_ASSERT(str); + if (!str) return 0; + + /* skip whitespace */ + while (*p == ' ') p++; + if (*p == '-') { + neg = -1.0; + p++; + } + + while (*p && *p != '.' && *p != 'e') { + value = value * 10.0 + (double) (*p - '0'); + p++; + } + + if (*p == '.') { + p++; + for(m = 0.1; *p && *p != 'e'; p++ ) { + value = value + (double) (*p - '0') * m; + m *= 0.1; + } + } + if (*p == 'e') { + int i, pow, div; + p++; + if (*p == '-') { + div = nk_true; + p++; + } else if (*p == '+') { + div = nk_false; + p++; + } else div = nk_false; + + for (pow = 0; *p; p++) + pow = pow * 10 + (int) (*p - '0'); + + for (m = 1.0, i = 0; i < pow; i++) + m *= 10.0; + + if (div) + value /= m; + else value *= m; + } + number = value * neg; + if (endptr) + *endptr = p; + return number; +} +NK_API float +nk_strtof(const char *str, const char **endptr) +{ + float float_value; + double double_value; + double_value = NK_STRTOD(str, endptr); + float_value = (float)double_value; + return float_value; +} +NK_API int +nk_stricmp(const char *s1, const char *s2) +{ + nk_int c1,c2,d; + do { + c1 = *s1++; + c2 = *s2++; + d = c1 - c2; + while (d) { + if (c1 <= 'Z' && c1 >= 'A') { + d += ('a' - 'A'); + if (!d) break; + } + if (c2 <= 'Z' && c2 >= 'A') { + d -= ('a' - 'A'); + if (!d) break; + } + return ((d >= 0) << 1) - 1; + } + } while (c1); + return 0; +} +NK_API int +nk_stricmpn(const char *s1, const char *s2, int n) +{ + int c1,c2,d; + NK_ASSERT(n >= 0); + do { + c1 = *s1++; + c2 = *s2++; + if (!n--) return 0; + + d = c1 - c2; + while (d) { + if (c1 <= 'Z' && c1 >= 'A') { + d += ('a' - 'A'); + if (!d) break; + } + if (c2 <= 'Z' && c2 >= 'A') { + d -= ('a' - 'A'); + if (!d) break; + } + return ((d >= 0) << 1) - 1; + } + } while (c1); + return 0; +} +NK_INTERN int +nk_str_match_here(const char *regexp, const char *text) +{ + if (regexp[0] == '\0') + return 1; + if (regexp[1] == '*') + return nk_str_match_star(regexp[0], regexp+2, text); + if (regexp[0] == '$' && regexp[1] == '\0') + return *text == '\0'; + if (*text!='\0' && (regexp[0]=='.' || regexp[0]==*text)) + return nk_str_match_here(regexp+1, text+1); + return 0; +} +NK_INTERN int +nk_str_match_star(int c, const char *regexp, const char *text) +{ + do {/* a '* matches zero or more instances */ + if (nk_str_match_here(regexp, text)) + return 1; + } while (*text != '\0' && (*text++ == c || c == '.')); + return 0; +} +NK_API int +nk_strfilter(const char *text, const char *regexp) +{ + /* + c matches any literal character c + . matches any single character + ^ matches the beginning of the input string + $ matches the end of the input string + * matches zero or more occurrences of the previous character*/ + if (regexp[0] == '^') + return nk_str_match_here(regexp+1, text); + do { /* must look even if string is empty */ + if (nk_str_match_here(regexp, text)) + return 1; + } while (*text++ != '\0'); + return 0; +} +NK_API int +nk_strmatch_fuzzy_text(const char *str, int str_len, + const char *pattern, int *out_score) +{ + /* Returns true if each character in pattern is found sequentially within str + * if found then out_score is also set. Score value has no intrinsic meaning. + * Range varies with pattern. Can only compare scores with same search pattern. */ + + /* bonus for adjacent matches */ + #define NK_ADJACENCY_BONUS 5 + /* bonus if match occurs after a separator */ + #define NK_SEPARATOR_BONUS 10 + /* bonus if match is uppercase and prev is lower */ + #define NK_CAMEL_BONUS 10 + /* penalty applied for every letter in str before the first match */ + #define NK_LEADING_LETTER_PENALTY (-3) + /* maximum penalty for leading letters */ + #define NK_MAX_LEADING_LETTER_PENALTY (-9) + /* penalty for every letter that doesn't matter */ + #define NK_UNMATCHED_LETTER_PENALTY (-1) + + /* loop variables */ + int score = 0; + char const * pattern_iter = pattern; + int str_iter = 0; + int prev_matched = nk_false; + int prev_lower = nk_false; + /* true so if first letter match gets separator bonus*/ + int prev_separator = nk_true; + + /* use "best" matched letter if multiple string letters match the pattern */ + char const * best_letter = 0; + int best_letter_score = 0; + + /* loop over strings */ + NK_ASSERT(str); + NK_ASSERT(pattern); + if (!str || !str_len || !pattern) return 0; + while (str_iter < str_len) + { + const char pattern_letter = *pattern_iter; + const char str_letter = str[str_iter]; + + int next_match = *pattern_iter != '\0' && + nk_to_lower(pattern_letter) == nk_to_lower(str_letter); + int rematch = best_letter && nk_to_upper(*best_letter) == nk_to_upper(str_letter); + + int advanced = next_match && best_letter; + int pattern_repeat = best_letter && *pattern_iter != '\0'; + pattern_repeat = pattern_repeat && + nk_to_lower(*best_letter) == nk_to_lower(pattern_letter); + + if (advanced || pattern_repeat) { + score += best_letter_score; + best_letter = 0; + best_letter_score = 0; + } + + if (next_match || rematch) + { + int new_score = 0; + /* Apply penalty for each letter before the first pattern match */ + if (pattern_iter == pattern) { + int count = (int)(&str[str_iter] - str); + int penalty = NK_LEADING_LETTER_PENALTY * count; + if (penalty < NK_MAX_LEADING_LETTER_PENALTY) + penalty = NK_MAX_LEADING_LETTER_PENALTY; + + score += penalty; + } + + /* apply bonus for consecutive bonuses */ + if (prev_matched) + new_score += NK_ADJACENCY_BONUS; + + /* apply bonus for matches after a separator */ + if (prev_separator) + new_score += NK_SEPARATOR_BONUS; + + /* apply bonus across camel case boundaries */ + if (prev_lower && nk_is_upper(str_letter)) + new_score += NK_CAMEL_BONUS; + + /* update pattern iter IFF the next pattern letter was matched */ + if (next_match) + ++pattern_iter; + + /* update best letter in str which may be for a "next" letter or a rematch */ + if (new_score >= best_letter_score) { + /* apply penalty for now skipped letter */ + if (best_letter != 0) + score += NK_UNMATCHED_LETTER_PENALTY; + + best_letter = &str[str_iter]; + best_letter_score = new_score; + } + prev_matched = nk_true; + } else { + score += NK_UNMATCHED_LETTER_PENALTY; + prev_matched = nk_false; + } + + /* separators should be more easily defined */ + prev_lower = nk_is_lower(str_letter) != 0; + prev_separator = str_letter == '_' || str_letter == ' '; + + ++str_iter; + } + + /* apply score for last match */ + if (best_letter) + score += best_letter_score; + + /* did not match full pattern */ + if (*pattern_iter != '\0') + return nk_false; + + if (out_score) + *out_score = score; + return nk_true; +} +NK_API int +nk_strmatch_fuzzy_string(char const *str, char const *pattern, int *out_score) +{ + return nk_strmatch_fuzzy_text(str, nk_strlen(str), pattern, out_score); +} +NK_LIB int +nk_string_float_limit(char *string, int prec) +{ + int dot = 0; + char *c = string; + while (*c) { + if (*c == '.') { + dot = 1; + c++; + continue; + } + if (dot == (prec+1)) { + *c = 0; + break; + } + if (dot > 0) dot++; + c++; + } + return (int)(c - string); +} +NK_INTERN void +nk_strrev_ascii(char *s) +{ + int len = nk_strlen(s); + int end = len / 2; + int i = 0; + char t; + for (; i < end; ++i) { + t = s[i]; + s[i] = s[len - 1 - i]; + s[len -1 - i] = t; + } +} +NK_LIB char* +nk_itoa(char *s, long n) +{ + long i = 0; + if (n == 0) { + s[i++] = '0'; + s[i] = 0; + return s; + } + if (n < 0) { + s[i++] = '-'; + n = -n; + } + while (n > 0) { + s[i++] = (char)('0' + (n % 10)); + n /= 10; + } + s[i] = 0; + if (s[0] == '-') + ++s; + + nk_strrev_ascii(s); + return s; +} +#ifndef NK_DTOA +#define NK_DTOA nk_dtoa +NK_LIB char* +nk_dtoa(char *s, double n) +{ + int useExp = 0; + int digit = 0, m = 0, m1 = 0; + char *c = s; + int neg = 0; + + NK_ASSERT(s); + if (!s) return 0; + + if (n == 0.0) { + s[0] = '0'; s[1] = '\0'; + return s; + } + + neg = (n < 0); + if (neg) n = -n; + + /* calculate magnitude */ + m = nk_log10(n); + useExp = (m >= 14 || (neg && m >= 9) || m <= -9); + if (neg) *(c++) = '-'; + + /* set up for scientific notation */ + if (useExp) { + if (m < 0) + m -= 1; + n = n / (double)nk_pow(10.0, m); + m1 = m; + m = 0; + } + if (m < 1.0) { + m = 0; + } + + /* convert the number */ + while (n > NK_FLOAT_PRECISION || m >= 0) { + double weight = nk_pow(10.0, m); + if (weight > 0) { + double t = (double)n / weight; + digit = nk_ifloord(t); + n -= ((double)digit * weight); + *(c++) = (char)('0' + (char)digit); + } + if (m == 0 && n > 0) + *(c++) = '.'; + m--; + } + + if (useExp) { + /* convert the exponent */ + int i, j; + *(c++) = 'e'; + if (m1 > 0) { + *(c++) = '+'; + } else { + *(c++) = '-'; + m1 = -m1; + } + m = 0; + while (m1 > 0) { + *(c++) = (char)('0' + (char)(m1 % 10)); + m1 /= 10; + m++; + } + c -= m; + for (i = 0, j = m-1; i= buf_size) break; + iter++; + + /* flag arguments */ + while (*iter) { + if (*iter == '-') flag |= NK_ARG_FLAG_LEFT; + else if (*iter == '+') flag |= NK_ARG_FLAG_PLUS; + else if (*iter == ' ') flag |= NK_ARG_FLAG_SPACE; + else if (*iter == '#') flag |= NK_ARG_FLAG_NUM; + else if (*iter == '0') flag |= NK_ARG_FLAG_ZERO; + else break; + iter++; + } + + /* width argument */ + width = NK_DEFAULT; + if (*iter >= '1' && *iter <= '9') { + const char *end; + width = nk_strtoi(iter, &end); + if (end == iter) + width = -1; + else iter = end; + } else if (*iter == '*') { + width = va_arg(args, int); + iter++; + } + + /* precision argument */ + precision = NK_DEFAULT; + if (*iter == '.') { + iter++; + if (*iter == '*') { + precision = va_arg(args, int); + iter++; + } else { + const char *end; + precision = nk_strtoi(iter, &end); + if (end == iter) + precision = -1; + else iter = end; + } + } + + /* length modifier */ + if (*iter == 'h') { + if (*(iter+1) == 'h') { + arg_type = NK_ARG_TYPE_CHAR; + iter++; + } else arg_type = NK_ARG_TYPE_SHORT; + iter++; + } else if (*iter == 'l') { + arg_type = NK_ARG_TYPE_LONG; + iter++; + } else arg_type = NK_ARG_TYPE_DEFAULT; + + /* specifier */ + if (*iter == '%') { + NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT); + NK_ASSERT(precision == NK_DEFAULT); + NK_ASSERT(width == NK_DEFAULT); + if (len < buf_size) + buf[len++] = '%'; + } else if (*iter == 's') { + /* string */ + const char *str = va_arg(args, const char*); + NK_ASSERT(str != buf && "buffer and argument are not allowed to overlap!"); + NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT); + NK_ASSERT(precision == NK_DEFAULT); + NK_ASSERT(width == NK_DEFAULT); + if (str == buf) return -1; + while (str && *str && len < buf_size) + buf[len++] = *str++; + } else if (*iter == 'n') { + /* current length callback */ + signed int *n = va_arg(args, int*); + NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT); + NK_ASSERT(precision == NK_DEFAULT); + NK_ASSERT(width == NK_DEFAULT); + if (n) *n = len; + } else if (*iter == 'c' || *iter == 'i' || *iter == 'd') { + /* signed integer */ + long value = 0; + const char *num_iter; + int num_len, num_print, padding; + int cur_precision = NK_MAX(precision, 1); + int cur_width = NK_MAX(width, 0); + + /* retrieve correct value type */ + if (arg_type == NK_ARG_TYPE_CHAR) + value = (signed char)va_arg(args, int); + else if (arg_type == NK_ARG_TYPE_SHORT) + value = (signed short)va_arg(args, int); + else if (arg_type == NK_ARG_TYPE_LONG) + value = va_arg(args, signed long); + else if (*iter == 'c') + value = (unsigned char)va_arg(args, int); + else value = va_arg(args, signed int); + + /* convert number to string */ + nk_itoa(number_buffer, value); + num_len = nk_strlen(number_buffer); + padding = NK_MAX(cur_width - NK_MAX(cur_precision, num_len), 0); + if ((flag & NK_ARG_FLAG_PLUS) || (flag & NK_ARG_FLAG_SPACE)) + padding = NK_MAX(padding-1, 0); + + /* fill left padding up to a total of `width` characters */ + if (!(flag & NK_ARG_FLAG_LEFT)) { + while (padding-- > 0 && (len < buf_size)) { + if ((flag & NK_ARG_FLAG_ZERO) && (precision == NK_DEFAULT)) + buf[len++] = '0'; + else buf[len++] = ' '; + } + } + + /* copy string value representation into buffer */ + if ((flag & NK_ARG_FLAG_PLUS) && value >= 0 && len < buf_size) + buf[len++] = '+'; + else if ((flag & NK_ARG_FLAG_SPACE) && value >= 0 && len < buf_size) + buf[len++] = ' '; + + /* fill up to precision number of digits with '0' */ + num_print = NK_MAX(cur_precision, num_len); + while (precision && (num_print > num_len) && (len < buf_size)) { + buf[len++] = '0'; + num_print--; + } + + /* copy string value representation into buffer */ + num_iter = number_buffer; + while (precision && *num_iter && len < buf_size) + buf[len++] = *num_iter++; + + /* fill right padding up to width characters */ + if (flag & NK_ARG_FLAG_LEFT) { + while ((padding-- > 0) && (len < buf_size)) + buf[len++] = ' '; + } + } else if (*iter == 'o' || *iter == 'x' || *iter == 'X' || *iter == 'u') { + /* unsigned integer */ + unsigned long value = 0; + int num_len = 0, num_print, padding = 0; + int cur_precision = NK_MAX(precision, 1); + int cur_width = NK_MAX(width, 0); + unsigned int base = (*iter == 'o') ? 8: (*iter == 'u')? 10: 16; + + /* print oct/hex/dec value */ + const char *upper_output_format = "0123456789ABCDEF"; + const char *lower_output_format = "0123456789abcdef"; + const char *output_format = (*iter == 'x') ? + lower_output_format: upper_output_format; + + /* retrieve correct value type */ + if (arg_type == NK_ARG_TYPE_CHAR) + value = (unsigned char)va_arg(args, int); + else if (arg_type == NK_ARG_TYPE_SHORT) + value = (unsigned short)va_arg(args, int); + else if (arg_type == NK_ARG_TYPE_LONG) + value = va_arg(args, unsigned long); + else value = va_arg(args, unsigned int); + + do { + /* convert decimal number into hex/oct number */ + int digit = output_format[value % base]; + if (num_len < NK_MAX_NUMBER_BUFFER) + number_buffer[num_len++] = (char)digit; + value /= base; + } while (value > 0); + + num_print = NK_MAX(cur_precision, num_len); + padding = NK_MAX(cur_width - NK_MAX(cur_precision, num_len), 0); + if (flag & NK_ARG_FLAG_NUM) + padding = NK_MAX(padding-1, 0); + + /* fill left padding up to a total of `width` characters */ + if (!(flag & NK_ARG_FLAG_LEFT)) { + while ((padding-- > 0) && (len < buf_size)) { + if ((flag & NK_ARG_FLAG_ZERO) && (precision == NK_DEFAULT)) + buf[len++] = '0'; + else buf[len++] = ' '; + } + } + + /* fill up to precision number of digits */ + if (num_print && (flag & NK_ARG_FLAG_NUM)) { + if ((*iter == 'o') && (len < buf_size)) { + buf[len++] = '0'; + } else if ((*iter == 'x') && ((len+1) < buf_size)) { + buf[len++] = '0'; + buf[len++] = 'x'; + } else if ((*iter == 'X') && ((len+1) < buf_size)) { + buf[len++] = '0'; + buf[len++] = 'X'; + } + } + while (precision && (num_print > num_len) && (len < buf_size)) { + buf[len++] = '0'; + num_print--; + } + + /* reverse number direction */ + while (num_len > 0) { + if (precision && (len < buf_size)) + buf[len++] = number_buffer[num_len-1]; + num_len--; + } + + /* fill right padding up to width characters */ + if (flag & NK_ARG_FLAG_LEFT) { + while ((padding-- > 0) && (len < buf_size)) + buf[len++] = ' '; + } + } else if (*iter == 'f') { + /* floating point */ + const char *num_iter; + int cur_precision = (precision < 0) ? 6: precision; + int prefix, cur_width = NK_MAX(width, 0); + double value = va_arg(args, double); + int num_len = 0, frac_len = 0, dot = 0; + int padding = 0; + + NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT); + NK_DTOA(number_buffer, value); + num_len = nk_strlen(number_buffer); + + /* calculate padding */ + num_iter = number_buffer; + while (*num_iter && *num_iter != '.') + num_iter++; + + prefix = (*num_iter == '.')?(int)(num_iter - number_buffer)+1:0; + padding = NK_MAX(cur_width - (prefix + NK_MIN(cur_precision, num_len - prefix)) , 0); + if ((flag & NK_ARG_FLAG_PLUS) || (flag & NK_ARG_FLAG_SPACE)) + padding = NK_MAX(padding-1, 0); + + /* fill left padding up to a total of `width` characters */ + if (!(flag & NK_ARG_FLAG_LEFT)) { + while (padding-- > 0 && (len < buf_size)) { + if (flag & NK_ARG_FLAG_ZERO) + buf[len++] = '0'; + else buf[len++] = ' '; + } + } + + /* copy string value representation into buffer */ + num_iter = number_buffer; + if ((flag & NK_ARG_FLAG_PLUS) && (value >= 0) && (len < buf_size)) + buf[len++] = '+'; + else if ((flag & NK_ARG_FLAG_SPACE) && (value >= 0) && (len < buf_size)) + buf[len++] = ' '; + while (*num_iter) { + if (dot) frac_len++; + if (len < buf_size) + buf[len++] = *num_iter; + if (*num_iter == '.') dot = 1; + if (frac_len >= cur_precision) break; + num_iter++; + } + + /* fill number up to precision */ + while (frac_len < cur_precision) { + if (!dot && len < buf_size) { + buf[len++] = '.'; + dot = 1; + } + if (len < buf_size) + buf[len++] = '0'; + frac_len++; + } + + /* fill right padding up to width characters */ + if (flag & NK_ARG_FLAG_LEFT) { + while ((padding-- > 0) && (len < buf_size)) + buf[len++] = ' '; + } + } else { + /* Specifier not supported: g,G,e,E,p,z */ + NK_ASSERT(0 && "specifier is not supported!"); + return result; + } + } + buf[(len >= buf_size)?(buf_size-1):len] = 0; + result = (len >= buf_size)?-1:len; + return result; +} +#endif +NK_LIB int +nk_strfmt(char *buf, int buf_size, const char *fmt, va_list args) +{ + int result = -1; + NK_ASSERT(buf); + NK_ASSERT(buf_size); + if (!buf || !buf_size || !fmt) return 0; +#ifdef NK_INCLUDE_STANDARD_IO + result = NK_VSNPRINTF(buf, (nk_size)buf_size, fmt, args); + result = (result >= buf_size) ? -1: result; + buf[buf_size-1] = 0; +#else + result = nk_vsnprintf(buf, buf_size, fmt, args); +#endif + return result; +} +#endif +NK_API nk_hash +nk_murmur_hash(const void * key, int len, nk_hash seed) +{ + /* 32-Bit MurmurHash3: https://code.google.com/p/smhasher/wiki/MurmurHash3*/ + #define NK_ROTL(x,r) ((x) << (r) | ((x) >> (32 - r))) + + nk_uint h1 = seed; + nk_uint k1; + const nk_byte *data = (const nk_byte*)key; + const nk_byte *keyptr = data; + nk_byte *k1ptr; + const int bsize = sizeof(k1); + const int nblocks = len/4; + + const nk_uint c1 = 0xcc9e2d51; + const nk_uint c2 = 0x1b873593; + const nk_byte *tail; + int i; + + /* body */ + if (!key) return 0; + for (i = 0; i < nblocks; ++i, keyptr += bsize) { + k1ptr = (nk_byte*)&k1; + k1ptr[0] = keyptr[0]; + k1ptr[1] = keyptr[1]; + k1ptr[2] = keyptr[2]; + k1ptr[3] = keyptr[3]; + + k1 *= c1; + k1 = NK_ROTL(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = NK_ROTL(h1,13); + h1 = h1*5+0xe6546b64; + } + + /* tail */ + tail = (const nk_byte*)(data + nblocks*4); + k1 = 0; + switch (len & 3) { + case 3: k1 ^= (nk_uint)(tail[2] << 16); /* fallthrough */ + case 2: k1 ^= (nk_uint)(tail[1] << 8u); /* fallthrough */ + case 1: k1 ^= tail[0]; + k1 *= c1; + k1 = NK_ROTL(k1,15); + k1 *= c2; + h1 ^= k1; + break; + default: break; + } + + /* finalization */ + h1 ^= (nk_uint)len; + /* fmix32 */ + h1 ^= h1 >> 16; + h1 *= 0x85ebca6b; + h1 ^= h1 >> 13; + h1 *= 0xc2b2ae35; + h1 ^= h1 >> 16; + + #undef NK_ROTL + return h1; +} +#ifdef NK_INCLUDE_STANDARD_IO +NK_LIB char* +nk_file_load(const char* path, nk_size* siz, struct nk_allocator *alloc) +{ + char *buf; + FILE *fd; + long ret; + + NK_ASSERT(path); + NK_ASSERT(siz); + NK_ASSERT(alloc); + if (!path || !siz || !alloc) + return 0; + + fd = fopen(path, "rb"); + if (!fd) return 0; + fseek(fd, 0, SEEK_END); + ret = ftell(fd); + if (ret < 0) { + fclose(fd); + return 0; + } + *siz = (nk_size)ret; + fseek(fd, 0, SEEK_SET); + buf = (char*)alloc->alloc(alloc->userdata,0, *siz); + NK_ASSERT(buf); + if (!buf) { + fclose(fd); + return 0; + } + *siz = (nk_size)fread(buf, 1,*siz, fd); + fclose(fd); + return buf; +} +#endif +NK_LIB int +nk_text_clamp(const struct nk_user_font *font, const char *text, + int text_len, float space, int *glyphs, float *text_width, + nk_rune *sep_list, int sep_count) +{ + int i = 0; + int glyph_len = 0; + float last_width = 0; + nk_rune unicode = 0; + float width = 0; + int len = 0; + int g = 0; + float s; + + int sep_len = 0; + int sep_g = 0; + float sep_width = 0; + sep_count = NK_MAX(sep_count,0); + + glyph_len = nk_utf_decode(text, &unicode, text_len); + while (glyph_len && (width < space) && (len < text_len)) { + len += glyph_len; + s = font->width(font->userdata, font->height, text, len); + for (i = 0; i < sep_count; ++i) { + if (unicode != sep_list[i]) continue; + sep_width = last_width = width; + sep_g = g+1; + sep_len = len; + break; + } + if (i == sep_count){ + last_width = sep_width = width; + sep_g = g+1; + } + width = s; + glyph_len = nk_utf_decode(&text[len], &unicode, text_len - len); + g++; + } + if (len >= text_len) { + *glyphs = g; + *text_width = last_width; + return len; + } else { + *glyphs = sep_g; + *text_width = sep_width; + return (!sep_len) ? len: sep_len; + } +} +NK_LIB struct nk_vec2 +nk_text_calculate_text_bounds(const struct nk_user_font *font, + const char *begin, int byte_len, float row_height, const char **remaining, + struct nk_vec2 *out_offset, int *glyphs, int op) +{ + float line_height = row_height; + struct nk_vec2 text_size = nk_vec2(0,0); + float line_width = 0.0f; + + float glyph_width; + int glyph_len = 0; + nk_rune unicode = 0; + int text_len = 0; + if (!begin || byte_len <= 0 || !font) + return nk_vec2(0,row_height); + + glyph_len = nk_utf_decode(begin, &unicode, byte_len); + if (!glyph_len) return text_size; + glyph_width = font->width(font->userdata, font->height, begin, glyph_len); + + *glyphs = 0; + while ((text_len < byte_len) && glyph_len) { + if (unicode == '\n') { + text_size.x = NK_MAX(text_size.x, line_width); + text_size.y += line_height; + line_width = 0; + *glyphs+=1; + if (op == NK_STOP_ON_NEW_LINE) + break; + + text_len++; + glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len-text_len); + continue; + } + + if (unicode == '\r') { + text_len++; + *glyphs+=1; + glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len-text_len); + continue; + } + + *glyphs = *glyphs + 1; + text_len += glyph_len; + line_width += (float)glyph_width; + glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len-text_len); + glyph_width = font->width(font->userdata, font->height, begin+text_len, glyph_len); + continue; + } + + if (text_size.x < line_width) + text_size.x = line_width; + if (out_offset) + *out_offset = nk_vec2(line_width, text_size.y + line_height); + if (line_width > 0 || text_size.y == 0.0f) + text_size.y += line_height; + if (remaining) + *remaining = begin+text_len; + return text_size; +} + + + + + +/* ============================================================== + * + * COLOR + * + * ===============================================================*/ +NK_INTERN int +nk_parse_hex(const char *p, int length) +{ + int i = 0; + int len = 0; + while (len < length) { + i <<= 4; + if (p[len] >= 'a' && p[len] <= 'f') + i += ((p[len] - 'a') + 10); + else if (p[len] >= 'A' && p[len] <= 'F') + i += ((p[len] - 'A') + 10); + else i += (p[len] - '0'); + len++; + } + return i; +} +NK_API struct nk_color +nk_rgba(int r, int g, int b, int a) +{ + struct nk_color ret; + ret.r = (nk_byte)NK_CLAMP(0, r, 255); + ret.g = (nk_byte)NK_CLAMP(0, g, 255); + ret.b = (nk_byte)NK_CLAMP(0, b, 255); + ret.a = (nk_byte)NK_CLAMP(0, a, 255); + return ret; +} +NK_API struct nk_color +nk_rgb_hex(const char *rgb) +{ + struct nk_color col; + const char *c = rgb; + if (*c == '#') c++; + col.r = (nk_byte)nk_parse_hex(c, 2); + col.g = (nk_byte)nk_parse_hex(c+2, 2); + col.b = (nk_byte)nk_parse_hex(c+4, 2); + col.a = 255; + return col; +} +NK_API struct nk_color +nk_rgba_hex(const char *rgb) +{ + struct nk_color col; + const char *c = rgb; + if (*c == '#') c++; + col.r = (nk_byte)nk_parse_hex(c, 2); + col.g = (nk_byte)nk_parse_hex(c+2, 2); + col.b = (nk_byte)nk_parse_hex(c+4, 2); + col.a = (nk_byte)nk_parse_hex(c+6, 2); + return col; +} +NK_API void +nk_color_hex_rgba(char *output, struct nk_color col) +{ + #define NK_TO_HEX(i) ((i) <= 9 ? '0' + (i): 'A' - 10 + (i)) + output[0] = (char)NK_TO_HEX((col.r & 0xF0) >> 4); + output[1] = (char)NK_TO_HEX((col.r & 0x0F)); + output[2] = (char)NK_TO_HEX((col.g & 0xF0) >> 4); + output[3] = (char)NK_TO_HEX((col.g & 0x0F)); + output[4] = (char)NK_TO_HEX((col.b & 0xF0) >> 4); + output[5] = (char)NK_TO_HEX((col.b & 0x0F)); + output[6] = (char)NK_TO_HEX((col.a & 0xF0) >> 4); + output[7] = (char)NK_TO_HEX((col.a & 0x0F)); + output[8] = '\0'; + #undef NK_TO_HEX +} +NK_API void +nk_color_hex_rgb(char *output, struct nk_color col) +{ + #define NK_TO_HEX(i) ((i) <= 9 ? '0' + (i): 'A' - 10 + (i)) + output[0] = (char)NK_TO_HEX((col.r & 0xF0) >> 4); + output[1] = (char)NK_TO_HEX((col.r & 0x0F)); + output[2] = (char)NK_TO_HEX((col.g & 0xF0) >> 4); + output[3] = (char)NK_TO_HEX((col.g & 0x0F)); + output[4] = (char)NK_TO_HEX((col.b & 0xF0) >> 4); + output[5] = (char)NK_TO_HEX((col.b & 0x0F)); + output[6] = '\0'; + #undef NK_TO_HEX +} +NK_API struct nk_color +nk_rgba_iv(const int *c) +{ + return nk_rgba(c[0], c[1], c[2], c[3]); +} +NK_API struct nk_color +nk_rgba_bv(const nk_byte *c) +{ + return nk_rgba(c[0], c[1], c[2], c[3]); +} +NK_API struct nk_color +nk_rgb(int r, int g, int b) +{ + struct nk_color ret; + ret.r = (nk_byte)NK_CLAMP(0, r, 255); + ret.g = (nk_byte)NK_CLAMP(0, g, 255); + ret.b = (nk_byte)NK_CLAMP(0, b, 255); + ret.a = (nk_byte)255; + return ret; +} +NK_API struct nk_color +nk_rgb_iv(const int *c) +{ + return nk_rgb(c[0], c[1], c[2]); +} +NK_API struct nk_color +nk_rgb_bv(const nk_byte* c) +{ + return nk_rgb(c[0], c[1], c[2]); +} +NK_API struct nk_color +nk_rgba_u32(nk_uint in) +{ + struct nk_color ret; + ret.r = (in & 0xFF); + ret.g = ((in >> 8) & 0xFF); + ret.b = ((in >> 16) & 0xFF); + ret.a = (nk_byte)((in >> 24) & 0xFF); + return ret; +} +NK_API struct nk_color +nk_rgba_f(float r, float g, float b, float a) +{ + struct nk_color ret; + ret.r = (nk_byte)(NK_SATURATE(r) * 255.0f); + ret.g = (nk_byte)(NK_SATURATE(g) * 255.0f); + ret.b = (nk_byte)(NK_SATURATE(b) * 255.0f); + ret.a = (nk_byte)(NK_SATURATE(a) * 255.0f); + return ret; +} +NK_API struct nk_color +nk_rgba_fv(const float *c) +{ + return nk_rgba_f(c[0], c[1], c[2], c[3]); +} +NK_API struct nk_color +nk_rgba_cf(struct nk_colorf c) +{ + return nk_rgba_f(c.r, c.g, c.b, c.a); +} +NK_API struct nk_color +nk_rgb_f(float r, float g, float b) +{ + struct nk_color ret; + ret.r = (nk_byte)(NK_SATURATE(r) * 255.0f); + ret.g = (nk_byte)(NK_SATURATE(g) * 255.0f); + ret.b = (nk_byte)(NK_SATURATE(b) * 255.0f); + ret.a = 255; + return ret; +} +NK_API struct nk_color +nk_rgb_fv(const float *c) +{ + return nk_rgb_f(c[0], c[1], c[2]); +} +NK_API struct nk_color +nk_rgb_cf(struct nk_colorf c) +{ + return nk_rgb_f(c.r, c.g, c.b); +} +NK_API struct nk_color +nk_hsv(int h, int s, int v) +{ + return nk_hsva(h, s, v, 255); +} +NK_API struct nk_color +nk_hsv_iv(const int *c) +{ + return nk_hsv(c[0], c[1], c[2]); +} +NK_API struct nk_color +nk_hsv_bv(const nk_byte *c) +{ + return nk_hsv(c[0], c[1], c[2]); +} +NK_API struct nk_color +nk_hsv_f(float h, float s, float v) +{ + return nk_hsva_f(h, s, v, 1.0f); +} +NK_API struct nk_color +nk_hsv_fv(const float *c) +{ + return nk_hsv_f(c[0], c[1], c[2]); +} +NK_API struct nk_color +nk_hsva(int h, int s, int v, int a) +{ + float hf = ((float)NK_CLAMP(0, h, 255)) / 255.0f; + float sf = ((float)NK_CLAMP(0, s, 255)) / 255.0f; + float vf = ((float)NK_CLAMP(0, v, 255)) / 255.0f; + float af = ((float)NK_CLAMP(0, a, 255)) / 255.0f; + return nk_hsva_f(hf, sf, vf, af); +} +NK_API struct nk_color +nk_hsva_iv(const int *c) +{ + return nk_hsva(c[0], c[1], c[2], c[3]); +} +NK_API struct nk_color +nk_hsva_bv(const nk_byte *c) +{ + return nk_hsva(c[0], c[1], c[2], c[3]); +} +NK_API struct nk_colorf +nk_hsva_colorf(float h, float s, float v, float a) +{ + int i; + float p, q, t, f; + struct nk_colorf out = {0,0,0,0}; + if (s <= 0.0f) { + out.r = v; out.g = v; out.b = v; out.a = a; + return out; + } + h = h / (60.0f/360.0f); + i = (int)h; + f = h - (float)i; + p = v * (1.0f - s); + q = v * (1.0f - (s * f)); + t = v * (1.0f - s * (1.0f - f)); + + switch (i) { + case 0: default: out.r = v; out.g = t; out.b = p; break; + case 1: out.r = q; out.g = v; out.b = p; break; + case 2: out.r = p; out.g = v; out.b = t; break; + case 3: out.r = p; out.g = q; out.b = v; break; + case 4: out.r = t; out.g = p; out.b = v; break; + case 5: out.r = v; out.g = p; out.b = q; break;} + out.a = a; + return out; +} +NK_API struct nk_colorf +nk_hsva_colorfv(float *c) +{ + return nk_hsva_colorf(c[0], c[1], c[2], c[3]); +} +NK_API struct nk_color +nk_hsva_f(float h, float s, float v, float a) +{ + struct nk_colorf c = nk_hsva_colorf(h, s, v, a); + return nk_rgba_f(c.r, c.g, c.b, c.a); +} +NK_API struct nk_color +nk_hsva_fv(const float *c) +{ + return nk_hsva_f(c[0], c[1], c[2], c[3]); +} +NK_API nk_uint +nk_color_u32(struct nk_color in) +{ + nk_uint out = (nk_uint)in.r; + out |= ((nk_uint)in.g << 8); + out |= ((nk_uint)in.b << 16); + out |= ((nk_uint)in.a << 24); + return out; +} +NK_API void +nk_color_f(float *r, float *g, float *b, float *a, struct nk_color in) +{ + NK_STORAGE const float s = 1.0f/255.0f; + *r = (float)in.r * s; + *g = (float)in.g * s; + *b = (float)in.b * s; + *a = (float)in.a * s; +} +NK_API void +nk_color_fv(float *c, struct nk_color in) +{ + nk_color_f(&c[0], &c[1], &c[2], &c[3], in); +} +NK_API struct nk_colorf +nk_color_cf(struct nk_color in) +{ + struct nk_colorf o; + nk_color_f(&o.r, &o.g, &o.b, &o.a, in); + return o; +} +NK_API void +nk_color_d(double *r, double *g, double *b, double *a, struct nk_color in) +{ + NK_STORAGE const double s = 1.0/255.0; + *r = (double)in.r * s; + *g = (double)in.g * s; + *b = (double)in.b * s; + *a = (double)in.a * s; +} +NK_API void +nk_color_dv(double *c, struct nk_color in) +{ + nk_color_d(&c[0], &c[1], &c[2], &c[3], in); +} +NK_API void +nk_color_hsv_f(float *out_h, float *out_s, float *out_v, struct nk_color in) +{ + float a; + nk_color_hsva_f(out_h, out_s, out_v, &a, in); +} +NK_API void +nk_color_hsv_fv(float *out, struct nk_color in) +{ + float a; + nk_color_hsva_f(&out[0], &out[1], &out[2], &a, in); +} +NK_API void +nk_colorf_hsva_f(float *out_h, float *out_s, + float *out_v, float *out_a, struct nk_colorf in) +{ + float chroma; + float K = 0.0f; + if (in.g < in.b) { + const float t = in.g; in.g = in.b; in.b = t; + K = -1.f; + } + if (in.r < in.g) { + const float t = in.r; in.r = in.g; in.g = t; + K = -2.f/6.0f - K; + } + chroma = in.r - ((in.g < in.b) ? in.g: in.b); + *out_h = NK_ABS(K + (in.g - in.b)/(6.0f * chroma + 1e-20f)); + *out_s = chroma / (in.r + 1e-20f); + *out_v = in.r; + *out_a = in.a; + +} +NK_API void +nk_colorf_hsva_fv(float *hsva, struct nk_colorf in) +{ + nk_colorf_hsva_f(&hsva[0], &hsva[1], &hsva[2], &hsva[3], in); +} +NK_API void +nk_color_hsva_f(float *out_h, float *out_s, + float *out_v, float *out_a, struct nk_color in) +{ + struct nk_colorf col; + nk_color_f(&col.r,&col.g,&col.b,&col.a, in); + nk_colorf_hsva_f(out_h, out_s, out_v, out_a, col); +} +NK_API void +nk_color_hsva_fv(float *out, struct nk_color in) +{ + nk_color_hsva_f(&out[0], &out[1], &out[2], &out[3], in); +} +NK_API void +nk_color_hsva_i(int *out_h, int *out_s, int *out_v, + int *out_a, struct nk_color in) +{ + float h,s,v,a; + nk_color_hsva_f(&h, &s, &v, &a, in); + *out_h = (nk_byte)(h * 255.0f); + *out_s = (nk_byte)(s * 255.0f); + *out_v = (nk_byte)(v * 255.0f); + *out_a = (nk_byte)(a * 255.0f); +} +NK_API void +nk_color_hsva_iv(int *out, struct nk_color in) +{ + nk_color_hsva_i(&out[0], &out[1], &out[2], &out[3], in); +} +NK_API void +nk_color_hsva_bv(nk_byte *out, struct nk_color in) +{ + int tmp[4]; + nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); + out[0] = (nk_byte)tmp[0]; + out[1] = (nk_byte)tmp[1]; + out[2] = (nk_byte)tmp[2]; + out[3] = (nk_byte)tmp[3]; +} +NK_API void +nk_color_hsva_b(nk_byte *h, nk_byte *s, nk_byte *v, nk_byte *a, struct nk_color in) +{ + int tmp[4]; + nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); + *h = (nk_byte)tmp[0]; + *s = (nk_byte)tmp[1]; + *v = (nk_byte)tmp[2]; + *a = (nk_byte)tmp[3]; +} +NK_API void +nk_color_hsv_i(int *out_h, int *out_s, int *out_v, struct nk_color in) +{ + int a; + nk_color_hsva_i(out_h, out_s, out_v, &a, in); +} +NK_API void +nk_color_hsv_b(nk_byte *out_h, nk_byte *out_s, nk_byte *out_v, struct nk_color in) +{ + int tmp[4]; + nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); + *out_h = (nk_byte)tmp[0]; + *out_s = (nk_byte)tmp[1]; + *out_v = (nk_byte)tmp[2]; +} +NK_API void +nk_color_hsv_iv(int *out, struct nk_color in) +{ + nk_color_hsv_i(&out[0], &out[1], &out[2], in); +} +NK_API void +nk_color_hsv_bv(nk_byte *out, struct nk_color in) +{ + int tmp[4]; + nk_color_hsv_i(&tmp[0], &tmp[1], &tmp[2], in); + out[0] = (nk_byte)tmp[0]; + out[1] = (nk_byte)tmp[1]; + out[2] = (nk_byte)tmp[2]; +} + + + + + +/* =============================================================== + * + * UTF-8 + * + * ===============================================================*/ +NK_GLOBAL const nk_byte nk_utfbyte[NK_UTF_SIZE+1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +NK_GLOBAL const nk_byte nk_utfmask[NK_UTF_SIZE+1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +NK_GLOBAL const nk_uint nk_utfmin[NK_UTF_SIZE+1] = {0, 0, 0x80, 0x800, 0x10000}; +NK_GLOBAL const nk_uint nk_utfmax[NK_UTF_SIZE+1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +NK_INTERN int +nk_utf_validate(nk_rune *u, int i) +{ + NK_ASSERT(u); + if (!u) return 0; + if (!NK_BETWEEN(*u, nk_utfmin[i], nk_utfmax[i]) || + NK_BETWEEN(*u, 0xD800, 0xDFFF)) + *u = NK_UTF_INVALID; + for (i = 1; *u > nk_utfmax[i]; ++i); + return i; +} +NK_INTERN nk_rune +nk_utf_decode_byte(char c, int *i) +{ + NK_ASSERT(i); + if (!i) return 0; + for(*i = 0; *i < (int)NK_LEN(nk_utfmask); ++(*i)) { + if (((nk_byte)c & nk_utfmask[*i]) == nk_utfbyte[*i]) + return (nk_byte)(c & ~nk_utfmask[*i]); + } + return 0; +} +NK_API int +nk_utf_decode(const char *c, nk_rune *u, int clen) +{ + int i, j, len, type=0; + nk_rune udecoded; + + NK_ASSERT(c); + NK_ASSERT(u); + + if (!c || !u) return 0; + if (!clen) return 0; + *u = NK_UTF_INVALID; + + udecoded = nk_utf_decode_byte(c[0], &len); + if (!NK_BETWEEN(len, 1, NK_UTF_SIZE)) + return 1; + + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | nk_utf_decode_byte(c[i], &type); + if (type != 0) + return j; + } + if (j < len) + return 0; + *u = udecoded; + nk_utf_validate(u, len); + return len; +} +NK_INTERN char +nk_utf_encode_byte(nk_rune u, int i) +{ + return (char)((nk_utfbyte[i]) | ((nk_byte)u & ~nk_utfmask[i])); +} +NK_API int +nk_utf_encode(nk_rune u, char *c, int clen) +{ + int len, i; + len = nk_utf_validate(&u, 0); + if (clen < len || !len || len > NK_UTF_SIZE) + return 0; + + for (i = len - 1; i != 0; --i) { + c[i] = nk_utf_encode_byte(u, 0); + u >>= 6; + } + c[0] = nk_utf_encode_byte(u, len); + return len; +} +NK_API int +nk_utf_len(const char *str, int len) +{ + const char *text; + int glyphs = 0; + int text_len; + int glyph_len; + int src_len = 0; + nk_rune unicode; + + NK_ASSERT(str); + if (!str || !len) return 0; + + text = str; + text_len = len; + glyph_len = nk_utf_decode(text, &unicode, text_len); + while (glyph_len && src_len < len) { + glyphs++; + src_len = src_len + glyph_len; + glyph_len = nk_utf_decode(text + src_len, &unicode, text_len - src_len); + } + return glyphs; +} +NK_API const char* +nk_utf_at(const char *buffer, int length, int index, + nk_rune *unicode, int *len) +{ + int i = 0; + int src_len = 0; + int glyph_len = 0; + const char *text; + int text_len; + + NK_ASSERT(buffer); + NK_ASSERT(unicode); + NK_ASSERT(len); + + if (!buffer || !unicode || !len) return 0; + if (index < 0) { + *unicode = NK_UTF_INVALID; + *len = 0; + return 0; + } + + text = buffer; + text_len = length; + glyph_len = nk_utf_decode(text, unicode, text_len); + while (glyph_len) { + if (i == index) { + *len = glyph_len; + break; + } + + i++; + src_len = src_len + glyph_len; + glyph_len = nk_utf_decode(text + src_len, unicode, text_len - src_len); + } + if (i != index) return 0; + return buffer + src_len; +} + + + + + +/* ============================================================== + * + * BUFFER + * + * ===============================================================*/ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_LIB void* +nk_malloc(nk_handle unused, void *old,nk_size size) +{ + NK_UNUSED(unused); + NK_UNUSED(old); + return malloc(size); +} +NK_LIB void +nk_mfree(nk_handle unused, void *ptr) +{ + NK_UNUSED(unused); + free(ptr); +} +NK_API void +nk_buffer_init_default(struct nk_buffer *buffer) +{ + struct nk_allocator alloc; + alloc.userdata.ptr = 0; + alloc.alloc = nk_malloc; + alloc.free = nk_mfree; + nk_buffer_init(buffer, &alloc, NK_BUFFER_DEFAULT_INITIAL_SIZE); +} +#endif + +NK_API void +nk_buffer_init(struct nk_buffer *b, const struct nk_allocator *a, + nk_size initial_size) +{ + NK_ASSERT(b); + NK_ASSERT(a); + NK_ASSERT(initial_size); + if (!b || !a || !initial_size) return; + + nk_zero(b, sizeof(*b)); + b->type = NK_BUFFER_DYNAMIC; + b->memory.ptr = a->alloc(a->userdata,0, initial_size); + b->memory.size = initial_size; + b->size = initial_size; + b->grow_factor = 2.0f; + b->pool = *a; +} +NK_API void +nk_buffer_init_fixed(struct nk_buffer *b, void *m, nk_size size) +{ + NK_ASSERT(b); + NK_ASSERT(m); + NK_ASSERT(size); + if (!b || !m || !size) return; + + nk_zero(b, sizeof(*b)); + b->type = NK_BUFFER_FIXED; + b->memory.ptr = m; + b->memory.size = size; + b->size = size; +} +NK_LIB void* +nk_buffer_align(void *unaligned, + nk_size align, nk_size *alignment, + enum nk_buffer_allocation_type type) +{ + void *memory = 0; + switch (type) { + default: + case NK_BUFFER_MAX: + case NK_BUFFER_FRONT: + if (align) { + memory = NK_ALIGN_PTR(unaligned, align); + *alignment = (nk_size)((nk_byte*)memory - (nk_byte*)unaligned); + } else { + memory = unaligned; + *alignment = 0; + } + break; + case NK_BUFFER_BACK: + if (align) { + memory = NK_ALIGN_PTR_BACK(unaligned, align); + *alignment = (nk_size)((nk_byte*)unaligned - (nk_byte*)memory); + } else { + memory = unaligned; + *alignment = 0; + } + break; + } + return memory; +} +NK_LIB void* +nk_buffer_realloc(struct nk_buffer *b, nk_size capacity, nk_size *size) +{ + void *temp; + nk_size buffer_size; + + NK_ASSERT(b); + NK_ASSERT(size); + if (!b || !size || !b->pool.alloc || !b->pool.free) + return 0; + + buffer_size = b->memory.size; + temp = b->pool.alloc(b->pool.userdata, b->memory.ptr, capacity); + NK_ASSERT(temp); + if (!temp) return 0; + + *size = capacity; + if (temp != b->memory.ptr) { + NK_MEMCPY(temp, b->memory.ptr, buffer_size); + b->pool.free(b->pool.userdata, b->memory.ptr); + } + + if (b->size == buffer_size) { + /* no back buffer so just set correct size */ + b->size = capacity; + return temp; + } else { + /* copy back buffer to the end of the new buffer */ + void *dst, *src; + nk_size back_size; + back_size = buffer_size - b->size; + dst = nk_ptr_add(void, temp, capacity - back_size); + src = nk_ptr_add(void, temp, b->size); + NK_MEMCPY(dst, src, back_size); + b->size = capacity - back_size; + } + return temp; +} +NK_LIB void* +nk_buffer_alloc(struct nk_buffer *b, enum nk_buffer_allocation_type type, + nk_size size, nk_size align) +{ + int full; + nk_size alignment; + void *unaligned; + void *memory; + + NK_ASSERT(b); + NK_ASSERT(size); + if (!b || !size) return 0; + b->needed += size; + + /* calculate total size with needed alignment + size */ + if (type == NK_BUFFER_FRONT) + unaligned = nk_ptr_add(void, b->memory.ptr, b->allocated); + else unaligned = nk_ptr_add(void, b->memory.ptr, b->size - size); + memory = nk_buffer_align(unaligned, align, &alignment, type); + + /* check if buffer has enough memory*/ + if (type == NK_BUFFER_FRONT) + full = ((b->allocated + size + alignment) > b->size); + else full = ((b->size - NK_MIN(b->size,(size + alignment))) <= b->allocated); + + if (full) { + nk_size capacity; + if (b->type != NK_BUFFER_DYNAMIC) + return 0; + NK_ASSERT(b->pool.alloc && b->pool.free); + if (b->type != NK_BUFFER_DYNAMIC || !b->pool.alloc || !b->pool.free) + return 0; + + /* buffer is full so allocate bigger buffer if dynamic */ + capacity = (nk_size)((float)b->memory.size * b->grow_factor); + capacity = NK_MAX(capacity, nk_round_up_pow2((nk_uint)(b->allocated + size))); + b->memory.ptr = nk_buffer_realloc(b, capacity, &b->memory.size); + if (!b->memory.ptr) return 0; + + /* align newly allocated pointer */ + if (type == NK_BUFFER_FRONT) + unaligned = nk_ptr_add(void, b->memory.ptr, b->allocated); + else unaligned = nk_ptr_add(void, b->memory.ptr, b->size - size); + memory = nk_buffer_align(unaligned, align, &alignment, type); + } + if (type == NK_BUFFER_FRONT) + b->allocated += size + alignment; + else b->size -= (size + alignment); + b->needed += alignment; + b->calls++; + return memory; +} +NK_API void +nk_buffer_push(struct nk_buffer *b, enum nk_buffer_allocation_type type, + const void *memory, nk_size size, nk_size align) +{ + void *mem = nk_buffer_alloc(b, type, size, align); + if (!mem) return; + NK_MEMCPY(mem, memory, size); +} +NK_API void +nk_buffer_mark(struct nk_buffer *buffer, enum nk_buffer_allocation_type type) +{ + NK_ASSERT(buffer); + if (!buffer) return; + buffer->marker[type].active = nk_true; + if (type == NK_BUFFER_BACK) + buffer->marker[type].offset = buffer->size; + else buffer->marker[type].offset = buffer->allocated; +} +NK_API void +nk_buffer_reset(struct nk_buffer *buffer, enum nk_buffer_allocation_type type) +{ + NK_ASSERT(buffer); + if (!buffer) return; + if (type == NK_BUFFER_BACK) { + /* reset back buffer either back to marker or empty */ + buffer->needed -= (buffer->memory.size - buffer->marker[type].offset); + if (buffer->marker[type].active) + buffer->size = buffer->marker[type].offset; + else buffer->size = buffer->memory.size; + buffer->marker[type].active = nk_false; + } else { + /* reset front buffer either back to back marker or empty */ + buffer->needed -= (buffer->allocated - buffer->marker[type].offset); + if (buffer->marker[type].active) + buffer->allocated = buffer->marker[type].offset; + else buffer->allocated = 0; + buffer->marker[type].active = nk_false; + } +} +NK_API void +nk_buffer_clear(struct nk_buffer *b) +{ + NK_ASSERT(b); + if (!b) return; + b->allocated = 0; + b->size = b->memory.size; + b->calls = 0; + b->needed = 0; +} +NK_API void +nk_buffer_free(struct nk_buffer *b) +{ + NK_ASSERT(b); + if (!b || !b->memory.ptr) return; + if (b->type == NK_BUFFER_FIXED) return; + if (!b->pool.free) return; + NK_ASSERT(b->pool.free); + b->pool.free(b->pool.userdata, b->memory.ptr); +} +NK_API void +nk_buffer_info(struct nk_memory_status *s, struct nk_buffer *b) +{ + NK_ASSERT(b); + NK_ASSERT(s); + if (!s || !b) return; + s->allocated = b->allocated; + s->size = b->memory.size; + s->needed = b->needed; + s->memory = b->memory.ptr; + s->calls = b->calls; +} +NK_API void* +nk_buffer_memory(struct nk_buffer *buffer) +{ + NK_ASSERT(buffer); + if (!buffer) return 0; + return buffer->memory.ptr; +} +NK_API const void* +nk_buffer_memory_const(const struct nk_buffer *buffer) +{ + NK_ASSERT(buffer); + if (!buffer) return 0; + return buffer->memory.ptr; +} +NK_API nk_size +nk_buffer_total(struct nk_buffer *buffer) +{ + NK_ASSERT(buffer); + if (!buffer) return 0; + return buffer->memory.size; +} + + + + + +/* =============================================================== + * + * STRING + * + * ===============================================================*/ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void +nk_str_init_default(struct nk_str *str) +{ + struct nk_allocator alloc; + alloc.userdata.ptr = 0; + alloc.alloc = nk_malloc; + alloc.free = nk_mfree; + nk_buffer_init(&str->buffer, &alloc, 32); + str->len = 0; +} +#endif + +NK_API void +nk_str_init(struct nk_str *str, const struct nk_allocator *alloc, nk_size size) +{ + nk_buffer_init(&str->buffer, alloc, size); + str->len = 0; +} +NK_API void +nk_str_init_fixed(struct nk_str *str, void *memory, nk_size size) +{ + nk_buffer_init_fixed(&str->buffer, memory, size); + str->len = 0; +} +NK_API int +nk_str_append_text_char(struct nk_str *s, const char *str, int len) +{ + char *mem; + NK_ASSERT(s); + NK_ASSERT(str); + if (!s || !str || !len) return 0; + mem = (char*)nk_buffer_alloc(&s->buffer, NK_BUFFER_FRONT, (nk_size)len * sizeof(char), 0); + if (!mem) return 0; + NK_MEMCPY(mem, str, (nk_size)len * sizeof(char)); + s->len += nk_utf_len(str, len); + return len; +} +NK_API int +nk_str_append_str_char(struct nk_str *s, const char *str) +{ + return nk_str_append_text_char(s, str, nk_strlen(str)); +} +NK_API int +nk_str_append_text_utf8(struct nk_str *str, const char *text, int len) +{ + int i = 0; + int byte_len = 0; + nk_rune unicode; + if (!str || !text || !len) return 0; + for (i = 0; i < len; ++i) + byte_len += nk_utf_decode(text+byte_len, &unicode, 4); + nk_str_append_text_char(str, text, byte_len); + return len; +} +NK_API int +nk_str_append_str_utf8(struct nk_str *str, const char *text) +{ + int runes = 0; + int byte_len = 0; + int num_runes = 0; + int glyph_len = 0; + nk_rune unicode; + if (!str || !text) return 0; + + glyph_len = byte_len = nk_utf_decode(text+byte_len, &unicode, 4); + while (unicode != '\0' && glyph_len) { + glyph_len = nk_utf_decode(text+byte_len, &unicode, 4); + byte_len += glyph_len; + num_runes++; + } + nk_str_append_text_char(str, text, byte_len); + return runes; +} +NK_API int +nk_str_append_text_runes(struct nk_str *str, const nk_rune *text, int len) +{ + int i = 0; + int byte_len = 0; + nk_glyph glyph; + + NK_ASSERT(str); + if (!str || !text || !len) return 0; + for (i = 0; i < len; ++i) { + byte_len = nk_utf_encode(text[i], glyph, NK_UTF_SIZE); + if (!byte_len) break; + nk_str_append_text_char(str, glyph, byte_len); + } + return len; +} +NK_API int +nk_str_append_str_runes(struct nk_str *str, const nk_rune *runes) +{ + int i = 0; + nk_glyph glyph; + int byte_len; + NK_ASSERT(str); + if (!str || !runes) return 0; + while (runes[i] != '\0') { + byte_len = nk_utf_encode(runes[i], glyph, NK_UTF_SIZE); + nk_str_append_text_char(str, glyph, byte_len); + i++; + } + return i; +} +NK_API int +nk_str_insert_at_char(struct nk_str *s, int pos, const char *str, int len) +{ + int i; + void *mem; + char *src; + char *dst; + + int copylen; + NK_ASSERT(s); + NK_ASSERT(str); + NK_ASSERT(len >= 0); + if (!s || !str || !len || (nk_size)pos > s->buffer.allocated) return 0; + if ((s->buffer.allocated + (nk_size)len >= s->buffer.memory.size) && + (s->buffer.type == NK_BUFFER_FIXED)) return 0; + + copylen = (int)s->buffer.allocated - pos; + if (!copylen) { + nk_str_append_text_char(s, str, len); + return 1; + } + mem = nk_buffer_alloc(&s->buffer, NK_BUFFER_FRONT, (nk_size)len * sizeof(char), 0); + if (!mem) return 0; + + /* memmove */ + NK_ASSERT(((int)pos + (int)len + ((int)copylen - 1)) >= 0); + NK_ASSERT(((int)pos + ((int)copylen - 1)) >= 0); + dst = nk_ptr_add(char, s->buffer.memory.ptr, pos + len + (copylen - 1)); + src = nk_ptr_add(char, s->buffer.memory.ptr, pos + (copylen-1)); + for (i = 0; i < copylen; ++i) *dst-- = *src--; + mem = nk_ptr_add(void, s->buffer.memory.ptr, pos); + NK_MEMCPY(mem, str, (nk_size)len * sizeof(char)); + s->len = nk_utf_len((char *)s->buffer.memory.ptr, (int)s->buffer.allocated); + return 1; +} +NK_API int +nk_str_insert_at_rune(struct nk_str *str, int pos, const char *cstr, int len) +{ + int glyph_len; + nk_rune unicode; + const char *begin; + const char *buffer; + + NK_ASSERT(str); + NK_ASSERT(cstr); + NK_ASSERT(len); + if (!str || !cstr || !len) return 0; + begin = nk_str_at_rune(str, pos, &unicode, &glyph_len); + if (!str->len) + return nk_str_append_text_char(str, cstr, len); + buffer = nk_str_get_const(str); + if (!begin) return 0; + return nk_str_insert_at_char(str, (int)(begin - buffer), cstr, len); +} +NK_API int +nk_str_insert_text_char(struct nk_str *str, int pos, const char *text, int len) +{ + return nk_str_insert_text_utf8(str, pos, text, len); +} +NK_API int +nk_str_insert_str_char(struct nk_str *str, int pos, const char *text) +{ + return nk_str_insert_text_utf8(str, pos, text, nk_strlen(text)); +} +NK_API int +nk_str_insert_text_utf8(struct nk_str *str, int pos, const char *text, int len) +{ + int i = 0; + int byte_len = 0; + nk_rune unicode; + + NK_ASSERT(str); + NK_ASSERT(text); + if (!str || !text || !len) return 0; + for (i = 0; i < len; ++i) + byte_len += nk_utf_decode(text+byte_len, &unicode, 4); + nk_str_insert_at_rune(str, pos, text, byte_len); + return len; +} +NK_API int +nk_str_insert_str_utf8(struct nk_str *str, int pos, const char *text) +{ + int runes = 0; + int byte_len = 0; + int num_runes = 0; + int glyph_len = 0; + nk_rune unicode; + if (!str || !text) return 0; + + glyph_len = byte_len = nk_utf_decode(text+byte_len, &unicode, 4); + while (unicode != '\0' && glyph_len) { + glyph_len = nk_utf_decode(text+byte_len, &unicode, 4); + byte_len += glyph_len; + num_runes++; + } + nk_str_insert_at_rune(str, pos, text, byte_len); + return runes; +} +NK_API int +nk_str_insert_text_runes(struct nk_str *str, int pos, const nk_rune *runes, int len) +{ + int i = 0; + int byte_len = 0; + nk_glyph glyph; + + NK_ASSERT(str); + if (!str || !runes || !len) return 0; + for (i = 0; i < len; ++i) { + byte_len = nk_utf_encode(runes[i], glyph, NK_UTF_SIZE); + if (!byte_len) break; + nk_str_insert_at_rune(str, pos+i, glyph, byte_len); + } + return len; +} +NK_API int +nk_str_insert_str_runes(struct nk_str *str, int pos, const nk_rune *runes) +{ + int i = 0; + nk_glyph glyph; + int byte_len; + NK_ASSERT(str); + if (!str || !runes) return 0; + while (runes[i] != '\0') { + byte_len = nk_utf_encode(runes[i], glyph, NK_UTF_SIZE); + nk_str_insert_at_rune(str, pos+i, glyph, byte_len); + i++; + } + return i; +} +NK_API void +nk_str_remove_chars(struct nk_str *s, int len) +{ + NK_ASSERT(s); + NK_ASSERT(len >= 0); + if (!s || len < 0 || (nk_size)len > s->buffer.allocated) return; + NK_ASSERT(((int)s->buffer.allocated - (int)len) >= 0); + s->buffer.allocated -= (nk_size)len; + s->len = nk_utf_len((char *)s->buffer.memory.ptr, (int)s->buffer.allocated); +} +NK_API void +nk_str_remove_runes(struct nk_str *str, int len) +{ + int index; + const char *begin; + const char *end; + nk_rune unicode; + + NK_ASSERT(str); + NK_ASSERT(len >= 0); + if (!str || len < 0) return; + if (len >= str->len) { + str->len = 0; + return; + } + + index = str->len - len; + begin = nk_str_at_rune(str, index, &unicode, &len); + end = (const char*)str->buffer.memory.ptr + str->buffer.allocated; + nk_str_remove_chars(str, (int)(end-begin)+1); +} +NK_API void +nk_str_delete_chars(struct nk_str *s, int pos, int len) +{ + NK_ASSERT(s); + if (!s || !len || (nk_size)pos > s->buffer.allocated || + (nk_size)(pos + len) > s->buffer.allocated) return; + + if ((nk_size)(pos + len) < s->buffer.allocated) { + /* memmove */ + char *dst = nk_ptr_add(char, s->buffer.memory.ptr, pos); + char *src = nk_ptr_add(char, s->buffer.memory.ptr, pos + len); + NK_MEMCPY(dst, src, s->buffer.allocated - (nk_size)(pos + len)); + NK_ASSERT(((int)s->buffer.allocated - (int)len) >= 0); + s->buffer.allocated -= (nk_size)len; + } else nk_str_remove_chars(s, len); + s->len = nk_utf_len((char *)s->buffer.memory.ptr, (int)s->buffer.allocated); +} +NK_API void +nk_str_delete_runes(struct nk_str *s, int pos, int len) +{ + char *temp; + nk_rune unicode; + char *begin; + char *end; + int unused; + + NK_ASSERT(s); + NK_ASSERT(s->len >= pos + len); + if (s->len < pos + len) + len = NK_CLAMP(0, (s->len - pos), s->len); + if (!len) return; + + temp = (char *)s->buffer.memory.ptr; + begin = nk_str_at_rune(s, pos, &unicode, &unused); + if (!begin) return; + s->buffer.memory.ptr = begin; + end = nk_str_at_rune(s, len, &unicode, &unused); + s->buffer.memory.ptr = temp; + if (!end) return; + nk_str_delete_chars(s, (int)(begin - temp), (int)(end - begin)); +} +NK_API char* +nk_str_at_char(struct nk_str *s, int pos) +{ + NK_ASSERT(s); + if (!s || pos > (int)s->buffer.allocated) return 0; + return nk_ptr_add(char, s->buffer.memory.ptr, pos); +} +NK_API char* +nk_str_at_rune(struct nk_str *str, int pos, nk_rune *unicode, int *len) +{ + int i = 0; + int src_len = 0; + int glyph_len = 0; + char *text; + int text_len; + + NK_ASSERT(str); + NK_ASSERT(unicode); + NK_ASSERT(len); + + if (!str || !unicode || !len) return 0; + if (pos < 0) { + *unicode = 0; + *len = 0; + return 0; + } + + text = (char*)str->buffer.memory.ptr; + text_len = (int)str->buffer.allocated; + glyph_len = nk_utf_decode(text, unicode, text_len); + while (glyph_len) { + if (i == pos) { + *len = glyph_len; + break; + } + + i++; + src_len = src_len + glyph_len; + glyph_len = nk_utf_decode(text + src_len, unicode, text_len - src_len); + } + if (i != pos) return 0; + return text + src_len; +} +NK_API const char* +nk_str_at_char_const(const struct nk_str *s, int pos) +{ + NK_ASSERT(s); + if (!s || pos > (int)s->buffer.allocated) return 0; + return nk_ptr_add(char, s->buffer.memory.ptr, pos); +} +NK_API const char* +nk_str_at_const(const struct nk_str *str, int pos, nk_rune *unicode, int *len) +{ + int i = 0; + int src_len = 0; + int glyph_len = 0; + char *text; + int text_len; + + NK_ASSERT(str); + NK_ASSERT(unicode); + NK_ASSERT(len); + + if (!str || !unicode || !len) return 0; + if (pos < 0) { + *unicode = 0; + *len = 0; + return 0; + } + + text = (char*)str->buffer.memory.ptr; + text_len = (int)str->buffer.allocated; + glyph_len = nk_utf_decode(text, unicode, text_len); + while (glyph_len) { + if (i == pos) { + *len = glyph_len; + break; + } + + i++; + src_len = src_len + glyph_len; + glyph_len = nk_utf_decode(text + src_len, unicode, text_len - src_len); + } + if (i != pos) return 0; + return text + src_len; +} +NK_API nk_rune +nk_str_rune_at(const struct nk_str *str, int pos) +{ + int len; + nk_rune unicode = 0; + nk_str_at_const(str, pos, &unicode, &len); + return unicode; +} +NK_API char* +nk_str_get(struct nk_str *s) +{ + NK_ASSERT(s); + if (!s || !s->len || !s->buffer.allocated) return 0; + return (char*)s->buffer.memory.ptr; +} +NK_API const char* +nk_str_get_const(const struct nk_str *s) +{ + NK_ASSERT(s); + if (!s || !s->len || !s->buffer.allocated) return 0; + return (const char*)s->buffer.memory.ptr; +} +NK_API int +nk_str_len(struct nk_str *s) +{ + NK_ASSERT(s); + if (!s || !s->len || !s->buffer.allocated) return 0; + return s->len; +} +NK_API int +nk_str_len_char(struct nk_str *s) +{ + NK_ASSERT(s); + if (!s || !s->len || !s->buffer.allocated) return 0; + return (int)s->buffer.allocated; +} +NK_API void +nk_str_clear(struct nk_str *str) +{ + NK_ASSERT(str); + nk_buffer_clear(&str->buffer); + str->len = 0; +} +NK_API void +nk_str_free(struct nk_str *str) +{ + NK_ASSERT(str); + nk_buffer_free(&str->buffer); + str->len = 0; +} + + + + + +/* ============================================================== + * + * DRAW + * + * ===============================================================*/ +NK_LIB void +nk_command_buffer_init(struct nk_command_buffer *cb, + struct nk_buffer *b, enum nk_command_clipping clip) +{ + NK_ASSERT(cb); + NK_ASSERT(b); + if (!cb || !b) return; + cb->base = b; + cb->use_clipping = (int)clip; + cb->begin = b->allocated; + cb->end = b->allocated; + cb->last = b->allocated; +} +NK_LIB void +nk_command_buffer_reset(struct nk_command_buffer *b) +{ + NK_ASSERT(b); + if (!b) return; + b->begin = 0; + b->end = 0; + b->last = 0; + b->clip = nk_null_rect; +#ifdef NK_INCLUDE_COMMAND_USERDATA + b->userdata.ptr = 0; +#endif +} +NK_LIB void* +nk_command_buffer_push(struct nk_command_buffer* b, + enum nk_command_type t, nk_size size) +{ + NK_STORAGE const nk_size align = NK_ALIGNOF(struct nk_command); + struct nk_command *cmd; + nk_size alignment; + void *unaligned; + void *memory; + + NK_ASSERT(b); + NK_ASSERT(b->base); + if (!b) return 0; + cmd = (struct nk_command*)nk_buffer_alloc(b->base,NK_BUFFER_FRONT,size,align); + if (!cmd) return 0; + + /* make sure the offset to the next command is aligned */ + b->last = (nk_size)((nk_byte*)cmd - (nk_byte*)b->base->memory.ptr); + unaligned = (nk_byte*)cmd + size; + memory = NK_ALIGN_PTR(unaligned, align); + alignment = (nk_size)((nk_byte*)memory - (nk_byte*)unaligned); +#ifdef NK_ZERO_COMMAND_MEMORY + NK_MEMSET(cmd, 0, size + alignment); +#endif + + cmd->type = t; + cmd->next = b->base->allocated + alignment; +#ifdef NK_INCLUDE_COMMAND_USERDATA + cmd->userdata = b->userdata; +#endif + b->end = cmd->next; + return cmd; +} +NK_API void +nk_push_scissor(struct nk_command_buffer *b, struct nk_rect r) +{ + struct nk_command_scissor *cmd; + NK_ASSERT(b); + if (!b) return; + + b->clip.x = r.x; + b->clip.y = r.y; + b->clip.w = r.w; + b->clip.h = r.h; + cmd = (struct nk_command_scissor*) + nk_command_buffer_push(b, NK_COMMAND_SCISSOR, sizeof(*cmd)); + + if (!cmd) return; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)NK_MAX(0, r.w); + cmd->h = (unsigned short)NK_MAX(0, r.h); +} +NK_API void +nk_stroke_line(struct nk_command_buffer *b, float x0, float y0, + float x1, float y1, float line_thickness, struct nk_color c) +{ + struct nk_command_line *cmd; + NK_ASSERT(b); + if (!b || line_thickness <= 0) return; + cmd = (struct nk_command_line*) + nk_command_buffer_push(b, NK_COMMAND_LINE, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->begin.x = (short)x0; + cmd->begin.y = (short)y0; + cmd->end.x = (short)x1; + cmd->end.y = (short)y1; + cmd->color = c; +} +NK_API void +nk_stroke_curve(struct nk_command_buffer *b, float ax, float ay, + float ctrl0x, float ctrl0y, float ctrl1x, float ctrl1y, + float bx, float by, float line_thickness, struct nk_color col) +{ + struct nk_command_curve *cmd; + NK_ASSERT(b); + if (!b || col.a == 0 || line_thickness <= 0) return; + + cmd = (struct nk_command_curve*) + nk_command_buffer_push(b, NK_COMMAND_CURVE, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->begin.x = (short)ax; + cmd->begin.y = (short)ay; + cmd->ctrl[0].x = (short)ctrl0x; + cmd->ctrl[0].y = (short)ctrl0y; + cmd->ctrl[1].x = (short)ctrl1x; + cmd->ctrl[1].y = (short)ctrl1y; + cmd->end.x = (short)bx; + cmd->end.y = (short)by; + cmd->color = col; +} +NK_API void +nk_stroke_rect(struct nk_command_buffer *b, struct nk_rect rect, + float rounding, float line_thickness, struct nk_color c) +{ + struct nk_command_rect *cmd; + NK_ASSERT(b); + if (!b || c.a == 0 || rect.w == 0 || rect.h == 0 || line_thickness <= 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, + clip->x, clip->y, clip->w, clip->h)) return; + } + cmd = (struct nk_command_rect*) + nk_command_buffer_push(b, NK_COMMAND_RECT, sizeof(*cmd)); + if (!cmd) return; + cmd->rounding = (unsigned short)rounding; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->x = (short)rect.x; + cmd->y = (short)rect.y; + cmd->w = (unsigned short)NK_MAX(0, rect.w); + cmd->h = (unsigned short)NK_MAX(0, rect.h); + cmd->color = c; +} +NK_API void +nk_fill_rect(struct nk_command_buffer *b, struct nk_rect rect, + float rounding, struct nk_color c) +{ + struct nk_command_rect_filled *cmd; + NK_ASSERT(b); + if (!b || c.a == 0 || rect.w == 0 || rect.h == 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, + clip->x, clip->y, clip->w, clip->h)) return; + } + + cmd = (struct nk_command_rect_filled*) + nk_command_buffer_push(b, NK_COMMAND_RECT_FILLED, sizeof(*cmd)); + if (!cmd) return; + cmd->rounding = (unsigned short)rounding; + cmd->x = (short)rect.x; + cmd->y = (short)rect.y; + cmd->w = (unsigned short)NK_MAX(0, rect.w); + cmd->h = (unsigned short)NK_MAX(0, rect.h); + cmd->color = c; +} +NK_API void +nk_fill_rect_multi_color(struct nk_command_buffer *b, struct nk_rect rect, + struct nk_color left, struct nk_color top, struct nk_color right, + struct nk_color bottom) +{ + struct nk_command_rect_multi_color *cmd; + NK_ASSERT(b); + if (!b || rect.w == 0 || rect.h == 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, + clip->x, clip->y, clip->w, clip->h)) return; + } + + cmd = (struct nk_command_rect_multi_color*) + nk_command_buffer_push(b, NK_COMMAND_RECT_MULTI_COLOR, sizeof(*cmd)); + if (!cmd) return; + cmd->x = (short)rect.x; + cmd->y = (short)rect.y; + cmd->w = (unsigned short)NK_MAX(0, rect.w); + cmd->h = (unsigned short)NK_MAX(0, rect.h); + cmd->left = left; + cmd->top = top; + cmd->right = right; + cmd->bottom = bottom; +} +NK_API void +nk_stroke_circle(struct nk_command_buffer *b, struct nk_rect r, + float line_thickness, struct nk_color c) +{ + struct nk_command_circle *cmd; + if (!b || r.w == 0 || r.h == 0 || line_thickness <= 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(r.x, r.y, r.w, r.h, clip->x, clip->y, clip->w, clip->h)) + return; + } + + cmd = (struct nk_command_circle*) + nk_command_buffer_push(b, NK_COMMAND_CIRCLE, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)NK_MAX(r.w, 0); + cmd->h = (unsigned short)NK_MAX(r.h, 0); + cmd->color = c; +} +NK_API void +nk_fill_circle(struct nk_command_buffer *b, struct nk_rect r, struct nk_color c) +{ + struct nk_command_circle_filled *cmd; + NK_ASSERT(b); + if (!b || c.a == 0 || r.w == 0 || r.h == 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(r.x, r.y, r.w, r.h, clip->x, clip->y, clip->w, clip->h)) + return; + } + + cmd = (struct nk_command_circle_filled*) + nk_command_buffer_push(b, NK_COMMAND_CIRCLE_FILLED, sizeof(*cmd)); + if (!cmd) return; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)NK_MAX(r.w, 0); + cmd->h = (unsigned short)NK_MAX(r.h, 0); + cmd->color = c; +} +NK_API void +nk_stroke_arc(struct nk_command_buffer *b, float cx, float cy, float radius, + float a_min, float a_max, float line_thickness, struct nk_color c) +{ + struct nk_command_arc *cmd; + if (!b || c.a == 0 || line_thickness <= 0) return; + cmd = (struct nk_command_arc*) + nk_command_buffer_push(b, NK_COMMAND_ARC, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->cx = (short)cx; + cmd->cy = (short)cy; + cmd->r = (unsigned short)radius; + cmd->a[0] = a_min; + cmd->a[1] = a_max; + cmd->color = c; +} +NK_API void +nk_fill_arc(struct nk_command_buffer *b, float cx, float cy, float radius, + float a_min, float a_max, struct nk_color c) +{ + struct nk_command_arc_filled *cmd; + NK_ASSERT(b); + if (!b || c.a == 0) return; + cmd = (struct nk_command_arc_filled*) + nk_command_buffer_push(b, NK_COMMAND_ARC_FILLED, sizeof(*cmd)); + if (!cmd) return; + cmd->cx = (short)cx; + cmd->cy = (short)cy; + cmd->r = (unsigned short)radius; + cmd->a[0] = a_min; + cmd->a[1] = a_max; + cmd->color = c; +} +NK_API void +nk_stroke_triangle(struct nk_command_buffer *b, float x0, float y0, float x1, + float y1, float x2, float y2, float line_thickness, struct nk_color c) +{ + struct nk_command_triangle *cmd; + NK_ASSERT(b); + if (!b || c.a == 0 || line_thickness <= 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INBOX(x0, y0, clip->x, clip->y, clip->w, clip->h) && + !NK_INBOX(x1, y1, clip->x, clip->y, clip->w, clip->h) && + !NK_INBOX(x2, y2, clip->x, clip->y, clip->w, clip->h)) + return; + } + + cmd = (struct nk_command_triangle*) + nk_command_buffer_push(b, NK_COMMAND_TRIANGLE, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->a.x = (short)x0; + cmd->a.y = (short)y0; + cmd->b.x = (short)x1; + cmd->b.y = (short)y1; + cmd->c.x = (short)x2; + cmd->c.y = (short)y2; + cmd->color = c; +} +NK_API void +nk_fill_triangle(struct nk_command_buffer *b, float x0, float y0, float x1, + float y1, float x2, float y2, struct nk_color c) +{ + struct nk_command_triangle_filled *cmd; + NK_ASSERT(b); + if (!b || c.a == 0) return; + if (!b) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INBOX(x0, y0, clip->x, clip->y, clip->w, clip->h) && + !NK_INBOX(x1, y1, clip->x, clip->y, clip->w, clip->h) && + !NK_INBOX(x2, y2, clip->x, clip->y, clip->w, clip->h)) + return; + } + + cmd = (struct nk_command_triangle_filled*) + nk_command_buffer_push(b, NK_COMMAND_TRIANGLE_FILLED, sizeof(*cmd)); + if (!cmd) return; + cmd->a.x = (short)x0; + cmd->a.y = (short)y0; + cmd->b.x = (short)x1; + cmd->b.y = (short)y1; + cmd->c.x = (short)x2; + cmd->c.y = (short)y2; + cmd->color = c; +} +NK_API void +nk_stroke_polygon(struct nk_command_buffer *b, float *points, int point_count, + float line_thickness, struct nk_color col) +{ + int i; + nk_size size = 0; + struct nk_command_polygon *cmd; + + NK_ASSERT(b); + if (!b || col.a == 0 || line_thickness <= 0) return; + size = sizeof(*cmd) + sizeof(short) * 2 * (nk_size)point_count; + cmd = (struct nk_command_polygon*) nk_command_buffer_push(b, NK_COMMAND_POLYGON, size); + if (!cmd) return; + cmd->color = col; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->point_count = (unsigned short)point_count; + for (i = 0; i < point_count; ++i) { + cmd->points[i].x = (short)points[i*2]; + cmd->points[i].y = (short)points[i*2+1]; + } +} +NK_API void +nk_fill_polygon(struct nk_command_buffer *b, float *points, int point_count, + struct nk_color col) +{ + int i; + nk_size size = 0; + struct nk_command_polygon_filled *cmd; + + NK_ASSERT(b); + if (!b || col.a == 0) return; + size = sizeof(*cmd) + sizeof(short) * 2 * (nk_size)point_count; + cmd = (struct nk_command_polygon_filled*) + nk_command_buffer_push(b, NK_COMMAND_POLYGON_FILLED, size); + if (!cmd) return; + cmd->color = col; + cmd->point_count = (unsigned short)point_count; + for (i = 0; i < point_count; ++i) { + cmd->points[i].x = (short)points[i*2+0]; + cmd->points[i].y = (short)points[i*2+1]; + } +} +NK_API void +nk_stroke_polyline(struct nk_command_buffer *b, float *points, int point_count, + float line_thickness, struct nk_color col) +{ + int i; + nk_size size = 0; + struct nk_command_polyline *cmd; + + NK_ASSERT(b); + if (!b || col.a == 0 || line_thickness <= 0) return; + size = sizeof(*cmd) + sizeof(short) * 2 * (nk_size)point_count; + cmd = (struct nk_command_polyline*) nk_command_buffer_push(b, NK_COMMAND_POLYLINE, size); + if (!cmd) return; + cmd->color = col; + cmd->point_count = (unsigned short)point_count; + cmd->line_thickness = (unsigned short)line_thickness; + for (i = 0; i < point_count; ++i) { + cmd->points[i].x = (short)points[i*2]; + cmd->points[i].y = (short)points[i*2+1]; + } +} +NK_API void +nk_draw_image(struct nk_command_buffer *b, struct nk_rect r, + const struct nk_image *img, struct nk_color col) +{ + struct nk_command_image *cmd; + NK_ASSERT(b); + if (!b) return; + if (b->use_clipping) { + const struct nk_rect *c = &b->clip; + if (c->w == 0 || c->h == 0 || !NK_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h)) + return; + } + + cmd = (struct nk_command_image*) + nk_command_buffer_push(b, NK_COMMAND_IMAGE, sizeof(*cmd)); + if (!cmd) return; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)NK_MAX(0, r.w); + cmd->h = (unsigned short)NK_MAX(0, r.h); + cmd->img = *img; + cmd->col = col; +} +NK_API void +nk_push_custom(struct nk_command_buffer *b, struct nk_rect r, + nk_command_custom_callback cb, nk_handle usr) +{ + struct nk_command_custom *cmd; + NK_ASSERT(b); + if (!b) return; + if (b->use_clipping) { + const struct nk_rect *c = &b->clip; + if (c->w == 0 || c->h == 0 || !NK_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h)) + return; + } + + cmd = (struct nk_command_custom*) + nk_command_buffer_push(b, NK_COMMAND_CUSTOM, sizeof(*cmd)); + if (!cmd) return; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)NK_MAX(0, r.w); + cmd->h = (unsigned short)NK_MAX(0, r.h); + cmd->callback_data = usr; + cmd->callback = cb; +} +NK_API void +nk_draw_text(struct nk_command_buffer *b, struct nk_rect r, + const char *string, int length, const struct nk_user_font *font, + struct nk_color bg, struct nk_color fg) +{ + float text_width = 0; + struct nk_command_text *cmd; + + NK_ASSERT(b); + NK_ASSERT(font); + if (!b || !string || !length || (bg.a == 0 && fg.a == 0)) return; + if (b->use_clipping) { + const struct nk_rect *c = &b->clip; + if (c->w == 0 || c->h == 0 || !NK_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h)) + return; + } + + /* make sure text fits inside bounds */ + text_width = font->width(font->userdata, font->height, string, length); + if (text_width > r.w){ + int glyphs = 0; + float txt_width = (float)text_width; + length = nk_text_clamp(font, string, length, r.w, &glyphs, &txt_width, 0,0); + } + + if (!length) return; + cmd = (struct nk_command_text*) + nk_command_buffer_push(b, NK_COMMAND_TEXT, sizeof(*cmd) + (nk_size)(length + 1)); + if (!cmd) return; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)r.w; + cmd->h = (unsigned short)r.h; + cmd->background = bg; + cmd->foreground = fg; + cmd->font = font; + cmd->length = length; + cmd->height = font->height; + NK_MEMCPY(cmd->string, string, (nk_size)length); + cmd->string[length] = '\0'; +} + + + + + +/* =============================================================== + * + * VERTEX + * + * ===============================================================*/ +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +NK_API void +nk_draw_list_init(struct nk_draw_list *list) +{ + nk_size i = 0; + NK_ASSERT(list); + if (!list) return; + nk_zero(list, sizeof(*list)); + for (i = 0; i < NK_LEN(list->circle_vtx); ++i) { + const float a = ((float)i / (float)NK_LEN(list->circle_vtx)) * 2 * NK_PI; + list->circle_vtx[i].x = (float)NK_COS(a); + list->circle_vtx[i].y = (float)NK_SIN(a); + } +} +NK_API void +nk_draw_list_setup(struct nk_draw_list *canvas, const struct nk_convert_config *config, + struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements, + enum nk_anti_aliasing line_aa, enum nk_anti_aliasing shape_aa) +{ + NK_ASSERT(canvas); + NK_ASSERT(config); + NK_ASSERT(cmds); + NK_ASSERT(vertices); + NK_ASSERT(elements); + if (!canvas || !config || !cmds || !vertices || !elements) + return; + + canvas->buffer = cmds; + canvas->config = *config; + canvas->elements = elements; + canvas->vertices = vertices; + canvas->line_AA = line_aa; + canvas->shape_AA = shape_aa; + canvas->clip_rect = nk_null_rect; + + canvas->cmd_offset = 0; + canvas->element_count = 0; + canvas->vertex_count = 0; + canvas->cmd_offset = 0; + canvas->cmd_count = 0; + canvas->path_count = 0; +} +NK_API const struct nk_draw_command* +nk__draw_list_begin(const struct nk_draw_list *canvas, const struct nk_buffer *buffer) +{ + nk_byte *memory; + nk_size offset; + const struct nk_draw_command *cmd; + + NK_ASSERT(buffer); + if (!buffer || !buffer->size || !canvas->cmd_count) + return 0; + + memory = (nk_byte*)buffer->memory.ptr; + offset = buffer->memory.size - canvas->cmd_offset; + cmd = nk_ptr_add(const struct nk_draw_command, memory, offset); + return cmd; +} +NK_API const struct nk_draw_command* +nk__draw_list_end(const struct nk_draw_list *canvas, const struct nk_buffer *buffer) +{ + nk_size size; + nk_size offset; + nk_byte *memory; + const struct nk_draw_command *end; + + NK_ASSERT(buffer); + NK_ASSERT(canvas); + if (!buffer || !canvas) + return 0; + + memory = (nk_byte*)buffer->memory.ptr; + size = buffer->memory.size; + offset = size - canvas->cmd_offset; + end = nk_ptr_add(const struct nk_draw_command, memory, offset); + end -= (canvas->cmd_count-1); + return end; +} +NK_API const struct nk_draw_command* +nk__draw_list_next(const struct nk_draw_command *cmd, + const struct nk_buffer *buffer, const struct nk_draw_list *canvas) +{ + const struct nk_draw_command *end; + NK_ASSERT(buffer); + NK_ASSERT(canvas); + if (!cmd || !buffer || !canvas) + return 0; + + end = nk__draw_list_end(canvas, buffer); + if (cmd <= end) return 0; + return (cmd-1); +} +NK_INTERN struct nk_vec2* +nk_draw_list_alloc_path(struct nk_draw_list *list, int count) +{ + struct nk_vec2 *points; + NK_STORAGE const nk_size point_align = NK_ALIGNOF(struct nk_vec2); + NK_STORAGE const nk_size point_size = sizeof(struct nk_vec2); + points = (struct nk_vec2*) + nk_buffer_alloc(list->buffer, NK_BUFFER_FRONT, + point_size * (nk_size)count, point_align); + + if (!points) return 0; + if (!list->path_offset) { + void *memory = nk_buffer_memory(list->buffer); + list->path_offset = (unsigned int)((nk_byte*)points - (nk_byte*)memory); + } + list->path_count += (unsigned int)count; + return points; +} +NK_INTERN struct nk_vec2 +nk_draw_list_path_last(struct nk_draw_list *list) +{ + void *memory; + struct nk_vec2 *point; + NK_ASSERT(list->path_count); + memory = nk_buffer_memory(list->buffer); + point = nk_ptr_add(struct nk_vec2, memory, list->path_offset); + point += (list->path_count-1); + return *point; +} +NK_INTERN struct nk_draw_command* +nk_draw_list_push_command(struct nk_draw_list *list, struct nk_rect clip, + nk_handle texture) +{ + NK_STORAGE const nk_size cmd_align = NK_ALIGNOF(struct nk_draw_command); + NK_STORAGE const nk_size cmd_size = sizeof(struct nk_draw_command); + struct nk_draw_command *cmd; + + NK_ASSERT(list); + cmd = (struct nk_draw_command*) + nk_buffer_alloc(list->buffer, NK_BUFFER_BACK, cmd_size, cmd_align); + + if (!cmd) return 0; + if (!list->cmd_count) { + nk_byte *memory = (nk_byte*)nk_buffer_memory(list->buffer); + nk_size total = nk_buffer_total(list->buffer); + memory = nk_ptr_add(nk_byte, memory, total); + list->cmd_offset = (nk_size)(memory - (nk_byte*)cmd); + } + + cmd->elem_count = 0; + cmd->clip_rect = clip; + cmd->texture = texture; +#ifdef NK_INCLUDE_COMMAND_USERDATA + cmd->userdata = list->userdata; +#endif + + list->cmd_count++; + list->clip_rect = clip; + return cmd; +} +NK_INTERN struct nk_draw_command* +nk_draw_list_command_last(struct nk_draw_list *list) +{ + void *memory; + nk_size size; + struct nk_draw_command *cmd; + NK_ASSERT(list->cmd_count); + + memory = nk_buffer_memory(list->buffer); + size = nk_buffer_total(list->buffer); + cmd = nk_ptr_add(struct nk_draw_command, memory, size - list->cmd_offset); + return (cmd - (list->cmd_count-1)); +} +NK_INTERN void +nk_draw_list_add_clip(struct nk_draw_list *list, struct nk_rect rect) +{ + NK_ASSERT(list); + if (!list) return; + if (!list->cmd_count) { + nk_draw_list_push_command(list, rect, list->config.null.texture); + } else { + struct nk_draw_command *prev = nk_draw_list_command_last(list); + if (prev->elem_count == 0) + prev->clip_rect = rect; + nk_draw_list_push_command(list, rect, prev->texture); + } +} +NK_INTERN void +nk_draw_list_push_image(struct nk_draw_list *list, nk_handle texture) +{ + NK_ASSERT(list); + if (!list) return; + if (!list->cmd_count) { + nk_draw_list_push_command(list, nk_null_rect, texture); + } else { + struct nk_draw_command *prev = nk_draw_list_command_last(list); + if (prev->elem_count == 0) { + prev->texture = texture; + #ifdef NK_INCLUDE_COMMAND_USERDATA + prev->userdata = list->userdata; + #endif + } else if (prev->texture.id != texture.id + #ifdef NK_INCLUDE_COMMAND_USERDATA + || prev->userdata.id != list->userdata.id + #endif + ) nk_draw_list_push_command(list, prev->clip_rect, texture); + } +} +#ifdef NK_INCLUDE_COMMAND_USERDATA +NK_API void +nk_draw_list_push_userdata(struct nk_draw_list *list, nk_handle userdata) +{ + list->userdata = userdata; +} +#endif +NK_INTERN void* +nk_draw_list_alloc_vertices(struct nk_draw_list *list, nk_size count) +{ + void *vtx; + NK_ASSERT(list); + if (!list) return 0; + vtx = nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, + list->config.vertex_size*count, list->config.vertex_alignment); + if (!vtx) return 0; + list->vertex_count += (unsigned int)count; + + /* This assert triggers because your are drawing a lot of stuff and nuklear + * defined `nk_draw_index` as `nk_ushort` to safe space be default. + * + * So you reached the maximum number of indicies or rather vertexes. + * To solve this issue please change typdef `nk_draw_index` to `nk_uint` + * and don't forget to specify the new element size in your drawing + * backend (OpenGL, DirectX, ...). For example in OpenGL for `glDrawElements` + * instead of specifing `GL_UNSIGNED_SHORT` you have to define `GL_UNSIGNED_INT`. + * Sorry for the inconvenience. */ + if(sizeof(nk_draw_index)==2) NK_ASSERT((list->vertex_count < NK_USHORT_MAX && + "To many verticies for 16-bit vertex indicies. Please read comment above on how to solve this problem")); + return vtx; +} +NK_INTERN nk_draw_index* +nk_draw_list_alloc_elements(struct nk_draw_list *list, nk_size count) +{ + nk_draw_index *ids; + struct nk_draw_command *cmd; + NK_STORAGE const nk_size elem_align = NK_ALIGNOF(nk_draw_index); + NK_STORAGE const nk_size elem_size = sizeof(nk_draw_index); + NK_ASSERT(list); + if (!list) return 0; + + ids = (nk_draw_index*) + nk_buffer_alloc(list->elements, NK_BUFFER_FRONT, elem_size*count, elem_align); + if (!ids) return 0; + cmd = nk_draw_list_command_last(list); + list->element_count += (unsigned int)count; + cmd->elem_count += (unsigned int)count; + return ids; +} +NK_INTERN int +nk_draw_vertex_layout_element_is_end_of_layout( + const struct nk_draw_vertex_layout_element *element) +{ + return (element->attribute == NK_VERTEX_ATTRIBUTE_COUNT || + element->format == NK_FORMAT_COUNT); +} +NK_INTERN void +nk_draw_vertex_color(void *attr, const float *vals, + enum nk_draw_vertex_layout_format format) +{ + /* if this triggers you tried to provide a value format for a color */ + float val[4]; + NK_ASSERT(format >= NK_FORMAT_COLOR_BEGIN); + NK_ASSERT(format <= NK_FORMAT_COLOR_END); + if (format < NK_FORMAT_COLOR_BEGIN || format > NK_FORMAT_COLOR_END) return; + + val[0] = NK_SATURATE(vals[0]); + val[1] = NK_SATURATE(vals[1]); + val[2] = NK_SATURATE(vals[2]); + val[3] = NK_SATURATE(vals[3]); + + switch (format) { + default: NK_ASSERT(0 && "Invalid vertex layout color format"); break; + case NK_FORMAT_R8G8B8A8: + case NK_FORMAT_R8G8B8: { + struct nk_color col = nk_rgba_fv(val); + NK_MEMCPY(attr, &col.r, sizeof(col)); + } break; + case NK_FORMAT_B8G8R8A8: { + struct nk_color col = nk_rgba_fv(val); + struct nk_color bgra = nk_rgba(col.b, col.g, col.r, col.a); + NK_MEMCPY(attr, &bgra, sizeof(bgra)); + } break; + case NK_FORMAT_R16G15B16: { + nk_ushort col[3]; + col[0] = (nk_ushort)(val[0]*(float)NK_USHORT_MAX); + col[1] = (nk_ushort)(val[1]*(float)NK_USHORT_MAX); + col[2] = (nk_ushort)(val[2]*(float)NK_USHORT_MAX); + NK_MEMCPY(attr, col, sizeof(col)); + } break; + case NK_FORMAT_R16G15B16A16: { + nk_ushort col[4]; + col[0] = (nk_ushort)(val[0]*(float)NK_USHORT_MAX); + col[1] = (nk_ushort)(val[1]*(float)NK_USHORT_MAX); + col[2] = (nk_ushort)(val[2]*(float)NK_USHORT_MAX); + col[3] = (nk_ushort)(val[3]*(float)NK_USHORT_MAX); + NK_MEMCPY(attr, col, sizeof(col)); + } break; + case NK_FORMAT_R32G32B32: { + nk_uint col[3]; + col[0] = (nk_uint)(val[0]*(float)NK_UINT_MAX); + col[1] = (nk_uint)(val[1]*(float)NK_UINT_MAX); + col[2] = (nk_uint)(val[2]*(float)NK_UINT_MAX); + NK_MEMCPY(attr, col, sizeof(col)); + } break; + case NK_FORMAT_R32G32B32A32: { + nk_uint col[4]; + col[0] = (nk_uint)(val[0]*(float)NK_UINT_MAX); + col[1] = (nk_uint)(val[1]*(float)NK_UINT_MAX); + col[2] = (nk_uint)(val[2]*(float)NK_UINT_MAX); + col[3] = (nk_uint)(val[3]*(float)NK_UINT_MAX); + NK_MEMCPY(attr, col, sizeof(col)); + } break; + case NK_FORMAT_R32G32B32A32_FLOAT: + NK_MEMCPY(attr, val, sizeof(float)*4); + break; + case NK_FORMAT_R32G32B32A32_DOUBLE: { + double col[4]; + col[0] = (double)val[0]; + col[1] = (double)val[1]; + col[2] = (double)val[2]; + col[3] = (double)val[3]; + NK_MEMCPY(attr, col, sizeof(col)); + } break; + case NK_FORMAT_RGB32: + case NK_FORMAT_RGBA32: { + struct nk_color col = nk_rgba_fv(val); + nk_uint color = nk_color_u32(col); + NK_MEMCPY(attr, &color, sizeof(color)); + } break; } +} +NK_INTERN void +nk_draw_vertex_element(void *dst, const float *values, int value_count, + enum nk_draw_vertex_layout_format format) +{ + int value_index; + void *attribute = dst; + /* if this triggers you tried to provide a color format for a value */ + NK_ASSERT(format < NK_FORMAT_COLOR_BEGIN); + if (format >= NK_FORMAT_COLOR_BEGIN && format <= NK_FORMAT_COLOR_END) return; + for (value_index = 0; value_index < value_count; ++value_index) { + switch (format) { + default: NK_ASSERT(0 && "invalid vertex layout format"); break; + case NK_FORMAT_SCHAR: { + char value = (char)NK_CLAMP((float)NK_SCHAR_MIN, values[value_index], (float)NK_SCHAR_MAX); + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(char)); + } break; + case NK_FORMAT_SSHORT: { + nk_short value = (nk_short)NK_CLAMP((float)NK_SSHORT_MIN, values[value_index], (float)NK_SSHORT_MAX); + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(value)); + } break; + case NK_FORMAT_SINT: { + nk_int value = (nk_int)NK_CLAMP((float)NK_SINT_MIN, values[value_index], (float)NK_SINT_MAX); + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(nk_int)); + } break; + case NK_FORMAT_UCHAR: { + unsigned char value = (unsigned char)NK_CLAMP((float)NK_UCHAR_MIN, values[value_index], (float)NK_UCHAR_MAX); + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(unsigned char)); + } break; + case NK_FORMAT_USHORT: { + nk_ushort value = (nk_ushort)NK_CLAMP((float)NK_USHORT_MIN, values[value_index], (float)NK_USHORT_MAX); + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(value)); + } break; + case NK_FORMAT_UINT: { + nk_uint value = (nk_uint)NK_CLAMP((float)NK_UINT_MIN, values[value_index], (float)NK_UINT_MAX); + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(nk_uint)); + } break; + case NK_FORMAT_FLOAT: + NK_MEMCPY(attribute, &values[value_index], sizeof(values[value_index])); + attribute = (void*)((char*)attribute + sizeof(float)); + break; + case NK_FORMAT_DOUBLE: { + double value = (double)values[value_index]; + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(double)); + } break; + } + } +} +NK_INTERN void* +nk_draw_vertex(void *dst, const struct nk_convert_config *config, + struct nk_vec2 pos, struct nk_vec2 uv, struct nk_colorf color) +{ + void *result = (void*)((char*)dst + config->vertex_size); + const struct nk_draw_vertex_layout_element *elem_iter = config->vertex_layout; + while (!nk_draw_vertex_layout_element_is_end_of_layout(elem_iter)) { + void *address = (void*)((char*)dst + elem_iter->offset); + switch (elem_iter->attribute) { + case NK_VERTEX_ATTRIBUTE_COUNT: + default: NK_ASSERT(0 && "wrong element attribute"); break; + case NK_VERTEX_POSITION: nk_draw_vertex_element(address, &pos.x, 2, elem_iter->format); break; + case NK_VERTEX_TEXCOORD: nk_draw_vertex_element(address, &uv.x, 2, elem_iter->format); break; + case NK_VERTEX_COLOR: nk_draw_vertex_color(address, &color.r, elem_iter->format); break; + } + elem_iter++; + } + return result; +} +NK_API void +nk_draw_list_stroke_poly_line(struct nk_draw_list *list, const struct nk_vec2 *points, + const unsigned int points_count, struct nk_color color, enum nk_draw_list_stroke closed, + float thickness, enum nk_anti_aliasing aliasing) +{ + nk_size count; + int thick_line; + struct nk_colorf col; + struct nk_colorf col_trans; + NK_ASSERT(list); + if (!list || points_count < 2) return; + + color.a = (nk_byte)((float)color.a * list->config.global_alpha); + count = points_count; + if (!closed) count = points_count-1; + thick_line = thickness > 1.0f; + +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_draw_list_push_userdata(list, list->userdata); +#endif + + color.a = (nk_byte)((float)color.a * list->config.global_alpha); + nk_color_fv(&col.r, color); + col_trans = col; + col_trans.a = 0; + + if (aliasing == NK_ANTI_ALIASING_ON) { + /* ANTI-ALIASED STROKE */ + const float AA_SIZE = 1.0f; + NK_STORAGE const nk_size pnt_align = NK_ALIGNOF(struct nk_vec2); + NK_STORAGE const nk_size pnt_size = sizeof(struct nk_vec2); + + /* allocate vertices and elements */ + nk_size i1 = 0; + nk_size vertex_offset; + nk_size index = list->vertex_count; + + const nk_size idx_count = (thick_line) ? (count * 18) : (count * 12); + const nk_size vtx_count = (thick_line) ? (points_count * 4): (points_count *3); + + void *vtx = nk_draw_list_alloc_vertices(list, vtx_count); + nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); + + nk_size size; + struct nk_vec2 *normals, *temp; + if (!vtx || !ids) return; + + /* temporary allocate normals + points */ + vertex_offset = (nk_size)((nk_byte*)vtx - (nk_byte*)list->vertices->memory.ptr); + nk_buffer_mark(list->vertices, NK_BUFFER_FRONT); + size = pnt_size * ((thick_line) ? 5 : 3) * points_count; + normals = (struct nk_vec2*) nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, size, pnt_align); + if (!normals) return; + temp = normals + points_count; + + /* make sure vertex pointer is still correct */ + vtx = (void*)((nk_byte*)list->vertices->memory.ptr + vertex_offset); + + /* calculate normals */ + for (i1 = 0; i1 < count; ++i1) { + const nk_size i2 = ((i1 + 1) == points_count) ? 0 : (i1 + 1); + struct nk_vec2 diff = nk_vec2_sub(points[i2], points[i1]); + float len; + + /* vec2 inverted length */ + len = nk_vec2_len_sqr(diff); + if (len != 0.0f) + len = nk_inv_sqrt(len); + else len = 1.0f; + + diff = nk_vec2_muls(diff, len); + normals[i1].x = diff.y; + normals[i1].y = -diff.x; + } + + if (!closed) + normals[points_count-1] = normals[points_count-2]; + + if (!thick_line) { + nk_size idx1, i; + if (!closed) { + struct nk_vec2 d; + temp[0] = nk_vec2_add(points[0], nk_vec2_muls(normals[0], AA_SIZE)); + temp[1] = nk_vec2_sub(points[0], nk_vec2_muls(normals[0], AA_SIZE)); + d = nk_vec2_muls(normals[points_count-1], AA_SIZE); + temp[(points_count-1) * 2 + 0] = nk_vec2_add(points[points_count-1], d); + temp[(points_count-1) * 2 + 1] = nk_vec2_sub(points[points_count-1], d); + } + + /* fill elements */ + idx1 = index; + for (i1 = 0; i1 < count; i1++) { + struct nk_vec2 dm; + float dmr2; + nk_size i2 = ((i1 + 1) == points_count) ? 0 : (i1 + 1); + nk_size idx2 = ((i1+1) == points_count) ? index: (idx1 + 3); + + /* average normals */ + dm = nk_vec2_muls(nk_vec2_add(normals[i1], normals[i2]), 0.5f); + dmr2 = dm.x * dm.x + dm.y* dm.y; + if (dmr2 > 0.000001f) { + float scale = 1.0f/dmr2; + scale = NK_MIN(100.0f, scale); + dm = nk_vec2_muls(dm, scale); + } + + dm = nk_vec2_muls(dm, AA_SIZE); + temp[i2*2+0] = nk_vec2_add(points[i2], dm); + temp[i2*2+1] = nk_vec2_sub(points[i2], dm); + + ids[0] = (nk_draw_index)(idx2 + 0); ids[1] = (nk_draw_index)(idx1+0); + ids[2] = (nk_draw_index)(idx1 + 2); ids[3] = (nk_draw_index)(idx1+2); + ids[4] = (nk_draw_index)(idx2 + 2); ids[5] = (nk_draw_index)(idx2+0); + ids[6] = (nk_draw_index)(idx2 + 1); ids[7] = (nk_draw_index)(idx1+1); + ids[8] = (nk_draw_index)(idx1 + 0); ids[9] = (nk_draw_index)(idx1+0); + ids[10]= (nk_draw_index)(idx2 + 0); ids[11]= (nk_draw_index)(idx2+1); + ids += 12; + idx1 = idx2; + } + + /* fill vertices */ + for (i = 0; i < points_count; ++i) { + const struct nk_vec2 uv = list->config.null.uv; + vtx = nk_draw_vertex(vtx, &list->config, points[i], uv, col); + vtx = nk_draw_vertex(vtx, &list->config, temp[i*2+0], uv, col_trans); + vtx = nk_draw_vertex(vtx, &list->config, temp[i*2+1], uv, col_trans); + } + } else { + nk_size idx1, i; + const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f; + if (!closed) { + struct nk_vec2 d1 = nk_vec2_muls(normals[0], half_inner_thickness + AA_SIZE); + struct nk_vec2 d2 = nk_vec2_muls(normals[0], half_inner_thickness); + + temp[0] = nk_vec2_add(points[0], d1); + temp[1] = nk_vec2_add(points[0], d2); + temp[2] = nk_vec2_sub(points[0], d2); + temp[3] = nk_vec2_sub(points[0], d1); + + d1 = nk_vec2_muls(normals[points_count-1], half_inner_thickness + AA_SIZE); + d2 = nk_vec2_muls(normals[points_count-1], half_inner_thickness); + + temp[(points_count-1)*4+0] = nk_vec2_add(points[points_count-1], d1); + temp[(points_count-1)*4+1] = nk_vec2_add(points[points_count-1], d2); + temp[(points_count-1)*4+2] = nk_vec2_sub(points[points_count-1], d2); + temp[(points_count-1)*4+3] = nk_vec2_sub(points[points_count-1], d1); + } + + /* add all elements */ + idx1 = index; + for (i1 = 0; i1 < count; ++i1) { + struct nk_vec2 dm_out, dm_in; + const nk_size i2 = ((i1+1) == points_count) ? 0: (i1 + 1); + nk_size idx2 = ((i1+1) == points_count) ? index: (idx1 + 4); + + /* average normals */ + struct nk_vec2 dm = nk_vec2_muls(nk_vec2_add(normals[i1], normals[i2]), 0.5f); + float dmr2 = dm.x * dm.x + dm.y* dm.y; + if (dmr2 > 0.000001f) { + float scale = 1.0f/dmr2; + scale = NK_MIN(100.0f, scale); + dm = nk_vec2_muls(dm, scale); + } + + dm_out = nk_vec2_muls(dm, ((half_inner_thickness) + AA_SIZE)); + dm_in = nk_vec2_muls(dm, half_inner_thickness); + temp[i2*4+0] = nk_vec2_add(points[i2], dm_out); + temp[i2*4+1] = nk_vec2_add(points[i2], dm_in); + temp[i2*4+2] = nk_vec2_sub(points[i2], dm_in); + temp[i2*4+3] = nk_vec2_sub(points[i2], dm_out); + + /* add indexes */ + ids[0] = (nk_draw_index)(idx2 + 1); ids[1] = (nk_draw_index)(idx1+1); + ids[2] = (nk_draw_index)(idx1 + 2); ids[3] = (nk_draw_index)(idx1+2); + ids[4] = (nk_draw_index)(idx2 + 2); ids[5] = (nk_draw_index)(idx2+1); + ids[6] = (nk_draw_index)(idx2 + 1); ids[7] = (nk_draw_index)(idx1+1); + ids[8] = (nk_draw_index)(idx1 + 0); ids[9] = (nk_draw_index)(idx1+0); + ids[10]= (nk_draw_index)(idx2 + 0); ids[11] = (nk_draw_index)(idx2+1); + ids[12]= (nk_draw_index)(idx2 + 2); ids[13] = (nk_draw_index)(idx1+2); + ids[14]= (nk_draw_index)(idx1 + 3); ids[15] = (nk_draw_index)(idx1+3); + ids[16]= (nk_draw_index)(idx2 + 3); ids[17] = (nk_draw_index)(idx2+2); + ids += 18; + idx1 = idx2; + } + + /* add vertices */ + for (i = 0; i < points_count; ++i) { + const struct nk_vec2 uv = list->config.null.uv; + vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+0], uv, col_trans); + vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+1], uv, col); + vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+2], uv, col); + vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+3], uv, col_trans); + } + } + /* free temporary normals + points */ + nk_buffer_reset(list->vertices, NK_BUFFER_FRONT); + } else { + /* NON ANTI-ALIASED STROKE */ + nk_size i1 = 0; + nk_size idx = list->vertex_count; + const nk_size idx_count = count * 6; + const nk_size vtx_count = count * 4; + void *vtx = nk_draw_list_alloc_vertices(list, vtx_count); + nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); + if (!vtx || !ids) return; + + for (i1 = 0; i1 < count; ++i1) { + float dx, dy; + const struct nk_vec2 uv = list->config.null.uv; + const nk_size i2 = ((i1+1) == points_count) ? 0 : i1 + 1; + const struct nk_vec2 p1 = points[i1]; + const struct nk_vec2 p2 = points[i2]; + struct nk_vec2 diff = nk_vec2_sub(p2, p1); + float len; + + /* vec2 inverted length */ + len = nk_vec2_len_sqr(diff); + if (len != 0.0f) + len = nk_inv_sqrt(len); + else len = 1.0f; + diff = nk_vec2_muls(diff, len); + + /* add vertices */ + dx = diff.x * (thickness * 0.5f); + dy = diff.y * (thickness * 0.5f); + + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p1.x + dy, p1.y - dx), uv, col); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p2.x + dy, p2.y - dx), uv, col); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p2.x - dy, p2.y + dx), uv, col); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p1.x - dy, p1.y + dx), uv, col); + + ids[0] = (nk_draw_index)(idx+0); ids[1] = (nk_draw_index)(idx+1); + ids[2] = (nk_draw_index)(idx+2); ids[3] = (nk_draw_index)(idx+0); + ids[4] = (nk_draw_index)(idx+2); ids[5] = (nk_draw_index)(idx+3); + + ids += 6; + idx += 4; + } + } +} +NK_API void +nk_draw_list_fill_poly_convex(struct nk_draw_list *list, + const struct nk_vec2 *points, const unsigned int points_count, + struct nk_color color, enum nk_anti_aliasing aliasing) +{ + struct nk_colorf col; + struct nk_colorf col_trans; + + NK_STORAGE const nk_size pnt_align = NK_ALIGNOF(struct nk_vec2); + NK_STORAGE const nk_size pnt_size = sizeof(struct nk_vec2); + NK_ASSERT(list); + if (!list || points_count < 3) return; + +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_draw_list_push_userdata(list, list->userdata); +#endif + + color.a = (nk_byte)((float)color.a * list->config.global_alpha); + nk_color_fv(&col.r, color); + col_trans = col; + col_trans.a = 0; + + if (aliasing == NK_ANTI_ALIASING_ON) { + nk_size i = 0; + nk_size i0 = 0; + nk_size i1 = 0; + + const float AA_SIZE = 1.0f; + nk_size vertex_offset = 0; + nk_size index = list->vertex_count; + + const nk_size idx_count = (points_count-2)*3 + points_count*6; + const nk_size vtx_count = (points_count*2); + + void *vtx = nk_draw_list_alloc_vertices(list, vtx_count); + nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); + + nk_size size = 0; + struct nk_vec2 *normals = 0; + unsigned int vtx_inner_idx = (unsigned int)(index + 0); + unsigned int vtx_outer_idx = (unsigned int)(index + 1); + if (!vtx || !ids) return; + + /* temporary allocate normals */ + vertex_offset = (nk_size)((nk_byte*)vtx - (nk_byte*)list->vertices->memory.ptr); + nk_buffer_mark(list->vertices, NK_BUFFER_FRONT); + size = pnt_size * points_count; + normals = (struct nk_vec2*) nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, size, pnt_align); + if (!normals) return; + vtx = (void*)((nk_byte*)list->vertices->memory.ptr + vertex_offset); + + /* add elements */ + for (i = 2; i < points_count; i++) { + ids[0] = (nk_draw_index)(vtx_inner_idx); + ids[1] = (nk_draw_index)(vtx_inner_idx + ((i-1) << 1)); + ids[2] = (nk_draw_index)(vtx_inner_idx + (i << 1)); + ids += 3; + } + + /* compute normals */ + for (i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) { + struct nk_vec2 p0 = points[i0]; + struct nk_vec2 p1 = points[i1]; + struct nk_vec2 diff = nk_vec2_sub(p1, p0); + + /* vec2 inverted length */ + float len = nk_vec2_len_sqr(diff); + if (len != 0.0f) + len = nk_inv_sqrt(len); + else len = 1.0f; + diff = nk_vec2_muls(diff, len); + + normals[i0].x = diff.y; + normals[i0].y = -diff.x; + } + + /* add vertices + indexes */ + for (i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) { + const struct nk_vec2 uv = list->config.null.uv; + struct nk_vec2 n0 = normals[i0]; + struct nk_vec2 n1 = normals[i1]; + struct nk_vec2 dm = nk_vec2_muls(nk_vec2_add(n0, n1), 0.5f); + float dmr2 = dm.x*dm.x + dm.y*dm.y; + if (dmr2 > 0.000001f) { + float scale = 1.0f / dmr2; + scale = NK_MIN(scale, 100.0f); + dm = nk_vec2_muls(dm, scale); + } + dm = nk_vec2_muls(dm, AA_SIZE * 0.5f); + + /* add vertices */ + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2_sub(points[i1], dm), uv, col); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2_add(points[i1], dm), uv, col_trans); + + /* add indexes */ + ids[0] = (nk_draw_index)(vtx_inner_idx+(i1<<1)); + ids[1] = (nk_draw_index)(vtx_inner_idx+(i0<<1)); + ids[2] = (nk_draw_index)(vtx_outer_idx+(i0<<1)); + ids[3] = (nk_draw_index)(vtx_outer_idx+(i0<<1)); + ids[4] = (nk_draw_index)(vtx_outer_idx+(i1<<1)); + ids[5] = (nk_draw_index)(vtx_inner_idx+(i1<<1)); + ids += 6; + } + /* free temporary normals + points */ + nk_buffer_reset(list->vertices, NK_BUFFER_FRONT); + } else { + nk_size i = 0; + nk_size index = list->vertex_count; + const nk_size idx_count = (points_count-2)*3; + const nk_size vtx_count = points_count; + void *vtx = nk_draw_list_alloc_vertices(list, vtx_count); + nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); + + if (!vtx || !ids) return; + for (i = 0; i < vtx_count; ++i) + vtx = nk_draw_vertex(vtx, &list->config, points[i], list->config.null.uv, col); + for (i = 2; i < points_count; ++i) { + ids[0] = (nk_draw_index)index; + ids[1] = (nk_draw_index)(index+ i - 1); + ids[2] = (nk_draw_index)(index+i); + ids += 3; + } + } +} +NK_API void +nk_draw_list_path_clear(struct nk_draw_list *list) +{ + NK_ASSERT(list); + if (!list) return; + nk_buffer_reset(list->buffer, NK_BUFFER_FRONT); + list->path_count = 0; + list->path_offset = 0; +} +NK_API void +nk_draw_list_path_line_to(struct nk_draw_list *list, struct nk_vec2 pos) +{ + struct nk_vec2 *points = 0; + struct nk_draw_command *cmd = 0; + NK_ASSERT(list); + if (!list) return; + if (!list->cmd_count) + nk_draw_list_add_clip(list, nk_null_rect); + + cmd = nk_draw_list_command_last(list); + if (cmd && cmd->texture.ptr != list->config.null.texture.ptr) + nk_draw_list_push_image(list, list->config.null.texture); + + points = nk_draw_list_alloc_path(list, 1); + if (!points) return; + points[0] = pos; +} +NK_API void +nk_draw_list_path_arc_to_fast(struct nk_draw_list *list, struct nk_vec2 center, + float radius, int a_min, int a_max) +{ + int a = 0; + NK_ASSERT(list); + if (!list) return; + if (a_min <= a_max) { + for (a = a_min; a <= a_max; a++) { + const struct nk_vec2 c = list->circle_vtx[(nk_size)a % NK_LEN(list->circle_vtx)]; + const float x = center.x + c.x * radius; + const float y = center.y + c.y * radius; + nk_draw_list_path_line_to(list, nk_vec2(x, y)); + } + } +} +NK_API void +nk_draw_list_path_arc_to(struct nk_draw_list *list, struct nk_vec2 center, + float radius, float a_min, float a_max, unsigned int segments) +{ + unsigned int i = 0; + NK_ASSERT(list); + if (!list) return; + if (radius == 0.0f) return; + + /* This algorithm for arc drawing relies on these two trigonometric identities[1]: + sin(a + b) = sin(a) * cos(b) + cos(a) * sin(b) + cos(a + b) = cos(a) * cos(b) - sin(a) * sin(b) + + Two coordinates (x, y) of a point on a circle centered on + the origin can be written in polar form as: + x = r * cos(a) + y = r * sin(a) + where r is the radius of the circle, + a is the angle between (x, y) and the origin. + + This allows us to rotate the coordinates around the + origin by an angle b using the following transformation: + x' = r * cos(a + b) = x * cos(b) - y * sin(b) + y' = r * sin(a + b) = y * cos(b) + x * sin(b) + + [1] https://en.wikipedia.org/wiki/List_of_trigonometric_identities#Angle_sum_and_difference_identities + */ + {const float d_angle = (a_max - a_min) / (float)segments; + const float sin_d = (float)NK_SIN(d_angle); + const float cos_d = (float)NK_COS(d_angle); + + float cx = (float)NK_COS(a_min) * radius; + float cy = (float)NK_SIN(a_min) * radius; + for(i = 0; i <= segments; ++i) { + float new_cx, new_cy; + const float x = center.x + cx; + const float y = center.y + cy; + nk_draw_list_path_line_to(list, nk_vec2(x, y)); + + new_cx = cx * cos_d - cy * sin_d; + new_cy = cy * cos_d + cx * sin_d; + cx = new_cx; + cy = new_cy; + }} +} +NK_API void +nk_draw_list_path_rect_to(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 b, float rounding) +{ + float r; + NK_ASSERT(list); + if (!list) return; + r = rounding; + r = NK_MIN(r, ((b.x-a.x) < 0) ? -(b.x-a.x): (b.x-a.x)); + r = NK_MIN(r, ((b.y-a.y) < 0) ? -(b.y-a.y): (b.y-a.y)); + + if (r == 0.0f) { + nk_draw_list_path_line_to(list, a); + nk_draw_list_path_line_to(list, nk_vec2(b.x,a.y)); + nk_draw_list_path_line_to(list, b); + nk_draw_list_path_line_to(list, nk_vec2(a.x,b.y)); + } else { + nk_draw_list_path_arc_to_fast(list, nk_vec2(a.x + r, a.y + r), r, 6, 9); + nk_draw_list_path_arc_to_fast(list, nk_vec2(b.x - r, a.y + r), r, 9, 12); + nk_draw_list_path_arc_to_fast(list, nk_vec2(b.x - r, b.y - r), r, 0, 3); + nk_draw_list_path_arc_to_fast(list, nk_vec2(a.x + r, b.y - r), r, 3, 6); + } +} +NK_API void +nk_draw_list_path_curve_to(struct nk_draw_list *list, struct nk_vec2 p2, + struct nk_vec2 p3, struct nk_vec2 p4, unsigned int num_segments) +{ + float t_step; + unsigned int i_step; + struct nk_vec2 p1; + + NK_ASSERT(list); + NK_ASSERT(list->path_count); + if (!list || !list->path_count) return; + num_segments = NK_MAX(num_segments, 1); + + p1 = nk_draw_list_path_last(list); + t_step = 1.0f/(float)num_segments; + for (i_step = 1; i_step <= num_segments; ++i_step) { + float t = t_step * (float)i_step; + float u = 1.0f - t; + float w1 = u*u*u; + float w2 = 3*u*u*t; + float w3 = 3*u*t*t; + float w4 = t * t *t; + float x = w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x; + float y = w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y; + nk_draw_list_path_line_to(list, nk_vec2(x,y)); + } +} +NK_API void +nk_draw_list_path_fill(struct nk_draw_list *list, struct nk_color color) +{ + struct nk_vec2 *points; + NK_ASSERT(list); + if (!list) return; + points = (struct nk_vec2*)nk_buffer_memory(list->buffer); + nk_draw_list_fill_poly_convex(list, points, list->path_count, color, list->config.shape_AA); + nk_draw_list_path_clear(list); +} +NK_API void +nk_draw_list_path_stroke(struct nk_draw_list *list, struct nk_color color, + enum nk_draw_list_stroke closed, float thickness) +{ + struct nk_vec2 *points; + NK_ASSERT(list); + if (!list) return; + points = (struct nk_vec2*)nk_buffer_memory(list->buffer); + nk_draw_list_stroke_poly_line(list, points, list->path_count, color, + closed, thickness, list->config.line_AA); + nk_draw_list_path_clear(list); +} +NK_API void +nk_draw_list_stroke_line(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 b, struct nk_color col, float thickness) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + if (list->line_AA == NK_ANTI_ALIASING_ON) { + nk_draw_list_path_line_to(list, a); + nk_draw_list_path_line_to(list, b); + } else { + nk_draw_list_path_line_to(list, nk_vec2_sub(a,nk_vec2(0.5f,0.5f))); + nk_draw_list_path_line_to(list, nk_vec2_sub(b,nk_vec2(0.5f,0.5f))); + } + nk_draw_list_path_stroke(list, col, NK_STROKE_OPEN, thickness); +} +NK_API void +nk_draw_list_fill_rect(struct nk_draw_list *list, struct nk_rect rect, + struct nk_color col, float rounding) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + + if (list->line_AA == NK_ANTI_ALIASING_ON) { + nk_draw_list_path_rect_to(list, nk_vec2(rect.x, rect.y), + nk_vec2(rect.x + rect.w, rect.y + rect.h), rounding); + } else { + nk_draw_list_path_rect_to(list, nk_vec2(rect.x-0.5f, rect.y-0.5f), + nk_vec2(rect.x + rect.w, rect.y + rect.h), rounding); + } nk_draw_list_path_fill(list, col); +} +NK_API void +nk_draw_list_stroke_rect(struct nk_draw_list *list, struct nk_rect rect, + struct nk_color col, float rounding, float thickness) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + if (list->line_AA == NK_ANTI_ALIASING_ON) { + nk_draw_list_path_rect_to(list, nk_vec2(rect.x, rect.y), + nk_vec2(rect.x + rect.w, rect.y + rect.h), rounding); + } else { + nk_draw_list_path_rect_to(list, nk_vec2(rect.x-0.5f, rect.y-0.5f), + nk_vec2(rect.x + rect.w, rect.y + rect.h), rounding); + } nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness); +} +NK_API void +nk_draw_list_fill_rect_multi_color(struct nk_draw_list *list, struct nk_rect rect, + struct nk_color left, struct nk_color top, struct nk_color right, + struct nk_color bottom) +{ + void *vtx; + struct nk_colorf col_left, col_top; + struct nk_colorf col_right, col_bottom; + nk_draw_index *idx; + nk_draw_index index; + + nk_color_fv(&col_left.r, left); + nk_color_fv(&col_right.r, right); + nk_color_fv(&col_top.r, top); + nk_color_fv(&col_bottom.r, bottom); + + NK_ASSERT(list); + if (!list) return; + + nk_draw_list_push_image(list, list->config.null.texture); + index = (nk_draw_index)list->vertex_count; + vtx = nk_draw_list_alloc_vertices(list, 4); + idx = nk_draw_list_alloc_elements(list, 6); + if (!vtx || !idx) return; + + idx[0] = (nk_draw_index)(index+0); idx[1] = (nk_draw_index)(index+1); + idx[2] = (nk_draw_index)(index+2); idx[3] = (nk_draw_index)(index+0); + idx[4] = (nk_draw_index)(index+2); idx[5] = (nk_draw_index)(index+3); + + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x, rect.y), list->config.null.uv, col_left); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x + rect.w, rect.y), list->config.null.uv, col_top); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x + rect.w, rect.y + rect.h), list->config.null.uv, col_right); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x, rect.y + rect.h), list->config.null.uv, col_bottom); +} +NK_API void +nk_draw_list_fill_triangle(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 b, struct nk_vec2 c, struct nk_color col) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + nk_draw_list_path_line_to(list, a); + nk_draw_list_path_line_to(list, b); + nk_draw_list_path_line_to(list, c); + nk_draw_list_path_fill(list, col); +} +NK_API void +nk_draw_list_stroke_triangle(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 b, struct nk_vec2 c, struct nk_color col, float thickness) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + nk_draw_list_path_line_to(list, a); + nk_draw_list_path_line_to(list, b); + nk_draw_list_path_line_to(list, c); + nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness); +} +NK_API void +nk_draw_list_fill_circle(struct nk_draw_list *list, struct nk_vec2 center, + float radius, struct nk_color col, unsigned int segs) +{ + float a_max; + NK_ASSERT(list); + if (!list || !col.a) return; + a_max = NK_PI * 2.0f * ((float)segs - 1.0f) / (float)segs; + nk_draw_list_path_arc_to(list, center, radius, 0.0f, a_max, segs); + nk_draw_list_path_fill(list, col); +} +NK_API void +nk_draw_list_stroke_circle(struct nk_draw_list *list, struct nk_vec2 center, + float radius, struct nk_color col, unsigned int segs, float thickness) +{ + float a_max; + NK_ASSERT(list); + if (!list || !col.a) return; + a_max = NK_PI * 2.0f * ((float)segs - 1.0f) / (float)segs; + nk_draw_list_path_arc_to(list, center, radius, 0.0f, a_max, segs); + nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness); +} +NK_API void +nk_draw_list_stroke_curve(struct nk_draw_list *list, struct nk_vec2 p0, + struct nk_vec2 cp0, struct nk_vec2 cp1, struct nk_vec2 p1, + struct nk_color col, unsigned int segments, float thickness) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + nk_draw_list_path_line_to(list, p0); + nk_draw_list_path_curve_to(list, cp0, cp1, p1, segments); + nk_draw_list_path_stroke(list, col, NK_STROKE_OPEN, thickness); +} +NK_INTERN void +nk_draw_list_push_rect_uv(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 c, struct nk_vec2 uva, struct nk_vec2 uvc, + struct nk_color color) +{ + void *vtx; + struct nk_vec2 uvb; + struct nk_vec2 uvd; + struct nk_vec2 b; + struct nk_vec2 d; + + struct nk_colorf col; + nk_draw_index *idx; + nk_draw_index index; + NK_ASSERT(list); + if (!list) return; + + nk_color_fv(&col.r, color); + uvb = nk_vec2(uvc.x, uva.y); + uvd = nk_vec2(uva.x, uvc.y); + b = nk_vec2(c.x, a.y); + d = nk_vec2(a.x, c.y); + + index = (nk_draw_index)list->vertex_count; + vtx = nk_draw_list_alloc_vertices(list, 4); + idx = nk_draw_list_alloc_elements(list, 6); + if (!vtx || !idx) return; + + idx[0] = (nk_draw_index)(index+0); idx[1] = (nk_draw_index)(index+1); + idx[2] = (nk_draw_index)(index+2); idx[3] = (nk_draw_index)(index+0); + idx[4] = (nk_draw_index)(index+2); idx[5] = (nk_draw_index)(index+3); + + vtx = nk_draw_vertex(vtx, &list->config, a, uva, col); + vtx = nk_draw_vertex(vtx, &list->config, b, uvb, col); + vtx = nk_draw_vertex(vtx, &list->config, c, uvc, col); + vtx = nk_draw_vertex(vtx, &list->config, d, uvd, col); +} +NK_API void +nk_draw_list_add_image(struct nk_draw_list *list, struct nk_image texture, + struct nk_rect rect, struct nk_color color) +{ + NK_ASSERT(list); + if (!list) return; + /* push new command with given texture */ + nk_draw_list_push_image(list, texture.handle); + if (nk_image_is_subimage(&texture)) { + /* add region inside of the texture */ + struct nk_vec2 uv[2]; + uv[0].x = (float)texture.region[0]/(float)texture.w; + uv[0].y = (float)texture.region[1]/(float)texture.h; + uv[1].x = (float)(texture.region[0] + texture.region[2])/(float)texture.w; + uv[1].y = (float)(texture.region[1] + texture.region[3])/(float)texture.h; + nk_draw_list_push_rect_uv(list, nk_vec2(rect.x, rect.y), + nk_vec2(rect.x + rect.w, rect.y + rect.h), uv[0], uv[1], color); + } else nk_draw_list_push_rect_uv(list, nk_vec2(rect.x, rect.y), + nk_vec2(rect.x + rect.w, rect.y + rect.h), + nk_vec2(0.0f, 0.0f), nk_vec2(1.0f, 1.0f),color); +} +NK_API void +nk_draw_list_add_text(struct nk_draw_list *list, const struct nk_user_font *font, + struct nk_rect rect, const char *text, int len, float font_height, + struct nk_color fg) +{ + float x = 0; + int text_len = 0; + nk_rune unicode = 0; + nk_rune next = 0; + int glyph_len = 0; + int next_glyph_len = 0; + struct nk_user_font_glyph g; + + NK_ASSERT(list); + if (!list || !len || !text) return; + if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, + list->clip_rect.x, list->clip_rect.y, list->clip_rect.w, list->clip_rect.h)) return; + + nk_draw_list_push_image(list, font->texture); + x = rect.x; + glyph_len = nk_utf_decode(text, &unicode, len); + if (!glyph_len) return; + + /* draw every glyph image */ + fg.a = (nk_byte)((float)fg.a * list->config.global_alpha); + while (text_len < len && glyph_len) { + float gx, gy, gh, gw; + float char_width = 0; + if (unicode == NK_UTF_INVALID) break; + + /* query currently drawn glyph information */ + next_glyph_len = nk_utf_decode(text + text_len + glyph_len, &next, (int)len - text_len); + font->query(font->userdata, font_height, &g, unicode, + (next == NK_UTF_INVALID) ? '\0' : next); + + /* calculate and draw glyph drawing rectangle and image */ + gx = x + g.offset.x; + gy = rect.y + g.offset.y; + gw = g.width; gh = g.height; + char_width = g.xadvance; + nk_draw_list_push_rect_uv(list, nk_vec2(gx,gy), nk_vec2(gx + gw, gy+ gh), + g.uv[0], g.uv[1], fg); + + /* offset next glyph */ + text_len += glyph_len; + x += char_width; + glyph_len = next_glyph_len; + unicode = next; + } +} +NK_API nk_flags +nk_convert(struct nk_context *ctx, struct nk_buffer *cmds, + struct nk_buffer *vertices, struct nk_buffer *elements, + const struct nk_convert_config *config) +{ + nk_flags res = NK_CONVERT_SUCCESS; + const struct nk_command *cmd; + NK_ASSERT(ctx); + NK_ASSERT(cmds); + NK_ASSERT(vertices); + NK_ASSERT(elements); + NK_ASSERT(config); + NK_ASSERT(config->vertex_layout); + NK_ASSERT(config->vertex_size); + if (!ctx || !cmds || !vertices || !elements || !config || !config->vertex_layout) + return NK_CONVERT_INVALID_PARAM; + + nk_draw_list_setup(&ctx->draw_list, config, cmds, vertices, elements, + config->line_AA, config->shape_AA); + nk_foreach(cmd, ctx) + { +#ifdef NK_INCLUDE_COMMAND_USERDATA + ctx->draw_list.userdata = cmd->userdata; +#endif + switch (cmd->type) { + case NK_COMMAND_NOP: break; + case NK_COMMAND_SCISSOR: { + const struct nk_command_scissor *s = (const struct nk_command_scissor*)cmd; + nk_draw_list_add_clip(&ctx->draw_list, nk_rect(s->x, s->y, s->w, s->h)); + } break; + case NK_COMMAND_LINE: { + const struct nk_command_line *l = (const struct nk_command_line*)cmd; + nk_draw_list_stroke_line(&ctx->draw_list, nk_vec2(l->begin.x, l->begin.y), + nk_vec2(l->end.x, l->end.y), l->color, l->line_thickness); + } break; + case NK_COMMAND_CURVE: { + const struct nk_command_curve *q = (const struct nk_command_curve*)cmd; + nk_draw_list_stroke_curve(&ctx->draw_list, nk_vec2(q->begin.x, q->begin.y), + nk_vec2(q->ctrl[0].x, q->ctrl[0].y), nk_vec2(q->ctrl[1].x, + q->ctrl[1].y), nk_vec2(q->end.x, q->end.y), q->color, + config->curve_segment_count, q->line_thickness); + } break; + case NK_COMMAND_RECT: { + const struct nk_command_rect *r = (const struct nk_command_rect*)cmd; + nk_draw_list_stroke_rect(&ctx->draw_list, nk_rect(r->x, r->y, r->w, r->h), + r->color, (float)r->rounding, r->line_thickness); + } break; + case NK_COMMAND_RECT_FILLED: { + const struct nk_command_rect_filled *r = (const struct nk_command_rect_filled*)cmd; + nk_draw_list_fill_rect(&ctx->draw_list, nk_rect(r->x, r->y, r->w, r->h), + r->color, (float)r->rounding); + } break; + case NK_COMMAND_RECT_MULTI_COLOR: { + const struct nk_command_rect_multi_color *r = (const struct nk_command_rect_multi_color*)cmd; + nk_draw_list_fill_rect_multi_color(&ctx->draw_list, nk_rect(r->x, r->y, r->w, r->h), + r->left, r->top, r->right, r->bottom); + } break; + case NK_COMMAND_CIRCLE: { + const struct nk_command_circle *c = (const struct nk_command_circle*)cmd; + nk_draw_list_stroke_circle(&ctx->draw_list, nk_vec2((float)c->x + (float)c->w/2, + (float)c->y + (float)c->h/2), (float)c->w/2, c->color, + config->circle_segment_count, c->line_thickness); + } break; + case NK_COMMAND_CIRCLE_FILLED: { + const struct nk_command_circle_filled *c = (const struct nk_command_circle_filled *)cmd; + nk_draw_list_fill_circle(&ctx->draw_list, nk_vec2((float)c->x + (float)c->w/2, + (float)c->y + (float)c->h/2), (float)c->w/2, c->color, + config->circle_segment_count); + } break; + case NK_COMMAND_ARC: { + const struct nk_command_arc *c = (const struct nk_command_arc*)cmd; + nk_draw_list_path_line_to(&ctx->draw_list, nk_vec2(c->cx, c->cy)); + nk_draw_list_path_arc_to(&ctx->draw_list, nk_vec2(c->cx, c->cy), c->r, + c->a[0], c->a[1], config->arc_segment_count); + nk_draw_list_path_stroke(&ctx->draw_list, c->color, NK_STROKE_CLOSED, c->line_thickness); + } break; + case NK_COMMAND_ARC_FILLED: { + const struct nk_command_arc_filled *c = (const struct nk_command_arc_filled*)cmd; + nk_draw_list_path_line_to(&ctx->draw_list, nk_vec2(c->cx, c->cy)); + nk_draw_list_path_arc_to(&ctx->draw_list, nk_vec2(c->cx, c->cy), c->r, + c->a[0], c->a[1], config->arc_segment_count); + nk_draw_list_path_fill(&ctx->draw_list, c->color); + } break; + case NK_COMMAND_TRIANGLE: { + const struct nk_command_triangle *t = (const struct nk_command_triangle*)cmd; + nk_draw_list_stroke_triangle(&ctx->draw_list, nk_vec2(t->a.x, t->a.y), + nk_vec2(t->b.x, t->b.y), nk_vec2(t->c.x, t->c.y), t->color, + t->line_thickness); + } break; + case NK_COMMAND_TRIANGLE_FILLED: { + const struct nk_command_triangle_filled *t = (const struct nk_command_triangle_filled*)cmd; + nk_draw_list_fill_triangle(&ctx->draw_list, nk_vec2(t->a.x, t->a.y), + nk_vec2(t->b.x, t->b.y), nk_vec2(t->c.x, t->c.y), t->color); + } break; + case NK_COMMAND_POLYGON: { + int i; + const struct nk_command_polygon*p = (const struct nk_command_polygon*)cmd; + for (i = 0; i < p->point_count; ++i) { + struct nk_vec2 pnt = nk_vec2((float)p->points[i].x, (float)p->points[i].y); + nk_draw_list_path_line_to(&ctx->draw_list, pnt); + } + nk_draw_list_path_stroke(&ctx->draw_list, p->color, NK_STROKE_CLOSED, p->line_thickness); + } break; + case NK_COMMAND_POLYGON_FILLED: { + int i; + const struct nk_command_polygon_filled *p = (const struct nk_command_polygon_filled*)cmd; + for (i = 0; i < p->point_count; ++i) { + struct nk_vec2 pnt = nk_vec2((float)p->points[i].x, (float)p->points[i].y); + nk_draw_list_path_line_to(&ctx->draw_list, pnt); + } + nk_draw_list_path_fill(&ctx->draw_list, p->color); + } break; + case NK_COMMAND_POLYLINE: { + int i; + const struct nk_command_polyline *p = (const struct nk_command_polyline*)cmd; + for (i = 0; i < p->point_count; ++i) { + struct nk_vec2 pnt = nk_vec2((float)p->points[i].x, (float)p->points[i].y); + nk_draw_list_path_line_to(&ctx->draw_list, pnt); + } + nk_draw_list_path_stroke(&ctx->draw_list, p->color, NK_STROKE_OPEN, p->line_thickness); + } break; + case NK_COMMAND_TEXT: { + const struct nk_command_text *t = (const struct nk_command_text*)cmd; + nk_draw_list_add_text(&ctx->draw_list, t->font, nk_rect(t->x, t->y, t->w, t->h), + t->string, t->length, t->height, t->foreground); + } break; + case NK_COMMAND_IMAGE: { + const struct nk_command_image *i = (const struct nk_command_image*)cmd; + nk_draw_list_add_image(&ctx->draw_list, i->img, nk_rect(i->x, i->y, i->w, i->h), i->col); + } break; + case NK_COMMAND_CUSTOM: { + const struct nk_command_custom *c = (const struct nk_command_custom*)cmd; + c->callback(&ctx->draw_list, c->x, c->y, c->w, c->h, c->callback_data); + } break; + default: break; + } + } + res |= (cmds->needed > cmds->allocated + (cmds->memory.size - cmds->size)) ? NK_CONVERT_COMMAND_BUFFER_FULL: 0; + res |= (vertices->needed > vertices->allocated) ? NK_CONVERT_VERTEX_BUFFER_FULL: 0; + res |= (elements->needed > elements->allocated) ? NK_CONVERT_ELEMENT_BUFFER_FULL: 0; + return res; +} +NK_API const struct nk_draw_command* +nk__draw_begin(const struct nk_context *ctx, + const struct nk_buffer *buffer) +{ + return nk__draw_list_begin(&ctx->draw_list, buffer); +} +NK_API const struct nk_draw_command* +nk__draw_end(const struct nk_context *ctx, const struct nk_buffer *buffer) +{ + return nk__draw_list_end(&ctx->draw_list, buffer); +} +NK_API const struct nk_draw_command* +nk__draw_next(const struct nk_draw_command *cmd, + const struct nk_buffer *buffer, const struct nk_context *ctx) +{ + return nk__draw_list_next(cmd, buffer, &ctx->draw_list); +} +#endif + + +/* stb_rect_pack.h - v1.00 - public domain - rectangle packing */ +/* Sean Barrett 2014 */ +/* */ +/* Useful for e.g. packing rectangular textures into an atlas. */ +/* Does not do rotation. */ +/* */ +/* Not necessarily the awesomest packing method, but better than */ +/* the totally naive one in stb_truetype (which is primarily what */ +/* this is meant to replace). */ +/* */ +/* Has only had a few tests run, may have issues. */ +/* */ +/* More docs to come. */ +/* */ +/* No memory allocations; uses qsort() and assert() from stdlib. */ +/* Can override those by defining STBRP_SORT and STBRP_ASSERT. */ +/* */ +/* This library currently uses the Skyline Bottom-Left algorithm. */ +/* */ +/* Please note: better rectangle packers are welcome! Please */ +/* implement them to the same API, but with a different init */ +/* function. */ +/* */ +/* Credits */ +/* */ +/* Library */ +/* Sean Barrett */ +/* Minor features */ +/* Martins Mozeiko */ +/* github:IntellectualKitty */ +/* */ +/* Bugfixes / warning fixes */ +/* Jeremy Jaussaud */ +/* Fabian Giesen */ +/* */ +/* Version history: */ +/* */ +/* 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles */ +/* 0.99 (2019-02-07) warning fixes */ +/* 0.11 (2017-03-03) return packing success/fail result */ +/* 0.10 (2016-10-25) remove cast-away-const to avoid warnings */ +/* 0.09 (2016-08-27) fix compiler warnings */ +/* 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) */ +/* 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) */ +/* 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort */ +/* 0.05: added STBRP_ASSERT to allow replacing assert */ +/* 0.04: fixed minor bug in STBRP_LARGE_RECTS support */ +/* 0.01: initial release */ +/* */ +/* LICENSE */ +/* */ +/* See end of file for license information. */ + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* INCLUDE SECTION */ +/* */ + +#ifndef STB_INCLUDE_STB_RECT_PACK_H +#define STB_INCLUDE_STB_RECT_PACK_H + +#define STB_RECT_PACK_VERSION 1 + +#ifdef STBRP_STATIC +#define STBRP_DEF static +#else +#define STBRP_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct stbrp_context stbrp_context; +typedef struct stbrp_node stbrp_node; +typedef struct stbrp_rect stbrp_rect; + +#ifdef STBRP_LARGE_RECTS +typedef int stbrp_coord; +#else +typedef unsigned short stbrp_coord; +#endif + +STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); +/* Assign packed locations to rectangles. The rectangles are of type */ +/* 'stbrp_rect' defined below, stored in the array 'rects', and there */ +/* are 'num_rects' many of them. */ +/* */ +/* Rectangles which are successfully packed have the 'was_packed' flag */ +/* set to a non-zero value and 'x' and 'y' store the minimum location */ +/* on each axis (i.e. bottom-left in cartesian coordinates, top-left */ +/* if you imagine y increasing downwards). Rectangles which do not fit */ +/* have the 'was_packed' flag set to 0. */ +/* */ +/* You should not try to access the 'rects' array from another thread */ +/* while this function is running, as the function temporarily reorders */ +/* the array while it executes. */ +/* */ +/* To pack into another rectangle, you need to call stbrp_init_target */ +/* again. To continue packing into the same rectangle, you can call */ +/* this function again. Calling this multiple times with multiple rect */ +/* arrays will probably produce worse packing results than calling it */ +/* a single time with the full rectangle array, but the option is */ +/* available. */ +/* */ +/* The function returns 1 if all of the rectangles were successfully */ +/* packed and 0 otherwise. */ + +struct stbrp_rect +{ + /* reserved for your use: */ + int id; + + /* input: */ + stbrp_coord w, h; + + /* output: */ + stbrp_coord x, y; + int was_packed; /* non-zero if valid packing */ + +}; /* 16 bytes, nominally */ + + +STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); +/* Initialize a rectangle packer to: */ +/* pack a rectangle that is 'width' by 'height' in dimensions */ +/* using temporary storage provided by the array 'nodes', which is 'num_nodes' long */ +/* */ +/* You must call this function every time you start packing into a new target. */ +/* */ +/* There is no "shutdown" function. The 'nodes' memory must stay valid for */ +/* the following stbrp_pack_rects() call (or calls), but can be freed after */ +/* the call (or calls) finish. */ +/* */ +/* Note: to guarantee best results, either: */ +/* 1. make sure 'num_nodes' >= 'width' */ +/* or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' */ +/* */ +/* If you don't do either of the above things, widths will be quantized to multiples */ +/* of small integers to guarantee the algorithm doesn't run out of temporary storage. */ +/* */ +/* If you do #2, then the non-quantized algorithm will be used, but the algorithm */ +/* may run out of temporary storage and be unable to pack some rectangles. */ + +STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); +/* Optionally call this function after init but before doing any packing to */ +/* change the handling of the out-of-temp-memory scenario, described above. */ +/* If you call init again, this will be reset to the default (false). */ + + +STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); +/* Optionally select which packing heuristic the library should use. Different */ +/* heuristics will produce better/worse results for different data sets. */ +/* If you call init again, this will be reset to the default. */ + +enum +{ + STBRP_HEURISTIC_Skyline_default=0, + STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, + STBRP_HEURISTIC_Skyline_BF_sortHeight +}; + + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* the details of the following structures don't matter to you, but they must */ +/* be visible so you can handle the memory allocations for them */ + +struct stbrp_node +{ + stbrp_coord x,y; + stbrp_node *next; +}; + +struct stbrp_context +{ + int width; + int height; + int align; + int init_mode; + int heuristic; + int num_nodes; + stbrp_node *active_head; + stbrp_node *free_head; + stbrp_node extra[2]; /* we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' */ +}; + +#ifdef __cplusplus +} +#endif + +#endif + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* IMPLEMENTATION SECTION */ +/* */ + +#ifdef STB_RECT_PACK_IMPLEMENTATION +#ifndef STBRP_SORT +#include +#define STBRP_SORT qsort +#endif + +#ifndef STBRP_ASSERT +#include +#define STBRP_ASSERT assert +#endif + +#ifdef _MSC_VER +#define STBRP__NOTUSED(v) (void)(v) +#else +#define STBRP__NOTUSED(v) (void)sizeof(v) +#endif + +enum +{ + STBRP__INIT_skyline = 1 +}; + +STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) +{ + switch (context->init_mode) { + case STBRP__INIT_skyline: + STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); + context->heuristic = heuristic; + break; + default: + STBRP_ASSERT(0); + } +} + +STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) +{ + if (allow_out_of_mem) + /* if it's ok to run out of memory, then don't bother aligning them; */ + /* this gives better packing, but may fail due to OOM (even though */ + /* the rectangles easily fit). @TODO a smarter approach would be to only */ + /* quantize once we've hit OOM, then we could get rid of this parameter. */ + context->align = 1; + else { + /* if it's not ok to run out of memory, then quantize the widths */ + /* so that num_nodes is always enough nodes. */ + /* */ + /* I.e. num_nodes * align >= width */ + /* align >= width / num_nodes */ + /* align = ceil(width/num_nodes) */ + + context->align = (context->width + context->num_nodes-1) / context->num_nodes; + } +} + +STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) +{ + int i; +#ifndef STBRP_LARGE_RECTS + STBRP_ASSERT(width <= 0xffff && height <= 0xffff); +#endif + + for (i=0; i < num_nodes-1; ++i) + nodes[i].next = &nodes[i+1]; + nodes[i].next = NULL; + context->init_mode = STBRP__INIT_skyline; + context->heuristic = STBRP_HEURISTIC_Skyline_default; + context->free_head = &nodes[0]; + context->active_head = &context->extra[0]; + context->width = width; + context->height = height; + context->num_nodes = num_nodes; + stbrp_setup_allow_out_of_mem(context, 0); + + /* node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) */ + context->extra[0].x = 0; + context->extra[0].y = 0; + context->extra[0].next = &context->extra[1]; + context->extra[1].x = (stbrp_coord) width; +#ifdef STBRP_LARGE_RECTS + context->extra[1].y = (1<<30); +#else + context->extra[1].y = 65535; +#endif + context->extra[1].next = NULL; +} + +/* find minimum y position if it starts at x1 */ +static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) +{ + stbrp_node *node = first; + int x1 = x0 + width; + int min_y, visited_width, waste_area; + + STBRP__NOTUSED(c); + + STBRP_ASSERT(first->x <= x0); + + #if 0 + /* skip in case we're past the node */ + while (node->next->x <= x0) + ++node; + #else + STBRP_ASSERT(node->next->x > x0); /* we ended up handling this in the caller for efficiency */ + #endif + + STBRP_ASSERT(node->x <= x0); + + min_y = 0; + waste_area = 0; + visited_width = 0; + while (node->x < x1) { + if (node->y > min_y) { + /* raise min_y higher. */ + /* we've accounted for all waste up to min_y, */ + /* but we'll now add more waste for everything we've visted */ + waste_area += visited_width * (node->y - min_y); + min_y = node->y; + /* the first time through, visited_width might be reduced */ + if (node->x < x0) + visited_width += node->next->x - x0; + else + visited_width += node->next->x - node->x; + } else { + /* add waste area */ + int under_width = node->next->x - node->x; + if (under_width + visited_width > width) + under_width = width - visited_width; + waste_area += under_width * (min_y - node->y); + visited_width += under_width; + } + node = node->next; + } + + *pwaste = waste_area; + return min_y; +} + +typedef struct +{ + int x,y; + stbrp_node **prev_link; +} stbrp__findresult; + +static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) +{ + int best_waste = (1<<30), best_x, best_y = (1 << 30); + stbrp__findresult fr; + stbrp_node **prev, *node, *tail, **best = NULL; + + /* align to multiple of c->align */ + width = (width + c->align - 1); + width -= width % c->align; + STBRP_ASSERT(width % c->align == 0); + + /* if it can't possibly fit, bail immediately */ + if (width > c->width || height > c->height) { + fr.prev_link = NULL; + fr.x = fr.y = 0; + return fr; + } + + node = c->active_head; + prev = &c->active_head; + while (node->x + width <= c->width) { + int y,waste; + y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); + if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { /* actually just want to test BL */ + /* bottom left */ + if (y < best_y) { + best_y = y; + best = prev; + } + } else { + /* best-fit */ + if (y + height <= c->height) { + /* can only use it if it first vertically */ + if (y < best_y || (y == best_y && waste < best_waste)) { + best_y = y; + best_waste = waste; + best = prev; + } + } + } + prev = &node->next; + node = node->next; + } + + best_x = (best == NULL) ? 0 : (*best)->x; + + /* if doing best-fit (BF), we also have to try aligning right edge to each node position */ + /* */ + /* e.g, if fitting */ + /* */ + /* ____________________ */ + /* |____________________| */ + /* */ + /* into */ + /* */ + /* | | */ + /* | ____________| */ + /* |____________| */ + /* */ + /* then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned */ + /* */ + /* This makes BF take about 2x the time */ + + if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { + tail = c->active_head; + node = c->active_head; + prev = &c->active_head; + /* find first node that's admissible */ + while (tail->x < width) + tail = tail->next; + while (tail) { + int xpos = tail->x - width; + int y,waste; + STBRP_ASSERT(xpos >= 0); + /* find the left position that matches this */ + while (node->next->x <= xpos) { + prev = &node->next; + node = node->next; + } + STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); + y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); + if (y + height <= c->height) { + if (y <= best_y) { + if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { + best_x = xpos; + STBRP_ASSERT(y <= best_y); + best_y = y; + best_waste = waste; + best = prev; + } + } + } + tail = tail->next; + } + } + + fr.prev_link = best; + fr.x = best_x; + fr.y = best_y; + return fr; +} + +static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) +{ + /* find best position according to heuristic */ + stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); + stbrp_node *node, *cur; + + /* bail if: */ + /* 1. it failed */ + /* 2. the best node doesn't fit (we don't always check this) */ + /* 3. we're out of memory */ + if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { + res.prev_link = NULL; + return res; + } + + /* on success, create new node */ + node = context->free_head; + node->x = (stbrp_coord) res.x; + node->y = (stbrp_coord) (res.y + height); + + context->free_head = node->next; + + /* insert the new node into the right starting point, and */ + /* let 'cur' point to the remaining nodes needing to be */ + /* stiched back in */ + + cur = *res.prev_link; + if (cur->x < res.x) { + /* preserve the existing one, so start testing with the next one */ + stbrp_node *next = cur->next; + cur->next = node; + cur = next; + } else { + *res.prev_link = node; + } + + /* from here, traverse cur and free the nodes, until we get to one */ + /* that shouldn't be freed */ + while (cur->next && cur->next->x <= res.x + width) { + stbrp_node *next = cur->next; + /* move the current node to the free list */ + cur->next = context->free_head; + context->free_head = cur; + cur = next; + } + + /* stitch the list back in */ + node->next = cur; + + if (cur->x < res.x + width) + cur->x = (stbrp_coord) (res.x + width); + +#ifdef _DEBUG + cur = context->active_head; + while (cur->x < context->width) { + STBRP_ASSERT(cur->x < cur->next->x); + cur = cur->next; + } + STBRP_ASSERT(cur->next == NULL); + + { + int count=0; + cur = context->active_head; + while (cur) { + cur = cur->next; + ++count; + } + cur = context->free_head; + while (cur) { + cur = cur->next; + ++count; + } + STBRP_ASSERT(count == context->num_nodes+2); + } +#endif + + return res; +} + +static int rect_height_compare(const void *a, const void *b) +{ + const stbrp_rect *p = (const stbrp_rect *) a; + const stbrp_rect *q = (const stbrp_rect *) b; + if (p->h > q->h) + return -1; + if (p->h < q->h) + return 1; + return (p->w > q->w) ? -1 : (p->w < q->w); +} + +static int rect_original_order(const void *a, const void *b) +{ + const stbrp_rect *p = (const stbrp_rect *) a; + const stbrp_rect *q = (const stbrp_rect *) b; + return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); +} + +#ifdef STBRP_LARGE_RECTS +#define STBRP__MAXVAL 0xffffffff +#else +#define STBRP__MAXVAL 0xffff +#endif + +STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) +{ + int i, all_rects_packed = 1; + + /* we use the 'was_packed' field internally to allow sorting/unsorting */ + for (i=0; i < num_rects; ++i) { + rects[i].was_packed = i; + } + + /* sort according to heuristic */ + STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); + + for (i=0; i < num_rects; ++i) { + if (rects[i].w == 0 || rects[i].h == 0) { + rects[i].x = rects[i].y = 0; /* empty rect needs no space */ + } else { + stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); + if (fr.prev_link) { + rects[i].x = (stbrp_coord) fr.x; + rects[i].y = (stbrp_coord) fr.y; + } else { + rects[i].x = rects[i].y = STBRP__MAXVAL; + } + } + } + + /* unsort */ + STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); + + /* set was_packed flags and all_rects_packed status */ + for (i=0; i < num_rects; ++i) { + rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); + if (!rects[i].was_packed) + all_rects_packed = 0; + } + + /* return the all_rects_packed status */ + return all_rects_packed; +} +#endif + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ + +/* stb_truetype.h - v1.24 - public domain */ +/* authored from 2009-2020 by Sean Barrett / RAD Game Tools */ +/* */ +/* ======================================================================= */ +/* */ +/* NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES */ +/* */ +/* This library does no range checking of the offsets found in the file, */ +/* meaning an attacker can use it to read arbitrary memory. */ +/* */ +/* ======================================================================= */ +/* */ +/* This library processes TrueType files: */ +/* parse files */ +/* extract glyph metrics */ +/* extract glyph shapes */ +/* render glyphs to one-channel bitmaps with antialiasing (box filter) */ +/* render glyphs to one-channel SDF bitmaps (signed-distance field/function) */ +/* */ +/* Todo: */ +/* non-MS cmaps */ +/* crashproof on bad data */ +/* hinting? (no longer patented) */ +/* cleartype-style AA? */ +/* optimize: use simple memory allocator for intermediates */ +/* optimize: build edge-list directly from curves */ +/* optimize: rasterize directly from curves? */ +/* */ +/* ADDITIONAL CONTRIBUTORS */ +/* */ +/* Mikko Mononen: compound shape support, more cmap formats */ +/* Tor Andersson: kerning, subpixel rendering */ +/* Dougall Johnson: OpenType / Type 2 font handling */ +/* Daniel Ribeiro Maciel: basic GPOS-based kerning */ +/* */ +/* Misc other: */ +/* Ryan Gordon */ +/* Simon Glass */ +/* github:IntellectualKitty */ +/* Imanol Celaya */ +/* Daniel Ribeiro Maciel */ +/* */ +/* Bug/warning reports/fixes: */ +/* "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe */ +/* Cass Everitt Martins Mozeiko github:aloucks */ +/* stoiko (Haemimont Games) Cap Petschulat github:oyvindjam */ +/* Brian Hook Omar Cornut github:vassvik */ +/* Walter van Niftrik Ryan Griege */ +/* David Gow Peter LaValle */ +/* David Given Sergey Popov */ +/* Ivan-Assen Ivanov Giumo X. Clanjor */ +/* Anthony Pesch Higor Euripedes */ +/* Johan Duparc Thomas Fields */ +/* Hou Qiming Derek Vinyard */ +/* Rob Loach Cort Stratton */ +/* Kenney Phillis Jr. Brian Costabile */ +/* Ken Voskuil (kaesve) */ +/* */ +/* VERSION HISTORY */ +/* */ +/* 1.24 (2020-02-05) fix warning */ +/* 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) */ +/* 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined */ +/* 1.21 (2019-02-25) fix warning */ +/* 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() */ +/* 1.19 (2018-02-11) GPOS kerning, STBTT_fmod */ +/* 1.18 (2018-01-29) add missing function */ +/* 1.17 (2017-07-23) make more arguments const; doc fix */ +/* 1.16 (2017-07-12) SDF support */ +/* 1.15 (2017-03-03) make more arguments const */ +/* 1.14 (2017-01-16) num-fonts-in-TTC function */ +/* 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts */ +/* 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual */ +/* 1.11 (2016-04-02) fix unused-variable warning */ +/* 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef */ +/* 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly */ +/* 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges */ +/* 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; */ +/* variant PackFontRanges to pack and render in separate phases; */ +/* fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); */ +/* fixed an assert() bug in the new rasterizer */ +/* replace assert() with STBTT_assert() in new rasterizer */ +/* */ +/* Full history can be found at the end of this file. */ +/* */ +/* LICENSE */ +/* */ +/* See end of file for license information. */ +/* */ +/* USAGE */ +/* */ +/* Include this file in whatever places need to refer to it. In ONE C/C++ */ +/* file, write: */ +/* #define STB_TRUETYPE_IMPLEMENTATION */ +/* before the #include of this file. This expands out the actual */ +/* implementation into that C/C++ file. */ +/* */ +/* To make the implementation private to the file that generates the implementation, */ +/* #define STBTT_STATIC */ +/* */ +/* Simple 3D API (don't ship this, but it's fine for tools and quick start) */ +/* stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture */ +/* stbtt_GetBakedQuad() -- compute quad to draw for a given char */ +/* */ +/* Improved 3D API (more shippable): */ +/* #include "stb_rect_pack.h" -- optional, but you really want it */ +/* stbtt_PackBegin() */ +/* stbtt_PackSetOversampling() -- for improved quality on small fonts */ +/* stbtt_PackFontRanges() -- pack and renders */ +/* stbtt_PackEnd() */ +/* stbtt_GetPackedQuad() */ +/* */ +/* "Load" a font file from a memory buffer (you have to keep the buffer loaded) */ +/* stbtt_InitFont() */ +/* stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections */ +/* stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections */ +/* */ +/* Render a unicode codepoint to a bitmap */ +/* stbtt_GetCodepointBitmap() -- allocates and returns a bitmap */ +/* stbtt_MakeCodepointBitmap() -- renders into bitmap you provide */ +/* stbtt_GetCodepointBitmapBox() -- how big the bitmap must be */ +/* */ +/* Character advance/positioning */ +/* stbtt_GetCodepointHMetrics() */ +/* stbtt_GetFontVMetrics() */ +/* stbtt_GetFontVMetricsOS2() */ +/* stbtt_GetCodepointKernAdvance() */ +/* */ +/* Starting with version 1.06, the rasterizer was replaced with a new, */ +/* faster and generally-more-precise rasterizer. The new rasterizer more */ +/* accurately measures pixel coverage for anti-aliasing, except in the case */ +/* where multiple shapes overlap, in which case it overestimates the AA pixel */ +/* coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If */ +/* this turns out to be a problem, you can re-enable the old rasterizer with */ +/* #define STBTT_RASTERIZER_VERSION 1 */ +/* which will incur about a 15% speed hit. */ +/* */ +/* ADDITIONAL DOCUMENTATION */ +/* */ +/* Immediately after this block comment are a series of sample programs. */ +/* */ +/* After the sample programs is the "header file" section. This section */ +/* includes documentation for each API function. */ +/* */ +/* Some important concepts to understand to use this library: */ +/* */ +/* Codepoint */ +/* Characters are defined by unicode codepoints, e.g. 65 is */ +/* uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is */ +/* the hiragana for "ma". */ +/* */ +/* Glyph */ +/* A visual character shape (every codepoint is rendered as */ +/* some glyph) */ +/* */ +/* Glyph index */ +/* A font-specific integer ID representing a glyph */ +/* */ +/* Baseline */ +/* Glyph shapes are defined relative to a baseline, which is the */ +/* bottom of uppercase characters. Characters extend both above */ +/* and below the baseline. */ +/* */ +/* Current Point */ +/* As you draw text to the screen, you keep track of a "current point" */ +/* which is the origin of each character. The current point's vertical */ +/* position is the baseline. Even "baked fonts" use this model. */ +/* */ +/* Vertical Font Metrics */ +/* The vertical qualities of the font, used to vertically position */ +/* and space the characters. See docs for stbtt_GetFontVMetrics. */ +/* */ +/* Font Size in Pixels or Points */ +/* The preferred interface for specifying font sizes in stb_truetype */ +/* is to specify how tall the font's vertical extent should be in pixels. */ +/* If that sounds good enough, skip the next paragraph. */ +/* */ +/* Most font APIs instead use "points", which are a common typographic */ +/* measurement for describing font size, defined as 72 points per inch. */ +/* stb_truetype provides a point API for compatibility. However, true */ +/* "per inch" conventions don't make much sense on computer displays */ +/* since different monitors have different number of pixels per */ +/* inch. For example, Windows traditionally uses a convention that */ +/* there are 96 pixels per inch, thus making 'inch' measurements have */ +/* nothing to do with inches, and thus effectively defining a point to */ +/* be 1.333 pixels. Additionally, the TrueType font data provides */ +/* an explicit scale factor to scale a given font's glyphs to points, */ +/* but the author has observed that this scale factor is often wrong */ +/* for non-commercial fonts, thus making fonts scaled in points */ +/* according to the TrueType spec incoherently sized in practice. */ +/* */ +/* DETAILED USAGE: */ +/* */ +/* Scale: */ +/* Select how high you want the font to be, in points or pixels. */ +/* Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute */ +/* a scale factor SF that will be used by all other functions. */ +/* */ +/* Baseline: */ +/* You need to select a y-coordinate that is the baseline of where */ +/* your text will appear. Call GetFontBoundingBox to get the baseline-relative */ +/* bounding box for all characters. SF*-y0 will be the distance in pixels */ +/* that the worst-case character could extend above the baseline, so if */ +/* you want the top edge of characters to appear at the top of the */ +/* screen where y=0, then you would set the baseline to SF*-y0. */ +/* */ +/* Current point: */ +/* Set the current point where the first character will appear. The */ +/* first character could extend left of the current point; this is font */ +/* dependent. You can either choose a current point that is the leftmost */ +/* point and hope, or add some padding, or check the bounding box or */ +/* left-side-bearing of the first character to be displayed and set */ +/* the current point based on that. */ +/* */ +/* Displaying a character: */ +/* Compute the bounding box of the character. It will contain signed values */ +/* relative to . I.e. if it returns x0,y0,x1,y1, */ +/* then the character should be displayed in the rectangle from */ +/* to = 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);/* 1=opengl & d3d10+,0=d3d9 */ + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +/* */ +/* */ +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* Complete program (this compiles): get a single bitmap, print as ASCII art */ +/* */ +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION /* force following include to generate implementation */ +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +/* */ +/* Output: */ +/* */ +/* .ii. */ +/* @@@@@@. */ +/* V@Mio@@o */ +/* :i. V@V */ +/* :oM@@M */ +/* :@@@MM@M */ +/* @@o o@M */ +/* :@@. M@M */ +/* @@@o@@@@ */ +/* :M@@V:@@. */ +/* */ +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* Complete program: print "Hello World!" banner, with bugs */ +/* */ +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=2; /* leave a little padding in case the character extends left */ + char *text = "Heljo World!"; /* intentionally misspelled to show 'lj' brokenness */ + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + /* note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong */ + /* because this API is really for baking character bitmaps into textures. if you want to render */ + /* a sequence of characters, you really need to render each bitmap to a temp buffer, then */ + /* "alpha blend" that into the working buffer */ + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +/* //////////////////////////////////////////////////////////////////////////// */ +/* //////////////////////////////////////////////////////////////////////////// */ +/* // */ +/* // INTEGRATION WITH YOUR CODEBASE */ +/* // */ +/* // The following sections allow you to supply alternate definitions */ +/* // of C library functions used by stb_truetype, e.g. if you don't */ +/* // link with the C runtime library. */ + +#ifdef STB_TRUETYPE_IMPLEMENTATION + /* #define your own (u)stbtt_int8/16/32 before including to override this */ + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + /* e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h */ + #ifndef STBTT_ifloor + #include + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + #ifndef STBTT_sqrt + #include + #define STBTT_sqrt(x) sqrt(x) + #define STBTT_pow(x,y) pow(x,y) + #endif + + #ifndef STBTT_fmod + #include + #define STBTT_fmod(x,y) fmod(x,y) + #endif + + #ifndef STBTT_cos + #include + #define STBTT_cos(x) cos(x) + #define STBTT_acos(x) acos(x) + #endif + + #ifndef STBTT_fabs + #include + #define STBTT_fabs(x) fabs(x) + #endif + + /* #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h */ + #ifndef STBTT_malloc + #include + #define STBTT_malloc(x,u) ((void)(u),malloc(x)) + #define STBTT_free(x,u) ((void)(u),free(x)) + #endif + + #ifndef STBTT_assert + #include + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +#endif + +/* ///////////////////////////////////////////////////////////////////////////// */ +/* ///////////////////////////////////////////////////////////////////////////// */ +/* // */ +/* // INTERFACE */ +/* // */ +/* // */ + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef STBTT_STATIC +#define STBTT_DEF static +#else +#define STBTT_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* private structure */ +typedef struct +{ + unsigned char *data; + int cursor; + int size; +} stbtt__buf; + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* TEXTURE BAKING API */ +/* */ +/* If you use this API, you only have to call two functions ever. */ +/* */ + +typedef struct +{ + unsigned short x0,y0,x1,y1; /* coordinates of bbox in bitmap */ + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, /* font location (use offset=0 for plain .ttf) */ + float pixel_height, /* height of font in pixels */ + unsigned char *pixels, int pw, int ph, /* bitmap to be filled in */ + int first_char, int num_chars, /* characters to bake */ + stbtt_bakedchar *chardata); /* you allocate this, it's num_chars long */ +/* if return is positive, the first unused row of the bitmap */ +/* if return is negative, returns the negative of the number of characters that fit */ +/* if return is 0, no characters fit and no rows were used */ +/* This uses a very crappy packing. */ + +typedef struct +{ + float x0,y0,s0,t0; /* top-left */ + float x1,y1,s1,t1; /* bottom-right */ +} stbtt_aligned_quad; + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, /* same data as above */ + int char_index, /* character to display */ + float *xpos, float *ypos, /* pointers to current position in screen pixel space */ + stbtt_aligned_quad *q, /* output: quad to draw */ + int opengl_fillrule); /* true if opengl fill rule; false if DX9 or earlier */ +/* Call GetBakedQuad with char_index = 'character - first_char', and it */ +/* creates the quad you need to draw and advances the current position. */ +/* */ +/* The coordinate system used assumes y increases downwards. */ +/* */ +/* Characters will extend both above and below the current position; */ +/* see discussion of "BASELINE" above. */ +/* */ +/* It's inefficient; you might want to c&p it and optimize it. */ + +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap); +/* Query the font vertical metrics without having to create a font first. */ + + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* NEW TEXTURE BAKING API */ +/* */ +/* This provides options for packing multiple fonts into one atlas, not */ +/* perfectly but better than nothing. */ + +typedef struct +{ + unsigned short x0,y0,x1,y1; /* coordinates of bbox in bitmap */ + float xoff,yoff,xadvance; + float xoff2,yoff2; +} stbtt_packedchar; + +typedef struct stbtt_pack_context stbtt_pack_context; +typedef struct stbtt_fontinfo stbtt_fontinfo; +#ifndef STB_RECT_PACK_VERSION +typedef struct stbrp_rect stbrp_rect; +#endif + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); +/* Initializes a packing context stored in the passed-in stbtt_pack_context. */ +/* Future calls using this context will pack characters into the bitmap passed */ +/* in here: a 1-channel bitmap that is width * height. stride_in_bytes is */ +/* the distance from one row to the next (or 0 to mean they are packed tightly */ +/* together). "padding" is the amount of padding to leave between each */ +/* character (normally you want '1' for bitmaps you'll use as textures with */ +/* bilinear filtering). */ +/* */ +/* Returns 0 on failure, 1 on success. */ + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); +/* Cleans up the packing context and frees all memory. */ + +#define STBTT_POINT_SIZE(x) (-(x)) + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); +/* Creates character bitmaps from the font_index'th font found in fontdata (use */ +/* font_index=0 if you don't know what that is). It creates num_chars_in_range */ +/* bitmaps for characters with unicode values starting at first_unicode_char_in_range */ +/* and increasing. Data for how to render them is stored in chardata_for_range; */ +/* pass these to stbtt_GetPackedQuad to get back renderable quads. */ +/* */ +/* font_size is the full height of the character from ascender to descender, */ +/* as computed by stbtt_ScaleForPixelHeight. To use a point size as computed */ +/* by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() */ +/* and pass that result as 'font_size': */ +/* ..., 20 , ... // font max minus min y is 20 pixels tall */ +/* ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall */ + +typedef struct +{ + float font_size; + int first_unicode_codepoint_in_range; /* if non-zero, then the chars are continuous, and this is the first codepoint */ + int *array_of_unicode_codepoints; /* if non-zero, then this is an array of unicode codepoints */ + int num_chars; + stbtt_packedchar *chardata_for_range; /* output */ + unsigned char h_oversample, v_oversample; /* don't set these, they're used internally */ +} stbtt_pack_range; + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +/* Creates character bitmaps from multiple ranges of characters stored in */ +/* ranges. This will usually create a better-packed bitmap than multiple */ +/* calls to stbtt_PackFontRange. Note that you can call this multiple */ +/* times within a single PackBegin/PackEnd. */ + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); +/* Oversampling a font increases the quality by allowing higher-quality subpixel */ +/* positioning, and is especially valuable at smaller text sizes. */ +/* */ +/* This function sets the amount of oversampling for all following calls to */ +/* stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given */ +/* pack context. The default (no oversampling) is achieved by h_oversample=1 */ +/* and v_oversample=1. The total number of pixels required is */ +/* h_oversample*v_oversample larger than the default; for example, 2x2 */ +/* oversampling requires 4x the storage of 1x1. For best results, render */ +/* oversampled textures with bilinear filtering. Look at the readme in */ +/* stb/tests/oversample for information about oversampled fonts */ +/* */ +/* To use with PackFontRangesGather etc., you must set it before calls */ +/* call to PackFontRangesGatherRects. */ + +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip); +/* If skip != 0, this tells stb_truetype to skip any codepoints for which */ +/* there is no corresponding glyph. If skip=0, which is the default, then */ +/* codepoints without a glyph recived the font's "missing character" glyph, */ +/* typically an empty box by convention. */ + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, /* same data as above */ + int char_index, /* character to display */ + float *xpos, float *ypos, /* pointers to current position in screen pixel space */ + stbtt_aligned_quad *q, /* output: quad to draw */ + int align_to_integer); + +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +/* Calling these functions in sequence is roughly equivalent to calling */ +/* stbtt_PackFontRanges(). If you more control over the packing of multiple */ +/* fonts, or if you want to pack custom data into a font texture, take a look */ +/* at the source to of stbtt_PackFontRanges() and create a custom version */ +/* using these functions, e.g. call GatherRects multiple times, */ +/* building up a single array of rects, then call PackRects once, */ +/* then call RenderIntoRects repeatedly. This may result in a */ +/* better packing than calling PackFontRanges multiple times */ +/* (or it may not). */ + +/* this is an opaque structure that you shouldn't mess with which holds */ +/* all the context needed from PackBegin to PackEnd. */ +struct stbtt_pack_context { + void *user_allocator_context; + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + int skip_missing; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* FONT LOADING */ +/* */ +/* */ + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data); +/* This function will determine the number of fonts in a font file. TrueType */ +/* collection (.ttc) files may contain multiple fonts, while TrueType font */ +/* (.ttf) files only contain one font. The number of fonts can be used for */ +/* indexing with the previous function where the index is between zero and one */ +/* less than the total fonts. If an error occurs, -1 is returned. */ + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +/* Each .ttf/.ttc file may have more than one font. Each font has a sequential */ +/* index number starting from 0. Call this function to get the font offset for */ +/* a given index; it returns -1 if the index is out of range. A regular .ttf */ +/* file will only define one font and it always be at offset 0, so it will */ +/* return '0' for index 0, and -1 for all other indices. */ + +/* The following structure is defined publicly so you can declare one on */ +/* the stack or as a global or etc, but you should treat it as opaque. */ +struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; /* pointer to .ttf file */ + int fontstart; /* offset of start of font */ + + int numGlyphs; /* number of glyphs, needed for range checking */ + + int loca,head,glyf,hhea,hmtx,kern,gpos,svg; /* table locations as offset from start of .ttf */ + int index_map; /* a cmap mapping for our chosen character encoding */ + int indexToLocFormat; /* format needed to map from glyph index to glyph */ + + stbtt__buf cff; /* cff font data */ + stbtt__buf charstrings; /* the charstring index */ + stbtt__buf gsubrs; /* global charstring subroutines index */ + stbtt__buf subrs; /* private charstring subroutines index */ + stbtt__buf fontdicts; /* array of font dicts */ + stbtt__buf fdselect; /* map from glyph to fontdict */ +}; + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +/* Given an offset into the file that defines a font, this function builds */ +/* the necessary cached info for the rest of the system. You must allocate */ +/* the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't */ +/* need to do anything special to free it, because the contents are pure */ +/* value data with no additional data structures. Returns 0 on failure. */ + + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* CHARACTER TO GLYPH-INDEX CONVERSIOn */ + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +/* If you're going to perform multiple operations on the same character */ +/* and you want a speed-up, call this function with the character you're */ +/* going to process, then use glyph-based functions instead of the */ +/* codepoint-based functions. */ +/* Returns 0 if the character codepoint is not defined in the font. */ + + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* CHARACTER PROPERTIES */ +/* */ + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +/* computes a scale factor to produce a font whose "height" is 'pixels' tall. */ +/* Height is measured as the distance from the highest ascender to the lowest */ +/* descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics */ +/* and computing: */ +/* scale = pixels / (ascent - descent) */ +/* so if you prefer to measure height by the ascent only, use a similar calculation. */ + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +/* computes a scale factor to produce a font whose EM size is mapped to */ +/* 'pixels' tall. This is probably what traditional APIs compute, but */ +/* I'm not positive. */ + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +/* ascent is the coordinate above the baseline the font extends; descent */ +/* is the coordinate below the baseline the font extends (i.e. it is typically negative) */ +/* lineGap is the spacing between one row's descent and the next row's ascent... */ +/* so you should advance the vertical position by "*ascent - *descent + *lineGap" */ +/* these are expressed in unscaled coordinates, so you must multiply by */ +/* the scale factor for a given size */ + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap); +/* analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2 */ +/* table (specific to MS/Windows TTF files). */ +/* */ +/* Returns 1 on success (table present), 0 on failure. */ + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +/* the bounding box around all possible characters */ + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +/* leftSideBearing is the offset from the current horizontal position to the left edge of the character */ +/* advanceWidth is the offset from the current horizontal position to the next horizontal position */ +/* these are expressed in unscaled coordinates */ + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +/* an additional amount to add to the 'advance' value between ch1 and ch2 */ + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +/* Gets the bounding box of the visible part of the glyph, in unscaled coordinates */ + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +/* as above, but takes one or more glyph indices for greater efficiency */ + +typedef struct stbtt_kerningentry +{ + int glyph1; /* use stbtt_FindGlyphIndex */ + int glyph2; + int advance; +} stbtt_kerningentry; + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info); +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length); +/* Retrieves a complete list of all of the kerning pairs provided by the font */ +/* stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write. */ +/* The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1) */ + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* GLYPH SHAPES (you probably don't need these, but they have to go before */ +/* the bitmaps for C declaration-order reasons) */ +/* */ + +#ifndef STBTT_vmove /* you can predefine these to use different values (but why?) */ + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve, + STBTT_vcubic + }; +#endif + +#ifndef stbtt_vertex /* you can predefine this to use different values */ + /* (we share this with other code at RAD) */ + #define stbtt_vertex_type short /* can't use stbtt_int16 because that's not visible in the header file */ + typedef struct + { + stbtt_vertex_type x,y,cx,cy,cx1,cy1; + unsigned char type,padding; + } stbtt_vertex; +#endif + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +/* returns non-zero if nothing is drawn for this glyph */ + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +/* returns # of vertices and fills *vertices with the pointer to them */ +/* these are expressed in "unscaled" coordinates */ +/* */ +/* The shape is a series of contours. Each one starts with */ +/* a STBTT_moveto, then consists of a series of mixed */ +/* STBTT_lineto and STBTT_curveto segments. A lineto */ +/* draws a line from previous endpoint to its x,y; a curveto */ +/* draws a quadratic bezier from previous endpoint to */ +/* its x,y, using cx,cy as the bezier control point. */ + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +/* frees the data allocated above */ + +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); +/* fills svg with the character's SVG data. */ +/* returns data size or 0 if SVG not found. */ + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* BITMAP RENDERING */ +/* */ + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +/* frees the bitmap allocated below */ + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +/* allocates a large-enough single-channel 8bpp bitmap and renders the */ +/* specified character/glyph at the specified scale into it, with */ +/* antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). */ +/* *width & *height are filled out with the width & height of the bitmap, */ +/* which is stored left-to-right, top-to-bottom. */ +/* */ +/* xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap */ + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +/* the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel */ +/* shift for the character */ + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +/* the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap */ +/* in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap */ +/* is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the */ +/* width and height and positioning info for it first. */ + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +/* same as stbtt_MakeCodepointBitmap, but you can specify a subpixel */ +/* shift for the character */ + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint); +/* same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering */ +/* is performed (see stbtt_PackSetOversampling) */ + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +/* get the bbox of the bitmap centered around the glyph origin; so the */ +/* bitmap width is ix1-ix0, height is iy1-iy0, and location to place */ +/* the bitmap top left is (leftSideBearing*scale,iy0). */ +/* (Note that the bitmap uses y-increases-down, but the shape uses */ +/* y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) */ + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +/* same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel */ +/* shift for the character */ + +/* the following functions are equivalent to the above functions, but operate */ +/* on glyph indices instead of Unicode codepoints (for efficiency) */ +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph); +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + +/* @TODO: don't expose this structure */ +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +/* rasterize a shape with quadratic beziers into a bitmap */ +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, /* 1-channel bitmap to draw into */ + float flatness_in_pixels, /* allowable error of curve in pixels */ + stbtt_vertex *vertices, /* array of vertices defining shape */ + int num_verts, /* number of vertices in above array */ + float scale_x, float scale_y, /* scale applied to input vertices */ + float shift_x, float shift_y, /* translation applied to input vertices */ + int x_off, int y_off, /* another translation applied to input */ + int invert, /* if non-zero, vertically flip shape */ + void *userdata); /* context for to STBTT_MALLOC */ + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* Signed Distance Function (or Field) rendering */ + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); +/* frees the SDF bitmap allocated below */ + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +/* These functions compute a discretized SDF field for a single character, suitable for storing */ +/* in a single-channel texture, sampling with bilinear filtering, and testing against */ +/* larger than some threshold to produce scalable fonts. */ +/* info -- the font */ +/* scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap */ +/* glyph/codepoint -- the character to generate the SDF for */ +/* padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), */ +/* which allows effects like bit outlines */ +/* onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) */ +/* pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) */ +/* if positive, > onedge_value is inside; if negative, < onedge_value is inside */ +/* width,height -- output height & width of the SDF bitmap (including padding) */ +/* xoff,yoff -- output origin of the character */ +/* return value -- a 2D array of bytes 0..255, width*height in size */ +/* */ +/* pixel_dist_scale & onedge_value are a scale & bias that allows you to make */ +/* optimal use of the limited 0..255 for your application, trading off precision */ +/* and special effects. SDF values outside the range 0..255 are clamped to 0..255. */ +/* */ +/* Example: */ +/* scale = stbtt_ScaleForPixelHeight(22) */ +/* padding = 5 */ +/* onedge_value = 180 */ +/* pixel_dist_scale = 180/5.0 = 36.0 */ +/* */ +/* This will create an SDF bitmap in which the character is about 22 pixels */ +/* high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled */ +/* shape, sample the SDF at each pixel and fill the pixel if the SDF value */ +/* is greater than or equal to 180/255. (You'll actually want to antialias, */ +/* which is beyond the scope of this example.) Additionally, you can compute */ +/* offset outlines (e.g. to stroke the character border inside & outside, */ +/* or only outside). For example, to fill outside the character up to 3 SDF */ +/* pixels, you would compare against (180-36.0*3)/255 = 72/255. The above */ +/* choice of variables maps a range from 5 pixels outside the shape to */ +/* 2 pixels inside the shape to 0..255; this is intended primarily for apply */ +/* outside effects only (the interior range is needed to allow proper */ +/* antialiasing of the font at *smaller* sizes) */ +/* */ +/* The function computes the SDF analytically at each SDF pixel, not by e.g. */ +/* building a higher-res bitmap and approximating it. In theory the quality */ +/* should be as high as possible for an SDF of this size & representation, but */ +/* unclear if this is true in practice (perhaps building a higher-res bitmap */ +/* and computing from that can allow drop-out prevention). */ +/* */ +/* The algorithm has not been optimized at all, so expect it to be slow */ +/* if computing lots of characters or very large sizes. */ + + + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* Finding the right font... */ +/* */ +/* You should really just solve this offline, keep your own tables */ +/* of what font is what, and don't try to get it out of the .ttf file. */ +/* That's because getting it out of the .ttf file is really hard, because */ +/* the names in the file can appear in many possible encodings, in many */ +/* possible languages, and e.g. if you need a case-insensitive comparison, */ +/* the details of that depend on the encoding & language in a complex way */ +/* (actually underspecified in truetype, but also gigantic). */ +/* */ +/* But you can use the provided functions in two possible ways: */ +/* stbtt_FindMatchingFont() will use *case-sensitive* comparisons on */ +/* unicode-encoded names to try to find the font you want; */ +/* you can run this before calling stbtt_InitFont() */ +/* */ +/* stbtt_GetFontNameString() lets you get any of the various strings */ +/* from the file yourself and do your own comparisons on them. */ +/* You have to have called stbtt_InitFont() first. */ + + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +/* returns the offset (not index) of the font that matches, or -1 if none */ +/* if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". */ +/* if you use any other flag, use a font name like "Arial"; this checks */ +/* the 'macStyle' header field; i don't know if fonts set this consistently */ +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 /* <= not same as 0, this makes us check the bitfield is 0 */ + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +/* returns 1/0 whether the first string interpreted as utf8 is identical to */ +/* the second string interpreted as big-endian utf16... useful for strings from next func */ + +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +/* returns the string (which may be big-endian double byte, e.g. for unicode) */ +/* and puts the length in bytes in *length. */ +/* */ +/* some of the values for the IDs are below; for more see the truetype spec: */ +/* http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html */ +/* http://www.microsoft.com/typography/otspec/name.htm */ + +enum { /* platformID */ + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { /* encodingID for STBTT_PLATFORM_ID_UNICODE */ + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { /* encodingID for STBTT_PLATFORM_ID_MICROSOFT */ + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { /* encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes */ + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { /* languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... */ + /* problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs */ + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { /* languageID for STBTT_PLATFORM_ID_MAC */ + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __STB_INCLUDE_STB_TRUETYPE_H__ */ + +/* ///////////////////////////////////////////////////////////////////////////// */ +/* ///////////////////////////////////////////////////////////////////////////// */ +/* // */ +/* // IMPLEMENTATION */ +/* // */ +/* // */ + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +#ifndef STBTT_MAX_OVERSAMPLE +#define STBTT_MAX_OVERSAMPLE 8 +#endif + +#if STBTT_MAX_OVERSAMPLE > 255 +#error "STBTT_MAX_OVERSAMPLE cannot be > 255" +#endif + +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +#ifndef STBTT_RASTERIZER_VERSION +#define STBTT_RASTERIZER_VERSION 2 +#endif + +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +/* //////////////////////////////////////////////////////////////////////// */ +/* */ +/* stbtt__buf helpers to parse data from file */ +/* */ + +static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor++]; +} + +static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor]; +} + +static void stbtt__buf_seek(stbtt__buf *b, int o) +{ + STBTT_assert(!(o > b->size || o < 0)); + b->cursor = (o > b->size || o < 0) ? b->size : o; +} + +static void stbtt__buf_skip(stbtt__buf *b, int o) +{ + stbtt__buf_seek(b, b->cursor + o); +} + +static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) +{ + stbtt_uint32 v = 0; + int i; + STBTT_assert(n >= 1 && n <= 4); + for (i = 0; i < n; i++) + v = (v << 8) | stbtt__buf_get8(b); + return v; +} + +static stbtt__buf stbtt__new_buf(const void *p, size_t size) +{ + stbtt__buf r; + STBTT_assert(size < 0x40000000); + r.data = (stbtt_uint8*) p; + r.size = (int) size; + r.cursor = 0; + return r; +} + +#define stbtt__buf_get16(b) stbtt__buf_get((b), 2) +#define stbtt__buf_get32(b) stbtt__buf_get((b), 4) + +static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s) +{ + stbtt__buf r = stbtt__new_buf(NULL, 0); + if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; + r.data = b->data + o; + r.size = s; + return r; +} + +static stbtt__buf stbtt__cff_get_index(stbtt__buf *b) +{ + int count, start, offsize; + start = b->cursor; + count = stbtt__buf_get16(b); + if (count) { + offsize = stbtt__buf_get8(b); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(b, offsize * count); + stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); + } + return stbtt__buf_range(b, start, b->cursor - start); +} + +static stbtt_uint32 stbtt__cff_int(stbtt__buf *b) +{ + int b0 = stbtt__buf_get8(b); + if (b0 >= 32 && b0 <= 246) return b0 - 139; + else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; + else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; + else if (b0 == 28) return stbtt__buf_get16(b); + else if (b0 == 29) return stbtt__buf_get32(b); + STBTT_assert(0); + return 0; +} + +static void stbtt__cff_skip_operand(stbtt__buf *b) { + int v, b0 = stbtt__buf_peek8(b); + STBTT_assert(b0 >= 28); + if (b0 == 30) { + stbtt__buf_skip(b, 1); + while (b->cursor < b->size) { + v = stbtt__buf_get8(b); + if ((v & 0xF) == 0xF || (v >> 4) == 0xF) + break; + } + } else { + stbtt__cff_int(b); + } +} + +static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) +{ + stbtt__buf_seek(b, 0); + while (b->cursor < b->size) { + int start = b->cursor, end, op; + while (stbtt__buf_peek8(b) >= 28) + stbtt__cff_skip_operand(b); + end = b->cursor; + op = stbtt__buf_get8(b); + if (op == 12) op = stbtt__buf_get8(b) | 0x100; + if (op == key) return stbtt__buf_range(b, start, end-start); + } + return stbtt__buf_range(b, 0, 0); +} + +static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out) +{ + int i; + stbtt__buf operands = stbtt__dict_get(b, key); + for (i = 0; i < outcount && operands.cursor < operands.size; i++) + out[i] = stbtt__cff_int(&operands); +} + +static int stbtt__cff_index_count(stbtt__buf *b) +{ + stbtt__buf_seek(b, 0); + return stbtt__buf_get16(b); +} + +static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) +{ + int count, offsize, start, end; + stbtt__buf_seek(&b, 0); + count = stbtt__buf_get16(&b); + offsize = stbtt__buf_get8(&b); + STBTT_assert(i >= 0 && i < count); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(&b, i*offsize); + start = stbtt__buf_get(&b, offsize); + end = stbtt__buf_get(&b, offsize); + return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); +} + +/* //////////////////////////////////////////////////////////////////////// */ +/* */ +/* accessors to parse data from file */ +/* */ + +/* on platforms that don't allow misaligned reads, if we want to allow */ +/* truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE */ + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } +static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(stbtt_uint8 *font) +{ + /* check the version number */ + if (stbtt_tag4(font, '1',0,0,0)) return 1; /* TrueType 1 */ + if (stbtt_tag(font, "typ1")) return 1; /* TrueType with type 1 font -- we don't support this! */ + if (stbtt_tag(font, "OTTO")) return 1; /* OpenType with CFF */ + if (stbtt_tag4(font, 0,1,0,0)) return 1; /* OpenType 1.0 */ + if (stbtt_tag(font, "true")) return 1; /* Apple specification for TrueType fonts */ + return 0; +} + +/* @OPTIMIZE: binary search */ +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index) +{ + /* if it's just a font, there's only one valid index */ + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + /* check if it's a TTC */ + if (stbtt_tag(font_collection, "ttcf")) { + /* version 1? */ + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*4); + } + } + return -1; +} + +static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) +{ + /* if it's just a font, there's only one valid font */ + if (stbtt__isfont(font_collection)) + return 1; + + /* check if it's a TTC */ + if (stbtt_tag(font_collection, "ttcf")) { + /* version 1? */ + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + return ttLONG(font_collection+8); + } + } + return 0; +} + +static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) +{ + stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; + stbtt__buf pdict; + stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); + if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); + pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); + stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); + if (!subrsoff) return stbtt__new_buf(NULL, 0); + stbtt__buf_seek(&cff, private_loc[1]+subrsoff); + return stbtt__cff_get_index(&cff); +} + +/* since most people won't use this, find this table the first time it's needed */ +static int stbtt__get_svg(stbtt_fontinfo *info) +{ + stbtt_uint32 t; + if (info->svg < 0) { + t = stbtt__find_table(info->data, info->fontstart, "SVG "); + if (t) { + stbtt_uint32 offset = ttULONG(info->data + t + 2); + info->svg = t + offset; + } else { + info->svg = 0; + } + } + return info->svg; +} + +static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) +{ + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + info->cff = stbtt__new_buf(NULL, 0); + + cmap = stbtt__find_table(data, fontstart, "cmap"); /* required */ + info->loca = stbtt__find_table(data, fontstart, "loca"); /* required */ + info->head = stbtt__find_table(data, fontstart, "head"); /* required */ + info->glyf = stbtt__find_table(data, fontstart, "glyf"); /* required */ + info->hhea = stbtt__find_table(data, fontstart, "hhea"); /* required */ + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); /* required */ + info->kern = stbtt__find_table(data, fontstart, "kern"); /* not required */ + info->gpos = stbtt__find_table(data, fontstart, "GPOS"); /* not required */ + + if (!cmap || !info->head || !info->hhea || !info->hmtx) + return 0; + if (info->glyf) { + /* required for truetype */ + if (!info->loca) return 0; + } else { + /* initialization for CFF / Type2 fonts (OTF) */ + stbtt__buf b, topdict, topdictidx; + stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; + stbtt_uint32 cff; + + cff = stbtt__find_table(data, fontstart, "CFF "); + if (!cff) return 0; + + info->fontdicts = stbtt__new_buf(NULL, 0); + info->fdselect = stbtt__new_buf(NULL, 0); + + /* @TODO this should use size from table (not 512MB) */ + info->cff = stbtt__new_buf(data+cff, 512*1024*1024); + b = info->cff; + + /* read the header */ + stbtt__buf_skip(&b, 2); + stbtt__buf_seek(&b, stbtt__buf_get8(&b)); /* hdrsize */ + + /* @TODO the name INDEX could list multiple fonts, */ + /* but we just use the first one. */ + stbtt__cff_get_index(&b); /* name INDEX */ + topdictidx = stbtt__cff_get_index(&b); + topdict = stbtt__cff_index_get(topdictidx, 0); + stbtt__cff_get_index(&b); /* string INDEX */ + info->gsubrs = stbtt__cff_get_index(&b); + + stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); + stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); + stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); + stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); + info->subrs = stbtt__get_subrs(b, topdict); + + /* we only support Type 2 charstrings */ + if (cstype != 2) return 0; + if (charstrings == 0) return 0; + + if (fdarrayoff) { + /* looks like a CID font */ + if (!fdselectoff) return 0; + stbtt__buf_seek(&b, fdarrayoff); + info->fontdicts = stbtt__cff_get_index(&b); + info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); + } + + stbtt__buf_seek(&b, charstrings); + info->charstrings = stbtt__cff_get_index(&b); + } + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + info->svg = -1; + + /* find a cmap encoding table we understand *now* to avoid searching */ + /* later. (todo: could make this installable) */ + /* the same regardless of glyph. */ + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + /* find an encoding we understand: */ + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + /* MS/Unicode */ + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + /* Mac/iOS has these */ + /* all the encodingIDs are unicode, so we don't bother to check it */ + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { /* apple byte encoding */ + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); /* @TODO: high-byte mapping for japanese/chinese/korean */ + return 0; + } else if (format == 4) { /* standard mapping for windows fonts: binary search collection of ranges */ + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + + /* do a binary search of the segments */ + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + /* they lie from endCount .. endCount + segCount */ + /* but searchRange is the nearest power of two, so... */ + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + /* now decrement to bias correctly to find smallest */ + search -= 2; + while (entrySelector) { + stbtt_uint16 end; + searchRange >>= 1; + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + { + stbtt_uint16 offset, start; + stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); + + STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + /* Binary search the right group. */ + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); /* rounds down, so low <= mid < high */ + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else /* format == 13 */ + return start_glyph; + } + } + return 0; /* not found */ + } + /* @TODO */ + STBTT_assert(0); + return 0; +} + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + STBTT_assert(!info->cff.size); + + if (glyph_index >= info->numGlyphs) return -1; /* glyph index out of range */ + if (info->indexToLocFormat >= 2) return -1; /* unknown index->glyph map format */ + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; /* if length is 0, return -1 */ +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); + +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + if (info->cff.size) { + stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); + } else { + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + } + return 1; +} + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g; + if (info->cff.size) + return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0; + g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; /* a loose bound on how many vertices we might need */ + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + /* in first pass, we load uninterpreted data into the allocated array */ + /* above, shifted to the end of the array so we won't overwrite it when */ + /* we create our final data starting from the front */ + + off = m - n; /* starting offset for uninterpreted data, regardless of how m ends up being calculated */ + + /* first load flags */ + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + /* now load x coordinates */ + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; /* ??? */ + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + /* now load y coordinates */ + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; /* ??? */ + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + /* now convert them to our format */ + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + /* now start the new one */ + start_off = !(flags & 1); + if (start_off) { + /* if we start off with an off-curve point, then when we need to find a point on the curve */ + /* where we can start, and we need to save some state for when we wraparound. */ + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + /* next point is also a curve point, so interpolate an on-point curve */ + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + /* otherwise just use the next point as our start point */ + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; /* we're using point i+1 as the starting point, so skip it */ + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { /* if it's a curve */ + if (was_off) /* two off-curve control points in a row means interpolate an on-curve midpoint */ + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours < 0) { + /* Compound shapes. */ + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { /* XY values */ + if (flags & 1) { /* shorts */ + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + /* @TODO handle matching point */ + STBTT_assert(0); + } + if (flags & (1<<3)) { /* WE_HAVE_A_SCALE */ + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { /* WE_HAVE_AN_X_AND_YSCALE */ + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { /* WE_HAVE_A_TWO_BY_TWO */ + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + /* Find transformation scales. */ + m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + /* Get indexed glyph. */ + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + /* Transform vertices. */ + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + /* Append vertices. */ + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + /* More components ? */ + more = flags & (1<<5); + } + } else { + /* numberOfCounters == 0, do nothing */ + } + + *pvertices = vertices; + return num_vertices; +} + +typedef struct +{ + int bounds; + int started; + float first_x, first_y; + float x, y; + stbtt_int32 min_x, max_x, min_y, max_y; + + stbtt_vertex *pvertices; + int num_vertices; +} stbtt__csctx; + +#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} + +static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) +{ + if (x > c->max_x || !c->started) c->max_x = x; + if (y > c->max_y || !c->started) c->max_y = y; + if (x < c->min_x || !c->started) c->min_x = x; + if (y < c->min_y || !c->started) c->min_y = y; + c->started = 1; +} + +static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1) +{ + if (c->bounds) { + stbtt__track_vertex(c, x, y); + if (type == STBTT_vcubic) { + stbtt__track_vertex(c, cx, cy); + stbtt__track_vertex(c, cx1, cy1); + } + } else { + stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); + c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1; + c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1; + } + c->num_vertices++; +} + +static void stbtt__csctx_close_shape(stbtt__csctx *ctx) +{ + if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) +{ + stbtt__csctx_close_shape(ctx); + ctx->first_x = ctx->x = ctx->x + dx; + ctx->first_y = ctx->y = ctx->y + dy; + stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) +{ + ctx->x += dx; + ctx->y += dy; + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) +{ + float cx1 = ctx->x + dx1; + float cy1 = ctx->y + dy1; + float cx2 = cx1 + dx2; + float cy2 = cy1 + dy2; + ctx->x = cx2 + dx3; + ctx->y = cy2 + dy3; + stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); +} + +static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) +{ + int count = stbtt__cff_index_count(&idx); + int bias = 107; + if (count >= 33900) + bias = 32768; + else if (count >= 1240) + bias = 1131; + n += bias; + if (n < 0 || n >= count) + return stbtt__new_buf(NULL, 0); + return stbtt__cff_index_get(idx, n); +} + +static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt__buf fdselect = info->fdselect; + int nranges, start, end, v, fmt, fdselector = -1, i; + + stbtt__buf_seek(&fdselect, 0); + fmt = stbtt__buf_get8(&fdselect); + if (fmt == 0) { + /* untested */ + stbtt__buf_skip(&fdselect, glyph_index); + fdselector = stbtt__buf_get8(&fdselect); + } else if (fmt == 3) { + nranges = stbtt__buf_get16(&fdselect); + start = stbtt__buf_get16(&fdselect); + for (i = 0; i < nranges; i++) { + v = stbtt__buf_get8(&fdselect); + end = stbtt__buf_get16(&fdselect); + if (glyph_index >= start && glyph_index < end) { + fdselector = v; + break; + } + start = end; + } + } + if (fdselector == -1) stbtt__new_buf(NULL, 0); + return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); +} + +static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c) +{ + int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; + int has_subrs = 0, clear_stack; + float s[48]; + stbtt__buf subr_stack[10], subrs = info->subrs, b; + float f; + +#define STBTT__CSERR(s) (0) + + /* this currently ignores the initial width value, which isn't needed if we have hmtx */ + b = stbtt__cff_index_get(info->charstrings, glyph_index); + while (b.cursor < b.size) { + i = 0; + clear_stack = 1; + b0 = stbtt__buf_get8(&b); + switch (b0) { + /* @TODO implement hinting */ + case 0x13: /* hintmask */ + case 0x14: /* cntrmask */ + if (in_header) + maskbits += (sp / 2); /* implicit "vstem" */ + in_header = 0; + stbtt__buf_skip(&b, (maskbits + 7) / 8); + break; + + case 0x01: /* hstem */ + case 0x03: /* vstem */ + case 0x12: /* hstemhm */ + case 0x17: /* vstemhm */ + maskbits += (sp / 2); + break; + + case 0x15: /* rmoveto */ + in_header = 0; + if (sp < 2) return STBTT__CSERR("rmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); + break; + case 0x04: /* vmoveto */ + in_header = 0; + if (sp < 1) return STBTT__CSERR("vmoveto stack"); + stbtt__csctx_rmove_to(c, 0, s[sp-1]); + break; + case 0x16: /* hmoveto */ + in_header = 0; + if (sp < 1) return STBTT__CSERR("hmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-1], 0); + break; + + case 0x05: /* rlineto */ + if (sp < 2) return STBTT__CSERR("rlineto stack"); + for (; i + 1 < sp; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + /* hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical */ + /* starting from a different place. */ + + case 0x07: /* vlineto */ + if (sp < 1) return STBTT__CSERR("vlineto stack"); + goto vlineto; + case 0x06: /* hlineto */ + if (sp < 1) return STBTT__CSERR("hlineto stack"); + for (;;) { + if (i >= sp) break; + stbtt__csctx_rline_to(c, s[i], 0); + i++; + vlineto: + if (i >= sp) break; + stbtt__csctx_rline_to(c, 0, s[i]); + i++; + } + break; + + case 0x1F: /* hvcurveto */ + if (sp < 4) return STBTT__CSERR("hvcurveto stack"); + goto hvcurveto; + case 0x1E: /* vhcurveto */ + if (sp < 4) return STBTT__CSERR("vhcurveto stack"); + for (;;) { + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); + i += 4; + hvcurveto: + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); + i += 4; + } + break; + + case 0x08: /* rrcurveto */ + if (sp < 6) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x18: /* rcurveline */ + if (sp < 8) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp - 2; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + case 0x19: /* rlinecurve */ + if (sp < 8) return STBTT__CSERR("rlinecurve stack"); + for (; i + 1 < sp - 6; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x1A: /* vvcurveto */ + case 0x1B: /* hhcurveto */ + if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); + f = 0.0; + if (sp & 1) { f = s[i]; i++; } + for (; i + 3 < sp; i += 4) { + if (b0 == 0x1B) + stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); + else + stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); + f = 0.0; + } + break; + + case 0x0A: /* callsubr */ + if (!has_subrs) { + if (info->fdselect.size) + subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); + has_subrs = 1; + } + /* fallthrough */ + case 0x1D: /* callgsubr */ + if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); + v = (int) s[--sp]; + if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); + subr_stack[subr_stack_height++] = b; + b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); + if (b.size == 0) return STBTT__CSERR("subr not found"); + b.cursor = 0; + clear_stack = 0; + break; + + case 0x0B: /* return */ + if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); + b = subr_stack[--subr_stack_height]; + clear_stack = 0; + break; + + case 0x0E: /* endchar */ + stbtt__csctx_close_shape(c); + return 1; + + case 0x0C: { /* two-byte escape */ + float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; + float dx, dy; + int b1 = stbtt__buf_get8(&b); + switch (b1) { + /* @TODO These "flex" implementations ignore the flex-depth and resolution, */ + /* and always draw beziers. */ + case 0x22: /* hflex */ + if (sp < 7) return STBTT__CSERR("hflex stack"); + dx1 = s[0]; + dx2 = s[1]; + dy2 = s[2]; + dx3 = s[3]; + dx4 = s[4]; + dx5 = s[5]; + dx6 = s[6]; + stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); + break; + + case 0x23: /* flex */ + if (sp < 13) return STBTT__CSERR("flex stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = s[10]; + dy6 = s[11]; + /* fd is s[12] */ + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + case 0x24: /* hflex1 */ + if (sp < 9) return STBTT__CSERR("hflex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dx4 = s[5]; + dx5 = s[6]; + dy5 = s[7]; + dx6 = s[8]; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); + break; + + case 0x25: /* flex1 */ + if (sp < 11) return STBTT__CSERR("flex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = dy6 = s[10]; + dx = dx1+dx2+dx3+dx4+dx5; + dy = dy1+dy2+dy3+dy4+dy5; + if (STBTT_fabs(dx) > STBTT_fabs(dy)) + dy6 = -dy; + else + dx6 = -dx; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + default: + return STBTT__CSERR("unimplemented"); + } + } break; + + default: + if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254)) + return STBTT__CSERR("reserved operator"); + + /* push immediate */ + if (b0 == 255) { + f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000; + } else { + stbtt__buf_skip(&b, -1); + f = (float)(stbtt_int16)stbtt__cff_int(&b); + } + if (sp >= 48) return STBTT__CSERR("push stack overflow"); + s[sp++] = f; + clear_stack = 0; + break; + } + if (clear_stack) sp = 0; + } + return STBTT__CSERR("no endchar"); + +#undef STBTT__CSERR +} + +static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + /* runs the charstring twice, once to count and once to output (to avoid realloc) */ + stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1); + stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0); + if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { + *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata); + output_ctx.pvertices = *pvertices; + if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { + STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices); + return output_ctx.num_vertices; + } + } + *pvertices = NULL; + return 0; +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + stbtt__csctx c = STBTT__CSCTX_INIT(1); + int r = stbtt__run_charstring(info, glyph_index, &c); + if (x0) *x0 = r ? c.min_x : 0; + if (y0) *y0 = r ? c.min_y : 0; + if (x1) *x1 = r ? c.max_x : 0; + if (y1) *y1 = r ? c.max_y : 0; + return r ? c.num_vertices : 0; +} + +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + if (!info->cff.size) + return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); + else + return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); +} + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info) +{ + stbtt_uint8 *data = info->data + info->kern; + + /* we only look at the first table. it must be 'horizontal' and format 0. */ + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) /* number of tables, need at least 1 */ + return 0; + if (ttUSHORT(data+8) != 1) /* horizontal flag must be set in format */ + return 0; + + return ttUSHORT(data+10); +} + +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length) +{ + stbtt_uint8 *data = info->data + info->kern; + int k, length; + + /* we only look at the first table. it must be 'horizontal' and format 0. */ + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) /* number of tables, need at least 1 */ + return 0; + if (ttUSHORT(data+8) != 1) /* horizontal flag must be set in format */ + return 0; + + length = ttUSHORT(data+10); + if (table_length < length) + length = table_length; + + for (k = 0; k < length; k++) + { + table[k].glyph1 = ttUSHORT(data+18+(k*6)); + table[k].glyph2 = ttUSHORT(data+20+(k*6)); + table[k].advance = ttSHORT(data+22+(k*6)); + } + + return length; +} + +static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + /* we only look at the first table. it must be 'horizontal' and format 0. */ + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) /* number of tables, need at least 1 */ + return 0; + if (ttUSHORT(data+8) != 1) /* horizontal flag must be set in format */ + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); /* note: unaligned read */ + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) +{ + stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); + switch(coverageFormat) { + case 1: { + stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); + + /* Binary search. */ + stbtt_int32 l=0, r=glyphCount-1, m; + int straw, needle=glyph; + while (l <= r) { + stbtt_uint8 *glyphArray = coverageTable + 4; + stbtt_uint16 glyphID; + m = (l + r) >> 1; + glyphID = ttUSHORT(glyphArray + 2 * m); + straw = glyphID; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + return m; + } + } + } break; + + case 2: { + stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); + stbtt_uint8 *rangeArray = coverageTable + 4; + + /* Binary search. */ + stbtt_int32 l=0, r=rangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *rangeRecord; + m = (l + r) >> 1; + rangeRecord = rangeArray + 6 * m; + strawStart = ttUSHORT(rangeRecord); + strawEnd = ttUSHORT(rangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else { + stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); + return startCoverageIndex + glyph - strawStart; + } + } + } break; + + default: { + /* There are no other cases. */ + STBTT_assert(0); + } break; + } + + return -1; +} + +static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) +{ + stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); + switch(classDefFormat) + { + case 1: { + stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); + stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); + stbtt_uint8 *classDef1ValueArray = classDefTable + 6; + + if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) + return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); + + classDefTable = classDef1ValueArray + 2 * glyphCount; + } break; + + case 2: { + stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); + stbtt_uint8 *classRangeRecords = classDefTable + 4; + + /* Binary search. */ + stbtt_int32 l=0, r=classRangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *classRangeRecord; + m = (l + r) >> 1; + classRangeRecord = classRangeRecords + 6 * m; + strawStart = ttUSHORT(classRangeRecord); + strawEnd = ttUSHORT(classRangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else + return (stbtt_int32)ttUSHORT(classRangeRecord + 4); + } + + classDefTable = classRangeRecords + 6 * classRangeCount; + } break; + + default: { + /* There are no other cases. */ + STBTT_assert(0); + } break; + } + + return -1; +} + +/* Define to STBTT_assert(x) if you want to break on unimplemented formats. */ +#define STBTT_GPOS_TODO_assert(x) + +static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint16 lookupListOffset; + stbtt_uint8 *lookupList; + stbtt_uint16 lookupCount; + stbtt_uint8 *data; + stbtt_int32 i; + + if (!info->gpos) return 0; + + data = info->data + info->gpos; + + if (ttUSHORT(data+0) != 1) return 0; /* Major version 1 */ + if (ttUSHORT(data+2) != 0) return 0; /* Minor version 0 */ + + lookupListOffset = ttUSHORT(data+8); + lookupList = data + lookupListOffset; + lookupCount = ttUSHORT(lookupList); + + for (i=0; i> 1; + pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; + secondGlyph = ttUSHORT(pairValue); + straw = secondGlyph; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + stbtt_int16 xAdvance = ttSHORT(pairValue + 2); + return xAdvance; + } + } + } break; + + case 2: { + stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); + stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); + + stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); + stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); + int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); + int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); + + stbtt_uint16 class1Count = ttUSHORT(table + 12); + stbtt_uint16 class2Count = ttUSHORT(table + 14); + STBTT_assert(glyph1class < class1Count); + STBTT_assert(glyph2class < class2Count); + + /* TODO: Support more formats. */ + STBTT_GPOS_TODO_assert(valueFormat1 == 4); + if (valueFormat1 != 4) return 0; + STBTT_GPOS_TODO_assert(valueFormat2 == 0); + if (valueFormat2 != 0) return 0; + + if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) { + stbtt_uint8 *class1Records = table + 16; + stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count); + stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class); + return xAdvance; + } + } break; + + default: { + /* There are no other cases. */ + STBTT_assert(0); + break; + }; + } + } + break; + }; + + default: + /* TODO: Implement other stuff. */ + break; + } + } + + return 0; +} + +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) +{ + int xAdvance = 0; + + if (info->gpos) + xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); + else if (info->kern) + xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); + + return xAdvance; +} + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern && !info->gpos) /* if no kerning table, don't waste time looking up both codepoint->glyphs */ + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap) +{ + int tab = stbtt__find_table(info->data, info->fontstart, "OS/2"); + if (!tab) + return 0; + if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68); + if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70); + if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72); + return 1; +} + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl) +{ + int i; + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info); + + int numEntries = ttUSHORT(svg_doc_list); + stbtt_uint8 *svg_docs = svg_doc_list + 2; + + for(i=0; i= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2))) + return svg_doc; + } + return 0; +} + +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg) +{ + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc; + + if (info->svg == 0) + return 0; + + svg_doc = stbtt_FindSVGDoc(info, gl); + if (svg_doc != NULL) { + *svg = (char *) data + info->svg + ttULONG(svg_doc + 4); + return ttULONG(svg_doc + 8); + } else { + return 0; + } +} + +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg) +{ + return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg); +} + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* antialiasing software rasterizer */ +/* */ + +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0=0,y0=0,x1,y1; /* =0 suppresses compiler warning */ + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + /* e.g. space character */ + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + /* move to integral bboxes (treating pixels as little squares, what pixels get touched)? */ + if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); + if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); + if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); + } +} + +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* Rasterizer */ + +typedef struct stbtt__hheap_chunk +{ + struct stbtt__hheap_chunk *next; +} stbtt__hheap_chunk; + +typedef struct stbtt__hheap +{ + struct stbtt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +} stbtt__hheap; + +static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); + if (c == NULL) + return NULL; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk; + } +} + +static void stbtt__hheap_free(stbtt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) +{ + stbtt__hheap_chunk *c = hh->head; + while (c) { + stbtt__hheap_chunk *n = c->next; + STBTT_free(c, userdata); + c = n; + } +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + + +typedef struct stbtt__active_edge +{ + struct stbtt__active_edge *next; + #if STBTT_RASTERIZER_VERSION==1 + int x,dx; + float ey; + int direction; + #elif STBTT_RASTERIZER_VERSION==2 + float fx,fdx,fdy; + float direction; + float sy; + float ey; + #else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" + #endif +} stbtt__active_edge; + +#if STBTT_RASTERIZER_VERSION == 1 +#define STBTT_FIXSHIFT 10 +#define STBTT_FIX (1 << STBTT_FIXSHIFT) +#define STBTT_FIXMASK (STBTT_FIX-1) + +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + if (!z) return z; + + /* round dx down to avoid overshooting */ + if (dxdy < 0) + z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); + else + z->dx = STBTT_ifloor(STBTT_FIX * dxdy); + + z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); /* use z->dx so when we offset later it's by the same amount */ + z->x -= off_x * STBTT_FIX; + + z->ey = e->y1; + z->next = 0; + z->direction = e->invert ? 1 : -1; + return z; +} +#elif STBTT_RASTERIZER_VERSION == 2 +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + /* STBTT_assert(e->y0 <= start_point); */ + if (!z) return z; + z->fdx = dxdy; + z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#if STBTT_RASTERIZER_VERSION == 1 +/* note: this routine clips fills that extend off the edges... ideally this */ +/* wouldn't happen, but it could happen if the truetype glyph bounding boxes */ +/* are wrong, or if the user supplies a too-small bitmap */ +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + /* non-zero winding fill */ + int x0=0, w=0; + + while (e) { + if (w == 0) { + /* if we're currently at zero, we need to record the edge start point */ + x0 = e->x; w += e->direction; + } else { + int x1 = e->x; w += e->direction; + /* if we went to zero, we need to draw */ + if (w == 0) { + int i = x0 >> STBTT_FIXSHIFT; + int j = x1 >> STBTT_FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + /* x0,x1 are the same pixel, so compute combined coverage */ + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); + } else { + if (i >= 0) /* add antialiasing for x0 */ + scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); + else + i = -1; /* clip */ + + if (j < len) /* add antialiasing for x1 */ + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); + else + j = len; /* clip */ + + for (++i; i < j; ++i) /* fill pixels between x0 and x1 */ + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); /* weight per vertical scanline */ + int s; /* vertical subsample index */ + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + /* find center of pixel for this scanline */ + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + /* update all active edges; */ + /* remove all active edges that terminate before the center of this scanline */ + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; /* delete from list */ + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + z->x += z->dx; /* advance to position for current scanline */ + step = &((*step)->next); /* advance through list */ + } + } + + /* resort the list if needed */ + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + /* insert all edges that start before the center of this scanline -- omit ones that also end on this scanline */ + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); + if (z != NULL) { + /* find insertion point */ + if (active == NULL) + active = z; + else if (z->x < active->x) { + /* insert at front */ + z->next = active; + active = z; + } else { + /* find thing to insert AFTER */ + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + /* at this point, p->next->x is NOT < z->x */ + z->next = p->next; + p->next = z; + } + } + } + ++e; + } + + /* now process all active edges in XOR fashion */ + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +#elif STBTT_RASTERIZER_VERSION == 2 + +/* the edge passed in here does not cross the vertical line at x or the vertical line at x+1 */ +/* (i.e. it has already been clipped to those) */ +static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) +{ + if (y0 == y1) return; + STBTT_assert(y0 < y1); + STBTT_assert(e->sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (x0 == x) + STBTT_assert(x1 <= x+1); + else if (x0 == x+1) + STBTT_assert(x1 >= x); + else if (x0 <= x) + STBTT_assert(x1 <= x); + else if (x0 >= x+1) + STBTT_assert(x1 >= x+1); + else + STBTT_assert(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1) + ; + else { + STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); /* coverage = 1 - average x position */ + } +} + +static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + + while (e) { + /* brute force every pixel */ + + /* compute intersection points with top & bottom */ + STBTT_assert(e->ey >= y_top); + + if (e->fdx == 0) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float sy0,sy1; + float dy = e->fdy; + STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); + + /* compute endpoints of line segment clipped to this scanline (if the */ + /* line segment starts on this scanline. x0 is the intersection of the */ + /* line with y_top, but that may be off the line segment. */ + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + sy0 = e->sy; + } else { + x_top = x0; + sy0 = y_top; + } + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + sy1 = e->ey; + } else { + x_bottom = xb; + sy1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { + /* from here on, we don't have to range check x values */ + + if ((int) x_top == (int) x_bottom) { + float height; + /* simple case, only spans one pixel */ + int x = (int) x_top; + height = sy1 - sy0; + STBTT_assert(x >= 0 && x < len); + scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height; + scanline_fill[x] += e->direction * height; /* everything right of this pixel is filled */ + } else { + int x,x1,x2; + float y_crossing, step, sign, area; + /* covers 2+ pixels */ + if (x_top > x_bottom) { + /* flip scanline vertically; signed area is the same */ + float t; + sy0 = y_bottom - (sy0 - y_top); + sy1 = y_bottom - (sy1 - y_top); + t = sy0, sy0 = sy1, sy1 = t; + t = x_bottom, x_bottom = x_top, x_top = t; + dx = -dx; + dy = -dy; + t = x0, x0 = xb, xb = t; + } + + x1 = (int) x_top; + x2 = (int) x_bottom; + /* compute intersection with y axis at x1+1 */ + y_crossing = (x1+1 - x0) * dy + y_top; + + sign = e->direction; + /* area of the rectangle covered from y0..y_crossing */ + area = sign * (y_crossing-sy0); + /* area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) */ + scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2); + + step = sign * dy; + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; + area += step; + } + y_crossing += dy * (x2 - (x1+1)); + + STBTT_assert(STBTT_fabs(area) <= 1.01f); + + scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); + + scanline_fill[x2] += sign * (sy1-sy0); + } + } else { + /* if edge goes outside of box we're drawing, we require */ + /* clipping logic. since this does not match the intended use */ + /* of this library, we use a different, very slow brute */ + /* force implementation */ + int x; + for (x=0; x < len; ++x) { + /* cases: */ + /* */ + /* there can be up to two intersections with the pixel. any intersection */ + /* with left or right edges can be handled by splitting into two (or three) */ + /* regions. intersections with top & bottom do not necessitate case-wise logic. */ + /* */ + /* the old way of doing this found the intersections with the left & right edges, */ + /* then used some simple logic to produce up to three segments in sorted order */ + /* from top-to-bottom. however, this had a problem: if an x edge was epsilon */ + /* across the x border, then the corresponding y position might not be distinct */ + /* from the other y segment, and it might ignored as an empty segment. to avoid */ + /* that, we need to explicitly produce segments based on x positions. */ + + /* rename variables to clearly-defined pairs */ + float y0 = y_top; + float x1 = (float) (x); + float x2 = (float) (x+1); + float x3 = xb; + float y3 = y_bottom; + + /* x = e->x + e->dx * (y-y_top) */ + /* (y-y_top) = (x - e->x) / e->dx */ + /* y = (x - e->x) / e->dx + y_top */ + float y1 = (x - x0) / dx + y_top; + float y2 = (x+1 - x0) / dx + y_top; + + if (x0 < x1 && x3 > x2) { /* three segments descending down-right */ + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x1 && x0 > x2) { /* three segments descending down-left */ + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x1 && x3 > x1) { /* two segments across x, down-right */ + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x3 < x1 && x0 > x1) { /* two segments across x, down-left */ + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x2 && x3 > x2) { /* two segments across x+1, down-right */ + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x2 && x0 > x2) { /* two segments across x+1, down-left */ + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else { /* one segment */ + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); + } + } + } + } + e = e->next; + } +} + +/* directly AA rasterize edges w/o supersampling */ +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + STBTT__NOTUSED(vsubsample); + + if (result->w > 64) + scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); + else + scanline = scanline_data; + + scanline2 = scanline + result->w; + + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) { + /* find center of pixel for this scanline */ + float scan_y_top = y + 0.0f; + float scan_y_bottom = y + 1.0f; + stbtt__active_edge **step = &active; + + STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); + STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); + + /* update all active edges; */ + /* remove all active edges that terminate before the top of this scanline */ + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; /* delete from list */ + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + step = &((*step)->next); /* advance through list */ + } + } + + /* insert all edges that start before the bottom of this scanline */ + while (e->y0 <= scan_y_bottom) { + if (e->y0 != e->y1) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); + if (z != NULL) { + if (j == 0 && off_y != 0) { + if (z->ey < scan_y_top) { + /* this can happen due to subpixel positioning and some kind of fp rounding error i think */ + z->ey = scan_y_top; + } + } + STBTT_assert(z->ey >= scan_y_top); /* if we get really unlucky a tiny bit of an edge can be out of bounds */ + /* insert at front */ + z->next = active; + active = z; + } + } + ++e; + } + + /* now process all active edges */ + if (active) + stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) STBTT_fabs(k)*255 + 0.5f; + m = (int) k; + if (m > 255) m = 255; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + /* advance all the edges */ + step = &active; + while (*step) { + stbtt__active_edge *z = *step; + z->fx += z->fdx; /* advance to position for current scanline */ + step = &((*step)->next); /* advance through list */ + } + + ++y; + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) + +static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + stbtt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + stbtt__edge *b = &p[j-1]; + int c = STBTT__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) +{ + /* threshold for transitioning to insertion sort */ + while (n > 12) { + stbtt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = STBTT__COMPARE(&p[0],&p[m]); + c12 = STBTT__COMPARE(&p[m],&p[n-1]); + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = STBTT__COMPARE(&p[0],&p[n-1]); + /* 0>mid && midn => n; 0 0 */ + /* 0n: 0>n => 0; 0 n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!STBTT__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!STBTT__COMPARE(&p[0], &p[j])) break; + } + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + } + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + stbtt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + stbtt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +static void stbtt__sort_edges(stbtt__edge *p, int n) +{ + stbtt__sort_edges_quicksort(p, n); + stbtt__sort_edges_ins_sort(p, n); +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; +#if STBTT_RASTERIZER_VERSION == 1 + int vsubsample = result->h < 8 ? 15 : 5; +#elif STBTT_RASTERIZER_VERSION == 2 + int vsubsample = 1; +#else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + /* vsubsample should divide 255 evenly; otherwise we won't reach full opacity */ + + /* now we have to blow out the windings into explicit edge lists */ + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); /* add an extra one as a sentinel */ + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + /* skip the edge if horizontal */ + if (p[j].y == p[k].y) + continue; + /* add edge from j to k to the list */ + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; + ++n; + } + } + + /* now sort the edges by their highest point (should snap to integer, and then by x) */ + /* STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); */ + stbtt__sort_edges(e, n); + + /* now, traverse the scanlines and find the intersections on each scanline, use xor winding rule */ + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; /* during first pass, it's unallocated */ + points[n].x = x; + points[n].y = y; +} + +/* tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching */ +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + /* midpoint */ + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + /* versus directly drawn line */ + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) /* 65536 segments on one curve better be enough! */ + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { /* half-pixel error allowed... need to be smaller if AA */ + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) +{ + /* @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough */ + float dx0 = x1-x0; + float dy0 = y1-y0; + float dx1 = x2-x1; + float dy1 = y2-y1; + float dx2 = x3-x2; + float dy2 = y3-y2; + float dx = x3-x0; + float dy = y3-y0; + float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); + float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy); + float flatness_squared = longlen*longlen-shortlen*shortlen; + + if (n > 16) /* 65536 segments on one curve better be enough! */ + return; + + if (flatness_squared > objspace_flatness_squared) { + float x01 = (x0+x1)/2; + float y01 = (y0+y1)/2; + float x12 = (x1+x2)/2; + float y12 = (y1+y2)/2; + float x23 = (x2+x3)/2; + float y23 = (y2+y3)/2; + + float xa = (x01+x12)/2; + float ya = (y01+y12)/2; + float xb = (x12+x23)/2; + float yb = (y12+y23)/2; + + float mx = (xa+xb)/2; + float my = (ya+yb)/2; + + stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x3,y3); + *num_points = *num_points+1; + } +} + +/* returns number of contours */ +static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + /* count how many "moves" there are to get the contour count */ + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + /* make two passes through the points so we don't need to realloc */ + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + /* start the next contour */ + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + case STBTT_vcubic: + stbtt__tesselate_cubic(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].cx1, vertices[i].cy1, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count = 0; + int *winding_lengths = NULL; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) { + STBTT_free(vertices, info->userdata); + return NULL; + } + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + /* now we get the size */ + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; /* in case we error */ + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* bitmap baking */ +/* */ +/* This is SUPER-CRAPPY packing to keep source code small */ + +static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, /* font location (use offset=0 for plain .ttf) */ + float pixel_height, /* height of font in pixels */ + unsigned char *pixels, int pw, int ph, /* bitmap to be filled in */ + int first_char, int num_chars, /* characters to bake */ + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + f.userdata = NULL; + if (!stbtt_InitFont(&f, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw*ph); /* background of 0 around pixels */ + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; /* advance to next row */ + if (y + gh + 1 >= ph) /* check if it fits vertically AFTER potentially moving to next row */ + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 1; + if (y+gh+1 > bottom_y) + bottom_y = y+gh+1; + } + return bottom_y; +} + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* rectangle packing replacement routines if you don't have stb_rect_pack.h */ +/* */ + +#ifndef STB_RECT_PACK_VERSION + +typedef int stbrp_coord; + +/* ////////////////////////////////////////////////////////////////////////////////// */ +/* // */ +/* // */ +/* COMPILER WARNING ?!?!? // */ +/* // */ +/* // */ +/* if you get a compile warning due to these symbols being defined more than // */ +/* once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // */ +/* // */ +/* ////////////////////////////////////////////////////////////////////////////////// */ + +typedef struct +{ + int width,height; + int x,y,bottom_y; +} stbrp_context; + +typedef struct +{ + unsigned char x; +} stbrp_node; + +struct stbrp_rect +{ + stbrp_coord x,y; + int id,w,h,was_packed; +}; + +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) +{ + con->width = pw; + con->height = ph; + con->x = 0; + con->y = 0; + con->bottom_y = 0; + STBTT__NOTUSED(nodes); + STBTT__NOTUSED(num_nodes); +} + +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) +{ + int i; + for (i=0; i < num_rects; ++i) { + if (con->x + rects[i].w > con->width) { + con->x = 0; + con->y = con->bottom_y; + } + if (con->y + rects[i].h > con->height) + break; + rects[i].x = con->x; + rects[i].y = con->y; + rects[i].was_packed = 1; + con->x += rects[i].w; + if (con->y + rects[i].h > con->bottom_y) + con->bottom_y = con->y + rects[i].h; + } + for ( ; i < num_rects; ++i) + rects[i].was_packed = 0; +} +#endif + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* bitmap baking */ +/* */ +/* This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If */ +/* stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. */ + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +{ + stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); + int num_nodes = pw - padding; + stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); + + if (context == NULL || nodes == NULL) { + if (context != NULL) STBTT_free(context, alloc_context); + if (nodes != NULL) STBTT_free(nodes , alloc_context); + return 0; + } + + spc->user_allocator_context = alloc_context; + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + spc->skip_missing = 0; + + stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + + if (pixels) + STBTT_memset(pixels, 0, pw*ph); /* background of 0 around pixels */ + + return 1; +} + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) +{ + STBTT_free(spc->nodes , spc->user_allocator_context); + STBTT_free(spc->pack_info, spc->user_allocator_context); +} + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +{ + STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); + STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); + if (h_oversample <= STBTT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= STBTT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip) +{ + spc->skip_missing = skip; +} + +#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) + +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); /* suppress bogus warning from VS2013 -analyze */ + for (j=0; j < h; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + /* make kernel_width a constant in common cases so compiler can optimize out the divide */ + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < w; ++i) { + STBTT_assert(pixels[i] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i] = (unsigned char) (total / kernel_width); + } + + pixels += stride_in_bytes; + } +} + +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); /* suppress bogus warning from VS2013 -analyze */ + for (j=0; j < w; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + /* make kernel_width a constant in common cases so compiler can optimize out the divide */ + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < h; ++i) { + STBTT_assert(pixels[i*stride_in_bytes] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + + pixels += 1; + } +} + +static float stbtt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + /* The prefilter is a box filter of width "oversample", */ + /* which shifts phase by (oversample - 1)/2 pixels in */ + /* oversampled space. We want to shift in the opposite */ + /* direction to counter this. */ + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +/* rects array must be big enough to accommodate all characters in the given ranges */ +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k; + int missing_glyph_added = 0; + + k=0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + ranges[i].h_oversample = (unsigned char) spc->h_oversample; + ranges[i].v_oversample = (unsigned char) spc->v_oversample; + for (j=0; j < ranges[i].num_chars; ++j) { + int x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) { + rects[k].w = rects[k].h = 0; + } else { + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + if (glyph == 0) + missing_glyph_added = 1; + } + ++k; + } + } + + return k; +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, + output, + out_w - (prefilter_x - 1), + out_h - (prefilter_y - 1), + out_stride, + scale_x, + scale_y, + shift_x, + shift_y, + glyph); + + if (prefilter_x > 1) + stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); + + if (prefilter_y > 1) + stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); + + *sub_x = stbtt__oversample_shift(prefilter_x); + *sub_y = stbtt__oversample_shift(prefilter_y); +} + +/* rects array must be big enough to accommodate all characters in the given ranges */ +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k, missing_glyph = -1, return_value = 1; + + /* save current values */ + int old_h_over = spc->h_oversample; + int old_v_over = spc->v_oversample; + + k = 0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + float recip_h,recip_v,sub_x,sub_y; + spc->h_oversample = ranges[i].h_oversample; + spc->v_oversample = ranges[i].v_oversample; + recip_h = 1.0f / spc->h_oversample; + recip_v = 1.0f / spc->v_oversample; + sub_x = stbtt__oversample_shift(spc->h_oversample); + sub_y = stbtt__oversample_shift(spc->v_oversample); + for (j=0; j < ranges[i].num_chars; ++j) { + stbrp_rect *r = &rects[k]; + if (r->was_packed && r->w != 0 && r->h != 0) { + stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbrp_coord pad = (stbrp_coord) spc->padding; + + /* pad on left and top */ + r->x += pad; + r->y += pad; + r->w -= pad; + r->h -= pad; + stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); + stbtt_GetGlyphBitmapBox(info, glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + &x0,&y0,&x1,&y1); + stbtt_MakeGlyphBitmapSubpixel(info, + spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w - spc->h_oversample+1, + r->h - spc->v_oversample+1, + spc->stride_in_bytes, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + glyph); + + if (spc->h_oversample > 1) + stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->h_oversample); + + if (spc->v_oversample > 1) + stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->v_oversample); + + bc->x0 = (stbtt_int16) r->x; + bc->y0 = (stbtt_int16) r->y; + bc->x1 = (stbtt_int16) (r->x + r->w); + bc->y1 = (stbtt_int16) (r->y + r->h); + bc->xadvance = scale * advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = (x0 + r->w) * recip_h + sub_x; + bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + + if (glyph == 0) + missing_glyph = j; + } else if (spc->skip_missing) { + return_value = 0; + } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { + ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; + } else { + return_value = 0; /* if any fail, report failure */ + } + + ++k; + } + } + + /* restore original values */ + spc->h_oversample = old_h_over; + spc->v_oversample = old_v_over; + + return return_value; +} + +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) +{ + stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); +} + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +{ + stbtt_fontinfo info; + int i,j,n, return_value = 1; + /* stbrp_context *context = (stbrp_context *) spc->pack_info; */ + stbrp_rect *rects; + + /* flag all characters as NOT packed */ + for (i=0; i < num_ranges; ++i) + for (j=0; j < ranges[i].num_chars; ++j) + ranges[i].chardata_for_range[j].x0 = + ranges[i].chardata_for_range[j].y0 = + ranges[i].chardata_for_range[j].x1 = + ranges[i].chardata_for_range[j].y1 = 0; + + n = 0; + for (i=0; i < num_ranges; ++i) + n += ranges[i].num_chars; + + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); + if (rects == NULL) + return 0; + + info.userdata = spc->user_allocator_context; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); + + n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); + + stbtt_PackFontRangesPackRects(spc, rects, n); + + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); + + STBTT_free(rects, spc->user_allocator_context); + return return_value; +} + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) +{ + stbtt_pack_range range; + range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; + range.array_of_unicode_codepoints = NULL; + range.num_chars = num_chars_in_range; + range.chardata_for_range = chardata_for_range; + range.font_size = font_size; + return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); +} + +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap) +{ + int i_ascent, i_descent, i_lineGap; + float scale; + stbtt_fontinfo info; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index)); + scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size); + stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap); + *ascent = (float) i_ascent * scale; + *descent = (float) i_descent * scale; + *lineGap = (float) i_lineGap * scale; +} + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +{ + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_packedchar *b = chardata + char_index; + + if (align_to_integer) { + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* sdf computation */ +/* */ + +#define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) +#define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) + +static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2]) +{ + float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; + float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; + float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; + float roperp = orig[1]*ray[0] - orig[0]*ray[1]; + + float a = q0perp - 2*q1perp + q2perp; + float b = q1perp - q0perp; + float c = q0perp - roperp; + + float s0 = 0., s1 = 0.; + int num_s = 0; + + if (a != 0.0) { + float discr = b*b - a*c; + if (discr > 0.0) { + float rcpna = -1 / a; + float d = (float) STBTT_sqrt(discr); + s0 = (b+d) * rcpna; + s1 = (b-d) * rcpna; + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { + if (num_s == 0) s0 = s1; + ++num_s; + } + } + } else { + /* 2*b*s + c = 0 */ + /* s = -c / (2*b) */ + s0 = c / (-2 * b); + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + } + + if (num_s == 0) + return 0; + else { + float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); + float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; + + float q0d = q0[0]*rayn_x + q0[1]*rayn_y; + float q1d = q1[0]*rayn_x + q1[1]*rayn_y; + float q2d = q2[0]*rayn_x + q2[1]*rayn_y; + float rod = orig[0]*rayn_x + orig[1]*rayn_y; + + float q10d = q1d - q0d; + float q20d = q2d - q0d; + float q0rd = q0d - rod; + + hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; + hits[0][1] = a*s0+b; + + if (num_s > 1) { + hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; + hits[1][1] = a*s1+b; + return 2; + } else { + return 1; + } + } +} + +static int equal(float *a, float *b) +{ + return (a[0] == b[0] && a[1] == b[1]); +} + +static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) +{ + int i; + float orig[2], ray[2] = { 1, 0 }; + float y_frac; + int winding = 0; + + orig[0] = x; + orig[1] = y; + + /* make sure y never passes through a vertex of the shape */ + y_frac = (float) STBTT_fmod(y, 1.0f); + if (y_frac < 0.01f) + y += 0.01f; + else if (y_frac > 0.99f) + y -= 0.01f; + orig[1] = y; + + /* test a ray from (-infinity,y) to (x,y) */ + for (i=0; i < nverts; ++i) { + if (verts[i].type == STBTT_vline) { + int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y; + int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } + if (verts[i].type == STBTT_vcurve) { + int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ; + int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy; + int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ; + int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); + int by = STBTT_max(y0,STBTT_max(y1,y2)); + if (y > ay && y < by && x > ax) { + float q0[2],q1[2],q2[2]; + float hits[2][2]; + q0[0] = (float)x0; + q0[1] = (float)y0; + q1[0] = (float)x1; + q1[1] = (float)y1; + q2[0] = (float)x2; + q2[1] = (float)y2; + if (equal(q0,q1) || equal(q1,q2)) { + x0 = (int)verts[i-1].x; + y0 = (int)verts[i-1].y; + x1 = (int)verts[i ].x; + y1 = (int)verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } else { + int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); + if (num_hits >= 1) + if (hits[0][0] < 0) + winding += (hits[0][1] < 0 ? -1 : 1); + if (num_hits >= 2) + if (hits[1][0] < 0) + winding += (hits[1][1] < 0 ? -1 : 1); + } + } + } + } + return winding; +} + +static float stbtt__cuberoot( float x ) +{ + if (x<0) + return -(float) STBTT_pow(-x,1.0f/3.0f); + else + return (float) STBTT_pow( x,1.0f/3.0f); +} + +/* x^3 + c*x^2 + b*x + a = 0 */ +static int stbtt__solve_cubic(float a, float b, float c, float* r) +{ + float s = -a / 3; + float p = b - a*a / 3; + float q = a * (2*a*a - 9*b) / 27 + c; + float p3 = p*p*p; + float d = q*q + 4*p3 / 27; + if (d >= 0) { + float z = (float) STBTT_sqrt(d); + float u = (-q + z) / 2; + float v = (-q - z) / 2; + u = stbtt__cuberoot(u); + v = stbtt__cuberoot(v); + r[0] = s + u + v; + return 1; + } else { + float u = (float) STBTT_sqrt(-p/3); + float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; /* p3 must be negative, since d is negative */ + float m = (float) STBTT_cos(v); + float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; + r[0] = s + u * 2 * m; + r[1] = s - u * (m + n); + r[2] = s - u * (m - n); + + /* STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? */ + /* STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); */ + /* STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); */ + return 3; + } +} + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + float scale_x = scale, scale_y = scale; + int ix0,iy0,ix1,iy1; + int w,h; + unsigned char *data; + + if (scale == 0) return NULL; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); + + /* if empty, return NULL */ + if (ix0 == ix1 || iy0 == iy1) + return NULL; + + ix0 -= padding; + iy0 -= padding; + ix1 += padding; + iy1 += padding; + + w = (ix1 - ix0); + h = (iy1 - iy0); + + if (width ) *width = w; + if (height) *height = h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + /* invert for y-downwards bitmaps */ + scale_y = -scale_y; + + { + int x,y,i,j; + float *precompute; + stbtt_vertex *verts; + int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); + data = (unsigned char *) STBTT_malloc(w * h, info->userdata); + precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); + + for (i=0,j=num_verts-1; i < num_verts; j=i++) { + if (verts[i].type == STBTT_vline) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; + float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); + precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; + float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; + float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float len2 = bx*bx + by*by; + if (len2 != 0.0f) + precompute[i] = 1.0f / (bx*bx + by*by); + else + precompute[i] = 0.0f; + } else + precompute[i] = 0.0f; + } + + for (y=iy0; y < iy1; ++y) { + for (x=ix0; x < ix1; ++x) { + float val; + float min_dist = 999999.0f; + float sx = (float) x + 0.5f; + float sy = (float) y + 0.5f; + float x_gspace = (sx / scale_x); + float y_gspace = (sy / scale_y); + + int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); /* @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path */ + + for (i=0; i < num_verts; ++i) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + + /* check against every point here rather than inside line/curve primitives -- @TODO: wrong if multiple 'moves' in a row produce a garbage point, and given culling, probably more efficient to do within line/curve */ + float dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + if (verts[i].type == STBTT_vline) { + float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; + + /* coarse culling against bbox */ + /* if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && */ + /* sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) */ + float dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; + STBTT_assert(i != 0); + if (dist < min_dist) { + /* check position along line */ + /* x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) */ + /* minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) */ + float dx = x1-x0, dy = y1-y0; + float px = x0-sx, py = y0-sy; + /* minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy */ + /* derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve */ + float t = -(px*dx + py*dy) / (dx*dx + dy*dy); + if (t >= 0.0f && t <= 1.0f) + min_dist = dist; + } + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; + float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; + float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); + float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); + float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); + float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); + /* coarse culling against bbox to avoid computing cubic unnecessarily */ + if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { + int num=0; + float ax = x1-x0, ay = y1-y0; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float mx = x0 - sx, my = y0 - sy; + float res[3],px,py,t,it; + float a_inv = precompute[i]; + if (a_inv == 0.0) { /* if a_inv is 0, it's 2nd degree so use quadratic formula */ + float a = 3*(ax*bx + ay*by); + float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); + float c = mx*ax+my*ay; + if (a == 0.0) { /* if a is 0, it's linear */ + if (b != 0.0) { + res[num++] = -c/b; + } + } else { + float discriminant = b*b - 4*a*c; + if (discriminant < 0) + num = 0; + else { + float root = (float) STBTT_sqrt(discriminant); + res[0] = (-b - root)/(2*a); + res[1] = (-b + root)/(2*a); + num = 2; /* don't bother distinguishing 1-solution case, as code below will still work */ + } + } + } else { + float b = 3*(ax*bx + ay*by) * a_inv; /* could precompute this as it doesn't depend on sample point */ + float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; + float d = (mx*ax+my*ay) * a_inv; + num = stbtt__solve_cubic(b, c, d, res); + } + if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { + t = res[0], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { + t = res[1], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { + t = res[2], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + } + } + } + if (winding == 0) + min_dist = -min_dist; /* if outside the shape, value is negative */ + val = onedge_value + pixel_dist_scale * min_dist; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + data[(y-iy0)*w+(x-ix0)] = (unsigned char) val; + } + } + STBTT_free(precompute, info->userdata); + STBTT_free(verts, info->userdata); + } + return data; +} + +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +/* //////////////////////////////////////////////////////////////////////////// */ +/* */ +/* font name matching -- recommended not to use this */ +/* */ + +/* check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string */ +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + /* convert utf16 to utf8 and compare the results while converting */ + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; /* plus another 2 below */ + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); +} + +/* returns results in whatever encoding you request... but note that 2-byte encodings */ +/* will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare */ +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + /* find the encoding */ + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + /* is this a Unicode encoding? */ + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8); + stbtt_int32 off = ttUSHORT(fc+loc+10); + + /* check if there's a prefix match */ + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + /* check for target_id+1 immediately following, with same encoding & language */ + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + slen = ttUSHORT(fc+loc+12+8); + off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + /* if nothing immediately following */ + if (matchlen == nlen) + return 1; + } + } + } + + /* @TODO handle other encodings */ + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + /* check italics/bold/underline flags in macStyle... */ + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + /* if we checked the macStyle flags, then just check the family and ignore the subfamily */ + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, + float pixel_height, unsigned char *pixels, int pw, int ph, + int first_char, int num_chars, stbtt_bakedchar *chardata) +{ + return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata); +} + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) +{ + return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); +} + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) +{ + return stbtt_GetNumberOfFonts_internal((unsigned char *) data); +} + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset) +{ + return stbtt_InitFont_internal(info, (unsigned char *) data, offset); +} + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags) +{ + return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags); +} + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2); +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#endif /* STB_TRUETYPE_IMPLEMENTATION */ + + +/* FULL VERSION HISTORY */ +/* */ +/* 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod */ +/* 1.18 (2018-01-29) add missing function */ +/* 1.17 (2017-07-23) make more arguments const; doc fix */ +/* 1.16 (2017-07-12) SDF support */ +/* 1.15 (2017-03-03) make more arguments const */ +/* 1.14 (2017-01-16) num-fonts-in-TTC function */ +/* 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts */ +/* 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual */ +/* 1.11 (2016-04-02) fix unused-variable warning */ +/* 1.10 (2016-04-02) allow user-defined fabs() replacement */ +/* fix memory leak if fontsize=0.0 */ +/* fix warning from duplicate typedef */ +/* 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges */ +/* 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges */ +/* 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; */ +/* allow PackFontRanges to pack and render in separate phases; */ +/* fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); */ +/* fixed an assert() bug in the new rasterizer */ +/* replace assert() with STBTT_assert() in new rasterizer */ +/* 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) */ +/* also more precise AA rasterizer, except if shapes overlap */ +/* remove need for STBTT_sort */ +/* 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC */ +/* 1.04 (2015-04-15) typo in example */ +/* 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes */ +/* 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ */ +/* 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match */ +/* non-oversampled; STBTT_POINT_SIZE for packed case only */ +/* 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling */ +/* 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) */ +/* 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID */ +/* 0.8b (2014-07-07) fix a warning */ +/* 0.8 (2014-05-25) fix a few more warnings */ +/* 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back */ +/* 0.6c (2012-07-24) improve documentation */ +/* 0.6b (2012-07-20) fix a few more warnings */ +/* 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, */ +/* stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty */ +/* 0.5 (2011-12-09) bugfixes: */ +/* subpixel glyph renderer computed wrong bounding box */ +/* first vertex of shape can be off-curve (FreeSans) */ +/* 0.4b (2011-12-03) fixed an error in the font baking example */ +/* 0.4 (2011-12-01) kerning, subpixel rendering (tor) */ +/* bugfixes for: */ +/* codepoint-to-glyph conversion using table fmt=12 */ +/* codepoint-to-glyph conversion using table fmt=4 */ +/* stbtt_GetBakedQuad with non-square texture (Zer) */ +/* updated Hello World! sample to use kerning and subpixel */ +/* fixed some warnings */ +/* 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) */ +/* userdata, malloc-from-userdata, non-zero fill (stb) */ +/* 0.2 (2009-03-11) Fix unsigned/signed char warnings */ +/* 0.1 (2009-03-09) First public release */ +/* */ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ + + + + +#ifdef NK_INCLUDE_FONT_BAKING +/* ------------------------------------------------------------- + * + * RECT PACK + * + * --------------------------------------------------------------*/ + + + +/* + * ============================================================== + * + * TRUETYPE + * + * =============================================================== + */ +#define STBTT_MAX_OVERSAMPLE 8 + + +/* ------------------------------------------------------------- + * + * FONT BAKING + * + * --------------------------------------------------------------*/ +struct nk_font_bake_data { + struct stbtt_fontinfo info; + struct stbrp_rect *rects; + stbtt_pack_range *ranges; + nk_rune range_count; +}; + +struct nk_font_baker { + struct nk_allocator alloc; + struct stbtt_pack_context spc; + struct nk_font_bake_data *build; + stbtt_packedchar *packed_chars; + struct stbrp_rect *rects; + stbtt_pack_range *ranges; +}; + +NK_GLOBAL const nk_size nk_rect_align = NK_ALIGNOF(struct stbrp_rect); +NK_GLOBAL const nk_size nk_range_align = NK_ALIGNOF(stbtt_pack_range); +NK_GLOBAL const nk_size nk_char_align = NK_ALIGNOF(stbtt_packedchar); +NK_GLOBAL const nk_size nk_build_align = NK_ALIGNOF(struct nk_font_bake_data); +NK_GLOBAL const nk_size nk_baker_align = NK_ALIGNOF(struct nk_font_baker); + +NK_INTERN int +nk_range_count(const nk_rune *range) +{ + const nk_rune *iter = range; + NK_ASSERT(range); + if (!range) return 0; + while (*(iter++) != 0); + return (iter == range) ? 0 : (int)((iter - range)/2); +} +NK_INTERN int +nk_range_glyph_count(const nk_rune *range, int count) +{ + int i = 0; + int total_glyphs = 0; + for (i = 0; i < count; ++i) { + int diff; + nk_rune f = range[(i*2)+0]; + nk_rune t = range[(i*2)+1]; + NK_ASSERT(t >= f); + diff = (int)((t - f) + 1); + total_glyphs += diff; + } + return total_glyphs; +} +NK_API const nk_rune* +nk_font_default_glyph_ranges(void) +{ + NK_STORAGE const nk_rune ranges[] = {0x0020, 0x00FF, 0}; + return ranges; +} +NK_API const nk_rune* +nk_font_chinese_glyph_ranges(void) +{ + NK_STORAGE const nk_rune ranges[] = { + 0x0020, 0x00FF, + 0x3000, 0x30FF, + 0x31F0, 0x31FF, + 0xFF00, 0xFFEF, + 0x4e00, 0x9FAF, + 0 + }; + return ranges; +} +NK_API const nk_rune* +nk_font_cyrillic_glyph_ranges(void) +{ + NK_STORAGE const nk_rune ranges[] = { + 0x0020, 0x00FF, + 0x0400, 0x052F, + 0x2DE0, 0x2DFF, + 0xA640, 0xA69F, + 0 + }; + return ranges; +} +NK_API const nk_rune* +nk_font_korean_glyph_ranges(void) +{ + NK_STORAGE const nk_rune ranges[] = { + 0x0020, 0x00FF, + 0x3131, 0x3163, + 0xAC00, 0xD79D, + 0 + }; + return ranges; +} +NK_INTERN void +nk_font_baker_memory(nk_size *temp, int *glyph_count, + struct nk_font_config *config_list, int count) +{ + int range_count = 0; + int total_range_count = 0; + struct nk_font_config *iter, *i; + + NK_ASSERT(config_list); + NK_ASSERT(glyph_count); + if (!config_list) { + *temp = 0; + *glyph_count = 0; + return; + } + *glyph_count = 0; + for (iter = config_list; iter; iter = iter->next) { + i = iter; + do {if (!i->range) iter->range = nk_font_default_glyph_ranges(); + range_count = nk_range_count(i->range); + total_range_count += range_count; + *glyph_count += nk_range_glyph_count(i->range, range_count); + } while ((i = i->n) != iter); + } + *temp = (nk_size)*glyph_count * sizeof(struct stbrp_rect); + *temp += (nk_size)total_range_count * sizeof(stbtt_pack_range); + *temp += (nk_size)*glyph_count * sizeof(stbtt_packedchar); + *temp += (nk_size)count * sizeof(struct nk_font_bake_data); + *temp += sizeof(struct nk_font_baker); + *temp += nk_rect_align + nk_range_align + nk_char_align; + *temp += nk_build_align + nk_baker_align; +} +NK_INTERN struct nk_font_baker* +nk_font_baker(void *memory, int glyph_count, int count, struct nk_allocator *alloc) +{ + struct nk_font_baker *baker; + if (!memory) return 0; + /* setup baker inside a memory block */ + baker = (struct nk_font_baker*)NK_ALIGN_PTR(memory, nk_baker_align); + baker->build = (struct nk_font_bake_data*)NK_ALIGN_PTR((baker + 1), nk_build_align); + baker->packed_chars = (stbtt_packedchar*)NK_ALIGN_PTR((baker->build + count), nk_char_align); + baker->rects = (struct stbrp_rect*)NK_ALIGN_PTR((baker->packed_chars + glyph_count), nk_rect_align); + baker->ranges = (stbtt_pack_range*)NK_ALIGN_PTR((baker->rects + glyph_count), nk_range_align); + baker->alloc = *alloc; + return baker; +} +NK_INTERN int +nk_font_bake_pack(struct nk_font_baker *baker, + nk_size *image_memory, int *width, int *height, struct nk_recti *custom, + const struct nk_font_config *config_list, int count, + struct nk_allocator *alloc) +{ + NK_STORAGE const nk_size max_height = 1024 * 32; + const struct nk_font_config *config_iter, *it; + int total_glyph_count = 0; + int total_range_count = 0; + int range_count = 0; + int i = 0; + + NK_ASSERT(image_memory); + NK_ASSERT(width); + NK_ASSERT(height); + NK_ASSERT(config_list); + NK_ASSERT(count); + NK_ASSERT(alloc); + + if (!image_memory || !width || !height || !config_list || !count) return nk_false; + for (config_iter = config_list; config_iter; config_iter = config_iter->next) { + it = config_iter; + do {range_count = nk_range_count(it->range); + total_range_count += range_count; + total_glyph_count += nk_range_glyph_count(it->range, range_count); + } while ((it = it->n) != config_iter); + } + /* setup font baker from temporary memory */ + for (config_iter = config_list; config_iter; config_iter = config_iter->next) { + struct stbtt_fontinfo *font_info = &baker->build[i++].info; + it = config_iter; + font_info->userdata = alloc; + do {if (!stbtt_InitFont(font_info, (const unsigned char*)it->ttf_blob, 0)) + return nk_false; + } while ((it = it->n) != config_iter); + } + *height = 0; + *width = (total_glyph_count > 1000) ? 1024 : 512; + stbtt_PackBegin(&baker->spc, 0, (int)*width, (int)max_height, 0, 1, alloc); + { + int input_i = 0; + int range_n = 0; + int rect_n = 0; + int char_n = 0; + + if (custom) { + /* pack custom user data first so it will be in the upper left corner*/ + struct stbrp_rect custom_space; + nk_zero(&custom_space, sizeof(custom_space)); + custom_space.w = (stbrp_coord)(custom->w); + custom_space.h = (stbrp_coord)(custom->h); + + stbtt_PackSetOversampling(&baker->spc, 1, 1); + stbrp_pack_rects((struct stbrp_context*)baker->spc.pack_info, &custom_space, 1); + *height = NK_MAX(*height, (int)(custom_space.y + custom_space.h)); + + custom->x = (short)custom_space.x; + custom->y = (short)custom_space.y; + custom->w = (short)custom_space.w; + custom->h = (short)custom_space.h; + } + + /* first font pass: pack all glyphs */ + for (input_i = 0, config_iter = config_list; input_i < count && config_iter; + config_iter = config_iter->next) { + it = config_iter; + do {int n = 0; + int glyph_count; + const nk_rune *in_range; + const struct nk_font_config *cfg = it; + struct nk_font_bake_data *tmp = &baker->build[input_i++]; + + /* count glyphs + ranges in current font */ + glyph_count = 0; range_count = 0; + for (in_range = cfg->range; in_range[0] && in_range[1]; in_range += 2) { + glyph_count += (int)(in_range[1] - in_range[0]) + 1; + range_count++; + } + + /* setup ranges */ + tmp->ranges = baker->ranges + range_n; + tmp->range_count = (nk_rune)range_count; + range_n += range_count; + for (i = 0; i < range_count; ++i) { + in_range = &cfg->range[i * 2]; + tmp->ranges[i].font_size = cfg->size; + tmp->ranges[i].first_unicode_codepoint_in_range = (int)in_range[0]; + tmp->ranges[i].num_chars = (int)(in_range[1]- in_range[0]) + 1; + tmp->ranges[i].chardata_for_range = baker->packed_chars + char_n; + char_n += tmp->ranges[i].num_chars; + } + + /* pack */ + tmp->rects = baker->rects + rect_n; + rect_n += glyph_count; + stbtt_PackSetOversampling(&baker->spc, cfg->oversample_h, cfg->oversample_v); + n = stbtt_PackFontRangesGatherRects(&baker->spc, &tmp->info, + tmp->ranges, (int)tmp->range_count, tmp->rects); + stbrp_pack_rects((struct stbrp_context*)baker->spc.pack_info, tmp->rects, (int)n); + + /* texture height */ + for (i = 0; i < n; ++i) { + if (tmp->rects[i].was_packed) + *height = NK_MAX(*height, tmp->rects[i].y + tmp->rects[i].h); + } + } while ((it = it->n) != config_iter); + } + NK_ASSERT(rect_n == total_glyph_count); + NK_ASSERT(char_n == total_glyph_count); + NK_ASSERT(range_n == total_range_count); + } + *height = (int)nk_round_up_pow2((nk_uint)*height); + *image_memory = (nk_size)(*width) * (nk_size)(*height); + return nk_true; +} +NK_INTERN void +nk_font_bake(struct nk_font_baker *baker, void *image_memory, int width, int height, + struct nk_font_glyph *glyphs, int glyphs_count, + const struct nk_font_config *config_list, int font_count) +{ + int input_i = 0; + nk_rune glyph_n = 0; + const struct nk_font_config *config_iter; + const struct nk_font_config *it; + + NK_ASSERT(image_memory); + NK_ASSERT(width); + NK_ASSERT(height); + NK_ASSERT(config_list); + NK_ASSERT(baker); + NK_ASSERT(font_count); + NK_ASSERT(glyphs_count); + if (!image_memory || !width || !height || !config_list || + !font_count || !glyphs || !glyphs_count) + return; + + /* second font pass: render glyphs */ + nk_zero(image_memory, (nk_size)((nk_size)width * (nk_size)height)); + baker->spc.pixels = (unsigned char*)image_memory; + baker->spc.height = (int)height; + for (input_i = 0, config_iter = config_list; input_i < font_count && config_iter; + config_iter = config_iter->next) { + it = config_iter; + do {const struct nk_font_config *cfg = it; + struct nk_font_bake_data *tmp = &baker->build[input_i++]; + stbtt_PackSetOversampling(&baker->spc, cfg->oversample_h, cfg->oversample_v); + stbtt_PackFontRangesRenderIntoRects(&baker->spc, &tmp->info, tmp->ranges, (int)tmp->range_count, tmp->rects); + } while ((it = it->n) != config_iter); + } stbtt_PackEnd(&baker->spc); + + /* third pass: setup font and glyphs */ + for (input_i = 0, config_iter = config_list; input_i < font_count && config_iter; + config_iter = config_iter->next) { + it = config_iter; + do {nk_size i = 0; + int char_idx = 0; + nk_rune glyph_count = 0; + const struct nk_font_config *cfg = it; + struct nk_font_bake_data *tmp = &baker->build[input_i++]; + struct nk_baked_font *dst_font = cfg->font; + + float font_scale = stbtt_ScaleForPixelHeight(&tmp->info, cfg->size); + int unscaled_ascent, unscaled_descent, unscaled_line_gap; + stbtt_GetFontVMetrics(&tmp->info, &unscaled_ascent, &unscaled_descent, + &unscaled_line_gap); + + /* fill baked font */ + if (!cfg->merge_mode) { + dst_font->ranges = cfg->range; + dst_font->height = cfg->size; + dst_font->ascent = ((float)unscaled_ascent * font_scale); + dst_font->descent = ((float)unscaled_descent * font_scale); + dst_font->glyph_offset = glyph_n; + /* + Need to zero this, or it will carry over from a previous + bake, and cause a segfault when accessing glyphs[]. + */ + dst_font->glyph_count = 0; + } + + /* fill own baked font glyph array */ + for (i = 0; i < tmp->range_count; ++i) { + stbtt_pack_range *range = &tmp->ranges[i]; + for (char_idx = 0; char_idx < range->num_chars; char_idx++) + { + nk_rune codepoint = 0; + float dummy_x = 0, dummy_y = 0; + stbtt_aligned_quad q; + struct nk_font_glyph *glyph; + + /* query glyph bounds from stb_truetype */ + const stbtt_packedchar *pc = &range->chardata_for_range[char_idx]; + if (!pc->x0 && !pc->x1 && !pc->y0 && !pc->y1) continue; + codepoint = (nk_rune)(range->first_unicode_codepoint_in_range + char_idx); + stbtt_GetPackedQuad(range->chardata_for_range, (int)width, + (int)height, char_idx, &dummy_x, &dummy_y, &q, 0); + + /* fill own glyph type with data */ + glyph = &glyphs[dst_font->glyph_offset + dst_font->glyph_count + (unsigned int)glyph_count]; + glyph->codepoint = codepoint; + glyph->x0 = q.x0; glyph->y0 = q.y0; + glyph->x1 = q.x1; glyph->y1 = q.y1; + glyph->y0 += (dst_font->ascent + 0.5f); + glyph->y1 += (dst_font->ascent + 0.5f); + glyph->w = glyph->x1 - glyph->x0 + 0.5f; + glyph->h = glyph->y1 - glyph->y0; + + if (cfg->coord_type == NK_COORD_PIXEL) { + glyph->u0 = q.s0 * (float)width; + glyph->v0 = q.t0 * (float)height; + glyph->u1 = q.s1 * (float)width; + glyph->v1 = q.t1 * (float)height; + } else { + glyph->u0 = q.s0; + glyph->v0 = q.t0; + glyph->u1 = q.s1; + glyph->v1 = q.t1; + } + glyph->xadvance = (pc->xadvance + cfg->spacing.x); + if (cfg->pixel_snap) + glyph->xadvance = (float)(int)(glyph->xadvance + 0.5f); + glyph_count++; + } + } + dst_font->glyph_count += glyph_count; + glyph_n += glyph_count; + } while ((it = it->n) != config_iter); + } +} +NK_INTERN void +nk_font_bake_custom_data(void *img_memory, int img_width, int img_height, + struct nk_recti img_dst, const char *texture_data_mask, int tex_width, + int tex_height, char white, char black) +{ + nk_byte *pixels; + int y = 0; + int x = 0; + int n = 0; + + NK_ASSERT(img_memory); + NK_ASSERT(img_width); + NK_ASSERT(img_height); + NK_ASSERT(texture_data_mask); + NK_UNUSED(tex_height); + if (!img_memory || !img_width || !img_height || !texture_data_mask) + return; + + pixels = (nk_byte*)img_memory; + for (y = 0, n = 0; y < tex_height; ++y) { + for (x = 0; x < tex_width; ++x, ++n) { + const int off0 = ((img_dst.x + x) + (img_dst.y + y) * img_width); + const int off1 = off0 + 1 + tex_width; + pixels[off0] = (texture_data_mask[n] == white) ? 0xFF : 0x00; + pixels[off1] = (texture_data_mask[n] == black) ? 0xFF : 0x00; + } + } +} +NK_INTERN void +nk_font_bake_convert(void *out_memory, int img_width, int img_height, + const void *in_memory) +{ + int n = 0; + nk_rune *dst; + const nk_byte *src; + + NK_ASSERT(out_memory); + NK_ASSERT(in_memory); + NK_ASSERT(img_width); + NK_ASSERT(img_height); + if (!out_memory || !in_memory || !img_height || !img_width) return; + + dst = (nk_rune*)out_memory; + src = (const nk_byte*)in_memory; + for (n = (int)(img_width * img_height); n > 0; n--) + *dst++ = ((nk_rune)(*src++) << 24) | 0x00FFFFFF; +} + +/* ------------------------------------------------------------- + * + * FONT + * + * --------------------------------------------------------------*/ +NK_INTERN float +nk_font_text_width(nk_handle handle, float height, const char *text, int len) +{ + nk_rune unicode; + int text_len = 0; + float text_width = 0; + int glyph_len = 0; + float scale = 0; + + struct nk_font *font = (struct nk_font*)handle.ptr; + NK_ASSERT(font); + NK_ASSERT(font->glyphs); + if (!font || !text || !len) + return 0; + + scale = height/font->info.height; + glyph_len = text_len = nk_utf_decode(text, &unicode, (int)len); + if (!glyph_len) return 0; + while (text_len <= (int)len && glyph_len) { + const struct nk_font_glyph *g; + if (unicode == NK_UTF_INVALID) break; + + /* query currently drawn glyph information */ + g = nk_font_find_glyph(font, unicode); + text_width += g->xadvance * scale; + + /* offset next glyph */ + glyph_len = nk_utf_decode(text + text_len, &unicode, (int)len - text_len); + text_len += glyph_len; + } + return text_width; +} +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +NK_INTERN void +nk_font_query_font_glyph(nk_handle handle, float height, + struct nk_user_font_glyph *glyph, nk_rune codepoint, nk_rune next_codepoint) +{ + float scale; + const struct nk_font_glyph *g; + struct nk_font *font; + + NK_ASSERT(glyph); + NK_UNUSED(next_codepoint); + + font = (struct nk_font*)handle.ptr; + NK_ASSERT(font); + NK_ASSERT(font->glyphs); + if (!font || !glyph) + return; + + scale = height/font->info.height; + g = nk_font_find_glyph(font, codepoint); + glyph->width = (g->x1 - g->x0) * scale; + glyph->height = (g->y1 - g->y0) * scale; + glyph->offset = nk_vec2(g->x0 * scale, g->y0 * scale); + glyph->xadvance = (g->xadvance * scale); + glyph->uv[0] = nk_vec2(g->u0, g->v0); + glyph->uv[1] = nk_vec2(g->u1, g->v1); +} +#endif +NK_API const struct nk_font_glyph* +nk_font_find_glyph(struct nk_font *font, nk_rune unicode) +{ + int i = 0; + int count; + int total_glyphs = 0; + const struct nk_font_glyph *glyph = 0; + const struct nk_font_config *iter = 0; + + NK_ASSERT(font); + NK_ASSERT(font->glyphs); + NK_ASSERT(font->info.ranges); + if (!font || !font->glyphs) return 0; + + glyph = font->fallback; + iter = font->config; + do {count = nk_range_count(iter->range); + for (i = 0; i < count; ++i) { + nk_rune f = iter->range[(i*2)+0]; + nk_rune t = iter->range[(i*2)+1]; + int diff = (int)((t - f) + 1); + if (unicode >= f && unicode <= t) + return &font->glyphs[((nk_rune)total_glyphs + (unicode - f))]; + total_glyphs += diff; + } + } while ((iter = iter->n) != font->config); + return glyph; +} +NK_INTERN void +nk_font_init(struct nk_font *font, float pixel_height, + nk_rune fallback_codepoint, struct nk_font_glyph *glyphs, + const struct nk_baked_font *baked_font, nk_handle atlas) +{ + struct nk_baked_font baked; + NK_ASSERT(font); + NK_ASSERT(glyphs); + NK_ASSERT(baked_font); + if (!font || !glyphs || !baked_font) + return; + + baked = *baked_font; + font->fallback = 0; + font->info = baked; + font->scale = (float)pixel_height / (float)font->info.height; + font->glyphs = &glyphs[baked_font->glyph_offset]; + font->texture = atlas; + font->fallback_codepoint = fallback_codepoint; + font->fallback = nk_font_find_glyph(font, fallback_codepoint); + + font->handle.height = font->info.height * font->scale; + font->handle.width = nk_font_text_width; + font->handle.userdata.ptr = font; +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + font->handle.query = nk_font_query_font_glyph; + font->handle.texture = font->texture; +#endif +} + +/* --------------------------------------------------------------------------- + * + * DEFAULT FONT + * + * ProggyClean.ttf + * Copyright (c) 2004, 2005 Tristan Grimmer + * MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip) + * Download and more information at http://upperbounds.net + *-----------------------------------------------------------------------------*/ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Woverlength-strings" +#elif defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverlength-strings" +#endif + +#ifdef NK_INCLUDE_DEFAULT_FONT + +NK_GLOBAL const char nk_proggy_clean_ttf_compressed_data_base85[11980+1] = + "7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/" + "2*>]b(MC;$jPfY.;h^`IWM9Qo#t'X#(v#Y9w0#1D$CIf;W'#pWUPXOuxXuU(H9M(1=Ke$$'5F%)]0^#0X@U.a$FBjVQTSDgEKnIS7EM9>ZY9w0#L;>>#Mx&4Mvt//L[MkA#W@lK.N'[0#7RL_&#w+F%HtG9M#XL`N&.,GM4Pg;--VsM.M0rJfLH2eTM`*oJMHRC`N" + "kfimM2J,W-jXS:)r0wK#@Fge$U>`w'N7G#$#fB#$E^$#:9:hk+eOe--6x)F7*E%?76%^GMHePW-Z5l'&GiF#$956:rS?dA#fiK:)Yr+`�j@'DbG&#^$PG.Ll+DNa&VZ>1i%h1S9u5o@YaaW$e+bROPOpxTO7Stwi1::iB1q)C_=dV26J;2,]7op$]uQr@_V7$q^%lQwtuHY]=DX,n3L#0PHDO4f9>dC@O>HBuKPpP*E,N+b3L#lpR/MrTEH.IAQk.a>D[.e;mc." + "x]Ip.PH^'/aqUO/$1WxLoW0[iLAw=4h(9.`G" + "CRUxHPeR`5Mjol(dUWxZa(>STrPkrJiWx`5U7F#.g*jrohGg`cg:lSTvEY/EV_7H4Q9[Z%cnv;JQYZ5q.l7Zeas:HOIZOB?Ggv:[7MI2k).'2($5FNP&EQ(,)" + "U]W]+fh18.vsai00);D3@4ku5P?DP8aJt+;qUM]=+b'8@;mViBKx0DE[-auGl8:PJ&Dj+M6OC]O^((##]`0i)drT;-7X`=-H3[igUnPG-NZlo.#k@h#=Ork$m>a>$-?Tm$UV(?#P6YY#" + "'/###xe7q.73rI3*pP/$1>s9)W,JrM7SN]'/4C#v$U`0#V.[0>xQsH$fEmPMgY2u7Kh(G%siIfLSoS+MK2eTM$=5,M8p`A.;_R%#u[K#$x4AG8.kK/HSB==-'Ie/QTtG?-.*^N-4B/ZM" + "_3YlQC7(p7q)&](`6_c)$/*JL(L-^(]$wIM`dPtOdGA,U3:w2M-0+WomX2u7lqM2iEumMTcsF?-aT=Z-97UEnXglEn1K-bnEO`gu" + "Ft(c%=;Am_Qs@jLooI&NX;]0#j4#F14;gl8-GQpgwhrq8'=l_f-b49'UOqkLu7-##oDY2L(te+Mch&gLYtJ,MEtJfLh'x'M=$CS-ZZ%P]8bZ>#S?YY#%Q&q'3^Fw&?D)UDNrocM3A76/" + "/oL?#h7gl85[qW/NDOk%16ij;+:1a'iNIdb-ou8.P*w,v5#EI$TWS>Pot-R*H'-SEpA:g)f+O$%%`kA#G=8RMmG1&O`>to8bC]T&$,n.LoO>29sp3dt-52U%VM#q7'DHpg+#Z9%H[Ket`e;)f#Km8&+DC$I46>#Kr]]u-[=99tts1.qb#q72g1WJO81q+eN'03'eM>&1XxY-caEnO" + "j%2n8)),?ILR5^.Ibn<-X-Mq7[a82Lq:F&#ce+S9wsCK*x`569E8ew'He]h:sI[2LM$[guka3ZRd6:t%IG:;$%YiJ:Nq=?eAw;/:nnDq0(CYcMpG)qLN4$##&J-XTt,%OVU4)S1+R-#dg0/Nn?Ku1^0f$B*P:Rowwm-`0PKjYDDM'3]d39VZHEl4,.j']Pk-M.h^&:0FACm$maq-&sgw0t7/6(^xtk%" + "LuH88Fj-ekm>GA#_>568x6(OFRl-IZp`&b,_P'$MhLbxfc$mj`,O;&%W2m`Zh:/)Uetw:aJ%]K9h:TcF]u_-Sj9,VK3M.*'&0D[Ca]J9gp8,kAW]" + "%(?A%R$f<->Zts'^kn=-^@c4%-pY6qI%J%1IGxfLU9CP8cbPlXv);C=b),<2mOvP8up,UVf3839acAWAW-W?#ao/^#%KYo8fRULNd2.>%m]UK:n%r$'sw]J;5pAoO_#2mO3n,'=H5(et" + "Hg*`+RLgv>=4U8guD$I%D:W>-r5V*%j*W:Kvej.Lp$'?;++O'>()jLR-^u68PHm8ZFWe+ej8h:9r6L*0//c&iH&R8pRbA#Kjm%upV1g:" + "a_#Ur7FuA#(tRh#.Y5K+@?3<-8m0$PEn;J:rh6?I6uG<-`wMU'ircp0LaE_OtlMb&1#6T.#FDKu#1Lw%u%+GM+X'e?YLfjM[VO0MbuFp7;>Q&#WIo)0@F%q7c#4XAXN-U&VBpqB>0ie&jhZ[?iLR@@_AvA-iQC(=ksRZRVp7`.=+NpBC%rh&3]R:8XDmE5^V8O(x<-+k?'(^](H.aREZSi,#1:[IXaZFOm<-ui#qUq2$##Ri;u75OK#(RtaW-K-F`S+cF]uN`-KMQ%rP/Xri.LRcB##=YL3BgM/3M" + "D?@f&1'BW-)Ju#bmmWCMkk&#TR`C,5d>g)F;t,4:@_l8G/5h4vUd%&%950:VXD'QdWoY-F$BtUwmfe$YqL'8(PWX(" + "P?^@Po3$##`MSs?DWBZ/S>+4%>fX,VWv/w'KD`LP5IbH;rTV>n3cEK8U#bX]l-/V+^lj3;vlMb&[5YQ8#pekX9JP3XUC72L,,?+Ni&co7ApnO*5NK,((W-i:$,kp'UDAO(G0Sq7MVjJs" + "bIu)'Z,*[>br5fX^:FPAWr-m2KgLQ_nN6'8uTGT5g)uLv:873UpTLgH+#FgpH'_o1780Ph8KmxQJ8#H72L4@768@Tm&Q" + "h4CB/5OvmA&,Q&QbUoi$a_%3M01H)4x7I^&KQVgtFnV+;[Pc>[m4k//,]1?#`VY[Jr*3&&slRfLiVZJ:]?=K3Sw=[$=uRB?3xk48@aege0jT6'N#(q%.O=?2S]u*(m<-" + "V8J'(1)G][68hW$5'q[GC&5j`TE?m'esFGNRM)j,ffZ?-qx8;->g4t*:CIP/[Qap7/9'#(1sao7w-.qNUdkJ)tCF&#B^;xGvn2r9FEPFFFcL@.iFNkTve$m%#QvQS8U@)2Z+3K:AKM5i" + "sZ88+dKQ)W6>J%CL`.d*(B`-n8D9oK-XV1q['-5k'cAZ69e;D_?$ZPP&s^+7])$*$#@QYi9,5P r+$%CE=68>K8r0=dSC%%(@p7" + ".m7jilQ02'0-VWAgTlGW'b)Tq7VT9q^*^$$.:&N@@" + "$&)WHtPm*5_rO0&e%K&#-30j(E4#'Zb.o/(Tpm$>K'f@[PvFl,hfINTNU6u'0pao7%XUp9]5.>%h`8_=VYbxuel.NTSsJfLacFu3B'lQSu/m6-Oqem8T+oE--$0a/k]uj9EwsG>%veR*" + "hv^BFpQj:K'#SJ,sB-'#](j.Lg92rTw-*n%@/;39rrJF,l#qV%OrtBeC6/,;qB3ebNW[?,Hqj2L.1NP&GjUR=1D8QaS3Up&@*9wP?+lo7b?@%'k4`p0Z$22%K3+iCZj?XJN4Nm&+YF]u" + "@-W$U%VEQ/,,>>#)D#%8cY#YZ?=,`Wdxu/ae&#" + "w6)R89tI#6@s'(6Bf7a&?S=^ZI_kS&ai`&=tE72L_D,;^R)7[$so8lKN%5/$(vdfq7+ebA#" + "u1p]ovUKW&Y%q]'>$1@-[xfn$7ZTp7mM,G,Ko7a&Gu%G[RMxJs[0MM%wci.LFDK)(%:_i2B5CsR8&9Z&#=mPEnm0f`<&c)QL5uJ#%u%lJj+D-r;BoFDoS97h5g)E#o:&S4weDF,9^Hoe`h*L+_a*NrLW-1pG_&2UdB8" + "6e%B/:=>)N4xeW.*wft-;$'58-ESqr#U`'6AQ]m&6/`Z>#S?YY#Vc;r7U2&326d=w&H####?TZ`*4?&.MK?LP8Vxg>$[QXc%QJv92.(Db*B)gb*BM9dM*hJMAo*c&#" + "b0v=Pjer]$gG&JXDf->'StvU7505l9$AFvgYRI^&<^b68?j#q9QX4SM'RO#&sL1IM.rJfLUAj221]d##DW=m83u5;'bYx,*Sl0hL(W;;$doB&O/TQ:(Z^xBdLjLV#*8U_72Lh+2Q8Cj0i:6hp&$C/:p(HK>T8Y[gHQ4`4)'$Ab(Nof%V'8hL&#SfD07&6D@M.*J:;$-rv29'M]8qMv-tLp,'886iaC=Hb*YJoKJ,(j%K=H`K.v9HggqBIiZu'QvBT.#=)0ukruV&.)3=(^1`o*Pj4<-#MJ+gLq9-##@HuZPN0]u:h7.T..G:;$/Usj(T7`Q8tT72LnYl<-qx8;-HV7Q-&Xdx%1a,hC=0u+HlsV>nuIQL-5" + "_>@kXQtMacfD.m-VAb8;IReM3$wf0''hra*so568'Ip&vRs849'MRYSp%:t:h5qSgwpEr$B>Q,;s(C#$)`svQuF$##-D,##,g68@2[T;.XSdN9Qe)rpt._K-#5wF)sP'##p#C0c%-Gb%" + "hd+<-j'Ai*x&&HMkT]C'OSl##5RG[JXaHN;d'uA#x._U;.`PU@(Z3dt4r152@:v,'R.Sj'w#0<-;kPI)FfJ&#AYJ&#//)>-k=m=*XnK$>=)72L]0I%>.G690a:$##<,);?;72#?x9+d;" + "^V'9;jY@;)br#q^YQpx:X#Te$Z^'=-=bGhLf:D6&bNwZ9-ZD#n^9HhLMr5G;']d&6'wYmTFmLq9wI>P(9mI[>kC-ekLC/R&CH+s'B;K-M6$EB%is00:" + "+A4[7xks.LrNk0&E)wILYF@2L'0Nb$+pv<(2.768/FrY&h$^3i&@+G%JT'<-,v`3;_)I9M^AE]CN?Cl2AZg+%4iTpT3$U4O]GKx'm9)b@p7YsvK3w^YR-" + "CdQ*:Ir<($u&)#(&?L9Rg3H)4fiEp^iI9O8KnTj,]H?D*r7'M;PwZ9K0E^k&-cpI;.p/6_vwoFMV<->#%Xi.LxVnrU(4&8/P+:hLSKj$#U%]49t'I:rgMi'FL@a:0Y-uA[39',(vbma*" + "hU%<-SRF`Tt:542R_VV$p@[p8DV[A,?1839FWdFTi1O*H&#(AL8[_P%.M>v^-))qOT*F5Cq0`Ye%+$B6i:7@0IXSsDiWP,##P`%/L-" + "S(qw%sf/@%#B6;/U7K]uZbi^Oc^2n%t<)'mEVE''n`WnJra$^TKvX5B>;_aSEK',(hwa0:i4G?.Bci.(X[?b*($,=-n<.Q%`(X=?+@Am*Js0&=3bh8K]mL69=Lb,OcZV/);TTm8VI;?%OtJ<(b4mq7M6:u?KRdFl*:xP?Yb.5)%w_I?7uk5JC+FS(m#i'k.'a0i)9<7b'fs'59hq$*5Uhv##pi^8+hIEBF`nvo`;'l0.^S1<-wUK2/Coh58KKhLj" + "M=SO*rfO`+qC`W-On.=AJ56>>i2@2LH6A:&5q`?9I3@@'04&p2/LVa*T-4<-i3;M9UvZd+N7>b*eIwg:CC)c<>nO&#$(>.Z-I&J(Q0Hd5Q%7Co-b`-cP)hI;*_F]u`Rb[.j8_Q/<&>uu+VsH$sM9TA%?)(vmJ80),P7E>)tjD%2L=-t#fK[%`v=Q8WlA2);Sa" + ">gXm8YB`1d@K#n]76-a$U,mF%Ul:#/'xoFM9QX-$.QN'>" + "[%$Z$uF6pA6Ki2O5:8w*vP1<-1`[G,)-m#>0`P&#eb#.3i)rtB61(o'$?X3B2Qft^ae_5tKL9MUe9b*sLEQ95C&`=G?@Mj=wh*'3E>=-<)Gt*Iw)'QG:`@I" + "wOf7&]1i'S01B+Ev/Nac#9S;=;YQpg_6U`*kVY39xK,[/6Aj7:'1Bm-_1EYfa1+o&o4hp7KN_Q(OlIo@S%;jVdn0'1h19w,WQhLI)3S#f$2(eb,jr*b;3Vw]*7NH%$c4Vs,eD9>XW8?N]o+(*pgC%/72LV-uW%iewS8W6m2rtCpo'RS1R84=@paTKt)>=%&1[)*vp'u+x,VrwN;&]kuO9JDbg=pO$J*.jVe;u'm0dr9l,<*wMK*Oe=g8lV_KEBFkO'oU]^=[-792#ok,)" + "i]lR8qQ2oA8wcRCZ^7w/Njh;?.stX?Q1>S1q4Bn$)K1<-rGdO'$Wr.Lc.CG)$/*JL4tNR/,SVO3,aUw'DJN:)Ss;wGn9A32ijw%FL+Z0Fn.U9;reSq)bmI32U==5ALuG&#Vf1398/pVo" + "1*c-(aY168o<`JsSbk-,1N;$>0:OUas(3:8Z972LSfF8eb=c-;>SPw7.6hn3m`9^Xkn(r.qS[0;T%&Qc=+STRxX'q1BNk3&*eu2;&8q$&x>Q#Q7^Tf+6<(d%ZVmj2bDi%.3L2n+4W'$P" + "iDDG)g,r%+?,$@?uou5tSe2aN_AQU*'IAO" + "URQ##V^Fv-XFbGM7Fl(N<3DhLGF%q.1rC$#:T__&Pi68%0xi_&[qFJ(77j_&JWoF.V735&T,[R*:xFR*K5>>#`bW-?4Ne_&6Ne_&6Ne_&n`kr-#GJcM6X;uM6X;uM(.a..^2TkL%oR(#" + ";u.T%fAr%4tJ8&><1=GHZ_+m9/#H1F^R#SC#*N=BA9(D?v[UiFY>>^8p,KKF.W]L29uLkLlu/+4T" + "w$)F./^n3+rlo+DB;5sIYGNk+i1t-69Jg--0pao7Sm#K)pdHW&;LuDNH@H>#/X-TI(;P>#,Gc>#0Su>#4`1?#8lC?#xL$#B.`$#F:r$#JF.%#NR@%#R_R%#Vke%#Zww%#_-4^Rh%Sflr-k'MS.o?.5/sWel/wpEM0%3'/1)K^f1-d>G21&v(35>V`39V7A4=onx4" + "A1OY5EI0;6Ibgr6M$HS7Q<)58C5w,;WoA*#[%T*#`1g*#d=#+#hI5+#lUG+#pbY+#tnl+#x$),#&1;,#*=M,#.I`,#2Ur,#6b.-#;w[H#iQtA#m^0B#qjBB#uvTB##-hB#'9$C#+E6C#" + "/QHC#3^ZC#7jmC#;v)D#?,)4kMYD4lVu`4m`:&5niUA5@(A5BA1]PBB:xlBCC=2CDLXMCEUtiCf&0g2'tN?PGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CP" + "GT4CPGT4CPGT4CPGT4CPGT4CPGT4CP-qekC`.9kEg^+F$kwViFJTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5o,^<-28ZI'O?;xp" + "O?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xp;7q-#lLYI:xvD=#"; + +#endif /* NK_INCLUDE_DEFAULT_FONT */ + +#define NK_CURSOR_DATA_W 90 +#define NK_CURSOR_DATA_H 27 +NK_GLOBAL const char nk_custom_cursor_data[NK_CURSOR_DATA_W * NK_CURSOR_DATA_H + 1] = +{ + "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX" + "..- -X.....X- X.X - X.X -X.....X - X.....X" + "--- -XXX.XXX- X...X - X...X -X....X - X....X" + "X - X.X - X.....X - X.....X -X...X - X...X" + "XX - X.X -X.......X- X.......X -X..X.X - X.X..X" + "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X" + "X..X - X.X - X.X - X.X -XX X.X - X.X XX" + "X...X - X.X - X.X - XX X.X XX - X.X - X.X " + "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X " + "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X " + "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X " + "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X " + "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X " + "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X " + "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X " + "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X " + "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX " + "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------" + "X.X X..X - -X.......X- X.......X - XX XX - " + "XX X..X - - X.....X - X.....X - X.X X.X - " + " X..X - X...X - X...X - X..X X..X - " + " XX - X.X - X.X - X...XXXXXXXXXXXXX...X - " + "------------ - X - X -X.....................X- " + " ----------------------------------- X...XXXXXXXXXXXXX...X - " + " - X..X X..X - " + " - X.X X.X - " + " - XX XX - " +}; + +#ifdef __clang__ +#pragma clang diagnostic pop +#elif defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic pop +#endif + +NK_GLOBAL unsigned char *nk__barrier; +NK_GLOBAL unsigned char *nk__barrier2; +NK_GLOBAL unsigned char *nk__barrier3; +NK_GLOBAL unsigned char *nk__barrier4; +NK_GLOBAL unsigned char *nk__dout; + +NK_INTERN unsigned int +nk_decompress_length(unsigned char *input) +{ + return (unsigned int)((input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11]); +} +NK_INTERN void +nk__match(unsigned char *data, unsigned int length) +{ + /* INVERSE of memmove... write each byte before copying the next...*/ + NK_ASSERT (nk__dout + length <= nk__barrier); + if (nk__dout + length > nk__barrier) { nk__dout += length; return; } + if (data < nk__barrier4) { nk__dout = nk__barrier+1; return; } + while (length--) *nk__dout++ = *data++; +} +NK_INTERN void +nk__lit(unsigned char *data, unsigned int length) +{ + NK_ASSERT (nk__dout + length <= nk__barrier); + if (nk__dout + length > nk__barrier) { nk__dout += length; return; } + if (data < nk__barrier2) { nk__dout = nk__barrier+1; return; } + NK_MEMCPY(nk__dout, data, length); + nk__dout += length; +} +NK_INTERN unsigned char* +nk_decompress_token(unsigned char *i) +{ + #define nk__in2(x) ((i[x] << 8) + i[(x)+1]) + #define nk__in3(x) ((i[x] << 16) + nk__in2((x)+1)) + #define nk__in4(x) ((i[x] << 24) + nk__in3((x)+1)) + + if (*i >= 0x20) { /* use fewer if's for cases that expand small */ + if (*i >= 0x80) nk__match(nk__dout-i[1]-1, (unsigned int)i[0] - 0x80 + 1), i += 2; + else if (*i >= 0x40) nk__match(nk__dout-(nk__in2(0) - 0x4000 + 1), (unsigned int)i[2]+1), i += 3; + else /* *i >= 0x20 */ nk__lit(i+1, (unsigned int)i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1); + } else { /* more ifs for cases that expand large, since overhead is amortized */ + if (*i >= 0x18) nk__match(nk__dout-(unsigned int)(nk__in3(0) - 0x180000 + 1), (unsigned int)i[3]+1), i += 4; + else if (*i >= 0x10) nk__match(nk__dout-(unsigned int)(nk__in3(0) - 0x100000 + 1), (unsigned int)nk__in2(3)+1), i += 5; + else if (*i >= 0x08) nk__lit(i+2, (unsigned int)nk__in2(0) - 0x0800 + 1), i += 2 + (nk__in2(0) - 0x0800 + 1); + else if (*i == 0x07) nk__lit(i+3, (unsigned int)nk__in2(1) + 1), i += 3 + (nk__in2(1) + 1); + else if (*i == 0x06) nk__match(nk__dout-(unsigned int)(nk__in3(1)+1), i[4]+1u), i += 5; + else if (*i == 0x04) nk__match(nk__dout-(unsigned int)(nk__in3(1)+1), (unsigned int)nk__in2(4)+1u), i += 6; + } + return i; +} +NK_INTERN unsigned int +nk_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen) +{ + const unsigned long ADLER_MOD = 65521; + unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16; + unsigned long blocklen, i; + + blocklen = buflen % 5552; + while (buflen) { + for (i=0; i + 7 < blocklen; i += 8) { + s1 += buffer[0]; s2 += s1; + s1 += buffer[1]; s2 += s1; + s1 += buffer[2]; s2 += s1; + s1 += buffer[3]; s2 += s1; + s1 += buffer[4]; s2 += s1; + s1 += buffer[5]; s2 += s1; + s1 += buffer[6]; s2 += s1; + s1 += buffer[7]; s2 += s1; + buffer += 8; + } + for (; i < blocklen; ++i) { + s1 += *buffer++; s2 += s1; + } + + s1 %= ADLER_MOD; s2 %= ADLER_MOD; + buflen -= (unsigned int)blocklen; + blocklen = 5552; + } + return (unsigned int)(s2 << 16) + (unsigned int)s1; +} +NK_INTERN unsigned int +nk_decompress(unsigned char *output, unsigned char *i, unsigned int length) +{ + unsigned int olen; + if (nk__in4(0) != 0x57bC0000) return 0; + if (nk__in4(4) != 0) return 0; /* error! stream is > 4GB */ + olen = nk_decompress_length(i); + nk__barrier2 = i; + nk__barrier3 = i+length; + nk__barrier = output + olen; + nk__barrier4 = output; + i += 16; + + nk__dout = output; + for (;;) { + unsigned char *old_i = i; + i = nk_decompress_token(i); + if (i == old_i) { + if (*i == 0x05 && i[1] == 0xfa) { + NK_ASSERT(nk__dout == output + olen); + if (nk__dout != output + olen) return 0; + if (nk_adler32(1, output, olen) != (unsigned int) nk__in4(2)) + return 0; + return olen; + } else { + NK_ASSERT(0); /* NOTREACHED */ + return 0; + } + } + NK_ASSERT(nk__dout <= output + olen); + if (nk__dout > output + olen) + return 0; + } +} +NK_INTERN unsigned int +nk_decode_85_byte(char c) +{ + return (unsigned int)((c >= '\\') ? c-36 : c-35); +} +NK_INTERN void +nk_decode_85(unsigned char* dst, const unsigned char* src) +{ + while (*src) + { + unsigned int tmp = + nk_decode_85_byte((char)src[0]) + + 85 * (nk_decode_85_byte((char)src[1]) + + 85 * (nk_decode_85_byte((char)src[2]) + + 85 * (nk_decode_85_byte((char)src[3]) + + 85 * nk_decode_85_byte((char)src[4])))); + + /* we can't assume little-endianess. */ + dst[0] = (unsigned char)((tmp >> 0) & 0xFF); + dst[1] = (unsigned char)((tmp >> 8) & 0xFF); + dst[2] = (unsigned char)((tmp >> 16) & 0xFF); + dst[3] = (unsigned char)((tmp >> 24) & 0xFF); + + src += 5; + dst += 4; + } +} + +/* ------------------------------------------------------------- + * + * FONT ATLAS + * + * --------------------------------------------------------------*/ +NK_API struct nk_font_config +nk_font_config(float pixel_height) +{ + struct nk_font_config cfg; + nk_zero_struct(cfg); + cfg.ttf_blob = 0; + cfg.ttf_size = 0; + cfg.ttf_data_owned_by_atlas = 0; + cfg.size = pixel_height; + cfg.oversample_h = 3; + cfg.oversample_v = 1; + cfg.pixel_snap = 0; + cfg.coord_type = NK_COORD_UV; + cfg.spacing = nk_vec2(0,0); + cfg.range = nk_font_default_glyph_ranges(); + cfg.merge_mode = 0; + cfg.fallback_glyph = '?'; + cfg.font = 0; + cfg.n = 0; + return cfg; +} +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void +nk_font_atlas_init_default(struct nk_font_atlas *atlas) +{ + NK_ASSERT(atlas); + if (!atlas) return; + nk_zero_struct(*atlas); + atlas->temporary.userdata.ptr = 0; + atlas->temporary.alloc = nk_malloc; + atlas->temporary.free = nk_mfree; + atlas->permanent.userdata.ptr = 0; + atlas->permanent.alloc = nk_malloc; + atlas->permanent.free = nk_mfree; +} +#endif +NK_API void +nk_font_atlas_init(struct nk_font_atlas *atlas, struct nk_allocator *alloc) +{ + NK_ASSERT(atlas); + NK_ASSERT(alloc); + if (!atlas || !alloc) return; + nk_zero_struct(*atlas); + atlas->permanent = *alloc; + atlas->temporary = *alloc; +} +NK_API void +nk_font_atlas_init_custom(struct nk_font_atlas *atlas, + struct nk_allocator *permanent, struct nk_allocator *temporary) +{ + NK_ASSERT(atlas); + NK_ASSERT(permanent); + NK_ASSERT(temporary); + if (!atlas || !permanent || !temporary) return; + nk_zero_struct(*atlas); + atlas->permanent = *permanent; + atlas->temporary = *temporary; +} +NK_API void +nk_font_atlas_begin(struct nk_font_atlas *atlas) +{ + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc && atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc && atlas->permanent.free); + if (!atlas || !atlas->permanent.alloc || !atlas->permanent.free || + !atlas->temporary.alloc || !atlas->temporary.free) return; + if (atlas->glyphs) { + atlas->permanent.free(atlas->permanent.userdata, atlas->glyphs); + atlas->glyphs = 0; + } + if (atlas->pixel) { + atlas->permanent.free(atlas->permanent.userdata, atlas->pixel); + atlas->pixel = 0; + } +} +NK_API struct nk_font* +nk_font_atlas_add(struct nk_font_atlas *atlas, const struct nk_font_config *config) +{ + struct nk_font *font = 0; + struct nk_font_config *cfg; + + NK_ASSERT(atlas); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + + NK_ASSERT(config); + NK_ASSERT(config->ttf_blob); + NK_ASSERT(config->ttf_size); + NK_ASSERT(config->size > 0.0f); + + if (!atlas || !config || !config->ttf_blob || !config->ttf_size || config->size <= 0.0f|| + !atlas->permanent.alloc || !atlas->permanent.free || + !atlas->temporary.alloc || !atlas->temporary.free) + return 0; + + /* allocate font config */ + cfg = (struct nk_font_config*) + atlas->permanent.alloc(atlas->permanent.userdata,0, sizeof(struct nk_font_config)); + NK_MEMCPY(cfg, config, sizeof(*config)); + cfg->n = cfg; + cfg->p = cfg; + + if (!config->merge_mode) { + /* insert font config into list */ + if (!atlas->config) { + atlas->config = cfg; + cfg->next = 0; + } else { + struct nk_font_config *i = atlas->config; + while (i->next) i = i->next; + i->next = cfg; + cfg->next = 0; + } + /* allocate new font */ + font = (struct nk_font*) + atlas->permanent.alloc(atlas->permanent.userdata,0, sizeof(struct nk_font)); + NK_ASSERT(font); + nk_zero(font, sizeof(*font)); + if (!font) return 0; + font->config = cfg; + + /* insert font into list */ + if (!atlas->fonts) { + atlas->fonts = font; + font->next = 0; + } else { + struct nk_font *i = atlas->fonts; + while (i->next) i = i->next; + i->next = font; + font->next = 0; + } + cfg->font = &font->info; + } else { + /* extend previously added font */ + struct nk_font *f = 0; + struct nk_font_config *c = 0; + NK_ASSERT(atlas->font_num); + f = atlas->fonts; + c = f->config; + cfg->font = &f->info; + + cfg->n = c; + cfg->p = c->p; + c->p->n = cfg; + c->p = cfg; + } + /* create own copy of .TTF font blob */ + if (!config->ttf_data_owned_by_atlas) { + cfg->ttf_blob = atlas->permanent.alloc(atlas->permanent.userdata,0, cfg->ttf_size); + NK_ASSERT(cfg->ttf_blob); + if (!cfg->ttf_blob) { + atlas->font_num++; + return 0; + } + NK_MEMCPY(cfg->ttf_blob, config->ttf_blob, cfg->ttf_size); + cfg->ttf_data_owned_by_atlas = 1; + } + atlas->font_num++; + return font; +} +NK_API struct nk_font* +nk_font_atlas_add_from_memory(struct nk_font_atlas *atlas, void *memory, + nk_size size, float height, const struct nk_font_config *config) +{ + struct nk_font_config cfg; + NK_ASSERT(memory); + NK_ASSERT(size); + + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + if (!atlas || !atlas->temporary.alloc || !atlas->temporary.free || !memory || !size || + !atlas->permanent.alloc || !atlas->permanent.free) + return 0; + + cfg = (config) ? *config: nk_font_config(height); + cfg.ttf_blob = memory; + cfg.ttf_size = size; + cfg.size = height; + cfg.ttf_data_owned_by_atlas = 0; + return nk_font_atlas_add(atlas, &cfg); +} +#ifdef NK_INCLUDE_STANDARD_IO +NK_API struct nk_font* +nk_font_atlas_add_from_file(struct nk_font_atlas *atlas, const char *file_path, + float height, const struct nk_font_config *config) +{ + nk_size size; + char *memory; + struct nk_font_config cfg; + + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + + if (!atlas || !file_path) return 0; + memory = nk_file_load(file_path, &size, &atlas->permanent); + if (!memory) return 0; + + cfg = (config) ? *config: nk_font_config(height); + cfg.ttf_blob = memory; + cfg.ttf_size = size; + cfg.size = height; + cfg.ttf_data_owned_by_atlas = 1; + return nk_font_atlas_add(atlas, &cfg); +} +#endif +NK_API struct nk_font* +nk_font_atlas_add_compressed(struct nk_font_atlas *atlas, + void *compressed_data, nk_size compressed_size, float height, + const struct nk_font_config *config) +{ + unsigned int decompressed_size; + void *decompressed_data; + struct nk_font_config cfg; + + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + + NK_ASSERT(compressed_data); + NK_ASSERT(compressed_size); + if (!atlas || !compressed_data || !atlas->temporary.alloc || !atlas->temporary.free || + !atlas->permanent.alloc || !atlas->permanent.free) + return 0; + + decompressed_size = nk_decompress_length((unsigned char*)compressed_data); + decompressed_data = atlas->permanent.alloc(atlas->permanent.userdata,0,decompressed_size); + NK_ASSERT(decompressed_data); + if (!decompressed_data) return 0; + nk_decompress((unsigned char*)decompressed_data, (unsigned char*)compressed_data, + (unsigned int)compressed_size); + + cfg = (config) ? *config: nk_font_config(height); + cfg.ttf_blob = decompressed_data; + cfg.ttf_size = decompressed_size; + cfg.size = height; + cfg.ttf_data_owned_by_atlas = 1; + return nk_font_atlas_add(atlas, &cfg); +} +NK_API struct nk_font* +nk_font_atlas_add_compressed_base85(struct nk_font_atlas *atlas, + const char *data_base85, float height, const struct nk_font_config *config) +{ + int compressed_size; + void *compressed_data; + struct nk_font *font; + + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + + NK_ASSERT(data_base85); + if (!atlas || !data_base85 || !atlas->temporary.alloc || !atlas->temporary.free || + !atlas->permanent.alloc || !atlas->permanent.free) + return 0; + + compressed_size = (((int)nk_strlen(data_base85) + 4) / 5) * 4; + compressed_data = atlas->temporary.alloc(atlas->temporary.userdata,0, (nk_size)compressed_size); + NK_ASSERT(compressed_data); + if (!compressed_data) return 0; + nk_decode_85((unsigned char*)compressed_data, (const unsigned char*)data_base85); + font = nk_font_atlas_add_compressed(atlas, compressed_data, + (nk_size)compressed_size, height, config); + atlas->temporary.free(atlas->temporary.userdata, compressed_data); + return font; +} + +#ifdef NK_INCLUDE_DEFAULT_FONT +NK_API struct nk_font* +nk_font_atlas_add_default(struct nk_font_atlas *atlas, + float pixel_height, const struct nk_font_config *config) +{ + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + return nk_font_atlas_add_compressed_base85(atlas, + nk_proggy_clean_ttf_compressed_data_base85, pixel_height, config); +} +#endif +NK_API const void* +nk_font_atlas_bake(struct nk_font_atlas *atlas, int *width, int *height, + enum nk_font_atlas_format fmt) +{ + int i = 0; + void *tmp = 0; + nk_size tmp_size, img_size; + struct nk_font *font_iter; + struct nk_font_baker *baker; + + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + + NK_ASSERT(width); + NK_ASSERT(height); + if (!atlas || !width || !height || + !atlas->temporary.alloc || !atlas->temporary.free || + !atlas->permanent.alloc || !atlas->permanent.free) + return 0; + +#ifdef NK_INCLUDE_DEFAULT_FONT + /* no font added so just use default font */ + if (!atlas->font_num) + atlas->default_font = nk_font_atlas_add_default(atlas, 13.0f, 0); +#endif + NK_ASSERT(atlas->font_num); + if (!atlas->font_num) return 0; + + /* allocate temporary baker memory required for the baking process */ + nk_font_baker_memory(&tmp_size, &atlas->glyph_count, atlas->config, atlas->font_num); + tmp = atlas->temporary.alloc(atlas->temporary.userdata,0, tmp_size); + NK_ASSERT(tmp); + if (!tmp) goto failed; + memset(tmp,0,tmp_size); + + /* allocate glyph memory for all fonts */ + baker = nk_font_baker(tmp, atlas->glyph_count, atlas->font_num, &atlas->temporary); + atlas->glyphs = (struct nk_font_glyph*)atlas->permanent.alloc( + atlas->permanent.userdata,0, sizeof(struct nk_font_glyph)*(nk_size)atlas->glyph_count); + NK_ASSERT(atlas->glyphs); + if (!atlas->glyphs) + goto failed; + + /* pack all glyphs into a tight fit space */ + atlas->custom.w = (NK_CURSOR_DATA_W*2)+1; + atlas->custom.h = NK_CURSOR_DATA_H + 1; + if (!nk_font_bake_pack(baker, &img_size, width, height, &atlas->custom, + atlas->config, atlas->font_num, &atlas->temporary)) + goto failed; + + /* allocate memory for the baked image font atlas */ + atlas->pixel = atlas->temporary.alloc(atlas->temporary.userdata,0, img_size); + NK_ASSERT(atlas->pixel); + if (!atlas->pixel) + goto failed; + + /* bake glyphs and custom white pixel into image */ + nk_font_bake(baker, atlas->pixel, *width, *height, + atlas->glyphs, atlas->glyph_count, atlas->config, atlas->font_num); + nk_font_bake_custom_data(atlas->pixel, *width, *height, atlas->custom, + nk_custom_cursor_data, NK_CURSOR_DATA_W, NK_CURSOR_DATA_H, '.', 'X'); + + if (fmt == NK_FONT_ATLAS_RGBA32) { + /* convert alpha8 image into rgba32 image */ + void *img_rgba = atlas->temporary.alloc(atlas->temporary.userdata,0, + (nk_size)(*width * *height * 4)); + NK_ASSERT(img_rgba); + if (!img_rgba) goto failed; + nk_font_bake_convert(img_rgba, *width, *height, atlas->pixel); + atlas->temporary.free(atlas->temporary.userdata, atlas->pixel); + atlas->pixel = img_rgba; + } + atlas->tex_width = *width; + atlas->tex_height = *height; + + /* initialize each font */ + for (font_iter = atlas->fonts; font_iter; font_iter = font_iter->next) { + struct nk_font *font = font_iter; + struct nk_font_config *config = font->config; + nk_font_init(font, config->size, config->fallback_glyph, atlas->glyphs, + config->font, nk_handle_ptr(0)); + } + + /* initialize each cursor */ + {NK_STORAGE const struct nk_vec2 nk_cursor_data[NK_CURSOR_COUNT][3] = { + /* Pos Size Offset */ + {{ 0, 3}, {12,19}, { 0, 0}}, + {{13, 0}, { 7,16}, { 4, 8}}, + {{31, 0}, {23,23}, {11,11}}, + {{21, 0}, { 9, 23}, { 5,11}}, + {{55,18}, {23, 9}, {11, 5}}, + {{73, 0}, {17,17}, { 9, 9}}, + {{55, 0}, {17,17}, { 9, 9}} + }; + for (i = 0; i < NK_CURSOR_COUNT; ++i) { + struct nk_cursor *cursor = &atlas->cursors[i]; + cursor->img.w = (unsigned short)*width; + cursor->img.h = (unsigned short)*height; + cursor->img.region[0] = (unsigned short)(atlas->custom.x + nk_cursor_data[i][0].x); + cursor->img.region[1] = (unsigned short)(atlas->custom.y + nk_cursor_data[i][0].y); + cursor->img.region[2] = (unsigned short)nk_cursor_data[i][1].x; + cursor->img.region[3] = (unsigned short)nk_cursor_data[i][1].y; + cursor->size = nk_cursor_data[i][1]; + cursor->offset = nk_cursor_data[i][2]; + }} + /* free temporary memory */ + atlas->temporary.free(atlas->temporary.userdata, tmp); + return atlas->pixel; + +failed: + /* error so cleanup all memory */ + if (tmp) atlas->temporary.free(atlas->temporary.userdata, tmp); + if (atlas->glyphs) { + atlas->permanent.free(atlas->permanent.userdata, atlas->glyphs); + atlas->glyphs = 0; + } + if (atlas->pixel) { + atlas->temporary.free(atlas->temporary.userdata, atlas->pixel); + atlas->pixel = 0; + } + return 0; +} +NK_API void +nk_font_atlas_end(struct nk_font_atlas *atlas, nk_handle texture, + struct nk_draw_null_texture *null) +{ + int i = 0; + struct nk_font *font_iter; + NK_ASSERT(atlas); + if (!atlas) { + if (!null) return; + null->texture = texture; + null->uv = nk_vec2(0.5f,0.5f); + } + if (null) { + null->texture = texture; + null->uv.x = (atlas->custom.x + 0.5f)/(float)atlas->tex_width; + null->uv.y = (atlas->custom.y + 0.5f)/(float)atlas->tex_height; + } + for (font_iter = atlas->fonts; font_iter; font_iter = font_iter->next) { + font_iter->texture = texture; +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + font_iter->handle.texture = texture; +#endif + } + for (i = 0; i < NK_CURSOR_COUNT; ++i) + atlas->cursors[i].img.handle = texture; + + atlas->temporary.free(atlas->temporary.userdata, atlas->pixel); + atlas->pixel = 0; + atlas->tex_width = 0; + atlas->tex_height = 0; + atlas->custom.x = 0; + atlas->custom.y = 0; + atlas->custom.w = 0; + atlas->custom.h = 0; +} +NK_API void +nk_font_atlas_cleanup(struct nk_font_atlas *atlas) +{ + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + if (!atlas || !atlas->permanent.alloc || !atlas->permanent.free) return; + if (atlas->config) { + struct nk_font_config *iter; + for (iter = atlas->config; iter; iter = iter->next) { + struct nk_font_config *i; + for (i = iter->n; i != iter; i = i->n) { + atlas->permanent.free(atlas->permanent.userdata, i->ttf_blob); + i->ttf_blob = 0; + } + atlas->permanent.free(atlas->permanent.userdata, iter->ttf_blob); + iter->ttf_blob = 0; + } + } +} +NK_API void +nk_font_atlas_clear(struct nk_font_atlas *atlas) +{ + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + if (!atlas || !atlas->permanent.alloc || !atlas->permanent.free) return; + + if (atlas->config) { + struct nk_font_config *iter, *next; + for (iter = atlas->config; iter; iter = next) { + struct nk_font_config *i, *n; + for (i = iter->n; i != iter; i = n) { + n = i->n; + if (i->ttf_blob) + atlas->permanent.free(atlas->permanent.userdata, i->ttf_blob); + atlas->permanent.free(atlas->permanent.userdata, i); + } + next = iter->next; + if (i->ttf_blob) + atlas->permanent.free(atlas->permanent.userdata, iter->ttf_blob); + atlas->permanent.free(atlas->permanent.userdata, iter); + } + atlas->config = 0; + } + if (atlas->fonts) { + struct nk_font *iter, *next; + for (iter = atlas->fonts; iter; iter = next) { + next = iter->next; + atlas->permanent.free(atlas->permanent.userdata, iter); + } + atlas->fonts = 0; + } + if (atlas->glyphs) + atlas->permanent.free(atlas->permanent.userdata, atlas->glyphs); + nk_zero_struct(*atlas); +} +#endif + + + + + +/* =============================================================== + * + * INPUT + * + * ===============================================================*/ +NK_API void +nk_input_begin(struct nk_context *ctx) +{ + int i; + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + for (i = 0; i < NK_BUTTON_MAX; ++i) + in->mouse.buttons[i].clicked = 0; + + in->keyboard.text_len = 0; + in->mouse.scroll_delta = nk_vec2(0,0); + in->mouse.prev.x = in->mouse.pos.x; + in->mouse.prev.y = in->mouse.pos.y; + in->mouse.delta.x = 0; + in->mouse.delta.y = 0; + for (i = 0; i < NK_KEY_MAX; i++) + in->keyboard.keys[i].clicked = 0; +} +NK_API void +nk_input_end(struct nk_context *ctx) +{ + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + if (in->mouse.grab) + in->mouse.grab = 0; + if (in->mouse.ungrab) { + in->mouse.grabbed = 0; + in->mouse.ungrab = 0; + in->mouse.grab = 0; + } +} +NK_API void +nk_input_motion(struct nk_context *ctx, int x, int y) +{ + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + in->mouse.pos.x = (float)x; + in->mouse.pos.y = (float)y; + in->mouse.delta.x = in->mouse.pos.x - in->mouse.prev.x; + in->mouse.delta.y = in->mouse.pos.y - in->mouse.prev.y; +} +NK_API void +nk_input_key(struct nk_context *ctx, enum nk_keys key, nk_bool down) +{ + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; +#ifdef NK_KEYSTATE_BASED_INPUT + if (in->keyboard.keys[key].down != down) + in->keyboard.keys[key].clicked++; +#else + in->keyboard.keys[key].clicked++; +#endif + in->keyboard.keys[key].down = down; +} +NK_API void +nk_input_button(struct nk_context *ctx, enum nk_buttons id, int x, int y, nk_bool down) +{ + struct nk_mouse_button *btn; + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + if (in->mouse.buttons[id].down == down) return; + + btn = &in->mouse.buttons[id]; + btn->clicked_pos.x = (float)x; + btn->clicked_pos.y = (float)y; + btn->down = down; + btn->clicked++; +} +NK_API void +nk_input_scroll(struct nk_context *ctx, struct nk_vec2 val) +{ + NK_ASSERT(ctx); + if (!ctx) return; + ctx->input.mouse.scroll_delta.x += val.x; + ctx->input.mouse.scroll_delta.y += val.y; +} +NK_API void +nk_input_glyph(struct nk_context *ctx, const nk_glyph glyph) +{ + int len = 0; + nk_rune unicode; + struct nk_input *in; + + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + + len = nk_utf_decode(glyph, &unicode, NK_UTF_SIZE); + if (len && ((in->keyboard.text_len + len) < NK_INPUT_MAX)) { + nk_utf_encode(unicode, &in->keyboard.text[in->keyboard.text_len], + NK_INPUT_MAX - in->keyboard.text_len); + in->keyboard.text_len += len; + } +} +NK_API void +nk_input_char(struct nk_context *ctx, char c) +{ + nk_glyph glyph; + NK_ASSERT(ctx); + if (!ctx) return; + glyph[0] = c; + nk_input_glyph(ctx, glyph); +} +NK_API void +nk_input_unicode(struct nk_context *ctx, nk_rune unicode) +{ + nk_glyph rune; + NK_ASSERT(ctx); + if (!ctx) return; + nk_utf_encode(unicode, rune, NK_UTF_SIZE); + nk_input_glyph(ctx, rune); +} +NK_API nk_bool +nk_input_has_mouse_click(const struct nk_input *i, enum nk_buttons id) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + return (btn->clicked && btn->down == nk_false) ? nk_true : nk_false; +} +NK_API nk_bool +nk_input_has_mouse_click_in_rect(const struct nk_input *i, enum nk_buttons id, + struct nk_rect b) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + if (!NK_INBOX(btn->clicked_pos.x,btn->clicked_pos.y,b.x,b.y,b.w,b.h)) + return nk_false; + return nk_true; +} +NK_API nk_bool +nk_input_has_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, + struct nk_rect b, nk_bool down) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + return nk_input_has_mouse_click_in_rect(i, id, b) && (btn->down == down); +} +NK_API nk_bool +nk_input_is_mouse_click_in_rect(const struct nk_input *i, enum nk_buttons id, + struct nk_rect b) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + return (nk_input_has_mouse_click_down_in_rect(i, id, b, nk_false) && + btn->clicked) ? nk_true : nk_false; +} +NK_API nk_bool +nk_input_is_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, + struct nk_rect b, nk_bool down) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + return (nk_input_has_mouse_click_down_in_rect(i, id, b, down) && + btn->clicked) ? nk_true : nk_false; +} +NK_API nk_bool +nk_input_any_mouse_click_in_rect(const struct nk_input *in, struct nk_rect b) +{ + int i, down = 0; + for (i = 0; i < NK_BUTTON_MAX; ++i) + down = down || nk_input_is_mouse_click_in_rect(in, (enum nk_buttons)i, b); + return down; +} +NK_API nk_bool +nk_input_is_mouse_hovering_rect(const struct nk_input *i, struct nk_rect rect) +{ + if (!i) return nk_false; + return NK_INBOX(i->mouse.pos.x, i->mouse.pos.y, rect.x, rect.y, rect.w, rect.h); +} +NK_API nk_bool +nk_input_is_mouse_prev_hovering_rect(const struct nk_input *i, struct nk_rect rect) +{ + if (!i) return nk_false; + return NK_INBOX(i->mouse.prev.x, i->mouse.prev.y, rect.x, rect.y, rect.w, rect.h); +} +NK_API nk_bool +nk_input_mouse_clicked(const struct nk_input *i, enum nk_buttons id, struct nk_rect rect) +{ + if (!i) return nk_false; + if (!nk_input_is_mouse_hovering_rect(i, rect)) return nk_false; + return nk_input_is_mouse_click_in_rect(i, id, rect); +} +NK_API nk_bool +nk_input_is_mouse_down(const struct nk_input *i, enum nk_buttons id) +{ + if (!i) return nk_false; + return i->mouse.buttons[id].down; +} +NK_API nk_bool +nk_input_is_mouse_pressed(const struct nk_input *i, enum nk_buttons id) +{ + const struct nk_mouse_button *b; + if (!i) return nk_false; + b = &i->mouse.buttons[id]; + if (b->down && b->clicked) + return nk_true; + return nk_false; +} +NK_API nk_bool +nk_input_is_mouse_released(const struct nk_input *i, enum nk_buttons id) +{ + if (!i) return nk_false; + return (!i->mouse.buttons[id].down && i->mouse.buttons[id].clicked); +} +NK_API nk_bool +nk_input_is_key_pressed(const struct nk_input *i, enum nk_keys key) +{ + const struct nk_key *k; + if (!i) return nk_false; + k = &i->keyboard.keys[key]; + if ((k->down && k->clicked) || (!k->down && k->clicked >= 2)) + return nk_true; + return nk_false; +} +NK_API nk_bool +nk_input_is_key_released(const struct nk_input *i, enum nk_keys key) +{ + const struct nk_key *k; + if (!i) return nk_false; + k = &i->keyboard.keys[key]; + if ((!k->down && k->clicked) || (k->down && k->clicked >= 2)) + return nk_true; + return nk_false; +} +NK_API nk_bool +nk_input_is_key_down(const struct nk_input *i, enum nk_keys key) +{ + const struct nk_key *k; + if (!i) return nk_false; + k = &i->keyboard.keys[key]; + if (k->down) return nk_true; + return nk_false; +} + + + + + +/* =============================================================== + * + * STYLE + * + * ===============================================================*/ +NK_API void nk_style_default(struct nk_context *ctx){nk_style_from_table(ctx, 0);} +#define NK_COLOR_MAP(NK_COLOR)\ + NK_COLOR(NK_COLOR_TEXT, 175,175,175,255) \ + NK_COLOR(NK_COLOR_WINDOW, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_HEADER, 40, 40, 40, 255) \ + NK_COLOR(NK_COLOR_BORDER, 65, 65, 65, 255) \ + NK_COLOR(NK_COLOR_BUTTON, 50, 50, 50, 255) \ + NK_COLOR(NK_COLOR_BUTTON_HOVER, 40, 40, 40, 255) \ + NK_COLOR(NK_COLOR_BUTTON_ACTIVE, 35, 35, 35, 255) \ + NK_COLOR(NK_COLOR_TOGGLE, 100,100,100,255) \ + NK_COLOR(NK_COLOR_TOGGLE_HOVER, 120,120,120,255) \ + NK_COLOR(NK_COLOR_TOGGLE_CURSOR, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_SELECT, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_SELECT_ACTIVE, 35, 35, 35,255) \ + NK_COLOR(NK_COLOR_SLIDER, 38, 38, 38, 255) \ + NK_COLOR(NK_COLOR_SLIDER_CURSOR, 100,100,100,255) \ + NK_COLOR(NK_COLOR_SLIDER_CURSOR_HOVER, 120,120,120,255) \ + NK_COLOR(NK_COLOR_SLIDER_CURSOR_ACTIVE, 150,150,150,255) \ + NK_COLOR(NK_COLOR_PROPERTY, 38, 38, 38, 255) \ + NK_COLOR(NK_COLOR_EDIT, 38, 38, 38, 255) \ + NK_COLOR(NK_COLOR_EDIT_CURSOR, 175,175,175,255) \ + NK_COLOR(NK_COLOR_COMBO, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_CHART, 120,120,120,255) \ + NK_COLOR(NK_COLOR_CHART_COLOR, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_CHART_COLOR_HIGHLIGHT, 255, 0, 0, 255) \ + NK_COLOR(NK_COLOR_SCROLLBAR, 40, 40, 40, 255) \ + NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR, 100,100,100,255) \ + NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR_HOVER, 120,120,120,255) \ + NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR_ACTIVE, 150,150,150,255) \ + NK_COLOR(NK_COLOR_TAB_HEADER, 40, 40, 40,255) + +NK_GLOBAL const struct nk_color +nk_default_color_style[NK_COLOR_COUNT] = { +#define NK_COLOR(a,b,c,d,e) {b,c,d,e}, + NK_COLOR_MAP(NK_COLOR) +#undef NK_COLOR +}; +NK_GLOBAL const char *nk_color_names[NK_COLOR_COUNT] = { +#define NK_COLOR(a,b,c,d,e) #a, + NK_COLOR_MAP(NK_COLOR) +#undef NK_COLOR +}; + +NK_API const char* +nk_style_get_color_by_name(enum nk_style_colors c) +{ + return nk_color_names[c]; +} +NK_API struct nk_style_item +nk_style_item_image(struct nk_image img) +{ + struct nk_style_item i; + i.type = NK_STYLE_ITEM_IMAGE; + i.data.image = img; + return i; +} +NK_API struct nk_style_item +nk_style_item_color(struct nk_color col) +{ + struct nk_style_item i; + i.type = NK_STYLE_ITEM_COLOR; + i.data.color = col; + return i; +} +NK_API struct nk_style_item +nk_style_item_hide(void) +{ + struct nk_style_item i; + i.type = NK_STYLE_ITEM_COLOR; + i.data.color = nk_rgba(0,0,0,0); + return i; +} +NK_API void +nk_style_from_table(struct nk_context *ctx, const struct nk_color *table) +{ + struct nk_style *style; + struct nk_style_text *text; + struct nk_style_button *button; + struct nk_style_toggle *toggle; + struct nk_style_selectable *select; + struct nk_style_slider *slider; + struct nk_style_progress *prog; + struct nk_style_scrollbar *scroll; + struct nk_style_edit *edit; + struct nk_style_property *property; + struct nk_style_combo *combo; + struct nk_style_chart *chart; + struct nk_style_tab *tab; + struct nk_style_window *win; + + NK_ASSERT(ctx); + if (!ctx) return; + style = &ctx->style; + table = (!table) ? nk_default_color_style: table; + + /* default text */ + text = &style->text; + text->color = table[NK_COLOR_TEXT]; + text->padding = nk_vec2(0,0); + + /* default button */ + button = &style->button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_BUTTON]); + button->hover = nk_style_item_color(table[NK_COLOR_BUTTON_HOVER]); + button->active = nk_style_item_color(table[NK_COLOR_BUTTON_ACTIVE]); + button->border_color = table[NK_COLOR_BORDER]; + button->text_background = table[NK_COLOR_BUTTON]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->image_padding = nk_vec2(0.0f,0.0f); + button->touch_padding = nk_vec2(0.0f, 0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 1.0f; + button->rounding = 4.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* contextual button */ + button = &style->contextual_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->hover = nk_style_item_color(table[NK_COLOR_BUTTON_HOVER]); + button->active = nk_style_item_color(table[NK_COLOR_BUTTON_ACTIVE]); + button->border_color = table[NK_COLOR_WINDOW]; + button->text_background = table[NK_COLOR_WINDOW]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* menu button */ + button = &style->menu_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->hover = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->active = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->border_color = table[NK_COLOR_WINDOW]; + button->text_background = table[NK_COLOR_WINDOW]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 1.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* checkbox toggle */ + toggle = &style->checkbox; + nk_zero_struct(*toggle); + toggle->normal = nk_style_item_color(table[NK_COLOR_TOGGLE]); + toggle->hover = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); + toggle->active = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); + toggle->cursor_normal = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); + toggle->cursor_hover = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); + toggle->userdata = nk_handle_ptr(0); + toggle->text_background = table[NK_COLOR_WINDOW]; + toggle->text_normal = table[NK_COLOR_TEXT]; + toggle->text_hover = table[NK_COLOR_TEXT]; + toggle->text_active = table[NK_COLOR_TEXT]; + toggle->padding = nk_vec2(2.0f, 2.0f); + toggle->touch_padding = nk_vec2(0,0); + toggle->border_color = nk_rgba(0,0,0,0); + toggle->border = 0.0f; + toggle->spacing = 4; + + /* option toggle */ + toggle = &style->option; + nk_zero_struct(*toggle); + toggle->normal = nk_style_item_color(table[NK_COLOR_TOGGLE]); + toggle->hover = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); + toggle->active = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); + toggle->cursor_normal = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); + toggle->cursor_hover = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); + toggle->userdata = nk_handle_ptr(0); + toggle->text_background = table[NK_COLOR_WINDOW]; + toggle->text_normal = table[NK_COLOR_TEXT]; + toggle->text_hover = table[NK_COLOR_TEXT]; + toggle->text_active = table[NK_COLOR_TEXT]; + toggle->padding = nk_vec2(3.0f, 3.0f); + toggle->touch_padding = nk_vec2(0,0); + toggle->border_color = nk_rgba(0,0,0,0); + toggle->border = 0.0f; + toggle->spacing = 4; + + /* selectable */ + select = &style->selectable; + nk_zero_struct(*select); + select->normal = nk_style_item_color(table[NK_COLOR_SELECT]); + select->hover = nk_style_item_color(table[NK_COLOR_SELECT]); + select->pressed = nk_style_item_color(table[NK_COLOR_SELECT]); + select->normal_active = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]); + select->hover_active = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]); + select->pressed_active = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]); + select->text_normal = table[NK_COLOR_TEXT]; + select->text_hover = table[NK_COLOR_TEXT]; + select->text_pressed = table[NK_COLOR_TEXT]; + select->text_normal_active = table[NK_COLOR_TEXT]; + select->text_hover_active = table[NK_COLOR_TEXT]; + select->text_pressed_active = table[NK_COLOR_TEXT]; + select->padding = nk_vec2(2.0f,2.0f); + select->image_padding = nk_vec2(2.0f,2.0f); + select->touch_padding = nk_vec2(0,0); + select->userdata = nk_handle_ptr(0); + select->rounding = 0.0f; + select->draw_begin = 0; + select->draw_end = 0; + + /* slider */ + slider = &style->slider; + nk_zero_struct(*slider); + slider->normal = nk_style_item_hide(); + slider->hover = nk_style_item_hide(); + slider->active = nk_style_item_hide(); + slider->bar_normal = table[NK_COLOR_SLIDER]; + slider->bar_hover = table[NK_COLOR_SLIDER]; + slider->bar_active = table[NK_COLOR_SLIDER]; + slider->bar_filled = table[NK_COLOR_SLIDER_CURSOR]; + slider->cursor_normal = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR]); + slider->cursor_hover = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_HOVER]); + slider->cursor_active = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_ACTIVE]); + slider->inc_symbol = NK_SYMBOL_TRIANGLE_RIGHT; + slider->dec_symbol = NK_SYMBOL_TRIANGLE_LEFT; + slider->cursor_size = nk_vec2(16,16); + slider->padding = nk_vec2(2,2); + slider->spacing = nk_vec2(2,2); + slider->userdata = nk_handle_ptr(0); + slider->show_buttons = nk_false; + slider->bar_height = 8; + slider->rounding = 0; + slider->draw_begin = 0; + slider->draw_end = 0; + + /* slider buttons */ + button = &style->slider.inc_button; + button->normal = nk_style_item_color(nk_rgb(40,40,40)); + button->hover = nk_style_item_color(nk_rgb(42,42,42)); + button->active = nk_style_item_color(nk_rgb(44,44,44)); + button->border_color = nk_rgb(65,65,65); + button->text_background = nk_rgb(40,40,40); + button->text_normal = nk_rgb(175,175,175); + button->text_hover = nk_rgb(175,175,175); + button->text_active = nk_rgb(175,175,175); + button->padding = nk_vec2(8.0f,8.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 1.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + style->slider.dec_button = style->slider.inc_button; + + /* progressbar */ + prog = &style->progress; + nk_zero_struct(*prog); + prog->normal = nk_style_item_color(table[NK_COLOR_SLIDER]); + prog->hover = nk_style_item_color(table[NK_COLOR_SLIDER]); + prog->active = nk_style_item_color(table[NK_COLOR_SLIDER]); + prog->cursor_normal = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR]); + prog->cursor_hover = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_HOVER]); + prog->cursor_active = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_ACTIVE]); + prog->border_color = nk_rgba(0,0,0,0); + prog->cursor_border_color = nk_rgba(0,0,0,0); + prog->userdata = nk_handle_ptr(0); + prog->padding = nk_vec2(4,4); + prog->rounding = 0; + prog->border = 0; + prog->cursor_rounding = 0; + prog->cursor_border = 0; + prog->draw_begin = 0; + prog->draw_end = 0; + + /* scrollbars */ + scroll = &style->scrollh; + nk_zero_struct(*scroll); + scroll->normal = nk_style_item_color(table[NK_COLOR_SCROLLBAR]); + scroll->hover = nk_style_item_color(table[NK_COLOR_SCROLLBAR]); + scroll->active = nk_style_item_color(table[NK_COLOR_SCROLLBAR]); + scroll->cursor_normal = nk_style_item_color(table[NK_COLOR_SCROLLBAR_CURSOR]); + scroll->cursor_hover = nk_style_item_color(table[NK_COLOR_SCROLLBAR_CURSOR_HOVER]); + scroll->cursor_active = nk_style_item_color(table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE]); + scroll->dec_symbol = NK_SYMBOL_CIRCLE_SOLID; + scroll->inc_symbol = NK_SYMBOL_CIRCLE_SOLID; + scroll->userdata = nk_handle_ptr(0); + scroll->border_color = table[NK_COLOR_SCROLLBAR]; + scroll->cursor_border_color = table[NK_COLOR_SCROLLBAR]; + scroll->padding = nk_vec2(0,0); + scroll->show_buttons = nk_false; + scroll->border = 0; + scroll->rounding = 0; + scroll->border_cursor = 0; + scroll->rounding_cursor = 0; + scroll->draw_begin = 0; + scroll->draw_end = 0; + style->scrollv = style->scrollh; + + /* scrollbars buttons */ + button = &style->scrollh.inc_button; + button->normal = nk_style_item_color(nk_rgb(40,40,40)); + button->hover = nk_style_item_color(nk_rgb(42,42,42)); + button->active = nk_style_item_color(nk_rgb(44,44,44)); + button->border_color = nk_rgb(65,65,65); + button->text_background = nk_rgb(40,40,40); + button->text_normal = nk_rgb(175,175,175); + button->text_hover = nk_rgb(175,175,175); + button->text_active = nk_rgb(175,175,175); + button->padding = nk_vec2(4.0f,4.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 1.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + style->scrollh.dec_button = style->scrollh.inc_button; + style->scrollv.inc_button = style->scrollh.inc_button; + style->scrollv.dec_button = style->scrollh.inc_button; + + /* edit */ + edit = &style->edit; + nk_zero_struct(*edit); + edit->normal = nk_style_item_color(table[NK_COLOR_EDIT]); + edit->hover = nk_style_item_color(table[NK_COLOR_EDIT]); + edit->active = nk_style_item_color(table[NK_COLOR_EDIT]); + edit->cursor_normal = table[NK_COLOR_TEXT]; + edit->cursor_hover = table[NK_COLOR_TEXT]; + edit->cursor_text_normal= table[NK_COLOR_EDIT]; + edit->cursor_text_hover = table[NK_COLOR_EDIT]; + edit->border_color = table[NK_COLOR_BORDER]; + edit->text_normal = table[NK_COLOR_TEXT]; + edit->text_hover = table[NK_COLOR_TEXT]; + edit->text_active = table[NK_COLOR_TEXT]; + edit->selected_normal = table[NK_COLOR_TEXT]; + edit->selected_hover = table[NK_COLOR_TEXT]; + edit->selected_text_normal = table[NK_COLOR_EDIT]; + edit->selected_text_hover = table[NK_COLOR_EDIT]; + edit->scrollbar_size = nk_vec2(10,10); + edit->scrollbar = style->scrollv; + edit->padding = nk_vec2(4,4); + edit->row_padding = 2; + edit->cursor_size = 4; + edit->border = 1; + edit->rounding = 0; + + /* property */ + property = &style->property; + nk_zero_struct(*property); + property->normal = nk_style_item_color(table[NK_COLOR_PROPERTY]); + property->hover = nk_style_item_color(table[NK_COLOR_PROPERTY]); + property->active = nk_style_item_color(table[NK_COLOR_PROPERTY]); + property->border_color = table[NK_COLOR_BORDER]; + property->label_normal = table[NK_COLOR_TEXT]; + property->label_hover = table[NK_COLOR_TEXT]; + property->label_active = table[NK_COLOR_TEXT]; + property->sym_left = NK_SYMBOL_TRIANGLE_LEFT; + property->sym_right = NK_SYMBOL_TRIANGLE_RIGHT; + property->userdata = nk_handle_ptr(0); + property->padding = nk_vec2(4,4); + property->border = 1; + property->rounding = 10; + property->draw_begin = 0; + property->draw_end = 0; + + /* property buttons */ + button = &style->property.dec_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_PROPERTY]); + button->hover = nk_style_item_color(table[NK_COLOR_PROPERTY]); + button->active = nk_style_item_color(table[NK_COLOR_PROPERTY]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_PROPERTY]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(0.0f,0.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + style->property.inc_button = style->property.dec_button; + + /* property edit */ + edit = &style->property.edit; + nk_zero_struct(*edit); + edit->normal = nk_style_item_color(table[NK_COLOR_PROPERTY]); + edit->hover = nk_style_item_color(table[NK_COLOR_PROPERTY]); + edit->active = nk_style_item_color(table[NK_COLOR_PROPERTY]); + edit->border_color = nk_rgba(0,0,0,0); + edit->cursor_normal = table[NK_COLOR_TEXT]; + edit->cursor_hover = table[NK_COLOR_TEXT]; + edit->cursor_text_normal= table[NK_COLOR_EDIT]; + edit->cursor_text_hover = table[NK_COLOR_EDIT]; + edit->text_normal = table[NK_COLOR_TEXT]; + edit->text_hover = table[NK_COLOR_TEXT]; + edit->text_active = table[NK_COLOR_TEXT]; + edit->selected_normal = table[NK_COLOR_TEXT]; + edit->selected_hover = table[NK_COLOR_TEXT]; + edit->selected_text_normal = table[NK_COLOR_EDIT]; + edit->selected_text_hover = table[NK_COLOR_EDIT]; + edit->padding = nk_vec2(0,0); + edit->cursor_size = 8; + edit->border = 0; + edit->rounding = 0; + + /* chart */ + chart = &style->chart; + nk_zero_struct(*chart); + chart->background = nk_style_item_color(table[NK_COLOR_CHART]); + chart->border_color = table[NK_COLOR_BORDER]; + chart->selected_color = table[NK_COLOR_CHART_COLOR_HIGHLIGHT]; + chart->color = table[NK_COLOR_CHART_COLOR]; + chart->padding = nk_vec2(4,4); + chart->border = 0; + chart->rounding = 0; + + /* combo */ + combo = &style->combo; + combo->normal = nk_style_item_color(table[NK_COLOR_COMBO]); + combo->hover = nk_style_item_color(table[NK_COLOR_COMBO]); + combo->active = nk_style_item_color(table[NK_COLOR_COMBO]); + combo->border_color = table[NK_COLOR_BORDER]; + combo->label_normal = table[NK_COLOR_TEXT]; + combo->label_hover = table[NK_COLOR_TEXT]; + combo->label_active = table[NK_COLOR_TEXT]; + combo->sym_normal = NK_SYMBOL_TRIANGLE_DOWN; + combo->sym_hover = NK_SYMBOL_TRIANGLE_DOWN; + combo->sym_active = NK_SYMBOL_TRIANGLE_DOWN; + combo->content_padding = nk_vec2(4,4); + combo->button_padding = nk_vec2(0,4); + combo->spacing = nk_vec2(4,0); + combo->border = 1; + combo->rounding = 0; + + /* combo button */ + button = &style->combo.button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_COMBO]); + button->hover = nk_style_item_color(table[NK_COLOR_COMBO]); + button->active = nk_style_item_color(table[NK_COLOR_COMBO]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_COMBO]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* tab */ + tab = &style->tab; + tab->background = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); + tab->border_color = table[NK_COLOR_BORDER]; + tab->text = table[NK_COLOR_TEXT]; + tab->sym_minimize = NK_SYMBOL_TRIANGLE_RIGHT; + tab->sym_maximize = NK_SYMBOL_TRIANGLE_DOWN; + tab->padding = nk_vec2(4,4); + tab->spacing = nk_vec2(4,4); + tab->indent = 10.0f; + tab->border = 1; + tab->rounding = 0; + + /* tab button */ + button = &style->tab.tab_minimize_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); + button->hover = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); + button->active = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_TAB_HEADER]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + style->tab.tab_maximize_button =*button; + + /* node button */ + button = &style->tab.node_minimize_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->hover = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->active = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_TAB_HEADER]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + style->tab.node_maximize_button =*button; + + /* window header */ + win = &style->window; + win->header.align = NK_HEADER_RIGHT; + win->header.close_symbol = NK_SYMBOL_X; + win->header.minimize_symbol = NK_SYMBOL_MINUS; + win->header.maximize_symbol = NK_SYMBOL_PLUS; + win->header.normal = nk_style_item_color(table[NK_COLOR_HEADER]); + win->header.hover = nk_style_item_color(table[NK_COLOR_HEADER]); + win->header.active = nk_style_item_color(table[NK_COLOR_HEADER]); + win->header.label_normal = table[NK_COLOR_TEXT]; + win->header.label_hover = table[NK_COLOR_TEXT]; + win->header.label_active = table[NK_COLOR_TEXT]; + win->header.label_padding = nk_vec2(4,4); + win->header.padding = nk_vec2(4,4); + win->header.spacing = nk_vec2(0,0); + + /* window header close button */ + button = &style->window.header.close_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_HEADER]); + button->hover = nk_style_item_color(table[NK_COLOR_HEADER]); + button->active = nk_style_item_color(table[NK_COLOR_HEADER]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_HEADER]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(0.0f,0.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* window header minimize button */ + button = &style->window.header.minimize_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_HEADER]); + button->hover = nk_style_item_color(table[NK_COLOR_HEADER]); + button->active = nk_style_item_color(table[NK_COLOR_HEADER]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_HEADER]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(0.0f,0.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* window */ + win->background = table[NK_COLOR_WINDOW]; + win->fixed_background = nk_style_item_color(table[NK_COLOR_WINDOW]); + win->border_color = table[NK_COLOR_BORDER]; + win->popup_border_color = table[NK_COLOR_BORDER]; + win->combo_border_color = table[NK_COLOR_BORDER]; + win->contextual_border_color = table[NK_COLOR_BORDER]; + win->menu_border_color = table[NK_COLOR_BORDER]; + win->group_border_color = table[NK_COLOR_BORDER]; + win->tooltip_border_color = table[NK_COLOR_BORDER]; + win->scaler = nk_style_item_color(table[NK_COLOR_TEXT]); + + win->rounding = 0.0f; + win->spacing = nk_vec2(4,4); + win->scrollbar_size = nk_vec2(10,10); + win->min_size = nk_vec2(64,64); + + win->combo_border = 1.0f; + win->contextual_border = 1.0f; + win->menu_border = 1.0f; + win->group_border = 1.0f; + win->tooltip_border = 1.0f; + win->popup_border = 1.0f; + win->border = 2.0f; + win->min_row_height_padding = 8; + + win->padding = nk_vec2(4,4); + win->group_padding = nk_vec2(4,4); + win->popup_padding = nk_vec2(4,4); + win->combo_padding = nk_vec2(4,4); + win->contextual_padding = nk_vec2(4,4); + win->menu_padding = nk_vec2(4,4); + win->tooltip_padding = nk_vec2(4,4); +} +NK_API void +nk_style_set_font(struct nk_context *ctx, const struct nk_user_font *font) +{ + struct nk_style *style; + NK_ASSERT(ctx); + + if (!ctx) return; + style = &ctx->style; + style->font = font; + ctx->stacks.fonts.head = 0; + if (ctx->current) + nk_layout_reset_min_row_height(ctx); +} +NK_API nk_bool +nk_style_push_font(struct nk_context *ctx, const struct nk_user_font *font) +{ + struct nk_config_stack_user_font *font_stack; + struct nk_config_stack_user_font_element *element; + + NK_ASSERT(ctx); + if (!ctx) return 0; + + font_stack = &ctx->stacks.fonts; + NK_ASSERT(font_stack->head < (int)NK_LEN(font_stack->elements)); + if (font_stack->head >= (int)NK_LEN(font_stack->elements)) + return 0; + + element = &font_stack->elements[font_stack->head++]; + element->address = &ctx->style.font; + element->old_value = ctx->style.font; + ctx->style.font = font; + return 1; +} +NK_API nk_bool +nk_style_pop_font(struct nk_context *ctx) +{ + struct nk_config_stack_user_font *font_stack; + struct nk_config_stack_user_font_element *element; + + NK_ASSERT(ctx); + if (!ctx) return 0; + + font_stack = &ctx->stacks.fonts; + NK_ASSERT(font_stack->head > 0); + if (font_stack->head < 1) + return 0; + + element = &font_stack->elements[--font_stack->head]; + *element->address = element->old_value; + return 1; +} +#define NK_STYLE_PUSH_IMPLEMENATION(prefix, type, stack) \ +nk_style_push_##type(struct nk_context *ctx, prefix##_##type *address, prefix##_##type value)\ +{\ + struct nk_config_stack_##type * type_stack;\ + struct nk_config_stack_##type##_element *element;\ + NK_ASSERT(ctx);\ + if (!ctx) return 0;\ + type_stack = &ctx->stacks.stack;\ + NK_ASSERT(type_stack->head < (int)NK_LEN(type_stack->elements));\ + if (type_stack->head >= (int)NK_LEN(type_stack->elements))\ + return 0;\ + element = &type_stack->elements[type_stack->head++];\ + element->address = address;\ + element->old_value = *address;\ + *address = value;\ + return 1;\ +} +#define NK_STYLE_POP_IMPLEMENATION(type, stack) \ +nk_style_pop_##type(struct nk_context *ctx)\ +{\ + struct nk_config_stack_##type *type_stack;\ + struct nk_config_stack_##type##_element *element;\ + NK_ASSERT(ctx);\ + if (!ctx) return 0;\ + type_stack = &ctx->stacks.stack;\ + NK_ASSERT(type_stack->head > 0);\ + if (type_stack->head < 1)\ + return 0;\ + element = &type_stack->elements[--type_stack->head];\ + *element->address = element->old_value;\ + return 1;\ +} +NK_API nk_bool NK_STYLE_PUSH_IMPLEMENATION(struct nk, style_item, style_items) +NK_API nk_bool NK_STYLE_PUSH_IMPLEMENATION(nk,float, floats) +NK_API nk_bool NK_STYLE_PUSH_IMPLEMENATION(struct nk, vec2, vectors) +NK_API nk_bool NK_STYLE_PUSH_IMPLEMENATION(nk,flags, flags) +NK_API nk_bool NK_STYLE_PUSH_IMPLEMENATION(struct nk,color, colors) + +NK_API nk_bool NK_STYLE_POP_IMPLEMENATION(style_item, style_items) +NK_API nk_bool NK_STYLE_POP_IMPLEMENATION(float,floats) +NK_API nk_bool NK_STYLE_POP_IMPLEMENATION(vec2, vectors) +NK_API nk_bool NK_STYLE_POP_IMPLEMENATION(flags,flags) +NK_API nk_bool NK_STYLE_POP_IMPLEMENATION(color,colors) + +NK_API nk_bool +nk_style_set_cursor(struct nk_context *ctx, enum nk_style_cursor c) +{ + struct nk_style *style; + NK_ASSERT(ctx); + if (!ctx) return 0; + style = &ctx->style; + if (style->cursors[c]) { + style->cursor_active = style->cursors[c]; + return 1; + } + return 0; +} +NK_API void +nk_style_show_cursor(struct nk_context *ctx) +{ + ctx->style.cursor_visible = nk_true; +} +NK_API void +nk_style_hide_cursor(struct nk_context *ctx) +{ + ctx->style.cursor_visible = nk_false; +} +NK_API void +nk_style_load_cursor(struct nk_context *ctx, enum nk_style_cursor cursor, + const struct nk_cursor *c) +{ + struct nk_style *style; + NK_ASSERT(ctx); + if (!ctx) return; + style = &ctx->style; + style->cursors[cursor] = c; +} +NK_API void +nk_style_load_all_cursors(struct nk_context *ctx, struct nk_cursor *cursors) +{ + int i = 0; + struct nk_style *style; + NK_ASSERT(ctx); + if (!ctx) return; + style = &ctx->style; + for (i = 0; i < NK_CURSOR_COUNT; ++i) + style->cursors[i] = &cursors[i]; + style->cursor_visible = nk_true; +} + + + + + +/* ============================================================== + * + * CONTEXT + * + * ===============================================================*/ +NK_INTERN void +nk_setup(struct nk_context *ctx, const struct nk_user_font *font) +{ + NK_ASSERT(ctx); + if (!ctx) return; + nk_zero_struct(*ctx); + nk_style_default(ctx); + ctx->seq = 1; + if (font) ctx->style.font = font; +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + nk_draw_list_init(&ctx->draw_list); +#endif +} +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API nk_bool +nk_init_default(struct nk_context *ctx, const struct nk_user_font *font) +{ + struct nk_allocator alloc; + alloc.userdata.ptr = 0; + alloc.alloc = nk_malloc; + alloc.free = nk_mfree; + return nk_init(ctx, &alloc, font); +} +#endif +NK_API nk_bool +nk_init_fixed(struct nk_context *ctx, void *memory, nk_size size, + const struct nk_user_font *font) +{ + NK_ASSERT(memory); + if (!memory) return 0; + nk_setup(ctx, font); + nk_buffer_init_fixed(&ctx->memory, memory, size); + ctx->use_pool = nk_false; + return 1; +} +NK_API nk_bool +nk_init_custom(struct nk_context *ctx, struct nk_buffer *cmds, + struct nk_buffer *pool, const struct nk_user_font *font) +{ + NK_ASSERT(cmds); + NK_ASSERT(pool); + if (!cmds || !pool) return 0; + + nk_setup(ctx, font); + ctx->memory = *cmds; + if (pool->type == NK_BUFFER_FIXED) { + /* take memory from buffer and alloc fixed pool */ + nk_pool_init_fixed(&ctx->pool, pool->memory.ptr, pool->memory.size); + } else { + /* create dynamic pool from buffer allocator */ + struct nk_allocator *alloc = &pool->pool; + nk_pool_init(&ctx->pool, alloc, NK_POOL_DEFAULT_CAPACITY); + } + ctx->use_pool = nk_true; + return 1; +} +NK_API nk_bool +nk_init(struct nk_context *ctx, struct nk_allocator *alloc, + const struct nk_user_font *font) +{ + NK_ASSERT(alloc); + if (!alloc) return 0; + nk_setup(ctx, font); + nk_buffer_init(&ctx->memory, alloc, NK_DEFAULT_COMMAND_BUFFER_SIZE); + nk_pool_init(&ctx->pool, alloc, NK_POOL_DEFAULT_CAPACITY); + ctx->use_pool = nk_true; + return 1; +} +#ifdef NK_INCLUDE_COMMAND_USERDATA +NK_API void +nk_set_user_data(struct nk_context *ctx, nk_handle handle) +{ + if (!ctx) return; + ctx->userdata = handle; + if (ctx->current) + ctx->current->buffer.userdata = handle; +} +#endif +NK_API void +nk_free(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + if (!ctx) return; + nk_buffer_free(&ctx->memory); + if (ctx->use_pool) + nk_pool_free(&ctx->pool); + + nk_zero(&ctx->input, sizeof(ctx->input)); + nk_zero(&ctx->style, sizeof(ctx->style)); + nk_zero(&ctx->memory, sizeof(ctx->memory)); + + ctx->seq = 0; + ctx->build = 0; + ctx->begin = 0; + ctx->end = 0; + ctx->active = 0; + ctx->current = 0; + ctx->freelist = 0; + ctx->count = 0; +} +NK_API void +nk_clear(struct nk_context *ctx) +{ + struct nk_window *iter; + struct nk_window *next; + NK_ASSERT(ctx); + + if (!ctx) return; + if (ctx->use_pool) + nk_buffer_clear(&ctx->memory); + else nk_buffer_reset(&ctx->memory, NK_BUFFER_FRONT); + + ctx->build = 0; + ctx->memory.calls = 0; + ctx->last_widget_state = 0; + ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_ARROW]; + NK_MEMSET(&ctx->overlay, 0, sizeof(ctx->overlay)); + + /* garbage collector */ + iter = ctx->begin; + while (iter) { + /* make sure valid minimized windows do not get removed */ + if ((iter->flags & NK_WINDOW_MINIMIZED) && + !(iter->flags & NK_WINDOW_CLOSED) && + iter->seq == ctx->seq) { + iter = iter->next; + continue; + } + /* remove hotness from hidden or closed windows*/ + if (((iter->flags & NK_WINDOW_HIDDEN) || + (iter->flags & NK_WINDOW_CLOSED)) && + iter == ctx->active) { + ctx->active = iter->prev; + ctx->end = iter->prev; + if (!ctx->end) + ctx->begin = 0; + if (ctx->active) + ctx->active->flags &= ~(unsigned)NK_WINDOW_ROM; + } + /* free unused popup windows */ + if (iter->popup.win && iter->popup.win->seq != ctx->seq) { + nk_free_window(ctx, iter->popup.win); + iter->popup.win = 0; + } + /* remove unused window state tables */ + {struct nk_table *n, *it = iter->tables; + while (it) { + n = it->next; + if (it->seq != ctx->seq) { + nk_remove_table(iter, it); + nk_zero(it, sizeof(union nk_page_data)); + nk_free_table(ctx, it); + if (it == iter->tables) + iter->tables = n; + } it = n; + }} + /* window itself is not used anymore so free */ + if (iter->seq != ctx->seq || iter->flags & NK_WINDOW_CLOSED) { + next = iter->next; + nk_remove_window(ctx, iter); + nk_free_window(ctx, iter); + iter = next; + } else iter = iter->next; + } + ctx->seq++; +} +NK_LIB void +nk_start_buffer(struct nk_context *ctx, struct nk_command_buffer *buffer) +{ + NK_ASSERT(ctx); + NK_ASSERT(buffer); + if (!ctx || !buffer) return; + buffer->begin = ctx->memory.allocated; + buffer->end = buffer->begin; + buffer->last = buffer->begin; + buffer->clip = nk_null_rect; +} +NK_LIB void +nk_start(struct nk_context *ctx, struct nk_window *win) +{ + NK_ASSERT(ctx); + NK_ASSERT(win); + nk_start_buffer(ctx, &win->buffer); +} +NK_LIB void +nk_start_popup(struct nk_context *ctx, struct nk_window *win) +{ + struct nk_popup_buffer *buf; + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!ctx || !win) return; + + /* save buffer fill state for popup */ + buf = &win->popup.buf; + buf->begin = win->buffer.end; + buf->end = win->buffer.end; + buf->parent = win->buffer.last; + buf->last = buf->begin; + buf->active = nk_true; +} +NK_LIB void +nk_finish_popup(struct nk_context *ctx, struct nk_window *win) +{ + struct nk_popup_buffer *buf; + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!ctx || !win) return; + + buf = &win->popup.buf; + buf->last = win->buffer.last; + buf->end = win->buffer.end; +} +NK_LIB void +nk_finish_buffer(struct nk_context *ctx, struct nk_command_buffer *buffer) +{ + NK_ASSERT(ctx); + NK_ASSERT(buffer); + if (!ctx || !buffer) return; + buffer->end = ctx->memory.allocated; +} +NK_LIB void +nk_finish(struct nk_context *ctx, struct nk_window *win) +{ + struct nk_popup_buffer *buf; + struct nk_command *parent_last; + void *memory; + + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!ctx || !win) return; + nk_finish_buffer(ctx, &win->buffer); + if (!win->popup.buf.active) return; + + buf = &win->popup.buf; + memory = ctx->memory.memory.ptr; + parent_last = nk_ptr_add(struct nk_command, memory, buf->parent); + parent_last->next = buf->end; +} +NK_LIB void +nk_build(struct nk_context *ctx) +{ + struct nk_window *it = 0; + struct nk_command *cmd = 0; + nk_byte *buffer = 0; + + /* draw cursor overlay */ + if (!ctx->style.cursor_active) + ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_ARROW]; + if (ctx->style.cursor_active && !ctx->input.mouse.grabbed && ctx->style.cursor_visible) { + struct nk_rect mouse_bounds; + const struct nk_cursor *cursor = ctx->style.cursor_active; + nk_command_buffer_init(&ctx->overlay, &ctx->memory, NK_CLIPPING_OFF); + nk_start_buffer(ctx, &ctx->overlay); + + mouse_bounds.x = ctx->input.mouse.pos.x - cursor->offset.x; + mouse_bounds.y = ctx->input.mouse.pos.y - cursor->offset.y; + mouse_bounds.w = cursor->size.x; + mouse_bounds.h = cursor->size.y; + + nk_draw_image(&ctx->overlay, mouse_bounds, &cursor->img, nk_white); + nk_finish_buffer(ctx, &ctx->overlay); + } + /* build one big draw command list out of all window buffers */ + it = ctx->begin; + buffer = (nk_byte*)ctx->memory.memory.ptr; + while (it != 0) { + struct nk_window *next = it->next; + if (it->buffer.last == it->buffer.begin || (it->flags & NK_WINDOW_HIDDEN)|| + it->seq != ctx->seq) + goto cont; + + cmd = nk_ptr_add(struct nk_command, buffer, it->buffer.last); + while (next && ((next->buffer.last == next->buffer.begin) || + (next->flags & NK_WINDOW_HIDDEN) || next->seq != ctx->seq)) + next = next->next; /* skip empty command buffers */ + + if (next) cmd->next = next->buffer.begin; + cont: it = next; + } + /* append all popup draw commands into lists */ + it = ctx->begin; + while (it != 0) { + struct nk_window *next = it->next; + struct nk_popup_buffer *buf; + if (!it->popup.buf.active) + goto skip; + + buf = &it->popup.buf; + cmd->next = buf->begin; + cmd = nk_ptr_add(struct nk_command, buffer, buf->last); + buf->active = nk_false; + skip: it = next; + } + if (cmd) { + /* append overlay commands */ + if (ctx->overlay.end != ctx->overlay.begin) + cmd->next = ctx->overlay.begin; + else cmd->next = ctx->memory.allocated; + } +} +NK_API const struct nk_command* +nk__begin(struct nk_context *ctx) +{ + struct nk_window *iter; + nk_byte *buffer; + NK_ASSERT(ctx); + if (!ctx) return 0; + if (!ctx->count) return 0; + + buffer = (nk_byte*)ctx->memory.memory.ptr; + if (!ctx->build) { + nk_build(ctx); + ctx->build = nk_true; + } + iter = ctx->begin; + while (iter && ((iter->buffer.begin == iter->buffer.end) || + (iter->flags & NK_WINDOW_HIDDEN) || iter->seq != ctx->seq)) + iter = iter->next; + if (!iter) return 0; + return nk_ptr_add_const(struct nk_command, buffer, iter->buffer.begin); +} + +NK_API const struct nk_command* +nk__next(struct nk_context *ctx, const struct nk_command *cmd) +{ + nk_byte *buffer; + const struct nk_command *next; + NK_ASSERT(ctx); + if (!ctx || !cmd || !ctx->count) return 0; + if (cmd->next >= ctx->memory.allocated) return 0; + buffer = (nk_byte*)ctx->memory.memory.ptr; + next = nk_ptr_add_const(struct nk_command, buffer, cmd->next); + return next; +} + + + + + + +/* =============================================================== + * + * POOL + * + * ===============================================================*/ +NK_LIB void +nk_pool_init(struct nk_pool *pool, struct nk_allocator *alloc, + unsigned int capacity) +{ + NK_ASSERT(capacity >= 1); + nk_zero(pool, sizeof(*pool)); + pool->alloc = *alloc; + pool->capacity = capacity; + pool->type = NK_BUFFER_DYNAMIC; + pool->pages = 0; +} +NK_LIB void +nk_pool_free(struct nk_pool *pool) +{ + struct nk_page *iter; + if (!pool) return; + iter = pool->pages; + if (pool->type == NK_BUFFER_FIXED) return; + while (iter) { + struct nk_page *next = iter->next; + pool->alloc.free(pool->alloc.userdata, iter); + iter = next; + } +} +NK_LIB void +nk_pool_init_fixed(struct nk_pool *pool, void *memory, nk_size size) +{ + nk_zero(pool, sizeof(*pool)); + NK_ASSERT(size >= sizeof(struct nk_page)); + if (size < sizeof(struct nk_page)) return; + /* first nk_page_element is embedded in nk_page, additional elements follow in adjacent space */ + pool->capacity = 1 + (unsigned)(size - sizeof(struct nk_page)) / sizeof(struct nk_page_element); + pool->pages = (struct nk_page*)memory; + pool->type = NK_BUFFER_FIXED; + pool->size = size; +} +NK_LIB struct nk_page_element* +nk_pool_alloc(struct nk_pool *pool) +{ + if (!pool->pages || pool->pages->size >= pool->capacity) { + /* allocate new page */ + struct nk_page *page; + if (pool->type == NK_BUFFER_FIXED) { + NK_ASSERT(pool->pages); + if (!pool->pages) return 0; + NK_ASSERT(pool->pages->size < pool->capacity); + return 0; + } else { + nk_size size = sizeof(struct nk_page); + size += (pool->capacity - 1) * sizeof(struct nk_page_element); + page = (struct nk_page*)pool->alloc.alloc(pool->alloc.userdata,0, size); + page->next = pool->pages; + pool->pages = page; + page->size = 0; + } + } return &pool->pages->win[pool->pages->size++]; +} + + + + + +/* =============================================================== + * + * PAGE ELEMENT + * + * ===============================================================*/ +NK_LIB struct nk_page_element* +nk_create_page_element(struct nk_context *ctx) +{ + struct nk_page_element *elem; + if (ctx->freelist) { + /* unlink page element from free list */ + elem = ctx->freelist; + ctx->freelist = elem->next; + } else if (ctx->use_pool) { + /* allocate page element from memory pool */ + elem = nk_pool_alloc(&ctx->pool); + NK_ASSERT(elem); + if (!elem) return 0; + } else { + /* allocate new page element from back of fixed size memory buffer */ + NK_STORAGE const nk_size size = sizeof(struct nk_page_element); + NK_STORAGE const nk_size align = NK_ALIGNOF(struct nk_page_element); + elem = (struct nk_page_element*)nk_buffer_alloc(&ctx->memory, NK_BUFFER_BACK, size, align); + NK_ASSERT(elem); + if (!elem) return 0; + } + nk_zero_struct(*elem); + elem->next = 0; + elem->prev = 0; + return elem; +} +NK_LIB void +nk_link_page_element_into_freelist(struct nk_context *ctx, + struct nk_page_element *elem) +{ + /* link table into freelist */ + if (!ctx->freelist) { + ctx->freelist = elem; + } else { + elem->next = ctx->freelist; + ctx->freelist = elem; + } +} +NK_LIB void +nk_free_page_element(struct nk_context *ctx, struct nk_page_element *elem) +{ + /* we have a pool so just add to free list */ + if (ctx->use_pool) { + nk_link_page_element_into_freelist(ctx, elem); + return; + } + /* if possible remove last element from back of fixed memory buffer */ + {void *elem_end = (void*)(elem + 1); + void *buffer_end = (nk_byte*)ctx->memory.memory.ptr + ctx->memory.size; + if (elem_end == buffer_end) + ctx->memory.size -= sizeof(struct nk_page_element); + else nk_link_page_element_into_freelist(ctx, elem);} +} + + + + + +/* =============================================================== + * + * TABLE + * + * ===============================================================*/ +NK_LIB struct nk_table* +nk_create_table(struct nk_context *ctx) +{ + struct nk_page_element *elem; + elem = nk_create_page_element(ctx); + if (!elem) return 0; + nk_zero_struct(*elem); + return &elem->data.tbl; +} +NK_LIB void +nk_free_table(struct nk_context *ctx, struct nk_table *tbl) +{ + union nk_page_data *pd = NK_CONTAINER_OF(tbl, union nk_page_data, tbl); + struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data); + nk_free_page_element(ctx, pe); +} +NK_LIB void +nk_push_table(struct nk_window *win, struct nk_table *tbl) +{ + if (!win->tables) { + win->tables = tbl; + tbl->next = 0; + tbl->prev = 0; + tbl->size = 0; + win->table_count = 1; + return; + } + win->tables->prev = tbl; + tbl->next = win->tables; + tbl->prev = 0; + tbl->size = 0; + win->tables = tbl; + win->table_count++; +} +NK_LIB void +nk_remove_table(struct nk_window *win, struct nk_table *tbl) +{ + if (win->tables == tbl) + win->tables = tbl->next; + if (tbl->next) + tbl->next->prev = tbl->prev; + if (tbl->prev) + tbl->prev->next = tbl->next; + tbl->next = 0; + tbl->prev = 0; +} +NK_LIB nk_uint* +nk_add_value(struct nk_context *ctx, struct nk_window *win, + nk_hash name, nk_uint value) +{ + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!win || !ctx) return 0; + if (!win->tables || win->tables->size >= NK_VALUE_PAGE_CAPACITY) { + struct nk_table *tbl = nk_create_table(ctx); + NK_ASSERT(tbl); + if (!tbl) return 0; + nk_push_table(win, tbl); + } + win->tables->seq = win->seq; + win->tables->keys[win->tables->size] = name; + win->tables->values[win->tables->size] = value; + return &win->tables->values[win->tables->size++]; +} +NK_LIB nk_uint* +nk_find_value(struct nk_window *win, nk_hash name) +{ + struct nk_table *iter = win->tables; + while (iter) { + unsigned int i = 0; + unsigned int size = iter->size; + for (i = 0; i < size; ++i) { + if (iter->keys[i] == name) { + iter->seq = win->seq; + return &iter->values[i]; + } + } size = NK_VALUE_PAGE_CAPACITY; + iter = iter->next; + } + return 0; +} + + + + + +/* =============================================================== + * + * PANEL + * + * ===============================================================*/ +NK_LIB void* +nk_create_panel(struct nk_context *ctx) +{ + struct nk_page_element *elem; + elem = nk_create_page_element(ctx); + if (!elem) return 0; + nk_zero_struct(*elem); + return &elem->data.pan; +} +NK_LIB void +nk_free_panel(struct nk_context *ctx, struct nk_panel *pan) +{ + union nk_page_data *pd = NK_CONTAINER_OF(pan, union nk_page_data, pan); + struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data); + nk_free_page_element(ctx, pe); +} +NK_LIB nk_bool +nk_panel_has_header(nk_flags flags, const char *title) +{ + nk_bool active = 0; + active = (flags & (NK_WINDOW_CLOSABLE|NK_WINDOW_MINIMIZABLE)); + active = active || (flags & NK_WINDOW_TITLE); + active = active && !(flags & NK_WINDOW_HIDDEN) && title; + return active; +} +NK_LIB struct nk_vec2 +nk_panel_get_padding(const struct nk_style *style, enum nk_panel_type type) +{ + switch (type) { + default: + case NK_PANEL_WINDOW: return style->window.padding; + case NK_PANEL_GROUP: return style->window.group_padding; + case NK_PANEL_POPUP: return style->window.popup_padding; + case NK_PANEL_CONTEXTUAL: return style->window.contextual_padding; + case NK_PANEL_COMBO: return style->window.combo_padding; + case NK_PANEL_MENU: return style->window.menu_padding; + case NK_PANEL_TOOLTIP: return style->window.menu_padding;} +} +NK_LIB float +nk_panel_get_border(const struct nk_style *style, nk_flags flags, + enum nk_panel_type type) +{ + if (flags & NK_WINDOW_BORDER) { + switch (type) { + default: + case NK_PANEL_WINDOW: return style->window.border; + case NK_PANEL_GROUP: return style->window.group_border; + case NK_PANEL_POPUP: return style->window.popup_border; + case NK_PANEL_CONTEXTUAL: return style->window.contextual_border; + case NK_PANEL_COMBO: return style->window.combo_border; + case NK_PANEL_MENU: return style->window.menu_border; + case NK_PANEL_TOOLTIP: return style->window.menu_border; + }} else return 0; +} +NK_LIB struct nk_color +nk_panel_get_border_color(const struct nk_style *style, enum nk_panel_type type) +{ + switch (type) { + default: + case NK_PANEL_WINDOW: return style->window.border_color; + case NK_PANEL_GROUP: return style->window.group_border_color; + case NK_PANEL_POPUP: return style->window.popup_border_color; + case NK_PANEL_CONTEXTUAL: return style->window.contextual_border_color; + case NK_PANEL_COMBO: return style->window.combo_border_color; + case NK_PANEL_MENU: return style->window.menu_border_color; + case NK_PANEL_TOOLTIP: return style->window.menu_border_color;} +} +NK_LIB nk_bool +nk_panel_is_sub(enum nk_panel_type type) +{ + return (type & NK_PANEL_SET_SUB)?1:0; +} +NK_LIB nk_bool +nk_panel_is_nonblock(enum nk_panel_type type) +{ + return (type & NK_PANEL_SET_NONBLOCK)?1:0; +} +NK_LIB nk_bool +nk_panel_begin(struct nk_context *ctx, const char *title, enum nk_panel_type panel_type) +{ + struct nk_input *in; + struct nk_window *win; + struct nk_panel *layout; + struct nk_command_buffer *out; + const struct nk_style *style; + const struct nk_user_font *font; + + struct nk_vec2 scrollbar_size; + struct nk_vec2 panel_padding; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return 0; + nk_zero(ctx->current->layout, sizeof(*ctx->current->layout)); + if ((ctx->current->flags & NK_WINDOW_HIDDEN) || (ctx->current->flags & NK_WINDOW_CLOSED)) { + nk_zero(ctx->current->layout, sizeof(struct nk_panel)); + ctx->current->layout->type = panel_type; + return 0; + } + /* pull state into local stack */ + style = &ctx->style; + font = style->font; + win = ctx->current; + layout = win->layout; + out = &win->buffer; + in = (win->flags & NK_WINDOW_NO_INPUT) ? 0: &ctx->input; +#ifdef NK_INCLUDE_COMMAND_USERDATA + win->buffer.userdata = ctx->userdata; +#endif + /* pull style configuration into local stack */ + scrollbar_size = style->window.scrollbar_size; + panel_padding = nk_panel_get_padding(style, panel_type); + + /* window movement */ + if ((win->flags & NK_WINDOW_MOVABLE) && !(win->flags & NK_WINDOW_ROM)) { + int left_mouse_down; + int left_mouse_clicked; + int left_mouse_click_in_cursor; + + /* calculate draggable window space */ + struct nk_rect header; + header.x = win->bounds.x; + header.y = win->bounds.y; + header.w = win->bounds.w; + if (nk_panel_has_header(win->flags, title)) { + header.h = font->height + 2.0f * style->window.header.padding.y; + header.h += 2.0f * style->window.header.label_padding.y; + } else header.h = panel_padding.y; + + /* window movement by dragging */ + left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; + left_mouse_clicked = (int)in->mouse.buttons[NK_BUTTON_LEFT].clicked; + left_mouse_click_in_cursor = nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, header, nk_true); + if (left_mouse_down && left_mouse_click_in_cursor && !left_mouse_clicked) { + win->bounds.x = win->bounds.x + in->mouse.delta.x; + win->bounds.y = win->bounds.y + in->mouse.delta.y; + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x += in->mouse.delta.x; + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y += in->mouse.delta.y; + ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_MOVE]; + } + } + + /* setup panel */ + layout->type = panel_type; + layout->flags = win->flags; + layout->bounds = win->bounds; + layout->bounds.x += panel_padding.x; + layout->bounds.w -= 2*panel_padding.x; + if (win->flags & NK_WINDOW_BORDER) { + layout->border = nk_panel_get_border(style, win->flags, panel_type); + layout->bounds = nk_shrink_rect(layout->bounds, layout->border); + } else layout->border = 0; + layout->at_y = layout->bounds.y; + layout->at_x = layout->bounds.x; + layout->max_x = 0; + layout->header_height = 0; + layout->footer_height = 0; + nk_layout_reset_min_row_height(ctx); + layout->row.index = 0; + layout->row.columns = 0; + layout->row.ratio = 0; + layout->row.item_width = 0; + layout->row.tree_depth = 0; + layout->row.height = panel_padding.y; + layout->has_scrolling = nk_true; + if (!(win->flags & NK_WINDOW_NO_SCROLLBAR)) + layout->bounds.w -= scrollbar_size.x; + if (!nk_panel_is_nonblock(panel_type)) { + layout->footer_height = 0; + if (!(win->flags & NK_WINDOW_NO_SCROLLBAR) || win->flags & NK_WINDOW_SCALABLE) + layout->footer_height = scrollbar_size.y; + layout->bounds.h -= layout->footer_height; + } + + /* panel header */ + if (nk_panel_has_header(win->flags, title)) + { + struct nk_text text; + struct nk_rect header; + const struct nk_style_item *background = 0; + + /* calculate header bounds */ + header.x = win->bounds.x; + header.y = win->bounds.y; + header.w = win->bounds.w; + header.h = font->height + 2.0f * style->window.header.padding.y; + header.h += (2.0f * style->window.header.label_padding.y); + + /* shrink panel by header */ + layout->header_height = header.h; + layout->bounds.y += header.h; + layout->bounds.h -= header.h; + layout->at_y += header.h; + + /* select correct header background and text color */ + if (ctx->active == win) { + background = &style->window.header.active; + text.text = style->window.header.label_active; + } else if (nk_input_is_mouse_hovering_rect(&ctx->input, header)) { + background = &style->window.header.hover; + text.text = style->window.header.label_hover; + } else { + background = &style->window.header.normal; + text.text = style->window.header.label_normal; + } + + /* draw header background */ + header.h += 1.0f; + if (background->type == NK_STYLE_ITEM_IMAGE) { + text.background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image, nk_white); + } else { + text.background = background->data.color; + nk_fill_rect(out, header, 0, background->data.color); + } + + /* window close button */ + {struct nk_rect button; + button.y = header.y + style->window.header.padding.y; + button.h = header.h - 2 * style->window.header.padding.y; + button.w = button.h; + if (win->flags & NK_WINDOW_CLOSABLE) { + nk_flags ws = 0; + if (style->window.header.align == NK_HEADER_RIGHT) { + button.x = (header.w + header.x) - (button.w + style->window.header.padding.x); + header.w -= button.w + style->window.header.spacing.x + style->window.header.padding.x; + } else { + button.x = header.x + style->window.header.padding.x; + header.x += button.w + style->window.header.spacing.x + style->window.header.padding.x; + } + + if (nk_do_button_symbol(&ws, &win->buffer, button, + style->window.header.close_symbol, NK_BUTTON_DEFAULT, + &style->window.header.close_button, in, style->font) && !(win->flags & NK_WINDOW_ROM)) + { + layout->flags |= NK_WINDOW_HIDDEN; + layout->flags &= (nk_flags)~NK_WINDOW_MINIMIZED; + } + } + + /* window minimize button */ + if (win->flags & NK_WINDOW_MINIMIZABLE) { + nk_flags ws = 0; + if (style->window.header.align == NK_HEADER_RIGHT) { + button.x = (header.w + header.x) - button.w; + if (!(win->flags & NK_WINDOW_CLOSABLE)) { + button.x -= style->window.header.padding.x; + header.w -= style->window.header.padding.x; + } + header.w -= button.w + style->window.header.spacing.x; + } else { + button.x = header.x; + header.x += button.w + style->window.header.spacing.x + style->window.header.padding.x; + } + if (nk_do_button_symbol(&ws, &win->buffer, button, (layout->flags & NK_WINDOW_MINIMIZED)? + style->window.header.maximize_symbol: style->window.header.minimize_symbol, + NK_BUTTON_DEFAULT, &style->window.header.minimize_button, in, style->font) && !(win->flags & NK_WINDOW_ROM)) + layout->flags = (layout->flags & NK_WINDOW_MINIMIZED) ? + layout->flags & (nk_flags)~NK_WINDOW_MINIMIZED: + layout->flags | NK_WINDOW_MINIMIZED; + }} + + {/* window header title */ + int text_len = nk_strlen(title); + struct nk_rect label = {0,0,0,0}; + float t = font->width(font->userdata, font->height, title, text_len); + text.padding = nk_vec2(0,0); + + label.x = header.x + style->window.header.padding.x; + label.x += style->window.header.label_padding.x; + label.y = header.y + style->window.header.label_padding.y; + label.h = font->height + 2 * style->window.header.label_padding.y; + label.w = t + 2 * style->window.header.spacing.x; + label.w = NK_CLAMP(0, label.w, header.x + header.w - label.x); + nk_widget_text(out, label,(const char*)title, text_len, &text, NK_TEXT_LEFT, font);} + } + + /* draw window background */ + if (!(layout->flags & NK_WINDOW_MINIMIZED) && !(layout->flags & NK_WINDOW_DYNAMIC)) { + struct nk_rect body; + body.x = win->bounds.x; + body.w = win->bounds.w; + body.y = (win->bounds.y + layout->header_height); + body.h = (win->bounds.h - layout->header_height); + if (style->window.fixed_background.type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, body, &style->window.fixed_background.data.image, nk_white); + else nk_fill_rect(out, body, 0, style->window.fixed_background.data.color); + } + + /* set clipping rectangle */ + {struct nk_rect clip; + layout->clip = layout->bounds; + nk_unify(&clip, &win->buffer.clip, layout->clip.x, layout->clip.y, + layout->clip.x + layout->clip.w, layout->clip.y + layout->clip.h); + nk_push_scissor(out, clip); + layout->clip = clip;} + return !(layout->flags & NK_WINDOW_HIDDEN) && !(layout->flags & NK_WINDOW_MINIMIZED); +} +NK_LIB void +nk_panel_end(struct nk_context *ctx) +{ + struct nk_input *in; + struct nk_window *window; + struct nk_panel *layout; + const struct nk_style *style; + struct nk_command_buffer *out; + + struct nk_vec2 scrollbar_size; + struct nk_vec2 panel_padding; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + window = ctx->current; + layout = window->layout; + style = &ctx->style; + out = &window->buffer; + in = (layout->flags & NK_WINDOW_ROM || layout->flags & NK_WINDOW_NO_INPUT) ? 0 :&ctx->input; + if (!nk_panel_is_sub(layout->type)) + nk_push_scissor(out, nk_null_rect); + + /* cache configuration data */ + scrollbar_size = style->window.scrollbar_size; + panel_padding = nk_panel_get_padding(style, layout->type); + + /* update the current cursor Y-position to point over the last added widget */ + layout->at_y += layout->row.height; + + /* dynamic panels */ + if (layout->flags & NK_WINDOW_DYNAMIC && !(layout->flags & NK_WINDOW_MINIMIZED)) + { + /* update panel height to fit dynamic growth */ + struct nk_rect empty_space; + if (layout->at_y < (layout->bounds.y + layout->bounds.h)) + layout->bounds.h = layout->at_y - layout->bounds.y; + + /* fill top empty space */ + empty_space.x = window->bounds.x; + empty_space.y = layout->bounds.y; + empty_space.h = panel_padding.y; + empty_space.w = window->bounds.w; + nk_fill_rect(out, empty_space, 0, style->window.background); + + /* fill left empty space */ + empty_space.x = window->bounds.x; + empty_space.y = layout->bounds.y; + empty_space.w = panel_padding.x + layout->border; + empty_space.h = layout->bounds.h; + nk_fill_rect(out, empty_space, 0, style->window.background); + + /* fill right empty space */ + empty_space.x = layout->bounds.x + layout->bounds.w; + empty_space.y = layout->bounds.y; + empty_space.w = panel_padding.x + layout->border; + empty_space.h = layout->bounds.h; + if (*layout->offset_y == 0 && !(layout->flags & NK_WINDOW_NO_SCROLLBAR)) + empty_space.w += scrollbar_size.x; + nk_fill_rect(out, empty_space, 0, style->window.background); + + /* fill bottom empty space */ + if (layout->footer_height > 0) { + empty_space.x = window->bounds.x; + empty_space.y = layout->bounds.y + layout->bounds.h; + empty_space.w = window->bounds.w; + empty_space.h = layout->footer_height; + nk_fill_rect(out, empty_space, 0, style->window.background); + } + } + + /* scrollbars */ + if (!(layout->flags & NK_WINDOW_NO_SCROLLBAR) && + !(layout->flags & NK_WINDOW_MINIMIZED) && + window->scrollbar_hiding_timer < NK_SCROLLBAR_HIDING_TIMEOUT) + { + struct nk_rect scroll; + int scroll_has_scrolling; + float scroll_target; + float scroll_offset; + float scroll_step; + float scroll_inc; + + /* mouse wheel scrolling */ + if (nk_panel_is_sub(layout->type)) + { + /* sub-window mouse wheel scrolling */ + struct nk_window *root_window = window; + struct nk_panel *root_panel = window->layout; + while (root_panel->parent) + root_panel = root_panel->parent; + while (root_window->parent) + root_window = root_window->parent; + + /* only allow scrolling if parent window is active */ + scroll_has_scrolling = 0; + if ((root_window == ctx->active) && layout->has_scrolling) { + /* and panel is being hovered and inside clip rect*/ + if (nk_input_is_mouse_hovering_rect(in, layout->bounds) && + NK_INTERSECT(layout->bounds.x, layout->bounds.y, layout->bounds.w, layout->bounds.h, + root_panel->clip.x, root_panel->clip.y, root_panel->clip.w, root_panel->clip.h)) + { + /* deactivate all parent scrolling */ + root_panel = window->layout; + while (root_panel->parent) { + root_panel->has_scrolling = nk_false; + root_panel = root_panel->parent; + } + root_panel->has_scrolling = nk_false; + scroll_has_scrolling = nk_true; + } + } + } else if (!nk_panel_is_sub(layout->type)) { + /* window mouse wheel scrolling */ + scroll_has_scrolling = (window == ctx->active) && layout->has_scrolling; + if (in && (in->mouse.scroll_delta.y > 0 || in->mouse.scroll_delta.x > 0) && scroll_has_scrolling) + window->scrolled = nk_true; + else window->scrolled = nk_false; + } else scroll_has_scrolling = nk_false; + + { + /* vertical scrollbar */ + nk_flags state = 0; + scroll.x = layout->bounds.x + layout->bounds.w + panel_padding.x; + scroll.y = layout->bounds.y; + scroll.w = scrollbar_size.x; + scroll.h = layout->bounds.h; + + scroll_offset = (float)*layout->offset_y; + scroll_step = scroll.h * 0.10f; + scroll_inc = scroll.h * 0.01f; + scroll_target = (float)(int)(layout->at_y - scroll.y); + scroll_offset = nk_do_scrollbarv(&state, out, scroll, scroll_has_scrolling, + scroll_offset, scroll_target, scroll_step, scroll_inc, + &ctx->style.scrollv, in, style->font); + *layout->offset_y = (nk_uint)scroll_offset; + if (in && scroll_has_scrolling) + in->mouse.scroll_delta.y = 0; + } + { + /* horizontal scrollbar */ + nk_flags state = 0; + scroll.x = layout->bounds.x; + scroll.y = layout->bounds.y + layout->bounds.h; + scroll.w = layout->bounds.w; + scroll.h = scrollbar_size.y; + + scroll_offset = (float)*layout->offset_x; + scroll_target = (float)(int)(layout->max_x - scroll.x); + scroll_step = layout->max_x * 0.05f; + scroll_inc = layout->max_x * 0.005f; + scroll_offset = nk_do_scrollbarh(&state, out, scroll, scroll_has_scrolling, + scroll_offset, scroll_target, scroll_step, scroll_inc, + &ctx->style.scrollh, in, style->font); + *layout->offset_x = (nk_uint)scroll_offset; + } + } + + /* hide scroll if no user input */ + if (window->flags & NK_WINDOW_SCROLL_AUTO_HIDE) { + int has_input = ctx->input.mouse.delta.x != 0 || ctx->input.mouse.delta.y != 0 || ctx->input.mouse.scroll_delta.y != 0; + int is_window_hovered = nk_window_is_hovered(ctx); + int any_item_active = (ctx->last_widget_state & NK_WIDGET_STATE_MODIFIED); + if ((!has_input && is_window_hovered) || (!is_window_hovered && !any_item_active)) + window->scrollbar_hiding_timer += ctx->delta_time_seconds; + else window->scrollbar_hiding_timer = 0; + } else window->scrollbar_hiding_timer = 0; + + /* window border */ + if (layout->flags & NK_WINDOW_BORDER) + { + struct nk_color border_color = nk_panel_get_border_color(style, layout->type); + const float padding_y = (layout->flags & NK_WINDOW_MINIMIZED) + ? (style->window.border + window->bounds.y + layout->header_height) + : ((layout->flags & NK_WINDOW_DYNAMIC) + ? (layout->bounds.y + layout->bounds.h + layout->footer_height) + : (window->bounds.y + window->bounds.h)); + struct nk_rect b = window->bounds; + b.h = padding_y - window->bounds.y; + nk_stroke_rect(out, b, 0, layout->border, border_color); + } + + /* scaler */ + if ((layout->flags & NK_WINDOW_SCALABLE) && in && !(layout->flags & NK_WINDOW_MINIMIZED)) + { + /* calculate scaler bounds */ + struct nk_rect scaler; + scaler.w = scrollbar_size.x; + scaler.h = scrollbar_size.y; + scaler.y = layout->bounds.y + layout->bounds.h; + if (layout->flags & NK_WINDOW_SCALE_LEFT) + scaler.x = layout->bounds.x - panel_padding.x * 0.5f; + else scaler.x = layout->bounds.x + layout->bounds.w + panel_padding.x; + if (layout->flags & NK_WINDOW_NO_SCROLLBAR) + scaler.x -= scaler.w; + + /* draw scaler */ + {const struct nk_style_item *item = &style->window.scaler; + if (item->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, scaler, &item->data.image, nk_white); + else { + if (layout->flags & NK_WINDOW_SCALE_LEFT) { + nk_fill_triangle(out, scaler.x, scaler.y, scaler.x, + scaler.y + scaler.h, scaler.x + scaler.w, + scaler.y + scaler.h, item->data.color); + } else { + nk_fill_triangle(out, scaler.x + scaler.w, scaler.y, scaler.x + scaler.w, + scaler.y + scaler.h, scaler.x, scaler.y + scaler.h, item->data.color); + } + }} + + /* do window scaling */ + if (!(window->flags & NK_WINDOW_ROM)) { + struct nk_vec2 window_size = style->window.min_size; + int left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; + int left_mouse_click_in_scaler = nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, scaler, nk_true); + + if (left_mouse_down && left_mouse_click_in_scaler) { + float delta_x = in->mouse.delta.x; + if (layout->flags & NK_WINDOW_SCALE_LEFT) { + delta_x = -delta_x; + window->bounds.x += in->mouse.delta.x; + } + /* dragging in x-direction */ + if (window->bounds.w + delta_x >= window_size.x) { + if ((delta_x < 0) || (delta_x > 0 && in->mouse.pos.x >= scaler.x)) { + window->bounds.w = window->bounds.w + delta_x; + scaler.x += in->mouse.delta.x; + } + } + /* dragging in y-direction (only possible if static window) */ + if (!(layout->flags & NK_WINDOW_DYNAMIC)) { + if (window_size.y < window->bounds.h + in->mouse.delta.y) { + if ((in->mouse.delta.y < 0) || (in->mouse.delta.y > 0 && in->mouse.pos.y >= scaler.y)) { + window->bounds.h = window->bounds.h + in->mouse.delta.y; + scaler.y += in->mouse.delta.y; + } + } + } + ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_RESIZE_TOP_RIGHT_DOWN_LEFT]; + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = scaler.x + scaler.w/2.0f; + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y = scaler.y + scaler.h/2.0f; + } + } + } + if (!nk_panel_is_sub(layout->type)) { + /* window is hidden so clear command buffer */ + if (layout->flags & NK_WINDOW_HIDDEN) + nk_command_buffer_reset(&window->buffer); + /* window is visible and not tab */ + else nk_finish(ctx, window); + } + + /* NK_WINDOW_REMOVE_ROM flag was set so remove NK_WINDOW_ROM */ + if (layout->flags & NK_WINDOW_REMOVE_ROM) { + layout->flags &= ~(nk_flags)NK_WINDOW_ROM; + layout->flags &= ~(nk_flags)NK_WINDOW_REMOVE_ROM; + } + window->flags = layout->flags; + + /* property garbage collector */ + if (window->property.active && window->property.old != window->property.seq && + window->property.active == window->property.prev) { + nk_zero(&window->property, sizeof(window->property)); + } else { + window->property.old = window->property.seq; + window->property.prev = window->property.active; + window->property.seq = 0; + } + /* edit garbage collector */ + if (window->edit.active && window->edit.old != window->edit.seq && + window->edit.active == window->edit.prev) { + nk_zero(&window->edit, sizeof(window->edit)); + } else { + window->edit.old = window->edit.seq; + window->edit.prev = window->edit.active; + window->edit.seq = 0; + } + /* contextual garbage collector */ + if (window->popup.active_con && window->popup.con_old != window->popup.con_count) { + window->popup.con_count = 0; + window->popup.con_old = 0; + window->popup.active_con = 0; + } else { + window->popup.con_old = window->popup.con_count; + window->popup.con_count = 0; + } + window->popup.combo_count = 0; + /* helper to make sure you have a 'nk_tree_push' for every 'nk_tree_pop' */ + NK_ASSERT(!layout->row.tree_depth); +} + + + + + +/* =============================================================== + * + * WINDOW + * + * ===============================================================*/ +NK_LIB void* +nk_create_window(struct nk_context *ctx) +{ + struct nk_page_element *elem; + elem = nk_create_page_element(ctx); + if (!elem) return 0; + elem->data.win.seq = ctx->seq; + return &elem->data.win; +} +NK_LIB void +nk_free_window(struct nk_context *ctx, struct nk_window *win) +{ + /* unlink windows from list */ + struct nk_table *it = win->tables; + if (win->popup.win) { + nk_free_window(ctx, win->popup.win); + win->popup.win = 0; + } + win->next = 0; + win->prev = 0; + + while (it) { + /*free window state tables */ + struct nk_table *n = it->next; + nk_remove_table(win, it); + nk_free_table(ctx, it); + if (it == win->tables) + win->tables = n; + it = n; + } + + /* link windows into freelist */ + {union nk_page_data *pd = NK_CONTAINER_OF(win, union nk_page_data, win); + struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data); + nk_free_page_element(ctx, pe);} +} +NK_LIB struct nk_window* +nk_find_window(struct nk_context *ctx, nk_hash hash, const char *name) +{ + struct nk_window *iter; + iter = ctx->begin; + while (iter) { + NK_ASSERT(iter != iter->next); + if (iter->name == hash) { + int max_len = nk_strlen(iter->name_string); + if (!nk_stricmpn(iter->name_string, name, max_len)) + return iter; + } + iter = iter->next; + } + return 0; +} +NK_LIB void +nk_insert_window(struct nk_context *ctx, struct nk_window *win, + enum nk_window_insert_location loc) +{ + const struct nk_window *iter; + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!win || !ctx) return; + + iter = ctx->begin; + while (iter) { + NK_ASSERT(iter != iter->next); + NK_ASSERT(iter != win); + if (iter == win) return; + iter = iter->next; + } + + if (!ctx->begin) { + win->next = 0; + win->prev = 0; + ctx->begin = win; + ctx->end = win; + ctx->count = 1; + return; + } + if (loc == NK_INSERT_BACK) { + struct nk_window *end; + end = ctx->end; + end->flags |= NK_WINDOW_ROM; + end->next = win; + win->prev = ctx->end; + win->next = 0; + ctx->end = win; + ctx->active = ctx->end; + ctx->end->flags &= ~(nk_flags)NK_WINDOW_ROM; + } else { + /*ctx->end->flags |= NK_WINDOW_ROM;*/ + ctx->begin->prev = win; + win->next = ctx->begin; + win->prev = 0; + ctx->begin = win; + ctx->begin->flags &= ~(nk_flags)NK_WINDOW_ROM; + } + ctx->count++; +} +NK_LIB void +nk_remove_window(struct nk_context *ctx, struct nk_window *win) +{ + if (win == ctx->begin || win == ctx->end) { + if (win == ctx->begin) { + ctx->begin = win->next; + if (win->next) + win->next->prev = 0; + } + if (win == ctx->end) { + ctx->end = win->prev; + if (win->prev) + win->prev->next = 0; + } + } else { + if (win->next) + win->next->prev = win->prev; + if (win->prev) + win->prev->next = win->next; + } + if (win == ctx->active || !ctx->active) { + ctx->active = ctx->end; + if (ctx->end) + ctx->end->flags &= ~(nk_flags)NK_WINDOW_ROM; + } + win->next = 0; + win->prev = 0; + ctx->count--; +} +NK_API nk_bool +nk_begin(struct nk_context *ctx, const char *title, + struct nk_rect bounds, nk_flags flags) +{ + return nk_begin_titled(ctx, title, title, bounds, flags); +} +NK_API nk_bool +nk_begin_titled(struct nk_context *ctx, const char *name, const char *title, + struct nk_rect bounds, nk_flags flags) +{ + struct nk_window *win; + struct nk_style *style; + nk_hash name_hash; + int name_len; + int ret = 0; + + NK_ASSERT(ctx); + NK_ASSERT(name); + NK_ASSERT(title); + NK_ASSERT(ctx->style.font && ctx->style.font->width && "if this triggers you forgot to add a font"); + NK_ASSERT(!ctx->current && "if this triggers you missed a `nk_end` call"); + if (!ctx || ctx->current || !title || !name) + return 0; + + /* find or create window */ + style = &ctx->style; + name_len = (int)nk_strlen(name); + name_hash = nk_murmur_hash(name, (int)name_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, name_hash, name); + if (!win) { + /* create new window */ + nk_size name_length = (nk_size)name_len; + win = (struct nk_window*)nk_create_window(ctx); + NK_ASSERT(win); + if (!win) return 0; + + if (flags & NK_WINDOW_BACKGROUND) + nk_insert_window(ctx, win, NK_INSERT_FRONT); + else nk_insert_window(ctx, win, NK_INSERT_BACK); + nk_command_buffer_init(&win->buffer, &ctx->memory, NK_CLIPPING_ON); + + win->flags = flags; + win->bounds = bounds; + win->name = name_hash; + name_length = NK_MIN(name_length, NK_WINDOW_MAX_NAME-1); + NK_MEMCPY(win->name_string, name, name_length); + win->name_string[name_length] = 0; + win->popup.win = 0; + if (!ctx->active) + ctx->active = win; + } else { + /* update window */ + win->flags &= ~(nk_flags)(NK_WINDOW_PRIVATE-1); + win->flags |= flags; + if (!(win->flags & (NK_WINDOW_MOVABLE | NK_WINDOW_SCALABLE))) + win->bounds = bounds; + /* If this assert triggers you either: + * + * I.) Have more than one window with the same name or + * II.) You forgot to actually draw the window. + * More specific you did not call `nk_clear` (nk_clear will be + * automatically called for you if you are using one of the + * provided demo backends). */ + NK_ASSERT(win->seq != ctx->seq); + win->seq = ctx->seq; + if (!ctx->active && !(win->flags & NK_WINDOW_HIDDEN)) { + ctx->active = win; + ctx->end = win; + } + } + if (win->flags & NK_WINDOW_HIDDEN) { + ctx->current = win; + win->layout = 0; + return 0; + } else nk_start(ctx, win); + + /* window overlapping */ + if (!(win->flags & NK_WINDOW_HIDDEN) && !(win->flags & NK_WINDOW_NO_INPUT)) + { + int inpanel, ishovered; + struct nk_window *iter = win; + float h = ctx->style.font->height + 2.0f * style->window.header.padding.y + + (2.0f * style->window.header.label_padding.y); + struct nk_rect win_bounds = (!(win->flags & NK_WINDOW_MINIMIZED))? + win->bounds: nk_rect(win->bounds.x, win->bounds.y, win->bounds.w, h); + + /* activate window if hovered and no other window is overlapping this window */ + inpanel = nk_input_has_mouse_click_down_in_rect(&ctx->input, NK_BUTTON_LEFT, win_bounds, nk_true); + inpanel = inpanel && ctx->input.mouse.buttons[NK_BUTTON_LEFT].clicked; + ishovered = nk_input_is_mouse_hovering_rect(&ctx->input, win_bounds); + if ((win != ctx->active) && ishovered && !ctx->input.mouse.buttons[NK_BUTTON_LEFT].down) { + iter = win->next; + while (iter) { + struct nk_rect iter_bounds = (!(iter->flags & NK_WINDOW_MINIMIZED))? + iter->bounds: nk_rect(iter->bounds.x, iter->bounds.y, iter->bounds.w, h); + if (NK_INTERSECT(win_bounds.x, win_bounds.y, win_bounds.w, win_bounds.h, + iter_bounds.x, iter_bounds.y, iter_bounds.w, iter_bounds.h) && + (!(iter->flags & NK_WINDOW_HIDDEN))) + break; + + if (iter->popup.win && iter->popup.active && !(iter->flags & NK_WINDOW_HIDDEN) && + NK_INTERSECT(win->bounds.x, win_bounds.y, win_bounds.w, win_bounds.h, + iter->popup.win->bounds.x, iter->popup.win->bounds.y, + iter->popup.win->bounds.w, iter->popup.win->bounds.h)) + break; + iter = iter->next; + } + } + + /* activate window if clicked */ + if (iter && inpanel && (win != ctx->end)) { + iter = win->next; + while (iter) { + /* try to find a panel with higher priority in the same position */ + struct nk_rect iter_bounds = (!(iter->flags & NK_WINDOW_MINIMIZED))? + iter->bounds: nk_rect(iter->bounds.x, iter->bounds.y, iter->bounds.w, h); + if (NK_INBOX(ctx->input.mouse.pos.x, ctx->input.mouse.pos.y, + iter_bounds.x, iter_bounds.y, iter_bounds.w, iter_bounds.h) && + !(iter->flags & NK_WINDOW_HIDDEN)) + break; + if (iter->popup.win && iter->popup.active && !(iter->flags & NK_WINDOW_HIDDEN) && + NK_INTERSECT(win_bounds.x, win_bounds.y, win_bounds.w, win_bounds.h, + iter->popup.win->bounds.x, iter->popup.win->bounds.y, + iter->popup.win->bounds.w, iter->popup.win->bounds.h)) + break; + iter = iter->next; + } + } + if (iter && !(win->flags & NK_WINDOW_ROM) && (win->flags & NK_WINDOW_BACKGROUND)) { + win->flags |= (nk_flags)NK_WINDOW_ROM; + iter->flags &= ~(nk_flags)NK_WINDOW_ROM; + ctx->active = iter; + if (!(iter->flags & NK_WINDOW_BACKGROUND)) { + /* current window is active in that position so transfer to top + * at the highest priority in stack */ + nk_remove_window(ctx, iter); + nk_insert_window(ctx, iter, NK_INSERT_BACK); + } + } else { + if (!iter && ctx->end != win) { + if (!(win->flags & NK_WINDOW_BACKGROUND)) { + /* current window is active in that position so transfer to top + * at the highest priority in stack */ + nk_remove_window(ctx, win); + nk_insert_window(ctx, win, NK_INSERT_BACK); + } + win->flags &= ~(nk_flags)NK_WINDOW_ROM; + ctx->active = win; + } + if (ctx->end != win && !(win->flags & NK_WINDOW_BACKGROUND)) + win->flags |= NK_WINDOW_ROM; + } + } + win->layout = (struct nk_panel*)nk_create_panel(ctx); + ctx->current = win; + ret = nk_panel_begin(ctx, title, NK_PANEL_WINDOW); + win->layout->offset_x = &win->scrollbar.x; + win->layout->offset_y = &win->scrollbar.y; + return ret; +} +NK_API void +nk_end(struct nk_context *ctx) +{ + struct nk_panel *layout; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current && "if this triggers you forgot to call `nk_begin`"); + if (!ctx || !ctx->current) + return; + + layout = ctx->current->layout; + if (!layout || (layout->type == NK_PANEL_WINDOW && (ctx->current->flags & NK_WINDOW_HIDDEN))) { + ctx->current = 0; + return; + } + nk_panel_end(ctx); + nk_free_panel(ctx, ctx->current->layout); + ctx->current = 0; +} +NK_API struct nk_rect +nk_window_get_bounds(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return nk_rect(0,0,0,0); + return ctx->current->bounds; +} +NK_API struct nk_vec2 +nk_window_get_position(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->bounds.x, ctx->current->bounds.y); +} +NK_API struct nk_vec2 +nk_window_get_size(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->bounds.w, ctx->current->bounds.h); +} +NK_API float +nk_window_get_width(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return 0; + return ctx->current->bounds.w; +} +NK_API float +nk_window_get_height(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return 0; + return ctx->current->bounds.h; +} +NK_API struct nk_rect +nk_window_get_content_region(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return nk_rect(0,0,0,0); + return ctx->current->layout->clip; +} +NK_API struct nk_vec2 +nk_window_get_content_region_min(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->layout->clip.x, ctx->current->layout->clip.y); +} +NK_API struct nk_vec2 +nk_window_get_content_region_max(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->layout->clip.x + ctx->current->layout->clip.w, + ctx->current->layout->clip.y + ctx->current->layout->clip.h); +} +NK_API struct nk_vec2 +nk_window_get_content_region_size(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->layout->clip.w, ctx->current->layout->clip.h); +} +NK_API struct nk_command_buffer* +nk_window_get_canvas(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return 0; + return &ctx->current->buffer; +} +NK_API struct nk_panel* +nk_window_get_panel(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return 0; + return ctx->current->layout; +} +NK_API void +nk_window_get_scroll(struct nk_context *ctx, nk_uint *offset_x, nk_uint *offset_y) +{ + struct nk_window *win; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return ; + win = ctx->current; + if (offset_x) + *offset_x = win->scrollbar.x; + if (offset_y) + *offset_y = win->scrollbar.y; +} +NK_API nk_bool +nk_window_has_focus(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return 0; + return ctx->current == ctx->active; +} +NK_API nk_bool +nk_window_is_hovered(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return 0; + if(ctx->current->flags & NK_WINDOW_HIDDEN) + return 0; + return nk_input_is_mouse_hovering_rect(&ctx->input, ctx->current->bounds); +} +NK_API nk_bool +nk_window_is_any_hovered(struct nk_context *ctx) +{ + struct nk_window *iter; + NK_ASSERT(ctx); + if (!ctx) return 0; + iter = ctx->begin; + while (iter) { + /* check if window is being hovered */ + if(!(iter->flags & NK_WINDOW_HIDDEN)) { + /* check if window popup is being hovered */ + if (iter->popup.active && iter->popup.win && nk_input_is_mouse_hovering_rect(&ctx->input, iter->popup.win->bounds)) + return 1; + + if (iter->flags & NK_WINDOW_MINIMIZED) { + struct nk_rect header = iter->bounds; + header.h = ctx->style.font->height + 2 * ctx->style.window.header.padding.y; + if (nk_input_is_mouse_hovering_rect(&ctx->input, header)) + return 1; + } else if (nk_input_is_mouse_hovering_rect(&ctx->input, iter->bounds)) { + return 1; + } + } + iter = iter->next; + } + return 0; +} +NK_API nk_bool +nk_item_is_any_active(struct nk_context *ctx) +{ + int any_hovered = nk_window_is_any_hovered(ctx); + int any_active = (ctx->last_widget_state & NK_WIDGET_STATE_MODIFIED); + return any_hovered || any_active; +} +NK_API nk_bool +nk_window_is_collapsed(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return 0; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) return 0; + return win->flags & NK_WINDOW_MINIMIZED; +} +NK_API nk_bool +nk_window_is_closed(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return 1; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) return 1; + return (win->flags & NK_WINDOW_CLOSED); +} +NK_API nk_bool +nk_window_is_hidden(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return 1; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) return 1; + return (win->flags & NK_WINDOW_HIDDEN); +} +NK_API nk_bool +nk_window_is_active(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return 0; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) return 0; + return win == ctx->active; +} +NK_API struct nk_window* +nk_window_find(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + return nk_find_window(ctx, title_hash, name); +} +NK_API void +nk_window_close(struct nk_context *ctx, const char *name) +{ + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return; + win = nk_window_find(ctx, name); + if (!win) return; + NK_ASSERT(ctx->current != win && "You cannot close a currently active window"); + if (ctx->current == win) return; + win->flags |= NK_WINDOW_HIDDEN; + win->flags |= NK_WINDOW_CLOSED; +} +NK_API void +nk_window_set_bounds(struct nk_context *ctx, + const char *name, struct nk_rect bounds) +{ + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return; + win = nk_window_find(ctx, name); + if (!win) return; + NK_ASSERT(ctx->current != win && "You cannot update a currently in procecss window"); + win->bounds = bounds; +} +NK_API void +nk_window_set_position(struct nk_context *ctx, + const char *name, struct nk_vec2 pos) +{ + struct nk_window *win = nk_window_find(ctx, name); + if (!win) return; + win->bounds.x = pos.x; + win->bounds.y = pos.y; +} +NK_API void +nk_window_set_size(struct nk_context *ctx, + const char *name, struct nk_vec2 size) +{ + struct nk_window *win = nk_window_find(ctx, name); + if (!win) return; + win->bounds.w = size.x; + win->bounds.h = size.y; +} +NK_API void +nk_window_set_scroll(struct nk_context *ctx, nk_uint offset_x, nk_uint offset_y) +{ + struct nk_window *win; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return; + win = ctx->current; + win->scrollbar.x = offset_x; + win->scrollbar.y = offset_y; +} +NK_API void +nk_window_collapse(struct nk_context *ctx, const char *name, + enum nk_collapse_states c) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) return; + if (c == NK_MINIMIZED) + win->flags |= NK_WINDOW_MINIMIZED; + else win->flags &= ~(nk_flags)NK_WINDOW_MINIMIZED; +} +NK_API void +nk_window_collapse_if(struct nk_context *ctx, const char *name, + enum nk_collapse_states c, int cond) +{ + NK_ASSERT(ctx); + if (!ctx || !cond) return; + nk_window_collapse(ctx, name, c); +} +NK_API void +nk_window_show(struct nk_context *ctx, const char *name, enum nk_show_states s) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) return; + if (s == NK_HIDDEN) { + win->flags |= NK_WINDOW_HIDDEN; + } else win->flags &= ~(nk_flags)NK_WINDOW_HIDDEN; +} +NK_API void +nk_window_show_if(struct nk_context *ctx, const char *name, + enum nk_show_states s, int cond) +{ + NK_ASSERT(ctx); + if (!ctx || !cond) return; + nk_window_show(ctx, name, s); +} + +NK_API void +nk_window_set_focus(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (win && ctx->end != win) { + nk_remove_window(ctx, win); + nk_insert_window(ctx, win, NK_INSERT_BACK); + } + ctx->active = win; +} + + + + +/* =============================================================== + * + * POPUP + * + * ===============================================================*/ +NK_API nk_bool +nk_popup_begin(struct nk_context *ctx, enum nk_popup_type type, + const char *title, nk_flags flags, struct nk_rect rect) +{ + struct nk_window *popup; + struct nk_window *win; + struct nk_panel *panel; + + int title_len; + nk_hash title_hash; + nk_size allocated; + + NK_ASSERT(ctx); + NK_ASSERT(title); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + panel = win->layout; + NK_ASSERT(!(panel->type & NK_PANEL_SET_POPUP) && "popups are not allowed to have popups"); + (void)panel; + title_len = (int)nk_strlen(title); + title_hash = nk_murmur_hash(title, (int)title_len, NK_PANEL_POPUP); + + popup = win->popup.win; + if (!popup) { + popup = (struct nk_window*)nk_create_window(ctx); + popup->parent = win; + win->popup.win = popup; + win->popup.active = 0; + win->popup.type = NK_PANEL_POPUP; + } + + /* make sure we have correct popup */ + if (win->popup.name != title_hash) { + if (!win->popup.active) { + nk_zero(popup, sizeof(*popup)); + win->popup.name = title_hash; + win->popup.active = 1; + win->popup.type = NK_PANEL_POPUP; + } else return 0; + } + + /* popup position is local to window */ + ctx->current = popup; + rect.x += win->layout->clip.x; + rect.y += win->layout->clip.y; + + /* setup popup data */ + popup->parent = win; + popup->bounds = rect; + popup->seq = ctx->seq; + popup->layout = (struct nk_panel*)nk_create_panel(ctx); + popup->flags = flags; + popup->flags |= NK_WINDOW_BORDER; + if (type == NK_POPUP_DYNAMIC) + popup->flags |= NK_WINDOW_DYNAMIC; + + popup->buffer = win->buffer; + nk_start_popup(ctx, win); + allocated = ctx->memory.allocated; + nk_push_scissor(&popup->buffer, nk_null_rect); + + if (nk_panel_begin(ctx, title, NK_PANEL_POPUP)) { + /* popup is running therefore invalidate parent panels */ + struct nk_panel *root; + root = win->layout; + while (root) { + root->flags |= NK_WINDOW_ROM; + root->flags &= ~(nk_flags)NK_WINDOW_REMOVE_ROM; + root = root->parent; + } + win->popup.active = 1; + popup->layout->offset_x = &popup->scrollbar.x; + popup->layout->offset_y = &popup->scrollbar.y; + popup->layout->parent = win->layout; + return 1; + } else { + /* popup was closed/is invalid so cleanup */ + struct nk_panel *root; + root = win->layout; + while (root) { + root->flags |= NK_WINDOW_REMOVE_ROM; + root = root->parent; + } + win->popup.buf.active = 0; + win->popup.active = 0; + ctx->memory.allocated = allocated; + ctx->current = win; + nk_free_panel(ctx, popup->layout); + popup->layout = 0; + return 0; + } +} +NK_LIB nk_bool +nk_nonblock_begin(struct nk_context *ctx, + nk_flags flags, struct nk_rect body, struct nk_rect header, + enum nk_panel_type panel_type) +{ + struct nk_window *popup; + struct nk_window *win; + struct nk_panel *panel; + int is_active = nk_true; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + /* popups cannot have popups */ + win = ctx->current; + panel = win->layout; + NK_ASSERT(!(panel->type & NK_PANEL_SET_POPUP)); + (void)panel; + popup = win->popup.win; + if (!popup) { + /* create window for nonblocking popup */ + popup = (struct nk_window*)nk_create_window(ctx); + popup->parent = win; + win->popup.win = popup; + win->popup.type = panel_type; + nk_command_buffer_init(&popup->buffer, &ctx->memory, NK_CLIPPING_ON); + } else { + /* close the popup if user pressed outside or in the header */ + int pressed, in_body, in_header; +#ifdef NK_BUTTON_TRIGGER_ON_RELEASE + pressed = nk_input_is_mouse_released(&ctx->input, NK_BUTTON_LEFT); +#else + pressed = nk_input_is_mouse_pressed(&ctx->input, NK_BUTTON_LEFT); +#endif + in_body = nk_input_is_mouse_hovering_rect(&ctx->input, body); + in_header = nk_input_is_mouse_hovering_rect(&ctx->input, header); + if (pressed && (!in_body || in_header)) + is_active = nk_false; + } + win->popup.header = header; + + if (!is_active) { + /* remove read only mode from all parent panels */ + struct nk_panel *root = win->layout; + while (root) { + root->flags |= NK_WINDOW_REMOVE_ROM; + root = root->parent; + } + return is_active; + } + popup->bounds = body; + popup->parent = win; + popup->layout = (struct nk_panel*)nk_create_panel(ctx); + popup->flags = flags; + popup->flags |= NK_WINDOW_BORDER; + popup->flags |= NK_WINDOW_DYNAMIC; + popup->seq = ctx->seq; + win->popup.active = 1; + NK_ASSERT(popup->layout); + + nk_start_popup(ctx, win); + popup->buffer = win->buffer; + nk_push_scissor(&popup->buffer, nk_null_rect); + ctx->current = popup; + + nk_panel_begin(ctx, 0, panel_type); + win->buffer = popup->buffer; + popup->layout->parent = win->layout; + popup->layout->offset_x = &popup->scrollbar.x; + popup->layout->offset_y = &popup->scrollbar.y; + + /* set read only mode to all parent panels */ + {struct nk_panel *root; + root = win->layout; + while (root) { + root->flags |= NK_WINDOW_ROM; + root = root->parent; + }} + return is_active; +} +NK_API void +nk_popup_close(struct nk_context *ctx) +{ + struct nk_window *popup; + NK_ASSERT(ctx); + if (!ctx || !ctx->current) return; + + popup = ctx->current; + NK_ASSERT(popup->parent); + NK_ASSERT(popup->layout->type & NK_PANEL_SET_POPUP); + popup->flags |= NK_WINDOW_HIDDEN; +} +NK_API void +nk_popup_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_window *popup; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + popup = ctx->current; + if (!popup->parent) return; + win = popup->parent; + if (popup->flags & NK_WINDOW_HIDDEN) { + struct nk_panel *root; + root = win->layout; + while (root) { + root->flags |= NK_WINDOW_REMOVE_ROM; + root = root->parent; + } + win->popup.active = 0; + } + nk_push_scissor(&popup->buffer, nk_null_rect); + nk_end(ctx); + + win->buffer = popup->buffer; + nk_finish_popup(ctx, win); + ctx->current = win; + nk_push_scissor(&win->buffer, win->layout->clip); +} +NK_API void +nk_popup_get_scroll(struct nk_context *ctx, nk_uint *offset_x, nk_uint *offset_y) +{ + struct nk_window *popup; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + popup = ctx->current; + if (offset_x) + *offset_x = popup->scrollbar.x; + if (offset_y) + *offset_y = popup->scrollbar.y; +} +NK_API void +nk_popup_set_scroll(struct nk_context *ctx, nk_uint offset_x, nk_uint offset_y) +{ + struct nk_window *popup; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + popup = ctx->current; + popup->scrollbar.x = offset_x; + popup->scrollbar.y = offset_y; +} + + + + +/* ============================================================== + * + * CONTEXTUAL + * + * ===============================================================*/ +NK_API nk_bool +nk_contextual_begin(struct nk_context *ctx, nk_flags flags, struct nk_vec2 size, + struct nk_rect trigger_bounds) +{ + struct nk_window *win; + struct nk_window *popup; + struct nk_rect body; + + NK_STORAGE const struct nk_rect null_rect = {-1,-1,0,0}; + int is_clicked = 0; + int is_open = 0; + int ret = 0; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + ++win->popup.con_count; + if (ctx->current != ctx->active) + return 0; + + /* check if currently active contextual is active */ + popup = win->popup.win; + is_open = (popup && win->popup.type == NK_PANEL_CONTEXTUAL); + is_clicked = nk_input_mouse_clicked(&ctx->input, NK_BUTTON_RIGHT, trigger_bounds); + if (win->popup.active_con && win->popup.con_count != win->popup.active_con) + return 0; + if (!is_open && win->popup.active_con) + win->popup.active_con = 0; + if ((!is_open && !is_clicked)) + return 0; + + /* calculate contextual position on click */ + win->popup.active_con = win->popup.con_count; + if (is_clicked) { + body.x = ctx->input.mouse.pos.x; + body.y = ctx->input.mouse.pos.y; + } else { + body.x = popup->bounds.x; + body.y = popup->bounds.y; + } + body.w = size.x; + body.h = size.y; + + /* start nonblocking contextual popup */ + ret = nk_nonblock_begin(ctx, flags|NK_WINDOW_NO_SCROLLBAR, body, + null_rect, NK_PANEL_CONTEXTUAL); + if (ret) win->popup.type = NK_PANEL_CONTEXTUAL; + else { + win->popup.active_con = 0; + win->popup.type = NK_PANEL_NONE; + if (win->popup.win) + win->popup.win->flags = 0; + } + return ret; +} +NK_API nk_bool +nk_contextual_item_text(struct nk_context *ctx, const char *text, int len, + nk_flags alignment) +{ + struct nk_window *win; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); + if (!state) return nk_false; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text(&ctx->last_widget_state, &win->buffer, bounds, + text, len, alignment, NK_BUTTON_DEFAULT, &style->contextual_button, in, style->font)) { + nk_contextual_close(ctx); + return nk_true; + } + return nk_false; +} +NK_API nk_bool +nk_contextual_item_label(struct nk_context *ctx, const char *label, nk_flags align) +{ + return nk_contextual_item_text(ctx, label, nk_strlen(label), align); +} +NK_API nk_bool +nk_contextual_item_image_text(struct nk_context *ctx, struct nk_image img, + const char *text, int len, nk_flags align) +{ + struct nk_window *win; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); + if (!state) return nk_false; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, bounds, + img, text, len, align, NK_BUTTON_DEFAULT, &style->contextual_button, style->font, in)){ + nk_contextual_close(ctx); + return nk_true; + } + return nk_false; +} +NK_API nk_bool +nk_contextual_item_image_label(struct nk_context *ctx, struct nk_image img, + const char *label, nk_flags align) +{ + return nk_contextual_item_image_text(ctx, img, label, nk_strlen(label), align); +} +NK_API nk_bool +nk_contextual_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type symbol, + const char *text, int len, nk_flags align) +{ + struct nk_window *win; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); + if (!state) return nk_false; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, bounds, + symbol, text, len, align, NK_BUTTON_DEFAULT, &style->contextual_button, style->font, in)) { + nk_contextual_close(ctx); + return nk_true; + } + return nk_false; +} +NK_API nk_bool +nk_contextual_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type symbol, + const char *text, nk_flags align) +{ + return nk_contextual_item_symbol_text(ctx, symbol, text, nk_strlen(text), align); +} +NK_API void +nk_contextual_close(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return; + nk_popup_close(ctx); +} +NK_API void +nk_contextual_end(struct nk_context *ctx) +{ + struct nk_window *popup; + struct nk_panel *panel; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + + popup = ctx->current; + panel = popup->layout; + NK_ASSERT(popup->parent); + NK_ASSERT(panel->type & NK_PANEL_SET_POPUP); + if (panel->flags & NK_WINDOW_DYNAMIC) { + /* Close behavior + This is a bit of a hack solution since we do not know before we end our popup + how big it will be. We therefore do not directly know when a + click outside the non-blocking popup must close it at that direct frame. + Instead it will be closed in the next frame.*/ + struct nk_rect body = {0,0,0,0}; + if (panel->at_y < (panel->bounds.y + panel->bounds.h)) { + struct nk_vec2 padding = nk_panel_get_padding(&ctx->style, panel->type); + body = panel->bounds; + body.y = (panel->at_y + panel->footer_height + panel->border + padding.y + panel->row.height); + body.h = (panel->bounds.y + panel->bounds.h) - body.y; + } + {int pressed = nk_input_is_mouse_pressed(&ctx->input, NK_BUTTON_LEFT); + int in_body = nk_input_is_mouse_hovering_rect(&ctx->input, body); + if (pressed && in_body) + popup->flags |= NK_WINDOW_HIDDEN; + } + } + if (popup->flags & NK_WINDOW_HIDDEN) + popup->seq = 0; + nk_popup_end(ctx); + return; +} + + + + + +/* =============================================================== + * + * MENU + * + * ===============================================================*/ +NK_API void +nk_menubar_begin(struct nk_context *ctx) +{ + struct nk_panel *layout; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + layout = ctx->current->layout; + NK_ASSERT(layout->at_y == layout->bounds.y); + /* if this assert triggers you allocated space between nk_begin and nk_menubar_begin. + If you want a menubar the first nuklear function after `nk_begin` has to be a + `nk_menubar_begin` call. Inside the menubar you then have to allocate space for + widgets (also supports multiple rows). + Example: + if (nk_begin(...)) { + nk_menubar_begin(...); + nk_layout_xxxx(...); + nk_button(...); + nk_layout_xxxx(...); + nk_button(...); + nk_menubar_end(...); + } + nk_end(...); + */ + if (layout->flags & NK_WINDOW_HIDDEN || layout->flags & NK_WINDOW_MINIMIZED) + return; + + layout->menu.x = layout->at_x; + layout->menu.y = layout->at_y + layout->row.height; + layout->menu.w = layout->bounds.w; + layout->menu.offset.x = *layout->offset_x; + layout->menu.offset.y = *layout->offset_y; + *layout->offset_y = 0; +} +NK_API void +nk_menubar_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + struct nk_command_buffer *out; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + out = &win->buffer; + layout = win->layout; + if (layout->flags & NK_WINDOW_HIDDEN || layout->flags & NK_WINDOW_MINIMIZED) + return; + + layout->menu.h = layout->at_y - layout->menu.y; + layout->menu.h += layout->row.height + ctx->style.window.spacing.y; + + layout->bounds.y += layout->menu.h; + layout->bounds.h -= layout->menu.h; + + *layout->offset_x = layout->menu.offset.x; + *layout->offset_y = layout->menu.offset.y; + layout->at_y = layout->bounds.y - layout->row.height; + + layout->clip.y = layout->bounds.y; + layout->clip.h = layout->bounds.h; + nk_push_scissor(out, layout->clip); +} +NK_INTERN int +nk_menu_begin(struct nk_context *ctx, struct nk_window *win, + const char *id, int is_clicked, struct nk_rect header, struct nk_vec2 size) +{ + int is_open = 0; + int is_active = 0; + struct nk_rect body; + struct nk_window *popup; + nk_hash hash = nk_murmur_hash(id, (int)nk_strlen(id), NK_PANEL_MENU); + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + body.x = header.x; + body.w = size.x; + body.y = header.y + header.h; + body.h = size.y; + + popup = win->popup.win; + is_open = popup ? nk_true : nk_false; + is_active = (popup && (win->popup.name == hash) && win->popup.type == NK_PANEL_MENU); + if ((is_clicked && is_open && !is_active) || (is_open && !is_active) || + (!is_open && !is_active && !is_clicked)) return 0; + if (!nk_nonblock_begin(ctx, NK_WINDOW_NO_SCROLLBAR, body, header, NK_PANEL_MENU)) + return 0; + + win->popup.type = NK_PANEL_MENU; + win->popup.name = hash; + return 1; +} +NK_API nk_bool +nk_menu_begin_text(struct nk_context *ctx, const char *title, int len, + nk_flags align, struct nk_vec2 size) +{ + struct nk_window *win; + const struct nk_input *in; + struct nk_rect header; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text(&ctx->last_widget_state, &win->buffer, header, + title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in, ctx->style.font)) + is_clicked = nk_true; + return nk_menu_begin(ctx, win, title, is_clicked, header, size); +} +NK_API nk_bool nk_menu_begin_label(struct nk_context *ctx, + const char *text, nk_flags align, struct nk_vec2 size) +{ + return nk_menu_begin_text(ctx, text, nk_strlen(text), align, size); +} +NK_API nk_bool +nk_menu_begin_image(struct nk_context *ctx, const char *id, struct nk_image img, + struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_rect header; + const struct nk_input *in; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_image(&ctx->last_widget_state, &win->buffer, header, + img, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in)) + is_clicked = nk_true; + return nk_menu_begin(ctx, win, id, is_clicked, header, size); +} +NK_API nk_bool +nk_menu_begin_symbol(struct nk_context *ctx, const char *id, + enum nk_symbol_type sym, struct nk_vec2 size) +{ + struct nk_window *win; + const struct nk_input *in; + struct nk_rect header; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_symbol(&ctx->last_widget_state, &win->buffer, header, + sym, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in, ctx->style.font)) + is_clicked = nk_true; + return nk_menu_begin(ctx, win, id, is_clicked, header, size); +} +NK_API nk_bool +nk_menu_begin_image_text(struct nk_context *ctx, const char *title, int len, + nk_flags align, struct nk_image img, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_rect header; + const struct nk_input *in; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, + header, img, title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, + ctx->style.font, in)) + is_clicked = nk_true; + return nk_menu_begin(ctx, win, title, is_clicked, header, size); +} +NK_API nk_bool +nk_menu_begin_image_label(struct nk_context *ctx, + const char *title, nk_flags align, struct nk_image img, struct nk_vec2 size) +{ + return nk_menu_begin_image_text(ctx, title, nk_strlen(title), align, img, size); +} +NK_API nk_bool +nk_menu_begin_symbol_text(struct nk_context *ctx, const char *title, int len, + nk_flags align, enum nk_symbol_type sym, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_rect header; + const struct nk_input *in; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, + header, sym, title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, + ctx->style.font, in)) is_clicked = nk_true; + return nk_menu_begin(ctx, win, title, is_clicked, header, size); +} +NK_API nk_bool +nk_menu_begin_symbol_label(struct nk_context *ctx, + const char *title, nk_flags align, enum nk_symbol_type sym, struct nk_vec2 size ) +{ + return nk_menu_begin_symbol_text(ctx, title, nk_strlen(title), align,sym,size); +} +NK_API nk_bool +nk_menu_item_text(struct nk_context *ctx, const char *title, int len, nk_flags align) +{ + return nk_contextual_item_text(ctx, title, len, align); +} +NK_API nk_bool +nk_menu_item_label(struct nk_context *ctx, const char *label, nk_flags align) +{ + return nk_contextual_item_label(ctx, label, align); +} +NK_API nk_bool +nk_menu_item_image_label(struct nk_context *ctx, struct nk_image img, + const char *label, nk_flags align) +{ + return nk_contextual_item_image_label(ctx, img, label, align); +} +NK_API nk_bool +nk_menu_item_image_text(struct nk_context *ctx, struct nk_image img, + const char *text, int len, nk_flags align) +{ + return nk_contextual_item_image_text(ctx, img, text, len, align); +} +NK_API nk_bool nk_menu_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, + const char *text, int len, nk_flags align) +{ + return nk_contextual_item_symbol_text(ctx, sym, text, len, align); +} +NK_API nk_bool nk_menu_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, + const char *label, nk_flags align) +{ + return nk_contextual_item_symbol_label(ctx, sym, label, align); +} +NK_API void nk_menu_close(struct nk_context *ctx) +{ + nk_contextual_close(ctx); +} +NK_API void +nk_menu_end(struct nk_context *ctx) +{ + nk_contextual_end(ctx); +} + + + + + +/* =============================================================== + * + * LAYOUT + * + * ===============================================================*/ +NK_API void +nk_layout_set_min_row_height(struct nk_context *ctx, float height) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + layout->row.min_height = height; +} +NK_API void +nk_layout_reset_min_row_height(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + layout->row.min_height = ctx->style.font->height; + layout->row.min_height += ctx->style.text.padding.y*2; + layout->row.min_height += ctx->style.window.min_row_height_padding*2; +} +NK_LIB float +nk_layout_row_calculate_usable_space(const struct nk_style *style, enum nk_panel_type type, + float total_space, int columns) +{ + float panel_spacing; + float panel_space; + + struct nk_vec2 spacing; + + spacing = style->window.spacing; + + /* calculate the usable panel space */ + panel_spacing = (float)NK_MAX(columns - 1, 0) * spacing.x; + panel_space = total_space - panel_spacing; + return panel_space; +} +NK_LIB void +nk_panel_layout(const struct nk_context *ctx, struct nk_window *win, + float height, int cols) +{ + struct nk_panel *layout; + const struct nk_style *style; + struct nk_command_buffer *out; + + struct nk_vec2 item_spacing; + struct nk_color color; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + /* prefetch some configuration data */ + layout = win->layout; + style = &ctx->style; + out = &win->buffer; + color = style->window.background; + item_spacing = style->window.spacing; + + /* if one of these triggers you forgot to add an `if` condition around either + a window, group, popup, combobox or contextual menu `begin` and `end` block. + Example: + if (nk_begin(...) {...} nk_end(...); or + if (nk_group_begin(...) { nk_group_end(...);} */ + NK_ASSERT(!(layout->flags & NK_WINDOW_MINIMIZED)); + NK_ASSERT(!(layout->flags & NK_WINDOW_HIDDEN)); + NK_ASSERT(!(layout->flags & NK_WINDOW_CLOSED)); + + /* update the current row and set the current row layout */ + layout->row.index = 0; + layout->at_y += layout->row.height; + layout->row.columns = cols; + if (height == 0.0f) + layout->row.height = NK_MAX(height, layout->row.min_height) + item_spacing.y; + else layout->row.height = height + item_spacing.y; + + layout->row.item_offset = 0; + if (layout->flags & NK_WINDOW_DYNAMIC) { + /* draw background for dynamic panels */ + struct nk_rect background; + background.x = win->bounds.x; + background.w = win->bounds.w; + background.y = layout->at_y - 1.0f; + background.h = layout->row.height + 1.0f; + nk_fill_rect(out, background, 0, color); + } +} +NK_LIB void +nk_row_layout(struct nk_context *ctx, enum nk_layout_format fmt, + float height, int cols, int width) +{ + /* update the current row and set the current row layout */ + struct nk_window *win; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + nk_panel_layout(ctx, win, height, cols); + if (fmt == NK_DYNAMIC) + win->layout->row.type = NK_LAYOUT_DYNAMIC_FIXED; + else win->layout->row.type = NK_LAYOUT_STATIC_FIXED; + + win->layout->row.ratio = 0; + win->layout->row.filled = 0; + win->layout->row.item_offset = 0; + win->layout->row.item_width = (float)width; +} +NK_API float +nk_layout_ratio_from_pixel(struct nk_context *ctx, float pixel_width) +{ + struct nk_window *win; + NK_ASSERT(ctx); + NK_ASSERT(pixel_width); + if (!ctx || !ctx->current || !ctx->current->layout) return 0; + win = ctx->current; + return NK_CLAMP(0.0f, pixel_width/win->bounds.x, 1.0f); +} +NK_API void +nk_layout_row_dynamic(struct nk_context *ctx, float height, int cols) +{ + nk_row_layout(ctx, NK_DYNAMIC, height, cols, 0); +} +NK_API void +nk_layout_row_static(struct nk_context *ctx, float height, int item_width, int cols) +{ + nk_row_layout(ctx, NK_STATIC, height, cols, item_width); +} +NK_API void +nk_layout_row_begin(struct nk_context *ctx, enum nk_layout_format fmt, + float row_height, int cols) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + nk_panel_layout(ctx, win, row_height, cols); + if (fmt == NK_DYNAMIC) + layout->row.type = NK_LAYOUT_DYNAMIC_ROW; + else layout->row.type = NK_LAYOUT_STATIC_ROW; + + layout->row.ratio = 0; + layout->row.filled = 0; + layout->row.item_width = 0; + layout->row.item_offset = 0; + layout->row.columns = cols; +} +NK_API void +nk_layout_row_push(struct nk_context *ctx, float ratio_or_width) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + NK_ASSERT(layout->row.type == NK_LAYOUT_STATIC_ROW || layout->row.type == NK_LAYOUT_DYNAMIC_ROW); + if (layout->row.type != NK_LAYOUT_STATIC_ROW && layout->row.type != NK_LAYOUT_DYNAMIC_ROW) + return; + + if (layout->row.type == NK_LAYOUT_DYNAMIC_ROW) { + float ratio = ratio_or_width; + if ((ratio + layout->row.filled) > 1.0f) return; + if (ratio > 0.0f) + layout->row.item_width = NK_SATURATE(ratio); + else layout->row.item_width = 1.0f - layout->row.filled; + } else layout->row.item_width = ratio_or_width; +} +NK_API void +nk_layout_row_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + NK_ASSERT(layout->row.type == NK_LAYOUT_STATIC_ROW || layout->row.type == NK_LAYOUT_DYNAMIC_ROW); + if (layout->row.type != NK_LAYOUT_STATIC_ROW && layout->row.type != NK_LAYOUT_DYNAMIC_ROW) + return; + layout->row.item_width = 0; + layout->row.item_offset = 0; +} +NK_API void +nk_layout_row(struct nk_context *ctx, enum nk_layout_format fmt, + float height, int cols, const float *ratio) +{ + int i; + int n_undef = 0; + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + nk_panel_layout(ctx, win, height, cols); + if (fmt == NK_DYNAMIC) { + /* calculate width of undefined widget ratios */ + float r = 0; + layout->row.ratio = ratio; + for (i = 0; i < cols; ++i) { + if (ratio[i] < 0.0f) + n_undef++; + else r += ratio[i]; + } + r = NK_SATURATE(1.0f - r); + layout->row.type = NK_LAYOUT_DYNAMIC; + layout->row.item_width = (r > 0 && n_undef > 0) ? (r / (float)n_undef):0; + } else { + layout->row.ratio = ratio; + layout->row.type = NK_LAYOUT_STATIC; + layout->row.item_width = 0; + layout->row.item_offset = 0; + } + layout->row.item_offset = 0; + layout->row.filled = 0; +} +NK_API void +nk_layout_row_template_begin(struct nk_context *ctx, float height) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + nk_panel_layout(ctx, win, height, 1); + layout->row.type = NK_LAYOUT_TEMPLATE; + layout->row.columns = 0; + layout->row.ratio = 0; + layout->row.item_width = 0; + layout->row.item_height = 0; + layout->row.item_offset = 0; + layout->row.filled = 0; + layout->row.item.x = 0; + layout->row.item.y = 0; + layout->row.item.w = 0; + layout->row.item.h = 0; +} +NK_API void +nk_layout_row_template_push_dynamic(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + NK_ASSERT(layout->row.type == NK_LAYOUT_TEMPLATE); + NK_ASSERT(layout->row.columns < NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS); + if (layout->row.type != NK_LAYOUT_TEMPLATE) return; + if (layout->row.columns >= NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS) return; + layout->row.templates[layout->row.columns++] = -1.0f; +} +NK_API void +nk_layout_row_template_push_variable(struct nk_context *ctx, float min_width) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + NK_ASSERT(layout->row.type == NK_LAYOUT_TEMPLATE); + NK_ASSERT(layout->row.columns < NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS); + if (layout->row.type != NK_LAYOUT_TEMPLATE) return; + if (layout->row.columns >= NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS) return; + layout->row.templates[layout->row.columns++] = -min_width; +} +NK_API void +nk_layout_row_template_push_static(struct nk_context *ctx, float width) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + NK_ASSERT(layout->row.type == NK_LAYOUT_TEMPLATE); + NK_ASSERT(layout->row.columns < NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS); + if (layout->row.type != NK_LAYOUT_TEMPLATE) return; + if (layout->row.columns >= NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS) return; + layout->row.templates[layout->row.columns++] = width; +} +NK_API void +nk_layout_row_template_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + + int i = 0; + int variable_count = 0; + int min_variable_count = 0; + float min_fixed_width = 0.0f; + float total_fixed_width = 0.0f; + float max_variable_width = 0.0f; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + NK_ASSERT(layout->row.type == NK_LAYOUT_TEMPLATE); + if (layout->row.type != NK_LAYOUT_TEMPLATE) return; + for (i = 0; i < layout->row.columns; ++i) { + float width = layout->row.templates[i]; + if (width >= 0.0f) { + total_fixed_width += width; + min_fixed_width += width; + } else if (width < -1.0f) { + width = -width; + total_fixed_width += width; + max_variable_width = NK_MAX(max_variable_width, width); + variable_count++; + } else { + min_variable_count++; + variable_count++; + } + } + if (variable_count) { + float space = nk_layout_row_calculate_usable_space(&ctx->style, layout->type, + layout->bounds.w, layout->row.columns); + float var_width = (NK_MAX(space-min_fixed_width,0.0f)) / (float)variable_count; + int enough_space = var_width >= max_variable_width; + if (!enough_space) + var_width = (NK_MAX(space-total_fixed_width,0)) / (float)min_variable_count; + for (i = 0; i < layout->row.columns; ++i) { + float *width = &layout->row.templates[i]; + *width = (*width >= 0.0f)? *width: (*width < -1.0f && !enough_space)? -(*width): var_width; + } + } +} +NK_API void +nk_layout_space_begin(struct nk_context *ctx, enum nk_layout_format fmt, + float height, int widget_count) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + nk_panel_layout(ctx, win, height, widget_count); + if (fmt == NK_STATIC) + layout->row.type = NK_LAYOUT_STATIC_FREE; + else layout->row.type = NK_LAYOUT_DYNAMIC_FREE; + + layout->row.ratio = 0; + layout->row.filled = 0; + layout->row.item_width = 0; + layout->row.item_offset = 0; +} +NK_API void +nk_layout_space_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + layout->row.item_width = 0; + layout->row.item_height = 0; + layout->row.item_offset = 0; + nk_zero(&layout->row.item, sizeof(layout->row.item)); +} +NK_API void +nk_layout_space_push(struct nk_context *ctx, struct nk_rect rect) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + layout->row.item = rect; +} +NK_API struct nk_rect +nk_layout_space_bounds(struct nk_context *ctx) +{ + struct nk_rect ret; + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x = layout->clip.x; + ret.y = layout->clip.y; + ret.w = layout->clip.w; + ret.h = layout->row.height; + return ret; +} +NK_API struct nk_rect +nk_layout_widget_bounds(struct nk_context *ctx) +{ + struct nk_rect ret; + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x = layout->at_x; + ret.y = layout->at_y; + ret.w = layout->bounds.w - NK_MAX(layout->at_x - layout->bounds.x,0); + ret.h = layout->row.height; + return ret; +} +NK_API struct nk_vec2 +nk_layout_space_to_screen(struct nk_context *ctx, struct nk_vec2 ret) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x += layout->at_x - (float)*layout->offset_x; + ret.y += layout->at_y - (float)*layout->offset_y; + return ret; +} +NK_API struct nk_vec2 +nk_layout_space_to_local(struct nk_context *ctx, struct nk_vec2 ret) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x += -layout->at_x + (float)*layout->offset_x; + ret.y += -layout->at_y + (float)*layout->offset_y; + return ret; +} +NK_API struct nk_rect +nk_layout_space_rect_to_screen(struct nk_context *ctx, struct nk_rect ret) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x += layout->at_x - (float)*layout->offset_x; + ret.y += layout->at_y - (float)*layout->offset_y; + return ret; +} +NK_API struct nk_rect +nk_layout_space_rect_to_local(struct nk_context *ctx, struct nk_rect ret) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x += -layout->at_x + (float)*layout->offset_x; + ret.y += -layout->at_y + (float)*layout->offset_y; + return ret; +} +NK_LIB void +nk_panel_alloc_row(const struct nk_context *ctx, struct nk_window *win) +{ + struct nk_panel *layout = win->layout; + struct nk_vec2 spacing = ctx->style.window.spacing; + const float row_height = layout->row.height - spacing.y; + nk_panel_layout(ctx, win, row_height, layout->row.columns); +} +NK_LIB void +nk_layout_widget_space(struct nk_rect *bounds, const struct nk_context *ctx, + struct nk_window *win, int modify) +{ + struct nk_panel *layout; + const struct nk_style *style; + + struct nk_vec2 spacing; + + float item_offset = 0; + float item_width = 0; + float item_spacing = 0; + float panel_space = 0; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + style = &ctx->style; + NK_ASSERT(bounds); + + spacing = style->window.spacing; + panel_space = nk_layout_row_calculate_usable_space(&ctx->style, layout->type, + layout->bounds.w, layout->row.columns); + + #define NK_FRAC(x) (x - (int)x) /* will be used to remove fookin gaps */ + /* calculate the width of one item inside the current layout space */ + switch (layout->row.type) { + case NK_LAYOUT_DYNAMIC_FIXED: { + /* scaling fixed size widgets item width */ + float w = NK_MAX(1.0f,panel_space) / (float)layout->row.columns; + item_offset = (float)layout->row.index * w; + item_width = w + NK_FRAC(item_offset); + item_spacing = (float)layout->row.index * spacing.x; + } break; + case NK_LAYOUT_DYNAMIC_ROW: { + /* scaling single ratio widget width */ + float w = layout->row.item_width * panel_space; + item_offset = layout->row.item_offset; + item_width = w + NK_FRAC(item_offset); + item_spacing = 0; + + if (modify) { + layout->row.item_offset += w + spacing.x; + layout->row.filled += layout->row.item_width; + layout->row.index = 0; + } + } break; + case NK_LAYOUT_DYNAMIC_FREE: { + /* panel width depended free widget placing */ + bounds->x = layout->at_x + (layout->bounds.w * layout->row.item.x); + bounds->x -= (float)*layout->offset_x; + bounds->y = layout->at_y + (layout->row.height * layout->row.item.y); + bounds->y -= (float)*layout->offset_y; + bounds->w = layout->bounds.w * layout->row.item.w + NK_FRAC(bounds->x); + bounds->h = layout->row.height * layout->row.item.h + NK_FRAC(bounds->y); + return; + } + case NK_LAYOUT_DYNAMIC: { + /* scaling arrays of panel width ratios for every widget */ + float ratio, w; + NK_ASSERT(layout->row.ratio); + ratio = (layout->row.ratio[layout->row.index] < 0) ? + layout->row.item_width : layout->row.ratio[layout->row.index]; + + w = (ratio * panel_space); + item_spacing = (float)layout->row.index * spacing.x; + item_offset = layout->row.item_offset; + item_width = w + NK_FRAC(item_offset); + + if (modify) { + layout->row.item_offset += w; + layout->row.filled += ratio; + } + } break; + case NK_LAYOUT_STATIC_FIXED: { + /* non-scaling fixed widgets item width */ + item_width = layout->row.item_width; + item_offset = (float)layout->row.index * item_width; + item_spacing = (float)layout->row.index * spacing.x; + } break; + case NK_LAYOUT_STATIC_ROW: { + /* scaling single ratio widget width */ + item_width = layout->row.item_width; + item_offset = layout->row.item_offset; + item_spacing = (float)layout->row.index * spacing.x; + if (modify) layout->row.item_offset += item_width; + } break; + case NK_LAYOUT_STATIC_FREE: { + /* free widget placing */ + bounds->x = layout->at_x + layout->row.item.x; + bounds->w = layout->row.item.w; + if (((bounds->x + bounds->w) > layout->max_x) && modify) + layout->max_x = (bounds->x + bounds->w); + bounds->x -= (float)*layout->offset_x; + bounds->y = layout->at_y + layout->row.item.y; + bounds->y -= (float)*layout->offset_y; + bounds->h = layout->row.item.h; + return; + } + case NK_LAYOUT_STATIC: { + /* non-scaling array of panel pixel width for every widget */ + item_spacing = (float)layout->row.index * spacing.x; + item_width = layout->row.ratio[layout->row.index]; + item_offset = layout->row.item_offset; + if (modify) layout->row.item_offset += item_width; + } break; + case NK_LAYOUT_TEMPLATE: { + /* stretchy row layout with combined dynamic/static widget width*/ + float w; + NK_ASSERT(layout->row.index < layout->row.columns); + NK_ASSERT(layout->row.index < NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS); + w = layout->row.templates[layout->row.index]; + item_offset = layout->row.item_offset; + item_width = w + NK_FRAC(item_offset); + item_spacing = (float)layout->row.index * spacing.x; + if (modify) layout->row.item_offset += w; + } break; + #undef NK_FRAC + default: NK_ASSERT(0); break; + }; + + /* set the bounds of the newly allocated widget */ + bounds->w = item_width; + bounds->h = layout->row.height - spacing.y; + bounds->y = layout->at_y - (float)*layout->offset_y; + bounds->x = layout->at_x + item_offset + item_spacing; + if (((bounds->x + bounds->w) > layout->max_x) && modify) + layout->max_x = bounds->x + bounds->w; + bounds->x -= (float)*layout->offset_x; +} +NK_LIB void +nk_panel_alloc_space(struct nk_rect *bounds, const struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + /* check if the end of the row has been hit and begin new row if so */ + win = ctx->current; + layout = win->layout; + if (layout->row.index >= layout->row.columns) + nk_panel_alloc_row(ctx, win); + + /* calculate widget position and size */ + nk_layout_widget_space(bounds, ctx, win, nk_true); + layout->row.index++; +} +NK_LIB void +nk_layout_peek(struct nk_rect *bounds, struct nk_context *ctx) +{ + float y; + int index; + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + y = layout->at_y; + index = layout->row.index; + if (layout->row.index >= layout->row.columns) { + layout->at_y += layout->row.height; + layout->row.index = 0; + } + nk_layout_widget_space(bounds, ctx, win, nk_false); + if (!layout->row.index) { + bounds->x -= layout->row.item_offset; + } + layout->at_y = y; + layout->row.index = index; +} + + + + + +/* =============================================================== + * + * TREE + * + * ===============================================================*/ +NK_INTERN int +nk_tree_state_base(struct nk_context *ctx, enum nk_tree_type type, + struct nk_image *img, const char *title, enum nk_collapse_states *state) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_style *style; + struct nk_command_buffer *out; + const struct nk_input *in; + const struct nk_style_button *button; + enum nk_symbol_type symbol; + float row_height; + + struct nk_vec2 item_spacing; + struct nk_rect header = {0,0,0,0}; + struct nk_rect sym = {0,0,0,0}; + struct nk_text text; + + nk_flags ws = 0; + enum nk_widget_layout_states widget_state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + /* cache some data */ + win = ctx->current; + layout = win->layout; + out = &win->buffer; + style = &ctx->style; + item_spacing = style->window.spacing; + + /* calculate header bounds and draw background */ + row_height = style->font->height + 2 * style->tab.padding.y; + nk_layout_set_min_row_height(ctx, row_height); + nk_layout_row_dynamic(ctx, row_height, 1); + nk_layout_reset_min_row_height(ctx); + + widget_state = nk_widget(&header, ctx); + if (type == NK_TREE_TAB) { + const struct nk_style_item *background = &style->tab.background; + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, header, &background->data.image, nk_white); + text.background = nk_rgba(0,0,0,0); + } else { + text.background = background->data.color; + nk_fill_rect(out, header, 0, style->tab.border_color); + nk_fill_rect(out, nk_shrink_rect(header, style->tab.border), + style->tab.rounding, background->data.color); + } + } else text.background = style->window.background; + + /* update node state */ + in = (!(layout->flags & NK_WINDOW_ROM)) ? &ctx->input: 0; + in = (in && widget_state == NK_WIDGET_VALID) ? &ctx->input : 0; + if (nk_button_behavior(&ws, header, in, NK_BUTTON_DEFAULT)) + *state = (*state == NK_MAXIMIZED) ? NK_MINIMIZED : NK_MAXIMIZED; + + /* select correct button style */ + if (*state == NK_MAXIMIZED) { + symbol = style->tab.sym_maximize; + if (type == NK_TREE_TAB) + button = &style->tab.tab_maximize_button; + else button = &style->tab.node_maximize_button; + } else { + symbol = style->tab.sym_minimize; + if (type == NK_TREE_TAB) + button = &style->tab.tab_minimize_button; + else button = &style->tab.node_minimize_button; + } + + {/* draw triangle button */ + sym.w = sym.h = style->font->height; + sym.y = header.y + style->tab.padding.y; + sym.x = header.x + style->tab.padding.x; + nk_do_button_symbol(&ws, &win->buffer, sym, symbol, NK_BUTTON_DEFAULT, + button, 0, style->font); + + if (img) { + /* draw optional image icon */ + sym.x = sym.x + sym.w + 4 * item_spacing.x; + nk_draw_image(&win->buffer, sym, img, nk_white); + sym.w = style->font->height + style->tab.spacing.x;} + } + + {/* draw label */ + struct nk_rect label; + header.w = NK_MAX(header.w, sym.w + item_spacing.x); + label.x = sym.x + sym.w + item_spacing.x; + label.y = sym.y; + label.w = header.w - (sym.w + item_spacing.y + style->tab.indent); + label.h = style->font->height; + text.text = style->tab.text; + text.padding = nk_vec2(0,0); + nk_widget_text(out, label, title, nk_strlen(title), &text, + NK_TEXT_LEFT, style->font);} + + /* increase x-axis cursor widget position pointer */ + if (*state == NK_MAXIMIZED) { + layout->at_x = header.x + (float)*layout->offset_x + style->tab.indent; + layout->bounds.w = NK_MAX(layout->bounds.w, style->tab.indent); + layout->bounds.w -= (style->tab.indent + style->window.padding.x); + layout->row.tree_depth++; + return nk_true; + } else return nk_false; +} +NK_INTERN int +nk_tree_base(struct nk_context *ctx, enum nk_tree_type type, + struct nk_image *img, const char *title, enum nk_collapse_states initial_state, + const char *hash, int len, int line) +{ + struct nk_window *win = ctx->current; + int title_len = 0; + nk_hash tree_hash = 0; + nk_uint *state = 0; + + /* retrieve tree state from internal widget state tables */ + if (!hash) { + title_len = (int)nk_strlen(title); + tree_hash = nk_murmur_hash(title, (int)title_len, (nk_hash)line); + } else tree_hash = nk_murmur_hash(hash, len, (nk_hash)line); + state = nk_find_value(win, tree_hash); + if (!state) { + state = nk_add_value(ctx, win, tree_hash, 0); + *state = initial_state; + } + return nk_tree_state_base(ctx, type, img, title, (enum nk_collapse_states*)state); +} +NK_API nk_bool +nk_tree_state_push(struct nk_context *ctx, enum nk_tree_type type, + const char *title, enum nk_collapse_states *state) +{ + return nk_tree_state_base(ctx, type, 0, title, state); +} +NK_API nk_bool +nk_tree_state_image_push(struct nk_context *ctx, enum nk_tree_type type, + struct nk_image img, const char *title, enum nk_collapse_states *state) +{ + return nk_tree_state_base(ctx, type, &img, title, state); +} +NK_API void +nk_tree_state_pop(struct nk_context *ctx) +{ + struct nk_window *win = 0; + struct nk_panel *layout = 0; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + layout->at_x -= ctx->style.tab.indent + (float)*layout->offset_x; + layout->bounds.w += ctx->style.tab.indent + ctx->style.window.padding.x; + NK_ASSERT(layout->row.tree_depth); + layout->row.tree_depth--; +} +NK_API nk_bool +nk_tree_push_hashed(struct nk_context *ctx, enum nk_tree_type type, + const char *title, enum nk_collapse_states initial_state, + const char *hash, int len, int line) +{ + return nk_tree_base(ctx, type, 0, title, initial_state, hash, len, line); +} +NK_API nk_bool +nk_tree_image_push_hashed(struct nk_context *ctx, enum nk_tree_type type, + struct nk_image img, const char *title, enum nk_collapse_states initial_state, + const char *hash, int len,int seed) +{ + return nk_tree_base(ctx, type, &img, title, initial_state, hash, len, seed); +} +NK_API void +nk_tree_pop(struct nk_context *ctx) +{ + nk_tree_state_pop(ctx); +} +NK_INTERN int +nk_tree_element_image_push_hashed_base(struct nk_context *ctx, enum nk_tree_type type, + struct nk_image *img, const char *title, int title_len, + enum nk_collapse_states *state, nk_bool *selected) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_style *style; + struct nk_command_buffer *out; + const struct nk_input *in; + const struct nk_style_button *button; + enum nk_symbol_type symbol; + float row_height; + struct nk_vec2 padding; + + int text_len; + float text_width; + + struct nk_vec2 item_spacing; + struct nk_rect header = {0,0,0,0}; + struct nk_rect sym = {0,0,0,0}; + + nk_flags ws = 0; + enum nk_widget_layout_states widget_state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + /* cache some data */ + win = ctx->current; + layout = win->layout; + out = &win->buffer; + style = &ctx->style; + item_spacing = style->window.spacing; + padding = style->selectable.padding; + + /* calculate header bounds and draw background */ + row_height = style->font->height + 2 * style->tab.padding.y; + nk_layout_set_min_row_height(ctx, row_height); + nk_layout_row_dynamic(ctx, row_height, 1); + nk_layout_reset_min_row_height(ctx); + + widget_state = nk_widget(&header, ctx); + if (type == NK_TREE_TAB) { + const struct nk_style_item *background = &style->tab.background; + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, header, &background->data.image, nk_white); + } else { + nk_fill_rect(out, header, 0, style->tab.border_color); + nk_fill_rect(out, nk_shrink_rect(header, style->tab.border), + style->tab.rounding, background->data.color); + } + } + + in = (!(layout->flags & NK_WINDOW_ROM)) ? &ctx->input: 0; + in = (in && widget_state == NK_WIDGET_VALID) ? &ctx->input : 0; + + /* select correct button style */ + if (*state == NK_MAXIMIZED) { + symbol = style->tab.sym_maximize; + if (type == NK_TREE_TAB) + button = &style->tab.tab_maximize_button; + else button = &style->tab.node_maximize_button; + } else { + symbol = style->tab.sym_minimize; + if (type == NK_TREE_TAB) + button = &style->tab.tab_minimize_button; + else button = &style->tab.node_minimize_button; + } + {/* draw triangle button */ + sym.w = sym.h = style->font->height; + sym.y = header.y + style->tab.padding.y; + sym.x = header.x + style->tab.padding.x; + if (nk_do_button_symbol(&ws, &win->buffer, sym, symbol, NK_BUTTON_DEFAULT, button, in, style->font)) + *state = (*state == NK_MAXIMIZED) ? NK_MINIMIZED : NK_MAXIMIZED;} + + /* draw label */ + {nk_flags dummy = 0; + struct nk_rect label; + /* calculate size of the text and tooltip */ + text_len = nk_strlen(title); + text_width = style->font->width(style->font->userdata, style->font->height, title, text_len); + text_width += (4 * padding.x); + + header.w = NK_MAX(header.w, sym.w + item_spacing.x); + label.x = sym.x + sym.w + item_spacing.x; + label.y = sym.y; + label.w = NK_MIN(header.w - (sym.w + item_spacing.y + style->tab.indent), text_width); + label.h = style->font->height; + + if (img) { + nk_do_selectable_image(&dummy, &win->buffer, label, title, title_len, NK_TEXT_LEFT, + selected, img, &style->selectable, in, style->font); + } else nk_do_selectable(&dummy, &win->buffer, label, title, title_len, NK_TEXT_LEFT, + selected, &style->selectable, in, style->font); + } + /* increase x-axis cursor widget position pointer */ + if (*state == NK_MAXIMIZED) { + layout->at_x = header.x + (float)*layout->offset_x + style->tab.indent; + layout->bounds.w = NK_MAX(layout->bounds.w, style->tab.indent); + layout->bounds.w -= (style->tab.indent + style->window.padding.x); + layout->row.tree_depth++; + return nk_true; + } else return nk_false; +} +NK_INTERN int +nk_tree_element_base(struct nk_context *ctx, enum nk_tree_type type, + struct nk_image *img, const char *title, enum nk_collapse_states initial_state, + nk_bool *selected, const char *hash, int len, int line) +{ + struct nk_window *win = ctx->current; + int title_len = 0; + nk_hash tree_hash = 0; + nk_uint *state = 0; + + /* retrieve tree state from internal widget state tables */ + if (!hash) { + title_len = (int)nk_strlen(title); + tree_hash = nk_murmur_hash(title, (int)title_len, (nk_hash)line); + } else tree_hash = nk_murmur_hash(hash, len, (nk_hash)line); + state = nk_find_value(win, tree_hash); + if (!state) { + state = nk_add_value(ctx, win, tree_hash, 0); + *state = initial_state; + } return nk_tree_element_image_push_hashed_base(ctx, type, img, title, + nk_strlen(title), (enum nk_collapse_states*)state, selected); +} +NK_API nk_bool +nk_tree_element_push_hashed(struct nk_context *ctx, enum nk_tree_type type, + const char *title, enum nk_collapse_states initial_state, + nk_bool *selected, const char *hash, int len, int seed) +{ + return nk_tree_element_base(ctx, type, 0, title, initial_state, selected, hash, len, seed); +} +NK_API nk_bool +nk_tree_element_image_push_hashed(struct nk_context *ctx, enum nk_tree_type type, + struct nk_image img, const char *title, enum nk_collapse_states initial_state, + nk_bool *selected, const char *hash, int len,int seed) +{ + return nk_tree_element_base(ctx, type, &img, title, initial_state, selected, hash, len, seed); +} +NK_API void +nk_tree_element_pop(struct nk_context *ctx) +{ + nk_tree_state_pop(ctx); +} + + + + + +/* =============================================================== + * + * GROUP + * + * ===============================================================*/ +NK_API nk_bool +nk_group_scrolled_offset_begin(struct nk_context *ctx, + nk_uint *x_offset, nk_uint *y_offset, const char *title, nk_flags flags) +{ + struct nk_rect bounds; + struct nk_window panel; + struct nk_window *win; + + win = ctx->current; + nk_panel_alloc_space(&bounds, ctx); + {const struct nk_rect *c = &win->layout->clip; + if (!NK_INTERSECT(c->x, c->y, c->w, c->h, bounds.x, bounds.y, bounds.w, bounds.h) && + !(flags & NK_WINDOW_MOVABLE)) { + return 0; + }} + if (win->flags & NK_WINDOW_ROM) + flags |= NK_WINDOW_ROM; + + /* initialize a fake window to create the panel from */ + nk_zero(&panel, sizeof(panel)); + panel.bounds = bounds; + panel.flags = flags; + panel.scrollbar.x = *x_offset; + panel.scrollbar.y = *y_offset; + panel.buffer = win->buffer; + panel.layout = (struct nk_panel*)nk_create_panel(ctx); + ctx->current = &panel; + nk_panel_begin(ctx, (flags & NK_WINDOW_TITLE) ? title: 0, NK_PANEL_GROUP); + + win->buffer = panel.buffer; + win->buffer.clip = panel.layout->clip; + panel.layout->offset_x = x_offset; + panel.layout->offset_y = y_offset; + panel.layout->parent = win->layout; + win->layout = panel.layout; + + ctx->current = win; + if ((panel.layout->flags & NK_WINDOW_CLOSED) || + (panel.layout->flags & NK_WINDOW_MINIMIZED)) + { + nk_flags f = panel.layout->flags; + nk_group_scrolled_end(ctx); + if (f & NK_WINDOW_CLOSED) + return NK_WINDOW_CLOSED; + if (f & NK_WINDOW_MINIMIZED) + return NK_WINDOW_MINIMIZED; + } + return 1; +} +NK_API void +nk_group_scrolled_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *parent; + struct nk_panel *g; + + struct nk_rect clip; + struct nk_window pan; + struct nk_vec2 panel_padding; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return; + + /* make sure nk_group_begin was called correctly */ + NK_ASSERT(ctx->current); + win = ctx->current; + NK_ASSERT(win->layout); + g = win->layout; + NK_ASSERT(g->parent); + parent = g->parent; + + /* dummy window */ + nk_zero_struct(pan); + panel_padding = nk_panel_get_padding(&ctx->style, NK_PANEL_GROUP); + pan.bounds.y = g->bounds.y - (g->header_height + g->menu.h); + pan.bounds.x = g->bounds.x - panel_padding.x; + pan.bounds.w = g->bounds.w + 2 * panel_padding.x; + pan.bounds.h = g->bounds.h + g->header_height + g->menu.h; + if (g->flags & NK_WINDOW_BORDER) { + pan.bounds.x -= g->border; + pan.bounds.y -= g->border; + pan.bounds.w += 2*g->border; + pan.bounds.h += 2*g->border; + } + if (!(g->flags & NK_WINDOW_NO_SCROLLBAR)) { + pan.bounds.w += ctx->style.window.scrollbar_size.x; + pan.bounds.h += ctx->style.window.scrollbar_size.y; + } + pan.scrollbar.x = *g->offset_x; + pan.scrollbar.y = *g->offset_y; + pan.flags = g->flags; + pan.buffer = win->buffer; + pan.layout = g; + pan.parent = win; + ctx->current = &pan; + + /* make sure group has correct clipping rectangle */ + nk_unify(&clip, &parent->clip, pan.bounds.x, pan.bounds.y, + pan.bounds.x + pan.bounds.w, pan.bounds.y + pan.bounds.h + panel_padding.x); + nk_push_scissor(&pan.buffer, clip); + nk_end(ctx); + + win->buffer = pan.buffer; + nk_push_scissor(&win->buffer, parent->clip); + ctx->current = win; + win->layout = parent; + g->bounds = pan.bounds; + return; +} +NK_API nk_bool +nk_group_scrolled_begin(struct nk_context *ctx, + struct nk_scroll *scroll, const char *title, nk_flags flags) +{ + return nk_group_scrolled_offset_begin(ctx, &scroll->x, &scroll->y, title, flags); +} +NK_API nk_bool +nk_group_begin_titled(struct nk_context *ctx, const char *id, + const char *title, nk_flags flags) +{ + int id_len; + nk_hash id_hash; + struct nk_window *win; + nk_uint *x_offset; + nk_uint *y_offset; + + NK_ASSERT(ctx); + NK_ASSERT(id); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !id) + return 0; + + /* find persistent group scrollbar value */ + win = ctx->current; + id_len = (int)nk_strlen(id); + id_hash = nk_murmur_hash(id, (int)id_len, NK_PANEL_GROUP); + x_offset = nk_find_value(win, id_hash); + if (!x_offset) { + x_offset = nk_add_value(ctx, win, id_hash, 0); + y_offset = nk_add_value(ctx, win, id_hash+1, 0); + + NK_ASSERT(x_offset); + NK_ASSERT(y_offset); + if (!x_offset || !y_offset) return 0; + *x_offset = *y_offset = 0; + } else y_offset = nk_find_value(win, id_hash+1); + return nk_group_scrolled_offset_begin(ctx, x_offset, y_offset, title, flags); +} +NK_API nk_bool +nk_group_begin(struct nk_context *ctx, const char *title, nk_flags flags) +{ + return nk_group_begin_titled(ctx, title, title, flags); +} +NK_API void +nk_group_end(struct nk_context *ctx) +{ + nk_group_scrolled_end(ctx); +} +NK_API void +nk_group_get_scroll(struct nk_context *ctx, const char *id, nk_uint *x_offset, nk_uint *y_offset) +{ + int id_len; + nk_hash id_hash; + struct nk_window *win; + nk_uint *x_offset_ptr; + nk_uint *y_offset_ptr; + + NK_ASSERT(ctx); + NK_ASSERT(id); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !id) + return; + + /* find persistent group scrollbar value */ + win = ctx->current; + id_len = (int)nk_strlen(id); + id_hash = nk_murmur_hash(id, (int)id_len, NK_PANEL_GROUP); + x_offset_ptr = nk_find_value(win, id_hash); + if (!x_offset_ptr) { + x_offset_ptr = nk_add_value(ctx, win, id_hash, 0); + y_offset_ptr = nk_add_value(ctx, win, id_hash+1, 0); + + NK_ASSERT(x_offset_ptr); + NK_ASSERT(y_offset_ptr); + if (!x_offset_ptr || !y_offset_ptr) return; + *x_offset_ptr = *y_offset_ptr = 0; + } else y_offset_ptr = nk_find_value(win, id_hash+1); + if (x_offset) + *x_offset = *x_offset_ptr; + if (y_offset) + *y_offset = *y_offset_ptr; +} +NK_API void +nk_group_set_scroll(struct nk_context *ctx, const char *id, nk_uint x_offset, nk_uint y_offset) +{ + int id_len; + nk_hash id_hash; + struct nk_window *win; + nk_uint *x_offset_ptr; + nk_uint *y_offset_ptr; + + NK_ASSERT(ctx); + NK_ASSERT(id); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !id) + return; + + /* find persistent group scrollbar value */ + win = ctx->current; + id_len = (int)nk_strlen(id); + id_hash = nk_murmur_hash(id, (int)id_len, NK_PANEL_GROUP); + x_offset_ptr = nk_find_value(win, id_hash); + if (!x_offset_ptr) { + x_offset_ptr = nk_add_value(ctx, win, id_hash, 0); + y_offset_ptr = nk_add_value(ctx, win, id_hash+1, 0); + + NK_ASSERT(x_offset_ptr); + NK_ASSERT(y_offset_ptr); + if (!x_offset_ptr || !y_offset_ptr) return; + *x_offset_ptr = *y_offset_ptr = 0; + } else y_offset_ptr = nk_find_value(win, id_hash+1); + *x_offset_ptr = x_offset; + *y_offset_ptr = y_offset; +} + + + + +/* =============================================================== + * + * LIST VIEW + * + * ===============================================================*/ +NK_API nk_bool +nk_list_view_begin(struct nk_context *ctx, struct nk_list_view *view, + const char *title, nk_flags flags, int row_height, int row_count) +{ + int title_len; + nk_hash title_hash; + nk_uint *x_offset; + nk_uint *y_offset; + + int result; + struct nk_window *win; + struct nk_panel *layout; + const struct nk_style *style; + struct nk_vec2 item_spacing; + + NK_ASSERT(ctx); + NK_ASSERT(view); + NK_ASSERT(title); + if (!ctx || !view || !title) return 0; + + win = ctx->current; + style = &ctx->style; + item_spacing = style->window.spacing; + row_height += NK_MAX(0, (int)item_spacing.y); + + /* find persistent list view scrollbar offset */ + title_len = (int)nk_strlen(title); + title_hash = nk_murmur_hash(title, (int)title_len, NK_PANEL_GROUP); + x_offset = nk_find_value(win, title_hash); + if (!x_offset) { + x_offset = nk_add_value(ctx, win, title_hash, 0); + y_offset = nk_add_value(ctx, win, title_hash+1, 0); + + NK_ASSERT(x_offset); + NK_ASSERT(y_offset); + if (!x_offset || !y_offset) return 0; + *x_offset = *y_offset = 0; + } else y_offset = nk_find_value(win, title_hash+1); + view->scroll_value = *y_offset; + view->scroll_pointer = y_offset; + + *y_offset = 0; + result = nk_group_scrolled_offset_begin(ctx, x_offset, y_offset, title, flags); + win = ctx->current; + layout = win->layout; + + view->total_height = row_height * NK_MAX(row_count,1); + view->begin = (int)NK_MAX(((float)view->scroll_value / (float)row_height), 0.0f); + view->count = (int)NK_MAX(nk_iceilf((layout->clip.h)/(float)row_height),0); + view->count = NK_MIN(view->count, row_count - view->begin); + view->end = view->begin + view->count; + view->ctx = ctx; + return result; +} +NK_API void +nk_list_view_end(struct nk_list_view *view) +{ + struct nk_context *ctx; + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(view); + NK_ASSERT(view->ctx); + NK_ASSERT(view->scroll_pointer); + if (!view || !view->ctx) return; + + ctx = view->ctx; + win = ctx->current; + layout = win->layout; + layout->at_y = layout->bounds.y + (float)view->total_height; + *view->scroll_pointer = *view->scroll_pointer + view->scroll_value; + nk_group_end(view->ctx); +} + + + + + +/* =============================================================== + * + * WIDGET + * + * ===============================================================*/ +NK_API struct nk_rect +nk_widget_bounds(struct nk_context *ctx) +{ + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return nk_rect(0,0,0,0); + nk_layout_peek(&bounds, ctx); + return bounds; +} +NK_API struct nk_vec2 +nk_widget_position(struct nk_context *ctx) +{ + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return nk_vec2(0,0); + + nk_layout_peek(&bounds, ctx); + return nk_vec2(bounds.x, bounds.y); +} +NK_API struct nk_vec2 +nk_widget_size(struct nk_context *ctx) +{ + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return nk_vec2(0,0); + + nk_layout_peek(&bounds, ctx); + return nk_vec2(bounds.w, bounds.h); +} +NK_API float +nk_widget_width(struct nk_context *ctx) +{ + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return 0; + + nk_layout_peek(&bounds, ctx); + return bounds.w; +} +NK_API float +nk_widget_height(struct nk_context *ctx) +{ + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return 0; + + nk_layout_peek(&bounds, ctx); + return bounds.h; +} +NK_API nk_bool +nk_widget_is_hovered(struct nk_context *ctx) +{ + struct nk_rect c, v; + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current || ctx->active != ctx->current) + return 0; + + c = ctx->current->layout->clip; + c.x = (float)((int)c.x); + c.y = (float)((int)c.y); + c.w = (float)((int)c.w); + c.h = (float)((int)c.h); + + nk_layout_peek(&bounds, ctx); + nk_unify(&v, &c, bounds.x, bounds.y, bounds.x + bounds.w, bounds.y + bounds.h); + if (!NK_INTERSECT(c.x, c.y, c.w, c.h, bounds.x, bounds.y, bounds.w, bounds.h)) + return 0; + return nk_input_is_mouse_hovering_rect(&ctx->input, bounds); +} +NK_API nk_bool +nk_widget_is_mouse_clicked(struct nk_context *ctx, enum nk_buttons btn) +{ + struct nk_rect c, v; + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current || ctx->active != ctx->current) + return 0; + + c = ctx->current->layout->clip; + c.x = (float)((int)c.x); + c.y = (float)((int)c.y); + c.w = (float)((int)c.w); + c.h = (float)((int)c.h); + + nk_layout_peek(&bounds, ctx); + nk_unify(&v, &c, bounds.x, bounds.y, bounds.x + bounds.w, bounds.y + bounds.h); + if (!NK_INTERSECT(c.x, c.y, c.w, c.h, bounds.x, bounds.y, bounds.w, bounds.h)) + return 0; + return nk_input_mouse_clicked(&ctx->input, btn, bounds); +} +NK_API nk_bool +nk_widget_has_mouse_click_down(struct nk_context *ctx, enum nk_buttons btn, nk_bool down) +{ + struct nk_rect c, v; + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current || ctx->active != ctx->current) + return 0; + + c = ctx->current->layout->clip; + c.x = (float)((int)c.x); + c.y = (float)((int)c.y); + c.w = (float)((int)c.w); + c.h = (float)((int)c.h); + + nk_layout_peek(&bounds, ctx); + nk_unify(&v, &c, bounds.x, bounds.y, bounds.x + bounds.w, bounds.y + bounds.h); + if (!NK_INTERSECT(c.x, c.y, c.w, c.h, bounds.x, bounds.y, bounds.w, bounds.h)) + return 0; + return nk_input_has_mouse_click_down_in_rect(&ctx->input, btn, bounds, down); +} +NK_API enum nk_widget_layout_states +nk_widget(struct nk_rect *bounds, const struct nk_context *ctx) +{ + struct nk_rect c, v; + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return NK_WIDGET_INVALID; + + /* allocate space and check if the widget needs to be updated and drawn */ + nk_panel_alloc_space(bounds, ctx); + win = ctx->current; + layout = win->layout; + in = &ctx->input; + c = layout->clip; + + /* if one of these triggers you forgot to add an `if` condition around either + a window, group, popup, combobox or contextual menu `begin` and `end` block. + Example: + if (nk_begin(...) {...} nk_end(...); or + if (nk_group_begin(...) { nk_group_end(...);} */ + NK_ASSERT(!(layout->flags & NK_WINDOW_MINIMIZED)); + NK_ASSERT(!(layout->flags & NK_WINDOW_HIDDEN)); + NK_ASSERT(!(layout->flags & NK_WINDOW_CLOSED)); + + /* need to convert to int here to remove floating point errors */ + bounds->x = (float)((int)bounds->x); + bounds->y = (float)((int)bounds->y); + bounds->w = (float)((int)bounds->w); + bounds->h = (float)((int)bounds->h); + + c.x = (float)((int)c.x); + c.y = (float)((int)c.y); + c.w = (float)((int)c.w); + c.h = (float)((int)c.h); + + nk_unify(&v, &c, bounds->x, bounds->y, bounds->x + bounds->w, bounds->y + bounds->h); + if (!NK_INTERSECT(c.x, c.y, c.w, c.h, bounds->x, bounds->y, bounds->w, bounds->h)) + return NK_WIDGET_INVALID; + if (!NK_INBOX(in->mouse.pos.x, in->mouse.pos.y, v.x, v.y, v.w, v.h)) + return NK_WIDGET_ROM; + return NK_WIDGET_VALID; +} +NK_API enum nk_widget_layout_states +nk_widget_fitting(struct nk_rect *bounds, struct nk_context *ctx, + struct nk_vec2 item_padding) +{ + /* update the bounds to stand without padding */ + enum nk_widget_layout_states state; + NK_UNUSED(item_padding); + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return NK_WIDGET_INVALID; + + state = nk_widget(bounds, ctx); + return state; +} +NK_API void +nk_spacing(struct nk_context *ctx, int cols) +{ + struct nk_window *win; + struct nk_panel *layout; + struct nk_rect none; + int i, index, rows; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + /* spacing over row boundaries */ + win = ctx->current; + layout = win->layout; + index = (layout->row.index + cols) % layout->row.columns; + rows = (layout->row.index + cols) / layout->row.columns; + if (rows) { + for (i = 0; i < rows; ++i) + nk_panel_alloc_row(ctx, win); + cols = index; + } + /* non table layout need to allocate space */ + if (layout->row.type != NK_LAYOUT_DYNAMIC_FIXED && + layout->row.type != NK_LAYOUT_STATIC_FIXED) { + for (i = 0; i < cols; ++i) + nk_panel_alloc_space(&none, ctx); + } layout->row.index = index; +} + + + + + +/* =============================================================== + * + * TEXT + * + * ===============================================================*/ +NK_LIB void +nk_widget_text(struct nk_command_buffer *o, struct nk_rect b, + const char *string, int len, const struct nk_text *t, + nk_flags a, const struct nk_user_font *f) +{ + struct nk_rect label; + float text_width; + + NK_ASSERT(o); + NK_ASSERT(t); + if (!o || !t) return; + + b.h = NK_MAX(b.h, 2 * t->padding.y); + label.x = 0; label.w = 0; + label.y = b.y + t->padding.y; + label.h = NK_MIN(f->height, b.h - 2 * t->padding.y); + + text_width = f->width(f->userdata, f->height, (const char*)string, len); + text_width += (2.0f * t->padding.x); + + /* align in x-axis */ + if (a & NK_TEXT_ALIGN_LEFT) { + label.x = b.x + t->padding.x; + label.w = NK_MAX(0, b.w - 2 * t->padding.x); + } else if (a & NK_TEXT_ALIGN_CENTERED) { + label.w = NK_MAX(1, 2 * t->padding.x + (float)text_width); + label.x = (b.x + t->padding.x + ((b.w - 2 * t->padding.x) - label.w) / 2); + label.x = NK_MAX(b.x + t->padding.x, label.x); + label.w = NK_MIN(b.x + b.w, label.x + label.w); + if (label.w >= label.x) label.w -= label.x; + } else if (a & NK_TEXT_ALIGN_RIGHT) { + label.x = NK_MAX(b.x + t->padding.x, (b.x + b.w) - (2 * t->padding.x + (float)text_width)); + label.w = (float)text_width + 2 * t->padding.x; + } else return; + + /* align in y-axis */ + if (a & NK_TEXT_ALIGN_MIDDLE) { + label.y = b.y + b.h/2.0f - (float)f->height/2.0f; + label.h = NK_MAX(b.h/2.0f, b.h - (b.h/2.0f + f->height/2.0f)); + } else if (a & NK_TEXT_ALIGN_BOTTOM) { + label.y = b.y + b.h - f->height; + label.h = f->height; + } + nk_draw_text(o, label, (const char*)string, len, f, t->background, t->text); +} +NK_LIB void +nk_widget_text_wrap(struct nk_command_buffer *o, struct nk_rect b, + const char *string, int len, const struct nk_text *t, + const struct nk_user_font *f) +{ + float width; + int glyphs = 0; + int fitting = 0; + int done = 0; + struct nk_rect line; + struct nk_text text; + NK_INTERN nk_rune seperator[] = {' '}; + + NK_ASSERT(o); + NK_ASSERT(t); + if (!o || !t) return; + + text.padding = nk_vec2(0,0); + text.background = t->background; + text.text = t->text; + + b.w = NK_MAX(b.w, 2 * t->padding.x); + b.h = NK_MAX(b.h, 2 * t->padding.y); + b.h = b.h - 2 * t->padding.y; + + line.x = b.x + t->padding.x; + line.y = b.y + t->padding.y; + line.w = b.w - 2 * t->padding.x; + line.h = 2 * t->padding.y + f->height; + + fitting = nk_text_clamp(f, string, len, line.w, &glyphs, &width, seperator,NK_LEN(seperator)); + while (done < len) { + if (!fitting || line.y + line.h >= (b.y + b.h)) break; + nk_widget_text(o, line, &string[done], fitting, &text, NK_TEXT_LEFT, f); + done += fitting; + line.y += f->height + 2 * t->padding.y; + fitting = nk_text_clamp(f, &string[done], len - done, line.w, &glyphs, &width, seperator,NK_LEN(seperator)); + } +} +NK_API void +nk_text_colored(struct nk_context *ctx, const char *str, int len, + nk_flags alignment, struct nk_color color) +{ + struct nk_window *win; + const struct nk_style *style; + + struct nk_vec2 item_padding; + struct nk_rect bounds; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return; + + win = ctx->current; + style = &ctx->style; + nk_panel_alloc_space(&bounds, ctx); + item_padding = style->text.padding; + + text.padding.x = item_padding.x; + text.padding.y = item_padding.y; + text.background = style->window.background; + text.text = color; + nk_widget_text(&win->buffer, bounds, str, len, &text, alignment, style->font); +} +NK_API void +nk_text_wrap_colored(struct nk_context *ctx, const char *str, + int len, struct nk_color color) +{ + struct nk_window *win; + const struct nk_style *style; + + struct nk_vec2 item_padding; + struct nk_rect bounds; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return; + + win = ctx->current; + style = &ctx->style; + nk_panel_alloc_space(&bounds, ctx); + item_padding = style->text.padding; + + text.padding.x = item_padding.x; + text.padding.y = item_padding.y; + text.background = style->window.background; + text.text = color; + nk_widget_text_wrap(&win->buffer, bounds, str, len, &text, style->font); +} +#ifdef NK_INCLUDE_STANDARD_VARARGS +NK_API void +nk_labelf_colored(struct nk_context *ctx, nk_flags flags, + struct nk_color color, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + nk_labelfv_colored(ctx, flags, color, fmt, args); + va_end(args); +} +NK_API void +nk_labelf_colored_wrap(struct nk_context *ctx, struct nk_color color, + const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + nk_labelfv_colored_wrap(ctx, color, fmt, args); + va_end(args); +} +NK_API void +nk_labelf(struct nk_context *ctx, nk_flags flags, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + nk_labelfv(ctx, flags, fmt, args); + va_end(args); +} +NK_API void +nk_labelf_wrap(struct nk_context *ctx, const char *fmt,...) +{ + va_list args; + va_start(args, fmt); + nk_labelfv_wrap(ctx, fmt, args); + va_end(args); +} +NK_API void +nk_labelfv_colored(struct nk_context *ctx, nk_flags flags, + struct nk_color color, const char *fmt, va_list args) +{ + char buf[256]; + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_label_colored(ctx, buf, flags, color); +} + +NK_API void +nk_labelfv_colored_wrap(struct nk_context *ctx, struct nk_color color, + const char *fmt, va_list args) +{ + char buf[256]; + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_label_colored_wrap(ctx, buf, color); +} + +NK_API void +nk_labelfv(struct nk_context *ctx, nk_flags flags, const char *fmt, va_list args) +{ + char buf[256]; + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_label(ctx, buf, flags); +} + +NK_API void +nk_labelfv_wrap(struct nk_context *ctx, const char *fmt, va_list args) +{ + char buf[256]; + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_label_wrap(ctx, buf); +} + +NK_API void +nk_value_bool(struct nk_context *ctx, const char *prefix, int value) +{ + nk_labelf(ctx, NK_TEXT_LEFT, "%s: %s", prefix, ((value) ? "true": "false")); +} +NK_API void +nk_value_int(struct nk_context *ctx, const char *prefix, int value) +{ + nk_labelf(ctx, NK_TEXT_LEFT, "%s: %d", prefix, value); +} +NK_API void +nk_value_uint(struct nk_context *ctx, const char *prefix, unsigned int value) +{ + nk_labelf(ctx, NK_TEXT_LEFT, "%s: %u", prefix, value); +} +NK_API void +nk_value_float(struct nk_context *ctx, const char *prefix, float value) +{ + double double_value = (double)value; + nk_labelf(ctx, NK_TEXT_LEFT, "%s: %.3f", prefix, double_value); +} +NK_API void +nk_value_color_byte(struct nk_context *ctx, const char *p, struct nk_color c) +{ + nk_labelf(ctx, NK_TEXT_LEFT, "%s: (%d, %d, %d, %d)", p, c.r, c.g, c.b, c.a); +} +NK_API void +nk_value_color_float(struct nk_context *ctx, const char *p, struct nk_color color) +{ + double c[4]; nk_color_dv(c, color); + nk_labelf(ctx, NK_TEXT_LEFT, "%s: (%.2f, %.2f, %.2f, %.2f)", + p, c[0], c[1], c[2], c[3]); +} +NK_API void +nk_value_color_hex(struct nk_context *ctx, const char *prefix, struct nk_color color) +{ + char hex[16]; + nk_color_hex_rgba(hex, color); + nk_labelf(ctx, NK_TEXT_LEFT, "%s: %s", prefix, hex); +} +#endif +NK_API void +nk_text(struct nk_context *ctx, const char *str, int len, nk_flags alignment) +{ + NK_ASSERT(ctx); + if (!ctx) return; + nk_text_colored(ctx, str, len, alignment, ctx->style.text.color); +} +NK_API void +nk_text_wrap(struct nk_context *ctx, const char *str, int len) +{ + NK_ASSERT(ctx); + if (!ctx) return; + nk_text_wrap_colored(ctx, str, len, ctx->style.text.color); +} +NK_API void +nk_label(struct nk_context *ctx, const char *str, nk_flags alignment) +{ + nk_text(ctx, str, nk_strlen(str), alignment); +} +NK_API void +nk_label_colored(struct nk_context *ctx, const char *str, nk_flags align, + struct nk_color color) +{ + nk_text_colored(ctx, str, nk_strlen(str), align, color); +} +NK_API void +nk_label_wrap(struct nk_context *ctx, const char *str) +{ + nk_text_wrap(ctx, str, nk_strlen(str)); +} +NK_API void +nk_label_colored_wrap(struct nk_context *ctx, const char *str, struct nk_color color) +{ + nk_text_wrap_colored(ctx, str, nk_strlen(str), color); +} + + + + + +/* =============================================================== + * + * IMAGE + * + * ===============================================================*/ +NK_API nk_handle +nk_handle_ptr(void *ptr) +{ + nk_handle handle = {0}; + handle.ptr = ptr; + return handle; +} +NK_API nk_handle +nk_handle_id(int id) +{ + nk_handle handle; + nk_zero_struct(handle); + handle.id = id; + return handle; +} +NK_API struct nk_image +nk_subimage_ptr(void *ptr, unsigned short w, unsigned short h, struct nk_rect r) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle.ptr = ptr; + s.w = w; s.h = h; + s.region[0] = (unsigned short)r.x; + s.region[1] = (unsigned short)r.y; + s.region[2] = (unsigned short)r.w; + s.region[3] = (unsigned short)r.h; + return s; +} +NK_API struct nk_image +nk_subimage_id(int id, unsigned short w, unsigned short h, struct nk_rect r) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle.id = id; + s.w = w; s.h = h; + s.region[0] = (unsigned short)r.x; + s.region[1] = (unsigned short)r.y; + s.region[2] = (unsigned short)r.w; + s.region[3] = (unsigned short)r.h; + return s; +} +NK_API struct nk_image +nk_subimage_handle(nk_handle handle, unsigned short w, unsigned short h, + struct nk_rect r) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle = handle; + s.w = w; s.h = h; + s.region[0] = (unsigned short)r.x; + s.region[1] = (unsigned short)r.y; + s.region[2] = (unsigned short)r.w; + s.region[3] = (unsigned short)r.h; + return s; +} +NK_API struct nk_image +nk_image_handle(nk_handle handle) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle = handle; + s.w = 0; s.h = 0; + s.region[0] = 0; + s.region[1] = 0; + s.region[2] = 0; + s.region[3] = 0; + return s; +} +NK_API struct nk_image +nk_image_ptr(void *ptr) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + NK_ASSERT(ptr); + s.handle.ptr = ptr; + s.w = 0; s.h = 0; + s.region[0] = 0; + s.region[1] = 0; + s.region[2] = 0; + s.region[3] = 0; + return s; +} +NK_API struct nk_image +nk_image_id(int id) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle.id = id; + s.w = 0; s.h = 0; + s.region[0] = 0; + s.region[1] = 0; + s.region[2] = 0; + s.region[3] = 0; + return s; +} +NK_API nk_bool +nk_image_is_subimage(const struct nk_image* img) +{ + NK_ASSERT(img); + return !(img->w == 0 && img->h == 0); +} +NK_API void +nk_image(struct nk_context *ctx, struct nk_image img) +{ + struct nk_window *win; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return; + + win = ctx->current; + if (!nk_widget(&bounds, ctx)) return; + nk_draw_image(&win->buffer, bounds, &img, nk_white); +} +NK_API void +nk_image_color(struct nk_context *ctx, struct nk_image img, struct nk_color col) +{ + struct nk_window *win; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return; + + win = ctx->current; + if (!nk_widget(&bounds, ctx)) return; + nk_draw_image(&win->buffer, bounds, &img, col); +} + + + + + +/* ============================================================== + * + * BUTTON + * + * ===============================================================*/ +NK_LIB void +nk_draw_symbol(struct nk_command_buffer *out, enum nk_symbol_type type, + struct nk_rect content, struct nk_color background, struct nk_color foreground, + float border_width, const struct nk_user_font *font) +{ + switch (type) { + case NK_SYMBOL_X: + case NK_SYMBOL_UNDERSCORE: + case NK_SYMBOL_PLUS: + case NK_SYMBOL_MINUS: { + /* single character text symbol */ + const char *X = (type == NK_SYMBOL_X) ? "x": + (type == NK_SYMBOL_UNDERSCORE) ? "_": + (type == NK_SYMBOL_PLUS) ? "+": "-"; + struct nk_text text; + text.padding = nk_vec2(0,0); + text.background = background; + text.text = foreground; + nk_widget_text(out, content, X, 1, &text, NK_TEXT_CENTERED, font); + } break; + case NK_SYMBOL_CIRCLE_SOLID: + case NK_SYMBOL_CIRCLE_OUTLINE: + case NK_SYMBOL_RECT_SOLID: + case NK_SYMBOL_RECT_OUTLINE: { + /* simple empty/filled shapes */ + if (type == NK_SYMBOL_RECT_SOLID || type == NK_SYMBOL_RECT_OUTLINE) { + nk_fill_rect(out, content, 0, foreground); + if (type == NK_SYMBOL_RECT_OUTLINE) + nk_fill_rect(out, nk_shrink_rect(content, border_width), 0, background); + } else { + nk_fill_circle(out, content, foreground); + if (type == NK_SYMBOL_CIRCLE_OUTLINE) + nk_fill_circle(out, nk_shrink_rect(content, 1), background); + } + } break; + case NK_SYMBOL_TRIANGLE_UP: + case NK_SYMBOL_TRIANGLE_DOWN: + case NK_SYMBOL_TRIANGLE_LEFT: + case NK_SYMBOL_TRIANGLE_RIGHT: { + enum nk_heading heading; + struct nk_vec2 points[3]; + heading = (type == NK_SYMBOL_TRIANGLE_RIGHT) ? NK_RIGHT : + (type == NK_SYMBOL_TRIANGLE_LEFT) ? NK_LEFT: + (type == NK_SYMBOL_TRIANGLE_UP) ? NK_UP: NK_DOWN; + nk_triangle_from_direction(points, content, 0, 0, heading); + nk_fill_triangle(out, points[0].x, points[0].y, points[1].x, points[1].y, + points[2].x, points[2].y, foreground); + } break; + default: + case NK_SYMBOL_NONE: + case NK_SYMBOL_MAX: break; + } +} +NK_LIB nk_bool +nk_button_behavior(nk_flags *state, struct nk_rect r, + const struct nk_input *i, enum nk_button_behavior behavior) +{ + int ret = 0; + nk_widget_state_reset(state); + if (!i) return 0; + if (nk_input_is_mouse_hovering_rect(i, r)) { + *state = NK_WIDGET_STATE_HOVERED; + if (nk_input_is_mouse_down(i, NK_BUTTON_LEFT)) + *state = NK_WIDGET_STATE_ACTIVE; + if (nk_input_has_mouse_click_in_rect(i, NK_BUTTON_LEFT, r)) { + ret = (behavior != NK_BUTTON_DEFAULT) ? + nk_input_is_mouse_down(i, NK_BUTTON_LEFT): +#ifdef NK_BUTTON_TRIGGER_ON_RELEASE + nk_input_is_mouse_released(i, NK_BUTTON_LEFT); +#else + nk_input_is_mouse_pressed(i, NK_BUTTON_LEFT); +#endif + } + } + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(i, r)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(i, r)) + *state |= NK_WIDGET_STATE_LEFT; + return ret; +} +NK_LIB const struct nk_style_item* +nk_draw_button(struct nk_command_buffer *out, + const struct nk_rect *bounds, nk_flags state, + const struct nk_style_button *style) +{ + const struct nk_style_item *background; + if (state & NK_WIDGET_STATE_HOVER) + background = &style->hover; + else if (state & NK_WIDGET_STATE_ACTIVED) + background = &style->active; + else background = &style->normal; + + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + } else { + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); + } + return background; +} +NK_LIB nk_bool +nk_do_button(nk_flags *state, struct nk_command_buffer *out, struct nk_rect r, + const struct nk_style_button *style, const struct nk_input *in, + enum nk_button_behavior behavior, struct nk_rect *content) +{ + struct nk_rect bounds; + NK_ASSERT(style); + NK_ASSERT(state); + NK_ASSERT(out); + if (!out || !style) + return nk_false; + + /* calculate button content space */ + content->x = r.x + style->padding.x + style->border + style->rounding; + content->y = r.y + style->padding.y + style->border + style->rounding; + content->w = r.w - (2 * style->padding.x + style->border + style->rounding*2); + content->h = r.h - (2 * style->padding.y + style->border + style->rounding*2); + + /* execute button behavior */ + bounds.x = r.x - style->touch_padding.x; + bounds.y = r.y - style->touch_padding.y; + bounds.w = r.w + 2 * style->touch_padding.x; + bounds.h = r.h + 2 * style->touch_padding.y; + return nk_button_behavior(state, bounds, in, behavior); +} +NK_LIB void +nk_draw_button_text(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state, + const struct nk_style_button *style, const char *txt, int len, + nk_flags text_alignment, const struct nk_user_font *font) +{ + struct nk_text text; + const struct nk_style_item *background; + background = nk_draw_button(out, bounds, state, style); + + /* select correct colors/images */ + if (background->type == NK_STYLE_ITEM_COLOR) + text.background = background->data.color; + else text.background = style->text_background; + if (state & NK_WIDGET_STATE_HOVER) + text.text = style->text_hover; + else if (state & NK_WIDGET_STATE_ACTIVED) + text.text = style->text_active; + else text.text = style->text_normal; + + text.padding = nk_vec2(0,0); + nk_widget_text(out, *content, txt, len, &text, text_alignment, font); +} +NK_LIB nk_bool +nk_do_button_text(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + const char *string, int len, nk_flags align, enum nk_button_behavior behavior, + const struct nk_style_button *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + struct nk_rect content; + int ret = nk_false; + + NK_ASSERT(state); + NK_ASSERT(style); + NK_ASSERT(out); + NK_ASSERT(string); + NK_ASSERT(font); + if (!out || !style || !font || !string) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_text(out, &bounds, &content, *state, style, string, len, align, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} +NK_LIB void +nk_draw_button_symbol(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *content, + nk_flags state, const struct nk_style_button *style, + enum nk_symbol_type type, const struct nk_user_font *font) +{ + struct nk_color sym, bg; + const struct nk_style_item *background; + + /* select correct colors/images */ + background = nk_draw_button(out, bounds, state, style); + if (background->type == NK_STYLE_ITEM_COLOR) + bg = background->data.color; + else bg = style->text_background; + + if (state & NK_WIDGET_STATE_HOVER) + sym = style->text_hover; + else if (state & NK_WIDGET_STATE_ACTIVED) + sym = style->text_active; + else sym = style->text_normal; + nk_draw_symbol(out, type, *content, bg, sym, 1, font); +} +NK_LIB nk_bool +nk_do_button_symbol(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + enum nk_symbol_type symbol, enum nk_button_behavior behavior, + const struct nk_style_button *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + int ret; + struct nk_rect content; + + NK_ASSERT(state); + NK_ASSERT(style); + NK_ASSERT(font); + NK_ASSERT(out); + if (!out || !style || !font || !state) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_symbol(out, &bounds, &content, *state, style, symbol, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} +NK_LIB void +nk_draw_button_image(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *content, + nk_flags state, const struct nk_style_button *style, const struct nk_image *img) +{ + nk_draw_button(out, bounds, state, style); + nk_draw_image(out, *content, img, nk_white); +} +NK_LIB nk_bool +nk_do_button_image(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + struct nk_image img, enum nk_button_behavior b, + const struct nk_style_button *style, const struct nk_input *in) +{ + int ret; + struct nk_rect content; + + NK_ASSERT(state); + NK_ASSERT(style); + NK_ASSERT(out); + if (!out || !style || !state) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, b, &content); + content.x += style->image_padding.x; + content.y += style->image_padding.y; + content.w -= 2 * style->image_padding.x; + content.h -= 2 * style->image_padding.y; + + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_image(out, &bounds, &content, *state, style, &img); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} +NK_LIB void +nk_draw_button_text_symbol(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *label, + const struct nk_rect *symbol, nk_flags state, const struct nk_style_button *style, + const char *str, int len, enum nk_symbol_type type, + const struct nk_user_font *font) +{ + struct nk_color sym; + struct nk_text text; + const struct nk_style_item *background; + + /* select correct background colors/images */ + background = nk_draw_button(out, bounds, state, style); + if (background->type == NK_STYLE_ITEM_COLOR) + text.background = background->data.color; + else text.background = style->text_background; + + /* select correct text colors */ + if (state & NK_WIDGET_STATE_HOVER) { + sym = style->text_hover; + text.text = style->text_hover; + } else if (state & NK_WIDGET_STATE_ACTIVED) { + sym = style->text_active; + text.text = style->text_active; + } else { + sym = style->text_normal; + text.text = style->text_normal; + } + + text.padding = nk_vec2(0,0); + nk_draw_symbol(out, type, *symbol, style->text_background, sym, 0, font); + nk_widget_text(out, *label, str, len, &text, NK_TEXT_CENTERED, font); +} +NK_LIB nk_bool +nk_do_button_text_symbol(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + enum nk_symbol_type symbol, const char *str, int len, nk_flags align, + enum nk_button_behavior behavior, const struct nk_style_button *style, + const struct nk_user_font *font, const struct nk_input *in) +{ + int ret; + struct nk_rect tri = {0,0,0,0}; + struct nk_rect content; + + NK_ASSERT(style); + NK_ASSERT(out); + NK_ASSERT(font); + if (!out || !style || !font) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + tri.y = content.y + (content.h/2) - font->height/2; + tri.w = font->height; tri.h = font->height; + if (align & NK_TEXT_ALIGN_LEFT) { + tri.x = (content.x + content.w) - (2 * style->padding.x + tri.w); + tri.x = NK_MAX(tri.x, 0); + } else tri.x = content.x + 2 * style->padding.x; + + /* draw button */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_text_symbol(out, &bounds, &content, &tri, + *state, style, str, len, symbol, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} +NK_LIB void +nk_draw_button_text_image(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *label, + const struct nk_rect *image, nk_flags state, const struct nk_style_button *style, + const char *str, int len, const struct nk_user_font *font, + const struct nk_image *img) +{ + struct nk_text text; + const struct nk_style_item *background; + background = nk_draw_button(out, bounds, state, style); + + /* select correct colors */ + if (background->type == NK_STYLE_ITEM_COLOR) + text.background = background->data.color; + else text.background = style->text_background; + if (state & NK_WIDGET_STATE_HOVER) + text.text = style->text_hover; + else if (state & NK_WIDGET_STATE_ACTIVED) + text.text = style->text_active; + else text.text = style->text_normal; + + text.padding = nk_vec2(0,0); + nk_widget_text(out, *label, str, len, &text, NK_TEXT_CENTERED, font); + nk_draw_image(out, *image, img, nk_white); +} +NK_LIB nk_bool +nk_do_button_text_image(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + struct nk_image img, const char* str, int len, nk_flags align, + enum nk_button_behavior behavior, const struct nk_style_button *style, + const struct nk_user_font *font, const struct nk_input *in) +{ + int ret; + struct nk_rect icon; + struct nk_rect content; + + NK_ASSERT(style); + NK_ASSERT(state); + NK_ASSERT(font); + NK_ASSERT(out); + if (!out || !font || !style || !str) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + icon.y = bounds.y + style->padding.y; + icon.w = icon.h = bounds.h - 2 * style->padding.y; + if (align & NK_TEXT_ALIGN_LEFT) { + icon.x = (bounds.x + bounds.w) - (2 * style->padding.x + icon.w); + icon.x = NK_MAX(icon.x, 0); + } else icon.x = bounds.x + 2 * style->padding.x; + + icon.x += style->image_padding.x; + icon.y += style->image_padding.y; + icon.w -= 2 * style->image_padding.x; + icon.h -= 2 * style->image_padding.y; + + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_text_image(out, &bounds, &content, &icon, *state, style, str, len, font, &img); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} +NK_API void +nk_button_set_behavior(struct nk_context *ctx, enum nk_button_behavior behavior) +{ + NK_ASSERT(ctx); + if (!ctx) return; + ctx->button_behavior = behavior; +} +NK_API nk_bool +nk_button_push_behavior(struct nk_context *ctx, enum nk_button_behavior behavior) +{ + struct nk_config_stack_button_behavior *button_stack; + struct nk_config_stack_button_behavior_element *element; + + NK_ASSERT(ctx); + if (!ctx) return 0; + + button_stack = &ctx->stacks.button_behaviors; + NK_ASSERT(button_stack->head < (int)NK_LEN(button_stack->elements)); + if (button_stack->head >= (int)NK_LEN(button_stack->elements)) + return 0; + + element = &button_stack->elements[button_stack->head++]; + element->address = &ctx->button_behavior; + element->old_value = ctx->button_behavior; + ctx->button_behavior = behavior; + return 1; +} +NK_API nk_bool +nk_button_pop_behavior(struct nk_context *ctx) +{ + struct nk_config_stack_button_behavior *button_stack; + struct nk_config_stack_button_behavior_element *element; + + NK_ASSERT(ctx); + if (!ctx) return 0; + + button_stack = &ctx->stacks.button_behaviors; + NK_ASSERT(button_stack->head > 0); + if (button_stack->head < 1) + return 0; + + element = &button_stack->elements[--button_stack->head]; + *element->address = element->old_value; + return 1; +} +NK_API nk_bool +nk_button_text_styled(struct nk_context *ctx, + const struct nk_style_button *style, const char *title, int len) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(style); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!style || !ctx || !ctx->current || !ctx->current->layout) return 0; + + win = ctx->current; + layout = win->layout; + state = nk_widget(&bounds, ctx); + + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_text(&ctx->last_widget_state, &win->buffer, bounds, + title, len, style->text_alignment, ctx->button_behavior, + style, in, ctx->style.font); +} +NK_API nk_bool +nk_button_text(struct nk_context *ctx, const char *title, int len) +{ + NK_ASSERT(ctx); + if (!ctx) return 0; + return nk_button_text_styled(ctx, &ctx->style.button, title, len); +} +NK_API nk_bool nk_button_label_styled(struct nk_context *ctx, + const struct nk_style_button *style, const char *title) +{ + return nk_button_text_styled(ctx, style, title, nk_strlen(title)); +} +NK_API nk_bool nk_button_label(struct nk_context *ctx, const char *title) +{ + return nk_button_text(ctx, title, nk_strlen(title)); +} +NK_API nk_bool +nk_button_color(struct nk_context *ctx, struct nk_color color) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + struct nk_style_button button; + + int ret = 0; + struct nk_rect bounds; + struct nk_rect content; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + + button = ctx->style.button; + button.normal = nk_style_item_color(color); + button.hover = nk_style_item_color(color); + button.active = nk_style_item_color(color); + ret = nk_do_button(&ctx->last_widget_state, &win->buffer, bounds, + &button, in, ctx->button_behavior, &content); + nk_draw_button(&win->buffer, &bounds, ctx->last_widget_state, &button); + return ret; +} +NK_API nk_bool +nk_button_symbol_styled(struct nk_context *ctx, + const struct nk_style_button *style, enum nk_symbol_type symbol) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + layout = win->layout; + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_symbol(&ctx->last_widget_state, &win->buffer, bounds, + symbol, ctx->button_behavior, style, in, ctx->style.font); +} +NK_API nk_bool +nk_button_symbol(struct nk_context *ctx, enum nk_symbol_type symbol) +{ + NK_ASSERT(ctx); + if (!ctx) return 0; + return nk_button_symbol_styled(ctx, &ctx->style.button, symbol); +} +NK_API nk_bool +nk_button_image_styled(struct nk_context *ctx, const struct nk_style_button *style, + struct nk_image img) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_image(&ctx->last_widget_state, &win->buffer, bounds, + img, ctx->button_behavior, style, in); +} +NK_API nk_bool +nk_button_image(struct nk_context *ctx, struct nk_image img) +{ + NK_ASSERT(ctx); + if (!ctx) return 0; + return nk_button_image_styled(ctx, &ctx->style.button, img); +} +NK_API nk_bool +nk_button_symbol_text_styled(struct nk_context *ctx, + const struct nk_style_button *style, enum nk_symbol_type symbol, + const char *text, int len, nk_flags align) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, bounds, + symbol, text, len, align, ctx->button_behavior, + style, ctx->style.font, in); +} +NK_API nk_bool +nk_button_symbol_text(struct nk_context *ctx, enum nk_symbol_type symbol, + const char* text, int len, nk_flags align) +{ + NK_ASSERT(ctx); + if (!ctx) return 0; + return nk_button_symbol_text_styled(ctx, &ctx->style.button, symbol, text, len, align); +} +NK_API nk_bool nk_button_symbol_label(struct nk_context *ctx, enum nk_symbol_type symbol, + const char *label, nk_flags align) +{ + return nk_button_symbol_text(ctx, symbol, label, nk_strlen(label), align); +} +NK_API nk_bool nk_button_symbol_label_styled(struct nk_context *ctx, + const struct nk_style_button *style, enum nk_symbol_type symbol, + const char *title, nk_flags align) +{ + return nk_button_symbol_text_styled(ctx, style, symbol, title, nk_strlen(title), align); +} +NK_API nk_bool +nk_button_image_text_styled(struct nk_context *ctx, + const struct nk_style_button *style, struct nk_image img, const char *text, + int len, nk_flags align) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, + bounds, img, text, len, align, ctx->button_behavior, + style, ctx->style.font, in); +} +NK_API nk_bool +nk_button_image_text(struct nk_context *ctx, struct nk_image img, + const char *text, int len, nk_flags align) +{ + return nk_button_image_text_styled(ctx, &ctx->style.button,img, text, len, align); +} +NK_API nk_bool nk_button_image_label(struct nk_context *ctx, struct nk_image img, + const char *label, nk_flags align) +{ + return nk_button_image_text(ctx, img, label, nk_strlen(label), align); +} +NK_API nk_bool nk_button_image_label_styled(struct nk_context *ctx, + const struct nk_style_button *style, struct nk_image img, + const char *label, nk_flags text_alignment) +{ + return nk_button_image_text_styled(ctx, style, img, label, nk_strlen(label), text_alignment); +} + + + + + +/* =============================================================== + * + * TOGGLE + * + * ===============================================================*/ +NK_LIB nk_bool +nk_toggle_behavior(const struct nk_input *in, struct nk_rect select, + nk_flags *state, nk_bool active) +{ + nk_widget_state_reset(state); + if (nk_button_behavior(state, select, in, NK_BUTTON_DEFAULT)) { + *state = NK_WIDGET_STATE_ACTIVE; + active = !active; + } + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, select)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, select)) + *state |= NK_WIDGET_STATE_LEFT; + return active; +} +NK_LIB void +nk_draw_checkbox(struct nk_command_buffer *out, + nk_flags state, const struct nk_style_toggle *style, nk_bool active, + const struct nk_rect *label, const struct nk_rect *selector, + const struct nk_rect *cursors, const char *string, int len, + const struct nk_user_font *font) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + struct nk_text text; + + /* select correct colors/images */ + if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_hover; + } else if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_active; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + text.text = style->text_normal; + } + + /* draw background and cursor */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *selector, 0, style->border_color); + nk_fill_rect(out, nk_shrink_rect(*selector, style->border), 0, background->data.color); + } else nk_draw_image(out, *selector, &background->data.image, nk_white); + if (active) { + if (cursor->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *cursors, &cursor->data.image, nk_white); + else nk_fill_rect(out, *cursors, 0, cursor->data.color); + } + + text.padding.x = 0; + text.padding.y = 0; + text.background = style->text_background; + nk_widget_text(out, *label, string, len, &text, NK_TEXT_LEFT, font); +} +NK_LIB void +nk_draw_option(struct nk_command_buffer *out, + nk_flags state, const struct nk_style_toggle *style, nk_bool active, + const struct nk_rect *label, const struct nk_rect *selector, + const struct nk_rect *cursors, const char *string, int len, + const struct nk_user_font *font) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + struct nk_text text; + + /* select correct colors/images */ + if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_hover; + } else if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_active; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + text.text = style->text_normal; + } + + /* draw background and cursor */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_circle(out, *selector, style->border_color); + nk_fill_circle(out, nk_shrink_rect(*selector, style->border), background->data.color); + } else nk_draw_image(out, *selector, &background->data.image, nk_white); + if (active) { + if (cursor->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *cursors, &cursor->data.image, nk_white); + else nk_fill_circle(out, *cursors, cursor->data.color); + } + + text.padding.x = 0; + text.padding.y = 0; + text.background = style->text_background; + nk_widget_text(out, *label, string, len, &text, NK_TEXT_LEFT, font); +} +NK_LIB nk_bool +nk_do_toggle(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect r, + nk_bool *active, const char *str, int len, enum nk_toggle_type type, + const struct nk_style_toggle *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + int was_active; + struct nk_rect bounds; + struct nk_rect select; + struct nk_rect cursor; + struct nk_rect label; + + NK_ASSERT(style); + NK_ASSERT(out); + NK_ASSERT(font); + if (!out || !style || !font || !active) + return 0; + + r.w = NK_MAX(r.w, font->height + 2 * style->padding.x); + r.h = NK_MAX(r.h, font->height + 2 * style->padding.y); + + /* add additional touch padding for touch screen devices */ + bounds.x = r.x - style->touch_padding.x; + bounds.y = r.y - style->touch_padding.y; + bounds.w = r.w + 2 * style->touch_padding.x; + bounds.h = r.h + 2 * style->touch_padding.y; + + /* calculate the selector space */ + select.w = font->height; + select.h = select.w; + select.y = r.y + r.h/2.0f - select.h/2.0f; + select.x = r.x; + + /* calculate the bounds of the cursor inside the selector */ + cursor.x = select.x + style->padding.x + style->border; + cursor.y = select.y + style->padding.y + style->border; + cursor.w = select.w - (2 * style->padding.x + 2 * style->border); + cursor.h = select.h - (2 * style->padding.y + 2 * style->border); + + /* label behind the selector */ + label.x = select.x + select.w + style->spacing; + label.y = select.y; + label.w = NK_MAX(r.x + r.w, label.x) - label.x; + label.h = select.w; + + /* update selector */ + was_active = *active; + *active = nk_toggle_behavior(in, bounds, state, *active); + + /* draw selector */ + if (style->draw_begin) + style->draw_begin(out, style->userdata); + if (type == NK_TOGGLE_CHECK) { + nk_draw_checkbox(out, *state, style, *active, &label, &select, &cursor, str, len, font); + } else { + nk_draw_option(out, *state, style, *active, &label, &select, &cursor, str, len, font); + } + if (style->draw_end) + style->draw_end(out, style->userdata); + return (was_active != *active); +} +/*---------------------------------------------------------------- + * + * CHECKBOX + * + * --------------------------------------------------------------*/ +NK_API nk_bool +nk_check_text(struct nk_context *ctx, const char *text, int len, nk_bool active) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return active; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return active; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + nk_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &active, + text, len, NK_TOGGLE_CHECK, &style->checkbox, in, style->font); + return active; +} +NK_API unsigned int +nk_check_flags_text(struct nk_context *ctx, const char *text, int len, + unsigned int flags, unsigned int value) +{ + int old_active; + NK_ASSERT(ctx); + NK_ASSERT(text); + if (!ctx || !text) return flags; + old_active = (int)((flags & value) & value); + if (nk_check_text(ctx, text, len, old_active)) + flags |= value; + else flags &= ~value; + return flags; +} +NK_API nk_bool +nk_checkbox_text(struct nk_context *ctx, const char *text, int len, nk_bool *active) +{ + int old_val; + NK_ASSERT(ctx); + NK_ASSERT(text); + NK_ASSERT(active); + if (!ctx || !text || !active) return 0; + old_val = *active; + *active = nk_check_text(ctx, text, len, *active); + return old_val != *active; +} +NK_API nk_bool +nk_checkbox_flags_text(struct nk_context *ctx, const char *text, int len, + unsigned int *flags, unsigned int value) +{ + nk_bool active; + NK_ASSERT(ctx); + NK_ASSERT(text); + NK_ASSERT(flags); + if (!ctx || !text || !flags) return 0; + + active = (int)((*flags & value) & value); + if (nk_checkbox_text(ctx, text, len, &active)) { + if (active) *flags |= value; + else *flags &= ~value; + return 1; + } + return 0; +} +NK_API nk_bool nk_check_label(struct nk_context *ctx, const char *label, nk_bool active) +{ + return nk_check_text(ctx, label, nk_strlen(label), active); +} +NK_API unsigned int nk_check_flags_label(struct nk_context *ctx, const char *label, + unsigned int flags, unsigned int value) +{ + return nk_check_flags_text(ctx, label, nk_strlen(label), flags, value); +} +NK_API nk_bool nk_checkbox_label(struct nk_context *ctx, const char *label, nk_bool *active) +{ + return nk_checkbox_text(ctx, label, nk_strlen(label), active); +} +NK_API nk_bool nk_checkbox_flags_label(struct nk_context *ctx, const char *label, + unsigned int *flags, unsigned int value) +{ + return nk_checkbox_flags_text(ctx, label, nk_strlen(label), flags, value); +} +/*---------------------------------------------------------------- + * + * OPTION + * + * --------------------------------------------------------------*/ +NK_API nk_bool +nk_option_text(struct nk_context *ctx, const char *text, int len, nk_bool is_active) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return is_active; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return (int)state; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + nk_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &is_active, + text, len, NK_TOGGLE_OPTION, &style->option, in, style->font); + return is_active; +} +NK_API nk_bool +nk_radio_text(struct nk_context *ctx, const char *text, int len, nk_bool *active) +{ + int old_value; + NK_ASSERT(ctx); + NK_ASSERT(text); + NK_ASSERT(active); + if (!ctx || !text || !active) return 0; + old_value = *active; + *active = nk_option_text(ctx, text, len, old_value); + return old_value != *active; +} +NK_API nk_bool +nk_option_label(struct nk_context *ctx, const char *label, nk_bool active) +{ + return nk_option_text(ctx, label, nk_strlen(label), active); +} +NK_API nk_bool +nk_radio_label(struct nk_context *ctx, const char *label, nk_bool *active) +{ + return nk_radio_text(ctx, label, nk_strlen(label), active); +} + + + + + +/* =============================================================== + * + * SELECTABLE + * + * ===============================================================*/ +NK_LIB void +nk_draw_selectable(struct nk_command_buffer *out, + nk_flags state, const struct nk_style_selectable *style, nk_bool active, + const struct nk_rect *bounds, + const struct nk_rect *icon, const struct nk_image *img, enum nk_symbol_type sym, + const char *string, int len, nk_flags align, const struct nk_user_font *font) +{ + const struct nk_style_item *background; + struct nk_text text; + text.padding = style->padding; + + /* select correct colors/images */ + if (!active) { + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->pressed; + text.text = style->text_pressed; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + text.text = style->text_hover; + } else { + background = &style->normal; + text.text = style->text_normal; + } + } else { + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->pressed_active; + text.text = style->text_pressed_active; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover_active; + text.text = style->text_hover_active; + } else { + background = &style->normal_active; + text.text = style->text_normal_active; + } + } + /* draw selectable background and text */ + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + text.background = nk_rgba(0,0,0,0); + } else { + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + text.background = background->data.color; + } + if (icon) { + if (img) nk_draw_image(out, *icon, img, nk_white); + else nk_draw_symbol(out, sym, *icon, text.background, text.text, 1, font); + } + nk_widget_text(out, *bounds, string, len, &text, align, font); +} +NK_LIB nk_bool +nk_do_selectable(nk_flags *state, struct nk_command_buffer *out, + struct nk_rect bounds, const char *str, int len, nk_flags align, nk_bool *value, + const struct nk_style_selectable *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + int old_value; + struct nk_rect touch; + + NK_ASSERT(state); + NK_ASSERT(out); + NK_ASSERT(str); + NK_ASSERT(len); + NK_ASSERT(value); + NK_ASSERT(style); + NK_ASSERT(font); + + if (!state || !out || !str || !len || !value || !style || !font) return 0; + old_value = *value; + + /* remove padding */ + touch.x = bounds.x - style->touch_padding.x; + touch.y = bounds.y - style->touch_padding.y; + touch.w = bounds.w + style->touch_padding.x * 2; + touch.h = bounds.h + style->touch_padding.y * 2; + + /* update button */ + if (nk_button_behavior(state, touch, in, NK_BUTTON_DEFAULT)) + *value = !(*value); + + /* draw selectable */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_selectable(out, *state, style, *value, &bounds, 0,0,NK_SYMBOL_NONE, str, len, align, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return old_value != *value; +} +NK_LIB nk_bool +nk_do_selectable_image(nk_flags *state, struct nk_command_buffer *out, + struct nk_rect bounds, const char *str, int len, nk_flags align, nk_bool *value, + const struct nk_image *img, const struct nk_style_selectable *style, + const struct nk_input *in, const struct nk_user_font *font) +{ + nk_bool old_value; + struct nk_rect touch; + struct nk_rect icon; + + NK_ASSERT(state); + NK_ASSERT(out); + NK_ASSERT(str); + NK_ASSERT(len); + NK_ASSERT(value); + NK_ASSERT(style); + NK_ASSERT(font); + + if (!state || !out || !str || !len || !value || !style || !font) return 0; + old_value = *value; + + /* toggle behavior */ + touch.x = bounds.x - style->touch_padding.x; + touch.y = bounds.y - style->touch_padding.y; + touch.w = bounds.w + style->touch_padding.x * 2; + touch.h = bounds.h + style->touch_padding.y * 2; + if (nk_button_behavior(state, touch, in, NK_BUTTON_DEFAULT)) + *value = !(*value); + + icon.y = bounds.y + style->padding.y; + icon.w = icon.h = bounds.h - 2 * style->padding.y; + if (align & NK_TEXT_ALIGN_LEFT) { + icon.x = (bounds.x + bounds.w) - (2 * style->padding.x + icon.w); + icon.x = NK_MAX(icon.x, 0); + } else icon.x = bounds.x + 2 * style->padding.x; + + icon.x += style->image_padding.x; + icon.y += style->image_padding.y; + icon.w -= 2 * style->image_padding.x; + icon.h -= 2 * style->image_padding.y; + + /* draw selectable */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_selectable(out, *state, style, *value, &bounds, &icon, img, NK_SYMBOL_NONE, str, len, align, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return old_value != *value; +} +NK_LIB nk_bool +nk_do_selectable_symbol(nk_flags *state, struct nk_command_buffer *out, + struct nk_rect bounds, const char *str, int len, nk_flags align, nk_bool *value, + enum nk_symbol_type sym, const struct nk_style_selectable *style, + const struct nk_input *in, const struct nk_user_font *font) +{ + int old_value; + struct nk_rect touch; + struct nk_rect icon; + + NK_ASSERT(state); + NK_ASSERT(out); + NK_ASSERT(str); + NK_ASSERT(len); + NK_ASSERT(value); + NK_ASSERT(style); + NK_ASSERT(font); + + if (!state || !out || !str || !len || !value || !style || !font) return 0; + old_value = *value; + + /* toggle behavior */ + touch.x = bounds.x - style->touch_padding.x; + touch.y = bounds.y - style->touch_padding.y; + touch.w = bounds.w + style->touch_padding.x * 2; + touch.h = bounds.h + style->touch_padding.y * 2; + if (nk_button_behavior(state, touch, in, NK_BUTTON_DEFAULT)) + *value = !(*value); + + icon.y = bounds.y + style->padding.y; + icon.w = icon.h = bounds.h - 2 * style->padding.y; + if (align & NK_TEXT_ALIGN_LEFT) { + icon.x = (bounds.x + bounds.w) - (2 * style->padding.x + icon.w); + icon.x = NK_MAX(icon.x, 0); + } else icon.x = bounds.x + 2 * style->padding.x; + + icon.x += style->image_padding.x; + icon.y += style->image_padding.y; + icon.w -= 2 * style->image_padding.x; + icon.h -= 2 * style->image_padding.y; + + /* draw selectable */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_selectable(out, *state, style, *value, &bounds, &icon, 0, sym, str, len, align, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return old_value != *value; +} + +NK_API nk_bool +nk_selectable_text(struct nk_context *ctx, const char *str, int len, + nk_flags align, nk_bool *value) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + enum nk_widget_layout_states state; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(value); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !value) + return 0; + + win = ctx->current; + layout = win->layout; + style = &ctx->style; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_selectable(&ctx->last_widget_state, &win->buffer, bounds, + str, len, align, value, &style->selectable, in, style->font); +} +NK_API nk_bool +nk_selectable_image_text(struct nk_context *ctx, struct nk_image img, + const char *str, int len, nk_flags align, nk_bool *value) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + enum nk_widget_layout_states state; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(value); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !value) + return 0; + + win = ctx->current; + layout = win->layout; + style = &ctx->style; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_selectable_image(&ctx->last_widget_state, &win->buffer, bounds, + str, len, align, value, &img, &style->selectable, in, style->font); +} +NK_API nk_bool +nk_selectable_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, + const char *str, int len, nk_flags align, nk_bool *value) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + enum nk_widget_layout_states state; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(value); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !value) + return 0; + + win = ctx->current; + layout = win->layout; + style = &ctx->style; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_selectable_symbol(&ctx->last_widget_state, &win->buffer, bounds, + str, len, align, value, sym, &style->selectable, in, style->font); +} +NK_API nk_bool +nk_selectable_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, + const char *title, nk_flags align, nk_bool *value) +{ + return nk_selectable_symbol_text(ctx, sym, title, nk_strlen(title), align, value); +} +NK_API nk_bool nk_select_text(struct nk_context *ctx, const char *str, int len, + nk_flags align, nk_bool value) +{ + nk_selectable_text(ctx, str, len, align, &value);return value; +} +NK_API nk_bool nk_selectable_label(struct nk_context *ctx, const char *str, nk_flags align, nk_bool *value) +{ + return nk_selectable_text(ctx, str, nk_strlen(str), align, value); +} +NK_API nk_bool nk_selectable_image_label(struct nk_context *ctx,struct nk_image img, + const char *str, nk_flags align, nk_bool *value) +{ + return nk_selectable_image_text(ctx, img, str, nk_strlen(str), align, value); +} +NK_API nk_bool nk_select_label(struct nk_context *ctx, const char *str, nk_flags align, nk_bool value) +{ + nk_selectable_text(ctx, str, nk_strlen(str), align, &value);return value; +} +NK_API nk_bool nk_select_image_label(struct nk_context *ctx, struct nk_image img, + const char *str, nk_flags align, nk_bool value) +{ + nk_selectable_image_text(ctx, img, str, nk_strlen(str), align, &value);return value; +} +NK_API nk_bool nk_select_image_text(struct nk_context *ctx, struct nk_image img, + const char *str, int len, nk_flags align, nk_bool value) +{ + nk_selectable_image_text(ctx, img, str, len, align, &value);return value; +} +NK_API nk_bool +nk_select_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, + const char *title, int title_len, nk_flags align, nk_bool value) +{ + nk_selectable_symbol_text(ctx, sym, title, title_len, align, &value);return value; +} +NK_API nk_bool +nk_select_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, + const char *title, nk_flags align, nk_bool value) +{ + return nk_select_symbol_text(ctx, sym, title, nk_strlen(title), align, value); +} + + + + + +/* =============================================================== + * + * SLIDER + * + * ===============================================================*/ +NK_LIB float +nk_slider_behavior(nk_flags *state, struct nk_rect *logical_cursor, + struct nk_rect *visual_cursor, struct nk_input *in, + struct nk_rect bounds, float slider_min, float slider_max, float slider_value, + float slider_step, float slider_steps) +{ + int left_mouse_down; + int left_mouse_click_in_cursor; + + /* check if visual cursor is being dragged */ + nk_widget_state_reset(state); + left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down; + left_mouse_click_in_cursor = in && nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, *visual_cursor, nk_true); + + if (left_mouse_down && left_mouse_click_in_cursor) { + float ratio = 0; + const float d = in->mouse.pos.x - (visual_cursor->x+visual_cursor->w*0.5f); + const float pxstep = bounds.w / slider_steps; + + /* only update value if the next slider step is reached */ + *state = NK_WIDGET_STATE_ACTIVE; + if (NK_ABS(d) >= pxstep) { + const float steps = (float)((int)(NK_ABS(d) / pxstep)); + slider_value += (d > 0) ? (slider_step*steps) : -(slider_step*steps); + slider_value = NK_CLAMP(slider_min, slider_value, slider_max); + ratio = (slider_value - slider_min)/slider_step; + logical_cursor->x = bounds.x + (logical_cursor->w * ratio); + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = logical_cursor->x; + } + } + + /* slider widget state */ + if (nk_input_is_mouse_hovering_rect(in, bounds)) + *state = NK_WIDGET_STATE_HOVERED; + if (*state & NK_WIDGET_STATE_HOVER && + !nk_input_is_mouse_prev_hovering_rect(in, bounds)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, bounds)) + *state |= NK_WIDGET_STATE_LEFT; + return slider_value; +} +NK_LIB void +nk_draw_slider(struct nk_command_buffer *out, nk_flags state, + const struct nk_style_slider *style, const struct nk_rect *bounds, + const struct nk_rect *visual_cursor, float min, float value, float max) +{ + struct nk_rect fill; + struct nk_rect bar; + const struct nk_style_item *background; + + /* select correct slider images/colors */ + struct nk_color bar_color; + const struct nk_style_item *cursor; + + NK_UNUSED(min); + NK_UNUSED(max); + NK_UNUSED(value); + + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + bar_color = style->bar_active; + cursor = &style->cursor_active; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + bar_color = style->bar_hover; + cursor = &style->cursor_hover; + } else { + background = &style->normal; + bar_color = style->bar_normal; + cursor = &style->cursor_normal; + } + /* calculate slider background bar */ + bar.x = bounds->x; + bar.y = (visual_cursor->y + visual_cursor->h/2) - bounds->h/12; + bar.w = bounds->w; + bar.h = bounds->h/6; + + /* filled background bar style */ + fill.w = (visual_cursor->x + (visual_cursor->w/2.0f)) - bar.x; + fill.x = bar.x; + fill.y = bar.y; + fill.h = bar.h; + + /* draw background */ + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + } else { + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); + } + + /* draw slider bar */ + nk_fill_rect(out, bar, style->rounding, bar_color); + nk_fill_rect(out, fill, style->rounding, style->bar_filled); + + /* draw cursor */ + if (cursor->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *visual_cursor, &cursor->data.image, nk_white); + else nk_fill_circle(out, *visual_cursor, cursor->data.color); +} +NK_LIB float +nk_do_slider(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + float min, float val, float max, float step, + const struct nk_style_slider *style, struct nk_input *in, + const struct nk_user_font *font) +{ + float slider_range; + float slider_min; + float slider_max; + float slider_value; + float slider_steps; + float cursor_offset; + + struct nk_rect visual_cursor; + struct nk_rect logical_cursor; + + NK_ASSERT(style); + NK_ASSERT(out); + if (!out || !style) + return 0; + + /* remove padding from slider bounds */ + bounds.x = bounds.x + style->padding.x; + bounds.y = bounds.y + style->padding.y; + bounds.h = NK_MAX(bounds.h, 2*style->padding.y); + bounds.w = NK_MAX(bounds.w, 2*style->padding.x + style->cursor_size.x); + bounds.w -= 2 * style->padding.x; + bounds.h -= 2 * style->padding.y; + + /* optional buttons */ + if (style->show_buttons) { + nk_flags ws; + struct nk_rect button; + button.y = bounds.y; + button.w = bounds.h; + button.h = bounds.h; + + /* decrement button */ + button.x = bounds.x; + if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, NK_BUTTON_DEFAULT, + &style->dec_button, in, font)) + val -= step; + + /* increment button */ + button.x = (bounds.x + bounds.w) - button.w; + if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, NK_BUTTON_DEFAULT, + &style->inc_button, in, font)) + val += step; + + bounds.x = bounds.x + button.w + style->spacing.x; + bounds.w = bounds.w - (2*button.w + 2*style->spacing.x); + } + + /* remove one cursor size to support visual cursor */ + bounds.x += style->cursor_size.x*0.5f; + bounds.w -= style->cursor_size.x; + + /* make sure the provided values are correct */ + slider_max = NK_MAX(min, max); + slider_min = NK_MIN(min, max); + slider_value = NK_CLAMP(slider_min, val, slider_max); + slider_range = slider_max - slider_min; + slider_steps = slider_range / step; + cursor_offset = (slider_value - slider_min) / step; + + /* calculate cursor + Basically you have two cursors. One for visual representation and interaction + and one for updating the actual cursor value. */ + logical_cursor.h = bounds.h; + logical_cursor.w = bounds.w / slider_steps; + logical_cursor.x = bounds.x + (logical_cursor.w * cursor_offset); + logical_cursor.y = bounds.y; + + visual_cursor.h = style->cursor_size.y; + visual_cursor.w = style->cursor_size.x; + visual_cursor.y = (bounds.y + bounds.h*0.5f) - visual_cursor.h*0.5f; + visual_cursor.x = logical_cursor.x - visual_cursor.w*0.5f; + + slider_value = nk_slider_behavior(state, &logical_cursor, &visual_cursor, + in, bounds, slider_min, slider_max, slider_value, step, slider_steps); + visual_cursor.x = logical_cursor.x - visual_cursor.w*0.5f; + + /* draw slider */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_slider(out, *state, style, &bounds, &visual_cursor, slider_min, slider_value, slider_max); + if (style->draw_end) style->draw_end(out, style->userdata); + return slider_value; +} +NK_API nk_bool +nk_slider_float(struct nk_context *ctx, float min_value, float *value, float max_value, + float value_step) +{ + struct nk_window *win; + struct nk_panel *layout; + struct nk_input *in; + const struct nk_style *style; + + int ret = 0; + float old_value; + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + NK_ASSERT(value); + if (!ctx || !ctx->current || !ctx->current->layout || !value) + return ret; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return ret; + in = (/*state == NK_WIDGET_ROM || */ layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + + old_value = *value; + *value = nk_do_slider(&ctx->last_widget_state, &win->buffer, bounds, min_value, + old_value, max_value, value_step, &style->slider, in, style->font); + return (old_value > *value || old_value < *value); +} +NK_API float +nk_slide_float(struct nk_context *ctx, float min, float val, float max, float step) +{ + nk_slider_float(ctx, min, &val, max, step); return val; +} +NK_API int +nk_slide_int(struct nk_context *ctx, int min, int val, int max, int step) +{ + float value = (float)val; + nk_slider_float(ctx, (float)min, &value, (float)max, (float)step); + return (int)value; +} +NK_API nk_bool +nk_slider_int(struct nk_context *ctx, int min, int *val, int max, int step) +{ + int ret; + float value = (float)*val; + ret = nk_slider_float(ctx, (float)min, &value, (float)max, (float)step); + *val = (int)value; + return ret; +} + + + + + +/* =============================================================== + * + * PROGRESS + * + * ===============================================================*/ +NK_LIB nk_size +nk_progress_behavior(nk_flags *state, struct nk_input *in, + struct nk_rect r, struct nk_rect cursor, nk_size max, nk_size value, nk_bool modifiable) +{ + int left_mouse_down = 0; + int left_mouse_click_in_cursor = 0; + + nk_widget_state_reset(state); + if (!in || !modifiable) return value; + left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down; + left_mouse_click_in_cursor = in && nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, cursor, nk_true); + if (nk_input_is_mouse_hovering_rect(in, r)) + *state = NK_WIDGET_STATE_HOVERED; + + if (in && left_mouse_down && left_mouse_click_in_cursor) { + if (left_mouse_down && left_mouse_click_in_cursor) { + float ratio = NK_MAX(0, (float)(in->mouse.pos.x - cursor.x)) / (float)cursor.w; + value = (nk_size)NK_CLAMP(0, (float)max * ratio, (float)max); + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = cursor.x + cursor.w/2.0f; + *state |= NK_WIDGET_STATE_ACTIVE; + } + } + /* set progressbar widget state */ + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, r)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, r)) + *state |= NK_WIDGET_STATE_LEFT; + return value; +} +NK_LIB void +nk_draw_progress(struct nk_command_buffer *out, nk_flags state, + const struct nk_style_progress *style, const struct nk_rect *bounds, + const struct nk_rect *scursor, nk_size value, nk_size max) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + + NK_UNUSED(max); + NK_UNUSED(value); + + /* select correct colors/images to draw */ + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + cursor = &style->cursor_active; + } else if (state & NK_WIDGET_STATE_HOVER){ + background = &style->hover; + cursor = &style->cursor_hover; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + } + + /* draw background */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); + } else nk_draw_image(out, *bounds, &background->data.image, nk_white); + + /* draw cursor */ + if (cursor->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *scursor, style->rounding, cursor->data.color); + nk_stroke_rect(out, *scursor, style->rounding, style->border, style->border_color); + } else nk_draw_image(out, *scursor, &cursor->data.image, nk_white); +} +NK_LIB nk_size +nk_do_progress(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + nk_size value, nk_size max, nk_bool modifiable, + const struct nk_style_progress *style, struct nk_input *in) +{ + float prog_scale; + nk_size prog_value; + struct nk_rect cursor; + + NK_ASSERT(style); + NK_ASSERT(out); + if (!out || !style) return 0; + + /* calculate progressbar cursor */ + cursor.w = NK_MAX(bounds.w, 2 * style->padding.x + 2 * style->border); + cursor.h = NK_MAX(bounds.h, 2 * style->padding.y + 2 * style->border); + cursor = nk_pad_rect(bounds, nk_vec2(style->padding.x + style->border, style->padding.y + style->border)); + prog_scale = (float)value / (float)max; + + /* update progressbar */ + prog_value = NK_MIN(value, max); + prog_value = nk_progress_behavior(state, in, bounds, cursor,max, prog_value, modifiable); + cursor.w = cursor.w * prog_scale; + + /* draw progressbar */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_progress(out, *state, style, &bounds, &cursor, value, max); + if (style->draw_end) style->draw_end(out, style->userdata); + return prog_value; +} +NK_API nk_bool +nk_progress(struct nk_context *ctx, nk_size *cur, nk_size max, nk_bool is_modifyable) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_style *style; + struct nk_input *in; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + nk_size old_value; + + NK_ASSERT(ctx); + NK_ASSERT(cur); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !cur) + return 0; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + state = nk_widget(&bounds, ctx); + if (!state) return 0; + + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + old_value = *cur; + *cur = nk_do_progress(&ctx->last_widget_state, &win->buffer, bounds, + *cur, max, is_modifyable, &style->progress, in); + return (*cur != old_value); +} +NK_API nk_size +nk_prog(struct nk_context *ctx, nk_size cur, nk_size max, nk_bool modifyable) +{ + nk_progress(ctx, &cur, max, modifyable); + return cur; +} + + + + + +/* =============================================================== + * + * SCROLLBAR + * + * ===============================================================*/ +NK_LIB float +nk_scrollbar_behavior(nk_flags *state, struct nk_input *in, + int has_scrolling, const struct nk_rect *scroll, + const struct nk_rect *cursor, const struct nk_rect *empty0, + const struct nk_rect *empty1, float scroll_offset, + float target, float scroll_step, enum nk_orientation o) +{ + nk_flags ws = 0; + int left_mouse_down; + int left_mouse_clicked; + int left_mouse_click_in_cursor; + float scroll_delta; + + nk_widget_state_reset(state); + if (!in) return scroll_offset; + + left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; + left_mouse_clicked = in->mouse.buttons[NK_BUTTON_LEFT].clicked; + left_mouse_click_in_cursor = nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, *cursor, nk_true); + if (nk_input_is_mouse_hovering_rect(in, *scroll)) + *state = NK_WIDGET_STATE_HOVERED; + + scroll_delta = (o == NK_VERTICAL) ? in->mouse.scroll_delta.y: in->mouse.scroll_delta.x; + if (left_mouse_down && left_mouse_click_in_cursor && !left_mouse_clicked) { + /* update cursor by mouse dragging */ + float pixel, delta; + *state = NK_WIDGET_STATE_ACTIVE; + if (o == NK_VERTICAL) { + float cursor_y; + pixel = in->mouse.delta.y; + delta = (pixel / scroll->h) * target; + scroll_offset = NK_CLAMP(0, scroll_offset + delta, target - scroll->h); + cursor_y = scroll->y + ((scroll_offset/target) * scroll->h); + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y = cursor_y + cursor->h/2.0f; + } else { + float cursor_x; + pixel = in->mouse.delta.x; + delta = (pixel / scroll->w) * target; + scroll_offset = NK_CLAMP(0, scroll_offset + delta, target - scroll->w); + cursor_x = scroll->x + ((scroll_offset/target) * scroll->w); + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = cursor_x + cursor->w/2.0f; + } + } else if ((nk_input_is_key_pressed(in, NK_KEY_SCROLL_UP) && o == NK_VERTICAL && has_scrolling)|| + nk_button_behavior(&ws, *empty0, in, NK_BUTTON_DEFAULT)) { + /* scroll page up by click on empty space or shortcut */ + if (o == NK_VERTICAL) + scroll_offset = NK_MAX(0, scroll_offset - scroll->h); + else scroll_offset = NK_MAX(0, scroll_offset - scroll->w); + } else if ((nk_input_is_key_pressed(in, NK_KEY_SCROLL_DOWN) && o == NK_VERTICAL && has_scrolling) || + nk_button_behavior(&ws, *empty1, in, NK_BUTTON_DEFAULT)) { + /* scroll page down by click on empty space or shortcut */ + if (o == NK_VERTICAL) + scroll_offset = NK_MIN(scroll_offset + scroll->h, target - scroll->h); + else scroll_offset = NK_MIN(scroll_offset + scroll->w, target - scroll->w); + } else if (has_scrolling) { + if ((scroll_delta < 0 || (scroll_delta > 0))) { + /* update cursor by mouse scrolling */ + scroll_offset = scroll_offset + scroll_step * (-scroll_delta); + if (o == NK_VERTICAL) + scroll_offset = NK_CLAMP(0, scroll_offset, target - scroll->h); + else scroll_offset = NK_CLAMP(0, scroll_offset, target - scroll->w); + } else if (nk_input_is_key_pressed(in, NK_KEY_SCROLL_START)) { + /* update cursor to the beginning */ + if (o == NK_VERTICAL) scroll_offset = 0; + } else if (nk_input_is_key_pressed(in, NK_KEY_SCROLL_END)) { + /* update cursor to the end */ + if (o == NK_VERTICAL) scroll_offset = target - scroll->h; + } + } + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, *scroll)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, *scroll)) + *state |= NK_WIDGET_STATE_LEFT; + return scroll_offset; +} +NK_LIB void +nk_draw_scrollbar(struct nk_command_buffer *out, nk_flags state, + const struct nk_style_scrollbar *style, const struct nk_rect *bounds, + const struct nk_rect *scroll) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + + /* select correct colors/images to draw */ + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + cursor = &style->cursor_active; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + cursor = &style->cursor_hover; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + } + + /* draw background */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); + } else { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + } + + /* draw cursor */ + if (cursor->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *scroll, style->rounding_cursor, cursor->data.color); + nk_stroke_rect(out, *scroll, style->rounding_cursor, style->border_cursor, style->cursor_border_color); + } else nk_draw_image(out, *scroll, &cursor->data.image, nk_white); +} +NK_LIB float +nk_do_scrollbarv(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, + float offset, float target, float step, float button_pixel_inc, + const struct nk_style_scrollbar *style, struct nk_input *in, + const struct nk_user_font *font) +{ + struct nk_rect empty_north; + struct nk_rect empty_south; + struct nk_rect cursor; + + float scroll_step; + float scroll_offset; + float scroll_off; + float scroll_ratio; + + NK_ASSERT(out); + NK_ASSERT(style); + NK_ASSERT(state); + if (!out || !style) return 0; + + scroll.w = NK_MAX(scroll.w, 1); + scroll.h = NK_MAX(scroll.h, 0); + if (target <= scroll.h) return 0; + + /* optional scrollbar buttons */ + if (style->show_buttons) { + nk_flags ws; + float scroll_h; + struct nk_rect button; + + button.x = scroll.x; + button.w = scroll.w; + button.h = scroll.w; + + scroll_h = NK_MAX(scroll.h - 2 * button.h,0); + scroll_step = NK_MIN(step, button_pixel_inc); + + /* decrement button */ + button.y = scroll.y; + if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, + NK_BUTTON_REPEATER, &style->dec_button, in, font)) + offset = offset - scroll_step; + + /* increment button */ + button.y = scroll.y + scroll.h - button.h; + if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, + NK_BUTTON_REPEATER, &style->inc_button, in, font)) + offset = offset + scroll_step; + + scroll.y = scroll.y + button.h; + scroll.h = scroll_h; + } + + /* calculate scrollbar constants */ + scroll_step = NK_MIN(step, scroll.h); + scroll_offset = NK_CLAMP(0, offset, target - scroll.h); + scroll_ratio = scroll.h / target; + scroll_off = scroll_offset / target; + + /* calculate scrollbar cursor bounds */ + cursor.h = NK_MAX((scroll_ratio * scroll.h) - (2*style->border + 2*style->padding.y), 0); + cursor.y = scroll.y + (scroll_off * scroll.h) + style->border + style->padding.y; + cursor.w = scroll.w - (2 * style->border + 2 * style->padding.x); + cursor.x = scroll.x + style->border + style->padding.x; + + /* calculate empty space around cursor */ + empty_north.x = scroll.x; + empty_north.y = scroll.y; + empty_north.w = scroll.w; + empty_north.h = NK_MAX(cursor.y - scroll.y, 0); + + empty_south.x = scroll.x; + empty_south.y = cursor.y + cursor.h; + empty_south.w = scroll.w; + empty_south.h = NK_MAX((scroll.y + scroll.h) - (cursor.y + cursor.h), 0); + + /* update scrollbar */ + scroll_offset = nk_scrollbar_behavior(state, in, has_scrolling, &scroll, &cursor, + &empty_north, &empty_south, scroll_offset, target, scroll_step, NK_VERTICAL); + scroll_off = scroll_offset / target; + cursor.y = scroll.y + (scroll_off * scroll.h) + style->border_cursor + style->padding.y; + + /* draw scrollbar */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_scrollbar(out, *state, style, &scroll, &cursor); + if (style->draw_end) style->draw_end(out, style->userdata); + return scroll_offset; +} +NK_LIB float +nk_do_scrollbarh(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, + float offset, float target, float step, float button_pixel_inc, + const struct nk_style_scrollbar *style, struct nk_input *in, + const struct nk_user_font *font) +{ + struct nk_rect cursor; + struct nk_rect empty_west; + struct nk_rect empty_east; + + float scroll_step; + float scroll_offset; + float scroll_off; + float scroll_ratio; + + NK_ASSERT(out); + NK_ASSERT(style); + if (!out || !style) return 0; + + /* scrollbar background */ + scroll.h = NK_MAX(scroll.h, 1); + scroll.w = NK_MAX(scroll.w, 2 * scroll.h); + if (target <= scroll.w) return 0; + + /* optional scrollbar buttons */ + if (style->show_buttons) { + nk_flags ws; + float scroll_w; + struct nk_rect button; + button.y = scroll.y; + button.w = scroll.h; + button.h = scroll.h; + + scroll_w = scroll.w - 2 * button.w; + scroll_step = NK_MIN(step, button_pixel_inc); + + /* decrement button */ + button.x = scroll.x; + if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, + NK_BUTTON_REPEATER, &style->dec_button, in, font)) + offset = offset - scroll_step; + + /* increment button */ + button.x = scroll.x + scroll.w - button.w; + if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, + NK_BUTTON_REPEATER, &style->inc_button, in, font)) + offset = offset + scroll_step; + + scroll.x = scroll.x + button.w; + scroll.w = scroll_w; + } + + /* calculate scrollbar constants */ + scroll_step = NK_MIN(step, scroll.w); + scroll_offset = NK_CLAMP(0, offset, target - scroll.w); + scroll_ratio = scroll.w / target; + scroll_off = scroll_offset / target; + + /* calculate cursor bounds */ + cursor.w = (scroll_ratio * scroll.w) - (2*style->border + 2*style->padding.x); + cursor.x = scroll.x + (scroll_off * scroll.w) + style->border + style->padding.x; + cursor.h = scroll.h - (2 * style->border + 2 * style->padding.y); + cursor.y = scroll.y + style->border + style->padding.y; + + /* calculate empty space around cursor */ + empty_west.x = scroll.x; + empty_west.y = scroll.y; + empty_west.w = cursor.x - scroll.x; + empty_west.h = scroll.h; + + empty_east.x = cursor.x + cursor.w; + empty_east.y = scroll.y; + empty_east.w = (scroll.x + scroll.w) - (cursor.x + cursor.w); + empty_east.h = scroll.h; + + /* update scrollbar */ + scroll_offset = nk_scrollbar_behavior(state, in, has_scrolling, &scroll, &cursor, + &empty_west, &empty_east, scroll_offset, target, scroll_step, NK_HORIZONTAL); + scroll_off = scroll_offset / target; + cursor.x = scroll.x + (scroll_off * scroll.w); + + /* draw scrollbar */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_scrollbar(out, *state, style, &scroll, &cursor); + if (style->draw_end) style->draw_end(out, style->userdata); + return scroll_offset; +} + + + + + +/* =============================================================== + * + * TEXT EDITOR + * + * ===============================================================*/ +/* stb_textedit.h - v1.8 - public domain - Sean Barrett */ +struct nk_text_find { + float x,y; /* position of n'th character */ + float height; /* height of line */ + int first_char, length; /* first char of row, and length */ + int prev_first; /*_ first char of previous row */ +}; + +struct nk_text_edit_row { + float x0,x1; + /* starting x location, end x location (allows for align=right, etc) */ + float baseline_y_delta; + /* position of baseline relative to previous row's baseline*/ + float ymin,ymax; + /* height of row above and below baseline */ + int num_chars; +}; + +/* forward declarations */ +NK_INTERN void nk_textedit_makeundo_delete(struct nk_text_edit*, int, int); +NK_INTERN void nk_textedit_makeundo_insert(struct nk_text_edit*, int, int); +NK_INTERN void nk_textedit_makeundo_replace(struct nk_text_edit*, int, int, int); +#define NK_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) + +NK_INTERN float +nk_textedit_get_width(const struct nk_text_edit *edit, int line_start, int char_id, + const struct nk_user_font *font) +{ + int len = 0; + nk_rune unicode = 0; + const char *str = nk_str_at_const(&edit->string, line_start + char_id, &unicode, &len); + return font->width(font->userdata, font->height, str, len); +} +NK_INTERN void +nk_textedit_layout_row(struct nk_text_edit_row *r, struct nk_text_edit *edit, + int line_start_id, float row_height, const struct nk_user_font *font) +{ + int l; + int glyphs = 0; + nk_rune unicode; + const char *remaining; + int len = nk_str_len_char(&edit->string); + const char *end = nk_str_get_const(&edit->string) + len; + const char *text = nk_str_at_const(&edit->string, line_start_id, &unicode, &l); + const struct nk_vec2 size = nk_text_calculate_text_bounds(font, + text, (int)(end - text), row_height, &remaining, 0, &glyphs, NK_STOP_ON_NEW_LINE); + + r->x0 = 0.0f; + r->x1 = size.x; + r->baseline_y_delta = size.y; + r->ymin = 0.0f; + r->ymax = size.y; + r->num_chars = glyphs; +} +NK_INTERN int +nk_textedit_locate_coord(struct nk_text_edit *edit, float x, float y, + const struct nk_user_font *font, float row_height) +{ + struct nk_text_edit_row r; + int n = edit->string.len; + float base_y = 0, prev_x; + int i=0, k; + + r.x0 = r.x1 = 0; + r.ymin = r.ymax = 0; + r.num_chars = 0; + + /* search rows to find one that straddles 'y' */ + while (i < n) { + nk_textedit_layout_row(&r, edit, i, row_height, font); + if (r.num_chars <= 0) + return n; + + if (i==0 && y < base_y + r.ymin) + return 0; + + if (y < base_y + r.ymax) + break; + + i += r.num_chars; + base_y += r.baseline_y_delta; + } + + /* below all text, return 'after' last character */ + if (i >= n) + return n; + + /* check if it's before the beginning of the line */ + if (x < r.x0) + return i; + + /* check if it's before the end of the line */ + if (x < r.x1) { + /* search characters in row for one that straddles 'x' */ + k = i; + prev_x = r.x0; + for (i=0; i < r.num_chars; ++i) { + float w = nk_textedit_get_width(edit, k, i, font); + if (x < prev_x+w) { + if (x < prev_x+w/2) + return k+i; + else return k+i+1; + } + prev_x += w; + } + /* shouldn't happen, but if it does, fall through to end-of-line case */ + } + + /* if the last character is a newline, return that. + * otherwise return 'after' the last character */ + if (nk_str_rune_at(&edit->string, i+r.num_chars-1) == '\n') + return i+r.num_chars-1; + else return i+r.num_chars; +} +NK_LIB void +nk_textedit_click(struct nk_text_edit *state, float x, float y, + const struct nk_user_font *font, float row_height) +{ + /* API click: on mouse down, move the cursor to the clicked location, + * and reset the selection */ + state->cursor = nk_textedit_locate_coord(state, x, y, font, row_height); + state->select_start = state->cursor; + state->select_end = state->cursor; + state->has_preferred_x = 0; +} +NK_LIB void +nk_textedit_drag(struct nk_text_edit *state, float x, float y, + const struct nk_user_font *font, float row_height) +{ + /* API drag: on mouse drag, move the cursor and selection endpoint + * to the clicked location */ + int p = nk_textedit_locate_coord(state, x, y, font, row_height); + if (state->select_start == state->select_end) + state->select_start = state->cursor; + state->cursor = state->select_end = p; +} +NK_INTERN void +nk_textedit_find_charpos(struct nk_text_find *find, struct nk_text_edit *state, + int n, int single_line, const struct nk_user_font *font, float row_height) +{ + /* find the x/y location of a character, and remember info about the previous + * row in case we get a move-up event (for page up, we'll have to rescan) */ + struct nk_text_edit_row r; + int prev_start = 0; + int z = state->string.len; + int i=0, first; + + nk_zero_struct(r); + if (n == z) { + /* if it's at the end, then find the last line -- simpler than trying to + explicitly handle this case in the regular code */ + nk_textedit_layout_row(&r, state, 0, row_height, font); + if (single_line) { + find->first_char = 0; + find->length = z; + } else { + while (i < z) { + prev_start = i; + i += r.num_chars; + nk_textedit_layout_row(&r, state, i, row_height, font); + } + + find->first_char = i; + find->length = r.num_chars; + } + find->x = r.x1; + find->y = r.ymin; + find->height = r.ymax - r.ymin; + find->prev_first = prev_start; + return; + } + + /* search rows to find the one that straddles character n */ + find->y = 0; + + for(;;) { + nk_textedit_layout_row(&r, state, i, row_height, font); + if (n < i + r.num_chars) break; + prev_start = i; + i += r.num_chars; + find->y += r.baseline_y_delta; + } + + find->first_char = first = i; + find->length = r.num_chars; + find->height = r.ymax - r.ymin; + find->prev_first = prev_start; + + /* now scan to find xpos */ + find->x = r.x0; + for (i=0; first+i < n; ++i) + find->x += nk_textedit_get_width(state, first, i, font); +} +NK_INTERN void +nk_textedit_clamp(struct nk_text_edit *state) +{ + /* make the selection/cursor state valid if client altered the string */ + int n = state->string.len; + if (NK_TEXT_HAS_SELECTION(state)) { + if (state->select_start > n) state->select_start = n; + if (state->select_end > n) state->select_end = n; + /* if clamping forced them to be equal, move the cursor to match */ + if (state->select_start == state->select_end) + state->cursor = state->select_start; + } + if (state->cursor > n) state->cursor = n; +} +NK_API void +nk_textedit_delete(struct nk_text_edit *state, int where, int len) +{ + /* delete characters while updating undo */ + nk_textedit_makeundo_delete(state, where, len); + nk_str_delete_runes(&state->string, where, len); + state->has_preferred_x = 0; +} +NK_API void +nk_textedit_delete_selection(struct nk_text_edit *state) +{ + /* delete the section */ + nk_textedit_clamp(state); + if (NK_TEXT_HAS_SELECTION(state)) { + if (state->select_start < state->select_end) { + nk_textedit_delete(state, state->select_start, + state->select_end - state->select_start); + state->select_end = state->cursor = state->select_start; + } else { + nk_textedit_delete(state, state->select_end, + state->select_start - state->select_end); + state->select_start = state->cursor = state->select_end; + } + state->has_preferred_x = 0; + } +} +NK_INTERN void +nk_textedit_sortselection(struct nk_text_edit *state) +{ + /* canonicalize the selection so start <= end */ + if (state->select_end < state->select_start) { + int temp = state->select_end; + state->select_end = state->select_start; + state->select_start = temp; + } +} +NK_INTERN void +nk_textedit_move_to_first(struct nk_text_edit *state) +{ + /* move cursor to first character of selection */ + if (NK_TEXT_HAS_SELECTION(state)) { + nk_textedit_sortselection(state); + state->cursor = state->select_start; + state->select_end = state->select_start; + state->has_preferred_x = 0; + } +} +NK_INTERN void +nk_textedit_move_to_last(struct nk_text_edit *state) +{ + /* move cursor to last character of selection */ + if (NK_TEXT_HAS_SELECTION(state)) { + nk_textedit_sortselection(state); + nk_textedit_clamp(state); + state->cursor = state->select_end; + state->select_start = state->select_end; + state->has_preferred_x = 0; + } +} +NK_INTERN int +nk_is_word_boundary( struct nk_text_edit *state, int idx) +{ + int len; + nk_rune c; + if (idx <= 0) return 1; + if (!nk_str_at_rune(&state->string, idx, &c, &len)) return 1; + return (c == ' ' || c == '\t' ||c == 0x3000 || c == ',' || c == ';' || + c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']' || + c == '|'); +} +NK_INTERN int +nk_textedit_move_to_word_previous(struct nk_text_edit *state) +{ + int c = state->cursor - 1; + while( c >= 0 && !nk_is_word_boundary(state, c)) + --c; + + if( c < 0 ) + c = 0; + + return c; +} +NK_INTERN int +nk_textedit_move_to_word_next(struct nk_text_edit *state) +{ + const int len = state->string.len; + int c = state->cursor+1; + while( c < len && !nk_is_word_boundary(state, c)) + ++c; + + if( c > len ) + c = len; + + return c; +} +NK_INTERN void +nk_textedit_prep_selection_at_cursor(struct nk_text_edit *state) +{ + /* update selection and cursor to match each other */ + if (!NK_TEXT_HAS_SELECTION(state)) + state->select_start = state->select_end = state->cursor; + else state->cursor = state->select_end; +} +NK_API nk_bool +nk_textedit_cut(struct nk_text_edit *state) +{ + /* API cut: delete selection */ + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + return 0; + if (NK_TEXT_HAS_SELECTION(state)) { + nk_textedit_delete_selection(state); /* implicitly clamps */ + state->has_preferred_x = 0; + return 1; + } + return 0; +} +NK_API nk_bool +nk_textedit_paste(struct nk_text_edit *state, char const *ctext, int len) +{ + /* API paste: replace existing selection with passed-in text */ + int glyphs; + const char *text = (const char *) ctext; + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) return 0; + + /* if there's a selection, the paste should delete it */ + nk_textedit_clamp(state); + nk_textedit_delete_selection(state); + + /* try to insert the characters */ + glyphs = nk_utf_len(ctext, len); + if (nk_str_insert_text_char(&state->string, state->cursor, text, len)) { + nk_textedit_makeundo_insert(state, state->cursor, glyphs); + state->cursor += len; + state->has_preferred_x = 0; + return 1; + } + /* remove the undo since we didn't actually insert the characters */ + if (state->undo.undo_point) + --state->undo.undo_point; + return 0; +} +NK_API void +nk_textedit_text(struct nk_text_edit *state, const char *text, int total_len) +{ + nk_rune unicode; + int glyph_len; + int text_len = 0; + + NK_ASSERT(state); + NK_ASSERT(text); + if (!text || !total_len || state->mode == NK_TEXT_EDIT_MODE_VIEW) return; + + glyph_len = nk_utf_decode(text, &unicode, total_len); + while ((text_len < total_len) && glyph_len) + { + /* don't insert a backward delete, just process the event */ + if (unicode == 127) goto next; + /* can't add newline in single-line mode */ + if (unicode == '\n' && state->single_line) goto next; + /* filter incoming text */ + if (state->filter && !state->filter(state, unicode)) goto next; + + if (!NK_TEXT_HAS_SELECTION(state) && + state->cursor < state->string.len) + { + if (state->mode == NK_TEXT_EDIT_MODE_REPLACE) { + nk_textedit_makeundo_replace(state, state->cursor, 1, 1); + nk_str_delete_runes(&state->string, state->cursor, 1); + } + if (nk_str_insert_text_utf8(&state->string, state->cursor, + text+text_len, 1)) + { + ++state->cursor; + state->has_preferred_x = 0; + } + } else { + nk_textedit_delete_selection(state); /* implicitly clamps */ + if (nk_str_insert_text_utf8(&state->string, state->cursor, + text+text_len, 1)) + { + nk_textedit_makeundo_insert(state, state->cursor, 1); + ++state->cursor; + state->has_preferred_x = 0; + } + } + next: + text_len += glyph_len; + glyph_len = nk_utf_decode(text + text_len, &unicode, total_len-text_len); + } +} +NK_LIB void +nk_textedit_key(struct nk_text_edit *state, enum nk_keys key, int shift_mod, + const struct nk_user_font *font, float row_height) +{ +retry: + switch (key) + { + case NK_KEY_NONE: + case NK_KEY_CTRL: + case NK_KEY_ENTER: + case NK_KEY_SHIFT: + case NK_KEY_TAB: + case NK_KEY_COPY: + case NK_KEY_CUT: + case NK_KEY_PASTE: + case NK_KEY_MAX: + default: break; + case NK_KEY_TEXT_UNDO: + nk_textedit_undo(state); + state->has_preferred_x = 0; + break; + + case NK_KEY_TEXT_REDO: + nk_textedit_redo(state); + state->has_preferred_x = 0; + break; + + case NK_KEY_TEXT_SELECT_ALL: + nk_textedit_select_all(state); + state->has_preferred_x = 0; + break; + + case NK_KEY_TEXT_INSERT_MODE: + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + state->mode = NK_TEXT_EDIT_MODE_INSERT; + break; + case NK_KEY_TEXT_REPLACE_MODE: + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + state->mode = NK_TEXT_EDIT_MODE_REPLACE; + break; + case NK_KEY_TEXT_RESET_MODE: + if (state->mode == NK_TEXT_EDIT_MODE_INSERT || + state->mode == NK_TEXT_EDIT_MODE_REPLACE) + state->mode = NK_TEXT_EDIT_MODE_VIEW; + break; + + case NK_KEY_LEFT: + if (shift_mod) { + nk_textedit_clamp(state); + nk_textedit_prep_selection_at_cursor(state); + /* move selection left */ + if (state->select_end > 0) + --state->select_end; + state->cursor = state->select_end; + state->has_preferred_x = 0; + } else { + /* if currently there's a selection, + * move cursor to start of selection */ + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_first(state); + else if (state->cursor > 0) + --state->cursor; + state->has_preferred_x = 0; + } break; + + case NK_KEY_RIGHT: + if (shift_mod) { + nk_textedit_prep_selection_at_cursor(state); + /* move selection right */ + ++state->select_end; + nk_textedit_clamp(state); + state->cursor = state->select_end; + state->has_preferred_x = 0; + } else { + /* if currently there's a selection, + * move cursor to end of selection */ + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_last(state); + else ++state->cursor; + nk_textedit_clamp(state); + state->has_preferred_x = 0; + } break; + + case NK_KEY_TEXT_WORD_LEFT: + if (shift_mod) { + if( !NK_TEXT_HAS_SELECTION( state ) ) + nk_textedit_prep_selection_at_cursor(state); + state->cursor = nk_textedit_move_to_word_previous(state); + state->select_end = state->cursor; + nk_textedit_clamp(state ); + } else { + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_first(state); + else { + state->cursor = nk_textedit_move_to_word_previous(state); + nk_textedit_clamp(state ); + } + } break; + + case NK_KEY_TEXT_WORD_RIGHT: + if (shift_mod) { + if( !NK_TEXT_HAS_SELECTION( state ) ) + nk_textedit_prep_selection_at_cursor(state); + state->cursor = nk_textedit_move_to_word_next(state); + state->select_end = state->cursor; + nk_textedit_clamp(state); + } else { + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_last(state); + else { + state->cursor = nk_textedit_move_to_word_next(state); + nk_textedit_clamp(state ); + } + } break; + + case NK_KEY_DOWN: { + struct nk_text_find find; + struct nk_text_edit_row row; + int i, sel = shift_mod; + + if (state->single_line) { + /* on windows, up&down in single-line behave like left&right */ + key = NK_KEY_RIGHT; + goto retry; + } + + if (sel) + nk_textedit_prep_selection_at_cursor(state); + else if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_last(state); + + /* compute current position of cursor point */ + nk_textedit_clamp(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + + /* now find character position down a row */ + if (find.length) + { + float x; + float goal_x = state->has_preferred_x ? state->preferred_x : find.x; + int start = find.first_char + find.length; + + state->cursor = start; + nk_textedit_layout_row(&row, state, state->cursor, row_height, font); + x = row.x0; + + for (i=0; i < row.num_chars && x < row.x1; ++i) { + float dx = nk_textedit_get_width(state, start, i, font); + x += dx; + if (x > goal_x) + break; + ++state->cursor; + } + nk_textedit_clamp(state); + + state->has_preferred_x = 1; + state->preferred_x = goal_x; + if (sel) + state->select_end = state->cursor; + } + } break; + + case NK_KEY_UP: { + struct nk_text_find find; + struct nk_text_edit_row row; + int i, sel = shift_mod; + + if (state->single_line) { + /* on windows, up&down become left&right */ + key = NK_KEY_LEFT; + goto retry; + } + + if (sel) + nk_textedit_prep_selection_at_cursor(state); + else if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_first(state); + + /* compute current position of cursor point */ + nk_textedit_clamp(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + + /* can only go up if there's a previous row */ + if (find.prev_first != find.first_char) { + /* now find character position up a row */ + float x; + float goal_x = state->has_preferred_x ? state->preferred_x : find.x; + + state->cursor = find.prev_first; + nk_textedit_layout_row(&row, state, state->cursor, row_height, font); + x = row.x0; + + for (i=0; i < row.num_chars && x < row.x1; ++i) { + float dx = nk_textedit_get_width(state, find.prev_first, i, font); + x += dx; + if (x > goal_x) + break; + ++state->cursor; + } + nk_textedit_clamp(state); + + state->has_preferred_x = 1; + state->preferred_x = goal_x; + if (sel) state->select_end = state->cursor; + } + } break; + + case NK_KEY_DEL: + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + break; + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_delete_selection(state); + else { + int n = state->string.len; + if (state->cursor < n) + nk_textedit_delete(state, state->cursor, 1); + } + state->has_preferred_x = 0; + break; + + case NK_KEY_BACKSPACE: + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + break; + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_delete_selection(state); + else { + nk_textedit_clamp(state); + if (state->cursor > 0) { + nk_textedit_delete(state, state->cursor-1, 1); + --state->cursor; + } + } + state->has_preferred_x = 0; + break; + + case NK_KEY_TEXT_START: + if (shift_mod) { + nk_textedit_prep_selection_at_cursor(state); + state->cursor = state->select_end = 0; + state->has_preferred_x = 0; + } else { + state->cursor = state->select_start = state->select_end = 0; + state->has_preferred_x = 0; + } + break; + + case NK_KEY_TEXT_END: + if (shift_mod) { + nk_textedit_prep_selection_at_cursor(state); + state->cursor = state->select_end = state->string.len; + state->has_preferred_x = 0; + } else { + state->cursor = state->string.len; + state->select_start = state->select_end = 0; + state->has_preferred_x = 0; + } + break; + + case NK_KEY_TEXT_LINE_START: { + if (shift_mod) { + struct nk_text_find find; + nk_textedit_clamp(state); + nk_textedit_prep_selection_at_cursor(state); + if (state->string.len && state->cursor == state->string.len) + --state->cursor; + nk_textedit_find_charpos(&find, state,state->cursor, state->single_line, + font, row_height); + state->cursor = state->select_end = find.first_char; + state->has_preferred_x = 0; + } else { + struct nk_text_find find; + if (state->string.len && state->cursor == state->string.len) + --state->cursor; + nk_textedit_clamp(state); + nk_textedit_move_to_first(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + state->cursor = find.first_char; + state->has_preferred_x = 0; + } + } break; + + case NK_KEY_TEXT_LINE_END: { + if (shift_mod) { + struct nk_text_find find; + nk_textedit_clamp(state); + nk_textedit_prep_selection_at_cursor(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + state->has_preferred_x = 0; + state->cursor = find.first_char + find.length; + if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) == '\n') + --state->cursor; + state->select_end = state->cursor; + } else { + struct nk_text_find find; + nk_textedit_clamp(state); + nk_textedit_move_to_first(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + + state->has_preferred_x = 0; + state->cursor = find.first_char + find.length; + if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) == '\n') + --state->cursor; + }} break; + } +} +NK_INTERN void +nk_textedit_flush_redo(struct nk_text_undo_state *state) +{ + state->redo_point = NK_TEXTEDIT_UNDOSTATECOUNT; + state->redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT; +} +NK_INTERN void +nk_textedit_discard_undo(struct nk_text_undo_state *state) +{ + /* discard the oldest entry in the undo list */ + if (state->undo_point > 0) { + /* if the 0th undo state has characters, clean those up */ + if (state->undo_rec[0].char_storage >= 0) { + int n = state->undo_rec[0].insert_length, i; + /* delete n characters from all other records */ + state->undo_char_point = (short)(state->undo_char_point - n); + NK_MEMCPY(state->undo_char, state->undo_char + n, + (nk_size)state->undo_char_point*sizeof(nk_rune)); + for (i=0; i < state->undo_point; ++i) { + if (state->undo_rec[i].char_storage >= 0) + state->undo_rec[i].char_storage = (short) + (state->undo_rec[i].char_storage - n); + } + } + --state->undo_point; + NK_MEMCPY(state->undo_rec, state->undo_rec+1, + (nk_size)((nk_size)state->undo_point * sizeof(state->undo_rec[0]))); + } +} +NK_INTERN void +nk_textedit_discard_redo(struct nk_text_undo_state *state) +{ +/* discard the oldest entry in the redo list--it's bad if this + ever happens, but because undo & redo have to store the actual + characters in different cases, the redo character buffer can + fill up even though the undo buffer didn't */ + nk_size num; + int k = NK_TEXTEDIT_UNDOSTATECOUNT-1; + if (state->redo_point <= k) { + /* if the k'th undo state has characters, clean those up */ + if (state->undo_rec[k].char_storage >= 0) { + int n = state->undo_rec[k].insert_length, i; + /* delete n characters from all other records */ + state->redo_char_point = (short)(state->redo_char_point + n); + num = (nk_size)(NK_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point); + NK_MEMCPY(state->undo_char + state->redo_char_point, + state->undo_char + state->redo_char_point-n, num * sizeof(char)); + for (i = state->redo_point; i < k; ++i) { + if (state->undo_rec[i].char_storage >= 0) { + state->undo_rec[i].char_storage = (short) + (state->undo_rec[i].char_storage + n); + } + } + } + ++state->redo_point; + num = (nk_size)(NK_TEXTEDIT_UNDOSTATECOUNT - state->redo_point); + if (num) NK_MEMCPY(state->undo_rec + state->redo_point-1, + state->undo_rec + state->redo_point, num * sizeof(state->undo_rec[0])); + } +} +NK_INTERN struct nk_text_undo_record* +nk_textedit_create_undo_record(struct nk_text_undo_state *state, int numchars) +{ + /* any time we create a new undo record, we discard redo*/ + nk_textedit_flush_redo(state); + + /* if we have no free records, we have to make room, + * by sliding the existing records down */ + if (state->undo_point == NK_TEXTEDIT_UNDOSTATECOUNT) + nk_textedit_discard_undo(state); + + /* if the characters to store won't possibly fit in the buffer, + * we can't undo */ + if (numchars > NK_TEXTEDIT_UNDOCHARCOUNT) { + state->undo_point = 0; + state->undo_char_point = 0; + return 0; + } + + /* if we don't have enough free characters in the buffer, + * we have to make room */ + while (state->undo_char_point + numchars > NK_TEXTEDIT_UNDOCHARCOUNT) + nk_textedit_discard_undo(state); + return &state->undo_rec[state->undo_point++]; +} +NK_INTERN nk_rune* +nk_textedit_createundo(struct nk_text_undo_state *state, int pos, + int insert_len, int delete_len) +{ + struct nk_text_undo_record *r = nk_textedit_create_undo_record(state, insert_len); + if (r == 0) + return 0; + + r->where = pos; + r->insert_length = (short) insert_len; + r->delete_length = (short) delete_len; + + if (insert_len == 0) { + r->char_storage = -1; + return 0; + } else { + r->char_storage = state->undo_char_point; + state->undo_char_point = (short)(state->undo_char_point + insert_len); + return &state->undo_char[r->char_storage]; + } +} +NK_API void +nk_textedit_undo(struct nk_text_edit *state) +{ + struct nk_text_undo_state *s = &state->undo; + struct nk_text_undo_record u, *r; + if (s->undo_point == 0) + return; + + /* we need to do two things: apply the undo record, and create a redo record */ + u = s->undo_rec[s->undo_point-1]; + r = &s->undo_rec[s->redo_point-1]; + r->char_storage = -1; + + r->insert_length = u.delete_length; + r->delete_length = u.insert_length; + r->where = u.where; + + if (u.delete_length) + { + /* if the undo record says to delete characters, then the redo record will + need to re-insert the characters that get deleted, so we need to store + them. + there are three cases: + - there's enough room to store the characters + - characters stored for *redoing* don't leave room for redo + - characters stored for *undoing* don't leave room for redo + if the last is true, we have to bail */ + if (s->undo_char_point + u.delete_length >= NK_TEXTEDIT_UNDOCHARCOUNT) { + /* the undo records take up too much character space; there's no space + * to store the redo characters */ + r->insert_length = 0; + } else { + int i; + /* there's definitely room to store the characters eventually */ + while (s->undo_char_point + u.delete_length > s->redo_char_point) { + /* there's currently not enough room, so discard a redo record */ + nk_textedit_discard_redo(s); + /* should never happen: */ + if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT) + return; + } + + r = &s->undo_rec[s->redo_point-1]; + r->char_storage = (short)(s->redo_char_point - u.delete_length); + s->redo_char_point = (short)(s->redo_char_point - u.delete_length); + + /* now save the characters */ + for (i=0; i < u.delete_length; ++i) + s->undo_char[r->char_storage + i] = + nk_str_rune_at(&state->string, u.where + i); + } + /* now we can carry out the deletion */ + nk_str_delete_runes(&state->string, u.where, u.delete_length); + } + + /* check type of recorded action: */ + if (u.insert_length) { + /* easy case: was a deletion, so we need to insert n characters */ + nk_str_insert_text_runes(&state->string, u.where, + &s->undo_char[u.char_storage], u.insert_length); + s->undo_char_point = (short)(s->undo_char_point - u.insert_length); + } + state->cursor = (short)(u.where + u.insert_length); + + s->undo_point--; + s->redo_point--; +} +NK_API void +nk_textedit_redo(struct nk_text_edit *state) +{ + struct nk_text_undo_state *s = &state->undo; + struct nk_text_undo_record *u, r; + if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT) + return; + + /* we need to do two things: apply the redo record, and create an undo record */ + u = &s->undo_rec[s->undo_point]; + r = s->undo_rec[s->redo_point]; + + /* we KNOW there must be room for the undo record, because the redo record + was derived from an undo record */ + u->delete_length = r.insert_length; + u->insert_length = r.delete_length; + u->where = r.where; + u->char_storage = -1; + + if (r.delete_length) { + /* the redo record requires us to delete characters, so the undo record + needs to store the characters */ + if (s->undo_char_point + u->insert_length > s->redo_char_point) { + u->insert_length = 0; + u->delete_length = 0; + } else { + int i; + u->char_storage = s->undo_char_point; + s->undo_char_point = (short)(s->undo_char_point + u->insert_length); + + /* now save the characters */ + for (i=0; i < u->insert_length; ++i) { + s->undo_char[u->char_storage + i] = + nk_str_rune_at(&state->string, u->where + i); + } + } + nk_str_delete_runes(&state->string, r.where, r.delete_length); + } + + if (r.insert_length) { + /* easy case: need to insert n characters */ + nk_str_insert_text_runes(&state->string, r.where, + &s->undo_char[r.char_storage], r.insert_length); + } + state->cursor = r.where + r.insert_length; + + s->undo_point++; + s->redo_point++; +} +NK_INTERN void +nk_textedit_makeundo_insert(struct nk_text_edit *state, int where, int length) +{ + nk_textedit_createundo(&state->undo, where, 0, length); +} +NK_INTERN void +nk_textedit_makeundo_delete(struct nk_text_edit *state, int where, int length) +{ + int i; + nk_rune *p = nk_textedit_createundo(&state->undo, where, length, 0); + if (p) { + for (i=0; i < length; ++i) + p[i] = nk_str_rune_at(&state->string, where+i); + } +} +NK_INTERN void +nk_textedit_makeundo_replace(struct nk_text_edit *state, int where, + int old_length, int new_length) +{ + int i; + nk_rune *p = nk_textedit_createundo(&state->undo, where, old_length, new_length); + if (p) { + for (i=0; i < old_length; ++i) + p[i] = nk_str_rune_at(&state->string, where+i); + } +} +NK_LIB void +nk_textedit_clear_state(struct nk_text_edit *state, enum nk_text_edit_type type, + nk_plugin_filter filter) +{ + /* reset the state to default */ + state->undo.undo_point = 0; + state->undo.undo_char_point = 0; + state->undo.redo_point = NK_TEXTEDIT_UNDOSTATECOUNT; + state->undo.redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT; + state->select_end = state->select_start = 0; + state->cursor = 0; + state->has_preferred_x = 0; + state->preferred_x = 0; + state->cursor_at_end_of_line = 0; + state->initialized = 1; + state->single_line = (unsigned char)(type == NK_TEXT_EDIT_SINGLE_LINE); + state->mode = NK_TEXT_EDIT_MODE_VIEW; + state->filter = filter; + state->scrollbar = nk_vec2(0,0); +} +NK_API void +nk_textedit_init_fixed(struct nk_text_edit *state, void *memory, nk_size size) +{ + NK_ASSERT(state); + NK_ASSERT(memory); + if (!state || !memory || !size) return; + NK_MEMSET(state, 0, sizeof(struct nk_text_edit)); + nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); + nk_str_init_fixed(&state->string, memory, size); +} +NK_API void +nk_textedit_init(struct nk_text_edit *state, struct nk_allocator *alloc, nk_size size) +{ + NK_ASSERT(state); + NK_ASSERT(alloc); + if (!state || !alloc) return; + NK_MEMSET(state, 0, sizeof(struct nk_text_edit)); + nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); + nk_str_init(&state->string, alloc, size); +} +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void +nk_textedit_init_default(struct nk_text_edit *state) +{ + NK_ASSERT(state); + if (!state) return; + NK_MEMSET(state, 0, sizeof(struct nk_text_edit)); + nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); + nk_str_init_default(&state->string); +} +#endif +NK_API void +nk_textedit_select_all(struct nk_text_edit *state) +{ + NK_ASSERT(state); + state->select_start = 0; + state->select_end = state->string.len; +} +NK_API void +nk_textedit_free(struct nk_text_edit *state) +{ + NK_ASSERT(state); + if (!state) return; + nk_str_free(&state->string); +} + + + + + +/* =============================================================== + * + * FILTER + * + * ===============================================================*/ +NK_API nk_bool +nk_filter_default(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(unicode); + NK_UNUSED(box); + return nk_true; +} +NK_API nk_bool +nk_filter_ascii(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if (unicode > 128) return nk_false; + else return nk_true; +} +NK_API nk_bool +nk_filter_float(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if ((unicode < '0' || unicode > '9') && unicode != '.' && unicode != '-') + return nk_false; + else return nk_true; +} +NK_API nk_bool +nk_filter_decimal(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if ((unicode < '0' || unicode > '9') && unicode != '-') + return nk_false; + else return nk_true; +} +NK_API nk_bool +nk_filter_hex(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if ((unicode < '0' || unicode > '9') && + (unicode < 'a' || unicode > 'f') && + (unicode < 'A' || unicode > 'F')) + return nk_false; + else return nk_true; +} +NK_API nk_bool +nk_filter_oct(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if (unicode < '0' || unicode > '7') + return nk_false; + else return nk_true; +} +NK_API nk_bool +nk_filter_binary(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if (unicode != '0' && unicode != '1') + return nk_false; + else return nk_true; +} + +/* =============================================================== + * + * EDIT + * + * ===============================================================*/ +NK_LIB void +nk_edit_draw_text(struct nk_command_buffer *out, + const struct nk_style_edit *style, float pos_x, float pos_y, + float x_offset, const char *text, int byte_len, float row_height, + const struct nk_user_font *font, struct nk_color background, + struct nk_color foreground, nk_bool is_selected) +{ + NK_ASSERT(out); + NK_ASSERT(font); + NK_ASSERT(style); + if (!text || !byte_len || !out || !style) return; + + {int glyph_len = 0; + nk_rune unicode = 0; + int text_len = 0; + float line_width = 0; + float glyph_width; + const char *line = text; + float line_offset = 0; + int line_count = 0; + + struct nk_text txt; + txt.padding = nk_vec2(0,0); + txt.background = background; + txt.text = foreground; + + glyph_len = nk_utf_decode(text+text_len, &unicode, byte_len-text_len); + if (!glyph_len) return; + while ((text_len < byte_len) && glyph_len) + { + if (unicode == '\n') { + /* new line separator so draw previous line */ + struct nk_rect label; + label.y = pos_y + line_offset; + label.h = row_height; + label.w = line_width; + label.x = pos_x; + if (!line_count) + label.x += x_offset; + + if (is_selected) /* selection needs to draw different background color */ + nk_fill_rect(out, label, 0, background); + nk_widget_text(out, label, line, (int)((text + text_len) - line), + &txt, NK_TEXT_CENTERED, font); + + text_len++; + line_count++; + line_width = 0; + line = text + text_len; + line_offset += row_height; + glyph_len = nk_utf_decode(text + text_len, &unicode, (int)(byte_len-text_len)); + continue; + } + if (unicode == '\r') { + text_len++; + glyph_len = nk_utf_decode(text + text_len, &unicode, byte_len-text_len); + continue; + } + glyph_width = font->width(font->userdata, font->height, text+text_len, glyph_len); + line_width += (float)glyph_width; + text_len += glyph_len; + glyph_len = nk_utf_decode(text + text_len, &unicode, byte_len-text_len); + continue; + } + if (line_width > 0) { + /* draw last line */ + struct nk_rect label; + label.y = pos_y + line_offset; + label.h = row_height; + label.w = line_width; + label.x = pos_x; + if (!line_count) + label.x += x_offset; + + if (is_selected) + nk_fill_rect(out, label, 0, background); + nk_widget_text(out, label, line, (int)((text + text_len) - line), + &txt, NK_TEXT_LEFT, font); + }} +} +NK_LIB nk_flags +nk_do_edit(nk_flags *state, struct nk_command_buffer *out, + struct nk_rect bounds, nk_flags flags, nk_plugin_filter filter, + struct nk_text_edit *edit, const struct nk_style_edit *style, + struct nk_input *in, const struct nk_user_font *font) +{ + struct nk_rect area; + nk_flags ret = 0; + float row_height; + char prev_state = 0; + char is_hovered = 0; + char select_all = 0; + char cursor_follow = 0; + struct nk_rect old_clip; + struct nk_rect clip; + + NK_ASSERT(state); + NK_ASSERT(out); + NK_ASSERT(style); + if (!state || !out || !style) + return ret; + + /* visible text area calculation */ + area.x = bounds.x + style->padding.x + style->border; + area.y = bounds.y + style->padding.y + style->border; + area.w = bounds.w - (2.0f * style->padding.x + 2 * style->border); + area.h = bounds.h - (2.0f * style->padding.y + 2 * style->border); + if (flags & NK_EDIT_MULTILINE) + area.w = NK_MAX(0, area.w - style->scrollbar_size.x); + row_height = (flags & NK_EDIT_MULTILINE)? font->height + style->row_padding: area.h; + + /* calculate clipping rectangle */ + old_clip = out->clip; + nk_unify(&clip, &old_clip, area.x, area.y, area.x + area.w, area.y + area.h); + + /* update edit state */ + prev_state = (char)edit->active; + is_hovered = (char)nk_input_is_mouse_hovering_rect(in, bounds); + if (in && in->mouse.buttons[NK_BUTTON_LEFT].clicked && in->mouse.buttons[NK_BUTTON_LEFT].down) { + edit->active = NK_INBOX(in->mouse.pos.x, in->mouse.pos.y, + bounds.x, bounds.y, bounds.w, bounds.h); + } + + /* (de)activate text editor */ + if (!prev_state && edit->active) { + const enum nk_text_edit_type type = (flags & NK_EDIT_MULTILINE) ? + NK_TEXT_EDIT_MULTI_LINE: NK_TEXT_EDIT_SINGLE_LINE; + /* keep scroll position when re-activating edit widget */ + struct nk_vec2 oldscrollbar = edit->scrollbar; + nk_textedit_clear_state(edit, type, filter); + edit->scrollbar = oldscrollbar; + if (flags & NK_EDIT_AUTO_SELECT) + select_all = nk_true; + if (flags & NK_EDIT_GOTO_END_ON_ACTIVATE) { + edit->cursor = edit->string.len; + in = 0; + } + } else if (!edit->active) edit->mode = NK_TEXT_EDIT_MODE_VIEW; + if (flags & NK_EDIT_READ_ONLY) + edit->mode = NK_TEXT_EDIT_MODE_VIEW; + else if (flags & NK_EDIT_ALWAYS_INSERT_MODE) + edit->mode = NK_TEXT_EDIT_MODE_INSERT; + + ret = (edit->active) ? NK_EDIT_ACTIVE: NK_EDIT_INACTIVE; + if (prev_state != edit->active) + ret |= (edit->active) ? NK_EDIT_ACTIVATED: NK_EDIT_DEACTIVATED; + + /* handle user input */ + if (edit->active && in) + { + int shift_mod = in->keyboard.keys[NK_KEY_SHIFT].down; + const float mouse_x = (in->mouse.pos.x - area.x) + edit->scrollbar.x; + const float mouse_y = (in->mouse.pos.y - area.y) + edit->scrollbar.y; + + /* mouse click handler */ + is_hovered = (char)nk_input_is_mouse_hovering_rect(in, area); + if (select_all) { + nk_textedit_select_all(edit); + } else if (is_hovered && in->mouse.buttons[NK_BUTTON_LEFT].down && + in->mouse.buttons[NK_BUTTON_LEFT].clicked) { + nk_textedit_click(edit, mouse_x, mouse_y, font, row_height); + } else if (is_hovered && in->mouse.buttons[NK_BUTTON_LEFT].down && + (in->mouse.delta.x != 0.0f || in->mouse.delta.y != 0.0f)) { + nk_textedit_drag(edit, mouse_x, mouse_y, font, row_height); + cursor_follow = nk_true; + } else if (is_hovered && in->mouse.buttons[NK_BUTTON_RIGHT].clicked && + in->mouse.buttons[NK_BUTTON_RIGHT].down) { + nk_textedit_key(edit, NK_KEY_TEXT_WORD_LEFT, nk_false, font, row_height); + nk_textedit_key(edit, NK_KEY_TEXT_WORD_RIGHT, nk_true, font, row_height); + cursor_follow = nk_true; + } + + {int i; /* keyboard input */ + int old_mode = edit->mode; + for (i = 0; i < NK_KEY_MAX; ++i) { + if (i == NK_KEY_ENTER || i == NK_KEY_TAB) continue; /* special case */ + if (nk_input_is_key_pressed(in, (enum nk_keys)i)) { + nk_textedit_key(edit, (enum nk_keys)i, shift_mod, font, row_height); + cursor_follow = nk_true; + } + } + if (old_mode != edit->mode) { + in->keyboard.text_len = 0; + }} + + /* text input */ + edit->filter = filter; + if (in->keyboard.text_len) { + nk_textedit_text(edit, in->keyboard.text, in->keyboard.text_len); + cursor_follow = nk_true; + in->keyboard.text_len = 0; + } + + /* enter key handler */ + if (nk_input_is_key_pressed(in, NK_KEY_ENTER)) { + cursor_follow = nk_true; + if (flags & NK_EDIT_CTRL_ENTER_NEWLINE && shift_mod) + nk_textedit_text(edit, "\n", 1); + else if (flags & NK_EDIT_SIG_ENTER) + ret |= NK_EDIT_COMMITED; + else nk_textedit_text(edit, "\n", 1); + } + + /* cut & copy handler */ + {int copy= nk_input_is_key_pressed(in, NK_KEY_COPY); + int cut = nk_input_is_key_pressed(in, NK_KEY_CUT); + if ((copy || cut) && (flags & NK_EDIT_CLIPBOARD)) + { + int glyph_len; + nk_rune unicode; + const char *text; + int b = edit->select_start; + int e = edit->select_end; + + int begin = NK_MIN(b, e); + int end = NK_MAX(b, e); + text = nk_str_at_const(&edit->string, begin, &unicode, &glyph_len); + if (edit->clip.copy) + edit->clip.copy(edit->clip.userdata, text, end - begin); + if (cut && !(flags & NK_EDIT_READ_ONLY)){ + nk_textedit_cut(edit); + cursor_follow = nk_true; + } + }} + + /* paste handler */ + {int paste = nk_input_is_key_pressed(in, NK_KEY_PASTE); + if (paste && (flags & NK_EDIT_CLIPBOARD) && edit->clip.paste) { + edit->clip.paste(edit->clip.userdata, edit); + cursor_follow = nk_true; + }} + + /* tab handler */ + {int tab = nk_input_is_key_pressed(in, NK_KEY_TAB); + if (tab && (flags & NK_EDIT_ALLOW_TAB)) { + nk_textedit_text(edit, " ", 4); + cursor_follow = nk_true; + }} + } + + /* set widget state */ + if (edit->active) + *state = NK_WIDGET_STATE_ACTIVE; + else nk_widget_state_reset(state); + + if (is_hovered) + *state |= NK_WIDGET_STATE_HOVERED; + + /* DRAW EDIT */ + {const char *text = nk_str_get_const(&edit->string); + int len = nk_str_len_char(&edit->string); + + {/* select background colors/images */ + const struct nk_style_item *background; + if (*state & NK_WIDGET_STATE_ACTIVED) + background = &style->active; + else if (*state & NK_WIDGET_STATE_HOVER) + background = &style->hover; + else background = &style->normal; + + /* draw background frame */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, bounds, style->rounding, background->data.color); + nk_stroke_rect(out, bounds, style->rounding, style->border, style->border_color); + } else nk_draw_image(out, bounds, &background->data.image, nk_white);} + + area.w = NK_MAX(0, area.w - style->cursor_size); + if (edit->active) + { + int total_lines = 1; + struct nk_vec2 text_size = nk_vec2(0,0); + + /* text pointer positions */ + const char *cursor_ptr = 0; + const char *select_begin_ptr = 0; + const char *select_end_ptr = 0; + + /* 2D pixel positions */ + struct nk_vec2 cursor_pos = nk_vec2(0,0); + struct nk_vec2 selection_offset_start = nk_vec2(0,0); + struct nk_vec2 selection_offset_end = nk_vec2(0,0); + + int selection_begin = NK_MIN(edit->select_start, edit->select_end); + int selection_end = NK_MAX(edit->select_start, edit->select_end); + + /* calculate total line count + total space + cursor/selection position */ + float line_width = 0.0f; + if (text && len) + { + /* utf8 encoding */ + float glyph_width; + int glyph_len = 0; + nk_rune unicode = 0; + int text_len = 0; + int glyphs = 0; + int row_begin = 0; + + glyph_len = nk_utf_decode(text, &unicode, len); + glyph_width = font->width(font->userdata, font->height, text, glyph_len); + line_width = 0; + + /* iterate all lines */ + while ((text_len < len) && glyph_len) + { + /* set cursor 2D position and line */ + if (!cursor_ptr && glyphs == edit->cursor) + { + int glyph_offset; + struct nk_vec2 out_offset; + struct nk_vec2 row_size; + const char *remaining; + + /* calculate 2d position */ + cursor_pos.y = (float)(total_lines-1) * row_height; + row_size = nk_text_calculate_text_bounds(font, text+row_begin, + text_len-row_begin, row_height, &remaining, + &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); + cursor_pos.x = row_size.x; + cursor_ptr = text + text_len; + } + + /* set start selection 2D position and line */ + if (!select_begin_ptr && edit->select_start != edit->select_end && + glyphs == selection_begin) + { + int glyph_offset; + struct nk_vec2 out_offset; + struct nk_vec2 row_size; + const char *remaining; + + /* calculate 2d position */ + selection_offset_start.y = (float)(NK_MAX(total_lines-1,0)) * row_height; + row_size = nk_text_calculate_text_bounds(font, text+row_begin, + text_len-row_begin, row_height, &remaining, + &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); + selection_offset_start.x = row_size.x; + select_begin_ptr = text + text_len; + } + + /* set end selection 2D position and line */ + if (!select_end_ptr && edit->select_start != edit->select_end && + glyphs == selection_end) + { + int glyph_offset; + struct nk_vec2 out_offset; + struct nk_vec2 row_size; + const char *remaining; + + /* calculate 2d position */ + selection_offset_end.y = (float)(total_lines-1) * row_height; + row_size = nk_text_calculate_text_bounds(font, text+row_begin, + text_len-row_begin, row_height, &remaining, + &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); + selection_offset_end.x = row_size.x; + select_end_ptr = text + text_len; + } + if (unicode == '\n') { + text_size.x = NK_MAX(text_size.x, line_width); + total_lines++; + line_width = 0; + text_len++; + glyphs++; + row_begin = text_len; + glyph_len = nk_utf_decode(text + text_len, &unicode, len-text_len); + glyph_width = font->width(font->userdata, font->height, text+text_len, glyph_len); + continue; + } + + glyphs++; + text_len += glyph_len; + line_width += (float)glyph_width; + + glyph_len = nk_utf_decode(text + text_len, &unicode, len-text_len); + glyph_width = font->width(font->userdata, font->height, + text+text_len, glyph_len); + continue; + } + text_size.y = (float)total_lines * row_height; + + /* handle case when cursor is at end of text buffer */ + if (!cursor_ptr && edit->cursor == edit->string.len) { + cursor_pos.x = line_width; + cursor_pos.y = text_size.y - row_height; + } + } + { + /* scrollbar */ + if (cursor_follow) + { + /* update scrollbar to follow cursor */ + if (!(flags & NK_EDIT_NO_HORIZONTAL_SCROLL)) { + /* horizontal scroll */ + const float scroll_increment = area.w * 0.25f; + if (cursor_pos.x < edit->scrollbar.x) + edit->scrollbar.x = (float)(int)NK_MAX(0.0f, cursor_pos.x - scroll_increment); + if (cursor_pos.x >= edit->scrollbar.x + area.w) + edit->scrollbar.x = (float)(int)NK_MAX(0.0f, cursor_pos.x - area.w + scroll_increment); + } else edit->scrollbar.x = 0; + + if (flags & NK_EDIT_MULTILINE) { + /* vertical scroll */ + if (cursor_pos.y < edit->scrollbar.y) + edit->scrollbar.y = NK_MAX(0.0f, cursor_pos.y - row_height); + if (cursor_pos.y >= edit->scrollbar.y + area.h) + edit->scrollbar.y = edit->scrollbar.y + row_height; + } else edit->scrollbar.y = 0; + } + + /* scrollbar widget */ + if (flags & NK_EDIT_MULTILINE) + { + nk_flags ws; + struct nk_rect scroll; + float scroll_target; + float scroll_offset; + float scroll_step; + float scroll_inc; + + scroll = area; + scroll.x = (bounds.x + bounds.w - style->border) - style->scrollbar_size.x; + scroll.w = style->scrollbar_size.x; + + scroll_offset = edit->scrollbar.y; + scroll_step = scroll.h * 0.10f; + scroll_inc = scroll.h * 0.01f; + scroll_target = text_size.y; + edit->scrollbar.y = nk_do_scrollbarv(&ws, out, scroll, 0, + scroll_offset, scroll_target, scroll_step, scroll_inc, + &style->scrollbar, in, font); + } + } + + /* draw text */ + {struct nk_color background_color; + struct nk_color text_color; + struct nk_color sel_background_color; + struct nk_color sel_text_color; + struct nk_color cursor_color; + struct nk_color cursor_text_color; + const struct nk_style_item *background; + nk_push_scissor(out, clip); + + /* select correct colors to draw */ + if (*state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + text_color = style->text_active; + sel_text_color = style->selected_text_hover; + sel_background_color = style->selected_hover; + cursor_color = style->cursor_hover; + cursor_text_color = style->cursor_text_hover; + } else if (*state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + text_color = style->text_hover; + sel_text_color = style->selected_text_hover; + sel_background_color = style->selected_hover; + cursor_text_color = style->cursor_text_hover; + cursor_color = style->cursor_hover; + } else { + background = &style->normal; + text_color = style->text_normal; + sel_text_color = style->selected_text_normal; + sel_background_color = style->selected_normal; + cursor_color = style->cursor_normal; + cursor_text_color = style->cursor_text_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) + background_color = nk_rgba(0,0,0,0); + else background_color = background->data.color; + + + if (edit->select_start == edit->select_end) { + /* no selection so just draw the complete text */ + const char *begin = nk_str_get_const(&edit->string); + int l = nk_str_len_char(&edit->string); + nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, + area.y - edit->scrollbar.y, 0, begin, l, row_height, font, + background_color, text_color, nk_false); + } else { + /* edit has selection so draw 1-3 text chunks */ + if (edit->select_start != edit->select_end && selection_begin > 0){ + /* draw unselected text before selection */ + const char *begin = nk_str_get_const(&edit->string); + NK_ASSERT(select_begin_ptr); + nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, + area.y - edit->scrollbar.y, 0, begin, (int)(select_begin_ptr - begin), + row_height, font, background_color, text_color, nk_false); + } + if (edit->select_start != edit->select_end) { + /* draw selected text */ + NK_ASSERT(select_begin_ptr); + if (!select_end_ptr) { + const char *begin = nk_str_get_const(&edit->string); + select_end_ptr = begin + nk_str_len_char(&edit->string); + } + nk_edit_draw_text(out, style, + area.x - edit->scrollbar.x, + area.y + selection_offset_start.y - edit->scrollbar.y, + selection_offset_start.x, + select_begin_ptr, (int)(select_end_ptr - select_begin_ptr), + row_height, font, sel_background_color, sel_text_color, nk_true); + } + if ((edit->select_start != edit->select_end && + selection_end < edit->string.len)) + { + /* draw unselected text after selected text */ + const char *begin = select_end_ptr; + const char *end = nk_str_get_const(&edit->string) + + nk_str_len_char(&edit->string); + NK_ASSERT(select_end_ptr); + nk_edit_draw_text(out, style, + area.x - edit->scrollbar.x, + area.y + selection_offset_end.y - edit->scrollbar.y, + selection_offset_end.x, + begin, (int)(end - begin), row_height, font, + background_color, text_color, nk_true); + } + } + + /* cursor */ + if (edit->select_start == edit->select_end) + { + if (edit->cursor >= nk_str_len(&edit->string) || + (cursor_ptr && *cursor_ptr == '\n')) { + /* draw cursor at end of line */ + struct nk_rect cursor; + cursor.w = style->cursor_size; + cursor.h = font->height; + cursor.x = area.x + cursor_pos.x - edit->scrollbar.x; + cursor.y = area.y + cursor_pos.y + row_height/2.0f - cursor.h/2.0f; + cursor.y -= edit->scrollbar.y; + nk_fill_rect(out, cursor, 0, cursor_color); + } else { + /* draw cursor inside text */ + int glyph_len; + struct nk_rect label; + struct nk_text txt; + + nk_rune unicode; + NK_ASSERT(cursor_ptr); + glyph_len = nk_utf_decode(cursor_ptr, &unicode, 4); + + label.x = area.x + cursor_pos.x - edit->scrollbar.x; + label.y = area.y + cursor_pos.y - edit->scrollbar.y; + label.w = font->width(font->userdata, font->height, cursor_ptr, glyph_len); + label.h = row_height; + + txt.padding = nk_vec2(0,0); + txt.background = cursor_color;; + txt.text = cursor_text_color; + nk_fill_rect(out, label, 0, cursor_color); + nk_widget_text(out, label, cursor_ptr, glyph_len, &txt, NK_TEXT_LEFT, font); + } + }} + } else { + /* not active so just draw text */ + int l = nk_str_len_char(&edit->string); + const char *begin = nk_str_get_const(&edit->string); + + const struct nk_style_item *background; + struct nk_color background_color; + struct nk_color text_color; + nk_push_scissor(out, clip); + if (*state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + text_color = style->text_active; + } else if (*state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + text_color = style->text_hover; + } else { + background = &style->normal; + text_color = style->text_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) + background_color = nk_rgba(0,0,0,0); + else background_color = background->data.color; + nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, + area.y - edit->scrollbar.y, 0, begin, l, row_height, font, + background_color, text_color, nk_false); + } + nk_push_scissor(out, old_clip);} + return ret; +} +NK_API void +nk_edit_focus(struct nk_context *ctx, nk_flags flags) +{ + nk_hash hash; + struct nk_window *win; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + + win = ctx->current; + hash = win->edit.seq; + win->edit.active = nk_true; + win->edit.name = hash; + if (flags & NK_EDIT_ALWAYS_INSERT_MODE) + win->edit.mode = NK_TEXT_EDIT_MODE_INSERT; +} +NK_API void +nk_edit_unfocus(struct nk_context *ctx) +{ + struct nk_window *win; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + + win = ctx->current; + win->edit.active = nk_false; + win->edit.name = 0; +} +NK_API nk_flags +nk_edit_string(struct nk_context *ctx, nk_flags flags, + char *memory, int *len, int max, nk_plugin_filter filter) +{ + nk_hash hash; + nk_flags state; + struct nk_text_edit *edit; + struct nk_window *win; + + NK_ASSERT(ctx); + NK_ASSERT(memory); + NK_ASSERT(len); + if (!ctx || !memory || !len) + return 0; + + filter = (!filter) ? nk_filter_default: filter; + win = ctx->current; + hash = win->edit.seq; + edit = &ctx->text_edit; + nk_textedit_clear_state(&ctx->text_edit, (flags & NK_EDIT_MULTILINE)? + NK_TEXT_EDIT_MULTI_LINE: NK_TEXT_EDIT_SINGLE_LINE, filter); + + if (win->edit.active && hash == win->edit.name) { + if (flags & NK_EDIT_NO_CURSOR) + edit->cursor = nk_utf_len(memory, *len); + else edit->cursor = win->edit.cursor; + if (!(flags & NK_EDIT_SELECTABLE)) { + edit->select_start = win->edit.cursor; + edit->select_end = win->edit.cursor; + } else { + edit->select_start = win->edit.sel_start; + edit->select_end = win->edit.sel_end; + } + edit->mode = win->edit.mode; + edit->scrollbar.x = (float)win->edit.scrollbar.x; + edit->scrollbar.y = (float)win->edit.scrollbar.y; + edit->active = nk_true; + } else edit->active = nk_false; + + max = NK_MAX(1, max); + *len = NK_MIN(*len, max-1); + nk_str_init_fixed(&edit->string, memory, (nk_size)max); + edit->string.buffer.allocated = (nk_size)*len; + edit->string.len = nk_utf_len(memory, *len); + state = nk_edit_buffer(ctx, flags, edit, filter); + *len = (int)edit->string.buffer.allocated; + + if (edit->active) { + win->edit.cursor = edit->cursor; + win->edit.sel_start = edit->select_start; + win->edit.sel_end = edit->select_end; + win->edit.mode = edit->mode; + win->edit.scrollbar.x = (nk_uint)edit->scrollbar.x; + win->edit.scrollbar.y = (nk_uint)edit->scrollbar.y; + } return state; +} +NK_API nk_flags +nk_edit_buffer(struct nk_context *ctx, nk_flags flags, + struct nk_text_edit *edit, nk_plugin_filter filter) +{ + struct nk_window *win; + struct nk_style *style; + struct nk_input *in; + + enum nk_widget_layout_states state; + struct nk_rect bounds; + + nk_flags ret_flags = 0; + unsigned char prev_state; + nk_hash hash; + + /* make sure correct values */ + NK_ASSERT(ctx); + NK_ASSERT(edit); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget(&bounds, ctx); + if (!state) return state; + in = (win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + + /* check if edit is currently hot item */ + hash = win->edit.seq++; + if (win->edit.active && hash == win->edit.name) { + if (flags & NK_EDIT_NO_CURSOR) + edit->cursor = edit->string.len; + if (!(flags & NK_EDIT_SELECTABLE)) { + edit->select_start = edit->cursor; + edit->select_end = edit->cursor; + } + if (flags & NK_EDIT_CLIPBOARD) + edit->clip = ctx->clip; + edit->active = (unsigned char)win->edit.active; + } else edit->active = nk_false; + edit->mode = win->edit.mode; + + filter = (!filter) ? nk_filter_default: filter; + prev_state = (unsigned char)edit->active; + in = (flags & NK_EDIT_READ_ONLY) ? 0: in; + ret_flags = nk_do_edit(&ctx->last_widget_state, &win->buffer, bounds, flags, + filter, edit, &style->edit, in, style->font); + + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_TEXT]; + if (edit->active && prev_state != edit->active) { + /* current edit is now hot */ + win->edit.active = nk_true; + win->edit.name = hash; + } else if (prev_state && !edit->active) { + /* current edit is now cold */ + win->edit.active = nk_false; + } return ret_flags; +} +NK_API nk_flags +nk_edit_string_zero_terminated(struct nk_context *ctx, nk_flags flags, + char *buffer, int max, nk_plugin_filter filter) +{ + nk_flags result; + int len = nk_strlen(buffer); + result = nk_edit_string(ctx, flags, buffer, &len, max, filter); + buffer[NK_MIN(NK_MAX(max-1,0), len)] = '\0'; + return result; +} + + + + + +/* =============================================================== + * + * PROPERTY + * + * ===============================================================*/ +NK_LIB void +nk_drag_behavior(nk_flags *state, const struct nk_input *in, + struct nk_rect drag, struct nk_property_variant *variant, + float inc_per_pixel) +{ + int left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down; + int left_mouse_click_in_cursor = in && + nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, drag, nk_true); + + nk_widget_state_reset(state); + if (nk_input_is_mouse_hovering_rect(in, drag)) + *state = NK_WIDGET_STATE_HOVERED; + + if (left_mouse_down && left_mouse_click_in_cursor) { + float delta, pixels; + pixels = in->mouse.delta.x; + delta = pixels * inc_per_pixel; + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + variant->value.i = variant->value.i + (int)delta; + variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i, variant->max_value.i); + break; + case NK_PROPERTY_FLOAT: + variant->value.f = variant->value.f + (float)delta; + variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f, variant->max_value.f); + break; + case NK_PROPERTY_DOUBLE: + variant->value.d = variant->value.d + (double)delta; + variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d, variant->max_value.d); + break; + } + *state = NK_WIDGET_STATE_ACTIVE; + } + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, drag)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, drag)) + *state |= NK_WIDGET_STATE_LEFT; +} +NK_LIB void +nk_property_behavior(nk_flags *ws, const struct nk_input *in, + struct nk_rect property, struct nk_rect label, struct nk_rect edit, + struct nk_rect empty, int *state, struct nk_property_variant *variant, + float inc_per_pixel) +{ + if (in && *state == NK_PROPERTY_DEFAULT) { + if (nk_button_behavior(ws, edit, in, NK_BUTTON_DEFAULT)) + *state = NK_PROPERTY_EDIT; + else if (nk_input_is_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, label, nk_true)) + *state = NK_PROPERTY_DRAG; + else if (nk_input_is_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, empty, nk_true)) + *state = NK_PROPERTY_DRAG; + } + if (*state == NK_PROPERTY_DRAG) { + nk_drag_behavior(ws, in, property, variant, inc_per_pixel); + if (!(*ws & NK_WIDGET_STATE_ACTIVED)) *state = NK_PROPERTY_DEFAULT; + } +} +NK_LIB void +nk_draw_property(struct nk_command_buffer *out, const struct nk_style_property *style, + const struct nk_rect *bounds, const struct nk_rect *label, nk_flags state, + const char *name, int len, const struct nk_user_font *font) +{ + struct nk_text text; + const struct nk_style_item *background; + + /* select correct background and text color */ + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + text.text = style->label_active; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + text.text = style->label_hover; + } else { + background = &style->normal; + text.text = style->label_normal; + } + + /* draw background */ + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + text.background = nk_rgba(0,0,0,0); + } else { + text.background = background->data.color; + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + nk_stroke_rect(out, *bounds, style->rounding, style->border, background->data.color); + } + + /* draw label */ + text.padding = nk_vec2(0,0); + nk_widget_text(out, *label, name, len, &text, NK_TEXT_CENTERED, font); +} +NK_LIB void +nk_do_property(nk_flags *ws, + struct nk_command_buffer *out, struct nk_rect property, + const char *name, struct nk_property_variant *variant, + float inc_per_pixel, char *buffer, int *len, + int *state, int *cursor, int *select_begin, int *select_end, + const struct nk_style_property *style, + enum nk_property_filter filter, struct nk_input *in, + const struct nk_user_font *font, struct nk_text_edit *text_edit, + enum nk_button_behavior behavior) +{ + const nk_plugin_filter filters[] = { + nk_filter_decimal, + nk_filter_float + }; + nk_bool active, old; + int num_len, name_len; + char string[NK_MAX_NUMBER_BUFFER]; + float size; + + char *dst = 0; + int *length; + + struct nk_rect left; + struct nk_rect right; + struct nk_rect label; + struct nk_rect edit; + struct nk_rect empty; + + /* left decrement button */ + left.h = font->height/2; + left.w = left.h; + left.x = property.x + style->border + style->padding.x; + left.y = property.y + style->border + property.h/2.0f - left.h/2; + + /* text label */ + name_len = nk_strlen(name); + size = font->width(font->userdata, font->height, name, name_len); + label.x = left.x + left.w + style->padding.x; + label.w = (float)size + 2 * style->padding.x; + label.y = property.y + style->border + style->padding.y; + label.h = property.h - (2 * style->border + 2 * style->padding.y); + + /* right increment button */ + right.y = left.y; + right.w = left.w; + right.h = left.h; + right.x = property.x + property.w - (right.w + style->padding.x); + + /* edit */ + if (*state == NK_PROPERTY_EDIT) { + size = font->width(font->userdata, font->height, buffer, *len); + size += style->edit.cursor_size; + length = len; + dst = buffer; + } else { + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + nk_itoa(string, variant->value.i); + num_len = nk_strlen(string); + break; + case NK_PROPERTY_FLOAT: + NK_DTOA(string, (double)variant->value.f); + num_len = nk_string_float_limit(string, NK_MAX_FLOAT_PRECISION); + break; + case NK_PROPERTY_DOUBLE: + NK_DTOA(string, variant->value.d); + num_len = nk_string_float_limit(string, NK_MAX_FLOAT_PRECISION); + break; + } + size = font->width(font->userdata, font->height, string, num_len); + dst = string; + length = &num_len; + } + + edit.w = (float)size + 2 * style->padding.x; + edit.w = NK_MIN(edit.w, right.x - (label.x + label.w)); + edit.x = right.x - (edit.w + style->padding.x); + edit.y = property.y + style->border; + edit.h = property.h - (2 * style->border); + + /* empty left space activator */ + empty.w = edit.x - (label.x + label.w); + empty.x = label.x + label.w; + empty.y = property.y; + empty.h = property.h; + + /* update property */ + old = (*state == NK_PROPERTY_EDIT); + nk_property_behavior(ws, in, property, label, edit, empty, state, variant, inc_per_pixel); + + /* draw property */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_property(out, style, &property, &label, *ws, name, name_len, font); + if (style->draw_end) style->draw_end(out, style->userdata); + + /* execute right button */ + if (nk_do_button_symbol(ws, out, left, style->sym_left, behavior, &style->dec_button, in, font)) { + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i - variant->step.i, variant->max_value.i); break; + case NK_PROPERTY_FLOAT: + variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f - variant->step.f, variant->max_value.f); break; + case NK_PROPERTY_DOUBLE: + variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d - variant->step.d, variant->max_value.d); break; + } + } + /* execute left button */ + if (nk_do_button_symbol(ws, out, right, style->sym_right, behavior, &style->inc_button, in, font)) { + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i + variant->step.i, variant->max_value.i); break; + case NK_PROPERTY_FLOAT: + variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f + variant->step.f, variant->max_value.f); break; + case NK_PROPERTY_DOUBLE: + variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d + variant->step.d, variant->max_value.d); break; + } + } + if (old != NK_PROPERTY_EDIT && (*state == NK_PROPERTY_EDIT)) { + /* property has been activated so setup buffer */ + NK_MEMCPY(buffer, dst, (nk_size)*length); + *cursor = nk_utf_len(buffer, *length); + *len = *length; + length = len; + dst = buffer; + active = 0; + } else active = (*state == NK_PROPERTY_EDIT); + + /* execute and run text edit field */ + nk_textedit_clear_state(text_edit, NK_TEXT_EDIT_SINGLE_LINE, filters[filter]); + text_edit->active = (unsigned char)active; + text_edit->string.len = *length; + text_edit->cursor = NK_CLAMP(0, *cursor, *length); + text_edit->select_start = NK_CLAMP(0,*select_begin, *length); + text_edit->select_end = NK_CLAMP(0,*select_end, *length); + text_edit->string.buffer.allocated = (nk_size)*length; + text_edit->string.buffer.memory.size = NK_MAX_NUMBER_BUFFER; + text_edit->string.buffer.memory.ptr = dst; + text_edit->string.buffer.size = NK_MAX_NUMBER_BUFFER; + text_edit->mode = NK_TEXT_EDIT_MODE_INSERT; + nk_do_edit(ws, out, edit, NK_EDIT_FIELD|NK_EDIT_AUTO_SELECT, + filters[filter], text_edit, &style->edit, (*state == NK_PROPERTY_EDIT) ? in: 0, font); + + *length = text_edit->string.len; + *cursor = text_edit->cursor; + *select_begin = text_edit->select_start; + *select_end = text_edit->select_end; + if (text_edit->active && nk_input_is_key_pressed(in, NK_KEY_ENTER)) + text_edit->active = nk_false; + + if (active && !text_edit->active) { + /* property is now not active so convert edit text to value*/ + *state = NK_PROPERTY_DEFAULT; + buffer[*len] = '\0'; + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + variant->value.i = nk_strtoi(buffer, 0); + variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i, variant->max_value.i); + break; + case NK_PROPERTY_FLOAT: + nk_string_float_limit(buffer, NK_MAX_FLOAT_PRECISION); + variant->value.f = nk_strtof(buffer, 0); + variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f, variant->max_value.f); + break; + case NK_PROPERTY_DOUBLE: + nk_string_float_limit(buffer, NK_MAX_FLOAT_PRECISION); + variant->value.d = nk_strtod(buffer, 0); + variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d, variant->max_value.d); + break; + } + } +} +NK_LIB struct nk_property_variant +nk_property_variant_int(int value, int min_value, int max_value, int step) +{ + struct nk_property_variant result; + result.kind = NK_PROPERTY_INT; + result.value.i = value; + result.min_value.i = min_value; + result.max_value.i = max_value; + result.step.i = step; + return result; +} +NK_LIB struct nk_property_variant +nk_property_variant_float(float value, float min_value, float max_value, float step) +{ + struct nk_property_variant result; + result.kind = NK_PROPERTY_FLOAT; + result.value.f = value; + result.min_value.f = min_value; + result.max_value.f = max_value; + result.step.f = step; + return result; +} +NK_LIB struct nk_property_variant +nk_property_variant_double(double value, double min_value, double max_value, + double step) +{ + struct nk_property_variant result; + result.kind = NK_PROPERTY_DOUBLE; + result.value.d = value; + result.min_value.d = min_value; + result.max_value.d = max_value; + result.step.d = step; + return result; +} +NK_LIB void +nk_property(struct nk_context *ctx, const char *name, struct nk_property_variant *variant, + float inc_per_pixel, const enum nk_property_filter filter) +{ + struct nk_window *win; + struct nk_panel *layout; + struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states s; + + int *state = 0; + nk_hash hash = 0; + char *buffer = 0; + int *len = 0; + int *cursor = 0; + int *select_begin = 0; + int *select_end = 0; + int old_state; + + char dummy_buffer[NK_MAX_NUMBER_BUFFER]; + int dummy_state = NK_PROPERTY_DEFAULT; + int dummy_length = 0; + int dummy_cursor = 0; + int dummy_select_begin = 0; + int dummy_select_end = 0; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + style = &ctx->style; + s = nk_widget(&bounds, ctx); + if (!s) return; + + /* calculate hash from name */ + if (name[0] == '#') { + hash = nk_murmur_hash(name, (int)nk_strlen(name), win->property.seq++); + name++; /* special number hash */ + } else hash = nk_murmur_hash(name, (int)nk_strlen(name), 42); + + /* check if property is currently hot item */ + if (win->property.active && hash == win->property.name) { + buffer = win->property.buffer; + len = &win->property.length; + cursor = &win->property.cursor; + state = &win->property.state; + select_begin = &win->property.select_start; + select_end = &win->property.select_end; + } else { + buffer = dummy_buffer; + len = &dummy_length; + cursor = &dummy_cursor; + state = &dummy_state; + select_begin = &dummy_select_begin; + select_end = &dummy_select_end; + } + + /* execute property widget */ + old_state = *state; + ctx->text_edit.clip = ctx->clip; + in = ((s == NK_WIDGET_ROM && !win->property.active) || + layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + nk_do_property(&ctx->last_widget_state, &win->buffer, bounds, name, + variant, inc_per_pixel, buffer, len, state, cursor, select_begin, + select_end, &style->property, filter, in, style->font, &ctx->text_edit, + ctx->button_behavior); + + if (in && *state != NK_PROPERTY_DEFAULT && !win->property.active) { + /* current property is now hot */ + win->property.active = 1; + NK_MEMCPY(win->property.buffer, buffer, (nk_size)*len); + win->property.length = *len; + win->property.cursor = *cursor; + win->property.state = *state; + win->property.name = hash; + win->property.select_start = *select_begin; + win->property.select_end = *select_end; + if (*state == NK_PROPERTY_DRAG) { + ctx->input.mouse.grab = nk_true; + ctx->input.mouse.grabbed = nk_true; + } + } + /* check if previously active property is now inactive */ + if (*state == NK_PROPERTY_DEFAULT && old_state != NK_PROPERTY_DEFAULT) { + if (old_state == NK_PROPERTY_DRAG) { + ctx->input.mouse.grab = nk_false; + ctx->input.mouse.grabbed = nk_false; + ctx->input.mouse.ungrab = nk_true; + } + win->property.select_start = 0; + win->property.select_end = 0; + win->property.active = 0; + } +} +NK_API void +nk_property_int(struct nk_context *ctx, const char *name, + int min, int *val, int max, int step, float inc_per_pixel) +{ + struct nk_property_variant variant; + NK_ASSERT(ctx); + NK_ASSERT(name); + NK_ASSERT(val); + + if (!ctx || !ctx->current || !name || !val) return; + variant = nk_property_variant_int(*val, min, max, step); + nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_INT); + *val = variant.value.i; +} +NK_API void +nk_property_float(struct nk_context *ctx, const char *name, + float min, float *val, float max, float step, float inc_per_pixel) +{ + struct nk_property_variant variant; + NK_ASSERT(ctx); + NK_ASSERT(name); + NK_ASSERT(val); + + if (!ctx || !ctx->current || !name || !val) return; + variant = nk_property_variant_float(*val, min, max, step); + nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT); + *val = variant.value.f; +} +NK_API void +nk_property_double(struct nk_context *ctx, const char *name, + double min, double *val, double max, double step, float inc_per_pixel) +{ + struct nk_property_variant variant; + NK_ASSERT(ctx); + NK_ASSERT(name); + NK_ASSERT(val); + + if (!ctx || !ctx->current || !name || !val) return; + variant = nk_property_variant_double(*val, min, max, step); + nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT); + *val = variant.value.d; +} +NK_API nk_bool +nk_propertyi(struct nk_context *ctx, const char *name, int min, int val, + int max, int step, float inc_per_pixel) +{ + struct nk_property_variant variant; + NK_ASSERT(ctx); + NK_ASSERT(name); + + if (!ctx || !ctx->current || !name) return val; + variant = nk_property_variant_int(val, min, max, step); + nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_INT); + val = variant.value.i; + return val; +} +NK_API float +nk_propertyf(struct nk_context *ctx, const char *name, float min, + float val, float max, float step, float inc_per_pixel) +{ + struct nk_property_variant variant; + NK_ASSERT(ctx); + NK_ASSERT(name); + + if (!ctx || !ctx->current || !name) return val; + variant = nk_property_variant_float(val, min, max, step); + nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT); + val = variant.value.f; + return val; +} +NK_API double +nk_propertyd(struct nk_context *ctx, const char *name, double min, + double val, double max, double step, float inc_per_pixel) +{ + struct nk_property_variant variant; + NK_ASSERT(ctx); + NK_ASSERT(name); + + if (!ctx || !ctx->current || !name) return val; + variant = nk_property_variant_double(val, min, max, step); + nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT); + val = variant.value.d; + return val; +} + + + + + +/* ============================================================== + * + * CHART + * + * ===============================================================*/ +NK_API nk_bool +nk_chart_begin_colored(struct nk_context *ctx, enum nk_chart_type type, + struct nk_color color, struct nk_color highlight, + int count, float min_value, float max_value) +{ + struct nk_window *win; + struct nk_chart *chart; + const struct nk_style *config; + const struct nk_style_chart *style; + + const struct nk_style_item *background; + struct nk_rect bounds = {0, 0, 0, 0}; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + + if (!ctx || !ctx->current || !ctx->current->layout) return 0; + if (!nk_widget(&bounds, ctx)) { + chart = &ctx->current->layout->chart; + nk_zero(chart, sizeof(*chart)); + return 0; + } + + win = ctx->current; + config = &ctx->style; + chart = &win->layout->chart; + style = &config->chart; + + /* setup basic generic chart */ + nk_zero(chart, sizeof(*chart)); + chart->x = bounds.x + style->padding.x; + chart->y = bounds.y + style->padding.y; + chart->w = bounds.w - 2 * style->padding.x; + chart->h = bounds.h - 2 * style->padding.y; + chart->w = NK_MAX(chart->w, 2 * style->padding.x); + chart->h = NK_MAX(chart->h, 2 * style->padding.y); + + /* add first slot into chart */ + {struct nk_chart_slot *slot = &chart->slots[chart->slot++]; + slot->type = type; + slot->count = count; + slot->color = color; + slot->highlight = highlight; + slot->min = NK_MIN(min_value, max_value); + slot->max = NK_MAX(min_value, max_value); + slot->range = slot->max - slot->min;} + + /* draw chart background */ + background = &style->background; + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(&win->buffer, bounds, &background->data.image, nk_white); + } else { + nk_fill_rect(&win->buffer, bounds, style->rounding, style->border_color); + nk_fill_rect(&win->buffer, nk_shrink_rect(bounds, style->border), + style->rounding, style->background.data.color); + } + return 1; +} +NK_API nk_bool +nk_chart_begin(struct nk_context *ctx, const enum nk_chart_type type, + int count, float min_value, float max_value) +{ + return nk_chart_begin_colored(ctx, type, ctx->style.chart.color, + ctx->style.chart.selected_color, count, min_value, max_value); +} +NK_API void +nk_chart_add_slot_colored(struct nk_context *ctx, const enum nk_chart_type type, + struct nk_color color, struct nk_color highlight, + int count, float min_value, float max_value) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + NK_ASSERT(ctx->current->layout->chart.slot < NK_CHART_MAX_SLOT); + if (!ctx || !ctx->current || !ctx->current->layout) return; + if (ctx->current->layout->chart.slot >= NK_CHART_MAX_SLOT) return; + + /* add another slot into the graph */ + {struct nk_chart *chart = &ctx->current->layout->chart; + struct nk_chart_slot *slot = &chart->slots[chart->slot++]; + slot->type = type; + slot->count = count; + slot->color = color; + slot->highlight = highlight; + slot->min = NK_MIN(min_value, max_value); + slot->max = NK_MAX(min_value, max_value); + slot->range = slot->max - slot->min;} +} +NK_API void +nk_chart_add_slot(struct nk_context *ctx, const enum nk_chart_type type, + int count, float min_value, float max_value) +{ + nk_chart_add_slot_colored(ctx, type, ctx->style.chart.color, + ctx->style.chart.selected_color, count, min_value, max_value); +} +NK_INTERN nk_flags +nk_chart_push_line(struct nk_context *ctx, struct nk_window *win, + struct nk_chart *g, float value, int slot) +{ + struct nk_panel *layout = win->layout; + const struct nk_input *i = &ctx->input; + struct nk_command_buffer *out = &win->buffer; + + nk_flags ret = 0; + struct nk_vec2 cur; + struct nk_rect bounds; + struct nk_color color; + float step; + float range; + float ratio; + + NK_ASSERT(slot >= 0 && slot < NK_CHART_MAX_SLOT); + step = g->w / (float)g->slots[slot].count; + range = g->slots[slot].max - g->slots[slot].min; + ratio = (value - g->slots[slot].min) / range; + + if (g->slots[slot].index == 0) { + /* first data point does not have a connection */ + g->slots[slot].last.x = g->x; + g->slots[slot].last.y = (g->y + g->h) - ratio * (float)g->h; + + bounds.x = g->slots[slot].last.x - 2; + bounds.y = g->slots[slot].last.y - 2; + bounds.w = bounds.h = 4; + + color = g->slots[slot].color; + if (!(layout->flags & NK_WINDOW_ROM) && + NK_INBOX(i->mouse.pos.x,i->mouse.pos.y, g->slots[slot].last.x-3, g->slots[slot].last.y-3, 6, 6)){ + ret = nk_input_is_mouse_hovering_rect(i, bounds) ? NK_CHART_HOVERING : 0; + ret |= (i->mouse.buttons[NK_BUTTON_LEFT].down && + i->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0; + color = g->slots[slot].highlight; + } + nk_fill_rect(out, bounds, 0, color); + g->slots[slot].index += 1; + return ret; + } + + /* draw a line between the last data point and the new one */ + color = g->slots[slot].color; + cur.x = g->x + (float)(step * (float)g->slots[slot].index); + cur.y = (g->y + g->h) - (ratio * (float)g->h); + nk_stroke_line(out, g->slots[slot].last.x, g->slots[slot].last.y, cur.x, cur.y, 1.0f, color); + + bounds.x = cur.x - 3; + bounds.y = cur.y - 3; + bounds.w = bounds.h = 6; + + /* user selection of current data point */ + if (!(layout->flags & NK_WINDOW_ROM)) { + if (nk_input_is_mouse_hovering_rect(i, bounds)) { + ret = NK_CHART_HOVERING; + ret |= (!i->mouse.buttons[NK_BUTTON_LEFT].down && + i->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0; + color = g->slots[slot].highlight; + } + } + nk_fill_rect(out, nk_rect(cur.x - 2, cur.y - 2, 4, 4), 0, color); + + /* save current data point position */ + g->slots[slot].last.x = cur.x; + g->slots[slot].last.y = cur.y; + g->slots[slot].index += 1; + return ret; +} +NK_INTERN nk_flags +nk_chart_push_column(const struct nk_context *ctx, struct nk_window *win, + struct nk_chart *chart, float value, int slot) +{ + struct nk_command_buffer *out = &win->buffer; + const struct nk_input *in = &ctx->input; + struct nk_panel *layout = win->layout; + + float ratio; + nk_flags ret = 0; + struct nk_color color; + struct nk_rect item = {0,0,0,0}; + + NK_ASSERT(slot >= 0 && slot < NK_CHART_MAX_SLOT); + if (chart->slots[slot].index >= chart->slots[slot].count) + return nk_false; + if (chart->slots[slot].count) { + float padding = (float)(chart->slots[slot].count-1); + item.w = (chart->w - padding) / (float)(chart->slots[slot].count); + } + + /* calculate bounds of current bar chart entry */ + color = chart->slots[slot].color;; + item.h = chart->h * NK_ABS((value/chart->slots[slot].range)); + if (value >= 0) { + ratio = (value + NK_ABS(chart->slots[slot].min)) / NK_ABS(chart->slots[slot].range); + item.y = (chart->y + chart->h) - chart->h * ratio; + } else { + ratio = (value - chart->slots[slot].max) / chart->slots[slot].range; + item.y = chart->y + (chart->h * NK_ABS(ratio)) - item.h; + } + item.x = chart->x + ((float)chart->slots[slot].index * item.w); + item.x = item.x + ((float)chart->slots[slot].index); + + /* user chart bar selection */ + if (!(layout->flags & NK_WINDOW_ROM) && + NK_INBOX(in->mouse.pos.x,in->mouse.pos.y,item.x,item.y,item.w,item.h)) { + ret = NK_CHART_HOVERING; + ret |= (!in->mouse.buttons[NK_BUTTON_LEFT].down && + in->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0; + color = chart->slots[slot].highlight; + } + nk_fill_rect(out, item, 0, color); + chart->slots[slot].index += 1; + return ret; +} +NK_API nk_flags +nk_chart_push_slot(struct nk_context *ctx, float value, int slot) +{ + nk_flags flags; + struct nk_window *win; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(slot >= 0 && slot < NK_CHART_MAX_SLOT); + NK_ASSERT(slot < ctx->current->layout->chart.slot); + if (!ctx || !ctx->current || slot >= NK_CHART_MAX_SLOT) return nk_false; + if (slot >= ctx->current->layout->chart.slot) return nk_false; + + win = ctx->current; + if (win->layout->chart.slot < slot) return nk_false; + switch (win->layout->chart.slots[slot].type) { + case NK_CHART_LINES: + flags = nk_chart_push_line(ctx, win, &win->layout->chart, value, slot); break; + case NK_CHART_COLUMN: + flags = nk_chart_push_column(ctx, win, &win->layout->chart, value, slot); break; + default: + case NK_CHART_MAX: + flags = 0; + } + return flags; +} +NK_API nk_flags +nk_chart_push(struct nk_context *ctx, float value) +{ + return nk_chart_push_slot(ctx, value, 0); +} +NK_API void +nk_chart_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_chart *chart; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return; + + win = ctx->current; + chart = &win->layout->chart; + NK_MEMSET(chart, 0, sizeof(*chart)); + return; +} +NK_API void +nk_plot(struct nk_context *ctx, enum nk_chart_type type, const float *values, + int count, int offset) +{ + int i = 0; + float min_value; + float max_value; + + NK_ASSERT(ctx); + NK_ASSERT(values); + if (!ctx || !values || !count) return; + + min_value = values[offset]; + max_value = values[offset]; + for (i = 0; i < count; ++i) { + min_value = NK_MIN(values[i + offset], min_value); + max_value = NK_MAX(values[i + offset], max_value); + } + + if (nk_chart_begin(ctx, type, count, min_value, max_value)) { + for (i = 0; i < count; ++i) + nk_chart_push(ctx, values[i + offset]); + nk_chart_end(ctx); + } +} +NK_API void +nk_plot_function(struct nk_context *ctx, enum nk_chart_type type, void *userdata, + float(*value_getter)(void* user, int index), int count, int offset) +{ + int i = 0; + float min_value; + float max_value; + + NK_ASSERT(ctx); + NK_ASSERT(value_getter); + if (!ctx || !value_getter || !count) return; + + max_value = min_value = value_getter(userdata, offset); + for (i = 0; i < count; ++i) { + float value = value_getter(userdata, i + offset); + min_value = NK_MIN(value, min_value); + max_value = NK_MAX(value, max_value); + } + + if (nk_chart_begin(ctx, type, count, min_value, max_value)) { + for (i = 0; i < count; ++i) + nk_chart_push(ctx, value_getter(userdata, i + offset)); + nk_chart_end(ctx); + } +} + + + + + +/* ============================================================== + * + * COLOR PICKER + * + * ===============================================================*/ +NK_LIB nk_bool +nk_color_picker_behavior(nk_flags *state, + const struct nk_rect *bounds, const struct nk_rect *matrix, + const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, + struct nk_colorf *color, const struct nk_input *in) +{ + float hsva[4]; + nk_bool value_changed = 0; + nk_bool hsv_changed = 0; + + NK_ASSERT(state); + NK_ASSERT(matrix); + NK_ASSERT(hue_bar); + NK_ASSERT(color); + + /* color matrix */ + nk_colorf_hsva_fv(hsva, *color); + if (nk_button_behavior(state, *matrix, in, NK_BUTTON_REPEATER)) { + hsva[1] = NK_SATURATE((in->mouse.pos.x - matrix->x) / (matrix->w-1)); + hsva[2] = 1.0f - NK_SATURATE((in->mouse.pos.y - matrix->y) / (matrix->h-1)); + value_changed = hsv_changed = 1; + } + /* hue bar */ + if (nk_button_behavior(state, *hue_bar, in, NK_BUTTON_REPEATER)) { + hsva[0] = NK_SATURATE((in->mouse.pos.y - hue_bar->y) / (hue_bar->h-1)); + value_changed = hsv_changed = 1; + } + /* alpha bar */ + if (alpha_bar) { + if (nk_button_behavior(state, *alpha_bar, in, NK_BUTTON_REPEATER)) { + hsva[3] = 1.0f - NK_SATURATE((in->mouse.pos.y - alpha_bar->y) / (alpha_bar->h-1)); + value_changed = 1; + } + } + nk_widget_state_reset(state); + if (hsv_changed) { + *color = nk_hsva_colorfv(hsva); + *state = NK_WIDGET_STATE_ACTIVE; + } + if (value_changed) { + color->a = hsva[3]; + *state = NK_WIDGET_STATE_ACTIVE; + } + /* set color picker widget state */ + if (nk_input_is_mouse_hovering_rect(in, *bounds)) + *state = NK_WIDGET_STATE_HOVERED; + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, *bounds)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, *bounds)) + *state |= NK_WIDGET_STATE_LEFT; + return value_changed; +} +NK_LIB void +nk_draw_color_picker(struct nk_command_buffer *o, const struct nk_rect *matrix, + const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, + struct nk_colorf col) +{ + NK_STORAGE const struct nk_color black = {0,0,0,255}; + NK_STORAGE const struct nk_color white = {255, 255, 255, 255}; + NK_STORAGE const struct nk_color black_trans = {0,0,0,0}; + + const float crosshair_size = 7.0f; + struct nk_color temp; + float hsva[4]; + float line_y; + int i; + + NK_ASSERT(o); + NK_ASSERT(matrix); + NK_ASSERT(hue_bar); + + /* draw hue bar */ + nk_colorf_hsva_fv(hsva, col); + for (i = 0; i < 6; ++i) { + NK_GLOBAL const struct nk_color hue_colors[] = { + {255, 0, 0, 255}, {255,255,0,255}, {0,255,0,255}, {0, 255,255,255}, + {0,0,255,255}, {255, 0, 255, 255}, {255, 0, 0, 255} + }; + nk_fill_rect_multi_color(o, + nk_rect(hue_bar->x, hue_bar->y + (float)i * (hue_bar->h/6.0f) + 0.5f, + hue_bar->w, (hue_bar->h/6.0f) + 0.5f), hue_colors[i], hue_colors[i], + hue_colors[i+1], hue_colors[i+1]); + } + line_y = (float)(int)(hue_bar->y + hsva[0] * matrix->h + 0.5f); + nk_stroke_line(o, hue_bar->x-1, line_y, hue_bar->x + hue_bar->w + 2, + line_y, 1, nk_rgb(255,255,255)); + + /* draw alpha bar */ + if (alpha_bar) { + float alpha = NK_SATURATE(col.a); + line_y = (float)(int)(alpha_bar->y + (1.0f - alpha) * matrix->h + 0.5f); + + nk_fill_rect_multi_color(o, *alpha_bar, white, white, black, black); + nk_stroke_line(o, alpha_bar->x-1, line_y, alpha_bar->x + alpha_bar->w + 2, + line_y, 1, nk_rgb(255,255,255)); + } + + /* draw color matrix */ + temp = nk_hsv_f(hsva[0], 1.0f, 1.0f); + nk_fill_rect_multi_color(o, *matrix, white, temp, temp, white); + nk_fill_rect_multi_color(o, *matrix, black_trans, black_trans, black, black); + + /* draw cross-hair */ + {struct nk_vec2 p; float S = hsva[1]; float V = hsva[2]; + p.x = (float)(int)(matrix->x + S * matrix->w); + p.y = (float)(int)(matrix->y + (1.0f - V) * matrix->h); + nk_stroke_line(o, p.x - crosshair_size, p.y, p.x-2, p.y, 1.0f, white); + nk_stroke_line(o, p.x + crosshair_size + 1, p.y, p.x+3, p.y, 1.0f, white); + nk_stroke_line(o, p.x, p.y + crosshair_size + 1, p.x, p.y+3, 1.0f, white); + nk_stroke_line(o, p.x, p.y - crosshair_size, p.x, p.y-2, 1.0f, white);} +} +NK_LIB nk_bool +nk_do_color_picker(nk_flags *state, + struct nk_command_buffer *out, struct nk_colorf *col, + enum nk_color_format fmt, struct nk_rect bounds, + struct nk_vec2 padding, const struct nk_input *in, + const struct nk_user_font *font) +{ + int ret = 0; + struct nk_rect matrix; + struct nk_rect hue_bar; + struct nk_rect alpha_bar; + float bar_w; + + NK_ASSERT(out); + NK_ASSERT(col); + NK_ASSERT(state); + NK_ASSERT(font); + if (!out || !col || !state || !font) + return ret; + + bar_w = font->height; + bounds.x += padding.x; + bounds.y += padding.x; + bounds.w -= 2 * padding.x; + bounds.h -= 2 * padding.y; + + matrix.x = bounds.x; + matrix.y = bounds.y; + matrix.h = bounds.h; + matrix.w = bounds.w - (3 * padding.x + 2 * bar_w); + + hue_bar.w = bar_w; + hue_bar.y = bounds.y; + hue_bar.h = matrix.h; + hue_bar.x = matrix.x + matrix.w + padding.x; + + alpha_bar.x = hue_bar.x + hue_bar.w + padding.x; + alpha_bar.y = bounds.y; + alpha_bar.w = bar_w; + alpha_bar.h = matrix.h; + + ret = nk_color_picker_behavior(state, &bounds, &matrix, &hue_bar, + (fmt == NK_RGBA) ? &alpha_bar:0, col, in); + nk_draw_color_picker(out, &matrix, &hue_bar, (fmt == NK_RGBA) ? &alpha_bar:0, *col); + return ret; +} +NK_API nk_bool +nk_color_pick(struct nk_context * ctx, struct nk_colorf *color, + enum nk_color_format fmt) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_style *config; + const struct nk_input *in; + + enum nk_widget_layout_states state; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(color); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !color) + return 0; + + win = ctx->current; + config = &ctx->style; + layout = win->layout; + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_color_picker(&ctx->last_widget_state, &win->buffer, color, fmt, bounds, + nk_vec2(0,0), in, config->font); +} +NK_API struct nk_colorf +nk_color_picker(struct nk_context *ctx, struct nk_colorf color, + enum nk_color_format fmt) +{ + nk_color_pick(ctx, &color, fmt); + return color; +} + + + + + +/* ============================================================== + * + * COMBO + * + * ===============================================================*/ +NK_INTERN nk_bool +nk_combo_begin(struct nk_context *ctx, struct nk_window *win, + struct nk_vec2 size, nk_bool is_clicked, struct nk_rect header) +{ + struct nk_window *popup; + int is_open = 0; + int is_active = 0; + struct nk_rect body; + nk_hash hash; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + popup = win->popup.win; + body.x = header.x; + body.w = size.x; + body.y = header.y + header.h-ctx->style.window.combo_border; + body.h = size.y; + + hash = win->popup.combo_count++; + is_open = (popup) ? nk_true:nk_false; + is_active = (popup && (win->popup.name == hash) && win->popup.type == NK_PANEL_COMBO); + if ((is_clicked && is_open && !is_active) || (is_open && !is_active) || + (!is_open && !is_active && !is_clicked)) return 0; + if (!nk_nonblock_begin(ctx, 0, body, + (is_clicked && is_open)?nk_rect(0,0,0,0):header, NK_PANEL_COMBO)) return 0; + + win->popup.type = NK_PANEL_COMBO; + win->popup.name = hash; + return 1; +} +NK_API nk_bool +nk_combo_begin_text(struct nk_context *ctx, const char *selected, int len, + struct nk_vec2 size) +{ + const struct nk_input *in; + struct nk_window *win; + struct nk_style *style; + + enum nk_widget_layout_states s; + int is_clicked = nk_false; + struct nk_rect header; + const struct nk_style_item *background; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(selected); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !selected) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (s == NK_WIDGET_INVALID) + return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_clicked = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) { + background = &style->combo.active; + text.text = style->combo.label_active; + } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) { + background = &style->combo.hover; + text.text = style->combo.label_hover; + } else { + background = &style->combo.normal; + text.text = style->combo.label_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) { + text.background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image, nk_white); + } else { + text.background = background->data.color; + nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color); + nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color); + } + { + /* print currently selected text item */ + struct nk_rect label; + struct nk_rect button; + struct nk_rect content; + int draw_button_symbol; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + sym = style->combo.sym_hover; + else if (is_clicked) + sym = style->combo.sym_active; + else + sym = style->combo.sym_normal; + + /* represents whether or not the combo's button symbol should be drawn */ + draw_button_symbol = sym != NK_SYMBOL_NONE; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + + /* draw selected label */ + text.padding = nk_vec2(0,0); + label.x = header.x + style->combo.content_padding.x; + label.y = header.y + style->combo.content_padding.y; + label.h = header.h - 2 * style->combo.content_padding.y; + if (draw_button_symbol) + label.w = button.x - (style->combo.content_padding.x + style->combo.spacing.x) - label.x; + else + label.w = header.w - 2 * style->combo.content_padding.x; + nk_widget_text(&win->buffer, label, selected, len, &text, + NK_TEXT_LEFT, ctx->style.font); + + /* draw open/close button */ + if (draw_button_symbol) + nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, style->font); + } + return nk_combo_begin(ctx, win, size, is_clicked, header); +} +NK_API nk_bool +nk_combo_begin_label(struct nk_context *ctx, const char *selected, struct nk_vec2 size) +{ + return nk_combo_begin_text(ctx, selected, nk_strlen(selected), size); +} +NK_API nk_bool +nk_combo_begin_color(struct nk_context *ctx, struct nk_color color, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_style *style; + const struct nk_input *in; + + struct nk_rect header; + int is_clicked = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (s == NK_WIDGET_INVALID) + return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_clicked = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) + background = &style->combo.active; + else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + background = &style->combo.hover; + else background = &style->combo.normal; + + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(&win->buffer, header, &background->data.image,nk_white); + } else { + nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color); + nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color); + } + { + struct nk_rect content; + struct nk_rect button; + struct nk_rect bounds; + int draw_button_symbol; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + sym = style->combo.sym_hover; + else if (is_clicked) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* represents whether or not the combo's button symbol should be drawn */ + draw_button_symbol = sym != NK_SYMBOL_NONE; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + + /* draw color */ + bounds.h = header.h - 4 * style->combo.content_padding.y; + bounds.y = header.y + 2 * style->combo.content_padding.y; + bounds.x = header.x + 2 * style->combo.content_padding.x; + if (draw_button_symbol) + bounds.w = (button.x - (style->combo.content_padding.x + style->combo.spacing.x)) - bounds.x; + else + bounds.w = header.w - 4 * style->combo.content_padding.x; + nk_fill_rect(&win->buffer, bounds, 0, color); + + /* draw open/close button */ + if (draw_button_symbol) + nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, style->font); + } + return nk_combo_begin(ctx, win, size, is_clicked, header); +} +NK_API nk_bool +nk_combo_begin_symbol(struct nk_context *ctx, enum nk_symbol_type symbol, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_style *style; + const struct nk_input *in; + + struct nk_rect header; + int is_clicked = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + struct nk_color sym_background; + struct nk_color symbol_color; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (s == NK_WIDGET_INVALID) + return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_clicked = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) { + background = &style->combo.active; + symbol_color = style->combo.symbol_active; + } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) { + background = &style->combo.hover; + symbol_color = style->combo.symbol_hover; + } else { + background = &style->combo.normal; + symbol_color = style->combo.symbol_hover; + } + + if (background->type == NK_STYLE_ITEM_IMAGE) { + sym_background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image, nk_white); + } else { + sym_background = background->data.color; + nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color); + nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color); + } + { + struct nk_rect bounds = {0,0,0,0}; + struct nk_rect content; + struct nk_rect button; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + sym = style->combo.sym_hover; + else if (is_clicked) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.y; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + + /* draw symbol */ + bounds.h = header.h - 2 * style->combo.content_padding.y; + bounds.y = header.y + style->combo.content_padding.y; + bounds.x = header.x + style->combo.content_padding.x; + bounds.w = (button.x - style->combo.content_padding.y) - bounds.x; + nk_draw_symbol(&win->buffer, symbol, bounds, sym_background, symbol_color, + 1.0f, style->font); + + /* draw open/close button */ + nk_draw_button_symbol(&win->buffer, &bounds, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, style->font); + } + return nk_combo_begin(ctx, win, size, is_clicked, header); +} +NK_API nk_bool +nk_combo_begin_symbol_text(struct nk_context *ctx, const char *selected, int len, + enum nk_symbol_type symbol, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_style *style; + struct nk_input *in; + + struct nk_rect header; + int is_clicked = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + struct nk_color symbol_color; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (!s) return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_clicked = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) { + background = &style->combo.active; + symbol_color = style->combo.symbol_active; + text.text = style->combo.label_active; + } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) { + background = &style->combo.hover; + symbol_color = style->combo.symbol_hover; + text.text = style->combo.label_hover; + } else { + background = &style->combo.normal; + symbol_color = style->combo.symbol_normal; + text.text = style->combo.label_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) { + text.background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image, nk_white); + } else { + text.background = background->data.color; + nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color); + nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color); + } + { + struct nk_rect content; + struct nk_rect button; + struct nk_rect label; + struct nk_rect image; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + sym = style->combo.sym_hover; + else if (is_clicked) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, style->font); + + /* draw symbol */ + image.x = header.x + style->combo.content_padding.x; + image.y = header.y + style->combo.content_padding.y; + image.h = header.h - 2 * style->combo.content_padding.y; + image.w = image.h; + nk_draw_symbol(&win->buffer, symbol, image, text.background, symbol_color, + 1.0f, style->font); + + /* draw label */ + text.padding = nk_vec2(0,0); + label.x = image.x + image.w + style->combo.spacing.x + style->combo.content_padding.x; + label.y = header.y + style->combo.content_padding.y; + label.w = (button.x - style->combo.content_padding.x) - label.x; + label.h = header.h - 2 * style->combo.content_padding.y; + nk_widget_text(&win->buffer, label, selected, len, &text, NK_TEXT_LEFT, style->font); + } + return nk_combo_begin(ctx, win, size, is_clicked, header); +} +NK_API nk_bool +nk_combo_begin_image(struct nk_context *ctx, struct nk_image img, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_style *style; + const struct nk_input *in; + + struct nk_rect header; + int is_clicked = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (s == NK_WIDGET_INVALID) + return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_clicked = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) + background = &style->combo.active; + else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + background = &style->combo.hover; + else background = &style->combo.normal; + + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(&win->buffer, header, &background->data.image, nk_white); + } else { + nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color); + nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color); + } + { + struct nk_rect bounds = {0,0,0,0}; + struct nk_rect content; + struct nk_rect button; + int draw_button_symbol; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + sym = style->combo.sym_hover; + else if (is_clicked) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* represents whether or not the combo's button symbol should be drawn */ + draw_button_symbol = sym != NK_SYMBOL_NONE; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.y; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + + /* draw image */ + bounds.h = header.h - 2 * style->combo.content_padding.y; + bounds.y = header.y + style->combo.content_padding.y; + bounds.x = header.x + style->combo.content_padding.x; + if (draw_button_symbol) + bounds.w = (button.x - style->combo.content_padding.y) - bounds.x; + else + bounds.w = header.w - 2 * style->combo.content_padding.x; + nk_draw_image(&win->buffer, bounds, &img, nk_white); + + /* draw open/close button */ + if (draw_button_symbol) + nk_draw_button_symbol(&win->buffer, &bounds, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, style->font); + } + return nk_combo_begin(ctx, win, size, is_clicked, header); +} +NK_API nk_bool +nk_combo_begin_image_text(struct nk_context *ctx, const char *selected, int len, + struct nk_image img, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_style *style; + struct nk_input *in; + + struct nk_rect header; + int is_clicked = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (!s) return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_clicked = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) { + background = &style->combo.active; + text.text = style->combo.label_active; + } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) { + background = &style->combo.hover; + text.text = style->combo.label_hover; + } else { + background = &style->combo.normal; + text.text = style->combo.label_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) { + text.background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image, nk_white); + } else { + text.background = background->data.color; + nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color); + nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color); + } + { + struct nk_rect content; + struct nk_rect button; + struct nk_rect label; + struct nk_rect image; + int draw_button_symbol; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + sym = style->combo.sym_hover; + else if (is_clicked) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* represents whether or not the combo's button symbol should be drawn */ + draw_button_symbol = sym != NK_SYMBOL_NONE; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + if (draw_button_symbol) + nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, style->font); + + /* draw image */ + image.x = header.x + style->combo.content_padding.x; + image.y = header.y + style->combo.content_padding.y; + image.h = header.h - 2 * style->combo.content_padding.y; + image.w = image.h; + nk_draw_image(&win->buffer, image, &img, nk_white); + + /* draw label */ + text.padding = nk_vec2(0,0); + label.x = image.x + image.w + style->combo.spacing.x + style->combo.content_padding.x; + label.y = header.y + style->combo.content_padding.y; + label.h = header.h - 2 * style->combo.content_padding.y; + if (draw_button_symbol) + label.w = (button.x - style->combo.content_padding.x) - label.x; + else + label.w = (header.x + header.w - style->combo.content_padding.x) - label.x; + nk_widget_text(&win->buffer, label, selected, len, &text, NK_TEXT_LEFT, style->font); + } + return nk_combo_begin(ctx, win, size, is_clicked, header); +} +NK_API nk_bool +nk_combo_begin_symbol_label(struct nk_context *ctx, + const char *selected, enum nk_symbol_type type, struct nk_vec2 size) +{ + return nk_combo_begin_symbol_text(ctx, selected, nk_strlen(selected), type, size); +} +NK_API nk_bool +nk_combo_begin_image_label(struct nk_context *ctx, + const char *selected, struct nk_image img, struct nk_vec2 size) +{ + return nk_combo_begin_image_text(ctx, selected, nk_strlen(selected), img, size); +} +NK_API nk_bool +nk_combo_item_text(struct nk_context *ctx, const char *text, int len,nk_flags align) +{ + return nk_contextual_item_text(ctx, text, len, align); +} +NK_API nk_bool +nk_combo_item_label(struct nk_context *ctx, const char *label, nk_flags align) +{ + return nk_contextual_item_label(ctx, label, align); +} +NK_API nk_bool +nk_combo_item_image_text(struct nk_context *ctx, struct nk_image img, const char *text, + int len, nk_flags alignment) +{ + return nk_contextual_item_image_text(ctx, img, text, len, alignment); +} +NK_API nk_bool +nk_combo_item_image_label(struct nk_context *ctx, struct nk_image img, + const char *text, nk_flags alignment) +{ + return nk_contextual_item_image_label(ctx, img, text, alignment); +} +NK_API nk_bool +nk_combo_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, + const char *text, int len, nk_flags alignment) +{ + return nk_contextual_item_symbol_text(ctx, sym, text, len, alignment); +} +NK_API nk_bool +nk_combo_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, + const char *label, nk_flags alignment) +{ + return nk_contextual_item_symbol_label(ctx, sym, label, alignment); +} +NK_API void nk_combo_end(struct nk_context *ctx) +{ + nk_contextual_end(ctx); +} +NK_API void nk_combo_close(struct nk_context *ctx) +{ + nk_contextual_close(ctx); +} +NK_API int +nk_combo(struct nk_context *ctx, const char **items, int count, + int selected, int item_height, struct nk_vec2 size) +{ + int i = 0; + int max_height; + struct nk_vec2 item_spacing; + struct nk_vec2 window_padding; + + NK_ASSERT(ctx); + NK_ASSERT(items); + NK_ASSERT(ctx->current); + if (!ctx || !items ||!count) + return selected; + + item_spacing = ctx->style.window.spacing; + window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type); + max_height = count * item_height + count * (int)item_spacing.y; + max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2; + size.y = NK_MIN(size.y, (float)max_height); + if (nk_combo_begin_label(ctx, items[selected], size)) { + nk_layout_row_dynamic(ctx, (float)item_height, 1); + for (i = 0; i < count; ++i) { + if (nk_combo_item_label(ctx, items[i], NK_TEXT_LEFT)) + selected = i; + } + nk_combo_end(ctx); + } + return selected; +} +NK_API int +nk_combo_separator(struct nk_context *ctx, const char *items_separated_by_separator, + int separator, int selected, int count, int item_height, struct nk_vec2 size) +{ + int i; + int max_height; + struct nk_vec2 item_spacing; + struct nk_vec2 window_padding; + const char *current_item; + const char *iter; + int length = 0; + + NK_ASSERT(ctx); + NK_ASSERT(items_separated_by_separator); + if (!ctx || !items_separated_by_separator) + return selected; + + /* calculate popup window */ + item_spacing = ctx->style.window.spacing; + window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type); + max_height = count * item_height + count * (int)item_spacing.y; + max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2; + size.y = NK_MIN(size.y, (float)max_height); + + /* find selected item */ + current_item = items_separated_by_separator; + for (i = 0; i < count; ++i) { + iter = current_item; + while (*iter && *iter != separator) iter++; + length = (int)(iter - current_item); + if (i == selected) break; + current_item = iter + 1; + } + + if (nk_combo_begin_text(ctx, current_item, length, size)) { + current_item = items_separated_by_separator; + nk_layout_row_dynamic(ctx, (float)item_height, 1); + for (i = 0; i < count; ++i) { + iter = current_item; + while (*iter && *iter != separator) iter++; + length = (int)(iter - current_item); + if (nk_combo_item_text(ctx, current_item, length, NK_TEXT_LEFT)) + selected = i; + current_item = current_item + length + 1; + } + nk_combo_end(ctx); + } + return selected; +} +NK_API int +nk_combo_string(struct nk_context *ctx, const char *items_separated_by_zeros, + int selected, int count, int item_height, struct nk_vec2 size) +{ + return nk_combo_separator(ctx, items_separated_by_zeros, '\0', selected, count, item_height, size); +} +NK_API int +nk_combo_callback(struct nk_context *ctx, void(*item_getter)(void*, int, const char**), + void *userdata, int selected, int count, int item_height, struct nk_vec2 size) +{ + int i; + int max_height; + struct nk_vec2 item_spacing; + struct nk_vec2 window_padding; + const char *item; + + NK_ASSERT(ctx); + NK_ASSERT(item_getter); + if (!ctx || !item_getter) + return selected; + + /* calculate popup window */ + item_spacing = ctx->style.window.spacing; + window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type); + max_height = count * item_height + count * (int)item_spacing.y; + max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2; + size.y = NK_MIN(size.y, (float)max_height); + + item_getter(userdata, selected, &item); + if (nk_combo_begin_label(ctx, item, size)) { + nk_layout_row_dynamic(ctx, (float)item_height, 1); + for (i = 0; i < count; ++i) { + item_getter(userdata, i, &item); + if (nk_combo_item_label(ctx, item, NK_TEXT_LEFT)) + selected = i; + } + nk_combo_end(ctx); + } return selected; +} +NK_API void +nk_combobox(struct nk_context *ctx, const char **items, int count, + int *selected, int item_height, struct nk_vec2 size) +{ + *selected = nk_combo(ctx, items, count, *selected, item_height, size); +} +NK_API void +nk_combobox_string(struct nk_context *ctx, const char *items_separated_by_zeros, + int *selected, int count, int item_height, struct nk_vec2 size) +{ + *selected = nk_combo_string(ctx, items_separated_by_zeros, *selected, count, item_height, size); +} +NK_API void +nk_combobox_separator(struct nk_context *ctx, const char *items_separated_by_separator, + int separator, int *selected, int count, int item_height, struct nk_vec2 size) +{ + *selected = nk_combo_separator(ctx, items_separated_by_separator, separator, + *selected, count, item_height, size); +} +NK_API void +nk_combobox_callback(struct nk_context *ctx, + void(*item_getter)(void* data, int id, const char **out_text), + void *userdata, int *selected, int count, int item_height, struct nk_vec2 size) +{ + *selected = nk_combo_callback(ctx, item_getter, userdata, *selected, count, item_height, size); +} + + + + + +/* =============================================================== + * + * TOOLTIP + * + * ===============================================================*/ +NK_API nk_bool +nk_tooltip_begin(struct nk_context *ctx, float width) +{ + int x,y,w,h; + struct nk_window *win; + const struct nk_input *in; + struct nk_rect bounds; + int ret; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + /* make sure that no nonblocking popup is currently active */ + win = ctx->current; + in = &ctx->input; + if (win->popup.win && (win->popup.type & NK_PANEL_SET_NONBLOCK)) + return 0; + + w = nk_iceilf(width); + h = nk_iceilf(nk_null_rect.h); + x = nk_ifloorf(in->mouse.pos.x + 1) - (int)win->layout->clip.x; + y = nk_ifloorf(in->mouse.pos.y + 1) - (int)win->layout->clip.y; + + bounds.x = (float)x; + bounds.y = (float)y; + bounds.w = (float)w; + bounds.h = (float)h; + + ret = nk_popup_begin(ctx, NK_POPUP_DYNAMIC, + "__##Tooltip##__", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER, bounds); + if (ret) win->layout->flags &= ~(nk_flags)NK_WINDOW_ROM; + win->popup.type = NK_PANEL_TOOLTIP; + ctx->current->layout->type = NK_PANEL_TOOLTIP; + return ret; +} + +NK_API void +nk_tooltip_end(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + ctx->current->seq--; + nk_popup_close(ctx); + nk_popup_end(ctx); +} +NK_API void +nk_tooltip(struct nk_context *ctx, const char *text) +{ + const struct nk_style *style; + struct nk_vec2 padding; + + int text_len; + float text_width; + float text_height; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + NK_ASSERT(text); + if (!ctx || !ctx->current || !ctx->current->layout || !text) + return; + + /* fetch configuration data */ + style = &ctx->style; + padding = style->window.padding; + + /* calculate size of the text and tooltip */ + text_len = nk_strlen(text); + text_width = style->font->width(style->font->userdata, + style->font->height, text, text_len); + text_width += (4 * padding.x); + text_height = (style->font->height + 2 * padding.y); + + /* execute tooltip and fill with text */ + if (nk_tooltip_begin(ctx, (float)text_width)) { + nk_layout_row_dynamic(ctx, (float)text_height, 1); + nk_text(ctx, text, text_len, NK_TEXT_LEFT); + nk_tooltip_end(ctx); + } +} +#ifdef NK_INCLUDE_STANDARD_VARARGS +NK_API void +nk_tooltipf(struct nk_context *ctx, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + nk_tooltipfv(ctx, fmt, args); + va_end(args); +} +NK_API void +nk_tooltipfv(struct nk_context *ctx, const char *fmt, va_list args) +{ + char buf[256]; + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_tooltip(ctx, buf); +} +#endif + + + +#endif /* NK_IMPLEMENTATION */ + +/* +/// ## License +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~none +/// ------------------------------------------------------------------------------ +/// This software is available under 2 licenses -- choose whichever you prefer. +/// ------------------------------------------------------------------------------ +/// ALTERNATIVE A - MIT License +/// Copyright (c) 2016-2018 Micha Mettke +/// Permission is hereby granted, free of charge, to any person obtaining a copy of +/// this software and associated documentation files (the "Software"), to deal in +/// the Software without restriction, including without limitation the rights to +/// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +/// of the Software, and to permit persons to whom the Software is furnished to do +/// so, subject to the following conditions: +/// The above copyright notice and this permission notice shall be included in all +/// copies or substantial portions of the Software. +/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +/// SOFTWARE. +/// ------------------------------------------------------------------------------ +/// ALTERNATIVE B - Public Domain (www.unlicense.org) +/// This is free and unencumbered software released into the public domain. +/// Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +/// software, either in source code form or as a compiled binary, for any purpose, +/// commercial or non-commercial, and by any means. +/// In jurisdictions that recognize copyright laws, the author or authors of this +/// software dedicate any and all copyright interest in the software to the public +/// domain. We make this dedication for the benefit of the public at large and to +/// the detriment of our heirs and successors. We intend this dedication to be an +/// overt act of relinquishment in perpetuity of all present and future rights to +/// this software under copyright law. +/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +/// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +/// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +/// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/// ------------------------------------------------------------------------------ +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +/// ## Changelog +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~none +/// [date][x.yy.zz]-[description] +/// -[date]: date on which the change has been pushed +/// -[x.yy.zz]: Numerical version string representation. Each version number on the right +/// resets back to zero if version on the left is incremented. +/// - [x]: Major version with API and library breaking changes +/// - [yy]: Minor version with non-breaking API and library changes +/// - [zz]: Bug fix version with no direct changes to API +/// +/// - 2020/10/11 (4.06.1) - Fix C++ style comments which are not allowed in ISO C90. +/// - 2020/10/07 (4.06.0) - Fix nk_combo return type wrongly changed to nk_bool +/// - 2020/09/05 (4.05.0) - Use the nk_font_atlas allocator for stb_truetype memory management. +/// - 2020/09/04 (4.04.1) - Replace every boolean int by nk_bool +/// - 2020/09/04 (4.04.0) - Add nk_bool with NK_INCLUDE_STANDARD_BOOL +/// - 2020/06/13 (4.03.1) - Fix nk_pool allocation sizes. +/// - 2020/06/04 (4.03.0) - Made nk_combo header symbols optional. +/// - 2020/05/27 (4.02.5) - Fix nk_do_edit: Keep scroll position when re-activating edit widget. +/// - 2020/05/09 (4.02.4) - Fix nk_menubar height calculation bug +/// - 2020/05/08 (4.02.3) - Fix missing stdarg.h with NK_INCLUDE_STANDARD_VARARGS +/// - 2020/04/30 (4.02.2) - Fix nk_edit border drawing bug +/// - 2020/04/09 (4.02.1) - Removed unused nk_sqrt function to fix compiler warnings +/// - Fixed compiler warnings if you bring your own methods for +/// nk_cos/nk_sin/nk_strtod/nk_memset/nk_memcopy/nk_dtoa +/// - 2020/04/06 (4.01.10) - Fix bug: Do not use pool before checking for NULL +/// - 2020/03/22 (4.01.9) - Fix bug where layout state wasn't restored correctly after +/// popping a tree. +/// - 2020/03/11 (4.01.8) - Fix bug where padding is subtracted from widget +/// - 2020/03/06 (4.01.7) - Fix bug where width padding was applied twice +/// - 2020/02/06 (4.01.6) - Update stb_truetype.h and stb_rect_pack.h and separate them +/// - 2019/12/10 (4.01.5) - Fix off-by-one error in NK_INTERSECT +/// - 2019/10/09 (4.01.4) - Fix bug for autoscrolling in nk_do_edit +/// - 2019/09/20 (4.01.3) - Fixed a bug wherein combobox cannot be closed by clicking the header +/// when NK_BUTTON_TRIGGER_ON_RELEASE is defined. +/// - 2019/09/10 (4.01.2) - Fixed the nk_cos function, which deviated significantly. +/// - 2019/09/08 (4.01.1) - Fixed a bug wherein re-baking of fonts caused a segmentation +/// fault due to dst_font->glyph_count not being zeroed on subsequent +/// bakes of the same set of fonts. +/// - 2019/06/23 (4.01.0) - Added nk_***_get_scroll and nk_***_set_scroll for groups, windows, and popups. +/// - 2019/06/12 (4.00.3) - Fix panel background drawing bug. +/// - 2018/10/31 (4.00.2) - Added NK_KEYSTATE_BASED_INPUT to "fix" state based backends +/// like GLFW without breaking key repeat behavior on event based. +/// - 2018/04/01 (4.00.1) - Fixed calling `nk_convert` multiple time per single frame. +/// - 2018/04/01 (4.00.0) - BREAKING CHANGE: nk_draw_list_clear no longer tries to +/// clear provided buffers. So make sure to either free +/// or clear each passed buffer after calling nk_convert. +/// - 2018/02/23 (3.00.6) - Fixed slider dragging behavior. +/// - 2018/01/31 (3.00.5) - Fixed overcalculation of cursor data in font baking process. +/// - 2018/01/31 (3.00.4) - Removed name collision with stb_truetype. +/// - 2018/01/28 (3.00.3) - Fixed panel window border drawing bug. +/// - 2018/01/12 (3.00.2) - Added `nk_group_begin_titled` for separed group identifier and title. +/// - 2018/01/07 (3.00.1) - Started to change documentation style. +/// - 2018/01/05 (3.00.0) - BREAKING CHANGE: The previous color picker API was broken +/// because of conversions between float and byte color representation. +/// Color pickers now use floating point values to represent +/// HSV values. To get back the old behavior I added some additional +/// color conversion functions to cast between nk_color and +/// nk_colorf. +/// - 2017/12/23 (2.00.7) - Fixed small warning. +/// - 2017/12/23 (2.00.7) - Fixed `nk_edit_buffer` behavior if activated to allow input. +/// - 2017/12/23 (2.00.7) - Fixed modifyable progressbar dragging visuals and input behavior. +/// - 2017/12/04 (2.00.6) - Added formated string tooltip widget. +/// - 2017/11/18 (2.00.5) - Fixed window becoming hidden with flag `NK_WINDOW_NO_INPUT`. +/// - 2017/11/15 (2.00.4) - Fixed font merging. +/// - 2017/11/07 (2.00.3) - Fixed window size and position modifier functions. +/// - 2017/09/14 (2.00.2) - Fixed `nk_edit_buffer` and `nk_edit_focus` behavior. +/// - 2017/09/14 (2.00.1) - Fixed window closing behavior. +/// - 2017/09/14 (2.00.0) - BREAKING CHANGE: Modifing window position and size funtions now +/// require the name of the window and must happen outside the window +/// building process (between function call nk_begin and nk_end). +/// - 2017/09/11 (1.40.9) - Fixed window background flag if background window is declared last. +/// - 2017/08/27 (1.40.8) - Fixed `nk_item_is_any_active` for hidden windows. +/// - 2017/08/27 (1.40.7) - Fixed window background flag. +/// - 2017/07/07 (1.40.6) - Fixed missing clipping rect check for hovering/clicked +/// query for widgets. +/// - 2017/07/07 (1.40.5) - Fixed drawing bug for vertex output for lines and stroked +/// and filled rectangles. +/// - 2017/07/07 (1.40.4) - Fixed bug in nk_convert trying to add windows that are in +/// process of being destroyed. +/// - 2017/07/07 (1.40.3) - Fixed table internal bug caused by storing table size in +/// window instead of directly in table. +/// - 2017/06/30 (1.40.2) - Removed unneeded semicolon in C++ NK_ALIGNOF macro. +/// - 2017/06/30 (1.40.1) - Fixed drawing lines smaller or equal zero. +/// - 2017/06/08 (1.40.0) - Removed the breaking part of last commit. Auto layout now only +/// comes in effect if you pass in zero was row height argument. +/// - 2017/06/08 (1.40.0) - BREAKING CHANGE: while not directly API breaking it will change +/// how layouting works. From now there will be an internal minimum +/// row height derived from font height. If you need a row smaller than +/// that you can directly set it by `nk_layout_set_min_row_height` and +/// reset the value back by calling `nk_layout_reset_min_row_height. +/// - 2017/06/08 (1.39.1) - Fixed property text edit handling bug caused by past `nk_widget` fix. +/// - 2017/06/08 (1.39.0) - Added function to retrieve window space without calling a `nk_layout_xxx` function. +/// - 2017/06/06 (1.38.5) - Fixed `nk_convert` return flag for command buffer. +/// - 2017/05/23 (1.38.4) - Fixed activation behavior for widgets partially clipped. +/// - 2017/05/10 (1.38.3) - Fixed wrong min window size mouse scaling over boundries. +/// - 2017/05/09 (1.38.2) - Fixed vertical scrollbar drawing with not enough space. +/// - 2017/05/09 (1.38.1) - Fixed scaler dragging behavior if window size hits minimum size. +/// - 2017/05/06 (1.38.0) - Added platform double-click support. +/// - 2017/04/20 (1.37.1) - Fixed key repeat found inside glfw demo backends. +/// - 2017/04/20 (1.37.0) - Extended properties with selection and clipboard support. +/// - 2017/04/20 (1.36.2) - Fixed #405 overlapping rows with zero padding and spacing. +/// - 2017/04/09 (1.36.1) - Fixed #403 with another widget float error. +/// - 2017/04/09 (1.36.0) - Added window `NK_WINDOW_NO_INPUT` and `NK_WINDOW_NOT_INTERACTIVE` flags. +/// - 2017/04/09 (1.35.3) - Fixed buffer heap corruption. +/// - 2017/03/25 (1.35.2) - Fixed popup overlapping for `NK_WINDOW_BACKGROUND` windows. +/// - 2017/03/25 (1.35.1) - Fixed windows closing behavior. +/// - 2017/03/18 (1.35.0) - Added horizontal scroll requested in #377. +/// - 2017/03/18 (1.34.3) - Fixed long window header titles. +/// - 2017/03/04 (1.34.2) - Fixed text edit filtering. +/// - 2017/03/04 (1.34.1) - Fixed group closable flag. +/// - 2017/02/25 (1.34.0) - Added custom draw command for better language binding support. +/// - 2017/01/24 (1.33.0) - Added programatic way of remove edit focus. +/// - 2017/01/24 (1.32.3) - Fixed wrong define for basic type definitions for windows. +/// - 2017/01/21 (1.32.2) - Fixed input capture from hidden or closed windows. +/// - 2017/01/21 (1.32.1) - Fixed slider behavior and drawing. +/// - 2017/01/13 (1.32.0) - Added flag to put scaler into the bottom left corner. +/// - 2017/01/13 (1.31.0) - Added additional row layouting method to combine both +/// dynamic and static widgets. +/// - 2016/12/31 (1.30.0) - Extended scrollbar offset from 16-bit to 32-bit. +/// - 2016/12/31 (1.29.2) - Fixed closing window bug of minimized windows. +/// - 2016/12/03 (1.29.1) - Fixed wrapped text with no seperator and C89 error. +/// - 2016/12/03 (1.29.0) - Changed text wrapping to process words not characters. +/// - 2016/11/22 (1.28.6) - Fixed window minimized closing bug. +/// - 2016/11/19 (1.28.5) - Fixed abstract combo box closing behavior. +/// - 2016/11/19 (1.28.4) - Fixed tooltip flickering. +/// - 2016/11/19 (1.28.3) - Fixed memory leak caused by popup repeated closing. +/// - 2016/11/18 (1.28.2) - Fixed memory leak caused by popup panel allocation. +/// - 2016/11/10 (1.28.1) - Fixed some warnings and C++ error. +/// - 2016/11/10 (1.28.0) - Added additional `nk_button` versions which allows to directly +/// pass in a style struct to change buttons visual. +/// - 2016/11/10 (1.27.0) - Added additional `nk_tree` versions to support external state +/// storage. Just like last the `nk_group` commit the main +/// advantage is that you optionally can minimize nuklears runtime +/// memory consumption or handle hash collisions. +/// - 2016/11/09 (1.26.0) - Added additional `nk_group` version to support external scrollbar +/// offset storage. Main advantage is that you can externalize +/// the memory management for the offset. It could also be helpful +/// if you have a hash collision in `nk_group_begin` but really +/// want the name. In addition I added `nk_list_view` which allows +/// to draw big lists inside a group without actually having to +/// commit the whole list to nuklear (issue #269). +/// - 2016/10/30 (1.25.1) - Fixed clipping rectangle bug inside `nk_draw_list`. +/// - 2016/10/29 (1.25.0) - Pulled `nk_panel` memory management into nuklear and out of +/// the hands of the user. From now on users don't have to care +/// about panels unless they care about some information. If you +/// still need the panel just call `nk_window_get_panel`. +/// - 2016/10/21 (1.24.0) - Changed widget border drawing to stroked rectangle from filled +/// rectangle for less overdraw and widget background transparency. +/// - 2016/10/18 (1.23.0) - Added `nk_edit_focus` for manually edit widget focus control. +/// - 2016/09/29 (1.22.7) - Fixed deduction of basic type in non `` compilation. +/// - 2016/09/29 (1.22.6) - Fixed edit widget UTF-8 text cursor drawing bug. +/// - 2016/09/28 (1.22.5) - Fixed edit widget UTF-8 text appending/inserting/removing. +/// - 2016/09/28 (1.22.4) - Fixed drawing bug inside edit widgets which offset all text +/// text in every edit widget if one of them is scrolled. +/// - 2016/09/28 (1.22.3) - Fixed small bug in edit widgets if not active. The wrong +/// text length is passed. It should have been in bytes but +/// was passed as glyphes. +/// - 2016/09/20 (1.22.2) - Fixed color button size calculation. +/// - 2016/09/20 (1.22.1) - Fixed some `nk_vsnprintf` behavior bugs and removed `` +/// again from `NK_INCLUDE_STANDARD_VARARGS`. +/// - 2016/09/18 (1.22.0) - C89 does not support vsnprintf only C99 and newer as well +/// as C++11 and newer. In addition to use vsnprintf you have +/// to include . So just defining `NK_INCLUDE_STD_VAR_ARGS` +/// is not enough. That behavior is now fixed. By default if +/// both varargs as well as stdio is selected I try to use +/// vsnprintf if not possible I will revert to vsprintf. If +/// varargs but not stdio was defined I will use my own function. +/// - 2016/09/15 (1.21.2) - Fixed panel `close` behavior for deeper panel levels. +/// - 2016/09/15 (1.21.1) - Fixed C++ errors and wrong argument to `nk_panel_get_xxxx`. +/// - 2016/09/13 (1.21.0) - !BREAKING! Fixed nonblocking popup behavior in menu, combo, +/// and contextual which prevented closing in y-direction if +/// popup did not reach max height. +/// In addition the height parameter was changed into vec2 +/// for width and height to have more control over the popup size. +/// - 2016/09/13 (1.20.3) - Cleaned up and extended type selection. +/// - 2016/09/13 (1.20.2) - Fixed slider behavior hopefully for the last time. This time +/// all calculation are correct so no more hackery. +/// - 2016/09/13 (1.20.1) - Internal change to divide window/panel flags into panel flags and types. +/// Suprisinly spend years in C and still happened to confuse types +/// with flags. Probably something to take note. +/// - 2016/09/08 (1.20.0) - Added additional helper function to make it easier to just +/// take the produced buffers from `nk_convert` and unplug the +/// iteration process from `nk_context`. So now you can +/// just use the vertex,element and command buffer + two pointer +/// inside the command buffer retrieved by calls `nk__draw_begin` +/// and `nk__draw_end` and macro `nk_draw_foreach_bounded`. +/// - 2016/09/08 (1.19.0) - Added additional asserts to make sure every `nk_xxx_begin` call +/// for windows, popups, combobox, menu and contextual is guarded by +/// `if` condition and does not produce false drawing output. +/// - 2016/09/08 (1.18.0) - Changed confusing name for `NK_SYMBOL_RECT_FILLED`, `NK_SYMBOL_RECT` +/// to hopefully easier to understand `NK_SYMBOL_RECT_FILLED` and +/// `NK_SYMBOL_RECT_OUTLINE`. +/// - 2016/09/08 (1.17.0) - Changed confusing name for `NK_SYMBOL_CIRLCE_FILLED`, `NK_SYMBOL_CIRCLE` +/// to hopefully easier to understand `NK_SYMBOL_CIRCLE_FILLED` and +/// `NK_SYMBOL_CIRCLE_OUTLINE`. +/// - 2016/09/08 (1.16.0) - Added additional checks to select correct types if `NK_INCLUDE_FIXED_TYPES` +/// is not defined by supporting the biggest compiler GCC, clang and MSVC. +/// - 2016/09/07 (1.15.3) - Fixed `NK_INCLUDE_COMMAND_USERDATA` define to not cause an error. +/// - 2016/09/04 (1.15.2) - Fixed wrong combobox height calculation. +/// - 2016/09/03 (1.15.1) - Fixed gaps inside combo boxes in OpenGL. +/// - 2016/09/02 (1.15.0) - Changed nuklear to not have any default vertex layout and +/// instead made it user provided. The range of types to convert +/// to is quite limited at the moment, but I would be more than +/// happy to accept PRs to add additional. +/// - 2016/08/30 (1.14.2) - Removed unused variables. +/// - 2016/08/30 (1.14.1) - Fixed C++ build errors. +/// - 2016/08/30 (1.14.0) - Removed mouse dragging from SDL demo since it does not work correctly. +/// - 2016/08/30 (1.13.4) - Tweaked some default styling variables. +/// - 2016/08/30 (1.13.3) - Hopefully fixed drawing bug in slider, in general I would +/// refrain from using slider with a big number of steps. +/// - 2016/08/30 (1.13.2) - Fixed close and minimize button which would fire even if the +/// window was in Read Only Mode. +/// - 2016/08/30 (1.13.1) - Fixed popup panel padding handling which was previously just +/// a hack for combo box and menu. +/// - 2016/08/30 (1.13.0) - Removed `NK_WINDOW_DYNAMIC` flag from public API since +/// it is bugged and causes issues in window selection. +/// - 2016/08/30 (1.12.0) - Removed scaler size. The size of the scaler is now +/// determined by the scrollbar size. +/// - 2016/08/30 (1.11.2) - Fixed some drawing bugs caused by changes from 1.11.0. +/// - 2016/08/30 (1.11.1) - Fixed overlapping minimized window selection. +/// - 2016/08/30 (1.11.0) - Removed some internal complexity and overly complex code +/// handling panel padding and panel border. +/// - 2016/08/29 (1.10.0) - Added additional height parameter to `nk_combobox_xxx`. +/// - 2016/08/29 (1.10.0) - Fixed drawing bug in dynamic popups. +/// - 2016/08/29 (1.10.0) - Added experimental mouse scrolling to popups, menus and comboboxes. +/// - 2016/08/26 (1.10.0) - Added window name string prepresentation to account for +/// hash collisions. Currently limited to `NK_WINDOW_MAX_NAME` +/// which in term can be redefined if not big enough. +/// - 2016/08/26 (1.10.0) - Added stacks for temporary style/UI changes in code. +/// - 2016/08/25 (1.10.0) - Changed `nk_input_is_key_pressed` and 'nk_input_is_key_released' +/// to account for key press and release happening in one frame. +/// - 2016/08/25 (1.10.0) - Added additional nk_edit flag to directly jump to the end on activate. +/// - 2016/08/17 (1.09.6) - Removed invalid check for value zero in `nk_propertyx`. +/// - 2016/08/16 (1.09.5) - Fixed ROM mode for deeper levels of popup windows parents. +/// - 2016/08/15 (1.09.4) - Editbox are now still active if enter was pressed with flag +/// `NK_EDIT_SIG_ENTER`. Main reasoning is to be able to keep +/// typing after commiting. +/// - 2016/08/15 (1.09.4) - Removed redundant code. +/// - 2016/08/15 (1.09.4) - Fixed negative numbers in `nk_strtoi` and remove unused variable. +/// - 2016/08/15 (1.09.3) - Fixed `NK_WINDOW_BACKGROUND` flag behavior to select a background +/// window only as selected by hovering and not by clicking. +/// - 2016/08/14 (1.09.2) - Fixed a bug in font atlas which caused wrong loading +/// of glyphes for font with multiple ranges. +/// - 2016/08/12 (1.09.1) - Added additional function to check if window is currently +/// hidden and therefore not visible. +/// - 2016/08/12 (1.09.1) - nk_window_is_closed now queries the correct flag `NK_WINDOW_CLOSED` +/// instead of the old flag `NK_WINDOW_HIDDEN`. +/// - 2016/08/09 (1.09.0) - Added additional double version to nk_property and changed +/// the underlying implementation to not cast to float and instead +/// work directly on the given values. +/// - 2016/08/09 (1.08.0) - Added additional define to overwrite library internal +/// floating pointer number to string conversion for additional +/// precision. +/// - 2016/08/09 (1.08.0) - Added additional define to overwrite library internal +/// string to floating point number conversion for additional +/// precision. +/// - 2016/08/08 (1.07.2) - Fixed compiling error without define `NK_INCLUDE_FIXED_TYPE`. +/// - 2016/08/08 (1.07.1) - Fixed possible floating point error inside `nk_widget` leading +/// to wrong wiget width calculation which results in widgets falsly +/// becomming tagged as not inside window and cannot be accessed. +/// - 2016/08/08 (1.07.0) - Nuklear now differentiates between hiding a window (NK_WINDOW_HIDDEN) and +/// closing a window (NK_WINDOW_CLOSED). A window can be hidden/shown +/// by using `nk_window_show` and closed by either clicking the close +/// icon in a window or by calling `nk_window_close`. Only closed +/// windows get removed at the end of the frame while hidden windows +/// remain. +/// - 2016/08/08 (1.06.0) - Added `nk_edit_string_zero_terminated` as a second option to +/// `nk_edit_string` which takes, edits and outputs a '\0' terminated string. +/// - 2016/08/08 (1.05.4) - Fixed scrollbar auto hiding behavior. +/// - 2016/08/08 (1.05.3) - Fixed wrong panel padding selection in `nk_layout_widget_space`. +/// - 2016/08/07 (1.05.2) - Fixed old bug in dynamic immediate mode layout API, calculating +/// wrong item spacing and panel width. +/// - 2016/08/07 (1.05.1) - Hopefully finally fixed combobox popup drawing bug. +/// - 2016/08/07 (1.05.0) - Split varargs away from `NK_INCLUDE_STANDARD_IO` into own +/// define `NK_INCLUDE_STANDARD_VARARGS` to allow more fine +/// grained controlled over library includes. +/// - 2016/08/06 (1.04.5) - Changed memset calls to `NK_MEMSET`. +/// - 2016/08/04 (1.04.4) - Fixed fast window scaling behavior. +/// - 2016/08/04 (1.04.3) - Fixed window scaling, movement bug which appears if you +/// move/scale a window and another window is behind it. +/// If you are fast enough then the window behind gets activated +/// and the operation is blocked. I now require activating +/// by hovering only if mouse is not pressed. +/// - 2016/08/04 (1.04.2) - Fixed changing fonts. +/// - 2016/08/03 (1.04.1) - Fixed `NK_WINDOW_BACKGROUND` behavior. +/// - 2016/08/03 (1.04.0) - Added color parameter to `nk_draw_image`. +/// - 2016/08/03 (1.04.0) - Added additional window padding style attributes for +/// sub windows (combo, menu, ...). +/// - 2016/08/03 (1.04.0) - Added functions to show/hide software cursor. +/// - 2016/08/03 (1.04.0) - Added `NK_WINDOW_BACKGROUND` flag to force a window +/// to be always in the background of the screen. +/// - 2016/08/03 (1.03.2) - Removed invalid assert macro for NK_RGB color picker. +/// - 2016/08/01 (1.03.1) - Added helper macros into header include guard. +/// - 2016/07/29 (1.03.0) - Moved the window/table pool into the header part to +/// simplify memory management by removing the need to +/// allocate the pool. +/// - 2016/07/29 (1.02.0) - Added auto scrollbar hiding window flag which if enabled +/// will hide the window scrollbar after NK_SCROLLBAR_HIDING_TIMEOUT +/// seconds without window interaction. To make it work +/// you have to also set a delta time inside the `nk_context`. +/// - 2016/07/25 (1.01.1) - Fixed small panel and panel border drawing bugs. +/// - 2016/07/15 (1.01.0) - Added software cursor to `nk_style` and `nk_context`. +/// - 2016/07/15 (1.01.0) - Added const correctness to `nk_buffer_push' data argument. +/// - 2016/07/15 (1.01.0) - Removed internal font baking API and simplified +/// font atlas memory management by converting pointer +/// arrays for fonts and font configurations to lists. +/// - 2016/07/15 (1.00.0) - Changed button API to use context dependend button +/// behavior instead of passing it for every function call. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// ## Gallery +/// ![Figure [blue]: Feature overview with blue color styling](https://cloud.githubusercontent.com/assets/8057201/13538240/acd96876-e249-11e5-9547-5ac0b19667a0.png) +/// ![Figure [red]: Feature overview with red color styling](https://cloud.githubusercontent.com/assets/8057201/13538243/b04acd4c-e249-11e5-8fd2-ad7744a5b446.png) +/// ![Figure [widgets]: Widget overview](https://cloud.githubusercontent.com/assets/8057201/11282359/3325e3c6-8eff-11e5-86cb-cf02b0596087.png) +/// ![Figure [blackwhite]: Black and white](https://cloud.githubusercontent.com/assets/8057201/11033668/59ab5d04-86e5-11e5-8091-c56f16411565.png) +/// ![Figure [filexp]: File explorer](https://cloud.githubusercontent.com/assets/8057201/10718115/02a9ba08-7b6b-11e5-950f-adacdd637739.png) +/// ![Figure [opengl]: OpenGL Editor](https://cloud.githubusercontent.com/assets/8057201/12779619/2a20d72c-ca69-11e5-95fe-4edecf820d5c.png) +/// ![Figure [nodedit]: Node Editor](https://cloud.githubusercontent.com/assets/8057201/9976995/e81ac04a-5ef7-11e5-872b-acd54fbeee03.gif) +/// ![Figure [skinning]: Using skinning in Nuklear](https://cloud.githubusercontent.com/assets/8057201/15991632/76494854-30b8-11e6-9555-a69840d0d50b.png) +/// ![Figure [bf]: Heavy modified version](https://cloud.githubusercontent.com/assets/8057201/14902576/339926a8-0d9c-11e6-9fee-a8b73af04473.png) +/// +/// ## Credits +/// Developed by Micha Mettke and every direct or indirect github contributor.

+/// +/// Embeds [stb_texedit](https://github.com/nothings/stb/blob/master/stb_textedit.h), [stb_truetype](https://github.com/nothings/stb/blob/master/stb_truetype.h) and [stb_rectpack](https://github.com/nothings/stb/blob/master/stb_rect_pack.h) by Sean Barret (public domain)
+/// Uses [stddoc.c](https://github.com/r-lyeh/stddoc.c) from r-lyeh@github.com for documentation generation

+/// Embeds ProggyClean.ttf font by Tristan Grimmer (MIT license).
+/// +/// Big thank you to Omar Cornut (ocornut@github) for his [imgui library](https://github.com/ocornut/imgui) and +/// giving me the inspiration for this library, Casey Muratori for handmade hero +/// and his original immediate mode graphical user interface idea and Sean +/// Barret for his amazing single header libraries which restored my faith +/// in libraries and brought me to create some of my own. Finally Apoorva Joshi +/// for his single header file packer. +*/ + diff --git a/thirdparty/sokol/tests/ext/stb_truetype.h b/thirdparty/sokol/tests/ext/stb_truetype.h new file mode 100644 index 0000000..98a12c9 --- /dev/null +++ b/thirdparty/sokol/tests/ext/stb_truetype.h @@ -0,0 +1,4548 @@ +// stb_truetype.h - v1.16 - public domain +// authored from 2009-2016 by Sean Barrett / RAD Game Tools +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// render glyphs to one-channel SDF bitmaps (signed-distance field/function) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// Dougall Johnson: OpenType / Type 2 font handling +// +// Misc other: +// Ryan Gordon +// Simon Glass +// github:IntellectualKitty +// +// Bug/warning reports/fixes: +// "Zer" on mollyrocket +// Cass Everitt +// stoiko (Haemimont Games) +// Brian Hook +// Walter van Niftrik +// David Gow +// David Given +// Ivan-Assen Ivanov +// Anthony Pesch +// Johan Duparc +// Hou Qiming +// Fabian "ryg" Giesen +// Martins Mozeiko +// Cap Petschulat +// Omar Cornut +// github:aloucks +// Peter LaValle +// Sergey Popov +// Giumo X. Clanjor +// Higor Euripedes +// Thomas Fields +// Derek Vinyard +// Cort Stratton +// +// VERSION HISTORY +// +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// variant PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// +// Full history can be found at the end of this file. +// +// LICENSE +// +// See end of file for license information. +// +// USAGE +// +// Include this file in whatever places neeed to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// To make the implementation private to the file that generates the implementation, +// #define STBTT_STATIC +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// Improved 3D API (more shippable): +// #include "stb_rect_pack.h" -- optional, but you really want it +// stbtt_PackBegin() +// stbtt_PackSetOversample() -- for improved quality on small fonts +// stbtt_PackFontRanges() -- pack and renders +// stbtt_PackEnd() +// stbtt_GetPackedQuad() +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections +// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetCodepointKernAdvance() +// +// Starting with version 1.06, the rasterizer was replaced with a new, +// faster and generally-more-precise rasterizer. The new rasterizer more +// accurately measures pixel coverage for anti-aliasing, except in the case +// where multiple shapes overlap, in which case it overestimates the AA pixel +// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If +// this turns out to be a problem, you can re-enable the old rasterizer with +// #define STBTT_RASTERIZER_VERSION 1 +// which will incur about a 15% speed hit. +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since they different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// ADVANCED USAGE +// +// Quality: +// +// - Use the functions with Subpixel at the end to allow your characters +// to have subpixel positioning. Since the font is anti-aliased, not +// hinted, this is very import for quality. (This is not possible with +// baked fonts.) +// +// - Kerning is now supported, and if you're supporting subpixel rendering +// then kerning is worth using to give your text a polished look. +// +// Performance: +// +// - Convert Unicode codepoints to glyph indexes and operate on the glyphs; +// if you don't do this, stb_truetype is forced to do the conversion on +// every call. +// +// - There are a lot of memory allocations. We should modify it to take +// a temp buffer and allocate from the temp buffer (without freeing), +// should help performance a lot. +// +// NOTES +// +// The system uses the raw data found in the .ttf file without changing it +// and without building auxiliary data structures. This is a bit inefficient +// on little-endian systems (the data is big-endian), but assuming you're +// caching the bitmaps or glyph shapes this shouldn't be a big deal. +// +// It appears to be very hard to programmatically determine what font a +// given file is in a general way. I provide an API for this, but I don't +// recommend it. +// +// +// SOURCE STATISTICS (based on v0.6c, 2050 LOC) +// +// Documentation & header file 520 LOC \___ 660 LOC documentation +// Sample code 140 LOC / +// Truetype parsing 620 LOC ---- 620 LOC TrueType +// Software rasterization 240 LOC \ . +// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation +// Bitmap management 100 LOC / +// Baked bitmap interface 70 LOC / +// Font name matching & access 150 LOC ---- 150 +// C runtime library abstraction 60 LOC ---- 60 +// +// +// PERFORMANCE MEASUREMENTS FOR 1.06: +// +// 32-bit 64-bit +// Previous release: 8.83 s 7.68 s +// Pool allocations: 7.72 s 6.34 s +// Inline sort : 6.54 s 5.65 s +// New rasterizer : 5.63 s 5.00 s + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// SAMPLE PROGRAMS +//// +// +// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless +// +#if 0 +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +unsigned char ttf_buffer[1<<20]; +unsigned char temp_bitmap[512*512]; + +stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs +GLuint ftex; + +void my_stbtt_initfont(void) +{ + fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); + stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! + // can free ttf_buffer at this point + glGenTextures(1, &ftex); + glBindTexture(GL_TEXTURE_2D, ftex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); + // can free temp_bitmap at this point + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +} + +void my_stbtt_print(float x, float y, char *text) +{ + // assume orthographic projection with units = screen pixels, origin at top left + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, ftex); + glBegin(GL_QUADS); + while (*text) { + if (*text >= 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=2; // leave a little padding in case the character extends left + char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype. + +#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + #ifndef STBTT_sqrt + #include + #define STBTT_sqrt(x) sqrt(x) + #define STBTT_pow(x,y) pow(x,y) + #endif + + #ifndef STBTT_cos + #include + #define STBTT_cos(x) cos(x) + #define STBTT_acos(x) acos(x) + #endif + + #ifndef STBTT_fabs + #include + #define STBTT_fabs(x) fabs(x) + #endif + + #ifndef STBTT_fabs + #include + #define STBTT_fabs(x) fabs(x) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include + #define STBTT_malloc(x,u) ((void)(u),malloc(x)) + #define STBTT_free(x,u) ((void)(u),free(x)) + #endif + + #ifndef STBTT_assert + #include + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef STBTT_STATIC +#define STBTT_DEF static +#else +#define STBTT_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// private structure +typedef struct +{ + unsigned char *data; + int cursor; + int size; +} stbtt__buf; + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +// if return is positive, the first unused row of the bitmap +// if return is negative, returns the negative of the number of characters that fit +// if return is 0, no characters fit and no rows were used +// This uses a very crappy packing. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// Call GetBakedQuad with char_index = 'character - first_char', and it +// creates the quad you need to draw and advances the current position. +// +// The coordinate system used assumes y increases downwards. +// +// Characters will extend both above and below the current position; +// see discussion of "BASELINE" above. +// +// It's inefficient; you might want to c&p it and optimize it. + + + +////////////////////////////////////////////////////////////////////////////// +// +// NEW TEXTURE BAKING API +// +// This provides options for packing multiple fonts into one atlas, not +// perfectly but better than nothing. + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; +} stbtt_packedchar; + +typedef struct stbtt_pack_context stbtt_pack_context; +typedef struct stbtt_fontinfo stbtt_fontinfo; +#ifndef STB_RECT_PACK_VERSION +typedef struct stbrp_rect stbrp_rect; +#endif + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); +// Initializes a packing context stored in the passed-in stbtt_pack_context. +// Future calls using this context will pack characters into the bitmap passed +// in here: a 1-channel bitmap that is width * height. stride_in_bytes is +// the distance from one row to the next (or 0 to mean they are packed tightly +// together). "padding" is the amount of padding to leave between each +// character (normally you want '1' for bitmaps you'll use as textures with +// bilinear filtering). +// +// Returns 0 on failure, 1 on success. + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); +// Cleans up the packing context and frees all memory. + +#define STBTT_POINT_SIZE(x) (-(x)) + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); +// Creates character bitmaps from the font_index'th font found in fontdata (use +// font_index=0 if you don't know what that is). It creates num_chars_in_range +// bitmaps for characters with unicode values starting at first_unicode_char_in_range +// and increasing. Data for how to render them is stored in chardata_for_range; +// pass these to stbtt_GetPackedQuad to get back renderable quads. +// +// font_size is the full height of the character from ascender to descender, +// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed +// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() +// and pass that result as 'font_size': +// ..., 20 , ... // font max minus min y is 20 pixels tall +// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall + +typedef struct +{ + float font_size; + int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint + int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints + int num_chars; + stbtt_packedchar *chardata_for_range; // output + unsigned char h_oversample, v_oversample; // don't set these, they're used internally +} stbtt_pack_range; + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +// Creates character bitmaps from multiple ranges of characters stored in +// ranges. This will usually create a better-packed bitmap than multiple +// calls to stbtt_PackFontRange. Note that you can call this multiple +// times within a single PackBegin/PackEnd. + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); +// Oversampling a font increases the quality by allowing higher-quality subpixel +// positioning, and is especially valuable at smaller text sizes. +// +// This function sets the amount of oversampling for all following calls to +// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given +// pack context. The default (no oversampling) is achieved by h_oversample=1 +// and v_oversample=1. The total number of pixels required is +// h_oversample*v_oversample larger than the default; for example, 2x2 +// oversampling requires 4x the storage of 1x1. For best results, render +// oversampled textures with bilinear filtering. Look at the readme in +// stb/tests/oversample for information about oversampled fonts +// +// To use with PackFontRangesGather etc., you must set it before calls +// call to PackFontRangesGatherRects. + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int align_to_integer); + +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +// Calling these functions in sequence is roughly equivalent to calling +// stbtt_PackFontRanges(). If you more control over the packing of multiple +// fonts, or if you want to pack custom data into a font texture, take a look +// at the source to of stbtt_PackFontRanges() and create a custom version +// using these functions, e.g. call GatherRects multiple times, +// building up a single array of rects, then call PackRects once, +// then call RenderIntoRects repeatedly. This may result in a +// better packing than calling PackFontRanges multiple times +// (or it may not). + +// this is an opaque structure that you shouldn't mess with which holds +// all the context needed from PackBegin to PackEnd. +struct stbtt_pack_context { + void *user_allocator_context; + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data); +// This function will determine the number of fonts in a font file. TrueType +// collection (.ttc) files may contain multiple fonts, while TrueType font +// (.ttf) files only contain one font. The number of fonts can be used for +// indexing with the previous function where the index is between zero and one +// less than the total fonts. If an error occurs, -1 is returned. + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// Each .ttf/.ttc file may have more than one font. Each font has a sequential +// index number starting from 0. Call this function to get the font offset for +// a given index; it returns -1 if the index is out of range. A regular .ttf +// file will only define one font and it always be at offset 0, so it will +// return '0' for index 0, and -1 for all other indices. + +// The following structure is defined publically so you can declare one on +// the stack or as a global or etc, but you should treat it as opaque. +struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph + + stbtt__buf cff; // cff font data + stbtt__buf charstrings; // the charstring index + stbtt__buf gsubrs; // global charstring subroutines index + stbtt__buf subrs; // private charstring subroutines index + stbtt__buf fontdicts; // array of font dicts + stbtt__buf fdselect; // map from glyph to fontdict +}; + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// Given an offset into the file that defines a font, this function builds +// the necessary cached info for the rest of the system. You must allocate +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +// need to do anything special to free it, because the contents are pure +// value data with no additional data structures. Returns 0 on failure. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// If you're going to perform multiple operations on the same character +// and you want a speed-up, call this function with the character you're +// going to process, then use glyph-based functions instead of the +// codepoint-based functions. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose "height" is 'pixels' tall. +// Height is measured as the distance from the highest ascender to the lowest +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +// and computing: +// scale = pixels / (ascent - descent) +// so if you prefer to measure height by the ascent only, use a similar calculation. + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose EM size is mapped to +// 'pixels' tall. This is probably what traditional APIs compute, but +// I'm not positive. + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// ascent is the coordinate above the baseline the font extends; descent +// is the coordinate below the baseline the font extends (i.e. it is typically negative) +// lineGap is the spacing between one row's descent and the next row's ascent... +// so you should advance the vertical position by "*ascent - *descent + *lineGap" +// these are expressed in unscaled coordinates, so you must multiply by +// the scale factor for a given size + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +// the bounding box around all possible characters + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// leftSideBearing is the offset from the current horizontal position to the left edge of the character +// advanceWidth is the offset from the current horizontal position to the next horizontal position +// these are expressed in unscaled coordinates + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve, + STBTT_vcubic + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy,cx1,cy1; + unsigned char type,padding; + } stbtt_vertex; +#endif + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +// returns non-zero if nothing is drawn for this glyph + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// returns # of vertices and fills *vertices with the pointer to them +// these are expressed in "unscaled" coordinates +// +// The shape is a series of countours. Each one starts with +// a STBTT_moveto, then consists of a series of mixed +// STBTT_lineto and STBTT_curveto segments. A lineto +// draws a line from previous endpoint to its x,y; a curveto +// draws a quadratic bezier from previous endpoint to +// its x,y, using cx,cy as the bezier control point. + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// allocates a large-enough single-channel 8bpp bitmap and renders the +// specified character/glyph at the specified scale into it, with +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +// *width & *height are filled out with the width & height of the bitmap, +// which is stored left-to-right, top-to-bottom. +// +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap +// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap +// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the +// width and height and positioning info for it first. + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel +// shift for the character + +// NOTE sokol-samples: declared static but never defined: T_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint); +// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering +// is performed (see stbtt_PackSetOversampling) + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +// get the bbox of the bitmap centered around the glyph origin; so the +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +// the bitmap top left is (leftSideBearing*scale,iy0). +// (Note that the bitmap uses y-increases-down, but the shape uses +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel +// shift for the character + +// the following functions are equivalent to the above functions, but operate +// on glyph indices instead of Unicode codepoints (for efficiency) +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph); +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +// rasterize a shape with quadratic beziers into a bitmap +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into + float flatness_in_pixels, // allowable error of curve in pixels + stbtt_vertex *vertices, // array of vertices defining shape + int num_verts, // number of vertices in above array + float scale_x, float scale_y, // scale applied to input vertices + float shift_x, float shift_y, // translation applied to input vertices + int x_off, int y_off, // another translation applied to input + int invert, // if non-zero, vertically flip shape + void *userdata); // context for to STBTT_MALLOC + +////////////////////////////////////////////////////////////////////////////// +// +// Signed Distance Function (or Field) rendering + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); +// frees the SDF bitmap allocated below + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +// These functions compute a discretized SDF field for a single character, suitable for storing +// in a single-channel texture, sampling with bilinear filtering, and testing against +// larger than some threshhold to produce scalable fonts. +// info -- the font +// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap +// glyph/codepoint -- the character to generate the SDF for +// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), +// which allows effects like bit outlines +// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) +// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) +// if positive, > onedge_value is inside; if negative, < onedge_value is inside +// width,height -- output height & width of the SDF bitmap (including padding) +// xoff,yoff -- output origin of the character +// return value -- a 2D array of bytes 0..255, width*height in size +// +// pixel_dist_scale & onedge_value are a scale & bias that allows you to make +// optimal use of the limited 0..255 for your application, trading off precision +// and special effects. SDF values outside the range 0..255 are clamped to 0..255. +// +// Example: +// scale = stbtt_ScaleForPixelHeight(22) +// padding = 5 +// onedge_value = 180 +// pixel_dist_scale = 180/5.0 = 36.0 +// +// This will create an SDF bitmap in which the character is about 22 pixels +// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled +// shape, sample the SDF at each pixel and fill the pixel if the SDF value +// is greater than or equal to 180/255. (You'll actually want to antialias, +// which is beyond the scope of this example.) Additionally, you can compute +// offset outlines (e.g. to stroke the character border inside & outside, +// or only outside). For example, to fill outside the character up to 3 SDF +// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above +// choice of variables maps a range from 5 pixels outside the shape to +// 2 pixels inside the shape to 0..255; this is intended primarily for apply +// outside effects only (the interior range is needed to allow proper +// antialiasing of the font at *smaller* sizes) +// +// The function computes the SDF analytically at each SDF pixel, not by e.g. +// building a higher-res bitmap and approximating it. In theory the quality +// should be as high as possible for an SDF of this size & representation, but +// unclear if this is true in practice (perhaps building a higher-res bitmap +// and computing from that can allow drop-out prevention). +// +// The algorithm has not been optimized at all, so expect it to be slow +// if computing lots of characters or very large sizes. + + + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// returns the offset (not index) of the font that matches, or -1 if none +// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +// if you use any other flag, use a font name like "Arial"; this checks +// the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// returns 1/0 whether the first string interpreted as utf8 is identical to +// the second string interpreted as big-endian utf16... useful for strings from next func + +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// returns the string (which may be big-endian double byte, e.g. for unicode) +// and puts the length in bytes in *length. +// +// some of the values for the IDs are below; for more see the truetype spec: +// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +// http://www.microsoft.com/typography/otspec/name.htm + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +#ifndef STBTT_MAX_OVERSAMPLE +#define STBTT_MAX_OVERSAMPLE 8 +#endif + +#if STBTT_MAX_OVERSAMPLE > 255 +#error "STBTT_MAX_OVERSAMPLE cannot be > 255" +#endif + +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +#ifndef STBTT_RASTERIZER_VERSION +#define STBTT_RASTERIZER_VERSION 2 +#endif + +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +////////////////////////////////////////////////////////////////////////// +// +// stbtt__buf helpers to parse data from file +// + +static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + assert(b->data); + return b->data[b->cursor++]; +} + +static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor]; +} + +static void stbtt__buf_seek(stbtt__buf *b, int o) +{ + STBTT_assert(!(o > b->size || o < 0)); + b->cursor = (o > b->size || o < 0) ? b->size : o; +} + +static void stbtt__buf_skip(stbtt__buf *b, int o) +{ + stbtt__buf_seek(b, b->cursor + o); +} + +static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) +{ + stbtt_uint32 v = 0; + int i; + STBTT_assert(n >= 1 && n <= 4); + for (i = 0; i < n; i++) + v = (v << 8) | stbtt__buf_get8(b); + return v; +} + +static stbtt__buf stbtt__new_buf(const void *p, size_t size) +{ + stbtt__buf r; + STBTT_assert(size < 0x40000000); + r.data = (stbtt_uint8*) p; + r.size = (int) size; + r.cursor = 0; + return r; +} + +#define stbtt__buf_get16(b) stbtt__buf_get((b), 2) +#define stbtt__buf_get32(b) stbtt__buf_get((b), 4) + +static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s) +{ + stbtt__buf r = stbtt__new_buf(NULL, 0); + if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; + r.data = b->data + o; + r.size = s; + return r; +} + +static stbtt__buf stbtt__cff_get_index(stbtt__buf *b) +{ + int count, start, offsize; + start = b->cursor; + count = stbtt__buf_get16(b); + if (count) { + offsize = stbtt__buf_get8(b); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(b, offsize * count); + stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); + } + return stbtt__buf_range(b, start, b->cursor - start); +} + +static stbtt_uint32 stbtt__cff_int(stbtt__buf *b) +{ + int b0 = stbtt__buf_get8(b); + if (b0 >= 32 && b0 <= 246) return b0 - 139; + else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; + else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; + else if (b0 == 28) return stbtt__buf_get16(b); + else if (b0 == 29) return stbtt__buf_get32(b); + STBTT_assert(0); + return 0; +} + +static void stbtt__cff_skip_operand(stbtt__buf *b) { + int v, b0 = stbtt__buf_peek8(b); + STBTT_assert(b0 >= 28); + if (b0 == 30) { + stbtt__buf_skip(b, 1); + while (b->cursor < b->size) { + v = stbtt__buf_get8(b); + if ((v & 0xF) == 0xF || (v >> 4) == 0xF) + break; + } + } else { + stbtt__cff_int(b); + } +} + +static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) +{ + stbtt__buf_seek(b, 0); + while (b->cursor < b->size) { + int start = b->cursor, end, op; + while (stbtt__buf_peek8(b) >= 28) + stbtt__cff_skip_operand(b); + end = b->cursor; + op = stbtt__buf_get8(b); + if (op == 12) op = stbtt__buf_get8(b) | 0x100; + if (op == key) return stbtt__buf_range(b, start, end-start); + } + return stbtt__buf_range(b, 0, 0); +} + +static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out) +{ + int i; + stbtt__buf operands = stbtt__dict_get(b, key); + for (i = 0; i < outcount && operands.cursor < operands.size; i++) + out[i] = stbtt__cff_int(&operands); +} + +static int stbtt__cff_index_count(stbtt__buf *b) +{ + stbtt__buf_seek(b, 0); + return stbtt__buf_get16(b); +} + +static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) +{ + int count, offsize, start, end; + stbtt__buf_seek(&b, 0); + count = stbtt__buf_get16(&b); + offsize = stbtt__buf_get8(&b); + STBTT_assert(i >= 0 && i < count); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(&b, i*offsize); + start = stbtt__buf_get(&b, offsize); + end = stbtt__buf_get(&b, offsize); + return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); +} + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } +static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*4); + } + } + return -1; +} + +static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) +{ + // if it's just a font, there's only one valid font + if (stbtt__isfont(font_collection)) + return 1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + return ttLONG(font_collection+8); + } + } + return 0; +} + +static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) +{ + stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; + stbtt__buf pdict; + stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); + if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); + pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); + stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); + if (!subrsoff) return stbtt__new_buf(NULL, 0); + stbtt__buf_seek(&cff, private_loc[1]+subrsoff); + return stbtt__cff_get_index(&cff); +} + +static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) +{ + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + info->cff = stbtt__new_buf(NULL, 0); + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + + if (!cmap || !info->head || !info->hhea || !info->hmtx) + return 0; + if (info->glyf) { + // required for truetype + if (!info->loca) return 0; + } else { + // initialization for CFF / Type2 fonts (OTF) + stbtt__buf b, topdict, topdictidx; + stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; + stbtt_uint32 cff; + + cff = stbtt__find_table(data, fontstart, "CFF "); + if (!cff) return 0; + + info->fontdicts = stbtt__new_buf(NULL, 0); + info->fdselect = stbtt__new_buf(NULL, 0); + + // @TODO this should use size from table (not 512MB) + info->cff = stbtt__new_buf(data+cff, 512*1024*1024); + b = info->cff; + + // read the header + stbtt__buf_skip(&b, 2); + stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize + + // @TODO the name INDEX could list multiple fonts, + // but we just use the first one. + stbtt__cff_get_index(&b); // name INDEX + topdictidx = stbtt__cff_get_index(&b); + topdict = stbtt__cff_index_get(topdictidx, 0); + stbtt__cff_get_index(&b); // string INDEX + info->gsubrs = stbtt__cff_get_index(&b); + + stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); + stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); + stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); + stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); + info->subrs = stbtt__get_subrs(b, topdict); + + // we only support Type 2 charstrings + if (cstype != 2) return 0; + if (charstrings == 0) return 0; + + if (fdarrayoff) { + // looks like a CID font + if (!fdselectoff) return 0; + stbtt__buf_seek(&b, fdarrayoff); + info->fontdicts = stbtt__cff_get_index(&b); + info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); + } + + stbtt__buf_seek(&b, charstrings); + info->charstrings = stbtt__cff_get_index(&b); + } + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + // Mac/iOS has these + // all the encodingIDs are unicode, so we don't bother to check it + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + stbtt_uint16 end; + searchRange >>= 1; + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + { + stbtt_uint16 offset, start; + stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); + + STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + STBTT_assert(!info->cff.size); + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); + +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + if (info->cff.size) { + stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); + } else { + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + } + return 1; +} + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g; + if (info->cff.size) + return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0; + g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours == -1) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else if (numberOfContours < 0) { + // @TODO other compound variations? + STBTT_assert(0); + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +typedef struct +{ + int bounds; + int started; + float first_x, first_y; + float x, y; + stbtt_int32 min_x, max_x, min_y, max_y; + + stbtt_vertex *pvertices; + int num_vertices; +} stbtt__csctx; + +#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} + +static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) +{ + if (x > c->max_x || !c->started) c->max_x = x; + if (y > c->max_y || !c->started) c->max_y = y; + if (x < c->min_x || !c->started) c->min_x = x; + if (y < c->min_y || !c->started) c->min_y = y; + c->started = 1; +} + +static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1) +{ + if (c->bounds) { + stbtt__track_vertex(c, x, y); + if (type == STBTT_vcubic) { + stbtt__track_vertex(c, cx, cy); + stbtt__track_vertex(c, cx1, cy1); + } + } else { + stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); + c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1; + c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1; + } + c->num_vertices++; +} + +static void stbtt__csctx_close_shape(stbtt__csctx *ctx) +{ + if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) +{ + stbtt__csctx_close_shape(ctx); + ctx->first_x = ctx->x = ctx->x + dx; + ctx->first_y = ctx->y = ctx->y + dy; + stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) +{ + ctx->x += dx; + ctx->y += dy; + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) +{ + float cx1 = ctx->x + dx1; + float cy1 = ctx->y + dy1; + float cx2 = cx1 + dx2; + float cy2 = cy1 + dy2; + ctx->x = cx2 + dx3; + ctx->y = cy2 + dy3; + stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); +} + +static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) +{ + int count = stbtt__cff_index_count(&idx); + int bias = 107; + if (count >= 33900) + bias = 32768; + else if (count >= 1240) + bias = 1131; + n += bias; + if (n < 0 || n >= count) + return stbtt__new_buf(NULL, 0); + return stbtt__cff_index_get(idx, n); +} + +static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt__buf fdselect = info->fdselect; + int nranges, start, end, v, fmt, fdselector = -1, i; + + stbtt__buf_seek(&fdselect, 0); + fmt = stbtt__buf_get8(&fdselect); + if (fmt == 0) { + // untested + stbtt__buf_skip(&fdselect, glyph_index); + fdselector = stbtt__buf_get8(&fdselect); + } else if (fmt == 3) { + nranges = stbtt__buf_get16(&fdselect); + start = stbtt__buf_get16(&fdselect); + for (i = 0; i < nranges; i++) { + v = stbtt__buf_get8(&fdselect); + end = stbtt__buf_get16(&fdselect); + if (glyph_index >= start && glyph_index < end) { + fdselector = v; + break; + } + start = end; + } + } + if (fdselector == -1) stbtt__new_buf(NULL, 0); + return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); +} + +static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c) +{ + int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; + int has_subrs = 0, clear_stack; + float s[48]; + stbtt__buf subr_stack[10], subrs = info->subrs, b; + float f; + +#define STBTT__CSERR(s) (0) + + // this currently ignores the initial width value, which isn't needed if we have hmtx + b = stbtt__cff_index_get(info->charstrings, glyph_index); + while (b.cursor < b.size) { + i = 0; + clear_stack = 1; + b0 = stbtt__buf_get8(&b); + switch (b0) { + // @TODO implement hinting + case 0x13: // hintmask + case 0x14: // cntrmask + if (in_header) + maskbits += (sp / 2); // implicit "vstem" + in_header = 0; + stbtt__buf_skip(&b, (maskbits + 7) / 8); + break; + + case 0x01: // hstem + case 0x03: // vstem + case 0x12: // hstemhm + case 0x17: // vstemhm + maskbits += (sp / 2); + break; + + case 0x15: // rmoveto + in_header = 0; + if (sp < 2) return STBTT__CSERR("rmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); + break; + case 0x04: // vmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("vmoveto stack"); + stbtt__csctx_rmove_to(c, 0, s[sp-1]); + break; + case 0x16: // hmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("hmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-1], 0); + break; + + case 0x05: // rlineto + if (sp < 2) return STBTT__CSERR("rlineto stack"); + for (; i + 1 < sp; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical + // starting from a different place. + + case 0x07: // vlineto + if (sp < 1) return STBTT__CSERR("vlineto stack"); + goto vlineto; + case 0x06: // hlineto + if (sp < 1) return STBTT__CSERR("hlineto stack"); + for (;;) { + if (i >= sp) break; + stbtt__csctx_rline_to(c, s[i], 0); + i++; + vlineto: + if (i >= sp) break; + stbtt__csctx_rline_to(c, 0, s[i]); + i++; + } + break; + + case 0x1F: // hvcurveto + if (sp < 4) return STBTT__CSERR("hvcurveto stack"); + goto hvcurveto; + case 0x1E: // vhcurveto + if (sp < 4) return STBTT__CSERR("vhcurveto stack"); + for (;;) { + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); + i += 4; + hvcurveto: + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); + i += 4; + } + break; + + case 0x08: // rrcurveto + if (sp < 6) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x18: // rcurveline + if (sp < 8) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp - 2; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + case 0x19: // rlinecurve + if (sp < 8) return STBTT__CSERR("rlinecurve stack"); + for (; i + 1 < sp - 6; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x1A: // vvcurveto + case 0x1B: // hhcurveto + if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); + f = 0.0; + if (sp & 1) { f = s[i]; i++; } + for (; i + 3 < sp; i += 4) { + if (b0 == 0x1B) + stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); + else + stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); + f = 0.0; + } + break; + + case 0x0A: // callsubr + if (!has_subrs) { + if (info->fdselect.size) + subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); + has_subrs = 1; + } + // fallthrough + case 0x1D: // callgsubr + if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); + v = (int) s[--sp]; + if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); + subr_stack[subr_stack_height++] = b; + b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); + if (b.size == 0) return STBTT__CSERR("subr not found"); + b.cursor = 0; + clear_stack = 0; + break; + + case 0x0B: // return + if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); + b = subr_stack[--subr_stack_height]; + clear_stack = 0; + break; + + case 0x0E: // endchar + stbtt__csctx_close_shape(c); + return 1; + + case 0x0C: { // two-byte escape + float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; + float dx, dy; + int b1 = stbtt__buf_get8(&b); + switch (b1) { + // @TODO These "flex" implementations ignore the flex-depth and resolution, + // and always draw beziers. + case 0x22: // hflex + if (sp < 7) return STBTT__CSERR("hflex stack"); + dx1 = s[0]; + dx2 = s[1]; + dy2 = s[2]; + dx3 = s[3]; + dx4 = s[4]; + dx5 = s[5]; + dx6 = s[6]; + stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); + break; + + case 0x23: // flex + if (sp < 13) return STBTT__CSERR("flex stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = s[10]; + dy6 = s[11]; + //fd is s[12] + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + case 0x24: // hflex1 + if (sp < 9) return STBTT__CSERR("hflex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dx4 = s[5]; + dx5 = s[6]; + dy5 = s[7]; + dx6 = s[8]; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); + break; + + case 0x25: // flex1 + if (sp < 11) return STBTT__CSERR("flex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = dy6 = s[10]; + dx = dx1+dx2+dx3+dx4+dx5; + dy = dy1+dy2+dy3+dy4+dy5; + if (STBTT_fabs(dx) > STBTT_fabs(dy)) + dy6 = -dy; + else + dx6 = -dx; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + default: + return STBTT__CSERR("unimplemented"); + } + } break; + + default: + if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254)) + return STBTT__CSERR("reserved operator"); + + // push immediate + if (b0 == 255) { + f = (float)stbtt__buf_get32(&b) / 0x10000; + } else { + stbtt__buf_skip(&b, -1); + f = (float)(stbtt_int16)stbtt__cff_int(&b); + } + if (sp >= 48) return STBTT__CSERR("push stack overflow"); + s[sp++] = f; + clear_stack = 0; + break; + } + if (clear_stack) sp = 0; + } + return STBTT__CSERR("no endchar"); + +#undef STBTT__CSERR +} + +static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + // runs the charstring twice, once to count and once to output (to avoid realloc) + stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1); + stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0); + if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { + *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata); + output_ctx.pvertices = *pvertices; + if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { + STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices); + return output_ctx.num_vertices; + } + } + *pvertices = NULL; + return 0; +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + stbtt__csctx c = STBTT__CSCTX_INIT(1); + int r = stbtt__run_charstring(info, glyph_index, &c); + if (x0) { + *x0 = r ? c.min_x : 0; + *y0 = r ? c.min_y : 0; + *x1 = r ? c.max_x : 0; + *y1 = r ? c.max_y : 0; + } + return r ? c.num_vertices : 0; +} + +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + if (!info->cff.size) + return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); + else + return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); +} + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + // e.g. space character + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + // move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); + if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); + if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); + } +} + +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Rasterizer + +typedef struct stbtt__hheap_chunk +{ + struct stbtt__hheap_chunk *next; +} stbtt__hheap_chunk; + +typedef struct stbtt__hheap +{ + struct stbtt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +} stbtt__hheap; + +static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); + if (c == NULL) + return NULL; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + size * hh->num_remaining_in_head_chunk; + } +} + +static void stbtt__hheap_free(stbtt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) +{ + stbtt__hheap_chunk *c = hh->head; + while (c) { + stbtt__hheap_chunk *n = c->next; + STBTT_free(c, userdata); + c = n; + } +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + + +typedef struct stbtt__active_edge +{ + struct stbtt__active_edge *next; + #if STBTT_RASTERIZER_VERSION==1 + int x,dx; + float ey; + int direction; + #elif STBTT_RASTERIZER_VERSION==2 + float fx,fdx,fdy; + float direction; + float sy; + float ey; + #else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" + #endif +} stbtt__active_edge; + +#if STBTT_RASTERIZER_VERSION == 1 +#define STBTT_FIXSHIFT 10 +#define STBTT_FIX (1 << STBTT_FIXSHIFT) +#define STBTT_FIXMASK (STBTT_FIX-1) + +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + if (!z) return z; + + // round dx down to avoid overshooting + if (dxdy < 0) + z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); + else + z->dx = STBTT_ifloor(STBTT_FIX * dxdy); + + z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount + z->x -= off_x * STBTT_FIX; + + z->ey = e->y1; + z->next = 0; + z->direction = e->invert ? 1 : -1; + return z; +} +#elif STBTT_RASTERIZER_VERSION == 2 +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + //STBTT_assert(e->y0 <= start_point); + if (!z) return z; + z->fdx = dxdy; + z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#if STBTT_RASTERIZER_VERSION == 1 +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->direction; + } else { + int x1 = e->x; w += e->direction; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> STBTT_FIXSHIFT; + int j = x1 >> STBTT_FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); + if (z != NULL) { + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +#elif STBTT_RASTERIZER_VERSION == 2 + +// the edge passed in here does not cross the vertical line at x or the vertical line at x+1 +// (i.e. it has already been clipped to those) +static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) +{ + if (y0 == y1) return; + STBTT_assert(y0 < y1); + STBTT_assert(e->sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (x0 == x) + STBTT_assert(x1 <= x+1); + else if (x0 == x+1) + STBTT_assert(x1 >= x); + else if (x0 <= x) + STBTT_assert(x1 <= x); + else if (x0 >= x+1) + STBTT_assert(x1 >= x+1); + else + STBTT_assert(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1) + ; + else { + STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position + } +} + +static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + + while (e) { + // brute force every pixel + + // compute intersection points with top & bottom + STBTT_assert(e->ey >= y_top); + + if (e->fdx == 0) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float sy0,sy1; + float dy = e->fdy; + STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); + + // compute endpoints of line segment clipped to this scanline (if the + // line segment starts on this scanline. x0 is the intersection of the + // line with y_top, but that may be off the line segment. + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + sy0 = e->sy; + } else { + x_top = x0; + sy0 = y_top; + } + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + sy1 = e->ey; + } else { + x_bottom = xb; + sy1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { + // from here on, we don't have to range check x values + + if ((int) x_top == (int) x_bottom) { + float height; + // simple case, only spans one pixel + int x = (int) x_top; + height = sy1 - sy0; + STBTT_assert(x >= 0 && x < len); + scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height; + scanline_fill[x] += e->direction * height; // everything right of this pixel is filled + } else { + int x,x1,x2; + float y_crossing, step, sign, area; + // covers 2+ pixels + if (x_top > x_bottom) { + // flip scanline vertically; signed area is the same + float t; + sy0 = y_bottom - (sy0 - y_top); + sy1 = y_bottom - (sy1 - y_top); + t = sy0, sy0 = sy1, sy1 = t; + t = x_bottom, x_bottom = x_top, x_top = t; + // SOKOL FIXME: static analyzer fix 'Value stored to 'dx' is never read' + //dx = -dx; + dy = -dy; + // SOKOL FIXME: static analyzer fix 'Although the value stored to 'xb' is used in the enclosing expression, the value is never actually read from 'xb'' + /*t = x0,*/x0 = xb/*xb = t*/; + } + + x1 = (int) x_top; + x2 = (int) x_bottom; + // compute intersection with y axis at x1+1 + y_crossing = (x1+1 - x0) * dy + y_top; + + sign = e->direction; + // area of the rectangle covered from y0..y_crossing + area = sign * (y_crossing-sy0); + // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) + scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2); + + step = sign * dy; + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; + area += step; + } + y_crossing += dy * (x2 - (x1+1)); + + STBTT_assert(STBTT_fabs(area) <= 1.01f); + + scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); + + scanline_fill[x2] += sign * (sy1-sy0); + } + } else { + // if edge goes outside of box we're drawing, we require + // clipping logic. since this does not match the intended use + // of this library, we use a different, very slow brute + // force implementation + int x; + for (x=0; x < len; ++x) { + // cases: + // + // there can be up to two intersections with the pixel. any intersection + // with left or right edges can be handled by splitting into two (or three) + // regions. intersections with top & bottom do not necessitate case-wise logic. + // + // the old way of doing this found the intersections with the left & right edges, + // then used some simple logic to produce up to three segments in sorted order + // from top-to-bottom. however, this had a problem: if an x edge was epsilon + // across the x border, then the corresponding y position might not be distinct + // from the other y segment, and it might ignored as an empty segment. to avoid + // that, we need to explicitly produce segments based on x positions. + + // rename variables to clearly-defined pairs + float y0 = y_top; + float x1 = (float) (x); + float x2 = (float) (x+1); + float x3 = xb; + float y3 = y_bottom; + + // x = e->x + e->dx * (y-y_top) + // (y-y_top) = (x - e->x) / e->dx + // y = (x - e->x) / e->dx + y_top + float y1 = (x - x0) / dx + y_top; + float y2 = (x+1 - x0) / dx + y_top; + + if (x0 < x1 && x3 > x2) { // three segments descending down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x1 && x0 > x2) { // three segments descending down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else { // one segment + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); + } + } + } + } + e = e->next; + } +} + +// directly AA rasterize edges w/o supersampling +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + STBTT__NOTUSED(vsubsample); + + if (result->w > 64) + scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); + else + scanline = scanline_data; + + scanline2 = scanline + result->w; + + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) { + // find center of pixel for this scanline + float scan_y_top = y + 0.0f; + float scan_y_bottom = y + 1.0f; + stbtt__active_edge **step = &active; + + STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); + STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); + + // update all active edges; + // remove all active edges that terminate before the top of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + step = &((*step)->next); // advance through list + } + } + + // insert all edges that start before the bottom of this scanline + while (e->y0 <= scan_y_bottom) { + if (e->y0 != e->y1) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); + if (z != NULL) { + STBTT_assert(z->ey >= scan_y_top); + // insert at front + z->next = active; + active = z; + } + } + ++e; + } + + // now process all active edges + if (active) + stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) STBTT_fabs(k)*255 + 0.5f; + m = (int) k; + if (m > 255) m = 255; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + // advance all the edges + step = &active; + while (*step) { + stbtt__active_edge *z = *step; + z->fx += z->fdx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + + ++y; + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) + +static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + stbtt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + stbtt__edge *b = &p[j-1]; + int c = STBTT__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) +{ + /* threshhold for transitioning to insertion sort */ + while (n > 12) { + stbtt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = STBTT__COMPARE(&p[0],&p[m]); + c12 = STBTT__COMPARE(&p[m],&p[n-1]); + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = STBTT__COMPARE(&p[0],&p[n-1]); + /* 0>mid && midn => n; 0 0 */ + /* 0n: 0>n => 0; 0 n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!STBTT__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!STBTT__COMPARE(&p[0], &p[j])) break; + } + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + } + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + stbtt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + stbtt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +static void stbtt__sort_edges(stbtt__edge *p, int n) +{ + stbtt__sort_edges_quicksort(p, n); + stbtt__sort_edges_ins_sort(p, n); +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; +#if STBTT_RASTERIZER_VERSION == 1 + int vsubsample = result->h < 8 ? 15 : 5; +#elif STBTT_RASTERIZER_VERSION == 2 + int vsubsample = 1; +#else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + stbtt__sort_edges(e, n); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) +{ + // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough + float dx0 = x1-x0; + float dy0 = y1-y0; + float dx1 = x2-x1; + float dy1 = y2-y1; + float dx2 = x3-x2; + float dy2 = y3-y2; + float dx = x3-x0; + float dy = y3-y0; + float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); + float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy); + float flatness_squared = longlen*longlen-shortlen*shortlen; + + if (n > 16) // 65536 segments on one curve better be enough! + return; + + if (flatness_squared > objspace_flatness_squared) { + float x01 = (x0+x1)/2; + float y01 = (y0+y1)/2; + float x12 = (x1+x2)/2; + float y12 = (y1+y2)/2; + float x23 = (x2+x3)/2; + float y23 = (y2+y3)/2; + + float xa = (x01+x12)/2; + float ya = (y01+y12)/2; + float xb = (x12+x23)/2; + float yb = (y12+y23)/2; + + float mx = (xa+xb)/2; + float my = (ya+yb)/2; + + stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x3,y3); + *num_points = *num_points+1; + } +} + +// returns number of contours +static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + case STBTT_vcubic: + stbtt__tesselate_cubic(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].cx1, vertices[i].cy1, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count, *winding_lengths; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) { + STBTT_free(vertices, info->userdata); + return NULL; + } + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + f.userdata = NULL; + if (!stbtt_InitFont(&f, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 1; + if (y+gh+1 > bottom_y) + bottom_y = y+gh+1; + } + return bottom_y; +} + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// rectangle packing replacement routines if you don't have stb_rect_pack.h +// + +#ifndef STB_RECT_PACK_VERSION + +typedef int stbrp_coord; + +//////////////////////////////////////////////////////////////////////////////////// +// // +// // +// COMPILER WARNING ?!?!? // +// // +// // +// if you get a compile warning due to these symbols being defined more than // +// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // +// // +//////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int width,height; + int x,y,bottom_y; +} stbrp_context; + +typedef struct +{ + unsigned char x; +} stbrp_node; + +struct stbrp_rect +{ + stbrp_coord x,y; + int id,w,h,was_packed; +}; + +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) +{ + con->width = pw; + con->height = ph; + con->x = 0; + con->y = 0; + con->bottom_y = 0; + STBTT__NOTUSED(nodes); + STBTT__NOTUSED(num_nodes); +} + +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) +{ + int i; + for (i=0; i < num_rects; ++i) { + if (con->x + rects[i].w > con->width) { + con->x = 0; + con->y = con->bottom_y; + } + if (con->y + rects[i].h > con->height) + break; + rects[i].x = con->x; + rects[i].y = con->y; + rects[i].was_packed = 1; + con->x += rects[i].w; + if (con->y + rects[i].h > con->bottom_y) + con->bottom_y = con->y + rects[i].h; + } + for ( ; i < num_rects; ++i) + rects[i].was_packed = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If +// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +{ + stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); + int num_nodes = pw - padding; + stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); + + if (context == NULL || nodes == NULL) { + if (context != NULL) STBTT_free(context, alloc_context); + if (nodes != NULL) STBTT_free(nodes , alloc_context); + return 0; + } + + spc->user_allocator_context = alloc_context; + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + + stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + + if (pixels) + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + + return 1; +} + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) +{ + STBTT_free(spc->nodes , spc->user_allocator_context); + STBTT_free(spc->pack_info, spc->user_allocator_context); +} + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +{ + STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); + STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); + if (h_oversample <= STBTT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= STBTT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) + +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < h; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < w; ++i) { + STBTT_assert(pixels[i] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i] = (unsigned char) (total / kernel_width); + } + + pixels += stride_in_bytes; + } +} + +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < w; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < h; ++i) { + STBTT_assert(pixels[i*stride_in_bytes] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + + pixels += 1; + } +} + +static float stbtt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + // The prefilter is a box filter of width "oversample", + // which shifts phase by (oversample - 1)/2 pixels in + // oversampled space. We want to shift in the opposite + // direction to counter this. + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k; + + k=0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + ranges[i].h_oversample = (unsigned char) spc->h_oversample; + ranges[i].v_oversample = (unsigned char) spc->v_oversample; + for (j=0; j < ranges[i].num_chars; ++j) { + int x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + ++k; + } + } + + return k; +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, + output, + out_w - (prefilter_x - 1), + out_h - (prefilter_y - 1), + out_stride, + scale_x, + scale_y, + shift_x, + shift_y, + glyph); + + if (prefilter_x > 1) + stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); + + if (prefilter_y > 1) + stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); + + *sub_x = stbtt__oversample_shift(prefilter_x); + *sub_y = stbtt__oversample_shift(prefilter_y); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k, return_value = 1; + + // save current values + int old_h_over = spc->h_oversample; + int old_v_over = spc->v_oversample; + + k = 0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + float recip_h,recip_v,sub_x,sub_y; + spc->h_oversample = ranges[i].h_oversample; + spc->v_oversample = ranges[i].v_oversample; + recip_h = 1.0f / spc->h_oversample; + recip_v = 1.0f / spc->v_oversample; + sub_x = stbtt__oversample_shift(spc->h_oversample); + sub_y = stbtt__oversample_shift(spc->v_oversample); + for (j=0; j < ranges[i].num_chars; ++j) { + stbrp_rect *r = &rects[k]; + if (r->was_packed) { + stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbrp_coord pad = (stbrp_coord) spc->padding; + + // pad on left and top + r->x += pad; + r->y += pad; + r->w -= pad; + r->h -= pad; + stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); + stbtt_GetGlyphBitmapBox(info, glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + &x0,&y0,&x1,&y1); + stbtt_MakeGlyphBitmapSubpixel(info, + spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w - spc->h_oversample+1, + r->h - spc->v_oversample+1, + spc->stride_in_bytes, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + glyph); + + if (spc->h_oversample > 1) + stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->h_oversample); + + if (spc->v_oversample > 1) + stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->v_oversample); + + bc->x0 = (stbtt_int16) r->x; + bc->y0 = (stbtt_int16) r->y; + bc->x1 = (stbtt_int16) (r->x + r->w); + bc->y1 = (stbtt_int16) (r->y + r->h); + bc->xadvance = scale * advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = (x0 + r->w) * recip_h + sub_x; + bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + } else { + return_value = 0; // if any fail, report failure + } + + ++k; + } + } + + // restore original values + spc->h_oversample = old_h_over; + spc->v_oversample = old_v_over; + + return return_value; +} + +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) +{ + stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); +} + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +{ + stbtt_fontinfo info; + int i,j,n, return_value = 1; + //stbrp_context *context = (stbrp_context *) spc->pack_info; + stbrp_rect *rects; + + // flag all characters as NOT packed + for (i=0; i < num_ranges; ++i) + for (j=0; j < ranges[i].num_chars; ++j) + ranges[i].chardata_for_range[j].x0 = + ranges[i].chardata_for_range[j].y0 = + ranges[i].chardata_for_range[j].x1 = + ranges[i].chardata_for_range[j].y1 = 0; + + n = 0; + for (i=0; i < num_ranges; ++i) + n += ranges[i].num_chars; + + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); + if (rects == NULL) + return 0; + + info.userdata = spc->user_allocator_context; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); + + n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); + + stbtt_PackFontRangesPackRects(spc, rects, n); + + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); + + STBTT_free(rects, spc->user_allocator_context); + return return_value; +} + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, + int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) +{ + stbtt_pack_range range; + range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; + range.array_of_unicode_codepoints = NULL; + range.num_chars = num_chars_in_range; + range.chardata_for_range = chardata_for_range; + range.font_size = font_size; + return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); +} + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +{ + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_packedchar *b = chardata + char_index; + + if (align_to_integer) { + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// sdf computation +// + +#define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) +#define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) + +static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2]) +{ + float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; + float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; + float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; + float roperp = orig[1]*ray[0] - orig[0]*ray[1]; + + float a = q0perp - 2*q1perp + q2perp; + float b = q1perp - q0perp; + float c = q0perp - roperp; + + float s0 = 0., s1 = 0.; + int num_s = 0; + + if (a != 0.0) { + float discr = b*b - a*c; + if (discr > 0.0) { + float rcpna = -1 / a; + float d = (float) sqrt(discr); + s0 = (b+d) * rcpna; + s1 = (b-d) * rcpna; + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { + if (num_s == 0) s0 = s1; + ++num_s; + } + } + } else { + // 2*b*s + c = 0 + // s = -c / (2*b) + s0 = c / (-2 * b); + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + } + + if (num_s == 0) + return 0; + else { + float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); + float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; + + float q0d = q0[0]*rayn_x + q0[1]*rayn_y; + float q1d = q1[0]*rayn_x + q1[1]*rayn_y; + float q2d = q2[0]*rayn_x + q2[1]*rayn_y; + float rod = orig[0]*rayn_x + orig[1]*rayn_y; + + float q10d = q1d - q0d; + float q20d = q2d - q0d; + float q0rd = q0d - rod; + + hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; + hits[0][1] = a*s0+b; + + if (num_s > 1) { + hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; + hits[1][1] = a*s1+b; + return 2; + } else { + return 1; + } + } +} + +static int equal(float *a, float *b) +{ + return (a[0] == b[0] && a[1] == b[1]); +} + +static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) +{ + int i; + float orig[2], ray[2] = { 1, 0 }; + float y_frac; + int winding = 0; + + orig[0] = x; + orig[1] = y; + + // make sure y never passes through a vertex of the shape + y_frac = (float) fmod(y, 1.0f); + if (y_frac < 0.01f) + y += 0.01f; + else if (y_frac > 0.99f) + y -= 0.01f; + orig[1] = y; + + // test a ray from (-infinity,y) to (x,y) + for (i=0; i < nverts; ++i) { + if (verts[i].type == STBTT_vline) { + int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y; + int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } + if (verts[i].type == STBTT_vcurve) { + int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ; + int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy; + int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ; + int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); + int by = STBTT_max(y0,STBTT_max(y1,y2)); + if (y > ay && y < by && x > ax) { + float q0[2],q1[2],q2[2]; + float hits[2][2]; + q0[0] = (float)x0; + q0[1] = (float)y0; + q1[0] = (float)x1; + q1[1] = (float)y1; + q2[0] = (float)x2; + q2[1] = (float)y2; + if (equal(q0,q1) || equal(q1,q2)) { + x0 = (int)verts[i-1].x; + y0 = (int)verts[i-1].y; + x1 = (int)verts[i ].x; + y1 = (int)verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } else { + int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); + if (num_hits >= 1) + if (hits[0][0] < 0) + winding += (hits[0][1] < 0 ? -1 : 1); + if (num_hits >= 2) + if (hits[1][0] < 0) + winding += (hits[1][1] < 0 ? -1 : 1); + } + } + } + } + return winding; +} + +static float stbtt__cuberoot( float x ) +{ + if (x<0) + return -(float) STBTT_pow(-x,1.0f/3.0f); + else + return (float) STBTT_pow( x,1.0f/3.0f); +} + +// x^3 + c*x^2 + b*x + a = 0 +static int stbtt__solve_cubic(float a, float b, float c, float* r) +{ + float s = -a / 3; + float p = b - a*a / 3; + float q = a * (2*a*a - 9*b) / 27 + c; + float p3 = p*p*p; + float d = q*q + 4*p3 / 27; + if (d >= 0) { + float z = (float) STBTT_sqrt(d); + float u = (-q + z) / 2; + float v = (-q - z) / 2; + u = stbtt__cuberoot(u); + v = stbtt__cuberoot(v); + r[0] = s + u + v; + return 1; + } else { + float u = (float) STBTT_sqrt(-p/3); + float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative + float m = (float) STBTT_cos(v); + float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; + r[0] = s + u * 2 * m; + r[1] = s - u * (m + n); + r[2] = s - u * (m - n); + + //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? + //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); + //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); + return 3; + } +} + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + float scale_x = scale, scale_y = scale; + int ix0,iy0,ix1,iy1; + int w,h; + unsigned char *data; + + // if one scale is 0, use same scale for both + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) return NULL; // if both scales are 0, return NULL + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); + + // if empty, return NULL + if (ix0 == ix1 || iy0 == iy1) + return NULL; + + ix0 -= padding; + iy0 -= padding; + ix1 += padding; + iy1 += padding; + + w = (ix1 - ix0); + h = (iy1 - iy0); + + if (width ) *width = w; + if (height) *height = h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + // invert for y-downwards bitmaps + scale_y = -scale_y; + + { + int x,y,i,j; + float *precompute; + stbtt_vertex *verts; + int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); + data = (unsigned char *) STBTT_malloc(w * h, info->userdata); + precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); + + for (i=0,j=num_verts-1; i < num_verts; j=i++) { + if (verts[i].type == STBTT_vline) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; + float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); + precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; + float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; + float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float len2 = bx*bx + by*by; + if (len2 != 0.0f) + precompute[i] = 1.0f / (bx*bx + by*by); + else + precompute[i] = 0.0f; + } else + precompute[i] = 0.0f; + } + + for (y=iy0; y < iy1; ++y) { + for (x=ix0; x < ix1; ++x) { + float val; + float min_dist = 999999.0f; + float sx = (float) x + 0.5f; + float sy = (float) y + 0.5f; + float x_gspace = (sx / scale_x); + float y_gspace = (sy / scale_y); + + int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path + + for (i=0; i < num_verts; ++i) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + + // check against every point here rather than inside line/curve primitives -- @TODO: wrong if multiple 'moves' in a row produce a garbage point, and given culling, probably more efficient to do within line/curve + float dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + if (verts[i].type == STBTT_vline) { + float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; + + // coarse culling against bbox + //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && + // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) + float dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; + STBTT_assert(i != 0); + if (dist < min_dist) { + // check position along line + // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) + // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) + float dx = x1-x0, dy = y1-y0; + float px = x0-sx, py = y0-sy; + // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy + // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve + float t = -(px*dx + py*dy) / (dx*dx + dy*dy); + if (t >= 0.0f && t <= 1.0f) + min_dist = dist; + } + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; + float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; + float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); + float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); + float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); + float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); + // coarse culling against bbox to avoid computing cubic unnecessarily + if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { + int num=0; + float ax = x1-x0, ay = y1-y0; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float mx = x0 - sx, my = y0 - sy; + float res[3],px,py,t,it; + float a_inv = precompute[i]; + if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula + float a = 3*(ax*bx + ay*by); + float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); + float c = mx*ax+my*ay; + if (a == 0.0) { // if a is 0, it's linear + if (b != 0.0) { + res[num++] = -c/b; + } + } else { + float discriminant = b*b - 4*a*c; + if (discriminant < 0) + num = 0; + else { + float root = (float) STBTT_sqrt(discriminant); + res[0] = (-b - root)/(2*a); + res[1] = (-b + root)/(2*a); + num = 2; // don't bother distinguishing 1-solution case, as code below will still work + } + } + } else { + float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point + float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; + float d = (mx*ax+my*ay) * a_inv; + num = stbtt__solve_cubic(b, c, d, res); + } + if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { + t = res[0], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { + t = res[1], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { + t = res[2], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + } + } + } + if (winding == 0) + min_dist = -min_dist; // if outside the shape, value is negative + val = onedge_value + pixel_dist_scale * min_dist; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + data[(y-iy0)*w+(x-ix0)] = (unsigned char) val; + } + } + STBTT_free(precompute, info->userdata); + STBTT_free(verts, info->userdata); + } + return data; +} + +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8); + stbtt_int32 off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + slen = ttUSHORT(fc+loc+12+8); + off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, + float pixel_height, unsigned char *pixels, int pw, int ph, + int first_char, int num_chars, stbtt_bakedchar *chardata) +{ + return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata); +} + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) +{ + return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); +} + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) +{ + return stbtt_GetNumberOfFonts_internal((unsigned char *) data); +} + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset) +{ + return stbtt_InitFont_internal(info, (unsigned char *) data, offset); +} + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags) +{ + return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags); +} + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2); +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#endif // STB_TRUETYPE_IMPLEMENTATION + + +// FULL VERSION HISTORY +// +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) allow user-defined fabs() replacement +// fix memory leak if fontsize=0.0 +// fix warning from duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// allow PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) +// also more precise AA rasterizer, except if shapes overlap +// remove need for STBTT_sort +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example +// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes +// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ +// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match +// non-oversampled; STBTT_POINT_SIZE for packed case only +// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling +// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) +// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID +// 0.8b (2014-07-07) fix a warning +// 0.8 (2014-05-25) fix a few more warnings +// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (stb) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/thirdparty/sokol/tests/functional/CMakeLists.txt b/thirdparty/sokol/tests/functional/CMakeLists.txt new file mode 100644 index 0000000..9a73c08 --- /dev/null +++ b/thirdparty/sokol/tests/functional/CMakeLists.txt @@ -0,0 +1,23 @@ +if (NOT ANDROID) + +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets/comsi.s3m DESTINATION ${CMAKE_BINARY_DIR}) +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets/comsi.s3m DESTINATION ${CMAKE_BINARY_DIR}/Debug) + +set(c_sources + sokol_log_test.c + sokol_args_test.c + sokol_audio_test.c + sokol_debugtext_test.c + sokol_fetch_test.c + sokol_gfx_test.c + sokol_gl_test.c + sokol_shape_test.c + sokol_color_test.c + sokol_spine_test.c + sokol_test.c +) +add_executable(sokol-test ${c_sources}) +target_link_libraries(sokol-test PUBLIC spine) +configure_c(sokol-test) + +endif() diff --git a/thirdparty/sokol/tests/functional/README.txt b/thirdparty/sokol/tests/functional/README.txt new file mode 100644 index 0000000..28fd594 --- /dev/null +++ b/thirdparty/sokol/tests/functional/README.txt @@ -0,0 +1 @@ +Test the public API behaviour with dummy backends. diff --git a/thirdparty/sokol/tests/functional/assets/comsi.s3m b/thirdparty/sokol/tests/functional/assets/comsi.s3m new file mode 100644 index 0000000..a33c90a Binary files /dev/null and b/thirdparty/sokol/tests/functional/assets/comsi.s3m differ diff --git a/thirdparty/sokol/tests/functional/assets/readme.txt b/thirdparty/sokol/tests/functional/assets/readme.txt new file mode 100644 index 0000000..568e95c --- /dev/null +++ b/thirdparty/sokol/tests/functional/assets/readme.txt @@ -0,0 +1,3 @@ +Spine examples taken from: + +https://github.com/EsotericSoftware/spine-runtimes/tree/4.1/examples diff --git a/thirdparty/sokol/tests/functional/force_dummy_backend.h b/thirdparty/sokol/tests/functional/force_dummy_backend.h new file mode 100644 index 0000000..ac1b673 --- /dev/null +++ b/thirdparty/sokol/tests/functional/force_dummy_backend.h @@ -0,0 +1,18 @@ +#if defined(SOKOL_GLES3) +#undef SOKOL_GLES3 +#endif +#if defined(SOKOL_GLCORE) +#undef SOKOL_GLCORE +#endif +#if defined(SOKOL_METAL) +#undef SOKOL_METAL +#endif +#if defined(SOKOL_D3D11) +#undef SOKOL_D3D11 +#endif +#if defined(SOKOL_WGPU) +#undef SOKOL_WGPU +#endif +#ifndef SOKOL_DUMMY_BACKEND +#define SOKOL_DUMMY_BACKEND +#endif diff --git a/thirdparty/sokol/tests/functional/sokol_args_test.c b/thirdparty/sokol/tests/functional/sokol_args_test.c new file mode 100644 index 0000000..e2928d1 --- /dev/null +++ b/thirdparty/sokol/tests/functional/sokol_args_test.c @@ -0,0 +1,302 @@ +//------------------------------------------------------------------------------ +// sokol-args-test.c +//------------------------------------------------------------------------------ +#define SOKOL_IMPL +#include "sokol_args.h" +#include "utest.h" + +#define T(b) EXPECT_TRUE(b) +#define TSTR(s0, s1) EXPECT_TRUE(0 == strcmp(s0,s1)) +#define NUM_ARGS(x) (sizeof(x)/sizeof(void*)) + +static char* argv_0[] = { "exe_name " }; +UTEST(sokol_args, init_shutdown) { + sargs_setup(&(sargs_desc){0}); + T(sargs_isvalid()); + T(_sargs.max_args == _SARGS_MAX_ARGS_DEF); + T(_sargs.args); + T(_sargs.buf_size == _SARGS_BUF_SIZE_DEF); + T(_sargs.buf_pos == 1); + T(_sargs.buf); + T(sargs_num_args() == 0); + TSTR(sargs_key_at(0), ""); + TSTR(sargs_value_at(0), ""); + sargs_shutdown(); + T(!sargs_isvalid()); + T(0 == _sargs.args); + T(0 == _sargs.buf); +} + +UTEST(sokol_args, no_args) { + sargs_setup(&(sargs_desc){ + .argc = NUM_ARGS(argv_0), + .argv = argv_0 + }); + T(sargs_isvalid()); + T(sargs_num_args() == 0); + TSTR(sargs_key_at(0), ""); + TSTR(sargs_value_at(0), ""); + T(-1 == sargs_find("bla")); + T(!sargs_exists("bla")); + TSTR(sargs_value("bla"), ""); + TSTR(sargs_value_def("bla", "blub"), "blub"); + sargs_shutdown(); +} + +static char* argv_1[] = { "exe_name", "kvp0=val0", "kvp1=val1", "kvp2=val2" }; +UTEST(sokol_args, simple_args) { + sargs_setup(&(sargs_desc){ + .argc = NUM_ARGS(argv_1), + .argv = argv_1, + }); + T(sargs_isvalid()); + T(sargs_num_args() == 3); + T(0 == sargs_find("kvp0")); + TSTR(sargs_value("kvp0"), "val0"); + TSTR(sargs_key_at(0), "kvp0"); + TSTR(sargs_value_at(0), "val0"); + T(1 == sargs_find("kvp1")); + TSTR(sargs_value("kvp1"), "val1"); + TSTR(sargs_key_at(1), "kvp1"); + TSTR(sargs_value_at(1), "val1"); + T(2 == sargs_find("kvp2")); + TSTR(sargs_value("kvp2"), "val2"); + TSTR(sargs_key_at(2), "kvp2"); + TSTR(sargs_value_at(2), "val2"); + T(_sargs.buf_pos == 31); + sargs_shutdown(); +} + +static char* argv_2[] = { "exe_name", "kvp0 = val0 ", " \tkvp1= val1", "kvp2 = val2 "}; +UTEST(sokol_args, simple_whitespace) { + sargs_setup(&(sargs_desc){ + .argc = NUM_ARGS(argv_2), + .argv = argv_2 + }); + T(sargs_isvalid()); + T(sargs_num_args() == 3); + T(0 == sargs_find("kvp0")); + TSTR(sargs_value("kvp0"), "val0"); + TSTR(sargs_key_at(0), "kvp0"); + TSTR(sargs_value_at(0), "val0"); + T(1 == sargs_find("kvp1")); + TSTR(sargs_value("kvp1"), "val1"); + TSTR(sargs_key_at(1), "kvp1"); + TSTR(sargs_value_at(1), "val1"); + T(2 == sargs_find("kvp2")); + TSTR(sargs_value("kvp2"), "val2"); + TSTR(sargs_key_at(2), "kvp2"); + TSTR(sargs_value_at(2), "val2"); + T(_sargs.buf_pos == 31); + sargs_shutdown(); +} + +static char* argv_4[] = { "exe_name", "kvp0 ", "=val0 ", " kvp1", "=", "val1", "kvp2 \t", "= val2 "}; +UTEST(sokol_args, standalone_separator) { + sargs_setup(&(sargs_desc){ + .argc = NUM_ARGS(argv_4), + .argv = argv_4 + }); + T(sargs_isvalid()); + T(sargs_num_args() == 3); + T(0 == sargs_find("kvp0")); + TSTR(sargs_value("kvp0"), "val0"); + TSTR(sargs_key_at(0), "kvp0"); + TSTR(sargs_value_at(0), "val0"); + T(1 == sargs_find("kvp1")); + TSTR(sargs_value("kvp1"), "val1"); + TSTR(sargs_key_at(1), "kvp1"); + TSTR(sargs_value_at(1), "val1"); + T(2 == sargs_find("kvp2")); + TSTR(sargs_value("kvp2"), "val2"); + TSTR(sargs_key_at(2), "kvp2"); + TSTR(sargs_value_at(2), "val2"); + T(_sargs.buf_pos == 31); + sargs_shutdown(); +} + +static char* argv_5[] = { "exe_name", "kvp0='bla bla'", "kvp1=' blub blub'", "kvp2='blob blob '"}; +UTEST(sokol_args, single_quotes) { + sargs_setup(&(sargs_desc){ + .argc = NUM_ARGS(argv_5), + .argv = argv_5 + }); + T(sargs_isvalid()); + T(sargs_num_args() == 3); + T(0 == sargs_find("kvp0")); + TSTR(sargs_value("kvp0"), "bla bla"); + TSTR(sargs_key_at(0), "kvp0"); + TSTR(sargs_value_at(0), "bla bla"); + T(1 == sargs_find("kvp1")); + TSTR(sargs_value("kvp1"), " blub blub"); + TSTR(sargs_key_at(1), "kvp1"); + TSTR(sargs_value_at(1), " blub blub"); + T(2 == sargs_find("kvp2")); + TSTR(sargs_value("kvp2"), "blob blob "); + TSTR(sargs_key_at(2), "kvp2"); + TSTR(sargs_value_at(2), "blob blob "); + sargs_shutdown(); +} + +static char* argv_6[] = { "exe_name", "kvp0=\"bla bla\"", "kvp1=\" blub blub\"", "kvp2=\"blob blob \""}; +UTEST(sokol_args, double_quotes) { + sargs_setup(&(sargs_desc){ + .argc = NUM_ARGS(argv_6), + .argv = argv_6 + }); + T(sargs_isvalid()); + T(sargs_num_args() == 3); + T(0 == sargs_find("kvp0")); + TSTR(sargs_value("kvp0"), "bla bla"); + TSTR(sargs_key_at(0), "kvp0"); + TSTR(sargs_value_at(0), "bla bla"); + T(1 == sargs_find("kvp1")); + TSTR(sargs_value("kvp1"), " blub blub"); + TSTR(sargs_key_at(1), "kvp1"); + TSTR(sargs_value_at(1), " blub blub"); + T(2 == sargs_find("kvp2")); + TSTR(sargs_value("kvp2"), "blob blob "); + TSTR(sargs_key_at(2), "kvp2"); + TSTR(sargs_value_at(2), "blob blob "); + sargs_shutdown(); +} + +static char* argv_7[] = { "exe_name", "kvp0='bla \"bla\"'", "kvp1=' \"blub blub\"'", "kvp2='blob \"blob\" '"}; +UTEST(sokol_args, double_in_single_quotes) { + sargs_setup(&(sargs_desc){ + .argc = NUM_ARGS(argv_7), + .argv = argv_7 + }); + T(sargs_isvalid()); + T(sargs_num_args() == 3); + T(0 == sargs_find("kvp0")); + TSTR(sargs_value("kvp0"), "bla \"bla\""); + TSTR(sargs_key_at(0), "kvp0"); + TSTR(sargs_value_at(0), "bla \"bla\""); + T(1 == sargs_find("kvp1")); + TSTR(sargs_value("kvp1"), " \"blub blub\""); + TSTR(sargs_key_at(1), "kvp1"); + TSTR(sargs_value_at(1), " \"blub blub\""); + T(2 == sargs_find("kvp2")); + TSTR(sargs_value("kvp2"), "blob \"blob\" "); + TSTR(sargs_key_at(2), "kvp2"); + TSTR(sargs_value_at(2), "blob \"blob\" "); + sargs_shutdown(); +} + +static char* argv_8[] = { "exe_name", "kvp0=\"bla 'bla'\"", "kvp1=\" 'blub blub'\"", "kvp2=\"blob 'blob' \""}; +UTEST(sokol_args, single_in_double_quotes) { + sargs_setup(&(sargs_desc){ + .argc = NUM_ARGS(argv_8), + .argv = argv_8 + }); + T(sargs_isvalid()); + T(sargs_num_args() == 3); + T(0 == sargs_find("kvp0")); + TSTR(sargs_value("kvp0"), "bla 'bla'"); + TSTR(sargs_key_at(0), "kvp0"); + TSTR(sargs_value_at(0), "bla 'bla'"); + T(1 == sargs_find("kvp1")); + TSTR(sargs_value("kvp1"), " 'blub blub'"); + TSTR(sargs_key_at(1), "kvp1"); + TSTR(sargs_value_at(1), " 'blub blub'"); + T(2 == sargs_find("kvp2")); + TSTR(sargs_value("kvp2"), "blob 'blob' "); + TSTR(sargs_key_at(2), "kvp2"); + TSTR(sargs_value_at(2), "blob 'blob' "); + sargs_shutdown(); +} + +static char* argv_9[] = { "exe_name", "kvp0='bla ", "bla'", "kvp1= ' blub", " blub'", "kvp2='blob blob '"}; +UTEST(sokol_args, test_split_quotes) { + sargs_setup(&(sargs_desc){ + .argc = NUM_ARGS(argv_9), + .argv = argv_9 + }); + T(sargs_isvalid()); + T(sargs_num_args() == 3); + T(0 == sargs_find("kvp0")); + TSTR(sargs_value("kvp0"), "bla bla"); + TSTR(sargs_key_at(0), "kvp0"); + TSTR(sargs_value_at(0), "bla bla"); + T(1 == sargs_find("kvp1")); + TSTR(sargs_value("kvp1"), " blub blub"); + TSTR(sargs_key_at(1), "kvp1"); + TSTR(sargs_value_at(1), " blub blub"); + T(2 == sargs_find("kvp2")); + TSTR(sargs_value("kvp2"), "blob blob "); + TSTR(sargs_key_at(2), "kvp2"); + TSTR(sargs_value_at(2), "blob blob "); + sargs_shutdown(); +} + +static char* argv_10[] = { "exe_name", "kvp0=\\\\val0\\nval1", "kvp1=val1\\rval2", "kvp2='val2\\tval3'" }; +UTEST(sokol_args, escape_sequence) { + sargs_setup(&(sargs_desc){ + .argc = NUM_ARGS(argv_10), + .argv = argv_10, + }); + T(sargs_isvalid()); + T(sargs_num_args() == 3); + T(0 == sargs_find("kvp0")); + TSTR(sargs_value("kvp0"), "\\val0\nval1"); + TSTR(sargs_key_at(0), "kvp0"); + TSTR(sargs_value_at(0), "\\val0\nval1"); + T(1 == sargs_find("kvp1")); + TSTR(sargs_value("kvp1"), "val1\rval2"); + TSTR(sargs_key_at(1), "kvp1"); + TSTR(sargs_value_at(1), "val1\rval2"); + T(2 == sargs_find("kvp2")); + TSTR(sargs_value("kvp2"), "val2\tval3"); + TSTR(sargs_key_at(2), "kvp2"); + TSTR(sargs_value_at(2), "val2\tval3"); + sargs_shutdown(); +} + +static char* argv_11[] = { "exe_name", "kvp0 kvp1", "kvp2 = val2", "kvp3", "kvp4=val4" }; +UTEST(sokol_args, key_only_args) { + sargs_setup(&(sargs_desc){ + .argc = NUM_ARGS(argv_11), + .argv = argv_11, + }); + T(sargs_isvalid()); + T(sargs_num_args() == 5); + T(0 == sargs_find("kvp0")); + T(1 == sargs_find("kvp1")); + T(2 == sargs_find("kvp2")); + T(3 == sargs_find("kvp3")); + T(4 == sargs_find("kvp4")) + T(-1 == sargs_find("kvp5")); + T(-1 == sargs_find("val2")); + T(-1 == sargs_find("val4")); + T(sargs_exists("kvp0")); + T(sargs_exists("kvp1")); + T(sargs_exists("kvp2")); + T(sargs_exists("kvp3")); + T(sargs_exists("kvp4")); + T(!sargs_exists("kvp5")); + TSTR(sargs_value("kvp0"), ""); + TSTR(sargs_value("kvp1"), ""); + TSTR(sargs_value("kvp2"), "val2"); + TSTR(sargs_value("kvp3"), ""); + TSTR(sargs_value("kvp4"), "val4"); + TSTR(sargs_value("kvp5"), ""); + TSTR(sargs_value_def("kvp0", "bla0"), "bla0"); + TSTR(sargs_value_def("kvp1", "bla1"), "bla1"); + TSTR(sargs_value_def("kvp2", "bla2"), "val2"); + TSTR(sargs_value_def("kvp3", "bla3"), "bla3"); + TSTR(sargs_value_def("kvp4", "bla4"), "val4"); + TSTR(sargs_value_def("kvp5", "bla5"), "bla5"); + TSTR(sargs_key_at(0), "kvp0"); + TSTR(sargs_key_at(1), "kvp1"); + TSTR(sargs_key_at(2), "kvp2"); + TSTR(sargs_key_at(3), "kvp3"); + TSTR(sargs_key_at(4), "kvp4"); + TSTR(sargs_key_at(5), ""); + TSTR(sargs_value_at(0), ""); + TSTR(sargs_value_at(1), ""); + TSTR(sargs_value_at(2), "val2"); + TSTR(sargs_value_at(3), ""); + TSTR(sargs_value_at(4), "val4"); + TSTR(sargs_value_at(5), ""); +} diff --git a/thirdparty/sokol/tests/functional/sokol_audio_test.c b/thirdparty/sokol/tests/functional/sokol_audio_test.c new file mode 100644 index 0000000..a607d29 --- /dev/null +++ b/thirdparty/sokol/tests/functional/sokol_audio_test.c @@ -0,0 +1,99 @@ +//------------------------------------------------------------------------------ +// sokol_audio_test.c +//------------------------------------------------------------------------------ +#define SOKOL_IMPL +#ifndef SOKOL_DUMMY_BACKEND +#define SOKOL_DUMMY_BACKEND +#endif +#include "sokol_audio.h" +#include "utest.h" + +#define T(b) EXPECT_TRUE(b) + +UTEST(sokol_audio, ring_buffer) { + _saudio_ring_t rb; + _saudio_ring_init(&rb, 4); + T(0 == rb.head); + T(0 == rb.tail); + T(5 == rb.num); + T(!_saudio_ring_full(&rb)); + T(_saudio_ring_empty(&rb)); + T(0 == _saudio_ring_count(&rb)); + + _saudio_ring_enqueue(&rb, 23); + T(1 == rb.head); + T(0 == rb.tail); + T(!_saudio_ring_full(&rb)); + T(!_saudio_ring_empty(&rb)); + T(1 == _saudio_ring_count(&rb)); + + T(23 == _saudio_ring_dequeue(&rb)); + T(1 == rb.head); + T(1 == rb.tail); + T(!_saudio_ring_full(&rb)); + T(_saudio_ring_empty(&rb)); + T(0 == _saudio_ring_count(&rb)); + + _saudio_ring_enqueue(&rb, 23); + _saudio_ring_enqueue(&rb, 46); + T(3 == rb.head); + T(1 == rb.tail); + T(!_saudio_ring_full(&rb)); + T(!_saudio_ring_empty(&rb)); + T(2 == _saudio_ring_count(&rb)); + T(23 == _saudio_ring_dequeue(&rb)); + T(46 == _saudio_ring_dequeue(&rb)); + T(3 == rb.head); + T(3 == rb.tail); + T(!_saudio_ring_full(&rb)); + T(_saudio_ring_empty(&rb)); + T(0 == _saudio_ring_count(&rb)); + + _saudio_ring_enqueue(&rb, 12); + _saudio_ring_enqueue(&rb, 34); + _saudio_ring_enqueue(&rb, 56); + _saudio_ring_enqueue(&rb, 78); + T(2 == rb.head); + T(3 == rb.tail); + T(_saudio_ring_full(&rb)); + T(!_saudio_ring_empty(&rb)); + T(4 == _saudio_ring_count(&rb)); + T(12 == _saudio_ring_dequeue(&rb)); + T(2 == rb.head); + T(4 == rb.tail); + T(!_saudio_ring_full(&rb)); + T(!_saudio_ring_empty(&rb)); + T(3 == _saudio_ring_count(&rb)); + _saudio_ring_enqueue(&rb, 90); + T(3 == rb.head); + T(4 == rb.tail); + T(_saudio_ring_full(&rb)); + T(!_saudio_ring_empty(&rb)); + T(4 == _saudio_ring_count(&rb)); + T(34 == _saudio_ring_dequeue(&rb)); + T(56 == _saudio_ring_dequeue(&rb)); + T(78 == _saudio_ring_dequeue(&rb)); + T(90 == _saudio_ring_dequeue(&rb)); + T(!_saudio_ring_full(&rb)); + T(_saudio_ring_empty(&rb)); + T(3 == rb.head); + T(3 == rb.tail); +} + +UTEST(saudio, api_test) { + saudio_setup(&(saudio_desc){ + .sample_rate = 22050, + .num_channels = 2, + .buffer_frames = 8192, + .num_packets = 128, + .packet_frames = 8192 / 128, + .user_data = (void*)12345 + }); + T(saudio_isvalid()); + T(saudio_query_desc().sample_rate == 22050); + T(saudio_userdata() == (void*)12345); + T(saudio_sample_rate() == 22050); + T(saudio_channels() == 2); + T(saudio_expect() == 8192); + T(saudio_buffer_frames() == 8192); +} diff --git a/thirdparty/sokol/tests/functional/sokol_color_test.c b/thirdparty/sokol/tests/functional/sokol_color_test.c new file mode 100644 index 0000000..c3c1345 --- /dev/null +++ b/thirdparty/sokol/tests/functional/sokol_color_test.c @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------ +// sokol_color_test.c +//------------------------------------------------------------------------------ +#include "sokol_gfx.h" +#define SOKOL_COLOR_IMPL +#include "sokol_color.h" +#include "utest.h" +#include + +#define T(b) EXPECT_TRUE(b) +#define TFLT(f0,f1,epsilon) {T(fabs((f0)-(f1))<=(epsilon));} + +UTEST(sokol_color, make_color) { + const sg_color c0 = sg_make_color_4b(255, 127, 0, 255); + TFLT(c0.r, 1.0f, 0.01f); + TFLT(c0.g, 0.5f, 0.01f); + TFLT(c0.b, 0.0f, 0.01f); + TFLT(c0.a, 1.0f, 0.01f); + const sg_color c1 = sg_make_color_1i(SG_BLACK_RGBA32); + TFLT(c1.r, 0.0f, 0.01f); + TFLT(c1.g, 0.0f, 0.01f); + TFLT(c1.b, 0.0f, 0.01f); + TFLT(c1.a, 1.0f, 0.01f); + const sg_color c2 = sg_make_color_1i(SG_GREEN_RGBA32); + TFLT(c2.r, 0.0f, 0.01f); + TFLT(c2.g, 1.0f, 0.01f); + TFLT(c2.b, 0.0f, 0.01f); + TFLT(c2.a, 1.0f, 0.01f); + const sg_color c3 = sg_make_color_1i(SG_RED_RGBA32); + TFLT(c3.r, 1.0f, 0.01f); + TFLT(c3.g, 0.0f, 0.01f); + TFLT(c3.b, 0.0f, 0.01f); + TFLT(c3.a, 1.0f, 0.01f); + const sg_color c4 = sg_make_color_1i(SG_BLUE_RGBA32); + TFLT(c4.r, 0.0f, 0.01f); + TFLT(c4.g, 0.0f, 0.01f); + TFLT(c4.b, 1.0f, 0.01f); + TFLT(c4.a, 1.0f, 0.01f); +} + +UTEST(sokol_color, lerp) { + const sg_color c0 = sg_color_lerp(&sg_red, &sg_green, 0.5f); + TFLT(c0.r, 0.5f, 0.001f); + TFLT(c0.g, 0.5f, 0.001f); + TFLT(c0.b, 0.0f, 0.001f); + TFLT(c0.a, 1.0f, 0.001f); + const sg_color c1 = sg_color_lerp_precise(&sg_red, &sg_green, 0.5f); + TFLT(c1.r, 0.5f, 0.001f); + TFLT(c1.g, 0.5f, 0.001f); + TFLT(c1.b, 0.0f, 0.001f); + TFLT(c1.a, 1.0f, 0.001f); +} + +UTEST(sokol_color, multiply) { + const sg_color c0 = sg_color_multiply(&sg_red, 0.5f); + TFLT(c0.r, 0.5f, 0.001f); + TFLT(c0.g, 0.0f, 0.001f); + TFLT(c0.b, 0.0f, 0.001f); + TFLT(c0.a, 0.5f, 0.001f); + const sg_color c1 = sg_color_multiply(&sg_green, 0.5f); + TFLT(c1.r, 0.0f, 0.001f); + TFLT(c1.g, 0.5f, 0.001f); + TFLT(c1.b, 0.0f, 0.001f); + TFLT(c1.a, 0.5f, 0.001f); + const sg_color c2 = sg_color_multiply(&sg_blue, 0.5f); + TFLT(c2.r, 0.0f, 0.001f); + TFLT(c2.g, 0.0f, 0.001f); + TFLT(c2.b, 0.5f, 0.001f); + TFLT(c2.a, 0.5f, 0.001f); +} diff --git a/thirdparty/sokol/tests/functional/sokol_debugtext_test.c b/thirdparty/sokol/tests/functional/sokol_debugtext_test.c new file mode 100644 index 0000000..205fd53 --- /dev/null +++ b/thirdparty/sokol/tests/functional/sokol_debugtext_test.c @@ -0,0 +1,520 @@ +//------------------------------------------------------------------------------ +// sokol-debugtext-test.c +// For best results, run with ASAN and UBSAN. +//------------------------------------------------------------------------------ +#include "sokol_gfx.h" +#include "sokol_log.h" +#define SOKOL_DEBUGTEXT_IMPL +#include "sokol_debugtext.h" +#include "utest.h" + +#define T(b) EXPECT_TRUE(b) +#define TFLT(f0,f1) {T(fabs((f0)-(f1))<=(0.000001));} + +static void init(void) { + sg_setup(&(sg_desc){ .logger = { .func = slog_func }}); + sdtx_setup(&(sdtx_desc_t){ .logger = { .func = slog_func }}); +} + +static void init_with(const sdtx_desc_t* desc) { + sg_setup(&(sg_desc){0}); + sdtx_setup(desc); +} + +static void shutdown(void) { + sdtx_shutdown(); + sg_shutdown(); +} + +UTEST(sokol_debugtext, default_init_shutdown) { + init(); + T(_sdtx.init_cookie == _SDTX_INIT_COOKIE); + T(_sdtx.desc.context_pool_size == _SDTX_DEFAULT_CONTEXT_POOL_SIZE); + T(_sdtx.desc.printf_buf_size == _SDTX_DEFAULT_PRINTF_BUF_SIZE); + T(_sdtx.desc.context.char_buf_size == _SDTX_DEFAULT_CHAR_BUF_SIZE); + T(_sdtx.desc.context.canvas_width == _SDTX_DEFAULT_CANVAS_WIDTH); + T(_sdtx.desc.context.canvas_height == _SDTX_DEFAULT_CANVAS_HEIGHT); + T(_sdtx.desc.context.tab_width == _SDTX_DEFAULT_TAB_WIDTH); + T(_sdtx.desc.context.color_format == 0); + T(_sdtx.desc.context.depth_format == 0); + T(_sdtx.desc.context.sample_count == 0); + for (int i = 0; i < SDTX_MAX_FONTS; i++) { + T(_sdtx.desc.fonts[i].data.ptr == 0); + T(_sdtx.desc.fonts[i].data.size == 0); + T(_sdtx.desc.fonts[i].first_char == 0); + T(_sdtx.desc.fonts[i].last_char == 0); + } + T(_sdtx.font_img.id != SG_INVALID_ID); + T(_sdtx.shader.id != SG_INVALID_ID); + T(_sdtx.fmt_buf_size == (_SDTX_DEFAULT_CHAR_BUF_SIZE + 1)); + T(_sdtx.fmt_buf); + T(_sdtx.def_ctx_id.id != 0); + T(_sdtx.def_ctx_id.id == _sdtx.cur_ctx_id.id); + T(_sdtx.cur_ctx == _sdtx_lookup_context(_sdtx.cur_ctx_id.id)); + T(_sdtx.cur_ctx->desc.char_buf_size == _sdtx.desc.context.char_buf_size); + T(_sdtx.cur_ctx->desc.canvas_width == _sdtx.desc.context.canvas_width); + T(_sdtx.cur_ctx->desc.canvas_height == _sdtx.desc.context.canvas_height); + T(_sdtx.cur_ctx->desc.tab_width == _sdtx.desc.context.tab_width); + T(_sdtx.cur_ctx->desc.color_format == 0); + T(_sdtx.cur_ctx->desc.depth_format == 0); + T(_sdtx.cur_ctx->desc.sample_count == 0); + T(_sdtx.cur_ctx->vertices.cap == _SDTX_DEFAULT_CHAR_BUF_SIZE * 6); + T(_sdtx.cur_ctx->vertices.next == 0); + T(_sdtx.cur_ctx->vertices.ptr); + T(_sdtx.cur_ctx->commands.cap == _SDTX_DEFAULT_MAX_COMMANDS); + T(_sdtx.cur_ctx->commands.next == 1); + T(_sdtx.cur_ctx->commands.ptr); + T(_sdtx.cur_ctx->vbuf.id != 0); + T(_sdtx.cur_ctx->pip.id != 0); + TFLT(_sdtx.cur_ctx->canvas_size.x, 640.0f); + TFLT(_sdtx.cur_ctx->canvas_size.y, 480.0f); + TFLT(_sdtx.cur_ctx->glyph_size.x, 8.0f / 640.0f); + TFLT(_sdtx.cur_ctx->glyph_size.y, 8.0f / 480.0f); + TFLT(_sdtx.cur_ctx->origin.x, 0.0f); + TFLT(_sdtx.cur_ctx->origin.y, 0.0f); + TFLT(_sdtx.cur_ctx->pos.x, 0.0f); + TFLT(_sdtx.cur_ctx->pos.y, 0.0f); + TFLT(_sdtx.cur_ctx->tab_width, 4.0f); + T(_sdtx.cur_ctx->color == _SDTX_DEFAULT_COLOR); + T(_sdtx.context_pool.contexts); + T(_sdtx.context_pool.pool.size == (_SDTX_DEFAULT_CONTEXT_POOL_SIZE + 1)); + shutdown(); + T(_sdtx.init_cookie == 0); +} + +UTEST(sokol_debugtext, init_with_params) { + init_with(&(sdtx_desc_t){ + .context_pool_size = 2, + .printf_buf_size = 128, + .context = { + .char_buf_size = 256, + .canvas_width = 320, + .canvas_height = 200, + .tab_width = 8, + .color_format = SG_PIXELFORMAT_RGBA8, + .depth_format = SG_PIXELFORMAT_DEPTH_STENCIL, + .sample_count = 4, + } + }); + T(_sdtx.init_cookie == _SDTX_INIT_COOKIE); + T(_sdtx.desc.context_pool_size == 2); + T(_sdtx.desc.printf_buf_size == 128); + T(_sdtx.desc.context.char_buf_size == 256); + T(_sdtx.desc.context.canvas_width == 320); + T(_sdtx.desc.context.canvas_height == 200); + T(_sdtx.desc.context.tab_width == 8); + T(_sdtx.desc.context.color_format == SG_PIXELFORMAT_RGBA8); + T(_sdtx.desc.context.depth_format == SG_PIXELFORMAT_DEPTH_STENCIL); + T(_sdtx.desc.context.sample_count == 4); + T(_sdtx.fmt_buf_size == 129); + T(_sdtx.cur_ctx->desc.char_buf_size == _sdtx.desc.context.char_buf_size); + T(_sdtx.cur_ctx->desc.canvas_width == _sdtx.desc.context.canvas_width); + T(_sdtx.cur_ctx->desc.canvas_height == _sdtx.desc.context.canvas_height); + T(_sdtx.cur_ctx->desc.tab_width == _sdtx.desc.context.tab_width); + T(_sdtx.cur_ctx->desc.color_format == SG_PIXELFORMAT_RGBA8); + T(_sdtx.cur_ctx->desc.depth_format == SG_PIXELFORMAT_DEPTH_STENCIL); + T(_sdtx.cur_ctx->desc.sample_count == 4); + T(_sdtx.cur_ctx->vertices.cap == (256 * 6)); + TFLT(_sdtx.cur_ctx->canvas_size.x, 320.0f); + TFLT(_sdtx.cur_ctx->canvas_size.y, 200.0f); + TFLT(_sdtx.cur_ctx->glyph_size.x, 8.0f / 320.0f); + TFLT(_sdtx.cur_ctx->glyph_size.y, 8.0f / 200.0f); + TFLT(_sdtx.cur_ctx->tab_width, 8.0f); + T(_sdtx.context_pool.pool.size == 3); + shutdown(); +} + +UTEST(sokol_debugtext, make_destroy_context) { + init(); + sdtx_context ctx_id = sdtx_make_context(&(sdtx_context_desc_t){ + .char_buf_size = 64, + .canvas_width = 1024, + .canvas_height = 768, + .tab_width = 3, + .color_format = SG_PIXELFORMAT_RGBA32F, + .sample_count = 2 + }); + T(ctx_id.id != 0); + T(ctx_id.id != _sdtx.cur_ctx_id.id); + _sdtx_context_t* ctx = _sdtx_lookup_context(ctx_id.id); + T(ctx); + T(ctx != _sdtx.cur_ctx); + T(ctx->desc.char_buf_size == 64); + T(ctx->desc.canvas_width == 1024); + T(ctx->desc.canvas_height == 768); + T(ctx->desc.tab_width == 3); + T(ctx->desc.color_format == SG_PIXELFORMAT_RGBA32F); + T(ctx->desc.depth_format == 0); + T(ctx->desc.sample_count == 2); + T(ctx->vertices.ptr); + T(ctx->vertices.next == 0); + T(ctx->vertices.cap == (64 * 6)); + TFLT(ctx->canvas_size.x, 1024.0f); + TFLT(ctx->canvas_size.y, 768.0f); + TFLT(ctx->glyph_size.x, 8.0f / 1024.0f); + TFLT(ctx->glyph_size.y, 8.0f / 768.0f); + TFLT(ctx->tab_width, 3.0f); + sdtx_destroy_context(ctx_id); + T(0 == _sdtx_lookup_context(ctx_id.id)); + T(ctx->desc.char_buf_size == 0); + T(ctx->vertices.ptr == 0); + shutdown(); +} + +UTEST(sokol_debugtext, get_default_context) { + // getting the default context must always return SDTX_DEFAULT_CONTEXT + init(); + T(sdtx_get_context().id == SDTX_DEFAULT_CONTEXT.id); + shutdown(); +} + +UTEST(sokol_debugtext, set_get_context) { + init(); + sdtx_context ctx_id = sdtx_make_context(&(sdtx_context_desc_t){ 0 }); + T(ctx_id.id != 0); + T(ctx_id.id != _sdtx.cur_ctx_id.id); + sdtx_set_context(ctx_id); + T(sdtx_get_context().id == ctx_id.id); + T(ctx_id.id == _sdtx.cur_ctx_id.id); + const _sdtx_context_t* ctx = _sdtx_lookup_context(ctx_id.id); + T(ctx == _sdtx.cur_ctx); + sdtx_set_context(SDTX_DEFAULT_CONTEXT); + T(sdtx_get_context().id == SDTX_DEFAULT_CONTEXT.id); + T(_sdtx.cur_ctx); + T(ctx != _sdtx.cur_ctx); + T(_sdtx.cur_ctx == _sdtx_lookup_context(_sdtx.def_ctx_id.id)); + shutdown(); +} + +UTEST(sokol_debugtext, destroy_default_context) { + // destroying the default context is not allowed + init(); + sdtx_context def_ctx_id = _sdtx.def_ctx_id; + T(def_ctx_id.id == _sdtx.cur_ctx_id.id); + sdtx_destroy_context(def_ctx_id); + T(def_ctx_id.id == _sdtx.def_ctx_id.id); + T(def_ctx_id.id == _sdtx.cur_ctx_id.id); + T(_sdtx.cur_ctx); + shutdown(); +} + +UTEST(sokol_debugtext, destroy_current_context) { + // destroying the current context has the same effect + // as setting a current context with an invalid context handle + init(); + sdtx_context ctx_id = sdtx_make_context(&(sdtx_context_desc_t){ 0 }); + sdtx_set_context(ctx_id); + T(sdtx_get_context().id == ctx_id.id); + T(ctx_id.id == _sdtx.cur_ctx_id.id); + T(_sdtx_lookup_context(ctx_id.id) == _sdtx.cur_ctx); + sdtx_destroy_context(ctx_id); + T(_sdtx.cur_ctx_id.id == ctx_id.id); + T(_sdtx.cur_ctx == 0); + T(sdtx_get_context().id == ctx_id.id); + shutdown(); +} + +UTEST(sokol_debugtext, ignore_invalid_context_handle) { + // trying to render with an invalid context handle must not crash, + // instead ignore all operations + init(); + sdtx_context ctx_id = sdtx_make_context(&(sdtx_context_desc_t){ 0 }); + sdtx_set_context(ctx_id); + sdtx_destroy_context(ctx_id); + T(0 == _sdtx.cur_ctx); + T(sdtx_get_context().id == ctx_id.id); + sdtx_font(0); + sdtx_canvas(100.0f, 200.0f); + sdtx_origin(10.0f, 10.0f); + sdtx_home(); + sdtx_pos(1.0f, 2.0f); + sdtx_pos_x(1.0f); + sdtx_pos_y(2.0f); + sdtx_move(2.0f, 3.0f); + sdtx_move_x(2.0f); + sdtx_move_y(3.0f); + sdtx_crlf(); + sdtx_color3b(255, 255, 255); + sdtx_color3f(1.0f, 1.0f, 1.0f); + sdtx_color4b(255, 255, 255, 255); + sdtx_color4f(1.0f, 1.0f, 1.0f, 1.0f); + sdtx_color1i(0xFFFFFFFF); + sdtx_putc('A'); + sdtx_puts("Hello World!"); + sdtx_putr("Hello World!", 5); + sdtx_printf("Hello World %d %d %d", 1, 2, 3); + shutdown(); +} + +UTEST(sokol_debugtext, set_font) { + init(); + T(0 == _sdtx.cur_ctx->cur_font); + sdtx_font(1); + T(1 == _sdtx.cur_ctx->cur_font); + sdtx_font(2); + T(2 == _sdtx.cur_ctx->cur_font); + shutdown(); +} + +UTEST(sokol_debugtext, set_canvas) { + init(); + sdtx_origin(10.0f, 11.0f); + sdtx_pos(1.0f, 2.0f); + sdtx_canvas(320.0f, 200.0f); + TFLT(_sdtx.cur_ctx->canvas_size.x, 320.0f); + TFLT(_sdtx.cur_ctx->canvas_size.y, 200.0f); + TFLT(_sdtx.cur_ctx->glyph_size.x, 8.0f / 320.0f); + TFLT(_sdtx.cur_ctx->glyph_size.y, 8.0f / 200.0f); + // origin and pos must be reset to 0 when canvas is set + TFLT(_sdtx.cur_ctx->origin.x, 0.0f); + TFLT(_sdtx.cur_ctx->origin.y, 0.0f); + TFLT(_sdtx.cur_ctx->pos.x, 0.0f); + TFLT(_sdtx.cur_ctx->pos.y, 0.0f); + shutdown(); +} + +UTEST(sokol_debugtext, set_origin) { + init(); + sdtx_origin(10.0f, 20.0f); + TFLT(_sdtx.cur_ctx->origin.x, 10.0f); + TFLT(_sdtx.cur_ctx->origin.y, 20.0f); + shutdown(); +} + +UTEST(sokol_debugtext, cursor_movement) { + init(); + sdtx_pos(1.0f, 2.0f); + TFLT(_sdtx.cur_ctx->pos.x, 1.0f); + TFLT(_sdtx.cur_ctx->pos.y, 2.0f); + sdtx_pos_x(5.0f); + TFLT(_sdtx.cur_ctx->pos.x, 5.0f); + TFLT(_sdtx.cur_ctx->pos.y, 2.0f); + sdtx_pos_y(6.0f); + TFLT(_sdtx.cur_ctx->pos.x, 5.0f); + TFLT(_sdtx.cur_ctx->pos.y, 6.0f); + sdtx_move(-1.0f, -3.0f); + TFLT(_sdtx.cur_ctx->pos.x, 4.0f); + TFLT(_sdtx.cur_ctx->pos.y, 3.0f); + sdtx_move_x(+1.0f); + TFLT(_sdtx.cur_ctx->pos.x, 5.0f); + TFLT(_sdtx.cur_ctx->pos.y, 3.0f); + sdtx_move_y(+3.0f); + TFLT(_sdtx.cur_ctx->pos.x, 5.0f); + TFLT(_sdtx.cur_ctx->pos.y, 6.0f); + sdtx_crlf(); + TFLT(_sdtx.cur_ctx->pos.x, 0.0f); + TFLT(_sdtx.cur_ctx->pos.y, 7.0f); + sdtx_pos(20.0f, 30.0f); + sdtx_home(); + TFLT(_sdtx.cur_ctx->pos.x, 0.0f); + TFLT(_sdtx.cur_ctx->pos.y, 0.0f); + shutdown(); +} + +UTEST(sokol_debugtext, set_color) { + init(); + T(_sdtx.cur_ctx->color == _SDTX_DEFAULT_COLOR); + sdtx_color3b(255, 127, 0); + T(_sdtx.cur_ctx->color == 0xFF007FFF); + sdtx_color4b(0, 127, 255, 255); + T(_sdtx.cur_ctx->color == 0xFFFF7F00); + sdtx_color3f(1.0f, 0.5f, 0.0f); + T(_sdtx.cur_ctx->color == 0xFF007FFF); + sdtx_color4f(0.0f, 0.5f, 1.0f, 1.0f); + T(_sdtx.cur_ctx->color == 0xFFFF7F00); + sdtx_color1i(0xFF000000); + T(_sdtx.cur_ctx->color == 0xFF000000); + shutdown(); +} + +UTEST(sokol_debugtext, vertex_overflow) { + // overflowing the vertex buffer must not crash + init_with(&(sdtx_desc_t){ + .context.char_buf_size = 8, + }); + for (int i = 0; i < 32; i++) { + sdtx_putc('A'); + } + sdtx_puts("1234567890"); + sdtx_putr("1234567890", 5); + sdtx_printf("Hello World %d!\n", 12); + T(_sdtx.cur_ctx->vertices.next == _sdtx.cur_ctx->vertices.cap); + shutdown(); +} + +UTEST(sokol_debugtext, context_overflow) { + // creating too many contexts should not crash + init_with(&(sdtx_desc_t){ + .context_pool_size = 4, + }); + T(_sdtx.context_pool.pool.size == 5); + // one slot is taken by the default context + sdtx_context ctx[4]; + for (int i = 0; i < 4; i++) { + ctx[i] = sdtx_make_context(&(sdtx_context_desc_t){ 0 }); + if (i < 3) { + T(ctx[i].id != 0); + } + else { + T(ctx[i].id == 0); + } + } + // destroying an invalid context should not crash + for (int i = 0; i < 4; i++) { + sdtx_destroy_context(ctx[i]); + } + shutdown(); +} + +UTEST(sokol_debugtext, printf_overflow) { + // overflowing the printf formatting buffer should not crash + init_with(&(sdtx_desc_t){ + .printf_buf_size = 8 + }); + T(9 == _sdtx.fmt_buf_size); + T(16 == sdtx_printf("Hello %d\n", 123456789)); + T('H' == _sdtx.fmt_buf[0]) + T('e' == _sdtx.fmt_buf[1]) + T('l' == _sdtx.fmt_buf[2]) + T('l' == _sdtx.fmt_buf[3]) + T('o' == _sdtx.fmt_buf[4]) + T(' ' == _sdtx.fmt_buf[5]) + T('1' == _sdtx.fmt_buf[6]) + T('2' == _sdtx.fmt_buf[7]) + T(0 == _sdtx.fmt_buf[8]) + shutdown(); +} + +UTEST(sokol_debugtext, rewind_after_draw) { + // calling sdtx_draw() must rewind the cursor position, font and + // vertex pointer, to keep canvas size and origin as is + init(); + sdtx_canvas(256, 128); + TFLT(_sdtx.cur_ctx->canvas_size.x, 256); + TFLT(_sdtx.cur_ctx->canvas_size.y, 128); + sdtx_origin(5, 5); + TFLT(_sdtx.cur_ctx->origin.x, 5); + TFLT(_sdtx.cur_ctx->origin.y, 5); + sdtx_pos(10, 20); + TFLT(_sdtx.cur_ctx->pos.x, 10); + TFLT(_sdtx.cur_ctx->pos.y, 20); + sdtx_font(3); + T(_sdtx.cur_ctx->cur_font == 3); + sdtx_printf("Hello World!\n"); + T(_sdtx.cur_ctx->vertices.next != 0); + sg_begin_pass(&(sg_pass){ + .swapchain = { + .width = 256, + .height = 256, + .sample_count = 1, + .color_format = SG_PIXELFORMAT_RGBA8, + .depth_format = SG_PIXELFORMAT_DEPTH_STENCIL, + } + }); + sdtx_draw(); + sg_end_pass(); + sg_commit(); + TFLT(_sdtx.cur_ctx->canvas_size.x, 256); + TFLT(_sdtx.cur_ctx->canvas_size.y, 128); + TFLT(_sdtx.cur_ctx->origin.x, 5); + TFLT(_sdtx.cur_ctx->origin.y, 5); + TFLT(_sdtx.cur_ctx->pos.x, 0); + TFLT(_sdtx.cur_ctx->pos.x, 0); + T(_sdtx.cur_ctx->cur_font == 0); + T(_sdtx.cur_ctx->vertices.next == 0); + shutdown(); +} + +UTEST(sokol_debugtext, putr) { + // test if sdtx_putr() draws the right amount of characters + init(); + int start_index = _sdtx.cur_ctx->vertices.next; + sdtx_putr("Hello World!", 5); + T((5 * 6) == (_sdtx.cur_ctx->vertices.next - start_index)); + + start_index = _sdtx.cur_ctx->vertices.next; + sdtx_putr("Hello!\n\n\n\n\n\n\n\n\n\n\n", 10); + // NOTE: the \n's don't result in rendered vertices + T((6 * 6) == (_sdtx.cur_ctx->vertices.next - start_index)); + shutdown(); +} + +UTEST(sokol_debugtext, default_context) { + init(); + T(sdtx_default_context().id == SDTX_DEFAULT_CONTEXT.id); + shutdown(); +} + +// switching layers without any text inbetween should not advance the current draw command +UTEST(sokol_debug_text, empty_layers) { + init(); + T(_sdtx.cur_ctx->commands.next == 1); + T(_sdtx.cur_ctx->commands.ptr[0].layer_id == 0); + sdtx_layer(1); + T(_sdtx.cur_ctx->commands.next == 1); + T(_sdtx.cur_ctx->commands.ptr[0].layer_id == 1); + sdtx_layer(2); + T(_sdtx.cur_ctx->commands.next == 1); + T(_sdtx.cur_ctx->commands.ptr[0].layer_id == 2); + sdtx_layer(0); + T(_sdtx.cur_ctx->commands.next == 1); + T(_sdtx.cur_ctx->commands.ptr[0].layer_id == 0); + shutdown(); +} + +// switching layers with text inbetween should advance the current draw command +UTEST(sokol_debug_text, non_empty_layers) { + init(); + T(_sdtx.cur_ctx->commands.next == 1); + T(_sdtx.cur_ctx->commands.ptr[0].layer_id == 0); + T(_sdtx.cur_ctx->commands.ptr[0].first_vertex == 0); + T(_sdtx.cur_ctx->commands.ptr[0].num_vertices == 0); + sdtx_puts("123"); + T(_sdtx.cur_ctx->commands.next == 1); + T(_sdtx.cur_ctx->commands.ptr[0].layer_id == 0); + T(_sdtx.cur_ctx->commands.ptr[0].first_vertex == 0); + T(_sdtx.cur_ctx->commands.ptr[0].num_vertices == (3 * 6)); + sdtx_layer(1); + sdtx_puts("1234"); + T(_sdtx.cur_ctx->commands.next == 2); + T(_sdtx.cur_ctx->commands.ptr[1].layer_id == 1); + T(_sdtx.cur_ctx->commands.ptr[1].first_vertex == (3 * 6)); + T(_sdtx.cur_ctx->commands.ptr[1].num_vertices == (4 * 6)); + // switching to same layer should not start a new draw commands + sdtx_layer(1); + sdtx_puts("12345"); + T(_sdtx.cur_ctx->commands.next == 2); + T(_sdtx.cur_ctx->commands.ptr[1].layer_id == 1); + T(_sdtx.cur_ctx->commands.ptr[1].first_vertex == (3 * 6)); + T(_sdtx.cur_ctx->commands.ptr[1].num_vertices == (9 * 6)); + sdtx_layer(0); + sdtx_puts("123456"); + T(_sdtx.cur_ctx->commands.next == 3); + T(_sdtx.cur_ctx->commands.ptr[2].layer_id == 0); + T(_sdtx.cur_ctx->commands.ptr[2].first_vertex == (12 * 6)); + T(_sdtx.cur_ctx->commands.ptr[2].num_vertices == (6 * 6)); + shutdown(); +} + +UTEST(sokol_debug_text, command_buffer_overflow) { + init_with(&(sdtx_desc_t){ + .context = { + .max_commands = 4 + } + }); + sdtx_puts("0"); + T(_sdtx.cur_ctx->commands.next == 1); + sdtx_layer(1); + sdtx_puts("1"); + T(_sdtx.cur_ctx->commands.next == 2); + sdtx_layer(2); + sdtx_puts("2"); + T(_sdtx.cur_ctx->commands.next == 3); + sdtx_layer(3); + sdtx_puts("3"); + T(_sdtx.cur_ctx->commands.next == 4); + // from here on should fail + sdtx_layer(4); + sdtx_puts("4"); + T(_sdtx.cur_ctx->commands.next == 4); +} \ No newline at end of file diff --git a/thirdparty/sokol/tests/functional/sokol_fetch_test.c b/thirdparty/sokol/tests/functional/sokol_fetch_test.c new file mode 100644 index 0000000..8318a9e --- /dev/null +++ b/thirdparty/sokol/tests/functional/sokol_fetch_test.c @@ -0,0 +1,862 @@ +//------------------------------------------------------------------------------ +// sokol-fetch-test.c +// +// FIXME: simulate allocation errors +//------------------------------------------------------------------------------ +#define SOKOL_IMPL +#define SFETCH_MAX_USERDATA_UINT64 (8) +#define SFETCH_MAX_PATH (32) +#include "sokol_fetch.h" +#include "utest.h" + +#define T(b) EXPECT_TRUE(b) +#define TSTR(s0, s1) EXPECT_TRUE(0 == strcmp(s0,s1)) + +static uint8_t load_file_buf[500000]; +static const uint64_t combatsignal_file_size = 409482; + +typedef struct { + int a, b, c; +} userdata_t; + +static const _sfetch_item_t zeroed_item = {0}; + +#ifdef _WIN32 +#include +static void sleep_ms(int ms) { + Sleep((DWORD)ms); +} +#else +#include +static void sleep_ms(uint32_t ms) { + usleep(ms * 1000); +} +#endif + +/* private implementation functions */ +UTEST(sokol_fetch, path_make) { + const char* str31 = "1234567890123456789012345678901"; + const char* str32 = "12345678901234567890123456789012"; + // max allowed string length (MAX_PATH - 1) + _sfetch_path_t p31 = _sfetch_path_make(str31); + TSTR(p31.buf, str31); + // overflow + _sfetch_path_t p32 = _sfetch_path_make(str32); + T(p32.buf[0] == 0); +} + +UTEST(sokol_fetch, make_id) { + uint32_t slot_id = _sfetch_make_id(123, 456); + T(slot_id == ((456<<16)|123)); + T(_sfetch_slot_index(slot_id) == 123); +} + +UTEST(sokol_fetch, item_init_discard) { + userdata_t user_data = { + .a = 123, + .b = 456, + .c = 789 + }; + sfetch_request_t request = { + .channel = 4, + .path = "hello_world.txt", + .chunk_size = 128, + .user_data = SFETCH_RANGE(user_data) + }; + _sfetch_item_t item = zeroed_item; + uint32_t slot_id = _sfetch_make_id(1, 1); + _sfetch_item_init(&item, slot_id, &request); + T(item.handle.id == slot_id); + T(item.channel == 4); + T(item.lane == _SFETCH_INVALID_LANE); + T(item.chunk_size == 128); + T(item.state == _SFETCH_STATE_INITIAL); + TSTR(item.path.buf, request.path); + T(item.user.user_data_size == sizeof(userdata_t)); + const userdata_t* ud = (const userdata_t*) item.user.user_data; + T((((uintptr_t)ud) & 0x7) == 0); // check alignment + T(ud->a == 123); + T(ud->b == 456); + T(ud->c == 789); + + item.state = _SFETCH_STATE_FETCHING; + _sfetch_item_discard(&item); + T(item.handle.id == 0); + T(item.path.buf[0] == 0); + T(item.state == _SFETCH_STATE_INITIAL); + T(item.user.user_data_size == 0); + T(item.user.user_data[0] == 0); +} + +UTEST(sokol_fetch, item_init_path_overflow) { + sfetch_request_t request = { + .path = "012345678901234567890123456789012", + }; + _sfetch_item_t item = zeroed_item; + _sfetch_item_init(&item, _sfetch_make_id(1, 1), &request); + T(item.path.buf[0] == 0); +} + +UTEST(sokol_fetch, item_init_userdata_overflow) { + uint8_t big_data[128] = { 0xFF }; + sfetch_request_t request = { + .path = "hello_world.txt", + .user_data = SFETCH_RANGE(big_data), + }; + _sfetch_item_t item = zeroed_item; + _sfetch_item_init(&item, _sfetch_make_id(1, 1), &request); + T(item.user.user_data_size == 0); + T(item.user.user_data[0] == 0); +} + +UTEST(sokol_fetch, pool_init_discard) { + sfetch_setup(&(sfetch_desc_t){0}); + _sfetch_pool_t pool = {0}; + const uint32_t num_items = 127; + T(_sfetch_pool_init(&pool, num_items)); + T(pool.valid); + T(pool.size == 128); + T(pool.free_top == 127); + T(pool.free_slots[0] == 127); + T(pool.free_slots[1] == 126); + T(pool.free_slots[126] == 1); + _sfetch_pool_discard(&pool); + T(!pool.valid); + T(pool.free_slots == 0); + T(pool.items == 0); + sfetch_shutdown(); +} + +UTEST(sokol_fetch, pool_alloc_free) { + sfetch_setup(&(sfetch_desc_t){0}); + uint8_t buf[32]; + _sfetch_pool_t pool = {0}; + const uint32_t num_items = 16; + _sfetch_pool_init(&pool, num_items); + uint32_t slot_id = _sfetch_pool_item_alloc(&pool, &(sfetch_request_t){ + .path = "hello_world.txt", + .buffer = SFETCH_RANGE(buf), + }); + T(slot_id == 0x00010001); + T(pool.items[1].state == _SFETCH_STATE_ALLOCATED); + T(pool.items[1].handle.id == slot_id); + TSTR(pool.items[1].path.buf, "hello_world.txt"); + T(pool.items[1].buffer.ptr == buf); + T(pool.items[1].buffer.size == sizeof(buf)); + T(pool.free_top == 15); + _sfetch_pool_item_free(&pool, slot_id); + T(pool.items[1].handle.id == 0); + T(pool.items[1].state == _SFETCH_STATE_INITIAL); + T(pool.items[1].path.buf[0] == 0); + T(pool.items[1].buffer.ptr == 0); + T(pool.items[1].buffer.size == 0); + T(pool.free_top == 16); + _sfetch_pool_discard(&pool); + sfetch_shutdown(); +} + +UTEST(sokol_fetch, pool_overflow) { + sfetch_setup(&(sfetch_desc_t){0}); + _sfetch_pool_t pool = {0}; + _sfetch_pool_init(&pool, 4); + uint32_t id0 = _sfetch_pool_item_alloc(&pool, &(sfetch_request_t){ .path="path0" }); + uint32_t id1 = _sfetch_pool_item_alloc(&pool, &(sfetch_request_t){ .path="path1" }); + uint32_t id2 = _sfetch_pool_item_alloc(&pool, &(sfetch_request_t){ .path="path2" }); + uint32_t id3 = _sfetch_pool_item_alloc(&pool, &(sfetch_request_t){ .path="path3" }); + // next alloc should fail + uint32_t id4 = _sfetch_pool_item_alloc(&pool, &(sfetch_request_t){ .path="path4" }); + T(id0 == 0x00010001); + T(id1 == 0x00010002); + T(id2 == 0x00010003); + T(id3 == 0x00010004); + T(id4 == 0); + T(pool.items[1].handle.id == id0); + T(pool.items[2].handle.id == id1); + T(pool.items[3].handle.id == id2); + T(pool.items[4].handle.id == id3); + // free one item, alloc should work now + _sfetch_pool_item_free(&pool, id0); + uint32_t id5 = _sfetch_pool_item_alloc(&pool, &(sfetch_request_t){ .path="path5" }); + T(id5 == 0x00020001); + T(pool.items[1].handle.id == id5); + TSTR(pool.items[1].path.buf, "path5"); + _sfetch_pool_discard(&pool); + sfetch_shutdown(); +} + +UTEST(sokol_fetch, lookup_item) { + sfetch_setup(&(sfetch_desc_t){0}); + _sfetch_pool_t pool = {0}; + _sfetch_pool_init(&pool, 4); + uint32_t id0 = _sfetch_pool_item_alloc(&pool, &(sfetch_request_t){ .path="path0" }); + uint32_t id1 = _sfetch_pool_item_alloc(&pool, &(sfetch_request_t){ .path="path1" }); + const _sfetch_item_t* item0 = _sfetch_pool_item_lookup(&pool, id0); + const _sfetch_item_t* item1 = _sfetch_pool_item_lookup(&pool, id1); + T(item0 == &pool.items[1]); + T(item1 == &pool.items[2]); + /* invalid handle always returns 0-ptr */ + T(0 == _sfetch_pool_item_lookup(&pool, _sfetch_make_id(0, 0))); + /* free an item and make sure it's detected as dangling */ + _sfetch_pool_item_free(&pool, id0); + T(0 == _sfetch_pool_item_lookup(&pool, id0)); + _sfetch_pool_discard(&pool); + sfetch_shutdown(); +} + +UTEST(sokol_fetch, ring_init_discard) { + sfetch_setup(&(sfetch_desc_t){0}); + _sfetch_ring_t ring = {0}; + const uint32_t num_slots = 4; + T(_sfetch_ring_init(&ring, num_slots)); + T(ring.head == 0); + T(ring.tail == 0); + T(ring.num == (num_slots + 1)); + T(ring.buf); + _sfetch_ring_discard(&ring); + T(ring.head == 0); + T(ring.tail == 0); + T(ring.num == 0); + T(ring.buf == 0); + sfetch_shutdown(); +} + +UTEST(sokol_fetch, ring_enqueue_dequeue) { + sfetch_setup(&(sfetch_desc_t){0}); + _sfetch_ring_t ring = {0}; + const uint32_t num_slots = 4; + _sfetch_ring_init(&ring, num_slots); + T(_sfetch_ring_count(&ring) == 0); + T(_sfetch_ring_empty(&ring)); + T(!_sfetch_ring_full(&ring)); + for (uint32_t i = 0; i < num_slots; i++) { + T(!_sfetch_ring_full(&ring)); + _sfetch_ring_enqueue(&ring, _sfetch_make_id(1, i+1)); + T(_sfetch_ring_count(&ring) == (i+1)); + T(!_sfetch_ring_empty(&ring)); + } + T(_sfetch_ring_count(&ring) == 4); + T(!_sfetch_ring_empty(&ring)); + T(_sfetch_ring_full(&ring)); + for (uint32_t i = 0; i < num_slots; i++) { + T(_sfetch_ring_peek(&ring, i) == _sfetch_make_id(1, i+1)); + } + for (uint32_t i = 0; i < num_slots; i++) { + T(!_sfetch_ring_empty(&ring)); + const uint32_t slot_id = _sfetch_ring_dequeue(&ring); + T(slot_id == _sfetch_make_id(1, i+1)); + T(!_sfetch_ring_full(&ring)); + } + T(_sfetch_ring_count(&ring) == 0); + T(_sfetch_ring_empty(&ring)); + T(!_sfetch_ring_full(&ring)); + _sfetch_ring_discard(&ring); + sfetch_shutdown(); +} + +UTEST(sokol_fetch, ring_wrap_around) { + sfetch_setup(&(sfetch_desc_t){0}); + _sfetch_ring_t ring = {0}; + _sfetch_ring_init(&ring, 4); + uint32_t i = 0; + for (i = 0; i < 4; i++) { + _sfetch_ring_enqueue(&ring, _sfetch_make_id(1, i+1)); + } + T(_sfetch_ring_full(&ring)); + for (; i < 64; i++) { + T(_sfetch_ring_full(&ring)); + T(_sfetch_ring_dequeue(&ring) == _sfetch_make_id(1, (i - 3))); + T(!_sfetch_ring_full(&ring)); + _sfetch_ring_enqueue(&ring, _sfetch_make_id(1, i+1)); + } + T(_sfetch_ring_full(&ring)); + for (i = 0; i < 4; i++) { + T(_sfetch_ring_dequeue(&ring) == _sfetch_make_id(1, (i + 61))); + } + T(_sfetch_ring_empty(&ring)); + _sfetch_ring_discard(&ring); + sfetch_shutdown(); +} + +UTEST(sokol_fetch, ring_wrap_count) { + sfetch_setup(&(sfetch_desc_t){0}); + _sfetch_ring_t ring = {0}; + _sfetch_ring_init(&ring, 8); + // add and remove 4 items to move tail to the middle + for (uint32_t i = 0; i < 4; i++) { + _sfetch_ring_enqueue(&ring, _sfetch_make_id(1, i+1)); + _sfetch_ring_dequeue(&ring); + T(_sfetch_ring_empty(&ring)); + } + // add another 8 items + for (uint32_t i = 0; i < 8; i++) { + _sfetch_ring_enqueue(&ring, _sfetch_make_id(1, i+1)); + } + // now test, dequeue and test... + T(_sfetch_ring_full(&ring)); + for (uint32_t i = 0; i < 8; i++) { + T(_sfetch_ring_count(&ring) == (8 - i)); + _sfetch_ring_dequeue(&ring); + } + T(_sfetch_ring_count(&ring) == 0); + T(_sfetch_ring_empty(&ring)); + _sfetch_ring_discard(&ring); + sfetch_shutdown(); +} + +/* NOTE: channel_worker is called from a thread */ +static int num_processed_items = 0; +static void channel_worker(_sfetch_t* ctx, uint32_t slot_id) { + (void)ctx; + (void)slot_id; + num_processed_items++; +} + +UTEST(sokol_fetch, channel_init_discard) { + sfetch_setup(&(sfetch_desc_t){0}); + num_processed_items = 0; + _sfetch_channel_t chn = {0}; + const uint32_t num_slots = 12; + const uint32_t num_lanes = 64; + _sfetch_channel_init(&chn, 0, num_slots, num_lanes, channel_worker); + T(chn.valid); + T(_sfetch_ring_full(&chn.free_lanes)); + T(_sfetch_ring_empty(&chn.user_sent)); + T(_sfetch_ring_empty(&chn.user_incoming)); + #if !defined(__EMSCRIPTEN__) + T(_sfetch_ring_empty(&chn.thread_incoming)); + T(_sfetch_ring_empty(&chn.thread_outgoing)); + #endif + T(_sfetch_ring_empty(&chn.user_outgoing)); + _sfetch_channel_discard(&chn); + T(!chn.valid); + sfetch_shutdown(); +} + +/* public API functions */ +UTEST(sokol_fetch, setup_shutdown) { + sfetch_setup(&(sfetch_desc_t){0}); + T(sfetch_valid()); + // check default values + T(sfetch_desc().max_requests == 128); + T(sfetch_desc().num_channels == 1); + T(sfetch_desc().num_lanes == 1); + sfetch_shutdown(); + T(!sfetch_valid()); +} + +UTEST(sokol_fetch, setup_too_many_channels) { + /* try to initialize with too many channels, this should clamp to + SFETCH_MAX_CHANNELS + */ + sfetch_setup(&(sfetch_desc_t){ + .num_channels = 64 + }); + T(sfetch_valid()); + T(sfetch_desc().num_channels == SFETCH_MAX_CHANNELS); + sfetch_shutdown(); +} + +UTEST(sokol_fetch, max_path) { + T(sfetch_max_path() == SFETCH_MAX_PATH); +} + +UTEST(sokol_fetch, max_userdata) { + T(sfetch_max_userdata_bytes() == (SFETCH_MAX_USERDATA_UINT64 * sizeof(uint64_t))); +} + +static uint8_t fail_open_buffer[128]; +static bool fail_open_passed; +static void fail_open_callback(const sfetch_response_t* response) { + /* if opening a file fails, it will immediate switch into CLOSED state */ + if ((response->failed) && (response->error_code == SFETCH_ERROR_FILE_NOT_FOUND)) { + fail_open_passed = true; + } +} + +UTEST(sokol_fetch, fail_open) { + sfetch_setup(&(sfetch_desc_t){0}); + sfetch_handle_t h = sfetch_send(&(sfetch_request_t){ + .path = "non_existing_file.txt", + .callback = fail_open_callback, + .buffer = SFETCH_RANGE(fail_open_buffer), + }); + fail_open_passed = false; + int frame_count = 0; + const int max_frames = 10000; + while (sfetch_handle_valid(h) && (frame_count++ < max_frames)) { + sfetch_dowork(); + sleep_ms(1); + } + T(frame_count < max_frames); + T(fail_open_passed); + sfetch_shutdown(); +} + +static bool load_file_fixed_buffer_passed; + +// The file callback is called from the "current user thread" (the same +// thread where the sfetch_send() for this request was called). Note that you +// can call sfetch_setup/shutdown() on multiple threads, each thread will +// get its own thread-local "sokol-fetch instance" and its own set of +// IO-channel threads. +static void load_file_fixed_buffer_callback(const sfetch_response_t* response) { + // when loading the whole file at once, the fetched state + // is the best place to grab/process the data + if (response->fetched) { + if ((response->data_offset == 0) && + (response->data.ptr == load_file_buf) && + (response->data.size == combatsignal_file_size) && + (response->buffer.ptr == load_file_buf) && + (response->buffer.size == sizeof(load_file_buf)) && + response->finished) + { + load_file_fixed_buffer_passed = true; + } + } +} + +UTEST(sokol_fetch, load_file_fixed_buffer) { + memset(load_file_buf, 0, sizeof(load_file_buf)); + sfetch_setup(&(sfetch_desc_t){0}); + // send a load-request for a file where we know the max size upfront, + // so we can provide a buffer right in the fetch request (otherwise + // the buffer needs to be provided in the callback when the request + // is in OPENED state, since only then the file size will be known). + sfetch_handle_t h = sfetch_send(&(sfetch_request_t){ + .path = "comsi.s3m", + .callback = load_file_fixed_buffer_callback, + .buffer = SFETCH_RANGE(load_file_buf), + }); + // simulate a frame-loop for as long as the request is in flight, normally + // the sfetch_dowork() function is just called somewhere in the frame + // to pump messages in and out of the IO threads, and invoke user-callbacks + int frame_count = 0; + const int max_frames = 10000; + while (sfetch_handle_valid(h) && (frame_count++ < max_frames)) { + sfetch_dowork(); + sleep_ms(1); + } + T(frame_count < max_frames); + T(load_file_fixed_buffer_passed); + sfetch_shutdown(); +} + +/* tests whether files with unknown size are processed correctly */ +static bool load_file_unknown_size_opened_passed; +static bool load_file_unknown_size_fetched_passed; +static void load_file_unknown_size_callback(const sfetch_response_t* response) { + if (response->dispatched) { + if ((response->data_offset == 0) && + (response->data.ptr == 0) && + (response->data.size == 0) && + (response->buffer.ptr == 0) && + (response->buffer.size == 0) && + !response->finished) + { + load_file_unknown_size_opened_passed = true; + sfetch_bind_buffer(response->handle, SFETCH_RANGE(load_file_buf)); + } + } + else if (response->fetched) { + if ((response->data_offset == 0) && + (response->data.ptr == load_file_buf) && + (response->data.size == combatsignal_file_size) && + (response->buffer.ptr == load_file_buf) && + (response->buffer.size == sizeof(load_file_buf)) && + response->finished) + { + load_file_unknown_size_fetched_passed = true; + } + } +} + +UTEST(sokol_fetch, load_file_unknown_size) { + memset(load_file_buf, 0, sizeof(load_file_buf)); + sfetch_setup(&(sfetch_desc_t){0}); + sfetch_handle_t h = sfetch_send(&(sfetch_request_t){ + .path = "comsi.s3m", + .callback = load_file_unknown_size_callback + }); + int frame_count = 0; + const int max_frames = 10000; + while (sfetch_handle_valid(h) && (frame_count++ < max_frames)) { + sfetch_dowork(); + sleep_ms(1); + } + T(frame_count < max_frames); + T(load_file_unknown_size_opened_passed); + T(load_file_unknown_size_fetched_passed); + sfetch_shutdown(); +} + +/* tests whether not providing a buffer in OPENED properly fails */ +static bool load_file_no_buffer_opened_passed; +static bool load_file_no_buffer_failed_passed; +static void load_file_no_buffer_callback(const sfetch_response_t* response) { + if (response->dispatched) { + if ((response->data_offset == 0) && + (response->data.ptr == 0) && + (response->data.size == 0) && + (response->buffer.ptr == 0) && + (response->buffer.size == 0) && + !response->finished) + { + /* DO NOT provide a buffer here, see if that properly fails */ + load_file_no_buffer_opened_passed = true; + } + } + else if ((response->failed) && (response->error_code == SFETCH_ERROR_NO_BUFFER)) { + if (load_file_no_buffer_opened_passed) { + load_file_no_buffer_failed_passed = true; + } + } +} + +UTEST(sokol_fetch, load_file_no_buffer) { + memset(load_file_buf, 0, sizeof(load_file_buf)); + sfetch_setup(&(sfetch_desc_t){0}); + sfetch_handle_t h = sfetch_send(&(sfetch_request_t){ + .path = "comsi.s3m", + .callback = load_file_no_buffer_callback + }); + int frame_count = 0; + const int max_frames = 10000; + while (sfetch_handle_valid(h) && (frame_count++ < max_frames)) { + sfetch_dowork(); + sleep_ms(1); + } + T(frame_count < max_frames); + T(load_file_no_buffer_opened_passed); + T(load_file_no_buffer_failed_passed); + sfetch_shutdown(); +} + +static bool load_file_too_small_passed; +static uint8_t load_file_too_small_buf[8192]; +static void load_file_too_small_callback(const sfetch_response_t* response) { + if (response->failed && (response->error_code == SFETCH_ERROR_BUFFER_TOO_SMALL)) { + load_file_too_small_passed = true; + } +} + +UTEST(sokol_fetch, load_file_too_small_buffer) { + memset(load_file_buf, 0, sizeof(load_file_buf)); + sfetch_setup(&(sfetch_desc_t){0}); + sfetch_handle_t h = sfetch_send(&(sfetch_request_t){ + .path = "comsi.s3m", + .callback = load_file_too_small_callback, + .buffer = SFETCH_RANGE(load_file_too_small_buf), + }); + int frame_count = 0; + const int max_frames = 10000; + while (sfetch_handle_valid(h) && (frame_count++ < max_frames)) { + sfetch_dowork(); + sleep_ms(1); + } + T(frame_count < max_frames); + T(load_file_too_small_passed); + sfetch_shutdown(); +} + + +/* test loading a big file via a small chunk-buffer, the callback will + be called multiple times with the FETCHED state until the entire file + is loaded +*/ +static bool load_file_chunked_passed; +static uint8_t load_chunk_buf[8192]; +static uint8_t load_file_chunked_content[500000]; +static void load_file_chunked_callback(const sfetch_response_t* response) { + if (response->fetched) { + uint8_t* dst = &load_file_chunked_content[response->data_offset]; + const uint8_t* src = response->data.ptr; + size_t num_bytes = response->data.size; + memcpy(dst, src, num_bytes); + if (response->finished) { + load_file_chunked_passed = true; + } + } +} + +UTEST(sokol_fetch, load_file_chunked) { + memset(load_file_buf, 0, sizeof(load_file_buf)); + memset(load_chunk_buf, 0, sizeof(load_chunk_buf)); + memset(load_file_chunked_content, 0, sizeof(load_file_chunked_content)); + load_file_fixed_buffer_passed = false; + sfetch_setup(&(sfetch_desc_t){0}); + // request for chunked-loading + sfetch_handle_t h0 = sfetch_send(&(sfetch_request_t){ + .path = "comsi.s3m", + .callback = load_file_chunked_callback, + .buffer = SFETCH_RANGE(load_chunk_buf), + .chunk_size = sizeof(load_chunk_buf) + }); + // request for all-in-one loading for comparing with the chunked buffer + sfetch_handle_t h1 = sfetch_send(&(sfetch_request_t){ + .path = "comsi.s3m", + .callback = load_file_fixed_buffer_callback, + .buffer = SFETCH_RANGE(load_file_buf), + }); + int frame_count = 0; + const int max_frames = 10000; + while ((sfetch_handle_valid(h0) || sfetch_handle_valid(h1)) && (frame_count++ < max_frames)) { + sfetch_dowork(); + sleep_ms(1); + } + T(frame_count < max_frames); + T(load_file_chunked_passed); + T(0 == memcmp(load_file_chunked_content, load_file_buf, combatsignal_file_size)); + sfetch_shutdown(); +} + +/* load N big files in small chunks interleaved on the same channel via lanes */ +#define LOAD_FILE_LANES_NUM_LANES (4) + +static uint8_t load_file_lanes_chunk_buf[LOAD_FILE_LANES_NUM_LANES][8192]; +static uint8_t load_file_lanes_content[LOAD_FILE_LANES_NUM_LANES][500000]; +static int load_file_lanes_passed[LOAD_FILE_LANES_NUM_LANES]; +static void load_file_lanes_callback(const sfetch_response_t* response) { + assert((response->channel == 0) && (response->lane < LOAD_FILE_LANES_NUM_LANES)); + if (response->fetched) { + uint8_t* dst = &load_file_lanes_content[response->lane][response->data_offset]; + const uint8_t* src = response->data.ptr; + size_t num_bytes = response->data.size; + memcpy(dst, src, num_bytes); + if (response->finished) { + load_file_lanes_passed[response->lane]++; + } + } +} + +UTEST(sokol_fetch, load_file_lanes) { + for (int i = 0; i < LOAD_FILE_LANES_NUM_LANES; i++) { + memset(load_file_lanes_content[i], i, sizeof(load_file_lanes_content[i])); + } + sfetch_setup(&(sfetch_desc_t){ + .num_channels = 1, + .num_lanes = LOAD_FILE_LANES_NUM_LANES, + }); + sfetch_handle_t h[LOAD_FILE_LANES_NUM_LANES]; + for (int lane = 0; lane < LOAD_FILE_LANES_NUM_LANES; lane++) { + h[lane] = sfetch_send(&(sfetch_request_t){ + .path = "comsi.s3m", + .callback = load_file_lanes_callback, + .buffer = { .ptr = load_file_lanes_chunk_buf[lane], .size = sizeof(load_file_lanes_chunk_buf[0]) }, + .chunk_size = sizeof(load_file_lanes_chunk_buf[0]) + }); + } + bool done = false; + int frame_count = 0; + const int max_frames = 10000; + while (!done && (frame_count++ < max_frames)) { + done = true; + for (int i = 0; i < LOAD_FILE_LANES_NUM_LANES; i++) { + done &= !sfetch_handle_valid(h[i]); + } + sfetch_dowork(); + sleep_ms(1); + } + T(frame_count < max_frames); + for (int i = 0; i < LOAD_FILE_LANES_NUM_LANES; i++) { + T(1 == load_file_lanes_passed[i]); + T(0 == memcmp(load_file_lanes_content[0], load_file_lanes_content[i], combatsignal_file_size)); + } + sfetch_shutdown(); +} + +/* same as above, but issue more requests than available lanes to test if rate-limiting works */ +#define LOAD_FILE_THROTTLE_NUM_LANES (4) +#define LOAD_FILE_THROTTLE_NUM_PASSES (3) +#define LOAD_FILE_THROTTLE_NUM_REQUESTS (12) // lanes * passes + +static uint8_t load_file_throttle_chunk_buf[LOAD_FILE_THROTTLE_NUM_LANES][128000]; +static uint8_t load_file_throttle_content[LOAD_FILE_THROTTLE_NUM_PASSES][LOAD_FILE_THROTTLE_NUM_LANES][500000]; +static int load_file_throttle_passed[LOAD_FILE_THROTTLE_NUM_LANES]; + +static void load_file_throttle_callback(const sfetch_response_t* response) { + assert((response->channel == 0) && (response->lane < LOAD_FILE_LANES_NUM_LANES)); + if (response->fetched) { + assert(load_file_throttle_passed[response->lane] < LOAD_FILE_THROTTLE_NUM_PASSES); + uint8_t* dst = &load_file_throttle_content[load_file_throttle_passed[response->lane]][response->lane][response->data_offset]; + const uint8_t* src = response->data.ptr; + size_t num_bytes = response->data.size; + memcpy(dst, src, num_bytes); + if (response->finished) { + load_file_throttle_passed[response->lane]++; + } + } +} + +UTEST(sokol_fetch, load_file_throttle) { + for (int pass = 0; pass < LOAD_FILE_THROTTLE_NUM_PASSES; pass++) { + for (int lane = 0; lane < LOAD_FILE_THROTTLE_NUM_LANES; lane++) { + memset(load_file_throttle_content[pass][lane], 10*pass+lane, sizeof(load_file_throttle_content[pass][lane])); + } + } + sfetch_setup(&(sfetch_desc_t){ + .num_channels = 1, + .num_lanes = LOAD_FILE_THROTTLE_NUM_LANES, + }); + sfetch_handle_t h[LOAD_FILE_THROTTLE_NUM_REQUESTS]; + for (int i = 0; i < LOAD_FILE_THROTTLE_NUM_REQUESTS; i++) { + h[i] = sfetch_send(&(sfetch_request_t){ + .path = "comsi.s3m", + .callback = load_file_throttle_callback, + .buffer = { + .ptr = load_file_throttle_chunk_buf[i % LOAD_FILE_THROTTLE_NUM_LANES], + .size = sizeof(load_file_throttle_chunk_buf[0]), + }, + .chunk_size = sizeof(load_file_throttle_chunk_buf[0]) + }); + T(sfetch_handle_valid(h[i])); + } + bool done = false; + int frame_count = 0; + const int max_frames = 10000; + while (!done && (frame_count++ < max_frames)) { + done = true; + for (int i = 0; i < LOAD_FILE_THROTTLE_NUM_REQUESTS; i++) { + done &= !sfetch_handle_valid(h[i]); + } + sfetch_dowork(); + sleep_ms(1); + } + T(frame_count < max_frames); + for (int lane = 0; lane < LOAD_FILE_THROTTLE_NUM_LANES; lane++) { + T(LOAD_FILE_THROTTLE_NUM_PASSES == load_file_throttle_passed[lane]); + for (int pass = 0; pass < LOAD_FILE_THROTTLE_NUM_PASSES; pass++) { + T(0 == memcmp(load_file_throttle_content[0][0], load_file_throttle_content[pass][lane], combatsignal_file_size)); + } + } + sfetch_shutdown(); +} + +/* test parallel fetches on multiple channels */ +#define LOAD_CHANNEL_NUM_CHANNELS (16) +static uint8_t load_channel_buf[LOAD_CHANNEL_NUM_CHANNELS][500000]; +static bool load_channel_passed[LOAD_CHANNEL_NUM_CHANNELS]; + +void load_channel_callback(const sfetch_response_t* response) { + assert(response->channel < LOAD_CHANNEL_NUM_CHANNELS); + assert(!load_channel_passed[response->channel]); + if (response->fetched) { + if ((response->data.size == combatsignal_file_size) && response->finished) { + load_channel_passed[response->channel] = true; + } + } +} + +UTEST(sokol_fetch, load_channel) { + for (int chn = 0; chn < LOAD_CHANNEL_NUM_CHANNELS; chn++) { + memset(load_channel_buf[chn], chn, sizeof(load_channel_buf[chn])); + } + sfetch_setup(&(sfetch_desc_t){ + .num_channels = LOAD_CHANNEL_NUM_CHANNELS + }); + sfetch_handle_t h[LOAD_CHANNEL_NUM_CHANNELS]; + for (uint32_t chn = 0; chn < LOAD_CHANNEL_NUM_CHANNELS; chn++) { + h[chn] = sfetch_send(&(sfetch_request_t){ + .path = "comsi.s3m", + .channel = chn, + .callback = load_channel_callback, + .buffer = SFETCH_RANGE(load_channel_buf[chn]), + }); + } + bool done = false; + int frame_count = 0; + const int max_frames = 100000; + while (!done && (frame_count++ < max_frames)) { + done = true; + for (int i = 0; i < LOAD_CHANNEL_NUM_CHANNELS; i++) { + done &= !sfetch_handle_valid(h[i]); + } + sfetch_dowork(); + sleep_ms(1); + } + T(frame_count < max_frames); + for (int chn = 0; chn < LOAD_CHANNEL_NUM_CHANNELS; chn++) { + T(load_channel_passed[chn]); + T(0 == memcmp(load_channel_buf[0], load_channel_buf[chn], combatsignal_file_size)); + } + sfetch_shutdown(); +} + +static bool load_file_cancel_passed = false; +static void load_file_cancel_callback(const sfetch_response_t* response) { + if (response->dispatched) { + sfetch_cancel(response->handle); + } + if (response->cancelled && response->finished && response->failed && (response->error_code == SFETCH_ERROR_CANCELLED)) { + load_file_cancel_passed = true; + } +} + +UTEST(sokol_fetch, load_file_cancel) { + sfetch_setup(&(sfetch_desc_t){ + .num_channels = 1 + }); + sfetch_handle_t h = sfetch_send(&(sfetch_request_t){ + .path = "comsi.s3m", + .callback = load_file_cancel_callback, + }); + int frame_count = 0; + const int max_frames = 10000; + while (sfetch_handle_valid(h) && (frame_count++ < max_frames)) { + sfetch_dowork(); + sleep_ms(1); + } + T(frame_count < max_frames); + T(load_file_cancel_passed); + sfetch_shutdown(); +} + +static bool load_file_cancel_before_dispatch_passed = false; +static void load_file_cancel_before_dispatch_callback(const sfetch_response_t* response) { + // cancelled, finished, failed and error code must all be set + if (response->cancelled && response->finished && response->failed && (response->error_code == SFETCH_ERROR_CANCELLED)) { + load_file_cancel_before_dispatch_passed = true; + } +} + +UTEST(sokol_fetch, load_file_cancel_before_dispatch) { + sfetch_setup(&(sfetch_desc_t){ + .num_channels = 1, + }); + sfetch_handle_t h = sfetch_send(&(sfetch_request_t){ + .path = "comsi.s3m", + .callback = load_file_cancel_before_dispatch_callback, + }); + sfetch_cancel(h); + sfetch_dowork(); + T(load_file_cancel_before_dispatch_passed); + sfetch_shutdown(); +} + +static bool load_file_cancel_after_dispatch_passed = false; +static void load_file_cancel_after_dispatch_callback(const sfetch_response_t* response) { + // when cancelled, then finished, failed and error code must all be set + if (response->cancelled && response->finished && response->failed && (response->error_code == SFETCH_ERROR_CANCELLED)) { + load_file_cancel_after_dispatch_passed = true; + } +} + +UTEST(sokol_fetch, load_file_cancel_after_dispatch) { + sfetch_setup(&(sfetch_desc_t){ + .num_channels = 1, + }); + sfetch_handle_t h = sfetch_send(&(sfetch_request_t){ + .path = "comsi.s3m", + .callback = load_file_cancel_after_dispatch_callback, + .buffer = SFETCH_RANGE(load_file_buf), + }); + int frame_count = 0; + const int max_frames = 10000; + while (sfetch_handle_valid(h) && (frame_count++ < max_frames)) { + sfetch_dowork(); + sfetch_cancel(h); + sleep_ms(1); + } + T(frame_count < max_frames); + T(load_file_cancel_after_dispatch_passed); + sfetch_shutdown(); +} diff --git a/thirdparty/sokol/tests/functional/sokol_gfx_test.c b/thirdparty/sokol/tests/functional/sokol_gfx_test.c new file mode 100644 index 0000000..121b3d0 --- /dev/null +++ b/thirdparty/sokol/tests/functional/sokol_gfx_test.c @@ -0,0 +1,2977 @@ +//------------------------------------------------------------------------------ +// sokol-gfx-test.c +// NOTE: this is not only testing the public API behaviour, but also +// accesses private functions and data. It may make sense to split +// these into two separate tests. +//------------------------------------------------------------------------------ +#include "force_dummy_backend.h" +#define SOKOL_IMPL +#include "sokol_gfx.h" +#include "utest.h" + +#define T(b) EXPECT_TRUE(b) + +#define MAX_LOGITEMS (32) +static int num_log_called = 0; +static sg_log_item log_items[MAX_LOGITEMS]; + +static void test_logger(const char* tag, uint32_t log_level, uint32_t log_item_id, const char* message_or_null, uint32_t line_nr, const char* filename_or_null, void* user_data) { + (void)tag; + (void)log_level; + (void)message_or_null; + (void)line_nr; + (void)filename_or_null; + (void)user_data; + if (num_log_called < MAX_LOGITEMS) { + log_items[num_log_called++] = log_item_id; + } + if (message_or_null) { + printf("%s\n", message_or_null); + } +} + +static void reset_log_items(void) { + num_log_called = 0; + memset(log_items, 0, sizeof(log_items)); +} + +static void setup(const sg_desc* desc) { + reset_log_items(); + sg_desc desc_with_logger = *desc; + desc_with_logger.logger.func = test_logger; + sg_setup(&desc_with_logger); +} + +static sg_buffer create_buffer(void) { + static const float data[] = { 1, 2, 3, 4 }; + return sg_make_buffer(&(sg_buffer_desc){ .data = SG_RANGE(data) }); +} + +static sg_image create_image(void) { + return sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 128 + }); +} + +static sg_shader create_shader(void) { + return sg_make_shader(&(sg_shader_desc){0}); +} + +static sg_pipeline create_pipeline(void) { + return sg_make_pipeline(&(sg_pipeline_desc){ + .layout = { + .attrs[0].format = SG_VERTEXFORMAT_FLOAT3 + }, + .shader = sg_make_shader(&(sg_shader_desc){0}) + }); +} + +static sg_attachments create_attachments(void) { + sg_image_desc img_desc = { + .render_target = true, + .width = 128, + .height = 128, + }; + return sg_make_attachments(&(sg_attachments_desc){ + .colors = { + [0].image = sg_make_image(&img_desc), + [1].image = sg_make_image(&img_desc), + [2].image = sg_make_image(&img_desc) + }, + }); +} + +UTEST(sokol_gfx, init_shutdown) { + setup(&(sg_desc){0}); + T(sg_isvalid()); + sg_shutdown(); + T(!sg_isvalid()); +} + +UTEST(sokol_gfx, query_desc) { + setup(&(sg_desc){ + .buffer_pool_size = 1024, + .sampler_pool_size = 8, + .shader_pool_size = 128, + .attachments_pool_size = 64, + }); + const sg_desc desc = sg_query_desc(); + T(desc.buffer_pool_size == 1024); + T(desc.image_pool_size == _SG_DEFAULT_IMAGE_POOL_SIZE); + T(desc.sampler_pool_size == 8); + T(desc.shader_pool_size == 128); + T(desc.pipeline_pool_size == _SG_DEFAULT_PIPELINE_POOL_SIZE); + T(desc.attachments_pool_size == 64); + T(desc.uniform_buffer_size == _SG_DEFAULT_UB_SIZE); + sg_shutdown(); +} + +UTEST(sokol_gfx, query_backend) { + setup(&(sg_desc){0}); + T(sg_query_backend() == SG_BACKEND_DUMMY); + sg_shutdown(); +} + +UTEST(sokol_gfx, pool_size) { + setup(&(sg_desc){ + .buffer_pool_size = 1024, + .image_pool_size = 2048, + .shader_pool_size = 128, + .pipeline_pool_size = 256, + .attachments_pool_size = 64, + }); + T(sg_isvalid()); + /* pool slot 0 is reserved (this is the "invalid slot") */ + T(_sg.pools.buffer_pool.size == 1025); + T(_sg.pools.image_pool.size == 2049); + T(_sg.pools.shader_pool.size == 129); + T(_sg.pools.pipeline_pool.size == 257); + T(_sg.pools.attachments_pool.size == 65); + T(_sg.pools.buffer_pool.queue_top == 1024); + T(_sg.pools.image_pool.queue_top == 2048); + T(_sg.pools.shader_pool.queue_top == 128); + T(_sg.pools.pipeline_pool.queue_top == 256); + T(_sg.pools.attachments_pool.queue_top == 64); + sg_shutdown(); +} + +UTEST(sokol_gfx, alloc_fail_destroy_buffers) { + setup(&(sg_desc){ + .buffer_pool_size = 3 + }); + T(sg_isvalid()); + + sg_buffer buf[3] = { {0} }; + for (int i = 0; i < 3; i++) { + buf[i] = sg_alloc_buffer(); + T(buf[i].id != SG_INVALID_ID); + T((2-i) == _sg.pools.buffer_pool.queue_top); + T(sg_query_buffer_state(buf[i]) == SG_RESOURCESTATE_ALLOC); + } + /* the next alloc will fail because the pool is exhausted */ + sg_buffer b3 = sg_alloc_buffer(); + T(b3.id == SG_INVALID_ID); + T(sg_query_buffer_state(b3) == SG_RESOURCESTATE_INVALID); + + /* before destroying, the resources must be either in valid or failed state */ + for (int i = 0; i < 3; i++) { + sg_fail_buffer(buf[i]); + T(sg_query_buffer_state(buf[i]) == SG_RESOURCESTATE_FAILED); + } + for (int i = 0; i < 3; i++) { + sg_destroy_buffer(buf[i]); + T(sg_query_buffer_state(buf[i]) == SG_RESOURCESTATE_INVALID); + T((i+1) == _sg.pools.buffer_pool.queue_top); + } + sg_shutdown(); +} + +UTEST(sokol_gfx, alloc_fail_destroy_images) { + setup(&(sg_desc){ + .image_pool_size = 3 + }); + T(sg_isvalid()); + + sg_image img[3] = { {0} }; + for (int i = 0; i < 3; i++) { + img[i] = sg_alloc_image(); + T(img[i].id != SG_INVALID_ID); + T((2-i) == _sg.pools.image_pool.queue_top); + T(sg_query_image_state(img[i]) == SG_RESOURCESTATE_ALLOC); + } + /* the next alloc will fail because the pool is exhausted */ + sg_image i3 = sg_alloc_image(); + T(i3.id == SG_INVALID_ID); + T(sg_query_image_state(i3) == SG_RESOURCESTATE_INVALID); + + /* before destroying, the resources must be either in valid or failed state */ + for (int i = 0; i < 3; i++) { + sg_fail_image(img[i]); + T(sg_query_image_state(img[i]) == SG_RESOURCESTATE_FAILED); + } + for (int i = 0; i < 3; i++) { + sg_destroy_image(img[i]); + T(sg_query_image_state(img[i]) == SG_RESOURCESTATE_INVALID); + T((i+1) == _sg.pools.image_pool.queue_top); + } + sg_shutdown(); +} + +UTEST(sokol_gfx, alloc_fail_destroy_samplers) { + setup(&(sg_desc){ + .sampler_pool_size = 3, + }); + + sg_sampler smp[3] = { {0} }; + for (int i = 0; i < 3; i++) { + smp[i] = sg_alloc_sampler(); + T(smp[i].id != SG_INVALID_ID); + T((2-i) == _sg.pools.sampler_pool.queue_top); + T(sg_query_sampler_state(smp[i]) == SG_RESOURCESTATE_ALLOC); + } + // the next alloc will fail because the pool is exhausted + sg_sampler s3 = sg_alloc_sampler(); + T(s3.id == SG_INVALID_ID); + T(sg_query_sampler_state(s3) == SG_RESOURCESTATE_INVALID); + + // before destroying, the resources must be either in valid or failed state + for (int i = 0; i < 3; i++) { + sg_fail_sampler(smp[i]); + T(sg_query_sampler_state(smp[i]) == SG_RESOURCESTATE_FAILED); + } + for (int i = 0; i < 3; i++) { + sg_destroy_sampler(smp[i]); + T(sg_query_sampler_state(smp[i]) == SG_RESOURCESTATE_INVALID); + T((i+1) == _sg.pools.sampler_pool.queue_top); + } + sg_shutdown(); +} + +UTEST(sokol_gfx, alloc_fail_destroy_shaders) { + setup(&(sg_desc){ + .shader_pool_size = 3 + }); + T(sg_isvalid()); + + sg_shader shd[3] = { {0} }; + for (int i = 0; i < 3; i++) { + shd[i] = sg_alloc_shader(); + T(shd[i].id != SG_INVALID_ID); + T((2-i) == _sg.pools.shader_pool.queue_top); + T(sg_query_shader_state(shd[i]) == SG_RESOURCESTATE_ALLOC); + } + /* the next alloc will fail because the pool is exhausted */ + sg_shader s3 = sg_alloc_shader(); + T(s3.id == SG_INVALID_ID); + T(sg_query_shader_state(s3) == SG_RESOURCESTATE_INVALID); + + /* before destroying, the resources must be either in valid or failed state */ + for (int i = 0; i < 3; i++) { + sg_fail_shader(shd[i]); + T(sg_query_shader_state(shd[i]) == SG_RESOURCESTATE_FAILED); + } + for (int i = 0; i < 3; i++) { + sg_destroy_shader(shd[i]); + T(sg_query_shader_state(shd[i]) == SG_RESOURCESTATE_INVALID); + T((i+1) == _sg.pools.shader_pool.queue_top); + } + sg_shutdown(); +} + +UTEST(sokol_gfx, alloc_fail_destroy_pipelines) { + setup(&(sg_desc){ + .pipeline_pool_size = 3 + }); + T(sg_isvalid()); + + sg_pipeline pip[3] = { {0} }; + for (int i = 0; i < 3; i++) { + pip[i] = sg_alloc_pipeline(); + T(pip[i].id != SG_INVALID_ID); + T((2-i) == _sg.pools.pipeline_pool.queue_top); + T(sg_query_pipeline_state(pip[i]) == SG_RESOURCESTATE_ALLOC); + } + + /* the next alloc will fail because the pool is exhausted */ + sg_pipeline p3 = sg_alloc_pipeline(); + T(p3.id == SG_INVALID_ID); + T(sg_query_pipeline_state(p3) == SG_RESOURCESTATE_INVALID); + + /* before destroying, the resources must be either in valid or failed state */ + for (int i = 0; i < 3; i++) { + sg_fail_pipeline(pip[i]); + T(sg_query_pipeline_state(pip[i]) == SG_RESOURCESTATE_FAILED); + } + for (int i = 0; i < 3; i++) { + sg_destroy_pipeline(pip[i]); + T(sg_query_pipeline_state(pip[i]) == SG_RESOURCESTATE_INVALID); + T((i+1) == _sg.pools.pipeline_pool.queue_top); + } + sg_shutdown(); +} + +UTEST(sokol_gfx, alloc_fail_destroy_attachments) { + setup(&(sg_desc){ + .attachments_pool_size = 3 + }); + T(sg_isvalid()); + + sg_attachments atts[3] = { {0} }; + for (int i = 0; i < 3; i++) { + atts[i] = sg_alloc_attachments(); + T(atts[i].id != SG_INVALID_ID); + T((2-i) == _sg.pools.attachments_pool.queue_top); + T(sg_query_attachments_state(atts[i]) == SG_RESOURCESTATE_ALLOC); + } + /* the next alloc will fail because the pool is exhausted */ + sg_attachments a3 = sg_alloc_attachments(); + T(a3.id == SG_INVALID_ID); + T(sg_query_attachments_state(a3) == SG_RESOURCESTATE_INVALID); + + /* before destroying, the resources must be either in valid or failed state */ + for (int i = 0; i < 3; i++) { + sg_fail_attachments(atts[i]); + T(sg_query_attachments_state(atts[i]) == SG_RESOURCESTATE_FAILED); + } + for (int i = 0; i < 3; i++) { + sg_destroy_attachments(atts[i]); + T(sg_query_attachments_state(atts[i]) == SG_RESOURCESTATE_INVALID); + T((i+1) == _sg.pools.attachments_pool.queue_top); + } + sg_shutdown(); +} + +UTEST(sokol_gfx, make_destroy_buffers) { + setup(&(sg_desc){ + .buffer_pool_size = 3 + }); + T(sg_isvalid()); + + float data[] = { 1.0f, 2.0f, 3.0f, 4.0f }; + + sg_buffer buf[3] = { {0} }; + sg_buffer_desc desc = { .data = SG_RANGE(data) }; + for (int i = 0; i < 3; i++) { + buf[i] = sg_make_buffer(&desc); + T(buf[i].id != SG_INVALID_ID); + T((2-i) == _sg.pools.buffer_pool.queue_top); + T(sg_query_buffer_state(buf[i]) == SG_RESOURCESTATE_VALID); + const _sg_buffer_t* bufptr = _sg_lookup_buffer(&_sg.pools, buf[i].id); + T(bufptr); + T(bufptr->slot.id == buf[i].id); + T(bufptr->slot.state == SG_RESOURCESTATE_VALID); + T(bufptr->cmn.size == sizeof(data)); + T(bufptr->cmn.append_pos == 0); + T(!bufptr->cmn.append_overflow); + T(bufptr->cmn.type == SG_BUFFERTYPE_VERTEXBUFFER); + T(bufptr->cmn.usage == SG_USAGE_IMMUTABLE); + T(bufptr->cmn.update_frame_index == 0); + T(bufptr->cmn.append_frame_index == 0); + T(bufptr->cmn.num_slots == 1); + T(bufptr->cmn.active_slot == 0); + } + /* trying to create another one fails because pool is exhausted */ + T(sg_make_buffer(&desc).id == SG_INVALID_ID); + + for (int i = 0; i < 3; i++) { + sg_destroy_buffer(buf[i]); + T(sg_query_buffer_state(buf[i]) == SG_RESOURCESTATE_INVALID); + T((i+1) == _sg.pools.buffer_pool.queue_top); + } + sg_shutdown(); +} + +UTEST(sokol_gfx, make_destroy_images) { + setup(&(sg_desc){ + .image_pool_size = 3 + }); + T(sg_isvalid()); + + uint32_t data[8*8] = { 0 }; + + sg_image img[3] = { {0} }; + sg_image_desc desc = { + .width = 8, + .height = 8, + .data.subimage[0][0] = SG_RANGE(data) + }; + for (int i = 0; i < 3; i++) { + img[i] = sg_make_image(&desc); + T(img[i].id != SG_INVALID_ID); + T((2-i) == _sg.pools.image_pool.queue_top); + T(sg_query_image_state(img[i]) == SG_RESOURCESTATE_VALID); + const _sg_image_t* imgptr = _sg_lookup_image(&_sg.pools, img[i].id); + T(imgptr); + T(imgptr->slot.id == img[i].id); + T(imgptr->slot.state == SG_RESOURCESTATE_VALID); + T(imgptr->cmn.type == SG_IMAGETYPE_2D); + T(!imgptr->cmn.render_target); + T(imgptr->cmn.width == 8); + T(imgptr->cmn.height == 8); + T(imgptr->cmn.num_slices == 1); + T(imgptr->cmn.num_mipmaps == 1); + T(imgptr->cmn.usage == SG_USAGE_IMMUTABLE); + T(imgptr->cmn.pixel_format == SG_PIXELFORMAT_RGBA8); + T(imgptr->cmn.sample_count == 1); + T(imgptr->cmn.upd_frame_index == 0); + T(imgptr->cmn.num_slots == 1); + T(imgptr->cmn.active_slot == 0); + } + // trying to create another one fails because pool is exhausted + T(sg_make_image(&desc).id == SG_INVALID_ID); + + for (int i = 0; i < 3; i++) { + sg_destroy_image(img[i]); + T(sg_query_image_state(img[i]) == SG_RESOURCESTATE_INVALID); + T((i+1) == _sg.pools.image_pool.queue_top); + } + sg_shutdown(); +} + +UTEST(sokol_gfx, make_destroy_samplers) { + setup(&(sg_desc){ + .sampler_pool_size = 3 + }); + T(sg_isvalid()); + + sg_sampler smp[3] = { {0} }; + sg_sampler_desc desc = { 0 }; + for (int i = 0; i < 3; i++) { + smp[i] = sg_make_sampler(&desc); + T(smp[i].id != SG_INVALID_ID); + T((2-i) == _sg.pools.sampler_pool.queue_top); + T(sg_query_sampler_state(smp[i]) == SG_RESOURCESTATE_VALID); + const _sg_sampler_t* smpptr = _sg_lookup_sampler(&_sg.pools, smp[i].id); + T(smpptr); + T(smpptr->slot.id == smp[i].id); + T(smpptr->slot.state == SG_RESOURCESTATE_VALID); + T(smpptr->cmn.min_filter == SG_FILTER_NEAREST); + T(smpptr->cmn.mag_filter == SG_FILTER_NEAREST); + T(smpptr->cmn.mipmap_filter == SG_FILTER_NONE); + T(smpptr->cmn.wrap_u == SG_WRAP_REPEAT); + T(smpptr->cmn.wrap_v == SG_WRAP_REPEAT); + T(smpptr->cmn.wrap_w == SG_WRAP_REPEAT); + T(smpptr->cmn.min_lod == 0.0f); + T(smpptr->cmn.max_lod == FLT_MAX); + T(smpptr->cmn.border_color == SG_BORDERCOLOR_OPAQUE_BLACK); + T(smpptr->cmn.compare == SG_COMPAREFUNC_NEVER); + T(smpptr->cmn.max_anisotropy == 1); + } + // trying to create another one fails because pool is exhausted + T(sg_make_sampler(&desc).id == SG_INVALID_ID); + + for (int i = 0; i < 3; i++) { + sg_destroy_sampler(smp[i]); + T(sg_query_sampler_state(smp[i]) == SG_RESOURCESTATE_INVALID); + T((i+1) == _sg.pools.sampler_pool.queue_top); + } + sg_shutdown(); +} + +UTEST(sokol_gfx, make_destroy_shaders) { + setup(&(sg_desc){ + .shader_pool_size = 3 + }); + T(sg_isvalid()); + + sg_shader shd[3] = { {0} }; + sg_shader_desc desc = { + .vs.uniform_blocks[0] = { + .size = 16 + } + }; + for (int i = 0; i < 3; i++) { + shd[i] = sg_make_shader(&desc); + T(shd[i].id != SG_INVALID_ID); + T((2-i) == _sg.pools.shader_pool.queue_top); + T(sg_query_shader_state(shd[i]) == SG_RESOURCESTATE_VALID); + const _sg_shader_t* shdptr = _sg_lookup_shader(&_sg.pools, shd[i].id); + T(shdptr); + T(shdptr->slot.id == shd[i].id); + T(shdptr->slot.state == SG_RESOURCESTATE_VALID); + T(shdptr->cmn.stage[SG_SHADERSTAGE_VS].num_uniform_blocks == 1); + T(shdptr->cmn.stage[SG_SHADERSTAGE_VS].num_images == 0); + T(shdptr->cmn.stage[SG_SHADERSTAGE_VS].uniform_blocks[0].size == 16); + T(shdptr->cmn.stage[SG_SHADERSTAGE_FS].num_uniform_blocks == 0); + T(shdptr->cmn.stage[SG_SHADERSTAGE_FS].num_images == 0); + } + /* trying to create another one fails because pool is exhausted */ + T(sg_make_shader(&desc).id == SG_INVALID_ID); + + for (int i = 0; i < 3; i++) { + sg_destroy_shader(shd[i]); + T(sg_query_shader_state(shd[i]) == SG_RESOURCESTATE_INVALID); + T((i+1) == _sg.pools.shader_pool.queue_top); + } + sg_shutdown(); +} + +UTEST(sokol_gfx, make_destroy_pipelines) { + setup(&(sg_desc){ + .pipeline_pool_size = 3 + }); + T(sg_isvalid()); + + sg_pipeline pip[3] = { {0} }; + sg_pipeline_desc desc = { + .shader = sg_make_shader(&(sg_shader_desc){ 0 }), + .layout = { + .attrs = { + [0] = { .format=SG_VERTEXFORMAT_FLOAT3 }, + [1] = { .format=SG_VERTEXFORMAT_FLOAT4 } + } + }, + }; + for (int i = 0; i < 3; i++) { + pip[i] = sg_make_pipeline(&desc); + T(pip[i].id != SG_INVALID_ID); + T((2-i) == _sg.pools.pipeline_pool.queue_top); + T(sg_query_pipeline_state(pip[i]) == SG_RESOURCESTATE_VALID); + const _sg_pipeline_t* pipptr = _sg_lookup_pipeline(&_sg.pools, pip[i].id); + T(pipptr); + T(pipptr->slot.id == pip[i].id); + T(pipptr->slot.state == SG_RESOURCESTATE_VALID); + T(pipptr->shader == _sg_lookup_shader(&_sg.pools, desc.shader.id)); + T(pipptr->cmn.shader_id.id == desc.shader.id); + T(pipptr->cmn.color_count == 1); + T(pipptr->cmn.colors[0].pixel_format == SG_PIXELFORMAT_RGBA8); + T(pipptr->cmn.depth.pixel_format == SG_PIXELFORMAT_DEPTH_STENCIL); + T(pipptr->cmn.sample_count == 1); + T(pipptr->cmn.index_type == SG_INDEXTYPE_NONE); + T(pipptr->cmn.vertex_buffer_layout_active[0]); + T(!pipptr->cmn.vertex_buffer_layout_active[1]); + } + /* trying to create another one fails because pool is exhausted */ + T(sg_make_pipeline(&desc).id == SG_INVALID_ID); + + for (int i = 0; i < 3; i++) { + sg_destroy_pipeline(pip[i]); + T(sg_query_pipeline_state(pip[i]) == SG_RESOURCESTATE_INVALID); + T((i+1) == _sg.pools.pipeline_pool.queue_top); + } + sg_shutdown(); +} + +UTEST(sokol_gfx, make_destroy_attachments) { + setup(&(sg_desc){ + .attachments_pool_size = 3 + }); + T(sg_isvalid()); + + sg_attachments atts[3] = { {0} }; + + sg_image_desc img_desc = { + .render_target = true, + .width = 128, + .height = 128, + }; + sg_attachments_desc atts_desc = (sg_attachments_desc){ + .colors = { + [0].image = sg_make_image(&img_desc), + [1].image = sg_make_image(&img_desc), + [2].image = sg_make_image(&img_desc) + }, + }; + for (int i = 0; i < 3; i++) { + atts[i] = sg_make_attachments(&atts_desc); + T(atts[i].id != SG_INVALID_ID); + T((2-i) == _sg.pools.attachments_pool.queue_top); + T(sg_query_attachments_state(atts[i]) == SG_RESOURCESTATE_VALID); + const _sg_attachments_t* attsptr = _sg_lookup_attachments(&_sg.pools, atts[i].id); + T(attsptr); + T(attsptr->slot.id == atts[i].id); + T(attsptr->slot.state == SG_RESOURCESTATE_VALID); + T(attsptr->cmn.num_colors == 3); + for (int ai = 0; ai < 3; ai++) { + const _sg_image_t* img = _sg_attachments_color_image(attsptr, ai); + T(img == _sg_lookup_image(&_sg.pools, atts_desc.colors[ai].image.id)); + T(attsptr->cmn.colors[ai].image_id.id == atts_desc.colors[ai].image.id); + } + } + /* trying to create another one fails because pool is exhausted */ + T(sg_make_attachments(&atts_desc).id == SG_INVALID_ID); + + for (int i = 0; i < 3; i++) { + sg_destroy_attachments(atts[i]); + T(sg_query_attachments_state(atts[i]) == SG_RESOURCESTATE_INVALID); + T((i+1) == _sg.pools.attachments_pool.queue_top); + } + sg_shutdown(); +} + +UTEST(sokol_gfx, generation_counter) { + setup(&(sg_desc){ + .buffer_pool_size = 1, + }); + + static float data[] = { 1.0f, 2.0f, 3.0f, 4.0f }; + for (int i = 0; i < 64; i++) { + sg_buffer buf = sg_make_buffer(&(sg_buffer_desc){ .data = SG_RANGE(data) }); + T(buf.id != SG_INVALID_ID); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_VALID); + T((buf.id >> 16) == (uint32_t)(i + 1)); /* this is the generation counter */ + T(_sg_slot_index(buf.id) == 1); /* slot index should remain the same */ + sg_destroy_buffer(buf); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_INVALID); + } + sg_shutdown(); +} + +UTEST(sokol_gfx, query_buffer_defaults) { + setup(&(sg_desc){0}); + sg_buffer_desc desc; + desc = sg_query_buffer_defaults(&(sg_buffer_desc){0}); + T(desc.type == SG_BUFFERTYPE_VERTEXBUFFER); + T(desc.usage == SG_USAGE_IMMUTABLE); + desc = sg_query_buffer_defaults(&(sg_buffer_desc){ + .type = SG_BUFFERTYPE_INDEXBUFFER, + }); + T(desc.type == SG_BUFFERTYPE_INDEXBUFFER); + T(desc.usage == SG_USAGE_IMMUTABLE); + desc = sg_query_buffer_defaults(&(sg_buffer_desc){ + .usage = SG_USAGE_DYNAMIC + }); + T(desc.type == SG_BUFFERTYPE_VERTEXBUFFER); + T(desc.usage == SG_USAGE_DYNAMIC); + sg_shutdown(); +} + +UTEST(sokol_gfx, query_image_defaults) { + setup(&(sg_desc){0}); + const sg_image_desc desc = sg_query_image_defaults(&(sg_image_desc){0}); + T(desc.type == SG_IMAGETYPE_2D); + T(!desc.render_target); + T(desc.num_mipmaps == 1); + T(desc.usage == SG_USAGE_IMMUTABLE); + T(desc.pixel_format == SG_PIXELFORMAT_RGBA8); + T(desc.sample_count == 1); + sg_shutdown(); +} + +UTEST(sokol_gfx, query_sampler_defaults) { + setup(&(sg_desc){0}); + const sg_sampler_desc desc = sg_query_sampler_defaults(&(sg_sampler_desc){0}); + T(desc.min_filter == SG_FILTER_NEAREST); + T(desc.mag_filter == SG_FILTER_NEAREST); + T(desc.mipmap_filter == SG_FILTER_NONE); + T(desc.wrap_u == SG_WRAP_REPEAT); + T(desc.wrap_v == SG_WRAP_REPEAT); + T(desc.wrap_w == SG_WRAP_REPEAT); + T(desc.min_lod == 0.0f); + T(desc.max_lod == FLT_MAX); + T(desc.border_color == SG_BORDERCOLOR_OPAQUE_BLACK); + T(desc.compare == SG_COMPAREFUNC_NEVER); + T(desc.max_anisotropy == 1); + sg_shutdown(); +} + +UTEST(sokol_gfx, query_shader_defaults) { + setup(&(sg_desc){0}); + const sg_shader_desc desc = sg_query_shader_defaults(&(sg_shader_desc){0}); + T(0 == strcmp(desc.vs.entry, "main")); + T(0 == strcmp(desc.fs.entry, "main")); + sg_shutdown(); +} + +UTEST(sokol_gfx, query_pipeline_defaults) { + setup(&(sg_desc){0}); + const sg_pipeline_desc desc = sg_query_pipeline_defaults(&(sg_pipeline_desc){ + .layout.attrs = { + [0].format = SG_VERTEXFORMAT_FLOAT3, + [1].format = SG_VERTEXFORMAT_FLOAT4 + } + }); + T(desc.layout.buffers[0].stride == 28); + T(desc.layout.buffers[0].step_rate == 1); + T(desc.layout.buffers[0].step_func == SG_VERTEXSTEP_PER_VERTEX); + T(desc.layout.attrs[0].offset == 0); + T(desc.layout.attrs[0].buffer_index == 0); + T(desc.layout.attrs[0].format == SG_VERTEXFORMAT_FLOAT3); + T(desc.layout.attrs[1].offset == 12); + T(desc.layout.attrs[1].buffer_index == 0); + T(desc.layout.attrs[1].format == SG_VERTEXFORMAT_FLOAT4); + T(desc.stencil.front.fail_op == SG_STENCILOP_KEEP); + T(desc.stencil.front.depth_fail_op == SG_STENCILOP_KEEP); + T(desc.stencil.front.pass_op == SG_STENCILOP_KEEP); + T(desc.stencil.front.compare == SG_COMPAREFUNC_ALWAYS); + T(desc.stencil.back.fail_op == SG_STENCILOP_KEEP); + T(desc.stencil.back.depth_fail_op == SG_STENCILOP_KEEP); + T(desc.stencil.back.pass_op == SG_STENCILOP_KEEP); + T(desc.stencil.back.compare == SG_COMPAREFUNC_ALWAYS); + T(desc.stencil.enabled == false); + T(desc.stencil.read_mask == 0); + T(desc.stencil.write_mask == 0); + T(desc.stencil.ref == 0); + T(desc.depth.pixel_format == SG_PIXELFORMAT_DEPTH_STENCIL); + T(desc.depth.compare == SG_COMPAREFUNC_ALWAYS); + T(desc.depth.write_enabled == false); + T(desc.depth.bias == 0); + T(desc.depth.bias_slope_scale == 0); + T(desc.depth.bias_clamp == 0); + T(desc.color_count == 1); + T(desc.colors[0].pixel_format == SG_PIXELFORMAT_RGBA8); + T(desc.colors[0].write_mask == 0xF); + T(desc.colors[0].blend.enabled == false); + T(desc.colors[0].blend.src_factor_rgb == SG_BLENDFACTOR_ONE); + T(desc.colors[0].blend.dst_factor_rgb == SG_BLENDFACTOR_ZERO); + T(desc.colors[0].blend.op_rgb == SG_BLENDOP_ADD); + T(desc.colors[0].blend.src_factor_alpha == SG_BLENDFACTOR_ONE); + T(desc.colors[0].blend.dst_factor_alpha == SG_BLENDFACTOR_ZERO); + T(desc.colors[0].blend.op_alpha == SG_BLENDOP_ADD); + T(desc.alpha_to_coverage_enabled == false); + T(desc.primitive_type == SG_PRIMITIVETYPE_TRIANGLES); + T(desc.index_type == SG_INDEXTYPE_NONE); + T(desc.cull_mode == SG_CULLMODE_NONE); + T(desc.face_winding == SG_FACEWINDING_CW); + T(desc.sample_count == 1); + sg_shutdown(); +} + +// test that color attachment defaults are set in all attachments +UTEST(sokol_gfx, query_mrt_pipeline_defaults) { + setup(&(sg_desc){0}); + const sg_pipeline_desc desc = sg_query_pipeline_defaults(&(sg_pipeline_desc){ + .color_count = 3, + }); + T(desc.color_count == 3); + for (int i = 0; i < desc.color_count; i++) { + T(desc.colors[i].pixel_format == SG_PIXELFORMAT_RGBA8); + T(desc.colors[i].write_mask == 0xF); + T(desc.colors[i].blend.enabled == false); + T(desc.colors[i].blend.src_factor_rgb == SG_BLENDFACTOR_ONE); + T(desc.colors[i].blend.dst_factor_rgb == SG_BLENDFACTOR_ZERO); + T(desc.colors[i].blend.op_rgb == SG_BLENDOP_ADD); + T(desc.colors[i].blend.src_factor_alpha == SG_BLENDFACTOR_ONE); + T(desc.colors[i].blend.dst_factor_alpha == SG_BLENDFACTOR_ZERO); + T(desc.colors[i].blend.op_alpha == SG_BLENDOP_ADD); + }; + sg_shutdown(); +} + +// test that first color attachment values are duplicated to other attachments +UTEST(sokol_gfx, multiple_color_state) { + setup(&(sg_desc){0}); + const sg_pipeline_desc desc = sg_query_pipeline_defaults(&(sg_pipeline_desc){ + .color_count = 3, + .colors = { + [0] = { + .pixel_format = SG_PIXELFORMAT_R8, + .write_mask = SG_COLORMASK_BA, + .blend = { + .enabled = true, + .src_factor_rgb = SG_BLENDFACTOR_SRC_COLOR, + .dst_factor_rgb = SG_BLENDFACTOR_DST_COLOR, + .op_rgb = SG_BLENDOP_SUBTRACT, + .src_factor_alpha = SG_BLENDFACTOR_SRC_ALPHA, + .dst_factor_alpha = SG_BLENDFACTOR_DST_ALPHA, + .op_alpha = SG_BLENDOP_REVERSE_SUBTRACT + } + }, + [2] = { + .pixel_format = SG_PIXELFORMAT_RG8, + .write_mask = SG_COLORMASK_GA, + .blend = { + .enabled = true, + .src_factor_rgb = SG_BLENDFACTOR_DST_COLOR, + .dst_factor_rgb = SG_BLENDFACTOR_SRC_COLOR, + .op_rgb = SG_BLENDOP_REVERSE_SUBTRACT, + .src_factor_alpha = SG_BLENDFACTOR_DST_ALPHA, + .dst_factor_alpha = SG_BLENDFACTOR_SRC_ALPHA, + .op_alpha = SG_BLENDOP_SUBTRACT + } + }, + } + }); + T(desc.color_count == 3); + + T(desc.colors[0].pixel_format == SG_PIXELFORMAT_R8); + T(desc.colors[0].write_mask == SG_COLORMASK_BA); + T(desc.colors[0].blend.enabled == true); + T(desc.colors[0].blend.src_factor_rgb == SG_BLENDFACTOR_SRC_COLOR); + T(desc.colors[0].blend.dst_factor_rgb == SG_BLENDFACTOR_DST_COLOR); + T(desc.colors[0].blend.op_rgb == SG_BLENDOP_SUBTRACT); + T(desc.colors[0].blend.src_factor_alpha == SG_BLENDFACTOR_SRC_ALPHA); + T(desc.colors[0].blend.dst_factor_alpha == SG_BLENDFACTOR_DST_ALPHA); + T(desc.colors[0].blend.op_alpha == SG_BLENDOP_REVERSE_SUBTRACT); + + T(desc.colors[1].pixel_format == SG_PIXELFORMAT_RGBA8); + T(desc.colors[1].write_mask == SG_COLORMASK_RGBA); + T(desc.colors[1].blend.enabled == false); + T(desc.colors[1].blend.src_factor_rgb == SG_BLENDFACTOR_ONE); + T(desc.colors[1].blend.dst_factor_rgb == SG_BLENDFACTOR_ZERO); + T(desc.colors[1].blend.op_rgb == SG_BLENDOP_ADD); + T(desc.colors[1].blend.src_factor_alpha == SG_BLENDFACTOR_ONE); + T(desc.colors[1].blend.dst_factor_alpha == SG_BLENDFACTOR_ZERO); + T(desc.colors[1].blend.op_alpha == SG_BLENDOP_ADD); + + T(desc.colors[2].pixel_format == SG_PIXELFORMAT_RG8); + T(desc.colors[2].write_mask == SG_COLORMASK_GA); + T(desc.colors[2].blend.enabled == true); + T(desc.colors[2].blend.src_factor_rgb == SG_BLENDFACTOR_DST_COLOR); + T(desc.colors[2].blend.dst_factor_rgb == SG_BLENDFACTOR_SRC_COLOR); + T(desc.colors[2].blend.op_rgb == SG_BLENDOP_REVERSE_SUBTRACT); + T(desc.colors[2].blend.src_factor_alpha == SG_BLENDFACTOR_DST_ALPHA); + T(desc.colors[2].blend.dst_factor_alpha == SG_BLENDFACTOR_SRC_ALPHA); + T(desc.colors[2].blend.op_alpha == SG_BLENDOP_SUBTRACT); + + sg_shutdown(); +} + +UTEST(sokol_gfx, query_attachments_defaults) { + setup(&(sg_desc){0}); + /* sg_attachments_desc doesn't actually have any meaningful default values */ + const sg_attachments_desc desc = sg_query_attachments_defaults(&(sg_attachments_desc){0}); + T(desc.colors[0].image.id == SG_INVALID_ID); + T(desc.colors[0].mip_level == 0); + sg_shutdown(); +} + +UTEST(sokol_gfx, query_buffer_info) { + setup(&(sg_desc){0}); + sg_buffer buf = sg_make_buffer(&(sg_buffer_desc){ + .size = 256, + .type = SG_BUFFERTYPE_VERTEXBUFFER, + .usage = SG_USAGE_STREAM + }); + T(buf.id != SG_INVALID_ID); + const sg_buffer_info info = sg_query_buffer_info(buf); + T(info.slot.state == SG_RESOURCESTATE_VALID); + T(info.slot.res_id == buf.id); + sg_shutdown(); +} + +UTEST(sokol_gfx, query_image_info) { + setup(&(sg_desc){0}); + sg_image img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 128 + }); + T(img.id != SG_INVALID_ID); + const sg_image_info info = sg_query_image_info(img); + T(info.slot.state == SG_RESOURCESTATE_VALID); + T(info.slot.res_id == img.id); + T(info.num_slots == 1); + sg_shutdown(); +} + +UTEST(sokoL_gfx, query_sampler_info) { + setup(&(sg_desc){0}); + sg_sampler smp = sg_make_sampler(&(sg_sampler_desc){ 0 }); + T(smp.id != SG_INVALID_ID); + const sg_sampler_info info = sg_query_sampler_info(smp); + T(info.slot.state == SG_RESOURCESTATE_VALID); + T(info.slot.res_id == smp.id); + sg_shutdown(); +} + +UTEST(sokol_gfx, query_shader_info) { + setup(&(sg_desc){0}); + sg_shader shd = sg_make_shader(&(sg_shader_desc){ + .attrs = { + [0] = { .name = "pos" } + }, + .vs.source = "bla", + .fs.source = "blub" + }); + const sg_shader_info info = sg_query_shader_info(shd); + T(info.slot.state == SG_RESOURCESTATE_VALID); + T(info.slot.res_id == shd.id); + sg_shutdown(); +} + +UTEST(sokol_gfx, query_pipeline_info) { + setup(&(sg_desc){0}); + sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){ + .layout = { + .attrs[0].format = SG_VERTEXFORMAT_FLOAT3 + }, + .shader = sg_make_shader(&(sg_shader_desc){ + .attrs = { + [0] = { .name = "pos" } + }, + .vs.source = "bla", + .fs.source = "blub" + }) + }); + const sg_pipeline_info info = sg_query_pipeline_info(pip); + T(info.slot.state == SG_RESOURCESTATE_VALID); + T(info.slot.res_id == pip.id); + sg_shutdown(); +} + +UTEST(sokol_gfx, query_attachments_info) { + setup(&(sg_desc){0}); + sg_image_desc img_desc = { + .render_target = true, + .width = 128, + .height = 128, + }; + sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors = { + [0].image = sg_make_image(&img_desc), + [1].image = sg_make_image(&img_desc), + [2].image = sg_make_image(&img_desc) + }, + }); + const sg_attachments_info info = sg_query_attachments_info(atts); + T(info.slot.state == SG_RESOURCESTATE_VALID); + T(info.slot.res_id == atts.id); + sg_shutdown(); +} + +UTEST(sokol_gfx, query_buffer_desc) { + setup(&(sg_desc){0}); + + sg_buffer b0 = sg_make_buffer(&(sg_buffer_desc){ + .size = 32, + .usage = SG_USAGE_STREAM, + .label = "bla", + }); + const sg_buffer_desc b0_desc = sg_query_buffer_desc(b0); + T(b0_desc.size == 32); + T(b0_desc.type == SG_BUFFERTYPE_VERTEXBUFFER); + T(b0_desc.usage == SG_USAGE_STREAM); + T(b0_desc.data.ptr == 0); + T(b0_desc.data.size == 0); + T(b0_desc.gl_buffers[0] == 0); + T(b0_desc.mtl_buffers[0] == 0); + T(b0_desc.d3d11_buffer == 0); + T(b0_desc.wgpu_buffer == 0); + + float vtx_data[16]; + sg_buffer b1 = sg_make_buffer(&(sg_buffer_desc){ + .data = SG_RANGE(vtx_data) + }); + const sg_buffer_desc b1_desc = sg_query_buffer_desc(b1); + T(b1_desc.size == sizeof(vtx_data)); + T(b1_desc.type == SG_BUFFERTYPE_VERTEXBUFFER); + T(b1_desc.usage == SG_USAGE_IMMUTABLE); + T(b1_desc.data.ptr == 0); + T(b1_desc.data.size == 0); + + uint16_t idx_data[8]; + sg_buffer b2 = sg_make_buffer(&(sg_buffer_desc){ + .type = SG_BUFFERTYPE_INDEXBUFFER, + .data = SG_RANGE(idx_data), + }); + const sg_buffer_desc b2_desc = sg_query_buffer_desc(b2); + T(b2_desc.size == sizeof(idx_data)); + T(b2_desc.type == SG_BUFFERTYPE_INDEXBUFFER); + T(b2_desc.usage == SG_USAGE_IMMUTABLE); + T(b2_desc.data.ptr == 0); + T(b2_desc.data.size == 0); + + // invalid buffer (returns zeroed desc) + sg_buffer b3 = sg_make_buffer(&(sg_buffer_desc){ + .size = 32, + .usage = SG_USAGE_STREAM, + .label = "bla", + }); + sg_destroy_buffer(b3); + const sg_buffer_desc b3_desc = sg_query_buffer_desc(b3); + T(b3_desc.size == 0); + T(b3_desc.type == 0); + T(b3_desc.usage == 0); + + sg_shutdown(); +} + +UTEST(sokol_gfx, query_image_desc) { + setup(&(sg_desc){0}); + + sg_image i0 = sg_make_image(&(sg_image_desc){ + .width = 256, + .height = 512, + .pixel_format = SG_PIXELFORMAT_R8, + .usage = SG_USAGE_DYNAMIC, + }); + const sg_image_desc i0_desc = sg_query_image_desc(i0); + T(i0_desc.type == SG_IMAGETYPE_2D); + T(i0_desc.render_target == false); + T(i0_desc.width == 256); + T(i0_desc.height == 512); + T(i0_desc.num_slices == 1); + T(i0_desc.num_mipmaps == 1); + T(i0_desc.usage == SG_USAGE_DYNAMIC); + T(i0_desc.pixel_format == SG_PIXELFORMAT_R8); + T(i0_desc.sample_count == 1); + T(i0_desc.data.subimage[0][0].ptr == 0); + T(i0_desc.data.subimage[0][0].size == 0); + T(i0_desc.gl_textures[0] == 0); + T(i0_desc.gl_texture_target == 0); + T(i0_desc.mtl_textures[0] == 0); + T(i0_desc.d3d11_texture == 0); + T(i0_desc.d3d11_shader_resource_view == 0); + T(i0_desc.wgpu_texture == 0); + + sg_destroy_image(i0); + const sg_image_desc i0_desc_x = sg_query_image_desc(i0); + T(i0_desc_x.type == 0); + T(i0_desc_x.render_target == false); + T(i0_desc_x.width == 0); + T(i0_desc_x.height == 0); + T(i0_desc_x.num_slices == 0); + T(i0_desc_x.num_mipmaps == 0); + T(i0_desc_x.usage == 0); + T(i0_desc_x.pixel_format == 0); + T(i0_desc_x.sample_count == 0); + + sg_shutdown(); +} + +UTEST(sokol_gfx, query_sampler_desc) { + setup(&(sg_desc){0}); + sg_sampler s0 = sg_make_sampler(&(sg_sampler_desc){ + .min_filter = SG_FILTER_LINEAR, + .mag_filter = SG_FILTER_LINEAR, + .mipmap_filter = SG_FILTER_LINEAR, + .wrap_v = SG_WRAP_MIRRORED_REPEAT, + .max_anisotropy = 8, + .border_color = SG_BORDERCOLOR_TRANSPARENT_BLACK, + .compare = SG_COMPAREFUNC_GREATER, + }); + const sg_sampler_desc s0_desc = sg_query_sampler_desc(s0); + T(s0_desc.min_filter == SG_FILTER_LINEAR); + T(s0_desc.mag_filter == SG_FILTER_LINEAR); + T(s0_desc.mipmap_filter == SG_FILTER_LINEAR); + T(s0_desc.wrap_u == SG_WRAP_REPEAT); + T(s0_desc.wrap_v == SG_WRAP_MIRRORED_REPEAT); + T(s0_desc.wrap_w == SG_WRAP_REPEAT); + T(s0_desc.min_lod == 0.0f); + T(s0_desc.max_lod == FLT_MAX); + T(s0_desc.border_color == SG_BORDERCOLOR_TRANSPARENT_BLACK); + T(s0_desc.compare == SG_COMPAREFUNC_GREATER); + T(s0_desc.max_anisotropy == 8); + + sg_destroy_sampler(s0); + const sg_sampler_desc s0_desc_x = sg_query_sampler_desc(s0); + T(s0_desc_x.min_filter == 0); + T(s0_desc_x.compare == 0); + + sg_shutdown(); +} + +UTEST(sokol_gfx, query_shader_desc) { + setup(&(sg_desc){0}); + + sg_shader s0 = sg_make_shader(&(sg_shader_desc){ + .attrs = { + [0] = { .name = "pos", .sem_name = "POS", .sem_index = 1 }, + }, + .vs = { + .source = "vs_source", + .uniform_blocks = { + [0] = { + .size = 128, + .layout = SG_UNIFORMLAYOUT_STD140, + .uniforms = { + [0] = { .name = "blub", .type = SG_UNIFORMTYPE_FLOAT4, .array_count = 1 }, + [1] = { .name = "blob", .type = SG_UNIFORMTYPE_FLOAT2, .array_count = 1 }, + } + } + }, + .images[0] = { .used = true, .image_type = SG_IMAGETYPE_2D, .sample_type = SG_IMAGESAMPLETYPE_FLOAT, .multisampled = true }, + .images[1] = { .used = true, .image_type = SG_IMAGETYPE_3D, .sample_type = SG_IMAGESAMPLETYPE_DEPTH }, + .samplers[0] = { .used = true, .sampler_type = SG_SAMPLERTYPE_FILTERING }, + .samplers[1] = { .used = true, .sampler_type = SG_SAMPLERTYPE_COMPARISON }, + .image_sampler_pairs[0] = { .used = true, .image_slot = 0, .sampler_slot = 0, .glsl_name = "img0" }, + .image_sampler_pairs[1] = { .used = true, .image_slot = 1, .sampler_slot = 1, .glsl_name = "img1" }, + }, + .fs = { + .source = "fs_source", + .images[0] = { .used = true, .image_type = SG_IMAGETYPE_ARRAY, .sample_type = SG_IMAGESAMPLETYPE_DEPTH }, + .images[1] = { .used = true, .image_type = SG_IMAGETYPE_CUBE, .sample_type = SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT }, + .samplers[0] = { .used = true, .sampler_type = SG_SAMPLERTYPE_COMPARISON }, + .samplers[1] = { .used = true, .sampler_type = SG_SAMPLERTYPE_NONFILTERING }, + .image_sampler_pairs[0] = { .used = true, .image_slot = 0, .sampler_slot = 0, .glsl_name = "img3" }, + .image_sampler_pairs[1] = { .used = true, .image_slot = 1, .sampler_slot = 1, .glsl_name = "img4" }, + }, + .label = "label", + }); + const sg_shader_desc s0_desc = sg_query_shader_desc(s0); + T(s0_desc.attrs[0].name == 0); + T(s0_desc.attrs[0].sem_name == 0); + T(s0_desc.attrs[0].sem_index == 0); + T(s0_desc.vs.source == 0); + T(s0_desc.vs.uniform_blocks[0].size == 128); + T(s0_desc.vs.uniform_blocks[0].layout == 0); + T(s0_desc.vs.uniform_blocks[0].uniforms[0].name == 0); + T(s0_desc.vs.uniform_blocks[0].uniforms[0].type == 0); + T(s0_desc.vs.uniform_blocks[0].uniforms[0].array_count == 0); + T(s0_desc.vs.images[0].used); + T(s0_desc.vs.images[0].image_type == SG_IMAGETYPE_2D); + T(s0_desc.vs.images[0].sample_type == SG_IMAGESAMPLETYPE_FLOAT); + T(s0_desc.vs.images[0].multisampled); + T(s0_desc.vs.images[1].used); + T(s0_desc.vs.images[1].image_type == SG_IMAGETYPE_3D); + T(s0_desc.vs.images[1].sample_type == SG_IMAGESAMPLETYPE_DEPTH); + T(s0_desc.vs.images[1].multisampled == false); + T(s0_desc.vs.samplers[0].used); + T(s0_desc.vs.samplers[0].sampler_type == SG_SAMPLERTYPE_FILTERING); + T(s0_desc.vs.samplers[1].used); + T(s0_desc.vs.samplers[1].sampler_type == SG_SAMPLERTYPE_COMPARISON); + T(s0_desc.vs.image_sampler_pairs[0].used); + T(s0_desc.vs.image_sampler_pairs[0].image_slot == 0); + T(s0_desc.vs.image_sampler_pairs[0].sampler_slot == 0); + T(s0_desc.vs.image_sampler_pairs[0].glsl_name == 0); + T(s0_desc.vs.image_sampler_pairs[1].used); + T(s0_desc.vs.image_sampler_pairs[1].image_slot == 1); + T(s0_desc.vs.image_sampler_pairs[1].sampler_slot == 1); + T(s0_desc.vs.image_sampler_pairs[1].glsl_name == 0); + T(s0_desc.fs.source == 0); + T(s0_desc.fs.uniform_blocks[0].size == 0); + T(s0_desc.fs.uniform_blocks[0].layout == 0); + T(s0_desc.fs.uniform_blocks[0].uniforms[0].name == 0); + T(s0_desc.fs.uniform_blocks[0].uniforms[0].type == 0); + T(s0_desc.fs.uniform_blocks[0].uniforms[0].array_count == 0); + T(s0_desc.fs.images[0].used); + T(s0_desc.fs.images[0].image_type == SG_IMAGETYPE_ARRAY); + T(s0_desc.fs.images[0].sample_type == SG_IMAGESAMPLETYPE_DEPTH); + T(s0_desc.fs.images[0].multisampled == false); + T(s0_desc.fs.images[1].used); + T(s0_desc.fs.images[1].image_type == SG_IMAGETYPE_CUBE); + T(s0_desc.fs.images[1].sample_type == SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT); + T(s0_desc.fs.images[1].multisampled == false); + T(s0_desc.fs.samplers[0].used); + T(s0_desc.fs.samplers[0].sampler_type == SG_SAMPLERTYPE_COMPARISON); + T(s0_desc.fs.samplers[1].used); + T(s0_desc.fs.samplers[1].sampler_type == SG_SAMPLERTYPE_NONFILTERING); + T(s0_desc.fs.image_sampler_pairs[0].used); + T(s0_desc.fs.image_sampler_pairs[0].image_slot == 0); + T(s0_desc.fs.image_sampler_pairs[0].sampler_slot == 0); + T(s0_desc.fs.image_sampler_pairs[0].glsl_name == 0); + T(s0_desc.fs.image_sampler_pairs[1].used); + T(s0_desc.fs.image_sampler_pairs[1].image_slot == 1); + T(s0_desc.fs.image_sampler_pairs[1].sampler_slot == 1); + T(s0_desc.fs.image_sampler_pairs[1].glsl_name == 0); + + sg_shutdown(); +} + +UTEST(sokol_gfx, query_pipeline_desc) { + setup(&(sg_desc){0}); + + sg_shader shd = sg_make_shader(&(sg_shader_desc){0}); + sg_pipeline p0 = sg_make_pipeline(&(sg_pipeline_desc){ + .shader = shd, + .layout = { + .attrs = { + [0] = { .format = SG_VERTEXFORMAT_FLOAT4 }, + [1] = { .format = SG_VERTEXFORMAT_FLOAT2 }, + } + }, + .label = "p0", + }); + + const sg_pipeline_desc p0_desc = sg_query_pipeline_desc(p0); + T(p0_desc.shader.id == shd.id); + T(p0_desc.layout.buffers[0].stride == 24); + T(p0_desc.layout.buffers[0].step_func == SG_VERTEXSTEP_PER_VERTEX); + T(p0_desc.layout.buffers[0].step_rate == 1); + T(p0_desc.layout.buffers[1].stride == 0); + T(p0_desc.layout.buffers[1].step_func == 0); + T(p0_desc.layout.buffers[1].step_rate == 0); + T(p0_desc.layout.attrs[0].format == SG_VERTEXFORMAT_FLOAT4); + T(p0_desc.layout.attrs[0].offset == 0); + T(p0_desc.layout.attrs[0].buffer_index == 0); + T(p0_desc.layout.attrs[1].format == SG_VERTEXFORMAT_FLOAT2); + T(p0_desc.layout.attrs[1].offset == 16); + T(p0_desc.layout.attrs[1].buffer_index == 0); + T(p0_desc.layout.attrs[2].format == 0); + T(p0_desc.layout.attrs[2].offset == 0); + T(p0_desc.layout.attrs[2].buffer_index == 0); + T(p0_desc.depth.pixel_format == SG_PIXELFORMAT_DEPTH_STENCIL); + T(p0_desc.depth.compare == SG_COMPAREFUNC_ALWAYS); + T(p0_desc.depth.write_enabled == false); + T(p0_desc.depth.bias == 0.0f); + T(p0_desc.depth.bias_slope_scale == 0.0f); + T(p0_desc.depth.bias_clamp == 0.0f); + T(p0_desc.stencil.enabled == false); + T(p0_desc.stencil.front.compare == SG_COMPAREFUNC_ALWAYS); + T(p0_desc.stencil.front.fail_op == SG_STENCILOP_KEEP); + T(p0_desc.stencil.front.depth_fail_op == SG_STENCILOP_KEEP); + T(p0_desc.stencil.front.pass_op == SG_STENCILOP_KEEP); + T(p0_desc.stencil.back.compare == SG_COMPAREFUNC_ALWAYS); + T(p0_desc.stencil.back.fail_op == SG_STENCILOP_KEEP); + T(p0_desc.stencil.back.depth_fail_op == SG_STENCILOP_KEEP); + T(p0_desc.stencil.back.pass_op == SG_STENCILOP_KEEP); + T(p0_desc.stencil.read_mask == 0); + T(p0_desc.stencil.write_mask == 0); + T(p0_desc.stencil.ref == 0); + T(p0_desc.color_count == 1); + T(p0_desc.colors[0].pixel_format == SG_PIXELFORMAT_RGBA8); + T(p0_desc.colors[0].write_mask == SG_COLORMASK_RGBA); + T(p0_desc.colors[0].blend.enabled == false); + T(p0_desc.colors[0].blend.src_factor_rgb == SG_BLENDFACTOR_ONE); + T(p0_desc.colors[0].blend.dst_factor_rgb == SG_BLENDFACTOR_ZERO); + T(p0_desc.colors[0].blend.op_rgb == SG_BLENDOP_ADD); + T(p0_desc.colors[0].blend.src_factor_alpha == SG_BLENDFACTOR_ONE); + T(p0_desc.colors[0].blend.dst_factor_alpha == SG_BLENDFACTOR_ZERO); + T(p0_desc.colors[0].blend.op_alpha == SG_BLENDOP_ADD); + T(p0_desc.primitive_type == SG_PRIMITIVETYPE_TRIANGLES); + T(p0_desc.index_type == SG_INDEXTYPE_NONE); + T(p0_desc.cull_mode == SG_CULLMODE_NONE); + T(p0_desc.face_winding == SG_FACEWINDING_CW); + T(p0_desc.sample_count == 1); + T(p0_desc.blend_color.r == 0.0f); + T(p0_desc.blend_color.g == 0.0f); + T(p0_desc.blend_color.b == 0.0f); + T(p0_desc.blend_color.a == 0.0f); + T(p0_desc.alpha_to_coverage_enabled == false); + T(p0_desc.label == 0); + sg_shutdown(); +} + +UTEST(sokol_gfx, query_attachments_desc) { + setup(&(sg_desc){0}); + + const sg_image_desc color_img_desc = { + .render_target = true, + .width = 128, + .height = 128, + }; + const sg_image_desc depth_img_desc = { + .render_target = true, + .width = 128, + .height = 128, + .pixel_format = SG_PIXELFORMAT_DEPTH, + }; + sg_image color_img_0 = sg_make_image(&color_img_desc); + sg_image color_img_1 = sg_make_image(&color_img_desc); + sg_image color_img_2 = sg_make_image(&color_img_desc); + sg_image depth_img = sg_make_image(&depth_img_desc); + + sg_attachments a0 = sg_make_attachments(&(sg_attachments_desc){ + .colors = { + [0].image = color_img_0, + [1].image = color_img_1, + [2].image = color_img_2, + }, + .depth_stencil.image = depth_img, + }); + const sg_attachments_desc a0_desc = sg_query_attachments_desc(a0); + T(a0_desc.colors[0].image.id == color_img_0.id); + T(a0_desc.colors[0].mip_level == 0); + T(a0_desc.colors[0].slice == 0); + T(a0_desc.colors[1].image.id == color_img_1.id); + T(a0_desc.colors[1].mip_level == 0); + T(a0_desc.colors[1].slice == 0); + T(a0_desc.colors[2].image.id == color_img_2.id); + T(a0_desc.colors[2].mip_level == 0); + T(a0_desc.colors[2].slice == 0); + T(a0_desc.depth_stencil.image.id == depth_img.id); + T(a0_desc.depth_stencil.mip_level == 0); + T(a0_desc.depth_stencil.slice == 0); + + sg_shutdown(); +} + +UTEST(sokol_gfx, buffer_resource_states) { + setup(&(sg_desc){0}); + sg_buffer buf = sg_alloc_buffer(); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_ALLOC); + sg_init_buffer(buf, &(sg_buffer_desc){ .usage = SG_USAGE_STREAM, .size = 128 }); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_VALID); + sg_uninit_buffer(buf); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_ALLOC); + sg_dealloc_buffer(buf); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, image_resource_states) { + setup(&(sg_desc){0}); + sg_image img = sg_alloc_image(); + T(sg_query_image_state(img) == SG_RESOURCESTATE_ALLOC); + sg_init_image(img, &(sg_image_desc){ .render_target = true, .width = 16, .height = 16 }); + T(sg_query_image_state(img) == SG_RESOURCESTATE_VALID); + sg_uninit_image(img); + T(sg_query_image_state(img) == SG_RESOURCESTATE_ALLOC); + sg_dealloc_image(img); + T(sg_query_image_state(img) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, sampler_resource_states) { + setup(&(sg_desc){0}); + sg_sampler smp = sg_alloc_sampler(); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_ALLOC); + sg_init_sampler(smp, &(sg_sampler_desc){ .min_filter = SG_FILTER_LINEAR, .mag_filter = SG_FILTER_LINEAR }); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_VALID); + sg_uninit_sampler(smp); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_ALLOC); + sg_dealloc_sampler(smp); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, shader_resource_states) { + setup(&(sg_desc){0}); + sg_shader shd = sg_alloc_shader(); + T(sg_query_shader_state(shd) == SG_RESOURCESTATE_ALLOC); + sg_init_shader(shd, &(sg_shader_desc){0}); + T(sg_query_shader_state(shd) == SG_RESOURCESTATE_VALID); + sg_uninit_shader(shd); + T(sg_query_shader_state(shd) == SG_RESOURCESTATE_ALLOC); + sg_dealloc_shader(shd); + T(sg_query_shader_state(shd) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, pipeline_resource_states) { + setup(&(sg_desc){0}); + sg_pipeline pip = sg_alloc_pipeline(); + T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_ALLOC); + sg_init_pipeline(pip, &(sg_pipeline_desc){ + .shader = sg_make_shader(&(sg_shader_desc){0}), + .layout.attrs[0].format = SG_VERTEXFORMAT_FLOAT3 + }); + T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_VALID); + sg_uninit_pipeline(pip); + T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_ALLOC); + sg_dealloc_pipeline(pip); + T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, attachments_resource_states) { + setup(&(sg_desc){0}); + sg_attachments atts = sg_alloc_attachments(); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_ALLOC); + sg_init_attachments(atts, &(sg_attachments_desc){ + .colors[0].image = sg_make_image(&(sg_image_desc){ .render_target=true, .width=16, .height=16}) + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_VALID); + sg_uninit_attachments(atts); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_ALLOC); + sg_dealloc_attachments(atts); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, query_buffer_will_overflow) { + setup(&(sg_desc){0}); + sg_buffer buf = sg_make_buffer(&(sg_buffer_desc){ + .size = 64, + .usage = SG_USAGE_STREAM + }); + T(!sg_query_buffer_will_overflow(buf, 32)); + T(!sg_query_buffer_will_overflow(buf, 64)); + T(sg_query_buffer_will_overflow(buf, 65)); + static const uint8_t data[32] = {0}; + sg_append_buffer(buf, &SG_RANGE(data)); + T(!sg_query_buffer_will_overflow(buf, 32)); + T(sg_query_buffer_will_overflow(buf, 33)); + sg_shutdown(); +} + +static struct { + uintptr_t userdata; + int num_called; +} commit_listener; +static void reset_commit_listener(void) { + commit_listener.userdata = 0; + commit_listener.num_called = 0; +} +static void commit_listener_func(void* ud) { + commit_listener.userdata = (uintptr_t)ud; + commit_listener.num_called++; +} + +UTEST(sokol_gfx, commit_listener_called) { + reset_commit_listener(); + setup(&(sg_desc){0}); + const bool added = sg_add_commit_listener((sg_commit_listener){ + .func = commit_listener_func, + .user_data = (void*)23, + }); + T(added); + T(_sg.commit_listeners.upper == 1); + sg_commit(); + T(23 == commit_listener.userdata); + T(1 == commit_listener.num_called); + sg_shutdown(); +} + +UTEST(sokol_gfx, commit_listener_add_twice) { + reset_commit_listener(); + setup(&(sg_desc){0}); + const sg_commit_listener listener = { + .func = commit_listener_func, + .user_data = (void*)23, + }; + T(sg_add_commit_listener(listener)); + T(_sg.commit_listeners.upper == 1); + T(!sg_add_commit_listener(listener)); + T(_sg.commit_listeners.upper == 1); + sg_commit(); + T(23 == commit_listener.userdata); + T(1 == commit_listener.num_called); + sg_shutdown(); +} + +UTEST(sokol_gfx, commit_listener_same_func_diff_ud) { + reset_commit_listener(); + setup(&(sg_desc){0}); + T(sg_add_commit_listener((sg_commit_listener){ + .func = commit_listener_func, + .user_data = (void*)23, + })); + T(_sg.commit_listeners.upper == 1); + T(sg_add_commit_listener((sg_commit_listener){ + .func = commit_listener_func, + .user_data = (void*)25, + })); + T(_sg.commit_listeners.upper == 2); + sg_commit(); + T(2 == commit_listener.num_called); + sg_shutdown(); +} + +UTEST(sokol_gfx, commit_listener_add_remove_add) { + reset_commit_listener(); + setup(&(sg_desc){0}); + const sg_commit_listener listener = { + .func = commit_listener_func, + .user_data = (void*)23, + }; + T(sg_add_commit_listener(listener)); + T(_sg.commit_listeners.upper == 1); + T(sg_remove_commit_listener(listener)); + T(_sg.commit_listeners.upper == 1); + sg_commit(); + T(0 == commit_listener.num_called); + T(sg_add_commit_listener(listener)); + T(_sg.commit_listeners.upper == 1); + sg_commit(); + T(1 == commit_listener.num_called); + T(23 == commit_listener.userdata); + sg_shutdown(); +} + +UTEST(sokol_gfx, commit_listener_remove_non_existent) { + reset_commit_listener(); + setup(&(sg_desc){0}); + const sg_commit_listener l0 = { + .func = commit_listener_func, + .user_data = (void*)23, + }; + const sg_commit_listener l1 = { + .func = commit_listener_func, + .user_data = (void*)46, + }; + const sg_commit_listener l2 = { + .func = commit_listener_func, + .user_data = (void*)256, + }; + T(sg_add_commit_listener(l0)); + T(sg_add_commit_listener(l1)); + T(_sg.commit_listeners.upper == 2); + T(!sg_remove_commit_listener(l2)); + T(_sg.commit_listeners.upper == 2); + sg_shutdown(); +} + +UTEST(sokol_gfx, commit_listener_multi_add_remove) { + reset_commit_listener(); + setup(&(sg_desc){0}); + const sg_commit_listener l0 = { + .func = commit_listener_func, + .user_data = (void*)23, + }; + const sg_commit_listener l1 = { + .func = commit_listener_func, + .user_data = (void*)46, + }; + T(sg_add_commit_listener(l0)); + T(sg_add_commit_listener(l1)); + T(_sg.commit_listeners.upper == 2); + // removing the first listener will just clear its slot + T(sg_remove_commit_listener(l0)); + T(_sg.commit_listeners.upper == 2); + sg_commit(); + T(commit_listener.num_called == 1); + T(commit_listener.userdata == 46); + commit_listener.num_called = 0; + // adding the first listener back will fill that same slot again + T(sg_add_commit_listener(l0)); + T(_sg.commit_listeners.upper == 2); + sg_commit(); + T(commit_listener.num_called == 2); + T(commit_listener.userdata == 46); + commit_listener.num_called = 0; + // removing the second listener will decrement the upper bound + T(sg_remove_commit_listener(l1)); + T(_sg.commit_listeners.upper == 2); + sg_commit(); + T(commit_listener.num_called == 1); + T(commit_listener.userdata == 23); + commit_listener.num_called = 0; + // and finally remove the first listener too + T(sg_remove_commit_listener(l0)); + T(_sg.commit_listeners.upper == 2); + sg_commit(); + T(commit_listener.num_called == 0); + // removing the same listener twice just returns false + T(!sg_remove_commit_listener(l0)); + T(!sg_remove_commit_listener(l1)); + sg_shutdown(); +} + +UTEST(sokol_gfx, commit_listener_array_full) { + reset_commit_listener(); + setup(&(sg_desc){ + .max_commit_listeners = 3, + }); + const sg_commit_listener l0 = { + .func = commit_listener_func, + .user_data = (void*)23, + }; + const sg_commit_listener l1 = { + .func = commit_listener_func, + .user_data = (void*)46, + }; + const sg_commit_listener l2 = { + .func = commit_listener_func, + .user_data = (void*)128, + }; + const sg_commit_listener l3 = { + .func = commit_listener_func, + .user_data = (void*)256, + }; + T(sg_add_commit_listener(l0)); + T(sg_add_commit_listener(l1)); + T(sg_add_commit_listener(l2)); + T(_sg.commit_listeners.upper == 3); + // overflow! + T(!sg_add_commit_listener(l3)); + T(_sg.commit_listeners.upper == 3); + sg_commit(); + T(commit_listener.num_called == 3); + T(commit_listener.userdata == 128); + sg_shutdown(); +} + +UTEST(sokol_gfx, buffer_double_destroy_is_ok) { + setup(&(sg_desc){0}); + sg_buffer buf = create_buffer(); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_VALID); + sg_destroy_buffer(buf); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_INVALID); + sg_destroy_buffer(buf); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, image_double_destroy_is_ok) { + setup(&(sg_desc){0}); + sg_image img = create_image(); + T(sg_query_image_state(img) == SG_RESOURCESTATE_VALID); + sg_destroy_image(img); + T(sg_query_image_state(img) == SG_RESOURCESTATE_INVALID); + sg_destroy_image(img); + T(sg_query_image_state(img) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, sampler_double_destroy_is_ok) { + setup(&(sg_desc){0}); + sg_sampler smp = sg_make_sampler(&(sg_sampler_desc){0}); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_VALID); + sg_destroy_sampler(smp); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_INVALID); + sg_destroy_sampler(smp); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, shader_double_destroy_is_ok) { + setup(&(sg_desc){0}); + sg_shader shd = create_shader(); + T(sg_query_shader_state(shd) == SG_RESOURCESTATE_VALID); + sg_destroy_shader(shd); + T(sg_query_shader_state(shd) == SG_RESOURCESTATE_INVALID); + sg_destroy_shader(shd); + T(sg_query_shader_state(shd) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, pipeline_double_destroy_is_ok) { + setup(&(sg_desc){0}); + sg_pipeline pip = create_pipeline(); + T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_VALID); + sg_destroy_pipeline(pip); + T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_INVALID); + sg_destroy_pipeline(pip); + T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokoL_gfx, attachments_double_destroy_is_ok) { + setup(&(sg_desc){0}); + sg_attachments atts = create_attachments(); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_VALID); + sg_destroy_attachments(atts); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_INVALID); + sg_destroy_attachments(atts); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_dealloc_buffer_warns) { + setup(&(sg_desc){0}); + sg_buffer buf = create_buffer(); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_VALID); + sg_dealloc_buffer(buf); + T(log_items[0] == SG_LOGITEM_DEALLOC_BUFFER_INVALID_STATE); + T(num_log_called == 1); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_VALID); + sg_destroy_buffer(buf); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_dealloc_image_warns) { + setup(&(sg_desc){0}); + sg_image img = create_image(); + T(sg_query_image_state(img) == SG_RESOURCESTATE_VALID); + sg_dealloc_image(img); + T(log_items[0] == SG_LOGITEM_DEALLOC_IMAGE_INVALID_STATE); + T(num_log_called == 1); + T(sg_query_image_state(img) == SG_RESOURCESTATE_VALID); + sg_destroy_image(img); + T(sg_query_image_state(img) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_dealloc_sampler_warns) { + setup(&(sg_desc){0}); + sg_sampler smp = sg_make_sampler(&(sg_sampler_desc){0}); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_VALID); + sg_dealloc_sampler(smp); + T(log_items[0] == SG_LOGITEM_DEALLOC_SAMPLER_INVALID_STATE); + T(num_log_called == 1); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_VALID); + sg_destroy_sampler(smp); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_dealloc_shader_warns) { + setup(&(sg_desc){0}); + sg_shader shd = create_shader(); + T(sg_query_shader_state(shd) == SG_RESOURCESTATE_VALID); + sg_dealloc_shader(shd); + T(log_items[0] == SG_LOGITEM_DEALLOC_SHADER_INVALID_STATE); + T(num_log_called == 1); + T(sg_query_shader_state(shd) == SG_RESOURCESTATE_VALID); + sg_destroy_shader(shd); + T(sg_query_shader_state(shd) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_dealloc_pipeline_warns) { + setup(&(sg_desc){0}); + sg_pipeline pip = create_pipeline(); + T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_VALID); + sg_dealloc_pipeline(pip); + T(log_items[0] == SG_LOGITEM_DEALLOC_PIPELINE_INVALID_STATE); + T(num_log_called == 1); + T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_VALID); + sg_destroy_pipeline(pip); + T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_dealloc_attachments_warns) { + setup(&(sg_desc){0}); + sg_attachments atts = create_attachments(); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_VALID); + sg_dealloc_attachments(atts); + T(log_items[0] == SG_LOGITEM_DEALLOC_ATTACHMENTS_INVALID_STATE); + T(num_log_called == 1); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_VALID); + sg_destroy_attachments(atts); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, alloc_uninit_buffer_warns) { + setup(&(sg_desc){0}); + sg_buffer buf = sg_alloc_buffer(); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_ALLOC); + sg_uninit_buffer(buf); + T(log_items[0] == SG_LOGITEM_UNINIT_BUFFER_INVALID_STATE); + T(num_log_called == 1); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_ALLOC); + sg_shutdown(); +} + +UTEST(sokol_gfx, alloc_uninit_image_warns) { + setup(&(sg_desc){0}); + sg_image img = sg_alloc_image(); + T(sg_query_image_state(img) == SG_RESOURCESTATE_ALLOC); + sg_uninit_image(img); + T(log_items[0] == SG_LOGITEM_UNINIT_IMAGE_INVALID_STATE); + T(num_log_called == 1); + T(sg_query_image_state(img) == SG_RESOURCESTATE_ALLOC); + sg_shutdown(); +} + +UTEST(sokol_gfx, alloc_uninit_sampler_warns) { + setup(&(sg_desc){0}); + sg_sampler smp = sg_alloc_sampler(); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_ALLOC); + sg_uninit_sampler(smp); + T(log_items[0] == SG_LOGITEM_UNINIT_SAMPLER_INVALID_STATE); + T(num_log_called == 1); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_ALLOC); + sg_shutdown(); +} + +UTEST(sokol_gfx, alloc_uninit_shader_warns) { + setup(&(sg_desc){0}); + sg_shader shd = sg_alloc_shader(); + T(sg_query_shader_state(shd) == SG_RESOURCESTATE_ALLOC); + sg_uninit_shader(shd); + T(log_items[0] == SG_LOGITEM_UNINIT_SHADER_INVALID_STATE); + T(num_log_called == 1); + T(sg_query_shader_state(shd) == SG_RESOURCESTATE_ALLOC); + sg_shutdown(); +} + +UTEST(sokol_gfx, alloc_uninit_pipeline_warns) { + setup(&(sg_desc){0}); + sg_pipeline pip = sg_alloc_pipeline(); + T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_ALLOC); + sg_uninit_pipeline(pip); + T(log_items[0] == SG_LOGITEM_UNINIT_PIPELINE_INVALID_STATE); + T(num_log_called == 1); + T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_ALLOC); + sg_shutdown(); +} + +UTEST(sokol_gfx, alloc_uninit_attachments_warns) { + setup(&(sg_desc){0}); + sg_attachments atts = sg_alloc_attachments(); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_ALLOC); + sg_uninit_attachments(atts); + T(log_items[0] == SG_LOGITEM_UNINIT_ATTACHMENTS_INVALID_STATE); + T(num_log_called == 1); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_ALLOC); + sg_shutdown(); +} + +UTEST(sokol_gfx, alloc_destroy_buffer_is_ok) { + setup(&(sg_desc){0}); + sg_buffer buf = sg_alloc_buffer(); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_ALLOC); + sg_destroy_buffer(buf); + T(num_log_called == 0); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, alloc_destroy_image_is_ok) { + setup(&(sg_desc){0}); + sg_image img = sg_alloc_image(); + T(sg_query_image_state(img) == SG_RESOURCESTATE_ALLOC); + sg_destroy_image(img); + T(num_log_called == 0); + T(sg_query_image_state(img) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, alloc_destroy_sampler_is_ok) { + setup(&(sg_desc){0}); + sg_sampler smp = sg_alloc_sampler(); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_ALLOC); + sg_destroy_sampler(smp); + T(num_log_called == 0); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); + +} + +UTEST(sokol_gfx, alloc_destroy_shader_is_ok) { + setup(&(sg_desc){0}); + sg_shader shd = sg_alloc_shader(); + T(sg_query_shader_state(shd) == SG_RESOURCESTATE_ALLOC); + sg_destroy_shader(shd); + T(num_log_called == 0); + T(sg_query_shader_state(shd) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, alloc_destroy_pipeline_is_ok) { + setup(&(sg_desc){0}); + sg_pipeline pip = sg_alloc_pipeline(); + T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_ALLOC); + sg_destroy_pipeline(pip); + T(num_log_called == 0); + T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, alloc_destroy_attachments_is_ok) { + setup(&(sg_desc){0}); + sg_attachments atts = sg_alloc_attachments(); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_ALLOC); + sg_destroy_attachments(atts); + T(num_log_called == 0); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_pipeline_with_nonvalid_shader) { + setup(&(sg_desc){ + .disable_validation = true, + }); + sg_shader shd = sg_alloc_shader(); + T(sg_query_shader_state(shd) == SG_RESOURCESTATE_ALLOC); + sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){ + .shader = shd, + .layout = { + .attrs[0].format = SG_VERTEXFORMAT_FLOAT3 + }, + }); + T(sg_query_pipeline_state(pip) == SG_RESOURCESTATE_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_with_nonvalid_color_images) { + setup(&(sg_desc){ + .disable_validation = true, + }); + sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors = { + [0].image = sg_alloc_image(), + [1].image = sg_alloc_image(), + }, + .depth_stencil = { + .image = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 128, + .height = 128 + }) + } + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + sg_destroy_attachments(atts); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_INVALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_without_color_attachments) { + setup(&(sg_desc){0}); + sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .depth_stencil.image = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .pixel_format = SG_PIXELFORMAT_DEPTH, + }) + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_VALID); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_buffer_validate_start_canary) { + setup(&(sg_desc){0}); + const uint32_t data[32] = {0}; + sg_buffer buf = sg_make_buffer(&(sg_buffer_desc){ + ._start_canary = 1234, + .data = SG_RANGE(data), + }); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_BUFFERDESC_CANARY); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_buffer_validate_end_canary) { + setup(&(sg_desc){0}); + const uint32_t data[32] = {0}; + sg_buffer buf = sg_make_buffer(&(sg_buffer_desc){ + .data = SG_RANGE(data), + ._end_canary = 1234, + }); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_BUFFERDESC_CANARY); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_buffer_validate_immutable_nodata) { + setup(&(sg_desc){0}); + sg_buffer buf = sg_make_buffer(&(sg_buffer_desc){ 0 }); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_BUFFERDESC_SIZE); + T(log_items[1] == SG_LOGITEM_VALIDATE_BUFFERDESC_DATA); + T(log_items[2] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_buffer_validate_size_mismatch) { + setup(&(sg_desc){0}); + const uint32_t data[16] = { 0 }; + sg_buffer buf = sg_make_buffer(&(sg_buffer_desc){ + .size = 15 * sizeof(uint32_t), + .data = SG_RANGE(data), + }); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_BUFFERDESC_DATA_SIZE); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_buffer_validate_data_ptr_but_no_size) { + setup(&(sg_desc){0}); + const uint32_t data[16] = {0}; + sg_buffer buf = sg_make_buffer(&(sg_buffer_desc){ + .data.ptr = data, + }); + T(sg_query_buffer_state(buf) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_BUFFERDESC_SIZE); + T(log_items[1] == SG_LOGITEM_VALIDATE_BUFFERDESC_DATA); + T(log_items[2] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_image_validate_start_canary) { + setup(&(sg_desc){0}); + const uint32_t pixels[8][8] = {0}; + sg_image img = sg_make_image(&(sg_image_desc){ + ._start_canary = 1234, + .width = 8, + .height = 8, + .data.subimage[0][0] = SG_RANGE(pixels), + }); + T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_CANARY); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_image_validate_end_canary) { + setup(&(sg_desc){0}); + const uint32_t pixels[8][8] = {0}; + sg_image img = sg_make_image(&(sg_image_desc){ + .width = 8, + .height = 8, + .data.subimage[0][0] = SG_RANGE(pixels), + ._end_canary = 1234, + }); + T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_CANARY); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_image_zero_width_height) { + setup(&(sg_desc){0}); + const uint32_t pixels[8][8] = {0}; + sg_image img = sg_make_image(&(sg_image_desc){ + .width = 0, + .height = 0, + .data.subimage[0][0] = SG_RANGE(pixels), + }); + T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_WIDTH); + T(log_items[1] == SG_LOGITEM_VALIDATE_IMAGEDESC_HEIGHT); + T(log_items[2] == SG_LOGITEM_VALIDATE_IMAGEDATA_DATA_SIZE); + T(log_items[3] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_image_validate_msaa_no_rt) { + setup(&(sg_desc){0}); + const uint32_t pixels[8][8] = {0}; + sg_image img = sg_make_image(&(sg_image_desc){ + .width = 8, + .height = 8, + .sample_count = 4, + .data.subimage[0][0] = SG_RANGE(pixels), + }); + T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_image_validate_msaa_num_mipmaps) { + setup(&(sg_desc){0}); + sg_image img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .sample_count = 4, + .num_mipmaps = 2, + }); + T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_MSAA_NUM_MIPMAPS); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_image_validate_msaa_3d_image) { + setup(&(sg_desc){0}); + sg_image img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .type = SG_IMAGETYPE_3D, + .width = 32, + .height = 32, + .num_slices = 32, + .sample_count = 4, + }); + T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_MSAA_3D_IMAGE); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_image_validate_depth_3d_image_with_depth_format) { + setup(&(sg_desc){0}); + sg_image img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .type = SG_IMAGETYPE_3D, + .width = 8, + .height = 8, + .num_slices = 8, + .pixel_format = SG_PIXELFORMAT_DEPTH, + }); + T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_image_validate_rt_immutable) { + setup(&(sg_desc){0}); + sg_image img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .usage = SG_USAGE_DYNAMIC, + .width = 8, + .height = 8, + }); + T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_RT_IMMUTABLE); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_image_validate_dynamic_no_data) { + setup(&(sg_desc){0}); + uint32_t pixels[8][8] = {0}; + sg_image img = sg_make_image(&(sg_image_desc){ + .width = 8, + .height = 8, + .usage = SG_USAGE_DYNAMIC, + .data.subimage[0][0] = SG_RANGE(pixels), + }); + T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_image_valiate_compressed_immutable) { + setup(&(sg_desc){0}); + sg_image img = sg_make_image(&(sg_image_desc){ + .width = 8, + .height = 8, + .pixel_format = SG_PIXELFORMAT_BC1_RGBA, + .usage = SG_USAGE_DYNAMIC, + }); + T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_image_validate_nodata) { + setup(&(sg_desc){0}); + sg_image img = sg_make_image(&(sg_image_desc){ + .width = 8, + .height = 8, + }); + T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDATA_NODATA); + T(log_items[1] == SG_LOGITEM_VALIDATE_IMAGEDATA_DATA_SIZE); + T(log_items[2] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_image_validate_data_size) { + setup(&(sg_desc){0}); + uint32_t pixels[4][4] = {0}; + sg_image img = sg_make_image(&(sg_image_desc){ + .width = 8, + .height = 8, + .data.subimage[0][0] = SG_RANGE(pixels), + }); + T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDATA_DATA_SIZE); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_image_validate_missing_mipdata) { + setup(&(sg_desc){0}); + uint32_t mip0[8][8] = {0}; + uint32_t mip1[4][4] = {0}; + uint32_t mip2[2][2] = {0}; + sg_image img = sg_make_image(&(sg_image_desc){ + .width = 8, + .height = 8, + .num_mipmaps = 4, + .data.subimage[0][0] = SG_RANGE(mip0), + .data.subimage[0][1] = SG_RANGE(mip1), + .data.subimage[0][2] = SG_RANGE(mip2), + }); + T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDATA_NODATA); + T(log_items[1] == SG_LOGITEM_VALIDATE_IMAGEDATA_DATA_SIZE); + T(log_items[2] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_image_validate_wrong_mipsize) { + setup(&(sg_desc){0}); + uint32_t mip0[8][8] = {0}; + uint32_t mip1[4][4] = {0}; + uint32_t mip2[2][2] = {0}; + uint32_t mip3[1][1] = {0}; + sg_image img = sg_make_image(&(sg_image_desc){ + .width = 8, + .height = 8, + .num_mipmaps = 4, + .data.subimage[0][0] = SG_RANGE(mip0), + .data.subimage[0][1] = SG_RANGE(mip2), + .data.subimage[0][2] = SG_RANGE(mip1), + .data.subimage[0][3] = SG_RANGE(mip3) + }); + T(sg_query_image_state(img) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_IMAGEDATA_DATA_SIZE); + T(log_items[1] == SG_LOGITEM_VALIDATE_IMAGEDATA_DATA_SIZE); + T(log_items[2] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_sampler_validate_start_canary) { + setup(&(sg_desc){0}); + sg_sampler smp = sg_make_sampler(&(sg_sampler_desc){ + ._start_canary = 1234, + }); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_SAMPLERDESC_CANARY); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_sampler_validate_minfilter_none) { + setup(&(sg_desc){0}); + sg_sampler smp = sg_make_sampler(&(sg_sampler_desc){ + .min_filter = SG_FILTER_NONE, + }); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_SAMPLERDESC_MINFILTER_NONE); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_sampler_validate_magfilter_none) { + setup(&(sg_desc){0}); + sg_sampler smp = sg_make_sampler(&(sg_sampler_desc){ + .mag_filter = SG_FILTER_NONE, + }); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_SAMPLERDESC_MAGFILTER_NONE); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_sampler_validate_anistropic_requires_linear_filtering) { + setup(&(sg_desc){0}); + sg_sampler smp; + + smp = sg_make_sampler(&(sg_sampler_desc){ + .max_anisotropy = 2, + .min_filter = SG_FILTER_LINEAR, + .mag_filter = SG_FILTER_LINEAR, + .mipmap_filter = SG_FILTER_NONE, + }); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING); + + reset_log_items(); + smp = sg_make_sampler(&(sg_sampler_desc){ + .max_anisotropy = 2, + .min_filter = SG_FILTER_LINEAR, + .mag_filter = SG_FILTER_LINEAR, + .mipmap_filter = SG_FILTER_NEAREST, + }); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING); + + reset_log_items(); + smp = sg_make_sampler(&(sg_sampler_desc){ + .max_anisotropy = 2, + .min_filter = SG_FILTER_NEAREST, + .mag_filter = SG_FILTER_LINEAR, + .mipmap_filter = SG_FILTER_LINEAR, + }); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING); + + reset_log_items(); + smp = sg_make_sampler(&(sg_sampler_desc){ + .max_anisotropy = 2, + .min_filter = SG_FILTER_LINEAR, + .mag_filter = SG_FILTER_NEAREST, + .mipmap_filter = SG_FILTER_LINEAR, + }); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING); + + reset_log_items(); + smp = sg_make_sampler(&(sg_sampler_desc){ + .max_anisotropy = 2, + .min_filter = SG_FILTER_LINEAR, + .mag_filter = SG_FILTER_LINEAR, + .mipmap_filter = SG_FILTER_LINEAR, + }); + T(sg_query_sampler_state(smp) == SG_RESOURCESTATE_VALID); + + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_start_canary) { + setup(&(sg_desc){0}); + sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + ._start_canary = 1234, + .colors[0].image = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + }), + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_CANARY); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_end_canary) { + setup(&(sg_desc){0}); + sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0].image = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + }), + ._end_canary = 1234, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_CANARY); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_no_cont_color_atts1) { + setup(&(sg_desc){0}); + const sg_image_desc img_desc = { .render_target = true, .width = 64, .height = 64 }; + sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors = { + [0].image = sg_make_image(&img_desc), + [2].image = sg_make_image(&img_desc), + } + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_NO_CONT_COLOR_ATTS); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_image) { + setup(&(sg_desc){0}); + const sg_image_desc img_desc = { .render_target = true, .width = 64, .height = 64 }; + const sg_image img0 = sg_make_image(&img_desc); + const sg_image img1 = sg_make_image(&img_desc); + sg_destroy_image(img1); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors = { + [0].image = img0, + [1].image = img1, + } + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_IMAGE); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_miplevel) { + setup(&(sg_desc){0}); + const sg_image img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 16, + .height = 16, + .num_mipmaps = 4, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { .image = img, .mip_level = 4 } + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_MIPLEVEL); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_face) { + setup(&(sg_desc){0}); + const sg_image img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .type = SG_IMAGETYPE_CUBE, + .width = 64, + .height = 64, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { .image = img, .slice = 6 } + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_FACE); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_layer) { + setup(&(sg_desc){0}); + const sg_image img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .type = SG_IMAGETYPE_ARRAY, + .width = 64, + .height = 64, + .num_slices = 4, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { .image = img, .slice = 5 }, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_LAYER); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_slice) { + setup(&(sg_desc){0}); + const sg_image img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .type = SG_IMAGETYPE_3D, + .width = 64, + .height = 64, + .num_slices = 4, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { .image = img, .slice = 5 }, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_SLICE); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_image_no_rt) { + setup(&(sg_desc){0}); + const sg_image img = sg_make_image(&(sg_image_desc){ + .width = 8, + .height = 8, + .usage = SG_USAGE_DYNAMIC, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0].image = img, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_IMAGE_NO_RT); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_color_inv_pixelformat) { + setup(&(sg_desc){0}); + const sg_image_desc img_desc = { + .render_target = true, + .width = 8, + .height = 8, + .pixel_format = SG_PIXELFORMAT_DEPTH, + }; + reset_log_items(); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0].image = sg_make_image(&img_desc), + .depth_stencil.image = sg_make_image(&img_desc), + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_COLOR_INV_PIXELFORMAT); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_depth_inv_pixelformat) { + setup(&(sg_desc){0}); + const sg_image_desc img_desc = { + .render_target = true, + .width = 8, + .height = 8, + }; + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0].image = sg_make_image(&img_desc), + .depth_stencil.image = sg_make_image(&img_desc), + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_DEPTH_INV_PIXELFORMAT); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_image_sizes) { + setup(&(sg_desc){0}); + const sg_image img0 = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + }); + const sg_image img1 = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 32, + .height = 32, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors = { + [0].image = img0, + [1].image = img1, + } + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_IMAGE_SIZES); + T(log_items[1] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_IMAGE_SIZES); + T(log_items[2] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_image_sample_counts) { + setup(&(sg_desc){0}); + const sg_image img0 = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .sample_count = 4, + }); + const sg_image img1 = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .sample_count = 2, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors = { + [0].image = img0, + [1].image = img1, + } + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_IMAGE_SAMPLE_COUNTS); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_resolve_color_image_msaa) { + setup(&(sg_desc){0}); + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .sample_count = 1, + }); + const sg_image resolve_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .sample_count = 1, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0].image = color_img, + .resolves[0].image = resolve_img, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_RESOLVE_COLOR_IMAGE_MSAA); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_resolve_image) { + setup(&(sg_desc){0}); + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .sample_count = 4, + }); + const sg_image resolve_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .sample_count = 1, + }); + sg_destroy_image(resolve_img); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0].image = color_img, + .resolves[0].image = resolve_img, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_resolve_sample_count) { + setup(&(sg_desc){0}); + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .sample_count = 4, + }); + const sg_image resolve_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .sample_count = 4, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0].image = color_img, + .resolves[0].image = resolve_img, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_RESOLVE_SAMPLE_COUNT); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_resolve_miplevel) { + setup(&(sg_desc){0}); + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .sample_count = 4, + }); + const sg_image resolve_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .sample_count = 1, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { .image = color_img }, + .resolves[0] = { .image = resolve_img, .mip_level = 1 }, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_RESOLVE_MIPLEVEL); + // FIXME: these are confusing + T(log_items[1] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_SIZES); + T(log_items[2] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_SIZES); + T(log_items[3] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_resolve_face) { + setup(&(sg_desc){0}); + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .sample_count = 4, + }); + const sg_image resolve_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .type = SG_IMAGETYPE_CUBE, + .width = 64, + .height = 64, + .sample_count = 1, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { .image = color_img }, + .resolves[0] = { .image = resolve_img, .slice = 6 }, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_RESOLVE_FACE); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_resolve_layer) { + setup(&(sg_desc){0}); + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .sample_count = 4, + }); + const sg_image resolve_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .type = SG_IMAGETYPE_ARRAY, + .width = 64, + .height = 64, + .num_slices = 4, + .sample_count = 1, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { .image = color_img }, + .resolves[0] = { .image = resolve_img, .slice = 4 }, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_RESOLVE_LAYER); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_resolve_slice) { + setup(&(sg_desc){0}); + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .sample_count = 4, + }); + const sg_image resolve_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .type = SG_IMAGETYPE_3D, + .width = 64, + .height = 64, + .num_slices = 4, + .sample_count = 1, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { .image = color_img }, + .resolves[0] = { .image = resolve_img, .slice = 4 }, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_RESOLVE_SLICE); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_resolve_image_no_rt) { + setup(&(sg_desc){0}); + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .sample_count = 4, + }); + const sg_image resolve_img = sg_make_image(&(sg_image_desc){ + .width = 64, + .height = 64, + .usage = SG_USAGE_DYNAMIC, + .sample_count = 1, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { .image = color_img }, + .resolves[0] = { .image = resolve_img }, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_NO_RT); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_resolve_image_sizes) { + setup(&(sg_desc){0}); + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .sample_count = 4, + }); + const sg_image resolve_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 32, + .height = 32, + .sample_count = 1, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { .image = color_img }, + .resolves[0] = { .image = resolve_img }, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_SIZES); + T(log_items[1] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_SIZES); + T(log_items[2] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_resolve_image_format) { + setup(&(sg_desc){0}); + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .sample_count = 4, + }); + const sg_image resolve_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .pixel_format = SG_PIXELFORMAT_R8, + .sample_count = 1, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { .image = color_img }, + .resolves[0] = { .image = resolve_img }, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_FORMAT); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_depth_image) { + setup(&(sg_desc){0}); + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + }); + const sg_image depth_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .pixel_format = SG_PIXELFORMAT_DEPTH, + }); + sg_destroy_image(depth_img); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { .image = color_img }, + .depth_stencil.image = depth_img, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_depth_miplevel) { + setup(&(sg_desc){0}); + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + }); + const sg_image depth_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .pixel_format = SG_PIXELFORMAT_DEPTH, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { .image = color_img }, + .depth_stencil = { .image = depth_img, .mip_level = 1 }, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_DEPTH_MIPLEVEL); + // FIXME: these additional validation errors are confusing + T(log_items[1] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SIZES); + T(log_items[2] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SIZES); + T(log_items[3] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_depth_face) { + setup(&(sg_desc){0}); + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + }); + const sg_image depth_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .type = SG_IMAGETYPE_CUBE, + .width = 64, + .height = 64, + .pixel_format = SG_PIXELFORMAT_DEPTH, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { .image = color_img }, + .depth_stencil = { .image = depth_img, .slice = 6 }, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_DEPTH_FACE); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_depth_layer) { + setup(&(sg_desc){0}); + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + }); + const sg_image depth_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .type = SG_IMAGETYPE_ARRAY, + .width = 64, + .height = 64, + .num_slices = 4, + .pixel_format = SG_PIXELFORMAT_DEPTH, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { .image = color_img }, + .depth_stencil = { .image = depth_img, .slice = 4 }, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_DEPTH_LAYER); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +// NOTE: VALIDATE_PASSDESC_DEPTH_SLICE can't actually happen because VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE + +// NOTE: VALIDATE_DEPTH_IMAGE_NO_RT can't actually happen because VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT + +UTEST(sokol_gfx, make_attachments_validate_depth_image_sizes) { + setup(&(sg_desc){0}); + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + }); + const sg_image depth_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 32, + .height = 32, + .pixel_format = SG_PIXELFORMAT_DEPTH, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { .image = color_img }, + .depth_stencil = { .image = depth_img }, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SIZES); + T(log_items[1] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SIZES); + T(log_items[2] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, make_attachments_validate_depth_image_sample_count) { + setup(&(sg_desc){0}); + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .sample_count = 4, + }); + const sg_image depth_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 64, + .height = 64, + .pixel_format = SG_PIXELFORMAT_DEPTH, + .sample_count = 2, + }); + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { .image = color_img }, + .depth_stencil = { .image = depth_img }, + }); + T(sg_query_attachments_state(atts) == SG_RESOURCESTATE_FAILED); + T(log_items[0] == SG_LOGITEM_VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SAMPLE_COUNT); + T(log_items[1] == SG_LOGITEM_VALIDATION_FAILED); + sg_shutdown(); +} + +UTEST(sokol_gfx, query_pixelformat_bytesperpixel) { + setup(&(sg_desc){0}); + T(sg_query_pixelformat(SG_PIXELFORMAT_R8).bytes_per_pixel == 1); + T(sg_query_pixelformat(SG_PIXELFORMAT_R8SN).bytes_per_pixel == 1); + T(sg_query_pixelformat(SG_PIXELFORMAT_R8UI).bytes_per_pixel == 1); + T(sg_query_pixelformat(SG_PIXELFORMAT_R8SI).bytes_per_pixel == 1); + T(sg_query_pixelformat(SG_PIXELFORMAT_R16).bytes_per_pixel == 2); + T(sg_query_pixelformat(SG_PIXELFORMAT_R16SN).bytes_per_pixel == 2); + T(sg_query_pixelformat(SG_PIXELFORMAT_R16UI).bytes_per_pixel == 2); + T(sg_query_pixelformat(SG_PIXELFORMAT_R16SI).bytes_per_pixel == 2); + T(sg_query_pixelformat(SG_PIXELFORMAT_R16F).bytes_per_pixel == 2); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG8).bytes_per_pixel == 2); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG8SN).bytes_per_pixel == 2); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG8UI).bytes_per_pixel == 2); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG8SI).bytes_per_pixel == 2); + T(sg_query_pixelformat(SG_PIXELFORMAT_R32UI).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_R32SI).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_R32F).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG16).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG16SN).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG16UI).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG16SI).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG16F).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA8).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_SRGB8A8).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA8SN).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA8UI).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA8SI).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_BGRA8).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGB10A2).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG11B10F).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGB9E5).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG32UI).bytes_per_pixel == 8); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG32SI).bytes_per_pixel == 8); + T(sg_query_pixelformat(SG_PIXELFORMAT_RG32F).bytes_per_pixel == 8); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA16).bytes_per_pixel == 8); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA16SN).bytes_per_pixel == 8); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA16UI).bytes_per_pixel == 8); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA16SI).bytes_per_pixel == 8); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA16F).bytes_per_pixel == 8); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA32UI).bytes_per_pixel == 16); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA32SI).bytes_per_pixel == 16); + T(sg_query_pixelformat(SG_PIXELFORMAT_RGBA32F).bytes_per_pixel == 16); + T(sg_query_pixelformat(SG_PIXELFORMAT_DEPTH).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_DEPTH_STENCIL).bytes_per_pixel == 4); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC1_RGBA).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC2_RGBA).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC3_RGBA).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC4_R).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC4_RSN).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC5_RG).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC5_RGSN).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC6H_RGBF).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC6H_RGBUF).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_BC7_RGBA).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_PVRTC_RGB_2BPP).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_PVRTC_RGB_4BPP).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_PVRTC_RGBA_2BPP).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_PVRTC_RGBA_4BPP).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_ETC2_RGB8).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_ETC2_RGB8A1).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_ETC2_RGBA8).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_EAC_R11).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_EAC_R11SN).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_EAC_R11).bytes_per_pixel == 0); + T(sg_query_pixelformat(SG_PIXELFORMAT_EAC_R11SN).bytes_per_pixel == 0); + sg_shutdown(); +} + +UTEST(sokol_gfx, query_pixelformat_compressed) { + setup(&(sg_desc){0}); + int i = SG_PIXELFORMAT_NONE + 1; + for (; i < SG_PIXELFORMAT_BC1_RGBA; i++) { + T(sg_query_pixelformat((sg_pixel_format)i).compressed == false); + } + for (; i < _SG_PIXELFORMAT_NUM; i++) { + T(sg_query_pixelformat((sg_pixel_format)i).compressed == true); + } + sg_shutdown(); +} + +UTEST(sokol_gfx, query_row_pitch) { + setup(&(sg_desc){0}); + T(sg_query_row_pitch(SG_PIXELFORMAT_R8, 13, 1) == 13); + T(sg_query_row_pitch(SG_PIXELFORMAT_R8, 13, 32) == 32); + T(sg_query_row_pitch(SG_PIXELFORMAT_RG8SN, 256, 16) == 512); + T(sg_query_row_pitch(SG_PIXELFORMAT_RGBA8, 256, 16) == 1024); + T(sg_query_row_pitch(SG_PIXELFORMAT_BC1_RGBA, 1024, 1) == 2048); + T(sg_query_row_pitch(SG_PIXELFORMAT_BC1_RGBA, 1, 1) == 8); + T(sg_query_row_pitch(SG_PIXELFORMAT_DEPTH, 256, 4) == 1024); + T(sg_query_row_pitch(SG_PIXELFORMAT_DEPTH_STENCIL, 256, 4) == 1024); + sg_shutdown(); +} + +UTEST(sokol_gfx, sg_query_surface_pitch) { + setup(&(sg_desc){0}); + T(sg_query_surface_pitch(SG_PIXELFORMAT_R8, 256, 256, 1) == (256 * 256)); + T(sg_query_surface_pitch(SG_PIXELFORMAT_R8, 256, 256, 1024) == (256 * 1024)); + T(sg_query_surface_pitch(SG_PIXELFORMAT_RG8, 1, 1, 1) == 2); + T(sg_query_surface_pitch(SG_PIXELFORMAT_RG8, 256, 256, 4) == (256 * 256 * 2)); + T(sg_query_surface_pitch(SG_PIXELFORMAT_RGBA32F, 256, 256, 1) == (256 * 256 * 16)); + T(sg_query_surface_pitch(SG_PIXELFORMAT_BC1_RGBA, 256, 256, 1) == (256 * 2 * 64)); + T(sg_query_surface_pitch(SG_PIXELFORMAT_BC1_RGBA, 256, 1, 1) == (256 * 2)); + T(sg_query_surface_pitch(SG_PIXELFORMAT_BC1_RGBA, 256, 2, 1) == (256 * 2)); + T(sg_query_surface_pitch(SG_PIXELFORMAT_BC1_RGBA, 256, 3, 1) == (256 * 2)); + T(sg_query_surface_pitch(SG_PIXELFORMAT_BC1_RGBA, 256, 4, 1) == (256 * 2)); + T(sg_query_surface_pitch(SG_PIXELFORMAT_BC1_RGBA, 256, 5, 1) == (256 * 2 * 2)); + sg_shutdown(); +} diff --git a/thirdparty/sokol/tests/functional/sokol_gl_test.c b/thirdparty/sokol/tests/functional/sokol_gl_test.c new file mode 100644 index 0000000..4fe4f45 --- /dev/null +++ b/thirdparty/sokol/tests/functional/sokol_gl_test.c @@ -0,0 +1,323 @@ +//------------------------------------------------------------------------------ +// sokol-gl-test.c +//------------------------------------------------------------------------------ +#include "sokol_gfx.h" +#define SOKOL_GL_IMPL +#include "sokol_gl.h" +#include "utest.h" +#include + +#define T(b) EXPECT_TRUE(b) +#define TFLT(f0,f1,epsilon) {T(fabs((f0)-(f1))<=(epsilon));} + +static void init(void) { + sg_setup(&(sg_desc){0}); + sgl_setup(&(sgl_desc_t){0}); +} + +static void shutdown(void) { + sgl_shutdown(); + sg_shutdown(); +} + +UTEST(sokol_gl, default_init_shutdown) { + init(); + T(_sgl.init_cookie == _SGL_INIT_COOKIE); + T(_sgl.def_ctx_id.id == SGL_DEFAULT_CONTEXT.id); + T(_sgl.cur_ctx_id.id == _sgl.def_ctx_id.id); + T(_sgl.cur_ctx); + T(_sgl.cur_ctx->vertices.cap == 65536); + T(_sgl.cur_ctx->commands.cap == 16384); + T(_sgl.cur_ctx->uniforms.cap == 16384); + T(_sgl.cur_ctx->vertices.next == 0); + T(_sgl.cur_ctx->commands.next == 0); + T(_sgl.cur_ctx->uniforms.next == 0); + T(_sgl.cur_ctx->vertices.ptr != 0); + T(_sgl.cur_ctx->uniforms.ptr != 0); + T(_sgl.cur_ctx->commands.ptr != 0); + T(_sgl.cur_ctx->error == SGL_NO_ERROR); + T(!_sgl.cur_ctx->in_begin); + T(_sgl.cur_ctx->def_pip.id != SG_INVALID_ID); + T(_sgl.pip_pool.pool.size == (_SGL_DEFAULT_PIPELINE_POOL_SIZE + 1)); + TFLT(_sgl.cur_ctx->u, 0.0f, FLT_MIN); + TFLT(_sgl.cur_ctx->v, 0.0f, FLT_MIN); + T(_sgl.cur_ctx->rgba == 0xFFFFFFFF); + T(_sgl.cur_ctx->cur_img.id == _sgl.def_img.id); + shutdown(); +} + +UTEST(sokol_gl, viewport) { + init(); + sgl_viewport(1, 2, 3, 4, true); + T(_sgl.cur_ctx->commands.next == 1); + T(_sgl.cur_ctx->commands.ptr[0].cmd == SGL_COMMAND_VIEWPORT); + T(_sgl.cur_ctx->commands.ptr[0].args.viewport.x == 1); + T(_sgl.cur_ctx->commands.ptr[0].args.viewport.y == 2); + T(_sgl.cur_ctx->commands.ptr[0].args.viewport.w == 3); + T(_sgl.cur_ctx->commands.ptr[0].args.viewport.h == 4); + T(_sgl.cur_ctx->commands.ptr[0].args.viewport.origin_top_left); + sgl_viewport(5, 6, 7, 8, false); + T(_sgl.cur_ctx->commands.next == 2); + T(_sgl.cur_ctx->commands.ptr[1].cmd == SGL_COMMAND_VIEWPORT); + T(_sgl.cur_ctx->commands.ptr[1].args.viewport.x == 5); + T(_sgl.cur_ctx->commands.ptr[1].args.viewport.y == 6); + T(_sgl.cur_ctx->commands.ptr[1].args.viewport.w == 7); + T(_sgl.cur_ctx->commands.ptr[1].args.viewport.h == 8); + T(!_sgl.cur_ctx->commands.ptr[1].args.viewport.origin_top_left); + shutdown(); +} + +UTEST(sokol_gl, scissor_rect) { + init(); + sgl_scissor_rect(10, 20, 30, 40, true); + T(_sgl.cur_ctx->commands.next == 1); + T(_sgl.cur_ctx->commands.ptr[0].cmd == SGL_COMMAND_SCISSOR_RECT); + T(_sgl.cur_ctx->commands.ptr[0].args.scissor_rect.x == 10); + T(_sgl.cur_ctx->commands.ptr[0].args.scissor_rect.y == 20); + T(_sgl.cur_ctx->commands.ptr[0].args.scissor_rect.w == 30); + T(_sgl.cur_ctx->commands.ptr[0].args.scissor_rect.h == 40); + T(_sgl.cur_ctx->commands.ptr[0].args.scissor_rect.origin_top_left); + sgl_scissor_rect(50, 60, 70, 80, false); + T(_sgl.cur_ctx->commands.next == 2); + T(_sgl.cur_ctx->commands.ptr[1].cmd == SGL_COMMAND_SCISSOR_RECT); + T(_sgl.cur_ctx->commands.ptr[1].args.scissor_rect.x == 50); + T(_sgl.cur_ctx->commands.ptr[1].args.scissor_rect.y == 60); + T(_sgl.cur_ctx->commands.ptr[1].args.scissor_rect.w == 70); + T(_sgl.cur_ctx->commands.ptr[1].args.scissor_rect.h == 80); + T(!_sgl.cur_ctx->commands.ptr[1].args.scissor_rect.origin_top_left); + shutdown(); +} + +UTEST(sokol_gl, texture) { + init(); + T(_sgl.cur_ctx->cur_img.id == _sgl.def_img.id); + uint32_t pixels[64] = { 0 }; + sg_image img = sg_make_image(&(sg_image_desc){ + .type = SG_IMAGETYPE_2D, + .width = 8, + .height = 8, + .data.subimage[0][0] = SG_RANGE(pixels), + }); + sg_sampler smp = sg_make_sampler(&(sg_sampler_desc){0}); + sgl_texture(img, smp); + T(_sgl.cur_ctx->cur_img.id == img.id); + T(_sgl.cur_ctx->cur_smp.id == smp.id); + shutdown(); +} + +UTEST(sokol_gl, texture_image_nosampler) { + init(); + T(_sgl.cur_ctx->cur_img.id == _sgl.def_img.id); + uint32_t pixels[64] = { 0 }; + sg_image img = sg_make_image(&(sg_image_desc){ + .type = SG_IMAGETYPE_2D, + .width = 8, + .height = 8, + .data.subimage[0][0] = SG_RANGE(pixels), + }); + sgl_texture(img, (sg_sampler){0}); + T(_sgl.cur_ctx->cur_img.id == img.id); + T(_sgl.cur_ctx->cur_smp.id == _sgl.def_smp.id); + shutdown(); +} + +UTEST(sokol_gl, texture_noimage_sampler) { + init(); + T(_sgl.cur_ctx->cur_img.id == _sgl.def_img.id); + sg_sampler smp = sg_make_sampler(&(sg_sampler_desc){0}); + sgl_texture((sg_image){0}, smp); + T(_sgl.cur_ctx->cur_img.id == _sgl.def_img.id); + T(_sgl.cur_ctx->cur_smp.id == smp.id); + shutdown(); +} + +UTEST(sokol_gl, texture_noimage_nosampler) { + init(); + T(_sgl.cur_ctx->cur_img.id == _sgl.def_img.id); + sgl_texture((sg_image){0}, (sg_sampler){0}); + T(_sgl.cur_ctx->cur_img.id == _sgl.def_img.id); + T(_sgl.cur_ctx->cur_smp.id == _sgl.def_smp.id); + shutdown(); +} +UTEST(sokol_gl, begin_end) { + init(); + sgl_begin_triangles(); + sgl_v3f(1.0f, 2.0f, 3.0f); + sgl_v3f(4.0f, 5.0f, 6.0f); + sgl_v3f(7.0f, 8.0f, 9.0f); + sgl_end(); + T(_sgl.cur_ctx->base_vertex == 0); + T(_sgl.cur_ctx->vertices.next == 3); + T(_sgl.cur_ctx->commands.next == 1); + T(_sgl.cur_ctx->uniforms.next == 1); + T(_sgl.cur_ctx->commands.ptr[0].cmd == SGL_COMMAND_DRAW); + T(_sgl.cur_ctx->commands.ptr[0].args.draw.pip.id == _sgl_pipeline_at(_sgl.cur_ctx->def_pip.id)->pip[SGL_PRIMITIVETYPE_TRIANGLES].id); + T(_sgl.cur_ctx->commands.ptr[0].args.draw.base_vertex == 0); + T(_sgl.cur_ctx->commands.ptr[0].args.draw.num_vertices == 3); + T(_sgl.cur_ctx->commands.ptr[0].args.draw.uniform_index == 0); + shutdown(); +} + +UTEST(sokol_gl, matrix_mode) { + init(); + sgl_matrix_mode_modelview(); T(_sgl.cur_ctx->cur_matrix_mode == SGL_MATRIXMODE_MODELVIEW); + sgl_matrix_mode_projection(); T(_sgl.cur_ctx->cur_matrix_mode == SGL_MATRIXMODE_PROJECTION); + sgl_matrix_mode_texture(); T(_sgl.cur_ctx->cur_matrix_mode == SGL_MATRIXMODE_TEXTURE); + shutdown(); +} + +UTEST(sokol_gl, load_identity) { + init(); + sgl_load_identity(); + const _sgl_matrix_t* m = _sgl_matrix_modelview(_sgl.cur_ctx); + TFLT(m->v[0][0], 1.0f, FLT_MIN); TFLT(m->v[0][1], 0.0f, FLT_MIN); TFLT(m->v[0][2], 0.0f, FLT_MIN); TFLT(m->v[0][3], 0.0f, FLT_MIN); + TFLT(m->v[1][0], 0.0f, FLT_MIN); TFLT(m->v[1][1], 1.0f, FLT_MIN); TFLT(m->v[1][2], 0.0f, FLT_MIN); TFLT(m->v[1][3], 0.0f, FLT_MIN); + TFLT(m->v[2][0], 0.0f, FLT_MIN); TFLT(m->v[2][1], 0.0f, FLT_MIN); TFLT(m->v[2][2], 1.0f, FLT_MIN); TFLT(m->v[2][3], 0.0f, FLT_MIN); + TFLT(m->v[3][0], 0.0f, FLT_MIN); TFLT(m->v[3][1], 0.0f, FLT_MIN); TFLT(m->v[3][2], 0.0f, FLT_MIN); TFLT(m->v[3][3], 1.0f, FLT_MIN); + shutdown(); +} + +UTEST(sokol_gl, load_matrix) { + init(); + const float m[16] = { + 0.5f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.5f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.5f, 0.0f, + 2.0f, 3.0f, 4.0f, 1.0f + }; + sgl_load_matrix(m); + const _sgl_matrix_t* m0 = _sgl_matrix_modelview(_sgl.cur_ctx); + TFLT(m0->v[0][0], 0.5f, FLT_MIN); + TFLT(m0->v[1][1], 0.5f, FLT_MIN); + TFLT(m0->v[2][2], 0.5f, FLT_MIN); + TFLT(m0->v[3][0], 2.0f, FLT_MIN); + TFLT(m0->v[3][1], 3.0f, FLT_MIN); + TFLT(m0->v[3][2], 4.0f, FLT_MIN); + TFLT(m0->v[3][3], 1.0f, FLT_MIN); + sgl_load_transpose_matrix(m); + const _sgl_matrix_t* m1 = _sgl_matrix_modelview(_sgl.cur_ctx); + TFLT(m1->v[0][0], 0.5f, FLT_MIN); + TFLT(m1->v[1][1], 0.5f, FLT_MIN); + TFLT(m1->v[2][2], 0.5f, FLT_MIN); + TFLT(m1->v[0][3], 2.0f, FLT_MIN); + TFLT(m1->v[1][3], 3.0f, FLT_MIN); + TFLT(m1->v[2][3], 4.0f, FLT_MIN); + TFLT(m1->v[3][3], 1.0f, FLT_MIN); + shutdown(); +} + +UTEST(sokol_gl, make_destroy_pipelines) { + sg_setup(&(sg_desc){0}); + sgl_setup(&(sgl_desc_t){ + /* one pool slot is used by soko-gl itself */ + .pipeline_pool_size = 4 + }); + + sgl_pipeline pip[3] = { {0} }; + sg_pipeline_desc desc = { + .depth = { + .write_enabled = true, + .compare = SG_COMPAREFUNC_LESS_EQUAL + } + }; + for (int i = 0; i < 3; i++) { + pip[i] = sgl_make_pipeline(&desc); + T(pip[i].id != SG_INVALID_ID); + T((2-i) == _sgl.pip_pool.pool.queue_top); + const _sgl_pipeline_t* pip_ptr = _sgl_lookup_pipeline(pip[i].id); + T(pip_ptr); + T(pip_ptr->slot.id == pip[i].id); + T(pip_ptr->slot.state == SG_RESOURCESTATE_VALID); + } + /* trying to create another one fails because buffer is exhausted */ + T(sgl_make_pipeline(&desc).id == SG_INVALID_ID); + + for (int i = 0; i < 3; i++) { + sgl_destroy_pipeline(pip[i]); + T(0 == _sgl_lookup_pipeline(pip[i].id)); + const _sgl_pipeline_t* pip_ptr = _sgl_pipeline_at(pip[i].id); + T(pip_ptr); + T(pip_ptr->slot.id == SG_INVALID_ID); + T(pip_ptr->slot.state == SG_RESOURCESTATE_INITIAL); + T((i+1) == _sgl.pip_pool.pool.queue_top); + } + sgl_shutdown(); + sg_shutdown(); +} + +UTEST(sokol_gl, make_destroy_contexts) { + init(); + sgl_context ctx = sgl_make_context(&(sgl_context_desc_t){ + .max_vertices = 1024, + .max_commands = 256, + .color_format = SG_PIXELFORMAT_RG8, + .depth_format = SG_PIXELFORMAT_NONE, + .sample_count = 4, + }); + T(ctx.id != SG_INVALID_ID); + T(ctx.id != SGL_DEFAULT_CONTEXT.id); + // creating a context should not change the current context + T(ctx.id != _sgl.cur_ctx_id.id); + sgl_set_context(ctx); + T(_sgl.cur_ctx->vertices.cap == 1024); + T(_sgl.cur_ctx->commands.cap == 256); + T(_sgl.cur_ctx->uniforms.cap == 256); + T(ctx.id == _sgl.cur_ctx_id.id); + T(sgl_get_context().id == ctx.id); + sgl_set_context(SGL_DEFAULT_CONTEXT); + T(sgl_get_context().id == SGL_DEFAULT_CONTEXT.id); + sgl_destroy_context(ctx); + shutdown(); +} + +UTEST(sokol_gl, destroy_active_context) { + init(); + sgl_context ctx = sgl_make_context(&(sgl_context_desc_t){ + .max_vertices = 1024, + .max_commands = 256, + .color_format = SG_PIXELFORMAT_RG8, + .depth_format = SG_PIXELFORMAT_NONE, + .sample_count = 4, + }); + sgl_set_context(ctx); + sgl_destroy_context(ctx); + T(_sgl.cur_ctx == 0); + T(sgl_error() == SGL_ERROR_NO_CONTEXT); + shutdown(); +} + +UTEST(sokol_gl, context_pipeline) { + init(); + sgl_context ctx1 = sgl_make_context(&(sgl_context_desc_t){ + .max_vertices = 1024, + .max_commands = 256, + .color_format = SG_PIXELFORMAT_R8, + .depth_format = SG_PIXELFORMAT_NONE, + .sample_count = 4, + }); + sgl_context ctx2 = sgl_make_context(&(sgl_context_desc_t){ + .max_vertices = 1024, + .max_commands = 256, + .color_format = SG_PIXELFORMAT_RG8, + .depth_format = SG_PIXELFORMAT_NONE, + .sample_count = 2, + }); + sgl_set_context(ctx1); + sgl_pipeline pip1 = sgl_make_pipeline(&(sg_pipeline_desc){ + .colors[0].blend.enabled = true, + }); + T(pip1.id != SG_INVALID_ID); + // FIXME: currently sg_query_pipeline_info() doesn't provide enough information + + sgl_pipeline pip2 = sgl_context_make_pipeline(ctx2, &(sg_pipeline_desc){ + .alpha_to_coverage_enabled = true, + }); + T(pip2.id != SG_INVALID_ID); + shutdown(); +} + +UTEST(sokol_gl, default_context) { + init(); + T(sgl_default_context().id == SGL_DEFAULT_CONTEXT.id); + shutdown(); +} diff --git a/thirdparty/sokol/tests/functional/sokol_log_test.c b/thirdparty/sokol/tests/functional/sokol_log_test.c new file mode 100644 index 0000000..1ed21ef --- /dev/null +++ b/thirdparty/sokol/tests/functional/sokol_log_test.c @@ -0,0 +1,2 @@ +#define SOKOL_LOG_IMPL +#include "sokol_log.h" diff --git a/thirdparty/sokol/tests/functional/sokol_shape_test.c b/thirdparty/sokol/tests/functional/sokol_shape_test.c new file mode 100644 index 0000000..8fc84b1 --- /dev/null +++ b/thirdparty/sokol/tests/functional/sokol_shape_test.c @@ -0,0 +1,421 @@ +//------------------------------------------------------------------------------ +// sokol-shape-test.c +//------------------------------------------------------------------------------ +#include "sokol_gfx.h" +#define SOKOL_SHAPE_IMPL +#include "sokol_shape.h" +#include "utest.h" + +#define T(b) EXPECT_TRUE(b) +#define TFLT(f0,f1,epsilon) {T(fabs((f0)-(f1))<=(epsilon));} + +UTEST(sokol_shape, color4f) { + T(sshape_color_4f(1.0f, 0.0f, 0.0f, 0.0f) == 0x000000FF); + T(sshape_color_4f(0.0f, 1.0f, 0.0f, 0.0f) == 0x0000FF00); + T(sshape_color_4f(0.0f, 0.0f, 1.0f, 0.0f) == 0x00FF0000); + T(sshape_color_4f(0.0f, 0.0f, 0.0f, 1.0f) == 0xFF000000); +} + +UTEST(sokol_shape, color3f) { + T(sshape_color_3f(1.0f, 0.0f, 0.0f) == 0xFF0000FF); + T(sshape_color_3f(0.0f, 1.0f, 0.0f) == 0xFF00FF00); + T(sshape_color_3f(0.0f, 0.0f, 1.0f) == 0xFFFF0000); +} + +UTEST(sokol_shape, color4b) { + T(sshape_color_4b(255, 0, 0, 0) == 0x000000FF); + T(sshape_color_4b(0, 255, 0, 0) == 0x0000FF00); + T(sshape_color_4b(0, 0, 255, 0) == 0x00FF0000); + T(sshape_color_4b(0, 0, 0, 255) == 0xFF000000); +} + +UTEST(sokol_shape, color3b) { + T(sshape_color_3b(255, 0, 0) == 0xFF0000FF); + T(sshape_color_3b(0, 255, 0) == 0xFF00FF00); + T(sshape_color_3b(0, 0, 255) == 0xFFFF0000); +} + +UTEST(sokol_shape, mat4) { + float values[16] = { + 1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 10.0f, 11.0f, 12.0f, + 13.0f, 14.0f, 15.0f, 16.0f + }; + sshape_mat4_t m = sshape_mat4(values); + T(m.m[0][0] == 1.0f); + T(m.m[0][1] == 2.0f); + T(m.m[0][2] == 3.0f); + T(m.m[0][3] == 4.0f); + T(m.m[1][0] == 5.0f); + T(m.m[1][1] == 6.0f); + T(m.m[1][2] == 7.0f); + T(m.m[1][3] == 8.0f); + T(m.m[2][0] == 9.0f); + T(m.m[2][1] == 10.0f); + T(m.m[2][2] == 11.0f); + T(m.m[2][3] == 12.0f); + T(m.m[3][0] == 13.0f); + T(m.m[3][1] == 14.0f); + T(m.m[3][2] == 15.0f); + T(m.m[3][3] == 16.0f); +} + +UTEST(sokol_shape, mat4_transpose) { + float values[16] = { + 1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 10.0f, 11.0f, 12.0f, + 13.0f, 14.0f, 15.0f, 16.0f + }; + sshape_mat4_t m = sshape_mat4_transpose(values); + T(m.m[0][0] == 1.0f); + T(m.m[1][0] == 2.0f); + T(m.m[2][0] == 3.0f); + T(m.m[3][0] == 4.0f); + T(m.m[0][1] == 5.0f); + T(m.m[1][1] == 6.0f); + T(m.m[2][1] == 7.0f); + T(m.m[3][1] == 8.0f); + T(m.m[0][2] == 9.0f); + T(m.m[1][2] == 10.0f); + T(m.m[2][2] == 11.0f); + T(m.m[3][2] == 12.0f); + T(m.m[0][3] == 13.0f); + T(m.m[1][3] == 14.0f); + T(m.m[2][3] == 15.0f); + T(m.m[3][3] == 16.0f); +} + +UTEST(sokol_shape, plane_buffer_sizes) { + sshape_sizes_t res; + + res = sshape_plane_sizes(1); + T(4 == res.vertices.num); + T(6 == res.indices.num); + T(res.vertices.num * sizeof(sshape_vertex_t) == res.vertices.size); + T(res.indices.num * sizeof(uint16_t) == res.indices.size); + + res = sshape_plane_sizes(2); + T(9 == res.vertices.num); + T(24 == res.indices.num); + T(res.vertices.num * sizeof(sshape_vertex_t) == res.vertices.size); + T(res.indices.num * sizeof(uint16_t) == res.indices.size); +} + +UTEST(sokol_shape, box_buffer_sizes) { + sshape_sizes_t res; + + res = sshape_box_sizes(1); + T(24 == res.vertices.num); + T(36 == res.indices.num); + T(res.vertices.num * sizeof(sshape_vertex_t) == res.vertices.size); + T(res.indices.num * sizeof(uint16_t) == res.indices.size); + + res = sshape_box_sizes(2); + T(54 == res.vertices.num); + T(144 == res.indices.num); + T(res.vertices.num * sizeof(sshape_vertex_t) == res.vertices.size); + T(res.indices.num * sizeof(uint16_t) == res.indices.size); +} + +UTEST(sokol_shape, sphere_buffer_sizes) { + sshape_sizes_t res; + + res = sshape_sphere_sizes(3, 2); + T(12 == res.vertices.num); + T(18 == res.indices.num); + T(res.vertices.num * sizeof(sshape_vertex_t) == res.vertices.size); + T(res.indices.num * sizeof(uint16_t) == res.indices.size); + + res = sshape_sphere_sizes(36, 12); + T(481 == res.vertices.num); + T(2376 == res.indices.num); + T(res.vertices.num * sizeof(sshape_vertex_t) == res.vertices.size); + T(res.indices.num * sizeof(uint16_t) == res.indices.size); +} + +UTEST(sokol_shape, cylinder_buffer_sizes) { + sshape_sizes_t res; + + res = sshape_cylinder_sizes(3, 1); + T(24 == res.vertices.num); + T(36 == res.indices.num); + T(res.vertices.num * sizeof(sshape_vertex_t) == res.vertices.size); + T(res.indices.num * sizeof(uint16_t) == res.indices.size); + + res = sshape_cylinder_sizes(5, 2); + T(42 == res.vertices.num); + T(90 == res.indices.num); + T(res.vertices.num * sizeof(sshape_vertex_t) == res.vertices.size); + T(res.indices.num * sizeof(uint16_t) == res.indices.size); +} + +UTEST(sokol_shape, torus_buffer_sizes) { + sshape_sizes_t res; + + res = sshape_torus_sizes(3, 3); + T(16 == res.vertices.num); + T(54 == res.indices.num); + T(res.vertices.num * sizeof(sshape_vertex_t) == res.vertices.size); + T(res.indices.num * sizeof(uint16_t) == res.indices.size); + + res = sshape_torus_sizes(4, 5); + T(30 == res.vertices.num); + T(120 == res.indices.num); + T(res.vertices.num * sizeof(sshape_vertex_t) == res.vertices.size); + T(res.indices.num * sizeof(uint16_t) == res.indices.size); +} + +UTEST(sokol_shape, buffer_layout_desc) { + const sg_vertex_buffer_layout_state l_state = sshape_vertex_buffer_layout_state(); + T(sizeof(sshape_vertex_t) == l_state.stride); + T(0 == l_state.step_func); + T(0 == l_state.step_rate); +} + +UTEST(sokol_shape, attr_descs) { + { + const sg_vertex_attr_state a_state = sshape_position_vertex_attr_state(); + T(offsetof(sshape_vertex_t, x) == a_state.offset); + T(SG_VERTEXFORMAT_FLOAT3 == a_state.format); + T(0 == a_state.buffer_index); + } + { + const sg_vertex_attr_state a_state = sshape_normal_vertex_attr_state(); + T(offsetof(sshape_vertex_t, normal) == a_state.offset); + T(SG_VERTEXFORMAT_BYTE4N == a_state.format); + T(0 == a_state.buffer_index); + } + { + const sg_vertex_attr_state a_state = sshape_texcoord_vertex_attr_state(); + T(offsetof(sshape_vertex_t, u) == a_state.offset); + T(SG_VERTEXFORMAT_USHORT2N == a_state.format); + T(0 == a_state.buffer_index); + } + { + const sg_vertex_attr_state a_state = sshape_color_vertex_attr_state(); + T(offsetof(sshape_vertex_t, color) == a_state.offset); + T(SG_VERTEXFORMAT_UBYTE4N == a_state.format); + T(0 == a_state.buffer_index); + } +} + +UTEST(sokol_shape, buffer_descs_elm_range) { + sshape_vertex_t vx[128] = { 0 }; + uint16_t ix[128] = { 0 }; + sshape_buffer_t buf = { + .vertices.buffer = SSHAPE_RANGE(vx), + .indices.buffer = SSHAPE_RANGE(ix), + }; + + // build a box... + { + buf = sshape_build_box(&buf, &(sshape_box_t) {0}); + const sg_buffer_desc vbuf_desc = sshape_vertex_buffer_desc(&buf); + const sg_buffer_desc ibuf_desc = sshape_index_buffer_desc(&buf); + const sshape_element_range_t elm_range = sshape_element_range(&buf); + T(vbuf_desc.size == 0); + T(vbuf_desc.type == SG_BUFFERTYPE_VERTEXBUFFER); + T(vbuf_desc.usage == SG_USAGE_IMMUTABLE); + T(vbuf_desc.data.ptr == vx); + T(vbuf_desc.data.size == 24 * sizeof(sshape_vertex_t)); + T(ibuf_desc.size == 0); + T(ibuf_desc.type == SG_BUFFERTYPE_INDEXBUFFER); + T(ibuf_desc.usage == SG_USAGE_IMMUTABLE); + T(ibuf_desc.data.ptr == ix); + T(ibuf_desc.data.size == 36 * sizeof(uint16_t)); + T(elm_range.base_element == 0); + T(elm_range.num_elements == 36); + } + + // append a plane... + { + buf = sshape_build_plane(&buf, &(sshape_plane_t) {0}); + const sg_buffer_desc vbuf_desc = sshape_vertex_buffer_desc(&buf); + const sg_buffer_desc ibuf_desc = sshape_index_buffer_desc(&buf); + const sshape_element_range_t elm_range = sshape_element_range(&buf); + T(vbuf_desc.size == 0); + T(vbuf_desc.type == SG_BUFFERTYPE_VERTEXBUFFER); + T(vbuf_desc.usage == SG_USAGE_IMMUTABLE); + T(vbuf_desc.data.ptr == vx); + T(vbuf_desc.data.size == 28 * sizeof(sshape_vertex_t)); + T(ibuf_desc.size == 0); + T(ibuf_desc.type == SG_BUFFERTYPE_INDEXBUFFER); + T(ibuf_desc.usage == SG_USAGE_IMMUTABLE); + T(ibuf_desc.data.ptr == ix); + T(ibuf_desc.data.size == 42 * sizeof(uint16_t)); + T(elm_range.base_element == 36); + T(elm_range.num_elements == 6); + } +} + +UTEST(sokol_shape, build_plane_defaults) { + sshape_vertex_t vx[64] = { 0 }; + uint16_t ix[64] = { 0 }; + + sshape_buffer_t buf = { + .vertices.buffer = SSHAPE_RANGE(vx), + .indices.buffer = SSHAPE_RANGE(ix), + }; + buf = sshape_build_plane(&buf, &(sshape_plane_t) { 0 }); + + T(buf.valid); + T(0 == buf.vertices.shape_offset); + T(4 * sizeof(sshape_vertex_t) == buf.vertices.data_size); + T(0 == buf.indices.shape_offset); + T(6 * sizeof(uint16_t) == buf.indices.data_size); + for (int i = 0; i < 4; i++) { + T(vx[i].color == 0xFFFFFFFF); + } + T(ix[0] == 0); + T(ix[1] == 1); + T(ix[2] == 3); + T(ix[3] == 0); + T(ix[4] == 3); + T(ix[5] == 2); +} + +UTEST(sokol_shape, build_plane_validate) { + sshape_vertex_t vx[64] = { 0 }; + uint16_t ix[64] = { 0 }; + const sshape_plane_t params = { 0 }; + + // vertex buffer too small + { + sshape_buffer_t buf = { + .vertices.buffer = { .ptr = vx, .size = 3 * sizeof(sshape_vertex_t) }, + .indices.buffer = SSHAPE_RANGE(ix), + }; + T(!sshape_build_plane(&buf, ¶ms).valid); + } + + // index buffer too small + { + sshape_buffer_t buf = { + .vertices.buffer = SSHAPE_RANGE(vx), + .indices.buffer = { .ptr = ix, .size = 5 * sizeof(uint16_t) } + }; + T(!sshape_build_plane(&buf, ¶ms).valid); + } + // just the right size + { + sshape_buffer_t buf = { + .vertices.buffer = { .ptr = vx, .size = 4 * sizeof(sshape_vertex_t) }, + .indices.buffer = { .ptr = ix, .size = 6 * sizeof(uint16_t) } + }; + T(sshape_build_plane(&buf, ¶ms).valid); + } + + // too small for two planes + { + sshape_buffer_t buf = { + .vertices.buffer = { .ptr = vx, .size = 5 * sizeof(sshape_vertex_t) }, + .indices.buffer = { .ptr = ix, .size = 7 * sizeof(uint16_t) } + }; + buf = sshape_build_plane(&buf, ¶ms); + T(buf.valid); + buf = sshape_build_plane(&buf, ¶ms); + T(!buf.valid); + } + + // just the right size for two planes + { + sshape_buffer_t buf = { + .vertices.buffer = { .ptr = vx, .size = 8 * sizeof(sshape_vertex_t) }, + .indices.buffer = { .ptr = ix, .size = 12 * sizeof(uint16_t) } + }; + buf = sshape_build_plane(&buf, ¶ms); + T(buf.valid); + T(buf.vertices.shape_offset == 0); + T(buf.vertices.data_size == 4 * sizeof(sshape_vertex_t)); + T(buf.indices.shape_offset == 0); + T(buf.indices.data_size == 6 * sizeof(uint16_t)); + buf = sshape_build_plane(&buf, ¶ms); + T(buf.valid); + T(buf.vertices.shape_offset == 4 * sizeof(sshape_vertex_t)); + T(buf.vertices.data_size == 8 * sizeof(sshape_vertex_t)); + T(buf.indices.shape_offset == 6 * sizeof(uint16_t)); + T(buf.indices.data_size == 12 * sizeof(uint16_t)); + } +} + +UTEST(sokol_shape, build_box_defaults) { + sshape_vertex_t vx[128] = { 0 }; + uint16_t ix[128] = { 0 }; + + sshape_buffer_t buf = { + .vertices.buffer = SSHAPE_RANGE(vx), + .indices.buffer = SSHAPE_RANGE(ix), + }; + buf = sshape_build_box(&buf, &(sshape_box_t) { .color = 0xFF0000FF }); + T(buf.valid); + T(buf.vertices.buffer.ptr == vx); + T(buf.vertices.buffer.size == sizeof(vx)); + T(buf.indices.buffer.ptr == ix); + T(buf.indices.buffer.size == sizeof(ix)); + T(buf.vertices.shape_offset == 0); + T(buf.vertices.data_size == 24 * sizeof(sshape_vertex_t)); + T(buf.indices.shape_offset == 0); + T(buf.indices.data_size == 36 * sizeof(uint16_t)); +} + +UTEST(sokol_shape, build_sphere_defaults) { + sshape_vertex_t vx[128] = { 0 }; + uint16_t ix[128] = { 0 }; + + sshape_buffer_t buf = { + .vertices.buffer = SSHAPE_RANGE(vx), + .indices.buffer = SSHAPE_RANGE(ix), + }; + buf = sshape_build_sphere(&buf, &(sshape_sphere_t) { .color = 0xFF0000FF }); + T(buf.valid); + T(buf.vertices.buffer.ptr == vx); + T(buf.vertices.buffer.size == sizeof(vx)); + T(buf.indices.buffer.ptr == ix); + T(buf.indices.buffer.size == sizeof(ix)); + T(buf.vertices.shape_offset == 0); + T(buf.vertices.data_size == 30 * sizeof(sshape_vertex_t)); + T(buf.indices.shape_offset == 0); + T(buf.indices.data_size == 90 * sizeof(uint16_t)); +} + +UTEST(sokol_shape, build_cylinder_defaults) { + sshape_vertex_t vx[128] = { 0 }; + uint16_t ix[128] = { 0 }; + + sshape_buffer_t buf = { + .vertices.buffer = SSHAPE_RANGE(vx), + .indices.buffer = SSHAPE_RANGE(ix) + }; + buf = sshape_build_cylinder(&buf, &(sshape_cylinder_t) { .color = 0xFF0000FF }); + T(buf.valid); + T(buf.vertices.buffer.ptr == vx); + T(buf.vertices.buffer.size == sizeof(vx)); + T(buf.indices.buffer.ptr == ix); + T(buf.indices.buffer.size == sizeof(ix)); + T(buf.vertices.shape_offset == 0); + T(buf.vertices.data_size == 36 * sizeof(sshape_vertex_t)); + T(buf.indices.shape_offset == 0); + T(buf.indices.data_size == 60 * sizeof(uint16_t)); +} + +UTEST(sokol_shape, build_torus_defaults) { + sshape_vertex_t vx[128] = { 0 }; + uint16_t ix[256] = { 0 }; + + sshape_buffer_t buf = { + .vertices.buffer = SSHAPE_RANGE(vx), + .indices.buffer = SSHAPE_RANGE(ix), + }; + buf = sshape_build_torus(&buf, &(sshape_torus_t) { .color = 0xFF0000FF }); + T(buf.valid); + T(buf.vertices.buffer.ptr == vx); + T(buf.vertices.buffer.size == sizeof(vx)); + T(buf.indices.buffer.ptr == ix); + T(buf.indices.buffer.size == sizeof(ix)); + T(buf.vertices.shape_offset == 0); + T(buf.vertices.data_size == 36 * sizeof(sshape_vertex_t)); + T(buf.indices.shape_offset == 0); + T(buf.indices.data_size == 150 * sizeof(uint16_t)); +} diff --git a/thirdparty/sokol/tests/functional/sokol_spine_test.c b/thirdparty/sokol/tests/functional/sokol_spine_test.c new file mode 100644 index 0000000..435b32b --- /dev/null +++ b/thirdparty/sokol/tests/functional/sokol_spine_test.c @@ -0,0 +1,1197 @@ +//------------------------------------------------------------------------------ +// sokol_spine_test.c +//------------------------------------------------------------------------------ +#include "sokol_gfx.h" +#define SOKOL_SPINE_IMPL +#include "spine/spine.h" +#include "sokol_spine.h" +#include "utest.h" + +#define T(b) EXPECT_TRUE(b) + +static sspine_log_item last_logitem = SSPINE_LOGITEM_OK; +static void log_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data) { + (void)tag; (void)log_level; (void)message; (void)line_nr; (void)filename; (void)user_data; + last_logitem = log_item; +} + +static void init(void) { + last_logitem = SSPINE_LOGITEM_OK; + sg_setup(&(sg_desc){0}); + sspine_setup(&(sspine_desc){ .logger = { .func = log_func } }); +} + +static void init_with_desc(const sspine_desc* desc) { + last_logitem = SSPINE_LOGITEM_OK; + sspine_desc desc1 = *desc; + desc1.logger.func = log_func; + sg_setup(&(sg_desc){0}); + sspine_setup(&desc1); +} + +static void shutdown(void) { + sspine_shutdown(); + sg_shutdown(); +} + +// NOTE: this guarantees that the data is zero terminated because the loaded data +// might either be binary or text (the zero sentinel is NOT counted in the returned size) +static sspine_range load_data(const char* path) { + assert(path); + FILE* fp = fopen(path, "rb"); + assert(fp); + fseek(fp, 0, SEEK_END); + const size_t size = (size_t)ftell(fp); + fseek(fp, 0, SEEK_SET); + // room for terminating zero + const size_t alloc_size = size + 1; + uint8_t* ptr = (uint8_t*)malloc(alloc_size); + memset(ptr, 0, alloc_size); + // NOTE: GCC warns if result of fread() is ignored + size_t num_bytes = fread(ptr, size, 1, fp); + (void)num_bytes; + fclose(fp); + return (sspine_range) { .ptr = ptr, .size = size }; +} + +static void free_data(sspine_range r) { + free((void*)r.ptr); +} + +static sspine_atlas create_atlas(void) { + sspine_range atlas_data = load_data("spineboy.atlas"); + sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){ + .data = atlas_data + }); + free_data(atlas_data); + return atlas; +} + +static sspine_skeleton create_skeleton_json(sspine_atlas atlas) { + sspine_range skeleton_json_data = load_data("spineboy-pro.json"); + sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){ + .atlas = atlas, + .json_data = (const char*)skeleton_json_data.ptr + }); + free_data(skeleton_json_data); + return skeleton; +} + +static sspine_skeleton create_skeleton_binary(sspine_atlas atlas) { + sspine_range skeleton_binary_data = load_data("spineboy-pro.skel"); + sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){ + .atlas = atlas, + .binary_data = skeleton_binary_data + }); + free_data(skeleton_binary_data); + return skeleton; +} + +static sspine_skeleton create_skeleton(void) { + return create_skeleton_json(create_atlas()); +} + +static sspine_instance create_instance(void) { + return sspine_make_instance(&(sspine_instance_desc){ + .skeleton = create_skeleton(), + }); +} + +UTEST(sokol_spine, default_init_shutdown) { + // FIXME! + T(true); +} + +UTEST(sokol_spine, atlas_pool_exhausted) { + init_with_desc(&(sspine_desc){ + .atlas_pool_size = 4, + }); + for (int i = 0; i < 4; i++) { + sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){0}); + T(sspine_get_atlas_resource_state(atlas) == SSPINE_RESOURCESTATE_FAILED); + T(last_logitem == SSPINE_LOGITEM_ATLAS_DESC_NO_DATA); + } + sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){0}); + T(SSPINE_INVALID_ID == atlas.id); + T(sspine_get_atlas_resource_state(atlas) == SSPINE_RESOURCESTATE_INVALID); + T(last_logitem == SSPINE_LOGITEM_ATLAS_POOL_EXHAUSTED); + shutdown(); +} + +UTEST(sokol_spine, make_destroy_atlas_ok) { + init(); + sspine_atlas atlas = create_atlas(); + T(sspine_get_atlas_resource_state(atlas) == SSPINE_RESOURCESTATE_VALID); + T(sspine_atlas_valid(atlas)); + sspine_destroy_atlas(atlas); + T(sspine_get_atlas_resource_state(atlas) == SSPINE_RESOURCESTATE_INVALID); + T(!sspine_atlas_valid(atlas)) + shutdown(); +} + +UTEST(sokol_spine, make_atlas_fail_no_data) { + init(); + sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){0}); + T(atlas.id != SSPINE_INVALID_ID); + T(last_logitem == SSPINE_LOGITEM_ATLAS_DESC_NO_DATA); + T(sspine_get_atlas_resource_state(atlas) == SSPINE_RESOURCESTATE_FAILED); + T(!sspine_atlas_valid(atlas)); + shutdown(); +} + +// an invalid atlas must return zero number of images +UTEST(sokol_spine, failed_atlas_no_images) { + init(); + sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){0}); + T(last_logitem == SSPINE_LOGITEM_ATLAS_DESC_NO_DATA); + T(atlas.id != SSPINE_INVALID_ID); + T(!sspine_atlas_valid(atlas)); + T(sspine_num_images(atlas) == 0); + shutdown(); + +} + +// NOTE: spine-c doesn't detect wrong/corrupt atlas file data, so we can't test for that + +UTEST(sokol_spine, image_valid) { + init(); + sspine_atlas atlas = create_atlas(); + T(sspine_image_valid(sspine_image_by_index(atlas, 0))); + T(!sspine_image_valid(sspine_image_by_index(atlas, 1))); + T(!sspine_image_valid(sspine_image_by_index(atlas, -1))); + sspine_destroy_atlas(atlas); + T(!sspine_image_valid(sspine_image_by_index(atlas, 0))); + shutdown(); +} + +UTEST(sokol_spine, atlas_image_info) { + init(); + sspine_atlas atlas = create_atlas(); + T(sspine_atlas_valid(atlas)); + T(sspine_num_images(atlas) == 1); + const sspine_image_info img_info = sspine_get_image_info(sspine_image_by_index(atlas, 0)); + T(img_info.valid); + T(img_info.sgimage.id != SG_INVALID_ID); + T(sg_query_image_state(img_info.sgimage) == SG_RESOURCESTATE_ALLOC); + T(strcmp(img_info.filename.cstr, "spineboy.png") == 0); + T(img_info.min_filter == SG_FILTER_LINEAR); + T(img_info.mag_filter == SG_FILTER_LINEAR); + T(img_info.wrap_u == SG_WRAP_CLAMP_TO_EDGE); + T(img_info.wrap_v == SG_WRAP_CLAMP_TO_EDGE); + T(img_info.width == 1024); + T(img_info.height == 256); + T(img_info.premul_alpha == false); + shutdown(); +} + +UTEST(sokol_spine, atlas_with_overrides) { + init(); + sspine_range atlas_data = load_data("spineboy.atlas"); + sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){ + .data = atlas_data, + .override = { + .min_filter = SG_FILTER_NEAREST, + .mag_filter = SG_FILTER_NEAREST, + .mipmap_filter = SG_FILTER_LINEAR, + .wrap_u = SG_WRAP_REPEAT, + .wrap_v = SG_WRAP_CLAMP_TO_EDGE, + .premul_alpha_enabled = true, + } + }); + T(sspine_atlas_valid(atlas)); + T(sspine_num_images(atlas) == 1); + const sspine_image_info img_info = sspine_get_image_info(sspine_image_by_index(atlas, 0)); + T(img_info.valid); + T(img_info.sgimage.id != SG_INVALID_ID); + T(sg_query_image_state(img_info.sgimage) == SG_RESOURCESTATE_ALLOC); + T(strcmp(img_info.filename.cstr, "spineboy.png") == 0); + T(img_info.min_filter == SG_FILTER_NEAREST); + T(img_info.mag_filter == SG_FILTER_NEAREST); + T(img_info.mipmap_filter == SG_FILTER_LINEAR); + T(img_info.wrap_u == SG_WRAP_REPEAT); + T(img_info.wrap_v == SG_WRAP_CLAMP_TO_EDGE); + T(img_info.width == 1024); + T(img_info.height == 256); + T(img_info.premul_alpha == true); + shutdown(); +} + +UTEST(sokol_spine, skeleton_pool_exhausted) { + init_with_desc(&(sspine_desc){ + .skeleton_pool_size = 4 + }); + for (int i = 0; i < 4; i++) { + sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){0}); + T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_FAILED); + T(last_logitem == SSPINE_LOGITEM_SKELETON_DESC_NO_DATA); + } + sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){0}); + T(SSPINE_INVALID_ID == skeleton.id); + T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_INVALID); + T(last_logitem == SSPINE_LOGITEM_SKELETON_POOL_EXHAUSTED); + shutdown(); +} + +UTEST(sokol_spine, make_destroy_skeleton_json_ok) { + init(); + sspine_skeleton skeleton = create_skeleton_json(create_atlas()); + T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_VALID); + T(sspine_skeleton_valid(skeleton)); + sspine_destroy_skeleton(skeleton); + T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_INVALID); + T(!sspine_skeleton_valid(skeleton)); + shutdown(); +} + +UTEST(sokol_spine, make_destroy_skeleton_binary_ok) { + init(); + sspine_skeleton skeleton = create_skeleton_binary(create_atlas()); + T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_VALID); + T(sspine_skeleton_valid(skeleton)); + sspine_destroy_skeleton(skeleton); + T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_INVALID); + T(!sspine_skeleton_valid(skeleton)); + shutdown(); +} + +UTEST(sokol_spine, make_skeleton_fail_no_data) { + init(); + sspine_atlas atlas = create_atlas(); + sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){ + .atlas = atlas + }); + T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_FAILED); + T(!sspine_skeleton_valid(skeleton)); + T(last_logitem == SSPINE_LOGITEM_SKELETON_DESC_NO_DATA); + shutdown(); +} + +UTEST(sokol_spine, make_skeleton_fail_no_atlas) { + init(); + sspine_range skeleton_json_data = load_data("spineboy-pro.json"); + sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){ + .json_data = (const char*)skeleton_json_data.ptr + }); + free_data(skeleton_json_data); + T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_FAILED); + T(!sspine_skeleton_valid(skeleton)); + T(last_logitem == SSPINE_LOGITEM_SKELETON_DESC_NO_ATLAS); + shutdown(); +} + +UTEST(sokol_spine, make_skeleton_fail_with_failed_atlas) { + init(); + sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){0}); + T(last_logitem == SSPINE_LOGITEM_ATLAS_DESC_NO_DATA); + T(sspine_get_atlas_resource_state(atlas) == SSPINE_RESOURCESTATE_FAILED); + sspine_skeleton skeleton = create_skeleton_json(atlas); + T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_FAILED); + T(!sspine_skeleton_valid(skeleton)); + T(last_logitem == SSPINE_LOGITEM_SKELETON_ATLAS_NOT_VALID); + shutdown(); +} + +UTEST(sokol_spine, make_skeleton_json_fail_corrupt_data) { + init(); + sspine_atlas atlas = create_atlas(); + const char* invalid_json_data = "This is not valid JSON!"; + sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){ + .atlas = atlas, + .json_data = (const char*)invalid_json_data, + }); + T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_FAILED); + T(last_logitem == SSPINE_LOGITEM_CREATE_SKELETON_DATA_FROM_JSON_FAILED); + sspine_destroy_skeleton(skeleton); + T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_INVALID); + shutdown(); +} + +// FIXME: this crashes the spine-c runtime +/* +UTEST(sokol_spine, make_skeleton_binary_fail_corrupt_data) { + init(); + sspine_atlas atlas = create_atlas(); + uint8_t invalid_binary_data[] = { 0x23, 0x63, 0x11, 0xFF }; + sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){ + .atlas = atlas, + .binary_data = { .ptr = invalid_binary_data, .size = sizeof(invalid_binary_data) } + }); + T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_FAILED); + sspine_destroy_skeleton(skeleton); + T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_INVALID); + shutdown(); +} +*/ + +UTEST(sokol_spine, instance_pool_exhausted) { + init_with_desc(&(sspine_desc){ + .instance_pool_size = 4 + }); + for (int i = 0; i < 4; i++) { + sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){0}); + T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_FAILED); + T(last_logitem == SSPINE_LOGITEM_INSTANCE_DESC_NO_SKELETON); + } + sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){0}); + T(SSPINE_INVALID_ID == instance.id); + T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_INVALID); + T(last_logitem == SSPINE_LOGITEM_INSTANCE_POOL_EXHAUSTED); + shutdown(); +} + +UTEST(sokol_spine, make_destroy_instance_ok) { + init(); + sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){ + .skeleton = create_skeleton_json(create_atlas()) + }); + T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_VALID); + T(sspine_instance_valid(instance)); + sspine_destroy_instance(instance); + T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_INVALID); + T(!sspine_instance_valid(instance)); + shutdown(); +} + +UTEST(sokol_spine, make_instance_fail_no_skeleton) { + init(); + sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){0}); + T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_FAILED); + T(last_logitem == SSPINE_LOGITEM_INSTANCE_DESC_NO_SKELETON); + sspine_destroy_instance(instance); + T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_INVALID); + shutdown(); +} + +UTEST(sokol_spine, make_instance_fail_with_failed_skeleton) { + init(); + sspine_skeleton failed_skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){0}); + T(last_logitem == SSPINE_LOGITEM_SKELETON_DESC_NO_DATA); + T(sspine_get_skeleton_resource_state(failed_skeleton) == SSPINE_RESOURCESTATE_FAILED); + sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){ + .skeleton = failed_skeleton + }); + T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_FAILED); + T(last_logitem == SSPINE_LOGITEM_INSTANCE_SKELETON_NOT_VALID); + shutdown(); +} + +UTEST(sokol_spine, make_instance_fail_with_destroyed_atlas) { + init(); + sspine_atlas atlas = create_atlas(); + T(sspine_atlas_valid(atlas)); + sspine_skeleton skeleton = create_skeleton_json(atlas); + T(sspine_skeleton_valid(skeleton)); + sspine_destroy_atlas(atlas); + T(!sspine_atlas_valid(atlas)); + sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){ + .skeleton = skeleton + }); + T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_FAILED); + T(last_logitem == SSPINE_LOGITEM_INSTANCE_ATLAS_NOT_VALID); + shutdown(); +} + +UTEST(sokol_spine, get_skeleton_atlas) { + init(); + sspine_atlas atlas = create_atlas(); + sspine_skeleton skeleton = create_skeleton_json(atlas); + T(sspine_get_skeleton_atlas(skeleton).id == atlas.id); + sspine_destroy_skeleton(skeleton); + T(sspine_get_skeleton_atlas(skeleton).id == SSPINE_INVALID_ID); + shutdown(); +} + +UTEST(sokol_spine, get_instance_skeleton) { + init(); + sspine_atlas atlas = create_atlas(); + sspine_skeleton skeleton = create_skeleton_json(atlas); + sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){ + .skeleton = skeleton + }); + T(sspine_get_instance_skeleton(instance).id == skeleton.id); + sspine_destroy_instance(instance); + T(sspine_get_instance_skeleton(instance).id == SSPINE_INVALID_ID); + shutdown(); +} + +UTEST(sokol_spine, set_get_position) { + init(); + sspine_instance instance = create_instance(); + sspine_set_position(instance, (sspine_vec2){ .x=1.0f, .y=2.0f }); + const sspine_vec2 pos = sspine_get_position(instance); + T(pos.x == 1.0f); + T(pos.y == 2.0f); + shutdown(); +} + +UTEST(sokol_spine, set_get_position_destroyed_instance) { + init(); + sspine_instance instance = create_instance(); + sspine_set_position(instance, (sspine_vec2){ .x=1.0f, .y=2.0f }); + sspine_destroy_instance(instance); + const sspine_vec2 pos = sspine_get_position(instance); + T(pos.x == 0.0f); + T(pos.y == 0.0f); + shutdown(); +} + +UTEST(sokol_spine, set_get_scale) { + init(); + sspine_instance instance = create_instance(); + sspine_set_scale(instance, (sspine_vec2){ .x=2.0f, .y=3.0f }); + const sspine_vec2 scale = sspine_get_scale(instance); + T(scale.x == 2.0f); + T(scale.y == 3.0f); + shutdown(); +} + +UTEST(sokol_spine, set_get_scale_destroyed_instance) { + init(); + sspine_instance instance = create_instance(); + sspine_set_scale(instance, (sspine_vec2){ .x=2.0f, .y=3.0f }); + sspine_destroy_instance(instance); + const sspine_vec2 scale = sspine_get_scale(instance); + T(scale.x == 0.0f); + T(scale.y == 0.0f); + shutdown(); +} + +UTEST(sokol_spine, set_get_color) { + init(); + sspine_instance instance = create_instance(); + sspine_set_color(instance, (sspine_color) { .r=1.0f, .g=2.0f, .b=3.0f, .a=4.0f }); + const sspine_color color = sspine_get_color(instance); + T(color.r == 1.0f); + T(color.g == 2.0f); + T(color.b == 3.0f); + T(color.a == 4.0f); + shutdown(); +} + +UTEST(sokol_spine, set_get_color_destroyed_instance) { + init(); + sspine_instance instance = create_instance(); + sspine_set_color(instance, (sspine_color) { .r=1.0f, .g=2.0f, .b=3.0f, .a=4.0f }); + sspine_destroy_instance(instance); + const sspine_color color = sspine_get_color(instance); + T(color.r == 0.0f); + T(color.g == 0.0f); + T(color.b == 0.0f); + T(color.a == 0.0f); + shutdown(); +} + +UTEST(sokol_spine, anim_by_name) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_anim a0 = sspine_anim_by_name(skeleton, "hoverboard"); + T((a0.skeleton_id == skeleton.id) && (a0.index == 2)); + sspine_anim a1 = sspine_anim_by_name(skeleton, "bla"); + T((a1.skeleton_id == 0) && (a1.index == 0)); + shutdown(); +} + +UTEST(sokol_spine, anim_by_name_destroyed_instance) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_destroy_skeleton(skeleton); + sspine_anim a0 = sspine_anim_by_name(skeleton, "hoverboard"); + T((a0.skeleton_id == 0) && (a0.index == 0)); + shutdown(); +} + +UTEST(sokol_spine, anim_valid) { + init(); + sspine_skeleton skeleton = create_skeleton(); + T(sspine_anim_valid(sspine_anim_by_index(skeleton, 0))); + T(sspine_anim_valid(sspine_anim_by_index(skeleton, 10))); + T(!sspine_anim_valid(sspine_anim_by_index(skeleton, -1))); + T(!sspine_anim_valid(sspine_anim_by_index(skeleton, 11))); + sspine_destroy_skeleton(skeleton); + T(!sspine_anim_valid(sspine_anim_by_index(skeleton, 0))); + shutdown(); +} + +UTEST(sokol_spine, anim_equal) { + init(); + T(sspine_anim_equal((sspine_anim){ 1, 2 }, (sspine_anim){ 1, 2 })); + T(!sspine_anim_equal((sspine_anim){ 2, 2 }, (sspine_anim){ 1, 2 })); + T(!sspine_anim_equal((sspine_anim){ 1, 3 }, (sspine_anim){ 1, 2 })); + shutdown(); +} + +UTEST(sokol_spine, num_anims) { + init(); + sspine_skeleton skeleton = create_skeleton(); + T(sspine_num_anims(skeleton) == 11); + sspine_destroy_skeleton(skeleton); + T(sspine_num_anims(skeleton) == 0); + shutdown(); +} + +UTEST(sokol_spine, get_anim_info) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_anim anim = sspine_anim_by_name(skeleton, "hoverboard"); + const sspine_anim_info info = sspine_get_anim_info(anim); + T(info.valid); + T(info.index == 2); + T(strcmp(info.name.cstr, "hoverboard") == 0); + T(info.duration == 1.0f); + shutdown(); +} + +UTEST(sokol_spine, get_anim_info_destroyed_skeleton) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_anim anim = sspine_anim_by_name(skeleton, "hoverboard"); + sspine_destroy_skeleton(skeleton); + const sspine_anim_info info = sspine_get_anim_info(anim); + T(!info.valid); + shutdown(); +} + +UTEST(sokol_spine, get_anim_info_invalid_index) { + init(); + sspine_skeleton skeleton = create_skeleton(); + const sspine_anim_info i0 = sspine_get_anim_info(sspine_anim_by_index(skeleton, -1)); + T(!i0.valid); + T(!i0.name.valid); + const sspine_anim_info i1 = sspine_get_anim_info(sspine_anim_by_index(skeleton, 1234)); + T(!i1.valid); + T(!i1.name.valid); + shutdown(); +} + +UTEST(sokol_spine, atlas_page_valid) { + init(); + sspine_atlas atlas = create_atlas(); + T(sspine_atlas_page_valid(sspine_atlas_page_by_index(atlas, 0))); + T(!sspine_atlas_page_valid(sspine_atlas_page_by_index(atlas, -1))); + T(!sspine_atlas_page_valid(sspine_atlas_page_by_index(atlas, 1))); + sspine_destroy_atlas(atlas); + T(!sspine_atlas_page_valid(sspine_atlas_page_by_index(atlas, 0))); + shutdown(); +} + +UTEST(sokol_spine, num_atlas_pages) { + init(); + sspine_atlas atlas = create_atlas(); + T(sspine_num_atlas_pages(atlas) == 1); + sspine_destroy_atlas(atlas); + T(sspine_num_atlas_pages(atlas) == 0); + shutdown(); +} + +UTEST(sokol_spine, get_atlas_page_info) { + init(); + sspine_atlas atlas = create_atlas(); + const sspine_atlas_page_info info = sspine_get_atlas_page_info(sspine_atlas_page_by_index(atlas, 0)); + T(info.valid); + T(info.atlas.id == atlas.id); + T(info.image.valid); + T(info.image.sgimage.id != SG_INVALID_ID); + T(sg_query_image_state(info.image.sgimage) == SG_RESOURCESTATE_ALLOC); + T(strcmp(info.image.filename.cstr, "spineboy.png") == 0); + T(info.image.min_filter == SG_FILTER_LINEAR); + T(info.image.mag_filter == SG_FILTER_LINEAR); + T(info.image.wrap_u == SG_WRAP_CLAMP_TO_EDGE); + T(info.image.wrap_v == SG_WRAP_CLAMP_TO_EDGE); + T(info.image.width == 1024); + T(info.image.height == 256); + T(info.image.premul_alpha == false); + T(info.overrides.min_filter == _SG_FILTER_DEFAULT); + T(info.overrides.mag_filter == _SG_FILTER_DEFAULT); + T(info.overrides.wrap_u == _SG_WRAP_DEFAULT); + T(info.overrides.wrap_v == _SG_WRAP_DEFAULT); + T(!info.overrides.premul_alpha_enabled); + T(!info.overrides.premul_alpha_disabled); + shutdown(); +} + +UTEST(sokol_spine, get_atlas_page_info_destroyed_atlas) { + init(); + sspine_atlas atlas = create_atlas(); + sspine_destroy_atlas(atlas); + const sspine_atlas_page_info info = sspine_get_atlas_page_info(sspine_atlas_page_by_index(atlas, 0)); + T(!info.valid); + T(info.atlas.id == SSPINE_INVALID_ID); + shutdown(); +} + +UTEST(sokol_spine, get_atlas_page_info_invalid_index) { + init(); + sspine_atlas atlas = create_atlas(); + sspine_destroy_atlas(atlas); + const sspine_atlas_page_info i0 = sspine_get_atlas_page_info(sspine_atlas_page_by_index(atlas, -1)); + T(!i0.valid); + T(i0.atlas.id == SSPINE_INVALID_ID); + const sspine_atlas_page_info i1 = sspine_get_atlas_page_info(sspine_atlas_page_by_index(atlas, 1234)); + T(!i0.valid); + T(i1.atlas.id == SSPINE_INVALID_ID); + shutdown(); +} + +UTEST(sokol_spine, atlas_get_atlas_page_info_with_overrides) { + init(); + sspine_range atlas_data = load_data("spineboy.atlas"); + sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){ + .data = atlas_data, + .override = { + .min_filter = SG_FILTER_NEAREST, + .mag_filter = SG_FILTER_NEAREST, + .mipmap_filter = SG_FILTER_NEAREST, + .wrap_u = SG_WRAP_REPEAT, + .wrap_v = SG_WRAP_CLAMP_TO_EDGE, + .premul_alpha_enabled = true, + } + }); + const sspine_atlas_page_info info = sspine_get_atlas_page_info(sspine_atlas_page_by_index(atlas, 0)); + T(info.valid); + T(info.atlas.id == atlas.id); + T(info.image.valid); + T(info.image.sgimage.id != SG_INVALID_ID); + T(sg_query_image_state(info.image.sgimage) == SG_RESOURCESTATE_ALLOC); + T(strcmp(info.image.filename.cstr, "spineboy.png") == 0); + T(info.image.min_filter == SG_FILTER_LINEAR); + T(info.image.mag_filter == SG_FILTER_LINEAR); + T(info.image.mipmap_filter == SG_FILTER_NONE); + T(info.image.wrap_u == SG_WRAP_CLAMP_TO_EDGE); + T(info.image.wrap_v == SG_WRAP_CLAMP_TO_EDGE); + T(info.image.width == 1024); + T(info.image.height == 256); + T(info.image.premul_alpha == true); // FIXME: hmm, this is actually inconsistent + T(info.overrides.min_filter == SG_FILTER_NEAREST); + T(info.overrides.mag_filter == SG_FILTER_NEAREST); + T(info.overrides.mipmap_filter == SG_FILTER_NEAREST); + T(info.overrides.wrap_u == SG_WRAP_REPEAT); + T(info.overrides.wrap_v == SG_WRAP_CLAMP_TO_EDGE); + T(info.overrides.premul_alpha_enabled); + T(!info.overrides.premul_alpha_disabled); + shutdown(); +} + +UTEST(sokol_spine, bone_by_name) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_bone b0 = sspine_bone_by_name(skeleton, "crosshair"); + T((b0.skeleton_id == skeleton.id) && (b0.index == 2)); + sspine_bone b1 = sspine_bone_by_name(skeleton, "blablub"); + T((b1.skeleton_id == 0) && (b1.index == 0)); + shutdown(); +} + +UTEST(sokol_spine, bone_by_name_destroyed_skeleton) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_destroy_skeleton(skeleton); + sspine_bone b0 = sspine_bone_by_name(skeleton, "crosshair"); + T((b0.skeleton_id == 0) && (b0.index == 0)); + shutdown(); +} + +UTEST(sokol_spine, bone_valid) { + init(); + sspine_skeleton skeleton = create_skeleton(); + T(sspine_bone_valid(sspine_bone_by_index(skeleton, 0))); + T(sspine_bone_valid(sspine_bone_by_index(skeleton, 66))); + T(!sspine_bone_valid(sspine_bone_by_index(skeleton, -1))); + T(!sspine_bone_valid(sspine_bone_by_index(skeleton, 67))); + sspine_destroy_skeleton(skeleton); + T(!sspine_bone_valid(sspine_bone_by_index(skeleton, 0))); + shutdown(); +} + +UTEST(sokol_spine, bone_equal) { + init(); + T(sspine_bone_equal((sspine_bone){ 1, 2 }, (sspine_bone){ 1, 2 })); + T(!sspine_bone_equal((sspine_bone){ 2, 2 }, (sspine_bone){ 1, 2 })); + T(!sspine_bone_equal((sspine_bone){ 1, 3 }, (sspine_bone){ 1, 2 })); + shutdown(); +} + +UTEST(sokol_spine, num_bones) { + init(); + sspine_skeleton skeleton = create_skeleton(); + T(sspine_num_bones(skeleton) == 67); + sspine_destroy_skeleton(skeleton); + T(sspine_num_bones(skeleton) == 0); + shutdown(); +} + +UTEST(sokol_spine, get_bone_info_root) { + init(); + sspine_skeleton skeleton = create_skeleton(); + const sspine_bone_info info = sspine_get_bone_info(sspine_bone_by_name(skeleton, "root")); + T(info.valid); + T(info.index == 0); + T((info.parent_bone.skeleton_id == 0) && (info.parent_bone.index == 0)); + T(strcmp(info.name.cstr, "root") == 0); + T(info.length == 0.0f); + T(info.pose.position.x == 0.0f); + T(info.pose.position.y == 0.0f); + T(info.pose.rotation == 0.05f); + T(info.pose.scale.x == 1.0f); + T(info.pose.scale.y == 1.0f); + T(info.pose.shear.x == 0.0f); + T(info.pose.shear.y == 0.0f); + shutdown(); +} + +UTEST(sokol_spine, get_bone_info_parent_bone) { + init(); + sspine_skeleton skeleton = create_skeleton(); + const sspine_bone_info info = sspine_get_bone_info(sspine_bone_by_name(skeleton, "rear-shin")); + T(info.valid); + T(info.index == 7); + T((info.parent_bone.skeleton_id == skeleton.id) && (info.parent_bone.index == 6)); + shutdown(); +} + +UTEST(sokol_spine, get_bone_info_destroyed_skeleton) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_bone bone = sspine_bone_by_name(skeleton, "root"); + sspine_destroy_skeleton(skeleton); + const sspine_bone_info info = sspine_get_bone_info(bone); + T(!info.valid); + T(!info.name.valid); + shutdown(); +} + +UTEST(sokol_spine, get_bone_info_invalid_index) { + init(); + sspine_skeleton skeleton = create_skeleton(); + const sspine_bone_info i0 = sspine_get_bone_info(sspine_bone_by_index(skeleton, -1)); + T(!i0.valid); + T(!i0.name.valid); + const sspine_bone_info i1 = sspine_get_bone_info(sspine_bone_by_index(skeleton, 1234)); + T(!i1.valid); + T(!i1.name.valid); + shutdown(); +} + +UTEST(sokol_spine, set_get_bone_transform) { + init(); + sspine_instance instance = create_instance(); + sspine_skeleton skeleton = sspine_get_instance_skeleton(instance); + sspine_bone bone = sspine_bone_by_name(skeleton, "root"); + sspine_set_bone_transform(instance, bone, &(sspine_bone_transform){ + .position = { 1.0f, 2.0f }, + .rotation = 3.0f, + .scale = { 4.0f, 5.0f }, + .shear = { 6.0f, 7.0f } + }); + const sspine_bone_transform tform = sspine_get_bone_transform(instance, bone); + T(tform.position.x == 1.0f); + T(tform.position.y == 2.0f); + T(tform.rotation == 3.0f); + T(tform.scale.x == 4.0f); + T(tform.scale.y == 5.0f); + T(tform.shear.x == 6.0f); + T(tform.shear.y == 7.0f); + shutdown(); +} + +UTEST(sokol_spine, set_get_bone_transform_destroyed_instance) { + init(); + sspine_instance instance = create_instance(); + sspine_skeleton skeleton = sspine_get_instance_skeleton(instance); + sspine_bone bone = sspine_bone_by_name(skeleton, "root"); + sspine_destroy_instance(instance); + sspine_set_bone_transform(instance, bone, &(sspine_bone_transform){ + .position = { 1.0f, 2.0f }, + .rotation = 3.0f, + .scale = { 4.0f, 5.0f }, + .shear = { 6.0f, 7.0f } + }); + const sspine_bone_transform tform = sspine_get_bone_transform(instance, bone); + T(tform.position.x == 0.0f); + T(tform.position.y == 0.0f); + T(tform.rotation == 0.0f); + T(tform.scale.x == 0.0f); + T(tform.scale.y == 0.0f); + T(tform.shear.x == 0.0f); + T(tform.shear.y == 0.0f); + shutdown(); +} + +UTEST(sokol_spine, set_get_bone_position) { + init(); + sspine_instance instance = create_instance(); + sspine_skeleton skeleton = sspine_get_instance_skeleton(instance); + sspine_bone bone = sspine_bone_by_name(skeleton, "root"); + sspine_set_bone_position(instance, bone, (sspine_vec2){ 1.0f, 2.0f }); + const sspine_vec2 p0 = sspine_get_bone_position(instance, bone); + T(p0.x == 1.0f); + T(p0.y == 2.0f); + sspine_destroy_instance(instance); + const sspine_vec2 p1 = sspine_get_bone_position(instance, bone); + T(p1.x == 0.0f); + T(p1.y == 0.0f); + shutdown(); +} + +UTEST(sokol_spine, set_get_bone_rotation) { + init(); + sspine_instance instance = create_instance(); + sspine_skeleton skeleton = sspine_get_instance_skeleton(instance); + sspine_bone bone = sspine_bone_by_name(skeleton, "root"); + sspine_set_bone_rotation(instance, bone, 5.0f); + T(sspine_get_bone_rotation(instance, bone) == 5.0f); + sspine_destroy_instance(instance); + T(sspine_get_bone_rotation(instance, bone) == 0.0f); + shutdown(); +} + +UTEST(sokol_spine, set_get_bone_scale) { + init(); + sspine_instance instance = create_instance(); + sspine_skeleton skeleton = sspine_get_instance_skeleton(instance); + sspine_bone bone = sspine_bone_by_name(skeleton, "root"); + sspine_set_bone_scale(instance, bone, (sspine_vec2){ 1.0f, 2.0f }); + const sspine_vec2 s0 = sspine_get_bone_scale(instance, bone); + T(s0.x == 1.0f); + T(s0.y == 2.0f); + sspine_destroy_instance(instance); + const sspine_vec2 s1 = sspine_get_bone_scale(instance, bone); + T(s1.x == 0.0f); + T(s1.y == 0.0f); + shutdown(); +} + +UTEST(sokol_spine, set_get_bone_shear) { + init(); + sspine_instance instance = create_instance(); + sspine_skeleton skeleton = sspine_get_instance_skeleton(instance); + sspine_bone bone = sspine_bone_by_name(skeleton, "root"); + sspine_set_bone_shear(instance, bone, (sspine_vec2){ 1.0f, 2.0f }); + const sspine_vec2 s0 = sspine_get_bone_shear(instance, bone); + T(s0.x == 1.0f); + T(s0.y == 2.0f); + sspine_destroy_instance(instance); + const sspine_vec2 s1 = sspine_get_bone_shear(instance, bone); + T(s1.x == 0.0f); + T(s1.y == 0.0f); + shutdown(); +} + +UTEST(sokol_spine, slot_by_name) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_slot s0 = sspine_slot_by_name(skeleton, "portal-streaks1"); + T((s0.skeleton_id == skeleton.id) && (s0.index == 3)); + sspine_slot s1 = sspine_slot_by_name(skeleton, "blablub"); + T((s1.skeleton_id == 0) && (s1.index == 0)); + shutdown(); +} + +UTEST(sokol_spine, slot_by_name_destroyed_skeleton) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_destroy_skeleton(skeleton); + sspine_slot s0 = sspine_slot_by_name(skeleton, "portal-streaks1"); + T((s0.skeleton_id == 0) && (s0.index == 0)); + shutdown(); +} + +UTEST(sokol_spine, num_slots) { + init(); + sspine_skeleton skeleton = create_skeleton(); + T(sspine_num_slots(skeleton) == 52); + sspine_destroy_skeleton(skeleton); + T(sspine_num_slots(skeleton) == 0); + shutdown(); +} + +UTEST(sokol_spine, slot_valid) { + init(); + sspine_skeleton skeleton = create_skeleton(); + T(sspine_slot_valid(sspine_slot_by_index(skeleton, 0))); + T(sspine_slot_valid(sspine_slot_by_index(skeleton, 51))); + T(!sspine_slot_valid(sspine_slot_by_index(skeleton, -1))); + T(!sspine_slot_valid(sspine_slot_by_index(skeleton, 52))); + sspine_destroy_skeleton(skeleton); + T(!sspine_slot_valid(sspine_slot_by_index(skeleton, 0))); + shutdown(); +} + +UTEST(sokol_spine, slot_equal) { + init(); + T(sspine_slot_equal((sspine_slot){ 1, 2 }, (sspine_slot){ 1, 2 })); + T(!sspine_slot_equal((sspine_slot){ 2, 2 }, (sspine_slot){ 1, 2 })); + T(!sspine_slot_equal((sspine_slot){ 1, 3 }, (sspine_slot){ 1, 2 })); + shutdown(); +} + +UTEST(sokol_spine, get_slot_info) { + init(); + sspine_skeleton skeleton = create_skeleton(); + const sspine_slot_info info = sspine_get_slot_info(sspine_slot_by_name(skeleton, "portal-streaks1")); + T(info.valid); + T(info.index == 3); + T(strcmp(info.name.cstr, "portal-streaks1") == 0); + T(!info.attachment_name.valid); + T((info.bone.skeleton_id == skeleton.id) && (info.bone.index == 62)); + T(info.color.r == 1.0f); + T(info.color.g == 1.0f); + T(info.color.b == 1.0f); + T(info.color.a == 1.0f); + shutdown(); +} + +UTEST(sokol_spine, get_slot_info_destroyed_skeleton) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_slot slot = sspine_slot_by_name(skeleton, "portal-streaks1"); + sspine_destroy_skeleton(skeleton); + const sspine_slot_info info = sspine_get_slot_info(slot); + T(!info.valid); + T(!info.name.valid); + shutdown(); +} + +UTEST(sokol_spine, get_slot_info_invalid_index) { + init(); + sspine_skeleton skeleton = create_skeleton(); + const sspine_slot_info i0 = sspine_get_slot_info(sspine_slot_by_index(skeleton, -1)); + T(!i0.valid); + T(!i0.name.valid); + const sspine_slot_info i1 = sspine_get_slot_info(sspine_slot_by_index(skeleton, 1234)); + T(!i1.valid); + T(!i1.name.valid); + shutdown(); +} + +UTEST(sokol_spine, set_get_slot_color) { + init(); + sspine_instance instance = create_instance(); + sspine_skeleton skeleton = sspine_get_instance_skeleton(instance); + sspine_slot slot = sspine_slot_by_name(skeleton, "portal-streaks1"); + sspine_set_slot_color(instance, slot, (sspine_color){ 1.0f, 2.0f, 3.0f, 4.0f }); + const sspine_color color = sspine_get_slot_color(instance, slot); + T(color.r == 1.0f); + T(color.g == 2.0f); + T(color.b == 3.0f); + T(color.a == 4.0f); + const sspine_slot_info info = sspine_get_slot_info(slot); + T(info.color.r == 1.0f); + T(info.color.g == 1.0f); + T(info.color.b == 1.0f); + T(info.color.a == 1.0f); + shutdown(); +} + +UTEST(sokol_spine, event_by_name) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_event e0 = sspine_event_by_name(skeleton, "footstep"); + T((e0.skeleton_id == skeleton.id) && (e0.index == 0)); + sspine_event e1 = sspine_event_by_name(skeleton, "bla"); + T((e1.skeleton_id == 0) && (e1.index == 0)); + shutdown(); +} + +UTEST(sokol_spine, event_by_name_destroyed_skeleton) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_destroy_skeleton(skeleton); + sspine_event e0 = sspine_event_by_name(skeleton, "footstep"); + T((e0.skeleton_id == 0) && (e0.index == 0)); + shutdown(); +} + +UTEST(sokol_spine, event_valid) { + init(); + sspine_skeleton skeleton = create_skeleton(); + T(sspine_event_valid(sspine_event_by_index(skeleton, 0))); + T(!sspine_event_valid(sspine_event_by_index(skeleton, 1))); + T(!sspine_event_valid(sspine_event_by_index(skeleton, -1))); + sspine_destroy_skeleton(skeleton); + T(!sspine_event_valid(sspine_event_by_index(skeleton, 0))); + shutdown(); +} + +UTEST(sokol_spine, event_equal) { + init(); + T(sspine_event_equal((sspine_event){ 1, 2 }, (sspine_event){ 1, 2 })); + T(!sspine_event_equal((sspine_event){ 2, 2 }, (sspine_event){ 1, 2 })); + T(!sspine_event_equal((sspine_event){ 1, 3 }, (sspine_event){ 1, 2 })); + shutdown(); +} + +UTEST(sokol_spine, num_events) { + init(); + sspine_skeleton skeleton = create_skeleton(); + T(sspine_num_events(skeleton) == 1); + sspine_destroy_skeleton(skeleton); + T(sspine_num_events(skeleton) == 0); + shutdown(); +} + +UTEST(sokol_spine, get_event_info) { + init(); + sspine_skeleton skeleton = create_skeleton(); + const sspine_event_info info = sspine_get_event_info(sspine_event_by_index(skeleton, 0)); + T(info.valid); + T(0 == strcmp(info.name.cstr, "footstep")); + T(0 == info.index); + T(0 == info.int_value); + T(0.0f == info.float_value); + T(!info.string_value.valid); + T(!info.audio_path.valid); + T(0.0f == info.volume); + T(0.0f == info.balance); + shutdown(); +} + +UTEST(sokol_spine, get_event_info_destroyed_skeleton) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_destroy_skeleton(skeleton); + const sspine_event_info info = sspine_get_event_info(sspine_event_by_index(skeleton, 0)); + T(!info.valid); + T(!info.name.valid); + shutdown(); +} + +UTEST(sokol_spine, iktarget_by_name) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_iktarget ik0 = sspine_iktarget_by_name(skeleton, "board-ik"); + T((ik0.skeleton_id == skeleton.id) && (ik0.index == 2)); + sspine_iktarget ik1 = sspine_iktarget_by_name(skeleton, "bla"); + T((ik1.skeleton_id == 0) && (ik1.index == 0)); + shutdown(); +} + +UTEST(sokol_spine, iktarget_by_name_destroyed_skeleton) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_destroy_skeleton(skeleton); + sspine_iktarget ik0 = sspine_iktarget_by_name(skeleton, "board-ik"); + T((ik0.skeleton_id == 0) && (ik0.index == 0)); + shutdown(); +} + +UTEST(sokol_spine, iktarget_valid) { + init(); + sspine_skeleton skeleton = create_skeleton(); + T(sspine_iktarget_valid(sspine_iktarget_by_index(skeleton, 0))); + T(sspine_iktarget_valid(sspine_iktarget_by_index(skeleton, 6))); + T(!sspine_iktarget_valid(sspine_iktarget_by_index(skeleton, -1))); + T(!sspine_iktarget_valid(sspine_iktarget_by_index(skeleton, 7))); + sspine_destroy_skeleton(skeleton); + T(!sspine_iktarget_valid(sspine_iktarget_by_index(skeleton, 0))); + shutdown(); +} + +UTEST(sokol_spine, iktarget_equal) { + init(); + T(sspine_iktarget_equal((sspine_iktarget){ 1, 2 }, (sspine_iktarget){ 1, 2 })); + T(!sspine_iktarget_equal((sspine_iktarget){ 2, 2 }, (sspine_iktarget){ 1, 2 })); + T(!sspine_iktarget_equal((sspine_iktarget){ 1, 3 }, (sspine_iktarget){ 1, 2 })); + shutdown(); +} + +UTEST(sokol_spine, num_iktargets) { + init(); + sspine_skeleton skeleton = create_skeleton(); + T(sspine_num_iktargets(skeleton) == 7); + sspine_destroy_skeleton(skeleton); + T(sspine_num_iktargets(skeleton) == 0); + shutdown(); +} + +UTEST(sokol_spine, get_iktarget_info) { + init(); + sspine_skeleton skeleton = create_skeleton(); + const sspine_iktarget_info info = sspine_get_iktarget_info(sspine_iktarget_by_index(skeleton, 1)); + T(info.valid); + T(1 == info.index); + T(0 == strcmp(info.name.cstr, "aim-torso-ik")); + T((info.target_bone.skeleton_id == skeleton.id) && (info.target_bone.index == 2)); + shutdown(); +} + +UTEST(sokol_spine, get_iktarget_info_destroyed_skeleton) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_destroy_skeleton(skeleton); + const sspine_iktarget_info info = sspine_get_iktarget_info(sspine_iktarget_by_index(skeleton, 1)); + T(!info.valid); + T(!info.name.valid); + shutdown(); +} + +UTEST(sokol_spine, get_iktarget_info_out_of_bounds) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_destroy_skeleton(skeleton); + const sspine_iktarget_info info0 = sspine_get_iktarget_info(sspine_iktarget_by_index(skeleton, -1)); + T(!info0.name.valid); + const sspine_iktarget_info info1 = sspine_get_iktarget_info(sspine_iktarget_by_index(skeleton, 7)); + T(!info1.name.valid); + shutdown(); +} + +UTEST(sokol_spine, skin_by_name) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_skin s0 = sspine_skin_by_name(skeleton, "default"); + T((s0.skeleton_id == skeleton.id) && (s0.index == 0)); + sspine_skin s1 = sspine_skin_by_name(skeleton, "bla"); + T((s1.skeleton_id == 0) && (s1.index == 0)); + sspine_destroy_skeleton(skeleton); + sspine_skin s2 = sspine_skin_by_name(skeleton, "default"); + T((s2.skeleton_id == 0) && (s2.index == 0)); + shutdown(); +} + +UTEST(sokol_spine, skin_valid) { + init(); + sspine_skeleton skeleton = create_skeleton(); + T(sspine_skin_valid(sspine_skin_by_index(skeleton, 0))); + T(!sspine_skin_valid(sspine_skin_by_index(skeleton, -1))); + T(!sspine_skin_valid(sspine_skin_by_index(skeleton, 1))); + sspine_destroy_skeleton(skeleton); + T(!sspine_skin_valid(sspine_skin_by_index(skeleton, 0))); + shutdown(); +} + +UTEST(sokol_spine, skin_equal) { + init(); + T(sspine_skin_equal((sspine_skin){ 1, 2 }, (sspine_skin){ 1, 2 })); + T(!sspine_skin_equal((sspine_skin){ 2, 2 }, (sspine_skin){ 1, 2 })); + T(!sspine_skin_equal((sspine_skin){ 1, 3 }, (sspine_skin){ 1, 2 })); + shutdown(); +} + +UTEST(sokol_spine, num_skins) { + init(); + sspine_skeleton skeleton = create_skeleton(); + T(sspine_num_skins(skeleton) == 1); + sspine_destroy_skeleton(skeleton); + T(sspine_num_skins(skeleton) == 0); + shutdown(); +} + +UTEST(sokol_spine, get_skin_info) { + init(); + sspine_skeleton skeleton = create_skeleton(); + const sspine_skin_info info = sspine_get_skin_info(sspine_skin_by_index(skeleton, 0)); + T(info.valid); + T(0 == info.index); + T(0 == strcmp(info.name.cstr, "default")); + shutdown(); +} + +UTEST(sokol_spine, get_skin_info_destroyed_skeleton) { + init(); + sspine_skeleton skeleton = create_skeleton(); + sspine_destroy_skeleton(skeleton); + const sspine_skin_info info = sspine_get_skin_info(sspine_skin_by_index(skeleton, 0)); + T(!info.valid); + T(!info.name.valid); + shutdown(); +} diff --git a/thirdparty/sokol/tests/functional/sokol_test.c b/thirdparty/sokol/tests/functional/sokol_test.c new file mode 100644 index 0000000..29528b6 --- /dev/null +++ b/thirdparty/sokol/tests/functional/sokol_test.c @@ -0,0 +1,7 @@ +//------------------------------------------------------------------------------ +// sokol-test.c +// Sokol headers main test source. +//------------------------------------------------------------------------------ +#include "utest.h" + +UTEST_MAIN(); diff --git a/thirdparty/sokol/tests/functional/utest.h b/thirdparty/sokol/tests/functional/utest.h new file mode 100644 index 0000000..0fe3023 --- /dev/null +++ b/thirdparty/sokol/tests/functional/utest.h @@ -0,0 +1,859 @@ +/* + The latest version of this library is available on GitHub; + https://github.com/sheredom/utest.h +*/ + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ + +#ifndef SHEREDOM_UTEST_H_INCLUDED +#define SHEREDOM_UTEST_H_INCLUDED + +#ifdef _MSC_VER +/* + Disable warning about not inlining 'inline' functions. + TODO: We'll fix this later by not using fprintf within our macros, and + instead use snprintf to a realloc'ed buffer. +*/ +#pragma warning(disable : 4710) + +/* + Disable warning about inlining functions that are not marked 'inline'. + TODO: add a UTEST_NOINLINE onto the macro generated functions to fix this. +*/ +#pragma warning(disable : 4711) +#pragma warning(push, 1) +#endif + +#if defined(_MSC_VER) +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +#include +#endif + +#include +#include +#include +#include + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#if defined(_MSC_VER) +#if defined(_M_IX86) +#define _X86_ +#endif + +#if defined(_M_AMD64) +#define _AMD64_ +#endif + +#pragma warning(push, 1) +#include +#include +#pragma warning(pop) + +#elif defined(__linux__) + +/* + slightly obscure include here - we need to include glibc's features.h, but + we don't want to just include a header that might not be defined for other + c libraries like musl. Instead we include limits.h, which we know on all + glibc distributions includes features.h +*/ +#include + +#if defined(__GLIBC__) && defined(__GLIBC_MINOR__) +#include + +#if ((2 < __GLIBC__) || ((2 == __GLIBC__) && (17 <= __GLIBC_MINOR__))) +/* glibc is version 2.17 or above, so we can just use clock_gettime */ +#define UTEST_USE_CLOCKGETTIME +#else +#include +#include +#endif +#endif + +#elif defined(__APPLE__) +#include +#endif + +#if defined(_MSC_VER) +#define UTEST_PRId64 "I64d" +#define UTEST_PRIu64 "I64u" +#define UTEST_INLINE __forceinline + +#pragma section(".CRT$XCU", read) +#define UTEST_INITIALIZER(f) \ + static void __cdecl f(void); \ + __declspec(allocate(".CRT$XCU")) void(__cdecl * f##_)(void) = f; \ + static void __cdecl f(void) +#else +#if defined(__linux__) +#if defined(__clang__) +#if __has_warning("-Wreserved-id-macro") +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif +#endif + +#define __STDC_FORMAT_MACROS 1 + +#if defined(__clang__) +#if __has_warning("-Wreserved-id-macro") +#pragma clang diagnostic pop +#endif +#endif +#endif + +#include + +#define UTEST_PRId64 PRId64 +#define UTEST_PRIu64 PRIu64 +#define UTEST_INLINE inline + +#define UTEST_INITIALIZER(f) \ + static void f(void) __attribute__((constructor)); \ + static void f(void) +#endif + +#if defined(__cplusplus) +#define UTEST_CAST(type, x) static_cast(x) +#define UTEST_PTR_CAST(type, x) reinterpret_cast(x) +#define UTEST_EXTERN extern "C" +#else +#define UTEST_CAST(type, x) ((type)x) +#define UTEST_PTR_CAST(type, x) ((type)x) +#define UTEST_EXTERN extern +#endif + +#ifdef _MSC_VER +/* + io.h contains definitions for some structures with natural padding. This is + uninteresting, but for some reason MSVC's behaviour is to warn about + including this system header. That *is* interesting +*/ +#pragma warning(disable : 4820) +#pragma warning(push, 1) +#include +#pragma warning(pop) +#define UTEST_COLOUR_OUTPUT() (_isatty(_fileno(stdout))) +#else +#include +#define UTEST_COLOUR_OUTPUT() (isatty(STDOUT_FILENO)) +#endif + +static UTEST_INLINE int64_t utest_ns(void) { +#ifdef _MSC_VER + LARGE_INTEGER counter; + LARGE_INTEGER frequency; + QueryPerformanceCounter(&counter); + QueryPerformanceFrequency(&frequency); + return UTEST_CAST(int64_t, + (counter.QuadPart * 1000000000) / frequency.QuadPart); +#elif defined(__linux) + struct timespec ts; + const clockid_t cid = CLOCK_REALTIME; +#if defined(UTEST_USE_CLOCKGETTIME) + clock_gettime(cid, &ts); +#else + syscall(SYS_clock_gettime, cid, &ts); +#endif + return UTEST_CAST(int64_t, ts.tv_sec) * 1000 * 1000 * 1000 + ts.tv_nsec; +#elif __APPLE__ + return UTEST_CAST(int64_t, mach_absolute_time()); +#else + /* hack to prevent warning on unsupported platforms */ + return 1; +#endif +} + +typedef void (*utest_testcase_t)(int *, size_t); + +struct utest_test_state_s { + utest_testcase_t func; + size_t index; + char *name; +}; + +struct utest_state_s { + struct utest_test_state_s *tests; + size_t tests_length; + FILE *output; +}; + +/* extern to the global state utest needs to execute */ +UTEST_EXTERN struct utest_state_s utest_state; + +#if defined(_MSC_VER) +#define UTEST_WEAK __forceinline +#else +#define UTEST_WEAK __attribute__((weak)) +#endif + +#if defined(_MSC_VER) +#define UTEST_UNUSED +#else +#define UTEST_UNUSED __attribute__((unused)) +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wvariadic-macros" +#endif +#define UTEST_PRINTF(...) \ + if (utest_state.output) { \ + fprintf(utest_state.output, __VA_ARGS__); \ + } \ + printf(__VA_ARGS__) +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#ifdef _MSC_VER +#define UTEST_SNPRINTF(BUFFER, N, ...) _snprintf_s(BUFFER, N, N, __VA_ARGS__) +#else +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wvariadic-macros" +#endif +#define UTEST_SNPRINTF(...) snprintf(__VA_ARGS__) +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#endif + +#if defined(__cplusplus) +/* if we are using c++ we can use overloaded methods (its in the language) */ +#define UTEST_OVERLOADABLE +#elif defined(__clang__) +/* otherwise, if we are using clang with c - use the overloadable attribute */ +#define UTEST_OVERLOADABLE __attribute__((overloadable)) +#endif + +#if defined(UTEST_OVERLOADABLE) +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(float f); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(float f) { + UTEST_PRINTF("%f", UTEST_CAST(double, f)); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d) { + UTEST_PRINTF("%f", d); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long double d); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long double d) { + UTEST_PRINTF("%Lf", d); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i) { + UTEST_PRINTF("%d", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned int i) { + UTEST_PRINTF("%u", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long int i) { + UTEST_PRINTF("%ld", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long unsigned int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long unsigned int i) { + UTEST_PRINTF("%lu", i); +} + +/* + long long is a c++11 extension + TODO: grok for c++11 version here +*/ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long int i) { + UTEST_PRINTF("%lld", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long unsigned int i); +UTEST_WEAK UTEST_OVERLOADABLE void +utest_type_printer(long long unsigned int i) { + UTEST_PRINTF("%llu", i); +} +#endif +#else +/* + we don't have the ability to print the values we got, so we create a macro + to tell our users we can't do anything fancy +*/ +#define utest_type_printer(...) UTEST_PRINTF("undef") +#endif + +#if defined(__clang__) +#define UTEST_EXPECT(x, y, cond) \ + { \ + _Pragma("clang diagnostic push") _Pragma( \ + "clang diagnostic ignored \"-Wlanguage-extension-token\"") typeof(y) \ + xEval = (x); \ + typeof(y) yEval = (y); \ + _Pragma("clang diagnostic pop") if (!((xEval)cond(yEval))) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : "); \ + utest_type_printer(xEval); \ + UTEST_PRINTF("\n"); \ + UTEST_PRINTF(" Actual : "); \ + utest_type_printer(yEval); \ + UTEST_PRINTF("\n"); \ + *utest_result = 1; \ + } \ + } +#elif defined(__GNUC__) +#define UTEST_EXPECT(x, y, cond) \ + { \ + typeof(y) xEval = (x); \ + typeof(y) yEval = (y); \ + if (!((xEval)cond(yEval))) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : "); \ + utest_type_printer(xEval); \ + UTEST_PRINTF("\n"); \ + UTEST_PRINTF(" Actual : "); \ + utest_type_printer(yEval); \ + UTEST_PRINTF("\n"); \ + *utest_result = 1; \ + } \ + } +#else +#define UTEST_EXPECT(x, y, cond) \ + { \ + if (!((x)cond(y))) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + *utest_result = 1; \ + } \ + } +#endif + +#define EXPECT_TRUE(x) \ + if (!(x)) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : true\n"); \ + UTEST_PRINTF(" Actual : %s\n", (x) ? "true" : "false"); \ + *utest_result = 1; \ + } + +#define EXPECT_FALSE(x) \ + if (x) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : false\n"); \ + UTEST_PRINTF(" Actual : %s\n", (x) ? "true" : "false"); \ + *utest_result = 1; \ + } + +#define EXPECT_EQ(x, y) UTEST_EXPECT(x, y, ==) +#define EXPECT_NE(x, y) UTEST_EXPECT(x, y, !=) +#define EXPECT_LT(x, y) UTEST_EXPECT(x, y, <) +#define EXPECT_LE(x, y) UTEST_EXPECT(x, y, <=) +#define EXPECT_GT(x, y) UTEST_EXPECT(x, y, >) +#define EXPECT_GE(x, y) UTEST_EXPECT(x, y, >=) + +#define EXPECT_STREQ(x, y) \ + if (0 != strcmp(x, y)) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%s\"\n", x); \ + UTEST_PRINTF(" Actual : \"%s\"\n", y); \ + *utest_result = 1; \ + } + +#define EXPECT_STRNE(x, y) \ + if (0 == strcmp(x, y)) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%s\"\n", x); \ + UTEST_PRINTF(" Actual : \"%s\"\n", y); \ + *utest_result = 1; \ + } + +#if defined(__clang__) +#define UTEST_ASSERT(x, y, cond) \ + { \ + _Pragma("clang diagnostic push") _Pragma( \ + "clang diagnostic ignored \"-Wlanguage-extension-token\"") typeof(y) \ + xEval = (x); \ + typeof(y) yEval = (y); \ + _Pragma("clang diagnostic pop") if (!((xEval)cond(yEval))) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : "); \ + utest_type_printer(xEval); \ + UTEST_PRINTF("\n"); \ + UTEST_PRINTF(" Actual : "); \ + utest_type_printer(yEval); \ + UTEST_PRINTF("\n"); \ + *utest_result = 1; \ + return; \ + } \ + } +#elif defined(__GNUC__) +#define UTEST_ASSERT(x, y, cond) \ + { \ + typeof(y) xEval = (x); \ + typeof(y) yEval = (y); \ + if (!((xEval)cond(yEval))) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : "); \ + utest_type_printer(xEval); \ + UTEST_PRINTF("\n"); \ + UTEST_PRINTF(" Actual : "); \ + utest_type_printer(yEval); \ + UTEST_PRINTF("\n"); \ + *utest_result = 1; \ + return; \ + } \ + } +#else +#define UTEST_ASSERT(x, y, cond) \ + { \ + if (!((x)cond(y))) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + *utest_result = 1; \ + return; \ + } \ + } +#endif + +#define ASSERT_TRUE(x) \ + if (!(x)) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : true\n"); \ + UTEST_PRINTF(" Actual : %s\n", (x) ? "true" : "false"); \ + *utest_result = 1; \ + return; \ + } + +#define ASSERT_FALSE(x) \ + if (x) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : false\n"); \ + UTEST_PRINTF(" Actual : %s\n", (x) ? "true" : "false"); \ + *utest_result = 1; \ + return; \ + } + +#define ASSERT_EQ(x, y) UTEST_ASSERT(x, y, ==) +#define ASSERT_NE(x, y) UTEST_ASSERT(x, y, !=) +#define ASSERT_LT(x, y) UTEST_ASSERT(x, y, <) +#define ASSERT_LE(x, y) UTEST_ASSERT(x, y, <=) +#define ASSERT_GT(x, y) UTEST_ASSERT(x, y, >) +#define ASSERT_GE(x, y) UTEST_ASSERT(x, y, >=) + +#define ASSERT_STREQ(x, y) \ + EXPECT_STREQ(x, y); \ + if (0 != strcmp(x, y)) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%s\"\n", x); \ + UTEST_PRINTF(" Actual : \"%s\"\n", y); \ + *utest_result = 1; \ + return; \ + } + +#define ASSERT_STRNE(x, y) \ + EXPECT_STRNE(x, y); \ + if (0 == strcmp(x, y)) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%s\"\n", x); \ + UTEST_PRINTF(" Actual : \"%s\"\n", y); \ + *utest_result = 1; \ + return; \ + } + +#define UTEST(SET, NAME) \ + UTEST_EXTERN struct utest_state_s utest_state; \ + static void utest_run_##SET##_##NAME(int *utest_result); \ + static void utest_##SET##_##NAME(int *utest_result, size_t utest_index) { \ + (void)utest_index; \ + utest_run_##SET##_##NAME(utest_result); \ + } \ + UTEST_INITIALIZER(utest_register_##SET##_##NAME) { \ + const size_t index = utest_state.tests_length++; \ + const char *name_part = #SET "." #NAME; \ + const size_t name_size = strlen(name_part) + 1; \ + char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ + utest_state.tests = \ + UTEST_PTR_CAST(struct utest_test_state_s *, \ + realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ + sizeof(struct utest_test_state_s) * \ + utest_state.tests_length)); \ + utest_state.tests[index].func = &utest_##SET##_##NAME; \ + utest_state.tests[index].name = name; \ + UTEST_SNPRINTF(name, name_size, "%s", name_part); \ + } \ + void utest_run_##SET##_##NAME(int *utest_result) + +#define UTEST_F_SETUP(FIXTURE) \ + static void utest_f_setup_##FIXTURE(int *utest_result, \ + struct FIXTURE *utest_fixture) + +#define UTEST_F_TEARDOWN(FIXTURE) \ + static void utest_f_teardown_##FIXTURE(int *utest_result, \ + struct FIXTURE *utest_fixture) + +#define UTEST_F(FIXTURE, NAME) \ + UTEST_EXTERN struct utest_state_s utest_state; \ + static void utest_f_setup_##FIXTURE(int *, struct FIXTURE *); \ + static void utest_f_teardown_##FIXTURE(int *, struct FIXTURE *); \ + static void utest_run_##FIXTURE##_##NAME(int *, struct FIXTURE *); \ + static void utest_f_##FIXTURE##_##NAME(int *utest_result, \ + size_t utest_index) { \ + struct FIXTURE fixture; \ + (void)utest_index; \ + memset(&fixture, 0, sizeof(fixture)); \ + utest_f_setup_##FIXTURE(utest_result, &fixture); \ + if (0 != *utest_result) { \ + return; \ + } \ + utest_run_##FIXTURE##_##NAME(utest_result, &fixture); \ + utest_f_teardown_##FIXTURE(utest_result, &fixture); \ + } \ + UTEST_INITIALIZER(utest_register_##FIXTURE##_##NAME) { \ + const size_t index = utest_state.tests_length++; \ + const char *name_part = #FIXTURE "." #NAME; \ + const size_t name_size = strlen(name_part) + 1; \ + char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ + utest_state.tests = \ + UTEST_PTR_CAST(struct utest_test_state_s *, \ + realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ + sizeof(struct utest_test_state_s) * \ + utest_state.tests_length)); \ + utest_state.tests[index].func = &utest_f_##FIXTURE##_##NAME; \ + utest_state.tests[index].name = name; \ + UTEST_SNPRINTF(name, name_size, "%s", name_part); \ + } \ + void utest_run_##FIXTURE##_##NAME(int *utest_result, \ + struct FIXTURE *utest_fixture) + +#define UTEST_I_SETUP(FIXTURE) \ + static void utest_i_setup_##FIXTURE( \ + int *utest_result, struct FIXTURE *utest_fixture, size_t utest_index) + +#define UTEST_I_TEARDOWN(FIXTURE) \ + static void utest_i_teardown_##FIXTURE( \ + int *utest_result, struct FIXTURE *utest_fixture, size_t utest_index) + +#define UTEST_I(FIXTURE, NAME, INDEX) \ + UTEST_EXTERN struct utest_state_s utest_state; \ + static void utest_run_##FIXTURE##_##NAME##_##INDEX(int *, struct FIXTURE *); \ + static void utest_i_##FIXTURE##_##NAME##_##INDEX(int *utest_result, \ + size_t index) { \ + struct FIXTURE fixture; \ + memset(&fixture, 0, sizeof(fixture)); \ + utest_i_setup_##FIXTURE(utest_result, &fixture, index); \ + if (0 != *utest_result) { \ + return; \ + } \ + utest_run_##FIXTURE##_##NAME##_##INDEX(utest_result, &fixture); \ + utest_i_teardown_##FIXTURE(utest_result, &fixture, index); \ + } \ + UTEST_INITIALIZER(utest_register_##FIXTURE##_##NAME##_##INDEX) { \ + size_t i; \ + uint64_t iUp; \ + for (i = 0; i < (INDEX); i++) { \ + const size_t index = utest_state.tests_length++; \ + const char *name_part = #FIXTURE "." #NAME; \ + const size_t name_size = strlen(name_part) + 32; \ + char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ + utest_state.tests = \ + UTEST_PTR_CAST(struct utest_test_state_s *, \ + realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ + sizeof(struct utest_test_state_s) * \ + utest_state.tests_length)); \ + utest_state.tests[index].func = &utest_i_##FIXTURE##_##NAME##_##INDEX; \ + utest_state.tests[index].index = i; \ + utest_state.tests[index].name = name; \ + iUp = UTEST_CAST(uint64_t, i); \ + UTEST_SNPRINTF(name, name_size, "%s/%" UTEST_PRIu64, name_part, iUp); \ + } \ + } \ + void utest_run_##FIXTURE##_##NAME##_##INDEX(int *utest_result, \ + struct FIXTURE *utest_fixture) + +UTEST_WEAK +int utest_should_filter_test(const char *filter, const char *testcase); +UTEST_WEAK int utest_should_filter_test(const char *filter, + const char *testcase) { + if (filter) { + const char *filter_cur = filter; + const char *testcase_cur = testcase; + const char *filter_wildcard = 0; + + while (('\0' != *filter_cur) && ('\0' != *testcase_cur)) { + if ('*' == *filter_cur) { + /* store the position of the wildcard */ + filter_wildcard = filter_cur; + + /* skip the wildcard character */ + filter_cur++; + + while (('\0' != *filter_cur) && ('\0' != *testcase_cur)) { + if ('*' == *filter_cur) { + /* + we found another wildcard (filter is something like *foo*) so we + exit the current loop, and return to the parent loop to handle + the wildcard case + */ + break; + } else if (*filter_cur != *testcase_cur) { + /* otherwise our filter didn't match, so reset it */ + filter_cur = filter_wildcard; + } + + /* move testcase along */ + testcase_cur++; + + /* move filter along */ + filter_cur++; + } + + if (('\0' == *filter_cur) && ('\0' == *testcase_cur)) { + return 0; + } + + /* if the testcase has been exhausted, we don't have a match! */ + if ('\0' == *testcase_cur) { + return 1; + } + } else { + if (*testcase_cur != *filter_cur) { + /* test case doesn't match filter */ + return 1; + } else { + /* move our filter and testcase forward */ + testcase_cur++; + filter_cur++; + } + } + } + + if (('\0' != *filter_cur) || + (('\0' != *testcase_cur) && + ((filter == filter_cur) || ('*' != filter_cur[-1])))) { + /* we have a mismatch! */ + return 1; + } + } + + return 0; +} + +static UTEST_INLINE int utest_strncmp(const char *a, const char *b, size_t n) { + /* strncmp breaks on Wall / Werror on gcc/clang, so we avoid using it */ + unsigned i; + + for (i = 0; i < n; i++) { + if (a[i] < b[i]) { + return -1; + } else if (a[i] > b[i]) { + return 1; + } + } + + return 0; +} + +static UTEST_INLINE FILE *utest_fopen(const char *filename, const char *mode) { +#ifdef _MSC_VER + FILE *file; + if (0 == fopen_s(&file, filename, mode)) { + return file; + } else { + return 0; + } +#else + return fopen(filename, mode); +#endif +} + +UTEST_WEAK int utest_main(int argc, const char *const argv[]); +UTEST_WEAK int utest_main(int argc, const char *const argv[]) { + uint64_t failed = 0; + size_t index = 0; + size_t *failed_testcases = 0; + size_t failed_testcases_length = 0; + const char *filter = 0; + uint64_t ran_tests = 0; + + enum colours { RESET, GREEN, RED }; + + const int use_colours = UTEST_COLOUR_OUTPUT(); + const char *colours[] = {"\033[0m", "\033[32m", "\033[31m"}; + if (!use_colours) { + for (index = 0; index < sizeof colours / sizeof colours[0]; index++) { + colours[index] = ""; + } + } + /* loop through all arguments looking for our options */ + for (index = 1; index < UTEST_CAST(size_t, argc); index++) { + const char help_str[] = "--help"; + const char filter_str[] = "--filter="; + const char output_str[] = "--output="; + + if (0 == utest_strncmp(argv[index], help_str, strlen(help_str))) { + printf("utest.h - the single file unit testing solution for C/C++!\n" + "Command line Options:\n" + " --help Show this message and exit.\n" + " --filter= Filter the test cases to run (EG. MyTest*.a " + "would run MyTestCase.a but not MyTestCase.b).\n" + " --output= Output an xunit XML file to the file " + "specified in .\n"); + goto cleanup; + } else if (0 == + utest_strncmp(argv[index], filter_str, strlen(filter_str))) { + /* user wants to filter what test cases run! */ + filter = argv[index] + strlen(filter_str); + } else if (0 == + utest_strncmp(argv[index], output_str, strlen(output_str))) { + utest_state.output = utest_fopen(argv[index] + strlen(output_str), "w+"); + } + } + + for (index = 0; index < utest_state.tests_length; index++) { + if (utest_should_filter_test(filter, utest_state.tests[index].name)) { + continue; + } + + ran_tests++; + } + + printf("%s[==========]%s Running %" UTEST_PRIu64 " test cases.\n", + colours[GREEN], colours[RESET], UTEST_CAST(uint64_t, ran_tests)); + + if (utest_state.output) { + fprintf(utest_state.output, "\n"); + fprintf(utest_state.output, + "\n", + UTEST_CAST(uint64_t, ran_tests)); + fprintf(utest_state.output, + "\n", + UTEST_CAST(uint64_t, ran_tests)); + } + + for (index = 0; index < utest_state.tests_length; index++) { + int result = 0; + int64_t ns = 0; + + if (utest_should_filter_test(filter, utest_state.tests[index].name)) { + continue; + } + + printf("%s[ RUN ]%s %s\n", colours[GREEN], colours[RESET], + utest_state.tests[index].name); + + if (utest_state.output) { + fprintf(utest_state.output, "", + utest_state.tests[index].name); + } + + ns = utest_ns(); + utest_state.tests[index].func(&result, utest_state.tests[index].index); + ns = utest_ns() - ns; + + if (utest_state.output) { + fprintf(utest_state.output, "\n"); + } + + if (0 != result) { + const size_t failed_testcase_index = failed_testcases_length++; + failed_testcases = UTEST_PTR_CAST( + size_t *, realloc(UTEST_PTR_CAST(void *, failed_testcases), + sizeof(size_t) * failed_testcases_length)); + failed_testcases[failed_testcase_index] = index; + failed++; + printf("%s[ FAILED ]%s %s (%" UTEST_PRId64 "ns)\n", colours[RED], + colours[RESET], utest_state.tests[index].name, ns); + } else { + printf("%s[ OK ]%s %s (%" UTEST_PRId64 "ns)\n", colours[GREEN], + colours[RESET], utest_state.tests[index].name, ns); + } + } + + printf("%s[==========]%s %" UTEST_PRIu64 " test cases ran.\n", colours[GREEN], + colours[RESET], ran_tests); + printf("%s[ PASSED ]%s %" UTEST_PRIu64 " tests.\n", colours[GREEN], + colours[RESET], ran_tests - failed); + + if (0 != failed) { + printf("%s[ FAILED ]%s %" UTEST_PRIu64 " tests, listed below:\n", + colours[RED], colours[RESET], failed); + for (index = 0; index < failed_testcases_length; index++) { + printf("%s[ FAILED ]%s %s\n", colours[RED], colours[RESET], + utest_state.tests[failed_testcases[index]].name); + } + } + + if (utest_state.output) { + fprintf(utest_state.output, "\n\n"); + } + +cleanup: + for (index = 0; index < utest_state.tests_length; index++) { + free(UTEST_PTR_CAST(void *, utest_state.tests[index].name)); + } + + free(UTEST_PTR_CAST(void *, failed_testcases)); + free(UTEST_PTR_CAST(void *, utest_state.tests)); + + if (utest_state.output) { + fclose(utest_state.output); + } + + return UTEST_CAST(int, failed); +} + +/* + we need, in exactly one source file, define the global struct that will hold + the data we need to run utest. This macro allows the user to declare the + data without having to use the UTEST_MAIN macro, thus allowing them to write + their own main() function. +*/ +#define UTEST_STATE() struct utest_state_s utest_state = {0, 0, 0} + +/* + define a main() function to call into utest.h and start executing tests! A + user can optionally not use this macro, and instead define their own main() + function and manually call utest_main. The user must, in exactly one source + file, use the UTEST_STATE macro to declare a global struct variable that + utest requires. +*/ +#define UTEST_MAIN() \ + UTEST_STATE(); \ + int main(int argc, const char *const argv[]) { \ + return utest_main(argc, argv); \ + } + +#endif /* SHEREDOM_UTEST_H_INCLUDED */ diff --git a/thirdparty/sokol/tests/test_android.sh b/thirdparty/sokol/tests/test_android.sh new file mode 100644 index 0000000..88fb1c9 --- /dev/null +++ b/thirdparty/sokol/tests/test_android.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -e +source test_common.sh +setup_android +build android_debug android_debug +build android_release android_release +build android_sles_debug android_sles_debug +build android_sles_release android_sles_release diff --git a/thirdparty/sokol/tests/test_common.sh b/thirdparty/sokol/tests/test_common.sh new file mode 100644 index 0000000..43f056b --- /dev/null +++ b/thirdparty/sokol/tests/test_common.sh @@ -0,0 +1,50 @@ +setup_emsdk() { + if [ ! -d "build/emsdk" ] ; then + mkdir -p build && cd build + git clone https://github.com/emscripten-core/emsdk.git + cd emsdk + ./emsdk install latest + ./emsdk activate latest + cd ../.. + fi + source build/emsdk/emsdk_env.sh +} + +setup_android() { + if [ ! -d "build/android_sdk" ] ; then + mkdir -p build/android_sdk && cd build/android_sdk + sdk_file="sdk-tools-linux-3859397.zip" + wget --no-verbose https://dl.google.com/android/repository/$sdk_file + unzip -q $sdk_file + cd tools/bin + yes | ./sdkmanager "platforms;android-28" >/dev/null + yes | ./sdkmanager "build-tools;29.0.3" >/dev/null + yes | ./sdkmanager "platform-tools" >/dev/null + yes | ./sdkmanager "ndk-bundle" >/dev/null + cd ../../../.. + fi +} + +build() { + gen_preset=$1 + build_preset=$2 + cmake --preset $gen_preset + cmake --build --preset $build_preset +} + +analyze() { + cfg=$1 + backend=$2 + mode=$3 + mkdir -p build/$cfg && cd build/$cfg + cmake -GNinja -DSOKOL_BACKEND=$backend -DCMAKE_BUILD_TYPE=$mode -DUSE_ANALYZER=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ ../.. + cmake --build . + cd ../.. +} + +runtest() { + cfg=$1 + cd build/$cfg + ./sokol-test + cd ../../.. +} diff --git a/thirdparty/sokol/tests/test_emscripten.sh b/thirdparty/sokol/tests/test_emscripten.sh new file mode 100644 index 0000000..aff4b08 --- /dev/null +++ b/thirdparty/sokol/tests/test_emscripten.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -e +source test_common.sh +setup_emsdk +build emsc_webgl2_debug emsc_webgl2_debug +build emsc_webgl2_release emsc_webgl2_release +build emsc_wgpu_debug emsc_wgpu_debug +build emsc_wgpu_release emsc_wgpu_release diff --git a/thirdparty/sokol/tests/test_ios.sh b/thirdparty/sokol/tests/test_ios.sh new file mode 100644 index 0000000..39e8868 --- /dev/null +++ b/thirdparty/sokol/tests/test_ios.sh @@ -0,0 +1,10 @@ +set -e +source test_common.sh +build ios_gl ios_gl_debug +build ios_gl ios_gl_release +build ios_metal ios_metal_debug +build ios_metal ios_metal_release +build ios_arc_gl ios_arc_gl_debug +build ios_arc_gl ios_arc_gl_release +build ios_arc_metal ios_arc_metal_debug +build ios_arc_metal ios_arc_metal_release diff --git a/thirdparty/sokol/tests/test_linux.sh b/thirdparty/sokol/tests/test_linux.sh new file mode 100644 index 0000000..ae35e8d --- /dev/null +++ b/thirdparty/sokol/tests/test_linux.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -e +source test_common.sh +build linux_gl_debug linux_gl_debug +build linux_gl_release linux_gl_release +build linux_gles3_debug linux_gles3_debug +build linux_gles3_release linux_gles3_release +build linux_gl_egl_debug linux_gl_egl_debug +build linux_gl_egl_release linux_gl_egl_release +runtest linux_gl_debug diff --git a/thirdparty/sokol/tests/test_macos.sh b/thirdparty/sokol/tests/test_macos.sh new file mode 100644 index 0000000..0c0299a --- /dev/null +++ b/thirdparty/sokol/tests/test_macos.sh @@ -0,0 +1,11 @@ +set -e +source test_common.sh +build macos_gl_debug macos_gl_debug +build macos_gl_release macos_gl_release +build macos_metal_debug macos_metal_debug +build macos_metal_release macos_metal_release +build macos_arc_gl_debug macos_arc_gl_debug +build macos_arc_gl_release macos_arc_gl_release +build macos_arc_metal_debug macos_arc_metal_debug +build macos_arc_metal_release macos_arc_metal_release +runtest macos_gl_debug diff --git a/thirdparty/sokol/tests/test_win.cmd b/thirdparty/sokol/tests/test_win.cmd new file mode 100644 index 0000000..d4c16a7 --- /dev/null +++ b/thirdparty/sokol/tests/test_win.cmd @@ -0,0 +1,11 @@ +cmake --preset win_gl || exit /b 10 +cmake --build --preset win_gl_debug || exit /b 10 +cmake --build --preset win_gl_release || exit /b 10 + +cmake --preset win_d3d11 || exit /b 10 +cmake --build --preset win_d3d11_debug || exit /b 10 +cmake --build --preset win_d3d11_release || exit /b 10 + +cd build\win_d3d11\Debug +sokol-test.exe || exit /b 10 +cd ..\..\.. diff --git a/thirdparty/sokol/util/gen_sokol_color.py b/thirdparty/sokol/util/gen_sokol_color.py new file mode 100644 index 0000000..f19a2ba --- /dev/null +++ b/thirdparty/sokol/util/gen_sokol_color.py @@ -0,0 +1,500 @@ +#------------------------------------------------------------------------------- +# Generate the sokol_color.h header from a predefined palette +#------------------------------------------------------------------------------- +# LICENSE +# ======= +# +# zlib/libpng license +# +# Copyright (c) 2020 Stuart Adams +# +# This software is provided 'as-is', without any express or implied warranty. +# In no event will the authors be held liable for any damages arising from the +# use of this software. +# +# Permission is granted to anyone to use this software for any purpose, +# including commercial applications, and to alter it and redistribute it +# freely, subject to the following restrictions: +# +# 1. The origin of this software must not be misrepresented; you must not +# claim that you wrote the original software. If you use this software in a +# product, an acknowledgment in the product documentation would be +# appreciated but is not required. +# +# 2. Altered source versions must be plainly marked as such, and must not +# be misrepresented as being the original software. +# +# 3. This notice may not be removed or altered from any source +# distribution. + +colors = [ +("Alice Blue", 0xF0F8FFFF), +("Antique White", 0xFAEBD7FF), +("Aqua", 0x00FFFFFF), +("Aquamarine", 0x7FFFD4FF), +("Azure", 0xF0FFFFFF), +("Beige", 0xF5F5DCFF), +("Bisque", 0xFFE4C4FF), +("Black", 0x000000FF), +("Blanched Almond", 0xFFEBCDFF), +("Blue", 0x0000FFFF), +("Blue Violet", 0x8A2BE2FF), +("Brown", 0xA52A2AFF), +("Burlywood", 0xDEB887FF), +("Cadet Blue", 0x5F9EA0FF), +("Chartreuse", 0x7FFF00FF), +("Chocolate", 0xD2691EFF), +("Coral", 0xFF7F50FF), +("Cornflower Blue", 0x6495EDFF), +("Cornsilk", 0xFFF8DCFF), +("Crimson", 0xDC143CFF), +("Cyan", 0x00FFFFFF), +("Dark Blue", 0x00008BFF), +("Dark Cyan", 0x008B8BFF), +("Dark Goldenrod", 0xB8860BFF), +("Dark Gray", 0xA9A9A9FF), +("Dark Green", 0x006400FF), +("Dark Khaki", 0xBDB76BFF), +("Dark Magenta", 0x8B008BFF), +("Dark Olive Green", 0x556B2FFF), +("Dark Orange", 0xFF8C00FF), +("Dark Orchid", 0x9932CCFF), +("Dark Red", 0x8B0000FF), +("Dark Salmon", 0xE9967AFF), +("Dark Sea Green", 0x8FBC8FFF), +("Dark Slate Blue", 0x483D8BFF), +("Dark Slate Gray", 0x2F4F4FFF), +("Dark Turquoise", 0x00CED1FF), +("Dark Violet", 0x9400D3FF), +("Deep Pink", 0xFF1493FF), +("Deep Sky Blue", 0x00BFFFFF), +("Dim Gray", 0x696969FF), +("Dodger Blue", 0x1E90FFFF), +("Firebrick", 0xB22222FF), +("Floral White", 0xFFFAF0FF), +("Forest Green", 0x228B22FF), +("Fuchsia", 0xFF00FFFF), +("Gainsboro", 0xDCDCDCFF), +("Ghost White", 0xF8F8FFFF), +("Gold", 0xFFD700FF), +("Goldenrod", 0xDAA520FF), +("Gray", 0xBEBEBEFF), +("Web Gray", 0x808080FF), +("Green", 0x00FF00FF), +("Web Green", 0x008000FF), +("Green Yellow", 0xADFF2FFF), +("Honeydew", 0xF0FFF0FF), +("Hot Pink", 0xFF69B4FF), +("Indian Red", 0xCD5C5CFF), +("Indigo", 0x4B0082FF), +("Ivory", 0xFFFFF0FF), +("Khaki", 0xF0E68CFF), +("Lavender", 0xE6E6FAFF), +("Lavender Blush", 0xFFF0F5FF), +("Lawn Green", 0x7CFC00FF), +("Lemon Chiffon", 0xFFFACDFF), +("Light Blue", 0xADD8E6FF), +("Light Coral", 0xF08080FF), +("Light Cyan", 0xE0FFFFFF), +("Light Goldenrod", 0xFAFAD2FF), +("Light Gray", 0xD3D3D3FF), +("Light Green", 0x90EE90FF), +("Light Pink", 0xFFB6C1FF), +("Light Salmon", 0xFFA07AFF), +("Light Sea Green", 0x20B2AAFF), +("Light Sky Blue", 0x87CEFAFF), +("Light Slate Gray", 0x778899FF), +("Light Steel Blue", 0xB0C4DEFF), +("Light Yellow", 0xFFFFE0FF), +("Lime", 0x00FF00FF), +("Lime Green", 0x32CD32FF), +("Linen", 0xFAF0E6FF), +("Magenta", 0xFF00FFFF), +("Maroon", 0xB03060FF), +("Web Maroon", 0x800000FF), +("Medium Aquamarine", 0x66CDAAFF), +("Medium Blue", 0x0000CDFF), +("Medium Orchid", 0xBA55D3FF), +("Medium Purple", 0x9370DBFF), +("Medium Sea Green", 0x3CB371FF), +("Medium Slate Blue", 0x7B68EEFF), +("Medium Spring Green", 0x00FA9AFF), +("Medium Turquoise", 0x48D1CCFF), +("Medium Violet Red", 0xC71585FF), +("Midnight Blue", 0x191970FF), +("Mint Cream", 0xF5FFFAFF), +("Misty Rose", 0xFFE4E1FF), +("Moccasin", 0xFFE4B5FF), +("Navajo White", 0xFFDEADFF), +("Navy Blue", 0x000080FF), +("Old Lace", 0xFDF5E6FF), +("Olive", 0x808000FF), +("Olive Drab", 0x6B8E23FF), +("Orange", 0xFFA500FF), +("Orange Red", 0xFF4500FF), +("Orchid", 0xDA70D6FF), +("Pale Goldenrod", 0xEEE8AAFF), +("Pale Green", 0x98FB98FF), +("Pale Turquoise", 0xAFEEEEFF), +("Pale Violet Red", 0xDB7093FF), +("Papaya Whip", 0xFFEFD5FF), +("Peach Puff", 0xFFDAB9FF), +("Peru", 0xCD853FFF), +("Pink", 0xFFC0CBFF), +("Plum", 0xDDA0DDFF), +("Powder Blue", 0xB0E0E6FF), +("Purple", 0xA020F0FF), +("Web Purple", 0x800080FF), +("Rebecca Purple", 0x663399FF), +("Red", 0xFF0000FF), +("Rosy Brown", 0xBC8F8FFF), +("Royal Blue", 0x4169E1FF), +("Saddle Brown", 0x8B4513FF), +("Salmon", 0xFA8072FF), +("Sandy Brown", 0xF4A460FF), +("Sea Green", 0x2E8B57FF), +("Seashell", 0xFFF5EEFF), +("Sienna", 0xA0522DFF), +("Silver", 0xC0C0C0FF), +("Sky Blue", 0x87CEEBFF), +("Slate Blue", 0x6A5ACDFF), +("Slate Gray", 0x708090FF), +("Snow", 0xFFFAFAFF), +("Spring Green", 0x00FF7FFF), +("Steel Blue", 0x4682B4FF), +("Tan", 0xD2B48CFF), +("Teal", 0x008080FF), +("Thistle", 0xD8BFD8FF), +("Tomato", 0xFF6347FF), +("Transparent", 0x00000000), +("Turquoise", 0x40E0D0FF), +("Violet", 0xEE82EEFF), +("Wheat", 0xF5DEB3FF), +("White", 0xFFFFFFFF), +("White Smoke", 0xF5F5F5FF), +("Yellow", 0xFFFF00FF), +("Yellow Green", 0x9ACD32FF) +] + +header = open("sokol_color.h", "w") + +header.write("""#if defined(SOKOL_IMPL) && !defined(SOKOL_COLOR_IMPL) +#define SOKOL_COLOR_IMPL +#endif +#ifndef SOKOL_COLOR_INCLUDED +/* + sokol_color.h -- sg_color utilities + + This header was generated by gen_sokol_color.py. Do not modify it. + + Project URL: https://github.com/floooh/sokol + + Include the following headers before including sokol_color.h: + + sokol_gfx.h + + FEATURE OVERVIEW + ================ + sokol_color.h defines preset colors based on the X11 color names, + alongside utility functions to create and modify sg_color objects. + + The predefined colors are based on the X11 color names: + + https://en.wikipedia.org/wiki/X11_color_names + + This palette is useful for prototyping - lots of programmers are familiar with + these colours due to their use in X11, web development and XNA / MonoGame. They + are also handy when you want to reference a familiar color, but don't want to + write it out by hand. + + COLORS + ====== + The palette is defined using static const (or constexpr if you are using a + C++ compiler) objects. These objects use lowercase names: + + static SOKOL_COLOR_CONSTEXPR sg_color sg_red = SG_RED; + static SOKOL_COLOR_CONSTEXPR sg_color sg_green = SG_GREEN; + static SOKOL_COLOR_CONSTEXPR sg_color sg_blue = SG_BLUE; + + An sg_color preset object like sg_red can be used to initialize + an sg_pass_action: + + sg_pass_action pass_action = { + .colors[0] = { .action=SG_ACTION_CLEAR, .value = sg_red } + }; + + Initializing an object with static storage duration is more complicated + because of C language rules. Technically, a static const is not a + compile-time constant in C. To work around this, the palette is also + defined as a series of brace-enclosed list macro definitions. These + definitions use uppercase names: + + #define SG_RED { 1.0f, 0.0f, 0.0f, 1.0f } + #define SG_GREEN { 0.0f, 1.0f, 0.0f, 1.0f } + #define SG_BLUE { 0.0f, 0.0f, 1.0f, 1.0f } + + A preset macro like SG_RED can be used to initialize objects with static + storage duration: + + static struct { + sg_pass_action pass_action; + } state = { + .pass_action = { + .colors[0] = { .action = SG_ACTION_CLEAR, .value = SG_RED } + } + }; + + A second set of macro definitions exists for colors packed as 32 bit integer + values. These definitions are also uppercase, but use the _RGBA32 suffix: + + #define SG_RED_RGBA32 0xFF0000FF + #define SG_GREEN_RGBA32 0x00FF00FF + #define SG_BLUE_RGBA32 0x0000FFFF + + This is useful if your code makes use of packed colors, as sokol_gl.h does for its + internal vertex format: + + sgl_begin_triangles(); + sgl_v2f_c1i( 0.0f, 0.5f, SG_RED_RGBA32); + sgl_v2f_c1i( 0.5f, -0.5f, SG_GREEN_RGBA32); + sgl_v2f_c1i(-0.5f, -0.5f, SG_BLUE_RGBA32); + sgl_end(); + + UTILITY FUNCTIONS + ================= + + Utility functions for creating colours are provided: + + - sg_make_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) + Create a sg_color object from separate R, G, B, A bytes. + + - sg_make_color_1i(uint32_t rgba) + Create a sg_color object from RGBA bytes packed into a 32-bit unsigned integer. + + - sg_color_lerp(const sg_color* color_a, const sg_color* color_b, float amount) + Linearly interpolate a color. + + - sg_color_lerp_precise(const sg_color* color_a, const sg_color* color_b, float amount) + Linearly interpolate a color. Less efficient but more precise than sg_color_lerp. + + - sg_color_multiply(const sg_color* color, float scale) + Multiply each color component by the scale factor. + + LICENSE + ======= + + zlib/libpng license + + Copyright (c) 2020 Stuart Adams + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_COLOR_INCLUDED (1) + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_color.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_GL_API_DECL) +#define SOKOL_COLOR_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_COLOR_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_COLOR_IMPL) +#define SOKOL_COLOR_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_COLOR_API_DECL __declspec(dllimport) +#else +#define SOKOL_COLOR_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +#define SOKOL_COLOR_CONSTEXPR constexpr +extern "C" { +#else +#define SOKOL_COLOR_CONSTEXPR const +#endif + +SOKOL_COLOR_API_DECL sg_color sg_make_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_COLOR_API_DECL sg_color sg_make_color_1i(uint32_t rgba); +SOKOL_COLOR_API_DECL sg_color sg_color_lerp(const sg_color* color_a, const sg_color* color_b, float amount); +SOKOL_COLOR_API_DECL sg_color sg_color_lerp_precise(const sg_color* color_a, const sg_color* color_b, float amount); +SOKOL_COLOR_API_DECL sg_color sg_color_multiply(const sg_color* color, float scale); + +""") + +def unpack_rgba(color): + red = (color & 0xFF000000) >> 24 + green = (color & 0xFF0000) >> 16 + blue = (color & 0xFF00) >> 8 + alpha = (color & 0xFF) + return (red, green, blue, alpha) + +def add_documentation(color): + documentation = "/* {name} color {{ R:{r}, G:{g}, B:{b}, A:{a} }} */\n" + rgba = unpack_rgba(color[1]) + header.write(documentation.format( + name = color[0], r = rgba[0], g = rgba[1], b = rgba[2], a = rgba[3])) + +for color in colors: + add_documentation(color) + init_color = "SG_" + color[0].upper().replace(" ", "_") + init_color_definition = "#define {name} {{ {r}f, {g}f, {b}f, {a}f }}\n" + rgba = unpack_rgba(color[1]) + r = rgba[0] / 255 + g = rgba[1] / 255 + b = rgba[2] / 255 + a = rgba[3] / 255 + r_text = "{:.1f}".format(r) if r.is_integer() else "{:.9g}".format(r) + g_text = "{:.1f}".format(g) if g.is_integer() else "{:.9g}".format(g) + b_text = "{:.1f}".format(b) if b.is_integer() else "{:.9g}".format(b) + a_text = "{:.1f}".format(a) if a.is_integer() else "{:.9g}".format(a) + header.write(init_color_definition.format( + name = init_color, r = r_text, g = g_text, b = b_text, a = a_text)) + +header.write("\n") + +for color in colors: + add_documentation(color) + init_color = "sg_" + color[0].lower().replace(" ", "_") + init_color_definition = "static SOKOL_COLOR_CONSTEXPR sg_color {name} = {init};\n" + init_color_name = "SG_" + color[0].upper().replace(" ", "_") + header.write(init_color_definition.format(name = init_color, init = init_color_name)) + +header.write("\n") + +for color in colors: + add_documentation(color) + hex_color = "0x{0:08X}".format(color[1]) + packed_color = "SG_" + color[0].upper().replace(" ", "_") + "_RGBA32" + packed_color_definition = "#define {name} {rgba}\n" + header.write(packed_color_definition.format(name = packed_color, rgba = hex_color)) + +header.write(""" +#ifdef __cplusplus +} /* extern "C" */ + +inline sg_color sg_make_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + return sg_make_color_4b(r, g, b, a); +} + +inline sg_color sg_make_color(uint32_t rgba) { + return sg_make_color_1i(rgba); +} + +inline sg_color sg_color_lerp(const sg_color& color_a, const sg_color& color_b, float amount) { + return sg_color_lerp(&color_a, &color_b, amount); +} + +inline sg_color sg_color_lerp_precise(const sg_color& color_a, const sg_color& color_b, float amount) { + return sg_color_lerp_precise(&color_a, &color_b, amount); +} + +inline sg_color sg_color_multiply(const sg_color& color, float scale) { + return sg_color_multiply(&color, scale); +} + +#endif /* __cplusplus */ + +#endif /* SOKOL_COLOR_INCLUDED */ + +/*-- IMPLEMENTATION ----------------------------------------------------------*/ +#ifdef SOKOL_COLOR_IMPL +#define SOKOL_COLOR_IMPL_INCLUDED (1) + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif + +static inline float _sg_color_clamp(float v, float low, float high) { + if (v < low) { + return low; + } else if (v > high) { + return high; + } + return v; +} + +static inline float _sg_color_lerp(float a, float b, float amount) { + return a + (b - a) * amount; +} + +static inline float _sg_color_lerp_precise(float a, float b, float amount) { + return ((1.0f - amount) * a) + (b * amount); +} + +SOKOL_API_IMPL sg_color sg_make_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + sg_color result; + result.r = r / 255.0f; + result.g = g / 255.0f; + result.b = b / 255.0f; + result.a = a / 255.0f; + return result; +} + +SOKOL_API_IMPL sg_color sg_make_color_1i(uint32_t rgba) { + return sg_make_color_4b( + (uint8_t)(rgba >> 24), + (uint8_t)(rgba >> 16), + (uint8_t)(rgba >> 8), + (uint8_t)(rgba >> 0) + ); +} + +SOKOL_API_IMPL sg_color sg_color_lerp(const sg_color* color_a, const sg_color* color_b, float amount) { + SOKOL_ASSERT(color_a); + SOKOL_ASSERT(color_b); + amount = _sg_color_clamp(amount, 0.0f, 1.0f); + sg_color result; + result.r = _sg_color_lerp(color_a->r, color_b->r, amount); + result.g = _sg_color_lerp(color_a->g, color_b->g, amount); + result.b = _sg_color_lerp(color_a->b, color_b->b, amount); + result.a = _sg_color_lerp(color_a->a, color_b->a, amount); + return result; +} + +SOKOL_API_IMPL sg_color sg_color_lerp_precise(const sg_color* color_a, const sg_color* color_b, float amount) { + SOKOL_ASSERT(color_a); + SOKOL_ASSERT(color_b); + amount = _sg_color_clamp(amount, 0.0f, 1.0f); + sg_color result; + result.r = _sg_color_lerp_precise(color_a->r, color_b->r, amount); + result.g = _sg_color_lerp_precise(color_a->g, color_b->g, amount); + result.b = _sg_color_lerp_precise(color_a->b, color_b->b, amount); + result.a = _sg_color_lerp_precise(color_a->a, color_b->a, amount); + return result; +} + +SOKOL_API_IMPL sg_color sg_color_multiply(const sg_color* color, float scale) { + SOKOL_ASSERT(color); + sg_color result; + result.r = color->r * scale; + result.g = color->g * scale; + result.b = color->b * scale; + result.a = color->a * scale; + return result; +} + +#endif /* SOKOL_COLOR_IMPL */ +""") diff --git a/thirdparty/sokol/util/sokol_color.h b/thirdparty/sokol/util/sokol_color.h new file mode 100644 index 0000000..cc31b44 --- /dev/null +++ b/thirdparty/sokol/util/sokol_color.h @@ -0,0 +1,1148 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_COLOR_IMPL) +#define SOKOL_COLOR_IMPL +#endif +#ifndef SOKOL_COLOR_INCLUDED +/* + sokol_color.h -- sg_color utilities + + This header was generated by gen_sokol_color.py. Do not modify it. + + Project URL: https://github.com/floooh/sokol + + Include the following headers before including sokol_color.h: + + sokol_gfx.h + + FEATURE OVERVIEW + ================ + sokol_color.h defines preset colors based on the X11 color names, + alongside utility functions to create and modify sg_color objects. + + The predefined colors are based on the X11 color names: + + https://en.wikipedia.org/wiki/X11_color_names + + This palette is useful for prototyping - lots of programmers are familiar with + these colours due to their use in X11, web development and XNA / MonoGame. They + are also handy when you want to reference a familiar color, but don't want to + write it out by hand. + + COLORS + ====== + The palette is defined using static const (or constexpr if you are using a + C++ compiler) objects. These objects use lowercase names: + + static SOKOL_COLOR_CONSTEXPR sg_color sg_red = SG_RED; + static SOKOL_COLOR_CONSTEXPR sg_color sg_green = SG_GREEN; + static SOKOL_COLOR_CONSTEXPR sg_color sg_blue = SG_BLUE; + + An sg_color preset object like sg_red can be used to initialize + an sg_pass_action: + + sg_pass_action pass_action = { + .colors[0] = { .action=SG_ACTION_CLEAR, .value = sg_red } + }; + + Initializing an object with static storage duration is more complicated + because of C language rules. Technically, a static const is not a + compile-time constant in C. To work around this, the palette is also + defined as a series of brace-enclosed list macro definitions. These + definitions use uppercase names: + + #define SG_RED { 1.0f, 0.0f, 0.0f, 1.0f } + #define SG_GREEN { 0.0f, 1.0f, 0.0f, 1.0f } + #define SG_BLUE { 0.0f, 0.0f, 1.0f, 1.0f } + + A preset macro like SG_RED can be used to initialize objects with static + storage duration: + + static struct { + sg_pass_action pass_action; + } state = { + .pass_action = { + .colors[0] = { .action = SG_ACTION_CLEAR, .value = SG_RED } + } + }; + + A second set of macro definitions exists for colors packed as 32 bit integer + values. These definitions are also uppercase, but use the _RGBA32 suffix: + + #define SG_RED_RGBA32 0xFF0000FF + #define SG_GREEN_RGBA32 0x00FF00FF + #define SG_BLUE_RGBA32 0x0000FFFF + + This is useful if your code makes use of packed colors, as sokol_gl.h does for its + internal vertex format: + + sgl_begin_triangles(); + sgl_v2f_c1i( 0.0f, 0.5f, SG_RED_RGBA32); + sgl_v2f_c1i( 0.5f, -0.5f, SG_GREEN_RGBA32); + sgl_v2f_c1i(-0.5f, -0.5f, SG_BLUE_RGBA32); + sgl_end(); + + UTILITY FUNCTIONS + ================= + + Utility functions for creating colours are provided: + + - sg_make_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) + Create a sg_color object from separate R, G, B, A bytes. + + - sg_make_color_1i(uint32_t rgba) + Create a sg_color object from RGBA bytes packed into a 32-bit unsigned integer. + + - sg_color_lerp(const sg_color* color_a, const sg_color* color_b, float amount) + Linearly interpolate a color. + + - sg_color_lerp_precise(const sg_color* color_a, const sg_color* color_b, float amount) + Linearly interpolate a color. Less efficient but more precise than sg_color_lerp. + + - sg_color_multiply(const sg_color* color, float scale) + Multiply each color component by the scale factor. + + LICENSE + ======= + + zlib/libpng license + + Copyright (c) 2020 Stuart Adams + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_COLOR_INCLUDED (1) + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_color.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_GL_API_DECL) +#define SOKOL_COLOR_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_COLOR_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_COLOR_IMPL) +#define SOKOL_COLOR_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_COLOR_API_DECL __declspec(dllimport) +#else +#define SOKOL_COLOR_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +#define SOKOL_COLOR_CONSTEXPR constexpr +extern "C" { +#else +#define SOKOL_COLOR_CONSTEXPR const +#endif + +SOKOL_COLOR_API_DECL sg_color sg_make_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_COLOR_API_DECL sg_color sg_make_color_1i(uint32_t rgba); +SOKOL_COLOR_API_DECL sg_color sg_color_lerp(const sg_color* color_a, const sg_color* color_b, float amount); +SOKOL_COLOR_API_DECL sg_color sg_color_lerp_precise(const sg_color* color_a, const sg_color* color_b, float amount); +SOKOL_COLOR_API_DECL sg_color sg_color_multiply(const sg_color* color, float scale); + +/* Alice Blue color { R:240, G:248, B:255, A:255 } */ +#define SG_ALICE_BLUE { 0.941176471f, 0.97254902f, 1.0f, 1.0f } +/* Antique White color { R:250, G:235, B:215, A:255 } */ +#define SG_ANTIQUE_WHITE { 0.980392157f, 0.921568627f, 0.843137255f, 1.0f } +/* Aqua color { R:0, G:255, B:255, A:255 } */ +#define SG_AQUA { 0.0f, 1.0f, 1.0f, 1.0f } +/* Aquamarine color { R:127, G:255, B:212, A:255 } */ +#define SG_AQUAMARINE { 0.498039216f, 1.0f, 0.831372549f, 1.0f } +/* Azure color { R:240, G:255, B:255, A:255 } */ +#define SG_AZURE { 0.941176471f, 1.0f, 1.0f, 1.0f } +/* Beige color { R:245, G:245, B:220, A:255 } */ +#define SG_BEIGE { 0.960784314f, 0.960784314f, 0.862745098f, 1.0f } +/* Bisque color { R:255, G:228, B:196, A:255 } */ +#define SG_BISQUE { 1.0f, 0.894117647f, 0.768627451f, 1.0f } +/* Black color { R:0, G:0, B:0, A:255 } */ +#define SG_BLACK { 0.0f, 0.0f, 0.0f, 1.0f } +/* Blanched Almond color { R:255, G:235, B:205, A:255 } */ +#define SG_BLANCHED_ALMOND { 1.0f, 0.921568627f, 0.803921569f, 1.0f } +/* Blue color { R:0, G:0, B:255, A:255 } */ +#define SG_BLUE { 0.0f, 0.0f, 1.0f, 1.0f } +/* Blue Violet color { R:138, G:43, B:226, A:255 } */ +#define SG_BLUE_VIOLET { 0.541176471f, 0.168627451f, 0.88627451f, 1.0f } +/* Brown color { R:165, G:42, B:42, A:255 } */ +#define SG_BROWN { 0.647058824f, 0.164705882f, 0.164705882f, 1.0f } +/* Burlywood color { R:222, G:184, B:135, A:255 } */ +#define SG_BURLYWOOD { 0.870588235f, 0.721568627f, 0.529411765f, 1.0f } +/* Cadet Blue color { R:95, G:158, B:160, A:255 } */ +#define SG_CADET_BLUE { 0.37254902f, 0.619607843f, 0.62745098f, 1.0f } +/* Chartreuse color { R:127, G:255, B:0, A:255 } */ +#define SG_CHARTREUSE { 0.498039216f, 1.0f, 0.0f, 1.0f } +/* Chocolate color { R:210, G:105, B:30, A:255 } */ +#define SG_CHOCOLATE { 0.823529412f, 0.411764706f, 0.117647059f, 1.0f } +/* Coral color { R:255, G:127, B:80, A:255 } */ +#define SG_CORAL { 1.0f, 0.498039216f, 0.31372549f, 1.0f } +/* Cornflower Blue color { R:100, G:149, B:237, A:255 } */ +#define SG_CORNFLOWER_BLUE { 0.392156863f, 0.584313725f, 0.929411765f, 1.0f } +/* Cornsilk color { R:255, G:248, B:220, A:255 } */ +#define SG_CORNSILK { 1.0f, 0.97254902f, 0.862745098f, 1.0f } +/* Crimson color { R:220, G:20, B:60, A:255 } */ +#define SG_CRIMSON { 0.862745098f, 0.0784313725f, 0.235294118f, 1.0f } +/* Cyan color { R:0, G:255, B:255, A:255 } */ +#define SG_CYAN { 0.0f, 1.0f, 1.0f, 1.0f } +/* Dark Blue color { R:0, G:0, B:139, A:255 } */ +#define SG_DARK_BLUE { 0.0f, 0.0f, 0.545098039f, 1.0f } +/* Dark Cyan color { R:0, G:139, B:139, A:255 } */ +#define SG_DARK_CYAN { 0.0f, 0.545098039f, 0.545098039f, 1.0f } +/* Dark Goldenrod color { R:184, G:134, B:11, A:255 } */ +#define SG_DARK_GOLDENROD { 0.721568627f, 0.525490196f, 0.0431372549f, 1.0f } +/* Dark Gray color { R:169, G:169, B:169, A:255 } */ +#define SG_DARK_GRAY { 0.662745098f, 0.662745098f, 0.662745098f, 1.0f } +/* Dark Green color { R:0, G:100, B:0, A:255 } */ +#define SG_DARK_GREEN { 0.0f, 0.392156863f, 0.0f, 1.0f } +/* Dark Khaki color { R:189, G:183, B:107, A:255 } */ +#define SG_DARK_KHAKI { 0.741176471f, 0.717647059f, 0.419607843f, 1.0f } +/* Dark Magenta color { R:139, G:0, B:139, A:255 } */ +#define SG_DARK_MAGENTA { 0.545098039f, 0.0f, 0.545098039f, 1.0f } +/* Dark Olive Green color { R:85, G:107, B:47, A:255 } */ +#define SG_DARK_OLIVE_GREEN { 0.333333333f, 0.419607843f, 0.184313725f, 1.0f } +/* Dark Orange color { R:255, G:140, B:0, A:255 } */ +#define SG_DARK_ORANGE { 1.0f, 0.549019608f, 0.0f, 1.0f } +/* Dark Orchid color { R:153, G:50, B:204, A:255 } */ +#define SG_DARK_ORCHID { 0.6f, 0.196078431f, 0.8f, 1.0f } +/* Dark Red color { R:139, G:0, B:0, A:255 } */ +#define SG_DARK_RED { 0.545098039f, 0.0f, 0.0f, 1.0f } +/* Dark Salmon color { R:233, G:150, B:122, A:255 } */ +#define SG_DARK_SALMON { 0.91372549f, 0.588235294f, 0.478431373f, 1.0f } +/* Dark Sea Green color { R:143, G:188, B:143, A:255 } */ +#define SG_DARK_SEA_GREEN { 0.560784314f, 0.737254902f, 0.560784314f, 1.0f } +/* Dark Slate Blue color { R:72, G:61, B:139, A:255 } */ +#define SG_DARK_SLATE_BLUE { 0.282352941f, 0.239215686f, 0.545098039f, 1.0f } +/* Dark Slate Gray color { R:47, G:79, B:79, A:255 } */ +#define SG_DARK_SLATE_GRAY { 0.184313725f, 0.309803922f, 0.309803922f, 1.0f } +/* Dark Turquoise color { R:0, G:206, B:209, A:255 } */ +#define SG_DARK_TURQUOISE { 0.0f, 0.807843137f, 0.819607843f, 1.0f } +/* Dark Violet color { R:148, G:0, B:211, A:255 } */ +#define SG_DARK_VIOLET { 0.580392157f, 0.0f, 0.82745098f, 1.0f } +/* Deep Pink color { R:255, G:20, B:147, A:255 } */ +#define SG_DEEP_PINK { 1.0f, 0.0784313725f, 0.576470588f, 1.0f } +/* Deep Sky Blue color { R:0, G:191, B:255, A:255 } */ +#define SG_DEEP_SKY_BLUE { 0.0f, 0.749019608f, 1.0f, 1.0f } +/* Dim Gray color { R:105, G:105, B:105, A:255 } */ +#define SG_DIM_GRAY { 0.411764706f, 0.411764706f, 0.411764706f, 1.0f } +/* Dodger Blue color { R:30, G:144, B:255, A:255 } */ +#define SG_DODGER_BLUE { 0.117647059f, 0.564705882f, 1.0f, 1.0f } +/* Firebrick color { R:178, G:34, B:34, A:255 } */ +#define SG_FIREBRICK { 0.698039216f, 0.133333333f, 0.133333333f, 1.0f } +/* Floral White color { R:255, G:250, B:240, A:255 } */ +#define SG_FLORAL_WHITE { 1.0f, 0.980392157f, 0.941176471f, 1.0f } +/* Forest Green color { R:34, G:139, B:34, A:255 } */ +#define SG_FOREST_GREEN { 0.133333333f, 0.545098039f, 0.133333333f, 1.0f } +/* Fuchsia color { R:255, G:0, B:255, A:255 } */ +#define SG_FUCHSIA { 1.0f, 0.0f, 1.0f, 1.0f } +/* Gainsboro color { R:220, G:220, B:220, A:255 } */ +#define SG_GAINSBORO { 0.862745098f, 0.862745098f, 0.862745098f, 1.0f } +/* Ghost White color { R:248, G:248, B:255, A:255 } */ +#define SG_GHOST_WHITE { 0.97254902f, 0.97254902f, 1.0f, 1.0f } +/* Gold color { R:255, G:215, B:0, A:255 } */ +#define SG_GOLD { 1.0f, 0.843137255f, 0.0f, 1.0f } +/* Goldenrod color { R:218, G:165, B:32, A:255 } */ +#define SG_GOLDENROD { 0.854901961f, 0.647058824f, 0.125490196f, 1.0f } +/* Gray color { R:190, G:190, B:190, A:255 } */ +#define SG_GRAY { 0.745098039f, 0.745098039f, 0.745098039f, 1.0f } +/* Web Gray color { R:128, G:128, B:128, A:255 } */ +#define SG_WEB_GRAY { 0.501960784f, 0.501960784f, 0.501960784f, 1.0f } +/* Green color { R:0, G:255, B:0, A:255 } */ +#define SG_GREEN { 0.0f, 1.0f, 0.0f, 1.0f } +/* Web Green color { R:0, G:128, B:0, A:255 } */ +#define SG_WEB_GREEN { 0.0f, 0.501960784f, 0.0f, 1.0f } +/* Green Yellow color { R:173, G:255, B:47, A:255 } */ +#define SG_GREEN_YELLOW { 0.678431373f, 1.0f, 0.184313725f, 1.0f } +/* Honeydew color { R:240, G:255, B:240, A:255 } */ +#define SG_HONEYDEW { 0.941176471f, 1.0f, 0.941176471f, 1.0f } +/* Hot Pink color { R:255, G:105, B:180, A:255 } */ +#define SG_HOT_PINK { 1.0f, 0.411764706f, 0.705882353f, 1.0f } +/* Indian Red color { R:205, G:92, B:92, A:255 } */ +#define SG_INDIAN_RED { 0.803921569f, 0.360784314f, 0.360784314f, 1.0f } +/* Indigo color { R:75, G:0, B:130, A:255 } */ +#define SG_INDIGO { 0.294117647f, 0.0f, 0.509803922f, 1.0f } +/* Ivory color { R:255, G:255, B:240, A:255 } */ +#define SG_IVORY { 1.0f, 1.0f, 0.941176471f, 1.0f } +/* Khaki color { R:240, G:230, B:140, A:255 } */ +#define SG_KHAKI { 0.941176471f, 0.901960784f, 0.549019608f, 1.0f } +/* Lavender color { R:230, G:230, B:250, A:255 } */ +#define SG_LAVENDER { 0.901960784f, 0.901960784f, 0.980392157f, 1.0f } +/* Lavender Blush color { R:255, G:240, B:245, A:255 } */ +#define SG_LAVENDER_BLUSH { 1.0f, 0.941176471f, 0.960784314f, 1.0f } +/* Lawn Green color { R:124, G:252, B:0, A:255 } */ +#define SG_LAWN_GREEN { 0.48627451f, 0.988235294f, 0.0f, 1.0f } +/* Lemon Chiffon color { R:255, G:250, B:205, A:255 } */ +#define SG_LEMON_CHIFFON { 1.0f, 0.980392157f, 0.803921569f, 1.0f } +/* Light Blue color { R:173, G:216, B:230, A:255 } */ +#define SG_LIGHT_BLUE { 0.678431373f, 0.847058824f, 0.901960784f, 1.0f } +/* Light Coral color { R:240, G:128, B:128, A:255 } */ +#define SG_LIGHT_CORAL { 0.941176471f, 0.501960784f, 0.501960784f, 1.0f } +/* Light Cyan color { R:224, G:255, B:255, A:255 } */ +#define SG_LIGHT_CYAN { 0.878431373f, 1.0f, 1.0f, 1.0f } +/* Light Goldenrod color { R:250, G:250, B:210, A:255 } */ +#define SG_LIGHT_GOLDENROD { 0.980392157f, 0.980392157f, 0.823529412f, 1.0f } +/* Light Gray color { R:211, G:211, B:211, A:255 } */ +#define SG_LIGHT_GRAY { 0.82745098f, 0.82745098f, 0.82745098f, 1.0f } +/* Light Green color { R:144, G:238, B:144, A:255 } */ +#define SG_LIGHT_GREEN { 0.564705882f, 0.933333333f, 0.564705882f, 1.0f } +/* Light Pink color { R:255, G:182, B:193, A:255 } */ +#define SG_LIGHT_PINK { 1.0f, 0.71372549f, 0.756862745f, 1.0f } +/* Light Salmon color { R:255, G:160, B:122, A:255 } */ +#define SG_LIGHT_SALMON { 1.0f, 0.62745098f, 0.478431373f, 1.0f } +/* Light Sea Green color { R:32, G:178, B:170, A:255 } */ +#define SG_LIGHT_SEA_GREEN { 0.125490196f, 0.698039216f, 0.666666667f, 1.0f } +/* Light Sky Blue color { R:135, G:206, B:250, A:255 } */ +#define SG_LIGHT_SKY_BLUE { 0.529411765f, 0.807843137f, 0.980392157f, 1.0f } +/* Light Slate Gray color { R:119, G:136, B:153, A:255 } */ +#define SG_LIGHT_SLATE_GRAY { 0.466666667f, 0.533333333f, 0.6f, 1.0f } +/* Light Steel Blue color { R:176, G:196, B:222, A:255 } */ +#define SG_LIGHT_STEEL_BLUE { 0.690196078f, 0.768627451f, 0.870588235f, 1.0f } +/* Light Yellow color { R:255, G:255, B:224, A:255 } */ +#define SG_LIGHT_YELLOW { 1.0f, 1.0f, 0.878431373f, 1.0f } +/* Lime color { R:0, G:255, B:0, A:255 } */ +#define SG_LIME { 0.0f, 1.0f, 0.0f, 1.0f } +/* Lime Green color { R:50, G:205, B:50, A:255 } */ +#define SG_LIME_GREEN { 0.196078431f, 0.803921569f, 0.196078431f, 1.0f } +/* Linen color { R:250, G:240, B:230, A:255 } */ +#define SG_LINEN { 0.980392157f, 0.941176471f, 0.901960784f, 1.0f } +/* Magenta color { R:255, G:0, B:255, A:255 } */ +#define SG_MAGENTA { 1.0f, 0.0f, 1.0f, 1.0f } +/* Maroon color { R:176, G:48, B:96, A:255 } */ +#define SG_MAROON { 0.690196078f, 0.188235294f, 0.376470588f, 1.0f } +/* Web Maroon color { R:128, G:0, B:0, A:255 } */ +#define SG_WEB_MAROON { 0.501960784f, 0.0f, 0.0f, 1.0f } +/* Medium Aquamarine color { R:102, G:205, B:170, A:255 } */ +#define SG_MEDIUM_AQUAMARINE { 0.4f, 0.803921569f, 0.666666667f, 1.0f } +/* Medium Blue color { R:0, G:0, B:205, A:255 } */ +#define SG_MEDIUM_BLUE { 0.0f, 0.0f, 0.803921569f, 1.0f } +/* Medium Orchid color { R:186, G:85, B:211, A:255 } */ +#define SG_MEDIUM_ORCHID { 0.729411765f, 0.333333333f, 0.82745098f, 1.0f } +/* Medium Purple color { R:147, G:112, B:219, A:255 } */ +#define SG_MEDIUM_PURPLE { 0.576470588f, 0.439215686f, 0.858823529f, 1.0f } +/* Medium Sea Green color { R:60, G:179, B:113, A:255 } */ +#define SG_MEDIUM_SEA_GREEN { 0.235294118f, 0.701960784f, 0.443137255f, 1.0f } +/* Medium Slate Blue color { R:123, G:104, B:238, A:255 } */ +#define SG_MEDIUM_SLATE_BLUE { 0.482352941f, 0.407843137f, 0.933333333f, 1.0f } +/* Medium Spring Green color { R:0, G:250, B:154, A:255 } */ +#define SG_MEDIUM_SPRING_GREEN { 0.0f, 0.980392157f, 0.603921569f, 1.0f } +/* Medium Turquoise color { R:72, G:209, B:204, A:255 } */ +#define SG_MEDIUM_TURQUOISE { 0.282352941f, 0.819607843f, 0.8f, 1.0f } +/* Medium Violet Red color { R:199, G:21, B:133, A:255 } */ +#define SG_MEDIUM_VIOLET_RED { 0.780392157f, 0.0823529412f, 0.521568627f, 1.0f } +/* Midnight Blue color { R:25, G:25, B:112, A:255 } */ +#define SG_MIDNIGHT_BLUE { 0.0980392157f, 0.0980392157f, 0.439215686f, 1.0f } +/* Mint Cream color { R:245, G:255, B:250, A:255 } */ +#define SG_MINT_CREAM { 0.960784314f, 1.0f, 0.980392157f, 1.0f } +/* Misty Rose color { R:255, G:228, B:225, A:255 } */ +#define SG_MISTY_ROSE { 1.0f, 0.894117647f, 0.882352941f, 1.0f } +/* Moccasin color { R:255, G:228, B:181, A:255 } */ +#define SG_MOCCASIN { 1.0f, 0.894117647f, 0.709803922f, 1.0f } +/* Navajo White color { R:255, G:222, B:173, A:255 } */ +#define SG_NAVAJO_WHITE { 1.0f, 0.870588235f, 0.678431373f, 1.0f } +/* Navy Blue color { R:0, G:0, B:128, A:255 } */ +#define SG_NAVY_BLUE { 0.0f, 0.0f, 0.501960784f, 1.0f } +/* Old Lace color { R:253, G:245, B:230, A:255 } */ +#define SG_OLD_LACE { 0.992156863f, 0.960784314f, 0.901960784f, 1.0f } +/* Olive color { R:128, G:128, B:0, A:255 } */ +#define SG_OLIVE { 0.501960784f, 0.501960784f, 0.0f, 1.0f } +/* Olive Drab color { R:107, G:142, B:35, A:255 } */ +#define SG_OLIVE_DRAB { 0.419607843f, 0.556862745f, 0.137254902f, 1.0f } +/* Orange color { R:255, G:165, B:0, A:255 } */ +#define SG_ORANGE { 1.0f, 0.647058824f, 0.0f, 1.0f } +/* Orange Red color { R:255, G:69, B:0, A:255 } */ +#define SG_ORANGE_RED { 1.0f, 0.270588235f, 0.0f, 1.0f } +/* Orchid color { R:218, G:112, B:214, A:255 } */ +#define SG_ORCHID { 0.854901961f, 0.439215686f, 0.839215686f, 1.0f } +/* Pale Goldenrod color { R:238, G:232, B:170, A:255 } */ +#define SG_PALE_GOLDENROD { 0.933333333f, 0.909803922f, 0.666666667f, 1.0f } +/* Pale Green color { R:152, G:251, B:152, A:255 } */ +#define SG_PALE_GREEN { 0.596078431f, 0.984313725f, 0.596078431f, 1.0f } +/* Pale Turquoise color { R:175, G:238, B:238, A:255 } */ +#define SG_PALE_TURQUOISE { 0.68627451f, 0.933333333f, 0.933333333f, 1.0f } +/* Pale Violet Red color { R:219, G:112, B:147, A:255 } */ +#define SG_PALE_VIOLET_RED { 0.858823529f, 0.439215686f, 0.576470588f, 1.0f } +/* Papaya Whip color { R:255, G:239, B:213, A:255 } */ +#define SG_PAPAYA_WHIP { 1.0f, 0.937254902f, 0.835294118f, 1.0f } +/* Peach Puff color { R:255, G:218, B:185, A:255 } */ +#define SG_PEACH_PUFF { 1.0f, 0.854901961f, 0.725490196f, 1.0f } +/* Peru color { R:205, G:133, B:63, A:255 } */ +#define SG_PERU { 0.803921569f, 0.521568627f, 0.247058824f, 1.0f } +/* Pink color { R:255, G:192, B:203, A:255 } */ +#define SG_PINK { 1.0f, 0.752941176f, 0.796078431f, 1.0f } +/* Plum color { R:221, G:160, B:221, A:255 } */ +#define SG_PLUM { 0.866666667f, 0.62745098f, 0.866666667f, 1.0f } +/* Powder Blue color { R:176, G:224, B:230, A:255 } */ +#define SG_POWDER_BLUE { 0.690196078f, 0.878431373f, 0.901960784f, 1.0f } +/* Purple color { R:160, G:32, B:240, A:255 } */ +#define SG_PURPLE { 0.62745098f, 0.125490196f, 0.941176471f, 1.0f } +/* Web Purple color { R:128, G:0, B:128, A:255 } */ +#define SG_WEB_PURPLE { 0.501960784f, 0.0f, 0.501960784f, 1.0f } +/* Rebecca Purple color { R:102, G:51, B:153, A:255 } */ +#define SG_REBECCA_PURPLE { 0.4f, 0.2f, 0.6f, 1.0f } +/* Red color { R:255, G:0, B:0, A:255 } */ +#define SG_RED { 1.0f, 0.0f, 0.0f, 1.0f } +/* Rosy Brown color { R:188, G:143, B:143, A:255 } */ +#define SG_ROSY_BROWN { 0.737254902f, 0.560784314f, 0.560784314f, 1.0f } +/* Royal Blue color { R:65, G:105, B:225, A:255 } */ +#define SG_ROYAL_BLUE { 0.254901961f, 0.411764706f, 0.882352941f, 1.0f } +/* Saddle Brown color { R:139, G:69, B:19, A:255 } */ +#define SG_SADDLE_BROWN { 0.545098039f, 0.270588235f, 0.0745098039f, 1.0f } +/* Salmon color { R:250, G:128, B:114, A:255 } */ +#define SG_SALMON { 0.980392157f, 0.501960784f, 0.447058824f, 1.0f } +/* Sandy Brown color { R:244, G:164, B:96, A:255 } */ +#define SG_SANDY_BROWN { 0.956862745f, 0.643137255f, 0.376470588f, 1.0f } +/* Sea Green color { R:46, G:139, B:87, A:255 } */ +#define SG_SEA_GREEN { 0.180392157f, 0.545098039f, 0.341176471f, 1.0f } +/* Seashell color { R:255, G:245, B:238, A:255 } */ +#define SG_SEASHELL { 1.0f, 0.960784314f, 0.933333333f, 1.0f } +/* Sienna color { R:160, G:82, B:45, A:255 } */ +#define SG_SIENNA { 0.62745098f, 0.321568627f, 0.176470588f, 1.0f } +/* Silver color { R:192, G:192, B:192, A:255 } */ +#define SG_SILVER { 0.752941176f, 0.752941176f, 0.752941176f, 1.0f } +/* Sky Blue color { R:135, G:206, B:235, A:255 } */ +#define SG_SKY_BLUE { 0.529411765f, 0.807843137f, 0.921568627f, 1.0f } +/* Slate Blue color { R:106, G:90, B:205, A:255 } */ +#define SG_SLATE_BLUE { 0.415686275f, 0.352941176f, 0.803921569f, 1.0f } +/* Slate Gray color { R:112, G:128, B:144, A:255 } */ +#define SG_SLATE_GRAY { 0.439215686f, 0.501960784f, 0.564705882f, 1.0f } +/* Snow color { R:255, G:250, B:250, A:255 } */ +#define SG_SNOW { 1.0f, 0.980392157f, 0.980392157f, 1.0f } +/* Spring Green color { R:0, G:255, B:127, A:255 } */ +#define SG_SPRING_GREEN { 0.0f, 1.0f, 0.498039216f, 1.0f } +/* Steel Blue color { R:70, G:130, B:180, A:255 } */ +#define SG_STEEL_BLUE { 0.274509804f, 0.509803922f, 0.705882353f, 1.0f } +/* Tan color { R:210, G:180, B:140, A:255 } */ +#define SG_TAN { 0.823529412f, 0.705882353f, 0.549019608f, 1.0f } +/* Teal color { R:0, G:128, B:128, A:255 } */ +#define SG_TEAL { 0.0f, 0.501960784f, 0.501960784f, 1.0f } +/* Thistle color { R:216, G:191, B:216, A:255 } */ +#define SG_THISTLE { 0.847058824f, 0.749019608f, 0.847058824f, 1.0f } +/* Tomato color { R:255, G:99, B:71, A:255 } */ +#define SG_TOMATO { 1.0f, 0.388235294f, 0.278431373f, 1.0f } +/* Transparent color { R:0, G:0, B:0, A:0 } */ +#define SG_TRANSPARENT { 0.0f, 0.0f, 0.0f, 0.0f } +/* Turquoise color { R:64, G:224, B:208, A:255 } */ +#define SG_TURQUOISE { 0.250980392f, 0.878431373f, 0.815686275f, 1.0f } +/* Violet color { R:238, G:130, B:238, A:255 } */ +#define SG_VIOLET { 0.933333333f, 0.509803922f, 0.933333333f, 1.0f } +/* Wheat color { R:245, G:222, B:179, A:255 } */ +#define SG_WHEAT { 0.960784314f, 0.870588235f, 0.701960784f, 1.0f } +/* White color { R:255, G:255, B:255, A:255 } */ +#define SG_WHITE { 1.0f, 1.0f, 1.0f, 1.0f } +/* White Smoke color { R:245, G:245, B:245, A:255 } */ +#define SG_WHITE_SMOKE { 0.960784314f, 0.960784314f, 0.960784314f, 1.0f } +/* Yellow color { R:255, G:255, B:0, A:255 } */ +#define SG_YELLOW { 1.0f, 1.0f, 0.0f, 1.0f } +/* Yellow Green color { R:154, G:205, B:50, A:255 } */ +#define SG_YELLOW_GREEN { 0.603921569f, 0.803921569f, 0.196078431f, 1.0f } + +/* Alice Blue color { R:240, G:248, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_alice_blue = SG_ALICE_BLUE; +/* Antique White color { R:250, G:235, B:215, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_antique_white = SG_ANTIQUE_WHITE; +/* Aqua color { R:0, G:255, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_aqua = SG_AQUA; +/* Aquamarine color { R:127, G:255, B:212, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_aquamarine = SG_AQUAMARINE; +/* Azure color { R:240, G:255, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_azure = SG_AZURE; +/* Beige color { R:245, G:245, B:220, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_beige = SG_BEIGE; +/* Bisque color { R:255, G:228, B:196, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_bisque = SG_BISQUE; +/* Black color { R:0, G:0, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_black = SG_BLACK; +/* Blanched Almond color { R:255, G:235, B:205, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_blanched_almond = SG_BLANCHED_ALMOND; +/* Blue color { R:0, G:0, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_blue = SG_BLUE; +/* Blue Violet color { R:138, G:43, B:226, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_blue_violet = SG_BLUE_VIOLET; +/* Brown color { R:165, G:42, B:42, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_brown = SG_BROWN; +/* Burlywood color { R:222, G:184, B:135, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_burlywood = SG_BURLYWOOD; +/* Cadet Blue color { R:95, G:158, B:160, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_cadet_blue = SG_CADET_BLUE; +/* Chartreuse color { R:127, G:255, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_chartreuse = SG_CHARTREUSE; +/* Chocolate color { R:210, G:105, B:30, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_chocolate = SG_CHOCOLATE; +/* Coral color { R:255, G:127, B:80, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_coral = SG_CORAL; +/* Cornflower Blue color { R:100, G:149, B:237, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_cornflower_blue = SG_CORNFLOWER_BLUE; +/* Cornsilk color { R:255, G:248, B:220, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_cornsilk = SG_CORNSILK; +/* Crimson color { R:220, G:20, B:60, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_crimson = SG_CRIMSON; +/* Cyan color { R:0, G:255, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_cyan = SG_CYAN; +/* Dark Blue color { R:0, G:0, B:139, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_blue = SG_DARK_BLUE; +/* Dark Cyan color { R:0, G:139, B:139, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_cyan = SG_DARK_CYAN; +/* Dark Goldenrod color { R:184, G:134, B:11, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_goldenrod = SG_DARK_GOLDENROD; +/* Dark Gray color { R:169, G:169, B:169, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_gray = SG_DARK_GRAY; +/* Dark Green color { R:0, G:100, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_green = SG_DARK_GREEN; +/* Dark Khaki color { R:189, G:183, B:107, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_khaki = SG_DARK_KHAKI; +/* Dark Magenta color { R:139, G:0, B:139, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_magenta = SG_DARK_MAGENTA; +/* Dark Olive Green color { R:85, G:107, B:47, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_olive_green = SG_DARK_OLIVE_GREEN; +/* Dark Orange color { R:255, G:140, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_orange = SG_DARK_ORANGE; +/* Dark Orchid color { R:153, G:50, B:204, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_orchid = SG_DARK_ORCHID; +/* Dark Red color { R:139, G:0, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_red = SG_DARK_RED; +/* Dark Salmon color { R:233, G:150, B:122, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_salmon = SG_DARK_SALMON; +/* Dark Sea Green color { R:143, G:188, B:143, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_sea_green = SG_DARK_SEA_GREEN; +/* Dark Slate Blue color { R:72, G:61, B:139, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_slate_blue = SG_DARK_SLATE_BLUE; +/* Dark Slate Gray color { R:47, G:79, B:79, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_slate_gray = SG_DARK_SLATE_GRAY; +/* Dark Turquoise color { R:0, G:206, B:209, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_turquoise = SG_DARK_TURQUOISE; +/* Dark Violet color { R:148, G:0, B:211, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dark_violet = SG_DARK_VIOLET; +/* Deep Pink color { R:255, G:20, B:147, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_deep_pink = SG_DEEP_PINK; +/* Deep Sky Blue color { R:0, G:191, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_deep_sky_blue = SG_DEEP_SKY_BLUE; +/* Dim Gray color { R:105, G:105, B:105, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dim_gray = SG_DIM_GRAY; +/* Dodger Blue color { R:30, G:144, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_dodger_blue = SG_DODGER_BLUE; +/* Firebrick color { R:178, G:34, B:34, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_firebrick = SG_FIREBRICK; +/* Floral White color { R:255, G:250, B:240, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_floral_white = SG_FLORAL_WHITE; +/* Forest Green color { R:34, G:139, B:34, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_forest_green = SG_FOREST_GREEN; +/* Fuchsia color { R:255, G:0, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_fuchsia = SG_FUCHSIA; +/* Gainsboro color { R:220, G:220, B:220, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_gainsboro = SG_GAINSBORO; +/* Ghost White color { R:248, G:248, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_ghost_white = SG_GHOST_WHITE; +/* Gold color { R:255, G:215, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_gold = SG_GOLD; +/* Goldenrod color { R:218, G:165, B:32, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_goldenrod = SG_GOLDENROD; +/* Gray color { R:190, G:190, B:190, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_gray = SG_GRAY; +/* Web Gray color { R:128, G:128, B:128, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_web_gray = SG_WEB_GRAY; +/* Green color { R:0, G:255, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_green = SG_GREEN; +/* Web Green color { R:0, G:128, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_web_green = SG_WEB_GREEN; +/* Green Yellow color { R:173, G:255, B:47, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_green_yellow = SG_GREEN_YELLOW; +/* Honeydew color { R:240, G:255, B:240, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_honeydew = SG_HONEYDEW; +/* Hot Pink color { R:255, G:105, B:180, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_hot_pink = SG_HOT_PINK; +/* Indian Red color { R:205, G:92, B:92, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_indian_red = SG_INDIAN_RED; +/* Indigo color { R:75, G:0, B:130, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_indigo = SG_INDIGO; +/* Ivory color { R:255, G:255, B:240, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_ivory = SG_IVORY; +/* Khaki color { R:240, G:230, B:140, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_khaki = SG_KHAKI; +/* Lavender color { R:230, G:230, B:250, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_lavender = SG_LAVENDER; +/* Lavender Blush color { R:255, G:240, B:245, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_lavender_blush = SG_LAVENDER_BLUSH; +/* Lawn Green color { R:124, G:252, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_lawn_green = SG_LAWN_GREEN; +/* Lemon Chiffon color { R:255, G:250, B:205, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_lemon_chiffon = SG_LEMON_CHIFFON; +/* Light Blue color { R:173, G:216, B:230, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_blue = SG_LIGHT_BLUE; +/* Light Coral color { R:240, G:128, B:128, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_coral = SG_LIGHT_CORAL; +/* Light Cyan color { R:224, G:255, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_cyan = SG_LIGHT_CYAN; +/* Light Goldenrod color { R:250, G:250, B:210, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_goldenrod = SG_LIGHT_GOLDENROD; +/* Light Gray color { R:211, G:211, B:211, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_gray = SG_LIGHT_GRAY; +/* Light Green color { R:144, G:238, B:144, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_green = SG_LIGHT_GREEN; +/* Light Pink color { R:255, G:182, B:193, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_pink = SG_LIGHT_PINK; +/* Light Salmon color { R:255, G:160, B:122, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_salmon = SG_LIGHT_SALMON; +/* Light Sea Green color { R:32, G:178, B:170, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_sea_green = SG_LIGHT_SEA_GREEN; +/* Light Sky Blue color { R:135, G:206, B:250, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_sky_blue = SG_LIGHT_SKY_BLUE; +/* Light Slate Gray color { R:119, G:136, B:153, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_slate_gray = SG_LIGHT_SLATE_GRAY; +/* Light Steel Blue color { R:176, G:196, B:222, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_steel_blue = SG_LIGHT_STEEL_BLUE; +/* Light Yellow color { R:255, G:255, B:224, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_light_yellow = SG_LIGHT_YELLOW; +/* Lime color { R:0, G:255, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_lime = SG_LIME; +/* Lime Green color { R:50, G:205, B:50, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_lime_green = SG_LIME_GREEN; +/* Linen color { R:250, G:240, B:230, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_linen = SG_LINEN; +/* Magenta color { R:255, G:0, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_magenta = SG_MAGENTA; +/* Maroon color { R:176, G:48, B:96, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_maroon = SG_MAROON; +/* Web Maroon color { R:128, G:0, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_web_maroon = SG_WEB_MAROON; +/* Medium Aquamarine color { R:102, G:205, B:170, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_medium_aquamarine = SG_MEDIUM_AQUAMARINE; +/* Medium Blue color { R:0, G:0, B:205, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_medium_blue = SG_MEDIUM_BLUE; +/* Medium Orchid color { R:186, G:85, B:211, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_medium_orchid = SG_MEDIUM_ORCHID; +/* Medium Purple color { R:147, G:112, B:219, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_medium_purple = SG_MEDIUM_PURPLE; +/* Medium Sea Green color { R:60, G:179, B:113, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_medium_sea_green = SG_MEDIUM_SEA_GREEN; +/* Medium Slate Blue color { R:123, G:104, B:238, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_medium_slate_blue = SG_MEDIUM_SLATE_BLUE; +/* Medium Spring Green color { R:0, G:250, B:154, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_medium_spring_green = SG_MEDIUM_SPRING_GREEN; +/* Medium Turquoise color { R:72, G:209, B:204, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_medium_turquoise = SG_MEDIUM_TURQUOISE; +/* Medium Violet Red color { R:199, G:21, B:133, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_medium_violet_red = SG_MEDIUM_VIOLET_RED; +/* Midnight Blue color { R:25, G:25, B:112, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_midnight_blue = SG_MIDNIGHT_BLUE; +/* Mint Cream color { R:245, G:255, B:250, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_mint_cream = SG_MINT_CREAM; +/* Misty Rose color { R:255, G:228, B:225, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_misty_rose = SG_MISTY_ROSE; +/* Moccasin color { R:255, G:228, B:181, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_moccasin = SG_MOCCASIN; +/* Navajo White color { R:255, G:222, B:173, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_navajo_white = SG_NAVAJO_WHITE; +/* Navy Blue color { R:0, G:0, B:128, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_navy_blue = SG_NAVY_BLUE; +/* Old Lace color { R:253, G:245, B:230, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_old_lace = SG_OLD_LACE; +/* Olive color { R:128, G:128, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_olive = SG_OLIVE; +/* Olive Drab color { R:107, G:142, B:35, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_olive_drab = SG_OLIVE_DRAB; +/* Orange color { R:255, G:165, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_orange = SG_ORANGE; +/* Orange Red color { R:255, G:69, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_orange_red = SG_ORANGE_RED; +/* Orchid color { R:218, G:112, B:214, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_orchid = SG_ORCHID; +/* Pale Goldenrod color { R:238, G:232, B:170, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_pale_goldenrod = SG_PALE_GOLDENROD; +/* Pale Green color { R:152, G:251, B:152, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_pale_green = SG_PALE_GREEN; +/* Pale Turquoise color { R:175, G:238, B:238, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_pale_turquoise = SG_PALE_TURQUOISE; +/* Pale Violet Red color { R:219, G:112, B:147, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_pale_violet_red = SG_PALE_VIOLET_RED; +/* Papaya Whip color { R:255, G:239, B:213, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_papaya_whip = SG_PAPAYA_WHIP; +/* Peach Puff color { R:255, G:218, B:185, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_peach_puff = SG_PEACH_PUFF; +/* Peru color { R:205, G:133, B:63, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_peru = SG_PERU; +/* Pink color { R:255, G:192, B:203, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_pink = SG_PINK; +/* Plum color { R:221, G:160, B:221, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_plum = SG_PLUM; +/* Powder Blue color { R:176, G:224, B:230, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_powder_blue = SG_POWDER_BLUE; +/* Purple color { R:160, G:32, B:240, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_purple = SG_PURPLE; +/* Web Purple color { R:128, G:0, B:128, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_web_purple = SG_WEB_PURPLE; +/* Rebecca Purple color { R:102, G:51, B:153, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_rebecca_purple = SG_REBECCA_PURPLE; +/* Red color { R:255, G:0, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_red = SG_RED; +/* Rosy Brown color { R:188, G:143, B:143, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_rosy_brown = SG_ROSY_BROWN; +/* Royal Blue color { R:65, G:105, B:225, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_royal_blue = SG_ROYAL_BLUE; +/* Saddle Brown color { R:139, G:69, B:19, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_saddle_brown = SG_SADDLE_BROWN; +/* Salmon color { R:250, G:128, B:114, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_salmon = SG_SALMON; +/* Sandy Brown color { R:244, G:164, B:96, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_sandy_brown = SG_SANDY_BROWN; +/* Sea Green color { R:46, G:139, B:87, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_sea_green = SG_SEA_GREEN; +/* Seashell color { R:255, G:245, B:238, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_seashell = SG_SEASHELL; +/* Sienna color { R:160, G:82, B:45, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_sienna = SG_SIENNA; +/* Silver color { R:192, G:192, B:192, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_silver = SG_SILVER; +/* Sky Blue color { R:135, G:206, B:235, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_sky_blue = SG_SKY_BLUE; +/* Slate Blue color { R:106, G:90, B:205, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_slate_blue = SG_SLATE_BLUE; +/* Slate Gray color { R:112, G:128, B:144, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_slate_gray = SG_SLATE_GRAY; +/* Snow color { R:255, G:250, B:250, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_snow = SG_SNOW; +/* Spring Green color { R:0, G:255, B:127, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_spring_green = SG_SPRING_GREEN; +/* Steel Blue color { R:70, G:130, B:180, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_steel_blue = SG_STEEL_BLUE; +/* Tan color { R:210, G:180, B:140, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_tan = SG_TAN; +/* Teal color { R:0, G:128, B:128, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_teal = SG_TEAL; +/* Thistle color { R:216, G:191, B:216, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_thistle = SG_THISTLE; +/* Tomato color { R:255, G:99, B:71, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_tomato = SG_TOMATO; +/* Transparent color { R:0, G:0, B:0, A:0 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_transparent = SG_TRANSPARENT; +/* Turquoise color { R:64, G:224, B:208, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_turquoise = SG_TURQUOISE; +/* Violet color { R:238, G:130, B:238, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_violet = SG_VIOLET; +/* Wheat color { R:245, G:222, B:179, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_wheat = SG_WHEAT; +/* White color { R:255, G:255, B:255, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_white = SG_WHITE; +/* White Smoke color { R:245, G:245, B:245, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_white_smoke = SG_WHITE_SMOKE; +/* Yellow color { R:255, G:255, B:0, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_yellow = SG_YELLOW; +/* Yellow Green color { R:154, G:205, B:50, A:255 } */ +static SOKOL_COLOR_CONSTEXPR sg_color sg_yellow_green = SG_YELLOW_GREEN; + +/* Alice Blue color { R:240, G:248, B:255, A:255 } */ +#define SG_ALICE_BLUE_RGBA32 0xF0F8FFFF +/* Antique White color { R:250, G:235, B:215, A:255 } */ +#define SG_ANTIQUE_WHITE_RGBA32 0xFAEBD7FF +/* Aqua color { R:0, G:255, B:255, A:255 } */ +#define SG_AQUA_RGBA32 0x00FFFFFF +/* Aquamarine color { R:127, G:255, B:212, A:255 } */ +#define SG_AQUAMARINE_RGBA32 0x7FFFD4FF +/* Azure color { R:240, G:255, B:255, A:255 } */ +#define SG_AZURE_RGBA32 0xF0FFFFFF +/* Beige color { R:245, G:245, B:220, A:255 } */ +#define SG_BEIGE_RGBA32 0xF5F5DCFF +/* Bisque color { R:255, G:228, B:196, A:255 } */ +#define SG_BISQUE_RGBA32 0xFFE4C4FF +/* Black color { R:0, G:0, B:0, A:255 } */ +#define SG_BLACK_RGBA32 0x000000FF +/* Blanched Almond color { R:255, G:235, B:205, A:255 } */ +#define SG_BLANCHED_ALMOND_RGBA32 0xFFEBCDFF +/* Blue color { R:0, G:0, B:255, A:255 } */ +#define SG_BLUE_RGBA32 0x0000FFFF +/* Blue Violet color { R:138, G:43, B:226, A:255 } */ +#define SG_BLUE_VIOLET_RGBA32 0x8A2BE2FF +/* Brown color { R:165, G:42, B:42, A:255 } */ +#define SG_BROWN_RGBA32 0xA52A2AFF +/* Burlywood color { R:222, G:184, B:135, A:255 } */ +#define SG_BURLYWOOD_RGBA32 0xDEB887FF +/* Cadet Blue color { R:95, G:158, B:160, A:255 } */ +#define SG_CADET_BLUE_RGBA32 0x5F9EA0FF +/* Chartreuse color { R:127, G:255, B:0, A:255 } */ +#define SG_CHARTREUSE_RGBA32 0x7FFF00FF +/* Chocolate color { R:210, G:105, B:30, A:255 } */ +#define SG_CHOCOLATE_RGBA32 0xD2691EFF +/* Coral color { R:255, G:127, B:80, A:255 } */ +#define SG_CORAL_RGBA32 0xFF7F50FF +/* Cornflower Blue color { R:100, G:149, B:237, A:255 } */ +#define SG_CORNFLOWER_BLUE_RGBA32 0x6495EDFF +/* Cornsilk color { R:255, G:248, B:220, A:255 } */ +#define SG_CORNSILK_RGBA32 0xFFF8DCFF +/* Crimson color { R:220, G:20, B:60, A:255 } */ +#define SG_CRIMSON_RGBA32 0xDC143CFF +/* Cyan color { R:0, G:255, B:255, A:255 } */ +#define SG_CYAN_RGBA32 0x00FFFFFF +/* Dark Blue color { R:0, G:0, B:139, A:255 } */ +#define SG_DARK_BLUE_RGBA32 0x00008BFF +/* Dark Cyan color { R:0, G:139, B:139, A:255 } */ +#define SG_DARK_CYAN_RGBA32 0x008B8BFF +/* Dark Goldenrod color { R:184, G:134, B:11, A:255 } */ +#define SG_DARK_GOLDENROD_RGBA32 0xB8860BFF +/* Dark Gray color { R:169, G:169, B:169, A:255 } */ +#define SG_DARK_GRAY_RGBA32 0xA9A9A9FF +/* Dark Green color { R:0, G:100, B:0, A:255 } */ +#define SG_DARK_GREEN_RGBA32 0x006400FF +/* Dark Khaki color { R:189, G:183, B:107, A:255 } */ +#define SG_DARK_KHAKI_RGBA32 0xBDB76BFF +/* Dark Magenta color { R:139, G:0, B:139, A:255 } */ +#define SG_DARK_MAGENTA_RGBA32 0x8B008BFF +/* Dark Olive Green color { R:85, G:107, B:47, A:255 } */ +#define SG_DARK_OLIVE_GREEN_RGBA32 0x556B2FFF +/* Dark Orange color { R:255, G:140, B:0, A:255 } */ +#define SG_DARK_ORANGE_RGBA32 0xFF8C00FF +/* Dark Orchid color { R:153, G:50, B:204, A:255 } */ +#define SG_DARK_ORCHID_RGBA32 0x9932CCFF +/* Dark Red color { R:139, G:0, B:0, A:255 } */ +#define SG_DARK_RED_RGBA32 0x8B0000FF +/* Dark Salmon color { R:233, G:150, B:122, A:255 } */ +#define SG_DARK_SALMON_RGBA32 0xE9967AFF +/* Dark Sea Green color { R:143, G:188, B:143, A:255 } */ +#define SG_DARK_SEA_GREEN_RGBA32 0x8FBC8FFF +/* Dark Slate Blue color { R:72, G:61, B:139, A:255 } */ +#define SG_DARK_SLATE_BLUE_RGBA32 0x483D8BFF +/* Dark Slate Gray color { R:47, G:79, B:79, A:255 } */ +#define SG_DARK_SLATE_GRAY_RGBA32 0x2F4F4FFF +/* Dark Turquoise color { R:0, G:206, B:209, A:255 } */ +#define SG_DARK_TURQUOISE_RGBA32 0x00CED1FF +/* Dark Violet color { R:148, G:0, B:211, A:255 } */ +#define SG_DARK_VIOLET_RGBA32 0x9400D3FF +/* Deep Pink color { R:255, G:20, B:147, A:255 } */ +#define SG_DEEP_PINK_RGBA32 0xFF1493FF +/* Deep Sky Blue color { R:0, G:191, B:255, A:255 } */ +#define SG_DEEP_SKY_BLUE_RGBA32 0x00BFFFFF +/* Dim Gray color { R:105, G:105, B:105, A:255 } */ +#define SG_DIM_GRAY_RGBA32 0x696969FF +/* Dodger Blue color { R:30, G:144, B:255, A:255 } */ +#define SG_DODGER_BLUE_RGBA32 0x1E90FFFF +/* Firebrick color { R:178, G:34, B:34, A:255 } */ +#define SG_FIREBRICK_RGBA32 0xB22222FF +/* Floral White color { R:255, G:250, B:240, A:255 } */ +#define SG_FLORAL_WHITE_RGBA32 0xFFFAF0FF +/* Forest Green color { R:34, G:139, B:34, A:255 } */ +#define SG_FOREST_GREEN_RGBA32 0x228B22FF +/* Fuchsia color { R:255, G:0, B:255, A:255 } */ +#define SG_FUCHSIA_RGBA32 0xFF00FFFF +/* Gainsboro color { R:220, G:220, B:220, A:255 } */ +#define SG_GAINSBORO_RGBA32 0xDCDCDCFF +/* Ghost White color { R:248, G:248, B:255, A:255 } */ +#define SG_GHOST_WHITE_RGBA32 0xF8F8FFFF +/* Gold color { R:255, G:215, B:0, A:255 } */ +#define SG_GOLD_RGBA32 0xFFD700FF +/* Goldenrod color { R:218, G:165, B:32, A:255 } */ +#define SG_GOLDENROD_RGBA32 0xDAA520FF +/* Gray color { R:190, G:190, B:190, A:255 } */ +#define SG_GRAY_RGBA32 0xBEBEBEFF +/* Web Gray color { R:128, G:128, B:128, A:255 } */ +#define SG_WEB_GRAY_RGBA32 0x808080FF +/* Green color { R:0, G:255, B:0, A:255 } */ +#define SG_GREEN_RGBA32 0x00FF00FF +/* Web Green color { R:0, G:128, B:0, A:255 } */ +#define SG_WEB_GREEN_RGBA32 0x008000FF +/* Green Yellow color { R:173, G:255, B:47, A:255 } */ +#define SG_GREEN_YELLOW_RGBA32 0xADFF2FFF +/* Honeydew color { R:240, G:255, B:240, A:255 } */ +#define SG_HONEYDEW_RGBA32 0xF0FFF0FF +/* Hot Pink color { R:255, G:105, B:180, A:255 } */ +#define SG_HOT_PINK_RGBA32 0xFF69B4FF +/* Indian Red color { R:205, G:92, B:92, A:255 } */ +#define SG_INDIAN_RED_RGBA32 0xCD5C5CFF +/* Indigo color { R:75, G:0, B:130, A:255 } */ +#define SG_INDIGO_RGBA32 0x4B0082FF +/* Ivory color { R:255, G:255, B:240, A:255 } */ +#define SG_IVORY_RGBA32 0xFFFFF0FF +/* Khaki color { R:240, G:230, B:140, A:255 } */ +#define SG_KHAKI_RGBA32 0xF0E68CFF +/* Lavender color { R:230, G:230, B:250, A:255 } */ +#define SG_LAVENDER_RGBA32 0xE6E6FAFF +/* Lavender Blush color { R:255, G:240, B:245, A:255 } */ +#define SG_LAVENDER_BLUSH_RGBA32 0xFFF0F5FF +/* Lawn Green color { R:124, G:252, B:0, A:255 } */ +#define SG_LAWN_GREEN_RGBA32 0x7CFC00FF +/* Lemon Chiffon color { R:255, G:250, B:205, A:255 } */ +#define SG_LEMON_CHIFFON_RGBA32 0xFFFACDFF +/* Light Blue color { R:173, G:216, B:230, A:255 } */ +#define SG_LIGHT_BLUE_RGBA32 0xADD8E6FF +/* Light Coral color { R:240, G:128, B:128, A:255 } */ +#define SG_LIGHT_CORAL_RGBA32 0xF08080FF +/* Light Cyan color { R:224, G:255, B:255, A:255 } */ +#define SG_LIGHT_CYAN_RGBA32 0xE0FFFFFF +/* Light Goldenrod color { R:250, G:250, B:210, A:255 } */ +#define SG_LIGHT_GOLDENROD_RGBA32 0xFAFAD2FF +/* Light Gray color { R:211, G:211, B:211, A:255 } */ +#define SG_LIGHT_GRAY_RGBA32 0xD3D3D3FF +/* Light Green color { R:144, G:238, B:144, A:255 } */ +#define SG_LIGHT_GREEN_RGBA32 0x90EE90FF +/* Light Pink color { R:255, G:182, B:193, A:255 } */ +#define SG_LIGHT_PINK_RGBA32 0xFFB6C1FF +/* Light Salmon color { R:255, G:160, B:122, A:255 } */ +#define SG_LIGHT_SALMON_RGBA32 0xFFA07AFF +/* Light Sea Green color { R:32, G:178, B:170, A:255 } */ +#define SG_LIGHT_SEA_GREEN_RGBA32 0x20B2AAFF +/* Light Sky Blue color { R:135, G:206, B:250, A:255 } */ +#define SG_LIGHT_SKY_BLUE_RGBA32 0x87CEFAFF +/* Light Slate Gray color { R:119, G:136, B:153, A:255 } */ +#define SG_LIGHT_SLATE_GRAY_RGBA32 0x778899FF +/* Light Steel Blue color { R:176, G:196, B:222, A:255 } */ +#define SG_LIGHT_STEEL_BLUE_RGBA32 0xB0C4DEFF +/* Light Yellow color { R:255, G:255, B:224, A:255 } */ +#define SG_LIGHT_YELLOW_RGBA32 0xFFFFE0FF +/* Lime color { R:0, G:255, B:0, A:255 } */ +#define SG_LIME_RGBA32 0x00FF00FF +/* Lime Green color { R:50, G:205, B:50, A:255 } */ +#define SG_LIME_GREEN_RGBA32 0x32CD32FF +/* Linen color { R:250, G:240, B:230, A:255 } */ +#define SG_LINEN_RGBA32 0xFAF0E6FF +/* Magenta color { R:255, G:0, B:255, A:255 } */ +#define SG_MAGENTA_RGBA32 0xFF00FFFF +/* Maroon color { R:176, G:48, B:96, A:255 } */ +#define SG_MAROON_RGBA32 0xB03060FF +/* Web Maroon color { R:128, G:0, B:0, A:255 } */ +#define SG_WEB_MAROON_RGBA32 0x800000FF +/* Medium Aquamarine color { R:102, G:205, B:170, A:255 } */ +#define SG_MEDIUM_AQUAMARINE_RGBA32 0x66CDAAFF +/* Medium Blue color { R:0, G:0, B:205, A:255 } */ +#define SG_MEDIUM_BLUE_RGBA32 0x0000CDFF +/* Medium Orchid color { R:186, G:85, B:211, A:255 } */ +#define SG_MEDIUM_ORCHID_RGBA32 0xBA55D3FF +/* Medium Purple color { R:147, G:112, B:219, A:255 } */ +#define SG_MEDIUM_PURPLE_RGBA32 0x9370DBFF +/* Medium Sea Green color { R:60, G:179, B:113, A:255 } */ +#define SG_MEDIUM_SEA_GREEN_RGBA32 0x3CB371FF +/* Medium Slate Blue color { R:123, G:104, B:238, A:255 } */ +#define SG_MEDIUM_SLATE_BLUE_RGBA32 0x7B68EEFF +/* Medium Spring Green color { R:0, G:250, B:154, A:255 } */ +#define SG_MEDIUM_SPRING_GREEN_RGBA32 0x00FA9AFF +/* Medium Turquoise color { R:72, G:209, B:204, A:255 } */ +#define SG_MEDIUM_TURQUOISE_RGBA32 0x48D1CCFF +/* Medium Violet Red color { R:199, G:21, B:133, A:255 } */ +#define SG_MEDIUM_VIOLET_RED_RGBA32 0xC71585FF +/* Midnight Blue color { R:25, G:25, B:112, A:255 } */ +#define SG_MIDNIGHT_BLUE_RGBA32 0x191970FF +/* Mint Cream color { R:245, G:255, B:250, A:255 } */ +#define SG_MINT_CREAM_RGBA32 0xF5FFFAFF +/* Misty Rose color { R:255, G:228, B:225, A:255 } */ +#define SG_MISTY_ROSE_RGBA32 0xFFE4E1FF +/* Moccasin color { R:255, G:228, B:181, A:255 } */ +#define SG_MOCCASIN_RGBA32 0xFFE4B5FF +/* Navajo White color { R:255, G:222, B:173, A:255 } */ +#define SG_NAVAJO_WHITE_RGBA32 0xFFDEADFF +/* Navy Blue color { R:0, G:0, B:128, A:255 } */ +#define SG_NAVY_BLUE_RGBA32 0x000080FF +/* Old Lace color { R:253, G:245, B:230, A:255 } */ +#define SG_OLD_LACE_RGBA32 0xFDF5E6FF +/* Olive color { R:128, G:128, B:0, A:255 } */ +#define SG_OLIVE_RGBA32 0x808000FF +/* Olive Drab color { R:107, G:142, B:35, A:255 } */ +#define SG_OLIVE_DRAB_RGBA32 0x6B8E23FF +/* Orange color { R:255, G:165, B:0, A:255 } */ +#define SG_ORANGE_RGBA32 0xFFA500FF +/* Orange Red color { R:255, G:69, B:0, A:255 } */ +#define SG_ORANGE_RED_RGBA32 0xFF4500FF +/* Orchid color { R:218, G:112, B:214, A:255 } */ +#define SG_ORCHID_RGBA32 0xDA70D6FF +/* Pale Goldenrod color { R:238, G:232, B:170, A:255 } */ +#define SG_PALE_GOLDENROD_RGBA32 0xEEE8AAFF +/* Pale Green color { R:152, G:251, B:152, A:255 } */ +#define SG_PALE_GREEN_RGBA32 0x98FB98FF +/* Pale Turquoise color { R:175, G:238, B:238, A:255 } */ +#define SG_PALE_TURQUOISE_RGBA32 0xAFEEEEFF +/* Pale Violet Red color { R:219, G:112, B:147, A:255 } */ +#define SG_PALE_VIOLET_RED_RGBA32 0xDB7093FF +/* Papaya Whip color { R:255, G:239, B:213, A:255 } */ +#define SG_PAPAYA_WHIP_RGBA32 0xFFEFD5FF +/* Peach Puff color { R:255, G:218, B:185, A:255 } */ +#define SG_PEACH_PUFF_RGBA32 0xFFDAB9FF +/* Peru color { R:205, G:133, B:63, A:255 } */ +#define SG_PERU_RGBA32 0xCD853FFF +/* Pink color { R:255, G:192, B:203, A:255 } */ +#define SG_PINK_RGBA32 0xFFC0CBFF +/* Plum color { R:221, G:160, B:221, A:255 } */ +#define SG_PLUM_RGBA32 0xDDA0DDFF +/* Powder Blue color { R:176, G:224, B:230, A:255 } */ +#define SG_POWDER_BLUE_RGBA32 0xB0E0E6FF +/* Purple color { R:160, G:32, B:240, A:255 } */ +#define SG_PURPLE_RGBA32 0xA020F0FF +/* Web Purple color { R:128, G:0, B:128, A:255 } */ +#define SG_WEB_PURPLE_RGBA32 0x800080FF +/* Rebecca Purple color { R:102, G:51, B:153, A:255 } */ +#define SG_REBECCA_PURPLE_RGBA32 0x663399FF +/* Red color { R:255, G:0, B:0, A:255 } */ +#define SG_RED_RGBA32 0xFF0000FF +/* Rosy Brown color { R:188, G:143, B:143, A:255 } */ +#define SG_ROSY_BROWN_RGBA32 0xBC8F8FFF +/* Royal Blue color { R:65, G:105, B:225, A:255 } */ +#define SG_ROYAL_BLUE_RGBA32 0x4169E1FF +/* Saddle Brown color { R:139, G:69, B:19, A:255 } */ +#define SG_SADDLE_BROWN_RGBA32 0x8B4513FF +/* Salmon color { R:250, G:128, B:114, A:255 } */ +#define SG_SALMON_RGBA32 0xFA8072FF +/* Sandy Brown color { R:244, G:164, B:96, A:255 } */ +#define SG_SANDY_BROWN_RGBA32 0xF4A460FF +/* Sea Green color { R:46, G:139, B:87, A:255 } */ +#define SG_SEA_GREEN_RGBA32 0x2E8B57FF +/* Seashell color { R:255, G:245, B:238, A:255 } */ +#define SG_SEASHELL_RGBA32 0xFFF5EEFF +/* Sienna color { R:160, G:82, B:45, A:255 } */ +#define SG_SIENNA_RGBA32 0xA0522DFF +/* Silver color { R:192, G:192, B:192, A:255 } */ +#define SG_SILVER_RGBA32 0xC0C0C0FF +/* Sky Blue color { R:135, G:206, B:235, A:255 } */ +#define SG_SKY_BLUE_RGBA32 0x87CEEBFF +/* Slate Blue color { R:106, G:90, B:205, A:255 } */ +#define SG_SLATE_BLUE_RGBA32 0x6A5ACDFF +/* Slate Gray color { R:112, G:128, B:144, A:255 } */ +#define SG_SLATE_GRAY_RGBA32 0x708090FF +/* Snow color { R:255, G:250, B:250, A:255 } */ +#define SG_SNOW_RGBA32 0xFFFAFAFF +/* Spring Green color { R:0, G:255, B:127, A:255 } */ +#define SG_SPRING_GREEN_RGBA32 0x00FF7FFF +/* Steel Blue color { R:70, G:130, B:180, A:255 } */ +#define SG_STEEL_BLUE_RGBA32 0x4682B4FF +/* Tan color { R:210, G:180, B:140, A:255 } */ +#define SG_TAN_RGBA32 0xD2B48CFF +/* Teal color { R:0, G:128, B:128, A:255 } */ +#define SG_TEAL_RGBA32 0x008080FF +/* Thistle color { R:216, G:191, B:216, A:255 } */ +#define SG_THISTLE_RGBA32 0xD8BFD8FF +/* Tomato color { R:255, G:99, B:71, A:255 } */ +#define SG_TOMATO_RGBA32 0xFF6347FF +/* Transparent color { R:0, G:0, B:0, A:0 } */ +#define SG_TRANSPARENT_RGBA32 0x00000000 +/* Turquoise color { R:64, G:224, B:208, A:255 } */ +#define SG_TURQUOISE_RGBA32 0x40E0D0FF +/* Violet color { R:238, G:130, B:238, A:255 } */ +#define SG_VIOLET_RGBA32 0xEE82EEFF +/* Wheat color { R:245, G:222, B:179, A:255 } */ +#define SG_WHEAT_RGBA32 0xF5DEB3FF +/* White color { R:255, G:255, B:255, A:255 } */ +#define SG_WHITE_RGBA32 0xFFFFFFFF +/* White Smoke color { R:245, G:245, B:245, A:255 } */ +#define SG_WHITE_SMOKE_RGBA32 0xF5F5F5FF +/* Yellow color { R:255, G:255, B:0, A:255 } */ +#define SG_YELLOW_RGBA32 0xFFFF00FF +/* Yellow Green color { R:154, G:205, B:50, A:255 } */ +#define SG_YELLOW_GREEN_RGBA32 0x9ACD32FF + +#ifdef __cplusplus +} /* extern "C" */ + +inline sg_color sg_make_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + return sg_make_color_4b(r, g, b, a); +} + +inline sg_color sg_make_color(uint32_t rgba) { + return sg_make_color_1i(rgba); +} + +inline sg_color sg_color_lerp(const sg_color& color_a, const sg_color& color_b, float amount) { + return sg_color_lerp(&color_a, &color_b, amount); +} + +inline sg_color sg_color_lerp_precise(const sg_color& color_a, const sg_color& color_b, float amount) { + return sg_color_lerp_precise(&color_a, &color_b, amount); +} + +inline sg_color sg_color_multiply(const sg_color& color, float scale) { + return sg_color_multiply(&color, scale); +} + +#endif /* __cplusplus */ + +#endif /* SOKOL_COLOR_INCLUDED */ + +/*-- IMPLEMENTATION ----------------------------------------------------------*/ +#ifdef SOKOL_COLOR_IMPL +#define SOKOL_COLOR_IMPL_INCLUDED (1) + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif + +static inline float _sg_color_clamp(float v, float low, float high) { + if (v < low) { + return low; + } else if (v > high) { + return high; + } + return v; +} + +static inline float _sg_color_lerp(float a, float b, float amount) { + return a + (b - a) * amount; +} + +static inline float _sg_color_lerp_precise(float a, float b, float amount) { + return ((1.0f - amount) * a) + (b * amount); +} + +SOKOL_API_IMPL sg_color sg_make_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + sg_color result; + result.r = r / 255.0f; + result.g = g / 255.0f; + result.b = b / 255.0f; + result.a = a / 255.0f; + return result; +} + +SOKOL_API_IMPL sg_color sg_make_color_1i(uint32_t rgba) { + return sg_make_color_4b( + (uint8_t)(rgba >> 24), + (uint8_t)(rgba >> 16), + (uint8_t)(rgba >> 8), + (uint8_t)(rgba >> 0) + ); +} + +SOKOL_API_IMPL sg_color sg_color_lerp(const sg_color* color_a, const sg_color* color_b, float amount) { + SOKOL_ASSERT(color_a); + SOKOL_ASSERT(color_b); + amount = _sg_color_clamp(amount, 0.0f, 1.0f); + sg_color result; + result.r = _sg_color_lerp(color_a->r, color_b->r, amount); + result.g = _sg_color_lerp(color_a->g, color_b->g, amount); + result.b = _sg_color_lerp(color_a->b, color_b->b, amount); + result.a = _sg_color_lerp(color_a->a, color_b->a, amount); + return result; +} + +SOKOL_API_IMPL sg_color sg_color_lerp_precise(const sg_color* color_a, const sg_color* color_b, float amount) { + SOKOL_ASSERT(color_a); + SOKOL_ASSERT(color_b); + amount = _sg_color_clamp(amount, 0.0f, 1.0f); + sg_color result; + result.r = _sg_color_lerp_precise(color_a->r, color_b->r, amount); + result.g = _sg_color_lerp_precise(color_a->g, color_b->g, amount); + result.b = _sg_color_lerp_precise(color_a->b, color_b->b, amount); + result.a = _sg_color_lerp_precise(color_a->a, color_b->a, amount); + return result; +} + +SOKOL_API_IMPL sg_color sg_color_multiply(const sg_color* color, float scale) { + SOKOL_ASSERT(color); + sg_color result; + result.r = color->r * scale; + result.g = color->g * scale; + result.b = color->b * scale; + result.a = color->a * scale; + return result; +} + +#endif /* SOKOL_COLOR_IMPL */ diff --git a/thirdparty/sokol/util/sokol_debugtext.h b/thirdparty/sokol/util/sokol_debugtext.h new file mode 100644 index 0000000..12ca7a6 --- /dev/null +++ b/thirdparty/sokol/util/sokol_debugtext.h @@ -0,0 +1,4567 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_DEBUGTEXT_IMPL) +#define SOKOL_DEBUGTEXT_IMPL +#endif +#ifndef SOKOL_DEBUGTEXT_INCLUDED +/* + sokol_debugtext.h - simple ASCII debug text rendering on top of sokol_gfx.h + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_DEBUGTEXT_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + The following defines are used by the implementation to select the + platform-specific embedded shader code (these are the same defines as + used by sokol_gfx.h and sokol_app.h): + + SOKOL_GLCORE + SOKOL_GLES3 + SOKOL_D3D11 + SOKOL_METAL + SOKOL_WGPU + + ...optionally provide the following macros to override defaults: + + SOKOL_VSNPRINTF - the function name of an alternative vsnprintf() function (default: vsnprintf) + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_DEBUGTEXT_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_DEBUGTEXT_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + + If sokol_debugtext.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_DEBUGTEXT_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + Include the following headers before including sokol_debugtext.h: + + sokol_gfx.h + + FEATURES AND CONCEPTS + ===================== + - renders 8-bit ASCII text as fixed-size 8x8 pixel characters + - comes with 6 embedded 8-bit home computer fonts (each taking up 2 KBytes) + - easily plug in your own fonts + - create multiple contexts for rendering text in different layers or render passes + + STEP BY STEP + ============ + + --- to initialize sokol-debugtext, call sdtx_setup() *after* initializing + sokol-gfx: + + sdtx_setup(&(sdtx_desc_t){ ... }); + + To see any warnings and errors, you should always install a logging callback. + The easiest way is via sokol_log.h: + + #include "sokol_log.h" + + sdtx_setup(&(sdtx_desc_t){ + .logger.func = slog_func, + }); + + --- configure sokol-debugtext by populating the sdtx_desc_t struct: + + .context_pool_size (default: 8) + The max number of text contexts that can be created. + + .printf_buf_size (default: 4096) + The size of the internal text formatting buffer used by + sdtx_printf() and sdtx_vprintf(). + + .fonts (default: none) + An array of sdtx_font_desc_t structs used to configure the + fonts that can be used for rendering. To use all builtin + fonts call sdtx_setup() like this (in C99): + + sdtx_setup(&(sdtx_desc_t){ + .fonts = { + [0] = sdtx_font_kc853(), + [1] = sdtx_font_kc854(), + [2] = sdtx_font_z1013(), + [3] = sdtx_font_cpc(), + [4] = sdtx_font_c64(), + [5] = sdtx_font_oric() + } + }); + + For documentation on how to use you own font data, search + below for "USING YOUR OWN FONT DATA". + + .context + The setup parameters for the default text context. This will + be active right after sdtx_setup(), or when calling + sdtx_set_context(SDTX_DEFAULT_CONTEXT): + + .max_commands (default: 4096) + The max number of render commands that can be recorded + into the internal command buffer. This directly translates + to the number of render layer changes in a single frame. + + .char_buf_size (default: 4096) + The number of characters that can be rendered per frame in this + context, defines the size of an internal fixed-size vertex + buffer. Any additional characters will be silently ignored. + + .canvas_width (default: 640) + .canvas_height (default: 480) + The 'virtual canvas size' in pixels. This defines how big + characters will be rendered relative to the default framebuffer + dimensions. Each character occupies a grid of 8x8 'virtual canvas + pixels' (so a virtual canvas size of 640x480 means that 80x60 characters + fit on the screen). For rendering in a resizeable window, you + should dynamically update the canvas size in each frame by + calling sdtx_canvas(w, h). + + .tab_width (default: 4) + The width of a tab character in number of character cells. + + .color_format (default: 0) + .depth_format (default: 0) + .sample_count (default: 0) + The pixel format description for the default context needed + for creating the context's sg_pipeline object. When + rendering to the default framebuffer you can leave those + zero-initialized, in this case the proper values will be + filled in by sokol-gfx. You only need to provide non-default + values here when rendering to render targets with different + pixel format attributes than the default framebuffer. + + --- Before starting to render text, optionally call sdtx_canvas() to + dynamically resize the virtual canvas. This is recommended when + rendering to a resizeable window. The virtual canvas size can + also be used to scale text in relation to the display resolution. + + Examples when using sokol-app: + + - to render characters at 8x8 'physical pixels': + + sdtx_canvas(sapp_width(), sapp_height()); + + - to render characters at 16x16 physical pixels: + + sdtx_canvas(sapp_width()/2.0f, sapp_height()/2.0f); + + Do *not* use integer math here, since this will not look nice + when the render target size isn't divisible by 2. + + --- Optionally define the origin for the character grid with: + + sdtx_origin(x, y); + + The provided coordinates are in character grid cells, not in + virtual canvas pixels. E.g. to set the origin to 2 character tiles + from the left and top border: + + sdtx_origin(2, 2); + + You can define fractions, e.g. to start rendering half + a character tile from the top-left corner: + + sdtx_origin(0.5f, 0.5f); + + --- Optionally set a different font by calling: + + sdtx_font(font_index) + + sokol-debugtext provides 8 font slots which can be populated + with the builtin fonts or with user-provided font data, so + 'font_index' must be a number from 0 to 7. + + --- Position the text cursor with one of the following calls. All arguments + are in character grid cells as floats and relative to the + origin defined with sdtx_origin(): + + sdtx_pos(x, y) - sets absolute cursor position + sdtx_pos_x(x) - only set absolute x cursor position + sdtx_pos_y(y) - only set absolute y cursor position + + sdtx_move(x, y) - move cursor relative in x and y direction + sdtx_move_x(x) - move cursor relative only in x direction + sdtx_move_y(y) - move cursor relative only in y direction + + sdtx_crlf() - set cursor to beginning of next line + (same as sdtx_pos_x(0) + sdtx_move_y(1)) + sdtx_home() - resets the cursor to the origin + (same as sdtx_pos(0, 0)) + + --- Set a new text color with any of the following functions: + + sdtx_color3b(r, g, b) - RGB 0..255, A=255 + sdtx_color3f(r, g, b) - RGB 0.0f..1.0f, A=1.0f + sdtx_color4b(r, g, b, a) - RGBA 0..255 + sdtx_color4f(r, g, b, a) - RGBA 0.0f..1.0f + sdtx_color1i(uint32_t rgba) - ABGR (0xAABBGGRR) + + --- Output 8-bit ASCII text with the following functions: + + sdtx_putc(c) - output a single character + + sdtx_puts(str) - output a null-terminated C string, note that + this will *not* append a newline (so it behaves + differently than the CRT's puts() function) + + sdtx_putr(str, len) - 'put range' output the first 'len' characters of + a C string or until the zero character is encountered + + sdtx_printf(fmt, ...) - output with printf-formatting, note that you + can inject your own printf-compatible function + by overriding the SOKOL_VSNPRINTF define before + including the implementation + + sdtx_vprintf(fmt, args) - same as sdtx_printf() but with the arguments + provided in a va_list + + - Note that the text will not yet be rendered, only recorded for rendering + at a later time, the actual rendering happens when sdtx_draw() is called + inside a sokol-gfx render pass. + - This means also you can output text anywhere in the frame, it doesn't + have to be inside a render pass. + - Note that character codes <32 are reserved as control characters + and won't render anything. Currently only the following control + characters are implemented: + + \r - carriage return (same as sdtx_pos_x(0)) + \n - carriage return + line feed (same as stdx_crlf()) + \t - a tab character + + --- You can 'record' text into render layers, this allows to mix/interleave + sokol-debugtext rendering with other rendering operations inside + sokol-gfx render passes. To start recording text into a different render + layer, call: + + sdtx_layer(int layer_id) + + ...outside a sokol-gfx render pass. + + --- finally, from within a sokol-gfx render pass, call: + + sdtx_draw() + + ...for non-layered rendering, or to draw a specific layer: + + sdtx_draw_layer(int layer_id) + + NOTE that sdtx_draw() is equivalent to: + + sdtx_draw_layer(0) + + ...so sdtx_draw() will *NOT* render all text layers, instead it will + only render the 'default layer' 0. + + --- at the end of a frame (defined by the call to sg_commit()), sokol-debugtext + will rewind all contexts: + + - the internal vertex index is set to 0 + - the internal command index is set to 0 + - the current layer id is set to 0 + - the current font is set to 0 + - the cursor position is reset + + + RENDERING WITH MULTIPLE CONTEXTS + ================================ + Use multiple text contexts if you need to render debug text in different + sokol-gfx render passes, or want to render text to different layers + in the same render pass, each with its own set of parameters. + + To create a new text context call: + + sdtx_context ctx = sdtx_make_context(&(sdtx_context_desc_t){ ... }); + + The creation parameters in the sdtx_context_desc_t struct are the same + as already described above in the sdtx_setup() function: + + .char_buf_size -- max number of characters rendered in one frame, default: 4096 + .canvas_width -- the initial virtual canvas width, default: 640 + .canvas_height -- the initial virtual canvas height, default: 400 + .tab_width -- tab width in number of characters, default: 4 + .color_format -- color pixel format of target render pass + .depth_format -- depth pixel format of target render pass + .sample_count -- MSAA sample count of target render pass + + To make a new context the active context, call: + + sdtx_set_context(ctx) + + ...and after that call the text output functions as described above, and + finally, inside a sokol-gfx render pass, call sdtx_draw() to actually + render the text for this context. + + A context keeps track of the following parameters: + + - the active font + - the virtual canvas size + - the origin position + - the current cursor position + - the current tab width + - the current color + - and the current layer-id + + You can get the currently active context with: + + sdtx_get_context() + + To make the default context current, call sdtx_set_context() with the + special SDTX_DEFAULT_CONTEXT handle: + + sdtx_set_context(SDTX_DEFAULT_CONTEXT) + + Alternatively, use the function sdtx_default_context() to get the default + context handle: + + sdtx_set_context(sdtx_default_context()); + + To destroy a context, call: + + sdtx_destroy_context(ctx) + + If a context is set as active that no longer exists, all sokol-debugtext + functions that require an active context will silently fail. + + You can directly draw the recorded text in a specific context without + setting the active context: + + sdtx_context_draw(ctx) + sdtx_context_draw_layer(ctx, layer_id) + + USING YOUR OWN FONT DATA + ======================== + + Instead of the built-in fonts you can also plug your own font data + into sokol-debugtext by providing one or several sdtx_font_desc_t + structures in the sdtx_setup call. + + For instance to use a built-in font at slot 0, and a user-font at + font slot 1, the sdtx_setup() call might look like this: + + sdtx_setup(&sdtx_desc_t){ + .fonts = { + [0] = sdtx_font_kc853(), + [1] = { + .data = { + .ptr = my_font_data, + .size = sizeof(my_font_data) + }, + .first_char = ..., + .last_char = ... + } + } + }); + + Where 'my_font_data' is a byte array where every character is described + by 8 bytes arranged like this: + + bits + 7 6 5 4 3 2 1 0 + . . . X X . . . byte 0: 0x18 + . . X X X X . . byte 1: 0x3C + . X X . . X X . byte 2: 0x66 + . X X . . X X . byte 3: 0x66 + . X X X X X X . byte 4: 0x7E + . X X . . X X . byte 5: 0x66 + . X X . . X X . byte 6: 0x66 + . . . . . . . . byte 7: 0x00 + + A complete font consists of 256 characters, resulting in 2048 bytes for + the font data array (but note that the character codes 0..31 will never + be rendered). + + If you provide such a complete font data array, you can drop the .first_char + and .last_char initialization parameters since those default to 0 and 255, + note that you can also use the SDTX_RANGE() helper macro to build the + .data item: + + sdtx_setup(&sdtx_desc_t){ + .fonts = { + [0] = sdtx_font_kc853(), + [1] = { + .data = SDTX_RANGE(my_font_data) + } + } + }); + + If the font doesn't define all 256 character tiles, or you don't need an + entire 256-character font and want to save a couple of bytes, use the + .first_char and .last_char initialization parameters to define a sub-range. + For instance if the font only contains the characters between the Space + (ASCII code 32) and uppercase character 'Z' (ASCII code 90): + + sdtx_setup(&sdtx_desc_t){ + .fonts = { + [0] = sdtx_font_kc853(), + [1] = { + .data = SDTX_RANGE(my_font_data), + .first_char = 32, // could also write ' ' + .last_char = 90 // could also write 'Z' + } + } + }); + + Character tiles that haven't been defined in the font will be rendered + as a solid 8x8 quad. + + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + sdtx_setup(&(sdtx_desc_t){ + // ... + .allocator = { + .alloc_fn = my_alloc, + .free_fn = my_free, + .user_data = ...; + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call, + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + sdtx_setup(&(sdtx_desc_t){ + // ... + .logger.func = slog_func + }); + + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'sdtx' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SDTX_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_debugtext.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-debugtext like this: + + sdtx_setup(&(sdtx_desc_t){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); + + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. + + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2020 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_DEBUGTEXT_INCLUDED (1) +#include +#include +#include // size_t +#include // va_list + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_debugtext.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_DEBUGTEXT_API_DECL) +#define SOKOL_DEBUGTEXT_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_DEBUGTEXT_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_DEBUGTEXT_IMPL) +#define SOKOL_DEBUGTEXT_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_DEBUGTEXT_API_DECL __declspec(dllimport) +#else +#define SOKOL_DEBUGTEXT_API_DECL extern +#endif +#endif + +#if defined(__GNUC__) +#define SOKOL_DEBUGTEXT_PRINTF_ATTR __attribute__((format(printf, 1, 2))) +#else +#define SOKOL_DEBUGTEXT_PRINTF_ATTR +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + sdtx_log_item_t + + Log items are defined via X-Macros, and expanded to an + enum 'sdtx_log_item' - and in debug mode only - corresponding strings. + + Used as parameter in the logging callback. +*/ +#define _SDTX_LOG_ITEMS \ + _SDTX_LOGITEM_XMACRO(OK, "Ok") \ + _SDTX_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SDTX_LOGITEM_XMACRO(ADD_COMMIT_LISTENER_FAILED, "sg_add_commit_listener() failed") \ + _SDTX_LOGITEM_XMACRO(COMMAND_BUFFER_FULL, "command buffer full (adjust via sdtx_context_desc_t.max_commands)") \ + _SDTX_LOGITEM_XMACRO(CONTEXT_POOL_EXHAUSTED, "context pool exhausted (adjust via sdtx_desc_t.context_pool_size)") \ + _SDTX_LOGITEM_XMACRO(CANNOT_DESTROY_DEFAULT_CONTEXT, "cannot destroy default context") \ + +#define _SDTX_LOGITEM_XMACRO(item,msg) SDTX_LOGITEM_##item, +typedef enum sdtx_log_item_t { + _SDTX_LOG_ITEMS +} sdtx_log_item_t; +#undef _SDTX_LOGITEM_XMACRO + +/* + sdtx_logger_t + + Used in sdtx_desc_t to provide a custom logging and error reporting + callback to sokol-debugtext. +*/ +typedef struct sdtx_logger_t { + void (*func)( + const char* tag, // always "sdtx" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SDTX_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_debugtext.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} sdtx_logger_t; + +/* a rendering context handle */ +typedef struct sdtx_context { uint32_t id; } sdtx_context; + +/* the default context handle */ +static const sdtx_context SDTX_DEFAULT_CONTEXT = { 0x00010001 }; + +/* + sdtx_range is a pointer-size-pair struct used to pass memory + blobs into sokol-debugtext. When initialized from a value type + (array or struct), use the SDTX_RANGE() macro to build + an sdtx_range struct. +*/ +typedef struct sdtx_range { + const void* ptr; + size_t size; +} sdtx_range; + +// disabling this for every includer isn't great, but the warning is also quite pointless +#if defined(_MSC_VER) +#pragma warning(disable:4221) /* /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y' */ +#pragma warning(disable:4204) /* VS2015: nonstandard extension used: non-constant aggregate initializer */ +#endif +#if defined(__cplusplus) +#define SDTX_RANGE(x) sdtx_range{ &x, sizeof(x) } +#else +#define SDTX_RANGE(x) (sdtx_range){ &x, sizeof(x) } +#endif + +/* + sdtx_font_desc_t + + Describes the pixel data of a font. A font consists of up to + 256 8x8 character tiles, where each character tile is described + by 8 consecutive bytes, each byte describing 8 pixels. + + For instance the character 'A' could look like this (this is also + how most home computers used to describe their fonts in ROM): + + bits + 7 6 5 4 3 2 1 0 + . . . X X . . . byte 0: 0x18 + . . X X X X . . byte 1: 0x3C + . X X . . X X . byte 2: 0x66 + . X X . . X X . byte 3: 0x66 + . X X X X X X . byte 4: 0x7E + . X X . . X X . byte 5: 0x66 + . X X . . X X . byte 6: 0x66 + . . . . . . . . byte 7: 0x00 + */ +#define SDTX_MAX_FONTS (8) + +typedef struct sdtx_font_desc_t { + sdtx_range data; // pointer to and size of font pixel data + uint8_t first_char; // first character index in font pixel data + uint8_t last_char; // last character index in font pixel data, inclusive (default: 255) +} sdtx_font_desc_t; + +/* + sdtx_context_desc_t + + Describes the initialization parameters of a rendering context. Creating + additional rendering contexts is useful if you want to render in + different sokol-gfx rendering passes, or when rendering several layers + of text. +*/ +typedef struct sdtx_context_desc_t { + int max_commands; // max number of draw commands, each layer transition counts as a command, default: 4096 + int char_buf_size; // max number of characters rendered in one frame, default: 4096 + float canvas_width; // the initial virtual canvas width, default: 640 + float canvas_height; // the initial virtual canvas height, default: 400 + int tab_width; // tab width in number of characters, default: 4 + sg_pixel_format color_format; // color pixel format of target render pass + sg_pixel_format depth_format; // depth pixel format of target render pass + int sample_count; // MSAA sample count of target render pass +} sdtx_context_desc_t; + +/* + sdtx_allocator_t + + Used in sdtx_desc_t to provide custom memory-alloc and -free functions + to sokol_debugtext.h. If memory management should be overridden, both the + alloc_fn and free_fn function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct sdtx_allocator_t { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} sdtx_allocator_t; + +/* + sdtx_desc_t + + Describes the sokol-debugtext API initialization parameters. Passed + to the sdtx_setup() function. + + NOTE: to populate the fonts item array with builtin fonts, use any + of the following functions: + + sdtx_font_kc853() + sdtx_font_kc854() + sdtx_font_z1013() + sdtx_font_cpc() + sdtx_font_c64() + sdtx_font_oric() +*/ +typedef struct sdtx_desc_t { + int context_pool_size; // max number of rendering contexts that can be created, default: 8 + int printf_buf_size; // size of internal buffer for snprintf(), default: 4096 + sdtx_font_desc_t fonts[SDTX_MAX_FONTS]; // up to 8 fonts descriptions + sdtx_context_desc_t context; // the default context creation parameters + sdtx_allocator_t allocator; // optional memory allocation overrides (default: malloc/free) + sdtx_logger_t logger; // optional log override function (default: NO LOGGING) +} sdtx_desc_t; + +/* initialization/shutdown */ +SOKOL_DEBUGTEXT_API_DECL void sdtx_setup(const sdtx_desc_t* desc); +SOKOL_DEBUGTEXT_API_DECL void sdtx_shutdown(void); + +/* builtin font data (use to populate sdtx_desc.font[]) */ +SOKOL_DEBUGTEXT_API_DECL sdtx_font_desc_t sdtx_font_kc853(void); +SOKOL_DEBUGTEXT_API_DECL sdtx_font_desc_t sdtx_font_kc854(void); +SOKOL_DEBUGTEXT_API_DECL sdtx_font_desc_t sdtx_font_z1013(void); +SOKOL_DEBUGTEXT_API_DECL sdtx_font_desc_t sdtx_font_cpc(void); +SOKOL_DEBUGTEXT_API_DECL sdtx_font_desc_t sdtx_font_c64(void); +SOKOL_DEBUGTEXT_API_DECL sdtx_font_desc_t sdtx_font_oric(void); + +/* context functions */ +SOKOL_DEBUGTEXT_API_DECL sdtx_context sdtx_make_context(const sdtx_context_desc_t* desc); +SOKOL_DEBUGTEXT_API_DECL void sdtx_destroy_context(sdtx_context ctx); +SOKOL_DEBUGTEXT_API_DECL void sdtx_set_context(sdtx_context ctx); +SOKOL_DEBUGTEXT_API_DECL sdtx_context sdtx_get_context(void); +SOKOL_DEBUGTEXT_API_DECL sdtx_context sdtx_default_context(void); + +/* drawing functions (call inside sokol-gfx render pass) */ +SOKOL_DEBUGTEXT_API_DECL void sdtx_draw(void); +SOKOL_DEBUGTEXT_API_DECL void sdtx_context_draw(sdtx_context ctx); +SOKOL_DEBUGTEXT_API_DECL void sdtx_draw_layer(int layer_id); +SOKOL_DEBUGTEXT_API_DECL void sdtx_context_draw_layer(sdtx_context ctx, int layer_id); + +/* switch render layer */ +SOKOL_DEBUGTEXT_API_DECL void sdtx_layer(int layer_id); + +/* switch to a different font */ +SOKOL_DEBUGTEXT_API_DECL void sdtx_font(int font_index); + +/* set a new virtual canvas size in screen pixels */ +SOKOL_DEBUGTEXT_API_DECL void sdtx_canvas(float w, float h); + +/* set a new origin in character grid coordinates */ +SOKOL_DEBUGTEXT_API_DECL void sdtx_origin(float x, float y); + +/* cursor movement functions (relative to origin in character grid coordinates) */ +SOKOL_DEBUGTEXT_API_DECL void sdtx_home(void); +SOKOL_DEBUGTEXT_API_DECL void sdtx_pos(float x, float y); +SOKOL_DEBUGTEXT_API_DECL void sdtx_pos_x(float x); +SOKOL_DEBUGTEXT_API_DECL void sdtx_pos_y(float y); +SOKOL_DEBUGTEXT_API_DECL void sdtx_move(float dx, float dy); +SOKOL_DEBUGTEXT_API_DECL void sdtx_move_x(float dx); +SOKOL_DEBUGTEXT_API_DECL void sdtx_move_y(float dy); +SOKOL_DEBUGTEXT_API_DECL void sdtx_crlf(void); + +/* set the current text color */ +SOKOL_DEBUGTEXT_API_DECL void sdtx_color3b(uint8_t r, uint8_t g, uint8_t b); // RGB 0..255, A=255 +SOKOL_DEBUGTEXT_API_DECL void sdtx_color3f(float r, float g, float b); // RGB 0.0f..1.0f, A=1.0f +SOKOL_DEBUGTEXT_API_DECL void sdtx_color4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a); // RGBA 0..255 +SOKOL_DEBUGTEXT_API_DECL void sdtx_color4f(float r, float g, float b, float a); // RGBA 0.0f..1.0f +SOKOL_DEBUGTEXT_API_DECL void sdtx_color1i(uint32_t rgba); // ABGR 0xAABBGGRR + +/* text rendering */ +SOKOL_DEBUGTEXT_API_DECL void sdtx_putc(char c); +SOKOL_DEBUGTEXT_API_DECL void sdtx_puts(const char* str); // does NOT append newline! +SOKOL_DEBUGTEXT_API_DECL void sdtx_putr(const char* str, int len); // 'put range', also stops at zero-char +SOKOL_DEBUGTEXT_API_DECL int sdtx_printf(const char* fmt, ...) SOKOL_DEBUGTEXT_PRINTF_ATTR; +SOKOL_DEBUGTEXT_API_DECL int sdtx_vprintf(const char* fmt, va_list args); + +#ifdef __cplusplus +} /* extern "C" */ +/* C++ const-ref wrappers */ +inline void sdtx_setup(const sdtx_desc_t& desc) { return sdtx_setup(&desc); } +inline sdtx_context sdtx_make_context(const sdtx_context_desc_t& desc) { return sdtx_make_context(&desc); } +#endif +#endif /* SOKOL_DEBUGTEXT_INCLUDED */ + +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation +#ifdef SOKOL_DEBUGTEXT_IMPL +#define SOKOL_DEBUGTEXT_IMPL_INCLUDED (1) + +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sdtx_desc_t.allocator to override memory allocation functions" +#endif + +#include // memset +#include // fmodf +#include // for vsnprintf +#include // malloc/free + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif + +#ifndef SOKOL_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#endif +#ifndef _SOKOL_UNUSED + #define _SOKOL_UNUSED(x) (void)(x) +#endif + +#ifndef SOKOL_VSNPRINTF +#include +#define SOKOL_VSNPRINTF vsnprintf +#endif + +#define _sdtx_def(val, def) (((val) == 0) ? (def) : (val)) +#define _SDTX_INIT_COOKIE (0xACBAABCA) + +#define _SDTX_DEFAULT_MAX_COMMANDS (4096) +#define _SDTX_DEFAULT_CONTEXT_POOL_SIZE (8) +#define _SDTX_DEFAULT_CHAR_BUF_SIZE (4096) +#define _SDTX_DEFAULT_PRINTF_BUF_SIZE (4096) +#define _SDTX_DEFAULT_CANVAS_WIDTH (640) +#define _SDTX_DEFAULT_CANVAS_HEIGHT (480) +#define _SDTX_DEFAULT_TAB_WIDTH (4) +#define _SDTX_DEFAULT_COLOR (0xFF00FFFF) +#define _SDTX_INVALID_SLOT_INDEX (0) +#define _SDTX_SLOT_SHIFT (16) +#define _SDTX_MAX_POOL_SIZE (1<<_SDTX_SLOT_SHIFT) +#define _SDTX_SLOT_MASK (_SDTX_MAX_POOL_SIZE-1) + +/* embedded font data */ +static const uint8_t _sdtx_font_kc853[2048] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xFF, // 00 + 0x00, 0x00, 0x22, 0x72, 0x22, 0x3E, 0x00, 0x00, // 01 + 0x00, 0x00, 0x12, 0x32, 0x7E, 0x32, 0x12, 0x00, // 02 + 0x7E, 0x81, 0xB9, 0xA5, 0xB9, 0xA5, 0xB9, 0x81, // 03 + 0x55, 0xFF, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0xFF, // 04 + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, // 05 + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, // 06 + 0x00, 0x00, 0x3C, 0x42, 0x42, 0x7E, 0x00, 0x00, // 07 + 0x00, 0x10, 0x30, 0x7E, 0x30, 0x10, 0x00, 0x00, // 08 + 0x00, 0x08, 0x0C, 0x7E, 0x0C, 0x08, 0x00, 0x00, // 09 + 0x00, 0x10, 0x10, 0x10, 0x7C, 0x38, 0x10, 0x00, // 0A + 0x08, 0x1C, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x00, // 0B + 0x38, 0x30, 0x28, 0x08, 0x08, 0x08, 0x3E, 0x00, // 0C + 0x00, 0x00, 0x12, 0x32, 0x7E, 0x30, 0x10, 0x00, // 0D + 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, // 0E + 0x3E, 0x7C, 0x7C, 0x3E, 0x3E, 0x7C, 0xF8, 0xF8, // 0F + 0x38, 0x30, 0x28, 0x04, 0x04, 0x04, 0x04, 0x00, // 10 + 0x7F, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x08, 0x00, // 11 + 0x00, 0x08, 0x08, 0x08, 0x2A, 0x1C, 0x08, 0x7F, // 12 + 0x7E, 0x81, 0x9D, 0xA1, 0xB9, 0x85, 0x85, 0xB9, // 13 + 0x00, 0x3C, 0x42, 0x5A, 0x5A, 0x42, 0x3C, 0x00, // 14 + 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11, // 15 + 0x00, 0x7F, 0x22, 0x72, 0x27, 0x22, 0x7F, 0x00, // 16 + 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88, // 17 + 0x00, 0x01, 0x09, 0x0D, 0x7F, 0x0D, 0x09, 0x01, // 18 + 0x00, 0x90, 0xB0, 0xFE, 0xB0, 0x90, 0x00, 0x00, // 19 + 0x00, 0x08, 0x7C, 0x06, 0x7C, 0x08, 0x00, 0x00, // 1A + 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, // 1B + 0x7E, 0x81, 0xA1, 0xA1, 0xA1, 0xA1, 0xBD, 0x81, // 1C + 0x7E, 0x81, 0xB9, 0xA5, 0xB9, 0xA5, 0xA5, 0x81, // 1D + 0x7E, 0x81, 0x99, 0xA1, 0xA1, 0xA1, 0x99, 0x81, // 1E + 0x00, 0x10, 0x3E, 0x60, 0x3E, 0x10, 0x00, 0x00, // 1F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 20 + 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00, // 21 + 0x00, 0x66, 0x66, 0xCC, 0x00, 0x00, 0x00, 0x00, // 22 + 0x00, 0x36, 0x7F, 0x36, 0x36, 0x7F, 0x36, 0x00, // 23 + 0x18, 0x3E, 0x6C, 0x3E, 0x1B, 0x1B, 0x7E, 0x18, // 24 + 0x00, 0x63, 0x66, 0x0C, 0x18, 0x36, 0x66, 0x00, // 25 + 0x18, 0x24, 0x28, 0x11, 0x2A, 0x44, 0x4A, 0x31, // 26 + 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, // 27 + 0x00, 0x18, 0x30, 0x30, 0x30, 0x30, 0x18, 0x00, // 28 + 0x00, 0x18, 0x0C, 0x0C, 0x0C, 0x0C, 0x18, 0x00, // 29 + 0x00, 0x00, 0x24, 0x18, 0x7E, 0x18, 0x24, 0x00, // 2A + 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, // 2B + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, // 2C + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, // 2D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, // 2E + 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00, 0x00, // 2F + 0x00, 0x3C, 0x6E, 0x6E, 0x76, 0x76, 0x3C, 0x00, // 30 + 0x00, 0x1C, 0x3C, 0x0C, 0x0C, 0x0C, 0x3E, 0x00, // 31 + 0x00, 0x3C, 0x66, 0x06, 0x3C, 0x60, 0x7E, 0x00, // 32 + 0x00, 0x3C, 0x66, 0x0C, 0x06, 0x66, 0x3C, 0x00, // 33 + 0x00, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x0C, 0x00, // 34 + 0x00, 0x7E, 0x60, 0x7C, 0x06, 0x66, 0x3C, 0x00, // 35 + 0x00, 0x3C, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00, // 36 + 0x00, 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00, // 37 + 0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, // 38 + 0x00, 0x3C, 0x66, 0x66, 0x3E, 0x06, 0x3C, 0x00, // 39 + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, // 3A + 0x00, 0x00, 0x18, 0x00, 0x18, 0x18, 0x30, 0x00, // 3B + 0x00, 0x00, 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, // 3C + 0x00, 0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, // 3D + 0x00, 0x00, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x00, // 3E + 0x00, 0x3C, 0x66, 0x06, 0x1C, 0x18, 0x00, 0x18, // 3F + 0x3C, 0x42, 0x81, 0x35, 0x49, 0x49, 0x49, 0x36, // 40 + 0x00, 0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x00, // 41 + 0x00, 0x7C, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00, // 42 + 0x00, 0x3C, 0x66, 0x60, 0x60, 0x66, 0x3C, 0x00, // 43 + 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, // 44 + 0x00, 0x7E, 0x60, 0x7C, 0x60, 0x60, 0x7E, 0x00, // 45 + 0x00, 0x7E, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x00, // 46 + 0x00, 0x3C, 0x66, 0x60, 0x6E, 0x66, 0x3C, 0x00, // 47 + 0x00, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // 48 + 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // 49 + 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x6C, 0x38, 0x00, // 4A + 0x00, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0x63, 0x00, // 4B + 0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00, // 4C + 0x00, 0x63, 0x77, 0x6B, 0x63, 0x63, 0x63, 0x00, // 4D + 0x00, 0x63, 0x73, 0x6B, 0x67, 0x63, 0x63, 0x00, // 4E + 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // 4F + 0x00, 0x7C, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00, // 50 + 0x00, 0x3C, 0x66, 0x66, 0x6E, 0x66, 0x3A, 0x01, // 51 + 0x00, 0x7C, 0x66, 0x7C, 0x6C, 0x66, 0x63, 0x00, // 52 + 0x00, 0x3C, 0x60, 0x3C, 0x06, 0x66, 0x3C, 0x00, // 53 + 0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // 54 + 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // 55 + 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // 56 + 0x00, 0x63, 0x63, 0x6B, 0x6B, 0x7F, 0x36, 0x00, // 57 + 0x00, 0x66, 0x3C, 0x18, 0x18, 0x3C, 0x66, 0x00, // 58 + 0x00, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x00, // 59 + 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00, // 5A + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 5B + 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // 5C + 0x00, 0x7E, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, // 5D + 0x00, 0x00, 0x00, 0x08, 0x1C, 0x36, 0x00, 0x00, // 5E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // 5F + 0x7E, 0x81, 0x99, 0xA1, 0xA1, 0x99, 0x81, 0x7E, // 60 + 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3B, 0x00, // 61 + 0x00, 0x60, 0x60, 0x78, 0x6C, 0x6C, 0x78, 0x00, // 62 + 0x00, 0x00, 0x3C, 0x66, 0x60, 0x66, 0x3C, 0x00, // 63 + 0x00, 0x06, 0x06, 0x1E, 0x36, 0x36, 0x1E, 0x00, // 64 + 0x00, 0x00, 0x38, 0x6C, 0x7C, 0x60, 0x38, 0x00, // 65 + 0x00, 0x1E, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x00, // 66 + 0x00, 0x00, 0x3C, 0x66, 0x66, 0x3F, 0x06, 0x3C, // 67 + 0x00, 0x60, 0x60, 0x6C, 0x76, 0x66, 0x66, 0x00, // 68 + 0x00, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, // 69 + 0x00, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x30, // 6A + 0x00, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0x00, // 6B + 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x00, // 6C + 0x00, 0x00, 0x36, 0x7F, 0x6B, 0x63, 0x63, 0x00, // 6D + 0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00, // 6E + 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, // 6F + 0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, // 70 + 0x00, 0x00, 0x3C, 0x66, 0x66, 0x3E, 0x06, 0x06, // 71 + 0x00, 0x00, 0x36, 0x38, 0x30, 0x30, 0x30, 0x00, // 72 + 0x00, 0x00, 0x1C, 0x30, 0x1C, 0x06, 0x3C, 0x00, // 73 + 0x00, 0x18, 0x18, 0x3C, 0x18, 0x18, 0x0C, 0x00, // 74 + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // 75 + 0x00, 0x00, 0x66, 0x66, 0x3C, 0x3C, 0x18, 0x00, // 76 + 0x00, 0x00, 0x63, 0x63, 0x6B, 0x7F, 0x36, 0x00, // 77 + 0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00, // 78 + 0x00, 0x00, 0x66, 0x3C, 0x18, 0x30, 0x60, 0x00, // 79 + 0x00, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x7E, 0x00, // 7A + 0x66, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3B, 0x00, // 7B + 0x66, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, // 7C + 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // 7D + 0x00, 0x38, 0x6C, 0x78, 0x6C, 0x78, 0x60, 0x60, // 7E + 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF, // 7F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x00, // 80 + 0xFF, 0xFF, 0xDD, 0x8D, 0xDD, 0xC1, 0xFF, 0xFF, // 81 + 0xFF, 0xFF, 0xED, 0xCD, 0x81, 0xCD, 0xED, 0xFF, // 82 + 0x81, 0x7E, 0x46, 0x5A, 0x46, 0x5A, 0x46, 0x7E, // 83 + 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, // 84 + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, // 85 + 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, // 86 + 0xFF, 0xFF, 0xC3, 0xBD, 0xBD, 0x81, 0xFF, 0xFF, // 87 + 0xFF, 0xEF, 0xCF, 0x81, 0xCF, 0xEF, 0xFF, 0xFF, // 88 + 0xFF, 0xF7, 0xF3, 0x81, 0xF3, 0xF7, 0xFF, 0xFF, // 89 + 0xFF, 0xEF, 0xEF, 0xEF, 0x83, 0xC7, 0xEF, 0xFF, // 8A + 0xF7, 0xE3, 0xC1, 0xF7, 0xF7, 0xF7, 0xF7, 0xFF, // 8B + 0xC7, 0xCF, 0xD7, 0xF7, 0xF7, 0xF7, 0xC1, 0xFF, // 8C + 0xFF, 0xFF, 0xED, 0xCD, 0x81, 0xCF, 0xEF, 0xFF, // 8D + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, // 8E + 0xC1, 0x83, 0x83, 0xC1, 0xC1, 0x83, 0x07, 0x07, // 8F + 0xC7, 0xCF, 0xD7, 0xFB, 0xFB, 0xFB, 0xFB, 0xFF, // 90 + 0x80, 0xF7, 0xE3, 0xD5, 0xF7, 0xF7, 0xF7, 0xFF, // 91 + 0xFF, 0xF7, 0xF7, 0xF7, 0xD5, 0xE3, 0xF7, 0x80, // 92 + 0x81, 0x7E, 0x62, 0x5E, 0x46, 0x7A, 0x7A, 0x46, // 93 + 0xFF, 0xC3, 0xBD, 0xA5, 0xA5, 0xBD, 0xC3, 0xFF, // 94 + 0x77, 0xBB, 0xDD, 0xEE, 0x77, 0xBB, 0xDD, 0xEE, // 95 + 0xFF, 0x80, 0xDD, 0x8D, 0xD8, 0xDD, 0x80, 0xFF, // 96 + 0xEE, 0xDD, 0xBB, 0x77, 0xEE, 0xDD, 0xBB, 0x77, // 97 + 0xFF, 0xFE, 0xF6, 0xF2, 0x80, 0xF2, 0xF6, 0xFE, // 98 + 0xFF, 0x6F, 0x4F, 0x01, 0x4F, 0x6F, 0xFF, 0xFF, // 99 + 0xFF, 0xF7, 0x83, 0xF9, 0x83, 0xF7, 0xFF, 0xFF, // 9A + 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, // 9B + 0x81, 0x7E, 0x5E, 0x5E, 0x5E, 0x5E, 0x42, 0x7E, // 9C + 0x81, 0x7E, 0x46, 0x5A, 0x46, 0x5A, 0x5A, 0x7E, // 9D + 0x81, 0x7E, 0x66, 0x5E, 0x5E, 0x5E, 0x66, 0x7E, // 9E + 0xFF, 0xEF, 0xC1, 0x9F, 0xC1, 0xEF, 0xFF, 0xFF, // 9F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // A0 + 0xFF, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, 0xE7, 0xFF, // A1 + 0xFF, 0x99, 0x99, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, // A2 + 0xFF, 0xC9, 0x80, 0xC9, 0xC9, 0x80, 0xC9, 0xFF, // A3 + 0xE7, 0xC1, 0x93, 0xC1, 0xE4, 0xE4, 0x81, 0xE7, // A4 + 0xFF, 0x9C, 0x99, 0xF3, 0xE7, 0xC9, 0x99, 0xFF, // A5 + 0xE7, 0xDB, 0xD7, 0xEE, 0xD5, 0xBB, 0xB5, 0xCE, // A6 + 0xFF, 0xE7, 0xE7, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, // A7 + 0xFF, 0xE7, 0xCF, 0xCF, 0xCF, 0xCF, 0xE7, 0xFF, // A8 + 0xFF, 0xE7, 0xF3, 0xF3, 0xF3, 0xF3, 0xE7, 0xFF, // A9 + 0xFF, 0xFF, 0xDB, 0xE7, 0x81, 0xE7, 0xDB, 0xFF, // AA + 0xFF, 0xFF, 0xE7, 0xE7, 0x81, 0xE7, 0xE7, 0xFF, // AB + 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0xCF, 0xFF, // AC + 0xFF, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, // AD + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0xFF, // AE + 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3F, 0xFF, 0xFF, // AF + 0xFF, 0xC3, 0x91, 0x91, 0x89, 0x89, 0xC3, 0xFF, // B0 + 0xFF, 0xE3, 0xC3, 0xF3, 0xF3, 0xF3, 0xC1, 0xFF, // B1 + 0xFF, 0xC3, 0x99, 0xF9, 0xC3, 0x9F, 0x81, 0xFF, // B2 + 0xFF, 0xC3, 0x99, 0xF3, 0xF9, 0x99, 0xC3, 0xFF, // B3 + 0xFF, 0xC3, 0x93, 0x33, 0x01, 0xF3, 0xF3, 0xFF, // B4 + 0xFF, 0x81, 0x9F, 0x83, 0xF9, 0x99, 0xC3, 0xFF, // B5 + 0xFF, 0xC3, 0x9F, 0x83, 0x99, 0x99, 0xC3, 0xFF, // B6 + 0xFF, 0x81, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0xFF, // B7 + 0xFF, 0xC3, 0x99, 0xC3, 0x99, 0x99, 0xC3, 0xFF, // B8 + 0xFF, 0xC3, 0x99, 0x99, 0xC1, 0xF9, 0xC3, 0xFF, // B9 + 0xFF, 0xFF, 0xE7, 0xE7, 0xFF, 0xE7, 0xE7, 0xFF, // BA + 0xFF, 0xFF, 0xE7, 0xFF, 0xE7, 0xE7, 0xCF, 0xFF, // BB + 0xFF, 0xFF, 0xE7, 0xCF, 0x9F, 0xCF, 0xE7, 0xFF, // BC + 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xC1, 0xFF, 0xFF, // BD + 0xFF, 0xFF, 0xCF, 0xE7, 0xF3, 0xE7, 0xCF, 0xFF, // BE + 0xFF, 0xC3, 0x99, 0xF9, 0xE3, 0xE7, 0xFF, 0xE7, // BF + 0xC3, 0xBD, 0x7E, 0xCA, 0xB6, 0xB6, 0xB6, 0xC9, // C0 + 0xFF, 0xC3, 0x99, 0x99, 0x81, 0x99, 0x99, 0xFF, // C1 + 0xFF, 0x83, 0x99, 0x83, 0x99, 0x99, 0x83, 0xFF, // C2 + 0xFF, 0xC3, 0x99, 0x9F, 0x9F, 0x99, 0xC3, 0xFF, // C3 + 0xFF, 0x83, 0x99, 0x99, 0x99, 0x99, 0x83, 0xFF, // C4 + 0xFF, 0x81, 0x9F, 0x83, 0x9F, 0x9F, 0x81, 0xFF, // C5 + 0xFF, 0x81, 0x9F, 0x83, 0x9F, 0x9F, 0x9F, 0xFF, // C6 + 0xFF, 0xC3, 0x99, 0x9F, 0x91, 0x99, 0xC3, 0xFF, // C7 + 0xFF, 0x99, 0x99, 0x81, 0x99, 0x99, 0x99, 0xFF, // C8 + 0xFF, 0xC3, 0xE7, 0xE7, 0xE7, 0xE7, 0xC3, 0xFF, // C9 + 0xFF, 0xE1, 0xF3, 0xF3, 0xF3, 0x93, 0xC7, 0xFF, // CA + 0xFF, 0x99, 0x93, 0x87, 0x93, 0x99, 0x9C, 0xFF, // CB + 0xFF, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x81, 0xFF, // CC + 0xFF, 0x9C, 0x88, 0x94, 0x9C, 0x9C, 0x9C, 0xFF, // CD + 0xFF, 0x9C, 0x8C, 0x94, 0x98, 0x9C, 0x9C, 0xFF, // CE + 0xFF, 0xC3, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xFF, // CF + 0xFF, 0x83, 0x99, 0x83, 0x9F, 0x9F, 0x9F, 0xFF, // D0 + 0xFF, 0xC3, 0x99, 0x99, 0x91, 0x99, 0xC5, 0xFE, // D1 + 0xFF, 0x83, 0x99, 0x83, 0x93, 0x99, 0x9C, 0xFF, // D2 + 0xFF, 0xC3, 0x9F, 0xC3, 0xF9, 0x99, 0xC3, 0xFF, // D3 + 0xFF, 0x81, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, // D4 + 0xFF, 0x99, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xFF, // D5 + 0xFF, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xE7, 0xFF, // D6 + 0xFF, 0x9C, 0x9C, 0x94, 0x94, 0x80, 0xC9, 0xFF, // D7 + 0xFF, 0x99, 0xC3, 0xE7, 0xE7, 0xC3, 0x99, 0xFF, // D8 + 0xFF, 0x99, 0xC3, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, // D9 + 0xFF, 0x81, 0xF3, 0xE7, 0xCF, 0x9F, 0x81, 0xFF, // DA + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // DB + 0xFF, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, // DC + 0xFF, 0x81, 0xF9, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, // DD + 0xFF, 0xFF, 0xFF, 0xF7, 0xE3, 0xC9, 0xFF, 0xFF, // DE + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, // DF + 0x81, 0x7E, 0x66, 0x5E, 0x5E, 0x66, 0x7E, 0x81, // E0 + 0xFF, 0xFF, 0xC3, 0x99, 0x99, 0x99, 0xC4, 0xFF, // E1 + 0xFF, 0x9F, 0x9F, 0x87, 0x93, 0x93, 0x87, 0xFF, // E2 + 0xFF, 0xFF, 0xC3, 0x99, 0x9F, 0x99, 0xC3, 0xFF, // E3 + 0xFF, 0xF9, 0xF9, 0xE1, 0xC9, 0xC9, 0xE1, 0xFF, // E4 + 0xFF, 0xFF, 0xC7, 0x93, 0x83, 0x9F, 0xC7, 0xFF, // E5 + 0xFF, 0xE1, 0xE7, 0x81, 0xE7, 0xE7, 0xE7, 0xFF, // E6 + 0xFF, 0xFF, 0xC3, 0x99, 0x99, 0xC0, 0xF9, 0xC3, // E7 + 0xFF, 0x9F, 0x9F, 0x93, 0x89, 0x99, 0x99, 0xFF, // E8 + 0xFF, 0xE7, 0xFF, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, // E9 + 0xFF, 0xE7, 0xFF, 0xC7, 0xE7, 0xE7, 0xE7, 0xCF, // EA + 0xFF, 0x9F, 0x99, 0x93, 0x87, 0x93, 0x99, 0xFF, // EB + 0xFF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xE7, 0xFF, // EC + 0xFF, 0xFF, 0xC9, 0x80, 0x94, 0x9C, 0x9C, 0xFF, // ED + 0xFF, 0xFF, 0x83, 0x99, 0x99, 0x99, 0x99, 0xFF, // EE + 0xFF, 0xFF, 0xC3, 0x99, 0x99, 0x99, 0xC3, 0xFF, // EF + 0xFF, 0xFF, 0x83, 0x99, 0x99, 0x83, 0x9F, 0x9F, // F0 + 0xFF, 0xFF, 0xC3, 0x99, 0x99, 0xC1, 0xF9, 0xF9, // F1 + 0xFF, 0xFF, 0xC9, 0xC7, 0xCF, 0xCF, 0xCF, 0xFF, // F2 + 0xFF, 0xFF, 0xE3, 0xCF, 0xE3, 0xF9, 0xC3, 0xFF, // F3 + 0xFF, 0xE7, 0xE7, 0xC3, 0xE7, 0xE7, 0xF3, 0xFF, // F4 + 0xFF, 0xFF, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xFF, // F5 + 0xFF, 0xFF, 0x99, 0x99, 0xC3, 0xC3, 0xE7, 0xFF, // F6 + 0xFF, 0xFF, 0x9C, 0x9C, 0x94, 0x80, 0xC9, 0xFF, // F7 + 0xFF, 0xFF, 0x99, 0xC3, 0xE7, 0xC3, 0x99, 0xFF, // F8 + 0xFF, 0xFF, 0x99, 0xC3, 0xE7, 0xCF, 0x9F, 0xFF, // F9 + 0xFF, 0xFF, 0x81, 0xF3, 0xE7, 0xCF, 0x81, 0xFF, // FA + 0x99, 0xFF, 0xC3, 0x99, 0x99, 0x99, 0xC4, 0xFF, // FB + 0x99, 0xFF, 0xC3, 0x99, 0x99, 0x99, 0xC3, 0xFF, // FC + 0x99, 0xFF, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xFF, // FD + 0xFF, 0xC7, 0x93, 0x87, 0x93, 0x87, 0x9F, 0x9F, // FE + 0x00, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x00, // FF +}; +static const uint8_t _sdtx_font_kc854[2048] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xFF, // 00 + 0x00, 0x00, 0x22, 0x72, 0x22, 0x3E, 0x00, 0x00, // 01 + 0x00, 0x00, 0x12, 0x32, 0x7E, 0x32, 0x12, 0x00, // 02 + 0x7E, 0x81, 0xB9, 0xA5, 0xB9, 0xA5, 0xB9, 0x81, // 03 + 0x55, 0xFF, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0xFF, // 04 + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, // 05 + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, // 06 + 0x00, 0x00, 0x3C, 0x42, 0x42, 0x7E, 0x00, 0x00, // 07 + 0x00, 0x10, 0x30, 0x7E, 0x30, 0x10, 0x00, 0x00, // 08 + 0x00, 0x08, 0x0C, 0x7E, 0x0C, 0x08, 0x00, 0x00, // 09 + 0x00, 0x10, 0x10, 0x10, 0x7C, 0x38, 0x10, 0x00, // 0A + 0x08, 0x1C, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x00, // 0B + 0x38, 0x30, 0x28, 0x08, 0x08, 0x08, 0x3E, 0x00, // 0C + 0x00, 0x00, 0x12, 0x32, 0x7E, 0x30, 0x10, 0x00, // 0D + 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, // 0E + 0x3E, 0x7C, 0x7C, 0x3E, 0x3E, 0x7C, 0xF8, 0xF8, // 0F + 0x38, 0x30, 0x28, 0x04, 0x04, 0x04, 0x04, 0x00, // 10 + 0x7F, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x08, 0x00, // 11 + 0x00, 0x08, 0x08, 0x08, 0x2A, 0x1C, 0x08, 0x7F, // 12 + 0x7E, 0x81, 0x9D, 0xA1, 0xB9, 0x85, 0x85, 0xB9, // 13 + 0x00, 0x3C, 0x42, 0x5A, 0x5A, 0x42, 0x3C, 0x00, // 14 + 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11, // 15 + 0x00, 0x7F, 0x22, 0x72, 0x27, 0x22, 0x7F, 0x00, // 16 + 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88, // 17 + 0x00, 0x01, 0x09, 0x0D, 0x7F, 0x0D, 0x09, 0x01, // 18 + 0x00, 0x90, 0xB0, 0xFE, 0xB0, 0x90, 0x00, 0x00, // 19 + 0x00, 0x08, 0x7C, 0x06, 0x7C, 0x08, 0x00, 0x00, // 1A + 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, // 1B + 0x7E, 0x81, 0xA1, 0xA1, 0xA1, 0xA1, 0xBD, 0x81, // 1C + 0x7E, 0x81, 0xB9, 0xA5, 0xB9, 0xA5, 0xA5, 0x81, // 1D + 0x7E, 0x81, 0x99, 0xA1, 0xA1, 0xA1, 0x99, 0x81, // 1E + 0x00, 0x10, 0x3E, 0x60, 0x3E, 0x10, 0x00, 0x00, // 1F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 20 + 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x00, // 21 + 0x77, 0x33, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, // 22 + 0x36, 0x36, 0xFE, 0x6C, 0xFE, 0xD8, 0xD8, 0x00, // 23 + 0x18, 0x3E, 0x6C, 0x3E, 0x1B, 0x1B, 0x7E, 0x18, // 24 + 0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00, // 25 + 0x38, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0x76, 0x00, // 26 + 0x1C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, // 27 + 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, // 28 + 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, // 29 + 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, // 2A + 0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0x00, // 2B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x0C, 0x18, // 2C + 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, // 2D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, // 2E + 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, // 2F + 0x7C, 0xC6, 0xCE, 0xDE, 0xF6, 0xE6, 0x7C, 0x00, // 30 + 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x00, // 31 + 0x78, 0xCC, 0x0C, 0x38, 0x60, 0xCC, 0xFC, 0x00, // 32 + 0xFC, 0x18, 0x30, 0x78, 0x0C, 0xCC, 0x78, 0x00, // 33 + 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x1E, 0x00, // 34 + 0xFC, 0xC0, 0xF8, 0x0C, 0x0C, 0xCC, 0x78, 0x00, // 35 + 0x38, 0x60, 0xC0, 0xF8, 0xCC, 0xCC, 0x78, 0x00, // 36 + 0xFC, 0xCC, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00, // 37 + 0x78, 0xCC, 0xCC, 0x78, 0xCC, 0xCC, 0x78, 0x00, // 38 + 0x78, 0xCC, 0xCC, 0x7C, 0x0C, 0x18, 0x70, 0x00, // 39 + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x00, // 3A + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, // 3B + 0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x00, // 3C + 0x00, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0x00, 0x00, // 3D + 0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0x00, // 3E + 0x78, 0xCC, 0x0C, 0x18, 0x30, 0x00, 0x30, 0x00, // 3F + 0x7C, 0xC6, 0xDE, 0xDE, 0xDE, 0xC0, 0x78, 0x00, // 40 + 0x30, 0x78, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0x00, // 41 + 0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xFC, 0x00, // 42 + 0x3C, 0x66, 0xC0, 0xC0, 0xC0, 0x66, 0x3C, 0x00, // 43 + 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, // 44 + 0xFE, 0x62, 0x68, 0x78, 0x68, 0x62, 0xFE, 0x00, // 45 + 0xFE, 0x62, 0x68, 0x78, 0x68, 0x60, 0xF0, 0x00, // 46 + 0x3C, 0x66, 0xC0, 0xC0, 0xCE, 0x66, 0x3C, 0x00, // 47 + 0xCC, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0xCC, 0x00, // 48 + 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, // 49 + 0x1E, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00, // 4A + 0xE6, 0x66, 0x6C, 0x70, 0x6C, 0x66, 0xE6, 0x00, // 4B + 0xF0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00, // 4C + 0xC6, 0xEE, 0xFE, 0xD6, 0xC6, 0xC6, 0xC6, 0x00, // 4D + 0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00, // 4E + 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00, // 4F + 0xFC, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, // 50 + 0x78, 0xCC, 0xCC, 0xCC, 0xDC, 0x78, 0x1C, 0x00, // 51 + 0xFC, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0xE6, 0x00, // 52 + 0x7C, 0xC6, 0xF0, 0x3C, 0x0E, 0xC6, 0x7C, 0x00, // 53 + 0xFC, 0xB4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, // 54 + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00, // 55 + 0xCC, 0xCC, 0xCC, 0x78, 0x78, 0x30, 0x30, 0x00, // 56 + 0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x00, // 57 + 0xC6, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0xC6, 0x00, // 58 + 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x30, 0x78, 0x00, // 59 + 0xFE, 0xC6, 0x8C, 0x18, 0x32, 0x66, 0xFE, 0x00, // 5A + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 5B + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // 5C + 0x00, 0xFE, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, // 5D + 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, // 5E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // 5F + 0x3C, 0x42, 0x99, 0xA1, 0xA1, 0x99, 0x42, 0x3C, // 60 + 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, // 61 + 0xE0, 0x60, 0x7C, 0x66, 0x66, 0x66, 0xDC, 0x00, // 62 + 0x00, 0x00, 0x78, 0xCC, 0xC0, 0xCC, 0x78, 0x00, // 63 + 0x1C, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, // 64 + 0x00, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, // 65 + 0x38, 0x6C, 0x60, 0xF0, 0x60, 0x60, 0xF0, 0x00, // 66 + 0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, // 67 + 0xE0, 0x60, 0x6C, 0x76, 0x66, 0x66, 0xE6, 0x00, // 68 + 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0xFC, 0x00, // 69 + 0x0C, 0x00, 0x1C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, // 6A + 0xE0, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0xE6, 0x00, // 6B + 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x00, // 6C + 0x00, 0x00, 0xCC, 0xFE, 0xFE, 0xD6, 0xC6, 0x00, // 6D + 0x00, 0x00, 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, // 6E + 0x00, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00, // 6F + 0x00, 0x00, 0xDC, 0x66, 0x66, 0x7C, 0x60, 0xF0, // 70 + 0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0x1E, // 71 + 0x00, 0x00, 0xDC, 0x76, 0x66, 0x60, 0xF0, 0x00, // 72 + 0x00, 0x00, 0x7C, 0xC0, 0x78, 0x0C, 0xF8, 0x00, // 73 + 0x10, 0x30, 0x7C, 0x30, 0x30, 0x34, 0x18, 0x00, // 74 + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, // 75 + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, // 76 + 0x00, 0x00, 0xC6, 0xD6, 0xFE, 0xFE, 0x6C, 0x00, // 77 + 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00, // 78 + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, // 79 + 0x00, 0x00, 0xFC, 0x98, 0x30, 0x64, 0xFC, 0x00, // 7A + 0x6C, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, // 7B + 0xCC, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00, // 7C + 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, // 7D + 0x3C, 0x66, 0x66, 0x6C, 0x66, 0x66, 0x6C, 0xF0, // 7E + 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF, // 7F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x00, // 80 + 0xFF, 0xFF, 0xDD, 0x8D, 0xDD, 0xC1, 0xFF, 0xFF, // 81 + 0xFF, 0xFF, 0xED, 0xCD, 0x81, 0xCD, 0xED, 0xFF, // 82 + 0x81, 0x7E, 0x46, 0x5A, 0x46, 0x5A, 0x46, 0x7E, // 83 + 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, // 84 + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, // 85 + 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, // 86 + 0xFF, 0xFF, 0xC3, 0xBD, 0xBD, 0x81, 0xFF, 0xFF, // 87 + 0xFF, 0xEF, 0xCF, 0x81, 0xCF, 0xEF, 0xFF, 0xFF, // 88 + 0xFF, 0xF7, 0xF3, 0x81, 0xF3, 0xF7, 0xFF, 0xFF, // 89 + 0xFF, 0xEF, 0xEF, 0xEF, 0x83, 0xC7, 0xEF, 0xFF, // 8A + 0xF7, 0xE3, 0xC1, 0xF7, 0xF7, 0xF7, 0xF7, 0xFF, // 8B + 0xC7, 0xCF, 0xD7, 0xF7, 0xF7, 0xF7, 0xC1, 0xFF, // 8C + 0xFF, 0xFF, 0xED, 0xCD, 0x81, 0xCF, 0xEF, 0xFF, // 8D + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, // 8E + 0xC1, 0x83, 0x83, 0xC1, 0xC1, 0x83, 0x07, 0x07, // 8F + 0xC7, 0xCF, 0xD7, 0xFB, 0xFB, 0xFB, 0xFB, 0xFF, // 90 + 0x80, 0xF7, 0xE3, 0xD5, 0xF7, 0xF7, 0xF7, 0xFF, // 91 + 0xFF, 0xF7, 0xF7, 0xF7, 0xD5, 0xE3, 0xF7, 0x80, // 92 + 0x81, 0x7E, 0x62, 0x5E, 0x46, 0x7A, 0x7A, 0x46, // 93 + 0xFF, 0xC3, 0xBD, 0xA5, 0xA5, 0xBD, 0xC3, 0xFF, // 94 + 0x77, 0xBB, 0xDD, 0xEE, 0x77, 0xBB, 0xDD, 0xEE, // 95 + 0xFF, 0x80, 0xDD, 0x8D, 0xD8, 0xDD, 0x80, 0xFF, // 96 + 0xEE, 0xDD, 0xBB, 0x77, 0xEE, 0xDD, 0xBB, 0x77, // 97 + 0xFF, 0xFE, 0xF6, 0xF2, 0x80, 0xF2, 0xF6, 0xFE, // 98 + 0xFF, 0x6F, 0x4F, 0x01, 0x4F, 0x6F, 0xFF, 0xFF, // 99 + 0xFF, 0xF7, 0x83, 0xF9, 0x83, 0xF7, 0xFF, 0xFF, // 9A + 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, // 9B + 0x81, 0x7E, 0x5E, 0x5E, 0x5E, 0x5E, 0x42, 0x7E, // 9C + 0x81, 0x7E, 0x46, 0x5A, 0x46, 0x5A, 0x5A, 0x7E, // 9D + 0x81, 0x7E, 0x66, 0x5E, 0x5E, 0x5E, 0x66, 0x7E, // 9E + 0xFF, 0xEF, 0xC1, 0x9F, 0xC1, 0xEF, 0xFF, 0xFF, // 9F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // A0 + 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xFF, 0xCF, 0xFF, // A1 + 0x88, 0xCC, 0x99, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // A2 + 0xC9, 0xC9, 0x01, 0x93, 0x01, 0x27, 0x27, 0xFF, // A3 + 0xE7, 0xC1, 0x93, 0xC1, 0xE4, 0xE4, 0x81, 0xE7, // A4 + 0xFF, 0x39, 0x33, 0xE7, 0xCF, 0x99, 0x39, 0xFF, // A5 + 0xC7, 0x93, 0xC7, 0x89, 0x23, 0x33, 0x89, 0xFF, // A6 + 0xE3, 0xF3, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // A7 + 0xE7, 0xCF, 0x9F, 0x9F, 0x9F, 0xCF, 0xE7, 0xFF, // A8 + 0x9F, 0xCF, 0xE7, 0xE7, 0xE7, 0xCF, 0x9F, 0xFF, // A9 + 0xFF, 0x99, 0xC3, 0x00, 0xC3, 0x99, 0xFF, 0xFF, // AA + 0xFF, 0xCF, 0xCF, 0x03, 0xCF, 0xCF, 0xFF, 0xFF, // AB + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xF3, 0xE7, // AC + 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, // AD + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xCF, 0xFF, // AE + 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3F, 0x7F, 0xFF, // AF + 0x83, 0x39, 0x31, 0x21, 0x09, 0x19, 0x83, 0xFF, // B0 + 0xCF, 0x8F, 0xCF, 0xCF, 0xCF, 0xCF, 0x03, 0xFF, // B1 + 0x87, 0x33, 0xF3, 0xC7, 0x9F, 0x33, 0x03, 0xFF, // B2 + 0x03, 0xE7, 0xCF, 0x87, 0xF3, 0x33, 0x87, 0xFF, // B3 + 0xE3, 0xC3, 0x93, 0x33, 0x01, 0xF3, 0xE1, 0xFF, // B4 + 0x03, 0x3F, 0x07, 0xF3, 0xF3, 0x33, 0x87, 0xFF, // B5 + 0xC7, 0x9F, 0x3F, 0x07, 0x33, 0x33, 0x87, 0xFF, // B6 + 0x03, 0x33, 0xF3, 0xE7, 0xCF, 0xCF, 0xCF, 0xFF, // B7 + 0x87, 0x33, 0x33, 0x87, 0x33, 0x33, 0x87, 0xFF, // B8 + 0x87, 0x33, 0x33, 0x83, 0xF3, 0xE7, 0x8F, 0xFF, // B9 + 0xFF, 0xFF, 0xCF, 0xCF, 0xFF, 0xCF, 0xCF, 0xFF, // BA + 0xFF, 0xFF, 0xCF, 0xCF, 0xFF, 0xCF, 0xCF, 0x9F, // BB + 0xE7, 0xCF, 0x9F, 0x3F, 0x9F, 0xCF, 0xE7, 0xFF, // BC + 0xFF, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, // BD + 0x9F, 0xCF, 0xE7, 0xF3, 0xE7, 0xCF, 0x9F, 0xFF, // BE + 0x87, 0x33, 0xF3, 0xE7, 0xCF, 0xFF, 0xCF, 0xFF, // BF + 0x83, 0x39, 0x21, 0x21, 0x21, 0x3F, 0x87, 0xFF, // C0 + 0xCF, 0x87, 0x33, 0x33, 0x03, 0x33, 0x33, 0xFF, // C1 + 0x03, 0x99, 0x99, 0x83, 0x99, 0x99, 0x03, 0xFF, // C2 + 0xC3, 0x99, 0x3F, 0x3F, 0x3F, 0x99, 0xC3, 0xFF, // C3 + 0x07, 0x93, 0x99, 0x99, 0x99, 0x93, 0x07, 0xFF, // C4 + 0x01, 0x9D, 0x97, 0x87, 0x97, 0x9D, 0x01, 0xFF, // C5 + 0x01, 0x9D, 0x97, 0x87, 0x97, 0x9F, 0x0F, 0xFF, // C6 + 0xC3, 0x99, 0x3F, 0x3F, 0x31, 0x99, 0xC3, 0xFF, // C7 + 0x33, 0x33, 0x33, 0x03, 0x33, 0x33, 0x33, 0xFF, // C8 + 0x87, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0x87, 0xFF, // C9 + 0xE1, 0xF3, 0xF3, 0xF3, 0x33, 0x33, 0x87, 0xFF, // CA + 0x19, 0x99, 0x93, 0x8F, 0x93, 0x99, 0x19, 0xFF, // CB + 0x0F, 0x9F, 0x9F, 0x9F, 0x9D, 0x99, 0x01, 0xFF, // CC + 0x39, 0x11, 0x01, 0x29, 0x39, 0x39, 0x39, 0xFF, // CD + 0x39, 0x19, 0x09, 0x21, 0x31, 0x39, 0x39, 0xFF, // CE + 0xC7, 0x93, 0x39, 0x39, 0x39, 0x93, 0xC7, 0xFF, // CF + 0x03, 0x99, 0x99, 0x83, 0x9F, 0x9F, 0x0F, 0xFF, // D0 + 0x87, 0x33, 0x33, 0x33, 0x23, 0x87, 0xE3, 0xFF, // D1 + 0x03, 0x99, 0x99, 0x83, 0x93, 0x99, 0x19, 0xFF, // D2 + 0x83, 0x39, 0x0F, 0xC3, 0xF1, 0x39, 0x83, 0xFF, // D3 + 0x03, 0x4B, 0xCF, 0xCF, 0xCF, 0xCF, 0x87, 0xFF, // D4 + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x87, 0xFF, // D5 + 0x33, 0x33, 0x33, 0x87, 0x87, 0xCF, 0xCF, 0xFF, // D6 + 0x39, 0x39, 0x39, 0x29, 0x01, 0x11, 0x39, 0xFF, // D7 + 0x39, 0x39, 0x93, 0xC7, 0x93, 0x39, 0x39, 0xFF, // D8 + 0x33, 0x33, 0x33, 0x87, 0xCF, 0xCF, 0x87, 0xFF, // D9 + 0x01, 0x39, 0x73, 0xE7, 0xCD, 0x99, 0x01, 0xFF, // DA + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // DB + 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, // DC + 0xFF, 0x01, 0xF9, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, // DD + 0xEF, 0xC7, 0x93, 0x39, 0xFF, 0xFF, 0xFF, 0xFF, // DE + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, // DF + 0xC3, 0xBD, 0x66, 0x5E, 0x5E, 0x66, 0xBD, 0xC3, // E0 + 0xFF, 0xFF, 0x87, 0xF3, 0x83, 0x33, 0x89, 0xFF, // E1 + 0x1F, 0x9F, 0x83, 0x99, 0x99, 0x99, 0x23, 0xFF, // E2 + 0xFF, 0xFF, 0x87, 0x33, 0x3F, 0x33, 0x87, 0xFF, // E3 + 0xE3, 0xF3, 0x83, 0x33, 0x33, 0x33, 0x89, 0xFF, // E4 + 0xFF, 0xFF, 0x87, 0x33, 0x03, 0x3F, 0x87, 0xFF, // E5 + 0xC7, 0x93, 0x9F, 0x0F, 0x9F, 0x9F, 0x0F, 0xFF, // E6 + 0xFF, 0xFF, 0x89, 0x33, 0x33, 0x83, 0xF3, 0x07, // E7 + 0x1F, 0x9F, 0x93, 0x89, 0x99, 0x99, 0x19, 0xFF, // E8 + 0xCF, 0xFF, 0x8F, 0xCF, 0xCF, 0xCF, 0x03, 0xFF, // E9 + 0xF3, 0xFF, 0xE3, 0xF3, 0xF3, 0x33, 0x33, 0x87, // EA + 0x1F, 0x9F, 0x99, 0x93, 0x87, 0x93, 0x19, 0xFF, // EB + 0x8F, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0x03, 0xFF, // EC + 0xFF, 0xFF, 0x33, 0x01, 0x01, 0x29, 0x39, 0xFF, // ED + 0xFF, 0xFF, 0x07, 0x33, 0x33, 0x33, 0x33, 0xFF, // EE + 0xFF, 0xFF, 0x87, 0x33, 0x33, 0x33, 0x87, 0xFF, // EF + 0xFF, 0xFF, 0x23, 0x99, 0x99, 0x83, 0x9F, 0x0F, // F0 + 0xFF, 0xFF, 0x89, 0x33, 0x33, 0x83, 0xF3, 0xE1, // F1 + 0xFF, 0xFF, 0x23, 0x89, 0x99, 0x9F, 0x0F, 0xFF, // F2 + 0xFF, 0xFF, 0x83, 0x3F, 0x87, 0xF3, 0x07, 0xFF, // F3 + 0xEF, 0xCF, 0x83, 0xCF, 0xCF, 0xCB, 0xE7, 0xFF, // F4 + 0xFF, 0xFF, 0x33, 0x33, 0x33, 0x33, 0x89, 0xFF, // F5 + 0xFF, 0xFF, 0x33, 0x33, 0x33, 0x87, 0xCF, 0xFF, // F6 + 0xFF, 0xFF, 0x39, 0x29, 0x01, 0x01, 0x93, 0xFF, // F7 + 0xFF, 0xFF, 0x39, 0x93, 0xC7, 0x93, 0x39, 0xFF, // F8 + 0xFF, 0xFF, 0x33, 0x33, 0x33, 0x83, 0xF3, 0x07, // F9 + 0xFF, 0xFF, 0x03, 0x67, 0xCF, 0x9B, 0x03, 0xFF, // FA + 0x93, 0xFF, 0x87, 0xF3, 0x83, 0x33, 0x89, 0xFF, // FB + 0x33, 0xFF, 0x87, 0x33, 0x33, 0x33, 0x87, 0xFF, // FC + 0x33, 0xFF, 0x33, 0x33, 0x33, 0x33, 0x89, 0xFF, // FD + 0xC3, 0x99, 0x99, 0x93, 0x99, 0x99, 0x93, 0x0F, // FE + 0x00, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x00, // FF +}; +static const uint8_t _sdtx_font_z1013[2048] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 00 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 01 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 02 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 03 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 04 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 05 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 06 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 07 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 08 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 09 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0A + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0B + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0C + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0D + 0x00, 0x00, 0x18, 0x24, 0x24, 0x18, 0x24, 0x42, // 0E + 0xDB, 0xA5, 0x81, 0xFF, 0x24, 0x24, 0x24, 0x42, // 0F + 0x08, 0x34, 0x42, 0x81, 0x91, 0x69, 0x09, 0x31, // 10 + 0x42, 0x7E, 0x81, 0xFF, 0x00, 0x00, 0x00, 0x00, // 11 + 0x18, 0x24, 0x42, 0x99, 0xBD, 0x99, 0x42, 0x24, // 12 + 0x7E, 0x42, 0x99, 0xE7, 0x00, 0x00, 0x00, 0x00, // 13 + 0x18, 0xDB, 0xC3, 0x18, 0x99, 0xE7, 0x81, 0x42, // 14 + 0x18, 0x24, 0x18, 0xC3, 0xBD, 0x81, 0x81, 0x42, // 15 + 0x24, 0x7E, 0x81, 0xFF, 0x00, 0x00, 0x00, 0x00, // 16 + 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x3C, 0x7E, // 17 + 0xDB, 0xFF, 0xFF, 0xFF, 0x3C, 0x3C, 0x3C, 0x7E, // 18 + 0x08, 0x3C, 0x7E, 0xFF, 0xFF, 0x6F, 0x0F, 0x3F, // 19 + 0x7E, 0x7E, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // 1A + 0x18, 0x3C, 0x7E, 0xE7, 0xC3, 0xE7, 0x7E, 0x3C, // 1B + 0x7E, 0x7E, 0xFF, 0xE7, 0x00, 0x00, 0x00, 0x00, // 1C + 0x18, 0xDB, 0xC3, 0x18, 0x99, 0xFF, 0xFF, 0x7E, // 1D + 0x18, 0x3C, 0x18, 0xC3, 0xFF, 0xFF, 0xFF, 0x7E, // 1E + 0x3C, 0x3C, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // 1F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 20 + 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x10, 0x00, // 21 + 0x28, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, // 22 + 0x24, 0x7E, 0x24, 0x24, 0x24, 0x7E, 0x24, 0x00, // 23 + 0x10, 0x3C, 0x50, 0x38, 0x14, 0x78, 0x10, 0x00, // 24 + 0x60, 0x64, 0x08, 0x10, 0x20, 0x4C, 0x0C, 0x00, // 25 + 0x10, 0x28, 0x28, 0x30, 0x54, 0x48, 0x34, 0x00, // 26 + 0x10, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, // 27 + 0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, // 28 + 0x20, 0x10, 0x08, 0x08, 0x08, 0x10, 0x20, 0x00, // 29 + 0x00, 0x10, 0x54, 0x38, 0x54, 0x10, 0x00, 0x00, // 2A + 0x00, 0x10, 0x10, 0x7C, 0x10, 0x10, 0x00, 0x00, // 2B + 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x20, 0x00, // 2C + 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, // 2D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, // 2E + 0x00, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00, // 2F + 0x38, 0x44, 0x44, 0x54, 0x44, 0x44, 0x38, 0x00, // 30 + 0x10, 0x30, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, // 31 + 0x38, 0x44, 0x04, 0x08, 0x10, 0x20, 0x7C, 0x00, // 32 + 0x7C, 0x08, 0x10, 0x08, 0x04, 0x44, 0x38, 0x00, // 33 + 0x08, 0x18, 0x28, 0x48, 0x7C, 0x08, 0x08, 0x00, // 34 + 0x7C, 0x40, 0x78, 0x04, 0x04, 0x44, 0x38, 0x00, // 35 + 0x18, 0x20, 0x40, 0x78, 0x44, 0x44, 0x38, 0x00, // 36 + 0x7C, 0x04, 0x08, 0x10, 0x20, 0x20, 0x20, 0x00, // 37 + 0x38, 0x44, 0x44, 0x38, 0x44, 0x44, 0x38, 0x00, // 38 + 0x38, 0x44, 0x44, 0x3C, 0x04, 0x08, 0x30, 0x00, // 39 + 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x00, 0x00, // 3A + 0x00, 0x00, 0x10, 0x00, 0x10, 0x10, 0x20, 0x00, // 3B + 0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x00, // 3C + 0x00, 0x00, 0x7C, 0x00, 0x7C, 0x00, 0x00, 0x00, // 3D + 0x20, 0x10, 0x08, 0x04, 0x08, 0x10, 0x20, 0x00, // 3E + 0x38, 0x44, 0x04, 0x08, 0x10, 0x00, 0x10, 0x00, // 3F + 0x38, 0x44, 0x5C, 0x54, 0x5C, 0x40, 0x3C, 0x00, // 40 + 0x38, 0x44, 0x44, 0x7C, 0x44, 0x44, 0x44, 0x00, // 41 + 0x78, 0x24, 0x24, 0x38, 0x24, 0x24, 0x78, 0x00, // 42 + 0x38, 0x44, 0x40, 0x40, 0x40, 0x44, 0x38, 0x00, // 43 + 0x78, 0x24, 0x24, 0x24, 0x24, 0x24, 0x78, 0x00, // 44 + 0x7C, 0x40, 0x40, 0x78, 0x40, 0x40, 0x7C, 0x00, // 45 + 0x7C, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x00, // 46 + 0x38, 0x44, 0x40, 0x40, 0x4C, 0x44, 0x3C, 0x00, // 47 + 0x44, 0x44, 0x44, 0x7C, 0x44, 0x44, 0x44, 0x00, // 48 + 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, // 49 + 0x1C, 0x08, 0x08, 0x08, 0x08, 0x48, 0x30, 0x00, // 4A + 0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x00, // 4B + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7C, 0x00, // 4C + 0x44, 0x6C, 0x54, 0x54, 0x44, 0x44, 0x44, 0x00, // 4D + 0x44, 0x44, 0x64, 0x54, 0x4C, 0x44, 0x44, 0x00, // 4E + 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, // 4F + 0x78, 0x44, 0x44, 0x78, 0x40, 0x40, 0x40, 0x00, // 50 + 0x38, 0x44, 0x44, 0x44, 0x54, 0x48, 0x34, 0x00, // 51 + 0x78, 0x44, 0x44, 0x78, 0x50, 0x48, 0x44, 0x00, // 52 + 0x3C, 0x40, 0x40, 0x38, 0x04, 0x04, 0x78, 0x00, // 53 + 0x7C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, // 54 + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, // 55 + 0x44, 0x44, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00, // 56 + 0x44, 0x44, 0x44, 0x54, 0x54, 0x6C, 0x44, 0x00, // 57 + 0x44, 0x44, 0x28, 0x10, 0x28, 0x44, 0x44, 0x00, // 58 + 0x44, 0x44, 0x44, 0x28, 0x10, 0x10, 0x10, 0x00, // 59 + 0x7C, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7C, 0x00, // 5A + 0x38, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38, 0x00, // 5B + 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00, // 5C + 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00, // 5D + 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, // 5E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, // 5F + 0x00, 0x20, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, // 60 + 0x00, 0x00, 0x34, 0x4C, 0x44, 0x44, 0x3A, 0x00, // 61 + 0x40, 0x40, 0x58, 0x64, 0x44, 0x44, 0x78, 0x00, // 62 + 0x00, 0x00, 0x38, 0x44, 0x40, 0x44, 0x38, 0x00, // 63 + 0x04, 0x04, 0x34, 0x4C, 0x44, 0x44, 0x3A, 0x00, // 64 + 0x00, 0x00, 0x38, 0x44, 0x7C, 0x40, 0x38, 0x00, // 65 + 0x08, 0x10, 0x38, 0x10, 0x10, 0x10, 0x10, 0x00, // 66 + 0x00, 0x00, 0x34, 0x4C, 0x44, 0x3C, 0x04, 0x38, // 67 + 0x40, 0x40, 0x58, 0x64, 0x44, 0x44, 0x44, 0x00, // 68 + 0x10, 0x00, 0x10, 0x10, 0x10, 0x10, 0x08, 0x00, // 69 + 0x10, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, // 6A + 0x40, 0x40, 0x48, 0x50, 0x70, 0x48, 0x44, 0x00, // 6B + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x00, // 6C + 0x00, 0x00, 0x68, 0x54, 0x54, 0x54, 0x54, 0x00, // 6D + 0x00, 0x00, 0x58, 0x64, 0x44, 0x44, 0x44, 0x00, // 6E + 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, // 6F + 0x00, 0x00, 0x58, 0x64, 0x44, 0x78, 0x40, 0x40, // 70 + 0x00, 0x00, 0x34, 0x4C, 0x44, 0x3C, 0x04, 0x04, // 71 + 0x00, 0x00, 0x58, 0x64, 0x40, 0x40, 0x40, 0x00, // 72 + 0x00, 0x00, 0x38, 0x40, 0x38, 0x04, 0x78, 0x00, // 73 + 0x10, 0x10, 0x38, 0x10, 0x10, 0x10, 0x08, 0x00, // 74 + 0x00, 0x00, 0x44, 0x44, 0x44, 0x4C, 0x34, 0x00, // 75 + 0x00, 0x00, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00, // 76 + 0x00, 0x00, 0x54, 0x54, 0x54, 0x54, 0x28, 0x00, // 77 + 0x00, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, // 78 + 0x00, 0x00, 0x44, 0x44, 0x44, 0x3C, 0x04, 0x38, // 79 + 0x00, 0x00, 0x7C, 0x08, 0x10, 0x20, 0x7C, 0x00, // 7A + 0x08, 0x10, 0x10, 0x20, 0x10, 0x10, 0x08, 0x00, // 7B + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, // 7C + 0x20, 0x10, 0x10, 0x08, 0x10, 0x10, 0x20, 0x00, // 7D + 0x00, 0x00, 0x00, 0x32, 0x4C, 0x00, 0x00, 0x00, // 7E + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 7F + 0xC0, 0x20, 0x10, 0x10, 0x10, 0x10, 0x20, 0xC0, // 80 + 0x03, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x03, // 81 + 0x81, 0x81, 0x42, 0x3C, 0x00, 0x00, 0x00, 0x00, // 82 + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x81, 0x81, // 83 + 0x10, 0x10, 0x20, 0xC0, 0x00, 0x00, 0x00, 0x00, // 84 + 0x08, 0x08, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, // 85 + 0x00, 0x00, 0x00, 0x00, 0x03, 0x04, 0x08, 0x08, // 86 + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x20, 0x10, 0x10, // 87 + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xFF, // 88 + 0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 89 + 0x00, 0x10, 0x28, 0x44, 0x82, 0x44, 0x28, 0x10, // 8A + 0xFF, 0xEF, 0xC7, 0x83, 0x01, 0x83, 0xC7, 0xEF, // 8B + 0x3C, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3C, // 8C + 0xC3, 0x81, 0x00, 0x00, 0x00, 0x00, 0x81, 0xC3, // 8D + 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, // 8E + 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF, // 8F + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, // 90 + 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, // 91 + 0x00, 0x00, 0x00, 0x00, 0x03, 0x0C, 0x30, 0xC0, // 92 + 0x03, 0x0C, 0x30, 0xC0, 0x00, 0x00, 0x00, 0x00, // 93 + 0x03, 0x0C, 0x30, 0xC0, 0xC0, 0x30, 0x0C, 0x03, // 94 + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x30, 0x0C, 0x03, // 95 + 0xC0, 0x30, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x00, // 96 + 0xC0, 0x30, 0x0C, 0x03, 0x03, 0x0C, 0x30, 0xC0, // 97 + 0x10, 0x10, 0x20, 0x20, 0x40, 0x40, 0x80, 0x80, // 98 + 0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, // 99 + 0x81, 0x81, 0x42, 0x42, 0x24, 0x24, 0x18, 0x18, // 9A + 0x80, 0x80, 0x40, 0x40, 0x20, 0x20, 0x10, 0x10, // 9B + 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x01, 0x01, // 9C + 0x18, 0x18, 0x24, 0x24, 0x42, 0x42, 0x81, 0x81, // 9D + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9E + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // 9F + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, // A0 + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // A1 + 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x00, 0x00, 0x00, // A2 + 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x18, 0x18, 0x18, // A3 + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x18, 0x18, // A4 + 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x18, 0x18, 0x18, // A5 + 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x18, 0x18, 0x18, // A6 + 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x00, 0x00, 0x00, // A7 + 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x18, 0x18, 0x18, // A8 + 0x00, 0x00, 0x00, 0xF8, 0xF8, 0x18, 0x18, 0x18, // A9 + 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x00, 0x00, 0x00, // AA + 0x80, 0x80, 0x80, 0x40, 0x40, 0x20, 0x18, 0x07, // AB + 0x01, 0x01, 0x01, 0x02, 0x02, 0x04, 0x18, 0xE0, // AC + 0xE0, 0x18, 0x04, 0x02, 0x02, 0x01, 0x01, 0x01, // AD + 0x07, 0x18, 0x20, 0x40, 0x40, 0x80, 0x80, 0x80, // AE + 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81, // AF + 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, // B0 + 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, // B1 + 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, // B2 + 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, // B3 + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, // B4 + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, // B5 + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // B6 + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // B7 + 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, // B8 + 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, // B9 + 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, // BA + 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, // BB + 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, // BC + 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, // BD + 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF, // BE + 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, // BF + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // C0 + 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, // C1 + 0xFF, 0x80, 0x80, 0x9C, 0x9C, 0x9C, 0x80, 0x80, // C2 + 0xFF, 0xFF, 0xFF, 0xE3, 0xE3, 0xE3, 0xFF, 0xFF, // C3 + 0x18, 0x3C, 0x7E, 0x3C, 0x18, 0x3C, 0x7E, 0xFF, // C4 + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, // C5 + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, // C6 + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, // C7 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF, // C8 + 0x00, 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, // C9 + 0x38, 0x10, 0x92, 0xFE, 0x92, 0x10, 0x38, 0x7C, // CA + 0x00, 0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, // CB + 0x10, 0x38, 0x7C, 0xFE, 0xFE, 0x7C, 0x10, 0x7C, // CC + 0xE7, 0xE7, 0x42, 0xFF, 0xFF, 0x42, 0xE7, 0xE7, // CD + 0xDB, 0xFF, 0xDB, 0x18, 0x18, 0xDB, 0xFF, 0xDB, // CE + 0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, // CF + 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // D0 + 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // D1 + 0x0C, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // D2 + 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // D3 + 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, // D4 + 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, // D5 + 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x00, 0x00, // D6 + 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, // D7 + 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, // D8 + 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, // D9 + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, // DA + 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, // DB + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, // DC + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, // DD + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, // DE + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, // DF + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, // E0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x3F, // E1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, // E2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFC, // E3 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, // E4 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, // E5 + 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, // E6 + 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // E7 + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // E8 + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, // E9 + 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, // EA + 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // EB + 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // EC + 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ED + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // EE + 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // EF + 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // F0 + 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // F1 + 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, // F2 + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, // F3 + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // F4 + 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // F5 + 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, // F6 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, // F7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // F8 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, // F9 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, // FA + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // FB + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // FC + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // FD + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // FE + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // FF +}; +static const uint8_t _sdtx_font_cpc[2048] = { + 0xFF, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, // 00 + 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // 01 + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, // 02 + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, // 03 + 0x0C, 0x18, 0x30, 0x7E, 0x0C, 0x18, 0x30, 0x00, // 04 + 0xFF, 0xC3, 0xE7, 0xDB, 0xDB, 0xE7, 0xC3, 0xFF, // 05 + 0x00, 0x01, 0x03, 0x06, 0xCC, 0x78, 0x30, 0x00, // 06 + 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0x24, 0xE7, 0x00, // 07 + 0x00, 0x00, 0x30, 0x60, 0xFF, 0x60, 0x30, 0x00, // 08 + 0x00, 0x00, 0x0C, 0x06, 0xFF, 0x06, 0x0C, 0x00, // 09 + 0x18, 0x18, 0x18, 0x18, 0xDB, 0x7E, 0x3C, 0x18, // 0A + 0x18, 0x3C, 0x7E, 0xDB, 0x18, 0x18, 0x18, 0x18, // 0B + 0x18, 0x5A, 0x3C, 0x99, 0xDB, 0x7E, 0x3C, 0x18, // 0C + 0x00, 0x03, 0x33, 0x63, 0xFE, 0x60, 0x30, 0x00, // 0D + 0x3C, 0x66, 0xFF, 0xDB, 0xDB, 0xFF, 0x66, 0x3C, // 0E + 0x3C, 0x66, 0xC3, 0xDB, 0xDB, 0xC3, 0x66, 0x3C, // 0F + 0xFF, 0xC3, 0xC3, 0xFF, 0xC3, 0xC3, 0xC3, 0xFF, // 10 + 0x3C, 0x7E, 0xDB, 0xDB, 0xDF, 0xC3, 0x66, 0x3C, // 11 + 0x3C, 0x66, 0xC3, 0xDF, 0xDB, 0xDB, 0x7E, 0x3C, // 12 + 0x3C, 0x66, 0xC3, 0xFB, 0xDB, 0xDB, 0x7E, 0x3C, // 13 + 0x3C, 0x7E, 0xDB, 0xDB, 0xFB, 0xC3, 0x66, 0x3C, // 14 + 0x00, 0x01, 0x33, 0x1E, 0xCE, 0x7B, 0x31, 0x00, // 15 + 0x7E, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xE7, // 16 + 0x03, 0x03, 0x03, 0xFF, 0x03, 0x03, 0x03, 0x00, // 17 + 0xFF, 0x66, 0x3C, 0x18, 0x18, 0x3C, 0x66, 0xFF, // 18 + 0x18, 0x18, 0x3C, 0x3C, 0x3C, 0x3C, 0x18, 0x18, // 19 + 0x3C, 0x66, 0x66, 0x30, 0x18, 0x00, 0x18, 0x00, // 1A + 0x3C, 0x66, 0xC3, 0xFF, 0xC3, 0xC3, 0x66, 0x3C, // 1B + 0xFF, 0xDB, 0xDB, 0xDB, 0xFB, 0xC3, 0xC3, 0xFF, // 1C + 0xFF, 0xC3, 0xC3, 0xFB, 0xDB, 0xDB, 0xDB, 0xFF, // 1D + 0xFF, 0xC3, 0xC3, 0xDF, 0xDB, 0xDB, 0xDB, 0xFF, // 1E + 0xFF, 0xDB, 0xDB, 0xDB, 0xDF, 0xC3, 0xC3, 0xFF, // 1F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 20 + 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00, // 21 + 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, // 22 + 0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00, // 23 + 0x18, 0x3E, 0x58, 0x3C, 0x1A, 0x7C, 0x18, 0x00, // 24 + 0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00, // 25 + 0x38, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0x76, 0x00, // 26 + 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, // 27 + 0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00, // 28 + 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00, // 29 + 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, // 2A + 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, // 2B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, // 2C + 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, // 2D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, // 2E + 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, // 2F + 0x7C, 0xC6, 0xCE, 0xD6, 0xE6, 0xC6, 0x7C, 0x00, // 30 + 0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, // 31 + 0x3C, 0x66, 0x06, 0x3C, 0x60, 0x66, 0x7E, 0x00, // 32 + 0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00, // 33 + 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x1E, 0x00, // 34 + 0x7E, 0x62, 0x60, 0x7C, 0x06, 0x66, 0x3C, 0x00, // 35 + 0x3C, 0x66, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00, // 36 + 0x7E, 0x66, 0x06, 0x0C, 0x18, 0x18, 0x18, 0x00, // 37 + 0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, // 38 + 0x3C, 0x66, 0x66, 0x3E, 0x06, 0x66, 0x3C, 0x00, // 39 + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, // 3A + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30, // 3B + 0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x00, // 3C + 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7E, 0x00, 0x00, // 3D + 0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0x00, // 3E + 0x3C, 0x66, 0x66, 0x0C, 0x18, 0x00, 0x18, 0x00, // 3F + 0x7C, 0xC6, 0xDE, 0xDE, 0xDE, 0xC0, 0x7C, 0x00, // 40 + 0x18, 0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x00, // 41 + 0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xFC, 0x00, // 42 + 0x3C, 0x66, 0xC0, 0xC0, 0xC0, 0x66, 0x3C, 0x00, // 43 + 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, // 44 + 0xFE, 0x62, 0x68, 0x78, 0x68, 0x62, 0xFE, 0x00, // 45 + 0xFE, 0x62, 0x68, 0x78, 0x68, 0x60, 0xF0, 0x00, // 46 + 0x3C, 0x66, 0xC0, 0xC0, 0xCE, 0x66, 0x3E, 0x00, // 47 + 0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // 48 + 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, // 49 + 0x1E, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00, // 4A + 0xE6, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0xE6, 0x00, // 4B + 0xF0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00, // 4C + 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00, // 4D + 0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00, // 4E + 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00, // 4F + 0xFC, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, // 50 + 0x38, 0x6C, 0xC6, 0xC6, 0xDA, 0xCC, 0x76, 0x00, // 51 + 0xFC, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0xE6, 0x00, // 52 + 0x3C, 0x66, 0x60, 0x3C, 0x06, 0x66, 0x3C, 0x00, // 53 + 0x7E, 0x5A, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // 54 + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // 55 + 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // 56 + 0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x00, // 57 + 0xC6, 0x6C, 0x38, 0x38, 0x6C, 0xC6, 0xC6, 0x00, // 58 + 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x3C, 0x00, // 59 + 0xFE, 0xC6, 0x8C, 0x18, 0x32, 0x66, 0xFE, 0x00, // 5A + 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, // 5B + 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00, // 5C + 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00, // 5D + 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x00, // 5E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // 5F + 0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, // 60 + 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, // 61 + 0xE0, 0x60, 0x7C, 0x66, 0x66, 0x66, 0xDC, 0x00, // 62 + 0x00, 0x00, 0x3C, 0x66, 0x60, 0x66, 0x3C, 0x00, // 63 + 0x1C, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, // 64 + 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, // 65 + 0x1C, 0x36, 0x30, 0x78, 0x30, 0x30, 0x78, 0x00, // 66 + 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x7C, // 67 + 0xE0, 0x60, 0x6C, 0x76, 0x66, 0x66, 0xE6, 0x00, // 68 + 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00, // 69 + 0x06, 0x00, 0x0E, 0x06, 0x06, 0x66, 0x66, 0x3C, // 6A + 0xE0, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0xE6, 0x00, // 6B + 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // 6C + 0x00, 0x00, 0x6C, 0xFE, 0xD6, 0xD6, 0xC6, 0x00, // 6D + 0x00, 0x00, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x00, // 6E + 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, // 6F + 0x00, 0x00, 0xDC, 0x66, 0x66, 0x7C, 0x60, 0xF0, // 70 + 0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0x1E, // 71 + 0x00, 0x00, 0xDC, 0x76, 0x60, 0x60, 0xF0, 0x00, // 72 + 0x00, 0x00, 0x3C, 0x60, 0x3C, 0x06, 0x7C, 0x00, // 73 + 0x30, 0x30, 0x7C, 0x30, 0x30, 0x36, 0x1C, 0x00, // 74 + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, // 75 + 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // 76 + 0x00, 0x00, 0xC6, 0xD6, 0xD6, 0xFE, 0x6C, 0x00, // 77 + 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00, // 78 + 0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x7C, // 79 + 0x00, 0x00, 0x7E, 0x4C, 0x18, 0x32, 0x7E, 0x00, // 7A + 0x0E, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0E, 0x00, // 7B + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // 7C + 0x70, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x70, 0x00, // 7D + 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 7E + 0xCC, 0x33, 0xCC, 0x33, 0xCC, 0x33, 0xCC, 0x33, // 7F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 80 + 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, // 81 + 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, // 82 + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // 83 + 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, // 84 + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, // 85 + 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, // 86 + 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, // 87 + 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, // 88 + 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, // 89 + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, // 8A + 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, // 8B + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // 8C + 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, // 8D + 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, // 8E + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 8F + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, // 90 + 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, // 91 + 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x00, 0x00, 0x00, // 92 + 0x18, 0x18, 0x18, 0x1F, 0x0F, 0x00, 0x00, 0x00, // 93 + 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, // 94 + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // 95 + 0x00, 0x00, 0x00, 0x0F, 0x1F, 0x18, 0x18, 0x18, // 96 + 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x18, 0x18, 0x18, // 97 + 0x00, 0x00, 0x00, 0xF8, 0xF8, 0x00, 0x00, 0x00, // 98 + 0x18, 0x18, 0x18, 0xF8, 0xF0, 0x00, 0x00, 0x00, // 99 + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, // 9A + 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x00, 0x00, 0x00, // 9B + 0x00, 0x00, 0x00, 0xF0, 0xF8, 0x18, 0x18, 0x18, // 9C + 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x18, 0x18, 0x18, // 9D + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x18, 0x18, // 9E + 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x18, 0x18, 0x18, // 9F + 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, // A0 + 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, // A1 + 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // A2 + 0x3C, 0x66, 0x60, 0xF8, 0x60, 0x66, 0xFE, 0x00, // A3 + 0x38, 0x44, 0xBA, 0xA2, 0xBA, 0x44, 0x38, 0x00, // A4 + 0x7E, 0xF4, 0xF4, 0x74, 0x34, 0x34, 0x34, 0x00, // A5 + 0x1E, 0x30, 0x38, 0x6C, 0x38, 0x18, 0xF0, 0x00, // A6 + 0x18, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, // A7 + 0x40, 0xC0, 0x44, 0x4C, 0x54, 0x1E, 0x04, 0x00, // A8 + 0x40, 0xC0, 0x4C, 0x52, 0x44, 0x08, 0x1E, 0x00, // A9 + 0xE0, 0x10, 0x62, 0x16, 0xEA, 0x0F, 0x02, 0x00, // AA + 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x7E, 0x00, // AB + 0x18, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x18, 0x00, // AC + 0x00, 0x00, 0x00, 0x7E, 0x06, 0x06, 0x00, 0x00, // AD + 0x18, 0x00, 0x18, 0x30, 0x66, 0x66, 0x3C, 0x00, // AE + 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // AF + 0x00, 0x00, 0x73, 0xDE, 0xCC, 0xDE, 0x73, 0x00, // B0 + 0x7C, 0xC6, 0xC6, 0xFC, 0xC6, 0xC6, 0xF8, 0xC0, // B1 + 0x00, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, // B2 + 0x3C, 0x60, 0x60, 0x3C, 0x66, 0x66, 0x3C, 0x00, // B3 + 0x00, 0x00, 0x1E, 0x30, 0x7C, 0x30, 0x1E, 0x00, // B4 + 0x38, 0x6C, 0xC6, 0xFE, 0xC6, 0x6C, 0x38, 0x00, // B5 + 0x00, 0xC0, 0x60, 0x30, 0x38, 0x6C, 0xC6, 0x00, // B6 + 0x00, 0x00, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, // B7 + 0x00, 0x00, 0x00, 0xFE, 0x6C, 0x6C, 0x6C, 0x00, // B8 + 0x00, 0x00, 0x00, 0x7E, 0xD8, 0xD8, 0x70, 0x00, // B9 + 0x03, 0x06, 0x0C, 0x3C, 0x66, 0x3C, 0x60, 0xC0, // BA + 0x03, 0x06, 0x0C, 0x66, 0x66, 0x3C, 0x60, 0xC0, // BB + 0x00, 0xE6, 0x3C, 0x18, 0x38, 0x6C, 0xC7, 0x00, // BC + 0x00, 0x00, 0x66, 0xC3, 0xDB, 0xDB, 0x7E, 0x00, // BD + 0xFE, 0xC6, 0x60, 0x30, 0x60, 0xC6, 0xFE, 0x00, // BE + 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x6C, 0xEE, 0x00, // BF + 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x00, // C0 + 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00, 0x00, 0x00, // C1 + 0x00, 0x00, 0x00, 0x01, 0x03, 0x06, 0x0C, 0x18, // C2 + 0x00, 0x00, 0x00, 0x80, 0xC0, 0x60, 0x30, 0x18, // C3 + 0x18, 0x3C, 0x66, 0xC3, 0x81, 0x00, 0x00, 0x00, // C4 + 0x18, 0x0C, 0x06, 0x03, 0x03, 0x06, 0x0C, 0x18, // C5 + 0x00, 0x00, 0x00, 0x81, 0xC3, 0x66, 0x3C, 0x18, // C6 + 0x18, 0x30, 0x60, 0xC0, 0xC0, 0x60, 0x30, 0x18, // C7 + 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, // C8 + 0x18, 0x0C, 0x06, 0x83, 0xC1, 0x60, 0x30, 0x18, // C9 + 0x18, 0x3C, 0x66, 0xC3, 0xC3, 0x66, 0x3C, 0x18, // CA + 0xC3, 0xE7, 0x7E, 0x3C, 0x3C, 0x7E, 0xE7, 0xC3, // CB + 0x03, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0xC0, // CC + 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03, // CD + 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, // CE + 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, // CF + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // D0 + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // D1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, // D2 + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // D3 + 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, // D4 + 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, // D5 + 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF, // D6 + 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF, // D7 + 0xAA, 0x55, 0xAA, 0x55, 0x00, 0x00, 0x00, 0x00, // D8 + 0x0A, 0x05, 0x0A, 0x05, 0x0A, 0x05, 0x0A, 0x05, // D9 + 0x00, 0x00, 0x00, 0x00, 0xAA, 0x55, 0xAA, 0x55, // DA + 0xA0, 0x50, 0xA0, 0x50, 0xA0, 0x50, 0xA0, 0x50, // DB + 0xAA, 0x54, 0xA8, 0x50, 0xA0, 0x40, 0x80, 0x00, // DC + 0xAA, 0x55, 0x2A, 0x15, 0x0A, 0x05, 0x02, 0x01, // DD + 0x01, 0x02, 0x05, 0x0A, 0x15, 0x2A, 0x55, 0xAA, // DE + 0x00, 0x80, 0x40, 0xA0, 0x50, 0xA8, 0x54, 0xAA, // DF + 0x7E, 0xFF, 0x99, 0xFF, 0xBD, 0xC3, 0xFF, 0x7E, // E0 + 0x7E, 0xFF, 0x99, 0xFF, 0xC3, 0xBD, 0xFF, 0x7E, // E1 + 0x38, 0x38, 0xFE, 0xFE, 0xFE, 0x10, 0x38, 0x00, // E2 + 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, 0x00, // E3 + 0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, // E4 + 0x10, 0x38, 0x7C, 0xFE, 0xFE, 0x10, 0x38, 0x00, // E5 + 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0x66, 0x3C, 0x00, // E6 + 0x00, 0x3C, 0x7E, 0xFF, 0xFF, 0x7E, 0x3C, 0x00, // E7 + 0x00, 0x7E, 0x66, 0x66, 0x66, 0x66, 0x7E, 0x00, // E8 + 0x00, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x00, // E9 + 0x0F, 0x07, 0x0D, 0x78, 0xCC, 0xCC, 0xCC, 0x78, // EA + 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18, // EB + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x7C, 0x38, // EC + 0x18, 0x1C, 0x1E, 0x1B, 0x18, 0x78, 0xF8, 0x70, // ED + 0x99, 0x5A, 0x24, 0xC3, 0xC3, 0x24, 0x5A, 0x99, // EE + 0x10, 0x38, 0x38, 0x38, 0x38, 0x38, 0x7C, 0xD6, // EF + 0x18, 0x3C, 0x7E, 0xFF, 0x18, 0x18, 0x18, 0x18, // F0 + 0x18, 0x18, 0x18, 0x18, 0xFF, 0x7E, 0x3C, 0x18, // F1 + 0x10, 0x30, 0x70, 0xFF, 0xFF, 0x70, 0x30, 0x10, // F2 + 0x08, 0x0C, 0x0E, 0xFF, 0xFF, 0x0E, 0x0C, 0x08, // F3 + 0x00, 0x00, 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x00, // F4 + 0x00, 0x00, 0xFF, 0xFF, 0x7E, 0x3C, 0x18, 0x00, // F5 + 0x80, 0xE0, 0xF8, 0xFE, 0xF8, 0xE0, 0x80, 0x00, // F6 + 0x02, 0x0E, 0x3E, 0xFE, 0x3E, 0x0E, 0x02, 0x00, // F7 + 0x38, 0x38, 0x92, 0x7C, 0x10, 0x28, 0x28, 0x28, // F8 + 0x38, 0x38, 0x10, 0xFE, 0x10, 0x28, 0x44, 0x82, // F9 + 0x38, 0x38, 0x12, 0x7C, 0x90, 0x28, 0x24, 0x22, // FA + 0x38, 0x38, 0x90, 0x7C, 0x12, 0x28, 0x48, 0x88, // FB + 0x00, 0x3C, 0x18, 0x3C, 0x3C, 0x3C, 0x18, 0x00, // FC + 0x3C, 0xFF, 0xFF, 0x18, 0x0C, 0x18, 0x30, 0x18, // FD + 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x7E, 0x3C, 0x18, // FE + 0x00, 0x24, 0x66, 0xFF, 0x66, 0x24, 0x00, 0x00, // FF +}; +static const uint8_t _sdtx_font_c64[2048] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 00 + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, // 01 + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // 02 + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 03 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // 04 + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // 05 + 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, // 06 + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // 07 + 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0x33, 0x33, // 08 + 0xCC, 0x99, 0x33, 0x66, 0xCC, 0x99, 0x33, 0x66, // 09 + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // 0A + 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x18, 0x18, 0x18, // 0B + 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, // 0C + 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x00, 0x00, 0x00, // 0D + 0x00, 0x00, 0x00, 0xF8, 0xF8, 0x18, 0x18, 0x18, // 0E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, // 0F + 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x18, 0x18, 0x18, // 10 + 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x00, 0x00, 0x00, // 11 + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x18, 0x18, // 12 + 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x18, 0x18, 0x18, // 13 + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // 14 + 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, // 15 + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, // 16 + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 17 + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, // 18 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, // 19 + 0x01, 0x03, 0x06, 0x6C, 0x78, 0x70, 0x60, 0x00, // 1A + 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, // 1B + 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, // 1C + 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x00, 0x00, 0x00, // 1D + 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, // 1E + 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, // 1F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 20 + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00, // 21 + 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, // 22 + 0x66, 0x66, 0xFF, 0x66, 0xFF, 0x66, 0x66, 0x00, // 23 + 0x18, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x18, 0x00, // 24 + 0x62, 0x66, 0x0C, 0x18, 0x30, 0x66, 0x46, 0x00, // 25 + 0x3C, 0x66, 0x3C, 0x38, 0x67, 0x66, 0x3F, 0x00, // 26 + 0x06, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, // 27 + 0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00, // 28 + 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00, // 29 + 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, // 2A + 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, // 2B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, // 2C + 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, // 2D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, // 2E + 0x00, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00, // 2F + 0x3C, 0x66, 0x6E, 0x76, 0x66, 0x66, 0x3C, 0x00, // 30 + 0x18, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7E, 0x00, // 31 + 0x3C, 0x66, 0x06, 0x0C, 0x30, 0x60, 0x7E, 0x00, // 32 + 0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00, // 33 + 0x06, 0x0E, 0x1E, 0x66, 0x7F, 0x06, 0x06, 0x00, // 34 + 0x7E, 0x60, 0x7C, 0x06, 0x06, 0x66, 0x3C, 0x00, // 35 + 0x3C, 0x66, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00, // 36 + 0x7E, 0x66, 0x0C, 0x18, 0x18, 0x18, 0x18, 0x00, // 37 + 0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, // 38 + 0x3C, 0x66, 0x66, 0x3E, 0x06, 0x66, 0x3C, 0x00, // 39 + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, // 3A + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, // 3B + 0x0E, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0E, 0x00, // 3C + 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00, // 3D + 0x70, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x70, 0x00, // 3E + 0x3C, 0x66, 0x06, 0x0C, 0x18, 0x00, 0x18, 0x00, // 3F + 0x3C, 0x66, 0x6E, 0x6E, 0x60, 0x62, 0x3C, 0x00, // 40 + 0x18, 0x3C, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // 41 + 0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00, // 42 + 0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00, // 43 + 0x78, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0x78, 0x00, // 44 + 0x7E, 0x60, 0x60, 0x78, 0x60, 0x60, 0x7E, 0x00, // 45 + 0x7E, 0x60, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00, // 46 + 0x3C, 0x66, 0x60, 0x6E, 0x66, 0x66, 0x3C, 0x00, // 47 + 0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // 48 + 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // 49 + 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x6C, 0x38, 0x00, // 4A + 0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66, 0x00, // 4B + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00, // 4C + 0x63, 0x77, 0x7F, 0x6B, 0x63, 0x63, 0x63, 0x00, // 4D + 0x66, 0x76, 0x7E, 0x7E, 0x6E, 0x66, 0x66, 0x00, // 4E + 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // 4F + 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00, // 50 + 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x0E, 0x00, // 51 + 0x7C, 0x66, 0x66, 0x7C, 0x78, 0x6C, 0x66, 0x00, // 52 + 0x3C, 0x66, 0x60, 0x3C, 0x06, 0x66, 0x3C, 0x00, // 53 + 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // 54 + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // 55 + 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // 56 + 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00, // 57 + 0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, // 58 + 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00, // 59 + 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00, // 5A + 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, // 5B + 0x0C, 0x12, 0x30, 0x7C, 0x30, 0x62, 0xFC, 0x00, // 5C + 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00, // 5D + 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, // 5E + 0x00, 0x10, 0x30, 0x7F, 0x7F, 0x30, 0x10, 0x00, // 5F + 0x3C, 0x66, 0x6E, 0x6E, 0x60, 0x62, 0x3C, 0x00, // 60 + 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, // 61 + 0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, // 62 + 0x00, 0x00, 0x3C, 0x60, 0x60, 0x60, 0x3C, 0x00, // 63 + 0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3E, 0x00, // 64 + 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, // 65 + 0x00, 0x0E, 0x18, 0x3E, 0x18, 0x18, 0x18, 0x00, // 66 + 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x7C, // 67 + 0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x00, // 68 + 0x00, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3C, 0x00, // 69 + 0x00, 0x06, 0x00, 0x06, 0x06, 0x06, 0x06, 0x3C, // 6A + 0x00, 0x60, 0x60, 0x6C, 0x78, 0x6C, 0x66, 0x00, // 6B + 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // 6C + 0x00, 0x00, 0x66, 0x7F, 0x7F, 0x6B, 0x63, 0x00, // 6D + 0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00, // 6E + 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, // 6F + 0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, // 70 + 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x06, // 71 + 0x00, 0x00, 0x7C, 0x66, 0x60, 0x60, 0x60, 0x00, // 72 + 0x00, 0x00, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x00, // 73 + 0x00, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x0E, 0x00, // 74 + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, // 75 + 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // 76 + 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x3E, 0x36, 0x00, // 77 + 0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00, // 78 + 0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x0C, 0x78, // 79 + 0x00, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x7E, 0x00, // 7A + 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, // 7B + 0x0C, 0x12, 0x30, 0x7C, 0x30, 0x62, 0xFC, 0x00, // 7C + 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00, // 7D + 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, // 7E + 0x00, 0x10, 0x30, 0x7F, 0x7F, 0x30, 0x10, 0x00, // 7F + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, // 80 + 0x08, 0x1C, 0x3E, 0x7F, 0x7F, 0x1C, 0x3E, 0x00, // 81 + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // 82 + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, // 83 + 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // 84 + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, // 85 + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, // 86 + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // 87 + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, // 88 + 0x00, 0x00, 0x00, 0xE0, 0xF0, 0x38, 0x18, 0x18, // 89 + 0x18, 0x18, 0x1C, 0x0F, 0x07, 0x00, 0x00, 0x00, // 8A + 0x18, 0x18, 0x38, 0xF0, 0xE0, 0x00, 0x00, 0x00, // 8B + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, 0xFF, // 8C + 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03, // 8D + 0x03, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0xC0, // 8E + 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // 8F + 0xFF, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // 90 + 0x00, 0x3C, 0x7E, 0x7E, 0x7E, 0x7E, 0x3C, 0x00, // 91 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, // 92 + 0x36, 0x7F, 0x7F, 0x7F, 0x3E, 0x1C, 0x08, 0x00, // 93 + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, // 94 + 0x00, 0x00, 0x00, 0x07, 0x0F, 0x1C, 0x18, 0x18, // 95 + 0xC3, 0xE7, 0x7E, 0x3C, 0x3C, 0x7E, 0xE7, 0xC3, // 96 + 0x00, 0x3C, 0x7E, 0x66, 0x66, 0x7E, 0x3C, 0x00, // 97 + 0x18, 0x18, 0x66, 0x66, 0x18, 0x18, 0x3C, 0x00, // 98 + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, // 99 + 0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x1C, 0x08, 0x00, // 9A + 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x18, 0x18, 0x18, // 9B + 0xC0, 0xC0, 0x30, 0x30, 0xC0, 0xC0, 0x30, 0x30, // 9C + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // 9D + 0x00, 0x00, 0x03, 0x3E, 0x76, 0x36, 0x36, 0x00, // 9E + 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, // 9F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // A0 + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, // A1 + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // A2 + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // A3 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // A4 + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // A5 + 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, // A6 + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // A7 + 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0x33, 0x33, // A8 + 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, // A9 + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // AA + 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x18, 0x18, 0x18, // AB + 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, // AC + 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x00, 0x00, 0x00, // AD + 0x00, 0x00, 0x00, 0xF8, 0xF8, 0x18, 0x18, 0x18, // AE + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, // AF + 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x18, 0x18, 0x18, // B0 + 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x00, 0x00, 0x00, // B1 + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x18, 0x18, // B2 + 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x18, 0x18, 0x18, // B3 + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // B4 + 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, // B5 + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, // B6 + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // B7 + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, // B8 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, // B9 + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, 0xFF, // BA + 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, // BB + 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, // BC + 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x00, 0x00, 0x00, // BD + 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, // BE + 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, // BF + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, // C0 + 0xF7, 0xE3, 0xC1, 0x80, 0x80, 0xE3, 0xC1, 0xFF, // C1 + 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, // C2 + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, // C3 + 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // C4 + 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // C5 + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, // C6 + 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, // C7 + 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, // C8 + 0xFF, 0xFF, 0xFF, 0x1F, 0x0F, 0xC7, 0xE7, 0xE7, // C9 + 0xE7, 0xE7, 0xE3, 0xF0, 0xF8, 0xFF, 0xFF, 0xFF, // CA + 0xE7, 0xE7, 0xC7, 0x0F, 0x1F, 0xFF, 0xFF, 0xFF, // CB + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x00, 0x00, // CC + 0x3F, 0x1F, 0x8F, 0xC7, 0xE3, 0xF1, 0xF8, 0xFC, // CD + 0xFC, 0xF8, 0xF1, 0xE3, 0xC7, 0x8F, 0x1F, 0x3F, // CE + 0x00, 0x00, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, // CF + 0x00, 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, // D0 + 0xFF, 0xC3, 0x81, 0x81, 0x81, 0x81, 0xC3, 0xFF, // D1 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, // D2 + 0xC9, 0x80, 0x80, 0x80, 0xC1, 0xE3, 0xF7, 0xFF, // D3 + 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, // D4 + 0xFF, 0xFF, 0xFF, 0xF8, 0xF0, 0xE3, 0xE7, 0xE7, // D5 + 0x3C, 0x18, 0x81, 0xC3, 0xC3, 0x81, 0x18, 0x3C, // D6 + 0xFF, 0xC3, 0x81, 0x99, 0x99, 0x81, 0xC3, 0xFF, // D7 + 0xE7, 0xE7, 0x99, 0x99, 0xE7, 0xE7, 0xC3, 0xFF, // D8 + 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, // D9 + 0xF7, 0xE3, 0xC1, 0x80, 0xC1, 0xE3, 0xF7, 0xFF, // DA + 0xE7, 0xE7, 0xE7, 0x00, 0x00, 0xE7, 0xE7, 0xE7, // DB + 0x3F, 0x3F, 0xCF, 0xCF, 0x3F, 0x3F, 0xCF, 0xCF, // DC + 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, // DD + 0xFF, 0xFF, 0xFC, 0xC1, 0x89, 0xC9, 0xC9, 0xFF, // DE + 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, // DF + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // E0 + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, // E1 + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // E2 + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // E3 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, // E4 + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, // E5 + 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, // E6 + 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, // E7 + 0xFF, 0xFF, 0xFF, 0xFF, 0x33, 0x33, 0xCC, 0xCC, // E8 + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, // E9 + 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, // EA + 0xE7, 0xE7, 0xE7, 0xE0, 0xE0, 0xE7, 0xE7, 0xE7, // EB + 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, // EC + 0xE7, 0xE7, 0xE7, 0xE0, 0xE0, 0xFF, 0xFF, 0xFF, // ED + 0xFF, 0xFF, 0xFF, 0x07, 0x07, 0xE7, 0xE7, 0xE7, // EE + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, // EF + 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xE7, 0xE7, 0xE7, // F0 + 0xE7, 0xE7, 0xE7, 0x00, 0x00, 0xFF, 0xFF, 0xFF, // F1 + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xE7, 0xE7, 0xE7, // F2 + 0xE7, 0xE7, 0xE7, 0x07, 0x07, 0xE7, 0xE7, 0xE7, // F3 + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, // F4 + 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, // F5 + 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, // F6 + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // F7 + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // F8 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, // F9 + 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, 0x00, // FA + 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, // FB + 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, // FC + 0xE7, 0xE7, 0xE7, 0x07, 0x07, 0xFF, 0xFF, 0xFF, // FD + 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, // FE + 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, // FF +}; +static const uint8_t _sdtx_font_oric[2048] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 00 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 01 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 02 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 03 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 04 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 05 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 06 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 07 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 08 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 09 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0A + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0C + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 10 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 11 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 12 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 13 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 14 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 15 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 16 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 17 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 18 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 19 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1A + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1C + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 20 + 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00, // 21 + 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, // 22 + 0x14, 0x14, 0x3E, 0x14, 0x3E, 0x14, 0x14, 0x00, // 23 + 0x08, 0x1E, 0x28, 0x1C, 0x0A, 0x3C, 0x08, 0x00, // 24 + 0x30, 0x32, 0x04, 0x08, 0x10, 0x26, 0x06, 0x00, // 25 + 0x10, 0x28, 0x28, 0x10, 0x2A, 0x24, 0x1A, 0x00, // 26 + 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // 27 + 0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, // 28 + 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08, 0x00, // 29 + 0x08, 0x2A, 0x1C, 0x08, 0x1C, 0x2A, 0x08, 0x00, // 2A + 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, // 2B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, // 2C + 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, // 2D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // 2E + 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, // 2F + 0x1C, 0x22, 0x26, 0x2A, 0x32, 0x22, 0x1C, 0x00, // 30 + 0x08, 0x18, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, // 31 + 0x1C, 0x22, 0x02, 0x04, 0x08, 0x10, 0x3E, 0x00, // 32 + 0x3E, 0x02, 0x04, 0x0C, 0x02, 0x22, 0x1C, 0x00, // 33 + 0x04, 0x0C, 0x14, 0x24, 0x3E, 0x04, 0x04, 0x00, // 34 + 0x3E, 0x20, 0x3C, 0x02, 0x02, 0x22, 0x1C, 0x00, // 35 + 0x0C, 0x10, 0x20, 0x3C, 0x22, 0x22, 0x1C, 0x00, // 36 + 0x3E, 0x02, 0x04, 0x08, 0x10, 0x10, 0x10, 0x00, // 37 + 0x1C, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x1C, 0x00, // 38 + 0x1C, 0x22, 0x22, 0x1E, 0x02, 0x04, 0x18, 0x00, // 39 + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, // 3A + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, // 3B + 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00, // 3C + 0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x00, // 3D + 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x00, // 3E + 0x1C, 0x22, 0x04, 0x08, 0x08, 0x00, 0x08, 0x00, // 3F + 0x1C, 0x22, 0x2A, 0x2E, 0x2C, 0x20, 0x1E, 0x00, // 40 + 0x08, 0x14, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x00, // 41 + 0x3C, 0x22, 0x22, 0x3C, 0x22, 0x22, 0x3C, 0x00, // 42 + 0x1C, 0x22, 0x20, 0x20, 0x20, 0x22, 0x1C, 0x00, // 43 + 0x3C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3C, 0x00, // 44 + 0x3E, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x3E, 0x00, // 45 + 0x3E, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x20, 0x00, // 46 + 0x1E, 0x20, 0x20, 0x20, 0x26, 0x22, 0x1E, 0x00, // 47 + 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, // 48 + 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, // 49 + 0x02, 0x02, 0x02, 0x02, 0x02, 0x22, 0x1C, 0x00, // 4A + 0x22, 0x24, 0x28, 0x30, 0x28, 0x24, 0x22, 0x00, // 4B + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3E, 0x00, // 4C + 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x00, // 4D + 0x22, 0x22, 0x32, 0x2A, 0x26, 0x22, 0x22, 0x00, // 4E + 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, // 4F + 0x3C, 0x22, 0x22, 0x3C, 0x20, 0x20, 0x20, 0x00, // 50 + 0x1C, 0x22, 0x22, 0x22, 0x2A, 0x24, 0x1A, 0x00, // 51 + 0x3C, 0x22, 0x22, 0x3C, 0x28, 0x24, 0x22, 0x00, // 52 + 0x1C, 0x22, 0x20, 0x1C, 0x02, 0x22, 0x1C, 0x00, // 53 + 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, // 54 + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, // 55 + 0x22, 0x22, 0x22, 0x22, 0x22, 0x14, 0x08, 0x00, // 56 + 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, // 57 + 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, // 58 + 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, // 59 + 0x3E, 0x02, 0x04, 0x08, 0x10, 0x20, 0x3E, 0x00, // 5A + 0x1E, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1E, 0x00, // 5B + 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00, // 5C + 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x3C, 0x00, // 5D + 0x08, 0x14, 0x2A, 0x08, 0x08, 0x08, 0x08, 0x00, // 5E + 0x0E, 0x11, 0x3C, 0x10, 0x3C, 0x11, 0x0E, 0x00, // 5F + 0x0C, 0x12, 0x2D, 0x29, 0x29, 0x2D, 0x12, 0x0C, // 60 + 0x00, 0x00, 0x1C, 0x02, 0x1E, 0x22, 0x1E, 0x00, // 61 + 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x3C, 0x00, // 62 + 0x00, 0x00, 0x1E, 0x20, 0x20, 0x20, 0x1E, 0x00, // 63 + 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, // 64 + 0x00, 0x00, 0x1C, 0x22, 0x3E, 0x20, 0x1E, 0x00, // 65 + 0x0C, 0x12, 0x10, 0x3C, 0x10, 0x10, 0x10, 0x00, // 66 + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x1E, 0x02, 0x1C, // 67 + 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x00, // 68 + 0x08, 0x00, 0x18, 0x08, 0x08, 0x08, 0x1C, 0x00, // 69 + 0x04, 0x00, 0x0C, 0x04, 0x04, 0x04, 0x24, 0x18, // 6A + 0x20, 0x20, 0x22, 0x24, 0x38, 0x24, 0x22, 0x00, // 6B + 0x18, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, // 6C + 0x00, 0x00, 0x36, 0x2A, 0x2A, 0x2A, 0x22, 0x00, // 6D + 0x00, 0x00, 0x3C, 0x22, 0x22, 0x22, 0x22, 0x00, // 6E + 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, // 6F + 0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x20, 0x20, // 70 + 0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x02, 0x02, // 71 + 0x00, 0x00, 0x2E, 0x30, 0x20, 0x20, 0x20, 0x00, // 72 + 0x00, 0x00, 0x1E, 0x20, 0x1C, 0x02, 0x3C, 0x00, // 73 + 0x10, 0x10, 0x3C, 0x10, 0x10, 0x12, 0x0C, 0x00, // 74 + 0x00, 0x00, 0x22, 0x22, 0x22, 0x26, 0x1A, 0x00, // 75 + 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x08, 0x00, // 76 + 0x00, 0x00, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x00, // 77 + 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, // 78 + 0x00, 0x00, 0x22, 0x22, 0x22, 0x1E, 0x02, 0x1C, // 79 + 0x00, 0x00, 0x3E, 0x04, 0x08, 0x10, 0x3E, 0x00, // 7A + 0x0E, 0x18, 0x18, 0x30, 0x18, 0x18, 0x0E, 0x00, // 7B + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, // 7C + 0x38, 0x0C, 0x0C, 0x06, 0x0C, 0x0C, 0x38, 0x00, // 7D + 0x2A, 0x15, 0x2A, 0x15, 0x2A, 0x15, 0x2A, 0x15, // 7E + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, // 7F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 80 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 81 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 82 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 83 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 84 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 85 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 86 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 87 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 88 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8A + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8C + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 90 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 91 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 92 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 93 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 94 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 95 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 96 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 97 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 98 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 99 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9A + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9C + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // A0 + 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xFF, 0xF7, 0xFF, // A1 + 0xEB, 0xEB, 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // A2 + 0xEB, 0xEB, 0xC1, 0xEB, 0xC1, 0xEB, 0xEB, 0xFF, // A3 + 0xF7, 0xE1, 0xD7, 0xE3, 0xF5, 0xC3, 0xF7, 0xFF, // A4 + 0xCF, 0xCD, 0xFB, 0xF7, 0xEF, 0xD9, 0xF9, 0xFF, // A5 + 0xEF, 0xD7, 0xD7, 0xEF, 0xD5, 0xDB, 0xE5, 0xFF, // A6 + 0xF7, 0xF7, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // A7 + 0xF7, 0xEF, 0xDF, 0xDF, 0xDF, 0xEF, 0xF7, 0xFF, // A8 + 0xF7, 0xFB, 0xFD, 0xFD, 0xFD, 0xFB, 0xF7, 0xFF, // A9 + 0xF7, 0xD5, 0xE3, 0xF7, 0xE3, 0xD5, 0xF7, 0xFF, // AA + 0xFF, 0xF7, 0xF7, 0xC1, 0xF7, 0xF7, 0xFF, 0xFF, // AB + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xF7, 0xEF, // AC + 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, // AD + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, // AE + 0xFF, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xFF, 0xFF, // AF + 0xE3, 0xDD, 0xD9, 0xD5, 0xCD, 0xDD, 0xE3, 0xFF, // B0 + 0xF7, 0xE7, 0xF7, 0xF7, 0xF7, 0xF7, 0xE3, 0xFF, // B1 + 0xE3, 0xDD, 0xFD, 0xFB, 0xF7, 0xEF, 0xC1, 0xFF, // B2 + 0xC1, 0xFD, 0xFB, 0xF3, 0xFD, 0xDD, 0xE3, 0xFF, // B3 + 0xFB, 0xF3, 0xEB, 0xDB, 0xC1, 0xFB, 0xFB, 0xFF, // B4 + 0xC1, 0xDF, 0xC3, 0xFD, 0xFD, 0xDD, 0xE3, 0xFF, // B5 + 0xF3, 0xEF, 0xDF, 0xC3, 0xDD, 0xDD, 0xE3, 0xFF, // B6 + 0xC1, 0xFD, 0xFB, 0xF7, 0xEF, 0xEF, 0xEF, 0xFF, // B7 + 0xE3, 0xDD, 0xDD, 0xE3, 0xDD, 0xDD, 0xE3, 0xFF, // B8 + 0xE3, 0xDD, 0xDD, 0xE1, 0xFD, 0xFB, 0xE7, 0xFF, // B9 + 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, // BA + 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xF7, 0xF7, 0xEF, // BB + 0xFB, 0xF7, 0xEF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFF, // BC + 0xFF, 0xFF, 0xC1, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, // BD + 0xEF, 0xF7, 0xFB, 0xFD, 0xFB, 0xF7, 0xEF, 0xFF, // BE + 0xE3, 0xDD, 0xFB, 0xF7, 0xF7, 0xFF, 0xF7, 0xFF, // BF + 0xE3, 0xDD, 0xD5, 0xD1, 0xD3, 0xDF, 0xE1, 0xFF, // C0 + 0xF7, 0xEB, 0xDD, 0xDD, 0xC1, 0xDD, 0xDD, 0xFF, // C1 + 0xC3, 0xDD, 0xDD, 0xC3, 0xDD, 0xDD, 0xC3, 0xFF, // C2 + 0xE3, 0xDD, 0xDF, 0xDF, 0xDF, 0xDD, 0xE3, 0xFF, // C3 + 0xC3, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xC3, 0xFF, // C4 + 0xC1, 0xDF, 0xDF, 0xC3, 0xDF, 0xDF, 0xC1, 0xFF, // C5 + 0xC1, 0xDF, 0xDF, 0xC3, 0xDF, 0xDF, 0xDF, 0xFF, // C6 + 0xE1, 0xDF, 0xDF, 0xDF, 0xD9, 0xDD, 0xE1, 0xFF, // C7 + 0xDD, 0xDD, 0xDD, 0xC1, 0xDD, 0xDD, 0xDD, 0xFF, // C8 + 0xE3, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xE3, 0xFF, // C9 + 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xDD, 0xE3, 0xFF, // CA + 0xDD, 0xDB, 0xD7, 0xCF, 0xD7, 0xDB, 0xDD, 0xFF, // CB + 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xC1, 0xFF, // CC + 0xDD, 0xC9, 0xD5, 0xD5, 0xDD, 0xDD, 0xDD, 0xFF, // CD + 0xDD, 0xDD, 0xCD, 0xD5, 0xD9, 0xDD, 0xDD, 0xFF, // CE + 0xE3, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xE3, 0xFF, // CF + 0xC3, 0xDD, 0xDD, 0xC3, 0xDF, 0xDF, 0xDF, 0xFF, // D0 + 0xE3, 0xDD, 0xDD, 0xDD, 0xD5, 0xDB, 0xE5, 0xFF, // D1 + 0xC3, 0xDD, 0xDD, 0xC3, 0xD7, 0xDB, 0xDD, 0xFF, // D2 + 0xE3, 0xDD, 0xDF, 0xE3, 0xFD, 0xDD, 0xE3, 0xFF, // D3 + 0xC1, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xFF, // D4 + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xE3, 0xFF, // D5 + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xEB, 0xF7, 0xFF, // D6 + 0xDD, 0xDD, 0xDD, 0xD5, 0xD5, 0xC9, 0xDD, 0xFF, // D7 + 0xDD, 0xDD, 0xEB, 0xF7, 0xEB, 0xDD, 0xDD, 0xFF, // D8 + 0xDD, 0xDD, 0xEB, 0xF7, 0xF7, 0xF7, 0xF7, 0xFF, // D9 + 0xC1, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xC1, 0xFF, // DA + 0xE1, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xE1, 0xFF, // DB + 0xFF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0xFF, 0xFF, // DC + 0xC3, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xC3, 0xFF, // DD + 0xF7, 0xEB, 0xD5, 0xF7, 0xF7, 0xF7, 0xF7, 0xFF, // DE + 0xF1, 0xEE, 0xC3, 0xEF, 0xC3, 0xEE, 0xF1, 0xFF, // DF + 0xF3, 0xED, 0xD2, 0xD6, 0xD6, 0xD2, 0xED, 0xF3, // E0 + 0xFF, 0xFF, 0xE3, 0xFD, 0xE1, 0xDD, 0xE1, 0xFF, // E1 + 0xDF, 0xDF, 0xC3, 0xDD, 0xDD, 0xDD, 0xC3, 0xFF, // E2 + 0xFF, 0xFF, 0xE1, 0xDF, 0xDF, 0xDF, 0xE1, 0xFF, // E3 + 0xFD, 0xFD, 0xE1, 0xDD, 0xDD, 0xDD, 0xE1, 0xFF, // E4 + 0xFF, 0xFF, 0xE3, 0xDD, 0xC1, 0xDF, 0xE1, 0xFF, // E5 + 0xF3, 0xED, 0xEF, 0xC3, 0xEF, 0xEF, 0xEF, 0xFF, // E6 + 0xFF, 0xFF, 0xE3, 0xDD, 0xDD, 0xE1, 0xFD, 0xE3, // E7 + 0xDF, 0xDF, 0xC3, 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, // E8 + 0xF7, 0xFF, 0xE7, 0xF7, 0xF7, 0xF7, 0xE3, 0xFF, // E9 + 0xFB, 0xFF, 0xF3, 0xFB, 0xFB, 0xFB, 0xDB, 0xE7, // EA + 0xDF, 0xDF, 0xDD, 0xDB, 0xC7, 0xDB, 0xDD, 0xFF, // EB + 0xE7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xE3, 0xFF, // EC + 0xFF, 0xFF, 0xC9, 0xD5, 0xD5, 0xD5, 0xDD, 0xFF, // ED + 0xFF, 0xFF, 0xC3, 0xDD, 0xDD, 0xDD, 0xDD, 0xFF, // EE + 0xFF, 0xFF, 0xE3, 0xDD, 0xDD, 0xDD, 0xE3, 0xFF, // EF + 0xFF, 0xFF, 0xC3, 0xDD, 0xDD, 0xC3, 0xDF, 0xDF, // F0 + 0xFF, 0xFF, 0xE1, 0xDD, 0xDD, 0xE1, 0xFD, 0xFD, // F1 + 0xFF, 0xFF, 0xD1, 0xCF, 0xDF, 0xDF, 0xDF, 0xFF, // F2 + 0xFF, 0xFF, 0xE1, 0xDF, 0xE3, 0xFD, 0xC3, 0xFF, // F3 + 0xEF, 0xEF, 0xC3, 0xEF, 0xEF, 0xED, 0xF3, 0xFF, // F4 + 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xD9, 0xE5, 0xFF, // F5 + 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xEB, 0xF7, 0xFF, // F6 + 0xFF, 0xFF, 0xDD, 0xDD, 0xD5, 0xD5, 0xC9, 0xFF, // F7 + 0xFF, 0xFF, 0xDD, 0xEB, 0xF7, 0xEB, 0xDD, 0xFF, // F8 + 0xFF, 0xFF, 0xDD, 0xDD, 0xDD, 0xE1, 0xFD, 0xE3, // F9 + 0xFF, 0xFF, 0xC1, 0xFB, 0xF7, 0xEF, 0xC1, 0xFF, // FA + 0xF1, 0xE7, 0xE7, 0xCF, 0xE7, 0xE7, 0xF1, 0xFF, // FB + 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, // FC + 0xC7, 0xF3, 0xF3, 0xF9, 0xF3, 0xF3, 0xC7, 0xFF, // FD + 0xD5, 0xEA, 0xD5, 0xEA, 0xD5, 0xEA, 0xD5, 0xEA, // FE + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // FF +}; + +/* + Embedded source code compiled with: + + sokol-shdc -i debugtext.glsl -o debugtext.h -l glsl410:glsl300es:hlsl4:metal_macos:metal_ios:metal_sim:wgsl -b + + (not that for Metal and D3D11 byte code, sokol-shdc must be run + on macOS and Windows) + + @vs vs + in vec2 position; + in vec2 texcoord0; + in vec4 color0; + out vec2 uv; + out vec4 color; + void main() { + gl_Position = vec4(position * vec2(2.0, -2.0) + vec2(-1.0, +1.0), 0.0, 1.0); + uv = texcoord0; + color = color0; + } + @end + + @fs fs + uniform texture2D tex; + uniform sampler smp; + in vec2 uv; + in vec4 color; + out vec4 frag_color; + void main() { + frag_color = texture(sampler2D(tex, smp), uv).xxxx * color; + } + @end + + @program debugtext vs fs +*/ +#if defined(SOKOL_GLCORE) +static const uint8_t _sdtx_vs_source_glsl410[343] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x6c,0x61, + 0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20, + 0x30,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65, + 0x63,0x32,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f, + 0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76, + 0x65,0x63,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x6c, + 0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d, + 0x20,0x31,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c, + 0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74, + 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x28,0x66, + 0x6d,0x61,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x2c,0x20,0x76,0x65,0x63, + 0x32,0x28,0x32,0x2e,0x30,0x2c,0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c,0x20,0x76,0x65, + 0x63,0x32,0x28,0x2d,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x29,0x2c,0x20, + 0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75, + 0x76,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const uint8_t _sdtx_fs_source_glsl410[224] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f, + 0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74, + 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32, + 0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x29,0x2e,0x78,0x78, + 0x78,0x78,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, + +}; +#elif defined(SOKOL_GLES3) +static const uint8_t _sdtx_vs_source_glsl300es[301] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x6f, + 0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32, + 0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63, + 0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x6f,0x75,0x74, + 0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79, + 0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32, + 0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30, + 0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b, + 0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x20,0x2a,0x20,0x76,0x65,0x63,0x32,0x28,0x32,0x2e,0x30,0x2c,0x20,0x2d,0x32,0x2e, + 0x30,0x29,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x28,0x2d,0x31,0x2e,0x30,0x2c,0x20, + 0x31,0x2e,0x30,0x29,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f, + 0x72,0x64,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const uint8_t _sdtx_fs_source_glsl300es[255] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d, + 0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69, + 0x6f,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a, + 0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x68,0x69,0x67,0x68,0x70,0x20, + 0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x75, + 0x76,0x3b,0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61, + 0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x29,0x2e,0x78,0x78,0x78, + 0x78,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_METAL) +static const uint8_t _sdtx_vs_bytecode_metal_macos[2796] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xec,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe0,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0xfe,0xa3,0xdd,0x3f,0xa3,0x19,0x66, + 0x48,0xdb,0x53,0x17,0xfa,0x47,0xab,0xcd,0x1c,0x32,0x10,0x34,0x47,0xde,0x1e,0x11, + 0x5c,0xfd,0x36,0x7a,0xb2,0xbe,0x26,0x50,0xa3,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x37,0x00,0x00,0x00,0x56,0x41,0x54, + 0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x80, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03,0x00,0x04,0x04,0x06, + 0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b, + 0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xcc,0x09,0x00,0x00,0xff,0xff,0xff,0xff, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x70,0x02,0x00,0x00,0x0b,0x82,0x20,0x00, + 0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49, + 0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62, + 0x80,0x10,0x45,0x02,0x42,0x92,0x0b,0x42,0x84,0x10,0x32,0x14,0x38,0x08,0x18,0x49, + 0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72, + 0x24,0x07,0xc8,0x08,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00, + 0x51,0x18,0x00,0x00,0x82,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8,0xff,0xff,0xff,0xff, + 0x01,0x90,0x00,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87,0x71,0xa0,0x07,0x76, + 0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72,0x20,0x87,0x36,0x20, + 0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88,0x07,0x79,0xa0,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0xc0,0x1c,0xc2,0x81, + 0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c, + 0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c,0xd8,0x81,0x1d,0xe6, + 0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87,0x72,0x98,0x87,0x79, + 0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72,0x68,0x03,0x73,0x80, + 0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xdc, + 0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda,0x00,0x1e,0xde,0x21, + 0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21,0x1d,0xda,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30,0x07,0x79,0x08,0x87, + 0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87,0x72,0x90,0x87,0x36, + 0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77,0x68,0x03,0x76,0x28, + 0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70,0x07,0x73,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1,0x1d,0xe0,0xa1,0x0d, + 0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87, + 0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87,0x79,0x68,0x03,0x73, + 0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d,0xca,0x81,0x1c,0xda, + 0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21, + 0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x70,0x87,0x77,0x68, + 0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38,0x87,0x36,0x68,0x87, + 0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0x62,0x1e,0xe8,0x21, + 0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1,0x1c,0xc6,0x81,0x1e, + 0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x0d,0xe6, + 0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87,0x79,0x08,0x07,0x73, + 0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e, + 0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d,0xe6,0xe1,0x1d,0xcc, + 0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00,0x73,0x08,0x07,0x76, + 0x98,0x87,0x72,0x00,0x36,0x18,0xc2,0xff,0xff,0xff,0xff,0x0f,0x80,0x04,0x50,0x00, + 0x49,0x18,0x00,0x00,0x02,0x00,0x00,0x00,0x13,0x82,0x60,0x42,0x20,0x00,0x00,0x00, + 0x89,0x20,0x00,0x00,0x11,0x00,0x00,0x00,0x32,0x22,0x08,0x09,0x20,0x64,0x85,0x04, + 0x13,0x22,0xa4,0x84,0x04,0x13,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x88,0x8c, + 0x0b,0x84,0x84,0x4c,0x10,0x34,0x33,0x00,0xc3,0x08,0x02,0x30,0x8c,0x40,0x00,0x76, + 0x08,0x91,0x42,0x4c,0x84,0x10,0x15,0x22,0x22,0x82,0x6c,0x20,0x60,0x8e,0x00,0x0c, + 0x52,0x20,0x87,0x11,0x88,0x64,0x04,0x00,0x00,0x00,0x00,0x00,0x13,0xb2,0x70,0x48, + 0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83, + 0x76,0x08,0x87,0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83,0x38, + 0x70,0x03,0x38,0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07,0x7a, + 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78,0xa0, + 0x07,0x78,0xd0,0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e, + 0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e,0x73, + 0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40, + 0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20,0x07, + 0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07,0x7a, + 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a,0x10, + 0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07,0x72, + 0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07, + 0x76,0x40,0x07,0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a, + 0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20, + 0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20,0x07, + 0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x72, + 0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0, + 0x06,0xf6,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07, + 0x71,0x20,0x07,0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70, + 0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0, + 0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x29,0x00,0x00,0x08,0x00,0x00, + 0x00,0x00,0x00,0x18,0xc2,0x1c,0x40,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x64,0x81, + 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90, + 0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0xca,0x12,0x18,0x01,0x28,0x82,0x42,0x28,0x08, + 0xd2,0xb1,0x84,0x26,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xb1,0x00,0x00,0x00, + 0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7, + 0x21,0x46,0x42,0x20,0x80,0x72,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3, + 0x2b,0x1b,0x62,0x24,0x02,0x22,0x24,0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4, + 0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06, + 0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac, + 0x65,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0x80,0x10, + 0x43,0x8c,0x44,0x48,0x8c,0x64,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x41,0x8e, + 0x44,0x48,0x84,0x64,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56, + 0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56, + 0x36,0x44,0x40,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65, + 0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c, + 0x65,0x43,0x04,0x64,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1, + 0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95, + 0xd1,0x8d,0xa1,0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x90,0x86,0x51, + 0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0xd7,0x5b,0x1d,0x1d,0x5c,0x1d, + 0x1d,0x97,0xba,0xb9,0x32,0x39,0x14,0xb6,0xb7,0x31,0x37,0x98,0x14,0x46,0x61,0x69, + 0x72,0x2e,0x61,0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x6e,0x61,0x6d,0x65,0x34, + 0xcc,0xd8,0xde,0xc2,0xe8,0x64,0xc8,0x84,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9, + 0x85,0xb5,0x95,0x51,0xa8,0xb3,0x1b,0xc2,0x20,0x0f,0x02,0x21,0x11,0x22,0x21,0x13, + 0x42,0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0xa1,0x61,0xc6, + 0xf6,0x16,0x46,0x47,0xc3,0x62,0xec,0x8d,0xed,0x4d,0x6e,0x08,0x83,0x3c,0x88,0x85, + 0x44,0xc8,0x85,0x4c,0x08,0x46,0x26,0x2c,0x4d,0xce,0x05,0xee,0x6d,0x2e,0x8d,0x2e, + 0xed,0xcd,0x8d,0xcb,0x19,0xdb,0x17,0xd4,0xdb,0x5c,0x1a,0x5d,0xda,0x9b,0xdb,0x10, + 0x05,0xd1,0x90,0x08,0xb9,0x90,0x09,0xd9,0x86,0x18,0x48,0x85,0x64,0x08,0x47,0x28, + 0x2c,0x4d,0xce,0xc5,0xae,0x4c,0x8e,0xae,0x0c,0xef,0x2b,0xcd,0x0d,0xae,0x8e,0x8e, + 0x52,0x58,0x9a,0x9c,0x0b,0xdb,0xdb,0x58,0x18,0x5d,0xda,0x9b,0xdb,0x57,0x9a,0x1b, + 0x59,0x19,0x1e,0xbd,0xb3,0x32,0xb7,0x32,0xb9,0x30,0xba,0x32,0x32,0x94,0xaf,0xaf, + 0xb0,0x34,0xb9,0x2f,0x38,0xb6,0xb0,0xb1,0x32,0xb4,0x37,0x36,0xb2,0x32,0xb9,0xaf, + 0xaf,0x14,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x43,0xa8,0x64,0x40,0x3c, + 0xe4,0x4b,0x86,0x44,0x40,0xc0,0x00,0x89,0x10,0x09,0x99,0x90,0x30,0x60,0x42,0x57, + 0x86,0x37,0xf6,0xf6,0x26,0x47,0x06,0x33,0x84,0x4a,0x04,0xc4,0x43,0xbe,0x44,0x48, + 0x04,0x04,0x0c,0x90,0x08,0x91,0x90,0x09,0x19,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72, + 0x30,0x43,0xa8,0x84,0x40,0x3c,0xe4,0x4b,0x88,0x44,0x40,0xc0,0x00,0x89,0x90,0x0b, + 0x99,0x90,0x32,0x18,0x62,0x20,0x62,0x80,0x90,0x01,0x62,0x06,0x43,0x8c,0x02,0x40, + 0x3a,0xe4,0x0c,0x46,0x44,0xec,0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0xd0,0x0e,0xef,0x40, + 0x0e,0xf5,0xc0,0x0e,0xe5,0xe0,0x06,0xe6,0xc0,0x0e,0xe1,0x70,0x0e,0xf3,0x30,0x45, + 0x08,0x86,0x11,0x0a,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0xa4,0x03,0x39,0x94,0x83, + 0x3b,0xd0,0xc3,0x94,0xa0,0x18,0xb1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39, + 0xc8,0xc3,0x3c,0xa4,0xc3,0x3b,0xb8,0xc3,0x94,0xc0,0x18,0x41,0x85,0x43,0x3a,0xc8, + 0x83,0x1b,0xb0,0x43,0x38,0xb8,0xc3,0x39,0xd4,0x43,0x38,0x9c,0x43,0x39,0xfc,0x82, + 0x3d,0x94,0x83,0x3c,0xcc,0x43,0x3a,0xbc,0x83,0x3b,0x4c,0x09,0x90,0x11,0x53,0x38, + 0xa4,0x83,0x3c,0xb8,0xc1,0x38,0xbc,0x43,0x3b,0xc0,0x43,0x3a,0xb0,0x43,0x39,0xfc, + 0xc2,0x3b,0xc0,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x3c,0x4c,0x19,0x14,0xc6,0x19, + 0xa1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39,0xc8,0x03,0x3d,0x94,0x03,0x3e, + 0x4c,0x09,0xd0,0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80,0x1c, + 0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07, + 0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e, + 0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43, + 0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c, + 0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76, + 0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e, + 0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8, + 0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4, + 0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68, + 0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07, + 0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71, + 0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5, + 0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4, + 0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90, + 0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43, + 0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b, + 0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78, + 0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2, + 0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20, + 0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0, + 0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83, + 0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07, + 0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61, + 0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d, + 0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39, + 0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79, + 0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c,0xe3, + 0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4, + 0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8, + 0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83, + 0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x05,0x00,0x00,0x00, + 0x06,0x50,0x30,0x00,0xd2,0xd0,0x16,0xd0,0x00,0x48,0xe4,0x17,0x0c,0xe0,0x57,0x76, + 0x71,0xdb,0x00,0x00,0x61,0x20,0x00,0x00,0x1b,0x00,0x00,0x00,0x13,0x04,0x41,0x2c, + 0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0xb4,0x63,0x11,0x40,0x60,0x1c,0x73,0x10, + 0x83,0xd0,0x34,0x94,0x33,0x00,0x14,0x63,0x09,0x20,0x08,0x82,0x20,0x18,0x80,0x20, + 0x08,0x82,0xe0,0x30,0x96,0x00,0x82,0x20,0x88,0xff,0x02,0x08,0x82,0x20,0xfe,0xcd, + 0x00,0x90,0xcc,0x41,0x54,0x15,0x35,0xd1,0xcc,0x00,0x10,0x8c,0x11,0x80,0x20,0x08, + 0xe2,0xdf,0x08,0xc0,0x0c,0x00,0x00,0x00,0x23,0x06,0x86,0x10,0x54,0x0e,0x72,0x0c, + 0x32,0x04,0xc7,0x32,0xc8,0x10,0x1c,0xcd,0x6c,0xc3,0x01,0x01,0xb3,0x0d,0x01,0x14, + 0xcc,0x36,0x04,0x83,0x90,0x01,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sdtx_fs_bytecode_metal_macos[2825] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x09,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x30,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xf6,0x61,0xa5,0x24,0x2e,0x8e,0x25, + 0x56,0xa9,0xb9,0xe4,0x35,0x58,0x0f,0xd8,0xb4,0x32,0x8b,0xbc,0x73,0xb0,0x70,0xbe, + 0x5d,0x66,0xf0,0xf4,0x12,0x93,0x26,0xe5,0xdd,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44, + 0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0x1c,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x84,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32, + 0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45, + 0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44, + 0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8, + 0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00, + 0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07,0x7a,0x60,0x87,0x7c, + 0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72,0x68,0x03,0x72,0x48, + 0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90,0x07,0x7a,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc,0x21,0x1c,0xd8,0x61, + 0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81,0x1d,0xca,0xa1,0x0d, + 0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d,0xd8,0x61,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87,0x79,0x98,0x87,0x36, + 0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36,0x30,0x07,0x78,0x68, + 0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0xc2,0x1d,0xde, + 0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0,0xe1,0x1d,0xd2,0xc1, + 0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1,0x1d,0x00,0x7a,0x90, + 0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90,0x87,0x70,0x68,0x87, + 0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07,0x79,0x68,0x83,0x72, + 0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36,0x60,0x87,0x72,0x08, + 0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30,0x87,0x79,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1, + 0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01,0x1e,0xda,0x80,0x1e, + 0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07, + 0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87,0x36,0x30,0x07,0x78, + 0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20, + 0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c,0xc8,0xa1,0x0d,0xf4, + 0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda,0xa0,0x1d,0xc2,0x81, + 0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0xa0, + 0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68,0x83,0x76,0x08,0x07, + 0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6,0x81,0x1e,0xc2,0x61, + 0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d, + 0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e,0xda,0x60,0x1e,0xd2, + 0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87,0x70,0x30,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e, + 0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e,0xde,0xc1,0x1c,0xe8, + 0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87,0x70,0x60,0x87,0x79, + 0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81,0x00,0x16,0xa0,0xda, + 0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5,0x06,0xa3,0x08,0x80, + 0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00,0x00,0x03,0x00,0x00, + 0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00,0x00,0x89,0x20,0x00, + 0x00,0x1e,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4, + 0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4, + 0x4c,0x10,0x4c,0x33,0x00,0xc3,0x08,0x04,0x60,0x83,0x30,0x8c,0x20,0x00,0x47,0x49, + 0x53,0x44,0x09,0x93,0xff,0x4f,0xc4,0x35,0x51,0x11,0xf1,0xdb,0xc3,0x3f,0x8d,0x11, + 0x00,0x83,0x08,0x44,0x70,0x91,0x34,0x45,0x94,0x30,0xf9,0xbf,0x04,0x30,0xcf,0x42, + 0x44,0xff,0x34,0x46,0x00,0x0c,0x22,0x18,0x42,0x29,0xc4,0x08,0xe5,0x10,0x9a,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0xca,0x19,0x8a,0x29,0x40,0x6d, + 0x20,0x20,0x05,0xd6,0x30,0x02,0xb1,0x8c,0x00,0x00,0x00,0x00,0x00,0x13,0xb2,0x70, + 0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68, + 0x83,0x76,0x08,0x87,0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83, + 0x38,0x70,0x03,0x38,0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78, + 0xa0,0x07,0x78,0xd0,0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90, + 0x0e,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e, + 0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76, + 0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20, + 0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a, + 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30, + 0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07, + 0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71, + 0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20, + 0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f, + 0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76, + 0xd0,0x06,0xf6,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50, + 0x07,0x71,0x20,0x07,0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07, + 0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78, + 0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x49,0x00,0x00,0x08,0x00, + 0x00,0x00,0x00,0x00,0x18,0xc2,0x38,0x40,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x64, + 0x81,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c, + 0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50,0x04,0x85,0x50, + 0x10,0x65,0x40,0x70,0x2c,0xa1,0x09,0x00,0x00,0x79,0x18,0x00,0x00,0xb9,0x00,0x00, + 0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37, + 0xb7,0x21,0xc6,0x42,0x3c,0x00,0x84,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b, + 0xd3,0x2b,0x1b,0x62,0x2c,0xc2,0x23,0x2c,0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c, + 0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26, + 0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac, + 0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0xf0, + 0x10,0x43,0x8c,0x45,0x58,0x8c,0x65,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x79, + 0x8e,0x45,0x58,0x84,0x65,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6, + 0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6, + 0x56,0x36,0x44,0x78,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c, + 0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62, + 0x6c,0x65,0x43,0x84,0x67,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5, + 0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99, + 0x95,0xd1,0x8d,0xa1,0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x9e,0x86, + 0x51,0x58,0x9a,0x9c,0x8b,0x5c,0x99,0x1b,0x59,0x99,0xdc,0x17,0x5d,0x98,0xdc,0x59, + 0x19,0x1d,0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0x2f, + 0xb7,0xb0,0xb6,0x32,0x1a,0x66,0x6c,0x6f,0x61,0x74,0x34,0x64,0xc2,0xd2,0xe4,0x5c, + 0xc2,0xe4,0xce,0xbe,0xdc,0xc2,0xda,0xca,0xa8,0x98,0xc9,0x85,0x9d,0x7d,0x8d,0xbd, + 0xb1,0xbd,0xc9,0x0d,0x61,0x9e,0x67,0x19,0x1e,0xe8,0x89,0x1e,0xe9,0x99,0x86,0x08, + 0x0f,0x45,0x29,0x2c,0x4d,0xce,0xc5,0x4c,0x2e,0xec,0xac,0xad,0xcc,0x8d,0xee,0x2b, + 0xcd,0x0d,0xae,0x8e,0x8e,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x1b,0x4c, + 0x0a,0x95,0xb0,0x34,0x39,0x97,0xb1,0x32,0x37,0xba,0x32,0x39,0x3e,0x61,0x69,0x72, + 0x2e,0x70,0x65,0x72,0x73,0x70,0x65,0x63,0x74,0x69,0x76,0x65,0x34,0xcc,0xd8,0xde, + 0xc2,0xe8,0x64,0x28,0xd4,0xd9,0x0d,0x91,0x96,0xe1,0xb1,0x9e,0xeb,0xc1,0x9e,0xec, + 0x81,0x1e,0xed,0x91,0x9e,0x8d,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x5b, + 0x4c,0x0a,0x8b,0xb1,0x37,0xb6,0x37,0xb9,0x21,0xd2,0x22,0x3c,0xd6,0xd3,0x3d,0xd8, + 0x93,0x3d,0xd0,0x13,0x3d,0xd2,0xe3,0x71,0x09,0x4b,0x93,0x73,0xa1,0x2b,0xc3,0xa3, + 0xab,0x93,0x2b,0xa3,0x14,0x96,0x26,0xe7,0xc2,0xf6,0x36,0x16,0x46,0x97,0xf6,0xe6, + 0xf6,0x95,0xe6,0x46,0x56,0x86,0x47,0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e, + 0xad,0x8c,0x18,0x5d,0x19,0x1e,0x5d,0x9d,0x5c,0x99,0x0c,0x19,0x8f,0x19,0xdb,0x5b, + 0x18,0x1d,0x0b,0xc8,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x0f,0x07,0xba,0x32,0xbc,0x21, + 0xd4,0x42,0x3c,0x60,0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x23,0x06,0x0f,0xf4,0x8c,0xc1, + 0x23,0x3d,0x64,0xc0,0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x8e, + 0xc7,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x87,0xb9,0x36,0xb8,0x21,0xd2,0x72,0x3c, + 0x66,0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x03,0x3d,0x67,0xf0,0x48,0x0f,0x1a,0x0c,0x41, + 0x1e,0xee,0xf9,0x9e,0x32,0x78,0xd2,0x60,0x88,0x91,0x00,0x4f,0xf5,0xa8,0xc1,0x88, + 0x88,0x1d,0xd8,0xc1,0x1e,0xda,0xc1,0x0d,0xda,0xe1,0x1d,0xc8,0xa1,0x1e,0xd8,0xa1, + 0x1c,0xdc,0xc0,0x1c,0xd8,0x21,0x1c,0xce,0x61,0x1e,0xa6,0x08,0xc1,0x30,0x42,0x61, + 0x07,0x76,0xb0,0x87,0x76,0x70,0x83,0x74,0x20,0x87,0x72,0x70,0x07,0x7a,0x98,0x12, + 0x14,0x23,0x96,0x70,0x48,0x07,0x79,0x70,0x03,0x7b,0x28,0x07,0x79,0x98,0x87,0x74, + 0x78,0x07,0x77,0x98,0x12,0x18,0x23,0xa8,0x70,0x48,0x07,0x79,0x70,0x03,0x76,0x08, + 0x07,0x77,0x38,0x87,0x7a,0x08,0x87,0x73,0x28,0x87,0x5f,0xb0,0x87,0x72,0x90,0x87, + 0x79,0x48,0x87,0x77,0x70,0x87,0x29,0x01,0x32,0x62,0x0a,0x87,0x74,0x90,0x07,0x37, + 0x18,0x87,0x77,0x68,0x07,0x78,0x48,0x07,0x76,0x28,0x87,0x5f,0x78,0x07,0x78,0xa0, + 0x87,0x74,0x78,0x07,0x77,0x98,0x87,0x29,0x83,0xc2,0x38,0x23,0x98,0x70,0x48,0x07, + 0x79,0x70,0x03,0x73,0x90,0x87,0x70,0x38,0x87,0x76,0x28,0x07,0x77,0xa0,0x87,0x29, + 0xc1,0x1a,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80, + 0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80, + 0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80, + 0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88, + 0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78, + 0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03, + 0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f, + 0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c, + 0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39, + 0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b, + 0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60, + 0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87, + 0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e, + 0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c, + 0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6, + 0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94, + 0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03, + 0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07, + 0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f, + 0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33, + 0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc, + 0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c, + 0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78, + 0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca, + 0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21, + 0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43, + 0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87, + 0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c, + 0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e, + 0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c, + 0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c, + 0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x08,0x00,0x00, + 0x00,0x16,0xb0,0x01,0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45, + 0xc4,0x6f,0x0f,0x7e,0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20,0x0d,0x0d,0x00,0x00, + 0x00,0x61,0x20,0x00,0x00,0x0f,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00, + 0x00,0x06,0x00,0x00,0x00,0x14,0x47,0x00,0x88,0x8d,0x00,0x90,0x1a,0x01,0xa8,0x01, + 0x12,0x33,0x00,0x14,0x66,0x00,0x08,0x8c,0x00,0x00,0x00,0x00,0x00,0x23,0x06,0x8a, + 0x10,0x4c,0x09,0xb2,0x10,0x46,0x11,0x0c,0x32,0x04,0x03,0x62,0x01,0x23,0x9f,0xd9, + 0x06,0x23,0x00,0x32,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sdtx_vs_bytecode_metal_ios[2796] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xec,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe0,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0xba,0x9f,0x15,0xc2,0x1c,0x90,0x81, + 0x14,0x52,0x1d,0xff,0x02,0xbe,0x84,0x58,0x4b,0x16,0xdb,0x77,0xe8,0xfc,0xb2,0x67, + 0xb1,0x23,0xf1,0x84,0xb0,0x8d,0xed,0xb3,0x81,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x37,0x00,0x00,0x00,0x56,0x41,0x54, + 0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x80, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03,0x00,0x04,0x04,0x06, + 0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b, + 0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xc4,0x09,0x00,0x00,0xff,0xff,0xff,0xff, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x6e,0x02,0x00,0x00,0x0b,0x82,0x20,0x00, + 0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49, + 0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62, + 0x80,0x10,0x45,0x02,0x42,0x92,0x0b,0x42,0x84,0x10,0x32,0x14,0x38,0x08,0x18,0x49, + 0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72, + 0x24,0x07,0xc8,0x08,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00, + 0x51,0x18,0x00,0x00,0x82,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8,0xff,0xff,0xff,0xff, + 0x01,0x90,0x00,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87,0x71,0xa0,0x07,0x76, + 0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72,0x20,0x87,0x36,0x20, + 0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88,0x07,0x79,0xa0,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0xc0,0x1c,0xc2,0x81, + 0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c, + 0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c,0xd8,0x81,0x1d,0xe6, + 0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87,0x72,0x98,0x87,0x79, + 0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72,0x68,0x03,0x73,0x80, + 0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xdc, + 0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda,0x00,0x1e,0xde,0x21, + 0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21,0x1d,0xda,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30,0x07,0x79,0x08,0x87, + 0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87,0x72,0x90,0x87,0x36, + 0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77,0x68,0x03,0x76,0x28, + 0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70,0x07,0x73,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1,0x1d,0xe0,0xa1,0x0d, + 0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87, + 0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87,0x79,0x68,0x03,0x73, + 0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d,0xca,0x81,0x1c,0xda, + 0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21, + 0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x70,0x87,0x77,0x68, + 0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38,0x87,0x36,0x68,0x87, + 0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0x62,0x1e,0xe8,0x21, + 0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1,0x1c,0xc6,0x81,0x1e, + 0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x0d,0xe6, + 0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87,0x79,0x08,0x07,0x73, + 0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e, + 0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d,0xe6,0xe1,0x1d,0xcc, + 0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00,0x73,0x08,0x07,0x76, + 0x98,0x87,0x72,0x00,0x36,0x18,0xc2,0xff,0xff,0xff,0xff,0x0f,0x80,0x04,0x50,0x00, + 0x49,0x18,0x00,0x00,0x02,0x00,0x00,0x00,0x13,0x82,0x60,0x42,0x20,0x00,0x00,0x00, + 0x89,0x20,0x00,0x00,0x11,0x00,0x00,0x00,0x32,0x22,0x08,0x09,0x20,0x64,0x85,0x04, + 0x13,0x22,0xa4,0x84,0x04,0x13,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x88,0x8c, + 0x0b,0x84,0x84,0x4c,0x10,0x34,0x33,0x00,0xc3,0x08,0x02,0x30,0x8c,0x40,0x00,0x76, + 0x08,0x91,0x42,0x4c,0x84,0x10,0x15,0x22,0x22,0x82,0x6c,0x20,0x60,0x8e,0x00,0x0c, + 0x52,0x20,0x87,0x11,0x88,0x64,0x04,0x00,0x00,0x00,0x00,0x00,0x13,0xa8,0x70,0x48, + 0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83, + 0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83,0x0d,0xb7,0x51,0x0e, + 0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78,0xa0, + 0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07, + 0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a, + 0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60, + 0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0, + 0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06, + 0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6, + 0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x60, + 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x90,0x07, + 0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6, + 0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80, + 0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07, + 0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75, + 0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75,0x10,0x07,0x72,0xa0, + 0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0,0x06,0xf6,0x10,0x07, + 0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70, + 0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73,0x20, + 0x07,0x43,0x98,0x02,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0xcc,0x01,0x04, + 0x80,0x00,0x00,0x00,0x00,0x00,0x40,0x16,0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0xca, + 0x12,0x18,0x01,0x28,0x82,0x42,0x28,0x08,0xd2,0xb1,0x04,0x48,0x00,0x00,0x00,0x00, + 0x79,0x18,0x00,0x00,0xb1,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25, + 0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0x46,0x42,0x20,0x80,0x72,0x50,0xb9, + 0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x24,0x02,0x22,0x24,0x05, + 0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c, + 0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06,0x04, + 0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6, + 0xe6,0xc6,0x45,0x26,0x65,0x88,0x80,0x10,0x43,0x8c,0x44,0x48,0x8c,0x64,0x60,0xd1, + 0x54,0x46,0x17,0xc6,0x36,0x04,0x41,0x8e,0x44,0x48,0x84,0x64,0xe0,0x16,0x96,0x26, + 0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36, + 0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x40,0x12,0x72,0x61,0x69,0x72, + 0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61, + 0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04,0x64,0x21,0x19,0x84,0xa5, + 0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89, + 0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85,0x89, + 0xb1,0x95,0x0d,0x11,0x90,0x86,0x51,0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19, + 0xde,0xd7,0x5b,0x1d,0x1d,0x5c,0x1d,0x1d,0x97,0xba,0xb9,0x32,0x39,0x14,0xb6,0xb7, + 0x31,0x37,0x98,0x14,0x46,0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x74,0x79,0x70, + 0x65,0x5f,0x6e,0x61,0x6d,0x65,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0xc8,0x84,0xa5, + 0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95,0x51,0xa8,0xb3,0x1b,0xc2,0x20, + 0x0f,0x02,0x21,0x11,0x22,0x21,0x13,0x42,0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61,0x7b, + 0x1b,0x73,0x8b,0x49,0xa1,0x61,0xc6,0xf6,0x16,0x46,0x47,0xc3,0x62,0xec,0x8d,0xed, + 0x4d,0x6e,0x08,0x83,0x3c,0x88,0x85,0x44,0xc8,0x85,0x4c,0x08,0x46,0x26,0x2c,0x4d, + 0xce,0x05,0xee,0x6d,0x2e,0x8d,0x2e,0xed,0xcd,0x8d,0xcb,0x19,0xdb,0x17,0xd4,0xdb, + 0x5c,0x1a,0x5d,0xda,0x9b,0xdb,0x10,0x05,0xd1,0x90,0x08,0xb9,0x90,0x09,0xd9,0x86, + 0x18,0x48,0x85,0x64,0x08,0x47,0x28,0x2c,0x4d,0xce,0xc5,0xae,0x4c,0x8e,0xae,0x0c, + 0xef,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x52,0x58,0x9a,0x9c,0x0b,0xdb,0xdb,0x58,0x18, + 0x5d,0xda,0x9b,0xdb,0x57,0x9a,0x1b,0x59,0x19,0x1e,0xbd,0xb3,0x32,0xb7,0x32,0xb9, + 0x30,0xba,0x32,0x32,0x94,0xaf,0xaf,0xb0,0x34,0xb9,0x2f,0x38,0xb6,0xb0,0xb1,0x32, + 0xb4,0x37,0x36,0xb2,0x32,0xb9,0xaf,0xaf,0x14,0x22,0x70,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x43,0xa8,0x64,0x40,0x3c,0xe4,0x4b,0x86,0x44,0x40,0xc0,0x00,0x89,0x10, + 0x09,0x99,0x90,0x30,0x60,0x42,0x57,0x86,0x37,0xf6,0xf6,0x26,0x47,0x06,0x33,0x84, + 0x4a,0x04,0xc4,0x43,0xbe,0x44,0x48,0x04,0x04,0x0c,0x90,0x08,0x91,0x90,0x09,0x19, + 0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8,0x84,0x40,0x3c,0xe4,0x4b,0x88, + 0x44,0x40,0xc0,0x00,0x89,0x90,0x0b,0x99,0x90,0x32,0x18,0x62,0x20,0x62,0x80,0x90, + 0x01,0x62,0x06,0x43,0x8c,0x02,0x40,0x3a,0xe4,0x0c,0x46,0x44,0xec,0xc0,0x0e,0xf6, + 0xd0,0x0e,0x6e,0xd0,0x0e,0xef,0x40,0x0e,0xf5,0xc0,0x0e,0xe5,0xe0,0x06,0xe6,0xc0, + 0x0e,0xe1,0x70,0x0e,0xf3,0x30,0x45,0x08,0x86,0x11,0x0a,0x3b,0xb0,0x83,0x3d,0xb4, + 0x83,0x1b,0xa4,0x03,0x39,0x94,0x83,0x3b,0xd0,0xc3,0x94,0xa0,0x18,0xb1,0x84,0x43, + 0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39,0xc8,0xc3,0x3c,0xa4,0xc3,0x3b,0xb8,0xc3,0x94, + 0xc0,0x18,0x41,0x85,0x43,0x3a,0xc8,0x83,0x1b,0xb0,0x43,0x38,0xb8,0xc3,0x39,0xd4, + 0x43,0x38,0x9c,0x43,0x39,0xfc,0x82,0x3d,0x94,0x83,0x3c,0xcc,0x43,0x3a,0xbc,0x83, + 0x3b,0x4c,0x09,0x90,0x11,0x53,0x38,0xa4,0x83,0x3c,0xb8,0xc1,0x38,0xbc,0x43,0x3b, + 0xc0,0x43,0x3a,0xb0,0x43,0x39,0xfc,0xc2,0x3b,0xc0,0x03,0x3d,0xa4,0xc3,0x3b,0xb8, + 0xc3,0x3c,0x4c,0x19,0x14,0xc6,0x19,0xa1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43, + 0x39,0xc8,0x03,0x3d,0x94,0x03,0x3e,0x4c,0x09,0xd0,0x00,0x00,0x79,0x18,0x00,0x00, + 0x7b,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88, + 0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6, + 0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce, + 0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8, + 0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48, + 0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11, + 0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e, + 0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89, + 0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b, + 0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37, + 0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78, + 0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81, + 0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1, + 0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c, + 0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39, + 0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc, + 0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58, + 0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87, + 0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f, + 0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e, + 0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66, + 0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b, + 0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0, + 0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e, + 0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc, + 0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3, + 0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07, + 0x7a,0x28,0x07,0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e, + 0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e, + 0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d, + 0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,0x00, + 0x71,0x20,0x00,0x00,0x05,0x00,0x00,0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x16,0xd0, + 0x00,0x48,0xe4,0x17,0x0c,0xe0,0x57,0x76,0x71,0xdb,0x00,0x00,0x61,0x20,0x00,0x00, + 0x1b,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0xb4,0x63,0x11,0x40,0x60,0x1c,0x73,0x10,0x83,0xd0,0x34,0x94,0x33,0x00,0x14,0x63, + 0x09,0x20,0x08,0x82,0x20,0x18,0x80,0x20,0x08,0x82,0xe0,0x30,0x96,0x00,0x82,0x20, + 0x88,0xff,0x02,0x08,0x82,0x20,0xfe,0xcd,0x00,0x90,0xcc,0x41,0x54,0x15,0x35,0xd1, + 0xcc,0x00,0x10,0x8c,0x11,0x80,0x20,0x08,0xe2,0xdf,0x08,0xc0,0x0c,0x00,0x00,0x00, + 0x23,0x06,0x86,0x10,0x54,0x0e,0x72,0x0c,0x32,0x04,0xc7,0x32,0xc8,0x10,0x1c,0xcd, + 0x6c,0xc3,0x01,0x01,0xb3,0x0d,0x01,0x14,0xcc,0x36,0x04,0x83,0x90,0x01,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sdtx_fs_bytecode_metal_ios[2825] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x09,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x30,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0x5c,0x3b,0x31,0x10,0xb3,0x9b,0xe2, + 0x6d,0x48,0xbf,0xdd,0x8e,0xac,0x5d,0xcc,0x7d,0x7b,0xba,0xe9,0xfb,0xe5,0xd0,0xdd, + 0xf7,0xec,0x82,0x0c,0xb9,0x6a,0xd2,0x30,0x4d,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44, + 0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0x14,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x82,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32, + 0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45, + 0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44, + 0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8, + 0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00, + 0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07,0x7a,0x60,0x87,0x7c, + 0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72,0x68,0x03,0x72,0x48, + 0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90,0x07,0x7a,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc,0x21,0x1c,0xd8,0x61, + 0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81,0x1d,0xca,0xa1,0x0d, + 0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d,0xd8,0x61,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87,0x79,0x98,0x87,0x36, + 0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36,0x30,0x07,0x78,0x68, + 0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0xc2,0x1d,0xde, + 0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0,0xe1,0x1d,0xd2,0xc1, + 0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1,0x1d,0x00,0x7a,0x90, + 0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90,0x87,0x70,0x68,0x87, + 0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07,0x79,0x68,0x83,0x72, + 0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36,0x60,0x87,0x72,0x08, + 0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30,0x87,0x79,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1, + 0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01,0x1e,0xda,0x80,0x1e, + 0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07, + 0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87,0x36,0x30,0x07,0x78, + 0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20, + 0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c,0xc8,0xa1,0x0d,0xf4, + 0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda,0xa0,0x1d,0xc2,0x81, + 0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0xa0, + 0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68,0x83,0x76,0x08,0x07, + 0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6,0x81,0x1e,0xc2,0x61, + 0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d, + 0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e,0xda,0x60,0x1e,0xd2, + 0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87,0x70,0x30,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e, + 0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e,0xde,0xc1,0x1c,0xe8, + 0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87,0x70,0x60,0x87,0x79, + 0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81,0x00,0x16,0xa0,0xda, + 0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5,0x06,0xa3,0x08,0x80, + 0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00,0x00,0x03,0x00,0x00, + 0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00,0x00,0x89,0x20,0x00, + 0x00,0x1e,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4, + 0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4, + 0x4c,0x10,0x4c,0x33,0x00,0xc3,0x08,0x04,0x60,0x83,0x30,0x8c,0x20,0x00,0x47,0x49, + 0x53,0x44,0x09,0x93,0xff,0x4f,0xc4,0x35,0x51,0x11,0xf1,0xdb,0xc3,0x3f,0x8d,0x11, + 0x00,0x83,0x08,0x44,0x70,0x91,0x34,0x45,0x94,0x30,0xf9,0xbf,0x04,0x30,0xcf,0x42, + 0x44,0xff,0x34,0x46,0x00,0x0c,0x22,0x18,0x42,0x29,0xc4,0x08,0xe5,0x10,0x9a,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0xca,0x19,0x8a,0x29,0x40,0x6d, + 0x20,0x20,0x05,0xd6,0x30,0x02,0xb1,0x8c,0x00,0x00,0x00,0x00,0x00,0x13,0xa8,0x70, + 0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68, + 0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83,0x0d,0xb7,0x51, + 0x0e,0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78, + 0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76,0xa0,0x07,0x71,0x60, + 0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07, + 0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a, + 0x60,0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30, + 0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76, + 0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0, + 0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06, + 0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6, + 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x90, + 0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06, + 0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72, + 0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0, + 0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07, + 0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75,0x10,0x07,0x72, + 0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0,0x06,0xf6,0x10, + 0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07, + 0x70,0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73, + 0x20,0x07,0x43,0x98,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x8c,0x03, + 0x04,0x80,0x00,0x00,0x00,0x00,0x00,0x40,0x16,0x08,0x00,0x00,0x00,0x08,0x00,0x00, + 0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43, + 0x5a,0x25,0x30,0x02,0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0x01,0x12,0x00, + 0x00,0x79,0x18,0x00,0x00,0xb9,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2, + 0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x42,0x3c,0x00,0x84,0x50, + 0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0xc2,0x23,0x2c, + 0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae, + 0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06, + 0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5, + 0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0xf0,0x10,0x43,0x8c,0x45,0x58,0x8c,0x65,0x60, + 0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x79,0x8e,0x45,0x58,0x84,0x65,0xe0,0x16,0x96, + 0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7, + 0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78,0x12,0x72,0x61,0x69, + 0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d, + 0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x84,0x67,0x21,0x19,0x84, + 0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95, + 0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85, + 0x89,0xb1,0x95,0x0d,0x11,0x9e,0x86,0x51,0x58,0x9a,0x9c,0x8b,0x5c,0x99,0x1b,0x59, + 0x99,0xdc,0x17,0x5d,0x98,0xdc,0x59,0x19,0x1d,0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9, + 0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0x2f,0xb7,0xb0,0xb6,0x32,0x1a,0x66,0x6c,0x6f,0x61, + 0x74,0x34,0x64,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xdc,0xc2,0xda,0xca,0xa8, + 0x98,0xc9,0x85,0x9d,0x7d,0x8d,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x9e,0x67,0x19,0x1e, + 0xe8,0x89,0x1e,0xe9,0x99,0x86,0x08,0x0f,0x45,0x29,0x2c,0x4d,0xce,0xc5,0x4c,0x2e, + 0xec,0xac,0xad,0xcc,0x8d,0xee,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x4b,0xdd,0x5c,0x99, + 0x1c,0x0a,0xdb,0xdb,0x98,0x1b,0x4c,0x0a,0x95,0xb0,0x34,0x39,0x97,0xb1,0x32,0x37, + 0xba,0x32,0x39,0x3e,0x61,0x69,0x72,0x2e,0x70,0x65,0x72,0x73,0x70,0x65,0x63,0x74, + 0x69,0x76,0x65,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x28,0xd4,0xd9,0x0d,0x91,0x96, + 0xe1,0xb1,0x9e,0xeb,0xc1,0x9e,0xec,0x81,0x1e,0xed,0x91,0x9e,0x8d,0x4b,0xdd,0x5c, + 0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x5b,0x4c,0x0a,0x8b,0xb1,0x37,0xb6,0x37,0xb9,0x21, + 0xd2,0x22,0x3c,0xd6,0xd3,0x3d,0xd8,0x93,0x3d,0xd0,0x13,0x3d,0xd2,0xe3,0x71,0x09, + 0x4b,0x93,0x73,0xa1,0x2b,0xc3,0xa3,0xab,0x93,0x2b,0xa3,0x14,0x96,0x26,0xe7,0xc2, + 0xf6,0x36,0x16,0x46,0x97,0xf6,0xe6,0xf6,0x95,0xe6,0x46,0x56,0x86,0x47,0x25,0x2c, + 0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e,0xad,0x8c,0x18,0x5d,0x19,0x1e,0x5d,0x9d,0x5c, + 0x99,0x0c,0x19,0x8f,0x19,0xdb,0x5b,0x18,0x1d,0x0b,0xc8,0x5c,0x58,0x1b,0x1c,0x5b, + 0x99,0x0f,0x07,0xba,0x32,0xbc,0x21,0xd4,0x42,0x3c,0x60,0xf0,0x84,0xc1,0x32,0x2c, + 0xc2,0x23,0x06,0x0f,0xf4,0x8c,0xc1,0x23,0x3d,0x64,0xc0,0x25,0x2c,0x4d,0xce,0x65, + 0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x8e,0xc7,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x87, + 0xb9,0x36,0xb8,0x21,0xd2,0x72,0x3c,0x66,0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x03,0x3d, + 0x67,0xf0,0x48,0x0f,0x1a,0x0c,0x41,0x1e,0xee,0xf9,0x9e,0x32,0x78,0xd2,0x60,0x88, + 0x91,0x00,0x4f,0xf5,0xa8,0xc1,0x88,0x88,0x1d,0xd8,0xc1,0x1e,0xda,0xc1,0x0d,0xda, + 0xe1,0x1d,0xc8,0xa1,0x1e,0xd8,0xa1,0x1c,0xdc,0xc0,0x1c,0xd8,0x21,0x1c,0xce,0x61, + 0x1e,0xa6,0x08,0xc1,0x30,0x42,0x61,0x07,0x76,0xb0,0x87,0x76,0x70,0x83,0x74,0x20, + 0x87,0x72,0x70,0x07,0x7a,0x98,0x12,0x14,0x23,0x96,0x70,0x48,0x07,0x79,0x70,0x03, + 0x7b,0x28,0x07,0x79,0x98,0x87,0x74,0x78,0x07,0x77,0x98,0x12,0x18,0x23,0xa8,0x70, + 0x48,0x07,0x79,0x70,0x03,0x76,0x08,0x07,0x77,0x38,0x87,0x7a,0x08,0x87,0x73,0x28, + 0x87,0x5f,0xb0,0x87,0x72,0x90,0x87,0x79,0x48,0x87,0x77,0x70,0x87,0x29,0x01,0x32, + 0x62,0x0a,0x87,0x74,0x90,0x07,0x37,0x18,0x87,0x77,0x68,0x07,0x78,0x48,0x07,0x76, + 0x28,0x87,0x5f,0x78,0x07,0x78,0xa0,0x87,0x74,0x78,0x07,0x77,0x98,0x87,0x29,0x83, + 0xc2,0x38,0x23,0x98,0x70,0x48,0x07,0x79,0x70,0x03,0x73,0x90,0x87,0x70,0x38,0x87, + 0x76,0x28,0x07,0x77,0xa0,0x87,0x29,0xc1,0x1a,0x00,0x00,0x00,0x00,0x79,0x18,0x00, + 0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d, + 0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c, + 0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d, + 0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d, + 0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79, + 0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc, + 0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50, + 0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30, + 0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03, + 0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07, + 0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76, + 0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98, + 0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8, + 0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21, + 0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43, + 0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f, + 0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70, + 0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0, + 0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40, + 0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41, + 0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e, + 0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07, + 0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f, + 0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d, + 0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38, + 0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88, + 0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08, + 0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50, + 0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01, + 0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03, + 0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c, + 0x00,0x71,0x20,0x00,0x00,0x08,0x00,0x00,0x00,0x16,0xb0,0x01,0x48,0xe4,0x4b,0x00, + 0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e,0x85,0x17,0xb7,0x6d, + 0x00,0x05,0x03,0x20,0x0d,0x0d,0x00,0x00,0x00,0x61,0x20,0x00,0x00,0x0f,0x00,0x00, + 0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x14,0x47,0x00, + 0x88,0x8d,0x00,0x90,0x1a,0x01,0xa8,0x01,0x12,0x33,0x00,0x14,0x66,0x00,0x08,0x8c, + 0x00,0x00,0x00,0x00,0x00,0x23,0x06,0x8a,0x10,0x4c,0x09,0xb2,0x10,0x46,0x11,0x0c, + 0x32,0x04,0x03,0x62,0x01,0x23,0x9f,0xd9,0x06,0x23,0x00,0x32,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sdtx_vs_source_metal_sim[577] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x32,0x20,0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28, + 0x6c,0x6f,0x63,0x6e,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c, + 0x6f,0x61,0x74,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65, + 0x72,0x28,0x6c,0x6f,0x63,0x6e,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x20,0x5b,0x5b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b, + 0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e, + 0x30,0x5f,0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x32,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x61,0x74,0x74, + 0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64, + 0x30,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x31,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x30,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74, + 0x65,0x28,0x32,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x76,0x65,0x72,0x74, + 0x65,0x78,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69, + 0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b, + 0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20, + 0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74, + 0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x67, + 0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x28,0x66,0x6d,0x61,0x28,0x69,0x6e,0x2e,0x70,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x32,0x2e,0x30,0x2c, + 0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x2d, + 0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x29,0x2c,0x20,0x30,0x2e,0x30,0x2c, + 0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75, + 0x76,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x20, + 0x3d,0x20,0x69,0x6e,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a, + 0x00, +}; +static const uint8_t _sdtx_fs_source_metal_sim[441] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d, + 0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f, + 0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, + 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x66,0x72,0x61,0x67,0x6d,0x65, + 0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69, + 0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b, + 0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x74,0x65, + 0x78,0x20,0x5b,0x5b,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x30,0x29,0x5d,0x5d, + 0x2c,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x73,0x6d,0x70,0x20,0x5b,0x5b, + 0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a, + 0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75, + 0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e, + 0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78, + 0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x73,0x6d,0x70,0x2c,0x20,0x69,0x6e,0x2e, + 0x75,0x76,0x29,0x2e,0x78,0x78,0x78,0x78,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x63,0x6f, + 0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20, + 0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_D3D11) +static const uint8_t _sdtx_vs_bytecode_hlsl4[692] = { + 0x44,0x58,0x42,0x43,0x07,0x05,0xa0,0xb3,0x53,0xc1,0x0a,0x0d,0x1e,0xf4,0xe4,0xa6, + 0x91,0xaf,0x4c,0xca,0x01,0x00,0x00,0x00,0xb4,0x02,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xe4,0x00,0x00,0x00,0x54,0x01,0x00,0x00, + 0x38,0x02,0x00,0x00,0x52,0x44,0x45,0x46,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xfe,0xff, + 0x10,0x81,0x00,0x00,0x1c,0x00,0x00,0x00,0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f,0x66, + 0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c,0x53,0x4c,0x20,0x53,0x68,0x61,0x64,0x65, + 0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x72,0x20,0x31,0x30,0x2e,0x31,0x00, + 0x49,0x53,0x47,0x4e,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x03,0x00,0x00, + 0x50,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x68,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0c,0x00,0x00,0x50,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x54,0x45,0x58,0x43, + 0x4f,0x4f,0x52,0x44,0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x00,0xab,0xab,0xab,0x53,0x48,0x44,0x52,0xdc,0x00,0x00,0x00,0x40,0x00,0x01,0x00, + 0x37,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x5f,0x00,0x00,0x03,0x32,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x5f,0x00,0x00,0x03, + 0xf2,0x10,0x10,0x00,0x02,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0x32,0x20,0x10,0x00, + 0x00,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00, + 0x67,0x00,0x00,0x04,0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x36,0x00,0x00,0x05,0x32,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00, + 0x01,0x00,0x00,0x00,0x36,0x00,0x00,0x05,0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00, + 0x46,0x1e,0x10,0x00,0x02,0x00,0x00,0x00,0x32,0x00,0x00,0x0f,0x32,0x20,0x10,0x00, + 0x02,0x00,0x00,0x00,0x46,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x02,0x40,0x00,0x00, + 0x00,0x00,0x00,0x40,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x02,0x40,0x00,0x00,0x00,0x00,0x80,0xbf,0x00,0x00,0x80,0x3f,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x08,0xc2,0x20,0x10,0x00,0x02,0x00,0x00,0x00, + 0x02,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x80,0x3f,0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, +}; +static const uint8_t _sdtx_fs_bytecode_hlsl4[608] = { + 0x44,0x58,0x42,0x43,0xb7,0xcd,0xbd,0xb1,0x6f,0x85,0x5d,0x59,0x07,0x7e,0xa3,0x6e, + 0xe2,0x23,0x68,0xa0,0x01,0x00,0x00,0x00,0x60,0x02,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x48,0x01,0x00,0x00, + 0xe4,0x01,0x00,0x00,0x52,0x44,0x45,0x46,0x8c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xff,0xff, + 0x10,0x81,0x00,0x00,0x64,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x73,0x6d,0x70,0x00,0x74,0x65,0x78,0x00, + 0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f,0x66,0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c, + 0x53,0x4c,0x20,0x53,0x68,0x61,0x64,0x65,0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c, + 0x65,0x72,0x20,0x31,0x30,0x2e,0x31,0x00,0x49,0x53,0x47,0x4e,0x44,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x00,0x00, + 0x38,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x2c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x53,0x56,0x5f,0x54, + 0x61,0x72,0x67,0x65,0x74,0x00,0xab,0xab,0x53,0x48,0x44,0x52,0x94,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x5a,0x00,0x00,0x03,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x58,0x18,0x00,0x04,0x00,0x70,0x10,0x00,0x00,0x00,0x00,0x00, + 0x55,0x55,0x00,0x00,0x62,0x10,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x62,0x10,0x00,0x03,0xf2,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x65,0x00,0x00,0x03, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00, + 0x45,0x00,0x00,0x09,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x7e,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x07,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x06,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x01,0x00,0x00,0x00, + 0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +#elif defined(SOKOL_WGPU) +static const uint8_t _sdtx_vs_source_wgsl[922] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70, + 0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65, + 0x3e,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x3a,0x20,0x76,0x65, + 0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74, + 0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a, + 0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x67,0x6c, + 0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20,0x76,0x65,0x63,0x34, + 0x66,0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20, + 0x7b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x39,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f, + 0x31,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x37,0x20,0x3a,0x20, + 0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x28,0x28,0x78,0x5f,0x31,0x39,0x20,0x2a, + 0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x32,0x2e,0x30,0x66,0x2c,0x20,0x2d,0x32,0x2e, + 0x30,0x66,0x29,0x29,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x2d,0x31,0x2e, + 0x30,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x29,0x3b,0x0a,0x20,0x20,0x67,0x6c, + 0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x76,0x65,0x63,0x34, + 0x66,0x28,0x78,0x5f,0x32,0x37,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x32,0x37,0x2e,0x79, + 0x2c,0x20,0x30,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x3b,0x0a,0x20, + 0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x32, + 0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x20, + 0x20,0x75,0x76,0x20,0x3d,0x20,0x78,0x5f,0x33,0x37,0x3b,0x0a,0x20,0x20,0x6c,0x65, + 0x74,0x20,0x78,0x5f,0x34,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3d,0x20,0x78,0x5f,0x34,0x31,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72, + 0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69, + 0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x40,0x62,0x75,0x69,0x6c,0x74, + 0x69,0x6e,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x29,0x0a,0x20,0x20,0x67, + 0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x34,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28, + 0x30,0x29,0x0a,0x20,0x20,0x75,0x76,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x32, + 0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31, + 0x29,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65, + 0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x76,0x65,0x72,0x74,0x65,0x78,0x0a, + 0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f, + 0x6e,0x28,0x30,0x29,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20,0x40, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x20,0x74,0x65,0x78,0x63, + 0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65, + 0x63,0x32,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x32, + 0x29,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x34,0x66,0x29,0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f, + 0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x5f,0x31,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72, + 0x64,0x30,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3d, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20, + 0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74, + 0x75,0x72,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x67,0x6c,0x5f, + 0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x2c,0x20,0x75,0x76,0x2c,0x20,0x63,0x6f, + 0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const uint8_t _sdtx_fs_source_wgsl[663] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75, + 0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x34,0x38, + 0x29,0x20,0x76,0x61,0x72,0x20,0x74,0x65,0x78,0x20,0x3a,0x20,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x5f,0x32,0x64,0x3c,0x66,0x33,0x32,0x3e,0x3b,0x0a,0x0a,0x40,0x67, + 0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67, + 0x28,0x36,0x34,0x29,0x20,0x76,0x61,0x72,0x20,0x73,0x6d,0x70,0x20,0x3a,0x20,0x73, + 0x61,0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, + 0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a, + 0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20, + 0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x33,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, + 0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x2c,0x20,0x73, + 0x6d,0x70,0x2c,0x20,0x78,0x5f,0x32,0x33,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74, + 0x20,0x78,0x5f,0x32,0x38,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f, + 0x6c,0x6f,0x72,0x20,0x3d,0x20,0x28,0x76,0x65,0x63,0x34,0x66,0x28,0x78,0x5f,0x32, + 0x34,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x32,0x34,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x32, + 0x34,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x32,0x34,0x2e,0x78,0x29,0x20,0x2a,0x20,0x78, + 0x5f,0x32,0x38,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a, + 0x7d,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f, + 0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e, + 0x28,0x30,0x29,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40, + 0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e, + 0x28,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x75,0x76, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20, + 0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x20,0x63,0x6f,0x6c, + 0x6f,0x72,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x29,0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a, + 0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x75,0x76,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b, + 0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31, + 0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6d,0x61,0x69, + 0x6e,0x5f,0x6f,0x75,0x74,0x28,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_DUMMY_BACKEND) +static const char* _sdtx_vs_src_dummy = ""; +static const char* _sdtx_fs_src_dummy = ""; +#else +#error "Please define one of SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" +#endif + +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs +typedef struct { + uint32_t id; + sg_resource_state state; +} _sdtx_slot_t; + +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _sdtx_pool_t; + +typedef struct { + float x, y; +} _sdtx_float2_t; + +typedef struct { + float x, y; + uint16_t u, v; + uint32_t color; +} _sdtx_vertex_t; + +typedef struct { + int layer_id; + int first_vertex; + int num_vertices; +} _sdtx_command_t; + +typedef struct { + _sdtx_slot_t slot; + sdtx_context_desc_t desc; + uint32_t frame_id; + uint32_t update_frame_id; + struct { + int cap; + int next; + _sdtx_vertex_t* ptr; + } vertices; + struct { + int cap; + int next; + _sdtx_command_t* ptr; + } commands; + sg_buffer vbuf; + sg_pipeline pip; + int cur_font; + int cur_layer_id; + _sdtx_float2_t canvas_size; + _sdtx_float2_t glyph_size; + _sdtx_float2_t origin; + _sdtx_float2_t pos; + float tab_width; + uint32_t color; +} _sdtx_context_t; + +typedef struct { + _sdtx_pool_t pool; + _sdtx_context_t* contexts; +} _sdtx_context_pool_t; + +typedef struct { + uint32_t init_cookie; + sdtx_desc_t desc; + sg_image font_img; + sg_sampler font_smp; + sg_shader shader; + uint32_t fmt_buf_size; + char* fmt_buf; + sdtx_context def_ctx_id; + sdtx_context cur_ctx_id; + _sdtx_context_t* cur_ctx; // may be 0! + _sdtx_context_pool_t context_pool; + uint8_t font_pixels[SDTX_MAX_FONTS * 256 * 8 * 8]; +} _sdtx_t; +static _sdtx_t _sdtx; + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SDTX_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sdtx_log_messages[] = { + _SDTX_LOG_ITEMS +}; +#undef _SDTX_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SDTX_PANIC(code) _sdtx_log(SDTX_LOGITEM_ ##code, 0, __LINE__) +#define _SDTX_ERROR(code) _sdtx_log(SDTX_LOGITEM_ ##code, 1, __LINE__) +#define _SDTX_WARN(code) _sdtx_log(SDTX_LOGITEM_ ##code, 2, __LINE__) +#define _SDTX_INFO(code) _sdtx_log(SDTX_LOGITEM_ ##code, 3, __LINE__) + +static void _sdtx_log(sdtx_log_item_t log_item, uint32_t log_level, uint32_t line_nr) { + if (_sdtx.desc.logger.func) { + #if defined(SOKOL_DEBUG) + const char* filename = __FILE__; + const char* message = _sdtx_log_messages[log_item]; + #else + const char* filename = 0; + const char* message = 0; + #endif + _sdtx.desc.logger.func("sdtx", log_level, log_item, message, line_nr, filename, _sdtx.desc.logger.user_data); + } else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory +static void _sdtx_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +static void* _sdtx_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_sdtx.desc.allocator.alloc_fn) { + ptr = _sdtx.desc.allocator.alloc_fn(size, _sdtx.desc.allocator.user_data); + } else { + ptr = malloc(size); + } + if (0 == ptr) { + _SDTX_PANIC(MALLOC_FAILED); + } + return ptr; +} + +static void* _sdtx_malloc_clear(size_t size) { + void* ptr = _sdtx_malloc(size); + _sdtx_clear(ptr, size); + return ptr; +} + +static void _sdtx_free(void* ptr) { + if (_sdtx.desc.allocator.free_fn) { + _sdtx.desc.allocator.free_fn(ptr, _sdtx.desc.allocator.user_data); + } else { + free(ptr); + } +} + +// ██████ ██████ ███ ██ ████████ ███████ ██ ██ ████████ ██████ ██████ ██████ ██ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ █████ ███ ██ ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ████ ██ ███████ ██ ██ ██ ██ ██████ ██████ ███████ +// +// >>context pool +static void _sdtx_init_pool(_sdtx_pool_t* pool, int num) { + SOKOL_ASSERT(pool && (num >= 1)); + // slot 0 is reserved for the 'invalid id', so bump the pool size by 1 + pool->size = num + 1; + pool->queue_top = 0; + // generation counters indexable by pool slot index, slot 0 is reserved + size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size; + pool->gen_ctrs = (uint32_t*) _sdtx_malloc_clear(gen_ctrs_size); + // it's not a bug to only reserve 'num' here + pool->free_queue = (int*) _sdtx_malloc_clear(sizeof(int) * (size_t)num); + // never allocate the zero-th pool item since the invalid id is 0 + for (int i = pool->size-1; i >= 1; i--) { + pool->free_queue[pool->queue_top++] = i; + } +} + +static void _sdtx_discard_pool(_sdtx_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + _sdtx_free(pool->free_queue); + pool->free_queue = 0; + SOKOL_ASSERT(pool->gen_ctrs); + _sdtx_free(pool->gen_ctrs); + pool->gen_ctrs = 0; + pool->size = 0; + pool->queue_top = 0; +} + +static int _sdtx_pool_alloc_index(_sdtx_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } else { + // pool exhausted + return _SDTX_INVALID_SLOT_INDEX; + } +} + +static void _sdtx_pool_free_index(_sdtx_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SDTX_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_ASSERT(pool->queue_top < pool->size); + #ifdef SOKOL_DEBUG + // debug check against double-free + for (int i = 0; i < pool->queue_top; i++) { + SOKOL_ASSERT(pool->free_queue[i] != slot_index); + } + #endif + pool->free_queue[pool->queue_top++] = slot_index; + SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); +} + +static void _sdtx_setup_context_pool(const sdtx_desc_t* desc) { + SOKOL_ASSERT(desc); + // note: the pool will have an additional item, since slot 0 is reserved + SOKOL_ASSERT((desc->context_pool_size > 0) && (desc->context_pool_size < _SDTX_MAX_POOL_SIZE)); + _sdtx_init_pool(&_sdtx.context_pool.pool, desc->context_pool_size); + size_t pool_byte_size = sizeof(_sdtx_context_t) * (size_t)_sdtx.context_pool.pool.size; + _sdtx.context_pool.contexts = (_sdtx_context_t*) _sdtx_malloc_clear(pool_byte_size); +} + +static void _sdtx_discard_context_pool(void) { + SOKOL_ASSERT(_sdtx.context_pool.contexts); + _sdtx_free(_sdtx.context_pool.contexts); + _sdtx.context_pool.contexts = 0; + _sdtx_discard_pool(&_sdtx.context_pool.pool); +} + +/* allocate the slot at slot_index: + - bump the slot's generation counter + - create a resource id from the generation counter and slot index + - set the slot's id to this id + - set the slot's state to ALLOC + - return the resource id +*/ +static uint32_t _sdtx_slot_alloc(_sdtx_pool_t* pool, _sdtx_slot_t* slot, int slot_index) { + /* FIXME: add handling for an overflowing generation counter, + for now, just overflow (another option is to disable + the slot) + */ + SOKOL_ASSERT(pool && pool->gen_ctrs); + SOKOL_ASSERT((slot_index > _SDTX_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT((slot->state == SG_RESOURCESTATE_INITIAL) && (slot->id == SG_INVALID_ID)); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + slot->id = (ctr<<_SDTX_SLOT_SHIFT)|(slot_index & _SDTX_SLOT_MASK); + slot->state = SG_RESOURCESTATE_ALLOC; + return slot->id; +} + +// extract slot index from id +static int _sdtx_slot_index(uint32_t id) { + int slot_index = (int) (id & _SDTX_SLOT_MASK); + SOKOL_ASSERT(_SDTX_INVALID_SLOT_INDEX != slot_index); + return slot_index; +} + +// get context pointer without id-check +static _sdtx_context_t* _sdtx_context_at(uint32_t ctx_id) { + SOKOL_ASSERT(SG_INVALID_ID != ctx_id); + int slot_index = _sdtx_slot_index(ctx_id); + SOKOL_ASSERT((slot_index > _SDTX_INVALID_SLOT_INDEX) && (slot_index < _sdtx.context_pool.pool.size)); + return &_sdtx.context_pool.contexts[slot_index]; +} + +// get context pointer with id-check, returns 0 if no match +static _sdtx_context_t* _sdtx_lookup_context(uint32_t ctx_id) { + if (SG_INVALID_ID != ctx_id) { + _sdtx_context_t* ctx = _sdtx_context_at(ctx_id); + if (ctx->slot.id == ctx_id) { + return ctx; + } + } + return 0; +} + +// make context handle from raw uint32_t id +static sdtx_context _sdtx_make_ctx_id(uint32_t ctx_id) { + sdtx_context ctx; + ctx.id = ctx_id; + return ctx; +} + +static sdtx_context _sdtx_alloc_context(void) { + sdtx_context ctx_id; + int slot_index = _sdtx_pool_alloc_index(&_sdtx.context_pool.pool); + if (_SDTX_INVALID_SLOT_INDEX != slot_index) { + ctx_id = _sdtx_make_ctx_id(_sdtx_slot_alloc(&_sdtx.context_pool.pool, &_sdtx.context_pool.contexts[slot_index].slot, slot_index)); + } else { + // pool is exhausted + ctx_id = _sdtx_make_ctx_id(SG_INVALID_ID); + } + return ctx_id; +} + +static sdtx_context_desc_t _sdtx_context_desc_defaults(const sdtx_context_desc_t* desc) { + sdtx_context_desc_t res = *desc; + res.max_commands = _sdtx_def(res.max_commands, _SDTX_DEFAULT_MAX_COMMANDS); + res.char_buf_size = _sdtx_def(res.char_buf_size, _SDTX_DEFAULT_CHAR_BUF_SIZE); + res.canvas_width = _sdtx_def(res.canvas_width, _SDTX_DEFAULT_CANVAS_WIDTH); + res.canvas_height = _sdtx_def(res.canvas_height, _SDTX_DEFAULT_CANVAS_HEIGHT); + res.tab_width = _sdtx_def(res.tab_width, _SDTX_DEFAULT_TAB_WIDTH); + // keep pixel format attrs are passed as is into pipeline creation + SOKOL_ASSERT(res.char_buf_size > 0); + SOKOL_ASSERT(!isnan(res.canvas_width)); + SOKOL_ASSERT(!isnan(res.canvas_height)); + SOKOL_ASSERT(res.canvas_width > 0.0f); + SOKOL_ASSERT(res.canvas_height > 0.0f); + return res; +} + +static void _sdtx_set_layer(_sdtx_context_t* ctx, int layer_id); +static void _sdtx_rewind(_sdtx_context_t* ctx) { + SOKOL_ASSERT(ctx); + ctx->frame_id++; + ctx->vertices.next = 0; + ctx->commands.next = 0; + _sdtx_set_layer(ctx, 0); + ctx->cur_font = 0; + ctx->pos.x = 0.0f; + ctx->pos.y = 0.0f; +} + +static void _sdtx_commit_listener(void* userdata) { + _sdtx_context_t* ctx = _sdtx_lookup_context((uint32_t)(uintptr_t)userdata); + if (ctx) { + _sdtx_rewind(ctx); + } +} + +static sg_commit_listener _sdtx_make_commit_listener(_sdtx_context_t* ctx) { + sg_commit_listener listener = { _sdtx_commit_listener, (void*)(uintptr_t)(ctx->slot.id) }; + return listener; +} + +static void _sdtx_init_context(sdtx_context ctx_id, const sdtx_context_desc_t* in_desc) { + sg_push_debug_group("sokol-debugtext"); + + SOKOL_ASSERT((ctx_id.id != SG_INVALID_ID) && in_desc); + _sdtx_context_t* ctx = _sdtx_lookup_context(ctx_id.id); + SOKOL_ASSERT(ctx); + ctx->desc = _sdtx_context_desc_defaults(in_desc); + // NOTE: frame_id must be non-zero, so that updates trigger in first frame + ctx->frame_id = 1; + + ctx->vertices.cap = 6 * ctx->desc.char_buf_size; + const size_t vbuf_size = (size_t)ctx->vertices.cap * sizeof(_sdtx_vertex_t); + ctx->vertices.ptr = (_sdtx_vertex_t*) _sdtx_malloc(vbuf_size); + + ctx->commands.cap = ctx->desc.max_commands; + ctx->commands.ptr = (_sdtx_command_t*) _sdtx_malloc((size_t)ctx->commands.cap * sizeof(_sdtx_command_t)); + _sdtx_set_layer(ctx, 0); + + sg_buffer_desc vbuf_desc; + _sdtx_clear(&vbuf_desc, sizeof(vbuf_desc)); + vbuf_desc.size = vbuf_size; + vbuf_desc.type = SG_BUFFERTYPE_VERTEXBUFFER; + vbuf_desc.usage = SG_USAGE_STREAM; + vbuf_desc.label = "sdtx-vbuf"; + ctx->vbuf = sg_make_buffer(&vbuf_desc); + SOKOL_ASSERT(SG_INVALID_ID != ctx->vbuf.id); + + sg_pipeline_desc pip_desc; + _sdtx_clear(&pip_desc, sizeof(pip_desc)); + pip_desc.layout.buffers[0].stride = sizeof(_sdtx_vertex_t); + pip_desc.layout.attrs[0].format = SG_VERTEXFORMAT_FLOAT2; + pip_desc.layout.attrs[1].format = SG_VERTEXFORMAT_USHORT2N; + pip_desc.layout.attrs[2].format = SG_VERTEXFORMAT_UBYTE4N; + pip_desc.shader = _sdtx.shader; + pip_desc.index_type = SG_INDEXTYPE_NONE; + pip_desc.sample_count = ctx->desc.sample_count; + pip_desc.depth.pixel_format = ctx->desc.depth_format; + pip_desc.colors[0].pixel_format = ctx->desc.color_format; + pip_desc.colors[0].blend.enabled = true; + pip_desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA; + pip_desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + pip_desc.colors[0].blend.src_factor_alpha = SG_BLENDFACTOR_ONE; + pip_desc.colors[0].blend.dst_factor_alpha = SG_BLENDFACTOR_ZERO; + pip_desc.label = "sdtx-pipeline"; + ctx->pip = sg_make_pipeline(&pip_desc); + SOKOL_ASSERT(SG_INVALID_ID != ctx->pip.id); + + ctx->canvas_size.x = ctx->desc.canvas_width; + ctx->canvas_size.y = ctx->desc.canvas_height; + ctx->glyph_size.x = 8.0f / ctx->canvas_size.x; + ctx->glyph_size.y = 8.0f / ctx->canvas_size.y; + ctx->tab_width = (float) ctx->desc.tab_width; + ctx->color = _SDTX_DEFAULT_COLOR; + + if (!sg_add_commit_listener(_sdtx_make_commit_listener(ctx))) { + _SDTX_ERROR(ADD_COMMIT_LISTENER_FAILED); + } + sg_pop_debug_group(); +} + +static void _sdtx_destroy_context(sdtx_context ctx_id) { + _sdtx_context_t* ctx = _sdtx_lookup_context(ctx_id.id); + if (ctx) { + if (ctx->vertices.ptr) { + _sdtx_free(ctx->vertices.ptr); + ctx->vertices.ptr = 0; + ctx->vertices.cap = 0; + ctx->vertices.next = 0; + } + if (ctx->commands.ptr) { + _sdtx_free(ctx->commands.ptr); + ctx->commands.ptr = 0; + ctx->commands.cap = 0; + ctx->commands.next = 0; + } + sg_push_debug_group("sokol_debugtext"); + sg_destroy_buffer(ctx->vbuf); + sg_destroy_pipeline(ctx->pip); + sg_remove_commit_listener(_sdtx_make_commit_listener(ctx)); + sg_pop_debug_group(); + _sdtx_clear(ctx, sizeof(*ctx)); + _sdtx_pool_free_index(&_sdtx.context_pool.pool, _sdtx_slot_index(ctx_id.id)); + } +} + +static bool _sdtx_is_default_context(sdtx_context ctx_id) { + return ctx_id.id == SDTX_DEFAULT_CONTEXT.id; +} + +// ███ ███ ██ ███████ ██████ +// ████ ████ ██ ██ ██ +// ██ ████ ██ ██ ███████ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ███████ ██████ +// +// >>misc + +// unpack linear 8x8 bits-per-pixel font data into 2D byte-per-pixel texture data +static void _sdtx_unpack_font(const sdtx_font_desc_t* font_desc, uint8_t* out_pixels) { + SOKOL_ASSERT(font_desc->data.ptr); + SOKOL_ASSERT((font_desc->data.size > 0) && ((font_desc->data.size % 8) == 0)); + SOKOL_ASSERT(font_desc->first_char <= font_desc->last_char); + SOKOL_ASSERT((size_t)(((font_desc->last_char - font_desc->first_char) + 1) * 8) == font_desc->data.size); + const uint8_t* ptr = (const uint8_t*) font_desc->data.ptr; + for (int chr = font_desc->first_char; chr <= font_desc->last_char; chr++) { + for (int line = 0; line < 8; line++) { + uint8_t bits = *ptr++; + for (int x = 0; x < 8; x++) { + out_pixels[line*256*8 + chr*8 + x] = ((bits>>(7-x)) & 1) ? 0xFF : 0x00; + } + } + } +} + +static void _sdtx_setup_common(void) { + + // common printf formatting buffer + _sdtx.fmt_buf_size = (uint32_t) _sdtx.desc.printf_buf_size + 1; + _sdtx.fmt_buf = (char*) _sdtx_malloc_clear(_sdtx.fmt_buf_size); + + sg_push_debug_group("sokol-debugtext"); + + // common shader for all contexts + sg_shader_desc shd_desc; + _sdtx_clear(&shd_desc, sizeof(shd_desc)); + shd_desc.label = "sokol-debugtext-shader"; + shd_desc.attrs[0].name = "position"; + shd_desc.attrs[1].name = "texcoord0"; + shd_desc.attrs[2].name = "color0"; + shd_desc.attrs[0].sem_name = "TEXCOORD"; + shd_desc.attrs[0].sem_index = 0; + shd_desc.attrs[1].sem_name = "TEXCOORD"; + shd_desc.attrs[1].sem_index = 1; + shd_desc.attrs[2].sem_name = "TEXCOORD"; + shd_desc.attrs[2].sem_index = 2; + shd_desc.fs.images[0].used = true; + shd_desc.fs.images[0].image_type = SG_IMAGETYPE_2D; + shd_desc.fs.images[0].sample_type = SG_IMAGESAMPLETYPE_FLOAT; + shd_desc.fs.samplers[0].used = true; + shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING; + shd_desc.fs.image_sampler_pairs[0].used = true; + shd_desc.fs.image_sampler_pairs[0].image_slot = 0; + shd_desc.fs.image_sampler_pairs[0].sampler_slot = 0; + shd_desc.fs.image_sampler_pairs[0].glsl_name = "tex_smp"; + #if defined(SOKOL_GLCORE) + shd_desc.vs.source = (const char*)_sdtx_vs_source_glsl410; + shd_desc.fs.source = (const char*)_sdtx_fs_source_glsl410; + #elif defined(SOKOL_GLES3) + shd_desc.vs.source = (const char*)_sdtx_vs_source_glsl300es; + shd_desc.fs.source = (const char*)_sdtx_fs_source_glsl300es; + #elif defined(SOKOL_METAL) + shd_desc.vs.entry = "main0"; + shd_desc.fs.entry = "main0"; + switch (sg_query_backend()) { + case SG_BACKEND_METAL_MACOS: + shd_desc.vs.bytecode = SG_RANGE(_sdtx_vs_bytecode_metal_macos); + shd_desc.fs.bytecode = SG_RANGE(_sdtx_fs_bytecode_metal_macos); + break; + case SG_BACKEND_METAL_IOS: + shd_desc.vs.bytecode = SG_RANGE(_sdtx_vs_bytecode_metal_ios); + shd_desc.fs.bytecode = SG_RANGE(_sdtx_fs_bytecode_metal_ios); + break; + default: + shd_desc.vs.source = (const char*)_sdtx_vs_source_metal_sim; + shd_desc.fs.source = (const char*)_sdtx_fs_source_metal_sim; + break; + } + #elif defined(SOKOL_D3D11) + shd_desc.vs.bytecode = SG_RANGE(_sdtx_vs_bytecode_hlsl4); + shd_desc.fs.bytecode = SG_RANGE(_sdtx_fs_bytecode_hlsl4); + #elif defined(SOKOL_WGPU) + shd_desc.vs.source = (const char*)_sdtx_vs_source_wgsl; + shd_desc.fs.source = (const char*)_sdtx_fs_source_wgsl; + #else + shd_desc.vs.source = _sdtx_vs_src_dummy; + shd_desc.fs.source = _sdtx_fs_src_dummy; + #endif + _sdtx.shader = sg_make_shader(&shd_desc); + SOKOL_ASSERT(SG_INVALID_ID != _sdtx.shader.id); + + // unpack font data + memset(_sdtx.font_pixels, 0xFF, sizeof(_sdtx.font_pixels)); + const int unpacked_font_size = (int) (sizeof(_sdtx.font_pixels) / SDTX_MAX_FONTS); + for (int i = 0; i < SDTX_MAX_FONTS; i++) { + if (_sdtx.desc.fonts[i].data.ptr) { + _sdtx_unpack_font(&_sdtx.desc.fonts[i], &_sdtx.font_pixels[i * unpacked_font_size]); + } + } + + // create font texture and sampler + sg_image_desc img_desc; + _sdtx_clear(&img_desc, sizeof(img_desc)); + img_desc.width = 256 * 8; + img_desc.height = SDTX_MAX_FONTS * 8; + img_desc.pixel_format = SG_PIXELFORMAT_R8; + img_desc.data.subimage[0][0] = SG_RANGE(_sdtx.font_pixels); + img_desc.label = "sdtx-font-texture"; + _sdtx.font_img = sg_make_image(&img_desc); + SOKOL_ASSERT(SG_INVALID_ID != _sdtx.font_img.id); + + sg_sampler_desc smp_desc; + _sdtx_clear(&smp_desc, sizeof(smp_desc)); + smp_desc.min_filter = SG_FILTER_NEAREST; + smp_desc.mag_filter = SG_FILTER_NEAREST; + smp_desc.wrap_u = SG_WRAP_CLAMP_TO_EDGE; + smp_desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE; + smp_desc.label = "sdtx-font-sampler"; + _sdtx.font_smp = sg_make_sampler(&smp_desc); + SOKOL_ASSERT(SG_INVALID_ID != _sdtx.font_smp.id); + + sg_pop_debug_group(); +} + +static void _sdtx_discard_common(void) { + sg_push_debug_group("sokol-debugtext"); + sg_destroy_sampler(_sdtx.font_smp); + sg_destroy_image(_sdtx.font_img); + sg_destroy_shader(_sdtx.shader); + if (_sdtx.fmt_buf) { + _sdtx_free(_sdtx.fmt_buf); + _sdtx.fmt_buf = 0; + } + sg_pop_debug_group(); +} + +static uint32_t _sdtx_pack_rgbab(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + return (uint32_t)(((uint32_t)a<<24)|((uint32_t)b<<16)|((uint32_t)g<<8)|r); +} + +static float _sdtx_clamp(float v, float lo, float hi) { + if (v < lo) return lo; + else if (v > hi) return hi; + else return v; +} + +static uint32_t _sdtx_pack_rgbaf(float r, float g, float b, float a) { + uint8_t r_u8 = (uint8_t) (_sdtx_clamp(r, 0.0f, 1.0f) * 255.0f); + uint8_t g_u8 = (uint8_t) (_sdtx_clamp(g, 0.0f, 1.0f) * 255.0f); + uint8_t b_u8 = (uint8_t) (_sdtx_clamp(b, 0.0f, 1.0f) * 255.0f); + uint8_t a_u8 = (uint8_t) (_sdtx_clamp(a, 0.0f, 1.0f) * 255.0f); + return _sdtx_pack_rgbab(r_u8, g_u8, b_u8, a_u8); +} + +static void _sdtx_ctrl_char(_sdtx_context_t* ctx, uint8_t c) { + switch (c) { + case '\r': + ctx->pos.x = 0.0f; + break; + case '\n': + ctx->pos.x = 0.0f; + ctx->pos.y += 1.0f; + break; + case '\t': + ctx->pos.x = (ctx->pos.x - fmodf(ctx->pos.x, ctx->tab_width)) + ctx->tab_width; + break; + case ' ': + ctx->pos.x += 1.0f; + break; + } +} + +static _sdtx_vertex_t* _sdtx_next_vertex(_sdtx_context_t* ctx) { + if ((ctx->vertices.next + 6) <= ctx->vertices.cap) { + _sdtx_vertex_t* vx = &ctx->vertices.ptr[ctx->vertices.next]; + ctx->vertices.next += 6; + return vx; + } else { + return 0; + } +} + +static _sdtx_command_t* _sdtx_cur_command(_sdtx_context_t* ctx) { + if (ctx->commands.next > 0) { + return &ctx->commands.ptr[ctx->commands.next - 1]; + } else { + return 0; + } +} + +static _sdtx_command_t* _sdtx_next_command(_sdtx_context_t* ctx) { + if (ctx->commands.next < ctx->commands.cap) { + return &ctx->commands.ptr[ctx->commands.next++]; + } else { + _SDTX_ERROR(COMMAND_BUFFER_FULL); + return 0; + } +} + +static void _sdtx_set_layer(_sdtx_context_t* ctx, int layer_id) { + ctx->cur_layer_id = layer_id; + _sdtx_command_t* cur_cmd = _sdtx_cur_command(ctx); + if (cur_cmd) { + if ((cur_cmd->num_vertices == 0) || (cur_cmd->layer_id == layer_id)) { + // no vertices recorded in current draw command, or layer hasn't changed, can just reuse this + cur_cmd->layer_id = layer_id; + } else { + // layer has changed, need to start a new draw command + _sdtx_command_t* next_cmd = _sdtx_next_command(ctx); + if (next_cmd) { + next_cmd->layer_id = layer_id; + next_cmd->first_vertex = cur_cmd->first_vertex + cur_cmd->num_vertices; + next_cmd->num_vertices = 0; + } + } + } else { + // first draw command in frame + _sdtx_command_t* next_cmd = _sdtx_next_command(ctx); + if (next_cmd) { + next_cmd->layer_id = layer_id; + next_cmd->first_vertex = 0; + next_cmd->num_vertices = 0; + } + } +} + +static void _sdtx_render_char(_sdtx_context_t* ctx, uint8_t c) { + _sdtx_vertex_t* vx = _sdtx_next_vertex(ctx); + _sdtx_command_t* cmd = _sdtx_cur_command(ctx); + if (vx && cmd) { + // update vertex count in current draw command + cmd->num_vertices += 6; + + const float x0 = (ctx->origin.x + ctx->pos.x) * ctx->glyph_size.x; + const float y0 = (ctx->origin.y + ctx->pos.y) * ctx->glyph_size.y; + const float x1 = x0 + ctx->glyph_size.x; + const float y1 = y0 + ctx->glyph_size.y; + + // glyph width and height in font texture space + // NOTE: the '+1' and '-2' fixes texture bleeding into the neighboring font texture cell + const uint16_t uvw = 0x10000 / 0x100; + const uint16_t uvh = 0x10000 / SDTX_MAX_FONTS; + const uint16_t u0 = (((uint16_t)c) * uvw) + 1; + const uint16_t v0 = (((uint16_t)ctx->cur_font) * uvh) + 1; + uint16_t u1 = (u0 + uvw) - 2; + uint16_t v1 = (v0 + uvh) - 2; + const uint32_t color = ctx->color; + + // write 6 vertices + vx->x=x0; vx->y=y0; vx->u = u0; vx->v = v0; vx->color = color; vx++; + vx->x=x1; vx->y=y0; vx->u = u1; vx->v = v0; vx->color = color; vx++; + vx->x=x1; vx->y=y1; vx->u = u1; vx->v = v1; vx->color = color; vx++; + + vx->x=x0; vx->y=y0; vx->u = u0; vx->v = v0; vx->color = color; vx++; + vx->x=x1; vx->y=y1; vx->u = u1; vx->v = v1; vx->color = color; vx++; + vx->x=x0; vx->y=y1; vx->u = u0; vx->v = v1; vx->color = color; vx++; + } + ctx->pos.x += 1.0f; +} + +static void _sdtx_put_char(_sdtx_context_t* ctx, char c) { + uint8_t c_u8 = (uint8_t)c; + if (c_u8 <= 32) { + _sdtx_ctrl_char(ctx, c_u8); + } else { + _sdtx_render_char(ctx, c_u8); + } +} + +SOKOL_API_IMPL void _sdtx_draw_layer(_sdtx_context_t* ctx, int layer_id) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + SOKOL_ASSERT(ctx); + if ((ctx->vertices.next > 0) && (ctx->commands.next > 0)) { + sg_push_debug_group("sokol-debugtext"); + + if (ctx->update_frame_id != ctx->frame_id) { + ctx->update_frame_id = ctx->frame_id; + const sg_range range = { ctx->vertices.ptr, (size_t)ctx->vertices.next * sizeof(_sdtx_vertex_t) }; + sg_update_buffer(ctx->vbuf, &range); + } + + sg_apply_pipeline(ctx->pip); + sg_bindings bindings; + _sdtx_clear(&bindings, sizeof(bindings)); + bindings.vertex_buffers[0] = ctx->vbuf; + bindings.fs.images[0] = _sdtx.font_img; + bindings.fs.samplers[0] = _sdtx.font_smp; + sg_apply_bindings(&bindings); + for (int cmd_index = 0; cmd_index < ctx->commands.next; cmd_index++) { + const _sdtx_command_t* cmd = &ctx->commands.ptr[cmd_index]; + if (cmd->layer_id != layer_id) { + continue; + } + SOKOL_ASSERT((cmd->num_vertices % 6) == 0); + sg_draw(cmd->first_vertex, cmd->num_vertices, 1); + } + sg_pop_debug_group(); + } +} + + +static sdtx_desc_t _sdtx_desc_defaults(const sdtx_desc_t* desc) { + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + sdtx_desc_t res = *desc; + res.context_pool_size = _sdtx_def(res.context_pool_size, _SDTX_DEFAULT_CONTEXT_POOL_SIZE); + res.printf_buf_size = _sdtx_def(res.printf_buf_size, _SDTX_DEFAULT_PRINTF_BUF_SIZE); + for (int i = 0; i < SDTX_MAX_FONTS; i++) { + if (res.fonts[i].data.ptr) { + res.fonts[i].last_char = _sdtx_def(res.fonts[i].last_char, 255); + } + } + res.context = _sdtx_context_desc_defaults(&res.context); + SOKOL_ASSERT(res.context_pool_size > 0); + SOKOL_ASSERT(res.printf_buf_size > 0); + SOKOL_ASSERT(res.context.char_buf_size > 0); + return res; +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void sdtx_setup(const sdtx_desc_t* desc) { + SOKOL_ASSERT(desc); + _sdtx_clear(&_sdtx, sizeof(_sdtx)); + _sdtx.init_cookie = _SDTX_INIT_COOKIE; + _sdtx.desc = _sdtx_desc_defaults(desc); + _sdtx_setup_context_pool(&_sdtx.desc); + _sdtx_setup_common(); + _sdtx.def_ctx_id = sdtx_make_context(&_sdtx.desc.context); + SOKOL_ASSERT(SDTX_DEFAULT_CONTEXT.id == _sdtx.def_ctx_id.id); + sdtx_set_context(_sdtx.def_ctx_id); +} + +SOKOL_API_IMPL void sdtx_shutdown(void) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + for (int i = 0; i < _sdtx.context_pool.pool.size; i++) { + _sdtx_context_t* ctx = &_sdtx.context_pool.contexts[i]; + _sdtx_destroy_context(_sdtx_make_ctx_id(ctx->slot.id)); + } + _sdtx_discard_common(); + _sdtx_discard_context_pool(); + _sdtx.init_cookie = 0; +} + +SOKOL_API_IMPL sdtx_font_desc_t sdtx_font_kc853(void) { + sdtx_font_desc_t desc = { { _sdtx_font_kc853, sizeof(_sdtx_font_kc853) }, 0, 255 }; + return desc; +} + +SOKOL_API_IMPL sdtx_font_desc_t sdtx_font_kc854(void) { + sdtx_font_desc_t desc = { { _sdtx_font_kc854, sizeof(_sdtx_font_kc854) }, 0, 255 }; + return desc; +} + +SOKOL_API_IMPL sdtx_font_desc_t sdtx_font_z1013(void) { + sdtx_font_desc_t desc = { { _sdtx_font_z1013, sizeof(_sdtx_font_z1013) }, 0, 255 }; + return desc; +} + +SOKOL_API_IMPL sdtx_font_desc_t sdtx_font_cpc(void) { + sdtx_font_desc_t desc = { { _sdtx_font_cpc, sizeof(_sdtx_font_cpc) }, 0, 255 }; + return desc; +} + +SOKOL_API_IMPL sdtx_font_desc_t sdtx_font_c64(void) { + sdtx_font_desc_t desc = { { _sdtx_font_c64, sizeof(_sdtx_font_c64) }, 0, 255 }; + return desc; +} + +SOKOL_API_IMPL sdtx_font_desc_t sdtx_font_oric(void) { + sdtx_font_desc_t desc = { { _sdtx_font_oric, sizeof(_sdtx_font_oric) }, 0, 255 }; + return desc; +} + +SOKOL_API_IMPL sdtx_context sdtx_make_context(const sdtx_context_desc_t* desc) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + SOKOL_ASSERT(desc); + sdtx_context ctx_id = _sdtx_alloc_context(); + if (ctx_id.id != SG_INVALID_ID) { + _sdtx_init_context(ctx_id, desc); + } else { + _SDTX_ERROR(CONTEXT_POOL_EXHAUSTED); + } + return ctx_id; +} + +SOKOL_API_IMPL void sdtx_destroy_context(sdtx_context ctx_id) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + if (_sdtx_is_default_context(ctx_id)) { + _SDTX_ERROR(CANNOT_DESTROY_DEFAULT_CONTEXT); + return; + } + _sdtx_destroy_context(ctx_id); + // re-validate the current context pointer (this will return a nullptr + // if we just destroyed the current context) + _sdtx.cur_ctx = _sdtx_lookup_context(_sdtx.cur_ctx_id.id); +} + +SOKOL_API_IMPL void sdtx_set_context(sdtx_context ctx_id) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + if (_sdtx_is_default_context(ctx_id)) { + _sdtx.cur_ctx_id = _sdtx.def_ctx_id; + } else { + _sdtx.cur_ctx_id = ctx_id; + } + // this may return a nullptr if the ctx_id handle is invalid + _sdtx.cur_ctx = _sdtx_lookup_context(_sdtx.cur_ctx_id.id); +} + +SOKOL_API_IMPL sdtx_context sdtx_get_context(void) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + return _sdtx.cur_ctx_id; +} + +SOKOL_API_IMPL sdtx_context sdtx_default_context(void) { + return SDTX_DEFAULT_CONTEXT; +} + +SOKOL_API_IMPL void sdtx_layer(int layer_id) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + _sdtx_set_layer(ctx, layer_id); + } +} + +SOKOL_API_IMPL void sdtx_font(int font_index) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + SOKOL_ASSERT((font_index >= 0) && (font_index < SDTX_MAX_FONTS)); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->cur_font = font_index; + } +} + +SOKOL_API_IMPL void sdtx_canvas(float w, float h) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + SOKOL_ASSERT(!isnan(w)); + SOKOL_ASSERT(!isnan(h)); + SOKOL_ASSERT((w > 0.0f) && (h > 0.0f)); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->canvas_size.x = w; + ctx->canvas_size.y = h; + ctx->glyph_size.x = (8.0f / ctx->canvas_size.x); + ctx->glyph_size.y = (8.0f / ctx->canvas_size.y); + ctx->origin.x = 0.0f; + ctx->origin.y = 0.0f; + ctx->pos.x = 0.0f; + ctx->pos.y = 0.0f; + } +} + +SOKOL_API_IMPL void sdtx_origin(float x, float y) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->origin.x = x; + ctx->origin.y = y; + } +} + +SOKOL_API_IMPL void sdtx_home(void) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->pos.x = 0.0f; + ctx->pos.y = 0.0f; + } +} + +SOKOL_API_IMPL void sdtx_pos(float x, float y) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->pos.x = x; + ctx->pos.y = y; + } +} + +SOKOL_API_IMPL void sdtx_pos_x(float x) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->pos.x = x; + } +} + +SOKOL_API_IMPL void sdtx_pos_y(float y) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->pos.y = y; + } +} + +SOKOL_API_IMPL void sdtx_move(float dx, float dy) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->pos.x += dx; + ctx->pos.y += dy; + } +} + +SOKOL_API_IMPL void sdtx_move_x(float dx) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->pos.x += dx; + } +} + +SOKOL_API_IMPL void sdtx_move_y(float dy) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->pos.y += dy; + } +} + +SOKOL_API_IMPL void sdtx_crlf(void) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->pos.x = 0.0f; + ctx->pos.y += 1.0f; + } +} + +SOKOL_API_IMPL void sdtx_color3b(uint8_t r, uint8_t g, uint8_t b) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->color = _sdtx_pack_rgbab(r, g, b, 255); + } +} + +SOKOL_API_IMPL void sdtx_color3f(float r, float g, float b) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->color = _sdtx_pack_rgbaf(r, g, b, 1.0f); + } +} + +SOKOL_API_IMPL void sdtx_color4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->color = _sdtx_pack_rgbab(r, g, b, a); + } +} + +SOKOL_API_IMPL void sdtx_color4f(float r, float g, float b, float a) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->color = _sdtx_pack_rgbaf(r, g, b, a); + } +} + +SOKOL_API_IMPL void sdtx_color1i(uint32_t rgba) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + ctx->color = rgba; + } +} + +SOKOL_DEBUGTEXT_API_DECL void sdtx_putc(char chr) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + _sdtx_put_char(ctx, chr); + } +} + +SOKOL_DEBUGTEXT_API_DECL void sdtx_puts(const char* str) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + char chr; + while (0 != (chr = *str++)) { + _sdtx_put_char(ctx, chr); + } + } +} + +SOKOL_DEBUGTEXT_API_DECL void sdtx_putr(const char* str, int len) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + for (int i = 0; i < len; i++) { + char chr = str[i]; + if (0 == chr) { + break; + } + _sdtx_put_char(ctx, chr); + } + } +} + +SOKOL_DEBUGTEXT_API_DECL int sdtx_vprintf(const char* fmt, va_list args) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + SOKOL_ASSERT(_sdtx.fmt_buf && (_sdtx.fmt_buf_size >= 2)); + int res = SOKOL_VSNPRINTF(_sdtx.fmt_buf, _sdtx.fmt_buf_size, fmt, args); + // make sure we're 0-terminated in case we're on an old MSVC + _sdtx.fmt_buf[_sdtx.fmt_buf_size-1] = 0; + sdtx_puts(_sdtx.fmt_buf); + return res; +} + +SOKOL_DEBUGTEXT_API_DECL int sdtx_printf(const char* fmt, ...) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + SOKOL_ASSERT(_sdtx.fmt_buf && (_sdtx.fmt_buf_size >= 2)); + va_list args; + va_start(args, fmt); + int res = SOKOL_VSNPRINTF(_sdtx.fmt_buf, _sdtx.fmt_buf_size, fmt, args); + va_end(args); + // make sure we're 0-terminated in case we're on an old MSVC + _sdtx.fmt_buf[_sdtx.fmt_buf_size-1] = 0; + sdtx_puts(_sdtx.fmt_buf); + return res; +} + +SOKOL_API_IMPL void sdtx_draw(void) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + _sdtx_draw_layer(ctx, 0); + } +} + +SOKOL_API_IMPL void sdtx_context_draw(sdtx_context ctx_id) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx_lookup_context(ctx_id.id); + if (ctx) { + _sdtx_draw_layer(ctx, 0); + } +} + +SOKOL_API_IMPL void sdtx_draw_layer(int layer_id) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx.cur_ctx; + if (ctx) { + _sdtx_draw_layer(ctx, layer_id); + } +} + +SOKOL_API_IMPL void sdtx_context_draw_layer(sdtx_context ctx_id, int layer_id) { + SOKOL_ASSERT(_SDTX_INIT_COOKIE == _sdtx.init_cookie); + _sdtx_context_t* ctx = _sdtx_lookup_context(ctx_id.id); + if (ctx) { + _sdtx_draw_layer(ctx, layer_id); + } +} +#endif // SOKOL_DEBUGTEXT_IMPL diff --git a/thirdparty/sokol/util/sokol_fontstash.h b/thirdparty/sokol/util/sokol_fontstash.h new file mode 100644 index 0000000..a650585 --- /dev/null +++ b/thirdparty/sokol/util/sokol_fontstash.h @@ -0,0 +1,1790 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_FONTSTASH_IMPL) +#define SOKOL_FONTSTASH_IMPL +#endif +#ifndef SOKOL_FONTSTASH_INCLUDED +/* + sokol_fontstash.h -- renderer for https://github.com/memononen/fontstash + on top of sokol_gl.h + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_FONTSTASH_IMPL + + before you include this file in *one* C or C++ file to create the + implementation. + + The following defines are used by the implementation to select the + platform-specific embedded shader code (these are the same defines as + used by sokol_gfx.h and sokol_app.h): + + SOKOL_GLCORE + SOKOL_GLES3 + SOKOL_D3D11 + SOKOL_METAL + + ...optionally provide the following macros to override defaults: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_FONTSTASH_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_FONTSTASH_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + + Include the following headers before including sokol_fontstash.h: + + sokol_gfx.h + + Additionally include the following headers for including the sokol_fontstash.h + implementation: + + sokol_gl.h + + HOW TO + ====== + --- First initialize sokol-gfx and sokol-gl as usual: + + sg_setup(&(sg_desc){...}); + sgl_setup(&(sgl_desc){...}); + + --- Create at least one fontstash context with sfons_create() (this replaces + glfonsCreate() from fontstash.h's example GL renderer: + + FONScontext* ctx = sfons_create(&(sfons_desc_t){ + .width = atlas_width, + .height = atlas_height, + }); + + Each FONScontext manages one font atlas texture which can hold rasterized + glyphs for multiple fonts. + + --- From here on, use fontstash.h's functions "as usual" to add TTF + font data and draw text. Note that (just like with sokol-gl), text + rendering can happen anywhere in the frame, not only inside + a sokol-gfx rendering pass. + + --- You can use the helper function + + uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) + + To convert a 0..255 RGBA color into a packed uint32_t color value + expected by fontstash.h. + + --- Once per frame before calling sgl_draw(), call: + + sfons_flush(FONScontext* ctx) + + ...this will update the dynamic sokol-gfx texture with the latest font + atlas content. + + --- To actually render the text (and any other sokol-gl draw commands), + call sgl_draw() inside a sokol-gfx frame. + + --- NOTE that you can mix fontstash.h calls with sokol-gl calls to mix + text rendering with sokol-gl rendering. You can also use + sokol-gl's matrix stack to position fontstash.h text in 3D. + + --- finally on application shutdown, call: + + sfons_destroy(FONScontext* ctx) + + before sgl_shutdown() and sg_shutdown() + + + WHAT HAPPENS UNDER THE HOOD: + ============================ + + FONScontext* sfons_create(const sfons_desc_t* desc) + - creates a sokol-gfx shader compatible with sokol-gl + - creates an sgl_pipeline object with alpha-blending using + this shader + - creates a 1-byte-per-pixel font atlas texture via sokol-gfx + (pixel format SG_PIXELFORMAT_R8) + + fonsDrawText(): + - this will call the following sequence of sokol-gl functions: + + sgl_enable_texture(); + sgl_texture(...); + sgl_push_pipeline(); + sgl_load_pipeline(...); + sgl_begin_triangles(); + for each vertex: + sgl_v2f_t2f_c1i(...); + sgl_end(); + sgl_pop_pipeline(); + sgl_disable_texture(); + + - note that sokol-gl will merge several sgl_*_begin/sgl_end pairs + into a single draw call if no relevant state has changed, typically + all calls to fonsDrawText() will be merged into a single draw call + as long as all calls use the same FONScontext + + sfons_flush(FONScontext* ctx): + - this will call sg_update_image() on the font atlas texture + if fontstash.h has added any rasterized glyphs since the last + frame + + sfons_destroy(FONScontext* ctx): + - destroy the font atlas texture, sgl_pipeline and sg_shader objects + + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + FONScontext* fons_context = sfons_create(&(sfons_desc_t){ + ... + .allocator = { + .alloc_fn = my_alloc, + .free_fn = my_free, + .user_data = ..., + } + }); + ... + + If no overrides are provided, malloc and free will be used. Please + note that this doesn't affect any memory allocation performed + in fontstash.h (unfortunately those are hardwired to malloc/free). + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_FONTSTASH_INCLUDED (1) +#include +#include +#include // size_t + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_fontstash.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_FONTSTASH_API_DECL) +#define SOKOL_FONTSTASH_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_FONTSTASH_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_FONTSTASH_IMPL) +#define SOKOL_FONTSTASH_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_FONTSTASH_API_DECL __declspec(dllimport) +#else +#define SOKOL_FONTSTASH_API_DECL extern +#endif +#endif +#ifdef __cplusplus +extern "C" { +#endif + +/* + sfonst_allocator_t + + Used in sfons_desc_t to provide custom memory-alloc and -free functions + to sokol_fontstash.h. If memory management should be overridden, both the + alloc_fn and free_fn function must be provided (e.g. it's not valid to + override one function but not the other). + + NOTE that this does not affect memory allocation calls inside + fontstash.h +*/ +typedef struct sfons_allocator_t { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} sfons_allocator_t; + +typedef struct sfons_desc_t { + int width; // initial width of font atlas texture (default: 512, must be power of 2) + int height; // initial height of font atlas texture (default: 512, must be power of 2) + sfons_allocator_t allocator; // optional memory allocation overrides +} sfons_desc_t; + +SOKOL_FONTSTASH_API_DECL FONScontext* sfons_create(const sfons_desc_t* desc); +SOKOL_FONTSTASH_API_DECL void sfons_destroy(FONScontext* ctx); +SOKOL_FONTSTASH_API_DECL void sfons_flush(FONScontext* ctx); +SOKOL_FONTSTASH_API_DECL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* SOKOL_FONTSTASH_INCLUDED */ + +/*-- IMPLEMENTATION ----------------------------------------------------------*/ +#ifdef SOKOL_FONTSTASH_IMPL +#define SOKOL_FONTSTASH_IMPL_INCLUDED (1) + +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sfons_desc_t.allocator to override memory allocation functions" +#endif + +#include // memset, memcpy +#include // malloc, free + +#if !defined(SOKOL_GL_INCLUDED) +#error "Please include sokol_gl.h before sokol_fontstash.h" +#endif +#if !defined(FONS_H) +#error "Please include fontstash.h before sokol_fontstash.h" +#endif + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef SOKOL_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#endif +#ifndef _SOKOL_UNUSED + #define _SOKOL_UNUSED(x) (void)(x) +#endif + +#if defined(SOKOL_GLCORE) +/* + Embedded source code compiled with: + + sokol-shdc -i sfons.glsl -o sfons.h -l glsl410:glsl300es:hlsl4:metal_macos:metal_ios:metal_sim:wgsl -b + + (not that for Metal and D3D11 byte code, sokol-shdc must be run + on macOS and Windows) + + @vs vs + uniform vs_params { + uniform mat4 mvp; + uniform mat4 tm; + }; + in vec4 position; + in vec2 texcoord0; + in vec4 color0; + in float psize; + out vec4 uv; + out vec4 color; + void main() { + gl_Position = mvp * position; + #ifndef SOKOL_WGSL + gl_PointSize = psize; + #endif + uv = tm * vec4(texcoord0, 0.0, 1.0); + color = color0; + } + @end + + @fs fs + uniform texture2D tex; + uniform sampler smp; + in vec4 uv; + in vec4 color; + out vec4 frag_color; + void main() { + frag_color = vec4(1.0, 1.0, 1.0, texture(sampler2D(tex, smp), uv.xy).r) * color; + } + @end + + @program sfontstash vs fs +*/ +static const uint8_t _sfons_vs_source_glsl410[520] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e, + 0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a, + 0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20, + 0x3d,0x20,0x33,0x29,0x20,0x69,0x6e,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x70,0x73, + 0x69,0x7a,0x65,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65, + 0x63,0x34,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f, + 0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76, + 0x65,0x63,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x6c, + 0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d, + 0x20,0x31,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c, + 0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74, + 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61, + 0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74,0x53, + 0x69,0x7a,0x65,0x20,0x3d,0x20,0x70,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61, + 0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73, + 0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x37, + 0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34,0x28,0x74,0x65,0x78,0x63,0x6f,0x6f, + 0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const uint8_t _sfons_fs_source_glsl410[245] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f, + 0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74, + 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34, + 0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x28,0x31,0x2e, + 0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76, + 0x2e,0x78,0x79,0x29,0x2e,0x78,0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_GLES3) +static const uint8_t _sfons_vs_source_glsl300es[481] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f, + 0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29, + 0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69, + 0x6f,0x6e,0x20,0x3d,0x20,0x33,0x29,0x20,0x69,0x6e,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x20,0x70,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34, + 0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63, + 0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x6f,0x75,0x74, + 0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79, + 0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32, + 0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30, + 0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b, + 0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b, + 0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d, + 0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20, + 0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74,0x53,0x69,0x7a,0x65,0x20,0x3d,0x20,0x70, + 0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d, + 0x61,0x74,0x34,0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d, + 0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65, + 0x63,0x34,0x28,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e, + 0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c, + 0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a, + 0x00, +}; +static const uint8_t _sfons_fs_source_glsl300es[276] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d, + 0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69, + 0x6f,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a, + 0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x68,0x69,0x67,0x68,0x70,0x20, + 0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34,0x20,0x75, + 0x76,0x3b,0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61, + 0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x28,0x31,0x2e,0x30, + 0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x2e, + 0x78,0x79,0x29,0x2e,0x78,0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a, + 0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_METAL) +static const uint8_t _sfons_vs_bytecode_metal_macos[3317] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf5,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe0,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x76,0x25,0x5f,0x37,0x22,0xd0,0x3f, + 0x64,0xef,0xff,0xc3,0x45,0x1a,0x3d,0xb7,0x5e,0x83,0x13,0x96,0xd3,0x09,0xec,0x53, + 0x25,0xd5,0x7e,0x0c,0xed,0xb9,0x58,0x34,0x02,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x40,0x00,0x00,0x00,0x56,0x41,0x54, + 0x54,0x2a,0x00,0x04,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x80, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x00,0x02,0x80,0x70,0x73,0x69,0x7a,0x65,0x00,0x03,0x80,0x56,0x41,0x54, + 0x59,0x06,0x00,0x04,0x00,0x06,0x04,0x06,0x03,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, + 0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00, + 0x00,0xc4,0x0b,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00, + 0x00,0xee,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00, + 0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84, + 0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b, + 0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90, + 0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8, + 0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00,0x00,0x81,0x00,0x00, + 0x00,0x1b,0xc8,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x90,0x80,0x8a,0x18,0x87,0x77, + 0x90,0x07,0x79,0x28,0x87,0x71,0xa0,0x07,0x76,0xc8,0x87,0x36,0x90,0x87,0x77,0xa8, + 0x07,0x77,0x20,0x87,0x72,0x20,0x87,0x36,0x20,0x87,0x74,0xb0,0x87,0x74,0x20,0x87, + 0x72,0x68,0x83,0x79,0x88,0x07,0x79,0xa0,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76, + 0x08,0x07,0x7a,0x40,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c, + 0xd2,0x61,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8, + 0xa1,0x0d,0xc6,0x21,0x1c,0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79, + 0x28,0x07,0x80,0x60,0x87,0x72,0x98,0x87,0x79,0x68,0x03,0x78,0x90,0x87,0x72,0x18, + 0x87,0x74,0x98,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x76,0x08,0x07,0x72,0x00,0xcc, + 0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21, + 0x1c,0xda,0xa1,0x1c,0xda,0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e, + 0xda,0xa0,0x1c,0xd8,0x21,0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x06, + 0x77,0x78,0x87,0x36,0x30,0x07,0x79,0x08,0x87,0x76,0x28,0x87,0x36,0x80,0x87,0x77, + 0x48,0x07,0x77,0xa0,0x87,0x72,0x90,0x87,0x36,0x28,0x07,0x76,0x48,0x87,0x76,0x68, + 0x03,0x77,0x78,0x07,0x77,0x68,0x03,0x76,0x28,0x87,0x70,0x30,0x07,0x80,0x70,0x87, + 0x77,0x68,0x83,0x74,0x70,0x07,0x73,0x98,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76, + 0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d, + 0xda,0x40,0x1d,0xea,0xa1,0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca, + 0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36, + 0x70,0x87,0x70,0x70,0x87,0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0, + 0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6, + 0x21,0x1d,0xce,0xc1,0x1d,0xca,0x81,0x1c,0xda,0x40,0x1f,0xca,0x41,0x1e,0xde,0x61, + 0x1e,0xda,0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90, + 0x87,0x7a,0x28,0x07,0x80,0x70,0x87,0x77,0x68,0x03,0x7a,0x90,0x87,0x70,0x80,0x07, + 0x78,0x48,0x07,0x77,0x38,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41, + 0x1e,0xea,0xa1,0x1c,0x00,0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e, + 0xe4,0xe1,0x1d,0xe8,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0x40,0x1c,0xea, + 0xc1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c, + 0x00,0x88,0x7a,0x70,0x87,0x79,0x08,0x07,0x73,0x28,0x87,0x36,0x30,0x07,0x78,0x68, + 0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea, + 0x61,0x1e,0xca,0xa1,0x0d,0xe6,0xe1,0x1d,0xcc,0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1, + 0x1d,0xc2,0x81,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x36,0x18,0x42, + 0x01,0x2c,0x40,0x05,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40, + 0x00,0x89,0x20,0x00,0x00,0x20,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85, + 0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a, + 0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x44,0x33,0x00,0xc3,0x08,0x04,0x60,0x89,0x10,0x02, + 0x18,0x46,0x10,0x80,0x24,0x08,0x33,0x51,0xf3,0x40,0x0f,0xf2,0x50,0x0f,0xe3,0x40, + 0x0f,0x6e,0xd0,0x0e,0xe5,0x40,0x0f,0xe1,0xc0,0x0e,0x7a,0xa0,0x07,0xed,0x10,0x0e, + 0xf4,0x20,0x0f,0xe9,0x80,0x0f,0x28,0x20,0x07,0x49,0x53,0x44,0x09,0x93,0x5f,0x49, + 0xff,0x03,0x44,0x00,0x23,0x21,0xa1,0x94,0x41,0x04,0x43,0x28,0x86,0x08,0x23,0x80, + 0x43,0x68,0x20,0x60,0x8e,0x00,0x0c,0x52,0x60,0xcd,0x11,0x80,0xc2,0x20,0x42,0x20, + 0x0c,0x23,0x10,0xcb,0x08,0x00,0x00,0x00,0x00,0x13,0xb2,0x70,0x48,0x07,0x79,0xb0, + 0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x76,0x08,0x87, + 0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83,0x38,0x70,0x03,0x38, + 0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0, + 0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x71,0x60,0x07, + 0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e,0x73,0x20,0x07,0x7a, + 0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60, + 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0, + 0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07, + 0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73, + 0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40, + 0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07, + 0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72, + 0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0, + 0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20,0x07,0x7a,0x20,0x07, + 0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x72,0x50,0x07,0x76, + 0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x50, + 0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07, + 0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74, + 0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60, + 0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x49,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00, + 0xc8,0x02,0x01,0x00,0x00,0x0b,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c, + 0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50,0x04,0x05,0x18, + 0x50,0x08,0x65,0x50,0x80,0x02,0x05,0x51,0x20,0xd4,0x46,0x00,0x88,0x8d,0x25,0x34, + 0x01,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x02,0x01,0x00,0x00,0x1a,0x03,0x4c, + 0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x32, + 0x28,0x00,0xb3,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62, + 0x2c,0x81,0x22,0x2c,0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e, + 0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6, + 0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06, + 0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25, + 0x58,0x90,0x45,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x82, + 0x45,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56, + 0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50, + 0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61, + 0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04, + 0x65,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98, + 0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1, + 0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x51,0x58,0x9a,0x9c, + 0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0xd7,0x5b,0x1d,0x1d,0x5c,0x1d,0x1d,0x97,0xba, + 0xb9,0x32,0x39,0x14,0xb6,0xb7,0x31,0x37,0x98,0x14,0x46,0x61,0x69,0x72,0x2e,0x61, + 0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x6e,0x61,0x6d,0x65,0x34,0xcc,0xd8,0xde, + 0xc2,0xe8,0x68,0xc8,0x84,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95, + 0x51,0xa8,0xb3,0x1b,0xc2,0x28,0x8f,0x02,0x29,0x91,0x22,0x29,0x93,0x42,0x71,0xa9, + 0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0x61,0x31,0xf6,0xc6,0xf6,0x26, + 0x37,0x84,0x51,0x1e,0xc5,0x52,0x22,0x45,0x52,0x26,0xe5,0x22,0x13,0x96,0x26,0xe7, + 0x02,0xf7,0x36,0x97,0x46,0x97,0xf6,0xe6,0xc6,0xe5,0x8c,0xed,0x0b,0xea,0x6d,0x2e, + 0x8d,0x2e,0xed,0xcd,0x6d,0x88,0xa2,0x64,0x4a,0xa4,0x48,0xca,0xa4,0x68,0x74,0xc2, + 0xd2,0xe4,0x5c,0xe0,0xde,0xd2,0xdc,0xe8,0xbe,0xe6,0xd2,0xf4,0xca,0x58,0x98,0xb1, + 0xbd,0x85,0xd1,0x91,0x39,0x63,0xfb,0x82,0x7a,0x4b,0x73,0xa3,0x9b,0x4a,0xd3,0x2b, + 0x1b,0xa2,0x28,0x9c,0x12,0x29,0x9d,0x32,0x29,0xde,0x10,0x44,0xa9,0x14,0x4c,0xd9, + 0x94,0x8f,0x50,0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0x57,0x9a,0x1b, + 0x5c,0x1d,0x1d,0xa5,0xb0,0x34,0x39,0x17,0xb6,0xb7,0xb1,0x30,0xba,0xb4,0x37,0xb7, + 0xaf,0x34,0x37,0xb2,0x32,0x3c,0x7a,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64, + 0x28,0x5f,0x5f,0x61,0x69,0x72,0x5f,0x70,0x6c,0x61,0x63,0x65,0x68,0x6f,0x6c,0x64, + 0x65,0x72,0x5f,0x5f,0x29,0x44,0xe0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x86,0x50, + 0x8b,0xa0,0x84,0x81,0x22,0x06,0x8b,0xb0,0x04,0xca,0x18,0x28,0x91,0x22,0x29,0x93, + 0x42,0x06,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x98,0xd0,0x95,0xe1,0x8d,0xbd,0xbd, + 0xc9,0x91,0xc1,0x0c,0xa1,0x96,0x40,0x09,0x03,0x45,0x0c,0x96,0x60,0x09,0x94,0x31, + 0x50,0x22,0xc5,0x0c,0x94,0x49,0x39,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43, + 0xa8,0x65,0x50,0xc2,0x40,0x11,0x83,0x65,0x58,0x02,0x65,0x0c,0x94,0x48,0x91,0x94, + 0x49,0x49,0x03,0x16,0x70,0x73,0x69,0x7a,0x65,0x43,0xa8,0xc5,0x50,0xc2,0x40,0x11, + 0x83,0xc5,0x58,0x02,0x65,0x0c,0x94,0x48,0xe9,0x94,0x49,0x59,0x03,0x2a,0x61,0x69, + 0x72,0x2e,0x62,0x75,0x66,0x66,0x65,0x72,0x7c,0xc2,0xd2,0xe4,0x5c,0xc4,0xea,0xcc, + 0xcc,0xca,0xe4,0xbe,0xe6,0xd2,0xf4,0xca,0x88,0x84,0xa5,0xc9,0xb9,0xc8,0x95,0x85, + 0x91,0x91,0x0a,0x4b,0x93,0x73,0x99,0xa3,0x93,0xab,0x1b,0xa3,0xfb,0xa2,0xcb,0x83, + 0x2b,0xfb,0x4a,0x73,0x33,0x7b,0x23,0x62,0xc6,0xf6,0x16,0x46,0x47,0x83,0x47,0xc3, + 0xa1,0xcd,0x0e,0x8e,0x02,0x5d,0xdb,0x10,0x6a,0x11,0x16,0x62,0x11,0x94,0x38,0x50, + 0xe4,0x60,0x21,0x16,0x62,0x11,0x94,0x38,0x50,0xe6,0x80,0x51,0x58,0x9a,0x9c,0x4b, + 0x98,0xdc,0xd9,0x17,0x5d,0x1e,0x5c,0xd9,0xd7,0x5c,0x9a,0x5e,0x19,0xaf,0xb0,0x34, + 0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x30,0xb6,0xb4,0x33,0xb7, + 0xaf,0xb9,0x34,0xbd,0x32,0x26,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x1c, + 0xbe,0x62,0x72,0x86,0x90,0xc1,0x52,0x28,0x6d,0xa0,0xb8,0xc1,0x72,0x28,0x62,0xb0, + 0x08,0x4b,0xa0,0xbc,0x81,0x02,0x07,0x0a,0x1d,0x28,0x75,0xb0,0x1c,0x8a,0x1d,0x2c, + 0x89,0x12,0x29,0x77,0xa0,0x4c,0x0a,0x1e,0x0c,0x51,0x94,0x32,0x50,0xd0,0x40,0x51, + 0x03,0x85,0x0d,0x94,0x3c,0x18,0x62,0x24,0x80,0x02,0x06,0x8a,0x1e,0xf0,0x79,0x6b, + 0x73,0x4b,0x83,0x7b,0xa3,0x2b,0x73,0xa3,0x03,0x19,0x43,0x0b,0x93,0xe3,0x33,0x95, + 0xd6,0x06,0xc7,0x56,0x06,0x32,0xb4,0xb2,0x02,0x42,0x25,0x14,0x14,0x34,0x44,0x50, + 0xfa,0x60,0x88,0xa1,0xf0,0x81,0xe2,0x07,0x8d,0x32,0xc4,0x50,0xfe,0x40,0xf9,0x83, + 0x46,0x19,0x11,0xb1,0x03,0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3b,0xbc,0x03,0x39,0xd4, + 0x03,0x3b,0x94,0x83,0x1b,0x98,0x03,0x3b,0x84,0xc3,0x39,0xcc,0xc3,0x14,0x21,0x18, + 0x46,0x28,0xec,0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0x90,0x0e,0xe4,0x50,0x0e,0xee,0x40, + 0x0f,0x53,0x82,0x62,0xc4,0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f, + 0xf3,0x90,0x0e,0xef,0xe0,0x0e,0x53,0x02,0x63,0x04,0x15,0x0e,0xe9,0x20,0x0f,0x6e, + 0xc0,0x0e,0xe1,0xe0,0x0e,0xe7,0x50,0x0f,0xe1,0x70,0x0e,0xe5,0xf0,0x0b,0xf6,0x50, + 0x0e,0xf2,0x30,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x40,0x46,0x4c,0xe1,0x90,0x0e, + 0xf2,0xe0,0x06,0xe3,0xf0,0x0e,0xed,0x00,0x0f,0xe9,0xc0,0x0e,0xe5,0xf0,0x0b,0xef, + 0x00,0x0f,0xf4,0x90,0x0e,0xef,0xe0,0x0e,0xf3,0x30,0x65,0x50,0x18,0x67,0x84,0x12, + 0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f,0xf4,0x50,0x0e,0xf8,0x30,0x25, + 0xd8,0x03,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80, + 0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80, + 0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80, + 0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88, + 0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78, + 0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03, + 0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f, + 0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c, + 0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39, + 0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b, + 0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60, + 0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87, + 0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e, + 0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c, + 0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6, + 0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94, + 0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03, + 0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07, + 0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f, + 0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33, + 0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc, + 0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c, + 0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78, + 0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca, + 0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21, + 0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43, + 0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87, + 0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c, + 0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e, + 0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c, + 0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c, + 0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00, + 0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x3e,0x00,0x00, + 0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0xf4,0xc6,0x22, + 0x86,0x61,0x18,0xc6,0x22,0x04,0x41,0x10,0xc6,0x22,0x82,0x20,0x08,0xa8,0x95,0x40, + 0x19,0x14,0x01,0xbd,0x11,0x00,0x1a,0x33,0x00,0x24,0x66,0x00,0x28,0xcc,0x00,0x00, + 0x00,0xe3,0x15,0x4b,0x94,0x65,0x11,0x05,0x65,0x90,0x21,0x1a,0x0c,0x13,0x02,0xf9, + 0x8c,0x57,0x3c,0x55,0xd7,0x2d,0x14,0x94,0x41,0x86,0xea,0x70,0x4c,0x08,0xe4,0x63, + 0x41,0x01,0x9f,0xf1,0x0a,0x4a,0x13,0x03,0x31,0x70,0x28,0x28,0x83,0x0c,0x1a,0x43, + 0x99,0x10,0xc8,0xc7,0x8a,0x00,0x3e,0xe3,0x15,0xd9,0x77,0x06,0x67,0x40,0x51,0x50, + 0x06,0x19,0xbe,0x48,0x33,0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6,0x2b,0x3c,0x32,0x68, + 0x03,0x36,0x20,0x03,0x0a,0xca,0x20,0xc3,0x18,0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b, + 0xc4,0x00,0x0d,0xe2,0x00,0x0e,0x3c,0x0a,0xca,0x20,0xc3,0x19,0x70,0x61,0x60,0x42, + 0x20,0x1f,0x0b,0x0a,0xf8,0x8c,0x57,0x9c,0x41,0x1b,0xd8,0x41,0x1d,0x88,0x01,0x05, + 0xc5,0x86,0x00,0x3e,0xb3,0x0d,0x61,0x10,0x00,0xb3,0x0d,0x41,0x1b,0x04,0xb3,0x0d, + 0xc1,0x23,0xcc,0x36,0x04,0x6e,0x30,0x64,0x10,0x10,0x03,0x00,0x00,0x09,0x00,0x00, + 0x00,0x5b,0x86,0x20,0x00,0x85,0x2d,0x43,0x11,0x80,0xc2,0x96,0x41,0x09,0x40,0x61, + 0xcb,0xf0,0x04,0xa0,0xb0,0x65,0xa0,0x02,0x50,0xd8,0x32,0x60,0x01,0x28,0x6c,0x19, + 0xba,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sfons_fs_bytecode_metal_macos[2857] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x29,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x50,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0x38,0x3c,0x07,0x7d,0xe0,0x21,0x6b, + 0x04,0x03,0x36,0x12,0x1d,0xbc,0x24,0x12,0xc4,0x27,0xab,0xef,0x84,0x17,0xbe,0x12, + 0x8d,0xc7,0xbc,0x06,0xbc,0x98,0x53,0xdb,0x0b,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44, + 0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0x30,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x89,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32, + 0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45, + 0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44, + 0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8, + 0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00, + 0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07,0x7a,0x60,0x87,0x7c, + 0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72,0x68,0x03,0x72,0x48, + 0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90,0x07,0x7a,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc,0x21,0x1c,0xd8,0x61, + 0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81,0x1d,0xca,0xa1,0x0d, + 0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d,0xd8,0x61,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87,0x79,0x98,0x87,0x36, + 0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36,0x30,0x07,0x78,0x68, + 0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0xc2,0x1d,0xde, + 0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0,0xe1,0x1d,0xd2,0xc1, + 0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1,0x1d,0x00,0x7a,0x90, + 0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90,0x87,0x70,0x68,0x87, + 0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07,0x79,0x68,0x83,0x72, + 0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36,0x60,0x87,0x72,0x08, + 0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30,0x87,0x79,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1, + 0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01,0x1e,0xda,0x80,0x1e, + 0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07, + 0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87,0x36,0x30,0x07,0x78, + 0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20, + 0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c,0xc8,0xa1,0x0d,0xf4, + 0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda,0xa0,0x1d,0xc2,0x81, + 0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0xa0, + 0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68,0x83,0x76,0x08,0x07, + 0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6,0x81,0x1e,0xc2,0x61, + 0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d, + 0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e,0xda,0x60,0x1e,0xd2, + 0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87,0x70,0x30,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e, + 0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e,0xde,0xc1,0x1c,0xe8, + 0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87,0x70,0x60,0x87,0x79, + 0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81,0x00,0x16,0xa0,0xda, + 0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5,0x06,0xa3,0x08,0x80, + 0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00,0x00,0x03,0x00,0x00, + 0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00,0x00,0x89,0x20,0x00, + 0x00,0x1e,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4, + 0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4, + 0x4c,0x10,0x4c,0x33,0x00,0xc3,0x08,0x04,0x60,0x83,0x70,0x94,0x34,0x45,0x94,0x30, + 0xf9,0xff,0x44,0x5c,0x13,0x15,0x11,0xbf,0x3d,0xfc,0xd3,0x18,0x01,0x30,0x88,0x30, + 0x04,0x17,0x49,0x53,0x44,0x09,0x93,0xff,0x4b,0x00,0xf3,0x2c,0x44,0xf4,0x4f,0x63, + 0x04,0xc0,0x20,0x42,0x21,0x94,0x42,0x84,0x40,0x0c,0x9d,0x61,0x04,0x01,0x98,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0x88,0x49,0x8a,0x29,0x40,0x6d, + 0x20,0x20,0x05,0xd6,0x30,0x02,0xb1,0x8c,0x00,0x00,0x00,0x00,0x00,0x13,0xb2,0x70, + 0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68, + 0x83,0x76,0x08,0x87,0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83, + 0x38,0x70,0x03,0x38,0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78, + 0xa0,0x07,0x78,0xd0,0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90, + 0x0e,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e, + 0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76, + 0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20, + 0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a, + 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30, + 0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07, + 0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71, + 0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20, + 0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f, + 0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76, + 0xd0,0x06,0xf6,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50, + 0x07,0x71,0x20,0x07,0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07, + 0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78, + 0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x41,0x00,0x00,0x08,0x00, + 0x00,0x00,0x00,0x00,0x18,0xc2,0x38,0x40,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x64, + 0x81,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c, + 0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50,0x04,0x85,0x50, + 0x10,0x65,0x40,0x70,0x2c,0xa1,0x09,0x00,0x00,0x79,0x18,0x00,0x00,0xb7,0x00,0x00, + 0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37, + 0xb7,0x21,0xc6,0x42,0x3c,0x00,0x84,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b, + 0xd3,0x2b,0x1b,0x62,0x2c,0xc2,0x23,0x2c,0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c, + 0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26, + 0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac, + 0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0xf0, + 0x10,0x43,0x8c,0x45,0x58,0x8c,0x65,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x79, + 0x8e,0x45,0x58,0x84,0x65,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6, + 0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6, + 0x56,0x36,0x44,0x78,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c, + 0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62, + 0x6c,0x65,0x43,0x84,0x67,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5, + 0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99, + 0x95,0xd1,0x8d,0xa1,0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x9e,0x86, + 0x51,0x58,0x9a,0x9c,0x8b,0x5c,0x99,0x1b,0x59,0x99,0xdc,0x17,0x5d,0x98,0xdc,0x59, + 0x19,0x1d,0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0x2f, + 0xb7,0xb0,0xb6,0x32,0x1a,0x66,0x6c,0x6f,0x61,0x74,0x34,0x64,0xc2,0xd2,0xe4,0x5c, + 0xc2,0xe4,0xce,0xbe,0xdc,0xc2,0xda,0xca,0xa8,0x98,0xc9,0x85,0x9d,0x7d,0x8d,0xbd, + 0xb1,0xbd,0xc9,0x0d,0x61,0x9e,0x67,0x19,0x1e,0xe8,0x89,0x1e,0xe9,0x99,0x86,0x08, + 0x0f,0x45,0x29,0x2c,0x4d,0xce,0xc5,0x4c,0x2e,0xec,0xac,0xad,0xcc,0x8d,0xee,0x2b, + 0xcd,0x0d,0xae,0x8e,0x8e,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x1b,0x4c, + 0x0a,0x95,0xb0,0x34,0x39,0x97,0xb1,0x32,0x37,0xba,0x32,0x39,0x3e,0x61,0x69,0x72, + 0x2e,0x70,0x65,0x72,0x73,0x70,0x65,0x63,0x74,0x69,0x76,0x65,0x14,0xea,0xec,0x86, + 0x48,0xcb,0xf0,0x58,0xcf,0xf5,0x60,0x4f,0xf6,0x40,0x4f,0xf4,0x48,0x8f,0xc6,0xa5, + 0x6e,0xae,0x4c,0x0e,0x85,0xed,0x6d,0xcc,0x2d,0x26,0x85,0xc5,0xd8,0x1b,0xdb,0x9b, + 0xdc,0x10,0x69,0x11,0x1e,0xeb,0xe1,0x1e,0xec,0xc9,0x1e,0xe8,0x89,0x1e,0xe9,0xe9, + 0xb8,0x84,0xa5,0xc9,0xb9,0xd0,0x95,0xe1,0xd1,0xd5,0xc9,0x95,0x51,0x0a,0x4b,0x93, + 0x73,0x61,0x7b,0x1b,0x0b,0xa3,0x4b,0x7b,0x73,0xfb,0x4a,0x73,0x23,0x2b,0xc3,0xa3, + 0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7,0x56,0x46,0x8c,0xae,0x0c,0x8f,0xae, + 0x4e,0xae,0x4c,0x86,0x8c,0xc7,0x8c,0xed,0x2d,0x8c,0x8e,0x05,0x64,0x2e,0xac,0x0d, + 0x8e,0xad,0xcc,0x87,0x03,0x5d,0x19,0xde,0x10,0x6a,0x21,0x9e,0xef,0x01,0x83,0x65, + 0x58,0x84,0x27,0x0c,0x1e,0xe8,0x11,0x83,0x47,0x7a,0xc6,0x80,0x4b,0x58,0x9a,0x9c, + 0xcb,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x8f,0xb9,0xb0,0x36,0x38,0xb6,0x32,0x39, + 0x0e,0x73,0x6d,0x70,0x43,0xa4,0xe5,0x78,0xca,0xe0,0x01,0x83,0x65,0x58,0x84,0x07, + 0x7a,0xcc,0xe0,0x91,0x9e,0x33,0x18,0x82,0x3c,0xdb,0xe3,0x3d,0x64,0xf0,0xa0,0xc1, + 0x10,0x03,0x01,0x9e,0xea,0x49,0x83,0x11,0x11,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b, + 0xb4,0xc3,0x3b,0x90,0x43,0x3d,0xb0,0x43,0x39,0xb8,0x81,0x39,0xb0,0x43,0x38,0x9c, + 0xc3,0x3c,0x4c,0x11,0x82,0x61,0x84,0xc2,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xe9, + 0x40,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25,0x28,0x46,0x2c,0xe1,0x90,0x0e,0xf2,0xe0, + 0x06,0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x30,0x46,0x50, + 0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xec,0x10,0x0e,0xee,0x70,0x0e,0xf5,0x10,0x0e,0xe7, + 0x50,0x0e,0xbf,0x60,0x0f,0xe5,0x20,0x0f,0xf3,0x90,0x0e,0xef,0xe0,0x0e,0x53,0x02, + 0x64,0xc4,0x14,0x0e,0xe9,0x20,0x0f,0x6e,0x30,0x0e,0xef,0xd0,0x0e,0xf0,0x90,0x0e, + 0xec,0x50,0x0e,0xbf,0xf0,0x0e,0xf0,0x40,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x0f,0x53, + 0x06,0x85,0x71,0x46,0x30,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xe6,0x20,0x0f,0xe1,0x70, + 0x0e,0xed,0x50,0x0e,0xee,0x40,0x0f,0x53,0x02,0x35,0x00,0x00,0x00,0x79,0x18,0x00, + 0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d, + 0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c, + 0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d, + 0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d, + 0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79, + 0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc, + 0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50, + 0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30, + 0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03, + 0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07, + 0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76, + 0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98, + 0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8, + 0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21, + 0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43, + 0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f, + 0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70, + 0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0, + 0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40, + 0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41, + 0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e, + 0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07, + 0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f, + 0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d, + 0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38, + 0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88, + 0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08, + 0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50, + 0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01, + 0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03, + 0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c, + 0x00,0x71,0x20,0x00,0x00,0x08,0x00,0x00,0x00,0x16,0xb0,0x01,0x48,0xe4,0x4b,0x00, + 0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e,0x85,0x17,0xb7,0x6d, + 0x00,0x05,0x03,0x20,0x0d,0x0d,0x00,0x00,0x00,0x61,0x20,0x00,0x00,0x16,0x00,0x00, + 0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x14,0xc7,0x22, + 0x80,0x40,0x20,0x88,0x8d,0x00,0x8c,0x25,0x00,0x01,0xa9,0x11,0x80,0x1a,0x20,0x31, + 0x03,0x40,0x61,0x0e,0xe2,0xba,0xae,0x6a,0x06,0x80,0xc0,0x0c,0xc0,0x08,0xc0,0x18, + 0x01,0x08,0x82,0x20,0xfe,0x01,0x00,0x00,0x00,0x83,0x0c,0x0f,0x91,0x8c,0x18,0x28, + 0x42,0x80,0x39,0x4d,0x80,0x2c,0xc9,0x30,0xc8,0x70,0x04,0x8d,0x05,0x91,0x7c,0x66, + 0x1b,0x94,0x00,0xc8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sfons_vs_bytecode_metal_ios[3317] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf5,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe0,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x17,0x11,0x57,0x16,0x94,0x42,0x52, + 0xfb,0x1e,0xd0,0x32,0xfd,0x87,0x16,0xb0,0xa4,0xd0,0xc2,0x43,0xbe,0x93,0x8c,0xe0, + 0x2d,0x7a,0x5c,0x3e,0x06,0x4c,0x57,0xeb,0x4b,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x40,0x00,0x00,0x00,0x56,0x41,0x54, + 0x54,0x2a,0x00,0x04,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x80, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x00,0x02,0x80,0x70,0x73,0x69,0x7a,0x65,0x00,0x03,0x80,0x56,0x41,0x54, + 0x59,0x06,0x00,0x04,0x00,0x06,0x04,0x06,0x03,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, + 0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00, + 0x00,0xc0,0x0b,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00, + 0x00,0xed,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00, + 0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84, + 0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b, + 0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90, + 0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8, + 0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00,0x00,0x82,0x00,0x00, + 0x00,0x1b,0xc8,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x90,0x80,0x8a,0x18,0x87,0x77, + 0x90,0x07,0x79,0x28,0x87,0x71,0xa0,0x07,0x76,0xc8,0x87,0x36,0x90,0x87,0x77,0xa8, + 0x07,0x77,0x20,0x87,0x72,0x20,0x87,0x36,0x20,0x87,0x74,0xb0,0x87,0x74,0x20,0x87, + 0x72,0x68,0x83,0x79,0x88,0x07,0x79,0xa0,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76, + 0x08,0x07,0x7a,0x40,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c, + 0xd2,0x61,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8, + 0xa1,0x0d,0xc6,0x21,0x1c,0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79, + 0x28,0x07,0x80,0x60,0x87,0x72,0x98,0x87,0x79,0x68,0x03,0x78,0x90,0x87,0x72,0x18, + 0x87,0x74,0x98,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x76,0x08,0x07,0x72,0x00,0xcc, + 0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21, + 0x1c,0xda,0xa1,0x1c,0xda,0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e, + 0xda,0xa0,0x1c,0xd8,0x21,0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x06, + 0x77,0x78,0x87,0x36,0x30,0x07,0x79,0x08,0x87,0x76,0x28,0x87,0x36,0x80,0x87,0x77, + 0x48,0x07,0x77,0xa0,0x87,0x72,0x90,0x87,0x36,0x28,0x07,0x76,0x48,0x87,0x76,0x68, + 0x03,0x77,0x78,0x07,0x77,0x68,0x03,0x76,0x28,0x87,0x70,0x30,0x07,0x80,0x70,0x87, + 0x77,0x68,0x83,0x74,0x70,0x07,0x73,0x98,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76, + 0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d, + 0xda,0x40,0x1d,0xea,0xa1,0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca, + 0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36, + 0x70,0x87,0x70,0x70,0x87,0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0, + 0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6, + 0x21,0x1d,0xce,0xc1,0x1d,0xca,0x81,0x1c,0xda,0x40,0x1f,0xca,0x41,0x1e,0xde,0x61, + 0x1e,0xda,0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90, + 0x87,0x7a,0x28,0x07,0x80,0x70,0x87,0x77,0x68,0x03,0x7a,0x90,0x87,0x70,0x80,0x07, + 0x78,0x48,0x07,0x77,0x38,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41, + 0x1e,0xea,0xa1,0x1c,0x00,0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e, + 0xe4,0xe1,0x1d,0xe8,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0x40,0x1c,0xea, + 0xc1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c, + 0x00,0x88,0x7a,0x70,0x87,0x79,0x08,0x07,0x73,0x28,0x87,0x36,0x30,0x07,0x78,0x68, + 0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea, + 0x61,0x1e,0xca,0xa1,0x0d,0xe6,0xe1,0x1d,0xcc,0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1, + 0x1d,0xc2,0x81,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x36,0x20,0x42, + 0x01,0x24,0xc0,0x02,0x54,0x00,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00, + 0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00,0x20,0x00,0x00,0x00,0x32,0x22,0x48, + 0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90, + 0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x44,0x33,0x00,0xc3,0x08,0x04, + 0x60,0x89,0x10,0x02,0x18,0x46,0x10,0x80,0x24,0x08,0x33,0x51,0xf3,0x40,0x0f,0xf2, + 0x50,0x0f,0xe3,0x40,0x0f,0x6e,0xd0,0x0e,0xe5,0x40,0x0f,0xe1,0xc0,0x0e,0x7a,0xa0, + 0x07,0xed,0x10,0x0e,0xf4,0x20,0x0f,0xe9,0x80,0x0f,0x28,0x20,0x07,0x49,0x53,0x44, + 0x09,0x93,0x5f,0x49,0xff,0x03,0x44,0x00,0x23,0x21,0xa1,0x94,0x41,0x04,0x43,0x28, + 0x86,0x08,0x23,0x80,0x43,0x68,0x20,0x60,0x8e,0x00,0x0c,0x52,0x60,0xcd,0x11,0x80, + 0xc2,0x20,0x42,0x20,0x0c,0x23,0x10,0xcb,0x08,0x00,0x00,0x00,0x00,0x13,0xa8,0x70, + 0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68, + 0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83,0x0d,0xb7,0x51, + 0x0e,0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78, + 0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76,0xa0,0x07,0x71,0x60, + 0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07, + 0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a, + 0x60,0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30, + 0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76, + 0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0, + 0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06, + 0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6, + 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x90, + 0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06, + 0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72, + 0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0, + 0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07, + 0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75,0x10,0x07,0x72, + 0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0,0x06,0xf6,0x10, + 0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07, + 0x70,0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73, + 0x20,0x07,0x43,0x98,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x2c,0x10,0x00, + 0x00,0x0b,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26, + 0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50,0x04,0x05,0x18,0x50,0x08,0x65,0x50, + 0x80,0x02,0x05,0x51,0x20,0xd4,0x46,0x00,0x88,0x8d,0x25,0x40,0x02,0x00,0x00,0x00, + 0x00,0x79,0x18,0x00,0x00,0x02,0x01,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2, + 0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x32,0x28,0x00,0xb3,0x50, + 0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0x81,0x22,0x2c, + 0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae, + 0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06, + 0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5, + 0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25,0x58,0x90,0x45,0x60, + 0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x82,0x45,0xe0,0x16,0x96, + 0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7, + 0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50,0x12,0x72,0x61,0x69, + 0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d, + 0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04,0x65,0x21,0x19,0x84, + 0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95, + 0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85, + 0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x51,0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d, + 0x19,0xde,0xd7,0x5b,0x1d,0x1d,0x5c,0x1d,0x1d,0x97,0xba,0xb9,0x32,0x39,0x14,0xb6, + 0xb7,0x31,0x37,0x98,0x14,0x46,0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x74,0x79, + 0x70,0x65,0x5f,0x6e,0x61,0x6d,0x65,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x68,0xc8,0x84, + 0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95,0x51,0xa8,0xb3,0x1b,0xc2, + 0x28,0x8f,0x02,0x29,0x91,0x22,0x29,0x93,0x42,0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61, + 0x7b,0x1b,0x73,0x8b,0x49,0x61,0x31,0xf6,0xc6,0xf6,0x26,0x37,0x84,0x51,0x1e,0xc5, + 0x52,0x22,0x45,0x52,0x26,0xe5,0x22,0x13,0x96,0x26,0xe7,0x02,0xf7,0x36,0x97,0x46, + 0x97,0xf6,0xe6,0xc6,0xe5,0x8c,0xed,0x0b,0xea,0x6d,0x2e,0x8d,0x2e,0xed,0xcd,0x6d, + 0x88,0xa2,0x64,0x4a,0xa4,0x48,0xca,0xa4,0x68,0x74,0xc2,0xd2,0xe4,0x5c,0xe0,0xde, + 0xd2,0xdc,0xe8,0xbe,0xe6,0xd2,0xf4,0xca,0x58,0x98,0xb1,0xbd,0x85,0xd1,0x91,0x39, + 0x63,0xfb,0x82,0x7a,0x4b,0x73,0xa3,0x9b,0x4a,0xd3,0x2b,0x1b,0xa2,0x28,0x9c,0x12, + 0x29,0x9d,0x32,0x29,0xde,0x10,0x44,0xa9,0x14,0x4c,0xd9,0x94,0x8f,0x50,0x58,0x9a, + 0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0x57,0x9a,0x1b,0x5c,0x1d,0x1d,0xa5,0xb0, + 0x34,0x39,0x17,0xb6,0xb7,0xb1,0x30,0xba,0xb4,0x37,0xb7,0xaf,0x34,0x37,0xb2,0x32, + 0x3c,0x7a,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64,0x28,0x5f,0x5f,0x61,0x69, + 0x72,0x5f,0x70,0x6c,0x61,0x63,0x65,0x68,0x6f,0x6c,0x64,0x65,0x72,0x5f,0x5f,0x29, + 0x44,0xe0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x86,0x50,0x8b,0xa0,0x84,0x81,0x22, + 0x06,0x8b,0xb0,0x04,0xca,0x18,0x28,0x91,0x22,0x29,0x93,0x42,0x06,0x34,0xcc,0xd8, + 0xde,0xc2,0xe8,0x64,0x98,0xd0,0x95,0xe1,0x8d,0xbd,0xbd,0xc9,0x91,0xc1,0x0c,0xa1, + 0x96,0x40,0x09,0x03,0x45,0x0c,0x96,0x60,0x09,0x94,0x31,0x50,0x22,0xc5,0x0c,0x94, + 0x49,0x39,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8,0x65,0x50,0xc2,0x40, + 0x11,0x83,0x65,0x58,0x02,0x65,0x0c,0x94,0x48,0x91,0x94,0x49,0x49,0x03,0x16,0x70, + 0x73,0x69,0x7a,0x65,0x43,0xa8,0xc5,0x50,0xc2,0x40,0x11,0x83,0xc5,0x58,0x02,0x65, + 0x0c,0x94,0x48,0xe9,0x94,0x49,0x59,0x03,0x2a,0x61,0x69,0x72,0x2e,0x62,0x75,0x66, + 0x66,0x65,0x72,0x7c,0xc2,0xd2,0xe4,0x5c,0xc4,0xea,0xcc,0xcc,0xca,0xe4,0xbe,0xe6, + 0xd2,0xf4,0xca,0x88,0x84,0xa5,0xc9,0xb9,0xc8,0x95,0x85,0x91,0x91,0x0a,0x4b,0x93, + 0x73,0x99,0xa3,0x93,0xab,0x1b,0xa3,0xfb,0xa2,0xcb,0x83,0x2b,0xfb,0x4a,0x73,0x33, + 0x7b,0x23,0x62,0xc6,0xf6,0x16,0x46,0x47,0x83,0x47,0xc3,0xa1,0xcd,0x0e,0x8e,0x02, + 0x5d,0xdb,0x10,0x6a,0x11,0x16,0x62,0x11,0x94,0x38,0x50,0xe4,0x60,0x21,0x16,0x62, + 0x11,0x94,0x38,0x50,0xe6,0x80,0x51,0x58,0x9a,0x9c,0x4b,0x98,0xdc,0xd9,0x17,0x5d, + 0x1e,0x5c,0xd9,0xd7,0x5c,0x9a,0x5e,0x19,0xaf,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3, + 0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x30,0xb6,0xb4,0x33,0xb7,0xaf,0xb9,0x34,0xbd,0x32, + 0x26,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x1c,0xbe,0x62,0x72,0x86,0x90, + 0xc1,0x52,0x28,0x6d,0xa0,0xb8,0xc1,0x72,0x28,0x62,0xb0,0x08,0x4b,0xa0,0xbc,0x81, + 0x02,0x07,0x0a,0x1d,0x28,0x75,0xb0,0x1c,0x8a,0x1d,0x2c,0x89,0x12,0x29,0x77,0xa0, + 0x4c,0x0a,0x1e,0x0c,0x51,0x94,0x32,0x50,0xd0,0x40,0x51,0x03,0x85,0x0d,0x94,0x3c, + 0x18,0x62,0x24,0x80,0x02,0x06,0x8a,0x1e,0xf0,0x79,0x6b,0x73,0x4b,0x83,0x7b,0xa3, + 0x2b,0x73,0xa3,0x03,0x19,0x43,0x0b,0x93,0xe3,0x33,0x95,0xd6,0x06,0xc7,0x56,0x06, + 0x32,0xb4,0xb2,0x02,0x42,0x25,0x14,0x14,0x34,0x44,0x50,0xfa,0x60,0x88,0xa1,0xf0, + 0x81,0xe2,0x07,0x8d,0x32,0xc4,0x50,0xfe,0x40,0xf9,0x83,0x46,0x19,0x11,0xb1,0x03, + 0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3b,0xbc,0x03,0x39,0xd4,0x03,0x3b,0x94,0x83,0x1b, + 0x98,0x03,0x3b,0x84,0xc3,0x39,0xcc,0xc3,0x14,0x21,0x18,0x46,0x28,0xec,0xc0,0x0e, + 0xf6,0xd0,0x0e,0x6e,0x90,0x0e,0xe4,0x50,0x0e,0xee,0x40,0x0f,0x53,0x82,0x62,0xc4, + 0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f,0xf3,0x90,0x0e,0xef,0xe0, + 0x0e,0x53,0x02,0x63,0x04,0x15,0x0e,0xe9,0x20,0x0f,0x6e,0xc0,0x0e,0xe1,0xe0,0x0e, + 0xe7,0x50,0x0f,0xe1,0x70,0x0e,0xe5,0xf0,0x0b,0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9, + 0xf0,0x0e,0xee,0x30,0x25,0x40,0x46,0x4c,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xe3,0xf0, + 0x0e,0xed,0x00,0x0f,0xe9,0xc0,0x0e,0xe5,0xf0,0x0b,0xef,0x00,0x0f,0xf4,0x90,0x0e, + 0xef,0xe0,0x0e,0xf3,0x30,0x65,0x50,0x18,0x67,0x84,0x12,0x0e,0xe9,0x20,0x0f,0x6e, + 0x60,0x0f,0xe5,0x20,0x0f,0xf4,0x50,0x0e,0xf8,0x30,0x25,0xd8,0x03,0x00,0x00,0x00, + 0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c, + 0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07, + 0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42, + 0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83, + 0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07, + 0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70, + 0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3, + 0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2, + 0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc, + 0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68, + 0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07, + 0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72, + 0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec, + 0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc, + 0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8, + 0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03, + 0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c, + 0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74, + 0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4, + 0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc, + 0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1, + 0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50, + 0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51, + 0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21, + 0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06, + 0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c, + 0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77, + 0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec, + 0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8, + 0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4, + 0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43, + 0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30, + 0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x3e,0x00,0x00,0x00,0x13,0x04,0x41, + 0x2c,0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0xf4,0xc6,0x22,0x86,0x61,0x18,0xc6, + 0x22,0x04,0x41,0x10,0xc6,0x22,0x82,0x20,0x08,0xa8,0x95,0x40,0x19,0x14,0x01,0xbd, + 0x11,0x00,0x1a,0x33,0x00,0x24,0x66,0x00,0x28,0xcc,0x00,0x00,0x00,0xe3,0x15,0x4b, + 0x94,0x65,0x11,0x05,0x65,0x90,0x21,0x1a,0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x55, + 0xd7,0x2d,0x14,0x94,0x41,0x86,0xea,0x70,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1, + 0x0a,0x4a,0x13,0x03,0x31,0x70,0x28,0x28,0x83,0x0c,0x1a,0x43,0x99,0x10,0xc8,0xc7, + 0x8a,0x00,0x3e,0xe3,0x15,0xd9,0x77,0x06,0x67,0x40,0x51,0x50,0x06,0x19,0xbe,0x48, + 0x33,0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6,0x2b,0x3c,0x32,0x68,0x03,0x36,0x20,0x03, + 0x0a,0xca,0x20,0xc3,0x18,0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b,0xc4,0x00,0x0d,0xe2, + 0x00,0x0e,0x3c,0x0a,0xca,0x20,0xc3,0x19,0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a, + 0xf8,0x8c,0x57,0x9c,0x41,0x1b,0xd8,0x41,0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e, + 0xb3,0x0d,0x61,0x10,0x00,0xb3,0x0d,0x41,0x1b,0x04,0xb3,0x0d,0xc1,0x23,0xcc,0x36, + 0x04,0x6e,0x30,0x64,0x10,0x10,0x03,0x00,0x00,0x09,0x00,0x00,0x00,0x5b,0x86,0x20, + 0x00,0x85,0x2d,0x43,0x11,0x80,0xc2,0x96,0x41,0x09,0x40,0x61,0xcb,0xf0,0x04,0xa0, + 0xb0,0x65,0xa0,0x02,0x50,0xd8,0x32,0x60,0x01,0x28,0x6c,0x19,0xba,0x00,0x14,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sfons_fs_bytecode_metal_ios[2841] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x19,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x40,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0x7c,0x59,0x61,0x85,0x8c,0x08,0x62, + 0xf6,0xd0,0x45,0x0a,0x3a,0xcb,0xa8,0xa5,0x3c,0x2d,0x6c,0x6b,0x9c,0x3d,0xf0,0xf5, + 0xc4,0xdc,0xb5,0x90,0xdc,0xee,0x5a,0x9f,0x63,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44, + 0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0x28,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x87,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32, + 0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45, + 0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44, + 0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8, + 0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00, + 0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07,0x7a,0x60,0x87,0x7c, + 0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72,0x68,0x03,0x72,0x48, + 0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90,0x07,0x7a,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc,0x21,0x1c,0xd8,0x61, + 0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81,0x1d,0xca,0xa1,0x0d, + 0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d,0xd8,0x61,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87,0x79,0x98,0x87,0x36, + 0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36,0x30,0x07,0x78,0x68, + 0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0xc2,0x1d,0xde, + 0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0,0xe1,0x1d,0xd2,0xc1, + 0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1,0x1d,0x00,0x7a,0x90, + 0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90,0x87,0x70,0x68,0x87, + 0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07,0x79,0x68,0x83,0x72, + 0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36,0x60,0x87,0x72,0x08, + 0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30,0x87,0x79,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1, + 0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01,0x1e,0xda,0x80,0x1e, + 0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07, + 0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87,0x36,0x30,0x07,0x78, + 0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20, + 0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c,0xc8,0xa1,0x0d,0xf4, + 0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda,0xa0,0x1d,0xc2,0x81, + 0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0xa0, + 0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68,0x83,0x76,0x08,0x07, + 0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6,0x81,0x1e,0xc2,0x61, + 0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d, + 0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e,0xda,0x60,0x1e,0xd2, + 0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87,0x70,0x30,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e, + 0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e,0xde,0xc1,0x1c,0xe8, + 0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87,0x70,0x60,0x87,0x79, + 0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81,0x00,0x16,0xa0,0xda, + 0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5,0x06,0xa3,0x08,0x80, + 0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00,0x00,0x03,0x00,0x00, + 0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00,0x00,0x89,0x20,0x00, + 0x00,0x1e,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4, + 0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4, + 0x4c,0x10,0x4c,0x33,0x00,0xc3,0x08,0x04,0x60,0x83,0x70,0x94,0x34,0x45,0x94,0x30, + 0xf9,0xff,0x44,0x5c,0x13,0x15,0x11,0xbf,0x3d,0xfc,0xd3,0x18,0x01,0x30,0x88,0x30, + 0x04,0x17,0x49,0x53,0x44,0x09,0x93,0xff,0x4b,0x00,0xf3,0x2c,0x44,0xf4,0x4f,0x63, + 0x04,0xc0,0x20,0x42,0x21,0x94,0x42,0x84,0x40,0x0c,0x9d,0x61,0x04,0x01,0x98,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0x88,0x49,0x8a,0x29,0x40,0x6d, + 0x20,0x20,0x05,0xd6,0x30,0x02,0xb1,0x8c,0x00,0x00,0x00,0x00,0x00,0x13,0xa8,0x70, + 0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68, + 0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83,0x0d,0xb7,0x51, + 0x0e,0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78, + 0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76,0xa0,0x07,0x71,0x60, + 0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07, + 0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a, + 0x60,0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30, + 0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76, + 0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0, + 0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06, + 0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6, + 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x90, + 0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06, + 0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72, + 0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0, + 0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07, + 0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75,0x10,0x07,0x72, + 0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0,0x06,0xf6,0x10, + 0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07, + 0x70,0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73, + 0x20,0x07,0x43,0x18,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x8c,0x03, + 0x04,0x80,0x00,0x00,0x00,0x00,0x00,0x40,0x16,0x08,0x00,0x00,0x00,0x08,0x00,0x00, + 0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43, + 0x5a,0x25,0x30,0x02,0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0x01,0x12,0x00, + 0x00,0x79,0x18,0x00,0x00,0xb7,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2, + 0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x42,0x3c,0x00,0x84,0x50, + 0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0xc2,0x23,0x2c, + 0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae, + 0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06, + 0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5, + 0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0xf0,0x10,0x43,0x8c,0x45,0x58,0x8c,0x65,0x60, + 0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x79,0x8e,0x45,0x58,0x84,0x65,0xe0,0x16,0x96, + 0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7, + 0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78,0x12,0x72,0x61,0x69, + 0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d, + 0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x84,0x67,0x21,0x19,0x84, + 0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95, + 0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85, + 0x89,0xb1,0x95,0x0d,0x11,0x9e,0x86,0x51,0x58,0x9a,0x9c,0x8b,0x5c,0x99,0x1b,0x59, + 0x99,0xdc,0x17,0x5d,0x98,0xdc,0x59,0x19,0x1d,0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9, + 0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0x2f,0xb7,0xb0,0xb6,0x32,0x1a,0x66,0x6c,0x6f,0x61, + 0x74,0x34,0x64,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xdc,0xc2,0xda,0xca,0xa8, + 0x98,0xc9,0x85,0x9d,0x7d,0x8d,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x9e,0x67,0x19,0x1e, + 0xe8,0x89,0x1e,0xe9,0x99,0x86,0x08,0x0f,0x45,0x29,0x2c,0x4d,0xce,0xc5,0x4c,0x2e, + 0xec,0xac,0xad,0xcc,0x8d,0xee,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x4b,0xdd,0x5c,0x99, + 0x1c,0x0a,0xdb,0xdb,0x98,0x1b,0x4c,0x0a,0x95,0xb0,0x34,0x39,0x97,0xb1,0x32,0x37, + 0xba,0x32,0x39,0x3e,0x61,0x69,0x72,0x2e,0x70,0x65,0x72,0x73,0x70,0x65,0x63,0x74, + 0x69,0x76,0x65,0x14,0xea,0xec,0x86,0x48,0xcb,0xf0,0x58,0xcf,0xf5,0x60,0x4f,0xf6, + 0x40,0x4f,0xf4,0x48,0x8f,0xc6,0xa5,0x6e,0xae,0x4c,0x0e,0x85,0xed,0x6d,0xcc,0x2d, + 0x26,0x85,0xc5,0xd8,0x1b,0xdb,0x9b,0xdc,0x10,0x69,0x11,0x1e,0xeb,0xe1,0x1e,0xec, + 0xc9,0x1e,0xe8,0x89,0x1e,0xe9,0xe9,0xb8,0x84,0xa5,0xc9,0xb9,0xd0,0x95,0xe1,0xd1, + 0xd5,0xc9,0x95,0x51,0x0a,0x4b,0x93,0x73,0x61,0x7b,0x1b,0x0b,0xa3,0x4b,0x7b,0x73, + 0xfb,0x4a,0x73,0x23,0x2b,0xc3,0xa3,0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7, + 0x56,0x46,0x8c,0xae,0x0c,0x8f,0xae,0x4e,0xae,0x4c,0x86,0x8c,0xc7,0x8c,0xed,0x2d, + 0x8c,0x8e,0x05,0x64,0x2e,0xac,0x0d,0x8e,0xad,0xcc,0x87,0x03,0x5d,0x19,0xde,0x10, + 0x6a,0x21,0x9e,0xef,0x01,0x83,0x65,0x58,0x84,0x27,0x0c,0x1e,0xe8,0x11,0x83,0x47, + 0x7a,0xc6,0x80,0x4b,0x58,0x9a,0x9c,0xcb,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x8f, + 0xb9,0xb0,0x36,0x38,0xb6,0x32,0x39,0x0e,0x73,0x6d,0x70,0x43,0xa4,0xe5,0x78,0xca, + 0xe0,0x01,0x83,0x65,0x58,0x84,0x07,0x7a,0xcc,0xe0,0x91,0x9e,0x33,0x18,0x82,0x3c, + 0xdb,0xe3,0x3d,0x64,0xf0,0xa0,0xc1,0x10,0x03,0x01,0x9e,0xea,0x49,0x83,0x11,0x11, + 0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0xb4,0xc3,0x3b,0x90,0x43,0x3d,0xb0,0x43,0x39, + 0xb8,0x81,0x39,0xb0,0x43,0x38,0x9c,0xc3,0x3c,0x4c,0x11,0x82,0x61,0x84,0xc2,0x0e, + 0xec,0x60,0x0f,0xed,0xe0,0x06,0xe9,0x40,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25,0x28, + 0x46,0x2c,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9,0xf0, + 0x0e,0xee,0x30,0x25,0x30,0x46,0x50,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xec,0x10,0x0e, + 0xee,0x70,0x0e,0xf5,0x10,0x0e,0xe7,0x50,0x0e,0xbf,0x60,0x0f,0xe5,0x20,0x0f,0xf3, + 0x90,0x0e,0xef,0xe0,0x0e,0x53,0x02,0x64,0xc4,0x14,0x0e,0xe9,0x20,0x0f,0x6e,0x30, + 0x0e,0xef,0xd0,0x0e,0xf0,0x90,0x0e,0xec,0x50,0x0e,0xbf,0xf0,0x0e,0xf0,0x40,0x0f, + 0xe9,0xf0,0x0e,0xee,0x30,0x0f,0x53,0x06,0x85,0x71,0x46,0x30,0xe1,0x90,0x0e,0xf2, + 0xe0,0x06,0xe6,0x20,0x0f,0xe1,0x70,0x0e,0xed,0x50,0x0e,0xee,0x40,0x0f,0x53,0x02, + 0x35,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80, + 0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80, + 0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80, + 0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88, + 0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78, + 0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03, + 0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f, + 0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c, + 0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39, + 0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b, + 0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60, + 0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87, + 0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e, + 0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c, + 0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6, + 0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94, + 0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03, + 0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07, + 0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f, + 0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33, + 0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc, + 0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c, + 0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78, + 0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca, + 0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21, + 0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43, + 0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87, + 0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c, + 0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e, + 0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c, + 0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c, + 0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x08,0x00,0x00, + 0x00,0x16,0xb0,0x01,0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45, + 0xc4,0x6f,0x0f,0x7e,0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20,0x0d,0x0d,0x00,0x00, + 0x00,0x61,0x20,0x00,0x00,0x16,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00, + 0x00,0x0b,0x00,0x00,0x00,0x14,0xc7,0x22,0x80,0x40,0x20,0x88,0x8d,0x00,0x8c,0x25, + 0x00,0x01,0xa9,0x11,0x80,0x1a,0x20,0x31,0x03,0x40,0x61,0x0e,0xe2,0xba,0xae,0x6a, + 0x06,0x80,0xc0,0x0c,0xc0,0x08,0xc0,0x18,0x01,0x08,0x82,0x20,0xfe,0x01,0x00,0x00, + 0x00,0x83,0x0c,0x0f,0x91,0x8c,0x18,0x28,0x42,0x80,0x39,0x4d,0x80,0x2c,0xc9,0x30, + 0xc8,0x70,0x04,0x8d,0x05,0x91,0x7c,0x66,0x1b,0x94,0x00,0xc8,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sfons_vs_source_metal_sim[756] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20,0x6d,0x76,0x70,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20,0x74,0x6d,0x3b,0x0a,0x7d,0x3b, + 0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f, + 0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, + 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, + 0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x70, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74,0x53,0x69,0x7a, + 0x65,0x20,0x5b,0x5b,0x70,0x6f,0x69,0x6e,0x74,0x5f,0x73,0x69,0x7a,0x65,0x5d,0x5d, + 0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69, + 0x6e,0x30,0x5f,0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x61,0x74, + 0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72, + 0x64,0x30,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x31, + 0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75, + 0x74,0x65,0x28,0x32,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x20,0x70,0x73,0x69,0x7a,0x65,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69, + 0x62,0x75,0x74,0x65,0x28,0x33,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x76, + 0x65,0x72,0x74,0x65,0x78,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20, + 0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69, + 0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20, + 0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61, + 0x6d,0x73,0x26,0x20,0x5f,0x31,0x39,0x20,0x5b,0x5b,0x62,0x75,0x66,0x66,0x65,0x72, + 0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x6d,0x61,0x69, + 0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x5f,0x31,0x39,0x2e,0x6d,0x76,0x70,0x20,0x2a, + 0x20,0x69,0x6e,0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74,0x53,0x69, + 0x7a,0x65,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x70,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75,0x76,0x20,0x3d,0x20,0x5f,0x31,0x39,0x2e, + 0x74,0x6d,0x20,0x2a,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x2e,0x74, + 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31, + 0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x63,0x6f,0x6c, + 0x6f,0x72,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a, + 0x7d,0x0a,0x0a,0x00, +}; +static const uint8_t _sfons_fs_source_metal_sim[464] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d, + 0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f, + 0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, + 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x66,0x72,0x61,0x67,0x6d,0x65, + 0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69, + 0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b, + 0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x74,0x65, + 0x78,0x20,0x5b,0x5b,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x30,0x29,0x5d,0x5d, + 0x2c,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x73,0x6d,0x70,0x20,0x5b,0x5b, + 0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a, + 0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75, + 0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e, + 0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x28,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e, + 0x30,0x2c,0x20,0x74,0x65,0x78,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x73,0x6d, + 0x70,0x2c,0x20,0x69,0x6e,0x2e,0x75,0x76,0x2e,0x78,0x79,0x29,0x2e,0x78,0x29,0x20, + 0x2a,0x20,0x69,0x6e,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, + +}; +#elif defined(SOKOL_D3D11) +static const uint8_t _sfons_vs_bytecode_hlsl4[1032] = { + 0x44,0x58,0x42,0x43,0x74,0x7f,0x01,0xd9,0xf4,0xd5,0xed,0x1d,0x74,0xc1,0x30,0x27, + 0xd8,0xe9,0x9d,0x50,0x01,0x00,0x00,0x00,0x08,0x04,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x90,0x01,0x00,0x00,0x00,0x02,0x00,0x00, + 0x8c,0x03,0x00,0x00,0x52,0x44,0x45,0x46,0xd8,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x48,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xfe,0xff, + 0x10,0x81,0x00,0x00,0xaf,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0xab,0xab,0x3c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xa8,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0x31,0x39,0x5f, + 0x6d,0x76,0x70,0x00,0x02,0x00,0x03,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x31,0x39,0x5f,0x74,0x6d,0x00,0x4d,0x69,0x63,0x72,0x6f, + 0x73,0x6f,0x66,0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c,0x53,0x4c,0x20,0x53,0x68, + 0x61,0x64,0x65,0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x72,0x20,0x31,0x30, + 0x2e,0x31,0x00,0xab,0x49,0x53,0x47,0x4e,0x74,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x68,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x03,0x03,0x00,0x00,0x68,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x68,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab, + 0x4f,0x53,0x47,0x4e,0x68,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0xab,0xab,0xab, + 0x53,0x48,0x44,0x52,0x84,0x01,0x00,0x00,0x40,0x00,0x01,0x00,0x61,0x00,0x00,0x00, + 0x59,0x00,0x00,0x04,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x5f,0x00,0x00,0x03, + 0x32,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00, + 0x02,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00,0x67,0x00,0x00,0x04, + 0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x68,0x00,0x00,0x02, + 0x01,0x00,0x00,0x00,0x38,0x00,0x00,0x08,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x56,0x15,0x10,0x00,0x01,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x06,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x36,0x00,0x00,0x05, + 0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x02,0x00,0x00,0x00, + 0x38,0x00,0x00,0x08,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x56,0x15,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00, + 0x00,0x00,0x00,0x00,0xa6,0x1a,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x32,0x00,0x00,0x0a,0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0xf6,0x1f,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54, + 0x74,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sfons_fs_bytecode_hlsl4[628] = { + 0x44,0x58,0x42,0x43,0xb6,0x66,0xf0,0xfc,0x09,0x54,0x2a,0x35,0x84,0x1d,0x27,0xd2, + 0xff,0xb3,0x2e,0xdb,0x01,0x00,0x00,0x00,0x74,0x02,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x48,0x01,0x00,0x00, + 0xf8,0x01,0x00,0x00,0x52,0x44,0x45,0x46,0x8c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xff,0xff, + 0x10,0x81,0x00,0x00,0x64,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x73,0x6d,0x70,0x00,0x74,0x65,0x78,0x00, + 0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f,0x66,0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c, + 0x53,0x4c,0x20,0x53,0x68,0x61,0x64,0x65,0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c, + 0x65,0x72,0x20,0x31,0x30,0x2e,0x31,0x00,0x49,0x53,0x47,0x4e,0x44,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x03,0x00,0x00, + 0x38,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x2c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x53,0x56,0x5f,0x54, + 0x61,0x72,0x67,0x65,0x74,0x00,0xab,0xab,0x53,0x48,0x44,0x52,0xa8,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x5a,0x00,0x00,0x03,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x58,0x18,0x00,0x04,0x00,0x70,0x10,0x00,0x00,0x00,0x00,0x00, + 0x55,0x55,0x00,0x00,0x62,0x10,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x62,0x10,0x00,0x03,0xf2,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x65,0x00,0x00,0x03, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00, + 0x45,0x00,0x00,0x09,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x96,0x73,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x05,0x12,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x01,0x40,0x00,0x00,0x00,0x00,0x80,0x3f,0x38,0x00,0x00,0x07,0xf2,0x20,0x10,0x00, + 0x00,0x00,0x00,0x00,0x06,0x0c,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x1e,0x10,0x00, + 0x01,0x00,0x00,0x00,0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, +}; +#elif defined(SOKOL_WGPU) +static const uint8_t _sfons_vs_source_wgsl[1162] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a, + 0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20, + 0x20,0x6d,0x76,0x70,0x20,0x3a,0x20,0x6d,0x61,0x74,0x34,0x78,0x34,0x66,0x2c,0x0a, + 0x20,0x20,0x2f,0x2a,0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x36,0x34,0x29, + 0x20,0x2a,0x2f,0x0a,0x20,0x20,0x74,0x6d,0x20,0x3a,0x20,0x6d,0x61,0x74,0x34,0x78, + 0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x30,0x29, + 0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x30,0x29,0x20,0x76,0x61,0x72, + 0x3c,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x3e,0x20,0x78,0x5f,0x31,0x39,0x20,0x3a, + 0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x3b,0x0a,0x0a,0x76,0x61,0x72, + 0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x70,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x76, + 0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72, + 0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61, + 0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65, + 0x3e,0x20,0x70,0x73,0x69,0x7a,0x65,0x20,0x3a,0x20,0x66,0x33,0x32,0x3b,0x0a,0x0a, + 0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x67,0x6c,0x5f, + 0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b, + 0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x32,0x20,0x3a,0x20,0x6d,0x61, + 0x74,0x34,0x78,0x34,0x66,0x20,0x3d,0x20,0x78,0x5f,0x31,0x39,0x2e,0x6d,0x76,0x70, + 0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x35,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f, + 0x31,0x3b,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x28,0x78,0x5f,0x32,0x32,0x20,0x2a,0x20,0x78,0x5f,0x32,0x35,0x29, + 0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x32,0x20,0x3a,0x20,0x6d, + 0x61,0x74,0x34,0x78,0x34,0x66,0x20,0x3d,0x20,0x78,0x5f,0x31,0x39,0x2e,0x74,0x6d, + 0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x36,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30, + 0x3b,0x0a,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x28,0x78,0x5f,0x33,0x32,0x20,0x2a, + 0x20,0x76,0x65,0x63,0x34,0x66,0x28,0x78,0x5f,0x33,0x36,0x2e,0x78,0x2c,0x20,0x78, + 0x5f,0x33,0x36,0x2e,0x79,0x2c,0x20,0x30,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30, + 0x66,0x29,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x35,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30, + 0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x78,0x5f,0x34,0x35, + 0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73, + 0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b, + 0x0a,0x20,0x20,0x40,0x62,0x75,0x69,0x6c,0x74,0x69,0x6e,0x28,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x29,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x20,0x20,0x40, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x0a,0x20,0x20,0x75,0x76, + 0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c, + 0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x0a,0x20,0x20,0x63,0x6f,0x6c, + 0x6f,0x72,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a, + 0x0a,0x40,0x76,0x65,0x72,0x74,0x65,0x78,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e, + 0x28,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x70,0x6f, + 0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f, + 0x6e,0x28,0x31,0x29,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c, + 0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x32,0x29,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c, + 0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x33,0x29,0x20,0x70,0x73, + 0x69,0x7a,0x65,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x66,0x33,0x32,0x29, + 0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20, + 0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x20,0x3d,0x20,0x70,0x6f, + 0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a, + 0x20,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x3d,0x20,0x74,0x65, + 0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x70,0x73,0x69,0x7a,0x65,0x20, + 0x3d,0x20,0x70,0x73,0x69,0x7a,0x65,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20, + 0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74, + 0x75,0x72,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x67,0x6c,0x5f, + 0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x2c,0x20,0x75,0x76,0x2c,0x20,0x63,0x6f, + 0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const uint8_t _sfons_fs_source_wgsl[674] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75, + 0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x34,0x38, + 0x29,0x20,0x76,0x61,0x72,0x20,0x74,0x65,0x78,0x20,0x3a,0x20,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x5f,0x32,0x64,0x3c,0x66,0x33,0x32,0x3e,0x3b,0x0a,0x0a,0x40,0x67, + 0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67, + 0x28,0x36,0x34,0x29,0x20,0x76,0x61,0x72,0x20,0x73,0x6d,0x70,0x20,0x3a,0x20,0x73, + 0x61,0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a, + 0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20, + 0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x36,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x2c,0x20,0x73, + 0x6d,0x70,0x2c,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x78,0x5f,0x32,0x34,0x2e,0x78, + 0x2c,0x20,0x78,0x5f,0x32,0x34,0x2e,0x79,0x29,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65, + 0x74,0x20,0x78,0x5f,0x33,0x32,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x28,0x76,0x65,0x63,0x34,0x66,0x28,0x31,0x2e, + 0x30,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x2c,0x20, + 0x78,0x5f,0x32,0x36,0x2e,0x78,0x29,0x20,0x2a,0x20,0x78,0x5f,0x33,0x32,0x29,0x3b, + 0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73,0x74, + 0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a, + 0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x0a,0x20, + 0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x31,0x20,0x3a,0x20, + 0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x66,0x72,0x61,0x67,0x6d, + 0x65,0x6e,0x74,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x40,0x6c,0x6f,0x63, + 0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x75,0x76,0x5f,0x70,0x61,0x72,0x61, + 0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x29,0x20,0x2d,0x3e,0x20, + 0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x75,0x76,0x20, + 0x3d,0x20,0x75,0x76,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x63,0x6f, + 0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x70,0x61,0x72,0x61, + 0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x3b,0x0a,0x20, + 0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74, + 0x28,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a, + 0x0a,0x00, +}; +#elif defined(SOKOL_DUMMY_BACKEND) +static const char* _sfons_vs_source_dummy = ""; +static const char* _sfons_fs_source_dummy = ""; +#else +#error "Please define one of SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" +#endif + +typedef struct _sfons_t { + sfons_desc_t desc; + sg_shader shd; + sgl_pipeline pip; + sg_image img; + sg_sampler smp; + int cur_width, cur_height; + bool img_dirty; +} _sfons_t; + +static void _sfons_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +static void* _sfons_malloc(const sfons_allocator_t* allocator, size_t size) { + SOKOL_ASSERT(allocator && (size > 0)); + void* ptr; + if (allocator->alloc_fn) { + ptr = allocator->alloc_fn(size, allocator->user_data); + } else { + ptr = malloc(size); + } + SOKOL_ASSERT(ptr); + return ptr; +} + +static void* _sfons_malloc_clear(const sfons_allocator_t* allocator, size_t size) { + void* ptr = _sfons_malloc(allocator, size); + _sfons_clear(ptr, size); + return ptr; +} + +static void _sfons_free(const sfons_allocator_t* allocator, void* ptr) { + SOKOL_ASSERT(allocator); + if (allocator->free_fn) { + allocator->free_fn(ptr, allocator->user_data); + } else { + free(ptr); + } +} + +static int _sfons_render_create(void* user_ptr, int width, int height) { + SOKOL_ASSERT(user_ptr && (width > 8) && (height > 8)); + _sfons_t* sfons = (_sfons_t*) user_ptr; + + // sokol-gl compatible shader which treats RED channel as alpha + if (sfons->shd.id == SG_INVALID_ID) { + sg_shader_desc shd_desc; + _sfons_clear(&shd_desc, sizeof(shd_desc)); + shd_desc.attrs[0].name = "position"; + shd_desc.attrs[1].name = "texcoord0"; + shd_desc.attrs[2].name = "color0"; + shd_desc.attrs[3].name = "psize"; + shd_desc.attrs[0].sem_name = "TEXCOORD"; + shd_desc.attrs[0].sem_index = 0; + shd_desc.attrs[1].sem_name = "TEXCOORD"; + shd_desc.attrs[1].sem_index = 1; + shd_desc.attrs[2].sem_name = "TEXCOORD"; + shd_desc.attrs[2].sem_index = 2; + shd_desc.attrs[3].sem_name = "TEXCOORD"; + shd_desc.attrs[3].sem_index = 3; + sg_shader_uniform_block_desc* ub = &shd_desc.vs.uniform_blocks[0]; + ub->size = 128; + ub->uniforms[0].name = "vs_params"; + ub->uniforms[0].type = SG_UNIFORMTYPE_FLOAT4; + ub->uniforms[0].array_count = 8; + shd_desc.fs.images[0].used = true; + shd_desc.fs.images[0].image_type = SG_IMAGETYPE_2D; + shd_desc.fs.images[0].sample_type = SG_IMAGESAMPLETYPE_FLOAT; + shd_desc.fs.samplers[0].used = true; + shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING; + shd_desc.fs.image_sampler_pairs[0].used = true; + shd_desc.fs.image_sampler_pairs[0].glsl_name = "tex_smp"; + shd_desc.fs.image_sampler_pairs[0].image_slot = 0; + shd_desc.fs.image_sampler_pairs[0].sampler_slot = 0; + shd_desc.label = "sokol-fontstash-shader"; + #if defined(SOKOL_GLCORE) + shd_desc.vs.source = (const char*)_sfons_vs_source_glsl410; + shd_desc.fs.source = (const char*)_sfons_fs_source_glsl410; + #elif defined(SOKOL_GLES3) + shd_desc.vs.source = (const char*)_sfons_vs_source_glsl300es; + shd_desc.fs.source = (const char*)_sfons_fs_source_glsl300es; + #elif defined(SOKOL_METAL) + shd_desc.vs.entry = "main0"; + shd_desc.fs.entry = "main0"; + switch (sg_query_backend()) { + case SG_BACKEND_METAL_MACOS: + shd_desc.vs.bytecode = SG_RANGE(_sfons_vs_bytecode_metal_macos); + shd_desc.fs.bytecode = SG_RANGE(_sfons_fs_bytecode_metal_macos); + break; + case SG_BACKEND_METAL_IOS: + shd_desc.vs.bytecode = SG_RANGE(_sfons_vs_bytecode_metal_ios); + shd_desc.fs.bytecode = SG_RANGE(_sfons_fs_bytecode_metal_ios); + break; + default: + shd_desc.vs.source = (const char*)_sfons_vs_source_metal_sim; + shd_desc.fs.source = (const char*)_sfons_fs_source_metal_sim; + break; + } + #elif defined(SOKOL_D3D11) + shd_desc.vs.bytecode = SG_RANGE(_sfons_vs_bytecode_hlsl4); + shd_desc.fs.bytecode = SG_RANGE(_sfons_fs_bytecode_hlsl4); + #elif defined(SOKOL_WGPU) + shd_desc.vs.source = (const char*)_sfons_vs_source_wgsl; + shd_desc.fs.source = (const char*)_sfons_fs_source_wgsl; + #else + shd_desc.vs.source = _sfons_vs_source_dummy; + shd_desc.fs.source = _sfons_fs_source_dummy; + #endif + shd_desc.label = "sfons-shader"; + sfons->shd = sg_make_shader(&shd_desc); + } + + // sokol-gl pipeline object + if (sfons->pip.id == SG_INVALID_ID) { + sg_pipeline_desc pip_desc; + _sfons_clear(&pip_desc, sizeof(pip_desc)); + pip_desc.shader = sfons->shd; + pip_desc.colors[0].blend.enabled = true; + pip_desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA; + pip_desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + sfons->pip = sgl_make_pipeline(&pip_desc); + } + + // a sampler object + if (sfons->smp.id == SG_INVALID_ID) { + sg_sampler_desc smp_desc; + _sfons_clear(&smp_desc, sizeof(smp_desc)); + smp_desc.min_filter = SG_FILTER_LINEAR; + smp_desc.mag_filter = SG_FILTER_LINEAR; + smp_desc.mipmap_filter = SG_FILTER_NONE; + sfons->smp = sg_make_sampler(&smp_desc); + } + + // create or re-create font atlas texture + if (sfons->img.id != SG_INVALID_ID) { + sg_destroy_image(sfons->img); + sfons->img.id = SG_INVALID_ID; + } + sfons->cur_width = width; + sfons->cur_height = height; + + SOKOL_ASSERT(sfons->img.id == SG_INVALID_ID); + sg_image_desc img_desc; + _sfons_clear(&img_desc, sizeof(img_desc)); + img_desc.width = sfons->cur_width; + img_desc.height = sfons->cur_height; + img_desc.usage = SG_USAGE_DYNAMIC; + img_desc.pixel_format = SG_PIXELFORMAT_R8; + sfons->img = sg_make_image(&img_desc); + return 1; +} + +static int _sfons_render_resize(void* user_ptr, int width, int height) { + return _sfons_render_create(user_ptr, width, height); +} + +static void _sfons_render_update(void* user_ptr, int* rect, const unsigned char* data) { + SOKOL_ASSERT(user_ptr && rect && data); + _SOKOL_UNUSED(rect); + _SOKOL_UNUSED(data); + _sfons_t* sfons = (_sfons_t*) user_ptr; + sfons->img_dirty = true; +} + +static void _sfons_render_draw(void* user_ptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts) { + SOKOL_ASSERT(user_ptr && verts && tcoords && colors && (nverts > 0)); + _sfons_t* sfons = (_sfons_t*) user_ptr; + sgl_enable_texture(); + sgl_texture(sfons->img, sfons->smp); + sgl_push_pipeline(); + sgl_load_pipeline(sfons->pip); + sgl_begin_triangles(); + for (int i = 0; i < nverts; i++) { + sgl_v2f_t2f_c1i(verts[2*i+0], verts[2*i+1], tcoords[2*i+0], tcoords[2*i+1], colors[i]); + } + sgl_end(); + sgl_pop_pipeline(); + sgl_disable_texture(); +} + +static void _sfons_render_delete(void* user_ptr) { + SOKOL_ASSERT(user_ptr); + _sfons_t* sfons = (_sfons_t*) user_ptr; + if (sfons->img.id != SG_INVALID_ID) { + sg_destroy_image(sfons->img); + sfons->img.id = SG_INVALID_ID; + } + if (sfons->smp.id != SG_INVALID_ID) { + sg_destroy_sampler(sfons->smp); + sfons->smp.id = SG_INVALID_ID; + } + if (sfons->pip.id != SG_INVALID_ID) { + sgl_destroy_pipeline(sfons->pip); + sfons->pip.id = SG_INVALID_ID; + } + if (sfons->shd.id != SG_INVALID_ID) { + sg_destroy_shader(sfons->shd); + sfons->shd.id = SG_INVALID_ID; + } +} + +#define _sfons_def(val, def) (((val) == 0) ? (def) : (val)) + +static sfons_desc_t _sfons_desc_defaults(const sfons_desc_t* desc) { + SOKOL_ASSERT(desc); + sfons_desc_t res = *desc; + res.width = _sfons_def(res.width, 512); + res.height = _sfons_def(res.height, 512); + return res; +} + +SOKOL_API_IMPL FONScontext* sfons_create(const sfons_desc_t* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + _sfons_t* sfons = (_sfons_t*) _sfons_malloc_clear(&desc->allocator, sizeof(_sfons_t)); + sfons->desc = _sfons_desc_defaults(desc); + FONSparams params; + _sfons_clear(¶ms, sizeof(params)); + params.width = sfons->desc.width; + params.height = sfons->desc.height; + params.flags = FONS_ZERO_TOPLEFT; + params.renderCreate = _sfons_render_create; + params.renderResize = _sfons_render_resize; + params.renderUpdate = _sfons_render_update; + params.renderDraw = _sfons_render_draw; + params.renderDelete = _sfons_render_delete; + params.userPtr = sfons; + return fonsCreateInternal(¶ms); +} + +SOKOL_API_IMPL void sfons_destroy(FONScontext* ctx) { + SOKOL_ASSERT(ctx); + _sfons_t* sfons = (_sfons_t*) ctx->params.userPtr; + fonsDeleteInternal(ctx); + const sfons_allocator_t allocator = sfons->desc.allocator; + _sfons_free(&allocator, sfons); +} + +SOKOL_API_IMPL void sfons_flush(FONScontext* ctx) { + SOKOL_ASSERT(ctx && ctx->params.userPtr); + _sfons_t* sfons = (_sfons_t*) ctx->params.userPtr; + if (sfons->img_dirty) { + sfons->img_dirty = false; + sg_image_data data; + _sfons_clear(&data, sizeof(data)); + data.subimage[0][0].ptr = ctx->texData; + data.subimage[0][0].size = (size_t) (sfons->cur_width * sfons->cur_height); + sg_update_image(sfons->img, &data); + } +} + +SOKOL_API_IMPL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + return ((uint32_t)r) | ((uint32_t)g<<8) | ((uint32_t)b<<16) | ((uint32_t)a<<24); +} + +#endif // SOKOL_FONTSTASH_IMPL diff --git a/thirdparty/sokol/util/sokol_gfx_imgui.h b/thirdparty/sokol/util/sokol_gfx_imgui.h new file mode 100644 index 0000000..8af2d47 --- /dev/null +++ b/thirdparty/sokol/util/sokol_gfx_imgui.h @@ -0,0 +1,4765 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_GFX_IMGUI_IMPL) +#define SOKOL_GFX_IMGUI_IMPL +#endif +#ifndef SOKOL_GFX_IMGUI_INCLUDED +/* + sokol_gfx_imgui.h -- debug-inspection UI for sokol_gfx.h using Dear ImGui + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_GFX_IMGUI_IMPL + + before you include this file in *one* C or C++ file to create the + implementation. + + NOTE that the implementation can be compiled either as C++ or as C. + When compiled as C++, sokol_gfx_imgui.h will directly call into the + Dear ImGui C++ API. When compiled as C, sokol_gfx_imgui.h will call + cimgui.h functions instead. + + Include the following file(s) before including sokol_gfx_imgui.h: + + sokol_gfx.h + + Additionally, include the following headers before including the + implementation: + + If the implementation is compiled as C++: + imgui.h + + If the implementation is compiled as C: + cimgui.h + + The sokol_gfx.h implementation must be compiled with debug trace hooks + enabled by defining: + + SOKOL_TRACE_HOOKS + + ...before including the sokol_gfx.h implementation. + + Before including the sokol_gfx_imgui.h implementation, optionally + override the following macros: + + SOKOL_ASSERT(c) -- your own assert macro, default: assert(c) + SOKOL_UNREACHABLE -- your own macro to annotate unreachable code, + default: SOKOL_ASSERT(false) + SOKOL_GFX_IMGUI_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_GFX_IMGUI_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + If sokol_gfx_imgui.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_GFX_IMGUI_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + STEP BY STEP: + ============= + --- create an sgimgui_t struct (which must be preserved between frames) + and initialize it with: + + sgimgui_init(&sgimgui, &(sgimgui_desc_t){ 0 }); + + Note that from C++ you can't inline the desc structure initialization: + + const sgimgui_desc_t desc = { }; + sgimgui_init(&sgimgui, &desc); + + Provide optional memory allocator override functions (compatible with malloc/free) like this: + + sgimgui_init(&sgimgui, &(sgimgui_desc_t){ + .allocator = { + .alloc_fn = my_malloc, + .free_fn = my_free, + } + }); + + --- somewhere in the per-frame code call: + + sgimgui_draw(&sgimgui) + + this won't draw anything yet, since no windows are open. + + --- call the convenience function sgimgui_draw_menu(ctx, title) + to render a menu which allows to open/close the provided debug windows + + sgimgui_draw_menu(&sgimgui, "sokol-gfx"); + + --- alternative, open and close windows directly by setting the following public + booleans in the sgimgui_t struct: + + sgimgui.caps_window.open = true; + sgimgui.frame_stats_window.open = true; + sgimgui.buffer_window.open = true; + sgimgui.image_window.open = true; + sgimgui.sampler_window.open = true; + sgimgui.shader_window.open = true; + sgimgui.pipeline_window.open = true; + sgimgui.attachments_window.open = true; + sgimgui.capture_window.open = true; + sgimgui.frame_stats_window.open = true; + + ...for instance, to control the window visibility through + menu items, the following code can be used: + + if (ImGui::BeginMainMenuBar()) { + if (ImGui::BeginMenu("sokol-gfx")) { + ImGui::MenuItem("Capabilities", 0, &sgimgui.caps_window.open); + ImGui::MenuItem("Frame Stats", 0, &sgimgui.frame_stats_window.open); + ImGui::MenuItem("Buffers", 0, &sgimgui.buffer_window.open); + ImGui::MenuItem("Images", 0, &sgimgui.image_window.open); + ImGui::MenuItem("Samplers", 0, &sgimgui.sampler_window.open); + ImGui::MenuItem("Shaders", 0, &sgimgui.shader_window.open); + ImGui::MenuItem("Pipelines", 0, &sgimgui.pipeline_window.open); + ImGui::MenuItem("Attachments", 0, &sgimgui.attachments_window.open); + ImGui::MenuItem("Calls", 0, &sgimgui.capture_window.open); + ImGui::EndMenu(); + } + ImGui::EndMainMenuBar(); + } + + --- before application shutdown, call: + + sgimgui_discard(&sgimgui); + + ...this is not strictly necessary because the application exits + anyway, but not doing this may trigger memory leak detection tools. + + --- finally, your application needs an ImGui renderer, you can either + provide your own, or drop in the sokol_imgui.h utility header + + ALTERNATIVE DRAWING FUNCTIONS: + ============================== + Instead of the convenient, but all-in-one sgimgui_draw() function, + you can also use the following granular functions which might allow + better integration with your existing UI: + + The following functions only render the window *content* (so you + can integrate the UI into you own windows): + + void sgimgui_draw_buffer_window_content(sgimgui_t* ctx); + void sgimgui_draw_image_window_content(sgimgui_t* ctx); + void sgimgui_draw_sampler_window_content(sgimgui_t* ctx); + void sgimgui_draw_shader_window_content(sgimgui_t* ctx); + void sgimgui_draw_pipeline_window_content(sgimgui_t* ctx); + void sgimgui_draw_attachments_window_content(sgimgui_t* ctx); + void sgimgui_draw_capture_window_content(sgimgui_t* ctx); + + And these are the 'full window' drawing functions: + + void sgimgui_draw_buffer_window(sgimgui_t* ctx); + void sgimgui_draw_image_window(sgimgui_t* ctx); + void sgimgui_draw_sampler_window(sgimgui_t* ctx); + void sgimgui_draw_shader_window(sgimgui_t* ctx); + void sgimgui_draw_pipeline_window(sgimgui_t* ctx); + void sgimgui_draw_attachments_window(sgimgui_t* ctx); + void sgimgui_draw_capture_window(sgimgui_t* ctx); + + Finer-grained drawing functions may be moved to the public API + in the future as needed. + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + sgimgui_init(&(&ctx, &(sgimgui_desc_t){ + // ... + .allocator = { + .alloc_fn = my_alloc, + .free_fn = my_free, + .user_data = ...; + } + }); + ... + + This only affects memory allocation calls done by sokol_gfx_imgui.h + itself though, not any allocations in OS libraries. + + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_GFX_IMGUI_INCLUDED (1) +#include +#include +#include // size_t + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_gfx_imgui.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_GFX_IMGUI_API_DECL) +#define SOKOL_GFX_IMGUI_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_GFX_IMGUI_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GFX_IMGUI_IMPL) +#define SOKOL_GFX_IMGUI_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_GFX_IMGUI_API_DECL __declspec(dllimport) +#else +#define SOKOL_GFX_IMGUI_API_DECL extern +#endif +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#define sgimgui_STRBUF_LEN (96) +/* max number of captured calls per frame */ +#define sgimgui_MAX_FRAMECAPTURE_ITEMS (4096) + +typedef struct sgimgui_str_t { + char buf[sgimgui_STRBUF_LEN]; +} sgimgui_str_t; + +typedef struct sgimgui_buffer_t { + sg_buffer res_id; + sgimgui_str_t label; + sg_buffer_desc desc; +} sgimgui_buffer_t; + +typedef struct sgimgui_image_t { + sg_image res_id; + float ui_scale; + sgimgui_str_t label; + sg_image_desc desc; + simgui_image_t simgui_img; +} sgimgui_image_t; + +typedef struct sgimgui_sampler_t { + sg_sampler res_id; + sgimgui_str_t label; + sg_sampler_desc desc; +} sgimgui_sampler_t; + +typedef struct sgimgui_shader_t { + sg_shader res_id; + sgimgui_str_t label; + sgimgui_str_t vs_entry; + sgimgui_str_t vs_d3d11_target; + sgimgui_str_t vs_image_sampler_name[SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS]; + sgimgui_str_t vs_uniform_name[SG_MAX_SHADERSTAGE_UBS][SG_MAX_UB_MEMBERS]; + sgimgui_str_t fs_entry; + sgimgui_str_t fs_d3d11_target; + sgimgui_str_t fs_image_sampler_name[SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS]; + sgimgui_str_t fs_uniform_name[SG_MAX_SHADERSTAGE_UBS][SG_MAX_UB_MEMBERS]; + sgimgui_str_t attr_name[SG_MAX_VERTEX_ATTRIBUTES]; + sgimgui_str_t attr_sem_name[SG_MAX_VERTEX_ATTRIBUTES]; + sg_shader_desc desc; +} sgimgui_shader_t; + +typedef struct sgimgui_pipeline_t { + sg_pipeline res_id; + sgimgui_str_t label; + sg_pipeline_desc desc; +} sgimgui_pipeline_t; + +typedef struct sgimgui_attachments_t { + sg_attachments res_id; + sgimgui_str_t label; + float color_image_scale[SG_MAX_COLOR_ATTACHMENTS]; + float resolve_image_scale[SG_MAX_COLOR_ATTACHMENTS]; + float ds_image_scale; + sg_attachments_desc desc; +} sgimgui_attachments_t; + +typedef struct sgimgui_buffer_window_t { + bool open; + int num_slots; + sg_buffer sel_buf; + sgimgui_buffer_t* slots; +} sgimgui_buffer_window_t; + +typedef struct sgimgui_image_window_t { + bool open; + int num_slots; + sg_image sel_img; + sgimgui_image_t* slots; +} sgimgui_image_window_t; + +typedef struct sgimgui_sampler_window_t { + bool open; + int num_slots; + sg_sampler sel_smp; + sgimgui_sampler_t* slots; +} sgimgui_sampler_window_t; + +typedef struct sgimgui_shader_window_t { + bool open; + int num_slots; + sg_shader sel_shd; + sgimgui_shader_t* slots; +} sgimgui_shader_window_t; + +typedef struct sgimgui_pipeline_window_t { + bool open; + int num_slots; + sg_pipeline sel_pip; + sgimgui_pipeline_t* slots; +} sgimgui_pipeline_window_t; + +typedef struct sgimgui_attachments_window_t { + bool open; + int num_slots; + sg_attachments sel_atts; + sgimgui_attachments_t* slots; +} sgimgui_attachments_window_t; + +typedef enum sgimgui_cmd_t { + SGIMGUI_CMD_INVALID, + SGIMGUI_CMD_RESET_STATE_CACHE, + SGIMGUI_CMD_MAKE_BUFFER, + SGIMGUI_CMD_MAKE_IMAGE, + SGIMGUI_CMD_MAKE_SAMPLER, + SGIMGUI_CMD_MAKE_SHADER, + SGIMGUI_CMD_MAKE_PIPELINE, + SGIMGUI_CMD_MAKE_ATTACHMENTS, + SGIMGUI_CMD_DESTROY_BUFFER, + SGIMGUI_CMD_DESTROY_IMAGE, + SGIMGUI_CMD_DESTROY_SAMPLER, + SGIMGUI_CMD_DESTROY_SHADER, + SGIMGUI_CMD_DESTROY_PIPELINE, + SGIMGUI_CMD_DESTROY_ATTACHMENTS, + SGIMGUI_CMD_UPDATE_BUFFER, + SGIMGUI_CMD_UPDATE_IMAGE, + SGIMGUI_CMD_APPEND_BUFFER, + SGIMGUI_CMD_BEGIN_PASS, + SGIMGUI_CMD_APPLY_VIEWPORT, + SGIMGUI_CMD_APPLY_SCISSOR_RECT, + SGIMGUI_CMD_APPLY_PIPELINE, + SGIMGUI_CMD_APPLY_BINDINGS, + SGIMGUI_CMD_APPLY_UNIFORMS, + SGIMGUI_CMD_DRAW, + SGIMGUI_CMD_END_PASS, + SGIMGUI_CMD_COMMIT, + SGIMGUI_CMD_ALLOC_BUFFER, + SGIMGUI_CMD_ALLOC_IMAGE, + SGIMGUI_CMD_ALLOC_SAMPLER, + SGIMGUI_CMD_ALLOC_SHADER, + SGIMGUI_CMD_ALLOC_PIPELINE, + SGIMGUI_CMD_ALLOC_ATTACHMENTS, + SGIMGUI_CMD_DEALLOC_BUFFER, + SGIMGUI_CMD_DEALLOC_IMAGE, + SGIMGUI_CMD_DEALLOC_SAMPLER, + SGIMGUI_CMD_DEALLOC_SHADER, + SGIMGUI_CMD_DEALLOC_PIPELINE, + SGIMGUI_CMD_DEALLOC_ATTACHMENTS, + SGIMGUI_CMD_INIT_BUFFER, + SGIMGUI_CMD_INIT_IMAGE, + SGIMGUI_CMD_INIT_SAMPLER, + SGIMGUI_CMD_INIT_SHADER, + SGIMGUI_CMD_INIT_PIPELINE, + SGIMGUI_CMD_INIT_ATTACHMENTS, + SGIMGUI_CMD_UNINIT_BUFFER, + SGIMGUI_CMD_UNINIT_IMAGE, + SGIMGUI_CMD_UNINIT_SAMPLER, + SGIMGUI_CMD_UNINIT_SHADER, + SGIMGUI_CMD_UNINIT_PIPELINE, + SGIMGUI_CMD_UNINIT_ATTACHMENTS, + SGIMGUI_CMD_FAIL_BUFFER, + SGIMGUI_CMD_FAIL_IMAGE, + SGIMGUI_CMD_FAIL_SAMPLER, + SGIMGUI_CMD_FAIL_SHADER, + SGIMGUI_CMD_FAIL_PIPELINE, + SGIMGUI_CMD_FAIL_ATTACHMENTS, + SGIMGUI_CMD_PUSH_DEBUG_GROUP, + SGIMGUI_CMD_POP_DEBUG_GROUP, +} sgimgui_cmd_t; + +typedef struct sgimgui_args_make_buffer_t { + sg_buffer result; +} sgimgui_args_make_buffer_t; + +typedef struct sgimgui_args_make_image_t { + sg_image result; +} sgimgui_args_make_image_t; + +typedef struct sgimgui_args_make_sampler_t { + sg_sampler result; +} sgimgui_args_make_sampler_t; + +typedef struct sgimgui_args_make_shader_t { + sg_shader result; +} sgimgui_args_make_shader_t; + +typedef struct sgimgui_args_make_pipeline_t { + sg_pipeline result; +} sgimgui_args_make_pipeline_t; + +typedef struct sgimgui_args_make_attachments_t { + sg_attachments result; +} sgimgui_args_make_attachments_t; + +typedef struct sgimgui_args_destroy_buffer_t { + sg_buffer buffer; +} sgimgui_args_destroy_buffer_t; + +typedef struct sgimgui_args_destroy_image_t { + sg_image image; +} sgimgui_args_destroy_image_t; + +typedef struct sgimgui_args_destroy_sampler_t { + sg_sampler sampler; +} sgimgui_args_destroy_sampler_t; + +typedef struct sgimgui_args_destroy_shader_t { + sg_shader shader; +} sgimgui_args_destroy_shader_t; + +typedef struct sgimgui_args_destroy_pipeline_t { + sg_pipeline pipeline; +} sgimgui_args_destroy_pipeline_t; + +typedef struct sgimgui_args_destroy_attachments_t { + sg_attachments attachments; +} sgimgui_args_destroy_attachments_t; + +typedef struct sgimgui_args_update_buffer_t { + sg_buffer buffer; + size_t data_size; +} sgimgui_args_update_buffer_t; + +typedef struct sgimgui_args_update_image_t { + sg_image image; +} sgimgui_args_update_image_t; + +typedef struct sgimgui_args_append_buffer_t { + sg_buffer buffer; + size_t data_size; + int result; +} sgimgui_args_append_buffer_t; + +typedef struct sgimgui_args_begin_pass_t { + sg_pass pass; +} sgimgui_args_begin_pass_t; + +typedef struct sgimgui_args_apply_viewport_t { + int x, y, width, height; + bool origin_top_left; +} sgimgui_args_apply_viewport_t; + +typedef struct sgimgui_args_apply_scissor_rect_t { + int x, y, width, height; + bool origin_top_left; +} sgimgui_args_apply_scissor_rect_t; + +typedef struct sgimgui_args_apply_pipeline_t { + sg_pipeline pipeline; +} sgimgui_args_apply_pipeline_t; + +typedef struct sgimgui_args_apply_bindings_t { + sg_bindings bindings; +} sgimgui_args_apply_bindings_t; + +typedef struct sgimgui_args_apply_uniforms_t { + sg_shader_stage stage; + int ub_index; + size_t data_size; + sg_pipeline pipeline; /* the pipeline which was active at this call */ + size_t ubuf_pos; /* start of copied data in capture buffer */ +} sgimgui_args_apply_uniforms_t; + +typedef struct sgimgui_args_draw_t { + int base_element; + int num_elements; + int num_instances; +} sgimgui_args_draw_t; + +typedef struct sgimgui_args_alloc_buffer_t { + sg_buffer result; +} sgimgui_args_alloc_buffer_t; + +typedef struct sgimgui_args_alloc_image_t { + sg_image result; +} sgimgui_args_alloc_image_t; + +typedef struct sgimgui_args_alloc_sampler_t { + sg_sampler result; +} sgimgui_args_alloc_sampler_t; + +typedef struct sgimgui_args_alloc_shader_t { + sg_shader result; +} sgimgui_args_alloc_shader_t; + +typedef struct sgimgui_args_alloc_pipeline_t { + sg_pipeline result; +} sgimgui_args_alloc_pipeline_t; + +typedef struct sgimgui_args_alloc_attachments_t { + sg_attachments result; +} sgimgui_args_alloc_attachments_t; + +typedef struct sgimgui_args_dealloc_buffer_t { + sg_buffer buffer; +} sgimgui_args_dealloc_buffer_t; + +typedef struct sgimgui_args_dealloc_image_t { + sg_image image; +} sgimgui_args_dealloc_image_t; + +typedef struct sgimgui_args_dealloc_sampler_t { + sg_sampler sampler; +} sgimgui_args_dealloc_sampler_t; + +typedef struct sgimgui_args_dealloc_shader_t { + sg_shader shader; +} sgimgui_args_dealloc_shader_t; + +typedef struct sgimgui_args_dealloc_pipeline_t { + sg_pipeline pipeline; +} sgimgui_args_dealloc_pipeline_t; + +typedef struct sgimgui_args_dealloc_attachments_t { + sg_attachments attachments; +} sgimgui_args_dealloc_attachments_t; + +typedef struct sgimgui_args_init_buffer_t { + sg_buffer buffer; +} sgimgui_args_init_buffer_t; + +typedef struct sgimgui_args_init_image_t { + sg_image image; +} sgimgui_args_init_image_t; + +typedef struct sgimgui_args_init_sampler_t { + sg_sampler sampler; +} sgimgui_args_init_sampler_t; + +typedef struct sgimgui_args_init_shader_t { + sg_shader shader; +} sgimgui_args_init_shader_t; + +typedef struct sgimgui_args_init_pipeline_t { + sg_pipeline pipeline; +} sgimgui_args_init_pipeline_t; + +typedef struct sgimgui_args_init_attachments_t { + sg_attachments attachments; +} sgimgui_args_init_attachments_t; + +typedef struct sgimgui_args_uninit_buffer_t { + sg_buffer buffer; +} sgimgui_args_uninit_buffer_t; + +typedef struct sgimgui_args_uninit_image_t { + sg_image image; +} sgimgui_args_uninit_image_t; + +typedef struct sgimgui_args_uninit_sampler_t { + sg_sampler sampler; +} sgimgui_args_uninit_sampler_t; + +typedef struct sgimgui_args_uninit_shader_t { + sg_shader shader; +} sgimgui_args_uninit_shader_t; + +typedef struct sgimgui_args_uninit_pipeline_t { + sg_pipeline pipeline; +} sgimgui_args_uninit_pipeline_t; + +typedef struct sgimgui_args_uninit_attachments_t { + sg_attachments attachments; +} sgimgui_args_uninit_attachments_t; + +typedef struct sgimgui_args_fail_buffer_t { + sg_buffer buffer; +} sgimgui_args_fail_buffer_t; + +typedef struct sgimgui_args_fail_image_t { + sg_image image; +} sgimgui_args_fail_image_t; + +typedef struct sgimgui_args_fail_sampler_t { + sg_sampler sampler; +} sgimgui_args_fail_sampler_t; + +typedef struct sgimgui_args_fail_shader_t { + sg_shader shader; +} sgimgui_args_fail_shader_t; + +typedef struct sgimgui_args_fail_pipeline_t { + sg_pipeline pipeline; +} sgimgui_args_fail_pipeline_t; + +typedef struct sgimgui_args_fail_attachments_t { + sg_attachments attachments; +} sgimgui_args_fail_attachments_t; + +typedef struct sgimgui_args_push_debug_group_t { + sgimgui_str_t name; +} sgimgui_args_push_debug_group_t; + +typedef union sgimgui_args_t { + sgimgui_args_make_buffer_t make_buffer; + sgimgui_args_make_image_t make_image; + sgimgui_args_make_sampler_t make_sampler; + sgimgui_args_make_shader_t make_shader; + sgimgui_args_make_pipeline_t make_pipeline; + sgimgui_args_make_attachments_t make_attachments; + sgimgui_args_destroy_buffer_t destroy_buffer; + sgimgui_args_destroy_image_t destroy_image; + sgimgui_args_destroy_sampler_t destroy_sampler; + sgimgui_args_destroy_shader_t destroy_shader; + sgimgui_args_destroy_pipeline_t destroy_pipeline; + sgimgui_args_destroy_attachments_t destroy_attachments; + sgimgui_args_update_buffer_t update_buffer; + sgimgui_args_update_image_t update_image; + sgimgui_args_append_buffer_t append_buffer; + sgimgui_args_begin_pass_t begin_pass; + sgimgui_args_apply_viewport_t apply_viewport; + sgimgui_args_apply_scissor_rect_t apply_scissor_rect; + sgimgui_args_apply_pipeline_t apply_pipeline; + sgimgui_args_apply_bindings_t apply_bindings; + sgimgui_args_apply_uniforms_t apply_uniforms; + sgimgui_args_draw_t draw; + sgimgui_args_alloc_buffer_t alloc_buffer; + sgimgui_args_alloc_image_t alloc_image; + sgimgui_args_alloc_sampler_t alloc_sampler; + sgimgui_args_alloc_shader_t alloc_shader; + sgimgui_args_alloc_pipeline_t alloc_pipeline; + sgimgui_args_alloc_attachments_t alloc_attachments; + sgimgui_args_dealloc_buffer_t dealloc_buffer; + sgimgui_args_dealloc_image_t dealloc_image; + sgimgui_args_dealloc_sampler_t dealloc_sampler; + sgimgui_args_dealloc_shader_t dealloc_shader; + sgimgui_args_dealloc_pipeline_t dealloc_pipeline; + sgimgui_args_dealloc_attachments_t dealloc_attachments; + sgimgui_args_init_buffer_t init_buffer; + sgimgui_args_init_image_t init_image; + sgimgui_args_init_sampler_t init_sampler; + sgimgui_args_init_shader_t init_shader; + sgimgui_args_init_pipeline_t init_pipeline; + sgimgui_args_init_attachments_t init_attachments; + sgimgui_args_uninit_buffer_t uninit_buffer; + sgimgui_args_uninit_image_t uninit_image; + sgimgui_args_uninit_sampler_t uninit_sampler; + sgimgui_args_uninit_shader_t uninit_shader; + sgimgui_args_uninit_pipeline_t uninit_pipeline; + sgimgui_args_uninit_attachments_t uninit_attachments; + sgimgui_args_fail_buffer_t fail_buffer; + sgimgui_args_fail_image_t fail_image; + sgimgui_args_fail_sampler_t fail_sampler; + sgimgui_args_fail_shader_t fail_shader; + sgimgui_args_fail_pipeline_t fail_pipeline; + sgimgui_args_fail_attachments_t fail_attachments; + sgimgui_args_push_debug_group_t push_debug_group; +} sgimgui_args_t; + +typedef struct sgimgui_capture_item_t { + sgimgui_cmd_t cmd; + uint32_t color; + sgimgui_args_t args; +} sgimgui_capture_item_t; + +typedef struct sgimgui_capture_bucket_t { + size_t ubuf_size; /* size of uniform capture buffer in bytes */ + size_t ubuf_pos; /* current uniform buffer pos */ + uint8_t* ubuf; /* buffer for capturing uniform updates */ + int num_items; + sgimgui_capture_item_t items[sgimgui_MAX_FRAMECAPTURE_ITEMS]; +} sgimgui_capture_bucket_t; + +/* double-buffered call-capture buckets, one bucket is currently recorded, + the previous bucket is displayed +*/ +typedef struct sgimgui_capture_window_t { + bool open; + int bucket_index; /* which bucket to record to, 0 or 1 */ + int sel_item; /* currently selected capture item by index */ + sgimgui_capture_bucket_t bucket[2]; +} sgimgui_capture_window_t; + +typedef struct sgimgui_caps_window_t { + bool open; +} sgimgui_caps_window_t; + +typedef struct sgimgui_frame_stats_window_t { + bool open; + bool disable_sokol_imgui_stats; + bool in_sokol_imgui; + sg_frame_stats stats; + // FIXME: add a ringbuffer for a stats history here +} sgimgui_frame_stats_window_t; + +/* + sgimgui_allocator_t + + Used in sgimgui_desc_t to provide custom memory-alloc and -free functions + to sokol_gfx_imgui.h. If memory management should be overridden, both the + alloc and free function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct sgimgui_allocator_t { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} sgimgui_allocator_t; + +/* + sgimgui_desc_t + + Initialization options for sgimgui_init(). +*/ +typedef struct sgimgui_desc_t { + sgimgui_allocator_t allocator; // optional memory allocation overrides (default: malloc/free) +} sgimgui_desc_t; + +typedef struct sgimgui_t { + uint32_t init_tag; + sgimgui_desc_t desc; + sgimgui_buffer_window_t buffer_window; + sgimgui_image_window_t image_window; + sgimgui_sampler_window_t sampler_window; + sgimgui_shader_window_t shader_window; + sgimgui_pipeline_window_t pipeline_window; + sgimgui_attachments_window_t attachments_window; + sgimgui_capture_window_t capture_window; + sgimgui_caps_window_t caps_window; + sgimgui_frame_stats_window_t frame_stats_window; + sg_pipeline cur_pipeline; + sg_trace_hooks hooks; +} sgimgui_t; + +SOKOL_GFX_IMGUI_API_DECL void sgimgui_init(sgimgui_t* ctx, const sgimgui_desc_t* desc); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_discard(sgimgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw(sgimgui_t* ctx); + +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_menu(sgimgui_t* ctx, const char* title); + +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_buffer_window_content(sgimgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_image_window_content(sgimgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_sampler_window_content(sgimgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_shader_window_content(sgimgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_pipeline_window_content(sgimgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_attachments_window_content(sgimgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_capture_window_content(sgimgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_capabilities_window_content(sgimgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_frame_stats_window_content(sgimgui_t* ctx); + +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_buffer_window(sgimgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_image_window(sgimgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_sampler_window(sgimgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_shader_window(sgimgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_pipeline_window(sgimgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_attachments_window(sgimgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_capture_window(sgimgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_capabilities_window(sgimgui_t* ctx); +SOKOL_GFX_IMGUI_API_DECL void sgimgui_draw_frame_stats_window(sgimgui_t* ctx); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif +#endif /* SOKOL_GFX_IMGUI_INCLUDED */ + +/*=== IMPLEMENTATION =========================================================*/ +#ifdef SOKOL_GFX_IMGUI_IMPL +#define SOKOL_GFX_IMGUI_IMPL_INCLUDED (1) + +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sgimgui_desc_t.allocator to override memory allocation functions" +#endif + +#if defined(__cplusplus) + #if !defined(IMGUI_VERSION) + #error "Please include imgui.h before the sokol_imgui.h implementation" + #endif +#else + #if !defined(CIMGUI_INCLUDED) + #error "Please include cimgui.h before the sokol_imgui.h implementation" + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef SOKOL_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#endif +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif +#ifndef _SOKOL_UNUSED +#define _SOKOL_UNUSED(x) (void)(x) +#endif +#ifndef SOKOL_API_IMPL +#define SOKOL_API_IMPL +#endif + +#include +#include // snprintf +#include // malloc, free + +#define _SGIMGUI_SLOT_MASK (0xFFFF) +#define _SGIMGUI_LIST_WIDTH (192) +#define _SGIMGUI_COLOR_OTHER 0xFFCCCCCC +#define _SGIMGUI_COLOR_RSRC 0xFF00FFFF +#define _SGIMGUI_COLOR_PASS 0xFFFFFF00 +#define _SGIMGUI_COLOR_APPLY 0xFFCCCC00 +#define _SGIMGUI_COLOR_DRAW 0xFF00FF00 +#define _SGIMGUI_COLOR_ERR 0xFF8888FF + +/*--- C => C++ layer ---------------------------------------------------------*/ +#if defined(__cplusplus) +#define IMVEC2(x,y) ImVec2(x,y) +#define IMVEC4(x,y,z,w) ImVec4(x,y,z,w) +_SOKOL_PRIVATE void igText(const char* fmt,...) { + va_list args; + va_start(args, fmt); + ImGui::TextV(fmt, args); + va_end(args); +} +_SOKOL_PRIVATE void igSeparator() { + return ImGui::Separator(); +} +_SOKOL_PRIVATE void igSameLine(float offset_from_start_x, float spacing) { + return ImGui::SameLine(offset_from_start_x,spacing); +} +_SOKOL_PRIVATE void igPushID_Int(int int_id) { + return ImGui::PushID(int_id); +} +_SOKOL_PRIVATE void igPopID() { + return ImGui::PopID(); +} +_SOKOL_PRIVATE bool igSelectable_Bool(const char* label,bool selected,ImGuiSelectableFlags flags,const ImVec2 size) { + return ImGui::Selectable(label,selected,flags,size); +} +_SOKOL_PRIVATE bool igSmallButton(const char* label) { + return ImGui::SmallButton(label); +} +_SOKOL_PRIVATE bool igBeginChild_Str(const char* str_id,const ImVec2 size,bool border,ImGuiWindowFlags flags) { + return ImGui::BeginChild(str_id,size,border,flags); +} +_SOKOL_PRIVATE void igEndChild() { + return ImGui::EndChild(); +} +_SOKOL_PRIVATE void igPushStyleColor_U32(ImGuiCol idx, ImU32 col) { + return ImGui::PushStyleColor(idx,col); +} +_SOKOL_PRIVATE void igPopStyleColor(int count) { + return ImGui::PopStyleColor(count); +} +_SOKOL_PRIVATE bool igTreeNode_StrStr(const char* str_id,const char* fmt,...) { + va_list args; + va_start(args, fmt); + bool ret = ImGui::TreeNodeV(str_id,fmt,args); + va_end(args); + return ret; +} +_SOKOL_PRIVATE bool igTreeNode_Str(const char* label) { + return ImGui::TreeNode(label); +} +_SOKOL_PRIVATE void igTreePop() { + return ImGui::TreePop(); +} +_SOKOL_PRIVATE bool igIsItemHovered(ImGuiHoveredFlags flags) { + return ImGui::IsItemHovered(flags); +} +_SOKOL_PRIVATE void igSetTooltip(const char* fmt,...) { + va_list args; + va_start(args, fmt); + ImGui::SetTooltipV(fmt,args); + va_end(args); +} +_SOKOL_PRIVATE bool igSliderFloat(const char* label,float* v,float v_min,float v_max,const char* format,ImGuiSliderFlags flags) { + return ImGui::SliderFloat(label,v,v_min,v_max,format,flags); +} +_SOKOL_PRIVATE void igImage(ImTextureID user_texture_id,const ImVec2 size,const ImVec2 uv0,const ImVec2 uv1,const ImVec4 tint_col,const ImVec4 border_col) { + return ImGui::Image(user_texture_id,size,uv0,uv1,tint_col,border_col); +} +_SOKOL_PRIVATE void igSetNextWindowSize(const ImVec2 size,ImGuiCond cond) { + return ImGui::SetNextWindowSize(size,cond); +} +_SOKOL_PRIVATE bool igBegin(const char* name,bool* p_open,ImGuiWindowFlags flags) { + return ImGui::Begin(name,p_open,flags); +} +_SOKOL_PRIVATE void igEnd() { + return ImGui::End(); +} +_SOKOL_PRIVATE bool igBeginMenu(const char* label, bool enabled) { + return ImGui::BeginMenu(label, enabled); +} +_SOKOL_PRIVATE void igEndMenu(void) { + ImGui::EndMenu(); +} +_SOKOL_PRIVATE bool igMenuItem_BoolPtr(const char* label, const char* shortcut, bool* p_selected, bool enabled) { + return ImGui::MenuItem(label, shortcut, p_selected, enabled); +} +_SOKOL_PRIVATE bool igBeginTable(const char* str_id, int column, ImGuiTableFlags flags, const ImVec2 outer_size, float inner_width) { + return ImGui::BeginTable(str_id, column, flags, outer_size, inner_width); +} +_SOKOL_PRIVATE void igEndTable(void) { + ImGui::EndTable(); +} +_SOKOL_PRIVATE void igTableSetupScrollFreeze(int cols, int rows) { + ImGui::TableSetupScrollFreeze(cols, rows); +} +_SOKOL_PRIVATE void igTableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id) { + ImGui::TableSetupColumn(label, flags, init_width_or_weight, user_id); +} +_SOKOL_PRIVATE void igTableHeadersRow(void) { + ImGui::TableHeadersRow(); +} +_SOKOL_PRIVATE void igTableNextRow(ImGuiTableRowFlags row_flags, float min_row_height) { + ImGui::TableNextRow(row_flags, min_row_height); +} +_SOKOL_PRIVATE bool igTableSetColumnIndex(int column_n) { + return ImGui::TableSetColumnIndex(column_n); +} +_SOKOL_PRIVATE bool igCheckbox(const char* label, bool* v) { + return ImGui::Checkbox(label, v); +} +#else +#define IMVEC2(x,y) (ImVec2){x,y} +#define IMVEC4(x,y,z,w) (ImVec4){x,y,z,w} +#endif + +/*--- UTILS ------------------------------------------------------------------*/ +_SOKOL_PRIVATE void _sgimgui_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +_SOKOL_PRIVATE void* _sgimgui_malloc(const sgimgui_allocator_t* allocator, size_t size) { + SOKOL_ASSERT(allocator && (size > 0)); + void* ptr; + if (allocator->alloc_fn) { + ptr = allocator->alloc_fn(size, allocator->user_data); + } else { + ptr = malloc(size); + } + SOKOL_ASSERT(ptr); + return ptr; +} + +_SOKOL_PRIVATE void* _sgimgui_malloc_clear(const sgimgui_allocator_t* allocator, size_t size) { + void* ptr = _sgimgui_malloc(allocator, size); + _sgimgui_clear(ptr, size); + return ptr; +} + +_SOKOL_PRIVATE void _sgimgui_free(const sgimgui_allocator_t* allocator, void* ptr) { + SOKOL_ASSERT(allocator); + if (allocator->free_fn) { + allocator->free_fn(ptr, allocator->user_data); + } else { + free(ptr); + } +} + + _SOKOL_PRIVATE void* _sgimgui_realloc(const sgimgui_allocator_t* allocator, void* old_ptr, size_t old_size, size_t new_size) { + SOKOL_ASSERT(allocator && (new_size > 0) && (new_size > old_size)); + void* new_ptr = _sgimgui_malloc(allocator, new_size); + if (old_ptr) { + if (old_size > 0) { + memcpy(new_ptr, old_ptr, old_size); + } + _sgimgui_free(allocator, old_ptr); + } + return new_ptr; +} + +_SOKOL_PRIVATE int _sgimgui_slot_index(uint32_t id) { + int slot_index = (int) (id & _SGIMGUI_SLOT_MASK); + SOKOL_ASSERT(0 != slot_index); + return slot_index; +} + +_SOKOL_PRIVATE uint32_t _sgimgui_align_u32(uint32_t val, uint32_t align) { + SOKOL_ASSERT((align > 0) && ((align & (align - 1)) == 0)); + return (val + (align - 1)) & ~(align - 1); +} + +_SOKOL_PRIVATE uint32_t _sgimgui_std140_uniform_alignment(sg_uniform_type type, int array_count) { + SOKOL_ASSERT(array_count > 0); + if (array_count == 1) { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_INT: + return 4; + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_INT2: + return 8; + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT3: + case SG_UNIFORMTYPE_INT4: + return 16; + case SG_UNIFORMTYPE_MAT4: + return 16; + default: + SOKOL_UNREACHABLE; + return 1; + } + } else { + return 16; + } +} + +_SOKOL_PRIVATE uint32_t _sgimgui_std140_uniform_size(sg_uniform_type type, int array_count) { + SOKOL_ASSERT(array_count > 0); + if (array_count == 1) { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_INT: + return 4; + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_INT2: + return 8; + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_INT3: + return 12; + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT4: + return 16; + case SG_UNIFORMTYPE_MAT4: + return 64; + default: + SOKOL_UNREACHABLE; + return 0; + } + } else { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT: + case SG_UNIFORMTYPE_INT2: + case SG_UNIFORMTYPE_INT3: + case SG_UNIFORMTYPE_INT4: + return 16 * (uint32_t)array_count; + case SG_UNIFORMTYPE_MAT4: + return 64 * (uint32_t)array_count; + default: + SOKOL_UNREACHABLE; + return 0; + } + } +} + +_SOKOL_PRIVATE void _sgimgui_strcpy(sgimgui_str_t* dst, const char* src) { + SOKOL_ASSERT(dst); + if (src) { + #if defined(_MSC_VER) + strncpy_s(dst->buf, sgimgui_STRBUF_LEN, src, (sgimgui_STRBUF_LEN-1)); + #else + strncpy(dst->buf, src, sgimgui_STRBUF_LEN); + #endif + dst->buf[sgimgui_STRBUF_LEN-1] = 0; + } else { + _sgimgui_clear(dst->buf, sgimgui_STRBUF_LEN); + } +} + +_SOKOL_PRIVATE sgimgui_str_t _sgimgui_make_str(const char* str) { + sgimgui_str_t res; + _sgimgui_strcpy(&res, str); + return res; +} + +_SOKOL_PRIVATE const char* _sgimgui_str_dup(const sgimgui_allocator_t* allocator, const char* src) { + SOKOL_ASSERT(allocator && src); + size_t len = strlen(src) + 1; + char* dst = (char*) _sgimgui_malloc(allocator, len); + memcpy(dst, src, len); + return (const char*) dst; +} + +_SOKOL_PRIVATE const void* _sgimgui_bin_dup(const sgimgui_allocator_t* allocator, const void* src, size_t num_bytes) { + SOKOL_ASSERT(allocator && src && (num_bytes > 0)); + void* dst = _sgimgui_malloc(allocator, num_bytes); + memcpy(dst, src, num_bytes); + return (const void*) dst; +} + +_SOKOL_PRIVATE void _sgimgui_snprintf(sgimgui_str_t* dst, const char* fmt, ...) { + SOKOL_ASSERT(dst); + va_list args; + va_start(args, fmt); + vsnprintf(dst->buf, sizeof(dst->buf), fmt, args); + dst->buf[sizeof(dst->buf)-1] = 0; + va_end(args); +} + +/*--- STRING CONVERSION ------------------------------------------------------*/ +_SOKOL_PRIVATE const char* _sgimgui_resourcestate_string(sg_resource_state s) { + switch (s) { + case SG_RESOURCESTATE_INITIAL: return "SG_RESOURCESTATE_INITIAL"; + case SG_RESOURCESTATE_ALLOC: return "SG_RESOURCESTATE_ALLOC"; + case SG_RESOURCESTATE_VALID: return "SG_RESOURCESTATE_VALID"; + case SG_RESOURCESTATE_FAILED: return "SG_RESOURCESTATE_FAILED"; + default: return "SG_RESOURCESTATE_INVALID"; + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_resource_slot(const sg_slot_info* slot) { + igText("ResId: %08X", slot->res_id); + igText("State: %s", _sgimgui_resourcestate_string(slot->state)); +} + +_SOKOL_PRIVATE const char* _sgimgui_backend_string(sg_backend b) { + switch (b) { + case SG_BACKEND_GLCORE: return "SG_BACKEND_GLCORE"; + case SG_BACKEND_GLES3: return "SG_BACKEND_GLES3"; + case SG_BACKEND_D3D11: return "SG_BACKEND_D3D11"; + case SG_BACKEND_METAL_IOS: return "SG_BACKEND_METAL_IOS"; + case SG_BACKEND_METAL_MACOS: return "SG_BACKEND_METAL_MACOS"; + case SG_BACKEND_METAL_SIMULATOR: return "SG_BACKEND_METAL_SIMULATOR"; + case SG_BACKEND_WGPU: return "SG_BACKEND_WGPU"; + case SG_BACKEND_DUMMY: return "SG_BACKEND_DUMMY"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_buffertype_string(sg_buffer_type t) { + switch (t) { + case SG_BUFFERTYPE_VERTEXBUFFER: return "SG_BUFFERTYPE_VERTEXBUFFER"; + case SG_BUFFERTYPE_INDEXBUFFER: return "SG_BUFFERTYPE_INDEXBUFFER"; + case SG_BUFFERTYPE_STORAGEBUFFER: return "SG_BUFFERTYPE_STORAGEBUFFER"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_usage_string(sg_usage u) { + switch (u) { + case SG_USAGE_IMMUTABLE: return "SG_USAGE_IMMUTABLE"; + case SG_USAGE_DYNAMIC: return "SG_USAGE_DYNAMIC"; + case SG_USAGE_STREAM: return "SG_USAGE_STREAM"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_imagetype_string(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return "SG_IMAGETYPE_2D"; + case SG_IMAGETYPE_CUBE: return "SG_IMAGETYPE_CUBE"; + case SG_IMAGETYPE_3D: return "SG_IMAGETYPE_3D"; + case SG_IMAGETYPE_ARRAY: return "SG_IMAGETYPE_ARRAY"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_imagesampletype_string(sg_image_sample_type t) { + switch (t) { + case SG_IMAGESAMPLETYPE_FLOAT: return "SG_IMAGESAMPLETYPE_FLOAT"; + case SG_IMAGESAMPLETYPE_DEPTH: return "SG_IMAGESAMPLETYPE_DEPTH"; + case SG_IMAGESAMPLETYPE_SINT: return "SG_IMAGESAMPLETYPE_SINT"; + case SG_IMAGESAMPLETYPE_UINT: return "SG_IMAGESAMPLETYPE_UINT"; + case SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT: return "SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_samplertype_string(sg_sampler_type t) { + switch (t) { + case SG_SAMPLERTYPE_FILTERING: return "SG_SAMPLERTYPE_FILTERING"; + case SG_SAMPLERTYPE_COMPARISON: return "SG_SAMPLERTYPE_COMPARISON"; + case SG_SAMPLERTYPE_NONFILTERING: return "SG_SAMPLERTYPE_NONFILTERING"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_uniformlayout_string(sg_uniform_layout l) { + switch (l) { + case SG_UNIFORMLAYOUT_NATIVE: return "SG_UNIFORMLAYOUT_NATIVE"; + case SG_UNIFORMLAYOUT_STD140: return "SG_UNIFORMLAYOUT_STD140"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_pixelformat_string(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_NONE: return "SG_PIXELFORMAT_NONE"; + case SG_PIXELFORMAT_R8: return "SG_PIXELFORMAT_R8"; + case SG_PIXELFORMAT_R8SN: return "SG_PIXELFORMAT_R8SN"; + case SG_PIXELFORMAT_R8UI: return "SG_PIXELFORMAT_R8UI"; + case SG_PIXELFORMAT_R8SI: return "SG_PIXELFORMAT_R8SI"; + case SG_PIXELFORMAT_R16: return "SG_PIXELFORMAT_R16"; + case SG_PIXELFORMAT_R16SN: return "SG_PIXELFORMAT_R16SN"; + case SG_PIXELFORMAT_R16UI: return "SG_PIXELFORMAT_R16UI"; + case SG_PIXELFORMAT_R16SI: return "SG_PIXELFORMAT_R16SI"; + case SG_PIXELFORMAT_R16F: return "SG_PIXELFORMAT_R16F"; + case SG_PIXELFORMAT_RG8: return "SG_PIXELFORMAT_RG8"; + case SG_PIXELFORMAT_RG8SN: return "SG_PIXELFORMAT_RG8SN"; + case SG_PIXELFORMAT_RG8UI: return "SG_PIXELFORMAT_RG8UI"; + case SG_PIXELFORMAT_RG8SI: return "SG_PIXELFORMAT_RG8SI"; + case SG_PIXELFORMAT_R32UI: return "SG_PIXELFORMAT_R32UI"; + case SG_PIXELFORMAT_R32SI: return "SG_PIXELFORMAT_R32SI"; + case SG_PIXELFORMAT_R32F: return "SG_PIXELFORMAT_R32F"; + case SG_PIXELFORMAT_RG16: return "SG_PIXELFORMAT_RG16"; + case SG_PIXELFORMAT_RG16SN: return "SG_PIXELFORMAT_RG16SN"; + case SG_PIXELFORMAT_RG16UI: return "SG_PIXELFORMAT_RG16UI"; + case SG_PIXELFORMAT_RG16SI: return "SG_PIXELFORMAT_RG16SI"; + case SG_PIXELFORMAT_RG16F: return "SG_PIXELFORMAT_RG16F"; + case SG_PIXELFORMAT_RGBA8: return "SG_PIXELFORMAT_RGBA8"; + case SG_PIXELFORMAT_SRGB8A8: return "SG_PIXELFORMAT_SRGB8A8"; + case SG_PIXELFORMAT_RGBA8SN: return "SG_PIXELFORMAT_RGBA8SN"; + case SG_PIXELFORMAT_RGBA8UI: return "SG_PIXELFORMAT_RGBA8UI"; + case SG_PIXELFORMAT_RGBA8SI: return "SG_PIXELFORMAT_RGBA8SI"; + case SG_PIXELFORMAT_BGRA8: return "SG_PIXELFORMAT_BGRA8"; + case SG_PIXELFORMAT_RGB10A2: return "SG_PIXELFORMAT_RGB10A2"; + case SG_PIXELFORMAT_RG11B10F: return "SG_PIXELFORMAT_RG11B10F"; + case SG_PIXELFORMAT_RG32UI: return "SG_PIXELFORMAT_RG32UI"; + case SG_PIXELFORMAT_RG32SI: return "SG_PIXELFORMAT_RG32SI"; + case SG_PIXELFORMAT_RG32F: return "SG_PIXELFORMAT_RG32F"; + case SG_PIXELFORMAT_RGBA16: return "SG_PIXELFORMAT_RGBA16"; + case SG_PIXELFORMAT_RGBA16SN: return "SG_PIXELFORMAT_RGBA16SN"; + case SG_PIXELFORMAT_RGBA16UI: return "SG_PIXELFORMAT_RGBA16UI"; + case SG_PIXELFORMAT_RGBA16SI: return "SG_PIXELFORMAT_RGBA16SI"; + case SG_PIXELFORMAT_RGBA16F: return "SG_PIXELFORMAT_RGBA16F"; + case SG_PIXELFORMAT_RGBA32UI: return "SG_PIXELFORMAT_RGBA32UI"; + case SG_PIXELFORMAT_RGBA32SI: return "SG_PIXELFORMAT_RGBA32SI"; + case SG_PIXELFORMAT_RGBA32F: return "SG_PIXELFORMAT_RGBA32F"; + case SG_PIXELFORMAT_DEPTH: return "SG_PIXELFORMAT_DEPTH"; + case SG_PIXELFORMAT_DEPTH_STENCIL: return "SG_PIXELFORMAT_DEPTH_STENCIL"; + case SG_PIXELFORMAT_BC1_RGBA: return "SG_PIXELFORMAT_BC1_RGBA"; + case SG_PIXELFORMAT_BC2_RGBA: return "SG_PIXELFORMAT_BC2_RGBA"; + case SG_PIXELFORMAT_BC3_RGBA: return "SG_PIXELFORMAT_BC3_RGBA"; + case SG_PIXELFORMAT_BC4_R: return "SG_PIXELFORMAT_BC4_R"; + case SG_PIXELFORMAT_BC4_RSN: return "SG_PIXELFORMAT_BC4_RSN"; + case SG_PIXELFORMAT_BC5_RG: return "SG_PIXELFORMAT_BC5_RG"; + case SG_PIXELFORMAT_BC5_RGSN: return "SG_PIXELFORMAT_BC5_RGSN"; + case SG_PIXELFORMAT_BC6H_RGBF: return "SG_PIXELFORMAT_BC6H_RGBF"; + case SG_PIXELFORMAT_BC6H_RGBUF: return "SG_PIXELFORMAT_BC6H_RGBUF"; + case SG_PIXELFORMAT_BC7_RGBA: return "SG_PIXELFORMAT_BC7_RGBA"; + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: return "SG_PIXELFORMAT_PVRTC_RGB_2BPP"; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: return "SG_PIXELFORMAT_PVRTC_RGB_4BPP"; + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: return "SG_PIXELFORMAT_PVRTC_RGBA_2BPP"; + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: return "SG_PIXELFORMAT_PVRTC_RGBA_4BPP"; + case SG_PIXELFORMAT_ETC2_RGB8: return "SG_PIXELFORMAT_ETC2_RGB8"; + case SG_PIXELFORMAT_ETC2_RGB8A1: return "SG_PIXELFORMAT_ETC2_RGB8A1"; + case SG_PIXELFORMAT_ETC2_RGBA8: return "SG_PIXELFORMAT_ETC2_RGBA8"; + case SG_PIXELFORMAT_EAC_R11: return "SG_PIXELFORMAT_EAC_R11"; + case SG_PIXELFORMAT_EAC_R11SN: return "SG_PIXELFORMAT_EAC_R11SN"; + case SG_PIXELFORMAT_EAC_RG11: return "SG_PIXELFORMAT_EAC_RG11"; + case SG_PIXELFORMAT_EAC_RG11SN: return "SG_PIXELFORMAT_EAC_RG11SN"; + case SG_PIXELFORMAT_RGB9E5: return "SG_PIXELFORMAT_RGB9E5"; + case SG_PIXELFORMAT_BC3_SRGBA: return "SG_PIXELFORMAT_BC3_SRGBA"; + case SG_PIXELFORMAT_BC7_SRGBA: return "SG_PIXELFORMAT_BC7_SRGBA"; + case SG_PIXELFORMAT_ETC2_SRGB8: return "SG_PIXELFORMAT_ETC2_SRGB8"; + case SG_PIXELFORMAT_ETC2_SRGB8A8: return "SG_PIXELFORMAT_ETC2_SRGB8A8"; + case SG_PIXELFORMAT_ASTC_4x4_RGBA: return "SG_PIXELFORMAT_ASTC_4x4_RGBA"; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return "SG_PIXELFORMAT_ASTC_4x4_SRGBA"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_filter_string(sg_filter f) { + switch (f) { + case SG_FILTER_NONE: return "SG_FILTER_NONE"; + case SG_FILTER_NEAREST: return "SG_FILTER_NEAREST"; + case SG_FILTER_LINEAR: return "SG_FILTER_LINEAR"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_wrap_string(sg_wrap w) { + switch (w) { + case SG_WRAP_REPEAT: return "SG_WRAP_REPEAT"; + case SG_WRAP_CLAMP_TO_EDGE: return "SG_WRAP_CLAMP_TO_EDGE"; + case SG_WRAP_CLAMP_TO_BORDER: return "SG_WRAP_CLAMP_TO_BORDER"; + case SG_WRAP_MIRRORED_REPEAT: return "SG_WRAP_MIRRORED_REPEAT"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_bordercolor_string(sg_border_color bc) { + switch (bc) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: return "SG_BORDERCOLOR_TRANSPARENT_BLACK"; + case SG_BORDERCOLOR_OPAQUE_BLACK: return "SG_BORDERCOLOR_OPAQUE_BLACK"; + case SG_BORDERCOLOR_OPAQUE_WHITE: return "SG_BORDERCOLOR_OPAQUE_WHITE"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_uniformtype_string(sg_uniform_type t) { + switch (t) { + case SG_UNIFORMTYPE_FLOAT: return "SG_UNIFORMTYPE_FLOAT"; + case SG_UNIFORMTYPE_FLOAT2: return "SG_UNIFORMTYPE_FLOAT2"; + case SG_UNIFORMTYPE_FLOAT3: return "SG_UNIFORMTYPE_FLOAT3"; + case SG_UNIFORMTYPE_FLOAT4: return "SG_UNIFORMTYPE_FLOAT4"; + case SG_UNIFORMTYPE_INT: return "SG_UNIFORMTYPE_INT"; + case SG_UNIFORMTYPE_INT2: return "SG_UNIFORMTYPE_INT2"; + case SG_UNIFORMTYPE_INT3: return "SG_UNIFORMTYPE_INT3"; + case SG_UNIFORMTYPE_INT4: return "SG_UNIFORMTYPE_INT4"; + case SG_UNIFORMTYPE_MAT4: return "SG_UNIFORMTYPE_MAT4"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_vertexstep_string(sg_vertex_step s) { + switch (s) { + case SG_VERTEXSTEP_PER_VERTEX: return "SG_VERTEXSTEP_PER_VERTEX"; + case SG_VERTEXSTEP_PER_INSTANCE: return "SG_VERTEXSTEP_PER_INSTANCE"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_vertexformat_string(sg_vertex_format f) { + switch (f) { + case SG_VERTEXFORMAT_FLOAT: return "SG_VERTEXFORMAT_FLOAT"; + case SG_VERTEXFORMAT_FLOAT2: return "SG_VERTEXFORMAT_FLOAT2"; + case SG_VERTEXFORMAT_FLOAT3: return "SG_VERTEXFORMAT_FLOAT3"; + case SG_VERTEXFORMAT_FLOAT4: return "SG_VERTEXFORMAT_FLOAT4"; + case SG_VERTEXFORMAT_BYTE4: return "SG_VERTEXFORMAT_BYTE4"; + case SG_VERTEXFORMAT_BYTE4N: return "SG_VERTEXFORMAT_BYTE4N"; + case SG_VERTEXFORMAT_UBYTE4: return "SG_VERTEXFORMAT_UBYTE4"; + case SG_VERTEXFORMAT_UBYTE4N: return "SG_VERTEXFORMAT_UBYTE4N"; + case SG_VERTEXFORMAT_SHORT2: return "SG_VERTEXFORMAT_SHORT2"; + case SG_VERTEXFORMAT_SHORT2N: return "SG_VERTEXFORMAT_SHORT2N"; + case SG_VERTEXFORMAT_USHORT2N: return "SG_VERTEXFORMAT_USHORT2N"; + case SG_VERTEXFORMAT_SHORT4: return "SG_VERTEXFORMAT_SHORT4"; + case SG_VERTEXFORMAT_SHORT4N: return "SG_VERTEXFORMAT_SHORT4N"; + case SG_VERTEXFORMAT_USHORT4N: return "SG_VERTEXFORMAT_USHORT4N"; + case SG_VERTEXFORMAT_UINT10_N2: return "SG_VERTEXFORMAT_UINT10_N2"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_primitivetype_string(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return "SG_PRIMITIVETYPE_POINTS"; + case SG_PRIMITIVETYPE_LINES: return "SG_PRIMITIVETYPE_LINES"; + case SG_PRIMITIVETYPE_LINE_STRIP: return "SG_PRIMITIVETYPE_LINE_STRIP"; + case SG_PRIMITIVETYPE_TRIANGLES: return "SG_PRIMITIVETYPE_TRIANGLES"; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return "SG_PRIMITIVETYPE_TRIANGLE_STRIP"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_indextype_string(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_NONE: return "SG_INDEXTYPE_NONE"; + case SG_INDEXTYPE_UINT16: return "SG_INDEXTYPE_UINT16"; + case SG_INDEXTYPE_UINT32: return "SG_INDEXTYPE_UINT32"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_stencilop_string(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return "SG_STENCILOP_KEEP"; + case SG_STENCILOP_ZERO: return "SG_STENCILOP_ZERO"; + case SG_STENCILOP_REPLACE: return "SG_STENCILOP_REPLACE"; + case SG_STENCILOP_INCR_CLAMP: return "SG_STENCILOP_INCR_CLAMP"; + case SG_STENCILOP_DECR_CLAMP: return "SG_STENCILOP_DECR_CLAMP"; + case SG_STENCILOP_INVERT: return "SG_STENCILOP_INVERT"; + case SG_STENCILOP_INCR_WRAP: return "SG_STENCILOP_INCR_WRAP"; + case SG_STENCILOP_DECR_WRAP: return "SG_STENCILOP_DECR_WRAP"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_comparefunc_string(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return "SG_COMPAREFUNC_NEVER"; + case SG_COMPAREFUNC_LESS: return "SG_COMPAREFUNC_LESS"; + case SG_COMPAREFUNC_EQUAL: return "SG_COMPAREFUNC_EQUAL"; + case SG_COMPAREFUNC_LESS_EQUAL: return "SG_COMPAREFUNC_LESS_EQUAL"; + case SG_COMPAREFUNC_GREATER: return "SG_COMPAREFUNC_GREATER"; + case SG_COMPAREFUNC_NOT_EQUAL: return "SG_COMPAREFUNC_NOT_EQUAL"; + case SG_COMPAREFUNC_GREATER_EQUAL: return "SG_COMPAREFUNC_GREATER_EQUAL"; + case SG_COMPAREFUNC_ALWAYS: return "SG_COMPAREFUNC_ALWAYS"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_blendfactor_string(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return "SG_BLENDFACTOR_ZERO"; + case SG_BLENDFACTOR_ONE: return "SG_BLENDFACTOR_ONE"; + case SG_BLENDFACTOR_SRC_COLOR: return "SG_BLENDFACTOR_SRC_COLOR"; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return "SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR"; + case SG_BLENDFACTOR_SRC_ALPHA: return "SG_BLENDFACTOR_SRC_ALPHA"; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return "SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA"; + case SG_BLENDFACTOR_DST_COLOR: return "SG_BLENDFACTOR_DST_COLOR"; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return "SG_BLENDFACTOR_ONE_MINUS_DST_COLOR"; + case SG_BLENDFACTOR_DST_ALPHA: return "SG_BLENDFACTOR_DST_ALPHA"; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return "SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA"; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return "SG_BLENDFACTOR_SRC_ALPHA_SATURATED"; + case SG_BLENDFACTOR_BLEND_COLOR: return "SG_BLENDFACTOR_BLEND_COLOR"; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return "SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR"; + case SG_BLENDFACTOR_BLEND_ALPHA: return "SG_BLENDFACTOR_BLEND_ALPHA"; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return "SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_blendop_string(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return "SG_BLENDOP_ADD"; + case SG_BLENDOP_SUBTRACT: return "SG_BLENDOP_SUBTRACT"; + case SG_BLENDOP_REVERSE_SUBTRACT: return "SG_BLENDOP_REVERSE_SUBTRACT"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_colormask_string(sg_color_mask m) { + static const char* str[] = { + "NONE", + "R", + "G", + "RG", + "B", + "RB", + "GB", + "RGB", + "A", + "RA", + "GA", + "RGA", + "BA", + "RBA", + "GBA", + "RGBA", + }; + return str[m & 0xF]; +} + +_SOKOL_PRIVATE const char* _sgimgui_cullmode_string(sg_cull_mode cm) { + switch (cm) { + case SG_CULLMODE_NONE: return "SG_CULLMODE_NONE"; + case SG_CULLMODE_FRONT: return "SG_CULLMODE_FRONT"; + case SG_CULLMODE_BACK: return "SG_CULLMODE_BACK"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_facewinding_string(sg_face_winding fw) { + switch (fw) { + case SG_FACEWINDING_CCW: return "SG_FACEWINDING_CCW"; + case SG_FACEWINDING_CW: return "SG_FACEWINDING_CW"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_shaderstage_string(sg_shader_stage stage) { + switch (stage) { + case SG_SHADERSTAGE_VS: return "SG_SHADERSTAGE_VS"; + case SG_SHADERSTAGE_FS: return "SG_SHADERSTAGE_FS"; + default: return "???"; + } +} + +_SOKOL_PRIVATE const char* _sgimgui_bool_string(bool b) { + return b ? "true" : "false"; +} + +_SOKOL_PRIVATE const char* _sgimgui_color_string(sgimgui_str_t* dst_str, sg_color color) { + _sgimgui_snprintf(dst_str, "%.3f %.3f %.3f %.3f", color.r, color.g, color.b, color.a); + return dst_str->buf; +} + +_SOKOL_PRIVATE sgimgui_str_t _sgimgui_res_id_string(uint32_t res_id, const char* label) { + SOKOL_ASSERT(label); + sgimgui_str_t res; + if (label[0]) { + _sgimgui_snprintf(&res, "'%s'", label); + } else { + _sgimgui_snprintf(&res, "0x%08X", res_id); + } + return res; +} + +_SOKOL_PRIVATE sgimgui_str_t _sgimgui_buffer_id_string(sgimgui_t* ctx, sg_buffer buf_id) { + if (buf_id.id != SG_INVALID_ID) { + const sgimgui_buffer_t* buf_ui = &ctx->buffer_window.slots[_sgimgui_slot_index(buf_id.id)]; + return _sgimgui_res_id_string(buf_id.id, buf_ui->label.buf); + } else { + return _sgimgui_make_str(""); + } +} + +_SOKOL_PRIVATE sgimgui_str_t _sgimgui_image_id_string(sgimgui_t* ctx, sg_image img_id) { + if (img_id.id != SG_INVALID_ID) { + const sgimgui_image_t* img_ui = &ctx->image_window.slots[_sgimgui_slot_index(img_id.id)]; + return _sgimgui_res_id_string(img_id.id, img_ui->label.buf); + } else { + return _sgimgui_make_str(""); + } +} + +_SOKOL_PRIVATE sgimgui_str_t _sgimgui_sampler_id_string(sgimgui_t* ctx, sg_sampler smp_id) { + if (smp_id.id != SG_INVALID_ID) { + const sgimgui_sampler_t* smp_ui = &ctx->sampler_window.slots[_sgimgui_slot_index(smp_id.id)]; + return _sgimgui_res_id_string(smp_id.id, smp_ui->label.buf); + } else { + return _sgimgui_make_str(""); + } +} + +_SOKOL_PRIVATE sgimgui_str_t _sgimgui_shader_id_string(sgimgui_t* ctx, sg_shader shd_id) { + if (shd_id.id != SG_INVALID_ID) { + const sgimgui_shader_t* shd_ui = &ctx->shader_window.slots[_sgimgui_slot_index(shd_id.id)]; + return _sgimgui_res_id_string(shd_id.id, shd_ui->label.buf); + } else { + return _sgimgui_make_str(""); + } +} + +_SOKOL_PRIVATE sgimgui_str_t _sgimgui_pipeline_id_string(sgimgui_t* ctx, sg_pipeline pip_id) { + if (pip_id.id != SG_INVALID_ID) { + const sgimgui_pipeline_t* pip_ui = &ctx->pipeline_window.slots[_sgimgui_slot_index(pip_id.id)]; + return _sgimgui_res_id_string(pip_id.id, pip_ui->label.buf); + } else { + return _sgimgui_make_str(""); + } +} + +_SOKOL_PRIVATE sgimgui_str_t _sgimgui_attachments_id_string(sgimgui_t* ctx, sg_attachments atts_id) { + if (atts_id.id != SG_INVALID_ID) { + const sgimgui_attachments_t* atts_ui = &ctx->attachments_window.slots[_sgimgui_slot_index(atts_id.id)]; + return _sgimgui_res_id_string(atts_id.id, atts_ui->label.buf); + } else { + return _sgimgui_make_str(""); + } +} + +/*--- RESOURCE HELPERS -------------------------------------------------------*/ +_SOKOL_PRIVATE void _sgimgui_buffer_created(sgimgui_t* ctx, sg_buffer res_id, int slot_index, const sg_buffer_desc* desc) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->buffer_window.num_slots)); + sgimgui_buffer_t* buf = &ctx->buffer_window.slots[slot_index]; + buf->res_id = res_id; + buf->desc = *desc; + buf->label = _sgimgui_make_str(desc->label); +} + +_SOKOL_PRIVATE void _sgimgui_buffer_destroyed(sgimgui_t* ctx, int slot_index) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->buffer_window.num_slots)); + sgimgui_buffer_t* buf = &ctx->buffer_window.slots[slot_index]; + buf->res_id.id = SG_INVALID_ID; +} + +_SOKOL_PRIVATE void _sgimgui_image_created(sgimgui_t* ctx, sg_image res_id, int slot_index, const sg_image_desc* desc) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->image_window.num_slots)); + sgimgui_image_t* img = &ctx->image_window.slots[slot_index]; + img->res_id = res_id; + img->desc = *desc; + img->ui_scale = 1.0f; + img->label = _sgimgui_make_str(desc->label); + simgui_image_desc_t simgui_img_desc; + _sgimgui_clear(&simgui_img_desc, sizeof(simgui_img_desc)); + simgui_img_desc.image = res_id; + // keep sampler at default, which will use sokol_imgui.h's default nearest-filtering sampler + img->simgui_img = simgui_make_image(&simgui_img_desc); +} + +_SOKOL_PRIVATE void _sgimgui_image_destroyed(sgimgui_t* ctx, int slot_index) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->image_window.num_slots)); + sgimgui_image_t* img = &ctx->image_window.slots[slot_index]; + img->res_id.id = SG_INVALID_ID; + simgui_destroy_image(img->simgui_img); +} + +_SOKOL_PRIVATE void _sgimgui_sampler_created(sgimgui_t* ctx, sg_sampler res_id, int slot_index, const sg_sampler_desc* desc) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->sampler_window.num_slots)); + sgimgui_sampler_t* smp = &ctx->sampler_window.slots[slot_index]; + smp->res_id = res_id; + smp->desc = *desc; + smp->label = _sgimgui_make_str(desc->label); +} + +_SOKOL_PRIVATE void _sgimgui_sampler_destroyed(sgimgui_t* ctx, int slot_index) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->sampler_window.num_slots)); + sgimgui_sampler_t* smp = &ctx->sampler_window.slots[slot_index]; + smp->res_id.id = SG_INVALID_ID; +} + +_SOKOL_PRIVATE void _sgimgui_shader_created(sgimgui_t* ctx, sg_shader res_id, int slot_index, const sg_shader_desc* desc) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->shader_window.num_slots)); + sgimgui_shader_t* shd = &ctx->shader_window.slots[slot_index]; + shd->res_id = res_id; + shd->desc = *desc; + shd->label = _sgimgui_make_str(desc->label); + if (shd->desc.vs.entry) { + shd->vs_entry = _sgimgui_make_str(shd->desc.vs.entry); + shd->desc.vs.entry = shd->vs_entry.buf; + } + if (shd->desc.fs.entry) { + shd->fs_entry = _sgimgui_make_str(shd->desc.fs.entry); + shd->desc.fs.entry = shd->fs_entry.buf; + } + if (shd->desc.vs.d3d11_target) { + shd->vs_d3d11_target = _sgimgui_make_str(shd->desc.vs.d3d11_target); + shd->desc.fs.d3d11_target = shd->vs_d3d11_target.buf; + } + if (shd->desc.fs.d3d11_target) { + shd->fs_d3d11_target = _sgimgui_make_str(shd->desc.fs.d3d11_target); + shd->desc.fs.d3d11_target = shd->fs_d3d11_target.buf; + } + for (int i = 0; i < SG_MAX_SHADERSTAGE_UBS; i++) { + for (int j = 0; j < SG_MAX_UB_MEMBERS; j++) { + sg_shader_uniform_desc* ud = &shd->desc.vs.uniform_blocks[i].uniforms[j]; + if (ud->name) { + shd->vs_uniform_name[i][j] = _sgimgui_make_str(ud->name); + ud->name = shd->vs_uniform_name[i][j].buf; + } + } + } + for (int i = 0; i < SG_MAX_SHADERSTAGE_UBS; i++) { + for (int j = 0; j < SG_MAX_UB_MEMBERS; j++) { + sg_shader_uniform_desc* ud = &shd->desc.fs.uniform_blocks[i].uniforms[j]; + if (ud->name) { + shd->fs_uniform_name[i][j] = _sgimgui_make_str(ud->name); + ud->name = shd->fs_uniform_name[i][j].buf; + } + } + } + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS; i++) { + if (shd->desc.vs.image_sampler_pairs[i].glsl_name) { + shd->vs_image_sampler_name[i] = _sgimgui_make_str(shd->desc.vs.image_sampler_pairs[i].glsl_name); + shd->desc.vs.image_sampler_pairs[i].glsl_name = shd->vs_image_sampler_name[i].buf; + } + } + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS; i++) { + if (shd->desc.fs.image_sampler_pairs[i].glsl_name) { + shd->fs_image_sampler_name[i] = _sgimgui_make_str(shd->desc.fs.image_sampler_pairs[i].glsl_name); + shd->desc.fs.image_sampler_pairs[i].glsl_name = shd->fs_image_sampler_name[i].buf; + } + } + if (shd->desc.vs.source) { + shd->desc.vs.source = _sgimgui_str_dup(&ctx->desc.allocator, shd->desc.vs.source); + } + if (shd->desc.vs.bytecode.ptr) { + shd->desc.vs.bytecode.ptr = _sgimgui_bin_dup(&ctx->desc.allocator, shd->desc.vs.bytecode.ptr, shd->desc.vs.bytecode.size); + } + if (shd->desc.fs.source) { + shd->desc.fs.source = _sgimgui_str_dup(&ctx->desc.allocator, shd->desc.fs.source); + } + if (shd->desc.fs.bytecode.ptr) { + shd->desc.fs.bytecode.ptr = _sgimgui_bin_dup(&ctx->desc.allocator, shd->desc.fs.bytecode.ptr, shd->desc.fs.bytecode.size); + } + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + sg_shader_attr_desc* ad = &shd->desc.attrs[i]; + if (ad->name) { + shd->attr_name[i] = _sgimgui_make_str(ad->name); + ad->name = shd->attr_name[i].buf; + } + if (ad->sem_name) { + shd->attr_sem_name[i] = _sgimgui_make_str(ad->sem_name); + ad->sem_name = shd->attr_sem_name[i].buf; + } + } +} + +_SOKOL_PRIVATE void _sgimgui_shader_destroyed(sgimgui_t* ctx, int slot_index) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->shader_window.num_slots)); + sgimgui_shader_t* shd = &ctx->shader_window.slots[slot_index]; + shd->res_id.id = SG_INVALID_ID; + if (shd->desc.vs.source) { + _sgimgui_free(&ctx->desc.allocator, (void*)shd->desc.vs.source); + shd->desc.vs.source = 0; + } + if (shd->desc.vs.bytecode.ptr) { + _sgimgui_free(&ctx->desc.allocator, (void*)shd->desc.vs.bytecode.ptr); + shd->desc.vs.bytecode.ptr = 0; + } + if (shd->desc.fs.source) { + _sgimgui_free(&ctx->desc.allocator, (void*)shd->desc.fs.source); + shd->desc.fs.source = 0; + } + if (shd->desc.fs.bytecode.ptr) { + _sgimgui_free(&ctx->desc.allocator, (void*)shd->desc.fs.bytecode.ptr); + shd->desc.fs.bytecode.ptr = 0; + } +} + +_SOKOL_PRIVATE void _sgimgui_pipeline_created(sgimgui_t* ctx, sg_pipeline res_id, int slot_index, const sg_pipeline_desc* desc) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->pipeline_window.num_slots)); + sgimgui_pipeline_t* pip = &ctx->pipeline_window.slots[slot_index]; + pip->res_id = res_id; + pip->label = _sgimgui_make_str(desc->label); + pip->desc = *desc; + +} + +_SOKOL_PRIVATE void _sgimgui_pipeline_destroyed(sgimgui_t* ctx, int slot_index) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->pipeline_window.num_slots)); + sgimgui_pipeline_t* pip = &ctx->pipeline_window.slots[slot_index]; + pip->res_id.id = SG_INVALID_ID; +} + +_SOKOL_PRIVATE void _sgimgui_attachments_created(sgimgui_t* ctx, sg_attachments res_id, int slot_index, const sg_attachments_desc* desc) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->attachments_window.num_slots)); + sgimgui_attachments_t* atts = &ctx->attachments_window.slots[slot_index]; + atts->res_id = res_id; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + atts->color_image_scale[i] = 0.25f; + atts->resolve_image_scale[i] = 0.25f; + } + atts->ds_image_scale = 0.25f; + atts->label = _sgimgui_make_str(desc->label); + atts->desc = *desc; +} + +_SOKOL_PRIVATE void _sgimgui_attachments_destroyed(sgimgui_t* ctx, int slot_index) { + SOKOL_ASSERT((slot_index > 0) && (slot_index < ctx->attachments_window.num_slots)); + sgimgui_attachments_t* atts = &ctx->attachments_window.slots[slot_index]; + atts->res_id.id = SG_INVALID_ID; +} + +/*--- COMMAND CAPTURING ------------------------------------------------------*/ +_SOKOL_PRIVATE void _sgimgui_capture_init(sgimgui_t* ctx) { + const size_t ubuf_initial_size = 256 * 1024; + for (int i = 0; i < 2; i++) { + sgimgui_capture_bucket_t* bucket = &ctx->capture_window.bucket[i]; + bucket->ubuf_size = ubuf_initial_size; + bucket->ubuf = (uint8_t*) _sgimgui_malloc(&ctx->desc.allocator, bucket->ubuf_size); + } +} + +_SOKOL_PRIVATE void _sgimgui_capture_discard(sgimgui_t* ctx) { + for (int i = 0; i < 2; i++) { + sgimgui_capture_bucket_t* bucket = &ctx->capture_window.bucket[i]; + SOKOL_ASSERT(bucket->ubuf); + _sgimgui_free(&ctx->desc.allocator, bucket->ubuf); + bucket->ubuf = 0; + } +} + +_SOKOL_PRIVATE sgimgui_capture_bucket_t* _sgimgui_capture_get_write_bucket(sgimgui_t* ctx) { + return &ctx->capture_window.bucket[ctx->capture_window.bucket_index & 1]; +} + +_SOKOL_PRIVATE sgimgui_capture_bucket_t* _sgimgui_capture_get_read_bucket(sgimgui_t* ctx) { + return &ctx->capture_window.bucket[(ctx->capture_window.bucket_index + 1) & 1]; +} + +_SOKOL_PRIVATE void _sgimgui_capture_next_frame(sgimgui_t* ctx) { + ctx->capture_window.bucket_index = (ctx->capture_window.bucket_index + 1) & 1; + sgimgui_capture_bucket_t* bucket = &ctx->capture_window.bucket[ctx->capture_window.bucket_index]; + bucket->num_items = 0; + bucket->ubuf_pos = 0; +} + +_SOKOL_PRIVATE void _sgimgui_capture_grow_ubuf(sgimgui_t* ctx, size_t required_size) { + sgimgui_capture_bucket_t* bucket = _sgimgui_capture_get_write_bucket(ctx); + SOKOL_ASSERT(required_size > bucket->ubuf_size); + size_t old_size = bucket->ubuf_size; + size_t new_size = required_size + (required_size>>1); /* allocate a bit ahead */ + bucket->ubuf_size = new_size; + bucket->ubuf = (uint8_t*) _sgimgui_realloc(&ctx->desc.allocator, bucket->ubuf, old_size, new_size); +} + +_SOKOL_PRIVATE sgimgui_capture_item_t* _sgimgui_capture_next_write_item(sgimgui_t* ctx) { + sgimgui_capture_bucket_t* bucket = _sgimgui_capture_get_write_bucket(ctx); + if (bucket->num_items < sgimgui_MAX_FRAMECAPTURE_ITEMS) { + sgimgui_capture_item_t* item = &bucket->items[bucket->num_items++]; + return item; + } else { + return 0; + } +} + +_SOKOL_PRIVATE int _sgimgui_capture_num_read_items(sgimgui_t* ctx) { + sgimgui_capture_bucket_t* bucket = _sgimgui_capture_get_read_bucket(ctx); + return bucket->num_items; +} + +_SOKOL_PRIVATE sgimgui_capture_item_t* _sgimgui_capture_read_item_at(sgimgui_t* ctx, int index) { + sgimgui_capture_bucket_t* bucket = _sgimgui_capture_get_read_bucket(ctx); + SOKOL_ASSERT(index < bucket->num_items); + return &bucket->items[index]; +} + +_SOKOL_PRIVATE size_t _sgimgui_capture_uniforms(sgimgui_t* ctx, const sg_range* data) { + sgimgui_capture_bucket_t* bucket = _sgimgui_capture_get_write_bucket(ctx); + const size_t required_size = bucket->ubuf_pos + data->size; + if (required_size > bucket->ubuf_size) { + _sgimgui_capture_grow_ubuf(ctx, required_size); + } + SOKOL_ASSERT(required_size <= bucket->ubuf_size); + memcpy(bucket->ubuf + bucket->ubuf_pos, data->ptr, data->size); + const size_t pos = bucket->ubuf_pos; + bucket->ubuf_pos += data->size; + SOKOL_ASSERT(bucket->ubuf_pos <= bucket->ubuf_size); + return pos; +} + +_SOKOL_PRIVATE sgimgui_str_t _sgimgui_capture_item_string(sgimgui_t* ctx, int index, const sgimgui_capture_item_t* item) { + sgimgui_str_t str = _sgimgui_make_str(0); + switch (item->cmd) { + case SGIMGUI_CMD_RESET_STATE_CACHE: + _sgimgui_snprintf(&str, "%d: sg_reset_state_cache()", index); + break; + + case SGIMGUI_CMD_MAKE_BUFFER: + { + sgimgui_str_t res_id = _sgimgui_buffer_id_string(ctx, item->args.make_buffer.result); + _sgimgui_snprintf(&str, "%d: sg_make_buffer(desc=..) => %s", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_MAKE_IMAGE: + { + sgimgui_str_t res_id = _sgimgui_image_id_string(ctx, item->args.make_image.result); + _sgimgui_snprintf(&str, "%d: sg_make_image(desc=..) => %s", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_MAKE_SAMPLER: + { + sgimgui_str_t res_id = _sgimgui_sampler_id_string(ctx, item->args.make_sampler.result); + _sgimgui_snprintf(&str, "%d: sg_make_sampler(desc=..) => %s", index, res_id.buf); + } + break; + case SGIMGUI_CMD_MAKE_SHADER: + { + sgimgui_str_t res_id = _sgimgui_shader_id_string(ctx, item->args.make_shader.result); + _sgimgui_snprintf(&str, "%d: sg_make_shader(desc=..) => %s", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_MAKE_PIPELINE: + { + sgimgui_str_t res_id = _sgimgui_pipeline_id_string(ctx, item->args.make_pipeline.result); + _sgimgui_snprintf(&str, "%d: sg_make_pipeline(desc=..) => %s", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_MAKE_ATTACHMENTS: + { + sgimgui_str_t res_id = _sgimgui_attachments_id_string(ctx, item->args.make_attachments.result); + _sgimgui_snprintf(&str, "%d: sg_make_attachments(desc=..) => %s", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_DESTROY_BUFFER: + { + sgimgui_str_t res_id = _sgimgui_buffer_id_string(ctx, item->args.destroy_buffer.buffer); + _sgimgui_snprintf(&str, "%d: sg_destroy_buffer(buf=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_DESTROY_IMAGE: + { + sgimgui_str_t res_id = _sgimgui_image_id_string(ctx, item->args.destroy_image.image); + _sgimgui_snprintf(&str, "%d: sg_destroy_image(img=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_DESTROY_SAMPLER: + { + sgimgui_str_t res_id = _sgimgui_sampler_id_string(ctx, item->args.destroy_sampler.sampler); + _sgimgui_snprintf(&str, "%d: sg_destroy_sampler(smp=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_DESTROY_SHADER: + { + sgimgui_str_t res_id = _sgimgui_shader_id_string(ctx, item->args.destroy_shader.shader); + _sgimgui_snprintf(&str, "%d: sg_destroy_shader(shd=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_DESTROY_PIPELINE: + { + sgimgui_str_t res_id = _sgimgui_pipeline_id_string(ctx, item->args.destroy_pipeline.pipeline); + _sgimgui_snprintf(&str, "%d: sg_destroy_pipeline(pip=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_DESTROY_ATTACHMENTS: + { + sgimgui_str_t res_id = _sgimgui_attachments_id_string(ctx, item->args.destroy_attachments.attachments); + _sgimgui_snprintf(&str, "%d: sg_destroy_attachments(atts=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_UPDATE_BUFFER: + { + sgimgui_str_t res_id = _sgimgui_buffer_id_string(ctx, item->args.update_buffer.buffer); + _sgimgui_snprintf(&str, "%d: sg_update_buffer(buf=%s, data.size=%d)", + index, res_id.buf, + item->args.update_buffer.data_size); + } + break; + + case SGIMGUI_CMD_UPDATE_IMAGE: + { + sgimgui_str_t res_id = _sgimgui_image_id_string(ctx, item->args.update_image.image); + _sgimgui_snprintf(&str, "%d: sg_update_image(img=%s, data=..)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_APPEND_BUFFER: + { + sgimgui_str_t res_id = _sgimgui_buffer_id_string(ctx, item->args.append_buffer.buffer); + _sgimgui_snprintf(&str, "%d: sg_append_buffer(buf=%s, data.size=%d) => %d", + index, res_id.buf, + item->args.append_buffer.data_size, + item->args.append_buffer.result); + } + break; + + case SGIMGUI_CMD_BEGIN_PASS: + { + _sgimgui_snprintf(&str, "%d: sg_begin_pass(pass=...)", index); + } + break; + + case SGIMGUI_CMD_APPLY_VIEWPORT: + _sgimgui_snprintf(&str, "%d: sg_apply_viewport(x=%d, y=%d, width=%d, height=%d, origin_top_left=%s)", + index, + item->args.apply_viewport.x, + item->args.apply_viewport.y, + item->args.apply_viewport.width, + item->args.apply_viewport.height, + _sgimgui_bool_string(item->args.apply_viewport.origin_top_left)); + break; + + case SGIMGUI_CMD_APPLY_SCISSOR_RECT: + _sgimgui_snprintf(&str, "%d: sg_apply_scissor_rect(x=%d, y=%d, width=%d, height=%d, origin_top_left=%s)", + index, + item->args.apply_scissor_rect.x, + item->args.apply_scissor_rect.y, + item->args.apply_scissor_rect.width, + item->args.apply_scissor_rect.height, + _sgimgui_bool_string(item->args.apply_scissor_rect.origin_top_left)); + break; + + case SGIMGUI_CMD_APPLY_PIPELINE: + { + sgimgui_str_t res_id = _sgimgui_pipeline_id_string(ctx, item->args.apply_pipeline.pipeline); + _sgimgui_snprintf(&str, "%d: sg_apply_pipeline(pip=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_APPLY_BINDINGS: + _sgimgui_snprintf(&str, "%d: sg_apply_bindings(bindings=..)", index); + break; + + case SGIMGUI_CMD_APPLY_UNIFORMS: + _sgimgui_snprintf(&str, "%d: sg_apply_uniforms(stage=%s, ub_index=%d, data.size=%d)", + index, + _sgimgui_shaderstage_string(item->args.apply_uniforms.stage), + item->args.apply_uniforms.ub_index, + item->args.apply_uniforms.data_size); + break; + + case SGIMGUI_CMD_DRAW: + _sgimgui_snprintf(&str, "%d: sg_draw(base_element=%d, num_elements=%d, num_instances=%d)", + index, + item->args.draw.base_element, + item->args.draw.num_elements, + item->args.draw.num_instances); + break; + + case SGIMGUI_CMD_END_PASS: + _sgimgui_snprintf(&str, "%d: sg_end_pass()", index); + break; + + case SGIMGUI_CMD_COMMIT: + _sgimgui_snprintf(&str, "%d: sg_commit()", index); + break; + + case SGIMGUI_CMD_ALLOC_BUFFER: + { + sgimgui_str_t res_id = _sgimgui_buffer_id_string(ctx, item->args.alloc_buffer.result); + _sgimgui_snprintf(&str, "%d: sg_alloc_buffer() => %s", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_ALLOC_IMAGE: + { + sgimgui_str_t res_id = _sgimgui_image_id_string(ctx, item->args.alloc_image.result); + _sgimgui_snprintf(&str, "%d: sg_alloc_image() => %s", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_ALLOC_SAMPLER: + { + sgimgui_str_t res_id = _sgimgui_sampler_id_string(ctx, item->args.alloc_sampler.result); + _sgimgui_snprintf(&str, "%d: sg_alloc_sampler() => %s", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_ALLOC_SHADER: + { + sgimgui_str_t res_id = _sgimgui_shader_id_string(ctx, item->args.alloc_shader.result); + _sgimgui_snprintf(&str, "%d: sg_alloc_shader() => %s", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_ALLOC_PIPELINE: + { + sgimgui_str_t res_id = _sgimgui_pipeline_id_string(ctx, item->args.alloc_pipeline.result); + _sgimgui_snprintf(&str, "%d: sg_alloc_pipeline() => %s", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_ALLOC_ATTACHMENTS: + { + sgimgui_str_t res_id = _sgimgui_attachments_id_string(ctx, item->args.alloc_attachments.result); + _sgimgui_snprintf(&str, "%d: sg_alloc_attachments() => %s", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_DEALLOC_BUFFER: + { + sgimgui_str_t res_id = _sgimgui_buffer_id_string(ctx, item->args.dealloc_buffer.buffer); + _sgimgui_snprintf(&str, "%d: sg_dealloc_buffer(buf=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_DEALLOC_IMAGE: + { + sgimgui_str_t res_id = _sgimgui_image_id_string(ctx, item->args.dealloc_image.image); + _sgimgui_snprintf(&str, "%d: sg_dealloc_image(img=%d)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_DEALLOC_SAMPLER: + { + sgimgui_str_t res_id = _sgimgui_sampler_id_string(ctx, item->args.dealloc_sampler.sampler); + _sgimgui_snprintf(&str, "%d: sg_dealloc_sampler(smp=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_DEALLOC_SHADER: + { + sgimgui_str_t res_id = _sgimgui_shader_id_string(ctx, item->args.dealloc_shader.shader); + _sgimgui_snprintf(&str, "%d: sg_dealloc_shader(shd=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_DEALLOC_PIPELINE: + { + sgimgui_str_t res_id = _sgimgui_pipeline_id_string(ctx, item->args.dealloc_pipeline.pipeline); + _sgimgui_snprintf(&str, "%d: sg_dealloc_pipeline(pip=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_DEALLOC_ATTACHMENTS: + { + sgimgui_str_t res_id = _sgimgui_attachments_id_string(ctx, item->args.dealloc_attachments.attachments); + _sgimgui_snprintf(&str, "%d: sg_dealloc_attachments(atts=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_INIT_BUFFER: + { + sgimgui_str_t res_id = _sgimgui_buffer_id_string(ctx, item->args.init_buffer.buffer); + _sgimgui_snprintf(&str, "%d: sg_init_buffer(buf=%s, desc=..)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_INIT_IMAGE: + { + sgimgui_str_t res_id = _sgimgui_image_id_string(ctx, item->args.init_image.image); + _sgimgui_snprintf(&str, "%d: sg_init_image(img=%s, desc=..)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_INIT_SAMPLER: + { + sgimgui_str_t res_id = _sgimgui_sampler_id_string(ctx, item->args.init_sampler.sampler); + _sgimgui_snprintf(&str, "%d: sg_init_sampler(smp=%s, desc=..)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_INIT_SHADER: + { + sgimgui_str_t res_id = _sgimgui_shader_id_string(ctx, item->args.init_shader.shader); + _sgimgui_snprintf(&str, "%d: sg_init_shader(shd=%s, desc=..)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_INIT_PIPELINE: + { + sgimgui_str_t res_id = _sgimgui_pipeline_id_string(ctx, item->args.init_pipeline.pipeline); + _sgimgui_snprintf(&str, "%d: sg_init_pipeline(pip=%s, desc=..)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_INIT_ATTACHMENTS: + { + sgimgui_str_t res_id = _sgimgui_attachments_id_string(ctx, item->args.init_attachments.attachments); + _sgimgui_snprintf(&str, "%d: sg_init_attachments(atts=%s, desc=..)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_UNINIT_BUFFER: + { + sgimgui_str_t res_id = _sgimgui_buffer_id_string(ctx, item->args.uninit_buffer.buffer); + _sgimgui_snprintf(&str, "%d: sg_uninit_buffer(buf=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_UNINIT_IMAGE: + { + sgimgui_str_t res_id = _sgimgui_image_id_string(ctx, item->args.uninit_image.image); + _sgimgui_snprintf(&str, "%d: sg_uninit_image(img=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_UNINIT_SAMPLER: + { + sgimgui_str_t res_id = _sgimgui_sampler_id_string(ctx, item->args.uninit_sampler.sampler); + _sgimgui_snprintf(&str, "%d: sg_uninit_sampler(smp=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_UNINIT_SHADER: + { + sgimgui_str_t res_id = _sgimgui_shader_id_string(ctx, item->args.uninit_shader.shader); + _sgimgui_snprintf(&str, "%d: sg_uninit_shader(shd=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_UNINIT_PIPELINE: + { + sgimgui_str_t res_id = _sgimgui_pipeline_id_string(ctx, item->args.uninit_pipeline.pipeline); + _sgimgui_snprintf(&str, "%d: sg_uninit_pipeline(pip=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_UNINIT_ATTACHMENTS: + { + sgimgui_str_t res_id = _sgimgui_attachments_id_string(ctx, item->args.uninit_attachments.attachments); + _sgimgui_snprintf(&str, "%d: sg_uninit_attachments(atts=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_FAIL_BUFFER: + { + sgimgui_str_t res_id = _sgimgui_buffer_id_string(ctx, item->args.fail_buffer.buffer); + _sgimgui_snprintf(&str, "%d: sg_fail_buffer(buf=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_FAIL_IMAGE: + { + sgimgui_str_t res_id = _sgimgui_image_id_string(ctx, item->args.fail_image.image); + _sgimgui_snprintf(&str, "%d: sg_fail_image(img=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_FAIL_SAMPLER: + { + sgimgui_str_t res_id = _sgimgui_sampler_id_string(ctx, item->args.fail_sampler.sampler); + _sgimgui_snprintf(&str, "%d: sg_fail_sampler(smp=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_FAIL_SHADER: + { + sgimgui_str_t res_id = _sgimgui_shader_id_string(ctx, item->args.fail_shader.shader); + _sgimgui_snprintf(&str, "%d: sg_fail_shader(shd=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_FAIL_PIPELINE: + { + sgimgui_str_t res_id = _sgimgui_pipeline_id_string(ctx, item->args.fail_pipeline.pipeline); + _sgimgui_snprintf(&str, "%d: sg_fail_pipeline(shd=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_FAIL_ATTACHMENTS: + { + sgimgui_str_t res_id = _sgimgui_attachments_id_string(ctx, item->args.fail_attachments.attachments); + _sgimgui_snprintf(&str, "%d: sg_fail_attachments(atts=%s)", index, res_id.buf); + } + break; + + case SGIMGUI_CMD_PUSH_DEBUG_GROUP: + _sgimgui_snprintf(&str, "%d: sg_push_debug_group(name=%s)", index, + item->args.push_debug_group.name.buf); + break; + + case SGIMGUI_CMD_POP_DEBUG_GROUP: + _sgimgui_snprintf(&str, "%d: sg_pop_debug_group()", index); + break; + + default: + _sgimgui_snprintf(&str, "%d: ???", index); + break; + } + return str; +} + +/*--- CAPTURE CALLBACKS ------------------------------------------------------*/ +_SOKOL_PRIVATE void _sgimgui_reset_state_cache(void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_RESET_STATE_CACHE; + item->color = _SGIMGUI_COLOR_OTHER; + } + if (ctx->hooks.reset_state_cache) { + ctx->hooks.reset_state_cache(ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_make_buffer(const sg_buffer_desc* desc, sg_buffer buf_id, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_MAKE_BUFFER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.make_buffer.result = buf_id; + } + if (ctx->hooks.make_buffer) { + ctx->hooks.make_buffer(desc, buf_id, ctx->hooks.user_data); + } + if (buf_id.id != SG_INVALID_ID) { + _sgimgui_buffer_created(ctx, buf_id, _sgimgui_slot_index(buf_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_make_image(const sg_image_desc* desc, sg_image img_id, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_MAKE_IMAGE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.make_image.result = img_id; + } + if (ctx->hooks.make_image) { + ctx->hooks.make_image(desc, img_id, ctx->hooks.user_data); + } + if (img_id.id != SG_INVALID_ID) { + _sgimgui_image_created(ctx, img_id, _sgimgui_slot_index(img_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_make_sampler(const sg_sampler_desc* desc, sg_sampler smp_id, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_MAKE_SAMPLER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.make_sampler.result = smp_id; + } + if (ctx->hooks.make_sampler) { + ctx->hooks.make_sampler(desc, smp_id, ctx->hooks.user_data); + } + if (smp_id.id != SG_INVALID_ID) { + _sgimgui_sampler_created(ctx, smp_id, _sgimgui_slot_index(smp_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_make_shader(const sg_shader_desc* desc, sg_shader shd_id, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_MAKE_SHADER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.make_shader.result = shd_id; + } + if (ctx->hooks.make_shader) { + ctx->hooks.make_shader(desc, shd_id, ctx->hooks.user_data); + } + if (shd_id.id != SG_INVALID_ID) { + _sgimgui_shader_created(ctx, shd_id, _sgimgui_slot_index(shd_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_make_pipeline(const sg_pipeline_desc* desc, sg_pipeline pip_id, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_MAKE_PIPELINE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.make_pipeline.result = pip_id; + } + if (ctx->hooks.make_pipeline) { + ctx->hooks.make_pipeline(desc, pip_id, ctx->hooks.user_data); + } + if (pip_id.id != SG_INVALID_ID) { + _sgimgui_pipeline_created(ctx, pip_id, _sgimgui_slot_index(pip_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_make_attachments(const sg_attachments_desc* desc, sg_attachments atts_id, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_MAKE_ATTACHMENTS; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.make_attachments.result = atts_id; + } + if (ctx->hooks.make_attachments) { + ctx->hooks.make_attachments(desc, atts_id, ctx->hooks.user_data); + } + if (atts_id.id != SG_INVALID_ID) { + _sgimgui_attachments_created(ctx, atts_id, _sgimgui_slot_index(atts_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_destroy_buffer(sg_buffer buf, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_DESTROY_BUFFER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.destroy_buffer.buffer = buf; + } + if (ctx->hooks.destroy_buffer) { + ctx->hooks.destroy_buffer(buf, ctx->hooks.user_data); + } + if (buf.id != SG_INVALID_ID) { + _sgimgui_buffer_destroyed(ctx, _sgimgui_slot_index(buf.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_destroy_image(sg_image img, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_DESTROY_IMAGE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.destroy_image.image = img; + } + if (ctx->hooks.destroy_image) { + ctx->hooks.destroy_image(img, ctx->hooks.user_data); + } + if (img.id != SG_INVALID_ID) { + _sgimgui_image_destroyed(ctx, _sgimgui_slot_index(img.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_destroy_sampler(sg_sampler smp, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_DESTROY_SAMPLER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.destroy_sampler.sampler = smp; + } + if (ctx->hooks.destroy_sampler) { + ctx->hooks.destroy_sampler(smp, ctx->hooks.user_data); + } + if (smp.id != SG_INVALID_ID) { + _sgimgui_sampler_destroyed(ctx, _sgimgui_slot_index(smp.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_destroy_shader(sg_shader shd, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_DESTROY_SHADER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.destroy_shader.shader = shd; + } + if (ctx->hooks.destroy_shader) { + ctx->hooks.destroy_shader(shd, ctx->hooks.user_data); + } + if (shd.id != SG_INVALID_ID) { + _sgimgui_shader_destroyed(ctx, _sgimgui_slot_index(shd.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_destroy_pipeline(sg_pipeline pip, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_DESTROY_PIPELINE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.destroy_pipeline.pipeline = pip; + } + if (ctx->hooks.destroy_pipeline) { + ctx->hooks.destroy_pipeline(pip, ctx->hooks.user_data); + } + if (pip.id != SG_INVALID_ID) { + _sgimgui_pipeline_destroyed(ctx, _sgimgui_slot_index(pip.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_destroy_attachments(sg_attachments atts, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_DESTROY_ATTACHMENTS; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.destroy_attachments.attachments = atts; + } + if (ctx->hooks.destroy_attachments) { + ctx->hooks.destroy_attachments(atts, ctx->hooks.user_data); + } + if (atts.id != SG_INVALID_ID) { + _sgimgui_attachments_destroyed(ctx, _sgimgui_slot_index(atts.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_update_buffer(sg_buffer buf, const sg_range* data, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_UPDATE_BUFFER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.update_buffer.buffer = buf; + item->args.update_buffer.data_size = data->size; + } + if (ctx->hooks.update_buffer) { + ctx->hooks.update_buffer(buf, data, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_update_image(sg_image img, const sg_image_data* data, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_UPDATE_IMAGE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.update_image.image = img; + } + if (ctx->hooks.update_image) { + ctx->hooks.update_image(img, data, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_append_buffer(sg_buffer buf, const sg_range* data, int result, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_APPEND_BUFFER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.append_buffer.buffer = buf; + item->args.append_buffer.data_size = data->size; + item->args.append_buffer.result = result; + } + if (ctx->hooks.append_buffer) { + ctx->hooks.append_buffer(buf, data, result, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_begin_pass(const sg_pass* pass, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + SOKOL_ASSERT(pass); + item->cmd = SGIMGUI_CMD_BEGIN_PASS; + item->color = _SGIMGUI_COLOR_PASS; + item->args.begin_pass.pass = *pass; + } + if (ctx->hooks.begin_pass) { + ctx->hooks.begin_pass(pass, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_apply_viewport(int x, int y, int width, int height, bool origin_top_left, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_APPLY_VIEWPORT; + item->color = _SGIMGUI_COLOR_APPLY; + item->args.apply_viewport.x = x; + item->args.apply_viewport.y = y; + item->args.apply_viewport.width = width; + item->args.apply_viewport.height = height; + item->args.apply_viewport.origin_top_left = origin_top_left; + } + if (ctx->hooks.apply_viewport) { + ctx->hooks.apply_viewport(x, y, width, height, origin_top_left, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_APPLY_SCISSOR_RECT; + item->color = _SGIMGUI_COLOR_APPLY; + item->args.apply_scissor_rect.x = x; + item->args.apply_scissor_rect.y = y; + item->args.apply_scissor_rect.width = width; + item->args.apply_scissor_rect.height = height; + item->args.apply_scissor_rect.origin_top_left = origin_top_left; + } + if (ctx->hooks.apply_scissor_rect) { + ctx->hooks.apply_scissor_rect(x, y, width, height, origin_top_left, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_apply_pipeline(sg_pipeline pip, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + ctx->cur_pipeline = pip; /* stored for _sgimgui_apply_uniforms */ + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_APPLY_PIPELINE; + item->color = _SGIMGUI_COLOR_APPLY; + item->args.apply_pipeline.pipeline = pip; + } + if (ctx->hooks.apply_pipeline) { + ctx->hooks.apply_pipeline(pip, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_apply_bindings(const sg_bindings* bindings, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + SOKOL_ASSERT(bindings); + item->cmd = SGIMGUI_CMD_APPLY_BINDINGS; + item->color = _SGIMGUI_COLOR_APPLY; + item->args.apply_bindings.bindings = *bindings; + } + if (ctx->hooks.apply_bindings) { + ctx->hooks.apply_bindings(bindings, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + SOKOL_ASSERT(data); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_APPLY_UNIFORMS; + item->color = _SGIMGUI_COLOR_APPLY; + sgimgui_args_apply_uniforms_t* args = &item->args.apply_uniforms; + args->stage = stage; + args->ub_index = ub_index; + args->data_size = data->size; + args->pipeline = ctx->cur_pipeline; + args->ubuf_pos = _sgimgui_capture_uniforms(ctx, data); + } + if (ctx->hooks.apply_uniforms) { + ctx->hooks.apply_uniforms(stage, ub_index, data, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw(int base_element, int num_elements, int num_instances, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_DRAW; + item->color = _SGIMGUI_COLOR_DRAW; + item->args.draw.base_element = base_element; + item->args.draw.num_elements = num_elements; + item->args.draw.num_instances = num_instances; + } + if (ctx->hooks.draw) { + ctx->hooks.draw(base_element, num_elements, num_instances, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_end_pass(void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + ctx->cur_pipeline.id = SG_INVALID_ID; + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_END_PASS; + item->color = _SGIMGUI_COLOR_PASS; + } + if (ctx->hooks.end_pass) { + ctx->hooks.end_pass(ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_commit(void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_COMMIT; + item->color = _SGIMGUI_COLOR_OTHER; + } + _sgimgui_capture_next_frame(ctx); + if (ctx->hooks.commit) { + ctx->hooks.commit(ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_alloc_buffer(sg_buffer result, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_ALLOC_BUFFER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.alloc_buffer.result = result; + } + if (ctx->hooks.alloc_buffer) { + ctx->hooks.alloc_buffer(result, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_alloc_image(sg_image result, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_ALLOC_IMAGE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.alloc_image.result = result; + } + if (ctx->hooks.alloc_image) { + ctx->hooks.alloc_image(result, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_alloc_sampler(sg_sampler result, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_ALLOC_SAMPLER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.alloc_sampler.result = result; + } + if (ctx->hooks.alloc_sampler) { + ctx->hooks.alloc_sampler(result, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_alloc_shader(sg_shader result, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_ALLOC_SHADER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.alloc_shader.result = result; + } + if (ctx->hooks.alloc_shader) { + ctx->hooks.alloc_shader(result, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_alloc_pipeline(sg_pipeline result, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_ALLOC_PIPELINE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.alloc_pipeline.result = result; + } + if (ctx->hooks.alloc_pipeline) { + ctx->hooks.alloc_pipeline(result, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_alloc_attachments(sg_attachments result, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_ALLOC_ATTACHMENTS; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.alloc_attachments.result = result; + } + if (ctx->hooks.alloc_attachments) { + ctx->hooks.alloc_attachments(result, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_dealloc_buffer(sg_buffer buf_id, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_DEALLOC_BUFFER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.dealloc_buffer.buffer = buf_id; + } + if (ctx->hooks.dealloc_buffer) { + ctx->hooks.dealloc_buffer(buf_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_dealloc_image(sg_image img_id, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_DEALLOC_IMAGE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.dealloc_image.image = img_id; + } + if (ctx->hooks.dealloc_image) { + ctx->hooks.dealloc_image(img_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_dealloc_sampler(sg_sampler smp_id, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_DEALLOC_SAMPLER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.dealloc_sampler.sampler = smp_id; + } + if (ctx->hooks.dealloc_sampler) { + ctx->hooks.dealloc_sampler(smp_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_dealloc_shader(sg_shader shd_id, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_DEALLOC_SHADER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.dealloc_shader.shader = shd_id; + } + if (ctx->hooks.dealloc_shader) { + ctx->hooks.dealloc_shader(shd_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_dealloc_pipeline(sg_pipeline pip_id, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_DEALLOC_PIPELINE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.dealloc_pipeline.pipeline = pip_id; + } + if (ctx->hooks.dealloc_pipeline) { + ctx->hooks.dealloc_pipeline(pip_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_dealloc_attachments(sg_attachments atts_id, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_DEALLOC_ATTACHMENTS; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.dealloc_attachments.attachments = atts_id; + } + if (ctx->hooks.dealloc_attachments) { + ctx->hooks.dealloc_attachments(atts_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_INIT_BUFFER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.init_buffer.buffer = buf_id; + } + if (ctx->hooks.init_buffer) { + ctx->hooks.init_buffer(buf_id, desc, ctx->hooks.user_data); + } + if (buf_id.id != SG_INVALID_ID) { + _sgimgui_buffer_created(ctx, buf_id, _sgimgui_slot_index(buf_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_init_image(sg_image img_id, const sg_image_desc* desc, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_INIT_IMAGE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.init_image.image = img_id; + } + if (ctx->hooks.init_image) { + ctx->hooks.init_image(img_id, desc, ctx->hooks.user_data); + } + if (img_id.id != SG_INVALID_ID) { + _sgimgui_image_created(ctx, img_id, _sgimgui_slot_index(img_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_init_sampler(sg_sampler smp_id, const sg_sampler_desc* desc, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_INIT_SAMPLER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.init_sampler.sampler = smp_id; + } + if (ctx->hooks.init_sampler) { + ctx->hooks.init_sampler(smp_id, desc, ctx->hooks.user_data); + } + if (smp_id.id != SG_INVALID_ID) { + _sgimgui_sampler_created(ctx, smp_id, _sgimgui_slot_index(smp_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_init_shader(sg_shader shd_id, const sg_shader_desc* desc, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_INIT_SHADER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.init_shader.shader = shd_id; + } + if (ctx->hooks.init_shader) { + ctx->hooks.init_shader(shd_id, desc, ctx->hooks.user_data); + } + if (shd_id.id != SG_INVALID_ID) { + _sgimgui_shader_created(ctx, shd_id, _sgimgui_slot_index(shd_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_INIT_PIPELINE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.init_pipeline.pipeline = pip_id; + } + if (ctx->hooks.init_pipeline) { + ctx->hooks.init_pipeline(pip_id, desc, ctx->hooks.user_data); + } + if (pip_id.id != SG_INVALID_ID) { + _sgimgui_pipeline_created(ctx, pip_id, _sgimgui_slot_index(pip_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_init_attachments(sg_attachments atts_id, const sg_attachments_desc* desc, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_INIT_ATTACHMENTS; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.init_attachments.attachments = atts_id; + } + if (ctx->hooks.init_attachments) { + ctx->hooks.init_attachments(atts_id, desc, ctx->hooks.user_data); + } + if (atts_id.id != SG_INVALID_ID) { + _sgimgui_attachments_created(ctx, atts_id, _sgimgui_slot_index(atts_id.id), desc); + } +} + +_SOKOL_PRIVATE void _sgimgui_uninit_buffer(sg_buffer buf, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_UNINIT_BUFFER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.uninit_buffer.buffer = buf; + } + if (ctx->hooks.uninit_buffer) { + ctx->hooks.uninit_buffer(buf, ctx->hooks.user_data); + } + if (buf.id != SG_INVALID_ID) { + _sgimgui_buffer_destroyed(ctx, _sgimgui_slot_index(buf.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_uninit_image(sg_image img, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_UNINIT_IMAGE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.uninit_image.image = img; + } + if (ctx->hooks.uninit_image) { + ctx->hooks.uninit_image(img, ctx->hooks.user_data); + } + if (img.id != SG_INVALID_ID) { + _sgimgui_image_destroyed(ctx, _sgimgui_slot_index(img.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_uninit_sampler(sg_sampler smp, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_UNINIT_SAMPLER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.uninit_sampler.sampler = smp; + } + if (ctx->hooks.uninit_sampler) { + ctx->hooks.uninit_sampler(smp, ctx->hooks.user_data); + } + if (smp.id != SG_INVALID_ID) { + _sgimgui_sampler_destroyed(ctx, _sgimgui_slot_index(smp.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_uninit_shader(sg_shader shd, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_UNINIT_SHADER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.uninit_shader.shader = shd; + } + if (ctx->hooks.uninit_shader) { + ctx->hooks.uninit_shader(shd, ctx->hooks.user_data); + } + if (shd.id != SG_INVALID_ID) { + _sgimgui_shader_destroyed(ctx, _sgimgui_slot_index(shd.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_uninit_pipeline(sg_pipeline pip, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_UNINIT_PIPELINE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.uninit_pipeline.pipeline = pip; + } + if (ctx->hooks.uninit_pipeline) { + ctx->hooks.uninit_pipeline(pip, ctx->hooks.user_data); + } + if (pip.id != SG_INVALID_ID) { + _sgimgui_pipeline_destroyed(ctx, _sgimgui_slot_index(pip.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_uninit_attachments(sg_attachments atts, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_UNINIT_PIPELINE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.uninit_attachments.attachments = atts; + } + if (ctx->hooks.uninit_attachments) { + ctx->hooks.uninit_attachments(atts, ctx->hooks.user_data); + } + if (atts.id != SG_INVALID_ID) { + _sgimgui_attachments_destroyed(ctx, _sgimgui_slot_index(atts.id)); + } +} + +_SOKOL_PRIVATE void _sgimgui_fail_buffer(sg_buffer buf_id, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_FAIL_BUFFER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.fail_buffer.buffer = buf_id; + } + if (ctx->hooks.fail_buffer) { + ctx->hooks.fail_buffer(buf_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_fail_image(sg_image img_id, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_FAIL_IMAGE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.fail_image.image = img_id; + } + if (ctx->hooks.fail_image) { + ctx->hooks.fail_image(img_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_fail_sampler(sg_sampler smp_id, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_FAIL_SAMPLER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.fail_sampler.sampler = smp_id; + } + if (ctx->hooks.fail_sampler) { + ctx->hooks.fail_sampler(smp_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_fail_shader(sg_shader shd_id, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_FAIL_SHADER; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.fail_shader.shader = shd_id; + } + if (ctx->hooks.fail_shader) { + ctx->hooks.fail_shader(shd_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_fail_pipeline(sg_pipeline pip_id, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_FAIL_PIPELINE; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.fail_pipeline.pipeline = pip_id; + } + if (ctx->hooks.fail_pipeline) { + ctx->hooks.fail_pipeline(pip_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_fail_attachments(sg_attachments atts_id, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_FAIL_ATTACHMENTS; + item->color = _SGIMGUI_COLOR_RSRC; + item->args.fail_attachments.attachments = atts_id; + } + if (ctx->hooks.fail_attachments) { + ctx->hooks.fail_attachments(atts_id, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_push_debug_group(const char* name, void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + if (0 == strcmp(name, "sokol-imgui")) { + ctx->frame_stats_window.in_sokol_imgui = true; + if (ctx->frame_stats_window.disable_sokol_imgui_stats) { + sg_disable_frame_stats(); + } + } + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_PUSH_DEBUG_GROUP; + item->color = _SGIMGUI_COLOR_OTHER; + item->args.push_debug_group.name = _sgimgui_make_str(name); + } + if (ctx->hooks.push_debug_group) { + ctx->hooks.push_debug_group(name, ctx->hooks.user_data); + } +} + +_SOKOL_PRIVATE void _sgimgui_pop_debug_group(void* user_data) { + sgimgui_t* ctx = (sgimgui_t*) user_data; + SOKOL_ASSERT(ctx); + if (ctx->frame_stats_window.in_sokol_imgui) { + ctx->frame_stats_window.in_sokol_imgui = false; + if (ctx->frame_stats_window.disable_sokol_imgui_stats) { + sg_enable_frame_stats(); + } + } + sgimgui_capture_item_t* item = _sgimgui_capture_next_write_item(ctx); + if (item) { + item->cmd = SGIMGUI_CMD_POP_DEBUG_GROUP; + item->color = _SGIMGUI_COLOR_OTHER; + } + if (ctx->hooks.pop_debug_group) { + ctx->hooks.pop_debug_group(ctx->hooks.user_data); + } +} + +/*--- IMGUI HELPERS ----------------------------------------------------------*/ +_SOKOL_PRIVATE bool _sgimgui_draw_resid_list_item(uint32_t res_id, const char* label, bool selected) { + igPushID_Int((int)res_id); + bool res; + if (label[0]) { + res = igSelectable_Bool(label, selected, 0, IMVEC2(0,0)); + } else { + sgimgui_str_t str; + _sgimgui_snprintf(&str, "0x%08X", res_id); + res = igSelectable_Bool(str.buf, selected, 0, IMVEC2(0,0)); + } + igPopID(); + return res; +} + +_SOKOL_PRIVATE bool _sgimgui_draw_resid_link(uint32_t res_type, uint32_t res_id, const char* label) { + SOKOL_ASSERT(label); + sgimgui_str_t str_buf; + const char* str; + if (label[0]) { + str = label; + } else { + _sgimgui_snprintf(&str_buf, "0x%08X", res_id); + str = str_buf.buf; + } + igPushID_Int((int)((res_type<<24)|res_id)); + bool res = igSmallButton(str); + igPopID(); + return res; +} + +_SOKOL_PRIVATE bool _sgimgui_draw_buffer_link(sgimgui_t* ctx, sg_buffer buf) { + bool retval = false; + if (buf.id != SG_INVALID_ID) { + const sgimgui_buffer_t* buf_ui = &ctx->buffer_window.slots[_sgimgui_slot_index(buf.id)]; + retval = _sgimgui_draw_resid_link(1, buf.id, buf_ui->label.buf); + } + return retval; +} + +_SOKOL_PRIVATE bool _sgimgui_draw_image_link(sgimgui_t* ctx, sg_image img) { + bool retval = false; + if (img.id != SG_INVALID_ID) { + const sgimgui_image_t* img_ui = &ctx->image_window.slots[_sgimgui_slot_index(img.id)]; + retval = _sgimgui_draw_resid_link(2, img.id, img_ui->label.buf); + } + return retval; +} + +_SOKOL_PRIVATE bool _sgimgui_draw_sampler_link(sgimgui_t* ctx, sg_sampler smp) { + bool retval = false; + if (smp.id != SG_INVALID_ID) { + const sgimgui_sampler_t* smp_ui = &ctx->sampler_window.slots[_sgimgui_slot_index(smp.id)]; + retval = _sgimgui_draw_resid_link(2, smp.id, smp_ui->label.buf); + } + return retval; +} + +_SOKOL_PRIVATE bool _sgimgui_draw_shader_link(sgimgui_t* ctx, sg_shader shd) { + bool retval = false; + if (shd.id != SG_INVALID_ID) { + const sgimgui_shader_t* shd_ui = &ctx->shader_window.slots[_sgimgui_slot_index(shd.id)]; + retval = _sgimgui_draw_resid_link(3, shd.id, shd_ui->label.buf); + } + return retval; +} + +_SOKOL_PRIVATE void _sgimgui_show_buffer(sgimgui_t* ctx, sg_buffer buf) { + ctx->buffer_window.open = true; + ctx->buffer_window.sel_buf = buf; +} + +_SOKOL_PRIVATE void _sgimgui_show_image(sgimgui_t* ctx, sg_image img) { + ctx->image_window.open = true; + ctx->image_window.sel_img = img; +} + +_SOKOL_PRIVATE void _sgimgui_show_sampler(sgimgui_t* ctx, sg_sampler smp) { + ctx->sampler_window.open = true; + ctx->sampler_window.sel_smp = smp; +} + +_SOKOL_PRIVATE void _sgimgui_show_shader(sgimgui_t* ctx, sg_shader shd) { + ctx->shader_window.open = true; + ctx->shader_window.sel_shd = shd; +} + +_SOKOL_PRIVATE void _sgimgui_draw_buffer_list(sgimgui_t* ctx) { + igBeginChild_Str("buffer_list", IMVEC2(_SGIMGUI_LIST_WIDTH,0), true, 0); + for (int i = 0; i < ctx->buffer_window.num_slots; i++) { + sg_buffer buf = ctx->buffer_window.slots[i].res_id; + sg_resource_state state = sg_query_buffer_state(buf); + if ((state != SG_RESOURCESTATE_INVALID) && (state != SG_RESOURCESTATE_INITIAL)) { + bool selected = ctx->buffer_window.sel_buf.id == buf.id; + if (_sgimgui_draw_resid_list_item(buf.id, ctx->buffer_window.slots[i].label.buf, selected)) { + ctx->buffer_window.sel_buf.id = buf.id; + } + } + } + igEndChild(); +} + +_SOKOL_PRIVATE void _sgimgui_draw_image_list(sgimgui_t* ctx) { + igBeginChild_Str("image_list", IMVEC2(_SGIMGUI_LIST_WIDTH,0), true, 0); + for (int i = 0; i < ctx->image_window.num_slots; i++) { + sg_image img = ctx->image_window.slots[i].res_id; + sg_resource_state state = sg_query_image_state(img); + if ((state != SG_RESOURCESTATE_INVALID) && (state != SG_RESOURCESTATE_INITIAL)) { + bool selected = ctx->image_window.sel_img.id == img.id; + if (_sgimgui_draw_resid_list_item(img.id, ctx->image_window.slots[i].label.buf, selected)) { + ctx->image_window.sel_img.id = img.id; + } + } + } + igEndChild(); +} + +_SOKOL_PRIVATE void _sgimgui_draw_sampler_list(sgimgui_t* ctx) { + igBeginChild_Str("sampler_list", IMVEC2(_SGIMGUI_LIST_WIDTH,0), true, 0); + for (int i = 0; i < ctx->sampler_window.num_slots; i++) { + sg_sampler smp = ctx->sampler_window.slots[i].res_id; + sg_resource_state state = sg_query_sampler_state(smp); + if ((state != SG_RESOURCESTATE_INVALID) && (state != SG_RESOURCESTATE_INITIAL)) { + bool selected = ctx->sampler_window.sel_smp.id == smp.id; + if (_sgimgui_draw_resid_list_item(smp.id, ctx->sampler_window.slots[i].label.buf, selected)) { + ctx->sampler_window.sel_smp.id = smp.id; + } + } + } + igEndChild(); +} + +_SOKOL_PRIVATE void _sgimgui_draw_shader_list(sgimgui_t* ctx) { + igBeginChild_Str("shader_list", IMVEC2(_SGIMGUI_LIST_WIDTH,0), true, 0); + for (int i = 0; i < ctx->shader_window.num_slots; i++) { + sg_shader shd = ctx->shader_window.slots[i].res_id; + sg_resource_state state = sg_query_shader_state(shd); + if ((state != SG_RESOURCESTATE_INVALID) && (state != SG_RESOURCESTATE_INITIAL)) { + bool selected = ctx->shader_window.sel_shd.id == shd.id; + if (_sgimgui_draw_resid_list_item(shd.id, ctx->shader_window.slots[i].label.buf, selected)) { + ctx->shader_window.sel_shd.id = shd.id; + } + } + } + igEndChild(); +} + +_SOKOL_PRIVATE void _sgimgui_draw_pipeline_list(sgimgui_t* ctx) { + igBeginChild_Str("pipeline_list", IMVEC2(_SGIMGUI_LIST_WIDTH,0), true, 0); + for (int i = 1; i < ctx->pipeline_window.num_slots; i++) { + sg_pipeline pip = ctx->pipeline_window.slots[i].res_id; + sg_resource_state state = sg_query_pipeline_state(pip); + if ((state != SG_RESOURCESTATE_INVALID) && (state != SG_RESOURCESTATE_INITIAL)) { + bool selected = ctx->pipeline_window.sel_pip.id == pip.id; + if (_sgimgui_draw_resid_list_item(pip.id, ctx->pipeline_window.slots[i].label.buf, selected)) { + ctx->pipeline_window.sel_pip.id = pip.id; + } + } + } + igEndChild(); +} + +_SOKOL_PRIVATE void _sgimgui_draw_attachments_list(sgimgui_t* ctx) { + igBeginChild_Str("pass_list", IMVEC2(_SGIMGUI_LIST_WIDTH,0), true, 0); + for (int i = 1; i < ctx->attachments_window.num_slots; i++) { + sg_attachments atts = ctx->attachments_window.slots[i].res_id; + sg_resource_state state = sg_query_attachments_state(atts); + if ((state != SG_RESOURCESTATE_INVALID) && (state != SG_RESOURCESTATE_INITIAL)) { + bool selected = ctx->attachments_window.sel_atts.id == atts.id; + if (_sgimgui_draw_resid_list_item(atts.id, ctx->attachments_window.slots[i].label.buf, selected)) { + ctx->attachments_window.sel_atts.id = atts.id; + } + } + } + igEndChild(); +} + +_SOKOL_PRIVATE void _sgimgui_draw_capture_list(sgimgui_t* ctx) { + igBeginChild_Str("capture_list", IMVEC2(_SGIMGUI_LIST_WIDTH,0), true, 0); + const int num_items = _sgimgui_capture_num_read_items(ctx); + uint64_t group_stack = 1; /* bit set: group unfolded, cleared: folded */ + for (int i = 0; i < num_items; i++) { + const sgimgui_capture_item_t* item = _sgimgui_capture_read_item_at(ctx, i); + sgimgui_str_t item_string = _sgimgui_capture_item_string(ctx, i, item); + igPushStyleColor_U32(ImGuiCol_Text, item->color); + igPushID_Int(i); + if (item->cmd == SGIMGUI_CMD_PUSH_DEBUG_GROUP) { + if (group_stack & 1) { + group_stack <<= 1; + const char* group_name = item->args.push_debug_group.name.buf; + if (igTreeNode_StrStr(group_name, "Group: %s", group_name)) { + group_stack |= 1; + } + } else { + group_stack <<= 1; + } + } else if (item->cmd == SGIMGUI_CMD_POP_DEBUG_GROUP) { + if (group_stack & 1) { + igTreePop(); + } + group_stack >>= 1; + } else if (group_stack & 1) { + if (igSelectable_Bool(item_string.buf, ctx->capture_window.sel_item == i, 0, IMVEC2(0,0))) { + ctx->capture_window.sel_item = i; + } + if (igIsItemHovered(0)) { + igSetTooltip("%s", item_string.buf); + } + } + igPopID(); + igPopStyleColor(1); + } + igEndChild(); +} + +_SOKOL_PRIVATE void _sgimgui_draw_buffer_panel(sgimgui_t* ctx, sg_buffer buf) { + if (buf.id != SG_INVALID_ID) { + igBeginChild_Str("buffer", IMVEC2(0,0), false, 0); + sg_buffer_info info = sg_query_buffer_info(buf); + if (info.slot.state == SG_RESOURCESTATE_VALID) { + const sgimgui_buffer_t* buf_ui = &ctx->buffer_window.slots[_sgimgui_slot_index(buf.id)]; + igText("Label: %s", buf_ui->label.buf[0] ? buf_ui->label.buf : "---"); + _sgimgui_draw_resource_slot(&info.slot); + igSeparator(); + igText("Type: %s", _sgimgui_buffertype_string(buf_ui->desc.type)); + igText("Usage: %s", _sgimgui_usage_string(buf_ui->desc.usage)); + igText("Size: %d", buf_ui->desc.size); + if (buf_ui->desc.usage != SG_USAGE_IMMUTABLE) { + igSeparator(); + igText("Num Slots: %d", info.num_slots); + igText("Active Slot: %d", info.active_slot); + igText("Update Frame Index: %d", info.update_frame_index); + igText("Append Frame Index: %d", info.append_frame_index); + igText("Append Pos: %d", info.append_pos); + igText("Append Overflow: %s", _sgimgui_bool_string(info.append_overflow)); + } + } else { + igText("Buffer 0x%08X not valid.", buf.id); + } + igEndChild(); + } +} + +_SOKOL_PRIVATE bool _sgimgui_image_renderable(sg_image_type type, sg_pixel_format fmt, int sample_count) { + return (type == SG_IMAGETYPE_2D) + && sg_query_pixelformat(fmt).sample + && sample_count == 1; +} + +_SOKOL_PRIVATE void _sgimgui_draw_embedded_image(sgimgui_t* ctx, sg_image img, float* scale) { + if (sg_query_image_state(img) == SG_RESOURCESTATE_VALID) { + sgimgui_image_t* img_ui = &ctx->image_window.slots[_sgimgui_slot_index(img.id)]; + if (_sgimgui_image_renderable(img_ui->desc.type, img_ui->desc.pixel_format, img_ui->desc.sample_count)) { + igPushID_Int((int)img.id); + igSliderFloat("Scale", scale, 0.125f, 8.0f, "%.3f", ImGuiSliderFlags_Logarithmic); + float w = (float)img_ui->desc.width * (*scale); + float h = (float)img_ui->desc.height * (*scale); + igImage(simgui_imtextureid(img_ui->simgui_img), IMVEC2(w, h), IMVEC2(0,0), IMVEC2(1,1), IMVEC4(1,1,1,1), IMVEC4(0,0,0,0)); + igPopID(); + } else { + igText("Image not renderable."); + } + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_image_panel(sgimgui_t* ctx, sg_image img) { + if (img.id != SG_INVALID_ID) { + igBeginChild_Str("image", IMVEC2(0,0), false, 0); + sg_image_info info = sg_query_image_info(img); + if (info.slot.state == SG_RESOURCESTATE_VALID) { + sgimgui_image_t* img_ui = &ctx->image_window.slots[_sgimgui_slot_index(img.id)]; + const sg_image_desc* desc = &img_ui->desc; + igText("Label: %s", img_ui->label.buf[0] ? img_ui->label.buf : "---"); + _sgimgui_draw_resource_slot(&info.slot); + igSeparator(); + _sgimgui_draw_embedded_image(ctx, img, &img_ui->ui_scale); + igSeparator(); + igText("Type: %s", _sgimgui_imagetype_string(desc->type)); + igText("Usage: %s", _sgimgui_usage_string(desc->usage)); + igText("Render Target: %s", _sgimgui_bool_string(desc->render_target)); + igText("Width: %d", desc->width); + igText("Height: %d", desc->height); + igText("Num Slices: %d", desc->num_slices); + igText("Num Mipmaps: %d", desc->num_mipmaps); + igText("Pixel Format: %s", _sgimgui_pixelformat_string(desc->pixel_format)); + igText("Sample Count: %d", desc->sample_count); + if (desc->usage != SG_USAGE_IMMUTABLE) { + igSeparator(); + igText("Num Slots: %d", info.num_slots); + igText("Active Slot: %d", info.active_slot); + igText("Update Frame Index: %d", info.upd_frame_index); + } + } else { + igText("Image 0x%08X not valid.", img.id); + } + igEndChild(); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_sampler_panel(sgimgui_t* ctx, sg_sampler smp) { + if (smp.id != SG_INVALID_ID) { + igBeginChild_Str("sampler", IMVEC2(0,0), false, 0); + sg_sampler_info info = sg_query_sampler_info(smp); + if (info.slot.state == SG_RESOURCESTATE_VALID) { + sgimgui_sampler_t* smp_ui = &ctx->sampler_window.slots[_sgimgui_slot_index(smp.id)]; + const sg_sampler_desc* desc = &smp_ui->desc; + igText("Label: %s", smp_ui->label.buf[0] ? smp_ui->label.buf : "---"); + _sgimgui_draw_resource_slot(&info.slot); + igSeparator(); + igText("Min Filter: %s", _sgimgui_filter_string(desc->min_filter)); + igText("Mag Filter: %s", _sgimgui_filter_string(desc->mag_filter)); + igText("Mipmap Filter: %s", _sgimgui_filter_string(desc->mipmap_filter)); + igText("Wrap U: %s", _sgimgui_wrap_string(desc->wrap_u)); + igText("Wrap V: %s", _sgimgui_wrap_string(desc->wrap_v)); + igText("Wrap W: %s", _sgimgui_wrap_string(desc->wrap_w)); + igText("Min LOD: %.3f", desc->min_lod); + igText("Max LOD: %.3f", desc->max_lod); + igText("Border Color: %s", _sgimgui_bordercolor_string(desc->border_color)); + igText("Compare: %s", _sgimgui_comparefunc_string(desc->compare)); + igText("Max Anisotropy: %d", desc->max_anisotropy); + } else { + igText("Sampler 0x%08X not valid.", smp.id); + } + igEndChild(); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_shader_stage(const sg_shader_stage_desc* stage) { + int num_valid_ubs = 0; + for (int i = 0; i < SG_MAX_SHADERSTAGE_UBS; i++) { + const sg_shader_uniform_block_desc* ub = &stage->uniform_blocks[i]; + for (int j = 0; j < SG_MAX_UB_MEMBERS; j++) { + const sg_shader_uniform_desc* u = &ub->uniforms[j]; + if (SG_UNIFORMTYPE_INVALID != u->type) { + num_valid_ubs++; + break; + } + } + } + int num_valid_images = 0; + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + if (stage->images[i].used) { + num_valid_images++; + } else { + break; + } + } + int num_valid_samplers = 0; + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++) { + if (stage->samplers[i].used) { + num_valid_samplers++; + } else { + break; + } + } + int num_valid_image_sampler_pairs = 0; + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS; i++) { + if (stage->image_sampler_pairs[i].used) { + num_valid_image_sampler_pairs++; + } else { + break; + } + } + int num_valid_storage_buffers = 0; + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { + if (stage->storage_buffers[i].used) { + num_valid_storage_buffers++; + } else { + break; + } + } + + if (num_valid_ubs > 0) { + if (igTreeNode_Str("Uniform Blocks")) { + for (int i = 0; i < num_valid_ubs; i++) { + const sg_shader_uniform_block_desc* ub = &stage->uniform_blocks[i]; + igText("#%d: (size: %d layout: %s)\n", i, ub->size, _sgimgui_uniformlayout_string(ub->layout)); + for (int j = 0; j < SG_MAX_UB_MEMBERS; j++) { + const sg_shader_uniform_desc* u = &ub->uniforms[j]; + if (SG_UNIFORMTYPE_INVALID != u->type) { + if (u->array_count <= 1) { + igText(" %s %s", _sgimgui_uniformtype_string(u->type), u->name ? u->name : ""); + } else { + igText(" %s[%d] %s", _sgimgui_uniformtype_string(u->type), u->array_count, u->name ? u->name : ""); + } + } + } + } + igTreePop(); + } + } + if (num_valid_images > 0) { + if (igTreeNode_Str("Images")) { + for (int i = 0; i < num_valid_images; i++) { + const sg_shader_image_desc* sid = &stage->images[i]; + igText("slot: %d\n multisampled: %s\n image_type: %s\n sample_type: %s", + i, + sid->multisampled ? "true" : "false", + _sgimgui_imagetype_string(sid->image_type), + _sgimgui_imagesampletype_string(sid->sample_type)); + } + igTreePop(); + } + } + if (num_valid_samplers > 0) { + if (igTreeNode_Str("Samplers")) { + for (int i = 0; i < num_valid_samplers; i++) { + const sg_shader_sampler_desc* ssd = &stage->samplers[i]; + igText("slot: %d\n sampler_type: %s", i, _sgimgui_samplertype_string(ssd->sampler_type)); + } + igTreePop(); + } + } + if (num_valid_image_sampler_pairs > 0) { + if (igTreeNode_Str("Image Sampler Pairs")) { + for (int i = 0; i < num_valid_image_sampler_pairs; i++) { + const sg_shader_image_sampler_pair_desc* sispd = &stage->image_sampler_pairs[i]; + igText("slot: %d\n image_slot: %d\n sampler_slot: %d\n glsl_name: %s\n", + i, + sispd->image_slot, + sispd->sampler_slot, + sispd->glsl_name ? sispd->glsl_name : "---"); + } + igTreePop(); + } + } + if (num_valid_storage_buffers > 0) { + if (igTreeNode_Str("Storage Buffers")) { + for (int i = 0; i < num_valid_storage_buffers; i++) { + const sg_shader_storage_buffer_desc* sbuf_desc = &stage->storage_buffers[i]; + igText("slot: %d\n readonly: %s\n", i, sbuf_desc->readonly ? "true" : "false"); + } + igTreePop(); + } + } + if (stage->entry) { + igText("Entry: %s", stage->entry); + } + if (stage->d3d11_target) { + igText("D3D11 Target: %s", stage->d3d11_target); + } + if (stage->source) { + if (igTreeNode_Str("Source")) { + igText("%s", stage->source); + igTreePop(); + } + } else if (stage->bytecode.ptr) { + if (igTreeNode_Str("Byte Code")) { + igText("Byte-code display currently not supported."); + igTreePop(); + } + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_shader_panel(sgimgui_t* ctx, sg_shader shd) { + if (shd.id != SG_INVALID_ID) { + igBeginChild_Str("shader", IMVEC2(0,0), false, ImGuiWindowFlags_HorizontalScrollbar); + sg_shader_info info = sg_query_shader_info(shd); + if (info.slot.state == SG_RESOURCESTATE_VALID) { + const sgimgui_shader_t* shd_ui = &ctx->shader_window.slots[_sgimgui_slot_index(shd.id)]; + igText("Label: %s", shd_ui->label.buf[0] ? shd_ui->label.buf : "---"); + _sgimgui_draw_resource_slot(&info.slot); + igSeparator(); + if (igTreeNode_Str("Attrs")) { + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + const sg_shader_attr_desc* a_desc = &shd_ui->desc.attrs[i]; + if (a_desc->name || a_desc->sem_index) { + igText("#%d:", i); + igText(" Name: %s", a_desc->name ? a_desc->name : "---"); + igText(" Sem Name: %s", a_desc->sem_name ? a_desc->sem_name : "---"); + igText(" Sem Index: %d", a_desc->sem_index); + } + } + igTreePop(); + } + if (igTreeNode_Str("Vertex Shader Stage")) { + _sgimgui_draw_shader_stage(&shd_ui->desc.vs); + igTreePop(); + } + if (igTreeNode_Str("Fragment Shader Stage")) { + _sgimgui_draw_shader_stage(&shd_ui->desc.fs); + igTreePop(); + } + } else { + igText("Shader 0x%08X not valid!", shd.id); + } + igEndChild(); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_vertex_layout_state(const sg_vertex_layout_state* layout) { + if (igTreeNode_Str("Buffers")) { + for (int i = 0; i < SG_MAX_VERTEX_BUFFERS; i++) { + const sg_vertex_buffer_layout_state* l_state = &layout->buffers[i]; + if (l_state->stride > 0) { + igText("#%d:", i); + igText(" Stride: %d", l_state->stride); + igText(" Step Func: %s", _sgimgui_vertexstep_string(l_state->step_func)); + igText(" Step Rate: %d", l_state->step_rate); + } + } + igTreePop(); + } + if (igTreeNode_Str("Attrs")) { + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + const sg_vertex_attr_state* a_state = &layout->attrs[i]; + if (a_state->format != SG_VERTEXFORMAT_INVALID) { + igText("#%d:", i); + igText(" Format: %s", _sgimgui_vertexformat_string(a_state->format)); + igText(" Offset: %d", a_state->offset); + igText(" Buffer Index: %d", a_state->buffer_index); + } + } + igTreePop(); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_stencil_face_state(const sg_stencil_face_state* sfs) { + igText("Fail Op: %s", _sgimgui_stencilop_string(sfs->fail_op)); + igText("Depth Fail Op: %s", _sgimgui_stencilop_string(sfs->depth_fail_op)); + igText("Pass Op: %s", _sgimgui_stencilop_string(sfs->pass_op)); + igText("Compare: %s", _sgimgui_comparefunc_string(sfs->compare)); +} + +_SOKOL_PRIVATE void _sgimgui_draw_stencil_state(const sg_stencil_state* ss) { + igText("Enabled: %s", _sgimgui_bool_string(ss->enabled)); + igText("Read Mask: 0x%02X", ss->read_mask); + igText("Write Mask: 0x%02X", ss->write_mask); + igText("Ref: 0x%02X", ss->ref); + if (igTreeNode_Str("Front")) { + _sgimgui_draw_stencil_face_state(&ss->front); + igTreePop(); + } + if (igTreeNode_Str("Back")) { + _sgimgui_draw_stencil_face_state(&ss->back); + igTreePop(); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_depth_state(const sg_depth_state* ds) { + igText("Pixel Format: %s", _sgimgui_pixelformat_string(ds->pixel_format)); + igText("Compare: %s", _sgimgui_comparefunc_string(ds->compare)); + igText("Write Enabled: %s", _sgimgui_bool_string(ds->write_enabled)); + igText("Bias: %f", ds->bias); + igText("Bias Slope: %f", ds->bias_slope_scale); + igText("Bias Clamp: %f", ds->bias_clamp); +} + +_SOKOL_PRIVATE void _sgimgui_draw_blend_state(const sg_blend_state* bs) { + igText("Blend Enabled: %s", _sgimgui_bool_string(bs->enabled)); + igText("Src Factor RGB: %s", _sgimgui_blendfactor_string(bs->src_factor_rgb)); + igText("Dst Factor RGB: %s", _sgimgui_blendfactor_string(bs->dst_factor_rgb)); + igText("Op RGB: %s", _sgimgui_blendop_string(bs->op_rgb)); + igText("Src Factor Alpha: %s", _sgimgui_blendfactor_string(bs->src_factor_alpha)); + igText("Dst Factor Alpha: %s", _sgimgui_blendfactor_string(bs->dst_factor_alpha)); + igText("Op Alpha: %s", _sgimgui_blendop_string(bs->op_alpha)); +} + +_SOKOL_PRIVATE void _sgimgui_draw_color_target_state(const sg_color_target_state* cs) { + igText("Pixel Format: %s", _sgimgui_pixelformat_string(cs->pixel_format)); + igText("Write Mask: %s", _sgimgui_colormask_string(cs->write_mask)); + if (igTreeNode_Str("Blend State:")) { + _sgimgui_draw_blend_state(&cs->blend); + igTreePop(); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_pipeline_panel(sgimgui_t* ctx, sg_pipeline pip) { + if (pip.id != SG_INVALID_ID) { + igBeginChild_Str("pipeline", IMVEC2(0,0), false, 0); + sg_pipeline_info info = sg_query_pipeline_info(pip); + if (info.slot.state == SG_RESOURCESTATE_VALID) { + const sgimgui_pipeline_t* pip_ui = &ctx->pipeline_window.slots[_sgimgui_slot_index(pip.id)]; + igText("Label: %s", pip_ui->label.buf[0] ? pip_ui->label.buf : "---"); + _sgimgui_draw_resource_slot(&info.slot); + igSeparator(); + igText("Shader: "); igSameLine(0,-1); + if (_sgimgui_draw_shader_link(ctx, pip_ui->desc.shader)) { + _sgimgui_show_shader(ctx, pip_ui->desc.shader); + } + if (igTreeNode_Str("Vertex Layout State")) { + _sgimgui_draw_vertex_layout_state(&pip_ui->desc.layout); + igTreePop(); + } + if (igTreeNode_Str("Depth State")) { + _sgimgui_draw_depth_state(&pip_ui->desc.depth); + igTreePop(); + } + if (igTreeNode_Str("Stencil State")) { + _sgimgui_draw_stencil_state(&pip_ui->desc.stencil); + igTreePop(); + } + igText("Color Count: %d", pip_ui->desc.color_count); + for (int i = 0; i < pip_ui->desc.color_count; i++) { + sgimgui_str_t str; + _sgimgui_snprintf(&str, "Color Target %d", i); + if (igTreeNode_Str(str.buf)) { + _sgimgui_draw_color_target_state(&pip_ui->desc.colors[i]); + igTreePop(); + } + } + igText("Prim Type: %s", _sgimgui_primitivetype_string(pip_ui->desc.primitive_type)); + igText("Index Type: %s", _sgimgui_indextype_string(pip_ui->desc.index_type)); + igText("Cull Mode: %s", _sgimgui_cullmode_string(pip_ui->desc.cull_mode)); + igText("Face Winding: %s", _sgimgui_facewinding_string(pip_ui->desc.face_winding)); + igText("Sample Count: %d", pip_ui->desc.sample_count); + sgimgui_str_t blend_color_str; + igText("Blend Color: %.3f %.3f %.3f %.3f", _sgimgui_color_string(&blend_color_str, pip_ui->desc.blend_color)); + igText("Alpha To Coverage: %s", _sgimgui_bool_string(pip_ui->desc.alpha_to_coverage_enabled)); + } else { + igText("Pipeline 0x%08X not valid.", pip.id); + } + igEndChild(); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_attachment(sgimgui_t* ctx, const sg_attachment_desc* att, float* img_scale) { + igText(" Image: "); igSameLine(0,-1); + if (_sgimgui_draw_image_link(ctx, att->image)) { + _sgimgui_show_image(ctx, att->image); + } + igText(" Mip Level: %d", att->mip_level); + igText(" Slice: %d", att->slice); + _sgimgui_draw_embedded_image(ctx, att->image, img_scale); +} + +_SOKOL_PRIVATE void _sgimgui_draw_attachments_panel(sgimgui_t* ctx, sg_attachments atts) { + if (atts.id != SG_INVALID_ID) { + igBeginChild_Str("attachments", IMVEC2(0,0), false, 0); + sg_attachments_info info = sg_query_attachments_info(atts); + if (info.slot.state == SG_RESOURCESTATE_VALID) { + sgimgui_attachments_t* atts_ui = &ctx->attachments_window.slots[_sgimgui_slot_index(atts.id)]; + igText("Label: %s", atts_ui->label.buf[0] ? atts_ui->label.buf : "---"); + _sgimgui_draw_resource_slot(&info.slot); + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (atts_ui->desc.colors[i].image.id == SG_INVALID_ID) { + break; + } + igSeparator(); + igText("Color Image #%d:", i); + _sgimgui_draw_attachment(ctx, &atts_ui->desc.colors[i], &atts_ui->color_image_scale[i]); + } + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (atts_ui->desc.resolves[i].image.id == SG_INVALID_ID) { + break; + } + igSeparator(); + igText("Resolve Image #%d:", i); + _sgimgui_draw_attachment(ctx, &atts_ui->desc.resolves[i], &atts_ui->resolve_image_scale[i]); + } + if (atts_ui->desc.depth_stencil.image.id != SG_INVALID_ID) { + igSeparator(); + igText("Depth-Stencil Image:"); + _sgimgui_draw_attachment(ctx, &atts_ui->desc.depth_stencil, &atts_ui->ds_image_scale); + } + } else { + igText("Attachments 0x%08X not valid.", atts.id); + } + igEndChild(); + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_bindings_panel(sgimgui_t* ctx, const sg_bindings* bnd) { + for (int i = 0; i < SG_MAX_VERTEX_BUFFERS; i++) { + sg_buffer buf = bnd->vertex_buffers[i]; + if (buf.id != SG_INVALID_ID) { + igSeparator(); + igText("Vertex Buffer Slot #%d:", i); + igText(" Buffer: "); igSameLine(0,-1); + if (_sgimgui_draw_buffer_link(ctx, buf)) { + _sgimgui_show_buffer(ctx, buf); + } + igText(" Offset: %d", bnd->vertex_buffer_offsets[i]); + } else { + break; + } + } + if (bnd->index_buffer.id != SG_INVALID_ID) { + sg_buffer buf = bnd->index_buffer; + if (buf.id != SG_INVALID_ID) { + igSeparator(); + igText("Index Buffer Slot:"); + igText(" Buffer: "); igSameLine(0,-1); + if (_sgimgui_draw_buffer_link(ctx, buf)) { + _sgimgui_show_buffer(ctx, buf); + } + igText(" Offset: %d", bnd->index_buffer_offset); + } + } + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + sg_image img = bnd->vs.images[i]; + if (img.id != SG_INVALID_ID) { + igSeparator(); + igText("Vertex Stage Image Slot #%d:", i); + igText(" Image: "); igSameLine(0,-1); + if (_sgimgui_draw_image_link(ctx, img)) { + _sgimgui_show_image(ctx, img); + } + } else { + break; + } + } + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++) { + sg_sampler smp = bnd->vs.samplers[i]; + if (smp.id != SG_INVALID_ID) { + igSeparator(); + igText("Vertex Stage Sampler Slot #%d:", i); + igText(" Sampler: "); igSameLine(0,-1); + if (_sgimgui_draw_sampler_link(ctx, smp)) { + _sgimgui_show_sampler(ctx, smp); + } + } else { + break; + } + } + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { + sg_buffer buf = bnd->vs.storage_buffers[i]; + if (buf.id != SG_INVALID_ID) { + igSeparator(); + igText("Vertex Stage Storage Buffer Slot #%d:", i); + igText(" Buffer: "); igSameLine(0,-1); + if (_sgimgui_draw_buffer_link(ctx, buf)) { + _sgimgui_show_buffer(ctx, buf); + } + } + } + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + sg_image img = bnd->fs.images[i]; + if (img.id != SG_INVALID_ID) { + igSeparator(); + igText("Fragment Stage Image Slot #%d:", i); + igText(" Image: "); igSameLine(0,-1); + if (_sgimgui_draw_image_link(ctx, img)) { + _sgimgui_show_image(ctx, img); + } + } + } + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++) { + sg_sampler smp = bnd->fs.samplers[i]; + if (smp.id != SG_INVALID_ID) { + igSeparator(); + igText("Fragment Stage Sampler Slot #%d:", i); + igText(" Sampler: "); igSameLine(0,-1); + if (_sgimgui_draw_sampler_link(ctx, smp)) { + _sgimgui_show_sampler(ctx, smp); + } + } + } + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { + sg_buffer buf = bnd->fs.storage_buffers[i]; + if (buf.id != SG_INVALID_ID) { + igSeparator(); + igText("Fragment Stage Storage Buffer Slot #%d:", i); + igText(" Buffer: "); igSameLine(0,-1); + if (_sgimgui_draw_buffer_link(ctx, buf)) { + _sgimgui_show_buffer(ctx, buf); + } + } + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_uniforms_panel(sgimgui_t* ctx, const sgimgui_args_apply_uniforms_t* args) { + SOKOL_ASSERT(args->ub_index < SG_MAX_VERTEX_BUFFERS); + + /* check if all the required information for drawing the structured uniform block content + is available, otherwise just render a generic hexdump + */ + if (sg_query_pipeline_state(args->pipeline) != SG_RESOURCESTATE_VALID) { + igText("Pipeline object not valid!"); + return; + } + sgimgui_pipeline_t* pip_ui = &ctx->pipeline_window.slots[_sgimgui_slot_index(args->pipeline.id)]; + if (sg_query_shader_state(pip_ui->desc.shader) != SG_RESOURCESTATE_VALID) { + igText("Shader object not valid!"); + return; + } + sgimgui_shader_t* shd_ui = &ctx->shader_window.slots[_sgimgui_slot_index(pip_ui->desc.shader.id)]; + SOKOL_ASSERT(shd_ui->res_id.id == pip_ui->desc.shader.id); + const sg_shader_uniform_block_desc* ub_desc = (args->stage == SG_SHADERSTAGE_VS) ? + &shd_ui->desc.vs.uniform_blocks[args->ub_index] : + &shd_ui->desc.fs.uniform_blocks[args->ub_index]; + SOKOL_ASSERT(args->data_size <= ub_desc->size); + bool draw_dump = false; + if (ub_desc->uniforms[0].type == SG_UNIFORMTYPE_INVALID) { + draw_dump = true; + } + + sgimgui_capture_bucket_t* bucket = _sgimgui_capture_get_read_bucket(ctx); + SOKOL_ASSERT((args->ubuf_pos + args->data_size) <= bucket->ubuf_size); + const float* uptrf = (const float*) (bucket->ubuf + args->ubuf_pos); + const int32_t* uptri32 = (const int32_t*) uptrf; + if (!draw_dump) { + uint32_t u_off = 0; + for (int i = 0; i < SG_MAX_UB_MEMBERS; i++) { + const sg_shader_uniform_desc* ud = &ub_desc->uniforms[i]; + if (ud->type == SG_UNIFORMTYPE_INVALID) { + break; + } + int num_items = (ud->array_count > 1) ? ud->array_count : 1; + if (num_items > 1) { + igText("%d: %s %s[%d] =", i, _sgimgui_uniformtype_string(ud->type), ud->name?ud->name:"", ud->array_count); + } else { + igText("%d: %s %s =", i, _sgimgui_uniformtype_string(ud->type), ud->name?ud->name:""); + } + for (int item_index = 0; item_index < num_items; item_index++) { + const uint32_t u_size = _sgimgui_std140_uniform_size(ud->type, ud->array_count) / 4; + const uint32_t u_align = _sgimgui_std140_uniform_alignment(ud->type, ud->array_count) / 4; + u_off = _sgimgui_align_u32(u_off, u_align); + switch (ud->type) { + case SG_UNIFORMTYPE_FLOAT: + igText(" %.3f", uptrf[u_off]); + break; + case SG_UNIFORMTYPE_INT: + igText(" %d", uptri32[u_off]); + break; + case SG_UNIFORMTYPE_FLOAT2: + igText(" %.3f, %.3f", uptrf[u_off], uptrf[u_off+1]); + break; + case SG_UNIFORMTYPE_INT2: + igText(" %d, %d", uptri32[u_off], uptri32[u_off+1]); + break; + case SG_UNIFORMTYPE_FLOAT3: + igText(" %.3f, %.3f, %.3f", uptrf[u_off], uptrf[u_off+1], uptrf[u_off+2]); + break; + case SG_UNIFORMTYPE_INT3: + igText(" %d, %d, %d", uptri32[u_off], uptri32[u_off+1], uptri32[u_off+2]); + break; + case SG_UNIFORMTYPE_FLOAT4: + igText(" %.3f, %.3f, %.3f, %.3f", uptrf[u_off], uptrf[u_off+1], uptrf[u_off+2], uptrf[u_off+3]); + break; + case SG_UNIFORMTYPE_INT4: + igText(" %d, %d, %d, %d", uptri32[u_off], uptri32[u_off+1], uptri32[u_off+2], uptri32[u_off+3]); + break; + case SG_UNIFORMTYPE_MAT4: + igText(" %.3f, %.3f, %.3f, %.3f\n" + " %.3f, %.3f, %.3f, %.3f\n" + " %.3f, %.3f, %.3f, %.3f\n" + " %.3f, %.3f, %.3f, %.3f", + uptrf[u_off+0], uptrf[u_off+1], uptrf[u_off+2], uptrf[u_off+3], + uptrf[u_off+4], uptrf[u_off+5], uptrf[u_off+6], uptrf[u_off+7], + uptrf[u_off+8], uptrf[u_off+9], uptrf[u_off+10], uptrf[u_off+11], + uptrf[u_off+12], uptrf[u_off+13], uptrf[u_off+14], uptrf[u_off+15]); + break; + default: + igText("???"); + break; + } + u_off += u_size; + } + } + } else { + // FIXME: float vs int + const size_t num_floats = ub_desc->size / sizeof(float); + for (uint32_t i = 0; i < num_floats; i++) { + igText("%.3f, ", uptrf[i]); + if (((i + 1) % 4) != 0) { + igSameLine(0,-1); + } + } + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_passaction_panel(sgimgui_t* ctx, sg_attachments atts, const sg_pass_action* action) { + /* determine number of valid color attachments */ + int num_color_atts = 0; + if (SG_INVALID_ID == atts.id) { + /* a swapchain pass: one color attachment */ + num_color_atts = 1; + } else { + const sgimgui_attachments_t* atts_ui = &ctx->attachments_window.slots[_sgimgui_slot_index(atts.id)]; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (atts_ui->desc.colors[i].image.id != SG_INVALID_ID) { + num_color_atts++; + } + } + } + + igText("Pass Action: "); + for (int i = 0; i < num_color_atts; i++) { + const sg_color_attachment_action* c_att = &action->colors[i]; + igText(" Color Attachment %d:", i); + sgimgui_str_t color_str; + switch (c_att->load_action) { + case SG_LOADACTION_LOAD: igText(" SG_LOADACTION_LOAD"); break; + case SG_LOADACTION_DONTCARE: igText(" SG_LOADACTION_DONTCARE"); break; + case SG_LOADACTION_CLEAR: + igText(" SG_LOADACTION_CLEAR: %s", _sgimgui_color_string(&color_str, c_att->clear_value)); + break; + default: igText(" ???"); break; + } + switch (c_att->store_action) { + case SG_STOREACTION_STORE: igText(" SG_STOREACTION_STORE"); break; + case SG_STOREACTION_DONTCARE: igText(" SG_STOREACTION_DONTCARE"); break; + default: igText(" ???"); break; + } + } + const sg_depth_attachment_action* d_att = &action->depth; + igText(" Depth Attachment:"); + switch (d_att->load_action) { + case SG_LOADACTION_LOAD: igText(" SG_LOADACTION_LOAD"); break; + case SG_LOADACTION_DONTCARE: igText(" SG_LOADACTION_DONTCARE"); break; + case SG_LOADACTION_CLEAR: igText(" SG_LOADACTION_CLEAR: %.3f", d_att->clear_value); break; + default: igText(" ???"); break; + } + switch (d_att->store_action) { + case SG_STOREACTION_STORE: igText(" SG_STOREACTION_STORE"); break; + case SG_STOREACTION_DONTCARE: igText(" SG_STOREACTION_DONTCARE"); break; + default: igText(" ???"); break; + } + const sg_stencil_attachment_action* s_att = &action->stencil; + igText(" Stencil Attachment"); + switch (s_att->load_action) { + case SG_LOADACTION_LOAD: igText(" SG_LOADACTION_LOAD"); break; + case SG_LOADACTION_DONTCARE: igText(" SG_LOADACTION_DONTCARE"); break; + case SG_LOADACTION_CLEAR: igText(" SG_LOADACTION_CLEAR: 0x%02X", s_att->clear_value); break; + default: igText(" ???"); break; + } + switch (d_att->store_action) { + case SG_STOREACTION_STORE: igText(" SG_STOREACTION_STORE"); break; + case SG_STOREACTION_DONTCARE: igText(" SG_STOREACTION_DONTCARE"); break; + default: igText(" ???"); break; + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_swapchain_panel(sg_swapchain* swapchain) { + igText("Swapchain"); + igText(" Width: %d", swapchain->width); + igText(" Height: %d", swapchain->height); + igText(" Sample Count: %d", swapchain->sample_count); + igText(" Color Format: %s", _sgimgui_pixelformat_string(swapchain->color_format)); + igText(" Depth Format: %s", _sgimgui_pixelformat_string(swapchain->depth_format)); + igSeparator(); + switch (sg_query_backend()) { + case SG_BACKEND_D3D11: + igText("D3D11 Objects:"); + igText(" Render View: %p", swapchain->d3d11.render_view); + igText(" Resolve View: %p", swapchain->d3d11.resolve_view); + igText(" Depth Stencil View: %p", swapchain->d3d11.depth_stencil_view); + break; + case SG_BACKEND_WGPU: + igText("WGPU Objects:"); + igText(" Render View: %p", swapchain->wgpu.render_view); + igText(" Resolve View: %p", swapchain->wgpu.resolve_view); + igText(" Depth Stencil View: %p", swapchain->wgpu.depth_stencil_view); + break; + case SG_BACKEND_METAL_MACOS: + case SG_BACKEND_METAL_IOS: + case SG_BACKEND_METAL_SIMULATOR: + igText("Metal Objects:"); + igText(" Current Drawable: %p", swapchain->metal.current_drawable); + igText(" Depth Stencil Texture: %p", swapchain->metal.depth_stencil_texture); + igText(" MSAA Color Texture: %p", swapchain->metal.msaa_color_texture); + break; + case SG_BACKEND_GLCORE: + case SG_BACKEND_GLES3: + igText("GL Objects:"); + igText(" Framebuffer: %d", swapchain->gl.framebuffer); + break; + default: + igText(" UNKNOWN BACKEND!"); + break; + } +} + +_SOKOL_PRIVATE void _sgimgui_draw_capture_panel(sgimgui_t* ctx) { + int sel_item_index = ctx->capture_window.sel_item; + if (sel_item_index >= _sgimgui_capture_num_read_items(ctx)) { + return; + } + sgimgui_capture_item_t* item = _sgimgui_capture_read_item_at(ctx, sel_item_index); + igBeginChild_Str("capture_item", IMVEC2(0, 0), false, 0); + igPushStyleColor_U32(ImGuiCol_Text, item->color); + igText("%s", _sgimgui_capture_item_string(ctx, sel_item_index, item).buf); + igPopStyleColor(1); + igSeparator(); + switch (item->cmd) { + case SGIMGUI_CMD_RESET_STATE_CACHE: + break; + case SGIMGUI_CMD_MAKE_BUFFER: + _sgimgui_draw_buffer_panel(ctx, item->args.make_buffer.result); + break; + case SGIMGUI_CMD_MAKE_IMAGE: + _sgimgui_draw_image_panel(ctx, item->args.make_image.result); + break; + case SGIMGUI_CMD_MAKE_SAMPLER: + _sgimgui_draw_sampler_panel(ctx, item->args.make_sampler.result); + break; + case SGIMGUI_CMD_MAKE_SHADER: + _sgimgui_draw_shader_panel(ctx, item->args.make_shader.result); + break; + case SGIMGUI_CMD_MAKE_PIPELINE: + _sgimgui_draw_pipeline_panel(ctx, item->args.make_pipeline.result); + break; + case SGIMGUI_CMD_MAKE_ATTACHMENTS: + _sgimgui_draw_attachments_panel(ctx, item->args.make_attachments.result); + break; + case SGIMGUI_CMD_DESTROY_BUFFER: + _sgimgui_draw_buffer_panel(ctx, item->args.destroy_buffer.buffer); + break; + case SGIMGUI_CMD_DESTROY_IMAGE: + _sgimgui_draw_image_panel(ctx, item->args.destroy_image.image); + break; + case SGIMGUI_CMD_DESTROY_SAMPLER: + _sgimgui_draw_sampler_panel(ctx, item->args.destroy_sampler.sampler); + break; + case SGIMGUI_CMD_DESTROY_SHADER: + _sgimgui_draw_shader_panel(ctx, item->args.destroy_shader.shader); + break; + case SGIMGUI_CMD_DESTROY_PIPELINE: + _sgimgui_draw_pipeline_panel(ctx, item->args.destroy_pipeline.pipeline); + break; + case SGIMGUI_CMD_DESTROY_ATTACHMENTS: + _sgimgui_draw_attachments_panel(ctx, item->args.destroy_attachments.attachments); + break; + case SGIMGUI_CMD_UPDATE_BUFFER: + _sgimgui_draw_buffer_panel(ctx, item->args.update_buffer.buffer); + break; + case SGIMGUI_CMD_UPDATE_IMAGE: + _sgimgui_draw_image_panel(ctx, item->args.update_image.image); + break; + case SGIMGUI_CMD_APPEND_BUFFER: + _sgimgui_draw_buffer_panel(ctx, item->args.update_buffer.buffer); + break; + case SGIMGUI_CMD_BEGIN_PASS: + _sgimgui_draw_passaction_panel(ctx, item->args.begin_pass.pass.attachments, &item->args.begin_pass.pass.action); + igSeparator(); + if (item->args.begin_pass.pass.attachments.id != SG_INVALID_ID) { + _sgimgui_draw_attachments_panel(ctx, item->args.begin_pass.pass.attachments); + } else { + _sgimgui_draw_swapchain_panel(&item->args.begin_pass.pass.swapchain); + } + break; + case SGIMGUI_CMD_APPLY_VIEWPORT: + case SGIMGUI_CMD_APPLY_SCISSOR_RECT: + break; + case SGIMGUI_CMD_APPLY_PIPELINE: + _sgimgui_draw_pipeline_panel(ctx, item->args.apply_pipeline.pipeline); + break; + case SGIMGUI_CMD_APPLY_BINDINGS: + _sgimgui_draw_bindings_panel(ctx, &item->args.apply_bindings.bindings); + break; + case SGIMGUI_CMD_APPLY_UNIFORMS: + _sgimgui_draw_uniforms_panel(ctx, &item->args.apply_uniforms); + break; + case SGIMGUI_CMD_DRAW: + case SGIMGUI_CMD_END_PASS: + case SGIMGUI_CMD_COMMIT: + break; + case SGIMGUI_CMD_ALLOC_BUFFER: + _sgimgui_draw_buffer_panel(ctx, item->args.alloc_buffer.result); + break; + case SGIMGUI_CMD_ALLOC_IMAGE: + _sgimgui_draw_image_panel(ctx, item->args.alloc_image.result); + break; + case SGIMGUI_CMD_ALLOC_SAMPLER: + _sgimgui_draw_sampler_panel(ctx, item->args.alloc_sampler.result); + break; + case SGIMGUI_CMD_ALLOC_SHADER: + _sgimgui_draw_shader_panel(ctx, item->args.alloc_shader.result); + break; + case SGIMGUI_CMD_ALLOC_PIPELINE: + _sgimgui_draw_pipeline_panel(ctx, item->args.alloc_pipeline.result); + break; + case SGIMGUI_CMD_ALLOC_ATTACHMENTS: + _sgimgui_draw_attachments_panel(ctx, item->args.alloc_attachments.result); + break; + case SGIMGUI_CMD_INIT_BUFFER: + _sgimgui_draw_buffer_panel(ctx, item->args.init_buffer.buffer); + break; + case SGIMGUI_CMD_INIT_IMAGE: + _sgimgui_draw_image_panel(ctx, item->args.init_image.image); + break; + case SGIMGUI_CMD_INIT_SAMPLER: + _sgimgui_draw_sampler_panel(ctx, item->args.init_sampler.sampler); + break; + case SGIMGUI_CMD_INIT_SHADER: + _sgimgui_draw_shader_panel(ctx, item->args.init_shader.shader); + break; + case SGIMGUI_CMD_INIT_PIPELINE: + _sgimgui_draw_pipeline_panel(ctx, item->args.init_pipeline.pipeline); + break; + case SGIMGUI_CMD_INIT_ATTACHMENTS: + _sgimgui_draw_attachments_panel(ctx, item->args.init_attachments.attachments); + break; + case SGIMGUI_CMD_FAIL_BUFFER: + _sgimgui_draw_buffer_panel(ctx, item->args.fail_buffer.buffer); + break; + case SGIMGUI_CMD_FAIL_IMAGE: + _sgimgui_draw_image_panel(ctx, item->args.fail_image.image); + break; + case SGIMGUI_CMD_FAIL_SAMPLER: + _sgimgui_draw_sampler_panel(ctx, item->args.fail_sampler.sampler); + break; + case SGIMGUI_CMD_FAIL_SHADER: + _sgimgui_draw_shader_panel(ctx, item->args.fail_shader.shader); + break; + case SGIMGUI_CMD_FAIL_PIPELINE: + _sgimgui_draw_pipeline_panel(ctx, item->args.fail_pipeline.pipeline); + break; + case SGIMGUI_CMD_FAIL_ATTACHMENTS: + _sgimgui_draw_attachments_panel(ctx, item->args.fail_attachments.attachments); + break; + default: + break; + } + igEndChild(); +} + +_SOKOL_PRIVATE void _sgimgui_draw_caps_panel(void) { + igText("Backend: %s\n\n", _sgimgui_backend_string(sg_query_backend())); + sg_features f = sg_query_features(); + igText("Features:"); + igText(" origin_top_left: %s", _sgimgui_bool_string(f.origin_top_left)); + igText(" image_clamp_to_border: %s", _sgimgui_bool_string(f.image_clamp_to_border)); + igText(" mrt_independent_blend_state: %s", _sgimgui_bool_string(f.mrt_independent_blend_state)); + igText(" mrt_independent_write_mask: %s", _sgimgui_bool_string(f.mrt_independent_write_mask)); + igText(" storage_buffer: %s", _sgimgui_bool_string(f.storage_buffer)); + sg_limits l = sg_query_limits(); + igText("\nLimits:\n"); + igText(" max_image_size_2d: %d", l.max_image_size_2d); + igText(" max_image_size_cube: %d", l.max_image_size_cube); + igText(" max_image_size_3d: %d", l.max_image_size_3d); + igText(" max_image_size_array: %d", l.max_image_size_array); + igText(" max_image_array_layers: %d", l.max_image_array_layers); + igText(" max_vertex_attrs: %d", l.max_vertex_attrs); + igText(" gl_max_vertex_uniform_components: %d", l.gl_max_vertex_uniform_components); + igText(" gl_max_combined_texture_image_units: %d", l.gl_max_combined_texture_image_units); + igText("\nUsable Pixelformats:"); + for (int i = (int)(SG_PIXELFORMAT_NONE+1); i < (int)_SG_PIXELFORMAT_NUM; i++) { + sg_pixel_format fmt = (sg_pixel_format)i; + sg_pixelformat_info info = sg_query_pixelformat(fmt); + if (info.sample) { + igText(" %s: %s%s%s%s%s%s", + _sgimgui_pixelformat_string(fmt), + info.sample ? "SAMPLE ":"", + info.filter ? "FILTER ":"", + info.blend ? "BLEND ":"", + info.render ? "RENDER ":"", + info.msaa ? "MSAA ":"", + info.depth ? "DEPTH ":""); + } + } +} + +_SOKOL_PRIVATE void _sgimgui_frame_add_stats_row(const char* key, uint32_t value) { + igTableNextRow(0, 0.0f); + igTableSetColumnIndex(0); + igText(key); + igTableSetColumnIndex(1); + igText("%d", value); +} + +#define _sgimgui_frame_stats(key) _sgimgui_frame_add_stats_row(#key, stats->key) + +_SOKOL_PRIVATE void _sgimgui_draw_frame_stats_panel(sgimgui_t* ctx) { + _SOKOL_UNUSED(ctx); + igCheckbox("Ignore sokol_imgui.h", &ctx->frame_stats_window.disable_sokol_imgui_stats); + const sg_frame_stats* stats = &ctx->frame_stats_window.stats; + const ImGuiTableFlags flags = + ImGuiTableFlags_Resizable | + ImGuiTableFlags_ScrollY | + ImGuiTableFlags_SizingFixedFit | + ImGuiTableFlags_Borders; + if (igBeginTable("##frame_stats_table", 2, flags, IMVEC2(0, 0), 0)) { + igTableSetupScrollFreeze(0, 2); + igTableSetupColumn("key", ImGuiTableColumnFlags_None, 0, 0); + igTableSetupColumn("value", ImGuiTableColumnFlags_None, 0, 0); + igTableHeadersRow(); + _sgimgui_frame_stats(frame_index); + _sgimgui_frame_stats(num_passes); + _sgimgui_frame_stats(num_apply_viewport); + _sgimgui_frame_stats(num_apply_scissor_rect); + _sgimgui_frame_stats(num_apply_pipeline); + _sgimgui_frame_stats(num_apply_bindings); + _sgimgui_frame_stats(num_apply_uniforms); + _sgimgui_frame_stats(num_draw); + _sgimgui_frame_stats(num_update_buffer); + _sgimgui_frame_stats(num_append_buffer); + _sgimgui_frame_stats(num_update_image); + _sgimgui_frame_stats(size_apply_uniforms); + _sgimgui_frame_stats(size_update_buffer); + _sgimgui_frame_stats(size_append_buffer); + _sgimgui_frame_stats(size_update_image); + switch (sg_query_backend()) { + case SG_BACKEND_GLCORE: + case SG_BACKEND_GLES3: + _sgimgui_frame_stats(gl.num_bind_buffer); + _sgimgui_frame_stats(gl.num_active_texture); + _sgimgui_frame_stats(gl.num_bind_texture); + _sgimgui_frame_stats(gl.num_bind_sampler); + _sgimgui_frame_stats(gl.num_use_program); + _sgimgui_frame_stats(gl.num_render_state); + _sgimgui_frame_stats(gl.num_vertex_attrib_pointer); + _sgimgui_frame_stats(gl.num_vertex_attrib_divisor); + _sgimgui_frame_stats(gl.num_enable_vertex_attrib_array); + _sgimgui_frame_stats(gl.num_disable_vertex_attrib_array); + _sgimgui_frame_stats(gl.num_uniform); + break; + case SG_BACKEND_WGPU: + _sgimgui_frame_stats(wgpu.uniforms.num_set_bindgroup); + _sgimgui_frame_stats(wgpu.uniforms.size_write_buffer); + _sgimgui_frame_stats(wgpu.bindings.num_set_vertex_buffer); + _sgimgui_frame_stats(wgpu.bindings.num_skip_redundant_vertex_buffer); + _sgimgui_frame_stats(wgpu.bindings.num_set_index_buffer); + _sgimgui_frame_stats(wgpu.bindings.num_skip_redundant_index_buffer); + _sgimgui_frame_stats(wgpu.bindings.num_create_bindgroup); + _sgimgui_frame_stats(wgpu.bindings.num_discard_bindgroup); + _sgimgui_frame_stats(wgpu.bindings.num_set_bindgroup); + _sgimgui_frame_stats(wgpu.bindings.num_skip_redundant_bindgroup); + _sgimgui_frame_stats(wgpu.bindings.num_bindgroup_cache_hits); + _sgimgui_frame_stats(wgpu.bindings.num_bindgroup_cache_misses); + _sgimgui_frame_stats(wgpu.bindings.num_bindgroup_cache_collisions); + _sgimgui_frame_stats(wgpu.bindings.num_bindgroup_cache_hash_vs_key_mismatch); + break; + case SG_BACKEND_METAL_MACOS: + case SG_BACKEND_METAL_IOS: + case SG_BACKEND_METAL_SIMULATOR: + _sgimgui_frame_stats(metal.idpool.num_added); + _sgimgui_frame_stats(metal.idpool.num_released); + _sgimgui_frame_stats(metal.idpool.num_garbage_collected); + _sgimgui_frame_stats(metal.pipeline.num_set_blend_color); + _sgimgui_frame_stats(metal.pipeline.num_set_cull_mode); + _sgimgui_frame_stats(metal.pipeline.num_set_front_facing_winding); + _sgimgui_frame_stats(metal.pipeline.num_set_stencil_reference_value); + _sgimgui_frame_stats(metal.pipeline.num_set_depth_bias); + _sgimgui_frame_stats(metal.pipeline.num_set_render_pipeline_state); + _sgimgui_frame_stats(metal.pipeline.num_set_depth_stencil_state); + _sgimgui_frame_stats(metal.bindings.num_set_vertex_buffer); + _sgimgui_frame_stats(metal.bindings.num_set_vertex_texture); + _sgimgui_frame_stats(metal.bindings.num_set_vertex_sampler_state); + _sgimgui_frame_stats(metal.bindings.num_set_fragment_buffer); + _sgimgui_frame_stats(metal.bindings.num_set_fragment_texture); + _sgimgui_frame_stats(metal.bindings.num_set_fragment_sampler_state); + _sgimgui_frame_stats(metal.uniforms.num_set_vertex_buffer_offset); + _sgimgui_frame_stats(metal.uniforms.num_set_fragment_buffer_offset); + break; + case SG_BACKEND_D3D11: + _sgimgui_frame_stats(d3d11.pass.num_om_set_render_targets); + _sgimgui_frame_stats(d3d11.pass.num_clear_render_target_view); + _sgimgui_frame_stats(d3d11.pass.num_clear_depth_stencil_view); + _sgimgui_frame_stats(d3d11.pass.num_resolve_subresource); + _sgimgui_frame_stats(d3d11.pipeline.num_rs_set_state); + _sgimgui_frame_stats(d3d11.pipeline.num_om_set_depth_stencil_state); + _sgimgui_frame_stats(d3d11.pipeline.num_om_set_blend_state); + _sgimgui_frame_stats(d3d11.pipeline.num_ia_set_primitive_topology); + _sgimgui_frame_stats(d3d11.pipeline.num_ia_set_input_layout); + _sgimgui_frame_stats(d3d11.pipeline.num_vs_set_shader); + _sgimgui_frame_stats(d3d11.pipeline.num_vs_set_constant_buffers); + _sgimgui_frame_stats(d3d11.pipeline.num_ps_set_shader); + _sgimgui_frame_stats(d3d11.pipeline.num_ps_set_constant_buffers); + _sgimgui_frame_stats(d3d11.bindings.num_ia_set_vertex_buffers); + _sgimgui_frame_stats(d3d11.bindings.num_ia_set_index_buffer); + _sgimgui_frame_stats(d3d11.bindings.num_vs_set_shader_resources); + _sgimgui_frame_stats(d3d11.bindings.num_ps_set_shader_resources); + _sgimgui_frame_stats(d3d11.bindings.num_vs_set_samplers); + _sgimgui_frame_stats(d3d11.bindings.num_ps_set_samplers); + _sgimgui_frame_stats(d3d11.uniforms.num_update_subresource); + _sgimgui_frame_stats(d3d11.draw.num_draw_indexed_instanced); + _sgimgui_frame_stats(d3d11.draw.num_draw_indexed); + _sgimgui_frame_stats(d3d11.draw.num_draw_instanced); + _sgimgui_frame_stats(d3d11.draw.num_draw); + _sgimgui_frame_stats(d3d11.num_map); + _sgimgui_frame_stats(d3d11.num_unmap); + break; + default: break; + } + igEndTable(); + } +} + +#define _sgimgui_def(val, def) (((val) == 0) ? (def) : (val)) + +_SOKOL_PRIVATE sgimgui_desc_t _sgimgui_desc_defaults(const sgimgui_desc_t* desc) { + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + sgimgui_desc_t res = *desc; + // FIXME: any additional default overrides would go here + return res; +} + +/*--- PUBLIC FUNCTIONS -------------------------------------------------------*/ +SOKOL_API_IMPL void sgimgui_init(sgimgui_t* ctx, const sgimgui_desc_t* desc) { + SOKOL_ASSERT(ctx && desc); + _sgimgui_clear(ctx, sizeof(sgimgui_t)); + ctx->init_tag = 0xABCDABCD; + ctx->desc = _sgimgui_desc_defaults(desc); + _sgimgui_capture_init(ctx); + + /* hook into sokol_gfx functions */ + sg_trace_hooks hooks; + _sgimgui_clear(&hooks, sizeof(hooks)); + hooks.user_data = (void*) ctx; + hooks.reset_state_cache = _sgimgui_reset_state_cache; + hooks.make_buffer = _sgimgui_make_buffer; + hooks.make_image = _sgimgui_make_image; + hooks.make_sampler = _sgimgui_make_sampler; + hooks.make_shader = _sgimgui_make_shader; + hooks.make_pipeline = _sgimgui_make_pipeline; + hooks.make_attachments = _sgimgui_make_attachments; + hooks.destroy_buffer = _sgimgui_destroy_buffer; + hooks.destroy_image = _sgimgui_destroy_image; + hooks.destroy_sampler = _sgimgui_destroy_sampler; + hooks.destroy_shader = _sgimgui_destroy_shader; + hooks.destroy_pipeline = _sgimgui_destroy_pipeline; + hooks.destroy_attachments = _sgimgui_destroy_attachments; + hooks.update_buffer = _sgimgui_update_buffer; + hooks.update_image = _sgimgui_update_image; + hooks.append_buffer = _sgimgui_append_buffer; + hooks.begin_pass = _sgimgui_begin_pass; + hooks.apply_viewport = _sgimgui_apply_viewport; + hooks.apply_scissor_rect = _sgimgui_apply_scissor_rect; + hooks.apply_pipeline = _sgimgui_apply_pipeline; + hooks.apply_bindings = _sgimgui_apply_bindings; + hooks.apply_uniforms = _sgimgui_apply_uniforms; + hooks.draw = _sgimgui_draw; + hooks.end_pass = _sgimgui_end_pass; + hooks.commit = _sgimgui_commit; + hooks.alloc_buffer = _sgimgui_alloc_buffer; + hooks.alloc_image = _sgimgui_alloc_image; + hooks.alloc_sampler = _sgimgui_alloc_sampler; + hooks.alloc_shader = _sgimgui_alloc_shader; + hooks.alloc_pipeline = _sgimgui_alloc_pipeline; + hooks.alloc_attachments = _sgimgui_alloc_attachments; + hooks.dealloc_buffer = _sgimgui_dealloc_buffer; + hooks.dealloc_image = _sgimgui_dealloc_image; + hooks.dealloc_sampler = _sgimgui_dealloc_sampler; + hooks.dealloc_shader = _sgimgui_dealloc_shader; + hooks.dealloc_pipeline = _sgimgui_dealloc_pipeline; + hooks.dealloc_attachments = _sgimgui_dealloc_attachments; + hooks.init_buffer = _sgimgui_init_buffer; + hooks.init_image = _sgimgui_init_image; + hooks.init_sampler = _sgimgui_init_sampler; + hooks.init_shader = _sgimgui_init_shader; + hooks.init_pipeline = _sgimgui_init_pipeline; + hooks.init_attachments = _sgimgui_init_attachments; + hooks.uninit_buffer = _sgimgui_uninit_buffer; + hooks.uninit_image = _sgimgui_uninit_image; + hooks.uninit_sampler = _sgimgui_uninit_sampler; + hooks.uninit_shader = _sgimgui_uninit_shader; + hooks.uninit_pipeline = _sgimgui_uninit_pipeline; + hooks.uninit_attachments = _sgimgui_uninit_attachments; + hooks.fail_buffer = _sgimgui_fail_buffer; + hooks.fail_image = _sgimgui_fail_image; + hooks.fail_sampler = _sgimgui_fail_sampler; + hooks.fail_shader = _sgimgui_fail_shader; + hooks.fail_pipeline = _sgimgui_fail_pipeline; + hooks.fail_attachments = _sgimgui_fail_attachments; + hooks.push_debug_group = _sgimgui_push_debug_group; + hooks.pop_debug_group = _sgimgui_pop_debug_group; + ctx->hooks = sg_install_trace_hooks(&hooks); + + /* allocate resource debug-info slots */ + const sg_desc sgdesc = sg_query_desc(); + ctx->buffer_window.num_slots = sgdesc.buffer_pool_size; + ctx->image_window.num_slots = sgdesc.image_pool_size; + ctx->sampler_window.num_slots = sgdesc.sampler_pool_size; + ctx->shader_window.num_slots = sgdesc.shader_pool_size; + ctx->pipeline_window.num_slots = sgdesc.pipeline_pool_size; + ctx->attachments_window.num_slots = sgdesc.attachments_pool_size; + + const size_t buffer_pool_size = (size_t)ctx->buffer_window.num_slots * sizeof(sgimgui_buffer_t); + ctx->buffer_window.slots = (sgimgui_buffer_t*) _sgimgui_malloc_clear(&ctx->desc.allocator, buffer_pool_size); + + const size_t image_pool_size = (size_t)ctx->image_window.num_slots * sizeof(sgimgui_image_t); + ctx->image_window.slots = (sgimgui_image_t*) _sgimgui_malloc_clear(&ctx->desc.allocator, image_pool_size); + + const size_t sampler_pool_size = (size_t)ctx->sampler_window.num_slots * sizeof(sgimgui_sampler_t); + ctx->sampler_window.slots = (sgimgui_sampler_t*) _sgimgui_malloc_clear(&ctx->desc.allocator, sampler_pool_size); + + const size_t shader_pool_size = (size_t)ctx->shader_window.num_slots * sizeof(sgimgui_shader_t); + ctx->shader_window.slots = (sgimgui_shader_t*) _sgimgui_malloc_clear(&ctx->desc.allocator, shader_pool_size); + + const size_t pipeline_pool_size = (size_t)ctx->pipeline_window.num_slots * sizeof(sgimgui_pipeline_t); + ctx->pipeline_window.slots = (sgimgui_pipeline_t*) _sgimgui_malloc_clear(&ctx->desc.allocator, pipeline_pool_size); + + const size_t attachments_pool_size = (size_t)ctx->attachments_window.num_slots * sizeof(sgimgui_attachments_t); + ctx->attachments_window.slots = (sgimgui_attachments_t*) _sgimgui_malloc_clear(&ctx->desc.allocator, attachments_pool_size); +} + +SOKOL_API_IMPL void sgimgui_discard(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + /* restore original trace hooks */ + sg_install_trace_hooks(&ctx->hooks); + ctx->init_tag = 0; + _sgimgui_capture_discard(ctx); + if (ctx->buffer_window.slots) { + for (int i = 0; i < ctx->buffer_window.num_slots; i++) { + if (ctx->buffer_window.slots[i].res_id.id != SG_INVALID_ID) { + _sgimgui_buffer_destroyed(ctx, i); + } + } + _sgimgui_free(&ctx->desc.allocator, (void*)ctx->buffer_window.slots); + ctx->buffer_window.slots = 0; + } + if (ctx->image_window.slots) { + for (int i = 0; i < ctx->image_window.num_slots; i++) { + if (ctx->image_window.slots[i].res_id.id != SG_INVALID_ID) { + _sgimgui_image_destroyed(ctx, i); + } + } + _sgimgui_free(&ctx->desc.allocator, (void*)ctx->image_window.slots); + ctx->image_window.slots = 0; + } + if (ctx->sampler_window.slots) { + for (int i = 0; i < ctx->sampler_window.num_slots; i++) { + if (ctx->sampler_window.slots[i].res_id.id != SG_INVALID_ID) { + _sgimgui_sampler_destroyed(ctx, i); + } + } + _sgimgui_free(&ctx->desc.allocator, (void*)ctx->sampler_window.slots); + ctx->sampler_window.slots = 0; + } + if (ctx->shader_window.slots) { + for (int i = 0; i < ctx->shader_window.num_slots; i++) { + if (ctx->shader_window.slots[i].res_id.id != SG_INVALID_ID) { + _sgimgui_shader_destroyed(ctx, i); + } + } + _sgimgui_free(&ctx->desc.allocator, (void*)ctx->shader_window.slots); + ctx->shader_window.slots = 0; + } + if (ctx->pipeline_window.slots) { + for (int i = 0; i < ctx->pipeline_window.num_slots; i++) { + if (ctx->pipeline_window.slots[i].res_id.id != SG_INVALID_ID) { + _sgimgui_pipeline_destroyed(ctx, i); + } + } + _sgimgui_free(&ctx->desc.allocator, (void*)ctx->pipeline_window.slots); + ctx->pipeline_window.slots = 0; + } + if (ctx->attachments_window.slots) { + for (int i = 0; i < ctx->attachments_window.num_slots; i++) { + if (ctx->attachments_window.slots[i].res_id.id != SG_INVALID_ID) { + _sgimgui_attachments_destroyed(ctx, i); + } + } + _sgimgui_free(&ctx->desc.allocator, (void*)ctx->attachments_window.slots); + ctx->attachments_window.slots = 0; + } +} + +SOKOL_API_IMPL void sgimgui_draw(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + sgimgui_draw_buffer_window(ctx); + sgimgui_draw_image_window(ctx); + sgimgui_draw_sampler_window(ctx); + sgimgui_draw_shader_window(ctx); + sgimgui_draw_pipeline_window(ctx); + sgimgui_draw_attachments_window(ctx); + sgimgui_draw_capture_window(ctx); + sgimgui_draw_capabilities_window(ctx); + sgimgui_draw_frame_stats_window(ctx); +} + +SOKOL_API_IMPL void sgimgui_draw_menu(sgimgui_t* ctx, const char* title) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + SOKOL_ASSERT(title); + if (igBeginMenu(title, true)) { + igMenuItem_BoolPtr("Capabilities", 0, &ctx->caps_window.open, true); + igMenuItem_BoolPtr("Frame Stats", 0, &ctx->frame_stats_window.open, true); + igMenuItem_BoolPtr("Buffers", 0, &ctx->buffer_window.open, true); + igMenuItem_BoolPtr("Images", 0, &ctx->image_window.open, true); + igMenuItem_BoolPtr("Samplers", 0, &ctx->sampler_window.open, true); + igMenuItem_BoolPtr("Shaders", 0, &ctx->shader_window.open, true); + igMenuItem_BoolPtr("Pipelines", 0, &ctx->pipeline_window.open, true); + igMenuItem_BoolPtr("Attachments", 0, &ctx->attachments_window.open, true); + igMenuItem_BoolPtr("Calls", 0, &ctx->capture_window.open, true); + igEndMenu(); + } +} + +SOKOL_API_IMPL void sgimgui_draw_buffer_window(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + if (!ctx->buffer_window.open) { + return; + } + igSetNextWindowSize(IMVEC2(440, 280), ImGuiCond_Once); + if (igBegin("Buffers", &ctx->buffer_window.open, 0)) { + sgimgui_draw_buffer_window_content(ctx); + } + igEnd(); +} + +SOKOL_API_IMPL void sgimgui_draw_image_window(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + if (!ctx->image_window.open) { + return; + } + igSetNextWindowSize(IMVEC2(440, 400), ImGuiCond_Once); + if (igBegin("Images", &ctx->image_window.open, 0)) { + sgimgui_draw_image_window_content(ctx); + } + igEnd(); +} + +SOKOL_API_IMPL void sgimgui_draw_sampler_window(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + if (!ctx->sampler_window.open) { + return; + } + igSetNextWindowSize(IMVEC2(440, 400), ImGuiCond_Once); + if (igBegin("Samplers", &ctx->sampler_window.open, 0)) { + sgimgui_draw_sampler_window_content(ctx); + } + igEnd(); +} + +SOKOL_API_IMPL void sgimgui_draw_shader_window(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + if (!ctx->shader_window.open) { + return; + } + igSetNextWindowSize(IMVEC2(440, 400), ImGuiCond_Once); + if (igBegin("Shaders", &ctx->shader_window.open, 0)) { + sgimgui_draw_shader_window_content(ctx); + } + igEnd(); +} + +SOKOL_API_IMPL void sgimgui_draw_pipeline_window(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + if (!ctx->pipeline_window.open) { + return; + } + igSetNextWindowSize(IMVEC2(540, 400), ImGuiCond_Once); + if (igBegin("Pipelines", &ctx->pipeline_window.open, 0)) { + sgimgui_draw_pipeline_window_content(ctx); + } + igEnd(); +} + +SOKOL_API_IMPL void sgimgui_draw_attachments_window(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + if (!ctx->attachments_window.open) { + return; + } + igSetNextWindowSize(IMVEC2(440, 400), ImGuiCond_Once); + if (igBegin("Attachments", &ctx->attachments_window.open, 0)) { + sgimgui_draw_attachments_window_content(ctx); + } + igEnd(); +} + +SOKOL_API_IMPL void sgimgui_draw_capture_window(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + if (!ctx->capture_window.open) { + return; + } + igSetNextWindowSize(IMVEC2(640, 400), ImGuiCond_Once); + if (igBegin("Frame Capture", &ctx->capture_window.open, 0)) { + sgimgui_draw_capture_window_content(ctx); + } + igEnd(); +} + +SOKOL_API_IMPL void sgimgui_draw_capabilities_window(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + if (!ctx->caps_window.open) { + return; + } + igSetNextWindowSize(IMVEC2(440, 400), ImGuiCond_Once); + if (igBegin("Capabilities", &ctx->caps_window.open, 0)) { + sgimgui_draw_capabilities_window_content(ctx); + } + igEnd(); +} + +SOKOL_API_IMPL void sgimgui_draw_frame_stats_window(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + if (!ctx->frame_stats_window.open) { + return; + } + igSetNextWindowSize(IMVEC2(512, 400), ImGuiCond_Once); + if (igBegin("Frame Stats", &ctx->frame_stats_window.open, 0)) { + sgimgui_draw_frame_stats_window_content(ctx); + } + igEnd(); +} + +SOKOL_API_IMPL void sgimgui_draw_buffer_window_content(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + _sgimgui_draw_buffer_list(ctx); + igSameLine(0,-1); + _sgimgui_draw_buffer_panel(ctx, ctx->buffer_window.sel_buf); +} + +SOKOL_API_IMPL void sgimgui_draw_image_window_content(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + _sgimgui_draw_image_list(ctx); + igSameLine(0,-1); + _sgimgui_draw_image_panel(ctx, ctx->image_window.sel_img); +} + +SOKOL_API_IMPL void sgimgui_draw_sampler_window_content(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + _sgimgui_draw_sampler_list(ctx); + igSameLine(0,-1); + _sgimgui_draw_sampler_panel(ctx, ctx->sampler_window.sel_smp); +} + +SOKOL_API_IMPL void sgimgui_draw_shader_window_content(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + _sgimgui_draw_shader_list(ctx); + igSameLine(0,-1); + _sgimgui_draw_shader_panel(ctx, ctx->shader_window.sel_shd); +} + +SOKOL_API_IMPL void sgimgui_draw_pipeline_window_content(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + _sgimgui_draw_pipeline_list(ctx); + igSameLine(0,-1); + _sgimgui_draw_pipeline_panel(ctx, ctx->pipeline_window.sel_pip); +} + +SOKOL_API_IMPL void sgimgui_draw_attachments_window_content(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + _sgimgui_draw_attachments_list(ctx); + igSameLine(0,-1); + _sgimgui_draw_attachments_panel(ctx, ctx->attachments_window.sel_atts); +} + +SOKOL_API_IMPL void sgimgui_draw_capture_window_content(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + _sgimgui_draw_capture_list(ctx); + igSameLine(0,-1); + _sgimgui_draw_capture_panel(ctx); +} + +SOKOL_API_IMPL void sgimgui_draw_capabilities_window_content(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + _SOKOL_UNUSED(ctx); + _sgimgui_draw_caps_panel(); +} + +SOKOL_API_IMPL void sgimgui_draw_frame_stats_window_content(sgimgui_t* ctx) { + SOKOL_ASSERT(ctx && (ctx->init_tag == 0xABCDABCD)); + ctx->frame_stats_window.stats = sg_query_frame_stats(); + _sgimgui_draw_frame_stats_panel(ctx); +} + +#endif /* SOKOL_GFX_IMGUI_IMPL */ diff --git a/thirdparty/sokol/util/sokol_gl.h b/thirdparty/sokol/util/sokol_gl.h new file mode 100644 index 0000000..15b3880 --- /dev/null +++ b/thirdparty/sokol/util/sokol_gl.h @@ -0,0 +1,4259 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_GL_IMPL) +#define SOKOL_GL_IMPL +#endif +#ifndef SOKOL_GL_INCLUDED +/* + sokol_gl.h -- OpenGL 1.x style rendering on top of sokol_gfx.h + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_GL_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + The following defines are used by the implementation to select the + platform-specific embedded shader code (these are the same defines as + used by sokol_gfx.h and sokol_app.h): + + SOKOL_GLCORE + SOKOL_GLES3 + SOKOL_D3D11 + SOKOL_METAL + SOKOL_WGPU + + ...optionally provide the following macros to override defaults: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_GL_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_GL_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + + If sokol_gl.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_GL_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + Include the following headers before including sokol_gl.h: + + sokol_gfx.h + + Matrix functions have been taken from MESA and Regal. + + FEATURE OVERVIEW: + ================= + sokol_gl.h implements a subset of the OpenGLES 1.x feature set useful for + when you just want to quickly render a bunch of triangles or + lines without having to mess with buffers and shaders. + + The current feature set is mostly useful for debug visualizations + and simple UI-style 2D rendering: + + What's implemented: + - vertex components: + - position (x, y, z) + - 2D texture coords (u, v) + - color (r, g, b, a) + - primitive types: + - triangle list and strip + - line list and strip + - quad list (TODO: quad strips) + - point list + - one texture layer (no multi-texturing) + - viewport and scissor-rect with selectable origin (top-left or bottom-left) + - all GL 1.x matrix stack functions, and additionally equivalent + functions for gluPerspective and gluLookat + + Notable GLES 1.x features that are *NOT* implemented: + - vertex lighting (this is the most likely GL feature that might be added later) + - vertex arrays (although providing whole chunks of vertex data at once + might be a useful feature for a later version) + - texture coordinate generation + - line width + - all pixel store functions + - no ALPHA_TEST + - no clear functions (clearing is handled by the sokol-gfx render pass) + - fog + + Notable differences to GL: + - No "enum soup" for render states etc, instead there's a + 'pipeline stack', this is similar to GL's matrix stack, + but for pipeline-state-objects. The pipeline object at + the top of the pipeline stack defines the active set of render states + - All angles are in radians, not degrees (note the sgl_rad() and + sgl_deg() conversion functions) + - No enable/disable state for scissor test, this is always enabled + + STEP BY STEP: + ============= + --- To initialize sokol-gl, call: + + sgl_setup(const sgl_desc_t* desc) + + NOTE that sgl_setup() must be called *after* initializing sokol-gfx + (via sg_setup). This is because sgl_setup() needs to create + sokol-gfx resource objects. + + If you're intending to render to the default pass, and also don't + want to tweak memory usage, and don't want any logging output you can + just keep sgl_desc_t zero-initialized: + + sgl_setup(&(sgl_desc_t*){ 0 }); + + In this case, sokol-gl will create internal sg_pipeline objects that + are compatible with the sokol-app default framebuffer. + + I would recommend to at least install a logging callback so that + you'll see any warnings and errors. The easiest way is through + sokol_log.h: + + #include "sokol_log.h" + + sgl_setup(&(sgl_desc_t){ + .logger.func = slog_func. + }); + + If you want to render into a framebuffer with different pixel-format + and MSAA attributes you need to provide the matching attributes in the + sgl_setup() call: + + sgl_setup(&(sgl_desc_t*){ + .color_format = SG_PIXELFORMAT_..., + .depth_format = SG_PIXELFORMAT_..., + .sample_count = ..., + }); + + To reduce memory usage, or if you need to create more then the default number of + contexts, pipelines, vertices or draw commands, set the following sgl_desc_t + members: + + .context_pool_size (default: 4) + .pipeline_pool_size (default: 64) + .max_vertices (default: 64k) + .max_commands (default: 16k) + + Finally you can change the face winding for front-facing triangles + and quads: + + .face_winding - default is SG_FACEWINDING_CCW + + The default winding for front faces is counter-clock-wise. This is + the same as OpenGL's default, but different from sokol-gfx. + + --- Optionally create additional context objects if you want to render into + multiple sokol-gfx render passes (or generally if you want to + use multiple independent sokol-gl "state buckets") + + sgl_context ctx = sgl_make_context(const sgl_context_desc_t* desc) + + For details on rendering with sokol-gl contexts, search below for + WORKING WITH CONTEXTS. + + --- Optionally create pipeline-state-objects if you need render state + that differs from sokol-gl's default state: + + sgl_pipeline pip = sgl_make_pipeline(const sg_pipeline_desc* desc) + + ...this creates a pipeline object that's compatible with the currently + active context, alternatively call: + + sgl_pipeline_pip = sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline_desc* desc) + + ...to create a pipeline object that's compatible with an explicitly + provided context. + + The similarity with sokol_gfx.h's sg_pipeline type and sg_make_pipeline() + function is intended. sgl_make_pipeline() also takes a standard + sokol-gfx sg_pipeline_desc object to describe the render state, but + without: + - shader + - vertex layout + - color- and depth-pixel-formats + - primitive type (lines, triangles, ...) + - MSAA sample count + Those will be filled in by sgl_make_pipeline(). Note that each + call to sgl_make_pipeline() needs to create several sokol-gfx + pipeline objects (one for each primitive type). + + 'depth.write_enabled' will be forced to 'false' if the context this + pipeline object is intended for has its depth pixel format set to + SG_PIXELFORMAT_NONE (which means the framebuffer this context is used + with doesn't have a depth-stencil surface). + + --- if you need to destroy sgl_pipeline objects before sgl_shutdown(): + + sgl_destroy_pipeline(sgl_pipeline pip) + + --- After sgl_setup() you can call any of the sokol-gl functions anywhere + in a frame, *except* sgl_draw(). The 'vanilla' functions + will only change internal sokol-gl state, and not call any sokol-gfx + functions. + + --- Unlike OpenGL, sokol-gl has a function to reset internal state to + a known default. This is useful at the start of a sequence of + rendering operations: + + void sgl_defaults(void) + + This will set the following default state: + + - current texture coordinate to u=0.0f, v=0.0f + - current color to white (rgba all 1.0f) + - current point size to 1.0f + - unbind the current texture and texturing will be disabled + - *all* matrices will be set to identity (also the projection matrix) + - the default render state will be set by loading the 'default pipeline' + into the top of the pipeline stack + + The current matrix- and pipeline-stack-depths will not be changed by + sgl_defaults(). + + --- change the currently active renderstate through the + pipeline-stack functions, this works similar to the + traditional GL matrix stack: + + ...load the default pipeline state on the top of the pipeline stack: + + sgl_load_default_pipeline() + + ...load a specific pipeline on the top of the pipeline stack: + + sgl_load_pipeline(sgl_pipeline pip) + + ...push and pop the pipeline stack: + sgl_push_pipeline() + sgl_pop_pipeline() + + --- control texturing with: + + sgl_enable_texture() + sgl_disable_texture() + sgl_texture(sg_image img, sg_sampler smp) + + NOTE: the img and smp handles can be invalid (SG_INVALID_ID), in this + case, sokol-gl will fall back to the internal default (white) texture + and sampler. + + --- set the current viewport and scissor rect with: + + sgl_viewport(int x, int y, int w, int h, bool origin_top_left) + sgl_scissor_rect(int x, int y, int w, int h, bool origin_top_left) + + ...or call these alternatives which take float arguments (this might allow + to avoid casting between float and integer in more strongly typed languages + when floating point pixel coordinates are used): + + sgl_viewportf(float x, float y, float w, float h, bool origin_top_left) + sgl_scissor_rectf(float x, float y, float w, float h, bool origin_top_left) + + ...these calls add a new command to the internal command queue, so + that the viewport or scissor rect are set at the right time relative + to other sokol-gl calls. + + --- adjust the transform matrices, matrix manipulation works just like + the OpenGL matrix stack: + + ...set the current matrix mode: + + sgl_matrix_mode_modelview() + sgl_matrix_mode_projection() + sgl_matrix_mode_texture() + + ...load the identity matrix into the current matrix: + + sgl_load_identity() + + ...translate, rotate and scale the current matrix: + + sgl_translate(float x, float y, float z) + sgl_rotate(float angle_rad, float x, float y, float z) + sgl_scale(float x, float y, float z) + + NOTE that all angles in sokol-gl are in radians, not in degree. + Convert between radians and degree with the helper functions: + + float sgl_rad(float deg) - degrees to radians + float sgl_deg(float rad) - radians to degrees + + ...directly load the current matrix from a float[16] array: + + sgl_load_matrix(const float m[16]) + sgl_load_transpose_matrix(const float m[16]) + + ...directly multiply the current matrix from a float[16] array: + + sgl_mult_matrix(const float m[16]) + sgl_mult_transpose_matrix(const float m[16]) + + The memory layout of those float[16] arrays is the same as in OpenGL. + + ...more matrix functions: + + sgl_frustum(float left, float right, float bottom, float top, float near, float far) + sgl_ortho(float left, float right, float bottom, float top, float near, float far) + sgl_perspective(float fov_y, float aspect, float near, float far) + sgl_lookat(float eye_x, float eye_y, float eye_z, float center_x, float center_y, float center_z, float up_x, float up_y, float up_z) + + These functions work the same as glFrustum(), glOrtho(), gluPerspective() + and gluLookAt(). + + ...and finally to push / pop the current matrix stack: + + sgl_push_matrix(void) + sgl_pop_matrix(void) + + Again, these work the same as glPushMatrix() and glPopMatrix(). + + --- perform primitive rendering: + + ...set the current texture coordinate and color 'registers' with or + point size with: + + sgl_t2f(float u, float v) - set current texture coordinate + sgl_c*(...) - set current color + sgl_point_size(float size) - set current point size + + There are several functions for setting the color (as float values, + unsigned byte values, packed as unsigned 32-bit integer, with + and without alpha). + + NOTE that these are the only functions that can be called both inside + sgl_begin_*() / sgl_end() and outside. + + Also NOTE that point size is currently hardwired to 1.0f if the D3D11 + backend is used. + + ...start a primitive vertex sequence with: + + sgl_begin_points() + sgl_begin_lines() + sgl_begin_line_strip() + sgl_begin_triangles() + sgl_begin_triangle_strip() + sgl_begin_quads() + + ...after sgl_begin_*() specify vertices: + + sgl_v*(...) + sgl_v*_t*(...) + sgl_v*_c*(...) + sgl_v*_t*_c*(...) + + These functions write a new vertex to sokol-gl's internal vertex buffer, + optionally with texture-coords and color. If the texture coordinate + and/or color is missing, it will be taken from the current texture-coord + and color 'register'. + + ...finally, after specifying vertices, call: + + sgl_end() + + This will record a new draw command in sokol-gl's internal command + list, or it will extend the previous draw command if no relevant + state has changed since the last sgl_begin/end pair. + + --- inside a sokol-gfx rendering pass, call the sgl_draw() function + to render the currently active context: + + sgl_draw() + + ...or alternatively call: + + sgl_context_draw(ctx) + + ...to render an explicitly provided context. + + This will render everything that has been recorded in the context since + the last call to sgl_draw() through sokol-gfx, and will 'rewind' the internal + vertex-, uniform- and command-buffers. + + --- each sokol-gl context tracks an internal error code, to query the + current error code for the currently active context call: + + sgl_error_t sgl_error() + + ...alternatively with an explicit context argument: + + sgl_error_t sgl_context_error(ctx); + + ...which can return the following error codes: + + SGL_NO_ERROR - all OK, no error occurred since last sgl_draw() + SGL_ERROR_VERTICES_FULL - internal vertex buffer is full (checked in sgl_end()) + SGL_ERROR_UNIFORMS_FULL - the internal uniforms buffer is full (checked in sgl_end()) + SGL_ERROR_COMMANDS_FULL - the internal command buffer is full (checked in sgl_end()) + SGL_ERROR_STACK_OVERFLOW - matrix- or pipeline-stack overflow + SGL_ERROR_STACK_UNDERFLOW - matrix- or pipeline-stack underflow + SGL_ERROR_NO_CONTEXT - the active context no longer exists + + ...if sokol-gl is in an error-state, sgl_draw() will skip any rendering, + and reset the error code to SGL_NO_ERROR. + + RENDER LAYERS + ============= + Render layers allow to split sokol-gl rendering into separate draw-command + groups which can then be rendered separately in a sokol-gfx draw pass. This + allows to mix/interleave sokol-gl rendering with other render operations. + + Layered rendering is controlled through two functions: + + sgl_layer(int layer_id) + sgl_draw_layer(int layer_id) + + (and the context-variant sgl_draw_layer(): sgl_context_draw_layer() + + The sgl_layer() function sets the 'current layer', any sokol-gl calls + which internally record draw commands will also store the current layer + in the draw command, and later in a sokol-gfx render pass, a call + to sgl_draw_layer() will only render the draw commands that have + a matching layer. + + The default layer is '0', this is active after sokol-gl setup, and + is also restored at the start of a new frame (but *not* by calling + sgl_defaults()). + + NOTE that calling sgl_draw() is equivalent with sgl_draw_layer(0) + (in general you should either use either use sgl_draw() or + sgl_draw_layer() in an application, but not both). + + WORKING WITH CONTEXTS: + ====================== + If you want to render to more than one sokol-gfx render pass you need to + work with additional sokol-gl context objects (one context object for + each offscreen rendering pass, in addition to the implicitly created + 'default context'. + + All sokol-gl state is tracked per context, and there is always a "current + context" (with the notable exception that the currently set context is + destroyed, more on that later). + + Using multiple contexts can also be useful if you only render in + a single pass, but want to maintain multiple independent "state buckets". + + To create new context object, call: + + sgl_context ctx = sgl_make_context(&(sgl_context_desc){ + .max_vertices = ..., // default: 64k + .max_commands = ..., // default: 16k + .color_format = ..., + .depth_format = ..., + .sample_count = ..., + }); + + The color_format, depth_format and sample_count items must be compatible + with the render pass the sgl_draw() or sgL_context_draw() function + will be called in. + + Creating a context does *not* make the context current. To do this, call: + + sgl_set_context(ctx); + + The currently active context will implicitly be used by most sokol-gl functions + which don't take an explicit context handle as argument. + + To switch back to the default context, pass the global constant SGL_DEFAULT_CONTEXT: + + sgl_set_context(SGL_DEFAULT_CONTEXT); + + ...or alternatively use the function sgl_default_context() instead of the + global constant: + + sgl_set_context(sgl_default_context()); + + To get the currently active context, call: + + sgl_context cur_ctx = sgl_get_context(); + + The following functions exist in two variants, one which use the currently + active context (set with sgl_set_context()), and another version which + takes an explicit context handle instead: + + sgl_make_pipeline() vs sgl_context_make_pipeline() + sgl_error() vs sgl_context_error(); + sgl_draw() vs sgl_context_draw(); + + Except for using the currently active context versus a provided context + handle, the two variants are exactlyidentical, e.g. the following + code sequences do the same thing: + + sgl_set_context(ctx); + sgl_pipeline pip = sgl_make_pipeline(...); + sgl_error_t err = sgl_error(); + sgl_draw(); + + vs + + sgl_pipeline pip = sgl_context_make_pipeline(ctx, ...); + sgl_error_t err = sgl_context_error(ctx); + sgl_context_draw(ctx); + + Destroying the currently active context is a 'soft error'. All following + calls which require a currently active context will silently fail, + and sgl_error() will return SGL_ERROR_NO_CONTEXT. + + UNDER THE HOOD: + =============== + sokol_gl.h works by recording vertex data and rendering commands into + memory buffers, and then drawing the recorded commands via sokol_gfx.h + + The only functions which call into sokol_gfx.h are: + - sgl_setup() + - sgl_shutdown() + - sgl_draw() (and variants) + + sgl_setup() must be called after initializing sokol-gfx. + sgl_shutdown() must be called before shutting down sokol-gfx. + sgl_draw() must be called once per frame inside a sokol-gfx render pass. + + All other sokol-gl function can be called anywhere in a frame, since + they just record data into memory buffers owned by sokol-gl. + + What happens in: + + sgl_setup(): + Unique resources shared by all contexts are created: + - a shader object (using embedded shader source or byte code) + - an 8x8 white default texture + The default context is created, which involves: + - 3 memory buffers are created, one for vertex data, + one for uniform data, and one for commands + - a dynamic vertex buffer is created + - the default sgl_pipeline object is created, which involves + creating 5 sg_pipeline objects + + One vertex is 24 bytes: + - float3 position + - float2 texture coords + - uint32_t color + + One uniform block is 128 bytes: + - mat4 model-view-projection matrix + - mat4 texture matrix + + One draw command is ca. 24 bytes for the actual + command code plus command arguments. + + Each sgl_end() consumes one command, and one uniform block + (only when the matrices have changed). + The required size for one sgl_begin/end pair is (at most): + + (152 + 24 * num_verts) bytes + + sgl_shutdown(): + - all sokol-gfx resources (buffer, shader, default-texture and + all pipeline objects) are destroyed + - the 3 memory buffers are freed + + sgl_draw() (and variants) + - copy all recorded vertex data into the dynamic sokol-gfx buffer + via a call to sg_update_buffer() + - for each recorded command: + - if the layer number stored in the command doesn't match + the layer that's to be rendered, skip to the next + command + - if it's a viewport command, call sg_apply_viewport() + - if it's a scissor-rect command, call sg_apply_scissor_rect() + - if it's a draw command: + - depending on what has changed since the last draw command, + call sg_apply_pipeline(), sg_apply_bindings() and + sg_apply_uniforms() + - finally call sg_draw() + + All other functions only modify the internally tracked state, add + data to the vertex, uniform and command buffers, or manipulate + the matrix stack. + + ON DRAW COMMAND MERGING + ======================= + Not every call to sgl_end() will automatically record a new draw command. + If possible, the previous draw command will simply be extended, + resulting in fewer actual draw calls later in sgl_draw(). + + A draw command will be merged with the previous command if "no relevant + state has changed" since the last sgl_end(), meaning: + + - no calls to sgl_viewport() and sgl_scissor_rect() + - the primitive type hasn't changed + - the primitive type isn't a 'strip type' (no line or triangle strip) + - the pipeline state object hasn't changed + - the current layer hasn't changed + - none of the matrices has changed + - none of the texture state has changed + + Merging a draw command simply means that the number of vertices + to render in the previous draw command will be incremented by the + number of vertices in the new draw command. + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + sgl_setup(&(sgl_desc_t){ + // ... + .allocator = { + .alloc_fn = my_alloc, + .free_fn = my_free, + .user_data = ...; + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call, + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + sgl_setup(&(sgl_desc_t){ + // ... + .logger.func = slog_func + }); + + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'sgl' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SGL_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_gl.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-gl like this: + + sgl_setup(&(sgl_desc_t){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); + + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. + + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_GL_INCLUDED (1) +#include +#include +#include // size_t, offsetof + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_gl.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_GL_API_DECL) +#define SOKOL_GL_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_GL_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GL_IMPL) +#define SOKOL_GL_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_GL_API_DECL __declspec(dllimport) +#else +#define SOKOL_GL_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + sgl_log_item_t + + Log items are defined via X-Macros, and expanded to an + enum 'sgl_log_item' - and in debug mode only - corresponding strings. + + Used as parameter in the logging callback. +*/ +#define _SGL_LOG_ITEMS \ + _SGL_LOGITEM_XMACRO(OK, "Ok") \ + _SGL_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SGL_LOGITEM_XMACRO(MAKE_PIPELINE_FAILED, "sg_make_pipeline() failed") \ + _SGL_LOGITEM_XMACRO(PIPELINE_POOL_EXHAUSTED, "pipeline pool exhausted (use sgl_desc_t.pipeline_pool_size to adjust)") \ + _SGL_LOGITEM_XMACRO(ADD_COMMIT_LISTENER_FAILED, "sg_add_commit_listener() failed") \ + _SGL_LOGITEM_XMACRO(CONTEXT_POOL_EXHAUSTED, "context pool exhausted (use sgl_desc_t.context_pool_size to adjust)") \ + _SGL_LOGITEM_XMACRO(CANNOT_DESTROY_DEFAULT_CONTEXT, "cannot destroy default context") \ + +#define _SGL_LOGITEM_XMACRO(item,msg) SGL_LOGITEM_##item, +typedef enum sgl_log_item_t { + _SGL_LOG_ITEMS +} sgl_log_item_t; +#undef _SGL_LOGITEM_XMACRO + +/* + sgl_logger_t + + Used in sgl_desc_t to provide a custom logging and error reporting + callback to sokol-gl. +*/ +typedef struct sgl_logger_t { + void (*func)( + const char* tag, // always "sgl" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SGL_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_gl.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} sgl_logger_t; + +/* sokol_gl pipeline handle (created with sgl_make_pipeline()) */ +typedef struct sgl_pipeline { uint32_t id; } sgl_pipeline; + +/* a context handle (created with sgl_make_context()) */ +typedef struct sgl_context { uint32_t id; } sgl_context; + +/* + sgl_error_t + + Errors are reset each frame after calling sgl_draw(), + get the last error code with sgl_error() +*/ +typedef enum sgl_error_t { + SGL_NO_ERROR = 0, + SGL_ERROR_VERTICES_FULL, + SGL_ERROR_UNIFORMS_FULL, + SGL_ERROR_COMMANDS_FULL, + SGL_ERROR_STACK_OVERFLOW, + SGL_ERROR_STACK_UNDERFLOW, + SGL_ERROR_NO_CONTEXT, +} sgl_error_t; + +/* + sgl_context_desc_t + + Describes the initialization parameters of a rendering context. + Creating additional contexts is useful if you want to render + in separate sokol-gfx passes. +*/ +typedef struct sgl_context_desc_t { + int max_vertices; // default: 64k + int max_commands; // default: 16k + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; +} sgl_context_desc_t; + +/* + sgl_allocator_t + + Used in sgl_desc_t to provide custom memory-alloc and -free functions + to sokol_gl.h. If memory management should be overridden, both the + alloc and free function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct sgl_allocator_t { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} sgl_allocator_t; + +typedef struct sgl_desc_t { + int max_vertices; // default: 64k + int max_commands; // default: 16k + int context_pool_size; // max number of contexts (including default context), default: 4 + int pipeline_pool_size; // size of internal pipeline pool, default: 64 + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; + sg_face_winding face_winding; // default: SG_FACEWINDING_CCW + sgl_allocator_t allocator; // optional memory allocation overrides (default: malloc/free) + sgl_logger_t logger; // optional log function override (default: NO LOGGING) +} sgl_desc_t; + +/* the default context handle */ +static const sgl_context SGL_DEFAULT_CONTEXT = { 0x00010001 }; + +/* setup/shutdown/misc */ +SOKOL_GL_API_DECL void sgl_setup(const sgl_desc_t* desc); +SOKOL_GL_API_DECL void sgl_shutdown(void); +SOKOL_GL_API_DECL float sgl_rad(float deg); +SOKOL_GL_API_DECL float sgl_deg(float rad); +SOKOL_GL_API_DECL sgl_error_t sgl_error(void); +SOKOL_GL_API_DECL sgl_error_t sgl_context_error(sgl_context ctx); + +/* context functions */ +SOKOL_GL_API_DECL sgl_context sgl_make_context(const sgl_context_desc_t* desc); +SOKOL_GL_API_DECL void sgl_destroy_context(sgl_context ctx); +SOKOL_GL_API_DECL void sgl_set_context(sgl_context ctx); +SOKOL_GL_API_DECL sgl_context sgl_get_context(void); +SOKOL_GL_API_DECL sgl_context sgl_default_context(void); + +/* draw recorded commands (call inside a sokol-gfx render pass) */ +SOKOL_GL_API_DECL void sgl_draw(void); +SOKOL_GL_API_DECL void sgl_context_draw(sgl_context ctx); +SOKOL_GL_API_DECL void sgl_draw_layer(int layer_id); +SOKOL_GL_API_DECL void sgl_context_draw_layer(sgl_context ctx, int layer_id); + +/* create and destroy pipeline objects */ +SOKOL_GL_API_DECL sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc* desc); +SOKOL_GL_API_DECL sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline_desc* desc); +SOKOL_GL_API_DECL void sgl_destroy_pipeline(sgl_pipeline pip); + +/* render state functions */ +SOKOL_GL_API_DECL void sgl_defaults(void); +SOKOL_GL_API_DECL void sgl_viewport(int x, int y, int w, int h, bool origin_top_left); +SOKOL_GL_API_DECL void sgl_viewportf(float x, float y, float w, float h, bool origin_top_left); +SOKOL_GL_API_DECL void sgl_scissor_rect(int x, int y, int w, int h, bool origin_top_left); +SOKOL_GL_API_DECL void sgl_scissor_rectf(float x, float y, float w, float h, bool origin_top_left); +SOKOL_GL_API_DECL void sgl_enable_texture(void); +SOKOL_GL_API_DECL void sgl_disable_texture(void); +SOKOL_GL_API_DECL void sgl_texture(sg_image img, sg_sampler smp); +SOKOL_GL_API_DECL void sgl_layer(int layer_id); + +/* pipeline stack functions */ +SOKOL_GL_API_DECL void sgl_load_default_pipeline(void); +SOKOL_GL_API_DECL void sgl_load_pipeline(sgl_pipeline pip); +SOKOL_GL_API_DECL void sgl_push_pipeline(void); +SOKOL_GL_API_DECL void sgl_pop_pipeline(void); + +/* matrix stack functions */ +SOKOL_GL_API_DECL void sgl_matrix_mode_modelview(void); +SOKOL_GL_API_DECL void sgl_matrix_mode_projection(void); +SOKOL_GL_API_DECL void sgl_matrix_mode_texture(void); +SOKOL_GL_API_DECL void sgl_load_identity(void); +SOKOL_GL_API_DECL void sgl_load_matrix(const float m[16]); +SOKOL_GL_API_DECL void sgl_load_transpose_matrix(const float m[16]); +SOKOL_GL_API_DECL void sgl_mult_matrix(const float m[16]); +SOKOL_GL_API_DECL void sgl_mult_transpose_matrix(const float m[16]); +SOKOL_GL_API_DECL void sgl_rotate(float angle_rad, float x, float y, float z); +SOKOL_GL_API_DECL void sgl_scale(float x, float y, float z); +SOKOL_GL_API_DECL void sgl_translate(float x, float y, float z); +SOKOL_GL_API_DECL void sgl_frustum(float l, float r, float b, float t, float n, float f); +SOKOL_GL_API_DECL void sgl_ortho(float l, float r, float b, float t, float n, float f); +SOKOL_GL_API_DECL void sgl_perspective(float fov_y, float aspect, float z_near, float z_far); +SOKOL_GL_API_DECL void sgl_lookat(float eye_x, float eye_y, float eye_z, float center_x, float center_y, float center_z, float up_x, float up_y, float up_z); +SOKOL_GL_API_DECL void sgl_push_matrix(void); +SOKOL_GL_API_DECL void sgl_pop_matrix(void); + +/* these functions only set the internal 'current texcoord / color / point size' (valid inside or outside begin/end) */ +SOKOL_GL_API_DECL void sgl_t2f(float u, float v); +SOKOL_GL_API_DECL void sgl_c3f(float r, float g, float b); +SOKOL_GL_API_DECL void sgl_c4f(float r, float g, float b, float a); +SOKOL_GL_API_DECL void sgl_c3b(uint8_t r, uint8_t g, uint8_t b); +SOKOL_GL_API_DECL void sgl_c4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_GL_API_DECL void sgl_c1i(uint32_t rgba); +SOKOL_GL_API_DECL void sgl_point_size(float s); + +/* define primitives, each begin/end is one draw command */ +SOKOL_GL_API_DECL void sgl_begin_points(void); +SOKOL_GL_API_DECL void sgl_begin_lines(void); +SOKOL_GL_API_DECL void sgl_begin_line_strip(void); +SOKOL_GL_API_DECL void sgl_begin_triangles(void); +SOKOL_GL_API_DECL void sgl_begin_triangle_strip(void); +SOKOL_GL_API_DECL void sgl_begin_quads(void); +SOKOL_GL_API_DECL void sgl_v2f(float x, float y); +SOKOL_GL_API_DECL void sgl_v3f(float x, float y, float z); +SOKOL_GL_API_DECL void sgl_v2f_t2f(float x, float y, float u, float v); +SOKOL_GL_API_DECL void sgl_v3f_t2f(float x, float y, float z, float u, float v); +SOKOL_GL_API_DECL void sgl_v2f_c3f(float x, float y, float r, float g, float b); +SOKOL_GL_API_DECL void sgl_v2f_c3b(float x, float y, uint8_t r, uint8_t g, uint8_t b); +SOKOL_GL_API_DECL void sgl_v2f_c4f(float x, float y, float r, float g, float b, float a); +SOKOL_GL_API_DECL void sgl_v2f_c4b(float x, float y, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_GL_API_DECL void sgl_v2f_c1i(float x, float y, uint32_t rgba); +SOKOL_GL_API_DECL void sgl_v3f_c3f(float x, float y, float z, float r, float g, float b); +SOKOL_GL_API_DECL void sgl_v3f_c3b(float x, float y, float z, uint8_t r, uint8_t g, uint8_t b); +SOKOL_GL_API_DECL void sgl_v3f_c4f(float x, float y, float z, float r, float g, float b, float a); +SOKOL_GL_API_DECL void sgl_v3f_c4b(float x, float y, float z, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_GL_API_DECL void sgl_v3f_c1i(float x, float y, float z, uint32_t rgba); +SOKOL_GL_API_DECL void sgl_v2f_t2f_c3f(float x, float y, float u, float v, float r, float g, float b); +SOKOL_GL_API_DECL void sgl_v2f_t2f_c3b(float x, float y, float u, float v, uint8_t r, uint8_t g, uint8_t b); +SOKOL_GL_API_DECL void sgl_v2f_t2f_c4f(float x, float y, float u, float v, float r, float g, float b, float a); +SOKOL_GL_API_DECL void sgl_v2f_t2f_c4b(float x, float y, float u, float v, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_GL_API_DECL void sgl_v2f_t2f_c1i(float x, float y, float u, float v, uint32_t rgba); +SOKOL_GL_API_DECL void sgl_v3f_t2f_c3f(float x, float y, float z, float u, float v, float r, float g, float b); +SOKOL_GL_API_DECL void sgl_v3f_t2f_c3b(float x, float y, float z, float u, float v, uint8_t r, uint8_t g, uint8_t b); +SOKOL_GL_API_DECL void sgl_v3f_t2f_c4f(float x, float y, float z, float u, float v, float r, float g, float b, float a); +SOKOL_GL_API_DECL void sgl_v3f_t2f_c4b(float x, float y, float z, float u, float v, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_GL_API_DECL void sgl_v3f_t2f_c1i(float x, float y, float z, float u, float v, uint32_t rgba); +SOKOL_GL_API_DECL void sgl_end(void); + +#ifdef __cplusplus +} /* extern "C" */ + +/* reference-based equivalents for C++ */ +inline void sgl_setup(const sgl_desc_t& desc) { return sgl_setup(&desc); } +inline sgl_context sgl_make_context(const sgl_context_desc_t& desc) { return sgl_make_context(&desc); } +inline sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc& desc) { return sgl_make_pipeline(&desc); } +inline sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline_desc& desc) { return sgl_context_make_pipeline(ctx, &desc); } +#endif +#endif /* SOKOL_GL_INCLUDED */ + +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation +#ifdef SOKOL_GL_IMPL +#define SOKOL_GL_IMPL_INCLUDED (1) + +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sgl_desc_t.allocator to override memory allocation functions" +#endif + +#include // malloc/free +#include // memset +#include // M_PI, sqrtf, sinf, cosf + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338327 +#endif + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif + +#define _sgl_def(val, def) (((val) == 0) ? (def) : (val)) +#define _SGL_INIT_COOKIE (0xABCDABCD) + +/* + Embedded source code compiled with: + + sokol-shdc -i sgl.glsl -o sgl.h -l glsl410:glsl300es:hlsl4:metal_macos:metal_ios:metal_sim:wgpu -b + + (not that for Metal and D3D11 byte code, sokol-shdc must be run + on macOS and Windows) + + @vs vs + uniform vs_params { + mat4 mvp; + mat4 tm; + }; + in vec4 position; + in vec2 texcoord0; + in vec4 color0; + in float psize; + out vec4 uv; + out vec4 color; + void main() { + gl_Position = mvp * position; + #ifndef SOKOL_WGSL + gl_PointSize = psize; + #endif + uv = tm * vec4(texcoord0, 0.0, 1.0); + color = color0; + } + @end + + @fs fs + uniform texture2D tex; + uniform sampler smp; + in vec4 uv; + in vec4 color; + out vec4 frag_color; + void main() { + frag_color = texture(sampler2D(tex, smp), uv.xy) * color; + } + @end + + @program sgl vs fs +*/ + +#if defined(SOKOL_GLCORE) +static const uint8_t _sgl_vs_source_glsl410[520] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e, + 0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a, + 0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20, + 0x3d,0x20,0x33,0x29,0x20,0x69,0x6e,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x70,0x73, + 0x69,0x7a,0x65,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65, + 0x63,0x34,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f, + 0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76, + 0x65,0x63,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x6c, + 0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d, + 0x20,0x31,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c, + 0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74, + 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61, + 0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74,0x53, + 0x69,0x7a,0x65,0x20,0x3d,0x20,0x70,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61, + 0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73, + 0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x37, + 0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34,0x28,0x74,0x65,0x78,0x63,0x6f,0x6f, + 0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const uint8_t _sgl_fs_source_glsl410[222] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f, + 0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74, + 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34, + 0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x2e,0x78,0x79,0x29, + 0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_GLES3) +static const uint8_t _sgl_vs_source_glsl300es[481] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f, + 0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29, + 0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69, + 0x6f,0x6e,0x20,0x3d,0x20,0x33,0x29,0x20,0x69,0x6e,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x20,0x70,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34, + 0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63, + 0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x6f,0x75,0x74, + 0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79, + 0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32, + 0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30, + 0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b, + 0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b, + 0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d, + 0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20, + 0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74,0x53,0x69,0x7a,0x65,0x20,0x3d,0x20,0x70, + 0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d, + 0x61,0x74,0x34,0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d, + 0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65, + 0x63,0x34,0x28,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e, + 0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c, + 0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a, + 0x00, +}; +static const uint8_t _sgl_fs_source_glsl300es[253] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d, + 0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69, + 0x6f,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a, + 0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x68,0x69,0x67,0x68,0x70,0x20, + 0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34,0x20,0x75, + 0x76,0x3b,0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61, + 0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x2e,0x78,0x79,0x29,0x20, + 0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_METAL) +static const uint8_t _sgl_vs_bytecode_metal_macos[3317] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf5,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe0,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x76,0x25,0x5f,0x37,0x22,0xd0,0x3f, + 0x64,0xef,0xff,0xc3,0x45,0x1a,0x3d,0xb7,0x5e,0x83,0x13,0x96,0xd3,0x09,0xec,0x53, + 0x25,0xd5,0x7e,0x0c,0xed,0xb9,0x58,0x34,0x02,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x40,0x00,0x00,0x00,0x56,0x41,0x54, + 0x54,0x2a,0x00,0x04,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x80, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x00,0x02,0x80,0x70,0x73,0x69,0x7a,0x65,0x00,0x03,0x80,0x56,0x41,0x54, + 0x59,0x06,0x00,0x04,0x00,0x06,0x04,0x06,0x03,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, + 0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00, + 0x00,0xc4,0x0b,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00, + 0x00,0xee,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00, + 0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84, + 0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b, + 0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90, + 0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8, + 0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00,0x00,0x81,0x00,0x00, + 0x00,0x1b,0xc8,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x90,0x80,0x8a,0x18,0x87,0x77, + 0x90,0x07,0x79,0x28,0x87,0x71,0xa0,0x07,0x76,0xc8,0x87,0x36,0x90,0x87,0x77,0xa8, + 0x07,0x77,0x20,0x87,0x72,0x20,0x87,0x36,0x20,0x87,0x74,0xb0,0x87,0x74,0x20,0x87, + 0x72,0x68,0x83,0x79,0x88,0x07,0x79,0xa0,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76, + 0x08,0x07,0x7a,0x40,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c, + 0xd2,0x61,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8, + 0xa1,0x0d,0xc6,0x21,0x1c,0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79, + 0x28,0x07,0x80,0x60,0x87,0x72,0x98,0x87,0x79,0x68,0x03,0x78,0x90,0x87,0x72,0x18, + 0x87,0x74,0x98,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x76,0x08,0x07,0x72,0x00,0xcc, + 0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21, + 0x1c,0xda,0xa1,0x1c,0xda,0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e, + 0xda,0xa0,0x1c,0xd8,0x21,0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x06, + 0x77,0x78,0x87,0x36,0x30,0x07,0x79,0x08,0x87,0x76,0x28,0x87,0x36,0x80,0x87,0x77, + 0x48,0x07,0x77,0xa0,0x87,0x72,0x90,0x87,0x36,0x28,0x07,0x76,0x48,0x87,0x76,0x68, + 0x03,0x77,0x78,0x07,0x77,0x68,0x03,0x76,0x28,0x87,0x70,0x30,0x07,0x80,0x70,0x87, + 0x77,0x68,0x83,0x74,0x70,0x07,0x73,0x98,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76, + 0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d, + 0xda,0x40,0x1d,0xea,0xa1,0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca, + 0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36, + 0x70,0x87,0x70,0x70,0x87,0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0, + 0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6, + 0x21,0x1d,0xce,0xc1,0x1d,0xca,0x81,0x1c,0xda,0x40,0x1f,0xca,0x41,0x1e,0xde,0x61, + 0x1e,0xda,0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90, + 0x87,0x7a,0x28,0x07,0x80,0x70,0x87,0x77,0x68,0x03,0x7a,0x90,0x87,0x70,0x80,0x07, + 0x78,0x48,0x07,0x77,0x38,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41, + 0x1e,0xea,0xa1,0x1c,0x00,0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e, + 0xe4,0xe1,0x1d,0xe8,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0x40,0x1c,0xea, + 0xc1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c, + 0x00,0x88,0x7a,0x70,0x87,0x79,0x08,0x07,0x73,0x28,0x87,0x36,0x30,0x07,0x78,0x68, + 0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea, + 0x61,0x1e,0xca,0xa1,0x0d,0xe6,0xe1,0x1d,0xcc,0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1, + 0x1d,0xc2,0x81,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x36,0x18,0x42, + 0x01,0x2c,0x40,0x05,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40, + 0x00,0x89,0x20,0x00,0x00,0x20,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85, + 0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a, + 0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x44,0x33,0x00,0xc3,0x08,0x04,0x60,0x89,0x10,0x02, + 0x18,0x46,0x10,0x80,0x24,0x08,0x33,0x51,0xf3,0x40,0x0f,0xf2,0x50,0x0f,0xe3,0x40, + 0x0f,0x6e,0xd0,0x0e,0xe5,0x40,0x0f,0xe1,0xc0,0x0e,0x7a,0xa0,0x07,0xed,0x10,0x0e, + 0xf4,0x20,0x0f,0xe9,0x80,0x0f,0x28,0x20,0x07,0x49,0x53,0x44,0x09,0x93,0x5f,0x49, + 0xff,0x03,0x44,0x00,0x23,0x21,0xa1,0x94,0x41,0x04,0x43,0x28,0x86,0x08,0x23,0x80, + 0x43,0x68,0x20,0x60,0x8e,0x00,0x0c,0x52,0x60,0xcd,0x11,0x80,0xc2,0x20,0x42,0x20, + 0x0c,0x23,0x10,0xcb,0x08,0x00,0x00,0x00,0x00,0x13,0xb2,0x70,0x48,0x07,0x79,0xb0, + 0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x76,0x08,0x87, + 0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83,0x38,0x70,0x03,0x38, + 0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0, + 0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x71,0x60,0x07, + 0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e,0x73,0x20,0x07,0x7a, + 0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60, + 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0, + 0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07, + 0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73, + 0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40, + 0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07, + 0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72, + 0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0, + 0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20,0x07,0x7a,0x20,0x07, + 0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x72,0x50,0x07,0x76, + 0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x50, + 0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07, + 0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74, + 0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60, + 0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x49,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00, + 0xc8,0x02,0x01,0x00,0x00,0x0b,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c, + 0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50,0x04,0x05,0x18, + 0x50,0x08,0x65,0x50,0x80,0x02,0x05,0x51,0x20,0xd4,0x46,0x00,0x88,0x8d,0x25,0x34, + 0x01,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x02,0x01,0x00,0x00,0x1a,0x03,0x4c, + 0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x32, + 0x28,0x00,0xb3,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62, + 0x2c,0x81,0x22,0x2c,0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e, + 0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6, + 0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06, + 0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25, + 0x58,0x90,0x45,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x82, + 0x45,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56, + 0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50, + 0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61, + 0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04, + 0x65,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98, + 0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1, + 0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x51,0x58,0x9a,0x9c, + 0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0xd7,0x5b,0x1d,0x1d,0x5c,0x1d,0x1d,0x97,0xba, + 0xb9,0x32,0x39,0x14,0xb6,0xb7,0x31,0x37,0x98,0x14,0x46,0x61,0x69,0x72,0x2e,0x61, + 0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x6e,0x61,0x6d,0x65,0x34,0xcc,0xd8,0xde, + 0xc2,0xe8,0x68,0xc8,0x84,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95, + 0x51,0xa8,0xb3,0x1b,0xc2,0x28,0x8f,0x02,0x29,0x91,0x22,0x29,0x93,0x42,0x71,0xa9, + 0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0x61,0x31,0xf6,0xc6,0xf6,0x26, + 0x37,0x84,0x51,0x1e,0xc5,0x52,0x22,0x45,0x52,0x26,0xe5,0x22,0x13,0x96,0x26,0xe7, + 0x02,0xf7,0x36,0x97,0x46,0x97,0xf6,0xe6,0xc6,0xe5,0x8c,0xed,0x0b,0xea,0x6d,0x2e, + 0x8d,0x2e,0xed,0xcd,0x6d,0x88,0xa2,0x64,0x4a,0xa4,0x48,0xca,0xa4,0x68,0x74,0xc2, + 0xd2,0xe4,0x5c,0xe0,0xde,0xd2,0xdc,0xe8,0xbe,0xe6,0xd2,0xf4,0xca,0x58,0x98,0xb1, + 0xbd,0x85,0xd1,0x91,0x39,0x63,0xfb,0x82,0x7a,0x4b,0x73,0xa3,0x9b,0x4a,0xd3,0x2b, + 0x1b,0xa2,0x28,0x9c,0x12,0x29,0x9d,0x32,0x29,0xde,0x10,0x44,0xa9,0x14,0x4c,0xd9, + 0x94,0x8f,0x50,0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0x57,0x9a,0x1b, + 0x5c,0x1d,0x1d,0xa5,0xb0,0x34,0x39,0x17,0xb6,0xb7,0xb1,0x30,0xba,0xb4,0x37,0xb7, + 0xaf,0x34,0x37,0xb2,0x32,0x3c,0x7a,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64, + 0x28,0x5f,0x5f,0x61,0x69,0x72,0x5f,0x70,0x6c,0x61,0x63,0x65,0x68,0x6f,0x6c,0x64, + 0x65,0x72,0x5f,0x5f,0x29,0x44,0xe0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x86,0x50, + 0x8b,0xa0,0x84,0x81,0x22,0x06,0x8b,0xb0,0x04,0xca,0x18,0x28,0x91,0x22,0x29,0x93, + 0x42,0x06,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x98,0xd0,0x95,0xe1,0x8d,0xbd,0xbd, + 0xc9,0x91,0xc1,0x0c,0xa1,0x96,0x40,0x09,0x03,0x45,0x0c,0x96,0x60,0x09,0x94,0x31, + 0x50,0x22,0xc5,0x0c,0x94,0x49,0x39,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43, + 0xa8,0x65,0x50,0xc2,0x40,0x11,0x83,0x65,0x58,0x02,0x65,0x0c,0x94,0x48,0x91,0x94, + 0x49,0x49,0x03,0x16,0x70,0x73,0x69,0x7a,0x65,0x43,0xa8,0xc5,0x50,0xc2,0x40,0x11, + 0x83,0xc5,0x58,0x02,0x65,0x0c,0x94,0x48,0xe9,0x94,0x49,0x59,0x03,0x2a,0x61,0x69, + 0x72,0x2e,0x62,0x75,0x66,0x66,0x65,0x72,0x7c,0xc2,0xd2,0xe4,0x5c,0xc4,0xea,0xcc, + 0xcc,0xca,0xe4,0xbe,0xe6,0xd2,0xf4,0xca,0x88,0x84,0xa5,0xc9,0xb9,0xc8,0x95,0x85, + 0x91,0x91,0x0a,0x4b,0x93,0x73,0x99,0xa3,0x93,0xab,0x1b,0xa3,0xfb,0xa2,0xcb,0x83, + 0x2b,0xfb,0x4a,0x73,0x33,0x7b,0x23,0x62,0xc6,0xf6,0x16,0x46,0x47,0x83,0x47,0xc3, + 0xa1,0xcd,0x0e,0x8e,0x02,0x5d,0xdb,0x10,0x6a,0x11,0x16,0x62,0x11,0x94,0x38,0x50, + 0xe4,0x60,0x21,0x16,0x62,0x11,0x94,0x38,0x50,0xe6,0x80,0x51,0x58,0x9a,0x9c,0x4b, + 0x98,0xdc,0xd9,0x17,0x5d,0x1e,0x5c,0xd9,0xd7,0x5c,0x9a,0x5e,0x19,0xaf,0xb0,0x34, + 0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x30,0xb6,0xb4,0x33,0xb7, + 0xaf,0xb9,0x34,0xbd,0x32,0x26,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x1c, + 0xbe,0x62,0x72,0x86,0x90,0xc1,0x52,0x28,0x6d,0xa0,0xb8,0xc1,0x72,0x28,0x62,0xb0, + 0x08,0x4b,0xa0,0xbc,0x81,0x02,0x07,0x0a,0x1d,0x28,0x75,0xb0,0x1c,0x8a,0x1d,0x2c, + 0x89,0x12,0x29,0x77,0xa0,0x4c,0x0a,0x1e,0x0c,0x51,0x94,0x32,0x50,0xd0,0x40,0x51, + 0x03,0x85,0x0d,0x94,0x3c,0x18,0x62,0x24,0x80,0x02,0x06,0x8a,0x1e,0xf0,0x79,0x6b, + 0x73,0x4b,0x83,0x7b,0xa3,0x2b,0x73,0xa3,0x03,0x19,0x43,0x0b,0x93,0xe3,0x33,0x95, + 0xd6,0x06,0xc7,0x56,0x06,0x32,0xb4,0xb2,0x02,0x42,0x25,0x14,0x14,0x34,0x44,0x50, + 0xfa,0x60,0x88,0xa1,0xf0,0x81,0xe2,0x07,0x8d,0x32,0xc4,0x50,0xfe,0x40,0xf9,0x83, + 0x46,0x19,0x11,0xb1,0x03,0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3b,0xbc,0x03,0x39,0xd4, + 0x03,0x3b,0x94,0x83,0x1b,0x98,0x03,0x3b,0x84,0xc3,0x39,0xcc,0xc3,0x14,0x21,0x18, + 0x46,0x28,0xec,0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0x90,0x0e,0xe4,0x50,0x0e,0xee,0x40, + 0x0f,0x53,0x82,0x62,0xc4,0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f, + 0xf3,0x90,0x0e,0xef,0xe0,0x0e,0x53,0x02,0x63,0x04,0x15,0x0e,0xe9,0x20,0x0f,0x6e, + 0xc0,0x0e,0xe1,0xe0,0x0e,0xe7,0x50,0x0f,0xe1,0x70,0x0e,0xe5,0xf0,0x0b,0xf6,0x50, + 0x0e,0xf2,0x30,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x40,0x46,0x4c,0xe1,0x90,0x0e, + 0xf2,0xe0,0x06,0xe3,0xf0,0x0e,0xed,0x00,0x0f,0xe9,0xc0,0x0e,0xe5,0xf0,0x0b,0xef, + 0x00,0x0f,0xf4,0x90,0x0e,0xef,0xe0,0x0e,0xf3,0x30,0x65,0x50,0x18,0x67,0x84,0x12, + 0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f,0xf4,0x50,0x0e,0xf8,0x30,0x25, + 0xd8,0x03,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80, + 0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80, + 0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80, + 0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88, + 0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78, + 0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03, + 0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f, + 0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c, + 0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39, + 0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b, + 0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60, + 0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87, + 0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e, + 0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c, + 0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6, + 0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94, + 0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03, + 0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07, + 0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f, + 0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33, + 0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc, + 0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c, + 0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78, + 0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca, + 0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21, + 0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43, + 0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87, + 0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c, + 0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e, + 0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c, + 0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c, + 0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00, + 0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x3e,0x00,0x00, + 0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0xf4,0xc6,0x22, + 0x86,0x61,0x18,0xc6,0x22,0x04,0x41,0x10,0xc6,0x22,0x82,0x20,0x08,0xa8,0x95,0x40, + 0x19,0x14,0x01,0xbd,0x11,0x00,0x1a,0x33,0x00,0x24,0x66,0x00,0x28,0xcc,0x00,0x00, + 0x00,0xe3,0x15,0x4b,0x94,0x65,0x11,0x05,0x65,0x90,0x21,0x1a,0x0c,0x13,0x02,0xf9, + 0x8c,0x57,0x3c,0x55,0xd7,0x2d,0x14,0x94,0x41,0x86,0xea,0x70,0x4c,0x08,0xe4,0x63, + 0x41,0x01,0x9f,0xf1,0x0a,0x4a,0x13,0x03,0x31,0x70,0x28,0x28,0x83,0x0c,0x1a,0x43, + 0x99,0x10,0xc8,0xc7,0x8a,0x00,0x3e,0xe3,0x15,0xd9,0x77,0x06,0x67,0x40,0x51,0x50, + 0x06,0x19,0xbe,0x48,0x33,0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6,0x2b,0x3c,0x32,0x68, + 0x03,0x36,0x20,0x03,0x0a,0xca,0x20,0xc3,0x18,0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b, + 0xc4,0x00,0x0d,0xe2,0x00,0x0e,0x3c,0x0a,0xca,0x20,0xc3,0x19,0x70,0x61,0x60,0x42, + 0x20,0x1f,0x0b,0x0a,0xf8,0x8c,0x57,0x9c,0x41,0x1b,0xd8,0x41,0x1d,0x88,0x01,0x05, + 0xc5,0x86,0x00,0x3e,0xb3,0x0d,0x61,0x10,0x00,0xb3,0x0d,0x41,0x1b,0x04,0xb3,0x0d, + 0xc1,0x23,0xcc,0x36,0x04,0x6e,0x30,0x64,0x10,0x10,0x03,0x00,0x00,0x09,0x00,0x00, + 0x00,0x5b,0x86,0x20,0x00,0x85,0x2d,0x43,0x11,0x80,0xc2,0x96,0x41,0x09,0x40,0x61, + 0xcb,0xf0,0x04,0xa0,0xb0,0x65,0xa0,0x02,0x50,0xd8,0x32,0x60,0x01,0x28,0x6c,0x19, + 0xba,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sgl_fs_bytecode_metal_macos[2825] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x09,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x30,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xb9,0x65,0x80,0x88,0xa2,0xc8,0x18, + 0x8d,0x2a,0x38,0xc1,0x87,0xa4,0x7f,0x35,0x83,0x69,0xdc,0x8c,0xcb,0x7b,0x11,0x67, + 0x89,0xc7,0xf0,0x8a,0x99,0x36,0x06,0xc5,0x90,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44, + 0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0x10,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x81,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32, + 0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45, + 0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44, + 0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8, + 0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00, + 0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07,0x7a,0x60,0x87,0x7c, + 0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72,0x68,0x03,0x72,0x48, + 0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90,0x07,0x7a,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc,0x21,0x1c,0xd8,0x61, + 0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81,0x1d,0xca,0xa1,0x0d, + 0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d,0xd8,0x61,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87,0x79,0x98,0x87,0x36, + 0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36,0x30,0x07,0x78,0x68, + 0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0xc2,0x1d,0xde, + 0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0,0xe1,0x1d,0xd2,0xc1, + 0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1,0x1d,0x00,0x7a,0x90, + 0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90,0x87,0x70,0x68,0x87, + 0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07,0x79,0x68,0x83,0x72, + 0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36,0x60,0x87,0x72,0x08, + 0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30,0x87,0x79,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1, + 0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01,0x1e,0xda,0x80,0x1e, + 0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07, + 0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87,0x36,0x30,0x07,0x78, + 0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20, + 0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c,0xc8,0xa1,0x0d,0xf4, + 0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda,0xa0,0x1d,0xc2,0x81, + 0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0xa0, + 0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68,0x83,0x76,0x08,0x07, + 0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6,0x81,0x1e,0xc2,0x61, + 0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d, + 0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e,0xda,0x60,0x1e,0xd2, + 0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87,0x70,0x30,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e, + 0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e,0xde,0xc1,0x1c,0xe8, + 0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87,0x70,0x60,0x87,0x79, + 0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81,0x00,0x16,0xa0,0xda, + 0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5,0x06,0xa3,0x08,0x80, + 0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00,0x00,0x03,0x00,0x00, + 0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00,0x00,0x89,0x20,0x00, + 0x00,0x1d,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4, + 0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4, + 0x4c,0x10,0x48,0x33,0x00,0xc3,0x08,0x04,0x60,0x83,0x70,0x94,0x34,0x45,0x94,0x30, + 0xf9,0xff,0x44,0x5c,0x13,0x15,0x11,0xbf,0x3d,0xfc,0xd3,0x18,0x01,0x30,0x88,0x30, + 0x04,0x17,0x49,0x53,0x44,0x09,0x93,0xff,0x4b,0x00,0xf3,0x2c,0x44,0xf4,0x4f,0x63, + 0x04,0xc0,0x20,0x42,0x21,0x94,0x42,0x84,0x40,0x0c,0x9d,0x61,0x04,0x01,0x98,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0x88,0x49,0x8a,0x29,0x40,0x6d, + 0x20,0x20,0x05,0xd6,0x08,0x00,0x00,0x00,0x00,0x13,0xb2,0x70,0x48,0x07,0x79,0xb0, + 0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x76,0x08,0x87, + 0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83,0x38,0x70,0x03,0x38, + 0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0, + 0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x71,0x60,0x07, + 0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e,0x73,0x20,0x07,0x7a, + 0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60, + 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0, + 0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07, + 0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73, + 0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40, + 0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07, + 0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72, + 0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0, + 0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20,0x07,0x7a,0x20,0x07, + 0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x72,0x50,0x07,0x76, + 0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x50, + 0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07, + 0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74, + 0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60, + 0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x41,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00, + 0x18,0xc2,0x38,0x40,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x64,0x81,0x00,0x00,0x00, + 0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26, + 0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70, + 0x2c,0xa1,0x09,0x00,0x00,0x79,0x18,0x00,0x00,0xb7,0x00,0x00,0x00,0x1a,0x03,0x4c, + 0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x42, + 0x3c,0x00,0x84,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62, + 0x2c,0xc2,0x23,0x2c,0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e, + 0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6, + 0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06, + 0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0xf0,0x10,0x43,0x8c,0x45, + 0x58,0x8c,0x65,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x79,0x8e,0x45,0x58,0x84, + 0x65,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56, + 0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78, + 0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61, + 0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x84, + 0x67,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98, + 0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1, + 0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x9e,0x86,0x51,0x58,0x9a,0x9c, + 0x8b,0x5c,0x99,0x1b,0x59,0x99,0xdc,0x17,0x5d,0x98,0xdc,0x59,0x19,0x1d,0xa3,0xb0, + 0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0x2f,0xb7,0xb0,0xb6,0x32, + 0x1a,0x66,0x6c,0x6f,0x61,0x74,0x34,0x64,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe, + 0xdc,0xc2,0xda,0xca,0xa8,0x98,0xc9,0x85,0x9d,0x7d,0x8d,0xbd,0xb1,0xbd,0xc9,0x0d, + 0x61,0x9e,0x67,0x19,0x1e,0xe8,0x89,0x1e,0xe9,0x99,0x86,0x08,0x0f,0x45,0x29,0x2c, + 0x4d,0xce,0xc5,0x4c,0x2e,0xec,0xac,0xad,0xcc,0x8d,0xee,0x2b,0xcd,0x0d,0xae,0x8e, + 0x8e,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x1b,0x4c,0x0a,0x95,0xb0,0x34, + 0x39,0x97,0xb1,0x32,0x37,0xba,0x32,0x39,0x3e,0x61,0x69,0x72,0x2e,0x70,0x65,0x72, + 0x73,0x70,0x65,0x63,0x74,0x69,0x76,0x65,0x14,0xea,0xec,0x86,0x48,0xcb,0xf0,0x58, + 0xcf,0xf5,0x60,0x4f,0xf6,0x40,0x4f,0xf4,0x48,0x8f,0xc6,0xa5,0x6e,0xae,0x4c,0x0e, + 0x85,0xed,0x6d,0xcc,0x2d,0x26,0x85,0xc5,0xd8,0x1b,0xdb,0x9b,0xdc,0x10,0x69,0x11, + 0x1e,0xeb,0xe1,0x1e,0xec,0xc9,0x1e,0xe8,0x89,0x1e,0xe9,0xe9,0xb8,0x84,0xa5,0xc9, + 0xb9,0xd0,0x95,0xe1,0xd1,0xd5,0xc9,0x95,0x51,0x0a,0x4b,0x93,0x73,0x61,0x7b,0x1b, + 0x0b,0xa3,0x4b,0x7b,0x73,0xfb,0x4a,0x73,0x23,0x2b,0xc3,0xa3,0x12,0x96,0x26,0xe7, + 0x32,0x17,0xd6,0x06,0xc7,0x56,0x46,0x8c,0xae,0x0c,0x8f,0xae,0x4e,0xae,0x4c,0x86, + 0x8c,0xc7,0x8c,0xed,0x2d,0x8c,0x8e,0x05,0x64,0x2e,0xac,0x0d,0x8e,0xad,0xcc,0x87, + 0x03,0x5d,0x19,0xde,0x10,0x6a,0x21,0x9e,0xef,0x01,0x83,0x65,0x58,0x84,0x27,0x0c, + 0x1e,0xe8,0x11,0x83,0x47,0x7a,0xc6,0x80,0x4b,0x58,0x9a,0x9c,0xcb,0x5c,0x58,0x1b, + 0x1c,0x5b,0x99,0x1c,0x8f,0xb9,0xb0,0x36,0x38,0xb6,0x32,0x39,0x0e,0x73,0x6d,0x70, + 0x43,0xa4,0xe5,0x78,0xca,0xe0,0x01,0x83,0x65,0x58,0x84,0x07,0x7a,0xcc,0xe0,0x91, + 0x9e,0x33,0x18,0x82,0x3c,0xdb,0xe3,0x3d,0x64,0xf0,0xa0,0xc1,0x10,0x03,0x01,0x9e, + 0xea,0x49,0x83,0x11,0x11,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0xb4,0xc3,0x3b,0x90, + 0x43,0x3d,0xb0,0x43,0x39,0xb8,0x81,0x39,0xb0,0x43,0x38,0x9c,0xc3,0x3c,0x4c,0x11, + 0x82,0x61,0x84,0xc2,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xe9,0x40,0x0e,0xe5,0xe0, + 0x0e,0xf4,0x30,0x25,0x28,0x46,0x2c,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xf6,0x50,0x0e, + 0xf2,0x30,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x30,0x46,0x50,0xe1,0x90,0x0e,0xf2, + 0xe0,0x06,0xec,0x10,0x0e,0xee,0x70,0x0e,0xf5,0x10,0x0e,0xe7,0x50,0x0e,0xbf,0x60, + 0x0f,0xe5,0x20,0x0f,0xf3,0x90,0x0e,0xef,0xe0,0x0e,0x53,0x02,0x64,0xc4,0x14,0x0e, + 0xe9,0x20,0x0f,0x6e,0x30,0x0e,0xef,0xd0,0x0e,0xf0,0x90,0x0e,0xec,0x50,0x0e,0xbf, + 0xf0,0x0e,0xf0,0x40,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x0f,0x53,0x06,0x85,0x71,0x46, + 0x30,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xe6,0x20,0x0f,0xe1,0x70,0x0e,0xed,0x50,0x0e, + 0xee,0x40,0x0f,0x53,0x02,0x35,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00, + 0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84, + 0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed, + 0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66, + 0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c, + 0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70, + 0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90, + 0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4, + 0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83, + 0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14, + 0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70, + 0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80, + 0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0, + 0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1, + 0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d, + 0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39, + 0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc, + 0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70, + 0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53, + 0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e, + 0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c, + 0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38, + 0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37, + 0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33, + 0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4, + 0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0, + 0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3, + 0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07, + 0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23, + 0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59, + 0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b, + 0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00, + 0x00,0x08,0x00,0x00,0x00,0x16,0xb0,0x01,0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f, + 0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e,0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20, + 0x0d,0x0d,0x00,0x00,0x00,0x61,0x20,0x00,0x00,0x0f,0x00,0x00,0x00,0x13,0x04,0x41, + 0x2c,0x10,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0xc4,0x46,0x00,0xc6,0x12,0x80,0x80, + 0xd4,0x08,0x40,0x0d,0x90,0x98,0x01,0xa0,0x30,0x03,0x40,0x60,0x04,0x00,0x00,0x00, + 0x00,0x83,0x0c,0x8b,0x60,0x8c,0x18,0x28,0x42,0x40,0x29,0x49,0x50,0x20,0x86,0x60, + 0x01,0x23,0x9f,0xd9,0x06,0x23,0x00,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sgl_vs_bytecode_metal_ios[3317] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf5,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe0,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x17,0x11,0x57,0x16,0x94,0x42,0x52, + 0xfb,0x1e,0xd0,0x32,0xfd,0x87,0x16,0xb0,0xa4,0xd0,0xc2,0x43,0xbe,0x93,0x8c,0xe0, + 0x2d,0x7a,0x5c,0x3e,0x06,0x4c,0x57,0xeb,0x4b,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x40,0x00,0x00,0x00,0x56,0x41,0x54, + 0x54,0x2a,0x00,0x04,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x80, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x00,0x02,0x80,0x70,0x73,0x69,0x7a,0x65,0x00,0x03,0x80,0x56,0x41,0x54, + 0x59,0x06,0x00,0x04,0x00,0x06,0x04,0x06,0x03,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, + 0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00, + 0x00,0xc0,0x0b,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00, + 0x00,0xed,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00, + 0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84, + 0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b, + 0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90, + 0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8, + 0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00,0x00,0x82,0x00,0x00, + 0x00,0x1b,0xc8,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x90,0x80,0x8a,0x18,0x87,0x77, + 0x90,0x07,0x79,0x28,0x87,0x71,0xa0,0x07,0x76,0xc8,0x87,0x36,0x90,0x87,0x77,0xa8, + 0x07,0x77,0x20,0x87,0x72,0x20,0x87,0x36,0x20,0x87,0x74,0xb0,0x87,0x74,0x20,0x87, + 0x72,0x68,0x83,0x79,0x88,0x07,0x79,0xa0,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76, + 0x08,0x07,0x7a,0x40,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c, + 0xd2,0x61,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8, + 0xa1,0x0d,0xc6,0x21,0x1c,0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79, + 0x28,0x07,0x80,0x60,0x87,0x72,0x98,0x87,0x79,0x68,0x03,0x78,0x90,0x87,0x72,0x18, + 0x87,0x74,0x98,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x76,0x08,0x07,0x72,0x00,0xcc, + 0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21, + 0x1c,0xda,0xa1,0x1c,0xda,0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e, + 0xda,0xa0,0x1c,0xd8,0x21,0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x06, + 0x77,0x78,0x87,0x36,0x30,0x07,0x79,0x08,0x87,0x76,0x28,0x87,0x36,0x80,0x87,0x77, + 0x48,0x07,0x77,0xa0,0x87,0x72,0x90,0x87,0x36,0x28,0x07,0x76,0x48,0x87,0x76,0x68, + 0x03,0x77,0x78,0x07,0x77,0x68,0x03,0x76,0x28,0x87,0x70,0x30,0x07,0x80,0x70,0x87, + 0x77,0x68,0x83,0x74,0x70,0x07,0x73,0x98,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76, + 0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d, + 0xda,0x40,0x1d,0xea,0xa1,0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca, + 0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36, + 0x70,0x87,0x70,0x70,0x87,0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0, + 0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6, + 0x21,0x1d,0xce,0xc1,0x1d,0xca,0x81,0x1c,0xda,0x40,0x1f,0xca,0x41,0x1e,0xde,0x61, + 0x1e,0xda,0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90, + 0x87,0x7a,0x28,0x07,0x80,0x70,0x87,0x77,0x68,0x03,0x7a,0x90,0x87,0x70,0x80,0x07, + 0x78,0x48,0x07,0x77,0x38,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41, + 0x1e,0xea,0xa1,0x1c,0x00,0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e, + 0xe4,0xe1,0x1d,0xe8,0xa1,0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0x40,0x1c,0xea, + 0xc1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c, + 0x00,0x88,0x7a,0x70,0x87,0x79,0x08,0x07,0x73,0x28,0x87,0x36,0x30,0x07,0x78,0x68, + 0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea, + 0x61,0x1e,0xca,0xa1,0x0d,0xe6,0xe1,0x1d,0xcc,0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1, + 0x1d,0xc2,0x81,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x36,0x20,0x42, + 0x01,0x24,0xc0,0x02,0x54,0x00,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00, + 0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00,0x20,0x00,0x00,0x00,0x32,0x22,0x48, + 0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90, + 0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x44,0x33,0x00,0xc3,0x08,0x04, + 0x60,0x89,0x10,0x02,0x18,0x46,0x10,0x80,0x24,0x08,0x33,0x51,0xf3,0x40,0x0f,0xf2, + 0x50,0x0f,0xe3,0x40,0x0f,0x6e,0xd0,0x0e,0xe5,0x40,0x0f,0xe1,0xc0,0x0e,0x7a,0xa0, + 0x07,0xed,0x10,0x0e,0xf4,0x20,0x0f,0xe9,0x80,0x0f,0x28,0x20,0x07,0x49,0x53,0x44, + 0x09,0x93,0x5f,0x49,0xff,0x03,0x44,0x00,0x23,0x21,0xa1,0x94,0x41,0x04,0x43,0x28, + 0x86,0x08,0x23,0x80,0x43,0x68,0x20,0x60,0x8e,0x00,0x0c,0x52,0x60,0xcd,0x11,0x80, + 0xc2,0x20,0x42,0x20,0x0c,0x23,0x10,0xcb,0x08,0x00,0x00,0x00,0x00,0x13,0xa8,0x70, + 0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68, + 0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83,0x0d,0xb7,0x51, + 0x0e,0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78, + 0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76,0xa0,0x07,0x71,0x60, + 0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07, + 0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a, + 0x60,0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30, + 0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76, + 0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0, + 0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06, + 0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6, + 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x90, + 0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06, + 0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72, + 0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0, + 0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07, + 0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75,0x10,0x07,0x72, + 0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0,0x06,0xf6,0x10, + 0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07, + 0x70,0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73, + 0x20,0x07,0x43,0x98,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x2c,0x10,0x00, + 0x00,0x0b,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26, + 0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50,0x04,0x05,0x18,0x50,0x08,0x65,0x50, + 0x80,0x02,0x05,0x51,0x20,0xd4,0x46,0x00,0x88,0x8d,0x25,0x40,0x02,0x00,0x00,0x00, + 0x00,0x79,0x18,0x00,0x00,0x02,0x01,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2, + 0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x32,0x28,0x00,0xb3,0x50, + 0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0x81,0x22,0x2c, + 0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae, + 0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06, + 0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5, + 0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25,0x58,0x90,0x45,0x60, + 0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x82,0x45,0xe0,0x16,0x96, + 0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7, + 0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50,0x12,0x72,0x61,0x69, + 0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d, + 0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04,0x65,0x21,0x19,0x84, + 0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95, + 0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85, + 0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x51,0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d, + 0x19,0xde,0xd7,0x5b,0x1d,0x1d,0x5c,0x1d,0x1d,0x97,0xba,0xb9,0x32,0x39,0x14,0xb6, + 0xb7,0x31,0x37,0x98,0x14,0x46,0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x74,0x79, + 0x70,0x65,0x5f,0x6e,0x61,0x6d,0x65,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x68,0xc8,0x84, + 0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95,0x51,0xa8,0xb3,0x1b,0xc2, + 0x28,0x8f,0x02,0x29,0x91,0x22,0x29,0x93,0x42,0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61, + 0x7b,0x1b,0x73,0x8b,0x49,0x61,0x31,0xf6,0xc6,0xf6,0x26,0x37,0x84,0x51,0x1e,0xc5, + 0x52,0x22,0x45,0x52,0x26,0xe5,0x22,0x13,0x96,0x26,0xe7,0x02,0xf7,0x36,0x97,0x46, + 0x97,0xf6,0xe6,0xc6,0xe5,0x8c,0xed,0x0b,0xea,0x6d,0x2e,0x8d,0x2e,0xed,0xcd,0x6d, + 0x88,0xa2,0x64,0x4a,0xa4,0x48,0xca,0xa4,0x68,0x74,0xc2,0xd2,0xe4,0x5c,0xe0,0xde, + 0xd2,0xdc,0xe8,0xbe,0xe6,0xd2,0xf4,0xca,0x58,0x98,0xb1,0xbd,0x85,0xd1,0x91,0x39, + 0x63,0xfb,0x82,0x7a,0x4b,0x73,0xa3,0x9b,0x4a,0xd3,0x2b,0x1b,0xa2,0x28,0x9c,0x12, + 0x29,0x9d,0x32,0x29,0xde,0x10,0x44,0xa9,0x14,0x4c,0xd9,0x94,0x8f,0x50,0x58,0x9a, + 0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0x57,0x9a,0x1b,0x5c,0x1d,0x1d,0xa5,0xb0, + 0x34,0x39,0x17,0xb6,0xb7,0xb1,0x30,0xba,0xb4,0x37,0xb7,0xaf,0x34,0x37,0xb2,0x32, + 0x3c,0x7a,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64,0x28,0x5f,0x5f,0x61,0x69, + 0x72,0x5f,0x70,0x6c,0x61,0x63,0x65,0x68,0x6f,0x6c,0x64,0x65,0x72,0x5f,0x5f,0x29, + 0x44,0xe0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x86,0x50,0x8b,0xa0,0x84,0x81,0x22, + 0x06,0x8b,0xb0,0x04,0xca,0x18,0x28,0x91,0x22,0x29,0x93,0x42,0x06,0x34,0xcc,0xd8, + 0xde,0xc2,0xe8,0x64,0x98,0xd0,0x95,0xe1,0x8d,0xbd,0xbd,0xc9,0x91,0xc1,0x0c,0xa1, + 0x96,0x40,0x09,0x03,0x45,0x0c,0x96,0x60,0x09,0x94,0x31,0x50,0x22,0xc5,0x0c,0x94, + 0x49,0x39,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8,0x65,0x50,0xc2,0x40, + 0x11,0x83,0x65,0x58,0x02,0x65,0x0c,0x94,0x48,0x91,0x94,0x49,0x49,0x03,0x16,0x70, + 0x73,0x69,0x7a,0x65,0x43,0xa8,0xc5,0x50,0xc2,0x40,0x11,0x83,0xc5,0x58,0x02,0x65, + 0x0c,0x94,0x48,0xe9,0x94,0x49,0x59,0x03,0x2a,0x61,0x69,0x72,0x2e,0x62,0x75,0x66, + 0x66,0x65,0x72,0x7c,0xc2,0xd2,0xe4,0x5c,0xc4,0xea,0xcc,0xcc,0xca,0xe4,0xbe,0xe6, + 0xd2,0xf4,0xca,0x88,0x84,0xa5,0xc9,0xb9,0xc8,0x95,0x85,0x91,0x91,0x0a,0x4b,0x93, + 0x73,0x99,0xa3,0x93,0xab,0x1b,0xa3,0xfb,0xa2,0xcb,0x83,0x2b,0xfb,0x4a,0x73,0x33, + 0x7b,0x23,0x62,0xc6,0xf6,0x16,0x46,0x47,0x83,0x47,0xc3,0xa1,0xcd,0x0e,0x8e,0x02, + 0x5d,0xdb,0x10,0x6a,0x11,0x16,0x62,0x11,0x94,0x38,0x50,0xe4,0x60,0x21,0x16,0x62, + 0x11,0x94,0x38,0x50,0xe6,0x80,0x51,0x58,0x9a,0x9c,0x4b,0x98,0xdc,0xd9,0x17,0x5d, + 0x1e,0x5c,0xd9,0xd7,0x5c,0x9a,0x5e,0x19,0xaf,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3, + 0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x30,0xb6,0xb4,0x33,0xb7,0xaf,0xb9,0x34,0xbd,0x32, + 0x26,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x1c,0xbe,0x62,0x72,0x86,0x90, + 0xc1,0x52,0x28,0x6d,0xa0,0xb8,0xc1,0x72,0x28,0x62,0xb0,0x08,0x4b,0xa0,0xbc,0x81, + 0x02,0x07,0x0a,0x1d,0x28,0x75,0xb0,0x1c,0x8a,0x1d,0x2c,0x89,0x12,0x29,0x77,0xa0, + 0x4c,0x0a,0x1e,0x0c,0x51,0x94,0x32,0x50,0xd0,0x40,0x51,0x03,0x85,0x0d,0x94,0x3c, + 0x18,0x62,0x24,0x80,0x02,0x06,0x8a,0x1e,0xf0,0x79,0x6b,0x73,0x4b,0x83,0x7b,0xa3, + 0x2b,0x73,0xa3,0x03,0x19,0x43,0x0b,0x93,0xe3,0x33,0x95,0xd6,0x06,0xc7,0x56,0x06, + 0x32,0xb4,0xb2,0x02,0x42,0x25,0x14,0x14,0x34,0x44,0x50,0xfa,0x60,0x88,0xa1,0xf0, + 0x81,0xe2,0x07,0x8d,0x32,0xc4,0x50,0xfe,0x40,0xf9,0x83,0x46,0x19,0x11,0xb1,0x03, + 0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3b,0xbc,0x03,0x39,0xd4,0x03,0x3b,0x94,0x83,0x1b, + 0x98,0x03,0x3b,0x84,0xc3,0x39,0xcc,0xc3,0x14,0x21,0x18,0x46,0x28,0xec,0xc0,0x0e, + 0xf6,0xd0,0x0e,0x6e,0x90,0x0e,0xe4,0x50,0x0e,0xee,0x40,0x0f,0x53,0x82,0x62,0xc4, + 0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f,0xf3,0x90,0x0e,0xef,0xe0, + 0x0e,0x53,0x02,0x63,0x04,0x15,0x0e,0xe9,0x20,0x0f,0x6e,0xc0,0x0e,0xe1,0xe0,0x0e, + 0xe7,0x50,0x0f,0xe1,0x70,0x0e,0xe5,0xf0,0x0b,0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9, + 0xf0,0x0e,0xee,0x30,0x25,0x40,0x46,0x4c,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xe3,0xf0, + 0x0e,0xed,0x00,0x0f,0xe9,0xc0,0x0e,0xe5,0xf0,0x0b,0xef,0x00,0x0f,0xf4,0x90,0x0e, + 0xef,0xe0,0x0e,0xf3,0x30,0x65,0x50,0x18,0x67,0x84,0x12,0x0e,0xe9,0x20,0x0f,0x6e, + 0x60,0x0f,0xe5,0x20,0x0f,0xf4,0x50,0x0e,0xf8,0x30,0x25,0xd8,0x03,0x00,0x00,0x00, + 0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c, + 0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07, + 0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42, + 0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83, + 0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07, + 0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70, + 0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3, + 0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2, + 0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc, + 0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68, + 0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07, + 0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72, + 0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec, + 0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc, + 0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8, + 0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03, + 0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c, + 0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74, + 0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4, + 0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc, + 0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1, + 0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50, + 0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51, + 0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21, + 0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06, + 0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c, + 0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77, + 0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec, + 0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8, + 0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4, + 0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43, + 0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30, + 0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x3e,0x00,0x00,0x00,0x13,0x04,0x41, + 0x2c,0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0xf4,0xc6,0x22,0x86,0x61,0x18,0xc6, + 0x22,0x04,0x41,0x10,0xc6,0x22,0x82,0x20,0x08,0xa8,0x95,0x40,0x19,0x14,0x01,0xbd, + 0x11,0x00,0x1a,0x33,0x00,0x24,0x66,0x00,0x28,0xcc,0x00,0x00,0x00,0xe3,0x15,0x4b, + 0x94,0x65,0x11,0x05,0x65,0x90,0x21,0x1a,0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x55, + 0xd7,0x2d,0x14,0x94,0x41,0x86,0xea,0x70,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1, + 0x0a,0x4a,0x13,0x03,0x31,0x70,0x28,0x28,0x83,0x0c,0x1a,0x43,0x99,0x10,0xc8,0xc7, + 0x8a,0x00,0x3e,0xe3,0x15,0xd9,0x77,0x06,0x67,0x40,0x51,0x50,0x06,0x19,0xbe,0x48, + 0x33,0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6,0x2b,0x3c,0x32,0x68,0x03,0x36,0x20,0x03, + 0x0a,0xca,0x20,0xc3,0x18,0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b,0xc4,0x00,0x0d,0xe2, + 0x00,0x0e,0x3c,0x0a,0xca,0x20,0xc3,0x19,0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a, + 0xf8,0x8c,0x57,0x9c,0x41,0x1b,0xd8,0x41,0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e, + 0xb3,0x0d,0x61,0x10,0x00,0xb3,0x0d,0x41,0x1b,0x04,0xb3,0x0d,0xc1,0x23,0xcc,0x36, + 0x04,0x6e,0x30,0x64,0x10,0x10,0x03,0x00,0x00,0x09,0x00,0x00,0x00,0x5b,0x86,0x20, + 0x00,0x85,0x2d,0x43,0x11,0x80,0xc2,0x96,0x41,0x09,0x40,0x61,0xcb,0xf0,0x04,0xa0, + 0xb0,0x65,0xa0,0x02,0x50,0xd8,0x32,0x60,0x01,0x28,0x6c,0x19,0xba,0x00,0x14,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sgl_fs_bytecode_metal_ios[2809] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf9,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xc1,0x98,0x28,0x2b,0x2b,0x1f,0x36, + 0x7c,0x5c,0xb0,0x69,0x3f,0xc3,0xd1,0x80,0x4b,0xcf,0xa1,0x10,0xfc,0x19,0x31,0x58, + 0xad,0x45,0xd3,0x5a,0x81,0x72,0x0d,0x8f,0x85,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44, + 0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0x08,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x7f,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32, + 0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45, + 0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44, + 0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8, + 0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00, + 0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07,0x7a,0x60,0x87,0x7c, + 0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72,0x68,0x03,0x72,0x48, + 0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90,0x07,0x7a,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc,0x21,0x1c,0xd8,0x61, + 0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81,0x1d,0xca,0xa1,0x0d, + 0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d,0xd8,0x61,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87,0x79,0x98,0x87,0x36, + 0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36,0x30,0x07,0x78,0x68, + 0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0xc2,0x1d,0xde, + 0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0,0xe1,0x1d,0xd2,0xc1, + 0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1,0x1d,0x00,0x7a,0x90, + 0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90,0x87,0x70,0x68,0x87, + 0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07,0x79,0x68,0x83,0x72, + 0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36,0x60,0x87,0x72,0x08, + 0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30,0x87,0x79,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1, + 0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01,0x1e,0xda,0x80,0x1e, + 0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07, + 0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87,0x36,0x30,0x07,0x78, + 0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20, + 0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c,0xc8,0xa1,0x0d,0xf4, + 0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda,0xa0,0x1d,0xc2,0x81, + 0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0xa0, + 0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68,0x83,0x76,0x08,0x07, + 0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6,0x81,0x1e,0xc2,0x61, + 0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d, + 0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e,0xda,0x60,0x1e,0xd2, + 0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87,0x70,0x30,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e, + 0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e,0xde,0xc1,0x1c,0xe8, + 0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87,0x70,0x60,0x87,0x79, + 0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81,0x00,0x16,0xa0,0xda, + 0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5,0x06,0xa3,0x08,0x80, + 0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00,0x00,0x03,0x00,0x00, + 0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00,0x00,0x89,0x20,0x00, + 0x00,0x1d,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4, + 0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4, + 0x4c,0x10,0x48,0x33,0x00,0xc3,0x08,0x04,0x60,0x83,0x70,0x94,0x34,0x45,0x94,0x30, + 0xf9,0xff,0x44,0x5c,0x13,0x15,0x11,0xbf,0x3d,0xfc,0xd3,0x18,0x01,0x30,0x88,0x30, + 0x04,0x17,0x49,0x53,0x44,0x09,0x93,0xff,0x4b,0x00,0xf3,0x2c,0x44,0xf4,0x4f,0x63, + 0x04,0xc0,0x20,0x42,0x21,0x94,0x42,0x84,0x40,0x0c,0x9d,0x61,0x04,0x01,0x98,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0x88,0x49,0x8a,0x29,0x40,0x6d, + 0x20,0x20,0x05,0xd6,0x08,0x00,0x00,0x00,0x00,0x13,0xa8,0x70,0x48,0x07,0x79,0xb0, + 0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x74,0x78,0x87, + 0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83,0x0d,0xb7,0x51,0x0e,0x6d,0x00,0x0f, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe9, + 0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x78,0xa0, + 0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07, + 0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72, + 0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0, + 0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06, + 0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6, + 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xf6,0x20, + 0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x40,0x07,0x78, + 0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x90,0x07,0x76,0xa0,0x07, + 0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x72, + 0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60, + 0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07, + 0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a, + 0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10, + 0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0,0x06,0xf6,0x10,0x07,0x70,0x20,0x07, + 0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74, + 0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x43,0x18, + 0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x8c,0x03,0x04,0x80,0x00,0x00, + 0x00,0x00,0x00,0x40,0x16,0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98, + 0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02, + 0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0x01,0x12,0x00,0x00,0x79,0x18,0x00, + 0x00,0xb7,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32, + 0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x42,0x3c,0x00,0x84,0x50,0xb9,0x1b,0x43,0x0b, + 0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0xc2,0x23,0x2c,0x05,0xe7,0x20,0x08, + 0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed, + 0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c, + 0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45, + 0x26,0x65,0x88,0xf0,0x10,0x43,0x8c,0x45,0x58,0x8c,0x65,0x60,0xd1,0x54,0x46,0x17, + 0xc6,0x36,0x04,0x79,0x8e,0x45,0x58,0x84,0x65,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6, + 0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96, + 0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f, + 0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f, + 0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x84,0x67,0x21,0x19,0x84,0xa5,0xc9,0xb9,0x8c, + 0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99, + 0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85,0x89,0xb1,0x95,0x0d, + 0x11,0x9e,0x86,0x51,0x58,0x9a,0x9c,0x8b,0x5c,0x99,0x1b,0x59,0x99,0xdc,0x17,0x5d, + 0x98,0xdc,0x59,0x19,0x1d,0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c, + 0xb8,0xb2,0x2f,0xb7,0xb0,0xb6,0x32,0x1a,0x66,0x6c,0x6f,0x61,0x74,0x34,0x64,0xc2, + 0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xdc,0xc2,0xda,0xca,0xa8,0x98,0xc9,0x85,0x9d, + 0x7d,0x8d,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x9e,0x67,0x19,0x1e,0xe8,0x89,0x1e,0xe9, + 0x99,0x86,0x08,0x0f,0x45,0x29,0x2c,0x4d,0xce,0xc5,0x4c,0x2e,0xec,0xac,0xad,0xcc, + 0x8d,0xee,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb, + 0x98,0x1b,0x4c,0x0a,0x95,0xb0,0x34,0x39,0x97,0xb1,0x32,0x37,0xba,0x32,0x39,0x3e, + 0x61,0x69,0x72,0x2e,0x70,0x65,0x72,0x73,0x70,0x65,0x63,0x74,0x69,0x76,0x65,0x14, + 0xea,0xec,0x86,0x48,0xcb,0xf0,0x58,0xcf,0xf5,0x60,0x4f,0xf6,0x40,0x4f,0xf4,0x48, + 0x8f,0xc6,0xa5,0x6e,0xae,0x4c,0x0e,0x85,0xed,0x6d,0xcc,0x2d,0x26,0x85,0xc5,0xd8, + 0x1b,0xdb,0x9b,0xdc,0x10,0x69,0x11,0x1e,0xeb,0xe1,0x1e,0xec,0xc9,0x1e,0xe8,0x89, + 0x1e,0xe9,0xe9,0xb8,0x84,0xa5,0xc9,0xb9,0xd0,0x95,0xe1,0xd1,0xd5,0xc9,0x95,0x51, + 0x0a,0x4b,0x93,0x73,0x61,0x7b,0x1b,0x0b,0xa3,0x4b,0x7b,0x73,0xfb,0x4a,0x73,0x23, + 0x2b,0xc3,0xa3,0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7,0x56,0x46,0x8c,0xae, + 0x0c,0x8f,0xae,0x4e,0xae,0x4c,0x86,0x8c,0xc7,0x8c,0xed,0x2d,0x8c,0x8e,0x05,0x64, + 0x2e,0xac,0x0d,0x8e,0xad,0xcc,0x87,0x03,0x5d,0x19,0xde,0x10,0x6a,0x21,0x9e,0xef, + 0x01,0x83,0x65,0x58,0x84,0x27,0x0c,0x1e,0xe8,0x11,0x83,0x47,0x7a,0xc6,0x80,0x4b, + 0x58,0x9a,0x9c,0xcb,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x8f,0xb9,0xb0,0x36,0x38, + 0xb6,0x32,0x39,0x0e,0x73,0x6d,0x70,0x43,0xa4,0xe5,0x78,0xca,0xe0,0x01,0x83,0x65, + 0x58,0x84,0x07,0x7a,0xcc,0xe0,0x91,0x9e,0x33,0x18,0x82,0x3c,0xdb,0xe3,0x3d,0x64, + 0xf0,0xa0,0xc1,0x10,0x03,0x01,0x9e,0xea,0x49,0x83,0x11,0x11,0x3b,0xb0,0x83,0x3d, + 0xb4,0x83,0x1b,0xb4,0xc3,0x3b,0x90,0x43,0x3d,0xb0,0x43,0x39,0xb8,0x81,0x39,0xb0, + 0x43,0x38,0x9c,0xc3,0x3c,0x4c,0x11,0x82,0x61,0x84,0xc2,0x0e,0xec,0x60,0x0f,0xed, + 0xe0,0x06,0xe9,0x40,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25,0x28,0x46,0x2c,0xe1,0x90, + 0x0e,0xf2,0xe0,0x06,0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25, + 0x30,0x46,0x50,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xec,0x10,0x0e,0xee,0x70,0x0e,0xf5, + 0x10,0x0e,0xe7,0x50,0x0e,0xbf,0x60,0x0f,0xe5,0x20,0x0f,0xf3,0x90,0x0e,0xef,0xe0, + 0x0e,0x53,0x02,0x64,0xc4,0x14,0x0e,0xe9,0x20,0x0f,0x6e,0x30,0x0e,0xef,0xd0,0x0e, + 0xf0,0x90,0x0e,0xec,0x50,0x0e,0xbf,0xf0,0x0e,0xf0,0x40,0x0f,0xe9,0xf0,0x0e,0xee, + 0x30,0x0f,0x53,0x06,0x85,0x71,0x46,0x30,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xe6,0x20, + 0x0f,0xe1,0x70,0x0e,0xed,0x50,0x0e,0xee,0x40,0x0f,0x53,0x02,0x35,0x00,0x00,0x00, + 0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c, + 0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07, + 0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42, + 0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83, + 0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07, + 0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70, + 0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3, + 0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2, + 0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc, + 0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68, + 0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07, + 0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72, + 0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec, + 0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc, + 0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8, + 0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03, + 0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c, + 0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74, + 0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4, + 0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc, + 0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1, + 0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50, + 0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51, + 0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21, + 0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06, + 0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c, + 0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77, + 0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec, + 0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8, + 0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4, + 0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43, + 0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x08,0x00,0x00,0x00,0x16,0xb0,0x01, + 0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e, + 0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20,0x0d,0x0d,0x00,0x00,0x00,0x61,0x20,0x00, + 0x00,0x0f,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x06,0x00,0x00, + 0x00,0xc4,0x46,0x00,0xc6,0x12,0x80,0x80,0xd4,0x08,0x40,0x0d,0x90,0x98,0x01,0xa0, + 0x30,0x03,0x40,0x60,0x04,0x00,0x00,0x00,0x00,0x83,0x0c,0x8b,0x60,0x8c,0x18,0x28, + 0x42,0x40,0x29,0x49,0x50,0x20,0x86,0x60,0x01,0x23,0x9f,0xd9,0x06,0x23,0x00,0x32, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sgl_vs_source_metal_sim[756] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20,0x6d,0x76,0x70,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20,0x74,0x6d,0x3b,0x0a,0x7d,0x3b, + 0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f, + 0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, + 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, + 0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x70, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74,0x53,0x69,0x7a, + 0x65,0x20,0x5b,0x5b,0x70,0x6f,0x69,0x6e,0x74,0x5f,0x73,0x69,0x7a,0x65,0x5d,0x5d, + 0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69, + 0x6e,0x30,0x5f,0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x61,0x74, + 0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72, + 0x64,0x30,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x31, + 0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75, + 0x74,0x65,0x28,0x32,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x20,0x70,0x73,0x69,0x7a,0x65,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69, + 0x62,0x75,0x74,0x65,0x28,0x33,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x76, + 0x65,0x72,0x74,0x65,0x78,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20, + 0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69, + 0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20, + 0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61, + 0x6d,0x73,0x26,0x20,0x5f,0x31,0x39,0x20,0x5b,0x5b,0x62,0x75,0x66,0x66,0x65,0x72, + 0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x6d,0x61,0x69, + 0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x5f,0x31,0x39,0x2e,0x6d,0x76,0x70,0x20,0x2a, + 0x20,0x69,0x6e,0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74,0x53,0x69, + 0x7a,0x65,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x70,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75,0x76,0x20,0x3d,0x20,0x5f,0x31,0x39,0x2e, + 0x74,0x6d,0x20,0x2a,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x2e,0x74, + 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31, + 0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x63,0x6f,0x6c, + 0x6f,0x72,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a, + 0x7d,0x0a,0x0a,0x00, +}; +static const uint8_t _sgl_fs_source_metal_sim[439] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d, + 0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f, + 0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, + 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x66,0x72,0x61,0x67,0x6d,0x65, + 0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69, + 0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b, + 0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x74,0x65, + 0x78,0x20,0x5b,0x5b,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x30,0x29,0x5d,0x5d, + 0x2c,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x73,0x6d,0x70,0x20,0x5b,0x5b, + 0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a, + 0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75, + 0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e, + 0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78, + 0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x73,0x6d,0x70,0x2c,0x20,0x69,0x6e,0x2e, + 0x75,0x76,0x2e,0x78,0x79,0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x63,0x6f,0x6c,0x6f, + 0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75, + 0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_D3D11) +static const uint8_t _sgl_vs_bytecode_hlsl4[1032] = { + 0x44,0x58,0x42,0x43,0x74,0x7f,0x01,0xd9,0xf4,0xd5,0xed,0x1d,0x74,0xc1,0x30,0x27, + 0xd8,0xe9,0x9d,0x50,0x01,0x00,0x00,0x00,0x08,0x04,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x90,0x01,0x00,0x00,0x00,0x02,0x00,0x00, + 0x8c,0x03,0x00,0x00,0x52,0x44,0x45,0x46,0xd8,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x48,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xfe,0xff, + 0x10,0x81,0x00,0x00,0xaf,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0xab,0xab,0x3c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xa8,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0x31,0x39,0x5f, + 0x6d,0x76,0x70,0x00,0x02,0x00,0x03,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x31,0x39,0x5f,0x74,0x6d,0x00,0x4d,0x69,0x63,0x72,0x6f, + 0x73,0x6f,0x66,0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c,0x53,0x4c,0x20,0x53,0x68, + 0x61,0x64,0x65,0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x72,0x20,0x31,0x30, + 0x2e,0x31,0x00,0xab,0x49,0x53,0x47,0x4e,0x74,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x68,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x03,0x03,0x00,0x00,0x68,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x68,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab, + 0x4f,0x53,0x47,0x4e,0x68,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0xab,0xab,0xab, + 0x53,0x48,0x44,0x52,0x84,0x01,0x00,0x00,0x40,0x00,0x01,0x00,0x61,0x00,0x00,0x00, + 0x59,0x00,0x00,0x04,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x5f,0x00,0x00,0x03, + 0x32,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00, + 0x02,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00,0x67,0x00,0x00,0x04, + 0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x68,0x00,0x00,0x02, + 0x01,0x00,0x00,0x00,0x38,0x00,0x00,0x08,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x56,0x15,0x10,0x00,0x01,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x06,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x36,0x00,0x00,0x05, + 0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x02,0x00,0x00,0x00, + 0x38,0x00,0x00,0x08,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x56,0x15,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00, + 0x00,0x00,0x00,0x00,0xa6,0x1a,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x32,0x00,0x00,0x0a,0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0xf6,0x1f,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54, + 0x74,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sgl_fs_bytecode_hlsl4[608] = { + 0x44,0x58,0x42,0x43,0xc8,0x9b,0x66,0x64,0x80,0x2f,0xbe,0x14,0xd9,0x88,0xa0,0x97, + 0x64,0x14,0x66,0xff,0x01,0x00,0x00,0x00,0x60,0x02,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x48,0x01,0x00,0x00, + 0xe4,0x01,0x00,0x00,0x52,0x44,0x45,0x46,0x8c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xff,0xff, + 0x10,0x81,0x00,0x00,0x64,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x73,0x6d,0x70,0x00,0x74,0x65,0x78,0x00, + 0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f,0x66,0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c, + 0x53,0x4c,0x20,0x53,0x68,0x61,0x64,0x65,0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c, + 0x65,0x72,0x20,0x31,0x30,0x2e,0x31,0x00,0x49,0x53,0x47,0x4e,0x44,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x03,0x00,0x00, + 0x38,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x2c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x53,0x56,0x5f,0x54, + 0x61,0x72,0x67,0x65,0x74,0x00,0xab,0xab,0x53,0x48,0x44,0x52,0x94,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x5a,0x00,0x00,0x03,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x58,0x18,0x00,0x04,0x00,0x70,0x10,0x00,0x00,0x00,0x00,0x00, + 0x55,0x55,0x00,0x00,0x62,0x10,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x62,0x10,0x00,0x03,0xf2,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x65,0x00,0x00,0x03, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00, + 0x45,0x00,0x00,0x09,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x7e,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x07,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x01,0x00,0x00,0x00, + 0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +}; +#elif defined(SOKOL_WGPU) +static const uint8_t _sgl_vs_source_wgsl[1162] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a, + 0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20, + 0x20,0x6d,0x76,0x70,0x20,0x3a,0x20,0x6d,0x61,0x74,0x34,0x78,0x34,0x66,0x2c,0x0a, + 0x20,0x20,0x2f,0x2a,0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x36,0x34,0x29, + 0x20,0x2a,0x2f,0x0a,0x20,0x20,0x74,0x6d,0x20,0x3a,0x20,0x6d,0x61,0x74,0x34,0x78, + 0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x30,0x29, + 0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x30,0x29,0x20,0x76,0x61,0x72, + 0x3c,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x3e,0x20,0x78,0x5f,0x31,0x39,0x20,0x3a, + 0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x3b,0x0a,0x0a,0x76,0x61,0x72, + 0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x70,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x76, + 0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72, + 0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61, + 0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65, + 0x3e,0x20,0x70,0x73,0x69,0x7a,0x65,0x20,0x3a,0x20,0x66,0x33,0x32,0x3b,0x0a,0x0a, + 0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x67,0x6c,0x5f, + 0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b, + 0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x32,0x20,0x3a,0x20,0x6d,0x61, + 0x74,0x34,0x78,0x34,0x66,0x20,0x3d,0x20,0x78,0x5f,0x31,0x39,0x2e,0x6d,0x76,0x70, + 0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x35,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f, + 0x31,0x3b,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x28,0x78,0x5f,0x32,0x32,0x20,0x2a,0x20,0x78,0x5f,0x32,0x35,0x29, + 0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x32,0x20,0x3a,0x20,0x6d, + 0x61,0x74,0x34,0x78,0x34,0x66,0x20,0x3d,0x20,0x78,0x5f,0x31,0x39,0x2e,0x74,0x6d, + 0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x36,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30, + 0x3b,0x0a,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x28,0x78,0x5f,0x33,0x32,0x20,0x2a, + 0x20,0x76,0x65,0x63,0x34,0x66,0x28,0x78,0x5f,0x33,0x36,0x2e,0x78,0x2c,0x20,0x78, + 0x5f,0x33,0x36,0x2e,0x79,0x2c,0x20,0x30,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30, + 0x66,0x29,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x35,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30, + 0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x78,0x5f,0x34,0x35, + 0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73, + 0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b, + 0x0a,0x20,0x20,0x40,0x62,0x75,0x69,0x6c,0x74,0x69,0x6e,0x28,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x29,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x20,0x20,0x40, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x0a,0x20,0x20,0x75,0x76, + 0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c, + 0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x0a,0x20,0x20,0x63,0x6f,0x6c, + 0x6f,0x72,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a, + 0x0a,0x40,0x76,0x65,0x72,0x74,0x65,0x78,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e, + 0x28,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x70,0x6f, + 0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f, + 0x6e,0x28,0x31,0x29,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c, + 0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x32,0x29,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c, + 0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x33,0x29,0x20,0x70,0x73, + 0x69,0x7a,0x65,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x66,0x33,0x32,0x29, + 0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20, + 0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x20,0x3d,0x20,0x70,0x6f, + 0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a, + 0x20,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x3d,0x20,0x74,0x65, + 0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x70,0x73,0x69,0x7a,0x65,0x20, + 0x3d,0x20,0x70,0x73,0x69,0x7a,0x65,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20, + 0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74, + 0x75,0x72,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x67,0x6c,0x5f, + 0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x2c,0x20,0x75,0x76,0x2c,0x20,0x63,0x6f, + 0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const uint8_t _sgl_fs_source_wgsl[647] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75, + 0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x34,0x38, + 0x29,0x20,0x76,0x61,0x72,0x20,0x74,0x65,0x78,0x20,0x3a,0x20,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x5f,0x32,0x64,0x3c,0x66,0x33,0x32,0x3e,0x3b,0x0a,0x0a,0x40,0x67, + 0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67, + 0x28,0x36,0x34,0x29,0x20,0x76,0x61,0x72,0x20,0x73,0x6d,0x70,0x20,0x3a,0x20,0x73, + 0x61,0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a, + 0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20, + 0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x33,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x35,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x2c,0x20,0x73, + 0x6d,0x70,0x2c,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x78,0x5f,0x32,0x33,0x2e,0x78, + 0x2c,0x20,0x78,0x5f,0x32,0x33,0x2e,0x79,0x29,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65, + 0x74,0x20,0x78,0x5f,0x32,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x28,0x78,0x5f,0x32,0x35,0x20,0x2a,0x20,0x78, + 0x5f,0x32,0x37,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a, + 0x7d,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f, + 0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e, + 0x28,0x30,0x29,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40, + 0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e, + 0x28,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x75,0x76, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x20, + 0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x20,0x63,0x6f,0x6c, + 0x6f,0x72,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x29,0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a, + 0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x75,0x76,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b, + 0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31, + 0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6d,0x61,0x69, + 0x6e,0x5f,0x6f,0x75,0x74,0x28,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_DUMMY_BACKEND) +static const char* _sgl_vs_source_dummy = ""; +static const char* _sgl_fs_source_dummy = ""; +#else +#error "Please define one of SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" +#endif + +// ████████ ██ ██ ██████ ███████ ███████ +// ██ ██ ██ ██ ██ ██ ██ +// ██ ████ ██████ █████ ███████ +// ██ ██ ██ ██ ██ +// ██ ██ ██ ███████ ███████ +// +// >>types +typedef enum { + SGL_PRIMITIVETYPE_POINTS = 0, + SGL_PRIMITIVETYPE_LINES, + SGL_PRIMITIVETYPE_LINE_STRIP, + SGL_PRIMITIVETYPE_TRIANGLES, + SGL_PRIMITIVETYPE_TRIANGLE_STRIP, + SGL_PRIMITIVETYPE_QUADS, + SGL_NUM_PRIMITIVE_TYPES, +} _sgl_primitive_type_t; + +typedef struct { + uint32_t id; + sg_resource_state state; +} _sgl_slot_t; + +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _sgl_pool_t; + +typedef struct { + _sgl_slot_t slot; + sg_pipeline pip[SGL_NUM_PRIMITIVE_TYPES]; +} _sgl_pipeline_t; + +typedef struct { + _sgl_pool_t pool; + _sgl_pipeline_t* pips; +} _sgl_pipeline_pool_t; + +typedef enum { + SGL_MATRIXMODE_MODELVIEW, + SGL_MATRIXMODE_PROJECTION, + SGL_MATRIXMODE_TEXTURE, + SGL_NUM_MATRIXMODES +} _sgl_matrix_mode_t; + +typedef struct { + float pos[3]; + float uv[2]; + uint32_t rgba; + float psize; +} _sgl_vertex_t; + +typedef struct { + float v[4][4]; +} _sgl_matrix_t; + +typedef struct { + _sgl_matrix_t mvp; /* model-view-projection matrix */ + _sgl_matrix_t tm; /* texture matrix */ +} _sgl_uniform_t; + +typedef enum { + SGL_COMMAND_DRAW, + SGL_COMMAND_VIEWPORT, + SGL_COMMAND_SCISSOR_RECT, +} _sgl_command_type_t; + +typedef struct { + sg_pipeline pip; + sg_image img; + sg_sampler smp; + int base_vertex; + int num_vertices; + int uniform_index; +} _sgl_draw_args_t; + +typedef struct { + int x, y, w, h; + bool origin_top_left; +} _sgl_viewport_args_t; + +typedef struct { + int x, y, w, h; + bool origin_top_left; +} _sgl_scissor_rect_args_t; + +typedef union { + _sgl_draw_args_t draw; + _sgl_viewport_args_t viewport; + _sgl_scissor_rect_args_t scissor_rect; +} _sgl_args_t; + +typedef struct { + _sgl_command_type_t cmd; + int layer_id; + _sgl_args_t args; +} _sgl_command_t; + +#define _SGL_INVALID_SLOT_INDEX (0) +#define _SGL_MAX_STACK_DEPTH (64) +#define _SGL_DEFAULT_CONTEXT_POOL_SIZE (4) +#define _SGL_DEFAULT_PIPELINE_POOL_SIZE (64) +#define _SGL_DEFAULT_MAX_VERTICES (1<<16) +#define _SGL_DEFAULT_MAX_COMMANDS (1<<14) +#define _SGL_SLOT_SHIFT (16) +#define _SGL_MAX_POOL_SIZE (1<<_SGL_SLOT_SHIFT) +#define _SGL_SLOT_MASK (_SGL_MAX_POOL_SIZE-1) + +typedef struct { + _sgl_slot_t slot; + sgl_context_desc_t desc; + uint32_t frame_id; + uint32_t update_frame_id; + struct { + int cap; + int next; + _sgl_vertex_t* ptr; + } vertices; + struct { + int cap; + int next; + _sgl_uniform_t* ptr; + } uniforms; + struct { + int cap; + int next; + _sgl_command_t* ptr; + } commands; + + /* state tracking */ + int base_vertex; + int vtx_count; /* number of times vtx function has been called, used for non-triangle primitives */ + sgl_error_t error; + bool in_begin; + int layer_id; + float u, v; + uint32_t rgba; + float point_size; + _sgl_primitive_type_t cur_prim_type; + sg_image cur_img; + sg_sampler cur_smp; + bool texturing_enabled; + bool matrix_dirty; /* reset in sgl_end(), set in any of the matrix stack functions */ + + /* sokol-gfx resources */ + sg_buffer vbuf; + sgl_pipeline def_pip; + sg_bindings bind; + + /* pipeline stack */ + int pip_tos; + sgl_pipeline pip_stack[_SGL_MAX_STACK_DEPTH]; + + /* matrix stacks */ + _sgl_matrix_mode_t cur_matrix_mode; + int matrix_tos[SGL_NUM_MATRIXMODES]; + _sgl_matrix_t matrix_stack[SGL_NUM_MATRIXMODES][_SGL_MAX_STACK_DEPTH]; +} _sgl_context_t; + +typedef struct { + _sgl_pool_t pool; + _sgl_context_t* contexts; +} _sgl_context_pool_t; + +typedef struct { + uint32_t init_cookie; + sgl_desc_t desc; + sg_image def_img; // a default white texture + sg_sampler def_smp; // a default sampler + sg_shader shd; // same shader for all contexts + sgl_context def_ctx_id; + sgl_context cur_ctx_id; + _sgl_context_t* cur_ctx; // may be 0! + _sgl_pipeline_pool_t pip_pool; + _sgl_context_pool_t context_pool; +} _sgl_t; +static _sgl_t _sgl; + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SGL_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sgl_log_messages[] = { + _SGL_LOG_ITEMS +}; +#undef _SGL_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SGL_PANIC(code) _sgl_log(SGL_LOGITEM_ ##code, 0, __LINE__) +#define _SGL_ERROR(code) _sgl_log(SGL_LOGITEM_ ##code, 1, __LINE__) +#define _SGL_WARN(code) _sgl_log(SGL_LOGITEM_ ##code, 2, __LINE__) +#define _SGL_INFO(code) _sgl_log(SGL_LOGITEM_ ##code, 3, __LINE__) + +static void _sgl_log(sgl_log_item_t log_item, uint32_t log_level, uint32_t line_nr) { + if (_sgl.desc.logger.func) { + #if defined(SOKOL_DEBUG) + const char* filename = __FILE__; + const char* message = _sgl_log_messages[log_item]; + #else + const char* filename = 0; + const char* message = 0; + #endif + _sgl.desc.logger.func("sgl", log_level, log_item, message, line_nr, filename, _sgl.desc.logger.user_data); + } else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory +static void _sgl_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +static void* _sgl_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_sgl.desc.allocator.alloc_fn) { + ptr = _sgl.desc.allocator.alloc_fn(size, _sgl.desc.allocator.user_data); + } else { + ptr = malloc(size); + } + if (0 == ptr) { + _SGL_PANIC(MALLOC_FAILED); + } + return ptr; +} + +static void* _sgl_malloc_clear(size_t size) { + void* ptr = _sgl_malloc(size); + _sgl_clear(ptr, size); + return ptr; +} + +static void _sgl_free(void* ptr) { + if (_sgl.desc.allocator.free_fn) { + _sgl.desc.allocator.free_fn(ptr, _sgl.desc.allocator.user_data); + } else { + free(ptr); + } +} + +// ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ +// +// >>pool +static void _sgl_init_pool(_sgl_pool_t* pool, int num) { + SOKOL_ASSERT(pool && (num >= 1)); + /* slot 0 is reserved for the 'invalid id', so bump the pool size by 1 */ + pool->size = num + 1; + pool->queue_top = 0; + /* generation counters indexable by pool slot index, slot 0 is reserved */ + size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size; + pool->gen_ctrs = (uint32_t*) _sgl_malloc_clear(gen_ctrs_size); + /* it's not a bug to only reserve 'num' here */ + pool->free_queue = (int*) _sgl_malloc_clear(sizeof(int) * (size_t)num); + /* never allocate the zero-th pool item since the invalid id is 0 */ + for (int i = pool->size-1; i >= 1; i--) { + pool->free_queue[pool->queue_top++] = i; + } +} + +static void _sgl_discard_pool(_sgl_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + _sgl_free(pool->free_queue); + pool->free_queue = 0; + SOKOL_ASSERT(pool->gen_ctrs); + _sgl_free(pool->gen_ctrs); + pool->gen_ctrs = 0; + pool->size = 0; + pool->queue_top = 0; +} + +static int _sgl_pool_alloc_index(_sgl_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } else { + // pool exhausted + return _SGL_INVALID_SLOT_INDEX; + } +} + +static void _sgl_pool_free_index(_sgl_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SGL_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_ASSERT(pool->queue_top < pool->size); + #ifdef SOKOL_DEBUG + /* debug check against double-free */ + for (int i = 0; i < pool->queue_top; i++) { + SOKOL_ASSERT(pool->free_queue[i] != slot_index); + } + #endif + pool->free_queue[pool->queue_top++] = slot_index; + SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); +} + +/* allocate the slot at slot_index: + - bump the slot's generation counter + - create a resource id from the generation counter and slot index + - set the slot's id to this id + - set the slot's state to ALLOC + - return the resource id +*/ +static uint32_t _sgl_slot_alloc(_sgl_pool_t* pool, _sgl_slot_t* slot, int slot_index) { + /* FIXME: add handling for an overflowing generation counter, + for now, just overflow (another option is to disable + the slot) + */ + SOKOL_ASSERT(pool && pool->gen_ctrs); + SOKOL_ASSERT((slot_index > _SGL_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT((slot->state == SG_RESOURCESTATE_INITIAL) && (slot->id == SG_INVALID_ID)); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + slot->id = (ctr<<_SGL_SLOT_SHIFT)|(slot_index & _SGL_SLOT_MASK); + slot->state = SG_RESOURCESTATE_ALLOC; + return slot->id; +} + +/* extract slot index from id */ +static int _sgl_slot_index(uint32_t id) { + int slot_index = (int) (id & _SGL_SLOT_MASK); + SOKOL_ASSERT(_SGL_INVALID_SLOT_INDEX != slot_index); + return slot_index; +} + +// ██████ ██ ██████ ███████ ██ ██ ███ ██ ███████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██████ ██ ██████ █████ ██ ██ ██ ██ ██ █████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ███████ ███████ ██ ██ ████ ███████ ███████ +// +// >>pipelines +static void _sgl_reset_pipeline(_sgl_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _sgl_clear(pip, sizeof(_sgl_pipeline_t)); +} + +static void _sgl_setup_pipeline_pool(int pool_size) { + /* note: the pools here will have an additional item, since slot 0 is reserved */ + SOKOL_ASSERT((pool_size > 0) && (pool_size < _SGL_MAX_POOL_SIZE)); + _sgl_init_pool(&_sgl.pip_pool.pool, pool_size); + size_t pool_byte_size = sizeof(_sgl_pipeline_t) * (size_t)_sgl.pip_pool.pool.size; + _sgl.pip_pool.pips = (_sgl_pipeline_t*) _sgl_malloc_clear(pool_byte_size); +} + +static void _sgl_discard_pipeline_pool(void) { + SOKOL_ASSERT(0 != _sgl.pip_pool.pips); + _sgl_free(_sgl.pip_pool.pips); _sgl.pip_pool.pips = 0; + _sgl_discard_pool(&_sgl.pip_pool.pool); +} + +/* get pipeline pointer without id-check */ +static _sgl_pipeline_t* _sgl_pipeline_at(uint32_t pip_id) { + SOKOL_ASSERT(SG_INVALID_ID != pip_id); + int slot_index = _sgl_slot_index(pip_id); + SOKOL_ASSERT((slot_index > _SGL_INVALID_SLOT_INDEX) && (slot_index < _sgl.pip_pool.pool.size)); + return &_sgl.pip_pool.pips[slot_index]; +} + +/* get pipeline pointer with id-check, returns 0 if no match */ +static _sgl_pipeline_t* _sgl_lookup_pipeline(uint32_t pip_id) { + if (SG_INVALID_ID != pip_id) { + _sgl_pipeline_t* pip = _sgl_pipeline_at(pip_id); + if (pip->slot.id == pip_id) { + return pip; + } + } + return 0; +} + +/* make pipeline id from uint32_t id */ +static sgl_pipeline _sgl_make_pip_id(uint32_t pip_id) { + sgl_pipeline pip = { pip_id }; + return pip; +} + +static sgl_pipeline _sgl_alloc_pipeline(void) { + sgl_pipeline res; + int slot_index = _sgl_pool_alloc_index(&_sgl.pip_pool.pool); + if (_SGL_INVALID_SLOT_INDEX != slot_index) { + res = _sgl_make_pip_id(_sgl_slot_alloc(&_sgl.pip_pool.pool, &_sgl.pip_pool.pips[slot_index].slot, slot_index)); + } else { + /* pool is exhausted */ + res = _sgl_make_pip_id(SG_INVALID_ID); + } + return res; +} + +static void _sgl_init_pipeline(sgl_pipeline pip_id, const sg_pipeline_desc* in_desc, const sgl_context_desc_t* ctx_desc) { + SOKOL_ASSERT((pip_id.id != SG_INVALID_ID) && in_desc && ctx_desc); + + /* create a new desc with 'patched' shader and pixel format state */ + sg_pipeline_desc desc = *in_desc; + desc.layout.buffers[0].stride = sizeof(_sgl_vertex_t); + { + sg_vertex_attr_state* pos = &desc.layout.attrs[0]; + pos->offset = offsetof(_sgl_vertex_t, pos); + pos->format = SG_VERTEXFORMAT_FLOAT3; + } + { + sg_vertex_attr_state* uv = &desc.layout.attrs[1]; + uv->offset = offsetof(_sgl_vertex_t, uv); + uv->format = SG_VERTEXFORMAT_FLOAT2; + } + { + sg_vertex_attr_state* rgba = &desc.layout.attrs[2]; + rgba->offset = offsetof(_sgl_vertex_t, rgba); + rgba->format = SG_VERTEXFORMAT_UBYTE4N; + } + { + sg_vertex_attr_state* psize = &desc.layout.attrs[3]; + psize->offset = offsetof(_sgl_vertex_t, psize); + psize->format = SG_VERTEXFORMAT_FLOAT; + } + if (in_desc->shader.id == SG_INVALID_ID) { + desc.shader = _sgl.shd; + } + desc.index_type = SG_INDEXTYPE_NONE; + desc.sample_count = ctx_desc->sample_count; + if (desc.face_winding == _SG_FACEWINDING_DEFAULT) { + desc.face_winding = _sgl.desc.face_winding; + } + desc.depth.pixel_format = ctx_desc->depth_format; + if (ctx_desc->depth_format == SG_PIXELFORMAT_NONE) { + desc.depth.write_enabled = false; + } + desc.colors[0].pixel_format = ctx_desc->color_format; + if (desc.colors[0].write_mask == _SG_COLORMASK_DEFAULT) { + desc.colors[0].write_mask = SG_COLORMASK_RGB; + } + + _sgl_pipeline_t* pip = _sgl_lookup_pipeline(pip_id.id); + SOKOL_ASSERT(pip && (pip->slot.state == SG_RESOURCESTATE_ALLOC)); + pip->slot.state = SG_RESOURCESTATE_VALID; + for (int i = 0; i < SGL_NUM_PRIMITIVE_TYPES; i++) { + switch (i) { + case SGL_PRIMITIVETYPE_POINTS: + desc.primitive_type = SG_PRIMITIVETYPE_POINTS; + break; + case SGL_PRIMITIVETYPE_LINES: + desc.primitive_type = SG_PRIMITIVETYPE_LINES; + break; + case SGL_PRIMITIVETYPE_LINE_STRIP: + desc.primitive_type = SG_PRIMITIVETYPE_LINE_STRIP; + break; + case SGL_PRIMITIVETYPE_TRIANGLES: + desc.primitive_type = SG_PRIMITIVETYPE_TRIANGLES; + break; + case SGL_PRIMITIVETYPE_TRIANGLE_STRIP: + case SGL_PRIMITIVETYPE_QUADS: + desc.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP; + break; + } + if (SGL_PRIMITIVETYPE_QUADS == i) { + /* quads are emulated via triangles, use the same pipeline object */ + pip->pip[i] = pip->pip[SGL_PRIMITIVETYPE_TRIANGLES]; + } else { + pip->pip[i] = sg_make_pipeline(&desc); + if (pip->pip[i].id == SG_INVALID_ID) { + _SGL_ERROR(MAKE_PIPELINE_FAILED); + pip->slot.state = SG_RESOURCESTATE_FAILED; + } + } + } +} + +static sgl_pipeline _sgl_make_pipeline(const sg_pipeline_desc* desc, const sgl_context_desc_t* ctx_desc) { + SOKOL_ASSERT(desc && ctx_desc); + sgl_pipeline pip_id = _sgl_alloc_pipeline(); + if (pip_id.id != SG_INVALID_ID) { + _sgl_init_pipeline(pip_id, desc, ctx_desc); + } else { + _SGL_ERROR(PIPELINE_POOL_EXHAUSTED); + } + return pip_id; +} + +static void _sgl_destroy_pipeline(sgl_pipeline pip_id) { + _sgl_pipeline_t* pip = _sgl_lookup_pipeline(pip_id.id); + if (pip) { + sg_push_debug_group("sokol-gl"); + for (int i = 0; i < SGL_NUM_PRIMITIVE_TYPES; i++) { + if (i != SGL_PRIMITIVETYPE_QUADS) { + sg_destroy_pipeline(pip->pip[i]); + } + } + sg_pop_debug_group(); + _sgl_reset_pipeline(pip); + _sgl_pool_free_index(&_sgl.pip_pool.pool, _sgl_slot_index(pip_id.id)); + } +} + +static sg_pipeline _sgl_get_pipeline(sgl_pipeline pip_id, _sgl_primitive_type_t prim_type) { + _sgl_pipeline_t* pip = _sgl_lookup_pipeline(pip_id.id); + if (pip) { + return pip->pip[prim_type]; + } else { + sg_pipeline dummy_id = { SG_INVALID_ID }; + return dummy_id; + } +} + +// ██████ ██████ ███ ██ ████████ ███████ ██ ██ ████████ ███████ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ █████ ███ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ████ ██ ███████ ██ ██ ██ ███████ +// +// >>contexts +static void _sgl_reset_context(_sgl_context_t* ctx) { + SOKOL_ASSERT(ctx); + SOKOL_ASSERT(0 == ctx->vertices.ptr); + SOKOL_ASSERT(0 == ctx->uniforms.ptr); + SOKOL_ASSERT(0 == ctx->commands.ptr); + _sgl_clear(ctx, sizeof(_sgl_context_t)); +} + +static void _sgl_setup_context_pool(int pool_size) { + /* note: the pools here will have an additional item, since slot 0 is reserved */ + SOKOL_ASSERT((pool_size > 0) && (pool_size < _SGL_MAX_POOL_SIZE)); + _sgl_init_pool(&_sgl.context_pool.pool, pool_size); + size_t pool_byte_size = sizeof(_sgl_context_t) * (size_t)_sgl.context_pool.pool.size; + _sgl.context_pool.contexts = (_sgl_context_t*) _sgl_malloc_clear(pool_byte_size); +} + +static void _sgl_discard_context_pool(void) { + SOKOL_ASSERT(0 != _sgl.context_pool.contexts); + _sgl_free(_sgl.context_pool.contexts); _sgl.context_pool.contexts = 0; + _sgl_discard_pool(&_sgl.context_pool.pool); +} + +// get context pointer without id-check +static _sgl_context_t* _sgl_context_at(uint32_t ctx_id) { + SOKOL_ASSERT(SG_INVALID_ID != ctx_id); + int slot_index = _sgl_slot_index(ctx_id); + SOKOL_ASSERT((slot_index > _SGL_INVALID_SLOT_INDEX) && (slot_index < _sgl.context_pool.pool.size)); + return &_sgl.context_pool.contexts[slot_index]; +} + +// get context pointer with id-check, returns 0 if no match +static _sgl_context_t* _sgl_lookup_context(uint32_t ctx_id) { + if (SG_INVALID_ID != ctx_id) { + _sgl_context_t* ctx = _sgl_context_at(ctx_id); + if (ctx->slot.id == ctx_id) { + return ctx; + } + } + return 0; +} + +// make context id from uint32_t id +static sgl_context _sgl_make_ctx_id(uint32_t ctx_id) { + sgl_context ctx = { ctx_id }; + return ctx; +} + +static sgl_context _sgl_alloc_context(void) { + sgl_context res; + int slot_index = _sgl_pool_alloc_index(&_sgl.context_pool.pool); + if (_SGL_INVALID_SLOT_INDEX != slot_index) { + res = _sgl_make_ctx_id(_sgl_slot_alloc(&_sgl.context_pool.pool, &_sgl.context_pool.contexts[slot_index].slot, slot_index)); + } else { + // pool is exhausted + res = _sgl_make_ctx_id(SG_INVALID_ID); + } + return res; +} + +// return sgl_context_desc_t with patched defaults +static sgl_context_desc_t _sgl_context_desc_defaults(const sgl_context_desc_t* desc) { + sgl_context_desc_t res = *desc; + res.max_vertices = _sgl_def(desc->max_vertices, _SGL_DEFAULT_MAX_VERTICES); + res.max_commands = _sgl_def(desc->max_commands, _SGL_DEFAULT_MAX_COMMANDS); + return res; +} + +static void _sgl_identity(_sgl_matrix_t*); +static sg_commit_listener _sgl_make_commit_listener(_sgl_context_t* ctx); +static void _sgl_init_context(sgl_context ctx_id, const sgl_context_desc_t* in_desc) { + SOKOL_ASSERT((ctx_id.id != SG_INVALID_ID) && in_desc); + _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id); + SOKOL_ASSERT(ctx); + ctx->desc = _sgl_context_desc_defaults(in_desc); + // NOTE: frame_id must be non-zero, so that updates trigger in first frame + ctx->frame_id = 1; + ctx->cur_img = _sgl.def_img; + ctx->cur_smp = _sgl.def_smp; + + // allocate buffers and pools + ctx->vertices.cap = ctx->desc.max_vertices; + ctx->commands.cap = ctx->uniforms.cap = ctx->desc.max_commands; + ctx->vertices.ptr = (_sgl_vertex_t*) _sgl_malloc((size_t)ctx->vertices.cap * sizeof(_sgl_vertex_t)); + ctx->uniforms.ptr = (_sgl_uniform_t*) _sgl_malloc((size_t)ctx->uniforms.cap * sizeof(_sgl_uniform_t)); + ctx->commands.ptr = (_sgl_command_t*) _sgl_malloc((size_t)ctx->commands.cap * sizeof(_sgl_command_t)); + + // create sokol-gfx resource objects + sg_push_debug_group("sokol-gl"); + + sg_buffer_desc vbuf_desc; + _sgl_clear(&vbuf_desc, sizeof(vbuf_desc)); + vbuf_desc.size = (size_t)ctx->vertices.cap * sizeof(_sgl_vertex_t); + vbuf_desc.type = SG_BUFFERTYPE_VERTEXBUFFER; + vbuf_desc.usage = SG_USAGE_STREAM; + vbuf_desc.label = "sgl-vertex-buffer"; + ctx->vbuf = sg_make_buffer(&vbuf_desc); + SOKOL_ASSERT(SG_INVALID_ID != ctx->vbuf.id); + ctx->bind.vertex_buffers[0] = ctx->vbuf; + + sg_pipeline_desc def_pip_desc; + _sgl_clear(&def_pip_desc, sizeof(def_pip_desc)); + def_pip_desc.depth.write_enabled = true; + ctx->def_pip = _sgl_make_pipeline(&def_pip_desc, &ctx->desc); + if (!sg_add_commit_listener(_sgl_make_commit_listener(ctx))) { + _SGL_ERROR(ADD_COMMIT_LISTENER_FAILED); + } + sg_pop_debug_group(); + + // default state + ctx->rgba = 0xFFFFFFFF; + ctx->point_size = 1.0f; + for (int i = 0; i < SGL_NUM_MATRIXMODES; i++) { + _sgl_identity(&ctx->matrix_stack[i][0]); + } + ctx->pip_stack[0] = ctx->def_pip; + ctx->matrix_dirty = true; +} + +static sgl_context _sgl_make_context(const sgl_context_desc_t* desc) { + SOKOL_ASSERT(desc); + sgl_context ctx_id = _sgl_alloc_context(); + if (ctx_id.id != SG_INVALID_ID) { + _sgl_init_context(ctx_id, desc); + } else { + _SGL_ERROR(CONTEXT_POOL_EXHAUSTED); + } + return ctx_id; +} + +static void _sgl_destroy_context(sgl_context ctx_id) { + _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id); + if (ctx) { + SOKOL_ASSERT(ctx->vertices.ptr); + SOKOL_ASSERT(ctx->uniforms.ptr); + SOKOL_ASSERT(ctx->commands.ptr); + + _sgl_free(ctx->vertices.ptr); + _sgl_free(ctx->uniforms.ptr); + _sgl_free(ctx->commands.ptr); + ctx->vertices.ptr = 0; + ctx->uniforms.ptr = 0; + ctx->commands.ptr = 0; + + sg_push_debug_group("sokol-gl"); + sg_destroy_buffer(ctx->vbuf); + _sgl_destroy_pipeline(ctx->def_pip); + sg_remove_commit_listener(_sgl_make_commit_listener(ctx)); + sg_pop_debug_group(); + + _sgl_reset_context(ctx); + _sgl_pool_free_index(&_sgl.context_pool.pool, _sgl_slot_index(ctx_id.id)); + } +} + +// ███ ███ ██ ███████ ██████ +// ████ ████ ██ ██ ██ +// ██ ████ ██ ██ ███████ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ███████ ██████ +// +// >>misc +static void _sgl_begin(_sgl_context_t* ctx, _sgl_primitive_type_t mode) { + ctx->in_begin = true; + ctx->base_vertex = ctx->vertices.next; + ctx->vtx_count = 0; + ctx->cur_prim_type = mode; +} + +static void _sgl_rewind(_sgl_context_t* ctx) { + ctx->frame_id++; + ctx->vertices.next = 0; + ctx->uniforms.next = 0; + ctx->commands.next = 0; + ctx->base_vertex = 0; + ctx->error = SGL_NO_ERROR; + ctx->layer_id = 0; + ctx->matrix_dirty = true; +} + +// called from inside sokol-gfx sg_commit() +static void _sgl_commit_listener(void* userdata) { + _sgl_context_t* ctx = _sgl_lookup_context((uint32_t)(uintptr_t)userdata); + if (ctx) { + _sgl_rewind(ctx); + } +} + +static sg_commit_listener _sgl_make_commit_listener(_sgl_context_t* ctx) { + sg_commit_listener listener = { _sgl_commit_listener, (void*)(uintptr_t)(ctx->slot.id) }; + return listener; +} + +static _sgl_vertex_t* _sgl_next_vertex(_sgl_context_t* ctx) { + if (ctx->vertices.next < ctx->vertices.cap) { + return &ctx->vertices.ptr[ctx->vertices.next++]; + } else { + ctx->error = SGL_ERROR_VERTICES_FULL; + return 0; + } +} + +static _sgl_uniform_t* _sgl_next_uniform(_sgl_context_t* ctx) { + if (ctx->uniforms.next < ctx->uniforms.cap) { + return &ctx->uniforms.ptr[ctx->uniforms.next++]; + } else { + ctx->error = SGL_ERROR_UNIFORMS_FULL; + return 0; + } +} + +static _sgl_command_t* _sgl_cur_command(_sgl_context_t* ctx) { + if (ctx->commands.next > 0) { + return &ctx->commands.ptr[ctx->commands.next - 1]; + } else { + return 0; + } +} + +static _sgl_command_t* _sgl_next_command(_sgl_context_t* ctx) { + if (ctx->commands.next < ctx->commands.cap) { + return &ctx->commands.ptr[ctx->commands.next++]; + } else { + ctx->error = SGL_ERROR_COMMANDS_FULL; + return 0; + } +} + +static uint32_t _sgl_pack_rgbab(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + return (uint32_t)(((uint32_t)a<<24)|((uint32_t)b<<16)|((uint32_t)g<<8)|r); +} + +static float _sgl_clamp(float v, float lo, float hi) { + if (v < lo) return lo; + else if (v > hi) return hi; + else return v; +} + +static uint32_t _sgl_pack_rgbaf(float r, float g, float b, float a) { + uint8_t r_u8 = (uint8_t) (_sgl_clamp(r, 0.0f, 1.0f) * 255.0f); + uint8_t g_u8 = (uint8_t) (_sgl_clamp(g, 0.0f, 1.0f) * 255.0f); + uint8_t b_u8 = (uint8_t) (_sgl_clamp(b, 0.0f, 1.0f) * 255.0f); + uint8_t a_u8 = (uint8_t) (_sgl_clamp(a, 0.0f, 1.0f) * 255.0f); + return _sgl_pack_rgbab(r_u8, g_u8, b_u8, a_u8); +} + +static void _sgl_vtx(_sgl_context_t* ctx, float x, float y, float z, float u, float v, uint32_t rgba) { + SOKOL_ASSERT(ctx->in_begin); + _sgl_vertex_t* vtx; + /* handle non-native primitive types */ + if ((ctx->cur_prim_type == SGL_PRIMITIVETYPE_QUADS) && ((ctx->vtx_count & 3) == 3)) { + /* for quads, before writing the last quad vertex, reuse + the first and third vertex to start the second triangle in the quad + */ + vtx = _sgl_next_vertex(ctx); + if (vtx) { *vtx = *(vtx - 3); } + vtx = _sgl_next_vertex(ctx); + if (vtx) { *vtx = *(vtx - 2); } + } + vtx = _sgl_next_vertex(ctx); + if (vtx) { + vtx->pos[0] = x; vtx->pos[1] = y; vtx->pos[2] = z; + vtx->uv[0] = u; vtx->uv[1] = v; + vtx->rgba = rgba; + vtx->psize = ctx->point_size; + } + ctx->vtx_count++; +} + +static void _sgl_identity(_sgl_matrix_t* m) { + for (int c = 0; c < 4; c++) { + for (int r = 0; r < 4; r++) { + m->v[c][r] = (r == c) ? 1.0f : 0.0f; + } + } +} + +static void _sgl_transpose(_sgl_matrix_t* dst, const _sgl_matrix_t* m) { + SOKOL_ASSERT(dst != m); + for (int c = 0; c < 4; c++) { + for (int r = 0; r < 4; r++) { + dst->v[r][c] = m->v[c][r]; + } + } +} + +/* _sgl_rotate, _sgl_frustum, _sgl_ortho from MESA m_matric.c */ +static void _sgl_matmul4(_sgl_matrix_t* p, const _sgl_matrix_t* a, const _sgl_matrix_t* b) { + for (int r = 0; r < 4; r++) { + float ai0=a->v[0][r], ai1=a->v[1][r], ai2=a->v[2][r], ai3=a->v[3][r]; + p->v[0][r] = ai0*b->v[0][0] + ai1*b->v[0][1] + ai2*b->v[0][2] + ai3*b->v[0][3]; + p->v[1][r] = ai0*b->v[1][0] + ai1*b->v[1][1] + ai2*b->v[1][2] + ai3*b->v[1][3]; + p->v[2][r] = ai0*b->v[2][0] + ai1*b->v[2][1] + ai2*b->v[2][2] + ai3*b->v[2][3]; + p->v[3][r] = ai0*b->v[3][0] + ai1*b->v[3][1] + ai2*b->v[3][2] + ai3*b->v[3][3]; + } +} + +static void _sgl_mul(_sgl_matrix_t* dst, const _sgl_matrix_t* m) { + _sgl_matmul4(dst, dst, m); +} + +static void _sgl_rotate(_sgl_matrix_t* dst, float a, float x, float y, float z) { + + float s = sinf(a); + float c = cosf(a); + + float mag = sqrtf(x*x + y*y + z*z); + if (mag < 1.0e-4F) { + return; + } + x /= mag; + y /= mag; + z /= mag; + float xx = x * x; + float yy = y * y; + float zz = z * z; + float xy = x * y; + float yz = y * z; + float zx = z * x; + float xs = x * s; + float ys = y * s; + float zs = z * s; + float one_c = 1.0f - c; + + _sgl_matrix_t m; + m.v[0][0] = (one_c * xx) + c; + m.v[1][0] = (one_c * xy) - zs; + m.v[2][0] = (one_c * zx) + ys; + m.v[3][0] = 0.0f; + m.v[0][1] = (one_c * xy) + zs; + m.v[1][1] = (one_c * yy) + c; + m.v[2][1] = (one_c * yz) - xs; + m.v[3][1] = 0.0f; + m.v[0][2] = (one_c * zx) - ys; + m.v[1][2] = (one_c * yz) + xs; + m.v[2][2] = (one_c * zz) + c; + m.v[3][2] = 0.0f; + m.v[0][3] = 0.0f; + m.v[1][3] = 0.0f; + m.v[2][3] = 0.0f; + m.v[3][3] = 1.0f; + _sgl_mul(dst, &m); +} + +static void _sgl_scale(_sgl_matrix_t* dst, float x, float y, float z) { + for (int r = 0; r < 4; r++) { + dst->v[0][r] *= x; + dst->v[1][r] *= y; + dst->v[2][r] *= z; + } +} + +static void _sgl_translate(_sgl_matrix_t* dst, float x, float y, float z) { + for (int r = 0; r < 4; r++) { + dst->v[3][r] = dst->v[0][r]*x + dst->v[1][r]*y + dst->v[2][r]*z + dst->v[3][r]; + } +} + +static void _sgl_frustum(_sgl_matrix_t* dst, float left, float right, float bottom, float top, float znear, float zfar) { + float x = (2.0f * znear) / (right - left); + float y = (2.0f * znear) / (top - bottom); + float a = (right + left) / (right - left); + float b = (top + bottom) / (top - bottom); + float c = -(zfar + znear) / (zfar - znear); + float d = -(2.0f * zfar * znear) / (zfar - znear); + _sgl_matrix_t m; + m.v[0][0] = x; m.v[0][1] = 0.0f; m.v[0][2] = 0.0f; m.v[0][3] = 0.0f; + m.v[1][0] = 0.0f; m.v[1][1] = y; m.v[1][2] = 0.0f; m.v[1][3] = 0.0f; + m.v[2][0] = a; m.v[2][1] = b; m.v[2][2] = c; m.v[2][3] = -1.0f; + m.v[3][0] = 0.0f; m.v[3][1] = 0.0f; m.v[3][2] = d; m.v[3][3] = 0.0f; + _sgl_mul(dst, &m); +} + +static void _sgl_ortho(_sgl_matrix_t* dst, float left, float right, float bottom, float top, float znear, float zfar) { + _sgl_matrix_t m; + m.v[0][0] = 2.0f / (right - left); + m.v[1][0] = 0.0f; + m.v[2][0] = 0.0f; + m.v[3][0] = -(right + left) / (right - left); + m.v[0][1] = 0.0f; + m.v[1][1] = 2.0f / (top - bottom); + m.v[2][1] = 0.0f; + m.v[3][1] = -(top + bottom) / (top - bottom); + m.v[0][2] = 0.0f; + m.v[1][2] = 0.0f; + m.v[2][2] = -2.0f / (zfar - znear); + m.v[3][2] = -(zfar + znear) / (zfar - znear); + m.v[0][3] = 0.0f; + m.v[1][3] = 0.0f; + m.v[2][3] = 0.0f; + m.v[3][3] = 1.0f; + + _sgl_mul(dst, &m); +} + +/* _sgl_perspective, _sgl_lookat from Regal project.c */ +static void _sgl_perspective(_sgl_matrix_t* dst, float fovy, float aspect, float znear, float zfar) { + float sine = sinf(fovy / 2.0f); + float delta_z = zfar - znear; + if ((delta_z == 0.0f) || (sine == 0.0f) || (aspect == 0.0f)) { + return; + } + float cotan = cosf(fovy / 2.0f) / sine; + _sgl_matrix_t m; + _sgl_identity(&m); + m.v[0][0] = cotan / aspect; + m.v[1][1] = cotan; + m.v[2][2] = -(zfar + znear) / delta_z; + m.v[2][3] = -1.0f; + m.v[3][2] = -2.0f * znear * zfar / delta_z; + m.v[3][3] = 0.0f; + _sgl_mul(dst, &m); +} + +static void _sgl_normalize(float v[3]) { + float r = sqrtf(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); + if (r == 0.0f) { + return; + } + v[0] /= r; + v[1] /= r; + v[2] /= r; +} + +static void _sgl_cross(float v1[3], float v2[3], float res[3]) { + res[0] = v1[1]*v2[2] - v1[2]*v2[1]; + res[1] = v1[2]*v2[0] - v1[0]*v2[2]; + res[2] = v1[0]*v2[1] - v1[1]*v2[0]; +} + +static void _sgl_lookat(_sgl_matrix_t* dst, + float eye_x, float eye_y, float eye_z, + float center_x, float center_y, float center_z, + float up_x, float up_y, float up_z) +{ + float fwd[3], side[3], up[3]; + + fwd[0] = center_x - eye_x; fwd[1] = center_y - eye_y; fwd[2] = center_z - eye_z; + up[0] = up_x; up[1] = up_y; up[2] = up_z; + _sgl_normalize(fwd); + _sgl_cross(fwd, up, side); + _sgl_normalize(side); + _sgl_cross(side, fwd, up); + + _sgl_matrix_t m; + _sgl_identity(&m); + m.v[0][0] = side[0]; + m.v[1][0] = side[1]; + m.v[2][0] = side[2]; + m.v[0][1] = up[0]; + m.v[1][1] = up[1]; + m.v[2][1] = up[2]; + m.v[0][2] = -fwd[0]; + m.v[1][2] = -fwd[1]; + m.v[2][2] = -fwd[2]; + _sgl_mul(dst, &m); + _sgl_translate(dst, -eye_x, -eye_y, -eye_z); +} + +/* current top-of-stack projection matrix */ +static _sgl_matrix_t* _sgl_matrix_projection(_sgl_context_t* ctx) { + return &ctx->matrix_stack[SGL_MATRIXMODE_PROJECTION][ctx->matrix_tos[SGL_MATRIXMODE_PROJECTION]]; +} + +/* get top-of-stack modelview matrix */ +static _sgl_matrix_t* _sgl_matrix_modelview(_sgl_context_t* ctx) { + return &ctx->matrix_stack[SGL_MATRIXMODE_MODELVIEW][ctx->matrix_tos[SGL_MATRIXMODE_MODELVIEW]]; +} + +/* get top-of-stack texture matrix */ +static _sgl_matrix_t* _sgl_matrix_texture(_sgl_context_t* ctx) { + return &ctx->matrix_stack[SGL_MATRIXMODE_TEXTURE][ctx->matrix_tos[SGL_MATRIXMODE_TEXTURE]]; +} + +/* get pointer to current top-of-stack of current matrix mode */ +static _sgl_matrix_t* _sgl_matrix(_sgl_context_t* ctx) { + return &ctx->matrix_stack[ctx->cur_matrix_mode][ctx->matrix_tos[ctx->cur_matrix_mode]]; +} + +// return sg_context_desc_t with patched defaults +static sgl_desc_t _sgl_desc_defaults(const sgl_desc_t* desc) { + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + sgl_desc_t res = *desc; + res.max_vertices = _sgl_def(desc->max_vertices, _SGL_DEFAULT_MAX_VERTICES); + res.max_commands = _sgl_def(desc->max_commands, _SGL_DEFAULT_MAX_COMMANDS); + res.context_pool_size = _sgl_def(desc->context_pool_size, _SGL_DEFAULT_CONTEXT_POOL_SIZE); + res.pipeline_pool_size = _sgl_def(desc->pipeline_pool_size, _SGL_DEFAULT_PIPELINE_POOL_SIZE); + res.face_winding = _sgl_def(desc->face_winding, SG_FACEWINDING_CCW); + return res; +} + +// create resources which are shared between all contexts +static void _sgl_setup_common(void) { + sg_push_debug_group("sokol-gl"); + + uint32_t pixels[64]; + for (int i = 0; i < 64; i++) { + pixels[i] = 0xFFFFFFFF; + } + sg_image_desc img_desc; + _sgl_clear(&img_desc, sizeof(img_desc)); + img_desc.type = SG_IMAGETYPE_2D; + img_desc.width = 8; + img_desc.height = 8; + img_desc.num_mipmaps = 1; + img_desc.pixel_format = SG_PIXELFORMAT_RGBA8; + img_desc.data.subimage[0][0] = SG_RANGE(pixels); + img_desc.label = "sgl-default-texture"; + _sgl.def_img = sg_make_image(&img_desc); + SOKOL_ASSERT(SG_INVALID_ID != _sgl.def_img.id); + + sg_sampler_desc smp_desc; + _sgl_clear(&smp_desc, sizeof(smp_desc)); + smp_desc.min_filter = SG_FILTER_NEAREST; + smp_desc.mag_filter = SG_FILTER_NEAREST; + _sgl.def_smp = sg_make_sampler(&smp_desc); + SOKOL_ASSERT(SG_INVALID_ID != _sgl.def_smp.id); + + // one shader for all contexts + sg_shader_desc shd_desc; + _sgl_clear(&shd_desc, sizeof(shd_desc)); + shd_desc.attrs[0].name = "position"; + shd_desc.attrs[1].name = "texcoord0"; + shd_desc.attrs[2].name = "color0"; + shd_desc.attrs[3].name = "psize"; + shd_desc.attrs[0].sem_name = "TEXCOORD"; + shd_desc.attrs[0].sem_index = 0; + shd_desc.attrs[1].sem_name = "TEXCOORD"; + shd_desc.attrs[1].sem_index = 1; + shd_desc.attrs[2].sem_name = "TEXCOORD"; + shd_desc.attrs[2].sem_index = 2; + shd_desc.attrs[3].sem_name = "TEXCOORD"; + shd_desc.attrs[3].sem_index = 3; + sg_shader_uniform_block_desc* ub = &shd_desc.vs.uniform_blocks[0]; + ub->size = sizeof(_sgl_uniform_t); + ub->uniforms[0].name = "vs_params"; + ub->uniforms[0].type = SG_UNIFORMTYPE_FLOAT4; + ub->uniforms[0].array_count = 8; + shd_desc.fs.images[0].used = true; + shd_desc.fs.images[0].image_type = SG_IMAGETYPE_2D; + shd_desc.fs.images[0].sample_type = SG_IMAGESAMPLETYPE_FLOAT; + shd_desc.fs.samplers[0].used = true; + shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING; + shd_desc.fs.image_sampler_pairs[0].used = true; + shd_desc.fs.image_sampler_pairs[0].image_slot = 0; + shd_desc.fs.image_sampler_pairs[0].sampler_slot = 0; + shd_desc.fs.image_sampler_pairs[0].glsl_name = "tex_smp"; + shd_desc.label = "sgl-shader"; + #if defined(SOKOL_GLCORE) + shd_desc.vs.source = (const char*)_sgl_vs_source_glsl410; + shd_desc.fs.source = (const char*)_sgl_fs_source_glsl410; + #elif defined(SOKOL_GLES3) + shd_desc.vs.source = (const char*)_sgl_vs_source_glsl300es; + shd_desc.fs.source = (const char*)_sgl_fs_source_glsl300es; + #elif defined(SOKOL_METAL) + shd_desc.vs.entry = "main0"; + shd_desc.fs.entry = "main0"; + switch (sg_query_backend()) { + case SG_BACKEND_METAL_MACOS: + shd_desc.vs.bytecode = SG_RANGE(_sgl_vs_bytecode_metal_macos); + shd_desc.fs.bytecode = SG_RANGE(_sgl_fs_bytecode_metal_macos); + break; + case SG_BACKEND_METAL_IOS: + shd_desc.vs.bytecode = SG_RANGE(_sgl_vs_bytecode_metal_ios); + shd_desc.fs.bytecode = SG_RANGE(_sgl_fs_bytecode_metal_ios); + break; + default: + shd_desc.vs.source = (const char*)_sgl_vs_source_metal_sim; + shd_desc.fs.source = (const char*)_sgl_fs_source_metal_sim; + break; + } + #elif defined(SOKOL_D3D11) + shd_desc.vs.bytecode = SG_RANGE(_sgl_vs_bytecode_hlsl4); + shd_desc.fs.bytecode = SG_RANGE(_sgl_fs_bytecode_hlsl4); + #elif defined(SOKOL_WGPU) + shd_desc.vs.source = (const char*)_sgl_vs_source_wgsl; + shd_desc.fs.source = (const char*)_sgl_fs_source_wgsl; + #else + shd_desc.vs.source = _sgl_vs_source_dummy; + shd_desc.fs.source = _sgl_fs_source_dummy; + #endif + _sgl.shd = sg_make_shader(&shd_desc); + SOKOL_ASSERT(SG_INVALID_ID != _sgl.shd.id); + sg_pop_debug_group(); +} + +// discard resources which are shared between all contexts +static void _sgl_discard_common(void) { + sg_push_debug_group("sokol-gl"); + sg_destroy_image(_sgl.def_img); + sg_destroy_sampler(_sgl.def_smp); + sg_destroy_shader(_sgl.shd); + sg_pop_debug_group(); +} + +static bool _sgl_is_default_context(sgl_context ctx_id) { + return ctx_id.id == SGL_DEFAULT_CONTEXT.id; +} + +static void _sgl_draw(_sgl_context_t* ctx, int layer_id) { + SOKOL_ASSERT(ctx); + if ((ctx->error == SGL_NO_ERROR) && (ctx->vertices.next > 0) && (ctx->commands.next > 0)) { + sg_push_debug_group("sokol-gl"); + + uint32_t cur_pip_id = SG_INVALID_ID; + uint32_t cur_img_id = SG_INVALID_ID; + uint32_t cur_smp_id = SG_INVALID_ID; + int cur_uniform_index = -1; + + if (ctx->update_frame_id != ctx->frame_id) { + ctx->update_frame_id = ctx->frame_id; + const sg_range range = { ctx->vertices.ptr, (size_t)ctx->vertices.next * sizeof(_sgl_vertex_t) }; + sg_update_buffer(ctx->vbuf, &range); + } + + for (int i = 0; i < ctx->commands.next; i++) { + const _sgl_command_t* cmd = &ctx->commands.ptr[i]; + if (cmd->layer_id != layer_id) { + continue; + } + switch (cmd->cmd) { + case SGL_COMMAND_VIEWPORT: + { + const _sgl_viewport_args_t* args = &cmd->args.viewport; + sg_apply_viewport(args->x, args->y, args->w, args->h, args->origin_top_left); + } + break; + case SGL_COMMAND_SCISSOR_RECT: + { + const _sgl_scissor_rect_args_t* args = &cmd->args.scissor_rect; + sg_apply_scissor_rect(args->x, args->y, args->w, args->h, args->origin_top_left); + } + break; + case SGL_COMMAND_DRAW: + { + const _sgl_draw_args_t* args = &cmd->args.draw; + if (args->pip.id != cur_pip_id) { + sg_apply_pipeline(args->pip); + cur_pip_id = args->pip.id; + /* when pipeline changes, also need to re-apply uniforms and bindings */ + cur_img_id = SG_INVALID_ID; + cur_smp_id = SG_INVALID_ID; + cur_uniform_index = -1; + } + if ((cur_img_id != args->img.id) || (cur_smp_id != args->smp.id)) { + ctx->bind.fs.images[0] = args->img; + ctx->bind.fs.samplers[0] = args->smp; + sg_apply_bindings(&ctx->bind); + cur_img_id = args->img.id; + cur_smp_id = args->smp.id; + } + if (cur_uniform_index != args->uniform_index) { + const sg_range ub_range = { &ctx->uniforms.ptr[args->uniform_index], sizeof(_sgl_uniform_t) }; + sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &ub_range); + cur_uniform_index = args->uniform_index; + } + /* FIXME: what if number of vertices doesn't match the primitive type? */ + if (args->num_vertices > 0) { + sg_draw(args->base_vertex, args->num_vertices, 1); + } + } + break; + } + } + sg_pop_debug_group(); + } +} + +static sgl_context_desc_t _sgl_as_context_desc(const sgl_desc_t* desc) { + sgl_context_desc_t ctx_desc; + _sgl_clear(&ctx_desc, sizeof(ctx_desc)); + ctx_desc.max_vertices = desc->max_vertices; + ctx_desc.max_commands = desc->max_commands; + ctx_desc.color_format = desc->color_format; + ctx_desc.depth_format = desc->depth_format; + ctx_desc.sample_count = desc->sample_count; + return ctx_desc; +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void sgl_setup(const sgl_desc_t* desc) { + SOKOL_ASSERT(desc); + _sgl_clear(&_sgl, sizeof(_sgl)); + _sgl.init_cookie = _SGL_INIT_COOKIE; + _sgl.desc = _sgl_desc_defaults(desc); + _sgl_setup_pipeline_pool(_sgl.desc.pipeline_pool_size); + _sgl_setup_context_pool(_sgl.desc.context_pool_size); + _sgl_setup_common(); + const sgl_context_desc_t ctx_desc = _sgl_as_context_desc(&_sgl.desc); + _sgl.def_ctx_id = sgl_make_context(&ctx_desc); + SOKOL_ASSERT(SGL_DEFAULT_CONTEXT.id == _sgl.def_ctx_id.id); + sgl_set_context(_sgl.def_ctx_id); +} + +SOKOL_API_IMPL void sgl_shutdown(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + // contexts own a pipeline, so destroy contexts before pipelines + for (int i = 0; i < _sgl.context_pool.pool.size; i++) { + _sgl_context_t* ctx = &_sgl.context_pool.contexts[i]; + _sgl_destroy_context(_sgl_make_ctx_id(ctx->slot.id)); + } + for (int i = 0; i < _sgl.pip_pool.pool.size; i++) { + _sgl_pipeline_t* pip = &_sgl.pip_pool.pips[i]; + _sgl_destroy_pipeline(_sgl_make_pip_id(pip->slot.id)); + } + _sgl_discard_context_pool(); + _sgl_discard_pipeline_pool(); + _sgl_discard_common(); + _sgl.init_cookie = 0; +} + +SOKOL_API_IMPL sgl_error_t sgl_error(void) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + return ctx->error; + } else { + return SGL_ERROR_NO_CONTEXT; + } +} + +SOKOL_API_IMPL sgl_error_t sgl_context_error(sgl_context ctx_id) { + const _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id); + if (ctx) { + return ctx->error; + } else { + return SGL_ERROR_NO_CONTEXT; + } +} + +SOKOL_API_IMPL float sgl_rad(float deg) { + return (deg * (float)M_PI) / 180.0f; +} + +SOKOL_API_IMPL float sgl_deg(float rad) { + return (rad * 180.0f) / (float)M_PI; +} + +SOKOL_API_IMPL sgl_context sgl_make_context(const sgl_context_desc_t* desc) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + return _sgl_make_context(desc); +} + +SOKOL_API_IMPL void sgl_destroy_context(sgl_context ctx_id) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + if (_sgl_is_default_context(ctx_id)) { + _SGL_WARN(CANNOT_DESTROY_DEFAULT_CONTEXT); + return; + } + _sgl_destroy_context(ctx_id); + // re-validate the current context pointer (this will return a nullptr + // if we just destroyed the current context) + _sgl.cur_ctx = _sgl_lookup_context(_sgl.cur_ctx_id.id); +} + +SOKOL_API_IMPL void sgl_set_context(sgl_context ctx_id) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + if (_sgl_is_default_context(ctx_id)) { + _sgl.cur_ctx_id = _sgl.def_ctx_id; + } else { + _sgl.cur_ctx_id = ctx_id; + } + // this will return null if the handle isn't valid + _sgl.cur_ctx = _sgl_lookup_context(_sgl.cur_ctx_id.id); +} + +SOKOL_API_IMPL sgl_context sgl_get_context(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + return _sgl.cur_ctx_id; +} + +SOKOL_API_IMPL sgl_context sgl_default_context(void) { + return SGL_DEFAULT_CONTEXT; +} + +SOKOL_API_IMPL sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + return _sgl_make_pipeline(desc, &ctx->desc); + } else { + return _sgl_make_pip_id(SG_INVALID_ID); + } +} + +SOKOL_API_IMPL sgl_pipeline sgl_context_make_pipeline(sgl_context ctx_id, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + const _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id); + if (ctx) { + return _sgl_make_pipeline(desc, &ctx->desc); + } else { + return _sgl_make_pip_id(SG_INVALID_ID); + } +} + +SOKOL_API_IMPL void sgl_destroy_pipeline(sgl_pipeline pip_id) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_destroy_pipeline(pip_id); +} + +SOKOL_API_IMPL void sgl_load_pipeline(sgl_pipeline pip_id) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT((ctx->pip_tos >= 0) && (ctx->pip_tos < _SGL_MAX_STACK_DEPTH)); + ctx->pip_stack[ctx->pip_tos] = pip_id; +} + +SOKOL_API_IMPL void sgl_load_default_pipeline(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT((ctx->pip_tos >= 0) && (ctx->pip_tos < _SGL_MAX_STACK_DEPTH)); + ctx->pip_stack[ctx->pip_tos] = ctx->def_pip; +} + +SOKOL_API_IMPL void sgl_push_pipeline(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + if (ctx->pip_tos < (_SGL_MAX_STACK_DEPTH - 1)) { + ctx->pip_tos++; + ctx->pip_stack[ctx->pip_tos] = ctx->pip_stack[ctx->pip_tos-1]; + } else { + ctx->error = SGL_ERROR_STACK_OVERFLOW; + } +} + +SOKOL_API_IMPL void sgl_pop_pipeline(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + if (ctx->pip_tos > 0) { + ctx->pip_tos--; + } else { + ctx->error = SGL_ERROR_STACK_UNDERFLOW; + } +} + +SOKOL_API_IMPL void sgl_defaults(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + ctx->u = 0.0f; ctx->v = 0.0f; + ctx->rgba = 0xFFFFFFFF; + ctx->point_size = 1.0f; + ctx->texturing_enabled = false; + ctx->cur_img = _sgl.def_img; + ctx->cur_smp = _sgl.def_smp; + sgl_load_default_pipeline(); + _sgl_identity(_sgl_matrix_texture(ctx)); + _sgl_identity(_sgl_matrix_modelview(ctx)); + _sgl_identity(_sgl_matrix_projection(ctx)); + ctx->cur_matrix_mode = SGL_MATRIXMODE_MODELVIEW; + ctx->matrix_dirty = true; +} + +SOKOL_API_IMPL void sgl_layer(int layer_id) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + ctx->layer_id = layer_id; +} + +SOKOL_API_IMPL void sgl_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + _sgl_command_t* cmd = _sgl_next_command(ctx); + if (cmd) { + cmd->cmd = SGL_COMMAND_VIEWPORT; + cmd->layer_id = ctx->layer_id; + cmd->args.viewport.x = x; + cmd->args.viewport.y = y; + cmd->args.viewport.w = w; + cmd->args.viewport.h = h; + cmd->args.viewport.origin_top_left = origin_top_left; + } +} + +SOKOL_API_IMPL void sgl_viewportf(float x, float y, float w, float h, bool origin_top_left) { + sgl_viewport((int)x, (int)y, (int)w, (int)h, origin_top_left); +} + +SOKOL_API_IMPL void sgl_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + _sgl_command_t* cmd = _sgl_next_command(ctx); + if (cmd) { + cmd->cmd = SGL_COMMAND_SCISSOR_RECT; + cmd->layer_id = ctx->layer_id; + cmd->args.scissor_rect.x = x; + cmd->args.scissor_rect.y = y; + cmd->args.scissor_rect.w = w; + cmd->args.scissor_rect.h = h; + cmd->args.scissor_rect.origin_top_left = origin_top_left; + } +} + +SOKOL_API_IMPL void sgl_scissor_rectf(float x, float y, float w, float h, bool origin_top_left) { + sgl_scissor_rect((int)x, (int)y, (int)w, (int)h, origin_top_left); +} + +SOKOL_API_IMPL void sgl_enable_texture(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + ctx->texturing_enabled = true; +} + +SOKOL_API_IMPL void sgl_disable_texture(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + ctx->texturing_enabled = false; +} + +SOKOL_API_IMPL void sgl_texture(sg_image img, sg_sampler smp) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + if (SG_INVALID_ID != img.id) { + ctx->cur_img = img; + } else { + ctx->cur_img = _sgl.def_img; + } + if (SG_INVALID_ID != smp.id) { + ctx->cur_smp = smp; + } else { + ctx->cur_smp = _sgl.def_smp; + } +} + +SOKOL_API_IMPL void sgl_begin_points(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + _sgl_begin(ctx, SGL_PRIMITIVETYPE_POINTS); +} + +SOKOL_API_IMPL void sgl_begin_lines(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + _sgl_begin(ctx, SGL_PRIMITIVETYPE_LINES); +} + +SOKOL_API_IMPL void sgl_begin_line_strip(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + _sgl_begin(ctx, SGL_PRIMITIVETYPE_LINE_STRIP); +} + +SOKOL_API_IMPL void sgl_begin_triangles(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + _sgl_begin(ctx, SGL_PRIMITIVETYPE_TRIANGLES); +} + +SOKOL_API_IMPL void sgl_begin_triangle_strip(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + _sgl_begin(ctx, SGL_PRIMITIVETYPE_TRIANGLE_STRIP); +} + +SOKOL_API_IMPL void sgl_begin_quads(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(!ctx->in_begin); + _sgl_begin(ctx, SGL_PRIMITIVETYPE_QUADS); +} + +SOKOL_API_IMPL void sgl_end(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT(ctx->in_begin); + SOKOL_ASSERT(ctx->vertices.next >= ctx->base_vertex); + ctx->in_begin = false; + bool matrix_dirty = ctx->matrix_dirty; + if (matrix_dirty) { + ctx->matrix_dirty = false; + _sgl_uniform_t* uni = _sgl_next_uniform(ctx); + if (uni) { + _sgl_matmul4(&uni->mvp, _sgl_matrix_projection(ctx), _sgl_matrix_modelview(ctx)); + uni->tm = *_sgl_matrix_texture(ctx); + } + } + // check if command can be merged with current command + sg_pipeline pip = _sgl_get_pipeline(ctx->pip_stack[ctx->pip_tos], ctx->cur_prim_type); + sg_image img = ctx->texturing_enabled ? ctx->cur_img : _sgl.def_img; + sg_sampler smp = ctx->texturing_enabled ? ctx->cur_smp : _sgl.def_smp; + _sgl_command_t* cur_cmd = _sgl_cur_command(ctx); + bool merge_cmd = false; + if (cur_cmd) { + if ((cur_cmd->cmd == SGL_COMMAND_DRAW) && + (cur_cmd->layer_id == ctx->layer_id) && + (ctx->cur_prim_type != SGL_PRIMITIVETYPE_LINE_STRIP) && + (ctx->cur_prim_type != SGL_PRIMITIVETYPE_TRIANGLE_STRIP) && + !matrix_dirty && + (cur_cmd->args.draw.img.id == img.id) && + (cur_cmd->args.draw.smp.id == smp.id) && + (cur_cmd->args.draw.pip.id == pip.id)) + { + merge_cmd = true; + } + } + if (merge_cmd) { + // draw command can be merged with the previous command + cur_cmd->args.draw.num_vertices += ctx->vertices.next - ctx->base_vertex; + } else { + // append a new draw command + _sgl_command_t* cmd = _sgl_next_command(ctx); + if (cmd) { + SOKOL_ASSERT(ctx->uniforms.next > 0); + cmd->cmd = SGL_COMMAND_DRAW; + cmd->layer_id = ctx->layer_id; + cmd->args.draw.img = img; + cmd->args.draw.smp = smp; + cmd->args.draw.pip = _sgl_get_pipeline(ctx->pip_stack[ctx->pip_tos], ctx->cur_prim_type); + cmd->args.draw.base_vertex = ctx->base_vertex; + cmd->args.draw.num_vertices = ctx->vertices.next - ctx->base_vertex; + cmd->args.draw.uniform_index = ctx->uniforms.next - 1; + } + } +} + +SOKOL_API_IMPL void sgl_point_size(float s) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->point_size = s; + } +} + +SOKOL_API_IMPL void sgl_t2f(float u, float v) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->u = u; + ctx->v = v; + } +} + +SOKOL_API_IMPL void sgl_c3f(float r, float g, float b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->rgba = _sgl_pack_rgbaf(r, g, b, 1.0f); + } +} + +SOKOL_API_IMPL void sgl_c4f(float r, float g, float b, float a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->rgba = _sgl_pack_rgbaf(r, g, b, a); + } +} + +SOKOL_API_IMPL void sgl_c3b(uint8_t r, uint8_t g, uint8_t b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->rgba = _sgl_pack_rgbab(r, g, b, 255); + } +} + +SOKOL_API_IMPL void sgl_c4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->rgba = _sgl_pack_rgbab(r, g, b, a); + } +} + +SOKOL_API_IMPL void sgl_c1i(uint32_t rgba) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->rgba = rgba; + } +} + +SOKOL_API_IMPL void sgl_v2f(float x, float y) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, ctx->u, ctx->v, ctx->rgba); + } +} + +SOKOL_API_IMPL void sgl_v3f(float x, float y, float z) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, ctx->u, ctx->v, ctx->rgba); + } +} + +SOKOL_API_IMPL void sgl_v2f_t2f(float x, float y, float u, float v) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, u, v, ctx->rgba); + } +} + +SOKOL_API_IMPL void sgl_v3f_t2f(float x, float y, float z, float u, float v) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, u, v, ctx->rgba); + } +} + +SOKOL_API_IMPL void sgl_v2f_c3f(float x, float y, float r, float g, float b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, ctx->u, ctx->v, _sgl_pack_rgbaf(r, g, b, 1.0f)); + } +} + +SOKOL_API_IMPL void sgl_v2f_c3b(float x, float y, uint8_t r, uint8_t g, uint8_t b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, ctx->u, ctx->v, _sgl_pack_rgbab(r, g, b, 255)); + } +} + +SOKOL_API_IMPL void sgl_v2f_c4f(float x, float y, float r, float g, float b, float a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, ctx->u, ctx->v, _sgl_pack_rgbaf(r, g, b, a)); + } +} + +SOKOL_API_IMPL void sgl_v2f_c4b(float x, float y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, ctx->u, ctx->v, _sgl_pack_rgbab(r, g, b, a)); + } +} + +SOKOL_API_IMPL void sgl_v2f_c1i(float x, float y, uint32_t rgba) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, ctx->u, ctx->v, rgba); + } +} + +SOKOL_API_IMPL void sgl_v3f_c3f(float x, float y, float z, float r, float g, float b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, ctx->u, ctx->v, _sgl_pack_rgbaf(r, g, b, 1.0f)); + } +} + +SOKOL_API_IMPL void sgl_v3f_c3b(float x, float y, float z, uint8_t r, uint8_t g, uint8_t b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, ctx->u, ctx->v, _sgl_pack_rgbab(r, g, b, 255)); + } +} + +SOKOL_API_IMPL void sgl_v3f_c4f(float x, float y, float z, float r, float g, float b, float a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, ctx->u, ctx->v, _sgl_pack_rgbaf(r, g, b, a)); + } +} + +SOKOL_API_IMPL void sgl_v3f_c4b(float x, float y, float z, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, ctx->u, ctx->v, _sgl_pack_rgbab(r, g, b, a)); + } +} + +SOKOL_API_IMPL void sgl_v3f_c1i(float x, float y, float z, uint32_t rgba) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, ctx->u, ctx->v, rgba); + } +} + +SOKOL_API_IMPL void sgl_v2f_t2f_c3f(float x, float y, float u, float v, float r, float g, float b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, u, v, _sgl_pack_rgbaf(r, g, b, 1.0f)); + } +} + +SOKOL_API_IMPL void sgl_v2f_t2f_c3b(float x, float y, float u, float v, uint8_t r, uint8_t g, uint8_t b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, u, v, _sgl_pack_rgbab(r, g, b, 255)); + } +} + +SOKOL_API_IMPL void sgl_v2f_t2f_c4f(float x, float y, float u, float v, float r, float g, float b, float a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, u, v, _sgl_pack_rgbaf(r, g, b, a)); + } +} + +SOKOL_API_IMPL void sgl_v2f_t2f_c4b(float x, float y, float u, float v, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, u, v, _sgl_pack_rgbab(r, g, b, a)); + } +} + +SOKOL_API_IMPL void sgl_v2f_t2f_c1i(float x, float y, float u, float v, uint32_t rgba) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, 0.0f, u, v, rgba); + } +} + +SOKOL_API_IMPL void sgl_v3f_t2f_c3f(float x, float y, float z, float u, float v, float r, float g, float b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, u, v, _sgl_pack_rgbaf(r, g, b, 1.0f)); + } +} + +SOKOL_API_IMPL void sgl_v3f_t2f_c3b(float x, float y, float z, float u, float v, uint8_t r, uint8_t g, uint8_t b) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, u, v, _sgl_pack_rgbab(r, g, b, 255)); + } +} + +SOKOL_API_IMPL void sgl_v3f_t2f_c4f(float x, float y, float z, float u, float v, float r, float g, float b, float a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, u, v, _sgl_pack_rgbaf(r, g, b, a)); + } +} + +SOKOL_API_IMPL void sgl_v3f_t2f_c4b(float x, float y, float z, float u, float v, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx, x, y, z, u, v, _sgl_pack_rgbab(r, g, b, a)); + } +} + +SOKOL_API_IMPL void sgl_v3f_t2f_c1i(float x, float y, float z, float u, float v, uint32_t rgba) { + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_vtx(ctx,x, y, z, u, v, rgba); + } +} + +SOKOL_API_IMPL void sgl_matrix_mode_modelview(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->cur_matrix_mode = SGL_MATRIXMODE_MODELVIEW; + } +} + +SOKOL_API_IMPL void sgl_matrix_mode_projection(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->cur_matrix_mode = SGL_MATRIXMODE_PROJECTION; + } +} + +SOKOL_API_IMPL void sgl_matrix_mode_texture(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + ctx->cur_matrix_mode = SGL_MATRIXMODE_TEXTURE; + } +} + +SOKOL_API_IMPL void sgl_load_identity(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_identity(_sgl_matrix(ctx)); +} + +SOKOL_API_IMPL void sgl_load_matrix(const float m[16]) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + memcpy(&_sgl_matrix(ctx)->v[0][0], &m[0], 64); +} + +SOKOL_API_IMPL void sgl_load_transpose_matrix(const float m[16]) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_transpose(_sgl_matrix(ctx), (const _sgl_matrix_t*) &m[0]); +} + +SOKOL_API_IMPL void sgl_mult_matrix(const float m[16]) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + const _sgl_matrix_t* m0 = (const _sgl_matrix_t*) &m[0]; + _sgl_mul(_sgl_matrix(ctx), m0); +} + +SOKOL_API_IMPL void sgl_mult_transpose_matrix(const float m[16]) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_matrix_t m0; + _sgl_transpose(&m0, (const _sgl_matrix_t*) &m[0]); + _sgl_mul(_sgl_matrix(ctx), &m0); +} + +SOKOL_API_IMPL void sgl_rotate(float angle_rad, float x, float y, float z) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_rotate(_sgl_matrix(ctx), angle_rad, x, y, z); +} + +SOKOL_API_IMPL void sgl_scale(float x, float y, float z) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_scale(_sgl_matrix(ctx), x, y, z); +} + +SOKOL_API_IMPL void sgl_translate(float x, float y, float z) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_translate(_sgl_matrix(ctx), x, y, z); +} + +SOKOL_API_IMPL void sgl_frustum(float l, float r, float b, float t, float n, float f) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_frustum(_sgl_matrix(ctx), l, r, b, t, n, f); +} + +SOKOL_API_IMPL void sgl_ortho(float l, float r, float b, float t, float n, float f) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_ortho(_sgl_matrix(ctx), l, r, b, t, n, f); +} + +SOKOL_API_IMPL void sgl_perspective(float fov_y, float aspect, float z_near, float z_far) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_perspective(_sgl_matrix(ctx), fov_y, aspect, z_near, z_far); +} + +SOKOL_API_IMPL void sgl_lookat(float eye_x, float eye_y, float eye_z, float center_x, float center_y, float center_z, float up_x, float up_y, float up_z) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + ctx->matrix_dirty = true; + _sgl_lookat(_sgl_matrix(ctx), eye_x, eye_y, eye_z, center_x, center_y, center_z, up_x, up_y, up_z); +} + +SOKOL_GL_API_DECL void sgl_push_matrix(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT((ctx->cur_matrix_mode >= 0) && (ctx->cur_matrix_mode < SGL_NUM_MATRIXMODES)); + ctx->matrix_dirty = true; + if (ctx->matrix_tos[ctx->cur_matrix_mode] < (_SGL_MAX_STACK_DEPTH - 1)) { + const _sgl_matrix_t* src = _sgl_matrix(ctx); + ctx->matrix_tos[ctx->cur_matrix_mode]++; + _sgl_matrix_t* dst = _sgl_matrix(ctx); + *dst = *src; + } else { + ctx->error = SGL_ERROR_STACK_OVERFLOW; + } +} + +SOKOL_GL_API_DECL void sgl_pop_matrix(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (!ctx) { + return; + } + SOKOL_ASSERT((ctx->cur_matrix_mode >= 0) && (ctx->cur_matrix_mode < SGL_NUM_MATRIXMODES)); + ctx->matrix_dirty = true; + if (ctx->matrix_tos[ctx->cur_matrix_mode] > 0) { + ctx->matrix_tos[ctx->cur_matrix_mode]--; + } else { + ctx->error = SGL_ERROR_STACK_UNDERFLOW; + } +} + +SOKOL_API_IMPL void sgl_draw(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_draw(ctx, 0); + } +} + +SOKOL_API_IMPL void sgl_draw_layer(int layer_id) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl.cur_ctx; + if (ctx) { + _sgl_draw(ctx, layer_id); + } +} + +SOKOL_API_IMPL void sgl_context_draw(sgl_context ctx_id) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id); + if (ctx) { + _sgl_draw(ctx, 0); + } +} + +SOKOL_API_IMPL void sgl_context_draw_layer(sgl_context ctx_id, int layer_id) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id); + if (ctx) { + _sgl_draw(ctx, layer_id); + } +} + +#endif /* SOKOL_GL_IMPL */ diff --git a/thirdparty/sokol/util/sokol_imgui.h b/thirdparty/sokol/util/sokol_imgui.h new file mode 100644 index 0000000..0362a9a --- /dev/null +++ b/thirdparty/sokol/util/sokol_imgui.h @@ -0,0 +1,3150 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_IMGUI_IMPL) +#define SOKOL_IMGUI_IMPL +#endif +#ifndef SOKOL_IMGUI_INCLUDED +/* + sokol_imgui.h -- drop-in Dear ImGui renderer/event-handler for sokol_gfx.h + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_IMGUI_IMPL + + before you include this file in *one* C or C++ file to create the + implementation. + + NOTE that the implementation can be compiled either as C++ or as C. + When compiled as C++, sokol_imgui.h will directly call into the + Dear ImGui C++ API. When compiled as C, sokol_imgui.h will call + cimgui.h functions instead. + + NOTE that the formerly separate header sokol_cimgui.h has been + merged into sokol_imgui.h + + The following defines are used by the implementation to select the + platform-specific embedded shader code (these are the same defines as + used by sokol_gfx.h and sokol_app.h): + + SOKOL_GLCORE + SOKOL_GLES3 + SOKOL_D3D11 + SOKOL_METAL + SOKOL_WGPU + + Optionally provide the following configuration define both before including the + the declaration and implementation: + + SOKOL_IMGUI_NO_SOKOL_APP - don't depend on sokol_app.h (see below for details) + + Optionally provide the following macros before including the implementation + to override defaults: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_IMGUI_API_DECL- public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_IMGUI_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + If sokol_imgui.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_IMGUI_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + Include the following headers before sokol_imgui.h (both before including + the declaration and implementation): + + sokol_gfx.h + sokol_app.h (except SOKOL_IMGUI_NO_SOKOL_APP) + + Additionally, include the following headers before including the + implementation: + + If the implementation is compiled as C++: + imgui.h + + If the implementation is compiled as C: + cimgui.h + + + FEATURE OVERVIEW: + ================= + sokol_imgui.h implements the initialization, rendering and event-handling + code for Dear ImGui (https://github.com/ocornut/imgui) on top of + sokol_gfx.h and (optionally) sokol_app.h. + + The sokol_app.h dependency is optional and used for input event handling. + If you only use sokol_gfx.h but not sokol_app.h in your application, + define SOKOL_IMGUI_NO_SOKOL_APP before including the implementation + of sokol_imgui.h, this will remove any dependency to sokol_app.h, but + you must feed input events into Dear ImGui yourself. + + sokol_imgui.h is not thread-safe, all calls must be made from the + same thread where sokol_gfx.h is running. + + HOWTO: + ====== + + --- To initialize sokol-imgui, call: + + simgui_setup(const simgui_desc_t* desc) + + This will initialize Dear ImGui and create sokol-gfx resources + (two buffers for vertices and indices, a font texture and a pipeline- + state-object). + + Use the following simgui_desc_t members to configure behaviour: + + int max_vertices + The maximum number of vertices used for UI rendering, default is 65536. + sokol-imgui will use this to compute the size of the vertex- + and index-buffers allocated via sokol_gfx.h + + int image_pool_size + Number of simgui_image_t objects which can be alive at the same time. + The default is 256. + + sg_pixel_format color_format + The color pixel format of the render pass where the UI + will be rendered. The default (0) matches sokoL_gfx.h's + default pass. + + sg_pixel_format depth_format + The depth-buffer pixel format of the render pass where + the UI will be rendered. The default (0) matches + sokol_gfx.h's default pass depth format. + + int sample_count + The MSAA sample-count of the render pass where the UI + will be rendered. The default (0) matches sokol_gfx.h's + default pass sample count. + + const char* ini_filename + Sets this path as ImGui::GetIO().IniFilename where ImGui will store + and load UI persistency data. By default this is 0, so that Dear ImGui + will not preserve state between sessions (and also won't do + any filesystem calls). Also see the ImGui functions: + - LoadIniSettingsFromMemory() + - SaveIniSettingsFromMemory() + These functions give you explicit control over loading and saving + UI state while using your own filesystem wrapper functions (in this + case keep simgui_desc.ini_filename zero) + + bool no_default_font + Set this to true if you don't want to use ImGui's default + font. In this case you need to initialize the font + yourself after simgui_setup() is called. + + bool disable_paste_override + If set to true, sokol_imgui.h will not 'emulate' a Dear Imgui + clipboard paste action on SAPP_EVENTTYPE_CLIPBOARD_PASTED event. + This is mainly a hack/workaround to allow external workarounds + for making copy/paste work on the web platform. In general, + copy/paste support isn't properly fleshed out in sokol_imgui.h yet. + + bool disable_set_mouse_cursor + If true, sokol_imgui.h will not control the mouse cursor type + by calling sapp_set_mouse_cursor(). + + bool disable_windows_resize_from_edges + If true, windows can only be resized from the bottom right corner. + The default is false, meaning windows can be resized from edges. + + bool write_alpha_channel + Set this to true if you want alpha values written to the + framebuffer. By default this behavior is disabled to prevent + undesired behavior on platforms like the web where the canvas is + always alpha-blended with the background. + + simgui_allocator_t allocator + Used to override memory allocation functions. See further below + for details. + + simgui_logger_t logger + A user-provided logging callback. Note that without logging + callback, sokol-imgui will be completely silent! + See the section about ERROR REPORTING AND LOGGING below + for more details. + + --- At the start of a frame, call: + + simgui_new_frame(&(simgui_frame_desc_t){ + .width = ..., + .height = ..., + .delta_time = ..., + .dpi_scale = ... + }); + + 'width' and 'height' are the dimensions of the rendering surface, + passed to ImGui::GetIO().DisplaySize. + + 'delta_time' is the frame duration passed to ImGui::GetIO().DeltaTime. + + 'dpi_scale' is the current DPI scale factor, if this is left zero-initialized, + 1.0f will be used instead. Typical values for dpi_scale are >= 1.0f. + + For example, if you're using sokol_app.h and render to the default framebuffer: + + simgui_new_frame(&(simgui_frame_desc_t){ + .width = sapp_width(), + .height = sapp_height(), + .delta_time = sapp_frame_duration(), + .dpi_scale = sapp_dpi_scale() + }); + + --- at the end of the frame, before the sg_end_pass() where you + want to render the UI, call: + + simgui_render() + + This will first call ImGui::Render(), and then render ImGui's draw list + through sokol_gfx.h + + --- if you're using sokol_app.h, from inside the sokol_app.h event callback, + call: + + bool simgui_handle_event(const sapp_event* ev); + + The return value is the value of ImGui::GetIO().WantCaptureKeyboard, + if this is true, you might want to skip keyboard input handling + in your own event handler. + + If you want to use the ImGui functions for checking if a key is pressed + (e.g. ImGui::IsKeyPressed()) the following helper function to map + an sapp_keycode to an ImGuiKey value may be useful: + + int simgui_map_keycode(sapp_keycode c); + + Note that simgui_map_keycode() can be called outside simgui_setup()/simgui_shutdown(). + + --- finally, on application shutdown, call + + simgui_shutdown() + + + ON USER-PROVIDED IMAGES AND SAMPLERS + ==================================== + To render your own images via ImGui::Image(), first create an simgui_image_t + object from a sokol-gfx image and sampler object. + + // create a sokol-imgui image object which associates an sg_image with an sg_sampler + simgui_image_t simgui_img = simgui_make_image(&(simgui_image_desc_t){ + .image = sg_make_image(...), + .sampler = sg_make_sampler(...), + }); + + // convert the returned image handle into a ImTextureID handle + ImTextureID tex_id = simgui_imtextureid(simgui_img); + + // use the ImTextureID handle in Dear ImGui calls: + ImGui::Image(tex_id, ...); + + simgui_image_t objects are small and cheap (literally just the image and sampler + handle). + + You can omit the sampler handle in the simgui_make_image() call, in this case a + default sampler will be used with nearest-filtering and clamp-to-edge. + + Trying to render with an invalid simgui_image_t handle will render a small 8x8 + white default texture instead. + + To destroy a sokol-imgui image object, call + + simgui_destroy_image(simgui_img); + + But please be aware that the image object needs to be around until simgui_render() is called + in a frame (if this turns out to be too much of a hassle we could introduce some sort + of garbage collection where destroyed simgui_image_t objects are kept around until + the simgui_render() call). + + You can call: + + simgui_image_desc_t desc = simgui_query_image_desc(img) + + ...to get the original desc struct, useful if you need to get the sokol-gfx image + and sampler handle of the simgui_image_t object. + + You can convert an ImTextureID back into an simgui_image_t handle: + + simgui_image_t img = simgui_image_from_imtextureid(tex_id); + + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + simgui_setup(&(simgui_desc_t){ + // ... + .allocator = { + .alloc_fn = my_alloc, + .free_fn = my_free, + .user_data = ...; + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_imgui.h + itself though, not any allocations in Dear ImGui. + + + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + simgui_setup(&(simgui_desc_t){ + .logger.func = slog_func + }); + + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'simgui' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SIMGUI_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_imgui.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-imgui like this: + + simgui_setup(&(simgui_desc_t){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); + + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. + + + IMGUI EVENT HANDLING + ==================== + You can call these functions from your platform's events to handle ImGui events + when SOKOL_IMGUI_NO_SOKOL_APP is defined. + + E.g. mouse position events can be dispatched like this: + + simgui_add_mouse_pos_event(100, 200); + + For adding key events, you're responsible to map your own key codes to ImGuiKey + values and pass those as int: + + simgui_add_key_event(imgui_key, true); + + Take note that modifiers (shift, ctrl, etc.) must be updated manually. + + If sokol_app is being used, ImGui events are handled for you. + + + LICENSE + ======= + + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_IMGUI_INCLUDED (1) +#include +#include +#include // size_t + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_imgui.h" +#endif +#if !defined(SOKOL_IMGUI_NO_SOKOL_APP) && !defined(SOKOL_APP_INCLUDED) +#error "Please include sokol_app.h before sokol_imgui.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_IMGUI_API_DECL) +#define SOKOL_IMGUI_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_IMGUI_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_IMGUI_IMPL) +#define SOKOL_IMGUI_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_IMGUI_API_DECL __declspec(dllimport) +#else +#define SOKOL_IMGUI_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + SIMGUI_INVALID_ID = 0, +}; + +/* + simgui_image_t + + A combined image-sampler pair used to inject custom images and samplers into Dear ImGui. + + Create with simgui_make_image(), and convert to an ImTextureID handle via + simgui_imtextureid(). +*/ +typedef struct simgui_image_t { uint32_t id; } simgui_image_t; + +/* + simgui_image_desc_t + + Descriptor struct for simgui_make_image(). You must provide + at least an sg_image handle. Keeping the sg_sampler handle + zero-initialized will select the builtin default sampler + which uses linear filtering. +*/ +typedef struct simgui_image_desc_t { + sg_image image; + sg_sampler sampler; +} simgui_image_desc_t; + +/* + simgui_log_item + + An enum with a unique item for each log message, warning, error + and validation layer message. +*/ +#define _SIMGUI_LOG_ITEMS \ + _SIMGUI_LOGITEM_XMACRO(OK, "Ok") \ + _SIMGUI_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SIMGUI_LOGITEM_XMACRO(IMAGE_POOL_EXHAUSTED, "image pool exhausted") \ + +#define _SIMGUI_LOGITEM_XMACRO(item,msg) SIMGUI_LOGITEM_##item, +typedef enum simgui_log_item_t { + _SIMGUI_LOG_ITEMS +} simgui_log_item_t; +#undef _SIMGUI_LOGITEM_XMACRO + +/* + simgui_allocator_t + + Used in simgui_desc_t to provide custom memory-alloc and -free functions + to sokol_imgui.h. If memory management should be overridden, both the + alloc_fn and free_fn function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct simgui_allocator_t { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} simgui_allocator_t; + +/* + simgui_logger + + Used in simgui_desc_t to provide a logging function. Please be aware + that without logging function, sokol-imgui will be completely + silent, e.g. it will not report errors, warnings and + validation layer messages. For maximum error verbosity, + compile in debug mode (e.g. NDEBUG *not* defined) and install + a logger (for instance the standard logging function from sokol_log.h). +*/ +typedef struct simgui_logger_t { + void (*func)( + const char* tag, // always "simgui" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SIMGUI_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_imgui.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} simgui_logger_t; + +typedef struct simgui_desc_t { + int max_vertices; // default: 65536 + int image_pool_size; // default: 256 + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; + const char* ini_filename; + bool no_default_font; + bool disable_paste_override; // if true, don't send Ctrl-V on EVENTTYPE_CLIPBOARD_PASTED + bool disable_set_mouse_cursor; // if true, don't control the mouse cursor type via sapp_set_mouse_cursor() + bool disable_windows_resize_from_edges; // if true, only resize edges from the bottom right corner + bool write_alpha_channel; // if true, alpha values get written into the framebuffer + simgui_allocator_t allocator; // optional memory allocation overrides (default: malloc/free) + simgui_logger_t logger; // optional log function override +} simgui_desc_t; + +typedef struct simgui_frame_desc_t { + int width; + int height; + double delta_time; + float dpi_scale; +} simgui_frame_desc_t; + +typedef struct simgui_font_tex_desc_t { + sg_filter min_filter; + sg_filter mag_filter; +} simgui_font_tex_desc_t; + +SOKOL_IMGUI_API_DECL void simgui_setup(const simgui_desc_t* desc); +SOKOL_IMGUI_API_DECL void simgui_new_frame(const simgui_frame_desc_t* desc); +SOKOL_IMGUI_API_DECL void simgui_render(void); +SOKOL_IMGUI_API_DECL simgui_image_t simgui_make_image(const simgui_image_desc_t* desc); +SOKOL_IMGUI_API_DECL void simgui_destroy_image(simgui_image_t img); +SOKOL_IMGUI_API_DECL simgui_image_desc_t simgui_query_image_desc(simgui_image_t img); +SOKOL_IMGUI_API_DECL void* simgui_imtextureid(simgui_image_t img); +SOKOL_IMGUI_API_DECL simgui_image_t simgui_image_from_imtextureid(void* im_texture_id); +SOKOL_IMGUI_API_DECL void simgui_add_focus_event(bool focus); +SOKOL_IMGUI_API_DECL void simgui_add_mouse_pos_event(float x, float y); +SOKOL_IMGUI_API_DECL void simgui_add_touch_pos_event(float x, float y); +SOKOL_IMGUI_API_DECL void simgui_add_mouse_button_event(int mouse_button, bool down); +SOKOL_IMGUI_API_DECL void simgui_add_mouse_wheel_event(float wheel_x, float wheel_y); +SOKOL_IMGUI_API_DECL void simgui_add_key_event(int imgui_key, bool down); +SOKOL_IMGUI_API_DECL void simgui_add_input_character(uint32_t c); +SOKOL_IMGUI_API_DECL void simgui_add_input_characters_utf8(const char* c); +SOKOL_IMGUI_API_DECL void simgui_add_touch_button_event(int mouse_button, bool down); +#if !defined(SOKOL_IMGUI_NO_SOKOL_APP) +SOKOL_IMGUI_API_DECL bool simgui_handle_event(const sapp_event* ev); +SOKOL_IMGUI_API_DECL int simgui_map_keycode(sapp_keycode keycode); // returns ImGuiKey_* +#endif +SOKOL_IMGUI_API_DECL void simgui_shutdown(void); +SOKOL_IMGUI_API_DECL void simgui_create_fonts_texture(const simgui_font_tex_desc_t* desc); +SOKOL_IMGUI_API_DECL void simgui_destroy_fonts_texture(void); + +#ifdef __cplusplus +} // extern "C" + +// reference-based equivalents for C++ +inline void simgui_setup(const simgui_desc_t& desc) { return simgui_setup(&desc); } +inline simgui_image_t simgui_make_image(const simgui_image_desc_t& desc) { return simgui_make_image(&desc); } +inline void simgui_new_frame(const simgui_frame_desc_t& desc) { return simgui_new_frame(&desc); } +inline void simgui_create_fonts_texture(const simgui_font_tex_desc_t& desc) { return simgui_create_fonts_texture(&desc); } + +#endif +#endif /* SOKOL_IMGUI_INCLUDED */ + +//-- IMPLEMENTATION ------------------------------------------------------------ +#ifdef SOKOL_IMGUI_IMPL +#define SOKOL_IMGUI_IMPL_INCLUDED (1) + +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use simgui_desc_t.allocator to override memory allocation functions" +#endif + +#if defined(__cplusplus) + #if !defined(IMGUI_VERSION) + #error "Please include imgui.h before the sokol_imgui.h implementation" + #endif +#else + #if !defined(CIMGUI_INCLUDED) + #error "Please include cimgui.h before the sokol_imgui.h implementation" + #endif +#endif + +#include // memset +#include // malloc/free + +#if defined(__EMSCRIPTEN__) && !defined(SOKOL_DUMMY_BACKEND) +#include +#endif + +#ifndef SOKOL_API_IMPL +#define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif + +#define _SIMGUI_INIT_COOKIE (0xBABEBABE) +#define _SIMGUI_INVALID_SLOT_INDEX (0) +#define _SIMGUI_SLOT_SHIFT (16) +#define _SIMGUI_MAX_POOL_SIZE (1<<_SIMGUI_SLOT_SHIFT) +#define _SIMGUI_SLOT_MASK (_SIMGUI_MAX_POOL_SIZE-1) + +// helper macros and constants +#define _simgui_def(val, def) (((val) == 0) ? (def) : (val)) + +// workaround for missing ImDrawCallback_ResetRenderState in cimgui.h +// see: https://github.com/cimgui/cimgui/issues/261 +#ifndef ImDrawCallback_ResetRenderState +#define ImDrawCallback_ResetRenderState (ImDrawCallback)(-8) +#endif + +typedef struct { + ImVec2 disp_size; + uint8_t _pad_8[8]; +} _simgui_vs_params_t; + +typedef enum { + _SIMGUI_RESOURCESTATE_INITIAL, + _SIMGUI_RESOURCESTATE_ALLOC, + _SIMGUI_RESOURCESTATE_VALID, + _SIMGUI_RESOURCESTATE_FAILED, + _SIMGUI_RESOURCESTATE_INVALID, + _SIMGUI_RESOURCESTATE_FORCE_U32 = 0x7FFFFFFF +} _simgui_resource_state; + +typedef struct { + uint32_t id; + _simgui_resource_state state; +} _simgui_slot_t; + +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _simgui_pool_t; + +typedef struct { + _simgui_slot_t slot; + sg_image image; + sg_sampler sampler; + sg_pipeline pip; // this will either be _simgui.def_pip or _simgui.pip_unfilterable +} _simgui_image_t; + +typedef struct { + _simgui_pool_t pool; + _simgui_image_t* items; +} _simgui_image_pool_t; + +typedef struct { + uint32_t init_cookie; + simgui_desc_t desc; + float cur_dpi_scale; + sg_buffer vbuf; + sg_buffer ibuf; + sg_image font_img; + sg_sampler font_smp; + simgui_image_t default_font; + sg_image def_img; // used as default image for user images + sg_sampler def_smp; // used as default sampler for user images + sg_shader def_shd; + sg_pipeline def_pip; + // separate shader and pipeline for unfilterable user images + sg_shader shd_unfilterable; + sg_pipeline pip_unfilterable; + sg_range vertices; + sg_range indices; + bool is_osx; + _simgui_image_pool_t image_pool; +} _simgui_state_t; +static _simgui_state_t _simgui; + +/* + Embedded source code compiled with: + + sokol-shdc -i simgui.glsl -o simgui.h -l glsl410:glsl300es:hlsl4:metal_macos:metal_ios:metal_sim:wgpu -b + + (not that for Metal and D3D11 byte code, sokol-shdc must be run + on macOS and Windows) + + @vs vs + uniform vs_params { + vec2 disp_size; + }; + in vec2 position; + in vec2 texcoord0; + in vec4 color0; + out vec2 uv; + out vec4 color; + void main() { + gl_Position = vec4(((position/disp_size)-0.5)*vec2(2.0,-2.0), 0.5, 1.0); + uv = texcoord0; + color = color0; + } + @end + + @fs fs + uniform texture2D tex; + uniform sampler smp; + in vec2 uv; + in vec4 color; + out vec4 frag_color; + void main() { + frag_color = texture(sampler2D(tex, smp), uv) * color; + } + @end + + @program simgui vs fs +*/ +#if defined(SOKOL_GLCORE) +static const uint8_t _simgui_vs_source_glsl410[383] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e, + 0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a, + 0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20, + 0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,0x20,0x75,0x76, + 0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f, + 0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x74, + 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x6f, + 0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x6c, + 0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d, + 0x20,0x32,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29, + 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x28,0x28,0x28,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x20,0x2f,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73, + 0x5b,0x30,0x5d,0x2e,0x78,0x79,0x29,0x20,0x2d,0x20,0x76,0x65,0x63,0x32,0x28,0x30, + 0x2e,0x35,0x29,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x32,0x28,0x32,0x2e,0x30,0x2c, + 0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c,0x20,0x30,0x2e,0x35,0x2c,0x20,0x31,0x2e,0x30, + 0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x74,0x65,0x78,0x63, + 0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const uint8_t _simgui_fs_source_glsl410[219] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f, + 0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74, + 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32, + 0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x29,0x20,0x2a,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_GLES3) +static const uint8_t _simgui_vs_source_glsl300es[344] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f, + 0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29, + 0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,0x20,0x75,0x76,0x3b,0x0a, + 0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20, + 0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x74,0x65,0x78, + 0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c, + 0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69,0x6e,0x20, + 0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f, + 0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20, + 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x76,0x65, + 0x63,0x34,0x28,0x28,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x2f,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78,0x79,0x29, + 0x20,0x2d,0x20,0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x35,0x29,0x29,0x20,0x2a,0x20, + 0x76,0x65,0x63,0x32,0x28,0x32,0x2e,0x30,0x2c,0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c, + 0x20,0x30,0x2e,0x35,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x75,0x76,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const uint8_t _simgui_fs_source_glsl300es[250] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d, + 0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69, + 0x6f,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a, + 0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x68,0x69,0x67,0x68,0x70,0x20, + 0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x75, + 0x76,0x3b,0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61, + 0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x29,0x20,0x2a,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_METAL) +static const uint8_t _simgui_vs_bytecode_metal_macos[3052] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xec,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe0,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x7b,0x12,0x23,0x17,0xd9,0x25,0x1c, + 0x1b,0x42,0x42,0x9f,0xbf,0x31,0xd2,0x2c,0x3a,0x55,0x22,0x1d,0x40,0xd8,0xc4,0xf8, + 0x20,0x49,0x60,0x6d,0x3c,0xea,0x4e,0x1c,0x34,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x37,0x00,0x00,0x00,0x56,0x41,0x54, + 0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x80, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03,0x00,0x04,0x04,0x06, + 0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b, + 0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xc8,0x0a,0x00,0x00,0xff,0xff,0xff,0xff, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xaf,0x02,0x00,0x00,0x0b,0x82,0x20,0x00, + 0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49, + 0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62, + 0x80,0x10,0x45,0x02,0x42,0x92,0x0b,0x42,0x84,0x10,0x32,0x14,0x38,0x08,0x18,0x49, + 0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72, + 0x24,0x07,0xc8,0x08,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00, + 0x51,0x18,0x00,0x00,0x81,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8,0xff,0xff,0xff,0xff, + 0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87,0x71,0xa0,0x07,0x76, + 0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72,0x20,0x87,0x36,0x20, + 0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88,0x07,0x79,0xa0,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0xc0,0x1c,0xc2,0x81, + 0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c, + 0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c,0xd8,0x81,0x1d,0xe6, + 0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87,0x72,0x98,0x87,0x79, + 0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72,0x68,0x03,0x73,0x80, + 0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xdc, + 0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda,0x00,0x1e,0xde,0x21, + 0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21,0x1d,0xda,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30,0x07,0x79,0x08,0x87, + 0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87,0x72,0x90,0x87,0x36, + 0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77,0x68,0x03,0x76,0x28, + 0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70,0x07,0x73,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1,0x1d,0xe0,0xa1,0x0d, + 0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87, + 0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87,0x79,0x68,0x03,0x73, + 0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d,0xca,0x81,0x1c,0xda, + 0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21, + 0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x70,0x87,0x77,0x68, + 0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38,0x87,0x36,0x68,0x87, + 0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0x62,0x1e,0xe8,0x21, + 0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1,0x1c,0xc6,0x81,0x1e, + 0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x0d,0xe6, + 0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87,0x79,0x08,0x07,0x73, + 0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e, + 0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d,0xe6,0xe1,0x1d,0xcc, + 0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00,0x73,0x08,0x07,0x76, + 0x98,0x87,0x72,0x00,0x36,0x18,0x02,0x01,0x2c,0x40,0x05,0x00,0x49,0x18,0x00,0x00, + 0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00,0x16,0x00,0x00,0x00, + 0x32,0x22,0x08,0x09,0x20,0x64,0x85,0x04,0x13,0x22,0xa4,0x84,0x04,0x13,0x22,0xe3, + 0x84,0xa1,0x90,0x14,0x12,0x4c,0x88,0x8c,0x0b,0x84,0x84,0x4c,0x10,0x3c,0x33,0x00, + 0xc3,0x08,0x02,0x30,0x8c,0x40,0x00,0x76,0x08,0x91,0x83,0xa4,0x29,0xa2,0x84,0xc9, + 0xaf,0xa4,0xff,0x01,0x22,0x80,0x91,0x50,0x10,0x83,0x08,0x84,0x50,0x8a,0x89,0x90, + 0x22,0x1b,0x08,0x98,0x23,0x00,0x83,0x14,0xc8,0x39,0x02,0x50,0x18,0x44,0x08,0x84, + 0x61,0x04,0x22,0x19,0x01,0x00,0x00,0x00,0x13,0xb2,0x70,0x48,0x07,0x79,0xb0,0x03, + 0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x76,0x08,0x87,0x71, + 0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83,0x38,0x70,0x03,0x38,0xd8, + 0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06, + 0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a, + 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e,0x73,0x20,0x07,0x7a,0x30, + 0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72, + 0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07, + 0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73, + 0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20, + 0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07, + 0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d, + 0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80, + 0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07, + 0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20,0x07,0x7a,0x20,0x07,0x75, + 0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x72,0x50,0x07,0x76,0xa0, + 0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x50,0x07, + 0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x6d, + 0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xa0, + 0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07, + 0x7a,0x30,0x07,0x72,0x30,0x84,0x39,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0xc8, + 0x02,0x01,0x00,0x00,0x09,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90, + 0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0xca,0x12,0x18,0x01,0x28,0x88,0x22,0x28,0x84, + 0x32,0xa0,0x1d,0x01,0x20,0x1d,0x4b,0x68,0x02,0x00,0x00,0x00,0x79,0x18,0x00,0x00, + 0xea,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9, + 0xb9,0xb4,0x37,0xb7,0x21,0x46,0x42,0x20,0x80,0x82,0x50,0xb9,0x1b,0x43,0x0b,0x93, + 0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x24,0x01,0x22,0x24,0x05,0xe7,0x20,0x08,0x0e, + 0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd, + 0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e, + 0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45,0x26, + 0x65,0x88,0x80,0x10,0x43,0x8c,0x24,0x48,0x86,0x44,0x60,0xd1,0x54,0x46,0x17,0xc6, + 0x36,0x04,0x41,0x8e,0x24,0x48,0x82,0x44,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6, + 0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36, + 0x17,0x26,0xc6,0x56,0x36,0x44,0x40,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d, + 0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65, + 0x6e,0x61,0x62,0x6c,0x65,0x43,0x04,0x64,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd, + 0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95, + 0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d, + 0x11,0x90,0x86,0x51,0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0xd7,0x5b, + 0x1d,0x1d,0x5c,0x1d,0x1d,0x97,0xba,0xb9,0x32,0x39,0x14,0xb6,0xb7,0x31,0x37,0x98, + 0x14,0x46,0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x6e, + 0x61,0x6d,0x65,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0xc8,0x84,0xa5,0xc9,0xb9,0x84, + 0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95,0x51,0xa8,0xb3,0x1b,0xc2,0x20,0x0f,0x02,0x21, + 0x11,0x22,0x21,0x13,0x42,0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b, + 0x49,0xa1,0x61,0xc6,0xf6,0x16,0x46,0x47,0xc3,0x62,0xec,0x8d,0xed,0x4d,0x6e,0x08, + 0x83,0x3c,0x88,0x85,0x44,0xc8,0x85,0x4c,0x08,0x46,0x26,0x2c,0x4d,0xce,0x05,0xee, + 0x6d,0x2e,0x8d,0x2e,0xed,0xcd,0x8d,0xcb,0x19,0xdb,0x17,0xd4,0xdb,0x5c,0x1a,0x5d, + 0xda,0x9b,0xdb,0x10,0x05,0xd1,0x90,0x08,0xb9,0x90,0x09,0xd9,0x86,0x18,0x48,0x85, + 0x64,0x08,0x47,0x28,0x2c,0x4d,0xce,0xc5,0xae,0x4c,0x8e,0xae,0x0c,0xef,0x2b,0xcd, + 0x0d,0xae,0x8e,0x8e,0x52,0x58,0x9a,0x9c,0x0b,0xdb,0xdb,0x58,0x18,0x5d,0xda,0x9b, + 0xdb,0x57,0x9a,0x1b,0x59,0x19,0x1e,0xbd,0xb3,0x32,0xb7,0x32,0xb9,0x30,0xba,0x32, + 0x32,0x94,0xaf,0xaf,0xb0,0x34,0xb9,0x2f,0x38,0xb6,0xb0,0xb1,0x32,0xb4,0x37,0x36, + 0xb2,0x32,0xb9,0xaf,0xaf,0x14,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x43, + 0xa8,0x44,0x40,0x3c,0xe4,0x4b,0x84,0x24,0x40,0xc0,0x00,0x89,0x10,0x09,0x99,0x90, + 0x30,0x60,0x42,0x57,0x86,0x37,0xf6,0xf6,0x26,0x47,0x06,0x33,0x84,0x4a,0x02,0xc4, + 0x43,0xbe,0x24,0x48,0x02,0x04,0x0c,0x90,0x08,0x91,0x90,0x09,0x19,0x03,0x1a,0x63, + 0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8,0x84,0x40,0x3c,0xe4,0x4b,0x88,0x24,0x40,0xc0, + 0x00,0x89,0x90,0x0b,0x99,0x90,0x32,0xa0,0x12,0x96,0x26,0xe7,0x22,0x56,0x67,0x66, + 0x56,0x26,0xc7,0x27,0x2c,0x4d,0xce,0x45,0xac,0xce,0xcc,0xac,0x4c,0xee,0x6b,0x2e, + 0x4d,0xaf,0x8c,0x48,0x58,0x9a,0x9c,0x8b,0x5c,0x59,0x18,0x19,0xa9,0xb0,0x34,0x39, + 0x97,0x39,0x3a,0xb9,0xba,0x31,0xba,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x34,0x37,0xb3, + 0x37,0x26,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x43,0x94,0x44,0x48,0x86, + 0x44,0x40,0x24,0x64,0x0d,0x18,0x85,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xd1,0xe5, + 0xc1,0x95,0x7d,0xcd,0xa5,0xe9,0x95,0xf1,0x0a,0x4b,0x93,0x73,0x09,0x93,0x3b,0xfb, + 0xa2,0xcb,0x83,0x2b,0xfb,0x0a,0x63,0x4b,0x3b,0x73,0xfb,0x9a,0x4b,0xd3,0x2b,0x63, + 0x62,0x37,0xf7,0x05,0x17,0x26,0x17,0xd6,0x36,0xc7,0xe1,0x4b,0x46,0x66,0x08,0x19, + 0x24,0x06,0x72,0x06,0x08,0x1a,0x24,0x03,0xf2,0x25,0x42,0x12,0x20,0x69,0x80,0xa8, + 0x01,0xc2,0x06,0x48,0x1b,0x24,0x03,0xe2,0x06,0xc9,0x80,0x44,0xc8,0x1b,0x20,0x13, + 0x02,0x07,0x43,0x10,0x44,0x0c,0x10,0x32,0x40,0xcc,0x00,0x89,0x83,0x21,0xc6,0x01, + 0x20,0x1d,0x22,0x07,0x7c,0xde,0xda,0xdc,0xd2,0xe0,0xde,0xe8,0xca,0xdc,0xe8,0x40, + 0xc6,0xd0,0xc2,0xe4,0xf8,0x4c,0xa5,0xb5,0xc1,0xb1,0x95,0x81,0x0c,0xad,0xac,0x80, + 0x50,0x09,0x05,0x05,0x0d,0x11,0x90,0x3a,0x18,0x62,0x20,0x74,0x80,0xd8,0xc1,0x72, + 0x0c,0x31,0x90,0x3b,0x40,0xee,0x60,0x39,0x46,0x44,0xec,0xc0,0x0e,0xf6,0xd0,0x0e, + 0x6e,0xd0,0x0e,0xef,0x40,0x0e,0xf5,0xc0,0x0e,0xe5,0xe0,0x06,0xe6,0xc0,0x0e,0xe1, + 0x70,0x0e,0xf3,0x30,0x45,0x08,0x86,0x11,0x0a,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b, + 0xa4,0x03,0x39,0x94,0x83,0x3b,0xd0,0xc3,0x94,0xa0,0x18,0xb1,0x84,0x43,0x3a,0xc8, + 0x83,0x1b,0xd8,0x43,0x39,0xc8,0xc3,0x3c,0xa4,0xc3,0x3b,0xb8,0xc3,0x94,0xc0,0x18, + 0x41,0x85,0x43,0x3a,0xc8,0x83,0x1b,0xb0,0x43,0x38,0xb8,0xc3,0x39,0xd4,0x43,0x38, + 0x9c,0x43,0x39,0xfc,0x82,0x3d,0x94,0x83,0x3c,0xcc,0x43,0x3a,0xbc,0x83,0x3b,0x4c, + 0x09,0x90,0x11,0x53,0x38,0xa4,0x83,0x3c,0xb8,0xc1,0x38,0xbc,0x43,0x3b,0xc0,0x43, + 0x3a,0xb0,0x43,0x39,0xfc,0xc2,0x3b,0xc0,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x3c, + 0x4c,0x19,0x14,0xc6,0x19,0xa1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39,0xc8, + 0x03,0x3d,0x94,0x03,0x3e,0x4c,0x09,0xe6,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00, + 0x7b,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88, + 0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6, + 0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce, + 0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8, + 0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48, + 0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11, + 0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e, + 0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89, + 0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b, + 0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37, + 0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78, + 0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81, + 0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1, + 0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c, + 0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39, + 0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc, + 0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58, + 0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87, + 0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f, + 0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e, + 0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66, + 0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b, + 0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0, + 0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e, + 0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc, + 0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3, + 0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07, + 0x7a,0x28,0x07,0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e, + 0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e, + 0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d, + 0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,0x00, + 0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00, + 0x61,0x20,0x00,0x00,0x23,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0xd4,0x63,0x11,0x40,0x60,0x1c,0x73,0x10,0x42,0xf0,0x3c,0x94, + 0x33,0x00,0x14,0x63,0x09,0x20,0x08,0x82,0xf0,0x2f,0x80,0x20,0x08,0xc2,0xbf,0x30, + 0x96,0x00,0x82,0x20,0x08,0x82,0x01,0x08,0x82,0x20,0x08,0x0e,0x33,0x00,0x24,0x73, + 0x10,0xd7,0x65,0x55,0x34,0x33,0x00,0x04,0x63,0x04,0x20,0x08,0x82,0xf8,0x37,0x46, + 0x00,0x82,0x20,0x08,0x7f,0x33,0x00,0x00,0xe3,0x0d,0x4c,0x64,0x51,0x40,0x2c,0x0a, + 0xe8,0x63,0xc1,0x02,0x1f,0x0b,0x16,0xf9,0x0c,0x32,0x04,0xcb,0x33,0xc8,0x10,0x2c, + 0xd1,0x6c,0xc3,0x52,0x01,0xb3,0x0d,0x41,0x15,0xcc,0x36,0x04,0x83,0x90,0x41,0x40, + 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x5b,0x86,0x20,0xc0,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _simgui_fs_bytecode_metal_macos[2809] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf9,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xa1,0xce,0x6b,0xd1,0x1f,0x32,0x9e, + 0x8d,0x8d,0x1c,0xcc,0x19,0xcb,0xd3,0xb6,0x21,0x99,0x0b,0xb6,0x46,0x8b,0x87,0x98, + 0x8e,0x2d,0xb5,0x98,0x92,0x0a,0x81,0x7d,0xf3,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44, + 0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0x0c,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x80,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32, + 0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45, + 0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44, + 0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8, + 0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00, + 0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07,0x7a,0x60,0x87,0x7c, + 0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72,0x68,0x03,0x72,0x48, + 0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90,0x07,0x7a,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc,0x21,0x1c,0xd8,0x61, + 0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81,0x1d,0xca,0xa1,0x0d, + 0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d,0xd8,0x61,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87,0x79,0x98,0x87,0x36, + 0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36,0x30,0x07,0x78,0x68, + 0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0xc2,0x1d,0xde, + 0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0,0xe1,0x1d,0xd2,0xc1, + 0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1,0x1d,0x00,0x7a,0x90, + 0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90,0x87,0x70,0x68,0x87, + 0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07,0x79,0x68,0x83,0x72, + 0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36,0x60,0x87,0x72,0x08, + 0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30,0x87,0x79,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1, + 0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01,0x1e,0xda,0x80,0x1e, + 0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07, + 0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87,0x36,0x30,0x07,0x78, + 0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20, + 0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c,0xc8,0xa1,0x0d,0xf4, + 0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda,0xa0,0x1d,0xc2,0x81, + 0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0xa0, + 0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68,0x83,0x76,0x08,0x07, + 0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6,0x81,0x1e,0xc2,0x61, + 0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d, + 0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e,0xda,0x60,0x1e,0xd2, + 0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87,0x70,0x30,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e, + 0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e,0xde,0xc1,0x1c,0xe8, + 0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87,0x70,0x60,0x87,0x79, + 0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81,0x00,0x16,0xa0,0xda, + 0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5,0x06,0xa3,0x08,0x80, + 0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00,0x00,0x03,0x00,0x00, + 0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00,0x00,0x89,0x20,0x00, + 0x00,0x1d,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4, + 0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4, + 0x4c,0x10,0x48,0x33,0x00,0xc3,0x08,0x04,0x60,0x83,0x30,0x8c,0x20,0x00,0x47,0x49, + 0x53,0x44,0x09,0x93,0xff,0x4f,0xc4,0x35,0x51,0x11,0xf1,0xdb,0xc3,0x3f,0x8d,0x11, + 0x00,0x83,0x08,0x44,0x70,0x91,0x34,0x45,0x94,0x30,0xf9,0xbf,0x04,0x30,0xcf,0x42, + 0x44,0xff,0x34,0x46,0x00,0x0c,0x22,0x18,0x42,0x29,0xc4,0x08,0xe5,0x10,0x9a,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0xca,0x19,0x8a,0x29,0x40,0x6d, + 0x20,0x20,0x05,0xd6,0x08,0x00,0x00,0x00,0x00,0x13,0xb2,0x70,0x48,0x07,0x79,0xb0, + 0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x76,0x08,0x87, + 0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83,0x38,0x70,0x03,0x38, + 0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0, + 0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x71,0x60,0x07, + 0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e,0x73,0x20,0x07,0x7a, + 0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60, + 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0, + 0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07, + 0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73, + 0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40, + 0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07, + 0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72, + 0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0, + 0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20,0x07,0x7a,0x20,0x07, + 0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x72,0x50,0x07,0x76, + 0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x50, + 0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07, + 0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74, + 0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60, + 0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x49,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00, + 0x18,0xc2,0x38,0x40,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x64,0x81,0x00,0x00,0x00, + 0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26, + 0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70, + 0x2c,0xa1,0x09,0x00,0x00,0x79,0x18,0x00,0x00,0xb9,0x00,0x00,0x00,0x1a,0x03,0x4c, + 0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x42, + 0x3c,0x00,0x84,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62, + 0x2c,0xc2,0x23,0x2c,0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e, + 0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6, + 0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06, + 0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0xf0,0x10,0x43,0x8c,0x45, + 0x58,0x8c,0x65,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x79,0x8e,0x45,0x58,0x84, + 0x65,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56, + 0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78, + 0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61, + 0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x84, + 0x67,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98, + 0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1, + 0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x9e,0x86,0x51,0x58,0x9a,0x9c, + 0x8b,0x5c,0x99,0x1b,0x59,0x99,0xdc,0x17,0x5d,0x98,0xdc,0x59,0x19,0x1d,0xa3,0xb0, + 0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0x2f,0xb7,0xb0,0xb6,0x32, + 0x1a,0x66,0x6c,0x6f,0x61,0x74,0x34,0x64,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe, + 0xdc,0xc2,0xda,0xca,0xa8,0x98,0xc9,0x85,0x9d,0x7d,0x8d,0xbd,0xb1,0xbd,0xc9,0x0d, + 0x61,0x9e,0x67,0x19,0x1e,0xe8,0x89,0x1e,0xe9,0x99,0x86,0x08,0x0f,0x45,0x29,0x2c, + 0x4d,0xce,0xc5,0x4c,0x2e,0xec,0xac,0xad,0xcc,0x8d,0xee,0x2b,0xcd,0x0d,0xae,0x8e, + 0x8e,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x1b,0x4c,0x0a,0x95,0xb0,0x34, + 0x39,0x97,0xb1,0x32,0x37,0xba,0x32,0x39,0x3e,0x61,0x69,0x72,0x2e,0x70,0x65,0x72, + 0x73,0x70,0x65,0x63,0x74,0x69,0x76,0x65,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x28, + 0xd4,0xd9,0x0d,0x91,0x96,0xe1,0xb1,0x9e,0xeb,0xc1,0x9e,0xec,0x81,0x1e,0xed,0x91, + 0x9e,0x8d,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x5b,0x4c,0x0a,0x8b,0xb1, + 0x37,0xb6,0x37,0xb9,0x21,0xd2,0x22,0x3c,0xd6,0xd3,0x3d,0xd8,0x93,0x3d,0xd0,0x13, + 0x3d,0xd2,0xe3,0x71,0x09,0x4b,0x93,0x73,0xa1,0x2b,0xc3,0xa3,0xab,0x93,0x2b,0xa3, + 0x14,0x96,0x26,0xe7,0xc2,0xf6,0x36,0x16,0x46,0x97,0xf6,0xe6,0xf6,0x95,0xe6,0x46, + 0x56,0x86,0x47,0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e,0xad,0x8c,0x18,0x5d, + 0x19,0x1e,0x5d,0x9d,0x5c,0x99,0x0c,0x19,0x8f,0x19,0xdb,0x5b,0x18,0x1d,0x0b,0xc8, + 0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x0f,0x07,0xba,0x32,0xbc,0x21,0xd4,0x42,0x3c,0x60, + 0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x23,0x06,0x0f,0xf4,0x8c,0xc1,0x23,0x3d,0x64,0xc0, + 0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x8e,0xc7,0x5c,0x58,0x1b, + 0x1c,0x5b,0x99,0x1c,0x87,0xb9,0x36,0xb8,0x21,0xd2,0x72,0x3c,0x66,0xf0,0x84,0xc1, + 0x32,0x2c,0xc2,0x03,0x3d,0x67,0xf0,0x48,0x0f,0x1a,0x0c,0x41,0x1e,0xee,0xf9,0x9e, + 0x32,0x78,0xd2,0x60,0x88,0x91,0x00,0x4f,0xf5,0xa8,0xc1,0x88,0x88,0x1d,0xd8,0xc1, + 0x1e,0xda,0xc1,0x0d,0xda,0xe1,0x1d,0xc8,0xa1,0x1e,0xd8,0xa1,0x1c,0xdc,0xc0,0x1c, + 0xd8,0x21,0x1c,0xce,0x61,0x1e,0xa6,0x08,0xc1,0x30,0x42,0x61,0x07,0x76,0xb0,0x87, + 0x76,0x70,0x83,0x74,0x20,0x87,0x72,0x70,0x07,0x7a,0x98,0x12,0x14,0x23,0x96,0x70, + 0x48,0x07,0x79,0x70,0x03,0x7b,0x28,0x07,0x79,0x98,0x87,0x74,0x78,0x07,0x77,0x98, + 0x12,0x18,0x23,0xa8,0x70,0x48,0x07,0x79,0x70,0x03,0x76,0x08,0x07,0x77,0x38,0x87, + 0x7a,0x08,0x87,0x73,0x28,0x87,0x5f,0xb0,0x87,0x72,0x90,0x87,0x79,0x48,0x87,0x77, + 0x70,0x87,0x29,0x01,0x32,0x62,0x0a,0x87,0x74,0x90,0x07,0x37,0x18,0x87,0x77,0x68, + 0x07,0x78,0x48,0x07,0x76,0x28,0x87,0x5f,0x78,0x07,0x78,0xa0,0x87,0x74,0x78,0x07, + 0x77,0x98,0x87,0x29,0x83,0xc2,0x38,0x23,0x98,0x70,0x48,0x07,0x79,0x70,0x03,0x73, + 0x90,0x87,0x70,0x38,0x87,0x76,0x28,0x07,0x77,0xa0,0x87,0x29,0xc1,0x1a,0x00,0x00, + 0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c, + 0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07, + 0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42, + 0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83, + 0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07, + 0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70, + 0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3, + 0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2, + 0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc, + 0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68, + 0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07, + 0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72, + 0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec, + 0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc, + 0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8, + 0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03, + 0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c, + 0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74, + 0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4, + 0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc, + 0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1, + 0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50, + 0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51, + 0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21, + 0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06, + 0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c, + 0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77, + 0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec, + 0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8, + 0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4, + 0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43, + 0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x08,0x00,0x00,0x00,0x16,0xb0,0x01, + 0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e, + 0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20,0x0d,0x0d,0x00,0x00,0x00,0x61,0x20,0x00, + 0x00,0x0c,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x04,0x00,0x00, + 0x00,0xc4,0x46,0x00,0x48,0x8d,0x00,0xd4,0x00,0x89,0x19,0x00,0x02,0x23,0x00,0x00, + 0x00,0x23,0x06,0x8a,0x10,0x44,0x87,0x91,0x0c,0x05,0x11,0x58,0x90,0xc8,0x67,0xb6, + 0x81,0x08,0x80,0x0c,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _simgui_vs_bytecode_metal_ios[3052] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xec,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe0,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x69,0x97,0x6b,0xac,0xd8,0xa2,0x51, + 0x33,0x7c,0x5f,0x96,0xb2,0xb1,0x06,0x06,0x7c,0xbb,0x5f,0x88,0xa0,0xeb,0x9f,0xea, + 0x6e,0x6b,0x70,0xa9,0x6e,0xef,0xe6,0xa4,0xea,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x37,0x00,0x00,0x00,0x56,0x41,0x54, + 0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x80, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03,0x00,0x04,0x04,0x06, + 0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b, + 0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xc0,0x0a,0x00,0x00,0xff,0xff,0xff,0xff, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xad,0x02,0x00,0x00,0x0b,0x82,0x20,0x00, + 0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49, + 0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62, + 0x80,0x10,0x45,0x02,0x42,0x92,0x0b,0x42,0x84,0x10,0x32,0x14,0x38,0x08,0x18,0x49, + 0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72, + 0x24,0x07,0xc8,0x08,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00, + 0x51,0x18,0x00,0x00,0x82,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8,0xff,0xff,0xff,0xff, + 0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87,0x71,0xa0,0x07,0x76, + 0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72,0x20,0x87,0x36,0x20, + 0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88,0x07,0x79,0xa0,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0xc0,0x1c,0xc2,0x81, + 0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c, + 0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c,0xd8,0x81,0x1d,0xe6, + 0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87,0x72,0x98,0x87,0x79, + 0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72,0x68,0x03,0x73,0x80, + 0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xdc, + 0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda,0x00,0x1e,0xde,0x21, + 0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21,0x1d,0xda,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30,0x07,0x79,0x08,0x87, + 0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87,0x72,0x90,0x87,0x36, + 0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77,0x68,0x03,0x76,0x28, + 0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70,0x07,0x73,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1,0x1d,0xe0,0xa1,0x0d, + 0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87, + 0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87,0x79,0x68,0x03,0x73, + 0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d,0xca,0x81,0x1c,0xda, + 0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21, + 0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x70,0x87,0x77,0x68, + 0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38,0x87,0x36,0x68,0x87, + 0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0x62,0x1e,0xe8,0x21, + 0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1,0x1c,0xc6,0x81,0x1e, + 0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x0d,0xe6, + 0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87,0x79,0x08,0x07,0x73, + 0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e, + 0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d,0xe6,0xe1,0x1d,0xcc, + 0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00,0x73,0x08,0x07,0x76, + 0x98,0x87,0x72,0x00,0x36,0x20,0x02,0x01,0x24,0xc0,0x02,0x54,0x00,0x00,0x00,0x00, + 0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00, + 0x16,0x00,0x00,0x00,0x32,0x22,0x08,0x09,0x20,0x64,0x85,0x04,0x13,0x22,0xa4,0x84, + 0x04,0x13,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x88,0x8c,0x0b,0x84,0x84,0x4c, + 0x10,0x3c,0x33,0x00,0xc3,0x08,0x02,0x30,0x8c,0x40,0x00,0x76,0x08,0x91,0x83,0xa4, + 0x29,0xa2,0x84,0xc9,0xaf,0xa4,0xff,0x01,0x22,0x80,0x91,0x50,0x10,0x83,0x08,0x84, + 0x50,0x8a,0x89,0x90,0x22,0x1b,0x08,0x98,0x23,0x00,0x83,0x14,0xc8,0x39,0x02,0x50, + 0x18,0x44,0x08,0x84,0x61,0x04,0x22,0x19,0x01,0x00,0x00,0x00,0x13,0xa8,0x70,0x48, + 0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83, + 0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83,0x0d,0xb7,0x51,0x0e, + 0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78,0xa0, + 0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07, + 0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a, + 0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60, + 0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0, + 0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06, + 0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6, + 0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x60, + 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x90,0x07, + 0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6, + 0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80, + 0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07, + 0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75, + 0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75,0x10,0x07,0x72,0xa0, + 0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0,0x06,0xf6,0x10,0x07, + 0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70, + 0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73,0x20, + 0x07,0x43,0x98,0x03,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x2c,0x10,0x00,0x00, + 0x09,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47, + 0xc6,0x04,0x43,0xca,0x12,0x18,0x01,0x28,0x88,0x22,0x28,0x84,0x32,0xa0,0x1d,0x01, + 0x20,0x1d,0x4b,0x80,0x04,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xe9,0x00,0x00,0x00, + 0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7, + 0x21,0x46,0x42,0x20,0x80,0x82,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3, + 0x2b,0x1b,0x62,0x24,0x01,0x22,0x24,0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4, + 0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06, + 0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac, + 0x65,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0x80,0x10, + 0x43,0x8c,0x24,0x48,0x86,0x44,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x41,0x8e, + 0x24,0x48,0x82,0x44,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56, + 0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56, + 0x36,0x44,0x40,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65, + 0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c, + 0x65,0x43,0x04,0x64,0x21,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1, + 0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95, + 0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85,0x89,0xb1,0x95,0x0d,0x11,0x90,0x86,0x51,0x58, + 0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0xd7,0x5b,0x1d,0x1d,0x5c,0x1d,0x1d, + 0x97,0xba,0xb9,0x32,0x39,0x14,0xb6,0xb7,0x31,0x37,0x98,0x14,0x46,0x61,0x69,0x72, + 0x2e,0x61,0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x6e,0x61,0x6d,0x65,0x34,0xcc, + 0xd8,0xde,0xc2,0xe8,0x64,0xc8,0x84,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85, + 0xb5,0x95,0x51,0xa8,0xb3,0x1b,0xc2,0x20,0x0f,0x02,0x21,0x11,0x22,0x21,0x13,0x42, + 0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0xa1,0x61,0xc6,0xf6, + 0x16,0x46,0x47,0xc3,0x62,0xec,0x8d,0xed,0x4d,0x6e,0x08,0x83,0x3c,0x88,0x85,0x44, + 0xc8,0x85,0x4c,0x08,0x46,0x26,0x2c,0x4d,0xce,0x05,0xee,0x6d,0x2e,0x8d,0x2e,0xed, + 0xcd,0x8d,0xcb,0x19,0xdb,0x17,0xd4,0xdb,0x5c,0x1a,0x5d,0xda,0x9b,0xdb,0x10,0x05, + 0xd1,0x90,0x08,0xb9,0x90,0x09,0xd9,0x86,0x18,0x48,0x85,0x64,0x08,0x47,0x28,0x2c, + 0x4d,0xce,0xc5,0xae,0x4c,0x8e,0xae,0x0c,0xef,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x52, + 0x58,0x9a,0x9c,0x0b,0xdb,0xdb,0x58,0x18,0x5d,0xda,0x9b,0xdb,0x57,0x9a,0x1b,0x59, + 0x19,0x1e,0xbd,0xb3,0x32,0xb7,0x32,0xb9,0x30,0xba,0x32,0x32,0x94,0xaf,0xaf,0xb0, + 0x34,0xb9,0x2f,0x38,0xb6,0xb0,0xb1,0x32,0xb4,0x37,0x36,0xb2,0x32,0xb9,0xaf,0xaf, + 0x14,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x43,0xa8,0x44,0x40,0x3c,0xe4, + 0x4b,0x84,0x24,0x40,0xc0,0x00,0x89,0x10,0x09,0x99,0x90,0x30,0x60,0x42,0x57,0x86, + 0x37,0xf6,0xf6,0x26,0x47,0x06,0x33,0x84,0x4a,0x02,0xc4,0x43,0xbe,0x24,0x48,0x02, + 0x04,0x0c,0x90,0x08,0x91,0x90,0x09,0x19,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30, + 0x43,0xa8,0x84,0x40,0x3c,0xe4,0x4b,0x88,0x24,0x40,0xc0,0x00,0x89,0x90,0x0b,0x99, + 0x90,0x32,0xa0,0x12,0x96,0x26,0xe7,0x22,0x56,0x67,0x66,0x56,0x26,0xc7,0x27,0x2c, + 0x4d,0xce,0x45,0xac,0xce,0xcc,0xac,0x4c,0xee,0x6b,0x2e,0x4d,0xaf,0x8c,0x48,0x58, + 0x9a,0x9c,0x8b,0x5c,0x59,0x18,0x19,0xa9,0xb0,0x34,0x39,0x97,0x39,0x3a,0xb9,0xba, + 0x31,0xba,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x34,0x37,0xb3,0x37,0x26,0x64,0x69,0x73, + 0x70,0x5f,0x73,0x69,0x7a,0x65,0x43,0x94,0x44,0x48,0x86,0x44,0x40,0x24,0x64,0x0d, + 0x18,0x85,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xd1,0xe5,0xc1,0x95,0x7d,0xcd,0xa5, + 0xe9,0x95,0xf1,0x0a,0x4b,0x93,0x73,0x09,0x93,0x3b,0xfb,0xa2,0xcb,0x83,0x2b,0xfb, + 0x0a,0x63,0x4b,0x3b,0x73,0xfb,0x9a,0x4b,0xd3,0x2b,0x63,0x62,0x37,0xf7,0x05,0x17, + 0x26,0x17,0xd6,0x36,0xc7,0xe1,0x4b,0x46,0x66,0x08,0x19,0x24,0x06,0x72,0x06,0x08, + 0x1a,0x24,0x03,0xf2,0x25,0x42,0x12,0x20,0x69,0x80,0xa8,0x01,0xc2,0x06,0x48,0x1b, + 0x24,0x03,0xe2,0x06,0xc9,0x80,0x44,0xc8,0x1b,0x20,0x13,0x02,0x07,0x43,0x10,0x44, + 0x0c,0x10,0x32,0x40,0xcc,0x00,0x89,0x83,0x21,0xc6,0x01,0x20,0x1d,0x22,0x07,0x7c, + 0xde,0xda,0xdc,0xd2,0xe0,0xde,0xe8,0xca,0xdc,0xe8,0x40,0xc6,0xd0,0xc2,0xe4,0xf8, + 0x4c,0xa5,0xb5,0xc1,0xb1,0x95,0x81,0x0c,0xad,0xac,0x80,0x50,0x09,0x05,0x05,0x0d, + 0x11,0x90,0x3a,0x18,0x62,0x20,0x74,0x80,0xd8,0xc1,0x72,0x0c,0x31,0x90,0x3b,0x40, + 0xee,0x60,0x39,0x46,0x44,0xec,0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0xd0,0x0e,0xef,0x40, + 0x0e,0xf5,0xc0,0x0e,0xe5,0xe0,0x06,0xe6,0xc0,0x0e,0xe1,0x70,0x0e,0xf3,0x30,0x45, + 0x08,0x86,0x11,0x0a,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0xa4,0x03,0x39,0x94,0x83, + 0x3b,0xd0,0xc3,0x94,0xa0,0x18,0xb1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39, + 0xc8,0xc3,0x3c,0xa4,0xc3,0x3b,0xb8,0xc3,0x94,0xc0,0x18,0x41,0x85,0x43,0x3a,0xc8, + 0x83,0x1b,0xb0,0x43,0x38,0xb8,0xc3,0x39,0xd4,0x43,0x38,0x9c,0x43,0x39,0xfc,0x82, + 0x3d,0x94,0x83,0x3c,0xcc,0x43,0x3a,0xbc,0x83,0x3b,0x4c,0x09,0x90,0x11,0x53,0x38, + 0xa4,0x83,0x3c,0xb8,0xc1,0x38,0xbc,0x43,0x3b,0xc0,0x43,0x3a,0xb0,0x43,0x39,0xfc, + 0xc2,0x3b,0xc0,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x3c,0x4c,0x19,0x14,0xc6,0x19, + 0xa1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39,0xc8,0x03,0x3d,0x94,0x03,0x3e, + 0x4c,0x09,0xe6,0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80,0x1c, + 0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07, + 0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e, + 0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43, + 0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c, + 0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76, + 0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e, + 0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8, + 0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4, + 0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68, + 0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07, + 0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71, + 0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5, + 0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4, + 0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90, + 0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43, + 0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b, + 0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78, + 0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2, + 0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20, + 0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0, + 0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83, + 0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07, + 0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61, + 0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d, + 0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39, + 0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79, + 0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c,0xe3, + 0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4, + 0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8, + 0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83, + 0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00, + 0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x23,0x00,0x00,0x00, + 0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0xd4,0x63,0x11,0x40, + 0x60,0x1c,0x73,0x10,0x42,0xf0,0x3c,0x94,0x33,0x00,0x14,0x63,0x09,0x20,0x08,0x82, + 0xf0,0x2f,0x80,0x20,0x08,0xc2,0xbf,0x30,0x96,0x00,0x82,0x20,0x08,0x82,0x01,0x08, + 0x82,0x20,0x08,0x0e,0x33,0x00,0x24,0x73,0x10,0xd7,0x65,0x55,0x34,0x33,0x00,0x04, + 0x63,0x04,0x20,0x08,0x82,0xf8,0x37,0x46,0x00,0x82,0x20,0x08,0x7f,0x33,0x00,0x00, + 0xe3,0x0d,0x4c,0x64,0x51,0x40,0x2c,0x0a,0xe8,0x63,0xc1,0x02,0x1f,0x0b,0x16,0xf9, + 0x0c,0x32,0x04,0xcb,0x33,0xc8,0x10,0x2c,0xd1,0x6c,0xc3,0x52,0x01,0xb3,0x0d,0x41, + 0x15,0xcc,0x36,0x04,0x83,0x90,0x41,0x40,0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x5b,0x86,0x20,0xc0,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _simgui_fs_bytecode_metal_ios[2809] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf9,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xf0,0xa4,0xb3,0x95,0x4b,0xab,0x64, + 0x94,0xe7,0xa9,0x8a,0x69,0x27,0x6d,0x28,0x77,0x84,0x8d,0x3f,0xaf,0x7d,0x3c,0x39, + 0x31,0xc4,0xcb,0x53,0x6d,0xc0,0x0d,0xdf,0x08,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44, + 0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0x04,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x7e,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32, + 0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45, + 0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44, + 0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8, + 0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00, + 0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07,0x7a,0x60,0x87,0x7c, + 0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72,0x68,0x03,0x72,0x48, + 0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90,0x07,0x7a,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc,0x21,0x1c,0xd8,0x61, + 0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81,0x1d,0xca,0xa1,0x0d, + 0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d,0xd8,0x61,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87,0x79,0x98,0x87,0x36, + 0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36,0x30,0x07,0x78,0x68, + 0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0xc2,0x1d,0xde, + 0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0,0xe1,0x1d,0xd2,0xc1, + 0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1,0x1d,0x00,0x7a,0x90, + 0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90,0x87,0x70,0x68,0x87, + 0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07,0x79,0x68,0x83,0x72, + 0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36,0x60,0x87,0x72,0x08, + 0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30,0x87,0x79,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1, + 0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01,0x1e,0xda,0x80,0x1e, + 0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07, + 0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87,0x36,0x30,0x07,0x78, + 0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20, + 0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c,0xc8,0xa1,0x0d,0xf4, + 0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda,0xa0,0x1d,0xc2,0x81, + 0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0xa0, + 0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68,0x83,0x76,0x08,0x07, + 0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6,0x81,0x1e,0xc2,0x61, + 0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d, + 0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e,0xda,0x60,0x1e,0xd2, + 0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87,0x70,0x30,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e, + 0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e,0xde,0xc1,0x1c,0xe8, + 0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87,0x70,0x60,0x87,0x79, + 0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81,0x00,0x16,0xa0,0xda, + 0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5,0x06,0xa3,0x08,0x80, + 0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00,0x00,0x03,0x00,0x00, + 0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00,0x00,0x89,0x20,0x00, + 0x00,0x1d,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4, + 0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4, + 0x4c,0x10,0x48,0x33,0x00,0xc3,0x08,0x04,0x60,0x83,0x30,0x8c,0x20,0x00,0x47,0x49, + 0x53,0x44,0x09,0x93,0xff,0x4f,0xc4,0x35,0x51,0x11,0xf1,0xdb,0xc3,0x3f,0x8d,0x11, + 0x00,0x83,0x08,0x44,0x70,0x91,0x34,0x45,0x94,0x30,0xf9,0xbf,0x04,0x30,0xcf,0x42, + 0x44,0xff,0x34,0x46,0x00,0x0c,0x22,0x18,0x42,0x29,0xc4,0x08,0xe5,0x10,0x9a,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0xca,0x19,0x8a,0x29,0x40,0x6d, + 0x20,0x20,0x05,0xd6,0x08,0x00,0x00,0x00,0x00,0x13,0xa8,0x70,0x48,0x07,0x79,0xb0, + 0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x74,0x78,0x87, + 0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83,0x0d,0xb7,0x51,0x0e,0x6d,0x00,0x0f, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe9, + 0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x78,0xa0, + 0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07, + 0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72, + 0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0, + 0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06, + 0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6, + 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xf6,0x20, + 0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x40,0x07,0x78, + 0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x90,0x07,0x76,0xa0,0x07, + 0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x72, + 0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60, + 0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07, + 0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a, + 0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10, + 0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0,0x06,0xf6,0x10,0x07,0x70,0x20,0x07, + 0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74, + 0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x43,0x98, + 0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x8c,0x03,0x04,0x80,0x00,0x00, + 0x00,0x00,0x00,0x40,0x16,0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98, + 0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02, + 0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0x01,0x12,0x00,0x00,0x79,0x18,0x00, + 0x00,0xb9,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32, + 0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x42,0x3c,0x00,0x84,0x50,0xb9,0x1b,0x43,0x0b, + 0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0xc2,0x23,0x2c,0x05,0xe7,0x20,0x08, + 0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed, + 0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c, + 0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45, + 0x26,0x65,0x88,0xf0,0x10,0x43,0x8c,0x45,0x58,0x8c,0x65,0x60,0xd1,0x54,0x46,0x17, + 0xc6,0x36,0x04,0x79,0x8e,0x45,0x58,0x84,0x65,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6, + 0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96, + 0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f, + 0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f, + 0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x84,0x67,0x21,0x19,0x84,0xa5,0xc9,0xb9,0x8c, + 0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99, + 0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85,0x89,0xb1,0x95,0x0d, + 0x11,0x9e,0x86,0x51,0x58,0x9a,0x9c,0x8b,0x5c,0x99,0x1b,0x59,0x99,0xdc,0x17,0x5d, + 0x98,0xdc,0x59,0x19,0x1d,0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c, + 0xb8,0xb2,0x2f,0xb7,0xb0,0xb6,0x32,0x1a,0x66,0x6c,0x6f,0x61,0x74,0x34,0x64,0xc2, + 0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xdc,0xc2,0xda,0xca,0xa8,0x98,0xc9,0x85,0x9d, + 0x7d,0x8d,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x9e,0x67,0x19,0x1e,0xe8,0x89,0x1e,0xe9, + 0x99,0x86,0x08,0x0f,0x45,0x29,0x2c,0x4d,0xce,0xc5,0x4c,0x2e,0xec,0xac,0xad,0xcc, + 0x8d,0xee,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb, + 0x98,0x1b,0x4c,0x0a,0x95,0xb0,0x34,0x39,0x97,0xb1,0x32,0x37,0xba,0x32,0x39,0x3e, + 0x61,0x69,0x72,0x2e,0x70,0x65,0x72,0x73,0x70,0x65,0x63,0x74,0x69,0x76,0x65,0x34, + 0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x28,0xd4,0xd9,0x0d,0x91,0x96,0xe1,0xb1,0x9e,0xeb, + 0xc1,0x9e,0xec,0x81,0x1e,0xed,0x91,0x9e,0x8d,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb, + 0xdb,0x98,0x5b,0x4c,0x0a,0x8b,0xb1,0x37,0xb6,0x37,0xb9,0x21,0xd2,0x22,0x3c,0xd6, + 0xd3,0x3d,0xd8,0x93,0x3d,0xd0,0x13,0x3d,0xd2,0xe3,0x71,0x09,0x4b,0x93,0x73,0xa1, + 0x2b,0xc3,0xa3,0xab,0x93,0x2b,0xa3,0x14,0x96,0x26,0xe7,0xc2,0xf6,0x36,0x16,0x46, + 0x97,0xf6,0xe6,0xf6,0x95,0xe6,0x46,0x56,0x86,0x47,0x25,0x2c,0x4d,0xce,0x65,0x2e, + 0xac,0x0d,0x8e,0xad,0x8c,0x18,0x5d,0x19,0x1e,0x5d,0x9d,0x5c,0x99,0x0c,0x19,0x8f, + 0x19,0xdb,0x5b,0x18,0x1d,0x0b,0xc8,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x0f,0x07,0xba, + 0x32,0xbc,0x21,0xd4,0x42,0x3c,0x60,0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x23,0x06,0x0f, + 0xf4,0x8c,0xc1,0x23,0x3d,0x64,0xc0,0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e, + 0xad,0x4c,0x8e,0xc7,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x87,0xb9,0x36,0xb8,0x21, + 0xd2,0x72,0x3c,0x66,0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x03,0x3d,0x67,0xf0,0x48,0x0f, + 0x1a,0x0c,0x41,0x1e,0xee,0xf9,0x9e,0x32,0x78,0xd2,0x60,0x88,0x91,0x00,0x4f,0xf5, + 0xa8,0xc1,0x88,0x88,0x1d,0xd8,0xc1,0x1e,0xda,0xc1,0x0d,0xda,0xe1,0x1d,0xc8,0xa1, + 0x1e,0xd8,0xa1,0x1c,0xdc,0xc0,0x1c,0xd8,0x21,0x1c,0xce,0x61,0x1e,0xa6,0x08,0xc1, + 0x30,0x42,0x61,0x07,0x76,0xb0,0x87,0x76,0x70,0x83,0x74,0x20,0x87,0x72,0x70,0x07, + 0x7a,0x98,0x12,0x14,0x23,0x96,0x70,0x48,0x07,0x79,0x70,0x03,0x7b,0x28,0x07,0x79, + 0x98,0x87,0x74,0x78,0x07,0x77,0x98,0x12,0x18,0x23,0xa8,0x70,0x48,0x07,0x79,0x70, + 0x03,0x76,0x08,0x07,0x77,0x38,0x87,0x7a,0x08,0x87,0x73,0x28,0x87,0x5f,0xb0,0x87, + 0x72,0x90,0x87,0x79,0x48,0x87,0x77,0x70,0x87,0x29,0x01,0x32,0x62,0x0a,0x87,0x74, + 0x90,0x07,0x37,0x18,0x87,0x77,0x68,0x07,0x78,0x48,0x07,0x76,0x28,0x87,0x5f,0x78, + 0x07,0x78,0xa0,0x87,0x74,0x78,0x07,0x77,0x98,0x87,0x29,0x83,0xc2,0x38,0x23,0x98, + 0x70,0x48,0x07,0x79,0x70,0x03,0x73,0x90,0x87,0x70,0x38,0x87,0x76,0x28,0x07,0x77, + 0xa0,0x87,0x29,0xc1,0x1a,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00, + 0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84, + 0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed, + 0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66, + 0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c, + 0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70, + 0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90, + 0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4, + 0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83, + 0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14, + 0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70, + 0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80, + 0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0, + 0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1, + 0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d, + 0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39, + 0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc, + 0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70, + 0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53, + 0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e, + 0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c, + 0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38, + 0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37, + 0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33, + 0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4, + 0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0, + 0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3, + 0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07, + 0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23, + 0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59, + 0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b, + 0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00, + 0x00,0x08,0x00,0x00,0x00,0x16,0xb0,0x01,0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f, + 0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e,0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20, + 0x0d,0x0d,0x00,0x00,0x00,0x61,0x20,0x00,0x00,0x0c,0x00,0x00,0x00,0x13,0x04,0x41, + 0x2c,0x10,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xc4,0x46,0x00,0x48,0x8d,0x00,0xd4, + 0x00,0x89,0x19,0x00,0x02,0x23,0x00,0x00,0x00,0x23,0x06,0x8a,0x10,0x44,0x87,0x91, + 0x0c,0x05,0x11,0x58,0x90,0xc8,0x67,0xb6,0x81,0x08,0x80,0x0c,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _simgui_vs_source_metal_sim[672] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x32,0x20,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x3b, + 0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e, + 0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x32,0x20,0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63, + 0x6e,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c, + 0x6f,0x63,0x6e,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20, + 0x5b,0x5b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b, + 0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69, + 0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62, + 0x75,0x74,0x65,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c, + 0x6f,0x61,0x74,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x5b, + 0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x31,0x29,0x5d,0x5d,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x32, + 0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x76,0x65,0x72,0x74,0x65,0x78,0x20, + 0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28, + 0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74, + 0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61, + 0x6e,0x74,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x26,0x20,0x5f,0x32, + 0x32,0x20,0x5b,0x5b,0x62,0x75,0x66,0x66,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29, + 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74, + 0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f, + 0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x28,0x28,0x69,0x6e,0x2e,0x70,0x6f,0x73, + 0x69,0x74,0x69,0x6f,0x6e,0x20,0x2f,0x20,0x5f,0x32,0x32,0x2e,0x64,0x69,0x73,0x70, + 0x5f,0x73,0x69,0x7a,0x65,0x29,0x20,0x2d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28, + 0x30,0x2e,0x35,0x29,0x29,0x20,0x2a,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x32, + 0x2e,0x30,0x2c,0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c,0x20,0x30,0x2e,0x35,0x2c,0x20, + 0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75,0x76, + 0x20,0x3d,0x20,0x69,0x6e,0x2e,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d, + 0x20,0x69,0x6e,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, + +}; +static const uint8_t _simgui_fs_source_metal_sim[436] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d, + 0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f, + 0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, + 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x66,0x72,0x61,0x67,0x6d,0x65, + 0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69, + 0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b, + 0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x74,0x65, + 0x78,0x20,0x5b,0x5b,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x30,0x29,0x5d,0x5d, + 0x2c,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x73,0x6d,0x70,0x20,0x5b,0x5b, + 0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a, + 0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75, + 0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e, + 0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78, + 0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x73,0x6d,0x70,0x2c,0x20,0x69,0x6e,0x2e, + 0x75,0x76,0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a, + 0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_D3D11) +static const uint8_t _simgui_vs_bytecode_hlsl4[892] = { + 0x44,0x58,0x42,0x43,0x0d,0xbd,0x9e,0x9e,0x7d,0xc0,0x2b,0x54,0x88,0xf9,0xca,0x89, + 0x32,0xe4,0x0c,0x59,0x01,0x00,0x00,0x00,0x7c,0x03,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x60,0x01,0x00,0x00,0xd0,0x01,0x00,0x00, + 0x00,0x03,0x00,0x00,0x52,0x44,0x45,0x46,0xc0,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x48,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xfe,0xff, + 0x10,0x81,0x00,0x00,0x98,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0xab,0xab,0x3c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x88,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x32,0x32,0x5f,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a, + 0x65,0x00,0xab,0xab,0x01,0x00,0x03,0x00,0x01,0x00,0x02,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f,0x66,0x74,0x20,0x28,0x52, + 0x29,0x20,0x48,0x4c,0x53,0x4c,0x20,0x53,0x68,0x61,0x64,0x65,0x72,0x20,0x43,0x6f, + 0x6d,0x70,0x69,0x6c,0x65,0x72,0x20,0x31,0x30,0x2e,0x31,0x00,0x49,0x53,0x47,0x4e, + 0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x50,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x03,0x00,0x00,0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x50,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab, + 0x4f,0x53,0x47,0x4e,0x68,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x0c,0x00,0x00,0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0xab,0xab,0xab, + 0x53,0x48,0x44,0x52,0x28,0x01,0x00,0x00,0x40,0x00,0x01,0x00,0x4a,0x00,0x00,0x00, + 0x59,0x00,0x00,0x04,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x5f,0x00,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x5f,0x00,0x00,0x03, + 0x32,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00, + 0x02,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0x32,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00,0x67,0x00,0x00,0x04, + 0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x68,0x00,0x00,0x02, + 0x01,0x00,0x00,0x00,0x36,0x00,0x00,0x05,0x32,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x36,0x00,0x00,0x05,0xf2,0x20,0x10,0x00, + 0x01,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x02,0x00,0x00,0x00,0x0e,0x00,0x00,0x08, + 0x32,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x80,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a, + 0x32,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x02,0x40,0x00,0x00,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x0a,0x32,0x20,0x10,0x00,0x02,0x00,0x00,0x00, + 0x46,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x02,0x40,0x00,0x00,0x00,0x00,0x00,0x40, + 0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x08, + 0xc2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0x02,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x00,0x00,0x80,0x3f,0x3e,0x00,0x00,0x01, + 0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _simgui_fs_bytecode_hlsl4[608] = { + 0x44,0x58,0x42,0x43,0x3a,0xa7,0x41,0x21,0xb4,0x2d,0xa7,0x6e,0xfe,0x31,0xb0,0xe0, + 0x14,0xe0,0xdf,0x5a,0x01,0x00,0x00,0x00,0x60,0x02,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x48,0x01,0x00,0x00, + 0xe4,0x01,0x00,0x00,0x52,0x44,0x45,0x46,0x8c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xff,0xff, + 0x10,0x81,0x00,0x00,0x64,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x73,0x6d,0x70,0x00,0x74,0x65,0x78,0x00, + 0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f,0x66,0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c, + 0x53,0x4c,0x20,0x53,0x68,0x61,0x64,0x65,0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c, + 0x65,0x72,0x20,0x31,0x30,0x2e,0x31,0x00,0x49,0x53,0x47,0x4e,0x44,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x00,0x00, + 0x38,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x2c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x53,0x56,0x5f,0x54, + 0x61,0x72,0x67,0x65,0x74,0x00,0xab,0xab,0x53,0x48,0x44,0x52,0x94,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x5a,0x00,0x00,0x03,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x58,0x18,0x00,0x04,0x00,0x70,0x10,0x00,0x00,0x00,0x00,0x00, + 0x55,0x55,0x00,0x00,0x62,0x10,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x62,0x10,0x00,0x03,0xf2,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x65,0x00,0x00,0x03, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00, + 0x45,0x00,0x00,0x09,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x7e,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x07,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x01,0x00,0x00,0x00, + 0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +}; +#elif defined(SOKOL_WGPU) +static const uint8_t _simgui_vs_source_wgsl[1083] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a, + 0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20, + 0x20,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61, + 0x74,0x65,0x3e,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75,0x70,0x28, + 0x30,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x30,0x29,0x20,0x76, + 0x61,0x72,0x3c,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x3e,0x20,0x78,0x5f,0x32,0x32, + 0x20,0x3a,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x3b,0x0a,0x0a,0x76, + 0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72, + 0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61, + 0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65, + 0x3e,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20, + 0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f, + 0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x39, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x5f,0x31,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x35,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x78,0x5f,0x32,0x32, + 0x2e,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x6c,0x65, + 0x74,0x20,0x78,0x5f,0x33,0x33,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d, + 0x20,0x28,0x28,0x28,0x78,0x5f,0x31,0x39,0x20,0x2f,0x20,0x78,0x5f,0x32,0x35,0x29, + 0x20,0x2d,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x30,0x2e,0x35,0x66,0x2c,0x20,0x30, + 0x2e,0x35,0x66,0x29,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x32,0x2e, + 0x30,0x66,0x2c,0x20,0x2d,0x32,0x2e,0x30,0x66,0x29,0x29,0x3b,0x0a,0x20,0x20,0x67, + 0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x76,0x65,0x63, + 0x34,0x66,0x28,0x78,0x5f,0x33,0x33,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x33,0x33,0x2e, + 0x79,0x2c,0x20,0x30,0x2e,0x35,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x3b,0x0a, + 0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x33,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a, + 0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x78,0x5f,0x34,0x33,0x3b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x34,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20, + 0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x20,0x3d,0x20,0x78,0x5f,0x34,0x37,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75, + 0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61, + 0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x40,0x62,0x75,0x69,0x6c, + 0x74,0x69,0x6e,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x29,0x0a,0x20,0x20, + 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20,0x76,0x65, + 0x63,0x34,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e, + 0x28,0x30,0x29,0x0a,0x20,0x20,0x75,0x76,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28, + 0x31,0x29,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x31,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x76,0x65,0x72,0x74,0x65,0x78, + 0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69, + 0x6f,0x6e,0x28,0x30,0x29,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20, + 0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x20,0x74,0x65,0x78, + 0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28, + 0x32,0x29,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x29,0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e, + 0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x5f,0x31,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f, + 0x72,0x64,0x30,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20, + 0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a, + 0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65, + 0x74,0x75,0x72,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x67,0x6c, + 0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x2c,0x20,0x75,0x76,0x2c,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const uint8_t _simgui_fs_source_wgsl[630] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75, + 0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x34,0x38, + 0x29,0x20,0x76,0x61,0x72,0x20,0x74,0x65,0x78,0x20,0x3a,0x20,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x5f,0x32,0x64,0x3c,0x66,0x33,0x32,0x3e,0x3b,0x0a,0x0a,0x40,0x67, + 0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67, + 0x28,0x36,0x34,0x29,0x20,0x76,0x61,0x72,0x20,0x73,0x6d,0x70,0x20,0x3a,0x20,0x73, + 0x61,0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, + 0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a, + 0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20, + 0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x33,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, + 0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x2c,0x20,0x73, + 0x6d,0x70,0x2c,0x20,0x78,0x5f,0x32,0x33,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74, + 0x20,0x78,0x5f,0x32,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f, + 0x6c,0x6f,0x72,0x20,0x3d,0x20,0x28,0x78,0x5f,0x32,0x34,0x20,0x2a,0x20,0x78,0x5f, + 0x32,0x37,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d, + 0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75, + 0x74,0x20,0x7b,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28, + 0x30,0x29,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x5f, + 0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x66, + 0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28, + 0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x75,0x76,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20,0x40, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x29, + 0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20, + 0x20,0x75,0x76,0x20,0x3d,0x20,0x75,0x76,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a, + 0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28, + 0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6d,0x61,0x69,0x6e, + 0x5f,0x6f,0x75,0x74,0x28,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x29, + 0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_DUMMY_BACKEND) +static const char* _simgui_vs_source_dummy = ""; +static const char* _simgui_fs_source_dummy = ""; +#else +#error "Please define one of SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" +#endif + +#if !defined(SOKOL_IMGUI_NO_SOKOL_APP) +static void _simgui_set_clipboard(void* user_data, const char* text) { + (void)user_data; + sapp_set_clipboard_string(text); +} + +static const char* _simgui_get_clipboard(void* user_data) { + (void)user_data; + return sapp_get_clipboard_string(); +} +#endif + +#if defined(__EMSCRIPTEN__) && !defined(SOKOL_DUMMY_BACKEND) +EM_JS(int, simgui_js_is_osx, (void), { + if (navigator.userAgent.includes('Macintosh')) { + return 1; + } else { + return 0; + } +}); +#endif + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SIMGUI_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _simgui_log_messages[] = { + _SIMGUI_LOG_ITEMS +}; +#undef _SIMGUI_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SIMGUI_PANIC(code) _simgui_log(SIMGUI_LOGITEM_ ##code, 0, 0, __LINE__) +#define _SIMGUI_ERROR(code) _simgui_log(SIMGUI_LOGITEM_ ##code, 1, 0, __LINE__) +#define _SIMGUI_WARN(code) _simgui_log(SIMGUI_LOGITEM_ ##code, 2, 0, __LINE__) +#define _SIMGUI_INFO(code) _simgui_log(SIMGUI_LOGITEM_ ##code, 3, 0, __LINE__) +#define _SIMGUI_LOGMSG(code,msg) _simgui_log(SIMGUI_LOGITEM_ ##code, 3, msg, __LINE__) + +static void _simgui_log(simgui_log_item_t log_item, uint32_t log_level, const char* msg, uint32_t line_nr) { + if (_simgui.desc.logger.func) { + const char* filename = 0; + #if defined(SOKOL_DEBUG) + filename = __FILE__; + if (0 == msg) { + msg = _simgui_log_messages[log_item]; + } + #endif + _simgui.desc.logger.func("simgui", log_level, log_item, msg, line_nr, filename, _simgui.desc.logger.user_data); + } else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory +static void _simgui_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +static void* _simgui_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_simgui.desc.allocator.alloc_fn) { + ptr = _simgui.desc.allocator.alloc_fn(size, _simgui.desc.allocator.user_data); + } else { + ptr = malloc(size); + } + if (0 == ptr) { + _SIMGUI_PANIC(MALLOC_FAILED); + } + return ptr; +} + +static void* _simgui_malloc_clear(size_t size) { + void* ptr = _simgui_malloc(size); + _simgui_clear(ptr, size); + return ptr; +} + +static void _simgui_free(void* ptr) { + if (_simgui.desc.allocator.free_fn) { + _simgui.desc.allocator.free_fn(ptr, _simgui.desc.allocator.user_data); + } else { + free(ptr); + } +} + +// ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ +// +// >>pool +static void _simgui_init_pool(_simgui_pool_t* pool, int num) { + SOKOL_ASSERT(pool && (num >= 1)); + // slot 0 is reserved for the 'invalid id', so bump the pool size by 1 + pool->size = num + 1; + pool->queue_top = 0; + // generation counters indexable by pool slot index, slot 0 is reserved + size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size; + pool->gen_ctrs = (uint32_t*) _simgui_malloc_clear(gen_ctrs_size); + // it's not a bug to only reserve 'num' here + pool->free_queue = (int*) _simgui_malloc_clear(sizeof(int) * (size_t)num); + // never allocate the zero-th pool item since the invalid id is 0 + for (int i = pool->size-1; i >= 1; i--) { + pool->free_queue[pool->queue_top++] = i; + } +} + +static void _simgui_discard_pool(_simgui_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + _simgui_free(pool->free_queue); + pool->free_queue = 0; + SOKOL_ASSERT(pool->gen_ctrs); + _simgui_free(pool->gen_ctrs); + pool->gen_ctrs = 0; + pool->size = 0; + pool->queue_top = 0; +} + +static int _simgui_pool_alloc_index(_simgui_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } else { + // pool exhausted + return _SIMGUI_INVALID_SLOT_INDEX; + } +} + +static void _simgui_pool_free_index(_simgui_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SIMGUI_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_ASSERT(pool->queue_top < pool->size); + #ifdef SOKOL_DEBUG + // debug check against double-free + for (int i = 0; i < pool->queue_top; i++) { + SOKOL_ASSERT(pool->free_queue[i] != slot_index); + } + #endif + pool->free_queue[pool->queue_top++] = slot_index; + SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); +} + +/* initialize a pool slot: + - bump the slot's generation counter + - create a resource id from the generation counter and slot index + - set the slot's id to this id + - set the slot's state to ALLOC + - return the handle id +*/ +static uint32_t _simgui_slot_init(_simgui_pool_t* pool, _simgui_slot_t* slot, int slot_index) { + /* FIXME: add handling for an overflowing generation counter, + for now, just overflow (another option is to disable + the slot) + */ + SOKOL_ASSERT(pool && pool->gen_ctrs); + SOKOL_ASSERT((slot_index > _SIMGUI_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT((slot->state == _SIMGUI_RESOURCESTATE_INITIAL) && (slot->id == SIMGUI_INVALID_ID)); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + slot->id = (ctr<<_SIMGUI_SLOT_SHIFT)|(slot_index & _SIMGUI_SLOT_MASK); + slot->state = _SIMGUI_RESOURCESTATE_ALLOC; + return slot->id; +} + +// extract slot index from id +static int _simgui_slot_index(uint32_t id) { + int slot_index = (int) (id & _SIMGUI_SLOT_MASK); + SOKOL_ASSERT(_SIMGUI_INVALID_SLOT_INDEX != slot_index); + return slot_index; +} + +static void _simgui_init_item_pool(_simgui_pool_t* pool, int pool_size, void** items_ptr, size_t item_size_bytes) { + // NOTE: the pools will have an additional item, since slot 0 is reserved + SOKOL_ASSERT(pool && (pool->size == 0)); + SOKOL_ASSERT((pool_size > 0) && (pool_size < _SIMGUI_MAX_POOL_SIZE)); + SOKOL_ASSERT(items_ptr && (*items_ptr == 0)); + SOKOL_ASSERT(item_size_bytes > 0); + _simgui_init_pool(pool, pool_size); + const size_t pool_size_bytes = item_size_bytes * (size_t)pool->size; + *items_ptr = _simgui_malloc_clear(pool_size_bytes); +} + +static void _simgui_discard_item_pool(_simgui_pool_t* pool, void** items_ptr) { + SOKOL_ASSERT(pool && (pool->size != 0)); + SOKOL_ASSERT(items_ptr && (*items_ptr != 0)); + _simgui_free(*items_ptr); *items_ptr = 0; + _simgui_discard_pool(pool); +} + +static void _simgui_setup_image_pool(int pool_size) { + _simgui_image_pool_t* p = &_simgui.image_pool; + _simgui_init_item_pool(&p->pool, pool_size, (void**)&p->items, sizeof(_simgui_image_t)); +} + +static void _simgui_discard_image_pool(void) { + _simgui_image_pool_t* p = &_simgui.image_pool; + _simgui_discard_item_pool(&p->pool, (void**)&p->items); +} + +static simgui_image_t _simgui_make_image_handle(uint32_t id) { + simgui_image_t handle = { id }; + return handle; +} + +static _simgui_image_t* _simgui_image_at(uint32_t id) { + SOKOL_ASSERT(SIMGUI_INVALID_ID != id); + const _simgui_image_pool_t* p = &_simgui.image_pool; + int slot_index = _simgui_slot_index(id); + SOKOL_ASSERT((slot_index > _SIMGUI_INVALID_SLOT_INDEX) && (slot_index < p->pool.size)); + return &p->items[slot_index]; +} + +static _simgui_image_t* _simgui_lookup_image(uint32_t id) { + if (SIMGUI_INVALID_ID != id) { + _simgui_image_t* img = _simgui_image_at(id); + if (img->slot.id == id) { + return img; + } + } + return 0; +} + +static simgui_image_t _simgui_alloc_image(void) { + _simgui_image_pool_t* p = &_simgui.image_pool; + int slot_index = _simgui_pool_alloc_index(&p->pool); + if (_SIMGUI_INVALID_SLOT_INDEX != slot_index) { + uint32_t id = _simgui_slot_init(&p->pool, &p->items[slot_index].slot, slot_index); + return _simgui_make_image_handle(id); + } else { + // pool exhausted + return _simgui_make_image_handle(SIMGUI_INVALID_ID); + } +} + +static _simgui_resource_state _simgui_init_image(_simgui_image_t* img, const simgui_image_desc_t* desc) { + SOKOL_ASSERT(img && (img->slot.state == _SIMGUI_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + SOKOL_ASSERT(_simgui.def_pip.id != SIMGUI_INVALID_ID); + SOKOL_ASSERT(_simgui.pip_unfilterable.id != SIMGUI_INVALID_ID); + img->image = desc->image; + img->sampler = desc->sampler; + if (sg_query_pixelformat(sg_query_image_desc(desc->image).pixel_format).filter) { + img->pip = _simgui.def_pip; + } else { + img->pip = _simgui.pip_unfilterable; + } + return _SIMGUI_RESOURCESTATE_VALID; +} + +static void _simgui_deinit_image(_simgui_image_t* img) { + SOKOL_ASSERT(img); + img->image.id = SIMGUI_INVALID_ID; + img->sampler.id = SIMGUI_INVALID_ID; + img->pip.id = SIMGUI_INVALID_ID; +} + +static void _simgui_destroy_image(simgui_image_t img_id) { + _simgui_image_t* img = _simgui_lookup_image(img_id.id); + if (img) { + _simgui_deinit_image(img); + _simgui_image_pool_t* p = &_simgui.image_pool; + _simgui_clear(img, sizeof(_simgui_image_t)); + _simgui_pool_free_index(&p->pool, _simgui_slot_index(img_id.id)); + } +} + +static void _simgui_destroy_all_images(void) { + _simgui_image_pool_t* p = &_simgui.image_pool; + for (int i = 0; i < p->pool.size; i++) { + _simgui_image_t* img = &p->items[i]; + _simgui_destroy_image(_simgui_make_image_handle(img->slot.id)); + } +} + +static simgui_image_desc_t _simgui_image_desc_defaults(const simgui_image_desc_t* desc) { + SOKOL_ASSERT(desc); + simgui_image_desc_t res = *desc; + res.image.id = _simgui_def(res.image.id, _simgui.def_img.id); + res.sampler.id = _simgui_def(res.sampler.id, _simgui.def_smp.id); + return res; +} + +static bool _simgui_is_osx(void) { + #if defined(SOKOL_DUMMY_BACKEND) + return false; + #elif defined(__EMSCRIPTEN__) + return simgui_js_is_osx(); + #elif defined(__APPLE__) + return true; + #else + return false; + #endif +} + +static simgui_desc_t _simgui_desc_defaults(const simgui_desc_t* desc) { + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + simgui_desc_t res = *desc; + res.max_vertices = _simgui_def(res.max_vertices, 65536); + res.image_pool_size = _simgui_def(res.image_pool_size, 256); + return res; +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { + SOKOL_ASSERT(desc); + _simgui_clear(&_simgui, sizeof(_simgui)); + _simgui.init_cookie = _SIMGUI_INIT_COOKIE; + _simgui.desc = _simgui_desc_defaults(desc); + _simgui.cur_dpi_scale = 1.0f; + #if !defined(SOKOL_IMGUI_NO_SOKOL_APP) + _simgui.is_osx = _simgui_is_osx(); + #endif + // can keep color_format, depth_format and sample_count as is, + // since sokol_gfx.h will do its own default-value handling + + // setup image pool + _simgui_setup_image_pool(_simgui.desc.image_pool_size); + + // allocate an intermediate vertex- and index-buffer + SOKOL_ASSERT(_simgui.desc.max_vertices > 0); + _simgui.vertices.size = (size_t)_simgui.desc.max_vertices * sizeof(ImDrawVert); + _simgui.vertices.ptr = _simgui_malloc(_simgui.vertices.size); + _simgui.indices.size = (size_t)_simgui.desc.max_vertices * 3 * sizeof(ImDrawIdx); + _simgui.indices.ptr = _simgui_malloc(_simgui.indices.size); + + // initialize Dear ImGui + #if defined(__cplusplus) + ImGui::CreateContext(); + ImGui::StyleColorsDark(); + ImGuiIO* io = &ImGui::GetIO(); + if (!_simgui.desc.no_default_font) { + io->Fonts->AddFontDefault(); + } + #else + igCreateContext(NULL); + igStyleColorsDark(igGetStyle()); + ImGuiIO* io = igGetIO(); + if (!_simgui.desc.no_default_font) { + ImFontAtlas_AddFontDefault(io->Fonts, NULL); + } + #endif + io->IniFilename = _simgui.desc.ini_filename; + io->ConfigMacOSXBehaviors = _simgui_is_osx(); + io->BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; + #if !defined(SOKOL_IMGUI_NO_SOKOL_APP) + if (!_simgui.desc.disable_set_mouse_cursor) { + io->BackendFlags |= ImGuiBackendFlags_HasMouseCursors; + } + io->SetClipboardTextFn = _simgui_set_clipboard; + io->GetClipboardTextFn = _simgui_get_clipboard; + #endif + io->ConfigWindowsResizeFromEdges = !_simgui.desc.disable_windows_resize_from_edges; + + // create sokol-gfx resources + sg_push_debug_group("sokol-imgui"); + + // shader object for using the embedded shader source (or bytecode) + sg_shader_desc shd_desc; + _simgui_clear(&shd_desc, sizeof(shd_desc)); + shd_desc.attrs[0].name = "position"; + shd_desc.attrs[1].name = "texcoord0"; + shd_desc.attrs[2].name = "color0"; + shd_desc.attrs[0].sem_name = "TEXCOORD"; + shd_desc.attrs[0].sem_index = 0; + shd_desc.attrs[1].sem_name = "TEXCOORD"; + shd_desc.attrs[1].sem_index = 1; + shd_desc.attrs[2].sem_name = "TEXCOORD"; + shd_desc.attrs[2].sem_index = 2; + sg_shader_uniform_block_desc* ub = &shd_desc.vs.uniform_blocks[0]; + ub->size = sizeof(_simgui_vs_params_t); + ub->uniforms[0].name = "vs_params"; + ub->uniforms[0].type = SG_UNIFORMTYPE_FLOAT4; + ub->uniforms[0].array_count = 1; + shd_desc.fs.images[0].used = true; + shd_desc.fs.images[0].image_type = SG_IMAGETYPE_2D; + shd_desc.fs.images[0].sample_type = SG_IMAGESAMPLETYPE_FLOAT; + shd_desc.fs.samplers[0].used = true; + shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING; + shd_desc.fs.image_sampler_pairs[0].used = true; + shd_desc.fs.image_sampler_pairs[0].image_slot = 0; + shd_desc.fs.image_sampler_pairs[0].sampler_slot = 0; + shd_desc.fs.image_sampler_pairs[0].glsl_name = "tex_smp"; + shd_desc.label = "sokol-imgui-shader"; + #if defined(SOKOL_GLCORE) + shd_desc.vs.source = (const char*)_simgui_vs_source_glsl410; + shd_desc.fs.source = (const char*)_simgui_fs_source_glsl410; + #elif defined(SOKOL_GLES3) + shd_desc.vs.source = (const char*)_simgui_vs_source_glsl300es; + shd_desc.fs.source = (const char*)_simgui_fs_source_glsl300es; + #elif defined(SOKOL_METAL) + shd_desc.vs.entry = "main0"; + shd_desc.fs.entry = "main0"; + switch (sg_query_backend()) { + case SG_BACKEND_METAL_MACOS: + shd_desc.vs.bytecode = SG_RANGE(_simgui_vs_bytecode_metal_macos); + shd_desc.fs.bytecode = SG_RANGE(_simgui_fs_bytecode_metal_macos); + break; + case SG_BACKEND_METAL_IOS: + shd_desc.vs.bytecode = SG_RANGE(_simgui_vs_bytecode_metal_ios); + shd_desc.fs.bytecode = SG_RANGE(_simgui_fs_bytecode_metal_ios); + break; + default: + shd_desc.vs.source = (const char*)_simgui_vs_source_metal_sim; + shd_desc.fs.source = (const char*)_simgui_fs_source_metal_sim; + break; + } + #elif defined(SOKOL_D3D11) + shd_desc.vs.bytecode = SG_RANGE(_simgui_vs_bytecode_hlsl4); + shd_desc.fs.bytecode = SG_RANGE(_simgui_fs_bytecode_hlsl4); + #elif defined(SOKOL_WGPU) + shd_desc.vs.source = (const char*)_simgui_vs_source_wgsl; + shd_desc.fs.source = (const char*)_simgui_fs_source_wgsl; + #else + shd_desc.vs.source = _simgui_vs_source_dummy; + shd_desc.fs.source = _simgui_fs_source_dummy; + #endif + _simgui.def_shd = sg_make_shader(&shd_desc); + + // pipeline object for imgui rendering + sg_pipeline_desc pip_desc; + _simgui_clear(&pip_desc, sizeof(pip_desc)); + pip_desc.layout.buffers[0].stride = sizeof(ImDrawVert); + { + sg_vertex_attr_state* attr = &pip_desc.layout.attrs[0]; + attr->offset = offsetof(ImDrawVert, pos); + attr->format = SG_VERTEXFORMAT_FLOAT2; + } + { + sg_vertex_attr_state* attr = &pip_desc.layout.attrs[1]; + attr->offset = offsetof(ImDrawVert, uv); + attr->format = SG_VERTEXFORMAT_FLOAT2; + } + { + sg_vertex_attr_state* attr = &pip_desc.layout.attrs[2]; + attr->offset = offsetof(ImDrawVert, col); + attr->format = SG_VERTEXFORMAT_UBYTE4N; + } + pip_desc.shader = _simgui.def_shd; + pip_desc.index_type = SG_INDEXTYPE_UINT16; + pip_desc.sample_count = _simgui.desc.sample_count; + pip_desc.depth.pixel_format = _simgui.desc.depth_format; + pip_desc.colors[0].pixel_format = _simgui.desc.color_format; + pip_desc.colors[0].write_mask = _simgui.desc.write_alpha_channel ? SG_COLORMASK_RGBA : SG_COLORMASK_RGB; + pip_desc.colors[0].blend.enabled = true; + pip_desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA; + pip_desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + if (_simgui.desc.write_alpha_channel) { + pip_desc.colors[0].blend.src_factor_alpha = SG_BLENDFACTOR_ONE; + pip_desc.colors[0].blend.dst_factor_alpha = SG_BLENDFACTOR_ONE; + } + pip_desc.label = "sokol-imgui-pipeline"; + _simgui.def_pip = sg_make_pipeline(&pip_desc); + + // create a unfilterable/nonfiltering variants of the shader and pipeline + shd_desc.fs.images[0].sample_type = SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT; + shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_NONFILTERING; + shd_desc.label = "sokol-imgui-shader-unfilterable"; + _simgui.shd_unfilterable = sg_make_shader(&shd_desc); + pip_desc.shader = _simgui.shd_unfilterable; + pip_desc.label = "sokol-imgui-pipeline-unfilterable"; + _simgui.pip_unfilterable = sg_make_pipeline(&pip_desc); + + // NOTE: since we're in C++ mode here we can't use C99 designated init + sg_buffer_desc vb_desc; + _simgui_clear(&vb_desc, sizeof(vb_desc)); + vb_desc.usage = SG_USAGE_STREAM; + vb_desc.size = _simgui.vertices.size; + vb_desc.label = "sokol-imgui-vertices"; + _simgui.vbuf = sg_make_buffer(&vb_desc); + + sg_buffer_desc ib_desc; + _simgui_clear(&ib_desc, sizeof(ib_desc)); + ib_desc.type = SG_BUFFERTYPE_INDEXBUFFER; + ib_desc.usage = SG_USAGE_STREAM; + ib_desc.size = _simgui.indices.size; + ib_desc.label = "sokol-imgui-indices"; + _simgui.ibuf = sg_make_buffer(&ib_desc); + + // a default user-image sampler + sg_sampler_desc def_sampler_desc; + _simgui_clear(&def_sampler_desc, sizeof(def_sampler_desc)); + def_sampler_desc.min_filter = SG_FILTER_NEAREST; + def_sampler_desc.mag_filter = SG_FILTER_NEAREST; + def_sampler_desc.wrap_u = SG_WRAP_CLAMP_TO_EDGE; + def_sampler_desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE; + def_sampler_desc.label = "sokol-imgui-default-sampler"; + _simgui.def_smp = sg_make_sampler(&def_sampler_desc); + + // a default user image + static uint32_t def_pixels[64]; + memset(def_pixels, 0xFF, sizeof(def_pixels)); + sg_image_desc def_image_desc; + _simgui_clear(&def_image_desc, sizeof(def_image_desc)); + def_image_desc.width = 8; + def_image_desc.height = 8; + def_image_desc.pixel_format = SG_PIXELFORMAT_RGBA8; + def_image_desc.data.subimage[0][0].ptr = def_pixels; + def_image_desc.data.subimage[0][0].size = sizeof(def_pixels); + def_image_desc.label = "sokol-imgui-default-image"; + _simgui.def_img = sg_make_image(&def_image_desc); + + // default font texture + if (!_simgui.desc.no_default_font) { + simgui_font_tex_desc_t simgui_font_smp_desc; + _simgui_clear(&simgui_font_smp_desc, sizeof(simgui_font_smp_desc)); + simgui_create_fonts_texture(&simgui_font_smp_desc); + } + + sg_pop_debug_group(); +} + +SOKOL_API_IMPL void simgui_create_fonts_texture(const simgui_font_tex_desc_t* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT(SG_INVALID_ID == _simgui.font_smp.id); + SOKOL_ASSERT(SG_INVALID_ID == _simgui.font_img.id); + SOKOL_ASSERT(SIMGUI_INVALID_ID == _simgui.default_font.id); + + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + #else + ImGuiIO* io = igGetIO(); + #endif + + // a default font sampler + sg_sampler_desc font_smp_desc; + _simgui_clear(&font_smp_desc, sizeof(font_smp_desc)); + font_smp_desc.wrap_u = SG_WRAP_CLAMP_TO_EDGE; + font_smp_desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE; + font_smp_desc.min_filter = desc->min_filter; + font_smp_desc.mag_filter = desc->mag_filter; + font_smp_desc.mipmap_filter = SG_FILTER_NONE; + font_smp_desc.label = "sokol-imgui-font-sampler"; + _simgui.font_smp = sg_make_sampler(&font_smp_desc); + + unsigned char* font_pixels; + int font_width, font_height; + #if defined(__cplusplus) + io->Fonts->GetTexDataAsRGBA32(&font_pixels, &font_width, &font_height); + #else + int bytes_per_pixel; + ImFontAtlas_GetTexDataAsRGBA32(io->Fonts, &font_pixels, &font_width, &font_height, &bytes_per_pixel); + #endif + sg_image_desc font_img_desc; + _simgui_clear(&font_img_desc, sizeof(font_img_desc)); + font_img_desc.width = font_width; + font_img_desc.height = font_height; + font_img_desc.pixel_format = SG_PIXELFORMAT_RGBA8; + font_img_desc.data.subimage[0][0].ptr = font_pixels; + font_img_desc.data.subimage[0][0].size = (size_t)(font_width * font_height) * sizeof(uint32_t); + font_img_desc.label = "sokol-imgui-font-image"; + _simgui.font_img = sg_make_image(&font_img_desc); + + simgui_image_desc_t img_desc; + _simgui_clear(&img_desc, sizeof(img_desc)); + img_desc.image = _simgui.font_img; + img_desc.sampler = _simgui.font_smp; + _simgui.default_font = simgui_make_image(&img_desc); + io->Fonts->TexID = simgui_imtextureid(_simgui.default_font); +} + +SOKOL_API_IMPL void simgui_destroy_fonts_texture(void) { + // NOTE: it's valid to call the destroy funcs with SG_INVALID_ID + sg_destroy_sampler(_simgui.font_smp); + sg_destroy_image(_simgui.font_img); + simgui_destroy_image(_simgui.default_font); + _simgui.font_smp.id = SG_INVALID_ID; + _simgui.font_img.id = SG_INVALID_ID; + _simgui.default_font.id = SIMGUI_INVALID_ID; +} + +SOKOL_API_IMPL void simgui_shutdown(void) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGui::DestroyContext(); + #else + igDestroyContext(0); + #endif + // NOTE: it's valid to call the destroy funcs with SG_INVALID_ID + sg_destroy_pipeline(_simgui.pip_unfilterable); + sg_destroy_shader(_simgui.shd_unfilterable); + sg_destroy_pipeline(_simgui.def_pip); + sg_destroy_shader(_simgui.def_shd); + sg_destroy_sampler(_simgui.font_smp); + sg_destroy_image(_simgui.font_img); + sg_destroy_sampler(_simgui.def_smp); + sg_destroy_image(_simgui.def_img); + sg_destroy_buffer(_simgui.ibuf); + sg_destroy_buffer(_simgui.vbuf); + sg_pop_debug_group(); + sg_push_debug_group("sokol-imgui"); + _simgui_destroy_all_images(); + _simgui_discard_image_pool(); + SOKOL_ASSERT(_simgui.vertices.ptr); + _simgui_free((void*)_simgui.vertices.ptr); + SOKOL_ASSERT(_simgui.indices.ptr); + _simgui_free((void*)_simgui.indices.ptr); + _simgui.init_cookie = 0; +} + +SOKOL_API_IMPL simgui_image_t simgui_make_image(const simgui_image_desc_t* desc) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + SOKOL_ASSERT(desc); + const simgui_image_desc_t desc_def = _simgui_image_desc_defaults(desc); + simgui_image_t img_id = _simgui_alloc_image(); + _simgui_image_t* img = _simgui_lookup_image(img_id.id); + if (img) { + img->slot.state = _simgui_init_image(img, &desc_def); + SOKOL_ASSERT((img->slot.state == _SIMGUI_RESOURCESTATE_VALID) || (img->slot.state == _SIMGUI_RESOURCESTATE_FAILED)); + } else { + _SIMGUI_ERROR(IMAGE_POOL_EXHAUSTED); + } + return img_id; +} + +SOKOL_API_IMPL void simgui_destroy_image(simgui_image_t img_id) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + _simgui_destroy_image(img_id); +} + +SOKOL_API_IMPL simgui_image_desc_t simgui_query_image_desc(simgui_image_t img_id) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + _simgui_image_t* img = _simgui_lookup_image(img_id.id); + simgui_image_desc_t desc; + _simgui_clear(&desc, sizeof(desc)); + if (img) { + desc.image = img->image; + desc.sampler = img->sampler; + } + return desc; +} + +SOKOL_API_IMPL void* simgui_imtextureid(simgui_image_t img) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + return (void*)(uintptr_t)img.id; +} + +SOKOL_API_IMPL simgui_image_t simgui_image_from_imtextureid(void* im_texture_id) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + simgui_image_t img = { (uint32_t)(uintptr_t) im_texture_id }; + return img; +} + +SOKOL_API_IMPL void simgui_new_frame(const simgui_frame_desc_t* desc) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->width > 0); + SOKOL_ASSERT(desc->height > 0); + _simgui.cur_dpi_scale = _simgui_def(desc->dpi_scale, 1.0f); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + #else + ImGuiIO* io = igGetIO(); + #endif + if (!io->Fonts->TexReady) { + simgui_destroy_fonts_texture(); + simgui_font_tex_desc_t simgui_font_smp_desc; + _simgui_clear(&simgui_font_smp_desc, sizeof(simgui_font_smp_desc)); + simgui_create_fonts_texture(&simgui_font_smp_desc); + } + io->DisplaySize.x = ((float)desc->width) / _simgui.cur_dpi_scale; + io->DisplaySize.y = ((float)desc->height) / _simgui.cur_dpi_scale; + io->DeltaTime = (float)desc->delta_time; + #if !defined(SOKOL_IMGUI_NO_SOKOL_APP) + if (io->WantTextInput && !sapp_keyboard_shown()) { + sapp_show_keyboard(true); + } + if (!io->WantTextInput && sapp_keyboard_shown()) { + sapp_show_keyboard(false); + } + if (!_simgui.desc.disable_set_mouse_cursor) { + #if defined(__cplusplus) + ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); + #else + ImGuiMouseCursor imgui_cursor = igGetMouseCursor(); + #endif + sapp_mouse_cursor cursor = sapp_get_mouse_cursor(); + switch (imgui_cursor) { + case ImGuiMouseCursor_Arrow: cursor = SAPP_MOUSECURSOR_ARROW; break; + case ImGuiMouseCursor_TextInput: cursor = SAPP_MOUSECURSOR_IBEAM; break; + case ImGuiMouseCursor_ResizeAll: cursor = SAPP_MOUSECURSOR_RESIZE_ALL; break; + case ImGuiMouseCursor_ResizeNS: cursor = SAPP_MOUSECURSOR_RESIZE_NS; break; + case ImGuiMouseCursor_ResizeEW: cursor = SAPP_MOUSECURSOR_RESIZE_EW; break; + case ImGuiMouseCursor_ResizeNESW: cursor = SAPP_MOUSECURSOR_RESIZE_NESW; break; + case ImGuiMouseCursor_ResizeNWSE: cursor = SAPP_MOUSECURSOR_RESIZE_NWSE; break; + case ImGuiMouseCursor_Hand: cursor = SAPP_MOUSECURSOR_POINTING_HAND; break; + case ImGuiMouseCursor_NotAllowed: cursor = SAPP_MOUSECURSOR_NOT_ALLOWED; break; + default: break; + } + sapp_set_mouse_cursor(cursor); + } + #endif + #if defined(__cplusplus) + ImGui::NewFrame(); + #else + igNewFrame(); + #endif +} + +static const _simgui_image_t* _simgui_bind_image_sampler(sg_bindings* bindings, ImTextureID tex_id) { + const _simgui_image_t* img = _simgui_lookup_image((uint32_t)(uintptr_t)tex_id); + if (img) { + bindings->fs.images[0] = img->image; + bindings->fs.samplers[0] = img->sampler; + } else { + bindings->fs.images[0] = _simgui.def_img; + bindings->fs.samplers[0] = _simgui.def_smp; + } + return img; +} + +static ImDrawList* _simgui_imdrawlist_at(ImDrawData* draw_data, int cl_index) { + #if defined(__cplusplus) + return draw_data->CmdLists[cl_index]; + #else + return draw_data->CmdLists.Data[cl_index]; + #endif +} + +SOKOL_API_IMPL void simgui_render(void) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGui::Render(); + ImDrawData* draw_data = ImGui::GetDrawData(); + ImGuiIO* io = &ImGui::GetIO(); + #else + igRender(); + ImDrawData* draw_data = igGetDrawData(); + ImGuiIO* io = igGetIO(); + #endif + if (0 == draw_data) { + return; + } + if (draw_data->CmdListsCount == 0) { + return; + } + /* copy vertices and indices into an intermediate buffer so that + they can be updated with a single sg_update_buffer() call each + (sg_append_buffer() has performance problems on some GL platforms), + also keep track of valid number of command lists in case of a + buffer overflow + */ + size_t all_vtx_size = 0; + size_t all_idx_size = 0; + int cmd_list_count = 0; + for (int cl_index = 0; cl_index < draw_data->CmdListsCount; cl_index++, cmd_list_count++) { + ImDrawList* cl = _simgui_imdrawlist_at(draw_data, cl_index); + const size_t vtx_size = (size_t)cl->VtxBuffer.Size * sizeof(ImDrawVert); + const size_t idx_size = (size_t)cl->IdxBuffer.Size * sizeof(ImDrawIdx); + + // check for buffer overflow + if (((all_vtx_size + vtx_size) > _simgui.vertices.size) || + ((all_idx_size + idx_size) > _simgui.indices.size)) + { + break; + } + + // copy vertices and indices into common buffers + if (vtx_size > 0) { + const ImDrawVert* src_vtx_ptr = cl->VtxBuffer.Data; + void* dst_vtx_ptr = (void*) (((uint8_t*)_simgui.vertices.ptr) + all_vtx_size); + memcpy(dst_vtx_ptr, src_vtx_ptr, vtx_size); + } + if (idx_size > 0) { + const ImDrawIdx* src_idx_ptr = cl->IdxBuffer.Data; + void* dst_idx_ptr = (void*) (((uint8_t*)_simgui.indices.ptr) + all_idx_size); + memcpy(dst_idx_ptr, src_idx_ptr, idx_size); + } + all_vtx_size += vtx_size; + all_idx_size += idx_size; + } + if (0 == cmd_list_count) { + return; + } + + // update the sokol-gfx vertex- and index-buffer + sg_push_debug_group("sokol-imgui"); + if (all_vtx_size > 0) { + sg_range vtx_data = _simgui.vertices; + vtx_data.size = all_vtx_size; + sg_update_buffer(_simgui.vbuf, &vtx_data); + } + if (all_idx_size > 0) { + sg_range idx_data = _simgui.indices; + idx_data.size = all_idx_size; + sg_update_buffer(_simgui.ibuf, &idx_data); + } + + // render the ImGui command list + const float dpi_scale = _simgui.cur_dpi_scale; + const int fb_width = (int) (io->DisplaySize.x * dpi_scale); + const int fb_height = (int) (io->DisplaySize.y * dpi_scale); + sg_apply_viewport(0, 0, fb_width, fb_height, true); + sg_apply_scissor_rect(0, 0, fb_width, fb_height, true); + + sg_apply_pipeline(_simgui.def_pip); + _simgui_vs_params_t vs_params; + _simgui_clear((void*)&vs_params, sizeof(vs_params)); + vs_params.disp_size.x = io->DisplaySize.x; + vs_params.disp_size.y = io->DisplaySize.y; + sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(vs_params)); + sg_bindings bind; + _simgui_clear((void*)&bind, sizeof(bind)); + bind.vertex_buffers[0] = _simgui.vbuf; + bind.index_buffer = _simgui.ibuf; + ImTextureID tex_id = io->Fonts->TexID; + _simgui_bind_image_sampler(&bind, tex_id); + int vb_offset = 0; + int ib_offset = 0; + for (int cl_index = 0; cl_index < cmd_list_count; cl_index++) { + ImDrawList* cl = _simgui_imdrawlist_at(draw_data, cl_index); + + bind.vertex_buffer_offsets[0] = vb_offset; + bind.index_buffer_offset = ib_offset; + sg_apply_bindings(&bind); + + #if defined(__cplusplus) + const int num_cmds = cl->CmdBuffer.size(); + #else + const int num_cmds = cl->CmdBuffer.Size; + #endif + uint32_t vtx_offset = 0; + for (int cmd_index = 0; cmd_index < num_cmds; cmd_index++) { + ImDrawCmd* pcmd = &cl->CmdBuffer.Data[cmd_index]; + if (pcmd->UserCallback != 0) { + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + if (pcmd->UserCallback != ImDrawCallback_ResetRenderState) { + pcmd->UserCallback(cl, pcmd); + // need to re-apply all state after calling a user callback + sg_reset_state_cache(); + sg_apply_viewport(0, 0, fb_width, fb_height, true); + sg_apply_pipeline(_simgui.def_pip); + sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(vs_params)); + sg_apply_bindings(&bind); + } + } else { + if ((tex_id != pcmd->TextureId) || (vtx_offset != pcmd->VtxOffset)) { + tex_id = pcmd->TextureId; + vtx_offset = pcmd->VtxOffset; + const _simgui_image_t* img = _simgui_bind_image_sampler(&bind, tex_id); + if (img) { + sg_apply_pipeline(img->pip); + } else { + sg_apply_pipeline(_simgui.def_pip); + } + sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(vs_params)); + bind.vertex_buffer_offsets[0] = vb_offset + (int)(pcmd->VtxOffset * sizeof(ImDrawVert)); + sg_apply_bindings(&bind); + } + const int scissor_x = (int) (pcmd->ClipRect.x * dpi_scale); + const int scissor_y = (int) (pcmd->ClipRect.y * dpi_scale); + const int scissor_w = (int) ((pcmd->ClipRect.z - pcmd->ClipRect.x) * dpi_scale); + const int scissor_h = (int) ((pcmd->ClipRect.w - pcmd->ClipRect.y) * dpi_scale); + sg_apply_scissor_rect(scissor_x, scissor_y, scissor_w, scissor_h, true); + sg_draw((int)pcmd->IdxOffset, (int)pcmd->ElemCount, 1); + } + } + #if defined(__cplusplus) + const size_t vtx_size = (size_t)cl->VtxBuffer.size() * sizeof(ImDrawVert); + const size_t idx_size = (size_t)cl->IdxBuffer.size() * sizeof(ImDrawIdx); + #else + const size_t vtx_size = (size_t)cl->VtxBuffer.Size * sizeof(ImDrawVert); + const size_t idx_size = (size_t)cl->IdxBuffer.Size * sizeof(ImDrawIdx); + #endif + vb_offset += (int)vtx_size; + ib_offset += (int)idx_size; + } + sg_apply_viewport(0, 0, fb_width, fb_height, true); + sg_apply_scissor_rect(0, 0, fb_width, fb_height, true); + sg_pop_debug_group(); +} + +SOKOL_API_IMPL void simgui_add_focus_event(bool focus) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + io->AddFocusEvent(focus); + #else + ImGuiIO* io = igGetIO(); + ImGuiIO_AddFocusEvent(io, focus); + #endif +} + +SOKOL_API_IMPL void simgui_add_mouse_pos_event(float x, float y) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + io->AddMouseSourceEvent(ImGuiMouseSource_Mouse); + #endif + io->AddMousePosEvent(x, y); + #else + ImGuiIO* io = igGetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + ImGuiIO_AddMouseSourceEvent(io, ImGuiMouseSource_Mouse); + #endif + ImGuiIO_AddMousePosEvent(io, x, y); + #endif +} + +SOKOL_API_IMPL void simgui_add_touch_pos_event(float x, float y) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + io->AddMouseSourceEvent(ImGuiMouseSource_TouchScreen); + #endif + io->AddMousePosEvent(x, y); + #else + ImGuiIO* io = igGetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + ImGuiIO_AddMouseSourceEvent(io, ImGuiMouseSource_TouchScreen); + #endif + ImGuiIO_AddMousePosEvent(io, x, y); + #endif +} + +SOKOL_API_IMPL void simgui_add_mouse_button_event(int mouse_button, bool down) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + io->AddMouseSourceEvent(ImGuiMouseSource_Mouse); + #endif + io->AddMouseButtonEvent(mouse_button, down); + #else + ImGuiIO* io = igGetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + ImGuiIO_AddMouseSourceEvent(io, ImGuiMouseSource_Mouse); + #endif + ImGuiIO_AddMouseButtonEvent(io, mouse_button, down); + #endif +} + +SOKOL_API_IMPL void simgui_add_touch_button_event(int mouse_button, bool down) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + io->AddMouseSourceEvent(ImGuiMouseSource_TouchScreen); + #endif + io->AddMouseButtonEvent(mouse_button, down); + #else + ImGuiIO* io = igGetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + ImGuiIO_AddMouseSourceEvent(io, ImGuiMouseSource_TouchScreen); + #endif + ImGuiIO_AddMouseButtonEvent(io, mouse_button, down); + #endif +} + +SOKOL_API_IMPL void simgui_add_mouse_wheel_event(float wheel_x, float wheel_y) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + io->AddMouseSourceEvent(ImGuiMouseSource_Mouse); + #endif + io->AddMouseWheelEvent(wheel_x, wheel_y); + #else + ImGuiIO* io = igGetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + ImGuiIO_AddMouseSourceEvent(io, ImGuiMouseSource_Mouse); + #endif + ImGuiIO_AddMouseWheelEvent(io, wheel_x, wheel_y); + #endif +} + +SOKOL_API_IMPL void simgui_add_key_event(int imgui_key, bool down) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + io->AddKeyEvent((ImGuiKey)imgui_key, down); + #else + ImGuiIO* io = igGetIO(); + ImGuiIO_AddKeyEvent(io, (ImGuiKey)imgui_key, down); + #endif +} + +SOKOL_API_IMPL void simgui_add_input_character(uint32_t c) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + io->AddInputCharacter(c); + #else + ImGuiIO* io = igGetIO(); + ImGuiIO_AddInputCharacter(io, c); + #endif +} + +SOKOL_API_IMPL void simgui_add_input_characters_utf8(const char* c) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + io->AddInputCharactersUTF8(c); + #else + ImGuiIO* io = igGetIO(); + ImGuiIO_AddInputCharactersUTF8(io, c); + #endif +} + +#if !defined(SOKOL_IMGUI_NO_SOKOL_APP) +_SOKOL_PRIVATE bool _simgui_is_ctrl(uint32_t modifiers) { + if (_simgui.is_osx) { + return 0 != (modifiers & SAPP_MODIFIER_SUPER); + } else { + return 0 != (modifiers & SAPP_MODIFIER_CTRL); + } +} + +_SOKOL_PRIVATE ImGuiKey _simgui_map_keycode(sapp_keycode key) { + switch (key) { + case SAPP_KEYCODE_SPACE: return ImGuiKey_Space; + case SAPP_KEYCODE_APOSTROPHE: return ImGuiKey_Apostrophe; + case SAPP_KEYCODE_COMMA: return ImGuiKey_Comma; + case SAPP_KEYCODE_MINUS: return ImGuiKey_Minus; + case SAPP_KEYCODE_PERIOD: return ImGuiKey_Apostrophe; + case SAPP_KEYCODE_SLASH: return ImGuiKey_Slash; + case SAPP_KEYCODE_0: return ImGuiKey_0; + case SAPP_KEYCODE_1: return ImGuiKey_1; + case SAPP_KEYCODE_2: return ImGuiKey_2; + case SAPP_KEYCODE_3: return ImGuiKey_3; + case SAPP_KEYCODE_4: return ImGuiKey_4; + case SAPP_KEYCODE_5: return ImGuiKey_5; + case SAPP_KEYCODE_6: return ImGuiKey_6; + case SAPP_KEYCODE_7: return ImGuiKey_7; + case SAPP_KEYCODE_8: return ImGuiKey_8; + case SAPP_KEYCODE_9: return ImGuiKey_9; + case SAPP_KEYCODE_SEMICOLON: return ImGuiKey_Semicolon; + case SAPP_KEYCODE_EQUAL: return ImGuiKey_Equal; + case SAPP_KEYCODE_A: return ImGuiKey_A; + case SAPP_KEYCODE_B: return ImGuiKey_B; + case SAPP_KEYCODE_C: return ImGuiKey_C; + case SAPP_KEYCODE_D: return ImGuiKey_D; + case SAPP_KEYCODE_E: return ImGuiKey_E; + case SAPP_KEYCODE_F: return ImGuiKey_F; + case SAPP_KEYCODE_G: return ImGuiKey_G; + case SAPP_KEYCODE_H: return ImGuiKey_H; + case SAPP_KEYCODE_I: return ImGuiKey_I; + case SAPP_KEYCODE_J: return ImGuiKey_J; + case SAPP_KEYCODE_K: return ImGuiKey_K; + case SAPP_KEYCODE_L: return ImGuiKey_L; + case SAPP_KEYCODE_M: return ImGuiKey_M; + case SAPP_KEYCODE_N: return ImGuiKey_N; + case SAPP_KEYCODE_O: return ImGuiKey_O; + case SAPP_KEYCODE_P: return ImGuiKey_P; + case SAPP_KEYCODE_Q: return ImGuiKey_Q; + case SAPP_KEYCODE_R: return ImGuiKey_R; + case SAPP_KEYCODE_S: return ImGuiKey_S; + case SAPP_KEYCODE_T: return ImGuiKey_T; + case SAPP_KEYCODE_U: return ImGuiKey_U; + case SAPP_KEYCODE_V: return ImGuiKey_V; + case SAPP_KEYCODE_W: return ImGuiKey_W; + case SAPP_KEYCODE_X: return ImGuiKey_X; + case SAPP_KEYCODE_Y: return ImGuiKey_Y; + case SAPP_KEYCODE_Z: return ImGuiKey_Z; + case SAPP_KEYCODE_LEFT_BRACKET: return ImGuiKey_LeftBracket; + case SAPP_KEYCODE_BACKSLASH: return ImGuiKey_Backslash; + case SAPP_KEYCODE_RIGHT_BRACKET:return ImGuiKey_RightBracket; + case SAPP_KEYCODE_GRAVE_ACCENT: return ImGuiKey_GraveAccent; + case SAPP_KEYCODE_ESCAPE: return ImGuiKey_Escape; + case SAPP_KEYCODE_ENTER: return ImGuiKey_Enter; + case SAPP_KEYCODE_TAB: return ImGuiKey_Tab; + case SAPP_KEYCODE_BACKSPACE: return ImGuiKey_Backspace; + case SAPP_KEYCODE_INSERT: return ImGuiKey_Insert; + case SAPP_KEYCODE_DELETE: return ImGuiKey_Delete; + case SAPP_KEYCODE_RIGHT: return ImGuiKey_RightArrow; + case SAPP_KEYCODE_LEFT: return ImGuiKey_LeftArrow; + case SAPP_KEYCODE_DOWN: return ImGuiKey_DownArrow; + case SAPP_KEYCODE_UP: return ImGuiKey_UpArrow; + case SAPP_KEYCODE_PAGE_UP: return ImGuiKey_PageUp; + case SAPP_KEYCODE_PAGE_DOWN: return ImGuiKey_PageDown; + case SAPP_KEYCODE_HOME: return ImGuiKey_Home; + case SAPP_KEYCODE_END: return ImGuiKey_End; + case SAPP_KEYCODE_CAPS_LOCK: return ImGuiKey_CapsLock; + case SAPP_KEYCODE_SCROLL_LOCK: return ImGuiKey_ScrollLock; + case SAPP_KEYCODE_NUM_LOCK: return ImGuiKey_NumLock; + case SAPP_KEYCODE_PRINT_SCREEN: return ImGuiKey_PrintScreen; + case SAPP_KEYCODE_PAUSE: return ImGuiKey_Pause; + case SAPP_KEYCODE_F1: return ImGuiKey_F1; + case SAPP_KEYCODE_F2: return ImGuiKey_F2; + case SAPP_KEYCODE_F3: return ImGuiKey_F3; + case SAPP_KEYCODE_F4: return ImGuiKey_F4; + case SAPP_KEYCODE_F5: return ImGuiKey_F5; + case SAPP_KEYCODE_F6: return ImGuiKey_F6; + case SAPP_KEYCODE_F7: return ImGuiKey_F7; + case SAPP_KEYCODE_F8: return ImGuiKey_F8; + case SAPP_KEYCODE_F9: return ImGuiKey_F9; + case SAPP_KEYCODE_F10: return ImGuiKey_F10; + case SAPP_KEYCODE_F11: return ImGuiKey_F11; + case SAPP_KEYCODE_F12: return ImGuiKey_F12; + case SAPP_KEYCODE_KP_0: return ImGuiKey_Keypad0; + case SAPP_KEYCODE_KP_1: return ImGuiKey_Keypad1; + case SAPP_KEYCODE_KP_2: return ImGuiKey_Keypad2; + case SAPP_KEYCODE_KP_3: return ImGuiKey_Keypad3; + case SAPP_KEYCODE_KP_4: return ImGuiKey_Keypad4; + case SAPP_KEYCODE_KP_5: return ImGuiKey_Keypad5; + case SAPP_KEYCODE_KP_6: return ImGuiKey_Keypad6; + case SAPP_KEYCODE_KP_7: return ImGuiKey_Keypad7; + case SAPP_KEYCODE_KP_8: return ImGuiKey_Keypad8; + case SAPP_KEYCODE_KP_9: return ImGuiKey_Keypad9; + case SAPP_KEYCODE_KP_DECIMAL: return ImGuiKey_KeypadDecimal; + case SAPP_KEYCODE_KP_DIVIDE: return ImGuiKey_KeypadDivide; + case SAPP_KEYCODE_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; + case SAPP_KEYCODE_KP_SUBTRACT: return ImGuiKey_KeypadSubtract; + case SAPP_KEYCODE_KP_ADD: return ImGuiKey_KeypadAdd; + case SAPP_KEYCODE_KP_ENTER: return ImGuiKey_KeypadEnter; + case SAPP_KEYCODE_KP_EQUAL: return ImGuiKey_KeypadEqual; + case SAPP_KEYCODE_LEFT_SHIFT: return ImGuiKey_LeftShift; + case SAPP_KEYCODE_LEFT_CONTROL: return ImGuiKey_LeftCtrl; + case SAPP_KEYCODE_LEFT_ALT: return ImGuiKey_LeftAlt; + case SAPP_KEYCODE_LEFT_SUPER: return ImGuiKey_LeftSuper; + case SAPP_KEYCODE_RIGHT_SHIFT: return ImGuiKey_RightShift; + case SAPP_KEYCODE_RIGHT_CONTROL:return ImGuiKey_RightCtrl; + case SAPP_KEYCODE_RIGHT_ALT: return ImGuiKey_RightAlt; + case SAPP_KEYCODE_RIGHT_SUPER: return ImGuiKey_RightSuper; + case SAPP_KEYCODE_MENU: return ImGuiKey_Menu; + default: return ImGuiKey_None; + } +} + +_SOKOL_PRIVATE void _simgui_add_sapp_key_event(ImGuiIO* io, sapp_keycode sapp_key, bool down) { + const ImGuiKey imgui_key = _simgui_map_keycode(sapp_key); + #if defined(__cplusplus) + io->AddKeyEvent(imgui_key, down); + #else + ImGuiIO_AddKeyEvent(io, imgui_key, down); + #endif +} + +_SOKOL_PRIVATE void _simgui_add_imgui_key_event(ImGuiIO* io, ImGuiKey imgui_key, bool down) { + #if defined(__cplusplus) + io->AddKeyEvent(imgui_key, down); + #else + ImGuiIO_AddKeyEvent(io, imgui_key, down); + #endif +} + +_SOKOL_PRIVATE void _simgui_update_modifiers(ImGuiIO* io, uint32_t mods) { + _simgui_add_imgui_key_event(io, ImGuiMod_Ctrl, (mods & SAPP_MODIFIER_CTRL) != 0); + _simgui_add_imgui_key_event(io, ImGuiMod_Shift, (mods & SAPP_MODIFIER_SHIFT) != 0); + _simgui_add_imgui_key_event(io, ImGuiMod_Alt, (mods & SAPP_MODIFIER_ALT) != 0); + _simgui_add_imgui_key_event(io, ImGuiMod_Super, (mods & SAPP_MODIFIER_SUPER) != 0); +} + +// returns Ctrl or Super, depending on platform +_SOKOL_PRIVATE ImGuiKey _simgui_copypaste_modifier(void) { + return _simgui.is_osx ? ImGuiMod_Super : ImGuiMod_Ctrl; +} + +SOKOL_API_IMPL int simgui_map_keycode(sapp_keycode keycode) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + return (int)_simgui_map_keycode(keycode); +} + +SOKOL_API_IMPL bool simgui_handle_event(const sapp_event* ev) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + const float dpi_scale = _simgui.cur_dpi_scale; + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + #else + ImGuiIO* io = igGetIO(); + #endif + switch (ev->type) { + case SAPP_EVENTTYPE_FOCUSED: + simgui_add_focus_event(true); + break; + case SAPP_EVENTTYPE_UNFOCUSED: + simgui_add_focus_event(false); + break; + case SAPP_EVENTTYPE_MOUSE_DOWN: + simgui_add_mouse_pos_event(ev->mouse_x / dpi_scale, ev->mouse_y / dpi_scale); + simgui_add_mouse_button_event((int)ev->mouse_button, true); + _simgui_update_modifiers(io, ev->modifiers); + break; + case SAPP_EVENTTYPE_MOUSE_UP: + simgui_add_mouse_pos_event(ev->mouse_x / dpi_scale, ev->mouse_y / dpi_scale); + simgui_add_mouse_button_event((int)ev->mouse_button, false); + _simgui_update_modifiers(io, ev->modifiers); + break; + case SAPP_EVENTTYPE_MOUSE_MOVE: + simgui_add_mouse_pos_event(ev->mouse_x / dpi_scale, ev->mouse_y / dpi_scale); + break; + case SAPP_EVENTTYPE_MOUSE_ENTER: + case SAPP_EVENTTYPE_MOUSE_LEAVE: + // FIXME: since the sokol_app.h emscripten backend doesn't support + // mouse capture, mouse buttons must be released when the mouse leaves the + // browser window, so that they don't "stick" when released outside the window. + // A cleaner solution would be a new sokol_app.h function to query + // "platform behaviour flags". + #if defined(__EMSCRIPTEN__) + for (int i = 0; i < SAPP_MAX_MOUSEBUTTONS; i++) { + simgui_add_mouse_button_event(i, false); + } + #endif + break; + case SAPP_EVENTTYPE_MOUSE_SCROLL: + simgui_add_mouse_wheel_event(ev->scroll_x, ev->scroll_y); + break; + case SAPP_EVENTTYPE_TOUCHES_BEGAN: + simgui_add_touch_pos_event(ev->touches[0].pos_x / dpi_scale, ev->touches[0].pos_y / dpi_scale); + simgui_add_touch_button_event(0, true); + break; + case SAPP_EVENTTYPE_TOUCHES_MOVED: + simgui_add_touch_pos_event(ev->touches[0].pos_x / dpi_scale, ev->touches[0].pos_y / dpi_scale); + break; + case SAPP_EVENTTYPE_TOUCHES_ENDED: + simgui_add_touch_pos_event(ev->touches[0].pos_x / dpi_scale, ev->touches[0].pos_y / dpi_scale); + simgui_add_touch_button_event(0, false); + break; + case SAPP_EVENTTYPE_TOUCHES_CANCELLED: + simgui_add_touch_button_event(0, false); + break; + case SAPP_EVENTTYPE_KEY_DOWN: + _simgui_update_modifiers(io, ev->modifiers); + // intercept Ctrl-V, this is handled via EVENTTYPE_CLIPBOARD_PASTED + if (!_simgui.desc.disable_paste_override) { + if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_V)) { + break; + } + } + // on web platform, don't forward Ctrl-X, Ctrl-V to the browser + if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_X)) { + sapp_consume_event(); + } + if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_C)) { + sapp_consume_event(); + } + // it's ok to add ImGuiKey_None key events + _simgui_add_sapp_key_event(io, ev->key_code, true); + break; + case SAPP_EVENTTYPE_KEY_UP: + _simgui_update_modifiers(io, ev->modifiers); + // intercept Ctrl-V, this is handled via EVENTTYPE_CLIPBOARD_PASTED + if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_V)) { + break; + } + // on web platform, don't forward Ctrl-X, Ctrl-V to the browser + if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_X)) { + sapp_consume_event(); + } + if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_C)) { + sapp_consume_event(); + } + // it's ok to add ImGuiKey_None key events + _simgui_add_sapp_key_event(io, ev->key_code, false); + break; + case SAPP_EVENTTYPE_CHAR: + /* on some platforms, special keys may be reported as + characters, which may confuse some ImGui widgets, + drop those, also don't forward characters if some + modifiers have been pressed + */ + _simgui_update_modifiers(io, ev->modifiers); + if ((ev->char_code >= 32) && + (ev->char_code != 127) && + (0 == (ev->modifiers & (SAPP_MODIFIER_ALT|SAPP_MODIFIER_CTRL|SAPP_MODIFIER_SUPER)))) + { + simgui_add_input_character(ev->char_code); + } + break; + case SAPP_EVENTTYPE_CLIPBOARD_PASTED: + // simulate a Ctrl-V key down/up + if (!_simgui.desc.disable_paste_override) { + _simgui_add_imgui_key_event(io, _simgui_copypaste_modifier(), true); + _simgui_add_imgui_key_event(io, ImGuiKey_V, true); + _simgui_add_imgui_key_event(io, ImGuiKey_V, false); + _simgui_add_imgui_key_event(io, _simgui_copypaste_modifier(), false); + } + break; + default: + break; + } + return io->WantCaptureKeyboard || io->WantCaptureMouse; +} +#endif // SOKOL_IMGUI_NO_SOKOL_APP + +#endif // SOKOL_IMPL diff --git a/thirdparty/sokol/util/sokol_memtrack.h b/thirdparty/sokol/util/sokol_memtrack.h new file mode 100644 index 0000000..47f70fc --- /dev/null +++ b/thirdparty/sokol/util/sokol_memtrack.h @@ -0,0 +1,167 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_MEMTRACK_IMPL) +#define SOKOL_MEMTRACK_IMPL +#endif +#ifndef SOKOL_MEMTRACK_INCLUDED +/* + sokol_memtrack.h -- memory allocation wrapper to track memory usage + of sokol libraries + + Project URL: https://github.com/floooh/sokol + + Optionally provide the following defines with your own implementations: + + SOKOL_MEMTRACK_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_MEMTRACK_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + If sokol_memtrack.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + USAGE + ===== + Just plug the malloc/free wrapper functions into the desc.allocator + struct provided by most sokol header setup functions: + + sg_setup(&(sg_desc){ + //... + .allocator = { + .alloc_fn = smemtrack_alloc, + .free_fn = smemtrack_free, + } + }); + + Then call smemtrack_info() to get information about current number + of allocations and overall allocation size: + + const smemtrack_info_t info = smemtrack_info(); + const int num_allocs = info.num_allocs; + const int num_bytes = info.num_bytes; + + Note the sokol_memtrack.h can only track allocations issued by + the sokol headers, not allocations that happen under the hood + in system libraries. + + LICENSE + ======= + + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_MEMTRACK_INCLUDED (1) +#include +#include // size_t + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_MEMTRACK_API_DECL) +#define SOKOL_MEMTRACK_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_MEMTRACK_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_MEMTRACK_IMPL) +#define SOKOL_MEMTRACK_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_MEMTRACK_API_DECL __declspec(dllimport) +#else +#define SOKOL_MEMTRACK_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct smemtrack_info_t { + int num_allocs; + int num_bytes; +} smemtrack_info_t; + +SOKOL_MEMTRACK_API_DECL smemtrack_info_t smemtrack_info(void); +SOKOL_MEMTRACK_API_DECL void* smemtrack_alloc(size_t size, void* user_data); +SOKOL_MEMTRACK_API_DECL void smemtrack_free(void* ptr, void* user_data); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* SOKOL_MEMTRACK_INCLUDED */ + +/*=== IMPLEMENTATION =========================================================*/ +#ifdef SOKOL_MEMTRACK_IMPL +#define SOKOL_MEMTRACK_IMPL_INCLUDED (1) +#include // malloc, free +#include // memset + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG + #endif +#endif +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif + +// per-allocation header used to keep track of the allocation size +#define _SMEMTRACK_HEADER_SIZE (16) + +static struct { + smemtrack_info_t state; +} _smemtrack; + +SOKOL_API_IMPL void* smemtrack_alloc(size_t size, void* user_data) { + (void)user_data; + uint8_t* ptr = (uint8_t*) malloc(size + _SMEMTRACK_HEADER_SIZE); + if (ptr) { + // store allocation size (for allocation size tracking) + *(size_t*)ptr = size; + _smemtrack.state.num_allocs++; + _smemtrack.state.num_bytes += (int) size; + return ptr + _SMEMTRACK_HEADER_SIZE; + } + else { + // allocation failed, return null pointer + return ptr; + } +} + +SOKOL_API_IMPL void smemtrack_free(void* ptr, void* user_data) { + (void)user_data; + if (ptr) { + uint8_t* alloc_ptr = ((uint8_t*)ptr) - _SMEMTRACK_HEADER_SIZE; + size_t size = *(size_t*)alloc_ptr; + _smemtrack.state.num_allocs--; + _smemtrack.state.num_bytes -= (int) size; + free(alloc_ptr); + } +} + +SOKOL_API_IMPL smemtrack_info_t smemtrack_info(void) { + return _smemtrack.state; +} + +#endif /* SOKOL_MEMTRACK_IMPL */ diff --git a/thirdparty/sokol/util/sokol_nuklear.h b/thirdparty/sokol/util/sokol_nuklear.h new file mode 100644 index 0000000..06229f7 --- /dev/null +++ b/thirdparty/sokol/util/sokol_nuklear.h @@ -0,0 +1,2671 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_NUKLEAR_IMPL) +#define SOKOL_NUKLEAR_IMPL +#endif +#ifndef SOKOL_NUKLEAR_INCLUDED +/* + sokol_nuklear.h -- drop-in Nuklear renderer/event-handler for sokol_gfx.h + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_NUKLEAR_IMPL + + before you include this file in *one* C or C++ file to create the + implementation. + + The following defines are used by the implementation to select the + platform-specific embedded shader code (these are the same defines as + used by sokol_gfx.h and sokol_app.h): + + SOKOL_GLCORE + SOKOL_GLES3 + SOKOL_D3D11 + SOKOL_METAL + SOKOL_WGPU + + Optionally provide the following configuration defines before including the + implementation: + + SOKOL_NUKLEAR_NO_SOKOL_APP - don't depend on sokol_app.h (see below for details) + + Optionally provide the following macros before including the implementation + to override defaults: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_NUKLEAR_API_DECL- public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_NUKLEAR_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + If sokol_nuklear.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_NUKLEAR_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + Include the following headers before sokol_nuklear.h (both before including + the declaration and implementation): + + sokol_gfx.h + sokol_app.h (except SOKOL_NUKLEAR_NO_SOKOL_APP) + nuklear.h + + NOTE: Unlike most other sokol-headers, the implementation must be compiled + as C, compiling as C++ isn't supported. The interface is callable + from C++ of course. + + + FEATURE OVERVIEW: + ================= + sokol_nuklear.h implements the initialization, rendering and event-handling + code for Nuklear (https://github.com/Immediate-Mode-UI/Nuklear) on top of + sokol_gfx.h and (optionally) sokol_app.h. + + The sokol_app.h dependency is optional and used for input event handling. + If you only use sokol_gfx.h but not sokol_app.h in your application, + define SOKOL_NUKLEAR_NO_SOKOL_APP before including the implementation + of sokol_nuklear.h, this will remove any dependency to sokol_app.h, but + you must feed input events into Nuklear yourself. + + sokol_nuklear.h is not thread-safe, all calls must be made from the + same thread where sokol_gfx.h is running. + + HOWTO: + ====== + + --- To initialize sokol-nuklear, call: + + snk_setup(const snk_desc_t* desc) + + This will initialize Nuklear and create sokol-gfx resources + (two buffers for vertices and indices, a font texture and a pipeline- + state-object). + + Use the following snk_desc_t members to configure behaviour: + + int max_vertices + The maximum number of vertices used for UI rendering, default is 65536. + sokol-nuklear will use this to compute the size of the vertex- + and index-buffers allocated via sokol_gfx.h + + int image_pool_size + Number of snk_image_t objects which can be alive at the same time. + The default is 256. + + sg_pixel_format color_format + The color pixel format of the render pass where the UI + will be rendered. The default is SG_PIXELFORMAT_RGBA8 + + sg_pixel_format depth_format + The depth-buffer pixel format of the render pass where + the UI will be rendered. The default is SG_PIXELFORMAT_DEPTHSTENCIL. + + int sample_count + The MSAA sample-count of the render pass where the UI + will be rendered. The default is 1. + + float dpi_scale + DPI scaling factor. Set this to the result of sapp_dpi_scale(). + To render in high resolution on a Retina Mac this would + typically be 2.0. The default value is 1.0 + + bool no_default_font + Set this to true if you don't want to use Nuklear's default + font. In this case you need to initialize the font + yourself after snk_setup() is called. + + --- At the start of a frame, call: + + struct nk_context *snk_new_frame() + + This updates Nuklear's event handling state and then returns + a Nuklear context pointer which you use to build the UI. For + example: + + struct nk_context *ctx = snk_new_frame(); + if (nk_begin(ctx, "Demo", nk_rect(50, 50, 200, 200), ... + + + --- at the end of the frame, before the sg_end_pass() where you + want to render the UI, call: + + snk_render(int width, int height) + + where 'width' and 'height' are the dimensions of the rendering surface. + For example, if you're using sokol_app.h and render to the default + framebuffer: + + snk_render(sapp_width(), sapp_height()); + + This will convert Nuklear's command list into a vertex and index buffer, + and then render that through sokol_gfx.h + + --- if you're using sokol_app.h, from inside the sokol_app.h event callback, + call: + + bool snk_handle_event(const sapp_event* ev); + + This will feed the event into Nuklear's event handling code, and return + true if the event was handled by Nuklear, or false if the event should + be handled by the application. + + --- finally, on application shutdown, call + + snk_shutdown() + + --- Note that for touch-based systems, like iOS, there is a wrapper around + nk_edit_string(...), called snk_edit_string(...) which will show + and hide the onscreen keyboard as required. + + + ON USER-PROVIDED IMAGES AND SAMPLERS + ==================================== + To render your own images via nk_image(), first create an snk_image_t + object from a sokol-gfx image and sampler object. + + // create a sokol-nuklear image object which associates an sg_image with an sg_sampler + snk_image_t snk_img = snk_make_image(&(snk_image_desc_t){ + .image = sg_make_image(...), + .sampler = sg_make_sampler(...), + }); + + // convert the returned image handle into an nk_handle object + struct nk_handle nk_hnd = snk_nkhandle(snk_img); + + // create a nuklear image from the generic handle (note that there a different helper functions for this) + struct nk_image nk_img = nk_image_handle(nk_hnd); + + // finally specify a Nuklear image UI object + nk_image(ctx, nk_img); + + snk_image_t objects are small and cheap (literally just the image and sampler + handle). + + You can omit the sampler handle in the snk_make_image() call, in this case a + default sampler will be used with nearest-filtering and clamp-to-edge. + + Trying to render with an invalid snk_image_t handle will render a small 8x8 + white default texture instead. + + To destroy a sokol-nuklear image object, call + + snk_destroy_image(snk_img); + + But please be aware that the image object needs to be around until snk_render() is called + in a frame (if this turns out to be too much of a hassle we could introduce some sort + of garbage collection where destroyed snk_image_t objects are kept around until + the snk_render() call). + + You can call: + + snk_image_desc_t desc = snk_query_image_desc(img) + + ...to get the original desc struct, useful if you need to get the sokol-gfx image + and sampler handle of the snk_image_t object. + + You can convert an nk_handle back into an snk_image_t handle: + + snk_image_t img = snk_image_from_nkhandle(nk_hnd); + + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + snk_setup(&(snk_desc_t){ + // ... + .allocator = { + .alloc_fn = my_alloc, + .free_fn = my_free, + .user_data = ...; + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_nuklear.h + itself though, not any allocations in Nuklear. + + + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + snk_setup(&(snk_desc_t){ + .logger.func = slog_func + }); + + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'snk' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SNK_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_nuklear.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-nuklear like this: + + snk_setup(&(snk_desc_t){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); + + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. + + + LICENSE + ======= + + zlib/libpng license + + Copyright (c) 2020 Warren Merrifield + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_NUKLEAR_INCLUDED (1) +#include +#include +#include + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_nuklear.h" +#endif +#if !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) && !defined(SOKOL_APP_INCLUDED) +#error "Please include sokol_app.h before sokol_nuklear.h" +#endif +#if !defined(NK_UNDEFINED) +#error "Please include nuklear.h before sokol_nuklear.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_NUKLEAR_API_DECL) +#define SOKOL_NUKLEAR_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_NUKLEAR_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_NUKLEAR_IMPL) +#define SOKOL_NUKLEAR_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_NUKLEAR_API_DECL __declspec(dllimport) +#else +#define SOKOL_NUKLEAR_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + SNK_INVALID_ID = 0, +}; + +/* + snk_image_t + + A combined image-sampler pair used to inject custom images and samplers into Nuklear + + Create with snk_make_image(), and convert to an nk_handle via snk_nkhandle(). +*/ +typedef struct snk_image_t { uint32_t id; } snk_image_t; + +/* + snk_image_desc_t + + Descriptor struct for snk_make_image(). You must provide + at least an sg_image handle. Keeping the sg_sampler handle + zero-initialized will select the builtin default sampler + which uses linear filtering. +*/ +typedef struct snk_image_desc_t { + sg_image image; + sg_sampler sampler; +} snk_image_desc_t; + +/* + snk_log_item + + An enum with a unique item for each log message, warning, error + and validation layer message. +*/ +#define _SNK_LOG_ITEMS \ + _SNK_LOGITEM_XMACRO(OK, "Ok") \ + _SNK_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SNK_LOGITEM_XMACRO(IMAGE_POOL_EXHAUSTED, "image pool exhausted") \ + +#define _SNK_LOGITEM_XMACRO(item,msg) SNK_LOGITEM_##item, +typedef enum snk_log_item_t { + _SNK_LOG_ITEMS +} snk_log_item_t; +#undef _SNK_LOGITEM_XMACRO + +/* + snk_allocator_t + + Used in snk_desc_t to provide custom memory-alloc and -free functions + to sokol_nuklear.h. If memory management should be overridden, both the + alloc_fn and free_fn function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct snk_allocator_t { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} snk_allocator_t; + +/* + snk_logger + + Used in snk_desc_t to provide a logging function. Please be aware + that without logging function, sokol-nuklear will be completely + silent, e.g. it will not report errors, warnings and + validation layer messages. For maximum error verbosity, + compile in debug mode (e.g. NDEBUG *not* defined) and install + a logger (for instance the standard logging function from sokol_log.h). +*/ +typedef struct snk_logger_t { + void (*func)( + const char* tag, // always "snk" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SNK_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_imgui.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} snk_logger_t; + + +typedef struct snk_desc_t { + int max_vertices; // default: 65536 + int image_pool_size; // default: 256 + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; + float dpi_scale; + bool no_default_font; + snk_allocator_t allocator; // optional memory allocation overrides (default: malloc/free) + snk_logger_t logger; // optional log function override +} snk_desc_t; + +SOKOL_NUKLEAR_API_DECL void snk_setup(const snk_desc_t* desc); +SOKOL_NUKLEAR_API_DECL struct nk_context* snk_new_frame(void); +SOKOL_NUKLEAR_API_DECL void snk_render(int width, int height); +SOKOL_NUKLEAR_API_DECL snk_image_t snk_make_image(const snk_image_desc_t* desc); +SOKOL_NUKLEAR_API_DECL void snk_destroy_image(snk_image_t img); +SOKOL_NUKLEAR_API_DECL snk_image_desc_t snk_query_image_desc(snk_image_t img); +SOKOL_NUKLEAR_API_DECL nk_handle snk_nkhandle(snk_image_t img); +SOKOL_NUKLEAR_API_DECL snk_image_t snk_image_from_nkhandle(nk_handle handle); +#if !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) +SOKOL_NUKLEAR_API_DECL bool snk_handle_event(const sapp_event* ev); +SOKOL_NUKLEAR_API_DECL nk_flags snk_edit_string(struct nk_context *ctx, nk_flags flags, char *memory, int *len, int max, nk_plugin_filter filter); +#endif +SOKOL_NUKLEAR_API_DECL void snk_shutdown(void); + +#ifdef __cplusplus +} /* extern "C" */ + +/* reference-based equivalents for C++ */ +inline void snk_setup(const snk_desc_t& desc) { return snk_setup(&desc); } +inline snk_image_t snk_make_image(const snk_image_desc_t& desc) { return snk_make_image(&desc); } + +#endif +#endif /* SOKOL_NUKLEAR_INCLUDED */ + +/*-- IMPLEMENTATION ----------------------------------------------------------*/ +#ifdef SOKOL_NUKLEAR_IMPL +#define SOKOL_NUKLEAR_IMPL_INCLUDED (1) + +#if !defined(NK_INCLUDE_VERTEX_BUFFER_OUTPUT) +#error "Please ensure that NK_INCLUDE_VERTEX_BUFFER_OUTPUT is #defined before including nuklear.h" +#endif + +#ifdef __cplusplus +#error "The sokol_nuklear.h implementation must be compiled as C." +#endif + +#include +#include // memset + +#if defined(__EMSCRIPTEN__) && !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) && !defined(SOKOL_DUMMY_BACKEND) +#include +#endif + +#ifndef SOKOL_API_IMPL +#define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif + +#define _SNK_INIT_COOKIE (0xBABEBABE) +#define _SNK_INVALID_SLOT_INDEX (0) +#define _SNK_SLOT_SHIFT (16) +#define _SNK_MAX_POOL_SIZE (1<<_SNK_SLOT_SHIFT) +#define _SNK_SLOT_MASK (_SNK_MAX_POOL_SIZE-1) + +// helper macros +#define _snk_def(val, def) (((val) == 0) ? (def) : (val)) + +typedef struct _snk_vertex_t { + float pos[2]; + float uv[2]; + uint8_t col[4]; +} _snk_vertex_t; + +typedef struct _snk_vs_params_t { + float disp_size[2]; + uint8_t _pad_8[8]; +} _snk_vs_params_t; + +typedef enum { + _SNK_RESOURCESTATE_INITIAL, + _SNK_RESOURCESTATE_ALLOC, + _SNK_RESOURCESTATE_VALID, + _SNK_RESOURCESTATE_FAILED, + _SNK_RESOURCESTATE_INVALID, + _SNK_RESOURCESTATE_FORCE_U32 = 0x7FFFFFFF +} _snk_resource_state; + +typedef struct { + uint32_t id; + _snk_resource_state state; +} _snk_slot_t; + +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _snk_pool_t; + +typedef struct { + _snk_slot_t slot; + sg_image image; + sg_sampler sampler; +} _snk_image_t; + +typedef struct { + _snk_pool_t pool; + _snk_image_t* items; +} _snk_image_pool_t; + +typedef struct { + uint32_t init_cookie; + snk_desc_t desc; + struct nk_context ctx; + struct nk_font_atlas atlas; + _snk_vs_params_t vs_params; + size_t vertex_buffer_size; + size_t index_buffer_size; + sg_buffer vbuf; + sg_buffer ibuf; + sg_image font_img; + sg_sampler font_smp; + snk_image_t default_font; + sg_image def_img; + sg_sampler def_smp; + sg_shader shd; + sg_pipeline pip; + bool is_osx; // true if running on OSX (or HTML5 OSX), needed for copy/paste + _snk_image_pool_t image_pool; + #if !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) + int mouse_pos[2]; + float mouse_scroll[2]; + bool mouse_did_move; + bool mouse_did_scroll; + bool btn_down[NK_BUTTON_MAX]; + bool btn_up[NK_BUTTON_MAX]; + char char_buffer[NK_INPUT_MAX]; + bool keys_down[NK_KEY_MAX]; + bool keys_up[NK_KEY_MAX]; + #endif +} _snk_state_t; +static _snk_state_t _snuklear; + +/* + Embedded source code compiled with: + + sokol-shdc -i snuk.glsl -o snuk.h -l glsl410:glsl300es:hlsl4:metal_macos:metal_ios:metal_sim:wgsl -b + + (not that for Metal and D3D11 byte code, sokol-shdc must be run + on macOS and Windows) + + @vs vs + uniform vs_params { + vec2 disp_size; + }; + in vec2 position; + in vec2 texcoord0; + in vec4 color0; + out vec2 uv; + out vec4 color; + void main() { + gl_Position = vec4(((position/disp_size)-0.5)*vec2(2.0,-2.0), 0.5, 1.0); + uv = texcoord0; + color = color0; + } + @end + + @fs fs + uniform texture2D tex; + uniform sampler smp; + in vec2 uv; + in vec4 color; + out vec4 frag_color; + void main() { + frag_color = texture(sampler2D(tex, smp), uv) * color; + } + @end + + @program snuk vs fs +*/ +#if defined(SOKOL_GLCORE) +static const uint8_t _snk_vs_source_glsl410[383] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e, + 0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a, + 0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20, + 0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,0x20,0x75,0x76, + 0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f, + 0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x74, + 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x6f, + 0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x6c, + 0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d, + 0x20,0x32,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29, + 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x28,0x28,0x28,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x20,0x2f,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73, + 0x5b,0x30,0x5d,0x2e,0x78,0x79,0x29,0x20,0x2d,0x20,0x76,0x65,0x63,0x32,0x28,0x30, + 0x2e,0x35,0x29,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x32,0x28,0x32,0x2e,0x30,0x2c, + 0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c,0x20,0x30,0x2e,0x35,0x2c,0x20,0x31,0x2e,0x30, + 0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x74,0x65,0x78,0x63, + 0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const uint8_t _snk_fs_source_glsl410[219] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f, + 0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74, + 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32, + 0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x29,0x20,0x2a,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_GLES3) +static const uint8_t _snk_vs_source_glsl300es[344] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f, + 0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29, + 0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,0x20,0x75,0x76,0x3b,0x0a, + 0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20, + 0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x74,0x65,0x78, + 0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c, + 0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69,0x6e,0x20, + 0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f, + 0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20, + 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x76,0x65, + 0x63,0x34,0x28,0x28,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x2f,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78,0x79,0x29, + 0x20,0x2d,0x20,0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x35,0x29,0x29,0x20,0x2a,0x20, + 0x76,0x65,0x63,0x32,0x28,0x32,0x2e,0x30,0x2c,0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c, + 0x20,0x30,0x2e,0x35,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x75,0x76,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const uint8_t _snk_fs_source_glsl300es[250] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d, + 0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69, + 0x6f,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b,0x0a, + 0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e, + 0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x68,0x69,0x67,0x68,0x70,0x20, + 0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x75, + 0x76,0x3b,0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61, + 0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76,0x29,0x20,0x2a,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_METAL) +static const uint8_t _snk_vs_bytecode_metal_macos[3052] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xec,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe0,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x7b,0x12,0x23,0x17,0xd9,0x25,0x1c, + 0x1b,0x42,0x42,0x9f,0xbf,0x31,0xd2,0x2c,0x3a,0x55,0x22,0x1d,0x40,0xd8,0xc4,0xf8, + 0x20,0x49,0x60,0x6d,0x3c,0xea,0x4e,0x1c,0x34,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x37,0x00,0x00,0x00,0x56,0x41,0x54, + 0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x80, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03,0x00,0x04,0x04,0x06, + 0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b, + 0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xc8,0x0a,0x00,0x00,0xff,0xff,0xff,0xff, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xaf,0x02,0x00,0x00,0x0b,0x82,0x20,0x00, + 0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49, + 0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62, + 0x80,0x10,0x45,0x02,0x42,0x92,0x0b,0x42,0x84,0x10,0x32,0x14,0x38,0x08,0x18,0x49, + 0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72, + 0x24,0x07,0xc8,0x08,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00, + 0x51,0x18,0x00,0x00,0x81,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8,0xff,0xff,0xff,0xff, + 0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87,0x71,0xa0,0x07,0x76, + 0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72,0x20,0x87,0x36,0x20, + 0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88,0x07,0x79,0xa0,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0xc0,0x1c,0xc2,0x81, + 0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c, + 0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c,0xd8,0x81,0x1d,0xe6, + 0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87,0x72,0x98,0x87,0x79, + 0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72,0x68,0x03,0x73,0x80, + 0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xdc, + 0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda,0x00,0x1e,0xde,0x21, + 0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21,0x1d,0xda,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30,0x07,0x79,0x08,0x87, + 0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87,0x72,0x90,0x87,0x36, + 0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77,0x68,0x03,0x76,0x28, + 0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70,0x07,0x73,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1,0x1d,0xe0,0xa1,0x0d, + 0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87, + 0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87,0x79,0x68,0x03,0x73, + 0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d,0xca,0x81,0x1c,0xda, + 0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21, + 0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x70,0x87,0x77,0x68, + 0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38,0x87,0x36,0x68,0x87, + 0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0x62,0x1e,0xe8,0x21, + 0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1,0x1c,0xc6,0x81,0x1e, + 0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x0d,0xe6, + 0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87,0x79,0x08,0x07,0x73, + 0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e, + 0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d,0xe6,0xe1,0x1d,0xcc, + 0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00,0x73,0x08,0x07,0x76, + 0x98,0x87,0x72,0x00,0x36,0x18,0x02,0x01,0x2c,0x40,0x05,0x00,0x49,0x18,0x00,0x00, + 0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00,0x16,0x00,0x00,0x00, + 0x32,0x22,0x08,0x09,0x20,0x64,0x85,0x04,0x13,0x22,0xa4,0x84,0x04,0x13,0x22,0xe3, + 0x84,0xa1,0x90,0x14,0x12,0x4c,0x88,0x8c,0x0b,0x84,0x84,0x4c,0x10,0x3c,0x33,0x00, + 0xc3,0x08,0x02,0x30,0x8c,0x40,0x00,0x76,0x08,0x91,0x83,0xa4,0x29,0xa2,0x84,0xc9, + 0xaf,0xa4,0xff,0x01,0x22,0x80,0x91,0x50,0x10,0x83,0x08,0x84,0x50,0x8a,0x89,0x90, + 0x22,0x1b,0x08,0x98,0x23,0x00,0x83,0x14,0xc8,0x39,0x02,0x50,0x18,0x44,0x08,0x84, + 0x61,0x04,0x22,0x19,0x01,0x00,0x00,0x00,0x13,0xb2,0x70,0x48,0x07,0x79,0xb0,0x03, + 0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x76,0x08,0x87,0x71, + 0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83,0x38,0x70,0x03,0x38,0xd8, + 0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06, + 0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a, + 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e,0x73,0x20,0x07,0x7a,0x30, + 0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72, + 0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07, + 0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73, + 0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20, + 0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07, + 0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d, + 0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80, + 0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07, + 0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20,0x07,0x7a,0x20,0x07,0x75, + 0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x72,0x50,0x07,0x76,0xa0, + 0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x50,0x07, + 0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x6d, + 0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xa0, + 0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07, + 0x7a,0x30,0x07,0x72,0x30,0x84,0x39,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0xc8, + 0x02,0x01,0x00,0x00,0x09,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90, + 0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0xca,0x12,0x18,0x01,0x28,0x88,0x22,0x28,0x84, + 0x32,0xa0,0x1d,0x01,0x20,0x1d,0x4b,0x68,0x02,0x00,0x00,0x00,0x79,0x18,0x00,0x00, + 0xea,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9, + 0xb9,0xb4,0x37,0xb7,0x21,0x46,0x42,0x20,0x80,0x82,0x50,0xb9,0x1b,0x43,0x0b,0x93, + 0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x24,0x01,0x22,0x24,0x05,0xe7,0x20,0x08,0x0e, + 0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd, + 0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e, + 0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45,0x26, + 0x65,0x88,0x80,0x10,0x43,0x8c,0x24,0x48,0x86,0x44,0x60,0xd1,0x54,0x46,0x17,0xc6, + 0x36,0x04,0x41,0x8e,0x24,0x48,0x82,0x44,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6, + 0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36, + 0x17,0x26,0xc6,0x56,0x36,0x44,0x40,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d, + 0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65, + 0x6e,0x61,0x62,0x6c,0x65,0x43,0x04,0x64,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd, + 0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95, + 0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d, + 0x11,0x90,0x86,0x51,0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0xd7,0x5b, + 0x1d,0x1d,0x5c,0x1d,0x1d,0x97,0xba,0xb9,0x32,0x39,0x14,0xb6,0xb7,0x31,0x37,0x98, + 0x14,0x46,0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x6e, + 0x61,0x6d,0x65,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0xc8,0x84,0xa5,0xc9,0xb9,0x84, + 0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95,0x51,0xa8,0xb3,0x1b,0xc2,0x20,0x0f,0x02,0x21, + 0x11,0x22,0x21,0x13,0x42,0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b, + 0x49,0xa1,0x61,0xc6,0xf6,0x16,0x46,0x47,0xc3,0x62,0xec,0x8d,0xed,0x4d,0x6e,0x08, + 0x83,0x3c,0x88,0x85,0x44,0xc8,0x85,0x4c,0x08,0x46,0x26,0x2c,0x4d,0xce,0x05,0xee, + 0x6d,0x2e,0x8d,0x2e,0xed,0xcd,0x8d,0xcb,0x19,0xdb,0x17,0xd4,0xdb,0x5c,0x1a,0x5d, + 0xda,0x9b,0xdb,0x10,0x05,0xd1,0x90,0x08,0xb9,0x90,0x09,0xd9,0x86,0x18,0x48,0x85, + 0x64,0x08,0x47,0x28,0x2c,0x4d,0xce,0xc5,0xae,0x4c,0x8e,0xae,0x0c,0xef,0x2b,0xcd, + 0x0d,0xae,0x8e,0x8e,0x52,0x58,0x9a,0x9c,0x0b,0xdb,0xdb,0x58,0x18,0x5d,0xda,0x9b, + 0xdb,0x57,0x9a,0x1b,0x59,0x19,0x1e,0xbd,0xb3,0x32,0xb7,0x32,0xb9,0x30,0xba,0x32, + 0x32,0x94,0xaf,0xaf,0xb0,0x34,0xb9,0x2f,0x38,0xb6,0xb0,0xb1,0x32,0xb4,0x37,0x36, + 0xb2,0x32,0xb9,0xaf,0xaf,0x14,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x43, + 0xa8,0x44,0x40,0x3c,0xe4,0x4b,0x84,0x24,0x40,0xc0,0x00,0x89,0x10,0x09,0x99,0x90, + 0x30,0x60,0x42,0x57,0x86,0x37,0xf6,0xf6,0x26,0x47,0x06,0x33,0x84,0x4a,0x02,0xc4, + 0x43,0xbe,0x24,0x48,0x02,0x04,0x0c,0x90,0x08,0x91,0x90,0x09,0x19,0x03,0x1a,0x63, + 0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8,0x84,0x40,0x3c,0xe4,0x4b,0x88,0x24,0x40,0xc0, + 0x00,0x89,0x90,0x0b,0x99,0x90,0x32,0xa0,0x12,0x96,0x26,0xe7,0x22,0x56,0x67,0x66, + 0x56,0x26,0xc7,0x27,0x2c,0x4d,0xce,0x45,0xac,0xce,0xcc,0xac,0x4c,0xee,0x6b,0x2e, + 0x4d,0xaf,0x8c,0x48,0x58,0x9a,0x9c,0x8b,0x5c,0x59,0x18,0x19,0xa9,0xb0,0x34,0x39, + 0x97,0x39,0x3a,0xb9,0xba,0x31,0xba,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x34,0x37,0xb3, + 0x37,0x26,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x43,0x94,0x44,0x48,0x86, + 0x44,0x40,0x24,0x64,0x0d,0x18,0x85,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xd1,0xe5, + 0xc1,0x95,0x7d,0xcd,0xa5,0xe9,0x95,0xf1,0x0a,0x4b,0x93,0x73,0x09,0x93,0x3b,0xfb, + 0xa2,0xcb,0x83,0x2b,0xfb,0x0a,0x63,0x4b,0x3b,0x73,0xfb,0x9a,0x4b,0xd3,0x2b,0x63, + 0x62,0x37,0xf7,0x05,0x17,0x26,0x17,0xd6,0x36,0xc7,0xe1,0x4b,0x46,0x66,0x08,0x19, + 0x24,0x06,0x72,0x06,0x08,0x1a,0x24,0x03,0xf2,0x25,0x42,0x12,0x20,0x69,0x80,0xa8, + 0x01,0xc2,0x06,0x48,0x1b,0x24,0x03,0xe2,0x06,0xc9,0x80,0x44,0xc8,0x1b,0x20,0x13, + 0x02,0x07,0x43,0x10,0x44,0x0c,0x10,0x32,0x40,0xcc,0x00,0x89,0x83,0x21,0xc6,0x01, + 0x20,0x1d,0x22,0x07,0x7c,0xde,0xda,0xdc,0xd2,0xe0,0xde,0xe8,0xca,0xdc,0xe8,0x40, + 0xc6,0xd0,0xc2,0xe4,0xf8,0x4c,0xa5,0xb5,0xc1,0xb1,0x95,0x81,0x0c,0xad,0xac,0x80, + 0x50,0x09,0x05,0x05,0x0d,0x11,0x90,0x3a,0x18,0x62,0x20,0x74,0x80,0xd8,0xc1,0x72, + 0x0c,0x31,0x90,0x3b,0x40,0xee,0x60,0x39,0x46,0x44,0xec,0xc0,0x0e,0xf6,0xd0,0x0e, + 0x6e,0xd0,0x0e,0xef,0x40,0x0e,0xf5,0xc0,0x0e,0xe5,0xe0,0x06,0xe6,0xc0,0x0e,0xe1, + 0x70,0x0e,0xf3,0x30,0x45,0x08,0x86,0x11,0x0a,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b, + 0xa4,0x03,0x39,0x94,0x83,0x3b,0xd0,0xc3,0x94,0xa0,0x18,0xb1,0x84,0x43,0x3a,0xc8, + 0x83,0x1b,0xd8,0x43,0x39,0xc8,0xc3,0x3c,0xa4,0xc3,0x3b,0xb8,0xc3,0x94,0xc0,0x18, + 0x41,0x85,0x43,0x3a,0xc8,0x83,0x1b,0xb0,0x43,0x38,0xb8,0xc3,0x39,0xd4,0x43,0x38, + 0x9c,0x43,0x39,0xfc,0x82,0x3d,0x94,0x83,0x3c,0xcc,0x43,0x3a,0xbc,0x83,0x3b,0x4c, + 0x09,0x90,0x11,0x53,0x38,0xa4,0x83,0x3c,0xb8,0xc1,0x38,0xbc,0x43,0x3b,0xc0,0x43, + 0x3a,0xb0,0x43,0x39,0xfc,0xc2,0x3b,0xc0,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x3c, + 0x4c,0x19,0x14,0xc6,0x19,0xa1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39,0xc8, + 0x03,0x3d,0x94,0x03,0x3e,0x4c,0x09,0xe6,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00, + 0x7b,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88, + 0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6, + 0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce, + 0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8, + 0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48, + 0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11, + 0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e, + 0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89, + 0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b, + 0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37, + 0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78, + 0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81, + 0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1, + 0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c, + 0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39, + 0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc, + 0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58, + 0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87, + 0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f, + 0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e, + 0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66, + 0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b, + 0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0, + 0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e, + 0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc, + 0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3, + 0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07, + 0x7a,0x28,0x07,0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e, + 0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e, + 0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d, + 0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,0x00, + 0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00, + 0x61,0x20,0x00,0x00,0x23,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0xd4,0x63,0x11,0x40,0x60,0x1c,0x73,0x10,0x42,0xf0,0x3c,0x94, + 0x33,0x00,0x14,0x63,0x09,0x20,0x08,0x82,0xf0,0x2f,0x80,0x20,0x08,0xc2,0xbf,0x30, + 0x96,0x00,0x82,0x20,0x08,0x82,0x01,0x08,0x82,0x20,0x08,0x0e,0x33,0x00,0x24,0x73, + 0x10,0xd7,0x65,0x55,0x34,0x33,0x00,0x04,0x63,0x04,0x20,0x08,0x82,0xf8,0x37,0x46, + 0x00,0x82,0x20,0x08,0x7f,0x33,0x00,0x00,0xe3,0x0d,0x4c,0x64,0x51,0x40,0x2c,0x0a, + 0xe8,0x63,0xc1,0x02,0x1f,0x0b,0x16,0xf9,0x0c,0x32,0x04,0xcb,0x33,0xc8,0x10,0x2c, + 0xd1,0x6c,0xc3,0x52,0x01,0xb3,0x0d,0x41,0x15,0xcc,0x36,0x04,0x83,0x90,0x41,0x40, + 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x5b,0x86,0x20,0xc0,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _snk_fs_bytecode_metal_macos[2809] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf9,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xa1,0xce,0x6b,0xd1,0x1f,0x32,0x9e, + 0x8d,0x8d,0x1c,0xcc,0x19,0xcb,0xd3,0xb6,0x21,0x99,0x0b,0xb6,0x46,0x8b,0x87,0x98, + 0x8e,0x2d,0xb5,0x98,0x92,0x0a,0x81,0x7d,0xf3,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44, + 0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0x0c,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x80,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32, + 0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45, + 0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44, + 0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8, + 0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00, + 0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07,0x7a,0x60,0x87,0x7c, + 0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72,0x68,0x03,0x72,0x48, + 0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90,0x07,0x7a,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc,0x21,0x1c,0xd8,0x61, + 0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81,0x1d,0xca,0xa1,0x0d, + 0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d,0xd8,0x61,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87,0x79,0x98,0x87,0x36, + 0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36,0x30,0x07,0x78,0x68, + 0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0xc2,0x1d,0xde, + 0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0,0xe1,0x1d,0xd2,0xc1, + 0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1,0x1d,0x00,0x7a,0x90, + 0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90,0x87,0x70,0x68,0x87, + 0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07,0x79,0x68,0x83,0x72, + 0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36,0x60,0x87,0x72,0x08, + 0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30,0x87,0x79,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1, + 0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01,0x1e,0xda,0x80,0x1e, + 0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07, + 0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87,0x36,0x30,0x07,0x78, + 0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20, + 0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c,0xc8,0xa1,0x0d,0xf4, + 0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda,0xa0,0x1d,0xc2,0x81, + 0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0xa0, + 0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68,0x83,0x76,0x08,0x07, + 0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6,0x81,0x1e,0xc2,0x61, + 0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d, + 0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e,0xda,0x60,0x1e,0xd2, + 0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87,0x70,0x30,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e, + 0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e,0xde,0xc1,0x1c,0xe8, + 0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87,0x70,0x60,0x87,0x79, + 0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81,0x00,0x16,0xa0,0xda, + 0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5,0x06,0xa3,0x08,0x80, + 0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00,0x00,0x03,0x00,0x00, + 0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00,0x00,0x89,0x20,0x00, + 0x00,0x1d,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4, + 0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4, + 0x4c,0x10,0x48,0x33,0x00,0xc3,0x08,0x04,0x60,0x83,0x30,0x8c,0x20,0x00,0x47,0x49, + 0x53,0x44,0x09,0x93,0xff,0x4f,0xc4,0x35,0x51,0x11,0xf1,0xdb,0xc3,0x3f,0x8d,0x11, + 0x00,0x83,0x08,0x44,0x70,0x91,0x34,0x45,0x94,0x30,0xf9,0xbf,0x04,0x30,0xcf,0x42, + 0x44,0xff,0x34,0x46,0x00,0x0c,0x22,0x18,0x42,0x29,0xc4,0x08,0xe5,0x10,0x9a,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0xca,0x19,0x8a,0x29,0x40,0x6d, + 0x20,0x20,0x05,0xd6,0x08,0x00,0x00,0x00,0x00,0x13,0xb2,0x70,0x48,0x07,0x79,0xb0, + 0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x76,0x08,0x87, + 0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83,0x38,0x70,0x03,0x38, + 0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0, + 0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x71,0x60,0x07, + 0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e,0x73,0x20,0x07,0x7a, + 0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60, + 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0, + 0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07, + 0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73, + 0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40, + 0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07, + 0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72, + 0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0, + 0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20,0x07,0x7a,0x20,0x07, + 0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x72,0x50,0x07,0x76, + 0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x50, + 0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07, + 0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74, + 0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60, + 0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x49,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00, + 0x18,0xc2,0x38,0x40,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x64,0x81,0x00,0x00,0x00, + 0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26, + 0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70, + 0x2c,0xa1,0x09,0x00,0x00,0x79,0x18,0x00,0x00,0xb9,0x00,0x00,0x00,0x1a,0x03,0x4c, + 0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x42, + 0x3c,0x00,0x84,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62, + 0x2c,0xc2,0x23,0x2c,0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e, + 0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6, + 0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06, + 0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0xf0,0x10,0x43,0x8c,0x45, + 0x58,0x8c,0x65,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x79,0x8e,0x45,0x58,0x84, + 0x65,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56, + 0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78, + 0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61, + 0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x84, + 0x67,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98, + 0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1, + 0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x9e,0x86,0x51,0x58,0x9a,0x9c, + 0x8b,0x5c,0x99,0x1b,0x59,0x99,0xdc,0x17,0x5d,0x98,0xdc,0x59,0x19,0x1d,0xa3,0xb0, + 0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0x2f,0xb7,0xb0,0xb6,0x32, + 0x1a,0x66,0x6c,0x6f,0x61,0x74,0x34,0x64,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe, + 0xdc,0xc2,0xda,0xca,0xa8,0x98,0xc9,0x85,0x9d,0x7d,0x8d,0xbd,0xb1,0xbd,0xc9,0x0d, + 0x61,0x9e,0x67,0x19,0x1e,0xe8,0x89,0x1e,0xe9,0x99,0x86,0x08,0x0f,0x45,0x29,0x2c, + 0x4d,0xce,0xc5,0x4c,0x2e,0xec,0xac,0xad,0xcc,0x8d,0xee,0x2b,0xcd,0x0d,0xae,0x8e, + 0x8e,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x1b,0x4c,0x0a,0x95,0xb0,0x34, + 0x39,0x97,0xb1,0x32,0x37,0xba,0x32,0x39,0x3e,0x61,0x69,0x72,0x2e,0x70,0x65,0x72, + 0x73,0x70,0x65,0x63,0x74,0x69,0x76,0x65,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x28, + 0xd4,0xd9,0x0d,0x91,0x96,0xe1,0xb1,0x9e,0xeb,0xc1,0x9e,0xec,0x81,0x1e,0xed,0x91, + 0x9e,0x8d,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x5b,0x4c,0x0a,0x8b,0xb1, + 0x37,0xb6,0x37,0xb9,0x21,0xd2,0x22,0x3c,0xd6,0xd3,0x3d,0xd8,0x93,0x3d,0xd0,0x13, + 0x3d,0xd2,0xe3,0x71,0x09,0x4b,0x93,0x73,0xa1,0x2b,0xc3,0xa3,0xab,0x93,0x2b,0xa3, + 0x14,0x96,0x26,0xe7,0xc2,0xf6,0x36,0x16,0x46,0x97,0xf6,0xe6,0xf6,0x95,0xe6,0x46, + 0x56,0x86,0x47,0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e,0xad,0x8c,0x18,0x5d, + 0x19,0x1e,0x5d,0x9d,0x5c,0x99,0x0c,0x19,0x8f,0x19,0xdb,0x5b,0x18,0x1d,0x0b,0xc8, + 0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x0f,0x07,0xba,0x32,0xbc,0x21,0xd4,0x42,0x3c,0x60, + 0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x23,0x06,0x0f,0xf4,0x8c,0xc1,0x23,0x3d,0x64,0xc0, + 0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x8e,0xc7,0x5c,0x58,0x1b, + 0x1c,0x5b,0x99,0x1c,0x87,0xb9,0x36,0xb8,0x21,0xd2,0x72,0x3c,0x66,0xf0,0x84,0xc1, + 0x32,0x2c,0xc2,0x03,0x3d,0x67,0xf0,0x48,0x0f,0x1a,0x0c,0x41,0x1e,0xee,0xf9,0x9e, + 0x32,0x78,0xd2,0x60,0x88,0x91,0x00,0x4f,0xf5,0xa8,0xc1,0x88,0x88,0x1d,0xd8,0xc1, + 0x1e,0xda,0xc1,0x0d,0xda,0xe1,0x1d,0xc8,0xa1,0x1e,0xd8,0xa1,0x1c,0xdc,0xc0,0x1c, + 0xd8,0x21,0x1c,0xce,0x61,0x1e,0xa6,0x08,0xc1,0x30,0x42,0x61,0x07,0x76,0xb0,0x87, + 0x76,0x70,0x83,0x74,0x20,0x87,0x72,0x70,0x07,0x7a,0x98,0x12,0x14,0x23,0x96,0x70, + 0x48,0x07,0x79,0x70,0x03,0x7b,0x28,0x07,0x79,0x98,0x87,0x74,0x78,0x07,0x77,0x98, + 0x12,0x18,0x23,0xa8,0x70,0x48,0x07,0x79,0x70,0x03,0x76,0x08,0x07,0x77,0x38,0x87, + 0x7a,0x08,0x87,0x73,0x28,0x87,0x5f,0xb0,0x87,0x72,0x90,0x87,0x79,0x48,0x87,0x77, + 0x70,0x87,0x29,0x01,0x32,0x62,0x0a,0x87,0x74,0x90,0x07,0x37,0x18,0x87,0x77,0x68, + 0x07,0x78,0x48,0x07,0x76,0x28,0x87,0x5f,0x78,0x07,0x78,0xa0,0x87,0x74,0x78,0x07, + 0x77,0x98,0x87,0x29,0x83,0xc2,0x38,0x23,0x98,0x70,0x48,0x07,0x79,0x70,0x03,0x73, + 0x90,0x87,0x70,0x38,0x87,0x76,0x28,0x07,0x77,0xa0,0x87,0x29,0xc1,0x1a,0x00,0x00, + 0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c, + 0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07, + 0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42, + 0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83, + 0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07, + 0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70, + 0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3, + 0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2, + 0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc, + 0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68, + 0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07, + 0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72, + 0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec, + 0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc, + 0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8, + 0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03, + 0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c, + 0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74, + 0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4, + 0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc, + 0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1, + 0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50, + 0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51, + 0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21, + 0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06, + 0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c, + 0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77, + 0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec, + 0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8, + 0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4, + 0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43, + 0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x08,0x00,0x00,0x00,0x16,0xb0,0x01, + 0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e, + 0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20,0x0d,0x0d,0x00,0x00,0x00,0x61,0x20,0x00, + 0x00,0x0c,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x04,0x00,0x00, + 0x00,0xc4,0x46,0x00,0x48,0x8d,0x00,0xd4,0x00,0x89,0x19,0x00,0x02,0x23,0x00,0x00, + 0x00,0x23,0x06,0x8a,0x10,0x44,0x87,0x91,0x0c,0x05,0x11,0x58,0x90,0xc8,0x67,0xb6, + 0x81,0x08,0x80,0x0c,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _snk_vs_bytecode_metal_ios[3052] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xec,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe0,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x69,0x97,0x6b,0xac,0xd8,0xa2,0x51, + 0x33,0x7c,0x5f,0x96,0xb2,0xb1,0x06,0x06,0x7c,0xbb,0x5f,0x88,0xa0,0xeb,0x9f,0xea, + 0x6e,0x6b,0x70,0xa9,0x6e,0xef,0xe6,0xa4,0xea,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x37,0x00,0x00,0x00,0x56,0x41,0x54, + 0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x80, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03,0x00,0x04,0x04,0x06, + 0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b, + 0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xc0,0x0a,0x00,0x00,0xff,0xff,0xff,0xff, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xad,0x02,0x00,0x00,0x0b,0x82,0x20,0x00, + 0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49, + 0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62, + 0x80,0x10,0x45,0x02,0x42,0x92,0x0b,0x42,0x84,0x10,0x32,0x14,0x38,0x08,0x18,0x49, + 0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72, + 0x24,0x07,0xc8,0x08,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00, + 0x51,0x18,0x00,0x00,0x82,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8,0xff,0xff,0xff,0xff, + 0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87,0x71,0xa0,0x07,0x76, + 0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72,0x20,0x87,0x36,0x20, + 0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88,0x07,0x79,0xa0,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0xc0,0x1c,0xc2,0x81, + 0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c, + 0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c,0xd8,0x81,0x1d,0xe6, + 0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87,0x72,0x98,0x87,0x79, + 0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72,0x68,0x03,0x73,0x80, + 0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xdc, + 0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda,0x00,0x1e,0xde,0x21, + 0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21,0x1d,0xda,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30,0x07,0x79,0x08,0x87, + 0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87,0x72,0x90,0x87,0x36, + 0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77,0x68,0x03,0x76,0x28, + 0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70,0x07,0x73,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1,0x1d,0xe0,0xa1,0x0d, + 0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87, + 0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87,0x79,0x68,0x03,0x73, + 0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d,0xca,0x81,0x1c,0xda, + 0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21, + 0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x70,0x87,0x77,0x68, + 0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38,0x87,0x36,0x68,0x87, + 0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0x62,0x1e,0xe8,0x21, + 0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1,0x1c,0xc6,0x81,0x1e, + 0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x0d,0xe6, + 0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87,0x79,0x08,0x07,0x73, + 0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e, + 0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d,0xe6,0xe1,0x1d,0xcc, + 0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00,0x73,0x08,0x07,0x76, + 0x98,0x87,0x72,0x00,0x36,0x20,0x02,0x01,0x24,0xc0,0x02,0x54,0x00,0x00,0x00,0x00, + 0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00, + 0x16,0x00,0x00,0x00,0x32,0x22,0x08,0x09,0x20,0x64,0x85,0x04,0x13,0x22,0xa4,0x84, + 0x04,0x13,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x88,0x8c,0x0b,0x84,0x84,0x4c, + 0x10,0x3c,0x33,0x00,0xc3,0x08,0x02,0x30,0x8c,0x40,0x00,0x76,0x08,0x91,0x83,0xa4, + 0x29,0xa2,0x84,0xc9,0xaf,0xa4,0xff,0x01,0x22,0x80,0x91,0x50,0x10,0x83,0x08,0x84, + 0x50,0x8a,0x89,0x90,0x22,0x1b,0x08,0x98,0x23,0x00,0x83,0x14,0xc8,0x39,0x02,0x50, + 0x18,0x44,0x08,0x84,0x61,0x04,0x22,0x19,0x01,0x00,0x00,0x00,0x13,0xa8,0x70,0x48, + 0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83, + 0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83,0x0d,0xb7,0x51,0x0e, + 0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78,0xa0, + 0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07, + 0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a, + 0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60, + 0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0, + 0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06, + 0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6, + 0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x60, + 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x90,0x07, + 0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6, + 0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80, + 0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07, + 0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75, + 0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75,0x10,0x07,0x72,0xa0, + 0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0,0x06,0xf6,0x10,0x07, + 0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70, + 0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73,0x20, + 0x07,0x43,0x98,0x03,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x2c,0x10,0x00,0x00, + 0x09,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47, + 0xc6,0x04,0x43,0xca,0x12,0x18,0x01,0x28,0x88,0x22,0x28,0x84,0x32,0xa0,0x1d,0x01, + 0x20,0x1d,0x4b,0x80,0x04,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xe9,0x00,0x00,0x00, + 0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7, + 0x21,0x46,0x42,0x20,0x80,0x82,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3, + 0x2b,0x1b,0x62,0x24,0x01,0x22,0x24,0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4, + 0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06, + 0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac, + 0x65,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0x80,0x10, + 0x43,0x8c,0x24,0x48,0x86,0x44,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x41,0x8e, + 0x24,0x48,0x82,0x44,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56, + 0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56, + 0x36,0x44,0x40,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65, + 0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c, + 0x65,0x43,0x04,0x64,0x21,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1, + 0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95, + 0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85,0x89,0xb1,0x95,0x0d,0x11,0x90,0x86,0x51,0x58, + 0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0xd7,0x5b,0x1d,0x1d,0x5c,0x1d,0x1d, + 0x97,0xba,0xb9,0x32,0x39,0x14,0xb6,0xb7,0x31,0x37,0x98,0x14,0x46,0x61,0x69,0x72, + 0x2e,0x61,0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x6e,0x61,0x6d,0x65,0x34,0xcc, + 0xd8,0xde,0xc2,0xe8,0x64,0xc8,0x84,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85, + 0xb5,0x95,0x51,0xa8,0xb3,0x1b,0xc2,0x20,0x0f,0x02,0x21,0x11,0x22,0x21,0x13,0x42, + 0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0xa1,0x61,0xc6,0xf6, + 0x16,0x46,0x47,0xc3,0x62,0xec,0x8d,0xed,0x4d,0x6e,0x08,0x83,0x3c,0x88,0x85,0x44, + 0xc8,0x85,0x4c,0x08,0x46,0x26,0x2c,0x4d,0xce,0x05,0xee,0x6d,0x2e,0x8d,0x2e,0xed, + 0xcd,0x8d,0xcb,0x19,0xdb,0x17,0xd4,0xdb,0x5c,0x1a,0x5d,0xda,0x9b,0xdb,0x10,0x05, + 0xd1,0x90,0x08,0xb9,0x90,0x09,0xd9,0x86,0x18,0x48,0x85,0x64,0x08,0x47,0x28,0x2c, + 0x4d,0xce,0xc5,0xae,0x4c,0x8e,0xae,0x0c,0xef,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x52, + 0x58,0x9a,0x9c,0x0b,0xdb,0xdb,0x58,0x18,0x5d,0xda,0x9b,0xdb,0x57,0x9a,0x1b,0x59, + 0x19,0x1e,0xbd,0xb3,0x32,0xb7,0x32,0xb9,0x30,0xba,0x32,0x32,0x94,0xaf,0xaf,0xb0, + 0x34,0xb9,0x2f,0x38,0xb6,0xb0,0xb1,0x32,0xb4,0x37,0x36,0xb2,0x32,0xb9,0xaf,0xaf, + 0x14,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x43,0xa8,0x44,0x40,0x3c,0xe4, + 0x4b,0x84,0x24,0x40,0xc0,0x00,0x89,0x10,0x09,0x99,0x90,0x30,0x60,0x42,0x57,0x86, + 0x37,0xf6,0xf6,0x26,0x47,0x06,0x33,0x84,0x4a,0x02,0xc4,0x43,0xbe,0x24,0x48,0x02, + 0x04,0x0c,0x90,0x08,0x91,0x90,0x09,0x19,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30, + 0x43,0xa8,0x84,0x40,0x3c,0xe4,0x4b,0x88,0x24,0x40,0xc0,0x00,0x89,0x90,0x0b,0x99, + 0x90,0x32,0xa0,0x12,0x96,0x26,0xe7,0x22,0x56,0x67,0x66,0x56,0x26,0xc7,0x27,0x2c, + 0x4d,0xce,0x45,0xac,0xce,0xcc,0xac,0x4c,0xee,0x6b,0x2e,0x4d,0xaf,0x8c,0x48,0x58, + 0x9a,0x9c,0x8b,0x5c,0x59,0x18,0x19,0xa9,0xb0,0x34,0x39,0x97,0x39,0x3a,0xb9,0xba, + 0x31,0xba,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x34,0x37,0xb3,0x37,0x26,0x64,0x69,0x73, + 0x70,0x5f,0x73,0x69,0x7a,0x65,0x43,0x94,0x44,0x48,0x86,0x44,0x40,0x24,0x64,0x0d, + 0x18,0x85,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xd1,0xe5,0xc1,0x95,0x7d,0xcd,0xa5, + 0xe9,0x95,0xf1,0x0a,0x4b,0x93,0x73,0x09,0x93,0x3b,0xfb,0xa2,0xcb,0x83,0x2b,0xfb, + 0x0a,0x63,0x4b,0x3b,0x73,0xfb,0x9a,0x4b,0xd3,0x2b,0x63,0x62,0x37,0xf7,0x05,0x17, + 0x26,0x17,0xd6,0x36,0xc7,0xe1,0x4b,0x46,0x66,0x08,0x19,0x24,0x06,0x72,0x06,0x08, + 0x1a,0x24,0x03,0xf2,0x25,0x42,0x12,0x20,0x69,0x80,0xa8,0x01,0xc2,0x06,0x48,0x1b, + 0x24,0x03,0xe2,0x06,0xc9,0x80,0x44,0xc8,0x1b,0x20,0x13,0x02,0x07,0x43,0x10,0x44, + 0x0c,0x10,0x32,0x40,0xcc,0x00,0x89,0x83,0x21,0xc6,0x01,0x20,0x1d,0x22,0x07,0x7c, + 0xde,0xda,0xdc,0xd2,0xe0,0xde,0xe8,0xca,0xdc,0xe8,0x40,0xc6,0xd0,0xc2,0xe4,0xf8, + 0x4c,0xa5,0xb5,0xc1,0xb1,0x95,0x81,0x0c,0xad,0xac,0x80,0x50,0x09,0x05,0x05,0x0d, + 0x11,0x90,0x3a,0x18,0x62,0x20,0x74,0x80,0xd8,0xc1,0x72,0x0c,0x31,0x90,0x3b,0x40, + 0xee,0x60,0x39,0x46,0x44,0xec,0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0xd0,0x0e,0xef,0x40, + 0x0e,0xf5,0xc0,0x0e,0xe5,0xe0,0x06,0xe6,0xc0,0x0e,0xe1,0x70,0x0e,0xf3,0x30,0x45, + 0x08,0x86,0x11,0x0a,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0xa4,0x03,0x39,0x94,0x83, + 0x3b,0xd0,0xc3,0x94,0xa0,0x18,0xb1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39, + 0xc8,0xc3,0x3c,0xa4,0xc3,0x3b,0xb8,0xc3,0x94,0xc0,0x18,0x41,0x85,0x43,0x3a,0xc8, + 0x83,0x1b,0xb0,0x43,0x38,0xb8,0xc3,0x39,0xd4,0x43,0x38,0x9c,0x43,0x39,0xfc,0x82, + 0x3d,0x94,0x83,0x3c,0xcc,0x43,0x3a,0xbc,0x83,0x3b,0x4c,0x09,0x90,0x11,0x53,0x38, + 0xa4,0x83,0x3c,0xb8,0xc1,0x38,0xbc,0x43,0x3b,0xc0,0x43,0x3a,0xb0,0x43,0x39,0xfc, + 0xc2,0x3b,0xc0,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x3c,0x4c,0x19,0x14,0xc6,0x19, + 0xa1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39,0xc8,0x03,0x3d,0x94,0x03,0x3e, + 0x4c,0x09,0xe6,0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80,0x1c, + 0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07, + 0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e, + 0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43, + 0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c, + 0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76, + 0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e, + 0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8, + 0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4, + 0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68, + 0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07, + 0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71, + 0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5, + 0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4, + 0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90, + 0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43, + 0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b, + 0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78, + 0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2, + 0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20, + 0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0, + 0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83, + 0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07, + 0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61, + 0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d, + 0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39, + 0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79, + 0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c,0xe3, + 0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4, + 0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8, + 0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83, + 0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00, + 0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x23,0x00,0x00,0x00, + 0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0xd4,0x63,0x11,0x40, + 0x60,0x1c,0x73,0x10,0x42,0xf0,0x3c,0x94,0x33,0x00,0x14,0x63,0x09,0x20,0x08,0x82, + 0xf0,0x2f,0x80,0x20,0x08,0xc2,0xbf,0x30,0x96,0x00,0x82,0x20,0x08,0x82,0x01,0x08, + 0x82,0x20,0x08,0x0e,0x33,0x00,0x24,0x73,0x10,0xd7,0x65,0x55,0x34,0x33,0x00,0x04, + 0x63,0x04,0x20,0x08,0x82,0xf8,0x37,0x46,0x00,0x82,0x20,0x08,0x7f,0x33,0x00,0x00, + 0xe3,0x0d,0x4c,0x64,0x51,0x40,0x2c,0x0a,0xe8,0x63,0xc1,0x02,0x1f,0x0b,0x16,0xf9, + 0x0c,0x32,0x04,0xcb,0x33,0xc8,0x10,0x2c,0xd1,0x6c,0xc3,0x52,0x01,0xb3,0x0d,0x41, + 0x15,0xcc,0x36,0x04,0x83,0x90,0x41,0x40,0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x5b,0x86,0x20,0xc0,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _snk_fs_bytecode_metal_ios[2809] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf9,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xf0,0xa4,0xb3,0x95,0x4b,0xab,0x64, + 0x94,0xe7,0xa9,0x8a,0x69,0x27,0x6d,0x28,0x77,0x84,0x8d,0x3f,0xaf,0x7d,0x3c,0x39, + 0x31,0xc4,0xcb,0x53,0x6d,0xc0,0x0d,0xdf,0x08,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44, + 0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0x04,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x7e,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32, + 0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45, + 0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44, + 0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8, + 0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00, + 0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07,0x7a,0x60,0x87,0x7c, + 0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72,0x68,0x03,0x72,0x48, + 0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90,0x07,0x7a,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc,0x21,0x1c,0xd8,0x61, + 0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81,0x1d,0xca,0xa1,0x0d, + 0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d,0xd8,0x61,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87,0x79,0x98,0x87,0x36, + 0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36,0x30,0x07,0x78,0x68, + 0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0xc2,0x1d,0xde, + 0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0,0xe1,0x1d,0xd2,0xc1, + 0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1,0x1d,0x00,0x7a,0x90, + 0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90,0x87,0x70,0x68,0x87, + 0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07,0x79,0x68,0x83,0x72, + 0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36,0x60,0x87,0x72,0x08, + 0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30,0x87,0x79,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1, + 0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01,0x1e,0xda,0x80,0x1e, + 0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07, + 0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87,0x36,0x30,0x07,0x78, + 0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20, + 0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c,0xc8,0xa1,0x0d,0xf4, + 0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda,0xa0,0x1d,0xc2,0x81, + 0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0xa0, + 0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68,0x83,0x76,0x08,0x07, + 0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6,0x81,0x1e,0xc2,0x61, + 0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d, + 0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e,0xda,0x60,0x1e,0xd2, + 0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87,0x70,0x30,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e, + 0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e,0xde,0xc1,0x1c,0xe8, + 0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87,0x70,0x60,0x87,0x79, + 0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81,0x00,0x16,0xa0,0xda, + 0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5,0x06,0xa3,0x08,0x80, + 0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00,0x00,0x03,0x00,0x00, + 0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00,0x00,0x89,0x20,0x00, + 0x00,0x1d,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4, + 0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4, + 0x4c,0x10,0x48,0x33,0x00,0xc3,0x08,0x04,0x60,0x83,0x30,0x8c,0x20,0x00,0x47,0x49, + 0x53,0x44,0x09,0x93,0xff,0x4f,0xc4,0x35,0x51,0x11,0xf1,0xdb,0xc3,0x3f,0x8d,0x11, + 0x00,0x83,0x08,0x44,0x70,0x91,0x34,0x45,0x94,0x30,0xf9,0xbf,0x04,0x30,0xcf,0x42, + 0x44,0xff,0x34,0x46,0x00,0x0c,0x22,0x18,0x42,0x29,0xc4,0x08,0xe5,0x10,0x9a,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0xca,0x19,0x8a,0x29,0x40,0x6d, + 0x20,0x20,0x05,0xd6,0x08,0x00,0x00,0x00,0x00,0x13,0xa8,0x70,0x48,0x07,0x79,0xb0, + 0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x74,0x78,0x87, + 0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83,0x0d,0xb7,0x51,0x0e,0x6d,0x00,0x0f, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe9, + 0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x78,0xa0, + 0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07, + 0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72, + 0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0, + 0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06, + 0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6, + 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xf6,0x20, + 0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x40,0x07,0x78, + 0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x90,0x07,0x76,0xa0,0x07, + 0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x72, + 0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60, + 0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07, + 0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a, + 0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10, + 0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0,0x06,0xf6,0x10,0x07,0x70,0x20,0x07, + 0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74, + 0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x43,0x98, + 0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x8c,0x03,0x04,0x80,0x00,0x00, + 0x00,0x00,0x00,0x40,0x16,0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98, + 0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02, + 0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0x01,0x12,0x00,0x00,0x79,0x18,0x00, + 0x00,0xb9,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32, + 0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x42,0x3c,0x00,0x84,0x50,0xb9,0x1b,0x43,0x0b, + 0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0xc2,0x23,0x2c,0x05,0xe7,0x20,0x08, + 0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed, + 0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c, + 0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45, + 0x26,0x65,0x88,0xf0,0x10,0x43,0x8c,0x45,0x58,0x8c,0x65,0x60,0xd1,0x54,0x46,0x17, + 0xc6,0x36,0x04,0x79,0x8e,0x45,0x58,0x84,0x65,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6, + 0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96, + 0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f, + 0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f, + 0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x84,0x67,0x21,0x19,0x84,0xa5,0xc9,0xb9,0x8c, + 0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99, + 0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85,0x89,0xb1,0x95,0x0d, + 0x11,0x9e,0x86,0x51,0x58,0x9a,0x9c,0x8b,0x5c,0x99,0x1b,0x59,0x99,0xdc,0x17,0x5d, + 0x98,0xdc,0x59,0x19,0x1d,0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c, + 0xb8,0xb2,0x2f,0xb7,0xb0,0xb6,0x32,0x1a,0x66,0x6c,0x6f,0x61,0x74,0x34,0x64,0xc2, + 0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xdc,0xc2,0xda,0xca,0xa8,0x98,0xc9,0x85,0x9d, + 0x7d,0x8d,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x9e,0x67,0x19,0x1e,0xe8,0x89,0x1e,0xe9, + 0x99,0x86,0x08,0x0f,0x45,0x29,0x2c,0x4d,0xce,0xc5,0x4c,0x2e,0xec,0xac,0xad,0xcc, + 0x8d,0xee,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb, + 0x98,0x1b,0x4c,0x0a,0x95,0xb0,0x34,0x39,0x97,0xb1,0x32,0x37,0xba,0x32,0x39,0x3e, + 0x61,0x69,0x72,0x2e,0x70,0x65,0x72,0x73,0x70,0x65,0x63,0x74,0x69,0x76,0x65,0x34, + 0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x28,0xd4,0xd9,0x0d,0x91,0x96,0xe1,0xb1,0x9e,0xeb, + 0xc1,0x9e,0xec,0x81,0x1e,0xed,0x91,0x9e,0x8d,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb, + 0xdb,0x98,0x5b,0x4c,0x0a,0x8b,0xb1,0x37,0xb6,0x37,0xb9,0x21,0xd2,0x22,0x3c,0xd6, + 0xd3,0x3d,0xd8,0x93,0x3d,0xd0,0x13,0x3d,0xd2,0xe3,0x71,0x09,0x4b,0x93,0x73,0xa1, + 0x2b,0xc3,0xa3,0xab,0x93,0x2b,0xa3,0x14,0x96,0x26,0xe7,0xc2,0xf6,0x36,0x16,0x46, + 0x97,0xf6,0xe6,0xf6,0x95,0xe6,0x46,0x56,0x86,0x47,0x25,0x2c,0x4d,0xce,0x65,0x2e, + 0xac,0x0d,0x8e,0xad,0x8c,0x18,0x5d,0x19,0x1e,0x5d,0x9d,0x5c,0x99,0x0c,0x19,0x8f, + 0x19,0xdb,0x5b,0x18,0x1d,0x0b,0xc8,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x0f,0x07,0xba, + 0x32,0xbc,0x21,0xd4,0x42,0x3c,0x60,0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x23,0x06,0x0f, + 0xf4,0x8c,0xc1,0x23,0x3d,0x64,0xc0,0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e, + 0xad,0x4c,0x8e,0xc7,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x87,0xb9,0x36,0xb8,0x21, + 0xd2,0x72,0x3c,0x66,0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x03,0x3d,0x67,0xf0,0x48,0x0f, + 0x1a,0x0c,0x41,0x1e,0xee,0xf9,0x9e,0x32,0x78,0xd2,0x60,0x88,0x91,0x00,0x4f,0xf5, + 0xa8,0xc1,0x88,0x88,0x1d,0xd8,0xc1,0x1e,0xda,0xc1,0x0d,0xda,0xe1,0x1d,0xc8,0xa1, + 0x1e,0xd8,0xa1,0x1c,0xdc,0xc0,0x1c,0xd8,0x21,0x1c,0xce,0x61,0x1e,0xa6,0x08,0xc1, + 0x30,0x42,0x61,0x07,0x76,0xb0,0x87,0x76,0x70,0x83,0x74,0x20,0x87,0x72,0x70,0x07, + 0x7a,0x98,0x12,0x14,0x23,0x96,0x70,0x48,0x07,0x79,0x70,0x03,0x7b,0x28,0x07,0x79, + 0x98,0x87,0x74,0x78,0x07,0x77,0x98,0x12,0x18,0x23,0xa8,0x70,0x48,0x07,0x79,0x70, + 0x03,0x76,0x08,0x07,0x77,0x38,0x87,0x7a,0x08,0x87,0x73,0x28,0x87,0x5f,0xb0,0x87, + 0x72,0x90,0x87,0x79,0x48,0x87,0x77,0x70,0x87,0x29,0x01,0x32,0x62,0x0a,0x87,0x74, + 0x90,0x07,0x37,0x18,0x87,0x77,0x68,0x07,0x78,0x48,0x07,0x76,0x28,0x87,0x5f,0x78, + 0x07,0x78,0xa0,0x87,0x74,0x78,0x07,0x77,0x98,0x87,0x29,0x83,0xc2,0x38,0x23,0x98, + 0x70,0x48,0x07,0x79,0x70,0x03,0x73,0x90,0x87,0x70,0x38,0x87,0x76,0x28,0x07,0x77, + 0xa0,0x87,0x29,0xc1,0x1a,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00, + 0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84, + 0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed, + 0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66, + 0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c, + 0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70, + 0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90, + 0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4, + 0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83, + 0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14, + 0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70, + 0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80, + 0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0, + 0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1, + 0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d, + 0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39, + 0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc, + 0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70, + 0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53, + 0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e, + 0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c, + 0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38, + 0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37, + 0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33, + 0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4, + 0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0, + 0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3, + 0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07, + 0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23, + 0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59, + 0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b, + 0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00, + 0x00,0x08,0x00,0x00,0x00,0x16,0xb0,0x01,0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f, + 0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e,0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20, + 0x0d,0x0d,0x00,0x00,0x00,0x61,0x20,0x00,0x00,0x0c,0x00,0x00,0x00,0x13,0x04,0x41, + 0x2c,0x10,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xc4,0x46,0x00,0x48,0x8d,0x00,0xd4, + 0x00,0x89,0x19,0x00,0x02,0x23,0x00,0x00,0x00,0x23,0x06,0x8a,0x10,0x44,0x87,0x91, + 0x0c,0x05,0x11,0x58,0x90,0xc8,0x67,0xb6,0x81,0x08,0x80,0x0c,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _snk_vs_source_metal_sim[672] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x32,0x20,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x3b, + 0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e, + 0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x32,0x20,0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63, + 0x6e,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c, + 0x6f,0x63,0x6e,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20, + 0x5b,0x5b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b, + 0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69, + 0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62, + 0x75,0x74,0x65,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c, + 0x6f,0x61,0x74,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x5b, + 0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x31,0x29,0x5d,0x5d,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x32, + 0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x76,0x65,0x72,0x74,0x65,0x78,0x20, + 0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28, + 0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74, + 0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61, + 0x6e,0x74,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x26,0x20,0x5f,0x32, + 0x32,0x20,0x5b,0x5b,0x62,0x75,0x66,0x66,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29, + 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74, + 0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f, + 0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x28,0x28,0x69,0x6e,0x2e,0x70,0x6f,0x73, + 0x69,0x74,0x69,0x6f,0x6e,0x20,0x2f,0x20,0x5f,0x32,0x32,0x2e,0x64,0x69,0x73,0x70, + 0x5f,0x73,0x69,0x7a,0x65,0x29,0x20,0x2d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28, + 0x30,0x2e,0x35,0x29,0x29,0x20,0x2a,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x32, + 0x2e,0x30,0x2c,0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c,0x20,0x30,0x2e,0x35,0x2c,0x20, + 0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75,0x76, + 0x20,0x3d,0x20,0x69,0x6e,0x2e,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d, + 0x20,0x69,0x6e,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, + +}; +static const uint8_t _snk_fs_source_metal_sim[436] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d, + 0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f, + 0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, + 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x66,0x72,0x61,0x67,0x6d,0x65, + 0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69, + 0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b, + 0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x74,0x65, + 0x78,0x20,0x5b,0x5b,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x30,0x29,0x5d,0x5d, + 0x2c,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x73,0x6d,0x70,0x20,0x5b,0x5b, + 0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a, + 0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75, + 0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e, + 0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78, + 0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x73,0x6d,0x70,0x2c,0x20,0x69,0x6e,0x2e, + 0x75,0x76,0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a, + 0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_D3D11) +static const uint8_t _snk_vs_bytecode_hlsl4[892] = { + 0x44,0x58,0x42,0x43,0x0d,0xbd,0x9e,0x9e,0x7d,0xc0,0x2b,0x54,0x88,0xf9,0xca,0x89, + 0x32,0xe4,0x0c,0x59,0x01,0x00,0x00,0x00,0x7c,0x03,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x60,0x01,0x00,0x00,0xd0,0x01,0x00,0x00, + 0x00,0x03,0x00,0x00,0x52,0x44,0x45,0x46,0xc0,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x48,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xfe,0xff, + 0x10,0x81,0x00,0x00,0x98,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0xab,0xab,0x3c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x88,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x32,0x32,0x5f,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a, + 0x65,0x00,0xab,0xab,0x01,0x00,0x03,0x00,0x01,0x00,0x02,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f,0x66,0x74,0x20,0x28,0x52, + 0x29,0x20,0x48,0x4c,0x53,0x4c,0x20,0x53,0x68,0x61,0x64,0x65,0x72,0x20,0x43,0x6f, + 0x6d,0x70,0x69,0x6c,0x65,0x72,0x20,0x31,0x30,0x2e,0x31,0x00,0x49,0x53,0x47,0x4e, + 0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x50,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x03,0x00,0x00,0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x50,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab, + 0x4f,0x53,0x47,0x4e,0x68,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x0c,0x00,0x00,0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0xab,0xab,0xab, + 0x53,0x48,0x44,0x52,0x28,0x01,0x00,0x00,0x40,0x00,0x01,0x00,0x4a,0x00,0x00,0x00, + 0x59,0x00,0x00,0x04,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x5f,0x00,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x5f,0x00,0x00,0x03, + 0x32,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00, + 0x02,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0x32,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00,0x67,0x00,0x00,0x04, + 0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x68,0x00,0x00,0x02, + 0x01,0x00,0x00,0x00,0x36,0x00,0x00,0x05,0x32,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x36,0x00,0x00,0x05,0xf2,0x20,0x10,0x00, + 0x01,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x02,0x00,0x00,0x00,0x0e,0x00,0x00,0x08, + 0x32,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x80,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a, + 0x32,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x02,0x40,0x00,0x00,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x0a,0x32,0x20,0x10,0x00,0x02,0x00,0x00,0x00, + 0x46,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x02,0x40,0x00,0x00,0x00,0x00,0x00,0x40, + 0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x08, + 0xc2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0x02,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x00,0x00,0x80,0x3f,0x3e,0x00,0x00,0x01, + 0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _snk_fs_bytecode_hlsl4[608] = { + 0x44,0x58,0x42,0x43,0x3a,0xa7,0x41,0x21,0xb4,0x2d,0xa7,0x6e,0xfe,0x31,0xb0,0xe0, + 0x14,0xe0,0xdf,0x5a,0x01,0x00,0x00,0x00,0x60,0x02,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x48,0x01,0x00,0x00, + 0xe4,0x01,0x00,0x00,0x52,0x44,0x45,0x46,0x8c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xff,0xff, + 0x10,0x81,0x00,0x00,0x64,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x73,0x6d,0x70,0x00,0x74,0x65,0x78,0x00, + 0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f,0x66,0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c, + 0x53,0x4c,0x20,0x53,0x68,0x61,0x64,0x65,0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c, + 0x65,0x72,0x20,0x31,0x30,0x2e,0x31,0x00,0x49,0x53,0x47,0x4e,0x44,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x00,0x00, + 0x38,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x2c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x53,0x56,0x5f,0x54, + 0x61,0x72,0x67,0x65,0x74,0x00,0xab,0xab,0x53,0x48,0x44,0x52,0x94,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x5a,0x00,0x00,0x03,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x58,0x18,0x00,0x04,0x00,0x70,0x10,0x00,0x00,0x00,0x00,0x00, + 0x55,0x55,0x00,0x00,0x62,0x10,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x62,0x10,0x00,0x03,0xf2,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x65,0x00,0x00,0x03, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00, + 0x45,0x00,0x00,0x09,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x7e,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x07,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x01,0x00,0x00,0x00, + 0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +#elif defined(SOKOL_WGPU) +static const uint8_t _snk_vs_source_wgsl[1083] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a, + 0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20, + 0x20,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61, + 0x74,0x65,0x3e,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75,0x70,0x28, + 0x30,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x30,0x29,0x20,0x76, + 0x61,0x72,0x3c,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x3e,0x20,0x78,0x5f,0x32,0x32, + 0x20,0x3a,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x3b,0x0a,0x0a,0x76, + 0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72, + 0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61, + 0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65, + 0x3e,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20, + 0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f, + 0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x39, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x5f,0x31,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x35,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x78,0x5f,0x32,0x32, + 0x2e,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x6c,0x65, + 0x74,0x20,0x78,0x5f,0x33,0x33,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d, + 0x20,0x28,0x28,0x28,0x78,0x5f,0x31,0x39,0x20,0x2f,0x20,0x78,0x5f,0x32,0x35,0x29, + 0x20,0x2d,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x30,0x2e,0x35,0x66,0x2c,0x20,0x30, + 0x2e,0x35,0x66,0x29,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x32,0x2e, + 0x30,0x66,0x2c,0x20,0x2d,0x32,0x2e,0x30,0x66,0x29,0x29,0x3b,0x0a,0x20,0x20,0x67, + 0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x76,0x65,0x63, + 0x34,0x66,0x28,0x78,0x5f,0x33,0x33,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x33,0x33,0x2e, + 0x79,0x2c,0x20,0x30,0x2e,0x35,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x3b,0x0a, + 0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x33,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a, + 0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x78,0x5f,0x34,0x33,0x3b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x34,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20, + 0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x20,0x3d,0x20,0x78,0x5f,0x34,0x37,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75, + 0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61, + 0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x40,0x62,0x75,0x69,0x6c, + 0x74,0x69,0x6e,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x29,0x0a,0x20,0x20, + 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20,0x76,0x65, + 0x63,0x34,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e, + 0x28,0x30,0x29,0x0a,0x20,0x20,0x75,0x76,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28, + 0x31,0x29,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x31,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x76,0x65,0x72,0x74,0x65,0x78, + 0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69, + 0x6f,0x6e,0x28,0x30,0x29,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20, + 0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x20,0x74,0x65,0x78, + 0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28, + 0x32,0x29,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x29,0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e, + 0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x5f,0x31,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f, + 0x72,0x64,0x30,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20, + 0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a, + 0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65, + 0x74,0x75,0x72,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x67,0x6c, + 0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x2c,0x20,0x75,0x76,0x2c,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const uint8_t _snk_fs_source_wgsl[630] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75, + 0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x34,0x38, + 0x29,0x20,0x76,0x61,0x72,0x20,0x74,0x65,0x78,0x20,0x3a,0x20,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x5f,0x32,0x64,0x3c,0x66,0x33,0x32,0x3e,0x3b,0x0a,0x0a,0x40,0x67, + 0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67, + 0x28,0x36,0x34,0x29,0x20,0x76,0x61,0x72,0x20,0x73,0x6d,0x70,0x20,0x3a,0x20,0x73, + 0x61,0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, + 0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a, + 0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20, + 0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x33,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, + 0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x2c,0x20,0x73, + 0x6d,0x70,0x2c,0x20,0x78,0x5f,0x32,0x33,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74, + 0x20,0x78,0x5f,0x32,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f, + 0x6c,0x6f,0x72,0x20,0x3d,0x20,0x28,0x78,0x5f,0x32,0x34,0x20,0x2a,0x20,0x78,0x5f, + 0x32,0x37,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d, + 0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75, + 0x74,0x20,0x7b,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28, + 0x30,0x29,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x5f, + 0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x66, + 0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28, + 0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x75,0x76,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20,0x40, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x29, + 0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20, + 0x20,0x75,0x76,0x20,0x3d,0x20,0x75,0x76,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a, + 0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28, + 0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6d,0x61,0x69,0x6e, + 0x5f,0x6f,0x75,0x74,0x28,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x29, + 0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_DUMMY_BACKEND) +static const char* _snk_vs_source_dummy = ""; +static const char* _snk_fs_source_dummy = ""; +#else +#error "Please define one of SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" +#endif + +#if !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) +static void _snk_clipboard_copy(nk_handle usr, const char *text, int len) { + (void)usr; + if (len == 0) { + return; + } + sapp_set_clipboard_string(text); +} + +static void _snk_clipboard_paste(nk_handle usr, struct nk_text_edit *edit) { + const char *text = sapp_get_clipboard_string(); + if (text) { + nk_textedit_paste(edit, text, nk_strlen(text)); + } + (void)usr; +} + +#if defined(__EMSCRIPTEN__) && !defined(SOKOL_DUMMY_BACKEND) +EM_JS(int, snk_js_is_osx, (void), { + if (navigator.userAgent.includes('Macintosh')) { + return 1; + } else { + return 0; + } +}); +#endif + +static bool _snk_is_osx(void) { + #if defined(SOKOL_DUMMY_BACKEND) + return false; + #elif defined(__EMSCRIPTEN__) + return snk_js_is_osx(); + #elif defined(__APPLE__) + return true; + #else + return false; + #endif +} +#endif // !SOKOL_NUKLEAR_NO_SOKOL_APP + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SNK_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _snk_log_messages[] = { + _SNK_LOG_ITEMS +}; +#undef _SNK_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SNK_PANIC(code) _snk_log(SNK_LOGITEM_ ##code, 0, 0, __LINE__) +#define _SNK_ERROR(code) _snk_log(SNK_LOGITEM_ ##code, 1, 0, __LINE__) +#define _SNK_WARN(code) _snk_log(SNK_LOGITEM_ ##code, 2, 0, __LINE__) +#define _SNK_INFO(code) _snk_log(SNK_LOGITEM_ ##code, 3, 0, __LINE__) +#define _SNK_LOGMSG(code,msg) _snk_log(SNK_LOGITEM_ ##code, 3, msg, __LINE__) + +static void _snk_log(snk_log_item_t log_item, uint32_t log_level, const char* msg, uint32_t line_nr) { + if (_snuklear.desc.logger.func) { + const char* filename = 0; + #if defined(SOKOL_DEBUG) + filename = __FILE__; + if (0 == msg) { + msg = _snk_log_messages[log_item]; + } + #endif + _snuklear.desc.logger.func("snk", log_level, log_item, msg, line_nr, filename, _snuklear.desc.logger.user_data); + } else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory +static void _snk_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +static void* _snk_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_snuklear.desc.allocator.alloc_fn) { + ptr = _snuklear.desc.allocator.alloc_fn(size, _snuklear.desc.allocator.user_data); + } else { + ptr = malloc(size); + } + if (0 == ptr) { + _SNK_PANIC(MALLOC_FAILED); + } + return ptr; +} + +static void* _snk_malloc_clear(size_t size) { + void* ptr = _snk_malloc(size); + _snk_clear(ptr, size); + return ptr; +} + +static void _snk_free(void* ptr) { + if (_snuklear.desc.allocator.free_fn) { + _snuklear.desc.allocator.free_fn(ptr, _snuklear.desc.allocator.user_data); + } else { + free(ptr); + } +} + +// ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ +// +// >>pool +static void _snk_init_pool(_snk_pool_t* pool, int num) { + SOKOL_ASSERT(pool && (num >= 1)); + // slot 0 is reserved for the 'invalid id', so bump the pool size by 1 + pool->size = num + 1; + pool->queue_top = 0; + // generation counters indexable by pool slot index, slot 0 is reserved + size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size; + pool->gen_ctrs = (uint32_t*) _snk_malloc_clear(gen_ctrs_size); + // it's not a bug to only reserve 'num' here + pool->free_queue = (int*) _snk_malloc_clear(sizeof(int) * (size_t)num); + // never allocate the zero-th pool item since the invalid id is 0 + for (int i = pool->size-1; i >= 1; i--) { + pool->free_queue[pool->queue_top++] = i; + } +} + +static void _snk_discard_pool(_snk_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + _snk_free(pool->free_queue); + pool->free_queue = 0; + SOKOL_ASSERT(pool->gen_ctrs); + _snk_free(pool->gen_ctrs); + pool->gen_ctrs = 0; + pool->size = 0; + pool->queue_top = 0; +} + +static int _snk_pool_alloc_index(_snk_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } else { + // pool exhausted + return _SNK_INVALID_SLOT_INDEX; + } +} + +static void _snk_pool_free_index(_snk_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SNK_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_ASSERT(pool->queue_top < pool->size); + #ifdef SOKOL_DEBUG + // debug check against double-free + for (int i = 0; i < pool->queue_top; i++) { + SOKOL_ASSERT(pool->free_queue[i] != slot_index); + } + #endif + pool->free_queue[pool->queue_top++] = slot_index; + SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); +} + +/* initialize a pool slot: + - bump the slot's generation counter + - create a resource id from the generation counter and slot index + - set the slot's id to this id + - set the slot's state to ALLOC + - return the handle id +*/ +static uint32_t _snk_slot_init(_snk_pool_t* pool, _snk_slot_t* slot, int slot_index) { + /* FIXME: add handling for an overflowing generation counter, + for now, just overflow (another option is to disable + the slot) + */ + SOKOL_ASSERT(pool && pool->gen_ctrs); + SOKOL_ASSERT((slot_index > _SNK_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT((slot->state == _SNK_RESOURCESTATE_INITIAL) && (slot->id == SNK_INVALID_ID)); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + slot->id = (ctr<<_SNK_SLOT_SHIFT)|(slot_index & _SNK_SLOT_MASK); + slot->state = _SNK_RESOURCESTATE_ALLOC; + return slot->id; +} + +// extract slot index from id +static int _snk_slot_index(uint32_t id) { + int slot_index = (int) (id & _SNK_SLOT_MASK); + SOKOL_ASSERT(_SNK_INVALID_SLOT_INDEX != slot_index); + return slot_index; +} + +static void _snk_init_item_pool(_snk_pool_t* pool, int pool_size, void** items_ptr, size_t item_size_bytes) { + // NOTE: the pools will have an additional item, since slot 0 is reserved + SOKOL_ASSERT(pool && (pool->size == 0)); + SOKOL_ASSERT((pool_size > 0) && (pool_size < _SNK_MAX_POOL_SIZE)); + SOKOL_ASSERT(items_ptr && (*items_ptr == 0)); + SOKOL_ASSERT(item_size_bytes > 0); + _snk_init_pool(pool, pool_size); + const size_t pool_size_bytes = item_size_bytes * (size_t)pool->size; + *items_ptr = _snk_malloc_clear(pool_size_bytes); +} + +static void _snk_discard_item_pool(_snk_pool_t* pool, void** items_ptr) { + SOKOL_ASSERT(pool && (pool->size != 0)); + SOKOL_ASSERT(items_ptr && (*items_ptr != 0)); + _snk_free(*items_ptr); *items_ptr = 0; + _snk_discard_pool(pool); +} + +static void _snk_setup_image_pool(int pool_size) { + _snk_image_pool_t* p = &_snuklear.image_pool; + _snk_init_item_pool(&p->pool, pool_size, (void**)&p->items, sizeof(_snk_image_t)); +} + +static void _snk_discard_image_pool(void) { + _snk_image_pool_t* p = &_snuklear.image_pool; + _snk_discard_item_pool(&p->pool, (void**)&p->items); +} + +static snk_image_t _snk_make_image_handle(uint32_t id) { + snk_image_t handle = { id }; + return handle; +} + +static _snk_image_t* _snk_image_at(uint32_t id) { + SOKOL_ASSERT(SNK_INVALID_ID != id); + const _snk_image_pool_t* p = &_snuklear.image_pool; + int slot_index = _snk_slot_index(id); + SOKOL_ASSERT((slot_index > _SNK_INVALID_SLOT_INDEX) && (slot_index < p->pool.size)); + return &p->items[slot_index]; +} + +static _snk_image_t* _snk_lookup_image(uint32_t id) { + if (SNK_INVALID_ID != id) { + _snk_image_t* img = _snk_image_at(id); + if (img->slot.id == id) { + return img; + } + } + return 0; +} + +static snk_image_t _snk_alloc_image(void) { + _snk_image_pool_t* p = &_snuklear.image_pool; + int slot_index = _snk_pool_alloc_index(&p->pool); + if (_SNK_INVALID_SLOT_INDEX != slot_index) { + uint32_t id = _snk_slot_init(&p->pool, &p->items[slot_index].slot, slot_index); + return _snk_make_image_handle(id); + } else { + // pool exhausted + return _snk_make_image_handle(SNK_INVALID_ID); + } +} + +static _snk_resource_state _snk_init_image(_snk_image_t* img, const snk_image_desc_t* desc) { + SOKOL_ASSERT(img && (img->slot.state == _SNK_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + img->image = desc->image; + img->sampler = desc->sampler; + return _SNK_RESOURCESTATE_VALID; +} + +static void _snk_deinit_image(_snk_image_t* img) { + SOKOL_ASSERT(img); + img->image.id = SNK_INVALID_ID; + img->sampler.id = SNK_INVALID_ID; +} + +static void _snk_destroy_image(snk_image_t img_id) { + _snk_image_t* img = _snk_lookup_image(img_id.id); + if (img) { + _snk_deinit_image(img); + _snk_image_pool_t* p = &_snuklear.image_pool; + _snk_clear(img, sizeof(_snk_image_t)); + _snk_pool_free_index(&p->pool, _snk_slot_index(img_id.id)); + } +} + +static void _snk_destroy_all_images(void) { + _snk_image_pool_t* p = &_snuklear.image_pool; + for (int i = 0; i < p->pool.size; i++) { + _snk_image_t* img = &p->items[i]; + _snk_destroy_image(_snk_make_image_handle(img->slot.id)); + } +} + +static snk_image_desc_t _snk_image_desc_defaults(const snk_image_desc_t* desc) { + SOKOL_ASSERT(desc); + snk_image_desc_t res = *desc; + res.image.id = _snk_def(res.image.id, _snuklear.def_img.id); + res.sampler.id = _snk_def(res.sampler.id, _snuklear.def_smp.id); + return res; +} + +static snk_desc_t _snk_desc_defaults(const snk_desc_t* desc) { + SOKOL_ASSERT(desc); + snk_desc_t res = *desc; + res.max_vertices = _snk_def(res.max_vertices, 65536); + res.dpi_scale = _snk_def(res.dpi_scale, 1.0f); + res.image_pool_size = _snk_def(res.image_pool_size, 256); + return res; +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void snk_setup(const snk_desc_t* desc) { + SOKOL_ASSERT(desc); + _snk_clear(&_snuklear, sizeof(_snuklear)); + _snuklear.init_cookie = _SNK_INIT_COOKIE; + _snuklear.desc = _snk_desc_defaults(desc); + #if !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) + _snuklear.is_osx = _snk_is_osx(); + #endif + // can keep color_format, depth_format and sample_count as is, + // since sokol_gfx.h will do its own default-value handling + + _snk_setup_image_pool(_snuklear.desc.image_pool_size); + + // initialize Nuklear + nk_bool init_res = nk_init_default(&_snuklear.ctx, 0); + SOKOL_ASSERT(1 == init_res); (void)init_res; // silence unused warning in release mode +#if !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) + _snuklear.ctx.clip.copy = _snk_clipboard_copy; + _snuklear.ctx.clip.paste = _snk_clipboard_paste; +#endif + + // create sokol-gfx resources + sg_push_debug_group("sokol-nuklear"); + + // vertex buffer + _snuklear.vertex_buffer_size = (size_t)_snuklear.desc.max_vertices * sizeof(_snk_vertex_t); + _snuklear.vbuf = sg_make_buffer(&(sg_buffer_desc){ + .usage = SG_USAGE_STREAM, + .size = _snuklear.vertex_buffer_size, + .label = "sokol-nuklear-vertices" + }); + + // index buffer + _snuklear.index_buffer_size = (size_t)_snuklear.desc.max_vertices * 3 * sizeof(uint16_t); + _snuklear.ibuf = sg_make_buffer(&(sg_buffer_desc){ + .type = SG_BUFFERTYPE_INDEXBUFFER, + .usage = SG_USAGE_STREAM, + .size = _snuklear.index_buffer_size, + .label = "sokol-nuklear-indices" + }); + + // default font sampler + _snuklear.font_smp = sg_make_sampler(&(sg_sampler_desc){ + .min_filter = SG_FILTER_LINEAR, + .mag_filter = SG_FILTER_LINEAR, + .wrap_u = SG_WRAP_CLAMP_TO_EDGE, + .wrap_v = SG_WRAP_CLAMP_TO_EDGE, + .label = "sokol-nuklear-font-sampler", + }); + + // default user-image sampler + _snuklear.def_smp = sg_make_sampler(&(sg_sampler_desc){ + .min_filter = SG_FILTER_NEAREST, + .mag_filter = SG_FILTER_NEAREST, + .wrap_u = SG_WRAP_CLAMP_TO_EDGE, + .wrap_v = SG_WRAP_CLAMP_TO_EDGE, + .label = "sokol-nuklear-default-sampler", + }); + + // default font texture + if (!_snuklear.desc.no_default_font) { + nk_font_atlas_init_default(&_snuklear.atlas); + nk_font_atlas_begin(&_snuklear.atlas); + int font_width = 0, font_height = 0; + const void* pixels = nk_font_atlas_bake(&_snuklear.atlas, &font_width, &font_height, NK_FONT_ATLAS_RGBA32); + SOKOL_ASSERT((font_width > 0) && (font_height > 0)); + _snuklear.font_img = sg_make_image(&(sg_image_desc){ + .width = font_width, + .height = font_height, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .data.subimage[0][0] = { + .ptr = pixels, + .size = (size_t)(font_width * font_height) * sizeof(uint32_t) + }, + .label = "sokol-nuklear-font" + }); + _snuklear.default_font = snk_make_image(&(snk_image_desc_t){ + .image = _snuklear.font_img, + .sampler = _snuklear.font_smp, + }); + nk_font_atlas_end(&_snuklear.atlas, snk_nkhandle(_snuklear.default_font), 0); + nk_font_atlas_cleanup(&_snuklear.atlas); + if (_snuklear.atlas.default_font) { + nk_style_set_font(&_snuklear.ctx, &_snuklear.atlas.default_font->handle); + } + } + + // default user image + static uint32_t def_pixels[64]; + memset(def_pixels, 0xFF, sizeof(def_pixels)); + _snuklear.def_img = sg_make_image(&(sg_image_desc){ + .width = 8, + .height = 8, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .data.subimage[0][0] = SG_RANGE(def_pixels), + .label = "sokol-nuklear-default-image", + }); + + // shader + #if defined SOKOL_METAL + const char* vs_entry = "main0"; + const char* fs_entry = "main0"; + #else + const char* vs_entry = "main"; + const char* fs_entry = "main"; + #endif + sg_range vs_bytecode = { .ptr = 0, .size = 0 }; + sg_range fs_bytecode = { .ptr = 0, .size = 0 }; + const char* vs_source = 0; + const char* fs_source = 0; + #if defined(SOKOL_GLCORE) + vs_source = (const char*)_snk_vs_source_glsl410; + fs_source = (const char*)_snk_fs_source_glsl410; + #elif defined(SOKOL_GLES3) + vs_source = (const char*)_snk_vs_source_glsl300es; + fs_source = (const char*)_snk_fs_source_glsl300es; + #elif defined(SOKOL_METAL) + switch (sg_query_backend()) { + case SG_BACKEND_METAL_MACOS: + vs_bytecode = SG_RANGE(_snk_vs_bytecode_metal_macos); + fs_bytecode = SG_RANGE(_snk_fs_bytecode_metal_macos); + break; + case SG_BACKEND_METAL_IOS: + vs_bytecode = SG_RANGE(_snk_vs_bytecode_metal_ios); + fs_bytecode = SG_RANGE(_snk_fs_bytecode_metal_ios); + break; + default: + vs_source = (const char*)_snk_vs_source_metal_sim; + fs_source = (const char*)_snk_fs_source_metal_sim; + break; + } + #elif defined(SOKOL_D3D11) + vs_bytecode = SG_RANGE(_snk_vs_bytecode_hlsl4); + fs_bytecode = SG_RANGE(_snk_fs_bytecode_hlsl4); + #elif defined(SOKOL_WGPU) + vs_source = (const char*)_snk_vs_source_wgsl; + fs_source = (const char*)_snk_fs_source_wgsl; + #else + vs_source = _snk_vs_source_dummy; + fs_source = _snk_fs_source_dummy; + #endif + _snuklear.shd = sg_make_shader(&(sg_shader_desc){ + .attrs = { + [0] = { .name = "position", .sem_name = "TEXCOORD", .sem_index = 0 }, + [1] = { .name = "texcoord0", .sem_name = "TEXCOORD", .sem_index = 1 }, + [2] = { .name = "color0", .sem_name = "TEXCOORD", .sem_index = 2 }, + }, + .vs = { + .source = vs_source, + .bytecode = vs_bytecode, + .entry = vs_entry, + .d3d11_target = "vs_4_0", + .uniform_blocks[0] = { + .size = sizeof(_snk_vs_params_t), + .uniforms[0] = { + .name = "vs_params", + .type = SG_UNIFORMTYPE_FLOAT4, + .array_count = 1, + } + }, + }, + .fs = { + .source = fs_source, + .bytecode = fs_bytecode, + .entry = fs_entry, + .d3d11_target = "ps_4_0", + .images[0] = { .used = true, .image_type = SG_IMAGETYPE_2D, .sample_type = SG_IMAGESAMPLETYPE_FLOAT }, + .samplers[0] = { .used = true, .sampler_type = SG_SAMPLERTYPE_FILTERING }, + .image_sampler_pairs[0] = { .used = true, .glsl_name = "tex_smp", .image_slot = 0, .sampler_slot = 0 }, + }, + .label = "sokol-nuklear-shader" + }); + + // pipeline object + _snuklear.pip = sg_make_pipeline(&(sg_pipeline_desc){ + .layout = { + .attrs = { + [0] = { .offset = offsetof(_snk_vertex_t, pos), .format=SG_VERTEXFORMAT_FLOAT2 }, + [1] = { .offset = offsetof(_snk_vertex_t, uv), .format=SG_VERTEXFORMAT_FLOAT2 }, + [2] = { .offset = offsetof(_snk_vertex_t, col), .format=SG_VERTEXFORMAT_UBYTE4N } + } + }, + .shader = _snuklear.shd, + .index_type = SG_INDEXTYPE_UINT16, + .sample_count = _snuklear.desc.sample_count, + .depth.pixel_format = _snuklear.desc.depth_format, + .colors[0] = { + .pixel_format = _snuklear.desc.color_format, + .write_mask = SG_COLORMASK_RGB, + .blend = { + .enabled = true, + .src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA, + .dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, + } + }, + .label = "sokol-nuklear-pipeline" + }); + + sg_pop_debug_group(); +} + +SOKOL_API_IMPL void snk_shutdown(void) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + nk_free(&_snuklear.ctx); + nk_font_atlas_clear(&_snuklear.atlas); + + // NOTE: it's valid to call the destroy funcs with SG_INVALID_ID + sg_push_debug_group("sokol-nuklear"); + sg_destroy_pipeline(_snuklear.pip); + sg_destroy_shader(_snuklear.shd); + sg_destroy_sampler(_snuklear.font_smp); + sg_destroy_image(_snuklear.font_img); + sg_destroy_sampler(_snuklear.def_smp); + sg_destroy_image(_snuklear.def_img); + sg_destroy_buffer(_snuklear.ibuf); + sg_destroy_buffer(_snuklear.vbuf); + sg_pop_debug_group(); + _snk_destroy_all_images(); + _snk_discard_image_pool(); + _snuklear.init_cookie = 0; +} + +SOKOL_API_IMPL struct nk_context* snk_new_frame(void) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + #if !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) + nk_input_begin(&_snuklear.ctx); + if (_snuklear.mouse_did_move) { + nk_input_motion(&_snuklear.ctx, _snuklear.mouse_pos[0], _snuklear.mouse_pos[1]); + _snuklear.mouse_did_move = false; + } + if (_snuklear.mouse_did_scroll) { + nk_input_scroll(&_snuklear.ctx, nk_vec2(_snuklear.mouse_scroll[0], _snuklear.mouse_scroll[1])); + _snuklear.mouse_did_scroll = false; + } + for (int i = 0; i < NK_BUTTON_MAX; i++) { + if (_snuklear.btn_down[i]) { + _snuklear.btn_down[i] = false; + nk_input_button(&_snuklear.ctx, (enum nk_buttons)i, _snuklear.mouse_pos[0], _snuklear.mouse_pos[1], 1); + } else if (_snuklear.btn_up[i]) { + _snuklear.btn_up[i] = false; + nk_input_button(&_snuklear.ctx, (enum nk_buttons)i, _snuklear.mouse_pos[0], _snuklear.mouse_pos[1], 0); + } + } + const size_t char_buffer_len = strlen(_snuklear.char_buffer); + if (char_buffer_len > 0) { + for (size_t i = 0; i < char_buffer_len; i++) { + nk_input_char(&_snuklear.ctx, _snuklear.char_buffer[i]); + } + _snk_clear(_snuklear.char_buffer, NK_INPUT_MAX); + } + for (int i = 0; i < NK_KEY_MAX; i++) { + if (_snuklear.keys_down[i]) { + nk_input_key(&_snuklear.ctx, (enum nk_keys)i, true); + _snuklear.keys_down[i] = 0; + } + if (_snuklear.keys_up[i]) { + nk_input_key(&_snuklear.ctx, (enum nk_keys)i, false); + _snuklear.keys_up[i] = 0; + } + } + nk_input_end(&_snuklear.ctx); + #endif + + nk_clear(&_snuklear.ctx); + return &_snuklear.ctx; +} + +SOKOL_API_IMPL snk_image_t snk_make_image(const snk_image_desc_t* desc) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + SOKOL_ASSERT(desc); + const snk_image_desc_t desc_def = _snk_image_desc_defaults(desc); + snk_image_t img_id = _snk_alloc_image(); + _snk_image_t* img = _snk_lookup_image(img_id.id); + if (img) { + img->slot.state = _snk_init_image(img, &desc_def); + SOKOL_ASSERT((img->slot.state == _SNK_RESOURCESTATE_VALID) || (img->slot.state == _SNK_RESOURCESTATE_FAILED)); + } else { + _SNK_ERROR(IMAGE_POOL_EXHAUSTED); + } + return img_id; +} + +SOKOL_API_IMPL void snk_destroy_image(snk_image_t img_id) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + _snk_destroy_image(img_id); +} + +SOKOL_API_IMPL snk_image_desc_t snk_query_image_desc(snk_image_t img_id) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + _snk_image_t* img = _snk_lookup_image(img_id.id); + if (img) { + return (snk_image_desc_t){ + .image = img->image, + .sampler = img->sampler, + }; + } else { + return (snk_image_desc_t){0}; + } +} + +SOKOL_API_IMPL nk_handle snk_nkhandle(snk_image_t img) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + return (nk_handle) { .id = (int)img.id }; +} + +SOKOL_API_IMPL snk_image_t snk_image_from_nkhandle(nk_handle h) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + return (snk_image_t){ .id = (uint32_t) h.id }; +} + +static void _snk_bind_image_sampler(sg_bindings* bindings, nk_handle h) { + _snk_image_t* img = _snk_lookup_image((uint32_t)h.id); + if (img) { + bindings->fs.images[0] = img->image; + bindings->fs.samplers[0] = img->sampler; + } else { + bindings->fs.images[0] = _snuklear.def_img; + bindings->fs.samplers[0] = _snuklear.def_smp; + } +} + +SOKOL_API_IMPL void snk_render(int width, int height) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + static const struct nk_draw_vertex_layout_element vertex_layout[] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct _snk_vertex_t, pos)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct _snk_vertex_t, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct _snk_vertex_t, col)}, + {NK_VERTEX_LAYOUT_END} + }; + struct nk_convert_config cfg = { + .shape_AA = NK_ANTI_ALIASING_ON, + .line_AA = NK_ANTI_ALIASING_ON, + .vertex_layout = vertex_layout, + .vertex_size = sizeof(_snk_vertex_t), + .vertex_alignment = 4, + .circle_segment_count = 22, + .curve_segment_count = 22, + .arc_segment_count = 22, + .global_alpha = 1.0f + }; + + _snuklear.vs_params.disp_size[0] = (float)width; + _snuklear.vs_params.disp_size[1] = (float)height; + + // Setup vert/index buffers and convert + struct nk_buffer cmds, verts, idx; + nk_buffer_init_default(&cmds); + nk_buffer_init_default(&verts); + nk_buffer_init_default(&idx); + nk_convert(&_snuklear.ctx, &cmds, &verts, &idx, &cfg); + + // Check for vertex- and index-buffer overflow, assert in debug-mode, + // otherwise silently skip rendering + const bool vertex_buffer_overflow = nk_buffer_total(&verts) > _snuklear.vertex_buffer_size; + const bool index_buffer_overflow = nk_buffer_total(&idx) > _snuklear.index_buffer_size; + SOKOL_ASSERT(!vertex_buffer_overflow && !index_buffer_overflow); + if (!vertex_buffer_overflow && !index_buffer_overflow) { + + // Setup rendering + sg_update_buffer(_snuklear.vbuf, &(sg_range){ nk_buffer_memory_const(&verts), nk_buffer_total(&verts) }); + sg_update_buffer(_snuklear.ibuf, &(sg_range){ nk_buffer_memory_const(&idx), nk_buffer_total(&idx) }); + const float dpi_scale = _snuklear.desc.dpi_scale; + const int fb_width = (int)(_snuklear.vs_params.disp_size[0] * dpi_scale); + const int fb_height = (int)(_snuklear.vs_params.disp_size[1] * dpi_scale); + sg_apply_viewport(0, 0, fb_width, fb_height, true); + sg_apply_scissor_rect(0, 0, fb_width, fb_height, true); + sg_apply_pipeline(_snuklear.pip); + sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &SG_RANGE(_snuklear.vs_params)); + + // Iterate through the command list, rendering each one + const struct nk_draw_command* cmd = NULL; + int idx_offset = 0; + sg_bindings bindings = { + .vertex_buffers[0] = _snuklear.vbuf, + .index_buffer = _snuklear.ibuf, + .index_buffer_offset = idx_offset + }; + nk_draw_foreach(cmd, &_snuklear.ctx, &cmds) { + if (cmd->elem_count > 0) { + _snk_bind_image_sampler(&bindings, cmd->texture); + sg_apply_bindings(&bindings); + sg_apply_scissor_rectf(cmd->clip_rect.x * dpi_scale, + cmd->clip_rect.y * dpi_scale, + cmd->clip_rect.w * dpi_scale, + cmd->clip_rect.h * dpi_scale, + true); + sg_draw(0, (int)cmd->elem_count, 1); + bindings.index_buffer_offset += (int)cmd->elem_count * (int)sizeof(uint16_t); + } + } + sg_apply_scissor_rect(0, 0, fb_width, fb_height, true); + } + + // Cleanup + nk_buffer_free(&cmds); + nk_buffer_free(&verts); + nk_buffer_free(&idx); +} + +#if !defined(SOKOL_NUKLEAR_NO_SOKOL_APP) +_SOKOL_PRIVATE bool _snk_is_ctrl(uint32_t modifiers) { + if (_snuklear.is_osx) { + return 0 != (modifiers & SAPP_MODIFIER_SUPER); + } else { + return 0 != (modifiers & SAPP_MODIFIER_CTRL); + } +} + +_SOKOL_PRIVATE void _snk_append_char(uint32_t char_code) { + size_t idx = strlen(_snuklear.char_buffer); + if (idxkey_code) { + case SAPP_KEYCODE_C: + if (_snk_is_ctrl(ev->modifiers)) { + return NK_KEY_COPY; + } else { + return NK_KEY_NONE; + } + break; + case SAPP_KEYCODE_X: + if (_snk_is_ctrl(ev->modifiers)) { + return NK_KEY_CUT; + } else { + return NK_KEY_NONE; + } + break; + case SAPP_KEYCODE_A: + if (_snk_is_ctrl(ev->modifiers)) { + return NK_KEY_TEXT_SELECT_ALL; + } else { + return NK_KEY_NONE; + } + break; + case SAPP_KEYCODE_Z: + if (_snk_is_ctrl(ev->modifiers)) { + if (ev->modifiers & SAPP_MODIFIER_SHIFT) { + return NK_KEY_TEXT_REDO; + } else { + return NK_KEY_TEXT_UNDO; + } + } else { + return NK_KEY_NONE; + } + break; + case SAPP_KEYCODE_DELETE: return NK_KEY_DEL; + case SAPP_KEYCODE_ENTER: return NK_KEY_ENTER; + case SAPP_KEYCODE_TAB: return NK_KEY_TAB; + case SAPP_KEYCODE_BACKSPACE: return NK_KEY_BACKSPACE; + case SAPP_KEYCODE_UP: return NK_KEY_UP; + case SAPP_KEYCODE_DOWN: return NK_KEY_DOWN; + case SAPP_KEYCODE_LEFT: return NK_KEY_LEFT; + case SAPP_KEYCODE_RIGHT: return NK_KEY_RIGHT; + case SAPP_KEYCODE_LEFT_SHIFT: return NK_KEY_SHIFT; + case SAPP_KEYCODE_RIGHT_SHIFT: return NK_KEY_SHIFT; + case SAPP_KEYCODE_LEFT_CONTROL: return NK_KEY_CTRL; + case SAPP_KEYCODE_RIGHT_CONTROL: return NK_KEY_CTRL; + default: + return NK_KEY_NONE; + } +} + +SOKOL_API_IMPL bool snk_handle_event(const sapp_event* ev) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + const float dpi_scale = _snuklear.desc.dpi_scale; + switch (ev->type) { + case SAPP_EVENTTYPE_MOUSE_DOWN: + _snuklear.mouse_pos[0] = (int) (ev->mouse_x / dpi_scale); + _snuklear.mouse_pos[1] = (int) (ev->mouse_y / dpi_scale); + switch (ev->mouse_button) { + case SAPP_MOUSEBUTTON_LEFT: + _snuklear.btn_down[NK_BUTTON_LEFT] = true; + break; + case SAPP_MOUSEBUTTON_RIGHT: + _snuklear.btn_down[NK_BUTTON_RIGHT] = true; + break; + case SAPP_MOUSEBUTTON_MIDDLE: + _snuklear.btn_down[NK_BUTTON_MIDDLE] = true; + break; + default: + break; + } + break; + case SAPP_EVENTTYPE_MOUSE_UP: + _snuklear.mouse_pos[0] = (int) (ev->mouse_x / dpi_scale); + _snuklear.mouse_pos[1] = (int) (ev->mouse_y / dpi_scale); + switch (ev->mouse_button) { + case SAPP_MOUSEBUTTON_LEFT: + _snuklear.btn_up[NK_BUTTON_LEFT] = true; + break; + case SAPP_MOUSEBUTTON_RIGHT: + _snuklear.btn_up[NK_BUTTON_RIGHT] = true; + break; + case SAPP_MOUSEBUTTON_MIDDLE: + _snuklear.btn_up[NK_BUTTON_MIDDLE] = true; + break; + default: + break; + } + break; + case SAPP_EVENTTYPE_MOUSE_MOVE: + _snuklear.mouse_pos[0] = (int) (ev->mouse_x / dpi_scale); + _snuklear.mouse_pos[1] = (int) (ev->mouse_y / dpi_scale); + _snuklear.mouse_did_move = true; + break; + case SAPP_EVENTTYPE_MOUSE_ENTER: + case SAPP_EVENTTYPE_MOUSE_LEAVE: + for (int i = 0; i < NK_BUTTON_MAX; i++) { + _snuklear.btn_down[i] = false; + _snuklear.btn_up[i] = false; + } + break; + case SAPP_EVENTTYPE_MOUSE_SCROLL: + _snuklear.mouse_scroll[0] = ev->scroll_x; + _snuklear.mouse_scroll[1] = ev->scroll_y; + _snuklear.mouse_did_scroll = true; + break; + case SAPP_EVENTTYPE_TOUCHES_BEGAN: + _snuklear.btn_down[NK_BUTTON_LEFT] = true; + _snuklear.mouse_pos[0] = (int) (ev->touches[0].pos_x / dpi_scale); + _snuklear.mouse_pos[1] = (int) (ev->touches[0].pos_y / dpi_scale); + _snuklear.mouse_did_move = true; + break; + case SAPP_EVENTTYPE_TOUCHES_MOVED: + _snuklear.mouse_pos[0] = (int) (ev->touches[0].pos_x / dpi_scale); + _snuklear.mouse_pos[1] = (int) (ev->touches[0].pos_y / dpi_scale); + _snuklear.mouse_did_move = true; + break; + case SAPP_EVENTTYPE_TOUCHES_ENDED: + _snuklear.btn_up[NK_BUTTON_LEFT] = true; + _snuklear.mouse_pos[0] = (int) (ev->touches[0].pos_x / dpi_scale); + _snuklear.mouse_pos[1] = (int) (ev->touches[0].pos_y / dpi_scale); + _snuklear.mouse_did_move = true; + break; + case SAPP_EVENTTYPE_TOUCHES_CANCELLED: + _snuklear.btn_up[NK_BUTTON_LEFT] = false; + _snuklear.btn_down[NK_BUTTON_LEFT] = false; + break; + case SAPP_EVENTTYPE_KEY_DOWN: + /* intercept Ctrl-V, this is handled via EVENTTYPE_CLIPBOARD_PASTED */ + if (_snk_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_V)) { + break; + } + /* on web platform, don't forward Ctrl-X, Ctrl-V to the browser */ + if (_snk_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_X)) { + sapp_consume_event(); + } + if (_snk_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_C)) { + sapp_consume_event(); + } + _snuklear.keys_down[_snk_event_to_nuklearkey(ev)] = true; + break; + case SAPP_EVENTTYPE_KEY_UP: + /* intercept Ctrl-V, this is handled via EVENTTYPE_CLIPBOARD_PASTED */ + if (_snk_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_V)) { + break; + } + /* on web platform, don't forward Ctrl-X, Ctrl-V to the browser */ + if (_snk_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_X)) { + sapp_consume_event(); + } + if (_snk_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_C)) { + sapp_consume_event(); + } + _snuklear.keys_up[_snk_event_to_nuklearkey(ev)] = true; + break; + case SAPP_EVENTTYPE_CHAR: + if ((ev->char_code >= 32) && + (ev->char_code != 127) && + (0 == (ev->modifiers & (SAPP_MODIFIER_ALT|SAPP_MODIFIER_CTRL|SAPP_MODIFIER_SUPER)))) + { + _snk_append_char(ev->char_code); + } + break; + case SAPP_EVENTTYPE_CLIPBOARD_PASTED: + _snuklear.keys_down[NK_KEY_PASTE] = _snuklear.keys_up[NK_KEY_PASTE] = true; + break; + default: + break; + } + return nk_item_is_any_active(&_snuklear.ctx); +} + +SOKOL_API_IMPL nk_flags snk_edit_string(struct nk_context *ctx, nk_flags flags, char *memory, int *len, int max, nk_plugin_filter filter) { + SOKOL_ASSERT(_SNK_INIT_COOKIE == _snuklear.init_cookie); + nk_flags event = nk_edit_string(ctx, flags, memory, len, max, filter); + if ((event & NK_EDIT_ACTIVATED) && !sapp_keyboard_shown()) { + sapp_show_keyboard(true); + } + if ((event & NK_EDIT_DEACTIVATED) && sapp_keyboard_shown()) { + sapp_show_keyboard(false); + } + return event; +} +#endif // SOKOL_NUKLEAR_NO_SOKOL_APP + +#endif // SOKOL_IMPL diff --git a/thirdparty/sokol/util/sokol_shape.h b/thirdparty/sokol/util/sokol_shape.h new file mode 100644 index 0000000..d1676dd --- /dev/null +++ b/thirdparty/sokol/util/sokol_shape.h @@ -0,0 +1,1431 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_SHAPE_IMPL) +#define SOKOL_SHAPE_IMPL +#endif +#ifndef SOKOL_SHAPE_INCLUDED +/* + sokol_shape.h -- create simple primitive shapes for sokol_gfx.h + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_SHAPE_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + Include the following headers before including sokol_shape.h: + + sokol_gfx.h + + ...optionally provide the following macros to override defaults: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_SHAPE_API_DECL- public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_SHAPE_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + If sokol_shape.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_SHAPE_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + FEATURE OVERVIEW + ================ + sokol_shape.h creates vertices and indices for simple shapes and + builds structs which can be plugged into sokol-gfx resource + creation functions: + + The following shape types are supported: + + - plane + - cube + - sphere (with poles, not geodesic) + - cylinder + - torus (donut) + + Generated vertices look like this: + + typedef struct sshape_vertex_t { + float x, y, z; + uint32_t normal; // packed normal as BYTE4N + uint16_t u, v; // packed uv coords as USHORT2N + uint32_t color; // packed color as UBYTE4N (r,g,b,a); + } sshape_vertex_t; + + Indices are generally 16-bits wide (SG_INDEXTYPE_UINT16) and the indices + are written as triangle-lists (SG_PRIMITIVETYPE_TRIANGLES). + + EXAMPLES: + ========= + + Create multiple shapes into the same vertex- and index-buffer and + render with separate draw calls: + + https://github.com/floooh/sokol-samples/blob/master/sapp/shapes-sapp.c + + Same as the above, but pre-transform shapes and merge them into a single + shape that's rendered with a single draw call. + + https://github.com/floooh/sokol-samples/blob/master/sapp/shapes-transform-sapp.c + + STEP-BY-STEP: + ============= + + Setup an sshape_buffer_t struct with pointers to memory buffers where + generated vertices and indices will be written to: + + ```c + sshape_vertex_t vertices[512]; + uint16_t indices[4096]; + + sshape_buffer_t buf = { + .vertices = { + .buffer = SSHAPE_RANGE(vertices), + }, + .indices = { + .buffer = SSHAPE_RANGE(indices), + } + }; + ``` + + To find out how big those memory buffers must be (in case you want + to allocate dynamically) call the following functions: + + ```c + sshape_sizes_t sshape_plane_sizes(uint32_t tiles); + sshape_sizes_t sshape_box_sizes(uint32_t tiles); + sshape_sizes_t sshape_sphere_sizes(uint32_t slices, uint32_t stacks); + sshape_sizes_t sshape_cylinder_sizes(uint32_t slices, uint32_t stacks); + sshape_sizes_t sshape_torus_sizes(uint32_t sides, uint32_t rings); + ``` + + The returned sshape_sizes_t struct contains vertex- and index-counts + as well as the equivalent buffer sizes in bytes. For instance: + + ```c + sshape_sizes_t sizes = sshape_sphere_sizes(36, 12); + uint32_t num_vertices = sizes.vertices.num; + uint32_t num_indices = sizes.indices.num; + uint32_t vertex_buffer_size = sizes.vertices.size; + uint32_t index_buffer_size = sizes.indices.size; + ``` + + With the sshape_buffer_t struct that was setup earlier, call any + of the shape-builder functions: + + ```c + sshape_buffer_t sshape_build_plane(const sshape_buffer_t* buf, const sshape_plane_t* params); + sshape_buffer_t sshape_build_box(const sshape_buffer_t* buf, const sshape_box_t* params); + sshape_buffer_t sshape_build_sphere(const sshape_buffer_t* buf, const sshape_sphere_t* params); + sshape_buffer_t sshape_build_cylinder(const sshape_buffer_t* buf, const sshape_cylinder_t* params); + sshape_buffer_t sshape_build_torus(const sshape_buffer_t* buf, const sshape_torus_t* params); + ``` + + Note how the sshape_buffer_t struct is both an input value and the + return value. This can be used to append multiple shapes into the + same vertex- and index-buffers (more on this later). + + The second argument is a struct which holds creation parameters. + + For instance to build a sphere with radius 2, 36 "cake slices" and 12 stacks: + + ```c + sshape_buffer_t buf = ...; + buf = sshape_build_sphere(&buf, &(sshape_sphere_t){ + .radius = 2.0f, + .slices = 36, + .stacks = 12, + }); + ``` + + If the provided buffers are big enough to hold all generated vertices and + indices, the "valid" field in the result will be true: + + ```c + assert(buf.valid); + ``` + + The shape creation parameters have "useful defaults", refer to the + actual C struct declarations below to look up those defaults. + + You can also provide additional creation parameters, like a common vertex + color, a debug-helper to randomize colors, tell the shape builder function + to merge the new shape with the previous shape into the same draw-element-range, + or a 4x4 transform matrix to move, rotate and scale the generated vertices: + + ```c + sshape_buffer_t buf = ...; + buf = sshape_build_sphere(&buf, &(sshape_sphere_t){ + .radius = 2.0f, + .slices = 36, + .stacks = 12, + // merge with previous shape into a single element-range + .merge = true, + // set vertex color to red+opaque + .color = sshape_color_4f(1.0f, 0.0f, 0.0f, 1.0f), + // set position to y = 2.0 + .transform = { + .m = { + { 1.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f, 0.0f }, + { 0.0f, 2.0f, 0.0f, 1.0f }, + } + } + }); + assert(buf.valid); + ``` + + The following helper functions can be used to build a packed + color value or to convert from external matrix types: + + ```c + uint32_t sshape_color_4f(float r, float g, float b, float a); + uint32_t sshape_color_3f(float r, float g, float b); + uint32_t sshape_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a); + uint32_t sshape_color_3b(uint8_t r, uint8_t g, uint8_t b); + sshape_mat4_t sshape_mat4(const float m[16]); + sshape_mat4_t sshape_mat4_transpose(const float m[16]); + ``` + + After the shape builder function has been called, the following functions + are used to extract the build result for plugging into sokol_gfx.h: + + ```c + sshape_element_range_t sshape_element_range(const sshape_buffer_t* buf); + sg_buffer_desc sshape_vertex_buffer_desc(const sshape_buffer_t* buf); + sg_buffer_desc sshape_index_buffer_desc(const sshape_buffer_t* buf); + sg_vertex_buffer_layout_state sshape_vertex_buffer_layout_state(void); + sg_vertex_attr_state sshape_position_vertex_attr_state(void); + sg_vertex_attr_state sshape_normal_vertex_attr_state(void); + sg_vertex_attr_state sshape_texcoord_vertex_attr_state(void); + sg_vertex_attr_state sshape_color_vertex_attr_state(void); + ``` + + The sshape_element_range_t struct contains the base-index and number of + indices which can be plugged into the sg_draw() call: + + ```c + sshape_element_range_t elms = sshape_element_range(&buf); + ... + sg_draw(elms.base_element, elms.num_elements, 1); + ``` + + To create sokol-gfx vertex- and index-buffers from the generated + shape data: + + ```c + // create sokol-gfx vertex buffer + sg_buffer_desc vbuf_desc = sshape_vertex_buffer_desc(&buf); + sg_buffer vbuf = sg_make_buffer(&vbuf_desc); + + // create sokol-gfx index buffer + sg_buffer_desc ibuf_desc = sshape_index_buffer_desc(&buf); + sg_buffer ibuf = sg_make_buffer(&ibuf_desc); + ``` + + The remaining functions are used to populate the vertex-layout item + in sg_pipeline_desc, note that these functions don't depend on the + created geometry, they always return the same result: + + ```c + sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){ + .layout = { + .buffers[0] = sshape_vertex_buffer_layout_state(), + .attrs = { + [0] = sshape_position_vertex_attr_state(), + [1] = ssape_normal_vertex_attr_state(), + [2] = sshape_texcoord_vertex_attr_state(), + [3] = sshape_color_vertex_attr_state() + } + }, + ... + }); + ``` + + Note that you don't have to use all generated vertex attributes in the + pipeline's vertex layout, the sg_vertex_buffer_layout_state struct returned + by sshape_vertex_buffer_layout_state() contains the correct vertex stride + to skip vertex components. + + WRITING MULTIPLE SHAPES INTO THE SAME BUFFER + ============================================ + You can merge multiple shapes into the same vertex- and + index-buffers and either render them as a single shape, or + in separate draw calls. + + To build a single shape made of two cubes which can be rendered + in a single draw-call: + + ``` + sshape_vertex_t vertices[128]; + uint16_t indices[16]; + + sshape_buffer_t buf = { + .vertices.buffer = SSHAPE_RANGE(vertices), + .indices.buffer = SSHAPE_RANGE(indices) + }; + + // first cube at pos x=-2.0 (with default size of 1x1x1) + buf = sshape_build_cube(&buf, &(sshape_box_t){ + .transform = { + .m = { + { 1.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f, 0.0f }, + {-2.0f, 0.0f, 0.0f, 1.0f }, + } + } + }); + // ...and append another cube at pos pos=+1.0 + // NOTE the .merge = true, this tells the shape builder + // function to not advance the current shape start offset + buf = sshape_build_cube(&buf, &(sshape_box_t){ + .merge = true, + .transform = { + .m = { + { 1.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f, 0.0f }, + {-2.0f, 0.0f, 0.0f, 1.0f }, + } + } + }); + assert(buf.valid); + + // skipping buffer- and pipeline-creation... + + sshape_element_range_t elms = sshape_element_range(&buf); + sg_draw(elms.base_element, elms.num_elements, 1); + ``` + + To render the two cubes in separate draw-calls, the element-ranges used + in the sg_draw() calls must be captured right after calling the + builder-functions: + + ```c + sshape_vertex_t vertices[128]; + uint16_t indices[16]; + sshape_buffer_t buf = { + .vertices.buffer = SSHAPE_RANGE(vertices), + .indices.buffer = SSHAPE_RANGE(indices) + }; + + // build a red cube... + buf = sshape_build_cube(&buf, &(sshape_box_t){ + .color = sshape_color_3b(255, 0, 0) + }); + sshape_element_range_t red_cube = sshape_element_range(&buf); + + // append a green cube to the same vertex-/index-buffer: + buf = sshape_build_cube(&bud, &sshape_box_t){ + .color = sshape_color_3b(0, 255, 0); + }); + sshape_element_range_t green_cube = sshape_element_range(&buf); + + // skipping buffer- and pipeline-creation... + + sg_draw(red_cube.base_element, red_cube.num_elements, 1); + sg_draw(green_cube.base_element, green_cube.num_elements, 1); + ``` + + ...that's about all :) + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2020 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_SHAPE_INCLUDED +#include // size_t, offsetof +#include +#include + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_shape.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_SHAPE_API_DECL) +#define SOKOL_SHAPE_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_SHAPE_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_SHAPE_IMPL) +#define SOKOL_SHAPE_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_SHAPE_API_DECL __declspec(dllimport) +#else +#define SOKOL_SHAPE_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + sshape_range is a pointer-size-pair struct used to pass memory + blobs into sokol-shape. When initialized from a value type + (array or struct), use the SSHAPE_RANGE() macro to build + an sshape_range struct. +*/ +typedef struct sshape_range { + const void* ptr; + size_t size; +} sshape_range; + +// disabling this for every includer isn't great, but the warning is also quite pointless +#if defined(_MSC_VER) +#pragma warning(disable:4221) /* /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y' */ +#endif +#if defined(__cplusplus) +#define SSHAPE_RANGE(x) sshape_range{ &x, sizeof(x) } +#else +#define SSHAPE_RANGE(x) (sshape_range){ &x, sizeof(x) } +#endif + +/* a 4x4 matrix wrapper struct */ +typedef struct sshape_mat4_t { float m[4][4]; } sshape_mat4_t; + +/* vertex layout of the generated geometry */ +typedef struct sshape_vertex_t { + float x, y, z; + uint32_t normal; // packed normal as BYTE4N + uint16_t u, v; // packed uv coords as USHORT2N + uint32_t color; // packed color as UBYTE4N (r,g,b,a); +} sshape_vertex_t; + +/* a range of draw-elements (sg_draw(int base_element, int num_element, ...)) */ +typedef struct sshape_element_range_t { + int base_element; + int num_elements; +} sshape_element_range_t; + +/* number of elements and byte size of build actions */ +typedef struct sshape_sizes_item_t { + uint32_t num; // number of elements + uint32_t size; // the same as size in bytes +} sshape_sizes_item_t; + +typedef struct sshape_sizes_t { + sshape_sizes_item_t vertices; + sshape_sizes_item_t indices; +} sshape_sizes_t; + +/* in/out struct to keep track of mesh-build state */ +typedef struct sshape_buffer_item_t { + sshape_range buffer; // pointer/size pair of output buffer + size_t data_size; // size in bytes of valid data in buffer + size_t shape_offset; // data offset of the most recent shape +} sshape_buffer_item_t; + +typedef struct sshape_buffer_t { + bool valid; + sshape_buffer_item_t vertices; + sshape_buffer_item_t indices; +} sshape_buffer_t; + +/* creation parameters for the different shape types */ +typedef struct sshape_plane_t { + float width, depth; // default: 1.0 + uint16_t tiles; // default: 1 + uint32_t color; // default: white + bool random_colors; // default: false + bool merge; // if true merge with previous shape (default: false) + sshape_mat4_t transform; // default: identity matrix +} sshape_plane_t; + +typedef struct sshape_box_t { + float width, height, depth; // default: 1.0 + uint16_t tiles; // default: 1 + uint32_t color; // default: white + bool random_colors; // default: false + bool merge; // if true merge with previous shape (default: false) + sshape_mat4_t transform; // default: identity matrix +} sshape_box_t; + +typedef struct sshape_sphere_t { + float radius; // default: 0.5 + uint16_t slices; // default: 5 + uint16_t stacks; // default: 4 + uint32_t color; // default: white + bool random_colors; // default: false + bool merge; // if true merge with previous shape (default: false) + sshape_mat4_t transform; // default: identity matrix +} sshape_sphere_t; + +typedef struct sshape_cylinder_t { + float radius; // default: 0.5 + float height; // default: 1.0 + uint16_t slices; // default: 5 + uint16_t stacks; // default: 1 + uint32_t color; // default: white + bool random_colors; // default: false + bool merge; // if true merge with previous shape (default: false) + sshape_mat4_t transform; // default: identity matrix +} sshape_cylinder_t; + +typedef struct sshape_torus_t { + float radius; // default: 0.5f + float ring_radius; // default: 0.2f + uint16_t sides; // default: 5 + uint16_t rings; // default: 5 + uint32_t color; // default: white + bool random_colors; // default: false + bool merge; // if true merge with previous shape (default: false) + sshape_mat4_t transform; // default: identity matrix +} sshape_torus_t; + +/* shape builder functions */ +SOKOL_SHAPE_API_DECL sshape_buffer_t sshape_build_plane(const sshape_buffer_t* buf, const sshape_plane_t* params); +SOKOL_SHAPE_API_DECL sshape_buffer_t sshape_build_box(const sshape_buffer_t* buf, const sshape_box_t* params); +SOKOL_SHAPE_API_DECL sshape_buffer_t sshape_build_sphere(const sshape_buffer_t* buf, const sshape_sphere_t* params); +SOKOL_SHAPE_API_DECL sshape_buffer_t sshape_build_cylinder(const sshape_buffer_t* buf, const sshape_cylinder_t* params); +SOKOL_SHAPE_API_DECL sshape_buffer_t sshape_build_torus(const sshape_buffer_t* buf, const sshape_torus_t* params); + +/* query required vertex- and index-buffer sizes in bytes */ +SOKOL_SHAPE_API_DECL sshape_sizes_t sshape_plane_sizes(uint32_t tiles); +SOKOL_SHAPE_API_DECL sshape_sizes_t sshape_box_sizes(uint32_t tiles); +SOKOL_SHAPE_API_DECL sshape_sizes_t sshape_sphere_sizes(uint32_t slices, uint32_t stacks); +SOKOL_SHAPE_API_DECL sshape_sizes_t sshape_cylinder_sizes(uint32_t slices, uint32_t stacks); +SOKOL_SHAPE_API_DECL sshape_sizes_t sshape_torus_sizes(uint32_t sides, uint32_t rings); + +/* extract sokol-gfx desc structs and primitive ranges from build state */ +SOKOL_SHAPE_API_DECL sshape_element_range_t sshape_element_range(const sshape_buffer_t* buf); +SOKOL_SHAPE_API_DECL sg_buffer_desc sshape_vertex_buffer_desc(const sshape_buffer_t* buf); +SOKOL_SHAPE_API_DECL sg_buffer_desc sshape_index_buffer_desc(const sshape_buffer_t* buf); +SOKOL_SHAPE_API_DECL sg_vertex_buffer_layout_state sshape_vertex_buffer_layout_state(void); +SOKOL_SHAPE_API_DECL sg_vertex_attr_state sshape_position_vertex_attr_state(void); +SOKOL_SHAPE_API_DECL sg_vertex_attr_state sshape_normal_vertex_attr_state(void); +SOKOL_SHAPE_API_DECL sg_vertex_attr_state sshape_texcoord_vertex_attr_state(void); +SOKOL_SHAPE_API_DECL sg_vertex_attr_state sshape_color_vertex_attr_state(void); + +/* helper functions to build packed color value from floats or bytes */ +SOKOL_SHAPE_API_DECL uint32_t sshape_color_4f(float r, float g, float b, float a); +SOKOL_SHAPE_API_DECL uint32_t sshape_color_3f(float r, float g, float b); +SOKOL_SHAPE_API_DECL uint32_t sshape_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_SHAPE_API_DECL uint32_t sshape_color_3b(uint8_t r, uint8_t g, uint8_t b); + +/* adapter function for filling matrix struct from generic float[16] array */ +SOKOL_SHAPE_API_DECL sshape_mat4_t sshape_mat4(const float m[16]); +SOKOL_SHAPE_API_DECL sshape_mat4_t sshape_mat4_transpose(const float m[16]); + +#ifdef __cplusplus +} // extern "C" + +// FIXME: C++ helper functions + +#endif +#endif // SOKOL_SHAPE_INCLUDED + +/*-- IMPLEMENTATION ----------------------------------------------------------*/ +#ifdef SOKOL_SHAPE_IMPL +#define SOKOL_SHAPE_IMPL_INCLUDED (1) + +#include // memcpy +#include // sinf, cosf + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-field-initializers" +#endif + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif + +#define _sshape_def(val, def) (((val) == 0) ? (def) : (val)) +#define _sshape_def_flt(val, def) (((val) == 0.0f) ? (def) : (val)) +#define _sshape_white (0xFFFFFFFF) + +typedef struct { float x, y, z, w; } _sshape_vec4_t; +typedef struct { float x, y; } _sshape_vec2_t; + +static inline float _sshape_clamp(float v) { + if (v < 0.0f) return 0.0f; + else if (v > 1.0f) return 1.0f; + else return v; +} + +static inline uint32_t _sshape_pack_ub4_ubyte4n(uint8_t x, uint8_t y, uint8_t z, uint8_t w) { + return (uint32_t)(((uint32_t)w<<24)|((uint32_t)z<<16)|((uint32_t)y<<8)|x); +} + +static inline uint32_t _sshape_pack_f4_ubyte4n(float x, float y, float z, float w) { + uint8_t x8 = (uint8_t) (x * 255.0f); + uint8_t y8 = (uint8_t) (y * 255.0f); + uint8_t z8 = (uint8_t) (z * 255.0f); + uint8_t w8 = (uint8_t) (w * 255.0f); + return _sshape_pack_ub4_ubyte4n(x8, y8, z8, w8); +} + +static inline uint32_t _sshape_pack_f4_byte4n(float x, float y, float z, float w) { + int8_t x8 = (int8_t) (x * 127.0f); + int8_t y8 = (int8_t) (y * 127.0f); + int8_t z8 = (int8_t) (z * 127.0f); + int8_t w8 = (int8_t) (w * 127.0f); + return _sshape_pack_ub4_ubyte4n((uint8_t)x8, (uint8_t)y8, (uint8_t)z8, (uint8_t)w8); +} + +static inline uint16_t _sshape_pack_f_ushortn(float x) { + return (uint16_t) (x * 65535.0f); +} + +static inline _sshape_vec4_t _sshape_vec4(float x, float y, float z, float w) { + _sshape_vec4_t v = { x, y, z, w }; + return v; +} + +static inline _sshape_vec4_t _sshape_vec4_norm(_sshape_vec4_t v) { + float l = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w); + if (l != 0.0f) { + return _sshape_vec4(v.x/l, v.y/l, v.z/l, v.w/l); + } + else { + return _sshape_vec4(0.0f, 1.0f, 0.0f, 0.0f); + } +} + +static inline _sshape_vec2_t _sshape_vec2(float x, float y) { + _sshape_vec2_t v = { x, y }; + return v; +} + +static bool _sshape_mat4_isnull(const sshape_mat4_t* m) { + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + if (0.0f != m->m[y][x]) { + return false; + } + } + } + return true; +} + +static sshape_mat4_t _sshape_mat4_identity(void) { + sshape_mat4_t m = { + { + { 1.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 1.0f } + } + }; + return m; +} + +static _sshape_vec4_t _sshape_mat4_mul(const sshape_mat4_t* m, _sshape_vec4_t v) { + _sshape_vec4_t res = { + m->m[0][0]*v.x + m->m[1][0]*v.y + m->m[2][0]*v.z + m->m[3][0]*v.w, + m->m[0][1]*v.x + m->m[1][1]*v.y + m->m[2][1]*v.z + m->m[3][1]*v.w, + m->m[0][2]*v.x + m->m[1][2]*v.y + m->m[2][2]*v.z + m->m[3][2]*v.w, + m->m[0][3]*v.x + m->m[1][3]*v.y + m->m[2][3]*v.z + m->m[3][3]*v.w + }; + return res; +} + +static uint32_t _sshape_plane_num_vertices(uint32_t tiles) { + return (tiles + 1) * (tiles + 1); +} + +static uint32_t _sshape_plane_num_indices(uint32_t tiles) { + return tiles * tiles * 2 * 3; +} + +static uint32_t _sshape_box_num_vertices(uint32_t tiles) { + return (tiles + 1) * (tiles + 1) * 6; +} + +static uint32_t _sshape_box_num_indices(uint32_t tiles) { + return tiles * tiles * 2 * 6 * 3; +} + +static uint32_t _sshape_sphere_num_vertices(uint32_t slices, uint32_t stacks) { + return (slices + 1) * (stacks + 1); +} + +static uint32_t _sshape_sphere_num_indices(uint32_t slices, uint32_t stacks) { + return ((2 * slices * stacks) - (2 * slices)) * 3; +} + +static uint32_t _sshape_cylinder_num_vertices(uint32_t slices, uint32_t stacks) { + return (slices + 1) * (stacks + 5); +} + +static uint32_t _sshape_cylinder_num_indices(uint32_t slices, uint32_t stacks) { + return ((2 * slices * stacks) + (2 * slices)) * 3; +} + +static uint32_t _sshape_torus_num_vertices(uint32_t sides, uint32_t rings) { + return (sides + 1) * (rings + 1); +} + +static uint32_t _sshape_torus_num_indices(uint32_t sides, uint32_t rings) { + return sides * rings * 2 * 3; +} + +static bool _sshape_validate_buffer_item(const sshape_buffer_item_t* item, uint32_t build_size) { + if (0 == item->buffer.ptr) { + return false; + } + if (0 == item->buffer.size) { + return false; + } + if ((item->data_size + build_size) > item->buffer.size) { + return false; + } + if (item->shape_offset > item->data_size) { + return false; + } + return true; +} + +static bool _sshape_validate_buffer(const sshape_buffer_t* buf, uint32_t num_vertices, uint32_t num_indices) { + if (!_sshape_validate_buffer_item(&buf->vertices, num_vertices * sizeof(sshape_vertex_t))) { + return false; + } + if (!_sshape_validate_buffer_item(&buf->indices, num_indices * sizeof(uint16_t))) { + return false; + } + return true; +} + +static void _sshape_advance_offset(sshape_buffer_item_t* item) { + item->shape_offset = item->data_size; +} + +static uint16_t _sshape_base_index(const sshape_buffer_t* buf) { + return (uint16_t) (buf->vertices.data_size / sizeof(sshape_vertex_t)); +} + +static sshape_plane_t _sshape_plane_defaults(const sshape_plane_t* params) { + sshape_plane_t res = *params; + res.width = _sshape_def_flt(res.width, 1.0f); + res.depth = _sshape_def_flt(res.depth, 1.0f); + res.tiles = _sshape_def(res.tiles, 1); + res.color = _sshape_def(res.color, _sshape_white); + res.transform = _sshape_mat4_isnull(&res.transform) ? _sshape_mat4_identity() : res.transform; + return res; +} + +static sshape_box_t _sshape_box_defaults(const sshape_box_t* params) { + sshape_box_t res = *params; + res.width = _sshape_def_flt(res.width, 1.0f); + res.height = _sshape_def_flt(res.height, 1.0f); + res.depth = _sshape_def_flt(res.depth, 1.0f); + res.tiles = _sshape_def(res.tiles, 1); + res.color = _sshape_def(res.color, _sshape_white); + res.transform = _sshape_mat4_isnull(&res.transform) ? _sshape_mat4_identity() : res.transform; + return res; +} + +static sshape_sphere_t _sshape_sphere_defaults(const sshape_sphere_t* params) { + sshape_sphere_t res = *params; + res.radius = _sshape_def_flt(res.radius, 0.5f); + res.slices = _sshape_def(res.slices, 5); + res.stacks = _sshape_def(res.stacks, 4); + res.color = _sshape_def(res.color, _sshape_white); + res.transform = _sshape_mat4_isnull(&res.transform) ? _sshape_mat4_identity() : res.transform; + return res; +} + +static sshape_cylinder_t _sshape_cylinder_defaults(const sshape_cylinder_t* params) { + sshape_cylinder_t res = *params; + res.radius = _sshape_def_flt(res.radius, 0.5f); + res.height = _sshape_def_flt(res.height, 1.0f); + res.slices = _sshape_def(res.slices, 5); + res.stacks = _sshape_def(res.stacks, 1); + res.color = _sshape_def(res.color, _sshape_white); + res.transform = _sshape_mat4_isnull(&res.transform) ? _sshape_mat4_identity() : res.transform; + return res; +} + +static sshape_torus_t _sshape_torus_defaults(const sshape_torus_t* params) { + sshape_torus_t res = *params; + res.radius = _sshape_def_flt(res.radius, 0.5f); + res.ring_radius = _sshape_def_flt(res.ring_radius, 0.2f); + res.sides = _sshape_def_flt(res.sides, 5); + res.rings = _sshape_def_flt(res.rings, 5); + res.color = _sshape_def(res.color, _sshape_white); + res.transform = _sshape_mat4_isnull(&res.transform) ? _sshape_mat4_identity() : res.transform; + return res; +} + +static void _sshape_add_vertex(sshape_buffer_t* buf, _sshape_vec4_t pos, _sshape_vec4_t norm, _sshape_vec2_t uv, uint32_t color) { + size_t offset = buf->vertices.data_size; + SOKOL_ASSERT((offset + sizeof(sshape_vertex_t)) <= buf->vertices.buffer.size); + buf->vertices.data_size += sizeof(sshape_vertex_t); + sshape_vertex_t* v_ptr = (sshape_vertex_t*) ((uint8_t*)buf->vertices.buffer.ptr + offset); + v_ptr->x = pos.x; + v_ptr->y = pos.y; + v_ptr->z = pos.z; + v_ptr->normal = _sshape_pack_f4_byte4n(norm.x, norm.y, norm.z, norm.w); + v_ptr->u = _sshape_pack_f_ushortn(uv.x); + v_ptr->v = _sshape_pack_f_ushortn(uv.y); + v_ptr->color = color; +} + +static void _sshape_add_triangle(sshape_buffer_t* buf, uint16_t i0, uint16_t i1, uint16_t i2) { + size_t offset = buf->indices.data_size; + SOKOL_ASSERT((offset + 3*sizeof(uint16_t)) <= buf->indices.buffer.size); + buf->indices.data_size += 3*sizeof(uint16_t); + uint16_t* i_ptr = (uint16_t*) ((uint8_t*)buf->indices.buffer.ptr + offset); + i_ptr[0] = i0; + i_ptr[1] = i1; + i_ptr[2] = i2; +} + +static uint32_t _sshape_rand_color(uint32_t* xorshift_state) { + // xorshift32 + uint32_t x = *xorshift_state; + x ^= x<<13; + x ^= x>>17; + x ^= x<<5; + *xorshift_state = x; + + // rand => bright color with alpha 1.0 + x |= 0xFF000000; + return x; + +} + +/*=== PUBLIC API FUNCTIONS ===================================================*/ +SOKOL_API_IMPL uint32_t sshape_color_4f(float r, float g, float b, float a) { + return _sshape_pack_f4_ubyte4n(_sshape_clamp(r), _sshape_clamp(g), _sshape_clamp(b), _sshape_clamp(a)); +} + +SOKOL_API_IMPL uint32_t sshape_color_3f(float r, float g, float b) { + return _sshape_pack_f4_ubyte4n(_sshape_clamp(r), _sshape_clamp(g), _sshape_clamp(b), 1.0f); +} + +SOKOL_API_IMPL uint32_t sshape_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + return _sshape_pack_ub4_ubyte4n(r, g, b, a); +} + +SOKOL_API_IMPL uint32_t sshape_color_3b(uint8_t r, uint8_t g, uint8_t b) { + return _sshape_pack_ub4_ubyte4n(r, g, b, 255); +} + +SOKOL_API_IMPL sshape_mat4_t sshape_mat4(const float m[16]) { + sshape_mat4_t res; + memcpy(&res.m[0][0], &m[0], 64); + return res; +} + +SOKOL_API_IMPL sshape_mat4_t sshape_mat4_transpose(const float m[16]) { + sshape_mat4_t res; + for (int c = 0; c < 4; c++) { + for (int r = 0; r < 4; r++) { + res.m[r][c] = m[c*4 + r]; + } + } + return res; +} + +SOKOL_API_IMPL sshape_sizes_t sshape_plane_sizes(uint32_t tiles) { + SOKOL_ASSERT(tiles >= 1); + sshape_sizes_t res = { {0} }; + res.vertices.num = _sshape_plane_num_vertices(tiles); + res.indices.num = _sshape_plane_num_indices(tiles); + res.vertices.size = res.vertices.num * sizeof(sshape_vertex_t); + res.indices.size = res.indices.num * sizeof(uint16_t); + return res; +} + +SOKOL_API_IMPL sshape_sizes_t sshape_box_sizes(uint32_t tiles) { + SOKOL_ASSERT(tiles >= 1); + sshape_sizes_t res = { {0} }; + res.vertices.num = _sshape_box_num_vertices(tiles); + res.indices.num = _sshape_box_num_indices(tiles); + res.vertices.size = res.vertices.num * sizeof(sshape_vertex_t); + res.indices.size = res.indices.num * sizeof(uint16_t); + return res; +} + +SOKOL_API_IMPL sshape_sizes_t sshape_sphere_sizes(uint32_t slices, uint32_t stacks) { + SOKOL_ASSERT((slices >= 3) && (stacks >= 2)); + sshape_sizes_t res = { {0} }; + res.vertices.num = _sshape_sphere_num_vertices(slices, stacks); + res.indices.num = _sshape_sphere_num_indices(slices, stacks); + res.vertices.size = res.vertices.num * sizeof(sshape_vertex_t); + res.indices.size = res.indices.num * sizeof(uint16_t); + return res; +} + +SOKOL_API_IMPL sshape_sizes_t sshape_cylinder_sizes(uint32_t slices, uint32_t stacks) { + SOKOL_ASSERT((slices >= 3) && (stacks >= 1)); + sshape_sizes_t res = { {0} }; + res.vertices.num = _sshape_cylinder_num_vertices(slices, stacks); + res.indices.num = _sshape_cylinder_num_indices(slices, stacks); + res.vertices.size = res.vertices.num * sizeof(sshape_vertex_t); + res.indices.size = res.indices.num * sizeof(uint16_t); + return res; +} + +SOKOL_API_IMPL sshape_sizes_t sshape_torus_sizes(uint32_t sides, uint32_t rings) { + SOKOL_ASSERT((sides >= 3) && (rings >= 3)); + sshape_sizes_t res = { {0} }; + res.vertices.num = _sshape_torus_num_vertices(sides, rings); + res.indices.num = _sshape_torus_num_indices(sides, rings); + res.vertices.size = res.vertices.num * sizeof(sshape_vertex_t); + res.indices.size = res.indices.num * sizeof(uint16_t); + return res; +} + +/* + Geometry layout for plane (4 tiles): + +--+--+--+--+ + |\ |\ |\ |\ | + | \| \| \| \| + +--+--+--+--+ 25 vertices (tiles + 1) * (tiles + 1) + |\ |\ |\ |\ | 32 triangles (tiles + 1) * (tiles + 1) * 2 + | \| \| \| \| + +--+--+--+--+ + |\ |\ |\ |\ | + | \| \| \| \| + +--+--+--+--+ + |\ |\ |\ |\ | + | \| \| \| \| + +--+--+--+--+ +*/ +SOKOL_API_IMPL sshape_buffer_t sshape_build_plane(const sshape_buffer_t* in_buf, const sshape_plane_t* in_params) { + SOKOL_ASSERT(in_buf && in_params); + const sshape_plane_t params = _sshape_plane_defaults(in_params); + const uint32_t num_vertices = _sshape_plane_num_vertices(params.tiles); + const uint32_t num_indices = _sshape_plane_num_indices(params.tiles); + sshape_buffer_t buf = *in_buf; + if (!_sshape_validate_buffer(&buf, num_vertices, num_indices)) { + buf.valid = false; + return buf; + } + buf.valid = true; + const uint16_t start_index = _sshape_base_index(&buf); + if (!params.merge) { + _sshape_advance_offset(&buf.vertices); + _sshape_advance_offset(&buf.indices); + } + + // write vertices + uint32_t rand_seed = 0x12345678; + const float x0 = -params.width * 0.5f; + const float z0 = params.depth * 0.5f; + const float dx = params.width / params.tiles; + const float dz = -params.depth / params.tiles; + const float duv = 1.0f / params.tiles; + _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, _sshape_vec4(0.0f, 1.0f, 0.0f, 0.0f))); + for (uint32_t ix = 0; ix <= params.tiles; ix++) { + for (uint32_t iz = 0; iz <= params.tiles; iz++) { + const _sshape_vec4_t pos = _sshape_vec4(x0 + dx*ix, 0.0f, z0 + dz*iz, 1.0f); + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec2_t uv = _sshape_vec2(duv*ix, duv*iz); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + + // write indices + for (uint16_t j = 0; j < params.tiles; j++) { + for (uint16_t i = 0; i < params.tiles; i++) { + const uint16_t i0 = start_index + (j * (params.tiles + 1)) + i; + const uint16_t i1 = i0 + 1; + const uint16_t i2 = i0 + params.tiles + 1; + const uint16_t i3 = i2 + 1; + _sshape_add_triangle(&buf, i0, i1, i3); + _sshape_add_triangle(&buf, i0, i3, i2); + } + } + return buf; +} + +SOKOL_API_IMPL sshape_buffer_t sshape_build_box(const sshape_buffer_t* in_buf, const sshape_box_t* in_params) { + SOKOL_ASSERT(in_buf && in_params); + const sshape_box_t params = _sshape_box_defaults(in_params); + const uint32_t num_vertices = _sshape_box_num_vertices(params.tiles); + const uint32_t num_indices = _sshape_box_num_indices(params.tiles); + sshape_buffer_t buf = *in_buf; + if (!_sshape_validate_buffer(&buf, num_vertices, num_indices)) { + buf.valid = false; + return buf; + } + buf.valid = true; + const uint16_t start_index = _sshape_base_index(&buf); + if (!params.merge) { + _sshape_advance_offset(&buf.vertices); + _sshape_advance_offset(&buf.indices); + } + + // build vertices + uint32_t rand_seed = 0x12345678; + const float x0 = -params.width * 0.5f; + const float x1 = params.width * 0.5f; + const float y0 = -params.height * 0.5f; + const float y1 = params.height * 0.5f; + const float z0 = -params.depth * 0.5f; + const float z1 = params.depth * 0.5f; + const float dx = params.width / params.tiles; + const float dy = params.height / params.tiles; + const float dz = params.depth / params.tiles; + const float duv = 1.0f / params.tiles; + + // bottom/top vertices + for (uint32_t top_bottom = 0; top_bottom < 2; top_bottom++) { + _sshape_vec4_t pos = _sshape_vec4(0.0f, (0==top_bottom) ? y0:y1, 0.0f, 1.0f); + const _sshape_vec4_t norm = _sshape_vec4(0.0f, (0==top_bottom) ? -1.0f:1.0f, 0.0f, 0.0f); + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, norm)); + for (uint32_t ix = 0; ix <= params.tiles; ix++) { + pos.x = (0==top_bottom) ? (x0 + dx * ix) : (x1 - dx * ix); + for (uint32_t iz = 0; iz <= params.tiles; iz++) { + pos.z = z0 + dz * iz; + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec2_t uv = _sshape_vec2(ix * duv, iz * duv); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + } + + // left/right vertices + for (uint32_t left_right = 0; left_right < 2; left_right++) { + _sshape_vec4_t pos = _sshape_vec4((0==left_right) ? x0:x1, 0.0f, 0.0f, 1.0f); + const _sshape_vec4_t norm = _sshape_vec4((0==left_right) ? -1.0f:1.0f, 0.0f, 0.0f, 0.0f); + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, norm)); + for (uint32_t iy = 0; iy <= params.tiles; iy++) { + pos.y = (0==left_right) ? (y1 - dy * iy) : (y0 + dy * iy); + for (uint32_t iz = 0; iz <= params.tiles; iz++) { + pos.z = z0 + dz * iz; + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec2_t uv = _sshape_vec2(iy * duv, iz * duv); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + } + + // front/back vertices + for (uint32_t front_back = 0; front_back < 2; front_back++) { + _sshape_vec4_t pos = _sshape_vec4(0.0f, 0.0f, (0==front_back) ? z0:z1, 1.0f); + const _sshape_vec4_t norm = _sshape_vec4(0.0f, 0.0f, (0==front_back) ? -1.0f:1.0f, 0.0f); + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, norm)); + for (uint32_t ix = 0; ix <= params.tiles; ix++) { + pos.x = (0==front_back) ? (x1 - dx * ix) : (x0 + dx * ix); + for (uint32_t iy = 0; iy <= params.tiles; iy++) { + pos.y = y0 + dy * iy; + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec2_t uv = _sshape_vec2(ix * duv, iy * duv); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + } + + // build indices + const uint16_t verts_per_face = (params.tiles + 1) * (params.tiles + 1); + for (uint16_t face = 0; face < 6; face++) { + uint16_t face_start_index = start_index + face * verts_per_face; + for (uint16_t j = 0; j < params.tiles; j++) { + for (uint16_t i = 0; i < params.tiles; i++) { + const uint16_t i0 = face_start_index + (j * (params.tiles + 1)) + i; + const uint16_t i1 = i0 + 1; + const uint16_t i2 = i0 + params.tiles + 1; + const uint16_t i3 = i2 + 1; + _sshape_add_triangle(&buf, i0, i1, i3); + _sshape_add_triangle(&buf, i0, i3, i2); + } + } + } + return buf; +} + +/* + Geometry layout for spheres is as follows (for 5 slices, 4 stacks): + + + + + + + + north pole + |\ |\ |\ |\ |\ + | \| \| \| \| \ + +--+--+--+--+--+ 30 vertices (slices + 1) * (stacks + 1) + |\ |\ |\ |\ |\ | 30 triangles (2 * slices * stacks) - (2 * slices) + | \| \| \| \| \| 2 orphaned vertices + +--+--+--+--+--+ + |\ |\ |\ |\ |\ | + | \| \| \| \| \| + +--+--+--+--+--+ + \ |\ |\ |\ |\ | + \| \| \| \| \| + + + + + + + south pole +*/ +SOKOL_API_IMPL sshape_buffer_t sshape_build_sphere(const sshape_buffer_t* in_buf, const sshape_sphere_t* in_params) { + SOKOL_ASSERT(in_buf && in_params); + const sshape_sphere_t params = _sshape_sphere_defaults(in_params); + const uint32_t num_vertices = _sshape_sphere_num_vertices(params.slices, params.stacks); + const uint32_t num_indices = _sshape_sphere_num_indices(params.slices, params.stacks); + sshape_buffer_t buf = *in_buf; + if (!_sshape_validate_buffer(&buf, num_vertices, num_indices)) { + buf.valid = false; + return buf; + } + buf.valid = true; + const uint16_t start_index = _sshape_base_index(&buf); + if (!params.merge) { + _sshape_advance_offset(&buf.vertices); + _sshape_advance_offset(&buf.indices); + } + + uint32_t rand_seed = 0x12345678; + const float pi = 3.14159265358979323846f; + const float two_pi = 2.0f * pi; + const float du = 1.0f / params.slices; + const float dv = 1.0f / params.stacks; + + // generate vertices + for (uint32_t stack = 0; stack <= params.stacks; stack++) { + const float stack_angle = (pi * stack) / params.stacks; + const float sin_stack = sinf(stack_angle); + const float cos_stack = cosf(stack_angle); + for (uint32_t slice = 0; slice <= params.slices; slice++) { + const float slice_angle = (two_pi * slice) / params.slices; + const float sin_slice = sinf(slice_angle); + const float cos_slice = cosf(slice_angle); + const _sshape_vec4_t norm = _sshape_vec4(-sin_slice * sin_stack, cos_stack, cos_slice * sin_stack, 0.0f); + const _sshape_vec4_t pos = _sshape_vec4(norm.x * params.radius, norm.y * params.radius, norm.z * params.radius, 1.0f); + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, norm)); + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec2_t uv = _sshape_vec2(1.0f - slice * du, 1.0f - stack * dv); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + + // generate indices + { + // north-pole triangles + const uint16_t row_a = start_index; + const uint16_t row_b = row_a + params.slices + 1; + for (uint16_t slice = 0; slice < params.slices; slice++) { + _sshape_add_triangle(&buf, row_a + slice, row_b + slice, row_b + slice + 1); + } + } + // stack triangles + for (uint16_t stack = 1; stack < params.stacks - 1; stack++) { + const uint16_t row_a = start_index + stack * (params.slices + 1); + const uint16_t row_b = row_a + params.slices + 1; + for (uint16_t slice = 0; slice < params.slices; slice++) { + _sshape_add_triangle(&buf, row_a + slice, row_b + slice + 1, row_a + slice + 1); + _sshape_add_triangle(&buf, row_a + slice, row_b + slice, row_b + slice + 1); + } + } + { + // south-pole triangles + const uint16_t row_a = start_index + (params.stacks - 1) * (params.slices + 1); + const uint16_t row_b = row_a + params.slices + 1; + for (uint16_t slice = 0; slice < params.slices; slice++) { + _sshape_add_triangle(&buf, row_a + slice, row_b + slice + 1, row_a + slice + 1); + } + } + return buf; +} + +/* + Geometry for cylinders is as follows (2 stacks, 5 slices): + + + + + + + + + |\ |\ |\ |\ |\ + | \| \| \| \| \ + +--+--+--+--+--+ + +--+--+--+--+--+ 42 vertices (2 wasted) (slices + 1) * (stacks + 5) + |\ |\ |\ |\ |\ | 30 triangles (2 * slices * stacks) + (2 * slices) + | \| \| \| \| \| + +--+--+--+--+--+ + |\ |\ |\ |\ |\ | + | \| \| \| \| \| + +--+--+--+--+--+ + +--+--+--+--+--+ + \ |\ |\ |\ |\ | + \| \| \| \| \| + + + + + + + +*/ +static void _sshape_build_cylinder_cap_pole(sshape_buffer_t* buf, const sshape_cylinder_t* params, float pos_y, float norm_y, float du, float v, uint32_t* rand_seed) { + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms->transform, _sshape_vec4(0.0f, norm_y, 0.0f, 0.0f))); + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms->transform, _sshape_vec4(0.0f, pos_y, 0.0f, 1.0f)); + for (uint32_t slice = 0; slice <= params->slices; slice++) { + const _sshape_vec2_t uv = _sshape_vec2(slice * du, 1.0f - v); + const uint32_t color = params->random_colors ? _sshape_rand_color(rand_seed) : params->color; + _sshape_add_vertex(buf, tpos, tnorm, uv, color); + } +} + +static void _sshape_build_cylinder_cap_ring(sshape_buffer_t* buf, const sshape_cylinder_t* params, float pos_y, float norm_y, float du, float v, uint32_t* rand_seed) { + const float two_pi = 2.0f * 3.14159265358979323846f; + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms->transform, _sshape_vec4(0.0f, norm_y, 0.0f, 0.0f))); + for (uint32_t slice = 0; slice <= params->slices; slice++) { + const float slice_angle = (two_pi * slice) / params->slices; + const float sin_slice = sinf(slice_angle); + const float cos_slice = cosf(slice_angle); + const _sshape_vec4_t pos = _sshape_vec4(sin_slice * params->radius, pos_y, cos_slice * params->radius, 1.0f); + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms->transform, pos); + const _sshape_vec2_t uv = _sshape_vec2(slice * du, 1.0f - v); + const uint32_t color = params->random_colors ? _sshape_rand_color(rand_seed) : params->color; + _sshape_add_vertex(buf, tpos, tnorm, uv, color); + } +} + +SOKOL_SHAPE_API_DECL sshape_buffer_t sshape_build_cylinder(const sshape_buffer_t* in_buf, const sshape_cylinder_t* in_params) { + SOKOL_ASSERT(in_buf && in_params); + const sshape_cylinder_t params = _sshape_cylinder_defaults(in_params); + const uint32_t num_vertices = _sshape_cylinder_num_vertices(params.slices, params.stacks); + const uint32_t num_indices = _sshape_cylinder_num_indices(params.slices, params.stacks); + sshape_buffer_t buf = *in_buf; + if (!_sshape_validate_buffer(&buf, num_vertices, num_indices)) { + buf.valid = false; + return buf; + } + buf.valid = true; + const uint16_t start_index = _sshape_base_index(&buf); + if (!params.merge) { + _sshape_advance_offset(&buf.vertices); + _sshape_advance_offset(&buf.indices); + } + + uint32_t rand_seed = 0x12345678; + const float two_pi = 2.0f * 3.14159265358979323846f; + const float du = 1.0f / params.slices; + const float dv = 1.0f / (params.stacks + 2); + const float y0 = params.height * 0.5f; + const float y1 = -params.height * 0.5f; + const float dy = params.height / params.stacks; + + // generate vertices + _sshape_build_cylinder_cap_pole(&buf, ¶ms, y0, 1.0f, du, 0.0f, &rand_seed); + _sshape_build_cylinder_cap_ring(&buf, ¶ms, y0, 1.0f, du, dv, &rand_seed); + for (uint32_t stack = 0; stack <= params.stacks; stack++) { + const float y = y0 - dy * stack; + const float v = dv * stack + dv; + for (uint32_t slice = 0; slice <= params.slices; slice++) { + const float slice_angle = (two_pi * slice) / params.slices; + const float sin_slice = sinf(slice_angle); + const float cos_slice = cosf(slice_angle); + const _sshape_vec4_t pos = _sshape_vec4(sin_slice * params.radius, y, cos_slice * params.radius, 1.0f); + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec4_t norm = _sshape_vec4(sin_slice, 0.0f, cos_slice, 0.0f); + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, norm)); + const _sshape_vec2_t uv = _sshape_vec2(slice * du, 1.0f - v); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + _sshape_build_cylinder_cap_ring(&buf, ¶ms, y1, -1.0f, du, 1.0f - dv, &rand_seed); + _sshape_build_cylinder_cap_pole(&buf, ¶ms, y1, -1.0f, du, 1.0f, &rand_seed); + + // generate indices + { + // top-cap indices + const uint16_t row_a = start_index; + const uint16_t row_b = row_a + params.slices + 1; + for (uint16_t slice = 0; slice < params.slices; slice++) { + _sshape_add_triangle(&buf, row_a + slice, row_b + slice + 1, row_b + slice); + } + } + // shaft triangles + for (uint16_t stack = 0; stack < params.stacks; stack++) { + const uint16_t row_a = start_index + (stack + 2) * (params.slices + 1); + const uint16_t row_b = row_a + params.slices + 1; + for (uint16_t slice = 0; slice < params.slices; slice++) { + _sshape_add_triangle(&buf, row_a + slice, row_a + slice + 1, row_b + slice + 1); + _sshape_add_triangle(&buf, row_a + slice, row_b + slice + 1, row_b + slice); + } + } + { + // bottom-cap indices + const uint16_t row_a = start_index + (params.stacks + 3) * (params.slices + 1); + const uint16_t row_b = row_a + params.slices + 1; + for (uint16_t slice = 0; slice < params.slices; slice++) { + _sshape_add_triangle(&buf, row_a + slice, row_a + slice + 1, row_b + slice + 1); + } + } + return buf; +} + +/* + Geometry layout for torus (sides = 4, rings = 5): + + +--+--+--+--+--+ + |\ |\ |\ |\ |\ | + | \| \| \| \| \| + +--+--+--+--+--+ 30 vertices (sides + 1) * (rings + 1) + |\ |\ |\ |\ |\ | 40 triangles (2 * sides * rings) + | \| \| \| \| \| + +--+--+--+--+--+ + |\ |\ |\ |\ |\ | + | \| \| \| \| \| + +--+--+--+--+--+ + |\ |\ |\ |\ |\ | + | \| \| \| \| \| + +--+--+--+--+--+ +*/ +SOKOL_API_IMPL sshape_buffer_t sshape_build_torus(const sshape_buffer_t* in_buf, const sshape_torus_t* in_params) { + SOKOL_ASSERT(in_buf && in_params); + const sshape_torus_t params = _sshape_torus_defaults(in_params); + const uint32_t num_vertices = _sshape_torus_num_vertices(params.sides, params.rings); + const uint32_t num_indices = _sshape_torus_num_indices(params.sides, params.rings); + sshape_buffer_t buf = *in_buf; + if (!_sshape_validate_buffer(&buf, num_vertices, num_indices)) { + buf.valid = false; + return buf; + } + buf.valid = true; + const uint16_t start_index = _sshape_base_index(&buf); + if (!params.merge) { + _sshape_advance_offset(&buf.vertices); + _sshape_advance_offset(&buf.indices); + } + + uint32_t rand_seed = 0x12345678; + const float two_pi = 2.0f * 3.14159265358979323846f; + const float dv = 1.0f / params.sides; + const float du = 1.0f / params.rings; + + // generate vertices + for (uint32_t side = 0; side <= params.sides; side++) { + const float phi = (side * two_pi) / params.sides; + const float sin_phi = sinf(phi); + const float cos_phi = cosf(phi); + for (uint32_t ring = 0; ring <= params.rings; ring++) { + const float theta = (ring * two_pi) / params.rings; + const float sin_theta = sinf(theta); + const float cos_theta = cosf(theta); + + // torus surface position + const float spx = sin_theta * (params.radius - (params.ring_radius * cos_phi)); + const float spy = sin_phi * params.ring_radius; + const float spz = cos_theta * (params.radius - (params.ring_radius * cos_phi)); + + // torus position with ring-radius zero (for normal computation) + const float ipx = sin_theta * params.radius; + const float ipy = 0.0f; + const float ipz = cos_theta * params.radius; + + const _sshape_vec4_t pos = _sshape_vec4(spx, spy, spz, 1.0f); + const _sshape_vec4_t norm = _sshape_vec4(spx - ipx, spy - ipy, spz - ipz, 0.0f); + const _sshape_vec4_t tpos = _sshape_mat4_mul(¶ms.transform, pos); + const _sshape_vec4_t tnorm = _sshape_vec4_norm(_sshape_mat4_mul(¶ms.transform, norm)); + const _sshape_vec2_t uv = _sshape_vec2(ring * du, 1.0f - side * dv); + const uint32_t color = params.random_colors ? _sshape_rand_color(&rand_seed) : params.color; + _sshape_add_vertex(&buf, tpos, tnorm, uv, color); + } + } + + // generate indices + for (uint16_t side = 0; side < params.sides; side++) { + const uint16_t row_a = start_index + side * (params.rings + 1); + const uint16_t row_b = row_a + params.rings + 1; + for (uint16_t ring = 0; ring < params.rings; ring++) { + _sshape_add_triangle(&buf, row_a + ring, row_a + ring + 1, row_b + ring + 1); + _sshape_add_triangle(&buf, row_a + ring, row_b + ring + 1, row_b + ring); + } + } + return buf; +} + +SOKOL_API_IMPL sg_buffer_desc sshape_vertex_buffer_desc(const sshape_buffer_t* buf) { + SOKOL_ASSERT(buf && buf->valid); + sg_buffer_desc desc = { 0 }; + if (buf->valid) { + desc.type = SG_BUFFERTYPE_VERTEXBUFFER; + desc.usage = SG_USAGE_IMMUTABLE; + desc.data.ptr = buf->vertices.buffer.ptr; + desc.data.size = buf->vertices.data_size; + } + return desc; +} + +SOKOL_API_IMPL sg_buffer_desc sshape_index_buffer_desc(const sshape_buffer_t* buf) { + SOKOL_ASSERT(buf && buf->valid); + sg_buffer_desc desc = { 0 }; + if (buf->valid) { + desc.type = SG_BUFFERTYPE_INDEXBUFFER; + desc.usage = SG_USAGE_IMMUTABLE; + desc.data.ptr = buf->indices.buffer.ptr; + desc.data.size = buf->indices.data_size; + } + return desc; +} + +SOKOL_SHAPE_API_DECL sshape_element_range_t sshape_element_range(const sshape_buffer_t* buf) { + SOKOL_ASSERT(buf && buf->valid); + SOKOL_ASSERT(buf->indices.shape_offset < buf->indices.data_size); + SOKOL_ASSERT(0 == (buf->indices.shape_offset & (sizeof(uint16_t) - 1))); + SOKOL_ASSERT(0 == (buf->indices.data_size & (sizeof(uint16_t) - 1))); + sshape_element_range_t range = { 0 }; + range.base_element = (int) (buf->indices.shape_offset / sizeof(uint16_t)); + if (buf->valid) { + range.num_elements = (int) ((buf->indices.data_size - buf->indices.shape_offset) / sizeof(uint16_t)); + } + else { + range.num_elements = 0; + } + return range; +} + +SOKOL_API_IMPL sg_vertex_buffer_layout_state sshape_vertex_buffer_layout_state(void) { + sg_vertex_buffer_layout_state state = { 0 }; + state.stride = sizeof(sshape_vertex_t); + return state; +} + +SOKOL_API_IMPL sg_vertex_attr_state sshape_position_vertex_attr_state(void) { + sg_vertex_attr_state state = { 0 }; + state.offset = offsetof(sshape_vertex_t, x); + state.format = SG_VERTEXFORMAT_FLOAT3; + return state; +} + +SOKOL_API_IMPL sg_vertex_attr_state sshape_normal_vertex_attr_state(void) { + sg_vertex_attr_state state = { 0 }; + state.offset = offsetof(sshape_vertex_t, normal); + state.format = SG_VERTEXFORMAT_BYTE4N; + return state; +} + +SOKOL_API_IMPL sg_vertex_attr_state sshape_texcoord_vertex_attr_state(void) { + sg_vertex_attr_state state = { 0 }; + state.offset = offsetof(sshape_vertex_t, u); + state.format = SG_VERTEXFORMAT_USHORT2N; + return state; +} + +SOKOL_API_IMPL sg_vertex_attr_state sshape_color_vertex_attr_state(void) { + sg_vertex_attr_state state = { 0 }; + state.offset = offsetof(sshape_vertex_t, color); + state.format = SG_VERTEXFORMAT_UBYTE4N; + return state; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#endif // SOKOL_SHAPE_IMPL diff --git a/thirdparty/sokol/util/sokol_spine.h b/thirdparty/sokol/util/sokol_spine.h new file mode 100644 index 0000000..4eb9b66 --- /dev/null +++ b/thirdparty/sokol/util/sokol_spine.h @@ -0,0 +1,5945 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_SPINE_IMPL) +#define SOKOL_SPINE_IMPL +#endif +#ifndef SOKOL_SPINE_INCLUDED +/* + sokol_spine.h -- a sokol-gfx renderer for the spine-c runtime + (see https://github.com/EsotericSoftware/spine-runtimes/tree/4.1/spine-c) + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_SPINE_IMPL + + before you include this file in *one* C or C++ file to create the + implementation. + + The following defines are used by the implementation to select the + platform-specific embedded shader code (these are the same defines as + used by sokol_gfx.h and sokol_app.h): + + SOKOL_GLCORE + SOKOL_GLES3 + SOKOL_D3D11 + SOKOL_METAL + + ...optionally provide the following macros to override defaults: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_SPINE_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_SPINE_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + + If sokol_spine.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_SPINE_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + Include the following headers before including sokol_spine.h: + + sokol_gfx.h + + Include the following headers before include the sokol_spine.h *IMPLEMENTATION*: + + spine/spine.h + + You'll also need to compile and link with the spine-c runtime: + + https://github.com/EsotericSoftware/spine-runtimes/tree/4.1/spine-c/spine-c + + + FEATURE OVERVIEW + ================ + sokol_spine.h is a sokol-gfx renderer and 'handle wrapper' for Spine + (http://en.esotericsoftware.com/spine-in-depth) on top of the + spine-c runtime: http://en.esotericsoftware.com/spine-c (source code: + https://github.com/EsotericSoftware/spine-runtimes/tree/4.1/spine-c/spine-c). + + The sokol-gfx renderer allows to manage multiple contexts for rendering + Spine scenes into different sokol-gfx render passes (similar to sokol-gl and + sokol-debugtext), allows to split rendering into layers to mix Spine + rendering with other rendering operations, and it automatically batches + adjacent draw calls for Spine objects that use the same texture and in the + same layer. + + Sokol-spine wraps 'raw' spine-c objects with tagged index handles. This + eliminates the risk of memory corruption via dangling pointers. Any + API calls involving invalid objects either result in a no-op, or + in a proper error. + + The sokol-spine API exposes four 'base object types', and a number of + 'subobject types' which are owned by base objects. + + Base object types are: + + - sspine_atlas: A wrapper around a spine-c spAtlas object, each spAtlas + object owns at least one spAtlasPage object, and each spAtlasPage object + owns exactly one sokol-gfx image object. + + - sspine_skeleton: A skeleton object requires an atlas object for creation, + and is a wrapper around one spine-c spSkeletonData and one + spAnimationStateData object. both contain the shared static data for + individual spine instances + + - sspine_instance: Instance objects are created from skeleton objects. + Instances are the objects that are actually getting rendered. Each instance + tracks its own transformation and animation state, but otherwise just + references shared data of the skeleton object it was created from. An + sspine_instance object is a wrapper around one spine-c spSkeleton, + spAnimationState and spSkeletonClipping object each. + + - sspine_skinset: Skin-set objects are collections of skins which define + the look of an instance. Some Spine scenes consist of combinable skins + (for instance a human character could offer different skins for different + types of clothing, hats, scarfs, shirts, pants, and so on..., and a skin + set would represent a specific outfit). + + Subobject types allow to inspect and manipulate Spine objects in more detail: + + - sspine_anim: Each skeleton object usually offers animations which can + then be scheduled and mixed on an instance. + + - sspine_bone: Bone objects are the hierarchical transform nodes of + a skeleton. The sokol-spine API allows both to inspect the shared + static bone attributes of an sspine_skeleton object, as well as + inspecting and manipulating the per-instance bone attributes + on an sspine_instance object. + + - sspine_event: A running Spine animation may fire 'events' at certain + positions in time (for instance a 'footstep' event whenever a foot + hits the ground). Events can be used to play sound effects (or visual + effects) at the right time. + + - sspine_iktarget: Allows to set the target position for a group of + bones controlled by inverse kinematics. + + There's a couple of other subobject types which are mostly useful to + inspect the interior structure of skeletons. Those will be explained + in detail further down. + + MINIMAL API USAGE OVERVIEW + ========================== + During initialization: + + - call sspine_setup() after initializing sokol-gfx + - create an atlas object from a Spine atlas file with sspine_make_atlas() + - load and initialize the sokol-gfx image objects referenced by the atlas + - create a skeleton object from a Spine skeleton file with sspine_make_skeleton() + - create at least one instance object with sspine_make_instance() + + In the frame loop, outside of sokol-gfx render passes: + + - if needed, move instances around with sspine_set_position() + - if needed, schedule new animations with sspine_set_animation() and sspine_add_animation() + - each frame, advance the current instance animation state with sspine_update_instance() + - each frame, render instances with sspine_draw_instance_in_layer(), this just records + vertices, indices and draw commands into internal buffers, but does no actual + sokol-gfx rendering + + In the frame loop, inside a sokol-gfx render pass: + + - call sspine_draw_layer() to draw all previously recorded instances in a specific layer + + On shutdown: + + - call sspine_shutdown(), ideally before shutting down sokol-gfx + + QUICKSTART STEP BY STEP + ======================= + For a simple demo program using sokol_app.h, sokol_gfx.h and sokol_fetch.h, + see here: [TODO: add link to spine-simple-sapp wasm demo]. + + - sokol_spine.h must be included after sokol_gfx.h (this is true both + for the declaration and implementation): + + #include "sokol_gfx.h" + #include "sokol_spine.h" + + - ...and sokol_gfx.h must be initialized before sokol_spine.h: + + sg_setup(&(sg_desc){ ... }); + sspine_setup(&(sspine_desc){ ... }); + + - You should always provide a logging callback to sokol-spine, otherwise + no warning or errors will be logged. The easiest way is to use sokol_log.h + for this: + + #include "sokol_log.h" + + sspine_setup(&(sspine_desc){ + .logger = { + .func = slog_func + } + }); + + - You can tweak the memory usage of sokol-spine by limiting or expanding the + maximum number of vertices, draw commands and pool sizes: + + sspine_setup(&(sspine_desc){ + .max_vertices = 1024, // default: (1<<16) = 65536 + .max_commands = 128, // default: (1<<14) = 16384 + .context_pool_size = 1, // default: 4 + .atlas_pool_size = 1, // default: 64 + .skeleton_pool_size = 1, // default: 64 + .skinset_pool_size = 1, // default: 64 + .instance_pool_size = 16, // default: 1024 + .logger = { + .func = slog_func, + } + }); + + Sokol-spine uses 32-bit vertex-indices for rendering + (SG_INDEXTYPE_UINT32), so that the maximum number of Spine vertices + in a frame isn't limited to (1<<16). + + - You can override memory allocation and logging with your own + functions, this is explained in detail further down: + + sspine_setup(&(sspine_desc){ + .allocator = { + .alloc_fn = my_alloc, + .free_fn = my_free, + .user_data = ..., + }, + .logger = { + .log_func = my_log_func, + .user_data = ..., + } + }); + + - After initialization, the first thing you need is an sspine_atlas object. + Sokol-spine doesn't concern itself with file IO, it expects all external + data to be provided as pointer/size pairs: + + sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){ + .data = { + .ptr = ..., // pointer to Spine atlas file data in memory + .size = ..., // atlas file data size in bytes + } + }); + assert(sspine_atlas_valid(atlas)); + + If you load the atlas data asynchronously, you can still run your + per-frame rendering code without waiting for the atlas data to be loaded + and the atlas to be created. This works because calling sokol-spine + functions with 'invalid' object handles is a valid no-op. + + - Optionally you can override some or all of the atlas texture creation parameters: + + sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){ + .data = { ... }, + .overrides = { + .min_filter = SG_FILTER_NEAREST, + .mag_filter = SG_FILTER_NEAREST, + .mipmap_filter = SG_FILTER_NONE, + .wrap_u = SG_WRAP_MIRROR, + .wrap_v = SG_WRAP_MIRROR, + .premul_alpha_enabled = ..., + .premul_alpha_disabled = ..., + } + }); + + - The atlas file itself doesn't contain any texture data, it only contains + filenames of the required textures. Sokol-spine has already allocated + a sokol-gfx sg_image and sg_sample handle for each required texture, but the + actual loading and initialization must be performed by user code: + + // iterate over atlas textures and initialize sokol-gfx image objects + // with existing handles + const int num = sspine_num_images(atlas); + for (int i = 0; i < num; i++) { + const sspine_image img = sspine_image_by_index(atlas, i); + const sspine_image_info img_info = sspine_get_image_info(img); + assert(img_info.valid); + assert(!img_info.filename.truncated); + + // the filename is now in img_info.filename.cstr, 'somehow' + // load and decode the image data into memory, and then + // initialize the sokol-gfx image from the existing sg_image handle + // in img_info.sgimage: + sg_init_image(img_info.sgimage, &(sg_image_desc){ + .width = ..., + .height = ..., + .pixel_format = ..., + .data.subimage[0][0] = { + .ptr = ..., // pointer to decoded image pixel data + .size = ..., // size of decoded image pixel data in bytes + } + }); + + // ...and same procedure for the sampler object + sg_init_sampler(img_info.sgsampler, &(sg_image_desc){ + .min_filter = img_info.min_filter, + .mag_filter = img_info.mag_filter, + .mipmap_filter = img_info.mipmap_filter, + .wrap_u = img_info.wrap_u, + .wrap_v = img_info.wrap_v, + }); + } + + If you load the image data asynchronously, you can still simply start rendering + before the image data is loaded. This works because sokol-gfx will silently drop + any rendering operations that involve 'incomplete' objects. + + - Once an atlas object has been created (independently from loading any image data), + an sspine_skeleton object is needed next. This requires a valid atlas object + handle as input, and a pointer to the Spine skeleton file data loaded into memory. + + Spine skeleton files come in two flavours: binary or json, for binary data, + a ptr/size pair must be provided: + + sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){ + .atlas = atlas, // atlas must be a valid sspine_atlas handle + .binary_data = { + .ptr = ..., // pointer to binary skeleton data in memory + .size = ..., // size of binary skeleton data in bytes + } + }); + assert(sspine_skeleton_valid(skeleton)); + + For JSON skeleton file data, the data must be provided as a zero-terminated C string: + + sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){ + .atlas = atlas, + .json_data = ..., // JSON skeleton data as zero-terminated(!) C-string + }); + + Like with all sokol-spine objects, if you load the skeleton data asynchronously + and only then create a skeleton object, you can already start rendering before + the data is loaded and the Spine objects have been created. Any operations + involving 'incomplete' handles will be dropped. + + - You can pre-scale the Spine scene size, and you can provide a default cross-fade + duration for animation mixing: + + sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){ + .atlas = atlas, + .binary_data = { ... }, + .prescale = 0.5f, // scale to half-size + .anim_default_mix = 0.2f, // default anim mixing cross-fade duration 0.2 seconds + }); + + - Once the skeleton object has been created, it's finally time to create one or many instance objects. + If you want to independently render and animate the 'same' Spine object many times in a frame, + you should only create one sspine_skeleton object, and then as many sspine_instance object + as needed from the shared skeleton object: + + sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){ + .skeleton = skeleton, // must be a valid skeleton handle + }); + assert(sspine_instance_valid(instance)); + + After creation, the sspine_instance will have a 'default skin' set as its appearance. + + - To set the position of an instance: + + sspine_set_position(inst, (sspine_vec2){ .x=..., .y=... }); + + Sokol-spine doesn't define a specific unit (like pixels or meters), instead the + rendering coordinate system is defined later at 'render time'. + + - To schedule an initial looping animation by its name: + + // first lookup up the animation by name on the skeleton: + sspine_anim anim = sspine_anim_by_name(skeleton, "walk"); + assert(sspine_anim_valid(anim)); + + // then schedule the animation on the instance, on mixer track 0, as looping: + sspine_set_animation(instance, anim, 0, true); + + Scheduling and mixing animations will be explained in more detail further down. + + - To advance and mix instance animations: + + sspine_update_instance(instance, delta_time_in_seconds); + + Usually you'd call this each frame for each active instance with the + frame duration in seconds. + + - Now it's finally time to 'render' the instance at its current position and + animation state: + + sspine_draw_instance_in_layer(instance, 0); + + Instances are generally rendered into numbered virtual 'render layers' (in this + case, layer 0). Layers are useful for interleaving sokol-spine rendering + with other rendering commands (like background and foreground tile maps, + sprites or text). + + - It's important to note that no actual sokol-gfx rendering happens in + sspine_draw_instance_in_layer(), instead only vertices, indices and + draw commands are recorded into internal memory buffes. + + - The only sokol-spine function which *must* (and should) be called inside + a sokol-gfx rendering pass is sspine_draw_layer(). + + This renders all draw commands that have been recorded previously in a + specific layer via sspine_draw_instance_in_layer(). + + const sspine_layer_transform tform = { ... }; + + sg_begin_default_pass(...); + sspine_draw_layer(0, tform); + sg_end_pass(); + sg_commit(); + + IMPORTANT: DO *NOT* MIX any calls to sspine_draw_instance_in_layer() + with sspine_draw_layer(), as this will confuse the internal draw command + recording. Ideally, move all sokol-gfx pass rendering (including all + sspine_draw_layer() calls) towards the end of the frame, separate from + any other sokol-spine calls. + + The sspine_layer_transform struct defines the layer's screen space coordinate + system. For instance to map Spine coordinates to framebuffer pixels, with the + origin in the screen center, you'd setup the layer transform like this: + + const float width = sapp_widthf(); + const float height = sapp_heightf(); + const sspine_layer_transform tform = { + .size = { .x = width, .y = height }, + .origin = { .x = width * 0.5f, .y = height * 0.5f }, + }; + + With this pixel mapping, the Spine scene would *not* scale with window size, + which often is not very useful. Instead it might make more sense to render + to a fixed 'virtual' resolution, for instance 1024 * 768: + + const sspine_layer_transform tform = { + .size = { .x = 1024.0f, .y = 768.0f }, + .origin = { .x = 512.0f, .y = 384.0f }, + }; + + How to configure a virtual resolution with a fixed aspect ratio is + left as an exercise to the reader ;) + + - That's it for basic sokol-spine setup and rendering. Any existing objects + will automatically be cleaned up when calling sspine_shutdown(), this + should be called before shutting down sokol-gfx, but this is not required: + + sspine_shutdown(); + sg_shutdown(); + + - You can explicitly destroy the base object types if you don't need them + any longer. This will cause the underlying spine-c objects to be + freed and the memory to be returned to the operating system: + + sspine_destroy_instance(instance); + sspine_destroy_skinset(skinset); + sspine_destroy_skeleton(skeleton); + sspine_destroy_atlas(atlas); + + You can destroy these objects in any order without causing memory corruption + issues. Instead any dependent object handles will simply become invalid (e.g. + if you destroy an atlas object, all skeletons and instances created from + this atlas will 'technically' still exist, but their handles will resolve to + 'invalid' and all sokol-spine calls involving these handles will silently fail). + + For instance: + + // create an atlas, skeleton and instance + sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){ ... }); + assert(sspine_atlas_valid(atlas)); + + sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){ + .atlas = atlas, + ... + }); + assert(sspine_skeleton_valid(skeleton)); + + sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){ + .skeleton = skeleton, + }); + assert(sspine_instance_valid(instance)); + + // destroy the atlas object: + sspine_destroy_atlas(atlas); + + // the skeleton and instance handle should now be invalid, but + // otherwise, nothing bad will happen: + if (!sspine_skeleton_valid(skeleton)) { + ... + } + if (!sspine_instance_valid(instance)) { + ... + } + + RENDERER DETAILS + ================ + Any rendering related work happens in the functions sspine_draw_instance_in_layer() and + sspine_draw_layer(). + + sspine_draw_instance_in_layer() will result in vertices, indices and internal + draw commands which will be recorded into internal memory buffers (e.g. + no sokol-gfx functions will be called here). + + If possible, batching will be performed by merging a new draw command with + the previously recorded draw command. For two draw commands to be merged, + the following conditions must be tru: + + - rendering needs to go into the same layer + - the same atlas texture must be used + - the blend mode must be compatible (the Spine blending modes + 'normal' and 'additive' can be merged, but not 'multiply') + - the same premultiplied alpha mode must be used + + To make the most out of batching: + + - use Spine objects which only have a single atlas texture + and blend mode across all slots + - group sspine_draw_instance_in_layer() calls by layer + + After all instances have been 'rendered' (or rather: recorded) into layers, + the actually rendering happens inside a sokol-gfx pass by calling the + function sspine_draw_layer() for each layer in 'z order' (e.g. the layer + index doesn't matter for z-ordering, only the order how sspine_draw_layer() is + called). + + Only the first call to sspine_draw_layer() in a frame will copy the recorded + vertices and indices into sokol-gfx buffers. + + Each call to sspine_draw_layer() will iterate over all recorded (and + hopefully well-batched) draw commands, skip any draw commands with a + non-matching layer index, and draw only those with a matching layer by + calling: + + - if the pipeline object has changed: + - sg_apply_pipeline() + - sg_apply_uniforms() for the vertex stage + - if the atlas texture has changed: + - sg_apply_bindings() + - if the premultiplied-alpha mode has changed: + - sg_apply_uniforms() for the fragment stage + - and finally sg_draw() + + The main purpose of render layers is to mix Spine rendering with other + render operations. In the not too distant future, the same render layer idea + will also be implemented at least for sokol-gl and sokol-debugtext. + + FIXME: does this section need more details about layer transforms? + + RENDERING WITH CONTEXTS + ======================= + At first glance, render contexts may look like more heavy-weight + render layers, but they serve a different purpose: they are useful + if Spine rendering needs to happen in different sokol-gfx render passes + with different pixel formats and MSAA sample counts. + + All Spine rendering happens within a context, even you don't call any + of the context API functions, in this case, an internal 'default context' + will be used. + + Each context has its own internal vertex-, index- and command buffer and + all context state is completely independent from any other contexts. + + To create a new context object, call: + + sspine_context ctx = sspine_make_context(&(sspine_context_desc){ + .max_vertices = ..., + .max_commands = ..., + .color_format = SG_PIXELFORMAT_..., + .depth_format = SG_PIXELFORMAT_..., + .sample_count = ..., + .color_write_mask = SG_COLORMASK_..., + }); + + The color_format, depth_format and sample_count items must be compatible + with the sokol-gfx render pass you're going to render into. + + If you omit the color_format, depth_format and sample_count designators, + the new context will be compatible with the sokol-gfx default pass + (which is most likely not what you want, unless your offscreen render passes + exactly match the default pass attributes). + + Once a context has been created, it can be made active with: + + sspine_set_context(ctx); + + To set the default context again: + + sspine_set_contxt(sspine_default_context()); + + ...and to get the currently active context: + + sspine_context cur_ctx = sspine_get_context(); + + The currently active context only matter for two functions: + + - sspine_draw_instance_in_layer() + - sspine_draw_layer() + + Alternatively you can bypass the currently set context with these + alternative functions: + + - sspine_context_draw_layer_in_instance(ctx, ...) + - sspine_context_draw_layer(ctx, ...) + + These explicitly take a context argument, completely ignore + and don't change the active context. + + You can query some information about a context with the function: + + sspine_context_info info = ssgpine_get_context_info(ctx); + + This returns the current number of recorded vertices, indices + and draw commands. + + RESOURCE STATES: + ================ + Similar to sokol-gfx, you can query the current 'resource state' of Spine + objects: + + sspine_resource_state sspine_get_atlas_resource_state(sspine_atlas atlas); + sspine_resource_state sspine_get_skeleton_resource_state(sspine_atlas atlas); + sspine_resource_state sspine_get_instance_resource_state(sspine_atlas atlas); + sspine_resource_state sspine_get_skinset_resource_state(sspine_atlas atlas); + sspine_resource_state sspine_get_context_resource_state(sspine_atlas atlas); + + This returns one of + + - SSPINE_RESOURCE_VALID: the object is valid and ready to use + - SSPINE_RESOURCE_FAILED: the object creation has failed + - SSPINE_RESOURCE_INVALID: the object or one of its dependencies is + invalid, it either no longer exists, or the handle hasn't been + initialized with a call to one of the object creation functions + + MISC HELPER FUNCTIONS: + ====================== + There's a couple of helper functions which don't fit into a big enough category + of their own: + + You can ask a skeleton for the atlas it has been created from: + + sspine_atlas atlas = sspine_get_skeleton_atlas(skeleton); + + ...and likewise, ask an instance for the skeleton it has been created from: + + sspine_skeleton skeleton = sspine_get_instance_skeleton(instance); + + ...and finally you can convert a layer transform struct into a 4x4 projection + matrix that's memory-layout compatible with sokol-gl: + + const sspine_layer_transform tform = { ... }; + const sspine_mat4 proj = sspine_layer_transform_to_mat4(&tform); + sgl_matrix_mode_projection(); + sgl_load_matrix(proj.m); + + ANIMATIONS + ========== + Animations have their own handle type sspine_anim. A valid sspine_anim + handle is either obtained by looking up an animation by name from a skeleton: + + sspine_anim anim = sspine_anim_by_name(skeleton, "walk"); + + ...or by index: + + sspine_anim anim = sspine_anim_by_index(skeleton, 0); + + The returned anim handle will be invalid if an animation of that name doesn't + exist, or the provided index is out-of-range: + + if (!sspine_anim_is_valid(anim)) { + // animation handle is not valid + } + + An animation handle will also become invalid when the skeleton object it was + created is destroyed, or otherwise becomes invalid. + + You can iterate over all animations in a skeleton: + + const int num_anims = sspine_num_anims(skeleton); + for (int anim_index = 0; anim_index < num_anims; anim_index++) { + sspine_anim anim = sspine_anim_by_index(skeleton, anim_index); + ... + } + + Since sspine_anim is a 'fat handle' (it houses a skeleton handle and an index), + there's a helper function which checks if two anim handles are equal: + + if (sspine_anim_equal(anim0, anim1)) { + ... + } + + To query information about an animation: + + const sspine_anim_info info = sspine_get_anim_info(anim); + if (info.valid) { + printf("index: %d, duration: %f, name: %s", info.index, info.duration, info.name.cstr); + } + + Scheduling and mixing animations is controlled through the following functions: + + void sspine_clear_animation_tracks(sspine_instance instance); + void sspine_clear_animation_track(sspine_instance instance, int track_index); + void sspine_set_animation(sspine_instance instance, sspine_anim anim, int track_index, bool loop); + void sspine_add_animation(sspine_instance instance, sspine_anim anim, int track_index, bool loop, float delay); + void sspine_set_empty_animation(sspine_instance instance, int track_index, float mix_duration); + void sspine_add_empty_animation(sspine_instance instance, int track_index, float mix_duration, float delay); + + Please refer to the spine-c documentation to get an idea what these functions do: + + http://en.esotericsoftware.com/spine-c#Applying-animations + + EVENTS + ====== + For a general idea of Spine events, see here: http://esotericsoftware.com/spine-events + + After calling sspine_update_instance() to advance the currently configured animations, + you can poll for triggered events like this: + + const int num_triggered_events = sspine_num_triggered_events(instance); + for (int i = 0; i < num_triggered_events; i++) { + const sspine_triggered_event_info info = sspine_get_triggered_event_info(instance, i); + if (info.valid) { + ... + } + } + + The returned sspine_triggered_event_info struct gives you the current runtime properties + of the event (in case the event has keyed properties). For the actual list of event + properties please see the actual sspine_triggered_event_info struct declaration. + + It's also possible to inspect the static event definition on a skeleton, this works + the same as iterating through animations. You can lookup an event by name, + get the number of events, lookup an event by its index, and get detailed + information about an event: + + int sspine_num_events(sspine_skeleton skeleton); + sspine_event sspine_event_by_name(sspine_skeleton skeleton, const char* name); + sspine_event sspine_event_by_index(sspine_skeleton skeleton, int index); + bool sspine_event_valid(sspine_event event); + bool sspine_event_equal(sspine_event first, sspine_event second); + sspine_event_info sspine_get_event_info(sspine_event event); + + (FIXME: shouldn't the event info struct contains an sspine_anim handle?) + + IK TARGETS + ========== + The IK target function group allows to iterate over the IK targets that have been + defined on a skeleton, find an IK target by name, get detailed information about + an IK target, and most importantly, set the world space position of an IK target + which updates the position of all bones influenced by the IK target: + + int sspine_num_iktargets(sspine_skeleton skeleton); + sspine_iktarget sspine_iktarget_by_name(sspine_skeleton skeleton, const char* name); + sspine_iktarget sspine_iktarget_by_index(sspine_skeleton skeleton, int index); + bool sspine_iktarget_valid(sspine_iktarget iktarget); + bool sspine_iktarget_equal(sspine_iktarget first, sspine_iktarget second); + sspine_iktarget_info sspine_get_iktarget_info(sspine_iktarget iktarget); + void sspine_set_iktarget_world_pos(sspine_instance instance, sspine_iktarget iktarget, sspine_vec2 world_pos); + + BONES + ===== + Skeleton bones are wrapped with an sspine_bone handle which can be created from + a skeleton handle, and either a bone name: + + sspine_bone bone = sspine_bone_by_name(skeleton, "root"); + assert(sspine_bone_valid(bone)); + + ...or a bone index: + + sspine_bone bone = sspine_bone_by_index(skeleton, 0); + assert(sspine_bone_valid(bone)); + + ...to iterate over all bones of a skeleton and query information about each + bone: + + const int num_bones = sspine_num_bones(skeleton); + for (int bone_index = 0; bone_index < num_bones; bone_index++) { + sspine_bone bone = sspine_bone_by_index(skeleton, bone_index); + const sspine_bone_info info = sspine_get_bone_info(skeleton, bone); + if (info.valid) { + ... + } + } + + The sspine_bone_info struct provides the shared, static bone state in the skeleton (like + the name, a parent bone handle, bone length, pose transform and a color attribute), + but doesn't contain any dynamic information of per-instance bones. + + To manipulate the per-instance bone attributes use the following setter functions: + + void sspine_set_bone_transform(sspine_instance instance, sspine_bone bone, const sspine_bone_transform* transform); + void sspine_set_bone_position(sspine_instance instance, sspine_bone bone, sspine_vec2 position); + void sspine_set_bone_rotation(sspine_instance instance, sspine_bone bone, float rotation); + void sspine_set_bone_scale(sspine_instance instance, sspine_bone bone, sspine_vec2 scale); + void sspine_set_bone_shear(sspine_instance instance, sspine_bone bone, sspine_vec2 shear); + + ...and to query the per-instance bone attributes, the following getters: + + sspine_bone_transform sspine_get_bone_transform(sspine_instance instance, sspine_bone bone); + sspine_vec2 sspine_get_bone_position(sspine_instance instance, sspine_bone bone); + float sspine_get_bone_rotation(sspine_instance instance, sspine_bone bone); + sspine_vec2 sspine_get_bone_scale(sspine_instance instance, sspine_bone bone); + sspine_vec2 sspine_get_bone_shear(sspine_instance instance, sspine_bone bone); + + These functions all work in the local bone coordinate system (relative to a bone's parent bone). + + To transform positions between bone-local and global space use the following helper functions: + + sspine_vec2 sspine_bone_local_to_world(sspine_instance instance, sspine_bone bone, sspine_vec2 local_pos); + sspine_vec2 sspine_bone_world_to_local(sspine_instance instance, sspine_bone bone, sspine_vec2 world_pos); + + ...and as a convenience, there's a helper function which obtains the bone position in global space + directly: + + sspine_vec2 sspine_get_bone_world_position(sspine_instance instance, sspine_bone bone); + + SKINS AND SKINSETS + ================== + Skins are named pieces of geometry which can be turned on and off, what makes Spine skins a bit + confusing is that they are hierarchical. A skin can itself be a collection of other skins. Setting + the 'root skin' will also make all 'child skins' visible. In sokol-spine collections of skins are + managed through dedicated 'skin set' objects. Under the hood they create a 'root skin' where the + skins of the skin set are attached to, but from the outside it just looks like a 'flat' collection + of skins without the tricky hierarchical management. + + Like other 'subobjects', skin handles can be obtained by the skin name from a skeleton handle: + + sspine_skin skin = sspine_skin_by_name(skeleton, "jacket"); + assert(sspine_skin_valid(skin)); + + ...or by a skin index: + + sspine_skin skin = sspine_skin_by_index(skeleton, 0); + assert(sspine_skin_valid(skin)); + + ...you can iterate over all skins of a skeleton and query some information about the skin: + + const int num_skins = sspine_num_skins(skeleton); + for (int skin_index = 0; skin_index < num_skins; skin_index++) { + sspine_skin skin = sspine_skin_by_index(skin_index); + sspine_skin_info info = sspine_get_skin_info(skin); + if (info.valid) { + ... + } + } + + Currently, the only useful query item is the skin name though. + + To make a skin visible on an instance, just call: + + sspine_set_skin(instance, skin); + + ...this will first deactivate the previous skin before setting a new skin. + + A more powerful way to configure the skin visibility is through 'skin sets'. Skin + sets are simply flat collections of skins which should be made visible at once. + A new skin set is created like this: + + sspine_skinset skinset = sspine_make_skinset(&(sspine_skinset_desc){ + .skeleton = skeleton, + .skins = { + sspine_skin_by_name(skeleton, "blue-jacket"), + sspine_skin_by_name(skeleton, "green-pants"), + sspine_skin_by_name(skeleton, "blonde-hair"), + ... + } + }); + assert(sspine_skinset_valid(skinset)) + + ...then simply set the skinset on an instance to reconfigure the appearance + of the instance: + + sspine_set_skinset(instance, skinset); + + The functions sspine_set_skinset() and sspine_set_skin() will cancel each other. + Calling sspine_set_skinset() deactivates the effect of sspine_set_skin() and + vice versa. + + + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call, + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + sspine_setup(&(sspine_desc){ .logger.func = slog_func }); + + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'sspine' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SSPINE_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_spine.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-spine like this: + + sspine_setup(&(sspine_desc){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); + + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. + + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + sspine_setup(&(sspine_desc){ + // ... + .allocator = { + .alloc_fn = my_alloc, + .free_fn = my_free, + .user_data = ...; + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_gfx.h + itself though, not any allocations in OS libraries. + + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2022 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_SPINE_INCLUDED (1) +#include +#include +#include // size_t + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_spine.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_SPINE_API_DECL) +#define SOKOL_SPINE_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_SPINE_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_SPINE_IMPL) +#define SOKOL_SPINE_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_SPINE_API_DECL __declspec(dllimport) +#else +#define SOKOL_SPINE_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + SSPINE_INVALID_ID = 0, + SSPINE_MAX_SKINSET_SKINS = 32, + SSPINE_MAX_STRING_SIZE = 61, // see sspine_string struct +}; + +typedef struct sspine_context { uint32_t id; } sspine_context; +typedef struct sspine_atlas { uint32_t id; } sspine_atlas; +typedef struct sspine_skeleton { uint32_t id; } sspine_skeleton; +typedef struct sspine_instance { uint32_t id; } sspine_instance; +typedef struct sspine_skinset { uint32_t id; } sspine_skinset; + +typedef struct sspine_image { uint32_t atlas_id; int index; } sspine_image; +typedef struct sspine_atlas_page { uint32_t atlas_id; int index; } sspine_atlas_page; +typedef struct sspine_anim { uint32_t skeleton_id; int index; } sspine_anim; +typedef struct sspine_bone { uint32_t skeleton_id; int index; } sspine_bone; +typedef struct sspine_slot { uint32_t skeleton_id; int index; } sspine_slot; +typedef struct sspine_event { uint32_t skeleton_id; int index; } sspine_event; +typedef struct sspine_iktarget { uint32_t skeleton_id; int index; } sspine_iktarget; +typedef struct sspine_skin { uint32_t skeleton_id; int index; } sspine_skin; + +typedef struct sspine_range { const void* ptr; size_t size; } sspine_range; +typedef struct sspine_vec2 { float x, y; } sspine_vec2; +typedef struct sspine_mat4 { float m[16]; } sspine_mat4; +typedef sg_color sspine_color; + +typedef struct sspine_string { + bool valid; + bool truncated; + uint8_t len; + char cstr[SSPINE_MAX_STRING_SIZE]; +} sspine_string; + +typedef enum sspine_resource_state { + SSPINE_RESOURCESTATE_INITIAL, + SSPINE_RESOURCESTATE_ALLOC, + SSPINE_RESOURCESTATE_VALID, + SSPINE_RESOURCESTATE_FAILED, + SSPINE_RESOURCESTATE_INVALID, + _SSPINE_RESOURCESTATE_FORCE_U32 = 0x7FFFFFFF +} sspine_resource_state; + +// log item codes via x-macro magic +#define _SSPINE_LOG_ITEMS \ + _SSPINE_LOGITEM_XMACRO(OK, "Ok")\ + _SSPINE_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed")\ + _SSPINE_LOGITEM_XMACRO(CONTEXT_POOL_EXHAUSTED, "context pool exhausted (adjust via sspine_desc.context_pool_size)")\ + _SSPINE_LOGITEM_XMACRO(ATLAS_POOL_EXHAUSTED, "atlas pool exhausted (adjust via sspine_desc.atlas_pool_size)")\ + _SSPINE_LOGITEM_XMACRO(SKELETON_POOL_EXHAUSTED, "skeleton pool exhausted (adjust via sspine_desc.skeleton_pool_size)")\ + _SSPINE_LOGITEM_XMACRO(SKINSET_POOL_EXHAUSTED, "skinset pool exhausted (adjust via sspine_desc.skinset_pool_size)")\ + _SSPINE_LOGITEM_XMACRO(INSTANCE_POOL_EXHAUSTED, "instance pool exhausted (adjust via sspine_desc.instance_pool_size)")\ + _SSPINE_LOGITEM_XMACRO(CANNOT_DESTROY_DEFAULT_CONTEXT, "cannot destroy default context")\ + _SSPINE_LOGITEM_XMACRO(ATLAS_DESC_NO_DATA, "no data provided in sspine_atlas_desc.data")\ + _SSPINE_LOGITEM_XMACRO(SPINE_ATLAS_CREATION_FAILED, "spAtlas_create() failed")\ + _SSPINE_LOGITEM_XMACRO(SG_ALLOC_IMAGE_FAILED, "sg_alloc_image() failed")\ + _SSPINE_LOGITEM_XMACRO(SG_ALLOC_SAMPLER_FAILED, "sg_alloc_sampler() failed")\ + _SSPINE_LOGITEM_XMACRO(SKELETON_DESC_NO_DATA, "no data provided in sspine_skeleton_desc.json_data or .binary_data")\ + _SSPINE_LOGITEM_XMACRO(SKELETON_DESC_NO_ATLAS, "no atlas object provided in sspine_skeleton_desc.atlas")\ + _SSPINE_LOGITEM_XMACRO(SKELETON_ATLAS_NOT_VALID, "sspine_skeleton_desc.atlas is not in valid state")\ + _SSPINE_LOGITEM_XMACRO(CREATE_SKELETON_DATA_FROM_JSON_FAILED, "spSkeletonJson_readSkeletonData() failed")\ + _SSPINE_LOGITEM_XMACRO(CREATE_SKELETON_DATA_FROM_BINARY_FAILED, "spSkeletonBinary_readSkeletonData() failed")\ + _SSPINE_LOGITEM_XMACRO(SKINSET_DESC_NO_SKELETON, "no skeleton object provided in sspine_skinset_desc.skeleton")\ + _SSPINE_LOGITEM_XMACRO(SKINSET_SKELETON_NOT_VALID, "sspine_skinset_desc.skeleton is not in valid state")\ + _SSPINE_LOGITEM_XMACRO(SKINSET_INVALID_SKIN_HANDLE, "invalid skin handle in sspine_skinset_desc.skins[]")\ + _SSPINE_LOGITEM_XMACRO(INSTANCE_DESC_NO_SKELETON, "no skeleton object provided in sspine_instance_desc.skeleton")\ + _SSPINE_LOGITEM_XMACRO(INSTANCE_SKELETON_NOT_VALID, "sspine_instance_desc.skeleton is not in valid state")\ + _SSPINE_LOGITEM_XMACRO(INSTANCE_ATLAS_NOT_VALID, "skeleton's atlas object no longer valid via sspine_instance_desc.skeleton")\ + _SSPINE_LOGITEM_XMACRO(SPINE_SKELETON_CREATION_FAILED, "spSkeleton_create() failed")\ + _SSPINE_LOGITEM_XMACRO(SPINE_ANIMATIONSTATE_CREATION_FAILED, "spAnimationState_create() failed")\ + _SSPINE_LOGITEM_XMACRO(SPINE_SKELETONCLIPPING_CREATION_FAILED, "spSkeletonClipping_create() failed")\ + _SSPINE_LOGITEM_XMACRO(COMMAND_BUFFER_FULL, "command buffer full (adjust via sspine_desc.max_commands)")\ + _SSPINE_LOGITEM_XMACRO(VERTEX_BUFFER_FULL, "vertex buffer (adjust via sspine_desc.max_vertices)")\ + _SSPINE_LOGITEM_XMACRO(INDEX_BUFFER_FULL, "index buffer full (adjust via sspine_desc.max_vertices)")\ + _SSPINE_LOGITEM_XMACRO(STRING_TRUNCATED, "a string has been truncated")\ + _SSPINE_LOGITEM_XMACRO(ADD_COMMIT_LISTENER_FAILED, "sg_add_commit_listener() failed")\ + +#define _SSPINE_LOGITEM_XMACRO(item,msg) SSPINE_LOGITEM_##item, +typedef enum sspine_log_item { + _SSPINE_LOG_ITEMS +} sspine_log_item; +#undef _SSPINE_LOGITEM_XMACRO + +typedef struct sspine_layer_transform { + sspine_vec2 size; + sspine_vec2 origin; +} sspine_layer_transform; + +typedef struct sspine_bone_transform { + sspine_vec2 position; + float rotation; // in degrees + sspine_vec2 scale; + sspine_vec2 shear; // in degrees +} sspine_bone_transform; + +typedef struct sspine_context_desc { + int max_vertices; + int max_commands; + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; + sg_color_mask color_write_mask; +} sspine_context_desc; + +typedef struct sspine_context_info { + int num_vertices; // current number of vertices + int num_indices; // current number of indices + int num_commands; // current number of commands +} sspine_context_info; + +typedef struct sspine_image_info { + bool valid; + sg_image sgimage; + sg_sampler sgsampler; + sg_filter min_filter; + sg_filter mag_filter; + sg_filter mipmap_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + int width; + int height; + bool premul_alpha; + sspine_string filename; +} sspine_image_info; + +typedef struct sspine_atlas_overrides { + sg_filter min_filter; + sg_filter mag_filter; + sg_filter mipmap_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + bool premul_alpha_enabled; + bool premul_alpha_disabled; +} sspine_atlas_overrides; + +typedef struct sspine_atlas_desc { + sspine_range data; + sspine_atlas_overrides override; +} sspine_atlas_desc; + +typedef struct sspine_atlas_page_info { + bool valid; + sspine_atlas atlas; + sspine_image_info image; + sspine_atlas_overrides overrides; +} sspine_atlas_page_info; + +typedef struct sspine_skeleton_desc { + sspine_atlas atlas; + float prescale; + float anim_default_mix; + const char* json_data; + sspine_range binary_data; +} sspine_skeleton_desc; + +typedef struct sspine_skinset_desc { + sspine_skeleton skeleton; + sspine_skin skins[SSPINE_MAX_SKINSET_SKINS]; +} sspine_skinset_desc; + +typedef struct sspine_anim_info { + bool valid; + int index; + float duration; + sspine_string name; +} sspine_anim_info; + +typedef struct sspine_bone_info { + bool valid; + int index; + sspine_bone parent_bone; + float length; + sspine_bone_transform pose; + sspine_color color; + sspine_string name; +} sspine_bone_info; + +typedef struct sspine_slot_info { + bool valid; + int index; + sspine_bone bone; + sspine_color color; + sspine_string attachment_name; + sspine_string name; +} sspine_slot_info; + +typedef struct sspine_iktarget_info { + bool valid; + int index; + sspine_bone target_bone; + sspine_string name; +} sspine_iktarget_info; + +typedef struct sspine_skin_info { + bool valid; + int index; + sspine_string name; +} sspine_skin_info; + +typedef struct sspine_event_info { + bool valid; + int index; + int int_value; + float float_value; + float volume; + float balance; + sspine_string name; + sspine_string string_value; + sspine_string audio_path; +} sspine_event_info; + +typedef struct sspine_triggered_event_info { + bool valid; + sspine_event event; + float time; + int int_value; + float float_value; + float volume; + float balance; + sspine_string string_value; +} sspine_triggered_event_info; + +typedef struct sspine_instance_desc { + sspine_skeleton skeleton; +} sspine_instance_desc; + +typedef struct sspine_allocator { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} sspine_allocator; + +typedef struct sspine_logger { + void (*func)( + const char* tag, // always "sspine" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SSPINE_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_spine.h + const char* filename_or_null, // the source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} sspine_logger; + +typedef struct sspine_desc { + int max_vertices; + int max_commands; + int context_pool_size; + int atlas_pool_size; + int skeleton_pool_size; + int skinset_pool_size; + int instance_pool_size; + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; + sg_color_mask color_write_mask; + sspine_allocator allocator; // optional allocation override functions (default: malloc/free) + sspine_logger logger; // optional logging function (default: NO LOGGING!) +} sspine_desc; + +// setup/shutdown +SOKOL_SPINE_API_DECL void sspine_setup(const sspine_desc* desc); +SOKOL_SPINE_API_DECL void sspine_shutdown(void); + +// context functions +SOKOL_SPINE_API_DECL sspine_context sspine_make_context(const sspine_context_desc* desc); +SOKOL_SPINE_API_DECL void sspine_destroy_context(sspine_context ctx); +SOKOL_SPINE_API_DECL void sspine_set_context(sspine_context ctx); +SOKOL_SPINE_API_DECL sspine_context sspine_get_context(void); +SOKOL_SPINE_API_DECL sspine_context sspine_default_context(void); +SOKOL_SPINE_API_DECL sspine_context_info sspine_get_context_info(sspine_context ctx); + +// create and destroy spine objects +SOKOL_SPINE_API_DECL sspine_atlas sspine_make_atlas(const sspine_atlas_desc* desc); +SOKOL_SPINE_API_DECL sspine_skeleton sspine_make_skeleton(const sspine_skeleton_desc* desc); +SOKOL_SPINE_API_DECL sspine_skinset sspine_make_skinset(const sspine_skinset_desc* desc); +SOKOL_SPINE_API_DECL sspine_instance sspine_make_instance(const sspine_instance_desc* desc); +SOKOL_SPINE_API_DECL void sspine_destroy_atlas(sspine_atlas atlas); +SOKOL_SPINE_API_DECL void sspine_destroy_skeleton(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL void sspine_destroy_skinset(sspine_skinset skinset); +SOKOL_SPINE_API_DECL void sspine_destroy_instance(sspine_instance instance); + +// configure instance appearance via skinsets +SOKOL_SPINE_API_DECL void sspine_set_skinset(sspine_instance instance, sspine_skinset skinset); + +// update instance animations before drawing +SOKOL_SPINE_API_DECL void sspine_update_instance(sspine_instance instance, float delta_time); + +// iterate over triggered events after updating an instance +SOKOL_SPINE_API_DECL int sspine_num_triggered_events(sspine_instance instance); +SOKOL_SPINE_API_DECL sspine_triggered_event_info sspine_get_triggered_event_info(sspine_instance instance, int triggered_event_index); + +// draw instance into current or explicit context +SOKOL_SPINE_API_DECL void sspine_draw_instance_in_layer(sspine_instance instance, int layer); +SOKOL_SPINE_API_DECL void sspine_context_draw_instance_in_layer(sspine_context ctx, sspine_instance instance, int layer); + +// helper function to convert sspine_layer_transform into projection matrix +SOKOL_SPINE_API_DECL sspine_mat4 sspine_layer_transform_to_mat4(const sspine_layer_transform* tform); + +// draw a layer in current context or explicit context (call once per context and frame in sokol-gfx pass) +SOKOL_SPINE_API_DECL void sspine_draw_layer(int layer, const sspine_layer_transform* tform); +SOKOL_SPINE_API_DECL void sspine_context_draw_layer(sspine_context ctx, int layer, const sspine_layer_transform* tform); + +// get current resource state (INITIAL, ALLOC, VALID, FAILED, INVALID) +SOKOL_SPINE_API_DECL sspine_resource_state sspine_get_context_resource_state(sspine_context context); +SOKOL_SPINE_API_DECL sspine_resource_state sspine_get_atlas_resource_state(sspine_atlas atlas); +SOKOL_SPINE_API_DECL sspine_resource_state sspine_get_skeleton_resource_state(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL sspine_resource_state sspine_get_skinset_resource_state(sspine_skinset skinset); +SOKOL_SPINE_API_DECL sspine_resource_state sspine_get_instance_resource_state(sspine_instance instance); + +// shortcut for sspine_get_*_state() == SSPINE_RESOURCESTATE_VALID +SOKOL_SPINE_API_DECL bool sspine_context_valid(sspine_context context); +SOKOL_SPINE_API_DECL bool sspine_atlas_valid(sspine_atlas atlas); +SOKOL_SPINE_API_DECL bool sspine_skeleton_valid(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL bool sspine_instance_valid(sspine_instance instance); +SOKOL_SPINE_API_DECL bool sspine_skinset_valid(sspine_skinset skinset); + +// get dependency objects +SOKOL_SPINE_API_DECL sspine_atlas sspine_get_skeleton_atlas(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL sspine_skeleton sspine_get_instance_skeleton(sspine_instance instance); + +// atlas images +SOKOL_SPINE_API_DECL int sspine_num_images(sspine_atlas atlas); +SOKOL_SPINE_API_DECL sspine_image sspine_image_by_index(sspine_atlas atlas, int index); +SOKOL_SPINE_API_DECL bool sspine_image_valid(sspine_image image); +SOKOL_SPINE_API_DECL bool sspine_image_equal(sspine_image first, sspine_image second); +SOKOL_SPINE_API_DECL sspine_image_info sspine_get_image_info(sspine_image image); + +// atlas page functions +SOKOL_SPINE_API_DECL int sspine_num_atlas_pages(sspine_atlas atlas); +SOKOL_SPINE_API_DECL sspine_atlas_page sspine_atlas_page_by_index(sspine_atlas atlas, int index); +SOKOL_SPINE_API_DECL bool sspine_atlas_page_valid(sspine_atlas_page page); +SOKOL_SPINE_API_DECL bool sspine_atlas_page_equal(sspine_atlas_page first, sspine_atlas_page second); +SOKOL_SPINE_API_DECL sspine_atlas_page_info sspine_get_atlas_page_info(sspine_atlas_page page); + +// instance transform functions +SOKOL_SPINE_API_DECL void sspine_set_position(sspine_instance instance, sspine_vec2 position); +SOKOL_SPINE_API_DECL void sspine_set_scale(sspine_instance instance, sspine_vec2 scale); +SOKOL_SPINE_API_DECL void sspine_set_color(sspine_instance instance, sspine_color color); +SOKOL_SPINE_API_DECL sspine_vec2 sspine_get_position(sspine_instance instance); +SOKOL_SPINE_API_DECL sspine_vec2 sspine_get_scale(sspine_instance instance); +SOKOL_SPINE_API_DECL sspine_color sspine_get_color(sspine_instance instance); + +// instance animation functions +SOKOL_SPINE_API_DECL int sspine_num_anims(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL sspine_anim sspine_anim_by_name(sspine_skeleton skeleton, const char* name); +SOKOL_SPINE_API_DECL sspine_anim sspine_anim_by_index(sspine_skeleton skeleton, int index); +SOKOL_SPINE_API_DECL bool sspine_anim_valid(sspine_anim anim); +SOKOL_SPINE_API_DECL bool sspine_anim_equal(sspine_anim first, sspine_anim second); +SOKOL_SPINE_API_DECL sspine_anim_info sspine_get_anim_info(sspine_anim anim); +SOKOL_SPINE_API_DECL void sspine_clear_animation_tracks(sspine_instance instance); +SOKOL_SPINE_API_DECL void sspine_clear_animation_track(sspine_instance instance, int track_index); +SOKOL_SPINE_API_DECL void sspine_set_animation(sspine_instance instance, sspine_anim anim, int track_index, bool loop); +SOKOL_SPINE_API_DECL void sspine_add_animation(sspine_instance instance, sspine_anim anim, int track_index, bool loop, float delay); +SOKOL_SPINE_API_DECL void sspine_set_empty_animation(sspine_instance instance, int track_index, float mix_duration); +SOKOL_SPINE_API_DECL void sspine_add_empty_animation(sspine_instance instance, int track_index, float mix_duration, float delay); + +// bone functions +SOKOL_SPINE_API_DECL int sspine_num_bones(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL sspine_bone sspine_bone_by_name(sspine_skeleton skeleton, const char* name); +SOKOL_SPINE_API_DECL sspine_bone sspine_bone_by_index(sspine_skeleton skeleton, int index); +SOKOL_SPINE_API_DECL bool sspine_bone_valid(sspine_bone bone); +SOKOL_SPINE_API_DECL bool sspine_bone_equal(sspine_bone first, sspine_bone second); +SOKOL_SPINE_API_DECL sspine_bone_info sspine_get_bone_info(sspine_bone bone); +SOKOL_SPINE_API_DECL void sspine_set_bone_transform(sspine_instance instance, sspine_bone bone, const sspine_bone_transform* transform); +SOKOL_SPINE_API_DECL void sspine_set_bone_position(sspine_instance instance, sspine_bone bone, sspine_vec2 position); +SOKOL_SPINE_API_DECL void sspine_set_bone_rotation(sspine_instance instance, sspine_bone bone, float rotation); +SOKOL_SPINE_API_DECL void sspine_set_bone_scale(sspine_instance instance, sspine_bone bone, sspine_vec2 scale); +SOKOL_SPINE_API_DECL void sspine_set_bone_shear(sspine_instance instance, sspine_bone bone, sspine_vec2 shear); +SOKOL_SPINE_API_DECL sspine_bone_transform sspine_get_bone_transform(sspine_instance instance, sspine_bone bone); +SOKOL_SPINE_API_DECL sspine_vec2 sspine_get_bone_position(sspine_instance instance, sspine_bone bone); +SOKOL_SPINE_API_DECL float sspine_get_bone_rotation(sspine_instance instance, sspine_bone bone); +SOKOL_SPINE_API_DECL sspine_vec2 sspine_get_bone_scale(sspine_instance instance, sspine_bone bone); +SOKOL_SPINE_API_DECL sspine_vec2 sspine_get_bone_shear(sspine_instance instance, sspine_bone bone); +SOKOL_SPINE_API_DECL sspine_vec2 sspine_get_bone_world_position(sspine_instance instance, sspine_bone bone); +SOKOL_SPINE_API_DECL sspine_vec2 sspine_bone_local_to_world(sspine_instance instance, sspine_bone bone, sspine_vec2 local_pos); +SOKOL_SPINE_API_DECL sspine_vec2 sspine_bone_world_to_local(sspine_instance instance, sspine_bone bone, sspine_vec2 world_pos); + +// slot functions +SOKOL_SPINE_API_DECL int sspine_num_slots(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL sspine_slot sspine_slot_by_name(sspine_skeleton skeleton, const char* name); +SOKOL_SPINE_API_DECL sspine_slot sspine_slot_by_index(sspine_skeleton skeleton, int index); +SOKOL_SPINE_API_DECL bool sspine_slot_valid(sspine_slot slot); +SOKOL_SPINE_API_DECL bool sspine_slot_equal(sspine_slot first, sspine_slot second); +SOKOL_SPINE_API_DECL sspine_slot_info sspine_get_slot_info(sspine_slot slot); +SOKOL_SPINE_API_DECL void sspine_set_slot_color(sspine_instance instance, sspine_slot slot, sspine_color color); +SOKOL_SPINE_API_DECL sspine_color sspine_get_slot_color(sspine_instance instance, sspine_slot slot); + +// event functions +SOKOL_SPINE_API_DECL int sspine_num_events(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL sspine_event sspine_event_by_name(sspine_skeleton skeleton, const char* name); +SOKOL_SPINE_API_DECL sspine_event sspine_event_by_index(sspine_skeleton skeleton, int index); +SOKOL_SPINE_API_DECL bool sspine_event_valid(sspine_event event); +SOKOL_SPINE_API_DECL bool sspine_event_equal(sspine_event first, sspine_event second); +SOKOL_SPINE_API_DECL sspine_event_info sspine_get_event_info(sspine_event event); + +// ik target functions +SOKOL_SPINE_API_DECL int sspine_num_iktargets(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL sspine_iktarget sspine_iktarget_by_name(sspine_skeleton skeleton, const char* name); +SOKOL_SPINE_API_DECL sspine_iktarget sspine_iktarget_by_index(sspine_skeleton skeleton, int index); +SOKOL_SPINE_API_DECL bool sspine_iktarget_valid(sspine_iktarget iktarget); +SOKOL_SPINE_API_DECL bool sspine_iktarget_equal(sspine_iktarget first, sspine_iktarget second); +SOKOL_SPINE_API_DECL sspine_iktarget_info sspine_get_iktarget_info(sspine_iktarget iktarget); +SOKOL_SPINE_API_DECL void sspine_set_iktarget_world_pos(sspine_instance instance, sspine_iktarget iktarget, sspine_vec2 world_pos); + +// skin functions +SOKOL_SPINE_API_DECL int sspine_num_skins(sspine_skeleton skeleton); +SOKOL_SPINE_API_DECL sspine_skin sspine_skin_by_name(sspine_skeleton skeleton, const char* name); +SOKOL_SPINE_API_DECL sspine_skin sspine_skin_by_index(sspine_skeleton skeleton, int index); +SOKOL_SPINE_API_DECL bool sspine_skin_valid(sspine_skin skin); +SOKOL_SPINE_API_DECL bool sspine_skin_equal(sspine_skin first, sspine_skin second); +SOKOL_SPINE_API_DECL sspine_skin_info sspine_get_skin_info(sspine_skin skin); +SOKOL_SPINE_API_DECL void sspine_set_skin(sspine_instance instance, sspine_skin skin); + +#ifdef __cplusplus +} // extern "C" +#endif + +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation +#ifdef SOKOL_SPINE_IMPL +#define SOKOL_SPINE_IMPL_INCLUDED (1) + +#if !defined(SPINE_SPINE_H_) +#error "Please include spine/spine.h before the sokol_spine.h implementation" +#endif + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG (1) + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef SOKOL_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#endif +#ifndef SOKOL_UNUSED + #define SOKOL_UNUSED(x) (void)(x) +#endif + +#include // malloc/free +#include // memset, strcmp + +// ███████╗██╗ ██╗ █████╗ ██████╗ ███████╗██████╗ ███████╗ +// ██╔════╝██║ ██║██╔══██╗██╔══██╗██╔════╝██╔══██╗██╔════╝ +// ███████╗███████║███████║██║ ██║█████╗ ██████╔╝███████╗ +// ╚════██║██╔══██║██╔══██║██║ ██║██╔══╝ ██╔══██╗╚════██║ +// ███████║██║ ██║██║ ██║██████╔╝███████╗██║ ██║███████║ +// ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚══════╝╚═╝ ╚═╝╚══════╝ +// +// >>shaders +/* + Embedded source compiled with: + + sokol-shdc -i sspine.glsl -o sspine.h -l glsl410:glsl300es:hlsl4:metal_macos:metal_ios:metal_sim:wgsl -b + + @vs vs + uniform vs_params { + mat4 mvp; + }; + in vec2 position; + in vec2 texcoord0; + in vec4 color0; + out vec2 uv; + out vec4 color; + void main() { + gl_Position = mvp * vec4(position, 0.0, 1.0); + uv = texcoord0; + color = color0; + } + @end + + @fs fs + uniform texture2D tex; + uniform sampler smp; + uniform fs_params { + float pma; + }; + in vec2 uv; + in vec4 color; + out vec4 frag_color; + void main() { + vec4 c0 = texture(sampler2D(tex, smp), uv) * color; + vec4 c1 = vec4(c0.rgb * c0.a, c0.a) * color; + frag_color = mix(c0, c1, pma); + } + @end + + @program sspine vs fs +*/ +#if defined(SOKOL_GLCORE) +static const uint8_t _sspine_vs_source_glsl410[394] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e, + 0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a, + 0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20, + 0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,0x20,0x75,0x76, + 0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f, + 0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x74, + 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x6f, + 0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x6c, + 0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d, + 0x20,0x32,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29, + 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28,0x76,0x73,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x5b,0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b, + 0x32,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x33,0x5d, + 0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f, + 0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const uint8_t _sspine_fs_source_glsl410[350] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x3b,0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d, + 0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x5f,0x73, + 0x6d,0x70,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63, + 0x32,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63, + 0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65, + 0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f, + 0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29, + 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x76,0x65,0x63,0x34,0x20,0x5f,0x32,0x38,0x20, + 0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x74,0x65,0x78,0x5f,0x73,0x6d, + 0x70,0x2c,0x20,0x75,0x76,0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f,0x33,0x37,0x20,0x3d,0x20, + 0x5f,0x32,0x38,0x2e,0x77,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x6d,0x69,0x78,0x28,0x5f,0x32,0x38,0x2c, + 0x20,0x76,0x65,0x63,0x34,0x28,0x5f,0x32,0x38,0x2e,0x78,0x79,0x7a,0x20,0x2a,0x20, + 0x5f,0x33,0x37,0x2c,0x20,0x5f,0x33,0x37,0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x2c,0x20,0x76,0x65,0x63,0x34,0x28,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x5b,0x30,0x5d,0x2e,0x78,0x29,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_GLES3) +static const uint8_t _sspine_vs_source_glsl300es[355] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f, + 0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29, + 0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,0x20,0x75,0x76,0x3b,0x0a, + 0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20, + 0x3d,0x20,0x31,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x74,0x65,0x78, + 0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c, + 0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69,0x6e,0x20, + 0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f, + 0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20, + 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x6d,0x61, + 0x74,0x34,0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2c, + 0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76,0x73,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63, + 0x34,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x2c,0x20,0x30,0x2e,0x30,0x2c, + 0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d, + 0x0a,0x0a,0x00, +}; +static const uint8_t _sspine_fs_source_glsl300es[399] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, + 0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d, + 0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69, + 0x6f,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63, + 0x34,0x20,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x3b,0x0a, + 0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x3b, + 0x0a,0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20, + 0x75,0x76,0x3b,0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75, + 0x74,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61, + 0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d, + 0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x68,0x69,0x67,0x68, + 0x70,0x20,0x76,0x65,0x63,0x34,0x20,0x5f,0x32,0x38,0x20,0x3d,0x20,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x28,0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,0x2c,0x20,0x75,0x76, + 0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x68, + 0x69,0x67,0x68,0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f,0x33,0x37,0x20,0x3d, + 0x20,0x5f,0x32,0x38,0x2e,0x77,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x6d,0x69,0x78,0x28,0x5f,0x32,0x38, + 0x2c,0x20,0x76,0x65,0x63,0x34,0x28,0x5f,0x32,0x38,0x2e,0x78,0x79,0x7a,0x20,0x2a, + 0x20,0x5f,0x33,0x37,0x2c,0x20,0x5f,0x33,0x37,0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c, + 0x6f,0x72,0x2c,0x20,0x76,0x65,0x63,0x34,0x28,0x66,0x73,0x5f,0x70,0x61,0x72,0x61, + 0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78,0x29,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_D3D11) +static const uint8_t _sspine_vs_bytecode_hlsl4[844] = { + 0x44,0x58,0x42,0x43,0x2a,0xc9,0x57,0x52,0x4b,0x3d,0x3e,0x89,0x00,0xf4,0xfa,0x41, + 0xfd,0xd7,0x63,0xc3,0x01,0x00,0x00,0x00,0x4c,0x03,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xf4,0x00,0x00,0x00,0x58,0x01,0x00,0x00,0xc8,0x01,0x00,0x00, + 0xd0,0x02,0x00,0x00,0x52,0x44,0x45,0x46,0xb8,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x48,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xfe,0xff, + 0x10,0x81,0x00,0x00,0x90,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0xab,0xab,0x3c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x80,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x31,0x39,0x5f,0x6d,0x76,0x70,0x00,0x02,0x00,0x03,0x00, + 0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4d,0x69,0x63,0x72, + 0x6f,0x73,0x6f,0x66,0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c,0x53,0x4c,0x20,0x53, + 0x68,0x61,0x64,0x65,0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x72,0x20,0x31, + 0x30,0x2e,0x31,0x00,0x49,0x53,0x47,0x4e,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x50,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x03,0x03,0x00,0x00,0x50,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43, + 0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x68,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0c,0x00,0x00, + 0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x00,0xab,0xab,0xab,0x53,0x48,0x44,0x52,0x00,0x01,0x00,0x00, + 0x40,0x00,0x01,0x00,0x40,0x00,0x00,0x00,0x59,0x00,0x00,0x04,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0x32,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0x32,0x10,0x10,0x00,0x01,0x00,0x00,0x00, + 0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00,0x02,0x00,0x00,0x00,0x65,0x00,0x00,0x03, + 0x32,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00, + 0x01,0x00,0x00,0x00,0x67,0x00,0x00,0x04,0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00,0x36,0x00,0x00,0x05, + 0x32,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00,0x01,0x00,0x00,0x00, + 0x36,0x00,0x00,0x05,0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00,0x46,0x1e,0x10,0x00, + 0x02,0x00,0x00,0x00,0x38,0x00,0x00,0x08,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x56,0x15,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x06,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08, + 0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3e,0x00,0x00,0x01, + 0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sspine_fs_bytecode_hlsl4[868] = { + 0x44,0x58,0x42,0x43,0xfb,0x9d,0xdb,0x19,0x85,0xbc,0x50,0x5d,0x6b,0x03,0xd4,0xc8, + 0x1b,0xea,0x59,0xf5,0x01,0x00,0x00,0x00,0x64,0x03,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x3c,0x01,0x00,0x00,0x88,0x01,0x00,0x00,0xbc,0x01,0x00,0x00, + 0xe8,0x02,0x00,0x00,0x52,0x44,0x45,0x46,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0x90,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xff,0xff, + 0x10,0x81,0x00,0x00,0xd8,0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x73,0x6d,0x70,0x00,0x74,0x65,0x78,0x00, + 0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x00,0xab,0xab,0x84,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0xa8,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0x35,0x33,0x5f, + 0x70,0x6d,0x61,0x00,0x00,0x00,0x03,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f,0x66,0x74,0x20,0x28,0x52, + 0x29,0x20,0x48,0x4c,0x53,0x4c,0x20,0x53,0x68,0x61,0x64,0x65,0x72,0x20,0x43,0x6f, + 0x6d,0x70,0x69,0x6c,0x65,0x72,0x20,0x31,0x30,0x2e,0x31,0x00,0x49,0x53,0x47,0x4e, + 0x44,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x38,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x03,0x00,0x00,0x38,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43, + 0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x2c,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x53,0x56,0x5f,0x54,0x61,0x72,0x67,0x65,0x74,0x00,0xab,0xab,0x53,0x48,0x44,0x52, + 0x24,0x01,0x00,0x00,0x40,0x00,0x00,0x00,0x49,0x00,0x00,0x00,0x59,0x00,0x00,0x04, + 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x5a,0x00,0x00,0x03, + 0x00,0x60,0x10,0x00,0x00,0x00,0x00,0x00,0x58,0x18,0x00,0x04,0x00,0x70,0x10,0x00, + 0x00,0x00,0x00,0x00,0x55,0x55,0x00,0x00,0x62,0x10,0x00,0x03,0x32,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x62,0x10,0x00,0x03,0xf2,0x10,0x10,0x00,0x01,0x00,0x00,0x00, + 0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x02, + 0x02,0x00,0x00,0x00,0x45,0x00,0x00,0x09,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x7e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x00,0x60,0x10,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x07,0xf2,0x00,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x1e,0x10,0x00, + 0x01,0x00,0x00,0x00,0x38,0x00,0x00,0x07,0x72,0x00,0x10,0x00,0x01,0x00,0x00,0x00, + 0xf6,0x0f,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x02,0x10,0x00,0x00,0x00,0x00,0x00, + 0x36,0x00,0x00,0x05,0x82,0x00,0x10,0x00,0x01,0x00,0x00,0x00,0x3a,0x00,0x10,0x00, + 0x00,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00,0x01,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x01,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x01,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x80,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x32,0x00,0x00,0x0a, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x06,0x80,0x20,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x01,0x00,0x00,0x00,0x46,0x0e,0x10,0x00, + 0x00,0x00,0x00,0x00,0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, +}; +#elif defined(SOKOL_METAL) +static const uint8_t _sspine_vs_bytecode_metal_macos[3084] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x1d,0x26,0xf7,0xce,0x5d,0x78,0x21, + 0x2f,0xa7,0x97,0xe1,0xbd,0x15,0x30,0xfb,0xa6,0x98,0xcc,0x1d,0xba,0x1d,0x0d,0x92, + 0xaa,0x4e,0x3d,0x75,0xee,0x73,0x36,0x7c,0x99,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x37,0x00,0x00,0x00,0x56,0x41,0x54, + 0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x80, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03,0x00,0x04,0x04,0x06, + 0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b, + 0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xe0,0x0a,0x00,0x00,0xff,0xff,0xff,0xff, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xb5,0x02,0x00,0x00,0x0b,0x82,0x20,0x00, + 0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49, + 0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62, + 0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49, + 0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72, + 0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00, + 0x51,0x18,0x00,0x00,0x81,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8,0xff,0xff,0xff,0xff, + 0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87,0x71,0xa0,0x07,0x76, + 0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72,0x20,0x87,0x36,0x20, + 0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88,0x07,0x79,0xa0,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0xc0,0x1c,0xc2,0x81, + 0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c, + 0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c,0xd8,0x81,0x1d,0xe6, + 0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87,0x72,0x98,0x87,0x79, + 0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72,0x68,0x03,0x73,0x80, + 0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xdc, + 0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda,0x00,0x1e,0xde,0x21, + 0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21,0x1d,0xda,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30,0x07,0x79,0x08,0x87, + 0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87,0x72,0x90,0x87,0x36, + 0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77,0x68,0x03,0x76,0x28, + 0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70,0x07,0x73,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1,0x1d,0xe0,0xa1,0x0d, + 0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87, + 0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87,0x79,0x68,0x03,0x73, + 0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d,0xca,0x81,0x1c,0xda, + 0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21, + 0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x70,0x87,0x77,0x68, + 0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38,0x87,0x36,0x68,0x87, + 0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0x62,0x1e,0xe8,0x21, + 0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1,0x1c,0xc6,0x81,0x1e, + 0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x0d,0xe6, + 0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87,0x79,0x08,0x07,0x73, + 0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e, + 0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d,0xe6,0xe1,0x1d,0xcc, + 0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00,0x73,0x08,0x07,0x76, + 0x98,0x87,0x72,0x00,0x36,0x18,0x02,0x01,0x2c,0x40,0x05,0x00,0x49,0x18,0x00,0x00, + 0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00,0x1f,0x00,0x00,0x00, + 0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3, + 0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x44,0x33,0x00, + 0xc3,0x08,0x02,0x30,0x8c,0x40,0x00,0x76,0x08,0x42,0x24,0x81,0x98,0x89,0x9a,0x07, + 0x7a,0x90,0x87,0x7a,0x18,0x07,0x7a,0x70,0x83,0x76,0x28,0x07,0x7a,0x08,0x07,0x76, + 0xd0,0x03,0x3d,0x68,0x87,0x70,0xa0,0x07,0x79,0x48,0x07,0x7c,0x40,0x01,0x39,0x48, + 0x9a,0x22,0x4a,0x98,0xfc,0x4a,0xfa,0x1f,0x20,0x02,0x18,0x09,0x05,0x65,0x10,0xc1, + 0x10,0x4a,0x31,0x42,0x10,0x87,0xd0,0x40,0xc0,0x1c,0x01,0x18,0xa4,0xc0,0x9a,0x23, + 0x00,0x85,0x41,0x04,0x41,0x18,0x46,0x20,0x96,0x11,0x00,0x00,0x13,0xb2,0x70,0x48, + 0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83, + 0x76,0x08,0x87,0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83,0x38, + 0x70,0x03,0x38,0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07,0x7a, + 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78,0xa0, + 0x07,0x78,0xd0,0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e, + 0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e,0x73, + 0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40, + 0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20,0x07, + 0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07,0x7a, + 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a,0x10, + 0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07,0x72, + 0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07, + 0x76,0x40,0x07,0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a, + 0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20, + 0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20,0x07, + 0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x72, + 0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0, + 0x06,0xf6,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07, + 0x71,0x20,0x07,0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70, + 0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0, + 0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x49,0x00,0x00,0x08,0x00,0x00, + 0x00,0x00,0x00,0xc8,0x02,0x01,0x00,0x00,0x0a,0x00,0x00,0x00,0x32,0x1e,0x98,0x10, + 0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50, + 0x04,0x05,0x18,0x50,0x08,0x05,0x51,0x06,0x05,0x42,0x6d,0x04,0x80,0xd8,0x58,0x42, + 0x13,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xeb,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10, + 0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x32,0x28, + 0x00,0xa3,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c, + 0x81,0x22,0x2c,0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c, + 0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0xc5, + 0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06, + 0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25,0x58, + 0x8c,0x45,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x82,0x45, + 0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6, + 0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50,0x12, + 0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73, + 0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04,0x65, + 0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9, + 0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d, + 0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x51,0x58,0x9a,0x9c,0x8b, + 0x5d,0x99,0x1c,0x5d,0x19,0xde,0xd7,0x5b,0x1d,0x1d,0x5c,0x1d,0x1d,0x97,0xba,0xb9, + 0x32,0x39,0x14,0xb6,0xb7,0x31,0x37,0x98,0x14,0x46,0x61,0x69,0x72,0x2e,0x61,0x72, + 0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x6e,0x61,0x6d,0x65,0x34,0xcc,0xd8,0xde,0xc2, + 0xe8,0x64,0xc8,0x84,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95,0x51, + 0xa8,0xb3,0x1b,0xc2,0x28,0x8f,0x02,0x29,0x91,0x22,0x29,0x93,0x42,0x71,0xa9,0x9b, + 0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0xa1,0x61,0xc6,0xf6,0x16,0x46,0x47, + 0xc3,0x62,0xec,0x8d,0xed,0x4d,0x6e,0x08,0xa3,0x3c,0x8a,0xa5,0x44,0xca,0xa5,0x4c, + 0x0a,0x46,0x26,0x2c,0x4d,0xce,0x05,0xee,0x6d,0x2e,0x8d,0x2e,0xed,0xcd,0x8d,0xcb, + 0x19,0xdb,0x17,0xd4,0xdb,0x5c,0x1a,0x5d,0xda,0x9b,0xdb,0x10,0x45,0xd1,0x94,0x48, + 0xb9,0x94,0x49,0xd9,0x86,0x18,0x4a,0xa5,0x64,0x0a,0x47,0x28,0x2c,0x4d,0xce,0xc5, + 0xae,0x4c,0x8e,0xae,0x0c,0xef,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x52,0x58,0x9a,0x9c, + 0x0b,0xdb,0xdb,0x58,0x18,0x5d,0xda,0x9b,0xdb,0x57,0x9a,0x1b,0x59,0x19,0x1e,0xbd, + 0xb3,0x32,0xb7,0x32,0xb9,0x30,0xba,0x32,0x32,0x94,0xaf,0xaf,0xb0,0x34,0xb9,0x2f, + 0x38,0xb6,0xb0,0xb1,0x32,0xb4,0x37,0x36,0xb2,0x32,0xb9,0xaf,0xaf,0x14,0x22,0x70, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x43,0xa8,0x45,0x50,0x3c,0xe5,0x5b,0x84,0x25, + 0x50,0xc0,0x40,0x89,0x14,0x49,0x99,0x94,0x30,0x60,0x42,0x57,0x86,0x37,0xf6,0xf6, + 0x26,0x47,0x06,0x33,0x84,0x5a,0x02,0xc5,0x53,0xbe,0x25,0x58,0x02,0x05,0x0c,0x94, + 0x48,0x91,0x94,0x49,0x19,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8,0x65, + 0x50,0x3c,0xe5,0x5b,0x86,0x25,0x50,0xc0,0x40,0x89,0x94,0x4b,0x99,0x94,0x32,0xa0, + 0x12,0x96,0x26,0xe7,0x22,0x56,0x67,0x66,0x56,0x26,0xc7,0x27,0x2c,0x4d,0xce,0x45, + 0xac,0xce,0xcc,0xac,0x4c,0xee,0x6b,0x2e,0x4d,0xaf,0x8c,0x48,0x58,0x9a,0x9c,0x8b, + 0x5c,0x59,0x18,0x19,0xa9,0xb0,0x34,0x39,0x97,0x39,0x3a,0xb9,0xba,0x31,0xba,0x2f, + 0xba,0x3c,0xb8,0xb2,0xaf,0x34,0x37,0xb3,0x37,0x22,0x66,0x6c,0x6f,0x61,0x74,0x34, + 0x78,0x34,0x1c,0xda,0xec,0xe0,0x86,0x28,0x8b,0xb0,0x10,0x8b,0xa0,0xac,0x81,0xc2, + 0x06,0x8c,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xe8,0xf2,0xe0,0xca,0xbe,0xe6, + 0xd2,0xf4,0xca,0x78,0x85,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xd1,0xe5,0xc1,0x95, + 0x7d,0x85,0xb1,0xa5,0x9d,0xb9,0x7d,0xcd,0xa5,0xe9,0x95,0x31,0xb1,0x9b,0xfb,0x82, + 0x0b,0x93,0x0b,0x6b,0x9b,0xe3,0xf0,0x15,0x93,0x33,0x84,0x0c,0x96,0x43,0x39,0x03, + 0x05,0x0d,0x16,0x42,0xf9,0x16,0x61,0x09,0x94,0x34,0x50,0xd4,0x40,0x69,0x03,0xc5, + 0x0d,0x16,0x42,0x79,0x83,0x05,0x51,0x22,0x05,0x0e,0x94,0x49,0x89,0x83,0x21,0x88, + 0x22,0x06,0x0a,0x19,0x28,0x66,0xa0,0xc8,0xc1,0x10,0x23,0x01,0x94,0x4e,0x99,0x03, + 0x3e,0x6f,0x6d,0x6e,0x69,0x70,0x6f,0x74,0x65,0x6e,0x74,0x20,0x63,0x68,0x61,0x72, + 0x7c,0xa6,0xd2,0xda,0xe0,0xd8,0xca,0x40,0x86,0x56,0x56,0x40,0xa8,0x84,0x82,0x82, + 0x86,0x08,0x8a,0x1d,0x0c,0x31,0x94,0x3a,0x50,0xee,0xa0,0x49,0x86,0x18,0x0a,0x1e, + 0x28,0x78,0xd0,0x24,0x23,0x22,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x77, + 0x20,0x87,0x7a,0x60,0x87,0x72,0x70,0x03,0x73,0x60,0x87,0x70,0x38,0x87,0x79,0x98, + 0x22,0x04,0xc3,0x08,0x85,0x1d,0xd8,0xc1,0x1e,0xda,0xc1,0x0d,0xd2,0x81,0x1c,0xca, + 0xc1,0x1d,0xe8,0x61,0x4a,0x50,0x8c,0x58,0xc2,0x21,0x1d,0xe4,0xc1,0x0d,0xec,0xa1, + 0x1c,0xe4,0x61,0x1e,0xd2,0xe1,0x1d,0xdc,0x61,0x4a,0x60,0x8c,0xa0,0xc2,0x21,0x1d, + 0xe4,0xc1,0x0d,0xd8,0x21,0x1c,0xdc,0xe1,0x1c,0xea,0x21,0x1c,0xce,0xa1,0x1c,0x7e, + 0xc1,0x1e,0xca,0x41,0x1e,0xe6,0x21,0x1d,0xde,0xc1,0x1d,0xa6,0x04,0xc8,0x88,0x29, + 0x1c,0xd2,0x41,0x1e,0xdc,0x60,0x1c,0xde,0xa1,0x1d,0xe0,0x21,0x1d,0xd8,0xa1,0x1c, + 0x7e,0xe1,0x1d,0xe0,0x81,0x1e,0xd2,0xe1,0x1d,0xdc,0x61,0x1e,0xa6,0x0c,0x0a,0xe3, + 0x8c,0x50,0xc2,0x21,0x1d,0xe4,0xc1,0x0d,0xec,0xa1,0x1c,0xe4,0x81,0x1e,0xca,0x01, + 0x1f,0xa6,0x04,0x74,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00, + 0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3, + 0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10, + 0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30, + 0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03, + 0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07, + 0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e, + 0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d, + 0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b, + 0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76, + 0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90, + 0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87, + 0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e, + 0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c, + 0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca, + 0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8, + 0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82, + 0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83, + 0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f, + 0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec, + 0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc, + 0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0, + 0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60, + 0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e, + 0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1, + 0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43, + 0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c, + 0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72, + 0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1, + 0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38, + 0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8, + 0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00, + 0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00, + 0x1e,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x05,0x00,0x00,0x00, + 0xf4,0xc6,0x22,0x82,0x20,0x08,0x46,0x00,0xa8,0x95,0x40,0x19,0xd0,0x98,0x01,0xa0, + 0x30,0x03,0x00,0x00,0xe3,0x15,0x07,0x33,0x4d,0x0c,0x05,0x65,0x90,0x81,0x19,0x0e, + 0x13,0x02,0xf9,0x8c,0x57,0x2c,0xd0,0x75,0x21,0x14,0x94,0x41,0x06,0xe8,0x60,0x4c, + 0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1,0x0a,0xa8,0xe2,0x38,0x86,0x82,0x62,0x43,0x00, + 0x9f,0xd9,0x06,0xa7,0x02,0x66,0x1b,0x82,0x2a,0x98,0x6d,0x08,0x06,0x21,0x83,0x80, + 0x18,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x5b,0x86,0x20,0xc8,0x83,0x2d,0x43,0x11, + 0xe4,0xc1,0x96,0x41,0x09,0xf2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sspine_fs_bytecode_metal_macos[3257] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xb9,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe0,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0x47,0xc7,0x1e,0x49,0x53,0x1c,0xe5, + 0x90,0x01,0xa5,0x57,0x9a,0x30,0x01,0x5c,0x39,0x85,0x9f,0xb2,0x71,0x51,0xe1,0x73, + 0x0c,0xa4,0x4d,0xb1,0x81,0x33,0x8d,0x17,0x1e,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44, + 0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0xc4,0x0b,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0xee,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32, + 0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45, + 0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44, + 0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8, + 0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00, + 0x00,0x8e,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07,0x7a,0x60,0x87,0x7c, + 0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72,0x68,0x03,0x72,0x48, + 0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90,0x07,0x7a,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc,0x21,0x1c,0xd8,0x61, + 0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81,0x1d,0xca,0xa1,0x0d, + 0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d,0xd8,0x61,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87,0x79,0x98,0x87,0x36, + 0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36,0x30,0x07,0x78,0x68, + 0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0xc2,0x1d,0xde, + 0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0,0xe1,0x1d,0xd2,0xc1, + 0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1,0x1d,0x00,0x7a,0x90, + 0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90,0x87,0x70,0x68,0x87, + 0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07,0x79,0x68,0x83,0x72, + 0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36,0x60,0x87,0x72,0x08, + 0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30,0x87,0x79,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1, + 0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01,0x1e,0xda,0x80,0x1e, + 0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07, + 0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87,0x36,0x30,0x07,0x78, + 0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20, + 0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c,0xc8,0xa1,0x0d,0xf4, + 0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda,0xa0,0x1d,0xc2,0x81, + 0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0xa0, + 0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68,0x83,0x76,0x08,0x07, + 0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6,0x81,0x1e,0xc2,0x61, + 0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d, + 0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e,0xda,0x60,0x1e,0xd2, + 0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87,0x70,0x30,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e, + 0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e,0xde,0xc1,0x1c,0xe8, + 0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87,0x70,0x60,0x87,0x79, + 0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81,0x00,0x16,0xa0,0xda, + 0x60,0x10,0x05,0xb0,0x00,0xd5,0x06,0xa3,0xf8,0xff,0xff,0xff,0xff,0x01,0x90,0x00, + 0x6a,0x03,0x62,0xfc,0xff,0xff,0xff,0xff,0x00,0x30,0x80,0x04,0x54,0x1b,0x8c,0x23, + 0x00,0x16,0xa0,0xda,0x60,0x20,0x02,0xb0,0x00,0x15,0x00,0x00,0x00,0x49,0x18,0x00, + 0x00,0x03,0x00,0x00,0x00,0x13,0x88,0x40,0x18,0x88,0x09,0x41,0x31,0x61,0x30,0x0e, + 0x04,0x89,0x20,0x00,0x00,0x27,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85, + 0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a, + 0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x6c,0x33,0x00,0xc3,0x08,0x04,0x60,0x83,0x30,0x8c, + 0x20,0x00,0x07,0x49,0x53,0x44,0x09,0x93,0x5f,0x48,0xff,0x03,0x44,0x00,0x23,0xa1, + 0x00,0x0c,0x22,0x10,0xc2,0x51,0xd2,0x14,0x51,0xc2,0xe4,0xff,0x13,0x71,0x4d,0x54, + 0x44,0xfc,0xf6,0xf0,0x4f,0x63,0x04,0xc0,0x20,0x82,0x11,0x5c,0x24,0x4d,0x11,0x25, + 0x4c,0xfe,0x2f,0x01,0xcc,0xb3,0x10,0xd1,0x3f,0x8d,0x11,0x00,0x83,0x08,0x88,0x50, + 0x0c,0x31,0x42,0x39,0x89,0x54,0x21,0x42,0x08,0x81,0xd8,0x1c,0x41,0x30,0x47,0x00, + 0x06,0xc3,0x08,0xc2,0x53,0x90,0x70,0xd2,0x70,0xd0,0x01,0x8a,0x03,0x01,0x29,0xf0, + 0x86,0x11,0x86,0x67,0x18,0x61,0x00,0x86,0x11,0x88,0x67,0x8e,0x00,0x14,0x06,0x11, + 0x00,0x61,0x04,0x00,0x00,0x13,0xb2,0x70,0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83, + 0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x76,0x08,0x87,0x71,0x78,0x87,0x79, + 0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83,0x38,0x70,0x03,0x38,0xd8,0x70,0x1b,0xe5, + 0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40, + 0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x80,0x07, + 0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a,0x10,0x07,0x76, + 0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0, + 0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07, + 0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73, + 0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40, + 0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07, + 0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d, + 0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60, + 0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f, + 0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x79, + 0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60, + 0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07, + 0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a, + 0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50, + 0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x50,0x07,0x71,0x20,0x07, + 0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x6d,0x60,0x0f,0x71, + 0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00, + 0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07, + 0x72,0x30,0x84,0x59,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x18,0xc2,0x34,0x40, + 0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x0c,0x61,0x24,0x20,0x00,0x06,0x00,0x00,0x00, + 0x00,0x00,0xb2,0x40,0x00,0x09,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c, + 0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x7a,0x23,0x00,0x25,0x50,0x08,0x45,0x50, + 0x10,0x65,0x40,0x78,0x04,0x80,0xe8,0x58,0x42,0x13,0x00,0x00,0x00,0x79,0x18,0x00, + 0x00,0xf9,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32, + 0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x63,0x4c,0x00,0xa5,0x50,0xb9,0x1b,0x43,0x0b, + 0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x3c,0xc4,0x24,0x3c,0x05,0xe7,0x20,0x08, + 0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed, + 0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c, + 0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45, + 0x26,0x65,0x88,0x30,0x11,0x43,0x8c,0x87,0x78,0x8e,0x67,0x60,0xd1,0x54,0x46,0x17, + 0xc6,0x36,0x04,0x99,0x8e,0x87,0x78,0x88,0x67,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6, + 0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96, + 0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x98,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f, + 0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f, + 0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x84,0x69,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c, + 0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99, + 0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95, + 0x0d,0x11,0xa6,0x86,0x51,0x58,0x9a,0x9c,0x8b,0x5c,0x99,0x1b,0x59,0x99,0xdc,0x17, + 0x5d,0x98,0xdc,0x59,0x19,0x1d,0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba, + 0x3c,0xb8,0xb2,0x2f,0xb7,0xb0,0xb6,0x32,0x1a,0x66,0x6c,0x6f,0x61,0x74,0x34,0x64, + 0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xdc,0xc2,0xda,0xca,0xa8,0x98,0xc9,0x85, + 0x9d,0x7d,0x8d,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0xa6,0xe7,0x19,0x26,0x68,0x8a,0x26, + 0x69,0x9a,0x86,0x08,0x13,0x45,0x29,0x2c,0x4d,0xce,0xc5,0x4c,0x2e,0xec,0xac,0xad, + 0xcc,0x8d,0xee,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb, + 0xdb,0x98,0x1b,0x4c,0x0a,0x95,0xb0,0x34,0x39,0x97,0xb1,0x32,0x37,0xba,0x32,0x39, + 0x3e,0x61,0x69,0x72,0x2e,0x70,0x65,0x72,0x73,0x70,0x65,0x63,0x74,0x69,0x76,0x65, + 0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x28,0xd4,0xd9,0x0d,0x91,0x9e,0x61,0xb2,0xa6, + 0x6b,0xc2,0xa6,0x6c,0x82,0x26,0x6d,0x92,0xa6,0x8d,0x4b,0xdd,0x5c,0x99,0x1c,0x0a, + 0xdb,0xdb,0x98,0x5b,0x4c,0x0a,0x8b,0xb1,0x37,0xb6,0x37,0xb9,0x21,0xd2,0x43,0x4c, + 0xd6,0xd4,0x4d,0xd8,0x94,0x4d,0xd0,0x14,0x4d,0xd2,0xe4,0x51,0x09,0x4b,0x93,0x73, + 0x11,0xab,0x33,0x33,0x2b,0x93,0xe3,0x13,0x96,0x26,0xe7,0x22,0x56,0x67,0x66,0x56, + 0x26,0xf7,0x35,0x97,0xa6,0x57,0x46,0x29,0x2c,0x4d,0xce,0x85,0xed,0x6d,0x2c,0x8c, + 0x2e,0xed,0xcd,0xed,0x2b,0xcd,0x8d,0xac,0x0c,0x8f,0x48,0x58,0x9a,0x9c,0x8b,0x5c, + 0x59,0x18,0x19,0xa9,0xb0,0x34,0x39,0x97,0x39,0x3a,0xb9,0xba,0x31,0xba,0x2f,0xba, + 0x3c,0xb8,0xb2,0xaf,0x34,0x37,0xb3,0x37,0x16,0x66,0x6c,0x6f,0x61,0x74,0x1c,0xe0, + 0xda,0xc2,0x86,0x28,0xcf,0xf0,0x14,0xcf,0x30,0x95,0xc1,0x64,0x06,0x8c,0xc2,0xd2, + 0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xe8,0xf2,0xe0,0xca,0xbe,0xe6,0xd2,0xf4,0xca,0x78, + 0x85,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xd1,0xe5,0xc1,0x95,0x7d,0x85,0xb1,0xa5, + 0x9d,0xb9,0x7d,0xcd,0xa5,0xe9,0x95,0x31,0x31,0x9b,0xfb,0x82,0x0b,0x93,0x0b,0x6b, + 0x9b,0xe3,0xf0,0x55,0x33,0x33,0x84,0x0c,0x1e,0x63,0x02,0x83,0x29,0x0c,0x9e,0x62, + 0x12,0x83,0x67,0x78,0x88,0x69,0x0c,0x26,0x32,0x98,0xce,0x60,0x42,0x83,0xa7,0x98, + 0xd2,0xe0,0x29,0x26,0x68,0x52,0x83,0x49,0x9a,0xd6,0x80,0x4b,0x58,0x9a,0x9c,0x0b, + 0x5d,0x19,0x1e,0x5d,0x9d,0x5c,0x19,0x95,0xb0,0x34,0x39,0x97,0xb9,0xb0,0x36,0x38, + 0xb6,0x32,0x62,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x32,0x64,0x3c,0x66,0x6c,0x6f, + 0x61,0x74,0x2c,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x3e,0x1c,0xe8,0xca,0xf0,0x86, + 0x50,0x0f,0x32,0xb5,0xc1,0x24,0x06,0xcf,0xf0,0x10,0x93,0x1b,0x4c,0xd0,0xf4,0x06, + 0x93,0x34,0xc1,0x01,0x97,0xb0,0x34,0x39,0x97,0xb9,0xb0,0x36,0x38,0xb6,0x32,0x39, + 0x1e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x1c,0xe6,0xda,0xe0,0x86,0x48,0x4f,0x31, + 0xc9,0xc1,0x24,0x06,0xcf,0xf0,0x10,0x13,0x34,0xcd,0xc1,0x24,0x4d,0x74,0x30,0x44, + 0x99,0xb8,0xe9,0x9b,0xd8,0x60,0x8a,0x83,0xa9,0x0e,0x86,0x18,0x0b,0x30,0x55,0x93, + 0x1d,0xd0,0xf9,0xd2,0xa2,0x9a,0xca,0x31,0x9b,0xfb,0x82,0x0b,0x93,0x0b,0x6b,0x9b, + 0xe3,0xf3,0xd6,0xe6,0x96,0x06,0xf7,0x46,0x57,0xe6,0x46,0x07,0x32,0x86,0x16,0x26, + 0xc7,0x67,0x2a,0xad,0x0d,0x8e,0xad,0x0c,0x64,0x68,0x65,0x05,0x84,0x4a,0x28,0x28, + 0x68,0x88,0x30,0xe9,0xc1,0x10,0x63,0xca,0x83,0x69,0x0f,0xb0,0x64,0x88,0x31,0x95, + 0xc1,0xc4,0x07,0x58,0x32,0xc4,0x98,0xf0,0x60,0xea,0x03,0x2c,0x19,0x62,0x4c,0x7e, + 0x30,0xf5,0x01,0x96,0x8c,0x88,0xd8,0x81,0x1d,0xec,0xa1,0x1d,0xdc,0xa0,0x1d,0xde, + 0x81,0x1c,0xea,0x81,0x1d,0xca,0xc1,0x0d,0xcc,0x81,0x1d,0xc2,0xe1,0x1c,0xe6,0x61, + 0x8a,0x10,0x0c,0x23,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x48,0x07,0x72,0x28, + 0x07,0x77,0xa0,0x87,0x29,0x41,0x31,0x62,0x09,0x87,0x74,0x90,0x07,0x37,0xb0,0x87, + 0x72,0x90,0x87,0x79,0x48,0x87,0x77,0x70,0x87,0x29,0x81,0x31,0x82,0x0a,0x87,0x74, + 0x90,0x07,0x37,0x60,0x87,0x70,0x70,0x87,0x73,0xa8,0x87,0x70,0x38,0x87,0x72,0xf8, + 0x05,0x7b,0x28,0x07,0x79,0x98,0x87,0x74,0x78,0x07,0x77,0x98,0x12,0x20,0x23,0xa6, + 0x70,0x48,0x07,0x79,0x70,0x83,0x71,0x78,0x87,0x76,0x80,0x87,0x74,0x60,0x87,0x72, + 0xf8,0x85,0x77,0x80,0x07,0x7a,0x48,0x87,0x77,0x70,0x87,0x79,0x98,0x32,0x28,0x8c, + 0x33,0x82,0x09,0x87,0x74,0x90,0x07,0x37,0x30,0x07,0x79,0x08,0x87,0x73,0x68,0x87, + 0x72,0x70,0x07,0x7a,0x98,0x12,0xdc,0x01,0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00, + 0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84, + 0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed, + 0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66, + 0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c, + 0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70, + 0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90, + 0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4, + 0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83, + 0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14, + 0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70, + 0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80, + 0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0, + 0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1, + 0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d, + 0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39, + 0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc, + 0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70, + 0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53, + 0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e, + 0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c, + 0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38, + 0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37, + 0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33, + 0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4, + 0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0, + 0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3, + 0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07, + 0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23, + 0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59, + 0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b, + 0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00, + 0x00,0x0b,0x00,0x00,0x00,0x26,0xb0,0x01,0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f, + 0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e,0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20, + 0x0d,0x6d,0x01,0x0d,0x80,0x44,0x3e,0x83,0x5c,0x7e,0x85,0x17,0xb7,0x0d,0x00,0x00, + 0x00,0x61,0x20,0x00,0x00,0x25,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00, + 0x00,0x0c,0x00,0x00,0x00,0x74,0x47,0x00,0xc6,0x22,0x80,0x40,0x38,0xe6,0x20,0x06, + 0xc2,0xa8,0xc8,0xd5,0xc0,0x08,0x00,0xbd,0x19,0x00,0x82,0x23,0x00,0x54,0xc7,0x1a, + 0x80,0x40,0x18,0x6b,0x18,0x86,0x81,0xec,0x0c,0x00,0x89,0x19,0x00,0x0a,0x33,0x00, + 0x04,0x46,0x00,0x00,0x00,0x23,0x06,0xca,0x10,0x6c,0x8f,0x23,0x29,0x47,0x12,0x58, + 0x20,0xc9,0x67,0x90,0x21,0x20,0x90,0x41,0x06,0xa1,0x40,0x4c,0x08,0xe4,0x33,0xc8, + 0x10,0x24,0xd0,0x20,0x43,0x50,0x48,0x16,0x60,0xf2,0x19,0x6f,0xc0,0x38,0x31,0xa0, + 0x60,0xcc,0x31,0x30,0x01,0x19,0x0c,0x32,0x04,0x0d,0x36,0x62,0x60,0x08,0x01,0x1a, + 0x2c,0x45,0x30,0xdb,0x00,0x05,0x40,0x06,0x01,0x31,0x00,0x00,0x00,0x02,0x00,0x00, + 0x00,0x5b,0x86,0x24,0xf8,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sspine_vs_bytecode_metal_ios[3068] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xfc,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf0,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x93,0xd6,0x91,0x34,0xa6,0x5a,0xef, + 0xf4,0xa9,0x2b,0xc7,0x55,0x75,0x4a,0x7f,0xc5,0x46,0xc0,0x95,0x92,0x61,0x00,0x3e, + 0x6d,0x53,0x68,0xee,0xb6,0x8e,0xc8,0x26,0x6c,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x37,0x00,0x00,0x00,0x56,0x41,0x54, + 0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x80, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03,0x00,0x04,0x04,0x06, + 0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b, + 0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xd8,0x0a,0x00,0x00,0xff,0xff,0xff,0xff, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xb3,0x02,0x00,0x00,0x0b,0x82,0x20,0x00, + 0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49, + 0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62, + 0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49, + 0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72, + 0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00, + 0x51,0x18,0x00,0x00,0x82,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8,0xff,0xff,0xff,0xff, + 0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87,0x71,0xa0,0x07,0x76, + 0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72,0x20,0x87,0x36,0x20, + 0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88,0x07,0x79,0xa0,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0xc0,0x1c,0xc2,0x81, + 0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c, + 0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c,0xd8,0x81,0x1d,0xe6, + 0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87,0x72,0x98,0x87,0x79, + 0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72,0x68,0x03,0x73,0x80, + 0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xdc, + 0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda,0x00,0x1e,0xde,0x21, + 0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21,0x1d,0xda,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30,0x07,0x79,0x08,0x87, + 0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87,0x72,0x90,0x87,0x36, + 0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77,0x68,0x03,0x76,0x28, + 0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70,0x07,0x73,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1,0x1d,0xe0,0xa1,0x0d, + 0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87, + 0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87,0x79,0x68,0x03,0x73, + 0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d,0xca,0x81,0x1c,0xda, + 0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21, + 0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x70,0x87,0x77,0x68, + 0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38,0x87,0x36,0x68,0x87, + 0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0x62,0x1e,0xe8,0x21, + 0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1,0x1c,0xc6,0x81,0x1e, + 0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x0d,0xe6, + 0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87,0x79,0x08,0x07,0x73, + 0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e, + 0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d,0xe6,0xe1,0x1d,0xcc, + 0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00,0x73,0x08,0x07,0x76, + 0x98,0x87,0x72,0x00,0x36,0x20,0x02,0x01,0x24,0xc0,0x02,0x54,0x00,0x00,0x00,0x00, + 0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00, + 0x1f,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4,0x84, + 0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4,0x4c, + 0x10,0x44,0x33,0x00,0xc3,0x08,0x02,0x30,0x8c,0x40,0x00,0x76,0x08,0x42,0x24,0x81, + 0x98,0x89,0x9a,0x07,0x7a,0x90,0x87,0x7a,0x18,0x07,0x7a,0x70,0x83,0x76,0x28,0x07, + 0x7a,0x08,0x07,0x76,0xd0,0x03,0x3d,0x68,0x87,0x70,0xa0,0x07,0x79,0x48,0x07,0x7c, + 0x40,0x01,0x39,0x48,0x9a,0x22,0x4a,0x98,0xfc,0x4a,0xfa,0x1f,0x20,0x02,0x18,0x09, + 0x05,0x65,0x10,0xc1,0x10,0x4a,0x31,0x42,0x10,0x87,0xd0,0x40,0xc0,0x1c,0x01,0x18, + 0xa4,0xc0,0x9a,0x23,0x00,0x85,0x41,0x04,0x41,0x18,0x46,0x20,0x96,0x11,0x00,0x00, + 0x13,0xa8,0x70,0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60, + 0x87,0x72,0x68,0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83, + 0x0d,0xb7,0x51,0x0e,0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d, + 0x90,0x0e,0x78,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76,0xa0, + 0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07, + 0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07,0x76, + 0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20, + 0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a, + 0x10,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30, + 0x07,0x72,0xd0,0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xd0,0x06,0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xd0,0x06,0xf6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0, + 0x06,0xf6,0x90,0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07, + 0x78,0xd0,0x06,0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a, + 0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50, + 0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07, + 0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75, + 0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0, + 0x06,0xf6,0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07, + 0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07,0x76, + 0xa0,0x07,0x73,0x20,0x07,0x43,0x98,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80, + 0x2c,0x10,0x00,0x00,0x0a,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90, + 0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50,0x04,0x05,0x18,0x50, + 0x08,0x05,0x51,0x06,0x05,0x42,0x6d,0x04,0x80,0xd8,0x58,0x02,0x24,0x00,0x00,0x00, + 0x79,0x18,0x00,0x00,0xea,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25, + 0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x32,0x28,0x00,0xa3,0x50,0xb9, + 0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0x81,0x22,0x2c,0x05, + 0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c, + 0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06,0x04, + 0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6, + 0xe6,0xc6,0x45,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25,0x58,0x8c,0x45,0x60,0xd1, + 0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x82,0x45,0xe0,0x16,0x96,0x26, + 0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36, + 0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50,0x12,0x72,0x61,0x69,0x72, + 0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61, + 0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04,0x65,0x21,0x19,0x84,0xa5, + 0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89, + 0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85,0x89, + 0xb1,0x95,0x0d,0x11,0x94,0x86,0x51,0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19, + 0xde,0xd7,0x5b,0x1d,0x1d,0x5c,0x1d,0x1d,0x97,0xba,0xb9,0x32,0x39,0x14,0xb6,0xb7, + 0x31,0x37,0x98,0x14,0x46,0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x74,0x79,0x70, + 0x65,0x5f,0x6e,0x61,0x6d,0x65,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0xc8,0x84,0xa5, + 0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95,0x51,0xa8,0xb3,0x1b,0xc2,0x28, + 0x8f,0x02,0x29,0x91,0x22,0x29,0x93,0x42,0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61,0x7b, + 0x1b,0x73,0x8b,0x49,0xa1,0x61,0xc6,0xf6,0x16,0x46,0x47,0xc3,0x62,0xec,0x8d,0xed, + 0x4d,0x6e,0x08,0xa3,0x3c,0x8a,0xa5,0x44,0xca,0xa5,0x4c,0x0a,0x46,0x26,0x2c,0x4d, + 0xce,0x05,0xee,0x6d,0x2e,0x8d,0x2e,0xed,0xcd,0x8d,0xcb,0x19,0xdb,0x17,0xd4,0xdb, + 0x5c,0x1a,0x5d,0xda,0x9b,0xdb,0x10,0x45,0xd1,0x94,0x48,0xb9,0x94,0x49,0xd9,0x86, + 0x18,0x4a,0xa5,0x64,0x0a,0x47,0x28,0x2c,0x4d,0xce,0xc5,0xae,0x4c,0x8e,0xae,0x0c, + 0xef,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x52,0x58,0x9a,0x9c,0x0b,0xdb,0xdb,0x58,0x18, + 0x5d,0xda,0x9b,0xdb,0x57,0x9a,0x1b,0x59,0x19,0x1e,0xbd,0xb3,0x32,0xb7,0x32,0xb9, + 0x30,0xba,0x32,0x32,0x94,0xaf,0xaf,0xb0,0x34,0xb9,0x2f,0x38,0xb6,0xb0,0xb1,0x32, + 0xb4,0x37,0x36,0xb2,0x32,0xb9,0xaf,0xaf,0x14,0x22,0x70,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x43,0xa8,0x45,0x50,0x3c,0xe5,0x5b,0x84,0x25,0x50,0xc0,0x40,0x89,0x14, + 0x49,0x99,0x94,0x30,0x60,0x42,0x57,0x86,0x37,0xf6,0xf6,0x26,0x47,0x06,0x33,0x84, + 0x5a,0x02,0xc5,0x53,0xbe,0x25,0x58,0x02,0x05,0x0c,0x94,0x48,0x91,0x94,0x49,0x19, + 0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8,0x65,0x50,0x3c,0xe5,0x5b,0x86, + 0x25,0x50,0xc0,0x40,0x89,0x94,0x4b,0x99,0x94,0x32,0xa0,0x12,0x96,0x26,0xe7,0x22, + 0x56,0x67,0x66,0x56,0x26,0xc7,0x27,0x2c,0x4d,0xce,0x45,0xac,0xce,0xcc,0xac,0x4c, + 0xee,0x6b,0x2e,0x4d,0xaf,0x8c,0x48,0x58,0x9a,0x9c,0x8b,0x5c,0x59,0x18,0x19,0xa9, + 0xb0,0x34,0x39,0x97,0x39,0x3a,0xb9,0xba,0x31,0xba,0x2f,0xba,0x3c,0xb8,0xb2,0xaf, + 0x34,0x37,0xb3,0x37,0x22,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x1c,0xda,0xec, + 0xe0,0x86,0x28,0x8b,0xb0,0x10,0x8b,0xa0,0xac,0x81,0xc2,0x06,0x8c,0xc2,0xd2,0xe4, + 0x5c,0xc2,0xe4,0xce,0xbe,0xe8,0xf2,0xe0,0xca,0xbe,0xe6,0xd2,0xf4,0xca,0x78,0x85, + 0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xd1,0xe5,0xc1,0x95,0x7d,0x85,0xb1,0xa5,0x9d, + 0xb9,0x7d,0xcd,0xa5,0xe9,0x95,0x31,0xb1,0x9b,0xfb,0x82,0x0b,0x93,0x0b,0x6b,0x9b, + 0xe3,0xf0,0x15,0x93,0x33,0x84,0x0c,0x96,0x43,0x39,0x03,0x05,0x0d,0x16,0x42,0xf9, + 0x16,0x61,0x09,0x94,0x34,0x50,0xd4,0x40,0x69,0x03,0xc5,0x0d,0x16,0x42,0x79,0x83, + 0x05,0x51,0x22,0x05,0x0e,0x94,0x49,0x89,0x83,0x21,0x88,0x22,0x06,0x0a,0x19,0x28, + 0x66,0xa0,0xc8,0xc1,0x10,0x23,0x01,0x94,0x4e,0x99,0x03,0x3e,0x6f,0x6d,0x6e,0x69, + 0x70,0x6f,0x74,0x65,0x6e,0x74,0x20,0x63,0x68,0x61,0x72,0x7c,0xa6,0xd2,0xda,0xe0, + 0xd8,0xca,0x40,0x86,0x56,0x56,0x40,0xa8,0x84,0x82,0x82,0x86,0x08,0x8a,0x1d,0x0c, + 0x31,0x94,0x3a,0x50,0xee,0xa0,0x49,0x86,0x18,0x0a,0x1e,0x28,0x78,0xd0,0x24,0x23, + 0x22,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x77,0x20,0x87,0x7a,0x60,0x87, + 0x72,0x70,0x03,0x73,0x60,0x87,0x70,0x38,0x87,0x79,0x98,0x22,0x04,0xc3,0x08,0x85, + 0x1d,0xd8,0xc1,0x1e,0xda,0xc1,0x0d,0xd2,0x81,0x1c,0xca,0xc1,0x1d,0xe8,0x61,0x4a, + 0x50,0x8c,0x58,0xc2,0x21,0x1d,0xe4,0xc1,0x0d,0xec,0xa1,0x1c,0xe4,0x61,0x1e,0xd2, + 0xe1,0x1d,0xdc,0x61,0x4a,0x60,0x8c,0xa0,0xc2,0x21,0x1d,0xe4,0xc1,0x0d,0xd8,0x21, + 0x1c,0xdc,0xe1,0x1c,0xea,0x21,0x1c,0xce,0xa1,0x1c,0x7e,0xc1,0x1e,0xca,0x41,0x1e, + 0xe6,0x21,0x1d,0xde,0xc1,0x1d,0xa6,0x04,0xc8,0x88,0x29,0x1c,0xd2,0x41,0x1e,0xdc, + 0x60,0x1c,0xde,0xa1,0x1d,0xe0,0x21,0x1d,0xd8,0xa1,0x1c,0x7e,0xe1,0x1d,0xe0,0x81, + 0x1e,0xd2,0xe1,0x1d,0xdc,0x61,0x1e,0xa6,0x0c,0x0a,0xe3,0x8c,0x50,0xc2,0x21,0x1d, + 0xe4,0xc1,0x0d,0xec,0xa1,0x1c,0xe4,0x81,0x1e,0xca,0x01,0x1f,0xa6,0x04,0x74,0x00, + 0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66, + 0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73, + 0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e, + 0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b, + 0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b, + 0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20, + 0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0, + 0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61, + 0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83, + 0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87, + 0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76, + 0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98, + 0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30, + 0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61, + 0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43, + 0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b, + 0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7, + 0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18, + 0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90, + 0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1, + 0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d, + 0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24, + 0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c, + 0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d, + 0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54, + 0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4, + 0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18, + 0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec,0xc0, + 0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8,0xe1, + 0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4,0x83, + 0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43,0x3d, + 0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00, + 0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x1e,0x00,0x00,0x00,0x13,0x04,0x41,0x2c, + 0x10,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0xf4,0xc6,0x22,0x82,0x20,0x08,0x46,0x00, + 0xa8,0x95,0x40,0x19,0xd0,0x98,0x01,0xa0,0x30,0x03,0x00,0x00,0xe3,0x15,0x07,0x33, + 0x4d,0x0c,0x05,0x65,0x90,0x81,0x19,0x0e,0x13,0x02,0xf9,0x8c,0x57,0x2c,0xd0,0x75, + 0x21,0x14,0x94,0x41,0x06,0xe8,0x60,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1,0x0a, + 0xa8,0xe2,0x38,0x86,0x82,0x62,0x43,0x00,0x9f,0xd9,0x06,0xa7,0x02,0x66,0x1b,0x82, + 0x2a,0x98,0x6d,0x08,0x06,0x21,0x83,0x80,0x18,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x5b,0x86,0x20,0xc8,0x83,0x2d,0x43,0x11,0xe4,0xc1,0x96,0x41,0x09,0xf2,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sspine_fs_bytecode_metal_ios[3241] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xa9,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xd0,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xf2,0xd8,0x54,0xd8,0x76,0x28,0x3c, + 0xc1,0x24,0xd6,0xe7,0xb5,0xc4,0xbc,0x0f,0x0f,0x5a,0x55,0xdc,0x0c,0x24,0xa5,0x96, + 0x04,0x74,0x34,0x10,0x4e,0xdf,0x2e,0x1d,0xee,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44, + 0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0xbc,0x0b,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0xec,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32, + 0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45, + 0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x49,0x0a,0x32,0x44, + 0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21,0x72,0x24,0x07,0xc8, + 0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00,0x51,0x18,0x00, + 0x00,0x8e,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff,0xff,0x01,0x60,0x00, + 0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07,0x7a,0x60,0x87,0x7c, + 0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72,0x68,0x03,0x72,0x48, + 0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90,0x07,0x7a,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc,0x21,0x1c,0xd8,0x61, + 0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81,0x1d,0xca,0xa1,0x0d, + 0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d,0xd8,0x61,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87,0x79,0x98,0x87,0x36, + 0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36,0x30,0x07,0x78,0x68, + 0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0xc2,0x1d,0xde, + 0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0,0xe1,0x1d,0xd2,0xc1, + 0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1,0x1d,0x00,0x7a,0x90, + 0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90,0x87,0x70,0x68,0x87, + 0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07,0x79,0x68,0x83,0x72, + 0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36,0x60,0x87,0x72,0x08, + 0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30,0x87,0x79,0x68,0x03, + 0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1, + 0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01,0x1e,0xda,0x80,0x1e, + 0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07, + 0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87,0x36,0x30,0x07,0x78, + 0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20, + 0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c,0xc8,0xa1,0x0d,0xf4, + 0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda,0xa0,0x1d,0xc2,0x81, + 0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0xa0, + 0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68,0x83,0x76,0x08,0x07, + 0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6,0x81,0x1e,0xc2,0x61, + 0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d, + 0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e,0xda,0x60,0x1e,0xd2, + 0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87,0x70,0x30,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e, + 0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e,0xde,0xc1,0x1c,0xe8, + 0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87,0x70,0x60,0x87,0x79, + 0x28,0x07,0x60,0x03,0x22,0x0c,0x40,0x02,0x2c,0x40,0xb5,0xc1,0x18,0x08,0x60,0x01, + 0xaa,0x0d,0x06,0x51,0x00,0x0b,0x50,0x6d,0x30,0x8a,0xff,0xff,0xff,0xff,0x1f,0x00, + 0x09,0xa0,0x36,0x20,0xc6,0xff,0xff,0xff,0xff,0x0f,0x00,0x03,0x48,0x40,0xb5,0xc1, + 0x38,0x02,0x60,0x01,0xaa,0x0d,0x06,0x22,0x00,0x0b,0x50,0x01,0x00,0x49,0x18,0x00, + 0x00,0x03,0x00,0x00,0x00,0x13,0x88,0x40,0x18,0x88,0x09,0x41,0x31,0x61,0x30,0x0e, + 0x04,0x89,0x20,0x00,0x00,0x27,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85, + 0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a, + 0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x6c,0x33,0x00,0xc3,0x08,0x04,0x60,0x83,0x30,0x8c, + 0x20,0x00,0x07,0x49,0x53,0x44,0x09,0x93,0x5f,0x48,0xff,0x03,0x44,0x00,0x23,0xa1, + 0x00,0x0c,0x22,0x10,0xc2,0x51,0xd2,0x14,0x51,0xc2,0xe4,0xff,0x13,0x71,0x4d,0x54, + 0x44,0xfc,0xf6,0xf0,0x4f,0x63,0x04,0xc0,0x20,0x82,0x11,0x5c,0x24,0x4d,0x11,0x25, + 0x4c,0xfe,0x2f,0x01,0xcc,0xb3,0x10,0xd1,0x3f,0x8d,0x11,0x00,0x83,0x08,0x88,0x50, + 0x0c,0x31,0x42,0x39,0x89,0x54,0x21,0x42,0x08,0x81,0xd8,0x1c,0x41,0x30,0x47,0x00, + 0x06,0xc3,0x08,0xc2,0x53,0x90,0x70,0xd2,0x70,0xd0,0x01,0x8a,0x03,0x01,0x29,0xf0, + 0x86,0x11,0x86,0x67,0x18,0x61,0x00,0x86,0x11,0x88,0x67,0x8e,0x00,0x14,0x06,0x11, + 0x00,0x61,0x04,0x00,0x00,0x13,0xa8,0x70,0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83, + 0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37, + 0x80,0x03,0x37,0x80,0x83,0x0d,0xb7,0x51,0x0e,0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80, + 0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06, + 0xe9,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9, + 0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x60, + 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0, + 0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07, + 0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73, + 0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40, + 0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x90,0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78, + 0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10, + 0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07, + 0x72,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6, + 0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60, + 0x07,0x6d,0x60,0x0f,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07, + 0x75,0x10,0x07,0x72,0xd0,0x06,0xf6,0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71, + 0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xd0,0x06,0xee,0x80, + 0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x43,0x98,0x05,0x00,0x80,0x00, + 0x00,0x00,0x00,0x00,0x80,0x21,0x4c,0x03,0x04,0x80,0x00,0x00,0x00,0x00,0x00,0xc0, + 0x10,0x46,0x02,0x02,0x60,0x00,0x00,0x00,0x00,0x00,0x20,0x0b,0x04,0x09,0x00,0x00, + 0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43, + 0x7a,0x23,0x00,0x25,0x50,0x08,0x45,0x50,0x10,0x65,0x40,0x78,0x04,0x80,0xe8,0x58, + 0x02,0x24,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xf9,0x00,0x00,0x00,0x1a,0x03,0x4c, + 0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x63, + 0x4c,0x00,0xa5,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62, + 0x3c,0xc4,0x24,0x3c,0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e, + 0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6, + 0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06, + 0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0x30,0x11,0x43,0x8c,0x87, + 0x78,0x8e,0x67,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x99,0x8e,0x87,0x78,0x88, + 0x67,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56, + 0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x98, + 0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61, + 0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x84, + 0x69,0x21,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98, + 0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1, + 0x7d,0x95,0xb9,0x85,0x89,0xb1,0x95,0x0d,0x11,0xa6,0x86,0x51,0x58,0x9a,0x9c,0x8b, + 0x5c,0x99,0x1b,0x59,0x99,0xdc,0x17,0x5d,0x98,0xdc,0x59,0x19,0x1d,0xa3,0xb0,0x34, + 0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0x2f,0xb7,0xb0,0xb6,0x32,0x1a, + 0x66,0x6c,0x6f,0x61,0x74,0x34,0x64,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xdc, + 0xc2,0xda,0xca,0xa8,0x98,0xc9,0x85,0x9d,0x7d,0x8d,0xbd,0xb1,0xbd,0xc9,0x0d,0x61, + 0xa6,0xe7,0x19,0x26,0x68,0x8a,0x26,0x69,0x9a,0x86,0x08,0x13,0x45,0x29,0x2c,0x4d, + 0xce,0xc5,0x4c,0x2e,0xec,0xac,0xad,0xcc,0x8d,0xee,0x2b,0xcd,0x0d,0xae,0x8e,0x8e, + 0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x1b,0x4c,0x0a,0x95,0xb0,0x34,0x39, + 0x97,0xb1,0x32,0x37,0xba,0x32,0x39,0x3e,0x61,0x69,0x72,0x2e,0x70,0x65,0x72,0x73, + 0x70,0x65,0x63,0x74,0x69,0x76,0x65,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x28,0xd4, + 0xd9,0x0d,0x91,0x9e,0x61,0xb2,0xa6,0x6b,0xc2,0xa6,0x6c,0x82,0x26,0x6d,0x92,0xa6, + 0x8d,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x5b,0x4c,0x0a,0x8b,0xb1,0x37, + 0xb6,0x37,0xb9,0x21,0xd2,0x43,0x4c,0xd6,0xd4,0x4d,0xd8,0x94,0x4d,0xd0,0x14,0x4d, + 0xd2,0xe4,0x51,0x09,0x4b,0x93,0x73,0x11,0xab,0x33,0x33,0x2b,0x93,0xe3,0x13,0x96, + 0x26,0xe7,0x22,0x56,0x67,0x66,0x56,0x26,0xf7,0x35,0x97,0xa6,0x57,0x46,0x29,0x2c, + 0x4d,0xce,0x85,0xed,0x6d,0x2c,0x8c,0x2e,0xed,0xcd,0xed,0x2b,0xcd,0x8d,0xac,0x0c, + 0x8f,0x48,0x58,0x9a,0x9c,0x8b,0x5c,0x59,0x18,0x19,0xa9,0xb0,0x34,0x39,0x97,0x39, + 0x3a,0xb9,0xba,0x31,0xba,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x34,0x37,0xb3,0x37,0x16, + 0x66,0x6c,0x6f,0x61,0x74,0x1c,0xe0,0xda,0xc2,0x86,0x28,0xcf,0xf0,0x14,0xcf,0x30, + 0x95,0xc1,0x64,0x06,0x8c,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xe8,0xf2,0xe0, + 0xca,0xbe,0xe6,0xd2,0xf4,0xca,0x78,0x85,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xd1, + 0xe5,0xc1,0x95,0x7d,0x85,0xb1,0xa5,0x9d,0xb9,0x7d,0xcd,0xa5,0xe9,0x95,0x31,0x31, + 0x9b,0xfb,0x82,0x0b,0x93,0x0b,0x6b,0x9b,0xe3,0xf0,0x55,0x33,0x33,0x84,0x0c,0x1e, + 0x63,0x02,0x83,0x29,0x0c,0x9e,0x62,0x12,0x83,0x67,0x78,0x88,0x69,0x0c,0x26,0x32, + 0x98,0xce,0x60,0x42,0x83,0xa7,0x98,0xd2,0xe0,0x29,0x26,0x68,0x52,0x83,0x49,0x9a, + 0xd6,0x80,0x4b,0x58,0x9a,0x9c,0x0b,0x5d,0x19,0x1e,0x5d,0x9d,0x5c,0x19,0x95,0xb0, + 0x34,0x39,0x97,0xb9,0xb0,0x36,0x38,0xb6,0x32,0x62,0x74,0x65,0x78,0x74,0x75,0x72, + 0x65,0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x2c,0x20,0x73,0x61,0x6d,0x70,0x6c, + 0x65,0x3e,0x1c,0xe8,0xca,0xf0,0x86,0x50,0x0f,0x32,0xb5,0xc1,0x24,0x06,0xcf,0xf0, + 0x10,0x93,0x1b,0x4c,0xd0,0xf4,0x06,0x93,0x34,0xc1,0x01,0x97,0xb0,0x34,0x39,0x97, + 0xb9,0xb0,0x36,0x38,0xb6,0x32,0x39,0x1e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x1c, + 0xe6,0xda,0xe0,0x86,0x48,0x4f,0x31,0xc9,0xc1,0x24,0x06,0xcf,0xf0,0x10,0x13,0x34, + 0xcd,0xc1,0x24,0x4d,0x74,0x30,0x44,0x99,0xb8,0xe9,0x9b,0xd8,0x60,0x8a,0x83,0xa9, + 0x0e,0x86,0x18,0x0b,0x30,0x55,0x93,0x1d,0xd0,0xf9,0xd2,0xa2,0x9a,0xca,0x31,0x9b, + 0xfb,0x82,0x0b,0x93,0x0b,0x6b,0x9b,0xe3,0xf3,0xd6,0xe6,0x96,0x06,0xf7,0x46,0x57, + 0xe6,0x46,0x07,0x32,0x86,0x16,0x26,0xc7,0x67,0x2a,0xad,0x0d,0x8e,0xad,0x0c,0x64, + 0x68,0x65,0x05,0x84,0x4a,0x28,0x28,0x68,0x88,0x30,0xe9,0xc1,0x10,0x63,0xca,0x83, + 0x69,0x0f,0xb0,0x64,0x88,0x31,0x95,0xc1,0xc4,0x07,0x58,0x32,0xc4,0x98,0xf0,0x60, + 0xea,0x03,0x2c,0x19,0x62,0x4c,0x7e,0x30,0xf5,0x01,0x96,0x8c,0x88,0xd8,0x81,0x1d, + 0xec,0xa1,0x1d,0xdc,0xa0,0x1d,0xde,0x81,0x1c,0xea,0x81,0x1d,0xca,0xc1,0x0d,0xcc, + 0x81,0x1d,0xc2,0xe1,0x1c,0xe6,0x61,0x8a,0x10,0x0c,0x23,0x14,0x76,0x60,0x07,0x7b, + 0x68,0x07,0x37,0x48,0x07,0x72,0x28,0x07,0x77,0xa0,0x87,0x29,0x41,0x31,0x62,0x09, + 0x87,0x74,0x90,0x07,0x37,0xb0,0x87,0x72,0x90,0x87,0x79,0x48,0x87,0x77,0x70,0x87, + 0x29,0x81,0x31,0x82,0x0a,0x87,0x74,0x90,0x07,0x37,0x60,0x87,0x70,0x70,0x87,0x73, + 0xa8,0x87,0x70,0x38,0x87,0x72,0xf8,0x05,0x7b,0x28,0x07,0x79,0x98,0x87,0x74,0x78, + 0x07,0x77,0x98,0x12,0x20,0x23,0xa6,0x70,0x48,0x07,0x79,0x70,0x83,0x71,0x78,0x87, + 0x76,0x80,0x87,0x74,0x60,0x87,0x72,0xf8,0x85,0x77,0x80,0x07,0x7a,0x48,0x87,0x77, + 0x70,0x87,0x79,0x98,0x32,0x28,0x8c,0x33,0x82,0x09,0x87,0x74,0x90,0x07,0x37,0x30, + 0x07,0x79,0x08,0x87,0x73,0x68,0x87,0x72,0x70,0x07,0x7a,0x98,0x12,0xdc,0x01,0x00, + 0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c, + 0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07, + 0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42, + 0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83, + 0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07, + 0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70, + 0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3, + 0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2, + 0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc, + 0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68, + 0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07, + 0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72, + 0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec, + 0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc, + 0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8, + 0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03, + 0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c, + 0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74, + 0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4, + 0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc, + 0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1, + 0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50, + 0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51, + 0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21, + 0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06, + 0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c, + 0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77, + 0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec, + 0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8, + 0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4, + 0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43, + 0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x0b,0x00,0x00,0x00,0x26,0xb0,0x01, + 0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e, + 0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20,0x0d,0x6d,0x01,0x0d,0x80,0x44,0x3e,0x83, + 0x5c,0x7e,0x85,0x17,0xb7,0x0d,0x00,0x00,0x00,0x61,0x20,0x00,0x00,0x25,0x00,0x00, + 0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x74,0x47,0x00, + 0xc6,0x22,0x80,0x40,0x38,0xe6,0x20,0x06,0xc2,0xa8,0xc8,0xd5,0xc0,0x08,0x00,0xbd, + 0x19,0x00,0x82,0x23,0x00,0x54,0xc7,0x1a,0x80,0x40,0x18,0x6b,0x18,0x86,0x81,0xec, + 0x0c,0x00,0x89,0x19,0x00,0x0a,0x33,0x00,0x04,0x46,0x00,0x00,0x00,0x23,0x06,0xca, + 0x10,0x6c,0x8f,0x23,0x29,0x47,0x12,0x58,0x20,0xc9,0x67,0x90,0x21,0x20,0x90,0x41, + 0x06,0xa1,0x40,0x4c,0x08,0xe4,0x33,0xc8,0x10,0x24,0xd0,0x20,0x43,0x50,0x48,0x16, + 0x60,0xf2,0x19,0x6f,0xc0,0x38,0x31,0xa0,0x60,0xcc,0x31,0x30,0x01,0x19,0x0c,0x32, + 0x04,0x0d,0x36,0x62,0x60,0x08,0x01,0x1a,0x2c,0x45,0x30,0xdb,0x00,0x05,0x40,0x06, + 0x01,0x31,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x5b,0x86,0x24,0xf8,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sspine_vs_source_metal_sim[624] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20,0x6d,0x76,0x70,0x3b,0x0a,0x7d,0x3b,0x0a, + 0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75, + 0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x75, + 0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29,0x5d, + 0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63,0x6f, + 0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x31, + 0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x70,0x6f, + 0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74, + 0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x0a,0x7b,0x0a, + 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28, + 0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32, + 0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x5b,0x5b,0x61,0x74,0x74, + 0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x5b, + 0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x32,0x29,0x5d,0x5d,0x3b, + 0x0a,0x7d,0x3b,0x0a,0x0a,0x76,0x65,0x72,0x74,0x65,0x78,0x20,0x6d,0x61,0x69,0x6e, + 0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e, + 0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f, + 0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x26,0x20,0x5f,0x31,0x39,0x20,0x5b,0x5b, + 0x62,0x75,0x66,0x66,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20, + 0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74, + 0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x67, + 0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x5f,0x31,0x39, + 0x2e,0x6d,0x76,0x70,0x20,0x2a,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e, + 0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20, + 0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75,0x76, + 0x20,0x3d,0x20,0x69,0x6e,0x2e,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d, + 0x20,0x69,0x6e,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, + +}; +static const uint8_t _sspine_fs_source_metal_sim[619] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x66, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x20,0x70,0x6d,0x61,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74, + 0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b, + 0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30, + 0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x32,0x20,0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28, + 0x6c,0x6f,0x63,0x6e,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c, + 0x6f,0x61,0x74,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65, + 0x72,0x28,0x6c,0x6f,0x63,0x6e,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a, + 0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f, + 0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69, + 0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d, + 0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x66,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x26,0x20,0x5f,0x35,0x33,0x20,0x5b,0x5b,0x62,0x75,0x66, + 0x66,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72, + 0x65,0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x74,0x65,0x78,0x20,0x5b, + 0x5b,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x73, + 0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x73,0x6d,0x70,0x20,0x5b,0x5b,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20, + 0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d, + 0x20,0x7b,0x7d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x5f,0x32,0x38,0x20,0x3d,0x20,0x74,0x65,0x78,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65, + 0x28,0x73,0x6d,0x70,0x2c,0x20,0x69,0x6e,0x2e,0x75,0x76,0x29,0x20,0x2a,0x20,0x69, + 0x6e,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x20,0x5f,0x33,0x37,0x20,0x3d,0x20,0x5f,0x32,0x38,0x2e,0x77,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x20,0x3d,0x20,0x6d,0x69,0x78,0x28,0x5f,0x32,0x38,0x2c,0x20,0x66,0x6c, + 0x6f,0x61,0x74,0x34,0x28,0x5f,0x32,0x38,0x2e,0x78,0x79,0x7a,0x20,0x2a,0x20,0x5f, + 0x33,0x37,0x2c,0x20,0x5f,0x33,0x37,0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x63,0x6f, + 0x6c,0x6f,0x72,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x5f,0x35,0x33,0x2e, + 0x70,0x6d,0x61,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72, + 0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_WGPU) +static const uint8_t _sspine_vs_source_wgsl[1003] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a, + 0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20, + 0x20,0x6d,0x76,0x70,0x20,0x3a,0x20,0x6d,0x61,0x74,0x34,0x78,0x34,0x66,0x2c,0x0a, + 0x7d,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x30,0x29,0x20,0x40,0x62,0x69, + 0x6e,0x64,0x69,0x6e,0x67,0x28,0x30,0x29,0x20,0x76,0x61,0x72,0x3c,0x75,0x6e,0x69, + 0x66,0x6f,0x72,0x6d,0x3e,0x20,0x78,0x5f,0x31,0x39,0x20,0x3a,0x20,0x76,0x73,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x73,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70, + 0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65, + 0x3e,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x3a,0x20,0x76,0x65, + 0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74, + 0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66, + 0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a, + 0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x67,0x6c, + 0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20,0x76,0x65,0x63,0x34, + 0x66,0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20, + 0x7b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x32,0x20,0x3a,0x20,0x6d, + 0x61,0x74,0x34,0x78,0x34,0x66,0x20,0x3d,0x20,0x78,0x5f,0x31,0x39,0x2e,0x6d,0x76, + 0x70,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x36,0x20,0x3a,0x20, + 0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x5f,0x31,0x3b,0x0a,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x20,0x3d,0x20,0x28,0x78,0x5f,0x32,0x32,0x20,0x2a,0x20,0x76,0x65,0x63,0x34, + 0x66,0x28,0x78,0x5f,0x32,0x36,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x32,0x36,0x2e,0x79, + 0x2c,0x20,0x30,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x29,0x3b,0x0a, + 0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x38,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a, + 0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x78,0x5f,0x33,0x38,0x3b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x34,0x32,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20, + 0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x20,0x3d,0x20,0x78,0x5f,0x34,0x32,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75, + 0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61, + 0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x40,0x62,0x75,0x69,0x6c, + 0x74,0x69,0x6e,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x29,0x0a,0x20,0x20, + 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20,0x76,0x65, + 0x63,0x34,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e, + 0x28,0x30,0x29,0x0a,0x20,0x20,0x75,0x76,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28, + 0x31,0x29,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x31,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x76,0x65,0x72,0x74,0x65,0x78, + 0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69, + 0x6f,0x6e,0x28,0x30,0x29,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20, + 0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x20,0x74,0x65,0x78, + 0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28, + 0x32,0x29,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x29,0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e, + 0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x5f,0x31,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f, + 0x72,0x64,0x30,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20, + 0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a, + 0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65, + 0x74,0x75,0x72,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x67,0x6c, + 0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x2c,0x20,0x75,0x76,0x2c,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const uint8_t _sspine_fs_source_wgsl[1125] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a, + 0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20, + 0x20,0x70,0x6d,0x61,0x20,0x3a,0x20,0x66,0x33,0x32,0x2c,0x0a,0x7d,0x0a,0x0a,0x40, + 0x67,0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e, + 0x67,0x28,0x34,0x38,0x29,0x20,0x76,0x61,0x72,0x20,0x74,0x65,0x78,0x20,0x3a,0x20, + 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x32,0x64,0x3c,0x66,0x33,0x32,0x3e,0x3b, + 0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e, + 0x64,0x69,0x6e,0x67,0x28,0x36,0x34,0x29,0x20,0x76,0x61,0x72,0x20,0x73,0x6d,0x70, + 0x20,0x3a,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x76,0x61,0x72, + 0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61, + 0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34, + 0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e, + 0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65, + 0x63,0x34,0x66,0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x30,0x29,0x20, + 0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x34,0x29,0x20,0x76,0x61,0x72,0x3c, + 0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x3e,0x20,0x78,0x5f,0x35,0x33,0x20,0x3a,0x20, + 0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x6d, + 0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20, + 0x63,0x30,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x20,0x20,0x76,0x61, + 0x72,0x20,0x63,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x20,0x20, + 0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x33,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, + 0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x2c,0x20,0x73, + 0x6d,0x70,0x2c,0x20,0x78,0x5f,0x32,0x33,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74, + 0x20,0x78,0x5f,0x32,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x63,0x30,0x20,0x3d,0x20,0x28,0x78, + 0x5f,0x32,0x34,0x20,0x2a,0x20,0x78,0x5f,0x32,0x37,0x29,0x3b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x33,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20, + 0x3d,0x20,0x63,0x30,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x37, + 0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x63,0x30,0x2e,0x77,0x3b,0x0a,0x20, + 0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x38,0x20,0x3a,0x20,0x76,0x65,0x63,0x33, + 0x66,0x20,0x3d,0x20,0x28,0x76,0x65,0x63,0x33,0x66,0x28,0x78,0x5f,0x33,0x31,0x2e, + 0x78,0x2c,0x20,0x78,0x5f,0x33,0x31,0x2e,0x79,0x2c,0x20,0x78,0x5f,0x33,0x31,0x2e, + 0x7a,0x29,0x20,0x2a,0x20,0x78,0x5f,0x33,0x37,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65, + 0x74,0x20,0x78,0x5f,0x34,0x30,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x63, + 0x30,0x2e,0x77,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x35,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x20,0x20,0x63,0x31,0x20,0x3d,0x20,0x28,0x76,0x65,0x63,0x34,0x66,0x28,0x78, + 0x5f,0x33,0x38,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x33,0x38,0x2e,0x79,0x2c,0x20,0x78, + 0x5f,0x33,0x38,0x2e,0x7a,0x2c,0x20,0x78,0x5f,0x34,0x30,0x29,0x20,0x2a,0x20,0x78, + 0x5f,0x34,0x35,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x39, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x63,0x30,0x3b,0x0a,0x20, + 0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x35,0x30,0x20,0x3a,0x20,0x76,0x65,0x63,0x34, + 0x66,0x20,0x3d,0x20,0x63,0x31,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f, + 0x35,0x38,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x78,0x5f,0x35,0x33,0x2e, + 0x70,0x6d,0x61,0x3b,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f, + 0x72,0x20,0x3d,0x20,0x6d,0x69,0x78,0x28,0x78,0x5f,0x34,0x39,0x2c,0x20,0x78,0x5f, + 0x35,0x30,0x2c,0x20,0x76,0x65,0x63,0x34,0x66,0x28,0x78,0x5f,0x35,0x38,0x2c,0x20, + 0x78,0x5f,0x35,0x38,0x2c,0x20,0x78,0x5f,0x35,0x38,0x2c,0x20,0x78,0x5f,0x35,0x38, + 0x29,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d,0x0a, + 0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74, + 0x20,0x7b,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30, + 0x29,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x31, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x66,0x72, + 0x61,0x67,0x6d,0x65,0x6e,0x74,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x40, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x75,0x76,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c, + 0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x29,0x20, + 0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20, + 0x75,0x76,0x20,0x3d,0x20,0x75,0x76,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29, + 0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f, + 0x6f,0x75,0x74,0x28,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x29,0x3b, + 0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_DUMMY_BACKEND) +static const char* _sspine_vs_source_dummy = ""; +static const char* _sspine_fs_source_dummy = ""; +#else +#error "Please define one of SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" +#endif + +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs + +#define _sspine_def(val, def) (((val) == 0) ? (def) : (val)) +#define _SSPINE_INIT_COOKIE (0xABBAABBA) +#define _SSPINE_INVALID_SLOT_INDEX (0) +#define _SSPINE_DEFAULT_CONTEXT_POOL_SIZE (4) +#define _SSPINE_DEFAULT_ATLAS_POOL_SIZE (64) +#define _SSPINE_DEFAULT_SKELETON_POOL_SIZE (64) +#define _SSPINE_DEFAULT_SKINSET_POOL_SIZE (64) +#define _SSPINE_DEFAULT_INSTANCE_POOL_SIZE (1024) +#define _SSPINE_DEFAULT_MAX_VERTICES (1<<16) +#define _SSPINE_DEFAULT_MAX_COMMANDS (1<<14) +#define _SSPINE_MAX_TRIGGERED_EVENTS (16) +#define _SSPINE_SLOT_SHIFT (16) +#define _SSPINE_MAX_POOL_SIZE (1<<_SSPINE_SLOT_SHIFT) +#define _SSPINE_SLOT_MASK (_SSPINE_MAX_POOL_SIZE-1) + +typedef struct { + float mvp[16]; +} _sspine_vsparams_t; + +typedef struct { + float pma; + uint8_t _pad[12]; +} _sspine_fsparams_t; + +typedef struct { + uint32_t id; + sspine_resource_state state; +} _sspine_slot_t; + +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _sspine_pool_t; + +typedef struct { + _sspine_slot_t slot; + sspine_atlas_overrides overrides; + spAtlas* sp_atlas; + int num_pages; +} _sspine_atlas_t; + +typedef struct { + _sspine_pool_t pool; + _sspine_atlas_t* items; +} _sspine_atlas_pool_t; + +typedef struct { + uint32_t id; + _sspine_atlas_t* ptr; +} _sspine_atlas_ref_t; + +typedef struct { + sg_image img; + sg_sampler smp; +} _sspine_image_sampler_pair_t; + +typedef struct { + _sspine_slot_t slot; + _sspine_atlas_ref_t atlas; + spSkeletonData* sp_skel_data; + spAnimationStateData* sp_anim_data; + struct { + int cap; + sspine_vec2* ptr; + } tform_buf; +} _sspine_skeleton_t; + +typedef struct { + _sspine_pool_t pool; + _sspine_skeleton_t* items; +} _sspine_skeleton_pool_t; + +typedef struct { + uint32_t id; + _sspine_skeleton_t* ptr; +} _sspine_skeleton_ref_t; + +typedef struct { + _sspine_slot_t slot; + _sspine_skeleton_ref_t skel; + spSkin* sp_skin; +} _sspine_skinset_t; + +typedef struct { + _sspine_pool_t pool; + _sspine_skinset_t* items; +} _sspine_skinset_pool_t; + +typedef struct { + uint32_t id; + _sspine_skinset_t* ptr; +} _sspine_skinset_ref_t; + +typedef struct { + _sspine_slot_t slot; + _sspine_atlas_ref_t atlas; + _sspine_skeleton_ref_t skel; + _sspine_skinset_ref_t skinset; + spSkeleton* sp_skel; + spAnimationState* sp_anim_state; + spSkeletonClipping* sp_clip; + int cur_triggered_event_index; + sspine_triggered_event_info triggered_events[_SSPINE_MAX_TRIGGERED_EVENTS]; +} _sspine_instance_t; + +typedef struct { + _sspine_pool_t pool; + _sspine_instance_t* items; +} _sspine_instance_pool_t; + +typedef struct { + sspine_vec2 pos; + sspine_vec2 uv; + uint32_t color; +} _sspine_vertex_t; + +typedef struct { + _sspine_vertex_t* ptr; + int index; +} _sspine_alloc_vertices_result_t; + +typedef struct { + uint32_t* ptr; + int index; +} _sspine_alloc_indices_result_t; + +typedef struct { + int layer; + sg_pipeline pip; + sg_image img; + sg_sampler smp; + float pma; // pma = 0.0: use texture color as is, pma = 1.0: multiply texture rgb by texture alpha in fragment shader + int base_element; + int num_elements; +} _sspine_command_t; + +typedef struct { + _sspine_slot_t slot; + float transform[16]; + struct { + int cap; + int next; + uint32_t rewind_frame_id; + _sspine_vertex_t* ptr; + } vertices; + struct { + int cap; + int next; + uint32_t rewind_frame_id; + uint32_t* ptr; + } indices; + struct { + int cap; + int next; + uint32_t rewind_frame_id; + _sspine_command_t* ptr; + } commands; + uint32_t update_frame_id; + sg_buffer vbuf; + sg_buffer ibuf; + struct { + sg_pipeline normal_additive; + sg_pipeline multiply; + } pip; + sg_bindings bind; +} _sspine_context_t; + +typedef struct { + _sspine_pool_t pool; + _sspine_context_t* items; +} _sspine_context_pool_t; + +typedef struct { + uint32_t init_cookie; + uint32_t frame_id; + sspine_desc desc; + sspine_context def_ctx_id; + sspine_context cur_ctx_id; + _sspine_context_t* cur_ctx; // may be 0! + sg_shader shd; + _sspine_context_pool_t context_pool; + _sspine_atlas_pool_t atlas_pool; + _sspine_skeleton_pool_t skeleton_pool; + _sspine_skinset_pool_t skinset_pool; + _sspine_instance_pool_t instance_pool; +} _sspine_t; +static _sspine_t _sspine; + +// dummy spine-c platform implementation functions +#if defined(__cplusplus) +extern "C" { +#endif +void _spAtlasPage_createTexture(spAtlasPage* self, const char* path) { + // nothing to do here + (void)self; (void)path; +} + +static void _sspine_delete_image_sampler_pair(const _sspine_image_sampler_pair_t* isp); +void _spAtlasPage_disposeTexture(spAtlasPage* self) { + _sspine_delete_image_sampler_pair((const _sspine_image_sampler_pair_t*) self->rendererObject); +} + +char* _spUtil_readFile(const char* path, int* length) { + (void)path; + *length = 0; + return 0; +} +#if defined(__cplusplus) +} // extern "C" +#endif + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SSPINE_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sspine_log_messages[] = { + _SSPINE_LOG_ITEMS +}; +#undef _SSPINE_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SSPINE_PANIC(code) _sspine_log(SSPINE_LOGITEM_ ##code, 0, __LINE__) +#define _SSPINE_ERROR(code) _sspine_log(SSPINE_LOGITEM_ ##code, 1, __LINE__) +#define _SSPINE_WARN(code) _sspine_log(SSPINE_LOGITEM_ ##code, 2, __LINE__) +#define _SSPINE_INFO(code) _sspine_log(SSPINE_LOGITEM_ ##code, 3, __LINE__) + +static void _sspine_log(sspine_log_item log_item, uint32_t log_level, uint32_t line_nr) { + if (_sspine.desc.logger.func) { + #if defined(SOKOL_DEBUG) + const char* filename = __FILE__; + const char* message = _sspine_log_messages[log_item]; + #else + const char* filename = 0; + const char* message = 0; + #endif + _sspine.desc.logger.func("sspine", log_level, log_item, message, line_nr, filename, _sspine.desc.logger.user_data); + } else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory +static void _sspine_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +/* Copy a string into a fixed size buffer with guaranteed zero- + termination. + + Return false if the string didn't fit into the buffer and had to be clamped. + + FIXME: Currently UTF-8 strings might become invalid if the string + is clamped, because the last zero-byte might be written into + the middle of a multi-byte sequence. +*/ +static bool _sspine_strcpy(const char* src, char* dst, int max_len) { + SOKOL_ASSERT(src && dst && (max_len > 0)); + char* const end = &(dst[max_len-1]); + char c = 0; + for (int i = 0; i < max_len; i++) { + c = *src; + if (c != 0) { + src++; + } + *dst++ = c; + } + // truncated? + if (c != 0) { + *end = 0; + return false; + } else { + return true; + } +} + +static sspine_string _sspine_string(const char* cstr) { + sspine_string res; + _sspine_clear(&res, sizeof(res)); + if (cstr) { + res.valid = true; + res.truncated = !_sspine_strcpy(cstr, res.cstr, sizeof(res.cstr)); + res.len = (uint8_t)strlen(res.cstr); + } + return res; +} + +static void* _sspine_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_sspine.desc.allocator.alloc_fn) { + ptr = _sspine.desc.allocator.alloc_fn(size, _sspine.desc.allocator.user_data); + } else { + ptr = malloc(size); + } + if (0 == ptr) { + _SSPINE_PANIC(MALLOC_FAILED); + } + return ptr; +} + +static void* _sspine_malloc_clear(size_t size) { + void* ptr = _sspine_malloc(size); + _sspine_clear(ptr, size); + return ptr; +} + +static void _sspine_free(void* ptr) { + if (_sspine.desc.allocator.free_fn) { + _sspine.desc.allocator.free_fn(ptr, _sspine.desc.allocator.user_data); + } else { + free(ptr); + } +} + +// ██████ ███████ ███████ ███████ +// ██ ██ ██ ██ ██ +// ██████ █████ █████ ███████ +// ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ███████ +// +// >>refs +static bool _sspine_atlas_ref_valid(const _sspine_atlas_ref_t* ref) { + return ref->ptr && (ref->ptr->slot.id == ref->id); +} + +static bool _sspine_skeleton_ref_valid(const _sspine_skeleton_ref_t* ref) { + return ref->ptr && (ref->ptr->slot.id == ref->id); +} + +static bool _sspine_skinset_ref_valid(const _sspine_skinset_ref_t* ref) { + return ref->ptr && (ref->ptr->slot.id == ref->id); +} + +static bool _sspine_skeleton_and_deps_valid(_sspine_skeleton_t* skeleton) { + return skeleton && _sspine_atlas_ref_valid(&skeleton->atlas); +} + +static bool _sspine_skinset_and_deps_valid(_sspine_skinset_t* skinset) { + return skinset && _sspine_skeleton_ref_valid(&skinset->skel); +} + +static bool _sspine_instance_and_deps_valid(_sspine_instance_t* instance) { + return instance && + _sspine_atlas_ref_valid(&instance->atlas) && + _sspine_skeleton_ref_valid(&instance->skel) && + ((instance->skinset.id == SSPINE_INVALID_ID) || _sspine_skinset_ref_valid(&instance->skinset)); +} + +static sspine_image _sspine_image(uint32_t atlas_id, int index) { + sspine_image img = { atlas_id, index }; + return img; +} + +static sspine_atlas_page _sspine_atlas_page(uint32_t atlas_id, int index) { + sspine_atlas_page page = { atlas_id, index }; + return page; +} + +static sspine_anim _sspine_anim(uint32_t skeleton_id, int index) { + sspine_anim anim = { skeleton_id, index }; + return anim; +} + +static sspine_bone _sspine_bone(uint32_t skeleton_id, int index) { + sspine_bone bone = { skeleton_id, index }; + return bone; +} + +static sspine_slot _sspine_slot(uint32_t skeleton_id, int index) { + sspine_slot slot = { skeleton_id, index }; + return slot; +} + +static sspine_event _sspine_event(uint32_t skeleton_id, int index) { + sspine_event event = { skeleton_id, index }; + return event; +} + +static sspine_iktarget _sspine_iktarget(uint32_t skeleton_id, int index) { + sspine_iktarget iktarget = { skeleton_id, index }; + return iktarget; +} + +static sspine_skin _sspine_skin(uint32_t skeleton_id, int index) { + sspine_skin skin = { skeleton_id, index }; + return skin; +} + +// ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ +// +// >>pool +static void _sspine_init_pool(_sspine_pool_t* pool, int num) { + SOKOL_ASSERT(pool && (num >= 1)); + // slot 0 is reserved for the 'invalid id', so bump the pool size by 1 + pool->size = num + 1; + pool->queue_top = 0; + // generation counters indexable by pool slot index, slot 0 is reserved + size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size; + pool->gen_ctrs = (uint32_t*) _sspine_malloc_clear(gen_ctrs_size); + // it's not a bug to only reserve 'num' here + pool->free_queue = (int*) _sspine_malloc_clear(sizeof(int) * (size_t)num); + // never allocate the zero-th pool item since the invalid id is 0 + for (int i = pool->size-1; i >= 1; i--) { + pool->free_queue[pool->queue_top++] = i; + } +} + +static void _sspine_discard_pool(_sspine_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + _sspine_free(pool->free_queue); + pool->free_queue = 0; + SOKOL_ASSERT(pool->gen_ctrs); + _sspine_free(pool->gen_ctrs); + pool->gen_ctrs = 0; + pool->size = 0; + pool->queue_top = 0; +} + +static int _sspine_pool_alloc_index(_sspine_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } else { + // pool exhausted + return _SSPINE_INVALID_SLOT_INDEX; + } +} + +static void _sspine_pool_free_index(_sspine_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SSPINE_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_ASSERT(pool->queue_top < pool->size); + #ifdef SOKOL_DEBUG + // debug check against double-free + for (int i = 0; i < pool->queue_top; i++) { + SOKOL_ASSERT(pool->free_queue[i] != slot_index); + } + #endif + pool->free_queue[pool->queue_top++] = slot_index; + SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); +} + +/* initialize a pool slot: + - bump the slot's generation counter + - create a resource id from the generation counter and slot index + - set the slot's id to this id + - set the slot's state to ALLOC + - return the handle id +*/ +static uint32_t _sspine_slot_init(_sspine_pool_t* pool, _sspine_slot_t* slot, int slot_index) { + /* FIXME: add handling for an overflowing generation counter, + for now, just overflow (another option is to disable + the slot) + */ + SOKOL_ASSERT(pool && pool->gen_ctrs); + SOKOL_ASSERT((slot_index > _SSPINE_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT((slot->state == SSPINE_RESOURCESTATE_INITIAL) && (slot->id == SSPINE_INVALID_ID)); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + slot->id = (ctr<<_SSPINE_SLOT_SHIFT)|(slot_index & _SSPINE_SLOT_MASK); + slot->state = SSPINE_RESOURCESTATE_ALLOC; + return slot->id; +} + +// extract slot index from id +static int _sspine_slot_index(uint32_t id) { + int slot_index = (int) (id & _SSPINE_SLOT_MASK); + SOKOL_ASSERT(_SSPINE_INVALID_SLOT_INDEX != slot_index); + return slot_index; +} + +static void _sspine_init_item_pool(_sspine_pool_t* pool, int pool_size, void** items_ptr, size_t item_size_bytes) { + // NOTE: the pools will have an additional item, since slot 0 is reserved + SOKOL_ASSERT(pool && (pool->size == 0)); + SOKOL_ASSERT((pool_size > 0) && (pool_size < _SSPINE_MAX_POOL_SIZE)); + SOKOL_ASSERT(items_ptr && (*items_ptr == 0)); + SOKOL_ASSERT(item_size_bytes > 0); + _sspine_init_pool(pool, pool_size); + const size_t pool_size_bytes = item_size_bytes * (size_t)pool->size; + *items_ptr = _sspine_malloc_clear(pool_size_bytes); +} + +static void _sspine_discard_item_pool(_sspine_pool_t* pool, void** items_ptr) { + SOKOL_ASSERT(pool && (pool->size != 0)); + SOKOL_ASSERT(items_ptr && (*items_ptr != 0)); + _sspine_free(*items_ptr); *items_ptr = 0; + _sspine_discard_pool(pool); +} + +// ██████ ██████ ███ ██ ████████ ███████ ██ ██ ████████ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ █████ ███ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ████ ██ ███████ ██ ██ ██ +// +// >>context +static void _sspine_setup_context_pool(int pool_size) { + _sspine_context_pool_t* p = &_sspine.context_pool; + _sspine_init_item_pool(&p->pool, pool_size, (void**)&p->items, sizeof(_sspine_context_t)); +} + +static void _sspine_discard_context_pool(void) { + _sspine_context_pool_t* p = &_sspine.context_pool; + _sspine_discard_item_pool(&p->pool, (void**)&p->items); +} + +static sspine_context _sspine_make_context_handle(uint32_t id) { + sspine_context handle = { id }; + return handle; +} + +static _sspine_context_t* _sspine_context_at(uint32_t id) { + SOKOL_ASSERT(SSPINE_INVALID_ID != id); + const _sspine_context_pool_t* p = &_sspine.context_pool; + int slot_index = _sspine_slot_index(id); + SOKOL_ASSERT((slot_index > _SSPINE_INVALID_SLOT_INDEX) && (slot_index < p->pool.size)); + return &p->items[slot_index]; +} + +static _sspine_context_t* _sspine_lookup_context(uint32_t id) { + if (SSPINE_INVALID_ID != id) { + _sspine_context_t* ctx = _sspine_context_at(id); + if (ctx->slot.id == id) { + return ctx; + } + } + return 0; +} + +static sspine_context _sspine_alloc_context(void) { + _sspine_context_pool_t* p = &_sspine.context_pool; + int slot_index = _sspine_pool_alloc_index(&p->pool); + if (_SSPINE_INVALID_SLOT_INDEX != slot_index) { + uint32_t id = _sspine_slot_init(&p->pool, &p->items[slot_index].slot, slot_index); + return _sspine_make_context_handle(id); + } else { + // pool exhausted + return _sspine_make_context_handle(SSPINE_INVALID_ID); + } +} + +static sspine_resource_state _sspine_init_context(_sspine_context_t* ctx, const sspine_context_desc* desc) { + SOKOL_ASSERT(ctx && (ctx->slot.state == SSPINE_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + + // setup vertex, index and command storage + ctx->vertices.cap = desc->max_vertices; + ctx->indices.cap = ctx->vertices.cap * 3; + ctx->commands.cap = desc->max_commands; + + const size_t vbuf_size = (size_t)ctx->vertices.cap * sizeof(_sspine_vertex_t); + const size_t ibuf_size = (size_t)ctx->indices.cap * sizeof(uint32_t); + const size_t cbuf_size = (size_t)ctx->commands.cap * sizeof(_sspine_command_t); + + ctx->vertices.ptr = (_sspine_vertex_t*) _sspine_malloc(vbuf_size); + ctx->indices.ptr = (uint32_t*) _sspine_malloc(ibuf_size); + ctx->commands.ptr = (_sspine_command_t*) _sspine_malloc(cbuf_size); + + sg_buffer_desc vbuf_desc; + _sspine_clear(&vbuf_desc, sizeof(vbuf_desc)); + vbuf_desc.type = SG_BUFFERTYPE_VERTEXBUFFER; + vbuf_desc.usage = SG_USAGE_STREAM; + vbuf_desc.size = vbuf_size; + vbuf_desc.label = "sspine-vbuf"; + ctx->vbuf = sg_make_buffer(&vbuf_desc); + ctx->bind.vertex_buffers[0] = ctx->vbuf; + + sg_buffer_desc ibuf_desc; + _sspine_clear(&ibuf_desc, sizeof(ibuf_desc)); + ibuf_desc.type = SG_BUFFERTYPE_INDEXBUFFER; + ibuf_desc.usage = SG_USAGE_STREAM; + ibuf_desc.size = ibuf_size; + ibuf_desc.label = "sspine-ibuf"; + ctx->ibuf = sg_make_buffer(&ibuf_desc); + ctx->bind.index_buffer = ctx->ibuf; + + // for blend modes, see: https://wiki.libsdl.org/SDL_BlendMode + // + // NOTE: we're configuring the blend mode for premultiplied alpha, + // and then do the premultiplication in the fragment shader + // if needed + sg_pipeline_desc pip_desc; + _sspine_clear(&pip_desc, sizeof(pip_desc)); + pip_desc.shader = _sspine.shd; + pip_desc.layout.buffers[0].stride = sizeof(_sspine_vertex_t); + pip_desc.layout.attrs[0].format = SG_VERTEXFORMAT_FLOAT2; + pip_desc.layout.attrs[1].format = SG_VERTEXFORMAT_FLOAT2; + pip_desc.layout.attrs[2].format = SG_VERTEXFORMAT_UBYTE4N; + pip_desc.index_type = SG_INDEXTYPE_UINT32; + pip_desc.sample_count = desc->sample_count; + pip_desc.depth.pixel_format = desc->depth_format; + pip_desc.colors[0].pixel_format = desc->color_format; + pip_desc.colors[0].write_mask = desc->color_write_mask; + pip_desc.colors[0].blend.enabled = true; + pip_desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_ONE; + pip_desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + pip_desc.colors[0].blend.src_factor_alpha = SG_BLENDFACTOR_ONE; + pip_desc.colors[0].blend.dst_factor_alpha = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + pip_desc.label = "sspine-pip-normal/additive"; + ctx->pip.normal_additive = sg_make_pipeline(&pip_desc); + + pip_desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_ZERO; + pip_desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_SRC_COLOR; + pip_desc.colors[0].blend.src_factor_alpha = SG_BLENDFACTOR_ZERO; + pip_desc.colors[0].blend.dst_factor_alpha = SG_BLENDFACTOR_ONE; + pip_desc.label = "sspine-pip-multiply"; + ctx->pip.multiply = sg_make_pipeline(&pip_desc); + + return SSPINE_RESOURCESTATE_VALID; +} + +static void _sspine_deinit_context(_sspine_context_t* ctx) { + // NOTE: it's ok to call sg_destroy functions with invalid handles + sg_destroy_pipeline(ctx->pip.normal_additive); + sg_destroy_pipeline(ctx->pip.multiply); + sg_destroy_buffer(ctx->ibuf); + sg_destroy_buffer(ctx->vbuf); + if (ctx->commands.ptr) { + _sspine_free(ctx->commands.ptr); + ctx->commands.ptr = 0; + } + if (ctx->indices.ptr) { + _sspine_free(ctx->indices.ptr); + ctx->indices.ptr = 0; + } + if (ctx->vertices.ptr) { + _sspine_free(ctx->vertices.ptr); + ctx->vertices.ptr = 0; + } +} + +static void _sspine_destroy_context(sspine_context ctx_id) { + _sspine_context_t* ctx = _sspine_lookup_context(ctx_id.id); + if (ctx) { + _sspine_deinit_context(ctx); + _sspine_context_pool_t* p = &_sspine.context_pool; + _sspine_clear(ctx, sizeof(_sspine_context_t)); + _sspine_pool_free_index(&p->pool, _sspine_slot_index(ctx_id.id)); + } +} + +static void _sspine_destroy_all_contexts(void) { + _sspine_context_pool_t* p = &_sspine.context_pool; + for (int i = 0; i < p->pool.size; i++) { + _sspine_context_t* ctx = &p->items[i]; + _sspine_destroy_context(_sspine_make_context_handle(ctx->slot.id)); + } +} + +static sspine_context_desc _sspine_context_desc_defaults(const sspine_context_desc* desc) { + sspine_context_desc res = *desc; + res.max_vertices = _sspine_def(desc->max_vertices, _SSPINE_DEFAULT_MAX_VERTICES); + res.max_commands = _sspine_def(desc->max_commands, _SSPINE_DEFAULT_MAX_COMMANDS); + return res; +} + +static bool _sspine_is_default_context(sspine_context ctx_id) { + return ctx_id.id == 0x00010001; +} + +// █████ ████████ ██ █████ ███████ +// ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ███████ ███████ +// ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ███████ ██ ██ ███████ +// +// >>atlas +static void _sspine_setup_atlas_pool(int pool_size) { + _sspine_atlas_pool_t* p = &_sspine.atlas_pool; + _sspine_init_item_pool(&p->pool, pool_size, (void**)&p->items, sizeof(_sspine_atlas_t)); +} + +static void _sspine_discard_atlas_pool(void) { + _sspine_atlas_pool_t* p = &_sspine.atlas_pool; + _sspine_discard_item_pool(&p->pool, (void**)&p->items); +} + +static sspine_atlas _sspine_make_atlas_handle(uint32_t id) { + sspine_atlas handle = { id }; + return handle; +} + +static _sspine_atlas_t* _sspine_atlas_at(uint32_t id) { + SOKOL_ASSERT(SSPINE_INVALID_ID != id); + const _sspine_atlas_pool_t* p = &_sspine.atlas_pool; + int slot_index = _sspine_slot_index(id); + SOKOL_ASSERT((slot_index > _SSPINE_INVALID_SLOT_INDEX) && (slot_index < p->pool.size)); + return &p->items[slot_index]; +} + +static _sspine_atlas_t* _sspine_lookup_atlas(uint32_t id) { + if (SSPINE_INVALID_ID != id) { + _sspine_atlas_t* atlas = _sspine_atlas_at(id); + if (atlas->slot.id == id) { + return atlas; + } + } + return 0; +} + +static sspine_atlas _sspine_alloc_atlas(void) { + _sspine_atlas_pool_t* p = &_sspine.atlas_pool; + int slot_index = _sspine_pool_alloc_index(&p->pool); + if (_SSPINE_INVALID_SLOT_INDEX != slot_index) { + uint32_t id = _sspine_slot_init(&p->pool, &p->items[slot_index].slot, slot_index); + return _sspine_make_atlas_handle(id); + } else { + // pool exhausted + return _sspine_make_atlas_handle(SSPINE_INVALID_ID); + } +} + +static const _sspine_image_sampler_pair_t* _sspine_new_image_sampler_pair(sg_image img, sg_sampler smp) { + _sspine_image_sampler_pair_t* isp = (_sspine_image_sampler_pair_t*) _sspine_malloc_clear(sizeof(_sspine_image_sampler_pair_t)); + SOKOL_ASSERT(isp); + isp->img = img; + isp->smp = smp; + return isp; +} + +static void _sspine_delete_image_sampler_pair(const _sspine_image_sampler_pair_t* isp) { + if (isp) { + sg_destroy_sampler(isp->smp); + sg_destroy_image(isp->img); + _sspine_free((void*)isp); + } +} + +static sg_image _sspine_image_from_renderer_object(void* renderer_object) { + SOKOL_ASSERT(renderer_object); + const _sspine_image_sampler_pair_t* isp = (const _sspine_image_sampler_pair_t*)renderer_object; + return isp->img; +} + +static sg_sampler _sspine_sampler_from_renderer_object(void* renderer_object) { + SOKOL_ASSERT(renderer_object); + const _sspine_image_sampler_pair_t* isp = (const _sspine_image_sampler_pair_t*)renderer_object; + return isp->smp; +} + +static sspine_resource_state _sspine_init_atlas(_sspine_atlas_t* atlas, const sspine_atlas_desc* desc) { + SOKOL_ASSERT(atlas && (atlas->slot.state == SSPINE_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + SOKOL_ASSERT(atlas->sp_atlas == 0); + + if ((0 == desc->data.ptr) || (0 == desc->data.size)) { + _SSPINE_ERROR(ATLAS_DESC_NO_DATA); + return SSPINE_RESOURCESTATE_FAILED; + } + atlas->overrides = desc->override; + + // NOTE: Spine doesn't detect when invalid or corrupt data is passed here, + // not much we can do about this... + atlas->sp_atlas = spAtlas_create((const char*)desc->data.ptr, (int)desc->data.size, "", 0); + if (0 == atlas->sp_atlas) { + _SSPINE_ERROR(SPINE_ATLAS_CREATION_FAILED); + return SSPINE_RESOURCESTATE_FAILED; + } + + // allocate a sokol-gfx image and sampler object for each page, but the actual + // initialization needs to be delegated to the application + for (spAtlasPage* page = atlas->sp_atlas->pages; page != 0; page = page->next) { + atlas->num_pages++; + const sg_image img = sg_alloc_image(); + if (sg_query_image_state(img) != SG_RESOURCESTATE_ALLOC) { + _SSPINE_ERROR(SG_ALLOC_IMAGE_FAILED); + return SSPINE_RESOURCESTATE_FAILED; + } + const sg_sampler smp = sg_alloc_sampler(); + if (sg_query_sampler_state(smp) != SG_RESOURCESTATE_ALLOC) { + _SSPINE_ERROR(SG_ALLOC_SAMPLER_FAILED); + return SSPINE_RESOURCESTATE_FAILED; + } + + // need to put the image and sampler handle into a heap-alloc unfortunately, + // because a void* isn't big enough to stash two 32-bit handles into + // it directly on platforms with 32-bit pointers (like wasm) + page->rendererObject = (void*) _sspine_new_image_sampler_pair(img, smp); + + if (desc->override.premul_alpha_enabled) { + // NOTE: -1 is spine-c convention for 'true' + page->pma = -1; + } else if (desc->override.premul_alpha_disabled) { + page->pma = 0; + } + } + return SSPINE_RESOURCESTATE_VALID; +} + +static void _sspine_deinit_atlas(_sspine_atlas_t* atlas) { + if (atlas->sp_atlas) { + spAtlas_dispose(atlas->sp_atlas); + atlas->sp_atlas = 0; + } +} + +static void _sspine_destroy_atlas(sspine_atlas atlas_id) { + _sspine_atlas_t* atlas = _sspine_lookup_atlas(atlas_id.id); + if (atlas) { + _sspine_deinit_atlas(atlas); + _sspine_atlas_pool_t* p = &_sspine.atlas_pool; + _sspine_clear(atlas, sizeof(_sspine_atlas_t)); + _sspine_pool_free_index(&p->pool, _sspine_slot_index(atlas_id.id)); + } +} + +static void _sspine_destroy_all_atlases(void) { + _sspine_atlas_pool_t* p = &_sspine.atlas_pool; + for (int i = 0; i < p->pool.size; i++) { + _sspine_atlas_t* atlas = &p->items[i]; + _sspine_destroy_atlas(_sspine_make_atlas_handle(atlas->slot.id)); + } +} + +static sspine_atlas_desc _sspine_atlas_desc_defaults(const sspine_atlas_desc* desc) { + sspine_atlas_desc res = *desc; + return res; +} + +static spAtlasPage* _sspine_lookup_atlas_page(uint32_t atlas_id, int page_index) { + _sspine_atlas_t* atlas = _sspine_lookup_atlas(atlas_id); + if (atlas) { + if ((page_index >= 0) && (page_index < atlas->num_pages)) { + int i = 0; + for (spAtlasPage* page = atlas->sp_atlas->pages; page != 0; page = page->next, i++) { + if (i == page_index) { + return page; + } + } + } + } + return 0; +} + +// ███████ ██ ██ ███████ ██ ███████ ████████ ██████ ███ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ███████ █████ █████ ██ █████ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ███████ ███████ ███████ ██ ██████ ██ ████ +// +// >>skeleton +static void _sspine_setup_skeleton_pool(int pool_size) { + _sspine_skeleton_pool_t* p = &_sspine.skeleton_pool; + _sspine_init_item_pool(&p->pool, pool_size, (void**)&p->items, sizeof(_sspine_skeleton_t)); +} + +static void _sspine_discard_skeleton_pool(void) { + _sspine_skeleton_pool_t* p = &_sspine.skeleton_pool; + _sspine_discard_item_pool(&p->pool, (void**)&p->items); +} + +static sspine_skeleton _sspine_make_skeleton_handle(uint32_t id) { + sspine_skeleton handle = { id }; + return handle; +} + +static _sspine_skeleton_t* _sspine_skeleton_at(uint32_t id) { + SOKOL_ASSERT(SSPINE_INVALID_ID != id); + const _sspine_skeleton_pool_t* p = &_sspine.skeleton_pool; + int slot_index = _sspine_slot_index(id); + SOKOL_ASSERT((slot_index > _SSPINE_INVALID_SLOT_INDEX) && (slot_index < p->pool.size)); + return &p->items[slot_index]; +} + +static _sspine_skeleton_t* _sspine_lookup_skeleton(uint32_t id) { + if (SSPINE_INVALID_ID != id) { + _sspine_skeleton_t* skeleton = _sspine_skeleton_at(id); + if (skeleton->slot.id == id) { + return skeleton; + } + } + return 0; +} + +static sspine_skeleton _sspine_alloc_skeleton(void) { + _sspine_skeleton_pool_t* p = &_sspine.skeleton_pool; + int slot_index = _sspine_pool_alloc_index(&p->pool); + if (_SSPINE_INVALID_SLOT_INDEX != slot_index) { + uint32_t id = _sspine_slot_init(&p->pool, &p->items[slot_index].slot, slot_index); + return _sspine_make_skeleton_handle(id); + } else { + // pool exhausted + return _sspine_make_skeleton_handle(SSPINE_INVALID_ID); + } +} + +static sspine_resource_state _sspine_init_skeleton(_sspine_skeleton_t* skeleton, const sspine_skeleton_desc* desc) { + SOKOL_ASSERT(skeleton && (skeleton->slot.state == SSPINE_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + + if ((0 == desc->json_data) && ((0 == desc->binary_data.ptr) || (0 == desc->binary_data.size))) { + _SSPINE_ERROR(SKELETON_DESC_NO_DATA); + return SSPINE_RESOURCESTATE_FAILED; + } + if (desc->atlas.id == SSPINE_INVALID_ID) { + _SSPINE_ERROR(SKELETON_DESC_NO_ATLAS); + return SSPINE_RESOURCESTATE_FAILED; + } + + skeleton->atlas.id = desc->atlas.id; + skeleton->atlas.ptr = _sspine_lookup_atlas(skeleton->atlas.id); + if (!_sspine_atlas_ref_valid(&skeleton->atlas)) { + _SSPINE_ERROR(SKELETON_ATLAS_NOT_VALID); + return SSPINE_RESOURCESTATE_FAILED; + } + _sspine_atlas_t* atlas = skeleton->atlas.ptr; + if (SSPINE_RESOURCESTATE_VALID != atlas->slot.state) { + _SSPINE_ERROR(SKELETON_ATLAS_NOT_VALID); + return SSPINE_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT(atlas->sp_atlas); + + if (desc->json_data) { + spSkeletonJson* skel_json = spSkeletonJson_create(atlas->sp_atlas); + SOKOL_ASSERT(skel_json); + skel_json->scale = desc->prescale; + skeleton->sp_skel_data = spSkeletonJson_readSkeletonData(skel_json, desc->json_data); + spSkeletonJson_dispose(skel_json); skel_json = 0; + if (0 == skeleton->sp_skel_data) { + _SSPINE_ERROR(CREATE_SKELETON_DATA_FROM_JSON_FAILED); + return SSPINE_RESOURCESTATE_FAILED; + } + } else { + spSkeletonBinary* skel_bin = spSkeletonBinary_create(atlas->sp_atlas); + SOKOL_ASSERT(skel_bin); + skel_bin->scale = desc->prescale; + skeleton->sp_skel_data = spSkeletonBinary_readSkeletonData(skel_bin, (const unsigned char*)desc->binary_data.ptr, (int)desc->binary_data.size); + spSkeletonBinary_dispose(skel_bin); skel_bin = 0; + if (0 == skeleton->sp_skel_data) { + _SSPINE_ERROR(CREATE_SKELETON_DATA_FROM_BINARY_FAILED); + return SSPINE_RESOURCESTATE_FAILED; + } + } + SOKOL_ASSERT(skeleton->sp_skel_data); + + skeleton->sp_anim_data = spAnimationStateData_create(skeleton->sp_skel_data); + SOKOL_ASSERT(skeleton->sp_anim_data); + skeleton->sp_anim_data->defaultMix = desc->anim_default_mix; + + // get the max number of vertices in any mesh attachment + int max_vertex_count = 4; // number of vertices in a 'region attachment' (a 2-triangle quad) + const spSkeletonData* sp_skel_data = skeleton->sp_skel_data; + for (int skinIndex = 0; skinIndex < sp_skel_data->skinsCount; skinIndex++) { + const spSkin* sp_skin = sp_skel_data->skins[skinIndex]; + const spSkinEntry* skin_entry = spSkin_getAttachments(sp_skin); + if (skin_entry) do { + if (skin_entry->attachment) { + if (skin_entry->attachment->type == SP_ATTACHMENT_MESH) { + const spMeshAttachment* mesh_attachment = (spMeshAttachment*)skin_entry->attachment; + // worldVerticesLength is number of floats + SOKOL_ASSERT((mesh_attachment->super.worldVerticesLength & 1) == 0); + const int num_vertices = mesh_attachment->super.worldVerticesLength / 2; + if (num_vertices > max_vertex_count) { + max_vertex_count = num_vertices; + } + } + } + } while ((skin_entry = skin_entry->next) != 0); + } + + // allocate a shared vertex transform buffer (big enough to hold vertices for biggest mesh attachment) + skeleton->tform_buf.cap = max_vertex_count; + skeleton->tform_buf.ptr = (sspine_vec2*) _sspine_malloc((size_t)skeleton->tform_buf.cap * sizeof(sspine_vec2)); + + return SSPINE_RESOURCESTATE_VALID; +} + +static void _sspine_deinit_skeleton(_sspine_skeleton_t* skeleton) { + if (skeleton->tform_buf.ptr) { + _sspine_free(skeleton->tform_buf.ptr); + skeleton->tform_buf.ptr = 0; + } + if (skeleton->sp_anim_data) { + spAnimationStateData_dispose(skeleton->sp_anim_data); + skeleton->sp_anim_data = 0; + } + if (skeleton->sp_skel_data) { + spSkeletonData_dispose(skeleton->sp_skel_data); + skeleton->sp_skel_data = 0; + } +} + +static void _sspine_destroy_skeleton(sspine_skeleton skeleton_id) { + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (skeleton) { + _sspine_deinit_skeleton(skeleton); + _sspine_skeleton_pool_t* p = &_sspine.skeleton_pool; + _sspine_clear(skeleton, sizeof(_sspine_skeleton_t)); + _sspine_pool_free_index(&p->pool, _sspine_slot_index(skeleton_id.id)); + } +} + +static void _sspine_destroy_all_skeletons(void) { + _sspine_skeleton_pool_t* p = &_sspine.skeleton_pool; + for (int i = 0; i < p->pool.size; i++) { + _sspine_skeleton_t* skeleton = &p->items[i]; + _sspine_destroy_skeleton(_sspine_make_skeleton_handle(skeleton->slot.id)); + } +} + +static sspine_skeleton_desc _sspine_skeleton_desc_defaults(const sspine_skeleton_desc* desc) { + sspine_skeleton_desc res = *desc; + res.prescale = _sspine_def(desc->prescale, 1.0f); + res.anim_default_mix = _sspine_def(desc->anim_default_mix, 0.2f); + return res; +} + +static spBoneData* _sspine_lookup_bone_data(uint32_t skeleton_id, int bone_index) { + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data && skeleton->sp_skel_data->bones); + if ((bone_index >= 0) && (bone_index <= skeleton->sp_skel_data->bonesCount)) { + return skeleton->sp_skel_data->bones[bone_index]; + } + } + return 0; +} + +static spSlotData* _sspine_lookup_slot_data(uint32_t skeleton_id, int slot_index) { + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data && skeleton->sp_skel_data->slots); + if ((slot_index >= 0) && (slot_index <= skeleton->sp_skel_data->slotsCount)) { + return skeleton->sp_skel_data->slots[slot_index]; + } + } + return 0; +} + +static spEventData* _sspine_lookup_event_data(uint32_t skeleton_id, int event_index) { + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data && skeleton->sp_skel_data->events); + if ((event_index >= 0) && (event_index < skeleton->sp_skel_data->eventsCount)) { + return skeleton->sp_skel_data->events[event_index]; + } + } + return 0; +} + +static spIkConstraintData* _sspine_lookup_ikconstraint_data(uint32_t skeleton_id, int iktarget_index) { + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data && skeleton->sp_skel_data->ikConstraints); + if ((iktarget_index >= 0) && (iktarget_index < skeleton->sp_skel_data->ikConstraintsCount)) { + return skeleton->sp_skel_data->ikConstraints[iktarget_index]; + } + } + return 0; +} + +static spSkin* _sspine_lookup_skin(uint32_t skeleton_id, int skin_index) { + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data && skeleton->sp_skel_data->skins); + if ((skin_index >= 0) && (skin_index < skeleton->sp_skel_data->skinsCount)) { + return skeleton->sp_skel_data->skins[skin_index]; + } + } + return 0; +} + +// ███████ ██ ██ ██ ███ ██ ███████ ███████ ████████ +// ██ ██ ██ ██ ████ ██ ██ ██ ██ +// ███████ █████ ██ ██ ██ ██ ███████ █████ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██ ████ ███████ ███████ ██ +// +// >>skinset +static void _sspine_setup_skinset_pool(int pool_size) { + _sspine_skinset_pool_t* p = &_sspine.skinset_pool; + _sspine_init_item_pool(&p->pool, pool_size, (void**)&p->items, sizeof(_sspine_skinset_t)); +} + +static void _sspine_discard_skinset_pool(void) { + _sspine_skinset_pool_t* p = &_sspine.skinset_pool; + _sspine_discard_item_pool(&p->pool, (void**)&p->items); +} + +static sspine_skinset _sspine_make_skinset_handle(uint32_t id) { + sspine_skinset handle = { id }; + return handle; +} + +static _sspine_skinset_t* _sspine_skinset_at(uint32_t id) { + SOKOL_ASSERT(SSPINE_INVALID_ID != id); + const _sspine_skinset_pool_t* p = &_sspine.skinset_pool; + int slot_index = _sspine_slot_index(id); + SOKOL_ASSERT((slot_index > _SSPINE_INVALID_SLOT_INDEX) && (slot_index < p->pool.size)); + return &p->items[slot_index]; +} + +static _sspine_skinset_t* _sspine_lookup_skinset(uint32_t id) { + if (SSPINE_INVALID_ID != id) { + _sspine_skinset_t* skinset = _sspine_skinset_at(id); + if (skinset->slot.id == id) { + return skinset; + } + } + return 0; +} + +static sspine_skinset _sspine_alloc_skinset(void) { + _sspine_skinset_pool_t* p = &_sspine.skinset_pool; + int slot_index = _sspine_pool_alloc_index(&p->pool); + if (_SSPINE_INVALID_SLOT_INDEX != slot_index) { + uint32_t id = _sspine_slot_init(&p->pool, &p->items[slot_index].slot, slot_index); + return _sspine_make_skinset_handle(id); + } else { + // pool exhausted + return _sspine_make_skinset_handle(SSPINE_INVALID_ID); + } +} + +static sspine_resource_state _sspine_init_skinset(_sspine_skinset_t* skinset, const sspine_skinset_desc* desc) { + SOKOL_ASSERT(skinset && (skinset->slot.state == SSPINE_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + + if (desc->skeleton.id == SSPINE_INVALID_ID) { + _SSPINE_ERROR(SKINSET_DESC_NO_SKELETON); + return SSPINE_RESOURCESTATE_FAILED; + } + skinset->skel.id = desc->skeleton.id; + skinset->skel.ptr = _sspine_lookup_skeleton(desc->skeleton.id); + if (!_sspine_skeleton_ref_valid(&skinset->skel)) { + _SSPINE_ERROR(SKINSET_SKELETON_NOT_VALID); + return SSPINE_RESOURCESTATE_FAILED; + } + _sspine_skeleton_t* skel = skinset->skel.ptr; + if (SSPINE_RESOURCESTATE_VALID != skel->slot.state) { + _SSPINE_ERROR(SKINSET_SKELETON_NOT_VALID); + return SSPINE_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT(skel->sp_skel_data); + skinset->sp_skin = spSkin_create("skinset"); + for (int i = 0; i < SSPINE_MAX_SKINSET_SKINS; i++) { + if (desc->skins[i].skeleton_id != SSPINE_INVALID_ID) { + spSkin* skin = _sspine_lookup_skin(desc->skins[i].skeleton_id, desc->skins[i].index); + if (0 == skin) { + _SSPINE_ERROR(SKINSET_INVALID_SKIN_HANDLE); + return SSPINE_RESOURCESTATE_FAILED; + } + spSkin_addSkin(skinset->sp_skin, skin); + } + } + return SSPINE_RESOURCESTATE_VALID; +} + +static void _sspine_deinit_skinset(_sspine_skinset_t* skinset) { + if (skinset->sp_skin) { + spSkin_clear(skinset->sp_skin); + spSkin_dispose(skinset->sp_skin); + skinset->sp_skin = 0; + } +} + +static void _sspine_destroy_skinset(sspine_skinset skinset_id) { + _sspine_skinset_t* skinset = _sspine_lookup_skinset(skinset_id.id); + if (skinset) { + _sspine_deinit_skinset(skinset); + _sspine_skinset_pool_t* p = &_sspine.skinset_pool; + _sspine_clear(skinset, sizeof(_sspine_skinset_t)); + _sspine_pool_free_index(&p->pool, _sspine_slot_index(skinset_id.id)); + } +} + +static void _sspine_destroy_all_skinsets(void) { + _sspine_skinset_pool_t* p = &_sspine.skinset_pool; + for (int i = 0; i < p->pool.size; i++) { + _sspine_skinset_t* skinset = &p->items[i]; + _sspine_destroy_skinset(_sspine_make_skinset_handle(skinset->slot.id)); + } +} + +static sspine_skinset_desc _sspine_skinset_desc_defaults(const sspine_skinset_desc* desc) { + sspine_skinset_desc res = *desc; + return res; +} + +// ██ ███ ██ ███████ ████████ █████ ███ ██ ██████ ███████ +// ██ ████ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ ██ ██ ███████ ██ ███████ ██ ██ ██ ██ █████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ████ ███████ ██ ██ ██ ██ ████ ██████ ███████ +// +// >>instance +static void _sspine_setup_instance_pool(int pool_size) { + _sspine_instance_pool_t* p = &_sspine.instance_pool; + _sspine_init_item_pool(&p->pool, pool_size, (void**)&p->items, sizeof(_sspine_instance_t)); +} + +static void _sspine_discard_instance_pool(void) { + _sspine_instance_pool_t* p = &_sspine.instance_pool; + _sspine_discard_item_pool(&p->pool, (void**)&p->items); +} + +static sspine_instance _sspine_make_instance_handle(uint32_t id) { + sspine_instance handle = { id }; + return handle; +} + +static _sspine_instance_t* _sspine_instance_at(uint32_t id) { + SOKOL_ASSERT(SSPINE_INVALID_ID != id); + const _sspine_instance_pool_t* p = &_sspine.instance_pool; + int slot_index = _sspine_slot_index(id); + SOKOL_ASSERT((slot_index > _SSPINE_INVALID_SLOT_INDEX) && (slot_index < p->pool.size)); + return &p->items[slot_index]; +} + +static _sspine_instance_t* _sspine_lookup_instance(uint32_t id) { + if (SSPINE_INVALID_ID != id) { + _sspine_instance_t* instance = _sspine_instance_at(id); + if (instance->slot.id == id) { + return instance; + } + } + return 0; +} + +static sspine_instance _sspine_alloc_instance(void) { + _sspine_instance_pool_t* p = &_sspine.instance_pool; + sspine_instance res; + int slot_index = _sspine_pool_alloc_index(&p->pool); + if (_SSPINE_INVALID_SLOT_INDEX != slot_index) { + uint32_t id = _sspine_slot_init(&p->pool, &p->items[slot_index].slot, slot_index); + res = _sspine_make_instance_handle(id); + } else { + // pool exhausted + res = _sspine_make_instance_handle(SSPINE_INVALID_ID); + } + return res; +} + +static void _sspine_rewind_triggered_events(_sspine_instance_t* instance) { + instance->cur_triggered_event_index = 0; + _sspine_clear(instance->triggered_events, sizeof(instance->triggered_events)); +} + +static sspine_triggered_event_info* _sspine_next_triggered_event_info(_sspine_instance_t* instance) { + if (instance->cur_triggered_event_index < _SSPINE_MAX_TRIGGERED_EVENTS) { + return &instance->triggered_events[instance->cur_triggered_event_index++]; + } else { + return 0; + } +} + +static void _sspine_event_listener(spAnimationState* sp_anim_state, spEventType sp_event_type, spTrackEntry* sp_track_entry, spEvent* sp_event) { + if (sp_event_type == SP_ANIMATION_EVENT) { + SOKOL_ASSERT(sp_anim_state && sp_track_entry && sp_event); (void)sp_track_entry; + SOKOL_ASSERT(sp_event->data && sp_event->data->name); + _sspine_instance_t* instance = _sspine_lookup_instance((uint32_t)(uintptr_t)sp_anim_state->userData); + if (_sspine_instance_and_deps_valid(instance)) { + sspine_triggered_event_info* info = _sspine_next_triggered_event_info(instance); + if (info) { + // FIXME: this sucks, but we really need the event index + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(instance->skel.id); + SOKOL_ASSERT(skeleton && skeleton->sp_skel_data->events); + const spEventData* sp_event_data = sp_event->data; + for (int i = 0; i < skeleton->sp_skel_data->eventsCount; i++) { + if (sp_event_data == skeleton->sp_skel_data->events[i]) { + info->event = _sspine_event(skeleton->slot.id, i); + break; + } + } + SOKOL_ASSERT(info->event.skeleton_id != SSPINE_INVALID_ID); + info->valid = true; + info->time = sp_event->time; + info->int_value = sp_event->intValue; + info->float_value = sp_event->floatValue; + info->volume = sp_event->volume; + info->balance = sp_event->balance; + info->string_value = _sspine_string(sp_event->stringValue); + if (info->string_value.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + } + } + } +} + +static sspine_resource_state _sspine_init_instance(_sspine_instance_t* instance, const sspine_instance_desc* desc) { + SOKOL_ASSERT(instance && (instance->slot.state == SSPINE_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + + if (desc->skeleton.id == SSPINE_INVALID_ID) { + _SSPINE_ERROR(INSTANCE_DESC_NO_SKELETON); + return SSPINE_RESOURCESTATE_FAILED; + } + instance->skel.id = desc->skeleton.id; + instance->skel.ptr = _sspine_lookup_skeleton(instance->skel.id); + if (!_sspine_skeleton_ref_valid(&instance->skel)) { + _SSPINE_ERROR(INSTANCE_SKELETON_NOT_VALID); + return SSPINE_RESOURCESTATE_FAILED; + } + _sspine_skeleton_t* skel = instance->skel.ptr; + if (SSPINE_RESOURCESTATE_VALID != skel->slot.state) { + _SSPINE_ERROR(INSTANCE_SKELETON_NOT_VALID); + return SSPINE_RESOURCESTATE_FAILED; + } + instance->atlas = skel->atlas; + if (!_sspine_atlas_ref_valid(&instance->atlas)) { + _SSPINE_ERROR(INSTANCE_ATLAS_NOT_VALID); + return SSPINE_RESOURCESTATE_FAILED; + } + if (SSPINE_RESOURCESTATE_VALID != instance->atlas.ptr->slot.state) { + _SSPINE_ERROR(INSTANCE_ATLAS_NOT_VALID); + return SSPINE_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT(skel->sp_skel_data); + SOKOL_ASSERT(skel->sp_anim_data); + + instance->sp_skel = spSkeleton_create(skel->sp_skel_data); + if (0 == instance->sp_skel) { + _SSPINE_ERROR(SPINE_SKELETON_CREATION_FAILED); + return SSPINE_RESOURCESTATE_FAILED; + } + instance->sp_anim_state = spAnimationState_create(skel->sp_anim_data); + if (0 == instance->sp_anim_state) { + _SSPINE_ERROR(SPINE_ANIMATIONSTATE_CREATION_FAILED); + return SSPINE_RESOURCESTATE_FAILED; + } + instance->sp_clip = spSkeletonClipping_create(); + if (0 == instance->sp_clip) { + _SSPINE_ERROR(SPINE_SKELETONCLIPPING_CREATION_FAILED); + return SSPINE_RESOURCESTATE_FAILED; + } + + instance->sp_anim_state->userData = (void*)(uintptr_t)instance->slot.id; + instance->sp_anim_state->listener = _sspine_event_listener; + + spSkeleton_setToSetupPose(instance->sp_skel); + spAnimationState_update(instance->sp_anim_state, 0.0f); + spAnimationState_apply(instance->sp_anim_state, instance->sp_skel); + spSkeleton_updateWorldTransform(instance->sp_skel); + + return SSPINE_RESOURCESTATE_VALID; +} + +static void _sspine_deinit_instance(_sspine_instance_t* instance) { + if (instance->sp_clip) { + spSkeletonClipping_dispose(instance->sp_clip); + instance->sp_clip = 0; + } + if (instance->sp_anim_state) { + spAnimationState_dispose(instance->sp_anim_state); + instance->sp_anim_state = 0; + } + if (instance->sp_skel) { + spSkeleton_dispose(instance->sp_skel); + instance->sp_skel = 0; + } +} + +static void _sspine_destroy_instance(sspine_instance instance_id) { + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (instance) { + _sspine_deinit_instance(instance); + _sspine_instance_pool_t* p = &_sspine.instance_pool; + _sspine_clear(instance, sizeof(_sspine_instance_t)); + _sspine_pool_free_index(&p->pool, _sspine_slot_index(instance_id.id)); + } +} + +static void _sspine_destroy_all_instances(void) { + _sspine_instance_pool_t* p = &_sspine.instance_pool; + for (int i = 0; i < p->pool.size; i++) { + _sspine_instance_t* instance = &p->items[i]; + _sspine_destroy_instance(_sspine_make_instance_handle(instance->slot.id)); + } +} + +static spAnimation* _sspine_lookup_skeleton_anim(uint32_t skeleton_id, int anim_index) { + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + if ((anim_index >= 0) && (anim_index < skeleton->sp_skel_data->animationsCount)) { + return skeleton->sp_skel_data->animations[anim_index]; + } + } + return 0; +} + +static spAnimation* _sspine_lookup_instance_anim(uint32_t instance_id, uint32_t skeleton_id, int anim_index) { + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id); + if (_sspine_instance_and_deps_valid(instance) && (instance->skel.id == skeleton_id)) { + SOKOL_ASSERT(instance->sp_skel && instance->sp_skel->data); + if ((anim_index >= 0) && (anim_index < instance->sp_skel->data->animationsCount)) { + return instance->sp_skel->data->animations[anim_index]; + } + } + return 0; +} + +static spBone* _sspine_lookup_bone(uint32_t instance_id, uint32_t skeleton_id, int bone_index) { + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id); + if (_sspine_instance_and_deps_valid(instance) && (instance->skel.id == skeleton_id)) { + SOKOL_ASSERT(instance->sp_skel && instance->sp_skel->bones); + if ((bone_index >= 0) && (bone_index <= instance->sp_skel->bonesCount)) { + return instance->sp_skel->bones[bone_index]; + } + } + return 0; +} + +static spSlot* _sspine_lookup_slot(uint32_t instance_id, uint32_t skeleton_id, int slot_index) { + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id); + if (_sspine_instance_and_deps_valid(instance) && (instance->skel.id == skeleton_id)) { + SOKOL_ASSERT(instance->sp_skel && instance->sp_skel->slots); + if ((slot_index >= 0) && (slot_index <= instance->sp_skel->slotsCount)) { + return instance->sp_skel->slots[slot_index]; + } + } + return 0; +} + +static spIkConstraint* _sspine_lookup_ikconstraint(uint32_t instance_id, uint32_t skeleton_id, int iktarget_index) { + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id); + if (_sspine_instance_and_deps_valid(instance) && (instance->skel.id == skeleton_id)) { + SOKOL_ASSERT(instance->sp_skel && instance->sp_skel->ikConstraints); + if ((iktarget_index >= 0) && (iktarget_index < instance->sp_skel->ikConstraintsCount)) { + return instance->sp_skel->ikConstraints[iktarget_index]; + } + } + return 0; +} + +static sspine_instance_desc _sspine_instance_desc_defaults(const sspine_instance_desc* desc) { + sspine_instance_desc res = *desc; + return res; +} + +// ██████ ██████ █████ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██████ ███████ ██ █ ██ +// ██ ██ ██ ██ ██ ██ ██ ███ ██ +// ██████ ██ ██ ██ ██ ███ ███ +// +// >>draw +static void _sspine_check_rewind_commands(_sspine_context_t* ctx) { + if (_sspine.frame_id != ctx->commands.rewind_frame_id) { + ctx->commands.next = 0; + ctx->commands.rewind_frame_id = _sspine.frame_id; + } +} + +static _sspine_command_t* _sspine_next_command(_sspine_context_t* ctx) { + _sspine_check_rewind_commands(ctx); + if (ctx->commands.next < ctx->commands.cap) { + return &(ctx->commands.ptr[ctx->commands.next++]); + } else { + _SSPINE_ERROR(COMMAND_BUFFER_FULL); + return 0; + } +} + +static _sspine_command_t* _sspine_cur_command(_sspine_context_t* ctx) { + _sspine_check_rewind_commands(ctx); + if (ctx->commands.next > 0) { + return &ctx->commands.ptr[ctx->commands.next - 1]; + } else { + return 0; + } +} + +static void _sspine_check_rewind_vertices(_sspine_context_t* ctx) { + if (_sspine.frame_id != ctx->vertices.rewind_frame_id) { + ctx->vertices.next = 0; + ctx->vertices.rewind_frame_id = _sspine.frame_id; + } +} + +static _sspine_alloc_vertices_result_t _sspine_alloc_vertices(_sspine_context_t* ctx, int num) { + _sspine_check_rewind_vertices(ctx); + _sspine_alloc_vertices_result_t res; + _sspine_clear(&res, sizeof(res)); + if ((ctx->vertices.next + num) <= ctx->vertices.cap) { + res.ptr = &(ctx->vertices.ptr[ctx->vertices.next]); + res.index = ctx->vertices.next; + ctx->vertices.next += num; + } else { + _SSPINE_ERROR(VERTEX_BUFFER_FULL); + } + return res; +} + +static void _sspine_check_rewind_indices(_sspine_context_t* ctx) { + if (_sspine.frame_id != ctx->indices.rewind_frame_id) { + ctx->indices.next = 0; + ctx->indices.rewind_frame_id = _sspine.frame_id; + } +} + +static _sspine_alloc_indices_result_t _sspine_alloc_indices(_sspine_context_t* ctx, int num) { + _sspine_check_rewind_indices(ctx); + _sspine_alloc_indices_result_t res; + _sspine_clear(&res, sizeof(res)); + if ((ctx->indices.next + num) <= ctx->indices.cap) { + res.ptr = &(ctx->indices.ptr[ctx->indices.next]); + res.index = ctx->indices.next; + ctx->indices.next += num; + } else { + _SSPINE_ERROR(INDEX_BUFFER_FULL); + } + return res; +} + +static void _sspine_draw_instance(_sspine_context_t* ctx, _sspine_instance_t* instance, int layer) { + SOKOL_ASSERT(_sspine_instance_and_deps_valid(instance)); + SOKOL_ASSERT(instance->sp_skel); + SOKOL_ASSERT(instance->sp_anim_state); + SOKOL_ASSERT(instance->sp_clip); + + // see: https://github.com/EsotericSoftware/spine-runtimes/blob/4.1/spine-sdl/src/spine-sdl-c.c + const spSkeleton* sp_skel = instance->sp_skel; + float* tform_buf = (float*)instance->skel.ptr->tform_buf.ptr; + const int max_tform_buf_verts = instance->skel.ptr->tform_buf.cap; + SOKOL_UNUSED(max_tform_buf_verts); // only used in asserts + const int tform_buf_stride = 2; // each element is 2 floats + spSkeletonClipping* sp_clip = instance->sp_clip; + for (int slot_index = 0; slot_index < sp_skel->slotsCount; slot_index++) { + spSlot* sp_slot = sp_skel->drawOrder[slot_index]; + if (!sp_slot->attachment) { + spSkeletonClipping_clipEnd(sp_clip, sp_slot); + continue; + } + + // early out if the slot alpha is 0 or the bone is not active + // FIXME: does alpha 0 actually mean 'invisible' for all blend modes? + if ((sp_slot->color.a == 0) || (!sp_slot->bone->active)) { + spSkeletonClipping_clipEnd(sp_clip, sp_slot); + continue; + } + + int num_vertices = 0; + float* uvs = 0; + float* vertices = 0; + int num_indices = 0; + const uint16_t* indices = 0; + const spColor* att_color = 0; + sg_image img = { SG_INVALID_ID }; + sg_sampler smp = { SG_INVALID_ID }; + bool premul_alpha = false; + if (sp_slot->attachment->type == SP_ATTACHMENT_REGION) { + static const uint16_t quad_indices[] = { 0, 1, 2, 2, 3, 0 }; + spRegionAttachment* region = (spRegionAttachment*)sp_slot->attachment; + att_color = ®ion->color; + // FIXME(?) early out if the slot alpha is 0 + if (att_color->a == 0) { + spSkeletonClipping_clipEnd(sp_clip, sp_slot); + continue; + } + spRegionAttachment_computeWorldVertices(region, sp_slot, tform_buf, 0, tform_buf_stride); + vertices = tform_buf; + num_vertices = 4; + indices = &quad_indices[0]; + num_indices = 6; + uvs = region->uvs; + const spAtlasPage* sp_page = ((spAtlasRegion*)region->rendererObject)->page; + img = _sspine_image_from_renderer_object(sp_page->rendererObject); + smp = _sspine_sampler_from_renderer_object(sp_page->rendererObject); + premul_alpha = sp_page->pma != 0; + } else if (sp_slot->attachment->type == SP_ATTACHMENT_MESH) { + spMeshAttachment* mesh = (spMeshAttachment*)sp_slot->attachment; + att_color = &mesh->color; + // FIXME(?) early out if the slot alpha is 0 + if (att_color->a == 0) { + spSkeletonClipping_clipEnd(sp_clip, sp_slot); + continue; + } + const int num_floats = mesh->super.worldVerticesLength; + num_vertices = num_floats / 2; + SOKOL_ASSERT(num_vertices <= max_tform_buf_verts); + spVertexAttachment_computeWorldVertices(&mesh->super, sp_slot, 0, num_floats, tform_buf, 0, tform_buf_stride); + vertices = tform_buf; + indices = mesh->triangles; + num_indices = mesh->trianglesCount; // actually indicesCount??? + uvs = mesh->uvs; + const spAtlasPage* sp_page = ((spAtlasRegion*)mesh->rendererObject)->page; + img = _sspine_image_from_renderer_object(sp_page->rendererObject); + smp = _sspine_sampler_from_renderer_object(sp_page->rendererObject); + premul_alpha = sp_page->pma != 0; + } else if (sp_slot->attachment->type == SP_ATTACHMENT_CLIPPING) { + spClippingAttachment* clip_attachment = (spClippingAttachment*) sp_slot->attachment; + spSkeletonClipping_clipStart(sp_clip, sp_slot, clip_attachment); + continue; + } else { + spSkeletonClipping_clipEnd(sp_clip, sp_slot); + continue; + } + SOKOL_ASSERT(vertices && (num_vertices > 0)); + SOKOL_ASSERT(indices && (num_indices > 0)); + SOKOL_ASSERT(uvs); + SOKOL_ASSERT(img.id != SG_INVALID_ID); + SOKOL_ASSERT(smp.id != SG_INVALID_ID); + + if (spSkeletonClipping_isClipping(sp_clip)) { + spSkeletonClipping_clipTriangles(sp_clip, tform_buf, num_vertices * 2, (uint16_t*)indices, num_indices, uvs, tform_buf_stride); + vertices = sp_clip->clippedVertices->items; + num_vertices = sp_clip->clippedVertices->size / 2; + uvs = sp_clip->clippedUVs->items; + indices = sp_clip->clippedTriangles->items; + num_indices = sp_clip->clippedTriangles->size; + } + SOKOL_ASSERT(vertices); + SOKOL_ASSERT(indices); + SOKOL_ASSERT(uvs); + + // there might be no geometry to render after clipping + if ((0 == num_vertices) || (0 == num_indices)) { + spSkeletonClipping_clipEnd(sp_clip, sp_slot); + continue; + } + + const _sspine_alloc_vertices_result_t dst_vertices = _sspine_alloc_vertices(ctx, num_vertices); + const _sspine_alloc_indices_result_t dst_indices = _sspine_alloc_indices(ctx, num_indices); + if ((0 == dst_vertices.ptr) || (0 == dst_indices.ptr)) { + spSkeletonClipping_clipEnd(sp_clip, sp_slot); + continue; + } + + // write transformed and potentially clipped vertices and indices + const uint8_t r = (uint8_t)(sp_skel->color.r * sp_slot->color.r * att_color->r * 255.0f); + const uint8_t g = (uint8_t)(sp_skel->color.g * sp_slot->color.g * att_color->g * 255.0f); + const uint8_t b = (uint8_t)(sp_skel->color.b * sp_slot->color.b * att_color->b * 255.0f); + const uint8_t a = (uint8_t)(sp_skel->color.a * sp_slot->color.a * att_color->a * 255.0f); + const uint32_t color = (((uint32_t)a<<24) | ((uint32_t)b<<16) | ((uint32_t)g<<8) | (uint32_t)r); + for (int vi = 0; vi < num_vertices; vi++) { + dst_vertices.ptr[vi].pos.x = vertices[vi*2]; + dst_vertices.ptr[vi].pos.y = vertices[vi*2 + 1]; + dst_vertices.ptr[vi].color = color; + dst_vertices.ptr[vi].uv.x = uvs[vi*2]; + dst_vertices.ptr[vi].uv.y = uvs[vi*2 + 1]; + } + for (int ii = 0; ii < num_indices; ii++) { + dst_indices.ptr[ii] = (uint32_t)indices[ii] + (uint32_t)dst_vertices.index; + } + + sg_pipeline pip = { SG_INVALID_ID }; + // NOTE: pma == 0.0: use color from texture as is + // pma == 1.0: multiply texture rgb by texture alpha in fragment shader + float pma = 0.0f; + switch (sp_slot->data->blendMode) { + case SP_BLEND_MODE_NORMAL: + case SP_BLEND_MODE_ADDITIVE: + case SP_BLEND_MODE_SCREEN: + pip = ctx->pip.normal_additive; + pma = premul_alpha ? 0.0f : 1.0f; // NOT A BUG + break; + case SP_BLEND_MODE_MULTIPLY: + pip = ctx->pip.multiply; + pma = 0.0f; // always use texture color as is + break; + } + + // write new draw command, or merge with current draw command + _sspine_command_t* cur_cmd = _sspine_cur_command(ctx); + if (cur_cmd + && (cur_cmd->layer == layer) + && (cur_cmd->pip.id == pip.id) + && (cur_cmd->img.id == img.id) + && (cur_cmd->smp.id == smp.id) + && (cur_cmd->pma == pma)) + { + // merge with current command + cur_cmd->num_elements += num_indices; + } else { + // record a new command + _sspine_command_t* cmd_ptr = _sspine_next_command(ctx); + if (cmd_ptr) { + cmd_ptr->layer = layer; + cmd_ptr->pip = pip; + cmd_ptr->img = img; + cmd_ptr->smp = smp; + cmd_ptr->pma = pma; + cmd_ptr->base_element = dst_indices.index; + cmd_ptr->num_elements = num_indices; + } + } + spSkeletonClipping_clipEnd(sp_clip, sp_slot); + } + spSkeletonClipping_clipEnd2(sp_clip); +} + +// compute orthographic projection matrix +static void _sspine_layer_transform_to_proj(const sspine_layer_transform* tform, float* res) { + const float left = -tform->origin.x; + const float right = tform->size.x - tform->origin.x; + const float top = -tform->origin.y; + const float bottom = tform->size.y - tform->origin.y; + const float znear = -1.0f; + const float zfar = 1.0f; + res[0] = 2.0f / (right - left); + res[1] = 0.0f; + res[2] = 0.0f; + res[3] = 0.0f; + res[4] = 0.0f; + res[5] = 2.0f / (top - bottom); + res[6] = 0.0f; + res[7] = 0.0f; + res[8] = 0.0f; + res[9] = 0.0f; + res[10] = -2.0f / (zfar - znear); + res[11] = 0.0f; + res[12] = -(right + left) / (right - left); + res[13] = -(top + bottom) / (top - bottom); + res[14] = -(zfar + znear) / (zfar - znear); + res[15] = 1.0f; +} + +static _sspine_vsparams_t _sspine_compute_vsparams(const sspine_layer_transform* tform) { + _sspine_vsparams_t p; + _sspine_clear(&p, sizeof(p)); + _sspine_layer_transform_to_proj(tform, p.mvp); + return p; +} + +static void _sspine_draw_layer(_sspine_context_t* ctx, int layer, const sspine_layer_transform* tform) { + if ((ctx->vertices.next > 0) && (ctx->commands.next > 0)) { + sg_push_debug_group("sokol-spine"); + + if (ctx->update_frame_id != _sspine.frame_id) { + ctx->update_frame_id = _sspine.frame_id; + const sg_range vtx_range = { ctx->vertices.ptr, (size_t)ctx->vertices.next * sizeof(_sspine_vertex_t) }; + sg_update_buffer(ctx->vbuf, &vtx_range); + const sg_range idx_range = { ctx->indices.ptr, (size_t)ctx->indices.next * sizeof(uint32_t) }; + sg_update_buffer(ctx->ibuf, &idx_range); + } + + _sspine_vsparams_t vsparams = _sspine_compute_vsparams(tform); + const sg_range vsparams_range = { &vsparams, sizeof(vsparams) }; + _sspine_fsparams_t fsparams; + _sspine_clear(&fsparams, sizeof(fsparams)); + const sg_range fsparams_range = { &fsparams, sizeof(fsparams) }; + + uint32_t cur_pip_id = SG_INVALID_ID; + uint32_t cur_img_id = SG_INVALID_ID; + uint32_t cur_smp_id = SG_INVALID_ID; + float cur_pma = -1.0f; + for (int i = 0; i < ctx->commands.next; i++) { + const _sspine_command_t* cmd = &ctx->commands.ptr[i]; + const bool img_valid = sg_query_image_state(cmd->img) == SG_RESOURCESTATE_VALID; + const bool smp_valid = sg_query_sampler_state(cmd->smp) == SG_RESOURCESTATE_VALID; + if ((layer == cmd->layer) && img_valid && smp_valid) { + if (cur_pip_id != cmd->pip.id) { + sg_apply_pipeline(cmd->pip); + cur_pip_id = cmd->pip.id; + sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &vsparams_range); + cur_img_id = SG_INVALID_ID; + } + if ((cur_img_id != cmd->img.id) || (cur_smp_id != cmd->smp.id)) { + ctx->bind.fs.images[0] = cmd->img; + ctx->bind.fs.samplers[0] = cmd->smp; + sg_apply_bindings(&ctx->bind); + cur_img_id = cmd->img.id; + cur_smp_id = cmd->smp.id; + } + if (cur_pma != cmd->pma) { + fsparams.pma = cmd->pma; + sg_apply_uniforms(SG_SHADERSTAGE_FS, 0, &fsparams_range); + cur_pma = cmd->pma; + } + if (cmd->num_elements > 0) { + sg_draw(cmd->base_element, cmd->num_elements, 1); + } + } + } + sg_pop_debug_group(); + } +} + +// ███ ███ ██ ███████ ██████ +// ████ ████ ██ ██ ██ +// ██ ████ ██ ██ ███████ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ███████ ██████ +// +// >>misc + +// return sspine_desc with patched defaults +static sspine_desc _sspine_desc_defaults(const sspine_desc* desc) { + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + sspine_desc res = *desc; + res.max_vertices = _sspine_def(desc->max_vertices, _SSPINE_DEFAULT_MAX_VERTICES); + res.max_commands = _sspine_def(desc->max_commands, _SSPINE_DEFAULT_MAX_COMMANDS); + res.context_pool_size = _sspine_def(desc->context_pool_size, _SSPINE_DEFAULT_CONTEXT_POOL_SIZE); + res.atlas_pool_size = _sspine_def(desc->atlas_pool_size, _SSPINE_DEFAULT_ATLAS_POOL_SIZE); + res.skeleton_pool_size = _sspine_def(desc->skeleton_pool_size, _SSPINE_DEFAULT_SKELETON_POOL_SIZE); + res.skinset_pool_size = _sspine_def(desc->skinset_pool_size, _SSPINE_DEFAULT_SKINSET_POOL_SIZE); + res.instance_pool_size = _sspine_def(desc->instance_pool_size, _SSPINE_DEFAULT_INSTANCE_POOL_SIZE); + return res; +} + +static sspine_context_desc _sspine_as_context_desc(const sspine_desc* desc) { + sspine_context_desc ctx_desc; + _sspine_clear(&ctx_desc, sizeof(ctx_desc)); + ctx_desc.max_vertices = desc->max_vertices; + ctx_desc.max_commands = desc->max_commands; + ctx_desc.color_format = desc->color_format; + ctx_desc.depth_format = desc->depth_format; + ctx_desc.sample_count = desc->sample_count; + ctx_desc.color_write_mask = desc->color_write_mask; + return ctx_desc; +} + +static sg_filter _sspine_as_sampler_filter(spAtlasFilter filter) { + switch (filter) { + case SP_ATLAS_UNKNOWN_FILTER: return _SG_FILTER_DEFAULT; + case SP_ATLAS_NEAREST: return SG_FILTER_NEAREST; + case SP_ATLAS_LINEAR: return SG_FILTER_LINEAR; + case SP_ATLAS_MIPMAP: return SG_FILTER_LINEAR; + case SP_ATLAS_MIPMAP_NEAREST_NEAREST: return SG_FILTER_NEAREST; + case SP_ATLAS_MIPMAP_LINEAR_NEAREST: return SG_FILTER_LINEAR; + case SP_ATLAS_MIPMAP_NEAREST_LINEAR: return SG_FILTER_NEAREST; + case SP_ATLAS_MIPMAP_LINEAR_LINEAR: return SG_FILTER_LINEAR; + default: return SG_FILTER_LINEAR; + } +} + +static sg_filter _sspine_as_sampler_mipmap_filter(spAtlasFilter filter) { + switch (filter) { + case SP_ATLAS_UNKNOWN_FILTER: return _SG_FILTER_DEFAULT; + case SP_ATLAS_NEAREST: return SG_FILTER_NONE; + case SP_ATLAS_LINEAR: return SG_FILTER_NONE; + case SP_ATLAS_MIPMAP: return SG_FILTER_NEAREST; + case SP_ATLAS_MIPMAP_NEAREST_NEAREST: return SG_FILTER_NEAREST; + case SP_ATLAS_MIPMAP_LINEAR_NEAREST: return SG_FILTER_NEAREST; + case SP_ATLAS_MIPMAP_NEAREST_LINEAR: return SG_FILTER_LINEAR; + case SP_ATLAS_MIPMAP_LINEAR_LINEAR: return SG_FILTER_LINEAR; + default: return SG_FILTER_NEAREST; + } +} + +static sg_wrap _sspine_as_sampler_wrap(spAtlasWrap wrap) { + switch (wrap) { + case SP_ATLAS_MIRROREDREPEAT: return SG_WRAP_MIRRORED_REPEAT; + case SP_ATLAS_CLAMPTOEDGE: return SG_WRAP_CLAMP_TO_EDGE; + case SP_ATLAS_REPEAT: return SG_WRAP_REPEAT; + default: return _SG_WRAP_DEFAULT; + } +} + +static void _sspine_init_image_info(const _sspine_atlas_t* atlas, int index, sspine_image_info* info, bool with_overrides) { + spAtlasPage* page = _sspine_lookup_atlas_page(atlas->slot.id, index); + SOKOL_ASSERT(page); + SOKOL_ASSERT(page->name); + info->valid = true; + info->sgimage = _sspine_image_from_renderer_object(page->rendererObject); + info->sgsampler = _sspine_sampler_from_renderer_object(page->rendererObject); + if (with_overrides && (atlas->overrides.min_filter != _SG_FILTER_DEFAULT)) { + info->min_filter = atlas->overrides.min_filter; + } else { + info->min_filter = _sspine_as_sampler_filter(page->minFilter); + } + if (with_overrides && (atlas->overrides.mag_filter != _SG_FILTER_DEFAULT)) { + info->mag_filter = atlas->overrides.mag_filter; + } else { + info->mag_filter = _sspine_as_sampler_filter(page->magFilter); + } + if (with_overrides && (atlas->overrides.mipmap_filter != _SG_FILTER_DEFAULT)) { + info->mipmap_filter = atlas->overrides.mipmap_filter; + } else { + info->mipmap_filter = _sspine_as_sampler_mipmap_filter(page->minFilter); + } + if (with_overrides && (atlas->overrides.wrap_u != _SG_WRAP_DEFAULT)) { + info->wrap_u = atlas->overrides.wrap_u; + } else { + info->wrap_u = _sspine_as_sampler_wrap(page->uWrap); + } + if (with_overrides && (atlas->overrides.wrap_v != _SG_WRAP_DEFAULT)) { + info->wrap_v = atlas->overrides.wrap_v; + } else { + info->wrap_v = _sspine_as_sampler_wrap(page->vWrap); + } + info->width = page->width; + info->height = page->height; + // NOTE: override already happened in atlas init + info->premul_alpha = page->pma != 0; + info->filename = _sspine_string(page->name); + if (info->filename.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } +} + +static void _sspine_init_shared(void) { + sg_shader_desc shd_desc; + _sspine_clear(&shd_desc, sizeof(shd_desc)); + shd_desc.attrs[0].name = "position"; + shd_desc.attrs[1].name = "texcoord0"; + shd_desc.attrs[2].name = "color0"; + shd_desc.attrs[0].sem_name = "TEXCOORD"; + shd_desc.attrs[0].sem_index = 0; + shd_desc.attrs[1].sem_name = "TEXCOORD"; + shd_desc.attrs[1].sem_index = 1; + shd_desc.attrs[2].sem_name = "TEXCOORD"; + shd_desc.attrs[2].sem_index = 2; + shd_desc.vs.uniform_blocks[0].size = sizeof(_sspine_vsparams_t); + shd_desc.vs.uniform_blocks[0].layout = SG_UNIFORMLAYOUT_STD140; + shd_desc.vs.uniform_blocks[0].uniforms[0].name = "vs_params"; + shd_desc.vs.uniform_blocks[0].uniforms[0].type = SG_UNIFORMTYPE_FLOAT4; + shd_desc.vs.uniform_blocks[0].uniforms[0].array_count = 4; + shd_desc.fs.uniform_blocks[0].size = 16; + shd_desc.fs.uniform_blocks[0].layout = SG_UNIFORMLAYOUT_STD140; + shd_desc.fs.uniform_blocks[0].uniforms[0].name = "fs_params"; + shd_desc.fs.uniform_blocks[0].uniforms[0].type = SG_UNIFORMTYPE_FLOAT4; + shd_desc.fs.uniform_blocks[0].uniforms[0].array_count = 1; + shd_desc.fs.images[0].used = true; + shd_desc.fs.images[0].image_type = SG_IMAGETYPE_2D; + shd_desc.fs.images[0].sample_type = SG_IMAGESAMPLETYPE_FLOAT; + shd_desc.fs.samplers[0].used = true; + shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING; + shd_desc.fs.image_sampler_pairs[0].used = true; + shd_desc.fs.image_sampler_pairs[0].image_slot = 0; + shd_desc.fs.image_sampler_pairs[0].sampler_slot = 0; + shd_desc.fs.image_sampler_pairs[0].glsl_name = "tex_smp"; + shd_desc.label = "sspine-shader"; + #if defined(SOKOL_GLCORE) + shd_desc.vs.source = (const char*)_sspine_vs_source_glsl410; + shd_desc.fs.source = (const char*)_sspine_fs_source_glsl410; + #elif defined(SOKOL_GLES3) + shd_desc.vs.source = (const char*)_sspine_vs_source_glsl300es; + shd_desc.fs.source = (const char*)_sspine_fs_source_glsl300es; + #elif defined(SOKOL_METAL) + shd_desc.vs.entry = "main0"; + shd_desc.fs.entry = "main0"; + switch (sg_query_backend()) { + case SG_BACKEND_METAL_MACOS: + shd_desc.vs.bytecode = SG_RANGE(_sspine_vs_bytecode_metal_macos); + shd_desc.fs.bytecode = SG_RANGE(_sspine_fs_bytecode_metal_macos); + break; + case SG_BACKEND_METAL_IOS: + shd_desc.vs.bytecode = SG_RANGE(_sspine_vs_bytecode_metal_ios); + shd_desc.fs.bytecode = SG_RANGE(_sspine_fs_bytecode_metal_ios); + break; + default: + shd_desc.vs.source = (const char*)_sspine_vs_source_metal_sim; + shd_desc.fs.source = (const char*)_sspine_fs_source_metal_sim; + break; + } + #elif defined(SOKOL_D3D11) + shd_desc.vs.bytecode = SG_RANGE(_sspine_vs_bytecode_hlsl4); + shd_desc.fs.bytecode = SG_RANGE(_sspine_fs_bytecode_hlsl4); + #elif defined(SOKOL_WGPU) + shd_desc.vs.source = (const char*)_sspine_vs_source_wgsl; + shd_desc.fs.source = (const char*)_sspine_fs_source_wgsl; + #else + shd_desc.vs.source = _sspine_vs_source_dummy; + shd_desc.fs.source = _sspine_fs_source_dummy; + #endif + _sspine.shd = sg_make_shader(&shd_desc); +} + +static void _sspine_destroy_shared(void) { + sg_destroy_shader(_sspine.shd); +} + +// called from inside sokol-gfx sg_commit() +static void _sspine_commit_listener_func(void* userdata) { + (void)userdata; + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine.frame_id++; +} + +static sg_commit_listener _sspine_make_commit_listener(void) { + sg_commit_listener commit_listener = { _sspine_commit_listener_func, 0 }; + return commit_listener; +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void sspine_setup(const sspine_desc* desc) { + SOKOL_ASSERT(desc); + spBone_setYDown(1); + _sspine_clear(&_sspine, sizeof(_sspine)); + _sspine.init_cookie = _SSPINE_INIT_COOKIE; + _sspine.desc = _sspine_desc_defaults(desc); + _sspine_init_shared(); + // important, need to setup the frame id with a non-zero value, + // otherwise updates won't trigger in the first frame + _sspine.frame_id = 1; + _sspine_setup_context_pool(_sspine.desc.context_pool_size); + _sspine_setup_atlas_pool(_sspine.desc.atlas_pool_size); + _sspine_setup_skeleton_pool(_sspine.desc.skeleton_pool_size); + _sspine_setup_skinset_pool(_sspine.desc.skinset_pool_size); + _sspine_setup_instance_pool(_sspine.desc.instance_pool_size); + const sspine_context_desc ctx_desc = _sspine_as_context_desc(&_sspine.desc); + _sspine.def_ctx_id = sspine_make_context(&ctx_desc); + SOKOL_ASSERT(_sspine_is_default_context(_sspine.def_ctx_id)); + sspine_set_context(_sspine.def_ctx_id); + if (!sg_add_commit_listener(_sspine_make_commit_listener())) { + _SSPINE_ERROR(ADD_COMMIT_LISTENER_FAILED); + } +} + +SOKOL_API_IMPL void sspine_shutdown(void) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sg_remove_commit_listener(_sspine_make_commit_listener()); + _sspine_destroy_all_instances(); + _sspine_destroy_all_skinsets(); + _sspine_destroy_all_skeletons(); + _sspine_destroy_all_atlases(); + _sspine_destroy_all_contexts(); + _sspine_discard_instance_pool(); + _sspine_discard_skinset_pool(); + _sspine_discard_skeleton_pool(); + _sspine_discard_atlas_pool(); + _sspine_discard_context_pool(); + _sspine_destroy_shared(); + _sspine.init_cookie = 0; +} + +SOKOL_API_IMPL sspine_context sspine_make_context(const sspine_context_desc* desc) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(desc); + const sspine_context_desc desc_def = _sspine_context_desc_defaults(desc); + sspine_context ctx_id = _sspine_alloc_context(); + _sspine_context_t* ctx = _sspine_lookup_context(ctx_id.id); + if (ctx) { + ctx->slot.state = _sspine_init_context(ctx, &desc_def); + SOKOL_ASSERT((ctx->slot.state == SSPINE_RESOURCESTATE_VALID) || (ctx->slot.state == SSPINE_RESOURCESTATE_FAILED)); + if (ctx->slot.state == SSPINE_RESOURCESTATE_FAILED) { + _sspine_deinit_context(ctx); + } + } else { + ctx->slot.state = SSPINE_RESOURCESTATE_FAILED; + _SSPINE_ERROR(CONTEXT_POOL_EXHAUSTED); + } + return ctx_id; +} + +SOKOL_API_IMPL void sspine_destroy_context(sspine_context ctx_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + if (_sspine_is_default_context(ctx_id)) { + _SSPINE_ERROR(CANNOT_DESTROY_DEFAULT_CONTEXT); + return; + } + _sspine_destroy_context(ctx_id); + // re-validate the current context pointer (this will return a nullptr + // if we just destroyed the current context) + _sspine.cur_ctx = _sspine_lookup_context(_sspine.cur_ctx_id.id); +} + +SOKOL_API_IMPL void sspine_set_context(sspine_context ctx_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + if (_sspine_is_default_context(ctx_id)) { + _sspine.cur_ctx_id = _sspine.def_ctx_id; + } else { + _sspine.cur_ctx_id = ctx_id; + } + // this will return null if the handle isn't valid + _sspine.cur_ctx = _sspine_lookup_context(_sspine.cur_ctx_id.id); +} + +SOKOL_API_IMPL sspine_context sspine_get_context(void) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + return _sspine.cur_ctx_id; +} + +SOKOL_API_IMPL sspine_context sspine_default_context(void) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + return _sspine_make_context_handle(0x00010001); +} + +SOKOL_API_IMPL sspine_context_info sspine_get_context_info(sspine_context ctx_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_context_info res; + _sspine_clear(&res, sizeof(res)); + const _sspine_context_t* ctx = _sspine_lookup_context(ctx_id.id); + if (ctx) { + res.num_vertices = ctx->vertices.next; + res.num_indices = ctx->indices.next; + res.num_commands = ctx->commands.next; + } + return res; +} + +SOKOL_API_IMPL void sspine_set_skinset(sspine_instance instance_id, sspine_skinset skinset_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + _sspine_skinset_t* skinset = _sspine_lookup_skinset(skinset_id.id); + if (_sspine_instance_and_deps_valid(instance) && _sspine_skinset_and_deps_valid(skinset) && (instance->skel.id == skinset->skel.id)) { + SOKOL_ASSERT(instance->sp_skel); + SOKOL_ASSERT(instance->sp_anim_state); + SOKOL_ASSERT(skinset->sp_skin); + spSkeleton_setSkin(instance->sp_skel, 0); + spSkeleton_setSkin(instance->sp_skel, skinset->sp_skin); + spSkeleton_setSlotsToSetupPose(instance->sp_skel); + spAnimationState_apply(instance->sp_anim_state, instance->sp_skel); + } +} + +SOKOL_API_IMPL void sspine_update_instance(sspine_instance instance_id, float delta_time) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (_sspine_instance_and_deps_valid(instance)) { + SOKOL_ASSERT(instance->sp_skel); + SOKOL_ASSERT(instance->sp_anim_state); + _sspine_rewind_triggered_events(instance); + spAnimationState_update(instance->sp_anim_state, delta_time); + spAnimationState_apply(instance->sp_anim_state, instance->sp_skel); + spSkeleton_updateWorldTransform(instance->sp_skel); + } +} + +SOKOL_API_IMPL int sspine_num_triggered_events(sspine_instance instance_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (_sspine_instance_and_deps_valid(instance)) { + SOKOL_ASSERT((instance->cur_triggered_event_index >= 0) && (instance->cur_triggered_event_index <= _SSPINE_MAX_TRIGGERED_EVENTS)); + return instance->cur_triggered_event_index; + } + return 0; +} + +SOKOL_API_IMPL sspine_triggered_event_info sspine_get_triggered_event_info(sspine_instance instance_id, int triggered_event_index) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + sspine_triggered_event_info res; + _sspine_clear(&res, sizeof(res)); + if (_sspine_instance_and_deps_valid(instance)) { + if ((triggered_event_index >= 0) && (triggered_event_index < instance->cur_triggered_event_index)) { + res = instance->triggered_events[triggered_event_index]; + } + } + return res; +} + +SOKOL_API_IMPL void sspine_draw_instance_in_layer(sspine_instance instance_id, int layer) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_context_t* ctx = _sspine.cur_ctx; + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (ctx && _sspine_instance_and_deps_valid(instance)) { + _sspine_draw_instance(ctx, instance, layer); + } +} + +SOKOL_API_IMPL sspine_mat4 sspine_layer_transform_to_mat4(const sspine_layer_transform* tform) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_mat4 res; + _sspine_layer_transform_to_proj(tform, res.m); + return res; +} + +SOKOL_API_IMPL void sspine_context_draw_instance_in_layer(sspine_context ctx_id, sspine_instance instance_id, int layer) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_context_t* ctx = _sspine_lookup_context(ctx_id.id); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (ctx && _sspine_instance_and_deps_valid(instance)) { + _sspine_draw_instance(ctx, instance, layer); + } +} + +SOKOL_API_IMPL void sspine_draw_layer(int layer, const sspine_layer_transform* tform) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(tform); + _sspine_context_t* ctx = _sspine.cur_ctx; + if (ctx) { + _sspine_draw_layer(ctx, layer, tform); + } +} + +SOKOL_API_IMPL void sspine_context_draw_layer(sspine_context ctx_id, int layer, const sspine_layer_transform* tform) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(tform); + _sspine_context_t* ctx = _sspine_lookup_context(ctx_id.id); + if (ctx) { + _sspine_draw_layer(ctx, layer, tform); + } +} + +SOKOL_API_IMPL sspine_atlas sspine_make_atlas(const sspine_atlas_desc* desc) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(desc); + const sspine_atlas_desc desc_def = _sspine_atlas_desc_defaults(desc); + sspine_atlas atlas_id = _sspine_alloc_atlas(); + _sspine_atlas_t* atlas = _sspine_lookup_atlas(atlas_id.id); + if (atlas) { + atlas->slot.state = _sspine_init_atlas(atlas, &desc_def); + SOKOL_ASSERT((atlas->slot.state == SSPINE_RESOURCESTATE_VALID) || (atlas->slot.state == SSPINE_RESOURCESTATE_FAILED)); + if (atlas->slot.state == SSPINE_RESOURCESTATE_FAILED) { + _sspine_deinit_atlas(atlas); + } + } else { + _SSPINE_ERROR(ATLAS_POOL_EXHAUSTED); + } + return atlas_id; +} + +SOKOL_API_IMPL void sspine_destroy_atlas(sspine_atlas atlas_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_destroy_atlas(atlas_id); +} + +SOKOL_API_IMPL sspine_skeleton sspine_make_skeleton(const sspine_skeleton_desc* desc) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(desc); + const sspine_skeleton_desc desc_def = _sspine_skeleton_desc_defaults(desc); + sspine_skeleton skeleton_id = _sspine_alloc_skeleton(); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (skeleton) { + skeleton->slot.state = _sspine_init_skeleton(skeleton, &desc_def); + SOKOL_ASSERT((skeleton->slot.state == SSPINE_RESOURCESTATE_VALID) || (skeleton->slot.state == SSPINE_RESOURCESTATE_FAILED)); + if (skeleton->slot.state == SSPINE_RESOURCESTATE_FAILED) { + _sspine_deinit_skeleton(skeleton); + } + } else { + _SSPINE_ERROR(SKELETON_POOL_EXHAUSTED); + } + return skeleton_id; +} + +SOKOL_API_IMPL void sspine_destroy_skeleton(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_destroy_skeleton(skeleton_id); +} + +SOKOL_API_IMPL sspine_skinset sspine_make_skinset(const sspine_skinset_desc* desc) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(desc); + const sspine_skinset_desc desc_def = _sspine_skinset_desc_defaults(desc); + sspine_skinset skinset_id = _sspine_alloc_skinset(); + _sspine_skinset_t* skinset = _sspine_lookup_skinset(skinset_id.id); + if (skinset) { + skinset->slot.state = _sspine_init_skinset(skinset, &desc_def); + SOKOL_ASSERT((skinset->slot.state == SSPINE_RESOURCESTATE_VALID) || (skinset->slot.state == SSPINE_RESOURCESTATE_FAILED)); + if (skinset->slot.state == SSPINE_RESOURCESTATE_FAILED) { + _sspine_deinit_skinset(skinset); + } + } else { + _SSPINE_ERROR(SKINSET_POOL_EXHAUSTED); + } + return skinset_id; +} + +SOKOL_API_IMPL void sspine_destroy_skinset(sspine_skinset skinset_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_destroy_skinset(skinset_id); +} + +SOKOL_API_IMPL sspine_instance sspine_make_instance(const sspine_instance_desc* desc) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(desc); + const sspine_instance_desc desc_def = _sspine_instance_desc_defaults(desc); + sspine_instance instance_id = _sspine_alloc_instance(); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (instance) { + instance->slot.state = _sspine_init_instance(instance, &desc_def); + SOKOL_ASSERT((instance->slot.state == SSPINE_RESOURCESTATE_VALID) || (instance->slot.state == SSPINE_RESOURCESTATE_FAILED)); + if (instance->slot.state == SSPINE_RESOURCESTATE_FAILED) { + _sspine_deinit_instance(instance); + } + } else { + _SSPINE_ERROR(INSTANCE_POOL_EXHAUSTED); + } + return instance_id; +} + +SOKOL_API_IMPL void sspine_destroy_instance(sspine_instance instance_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_destroy_instance(instance_id); +} + +SOKOL_API_IMPL sspine_resource_state sspine_get_context_resource_state(sspine_context ctx_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + const _sspine_context_t* ctx = _sspine_lookup_context(ctx_id.id); + if (ctx) { + return ctx->slot.state; + } else { + return SSPINE_RESOURCESTATE_INVALID; + } +} + +SOKOL_API_IMPL sspine_resource_state sspine_get_atlas_resource_state(sspine_atlas atlas_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + const _sspine_atlas_t* atlas = _sspine_lookup_atlas(atlas_id.id); + if (atlas) { + return atlas->slot.state; + } else { + return SSPINE_RESOURCESTATE_INVALID; + } +} + +SOKOL_API_IMPL sspine_resource_state sspine_get_skeleton_resource_state(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + const _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (skeleton) { + return skeleton->slot.state; + } else { + return SSPINE_RESOURCESTATE_INVALID; + } +} + +SOKOL_API_IMPL sspine_resource_state sspine_get_skinset_resource_state(sspine_skinset skinset_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + const _sspine_skinset_t* skinset = _sspine_lookup_skinset(skinset_id.id); + if (skinset) { + return skinset->slot.state; + } else { + return SSPINE_RESOURCESTATE_INVALID; + } +} + +SOKOL_API_IMPL sspine_resource_state sspine_get_instance_resource_state(sspine_instance instance_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + const _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (instance) { + return instance->slot.state; + } else { + return SSPINE_RESOURCESTATE_INVALID; + } +} + +SOKOL_API_IMPL bool sspine_context_valid(sspine_context ctx_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + return sspine_get_context_resource_state(ctx_id) == SSPINE_RESOURCESTATE_VALID; +} + +SOKOL_API_IMPL bool sspine_atlas_valid(sspine_atlas atlas_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + return sspine_get_atlas_resource_state(atlas_id) == SSPINE_RESOURCESTATE_VALID; +} + +SOKOL_API_IMPL bool sspine_skeleton_valid(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + return sspine_get_skeleton_resource_state(skeleton_id) == SSPINE_RESOURCESTATE_VALID; +} + +SOKOL_API_IMPL bool sspine_skinset_valid(sspine_skinset skinset_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + return sspine_get_skinset_resource_state(skinset_id) == SSPINE_RESOURCESTATE_VALID; +} + +SOKOL_API_IMPL bool sspine_instance_valid(sspine_instance instance_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + return sspine_get_instance_resource_state(instance_id) == SSPINE_RESOURCESTATE_VALID; +} + +SOKOL_API_IMPL sspine_atlas sspine_get_skeleton_atlas(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + sspine_atlas res; + _sspine_clear(&res, sizeof(res)); + if (skeleton) { + res.id = skeleton->atlas.id; + } + return res; +} + +SOKOL_API_IMPL sspine_skeleton sspine_get_instance_skeleton(sspine_instance instance_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + sspine_skeleton res; + _sspine_clear(&res, sizeof(res)); + if (instance) { + res.id = instance->skel.id; + } + return res; +} + +SOKOL_API_IMPL int sspine_num_images(sspine_atlas atlas_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_atlas_t* atlas = _sspine_lookup_atlas(atlas_id.id); + if (atlas) { + return atlas->num_pages; + } + return 0; +} + +SOKOL_API_IMPL sspine_image sspine_image_by_index(sspine_atlas atlas_id, int index) { + return _sspine_image(atlas_id.id, index); +} + +SOKOL_API_IMPL bool sspine_image_valid(sspine_image image) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_atlas_t* atlas = _sspine_lookup_atlas(image.atlas_id); + return atlas && (image.index >= 0) && (image.index < atlas->num_pages); +} + +SOKOL_API_IMPL bool sspine_image_equal(sspine_image first, sspine_image second) { + return (first.atlas_id == second.atlas_id) && (first.index == second.index); +} + +SOKOL_API_IMPL sspine_image_info sspine_get_image_info(sspine_image image) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_atlas_t* atlas = _sspine_lookup_atlas(image.atlas_id); + sspine_image_info img_info; + _sspine_clear(&img_info, sizeof(img_info)); + if (atlas && (image.index >= 0) && (image.index < atlas->num_pages)) { + _sspine_init_image_info(atlas, image.index, &img_info, true); + } + return img_info; +} + +SOKOL_API_IMPL int sspine_num_atlas_pages(sspine_atlas atlas_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_atlas_t* atlas = _sspine_lookup_atlas(atlas_id.id); + if (atlas) { + return atlas->num_pages; + } else { + return 0; + } +} + +SOKOL_API_IMPL sspine_atlas_page sspine_atlas_page_by_index(sspine_atlas atlas_id, int index) { + return _sspine_atlas_page(atlas_id.id, index); +} + +SOKOL_API_IMPL bool sspine_atlas_page_valid(sspine_atlas_page page) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_atlas_t* atlas = _sspine_lookup_atlas(page.atlas_id); + if (atlas) { + return (page.index >= 0) && (page.index < atlas->num_pages); + } + return false; +} + +SOKOL_API_IMPL bool sspine_atlas_page_equal(sspine_atlas_page first, sspine_atlas_page second) { + return (first.atlas_id == second.atlas_id) && (first.index == second.index); +} + +SOKOL_API_IMPL sspine_atlas_page_info sspine_get_atlas_page_info(sspine_atlas_page page) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_atlas_page_info res; + _sspine_clear(&res, sizeof(res)); + const spAtlasPage* sp_page = _sspine_lookup_atlas_page(page.atlas_id, page.index); + if (sp_page) { + // at this point, atlas is guaranteed to be valid + const _sspine_atlas_t* atlas = _sspine_lookup_atlas(page.atlas_id); + res.valid = true; + res.atlas.id = page.atlas_id; + // write image info without overrides + _sspine_init_image_info(atlas, page.index, &res.image, false); + // ...and provide the overrides separately + res.overrides = atlas->overrides; + } + return res; +} + +SOKOL_API_IMPL void sspine_set_position(sspine_instance instance_id, sspine_vec2 position) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (_sspine_instance_and_deps_valid(instance)) { + SOKOL_ASSERT(instance->sp_skel); + instance->sp_skel->x = position.x; + instance->sp_skel->y = position.y; + } +} + +SOKOL_API_IMPL void sspine_set_scale(sspine_instance instance_id, sspine_vec2 scale) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (instance) { + SOKOL_ASSERT(instance->sp_skel); + instance->sp_skel->scaleX = scale.x; + instance->sp_skel->scaleY = scale.y; + } +} + +SOKOL_API_IMPL void sspine_set_color(sspine_instance instance_id, sspine_color color) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (instance) { + SOKOL_ASSERT(instance->sp_skel); + instance->sp_skel->color.r = color.r; + instance->sp_skel->color.g = color.g; + instance->sp_skel->color.b = color.b; + instance->sp_skel->color.a = color.a; + } +} + +SOKOL_API_IMPL sspine_vec2 sspine_get_position(sspine_instance instance_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + sspine_vec2 v = { 0.0f, 0.0f }; + if (instance) { + SOKOL_ASSERT(instance->sp_skel); + v.x = instance->sp_skel->x; + v.y = instance->sp_skel->y; + } + return v; +} + +SOKOL_API_IMPL sspine_vec2 sspine_get_scale(sspine_instance instance_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + sspine_vec2 v = { 0.0f, 0.0f }; + if (instance) { + SOKOL_ASSERT(instance->sp_skel); + v.x = instance->sp_skel->scaleX; + v.y = instance->sp_skel->scaleY; + } + return v; +} + +SOKOL_API_IMPL sspine_color sspine_get_color(sspine_instance instance_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + sspine_color c = { 0.0f, 0.0f, 0.0f, 0.0f }; + if (instance) { + SOKOL_ASSERT(instance->sp_skel); + c.r = instance->sp_skel->color.r; + c.g = instance->sp_skel->color.g; + c.b = instance->sp_skel->color.b; + c.a = instance->sp_skel->color.a; + } + return c; +} + +SOKOL_API_IMPL int sspine_num_anims(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return skeleton->sp_skel_data->animationsCount; + } + return 0; +} + +SOKOL_API_IMPL sspine_anim sspine_anim_by_name(sspine_skeleton skeleton_id, const char* name) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(name); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + // NOTE: there's a spSkeletonData_findAnimation function, but that doesn't + // give us access to the index, so we'll need to do the loop ourselves + SOKOL_ASSERT(skeleton->sp_skel_data); + const spSkeletonData* sp_skel_data = skeleton->sp_skel_data; + const int num_anims = sp_skel_data->animationsCount; + SOKOL_ASSERT(sp_skel_data->animations); + for (int i = 0; i < num_anims; i++) { + SOKOL_ASSERT(sp_skel_data->animations[i]); + SOKOL_ASSERT(sp_skel_data->animations[i]->name); + if (0 == strcmp(sp_skel_data->animations[i]->name, name)) { + return _sspine_anim(skeleton_id.id, i); + } + } + } + return _sspine_anim(0, 0); +} + +SOKOL_API_IMPL sspine_anim sspine_anim_by_index(sspine_skeleton skeleton_id, int index) { + return _sspine_anim(skeleton_id.id, index); +} + +SOKOL_API_IMPL bool sspine_anim_valid(sspine_anim anim) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(anim.skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return (anim.index >= 0) && (anim.index < skeleton->sp_skel_data->animationsCount); + } + return false; +} + +SOKOL_API_IMPL bool sspine_anim_equal(sspine_anim first, sspine_anim second) { + return (first.skeleton_id == second.skeleton_id) && (first.index == second.index); +} + +SOKOL_API_IMPL sspine_anim_info sspine_get_anim_info(sspine_anim anim) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_anim_info res; + _sspine_clear(&res, sizeof(res)); + const spAnimation* sp_anim = _sspine_lookup_skeleton_anim(anim.skeleton_id, anim.index); + if (sp_anim) { + res.valid = true; + res.index = anim.index; + res.duration = sp_anim->duration; + res.name = _sspine_string(sp_anim->name); + if (res.name.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + } + return res; +} + +SOKOL_API_IMPL void sspine_clear_animation_tracks(sspine_instance instance_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (_sspine_instance_and_deps_valid(instance)) { + SOKOL_ASSERT(instance->sp_anim_state); + spAnimationState_clearTracks(instance->sp_anim_state); + } +} + +SOKOL_API_IMPL void sspine_clear_animation_track(sspine_instance instance_id, int track_index) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (_sspine_instance_and_deps_valid(instance)) { + SOKOL_ASSERT(instance->sp_anim_state); + spAnimationState_clearTrack(instance->sp_anim_state, track_index); + } +} + +SOKOL_API_IMPL void sspine_set_animation(sspine_instance instance_id, sspine_anim anim, int track_index, bool loop) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + spAnimation* sp_anim = _sspine_lookup_instance_anim(instance_id.id, anim.skeleton_id, anim.index); + if (sp_anim) { + // NOTE: at this point, instance is guaranteed to be valid + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + SOKOL_ASSERT(instance); + spAnimationState_setAnimation(instance->sp_anim_state, track_index, sp_anim, loop?1:0); + } +} + +SOKOL_API_IMPL void sspine_add_animation(sspine_instance instance_id, sspine_anim anim, int track_index, bool loop, float delay) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + spAnimation* sp_anim = _sspine_lookup_instance_anim(instance_id.id, anim.skeleton_id, anim.index); + if (sp_anim) { + // NOTE: at this point, instance is guaranteed to be valid + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + SOKOL_ASSERT(instance); + SOKOL_ASSERT(instance->sp_anim_state); + spAnimationState_addAnimation(instance->sp_anim_state, track_index, sp_anim, loop?1:0, delay); + } +} + +SOKOL_API_IMPL void sspine_set_empty_animation(sspine_instance instance_id, int track_index, float mix_duration) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (_sspine_instance_and_deps_valid(instance)) { + SOKOL_ASSERT(instance->sp_anim_state); + spAnimationState_setEmptyAnimation(instance->sp_anim_state, track_index, mix_duration); + } +} + +SOKOL_API_IMPL void sspine_add_empty_animation(sspine_instance instance_id, int track_index, float mix_duration, float delay) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (_sspine_instance_and_deps_valid(instance)) { + SOKOL_ASSERT(instance->sp_anim_state); + spAnimationState_addEmptyAnimation(instance->sp_anim_state, track_index, mix_duration, delay); + } +} + +SOKOL_API_IMPL int sspine_num_bones(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return skeleton->sp_skel_data->bonesCount; + } + return 0; +} + +SOKOL_API_IMPL sspine_bone sspine_bone_by_name(sspine_skeleton skeleton_id, const char* name) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(name); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + spBoneData* sp_bone_data = spSkeletonData_findBone(skeleton->sp_skel_data, name); + if (sp_bone_data) { + return _sspine_bone(skeleton_id.id, sp_bone_data->index); + } + } + return _sspine_bone(0, 0); +} + +SOKOL_API_IMPL sspine_bone sspine_bone_by_index(sspine_skeleton skeleton_id, int index) { + return _sspine_bone(skeleton_id.id, index); +} + +SOKOL_API_IMPL bool sspine_bone_valid(sspine_bone bone) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(bone.skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return (bone.index >= 0) && (bone.index < skeleton->sp_skel_data->bonesCount); + } + return false; +} + +SOKOL_API_IMPL bool sspine_bone_equal(sspine_bone first, sspine_bone second) { + return (first.skeleton_id == second.skeleton_id) && (first.index == second.index); +} + +SOKOL_API_IMPL sspine_bone_info sspine_get_bone_info(sspine_bone bone) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_bone_info res; + _sspine_clear(&res, sizeof(res)); + const spBoneData* sp_bone_data = _sspine_lookup_bone_data(bone.skeleton_id, bone.index); + if (sp_bone_data) { + SOKOL_ASSERT(sp_bone_data->index == bone.index); + SOKOL_ASSERT(sp_bone_data->name); + res.valid = true; + res.index = sp_bone_data->index; + if (sp_bone_data->parent) { + res.parent_bone = _sspine_bone(bone.skeleton_id, sp_bone_data->parent->index); + } + res.length = sp_bone_data->length; + res.pose.position.x = sp_bone_data->x; + res.pose.position.y = sp_bone_data->y; + res.pose.rotation = sp_bone_data->rotation; + res.pose.scale.x = sp_bone_data->scaleX; + res.pose.scale.y = sp_bone_data->scaleY; + res.pose.shear.x = sp_bone_data->shearX; + res.pose.shear.y = sp_bone_data->shearY; + res.color.r = sp_bone_data->color.r; + res.color.g = sp_bone_data->color.g; + res.color.b = sp_bone_data->color.b; + res.color.a = sp_bone_data->color.a; + res.name = _sspine_string(sp_bone_data->name); + if (res.name.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + } + return res; +} + +SOKOL_API_IMPL void sspine_set_bone_transform(sspine_instance instance_id, sspine_bone bone, const sspine_bone_transform* transform) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(transform); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + sp_bone->x = transform->position.x; + sp_bone->y = transform->position.y; + sp_bone->rotation = transform->rotation; + sp_bone->scaleX = transform->scale.x; + sp_bone->scaleY = transform->scale.y; + sp_bone->shearX = transform->shear.x; + sp_bone->shearY = transform->shear.y; + } +} + +SOKOL_API_IMPL void sspine_set_bone_position(sspine_instance instance_id, sspine_bone bone, sspine_vec2 position) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + sp_bone->x = position.x; + sp_bone->y = position.y; + } +} + +SOKOL_API_IMPL void sspine_set_bone_rotation(sspine_instance instance_id, sspine_bone bone, float rotation) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + sp_bone->rotation = rotation; + } +} + +SOKOL_API_IMPL void sspine_set_bone_scale(sspine_instance instance_id, sspine_bone bone, sspine_vec2 scale) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + sp_bone->scaleX = scale.x; + sp_bone->scaleY = scale.y; + } +} + +SOKOL_API_IMPL void sspine_set_bone_shear(sspine_instance instance_id, sspine_bone bone, sspine_vec2 shear) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + sp_bone->shearX = shear.x; + sp_bone->shearY = shear.y; + } +} + +SOKOL_API_IMPL sspine_bone_transform sspine_get_bone_transform(sspine_instance instance_id, sspine_bone bone) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_bone_transform res; + _sspine_clear(&res, sizeof(res)); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + res.position.x = sp_bone->x; + res.position.y = sp_bone->y; + res.rotation = sp_bone->rotation; + res.scale.x = sp_bone->scaleX; + res.scale.y = sp_bone->scaleY; + res.shear.x = sp_bone->shearX; + res.shear.y = sp_bone->shearY; + } + return res; +} + +SOKOL_API_IMPL sspine_vec2 sspine_get_bone_position(sspine_instance instance_id, sspine_bone bone) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_vec2 res; + _sspine_clear(&res, sizeof(res)); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + res.x = sp_bone->x; + res.y = sp_bone->y; + } + return res; +} + +SOKOL_API_IMPL float sspine_get_bone_rotation(sspine_instance instance_id, sspine_bone bone) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + return sp_bone->rotation; + } else { + return 0.0f; + } +} + +SOKOL_API_IMPL sspine_vec2 sspine_get_bone_scale(sspine_instance instance_id, sspine_bone bone) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_vec2 res; + _sspine_clear(&res, sizeof(res)); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + res.x = sp_bone->scaleX; + res.y = sp_bone->scaleY; + } + return res; +} + +SOKOL_API_IMPL sspine_vec2 sspine_get_bone_shear(sspine_instance instance_id, sspine_bone bone) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_vec2 res; + _sspine_clear(&res, sizeof(res)); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + res.x = sp_bone->shearX; + res.y = sp_bone->shearY; + } + return res; +} + +SOKOL_API_IMPL sspine_vec2 sspine_get_bone_world_position(sspine_instance instance_id, sspine_bone bone) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_vec2 res; + _sspine_clear(&res, sizeof(res)); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + res.x = sp_bone->worldX; + res.y = sp_bone->worldY; + } + return res; +} + +SOKOL_API_IMPL sspine_vec2 sspine_bone_local_to_world(sspine_instance instance_id, sspine_bone bone, sspine_vec2 local_pos) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_vec2 res; + _sspine_clear(&res, sizeof(res)); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + spBone_localToWorld(sp_bone, local_pos.x, local_pos.y, &res.x, &res.y); + } + return res; +} + +SOKOL_API_IMPL sspine_vec2 sspine_bone_world_to_local(sspine_instance instance_id, sspine_bone bone, sspine_vec2 world_pos) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_vec2 res; + _sspine_clear(&res, sizeof(res)); + spBone* sp_bone = _sspine_lookup_bone(instance_id.id, bone.skeleton_id, bone.index); + if (sp_bone) { + spBone_worldToLocal(sp_bone, world_pos.x, world_pos.y, &res.x, &res.y); + } + return res; +} + +SOKOL_API_IMPL int sspine_num_slots(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return skeleton->sp_skel_data->slotsCount; + } + return 0; +} + +SOKOL_API_IMPL sspine_slot sspine_slot_by_name(sspine_skeleton skeleton_id, const char* name) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(name); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + spSlotData* sp_slot_data = spSkeletonData_findSlot(skeleton->sp_skel_data, name); + if (sp_slot_data) { + return _sspine_slot(skeleton_id.id, sp_slot_data->index); + } + } + return _sspine_slot(0, 0); +} + +SOKOL_API_IMPL sspine_slot sspine_slot_by_index(sspine_skeleton skeleton_id, int index) { + return _sspine_slot(skeleton_id.id, index); +} + +SOKOL_API_IMPL bool sspine_slot_valid(sspine_slot slot) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(slot.skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return (slot.index >= 0) && (slot.index < skeleton->sp_skel_data->slotsCount); + } + return false; +} + +SOKOL_API_IMPL bool sspine_slot_equal(sspine_slot first, sspine_slot second) { + return (first.skeleton_id == second.skeleton_id) && (first.index == second.index); +} + +SOKOL_API_IMPL sspine_slot_info sspine_get_slot_info(sspine_slot slot) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_slot_info res; + _sspine_clear(&res, sizeof(res)); + const spSlotData* sp_slot_data = _sspine_lookup_slot_data(slot.skeleton_id, slot.index); + if (sp_slot_data) { + SOKOL_ASSERT(sp_slot_data->index == slot.index); + SOKOL_ASSERT(sp_slot_data->name); + SOKOL_ASSERT(sp_slot_data->boneData); + res.valid = true; + res.index = sp_slot_data->index; + res.bone = _sspine_bone(slot.skeleton_id, sp_slot_data->boneData->index); + res.color.r = sp_slot_data->color.r; + res.color.g = sp_slot_data->color.g; + res.color.b = sp_slot_data->color.b; + res.color.a = sp_slot_data->color.a; + res.attachment_name = _sspine_string(sp_slot_data->attachmentName); + if (res.attachment_name.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + res.name = _sspine_string(sp_slot_data->name); + if (res.name.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + } + return res; +} + +SOKOL_API_IMPL void sspine_set_slot_color(sspine_instance instance_id, sspine_slot slot, sspine_color color) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + spSlot* sp_slot = _sspine_lookup_slot(instance_id.id, slot.skeleton_id, slot.index); + if (sp_slot) { + sp_slot->color.r = color.r; + sp_slot->color.g = color.g; + sp_slot->color.b = color.b; + sp_slot->color.a = color.a; + } +} + +SOKOL_API_IMPL sspine_color sspine_get_slot_color(sspine_instance instance_id, sspine_slot slot) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_color color; + _sspine_clear(&color, sizeof(color)); + spSlot* sp_slot = _sspine_lookup_slot(instance_id.id, slot.skeleton_id, slot.index); + if (sp_slot) { + color.r = sp_slot->color.r; + color.g = sp_slot->color.g; + color.b = sp_slot->color.b; + color.a = sp_slot->color.a; + } + return color; +} + +SOKOL_API_IMPL int sspine_num_events(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return skeleton->sp_skel_data->eventsCount; + } + return 0; +} + +SOKOL_API_IMPL sspine_event sspine_event_by_name(sspine_skeleton skeleton_id, const char* name) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(name); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + SOKOL_ASSERT(skeleton->sp_skel_data->events); + // spEventData has no embedded index, so we need to loop over the events + for (int i = 0; i < skeleton->sp_skel_data->eventsCount; i++) { + SOKOL_ASSERT(skeleton->sp_skel_data->events[i]); + SOKOL_ASSERT(skeleton->sp_skel_data->events[i]->name); + if (0 == strcmp(skeleton->sp_skel_data->events[i]->name, name)) { + return _sspine_event(skeleton_id.id, i); + } + } + } + return _sspine_event(0, 0); +} + +SOKOL_API_IMPL sspine_event sspine_event_by_index(sspine_skeleton skeleton_id, int index) { + return _sspine_event(skeleton_id.id, index); +} + +SOKOL_API_IMPL bool sspine_event_valid(sspine_event event) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(event.skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return (event.index >= 0) && (event.index < skeleton->sp_skel_data->eventsCount); + } + return false; +} + +SOKOL_API_IMPL bool sspine_event_equal(sspine_event first, sspine_event second) { + return (first.skeleton_id == second.skeleton_id) && (first.index == second.index); +} + +SOKOL_API_IMPL sspine_event_info sspine_get_event_info(sspine_event event) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_event_info res; + _sspine_clear(&res, sizeof(res)); + const spEventData* sp_event_data = _sspine_lookup_event_data(event.skeleton_id, event.index); + if (sp_event_data) { + res.valid = true; + res.index = event.index; + res.int_value = sp_event_data->intValue; + res.float_value = sp_event_data->floatValue; + res.volume = sp_event_data->volume; + res.balance = sp_event_data->balance; + res.name = _sspine_string(sp_event_data->name); + if (res.name.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + res.string_value = _sspine_string(sp_event_data->stringValue); + if (res.string_value.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + res.audio_path = _sspine_string(sp_event_data->audioPath); + if (res.audio_path.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + } + return res; +} + +SOKOL_API_IMPL int sspine_num_iktargets(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return skeleton->sp_skel_data->ikConstraintsCount; + } + return 0; +} + +SOKOL_API_IMPL sspine_iktarget sspine_iktarget_by_name(sspine_skeleton skeleton_id, const char* name) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(name); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + SOKOL_ASSERT(skeleton->sp_skel_data->ikConstraints); + // spIkConstraintData has no embedded index, so we need to loop over the events + for (int i = 0; i < skeleton->sp_skel_data->ikConstraintsCount; i++) { + SOKOL_ASSERT(skeleton->sp_skel_data->ikConstraints[i]); + SOKOL_ASSERT(skeleton->sp_skel_data->ikConstraints[i]->name); + if (0 == strcmp(skeleton->sp_skel_data->ikConstraints[i]->name, name)) { + return _sspine_iktarget(skeleton_id.id, i); + } + } + } + return _sspine_iktarget(0, 0); +} + +SOKOL_API_IMPL sspine_iktarget sspine_iktarget_by_index(sspine_skeleton skeleton_id, int index) { + return _sspine_iktarget(skeleton_id.id, index); +} + +SOKOL_API_IMPL bool sspine_iktarget_valid(sspine_iktarget iktarget) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(iktarget.skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return (iktarget.index >= 0) && (iktarget.index < skeleton->sp_skel_data->ikConstraintsCount); + } + return false; +} + +SOKOL_API_IMPL bool sspine_iktarget_equal(sspine_iktarget first, sspine_iktarget second) { + return (first.skeleton_id == second.skeleton_id) && (first.index == second.index); +} + +SOKOL_API_IMPL sspine_iktarget_info sspine_get_iktarget_info(sspine_iktarget iktarget) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_iktarget_info res; + _sspine_clear(&res, sizeof(res)); + const spIkConstraintData* ik_data = _sspine_lookup_ikconstraint_data(iktarget.skeleton_id, iktarget.index); + if (ik_data) { + res.valid = true; + res.index = iktarget.index; + res.target_bone = _sspine_bone(iktarget.skeleton_id, ik_data->target->index); + res.name = _sspine_string(ik_data->name); + if (res.name.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + } + return res; +} + +SOKOL_API_IMPL void sspine_set_iktarget_world_pos(sspine_instance instance_id, sspine_iktarget iktarget, sspine_vec2 world_pos) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + spIkConstraint* ik_data = _sspine_lookup_ikconstraint(instance_id.id, iktarget.skeleton_id, iktarget.index); + if (ik_data) { + spBone* bone = ik_data->target; + spBone* parent_bone = bone->parent; + if (parent_bone) { + spBone_worldToLocal(parent_bone, world_pos.x, world_pos.y, &bone->x, &bone->y); + } + } +} + +SOKOL_API_IMPL int sspine_num_skins(sspine_skeleton skeleton_id) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return skeleton->sp_skel_data->skinsCount; + } + return 0; +} + +SOKOL_API_IMPL sspine_skin sspine_skin_by_name(sspine_skeleton skeleton_id, const char* name) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + SOKOL_ASSERT(name); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skeleton_id.id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + SOKOL_ASSERT(skeleton->sp_skel_data->skins); + // spSkin has no embedded index, so we need to loop over the skins + for (int i = 0; i < skeleton->sp_skel_data->skinsCount; i++) { + SOKOL_ASSERT(skeleton->sp_skel_data->skins[i]); + SOKOL_ASSERT(skeleton->sp_skel_data->skins[i]->name); + if (0 == strcmp(skeleton->sp_skel_data->skins[i]->name, name)) { + return _sspine_skin(skeleton_id.id, i); + } + } + } + return _sspine_skin(0, 0); +} + +SOKOL_API_IMPL sspine_skin sspine_skin_by_index(sspine_skeleton skeleton_id, int index) { + return _sspine_skin(skeleton_id.id, index); +} + +SOKOL_API_IMPL bool sspine_skin_valid(sspine_skin skin) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_skeleton_t* skeleton = _sspine_lookup_skeleton(skin.skeleton_id); + if (_sspine_skeleton_and_deps_valid(skeleton)) { + SOKOL_ASSERT(skeleton->sp_skel_data); + return (skin.index >= 0) && (skin.index < skeleton->sp_skel_data->skinsCount); + } + return false; +} + +SOKOL_API_IMPL bool sspine_skin_equal(sspine_skin first, sspine_skin second) { + return (first.skeleton_id == second.skeleton_id) && (first.index == second.index); +} + +SOKOL_API_IMPL sspine_skin_info sspine_get_skin_info(sspine_skin skin) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + sspine_skin_info res; + _sspine_clear(&res, sizeof(res)); + const spSkin* sp_skin = _sspine_lookup_skin(skin.skeleton_id, skin.index); + if (sp_skin) { + res.valid = true; + res.index = skin.index; + res.name = _sspine_string(sp_skin->name); + if (res.name.truncated) { + _SSPINE_WARN(STRING_TRUNCATED); + } + } + return res; +} + +SOKOL_SPINE_API_DECL void sspine_set_skin(sspine_instance instance_id, sspine_skin skin) { + SOKOL_ASSERT(_SSPINE_INIT_COOKIE == _sspine.init_cookie); + _sspine_instance_t* instance = _sspine_lookup_instance(instance_id.id); + if (_sspine_instance_and_deps_valid(instance) && (instance->skel.id == skin.skeleton_id)) { + SOKOL_ASSERT(instance->sp_skel); + SOKOL_ASSERT(instance->sp_anim_state); + // clear any currently set skinset + instance->skinset.id = SSPINE_INVALID_ID; + instance->skinset.ptr = 0; + spSkin* sp_skin = _sspine_lookup_skin(skin.skeleton_id, skin.index); + if (sp_skin) { + spSkeleton_setSkin(instance->sp_skel, 0); + spSkeleton_setSkin(instance->sp_skel, sp_skin); + spSkeleton_setSlotsToSetupPose(instance->sp_skel); + spAnimationState_apply(instance->sp_anim_state, instance->sp_skel); + } + } +} + +#endif // SOKOL_SPINE_IMPL +#endif // SOKOL_SPINE_INCLUDED diff --git a/thirdparty/ssl/README.md b/thirdparty/ssl/README.md new file mode 100644 index 0000000..c83ee30 --- /dev/null +++ b/thirdparty/ssl/README.md @@ -0,0 +1,4 @@ +# Sokol Static Library +This is what makes cross-compilation of graphical applications with the C backend possible in ASPL. + +TODO: Add more documentation \ No newline at end of file diff --git a/thirdparty/ssl/glbind.h b/thirdparty/ssl/glbind.h new file mode 100644 index 0000000..1b18f25 --- /dev/null +++ b/thirdparty/ssl/glbind.h @@ -0,0 +1,27334 @@ +/* +OpenGL API loader. Choice of public domain or MIT-0. See license statements at the end of this file. +glbind - v4.6.15 - 2023-05-26 + +David Reid - davidreidsoftware@gmail.com +*/ + +#ifndef GLBIND_H +#define GLBIND_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* For platform detection, I'm just assuming GLX if it's not Win32. Happy to look at making this more flexible, especially when it comes to GLES. */ +#if defined(_WIN32) + #define GLBIND_WGL +#else + #define GLBIND_GLX +#endif + +/* +The official OpenGL headers have a dependency on a header called khrplatform.h. From what I can see it's mainly just for sized types. Since glbind is a +single header, and that we can't just copy-and-paste the contents of khrplatform.h due to licensing, we need to do our own sized type declarations. +*/ +#ifndef __khrplatform_h_ +#include /* For size_t. */ +#ifdef _MSC_VER + #if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlanguage-extension-token" + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif + typedef signed __int8 khronos_int8_t; + typedef unsigned __int8 khronos_uint8_t; + typedef signed __int16 khronos_int16_t; + typedef unsigned __int16 khronos_uint16_t; + typedef signed __int32 khronos_int32_t; + typedef unsigned __int32 khronos_uint32_t; + typedef signed __int64 khronos_int64_t; + typedef unsigned __int64 khronos_uint64_t; + #if defined(__clang__) + #pragma GCC diagnostic pop + #endif +#else + #define MA_HAS_STDINT + #include + typedef int8_t khronos_int8_t; + typedef uint8_t khronos_uint8_t; + typedef int16_t khronos_int16_t; + typedef uint16_t khronos_uint16_t; + typedef int32_t khronos_int32_t; + typedef uint32_t khronos_uint32_t; + typedef int64_t khronos_int64_t; + typedef uint64_t khronos_uint64_t; +#endif + +#ifdef MA_HAS_STDINT + typedef uintptr_t khronos_uintptr_t; + typedef intptr_t khronos_intptr_t; + typedef uintptr_t khronos_usize_t; + typedef intptr_t khronos_ssize_t; +#else + #if defined(_WIN32) + #if defined(_WIN64) + typedef khronos_uint64_t khronos_uintptr_t; + typedef khronos_int64_t khronos_intptr_t; + typedef khronos_uint64_t khronos_usize_t; + typedef khronos_int64_t khronos_ssize_t; + #else + typedef khronos_uint32_t khronos_uintptr_t; + typedef khronos_int32_t khronos_intptr_t; + typedef khronos_uint32_t khronos_usize_t; + typedef khronos_int32_t khronos_ssize_t; + #endif + #elif defined(__GNUC__) + #if defined(__LP64__) + typedef khronos_uint64_t khronos_uintptr_t; + typedef khronos_int64_t khronos_intptr_t; + typedef khronos_uint64_t khronos_usize_t; + typedef khronos_int64_t khronos_ssize_t; + #else + typedef khronos_uint32_t khronos_uintptr_t; + typedef khronos_int32_t khronos_intptr_t; + typedef khronos_uint32_t khronos_usize_t; + typedef khronos_int32_t khronos_ssize_t; + #endif + #else + typedef khronos_uint64_t khronos_uintptr_t; + typedef khronos_int64_t khronos_intptr_t; + typedef khronos_uint64_t khronos_usize_t; /* Fallback. */ + typedef khronos_int64_t khronos_ssize_t; + #endif +#endif +typedef float khronos_float_t; +#endif /* __khrplatform_h_ */ + +/* Platform headers. */ +#if defined(GLBIND_WGL) + #include /* Can we remove this dependency? */ +#endif +#if defined(GLBIND_GLX) + #if !defined(GLBIND_NO_XLIB_HEADERS) + #include + #include + + typedef Display glbind_Display; + typedef Visual glbind_Visual; + typedef VisualID glbind_VisualID; + typedef XVisualInfo glbind_XVisualInfo; + typedef XSetWindowAttributes glbind_XSetWindowAttributes; + typedef XID glbind_XID; + typedef Window glbind_Window; + typedef Colormap glbind_Colormap; + typedef Pixmap glbind_Pixmap; + typedef Font glbind_Font; + typedef Atom glbind_Atom; + typedef Cursor glbind_Cursor; + typedef Bool glbind_Bool; + typedef Status glbind_Status; + + #define glbind_None None + #define glbind_AllocNone AllocNone + + #define glbind_CWBorderPixel CWBorderPixel + #define glbind_CWColormap CWColormap + + #define glbind_InputOutput InputOutput + #else + typedef void* glbind_Display; + typedef void* glbind_Visual; + typedef unsigned long glbind_VisualID; + typedef unsigned long glbind_XID; + typedef glbind_XID glbind_Window; + typedef glbind_XID glbind_Colormap; + typedef glbind_XID glbind_Pixmap; + typedef glbind_XID glbind_Font; + typedef glbind_XID glbind_Atom; + typedef glbind_XID glbind_Cursor; + typedef int glbind_Bool; + typedef int glbind_Status; + + #define glbind_None 0 + #define glbind_AllocNone 0 + + #define glbind_CWBorderPixel (1 << 3) + #define glbind_CWColormap (1 << 13) + + #define glbind_InputOutput 1 + + + /* We need to declare our own version of glbind_XVisualInfo*/ + typedef struct + { + glbind_Visual* visual; + glbind_VisualID visualid; + int screen; + int depth; + int class; + unsigned long red_mask; + unsigned long green_mask; + unsigned long blue_mask; + int colormap_size; + int bits_per_rgb; + } glbind_XVisualInfo; + + /* We need to declare our own version of glbind_XSetWindowAttributes*/ + typedef struct + { + glbind_Pixmap background_pixmap; + unsigned long background_pixel; + glbind_Pixmap border_pixmap; + unsigned long border_pixel; + int bit_gravity; + int win_gravity; + int backing_store; + unsigned long backing_planes; + unsigned long backing_pixel; + int save_under; + long event_mask; + long do_not_propagate_mask; + int override_redirect; + glbind_Colormap colormap; + glbind_Cursor cursor; + } glbind_XSetWindowAttributes; + #endif +#endif + +/* +The official OpenGL headers have traditionally defined their APIs with APIENTRY, APIENTRYP and GLAPI. I'm including these just in case +some program wants to use them. +*/ +#ifndef APIENTRY +#define APIENTRY +#endif +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif +#ifndef GLAPI +#define GLAPI extern +#endif + + + +#ifndef GL_VERSION_1_0 +#define GL_VERSION_1_0 1 +typedef void GLvoid; +typedef unsigned int GLenum; +typedef khronos_float_t GLfloat; +typedef int GLint; +typedef int GLsizei; +typedef unsigned int GLbitfield; +typedef double GLdouble; +typedef unsigned int GLuint; +typedef unsigned char GLboolean; +typedef khronos_uint8_t GLubyte; +typedef khronos_int8_t GLbyte; +typedef khronos_int16_t GLshort; +typedef khronos_uint16_t GLushort; +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_FALSE 0 +#define GL_TRUE 1 +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 +#define GL_QUADS 0x0007 +#define GL_NEVER 0x0200 +#define GL_LESS 0x0201 +#define GL_EQUAL 0x0202 +#define GL_LEQUAL 0x0203 +#define GL_GREATER 0x0204 +#define GL_NOTEQUAL 0x0205 +#define GL_GEQUAL 0x0206 +#define GL_ALWAYS 0x0207 +#define GL_ZERO 0 +#define GL_ONE 1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_SRC_ALPHA_SATURATE 0x0308 +#define GL_NONE 0 +#define GL_FRONT_LEFT 0x0400 +#define GL_FRONT_RIGHT 0x0401 +#define GL_BACK_LEFT 0x0402 +#define GL_BACK_RIGHT 0x0403 +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_LEFT 0x0406 +#define GL_RIGHT 0x0407 +#define GL_FRONT_AND_BACK 0x0408 +#define GL_NO_ERROR 0 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_OPERATION 0x0502 +#define GL_OUT_OF_MEMORY 0x0505 +#define GL_CW 0x0900 +#define GL_CCW 0x0901 +#define GL_POINT_SIZE 0x0B11 +#define GL_POINT_SIZE_RANGE 0x0B12 +#define GL_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_LINE_SMOOTH 0x0B20 +#define GL_LINE_WIDTH 0x0B21 +#define GL_LINE_WIDTH_RANGE 0x0B22 +#define GL_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_POLYGON_MODE 0x0B40 +#define GL_POLYGON_SMOOTH 0x0B41 +#define GL_CULL_FACE 0x0B44 +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_FRONT_FACE 0x0B46 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_TEST 0x0B71 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_STENCIL_TEST 0x0B90 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_VIEWPORT 0x0BA2 +#define GL_DITHER 0x0BD0 +#define GL_BLEND_DST 0x0BE0 +#define GL_BLEND_SRC 0x0BE1 +#define GL_BLEND 0x0BE2 +#define GL_LOGIC_OP_MODE 0x0BF0 +#define GL_DRAW_BUFFER 0x0C01 +#define GL_READ_BUFFER 0x0C02 +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_DOUBLEBUFFER 0x0C32 +#define GL_STEREO 0x0C33 +#define GL_LINE_SMOOTH_HINT 0x0C52 +#define GL_POLYGON_SMOOTH_HINT 0x0C53 +#define GL_UNPACK_SWAP_BYTES 0x0CF0 +#define GL_UNPACK_LSB_FIRST 0x0CF1 +#define GL_UNPACK_ROW_LENGTH 0x0CF2 +#define GL_UNPACK_SKIP_ROWS 0x0CF3 +#define GL_UNPACK_SKIP_PIXELS 0x0CF4 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_PACK_SWAP_BYTES 0x0D00 +#define GL_PACK_LSB_FIRST 0x0D01 +#define GL_PACK_ROW_LENGTH 0x0D02 +#define GL_PACK_SKIP_ROWS 0x0D03 +#define GL_PACK_SKIP_PIXELS 0x0D04 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_TEXTURE_1D 0x0DE0 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_TEXTURE_WIDTH 0x1000 +#define GL_TEXTURE_HEIGHT 0x1001 +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#define GL_DONT_CARE 0x1100 +#define GL_FASTEST 0x1101 +#define GL_NICEST 0x1102 +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_INT 0x1404 +#define GL_UNSIGNED_INT 0x1405 +#define GL_FLOAT 0x1406 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_CLEAR 0x1500 +#define GL_AND 0x1501 +#define GL_AND_REVERSE 0x1502 +#define GL_COPY 0x1503 +#define GL_AND_INVERTED 0x1504 +#define GL_NOOP 0x1505 +#define GL_XOR 0x1506 +#define GL_OR 0x1507 +#define GL_NOR 0x1508 +#define GL_EQUIV 0x1509 +#define GL_INVERT 0x150A +#define GL_OR_REVERSE 0x150B +#define GL_COPY_INVERTED 0x150C +#define GL_OR_INVERTED 0x150D +#define GL_NAND 0x150E +#define GL_SET 0x150F +#define GL_TEXTURE 0x1702 +#define GL_COLOR 0x1800 +#define GL_DEPTH 0x1801 +#define GL_STENCIL 0x1802 +#define GL_STENCIL_INDEX 0x1901 +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_RED 0x1903 +#define GL_GREEN 0x1904 +#define GL_BLUE 0x1905 +#define GL_ALPHA 0x1906 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +#define GL_POINT 0x1B00 +#define GL_LINE 0x1B01 +#define GL_FILL 0x1B02 +#define GL_KEEP 0x1E00 +#define GL_REPLACE 0x1E01 +#define GL_INCR 0x1E02 +#define GL_DECR 0x1E03 +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_REPEAT 0x2901 +#define GL_CURRENT_BIT 0x00000001 +#define GL_POINT_BIT 0x00000002 +#define GL_LINE_BIT 0x00000004 +#define GL_POLYGON_BIT 0x00000008 +#define GL_POLYGON_STIPPLE_BIT 0x00000010 +#define GL_PIXEL_MODE_BIT 0x00000020 +#define GL_LIGHTING_BIT 0x00000040 +#define GL_FOG_BIT 0x00000080 +#define GL_ACCUM_BUFFER_BIT 0x00000200 +#define GL_VIEWPORT_BIT 0x00000800 +#define GL_TRANSFORM_BIT 0x00001000 +#define GL_ENABLE_BIT 0x00002000 +#define GL_HINT_BIT 0x00008000 +#define GL_EVAL_BIT 0x00010000 +#define GL_LIST_BIT 0x00020000 +#define GL_TEXTURE_BIT 0x00040000 +#define GL_SCISSOR_BIT 0x00080000 +#define GL_ALL_ATTRIB_BITS 0xFFFFFFFF +#define GL_QUAD_STRIP 0x0008 +#define GL_POLYGON 0x0009 +#define GL_ACCUM 0x0100 +#define GL_LOAD 0x0101 +#define GL_RETURN 0x0102 +#define GL_MULT 0x0103 +#define GL_ADD 0x0104 +#define GL_AUX0 0x0409 +#define GL_AUX1 0x040A +#define GL_AUX2 0x040B +#define GL_AUX3 0x040C +#define GL_2D 0x0600 +#define GL_3D 0x0601 +#define GL_3D_COLOR 0x0602 +#define GL_3D_COLOR_TEXTURE 0x0603 +#define GL_4D_COLOR_TEXTURE 0x0604 +#define GL_PASS_THROUGH_TOKEN 0x0700 +#define GL_POINT_TOKEN 0x0701 +#define GL_LINE_TOKEN 0x0702 +#define GL_POLYGON_TOKEN 0x0703 +#define GL_BITMAP_TOKEN 0x0704 +#define GL_DRAW_PIXEL_TOKEN 0x0705 +#define GL_COPY_PIXEL_TOKEN 0x0706 +#define GL_LINE_RESET_TOKEN 0x0707 +#define GL_EXP 0x0800 +#define GL_EXP2 0x0801 +#define GL_COEFF 0x0A00 +#define GL_ORDER 0x0A01 +#define GL_DOMAIN 0x0A02 +#define GL_PIXEL_MAP_I_TO_I 0x0C70 +#define GL_PIXEL_MAP_S_TO_S 0x0C71 +#define GL_PIXEL_MAP_I_TO_R 0x0C72 +#define GL_PIXEL_MAP_I_TO_G 0x0C73 +#define GL_PIXEL_MAP_I_TO_B 0x0C74 +#define GL_PIXEL_MAP_I_TO_A 0x0C75 +#define GL_PIXEL_MAP_R_TO_R 0x0C76 +#define GL_PIXEL_MAP_G_TO_G 0x0C77 +#define GL_PIXEL_MAP_B_TO_B 0x0C78 +#define GL_PIXEL_MAP_A_TO_A 0x0C79 +#define GL_CURRENT_COLOR 0x0B00 +#define GL_CURRENT_INDEX 0x0B01 +#define GL_CURRENT_NORMAL 0x0B02 +#define GL_CURRENT_TEXTURE_COORDS 0x0B03 +#define GL_CURRENT_RASTER_COLOR 0x0B04 +#define GL_CURRENT_RASTER_INDEX 0x0B05 +#define GL_CURRENT_RASTER_TEXTURE_COORDS 0x0B06 +#define GL_CURRENT_RASTER_POSITION 0x0B07 +#define GL_CURRENT_RASTER_POSITION_VALID 0x0B08 +#define GL_CURRENT_RASTER_DISTANCE 0x0B09 +#define GL_POINT_SMOOTH 0x0B10 +#define GL_LINE_STIPPLE 0x0B24 +#define GL_LINE_STIPPLE_PATTERN 0x0B25 +#define GL_LINE_STIPPLE_REPEAT 0x0B26 +#define GL_LIST_MODE 0x0B30 +#define GL_MAX_LIST_NESTING 0x0B31 +#define GL_LIST_BASE 0x0B32 +#define GL_LIST_INDEX 0x0B33 +#define GL_POLYGON_STIPPLE 0x0B42 +#define GL_EDGE_FLAG 0x0B43 +#define GL_LIGHTING 0x0B50 +#define GL_LIGHT_MODEL_LOCAL_VIEWER 0x0B51 +#define GL_LIGHT_MODEL_TWO_SIDE 0x0B52 +#define GL_LIGHT_MODEL_AMBIENT 0x0B53 +#define GL_SHADE_MODEL 0x0B54 +#define GL_COLOR_MATERIAL_FACE 0x0B55 +#define GL_COLOR_MATERIAL_PARAMETER 0x0B56 +#define GL_COLOR_MATERIAL 0x0B57 +#define GL_FOG 0x0B60 +#define GL_FOG_INDEX 0x0B61 +#define GL_FOG_DENSITY 0x0B62 +#define GL_FOG_START 0x0B63 +#define GL_FOG_END 0x0B64 +#define GL_FOG_MODE 0x0B65 +#define GL_FOG_COLOR 0x0B66 +#define GL_ACCUM_CLEAR_VALUE 0x0B80 +#define GL_MATRIX_MODE 0x0BA0 +#define GL_NORMALIZE 0x0BA1 +#define GL_MODELVIEW_STACK_DEPTH 0x0BA3 +#define GL_PROJECTION_STACK_DEPTH 0x0BA4 +#define GL_TEXTURE_STACK_DEPTH 0x0BA5 +#define GL_MODELVIEW_MATRIX 0x0BA6 +#define GL_PROJECTION_MATRIX 0x0BA7 +#define GL_TEXTURE_MATRIX 0x0BA8 +#define GL_ATTRIB_STACK_DEPTH 0x0BB0 +#define GL_ALPHA_TEST 0x0BC0 +#define GL_ALPHA_TEST_FUNC 0x0BC1 +#define GL_ALPHA_TEST_REF 0x0BC2 +#define GL_LOGIC_OP 0x0BF1 +#define GL_AUX_BUFFERS 0x0C00 +#define GL_INDEX_CLEAR_VALUE 0x0C20 +#define GL_INDEX_WRITEMASK 0x0C21 +#define GL_INDEX_MODE 0x0C30 +#define GL_RGBA_MODE 0x0C31 +#define GL_RENDER_MODE 0x0C40 +#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50 +#define GL_POINT_SMOOTH_HINT 0x0C51 +#define GL_FOG_HINT 0x0C54 +#define GL_TEXTURE_GEN_S 0x0C60 +#define GL_TEXTURE_GEN_T 0x0C61 +#define GL_TEXTURE_GEN_R 0x0C62 +#define GL_TEXTURE_GEN_Q 0x0C63 +#define GL_PIXEL_MAP_I_TO_I_SIZE 0x0CB0 +#define GL_PIXEL_MAP_S_TO_S_SIZE 0x0CB1 +#define GL_PIXEL_MAP_I_TO_R_SIZE 0x0CB2 +#define GL_PIXEL_MAP_I_TO_G_SIZE 0x0CB3 +#define GL_PIXEL_MAP_I_TO_B_SIZE 0x0CB4 +#define GL_PIXEL_MAP_I_TO_A_SIZE 0x0CB5 +#define GL_PIXEL_MAP_R_TO_R_SIZE 0x0CB6 +#define GL_PIXEL_MAP_G_TO_G_SIZE 0x0CB7 +#define GL_PIXEL_MAP_B_TO_B_SIZE 0x0CB8 +#define GL_PIXEL_MAP_A_TO_A_SIZE 0x0CB9 +#define GL_MAP_COLOR 0x0D10 +#define GL_MAP_STENCIL 0x0D11 +#define GL_INDEX_SHIFT 0x0D12 +#define GL_INDEX_OFFSET 0x0D13 +#define GL_RED_SCALE 0x0D14 +#define GL_RED_BIAS 0x0D15 +#define GL_ZOOM_X 0x0D16 +#define GL_ZOOM_Y 0x0D17 +#define GL_GREEN_SCALE 0x0D18 +#define GL_GREEN_BIAS 0x0D19 +#define GL_BLUE_SCALE 0x0D1A +#define GL_BLUE_BIAS 0x0D1B +#define GL_ALPHA_SCALE 0x0D1C +#define GL_ALPHA_BIAS 0x0D1D +#define GL_DEPTH_SCALE 0x0D1E +#define GL_DEPTH_BIAS 0x0D1F +#define GL_MAX_EVAL_ORDER 0x0D30 +#define GL_MAX_LIGHTS 0x0D31 +#define GL_MAX_CLIP_PLANES 0x0D32 +#define GL_MAX_PIXEL_MAP_TABLE 0x0D34 +#define GL_MAX_ATTRIB_STACK_DEPTH 0x0D35 +#define GL_MAX_MODELVIEW_STACK_DEPTH 0x0D36 +#define GL_MAX_NAME_STACK_DEPTH 0x0D37 +#define GL_MAX_PROJECTION_STACK_DEPTH 0x0D38 +#define GL_MAX_TEXTURE_STACK_DEPTH 0x0D39 +#define GL_INDEX_BITS 0x0D51 +#define GL_RED_BITS 0x0D52 +#define GL_GREEN_BITS 0x0D53 +#define GL_BLUE_BITS 0x0D54 +#define GL_ALPHA_BITS 0x0D55 +#define GL_DEPTH_BITS 0x0D56 +#define GL_STENCIL_BITS 0x0D57 +#define GL_ACCUM_RED_BITS 0x0D58 +#define GL_ACCUM_GREEN_BITS 0x0D59 +#define GL_ACCUM_BLUE_BITS 0x0D5A +#define GL_ACCUM_ALPHA_BITS 0x0D5B +#define GL_NAME_STACK_DEPTH 0x0D70 +#define GL_AUTO_NORMAL 0x0D80 +#define GL_MAP1_COLOR_4 0x0D90 +#define GL_MAP1_INDEX 0x0D91 +#define GL_MAP1_NORMAL 0x0D92 +#define GL_MAP1_TEXTURE_COORD_1 0x0D93 +#define GL_MAP1_TEXTURE_COORD_2 0x0D94 +#define GL_MAP1_TEXTURE_COORD_3 0x0D95 +#define GL_MAP1_TEXTURE_COORD_4 0x0D96 +#define GL_MAP1_VERTEX_3 0x0D97 +#define GL_MAP1_VERTEX_4 0x0D98 +#define GL_MAP2_COLOR_4 0x0DB0 +#define GL_MAP2_INDEX 0x0DB1 +#define GL_MAP2_NORMAL 0x0DB2 +#define GL_MAP2_TEXTURE_COORD_1 0x0DB3 +#define GL_MAP2_TEXTURE_COORD_2 0x0DB4 +#define GL_MAP2_TEXTURE_COORD_3 0x0DB5 +#define GL_MAP2_TEXTURE_COORD_4 0x0DB6 +#define GL_MAP2_VERTEX_3 0x0DB7 +#define GL_MAP2_VERTEX_4 0x0DB8 +#define GL_MAP1_GRID_DOMAIN 0x0DD0 +#define GL_MAP1_GRID_SEGMENTS 0x0DD1 +#define GL_MAP2_GRID_DOMAIN 0x0DD2 +#define GL_MAP2_GRID_SEGMENTS 0x0DD3 +#define GL_TEXTURE_COMPONENTS 0x1003 +#define GL_TEXTURE_BORDER 0x1005 +#define GL_AMBIENT 0x1200 +#define GL_DIFFUSE 0x1201 +#define GL_SPECULAR 0x1202 +#define GL_POSITION 0x1203 +#define GL_SPOT_DIRECTION 0x1204 +#define GL_SPOT_EXPONENT 0x1205 +#define GL_SPOT_CUTOFF 0x1206 +#define GL_CONSTANT_ATTENUATION 0x1207 +#define GL_LINEAR_ATTENUATION 0x1208 +#define GL_QUADRATIC_ATTENUATION 0x1209 +#define GL_COMPILE 0x1300 +#define GL_COMPILE_AND_EXECUTE 0x1301 +#define GL_2_BYTES 0x1407 +#define GL_3_BYTES 0x1408 +#define GL_4_BYTES 0x1409 +#define GL_EMISSION 0x1600 +#define GL_SHININESS 0x1601 +#define GL_AMBIENT_AND_DIFFUSE 0x1602 +#define GL_COLOR_INDEXES 0x1603 +#define GL_MODELVIEW 0x1700 +#define GL_PROJECTION 0x1701 +#define GL_COLOR_INDEX 0x1900 +#define GL_LUMINANCE 0x1909 +#define GL_LUMINANCE_ALPHA 0x190A +#define GL_BITMAP 0x1A00 +#define GL_RENDER 0x1C00 +#define GL_FEEDBACK 0x1C01 +#define GL_SELECT 0x1C02 +#define GL_FLAT 0x1D00 +#define GL_SMOOTH 0x1D01 +#define GL_S 0x2000 +#define GL_T 0x2001 +#define GL_R 0x2002 +#define GL_Q 0x2003 +#define GL_MODULATE 0x2100 +#define GL_DECAL 0x2101 +#define GL_TEXTURE_ENV_MODE 0x2200 +#define GL_TEXTURE_ENV_COLOR 0x2201 +#define GL_TEXTURE_ENV 0x2300 +#define GL_EYE_LINEAR 0x2400 +#define GL_OBJECT_LINEAR 0x2401 +#define GL_SPHERE_MAP 0x2402 +#define GL_TEXTURE_GEN_MODE 0x2500 +#define GL_OBJECT_PLANE 0x2501 +#define GL_EYE_PLANE 0x2502 +#define GL_CLAMP 0x2900 +#define GL_CLIP_PLANE0 0x3000 +#define GL_CLIP_PLANE1 0x3001 +#define GL_CLIP_PLANE2 0x3002 +#define GL_CLIP_PLANE3 0x3003 +#define GL_CLIP_PLANE4 0x3004 +#define GL_CLIP_PLANE5 0x3005 +#define GL_LIGHT0 0x4000 +#define GL_LIGHT1 0x4001 +#define GL_LIGHT2 0x4002 +#define GL_LIGHT3 0x4003 +#define GL_LIGHT4 0x4004 +#define GL_LIGHT5 0x4005 +#define GL_LIGHT6 0x4006 +#define GL_LIGHT7 0x4007 +typedef void (APIENTRYP PFNGLCULLFACEPROC)(GLenum mode); +typedef void (APIENTRYP PFNGLFRONTFACEPROC)(GLenum mode); +typedef void (APIENTRYP PFNGLHINTPROC)(GLenum target, GLenum mode); +typedef void (APIENTRYP PFNGLLINEWIDTHPROC)(GLfloat width); +typedef void (APIENTRYP PFNGLPOINTSIZEPROC)(GLfloat size); +typedef void (APIENTRYP PFNGLPOLYGONMODEPROC)(GLenum face, GLenum mode); +typedef void (APIENTRYP PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLTEXIMAGE1DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLDRAWBUFFERPROC)(GLenum buf); +typedef void (APIENTRYP PFNGLCLEARPROC)(GLbitfield mask); +typedef void (APIENTRYP PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (APIENTRYP PFNGLCLEARSTENCILPROC)(GLint s); +typedef void (APIENTRYP PFNGLCLEARDEPTHPROC)(GLdouble depth); +typedef void (APIENTRYP PFNGLSTENCILMASKPROC)(GLuint mask); +typedef void (APIENTRYP PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +typedef void (APIENTRYP PFNGLDEPTHMASKPROC)(GLboolean flag); +typedef void (APIENTRYP PFNGLDISABLEPROC)(GLenum cap); +typedef void (APIENTRYP PFNGLENABLEPROC)(GLenum cap); +typedef void (APIENTRYP PFNGLFINISHPROC)(void); +typedef void (APIENTRYP PFNGLFLUSHPROC)(void); +typedef void (APIENTRYP PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor); +typedef void (APIENTRYP PFNGLLOGICOPPROC)(GLenum opcode); +typedef void (APIENTRYP PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass); +typedef void (APIENTRYP PFNGLDEPTHFUNCPROC)(GLenum func); +typedef void (APIENTRYP PFNGLPIXELSTOREFPROC)(GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLREADBUFFERPROC)(GLenum src); +typedef void (APIENTRYP PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * pixels); +typedef void (APIENTRYP PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean * data); +typedef void (APIENTRYP PFNGLGETDOUBLEVPROC)(GLenum pname, GLdouble * data); +typedef GLenum (APIENTRYP PFNGLGETERRORPROC)(void); +typedef void (APIENTRYP PFNGLGETFLOATVPROC)(GLenum pname, GLfloat * data); +typedef void (APIENTRYP PFNGLGETINTEGERVPROC)(GLenum pname, GLint * data); +typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGPROC)(GLenum name); +typedef void (APIENTRYP PFNGLGETTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, void * pixels); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum target, GLint level, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum target, GLint level, GLenum pname, GLint * params); +typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC)(GLenum cap); +typedef void (APIENTRYP PFNGLDEPTHRANGEPROC)(GLdouble n, GLdouble f); +typedef void (APIENTRYP PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLNEWLISTPROC)(GLuint list, GLenum mode); +typedef void (APIENTRYP PFNGLENDLISTPROC)(void); +typedef void (APIENTRYP PFNGLCALLLISTPROC)(GLuint list); +typedef void (APIENTRYP PFNGLCALLLISTSPROC)(GLsizei n, GLenum type, const void * lists); +typedef void (APIENTRYP PFNGLDELETELISTSPROC)(GLuint list, GLsizei range); +typedef GLuint (APIENTRYP PFNGLGENLISTSPROC)(GLsizei range); +typedef void (APIENTRYP PFNGLLISTBASEPROC)(GLuint base); +typedef void (APIENTRYP PFNGLBEGINPROC)(GLenum mode); +typedef void (APIENTRYP PFNGLBITMAPPROC)(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte * bitmap); +typedef void (APIENTRYP PFNGLCOLOR3BPROC)(GLbyte red, GLbyte green, GLbyte blue); +typedef void (APIENTRYP PFNGLCOLOR3BVPROC)(const GLbyte * v); +typedef void (APIENTRYP PFNGLCOLOR3DPROC)(GLdouble red, GLdouble green, GLdouble blue); +typedef void (APIENTRYP PFNGLCOLOR3DVPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLCOLOR3FPROC)(GLfloat red, GLfloat green, GLfloat blue); +typedef void (APIENTRYP PFNGLCOLOR3FVPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLCOLOR3IPROC)(GLint red, GLint green, GLint blue); +typedef void (APIENTRYP PFNGLCOLOR3IVPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLCOLOR3SPROC)(GLshort red, GLshort green, GLshort blue); +typedef void (APIENTRYP PFNGLCOLOR3SVPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLCOLOR3UBPROC)(GLubyte red, GLubyte green, GLubyte blue); +typedef void (APIENTRYP PFNGLCOLOR3UBVPROC)(const GLubyte * v); +typedef void (APIENTRYP PFNGLCOLOR3UIPROC)(GLuint red, GLuint green, GLuint blue); +typedef void (APIENTRYP PFNGLCOLOR3UIVPROC)(const GLuint * v); +typedef void (APIENTRYP PFNGLCOLOR3USPROC)(GLushort red, GLushort green, GLushort blue); +typedef void (APIENTRYP PFNGLCOLOR3USVPROC)(const GLushort * v); +typedef void (APIENTRYP PFNGLCOLOR4BPROC)(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha); +typedef void (APIENTRYP PFNGLCOLOR4BVPROC)(const GLbyte * v); +typedef void (APIENTRYP PFNGLCOLOR4DPROC)(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha); +typedef void (APIENTRYP PFNGLCOLOR4DVPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLCOLOR4FPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (APIENTRYP PFNGLCOLOR4FVPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLCOLOR4IPROC)(GLint red, GLint green, GLint blue, GLint alpha); +typedef void (APIENTRYP PFNGLCOLOR4IVPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLCOLOR4SPROC)(GLshort red, GLshort green, GLshort blue, GLshort alpha); +typedef void (APIENTRYP PFNGLCOLOR4SVPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLCOLOR4UBPROC)(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); +typedef void (APIENTRYP PFNGLCOLOR4UBVPROC)(const GLubyte * v); +typedef void (APIENTRYP PFNGLCOLOR4UIPROC)(GLuint red, GLuint green, GLuint blue, GLuint alpha); +typedef void (APIENTRYP PFNGLCOLOR4UIVPROC)(const GLuint * v); +typedef void (APIENTRYP PFNGLCOLOR4USPROC)(GLushort red, GLushort green, GLushort blue, GLushort alpha); +typedef void (APIENTRYP PFNGLCOLOR4USVPROC)(const GLushort * v); +typedef void (APIENTRYP PFNGLEDGEFLAGPROC)(GLboolean flag); +typedef void (APIENTRYP PFNGLEDGEFLAGVPROC)(const GLboolean * flag); +typedef void (APIENTRYP PFNGLENDPROC)(void); +typedef void (APIENTRYP PFNGLINDEXDPROC)(GLdouble c); +typedef void (APIENTRYP PFNGLINDEXDVPROC)(const GLdouble * c); +typedef void (APIENTRYP PFNGLINDEXFPROC)(GLfloat c); +typedef void (APIENTRYP PFNGLINDEXFVPROC)(const GLfloat * c); +typedef void (APIENTRYP PFNGLINDEXIPROC)(GLint c); +typedef void (APIENTRYP PFNGLINDEXIVPROC)(const GLint * c); +typedef void (APIENTRYP PFNGLINDEXSPROC)(GLshort c); +typedef void (APIENTRYP PFNGLINDEXSVPROC)(const GLshort * c); +typedef void (APIENTRYP PFNGLNORMAL3BPROC)(GLbyte nx, GLbyte ny, GLbyte nz); +typedef void (APIENTRYP PFNGLNORMAL3BVPROC)(const GLbyte * v); +typedef void (APIENTRYP PFNGLNORMAL3DPROC)(GLdouble nx, GLdouble ny, GLdouble nz); +typedef void (APIENTRYP PFNGLNORMAL3DVPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLNORMAL3FPROC)(GLfloat nx, GLfloat ny, GLfloat nz); +typedef void (APIENTRYP PFNGLNORMAL3FVPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLNORMAL3IPROC)(GLint nx, GLint ny, GLint nz); +typedef void (APIENTRYP PFNGLNORMAL3IVPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLNORMAL3SPROC)(GLshort nx, GLshort ny, GLshort nz); +typedef void (APIENTRYP PFNGLNORMAL3SVPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLRASTERPOS2DPROC)(GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLRASTERPOS2DVPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLRASTERPOS2FPROC)(GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLRASTERPOS2FVPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLRASTERPOS2IPROC)(GLint x, GLint y); +typedef void (APIENTRYP PFNGLRASTERPOS2IVPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLRASTERPOS2SPROC)(GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLRASTERPOS2SVPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLRASTERPOS3DPROC)(GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLRASTERPOS3DVPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLRASTERPOS3FPROC)(GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLRASTERPOS3FVPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLRASTERPOS3IPROC)(GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLRASTERPOS3IVPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLRASTERPOS3SPROC)(GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLRASTERPOS3SVPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLRASTERPOS4DPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLRASTERPOS4DVPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLRASTERPOS4FPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLRASTERPOS4FVPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLRASTERPOS4IPROC)(GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLRASTERPOS4IVPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLRASTERPOS4SPROC)(GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLRASTERPOS4SVPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLRECTDPROC)(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2); +typedef void (APIENTRYP PFNGLRECTDVPROC)(const GLdouble * v1, const GLdouble * v2); +typedef void (APIENTRYP PFNGLRECTFPROC)(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2); +typedef void (APIENTRYP PFNGLRECTFVPROC)(const GLfloat * v1, const GLfloat * v2); +typedef void (APIENTRYP PFNGLRECTIPROC)(GLint x1, GLint y1, GLint x2, GLint y2); +typedef void (APIENTRYP PFNGLRECTIVPROC)(const GLint * v1, const GLint * v2); +typedef void (APIENTRYP PFNGLRECTSPROC)(GLshort x1, GLshort y1, GLshort x2, GLshort y2); +typedef void (APIENTRYP PFNGLRECTSVPROC)(const GLshort * v1, const GLshort * v2); +typedef void (APIENTRYP PFNGLTEXCOORD1DPROC)(GLdouble s); +typedef void (APIENTRYP PFNGLTEXCOORD1DVPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLTEXCOORD1FPROC)(GLfloat s); +typedef void (APIENTRYP PFNGLTEXCOORD1FVPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLTEXCOORD1IPROC)(GLint s); +typedef void (APIENTRYP PFNGLTEXCOORD1IVPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLTEXCOORD1SPROC)(GLshort s); +typedef void (APIENTRYP PFNGLTEXCOORD1SVPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLTEXCOORD2DPROC)(GLdouble s, GLdouble t); +typedef void (APIENTRYP PFNGLTEXCOORD2DVPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLTEXCOORD2FPROC)(GLfloat s, GLfloat t); +typedef void (APIENTRYP PFNGLTEXCOORD2FVPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLTEXCOORD2IPROC)(GLint s, GLint t); +typedef void (APIENTRYP PFNGLTEXCOORD2IVPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLTEXCOORD2SPROC)(GLshort s, GLshort t); +typedef void (APIENTRYP PFNGLTEXCOORD2SVPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLTEXCOORD3DPROC)(GLdouble s, GLdouble t, GLdouble r); +typedef void (APIENTRYP PFNGLTEXCOORD3DVPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLTEXCOORD3FPROC)(GLfloat s, GLfloat t, GLfloat r); +typedef void (APIENTRYP PFNGLTEXCOORD3FVPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLTEXCOORD3IPROC)(GLint s, GLint t, GLint r); +typedef void (APIENTRYP PFNGLTEXCOORD3IVPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLTEXCOORD3SPROC)(GLshort s, GLshort t, GLshort r); +typedef void (APIENTRYP PFNGLTEXCOORD3SVPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLTEXCOORD4DPROC)(GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (APIENTRYP PFNGLTEXCOORD4DVPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLTEXCOORD4FPROC)(GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (APIENTRYP PFNGLTEXCOORD4FVPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLTEXCOORD4IPROC)(GLint s, GLint t, GLint r, GLint q); +typedef void (APIENTRYP PFNGLTEXCOORD4IVPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLTEXCOORD4SPROC)(GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (APIENTRYP PFNGLTEXCOORD4SVPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEX2DPROC)(GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEX2DVPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEX2FPROC)(GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEX2FVPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEX2IPROC)(GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEX2IVPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLVERTEX2SPROC)(GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEX2SVPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEX3DPROC)(GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEX3DVPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEX3FPROC)(GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEX3FVPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEX3IPROC)(GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEX3IVPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLVERTEX3SPROC)(GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEX3SVPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEX4DPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEX4DVPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEX4FPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEX4FVPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEX4IPROC)(GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEX4IVPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLVERTEX4SPROC)(GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEX4SVPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLCLIPPLANEPROC)(GLenum plane, const GLdouble * equation); +typedef void (APIENTRYP PFNGLCOLORMATERIALPROC)(GLenum face, GLenum mode); +typedef void (APIENTRYP PFNGLFOGFPROC)(GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFOGFVPROC)(GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLFOGIPROC)(GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFOGIVPROC)(GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLLIGHTFPROC)(GLenum light, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLLIGHTFVPROC)(GLenum light, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLLIGHTIPROC)(GLenum light, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLLIGHTIVPROC)(GLenum light, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLLIGHTMODELFPROC)(GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLLIGHTMODELFVPROC)(GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLLIGHTMODELIPROC)(GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLLIGHTMODELIVPROC)(GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLLINESTIPPLEPROC)(GLint factor, GLushort pattern); +typedef void (APIENTRYP PFNGLMATERIALFPROC)(GLenum face, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMATERIALFVPROC)(GLenum face, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLMATERIALIPROC)(GLenum face, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMATERIALIVPROC)(GLenum face, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLPOLYGONSTIPPLEPROC)(const GLubyte * mask); +typedef void (APIENTRYP PFNGLSHADEMODELPROC)(GLenum mode); +typedef void (APIENTRYP PFNGLTEXENVFPROC)(GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLTEXENVFVPROC)(GLenum target, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLTEXENVIPROC)(GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXENVIVPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLTEXGENDPROC)(GLenum coord, GLenum pname, GLdouble param); +typedef void (APIENTRYP PFNGLTEXGENDVPROC)(GLenum coord, GLenum pname, const GLdouble * params); +typedef void (APIENTRYP PFNGLTEXGENFPROC)(GLenum coord, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLTEXGENFVPROC)(GLenum coord, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLTEXGENIPROC)(GLenum coord, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXGENIVPROC)(GLenum coord, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLFEEDBACKBUFFERPROC)(GLsizei size, GLenum type, GLfloat * buffer); +typedef void (APIENTRYP PFNGLSELECTBUFFERPROC)(GLsizei size, GLuint * buffer); +typedef GLint (APIENTRYP PFNGLRENDERMODEPROC)(GLenum mode); +typedef void (APIENTRYP PFNGLINITNAMESPROC)(void); +typedef void (APIENTRYP PFNGLLOADNAMEPROC)(GLuint name); +typedef void (APIENTRYP PFNGLPASSTHROUGHPROC)(GLfloat token); +typedef void (APIENTRYP PFNGLPOPNAMEPROC)(void); +typedef void (APIENTRYP PFNGLPUSHNAMEPROC)(GLuint name); +typedef void (APIENTRYP PFNGLCLEARACCUMPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (APIENTRYP PFNGLCLEARINDEXPROC)(GLfloat c); +typedef void (APIENTRYP PFNGLINDEXMASKPROC)(GLuint mask); +typedef void (APIENTRYP PFNGLACCUMPROC)(GLenum op, GLfloat value); +typedef void (APIENTRYP PFNGLPOPATTRIBPROC)(void); +typedef void (APIENTRYP PFNGLPUSHATTRIBPROC)(GLbitfield mask); +typedef void (APIENTRYP PFNGLMAP1DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble * points); +typedef void (APIENTRYP PFNGLMAP1FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat * points); +typedef void (APIENTRYP PFNGLMAP2DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble * points); +typedef void (APIENTRYP PFNGLMAP2FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat * points); +typedef void (APIENTRYP PFNGLMAPGRID1DPROC)(GLint un, GLdouble u1, GLdouble u2); +typedef void (APIENTRYP PFNGLMAPGRID1FPROC)(GLint un, GLfloat u1, GLfloat u2); +typedef void (APIENTRYP PFNGLMAPGRID2DPROC)(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2); +typedef void (APIENTRYP PFNGLMAPGRID2FPROC)(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLEVALCOORD1DPROC)(GLdouble u); +typedef void (APIENTRYP PFNGLEVALCOORD1DVPROC)(const GLdouble * u); +typedef void (APIENTRYP PFNGLEVALCOORD1FPROC)(GLfloat u); +typedef void (APIENTRYP PFNGLEVALCOORD1FVPROC)(const GLfloat * u); +typedef void (APIENTRYP PFNGLEVALCOORD2DPROC)(GLdouble u, GLdouble v); +typedef void (APIENTRYP PFNGLEVALCOORD2DVPROC)(const GLdouble * u); +typedef void (APIENTRYP PFNGLEVALCOORD2FPROC)(GLfloat u, GLfloat v); +typedef void (APIENTRYP PFNGLEVALCOORD2FVPROC)(const GLfloat * u); +typedef void (APIENTRYP PFNGLEVALMESH1PROC)(GLenum mode, GLint i1, GLint i2); +typedef void (APIENTRYP PFNGLEVALPOINT1PROC)(GLint i); +typedef void (APIENTRYP PFNGLEVALMESH2PROC)(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2); +typedef void (APIENTRYP PFNGLEVALPOINT2PROC)(GLint i, GLint j); +typedef void (APIENTRYP PFNGLALPHAFUNCPROC)(GLenum func, GLfloat ref); +typedef void (APIENTRYP PFNGLPIXELZOOMPROC)(GLfloat xfactor, GLfloat yfactor); +typedef void (APIENTRYP PFNGLPIXELTRANSFERFPROC)(GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELTRANSFERIPROC)(GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPIXELMAPFVPROC)(GLenum map, GLsizei mapsize, const GLfloat * values); +typedef void (APIENTRYP PFNGLPIXELMAPUIVPROC)(GLenum map, GLsizei mapsize, const GLuint * values); +typedef void (APIENTRYP PFNGLPIXELMAPUSVPROC)(GLenum map, GLsizei mapsize, const GLushort * values); +typedef void (APIENTRYP PFNGLCOPYPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type); +typedef void (APIENTRYP PFNGLDRAWPIXELSPROC)(GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLGETCLIPPLANEPROC)(GLenum plane, GLdouble * equation); +typedef void (APIENTRYP PFNGLGETLIGHTFVPROC)(GLenum light, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETLIGHTIVPROC)(GLenum light, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETMAPDVPROC)(GLenum target, GLenum query, GLdouble * v); +typedef void (APIENTRYP PFNGLGETMAPFVPROC)(GLenum target, GLenum query, GLfloat * v); +typedef void (APIENTRYP PFNGLGETMAPIVPROC)(GLenum target, GLenum query, GLint * v); +typedef void (APIENTRYP PFNGLGETMATERIALFVPROC)(GLenum face, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETMATERIALIVPROC)(GLenum face, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETPIXELMAPFVPROC)(GLenum map, GLfloat * values); +typedef void (APIENTRYP PFNGLGETPIXELMAPUIVPROC)(GLenum map, GLuint * values); +typedef void (APIENTRYP PFNGLGETPIXELMAPUSVPROC)(GLenum map, GLushort * values); +typedef void (APIENTRYP PFNGLGETPOLYGONSTIPPLEPROC)(GLubyte * mask); +typedef void (APIENTRYP PFNGLGETTEXENVFVPROC)(GLenum target, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETTEXENVIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETTEXGENDVPROC)(GLenum coord, GLenum pname, GLdouble * params); +typedef void (APIENTRYP PFNGLGETTEXGENFVPROC)(GLenum coord, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETTEXGENIVPROC)(GLenum coord, GLenum pname, GLint * params); +typedef GLboolean (APIENTRYP PFNGLISLISTPROC)(GLuint list); +typedef void (APIENTRYP PFNGLFRUSTUMPROC)(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLLOADIDENTITYPROC)(void); +typedef void (APIENTRYP PFNGLLOADMATRIXFPROC)(const GLfloat * m); +typedef void (APIENTRYP PFNGLLOADMATRIXDPROC)(const GLdouble * m); +typedef void (APIENTRYP PFNGLMATRIXMODEPROC)(GLenum mode); +typedef void (APIENTRYP PFNGLMULTMATRIXFPROC)(const GLfloat * m); +typedef void (APIENTRYP PFNGLMULTMATRIXDPROC)(const GLdouble * m); +typedef void (APIENTRYP PFNGLORTHOPROC)(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLPOPMATRIXPROC)(void); +typedef void (APIENTRYP PFNGLPUSHMATRIXPROC)(void); +typedef void (APIENTRYP PFNGLROTATEDPROC)(GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLROTATEFPROC)(GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLSCALEDPROC)(GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLSCALEFPROC)(GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTRANSLATEDPROC)(GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLTRANSLATEFPROC)(GLfloat x, GLfloat y, GLfloat z); +#endif /* GL_VERSION_1_0 */ + +#ifndef GL_VERSION_1_1 +#define GL_VERSION_1_1 1 +typedef khronos_float_t GLclampf; +typedef double GLclampd; +#define GL_COLOR_LOGIC_OP 0x0BF2 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_POLYGON_OFFSET_POINT 0x2A01 +#define GL_POLYGON_OFFSET_LINE 0x2A02 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_TEXTURE_BINDING_1D 0x8068 +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_TEXTURE_INTERNAL_FORMAT 0x1003 +#define GL_TEXTURE_RED_SIZE 0x805C +#define GL_TEXTURE_GREEN_SIZE 0x805D +#define GL_TEXTURE_BLUE_SIZE 0x805E +#define GL_TEXTURE_ALPHA_SIZE 0x805F +#define GL_DOUBLE 0x140A +#define GL_PROXY_TEXTURE_1D 0x8063 +#define GL_PROXY_TEXTURE_2D 0x8064 +#define GL_R3_G3_B2 0x2A10 +#define GL_RGB4 0x804F +#define GL_RGB5 0x8050 +#define GL_RGB8 0x8051 +#define GL_RGB10 0x8052 +#define GL_RGB12 0x8053 +#define GL_RGB16 0x8054 +#define GL_RGBA2 0x8055 +#define GL_RGBA4 0x8056 +#define GL_RGB5_A1 0x8057 +#define GL_RGBA8 0x8058 +#define GL_RGB10_A2 0x8059 +#define GL_RGBA12 0x805A +#define GL_RGBA16 0x805B +#define GL_CLIENT_PIXEL_STORE_BIT 0x00000001 +#define GL_CLIENT_VERTEX_ARRAY_BIT 0x00000002 +#define GL_CLIENT_ALL_ATTRIB_BITS 0xFFFFFFFF +#define GL_VERTEX_ARRAY_POINTER 0x808E +#define GL_NORMAL_ARRAY_POINTER 0x808F +#define GL_COLOR_ARRAY_POINTER 0x8090 +#define GL_INDEX_ARRAY_POINTER 0x8091 +#define GL_TEXTURE_COORD_ARRAY_POINTER 0x8092 +#define GL_EDGE_FLAG_ARRAY_POINTER 0x8093 +#define GL_FEEDBACK_BUFFER_POINTER 0x0DF0 +#define GL_SELECTION_BUFFER_POINTER 0x0DF3 +#define GL_CLIENT_ATTRIB_STACK_DEPTH 0x0BB1 +#define GL_INDEX_LOGIC_OP 0x0BF1 +#define GL_MAX_CLIENT_ATTRIB_STACK_DEPTH 0x0D3B +#define GL_FEEDBACK_BUFFER_SIZE 0x0DF1 +#define GL_FEEDBACK_BUFFER_TYPE 0x0DF2 +#define GL_SELECTION_BUFFER_SIZE 0x0DF4 +#define GL_VERTEX_ARRAY 0x8074 +#define GL_NORMAL_ARRAY 0x8075 +#define GL_COLOR_ARRAY 0x8076 +#define GL_INDEX_ARRAY 0x8077 +#define GL_TEXTURE_COORD_ARRAY 0x8078 +#define GL_EDGE_FLAG_ARRAY 0x8079 +#define GL_VERTEX_ARRAY_SIZE 0x807A +#define GL_VERTEX_ARRAY_TYPE 0x807B +#define GL_VERTEX_ARRAY_STRIDE 0x807C +#define GL_NORMAL_ARRAY_TYPE 0x807E +#define GL_NORMAL_ARRAY_STRIDE 0x807F +#define GL_COLOR_ARRAY_SIZE 0x8081 +#define GL_COLOR_ARRAY_TYPE 0x8082 +#define GL_COLOR_ARRAY_STRIDE 0x8083 +#define GL_INDEX_ARRAY_TYPE 0x8085 +#define GL_INDEX_ARRAY_STRIDE 0x8086 +#define GL_TEXTURE_COORD_ARRAY_SIZE 0x8088 +#define GL_TEXTURE_COORD_ARRAY_TYPE 0x8089 +#define GL_TEXTURE_COORD_ARRAY_STRIDE 0x808A +#define GL_EDGE_FLAG_ARRAY_STRIDE 0x808C +#define GL_TEXTURE_LUMINANCE_SIZE 0x8060 +#define GL_TEXTURE_INTENSITY_SIZE 0x8061 +#define GL_TEXTURE_PRIORITY 0x8066 +#define GL_TEXTURE_RESIDENT 0x8067 +#define GL_ALPHA4 0x803B +#define GL_ALPHA8 0x803C +#define GL_ALPHA12 0x803D +#define GL_ALPHA16 0x803E +#define GL_LUMINANCE4 0x803F +#define GL_LUMINANCE8 0x8040 +#define GL_LUMINANCE12 0x8041 +#define GL_LUMINANCE16 0x8042 +#define GL_LUMINANCE4_ALPHA4 0x8043 +#define GL_LUMINANCE6_ALPHA2 0x8044 +#define GL_LUMINANCE8_ALPHA8 0x8045 +#define GL_LUMINANCE12_ALPHA4 0x8046 +#define GL_LUMINANCE12_ALPHA12 0x8047 +#define GL_LUMINANCE16_ALPHA16 0x8048 +#define GL_INTENSITY 0x8049 +#define GL_INTENSITY4 0x804A +#define GL_INTENSITY8 0x804B +#define GL_INTENSITY12 0x804C +#define GL_INTENSITY16 0x804D +#define GL_V2F 0x2A20 +#define GL_V3F 0x2A21 +#define GL_C4UB_V2F 0x2A22 +#define GL_C4UB_V3F 0x2A23 +#define GL_C3F_V3F 0x2A24 +#define GL_N3F_V3F 0x2A25 +#define GL_C4F_N3F_V3F 0x2A26 +#define GL_T2F_V3F 0x2A27 +#define GL_T4F_V4F 0x2A28 +#define GL_T2F_C4UB_V3F 0x2A29 +#define GL_T2F_C3F_V3F 0x2A2A +#define GL_T2F_N3F_V3F 0x2A2B +#define GL_T2F_C4F_N3F_V3F 0x2A2C +#define GL_T4F_C4F_N3F_V4F 0x2A2D +typedef void (APIENTRYP PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices); +typedef void (APIENTRYP PFNGLGETPOINTERVPROC)(GLenum pname, void ** params); +typedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units); +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint * textures); +typedef void (APIENTRYP PFNGLGENTEXTURESPROC)(GLsizei n, GLuint * textures); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC)(GLuint texture); +typedef void (APIENTRYP PFNGLARRAYELEMENTPROC)(GLint i); +typedef void (APIENTRYP PFNGLCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); +typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEPROC)(GLenum array); +typedef void (APIENTRYP PFNGLEDGEFLAGPOINTERPROC)(GLsizei stride, const void * pointer); +typedef void (APIENTRYP PFNGLENABLECLIENTSTATEPROC)(GLenum array); +typedef void (APIENTRYP PFNGLINDEXPOINTERPROC)(GLenum type, GLsizei stride, const void * pointer); +typedef void (APIENTRYP PFNGLINTERLEAVEDARRAYSPROC)(GLenum format, GLsizei stride, const void * pointer); +typedef void (APIENTRYP PFNGLNORMALPOINTERPROC)(GLenum type, GLsizei stride, const void * pointer); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); +typedef void (APIENTRYP PFNGLVERTEXPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); +typedef GLboolean (APIENTRYP PFNGLARETEXTURESRESIDENTPROC)(GLsizei n, const GLuint * textures, GLboolean * residences); +typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESPROC)(GLsizei n, const GLuint * textures, const GLfloat * priorities); +typedef void (APIENTRYP PFNGLINDEXUBPROC)(GLubyte c); +typedef void (APIENTRYP PFNGLINDEXUBVPROC)(const GLubyte * c); +typedef void (APIENTRYP PFNGLPOPCLIENTATTRIBPROC)(void); +typedef void (APIENTRYP PFNGLPUSHCLIENTATTRIBPROC)(GLbitfield mask); +#endif /* GL_VERSION_1_1 */ + +#ifndef GL_VERSION_1_2 +#define GL_VERSION_1_2 1 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_TEXTURE_3D 0x806F +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_RESCALE_NORMAL 0x803A +#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8 +#define GL_SINGLE_COLOR 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR 0x81FA +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices); +typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif /* GL_VERSION_1_2 */ + +#ifndef GL_VERSION_1_3 +#define GL_VERSION_1_3 1 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_COMPRESSED_RGB 0x84ED +#define GL_COMPRESSED_RGBA 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CLAMP_TO_BORDER 0x812D +#define GL_CLIENT_ACTIVE_TEXTURE 0x84E1 +#define GL_MAX_TEXTURE_UNITS 0x84E2 +#define GL_TRANSPOSE_MODELVIEW_MATRIX 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX 0x84E6 +#define GL_MULTISAMPLE_BIT 0x20000000 +#define GL_NORMAL_MAP 0x8511 +#define GL_REFLECTION_MAP 0x8512 +#define GL_COMPRESSED_ALPHA 0x84E9 +#define GL_COMPRESSED_LUMINANCE 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB +#define GL_COMPRESSED_INTENSITY 0x84EC +#define GL_COMBINE 0x8570 +#define GL_COMBINE_RGB 0x8571 +#define GL_COMBINE_ALPHA 0x8572 +#define GL_SOURCE0_RGB 0x8580 +#define GL_SOURCE1_RGB 0x8581 +#define GL_SOURCE2_RGB 0x8582 +#define GL_SOURCE0_ALPHA 0x8588 +#define GL_SOURCE1_ALPHA 0x8589 +#define GL_SOURCE2_ALPHA 0x858A +#define GL_OPERAND0_RGB 0x8590 +#define GL_OPERAND1_RGB 0x8591 +#define GL_OPERAND2_RGB 0x8592 +#define GL_OPERAND0_ALPHA 0x8598 +#define GL_OPERAND1_ALPHA 0x8599 +#define GL_OPERAND2_ALPHA 0x859A +#define GL_RGB_SCALE 0x8573 +#define GL_ADD_SIGNED 0x8574 +#define GL_INTERPOLATE 0x8575 +#define GL_SUBTRACT 0x84E7 +#define GL_CONSTANT 0x8576 +#define GL_PRIMARY_COLOR 0x8577 +#define GL_PREVIOUS 0x8578 +#define GL_DOT3_RGB 0x86AE +#define GL_DOT3_RGBA 0x86AF +typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC)(GLenum texture); +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void * data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void * data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void * data); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level, void * img); +typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREPROC)(GLenum texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DPROC)(GLenum target, GLdouble s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVPROC)(GLenum target, const GLdouble * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FPROC)(GLenum target, GLfloat s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVPROC)(GLenum target, const GLfloat * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IPROC)(GLenum target, GLint s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVPROC)(GLenum target, const GLint * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SPROC)(GLenum target, GLshort s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVPROC)(GLenum target, const GLshort * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DPROC)(GLenum target, GLdouble s, GLdouble t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVPROC)(GLenum target, const GLdouble * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FPROC)(GLenum target, GLfloat s, GLfloat t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVPROC)(GLenum target, const GLfloat * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IPROC)(GLenum target, GLint s, GLint t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVPROC)(GLenum target, const GLint * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SPROC)(GLenum target, GLshort s, GLshort t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVPROC)(GLenum target, const GLshort * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVPROC)(GLenum target, const GLdouble * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVPROC)(GLenum target, const GLfloat * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IPROC)(GLenum target, GLint s, GLint t, GLint r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVPROC)(GLenum target, const GLint * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SPROC)(GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVPROC)(GLenum target, const GLshort * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVPROC)(GLenum target, const GLdouble * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVPROC)(GLenum target, const GLfloat * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IPROC)(GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVPROC)(GLenum target, const GLint * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SPROC)(GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVPROC)(GLenum target, const GLshort * v); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFPROC)(const GLfloat * m); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDPROC)(const GLdouble * m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFPROC)(const GLfloat * m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDPROC)(const GLdouble * m); +#endif /* GL_VERSION_1_3 */ + +#ifndef GL_VERSION_1_4 +#define GL_VERSION_1_4 1 +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_INCR_WRAP 0x8507 +#define GL_DECR_WRAP 0x8508 +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_TEXTURE_COMPARE_MODE 0x884C +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#define GL_POINT_SIZE_MIN 0x8126 +#define GL_POINT_SIZE_MAX 0x8127 +#define GL_POINT_DISTANCE_ATTENUATION 0x8129 +#define GL_GENERATE_MIPMAP 0x8191 +#define GL_GENERATE_MIPMAP_HINT 0x8192 +#define GL_FOG_COORDINATE_SOURCE 0x8450 +#define GL_FOG_COORDINATE 0x8451 +#define GL_FRAGMENT_DEPTH 0x8452 +#define GL_CURRENT_FOG_COORDINATE 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER 0x8456 +#define GL_FOG_COORDINATE_ARRAY 0x8457 +#define GL_COLOR_SUM 0x8458 +#define GL_CURRENT_SECONDARY_COLOR 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER 0x845D +#define GL_SECONDARY_COLOR_ARRAY 0x845E +#define GL_TEXTURE_FILTER_CONTROL 0x8500 +#define GL_DEPTH_TEXTURE_MODE 0x884B +#define GL_COMPARE_R_TO_TEXTURE 0x884E +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_EQUATION 0x8009 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_FUNC_SUBTRACT 0x800A +#define GL_MIN 0x8007 +#define GL_MAX 0x8008 +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC)(GLenum mode, const GLint * first, const GLsizei * count, GLsizei drawcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC)(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei drawcount); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC)(GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC)(GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC)(GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC)(GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLFOGCOORDFPROC)(GLfloat coord); +typedef void (APIENTRYP PFNGLFOGCOORDFVPROC)(const GLfloat * coord); +typedef void (APIENTRYP PFNGLFOGCOORDDPROC)(GLdouble coord); +typedef void (APIENTRYP PFNGLFOGCOORDDVPROC)(const GLdouble * coord); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTERPROC)(GLenum type, GLsizei stride, const void * pointer); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BPROC)(GLbyte red, GLbyte green, GLbyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVPROC)(const GLbyte * v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DPROC)(GLdouble red, GLdouble green, GLdouble blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FPROC)(GLfloat red, GLfloat green, GLfloat blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IPROC)(GLint red, GLint green, GLint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SPROC)(GLshort red, GLshort green, GLshort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBPROC)(GLubyte red, GLubyte green, GLubyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVPROC)(const GLubyte * v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIPROC)(GLuint red, GLuint green, GLuint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVPROC)(const GLuint * v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USPROC)(GLushort red, GLushort green, GLushort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVPROC)(const GLushort * v); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); +typedef void (APIENTRYP PFNGLWINDOWPOS2DPROC)(GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FPROC)(GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IPROC)(GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SPROC)(GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DPROC)(GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FPROC)(GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IPROC)(GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SPROC)(GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC)(GLenum mode); +#endif /* GL_VERSION_1_4 */ + +#ifndef GL_VERSION_1_5 +#define GL_VERSION_1_5 1 +typedef khronos_ssize_t GLsizeiptr; +typedef khronos_intptr_t GLintptr; +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 +#define GL_QUERY_COUNTER_BITS 0x8864 +#define GL_CURRENT_QUERY 0x8865 +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_READ_ONLY 0x88B8 +#define GL_WRITE_ONLY 0x88B9 +#define GL_READ_WRITE 0x88BA +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_STREAM_COPY 0x88E2 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STATIC_COPY 0x88E6 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_COPY 0x88EA +#define GL_SAMPLES_PASSED 0x8914 +#define GL_SRC1_ALPHA 0x8589 +#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING 0x889E +#define GL_FOG_COORD_SRC 0x8450 +#define GL_FOG_COORD 0x8451 +#define GL_CURRENT_FOG_COORD 0x8453 +#define GL_FOG_COORD_ARRAY_TYPE 0x8454 +#define GL_FOG_COORD_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORD_ARRAY_POINTER 0x8456 +#define GL_FOG_COORD_ARRAY 0x8457 +#define GL_FOG_COORD_ARRAY_BUFFER_BINDING 0x889D +#define GL_SRC0_RGB 0x8580 +#define GL_SRC1_RGB 0x8581 +#define GL_SRC2_RGB 0x8582 +#define GL_SRC0_ALPHA 0x8588 +#define GL_SRC2_ALPHA 0x858A +typedef void (APIENTRYP PFNGLGENQUERIESPROC)(GLsizei n, GLuint * ids); +typedef void (APIENTRYP PFNGLDELETEQUERIESPROC)(GLsizei n, const GLuint * ids); +typedef GLboolean (APIENTRYP PFNGLISQUERYPROC)(GLuint id); +typedef void (APIENTRYP PFNGLBEGINQUERYPROC)(GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYPROC)(GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC)(GLuint id, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC)(GLuint id, GLenum pname, GLuint * params); +typedef void (APIENTRYP PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint * buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSPROC)(GLsizei n, GLuint * buffers); +typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC)(GLuint buffer); +typedef void (APIENTRYP PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void * data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void * data); +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, void * data); +typedef void * (APIENTRYP PFNGLMAPBUFFERPROC)(GLenum target, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC)(GLenum target); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, void ** params); +#endif /* GL_VERSION_1_5 */ + +#ifndef GL_VERSION_2_0 +#define GL_VERSION_2_0 1 +typedef char GLchar; +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_MAX_DRAW_BUFFERS 0x8824 +#define GL_DRAW_BUFFER0 0x8825 +#define GL_DRAW_BUFFER1 0x8826 +#define GL_DRAW_BUFFER2 0x8827 +#define GL_DRAW_BUFFER3 0x8828 +#define GL_DRAW_BUFFER4 0x8829 +#define GL_DRAW_BUFFER5 0x882A +#define GL_DRAW_BUFFER6 0x882B +#define GL_DRAW_BUFFER7 0x882C +#define GL_DRAW_BUFFER8 0x882D +#define GL_DRAW_BUFFER9 0x882E +#define GL_DRAW_BUFFER10 0x882F +#define GL_DRAW_BUFFER11 0x8830 +#define GL_DRAW_BUFFER12 0x8831 +#define GL_DRAW_BUFFER13 0x8832 +#define GL_DRAW_BUFFER14 0x8833 +#define GL_DRAW_BUFFER15 0x8834 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#define GL_MAX_VARYING_FLOATS 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_SHADER_TYPE 0x8B4F +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_DELETE_STATUS 0x8B80 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#define GL_VERTEX_PROGRAM_TWO_SIDE 0x8643 +#define GL_POINT_SPRITE 0x8861 +#define GL_COORD_REPLACE 0x8862 +#define GL_MAX_TEXTURE_COORDS 0x8871 +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum * bufs); +typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask); +typedef void (APIENTRYP PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar * name); +typedef void (APIENTRYP PFNGLCOMPILESHADERPROC)(GLuint shader); +typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC)(void); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC)(GLenum type); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC)(GLuint program); +typedef void (APIENTRYP PFNGLDELETESHADERPROC)(GLuint shader); +typedef void (APIENTRYP PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name); +typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei * count, GLuint * shaders); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar * name); +typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (APIENTRYP PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * source); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar * name); +typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat * params); +typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint * params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC)(GLuint index, GLenum pname, GLdouble * params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void ** pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC)(GLuint program); +typedef GLboolean (APIENTRYP PFNGLISSHADERPROC)(GLuint shader); +typedef void (APIENTRYP PFNGLLINKPROGRAMPROC)(GLuint program); +typedef void (APIENTRYP PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length); +typedef void (APIENTRYP PFNGLUSEPROGRAMPROC)(GLuint program); +typedef void (APIENTRYP PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLUNIFORM1IPROC)(GLint location, GLint v0); +typedef void (APIENTRYP PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (APIENTRYP PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (APIENTRYP PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (APIENTRYP PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (APIENTRYP PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (APIENTRYP PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (APIENTRYP PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (APIENTRYP PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC)(GLuint program); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC)(GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC)(GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC)(GLuint index, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC)(GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC)(GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC)(GLuint index, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC)(GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC)(GLuint index, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC)(GLuint index, const GLbyte * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC)(GLuint index, const GLint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC)(GLuint index, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC)(GLuint index, const GLubyte * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC)(GLuint index, const GLuint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC)(GLuint index, const GLushort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC)(GLuint index, const GLbyte * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC)(GLuint index, const GLint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC)(GLuint index, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC)(GLuint index, const GLubyte * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC)(GLuint index, const GLuint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC)(GLuint index, const GLushort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer); +#endif /* GL_VERSION_2_0 */ + +#ifndef GL_VERSION_2_1 +#define GL_VERSION_2_1 1 +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB_ALPHA 0x8C42 +#define GL_SRGB8_ALPHA8 0x8C43 +#define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 +#define GL_CURRENT_RASTER_SECONDARY_COLOR 0x845F +#define GL_SLUMINANCE_ALPHA 0x8C44 +#define GL_SLUMINANCE8_ALPHA8 0x8C45 +#define GL_SLUMINANCE 0x8C46 +#define GL_SLUMINANCE8 0x8C47 +#define GL_COMPRESSED_SLUMINANCE 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA 0x8C4B +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +#endif /* GL_VERSION_2_1 */ + +#ifndef GL_VERSION_3_0 +#define GL_VERSION_3_0 1 +typedef khronos_uint16_t GLhalf; +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#define GL_CLIP_DISTANCE0 0x3000 +#define GL_CLIP_DISTANCE1 0x3001 +#define GL_CLIP_DISTANCE2 0x3002 +#define GL_CLIP_DISTANCE3 0x3003 +#define GL_CLIP_DISTANCE4 0x3004 +#define GL_CLIP_DISTANCE5 0x3005 +#define GL_CLIP_DISTANCE6 0x3006 +#define GL_CLIP_DISTANCE7 0x3007 +#define GL_MAX_CLIP_DISTANCES 0x0D32 +#define GL_MAJOR_VERSION 0x821B +#define GL_MINOR_VERSION 0x821C +#define GL_NUM_EXTENSIONS 0x821D +#define GL_CONTEXT_FLAGS 0x821E +#define GL_COMPRESSED_RED 0x8225 +#define GL_COMPRESSED_RG 0x8226 +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 +#define GL_RGBA32F 0x8814 +#define GL_RGB32F 0x8815 +#define GL_RGBA16F 0x881A +#define GL_RGB16F 0x881B +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF +#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 +#define GL_CLAMP_READ_COLOR 0x891C +#define GL_FIXED_ONLY 0x891D +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_R11F_G11F_B10F 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define GL_RGB9_E5 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E +#define GL_TEXTURE_SHARED_SIZE 0x8C3F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 +#define GL_PRIMITIVES_GENERATED 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 +#define GL_RASTERIZER_DISCARD 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B +#define GL_INTERLEAVED_ATTRIBS 0x8C8C +#define GL_SEPARATE_ATTRIBS 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F +#define GL_RGBA32UI 0x8D70 +#define GL_RGB32UI 0x8D71 +#define GL_RGBA16UI 0x8D76 +#define GL_RGB16UI 0x8D77 +#define GL_RGBA8UI 0x8D7C +#define GL_RGB8UI 0x8D7D +#define GL_RGBA32I 0x8D82 +#define GL_RGB32I 0x8D83 +#define GL_RGBA16I 0x8D88 +#define GL_RGB16I 0x8D89 +#define GL_RGBA8I 0x8D8E +#define GL_RGB8I 0x8D8F +#define GL_RED_INTEGER 0x8D94 +#define GL_GREEN_INTEGER 0x8D95 +#define GL_BLUE_INTEGER 0x8D96 +#define GL_RGB_INTEGER 0x8D98 +#define GL_RGBA_INTEGER 0x8D99 +#define GL_BGR_INTEGER 0x8D9A +#define GL_BGRA_INTEGER 0x8D9B +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_QUERY_WAIT 0x8E13 +#define GL_QUERY_NO_WAIT 0x8E14 +#define GL_QUERY_BY_REGION_WAIT 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 +#define GL_BUFFER_ACCESS_FLAGS 0x911F +#define GL_BUFFER_MAP_LENGTH 0x9120 +#define GL_BUFFER_MAP_OFFSET 0x9121 +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_DEPTH32F_STENCIL8 0x8CAD +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#define GL_FRAMEBUFFER_DEFAULT 0x8218 +#define GL_FRAMEBUFFER_UNDEFINED 0x8219 +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_UNSIGNED_INT_24_8 0x84FA +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE 0x88F1 +#define GL_TEXTURE_RED_TYPE 0x8C10 +#define GL_TEXTURE_GREEN_TYPE 0x8C11 +#define GL_TEXTURE_BLUE_TYPE 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE 0x8C13 +#define GL_TEXTURE_DEPTH_TYPE 0x8C16 +#define GL_UNSIGNED_NORMALIZED 0x8C17 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA +#define GL_RENDERBUFFER_SAMPLES 0x8CAB +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#define GL_COLOR_ATTACHMENT11 0x8CEB +#define GL_COLOR_ATTACHMENT12 0x8CEC +#define GL_COLOR_ATTACHMENT13 0x8CED +#define GL_COLOR_ATTACHMENT14 0x8CEE +#define GL_COLOR_ATTACHMENT15 0x8CEF +#define GL_COLOR_ATTACHMENT16 0x8CF0 +#define GL_COLOR_ATTACHMENT17 0x8CF1 +#define GL_COLOR_ATTACHMENT18 0x8CF2 +#define GL_COLOR_ATTACHMENT19 0x8CF3 +#define GL_COLOR_ATTACHMENT20 0x8CF4 +#define GL_COLOR_ATTACHMENT21 0x8CF5 +#define GL_COLOR_ATTACHMENT22 0x8CF6 +#define GL_COLOR_ATTACHMENT23 0x8CF7 +#define GL_COLOR_ATTACHMENT24 0x8CF8 +#define GL_COLOR_ATTACHMENT25 0x8CF9 +#define GL_COLOR_ATTACHMENT26 0x8CFA +#define GL_COLOR_ATTACHMENT27 0x8CFB +#define GL_COLOR_ATTACHMENT28 0x8CFC +#define GL_COLOR_ATTACHMENT29 0x8CFD +#define GL_COLOR_ATTACHMENT30 0x8CFE +#define GL_COLOR_ATTACHMENT31 0x8CFF +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_STENCIL_INDEX1 0x8D46 +#define GL_STENCIL_INDEX4 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_INDEX16 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 +#define GL_MAX_SAMPLES 0x8D57 +#define GL_INDEX 0x8222 +#define GL_TEXTURE_LUMINANCE_TYPE 0x8C14 +#define GL_TEXTURE_INTENSITY_TYPE 0x8C15 +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#define GL_HALF_FLOAT 0x140B +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define GL_RG 0x8227 +#define GL_RG_INTEGER 0x8228 +#define GL_R8 0x8229 +#define GL_R16 0x822A +#define GL_RG8 0x822B +#define GL_RG16 0x822C +#define GL_R16F 0x822D +#define GL_R32F 0x822E +#define GL_RG16F 0x822F +#define GL_RG32F 0x8230 +#define GL_R8I 0x8231 +#define GL_R8UI 0x8232 +#define GL_R16I 0x8233 +#define GL_R16UI 0x8234 +#define GL_R32I 0x8235 +#define GL_R32UI 0x8236 +#define GL_RG8I 0x8237 +#define GL_RG8UI 0x8238 +#define GL_RG16I 0x8239 +#define GL_RG16UI 0x823A +#define GL_RG32I 0x823B +#define GL_RG32UI 0x823C +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +#define GL_CLAMP_VERTEX_COLOR 0x891A +#define GL_CLAMP_FRAGMENT_COLOR 0x891B +#define GL_ALPHA_INTEGER 0x8D97 +typedef void (APIENTRYP PFNGLCOLORMASKIPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC)(GLenum target, GLuint index, GLboolean * data); +typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC)(GLenum target, GLuint index, GLint * data); +typedef void (APIENTRYP PFNGLENABLEIPROC)(GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEIPROC)(GLenum target, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC)(GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC)(GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC)(void); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC)(GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC)(GLuint program, GLsizei count, const GLchar *const* varyings, GLenum bufferMode); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLsizei * size, GLenum * type, GLchar * name); +typedef void (APIENTRYP PFNGLCLAMPCOLORPROC)(GLenum target, GLenum clamp); +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC)(GLuint id, GLenum mode); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC)(void); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC)(GLuint index, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC)(GLuint index, GLenum pname, GLuint * params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC)(GLuint index, GLint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC)(GLuint index, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC)(GLuint index, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC)(GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC)(GLuint index, GLuint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC)(GLuint index, GLuint x, GLuint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC)(GLuint index, const GLint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC)(GLuint index, const GLint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC)(GLuint index, const GLint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC)(GLuint index, const GLint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC)(GLuint index, const GLuint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC)(GLuint index, const GLuint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC)(GLuint index, const GLuint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC)(GLuint index, const GLuint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC)(GLuint index, const GLbyte * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC)(GLuint index, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC)(GLuint index, const GLubyte * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC)(GLuint index, const GLushort * v); +typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC)(GLuint program, GLint location, GLuint * params); +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC)(GLuint program, GLuint color, const GLchar * name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC)(GLuint program, const GLchar * name); +typedef void (APIENTRYP PFNGLUNIFORM1UIPROC)(GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLUNIFORM2UIPROC)(GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLUNIFORM3UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLUNIFORM4UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, const GLuint * params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, GLuint * params); +typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC)(GLenum buffer, GLint drawbuffer, const GLint * value); +typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC)(GLenum buffer, GLint drawbuffer, const GLuint * value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC)(GLenum buffer, GLint drawbuffer, const GLfloat * value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGIPROC)(GLenum name, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer); +typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint * renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint * renderbuffers); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint * framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint * framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC)(GLenum target); +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void * (APIENTRYP PFNGLMAPBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC)(GLuint array); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC)(GLsizei n, const GLuint * arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint * arrays); +typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC)(GLuint array); +#endif /* GL_VERSION_3_0 */ + +#ifndef GL_VERSION_3_1 +#define GL_VERSION_3_1 1 +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_TEXTURE_BUFFER 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D +#define GL_TEXTURE_RECTANGLE 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 +#define GL_R8_SNORM 0x8F94 +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB8_SNORM 0x8F96 +#define GL_RGBA8_SNORM 0x8F97 +#define GL_R16_SNORM 0x8F98 +#define GL_RG16_SNORM 0x8F99 +#define GL_RGB16_SNORM 0x8F9A +#define GL_RGBA16_SNORM 0x8F9B +#define GL_SIGNED_NORMALIZED 0x8F9C +#define GL_PRIMITIVE_RESTART 0x8F9D +#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E +#define GL_COPY_READ_BUFFER 0x8F36 +#define GL_COPY_WRITE_BUFFER 0x8F37 +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_INVALID_INDEX 0xFFFFFFFF +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount); +typedef void (APIENTRYP PFNGLTEXBUFFERPROC)(GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC)(GLuint index); +typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC)(GLuint program, GLsizei uniformCount, const GLchar *const* uniformNames, GLuint * uniformIndices); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC)(GLuint program, GLsizei uniformCount, const GLuint * uniformIndices, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC)(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei * length, GLchar * uniformName); +typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint program, const GLchar * uniformBlockName); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei * length, GLchar * uniformBlockName); +typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#endif /* GL_VERSION_3_1 */ + +#ifndef GL_VERSION_3_2 +#define GL_VERSION_3_2 1 +typedef struct __GLsync *GLsync; +typedef khronos_uint64_t GLuint64; +typedef khronos_int64_t GLint64; +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_LINES_ADJACENCY 0x000A +#define GL_LINE_STRIP_ADJACENCY 0x000B +#define GL_TRIANGLES_ADJACENCY 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D +#define GL_PROGRAM_POINT_SIZE 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 +#define GL_GEOMETRY_SHADER 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT 0x8916 +#define GL_GEOMETRY_INPUT_TYPE 0x8917 +#define GL_GEOMETRY_OUTPUT_TYPE 0x8918 +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 +#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 +#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_DEPTH_CLAMP 0x864F +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION 0x8E4D +#define GL_LAST_VERTEX_CONVENTION 0x8E4E +#define GL_PROVOKING_VERTEX 0x8E4F +#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 +#define GL_OBJECT_TYPE 0x9112 +#define GL_SYNC_CONDITION 0x9113 +#define GL_SYNC_STATUS 0x9114 +#define GL_SYNC_FLAGS 0x9115 +#define GL_SYNC_FENCE 0x9116 +#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 +#define GL_UNSIGNALED 0x9118 +#define GL_SIGNALED 0x9119 +#define GL_ALREADY_SIGNALED 0x911A +#define GL_TIMEOUT_EXPIRED 0x911B +#define GL_CONDITION_SATISFIED 0x911C +#define GL_WAIT_FAILED 0x911D +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFF +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#define GL_SAMPLE_POSITION 0x8E50 +#define GL_SAMPLE_MASK 0x8E51 +#define GL_SAMPLE_MASK_VALUE 0x8E52 +#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 +#define GL_TEXTURE_SAMPLES 0x9106 +#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E +#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F +#define GL_MAX_INTEGER_SAMPLES 0x9110 +typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei drawcount, const GLint * basevertex); +typedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC)(GLenum mode); +typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC)(GLenum condition, GLbitfield flags); +typedef GLboolean (APIENTRYP PFNGLISSYNCPROC)(GLsync sync); +typedef void (APIENTRYP PFNGLDELETESYNCPROC)(GLsync sync); +typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLGETINTEGER64VPROC)(GLenum pname, GLint64 * data); +typedef void (APIENTRYP PFNGLGETSYNCIVPROC)(GLsync sync, GLenum pname, GLsizei count, GLsizei * length, GLint * values); +typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC)(GLenum target, GLuint index, GLint64 * data); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC)(GLenum target, GLenum pname, GLint64 * params); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC)(GLenum pname, GLuint index, GLfloat * val); +typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC)(GLuint maskNumber, GLbitfield mask); +#endif /* GL_VERSION_3_2 */ + +#ifndef GL_VERSION_3_3 +#define GL_VERSION_3_3 1 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE +#define GL_SRC1_COLOR 0x88F9 +#define GL_ONE_MINUS_SRC1_COLOR 0x88FA +#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC +#define GL_ANY_SAMPLES_PASSED 0x8C2F +#define GL_SAMPLER_BINDING 0x8919 +#define GL_RGB10_A2UI 0x906F +#define GL_TEXTURE_SWIZZLE_R 0x8E42 +#define GL_TEXTURE_SWIZZLE_G 0x8E43 +#define GL_TEXTURE_SWIZZLE_B 0x8E44 +#define GL_TEXTURE_SWIZZLE_A 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#define GL_TIME_ELAPSED 0x88BF +#define GL_TIMESTAMP 0x8E28 +#define GL_INT_2_10_10_10_REV 0x8D9F +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)(GLuint program, GLuint colorNumber, GLuint index, const GLchar * name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC)(GLuint program, const GLchar * name); +typedef void (APIENTRYP PFNGLGENSAMPLERSPROC)(GLsizei count, GLuint * samplers); +typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC)(GLsizei count, const GLuint * samplers); +typedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC)(GLuint sampler); +typedef void (APIENTRYP PFNGLBINDSAMPLERPROC)(GLuint unit, GLuint sampler); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC)(GLuint sampler, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, const GLint * param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC)(GLuint sampler, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, const GLfloat * param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, const GLint * param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, const GLuint * param); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, GLuint * params); +typedef void (APIENTRYP PFNGLQUERYCOUNTERPROC)(GLuint id, GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC)(GLuint id, GLenum pname, GLint64 * params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC)(GLuint id, GLenum pname, GLuint64 * params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC)(GLuint index, GLuint divisor); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); +typedef void (APIENTRYP PFNGLVERTEXP2UIPROC)(GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP2UIVPROC)(GLenum type, const GLuint * value); +typedef void (APIENTRYP PFNGLVERTEXP3UIPROC)(GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP3UIVPROC)(GLenum type, const GLuint * value); +typedef void (APIENTRYP PFNGLVERTEXP4UIPROC)(GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP4UIVPROC)(GLenum type, const GLuint * value); +typedef void (APIENTRYP PFNGLTEXCOORDP1UIPROC)(GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP1UIVPROC)(GLenum type, const GLuint * coords); +typedef void (APIENTRYP PFNGLTEXCOORDP2UIPROC)(GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP2UIVPROC)(GLenum type, const GLuint * coords); +typedef void (APIENTRYP PFNGLTEXCOORDP3UIPROC)(GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP3UIVPROC)(GLenum type, const GLuint * coords); +typedef void (APIENTRYP PFNGLTEXCOORDP4UIPROC)(GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP4UIVPROC)(GLenum type, const GLuint * coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIPROC)(GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIVPROC)(GLenum texture, GLenum type, const GLuint * coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIPROC)(GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIVPROC)(GLenum texture, GLenum type, const GLuint * coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIPROC)(GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIVPROC)(GLenum texture, GLenum type, const GLuint * coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIPROC)(GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIVPROC)(GLenum texture, GLenum type, const GLuint * coords); +typedef void (APIENTRYP PFNGLNORMALP3UIPROC)(GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLNORMALP3UIVPROC)(GLenum type, const GLuint * coords); +typedef void (APIENTRYP PFNGLCOLORP3UIPROC)(GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLCOLORP3UIVPROC)(GLenum type, const GLuint * color); +typedef void (APIENTRYP PFNGLCOLORP4UIPROC)(GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLCOLORP4UIVPROC)(GLenum type, const GLuint * color); +typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIPROC)(GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIVPROC)(GLenum type, const GLuint * color); +#endif /* GL_VERSION_3_3 */ + +#ifndef GL_VERSION_4_0 +#define GL_VERSION_4_0 1 +#define GL_SAMPLE_SHADING 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE 0x8C37 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F +#define GL_DRAW_INDIRECT_BUFFER 0x8F3F +#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 +#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C +#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D +#define GL_MAX_VERTEX_STREAMS 0x8E71 +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E +#define GL_ACTIVE_SUBROUTINES 0x8DE5 +#define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 +#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 +#define GL_MAX_SUBROUTINES 0x8DE7 +#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8 +#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A +#define GL_COMPATIBLE_SUBROUTINES 0x8E4B +#define GL_PATCHES 0x000E +#define GL_PATCH_VERTICES 0x8E72 +#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 +#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 +#define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75 +#define GL_TESS_GEN_MODE 0x8E76 +#define GL_TESS_GEN_SPACING 0x8E77 +#define GL_TESS_GEN_VERTEX_ORDER 0x8E78 +#define GL_TESS_GEN_POINT_MODE 0x8E79 +#define GL_ISOLINES 0x8E7A +#define GL_QUADS 0x0007 +#define GL_FRACTIONAL_ODD 0x8E7B +#define GL_FRACTIONAL_EVEN 0x8E7C +#define GL_MAX_PATCH_VERTICES 0x8E7D +#define GL_MAX_TESS_GEN_LEVEL 0x8E7E +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82 +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83 +#define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85 +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89 +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1 +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#define GL_TESS_CONTROL_SHADER 0x8E88 +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 +#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 +#define GL_MAX_VERTEX_STREAMS 0x8E71 +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC)(GLfloat value); +typedef void (APIENTRYP PFNGLBLENDEQUATIONIPROC)(GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIPROC)(GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLBLENDFUNCIPROC)(GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIPROC)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (APIENTRYP PFNGLDRAWARRAYSINDIRECTPROC)(GLenum mode, const void * indirect); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINDIRECTPROC)(GLenum mode, GLenum type, const void * indirect); +typedef void (APIENTRYP PFNGLUNIFORM1DPROC)(GLint location, GLdouble x); +typedef void (APIENTRYP PFNGLUNIFORM2DPROC)(GLint location, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLUNIFORM3DPROC)(GLint location, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLUNIFORM4DPROC)(GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLUNIFORM1DVPROC)(GLint location, GLsizei count, const GLdouble * value); +typedef void (APIENTRYP PFNGLUNIFORM2DVPROC)(GLint location, GLsizei count, const GLdouble * value); +typedef void (APIENTRYP PFNGLUNIFORM3DVPROC)(GLint location, GLsizei count, const GLdouble * value); +typedef void (APIENTRYP PFNGLUNIFORM4DVPROC)(GLint location, GLsizei count, const GLdouble * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLGETUNIFORMDVPROC)(GLuint program, GLint location, GLdouble * params); +typedef GLint (APIENTRYP PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC)(GLuint program, GLenum shadertype, const GLchar * name); +typedef GLuint (APIENTRYP PFNGLGETSUBROUTINEINDEXPROC)(GLuint program, GLenum shadertype, const GLchar * name); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC)(GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint * values); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC)(GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei * length, GLchar * name); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINENAMEPROC)(GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei * length, GLchar * name); +typedef void (APIENTRYP PFNGLUNIFORMSUBROUTINESUIVPROC)(GLenum shadertype, GLsizei count, const GLuint * indices); +typedef void (APIENTRYP PFNGLGETUNIFORMSUBROUTINEUIVPROC)(GLenum shadertype, GLint location, GLuint * params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTAGEIVPROC)(GLuint program, GLenum shadertype, GLenum pname, GLint * values); +typedef void (APIENTRYP PFNGLPATCHPARAMETERIPROC)(GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLPATCHPARAMETERFVPROC)(GLenum pname, const GLfloat * values); +typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC)(GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC)(GLsizei n, const GLuint * ids); +typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC)(GLsizei n, GLuint * ids); +typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC)(GLuint id); +typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC)(void); +typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC)(void); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKPROC)(GLenum mode, GLuint id); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC)(GLenum mode, GLuint id, GLuint stream); +typedef void (APIENTRYP PFNGLBEGINQUERYINDEXEDPROC)(GLenum target, GLuint index, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYINDEXEDPROC)(GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLGETQUERYINDEXEDIVPROC)(GLenum target, GLuint index, GLenum pname, GLint * params); +#endif /* GL_VERSION_4_0 */ + +#ifndef GL_VERSION_4_1 +#define GL_VERSION_4_1 1 +#define GL_FIXED 0x140C +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_LOW_FLOAT 0x8DF0 +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_LOW_INT 0x8DF3 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_HIGH_INT 0x8DF5 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_RGB565 0x8D62 +#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_PROGRAM_BINARY_FORMATS 0x87FF +#define GL_VERTEX_SHADER_BIT 0x00000001 +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#define GL_GEOMETRY_SHADER_BIT 0x00000004 +#define GL_TESS_CONTROL_SHADER_BIT 0x00000008 +#define GL_TESS_EVALUATION_SHADER_BIT 0x00000010 +#define GL_ALL_SHADER_BITS 0xFFFFFFFF +#define GL_PROGRAM_SEPARABLE 0x8258 +#define GL_ACTIVE_PROGRAM 0x8259 +#define GL_PROGRAM_PIPELINE_BINDING 0x825A +#define GL_MAX_VIEWPORTS 0x825B +#define GL_VIEWPORT_SUBPIXEL_BITS 0x825C +#define GL_VIEWPORT_BOUNDS_RANGE 0x825D +#define GL_LAYER_PROVOKING_VERTEX 0x825E +#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F +#define GL_UNDEFINED_VERTEX 0x8260 +typedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC)(void); +typedef void (APIENTRYP PFNGLSHADERBINARYPROC)(GLsizei count, const GLuint * shaders, GLenum binaryformat, const void * binary, GLsizei length); +typedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC)(GLenum shadertype, GLenum precisiontype, GLint * range, GLint * precision); +typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC)(GLfloat n, GLfloat f); +typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC)(GLfloat d); +typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC)(GLuint program, GLsizei bufSize, GLsizei * length, GLenum * binaryFormat, void * binary); +typedef void (APIENTRYP PFNGLPROGRAMBINARYPROC)(GLuint program, GLenum binaryFormat, const void * binary, GLsizei length); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC)(GLuint program, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESPROC)(GLuint pipeline, GLbitfield stages, GLuint program); +typedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMPROC)(GLuint pipeline, GLuint program); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC)(GLenum type, GLsizei count, const GLchar *const* strings); +typedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEPROC)(GLuint pipeline); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESPROC)(GLsizei n, const GLuint * pipelines); +typedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESPROC)(GLsizei n, GLuint * pipelines); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEPROC)(GLuint pipeline); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVPROC)(GLuint pipeline, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IPROC)(GLuint program, GLint location, GLint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FPROC)(GLuint program, GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DPROC)(GLuint program, GLint location, GLdouble v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIPROC)(GLuint program, GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IPROC)(GLuint program, GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DPROC)(GLuint program, GLint location, GLdouble v0, GLdouble v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIPROC)(GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IPROC)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DPROC)(GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIPROC)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IPROC)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DPROC)(GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIPROC)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEPROC)(GLuint pipeline); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGPROC)(GLuint pipeline, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DPROC)(GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DPROC)(GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVPROC)(GLuint index, GLenum pname, GLdouble * params); +typedef void (APIENTRYP PFNGLVIEWPORTARRAYVPROC)(GLuint first, GLsizei count, const GLfloat * v); +typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFVPROC)(GLuint index, const GLfloat * v); +typedef void (APIENTRYP PFNGLSCISSORARRAYVPROC)(GLuint first, GLsizei count, const GLint * v); +typedef void (APIENTRYP PFNGLSCISSORINDEXEDPROC)(GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLSCISSORINDEXEDVPROC)(GLuint index, const GLint * v); +typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYVPROC)(GLuint first, GLsizei count, const GLdouble * v); +typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC)(GLuint index, GLdouble n, GLdouble f); +typedef void (APIENTRYP PFNGLGETFLOATI_VPROC)(GLenum target, GLuint index, GLfloat * data); +typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC)(GLenum target, GLuint index, GLdouble * data); +#endif /* GL_VERSION_4_1 */ + +#ifndef GL_VERSION_4_2 +#define GL_VERSION_4_2 1 +#define GL_COPY_READ_BUFFER_BINDING 0x8F36 +#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37 +#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23 +#define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127 +#define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128 +#define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129 +#define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A +#define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B +#define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C +#define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D +#define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E +#define GL_NUM_SAMPLE_COUNTS 0x9380 +#define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC +#define GL_ATOMIC_COUNTER_BUFFER 0x92C0 +#define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1 +#define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2 +#define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3 +#define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB +#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE +#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF +#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0 +#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1 +#define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3 +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5 +#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6 +#define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC +#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9 +#define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA +#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 +#define GL_COMMAND_BARRIER_BIT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 +#define GL_ALL_BARRIER_BITS 0xFFFFFFFF +#define GL_MAX_IMAGE_UNITS 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39 +#define GL_IMAGE_BINDING_NAME 0x8F3A +#define GL_IMAGE_BINDING_LEVEL 0x8F3B +#define GL_IMAGE_BINDING_LAYERED 0x8F3C +#define GL_IMAGE_BINDING_LAYER 0x8F3D +#define GL_IMAGE_BINDING_ACCESS 0x8F3E +#define GL_IMAGE_1D 0x904C +#define GL_IMAGE_2D 0x904D +#define GL_IMAGE_3D 0x904E +#define GL_IMAGE_2D_RECT 0x904F +#define GL_IMAGE_CUBE 0x9050 +#define GL_IMAGE_BUFFER 0x9051 +#define GL_IMAGE_1D_ARRAY 0x9052 +#define GL_IMAGE_2D_ARRAY 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 +#define GL_INT_IMAGE_1D 0x9057 +#define GL_INT_IMAGE_2D 0x9058 +#define GL_INT_IMAGE_3D 0x9059 +#define GL_INT_IMAGE_2D_RECT 0x905A +#define GL_INT_IMAGE_CUBE 0x905B +#define GL_INT_IMAGE_BUFFER 0x905C +#define GL_INT_IMAGE_1D_ARRAY 0x905D +#define GL_INT_IMAGE_2D_ARRAY 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C +#define GL_MAX_IMAGE_SAMPLES 0x906D +#define GL_IMAGE_BINDING_FORMAT 0x906E +#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9 +#define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA +#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB +#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC +#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD +#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE +#define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF +#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F +#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC)(GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint * params); +typedef void (APIENTRYP PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC)(GLuint program, GLuint bufferIndex, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC)(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); +typedef void (APIENTRYP PFNGLMEMORYBARRIERPROC)(GLbitfield barriers); +typedef void (APIENTRYP PFNGLTEXSTORAGE1DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC)(GLenum mode, GLuint id, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC)(GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#endif /* GL_VERSION_4_2 */ + +#ifndef GL_VERSION_4_3 +#define GL_VERSION_4_3 1 +typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9 +#define GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define GL_COMPRESSED_R11_EAC 0x9270 +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define GL_COMPRESSED_RG11_EAC 0x9272 +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 +#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A +#define GL_MAX_ELEMENT_INDEX 0x8D6B +#define GL_COMPUTE_SHADER 0x91B9 +#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB +#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC +#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD +#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262 +#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263 +#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264 +#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265 +#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266 +#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB +#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE +#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF +#define GL_COMPUTE_WORK_GROUP_SIZE 0x8267 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED +#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE +#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF +#define GL_COMPUTE_SHADER_BIT 0x00000020 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_VERTEX_ARRAY 0x8074 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_MAX_UNIFORM_LOCATIONS 0x826E +#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 +#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 +#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 +#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 +#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315 +#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316 +#define GL_MAX_FRAMEBUFFER_LAYERS 0x9317 +#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318 +#define GL_INTERNALFORMAT_SUPPORTED 0x826F +#define GL_INTERNALFORMAT_PREFERRED 0x8270 +#define GL_INTERNALFORMAT_RED_SIZE 0x8271 +#define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 +#define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 +#define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 +#define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 +#define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 +#define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 +#define GL_INTERNALFORMAT_RED_TYPE 0x8278 +#define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 +#define GL_INTERNALFORMAT_BLUE_TYPE 0x827A +#define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B +#define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C +#define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D +#define GL_MAX_WIDTH 0x827E +#define GL_MAX_HEIGHT 0x827F +#define GL_MAX_DEPTH 0x8280 +#define GL_MAX_LAYERS 0x8281 +#define GL_MAX_COMBINED_DIMENSIONS 0x8282 +#define GL_COLOR_COMPONENTS 0x8283 +#define GL_DEPTH_COMPONENTS 0x8284 +#define GL_STENCIL_COMPONENTS 0x8285 +#define GL_COLOR_RENDERABLE 0x8286 +#define GL_DEPTH_RENDERABLE 0x8287 +#define GL_STENCIL_RENDERABLE 0x8288 +#define GL_FRAMEBUFFER_RENDERABLE 0x8289 +#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A +#define GL_FRAMEBUFFER_BLEND 0x828B +#define GL_READ_PIXELS 0x828C +#define GL_READ_PIXELS_FORMAT 0x828D +#define GL_READ_PIXELS_TYPE 0x828E +#define GL_TEXTURE_IMAGE_FORMAT 0x828F +#define GL_TEXTURE_IMAGE_TYPE 0x8290 +#define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 +#define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 +#define GL_MIPMAP 0x8293 +#define GL_MANUAL_GENERATE_MIPMAP 0x8294 +#define GL_AUTO_GENERATE_MIPMAP 0x8295 +#define GL_COLOR_ENCODING 0x8296 +#define GL_SRGB_READ 0x8297 +#define GL_SRGB_WRITE 0x8298 +#define GL_FILTER 0x829A +#define GL_VERTEX_TEXTURE 0x829B +#define GL_TESS_CONTROL_TEXTURE 0x829C +#define GL_TESS_EVALUATION_TEXTURE 0x829D +#define GL_GEOMETRY_TEXTURE 0x829E +#define GL_FRAGMENT_TEXTURE 0x829F +#define GL_COMPUTE_TEXTURE 0x82A0 +#define GL_TEXTURE_SHADOW 0x82A1 +#define GL_TEXTURE_GATHER 0x82A2 +#define GL_TEXTURE_GATHER_SHADOW 0x82A3 +#define GL_SHADER_IMAGE_LOAD 0x82A4 +#define GL_SHADER_IMAGE_STORE 0x82A5 +#define GL_SHADER_IMAGE_ATOMIC 0x82A6 +#define GL_IMAGE_TEXEL_SIZE 0x82A7 +#define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 +#define GL_IMAGE_PIXEL_FORMAT 0x82A9 +#define GL_IMAGE_PIXEL_TYPE 0x82AA +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF +#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1 +#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2 +#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3 +#define GL_CLEAR_BUFFER 0x82B4 +#define GL_TEXTURE_VIEW 0x82B5 +#define GL_VIEW_COMPATIBILITY_CLASS 0x82B6 +#define GL_FULL_SUPPORT 0x82B7 +#define GL_CAVEAT_SUPPORT 0x82B8 +#define GL_IMAGE_CLASS_4_X_32 0x82B9 +#define GL_IMAGE_CLASS_2_X_32 0x82BA +#define GL_IMAGE_CLASS_1_X_32 0x82BB +#define GL_IMAGE_CLASS_4_X_16 0x82BC +#define GL_IMAGE_CLASS_2_X_16 0x82BD +#define GL_IMAGE_CLASS_1_X_16 0x82BE +#define GL_IMAGE_CLASS_4_X_8 0x82BF +#define GL_IMAGE_CLASS_2_X_8 0x82C0 +#define GL_IMAGE_CLASS_1_X_8 0x82C1 +#define GL_IMAGE_CLASS_11_11_10 0x82C2 +#define GL_IMAGE_CLASS_10_10_10_2 0x82C3 +#define GL_VIEW_CLASS_128_BITS 0x82C4 +#define GL_VIEW_CLASS_96_BITS 0x82C5 +#define GL_VIEW_CLASS_64_BITS 0x82C6 +#define GL_VIEW_CLASS_48_BITS 0x82C7 +#define GL_VIEW_CLASS_32_BITS 0x82C8 +#define GL_VIEW_CLASS_24_BITS 0x82C9 +#define GL_VIEW_CLASS_16_BITS 0x82CA +#define GL_VIEW_CLASS_8_BITS 0x82CB +#define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC +#define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD +#define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE +#define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF +#define GL_VIEW_CLASS_RGTC1_RED 0x82D0 +#define GL_VIEW_CLASS_RGTC2_RG 0x82D1 +#define GL_VIEW_CLASS_BPTC_UNORM 0x82D2 +#define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3 +#define GL_UNIFORM 0x92E1 +#define GL_UNIFORM_BLOCK 0x92E2 +#define GL_PROGRAM_INPUT 0x92E3 +#define GL_PROGRAM_OUTPUT 0x92E4 +#define GL_BUFFER_VARIABLE 0x92E5 +#define GL_SHADER_STORAGE_BLOCK 0x92E6 +#define GL_VERTEX_SUBROUTINE 0x92E8 +#define GL_TESS_CONTROL_SUBROUTINE 0x92E9 +#define GL_TESS_EVALUATION_SUBROUTINE 0x92EA +#define GL_GEOMETRY_SUBROUTINE 0x92EB +#define GL_FRAGMENT_SUBROUTINE 0x92EC +#define GL_COMPUTE_SUBROUTINE 0x92ED +#define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE +#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF +#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0 +#define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 +#define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 +#define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 +#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4 +#define GL_ACTIVE_RESOURCES 0x92F5 +#define GL_MAX_NAME_LENGTH 0x92F6 +#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7 +#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8 +#define GL_NAME_LENGTH 0x92F9 +#define GL_TYPE 0x92FA +#define GL_ARRAY_SIZE 0x92FB +#define GL_OFFSET 0x92FC +#define GL_BLOCK_INDEX 0x92FD +#define GL_ARRAY_STRIDE 0x92FE +#define GL_MATRIX_STRIDE 0x92FF +#define GL_IS_ROW_MAJOR 0x9300 +#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 +#define GL_BUFFER_BINDING 0x9302 +#define GL_BUFFER_DATA_SIZE 0x9303 +#define GL_NUM_ACTIVE_VARIABLES 0x9304 +#define GL_ACTIVE_VARIABLES 0x9305 +#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308 +#define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309 +#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A +#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B +#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C +#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D +#define GL_LOCATION 0x930E +#define GL_LOCATION_INDEX 0x930F +#define GL_IS_PER_PATCH 0x92E7 +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3 +#define GL_SHADER_STORAGE_BUFFER_START 0x90D4 +#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5 +#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6 +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9 +#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA +#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB +#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC +#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD +#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE +#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF +#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 +#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39 +#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA +#define GL_TEXTURE_BUFFER_OFFSET 0x919D +#define GL_TEXTURE_BUFFER_SIZE 0x919E +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F +#define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB +#define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC +#define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD +#define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE +#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF +#define GL_VERTEX_ATTRIB_BINDING 0x82D4 +#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5 +#define GL_VERTEX_BINDING_DIVISOR 0x82D6 +#define GL_VERTEX_BINDING_OFFSET 0x82D7 +#define GL_VERTEX_BINDING_STRIDE 0x82D8 +#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9 +#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA +#define GL_VERTEX_BINDING_BUFFER 0x8F4F +#define GL_DISPLAY_LIST 0x82E7 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_STACK_OVERFLOW 0x0503 +typedef void (APIENTRYP PFNGLCLEARBUFFERDATAPROC)(GLenum target, GLenum internalformat, GLenum format, GLenum type, const void * data); +typedef void (APIENTRYP PFNGLCLEARBUFFERSUBDATAPROC)(GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void * data); +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC)(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEINDIRECTPROC)(GLintptr indirect); +typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATAPROC)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETINTERNALFORMATI64VPROC)(GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64 * params); +typedef void (APIENTRYP PFNGLINVALIDATETEXSUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLINVALIDATETEXIMAGEPROC)(GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLINVALIDATEBUFFERSUBDATAPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer); +typedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC)(GLenum target, GLsizei numAttachments, const GLenum * attachments); +typedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC)(GLenum target, GLsizei numAttachments, const GLenum * attachments, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTPROC)(GLenum mode, const void * indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTPROC)(GLenum mode, GLenum type, const void * indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLGETPROGRAMINTERFACEIVPROC)(GLuint program, GLenum programInterface, GLenum pname, GLint * params); +typedef GLuint (APIENTRYP PFNGLGETPROGRAMRESOURCEINDEXPROC)(GLuint program, GLenum programInterface, const GLchar * name); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCENAMEPROC)(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei * length, GLchar * name); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEIVPROC)(GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum * props, GLsizei count, GLsizei * length, GLint * params); +typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONPROC)(GLuint program, GLenum programInterface, const GLchar * name); +typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC)(GLuint program, GLenum programInterface, const GLchar * name); +typedef void (APIENTRYP PFNGLSHADERSTORAGEBLOCKBINDINGPROC)(GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +typedef void (APIENTRYP PFNGLTEXBUFFERRANGEPROC)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXSTORAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTUREVIEWPROC)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERPROC)(GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATPROC)(GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATPROC)(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATPROC)(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBBINDINGPROC)(GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXBINDINGDIVISORPROC)(GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint * ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar * buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC)(GLDEBUGPROC callback, const void * userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC)(GLuint count, GLsizei bufSize, GLenum * sources, GLenum * types, GLuint * ids, GLenum * severities, GLsizei * lengths, GLchar * messageLog); +typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC)(GLenum source, GLuint id, GLsizei length, const GLchar * message); +typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC)(void); +typedef void (APIENTRYP PFNGLOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar * label); +typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei * length, GLchar * label); +typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC)(const void * ptr, GLsizei length, const GLchar * label); +typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC)(const void * ptr, GLsizei bufSize, GLsizei * length, GLchar * label); +#endif /* GL_VERSION_4_3 */ + +#ifndef GL_VERSION_4_4 +#define GL_VERSION_4_4 1 +#define GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5 +#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221 +#define GL_TEXTURE_BUFFER_BINDING 0x8C2A +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_PERSISTENT_BIT 0x0040 +#define GL_MAP_COHERENT_BIT 0x0080 +#define GL_DYNAMIC_STORAGE_BIT 0x0100 +#define GL_CLIENT_STORAGE_BIT 0x0200 +#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000 +#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F +#define GL_BUFFER_STORAGE_FLAGS 0x8220 +#define GL_CLEAR_TEXTURE 0x9365 +#define GL_LOCATION_COMPONENT 0x934A +#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B +#define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C +#define GL_QUERY_BUFFER 0x9192 +#define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000 +#define GL_QUERY_BUFFER_BINDING 0x9193 +#define GL_QUERY_RESULT_NO_WAIT 0x9194 +#define GL_MIRROR_CLAMP_TO_EDGE 0x8743 +#define GL_STENCIL_INDEX 0x1901 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +typedef void (APIENTRYP PFNGLBUFFERSTORAGEPROC)(GLenum target, GLsizeiptr size, const void * data, GLbitfield flags); +typedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC)(GLuint texture, GLint level, GLenum format, GLenum type, const void * data); +typedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * data); +typedef void (APIENTRYP PFNGLBINDBUFFERSBASEPROC)(GLenum target, GLuint first, GLsizei count, const GLuint * buffers); +typedef void (APIENTRYP PFNGLBINDBUFFERSRANGEPROC)(GLenum target, GLuint first, GLsizei count, const GLuint * buffers, const GLintptr * offsets, const GLsizeiptr * sizes); +typedef void (APIENTRYP PFNGLBINDTEXTURESPROC)(GLuint first, GLsizei count, const GLuint * textures); +typedef void (APIENTRYP PFNGLBINDSAMPLERSPROC)(GLuint first, GLsizei count, const GLuint * samplers); +typedef void (APIENTRYP PFNGLBINDIMAGETEXTURESPROC)(GLuint first, GLsizei count, const GLuint * textures); +typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERSPROC)(GLuint first, GLsizei count, const GLuint * buffers, const GLintptr * offsets, const GLsizei * strides); +#endif /* GL_VERSION_4_4 */ + +#ifndef GL_VERSION_4_5 +#define GL_VERSION_4_5 1 +#define GL_CONTEXT_LOST 0x0507 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_NEGATIVE_ONE_TO_ONE 0x935E +#define GL_ZERO_TO_ONE 0x935F +#define GL_CLIP_ORIGIN 0x935C +#define GL_CLIP_DEPTH_MODE 0x935D +#define GL_QUERY_WAIT_INVERTED 0x8E17 +#define GL_QUERY_NO_WAIT_INVERTED 0x8E18 +#define GL_QUERY_BY_REGION_WAIT_INVERTED 0x8E19 +#define GL_QUERY_BY_REGION_NO_WAIT_INVERTED 0x8E1A +#define GL_MAX_CULL_DISTANCES 0x82F9 +#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES 0x82FA +#define GL_TEXTURE_TARGET 0x1006 +#define GL_QUERY_TARGET 0x82EA +#define GL_TEXTURE_BINDING_1D 0x8068 +#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_TEXTURE_BINDING_BUFFER 0x8C2C +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#define GL_BACK 0x0405 +#define GL_NO_ERROR 0 +#define GL_GUILTY_CONTEXT_RESET 0x8253 +#define GL_INNOCENT_CONTEXT_RESET 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY 0x8256 +#define GL_LOSE_CONTEXT_ON_RESET 0x8252 +#define GL_NO_RESET_NOTIFICATION 0x8261 +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT 0x00000004 +#define GL_CONTEXT_LOST 0x0507 +#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB +#define GL_NONE 0 +#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC +typedef void (APIENTRYP PFNGLCLIPCONTROLPROC)(GLenum origin, GLenum depth); +typedef void (APIENTRYP PFNGLCREATETRANSFORMFEEDBACKSPROC)(GLsizei n, GLuint * ids); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC)(GLuint xfb, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC)(GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKIVPROC)(GLuint xfb, GLenum pname, GLint * param); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI_VPROC)(GLuint xfb, GLenum pname, GLuint index, GLint * param); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI64_VPROC)(GLuint xfb, GLenum pname, GLuint index, GLint64 * param); +typedef void (APIENTRYP PFNGLCREATEBUFFERSPROC)(GLsizei n, GLuint * buffers); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEPROC)(GLuint buffer, GLsizeiptr size, const void * data, GLbitfield flags); +typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAPROC)(GLuint buffer, GLsizeiptr size, const void * data, GLenum usage); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, const void * data); +typedef void (APIENTRYP PFNGLCOPYNAMEDBUFFERSUBDATAPROC)(GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAPROC)(GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void * data); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAPROC)(GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void * data); +typedef void * (APIENTRYP PFNGLMAPNAMEDBUFFERPROC)(GLuint buffer, GLenum access); +typedef void * (APIENTRYP PFNGLMAPNAMEDBUFFERRANGEPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFERPROC)(GLuint buffer); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVPROC)(GLuint buffer, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERI64VPROC)(GLuint buffer, GLenum pname, GLint64 * params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVPROC)(GLuint buffer, GLenum pname, void ** params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, void * data); +typedef void (APIENTRYP PFNGLCREATEFRAMEBUFFERSPROC)(GLsizei n, GLuint * framebuffers); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC)(GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC)(GLuint framebuffer, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREPROC)(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC)(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC)(GLuint framebuffer, GLenum buf); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC)(GLuint framebuffer, GLsizei n, const GLenum * bufs); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC)(GLuint framebuffer, GLenum src); +typedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC)(GLuint framebuffer, GLsizei numAttachments, const GLenum * attachments); +typedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC)(GLuint framebuffer, GLsizei numAttachments, const GLenum * attachments, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERIVPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint * value); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint * value); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFVPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat * value); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFIPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +typedef void (APIENTRYP PFNGLBLITNAMEDFRAMEBUFFERPROC)(GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC)(GLuint framebuffer, GLenum target); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC)(GLuint framebuffer, GLenum pname, GLint * param); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLuint framebuffer, GLenum attachment, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLCREATERENDERBUFFERSPROC)(GLsizei n, GLuint * renderbuffers); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEPROC)(GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC)(GLuint renderbuffer, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLCREATETEXTURESPROC)(GLenum target, GLsizei n, GLuint * textures); +typedef void (APIENTRYP PFNGLTEXTUREBUFFERPROC)(GLuint texture, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEPROC)(GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DPROC)(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DPROC)(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DPROC)(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC)(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC)(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DPROC)(GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC)(GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void * data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void * data); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DPROC)(GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFPROC)(GLuint texture, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVPROC)(GLuint texture, GLenum pname, const GLfloat * param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIPROC)(GLuint texture, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVPROC)(GLuint texture, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVPROC)(GLuint texture, GLenum pname, const GLuint * params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVPROC)(GLuint texture, GLenum pname, const GLint * param); +typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPPROC)(GLuint texture); +typedef void (APIENTRYP PFNGLBINDTEXTUREUNITPROC)(GLuint unit, GLuint texture); +typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEPROC)(GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void * pixels); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC)(GLuint texture, GLint level, GLsizei bufSize, void * pixels); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVPROC)(GLuint texture, GLint level, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVPROC)(GLuint texture, GLint level, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVPROC)(GLuint texture, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVPROC)(GLuint texture, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVPROC)(GLuint texture, GLenum pname, GLuint * params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVPROC)(GLuint texture, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLCREATEVERTEXARRAYSPROC)(GLsizei n, GLuint * arrays); +typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBPROC)(GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBPROC)(GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLVERTEXARRAYELEMENTBUFFERPROC)(GLuint vaobj, GLuint buffer); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERPROC)(GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERSPROC)(GLuint vaobj, GLuint first, GLsizei count, const GLuint * buffers, const GLintptr * offsets, const GLsizei * strides); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBBINDINGPROC)(GLuint vaobj, GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBFORMATPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBIFORMATPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBLFORMATPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYBINDINGDIVISORPROC)(GLuint vaobj, GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYIVPROC)(GLuint vaobj, GLenum pname, GLint * param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXEDIVPROC)(GLuint vaobj, GLuint index, GLenum pname, GLint * param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXED64IVPROC)(GLuint vaobj, GLuint index, GLenum pname, GLint64 * param); +typedef void (APIENTRYP PFNGLCREATESAMPLERSPROC)(GLsizei n, GLuint * samplers); +typedef void (APIENTRYP PFNGLCREATEPROGRAMPIPELINESPROC)(GLsizei n, GLuint * pipelines); +typedef void (APIENTRYP PFNGLCREATEQUERIESPROC)(GLenum target, GLsizei n, GLuint * ids); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTI64VPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTIVPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUI64VPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUIVPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLMEMORYBARRIERBYREGIONPROC)(GLbitfield barriers); +typedef void (APIENTRYP PFNGLGETTEXTURESUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void * pixels); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void * pixels); +typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSPROC)(void); +typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint lod, GLsizei bufSize, void * pixels); +typedef void (APIENTRYP PFNGLGETNTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void * pixels); +typedef void (APIENTRYP PFNGLGETNUNIFORMDVPROC)(GLuint program, GLint location, GLsizei bufSize, GLdouble * params); +typedef void (APIENTRYP PFNGLGETNUNIFORMFVPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat * params); +typedef void (APIENTRYP PFNGLGETNUNIFORMIVPROC)(GLuint program, GLint location, GLsizei bufSize, GLint * params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUIVPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint * params); +typedef void (APIENTRYP PFNGLREADNPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void * data); +typedef void (APIENTRYP PFNGLGETNMAPDVPROC)(GLenum target, GLenum query, GLsizei bufSize, GLdouble * v); +typedef void (APIENTRYP PFNGLGETNMAPFVPROC)(GLenum target, GLenum query, GLsizei bufSize, GLfloat * v); +typedef void (APIENTRYP PFNGLGETNMAPIVPROC)(GLenum target, GLenum query, GLsizei bufSize, GLint * v); +typedef void (APIENTRYP PFNGLGETNPIXELMAPFVPROC)(GLenum map, GLsizei bufSize, GLfloat * values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUIVPROC)(GLenum map, GLsizei bufSize, GLuint * values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUSVPROC)(GLenum map, GLsizei bufSize, GLushort * values); +typedef void (APIENTRYP PFNGLGETNPOLYGONSTIPPLEPROC)(GLsizei bufSize, GLubyte * pattern); +typedef void (APIENTRYP PFNGLGETNCOLORTABLEPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void * table); +typedef void (APIENTRYP PFNGLGETNCONVOLUTIONFILTERPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void * image); +typedef void (APIENTRYP PFNGLGETNSEPARABLEFILTERPROC)(GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void * row, GLsizei columnBufSize, void * column, void * span); +typedef void (APIENTRYP PFNGLGETNHISTOGRAMPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void * values); +typedef void (APIENTRYP PFNGLGETNMINMAXPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void * values); +typedef void (APIENTRYP PFNGLTEXTUREBARRIERPROC)(void); +#endif /* GL_VERSION_4_5 */ + +#ifndef GL_VERSION_4_6 +#define GL_VERSION_4_6 1 +#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB +#define GL_NONE 0 +#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC +#define GL_SHADER_BINARY_FORMAT_SPIR_V 0x9551 +#define GL_SPIR_V_BINARY 0x9552 +#define GL_PARAMETER_BUFFER 0x80EE +#define GL_PARAMETER_BUFFER_BINDING 0x80EF +#define GL_CONTEXT_FLAG_NO_ERROR_BIT 0x00000008 +#define GL_VERTICES_SUBMITTED 0x82EE +#define GL_PRIMITIVES_SUBMITTED 0x82EF +#define GL_VERTEX_SHADER_INVOCATIONS 0x82F0 +#define GL_TESS_CONTROL_SHADER_PATCHES 0x82F1 +#define GL_TESS_EVALUATION_SHADER_INVOCATIONS 0x82F2 +#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F +#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED 0x82F3 +#define GL_FRAGMENT_SHADER_INVOCATIONS 0x82F4 +#define GL_COMPUTE_SHADER_INVOCATIONS 0x82F5 +#define GL_CLIPPING_INPUT_PRIMITIVES 0x82F6 +#define GL_CLIPPING_OUTPUT_PRIMITIVES 0x82F7 +#define GL_POLYGON_OFFSET_CLAMP 0x8E1B +#define GL_SPIR_V_EXTENSIONS 0x9553 +#define GL_NUM_SPIR_V_EXTENSIONS 0x9554 +#define GL_TEXTURE_MAX_ANISOTROPY 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF +#define GL_TRANSFORM_FEEDBACK_OVERFLOW 0x82EC +#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW 0x82ED +typedef void (APIENTRYP PFNGLSPECIALIZESHADERPROC)(GLuint shader, const GLchar * pEntryPoint, GLuint numSpecializationConstants, const GLuint * pConstantIndex, const GLuint * pConstantValue); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC)(GLenum mode, const void * indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC)(GLenum mode, GLenum type, const void * indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPPROC)(GLfloat factor, GLfloat units, GLfloat clamp); +#endif /* GL_VERSION_4_6 */ + +#if defined(GLBIND_WGL) +#ifndef WGL_VERSION_1_0 +#define WGL_VERSION_1_0 1 +#define WGL_FONT_LINES 0 +#define WGL_FONT_POLYGONS 1 +#define WGL_SWAP_MAIN_PLANE 0x00000001 +#define WGL_SWAP_OVERLAY1 0x00000002 +#define WGL_SWAP_OVERLAY2 0x00000004 +#define WGL_SWAP_OVERLAY3 0x00000008 +#define WGL_SWAP_OVERLAY4 0x00000010 +#define WGL_SWAP_OVERLAY5 0x00000020 +#define WGL_SWAP_OVERLAY6 0x00000040 +#define WGL_SWAP_OVERLAY7 0x00000080 +#define WGL_SWAP_OVERLAY8 0x00000100 +#define WGL_SWAP_OVERLAY9 0x00000200 +#define WGL_SWAP_OVERLAY10 0x00000400 +#define WGL_SWAP_OVERLAY11 0x00000800 +#define WGL_SWAP_OVERLAY12 0x00001000 +#define WGL_SWAP_OVERLAY13 0x00002000 +#define WGL_SWAP_OVERLAY14 0x00004000 +#define WGL_SWAP_OVERLAY15 0x00008000 +#define WGL_SWAP_UNDERLAY1 0x00010000 +#define WGL_SWAP_UNDERLAY2 0x00020000 +#define WGL_SWAP_UNDERLAY3 0x00040000 +#define WGL_SWAP_UNDERLAY4 0x00080000 +#define WGL_SWAP_UNDERLAY5 0x00100000 +#define WGL_SWAP_UNDERLAY6 0x00200000 +#define WGL_SWAP_UNDERLAY7 0x00400000 +#define WGL_SWAP_UNDERLAY8 0x00800000 +#define WGL_SWAP_UNDERLAY9 0x01000000 +#define WGL_SWAP_UNDERLAY10 0x02000000 +#define WGL_SWAP_UNDERLAY11 0x04000000 +#define WGL_SWAP_UNDERLAY12 0x08000000 +#define WGL_SWAP_UNDERLAY13 0x10000000 +#define WGL_SWAP_UNDERLAY14 0x20000000 +#define WGL_SWAP_UNDERLAY15 0x40000000 +typedef int (APIENTRYP PFNCHOOSEPIXELFORMATPROC)(HDC hDc, const PIXELFORMATDESCRIPTOR * pPfd); +typedef int (APIENTRYP PFNDESCRIBEPIXELFORMATPROC)(HDC hdc, int ipfd, UINT cjpfd, const PIXELFORMATDESCRIPTOR * ppfd); +typedef UINT (APIENTRYP PFNGETENHMETAFILEPIXELFORMATPROC)(HENHMETAFILE hemf, const PIXELFORMATDESCRIPTOR * ppfd); +typedef int (APIENTRYP PFNGETPIXELFORMATPROC)(HDC hdc); +typedef BOOL (APIENTRYP PFNSETPIXELFORMATPROC)(HDC hdc, int ipfd, const PIXELFORMATDESCRIPTOR * ppfd); +typedef BOOL (APIENTRYP PFNSWAPBUFFERSPROC)(HDC hdc); +typedef BOOL (APIENTRYP PFNWGLCOPYCONTEXTPROC)(HGLRC hglrcSrc, HGLRC hglrcDst, UINT mask); +typedef HGLRC (APIENTRYP PFNWGLCREATECONTEXTPROC)(HDC hDc); +typedef HGLRC (APIENTRYP PFNWGLCREATELAYERCONTEXTPROC)(HDC hDc, int level); +typedef BOOL (APIENTRYP PFNWGLDELETECONTEXTPROC)(HGLRC oldContext); +typedef BOOL (APIENTRYP PFNWGLDESCRIBELAYERPLANEPROC)(HDC hDc, int pixelFormat, int layerPlane, UINT nBytes, const LAYERPLANEDESCRIPTOR * plpd); +typedef HGLRC (APIENTRYP PFNWGLGETCURRENTCONTEXTPROC)(void); +typedef HDC (APIENTRYP PFNWGLGETCURRENTDCPROC)(void); +typedef int (APIENTRYP PFNWGLGETLAYERPALETTEENTRIESPROC)(HDC hdc, int iLayerPlane, int iStart, int cEntries, const COLORREF * pcr); +typedef PROC (APIENTRYP PFNWGLGETPROCADDRESSPROC)(LPCSTR lpszProc); +typedef BOOL (APIENTRYP PFNWGLMAKECURRENTPROC)(HDC hDc, HGLRC newContext); +typedef BOOL (APIENTRYP PFNWGLREALIZELAYERPALETTEPROC)(HDC hdc, int iLayerPlane, BOOL bRealize); +typedef int (APIENTRYP PFNWGLSETLAYERPALETTEENTRIESPROC)(HDC hdc, int iLayerPlane, int iStart, int cEntries, const COLORREF * pcr); +typedef BOOL (APIENTRYP PFNWGLSHARELISTSPROC)(HGLRC hrcSrvShare, HGLRC hrcSrvSource); +typedef BOOL (APIENTRYP PFNWGLSWAPLAYERBUFFERSPROC)(HDC hdc, UINT fuFlags); +typedef BOOL (APIENTRYP PFNWGLUSEFONTBITMAPSPROC)(HDC hDC, DWORD first, DWORD count, DWORD listBase); +typedef BOOL (APIENTRYP PFNWGLUSEFONTBITMAPSAPROC)(HDC hDC, DWORD first, DWORD count, DWORD listBase); +typedef BOOL (APIENTRYP PFNWGLUSEFONTBITMAPSWPROC)(HDC hDC, DWORD first, DWORD count, DWORD listBase); +typedef BOOL (APIENTRYP PFNWGLUSEFONTOUTLINESPROC)(HDC hDC, DWORD first, DWORD count, DWORD listBase, FLOAT deviation, FLOAT extrusion, int format, LPGLYPHMETRICSFLOAT lpgmf); +typedef BOOL (APIENTRYP PFNWGLUSEFONTOUTLINESAPROC)(HDC hDC, DWORD first, DWORD count, DWORD listBase, FLOAT deviation, FLOAT extrusion, int format, LPGLYPHMETRICSFLOAT lpgmf); +typedef BOOL (APIENTRYP PFNWGLUSEFONTOUTLINESWPROC)(HDC hDC, DWORD first, DWORD count, DWORD listBase, FLOAT deviation, FLOAT extrusion, int format, LPGLYPHMETRICSFLOAT lpgmf); +#endif /* WGL_VERSION_1_0 */ +#endif /* GLBIND_WGL */ + +#if defined(GLBIND_GLX) +#ifndef GLX_VERSION_1_0 +#define GLX_VERSION_1_0 1 +typedef struct __GLXcontextRec *GLXContext; +typedef glbind_XID GLXDrawable; +typedef glbind_XID GLXPixmap; +#define GLX_EXTENSION_NAME "GLX" +#define GLX_PbufferClobber 0 +#define GLX_BufferSwapComplete 1 +#define __GLX_NUMBER_EVENTS 17 +#define GLX_BAD_SCREEN 1 +#define GLX_BAD_ATTRIBUTE 2 +#define GLX_NO_EXTENSION 3 +#define GLX_BAD_VISUAL 4 +#define GLX_BAD_CONTEXT 5 +#define GLX_BAD_VALUE 6 +#define GLX_BAD_ENUM 7 +#define GLX_USE_GL 1 +#define GLX_BUFFER_SIZE 2 +#define GLX_LEVEL 3 +#define GLX_RGBA 4 +#define GLX_DOUBLEBUFFER 5 +#define GLX_STEREO 6 +#define GLX_AUX_BUFFERS 7 +#define GLX_RED_SIZE 8 +#define GLX_GREEN_SIZE 9 +#define GLX_BLUE_SIZE 10 +#define GLX_ALPHA_SIZE 11 +#define GLX_DEPTH_SIZE 12 +#define GLX_STENCIL_SIZE 13 +#define GLX_ACCUM_RED_SIZE 14 +#define GLX_ACCUM_GREEN_SIZE 15 +#define GLX_ACCUM_BLUE_SIZE 16 +#define GLX_ACCUM_ALPHA_SIZE 17 +typedef glbind_XVisualInfo* (APIENTRYP PFNGLXCHOOSEVISUALPROC)(glbind_Display* dpy, int screen, int * attribList); +typedef GLXContext (APIENTRYP PFNGLXCREATECONTEXTPROC)(glbind_Display* dpy, glbind_XVisualInfo* vis, GLXContext shareList, glbind_Bool direct); +typedef void (APIENTRYP PFNGLXDESTROYCONTEXTPROC)(glbind_Display* dpy, GLXContext ctx); +typedef glbind_Bool (APIENTRYP PFNGLXMAKECURRENTPROC)(glbind_Display* dpy, GLXDrawable drawable, GLXContext ctx); +typedef void (APIENTRYP PFNGLXCOPYCONTEXTPROC)(glbind_Display* dpy, GLXContext src, GLXContext dst, unsigned long mask); +typedef void (APIENTRYP PFNGLXSWAPBUFFERSPROC)(glbind_Display* dpy, GLXDrawable drawable); +typedef GLXPixmap (APIENTRYP PFNGLXCREATEGLXPIXMAPPROC)(glbind_Display* dpy, glbind_XVisualInfo* visual, glbind_Pixmap pixmap); +typedef void (APIENTRYP PFNGLXDESTROYGLXPIXMAPPROC)(glbind_Display* dpy, GLXPixmap pixmap); +typedef glbind_Bool (APIENTRYP PFNGLXQUERYEXTENSIONPROC)(glbind_Display* dpy, int * errorb, int * event); +typedef glbind_Bool (APIENTRYP PFNGLXQUERYVERSIONPROC)(glbind_Display* dpy, int * maj, int * min); +typedef glbind_Bool (APIENTRYP PFNGLXISDIRECTPROC)(glbind_Display* dpy, GLXContext ctx); +typedef int (APIENTRYP PFNGLXGETCONFIGPROC)(glbind_Display* dpy, glbind_XVisualInfo* visual, int attrib, int * value); +typedef GLXContext (APIENTRYP PFNGLXGETCURRENTCONTEXTPROC)(void); +typedef GLXDrawable (APIENTRYP PFNGLXGETCURRENTDRAWABLEPROC)(void); +typedef void (APIENTRYP PFNGLXWAITGLPROC)(void); +typedef void (APIENTRYP PFNGLXWAITXPROC)(void); +typedef void (APIENTRYP PFNGLXUSEXFONTPROC)(glbind_Font font, int first, int count, int list); +#endif /* GLX_VERSION_1_0 */ + +#ifndef GLX_VERSION_1_1 +#define GLX_VERSION_1_1 1 +#define GLX_VENDOR 0x1 +#define GLX_VERSION 0x2 +#define GLX_EXTENSIONS 0x3 +typedef const char * (APIENTRYP PFNGLXQUERYEXTENSIONSSTRINGPROC)(glbind_Display* dpy, int screen); +typedef const char * (APIENTRYP PFNGLXQUERYSERVERSTRINGPROC)(glbind_Display* dpy, int screen, int name); +typedef const char * (APIENTRYP PFNGLXGETCLIENTSTRINGPROC)(glbind_Display* dpy, int name); +#endif /* GLX_VERSION_1_1 */ + +#ifndef GLX_VERSION_1_2 +#define GLX_VERSION_1_2 1 +typedef glbind_Display* (APIENTRYP PFNGLXGETCURRENTDISPLAYPROC)(void); +#endif /* GLX_VERSION_1_2 */ + +#ifndef GLX_VERSION_1_3 +#define GLX_VERSION_1_3 1 +typedef glbind_XID GLXContextID; +typedef struct __GLXFBConfigRec *GLXFBConfig; +typedef glbind_XID GLXWindow; +typedef glbind_XID GLXPbuffer; +#define GLX_WINDOW_BIT 0x00000001 +#define GLX_PIXMAP_BIT 0x00000002 +#define GLX_PBUFFER_BIT 0x00000004 +#define GLX_RGBA_BIT 0x00000001 +#define GLX_COLOR_INDEX_BIT 0x00000002 +#define GLX_PBUFFER_CLOBBER_MASK 0x08000000 +#define GLX_FRONT_LEFT_BUFFER_BIT 0x00000001 +#define GLX_FRONT_RIGHT_BUFFER_BIT 0x00000002 +#define GLX_BACK_LEFT_BUFFER_BIT 0x00000004 +#define GLX_BACK_RIGHT_BUFFER_BIT 0x00000008 +#define GLX_AUX_BUFFERS_BIT 0x00000010 +#define GLX_DEPTH_BUFFER_BIT 0x00000020 +#define GLX_STENCIL_BUFFER_BIT 0x00000040 +#define GLX_ACCUM_BUFFER_BIT 0x00000080 +#define GLX_CONFIG_CAVEAT 0x20 +#define GLX_X_VISUAL_TYPE 0x22 +#define GLX_TRANSPARENT_TYPE 0x23 +#define GLX_TRANSPARENT_INDEX_VALUE 0x24 +#define GLX_TRANSPARENT_RED_VALUE 0x25 +#define GLX_TRANSPARENT_GREEN_VALUE 0x26 +#define GLX_TRANSPARENT_BLUE_VALUE 0x27 +#define GLX_TRANSPARENT_ALPHA_VALUE 0x28 +#define GLX_DONT_CARE 0xFFFFFFFF +#define GLX_NONE 0x8000 +#define GLX_SLOW_CONFIG 0x8001 +#define GLX_TRUE_COLOR 0x8002 +#define GLX_DIRECT_COLOR 0x8003 +#define GLX_PSEUDO_COLOR 0x8004 +#define GLX_STATIC_COLOR 0x8005 +#define GLX_GRAY_SCALE 0x8006 +#define GLX_STATIC_GRAY 0x8007 +#define GLX_TRANSPARENT_RGB 0x8008 +#define GLX_TRANSPARENT_INDEX 0x8009 +#define GLX_VISUAL_ID 0x800B +#define GLX_SCREEN 0x800C +#define GLX_NON_CONFORMANT_CONFIG 0x800D +#define GLX_DRAWABLE_TYPE 0x8010 +#define GLX_RENDER_TYPE 0x8011 +#define GLX_X_RENDERABLE 0x8012 +#define GLX_FBCONFIG_ID 0x8013 +#define GLX_RGBA_TYPE 0x8014 +#define GLX_COLOR_INDEX_TYPE 0x8015 +#define GLX_MAX_PBUFFER_WIDTH 0x8016 +#define GLX_MAX_PBUFFER_HEIGHT 0x8017 +#define GLX_MAX_PBUFFER_PIXELS 0x8018 +#define GLX_PRESERVED_CONTENTS 0x801B +#define GLX_LARGEST_PBUFFER 0x801C +#define GLX_WIDTH 0x801D +#define GLX_HEIGHT 0x801E +#define GLX_EVENT_MASK 0x801F +#define GLX_DAMAGED 0x8020 +#define GLX_SAVED 0x8021 +#define GLX_WINDOW 0x8022 +#define GLX_PBUFFER 0x8023 +#define GLX_PBUFFER_HEIGHT 0x8040 +#define GLX_PBUFFER_WIDTH 0x8041 +typedef GLXFBConfig * (APIENTRYP PFNGLXGETFBCONFIGSPROC)(glbind_Display* dpy, int screen, int * nelements); +typedef GLXFBConfig * (APIENTRYP PFNGLXCHOOSEFBCONFIGPROC)(glbind_Display* dpy, int screen, const int * attrib_list, int * nelements); +typedef int (APIENTRYP PFNGLXGETFBCONFIGATTRIBPROC)(glbind_Display* dpy, GLXFBConfig config, int attribute, int * value); +typedef glbind_XVisualInfo* (APIENTRYP PFNGLXGETVISUALFROMFBCONFIGPROC)(glbind_Display* dpy, GLXFBConfig config); +typedef GLXWindow (APIENTRYP PFNGLXCREATEWINDOWPROC)(glbind_Display* dpy, GLXFBConfig config, glbind_Window win, const int * attrib_list); +typedef void (APIENTRYP PFNGLXDESTROYWINDOWPROC)(glbind_Display* dpy, GLXWindow win); +typedef GLXPixmap (APIENTRYP PFNGLXCREATEPIXMAPPROC)(glbind_Display* dpy, GLXFBConfig config, glbind_Pixmap pixmap, const int * attrib_list); +typedef void (APIENTRYP PFNGLXDESTROYPIXMAPPROC)(glbind_Display* dpy, GLXPixmap pixmap); +typedef GLXPbuffer (APIENTRYP PFNGLXCREATEPBUFFERPROC)(glbind_Display* dpy, GLXFBConfig config, const int * attrib_list); +typedef void (APIENTRYP PFNGLXDESTROYPBUFFERPROC)(glbind_Display* dpy, GLXPbuffer pbuf); +typedef void (APIENTRYP PFNGLXQUERYDRAWABLEPROC)(glbind_Display* dpy, GLXDrawable draw, int attribute, unsigned int * value); +typedef GLXContext (APIENTRYP PFNGLXCREATENEWCONTEXTPROC)(glbind_Display* dpy, GLXFBConfig config, int render_type, GLXContext share_list, glbind_Bool direct); +typedef glbind_Bool (APIENTRYP PFNGLXMAKECONTEXTCURRENTPROC)(glbind_Display* dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx); +typedef GLXDrawable (APIENTRYP PFNGLXGETCURRENTREADDRAWABLEPROC)(void); +typedef int (APIENTRYP PFNGLXQUERYCONTEXTPROC)(glbind_Display* dpy, GLXContext ctx, int attribute, int * value); +typedef void (APIENTRYP PFNGLXSELECTEVENTPROC)(glbind_Display* dpy, GLXDrawable draw, unsigned long event_mask); +typedef void (APIENTRYP PFNGLXGETSELECTEDEVENTPROC)(glbind_Display* dpy, GLXDrawable draw, unsigned long * event_mask); +#endif /* GLX_VERSION_1_3 */ + +#ifndef GLX_VERSION_1_4 +#define GLX_VERSION_1_4 1 +typedef void (APIENTRY *__GLXextFuncPtr)(void); +#define GLX_SAMPLE_BUFFERS 100000 +#define GLX_SAMPLES 100001 +typedef __GLXextFuncPtr (APIENTRYP PFNGLXGETPROCADDRESSPROC)(const GLubyte * procName); +#endif /* GLX_VERSION_1_4 */ +#endif /* GLBIND_GLX */ +#ifndef GL_3DFX_multisample +#define GL_3DFX_multisample 1 +#define GL_MULTISAMPLE_3DFX 0x86B2 +#define GL_SAMPLE_BUFFERS_3DFX 0x86B3 +#define GL_SAMPLES_3DFX 0x86B4 +#define GL_MULTISAMPLE_BIT_3DFX 0x20000000 +#endif /* GL_3DFX_multisample */ + +#ifndef GL_3DFX_tbuffer +#define GL_3DFX_tbuffer 1 +typedef void (APIENTRYP PFNGLTBUFFERMASK3DFXPROC)(GLuint mask); +#endif /* GL_3DFX_tbuffer */ + +#ifndef GL_3DFX_texture_compression_FXT1 +#define GL_3DFX_texture_compression_FXT1 1 +#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 +#define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1 +#endif /* GL_3DFX_texture_compression_FXT1 */ + +#ifndef GL_AMD_blend_minmax_factor +#define GL_AMD_blend_minmax_factor 1 +#define GL_FACTOR_MIN_AMD 0x901C +#define GL_FACTOR_MAX_AMD 0x901D +#endif /* GL_AMD_blend_minmax_factor */ + +#ifndef GL_AMD_conservative_depth +#define GL_AMD_conservative_depth 1 +#endif /* GL_AMD_conservative_depth */ + +#ifndef GL_AMD_debug_output +#define GL_AMD_debug_output 1 +typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); +#define GL_MAX_DEBUG_MESSAGE_LENGTH_AMD 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_AMD 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_AMD 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_AMD 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_AMD 0x9147 +#define GL_DEBUG_SEVERITY_LOW_AMD 0x9148 +#define GL_DEBUG_CATEGORY_API_ERROR_AMD 0x9149 +#define GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD 0x914A +#define GL_DEBUG_CATEGORY_DEPRECATION_AMD 0x914B +#define GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD 0x914C +#define GL_DEBUG_CATEGORY_PERFORMANCE_AMD 0x914D +#define GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD 0x914E +#define GL_DEBUG_CATEGORY_APPLICATION_AMD 0x914F +#define GL_DEBUG_CATEGORY_OTHER_AMD 0x9150 +typedef void (APIENTRYP PFNGLDEBUGMESSAGEENABLEAMDPROC)(GLenum category, GLenum severity, GLsizei count, const GLuint * ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTAMDPROC)(GLenum category, GLenum severity, GLuint id, GLsizei length, const GLchar * buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKAMDPROC)(GLDEBUGPROCAMD callback, void * userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGAMDPROC)(GLuint count, GLsizei bufSize, GLenum * categories, GLuint * severities, GLuint * ids, GLsizei * lengths, GLchar * message); +#endif /* GL_AMD_debug_output */ + +#ifndef GL_AMD_depth_clamp_separate +#define GL_AMD_depth_clamp_separate 1 +#define GL_DEPTH_CLAMP_NEAR_AMD 0x901E +#define GL_DEPTH_CLAMP_FAR_AMD 0x901F +#endif /* GL_AMD_depth_clamp_separate */ + +#ifndef GL_AMD_draw_buffers_blend +#define GL_AMD_draw_buffers_blend 1 +typedef void (APIENTRYP PFNGLBLENDFUNCINDEXEDAMDPROC)(GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINDEXEDAMDPROC)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (APIENTRYP PFNGLBLENDEQUATIONINDEXEDAMDPROC)(GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEINDEXEDAMDPROC)(GLuint buf, GLenum modeRGB, GLenum modeAlpha); +#endif /* GL_AMD_draw_buffers_blend */ + +#ifndef GL_AMD_framebuffer_multisample_advanced +#define GL_AMD_framebuffer_multisample_advanced 1 +#define GL_RENDERBUFFER_STORAGE_SAMPLES_AMD 0x91B2 +#define GL_MAX_COLOR_FRAMEBUFFER_SAMPLES_AMD 0x91B3 +#define GL_MAX_COLOR_FRAMEBUFFER_STORAGE_SAMPLES_AMD 0x91B4 +#define GL_MAX_DEPTH_STENCIL_FRAMEBUFFER_SAMPLES_AMD 0x91B5 +#define GL_NUM_SUPPORTED_MULTISAMPLE_MODES_AMD 0x91B6 +#define GL_SUPPORTED_MULTISAMPLE_MODES_AMD 0x91B7 +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC)(GLenum target, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC)(GLuint renderbuffer, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); +#endif /* GL_AMD_framebuffer_multisample_advanced */ + +#ifndef GL_AMD_framebuffer_sample_positions +#define GL_AMD_framebuffer_sample_positions 1 +#define GL_SUBSAMPLE_DISTANCE_AMD 0x883F +#define GL_PIXELS_PER_SAMPLE_PATTERN_X_AMD 0x91AE +#define GL_PIXELS_PER_SAMPLE_PATTERN_Y_AMD 0x91AF +#define GL_ALL_PIXELS_AMD 0xFFFFFFFF +typedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLEPOSITIONSFVAMDPROC)(GLenum target, GLuint numsamples, GLuint pixelindex, const GLfloat * values); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLEPOSITIONSFVAMDPROC)(GLuint framebuffer, GLuint numsamples, GLuint pixelindex, const GLfloat * values); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERFVAMDPROC)(GLenum target, GLenum pname, GLuint numsamples, GLuint pixelindex, GLsizei size, GLfloat * values); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERFVAMDPROC)(GLuint framebuffer, GLenum pname, GLuint numsamples, GLuint pixelindex, GLsizei size, GLfloat * values); +#endif /* GL_AMD_framebuffer_sample_positions */ + +#ifndef GL_AMD_gcn_shader +#define GL_AMD_gcn_shader 1 +#endif /* GL_AMD_gcn_shader */ + +#ifndef GL_AMD_gpu_shader_half_float +#define GL_AMD_gpu_shader_half_float 1 +#define GL_FLOAT16_NV 0x8FF8 +#define GL_FLOAT16_VEC2_NV 0x8FF9 +#define GL_FLOAT16_VEC3_NV 0x8FFA +#define GL_FLOAT16_VEC4_NV 0x8FFB +#define GL_FLOAT16_MAT2_AMD 0x91C5 +#define GL_FLOAT16_MAT3_AMD 0x91C6 +#define GL_FLOAT16_MAT4_AMD 0x91C7 +#define GL_FLOAT16_MAT2x3_AMD 0x91C8 +#define GL_FLOAT16_MAT2x4_AMD 0x91C9 +#define GL_FLOAT16_MAT3x2_AMD 0x91CA +#define GL_FLOAT16_MAT3x4_AMD 0x91CB +#define GL_FLOAT16_MAT4x2_AMD 0x91CC +#define GL_FLOAT16_MAT4x3_AMD 0x91CD +#endif /* GL_AMD_gpu_shader_half_float */ + +#ifndef GL_AMD_gpu_shader_int16 +#define GL_AMD_gpu_shader_int16 1 +#endif /* GL_AMD_gpu_shader_int16 */ + +#ifndef GL_AMD_gpu_shader_int64 +#define GL_AMD_gpu_shader_int64 1 +typedef khronos_int64_t GLint64EXT; +typedef khronos_uint64_t GLuint64EXT; +#define GL_INT64_NV 0x140E +#define GL_UNSIGNED_INT64_NV 0x140F +#define GL_INT8_NV 0x8FE0 +#define GL_INT8_VEC2_NV 0x8FE1 +#define GL_INT8_VEC3_NV 0x8FE2 +#define GL_INT8_VEC4_NV 0x8FE3 +#define GL_INT16_NV 0x8FE4 +#define GL_INT16_VEC2_NV 0x8FE5 +#define GL_INT16_VEC3_NV 0x8FE6 +#define GL_INT16_VEC4_NV 0x8FE7 +#define GL_INT64_VEC2_NV 0x8FE9 +#define GL_INT64_VEC3_NV 0x8FEA +#define GL_INT64_VEC4_NV 0x8FEB +#define GL_UNSIGNED_INT8_NV 0x8FEC +#define GL_UNSIGNED_INT8_VEC2_NV 0x8FED +#define GL_UNSIGNED_INT8_VEC3_NV 0x8FEE +#define GL_UNSIGNED_INT8_VEC4_NV 0x8FEF +#define GL_UNSIGNED_INT16_NV 0x8FF0 +#define GL_UNSIGNED_INT16_VEC2_NV 0x8FF1 +#define GL_UNSIGNED_INT16_VEC3_NV 0x8FF2 +#define GL_UNSIGNED_INT16_VEC4_NV 0x8FF3 +#define GL_UNSIGNED_INT64_VEC2_NV 0x8FF5 +#define GL_UNSIGNED_INT64_VEC3_NV 0x8FF6 +#define GL_UNSIGNED_INT64_VEC4_NV 0x8FF7 +#define GL_FLOAT16_NV 0x8FF8 +#define GL_FLOAT16_VEC2_NV 0x8FF9 +#define GL_FLOAT16_VEC3_NV 0x8FFA +#define GL_FLOAT16_VEC4_NV 0x8FFB +typedef void (APIENTRYP PFNGLUNIFORM1I64NVPROC)(GLint location, GLint64EXT x); +typedef void (APIENTRYP PFNGLUNIFORM2I64NVPROC)(GLint location, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLUNIFORM3I64NVPROC)(GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLUNIFORM4I64NVPROC)(GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLUNIFORM1I64VNVPROC)(GLint location, GLsizei count, const GLint64EXT * value); +typedef void (APIENTRYP PFNGLUNIFORM2I64VNVPROC)(GLint location, GLsizei count, const GLint64EXT * value); +typedef void (APIENTRYP PFNGLUNIFORM3I64VNVPROC)(GLint location, GLsizei count, const GLint64EXT * value); +typedef void (APIENTRYP PFNGLUNIFORM4I64VNVPROC)(GLint location, GLsizei count, const GLint64EXT * value); +typedef void (APIENTRYP PFNGLUNIFORM1UI64NVPROC)(GLint location, GLuint64EXT x); +typedef void (APIENTRYP PFNGLUNIFORM2UI64NVPROC)(GLint location, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLUNIFORM3UI64NVPROC)(GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLUNIFORM4UI64NVPROC)(GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLUNIFORM1UI64VNVPROC)(GLint location, GLsizei count, const GLuint64EXT * value); +typedef void (APIENTRYP PFNGLUNIFORM2UI64VNVPROC)(GLint location, GLsizei count, const GLuint64EXT * value); +typedef void (APIENTRYP PFNGLUNIFORM3UI64VNVPROC)(GLint location, GLsizei count, const GLuint64EXT * value); +typedef void (APIENTRYP PFNGLUNIFORM4UI64VNVPROC)(GLint location, GLsizei count, const GLuint64EXT * value); +typedef void (APIENTRYP PFNGLGETUNIFORMI64VNVPROC)(GLuint program, GLint location, GLint64EXT * params); +typedef void (APIENTRYP PFNGLGETUNIFORMUI64VNVPROC)(GLuint program, GLint location, GLuint64EXT * params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64NVPROC)(GLuint program, GLint location, GLint64EXT x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64NVPROC)(GLuint program, GLint location, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64NVPROC)(GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64NVPROC)(GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VNVPROC)(GLuint program, GLint location, GLsizei count, const GLint64EXT * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VNVPROC)(GLuint program, GLint location, GLsizei count, const GLint64EXT * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VNVPROC)(GLuint program, GLint location, GLsizei count, const GLint64EXT * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VNVPROC)(GLuint program, GLint location, GLsizei count, const GLint64EXT * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64NVPROC)(GLuint program, GLint location, GLuint64EXT x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64NVPROC)(GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64NVPROC)(GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64NVPROC)(GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VNVPROC)(GLuint program, GLint location, GLsizei count, const GLuint64EXT * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VNVPROC)(GLuint program, GLint location, GLsizei count, const GLuint64EXT * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VNVPROC)(GLuint program, GLint location, GLsizei count, const GLuint64EXT * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VNVPROC)(GLuint program, GLint location, GLsizei count, const GLuint64EXT * value); +#endif /* GL_AMD_gpu_shader_int64 */ + +#ifndef GL_AMD_interleaved_elements +#define GL_AMD_interleaved_elements 1 +#define GL_VERTEX_ELEMENT_SWIZZLE_AMD 0x91A4 +#define GL_VERTEX_ID_SWIZZLE_AMD 0x91A5 +#define GL_RED 0x1903 +#define GL_GREEN 0x1904 +#define GL_BLUE 0x1905 +#define GL_ALPHA 0x1906 +#define GL_RG8UI 0x8238 +#define GL_RG16UI 0x823A +#define GL_RGBA8UI 0x8D7C +typedef void (APIENTRYP PFNGLVERTEXATTRIBPARAMETERIAMDPROC)(GLuint index, GLenum pname, GLint param); +#endif /* GL_AMD_interleaved_elements */ + +#ifndef GL_AMD_multi_draw_indirect +#define GL_AMD_multi_draw_indirect 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTAMDPROC)(GLenum mode, const void * indirect, GLsizei primcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTAMDPROC)(GLenum mode, GLenum type, const void * indirect, GLsizei primcount, GLsizei stride); +#endif /* GL_AMD_multi_draw_indirect */ + +#ifndef GL_AMD_name_gen_delete +#define GL_AMD_name_gen_delete 1 +#define GL_DATA_BUFFER_AMD 0x9151 +#define GL_PERFORMANCE_MONITOR_AMD 0x9152 +#define GL_QUERY_OBJECT_AMD 0x9153 +#define GL_VERTEX_ARRAY_OBJECT_AMD 0x9154 +#define GL_SAMPLER_OBJECT_AMD 0x9155 +typedef void (APIENTRYP PFNGLGENNAMESAMDPROC)(GLenum identifier, GLuint num, GLuint * names); +typedef void (APIENTRYP PFNGLDELETENAMESAMDPROC)(GLenum identifier, GLuint num, const GLuint * names); +typedef GLboolean (APIENTRYP PFNGLISNAMEAMDPROC)(GLenum identifier, GLuint name); +#endif /* GL_AMD_name_gen_delete */ + +#ifndef GL_AMD_occlusion_query_event +#define GL_AMD_occlusion_query_event 1 +#define GL_OCCLUSION_QUERY_EVENT_MASK_AMD 0x874F +#define GL_QUERY_DEPTH_PASS_EVENT_BIT_AMD 0x00000001 +#define GL_QUERY_DEPTH_FAIL_EVENT_BIT_AMD 0x00000002 +#define GL_QUERY_STENCIL_FAIL_EVENT_BIT_AMD 0x00000004 +#define GL_QUERY_DEPTH_BOUNDS_FAIL_EVENT_BIT_AMD 0x00000008 +#define GL_QUERY_ALL_EVENT_BITS_AMD 0xFFFFFFFF +typedef void (APIENTRYP PFNGLQUERYOBJECTPARAMETERUIAMDPROC)(GLenum target, GLuint id, GLenum pname, GLuint param); +#endif /* GL_AMD_occlusion_query_event */ + +#ifndef GL_AMD_performance_monitor +#define GL_AMD_performance_monitor 1 +#define GL_COUNTER_TYPE_AMD 0x8BC0 +#define GL_COUNTER_RANGE_AMD 0x8BC1 +#define GL_UNSIGNED_INT64_AMD 0x8BC2 +#define GL_PERCENTAGE_AMD 0x8BC3 +#define GL_PERFMON_RESULT_AVAILABLE_AMD 0x8BC4 +#define GL_PERFMON_RESULT_SIZE_AMD 0x8BC5 +#define GL_PERFMON_RESULT_AMD 0x8BC6 +typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSAMDPROC)(GLint * numGroups, GLsizei groupsSize, GLuint * groups); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSAMDPROC)(GLuint group, GLint * numCounters, GLint * maxActiveCounters, GLsizei counterSize, GLuint * counters); +typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC)(GLuint group, GLsizei bufSize, GLsizei * length, GLchar * groupString); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC)(GLuint group, GLuint counter, GLsizei bufSize, GLsizei * length, GLchar * counterString); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC)(GLuint group, GLuint counter, GLenum pname, void * data); +typedef void (APIENTRYP PFNGLGENPERFMONITORSAMDPROC)(GLsizei n, GLuint * monitors); +typedef void (APIENTRYP PFNGLDELETEPERFMONITORSAMDPROC)(GLsizei n, GLuint * monitors); +typedef void (APIENTRYP PFNGLSELECTPERFMONITORCOUNTERSAMDPROC)(GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint * counterList); +typedef void (APIENTRYP PFNGLBEGINPERFMONITORAMDPROC)(GLuint monitor); +typedef void (APIENTRYP PFNGLENDPERFMONITORAMDPROC)(GLuint monitor); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERDATAAMDPROC)(GLuint monitor, GLenum pname, GLsizei dataSize, GLuint * data, GLint * bytesWritten); +#endif /* GL_AMD_performance_monitor */ + +#ifndef GL_AMD_pinned_memory +#define GL_AMD_pinned_memory 1 +#define GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD 0x9160 +#endif /* GL_AMD_pinned_memory */ + +#ifndef GL_AMD_query_buffer_object +#define GL_AMD_query_buffer_object 1 +#define GL_QUERY_BUFFER_AMD 0x9192 +#define GL_QUERY_BUFFER_BINDING_AMD 0x9193 +#define GL_QUERY_RESULT_NO_WAIT_AMD 0x9194 +#endif /* GL_AMD_query_buffer_object */ + +#ifndef GL_AMD_sample_positions +#define GL_AMD_sample_positions 1 +#define GL_SUBSAMPLE_DISTANCE_AMD 0x883F +typedef void (APIENTRYP PFNGLSETMULTISAMPLEFVAMDPROC)(GLenum pname, GLuint index, const GLfloat * val); +#endif /* GL_AMD_sample_positions */ + +#ifndef GL_AMD_seamless_cubemap_per_texture +#define GL_AMD_seamless_cubemap_per_texture 1 +#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#endif /* GL_AMD_seamless_cubemap_per_texture */ + +#ifndef GL_AMD_shader_atomic_counter_ops +#define GL_AMD_shader_atomic_counter_ops 1 +#endif /* GL_AMD_shader_atomic_counter_ops */ + +#ifndef GL_AMD_shader_ballot +#define GL_AMD_shader_ballot 1 +#endif /* GL_AMD_shader_ballot */ + +#ifndef GL_AMD_shader_gpu_shader_half_float_fetch +#define GL_AMD_shader_gpu_shader_half_float_fetch 1 +#endif /* GL_AMD_shader_gpu_shader_half_float_fetch */ + +#ifndef GL_AMD_shader_image_load_store_lod +#define GL_AMD_shader_image_load_store_lod 1 +#endif /* GL_AMD_shader_image_load_store_lod */ + +#ifndef GL_AMD_shader_stencil_export +#define GL_AMD_shader_stencil_export 1 +#endif /* GL_AMD_shader_stencil_export */ + +#ifndef GL_AMD_shader_trinary_minmax +#define GL_AMD_shader_trinary_minmax 1 +#endif /* GL_AMD_shader_trinary_minmax */ + +#ifndef GL_AMD_shader_explicit_vertex_parameter +#define GL_AMD_shader_explicit_vertex_parameter 1 +#endif /* GL_AMD_shader_explicit_vertex_parameter */ + +#ifndef GL_AMD_sparse_texture +#define GL_AMD_sparse_texture 1 +#define GL_VIRTUAL_PAGE_SIZE_X_AMD 0x9195 +#define GL_VIRTUAL_PAGE_SIZE_Y_AMD 0x9196 +#define GL_VIRTUAL_PAGE_SIZE_Z_AMD 0x9197 +#define GL_MAX_SPARSE_TEXTURE_SIZE_AMD 0x9198 +#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_AMD 0x9199 +#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS 0x919A +#define GL_MIN_SPARSE_LEVEL_AMD 0x919B +#define GL_MIN_LOD_WARNING_AMD 0x919C +#define GL_TEXTURE_STORAGE_SPARSE_BIT_AMD 0x00000001 +typedef void (APIENTRYP PFNGLTEXSTORAGESPARSEAMDPROC)(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +typedef void (APIENTRYP PFNGLTEXTURESTORAGESPARSEAMDPROC)(GLuint texture, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +#endif /* GL_AMD_sparse_texture */ + +#ifndef GL_AMD_stencil_operation_extended +#define GL_AMD_stencil_operation_extended 1 +#define GL_SET_AMD 0x874A +#define GL_REPLACE_VALUE_AMD 0x874B +#define GL_STENCIL_OP_VALUE_AMD 0x874C +#define GL_STENCIL_BACK_OP_VALUE_AMD 0x874D +typedef void (APIENTRYP PFNGLSTENCILOPVALUEAMDPROC)(GLenum face, GLuint value); +#endif /* GL_AMD_stencil_operation_extended */ + +#ifndef GL_AMD_texture_gather_bias_lod +#define GL_AMD_texture_gather_bias_lod 1 +#endif /* GL_AMD_texture_gather_bias_lod */ + +#ifndef GL_AMD_texture_texture4 +#define GL_AMD_texture_texture4 1 +#endif /* GL_AMD_texture_texture4 */ + +#ifndef GL_AMD_transform_feedback3_lines_triangles +#define GL_AMD_transform_feedback3_lines_triangles 1 +#endif /* GL_AMD_transform_feedback3_lines_triangles */ + +#ifndef GL_AMD_transform_feedback4 +#define GL_AMD_transform_feedback4 1 +#define GL_STREAM_RASTERIZATION_AMD 0x91A0 +#endif /* GL_AMD_transform_feedback4 */ + +#ifndef GL_AMD_vertex_shader_layer +#define GL_AMD_vertex_shader_layer 1 +#endif /* GL_AMD_vertex_shader_layer */ + +#ifndef GL_AMD_vertex_shader_tessellator +#define GL_AMD_vertex_shader_tessellator 1 +#define GL_SAMPLER_BUFFER_AMD 0x9001 +#define GL_INT_SAMPLER_BUFFER_AMD 0x9002 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_AMD 0x9003 +#define GL_TESSELLATION_MODE_AMD 0x9004 +#define GL_TESSELLATION_FACTOR_AMD 0x9005 +#define GL_DISCRETE_AMD 0x9006 +#define GL_CONTINUOUS_AMD 0x9007 +typedef void (APIENTRYP PFNGLTESSELLATIONFACTORAMDPROC)(GLfloat factor); +typedef void (APIENTRYP PFNGLTESSELLATIONMODEAMDPROC)(GLenum mode); +#endif /* GL_AMD_vertex_shader_tessellator */ + +#ifndef GL_AMD_vertex_shader_viewport_index +#define GL_AMD_vertex_shader_viewport_index 1 +#endif /* GL_AMD_vertex_shader_viewport_index */ + +#ifndef GL_APPLE_aux_depth_stencil +#define GL_APPLE_aux_depth_stencil 1 +#define GL_AUX_DEPTH_STENCIL_APPLE 0x8A14 +#endif /* GL_APPLE_aux_depth_stencil */ + +#ifndef GL_APPLE_client_storage +#define GL_APPLE_client_storage 1 +#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2 +#endif /* GL_APPLE_client_storage */ + +#ifndef GL_APPLE_element_array +#define GL_APPLE_element_array 1 +#define GL_ELEMENT_ARRAY_APPLE 0x8A0C +#define GL_ELEMENT_ARRAY_TYPE_APPLE 0x8A0D +#define GL_ELEMENT_ARRAY_POINTER_APPLE 0x8A0E +typedef void (APIENTRYP PFNGLELEMENTPOINTERAPPLEPROC)(GLenum type, const void * pointer); +typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYAPPLEPROC)(GLenum mode, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYAPPLEPROC)(GLenum mode, GLuint start, GLuint end, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTARRAYAPPLEPROC)(GLenum mode, const GLint * first, const GLsizei * count, GLsizei primcount); +typedef void (APIENTRYP PFNGLMULTIDRAWRANGEELEMENTARRAYAPPLEPROC)(GLenum mode, GLuint start, GLuint end, const GLint * first, const GLsizei * count, GLsizei primcount); +#endif /* GL_APPLE_element_array */ + +#ifndef GL_APPLE_fence +#define GL_APPLE_fence 1 +#define GL_DRAW_PIXELS_APPLE 0x8A0A +#define GL_FENCE_APPLE 0x8A0B +typedef void (APIENTRYP PFNGLGENFENCESAPPLEPROC)(GLsizei n, GLuint * fences); +typedef void (APIENTRYP PFNGLDELETEFENCESAPPLEPROC)(GLsizei n, const GLuint * fences); +typedef void (APIENTRYP PFNGLSETFENCEAPPLEPROC)(GLuint fence); +typedef GLboolean (APIENTRYP PFNGLISFENCEAPPLEPROC)(GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTFENCEAPPLEPROC)(GLuint fence); +typedef void (APIENTRYP PFNGLFINISHFENCEAPPLEPROC)(GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTOBJECTAPPLEPROC)(GLenum object, GLuint name); +typedef void (APIENTRYP PFNGLFINISHOBJECTAPPLEPROC)(GLenum object, GLint name); +#endif /* GL_APPLE_fence */ + +#ifndef GL_APPLE_float_pixels +#define GL_APPLE_float_pixels 1 +#define GL_HALF_APPLE 0x140B +#define GL_RGBA_FLOAT32_APPLE 0x8814 +#define GL_RGB_FLOAT32_APPLE 0x8815 +#define GL_ALPHA_FLOAT32_APPLE 0x8816 +#define GL_INTENSITY_FLOAT32_APPLE 0x8817 +#define GL_LUMINANCE_FLOAT32_APPLE 0x8818 +#define GL_LUMINANCE_ALPHA_FLOAT32_APPLE 0x8819 +#define GL_RGBA_FLOAT16_APPLE 0x881A +#define GL_RGB_FLOAT16_APPLE 0x881B +#define GL_ALPHA_FLOAT16_APPLE 0x881C +#define GL_INTENSITY_FLOAT16_APPLE 0x881D +#define GL_LUMINANCE_FLOAT16_APPLE 0x881E +#define GL_LUMINANCE_ALPHA_FLOAT16_APPLE 0x881F +#define GL_COLOR_FLOAT_APPLE 0x8A0F +#endif /* GL_APPLE_float_pixels */ + +#ifndef GL_APPLE_flush_buffer_range +#define GL_APPLE_flush_buffer_range 1 +#define GL_BUFFER_SERIALIZED_MODIFY_APPLE 0x8A12 +#define GL_BUFFER_FLUSHING_UNMAP_APPLE 0x8A13 +typedef void (APIENTRYP PFNGLBUFFERPARAMETERIAPPLEPROC)(GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC)(GLenum target, GLintptr offset, GLsizeiptr size); +#endif /* GL_APPLE_flush_buffer_range */ + +#ifndef GL_APPLE_object_purgeable +#define GL_APPLE_object_purgeable 1 +#define GL_BUFFER_OBJECT_APPLE 0x85B3 +#define GL_RELEASED_APPLE 0x8A19 +#define GL_VOLATILE_APPLE 0x8A1A +#define GL_RETAINED_APPLE 0x8A1B +#define GL_UNDEFINED_APPLE 0x8A1C +#define GL_PURGEABLE_APPLE 0x8A1D +typedef GLenum (APIENTRYP PFNGLOBJECTPURGEABLEAPPLEPROC)(GLenum objectType, GLuint name, GLenum option); +typedef GLenum (APIENTRYP PFNGLOBJECTUNPURGEABLEAPPLEPROC)(GLenum objectType, GLuint name, GLenum option); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVAPPLEPROC)(GLenum objectType, GLuint name, GLenum pname, GLint * params); +#endif /* GL_APPLE_object_purgeable */ + +#ifndef GL_APPLE_rgb_422 +#define GL_APPLE_rgb_422 1 +#define GL_RGB_422_APPLE 0x8A1F +#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB +#define GL_RGB_RAW_422_APPLE 0x8A51 +#endif /* GL_APPLE_rgb_422 */ + +#ifndef GL_APPLE_row_bytes +#define GL_APPLE_row_bytes 1 +#define GL_PACK_ROW_BYTES_APPLE 0x8A15 +#define GL_UNPACK_ROW_BYTES_APPLE 0x8A16 +#endif /* GL_APPLE_row_bytes */ + +#ifndef GL_APPLE_specular_vector +#define GL_APPLE_specular_vector 1 +#define GL_LIGHT_MODEL_SPECULAR_VECTOR_APPLE 0x85B0 +#endif /* GL_APPLE_specular_vector */ + +#ifndef GL_APPLE_texture_range +#define GL_APPLE_texture_range 1 +#define GL_TEXTURE_RANGE_LENGTH_APPLE 0x85B7 +#define GL_TEXTURE_RANGE_POINTER_APPLE 0x85B8 +#define GL_TEXTURE_STORAGE_HINT_APPLE 0x85BC +#define GL_STORAGE_PRIVATE_APPLE 0x85BD +#define GL_STORAGE_CACHED_APPLE 0x85BE +#define GL_STORAGE_SHARED_APPLE 0x85BF +typedef void (APIENTRYP PFNGLTEXTURERANGEAPPLEPROC)(GLenum target, GLsizei length, const void * pointer); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERPOINTERVAPPLEPROC)(GLenum target, GLenum pname, void ** params); +#endif /* GL_APPLE_texture_range */ + +#ifndef GL_APPLE_transform_hint +#define GL_APPLE_transform_hint 1 +#define GL_TRANSFORM_HINT_APPLE 0x85B1 +#endif /* GL_APPLE_transform_hint */ + +#ifndef GL_APPLE_vertex_array_object +#define GL_APPLE_vertex_array_object 1 +#define GL_VERTEX_ARRAY_BINDING_APPLE 0x85B5 +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYAPPLEPROC)(GLuint array); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSAPPLEPROC)(GLsizei n, const GLuint * arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSAPPLEPROC)(GLsizei n, GLuint * arrays); +typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYAPPLEPROC)(GLuint array); +#endif /* GL_APPLE_vertex_array_object */ + +#ifndef GL_APPLE_vertex_array_range +#define GL_APPLE_vertex_array_range 1 +#define GL_VERTEX_ARRAY_RANGE_APPLE 0x851D +#define GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE 0x851E +#define GL_VERTEX_ARRAY_STORAGE_HINT_APPLE 0x851F +#define GL_VERTEX_ARRAY_RANGE_POINTER_APPLE 0x8521 +#define GL_STORAGE_CLIENT_APPLE 0x85B4 +#define GL_STORAGE_CACHED_APPLE 0x85BE +#define GL_STORAGE_SHARED_APPLE 0x85BF +typedef void (APIENTRYP PFNGLVERTEXARRAYRANGEAPPLEPROC)(GLsizei length, void * pointer); +typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGEAPPLEPROC)(GLsizei length, void * pointer); +typedef void (APIENTRYP PFNGLVERTEXARRAYPARAMETERIAPPLEPROC)(GLenum pname, GLint param); +#endif /* GL_APPLE_vertex_array_range */ + +#ifndef GL_APPLE_vertex_program_evaluators +#define GL_APPLE_vertex_program_evaluators 1 +#define GL_VERTEX_ATTRIB_MAP1_APPLE 0x8A00 +#define GL_VERTEX_ATTRIB_MAP2_APPLE 0x8A01 +#define GL_VERTEX_ATTRIB_MAP1_SIZE_APPLE 0x8A02 +#define GL_VERTEX_ATTRIB_MAP1_COEFF_APPLE 0x8A03 +#define GL_VERTEX_ATTRIB_MAP1_ORDER_APPLE 0x8A04 +#define GL_VERTEX_ATTRIB_MAP1_DOMAIN_APPLE 0x8A05 +#define GL_VERTEX_ATTRIB_MAP2_SIZE_APPLE 0x8A06 +#define GL_VERTEX_ATTRIB_MAP2_COEFF_APPLE 0x8A07 +#define GL_VERTEX_ATTRIB_MAP2_ORDER_APPLE 0x8A08 +#define GL_VERTEX_ATTRIB_MAP2_DOMAIN_APPLE 0x8A09 +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBAPPLEPROC)(GLuint index, GLenum pname); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBAPPLEPROC)(GLuint index, GLenum pname); +typedef GLboolean (APIENTRYP PFNGLISVERTEXATTRIBENABLEDAPPLEPROC)(GLuint index, GLenum pname); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB1DAPPLEPROC)(GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble * points); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB1FAPPLEPROC)(GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat * points); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB2DAPPLEPROC)(GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble * points); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB2FAPPLEPROC)(GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat * points); +#endif /* GL_APPLE_vertex_program_evaluators */ + +#ifndef GL_APPLE_ycbcr_422 +#define GL_APPLE_ycbcr_422 1 +#define GL_YCBCR_422_APPLE 0x85B9 +#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB +#endif /* GL_APPLE_ycbcr_422 */ + +#ifndef GL_ARB_ES2_compatibility +#define GL_ARB_ES2_compatibility 1 +#define GL_FIXED 0x140C +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_LOW_FLOAT 0x8DF0 +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_LOW_INT 0x8DF3 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_HIGH_INT 0x8DF5 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_RGB565 0x8D62 +#endif /* GL_ARB_ES2_compatibility */ + +#ifndef GL_ARB_ES3_1_compatibility +#define GL_ARB_ES3_1_compatibility 1 +#define GL_BACK 0x0405 +#endif /* GL_ARB_ES3_1_compatibility */ + +#ifndef GL_ARB_ES3_2_compatibility +#define GL_ARB_ES3_2_compatibility 1 +#define GL_PRIMITIVE_BOUNDING_BOX_ARB 0x92BE +#define GL_MULTISAMPLE_LINE_WIDTH_RANGE_ARB 0x9381 +#define GL_MULTISAMPLE_LINE_WIDTH_GRANULARITY_ARB 0x9382 +typedef void (APIENTRYP PFNGLPRIMITIVEBOUNDINGBOXARBPROC)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW); +#endif /* GL_ARB_ES3_2_compatibility */ + +#ifndef GL_ARB_ES3_compatibility +#define GL_ARB_ES3_compatibility 1 +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define GL_COMPRESSED_R11_EAC 0x9270 +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define GL_COMPRESSED_RG11_EAC 0x9272 +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 +#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A +#define GL_MAX_ELEMENT_INDEX 0x8D6B +#endif /* GL_ARB_ES3_compatibility */ + +#ifndef GL_ARB_arrays_of_arrays +#define GL_ARB_arrays_of_arrays 1 +#endif /* GL_ARB_arrays_of_arrays */ + +#ifndef GL_ARB_base_instance +#define GL_ARB_base_instance 1 +#endif /* GL_ARB_base_instance */ + +#ifndef GL_ARB_bindless_texture +#define GL_ARB_bindless_texture 1 +#define GL_UNSIGNED_INT64_ARB 0x140F +typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLEARBPROC)(GLuint texture); +typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLEARBPROC)(GLuint texture, GLuint sampler); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTARBPROC)(GLuint64 handle); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC)(GLuint64 handle); +typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLEARBPROC)(GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTARBPROC)(GLuint64 handle, GLenum access); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC)(GLuint64 handle); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64ARBPROC)(GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VARBPROC)(GLint location, GLsizei count, const GLuint64 * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC)(GLuint program, GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLuint64 * values); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTARBPROC)(GLuint64 handle); +typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTARBPROC)(GLuint64 handle); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64ARBPROC)(GLuint index, GLuint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VARBPROC)(GLuint index, const GLuint64EXT * v); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VARBPROC)(GLuint index, GLenum pname, GLuint64EXT * params); +#endif /* GL_ARB_bindless_texture */ + +#ifndef GL_ARB_blend_func_extended +#define GL_ARB_blend_func_extended 1 +#define GL_SRC1_COLOR 0x88F9 +#define GL_SRC1_ALPHA 0x8589 +#define GL_ONE_MINUS_SRC1_COLOR 0x88FA +#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC +#endif /* GL_ARB_blend_func_extended */ + +#ifndef GL_ARB_buffer_storage +#define GL_ARB_buffer_storage 1 +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_PERSISTENT_BIT 0x0040 +#define GL_MAP_COHERENT_BIT 0x0080 +#define GL_DYNAMIC_STORAGE_BIT 0x0100 +#define GL_CLIENT_STORAGE_BIT 0x0200 +#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000 +#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F +#define GL_BUFFER_STORAGE_FLAGS 0x8220 +#endif /* GL_ARB_buffer_storage */ + +#ifndef GL_ARB_cl_event +#define GL_ARB_cl_event 1 +struct _cl_context; +struct _cl_event; +#define GL_SYNC_CL_EVENT_ARB 0x8240 +#define GL_SYNC_CL_EVENT_COMPLETE_ARB 0x8241 +typedef GLsync (APIENTRYP PFNGLCREATESYNCFROMCLEVENTARBPROC)(struct _cl_context * context, struct _cl_event * event, GLbitfield flags); +#endif /* GL_ARB_cl_event */ + +#ifndef GL_ARB_clear_buffer_object +#define GL_ARB_clear_buffer_object 1 +#endif /* GL_ARB_clear_buffer_object */ + +#ifndef GL_ARB_clear_texture +#define GL_ARB_clear_texture 1 +#define GL_CLEAR_TEXTURE 0x9365 +#endif /* GL_ARB_clear_texture */ + +#ifndef GL_ARB_clip_control +#define GL_ARB_clip_control 1 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_NEGATIVE_ONE_TO_ONE 0x935E +#define GL_ZERO_TO_ONE 0x935F +#define GL_CLIP_ORIGIN 0x935C +#define GL_CLIP_DEPTH_MODE 0x935D +#endif /* GL_ARB_clip_control */ + +#ifndef GL_ARB_color_buffer_float +#define GL_ARB_color_buffer_float 1 +#define GL_RGBA_FLOAT_MODE_ARB 0x8820 +#define GL_CLAMP_VERTEX_COLOR_ARB 0x891A +#define GL_CLAMP_FRAGMENT_COLOR_ARB 0x891B +#define GL_CLAMP_READ_COLOR_ARB 0x891C +#define GL_FIXED_ONLY_ARB 0x891D +typedef void (APIENTRYP PFNGLCLAMPCOLORARBPROC)(GLenum target, GLenum clamp); +#endif /* GL_ARB_color_buffer_float */ + +#ifndef GL_ARB_compatibility +#define GL_ARB_compatibility 1 +#endif /* GL_ARB_compatibility */ + +#ifndef GL_ARB_compressed_texture_pixel_storage +#define GL_ARB_compressed_texture_pixel_storage 1 +#define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127 +#define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128 +#define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129 +#define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A +#define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B +#define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C +#define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D +#define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E +#endif /* GL_ARB_compressed_texture_pixel_storage */ + +#ifndef GL_ARB_compute_shader +#define GL_ARB_compute_shader 1 +#define GL_COMPUTE_SHADER 0x91B9 +#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB +#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC +#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD +#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262 +#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263 +#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264 +#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265 +#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266 +#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB +#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE +#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF +#define GL_COMPUTE_WORK_GROUP_SIZE 0x8267 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED +#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE +#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF +#define GL_COMPUTE_SHADER_BIT 0x00000020 +#endif /* GL_ARB_compute_shader */ + +#ifndef GL_ARB_compute_variable_group_size +#define GL_ARB_compute_variable_group_size 1 +#define GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB 0x9344 +#define GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB 0x90EB +#define GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB 0x9345 +#define GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB 0x91BF +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC)(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); +#endif /* GL_ARB_compute_variable_group_size */ + +#ifndef GL_ARB_conditional_render_inverted +#define GL_ARB_conditional_render_inverted 1 +#define GL_QUERY_WAIT_INVERTED 0x8E17 +#define GL_QUERY_NO_WAIT_INVERTED 0x8E18 +#define GL_QUERY_BY_REGION_WAIT_INVERTED 0x8E19 +#define GL_QUERY_BY_REGION_NO_WAIT_INVERTED 0x8E1A +#endif /* GL_ARB_conditional_render_inverted */ + +#ifndef GL_ARB_conservative_depth +#define GL_ARB_conservative_depth 1 +#endif /* GL_ARB_conservative_depth */ + +#ifndef GL_ARB_copy_buffer +#define GL_ARB_copy_buffer 1 +#define GL_COPY_READ_BUFFER 0x8F36 +#define GL_COPY_WRITE_BUFFER 0x8F37 +#endif /* GL_ARB_copy_buffer */ + +#ifndef GL_ARB_copy_image +#define GL_ARB_copy_image 1 +#endif /* GL_ARB_copy_image */ + +#ifndef GL_ARB_cull_distance +#define GL_ARB_cull_distance 1 +#define GL_MAX_CULL_DISTANCES 0x82F9 +#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES 0x82FA +#endif /* GL_ARB_cull_distance */ + +#ifndef GL_ARB_debug_output +#define GL_ARB_debug_output 1 +typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 +#define GL_DEBUG_SOURCE_API_ARB 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A +#define GL_DEBUG_SOURCE_OTHER_ARB 0x824B +#define GL_DEBUG_TYPE_ERROR_ARB 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E +#define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 +#define GL_DEBUG_TYPE_OTHER_ARB 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 +#define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLARBPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint * ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTARBPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar * buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKARBPROC)(GLDEBUGPROCARB callback, const void * userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGARBPROC)(GLuint count, GLsizei bufSize, GLenum * sources, GLenum * types, GLuint * ids, GLenum * severities, GLsizei * lengths, GLchar * messageLog); +#endif /* GL_ARB_debug_output */ + +#ifndef GL_ARB_depth_buffer_float +#define GL_ARB_depth_buffer_float 1 +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_DEPTH32F_STENCIL8 0x8CAD +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#endif /* GL_ARB_depth_buffer_float */ + +#ifndef GL_ARB_depth_clamp +#define GL_ARB_depth_clamp 1 +#define GL_DEPTH_CLAMP 0x864F +#endif /* GL_ARB_depth_clamp */ + +#ifndef GL_ARB_depth_texture +#define GL_ARB_depth_texture 1 +#define GL_DEPTH_COMPONENT16_ARB 0x81A5 +#define GL_DEPTH_COMPONENT24_ARB 0x81A6 +#define GL_DEPTH_COMPONENT32_ARB 0x81A7 +#define GL_TEXTURE_DEPTH_SIZE_ARB 0x884A +#define GL_DEPTH_TEXTURE_MODE_ARB 0x884B +#endif /* GL_ARB_depth_texture */ + +#ifndef GL_ARB_derivative_control +#define GL_ARB_derivative_control 1 +#endif /* GL_ARB_derivative_control */ + +#ifndef GL_ARB_direct_state_access +#define GL_ARB_direct_state_access 1 +#define GL_TEXTURE_TARGET 0x1006 +#define GL_QUERY_TARGET 0x82EA +#define GL_TEXTURE_BINDING_1D 0x8068 +#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_TEXTURE_BINDING_BUFFER 0x8C2C +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#endif /* GL_ARB_direct_state_access */ + +#ifndef GL_ARB_draw_buffers +#define GL_ARB_draw_buffers 1 +#define GL_MAX_DRAW_BUFFERS_ARB 0x8824 +#define GL_DRAW_BUFFER0_ARB 0x8825 +#define GL_DRAW_BUFFER1_ARB 0x8826 +#define GL_DRAW_BUFFER2_ARB 0x8827 +#define GL_DRAW_BUFFER3_ARB 0x8828 +#define GL_DRAW_BUFFER4_ARB 0x8829 +#define GL_DRAW_BUFFER5_ARB 0x882A +#define GL_DRAW_BUFFER6_ARB 0x882B +#define GL_DRAW_BUFFER7_ARB 0x882C +#define GL_DRAW_BUFFER8_ARB 0x882D +#define GL_DRAW_BUFFER9_ARB 0x882E +#define GL_DRAW_BUFFER10_ARB 0x882F +#define GL_DRAW_BUFFER11_ARB 0x8830 +#define GL_DRAW_BUFFER12_ARB 0x8831 +#define GL_DRAW_BUFFER13_ARB 0x8832 +#define GL_DRAW_BUFFER14_ARB 0x8833 +#define GL_DRAW_BUFFER15_ARB 0x8834 +typedef void (APIENTRYP PFNGLDRAWBUFFERSARBPROC)(GLsizei n, const GLenum * bufs); +#endif /* GL_ARB_draw_buffers */ + +#ifndef GL_ARB_draw_buffers_blend +#define GL_ARB_draw_buffers_blend 1 +typedef void (APIENTRYP PFNGLBLENDEQUATIONIARBPROC)(GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIARBPROC)(GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLBLENDFUNCIARBPROC)(GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIARBPROC)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif /* GL_ARB_draw_buffers_blend */ + +#ifndef GL_ARB_draw_elements_base_vertex +#define GL_ARB_draw_elements_base_vertex 1 +#endif /* GL_ARB_draw_elements_base_vertex */ + +#ifndef GL_ARB_draw_indirect +#define GL_ARB_draw_indirect 1 +#define GL_DRAW_INDIRECT_BUFFER 0x8F3F +#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 +#endif /* GL_ARB_draw_indirect */ + +#ifndef GL_ARB_draw_instanced +#define GL_ARB_draw_instanced 1 +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDARBPROC)(GLenum mode, GLint first, GLsizei count, GLsizei primcount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDARBPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei primcount); +#endif /* GL_ARB_draw_instanced */ + +#ifndef GL_ARB_enhanced_layouts +#define GL_ARB_enhanced_layouts 1 +#define GL_LOCATION_COMPONENT 0x934A +#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B +#define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C +#endif /* GL_ARB_enhanced_layouts */ + +#ifndef GL_ARB_explicit_attrib_location +#define GL_ARB_explicit_attrib_location 1 +#endif /* GL_ARB_explicit_attrib_location */ + +#ifndef GL_ARB_explicit_uniform_location +#define GL_ARB_explicit_uniform_location 1 +#define GL_MAX_UNIFORM_LOCATIONS 0x826E +#endif /* GL_ARB_explicit_uniform_location */ + +#ifndef GL_ARB_fragment_coord_conventions +#define GL_ARB_fragment_coord_conventions 1 +#endif /* GL_ARB_fragment_coord_conventions */ + +#ifndef GL_ARB_fragment_layer_viewport +#define GL_ARB_fragment_layer_viewport 1 +#endif /* GL_ARB_fragment_layer_viewport */ + +#ifndef GL_ARB_fragment_program +#define GL_ARB_fragment_program 1 +#define GL_FRAGMENT_PROGRAM_ARB 0x8804 +#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875 +#define GL_PROGRAM_LENGTH_ARB 0x8627 +#define GL_PROGRAM_FORMAT_ARB 0x8876 +#define GL_PROGRAM_BINDING_ARB 0x8677 +#define GL_PROGRAM_INSTRUCTIONS_ARB 0x88A0 +#define GL_MAX_PROGRAM_INSTRUCTIONS_ARB 0x88A1 +#define GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A2 +#define GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A3 +#define GL_PROGRAM_TEMPORARIES_ARB 0x88A4 +#define GL_MAX_PROGRAM_TEMPORARIES_ARB 0x88A5 +#define GL_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A6 +#define GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A7 +#define GL_PROGRAM_PARAMETERS_ARB 0x88A8 +#define GL_MAX_PROGRAM_PARAMETERS_ARB 0x88A9 +#define GL_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AA +#define GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AB +#define GL_PROGRAM_ATTRIBS_ARB 0x88AC +#define GL_MAX_PROGRAM_ATTRIBS_ARB 0x88AD +#define GL_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AE +#define GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AF +#define GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB 0x88B4 +#define GL_MAX_PROGRAM_ENV_PARAMETERS_ARB 0x88B5 +#define GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB 0x88B6 +#define GL_PROGRAM_ALU_INSTRUCTIONS_ARB 0x8805 +#define GL_PROGRAM_TEX_INSTRUCTIONS_ARB 0x8806 +#define GL_PROGRAM_TEX_INDIRECTIONS_ARB 0x8807 +#define GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x8808 +#define GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x8809 +#define GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x880A +#define GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB 0x880B +#define GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB 0x880C +#define GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB 0x880D +#define GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x880E +#define GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x880F +#define GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x8810 +#define GL_PROGRAM_STRING_ARB 0x8628 +#define GL_PROGRAM_ERROR_POSITION_ARB 0x864B +#define GL_CURRENT_MATRIX_ARB 0x8641 +#define GL_TRANSPOSE_CURRENT_MATRIX_ARB 0x88B7 +#define GL_CURRENT_MATRIX_STACK_DEPTH_ARB 0x8640 +#define GL_MAX_PROGRAM_MATRICES_ARB 0x862F +#define GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB 0x862E +#define GL_MAX_TEXTURE_COORDS_ARB 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872 +#define GL_PROGRAM_ERROR_STRING_ARB 0x8874 +#define GL_MATRIX0_ARB 0x88C0 +#define GL_MATRIX1_ARB 0x88C1 +#define GL_MATRIX2_ARB 0x88C2 +#define GL_MATRIX3_ARB 0x88C3 +#define GL_MATRIX4_ARB 0x88C4 +#define GL_MATRIX5_ARB 0x88C5 +#define GL_MATRIX6_ARB 0x88C6 +#define GL_MATRIX7_ARB 0x88C7 +#define GL_MATRIX8_ARB 0x88C8 +#define GL_MATRIX9_ARB 0x88C9 +#define GL_MATRIX10_ARB 0x88CA +#define GL_MATRIX11_ARB 0x88CB +#define GL_MATRIX12_ARB 0x88CC +#define GL_MATRIX13_ARB 0x88CD +#define GL_MATRIX14_ARB 0x88CE +#define GL_MATRIX15_ARB 0x88CF +#define GL_MATRIX16_ARB 0x88D0 +#define GL_MATRIX17_ARB 0x88D1 +#define GL_MATRIX18_ARB 0x88D2 +#define GL_MATRIX19_ARB 0x88D3 +#define GL_MATRIX20_ARB 0x88D4 +#define GL_MATRIX21_ARB 0x88D5 +#define GL_MATRIX22_ARB 0x88D6 +#define GL_MATRIX23_ARB 0x88D7 +#define GL_MATRIX24_ARB 0x88D8 +#define GL_MATRIX25_ARB 0x88D9 +#define GL_MATRIX26_ARB 0x88DA +#define GL_MATRIX27_ARB 0x88DB +#define GL_MATRIX28_ARB 0x88DC +#define GL_MATRIX29_ARB 0x88DD +#define GL_MATRIX30_ARB 0x88DE +#define GL_MATRIX31_ARB 0x88DF +typedef void (APIENTRYP PFNGLPROGRAMSTRINGARBPROC)(GLenum target, GLenum format, GLsizei len, const void * string); +typedef void (APIENTRYP PFNGLBINDPROGRAMARBPROC)(GLenum target, GLuint program); +typedef void (APIENTRYP PFNGLDELETEPROGRAMSARBPROC)(GLsizei n, const GLuint * programs); +typedef void (APIENTRYP PFNGLGENPROGRAMSARBPROC)(GLsizei n, GLuint * programs); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DARBPROC)(GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DVARBPROC)(GLenum target, GLuint index, const GLdouble * params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FARBPROC)(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FVARBPROC)(GLenum target, GLuint index, const GLfloat * params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DARBPROC)(GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DVARBPROC)(GLenum target, GLuint index, const GLdouble * params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FARBPROC)(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FVARBPROC)(GLenum target, GLuint index, const GLfloat * params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERDVARBPROC)(GLenum target, GLuint index, GLdouble * params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERFVARBPROC)(GLenum target, GLuint index, GLfloat * params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC)(GLenum target, GLuint index, GLdouble * params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC)(GLenum target, GLuint index, GLfloat * params); +typedef void (APIENTRYP PFNGLGETPROGRAMIVARBPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGARBPROC)(GLenum target, GLenum pname, void * string); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMARBPROC)(GLuint program); +#endif /* GL_ARB_fragment_program */ + +#ifndef GL_ARB_fragment_program_shadow +#define GL_ARB_fragment_program_shadow 1 +#endif /* GL_ARB_fragment_program_shadow */ + +#ifndef GL_ARB_fragment_shader +#define GL_ARB_fragment_shader 1 +#define GL_FRAGMENT_SHADER_ARB 0x8B30 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB 0x8B49 +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB 0x8B8B +#endif /* GL_ARB_fragment_shader */ + +#ifndef GL_ARB_fragment_shader_interlock +#define GL_ARB_fragment_shader_interlock 1 +#endif /* GL_ARB_fragment_shader_interlock */ + +#ifndef GL_ARB_framebuffer_no_attachments +#define GL_ARB_framebuffer_no_attachments 1 +#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 +#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 +#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 +#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 +#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315 +#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316 +#define GL_MAX_FRAMEBUFFER_LAYERS 0x9317 +#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318 +#endif /* GL_ARB_framebuffer_no_attachments */ + +#ifndef GL_ARB_framebuffer_object +#define GL_ARB_framebuffer_object 1 +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#define GL_FRAMEBUFFER_DEFAULT 0x8218 +#define GL_FRAMEBUFFER_UNDEFINED 0x8219 +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_UNSIGNED_INT_24_8 0x84FA +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE 0x88F1 +#define GL_UNSIGNED_NORMALIZED 0x8C17 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA +#define GL_RENDERBUFFER_SAMPLES 0x8CAB +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#define GL_COLOR_ATTACHMENT11 0x8CEB +#define GL_COLOR_ATTACHMENT12 0x8CEC +#define GL_COLOR_ATTACHMENT13 0x8CED +#define GL_COLOR_ATTACHMENT14 0x8CEE +#define GL_COLOR_ATTACHMENT15 0x8CEF +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_STENCIL_INDEX1 0x8D46 +#define GL_STENCIL_INDEX4 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_INDEX16 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 +#define GL_MAX_SAMPLES 0x8D57 +#define GL_INDEX 0x8222 +#endif /* GL_ARB_framebuffer_object */ + +#ifndef GL_ARB_framebuffer_sRGB +#define GL_ARB_framebuffer_sRGB 1 +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#endif /* GL_ARB_framebuffer_sRGB */ + +#ifndef GL_ARB_geometry_shader4 +#define GL_ARB_geometry_shader4 1 +#define GL_LINES_ADJACENCY_ARB 0x000A +#define GL_LINE_STRIP_ADJACENCY_ARB 0x000B +#define GL_TRIANGLES_ADJACENCY_ARB 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY_ARB 0x000D +#define GL_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9 +#define GL_GEOMETRY_SHADER_ARB 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT_ARB 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_ARB 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_ARB 0x8DDC +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB 0x8DDD +#define GL_MAX_VERTEX_VARYING_COMPONENTS_ARB 0x8DDE +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB 0x8DE1 +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIARBPROC)(GLuint program, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREARBPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERARBPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEARBPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif /* GL_ARB_geometry_shader4 */ + +#ifndef GL_ARB_get_program_binary +#define GL_ARB_get_program_binary 1 +#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_PROGRAM_BINARY_FORMATS 0x87FF +#endif /* GL_ARB_get_program_binary */ + +#ifndef GL_ARB_get_texture_sub_image +#define GL_ARB_get_texture_sub_image 1 +#endif /* GL_ARB_get_texture_sub_image */ + +#ifndef GL_ARB_gl_spirv +#define GL_ARB_gl_spirv 1 +#define GL_SHADER_BINARY_FORMAT_SPIR_V_ARB 0x9551 +#define GL_SPIR_V_BINARY_ARB 0x9552 +typedef void (APIENTRYP PFNGLSPECIALIZESHADERARBPROC)(GLuint shader, const GLchar * pEntryPoint, GLuint numSpecializationConstants, const GLuint * pConstantIndex, const GLuint * pConstantValue); +#endif /* GL_ARB_gl_spirv */ + +#ifndef GL_ARB_gpu_shader5 +#define GL_ARB_gpu_shader5 1 +#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C +#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D +#define GL_MAX_VERTEX_STREAMS 0x8E71 +#endif /* GL_ARB_gpu_shader5 */ + +#ifndef GL_ARB_gpu_shader_fp64 +#define GL_ARB_gpu_shader_fp64 1 +#define GL_DOUBLE 0x140A +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E +#endif /* GL_ARB_gpu_shader_fp64 */ + +#ifndef GL_ARB_gpu_shader_int64 +#define GL_ARB_gpu_shader_int64 1 +#define GL_INT64_ARB 0x140E +#define GL_UNSIGNED_INT64_ARB 0x140F +#define GL_INT64_VEC2_ARB 0x8FE9 +#define GL_INT64_VEC3_ARB 0x8FEA +#define GL_INT64_VEC4_ARB 0x8FEB +#define GL_UNSIGNED_INT64_VEC2_ARB 0x8FF5 +#define GL_UNSIGNED_INT64_VEC3_ARB 0x8FF6 +#define GL_UNSIGNED_INT64_VEC4_ARB 0x8FF7 +typedef void (APIENTRYP PFNGLUNIFORM1I64ARBPROC)(GLint location, GLint64 x); +typedef void (APIENTRYP PFNGLUNIFORM2I64ARBPROC)(GLint location, GLint64 x, GLint64 y); +typedef void (APIENTRYP PFNGLUNIFORM3I64ARBPROC)(GLint location, GLint64 x, GLint64 y, GLint64 z); +typedef void (APIENTRYP PFNGLUNIFORM4I64ARBPROC)(GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); +typedef void (APIENTRYP PFNGLUNIFORM1I64VARBPROC)(GLint location, GLsizei count, const GLint64 * value); +typedef void (APIENTRYP PFNGLUNIFORM2I64VARBPROC)(GLint location, GLsizei count, const GLint64 * value); +typedef void (APIENTRYP PFNGLUNIFORM3I64VARBPROC)(GLint location, GLsizei count, const GLint64 * value); +typedef void (APIENTRYP PFNGLUNIFORM4I64VARBPROC)(GLint location, GLsizei count, const GLint64 * value); +typedef void (APIENTRYP PFNGLUNIFORM1UI64ARBPROC)(GLint location, GLuint64 x); +typedef void (APIENTRYP PFNGLUNIFORM2UI64ARBPROC)(GLint location, GLuint64 x, GLuint64 y); +typedef void (APIENTRYP PFNGLUNIFORM3UI64ARBPROC)(GLint location, GLuint64 x, GLuint64 y, GLuint64 z); +typedef void (APIENTRYP PFNGLUNIFORM4UI64ARBPROC)(GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); +typedef void (APIENTRYP PFNGLUNIFORM1UI64VARBPROC)(GLint location, GLsizei count, const GLuint64 * value); +typedef void (APIENTRYP PFNGLUNIFORM2UI64VARBPROC)(GLint location, GLsizei count, const GLuint64 * value); +typedef void (APIENTRYP PFNGLUNIFORM3UI64VARBPROC)(GLint location, GLsizei count, const GLuint64 * value); +typedef void (APIENTRYP PFNGLUNIFORM4UI64VARBPROC)(GLint location, GLsizei count, const GLuint64 * value); +typedef void (APIENTRYP PFNGLGETUNIFORMI64VARBPROC)(GLuint program, GLint location, GLint64 * params); +typedef void (APIENTRYP PFNGLGETUNIFORMUI64VARBPROC)(GLuint program, GLint location, GLuint64 * params); +typedef void (APIENTRYP PFNGLGETNUNIFORMI64VARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLint64 * params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUI64VARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint64 * params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64ARBPROC)(GLuint program, GLint location, GLint64 x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64ARBPROC)(GLuint program, GLint location, GLint64 x, GLint64 y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64ARBPROC)(GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64ARBPROC)(GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLint64 * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLint64 * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLint64 * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLint64 * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64ARBPROC)(GLuint program, GLint location, GLuint64 x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64ARBPROC)(GLuint program, GLint location, GLuint64 x, GLuint64 y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64ARBPROC)(GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64ARBPROC)(GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLuint64 * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLuint64 * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLuint64 * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLuint64 * value); +#endif /* GL_ARB_gpu_shader_int64 */ + +#ifndef GL_ARB_half_float_pixel +#define GL_ARB_half_float_pixel 1 +typedef khronos_uint16_t GLhalfARB; +#define GL_HALF_FLOAT_ARB 0x140B +#endif /* GL_ARB_half_float_pixel */ + +#ifndef GL_ARB_half_float_vertex +#define GL_ARB_half_float_vertex 1 +#define GL_HALF_FLOAT 0x140B +#endif /* GL_ARB_half_float_vertex */ + +#ifndef GL_ARB_imaging +#define GL_ARB_imaging 1 +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_EQUATION 0x8009 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_FUNC_SUBTRACT 0x800A +#define GL_MIN 0x8007 +#define GL_MAX 0x8008 +#define GL_CONVOLUTION_1D 0x8010 +#define GL_CONVOLUTION_2D 0x8011 +#define GL_SEPARABLE_2D 0x8012 +#define GL_CONVOLUTION_BORDER_MODE 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS 0x8015 +#define GL_REDUCE 0x8016 +#define GL_CONVOLUTION_FORMAT 0x8017 +#define GL_CONVOLUTION_WIDTH 0x8018 +#define GL_CONVOLUTION_HEIGHT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS 0x8023 +#define GL_HISTOGRAM 0x8024 +#define GL_PROXY_HISTOGRAM 0x8025 +#define GL_HISTOGRAM_WIDTH 0x8026 +#define GL_HISTOGRAM_FORMAT 0x8027 +#define GL_HISTOGRAM_RED_SIZE 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE 0x802C +#define GL_HISTOGRAM_SINK 0x802D +#define GL_MINMAX 0x802E +#define GL_MINMAX_FORMAT 0x802F +#define GL_MINMAX_SINK 0x8030 +#define GL_TABLE_TOO_LARGE 0x8031 +#define GL_COLOR_MATRIX 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS 0x80BB +#define GL_COLOR_TABLE 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2 +#define GL_PROXY_COLOR_TABLE 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5 +#define GL_COLOR_TABLE_SCALE 0x80D6 +#define GL_COLOR_TABLE_BIAS 0x80D7 +#define GL_COLOR_TABLE_FORMAT 0x80D8 +#define GL_COLOR_TABLE_WIDTH 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE 0x80DF +#define GL_CONSTANT_BORDER 0x8151 +#define GL_REPLICATE_BORDER 0x8153 +#define GL_CONVOLUTION_BORDER_COLOR 0x8154 +typedef void (APIENTRYP PFNGLCOLORTABLEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void * table); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLCOPYCOLORTABLEPROC)(GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPROC)(GLenum target, GLenum format, GLenum type, void * table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLCOLORSUBTABLEPROC)(GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void * data); +typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEPROC)(GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DPROC)(GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void * image); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * image); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIPROC)(GLenum target, GLenum pname, GLint params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DPROC)(GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DPROC)(GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTERPROC)(GLenum target, GLenum format, GLenum type, void * image); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETSEPARABLEFILTERPROC)(GLenum target, GLenum format, GLenum type, void * row, void * column, void * span); +typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * row, const void * column); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, void * values); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETMINMAXPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, void * values); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLHISTOGRAMPROC)(GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLMINMAXPROC)(GLenum target, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLRESETHISTOGRAMPROC)(GLenum target); +typedef void (APIENTRYP PFNGLRESETMINMAXPROC)(GLenum target); +#endif /* GL_ARB_imaging */ + +#ifndef GL_ARB_indirect_parameters +#define GL_ARB_indirect_parameters 1 +#define GL_PARAMETER_BUFFER_ARB 0x80EE +#define GL_PARAMETER_BUFFER_BINDING_ARB 0x80EF +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC)(GLenum mode, const void * indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC)(GLenum mode, GLenum type, const void * indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#endif /* GL_ARB_indirect_parameters */ + +#ifndef GL_ARB_instanced_arrays +#define GL_ARB_instanced_arrays 1 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB 0x88FE +typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORARBPROC)(GLuint index, GLuint divisor); +#endif /* GL_ARB_instanced_arrays */ + +#ifndef GL_ARB_internalformat_query +#define GL_ARB_internalformat_query 1 +#define GL_NUM_SAMPLE_COUNTS 0x9380 +#endif /* GL_ARB_internalformat_query */ + +#ifndef GL_ARB_internalformat_query2 +#define GL_ARB_internalformat_query2 1 +#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 +#define GL_NUM_SAMPLE_COUNTS 0x9380 +#define GL_RENDERBUFFER 0x8D41 +#define GL_SAMPLES 0x80A9 +#define GL_TEXTURE_1D 0x0DE0 +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_TEXTURE_3D 0x806F +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#define GL_TEXTURE_RECTANGLE 0x84F5 +#define GL_TEXTURE_BUFFER 0x8C2A +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_INTERNALFORMAT_SUPPORTED 0x826F +#define GL_INTERNALFORMAT_PREFERRED 0x8270 +#define GL_INTERNALFORMAT_RED_SIZE 0x8271 +#define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 +#define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 +#define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 +#define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 +#define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 +#define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 +#define GL_INTERNALFORMAT_RED_TYPE 0x8278 +#define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 +#define GL_INTERNALFORMAT_BLUE_TYPE 0x827A +#define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B +#define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C +#define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D +#define GL_MAX_WIDTH 0x827E +#define GL_MAX_HEIGHT 0x827F +#define GL_MAX_DEPTH 0x8280 +#define GL_MAX_LAYERS 0x8281 +#define GL_MAX_COMBINED_DIMENSIONS 0x8282 +#define GL_COLOR_COMPONENTS 0x8283 +#define GL_DEPTH_COMPONENTS 0x8284 +#define GL_STENCIL_COMPONENTS 0x8285 +#define GL_COLOR_RENDERABLE 0x8286 +#define GL_DEPTH_RENDERABLE 0x8287 +#define GL_STENCIL_RENDERABLE 0x8288 +#define GL_FRAMEBUFFER_RENDERABLE 0x8289 +#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A +#define GL_FRAMEBUFFER_BLEND 0x828B +#define GL_READ_PIXELS 0x828C +#define GL_READ_PIXELS_FORMAT 0x828D +#define GL_READ_PIXELS_TYPE 0x828E +#define GL_TEXTURE_IMAGE_FORMAT 0x828F +#define GL_TEXTURE_IMAGE_TYPE 0x8290 +#define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 +#define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 +#define GL_MIPMAP 0x8293 +#define GL_MANUAL_GENERATE_MIPMAP 0x8294 +#define GL_AUTO_GENERATE_MIPMAP 0x8295 +#define GL_COLOR_ENCODING 0x8296 +#define GL_SRGB_READ 0x8297 +#define GL_SRGB_WRITE 0x8298 +#define GL_SRGB_DECODE_ARB 0x8299 +#define GL_FILTER 0x829A +#define GL_VERTEX_TEXTURE 0x829B +#define GL_TESS_CONTROL_TEXTURE 0x829C +#define GL_TESS_EVALUATION_TEXTURE 0x829D +#define GL_GEOMETRY_TEXTURE 0x829E +#define GL_FRAGMENT_TEXTURE 0x829F +#define GL_COMPUTE_TEXTURE 0x82A0 +#define GL_TEXTURE_SHADOW 0x82A1 +#define GL_TEXTURE_GATHER 0x82A2 +#define GL_TEXTURE_GATHER_SHADOW 0x82A3 +#define GL_SHADER_IMAGE_LOAD 0x82A4 +#define GL_SHADER_IMAGE_STORE 0x82A5 +#define GL_SHADER_IMAGE_ATOMIC 0x82A6 +#define GL_IMAGE_TEXEL_SIZE 0x82A7 +#define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 +#define GL_IMAGE_PIXEL_FORMAT 0x82A9 +#define GL_IMAGE_PIXEL_TYPE 0x82AA +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF +#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1 +#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2 +#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3 +#define GL_CLEAR_BUFFER 0x82B4 +#define GL_TEXTURE_VIEW 0x82B5 +#define GL_VIEW_COMPATIBILITY_CLASS 0x82B6 +#define GL_FULL_SUPPORT 0x82B7 +#define GL_CAVEAT_SUPPORT 0x82B8 +#define GL_IMAGE_CLASS_4_X_32 0x82B9 +#define GL_IMAGE_CLASS_2_X_32 0x82BA +#define GL_IMAGE_CLASS_1_X_32 0x82BB +#define GL_IMAGE_CLASS_4_X_16 0x82BC +#define GL_IMAGE_CLASS_2_X_16 0x82BD +#define GL_IMAGE_CLASS_1_X_16 0x82BE +#define GL_IMAGE_CLASS_4_X_8 0x82BF +#define GL_IMAGE_CLASS_2_X_8 0x82C0 +#define GL_IMAGE_CLASS_1_X_8 0x82C1 +#define GL_IMAGE_CLASS_11_11_10 0x82C2 +#define GL_IMAGE_CLASS_10_10_10_2 0x82C3 +#define GL_VIEW_CLASS_128_BITS 0x82C4 +#define GL_VIEW_CLASS_96_BITS 0x82C5 +#define GL_VIEW_CLASS_64_BITS 0x82C6 +#define GL_VIEW_CLASS_48_BITS 0x82C7 +#define GL_VIEW_CLASS_32_BITS 0x82C8 +#define GL_VIEW_CLASS_24_BITS 0x82C9 +#define GL_VIEW_CLASS_16_BITS 0x82CA +#define GL_VIEW_CLASS_8_BITS 0x82CB +#define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC +#define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD +#define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE +#define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF +#define GL_VIEW_CLASS_RGTC1_RED 0x82D0 +#define GL_VIEW_CLASS_RGTC2_RG 0x82D1 +#define GL_VIEW_CLASS_BPTC_UNORM 0x82D2 +#define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3 +#define GL_VIEW_CLASS_EAC_R11 0x9383 +#define GL_VIEW_CLASS_EAC_RG11 0x9384 +#define GL_VIEW_CLASS_ETC2_RGB 0x9385 +#define GL_VIEW_CLASS_ETC2_RGBA 0x9386 +#define GL_VIEW_CLASS_ETC2_EAC_RGBA 0x9387 +#define GL_VIEW_CLASS_ASTC_4x4_RGBA 0x9388 +#define GL_VIEW_CLASS_ASTC_5x4_RGBA 0x9389 +#define GL_VIEW_CLASS_ASTC_5x5_RGBA 0x938A +#define GL_VIEW_CLASS_ASTC_6x5_RGBA 0x938B +#define GL_VIEW_CLASS_ASTC_6x6_RGBA 0x938C +#define GL_VIEW_CLASS_ASTC_8x5_RGBA 0x938D +#define GL_VIEW_CLASS_ASTC_8x6_RGBA 0x938E +#define GL_VIEW_CLASS_ASTC_8x8_RGBA 0x938F +#define GL_VIEW_CLASS_ASTC_10x5_RGBA 0x9390 +#define GL_VIEW_CLASS_ASTC_10x6_RGBA 0x9391 +#define GL_VIEW_CLASS_ASTC_10x8_RGBA 0x9392 +#define GL_VIEW_CLASS_ASTC_10x10_RGBA 0x9393 +#define GL_VIEW_CLASS_ASTC_12x10_RGBA 0x9394 +#define GL_VIEW_CLASS_ASTC_12x12_RGBA 0x9395 +#endif /* GL_ARB_internalformat_query2 */ + +#ifndef GL_ARB_invalidate_subdata +#define GL_ARB_invalidate_subdata 1 +#endif /* GL_ARB_invalidate_subdata */ + +#ifndef GL_ARB_map_buffer_alignment +#define GL_ARB_map_buffer_alignment 1 +#define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC +#endif /* GL_ARB_map_buffer_alignment */ + +#ifndef GL_ARB_map_buffer_range +#define GL_ARB_map_buffer_range 1 +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#endif /* GL_ARB_map_buffer_range */ + +#ifndef GL_ARB_matrix_palette +#define GL_ARB_matrix_palette 1 +#define GL_MATRIX_PALETTE_ARB 0x8840 +#define GL_MAX_MATRIX_PALETTE_STACK_DEPTH_ARB 0x8841 +#define GL_MAX_PALETTE_MATRICES_ARB 0x8842 +#define GL_CURRENT_PALETTE_MATRIX_ARB 0x8843 +#define GL_MATRIX_INDEX_ARRAY_ARB 0x8844 +#define GL_CURRENT_MATRIX_INDEX_ARB 0x8845 +#define GL_MATRIX_INDEX_ARRAY_SIZE_ARB 0x8846 +#define GL_MATRIX_INDEX_ARRAY_TYPE_ARB 0x8847 +#define GL_MATRIX_INDEX_ARRAY_STRIDE_ARB 0x8848 +#define GL_MATRIX_INDEX_ARRAY_POINTER_ARB 0x8849 +typedef void (APIENTRYP PFNGLCURRENTPALETTEMATRIXARBPROC)(GLint index); +typedef void (APIENTRYP PFNGLMATRIXINDEXUBVARBPROC)(GLint size, const GLubyte * indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXUSVARBPROC)(GLint size, const GLushort * indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXUIVARBPROC)(GLint size, const GLuint * indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXPOINTERARBPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); +#endif /* GL_ARB_matrix_palette */ + +#ifndef GL_ARB_multi_bind +#define GL_ARB_multi_bind 1 +#endif /* GL_ARB_multi_bind */ + +#ifndef GL_ARB_multi_draw_indirect +#define GL_ARB_multi_draw_indirect 1 +#endif /* GL_ARB_multi_draw_indirect */ + +#ifndef GL_ARB_multisample +#define GL_ARB_multisample 1 +#define GL_MULTISAMPLE_ARB 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_ARB 0x809F +#define GL_SAMPLE_COVERAGE_ARB 0x80A0 +#define GL_SAMPLE_BUFFERS_ARB 0x80A8 +#define GL_SAMPLES_ARB 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE_ARB 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT_ARB 0x80AB +#define GL_MULTISAMPLE_BIT_ARB 0x20000000 +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEARBPROC)(GLfloat value, GLboolean invert); +#endif /* GL_ARB_multisample */ + +#ifndef GL_ARB_multitexture +#define GL_ARB_multitexture 1 +#define GL_TEXTURE0_ARB 0x84C0 +#define GL_TEXTURE1_ARB 0x84C1 +#define GL_TEXTURE2_ARB 0x84C2 +#define GL_TEXTURE3_ARB 0x84C3 +#define GL_TEXTURE4_ARB 0x84C4 +#define GL_TEXTURE5_ARB 0x84C5 +#define GL_TEXTURE6_ARB 0x84C6 +#define GL_TEXTURE7_ARB 0x84C7 +#define GL_TEXTURE8_ARB 0x84C8 +#define GL_TEXTURE9_ARB 0x84C9 +#define GL_TEXTURE10_ARB 0x84CA +#define GL_TEXTURE11_ARB 0x84CB +#define GL_TEXTURE12_ARB 0x84CC +#define GL_TEXTURE13_ARB 0x84CD +#define GL_TEXTURE14_ARB 0x84CE +#define GL_TEXTURE15_ARB 0x84CF +#define GL_TEXTURE16_ARB 0x84D0 +#define GL_TEXTURE17_ARB 0x84D1 +#define GL_TEXTURE18_ARB 0x84D2 +#define GL_TEXTURE19_ARB 0x84D3 +#define GL_TEXTURE20_ARB 0x84D4 +#define GL_TEXTURE21_ARB 0x84D5 +#define GL_TEXTURE22_ARB 0x84D6 +#define GL_TEXTURE23_ARB 0x84D7 +#define GL_TEXTURE24_ARB 0x84D8 +#define GL_TEXTURE25_ARB 0x84D9 +#define GL_TEXTURE26_ARB 0x84DA +#define GL_TEXTURE27_ARB 0x84DB +#define GL_TEXTURE28_ARB 0x84DC +#define GL_TEXTURE29_ARB 0x84DD +#define GL_TEXTURE30_ARB 0x84DE +#define GL_TEXTURE31_ARB 0x84DF +#define GL_ACTIVE_TEXTURE_ARB 0x84E0 +#define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 +#define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 +typedef void (APIENTRYP PFNGLACTIVETEXTUREARBPROC)(GLenum texture); +typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREARBPROC)(GLenum texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DARBPROC)(GLenum target, GLdouble s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVARBPROC)(GLenum target, const GLdouble * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FARBPROC)(GLenum target, GLfloat s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVARBPROC)(GLenum target, const GLfloat * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IARBPROC)(GLenum target, GLint s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVARBPROC)(GLenum target, const GLint * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SARBPROC)(GLenum target, GLshort s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVARBPROC)(GLenum target, const GLshort * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DARBPROC)(GLenum target, GLdouble s, GLdouble t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVARBPROC)(GLenum target, const GLdouble * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FARBPROC)(GLenum target, GLfloat s, GLfloat t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVARBPROC)(GLenum target, const GLfloat * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IARBPROC)(GLenum target, GLint s, GLint t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVARBPROC)(GLenum target, const GLint * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SARBPROC)(GLenum target, GLshort s, GLshort t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVARBPROC)(GLenum target, const GLshort * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DARBPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVARBPROC)(GLenum target, const GLdouble * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FARBPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVARBPROC)(GLenum target, const GLfloat * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IARBPROC)(GLenum target, GLint s, GLint t, GLint r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVARBPROC)(GLenum target, const GLint * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SARBPROC)(GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVARBPROC)(GLenum target, const GLshort * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DARBPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVARBPROC)(GLenum target, const GLdouble * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FARBPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVARBPROC)(GLenum target, const GLfloat * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IARBPROC)(GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVARBPROC)(GLenum target, const GLint * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SARBPROC)(GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVARBPROC)(GLenum target, const GLshort * v); +#endif /* GL_ARB_multitexture */ + +#ifndef GL_ARB_occlusion_query +#define GL_ARB_occlusion_query 1 +#define GL_QUERY_COUNTER_BITS_ARB 0x8864 +#define GL_CURRENT_QUERY_ARB 0x8865 +#define GL_QUERY_RESULT_ARB 0x8866 +#define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867 +#define GL_SAMPLES_PASSED_ARB 0x8914 +typedef void (APIENTRYP PFNGLGENQUERIESARBPROC)(GLsizei n, GLuint * ids); +typedef void (APIENTRYP PFNGLDELETEQUERIESARBPROC)(GLsizei n, const GLuint * ids); +typedef GLboolean (APIENTRYP PFNGLISQUERYARBPROC)(GLuint id); +typedef void (APIENTRYP PFNGLBEGINQUERYARBPROC)(GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYARBPROC)(GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYIVARBPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVARBPROC)(GLuint id, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVARBPROC)(GLuint id, GLenum pname, GLuint * params); +#endif /* GL_ARB_occlusion_query */ + +#ifndef GL_ARB_occlusion_query2 +#define GL_ARB_occlusion_query2 1 +#define GL_ANY_SAMPLES_PASSED 0x8C2F +#endif /* GL_ARB_occlusion_query2 */ + +#ifndef GL_ARB_parallel_shader_compile +#define GL_ARB_parallel_shader_compile 1 +#define GL_MAX_SHADER_COMPILER_THREADS_ARB 0x91B0 +#define GL_COMPLETION_STATUS_ARB 0x91B1 +typedef void (APIENTRYP PFNGLMAXSHADERCOMPILERTHREADSARBPROC)(GLuint count); +#endif /* GL_ARB_parallel_shader_compile */ + +#ifndef GL_ARB_pipeline_statistics_query +#define GL_ARB_pipeline_statistics_query 1 +#define GL_VERTICES_SUBMITTED_ARB 0x82EE +#define GL_PRIMITIVES_SUBMITTED_ARB 0x82EF +#define GL_VERTEX_SHADER_INVOCATIONS_ARB 0x82F0 +#define GL_TESS_CONTROL_SHADER_PATCHES_ARB 0x82F1 +#define GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB 0x82F2 +#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F +#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB 0x82F3 +#define GL_FRAGMENT_SHADER_INVOCATIONS_ARB 0x82F4 +#define GL_COMPUTE_SHADER_INVOCATIONS_ARB 0x82F5 +#define GL_CLIPPING_INPUT_PRIMITIVES_ARB 0x82F6 +#define GL_CLIPPING_OUTPUT_PRIMITIVES_ARB 0x82F7 +#endif /* GL_ARB_pipeline_statistics_query */ + +#ifndef GL_ARB_pixel_buffer_object +#define GL_ARB_pixel_buffer_object 1 +#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF +#endif /* GL_ARB_pixel_buffer_object */ + +#ifndef GL_ARB_point_parameters +#define GL_ARB_point_parameters 1 +#define GL_POINT_SIZE_MIN_ARB 0x8126 +#define GL_POINT_SIZE_MAX_ARB 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_ARB 0x8128 +#define GL_POINT_DISTANCE_ATTENUATION_ARB 0x8129 +typedef void (APIENTRYP PFNGLPOINTPARAMETERFARBPROC)(GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVARBPROC)(GLenum pname, const GLfloat * params); +#endif /* GL_ARB_point_parameters */ + +#ifndef GL_ARB_point_sprite +#define GL_ARB_point_sprite 1 +#define GL_POINT_SPRITE_ARB 0x8861 +#define GL_COORD_REPLACE_ARB 0x8862 +#endif /* GL_ARB_point_sprite */ + +#ifndef GL_ARB_polygon_offset_clamp +#define GL_ARB_polygon_offset_clamp 1 +#define GL_POLYGON_OFFSET_CLAMP 0x8E1B +#endif /* GL_ARB_polygon_offset_clamp */ + +#ifndef GL_ARB_post_depth_coverage +#define GL_ARB_post_depth_coverage 1 +#endif /* GL_ARB_post_depth_coverage */ + +#ifndef GL_ARB_program_interface_query +#define GL_ARB_program_interface_query 1 +#define GL_UNIFORM 0x92E1 +#define GL_UNIFORM_BLOCK 0x92E2 +#define GL_PROGRAM_INPUT 0x92E3 +#define GL_PROGRAM_OUTPUT 0x92E4 +#define GL_BUFFER_VARIABLE 0x92E5 +#define GL_SHADER_STORAGE_BLOCK 0x92E6 +#define GL_ATOMIC_COUNTER_BUFFER 0x92C0 +#define GL_VERTEX_SUBROUTINE 0x92E8 +#define GL_TESS_CONTROL_SUBROUTINE 0x92E9 +#define GL_TESS_EVALUATION_SUBROUTINE 0x92EA +#define GL_GEOMETRY_SUBROUTINE 0x92EB +#define GL_FRAGMENT_SUBROUTINE 0x92EC +#define GL_COMPUTE_SUBROUTINE 0x92ED +#define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE +#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF +#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0 +#define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 +#define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 +#define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 +#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4 +#define GL_ACTIVE_RESOURCES 0x92F5 +#define GL_MAX_NAME_LENGTH 0x92F6 +#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7 +#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8 +#define GL_NAME_LENGTH 0x92F9 +#define GL_TYPE 0x92FA +#define GL_ARRAY_SIZE 0x92FB +#define GL_OFFSET 0x92FC +#define GL_BLOCK_INDEX 0x92FD +#define GL_ARRAY_STRIDE 0x92FE +#define GL_MATRIX_STRIDE 0x92FF +#define GL_IS_ROW_MAJOR 0x9300 +#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 +#define GL_BUFFER_BINDING 0x9302 +#define GL_BUFFER_DATA_SIZE 0x9303 +#define GL_NUM_ACTIVE_VARIABLES 0x9304 +#define GL_ACTIVE_VARIABLES 0x9305 +#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308 +#define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309 +#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A +#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B +#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C +#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D +#define GL_LOCATION 0x930E +#define GL_LOCATION_INDEX 0x930F +#define GL_IS_PER_PATCH 0x92E7 +#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A +#define GL_COMPATIBLE_SUBROUTINES 0x8E4B +#endif /* GL_ARB_program_interface_query */ + +#ifndef GL_ARB_provoking_vertex +#define GL_ARB_provoking_vertex 1 +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION 0x8E4D +#define GL_LAST_VERTEX_CONVENTION 0x8E4E +#define GL_PROVOKING_VERTEX 0x8E4F +#endif /* GL_ARB_provoking_vertex */ + +#ifndef GL_ARB_query_buffer_object +#define GL_ARB_query_buffer_object 1 +#define GL_QUERY_BUFFER 0x9192 +#define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000 +#define GL_QUERY_BUFFER_BINDING 0x9193 +#define GL_QUERY_RESULT_NO_WAIT 0x9194 +#endif /* GL_ARB_query_buffer_object */ + +#ifndef GL_ARB_robust_buffer_access_behavior +#define GL_ARB_robust_buffer_access_behavior 1 +#endif /* GL_ARB_robust_buffer_access_behavior */ + +#ifndef GL_ARB_robustness +#define GL_ARB_robustness 1 +#define GL_NO_ERROR 0 +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GL_GUILTY_CONTEXT_RESET_ARB 0x8253 +#define GL_INNOCENT_CONTEXT_RESET_ARB 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GL_NO_RESET_NOTIFICATION_ARB 0x8261 +typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC)(void); +typedef void (APIENTRYP PFNGLGETNTEXIMAGEARBPROC)(GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void * img); +typedef void (APIENTRYP PFNGLREADNPIXELSARBPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void * data); +typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC)(GLenum target, GLint lod, GLsizei bufSize, void * img); +typedef void (APIENTRYP PFNGLGETNUNIFORMFVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat * params); +typedef void (APIENTRYP PFNGLGETNUNIFORMIVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLint * params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUIVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint * params); +typedef void (APIENTRYP PFNGLGETNUNIFORMDVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLdouble * params); +typedef void (APIENTRYP PFNGLGETNMAPDVARBPROC)(GLenum target, GLenum query, GLsizei bufSize, GLdouble * v); +typedef void (APIENTRYP PFNGLGETNMAPFVARBPROC)(GLenum target, GLenum query, GLsizei bufSize, GLfloat * v); +typedef void (APIENTRYP PFNGLGETNMAPIVARBPROC)(GLenum target, GLenum query, GLsizei bufSize, GLint * v); +typedef void (APIENTRYP PFNGLGETNPIXELMAPFVARBPROC)(GLenum map, GLsizei bufSize, GLfloat * values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUIVARBPROC)(GLenum map, GLsizei bufSize, GLuint * values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUSVARBPROC)(GLenum map, GLsizei bufSize, GLushort * values); +typedef void (APIENTRYP PFNGLGETNPOLYGONSTIPPLEARBPROC)(GLsizei bufSize, GLubyte * pattern); +typedef void (APIENTRYP PFNGLGETNCOLORTABLEARBPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void * table); +typedef void (APIENTRYP PFNGLGETNCONVOLUTIONFILTERARBPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void * image); +typedef void (APIENTRYP PFNGLGETNSEPARABLEFILTERARBPROC)(GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void * row, GLsizei columnBufSize, void * column, void * span); +typedef void (APIENTRYP PFNGLGETNHISTOGRAMARBPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void * values); +typedef void (APIENTRYP PFNGLGETNMINMAXARBPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void * values); +#endif /* GL_ARB_robustness */ + +#ifndef GL_ARB_robustness_isolation +#define GL_ARB_robustness_isolation 1 +#endif /* GL_ARB_robustness_isolation */ + +#ifndef GL_ARB_sample_locations +#define GL_ARB_sample_locations 1 +#define GL_SAMPLE_LOCATION_SUBPIXEL_BITS_ARB 0x933D +#define GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_ARB 0x933E +#define GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_ARB 0x933F +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_ARB 0x9340 +#define GL_SAMPLE_LOCATION_ARB 0x8E50 +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_ARB 0x9341 +#define GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_ARB 0x9342 +#define GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_ARB 0x9343 +typedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLELOCATIONSFVARBPROC)(GLenum target, GLuint start, GLsizei count, const GLfloat * v); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVARBPROC)(GLuint framebuffer, GLuint start, GLsizei count, const GLfloat * v); +typedef void (APIENTRYP PFNGLEVALUATEDEPTHVALUESARBPROC)(void); +#endif /* GL_ARB_sample_locations */ + +#ifndef GL_ARB_sample_shading +#define GL_ARB_sample_shading 1 +#define GL_SAMPLE_SHADING_ARB 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE_ARB 0x8C37 +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGARBPROC)(GLfloat value); +#endif /* GL_ARB_sample_shading */ + +#ifndef GL_ARB_sampler_objects +#define GL_ARB_sampler_objects 1 +#define GL_SAMPLER_BINDING 0x8919 +#endif /* GL_ARB_sampler_objects */ + +#ifndef GL_ARB_seamless_cube_map +#define GL_ARB_seamless_cube_map 1 +#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#endif /* GL_ARB_seamless_cube_map */ + +#ifndef GL_ARB_seamless_cubemap_per_texture +#define GL_ARB_seamless_cubemap_per_texture 1 +#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#endif /* GL_ARB_seamless_cubemap_per_texture */ + +#ifndef GL_ARB_separate_shader_objects +#define GL_ARB_separate_shader_objects 1 +#define GL_VERTEX_SHADER_BIT 0x00000001 +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#define GL_GEOMETRY_SHADER_BIT 0x00000004 +#define GL_TESS_CONTROL_SHADER_BIT 0x00000008 +#define GL_TESS_EVALUATION_SHADER_BIT 0x00000010 +#define GL_ALL_SHADER_BITS 0xFFFFFFFF +#define GL_PROGRAM_SEPARABLE 0x8258 +#define GL_ACTIVE_PROGRAM 0x8259 +#define GL_PROGRAM_PIPELINE_BINDING 0x825A +#endif /* GL_ARB_separate_shader_objects */ + +#ifndef GL_ARB_shader_atomic_counter_ops +#define GL_ARB_shader_atomic_counter_ops 1 +#endif /* GL_ARB_shader_atomic_counter_ops */ + +#ifndef GL_ARB_shader_atomic_counters +#define GL_ARB_shader_atomic_counters 1 +#define GL_ATOMIC_COUNTER_BUFFER 0x92C0 +#define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1 +#define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2 +#define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3 +#define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB +#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE +#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF +#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0 +#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1 +#define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3 +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5 +#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6 +#define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC +#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9 +#define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA +#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB +#endif /* GL_ARB_shader_atomic_counters */ + +#ifndef GL_ARB_shader_ballot +#define GL_ARB_shader_ballot 1 +#endif /* GL_ARB_shader_ballot */ + +#ifndef GL_ARB_shader_bit_encoding +#define GL_ARB_shader_bit_encoding 1 +#endif /* GL_ARB_shader_bit_encoding */ + +#ifndef GL_ARB_shader_clock +#define GL_ARB_shader_clock 1 +#endif /* GL_ARB_shader_clock */ + +#ifndef GL_ARB_shader_draw_parameters +#define GL_ARB_shader_draw_parameters 1 +#endif /* GL_ARB_shader_draw_parameters */ + +#ifndef GL_ARB_shader_group_vote +#define GL_ARB_shader_group_vote 1 +#endif /* GL_ARB_shader_group_vote */ + +#ifndef GL_ARB_shader_image_load_store +#define GL_ARB_shader_image_load_store 1 +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 +#define GL_COMMAND_BARRIER_BIT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 +#define GL_ALL_BARRIER_BITS 0xFFFFFFFF +#define GL_MAX_IMAGE_UNITS 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39 +#define GL_IMAGE_BINDING_NAME 0x8F3A +#define GL_IMAGE_BINDING_LEVEL 0x8F3B +#define GL_IMAGE_BINDING_LAYERED 0x8F3C +#define GL_IMAGE_BINDING_LAYER 0x8F3D +#define GL_IMAGE_BINDING_ACCESS 0x8F3E +#define GL_IMAGE_1D 0x904C +#define GL_IMAGE_2D 0x904D +#define GL_IMAGE_3D 0x904E +#define GL_IMAGE_2D_RECT 0x904F +#define GL_IMAGE_CUBE 0x9050 +#define GL_IMAGE_BUFFER 0x9051 +#define GL_IMAGE_1D_ARRAY 0x9052 +#define GL_IMAGE_2D_ARRAY 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 +#define GL_INT_IMAGE_1D 0x9057 +#define GL_INT_IMAGE_2D 0x9058 +#define GL_INT_IMAGE_3D 0x9059 +#define GL_INT_IMAGE_2D_RECT 0x905A +#define GL_INT_IMAGE_CUBE 0x905B +#define GL_INT_IMAGE_BUFFER 0x905C +#define GL_INT_IMAGE_1D_ARRAY 0x905D +#define GL_INT_IMAGE_2D_ARRAY 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C +#define GL_MAX_IMAGE_SAMPLES 0x906D +#define GL_IMAGE_BINDING_FORMAT 0x906E +#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9 +#define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA +#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB +#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC +#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD +#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE +#define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF +#endif /* GL_ARB_shader_image_load_store */ + +#ifndef GL_ARB_shader_image_size +#define GL_ARB_shader_image_size 1 +#endif /* GL_ARB_shader_image_size */ + +#ifndef GL_ARB_shader_objects +#define GL_ARB_shader_objects 1 +#ifdef __APPLE__ +typedef void *GLhandleARB; +#else +typedef unsigned int GLhandleARB; +#endif +typedef char GLcharARB; +#define GL_PROGRAM_OBJECT_ARB 0x8B40 +#define GL_SHADER_OBJECT_ARB 0x8B48 +#define GL_OBJECT_TYPE_ARB 0x8B4E +#define GL_OBJECT_SUBTYPE_ARB 0x8B4F +#define GL_FLOAT_VEC2_ARB 0x8B50 +#define GL_FLOAT_VEC3_ARB 0x8B51 +#define GL_FLOAT_VEC4_ARB 0x8B52 +#define GL_INT_VEC2_ARB 0x8B53 +#define GL_INT_VEC3_ARB 0x8B54 +#define GL_INT_VEC4_ARB 0x8B55 +#define GL_BOOL_ARB 0x8B56 +#define GL_BOOL_VEC2_ARB 0x8B57 +#define GL_BOOL_VEC3_ARB 0x8B58 +#define GL_BOOL_VEC4_ARB 0x8B59 +#define GL_FLOAT_MAT2_ARB 0x8B5A +#define GL_FLOAT_MAT3_ARB 0x8B5B +#define GL_FLOAT_MAT4_ARB 0x8B5C +#define GL_SAMPLER_1D_ARB 0x8B5D +#define GL_SAMPLER_2D_ARB 0x8B5E +#define GL_SAMPLER_3D_ARB 0x8B5F +#define GL_SAMPLER_CUBE_ARB 0x8B60 +#define GL_SAMPLER_1D_SHADOW_ARB 0x8B61 +#define GL_SAMPLER_2D_SHADOW_ARB 0x8B62 +#define GL_SAMPLER_2D_RECT_ARB 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW_ARB 0x8B64 +#define GL_OBJECT_DELETE_STATUS_ARB 0x8B80 +#define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81 +#define GL_OBJECT_LINK_STATUS_ARB 0x8B82 +#define GL_OBJECT_VALIDATE_STATUS_ARB 0x8B83 +#define GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84 +#define GL_OBJECT_ATTACHED_OBJECTS_ARB 0x8B85 +#define GL_OBJECT_ACTIVE_UNIFORMS_ARB 0x8B86 +#define GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB 0x8B87 +#define GL_OBJECT_SHADER_SOURCE_LENGTH_ARB 0x8B88 +typedef void (APIENTRYP PFNGLDELETEOBJECTARBPROC)(GLhandleARB obj); +typedef GLhandleARB (APIENTRYP PFNGLGETHANDLEARBPROC)(GLenum pname); +typedef void (APIENTRYP PFNGLDETACHOBJECTARBPROC)(GLhandleARB containerObj, GLhandleARB attachedObj); +typedef GLhandleARB (APIENTRYP PFNGLCREATESHADEROBJECTARBPROC)(GLenum shaderType); +typedef void (APIENTRYP PFNGLSHADERSOURCEARBPROC)(GLhandleARB shaderObj, GLsizei count, const GLcharARB ** string, const GLint * length); +typedef void (APIENTRYP PFNGLCOMPILESHADERARBPROC)(GLhandleARB shaderObj); +typedef GLhandleARB (APIENTRYP PFNGLCREATEPROGRAMOBJECTARBPROC)(void); +typedef void (APIENTRYP PFNGLATTACHOBJECTARBPROC)(GLhandleARB containerObj, GLhandleARB obj); +typedef void (APIENTRYP PFNGLLINKPROGRAMARBPROC)(GLhandleARB programObj); +typedef void (APIENTRYP PFNGLUSEPROGRAMOBJECTARBPROC)(GLhandleARB programObj); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMARBPROC)(GLhandleARB programObj); +typedef void (APIENTRYP PFNGLUNIFORM1FARBPROC)(GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLUNIFORM2FARBPROC)(GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLUNIFORM3FARBPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLUNIFORM4FARBPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLUNIFORM1IARBPROC)(GLint location, GLint v0); +typedef void (APIENTRYP PFNGLUNIFORM2IARBPROC)(GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLUNIFORM3IARBPROC)(GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLUNIFORM4IARBPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLUNIFORM1FVARBPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (APIENTRYP PFNGLUNIFORM2FVARBPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (APIENTRYP PFNGLUNIFORM3FVARBPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (APIENTRYP PFNGLUNIFORM4FVARBPROC)(GLint location, GLsizei count, const GLfloat * value); +typedef void (APIENTRYP PFNGLUNIFORM1IVARBPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (APIENTRYP PFNGLUNIFORM2IVARBPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (APIENTRYP PFNGLUNIFORM3IVARBPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (APIENTRYP PFNGLUNIFORM4IVARBPROC)(GLint location, GLsizei count, const GLint * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVARBPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVARBPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVARBPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERFVARBPROC)(GLhandleARB obj, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVARBPROC)(GLhandleARB obj, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETINFOLOGARBPROC)(GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog); +typedef void (APIENTRYP PFNGLGETATTACHEDOBJECTSARBPROC)(GLhandleARB containerObj, GLsizei maxCount, GLsizei * count, GLhandleARB * obj); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONARBPROC)(GLhandleARB programObj, const GLcharARB * name); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMARBPROC)(GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei * length, GLint * size, GLenum * type, GLcharARB * name); +typedef void (APIENTRYP PFNGLGETUNIFORMFVARBPROC)(GLhandleARB programObj, GLint location, GLfloat * params); +typedef void (APIENTRYP PFNGLGETUNIFORMIVARBPROC)(GLhandleARB programObj, GLint location, GLint * params); +typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC)(GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * source); +#endif /* GL_ARB_shader_objects */ + +#ifndef GL_ARB_shader_precision +#define GL_ARB_shader_precision 1 +#endif /* GL_ARB_shader_precision */ + +#ifndef GL_ARB_shader_stencil_export +#define GL_ARB_shader_stencil_export 1 +#endif /* GL_ARB_shader_stencil_export */ + +#ifndef GL_ARB_shader_storage_buffer_object +#define GL_ARB_shader_storage_buffer_object 1 +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3 +#define GL_SHADER_STORAGE_BUFFER_START 0x90D4 +#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5 +#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6 +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9 +#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA +#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB +#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC +#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD +#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE +#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF +#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 +#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39 +#endif /* GL_ARB_shader_storage_buffer_object */ + +#ifndef GL_ARB_shader_subroutine +#define GL_ARB_shader_subroutine 1 +#define GL_ACTIVE_SUBROUTINES 0x8DE5 +#define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 +#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 +#define GL_MAX_SUBROUTINES 0x8DE7 +#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8 +#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A +#define GL_COMPATIBLE_SUBROUTINES 0x8E4B +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#endif /* GL_ARB_shader_subroutine */ + +#ifndef GL_ARB_shader_texture_image_samples +#define GL_ARB_shader_texture_image_samples 1 +#endif /* GL_ARB_shader_texture_image_samples */ + +#ifndef GL_ARB_shader_texture_lod +#define GL_ARB_shader_texture_lod 1 +#endif /* GL_ARB_shader_texture_lod */ + +#ifndef GL_ARB_shader_viewport_layer_array +#define GL_ARB_shader_viewport_layer_array 1 +#endif /* GL_ARB_shader_viewport_layer_array */ + +#ifndef GL_ARB_shading_language_100 +#define GL_ARB_shading_language_100 1 +#define GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C +#endif /* GL_ARB_shading_language_100 */ + +#ifndef GL_ARB_shading_language_420pack +#define GL_ARB_shading_language_420pack 1 +#endif /* GL_ARB_shading_language_420pack */ + +#ifndef GL_ARB_shading_language_include +#define GL_ARB_shading_language_include 1 +#define GL_SHADER_INCLUDE_ARB 0x8DAE +#define GL_NAMED_STRING_LENGTH_ARB 0x8DE9 +#define GL_NAMED_STRING_TYPE_ARB 0x8DEA +typedef void (APIENTRYP PFNGLNAMEDSTRINGARBPROC)(GLenum type, GLint namelen, const GLchar * name, GLint stringlen, const GLchar * string); +typedef void (APIENTRYP PFNGLDELETENAMEDSTRINGARBPROC)(GLint namelen, const GLchar * name); +typedef void (APIENTRYP PFNGLCOMPILESHADERINCLUDEARBPROC)(GLuint shader, GLsizei count, const GLchar *const* path, const GLint * length); +typedef GLboolean (APIENTRYP PFNGLISNAMEDSTRINGARBPROC)(GLint namelen, const GLchar * name); +typedef void (APIENTRYP PFNGLGETNAMEDSTRINGARBPROC)(GLint namelen, const GLchar * name, GLsizei bufSize, GLint * stringlen, GLchar * string); +typedef void (APIENTRYP PFNGLGETNAMEDSTRINGIVARBPROC)(GLint namelen, const GLchar * name, GLenum pname, GLint * params); +#endif /* GL_ARB_shading_language_include */ + +#ifndef GL_ARB_shading_language_packing +#define GL_ARB_shading_language_packing 1 +#endif /* GL_ARB_shading_language_packing */ + +#ifndef GL_ARB_shadow +#define GL_ARB_shadow 1 +#define GL_TEXTURE_COMPARE_MODE_ARB 0x884C +#define GL_TEXTURE_COMPARE_FUNC_ARB 0x884D +#define GL_COMPARE_R_TO_TEXTURE_ARB 0x884E +#endif /* GL_ARB_shadow */ + +#ifndef GL_ARB_shadow_ambient +#define GL_ARB_shadow_ambient 1 +#define GL_TEXTURE_COMPARE_FAIL_VALUE_ARB 0x80BF +#endif /* GL_ARB_shadow_ambient */ + +#ifndef GL_ARB_sparse_buffer +#define GL_ARB_sparse_buffer 1 +#define GL_SPARSE_STORAGE_BIT_ARB 0x0400 +#define GL_SPARSE_BUFFER_PAGE_SIZE_ARB 0x82F8 +typedef void (APIENTRYP PFNGLBUFFERPAGECOMMITMENTARBPROC)(GLenum target, GLintptr offset, GLsizeiptr size, GLboolean commit); +typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTEXTPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTARBPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +#endif /* GL_ARB_sparse_buffer */ + +#ifndef GL_ARB_sparse_texture +#define GL_ARB_sparse_texture 1 +#define GL_TEXTURE_SPARSE_ARB 0x91A6 +#define GL_VIRTUAL_PAGE_SIZE_INDEX_ARB 0x91A7 +#define GL_NUM_SPARSE_LEVELS_ARB 0x91AA +#define GL_NUM_VIRTUAL_PAGE_SIZES_ARB 0x91A8 +#define GL_VIRTUAL_PAGE_SIZE_X_ARB 0x9195 +#define GL_VIRTUAL_PAGE_SIZE_Y_ARB 0x9196 +#define GL_VIRTUAL_PAGE_SIZE_Z_ARB 0x9197 +#define GL_MAX_SPARSE_TEXTURE_SIZE_ARB 0x9198 +#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_ARB 0x9199 +#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_ARB 0x919A +#define GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_ARB 0x91A9 +typedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTARBPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +#endif /* GL_ARB_sparse_texture */ + +#ifndef GL_ARB_sparse_texture2 +#define GL_ARB_sparse_texture2 1 +#endif /* GL_ARB_sparse_texture2 */ + +#ifndef GL_ARB_sparse_texture_clamp +#define GL_ARB_sparse_texture_clamp 1 +#endif /* GL_ARB_sparse_texture_clamp */ + +#ifndef GL_ARB_spirv_extensions +#define GL_ARB_spirv_extensions 1 +#define GL_SPIR_V_EXTENSIONS 0x9553 +#define GL_NUM_SPIR_V_EXTENSIONS 0x9554 +#endif /* GL_ARB_spirv_extensions */ + +#ifndef GL_ARB_stencil_texturing +#define GL_ARB_stencil_texturing 1 +#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA +#endif /* GL_ARB_stencil_texturing */ + +#ifndef GL_ARB_sync +#define GL_ARB_sync 1 +#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 +#define GL_OBJECT_TYPE 0x9112 +#define GL_SYNC_CONDITION 0x9113 +#define GL_SYNC_STATUS 0x9114 +#define GL_SYNC_FLAGS 0x9115 +#define GL_SYNC_FENCE 0x9116 +#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 +#define GL_UNSIGNALED 0x9118 +#define GL_SIGNALED 0x9119 +#define GL_ALREADY_SIGNALED 0x911A +#define GL_TIMEOUT_EXPIRED 0x911B +#define GL_CONDITION_SATISFIED 0x911C +#define GL_WAIT_FAILED 0x911D +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFF +#endif /* GL_ARB_sync */ + +#ifndef GL_ARB_tessellation_shader +#define GL_ARB_tessellation_shader 1 +#define GL_PATCHES 0x000E +#define GL_PATCH_VERTICES 0x8E72 +#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 +#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 +#define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75 +#define GL_TESS_GEN_MODE 0x8E76 +#define GL_TESS_GEN_SPACING 0x8E77 +#define GL_TESS_GEN_VERTEX_ORDER 0x8E78 +#define GL_TESS_GEN_POINT_MODE 0x8E79 +#define GL_TRIANGLES 0x0004 +#define GL_ISOLINES 0x8E7A +#define GL_QUADS 0x0007 +#define GL_EQUAL 0x0202 +#define GL_FRACTIONAL_ODD 0x8E7B +#define GL_FRACTIONAL_EVEN 0x8E7C +#define GL_CCW 0x0901 +#define GL_CW 0x0900 +#define GL_MAX_PATCH_VERTICES 0x8E7D +#define GL_MAX_TESS_GEN_LEVEL 0x8E7E +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82 +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83 +#define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85 +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89 +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1 +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#define GL_TESS_CONTROL_SHADER 0x8E88 +#endif /* GL_ARB_tessellation_shader */ + +#ifndef GL_ARB_texture_barrier +#define GL_ARB_texture_barrier 1 +#endif /* GL_ARB_texture_barrier */ + +#ifndef GL_ARB_texture_border_clamp +#define GL_ARB_texture_border_clamp 1 +#define GL_CLAMP_TO_BORDER_ARB 0x812D +#endif /* GL_ARB_texture_border_clamp */ + +#ifndef GL_ARB_texture_buffer_object +#define GL_ARB_texture_buffer_object 1 +#define GL_TEXTURE_BUFFER_ARB 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE_ARB 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER_ARB 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT_ARB 0x8C2E +typedef void (APIENTRYP PFNGLTEXBUFFERARBPROC)(GLenum target, GLenum internalformat, GLuint buffer); +#endif /* GL_ARB_texture_buffer_object */ + +#ifndef GL_ARB_texture_buffer_object_rgb32 +#define GL_ARB_texture_buffer_object_rgb32 1 +#define GL_RGB32F 0x8815 +#define GL_RGB32UI 0x8D71 +#define GL_RGB32I 0x8D83 +#endif /* GL_ARB_texture_buffer_object_rgb32 */ + +#ifndef GL_ARB_texture_buffer_range +#define GL_ARB_texture_buffer_range 1 +#define GL_TEXTURE_BUFFER_OFFSET 0x919D +#define GL_TEXTURE_BUFFER_SIZE 0x919E +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F +#endif /* GL_ARB_texture_buffer_range */ + +#ifndef GL_ARB_texture_compression +#define GL_ARB_texture_compression 1 +#define GL_COMPRESSED_ALPHA_ARB 0x84E9 +#define GL_COMPRESSED_LUMINANCE_ARB 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB +#define GL_COMPRESSED_INTENSITY_ARB 0x84EC +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGBA_ARB 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT_ARB 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB 0x86A0 +#define GL_TEXTURE_COMPRESSED_ARB 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3 +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DARBPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DARBPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DARBPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void * data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void * data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void * data); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEARBPROC)(GLenum target, GLint level, void * img); +#endif /* GL_ARB_texture_compression */ + +#ifndef GL_ARB_texture_compression_bptc +#define GL_ARB_texture_compression_bptc 1 +#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F +#endif /* GL_ARB_texture_compression_bptc */ + +#ifndef GL_ARB_texture_compression_rgtc +#define GL_ARB_texture_compression_rgtc 1 +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#endif /* GL_ARB_texture_compression_rgtc */ + +#ifndef GL_ARB_texture_cube_map +#define GL_ARB_texture_cube_map 1 +#define GL_NORMAL_MAP_ARB 0x8511 +#define GL_REFLECTION_MAP_ARB 0x8512 +#define GL_TEXTURE_CUBE_MAP_ARB 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARB 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARB 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB 0x851C +#endif /* GL_ARB_texture_cube_map */ + +#ifndef GL_ARB_texture_cube_map_array +#define GL_ARB_texture_cube_map_array 1 +#define GL_TEXTURE_CUBE_MAP_ARRAY_ARB 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F +#endif /* GL_ARB_texture_cube_map_array */ + +#ifndef GL_ARB_texture_env_add +#define GL_ARB_texture_env_add 1 +#endif /* GL_ARB_texture_env_add */ + +#ifndef GL_ARB_texture_env_combine +#define GL_ARB_texture_env_combine 1 +#define GL_COMBINE_ARB 0x8570 +#define GL_COMBINE_RGB_ARB 0x8571 +#define GL_COMBINE_ALPHA_ARB 0x8572 +#define GL_SOURCE0_RGB_ARB 0x8580 +#define GL_SOURCE1_RGB_ARB 0x8581 +#define GL_SOURCE2_RGB_ARB 0x8582 +#define GL_SOURCE0_ALPHA_ARB 0x8588 +#define GL_SOURCE1_ALPHA_ARB 0x8589 +#define GL_SOURCE2_ALPHA_ARB 0x858A +#define GL_OPERAND0_RGB_ARB 0x8590 +#define GL_OPERAND1_RGB_ARB 0x8591 +#define GL_OPERAND2_RGB_ARB 0x8592 +#define GL_OPERAND0_ALPHA_ARB 0x8598 +#define GL_OPERAND1_ALPHA_ARB 0x8599 +#define GL_OPERAND2_ALPHA_ARB 0x859A +#define GL_RGB_SCALE_ARB 0x8573 +#define GL_ADD_SIGNED_ARB 0x8574 +#define GL_INTERPOLATE_ARB 0x8575 +#define GL_SUBTRACT_ARB 0x84E7 +#define GL_CONSTANT_ARB 0x8576 +#define GL_PRIMARY_COLOR_ARB 0x8577 +#define GL_PREVIOUS_ARB 0x8578 +#endif /* GL_ARB_texture_env_combine */ + +#ifndef GL_ARB_texture_env_crossbar +#define GL_ARB_texture_env_crossbar 1 +#endif /* GL_ARB_texture_env_crossbar */ + +#ifndef GL_ARB_texture_env_dot3 +#define GL_ARB_texture_env_dot3 1 +#define GL_DOT3_RGB_ARB 0x86AE +#define GL_DOT3_RGBA_ARB 0x86AF +#endif /* GL_ARB_texture_env_dot3 */ + +#ifndef GL_ARB_texture_filter_anisotropic +#define GL_ARB_texture_filter_anisotropic 1 +#define GL_TEXTURE_MAX_ANISOTROPY 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF +#endif /* GL_ARB_texture_filter_anisotropic */ + +#ifndef GL_ARB_texture_filter_minmax +#define GL_ARB_texture_filter_minmax 1 +#define GL_TEXTURE_REDUCTION_MODE_ARB 0x9366 +#define GL_WEIGHTED_AVERAGE_ARB 0x9367 +#endif /* GL_ARB_texture_filter_minmax */ + +#ifndef GL_ARB_texture_float +#define GL_ARB_texture_float 1 +#define GL_TEXTURE_RED_TYPE_ARB 0x8C10 +#define GL_TEXTURE_GREEN_TYPE_ARB 0x8C11 +#define GL_TEXTURE_BLUE_TYPE_ARB 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE_ARB 0x8C13 +#define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14 +#define GL_TEXTURE_INTENSITY_TYPE_ARB 0x8C15 +#define GL_TEXTURE_DEPTH_TYPE_ARB 0x8C16 +#define GL_UNSIGNED_NORMALIZED_ARB 0x8C17 +#define GL_RGBA32F_ARB 0x8814 +#define GL_RGB32F_ARB 0x8815 +#define GL_ALPHA32F_ARB 0x8816 +#define GL_INTENSITY32F_ARB 0x8817 +#define GL_LUMINANCE32F_ARB 0x8818 +#define GL_LUMINANCE_ALPHA32F_ARB 0x8819 +#define GL_RGBA16F_ARB 0x881A +#define GL_RGB16F_ARB 0x881B +#define GL_ALPHA16F_ARB 0x881C +#define GL_INTENSITY16F_ARB 0x881D +#define GL_LUMINANCE16F_ARB 0x881E +#define GL_LUMINANCE_ALPHA16F_ARB 0x881F +#endif /* GL_ARB_texture_float */ + +#ifndef GL_ARB_texture_gather +#define GL_ARB_texture_gather 1 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5F +#define GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB 0x8F9F +#endif /* GL_ARB_texture_gather */ + +#ifndef GL_ARB_texture_mirror_clamp_to_edge +#define GL_ARB_texture_mirror_clamp_to_edge 1 +#define GL_MIRROR_CLAMP_TO_EDGE 0x8743 +#endif /* GL_ARB_texture_mirror_clamp_to_edge */ + +#ifndef GL_ARB_texture_mirrored_repeat +#define GL_ARB_texture_mirrored_repeat 1 +#define GL_MIRRORED_REPEAT_ARB 0x8370 +#endif /* GL_ARB_texture_mirrored_repeat */ + +#ifndef GL_ARB_texture_multisample +#define GL_ARB_texture_multisample 1 +#define GL_SAMPLE_POSITION 0x8E50 +#define GL_SAMPLE_MASK 0x8E51 +#define GL_SAMPLE_MASK_VALUE 0x8E52 +#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 +#define GL_TEXTURE_SAMPLES 0x9106 +#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E +#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F +#define GL_MAX_INTEGER_SAMPLES 0x9110 +#endif /* GL_ARB_texture_multisample */ + +#ifndef GL_ARB_texture_non_power_of_two +#define GL_ARB_texture_non_power_of_two 1 +#endif /* GL_ARB_texture_non_power_of_two */ + +#ifndef GL_ARB_texture_query_levels +#define GL_ARB_texture_query_levels 1 +#endif /* GL_ARB_texture_query_levels */ + +#ifndef GL_ARB_texture_query_lod +#define GL_ARB_texture_query_lod 1 +#endif /* GL_ARB_texture_query_lod */ + +#ifndef GL_ARB_texture_rectangle +#define GL_ARB_texture_rectangle 1 +#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_ARB 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_ARB 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 +#endif /* GL_ARB_texture_rectangle */ + +#ifndef GL_ARB_texture_rg +#define GL_ARB_texture_rg 1 +#define GL_RG 0x8227 +#define GL_RG_INTEGER 0x8228 +#define GL_R8 0x8229 +#define GL_R16 0x822A +#define GL_RG8 0x822B +#define GL_RG16 0x822C +#define GL_R16F 0x822D +#define GL_R32F 0x822E +#define GL_RG16F 0x822F +#define GL_RG32F 0x8230 +#define GL_R8I 0x8231 +#define GL_R8UI 0x8232 +#define GL_R16I 0x8233 +#define GL_R16UI 0x8234 +#define GL_R32I 0x8235 +#define GL_R32UI 0x8236 +#define GL_RG8I 0x8237 +#define GL_RG8UI 0x8238 +#define GL_RG16I 0x8239 +#define GL_RG16UI 0x823A +#define GL_RG32I 0x823B +#define GL_RG32UI 0x823C +#endif /* GL_ARB_texture_rg */ + +#ifndef GL_ARB_texture_rgb10_a2ui +#define GL_ARB_texture_rgb10_a2ui 1 +#define GL_RGB10_A2UI 0x906F +#endif /* GL_ARB_texture_rgb10_a2ui */ + +#ifndef GL_ARB_texture_stencil8 +#define GL_ARB_texture_stencil8 1 +#define GL_STENCIL_INDEX 0x1901 +#define GL_STENCIL_INDEX8 0x8D48 +#endif /* GL_ARB_texture_stencil8 */ + +#ifndef GL_ARB_texture_storage +#define GL_ARB_texture_storage 1 +#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F +#endif /* GL_ARB_texture_storage */ + +#ifndef GL_ARB_texture_storage_multisample +#define GL_ARB_texture_storage_multisample 1 +#endif /* GL_ARB_texture_storage_multisample */ + +#ifndef GL_ARB_texture_swizzle +#define GL_ARB_texture_swizzle 1 +#define GL_TEXTURE_SWIZZLE_R 0x8E42 +#define GL_TEXTURE_SWIZZLE_G 0x8E43 +#define GL_TEXTURE_SWIZZLE_B 0x8E44 +#define GL_TEXTURE_SWIZZLE_A 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#endif /* GL_ARB_texture_swizzle */ + +#ifndef GL_ARB_texture_view +#define GL_ARB_texture_view 1 +#define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB +#define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC +#define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD +#define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE +#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF +#endif /* GL_ARB_texture_view */ + +#ifndef GL_ARB_timer_query +#define GL_ARB_timer_query 1 +#define GL_TIME_ELAPSED 0x88BF +#define GL_TIMESTAMP 0x8E28 +#endif /* GL_ARB_timer_query */ + +#ifndef GL_ARB_transform_feedback2 +#define GL_ARB_transform_feedback2 1 +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 +#endif /* GL_ARB_transform_feedback2 */ + +#ifndef GL_ARB_transform_feedback3 +#define GL_ARB_transform_feedback3 1 +#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 +#define GL_MAX_VERTEX_STREAMS 0x8E71 +#endif /* GL_ARB_transform_feedback3 */ + +#ifndef GL_ARB_transform_feedback_instanced +#define GL_ARB_transform_feedback_instanced 1 +#endif /* GL_ARB_transform_feedback_instanced */ + +#ifndef GL_ARB_transform_feedback_overflow_query +#define GL_ARB_transform_feedback_overflow_query 1 +#define GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB 0x82EC +#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB 0x82ED +#endif /* GL_ARB_transform_feedback_overflow_query */ + +#ifndef GL_ARB_transpose_matrix +#define GL_ARB_transpose_matrix 1 +#define GL_TRANSPOSE_MODELVIEW_MATRIX_ARB 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX_ARB 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX_ARB 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX_ARB 0x84E6 +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFARBPROC)(const GLfloat * m); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDARBPROC)(const GLdouble * m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFARBPROC)(const GLfloat * m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDARBPROC)(const GLdouble * m); +#endif /* GL_ARB_transpose_matrix */ + +#ifndef GL_ARB_uniform_buffer_object +#define GL_ARB_uniform_buffer_object 1 +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_INVALID_INDEX 0xFFFFFFFF +#endif /* GL_ARB_uniform_buffer_object */ + +#ifndef GL_ARB_vertex_array_bgra +#define GL_ARB_vertex_array_bgra 1 +#define GL_BGRA 0x80E1 +#endif /* GL_ARB_vertex_array_bgra */ + +#ifndef GL_ARB_vertex_array_object +#define GL_ARB_vertex_array_object 1 +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +#endif /* GL_ARB_vertex_array_object */ + +#ifndef GL_ARB_vertex_attrib_64bit +#define GL_ARB_vertex_attrib_64bit 1 +#define GL_RGB32I 0x8D83 +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E +#endif /* GL_ARB_vertex_attrib_64bit */ + +#ifndef GL_ARB_vertex_attrib_binding +#define GL_ARB_vertex_attrib_binding 1 +#define GL_VERTEX_ATTRIB_BINDING 0x82D4 +#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5 +#define GL_VERTEX_BINDING_DIVISOR 0x82D6 +#define GL_VERTEX_BINDING_OFFSET 0x82D7 +#define GL_VERTEX_BINDING_STRIDE 0x82D8 +#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9 +#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA +#endif /* GL_ARB_vertex_attrib_binding */ + +#ifndef GL_ARB_vertex_blend +#define GL_ARB_vertex_blend 1 +#define GL_MAX_VERTEX_UNITS_ARB 0x86A4 +#define GL_ACTIVE_VERTEX_UNITS_ARB 0x86A5 +#define GL_WEIGHT_SUM_UNITY_ARB 0x86A6 +#define GL_VERTEX_BLEND_ARB 0x86A7 +#define GL_CURRENT_WEIGHT_ARB 0x86A8 +#define GL_WEIGHT_ARRAY_TYPE_ARB 0x86A9 +#define GL_WEIGHT_ARRAY_STRIDE_ARB 0x86AA +#define GL_WEIGHT_ARRAY_SIZE_ARB 0x86AB +#define GL_WEIGHT_ARRAY_POINTER_ARB 0x86AC +#define GL_WEIGHT_ARRAY_ARB 0x86AD +#define GL_MODELVIEW0_ARB 0x1700 +#define GL_MODELVIEW1_ARB 0x850A +#define GL_MODELVIEW2_ARB 0x8722 +#define GL_MODELVIEW3_ARB 0x8723 +#define GL_MODELVIEW4_ARB 0x8724 +#define GL_MODELVIEW5_ARB 0x8725 +#define GL_MODELVIEW6_ARB 0x8726 +#define GL_MODELVIEW7_ARB 0x8727 +#define GL_MODELVIEW8_ARB 0x8728 +#define GL_MODELVIEW9_ARB 0x8729 +#define GL_MODELVIEW10_ARB 0x872A +#define GL_MODELVIEW11_ARB 0x872B +#define GL_MODELVIEW12_ARB 0x872C +#define GL_MODELVIEW13_ARB 0x872D +#define GL_MODELVIEW14_ARB 0x872E +#define GL_MODELVIEW15_ARB 0x872F +#define GL_MODELVIEW16_ARB 0x8730 +#define GL_MODELVIEW17_ARB 0x8731 +#define GL_MODELVIEW18_ARB 0x8732 +#define GL_MODELVIEW19_ARB 0x8733 +#define GL_MODELVIEW20_ARB 0x8734 +#define GL_MODELVIEW21_ARB 0x8735 +#define GL_MODELVIEW22_ARB 0x8736 +#define GL_MODELVIEW23_ARB 0x8737 +#define GL_MODELVIEW24_ARB 0x8738 +#define GL_MODELVIEW25_ARB 0x8739 +#define GL_MODELVIEW26_ARB 0x873A +#define GL_MODELVIEW27_ARB 0x873B +#define GL_MODELVIEW28_ARB 0x873C +#define GL_MODELVIEW29_ARB 0x873D +#define GL_MODELVIEW30_ARB 0x873E +#define GL_MODELVIEW31_ARB 0x873F +typedef void (APIENTRYP PFNGLWEIGHTBVARBPROC)(GLint size, const GLbyte * weights); +typedef void (APIENTRYP PFNGLWEIGHTSVARBPROC)(GLint size, const GLshort * weights); +typedef void (APIENTRYP PFNGLWEIGHTIVARBPROC)(GLint size, const GLint * weights); +typedef void (APIENTRYP PFNGLWEIGHTFVARBPROC)(GLint size, const GLfloat * weights); +typedef void (APIENTRYP PFNGLWEIGHTDVARBPROC)(GLint size, const GLdouble * weights); +typedef void (APIENTRYP PFNGLWEIGHTUBVARBPROC)(GLint size, const GLubyte * weights); +typedef void (APIENTRYP PFNGLWEIGHTUSVARBPROC)(GLint size, const GLushort * weights); +typedef void (APIENTRYP PFNGLWEIGHTUIVARBPROC)(GLint size, const GLuint * weights); +typedef void (APIENTRYP PFNGLWEIGHTPOINTERARBPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); +typedef void (APIENTRYP PFNGLVERTEXBLENDARBPROC)(GLint count); +#endif /* GL_ARB_vertex_blend */ + +#ifndef GL_ARB_vertex_buffer_object +#define GL_ARB_vertex_buffer_object 1 +typedef khronos_ssize_t GLsizeiptrARB; +typedef khronos_intptr_t GLintptrARB; +#define GL_BUFFER_SIZE_ARB 0x8764 +#define GL_BUFFER_USAGE_ARB 0x8765 +#define GL_ARRAY_BUFFER_ARB 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893 +#define GL_ARRAY_BUFFER_BINDING_ARB 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895 +#define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F +#define GL_READ_ONLY_ARB 0x88B8 +#define GL_WRITE_ONLY_ARB 0x88B9 +#define GL_READ_WRITE_ARB 0x88BA +#define GL_BUFFER_ACCESS_ARB 0x88BB +#define GL_BUFFER_MAPPED_ARB 0x88BC +#define GL_BUFFER_MAP_POINTER_ARB 0x88BD +#define GL_STREAM_DRAW_ARB 0x88E0 +#define GL_STREAM_READ_ARB 0x88E1 +#define GL_STREAM_COPY_ARB 0x88E2 +#define GL_STATIC_DRAW_ARB 0x88E4 +#define GL_STATIC_READ_ARB 0x88E5 +#define GL_STATIC_COPY_ARB 0x88E6 +#define GL_DYNAMIC_DRAW_ARB 0x88E8 +#define GL_DYNAMIC_READ_ARB 0x88E9 +#define GL_DYNAMIC_COPY_ARB 0x88EA +typedef void (APIENTRYP PFNGLBINDBUFFERARBPROC)(GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSARBPROC)(GLsizei n, const GLuint * buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSARBPROC)(GLsizei n, GLuint * buffers); +typedef GLboolean (APIENTRYP PFNGLISBUFFERARBPROC)(GLuint buffer); +typedef void (APIENTRYP PFNGLBUFFERDATAARBPROC)(GLenum target, GLsizeiptrARB size, const void * data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAARBPROC)(GLenum target, GLintptrARB offset, GLsizeiptrARB size, const void * data); +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAARBPROC)(GLenum target, GLintptrARB offset, GLsizeiptrARB size, void * data); +typedef void * (APIENTRYP PFNGLMAPBUFFERARBPROC)(GLenum target, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERARBPROC)(GLenum target); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVARBPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVARBPROC)(GLenum target, GLenum pname, void ** params); +#endif /* GL_ARB_vertex_buffer_object */ + +#ifndef GL_ARB_vertex_program +#define GL_ARB_vertex_program 1 +#define GL_COLOR_SUM_ARB 0x8458 +#define GL_VERTEX_PROGRAM_ARB 0x8620 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB_ARB 0x8626 +#define GL_PROGRAM_LENGTH_ARB 0x8627 +#define GL_PROGRAM_STRING_ARB 0x8628 +#define GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB 0x862E +#define GL_MAX_PROGRAM_MATRICES_ARB 0x862F +#define GL_CURRENT_MATRIX_STACK_DEPTH_ARB 0x8640 +#define GL_CURRENT_MATRIX_ARB 0x8641 +#define GL_VERTEX_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_ARB 0x8643 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB 0x8645 +#define GL_PROGRAM_ERROR_POSITION_ARB 0x864B +#define GL_PROGRAM_BINDING_ARB 0x8677 +#define GL_MAX_VERTEX_ATTRIBS_ARB 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB 0x886A +#define GL_PROGRAM_ERROR_STRING_ARB 0x8874 +#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875 +#define GL_PROGRAM_FORMAT_ARB 0x8876 +#define GL_PROGRAM_INSTRUCTIONS_ARB 0x88A0 +#define GL_MAX_PROGRAM_INSTRUCTIONS_ARB 0x88A1 +#define GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A2 +#define GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A3 +#define GL_PROGRAM_TEMPORARIES_ARB 0x88A4 +#define GL_MAX_PROGRAM_TEMPORARIES_ARB 0x88A5 +#define GL_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A6 +#define GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A7 +#define GL_PROGRAM_PARAMETERS_ARB 0x88A8 +#define GL_MAX_PROGRAM_PARAMETERS_ARB 0x88A9 +#define GL_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AA +#define GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AB +#define GL_PROGRAM_ATTRIBS_ARB 0x88AC +#define GL_MAX_PROGRAM_ATTRIBS_ARB 0x88AD +#define GL_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AE +#define GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AF +#define GL_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B0 +#define GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B1 +#define GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B2 +#define GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B3 +#define GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB 0x88B4 +#define GL_MAX_PROGRAM_ENV_PARAMETERS_ARB 0x88B5 +#define GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB 0x88B6 +#define GL_TRANSPOSE_CURRENT_MATRIX_ARB 0x88B7 +#define GL_MATRIX0_ARB 0x88C0 +#define GL_MATRIX1_ARB 0x88C1 +#define GL_MATRIX2_ARB 0x88C2 +#define GL_MATRIX3_ARB 0x88C3 +#define GL_MATRIX4_ARB 0x88C4 +#define GL_MATRIX5_ARB 0x88C5 +#define GL_MATRIX6_ARB 0x88C6 +#define GL_MATRIX7_ARB 0x88C7 +#define GL_MATRIX8_ARB 0x88C8 +#define GL_MATRIX9_ARB 0x88C9 +#define GL_MATRIX10_ARB 0x88CA +#define GL_MATRIX11_ARB 0x88CB +#define GL_MATRIX12_ARB 0x88CC +#define GL_MATRIX13_ARB 0x88CD +#define GL_MATRIX14_ARB 0x88CE +#define GL_MATRIX15_ARB 0x88CF +#define GL_MATRIX16_ARB 0x88D0 +#define GL_MATRIX17_ARB 0x88D1 +#define GL_MATRIX18_ARB 0x88D2 +#define GL_MATRIX19_ARB 0x88D3 +#define GL_MATRIX20_ARB 0x88D4 +#define GL_MATRIX21_ARB 0x88D5 +#define GL_MATRIX22_ARB 0x88D6 +#define GL_MATRIX23_ARB 0x88D7 +#define GL_MATRIX24_ARB 0x88D8 +#define GL_MATRIX25_ARB 0x88D9 +#define GL_MATRIX26_ARB 0x88DA +#define GL_MATRIX27_ARB 0x88DB +#define GL_MATRIX28_ARB 0x88DC +#define GL_MATRIX29_ARB 0x88DD +#define GL_MATRIX30_ARB 0x88DE +#define GL_MATRIX31_ARB 0x88DF +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DARBPROC)(GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVARBPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FARBPROC)(GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVARBPROC)(GLuint index, const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SARBPROC)(GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVARBPROC)(GLuint index, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DARBPROC)(GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVARBPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FARBPROC)(GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVARBPROC)(GLuint index, const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SARBPROC)(GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVARBPROC)(GLuint index, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DARBPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVARBPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FARBPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVARBPROC)(GLuint index, const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SARBPROC)(GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVARBPROC)(GLuint index, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVARBPROC)(GLuint index, const GLbyte * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVARBPROC)(GLuint index, const GLint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVARBPROC)(GLuint index, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBARBPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVARBPROC)(GLuint index, const GLubyte * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVARBPROC)(GLuint index, const GLuint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVARBPROC)(GLuint index, const GLushort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVARBPROC)(GLuint index, const GLbyte * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DARBPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVARBPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FARBPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVARBPROC)(GLuint index, const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVARBPROC)(GLuint index, const GLint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SARBPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVARBPROC)(GLuint index, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVARBPROC)(GLuint index, const GLubyte * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVARBPROC)(GLuint index, const GLuint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVARBPROC)(GLuint index, const GLushort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERARBPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYARBPROC)(GLuint index); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYARBPROC)(GLuint index); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVARBPROC)(GLuint index, GLenum pname, GLdouble * params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVARBPROC)(GLuint index, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVARBPROC)(GLuint index, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVARBPROC)(GLuint index, GLenum pname, void ** pointer); +#endif /* GL_ARB_vertex_program */ + +#ifndef GL_ARB_vertex_shader +#define GL_ARB_vertex_shader 1 +#define GL_VERTEX_SHADER_ARB 0x8B31 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB 0x8B4A +#define GL_MAX_VARYING_FLOATS_ARB 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB 0x8B4D +#define GL_OBJECT_ACTIVE_ATTRIBUTES_ARB 0x8B89 +#define GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB 0x8B8A +#define GL_MAX_VERTEX_ATTRIBS_ARB 0x8869 +#define GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872 +#define GL_MAX_TEXTURE_COORDS_ARB 0x8871 +#define GL_VERTEX_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_ARB 0x8643 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB 0x8625 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB 0x886A +#define GL_CURRENT_VERTEX_ATTRIB_ARB 0x8626 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB 0x8645 +#define GL_FLOAT 0x1406 +#define GL_FLOAT_VEC2_ARB 0x8B50 +#define GL_FLOAT_VEC3_ARB 0x8B51 +#define GL_FLOAT_VEC4_ARB 0x8B52 +#define GL_FLOAT_MAT2_ARB 0x8B5A +#define GL_FLOAT_MAT3_ARB 0x8B5B +#define GL_FLOAT_MAT4_ARB 0x8B5C +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONARBPROC)(GLhandleARB programObj, GLuint index, const GLcharARB * name); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBARBPROC)(GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei * length, GLint * size, GLenum * type, GLcharARB * name); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONARBPROC)(GLhandleARB programObj, const GLcharARB * name); +#endif /* GL_ARB_vertex_shader */ + +#ifndef GL_ARB_vertex_type_10f_11f_11f_rev +#define GL_ARB_vertex_type_10f_11f_11f_rev 1 +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#endif /* GL_ARB_vertex_type_10f_11f_11f_rev */ + +#ifndef GL_ARB_vertex_type_2_10_10_10_rev +#define GL_ARB_vertex_type_2_10_10_10_rev 1 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_INT_2_10_10_10_REV 0x8D9F +#endif /* GL_ARB_vertex_type_2_10_10_10_rev */ + +#ifndef GL_ARB_viewport_array +#define GL_ARB_viewport_array 1 +#define GL_SCISSOR_BOX 0x0C10 +#define GL_VIEWPORT 0x0BA2 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_MAX_VIEWPORTS 0x825B +#define GL_VIEWPORT_SUBPIXEL_BITS 0x825C +#define GL_VIEWPORT_BOUNDS_RANGE 0x825D +#define GL_LAYER_PROVOKING_VERTEX 0x825E +#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F +#define GL_UNDEFINED_VERTEX 0x8260 +#define GL_FIRST_VERTEX_CONVENTION 0x8E4D +#define GL_LAST_VERTEX_CONVENTION 0x8E4E +#define GL_PROVOKING_VERTEX 0x8E4F +typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYDVNVPROC)(GLuint first, GLsizei count, const GLdouble * v); +typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDDNVPROC)(GLuint index, GLdouble n, GLdouble f); +#endif /* GL_ARB_viewport_array */ + +#ifndef GL_ARB_window_pos +#define GL_ARB_window_pos 1 +typedef void (APIENTRYP PFNGLWINDOWPOS2DARBPROC)(GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVARBPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FARBPROC)(GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVARBPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IARBPROC)(GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVARBPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SARBPROC)(GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVARBPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DARBPROC)(GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVARBPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FARBPROC)(GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVARBPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IARBPROC)(GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVARBPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SARBPROC)(GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVARBPROC)(const GLshort * v); +#endif /* GL_ARB_window_pos */ + +#ifndef GL_ATI_draw_buffers +#define GL_ATI_draw_buffers 1 +#define GL_MAX_DRAW_BUFFERS_ATI 0x8824 +#define GL_DRAW_BUFFER0_ATI 0x8825 +#define GL_DRAW_BUFFER1_ATI 0x8826 +#define GL_DRAW_BUFFER2_ATI 0x8827 +#define GL_DRAW_BUFFER3_ATI 0x8828 +#define GL_DRAW_BUFFER4_ATI 0x8829 +#define GL_DRAW_BUFFER5_ATI 0x882A +#define GL_DRAW_BUFFER6_ATI 0x882B +#define GL_DRAW_BUFFER7_ATI 0x882C +#define GL_DRAW_BUFFER8_ATI 0x882D +#define GL_DRAW_BUFFER9_ATI 0x882E +#define GL_DRAW_BUFFER10_ATI 0x882F +#define GL_DRAW_BUFFER11_ATI 0x8830 +#define GL_DRAW_BUFFER12_ATI 0x8831 +#define GL_DRAW_BUFFER13_ATI 0x8832 +#define GL_DRAW_BUFFER14_ATI 0x8833 +#define GL_DRAW_BUFFER15_ATI 0x8834 +typedef void (APIENTRYP PFNGLDRAWBUFFERSATIPROC)(GLsizei n, const GLenum * bufs); +#endif /* GL_ATI_draw_buffers */ + +#ifndef GL_ATI_element_array +#define GL_ATI_element_array 1 +#define GL_ELEMENT_ARRAY_ATI 0x8768 +#define GL_ELEMENT_ARRAY_TYPE_ATI 0x8769 +#define GL_ELEMENT_ARRAY_POINTER_ATI 0x876A +typedef void (APIENTRYP PFNGLELEMENTPOINTERATIPROC)(GLenum type, const void * pointer); +typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYATIPROC)(GLenum mode, GLsizei count); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYATIPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count); +#endif /* GL_ATI_element_array */ + +#ifndef GL_ATI_envmap_bumpmap +#define GL_ATI_envmap_bumpmap 1 +#define GL_BUMP_ROT_MATRIX_ATI 0x8775 +#define GL_BUMP_ROT_MATRIX_SIZE_ATI 0x8776 +#define GL_BUMP_NUM_TEX_UNITS_ATI 0x8777 +#define GL_BUMP_TEX_UNITS_ATI 0x8778 +#define GL_DUDV_ATI 0x8779 +#define GL_DU8DV8_ATI 0x877A +#define GL_BUMP_ENVMAP_ATI 0x877B +#define GL_BUMP_TARGET_ATI 0x877C +typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERIVATIPROC)(GLenum pname, const GLint * param); +typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERFVATIPROC)(GLenum pname, const GLfloat * param); +typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERIVATIPROC)(GLenum pname, GLint * param); +typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERFVATIPROC)(GLenum pname, GLfloat * param); +#endif /* GL_ATI_envmap_bumpmap */ + +#ifndef GL_ATI_fragment_shader +#define GL_ATI_fragment_shader 1 +#define GL_FRAGMENT_SHADER_ATI 0x8920 +#define GL_REG_0_ATI 0x8921 +#define GL_REG_1_ATI 0x8922 +#define GL_REG_2_ATI 0x8923 +#define GL_REG_3_ATI 0x8924 +#define GL_REG_4_ATI 0x8925 +#define GL_REG_5_ATI 0x8926 +#define GL_REG_6_ATI 0x8927 +#define GL_REG_7_ATI 0x8928 +#define GL_REG_8_ATI 0x8929 +#define GL_REG_9_ATI 0x892A +#define GL_REG_10_ATI 0x892B +#define GL_REG_11_ATI 0x892C +#define GL_REG_12_ATI 0x892D +#define GL_REG_13_ATI 0x892E +#define GL_REG_14_ATI 0x892F +#define GL_REG_15_ATI 0x8930 +#define GL_REG_16_ATI 0x8931 +#define GL_REG_17_ATI 0x8932 +#define GL_REG_18_ATI 0x8933 +#define GL_REG_19_ATI 0x8934 +#define GL_REG_20_ATI 0x8935 +#define GL_REG_21_ATI 0x8936 +#define GL_REG_22_ATI 0x8937 +#define GL_REG_23_ATI 0x8938 +#define GL_REG_24_ATI 0x8939 +#define GL_REG_25_ATI 0x893A +#define GL_REG_26_ATI 0x893B +#define GL_REG_27_ATI 0x893C +#define GL_REG_28_ATI 0x893D +#define GL_REG_29_ATI 0x893E +#define GL_REG_30_ATI 0x893F +#define GL_REG_31_ATI 0x8940 +#define GL_CON_0_ATI 0x8941 +#define GL_CON_1_ATI 0x8942 +#define GL_CON_2_ATI 0x8943 +#define GL_CON_3_ATI 0x8944 +#define GL_CON_4_ATI 0x8945 +#define GL_CON_5_ATI 0x8946 +#define GL_CON_6_ATI 0x8947 +#define GL_CON_7_ATI 0x8948 +#define GL_CON_8_ATI 0x8949 +#define GL_CON_9_ATI 0x894A +#define GL_CON_10_ATI 0x894B +#define GL_CON_11_ATI 0x894C +#define GL_CON_12_ATI 0x894D +#define GL_CON_13_ATI 0x894E +#define GL_CON_14_ATI 0x894F +#define GL_CON_15_ATI 0x8950 +#define GL_CON_16_ATI 0x8951 +#define GL_CON_17_ATI 0x8952 +#define GL_CON_18_ATI 0x8953 +#define GL_CON_19_ATI 0x8954 +#define GL_CON_20_ATI 0x8955 +#define GL_CON_21_ATI 0x8956 +#define GL_CON_22_ATI 0x8957 +#define GL_CON_23_ATI 0x8958 +#define GL_CON_24_ATI 0x8959 +#define GL_CON_25_ATI 0x895A +#define GL_CON_26_ATI 0x895B +#define GL_CON_27_ATI 0x895C +#define GL_CON_28_ATI 0x895D +#define GL_CON_29_ATI 0x895E +#define GL_CON_30_ATI 0x895F +#define GL_CON_31_ATI 0x8960 +#define GL_MOV_ATI 0x8961 +#define GL_ADD_ATI 0x8963 +#define GL_MUL_ATI 0x8964 +#define GL_SUB_ATI 0x8965 +#define GL_DOT3_ATI 0x8966 +#define GL_DOT4_ATI 0x8967 +#define GL_MAD_ATI 0x8968 +#define GL_LERP_ATI 0x8969 +#define GL_CND_ATI 0x896A +#define GL_CND0_ATI 0x896B +#define GL_DOT2_ADD_ATI 0x896C +#define GL_SECONDARY_INTERPOLATOR_ATI 0x896D +#define GL_NUM_FRAGMENT_REGISTERS_ATI 0x896E +#define GL_NUM_FRAGMENT_CONSTANTS_ATI 0x896F +#define GL_NUM_PASSES_ATI 0x8970 +#define GL_NUM_INSTRUCTIONS_PER_PASS_ATI 0x8971 +#define GL_NUM_INSTRUCTIONS_TOTAL_ATI 0x8972 +#define GL_NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI 0x8973 +#define GL_NUM_LOOPBACK_COMPONENTS_ATI 0x8974 +#define GL_COLOR_ALPHA_PAIRING_ATI 0x8975 +#define GL_SWIZZLE_STR_ATI 0x8976 +#define GL_SWIZZLE_STQ_ATI 0x8977 +#define GL_SWIZZLE_STR_DR_ATI 0x8978 +#define GL_SWIZZLE_STQ_DQ_ATI 0x8979 +#define GL_SWIZZLE_STRQ_ATI 0x897A +#define GL_SWIZZLE_STRQ_DQ_ATI 0x897B +#define GL_RED_BIT_ATI 0x00000001 +#define GL_GREEN_BIT_ATI 0x00000002 +#define GL_BLUE_BIT_ATI 0x00000004 +#define GL_2X_BIT_ATI 0x00000001 +#define GL_4X_BIT_ATI 0x00000002 +#define GL_8X_BIT_ATI 0x00000004 +#define GL_HALF_BIT_ATI 0x00000008 +#define GL_QUARTER_BIT_ATI 0x00000010 +#define GL_EIGHTH_BIT_ATI 0x00000020 +#define GL_SATURATE_BIT_ATI 0x00000040 +#define GL_COMP_BIT_ATI 0x00000002 +#define GL_NEGATE_BIT_ATI 0x00000004 +#define GL_BIAS_BIT_ATI 0x00000008 +typedef GLuint (APIENTRYP PFNGLGENFRAGMENTSHADERSATIPROC)(GLuint range); +typedef void (APIENTRYP PFNGLBINDFRAGMENTSHADERATIPROC)(GLuint id); +typedef void (APIENTRYP PFNGLDELETEFRAGMENTSHADERATIPROC)(GLuint id); +typedef void (APIENTRYP PFNGLBEGINFRAGMENTSHADERATIPROC)(void); +typedef void (APIENTRYP PFNGLENDFRAGMENTSHADERATIPROC)(void); +typedef void (APIENTRYP PFNGLPASSTEXCOORDATIPROC)(GLuint dst, GLuint coord, GLenum swizzle); +typedef void (APIENTRYP PFNGLSAMPLEMAPATIPROC)(GLuint dst, GLuint interp, GLenum swizzle); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP1ATIPROC)(GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP2ATIPROC)(GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP3ATIPROC)(GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP1ATIPROC)(GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP2ATIPROC)(GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP3ATIPROC)(GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +typedef void (APIENTRYP PFNGLSETFRAGMENTSHADERCONSTANTATIPROC)(GLuint dst, const GLfloat * value); +#endif /* GL_ATI_fragment_shader */ + +#ifndef GL_ATI_map_object_buffer +#define GL_ATI_map_object_buffer 1 +typedef void * (APIENTRYP PFNGLMAPOBJECTBUFFERATIPROC)(GLuint buffer); +typedef void (APIENTRYP PFNGLUNMAPOBJECTBUFFERATIPROC)(GLuint buffer); +#endif /* GL_ATI_map_object_buffer */ + +#ifndef GL_ATI_meminfo +#define GL_ATI_meminfo 1 +#define GL_VBO_FREE_MEMORY_ATI 0x87FB +#define GL_TEXTURE_FREE_MEMORY_ATI 0x87FC +#define GL_RENDERBUFFER_FREE_MEMORY_ATI 0x87FD +#endif /* GL_ATI_meminfo */ + +#ifndef GL_ATI_pixel_format_float +#define GL_ATI_pixel_format_float 1 +#define GL_RGBA_FLOAT_MODE_ATI 0x8820 +#define GL_COLOR_CLEAR_UNCLAMPED_VALUE_ATI 0x8835 +#endif /* GL_ATI_pixel_format_float */ + +#ifndef GL_ATI_pn_triangles +#define GL_ATI_pn_triangles 1 +#define GL_PN_TRIANGLES_ATI 0x87F0 +#define GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F1 +#define GL_PN_TRIANGLES_POINT_MODE_ATI 0x87F2 +#define GL_PN_TRIANGLES_NORMAL_MODE_ATI 0x87F3 +#define GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F4 +#define GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI 0x87F5 +#define GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI 0x87F6 +#define GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI 0x87F7 +#define GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI 0x87F8 +typedef void (APIENTRYP PFNGLPNTRIANGLESIATIPROC)(GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPNTRIANGLESFATIPROC)(GLenum pname, GLfloat param); +#endif /* GL_ATI_pn_triangles */ + +#ifndef GL_ATI_separate_stencil +#define GL_ATI_separate_stencil 1 +#define GL_STENCIL_BACK_FUNC_ATI 0x8800 +#define GL_STENCIL_BACK_FAIL_ATI 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL_ATI 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS_ATI 0x8803 +typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEATIPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEATIPROC)(GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); +#endif /* GL_ATI_separate_stencil */ + +#ifndef GL_ATI_text_fragment_shader +#define GL_ATI_text_fragment_shader 1 +#define GL_TEXT_FRAGMENT_SHADER_ATI 0x8200 +#endif /* GL_ATI_text_fragment_shader */ + +#ifndef GL_ATI_texture_env_combine3 +#define GL_ATI_texture_env_combine3 1 +#define GL_MODULATE_ADD_ATI 0x8744 +#define GL_MODULATE_SIGNED_ADD_ATI 0x8745 +#define GL_MODULATE_SUBTRACT_ATI 0x8746 +#endif /* GL_ATI_texture_env_combine3 */ + +#ifndef GL_ATI_texture_float +#define GL_ATI_texture_float 1 +#define GL_RGBA_FLOAT32_ATI 0x8814 +#define GL_RGB_FLOAT32_ATI 0x8815 +#define GL_ALPHA_FLOAT32_ATI 0x8816 +#define GL_INTENSITY_FLOAT32_ATI 0x8817 +#define GL_LUMINANCE_FLOAT32_ATI 0x8818 +#define GL_LUMINANCE_ALPHA_FLOAT32_ATI 0x8819 +#define GL_RGBA_FLOAT16_ATI 0x881A +#define GL_RGB_FLOAT16_ATI 0x881B +#define GL_ALPHA_FLOAT16_ATI 0x881C +#define GL_INTENSITY_FLOAT16_ATI 0x881D +#define GL_LUMINANCE_FLOAT16_ATI 0x881E +#define GL_LUMINANCE_ALPHA_FLOAT16_ATI 0x881F +#endif /* GL_ATI_texture_float */ + +#ifndef GL_ATI_texture_mirror_once +#define GL_ATI_texture_mirror_once 1 +#define GL_MIRROR_CLAMP_ATI 0x8742 +#define GL_MIRROR_CLAMP_TO_EDGE_ATI 0x8743 +#endif /* GL_ATI_texture_mirror_once */ + +#ifndef GL_ATI_vertex_array_object +#define GL_ATI_vertex_array_object 1 +#define GL_STATIC_ATI 0x8760 +#define GL_DYNAMIC_ATI 0x8761 +#define GL_PRESERVE_ATI 0x8762 +#define GL_DISCARD_ATI 0x8763 +#define GL_OBJECT_BUFFER_SIZE_ATI 0x8764 +#define GL_OBJECT_BUFFER_USAGE_ATI 0x8765 +#define GL_ARRAY_OBJECT_BUFFER_ATI 0x8766 +#define GL_ARRAY_OBJECT_OFFSET_ATI 0x8767 +typedef GLuint (APIENTRYP PFNGLNEWOBJECTBUFFERATIPROC)(GLsizei size, const void * pointer, GLenum usage); +typedef GLboolean (APIENTRYP PFNGLISOBJECTBUFFERATIPROC)(GLuint buffer); +typedef void (APIENTRYP PFNGLUPDATEOBJECTBUFFERATIPROC)(GLuint buffer, GLuint offset, GLsizei size, const void * pointer, GLenum preserve); +typedef void (APIENTRYP PFNGLGETOBJECTBUFFERFVATIPROC)(GLuint buffer, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETOBJECTBUFFERIVATIPROC)(GLuint buffer, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLFREEOBJECTBUFFERATIPROC)(GLuint buffer); +typedef void (APIENTRYP PFNGLARRAYOBJECTATIPROC)(GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETARRAYOBJECTFVATIPROC)(GLenum array, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETARRAYOBJECTIVATIPROC)(GLenum array, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLVARIANTARRAYOBJECTATIPROC)(GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTFVATIPROC)(GLuint id, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTIVATIPROC)(GLuint id, GLenum pname, GLint * params); +#endif /* GL_ATI_vertex_array_object */ + +#ifndef GL_ATI_vertex_attrib_array_object +#define GL_ATI_vertex_attrib_array_object 1 +typedef void (APIENTRYP PFNGLVERTEXATTRIBARRAYOBJECTATIPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTFVATIPROC)(GLuint index, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTIVATIPROC)(GLuint index, GLenum pname, GLint * params); +#endif /* GL_ATI_vertex_attrib_array_object */ + +#ifndef GL_ATI_vertex_streams +#define GL_ATI_vertex_streams 1 +#define GL_MAX_VERTEX_STREAMS_ATI 0x876B +#define GL_VERTEX_STREAM0_ATI 0x876C +#define GL_VERTEX_STREAM1_ATI 0x876D +#define GL_VERTEX_STREAM2_ATI 0x876E +#define GL_VERTEX_STREAM3_ATI 0x876F +#define GL_VERTEX_STREAM4_ATI 0x8770 +#define GL_VERTEX_STREAM5_ATI 0x8771 +#define GL_VERTEX_STREAM6_ATI 0x8772 +#define GL_VERTEX_STREAM7_ATI 0x8773 +#define GL_VERTEX_SOURCE_ATI 0x8774 +typedef void (APIENTRYP PFNGLVERTEXSTREAM1SATIPROC)(GLenum stream, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1SVATIPROC)(GLenum stream, const GLshort * coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1IATIPROC)(GLenum stream, GLint x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1IVATIPROC)(GLenum stream, const GLint * coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1FATIPROC)(GLenum stream, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1FVATIPROC)(GLenum stream, const GLfloat * coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1DATIPROC)(GLenum stream, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1DVATIPROC)(GLenum stream, const GLdouble * coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2SATIPROC)(GLenum stream, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2SVATIPROC)(GLenum stream, const GLshort * coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2IATIPROC)(GLenum stream, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2IVATIPROC)(GLenum stream, const GLint * coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2FATIPROC)(GLenum stream, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2FVATIPROC)(GLenum stream, const GLfloat * coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2DATIPROC)(GLenum stream, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2DVATIPROC)(GLenum stream, const GLdouble * coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3SATIPROC)(GLenum stream, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3SVATIPROC)(GLenum stream, const GLshort * coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3IATIPROC)(GLenum stream, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3IVATIPROC)(GLenum stream, const GLint * coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3FATIPROC)(GLenum stream, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3FVATIPROC)(GLenum stream, const GLfloat * coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3DATIPROC)(GLenum stream, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3DVATIPROC)(GLenum stream, const GLdouble * coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4SATIPROC)(GLenum stream, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4SVATIPROC)(GLenum stream, const GLshort * coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4IATIPROC)(GLenum stream, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4IVATIPROC)(GLenum stream, const GLint * coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4FATIPROC)(GLenum stream, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4FVATIPROC)(GLenum stream, const GLfloat * coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4DATIPROC)(GLenum stream, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4DVATIPROC)(GLenum stream, const GLdouble * coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3BATIPROC)(GLenum stream, GLbyte nx, GLbyte ny, GLbyte nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3BVATIPROC)(GLenum stream, const GLbyte * coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3SATIPROC)(GLenum stream, GLshort nx, GLshort ny, GLshort nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3SVATIPROC)(GLenum stream, const GLshort * coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3IATIPROC)(GLenum stream, GLint nx, GLint ny, GLint nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3IVATIPROC)(GLenum stream, const GLint * coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3FATIPROC)(GLenum stream, GLfloat nx, GLfloat ny, GLfloat nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3FVATIPROC)(GLenum stream, const GLfloat * coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3DATIPROC)(GLenum stream, GLdouble nx, GLdouble ny, GLdouble nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3DVATIPROC)(GLenum stream, const GLdouble * coords); +typedef void (APIENTRYP PFNGLCLIENTACTIVEVERTEXSTREAMATIPROC)(GLenum stream); +typedef void (APIENTRYP PFNGLVERTEXBLENDENVIATIPROC)(GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLVERTEXBLENDENVFATIPROC)(GLenum pname, GLfloat param); +#endif /* GL_ATI_vertex_streams */ + +#ifndef GL_EXT_422_pixels +#define GL_EXT_422_pixels 1 +#define GL_422_EXT 0x80CC +#define GL_422_REV_EXT 0x80CD +#define GL_422_AVERAGE_EXT 0x80CE +#define GL_422_REV_AVERAGE_EXT 0x80CF +#endif /* GL_EXT_422_pixels */ + +#ifndef GL_EXT_EGL_image_storage +#define GL_EXT_EGL_image_storage 1 +typedef void *GLeglImageOES; +typedef void (APIENTRYP PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC)(GLenum target, GLeglImageOES image, const GLint* attrib_list); +typedef void (APIENTRYP PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC)(GLuint texture, GLeglImageOES image, const GLint* attrib_list); +#endif /* GL_EXT_EGL_image_storage */ + +#ifndef GL_EXT_EGL_sync +#define GL_EXT_EGL_sync 1 +#endif /* GL_EXT_EGL_sync */ + +#ifndef GL_EXT_abgr +#define GL_EXT_abgr 1 +#define GL_ABGR_EXT 0x8000 +#endif /* GL_EXT_abgr */ + +#ifndef GL_EXT_bgra +#define GL_EXT_bgra 1 +#define GL_BGR_EXT 0x80E0 +#define GL_BGRA_EXT 0x80E1 +#endif /* GL_EXT_bgra */ + +#ifndef GL_EXT_bindable_uniform +#define GL_EXT_bindable_uniform 1 +#define GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT 0x8DE2 +#define GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT 0x8DE3 +#define GL_MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT 0x8DE4 +#define GL_MAX_BINDABLE_UNIFORM_SIZE_EXT 0x8DED +#define GL_UNIFORM_BUFFER_EXT 0x8DEE +#define GL_UNIFORM_BUFFER_BINDING_EXT 0x8DEF +typedef void (APIENTRYP PFNGLUNIFORMBUFFEREXTPROC)(GLuint program, GLint location, GLuint buffer); +typedef GLint (APIENTRYP PFNGLGETUNIFORMBUFFERSIZEEXTPROC)(GLuint program, GLint location); +typedef GLintptr (APIENTRYP PFNGLGETUNIFORMOFFSETEXTPROC)(GLuint program, GLint location); +#endif /* GL_EXT_bindable_uniform */ + +#ifndef GL_EXT_blend_color +#define GL_EXT_blend_color 1 +#define GL_CONSTANT_COLOR_EXT 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR_EXT 0x8002 +#define GL_CONSTANT_ALPHA_EXT 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA_EXT 0x8004 +#define GL_BLEND_COLOR_EXT 0x8005 +typedef void (APIENTRYP PFNGLBLENDCOLOREXTPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +#endif /* GL_EXT_blend_color */ + +#ifndef GL_EXT_blend_equation_separate +#define GL_EXT_blend_equation_separate 1 +#define GL_BLEND_EQUATION_RGB_EXT 0x8009 +#define GL_BLEND_EQUATION_ALPHA_EXT 0x883D +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEEXTPROC)(GLenum modeRGB, GLenum modeAlpha); +#endif /* GL_EXT_blend_equation_separate */ + +#ifndef GL_EXT_blend_func_separate +#define GL_EXT_blend_func_separate 1 +#define GL_BLEND_DST_RGB_EXT 0x80C8 +#define GL_BLEND_SRC_RGB_EXT 0x80C9 +#define GL_BLEND_DST_ALPHA_EXT 0x80CA +#define GL_BLEND_SRC_ALPHA_EXT 0x80CB +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEEXTPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#endif /* GL_EXT_blend_func_separate */ + +#ifndef GL_EXT_blend_logic_op +#define GL_EXT_blend_logic_op 1 +#endif /* GL_EXT_blend_logic_op */ + +#ifndef GL_EXT_blend_minmax +#define GL_EXT_blend_minmax 1 +#define GL_MIN_EXT 0x8007 +#define GL_MAX_EXT 0x8008 +#define GL_FUNC_ADD_EXT 0x8006 +#define GL_BLEND_EQUATION_EXT 0x8009 +typedef void (APIENTRYP PFNGLBLENDEQUATIONEXTPROC)(GLenum mode); +#endif /* GL_EXT_blend_minmax */ + +#ifndef GL_EXT_blend_subtract +#define GL_EXT_blend_subtract 1 +#define GL_FUNC_SUBTRACT_EXT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT_EXT 0x800B +#endif /* GL_EXT_blend_subtract */ + +#ifndef GL_EXT_clip_volume_hint +#define GL_EXT_clip_volume_hint 1 +#define GL_CLIP_VOLUME_CLIPPING_HINT_EXT 0x80F0 +#endif /* GL_EXT_clip_volume_hint */ + +#ifndef GL_EXT_cmyka +#define GL_EXT_cmyka 1 +#define GL_CMYK_EXT 0x800C +#define GL_CMYKA_EXT 0x800D +#define GL_PACK_CMYK_HINT_EXT 0x800E +#define GL_UNPACK_CMYK_HINT_EXT 0x800F +#endif /* GL_EXT_cmyka */ + +#ifndef GL_EXT_color_subtable +#define GL_EXT_color_subtable 1 +typedef void (APIENTRYP PFNGLCOLORSUBTABLEEXTPROC)(GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void * data); +typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEEXTPROC)(GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +#endif /* GL_EXT_color_subtable */ + +#ifndef GL_EXT_compiled_vertex_array +#define GL_EXT_compiled_vertex_array 1 +#define GL_ARRAY_ELEMENT_LOCK_FIRST_EXT 0x81A8 +#define GL_ARRAY_ELEMENT_LOCK_COUNT_EXT 0x81A9 +typedef void (APIENTRYP PFNGLLOCKARRAYSEXTPROC)(GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLUNLOCKARRAYSEXTPROC)(void); +#endif /* GL_EXT_compiled_vertex_array */ + +#ifndef GL_EXT_convolution +#define GL_EXT_convolution 1 +#define GL_CONVOLUTION_1D_EXT 0x8010 +#define GL_CONVOLUTION_2D_EXT 0x8011 +#define GL_SEPARABLE_2D_EXT 0x8012 +#define GL_CONVOLUTION_BORDER_MODE_EXT 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE_EXT 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS_EXT 0x8015 +#define GL_REDUCE_EXT 0x8016 +#define GL_CONVOLUTION_FORMAT_EXT 0x8017 +#define GL_CONVOLUTION_WIDTH_EXT 0x8018 +#define GL_CONVOLUTION_HEIGHT_EXT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH_EXT 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT_EXT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE_EXT 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE_EXT 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE_EXT 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE_EXT 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS_EXT 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS_EXT 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS_EXT 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS_EXT 0x8023 +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DEXTPROC)(GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void * image); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DEXTPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * image); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFEXTPROC)(GLenum target, GLenum pname, GLfloat params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVEXTPROC)(GLenum target, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIEXTPROC)(GLenum target, GLenum pname, GLint params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVEXTPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DEXTPROC)(GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DEXTPROC)(GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTEREXTPROC)(GLenum target, GLenum format, GLenum type, void * image); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVEXTPROC)(GLenum target, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVEXTPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETSEPARABLEFILTEREXTPROC)(GLenum target, GLenum format, GLenum type, void * row, void * column, void * span); +typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DEXTPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * row, const void * column); +#endif /* GL_EXT_convolution */ + +#ifndef GL_EXT_coordinate_frame +#define GL_EXT_coordinate_frame 1 +#define GL_TANGENT_ARRAY_EXT 0x8439 +#define GL_BINORMAL_ARRAY_EXT 0x843A +#define GL_CURRENT_TANGENT_EXT 0x843B +#define GL_CURRENT_BINORMAL_EXT 0x843C +#define GL_TANGENT_ARRAY_TYPE_EXT 0x843E +#define GL_TANGENT_ARRAY_STRIDE_EXT 0x843F +#define GL_BINORMAL_ARRAY_TYPE_EXT 0x8440 +#define GL_BINORMAL_ARRAY_STRIDE_EXT 0x8441 +#define GL_TANGENT_ARRAY_POINTER_EXT 0x8442 +#define GL_BINORMAL_ARRAY_POINTER_EXT 0x8443 +#define GL_MAP1_TANGENT_EXT 0x8444 +#define GL_MAP2_TANGENT_EXT 0x8445 +#define GL_MAP1_BINORMAL_EXT 0x8446 +#define GL_MAP2_BINORMAL_EXT 0x8447 +typedef void (APIENTRYP PFNGLTANGENT3BEXTPROC)(GLbyte tx, GLbyte ty, GLbyte tz); +typedef void (APIENTRYP PFNGLTANGENT3BVEXTPROC)(const GLbyte * v); +typedef void (APIENTRYP PFNGLTANGENT3DEXTPROC)(GLdouble tx, GLdouble ty, GLdouble tz); +typedef void (APIENTRYP PFNGLTANGENT3DVEXTPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLTANGENT3FEXTPROC)(GLfloat tx, GLfloat ty, GLfloat tz); +typedef void (APIENTRYP PFNGLTANGENT3FVEXTPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLTANGENT3IEXTPROC)(GLint tx, GLint ty, GLint tz); +typedef void (APIENTRYP PFNGLTANGENT3IVEXTPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLTANGENT3SEXTPROC)(GLshort tx, GLshort ty, GLshort tz); +typedef void (APIENTRYP PFNGLTANGENT3SVEXTPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLBINORMAL3BEXTPROC)(GLbyte bx, GLbyte by, GLbyte bz); +typedef void (APIENTRYP PFNGLBINORMAL3BVEXTPROC)(const GLbyte * v); +typedef void (APIENTRYP PFNGLBINORMAL3DEXTPROC)(GLdouble bx, GLdouble by, GLdouble bz); +typedef void (APIENTRYP PFNGLBINORMAL3DVEXTPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLBINORMAL3FEXTPROC)(GLfloat bx, GLfloat by, GLfloat bz); +typedef void (APIENTRYP PFNGLBINORMAL3FVEXTPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLBINORMAL3IEXTPROC)(GLint bx, GLint by, GLint bz); +typedef void (APIENTRYP PFNGLBINORMAL3IVEXTPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLBINORMAL3SEXTPROC)(GLshort bx, GLshort by, GLshort bz); +typedef void (APIENTRYP PFNGLBINORMAL3SVEXTPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLTANGENTPOINTEREXTPROC)(GLenum type, GLsizei stride, const void * pointer); +typedef void (APIENTRYP PFNGLBINORMALPOINTEREXTPROC)(GLenum type, GLsizei stride, const void * pointer); +#endif /* GL_EXT_coordinate_frame */ + +#ifndef GL_EXT_copy_texture +#define GL_EXT_copy_texture 1 +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DEXTPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DEXTPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DEXTPROC)(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DEXTPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DEXTPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif /* GL_EXT_copy_texture */ + +#ifndef GL_EXT_cull_vertex +#define GL_EXT_cull_vertex 1 +#define GL_CULL_VERTEX_EXT 0x81AA +#define GL_CULL_VERTEX_EYE_POSITION_EXT 0x81AB +#define GL_CULL_VERTEX_OBJECT_POSITION_EXT 0x81AC +typedef void (APIENTRYP PFNGLCULLPARAMETERDVEXTPROC)(GLenum pname, GLdouble * params); +typedef void (APIENTRYP PFNGLCULLPARAMETERFVEXTPROC)(GLenum pname, GLfloat * params); +#endif /* GL_EXT_cull_vertex */ + +#ifndef GL_EXT_debug_label +#define GL_EXT_debug_label 1 +#define GL_PROGRAM_PIPELINE_OBJECT_EXT 0x8A4F +#define GL_PROGRAM_OBJECT_EXT 0x8B40 +#define GL_SHADER_OBJECT_EXT 0x8B48 +#define GL_BUFFER_OBJECT_EXT 0x9151 +#define GL_QUERY_OBJECT_EXT 0x9153 +#define GL_VERTEX_ARRAY_OBJECT_EXT 0x9154 +#define GL_SAMPLER 0x82E6 +#define GL_TRANSFORM_FEEDBACK 0x8E22 +typedef void (APIENTRYP PFNGLLABELOBJECTEXTPROC)(GLenum type, GLuint object, GLsizei length, const GLchar * label); +typedef void (APIENTRYP PFNGLGETOBJECTLABELEXTPROC)(GLenum type, GLuint object, GLsizei bufSize, GLsizei * length, GLchar * label); +#endif /* GL_EXT_debug_label */ + +#ifndef GL_EXT_debug_marker +#define GL_EXT_debug_marker 1 +typedef void (APIENTRYP PFNGLINSERTEVENTMARKEREXTPROC)(GLsizei length, const GLchar * marker); +typedef void (APIENTRYP PFNGLPUSHGROUPMARKEREXTPROC)(GLsizei length, const GLchar * marker); +typedef void (APIENTRYP PFNGLPOPGROUPMARKEREXTPROC)(void); +#endif /* GL_EXT_debug_marker */ + +#ifndef GL_EXT_depth_bounds_test +#define GL_EXT_depth_bounds_test 1 +#define GL_DEPTH_BOUNDS_TEST_EXT 0x8890 +#define GL_DEPTH_BOUNDS_EXT 0x8891 +typedef void (APIENTRYP PFNGLDEPTHBOUNDSEXTPROC)(GLclampd zmin, GLclampd zmax); +#endif /* GL_EXT_depth_bounds_test */ + +#ifndef GL_EXT_direct_state_access +#define GL_EXT_direct_state_access 1 +#define GL_PROGRAM_MATRIX_EXT 0x8E2D +#define GL_TRANSPOSE_PROGRAM_MATRIX_EXT 0x8E2E +#define GL_PROGRAM_MATRIX_STACK_DEPTH_EXT 0x8E2F +typedef void (APIENTRYP PFNGLMATRIXLOADFEXTPROC)(GLenum mode, const GLfloat * m); +typedef void (APIENTRYP PFNGLMATRIXLOADDEXTPROC)(GLenum mode, const GLdouble * m); +typedef void (APIENTRYP PFNGLMATRIXMULTFEXTPROC)(GLenum mode, const GLfloat * m); +typedef void (APIENTRYP PFNGLMATRIXMULTDEXTPROC)(GLenum mode, const GLdouble * m); +typedef void (APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC)(GLenum mode); +typedef void (APIENTRYP PFNGLMATRIXROTATEFEXTPROC)(GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXROTATEDEXTPROC)(GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXSCALEFEXTPROC)(GLenum mode, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXSCALEDEXTPROC)(GLenum mode, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXTRANSLATEFEXTPROC)(GLenum mode, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXTRANSLATEDEXTPROC)(GLenum mode, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXFRUSTUMEXTPROC)(GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLMATRIXORTHOEXTPROC)(GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLMATRIXPOPEXTPROC)(GLenum mode); +typedef void (APIENTRYP PFNGLMATRIXPUSHEXTPROC)(GLenum mode); +typedef void (APIENTRYP PFNGLCLIENTATTRIBDEFAULTEXTPROC)(GLbitfield mask); +typedef void (APIENTRYP PFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC)(GLbitfield mask); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFEXTPROC)(GLuint texture, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVEXTPROC)(GLuint texture, GLenum target, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIEXTPROC)(GLuint texture, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVEXTPROC)(GLuint texture, GLenum target, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE1DEXTPROC)(GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DEXTPROC)(GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DEXTPROC)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DEXTPROC)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE1DEXTPROC)(GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE2DEXTPROC)(GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DEXTPROC)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DEXTPROC)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEEXTPROC)(GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void * pixels); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVEXTPROC)(GLuint texture, GLenum target, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVEXTPROC)(GLuint texture, GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVEXTPROC)(GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVEXTPROC)(GLuint texture, GLenum target, GLint level, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DEXTPROC)(GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DEXTPROC)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DEXTPROC)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLBINDMULTITEXTUREEXTPROC)(GLenum texunit, GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORDPOINTEREXTPROC)(GLenum texunit, GLint size, GLenum type, GLsizei stride, const void * pointer); +typedef void (APIENTRYP PFNGLMULTITEXENVFEXTPROC)(GLenum texunit, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXENVFVEXTPROC)(GLenum texunit, GLenum target, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLMULTITEXENVIEXTPROC)(GLenum texunit, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXENVIVEXTPROC)(GLenum texunit, GLenum target, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLMULTITEXGENDEXTPROC)(GLenum texunit, GLenum coord, GLenum pname, GLdouble param); +typedef void (APIENTRYP PFNGLMULTITEXGENDVEXTPROC)(GLenum texunit, GLenum coord, GLenum pname, const GLdouble * params); +typedef void (APIENTRYP PFNGLMULTITEXGENFEXTPROC)(GLenum texunit, GLenum coord, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXGENFVEXTPROC)(GLenum texunit, GLenum coord, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLMULTITEXGENIEXTPROC)(GLenum texunit, GLenum coord, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXGENIVEXTPROC)(GLenum texunit, GLenum coord, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLGETMULTITEXENVFVEXTPROC)(GLenum texunit, GLenum target, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETMULTITEXENVIVEXTPROC)(GLenum texunit, GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENDVEXTPROC)(GLenum texunit, GLenum coord, GLenum pname, GLdouble * params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENFVEXTPROC)(GLenum texunit, GLenum coord, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENIVEXTPROC)(GLenum texunit, GLenum coord, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIEXTPROC)(GLenum texunit, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIVEXTPROC)(GLenum texunit, GLenum target, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFEXTPROC)(GLenum texunit, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFVEXTPROC)(GLenum texunit, GLenum target, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE1DEXTPROC)(GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE2DEXTPROC)(GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE1DEXTPROC)(GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE2DEXTPROC)(GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE1DEXTPROC)(GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE2DEXTPROC)(GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC)(GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC)(GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETMULTITEXIMAGEEXTPROC)(GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void * pixels); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERFVEXTPROC)(GLenum texunit, GLenum target, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIVEXTPROC)(GLenum texunit, GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC)(GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC)(GLenum texunit, GLenum target, GLint level, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE3DEXTPROC)(GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE3DEXTPROC)(GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC)(GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLENABLECLIENTSTATEINDEXEDEXTPROC)(GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEINDEXEDEXTPROC)(GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLGETFLOATINDEXEDVEXTPROC)(GLenum target, GLuint index, GLfloat * data); +typedef void (APIENTRYP PFNGLGETDOUBLEINDEXEDVEXTPROC)(GLenum target, GLuint index, GLdouble * data); +typedef void (APIENTRYP PFNGLGETPOINTERINDEXEDVEXTPROC)(GLenum target, GLuint index, void ** data); +typedef void (APIENTRYP PFNGLENABLEINDEXEDEXTPROC)(GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEINDEXEDEXTPROC)(GLenum target, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISENABLEDINDEXEDEXTPROC)(GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLGETINTEGERINDEXEDVEXTPROC)(GLenum target, GLuint index, GLint * data); +typedef void (APIENTRYP PFNGLGETBOOLEANINDEXEDVEXTPROC)(GLenum target, GLuint index, GLboolean * data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC)(GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC)(GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC)(GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void * bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void * bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC)(GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void * bits); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC)(GLuint texture, GLenum target, GLint lod, void * img); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC)(GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC)(GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC)(GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void * bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC)(GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void * bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC)(GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC)(GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void * bits); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC)(GLenum texunit, GLenum target, GLint lod, void * img); +typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEFEXTPROC)(GLenum mode, const GLfloat * m); +typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEDEXTPROC)(GLenum mode, const GLdouble * m); +typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEFEXTPROC)(GLenum mode, const GLfloat * m); +typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEDEXTPROC)(GLenum mode, const GLdouble * m); +typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAEXTPROC)(GLuint buffer, GLsizeiptr size, const void * data, GLenum usage); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAEXTPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, const void * data); +typedef void * (APIENTRYP PFNGLMAPNAMEDBUFFEREXTPROC)(GLuint buffer, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFEREXTPROC)(GLuint buffer); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC)(GLuint buffer, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVEXTPROC)(GLuint buffer, GLenum pname, void ** params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAEXTPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, void * data); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FEXTPROC)(GLuint program, GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FEXTPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FEXTPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FEXTPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IEXTPROC)(GLuint program, GLint location, GLint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IEXTPROC)(GLuint program, GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IEXTPROC)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IEXTPROC)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +typedef void (APIENTRYP PFNGLTEXTUREBUFFEREXTPROC)(GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLMULTITEXBUFFEREXTPROC)(GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVEXTPROC)(GLuint texture, GLenum target, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVEXTPROC)(GLuint texture, GLenum target, GLenum pname, const GLuint * params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVEXTPROC)(GLuint texture, GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVEXTPROC)(GLuint texture, GLenum target, GLenum pname, GLuint * params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIIVEXTPROC)(GLenum texunit, GLenum target, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIUIVEXTPROC)(GLenum texunit, GLenum target, GLenum pname, const GLuint * params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIIVEXTPROC)(GLenum texunit, GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIUIVEXTPROC)(GLenum texunit, GLenum target, GLenum pname, GLuint * params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIEXTPROC)(GLuint program, GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIEXTPROC)(GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIEXTPROC)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIEXTPROC)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC)(GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat * params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC)(GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC)(GLuint program, GLenum target, GLuint index, const GLint * params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC)(GLuint program, GLenum target, GLuint index, GLsizei count, const GLint * params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC)(GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC)(GLuint program, GLenum target, GLuint index, const GLuint * params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC)(GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint * params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC)(GLuint program, GLenum target, GLuint index, GLint * params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC)(GLuint program, GLenum target, GLuint index, GLuint * params); +typedef void (APIENTRYP PFNGLENABLECLIENTSTATEIEXTPROC)(GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEIEXTPROC)(GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLGETFLOATI_VEXTPROC)(GLenum pname, GLuint index, GLfloat * params); +typedef void (APIENTRYP PFNGLGETDOUBLEI_VEXTPROC)(GLenum pname, GLuint index, GLdouble * params); +typedef void (APIENTRYP PFNGLGETPOINTERI_VEXTPROC)(GLenum pname, GLuint index, void ** params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMSTRINGEXTPROC)(GLuint program, GLenum target, GLenum format, GLsizei len, const void * string); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC)(GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC)(GLuint program, GLenum target, GLuint index, const GLdouble * params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC)(GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC)(GLuint program, GLenum target, GLuint index, const GLfloat * params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC)(GLuint program, GLenum target, GLuint index, GLdouble * params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC)(GLuint program, GLenum target, GLuint index, GLfloat * params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMIVEXTPROC)(GLuint program, GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMSTRINGEXTPROC)(GLuint program, GLenum target, GLenum pname, void * string); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC)(GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC)(GLuint renderbuffer, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)(GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC)(GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC)(GLuint framebuffer, GLenum target); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC)(GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC)(GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC)(GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC)(GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC)(GLuint framebuffer, GLenum attachment, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPEXTPROC)(GLuint texture, GLenum target); +typedef void (APIENTRYP PFNGLGENERATEMULTITEXMIPMAPEXTPROC)(GLenum texunit, GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFEREXTPROC)(GLuint framebuffer, GLenum mode); +typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC)(GLuint framebuffer, GLsizei n, const GLenum * bufs); +typedef void (APIENTRYP PFNGLFRAMEBUFFERREADBUFFEREXTPROC)(GLuint framebuffer, GLenum mode); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC)(GLuint framebuffer, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC)(GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC)(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC)(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC)(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); +typedef void (APIENTRYP PFNGLTEXTURERENDERBUFFEREXTPROC)(GLuint texture, GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLMULTITEXRENDERBUFFEREXTPROC)(GLenum texunit, GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXOFFSETEXTPROC)(GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYCOLOROFFSETEXTPROC)(GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYEDGEFLAGOFFSETEXTPROC)(GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYINDEXOFFSETEXTPROC)(GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYNORMALOFFSETEXTPROC)(GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYTEXCOORDOFFSETEXTPROC)(GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYMULTITEXCOORDOFFSETEXTPROC)(GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYFOGCOORDOFFSETEXTPROC)(GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYSECONDARYCOLOROFFSETEXTPROC)(GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBOFFSETEXTPROC)(GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIOFFSETEXTPROC)(GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYEXTPROC)(GLuint vaobj, GLenum array); +typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYEXTPROC)(GLuint vaobj, GLenum array); +typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBEXTPROC)(GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBEXTPROC)(GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERVEXTPROC)(GLuint vaobj, GLenum pname, GLint * param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERVEXTPROC)(GLuint vaobj, GLenum pname, void ** param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC)(GLuint vaobj, GLuint index, GLenum pname, GLint * param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC)(GLuint vaobj, GLuint index, GLenum pname, void ** param); +typedef void * (APIENTRYP PFNGLMAPNAMEDBUFFERRANGEEXTPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEEXTPROC)(GLuint buffer, GLsizeiptr size, const void * data, GLbitfield flags); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAEXTPROC)(GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void * data); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC)(GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void * data); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC)(GLuint framebuffer, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC)(GLuint framebuffer, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DEXTPROC)(GLuint program, GLint location, GLdouble x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DEXTPROC)(GLuint program, GLint location, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DEXTPROC)(GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DEXTPROC)(GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVEXTPROC)(GLuint program, GLint location, GLsizei count, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); +typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEEXTPROC)(GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DEXTPROC)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DEXTPROC)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DEXTPROC)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC)(GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC)(GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC)(GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC)(GLuint vaobj, GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC)(GLuint vaobj, GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC)(GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTEXTPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBDIVISOREXTPROC)(GLuint vaobj, GLuint index, GLuint divisor); +#endif /* GL_EXT_direct_state_access */ + +#ifndef GL_EXT_draw_buffers2 +#define GL_EXT_draw_buffers2 1 +typedef void (APIENTRYP PFNGLCOLORMASKINDEXEDEXTPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +#endif /* GL_EXT_draw_buffers2 */ + +#ifndef GL_EXT_draw_instanced +#define GL_EXT_draw_instanced 1 +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC)(GLenum mode, GLint start, GLsizei count, GLsizei primcount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei primcount); +#endif /* GL_EXT_draw_instanced */ + +#ifndef GL_EXT_draw_range_elements +#define GL_EXT_draw_range_elements 1 +#define GL_MAX_ELEMENTS_VERTICES_EXT 0x80E8 +#define GL_MAX_ELEMENTS_INDICES_EXT 0x80E9 +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSEXTPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices); +#endif /* GL_EXT_draw_range_elements */ + +#ifndef GL_EXT_external_buffer +#define GL_EXT_external_buffer 1 +typedef void *GLeglClientBufferEXT; +typedef void (APIENTRYP PFNGLBUFFERSTORAGEEXTERNALEXTPROC)(GLenum target, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEEXTERNALEXTPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags); +#endif /* GL_EXT_external_buffer */ + +#ifndef GL_EXT_fog_coord +#define GL_EXT_fog_coord 1 +#define GL_FOG_COORDINATE_SOURCE_EXT 0x8450 +#define GL_FOG_COORDINATE_EXT 0x8451 +#define GL_FRAGMENT_DEPTH_EXT 0x8452 +#define GL_CURRENT_FOG_COORDINATE_EXT 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE_EXT 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE_EXT 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER_EXT 0x8456 +#define GL_FOG_COORDINATE_ARRAY_EXT 0x8457 +typedef void (APIENTRYP PFNGLFOGCOORDFEXTPROC)(GLfloat coord); +typedef void (APIENTRYP PFNGLFOGCOORDFVEXTPROC)(const GLfloat * coord); +typedef void (APIENTRYP PFNGLFOGCOORDDEXTPROC)(GLdouble coord); +typedef void (APIENTRYP PFNGLFOGCOORDDVEXTPROC)(const GLdouble * coord); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTEREXTPROC)(GLenum type, GLsizei stride, const void * pointer); +#endif /* GL_EXT_fog_coord */ + +#ifndef GL_EXT_framebuffer_blit +#define GL_EXT_framebuffer_blit 1 +#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 +#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 +#define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFEREXTPROC)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif /* GL_EXT_framebuffer_blit */ + +#ifndef GL_EXT_framebuffer_multisample +#define GL_EXT_framebuffer_multisample 1 +#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 +#define GL_MAX_SAMPLES_EXT 0x8D57 +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +#endif /* GL_EXT_framebuffer_multisample */ + +#ifndef GL_EXT_framebuffer_multisample_blit_scaled +#define GL_EXT_framebuffer_multisample_blit_scaled 1 +#define GL_SCALED_RESOLVE_FASTEST_EXT 0x90BA +#define GL_SCALED_RESOLVE_NICEST_EXT 0x90BB +#endif /* GL_EXT_framebuffer_multisample_blit_scaled */ + +#ifndef GL_EXT_framebuffer_object +#define GL_EXT_framebuffer_object 1 +#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506 +#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8 +#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF +#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 +#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 +#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 +#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 +#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 +#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 +#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 +#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 +#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 +#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 +#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA +#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB +#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC +#define GL_COLOR_ATTACHMENT13_EXT 0x8CED +#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE +#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF +#define GL_DEPTH_ATTACHMENT_EXT 0x8D00 +#define GL_STENCIL_ATTACHMENT_EXT 0x8D20 +#define GL_FRAMEBUFFER_EXT 0x8D40 +#define GL_RENDERBUFFER_EXT 0x8D41 +#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42 +#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44 +#define GL_STENCIL_INDEX1_EXT 0x8D46 +#define GL_STENCIL_INDEX4_EXT 0x8D47 +#define GL_STENCIL_INDEX8_EXT 0x8D48 +#define GL_STENCIL_INDEX16_EXT 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55 +typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFEREXTPROC)(GLuint renderbuffer); +typedef void (APIENTRYP PFNGLBINDRENDERBUFFEREXTPROC)(GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC)(GLsizei n, const GLuint * renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSEXTPROC)(GLsizei n, GLuint * renderbuffers); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEEXTPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC)(GLenum target, GLenum pname, GLint * params); +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFEREXTPROC)(GLuint framebuffer); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFEREXTPROC)(GLenum target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSEXTPROC)(GLsizei n, const GLuint * framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSEXTPROC)(GLsizei n, GLuint * framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)(GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DEXTPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DEXTPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC)(GLenum target, GLenum attachment, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC)(GLenum target); +#endif /* GL_EXT_framebuffer_object */ + +#ifndef GL_EXT_framebuffer_sRGB +#define GL_EXT_framebuffer_sRGB 1 +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA +#endif /* GL_EXT_framebuffer_sRGB */ + +#ifndef GL_EXT_geometry_shader4 +#define GL_EXT_geometry_shader4 1 +#define GL_GEOMETRY_SHADER_EXT 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT_EXT 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_EXT 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_EXT 0x8DDC +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT 0x8C29 +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT 0x8DDD +#define GL_MAX_VERTEX_VARYING_COMPONENTS_EXT 0x8DDE +#define GL_MAX_VARYING_COMPONENTS_EXT 0x8B4B +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT 0x8DE1 +#define GL_LINES_ADJACENCY_EXT 0x000A +#define GL_LINE_STRIP_ADJACENCY_EXT 0x000B +#define GL_TRIANGLES_ADJACENCY_EXT 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY_EXT 0x000D +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT 0x8DA9 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT 0x8DA7 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4 +#define GL_PROGRAM_POINT_SIZE_EXT 0x8642 +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIEXTPROC)(GLuint program, GLenum pname, GLint value); +#endif /* GL_EXT_geometry_shader4 */ + +#ifndef GL_EXT_gpu_program_parameters +#define GL_EXT_gpu_program_parameters 1 +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERS4FVEXTPROC)(GLenum target, GLuint index, GLsizei count, const GLfloat * params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC)(GLenum target, GLuint index, GLsizei count, const GLfloat * params); +#endif /* GL_EXT_gpu_program_parameters */ + +#ifndef GL_EXT_gpu_shader4 +#define GL_EXT_gpu_shader4 1 +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_EXT 0x88FD +#define GL_SAMPLER_1D_ARRAY_EXT 0x8DC0 +#define GL_SAMPLER_2D_ARRAY_EXT 0x8DC1 +#define GL_SAMPLER_BUFFER_EXT 0x8DC2 +#define GL_SAMPLER_1D_ARRAY_SHADOW_EXT 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW_EXT 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW_EXT 0x8DC5 +#define GL_UNSIGNED_INT_VEC2_EXT 0x8DC6 +#define GL_UNSIGNED_INT_VEC3_EXT 0x8DC7 +#define GL_UNSIGNED_INT_VEC4_EXT 0x8DC8 +#define GL_INT_SAMPLER_1D_EXT 0x8DC9 +#define GL_INT_SAMPLER_2D_EXT 0x8DCA +#define GL_INT_SAMPLER_3D_EXT 0x8DCB +#define GL_INT_SAMPLER_CUBE_EXT 0x8DCC +#define GL_INT_SAMPLER_2D_RECT_EXT 0x8DCD +#define GL_INT_SAMPLER_1D_ARRAY_EXT 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY_EXT 0x8DCF +#define GL_INT_SAMPLER_BUFFER_EXT 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_1D_EXT 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D_EXT 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D_EXT 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE_EXT 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT_EXT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY_EXT 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY_EXT 0x8DD7 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT 0x8DD8 +#define GL_MIN_PROGRAM_TEXEL_OFFSET_EXT 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET_EXT 0x8905 +typedef void (APIENTRYP PFNGLGETUNIFORMUIVEXTPROC)(GLuint program, GLint location, GLuint * params); +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONEXTPROC)(GLuint program, GLuint color, const GLchar * name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONEXTPROC)(GLuint program, const GLchar * name); +typedef void (APIENTRYP PFNGLUNIFORM1UIEXTPROC)(GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLUNIFORM2UIEXTPROC)(GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLUNIFORM3UIEXTPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLUNIFORM4UIEXTPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLUNIFORM1UIVEXTPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (APIENTRYP PFNGLUNIFORM2UIVEXTPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (APIENTRYP PFNGLUNIFORM3UIVEXTPROC)(GLint location, GLsizei count, const GLuint * value); +typedef void (APIENTRYP PFNGLUNIFORM4UIVEXTPROC)(GLint location, GLsizei count, const GLuint * value); +#endif /* GL_EXT_gpu_shader4 */ + +#ifndef GL_EXT_histogram +#define GL_EXT_histogram 1 +#define GL_HISTOGRAM_EXT 0x8024 +#define GL_PROXY_HISTOGRAM_EXT 0x8025 +#define GL_HISTOGRAM_WIDTH_EXT 0x8026 +#define GL_HISTOGRAM_FORMAT_EXT 0x8027 +#define GL_HISTOGRAM_RED_SIZE_EXT 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE_EXT 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE_EXT 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE_EXT 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE_EXT 0x802C +#define GL_HISTOGRAM_SINK_EXT 0x802D +#define GL_MINMAX_EXT 0x802E +#define GL_MINMAX_FORMAT_EXT 0x802F +#define GL_MINMAX_SINK_EXT 0x8030 +#define GL_TABLE_TOO_LARGE_EXT 0x8031 +typedef void (APIENTRYP PFNGLGETHISTOGRAMEXTPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, void * values); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVEXTPROC)(GLenum target, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVEXTPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETMINMAXEXTPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, void * values); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVEXTPROC)(GLenum target, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVEXTPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLHISTOGRAMEXTPROC)(GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLMINMAXEXTPROC)(GLenum target, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLRESETHISTOGRAMEXTPROC)(GLenum target); +typedef void (APIENTRYP PFNGLRESETMINMAXEXTPROC)(GLenum target); +#endif /* GL_EXT_histogram */ + +#ifndef GL_EXT_index_array_formats +#define GL_EXT_index_array_formats 1 +#define GL_IUI_V2F_EXT 0x81AD +#define GL_IUI_V3F_EXT 0x81AE +#define GL_IUI_N3F_V2F_EXT 0x81AF +#define GL_IUI_N3F_V3F_EXT 0x81B0 +#define GL_T2F_IUI_V2F_EXT 0x81B1 +#define GL_T2F_IUI_V3F_EXT 0x81B2 +#define GL_T2F_IUI_N3F_V2F_EXT 0x81B3 +#define GL_T2F_IUI_N3F_V3F_EXT 0x81B4 +#endif /* GL_EXT_index_array_formats */ + +#ifndef GL_EXT_index_func +#define GL_EXT_index_func 1 +#define GL_INDEX_TEST_EXT 0x81B5 +#define GL_INDEX_TEST_FUNC_EXT 0x81B6 +#define GL_INDEX_TEST_REF_EXT 0x81B7 +typedef void (APIENTRYP PFNGLINDEXFUNCEXTPROC)(GLenum func, GLclampf ref); +#endif /* GL_EXT_index_func */ + +#ifndef GL_EXT_index_material +#define GL_EXT_index_material 1 +#define GL_INDEX_MATERIAL_EXT 0x81B8 +#define GL_INDEX_MATERIAL_PARAMETER_EXT 0x81B9 +#define GL_INDEX_MATERIAL_FACE_EXT 0x81BA +typedef void (APIENTRYP PFNGLINDEXMATERIALEXTPROC)(GLenum face, GLenum mode); +#endif /* GL_EXT_index_material */ + +#ifndef GL_EXT_index_texture +#define GL_EXT_index_texture 1 +#endif /* GL_EXT_index_texture */ + +#ifndef GL_EXT_light_texture +#define GL_EXT_light_texture 1 +#define GL_FRAGMENT_MATERIAL_EXT 0x8349 +#define GL_FRAGMENT_NORMAL_EXT 0x834A +#define GL_FRAGMENT_COLOR_EXT 0x834C +#define GL_ATTENUATION_EXT 0x834D +#define GL_SHADOW_ATTENUATION_EXT 0x834E +#define GL_TEXTURE_APPLICATION_MODE_EXT 0x834F +#define GL_TEXTURE_LIGHT_EXT 0x8350 +#define GL_TEXTURE_MATERIAL_FACE_EXT 0x8351 +#define GL_TEXTURE_MATERIAL_PARAMETER_EXT 0x8352 +#define GL_FRAGMENT_DEPTH_EXT 0x8452 +typedef void (APIENTRYP PFNGLAPPLYTEXTUREEXTPROC)(GLenum mode); +typedef void (APIENTRYP PFNGLTEXTURELIGHTEXTPROC)(GLenum pname); +typedef void (APIENTRYP PFNGLTEXTUREMATERIALEXTPROC)(GLenum face, GLenum mode); +#endif /* GL_EXT_light_texture */ + +#ifndef GL_EXT_memory_object +#define GL_EXT_memory_object 1 +#define GL_TEXTURE_TILING_EXT 0x9580 +#define GL_DEDICATED_MEMORY_OBJECT_EXT 0x9581 +#define GL_PROTECTED_MEMORY_OBJECT_EXT 0x959B +#define GL_NUM_TILING_TYPES_EXT 0x9582 +#define GL_TILING_TYPES_EXT 0x9583 +#define GL_OPTIMAL_TILING_EXT 0x9584 +#define GL_LINEAR_TILING_EXT 0x9585 +#define GL_NUM_DEVICE_UUIDS_EXT 0x9596 +#define GL_DEVICE_UUID_EXT 0x9597 +#define GL_DRIVER_UUID_EXT 0x9598 +#define GL_UUID_SIZE_EXT 16 +typedef void (APIENTRYP PFNGLGETUNSIGNEDBYTEVEXTPROC)(GLenum pname, GLubyte * data); +typedef void (APIENTRYP PFNGLGETUNSIGNEDBYTEI_VEXTPROC)(GLenum target, GLuint index, GLubyte * data); +typedef void (APIENTRYP PFNGLDELETEMEMORYOBJECTSEXTPROC)(GLsizei n, const GLuint * memoryObjects); +typedef GLboolean (APIENTRYP PFNGLISMEMORYOBJECTEXTPROC)(GLuint memoryObject); +typedef void (APIENTRYP PFNGLCREATEMEMORYOBJECTSEXTPROC)(GLsizei n, GLuint * memoryObjects); +typedef void (APIENTRYP PFNGLMEMORYOBJECTPARAMETERIVEXTPROC)(GLuint memoryObject, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLGETMEMORYOBJECTPARAMETERIVEXTPROC)(GLuint memoryObject, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLTEXSTORAGEMEM2DEXTPROC)(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXSTORAGEMEM2DMULTISAMPLEEXTPROC)(GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXSTORAGEMEM3DEXTPROC)(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXSTORAGEMEM3DMULTISAMPLEEXTPROC)(GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLBUFFERSTORAGEMEMEXTPROC)(GLenum target, GLsizeiptr size, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM2DEXTPROC)(GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM2DMULTISAMPLEEXTPROC)(GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM3DEXTPROC)(GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM3DMULTISAMPLEEXTPROC)(GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEMEMEXTPROC)(GLuint buffer, GLsizeiptr size, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXSTORAGEMEM1DEXTPROC)(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM1DEXTPROC)(GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLuint memory, GLuint64 offset); +#endif /* GL_EXT_memory_object */ + +#ifndef GL_EXT_memory_object_fd +#define GL_EXT_memory_object_fd 1 +#define GL_HANDLE_TYPE_OPAQUE_FD_EXT 0x9586 +typedef void (APIENTRYP PFNGLIMPORTMEMORYFDEXTPROC)(GLuint memory, GLuint64 size, GLenum handleType, GLint fd); +#endif /* GL_EXT_memory_object_fd */ + +#ifndef GL_EXT_memory_object_win32 +#define GL_EXT_memory_object_win32 1 +#define GL_HANDLE_TYPE_OPAQUE_WIN32_EXT 0x9587 +#define GL_HANDLE_TYPE_OPAQUE_WIN32_KMT_EXT 0x9588 +#define GL_DEVICE_LUID_EXT 0x9599 +#define GL_DEVICE_NODE_MASK_EXT 0x959A +#define GL_LUID_SIZE_EXT 8 +#define GL_HANDLE_TYPE_D3D12_TILEPOOL_EXT 0x9589 +#define GL_HANDLE_TYPE_D3D12_RESOURCE_EXT 0x958A +#define GL_HANDLE_TYPE_D3D11_IMAGE_EXT 0x958B +#define GL_HANDLE_TYPE_D3D11_IMAGE_KMT_EXT 0x958C +typedef void (APIENTRYP PFNGLIMPORTMEMORYWIN32HANDLEEXTPROC)(GLuint memory, GLuint64 size, GLenum handleType, void * handle); +typedef void (APIENTRYP PFNGLIMPORTMEMORYWIN32NAMEEXTPROC)(GLuint memory, GLuint64 size, GLenum handleType, const void * name); +#endif /* GL_EXT_memory_object_win32 */ + +#ifndef GL_EXT_misc_attribute +#define GL_EXT_misc_attribute 1 +#endif /* GL_EXT_misc_attribute */ + +#ifndef GL_EXT_multi_draw_arrays +#define GL_EXT_multi_draw_arrays 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC)(GLenum mode, const GLint * first, const GLsizei * count, GLsizei primcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC)(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei primcount); +#endif /* GL_EXT_multi_draw_arrays */ + +#ifndef GL_EXT_multisample +#define GL_EXT_multisample 1 +#define GL_MULTISAMPLE_EXT 0x809D +#define GL_SAMPLE_ALPHA_TO_MASK_EXT 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_EXT 0x809F +#define GL_SAMPLE_MASK_EXT 0x80A0 +#define GL_1PASS_EXT 0x80A1 +#define GL_2PASS_0_EXT 0x80A2 +#define GL_2PASS_1_EXT 0x80A3 +#define GL_4PASS_0_EXT 0x80A4 +#define GL_4PASS_1_EXT 0x80A5 +#define GL_4PASS_2_EXT 0x80A6 +#define GL_4PASS_3_EXT 0x80A7 +#define GL_SAMPLE_BUFFERS_EXT 0x80A8 +#define GL_SAMPLES_EXT 0x80A9 +#define GL_SAMPLE_MASK_VALUE_EXT 0x80AA +#define GL_SAMPLE_MASK_INVERT_EXT 0x80AB +#define GL_SAMPLE_PATTERN_EXT 0x80AC +#define GL_MULTISAMPLE_BIT_EXT 0x20000000 +typedef void (APIENTRYP PFNGLSAMPLEMASKEXTPROC)(GLclampf value, GLboolean invert); +typedef void (APIENTRYP PFNGLSAMPLEPATTERNEXTPROC)(GLenum pattern); +#endif /* GL_EXT_multisample */ + +#ifndef GL_EXT_multiview_tessellation_geometry_shader +#define GL_EXT_multiview_tessellation_geometry_shader 1 +#endif /* GL_EXT_multiview_tessellation_geometry_shader */ + +#ifndef GL_EXT_multiview_texture_multisample +#define GL_EXT_multiview_texture_multisample 1 +#endif /* GL_EXT_multiview_texture_multisample */ + +#ifndef GL_EXT_multiview_timer_query +#define GL_EXT_multiview_timer_query 1 +#endif /* GL_EXT_multiview_timer_query */ + +#ifndef GL_EXT_packed_depth_stencil +#define GL_EXT_packed_depth_stencil 1 +#define GL_DEPTH_STENCIL_EXT 0x84F9 +#define GL_UNSIGNED_INT_24_8_EXT 0x84FA +#define GL_DEPTH24_STENCIL8_EXT 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1 +#endif /* GL_EXT_packed_depth_stencil */ + +#ifndef GL_EXT_packed_float +#define GL_EXT_packed_float 1 +#define GL_R11F_G11F_B10F_EXT 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV_EXT 0x8C3B +#define GL_RGBA_SIGNED_COMPONENTS_EXT 0x8C3C +#endif /* GL_EXT_packed_float */ + +#ifndef GL_EXT_packed_pixels +#define GL_EXT_packed_pixels 1 +#define GL_UNSIGNED_BYTE_3_3_2_EXT 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4_EXT 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1_EXT 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8_EXT 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2_EXT 0x8036 +#endif /* GL_EXT_packed_pixels */ + +#ifndef GL_EXT_paletted_texture +#define GL_EXT_paletted_texture 1 +#define GL_COLOR_INDEX1_EXT 0x80E2 +#define GL_COLOR_INDEX2_EXT 0x80E3 +#define GL_COLOR_INDEX4_EXT 0x80E4 +#define GL_COLOR_INDEX8_EXT 0x80E5 +#define GL_COLOR_INDEX12_EXT 0x80E6 +#define GL_COLOR_INDEX16_EXT 0x80E7 +#define GL_TEXTURE_INDEX_SIZE_EXT 0x80ED +typedef void (APIENTRYP PFNGLCOLORTABLEEXTPROC)(GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const void * table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEEXTPROC)(GLenum target, GLenum format, GLenum type, void * data); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVEXTPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVEXTPROC)(GLenum target, GLenum pname, GLfloat * params); +#endif /* GL_EXT_paletted_texture */ + +#ifndef GL_EXT_pixel_buffer_object +#define GL_EXT_pixel_buffer_object 1 +#define GL_PIXEL_PACK_BUFFER_EXT 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_EXT 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_EXT 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_EXT 0x88EF +#endif /* GL_EXT_pixel_buffer_object */ + +#ifndef GL_EXT_pixel_transform +#define GL_EXT_pixel_transform 1 +#define GL_PIXEL_TRANSFORM_2D_EXT 0x8330 +#define GL_PIXEL_MAG_FILTER_EXT 0x8331 +#define GL_PIXEL_MIN_FILTER_EXT 0x8332 +#define GL_PIXEL_CUBIC_WEIGHT_EXT 0x8333 +#define GL_CUBIC_EXT 0x8334 +#define GL_AVERAGE_EXT 0x8335 +#define GL_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8336 +#define GL_MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8337 +#define GL_PIXEL_TRANSFORM_2D_MATRIX_EXT 0x8338 +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIEXTPROC)(GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFEXTPROC)(GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIVEXTPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFVEXTPROC)(GLenum target, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLGETPIXELTRANSFORMPARAMETERIVEXTPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETPIXELTRANSFORMPARAMETERFVEXTPROC)(GLenum target, GLenum pname, GLfloat * params); +#endif /* GL_EXT_pixel_transform */ + +#ifndef GL_EXT_pixel_transform_color_table +#define GL_EXT_pixel_transform_color_table 1 +#endif /* GL_EXT_pixel_transform_color_table */ + +#ifndef GL_EXT_point_parameters +#define GL_EXT_point_parameters 1 +#define GL_POINT_SIZE_MIN_EXT 0x8126 +#define GL_POINT_SIZE_MAX_EXT 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_EXT 0x8128 +#define GL_DISTANCE_ATTENUATION_EXT 0x8129 +typedef void (APIENTRYP PFNGLPOINTPARAMETERFEXTPROC)(GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVEXTPROC)(GLenum pname, const GLfloat * params); +#endif /* GL_EXT_point_parameters */ + +#ifndef GL_EXT_polygon_offset +#define GL_EXT_polygon_offset 1 +#define GL_POLYGON_OFFSET_EXT 0x8037 +#define GL_POLYGON_OFFSET_FACTOR_EXT 0x8038 +#define GL_POLYGON_OFFSET_BIAS_EXT 0x8039 +typedef void (APIENTRYP PFNGLPOLYGONOFFSETEXTPROC)(GLfloat factor, GLfloat bias); +#endif /* GL_EXT_polygon_offset */ + +#ifndef GL_EXT_polygon_offset_clamp +#define GL_EXT_polygon_offset_clamp 1 +#define GL_POLYGON_OFFSET_CLAMP_EXT 0x8E1B +typedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPEXTPROC)(GLfloat factor, GLfloat units, GLfloat clamp); +#endif /* GL_EXT_polygon_offset_clamp */ + +#ifndef GL_EXT_post_depth_coverage +#define GL_EXT_post_depth_coverage 1 +#endif /* GL_EXT_post_depth_coverage */ + +#ifndef GL_EXT_provoking_vertex +#define GL_EXT_provoking_vertex 1 +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION_EXT 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION_EXT 0x8E4D +#define GL_LAST_VERTEX_CONVENTION_EXT 0x8E4E +#define GL_PROVOKING_VERTEX_EXT 0x8E4F +typedef void (APIENTRYP PFNGLPROVOKINGVERTEXEXTPROC)(GLenum mode); +#endif /* GL_EXT_provoking_vertex */ + +#ifndef GL_EXT_raster_multisample +#define GL_EXT_raster_multisample 1 +#define GL_RASTER_MULTISAMPLE_EXT 0x9327 +#define GL_RASTER_SAMPLES_EXT 0x9328 +#define GL_MAX_RASTER_SAMPLES_EXT 0x9329 +#define GL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT 0x932A +#define GL_MULTISAMPLE_RASTERIZATION_ALLOWED_EXT 0x932B +#define GL_EFFECTIVE_RASTER_SAMPLES_EXT 0x932C +typedef void (APIENTRYP PFNGLRASTERSAMPLESEXTPROC)(GLuint samples, GLboolean fixedsamplelocations); +#endif /* GL_EXT_raster_multisample */ + +#ifndef GL_EXT_rescale_normal +#define GL_EXT_rescale_normal 1 +#define GL_RESCALE_NORMAL_EXT 0x803A +#endif /* GL_EXT_rescale_normal */ + +#ifndef GL_EXT_semaphore +#define GL_EXT_semaphore 1 +#define GL_NUM_DEVICE_UUIDS_EXT 0x9596 +#define GL_DEVICE_UUID_EXT 0x9597 +#define GL_DRIVER_UUID_EXT 0x9598 +#define GL_UUID_SIZE_EXT 16 +#define GL_LAYOUT_GENERAL_EXT 0x958D +#define GL_LAYOUT_COLOR_ATTACHMENT_EXT 0x958E +#define GL_LAYOUT_DEPTH_STENCIL_ATTACHMENT_EXT 0x958F +#define GL_LAYOUT_DEPTH_STENCIL_READ_ONLY_EXT 0x9590 +#define GL_LAYOUT_SHADER_READ_ONLY_EXT 0x9591 +#define GL_LAYOUT_TRANSFER_SRC_EXT 0x9592 +#define GL_LAYOUT_TRANSFER_DST_EXT 0x9593 +#define GL_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_EXT 0x9530 +#define GL_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_EXT 0x9531 +typedef void (APIENTRYP PFNGLGENSEMAPHORESEXTPROC)(GLsizei n, GLuint * semaphores); +typedef void (APIENTRYP PFNGLDELETESEMAPHORESEXTPROC)(GLsizei n, const GLuint * semaphores); +typedef GLboolean (APIENTRYP PFNGLISSEMAPHOREEXTPROC)(GLuint semaphore); +typedef void (APIENTRYP PFNGLSEMAPHOREPARAMETERUI64VEXTPROC)(GLuint semaphore, GLenum pname, const GLuint64 * params); +typedef void (APIENTRYP PFNGLGETSEMAPHOREPARAMETERUI64VEXTPROC)(GLuint semaphore, GLenum pname, GLuint64 * params); +typedef void (APIENTRYP PFNGLWAITSEMAPHOREEXTPROC)(GLuint semaphore, GLuint numBufferBarriers, const GLuint * buffers, GLuint numTextureBarriers, const GLuint * textures, const GLenum * srcLayouts); +typedef void (APIENTRYP PFNGLSIGNALSEMAPHOREEXTPROC)(GLuint semaphore, GLuint numBufferBarriers, const GLuint * buffers, GLuint numTextureBarriers, const GLuint * textures, const GLenum * dstLayouts); +#endif /* GL_EXT_semaphore */ + +#ifndef GL_EXT_semaphore_fd +#define GL_EXT_semaphore_fd 1 +#define GL_HANDLE_TYPE_OPAQUE_FD_EXT 0x9586 +typedef void (APIENTRYP PFNGLIMPORTSEMAPHOREFDEXTPROC)(GLuint semaphore, GLenum handleType, GLint fd); +#endif /* GL_EXT_semaphore_fd */ + +#ifndef GL_EXT_semaphore_win32 +#define GL_EXT_semaphore_win32 1 +#define GL_HANDLE_TYPE_OPAQUE_WIN32_EXT 0x9587 +#define GL_HANDLE_TYPE_OPAQUE_WIN32_KMT_EXT 0x9588 +#define GL_DEVICE_LUID_EXT 0x9599 +#define GL_DEVICE_NODE_MASK_EXT 0x959A +#define GL_LUID_SIZE_EXT 8 +#define GL_HANDLE_TYPE_D3D12_FENCE_EXT 0x9594 +#define GL_D3D12_FENCE_VALUE_EXT 0x9595 +typedef void (APIENTRYP PFNGLIMPORTSEMAPHOREWIN32HANDLEEXTPROC)(GLuint semaphore, GLenum handleType, void * handle); +typedef void (APIENTRYP PFNGLIMPORTSEMAPHOREWIN32NAMEEXTPROC)(GLuint semaphore, GLenum handleType, const void * name); +#endif /* GL_EXT_semaphore_win32 */ + +#ifndef GL_EXT_secondary_color +#define GL_EXT_secondary_color 1 +#define GL_COLOR_SUM_EXT 0x8458 +#define GL_CURRENT_SECONDARY_COLOR_EXT 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE_EXT 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE_EXT 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER_EXT 0x845D +#define GL_SECONDARY_COLOR_ARRAY_EXT 0x845E +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BEXTPROC)(GLbyte red, GLbyte green, GLbyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVEXTPROC)(const GLbyte * v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DEXTPROC)(GLdouble red, GLdouble green, GLdouble blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVEXTPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FEXTPROC)(GLfloat red, GLfloat green, GLfloat blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVEXTPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IEXTPROC)(GLint red, GLint green, GLint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVEXTPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SEXTPROC)(GLshort red, GLshort green, GLshort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVEXTPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBEXTPROC)(GLubyte red, GLubyte green, GLubyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVEXTPROC)(const GLubyte * v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIEXTPROC)(GLuint red, GLuint green, GLuint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVEXTPROC)(const GLuint * v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USEXTPROC)(GLushort red, GLushort green, GLushort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVEXTPROC)(const GLushort * v); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTEREXTPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); +#endif /* GL_EXT_secondary_color */ + +#ifndef GL_EXT_separate_shader_objects +#define GL_EXT_separate_shader_objects 1 +#define GL_ACTIVE_PROGRAM_EXT 0x8259 +#define GL_VERTEX_SHADER_BIT_EXT 0x00000001 +#define GL_FRAGMENT_SHADER_BIT_EXT 0x00000002 +#define GL_ALL_SHADER_BITS_EXT 0xFFFFFFFF +#define GL_PROGRAM_SEPARABLE_EXT 0x8258 +#define GL_ACTIVE_PROGRAM_EXT 0x8259 +#define GL_PROGRAM_PIPELINE_BINDING_EXT 0x825A +typedef void (APIENTRYP PFNGLUSESHADERPROGRAMEXTPROC)(GLenum type, GLuint program); +typedef void (APIENTRYP PFNGLACTIVEPROGRAMEXTPROC)(GLuint program); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMEXTPROC)(GLenum type, const GLchar * string); +typedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMEXTPROC)(GLuint pipeline, GLuint program); +typedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEEXTPROC)(GLuint pipeline); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVEXTPROC)(GLenum type, GLsizei count, const GLchar ** strings); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESEXTPROC)(GLsizei n, const GLuint * pipelines); +typedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESEXTPROC)(GLsizei n, GLuint * pipelines); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGEXTPROC)(GLuint pipeline, GLsizei bufSize, GLsizei * length, GLchar * infoLog); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVEXTPROC)(GLuint pipeline, GLenum pname, GLint * params); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEEXTPROC)(GLuint pipeline); +typedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESEXTPROC)(GLuint pipeline, GLbitfield stages, GLuint program); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEEXTPROC)(GLuint pipeline); +#endif /* GL_EXT_separate_shader_objects */ + +#ifndef GL_EXT_separate_specular_color +#define GL_EXT_separate_specular_color 1 +#define GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8 +#define GL_SINGLE_COLOR_EXT 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA +#endif /* GL_EXT_separate_specular_color */ + +#ifndef GL_EXT_shader_framebuffer_fetch +#define GL_EXT_shader_framebuffer_fetch 1 +#define GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT 0x8A52 +#endif /* GL_EXT_shader_framebuffer_fetch */ + +#ifndef GL_EXT_shader_framebuffer_fetch_non_coherent +#define GL_EXT_shader_framebuffer_fetch_non_coherent 1 +#define GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT 0x8A52 +typedef void (APIENTRYP PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC)(void); +#endif /* GL_EXT_shader_framebuffer_fetch_non_coherent */ + +#ifndef GL_EXT_shader_image_load_formatted +#define GL_EXT_shader_image_load_formatted 1 +#endif /* GL_EXT_shader_image_load_formatted */ + +#ifndef GL_EXT_shader_image_load_store +#define GL_EXT_shader_image_load_store 1 +#define GL_MAX_IMAGE_UNITS_EXT 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS_EXT 0x8F39 +#define GL_IMAGE_BINDING_NAME_EXT 0x8F3A +#define GL_IMAGE_BINDING_LEVEL_EXT 0x8F3B +#define GL_IMAGE_BINDING_LAYERED_EXT 0x8F3C +#define GL_IMAGE_BINDING_LAYER_EXT 0x8F3D +#define GL_IMAGE_BINDING_ACCESS_EXT 0x8F3E +#define GL_IMAGE_1D_EXT 0x904C +#define GL_IMAGE_2D_EXT 0x904D +#define GL_IMAGE_3D_EXT 0x904E +#define GL_IMAGE_2D_RECT_EXT 0x904F +#define GL_IMAGE_CUBE_EXT 0x9050 +#define GL_IMAGE_BUFFER_EXT 0x9051 +#define GL_IMAGE_1D_ARRAY_EXT 0x9052 +#define GL_IMAGE_2D_ARRAY_EXT 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY_EXT 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE_EXT 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9056 +#define GL_INT_IMAGE_1D_EXT 0x9057 +#define GL_INT_IMAGE_2D_EXT 0x9058 +#define GL_INT_IMAGE_3D_EXT 0x9059 +#define GL_INT_IMAGE_2D_RECT_EXT 0x905A +#define GL_INT_IMAGE_CUBE_EXT 0x905B +#define GL_INT_IMAGE_BUFFER_EXT 0x905C +#define GL_INT_IMAGE_1D_ARRAY_EXT 0x905D +#define GL_INT_IMAGE_2D_ARRAY_EXT 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE_EXT 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D_EXT 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D_EXT 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D_EXT 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT_EXT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE_EXT 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER_EXT 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY_EXT 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_EXT 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x906C +#define GL_MAX_IMAGE_SAMPLES_EXT 0x906D +#define GL_IMAGE_BINDING_FORMAT_EXT 0x906E +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT_EXT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT_EXT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT_EXT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT_EXT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT_EXT 0x00000020 +#define GL_COMMAND_BARRIER_BIT_EXT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT_EXT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT_EXT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT_EXT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT_EXT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT_EXT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT_EXT 0x00001000 +#define GL_ALL_BARRIER_BITS_EXT 0xFFFFFFFF +typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREEXTPROC)(GLuint index, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLint format); +typedef void (APIENTRYP PFNGLMEMORYBARRIEREXTPROC)(GLbitfield barriers); +#endif /* GL_EXT_shader_image_load_store */ + +#ifndef GL_EXT_shader_integer_mix +#define GL_EXT_shader_integer_mix 1 +#endif /* GL_EXT_shader_integer_mix */ + +#ifndef GL_EXT_shadow_funcs +#define GL_EXT_shadow_funcs 1 +#endif /* GL_EXT_shadow_funcs */ + +#ifndef GL_EXT_shared_texture_palette +#define GL_EXT_shared_texture_palette 1 +#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB +#endif /* GL_EXT_shared_texture_palette */ + +#ifndef GL_EXT_sparse_texture2 +#define GL_EXT_sparse_texture2 1 +#endif /* GL_EXT_sparse_texture2 */ + +#ifndef GL_EXT_stencil_clear_tag +#define GL_EXT_stencil_clear_tag 1 +#define GL_STENCIL_TAG_BITS_EXT 0x88F2 +#define GL_STENCIL_CLEAR_TAG_VALUE_EXT 0x88F3 +typedef void (APIENTRYP PFNGLSTENCILCLEARTAGEXTPROC)(GLsizei stencilTagBits, GLuint stencilClearTag); +#endif /* GL_EXT_stencil_clear_tag */ + +#ifndef GL_EXT_stencil_two_side +#define GL_EXT_stencil_two_side 1 +#define GL_STENCIL_TEST_TWO_SIDE_EXT 0x8910 +#define GL_ACTIVE_STENCIL_FACE_EXT 0x8911 +typedef void (APIENTRYP PFNGLACTIVESTENCILFACEEXTPROC)(GLenum face); +#endif /* GL_EXT_stencil_two_side */ + +#ifndef GL_EXT_stencil_wrap +#define GL_EXT_stencil_wrap 1 +#define GL_INCR_WRAP_EXT 0x8507 +#define GL_DECR_WRAP_EXT 0x8508 +#endif /* GL_EXT_stencil_wrap */ + +#ifndef GL_EXT_subtexture +#define GL_EXT_subtexture 1 +typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DEXTPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DEXTPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); +#endif /* GL_EXT_subtexture */ + +#ifndef GL_EXT_texture +#define GL_EXT_texture 1 +#define GL_ALPHA4_EXT 0x803B +#define GL_ALPHA8_EXT 0x803C +#define GL_ALPHA12_EXT 0x803D +#define GL_ALPHA16_EXT 0x803E +#define GL_LUMINANCE4_EXT 0x803F +#define GL_LUMINANCE8_EXT 0x8040 +#define GL_LUMINANCE12_EXT 0x8041 +#define GL_LUMINANCE16_EXT 0x8042 +#define GL_LUMINANCE4_ALPHA4_EXT 0x8043 +#define GL_LUMINANCE6_ALPHA2_EXT 0x8044 +#define GL_LUMINANCE8_ALPHA8_EXT 0x8045 +#define GL_LUMINANCE12_ALPHA4_EXT 0x8046 +#define GL_LUMINANCE12_ALPHA12_EXT 0x8047 +#define GL_LUMINANCE16_ALPHA16_EXT 0x8048 +#define GL_INTENSITY_EXT 0x8049 +#define GL_INTENSITY4_EXT 0x804A +#define GL_INTENSITY8_EXT 0x804B +#define GL_INTENSITY12_EXT 0x804C +#define GL_INTENSITY16_EXT 0x804D +#define GL_RGB2_EXT 0x804E +#define GL_RGB4_EXT 0x804F +#define GL_RGB5_EXT 0x8050 +#define GL_RGB8_EXT 0x8051 +#define GL_RGB10_EXT 0x8052 +#define GL_RGB12_EXT 0x8053 +#define GL_RGB16_EXT 0x8054 +#define GL_RGBA2_EXT 0x8055 +#define GL_RGBA4_EXT 0x8056 +#define GL_RGB5_A1_EXT 0x8057 +#define GL_RGBA8_EXT 0x8058 +#define GL_RGB10_A2_EXT 0x8059 +#define GL_RGBA12_EXT 0x805A +#define GL_RGBA16_EXT 0x805B +#define GL_TEXTURE_RED_SIZE_EXT 0x805C +#define GL_TEXTURE_GREEN_SIZE_EXT 0x805D +#define GL_TEXTURE_BLUE_SIZE_EXT 0x805E +#define GL_TEXTURE_ALPHA_SIZE_EXT 0x805F +#define GL_TEXTURE_LUMINANCE_SIZE_EXT 0x8060 +#define GL_TEXTURE_INTENSITY_SIZE_EXT 0x8061 +#define GL_REPLACE_EXT 0x8062 +#define GL_PROXY_TEXTURE_1D_EXT 0x8063 +#define GL_PROXY_TEXTURE_2D_EXT 0x8064 +#define GL_TEXTURE_TOO_LARGE_EXT 0x8065 +#endif /* GL_EXT_texture */ + +#ifndef GL_EXT_texture3D +#define GL_EXT_texture3D 1 +#define GL_PACK_SKIP_IMAGES_EXT 0x806B +#define GL_PACK_IMAGE_HEIGHT_EXT 0x806C +#define GL_UNPACK_SKIP_IMAGES_EXT 0x806D +#define GL_UNPACK_IMAGE_HEIGHT_EXT 0x806E +#define GL_TEXTURE_3D_EXT 0x806F +#define GL_PROXY_TEXTURE_3D_EXT 0x8070 +#define GL_TEXTURE_DEPTH_EXT 0x8071 +#define GL_TEXTURE_WRAP_R_EXT 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE_EXT 0x8073 +typedef void (APIENTRYP PFNGLTEXIMAGE3DEXTPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DEXTPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels); +#endif /* GL_EXT_texture3D */ + +#ifndef GL_EXT_texture_array +#define GL_EXT_texture_array 1 +#define GL_TEXTURE_1D_ARRAY_EXT 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY_EXT 0x8C19 +#define GL_TEXTURE_2D_ARRAY_EXT 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY_EXT 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY_EXT 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY_EXT 0x8C1D +#define GL_MAX_ARRAY_TEXTURE_LAYERS_EXT 0x88FF +#define GL_COMPARE_REF_DEPTH_TO_TEXTURE_EXT 0x884E +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4 +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYEREXTPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +#endif /* GL_EXT_texture_array */ + +#ifndef GL_EXT_texture_buffer_object +#define GL_EXT_texture_buffer_object 1 +#define GL_TEXTURE_BUFFER_EXT 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE_EXT 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER_EXT 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT_EXT 0x8C2E +typedef void (APIENTRYP PFNGLTEXBUFFEREXTPROC)(GLenum target, GLenum internalformat, GLuint buffer); +#endif /* GL_EXT_texture_buffer_object */ + +#ifndef GL_EXT_texture_compression_latc +#define GL_EXT_texture_compression_latc 1 +#define GL_COMPRESSED_LUMINANCE_LATC1_EXT 0x8C70 +#define GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT 0x8C71 +#define GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 +#define GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT 0x8C73 +#endif /* GL_EXT_texture_compression_latc */ + +#ifndef GL_EXT_texture_compression_rgtc +#define GL_EXT_texture_compression_rgtc 1 +#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC +#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD +#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE +#endif /* GL_EXT_texture_compression_rgtc */ + +#ifndef GL_EXT_texture_compression_s3tc +#define GL_EXT_texture_compression_s3tc 1 +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif /* GL_EXT_texture_compression_s3tc */ + +#ifndef GL_EXT_texture_cube_map +#define GL_EXT_texture_cube_map 1 +#define GL_NORMAL_MAP_EXT 0x8511 +#define GL_REFLECTION_MAP_EXT 0x8512 +#define GL_TEXTURE_CUBE_MAP_EXT 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_EXT 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_EXT 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_EXT 0x851C +#endif /* GL_EXT_texture_cube_map */ + +#ifndef GL_EXT_texture_env_add +#define GL_EXT_texture_env_add 1 +#endif /* GL_EXT_texture_env_add */ + +#ifndef GL_EXT_texture_env_combine +#define GL_EXT_texture_env_combine 1 +#define GL_COMBINE_EXT 0x8570 +#define GL_COMBINE_RGB_EXT 0x8571 +#define GL_COMBINE_ALPHA_EXT 0x8572 +#define GL_RGB_SCALE_EXT 0x8573 +#define GL_ADD_SIGNED_EXT 0x8574 +#define GL_INTERPOLATE_EXT 0x8575 +#define GL_CONSTANT_EXT 0x8576 +#define GL_PRIMARY_COLOR_EXT 0x8577 +#define GL_PREVIOUS_EXT 0x8578 +#define GL_SOURCE0_RGB_EXT 0x8580 +#define GL_SOURCE1_RGB_EXT 0x8581 +#define GL_SOURCE2_RGB_EXT 0x8582 +#define GL_SOURCE0_ALPHA_EXT 0x8588 +#define GL_SOURCE1_ALPHA_EXT 0x8589 +#define GL_SOURCE2_ALPHA_EXT 0x858A +#define GL_OPERAND0_RGB_EXT 0x8590 +#define GL_OPERAND1_RGB_EXT 0x8591 +#define GL_OPERAND2_RGB_EXT 0x8592 +#define GL_OPERAND0_ALPHA_EXT 0x8598 +#define GL_OPERAND1_ALPHA_EXT 0x8599 +#define GL_OPERAND2_ALPHA_EXT 0x859A +#endif /* GL_EXT_texture_env_combine */ + +#ifndef GL_EXT_texture_env_dot3 +#define GL_EXT_texture_env_dot3 1 +#define GL_DOT3_RGB_EXT 0x8740 +#define GL_DOT3_RGBA_EXT 0x8741 +#endif /* GL_EXT_texture_env_dot3 */ + +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_EXT_texture_filter_anisotropic 1 +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif /* GL_EXT_texture_filter_anisotropic */ + +#ifndef GL_EXT_texture_filter_minmax +#define GL_EXT_texture_filter_minmax 1 +#define GL_TEXTURE_REDUCTION_MODE_EXT 0x9366 +#define GL_WEIGHTED_AVERAGE_EXT 0x9367 +#endif /* GL_EXT_texture_filter_minmax */ + +#ifndef GL_EXT_texture_integer +#define GL_EXT_texture_integer 1 +#define GL_RGBA32UI_EXT 0x8D70 +#define GL_RGB32UI_EXT 0x8D71 +#define GL_ALPHA32UI_EXT 0x8D72 +#define GL_INTENSITY32UI_EXT 0x8D73 +#define GL_LUMINANCE32UI_EXT 0x8D74 +#define GL_LUMINANCE_ALPHA32UI_EXT 0x8D75 +#define GL_RGBA16UI_EXT 0x8D76 +#define GL_RGB16UI_EXT 0x8D77 +#define GL_ALPHA16UI_EXT 0x8D78 +#define GL_INTENSITY16UI_EXT 0x8D79 +#define GL_LUMINANCE16UI_EXT 0x8D7A +#define GL_LUMINANCE_ALPHA16UI_EXT 0x8D7B +#define GL_RGBA8UI_EXT 0x8D7C +#define GL_RGB8UI_EXT 0x8D7D +#define GL_ALPHA8UI_EXT 0x8D7E +#define GL_INTENSITY8UI_EXT 0x8D7F +#define GL_LUMINANCE8UI_EXT 0x8D80 +#define GL_LUMINANCE_ALPHA8UI_EXT 0x8D81 +#define GL_RGBA32I_EXT 0x8D82 +#define GL_RGB32I_EXT 0x8D83 +#define GL_ALPHA32I_EXT 0x8D84 +#define GL_INTENSITY32I_EXT 0x8D85 +#define GL_LUMINANCE32I_EXT 0x8D86 +#define GL_LUMINANCE_ALPHA32I_EXT 0x8D87 +#define GL_RGBA16I_EXT 0x8D88 +#define GL_RGB16I_EXT 0x8D89 +#define GL_ALPHA16I_EXT 0x8D8A +#define GL_INTENSITY16I_EXT 0x8D8B +#define GL_LUMINANCE16I_EXT 0x8D8C +#define GL_LUMINANCE_ALPHA16I_EXT 0x8D8D +#define GL_RGBA8I_EXT 0x8D8E +#define GL_RGB8I_EXT 0x8D8F +#define GL_ALPHA8I_EXT 0x8D90 +#define GL_INTENSITY8I_EXT 0x8D91 +#define GL_LUMINANCE8I_EXT 0x8D92 +#define GL_LUMINANCE_ALPHA8I_EXT 0x8D93 +#define GL_RED_INTEGER_EXT 0x8D94 +#define GL_GREEN_INTEGER_EXT 0x8D95 +#define GL_BLUE_INTEGER_EXT 0x8D96 +#define GL_ALPHA_INTEGER_EXT 0x8D97 +#define GL_RGB_INTEGER_EXT 0x8D98 +#define GL_RGBA_INTEGER_EXT 0x8D99 +#define GL_BGR_INTEGER_EXT 0x8D9A +#define GL_BGRA_INTEGER_EXT 0x8D9B +#define GL_LUMINANCE_INTEGER_EXT 0x8D9C +#define GL_LUMINANCE_ALPHA_INTEGER_EXT 0x8D9D +#define GL_RGBA_INTEGER_MODE_EXT 0x8D9E +typedef void (APIENTRYP PFNGLTEXPARAMETERIIVEXTPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVEXTPROC)(GLenum target, GLenum pname, const GLuint * params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVEXTPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVEXTPROC)(GLenum target, GLenum pname, GLuint * params); +typedef void (APIENTRYP PFNGLCLEARCOLORIIEXTPROC)(GLint red, GLint green, GLint blue, GLint alpha); +typedef void (APIENTRYP PFNGLCLEARCOLORIUIEXTPROC)(GLuint red, GLuint green, GLuint blue, GLuint alpha); +#endif /* GL_EXT_texture_integer */ + +#ifndef GL_EXT_texture_lod_bias +#define GL_EXT_texture_lod_bias 1 +#define GL_MAX_TEXTURE_LOD_BIAS_EXT 0x84FD +#define GL_TEXTURE_FILTER_CONTROL_EXT 0x8500 +#define GL_TEXTURE_LOD_BIAS_EXT 0x8501 +#endif /* GL_EXT_texture_lod_bias */ + +#ifndef GL_EXT_texture_mirror_clamp +#define GL_EXT_texture_mirror_clamp 1 +#define GL_MIRROR_CLAMP_EXT 0x8742 +#define GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743 +#define GL_MIRROR_CLAMP_TO_BORDER_EXT 0x8912 +#endif /* GL_EXT_texture_mirror_clamp */ + +#ifndef GL_EXT_texture_object +#define GL_EXT_texture_object 1 +#define GL_TEXTURE_PRIORITY_EXT 0x8066 +#define GL_TEXTURE_RESIDENT_EXT 0x8067 +#define GL_TEXTURE_1D_BINDING_EXT 0x8068 +#define GL_TEXTURE_2D_BINDING_EXT 0x8069 +#define GL_TEXTURE_3D_BINDING_EXT 0x806A +typedef GLboolean (APIENTRYP PFNGLARETEXTURESRESIDENTEXTPROC)(GLsizei n, const GLuint * textures, GLboolean * residences); +typedef void (APIENTRYP PFNGLBINDTEXTUREEXTPROC)(GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLDELETETEXTURESEXTPROC)(GLsizei n, const GLuint * textures); +typedef void (APIENTRYP PFNGLGENTEXTURESEXTPROC)(GLsizei n, GLuint * textures); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREEXTPROC)(GLuint texture); +typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESEXTPROC)(GLsizei n, const GLuint * textures, const GLclampf * priorities); +#endif /* GL_EXT_texture_object */ + +#ifndef GL_EXT_texture_perturb_normal +#define GL_EXT_texture_perturb_normal 1 +#define GL_PERTURB_EXT 0x85AE +#define GL_TEXTURE_NORMAL_EXT 0x85AF +typedef void (APIENTRYP PFNGLTEXTURENORMALEXTPROC)(GLenum mode); +#endif /* GL_EXT_texture_perturb_normal */ + +#ifndef GL_EXT_texture_sRGB +#define GL_EXT_texture_sRGB 1 +#define GL_SRGB_EXT 0x8C40 +#define GL_SRGB8_EXT 0x8C41 +#define GL_SRGB_ALPHA_EXT 0x8C42 +#define GL_SRGB8_ALPHA8_EXT 0x8C43 +#define GL_SLUMINANCE_ALPHA_EXT 0x8C44 +#define GL_SLUMINANCE8_ALPHA8_EXT 0x8C45 +#define GL_SLUMINANCE_EXT 0x8C46 +#define GL_SLUMINANCE8_EXT 0x8C47 +#define GL_COMPRESSED_SRGB_EXT 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA_EXT 0x8C49 +#define GL_COMPRESSED_SLUMINANCE_EXT 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA_EXT 0x8C4B +#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F +#endif /* GL_EXT_texture_sRGB */ + +#ifndef GL_EXT_texture_sRGB_R8 +#define GL_EXT_texture_sRGB_R8 1 +#define GL_SR8_EXT 0x8FBD +#endif /* GL_EXT_texture_sRGB_R8 */ + +#ifndef GL_EXT_texture_sRGB_decode +#define GL_EXT_texture_sRGB_decode 1 +#define GL_TEXTURE_SRGB_DECODE_EXT 0x8A48 +#define GL_DECODE_EXT 0x8A49 +#define GL_SKIP_DECODE_EXT 0x8A4A +#endif /* GL_EXT_texture_sRGB_decode */ + +#ifndef GL_EXT_texture_shared_exponent +#define GL_EXT_texture_shared_exponent 1 +#define GL_RGB9_E5_EXT 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV_EXT 0x8C3E +#define GL_TEXTURE_SHARED_SIZE_EXT 0x8C3F +#endif /* GL_EXT_texture_shared_exponent */ + +#ifndef GL_EXT_texture_snorm +#define GL_EXT_texture_snorm 1 +#define GL_ALPHA_SNORM 0x9010 +#define GL_LUMINANCE_SNORM 0x9011 +#define GL_LUMINANCE_ALPHA_SNORM 0x9012 +#define GL_INTENSITY_SNORM 0x9013 +#define GL_ALPHA8_SNORM 0x9014 +#define GL_LUMINANCE8_SNORM 0x9015 +#define GL_LUMINANCE8_ALPHA8_SNORM 0x9016 +#define GL_INTENSITY8_SNORM 0x9017 +#define GL_ALPHA16_SNORM 0x9018 +#define GL_LUMINANCE16_SNORM 0x9019 +#define GL_LUMINANCE16_ALPHA16_SNORM 0x901A +#define GL_INTENSITY16_SNORM 0x901B +#define GL_RED_SNORM 0x8F90 +#define GL_RG_SNORM 0x8F91 +#define GL_RGB_SNORM 0x8F92 +#define GL_RGBA_SNORM 0x8F93 +#define GL_R8_SNORM 0x8F94 +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB8_SNORM 0x8F96 +#define GL_RGBA8_SNORM 0x8F97 +#define GL_R16_SNORM 0x8F98 +#define GL_RG16_SNORM 0x8F99 +#define GL_RGB16_SNORM 0x8F9A +#define GL_RGBA16_SNORM 0x8F9B +#define GL_SIGNED_NORMALIZED 0x8F9C +#endif /* GL_EXT_texture_snorm */ + +#ifndef GL_EXT_texture_swizzle +#define GL_EXT_texture_swizzle 1 +#define GL_TEXTURE_SWIZZLE_R_EXT 0x8E42 +#define GL_TEXTURE_SWIZZLE_G_EXT 0x8E43 +#define GL_TEXTURE_SWIZZLE_B_EXT 0x8E44 +#define GL_TEXTURE_SWIZZLE_A_EXT 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA_EXT 0x8E46 +#endif /* GL_EXT_texture_swizzle */ + +#ifndef GL_NV_timeline_semaphore +#define GL_NV_timeline_semaphore 1 +#define GL_TIMELINE_SEMAPHORE_VALUE_NV 0x9595 +#define GL_SEMAPHORE_TYPE_NV 0x95B3 +#define GL_SEMAPHORE_TYPE_BINARY_NV 0x95B4 +#define GL_SEMAPHORE_TYPE_TIMELINE_NV 0x95B5 +#define GL_MAX_TIMELINE_SEMAPHORE_VALUE_DIFFERENCE_NV 0x95B6 +typedef void (APIENTRYP PFNGLCREATESEMAPHORESNVPROC)(GLsizei n, GLuint * semaphores); +typedef void (APIENTRYP PFNGLSEMAPHOREPARAMETERIVNVPROC)(GLuint semaphore, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLGETSEMAPHOREPARAMETERIVNVPROC)(GLuint semaphore, GLenum pname, GLint * params); +#endif /* GL_NV_timeline_semaphore */ + +#ifndef GL_EXT_timer_query +#define GL_EXT_timer_query 1 +#define GL_TIME_ELAPSED_EXT 0x88BF +typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VEXTPROC)(GLuint id, GLenum pname, GLint64 * params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VEXTPROC)(GLuint id, GLenum pname, GLuint64 * params); +#endif /* GL_EXT_timer_query */ + +#ifndef GL_EXT_transform_feedback +#define GL_EXT_transform_feedback 1 +#define GL_TRANSFORM_FEEDBACK_BUFFER_EXT 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_START_EXT 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_EXT 0x8C85 +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT 0x8C8F +#define GL_INTERLEAVED_ATTRIBS_EXT 0x8C8C +#define GL_SEPARATE_ATTRIBS_EXT 0x8C8D +#define GL_PRIMITIVES_GENERATED_EXT 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_EXT 0x8C88 +#define GL_RASTERIZER_DISCARD_EXT 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT 0x8C8B +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS_EXT 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_EXT 0x8C7F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH_EXT 0x8C76 +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKEXTPROC)(GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKEXTPROC)(void); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGEEXTPROC)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETEXTPROC)(GLenum target, GLuint index, GLuint buffer, GLintptr offset); +typedef void (APIENTRYP PFNGLBINDBUFFERBASEEXTPROC)(GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSEXTPROC)(GLuint program, GLsizei count, const GLchar *const* varyings, GLenum bufferMode); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGEXTPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLsizei * size, GLenum * type, GLchar * name); +#endif /* GL_EXT_transform_feedback */ + +#ifndef GL_EXT_vertex_array +#define GL_EXT_vertex_array 1 +#define GL_VERTEX_ARRAY_EXT 0x8074 +#define GL_NORMAL_ARRAY_EXT 0x8075 +#define GL_COLOR_ARRAY_EXT 0x8076 +#define GL_INDEX_ARRAY_EXT 0x8077 +#define GL_TEXTURE_COORD_ARRAY_EXT 0x8078 +#define GL_EDGE_FLAG_ARRAY_EXT 0x8079 +#define GL_VERTEX_ARRAY_SIZE_EXT 0x807A +#define GL_VERTEX_ARRAY_TYPE_EXT 0x807B +#define GL_VERTEX_ARRAY_STRIDE_EXT 0x807C +#define GL_VERTEX_ARRAY_COUNT_EXT 0x807D +#define GL_NORMAL_ARRAY_TYPE_EXT 0x807E +#define GL_NORMAL_ARRAY_STRIDE_EXT 0x807F +#define GL_NORMAL_ARRAY_COUNT_EXT 0x8080 +#define GL_COLOR_ARRAY_SIZE_EXT 0x8081 +#define GL_COLOR_ARRAY_TYPE_EXT 0x8082 +#define GL_COLOR_ARRAY_STRIDE_EXT 0x8083 +#define GL_COLOR_ARRAY_COUNT_EXT 0x8084 +#define GL_INDEX_ARRAY_TYPE_EXT 0x8085 +#define GL_INDEX_ARRAY_STRIDE_EXT 0x8086 +#define GL_INDEX_ARRAY_COUNT_EXT 0x8087 +#define GL_TEXTURE_COORD_ARRAY_SIZE_EXT 0x8088 +#define GL_TEXTURE_COORD_ARRAY_TYPE_EXT 0x8089 +#define GL_TEXTURE_COORD_ARRAY_STRIDE_EXT 0x808A +#define GL_TEXTURE_COORD_ARRAY_COUNT_EXT 0x808B +#define GL_EDGE_FLAG_ARRAY_STRIDE_EXT 0x808C +#define GL_EDGE_FLAG_ARRAY_COUNT_EXT 0x808D +#define GL_VERTEX_ARRAY_POINTER_EXT 0x808E +#define GL_NORMAL_ARRAY_POINTER_EXT 0x808F +#define GL_COLOR_ARRAY_POINTER_EXT 0x8090 +#define GL_INDEX_ARRAY_POINTER_EXT 0x8091 +#define GL_TEXTURE_COORD_ARRAY_POINTER_EXT 0x8092 +#define GL_EDGE_FLAG_ARRAY_POINTER_EXT 0x8093 +typedef void (APIENTRYP PFNGLARRAYELEMENTEXTPROC)(GLint i); +typedef void (APIENTRYP PFNGLCOLORPOINTEREXTPROC)(GLint size, GLenum type, GLsizei stride, GLsizei count, const void * pointer); +typedef void (APIENTRYP PFNGLDRAWARRAYSEXTPROC)(GLenum mode, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLEDGEFLAGPOINTEREXTPROC)(GLsizei stride, GLsizei count, const GLboolean * pointer); +typedef void (APIENTRYP PFNGLGETPOINTERVEXTPROC)(GLenum pname, void ** params); +typedef void (APIENTRYP PFNGLINDEXPOINTEREXTPROC)(GLenum type, GLsizei stride, GLsizei count, const void * pointer); +typedef void (APIENTRYP PFNGLNORMALPOINTEREXTPROC)(GLenum type, GLsizei stride, GLsizei count, const void * pointer); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTEREXTPROC)(GLint size, GLenum type, GLsizei stride, GLsizei count, const void * pointer); +typedef void (APIENTRYP PFNGLVERTEXPOINTEREXTPROC)(GLint size, GLenum type, GLsizei stride, GLsizei count, const void * pointer); +#endif /* GL_EXT_vertex_array */ + +#ifndef GL_EXT_vertex_array_bgra +#define GL_EXT_vertex_array_bgra 1 +#define GL_BGRA 0x80E1 +#endif /* GL_EXT_vertex_array_bgra */ + +#ifndef GL_EXT_vertex_attrib_64bit +#define GL_EXT_vertex_attrib_64bit 1 +#define GL_DOUBLE 0x140A +#define GL_DOUBLE_VEC2_EXT 0x8FFC +#define GL_DOUBLE_VEC3_EXT 0x8FFD +#define GL_DOUBLE_VEC4_EXT 0x8FFE +#define GL_DOUBLE_MAT2_EXT 0x8F46 +#define GL_DOUBLE_MAT3_EXT 0x8F47 +#define GL_DOUBLE_MAT4_EXT 0x8F48 +#define GL_DOUBLE_MAT2x3_EXT 0x8F49 +#define GL_DOUBLE_MAT2x4_EXT 0x8F4A +#define GL_DOUBLE_MAT3x2_EXT 0x8F4B +#define GL_DOUBLE_MAT3x4_EXT 0x8F4C +#define GL_DOUBLE_MAT4x2_EXT 0x8F4D +#define GL_DOUBLE_MAT4x3_EXT 0x8F4E +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DEXTPROC)(GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DEXTPROC)(GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DEXTPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DEXTPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVEXTPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVEXTPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVEXTPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVEXTPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTEREXTPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVEXTPROC)(GLuint index, GLenum pname, GLdouble * params); +#endif /* GL_EXT_vertex_attrib_64bit */ + +#ifndef GL_EXT_vertex_shader +#define GL_EXT_vertex_shader 1 +#define GL_VERTEX_SHADER_EXT 0x8780 +#define GL_VERTEX_SHADER_BINDING_EXT 0x8781 +#define GL_OP_INDEX_EXT 0x8782 +#define GL_OP_NEGATE_EXT 0x8783 +#define GL_OP_DOT3_EXT 0x8784 +#define GL_OP_DOT4_EXT 0x8785 +#define GL_OP_MUL_EXT 0x8786 +#define GL_OP_ADD_EXT 0x8787 +#define GL_OP_MADD_EXT 0x8788 +#define GL_OP_FRAC_EXT 0x8789 +#define GL_OP_MAX_EXT 0x878A +#define GL_OP_MIN_EXT 0x878B +#define GL_OP_SET_GE_EXT 0x878C +#define GL_OP_SET_LT_EXT 0x878D +#define GL_OP_CLAMP_EXT 0x878E +#define GL_OP_FLOOR_EXT 0x878F +#define GL_OP_ROUND_EXT 0x8790 +#define GL_OP_EXP_BASE_2_EXT 0x8791 +#define GL_OP_LOG_BASE_2_EXT 0x8792 +#define GL_OP_POWER_EXT 0x8793 +#define GL_OP_RECIP_EXT 0x8794 +#define GL_OP_RECIP_SQRT_EXT 0x8795 +#define GL_OP_SUB_EXT 0x8796 +#define GL_OP_CROSS_PRODUCT_EXT 0x8797 +#define GL_OP_MULTIPLY_MATRIX_EXT 0x8798 +#define GL_OP_MOV_EXT 0x8799 +#define GL_OUTPUT_VERTEX_EXT 0x879A +#define GL_OUTPUT_COLOR0_EXT 0x879B +#define GL_OUTPUT_COLOR1_EXT 0x879C +#define GL_OUTPUT_TEXTURE_COORD0_EXT 0x879D +#define GL_OUTPUT_TEXTURE_COORD1_EXT 0x879E +#define GL_OUTPUT_TEXTURE_COORD2_EXT 0x879F +#define GL_OUTPUT_TEXTURE_COORD3_EXT 0x87A0 +#define GL_OUTPUT_TEXTURE_COORD4_EXT 0x87A1 +#define GL_OUTPUT_TEXTURE_COORD5_EXT 0x87A2 +#define GL_OUTPUT_TEXTURE_COORD6_EXT 0x87A3 +#define GL_OUTPUT_TEXTURE_COORD7_EXT 0x87A4 +#define GL_OUTPUT_TEXTURE_COORD8_EXT 0x87A5 +#define GL_OUTPUT_TEXTURE_COORD9_EXT 0x87A6 +#define GL_OUTPUT_TEXTURE_COORD10_EXT 0x87A7 +#define GL_OUTPUT_TEXTURE_COORD11_EXT 0x87A8 +#define GL_OUTPUT_TEXTURE_COORD12_EXT 0x87A9 +#define GL_OUTPUT_TEXTURE_COORD13_EXT 0x87AA +#define GL_OUTPUT_TEXTURE_COORD14_EXT 0x87AB +#define GL_OUTPUT_TEXTURE_COORD15_EXT 0x87AC +#define GL_OUTPUT_TEXTURE_COORD16_EXT 0x87AD +#define GL_OUTPUT_TEXTURE_COORD17_EXT 0x87AE +#define GL_OUTPUT_TEXTURE_COORD18_EXT 0x87AF +#define GL_OUTPUT_TEXTURE_COORD19_EXT 0x87B0 +#define GL_OUTPUT_TEXTURE_COORD20_EXT 0x87B1 +#define GL_OUTPUT_TEXTURE_COORD21_EXT 0x87B2 +#define GL_OUTPUT_TEXTURE_COORD22_EXT 0x87B3 +#define GL_OUTPUT_TEXTURE_COORD23_EXT 0x87B4 +#define GL_OUTPUT_TEXTURE_COORD24_EXT 0x87B5 +#define GL_OUTPUT_TEXTURE_COORD25_EXT 0x87B6 +#define GL_OUTPUT_TEXTURE_COORD26_EXT 0x87B7 +#define GL_OUTPUT_TEXTURE_COORD27_EXT 0x87B8 +#define GL_OUTPUT_TEXTURE_COORD28_EXT 0x87B9 +#define GL_OUTPUT_TEXTURE_COORD29_EXT 0x87BA +#define GL_OUTPUT_TEXTURE_COORD30_EXT 0x87BB +#define GL_OUTPUT_TEXTURE_COORD31_EXT 0x87BC +#define GL_OUTPUT_FOG_EXT 0x87BD +#define GL_SCALAR_EXT 0x87BE +#define GL_VECTOR_EXT 0x87BF +#define GL_MATRIX_EXT 0x87C0 +#define GL_VARIANT_EXT 0x87C1 +#define GL_INVARIANT_EXT 0x87C2 +#define GL_LOCAL_CONSTANT_EXT 0x87C3 +#define GL_LOCAL_EXT 0x87C4 +#define GL_MAX_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87C5 +#define GL_MAX_VERTEX_SHADER_VARIANTS_EXT 0x87C6 +#define GL_MAX_VERTEX_SHADER_INVARIANTS_EXT 0x87C7 +#define GL_MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87C8 +#define GL_MAX_VERTEX_SHADER_LOCALS_EXT 0x87C9 +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CA +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT 0x87CB +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87CC +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT 0x87CD +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT 0x87CE +#define GL_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CF +#define GL_VERTEX_SHADER_VARIANTS_EXT 0x87D0 +#define GL_VERTEX_SHADER_INVARIANTS_EXT 0x87D1 +#define GL_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87D2 +#define GL_VERTEX_SHADER_LOCALS_EXT 0x87D3 +#define GL_VERTEX_SHADER_OPTIMIZED_EXT 0x87D4 +#define GL_X_EXT 0x87D5 +#define GL_Y_EXT 0x87D6 +#define GL_Z_EXT 0x87D7 +#define GL_W_EXT 0x87D8 +#define GL_NEGATIVE_X_EXT 0x87D9 +#define GL_NEGATIVE_Y_EXT 0x87DA +#define GL_NEGATIVE_Z_EXT 0x87DB +#define GL_NEGATIVE_W_EXT 0x87DC +#define GL_ZERO_EXT 0x87DD +#define GL_ONE_EXT 0x87DE +#define GL_NEGATIVE_ONE_EXT 0x87DF +#define GL_NORMALIZED_RANGE_EXT 0x87E0 +#define GL_FULL_RANGE_EXT 0x87E1 +#define GL_CURRENT_VERTEX_EXT 0x87E2 +#define GL_MVP_MATRIX_EXT 0x87E3 +#define GL_VARIANT_VALUE_EXT 0x87E4 +#define GL_VARIANT_DATATYPE_EXT 0x87E5 +#define GL_VARIANT_ARRAY_STRIDE_EXT 0x87E6 +#define GL_VARIANT_ARRAY_TYPE_EXT 0x87E7 +#define GL_VARIANT_ARRAY_EXT 0x87E8 +#define GL_VARIANT_ARRAY_POINTER_EXT 0x87E9 +#define GL_INVARIANT_VALUE_EXT 0x87EA +#define GL_INVARIANT_DATATYPE_EXT 0x87EB +#define GL_LOCAL_CONSTANT_VALUE_EXT 0x87EC +#define GL_LOCAL_CONSTANT_DATATYPE_EXT 0x87ED +typedef void (APIENTRYP PFNGLBEGINVERTEXSHADEREXTPROC)(void); +typedef void (APIENTRYP PFNGLENDVERTEXSHADEREXTPROC)(void); +typedef void (APIENTRYP PFNGLBINDVERTEXSHADEREXTPROC)(GLuint id); +typedef GLuint (APIENTRYP PFNGLGENVERTEXSHADERSEXTPROC)(GLuint range); +typedef void (APIENTRYP PFNGLDELETEVERTEXSHADEREXTPROC)(GLuint id); +typedef void (APIENTRYP PFNGLSHADEROP1EXTPROC)(GLenum op, GLuint res, GLuint arg1); +typedef void (APIENTRYP PFNGLSHADEROP2EXTPROC)(GLenum op, GLuint res, GLuint arg1, GLuint arg2); +typedef void (APIENTRYP PFNGLSHADEROP3EXTPROC)(GLenum op, GLuint res, GLuint arg1, GLuint arg2, GLuint arg3); +typedef void (APIENTRYP PFNGLSWIZZLEEXTPROC)(GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +typedef void (APIENTRYP PFNGLWRITEMASKEXTPROC)(GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +typedef void (APIENTRYP PFNGLINSERTCOMPONENTEXTPROC)(GLuint res, GLuint src, GLuint num); +typedef void (APIENTRYP PFNGLEXTRACTCOMPONENTEXTPROC)(GLuint res, GLuint src, GLuint num); +typedef GLuint (APIENTRYP PFNGLGENSYMBOLSEXTPROC)(GLenum datatype, GLenum storagetype, GLenum range, GLuint components); +typedef void (APIENTRYP PFNGLSETINVARIANTEXTPROC)(GLuint id, GLenum type, const void * addr); +typedef void (APIENTRYP PFNGLSETLOCALCONSTANTEXTPROC)(GLuint id, GLenum type, const void * addr); +typedef void (APIENTRYP PFNGLVARIANTBVEXTPROC)(GLuint id, const GLbyte * addr); +typedef void (APIENTRYP PFNGLVARIANTSVEXTPROC)(GLuint id, const GLshort * addr); +typedef void (APIENTRYP PFNGLVARIANTIVEXTPROC)(GLuint id, const GLint * addr); +typedef void (APIENTRYP PFNGLVARIANTFVEXTPROC)(GLuint id, const GLfloat * addr); +typedef void (APIENTRYP PFNGLVARIANTDVEXTPROC)(GLuint id, const GLdouble * addr); +typedef void (APIENTRYP PFNGLVARIANTUBVEXTPROC)(GLuint id, const GLubyte * addr); +typedef void (APIENTRYP PFNGLVARIANTUSVEXTPROC)(GLuint id, const GLushort * addr); +typedef void (APIENTRYP PFNGLVARIANTUIVEXTPROC)(GLuint id, const GLuint * addr); +typedef void (APIENTRYP PFNGLVARIANTPOINTEREXTPROC)(GLuint id, GLenum type, GLuint stride, const void * addr); +typedef void (APIENTRYP PFNGLENABLEVARIANTCLIENTSTATEEXTPROC)(GLuint id); +typedef void (APIENTRYP PFNGLDISABLEVARIANTCLIENTSTATEEXTPROC)(GLuint id); +typedef GLuint (APIENTRYP PFNGLBINDLIGHTPARAMETEREXTPROC)(GLenum light, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDMATERIALPARAMETEREXTPROC)(GLenum face, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDTEXGENPARAMETEREXTPROC)(GLenum unit, GLenum coord, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDTEXTUREUNITPARAMETEREXTPROC)(GLenum unit, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDPARAMETEREXTPROC)(GLenum value); +typedef GLboolean (APIENTRYP PFNGLISVARIANTENABLEDEXTPROC)(GLuint id, GLenum cap); +typedef void (APIENTRYP PFNGLGETVARIANTBOOLEANVEXTPROC)(GLuint id, GLenum value, GLboolean * data); +typedef void (APIENTRYP PFNGLGETVARIANTINTEGERVEXTPROC)(GLuint id, GLenum value, GLint * data); +typedef void (APIENTRYP PFNGLGETVARIANTFLOATVEXTPROC)(GLuint id, GLenum value, GLfloat * data); +typedef void (APIENTRYP PFNGLGETVARIANTPOINTERVEXTPROC)(GLuint id, GLenum value, void ** data); +typedef void (APIENTRYP PFNGLGETINVARIANTBOOLEANVEXTPROC)(GLuint id, GLenum value, GLboolean * data); +typedef void (APIENTRYP PFNGLGETINVARIANTINTEGERVEXTPROC)(GLuint id, GLenum value, GLint * data); +typedef void (APIENTRYP PFNGLGETINVARIANTFLOATVEXTPROC)(GLuint id, GLenum value, GLfloat * data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTBOOLEANVEXTPROC)(GLuint id, GLenum value, GLboolean * data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTINTEGERVEXTPROC)(GLuint id, GLenum value, GLint * data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTFLOATVEXTPROC)(GLuint id, GLenum value, GLfloat * data); +#endif /* GL_EXT_vertex_shader */ + +#ifndef GL_EXT_vertex_weighting +#define GL_EXT_vertex_weighting 1 +#define GL_MODELVIEW0_STACK_DEPTH_EXT 0x0BA3 +#define GL_MODELVIEW1_STACK_DEPTH_EXT 0x8502 +#define GL_MODELVIEW0_MATRIX_EXT 0x0BA6 +#define GL_MODELVIEW1_MATRIX_EXT 0x8506 +#define GL_VERTEX_WEIGHTING_EXT 0x8509 +#define GL_MODELVIEW0_EXT 0x1700 +#define GL_MODELVIEW1_EXT 0x850A +#define GL_CURRENT_VERTEX_WEIGHT_EXT 0x850B +#define GL_VERTEX_WEIGHT_ARRAY_EXT 0x850C +#define GL_VERTEX_WEIGHT_ARRAY_SIZE_EXT 0x850D +#define GL_VERTEX_WEIGHT_ARRAY_TYPE_EXT 0x850E +#define GL_VERTEX_WEIGHT_ARRAY_STRIDE_EXT 0x850F +#define GL_VERTEX_WEIGHT_ARRAY_POINTER_EXT 0x8510 +typedef void (APIENTRYP PFNGLVERTEXWEIGHTFEXTPROC)(GLfloat weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTFVEXTPROC)(const GLfloat * weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTPOINTEREXTPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); +#endif /* GL_EXT_vertex_weighting */ + +#ifndef GL_EXT_win32_keyed_mutex +#define GL_EXT_win32_keyed_mutex 1 +typedef GLboolean (APIENTRYP PFNGLACQUIREKEYEDMUTEXWIN32EXTPROC)(GLuint memory, GLuint64 key, GLuint timeout); +typedef GLboolean (APIENTRYP PFNGLRELEASEKEYEDMUTEXWIN32EXTPROC)(GLuint memory, GLuint64 key); +#endif /* GL_EXT_win32_keyed_mutex */ + +#ifndef GL_EXT_window_rectangles +#define GL_EXT_window_rectangles 1 +#define GL_INCLUSIVE_EXT 0x8F10 +#define GL_EXCLUSIVE_EXT 0x8F11 +#define GL_WINDOW_RECTANGLE_EXT 0x8F12 +#define GL_WINDOW_RECTANGLE_MODE_EXT 0x8F13 +#define GL_MAX_WINDOW_RECTANGLES_EXT 0x8F14 +#define GL_NUM_WINDOW_RECTANGLES_EXT 0x8F15 +typedef void (APIENTRYP PFNGLWINDOWRECTANGLESEXTPROC)(GLenum mode, GLsizei count, const GLint * box); +#endif /* GL_EXT_window_rectangles */ + +#ifndef GL_EXT_x11_sync_object +#define GL_EXT_x11_sync_object 1 +#define GL_SYNC_X11_FENCE_EXT 0x90E1 +typedef GLsync (APIENTRYP PFNGLIMPORTSYNCEXTPROC)(GLenum external_sync_type, GLintptr external_sync, GLbitfield flags); +#endif /* GL_EXT_x11_sync_object */ + +#ifndef GL_GREMEDY_frame_terminator +#define GL_GREMEDY_frame_terminator 1 +typedef void (APIENTRYP PFNGLFRAMETERMINATORGREMEDYPROC)(void); +#endif /* GL_GREMEDY_frame_terminator */ + +#ifndef GL_GREMEDY_string_marker +#define GL_GREMEDY_string_marker 1 +typedef void (APIENTRYP PFNGLSTRINGMARKERGREMEDYPROC)(GLsizei len, const void * string); +#endif /* GL_GREMEDY_string_marker */ + +#ifndef GL_HP_convolution_border_modes +#define GL_HP_convolution_border_modes 1 +#define GL_IGNORE_BORDER_HP 0x8150 +#define GL_CONSTANT_BORDER_HP 0x8151 +#define GL_REPLICATE_BORDER_HP 0x8153 +#define GL_CONVOLUTION_BORDER_COLOR_HP 0x8154 +#endif /* GL_HP_convolution_border_modes */ + +#ifndef GL_HP_image_transform +#define GL_HP_image_transform 1 +#define GL_IMAGE_SCALE_X_HP 0x8155 +#define GL_IMAGE_SCALE_Y_HP 0x8156 +#define GL_IMAGE_TRANSLATE_X_HP 0x8157 +#define GL_IMAGE_TRANSLATE_Y_HP 0x8158 +#define GL_IMAGE_ROTATE_ANGLE_HP 0x8159 +#define GL_IMAGE_ROTATE_ORIGIN_X_HP 0x815A +#define GL_IMAGE_ROTATE_ORIGIN_Y_HP 0x815B +#define GL_IMAGE_MAG_FILTER_HP 0x815C +#define GL_IMAGE_MIN_FILTER_HP 0x815D +#define GL_IMAGE_CUBIC_WEIGHT_HP 0x815E +#define GL_CUBIC_HP 0x815F +#define GL_AVERAGE_HP 0x8160 +#define GL_IMAGE_TRANSFORM_2D_HP 0x8161 +#define GL_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8162 +#define GL_PROXY_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8163 +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIHPPROC)(GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFHPPROC)(GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIVHPPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFVHPPROC)(GLenum target, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERIVHPPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERFVHPPROC)(GLenum target, GLenum pname, GLfloat * params); +#endif /* GL_HP_image_transform */ + +#ifndef GL_HP_occlusion_test +#define GL_HP_occlusion_test 1 +#define GL_OCCLUSION_TEST_HP 0x8165 +#define GL_OCCLUSION_TEST_RESULT_HP 0x8166 +#endif /* GL_HP_occlusion_test */ + +#ifndef GL_HP_texture_lighting +#define GL_HP_texture_lighting 1 +#define GL_TEXTURE_LIGHTING_MODE_HP 0x8167 +#define GL_TEXTURE_POST_SPECULAR_HP 0x8168 +#define GL_TEXTURE_PRE_SPECULAR_HP 0x8169 +#endif /* GL_HP_texture_lighting */ + +#ifndef GL_IBM_cull_vertex +#define GL_IBM_cull_vertex 1 +#define GL_CULL_VERTEX_IBM 103050 +#endif /* GL_IBM_cull_vertex */ + +#ifndef GL_IBM_multimode_draw_arrays +#define GL_IBM_multimode_draw_arrays 1 +typedef void (APIENTRYP PFNGLMULTIMODEDRAWARRAYSIBMPROC)(const GLenum * mode, const GLint * first, const GLsizei * count, GLsizei primcount, GLint modestride); +typedef void (APIENTRYP PFNGLMULTIMODEDRAWELEMENTSIBMPROC)(const GLenum * mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei primcount, GLint modestride); +#endif /* GL_IBM_multimode_draw_arrays */ + +#ifndef GL_IBM_rasterpos_clip +#define GL_IBM_rasterpos_clip 1 +#define GL_RASTER_POSITION_UNCLIPPED_IBM 0x19262 +#endif /* GL_IBM_rasterpos_clip */ + +#ifndef GL_IBM_static_data +#define GL_IBM_static_data 1 +#define GL_ALL_STATIC_DATA_IBM 103060 +#define GL_STATIC_VERTEX_ARRAY_IBM 103061 +typedef void (APIENTRYP PFNGLFLUSHSTATICDATAIBMPROC)(GLenum target); +#endif /* GL_IBM_static_data */ + +#ifndef GL_IBM_texture_mirrored_repeat +#define GL_IBM_texture_mirrored_repeat 1 +#define GL_MIRRORED_REPEAT_IBM 0x8370 +#endif /* GL_IBM_texture_mirrored_repeat */ + +#ifndef GL_IBM_vertex_array_lists +#define GL_IBM_vertex_array_lists 1 +#define GL_VERTEX_ARRAY_LIST_IBM 103070 +#define GL_NORMAL_ARRAY_LIST_IBM 103071 +#define GL_COLOR_ARRAY_LIST_IBM 103072 +#define GL_INDEX_ARRAY_LIST_IBM 103073 +#define GL_TEXTURE_COORD_ARRAY_LIST_IBM 103074 +#define GL_EDGE_FLAG_ARRAY_LIST_IBM 103075 +#define GL_FOG_COORDINATE_ARRAY_LIST_IBM 103076 +#define GL_SECONDARY_COLOR_ARRAY_LIST_IBM 103077 +#define GL_VERTEX_ARRAY_LIST_STRIDE_IBM 103080 +#define GL_NORMAL_ARRAY_LIST_STRIDE_IBM 103081 +#define GL_COLOR_ARRAY_LIST_STRIDE_IBM 103082 +#define GL_INDEX_ARRAY_LIST_STRIDE_IBM 103083 +#define GL_TEXTURE_COORD_ARRAY_LIST_STRIDE_IBM 103084 +#define GL_EDGE_FLAG_ARRAY_LIST_STRIDE_IBM 103085 +#define GL_FOG_COORDINATE_ARRAY_LIST_STRIDE_IBM 103086 +#define GL_SECONDARY_COLOR_ARRAY_LIST_STRIDE_IBM 103087 +typedef void (APIENTRYP PFNGLCOLORPOINTERLISTIBMPROC)(GLint size, GLenum type, GLint stride, const void ** pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERLISTIBMPROC)(GLint size, GLenum type, GLint stride, const void ** pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLEDGEFLAGPOINTERLISTIBMPROC)(GLint stride, const GLboolean ** pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTERLISTIBMPROC)(GLenum type, GLint stride, const void ** pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLINDEXPOINTERLISTIBMPROC)(GLenum type, GLint stride, const void ** pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLNORMALPOINTERLISTIBMPROC)(GLenum type, GLint stride, const void ** pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTERLISTIBMPROC)(GLint size, GLenum type, GLint stride, const void ** pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLVERTEXPOINTERLISTIBMPROC)(GLint size, GLenum type, GLint stride, const void ** pointer, GLint ptrstride); +#endif /* GL_IBM_vertex_array_lists */ + +#ifndef GL_INGR_blend_func_separate +#define GL_INGR_blend_func_separate 1 +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINGRPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#endif /* GL_INGR_blend_func_separate */ + +#ifndef GL_INGR_color_clamp +#define GL_INGR_color_clamp 1 +#define GL_RED_MIN_CLAMP_INGR 0x8560 +#define GL_GREEN_MIN_CLAMP_INGR 0x8561 +#define GL_BLUE_MIN_CLAMP_INGR 0x8562 +#define GL_ALPHA_MIN_CLAMP_INGR 0x8563 +#define GL_RED_MAX_CLAMP_INGR 0x8564 +#define GL_GREEN_MAX_CLAMP_INGR 0x8565 +#define GL_BLUE_MAX_CLAMP_INGR 0x8566 +#define GL_ALPHA_MAX_CLAMP_INGR 0x8567 +#endif /* GL_INGR_color_clamp */ + +#ifndef GL_INGR_interlace_read +#define GL_INGR_interlace_read 1 +#define GL_INTERLACE_READ_INGR 0x8568 +#endif /* GL_INGR_interlace_read */ + +#ifndef GL_INTEL_conservative_rasterization +#define GL_INTEL_conservative_rasterization 1 +#define GL_CONSERVATIVE_RASTERIZATION_INTEL 0x83FE +#endif /* GL_INTEL_conservative_rasterization */ + +#ifndef GL_INTEL_fragment_shader_ordering +#define GL_INTEL_fragment_shader_ordering 1 +#endif /* GL_INTEL_fragment_shader_ordering */ + +#ifndef GL_INTEL_framebuffer_CMAA +#define GL_INTEL_framebuffer_CMAA 1 +typedef void (APIENTRYP PFNGLAPPLYFRAMEBUFFERATTACHMENTCMAAINTELPROC)(void); +#endif /* GL_INTEL_framebuffer_CMAA */ + +#ifndef GL_INTEL_map_texture +#define GL_INTEL_map_texture 1 +#define GL_TEXTURE_MEMORY_LAYOUT_INTEL 0x83FF +#define GL_LAYOUT_DEFAULT_INTEL 0 +#define GL_LAYOUT_LINEAR_INTEL 1 +#define GL_LAYOUT_LINEAR_CPU_CACHED_INTEL 2 +typedef void (APIENTRYP PFNGLSYNCTEXTUREINTELPROC)(GLuint texture); +typedef void (APIENTRYP PFNGLUNMAPTEXTURE2DINTELPROC)(GLuint texture, GLint level); +typedef void * (APIENTRYP PFNGLMAPTEXTURE2DINTELPROC)(GLuint texture, GLint level, GLbitfield access, GLint * stride, GLenum * layout); +#endif /* GL_INTEL_map_texture */ + +#ifndef GL_INTEL_blackhole_render +#define GL_INTEL_blackhole_render 1 +#define GL_BLACKHOLE_RENDER_INTEL 0x83FC +#endif /* GL_INTEL_blackhole_render */ + +#ifndef GL_INTEL_parallel_arrays +#define GL_INTEL_parallel_arrays 1 +#define GL_PARALLEL_ARRAYS_INTEL 0x83F4 +#define GL_VERTEX_ARRAY_PARALLEL_POINTERS_INTEL 0x83F5 +#define GL_NORMAL_ARRAY_PARALLEL_POINTERS_INTEL 0x83F6 +#define GL_COLOR_ARRAY_PARALLEL_POINTERS_INTEL 0x83F7 +#define GL_TEXTURE_COORD_ARRAY_PARALLEL_POINTERS_INTEL 0x83F8 +typedef void (APIENTRYP PFNGLVERTEXPOINTERVINTELPROC)(GLint size, GLenum type, const void ** pointer); +typedef void (APIENTRYP PFNGLNORMALPOINTERVINTELPROC)(GLenum type, const void ** pointer); +typedef void (APIENTRYP PFNGLCOLORPOINTERVINTELPROC)(GLint size, GLenum type, const void ** pointer); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTERVINTELPROC)(GLint size, GLenum type, const void ** pointer); +#endif /* GL_INTEL_parallel_arrays */ + +#ifndef GL_INTEL_performance_query +#define GL_INTEL_performance_query 1 +#define GL_PERFQUERY_SINGLE_CONTEXT_INTEL 0x00000000 +#define GL_PERFQUERY_GLOBAL_CONTEXT_INTEL 0x00000001 +#define GL_PERFQUERY_WAIT_INTEL 0x83FB +#define GL_PERFQUERY_FLUSH_INTEL 0x83FA +#define GL_PERFQUERY_DONOT_FLUSH_INTEL 0x83F9 +#define GL_PERFQUERY_COUNTER_EVENT_INTEL 0x94F0 +#define GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL 0x94F1 +#define GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL 0x94F2 +#define GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL 0x94F3 +#define GL_PERFQUERY_COUNTER_RAW_INTEL 0x94F4 +#define GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL 0x94F5 +#define GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL 0x94F8 +#define GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL 0x94F9 +#define GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL 0x94FA +#define GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL 0x94FB +#define GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL 0x94FC +#define GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL 0x94FD +#define GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL 0x94FE +#define GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL 0x94FF +#define GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL 0x9500 +typedef void (APIENTRYP PFNGLBEGINPERFQUERYINTELPROC)(GLuint queryHandle); +typedef void (APIENTRYP PFNGLCREATEPERFQUERYINTELPROC)(GLuint queryId, GLuint * queryHandle); +typedef void (APIENTRYP PFNGLDELETEPERFQUERYINTELPROC)(GLuint queryHandle); +typedef void (APIENTRYP PFNGLENDPERFQUERYINTELPROC)(GLuint queryHandle); +typedef void (APIENTRYP PFNGLGETFIRSTPERFQUERYIDINTELPROC)(GLuint * queryId); +typedef void (APIENTRYP PFNGLGETNEXTPERFQUERYIDINTELPROC)(GLuint queryId, GLuint * nextQueryId); +typedef void (APIENTRYP PFNGLGETPERFCOUNTERINFOINTELPROC)(GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar * counterName, GLuint counterDescLength, GLchar * counterDesc, GLuint * counterOffset, GLuint * counterDataSize, GLuint * counterTypeEnum, GLuint * counterDataTypeEnum, GLuint64 * rawCounterMaxValue); +typedef void (APIENTRYP PFNGLGETPERFQUERYDATAINTELPROC)(GLuint queryHandle, GLuint flags, GLsizei dataSize, void * data, GLuint * bytesWritten); +typedef void (APIENTRYP PFNGLGETPERFQUERYIDBYNAMEINTELPROC)(GLchar * queryName, GLuint * queryId); +typedef void (APIENTRYP PFNGLGETPERFQUERYINFOINTELPROC)(GLuint queryId, GLuint queryNameLength, GLchar * queryName, GLuint * dataSize, GLuint * noCounters, GLuint * noInstances, GLuint * capsMask); +#endif /* GL_INTEL_performance_query */ + +#ifndef GL_KHR_blend_equation_advanced +#define GL_KHR_blend_equation_advanced 1 +#define GL_MULTIPLY_KHR 0x9294 +#define GL_SCREEN_KHR 0x9295 +#define GL_OVERLAY_KHR 0x9296 +#define GL_DARKEN_KHR 0x9297 +#define GL_LIGHTEN_KHR 0x9298 +#define GL_COLORDODGE_KHR 0x9299 +#define GL_COLORBURN_KHR 0x929A +#define GL_HARDLIGHT_KHR 0x929B +#define GL_SOFTLIGHT_KHR 0x929C +#define GL_DIFFERENCE_KHR 0x929E +#define GL_EXCLUSION_KHR 0x92A0 +#define GL_HSL_HUE_KHR 0x92AD +#define GL_HSL_SATURATION_KHR 0x92AE +#define GL_HSL_COLOR_KHR 0x92AF +#define GL_HSL_LUMINOSITY_KHR 0x92B0 +typedef void (APIENTRYP PFNGLBLENDBARRIERKHRPROC)(void); +#endif /* GL_KHR_blend_equation_advanced */ + +#ifndef GL_KHR_blend_equation_advanced_coherent +#define GL_KHR_blend_equation_advanced_coherent 1 +#define GL_BLEND_ADVANCED_COHERENT_KHR 0x9285 +#endif /* GL_KHR_blend_equation_advanced_coherent */ + +#ifndef GL_KHR_context_flush_control +#define GL_KHR_context_flush_control 1 +#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB +#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC +#define GL_NONE 0 +#define GL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x82FB +#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x82FC +#define GL_NONE 0 +#endif /* GL_KHR_context_flush_control */ + +#ifndef GL_KHR_debug +#define GL_KHR_debug 1 +typedef void (APIENTRY *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_VERTEX_ARRAY 0x8074 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_KHR 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION_KHR 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM_KHR 0x8245 +#define GL_DEBUG_SOURCE_API_KHR 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER_KHR 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY_KHR 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION_KHR 0x824A +#define GL_DEBUG_SOURCE_OTHER_KHR 0x824B +#define GL_DEBUG_TYPE_ERROR_KHR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY_KHR 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE_KHR 0x8250 +#define GL_DEBUG_TYPE_OTHER_KHR 0x8251 +#define GL_DEBUG_TYPE_MARKER_KHR 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP_KHR 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP_KHR 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION_KHR 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH_KHR 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH_KHR 0x826D +#define GL_BUFFER_KHR 0x82E0 +#define GL_SHADER_KHR 0x82E1 +#define GL_PROGRAM_KHR 0x82E2 +#define GL_VERTEX_ARRAY_KHR 0x8074 +#define GL_QUERY_KHR 0x82E3 +#define GL_PROGRAM_PIPELINE_KHR 0x82E4 +#define GL_SAMPLER_KHR 0x82E6 +#define GL_MAX_LABEL_LENGTH_KHR 0x82E8 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_KHR 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_KHR 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_KHR 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_KHR 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_KHR 0x9147 +#define GL_DEBUG_SEVERITY_LOW_KHR 0x9148 +#define GL_DEBUG_OUTPUT_KHR 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT_KHR 0x00000002 +#define GL_STACK_OVERFLOW_KHR 0x0503 +#define GL_STACK_UNDERFLOW_KHR 0x0504 +#define GL_DISPLAY_LIST 0x82E7 +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLKHRPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint * ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTKHRPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar * buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKKHRPROC)(GLDEBUGPROCKHR callback, const void * userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGKHRPROC)(GLuint count, GLsizei bufSize, GLenum * sources, GLenum * types, GLuint * ids, GLenum * severities, GLsizei * lengths, GLchar * messageLog); +typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPKHRPROC)(GLenum source, GLuint id, GLsizei length, const GLchar * message); +typedef void (APIENTRYP PFNGLPOPDEBUGGROUPKHRPROC)(void); +typedef void (APIENTRYP PFNGLOBJECTLABELKHRPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar * label); +typedef void (APIENTRYP PFNGLGETOBJECTLABELKHRPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei * length, GLchar * label); +typedef void (APIENTRYP PFNGLOBJECTPTRLABELKHRPROC)(const void * ptr, GLsizei length, const GLchar * label); +typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELKHRPROC)(const void * ptr, GLsizei bufSize, GLsizei * length, GLchar * label); +typedef void (APIENTRYP PFNGLGETPOINTERVKHRPROC)(GLenum pname, void ** params); +#endif /* GL_KHR_debug */ + +#ifndef GL_KHR_no_error +#define GL_KHR_no_error 1 +#define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008 +#endif /* GL_KHR_no_error */ + +#ifndef GL_KHR_robust_buffer_access_behavior +#define GL_KHR_robust_buffer_access_behavior 1 +#endif /* GL_KHR_robust_buffer_access_behavior */ + +#ifndef GL_KHR_robustness +#define GL_KHR_robustness 1 +#define GL_NO_ERROR 0 +#define GL_CONTEXT_ROBUST_ACCESS 0x90F3 +#define GL_LOSE_CONTEXT_ON_RESET 0x8252 +#define GL_GUILTY_CONTEXT_RESET 0x8253 +#define GL_INNOCENT_CONTEXT_RESET 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY 0x8256 +#define GL_NO_RESET_NOTIFICATION 0x8261 +#define GL_CONTEXT_LOST 0x0507 +#define GL_NO_ERROR 0 +#define GL_CONTEXT_ROBUST_ACCESS_KHR 0x90F3 +#define GL_LOSE_CONTEXT_ON_RESET_KHR 0x8252 +#define GL_GUILTY_CONTEXT_RESET_KHR 0x8253 +#define GL_INNOCENT_CONTEXT_RESET_KHR 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET_KHR 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY_KHR 0x8256 +#define GL_NO_RESET_NOTIFICATION_KHR 0x8261 +#define GL_CONTEXT_LOST_KHR 0x0507 +typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSKHRPROC)(void); +typedef void (APIENTRYP PFNGLREADNPIXELSKHRPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void * data); +typedef void (APIENTRYP PFNGLGETNUNIFORMFVKHRPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat * params); +typedef void (APIENTRYP PFNGLGETNUNIFORMIVKHRPROC)(GLuint program, GLint location, GLsizei bufSize, GLint * params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUIVKHRPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint * params); +#endif /* GL_KHR_robustness */ + +#ifndef GL_KHR_shader_subgroup +#define GL_KHR_shader_subgroup 1 +#define GL_SUBGROUP_SIZE_KHR 0x9532 +#define GL_SUBGROUP_SUPPORTED_STAGES_KHR 0x9533 +#define GL_SUBGROUP_SUPPORTED_FEATURES_KHR 0x9534 +#define GL_SUBGROUP_QUAD_ALL_STAGES_KHR 0x9535 +#define GL_SUBGROUP_FEATURE_BASIC_BIT_KHR 0x00000001 +#define GL_SUBGROUP_FEATURE_VOTE_BIT_KHR 0x00000002 +#define GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR 0x00000004 +#define GL_SUBGROUP_FEATURE_BALLOT_BIT_KHR 0x00000008 +#define GL_SUBGROUP_FEATURE_SHUFFLE_BIT_KHR 0x00000010 +#define GL_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT_KHR 0x00000020 +#define GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR 0x00000040 +#define GL_SUBGROUP_FEATURE_QUAD_BIT_KHR 0x00000080 +#endif /* GL_KHR_shader_subgroup */ + +#ifndef GL_KHR_texture_compression_astc_hdr +#define GL_KHR_texture_compression_astc_hdr 1 +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#endif /* GL_KHR_texture_compression_astc_hdr */ + +#ifndef GL_KHR_texture_compression_astc_ldr +#define GL_KHR_texture_compression_astc_ldr 1 +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#endif /* GL_KHR_texture_compression_astc_ldr */ + +#ifndef GL_KHR_texture_compression_astc_sliced_3d +#define GL_KHR_texture_compression_astc_sliced_3d 1 +#endif /* GL_KHR_texture_compression_astc_sliced_3d */ + +#ifndef GL_KHR_parallel_shader_compile +#define GL_KHR_parallel_shader_compile 1 +#define GL_MAX_SHADER_COMPILER_THREADS_KHR 0x91B0 +#define GL_COMPLETION_STATUS_KHR 0x91B1 +typedef void (APIENTRYP PFNGLMAXSHADERCOMPILERTHREADSKHRPROC)(GLuint count); +#endif /* GL_KHR_parallel_shader_compile */ + +#ifndef GL_MESAX_texture_stack +#define GL_MESAX_texture_stack 1 +#define GL_TEXTURE_1D_STACK_MESAX 0x8759 +#define GL_TEXTURE_2D_STACK_MESAX 0x875A +#define GL_PROXY_TEXTURE_1D_STACK_MESAX 0x875B +#define GL_PROXY_TEXTURE_2D_STACK_MESAX 0x875C +#define GL_TEXTURE_1D_STACK_BINDING_MESAX 0x875D +#define GL_TEXTURE_2D_STACK_BINDING_MESAX 0x875E +#endif /* GL_MESAX_texture_stack */ + +#ifndef GL_MESA_framebuffer_flip_x +#define GL_MESA_framebuffer_flip_x 1 +#define GL_FRAMEBUFFER_FLIP_X_MESA 0x8BBC +#endif /* GL_MESA_framebuffer_flip_x */ + +#ifndef GL_MESA_framebuffer_flip_y +#define GL_MESA_framebuffer_flip_y 1 +#define GL_FRAMEBUFFER_FLIP_Y_MESA 0x8BBB +typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIMESAPROC)(GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVMESAPROC)(GLenum target, GLenum pname, GLint * params); +#endif /* GL_MESA_framebuffer_flip_y */ + +#ifndef GL_MESA_framebuffer_swap_xy +#define GL_MESA_framebuffer_swap_xy 1 +#define GL_FRAMEBUFFER_SWAP_XY_MESA 0x8BBD +#endif /* GL_MESA_framebuffer_swap_xy */ + +#ifndef GL_MESA_pack_invert +#define GL_MESA_pack_invert 1 +#define GL_PACK_INVERT_MESA 0x8758 +#endif /* GL_MESA_pack_invert */ + +#ifndef GL_MESA_program_binary_formats +#define GL_MESA_program_binary_formats 1 +#define GL_PROGRAM_BINARY_FORMAT_MESA 0x875F +#endif /* GL_MESA_program_binary_formats */ + +#ifndef GL_MESA_resize_buffers +#define GL_MESA_resize_buffers 1 +typedef void (APIENTRYP PFNGLRESIZEBUFFERSMESAPROC)(void); +#endif /* GL_MESA_resize_buffers */ + +#ifndef GL_MESA_shader_integer_functions +#define GL_MESA_shader_integer_functions 1 +#endif /* GL_MESA_shader_integer_functions */ + +#ifndef GL_MESA_tile_raster_order +#define GL_MESA_tile_raster_order 1 +#define GL_TILE_RASTER_ORDER_FIXED_MESA 0x8BB8 +#define GL_TILE_RASTER_ORDER_INCREASING_X_MESA 0x8BB9 +#define GL_TILE_RASTER_ORDER_INCREASING_Y_MESA 0x8BBA +#endif /* GL_MESA_tile_raster_order */ + +#ifndef GL_MESA_window_pos +#define GL_MESA_window_pos 1 +typedef void (APIENTRYP PFNGLWINDOWPOS2DMESAPROC)(GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVMESAPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FMESAPROC)(GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVMESAPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IMESAPROC)(GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVMESAPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SMESAPROC)(GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVMESAPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DMESAPROC)(GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVMESAPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FMESAPROC)(GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVMESAPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IMESAPROC)(GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVMESAPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SMESAPROC)(GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVMESAPROC)(const GLshort * v); +typedef void (APIENTRYP PFNGLWINDOWPOS4DMESAPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLWINDOWPOS4DVMESAPROC)(const GLdouble * v); +typedef void (APIENTRYP PFNGLWINDOWPOS4FMESAPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLWINDOWPOS4FVMESAPROC)(const GLfloat * v); +typedef void (APIENTRYP PFNGLWINDOWPOS4IMESAPROC)(GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLWINDOWPOS4IVMESAPROC)(const GLint * v); +typedef void (APIENTRYP PFNGLWINDOWPOS4SMESAPROC)(GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLWINDOWPOS4SVMESAPROC)(const GLshort * v); +#endif /* GL_MESA_window_pos */ + +#ifndef GL_MESA_ycbcr_texture +#define GL_MESA_ycbcr_texture 1 +#define GL_UNSIGNED_SHORT_8_8_MESA 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_MESA 0x85BB +#define GL_YCBCR_MESA 0x8757 +#endif /* GL_MESA_ycbcr_texture */ + +#ifndef GL_NVX_blend_equation_advanced_multi_draw_buffers +#define GL_NVX_blend_equation_advanced_multi_draw_buffers 1 +#endif /* GL_NVX_blend_equation_advanced_multi_draw_buffers */ + +#ifndef GL_NVX_conditional_render +#define GL_NVX_conditional_render 1 +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVXPROC)(GLuint id); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVXPROC)(void); +#endif /* GL_NVX_conditional_render */ + +#ifndef GL_NVX_gpu_memory_info +#define GL_NVX_gpu_memory_info 1 +#define GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX 0x9047 +#define GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX 0x9048 +#define GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX 0x9049 +#define GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX 0x904A +#define GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX 0x904B +#endif /* GL_NVX_gpu_memory_info */ + +#ifndef GL_NVX_linked_gpu_multicast +#define GL_NVX_linked_gpu_multicast 1 +#define GL_LGPU_SEPARATE_STORAGE_BIT_NVX 0x0800 +#define GL_MAX_LGPU_GPUS_NVX 0x92BA +typedef void (APIENTRYP PFNGLLGPUNAMEDBUFFERSUBDATANVXPROC)(GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void * data); +typedef void (APIENTRYP PFNGLLGPUCOPYIMAGESUBDATANVXPROC)(GLuint sourceGpu, GLbitfield destinationGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srxY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLLGPUINTERLOCKNVXPROC)(void); +#endif /* GL_NVX_linked_gpu_multicast */ + +#ifndef GL_NV_alpha_to_coverage_dither_control +#define GL_NV_alpha_to_coverage_dither_control 1 +#define GL_ALPHA_TO_COVERAGE_DITHER_DEFAULT_NV 0x934D +#define GL_ALPHA_TO_COVERAGE_DITHER_ENABLE_NV 0x934E +#define GL_ALPHA_TO_COVERAGE_DITHER_DISABLE_NV 0x934F +#define GL_ALPHA_TO_COVERAGE_DITHER_MODE_NV 0x92BF +typedef void (APIENTRYP PFNGLALPHATOCOVERAGEDITHERCONTROLNVPROC)(GLenum mode); +#endif /* GL_NV_alpha_to_coverage_dither_control */ + +#ifndef GL_NV_bindless_multi_draw_indirect +#define GL_NV_bindless_multi_draw_indirect 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC)(GLenum mode, const void * indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSNVPROC)(GLenum mode, GLenum type, const void * indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +#endif /* GL_NV_bindless_multi_draw_indirect */ + +#ifndef GL_NV_bindless_multi_draw_indirect_count +#define GL_NV_bindless_multi_draw_indirect_count 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSCOUNTNVPROC)(GLenum mode, const void * indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSCOUNTNVPROC)(GLenum mode, GLenum type, const void * indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); +#endif /* GL_NV_bindless_multi_draw_indirect_count */ + +#ifndef GL_NV_bindless_texture +#define GL_NV_bindless_texture 1 +typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLENVPROC)(GLuint texture); +typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLENVPROC)(GLuint texture, GLuint sampler); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTNVPROC)(GLuint64 handle); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC)(GLuint64 handle); +typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLENVPROC)(GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTNVPROC)(GLuint64 handle, GLenum access); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC)(GLuint64 handle); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64NVPROC)(GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VNVPROC)(GLint location, GLsizei count, const GLuint64 * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64NVPROC)(GLuint program, GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC)(GLuint program, GLint location, GLsizei count, const GLuint64 * values); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTNVPROC)(GLuint64 handle); +typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTNVPROC)(GLuint64 handle); +#endif /* GL_NV_bindless_texture */ + +#ifndef GL_NV_blend_equation_advanced +#define GL_NV_blend_equation_advanced 1 +#define GL_BLEND_OVERLAP_NV 0x9281 +#define GL_BLEND_PREMULTIPLIED_SRC_NV 0x9280 +#define GL_BLUE_NV 0x1905 +#define GL_COLORBURN_NV 0x929A +#define GL_COLORDODGE_NV 0x9299 +#define GL_CONJOINT_NV 0x9284 +#define GL_CONTRAST_NV 0x92A1 +#define GL_DARKEN_NV 0x9297 +#define GL_DIFFERENCE_NV 0x929E +#define GL_DISJOINT_NV 0x9283 +#define GL_DST_ATOP_NV 0x928F +#define GL_DST_IN_NV 0x928B +#define GL_DST_NV 0x9287 +#define GL_DST_OUT_NV 0x928D +#define GL_DST_OVER_NV 0x9289 +#define GL_EXCLUSION_NV 0x92A0 +#define GL_GREEN_NV 0x1904 +#define GL_HARDLIGHT_NV 0x929B +#define GL_HARDMIX_NV 0x92A9 +#define GL_HSL_COLOR_NV 0x92AF +#define GL_HSL_HUE_NV 0x92AD +#define GL_HSL_LUMINOSITY_NV 0x92B0 +#define GL_HSL_SATURATION_NV 0x92AE +#define GL_INVERT 0x150A +#define GL_INVERT_OVG_NV 0x92B4 +#define GL_INVERT_RGB_NV 0x92A3 +#define GL_LIGHTEN_NV 0x9298 +#define GL_LINEARBURN_NV 0x92A5 +#define GL_LINEARDODGE_NV 0x92A4 +#define GL_LINEARLIGHT_NV 0x92A7 +#define GL_MINUS_CLAMPED_NV 0x92B3 +#define GL_MINUS_NV 0x929F +#define GL_MULTIPLY_NV 0x9294 +#define GL_OVERLAY_NV 0x9296 +#define GL_PINLIGHT_NV 0x92A8 +#define GL_PLUS_CLAMPED_ALPHA_NV 0x92B2 +#define GL_PLUS_CLAMPED_NV 0x92B1 +#define GL_PLUS_DARKER_NV 0x9292 +#define GL_PLUS_NV 0x9291 +#define GL_RED_NV 0x1903 +#define GL_SCREEN_NV 0x9295 +#define GL_SOFTLIGHT_NV 0x929C +#define GL_SRC_ATOP_NV 0x928E +#define GL_SRC_IN_NV 0x928A +#define GL_SRC_NV 0x9286 +#define GL_SRC_OUT_NV 0x928C +#define GL_SRC_OVER_NV 0x9288 +#define GL_UNCORRELATED_NV 0x9282 +#define GL_VIVIDLIGHT_NV 0x92A6 +#define GL_XOR_NV 0x1506 +#define GL_ZERO 0 +typedef void (APIENTRYP PFNGLBLENDPARAMETERINVPROC)(GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLBLENDBARRIERNVPROC)(void); +#endif /* GL_NV_blend_equation_advanced */ + +#ifndef GL_NV_blend_equation_advanced_coherent +#define GL_NV_blend_equation_advanced_coherent 1 +#define GL_BLEND_ADVANCED_COHERENT_NV 0x9285 +#endif /* GL_NV_blend_equation_advanced_coherent */ + +#ifndef GL_NV_blend_minmax_factor +#define GL_NV_blend_minmax_factor 1 +#define GL_FACTOR_MIN_AMD 0x901C +#define GL_FACTOR_MAX_AMD 0x901D +#endif /* GL_NV_blend_minmax_factor */ + +#ifndef GL_NV_blend_square +#define GL_NV_blend_square 1 +#endif /* GL_NV_blend_square */ + +#ifndef GL_NV_clip_space_w_scaling +#define GL_NV_clip_space_w_scaling 1 +#define GL_VIEWPORT_POSITION_W_SCALE_NV 0x937C +#define GL_VIEWPORT_POSITION_W_SCALE_X_COEFF_NV 0x937D +#define GL_VIEWPORT_POSITION_W_SCALE_Y_COEFF_NV 0x937E +typedef void (APIENTRYP PFNGLVIEWPORTPOSITIONWSCALENVPROC)(GLuint index, GLfloat xcoeff, GLfloat ycoeff); +#endif /* GL_NV_clip_space_w_scaling */ + +#ifndef GL_NV_command_list +#define GL_NV_command_list 1 +#define GL_TERMINATE_SEQUENCE_COMMAND_NV 0x0000 +#define GL_NOP_COMMAND_NV 0x0001 +#define GL_DRAW_ELEMENTS_COMMAND_NV 0x0002 +#define GL_DRAW_ARRAYS_COMMAND_NV 0x0003 +#define GL_DRAW_ELEMENTS_STRIP_COMMAND_NV 0x0004 +#define GL_DRAW_ARRAYS_STRIP_COMMAND_NV 0x0005 +#define GL_DRAW_ELEMENTS_INSTANCED_COMMAND_NV 0x0006 +#define GL_DRAW_ARRAYS_INSTANCED_COMMAND_NV 0x0007 +#define GL_ELEMENT_ADDRESS_COMMAND_NV 0x0008 +#define GL_ATTRIBUTE_ADDRESS_COMMAND_NV 0x0009 +#define GL_UNIFORM_ADDRESS_COMMAND_NV 0x000A +#define GL_BLEND_COLOR_COMMAND_NV 0x000B +#define GL_STENCIL_REF_COMMAND_NV 0x000C +#define GL_LINE_WIDTH_COMMAND_NV 0x000D +#define GL_POLYGON_OFFSET_COMMAND_NV 0x000E +#define GL_ALPHA_REF_COMMAND_NV 0x000F +#define GL_VIEWPORT_COMMAND_NV 0x0010 +#define GL_SCISSOR_COMMAND_NV 0x0011 +#define GL_FRONT_FACE_COMMAND_NV 0x0012 +typedef void (APIENTRYP PFNGLCREATESTATESNVPROC)(GLsizei n, GLuint * states); +typedef void (APIENTRYP PFNGLDELETESTATESNVPROC)(GLsizei n, const GLuint * states); +typedef GLboolean (APIENTRYP PFNGLISSTATENVPROC)(GLuint state); +typedef void (APIENTRYP PFNGLSTATECAPTURENVPROC)(GLuint state, GLenum mode); +typedef GLuint (APIENTRYP PFNGLGETCOMMANDHEADERNVPROC)(GLenum tokenID, GLuint size); +typedef GLushort (APIENTRYP PFNGLGETSTAGEINDEXNVPROC)(GLenum shadertype); +typedef void (APIENTRYP PFNGLDRAWCOMMANDSNVPROC)(GLenum primitiveMode, GLuint buffer, const GLintptr * indirects, const GLsizei * sizes, GLuint count); +typedef void (APIENTRYP PFNGLDRAWCOMMANDSADDRESSNVPROC)(GLenum primitiveMode, const GLuint64 * indirects, const GLsizei * sizes, GLuint count); +typedef void (APIENTRYP PFNGLDRAWCOMMANDSSTATESNVPROC)(GLuint buffer, const GLintptr * indirects, const GLsizei * sizes, const GLuint * states, const GLuint * fbos, GLuint count); +typedef void (APIENTRYP PFNGLDRAWCOMMANDSSTATESADDRESSNVPROC)(const GLuint64 * indirects, const GLsizei * sizes, const GLuint * states, const GLuint * fbos, GLuint count); +typedef void (APIENTRYP PFNGLCREATECOMMANDLISTSNVPROC)(GLsizei n, GLuint * lists); +typedef void (APIENTRYP PFNGLDELETECOMMANDLISTSNVPROC)(GLsizei n, const GLuint * lists); +typedef GLboolean (APIENTRYP PFNGLISCOMMANDLISTNVPROC)(GLuint list); +typedef void (APIENTRYP PFNGLLISTDRAWCOMMANDSSTATESCLIENTNVPROC)(GLuint list, GLuint segment, const void ** indirects, const GLsizei * sizes, const GLuint * states, const GLuint * fbos, GLuint count); +typedef void (APIENTRYP PFNGLCOMMANDLISTSEGMENTSNVPROC)(GLuint list, GLuint segments); +typedef void (APIENTRYP PFNGLCOMPILECOMMANDLISTNVPROC)(GLuint list); +typedef void (APIENTRYP PFNGLCALLCOMMANDLISTNVPROC)(GLuint list); +#endif /* GL_NV_command_list */ + +#ifndef GL_NV_compute_program5 +#define GL_NV_compute_program5 1 +#define GL_COMPUTE_PROGRAM_NV 0x90FB +#define GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV 0x90FC +#endif /* GL_NV_compute_program5 */ + +#ifndef GL_NV_compute_shader_derivatives +#define GL_NV_compute_shader_derivatives 1 +#endif /* GL_NV_compute_shader_derivatives */ + +#ifndef GL_NV_conditional_render +#define GL_NV_conditional_render 1 +#define GL_QUERY_WAIT_NV 0x8E13 +#define GL_QUERY_NO_WAIT_NV 0x8E14 +#define GL_QUERY_BY_REGION_WAIT_NV 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT_NV 0x8E16 +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVPROC)(GLuint id, GLenum mode); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVPROC)(void); +#endif /* GL_NV_conditional_render */ + +#ifndef GL_NV_conservative_raster +#define GL_NV_conservative_raster 1 +#define GL_CONSERVATIVE_RASTERIZATION_NV 0x9346 +#define GL_SUBPIXEL_PRECISION_BIAS_X_BITS_NV 0x9347 +#define GL_SUBPIXEL_PRECISION_BIAS_Y_BITS_NV 0x9348 +#define GL_MAX_SUBPIXEL_PRECISION_BIAS_BITS_NV 0x9349 +typedef void (APIENTRYP PFNGLSUBPIXELPRECISIONBIASNVPROC)(GLuint xbits, GLuint ybits); +#endif /* GL_NV_conservative_raster */ + +#ifndef GL_NV_conservative_raster_dilate +#define GL_NV_conservative_raster_dilate 1 +#define GL_CONSERVATIVE_RASTER_DILATE_NV 0x9379 +#define GL_CONSERVATIVE_RASTER_DILATE_RANGE_NV 0x937A +#define GL_CONSERVATIVE_RASTER_DILATE_GRANULARITY_NV 0x937B +typedef void (APIENTRYP PFNGLCONSERVATIVERASTERPARAMETERFNVPROC)(GLenum pname, GLfloat value); +#endif /* GL_NV_conservative_raster_dilate */ + +#ifndef GL_NV_conservative_raster_pre_snap +#define GL_NV_conservative_raster_pre_snap 1 +#define GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_NV 0x9550 +#endif /* GL_NV_conservative_raster_pre_snap */ + +#ifndef GL_NV_conservative_raster_pre_snap_triangles +#define GL_NV_conservative_raster_pre_snap_triangles 1 +#define GL_CONSERVATIVE_RASTER_MODE_NV 0x954D +#define GL_CONSERVATIVE_RASTER_MODE_POST_SNAP_NV 0x954E +#define GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_TRIANGLES_NV 0x954F +#define GL_CONSERVATIVE_RASTER_MODE_NV 0x954D +typedef void (APIENTRYP PFNGLCONSERVATIVERASTERPARAMETERINVPROC)(GLenum pname, GLint param); +#endif /* GL_NV_conservative_raster_pre_snap_triangles */ + +#ifndef GL_NV_conservative_raster_underestimation +#define GL_NV_conservative_raster_underestimation 1 +#endif /* GL_NV_conservative_raster_underestimation */ + +#ifndef GL_NV_copy_depth_to_color +#define GL_NV_copy_depth_to_color 1 +#define GL_DEPTH_STENCIL_TO_RGBA_NV 0x886E +#define GL_DEPTH_STENCIL_TO_BGRA_NV 0x886F +#endif /* GL_NV_copy_depth_to_color */ + +#ifndef GL_NV_copy_image +#define GL_NV_copy_image 1 +typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATANVPROC)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#endif /* GL_NV_copy_image */ + +#ifndef GL_NV_deep_texture3D +#define GL_NV_deep_texture3D 1 +#define GL_MAX_DEEP_3D_TEXTURE_WIDTH_HEIGHT_NV 0x90D0 +#define GL_MAX_DEEP_3D_TEXTURE_DEPTH_NV 0x90D1 +#endif /* GL_NV_deep_texture3D */ + +#ifndef GL_NV_depth_buffer_float +#define GL_NV_depth_buffer_float 1 +#define GL_DEPTH_COMPONENT32F_NV 0x8DAB +#define GL_DEPTH32F_STENCIL8_NV 0x8DAC +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV 0x8DAD +#define GL_DEPTH_BUFFER_FLOAT_MODE_NV 0x8DAF +typedef void (APIENTRYP PFNGLDEPTHRANGEDNVPROC)(GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLCLEARDEPTHDNVPROC)(GLdouble depth); +typedef void (APIENTRYP PFNGLDEPTHBOUNDSDNVPROC)(GLdouble zmin, GLdouble zmax); +#endif /* GL_NV_depth_buffer_float */ + +#ifndef GL_NV_depth_clamp +#define GL_NV_depth_clamp 1 +#define GL_DEPTH_CLAMP_NV 0x864F +#endif /* GL_NV_depth_clamp */ + +#ifndef GL_NV_draw_texture +#define GL_NV_draw_texture 1 +typedef void (APIENTRYP PFNGLDRAWTEXTURENVPROC)(GLuint texture, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); +#endif /* GL_NV_draw_texture */ + +#ifndef GL_NV_draw_vulkan_image +#define GL_NV_draw_vulkan_image 1 +typedef void (APIENTRY *GLVULKANPROCNV)(void); +typedef void (APIENTRYP PFNGLDRAWVKIMAGENVPROC)(GLuint64 vkImage, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); +typedef GLVULKANPROCNV (APIENTRYP PFNGLGETVKPROCADDRNVPROC)(const GLchar * name); +typedef void (APIENTRYP PFNGLWAITVKSEMAPHORENVPROC)(GLuint64 vkSemaphore); +typedef void (APIENTRYP PFNGLSIGNALVKSEMAPHORENVPROC)(GLuint64 vkSemaphore); +typedef void (APIENTRYP PFNGLSIGNALVKFENCENVPROC)(GLuint64 vkFence); +#endif /* GL_NV_draw_vulkan_image */ + +#ifndef GL_NV_evaluators +#define GL_NV_evaluators 1 +#define GL_EVAL_2D_NV 0x86C0 +#define GL_EVAL_TRIANGULAR_2D_NV 0x86C1 +#define GL_MAP_TESSELLATION_NV 0x86C2 +#define GL_MAP_ATTRIB_U_ORDER_NV 0x86C3 +#define GL_MAP_ATTRIB_V_ORDER_NV 0x86C4 +#define GL_EVAL_FRACTIONAL_TESSELLATION_NV 0x86C5 +#define GL_EVAL_VERTEX_ATTRIB0_NV 0x86C6 +#define GL_EVAL_VERTEX_ATTRIB1_NV 0x86C7 +#define GL_EVAL_VERTEX_ATTRIB2_NV 0x86C8 +#define GL_EVAL_VERTEX_ATTRIB3_NV 0x86C9 +#define GL_EVAL_VERTEX_ATTRIB4_NV 0x86CA +#define GL_EVAL_VERTEX_ATTRIB5_NV 0x86CB +#define GL_EVAL_VERTEX_ATTRIB6_NV 0x86CC +#define GL_EVAL_VERTEX_ATTRIB7_NV 0x86CD +#define GL_EVAL_VERTEX_ATTRIB8_NV 0x86CE +#define GL_EVAL_VERTEX_ATTRIB9_NV 0x86CF +#define GL_EVAL_VERTEX_ATTRIB10_NV 0x86D0 +#define GL_EVAL_VERTEX_ATTRIB11_NV 0x86D1 +#define GL_EVAL_VERTEX_ATTRIB12_NV 0x86D2 +#define GL_EVAL_VERTEX_ATTRIB13_NV 0x86D3 +#define GL_EVAL_VERTEX_ATTRIB14_NV 0x86D4 +#define GL_EVAL_VERTEX_ATTRIB15_NV 0x86D5 +#define GL_MAX_MAP_TESSELLATION_NV 0x86D6 +#define GL_MAX_RATIONAL_EVAL_ORDER_NV 0x86D7 +typedef void (APIENTRYP PFNGLMAPCONTROLPOINTSNVPROC)(GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLint uorder, GLint vorder, GLboolean packed, const void * points); +typedef void (APIENTRYP PFNGLMAPPARAMETERIVNVPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLMAPPARAMETERFVNVPROC)(GLenum target, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLGETMAPCONTROLPOINTSNVPROC)(GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLboolean packed, void * points); +typedef void (APIENTRYP PFNGLGETMAPPARAMETERIVNVPROC)(GLenum target, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETMAPPARAMETERFVNVPROC)(GLenum target, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERIVNVPROC)(GLenum target, GLuint index, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERFVNVPROC)(GLenum target, GLuint index, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLEVALMAPSNVPROC)(GLenum target, GLenum mode); +#endif /* GL_NV_evaluators */ + +#ifndef GL_NV_explicit_multisample +#define GL_NV_explicit_multisample 1 +#define GL_SAMPLE_POSITION_NV 0x8E50 +#define GL_SAMPLE_MASK_NV 0x8E51 +#define GL_SAMPLE_MASK_VALUE_NV 0x8E52 +#define GL_TEXTURE_BINDING_RENDERBUFFER_NV 0x8E53 +#define GL_TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV 0x8E54 +#define GL_TEXTURE_RENDERBUFFER_NV 0x8E55 +#define GL_SAMPLER_RENDERBUFFER_NV 0x8E56 +#define GL_INT_SAMPLER_RENDERBUFFER_NV 0x8E57 +#define GL_UNSIGNED_INT_SAMPLER_RENDERBUFFER_NV 0x8E58 +#define GL_MAX_SAMPLE_MASK_WORDS_NV 0x8E59 +typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVNVPROC)(GLenum pname, GLuint index, GLfloat * val); +typedef void (APIENTRYP PFNGLSAMPLEMASKINDEXEDNVPROC)(GLuint index, GLbitfield mask); +typedef void (APIENTRYP PFNGLTEXRENDERBUFFERNVPROC)(GLenum target, GLuint renderbuffer); +#endif /* GL_NV_explicit_multisample */ + +#ifndef GL_NV_fence +#define GL_NV_fence 1 +#define GL_ALL_COMPLETED_NV 0x84F2 +#define GL_FENCE_STATUS_NV 0x84F3 +#define GL_FENCE_CONDITION_NV 0x84F4 +typedef void (APIENTRYP PFNGLDELETEFENCESNVPROC)(GLsizei n, const GLuint * fences); +typedef void (APIENTRYP PFNGLGENFENCESNVPROC)(GLsizei n, GLuint * fences); +typedef GLboolean (APIENTRYP PFNGLISFENCENVPROC)(GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTFENCENVPROC)(GLuint fence); +typedef void (APIENTRYP PFNGLGETFENCEIVNVPROC)(GLuint fence, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLFINISHFENCENVPROC)(GLuint fence); +typedef void (APIENTRYP PFNGLSETFENCENVPROC)(GLuint fence, GLenum condition); +#endif /* GL_NV_fence */ + +#ifndef GL_NV_fill_rectangle +#define GL_NV_fill_rectangle 1 +#define GL_FILL_RECTANGLE_NV 0x933C +#endif /* GL_NV_fill_rectangle */ + +#ifndef GL_NV_float_buffer +#define GL_NV_float_buffer 1 +#define GL_FLOAT_R_NV 0x8880 +#define GL_FLOAT_RG_NV 0x8881 +#define GL_FLOAT_RGB_NV 0x8882 +#define GL_FLOAT_RGBA_NV 0x8883 +#define GL_FLOAT_R16_NV 0x8884 +#define GL_FLOAT_R32_NV 0x8885 +#define GL_FLOAT_RG16_NV 0x8886 +#define GL_FLOAT_RG32_NV 0x8887 +#define GL_FLOAT_RGB16_NV 0x8888 +#define GL_FLOAT_RGB32_NV 0x8889 +#define GL_FLOAT_RGBA16_NV 0x888A +#define GL_FLOAT_RGBA32_NV 0x888B +#define GL_TEXTURE_FLOAT_COMPONENTS_NV 0x888C +#define GL_FLOAT_CLEAR_COLOR_VALUE_NV 0x888D +#define GL_FLOAT_RGBA_MODE_NV 0x888E +#endif /* GL_NV_float_buffer */ + +#ifndef GL_NV_fog_distance +#define GL_NV_fog_distance 1 +#define GL_FOG_DISTANCE_MODE_NV 0x855A +#define GL_EYE_RADIAL_NV 0x855B +#define GL_EYE_PLANE_ABSOLUTE_NV 0x855C +#define GL_EYE_PLANE 0x2502 +#endif /* GL_NV_fog_distance */ + +#ifndef GL_NV_fragment_coverage_to_color +#define GL_NV_fragment_coverage_to_color 1 +#define GL_FRAGMENT_COVERAGE_TO_COLOR_NV 0x92DD +#define GL_FRAGMENT_COVERAGE_COLOR_NV 0x92DE +typedef void (APIENTRYP PFNGLFRAGMENTCOVERAGECOLORNVPROC)(GLuint color); +#endif /* GL_NV_fragment_coverage_to_color */ + +#ifndef GL_NV_fragment_program +#define GL_NV_fragment_program 1 +#define GL_MAX_FRAGMENT_PROGRAM_LOCAL_PARAMETERS_NV 0x8868 +#define GL_FRAGMENT_PROGRAM_NV 0x8870 +#define GL_MAX_TEXTURE_COORDS_NV 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS_NV 0x8872 +#define GL_FRAGMENT_PROGRAM_BINDING_NV 0x8873 +#define GL_PROGRAM_ERROR_STRING_NV 0x8874 +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FNVPROC)(GLuint id, GLsizei len, const GLubyte * name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FVNVPROC)(GLuint id, GLsizei len, const GLubyte * name, const GLfloat * v); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DNVPROC)(GLuint id, GLsizei len, const GLubyte * name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DVNVPROC)(GLuint id, GLsizei len, const GLubyte * name, const GLdouble * v); +typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERFVNVPROC)(GLuint id, GLsizei len, const GLubyte * name, GLfloat * params); +typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERDVNVPROC)(GLuint id, GLsizei len, const GLubyte * name, GLdouble * params); +#endif /* GL_NV_fragment_program */ + +#ifndef GL_NV_fragment_program2 +#define GL_NV_fragment_program2 1 +#define GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV 0x88F4 +#define GL_MAX_PROGRAM_CALL_DEPTH_NV 0x88F5 +#define GL_MAX_PROGRAM_IF_DEPTH_NV 0x88F6 +#define GL_MAX_PROGRAM_LOOP_DEPTH_NV 0x88F7 +#define GL_MAX_PROGRAM_LOOP_COUNT_NV 0x88F8 +#endif /* GL_NV_fragment_program2 */ + +#ifndef GL_NV_fragment_program4 +#define GL_NV_fragment_program4 1 +#endif /* GL_NV_fragment_program4 */ + +#ifndef GL_NV_fragment_program_option +#define GL_NV_fragment_program_option 1 +#endif /* GL_NV_fragment_program_option */ + +#ifndef GL_NV_fragment_shader_barycentric +#define GL_NV_fragment_shader_barycentric 1 +#endif /* GL_NV_fragment_shader_barycentric */ + +#ifndef GL_NV_fragment_shader_interlock +#define GL_NV_fragment_shader_interlock 1 +#endif /* GL_NV_fragment_shader_interlock */ + +#ifndef GL_NV_framebuffer_mixed_samples +#define GL_NV_framebuffer_mixed_samples 1 +#define GL_RASTER_MULTISAMPLE_EXT 0x9327 +#define GL_COVERAGE_MODULATION_TABLE_NV 0x9331 +#define GL_RASTER_SAMPLES_EXT 0x9328 +#define GL_MAX_RASTER_SAMPLES_EXT 0x9329 +#define GL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT 0x932A +#define GL_MULTISAMPLE_RASTERIZATION_ALLOWED_EXT 0x932B +#define GL_EFFECTIVE_RASTER_SAMPLES_EXT 0x932C +#define GL_COLOR_SAMPLES_NV 0x8E20 +#define GL_DEPTH_SAMPLES_NV 0x932D +#define GL_STENCIL_SAMPLES_NV 0x932E +#define GL_MIXED_DEPTH_SAMPLES_SUPPORTED_NV 0x932F +#define GL_MIXED_STENCIL_SAMPLES_SUPPORTED_NV 0x9330 +#define GL_COVERAGE_MODULATION_NV 0x9332 +#define GL_COVERAGE_MODULATION_TABLE_SIZE_NV 0x9333 +typedef void (APIENTRYP PFNGLCOVERAGEMODULATIONTABLENVPROC)(GLsizei n, const GLfloat * v); +typedef void (APIENTRYP PFNGLGETCOVERAGEMODULATIONTABLENVPROC)(GLsizei bufSize, GLfloat * v); +typedef void (APIENTRYP PFNGLCOVERAGEMODULATIONNVPROC)(GLenum components); +#endif /* GL_NV_framebuffer_mixed_samples */ + +#ifndef GL_NV_framebuffer_multisample_coverage +#define GL_NV_framebuffer_multisample_coverage 1 +#define GL_RENDERBUFFER_COVERAGE_SAMPLES_NV 0x8CAB +#define GL_RENDERBUFFER_COLOR_SAMPLES_NV 0x8E10 +#define GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV 0x8E11 +#define GL_MULTISAMPLE_COVERAGE_MODES_NV 0x8E12 +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC)(GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +#endif /* GL_NV_framebuffer_multisample_coverage */ + +#ifndef GL_NV_geometry_program4 +#define GL_NV_geometry_program4 1 +#define GL_LINES_ADJACENCY_EXT 0x000A +#define GL_LINE_STRIP_ADJACENCY_EXT 0x000B +#define GL_TRIANGLES_ADJACENCY_EXT 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY_EXT 0x000D +#define GL_GEOMETRY_PROGRAM_NV 0x8C26 +#define GL_MAX_PROGRAM_OUTPUT_VERTICES_NV 0x8C27 +#define GL_MAX_PROGRAM_TOTAL_OUTPUT_COMPONENTS_NV 0x8C28 +#define GL_GEOMETRY_VERTICES_OUT_EXT 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_EXT 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_EXT 0x8DDC +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT 0x8DA9 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4 +#define GL_PROGRAM_POINT_SIZE_EXT 0x8642 +typedef void (APIENTRYP PFNGLPROGRAMVERTEXLIMITNVPROC)(GLenum target, GLint limit); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREEXTPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEEXTPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif /* GL_NV_geometry_program4 */ + +#ifndef GL_NV_geometry_shader4 +#define GL_NV_geometry_shader4 1 +#endif /* GL_NV_geometry_shader4 */ + +#ifndef GL_NV_geometry_shader_passthrough +#define GL_NV_geometry_shader_passthrough 1 +#endif /* GL_NV_geometry_shader_passthrough */ + +#ifndef GL_NV_gpu_program4 +#define GL_NV_gpu_program4 1 +#define GL_MIN_PROGRAM_TEXEL_OFFSET_NV 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET_NV 0x8905 +#define GL_PROGRAM_ATTRIB_COMPONENTS_NV 0x8906 +#define GL_PROGRAM_RESULT_COMPONENTS_NV 0x8907 +#define GL_MAX_PROGRAM_ATTRIB_COMPONENTS_NV 0x8908 +#define GL_MAX_PROGRAM_RESULT_COMPONENTS_NV 0x8909 +#define GL_MAX_PROGRAM_GENERIC_ATTRIBS_NV 0x8DA5 +#define GL_MAX_PROGRAM_GENERIC_RESULTS_NV 0x8DA6 +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4INVPROC)(GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC)(GLenum target, GLuint index, const GLint * params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4IVNVPROC)(GLenum target, GLuint index, GLsizei count, const GLint * params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UINVPROC)(GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UIVNVPROC)(GLenum target, GLuint index, const GLuint * params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4UIVNVPROC)(GLenum target, GLuint index, GLsizei count, const GLuint * params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4INVPROC)(GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4IVNVPROC)(GLenum target, GLuint index, const GLint * params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4IVNVPROC)(GLenum target, GLuint index, GLsizei count, const GLint * params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UINVPROC)(GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UIVNVPROC)(GLenum target, GLuint index, const GLuint * params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4UIVNVPROC)(GLenum target, GLuint index, GLsizei count, const GLuint * params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIIVNVPROC)(GLenum target, GLuint index, GLint * params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIUIVNVPROC)(GLenum target, GLuint index, GLuint * params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIIVNVPROC)(GLenum target, GLuint index, GLint * params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIUIVNVPROC)(GLenum target, GLuint index, GLuint * params); +#endif /* GL_NV_gpu_program4 */ + +#ifndef GL_NV_gpu_program5 +#define GL_NV_gpu_program5 1 +#define GL_MAX_GEOMETRY_PROGRAM_INVOCATIONS_NV 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5C +#define GL_FRAGMENT_PROGRAM_INTERPOLATION_OFFSET_BITS_NV 0x8E5D +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_NV 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_NV 0x8E5F +#define GL_MAX_PROGRAM_SUBROUTINE_PARAMETERS_NV 0x8F44 +#define GL_MAX_PROGRAM_SUBROUTINE_NUM_NV 0x8F45 +typedef void (APIENTRYP PFNGLPROGRAMSUBROUTINEPARAMETERSUIVNVPROC)(GLenum target, GLsizei count, const GLuint * params); +typedef void (APIENTRYP PFNGLGETPROGRAMSUBROUTINEPARAMETERUIVNVPROC)(GLenum target, GLuint index, GLuint * param); +#endif /* GL_NV_gpu_program5 */ + +#ifndef GL_NV_gpu_program5_mem_extended +#define GL_NV_gpu_program5_mem_extended 1 +#endif /* GL_NV_gpu_program5_mem_extended */ + +#ifndef GL_NV_gpu_shader5 +#define GL_NV_gpu_shader5 1 +#define GL_INT64_NV 0x140E +#define GL_UNSIGNED_INT64_NV 0x140F +#define GL_INT8_NV 0x8FE0 +#define GL_INT8_VEC2_NV 0x8FE1 +#define GL_INT8_VEC3_NV 0x8FE2 +#define GL_INT8_VEC4_NV 0x8FE3 +#define GL_INT16_NV 0x8FE4 +#define GL_INT16_VEC2_NV 0x8FE5 +#define GL_INT16_VEC3_NV 0x8FE6 +#define GL_INT16_VEC4_NV 0x8FE7 +#define GL_INT64_VEC2_NV 0x8FE9 +#define GL_INT64_VEC3_NV 0x8FEA +#define GL_INT64_VEC4_NV 0x8FEB +#define GL_UNSIGNED_INT8_NV 0x8FEC +#define GL_UNSIGNED_INT8_VEC2_NV 0x8FED +#define GL_UNSIGNED_INT8_VEC3_NV 0x8FEE +#define GL_UNSIGNED_INT8_VEC4_NV 0x8FEF +#define GL_UNSIGNED_INT16_NV 0x8FF0 +#define GL_UNSIGNED_INT16_VEC2_NV 0x8FF1 +#define GL_UNSIGNED_INT16_VEC3_NV 0x8FF2 +#define GL_UNSIGNED_INT16_VEC4_NV 0x8FF3 +#define GL_UNSIGNED_INT64_VEC2_NV 0x8FF5 +#define GL_UNSIGNED_INT64_VEC3_NV 0x8FF6 +#define GL_UNSIGNED_INT64_VEC4_NV 0x8FF7 +#define GL_FLOAT16_NV 0x8FF8 +#define GL_FLOAT16_VEC2_NV 0x8FF9 +#define GL_FLOAT16_VEC3_NV 0x8FFA +#define GL_FLOAT16_VEC4_NV 0x8FFB +#define GL_PATCHES 0x000E +#endif /* GL_NV_gpu_shader5 */ + +#ifndef GL_NV_half_float +#define GL_NV_half_float 1 +typedef unsigned short GLhalfNV; +#define GL_HALF_FLOAT_NV 0x140B +typedef void (APIENTRYP PFNGLVERTEX2HNVPROC)(GLhalfNV x, GLhalfNV y); +typedef void (APIENTRYP PFNGLVERTEX2HVNVPROC)(const GLhalfNV * v); +typedef void (APIENTRYP PFNGLVERTEX3HNVPROC)(GLhalfNV x, GLhalfNV y, GLhalfNV z); +typedef void (APIENTRYP PFNGLVERTEX3HVNVPROC)(const GLhalfNV * v); +typedef void (APIENTRYP PFNGLVERTEX4HNVPROC)(GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +typedef void (APIENTRYP PFNGLVERTEX4HVNVPROC)(const GLhalfNV * v); +typedef void (APIENTRYP PFNGLNORMAL3HNVPROC)(GLhalfNV nx, GLhalfNV ny, GLhalfNV nz); +typedef void (APIENTRYP PFNGLNORMAL3HVNVPROC)(const GLhalfNV * v); +typedef void (APIENTRYP PFNGLCOLOR3HNVPROC)(GLhalfNV red, GLhalfNV green, GLhalfNV blue); +typedef void (APIENTRYP PFNGLCOLOR3HVNVPROC)(const GLhalfNV * v); +typedef void (APIENTRYP PFNGLCOLOR4HNVPROC)(GLhalfNV red, GLhalfNV green, GLhalfNV blue, GLhalfNV alpha); +typedef void (APIENTRYP PFNGLCOLOR4HVNVPROC)(const GLhalfNV * v); +typedef void (APIENTRYP PFNGLTEXCOORD1HNVPROC)(GLhalfNV s); +typedef void (APIENTRYP PFNGLTEXCOORD1HVNVPROC)(const GLhalfNV * v); +typedef void (APIENTRYP PFNGLTEXCOORD2HNVPROC)(GLhalfNV s, GLhalfNV t); +typedef void (APIENTRYP PFNGLTEXCOORD2HVNVPROC)(const GLhalfNV * v); +typedef void (APIENTRYP PFNGLTEXCOORD3HNVPROC)(GLhalfNV s, GLhalfNV t, GLhalfNV r); +typedef void (APIENTRYP PFNGLTEXCOORD3HVNVPROC)(const GLhalfNV * v); +typedef void (APIENTRYP PFNGLTEXCOORD4HNVPROC)(GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +typedef void (APIENTRYP PFNGLTEXCOORD4HVNVPROC)(const GLhalfNV * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1HNVPROC)(GLenum target, GLhalfNV s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1HVNVPROC)(GLenum target, const GLhalfNV * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2HNVPROC)(GLenum target, GLhalfNV s, GLhalfNV t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2HVNVPROC)(GLenum target, const GLhalfNV * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3HNVPROC)(GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3HVNVPROC)(GLenum target, const GLhalfNV * v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4HNVPROC)(GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4HVNVPROC)(GLenum target, const GLhalfNV * v); +typedef void (APIENTRYP PFNGLFOGCOORDHNVPROC)(GLhalfNV fog); +typedef void (APIENTRYP PFNGLFOGCOORDHVNVPROC)(const GLhalfNV * fog); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HNVPROC)(GLhalfNV red, GLhalfNV green, GLhalfNV blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HVNVPROC)(const GLhalfNV * v); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTHNVPROC)(GLhalfNV weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTHVNVPROC)(const GLhalfNV * weight); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1HNVPROC)(GLuint index, GLhalfNV x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1HVNVPROC)(GLuint index, const GLhalfNV * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2HNVPROC)(GLuint index, GLhalfNV x, GLhalfNV y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2HVNVPROC)(GLuint index, const GLhalfNV * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3HNVPROC)(GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3HVNVPROC)(GLuint index, const GLhalfNV * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4HNVPROC)(GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4HVNVPROC)(GLuint index, const GLhalfNV * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1HVNVPROC)(GLuint index, GLsizei n, const GLhalfNV * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2HVNVPROC)(GLuint index, GLsizei n, const GLhalfNV * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3HVNVPROC)(GLuint index, GLsizei n, const GLhalfNV * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4HVNVPROC)(GLuint index, GLsizei n, const GLhalfNV * v); +#endif /* GL_NV_half_float */ + +#ifndef GL_NV_internalformat_sample_query +#define GL_NV_internalformat_sample_query 1 +#define GL_RENDERBUFFER 0x8D41 +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#define GL_MULTISAMPLES_NV 0x9371 +#define GL_SUPERSAMPLE_SCALE_X_NV 0x9372 +#define GL_SUPERSAMPLE_SCALE_Y_NV 0x9373 +#define GL_CONFORMANT_NV 0x9374 +typedef void (APIENTRYP PFNGLGETINTERNALFORMATSAMPLEIVNVPROC)(GLenum target, GLenum internalformat, GLsizei samples, GLenum pname, GLsizei count, GLint * params); +#endif /* GL_NV_internalformat_sample_query */ + +#ifndef GL_NV_light_max_exponent +#define GL_NV_light_max_exponent 1 +#define GL_MAX_SHININESS_NV 0x8504 +#define GL_MAX_SPOT_EXPONENT_NV 0x8505 +#endif /* GL_NV_light_max_exponent */ + +#ifndef GL_NV_gpu_multicast +#define GL_NV_gpu_multicast 1 +#define GL_PER_GPU_STORAGE_BIT_NV 0x0800 +#define GL_MULTICAST_GPUS_NV 0x92BA +#define GL_RENDER_GPU_MASK_NV 0x9558 +#define GL_PER_GPU_STORAGE_NV 0x9548 +#define GL_MULTICAST_PROGRAMMABLE_SAMPLE_LOCATION_NV 0x9549 +typedef void (APIENTRYP PFNGLRENDERGPUMASKNVPROC)(GLbitfield mask); +typedef void (APIENTRYP PFNGLMULTICASTBUFFERSUBDATANVPROC)(GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void * data); +typedef void (APIENTRYP PFNGLMULTICASTCOPYBUFFERSUBDATANVPROC)(GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLMULTICASTCOPYIMAGESUBDATANVPROC)(GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +typedef void (APIENTRYP PFNGLMULTICASTBLITFRAMEBUFFERNVPROC)(GLuint srcGpu, GLuint dstGpu, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void (APIENTRYP PFNGLMULTICASTFRAMEBUFFERSAMPLELOCATIONSFVNVPROC)(GLuint gpu, GLuint framebuffer, GLuint start, GLsizei count, const GLfloat * v); +typedef void (APIENTRYP PFNGLMULTICASTBARRIERNVPROC)(void); +typedef void (APIENTRYP PFNGLMULTICASTWAITSYNCNVPROC)(GLuint signalGpu, GLbitfield waitGpuMask); +typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTIVNVPROC)(GLuint gpu, GLuint id, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTUIVNVPROC)(GLuint gpu, GLuint id, GLenum pname, GLuint * params); +typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTI64VNVPROC)(GLuint gpu, GLuint id, GLenum pname, GLint64 * params); +typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTUI64VNVPROC)(GLuint gpu, GLuint id, GLenum pname, GLuint64 * params); +#endif /* GL_NV_gpu_multicast */ + +#ifndef GL_NVX_gpu_multicast2 +#define GL_NVX_gpu_multicast2 1 +#define GL_UPLOAD_GPU_MASK_NVX 0x954A +typedef void (APIENTRYP PFNGLUPLOADGPUMASKNVXPROC)(GLbitfield mask); +typedef void (APIENTRYP PFNGLMULTICASTVIEWPORTARRAYVNVXPROC)(GLuint gpu, GLuint first, GLsizei count, const GLfloat * v); +typedef void (APIENTRYP PFNGLMULTICASTVIEWPORTPOSITIONWSCALENVXPROC)(GLuint gpu, GLuint index, GLfloat xcoeff, GLfloat ycoeff); +typedef void (APIENTRYP PFNGLMULTICASTSCISSORARRAYVNVXPROC)(GLuint gpu, GLuint first, GLsizei count, const GLint * v); +typedef GLuint (APIENTRYP PFNGLASYNCCOPYBUFFERSUBDATANVXPROC)(GLsizei waitSemaphoreCount, const GLuint * waitSemaphoreArray, const GLuint64 * fenceValueArray, GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size, GLsizei signalSemaphoreCount, const GLuint * signalSemaphoreArray, const GLuint64 * signalValueArray); +typedef GLuint (APIENTRYP PFNGLASYNCCOPYIMAGESUBDATANVXPROC)(GLsizei waitSemaphoreCount, const GLuint * waitSemaphoreArray, const GLuint64 * waitValueArray, GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth, GLsizei signalSemaphoreCount, const GLuint * signalSemaphoreArray, const GLuint64 * signalValueArray); +#endif /* GL_NVX_gpu_multicast2 */ + +#ifndef GL_NVX_progress_fence +#define GL_NVX_progress_fence 1 +typedef GLuint (APIENTRYP PFNGLCREATEPROGRESSFENCENVXPROC)(void); +typedef void (APIENTRYP PFNGLSIGNALSEMAPHOREUI64NVXPROC)(GLuint signalGpu, GLsizei fenceObjectCount, const GLuint * semaphoreArray, const GLuint64 * fenceValueArray); +typedef void (APIENTRYP PFNGLWAITSEMAPHOREUI64NVXPROC)(GLuint waitGpu, GLsizei fenceObjectCount, const GLuint * semaphoreArray, const GLuint64 * fenceValueArray); +typedef void (APIENTRYP PFNGLCLIENTWAITSEMAPHOREUI64NVXPROC)(GLsizei fenceObjectCount, const GLuint * semaphoreArray, const GLuint64 * fenceValueArray); +#endif /* GL_NVX_progress_fence */ + +#ifndef GL_NV_memory_attachment +#define GL_NV_memory_attachment 1 +#define GL_ATTACHED_MEMORY_OBJECT_NV 0x95A4 +#define GL_ATTACHED_MEMORY_OFFSET_NV 0x95A5 +#define GL_MEMORY_ATTACHABLE_ALIGNMENT_NV 0x95A6 +#define GL_MEMORY_ATTACHABLE_SIZE_NV 0x95A7 +#define GL_MEMORY_ATTACHABLE_NV 0x95A8 +#define GL_DETACHED_MEMORY_INCARNATION_NV 0x95A9 +#define GL_DETACHED_TEXTURES_NV 0x95AA +#define GL_DETACHED_BUFFERS_NV 0x95AB +#define GL_MAX_DETACHED_TEXTURES_NV 0x95AC +#define GL_MAX_DETACHED_BUFFERS_NV 0x95AD +typedef void (APIENTRYP PFNGLGETMEMORYOBJECTDETACHEDRESOURCESUIVNVPROC)(GLuint memory, GLenum pname, GLint first, GLsizei count, GLuint * params); +typedef void (APIENTRYP PFNGLRESETMEMORYOBJECTPARAMETERNVPROC)(GLuint memory, GLenum pname); +typedef void (APIENTRYP PFNGLTEXATTACHMEMORYNVPROC)(GLenum target, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLBUFFERATTACHMEMORYNVPROC)(GLenum target, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXTUREATTACHMEMORYNVPROC)(GLuint texture, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLNAMEDBUFFERATTACHMEMORYNVPROC)(GLuint buffer, GLuint memory, GLuint64 offset); +#endif /* GL_NV_memory_attachment */ + +#ifndef GL_NV_memory_object_sparse +#define GL_NV_memory_object_sparse 1 +typedef void (APIENTRYP PFNGLBUFFERPAGECOMMITMENTMEMNVPROC)(GLenum target, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); +typedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTMEMNVPROC)(GLenum target, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); +typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTMEMNVPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); +typedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTMEMNVPROC)(GLuint texture, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); +#endif /* GL_NV_memory_object_sparse */ + +#ifndef GL_NV_mesh_shader +#define GL_NV_mesh_shader 1 +#define GL_MESH_SHADER_NV 0x9559 +#define GL_TASK_SHADER_NV 0x955A +#define GL_MAX_MESH_UNIFORM_BLOCKS_NV 0x8E60 +#define GL_MAX_MESH_TEXTURE_IMAGE_UNITS_NV 0x8E61 +#define GL_MAX_MESH_IMAGE_UNIFORMS_NV 0x8E62 +#define GL_MAX_MESH_UNIFORM_COMPONENTS_NV 0x8E63 +#define GL_MAX_MESH_ATOMIC_COUNTER_BUFFERS_NV 0x8E64 +#define GL_MAX_MESH_ATOMIC_COUNTERS_NV 0x8E65 +#define GL_MAX_MESH_SHADER_STORAGE_BLOCKS_NV 0x8E66 +#define GL_MAX_COMBINED_MESH_UNIFORM_COMPONENTS_NV 0x8E67 +#define GL_MAX_TASK_UNIFORM_BLOCKS_NV 0x8E68 +#define GL_MAX_TASK_TEXTURE_IMAGE_UNITS_NV 0x8E69 +#define GL_MAX_TASK_IMAGE_UNIFORMS_NV 0x8E6A +#define GL_MAX_TASK_UNIFORM_COMPONENTS_NV 0x8E6B +#define GL_MAX_TASK_ATOMIC_COUNTER_BUFFERS_NV 0x8E6C +#define GL_MAX_TASK_ATOMIC_COUNTERS_NV 0x8E6D +#define GL_MAX_TASK_SHADER_STORAGE_BLOCKS_NV 0x8E6E +#define GL_MAX_COMBINED_TASK_UNIFORM_COMPONENTS_NV 0x8E6F +#define GL_MAX_MESH_WORK_GROUP_INVOCATIONS_NV 0x95A2 +#define GL_MAX_TASK_WORK_GROUP_INVOCATIONS_NV 0x95A3 +#define GL_MAX_MESH_TOTAL_MEMORY_SIZE_NV 0x9536 +#define GL_MAX_TASK_TOTAL_MEMORY_SIZE_NV 0x9537 +#define GL_MAX_MESH_OUTPUT_VERTICES_NV 0x9538 +#define GL_MAX_MESH_OUTPUT_PRIMITIVES_NV 0x9539 +#define GL_MAX_TASK_OUTPUT_COUNT_NV 0x953A +#define GL_MAX_DRAW_MESH_TASKS_COUNT_NV 0x953D +#define GL_MAX_MESH_VIEWS_NV 0x9557 +#define GL_MESH_OUTPUT_PER_VERTEX_GRANULARITY_NV 0x92DF +#define GL_MESH_OUTPUT_PER_PRIMITIVE_GRANULARITY_NV 0x9543 +#define GL_MAX_MESH_WORK_GROUP_SIZE_NV 0x953B +#define GL_MAX_TASK_WORK_GROUP_SIZE_NV 0x953C +#define GL_MESH_WORK_GROUP_SIZE_NV 0x953E +#define GL_TASK_WORK_GROUP_SIZE_NV 0x953F +#define GL_MESH_VERTICES_OUT_NV 0x9579 +#define GL_MESH_PRIMITIVES_OUT_NV 0x957A +#define GL_MESH_OUTPUT_TYPE_NV 0x957B +#define GL_UNIFORM_BLOCK_REFERENCED_BY_MESH_SHADER_NV 0x959C +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TASK_SHADER_NV 0x959D +#define GL_REFERENCED_BY_MESH_SHADER_NV 0x95A0 +#define GL_REFERENCED_BY_TASK_SHADER_NV 0x95A1 +#define GL_MESH_SHADER_BIT_NV 0x00000040 +#define GL_TASK_SHADER_BIT_NV 0x00000080 +#define GL_MESH_SUBROUTINE_NV 0x957C +#define GL_TASK_SUBROUTINE_NV 0x957D +#define GL_MESH_SUBROUTINE_UNIFORM_NV 0x957E +#define GL_TASK_SUBROUTINE_UNIFORM_NV 0x957F +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_MESH_SHADER_NV 0x959E +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TASK_SHADER_NV 0x959F +typedef void (APIENTRYP PFNGLDRAWMESHTASKSNVPROC)(GLuint first, GLuint count); +typedef void (APIENTRYP PFNGLDRAWMESHTASKSINDIRECTNVPROC)(GLintptr indirect); +typedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTNVPROC)(GLintptr indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTCOUNTNVPROC)(GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#endif /* GL_NV_mesh_shader */ + +#ifndef GL_NV_multisample_coverage +#define GL_NV_multisample_coverage 1 +#define GL_SAMPLES_ARB 0x80A9 +#define GL_COLOR_SAMPLES_NV 0x8E20 +#endif /* GL_NV_multisample_coverage */ + +#ifndef GL_NV_multisample_filter_hint +#define GL_NV_multisample_filter_hint 1 +#define GL_MULTISAMPLE_FILTER_HINT_NV 0x8534 +#endif /* GL_NV_multisample_filter_hint */ + +#ifndef GL_NV_occlusion_query +#define GL_NV_occlusion_query 1 +#define GL_PIXEL_COUNTER_BITS_NV 0x8864 +#define GL_CURRENT_OCCLUSION_QUERY_ID_NV 0x8865 +#define GL_PIXEL_COUNT_NV 0x8866 +#define GL_PIXEL_COUNT_AVAILABLE_NV 0x8867 +typedef void (APIENTRYP PFNGLGENOCCLUSIONQUERIESNVPROC)(GLsizei n, GLuint * ids); +typedef void (APIENTRYP PFNGLDELETEOCCLUSIONQUERIESNVPROC)(GLsizei n, const GLuint * ids); +typedef GLboolean (APIENTRYP PFNGLISOCCLUSIONQUERYNVPROC)(GLuint id); +typedef void (APIENTRYP PFNGLBEGINOCCLUSIONQUERYNVPROC)(GLuint id); +typedef void (APIENTRYP PFNGLENDOCCLUSIONQUERYNVPROC)(void); +typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYIVNVPROC)(GLuint id, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYUIVNVPROC)(GLuint id, GLenum pname, GLuint * params); +#endif /* GL_NV_occlusion_query */ + +#ifndef GL_NV_packed_depth_stencil +#define GL_NV_packed_depth_stencil 1 +#define GL_DEPTH_STENCIL_NV 0x84F9 +#define GL_UNSIGNED_INT_24_8_NV 0x84FA +#endif /* GL_NV_packed_depth_stencil */ + +#ifndef GL_NV_parameter_buffer_object +#define GL_NV_parameter_buffer_object 1 +#define GL_MAX_PROGRAM_PARAMETER_BUFFER_BINDINGS_NV 0x8DA0 +#define GL_MAX_PROGRAM_PARAMETER_BUFFER_SIZE_NV 0x8DA1 +#define GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV 0x8DA2 +#define GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV 0x8DA3 +#define GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV 0x8DA4 +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSFVNVPROC)(GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLfloat * params); +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIIVNVPROC)(GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLint * params); +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIUIVNVPROC)(GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLuint * params); +#endif /* GL_NV_parameter_buffer_object */ + +#ifndef GL_NV_parameter_buffer_object2 +#define GL_NV_parameter_buffer_object2 1 +#endif /* GL_NV_parameter_buffer_object2 */ + +#ifndef GL_NV_path_rendering +#define GL_NV_path_rendering 1 +#define GL_PATH_FORMAT_SVG_NV 0x9070 +#define GL_PATH_FORMAT_PS_NV 0x9071 +#define GL_STANDARD_FONT_NAME_NV 0x9072 +#define GL_SYSTEM_FONT_NAME_NV 0x9073 +#define GL_FILE_NAME_NV 0x9074 +#define GL_PATH_STROKE_WIDTH_NV 0x9075 +#define GL_PATH_END_CAPS_NV 0x9076 +#define GL_PATH_INITIAL_END_CAP_NV 0x9077 +#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 +#define GL_PATH_JOIN_STYLE_NV 0x9079 +#define GL_PATH_MITER_LIMIT_NV 0x907A +#define GL_PATH_DASH_CAPS_NV 0x907B +#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C +#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D +#define GL_PATH_DASH_OFFSET_NV 0x907E +#define GL_PATH_CLIENT_LENGTH_NV 0x907F +#define GL_PATH_FILL_MODE_NV 0x9080 +#define GL_PATH_FILL_MASK_NV 0x9081 +#define GL_PATH_FILL_COVER_MODE_NV 0x9082 +#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 +#define GL_PATH_STROKE_MASK_NV 0x9084 +#define GL_COUNT_UP_NV 0x9088 +#define GL_COUNT_DOWN_NV 0x9089 +#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A +#define GL_CONVEX_HULL_NV 0x908B +#define GL_BOUNDING_BOX_NV 0x908D +#define GL_TRANSLATE_X_NV 0x908E +#define GL_TRANSLATE_Y_NV 0x908F +#define GL_TRANSLATE_2D_NV 0x9090 +#define GL_TRANSLATE_3D_NV 0x9091 +#define GL_AFFINE_2D_NV 0x9092 +#define GL_AFFINE_3D_NV 0x9094 +#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 +#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 +#define GL_UTF8_NV 0x909A +#define GL_UTF16_NV 0x909B +#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C +#define GL_PATH_COMMAND_COUNT_NV 0x909D +#define GL_PATH_COORD_COUNT_NV 0x909E +#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F +#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 +#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 +#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 +#define GL_SQUARE_NV 0x90A3 +#define GL_ROUND_NV 0x90A4 +#define GL_TRIANGULAR_NV 0x90A5 +#define GL_BEVEL_NV 0x90A6 +#define GL_MITER_REVERT_NV 0x90A7 +#define GL_MITER_TRUNCATE_NV 0x90A8 +#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 +#define GL_USE_MISSING_GLYPH_NV 0x90AA +#define GL_PATH_ERROR_POSITION_NV 0x90AB +#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD +#define GL_ADJACENT_PAIRS_NV 0x90AE +#define GL_FIRST_TO_REST_NV 0x90AF +#define GL_PATH_GEN_MODE_NV 0x90B0 +#define GL_PATH_GEN_COEFF_NV 0x90B1 +#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 +#define GL_PATH_STENCIL_FUNC_NV 0x90B7 +#define GL_PATH_STENCIL_REF_NV 0x90B8 +#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 +#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD +#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE +#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF +#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 +#define GL_MOVE_TO_RESETS_NV 0x90B5 +#define GL_MOVE_TO_CONTINUES_NV 0x90B6 +#define GL_CLOSE_PATH_NV 0x00 +#define GL_MOVE_TO_NV 0x02 +#define GL_RELATIVE_MOVE_TO_NV 0x03 +#define GL_LINE_TO_NV 0x04 +#define GL_RELATIVE_LINE_TO_NV 0x05 +#define GL_HORIZONTAL_LINE_TO_NV 0x06 +#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 +#define GL_VERTICAL_LINE_TO_NV 0x08 +#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 +#define GL_QUADRATIC_CURVE_TO_NV 0x0A +#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B +#define GL_CUBIC_CURVE_TO_NV 0x0C +#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D +#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E +#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F +#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 +#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 +#define GL_SMALL_CCW_ARC_TO_NV 0x12 +#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 +#define GL_SMALL_CW_ARC_TO_NV 0x14 +#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 +#define GL_LARGE_CCW_ARC_TO_NV 0x16 +#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 +#define GL_LARGE_CW_ARC_TO_NV 0x18 +#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 +#define GL_RESTART_PATH_NV 0xF0 +#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 +#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 +#define GL_RECT_NV 0xF6 +#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 +#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA +#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC +#define GL_ARC_TO_NV 0xFE +#define GL_RELATIVE_ARC_TO_NV 0xFF +#define GL_BOLD_BIT_NV 0x01 +#define GL_ITALIC_BIT_NV 0x02 +#define GL_GLYPH_WIDTH_BIT_NV 0x01 +#define GL_GLYPH_HEIGHT_BIT_NV 0x02 +#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 +#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 +#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 +#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 +#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 +#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 +#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 +#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 +#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 +#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 +#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 +#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 +#define GL_FONT_ASCENDER_BIT_NV 0x00200000 +#define GL_FONT_DESCENDER_BIT_NV 0x00400000 +#define GL_FONT_HEIGHT_BIT_NV 0x00800000 +#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 +#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 +#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 +#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 +#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 +#define GL_ROUNDED_RECT_NV 0xE8 +#define GL_RELATIVE_ROUNDED_RECT_NV 0xE9 +#define GL_ROUNDED_RECT2_NV 0xEA +#define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB +#define GL_ROUNDED_RECT4_NV 0xEC +#define GL_RELATIVE_ROUNDED_RECT4_NV 0xED +#define GL_ROUNDED_RECT8_NV 0xEE +#define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF +#define GL_RELATIVE_RECT_NV 0xF7 +#define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368 +#define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369 +#define GL_FONT_UNAVAILABLE_NV 0x936A +#define GL_FONT_UNINTELLIGIBLE_NV 0x936B +#define GL_CONIC_CURVE_TO_NV 0x1A +#define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B +#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000 +#define GL_STANDARD_FONT_FORMAT_NV 0x936C +#define GL_2_BYTES_NV 0x1407 +#define GL_3_BYTES_NV 0x1408 +#define GL_4_BYTES_NV 0x1409 +#define GL_EYE_LINEAR_NV 0x2400 +#define GL_OBJECT_LINEAR_NV 0x2401 +#define GL_CONSTANT_NV 0x8576 +#define GL_PATH_FOG_GEN_MODE_NV 0x90AC +#define GL_PRIMARY_COLOR 0x8577 +#define GL_PRIMARY_COLOR_NV 0x852C +#define GL_SECONDARY_COLOR_NV 0x852D +#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 +#define GL_PATH_PROJECTION_NV 0x1701 +#define GL_PATH_MODELVIEW_NV 0x1700 +#define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3 +#define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6 +#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36 +#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3 +#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4 +#define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7 +#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38 +#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4 +#define GL_FRAGMENT_INPUT_NV 0x936D +typedef GLuint (APIENTRYP PFNGLGENPATHSNVPROC)(GLsizei range); +typedef void (APIENTRYP PFNGLDELETEPATHSNVPROC)(GLuint path, GLsizei range); +typedef GLboolean (APIENTRYP PFNGLISPATHNVPROC)(GLuint path); +typedef void (APIENTRYP PFNGLPATHCOMMANDSNVPROC)(GLuint path, GLsizei numCommands, const GLubyte * commands, GLsizei numCoords, GLenum coordType, const void * coords); +typedef void (APIENTRYP PFNGLPATHCOORDSNVPROC)(GLuint path, GLsizei numCoords, GLenum coordType, const void * coords); +typedef void (APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC)(GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte * commands, GLsizei numCoords, GLenum coordType, const void * coords); +typedef void (APIENTRYP PFNGLPATHSUBCOORDSNVPROC)(GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void * coords); +typedef void (APIENTRYP PFNGLPATHSTRINGNVPROC)(GLuint path, GLenum format, GLsizei length, const void * pathString); +typedef void (APIENTRYP PFNGLPATHGLYPHSNVPROC)(GLuint firstPathName, GLenum fontTarget, const void * fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void * charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (APIENTRYP PFNGLPATHGLYPHRANGENVPROC)(GLuint firstPathName, GLenum fontTarget, const void * fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (APIENTRYP PFNGLWEIGHTPATHSNVPROC)(GLuint resultPath, GLsizei numPaths, const GLuint * paths, const GLfloat * weights); +typedef void (APIENTRYP PFNGLCOPYPATHNVPROC)(GLuint resultPath, GLuint srcPath); +typedef void (APIENTRYP PFNGLINTERPOLATEPATHSNVPROC)(GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +typedef void (APIENTRYP PFNGLTRANSFORMPATHNVPROC)(GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat * transformValues); +typedef void (APIENTRYP PFNGLPATHPARAMETERIVNVPROC)(GLuint path, GLenum pname, const GLint * value); +typedef void (APIENTRYP PFNGLPATHPARAMETERINVPROC)(GLuint path, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLPATHPARAMETERFVNVPROC)(GLuint path, GLenum pname, const GLfloat * value); +typedef void (APIENTRYP PFNGLPATHPARAMETERFNVPROC)(GLuint path, GLenum pname, GLfloat value); +typedef void (APIENTRYP PFNGLPATHDASHARRAYNVPROC)(GLuint path, GLsizei dashCount, const GLfloat * dashArray); +typedef void (APIENTRYP PFNGLPATHSTENCILFUNCNVPROC)(GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC)(GLfloat factor, GLfloat units); +typedef void (APIENTRYP PFNGLSTENCILFILLPATHNVPROC)(GLuint path, GLenum fillMode, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC)(GLuint path, GLint reference, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC)(GLsizei numPaths, GLenum pathNameType, const void * paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat * transformValues); +typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC)(GLsizei numPaths, GLenum pathNameType, const void * paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat * transformValues); +typedef void (APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC)(GLenum func); +typedef void (APIENTRYP PFNGLCOVERFILLPATHNVPROC)(GLuint path, GLenum coverMode); +typedef void (APIENTRYP PFNGLCOVERSTROKEPATHNVPROC)(GLuint path, GLenum coverMode); +typedef void (APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC)(GLsizei numPaths, GLenum pathNameType, const void * paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat * transformValues); +typedef void (APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC)(GLsizei numPaths, GLenum pathNameType, const void * paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat * transformValues); +typedef void (APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC)(GLuint path, GLenum pname, GLint * value); +typedef void (APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC)(GLuint path, GLenum pname, GLfloat * value); +typedef void (APIENTRYP PFNGLGETPATHCOMMANDSNVPROC)(GLuint path, GLubyte * commands); +typedef void (APIENTRYP PFNGLGETPATHCOORDSNVPROC)(GLuint path, GLfloat * coords); +typedef void (APIENTRYP PFNGLGETPATHDASHARRAYNVPROC)(GLuint path, GLfloat * dashArray); +typedef void (APIENTRYP PFNGLGETPATHMETRICSNVPROC)(GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void * paths, GLuint pathBase, GLsizei stride, GLfloat * metrics); +typedef void (APIENTRYP PFNGLGETPATHMETRICRANGENVPROC)(GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat * metrics); +typedef void (APIENTRYP PFNGLGETPATHSPACINGNVPROC)(GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void * paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat * returnedSpacing); +typedef GLboolean (APIENTRYP PFNGLISPOINTINFILLPATHNVPROC)(GLuint path, GLuint mask, GLfloat x, GLfloat y); +typedef GLboolean (APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC)(GLuint path, GLfloat x, GLfloat y); +typedef GLfloat (APIENTRYP PFNGLGETPATHLENGTHNVPROC)(GLuint path, GLsizei startSegment, GLsizei numSegments); +typedef GLboolean (APIENTRYP PFNGLPOINTALONGPATHNVPROC)(GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat * x, GLfloat * y, GLfloat * tangentX, GLfloat * tangentY); +typedef void (APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC)(GLenum matrixMode, const GLfloat * m); +typedef void (APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC)(GLenum matrixMode, const GLfloat * m); +typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC)(GLenum matrixMode, const GLfloat * m); +typedef void (APIENTRYP PFNGLMATRIXMULT3X2FNVPROC)(GLenum matrixMode, const GLfloat * m); +typedef void (APIENTRYP PFNGLMATRIXMULT3X3FNVPROC)(GLenum matrixMode, const GLfloat * m); +typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC)(GLenum matrixMode, const GLfloat * m); +typedef void (APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC)(GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); +typedef void (APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC)(GLuint path, GLint reference, GLuint mask, GLenum coverMode); +typedef void (APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC)(GLsizei numPaths, GLenum pathNameType, const void * paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat * transformValues); +typedef void (APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC)(GLsizei numPaths, GLenum pathNameType, const void * paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat * transformValues); +typedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC)(GLenum fontTarget, const void * fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint baseAndCount); +typedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC)(GLuint firstPathName, GLenum fontTarget, const void * fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef GLenum (APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC)(GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void * fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC)(GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat * coeffs); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC)(GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum * props, GLsizei count, GLsizei * length, GLfloat * params); +typedef void (APIENTRYP PFNGLPATHCOLORGENNVPROC)(GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat * coeffs); +typedef void (APIENTRYP PFNGLPATHTEXGENNVPROC)(GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat * coeffs); +typedef void (APIENTRYP PFNGLPATHFOGGENNVPROC)(GLenum genMode); +typedef void (APIENTRYP PFNGLGETPATHCOLORGENIVNVPROC)(GLenum color, GLenum pname, GLint * value); +typedef void (APIENTRYP PFNGLGETPATHCOLORGENFVNVPROC)(GLenum color, GLenum pname, GLfloat * value); +typedef void (APIENTRYP PFNGLGETPATHTEXGENIVNVPROC)(GLenum texCoordSet, GLenum pname, GLint * value); +typedef void (APIENTRYP PFNGLGETPATHTEXGENFVNVPROC)(GLenum texCoordSet, GLenum pname, GLfloat * value); +#endif /* GL_NV_path_rendering */ + +#ifndef GL_NV_path_rendering_shared_edge +#define GL_NV_path_rendering_shared_edge 1 +#define GL_SHARED_EDGE_NV 0xC0 +#endif /* GL_NV_path_rendering_shared_edge */ + +#ifndef GL_NV_pixel_data_range +#define GL_NV_pixel_data_range 1 +#define GL_WRITE_PIXEL_DATA_RANGE_NV 0x8878 +#define GL_READ_PIXEL_DATA_RANGE_NV 0x8879 +#define GL_WRITE_PIXEL_DATA_RANGE_LENGTH_NV 0x887A +#define GL_READ_PIXEL_DATA_RANGE_LENGTH_NV 0x887B +#define GL_WRITE_PIXEL_DATA_RANGE_POINTER_NV 0x887C +#define GL_READ_PIXEL_DATA_RANGE_POINTER_NV 0x887D +typedef void (APIENTRYP PFNGLPIXELDATARANGENVPROC)(GLenum target, GLsizei length, const void * pointer); +typedef void (APIENTRYP PFNGLFLUSHPIXELDATARANGENVPROC)(GLenum target); +#endif /* GL_NV_pixel_data_range */ + +#ifndef GL_NV_point_sprite +#define GL_NV_point_sprite 1 +#define GL_POINT_SPRITE_NV 0x8861 +#define GL_COORD_REPLACE_NV 0x8862 +#define GL_POINT_SPRITE_R_MODE_NV 0x8863 +typedef void (APIENTRYP PFNGLPOINTPARAMETERINVPROC)(GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVNVPROC)(GLenum pname, const GLint * params); +#endif /* GL_NV_point_sprite */ + +#ifndef GL_NV_present_video +#define GL_NV_present_video 1 +#define GL_FRAME_NV 0x8E26 +#define GL_FIELDS_NV 0x8E27 +#define GL_CURRENT_TIME_NV 0x8E28 +#define GL_NUM_FILL_STREAMS_NV 0x8E29 +#define GL_PRESENT_TIME_NV 0x8E2A +#define GL_PRESENT_DURATION_NV 0x8E2B +typedef void (APIENTRYP PFNGLPRESENTFRAMEKEYEDNVPROC)(GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLuint key0, GLenum target1, GLuint fill1, GLuint key1); +typedef void (APIENTRYP PFNGLPRESENTFRAMEDUALFILLNVPROC)(GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLenum target1, GLuint fill1, GLenum target2, GLuint fill2, GLenum target3, GLuint fill3); +typedef void (APIENTRYP PFNGLGETVIDEOIVNVPROC)(GLuint video_slot, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETVIDEOUIVNVPROC)(GLuint video_slot, GLenum pname, GLuint * params); +typedef void (APIENTRYP PFNGLGETVIDEOI64VNVPROC)(GLuint video_slot, GLenum pname, GLint64EXT * params); +typedef void (APIENTRYP PFNGLGETVIDEOUI64VNVPROC)(GLuint video_slot, GLenum pname, GLuint64EXT * params); +#endif /* GL_NV_present_video */ + +#ifndef GL_NV_primitive_restart +#define GL_NV_primitive_restart 1 +#define GL_PRIMITIVE_RESTART_NV 0x8558 +#define GL_PRIMITIVE_RESTART_INDEX_NV 0x8559 +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTNVPROC)(void); +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXNVPROC)(GLuint index); +#endif /* GL_NV_primitive_restart */ + +#ifndef GL_NV_query_resource +#define GL_NV_query_resource 1 +#define GL_QUERY_RESOURCE_TYPE_VIDMEM_ALLOC_NV 0x9540 +#define GL_QUERY_RESOURCE_MEMTYPE_VIDMEM_NV 0x9542 +#define GL_QUERY_RESOURCE_SYS_RESERVED_NV 0x9544 +#define GL_QUERY_RESOURCE_TEXTURE_NV 0x9545 +#define GL_QUERY_RESOURCE_RENDERBUFFER_NV 0x9546 +#define GL_QUERY_RESOURCE_BUFFEROBJECT_NV 0x9547 +typedef GLint (APIENTRYP PFNGLQUERYRESOURCENVPROC)(GLenum queryType, GLint tagId, GLuint count, GLint * buffer); +#endif /* GL_NV_query_resource */ + +#ifndef GL_NV_query_resource_tag +#define GL_NV_query_resource_tag 1 +typedef void (APIENTRYP PFNGLGENQUERYRESOURCETAGNVPROC)(GLsizei n, GLint * tagIds); +typedef void (APIENTRYP PFNGLDELETEQUERYRESOURCETAGNVPROC)(GLsizei n, const GLint * tagIds); +typedef void (APIENTRYP PFNGLQUERYRESOURCETAGNVPROC)(GLint tagId, const GLchar * tagString); +#endif /* GL_NV_query_resource_tag */ + +#ifndef GL_NV_register_combiners +#define GL_NV_register_combiners 1 +#define GL_REGISTER_COMBINERS_NV 0x8522 +#define GL_VARIABLE_A_NV 0x8523 +#define GL_VARIABLE_B_NV 0x8524 +#define GL_VARIABLE_C_NV 0x8525 +#define GL_VARIABLE_D_NV 0x8526 +#define GL_VARIABLE_E_NV 0x8527 +#define GL_VARIABLE_F_NV 0x8528 +#define GL_VARIABLE_G_NV 0x8529 +#define GL_CONSTANT_COLOR0_NV 0x852A +#define GL_CONSTANT_COLOR1_NV 0x852B +#define GL_PRIMARY_COLOR_NV 0x852C +#define GL_SECONDARY_COLOR_NV 0x852D +#define GL_SPARE0_NV 0x852E +#define GL_SPARE1_NV 0x852F +#define GL_DISCARD_NV 0x8530 +#define GL_E_TIMES_F_NV 0x8531 +#define GL_SPARE0_PLUS_SECONDARY_COLOR_NV 0x8532 +#define GL_UNSIGNED_IDENTITY_NV 0x8536 +#define GL_UNSIGNED_INVERT_NV 0x8537 +#define GL_EXPAND_NORMAL_NV 0x8538 +#define GL_EXPAND_NEGATE_NV 0x8539 +#define GL_HALF_BIAS_NORMAL_NV 0x853A +#define GL_HALF_BIAS_NEGATE_NV 0x853B +#define GL_SIGNED_IDENTITY_NV 0x853C +#define GL_SIGNED_NEGATE_NV 0x853D +#define GL_SCALE_BY_TWO_NV 0x853E +#define GL_SCALE_BY_FOUR_NV 0x853F +#define GL_SCALE_BY_ONE_HALF_NV 0x8540 +#define GL_BIAS_BY_NEGATIVE_ONE_HALF_NV 0x8541 +#define GL_COMBINER_INPUT_NV 0x8542 +#define GL_COMBINER_MAPPING_NV 0x8543 +#define GL_COMBINER_COMPONENT_USAGE_NV 0x8544 +#define GL_COMBINER_AB_DOT_PRODUCT_NV 0x8545 +#define GL_COMBINER_CD_DOT_PRODUCT_NV 0x8546 +#define GL_COMBINER_MUX_SUM_NV 0x8547 +#define GL_COMBINER_SCALE_NV 0x8548 +#define GL_COMBINER_BIAS_NV 0x8549 +#define GL_COMBINER_AB_OUTPUT_NV 0x854A +#define GL_COMBINER_CD_OUTPUT_NV 0x854B +#define GL_COMBINER_SUM_OUTPUT_NV 0x854C +#define GL_MAX_GENERAL_COMBINERS_NV 0x854D +#define GL_NUM_GENERAL_COMBINERS_NV 0x854E +#define GL_COLOR_SUM_CLAMP_NV 0x854F +#define GL_COMBINER0_NV 0x8550 +#define GL_COMBINER1_NV 0x8551 +#define GL_COMBINER2_NV 0x8552 +#define GL_COMBINER3_NV 0x8553 +#define GL_COMBINER4_NV 0x8554 +#define GL_COMBINER5_NV 0x8555 +#define GL_COMBINER6_NV 0x8556 +#define GL_COMBINER7_NV 0x8557 +#define GL_TEXTURE0_ARB 0x84C0 +#define GL_TEXTURE1_ARB 0x84C1 +#define GL_ZERO 0 +#define GL_NONE 0 +#define GL_FOG 0x0B60 +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFVNVPROC)(GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFNVPROC)(GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERIVNVPROC)(GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERINVPROC)(GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLCOMBINERINPUTNVPROC)(GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +typedef void (APIENTRYP PFNGLCOMBINEROUTPUTNVPROC)(GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum); +typedef void (APIENTRYP PFNGLFINALCOMBINERINPUTNVPROC)(GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC)(GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC)(GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC)(GLenum stage, GLenum portion, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC)(GLenum stage, GLenum portion, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC)(GLenum variable, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC)(GLenum variable, GLenum pname, GLint * params); +#endif /* GL_NV_register_combiners */ + +#ifndef GL_NV_register_combiners2 +#define GL_NV_register_combiners2 1 +#define GL_PER_STAGE_CONSTANTS_NV 0x8535 +typedef void (APIENTRYP PFNGLCOMBINERSTAGEPARAMETERFVNVPROC)(GLenum stage, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLGETCOMBINERSTAGEPARAMETERFVNVPROC)(GLenum stage, GLenum pname, GLfloat * params); +#endif /* GL_NV_register_combiners2 */ + +#ifndef GL_NV_representative_fragment_test +#define GL_NV_representative_fragment_test 1 +#define GL_REPRESENTATIVE_FRAGMENT_TEST_NV 0x937F +#endif /* GL_NV_representative_fragment_test */ + +#ifndef GL_NV_robustness_video_memory_purge +#define GL_NV_robustness_video_memory_purge 1 +#define GL_PURGED_CONTEXT_RESET_NV 0x92BB +#endif /* GL_NV_robustness_video_memory_purge */ + +#ifndef GL_NV_sample_locations +#define GL_NV_sample_locations 1 +#define GL_SAMPLE_LOCATION_SUBPIXEL_BITS_NV 0x933D +#define GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_NV 0x933E +#define GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_NV 0x933F +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_NV 0x9340 +#define GL_SAMPLE_LOCATION_NV 0x8E50 +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_NV 0x9341 +#define GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_NV 0x9342 +#define GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_NV 0x9343 +typedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLELOCATIONSFVNVPROC)(GLenum target, GLuint start, GLsizei count, const GLfloat * v); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVNVPROC)(GLuint framebuffer, GLuint start, GLsizei count, const GLfloat * v); +typedef void (APIENTRYP PFNGLRESOLVEDEPTHVALUESNVPROC)(void); +#endif /* GL_NV_sample_locations */ + +#ifndef GL_NV_sample_mask_override_coverage +#define GL_NV_sample_mask_override_coverage 1 +#endif /* GL_NV_sample_mask_override_coverage */ + +#ifndef GL_NV_scissor_exclusive +#define GL_NV_scissor_exclusive 1 +#define GL_SCISSOR_TEST_EXCLUSIVE_NV 0x9555 +#define GL_SCISSOR_BOX_EXCLUSIVE_NV 0x9556 +typedef void (APIENTRYP PFNGLSCISSOREXCLUSIVENVPROC)(GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLSCISSOREXCLUSIVEARRAYVNVPROC)(GLuint first, GLsizei count, const GLint * v); +#endif /* GL_NV_scissor_exclusive */ + +#ifndef GL_NV_shader_atomic_counters +#define GL_NV_shader_atomic_counters 1 +#endif /* GL_NV_shader_atomic_counters */ + +#ifndef GL_NV_shader_atomic_float +#define GL_NV_shader_atomic_float 1 +#endif /* GL_NV_shader_atomic_float */ + +#ifndef GL_NV_shader_atomic_float64 +#define GL_NV_shader_atomic_float64 1 +#endif /* GL_NV_shader_atomic_float64 */ + +#ifndef GL_NV_shader_atomic_fp16_vector +#define GL_NV_shader_atomic_fp16_vector 1 +#endif /* GL_NV_shader_atomic_fp16_vector */ + +#ifndef GL_NV_shader_atomic_int64 +#define GL_NV_shader_atomic_int64 1 +#endif /* GL_NV_shader_atomic_int64 */ + +#ifndef GL_NV_shader_buffer_load +#define GL_NV_shader_buffer_load 1 +#define GL_BUFFER_GPU_ADDRESS_NV 0x8F1D +#define GL_GPU_ADDRESS_NV 0x8F34 +#define GL_MAX_SHADER_BUFFER_ADDRESS_NV 0x8F35 +typedef void (APIENTRYP PFNGLMAKEBUFFERRESIDENTNVPROC)(GLenum target, GLenum access); +typedef void (APIENTRYP PFNGLMAKEBUFFERNONRESIDENTNVPROC)(GLenum target); +typedef GLboolean (APIENTRYP PFNGLISBUFFERRESIDENTNVPROC)(GLenum target); +typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERRESIDENTNVPROC)(GLuint buffer, GLenum access); +typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC)(GLuint buffer); +typedef GLboolean (APIENTRYP PFNGLISNAMEDBUFFERRESIDENTNVPROC)(GLuint buffer); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERUI64VNVPROC)(GLenum target, GLenum pname, GLuint64EXT * params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC)(GLuint buffer, GLenum pname, GLuint64EXT * params); +typedef void (APIENTRYP PFNGLGETINTEGERUI64VNVPROC)(GLenum value, GLuint64EXT * result); +typedef void (APIENTRYP PFNGLUNIFORMUI64NVPROC)(GLint location, GLuint64EXT value); +typedef void (APIENTRYP PFNGLUNIFORMUI64VNVPROC)(GLint location, GLsizei count, const GLuint64EXT * value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64NVPROC)(GLuint program, GLint location, GLuint64EXT value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64VNVPROC)(GLuint program, GLint location, GLsizei count, const GLuint64EXT * value); +#endif /* GL_NV_shader_buffer_load */ + +#ifndef GL_NV_shader_buffer_store +#define GL_NV_shader_buffer_store 1 +#define GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV 0x00000010 +#define GL_READ_WRITE 0x88BA +#define GL_WRITE_ONLY 0x88B9 +#endif /* GL_NV_shader_buffer_store */ + +#ifndef GL_NV_shader_storage_buffer_object +#define GL_NV_shader_storage_buffer_object 1 +#endif /* GL_NV_shader_storage_buffer_object */ + +#ifndef GL_NV_shader_subgroup_partitioned +#define GL_NV_shader_subgroup_partitioned 1 +#define GL_SUBGROUP_FEATURE_PARTITIONED_BIT_NV 0x00000100 +#endif /* GL_NV_shader_subgroup_partitioned */ + +#ifndef GL_NV_shader_texture_footprint +#define GL_NV_shader_texture_footprint 1 +#endif /* GL_NV_shader_texture_footprint */ + +#ifndef GL_NV_shader_thread_group +#define GL_NV_shader_thread_group 1 +#define GL_WARP_SIZE_NV 0x9339 +#define GL_WARPS_PER_SM_NV 0x933A +#define GL_SM_COUNT_NV 0x933B +#endif /* GL_NV_shader_thread_group */ + +#ifndef GL_NV_shader_thread_shuffle +#define GL_NV_shader_thread_shuffle 1 +#endif /* GL_NV_shader_thread_shuffle */ + +#ifndef GL_NV_shading_rate_image +#define GL_NV_shading_rate_image 1 +#define GL_SHADING_RATE_IMAGE_NV 0x9563 +#define GL_SHADING_RATE_NO_INVOCATIONS_NV 0x9564 +#define GL_SHADING_RATE_1_INVOCATION_PER_PIXEL_NV 0x9565 +#define GL_SHADING_RATE_1_INVOCATION_PER_1X2_PIXELS_NV 0x9566 +#define GL_SHADING_RATE_1_INVOCATION_PER_2X1_PIXELS_NV 0x9567 +#define GL_SHADING_RATE_1_INVOCATION_PER_2X2_PIXELS_NV 0x9568 +#define GL_SHADING_RATE_1_INVOCATION_PER_2X4_PIXELS_NV 0x9569 +#define GL_SHADING_RATE_1_INVOCATION_PER_4X2_PIXELS_NV 0x956A +#define GL_SHADING_RATE_1_INVOCATION_PER_4X4_PIXELS_NV 0x956B +#define GL_SHADING_RATE_2_INVOCATIONS_PER_PIXEL_NV 0x956C +#define GL_SHADING_RATE_4_INVOCATIONS_PER_PIXEL_NV 0x956D +#define GL_SHADING_RATE_8_INVOCATIONS_PER_PIXEL_NV 0x956E +#define GL_SHADING_RATE_16_INVOCATIONS_PER_PIXEL_NV 0x956F +#define GL_SHADING_RATE_IMAGE_BINDING_NV 0x955B +#define GL_SHADING_RATE_IMAGE_TEXEL_WIDTH_NV 0x955C +#define GL_SHADING_RATE_IMAGE_TEXEL_HEIGHT_NV 0x955D +#define GL_SHADING_RATE_IMAGE_PALETTE_SIZE_NV 0x955E +#define GL_MAX_COARSE_FRAGMENT_SAMPLES_NV 0x955F +#define GL_SHADING_RATE_SAMPLE_ORDER_DEFAULT_NV 0x95AE +#define GL_SHADING_RATE_SAMPLE_ORDER_PIXEL_MAJOR_NV 0x95AF +#define GL_SHADING_RATE_SAMPLE_ORDER_SAMPLE_MAJOR_NV 0x95B0 +typedef void (APIENTRYP PFNGLBINDSHADINGRATEIMAGENVPROC)(GLuint texture); +typedef void (APIENTRYP PFNGLGETSHADINGRATEIMAGEPALETTENVPROC)(GLuint viewport, GLuint entry, GLenum * rate); +typedef void (APIENTRYP PFNGLGETSHADINGRATESAMPLELOCATIONIVNVPROC)(GLenum rate, GLuint samples, GLuint index, GLint * location); +typedef void (APIENTRYP PFNGLSHADINGRATEIMAGEBARRIERNVPROC)(GLboolean synchronize); +typedef void (APIENTRYP PFNGLSHADINGRATEIMAGEPALETTENVPROC)(GLuint viewport, GLuint first, GLsizei count, const GLenum * rates); +typedef void (APIENTRYP PFNGLSHADINGRATESAMPLEORDERNVPROC)(GLenum order); +typedef void (APIENTRYP PFNGLSHADINGRATESAMPLEORDERCUSTOMNVPROC)(GLenum rate, GLuint samples, const GLint * locations); +#endif /* GL_NV_shading_rate_image */ + +#ifndef GL_NV_stereo_view_rendering +#define GL_NV_stereo_view_rendering 1 +#endif /* GL_NV_stereo_view_rendering */ + +#ifndef GL_NV_tessellation_program5 +#define GL_NV_tessellation_program5 1 +#define GL_MAX_PROGRAM_PATCH_ATTRIBS_NV 0x86D8 +#define GL_TESS_CONTROL_PROGRAM_NV 0x891E +#define GL_TESS_EVALUATION_PROGRAM_NV 0x891F +#define GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV 0x8C74 +#define GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV 0x8C75 +#endif /* GL_NV_tessellation_program5 */ + +#ifndef GL_NV_texgen_emboss +#define GL_NV_texgen_emboss 1 +#define GL_EMBOSS_LIGHT_NV 0x855D +#define GL_EMBOSS_CONSTANT_NV 0x855E +#define GL_EMBOSS_MAP_NV 0x855F +#endif /* GL_NV_texgen_emboss */ + +#ifndef GL_NV_texgen_reflection +#define GL_NV_texgen_reflection 1 +#define GL_NORMAL_MAP_NV 0x8511 +#define GL_REFLECTION_MAP_NV 0x8512 +#endif /* GL_NV_texgen_reflection */ + +#ifndef GL_NV_texture_barrier +#define GL_NV_texture_barrier 1 +typedef void (APIENTRYP PFNGLTEXTUREBARRIERNVPROC)(void); +#endif /* GL_NV_texture_barrier */ + +#ifndef GL_NV_texture_compression_vtc +#define GL_NV_texture_compression_vtc 1 +#endif /* GL_NV_texture_compression_vtc */ + +#ifndef GL_NV_texture_env_combine4 +#define GL_NV_texture_env_combine4 1 +#define GL_COMBINE4_NV 0x8503 +#define GL_SOURCE3_RGB_NV 0x8583 +#define GL_SOURCE3_ALPHA_NV 0x858B +#define GL_OPERAND3_RGB_NV 0x8593 +#define GL_OPERAND3_ALPHA_NV 0x859B +#endif /* GL_NV_texture_env_combine4 */ + +#ifndef GL_NV_texture_expand_normal +#define GL_NV_texture_expand_normal 1 +#define GL_TEXTURE_UNSIGNED_REMAP_MODE_NV 0x888F +#endif /* GL_NV_texture_expand_normal */ + +#ifndef GL_NV_texture_multisample +#define GL_NV_texture_multisample 1 +#define GL_TEXTURE_COVERAGE_SAMPLES_NV 0x9045 +#define GL_TEXTURE_COLOR_SAMPLES_NV 0x9046 +typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLECOVERAGENVPROC)(GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLECOVERAGENVPROC)(GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DMULTISAMPLENVPROC)(GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DMULTISAMPLENVPROC)(GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DMULTISAMPLECOVERAGENVPROC)(GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DMULTISAMPLECOVERAGENVPROC)(GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +#endif /* GL_NV_texture_multisample */ + +#ifndef GL_NV_texture_rectangle +#define GL_NV_texture_rectangle 1 +#define GL_TEXTURE_RECTANGLE_NV 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_NV 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_NV 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 0x84F8 +#endif /* GL_NV_texture_rectangle */ + +#ifndef GL_NV_texture_rectangle_compressed +#define GL_NV_texture_rectangle_compressed 1 +#endif /* GL_NV_texture_rectangle_compressed */ + +#ifndef GL_NV_texture_shader +#define GL_NV_texture_shader 1 +#define GL_OFFSET_TEXTURE_RECTANGLE_NV 0x864C +#define GL_OFFSET_TEXTURE_RECTANGLE_SCALE_NV 0x864D +#define GL_DOT_PRODUCT_TEXTURE_RECTANGLE_NV 0x864E +#define GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV 0x86D9 +#define GL_UNSIGNED_INT_S8_S8_8_8_NV 0x86DA +#define GL_UNSIGNED_INT_8_8_S8_S8_REV_NV 0x86DB +#define GL_DSDT_MAG_INTENSITY_NV 0x86DC +#define GL_SHADER_CONSISTENT_NV 0x86DD +#define GL_TEXTURE_SHADER_NV 0x86DE +#define GL_SHADER_OPERATION_NV 0x86DF +#define GL_CULL_MODES_NV 0x86E0 +#define GL_OFFSET_TEXTURE_MATRIX_NV 0x86E1 +#define GL_OFFSET_TEXTURE_SCALE_NV 0x86E2 +#define GL_OFFSET_TEXTURE_BIAS_NV 0x86E3 +#define GL_OFFSET_TEXTURE_2D_MATRIX_NV 0x86E1 +#define GL_OFFSET_TEXTURE_2D_SCALE_NV 0x86E2 +#define GL_OFFSET_TEXTURE_2D_BIAS_NV 0x86E3 +#define GL_PREVIOUS_TEXTURE_INPUT_NV 0x86E4 +#define GL_CONST_EYE_NV 0x86E5 +#define GL_PASS_THROUGH_NV 0x86E6 +#define GL_CULL_FRAGMENT_NV 0x86E7 +#define GL_OFFSET_TEXTURE_2D_NV 0x86E8 +#define GL_DEPENDENT_AR_TEXTURE_2D_NV 0x86E9 +#define GL_DEPENDENT_GB_TEXTURE_2D_NV 0x86EA +#define GL_DOT_PRODUCT_NV 0x86EC +#define GL_DOT_PRODUCT_DEPTH_REPLACE_NV 0x86ED +#define GL_DOT_PRODUCT_TEXTURE_2D_NV 0x86EE +#define GL_DOT_PRODUCT_TEXTURE_CUBE_MAP_NV 0x86F0 +#define GL_DOT_PRODUCT_DIFFUSE_CUBE_MAP_NV 0x86F1 +#define GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV 0x86F2 +#define GL_DOT_PRODUCT_CONST_EYE_REFLECT_CUBE_MAP_NV 0x86F3 +#define GL_HILO_NV 0x86F4 +#define GL_DSDT_NV 0x86F5 +#define GL_DSDT_MAG_NV 0x86F6 +#define GL_DSDT_MAG_VIB_NV 0x86F7 +#define GL_HILO16_NV 0x86F8 +#define GL_SIGNED_HILO_NV 0x86F9 +#define GL_SIGNED_HILO16_NV 0x86FA +#define GL_SIGNED_RGBA_NV 0x86FB +#define GL_SIGNED_RGBA8_NV 0x86FC +#define GL_SIGNED_RGB_NV 0x86FE +#define GL_SIGNED_RGB8_NV 0x86FF +#define GL_SIGNED_LUMINANCE_NV 0x8701 +#define GL_SIGNED_LUMINANCE8_NV 0x8702 +#define GL_SIGNED_LUMINANCE_ALPHA_NV 0x8703 +#define GL_SIGNED_LUMINANCE8_ALPHA8_NV 0x8704 +#define GL_SIGNED_ALPHA_NV 0x8705 +#define GL_SIGNED_ALPHA8_NV 0x8706 +#define GL_SIGNED_INTENSITY_NV 0x8707 +#define GL_SIGNED_INTENSITY8_NV 0x8708 +#define GL_DSDT8_NV 0x8709 +#define GL_DSDT8_MAG8_NV 0x870A +#define GL_DSDT8_MAG8_INTENSITY8_NV 0x870B +#define GL_SIGNED_RGB_UNSIGNED_ALPHA_NV 0x870C +#define GL_SIGNED_RGB8_UNSIGNED_ALPHA8_NV 0x870D +#define GL_HI_SCALE_NV 0x870E +#define GL_LO_SCALE_NV 0x870F +#define GL_DS_SCALE_NV 0x8710 +#define GL_DT_SCALE_NV 0x8711 +#define GL_MAGNITUDE_SCALE_NV 0x8712 +#define GL_VIBRANCE_SCALE_NV 0x8713 +#define GL_HI_BIAS_NV 0x8714 +#define GL_LO_BIAS_NV 0x8715 +#define GL_DS_BIAS_NV 0x8716 +#define GL_DT_BIAS_NV 0x8717 +#define GL_MAGNITUDE_BIAS_NV 0x8718 +#define GL_VIBRANCE_BIAS_NV 0x8719 +#define GL_TEXTURE_BORDER_VALUES_NV 0x871A +#define GL_TEXTURE_HI_SIZE_NV 0x871B +#define GL_TEXTURE_LO_SIZE_NV 0x871C +#define GL_TEXTURE_DS_SIZE_NV 0x871D +#define GL_TEXTURE_DT_SIZE_NV 0x871E +#define GL_TEXTURE_MAG_SIZE_NV 0x871F +#endif /* GL_NV_texture_shader */ + +#ifndef GL_NV_texture_shader2 +#define GL_NV_texture_shader2 1 +#define GL_DOT_PRODUCT_TEXTURE_3D_NV 0x86EF +#endif /* GL_NV_texture_shader2 */ + +#ifndef GL_NV_texture_shader3 +#define GL_NV_texture_shader3 1 +#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_NV 0x8850 +#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_SCALE_NV 0x8851 +#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8852 +#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_SCALE_NV 0x8853 +#define GL_OFFSET_HILO_TEXTURE_2D_NV 0x8854 +#define GL_OFFSET_HILO_TEXTURE_RECTANGLE_NV 0x8855 +#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_2D_NV 0x8856 +#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8857 +#define GL_DEPENDENT_HILO_TEXTURE_2D_NV 0x8858 +#define GL_DEPENDENT_RGB_TEXTURE_3D_NV 0x8859 +#define GL_DEPENDENT_RGB_TEXTURE_CUBE_MAP_NV 0x885A +#define GL_DOT_PRODUCT_PASS_THROUGH_NV 0x885B +#define GL_DOT_PRODUCT_TEXTURE_1D_NV 0x885C +#define GL_DOT_PRODUCT_AFFINE_DEPTH_REPLACE_NV 0x885D +#define GL_HILO8_NV 0x885E +#define GL_SIGNED_HILO8_NV 0x885F +#define GL_FORCE_BLUE_TO_ONE_NV 0x8860 +#endif /* GL_NV_texture_shader3 */ + +#ifndef GL_NV_transform_feedback +#define GL_NV_transform_feedback 1 +#define GL_BACK_PRIMARY_COLOR_NV 0x8C77 +#define GL_BACK_SECONDARY_COLOR_NV 0x8C78 +#define GL_TEXTURE_COORD_NV 0x8C79 +#define GL_CLIP_DISTANCE_NV 0x8C7A +#define GL_VERTEX_ID_NV 0x8C7B +#define GL_PRIMITIVE_ID_NV 0x8C7C +#define GL_GENERIC_ATTRIB_NV 0x8C7D +#define GL_TRANSFORM_FEEDBACK_ATTRIBS_NV 0x8C7E +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_NV 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_NV 0x8C80 +#define GL_ACTIVE_VARYINGS_NV 0x8C81 +#define GL_ACTIVE_VARYING_MAX_LENGTH_NV 0x8C82 +#define GL_TRANSFORM_FEEDBACK_VARYINGS_NV 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START_NV 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_NV 0x8C85 +#define GL_TRANSFORM_FEEDBACK_RECORD_NV 0x8C86 +#define GL_PRIMITIVES_GENERATED_NV 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV 0x8C88 +#define GL_RASTERIZER_DISCARD_NV 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_NV 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_NV 0x8C8B +#define GL_INTERLEAVED_ATTRIBS_NV 0x8C8C +#define GL_SEPARATE_ATTRIBS_NV 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER_NV 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_NV 0x8C8F +#define GL_LAYER_NV 0x8DAA +#define GL_NEXT_BUFFER_NV -2 +#define GL_SKIP_COMPONENTS4_NV -3 +#define GL_SKIP_COMPONENTS3_NV -4 +#define GL_SKIP_COMPONENTS2_NV -5 +#define GL_SKIP_COMPONENTS1_NV -6 +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKNVPROC)(GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKNVPROC)(void); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKATTRIBSNVPROC)(GLsizei count, const GLint * attribs, GLenum bufferMode); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGENVPROC)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETNVPROC)(GLenum target, GLuint index, GLuint buffer, GLintptr offset); +typedef void (APIENTRYP PFNGLBINDBUFFERBASENVPROC)(GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSNVPROC)(GLuint program, GLsizei count, const GLint * locations, GLenum bufferMode); +typedef void (APIENTRYP PFNGLACTIVEVARYINGNVPROC)(GLuint program, const GLchar * name); +typedef GLint (APIENTRYP PFNGLGETVARYINGLOCATIONNVPROC)(GLuint program, const GLchar * name); +typedef void (APIENTRYP PFNGLGETACTIVEVARYINGNVPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLsizei * size, GLenum * type, GLchar * name); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGNVPROC)(GLuint program, GLuint index, GLint * location); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKSTREAMATTRIBSNVPROC)(GLsizei count, const GLint * attribs, GLsizei nbuffers, const GLint * bufstreams, GLenum bufferMode); +#endif /* GL_NV_transform_feedback */ + +#ifndef GL_NV_transform_feedback2 +#define GL_NV_transform_feedback2 1 +#define GL_TRANSFORM_FEEDBACK_NV 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED_NV 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE_NV 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING_NV 0x8E25 +typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKNVPROC)(GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSNVPROC)(GLsizei n, const GLuint * ids); +typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSNVPROC)(GLsizei n, GLuint * ids); +typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKNVPROC)(GLuint id); +typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKNVPROC)(void); +typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKNVPROC)(void); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKNVPROC)(GLenum mode, GLuint id); +#endif /* GL_NV_transform_feedback2 */ + +#ifndef GL_NV_uniform_buffer_unified_memory +#define GL_NV_uniform_buffer_unified_memory 1 +#define GL_UNIFORM_BUFFER_UNIFIED_NV 0x936E +#define GL_UNIFORM_BUFFER_ADDRESS_NV 0x936F +#define GL_UNIFORM_BUFFER_LENGTH_NV 0x9370 +#endif /* GL_NV_uniform_buffer_unified_memory */ + +#ifndef GL_NV_vdpau_interop +#define GL_NV_vdpau_interop 1 +typedef GLintptr GLvdpauSurfaceNV; +#define GL_SURFACE_STATE_NV 0x86EB +#define GL_SURFACE_REGISTERED_NV 0x86FD +#define GL_SURFACE_MAPPED_NV 0x8700 +#define GL_WRITE_DISCARD_NV 0x88BE +typedef void (APIENTRYP PFNGLVDPAUINITNVPROC)(const void * vdpDevice, const void * getProcAddress); +typedef void (APIENTRYP PFNGLVDPAUFININVPROC)(void); +typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTERVIDEOSURFACENVPROC)(const void * vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint * textureNames); +typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC)(const void * vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint * textureNames); +typedef GLboolean (APIENTRYP PFNGLVDPAUISSURFACENVPROC)(GLvdpauSurfaceNV surface); +typedef void (APIENTRYP PFNGLVDPAUUNREGISTERSURFACENVPROC)(GLvdpauSurfaceNV surface); +typedef void (APIENTRYP PFNGLVDPAUGETSURFACEIVNVPROC)(GLvdpauSurfaceNV surface, GLenum pname, GLsizei count, GLsizei * length, GLint * values); +typedef void (APIENTRYP PFNGLVDPAUSURFACEACCESSNVPROC)(GLvdpauSurfaceNV surface, GLenum access); +typedef void (APIENTRYP PFNGLVDPAUMAPSURFACESNVPROC)(GLsizei numSurfaces, const GLvdpauSurfaceNV * surfaces); +typedef void (APIENTRYP PFNGLVDPAUUNMAPSURFACESNVPROC)(GLsizei numSurface, const GLvdpauSurfaceNV * surfaces); +#endif /* GL_NV_vdpau_interop */ + +#ifndef GL_NV_vdpau_interop2 +#define GL_NV_vdpau_interop2 1 +typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTERVIDEOSURFACEWITHPICTURESTRUCTURENVPROC)(const void * vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint * textureNames, GLboolean isFrameStructure); +#endif /* GL_NV_vdpau_interop2 */ + +#ifndef GL_NV_vertex_array_range +#define GL_NV_vertex_array_range 1 +#define GL_VERTEX_ARRAY_RANGE_NV 0x851D +#define GL_VERTEX_ARRAY_RANGE_LENGTH_NV 0x851E +#define GL_VERTEX_ARRAY_RANGE_VALID_NV 0x851F +#define GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV 0x8520 +#define GL_VERTEX_ARRAY_RANGE_POINTER_NV 0x8521 +typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGENVPROC)(void); +typedef void (APIENTRYP PFNGLVERTEXARRAYRANGENVPROC)(GLsizei length, const void * pointer); +#endif /* GL_NV_vertex_array_range */ + +#ifndef GL_NV_vertex_array_range2 +#define GL_NV_vertex_array_range2 1 +#define GL_VERTEX_ARRAY_RANGE_WITHOUT_FLUSH_NV 0x8533 +#endif /* GL_NV_vertex_array_range2 */ + +#ifndef GL_NV_vertex_attrib_integer_64bit +#define GL_NV_vertex_attrib_integer_64bit 1 +#define GL_INT64_NV 0x140E +#define GL_UNSIGNED_INT64_NV 0x140F +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64NVPROC)(GLuint index, GLint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64NVPROC)(GLuint index, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64NVPROC)(GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64NVPROC)(GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64VNVPROC)(GLuint index, const GLint64EXT * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64VNVPROC)(GLuint index, const GLint64EXT * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64VNVPROC)(GLuint index, const GLint64EXT * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64VNVPROC)(GLuint index, const GLint64EXT * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64NVPROC)(GLuint index, GLuint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64NVPROC)(GLuint index, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64NVPROC)(GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64NVPROC)(GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VNVPROC)(GLuint index, const GLuint64EXT * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64VNVPROC)(GLuint index, const GLuint64EXT * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64VNVPROC)(GLuint index, const GLuint64EXT * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64VNVPROC)(GLuint index, const GLuint64EXT * v); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLI64VNVPROC)(GLuint index, GLenum pname, GLint64EXT * params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VNVPROC)(GLuint index, GLenum pname, GLuint64EXT * params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATNVPROC)(GLuint index, GLint size, GLenum type, GLsizei stride); +#endif /* GL_NV_vertex_attrib_integer_64bit */ + +#ifndef GL_NV_vertex_buffer_unified_memory +#define GL_NV_vertex_buffer_unified_memory 1 +#define GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV 0x8F1E +#define GL_ELEMENT_ARRAY_UNIFIED_NV 0x8F1F +#define GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV 0x8F20 +#define GL_VERTEX_ARRAY_ADDRESS_NV 0x8F21 +#define GL_NORMAL_ARRAY_ADDRESS_NV 0x8F22 +#define GL_COLOR_ARRAY_ADDRESS_NV 0x8F23 +#define GL_INDEX_ARRAY_ADDRESS_NV 0x8F24 +#define GL_TEXTURE_COORD_ARRAY_ADDRESS_NV 0x8F25 +#define GL_EDGE_FLAG_ARRAY_ADDRESS_NV 0x8F26 +#define GL_SECONDARY_COLOR_ARRAY_ADDRESS_NV 0x8F27 +#define GL_FOG_COORD_ARRAY_ADDRESS_NV 0x8F28 +#define GL_ELEMENT_ARRAY_ADDRESS_NV 0x8F29 +#define GL_VERTEX_ATTRIB_ARRAY_LENGTH_NV 0x8F2A +#define GL_VERTEX_ARRAY_LENGTH_NV 0x8F2B +#define GL_NORMAL_ARRAY_LENGTH_NV 0x8F2C +#define GL_COLOR_ARRAY_LENGTH_NV 0x8F2D +#define GL_INDEX_ARRAY_LENGTH_NV 0x8F2E +#define GL_TEXTURE_COORD_ARRAY_LENGTH_NV 0x8F2F +#define GL_EDGE_FLAG_ARRAY_LENGTH_NV 0x8F30 +#define GL_SECONDARY_COLOR_ARRAY_LENGTH_NV 0x8F31 +#define GL_FOG_COORD_ARRAY_LENGTH_NV 0x8F32 +#define GL_ELEMENT_ARRAY_LENGTH_NV 0x8F33 +#define GL_DRAW_INDIRECT_UNIFIED_NV 0x8F40 +#define GL_DRAW_INDIRECT_ADDRESS_NV 0x8F41 +#define GL_DRAW_INDIRECT_LENGTH_NV 0x8F42 +typedef void (APIENTRYP PFNGLBUFFERADDRESSRANGENVPROC)(GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); +typedef void (APIENTRYP PFNGLVERTEXFORMATNVPROC)(GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLNORMALFORMATNVPROC)(GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLCOLORFORMATNVPROC)(GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLINDEXFORMATNVPROC)(GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLTEXCOORDFORMATNVPROC)(GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLEDGEFLAGFORMATNVPROC)(GLsizei stride); +typedef void (APIENTRYP PFNGLSECONDARYCOLORFORMATNVPROC)(GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLFOGCOORDFORMATNVPROC)(GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATNVPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATNVPROC)(GLuint index, GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC)(GLenum value, GLuint index, GLuint64EXT * result); +#endif /* GL_NV_vertex_buffer_unified_memory */ + +#ifndef GL_NV_vertex_program +#define GL_NV_vertex_program 1 +#define GL_VERTEX_PROGRAM_NV 0x8620 +#define GL_VERTEX_STATE_PROGRAM_NV 0x8621 +#define GL_ATTRIB_ARRAY_SIZE_NV 0x8623 +#define GL_ATTRIB_ARRAY_STRIDE_NV 0x8624 +#define GL_ATTRIB_ARRAY_TYPE_NV 0x8625 +#define GL_CURRENT_ATTRIB_NV 0x8626 +#define GL_PROGRAM_LENGTH_NV 0x8627 +#define GL_PROGRAM_STRING_NV 0x8628 +#define GL_MODELVIEW_PROJECTION_NV 0x8629 +#define GL_IDENTITY_NV 0x862A +#define GL_INVERSE_NV 0x862B +#define GL_TRANSPOSE_NV 0x862C +#define GL_INVERSE_TRANSPOSE_NV 0x862D +#define GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV 0x862E +#define GL_MAX_TRACK_MATRICES_NV 0x862F +#define GL_MATRIX0_NV 0x8630 +#define GL_MATRIX1_NV 0x8631 +#define GL_MATRIX2_NV 0x8632 +#define GL_MATRIX3_NV 0x8633 +#define GL_MATRIX4_NV 0x8634 +#define GL_MATRIX5_NV 0x8635 +#define GL_MATRIX6_NV 0x8636 +#define GL_MATRIX7_NV 0x8637 +#define GL_CURRENT_MATRIX_STACK_DEPTH_NV 0x8640 +#define GL_CURRENT_MATRIX_NV 0x8641 +#define GL_VERTEX_PROGRAM_POINT_SIZE_NV 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_NV 0x8643 +#define GL_PROGRAM_PARAMETER_NV 0x8644 +#define GL_ATTRIB_ARRAY_POINTER_NV 0x8645 +#define GL_PROGRAM_TARGET_NV 0x8646 +#define GL_PROGRAM_RESIDENT_NV 0x8647 +#define GL_TRACK_MATRIX_NV 0x8648 +#define GL_TRACK_MATRIX_TRANSFORM_NV 0x8649 +#define GL_VERTEX_PROGRAM_BINDING_NV 0x864A +#define GL_PROGRAM_ERROR_POSITION_NV 0x864B +#define GL_VERTEX_ATTRIB_ARRAY0_NV 0x8650 +#define GL_VERTEX_ATTRIB_ARRAY1_NV 0x8651 +#define GL_VERTEX_ATTRIB_ARRAY2_NV 0x8652 +#define GL_VERTEX_ATTRIB_ARRAY3_NV 0x8653 +#define GL_VERTEX_ATTRIB_ARRAY4_NV 0x8654 +#define GL_VERTEX_ATTRIB_ARRAY5_NV 0x8655 +#define GL_VERTEX_ATTRIB_ARRAY6_NV 0x8656 +#define GL_VERTEX_ATTRIB_ARRAY7_NV 0x8657 +#define GL_VERTEX_ATTRIB_ARRAY8_NV 0x8658 +#define GL_VERTEX_ATTRIB_ARRAY9_NV 0x8659 +#define GL_VERTEX_ATTRIB_ARRAY10_NV 0x865A +#define GL_VERTEX_ATTRIB_ARRAY11_NV 0x865B +#define GL_VERTEX_ATTRIB_ARRAY12_NV 0x865C +#define GL_VERTEX_ATTRIB_ARRAY13_NV 0x865D +#define GL_VERTEX_ATTRIB_ARRAY14_NV 0x865E +#define GL_VERTEX_ATTRIB_ARRAY15_NV 0x865F +#define GL_MAP1_VERTEX_ATTRIB0_4_NV 0x8660 +#define GL_MAP1_VERTEX_ATTRIB1_4_NV 0x8661 +#define GL_MAP1_VERTEX_ATTRIB2_4_NV 0x8662 +#define GL_MAP1_VERTEX_ATTRIB3_4_NV 0x8663 +#define GL_MAP1_VERTEX_ATTRIB4_4_NV 0x8664 +#define GL_MAP1_VERTEX_ATTRIB5_4_NV 0x8665 +#define GL_MAP1_VERTEX_ATTRIB6_4_NV 0x8666 +#define GL_MAP1_VERTEX_ATTRIB7_4_NV 0x8667 +#define GL_MAP1_VERTEX_ATTRIB8_4_NV 0x8668 +#define GL_MAP1_VERTEX_ATTRIB9_4_NV 0x8669 +#define GL_MAP1_VERTEX_ATTRIB10_4_NV 0x866A +#define GL_MAP1_VERTEX_ATTRIB11_4_NV 0x866B +#define GL_MAP1_VERTEX_ATTRIB12_4_NV 0x866C +#define GL_MAP1_VERTEX_ATTRIB13_4_NV 0x866D +#define GL_MAP1_VERTEX_ATTRIB14_4_NV 0x866E +#define GL_MAP1_VERTEX_ATTRIB15_4_NV 0x866F +#define GL_MAP2_VERTEX_ATTRIB0_4_NV 0x8670 +#define GL_MAP2_VERTEX_ATTRIB1_4_NV 0x8671 +#define GL_MAP2_VERTEX_ATTRIB2_4_NV 0x8672 +#define GL_MAP2_VERTEX_ATTRIB3_4_NV 0x8673 +#define GL_MAP2_VERTEX_ATTRIB4_4_NV 0x8674 +#define GL_MAP2_VERTEX_ATTRIB5_4_NV 0x8675 +#define GL_MAP2_VERTEX_ATTRIB6_4_NV 0x8676 +#define GL_MAP2_VERTEX_ATTRIB7_4_NV 0x8677 +#define GL_MAP2_VERTEX_ATTRIB8_4_NV 0x8678 +#define GL_MAP2_VERTEX_ATTRIB9_4_NV 0x8679 +#define GL_MAP2_VERTEX_ATTRIB10_4_NV 0x867A +#define GL_MAP2_VERTEX_ATTRIB11_4_NV 0x867B +#define GL_MAP2_VERTEX_ATTRIB12_4_NV 0x867C +#define GL_MAP2_VERTEX_ATTRIB13_4_NV 0x867D +#define GL_MAP2_VERTEX_ATTRIB14_4_NV 0x867E +#define GL_MAP2_VERTEX_ATTRIB15_4_NV 0x867F +typedef GLboolean (APIENTRYP PFNGLAREPROGRAMSRESIDENTNVPROC)(GLsizei n, const GLuint * programs, GLboolean * residences); +typedef void (APIENTRYP PFNGLBINDPROGRAMNVPROC)(GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETEPROGRAMSNVPROC)(GLsizei n, const GLuint * programs); +typedef void (APIENTRYP PFNGLEXECUTEPROGRAMNVPROC)(GLenum target, GLuint id, const GLfloat * params); +typedef void (APIENTRYP PFNGLGENPROGRAMSNVPROC)(GLsizei n, GLuint * programs); +typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERDVNVPROC)(GLenum target, GLuint index, GLenum pname, GLdouble * params); +typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERFVNVPROC)(GLenum target, GLuint index, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETPROGRAMIVNVPROC)(GLuint id, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGNVPROC)(GLuint id, GLenum pname, GLubyte * program); +typedef void (APIENTRYP PFNGLGETTRACKMATRIXIVNVPROC)(GLenum target, GLuint address, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVNVPROC)(GLuint index, GLenum pname, GLdouble * params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVNVPROC)(GLuint index, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVNVPROC)(GLuint index, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVNVPROC)(GLuint index, GLenum pname, void ** pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMNVPROC)(GLuint id); +typedef void (APIENTRYP PFNGLLOADPROGRAMNVPROC)(GLenum target, GLuint id, GLsizei len, const GLubyte * program); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DNVPROC)(GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DVNVPROC)(GLenum target, GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FNVPROC)(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FVNVPROC)(GLenum target, GLuint index, const GLfloat * v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4DVNVPROC)(GLenum target, GLuint index, GLsizei count, const GLdouble * v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4FVNVPROC)(GLenum target, GLuint index, GLsizei count, const GLfloat * v); +typedef void (APIENTRYP PFNGLREQUESTRESIDENTPROGRAMSNVPROC)(GLsizei n, const GLuint * programs); +typedef void (APIENTRYP PFNGLTRACKMATRIXNVPROC)(GLenum target, GLuint address, GLenum matrix, GLenum transform); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERNVPROC)(GLuint index, GLint fsize, GLenum type, GLsizei stride, const void * pointer); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DNVPROC)(GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVNVPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FNVPROC)(GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVNVPROC)(GLuint index, const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SNVPROC)(GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVNVPROC)(GLuint index, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DNVPROC)(GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVNVPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FNVPROC)(GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVNVPROC)(GLuint index, const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SNVPROC)(GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVNVPROC)(GLuint index, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DNVPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVNVPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FNVPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVNVPROC)(GLuint index, const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SNVPROC)(GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVNVPROC)(GLuint index, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DNVPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVNVPROC)(GLuint index, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FNVPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVNVPROC)(GLuint index, const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SNVPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVNVPROC)(GLuint index, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBNVPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVNVPROC)(GLuint index, const GLubyte * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1DVNVPROC)(GLuint index, GLsizei count, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1FVNVPROC)(GLuint index, GLsizei count, const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1SVNVPROC)(GLuint index, GLsizei count, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2DVNVPROC)(GLuint index, GLsizei count, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2FVNVPROC)(GLuint index, GLsizei count, const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2SVNVPROC)(GLuint index, GLsizei count, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3DVNVPROC)(GLuint index, GLsizei count, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3FVNVPROC)(GLuint index, GLsizei count, const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3SVNVPROC)(GLuint index, GLsizei count, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4DVNVPROC)(GLuint index, GLsizei count, const GLdouble * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4FVNVPROC)(GLuint index, GLsizei count, const GLfloat * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4SVNVPROC)(GLuint index, GLsizei count, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4UBVNVPROC)(GLuint index, GLsizei count, const GLubyte * v); +#endif /* GL_NV_vertex_program */ + +#ifndef GL_NV_vertex_program1_1 +#define GL_NV_vertex_program1_1 1 +#endif /* GL_NV_vertex_program1_1 */ + +#ifndef GL_NV_vertex_program2 +#define GL_NV_vertex_program2 1 +#endif /* GL_NV_vertex_program2 */ + +#ifndef GL_NV_vertex_program2_option +#define GL_NV_vertex_program2_option 1 +#define GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV 0x88F4 +#define GL_MAX_PROGRAM_CALL_DEPTH_NV 0x88F5 +#endif /* GL_NV_vertex_program2_option */ + +#ifndef GL_NV_vertex_program3 +#define GL_NV_vertex_program3 1 +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB 0x8B4C +#endif /* GL_NV_vertex_program3 */ + +#ifndef GL_NV_vertex_program4 +#define GL_NV_vertex_program4 1 +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_NV 0x88FD +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IEXTPROC)(GLuint index, GLint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IEXTPROC)(GLuint index, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IEXTPROC)(GLuint index, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IEXTPROC)(GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIEXTPROC)(GLuint index, GLuint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIEXTPROC)(GLuint index, GLuint x, GLuint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIEXTPROC)(GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIEXTPROC)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVEXTPROC)(GLuint index, const GLint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVEXTPROC)(GLuint index, const GLint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVEXTPROC)(GLuint index, const GLint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVEXTPROC)(GLuint index, const GLint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVEXTPROC)(GLuint index, const GLuint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVEXTPROC)(GLuint index, const GLuint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVEXTPROC)(GLuint index, const GLuint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVEXTPROC)(GLuint index, const GLuint * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVEXTPROC)(GLuint index, const GLbyte * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVEXTPROC)(GLuint index, const GLshort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVEXTPROC)(GLuint index, const GLubyte * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVEXTPROC)(GLuint index, const GLushort * v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTEREXTPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVEXTPROC)(GLuint index, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVEXTPROC)(GLuint index, GLenum pname, GLuint * params); +#endif /* GL_NV_vertex_program4 */ + +#ifndef GL_NV_video_capture +#define GL_NV_video_capture 1 +#define GL_VIDEO_BUFFER_NV 0x9020 +#define GL_VIDEO_BUFFER_BINDING_NV 0x9021 +#define GL_FIELD_UPPER_NV 0x9022 +#define GL_FIELD_LOWER_NV 0x9023 +#define GL_NUM_VIDEO_CAPTURE_STREAMS_NV 0x9024 +#define GL_NEXT_VIDEO_CAPTURE_BUFFER_STATUS_NV 0x9025 +#define GL_VIDEO_CAPTURE_TO_422_SUPPORTED_NV 0x9026 +#define GL_LAST_VIDEO_CAPTURE_STATUS_NV 0x9027 +#define GL_VIDEO_BUFFER_PITCH_NV 0x9028 +#define GL_VIDEO_COLOR_CONVERSION_MATRIX_NV 0x9029 +#define GL_VIDEO_COLOR_CONVERSION_MAX_NV 0x902A +#define GL_VIDEO_COLOR_CONVERSION_MIN_NV 0x902B +#define GL_VIDEO_COLOR_CONVERSION_OFFSET_NV 0x902C +#define GL_VIDEO_BUFFER_INTERNAL_FORMAT_NV 0x902D +#define GL_PARTIAL_SUCCESS_NV 0x902E +#define GL_SUCCESS_NV 0x902F +#define GL_FAILURE_NV 0x9030 +#define GL_YCBYCR8_422_NV 0x9031 +#define GL_YCBAYCR8A_4224_NV 0x9032 +#define GL_Z6Y10Z6CB10Z6Y10Z6CR10_422_NV 0x9033 +#define GL_Z6Y10Z6CB10Z6A10Z6Y10Z6CR10Z6A10_4224_NV 0x9034 +#define GL_Z4Y12Z4CB12Z4Y12Z4CR12_422_NV 0x9035 +#define GL_Z4Y12Z4CB12Z4A12Z4Y12Z4CR12Z4A12_4224_NV 0x9036 +#define GL_Z4Y12Z4CB12Z4CR12_444_NV 0x9037 +#define GL_VIDEO_CAPTURE_FRAME_WIDTH_NV 0x9038 +#define GL_VIDEO_CAPTURE_FRAME_HEIGHT_NV 0x9039 +#define GL_VIDEO_CAPTURE_FIELD_UPPER_HEIGHT_NV 0x903A +#define GL_VIDEO_CAPTURE_FIELD_LOWER_HEIGHT_NV 0x903B +#define GL_VIDEO_CAPTURE_SURFACE_ORIGIN_NV 0x903C +typedef void (APIENTRYP PFNGLBEGINVIDEOCAPTURENVPROC)(GLuint video_capture_slot); +typedef void (APIENTRYP PFNGLBINDVIDEOCAPTURESTREAMBUFFERNVPROC)(GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLintptrARB offset); +typedef void (APIENTRYP PFNGLBINDVIDEOCAPTURESTREAMTEXTURENVPROC)(GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLENDVIDEOCAPTURENVPROC)(GLuint video_capture_slot); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTUREIVNVPROC)(GLuint video_capture_slot, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMIVNVPROC)(GLuint video_capture_slot, GLuint stream, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMFVNVPROC)(GLuint video_capture_slot, GLuint stream, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMDVNVPROC)(GLuint video_capture_slot, GLuint stream, GLenum pname, GLdouble * params); +typedef GLenum (APIENTRYP PFNGLVIDEOCAPTURENVPROC)(GLuint video_capture_slot, GLuint * sequence_num, GLuint64EXT * capture_time); +typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERIVNVPROC)(GLuint video_capture_slot, GLuint stream, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERFVNVPROC)(GLuint video_capture_slot, GLuint stream, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERDVNVPROC)(GLuint video_capture_slot, GLuint stream, GLenum pname, const GLdouble * params); +#endif /* GL_NV_video_capture */ + +#ifndef GL_NV_viewport_array2 +#define GL_NV_viewport_array2 1 +#endif /* GL_NV_viewport_array2 */ + +#ifndef GL_NV_viewport_swizzle +#define GL_NV_viewport_swizzle 1 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_X_NV 0x9350 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_X_NV 0x9351 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_Y_NV 0x9352 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_Y_NV 0x9353 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_Z_NV 0x9354 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_Z_NV 0x9355 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_W_NV 0x9356 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_W_NV 0x9357 +#define GL_VIEWPORT_SWIZZLE_X_NV 0x9358 +#define GL_VIEWPORT_SWIZZLE_Y_NV 0x9359 +#define GL_VIEWPORT_SWIZZLE_Z_NV 0x935A +#define GL_VIEWPORT_SWIZZLE_W_NV 0x935B +typedef void (APIENTRYP PFNGLVIEWPORTSWIZZLENVPROC)(GLuint index, GLenum swizzlex, GLenum swizzley, GLenum swizzlez, GLenum swizzlew); +#endif /* GL_NV_viewport_swizzle */ + +#ifndef GL_OES_byte_coordinates +#define GL_OES_byte_coordinates 1 +#define GL_BYTE 0x1400 +typedef void (APIENTRYP PFNGLMULTITEXCOORD1BOESPROC)(GLenum texture, GLbyte s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1BVOESPROC)(GLenum texture, const GLbyte * coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2BOESPROC)(GLenum texture, GLbyte s, GLbyte t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2BVOESPROC)(GLenum texture, const GLbyte * coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3BOESPROC)(GLenum texture, GLbyte s, GLbyte t, GLbyte r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3BVOESPROC)(GLenum texture, const GLbyte * coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4BOESPROC)(GLenum texture, GLbyte s, GLbyte t, GLbyte r, GLbyte q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4BVOESPROC)(GLenum texture, const GLbyte * coords); +typedef void (APIENTRYP PFNGLTEXCOORD1BOESPROC)(GLbyte s); +typedef void (APIENTRYP PFNGLTEXCOORD1BVOESPROC)(const GLbyte * coords); +typedef void (APIENTRYP PFNGLTEXCOORD2BOESPROC)(GLbyte s, GLbyte t); +typedef void (APIENTRYP PFNGLTEXCOORD2BVOESPROC)(const GLbyte * coords); +typedef void (APIENTRYP PFNGLTEXCOORD3BOESPROC)(GLbyte s, GLbyte t, GLbyte r); +typedef void (APIENTRYP PFNGLTEXCOORD3BVOESPROC)(const GLbyte * coords); +typedef void (APIENTRYP PFNGLTEXCOORD4BOESPROC)(GLbyte s, GLbyte t, GLbyte r, GLbyte q); +typedef void (APIENTRYP PFNGLTEXCOORD4BVOESPROC)(const GLbyte * coords); +typedef void (APIENTRYP PFNGLVERTEX2BOESPROC)(GLbyte x, GLbyte y); +typedef void (APIENTRYP PFNGLVERTEX2BVOESPROC)(const GLbyte * coords); +typedef void (APIENTRYP PFNGLVERTEX3BOESPROC)(GLbyte x, GLbyte y, GLbyte z); +typedef void (APIENTRYP PFNGLVERTEX3BVOESPROC)(const GLbyte * coords); +typedef void (APIENTRYP PFNGLVERTEX4BOESPROC)(GLbyte x, GLbyte y, GLbyte z, GLbyte w); +typedef void (APIENTRYP PFNGLVERTEX4BVOESPROC)(const GLbyte * coords); +#endif /* GL_OES_byte_coordinates */ + +#ifndef GL_OES_compressed_paletted_texture +#define GL_OES_compressed_paletted_texture 1 +#define GL_PALETTE4_RGB8_OES 0x8B90 +#define GL_PALETTE4_RGBA8_OES 0x8B91 +#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 +#define GL_PALETTE4_RGBA4_OES 0x8B93 +#define GL_PALETTE4_RGB5_A1_OES 0x8B94 +#define GL_PALETTE8_RGB8_OES 0x8B95 +#define GL_PALETTE8_RGBA8_OES 0x8B96 +#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 +#define GL_PALETTE8_RGBA4_OES 0x8B98 +#define GL_PALETTE8_RGB5_A1_OES 0x8B99 +#endif /* GL_OES_compressed_paletted_texture */ + +#ifndef GL_OES_fixed_point +#define GL_OES_fixed_point 1 +typedef khronos_int32_t GLfixed; +typedef khronos_int32_t GLclampx; +#define GL_FIXED_OES 0x140C +typedef void (APIENTRYP PFNGLALPHAFUNCXOESPROC)(GLenum func, GLfixed ref); +typedef void (APIENTRYP PFNGLCLEARCOLORXOESPROC)(GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCLEARDEPTHXOESPROC)(GLfixed depth); +typedef void (APIENTRYP PFNGLCLIPPLANEXOESPROC)(GLenum plane, const GLfixed * equation); +typedef void (APIENTRYP PFNGLCOLOR4XOESPROC)(GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLDEPTHRANGEXOESPROC)(GLfixed n, GLfixed f); +typedef void (APIENTRYP PFNGLFOGXOESPROC)(GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLFOGXVOESPROC)(GLenum pname, const GLfixed * param); +typedef void (APIENTRYP PFNGLFRUSTUMXOESPROC)(GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +typedef void (APIENTRYP PFNGLGETCLIPPLANEXOESPROC)(GLenum plane, GLfixed * equation); +typedef void (APIENTRYP PFNGLGETFIXEDVOESPROC)(GLenum pname, GLfixed * params); +typedef void (APIENTRYP PFNGLGETTEXENVXVOESPROC)(GLenum target, GLenum pname, GLfixed * params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERXVOESPROC)(GLenum target, GLenum pname, GLfixed * params); +typedef void (APIENTRYP PFNGLLIGHTMODELXOESPROC)(GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLLIGHTMODELXVOESPROC)(GLenum pname, const GLfixed * param); +typedef void (APIENTRYP PFNGLLIGHTXOESPROC)(GLenum light, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLLIGHTXVOESPROC)(GLenum light, GLenum pname, const GLfixed * params); +typedef void (APIENTRYP PFNGLLINEWIDTHXOESPROC)(GLfixed width); +typedef void (APIENTRYP PFNGLLOADMATRIXXOESPROC)(const GLfixed * m); +typedef void (APIENTRYP PFNGLMATERIALXOESPROC)(GLenum face, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLMATERIALXVOESPROC)(GLenum face, GLenum pname, const GLfixed * param); +typedef void (APIENTRYP PFNGLMULTMATRIXXOESPROC)(const GLfixed * m); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4XOESPROC)(GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +typedef void (APIENTRYP PFNGLNORMAL3XOESPROC)(GLfixed nx, GLfixed ny, GLfixed nz); +typedef void (APIENTRYP PFNGLORTHOXOESPROC)(GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +typedef void (APIENTRYP PFNGLPOINTPARAMETERXVOESPROC)(GLenum pname, const GLfixed * params); +typedef void (APIENTRYP PFNGLPOINTSIZEXOESPROC)(GLfixed size); +typedef void (APIENTRYP PFNGLPOLYGONOFFSETXOESPROC)(GLfixed factor, GLfixed units); +typedef void (APIENTRYP PFNGLROTATEXOESPROC)(GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLSCALEXOESPROC)(GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLTEXENVXOESPROC)(GLenum target, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLTEXENVXVOESPROC)(GLenum target, GLenum pname, const GLfixed * params); +typedef void (APIENTRYP PFNGLTEXPARAMETERXOESPROC)(GLenum target, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLTEXPARAMETERXVOESPROC)(GLenum target, GLenum pname, const GLfixed * params); +typedef void (APIENTRYP PFNGLTRANSLATEXOESPROC)(GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLGETLIGHTXVOESPROC)(GLenum light, GLenum pname, GLfixed * params); +typedef void (APIENTRYP PFNGLGETMATERIALXVOESPROC)(GLenum face, GLenum pname, GLfixed * params); +typedef void (APIENTRYP PFNGLPOINTPARAMETERXOESPROC)(GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEXOESPROC)(GLclampx value, GLboolean invert); +typedef void (APIENTRYP PFNGLACCUMXOESPROC)(GLenum op, GLfixed value); +typedef void (APIENTRYP PFNGLBITMAPXOESPROC)(GLsizei width, GLsizei height, GLfixed xorig, GLfixed yorig, GLfixed xmove, GLfixed ymove, const GLubyte * bitmap); +typedef void (APIENTRYP PFNGLBLENDCOLORXOESPROC)(GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCLEARACCUMXOESPROC)(GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCOLOR3XOESPROC)(GLfixed red, GLfixed green, GLfixed blue); +typedef void (APIENTRYP PFNGLCOLOR3XVOESPROC)(const GLfixed * components); +typedef void (APIENTRYP PFNGLCOLOR4XVOESPROC)(const GLfixed * components); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERXOESPROC)(GLenum target, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERXVOESPROC)(GLenum target, GLenum pname, const GLfixed * params); +typedef void (APIENTRYP PFNGLEVALCOORD1XOESPROC)(GLfixed u); +typedef void (APIENTRYP PFNGLEVALCOORD1XVOESPROC)(const GLfixed * coords); +typedef void (APIENTRYP PFNGLEVALCOORD2XOESPROC)(GLfixed u, GLfixed v); +typedef void (APIENTRYP PFNGLEVALCOORD2XVOESPROC)(const GLfixed * coords); +typedef void (APIENTRYP PFNGLFEEDBACKBUFFERXOESPROC)(GLsizei n, GLenum type, const GLfixed * buffer); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERXVOESPROC)(GLenum target, GLenum pname, GLfixed * params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERXVOESPROC)(GLenum target, GLenum pname, GLfixed * params); +typedef void (APIENTRYP PFNGLGETLIGHTXOESPROC)(GLenum light, GLenum pname, GLfixed * params); +typedef void (APIENTRYP PFNGLGETMAPXVOESPROC)(GLenum target, GLenum query, GLfixed * v); +typedef void (APIENTRYP PFNGLGETMATERIALXOESPROC)(GLenum face, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLGETPIXELMAPXVPROC)(GLenum map, GLint size, GLfixed * values); +typedef void (APIENTRYP PFNGLGETTEXGENXVOESPROC)(GLenum coord, GLenum pname, GLfixed * params); +typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERXVOESPROC)(GLenum target, GLint level, GLenum pname, GLfixed * params); +typedef void (APIENTRYP PFNGLINDEXXOESPROC)(GLfixed component); +typedef void (APIENTRYP PFNGLINDEXXVOESPROC)(const GLfixed * component); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXXOESPROC)(const GLfixed * m); +typedef void (APIENTRYP PFNGLMAP1XOESPROC)(GLenum target, GLfixed u1, GLfixed u2, GLint stride, GLint order, GLfixed points); +typedef void (APIENTRYP PFNGLMAP2XOESPROC)(GLenum target, GLfixed u1, GLfixed u2, GLint ustride, GLint uorder, GLfixed v1, GLfixed v2, GLint vstride, GLint vorder, GLfixed points); +typedef void (APIENTRYP PFNGLMAPGRID1XOESPROC)(GLint n, GLfixed u1, GLfixed u2); +typedef void (APIENTRYP PFNGLMAPGRID2XOESPROC)(GLint n, GLfixed u1, GLfixed u2, GLfixed v1, GLfixed v2); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXXOESPROC)(const GLfixed * m); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1XOESPROC)(GLenum texture, GLfixed s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1XVOESPROC)(GLenum texture, const GLfixed * coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2XOESPROC)(GLenum texture, GLfixed s, GLfixed t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2XVOESPROC)(GLenum texture, const GLfixed * coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3XOESPROC)(GLenum texture, GLfixed s, GLfixed t, GLfixed r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3XVOESPROC)(GLenum texture, const GLfixed * coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4XVOESPROC)(GLenum texture, const GLfixed * coords); +typedef void (APIENTRYP PFNGLNORMAL3XVOESPROC)(const GLfixed * coords); +typedef void (APIENTRYP PFNGLPASSTHROUGHXOESPROC)(GLfixed token); +typedef void (APIENTRYP PFNGLPIXELMAPXPROC)(GLenum map, GLint size, const GLfixed * values); +typedef void (APIENTRYP PFNGLPIXELSTOREXPROC)(GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLPIXELTRANSFERXOESPROC)(GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLPIXELZOOMXOESPROC)(GLfixed xfactor, GLfixed yfactor); +typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESXOESPROC)(GLsizei n, const GLuint * textures, const GLfixed * priorities); +typedef void (APIENTRYP PFNGLRASTERPOS2XOESPROC)(GLfixed x, GLfixed y); +typedef void (APIENTRYP PFNGLRASTERPOS2XVOESPROC)(const GLfixed * coords); +typedef void (APIENTRYP PFNGLRASTERPOS3XOESPROC)(GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLRASTERPOS3XVOESPROC)(const GLfixed * coords); +typedef void (APIENTRYP PFNGLRASTERPOS4XOESPROC)(GLfixed x, GLfixed y, GLfixed z, GLfixed w); +typedef void (APIENTRYP PFNGLRASTERPOS4XVOESPROC)(const GLfixed * coords); +typedef void (APIENTRYP PFNGLRECTXOESPROC)(GLfixed x1, GLfixed y1, GLfixed x2, GLfixed y2); +typedef void (APIENTRYP PFNGLRECTXVOESPROC)(const GLfixed * v1, const GLfixed * v2); +typedef void (APIENTRYP PFNGLTEXCOORD1XOESPROC)(GLfixed s); +typedef void (APIENTRYP PFNGLTEXCOORD1XVOESPROC)(const GLfixed * coords); +typedef void (APIENTRYP PFNGLTEXCOORD2XOESPROC)(GLfixed s, GLfixed t); +typedef void (APIENTRYP PFNGLTEXCOORD2XVOESPROC)(const GLfixed * coords); +typedef void (APIENTRYP PFNGLTEXCOORD3XOESPROC)(GLfixed s, GLfixed t, GLfixed r); +typedef void (APIENTRYP PFNGLTEXCOORD3XVOESPROC)(const GLfixed * coords); +typedef void (APIENTRYP PFNGLTEXCOORD4XOESPROC)(GLfixed s, GLfixed t, GLfixed r, GLfixed q); +typedef void (APIENTRYP PFNGLTEXCOORD4XVOESPROC)(const GLfixed * coords); +typedef void (APIENTRYP PFNGLTEXGENXOESPROC)(GLenum coord, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLTEXGENXVOESPROC)(GLenum coord, GLenum pname, const GLfixed * params); +typedef void (APIENTRYP PFNGLVERTEX2XOESPROC)(GLfixed x); +typedef void (APIENTRYP PFNGLVERTEX2XVOESPROC)(const GLfixed * coords); +typedef void (APIENTRYP PFNGLVERTEX3XOESPROC)(GLfixed x, GLfixed y); +typedef void (APIENTRYP PFNGLVERTEX3XVOESPROC)(const GLfixed * coords); +typedef void (APIENTRYP PFNGLVERTEX4XOESPROC)(GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLVERTEX4XVOESPROC)(const GLfixed * coords); +#endif /* GL_OES_fixed_point */ + +#ifndef GL_OES_query_matrix +#define GL_OES_query_matrix 1 +typedef GLbitfield (APIENTRYP PFNGLQUERYMATRIXXOESPROC)(GLfixed * mantissa, GLint * exponent); +#endif /* GL_OES_query_matrix */ + +#ifndef GL_OES_read_format +#define GL_OES_read_format 1 +#define GL_IMPLEMENTATION_COLOR_READ_TYPE_OES 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES 0x8B9B +#endif /* GL_OES_read_format */ + +#ifndef GL_OES_single_precision +#define GL_OES_single_precision 1 +typedef void (APIENTRYP PFNGLCLEARDEPTHFOESPROC)(GLclampf depth); +typedef void (APIENTRYP PFNGLCLIPPLANEFOESPROC)(GLenum plane, const GLfloat * equation); +typedef void (APIENTRYP PFNGLDEPTHRANGEFOESPROC)(GLclampf n, GLclampf f); +typedef void (APIENTRYP PFNGLFRUSTUMFOESPROC)(GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +typedef void (APIENTRYP PFNGLGETCLIPPLANEFOESPROC)(GLenum plane, GLfloat * equation); +typedef void (APIENTRYP PFNGLORTHOFOESPROC)(GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +#endif /* GL_OES_single_precision */ + +#ifndef GL_OML_interlace +#define GL_OML_interlace 1 +#define GL_INTERLACE_OML 0x8980 +#define GL_INTERLACE_READ_OML 0x8981 +#endif /* GL_OML_interlace */ + +#ifndef GL_OML_resample +#define GL_OML_resample 1 +#define GL_PACK_RESAMPLE_OML 0x8984 +#define GL_UNPACK_RESAMPLE_OML 0x8985 +#define GL_RESAMPLE_REPLICATE_OML 0x8986 +#define GL_RESAMPLE_ZERO_FILL_OML 0x8987 +#define GL_RESAMPLE_AVERAGE_OML 0x8988 +#define GL_RESAMPLE_DECIMATE_OML 0x8989 +#endif /* GL_OML_resample */ + +#ifndef GL_OML_subsample +#define GL_OML_subsample 1 +#define GL_FORMAT_SUBSAMPLE_24_24_OML 0x8982 +#define GL_FORMAT_SUBSAMPLE_244_244_OML 0x8983 +#endif /* GL_OML_subsample */ + +#ifndef GL_OVR_multiview +#define GL_OVR_multiview 1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR 0x9630 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR 0x9632 +#define GL_MAX_VIEWS_OVR 0x9631 +#define GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR 0x9633 +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); +#endif /* GL_OVR_multiview */ + +#ifndef GL_OVR_multiview2 +#define GL_OVR_multiview2 1 +#endif /* GL_OVR_multiview2 */ + +#ifndef GL_PGI_misc_hints +#define GL_PGI_misc_hints 1 +#define GL_PREFER_DOUBLEBUFFER_HINT_PGI 0x1A1F8 +#define GL_CONSERVE_MEMORY_HINT_PGI 0x1A1FD +#define GL_RECLAIM_MEMORY_HINT_PGI 0x1A1FE +#define GL_NATIVE_GRAPHICS_HANDLE_PGI 0x1A202 +#define GL_NATIVE_GRAPHICS_BEGIN_HINT_PGI 0x1A203 +#define GL_NATIVE_GRAPHICS_END_HINT_PGI 0x1A204 +#define GL_ALWAYS_FAST_HINT_PGI 0x1A20C +#define GL_ALWAYS_SOFT_HINT_PGI 0x1A20D +#define GL_ALLOW_DRAW_OBJ_HINT_PGI 0x1A20E +#define GL_ALLOW_DRAW_WIN_HINT_PGI 0x1A20F +#define GL_ALLOW_DRAW_FRG_HINT_PGI 0x1A210 +#define GL_ALLOW_DRAW_MEM_HINT_PGI 0x1A211 +#define GL_STRICT_DEPTHFUNC_HINT_PGI 0x1A216 +#define GL_STRICT_LIGHTING_HINT_PGI 0x1A217 +#define GL_STRICT_SCISSOR_HINT_PGI 0x1A218 +#define GL_FULL_STIPPLE_HINT_PGI 0x1A219 +#define GL_CLIP_NEAR_HINT_PGI 0x1A220 +#define GL_CLIP_FAR_HINT_PGI 0x1A221 +#define GL_WIDE_LINE_HINT_PGI 0x1A222 +#define GL_BACK_NORMALS_HINT_PGI 0x1A223 +typedef void (APIENTRYP PFNGLHINTPGIPROC)(GLenum target, GLint mode); +#endif /* GL_PGI_misc_hints */ + +#ifndef GL_PGI_vertex_hints +#define GL_PGI_vertex_hints 1 +#define GL_VERTEX_DATA_HINT_PGI 0x1A22A +#define GL_VERTEX_CONSISTENT_HINT_PGI 0x1A22B +#define GL_MATERIAL_SIDE_HINT_PGI 0x1A22C +#define GL_MAX_VERTEX_HINT_PGI 0x1A22D +#define GL_COLOR3_BIT_PGI 0x00010000 +#define GL_COLOR4_BIT_PGI 0x00020000 +#define GL_EDGEFLAG_BIT_PGI 0x00040000 +#define GL_INDEX_BIT_PGI 0x00080000 +#define GL_MAT_AMBIENT_BIT_PGI 0x00100000 +#define GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI 0x00200000 +#define GL_MAT_DIFFUSE_BIT_PGI 0x00400000 +#define GL_MAT_EMISSION_BIT_PGI 0x00800000 +#define GL_MAT_COLOR_INDEXES_BIT_PGI 0x01000000 +#define GL_MAT_SHININESS_BIT_PGI 0x02000000 +#define GL_MAT_SPECULAR_BIT_PGI 0x04000000 +#define GL_NORMAL_BIT_PGI 0x08000000 +#define GL_TEXCOORD1_BIT_PGI 0x10000000 +#define GL_TEXCOORD2_BIT_PGI 0x20000000 +#define GL_TEXCOORD3_BIT_PGI 0x40000000 +#define GL_TEXCOORD4_BIT_PGI 0x80000000 +#define GL_VERTEX23_BIT_PGI 0x00000004 +#define GL_VERTEX4_BIT_PGI 0x00000008 +#endif /* GL_PGI_vertex_hints */ + +#ifndef GL_REND_screen_coordinates +#define GL_REND_screen_coordinates 1 +#define GL_SCREEN_COORDINATES_REND 0x8490 +#define GL_INVERTED_SCREEN_W_REND 0x8491 +#endif /* GL_REND_screen_coordinates */ + +#ifndef GL_S3_s3tc +#define GL_S3_s3tc 1 +#define GL_RGB_S3TC 0x83A0 +#define GL_RGB4_S3TC 0x83A1 +#define GL_RGBA_S3TC 0x83A2 +#define GL_RGBA4_S3TC 0x83A3 +#define GL_RGBA_DXT5_S3TC 0x83A4 +#define GL_RGBA4_DXT5_S3TC 0x83A5 +#endif /* GL_S3_s3tc */ + +#ifndef GL_SGIS_detail_texture +#define GL_SGIS_detail_texture 1 +#define GL_DETAIL_TEXTURE_2D_SGIS 0x8095 +#define GL_DETAIL_TEXTURE_2D_BINDING_SGIS 0x8096 +#define GL_LINEAR_DETAIL_SGIS 0x8097 +#define GL_LINEAR_DETAIL_ALPHA_SGIS 0x8098 +#define GL_LINEAR_DETAIL_COLOR_SGIS 0x8099 +#define GL_DETAIL_TEXTURE_LEVEL_SGIS 0x809A +#define GL_DETAIL_TEXTURE_MODE_SGIS 0x809B +#define GL_DETAIL_TEXTURE_FUNC_POINTS_SGIS 0x809C +typedef void (APIENTRYP PFNGLDETAILTEXFUNCSGISPROC)(GLenum target, GLsizei n, const GLfloat * points); +typedef void (APIENTRYP PFNGLGETDETAILTEXFUNCSGISPROC)(GLenum target, GLfloat * points); +#endif /* GL_SGIS_detail_texture */ + +#ifndef GL_SGIS_fog_function +#define GL_SGIS_fog_function 1 +#define GL_FOG_FUNC_SGIS 0x812A +#define GL_FOG_FUNC_POINTS_SGIS 0x812B +#define GL_MAX_FOG_FUNC_POINTS_SGIS 0x812C +typedef void (APIENTRYP PFNGLFOGFUNCSGISPROC)(GLsizei n, const GLfloat * points); +typedef void (APIENTRYP PFNGLGETFOGFUNCSGISPROC)(GLfloat * points); +#endif /* GL_SGIS_fog_function */ + +#ifndef GL_SGIS_generate_mipmap +#define GL_SGIS_generate_mipmap 1 +#define GL_GENERATE_MIPMAP_SGIS 0x8191 +#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192 +#endif /* GL_SGIS_generate_mipmap */ + +#ifndef GL_SGIS_multisample +#define GL_SGIS_multisample 1 +#define GL_MULTISAMPLE_SGIS 0x809D +#define GL_SAMPLE_ALPHA_TO_MASK_SGIS 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_SGIS 0x809F +#define GL_SAMPLE_MASK_SGIS 0x80A0 +#define GL_1PASS_SGIS 0x80A1 +#define GL_2PASS_0_SGIS 0x80A2 +#define GL_2PASS_1_SGIS 0x80A3 +#define GL_4PASS_0_SGIS 0x80A4 +#define GL_4PASS_1_SGIS 0x80A5 +#define GL_4PASS_2_SGIS 0x80A6 +#define GL_4PASS_3_SGIS 0x80A7 +#define GL_SAMPLE_BUFFERS_SGIS 0x80A8 +#define GL_SAMPLES_SGIS 0x80A9 +#define GL_SAMPLE_MASK_VALUE_SGIS 0x80AA +#define GL_SAMPLE_MASK_INVERT_SGIS 0x80AB +#define GL_SAMPLE_PATTERN_SGIS 0x80AC +typedef void (APIENTRYP PFNGLSAMPLEMASKSGISPROC)(GLclampf value, GLboolean invert); +typedef void (APIENTRYP PFNGLSAMPLEPATTERNSGISPROC)(GLenum pattern); +#endif /* GL_SGIS_multisample */ + +#ifndef GL_SGIS_pixel_texture +#define GL_SGIS_pixel_texture 1 +#define GL_PIXEL_TEXTURE_SGIS 0x8353 +#define GL_PIXEL_FRAGMENT_RGB_SOURCE_SGIS 0x8354 +#define GL_PIXEL_FRAGMENT_ALPHA_SOURCE_SGIS 0x8355 +#define GL_PIXEL_GROUP_COLOR_SGIS 0x8356 +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERISGISPROC)(GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERIVSGISPROC)(GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFSGISPROC)(GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFVSGISPROC)(GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERIVSGISPROC)(GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERFVSGISPROC)(GLenum pname, GLfloat * params); +#endif /* GL_SGIS_pixel_texture */ + +#ifndef GL_SGIS_point_line_texgen +#define GL_SGIS_point_line_texgen 1 +#define GL_EYE_DISTANCE_TO_POINT_SGIS 0x81F0 +#define GL_OBJECT_DISTANCE_TO_POINT_SGIS 0x81F1 +#define GL_EYE_DISTANCE_TO_LINE_SGIS 0x81F2 +#define GL_OBJECT_DISTANCE_TO_LINE_SGIS 0x81F3 +#define GL_EYE_POINT_SGIS 0x81F4 +#define GL_OBJECT_POINT_SGIS 0x81F5 +#define GL_EYE_LINE_SGIS 0x81F6 +#define GL_OBJECT_LINE_SGIS 0x81F7 +#endif /* GL_SGIS_point_line_texgen */ + +#ifndef GL_SGIS_point_parameters +#define GL_SGIS_point_parameters 1 +#define GL_POINT_SIZE_MIN_SGIS 0x8126 +#define GL_POINT_SIZE_MAX_SGIS 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_SGIS 0x8128 +#define GL_DISTANCE_ATTENUATION_SGIS 0x8129 +typedef void (APIENTRYP PFNGLPOINTPARAMETERFSGISPROC)(GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVSGISPROC)(GLenum pname, const GLfloat * params); +#endif /* GL_SGIS_point_parameters */ + +#ifndef GL_SGIS_sharpen_texture +#define GL_SGIS_sharpen_texture 1 +#define GL_LINEAR_SHARPEN_SGIS 0x80AD +#define GL_LINEAR_SHARPEN_ALPHA_SGIS 0x80AE +#define GL_LINEAR_SHARPEN_COLOR_SGIS 0x80AF +#define GL_SHARPEN_TEXTURE_FUNC_POINTS_SGIS 0x80B0 +typedef void (APIENTRYP PFNGLSHARPENTEXFUNCSGISPROC)(GLenum target, GLsizei n, const GLfloat * points); +typedef void (APIENTRYP PFNGLGETSHARPENTEXFUNCSGISPROC)(GLenum target, GLfloat * points); +#endif /* GL_SGIS_sharpen_texture */ + +#ifndef GL_SGIS_texture4D +#define GL_SGIS_texture4D 1 +#define GL_PACK_SKIP_VOLUMES_SGIS 0x8130 +#define GL_PACK_IMAGE_DEPTH_SGIS 0x8131 +#define GL_UNPACK_SKIP_VOLUMES_SGIS 0x8132 +#define GL_UNPACK_IMAGE_DEPTH_SGIS 0x8133 +#define GL_TEXTURE_4D_SGIS 0x8134 +#define GL_PROXY_TEXTURE_4D_SGIS 0x8135 +#define GL_TEXTURE_4DSIZE_SGIS 0x8136 +#define GL_TEXTURE_WRAP_Q_SGIS 0x8137 +#define GL_MAX_4D_TEXTURE_SIZE_SGIS 0x8138 +#define GL_TEXTURE_4D_BINDING_SGIS 0x814F +typedef void (APIENTRYP PFNGLTEXIMAGE4DSGISPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLint border, GLenum format, GLenum type, const void * pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE4DSGISPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLenum format, GLenum type, const void * pixels); +#endif /* GL_SGIS_texture4D */ + +#ifndef GL_SGIS_texture_border_clamp +#define GL_SGIS_texture_border_clamp 1 +#define GL_CLAMP_TO_BORDER_SGIS 0x812D +#endif /* GL_SGIS_texture_border_clamp */ + +#ifndef GL_SGIS_texture_color_mask +#define GL_SGIS_texture_color_mask 1 +#define GL_TEXTURE_COLOR_WRITEMASK_SGIS 0x81EF +typedef void (APIENTRYP PFNGLTEXTURECOLORMASKSGISPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +#endif /* GL_SGIS_texture_color_mask */ + +#ifndef GL_SGIS_texture_edge_clamp +#define GL_SGIS_texture_edge_clamp 1 +#define GL_CLAMP_TO_EDGE_SGIS 0x812F +#endif /* GL_SGIS_texture_edge_clamp */ + +#ifndef GL_SGIS_texture_filter4 +#define GL_SGIS_texture_filter4 1 +#define GL_FILTER4_SGIS 0x8146 +#define GL_TEXTURE_FILTER4_SIZE_SGIS 0x8147 +typedef void (APIENTRYP PFNGLGETTEXFILTERFUNCSGISPROC)(GLenum target, GLenum filter, GLfloat * weights); +typedef void (APIENTRYP PFNGLTEXFILTERFUNCSGISPROC)(GLenum target, GLenum filter, GLsizei n, const GLfloat * weights); +#endif /* GL_SGIS_texture_filter4 */ + +#ifndef GL_SGIS_texture_lod +#define GL_SGIS_texture_lod 1 +#define GL_TEXTURE_MIN_LOD_SGIS 0x813A +#define GL_TEXTURE_MAX_LOD_SGIS 0x813B +#define GL_TEXTURE_BASE_LEVEL_SGIS 0x813C +#define GL_TEXTURE_MAX_LEVEL_SGIS 0x813D +#endif /* GL_SGIS_texture_lod */ + +#ifndef GL_SGIS_texture_select +#define GL_SGIS_texture_select 1 +#define GL_DUAL_ALPHA4_SGIS 0x8110 +#define GL_DUAL_ALPHA8_SGIS 0x8111 +#define GL_DUAL_ALPHA12_SGIS 0x8112 +#define GL_DUAL_ALPHA16_SGIS 0x8113 +#define GL_DUAL_LUMINANCE4_SGIS 0x8114 +#define GL_DUAL_LUMINANCE8_SGIS 0x8115 +#define GL_DUAL_LUMINANCE12_SGIS 0x8116 +#define GL_DUAL_LUMINANCE16_SGIS 0x8117 +#define GL_DUAL_INTENSITY4_SGIS 0x8118 +#define GL_DUAL_INTENSITY8_SGIS 0x8119 +#define GL_DUAL_INTENSITY12_SGIS 0x811A +#define GL_DUAL_INTENSITY16_SGIS 0x811B +#define GL_DUAL_LUMINANCE_ALPHA4_SGIS 0x811C +#define GL_DUAL_LUMINANCE_ALPHA8_SGIS 0x811D +#define GL_QUAD_ALPHA4_SGIS 0x811E +#define GL_QUAD_ALPHA8_SGIS 0x811F +#define GL_QUAD_LUMINANCE4_SGIS 0x8120 +#define GL_QUAD_LUMINANCE8_SGIS 0x8121 +#define GL_QUAD_INTENSITY4_SGIS 0x8122 +#define GL_QUAD_INTENSITY8_SGIS 0x8123 +#define GL_DUAL_TEXTURE_SELECT_SGIS 0x8124 +#define GL_QUAD_TEXTURE_SELECT_SGIS 0x8125 +#endif /* GL_SGIS_texture_select */ + +#ifndef GL_SGIX_async +#define GL_SGIX_async 1 +#define GL_ASYNC_MARKER_SGIX 0x8329 +typedef void (APIENTRYP PFNGLASYNCMARKERSGIXPROC)(GLuint marker); +typedef GLint (APIENTRYP PFNGLFINISHASYNCSGIXPROC)(GLuint * markerp); +typedef GLint (APIENTRYP PFNGLPOLLASYNCSGIXPROC)(GLuint * markerp); +typedef GLuint (APIENTRYP PFNGLGENASYNCMARKERSSGIXPROC)(GLsizei range); +typedef void (APIENTRYP PFNGLDELETEASYNCMARKERSSGIXPROC)(GLuint marker, GLsizei range); +typedef GLboolean (APIENTRYP PFNGLISASYNCMARKERSGIXPROC)(GLuint marker); +#endif /* GL_SGIX_async */ + +#ifndef GL_SGIX_async_histogram +#define GL_SGIX_async_histogram 1 +#define GL_ASYNC_HISTOGRAM_SGIX 0x832C +#define GL_MAX_ASYNC_HISTOGRAM_SGIX 0x832D +#endif /* GL_SGIX_async_histogram */ + +#ifndef GL_SGIX_async_pixel +#define GL_SGIX_async_pixel 1 +#define GL_ASYNC_TEX_IMAGE_SGIX 0x835C +#define GL_ASYNC_DRAW_PIXELS_SGIX 0x835D +#define GL_ASYNC_READ_PIXELS_SGIX 0x835E +#define GL_MAX_ASYNC_TEX_IMAGE_SGIX 0x835F +#define GL_MAX_ASYNC_DRAW_PIXELS_SGIX 0x8360 +#define GL_MAX_ASYNC_READ_PIXELS_SGIX 0x8361 +#endif /* GL_SGIX_async_pixel */ + +#ifndef GL_SGIX_blend_alpha_minmax +#define GL_SGIX_blend_alpha_minmax 1 +#define GL_ALPHA_MIN_SGIX 0x8320 +#define GL_ALPHA_MAX_SGIX 0x8321 +#endif /* GL_SGIX_blend_alpha_minmax */ + +#ifndef GL_SGIX_calligraphic_fragment +#define GL_SGIX_calligraphic_fragment 1 +#define GL_CALLIGRAPHIC_FRAGMENT_SGIX 0x8183 +#endif /* GL_SGIX_calligraphic_fragment */ + +#ifndef GL_SGIX_clipmap +#define GL_SGIX_clipmap 1 +#define GL_LINEAR_CLIPMAP_LINEAR_SGIX 0x8170 +#define GL_TEXTURE_CLIPMAP_CENTER_SGIX 0x8171 +#define GL_TEXTURE_CLIPMAP_FRAME_SGIX 0x8172 +#define GL_TEXTURE_CLIPMAP_OFFSET_SGIX 0x8173 +#define GL_TEXTURE_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8174 +#define GL_TEXTURE_CLIPMAP_LOD_OFFSET_SGIX 0x8175 +#define GL_TEXTURE_CLIPMAP_DEPTH_SGIX 0x8176 +#define GL_MAX_CLIPMAP_DEPTH_SGIX 0x8177 +#define GL_MAX_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8178 +#define GL_NEAREST_CLIPMAP_NEAREST_SGIX 0x844D +#define GL_NEAREST_CLIPMAP_LINEAR_SGIX 0x844E +#define GL_LINEAR_CLIPMAP_NEAREST_SGIX 0x844F +#endif /* GL_SGIX_clipmap */ + +#ifndef GL_SGIX_convolution_accuracy +#define GL_SGIX_convolution_accuracy 1 +#define GL_CONVOLUTION_HINT_SGIX 0x8316 +#endif /* GL_SGIX_convolution_accuracy */ + +#ifndef GL_SGIX_depth_pass_instrument +#define GL_SGIX_depth_pass_instrument 1 +#endif /* GL_SGIX_depth_pass_instrument */ + +#ifndef GL_SGIX_depth_texture +#define GL_SGIX_depth_texture 1 +#define GL_DEPTH_COMPONENT16_SGIX 0x81A5 +#define GL_DEPTH_COMPONENT24_SGIX 0x81A6 +#define GL_DEPTH_COMPONENT32_SGIX 0x81A7 +#endif /* GL_SGIX_depth_texture */ + +#ifndef GL_SGIX_flush_raster +#define GL_SGIX_flush_raster 1 +typedef void (APIENTRYP PFNGLFLUSHRASTERSGIXPROC)(void); +#endif /* GL_SGIX_flush_raster */ + +#ifndef GL_SGIX_fog_offset +#define GL_SGIX_fog_offset 1 +#define GL_FOG_OFFSET_SGIX 0x8198 +#define GL_FOG_OFFSET_VALUE_SGIX 0x8199 +#endif /* GL_SGIX_fog_offset */ + +#ifndef GL_SGIX_fragment_lighting +#define GL_SGIX_fragment_lighting 1 +#define GL_FRAGMENT_LIGHTING_SGIX 0x8400 +#define GL_FRAGMENT_COLOR_MATERIAL_SGIX 0x8401 +#define GL_FRAGMENT_COLOR_MATERIAL_FACE_SGIX 0x8402 +#define GL_FRAGMENT_COLOR_MATERIAL_PARAMETER_SGIX 0x8403 +#define GL_MAX_FRAGMENT_LIGHTS_SGIX 0x8404 +#define GL_MAX_ACTIVE_LIGHTS_SGIX 0x8405 +#define GL_CURRENT_RASTER_NORMAL_SGIX 0x8406 +#define GL_LIGHT_ENV_MODE_SGIX 0x8407 +#define GL_FRAGMENT_LIGHT_MODEL_LOCAL_VIEWER_SGIX 0x8408 +#define GL_FRAGMENT_LIGHT_MODEL_TWO_SIDE_SGIX 0x8409 +#define GL_FRAGMENT_LIGHT_MODEL_AMBIENT_SGIX 0x840A +#define GL_FRAGMENT_LIGHT_MODEL_NORMAL_INTERPOLATION_SGIX 0x840B +#define GL_FRAGMENT_LIGHT0_SGIX 0x840C +#define GL_FRAGMENT_LIGHT1_SGIX 0x840D +#define GL_FRAGMENT_LIGHT2_SGIX 0x840E +#define GL_FRAGMENT_LIGHT3_SGIX 0x840F +#define GL_FRAGMENT_LIGHT4_SGIX 0x8410 +#define GL_FRAGMENT_LIGHT5_SGIX 0x8411 +#define GL_FRAGMENT_LIGHT6_SGIX 0x8412 +#define GL_FRAGMENT_LIGHT7_SGIX 0x8413 +typedef void (APIENTRYP PFNGLFRAGMENTCOLORMATERIALSGIXPROC)(GLenum face, GLenum mode); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFSGIXPROC)(GLenum light, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFVSGIXPROC)(GLenum light, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTISGIXPROC)(GLenum light, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTIVSGIXPROC)(GLenum light, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFSGIXPROC)(GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFVSGIXPROC)(GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELISGIXPROC)(GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELIVSGIXPROC)(GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFSGIXPROC)(GLenum face, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFVSGIXPROC)(GLenum face, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALISGIXPROC)(GLenum face, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALIVSGIXPROC)(GLenum face, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTFVSGIXPROC)(GLenum light, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTIVSGIXPROC)(GLenum light, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALFVSGIXPROC)(GLenum face, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALIVSGIXPROC)(GLenum face, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLLIGHTENVISGIXPROC)(GLenum pname, GLint param); +#endif /* GL_SGIX_fragment_lighting */ + +#ifndef GL_SGIX_framezoom +#define GL_SGIX_framezoom 1 +#define GL_FRAMEZOOM_SGIX 0x818B +#define GL_FRAMEZOOM_FACTOR_SGIX 0x818C +#define GL_MAX_FRAMEZOOM_FACTOR_SGIX 0x818D +typedef void (APIENTRYP PFNGLFRAMEZOOMSGIXPROC)(GLint factor); +#endif /* GL_SGIX_framezoom */ + +#ifndef GL_SGIX_igloo_interface +#define GL_SGIX_igloo_interface 1 +typedef void (APIENTRYP PFNGLIGLOOINTERFACESGIXPROC)(GLenum pname, const void * params); +#endif /* GL_SGIX_igloo_interface */ + +#ifndef GL_SGIX_instruments +#define GL_SGIX_instruments 1 +#define GL_INSTRUMENT_BUFFER_POINTER_SGIX 0x8180 +#define GL_INSTRUMENT_MEASUREMENTS_SGIX 0x8181 +typedef GLint (APIENTRYP PFNGLGETINSTRUMENTSSGIXPROC)(void); +typedef void (APIENTRYP PFNGLINSTRUMENTSBUFFERSGIXPROC)(GLsizei size, GLint * buffer); +typedef GLint (APIENTRYP PFNGLPOLLINSTRUMENTSSGIXPROC)(GLint * marker_p); +typedef void (APIENTRYP PFNGLREADINSTRUMENTSSGIXPROC)(GLint marker); +typedef void (APIENTRYP PFNGLSTARTINSTRUMENTSSGIXPROC)(void); +typedef void (APIENTRYP PFNGLSTOPINSTRUMENTSSGIXPROC)(GLint marker); +#endif /* GL_SGIX_instruments */ + +#ifndef GL_SGIX_interlace +#define GL_SGIX_interlace 1 +#define GL_INTERLACE_SGIX 0x8094 +#endif /* GL_SGIX_interlace */ + +#ifndef GL_SGIX_ir_instrument1 +#define GL_SGIX_ir_instrument1 1 +#define GL_IR_INSTRUMENT1_SGIX 0x817F +#endif /* GL_SGIX_ir_instrument1 */ + +#ifndef GL_SGIX_list_priority +#define GL_SGIX_list_priority 1 +#define GL_LIST_PRIORITY_SGIX 0x8182 +typedef void (APIENTRYP PFNGLGETLISTPARAMETERFVSGIXPROC)(GLuint list, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETLISTPARAMETERIVSGIXPROC)(GLuint list, GLenum pname, GLint * params); +typedef void (APIENTRYP PFNGLLISTPARAMETERFSGIXPROC)(GLuint list, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLLISTPARAMETERFVSGIXPROC)(GLuint list, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLLISTPARAMETERISGIXPROC)(GLuint list, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLLISTPARAMETERIVSGIXPROC)(GLuint list, GLenum pname, const GLint * params); +#endif /* GL_SGIX_list_priority */ + +#ifndef GL_SGIX_pixel_texture +#define GL_SGIX_pixel_texture 1 +#define GL_PIXEL_TEX_GEN_SGIX 0x8139 +#define GL_PIXEL_TEX_GEN_MODE_SGIX 0x832B +typedef void (APIENTRYP PFNGLPIXELTEXGENSGIXPROC)(GLenum mode); +#endif /* GL_SGIX_pixel_texture */ + +#ifndef GL_SGIX_pixel_tiles +#define GL_SGIX_pixel_tiles 1 +#define GL_PIXEL_TILE_BEST_ALIGNMENT_SGIX 0x813E +#define GL_PIXEL_TILE_CACHE_INCREMENT_SGIX 0x813F +#define GL_PIXEL_TILE_WIDTH_SGIX 0x8140 +#define GL_PIXEL_TILE_HEIGHT_SGIX 0x8141 +#define GL_PIXEL_TILE_GRID_WIDTH_SGIX 0x8142 +#define GL_PIXEL_TILE_GRID_HEIGHT_SGIX 0x8143 +#define GL_PIXEL_TILE_GRID_DEPTH_SGIX 0x8144 +#define GL_PIXEL_TILE_CACHE_SIZE_SGIX 0x8145 +#endif /* GL_SGIX_pixel_tiles */ + +#ifndef GL_SGIX_polynomial_ffd +#define GL_SGIX_polynomial_ffd 1 +#define GL_TEXTURE_DEFORMATION_BIT_SGIX 0x00000001 +#define GL_GEOMETRY_DEFORMATION_BIT_SGIX 0x00000002 +#define GL_GEOMETRY_DEFORMATION_SGIX 0x8194 +#define GL_TEXTURE_DEFORMATION_SGIX 0x8195 +#define GL_DEFORMATIONS_MASK_SGIX 0x8196 +#define GL_MAX_DEFORMATION_ORDER_SGIX 0x8197 +typedef void (APIENTRYP PFNGLDEFORMATIONMAP3DSGIXPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, GLdouble w1, GLdouble w2, GLint wstride, GLint worder, const GLdouble * points); +typedef void (APIENTRYP PFNGLDEFORMATIONMAP3FSGIXPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, GLfloat w1, GLfloat w2, GLint wstride, GLint worder, const GLfloat * points); +typedef void (APIENTRYP PFNGLDEFORMSGIXPROC)(GLbitfield mask); +typedef void (APIENTRYP PFNGLLOADIDENTITYDEFORMATIONMAPSGIXPROC)(GLbitfield mask); +#endif /* GL_SGIX_polynomial_ffd */ + +#ifndef GL_SGIX_reference_plane +#define GL_SGIX_reference_plane 1 +#define GL_REFERENCE_PLANE_SGIX 0x817D +#define GL_REFERENCE_PLANE_EQUATION_SGIX 0x817E +typedef void (APIENTRYP PFNGLREFERENCEPLANESGIXPROC)(const GLdouble * equation); +#endif /* GL_SGIX_reference_plane */ + +#ifndef GL_SGIX_resample +#define GL_SGIX_resample 1 +#define GL_PACK_RESAMPLE_SGIX 0x842E +#define GL_UNPACK_RESAMPLE_SGIX 0x842F +#define GL_RESAMPLE_REPLICATE_SGIX 0x8433 +#define GL_RESAMPLE_ZERO_FILL_SGIX 0x8434 +#define GL_RESAMPLE_DECIMATE_SGIX 0x8430 +#endif /* GL_SGIX_resample */ + +#ifndef GL_SGIX_scalebias_hint +#define GL_SGIX_scalebias_hint 1 +#define GL_SCALEBIAS_HINT_SGIX 0x8322 +#endif /* GL_SGIX_scalebias_hint */ + +#ifndef GL_SGIX_shadow +#define GL_SGIX_shadow 1 +#define GL_TEXTURE_COMPARE_SGIX 0x819A +#define GL_TEXTURE_COMPARE_OPERATOR_SGIX 0x819B +#define GL_TEXTURE_LEQUAL_R_SGIX 0x819C +#define GL_TEXTURE_GEQUAL_R_SGIX 0x819D +#endif /* GL_SGIX_shadow */ + +#ifndef GL_SGIX_shadow_ambient +#define GL_SGIX_shadow_ambient 1 +#define GL_SHADOW_AMBIENT_SGIX 0x80BF +#endif /* GL_SGIX_shadow_ambient */ + +#ifndef GL_SGIX_sprite +#define GL_SGIX_sprite 1 +#define GL_SPRITE_SGIX 0x8148 +#define GL_SPRITE_MODE_SGIX 0x8149 +#define GL_SPRITE_AXIS_SGIX 0x814A +#define GL_SPRITE_TRANSLATION_SGIX 0x814B +#define GL_SPRITE_AXIAL_SGIX 0x814C +#define GL_SPRITE_OBJECT_ALIGNED_SGIX 0x814D +#define GL_SPRITE_EYE_ALIGNED_SGIX 0x814E +typedef void (APIENTRYP PFNGLSPRITEPARAMETERFSGIXPROC)(GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERFVSGIXPROC)(GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERISGIXPROC)(GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERIVSGIXPROC)(GLenum pname, const GLint * params); +#endif /* GL_SGIX_sprite */ + +#ifndef GL_SGIX_subsample +#define GL_SGIX_subsample 1 +#define GL_PACK_SUBSAMPLE_RATE_SGIX 0x85A0 +#define GL_UNPACK_SUBSAMPLE_RATE_SGIX 0x85A1 +#define GL_PIXEL_SUBSAMPLE_4444_SGIX 0x85A2 +#define GL_PIXEL_SUBSAMPLE_2424_SGIX 0x85A3 +#define GL_PIXEL_SUBSAMPLE_4242_SGIX 0x85A4 +#endif /* GL_SGIX_subsample */ + +#ifndef GL_SGIX_tag_sample_buffer +#define GL_SGIX_tag_sample_buffer 1 +typedef void (APIENTRYP PFNGLTAGSAMPLEBUFFERSGIXPROC)(void); +#endif /* GL_SGIX_tag_sample_buffer */ + +#ifndef GL_SGIX_texture_add_env +#define GL_SGIX_texture_add_env 1 +#define GL_TEXTURE_ENV_BIAS_SGIX 0x80BE +#endif /* GL_SGIX_texture_add_env */ + +#ifndef GL_SGIX_texture_coordinate_clamp +#define GL_SGIX_texture_coordinate_clamp 1 +#define GL_TEXTURE_MAX_CLAMP_S_SGIX 0x8369 +#define GL_TEXTURE_MAX_CLAMP_T_SGIX 0x836A +#define GL_TEXTURE_MAX_CLAMP_R_SGIX 0x836B +#endif /* GL_SGIX_texture_coordinate_clamp */ + +#ifndef GL_SGIX_texture_lod_bias +#define GL_SGIX_texture_lod_bias 1 +#define GL_TEXTURE_LOD_BIAS_S_SGIX 0x818E +#define GL_TEXTURE_LOD_BIAS_T_SGIX 0x818F +#define GL_TEXTURE_LOD_BIAS_R_SGIX 0x8190 +#endif /* GL_SGIX_texture_lod_bias */ + +#ifndef GL_SGIX_texture_multi_buffer +#define GL_SGIX_texture_multi_buffer 1 +#define GL_TEXTURE_MULTI_BUFFER_HINT_SGIX 0x812E +#endif /* GL_SGIX_texture_multi_buffer */ + +#ifndef GL_SGIX_texture_scale_bias +#define GL_SGIX_texture_scale_bias 1 +#define GL_POST_TEXTURE_FILTER_BIAS_SGIX 0x8179 +#define GL_POST_TEXTURE_FILTER_SCALE_SGIX 0x817A +#define GL_POST_TEXTURE_FILTER_BIAS_RANGE_SGIX 0x817B +#define GL_POST_TEXTURE_FILTER_SCALE_RANGE_SGIX 0x817C +#endif /* GL_SGIX_texture_scale_bias */ + +#ifndef GL_SGIX_vertex_preclip +#define GL_SGIX_vertex_preclip 1 +#define GL_VERTEX_PRECLIP_SGIX 0x83EE +#define GL_VERTEX_PRECLIP_HINT_SGIX 0x83EF +#endif /* GL_SGIX_vertex_preclip */ + +#ifndef GL_SGIX_ycrcb +#define GL_SGIX_ycrcb 1 +#define GL_YCRCB_422_SGIX 0x81BB +#define GL_YCRCB_444_SGIX 0x81BC +#endif /* GL_SGIX_ycrcb */ + +#ifndef GL_SGIX_ycrcb_subsample +#define GL_SGIX_ycrcb_subsample 1 +#endif /* GL_SGIX_ycrcb_subsample */ + +#ifndef GL_SGIX_ycrcba +#define GL_SGIX_ycrcba 1 +#define GL_YCRCB_SGIX 0x8318 +#define GL_YCRCBA_SGIX 0x8319 +#endif /* GL_SGIX_ycrcba */ + +#ifndef GL_SGI_color_matrix +#define GL_SGI_color_matrix 1 +#define GL_COLOR_MATRIX_SGI 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE_SGI 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE_SGI 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE_SGI 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE_SGI 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS_SGI 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS_SGI 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS_SGI 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS_SGI 0x80BB +#endif /* GL_SGI_color_matrix */ + +#ifndef GL_SGI_color_table +#define GL_SGI_color_table 1 +#define GL_COLOR_TABLE_SGI 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D2 +#define GL_PROXY_COLOR_TABLE_SGI 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D5 +#define GL_COLOR_TABLE_SCALE_SGI 0x80D6 +#define GL_COLOR_TABLE_BIAS_SGI 0x80D7 +#define GL_COLOR_TABLE_FORMAT_SGI 0x80D8 +#define GL_COLOR_TABLE_WIDTH_SGI 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE_SGI 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE_SGI 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE_SGI 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE_SGI 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE_SGI 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE_SGI 0x80DF +typedef void (APIENTRYP PFNGLCOLORTABLESGIPROC)(GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void * table); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVSGIPROC)(GLenum target, GLenum pname, const GLfloat * params); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVSGIPROC)(GLenum target, GLenum pname, const GLint * params); +typedef void (APIENTRYP PFNGLCOPYCOLORTABLESGIPROC)(GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLGETCOLORTABLESGIPROC)(GLenum target, GLenum format, GLenum type, void * table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVSGIPROC)(GLenum target, GLenum pname, GLfloat * params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVSGIPROC)(GLenum target, GLenum pname, GLint * params); +#endif /* GL_SGI_color_table */ + +#ifndef GL_SGI_texture_color_table +#define GL_SGI_texture_color_table 1 +#define GL_TEXTURE_COLOR_TABLE_SGI 0x80BC +#define GL_PROXY_TEXTURE_COLOR_TABLE_SGI 0x80BD +#endif /* GL_SGI_texture_color_table */ + +#ifndef GL_SUNX_constant_data +#define GL_SUNX_constant_data 1 +#define GL_UNPACK_CONSTANT_DATA_SUNX 0x81D5 +#define GL_TEXTURE_CONSTANT_DATA_SUNX 0x81D6 +typedef void (APIENTRYP PFNGLFINISHTEXTURESUNXPROC)(void); +#endif /* GL_SUNX_constant_data */ + +#ifndef GL_SUN_convolution_border_modes +#define GL_SUN_convolution_border_modes 1 +#define GL_WRAP_BORDER_SUN 0x81D4 +#endif /* GL_SUN_convolution_border_modes */ + +#ifndef GL_SUN_global_alpha +#define GL_SUN_global_alpha 1 +#define GL_GLOBAL_ALPHA_SUN 0x81D9 +#define GL_GLOBAL_ALPHA_FACTOR_SUN 0x81DA +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORBSUNPROC)(GLbyte factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORSSUNPROC)(GLshort factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORISUNPROC)(GLint factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORFSUNPROC)(GLfloat factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORDSUNPROC)(GLdouble factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUBSUNPROC)(GLubyte factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUSSUNPROC)(GLushort factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUISUNPROC)(GLuint factor); +#endif /* GL_SUN_global_alpha */ + +#ifndef GL_SUN_mesh_array +#define GL_SUN_mesh_array 1 +#define GL_QUAD_MESH_SUN 0x8614 +#define GL_TRIANGLE_MESH_SUN 0x8615 +typedef void (APIENTRYP PFNGLDRAWMESHARRAYSSUNPROC)(GLenum mode, GLint first, GLsizei count, GLsizei width); +#endif /* GL_SUN_mesh_array */ + +#ifndef GL_SUN_slice_accum +#define GL_SUN_slice_accum 1 +#define GL_SLICE_ACCUM_SUN 0x85CC +#endif /* GL_SUN_slice_accum */ + +#ifndef GL_SUN_triangle_list +#define GL_SUN_triangle_list 1 +#define GL_RESTART_SUN 0x0001 +#define GL_REPLACE_MIDDLE_SUN 0x0002 +#define GL_REPLACE_OLDEST_SUN 0x0003 +#define GL_TRIANGLE_LIST_SUN 0x81D7 +#define GL_REPLACEMENT_CODE_SUN 0x81D8 +#define GL_REPLACEMENT_CODE_ARRAY_SUN 0x85C0 +#define GL_REPLACEMENT_CODE_ARRAY_TYPE_SUN 0x85C1 +#define GL_REPLACEMENT_CODE_ARRAY_STRIDE_SUN 0x85C2 +#define GL_REPLACEMENT_CODE_ARRAY_POINTER_SUN 0x85C3 +#define GL_R1UI_V3F_SUN 0x85C4 +#define GL_R1UI_C4UB_V3F_SUN 0x85C5 +#define GL_R1UI_C3F_V3F_SUN 0x85C6 +#define GL_R1UI_N3F_V3F_SUN 0x85C7 +#define GL_R1UI_C4F_N3F_V3F_SUN 0x85C8 +#define GL_R1UI_T2F_V3F_SUN 0x85C9 +#define GL_R1UI_T2F_N3F_V3F_SUN 0x85CA +#define GL_R1UI_T2F_C4F_N3F_V3F_SUN 0x85CB +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUISUNPROC)(GLuint code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSSUNPROC)(GLushort code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBSUNPROC)(GLubyte code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVSUNPROC)(const GLuint * code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSVSUNPROC)(const GLushort * code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBVSUNPROC)(const GLubyte * code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEPOINTERSUNPROC)(GLenum type, GLsizei stride, const void ** pointer); +#endif /* GL_SUN_triangle_list */ + +#ifndef GL_SUN_vertex +#define GL_SUN_vertex 1 +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FSUNPROC)(GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FVSUNPROC)(const GLubyte * c, const GLfloat * v); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FSUNPROC)(GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FVSUNPROC)(const GLubyte * c, const GLfloat * v); +typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FSUNPROC)(GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FVSUNPROC)(const GLfloat * c, const GLfloat * v); +typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FSUNPROC)(GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FVSUNPROC)(const GLfloat * n, const GLfloat * v); +typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FSUNPROC)(GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FVSUNPROC)(const GLfloat * c, const GLfloat * n, const GLfloat * v); +typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FSUNPROC)(GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FVSUNPROC)(const GLfloat * tc, const GLfloat * v); +typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FSUNPROC)(GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FVSUNPROC)(const GLfloat * tc, const GLfloat * v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FSUNPROC)(GLfloat s, GLfloat t, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FVSUNPROC)(const GLfloat * tc, const GLubyte * c, const GLfloat * v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FSUNPROC)(GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FVSUNPROC)(const GLfloat * tc, const GLfloat * c, const GLfloat * v); +typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FSUNPROC)(GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FVSUNPROC)(const GLfloat * tc, const GLfloat * n, const GLfloat * v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC)(GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC)(const GLfloat * tc, const GLfloat * c, const GLfloat * n, const GLfloat * v); +typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FSUNPROC)(GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FVSUNPROC)(const GLfloat * tc, const GLfloat * c, const GLfloat * n, const GLfloat * v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FSUNPROC)(GLuint rc, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FVSUNPROC)(const GLuint * rc, const GLfloat * v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FSUNPROC)(GLuint rc, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FVSUNPROC)(const GLuint * rc, const GLubyte * c, const GLfloat * v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FSUNPROC)(GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FVSUNPROC)(const GLuint * rc, const GLfloat * c, const GLfloat * v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FSUNPROC)(GLuint rc, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FVSUNPROC)(const GLuint * rc, const GLfloat * n, const GLfloat * v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FSUNPROC)(GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FVSUNPROC)(const GLuint * rc, const GLfloat * c, const GLfloat * n, const GLfloat * v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FSUNPROC)(GLuint rc, GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FVSUNPROC)(const GLuint * rc, const GLfloat * tc, const GLfloat * v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FSUNPROC)(GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FVSUNPROC)(const GLuint * rc, const GLfloat * tc, const GLfloat * n, const GLfloat * v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC)(GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC)(const GLuint * rc, const GLfloat * tc, const GLfloat * c, const GLfloat * n, const GLfloat * v); +#endif /* GL_SUN_vertex */ + +#ifndef GL_WIN_phong_shading +#define GL_WIN_phong_shading 1 +#define GL_PHONG_WIN 0x80EA +#define GL_PHONG_HINT_WIN 0x80EB +#endif /* GL_WIN_phong_shading */ + +#ifndef GL_WIN_specular_fog +#define GL_WIN_specular_fog 1 +#define GL_FOG_SPECULAR_TEXTURE_WIN 0x80EC +#endif /* GL_WIN_specular_fog */ + +#ifndef GL_EXT_texture_shadow_lod +#define GL_EXT_texture_shadow_lod 1 +#endif /* GL_EXT_texture_shadow_lod */ + +#if defined(GLBIND_WGL) +#ifndef WGL_3DFX_multisample +#define WGL_3DFX_multisample 1 +#define WGL_SAMPLE_BUFFERS_3DFX 0x2060 +#define WGL_SAMPLES_3DFX 0x2061 +#endif /* WGL_3DFX_multisample */ + +#ifndef WGL_3DL_stereo_control +#define WGL_3DL_stereo_control 1 +#define WGL_STEREO_EMITTER_ENABLE_3DL 0x2055 +#define WGL_STEREO_EMITTER_DISABLE_3DL 0x2056 +#define WGL_STEREO_POLARITY_NORMAL_3DL 0x2057 +#define WGL_STEREO_POLARITY_INVERT_3DL 0x2058 +typedef BOOL (APIENTRYP PFNWGLSETSTEREOEMITTERSTATE3DLPROC)(HDC hDC, UINT uState); +#endif /* WGL_3DL_stereo_control */ + +#ifndef WGL_AMD_gpu_association +#define WGL_AMD_gpu_association 1 +#define WGL_GPU_VENDOR_AMD 0x1F00 +#define WGL_GPU_RENDERER_STRING_AMD 0x1F01 +#define WGL_GPU_OPENGL_VERSION_STRING_AMD 0x1F02 +#define WGL_GPU_FASTEST_TARGET_GPUS_AMD 0x21A2 +#define WGL_GPU_RAM_AMD 0x21A3 +#define WGL_GPU_CLOCK_AMD 0x21A4 +#define WGL_GPU_NUM_PIPES_AMD 0x21A5 +#define WGL_GPU_NUM_SIMD_AMD 0x21A6 +#define WGL_GPU_NUM_RB_AMD 0x21A7 +#define WGL_GPU_NUM_SPI_AMD 0x21A8 +typedef UINT (APIENTRYP PFNWGLGETGPUIDSAMDPROC)(UINT maxCount, UINT * ids); +typedef INT (APIENTRYP PFNWGLGETGPUINFOAMDPROC)(UINT id, INT property, GLenum dataType, UINT size, void * data); +typedef UINT (APIENTRYP PFNWGLGETCONTEXTGPUIDAMDPROC)(HGLRC hglrc); +typedef HGLRC (APIENTRYP PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC)(UINT id); +typedef HGLRC (APIENTRYP PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC)(UINT id, HGLRC hShareContext, const int * attribList); +typedef BOOL (APIENTRYP PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC)(HGLRC hglrc); +typedef BOOL (APIENTRYP PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC)(HGLRC hglrc); +typedef HGLRC (APIENTRYP PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC)(void); +typedef VOID (APIENTRYP PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC)(HGLRC dstCtx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif /* WGL_AMD_gpu_association */ + +#ifndef WGL_ARB_buffer_region +#define WGL_ARB_buffer_region 1 +#define WGL_FRONT_COLOR_BUFFER_BIT_ARB 0x00000001 +#define WGL_BACK_COLOR_BUFFER_BIT_ARB 0x00000002 +#define WGL_DEPTH_BUFFER_BIT_ARB 0x00000004 +#define WGL_STENCIL_BUFFER_BIT_ARB 0x00000008 +typedef HANDLE (APIENTRYP PFNWGLCREATEBUFFERREGIONARBPROC)(HDC hDC, int iLayerPlane, UINT uType); +typedef VOID (APIENTRYP PFNWGLDELETEBUFFERREGIONARBPROC)(HANDLE hRegion); +typedef BOOL (APIENTRYP PFNWGLSAVEBUFFERREGIONARBPROC)(HANDLE hRegion, int x, int y, int width, int height); +typedef BOOL (APIENTRYP PFNWGLRESTOREBUFFERREGIONARBPROC)(HANDLE hRegion, int x, int y, int width, int height, int xSrc, int ySrc); +#endif /* WGL_ARB_buffer_region */ + +#ifndef WGL_ARB_context_flush_control +#define WGL_ARB_context_flush_control 1 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 +#endif /* WGL_ARB_context_flush_control */ + +#ifndef WGL_ARB_create_context +#define WGL_ARB_create_context 1 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define ERROR_INVALID_VERSION_ARB 0x2095 +typedef HGLRC (APIENTRYP PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShareContext, const int * attribList); +#endif /* WGL_ARB_create_context */ + +#ifndef WGL_ARB_create_context_no_error +#define WGL_ARB_create_context_no_error 1 +#define WGL_CONTEXT_OPENGL_NO_ERROR_ARB 0x31B3 +#endif /* WGL_ARB_create_context_no_error */ + +#ifndef WGL_ARB_create_context_profile +#define WGL_ARB_create_context_profile 1 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define ERROR_INVALID_PROFILE_ARB 0x2096 +#endif /* WGL_ARB_create_context_profile */ + +#ifndef WGL_ARB_create_context_robustness +#define WGL_ARB_create_context_robustness 1 +#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define WGL_NO_RESET_NOTIFICATION_ARB 0x8261 +#endif /* WGL_ARB_create_context_robustness */ + +#ifndef WGL_ARB_extensions_string +#define WGL_ARB_extensions_string 1 +typedef const char * (APIENTRYP PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC hdc); +#endif /* WGL_ARB_extensions_string */ + +#ifndef WGL_ARB_framebuffer_sRGB +#define WGL_ARB_framebuffer_sRGB 1 +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20A9 +#endif /* WGL_ARB_framebuffer_sRGB */ + +#ifndef WGL_ARB_make_current_read +#define WGL_ARB_make_current_read 1 +#define ERROR_INVALID_PIXEL_TYPE_ARB 0x2043 +#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 +typedef BOOL (APIENTRYP PFNWGLMAKECONTEXTCURRENTARBPROC)(HDC hDrawDC, HDC hReadDC, HGLRC hglrc); +typedef HDC (APIENTRYP PFNWGLGETCURRENTREADDCARBPROC)(void); +#endif /* WGL_ARB_make_current_read */ + +#ifndef WGL_ARB_multisample +#define WGL_ARB_multisample 1 +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_SAMPLES_ARB 0x2042 +#endif /* WGL_ARB_multisample */ + +#ifndef WGL_ARB_pbuffer +#define WGL_ARB_pbuffer 1 +DECLARE_HANDLE(HPBUFFERARB); +#define WGL_DRAW_TO_PBUFFER_ARB 0x202D +#define WGL_MAX_PBUFFER_PIXELS_ARB 0x202E +#define WGL_MAX_PBUFFER_WIDTH_ARB 0x202F +#define WGL_MAX_PBUFFER_HEIGHT_ARB 0x2030 +#define WGL_PBUFFER_LARGEST_ARB 0x2033 +#define WGL_PBUFFER_WIDTH_ARB 0x2034 +#define WGL_PBUFFER_HEIGHT_ARB 0x2035 +#define WGL_PBUFFER_LOST_ARB 0x2036 +typedef HPBUFFERARB (APIENTRYP PFNWGLCREATEPBUFFERARBPROC)(HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int * piAttribList); +typedef HDC (APIENTRYP PFNWGLGETPBUFFERDCARBPROC)(HPBUFFERARB hPbuffer); +typedef int (APIENTRYP PFNWGLRELEASEPBUFFERDCARBPROC)(HPBUFFERARB hPbuffer, HDC hDC); +typedef BOOL (APIENTRYP PFNWGLDESTROYPBUFFERARBPROC)(HPBUFFERARB hPbuffer); +typedef BOOL (APIENTRYP PFNWGLQUERYPBUFFERARBPROC)(HPBUFFERARB hPbuffer, int iAttribute, int * piValue); +#endif /* WGL_ARB_pbuffer */ + +#ifndef WGL_ARB_pixel_format +#define WGL_ARB_pixel_format 1 +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_DRAW_TO_BITMAP_ARB 0x2002 +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NEED_PALETTE_ARB 0x2004 +#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005 +#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006 +#define WGL_SWAP_METHOD_ARB 0x2007 +#define WGL_NUMBER_OVERLAYS_ARB 0x2008 +#define WGL_NUMBER_UNDERLAYS_ARB 0x2009 +#define WGL_TRANSPARENT_ARB 0x200A +#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037 +#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038 +#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039 +#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A +#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B +#define WGL_SHARE_DEPTH_ARB 0x200C +#define WGL_SHARE_STENCIL_ARB 0x200D +#define WGL_SHARE_ACCUM_ARB 0x200E +#define WGL_SUPPORT_GDI_ARB 0x200F +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_STEREO_ARB 0x2012 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_RED_SHIFT_ARB 0x2016 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_GREEN_SHIFT_ARB 0x2018 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_BLUE_SHIFT_ARB 0x201A +#define WGL_ALPHA_BITS_ARB 0x201B +#define WGL_ALPHA_SHIFT_ARB 0x201C +#define WGL_ACCUM_BITS_ARB 0x201D +#define WGL_ACCUM_RED_BITS_ARB 0x201E +#define WGL_ACCUM_GREEN_BITS_ARB 0x201F +#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 +#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_AUX_BUFFERS_ARB 0x2024 +#define WGL_NO_ACCELERATION_ARB 0x2025 +#define WGL_GENERIC_ACCELERATION_ARB 0x2026 +#define WGL_FULL_ACCELERATION_ARB 0x2027 +#define WGL_SWAP_EXCHANGE_ARB 0x2028 +#define WGL_SWAP_COPY_ARB 0x2029 +#define WGL_SWAP_UNDEFINED_ARB 0x202A +#define WGL_TYPE_RGBA_ARB 0x202B +#define WGL_TYPE_COLORINDEX_ARB 0x202C +typedef BOOL (APIENTRYP PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int * piAttributes, int * piValues); +typedef BOOL (APIENTRYP PFNWGLGETPIXELFORMATATTRIBFVARBPROC)(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int * piAttributes, FLOAT * pfValues); +typedef BOOL (APIENTRYP PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int * piAttribIList, const FLOAT * pfAttribFList, UINT nMaxFormats, int * piFormats, UINT * nNumFormats); +#endif /* WGL_ARB_pixel_format */ + +#ifndef WGL_ARB_pixel_format_float +#define WGL_ARB_pixel_format_float 1 +#define WGL_TYPE_RGBA_FLOAT_ARB 0x21A0 +#endif /* WGL_ARB_pixel_format_float */ + +#ifndef WGL_ARB_render_texture +#define WGL_ARB_render_texture 1 +#define WGL_BIND_TO_TEXTURE_RGB_ARB 0x2070 +#define WGL_BIND_TO_TEXTURE_RGBA_ARB 0x2071 +#define WGL_TEXTURE_FORMAT_ARB 0x2072 +#define WGL_TEXTURE_TARGET_ARB 0x2073 +#define WGL_MIPMAP_TEXTURE_ARB 0x2074 +#define WGL_TEXTURE_RGB_ARB 0x2075 +#define WGL_TEXTURE_RGBA_ARB 0x2076 +#define WGL_NO_TEXTURE_ARB 0x2077 +#define WGL_TEXTURE_CUBE_MAP_ARB 0x2078 +#define WGL_TEXTURE_1D_ARB 0x2079 +#define WGL_TEXTURE_2D_ARB 0x207A +#define WGL_MIPMAP_LEVEL_ARB 0x207B +#define WGL_CUBE_MAP_FACE_ARB 0x207C +#define WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x207D +#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x207E +#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x207F +#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x2080 +#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x2081 +#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x2082 +#define WGL_FRONT_LEFT_ARB 0x2083 +#define WGL_FRONT_RIGHT_ARB 0x2084 +#define WGL_BACK_LEFT_ARB 0x2085 +#define WGL_BACK_RIGHT_ARB 0x2086 +#define WGL_AUX0_ARB 0x2087 +#define WGL_AUX1_ARB 0x2088 +#define WGL_AUX2_ARB 0x2089 +#define WGL_AUX3_ARB 0x208A +#define WGL_AUX4_ARB 0x208B +#define WGL_AUX5_ARB 0x208C +#define WGL_AUX6_ARB 0x208D +#define WGL_AUX7_ARB 0x208E +#define WGL_AUX8_ARB 0x208F +#define WGL_AUX9_ARB 0x2090 +typedef BOOL (APIENTRYP PFNWGLBINDTEXIMAGEARBPROC)(HPBUFFERARB hPbuffer, int iBuffer); +typedef BOOL (APIENTRYP PFNWGLRELEASETEXIMAGEARBPROC)(HPBUFFERARB hPbuffer, int iBuffer); +typedef BOOL (APIENTRYP PFNWGLSETPBUFFERATTRIBARBPROC)(HPBUFFERARB hPbuffer, const int * piAttribList); +#endif /* WGL_ARB_render_texture */ + +#ifndef WGL_ARB_robustness_application_isolation +#define WGL_ARB_robustness_application_isolation 1 +#define WGL_CONTEXT_RESET_ISOLATION_BIT_ARB 0x00000008 +#endif /* WGL_ARB_robustness_application_isolation */ + +#ifndef WGL_ARB_robustness_share_group_isolation +#define WGL_ARB_robustness_share_group_isolation 1 +#define WGL_CONTEXT_RESET_ISOLATION_BIT_ARB 0x00000008 +#endif /* WGL_ARB_robustness_share_group_isolation */ + +#ifndef WGL_ATI_pixel_format_float +#define WGL_ATI_pixel_format_float 1 +#define WGL_TYPE_RGBA_FLOAT_ATI 0x21A0 +#endif /* WGL_ATI_pixel_format_float */ + +#ifndef WGL_ATI_render_texture_rectangle +#define WGL_ATI_render_texture_rectangle 1 +#define WGL_TEXTURE_RECTANGLE_ATI 0x21A5 +#endif /* WGL_ATI_render_texture_rectangle */ + +#ifndef WGL_EXT_colorspace +#define WGL_EXT_colorspace 1 +#define WGL_COLORSPACE_EXT 0x309D +#define WGL_COLORSPACE_SRGB_EXT 0x3089 +#define WGL_COLORSPACE_LINEAR_EXT 0x308A +#endif /* WGL_EXT_colorspace */ + +#ifndef WGL_EXT_create_context_es_profile +#define WGL_EXT_create_context_es_profile 1 +#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004 +#endif /* WGL_EXT_create_context_es_profile */ + +#ifndef WGL_EXT_create_context_es2_profile +#define WGL_EXT_create_context_es2_profile 1 +#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 +#endif /* WGL_EXT_create_context_es2_profile */ + +#ifndef WGL_EXT_depth_float +#define WGL_EXT_depth_float 1 +#define WGL_DEPTH_FLOAT_EXT 0x2040 +#endif /* WGL_EXT_depth_float */ + +#ifndef WGL_EXT_display_color_table +#define WGL_EXT_display_color_table 1 +typedef GLboolean (APIENTRYP PFNWGLCREATEDISPLAYCOLORTABLEEXTPROC)(GLushort id); +typedef GLboolean (APIENTRYP PFNWGLLOADDISPLAYCOLORTABLEEXTPROC)(const GLushort * table, GLuint length); +typedef GLboolean (APIENTRYP PFNWGLBINDDISPLAYCOLORTABLEEXTPROC)(GLushort id); +typedef VOID (APIENTRYP PFNWGLDESTROYDISPLAYCOLORTABLEEXTPROC)(GLushort id); +#endif /* WGL_EXT_display_color_table */ + +#ifndef WGL_EXT_extensions_string +#define WGL_EXT_extensions_string 1 +typedef const char * (APIENTRYP PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void); +#endif /* WGL_EXT_extensions_string */ + +#ifndef WGL_EXT_framebuffer_sRGB +#define WGL_EXT_framebuffer_sRGB 1 +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x20A9 +#endif /* WGL_EXT_framebuffer_sRGB */ + +#ifndef WGL_EXT_make_current_read +#define WGL_EXT_make_current_read 1 +#define ERROR_INVALID_PIXEL_TYPE_EXT 0x2043 +typedef BOOL (APIENTRYP PFNWGLMAKECONTEXTCURRENTEXTPROC)(HDC hDrawDC, HDC hReadDC, HGLRC hglrc); +typedef HDC (APIENTRYP PFNWGLGETCURRENTREADDCEXTPROC)(void); +#endif /* WGL_EXT_make_current_read */ + +#ifndef WGL_EXT_multisample +#define WGL_EXT_multisample 1 +#define WGL_SAMPLE_BUFFERS_EXT 0x2041 +#define WGL_SAMPLES_EXT 0x2042 +#endif /* WGL_EXT_multisample */ + +#ifndef WGL_EXT_pbuffer +#define WGL_EXT_pbuffer 1 +DECLARE_HANDLE(HPBUFFEREXT); +#define WGL_DRAW_TO_PBUFFER_EXT 0x202D +#define WGL_MAX_PBUFFER_PIXELS_EXT 0x202E +#define WGL_MAX_PBUFFER_WIDTH_EXT 0x202F +#define WGL_MAX_PBUFFER_HEIGHT_EXT 0x2030 +#define WGL_OPTIMAL_PBUFFER_WIDTH_EXT 0x2031 +#define WGL_OPTIMAL_PBUFFER_HEIGHT_EXT 0x2032 +#define WGL_PBUFFER_LARGEST_EXT 0x2033 +#define WGL_PBUFFER_WIDTH_EXT 0x2034 +#define WGL_PBUFFER_HEIGHT_EXT 0x2035 +typedef HPBUFFEREXT (APIENTRYP PFNWGLCREATEPBUFFEREXTPROC)(HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int * piAttribList); +typedef HDC (APIENTRYP PFNWGLGETPBUFFERDCEXTPROC)(HPBUFFEREXT hPbuffer); +typedef int (APIENTRYP PFNWGLRELEASEPBUFFERDCEXTPROC)(HPBUFFEREXT hPbuffer, HDC hDC); +typedef BOOL (APIENTRYP PFNWGLDESTROYPBUFFEREXTPROC)(HPBUFFEREXT hPbuffer); +typedef BOOL (APIENTRYP PFNWGLQUERYPBUFFEREXTPROC)(HPBUFFEREXT hPbuffer, int iAttribute, int * piValue); +#endif /* WGL_EXT_pbuffer */ + +#ifndef WGL_EXT_pixel_format +#define WGL_EXT_pixel_format 1 +#define WGL_NUMBER_PIXEL_FORMATS_EXT 0x2000 +#define WGL_DRAW_TO_WINDOW_EXT 0x2001 +#define WGL_DRAW_TO_BITMAP_EXT 0x2002 +#define WGL_ACCELERATION_EXT 0x2003 +#define WGL_NEED_PALETTE_EXT 0x2004 +#define WGL_NEED_SYSTEM_PALETTE_EXT 0x2005 +#define WGL_SWAP_LAYER_BUFFERS_EXT 0x2006 +#define WGL_SWAP_METHOD_EXT 0x2007 +#define WGL_NUMBER_OVERLAYS_EXT 0x2008 +#define WGL_NUMBER_UNDERLAYS_EXT 0x2009 +#define WGL_TRANSPARENT_EXT 0x200A +#define WGL_TRANSPARENT_VALUE_EXT 0x200B +#define WGL_SHARE_DEPTH_EXT 0x200C +#define WGL_SHARE_STENCIL_EXT 0x200D +#define WGL_SHARE_ACCUM_EXT 0x200E +#define WGL_SUPPORT_GDI_EXT 0x200F +#define WGL_SUPPORT_OPENGL_EXT 0x2010 +#define WGL_DOUBLE_BUFFER_EXT 0x2011 +#define WGL_STEREO_EXT 0x2012 +#define WGL_PIXEL_TYPE_EXT 0x2013 +#define WGL_COLOR_BITS_EXT 0x2014 +#define WGL_RED_BITS_EXT 0x2015 +#define WGL_RED_SHIFT_EXT 0x2016 +#define WGL_GREEN_BITS_EXT 0x2017 +#define WGL_GREEN_SHIFT_EXT 0x2018 +#define WGL_BLUE_BITS_EXT 0x2019 +#define WGL_BLUE_SHIFT_EXT 0x201A +#define WGL_ALPHA_BITS_EXT 0x201B +#define WGL_ALPHA_SHIFT_EXT 0x201C +#define WGL_ACCUM_BITS_EXT 0x201D +#define WGL_ACCUM_RED_BITS_EXT 0x201E +#define WGL_ACCUM_GREEN_BITS_EXT 0x201F +#define WGL_ACCUM_BLUE_BITS_EXT 0x2020 +#define WGL_ACCUM_ALPHA_BITS_EXT 0x2021 +#define WGL_DEPTH_BITS_EXT 0x2022 +#define WGL_STENCIL_BITS_EXT 0x2023 +#define WGL_AUX_BUFFERS_EXT 0x2024 +#define WGL_NO_ACCELERATION_EXT 0x2025 +#define WGL_GENERIC_ACCELERATION_EXT 0x2026 +#define WGL_FULL_ACCELERATION_EXT 0x2027 +#define WGL_SWAP_EXCHANGE_EXT 0x2028 +#define WGL_SWAP_COPY_EXT 0x2029 +#define WGL_SWAP_UNDEFINED_EXT 0x202A +#define WGL_TYPE_RGBA_EXT 0x202B +#define WGL_TYPE_COLORINDEX_EXT 0x202C +typedef BOOL (APIENTRYP PFNWGLGETPIXELFORMATATTRIBIVEXTPROC)(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int * piAttributes, int * piValues); +typedef BOOL (APIENTRYP PFNWGLGETPIXELFORMATATTRIBFVEXTPROC)(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int * piAttributes, FLOAT * pfValues); +typedef BOOL (APIENTRYP PFNWGLCHOOSEPIXELFORMATEXTPROC)(HDC hdc, const int * piAttribIList, const FLOAT * pfAttribFList, UINT nMaxFormats, int * piFormats, UINT * nNumFormats); +#endif /* WGL_EXT_pixel_format */ + +#ifndef WGL_EXT_pixel_format_packed_float +#define WGL_EXT_pixel_format_packed_float 1 +#define WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT 0x20A8 +#endif /* WGL_EXT_pixel_format_packed_float */ + +#ifndef WGL_EXT_swap_control +#define WGL_EXT_swap_control 1 +typedef BOOL (APIENTRYP PFNWGLSWAPINTERVALEXTPROC)(int interval); +typedef int (APIENTRYP PFNWGLGETSWAPINTERVALEXTPROC)(void); +#endif /* WGL_EXT_swap_control */ + +#ifndef WGL_EXT_swap_control_tear +#define WGL_EXT_swap_control_tear 1 +#endif /* WGL_EXT_swap_control_tear */ + +#ifndef WGL_I3D_digital_video_control +#define WGL_I3D_digital_video_control 1 +#define WGL_DIGITAL_VIDEO_CURSOR_ALPHA_FRAMEBUFFER_I3D 0x2050 +#define WGL_DIGITAL_VIDEO_CURSOR_ALPHA_VALUE_I3D 0x2051 +#define WGL_DIGITAL_VIDEO_CURSOR_INCLUDED_I3D 0x2052 +#define WGL_DIGITAL_VIDEO_GAMMA_CORRECTED_I3D 0x2053 +typedef BOOL (APIENTRYP PFNWGLGETDIGITALVIDEOPARAMETERSI3DPROC)(HDC hDC, int iAttribute, int * piValue); +typedef BOOL (APIENTRYP PFNWGLSETDIGITALVIDEOPARAMETERSI3DPROC)(HDC hDC, int iAttribute, const int * piValue); +#endif /* WGL_I3D_digital_video_control */ + +#ifndef WGL_I3D_gamma +#define WGL_I3D_gamma 1 +#define WGL_GAMMA_TABLE_SIZE_I3D 0x204E +#define WGL_GAMMA_EXCLUDE_DESKTOP_I3D 0x204F +typedef BOOL (APIENTRYP PFNWGLGETGAMMATABLEPARAMETERSI3DPROC)(HDC hDC, int iAttribute, int * piValue); +typedef BOOL (APIENTRYP PFNWGLSETGAMMATABLEPARAMETERSI3DPROC)(HDC hDC, int iAttribute, const int * piValue); +typedef BOOL (APIENTRYP PFNWGLGETGAMMATABLEI3DPROC)(HDC hDC, int iEntries, USHORT * puRed, USHORT * puGreen, USHORT * puBlue); +typedef BOOL (APIENTRYP PFNWGLSETGAMMATABLEI3DPROC)(HDC hDC, int iEntries, const USHORT * puRed, const USHORT * puGreen, const USHORT * puBlue); +#endif /* WGL_I3D_gamma */ + +#ifndef WGL_I3D_genlock +#define WGL_I3D_genlock 1 +#define WGL_GENLOCK_SOURCE_MULTIVIEW_I3D 0x2044 +#define WGL_GENLOCK_SOURCE_EXTERNAL_SYNC_I3D 0x2045 +#define WGL_GENLOCK_SOURCE_EXTERNAL_FIELD_I3D 0x2046 +#define WGL_GENLOCK_SOURCE_EXTERNAL_TTL_I3D 0x2047 +#define WGL_GENLOCK_SOURCE_DIGITAL_SYNC_I3D 0x2048 +#define WGL_GENLOCK_SOURCE_DIGITAL_FIELD_I3D 0x2049 +#define WGL_GENLOCK_SOURCE_EDGE_FALLING_I3D 0x204A +#define WGL_GENLOCK_SOURCE_EDGE_RISING_I3D 0x204B +#define WGL_GENLOCK_SOURCE_EDGE_BOTH_I3D 0x204C +typedef BOOL (APIENTRYP PFNWGLENABLEGENLOCKI3DPROC)(HDC hDC); +typedef BOOL (APIENTRYP PFNWGLDISABLEGENLOCKI3DPROC)(HDC hDC); +typedef BOOL (APIENTRYP PFNWGLISENABLEDGENLOCKI3DPROC)(HDC hDC, BOOL * pFlag); +typedef BOOL (APIENTRYP PFNWGLGENLOCKSOURCEI3DPROC)(HDC hDC, UINT uSource); +typedef BOOL (APIENTRYP PFNWGLGETGENLOCKSOURCEI3DPROC)(HDC hDC, UINT * uSource); +typedef BOOL (APIENTRYP PFNWGLGENLOCKSOURCEEDGEI3DPROC)(HDC hDC, UINT uEdge); +typedef BOOL (APIENTRYP PFNWGLGETGENLOCKSOURCEEDGEI3DPROC)(HDC hDC, UINT * uEdge); +typedef BOOL (APIENTRYP PFNWGLGENLOCKSAMPLERATEI3DPROC)(HDC hDC, UINT uRate); +typedef BOOL (APIENTRYP PFNWGLGETGENLOCKSAMPLERATEI3DPROC)(HDC hDC, UINT * uRate); +typedef BOOL (APIENTRYP PFNWGLGENLOCKSOURCEDELAYI3DPROC)(HDC hDC, UINT uDelay); +typedef BOOL (APIENTRYP PFNWGLGETGENLOCKSOURCEDELAYI3DPROC)(HDC hDC, UINT * uDelay); +typedef BOOL (APIENTRYP PFNWGLQUERYGENLOCKMAXSOURCEDELAYI3DPROC)(HDC hDC, UINT * uMaxLineDelay, UINT * uMaxPixelDelay); +#endif /* WGL_I3D_genlock */ + +#ifndef WGL_I3D_image_buffer +#define WGL_I3D_image_buffer 1 +#define WGL_IMAGE_BUFFER_MIN_ACCESS_I3D 0x00000001 +#define WGL_IMAGE_BUFFER_LOCK_I3D 0x00000002 +typedef LPVOID (APIENTRYP PFNWGLCREATEIMAGEBUFFERI3DPROC)(HDC hDC, DWORD dwSize, UINT uFlags); +typedef BOOL (APIENTRYP PFNWGLDESTROYIMAGEBUFFERI3DPROC)(HDC hDC, LPVOID pAddress); +typedef BOOL (APIENTRYP PFNWGLASSOCIATEIMAGEBUFFEREVENTSI3DPROC)(HDC hDC, const HANDLE * pEvent, const LPVOID * pAddress, const DWORD * pSize, UINT count); +typedef BOOL (APIENTRYP PFNWGLRELEASEIMAGEBUFFEREVENTSI3DPROC)(HDC hDC, const LPVOID * pAddress, UINT count); +#endif /* WGL_I3D_image_buffer */ + +#ifndef WGL_I3D_swap_frame_lock +#define WGL_I3D_swap_frame_lock 1 +typedef BOOL (APIENTRYP PFNWGLENABLEFRAMELOCKI3DPROC)(void); +typedef BOOL (APIENTRYP PFNWGLDISABLEFRAMELOCKI3DPROC)(void); +typedef BOOL (APIENTRYP PFNWGLISENABLEDFRAMELOCKI3DPROC)(BOOL * pFlag); +typedef BOOL (APIENTRYP PFNWGLQUERYFRAMELOCKMASTERI3DPROC)(BOOL * pFlag); +#endif /* WGL_I3D_swap_frame_lock */ + +#ifndef WGL_I3D_swap_frame_usage +#define WGL_I3D_swap_frame_usage 1 +typedef BOOL (APIENTRYP PFNWGLGETFRAMEUSAGEI3DPROC)(float * pUsage); +typedef BOOL (APIENTRYP PFNWGLBEGINFRAMETRACKINGI3DPROC)(void); +typedef BOOL (APIENTRYP PFNWGLENDFRAMETRACKINGI3DPROC)(void); +typedef BOOL (APIENTRYP PFNWGLQUERYFRAMETRACKINGI3DPROC)(DWORD * pFrameCount, DWORD * pMissedFrames, float * pLastMissedUsage); +#endif /* WGL_I3D_swap_frame_usage */ + +#ifndef WGL_NV_copy_image +#define WGL_NV_copy_image 1 +typedef BOOL (APIENTRYP PFNWGLCOPYIMAGESUBDATANVPROC)(HGLRC hSrcRC, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, HGLRC hDstRC, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#endif /* WGL_NV_copy_image */ + +#ifndef WGL_NV_delay_before_swap +#define WGL_NV_delay_before_swap 1 +typedef BOOL (APIENTRYP PFNWGLDELAYBEFORESWAPNVPROC)(HDC hDC, GLfloat seconds); +#endif /* WGL_NV_delay_before_swap */ + +#ifndef WGL_NV_DX_interop +#define WGL_NV_DX_interop 1 +#define WGL_ACCESS_READ_ONLY_NV 0x00000000 +#define WGL_ACCESS_READ_WRITE_NV 0x00000001 +#define WGL_ACCESS_WRITE_DISCARD_NV 0x00000002 +typedef BOOL (APIENTRYP PFNWGLDXSETRESOURCESHAREHANDLENVPROC)(void * dxObject, HANDLE shareHandle); +typedef HANDLE (APIENTRYP PFNWGLDXOPENDEVICENVPROC)(void * dxDevice); +typedef BOOL (APIENTRYP PFNWGLDXCLOSEDEVICENVPROC)(HANDLE hDevice); +typedef HANDLE (APIENTRYP PFNWGLDXREGISTEROBJECTNVPROC)(HANDLE hDevice, void * dxObject, GLuint name, GLenum type, GLenum access); +typedef BOOL (APIENTRYP PFNWGLDXUNREGISTEROBJECTNVPROC)(HANDLE hDevice, HANDLE hObject); +typedef BOOL (APIENTRYP PFNWGLDXOBJECTACCESSNVPROC)(HANDLE hObject, GLenum access); +typedef BOOL (APIENTRYP PFNWGLDXLOCKOBJECTSNVPROC)(HANDLE hDevice, GLint count, HANDLE * hObjects); +typedef BOOL (APIENTRYP PFNWGLDXUNLOCKOBJECTSNVPROC)(HANDLE hDevice, GLint count, HANDLE * hObjects); +#endif /* WGL_NV_DX_interop */ + +#ifndef WGL_NV_DX_interop2 +#define WGL_NV_DX_interop2 1 +#endif /* WGL_NV_DX_interop2 */ + +#ifndef WGL_NV_float_buffer +#define WGL_NV_float_buffer 1 +#define WGL_FLOAT_COMPONENTS_NV 0x20B0 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_R_NV 0x20B1 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RG_NV 0x20B2 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGB_NV 0x20B3 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGBA_NV 0x20B4 +#define WGL_TEXTURE_FLOAT_R_NV 0x20B5 +#define WGL_TEXTURE_FLOAT_RG_NV 0x20B6 +#define WGL_TEXTURE_FLOAT_RGB_NV 0x20B7 +#define WGL_TEXTURE_FLOAT_RGBA_NV 0x20B8 +#endif /* WGL_NV_float_buffer */ + +#ifndef WGL_NV_gpu_affinity +#define WGL_NV_gpu_affinity 1 +DECLARE_HANDLE(HGPUNV); +typedef struct _GPU_DEVICE *PGPU_DEVICE; +#define ERROR_INCOMPATIBLE_AFFINITY_MASKS_NV 0x20D0 +#define ERROR_MISSING_AFFINITY_MASK_NV 0x20D1 +typedef BOOL (APIENTRYP PFNWGLENUMGPUSNVPROC)(UINT iGpuIndex, HGPUNV * phGpu); +typedef BOOL (APIENTRYP PFNWGLENUMGPUDEVICESNVPROC)(HGPUNV hGpu, UINT iDeviceIndex, PGPU_DEVICE lpGpuDevice); +typedef HDC (APIENTRYP PFNWGLCREATEAFFINITYDCNVPROC)(const HGPUNV * phGpuList); +typedef BOOL (APIENTRYP PFNWGLENUMGPUSFROMAFFINITYDCNVPROC)(HDC hAffinityDC, UINT iGpuIndex, HGPUNV * hGpu); +typedef BOOL (APIENTRYP PFNWGLDELETEDCNVPROC)(HDC hdc); +#endif /* WGL_NV_gpu_affinity */ + +#ifndef WGL_NV_multisample_coverage +#define WGL_NV_multisample_coverage 1 +#define WGL_COVERAGE_SAMPLES_NV 0x2042 +#define WGL_COLOR_SAMPLES_NV 0x20B9 +#endif /* WGL_NV_multisample_coverage */ + +#ifndef WGL_NV_present_video +#define WGL_NV_present_video 1 +DECLARE_HANDLE(HVIDEOOUTPUTDEVICENV); +#define WGL_NUM_VIDEO_SLOTS_NV 0x20F0 +typedef int (APIENTRYP PFNWGLENUMERATEVIDEODEVICESNVPROC)(HDC hDc, HVIDEOOUTPUTDEVICENV * phDeviceList); +typedef BOOL (APIENTRYP PFNWGLBINDVIDEODEVICENVPROC)(HDC hDc, unsigned int uVideoSlot, HVIDEOOUTPUTDEVICENV hVideoDevice, const int * piAttribList); +typedef BOOL (APIENTRYP PFNWGLQUERYCURRENTCONTEXTNVPROC)(int iAttribute, int * piValue); +#endif /* WGL_NV_present_video */ + +#ifndef WGL_NV_render_depth_texture +#define WGL_NV_render_depth_texture 1 +#define WGL_BIND_TO_TEXTURE_DEPTH_NV 0x20A3 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_DEPTH_NV 0x20A4 +#define WGL_DEPTH_TEXTURE_FORMAT_NV 0x20A5 +#define WGL_TEXTURE_DEPTH_COMPONENT_NV 0x20A6 +#define WGL_DEPTH_COMPONENT_NV 0x20A7 +#endif /* WGL_NV_render_depth_texture */ + +#ifndef WGL_NV_render_texture_rectangle +#define WGL_NV_render_texture_rectangle 1 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_RGB_NV 0x20A0 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_RGBA_NV 0x20A1 +#define WGL_TEXTURE_RECTANGLE_NV 0x20A2 +#endif /* WGL_NV_render_texture_rectangle */ + +#ifndef WGL_NV_swap_group +#define WGL_NV_swap_group 1 +typedef BOOL (APIENTRYP PFNWGLJOINSWAPGROUPNVPROC)(HDC hDC, GLuint group); +typedef BOOL (APIENTRYP PFNWGLBINDSWAPBARRIERNVPROC)(GLuint group, GLuint barrier); +typedef BOOL (APIENTRYP PFNWGLQUERYSWAPGROUPNVPROC)(HDC hDC, GLuint * group, GLuint * barrier); +typedef BOOL (APIENTRYP PFNWGLQUERYMAXSWAPGROUPSNVPROC)(HDC hDC, GLuint * maxGroups, GLuint * maxBarriers); +typedef BOOL (APIENTRYP PFNWGLQUERYFRAMECOUNTNVPROC)(HDC hDC, GLuint * count); +typedef BOOL (APIENTRYP PFNWGLRESETFRAMECOUNTNVPROC)(HDC hDC); +#endif /* WGL_NV_swap_group */ + +#ifndef WGL_NV_video_capture +#define WGL_NV_video_capture 1 +DECLARE_HANDLE(HVIDEOINPUTDEVICENV); +#define WGL_UNIQUE_ID_NV 0x20CE +#define WGL_NUM_VIDEO_CAPTURE_SLOTS_NV 0x20CF +typedef BOOL (APIENTRYP PFNWGLBINDVIDEOCAPTUREDEVICENVPROC)(UINT uVideoSlot, HVIDEOINPUTDEVICENV hDevice); +typedef UINT (APIENTRYP PFNWGLENUMERATEVIDEOCAPTUREDEVICESNVPROC)(HDC hDc, HVIDEOINPUTDEVICENV * phDeviceList); +typedef BOOL (APIENTRYP PFNWGLLOCKVIDEOCAPTUREDEVICENVPROC)(HDC hDc, HVIDEOINPUTDEVICENV hDevice); +typedef BOOL (APIENTRYP PFNWGLQUERYVIDEOCAPTUREDEVICENVPROC)(HDC hDc, HVIDEOINPUTDEVICENV hDevice, int iAttribute, int * piValue); +typedef BOOL (APIENTRYP PFNWGLRELEASEVIDEOCAPTUREDEVICENVPROC)(HDC hDc, HVIDEOINPUTDEVICENV hDevice); +#endif /* WGL_NV_video_capture */ + +#ifndef WGL_NV_video_output +#define WGL_NV_video_output 1 +DECLARE_HANDLE(HPVIDEODEV); +#define WGL_BIND_TO_VIDEO_RGB_NV 0x20C0 +#define WGL_BIND_TO_VIDEO_RGBA_NV 0x20C1 +#define WGL_BIND_TO_VIDEO_RGB_AND_DEPTH_NV 0x20C2 +#define WGL_VIDEO_OUT_COLOR_NV 0x20C3 +#define WGL_VIDEO_OUT_ALPHA_NV 0x20C4 +#define WGL_VIDEO_OUT_DEPTH_NV 0x20C5 +#define WGL_VIDEO_OUT_COLOR_AND_ALPHA_NV 0x20C6 +#define WGL_VIDEO_OUT_COLOR_AND_DEPTH_NV 0x20C7 +#define WGL_VIDEO_OUT_FRAME 0x20C8 +#define WGL_VIDEO_OUT_FIELD_1 0x20C9 +#define WGL_VIDEO_OUT_FIELD_2 0x20CA +#define WGL_VIDEO_OUT_STACKED_FIELDS_1_2 0x20CB +#define WGL_VIDEO_OUT_STACKED_FIELDS_2_1 0x20CC +typedef BOOL (APIENTRYP PFNWGLGETVIDEODEVICENVPROC)(HDC hDC, int numDevices, HPVIDEODEV * hVideoDevice); +typedef BOOL (APIENTRYP PFNWGLRELEASEVIDEODEVICENVPROC)(HPVIDEODEV hVideoDevice); +typedef BOOL (APIENTRYP PFNWGLBINDVIDEOIMAGENVPROC)(HPVIDEODEV hVideoDevice, HPBUFFERARB hPbuffer, int iVideoBuffer); +typedef BOOL (APIENTRYP PFNWGLRELEASEVIDEOIMAGENVPROC)(HPBUFFERARB hPbuffer, int iVideoBuffer); +typedef BOOL (APIENTRYP PFNWGLSENDPBUFFERTOVIDEONVPROC)(HPBUFFERARB hPbuffer, int iBufferType, unsigned long * pulCounterPbuffer, BOOL bBlock); +typedef BOOL (APIENTRYP PFNWGLGETVIDEOINFONVPROC)(HPVIDEODEV hpVideoDevice, unsigned long * pulCounterOutputPbuffer, unsigned long * pulCounterOutputVideo); +#endif /* WGL_NV_video_output */ + +#ifndef WGL_NV_vertex_array_range +#define WGL_NV_vertex_array_range 1 +typedef void * (APIENTRYP PFNWGLALLOCATEMEMORYNVPROC)(GLsizei size, GLfloat readfreq, GLfloat writefreq, GLfloat priority); +typedef void (APIENTRYP PFNWGLFREEMEMORYNVPROC)(void * pointer); +#endif /* WGL_NV_vertex_array_range */ + +#ifndef WGL_OML_sync_control +#define WGL_OML_sync_control 1 +typedef BOOL (APIENTRYP PFNWGLGETSYNCVALUESOMLPROC)(HDC hdc, INT64 * ust, INT64 * msc, INT64 * sbc); +typedef BOOL (APIENTRYP PFNWGLGETMSCRATEOMLPROC)(HDC hdc, INT32 * numerator, INT32 * denominator); +typedef INT64 (APIENTRYP PFNWGLSWAPBUFFERSMSCOMLPROC)(HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder); +typedef INT64 (APIENTRYP PFNWGLSWAPLAYERBUFFERSMSCOMLPROC)(HDC hdc, INT fuPlanes, INT64 target_msc, INT64 divisor, INT64 remainder); +typedef BOOL (APIENTRYP PFNWGLWAITFORMSCOMLPROC)(HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder, INT64 * ust, INT64 * msc, INT64 * sbc); +typedef BOOL (APIENTRYP PFNWGLWAITFORSBCOMLPROC)(HDC hdc, INT64 target_sbc, INT64 * ust, INT64 * msc, INT64 * sbc); +#endif /* WGL_OML_sync_control */ + +#ifndef WGL_NV_multigpu_context +#define WGL_NV_multigpu_context 1 +#define WGL_CONTEXT_MULTIGPU_ATTRIB_NV 0x20AA +#define WGL_CONTEXT_MULTIGPU_ATTRIB_SINGLE_NV 0x20AB +#define WGL_CONTEXT_MULTIGPU_ATTRIB_AFR_NV 0x20AC +#define WGL_CONTEXT_MULTIGPU_ATTRIB_MULTICAST_NV 0x20AD +#define WGL_CONTEXT_MULTIGPU_ATTRIB_MULTI_DISPLAY_MULTICAST_NV 0x20AE +#endif /* WGL_NV_multigpu_context */ +#endif /* GLBIND_WGL */ + +#if defined(GLBIND_GLX) +#ifndef GLX_3DFX_multisample +#define GLX_3DFX_multisample 1 +#define GLX_SAMPLE_BUFFERS_3DFX 0x8050 +#define GLX_SAMPLES_3DFX 0x8051 +#endif /* GLX_3DFX_multisample */ + +#ifndef GLX_AMD_gpu_association +#define GLX_AMD_gpu_association 1 +#define GLX_GPU_VENDOR_AMD 0x1F00 +#define GLX_GPU_RENDERER_STRING_AMD 0x1F01 +#define GLX_GPU_OPENGL_VERSION_STRING_AMD 0x1F02 +#define GLX_GPU_FASTEST_TARGET_GPUS_AMD 0x21A2 +#define GLX_GPU_RAM_AMD 0x21A3 +#define GLX_GPU_CLOCK_AMD 0x21A4 +#define GLX_GPU_NUM_PIPES_AMD 0x21A5 +#define GLX_GPU_NUM_SIMD_AMD 0x21A6 +#define GLX_GPU_NUM_RB_AMD 0x21A7 +#define GLX_GPU_NUM_SPI_AMD 0x21A8 +typedef unsigned int (APIENTRYP PFNGLXGETGPUIDSAMDPROC)(unsigned int maxCount, unsigned int * ids); +typedef int (APIENTRYP PFNGLXGETGPUINFOAMDPROC)(unsigned int id, int property, GLenum dataType, unsigned int size, void * data); +typedef unsigned int (APIENTRYP PFNGLXGETCONTEXTGPUIDAMDPROC)(GLXContext ctx); +typedef GLXContext (APIENTRYP PFNGLXCREATEASSOCIATEDCONTEXTAMDPROC)(unsigned int id, GLXContext share_list); +typedef GLXContext (APIENTRYP PFNGLXCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC)(unsigned int id, GLXContext share_context, const int * attribList); +typedef glbind_Bool (APIENTRYP PFNGLXDELETEASSOCIATEDCONTEXTAMDPROC)(GLXContext ctx); +typedef glbind_Bool (APIENTRYP PFNGLXMAKEASSOCIATEDCONTEXTCURRENTAMDPROC)(GLXContext ctx); +typedef GLXContext (APIENTRYP PFNGLXGETCURRENTASSOCIATEDCONTEXTAMDPROC)(void); +typedef void (APIENTRYP PFNGLXBLITCONTEXTFRAMEBUFFERAMDPROC)(GLXContext dstCtx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif /* GLX_AMD_gpu_association */ + +#ifndef GLX_ARB_context_flush_control +#define GLX_ARB_context_flush_control 1 +#define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 +#define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 +#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 +#endif /* GLX_ARB_context_flush_control */ + +#ifndef GLX_ARB_create_context +#define GLX_ARB_create_context 1 +#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define GLX_CONTEXT_FLAGS_ARB 0x2094 +typedef GLXContext (APIENTRYP PFNGLXCREATECONTEXTATTRIBSARBPROC)(glbind_Display* dpy, GLXFBConfig config, GLXContext share_context, glbind_Bool direct, const int * attrib_list); +#endif /* GLX_ARB_create_context */ + +#ifndef GLX_ARB_create_context_no_error +#define GLX_ARB_create_context_no_error 1 +#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31B3 +#endif /* GLX_ARB_create_context_no_error */ + +#ifndef GLX_ARB_create_context_profile +#define GLX_ARB_create_context_profile 1 +#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 +#endif /* GLX_ARB_create_context_profile */ + +#ifndef GLX_ARB_create_context_robustness +#define GLX_ARB_create_context_robustness 1 +#define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GLX_NO_RESET_NOTIFICATION_ARB 0x8261 +#endif /* GLX_ARB_create_context_robustness */ + +#ifndef GLX_ARB_fbconfig_float +#define GLX_ARB_fbconfig_float 1 +#define GLX_RGBA_FLOAT_TYPE_ARB 0x20B9 +#define GLX_RGBA_FLOAT_BIT_ARB 0x00000004 +#endif /* GLX_ARB_fbconfig_float */ + +#ifndef GLX_ARB_framebuffer_sRGB +#define GLX_ARB_framebuffer_sRGB 1 +#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20B2 +#endif /* GLX_ARB_framebuffer_sRGB */ + +#ifndef GLX_ARB_get_proc_address +#define GLX_ARB_get_proc_address 1 +typedef __GLXextFuncPtr (APIENTRYP PFNGLXGETPROCADDRESSARBPROC)(const GLubyte * procName); +#endif /* GLX_ARB_get_proc_address */ + +#ifndef GLX_ARB_multisample +#define GLX_ARB_multisample 1 +#define GLX_SAMPLE_BUFFERS_ARB 100000 +#define GLX_SAMPLES_ARB 100001 +#endif /* GLX_ARB_multisample */ + +#ifndef GLX_ARB_robustness_application_isolation +#define GLX_ARB_robustness_application_isolation 1 +#define GLX_CONTEXT_RESET_ISOLATION_BIT_ARB 0x00000008 +#endif /* GLX_ARB_robustness_application_isolation */ + +#ifndef GLX_ARB_robustness_share_group_isolation +#define GLX_ARB_robustness_share_group_isolation 1 +#define GLX_CONTEXT_RESET_ISOLATION_BIT_ARB 0x00000008 +#endif /* GLX_ARB_robustness_share_group_isolation */ + +#ifndef GLX_ARB_vertex_buffer_object +#define GLX_ARB_vertex_buffer_object 1 +#define GLX_CONTEXT_ALLOW_BUFFER_BYTE_ORDER_MISMATCH_ARB 0x2095 +#endif /* GLX_ARB_vertex_buffer_object */ + +#ifndef GLX_EXT_buffer_age +#define GLX_EXT_buffer_age 1 +#define GLX_BACK_BUFFER_AGE_EXT 0x20F4 +#endif /* GLX_EXT_buffer_age */ + +#ifndef GLX_EXT_context_priority +#define GLX_EXT_context_priority 1 +#define GLX_CONTEXT_PRIORITY_LEVEL_EXT 0x3100 +#define GLX_CONTEXT_PRIORITY_HIGH_EXT 0x3101 +#define GLX_CONTEXT_PRIORITY_MEDIUM_EXT 0x3102 +#define GLX_CONTEXT_PRIORITY_LOW_EXT 0x3103 +#endif /* GLX_EXT_context_priority */ + +#ifndef GLX_EXT_create_context_es_profile +#define GLX_EXT_create_context_es_profile 1 +#define GLX_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004 +#endif /* GLX_EXT_create_context_es_profile */ + +#ifndef GLX_EXT_create_context_es2_profile +#define GLX_EXT_create_context_es2_profile 1 +#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 +#endif /* GLX_EXT_create_context_es2_profile */ + +#ifndef GLX_EXT_fbconfig_packed_float +#define GLX_EXT_fbconfig_packed_float 1 +#define GLX_RGBA_UNSIGNED_FLOAT_TYPE_EXT 0x20B1 +#define GLX_RGBA_UNSIGNED_FLOAT_BIT_EXT 0x00000008 +#endif /* GLX_EXT_fbconfig_packed_float */ + +#ifndef GLX_EXT_framebuffer_sRGB +#define GLX_EXT_framebuffer_sRGB 1 +#define GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x20B2 +#endif /* GLX_EXT_framebuffer_sRGB */ + +#ifndef GLX_EXT_import_context +#define GLX_EXT_import_context 1 +#define GLX_SHARE_CONTEXT_EXT 0x800A +#define GLX_VISUAL_ID_EXT 0x800B +#define GLX_SCREEN_EXT 0x800C +typedef glbind_Display* (APIENTRYP PFNGLXGETCURRENTDISPLAYEXTPROC)(void); +typedef int (APIENTRYP PFNGLXQUERYCONTEXTINFOEXTPROC)(glbind_Display* dpy, GLXContext context, int attribute, int * value); +typedef GLXContextID (APIENTRYP PFNGLXGETCONTEXTIDEXTPROC)(const GLXContext context); +typedef GLXContext (APIENTRYP PFNGLXIMPORTCONTEXTEXTPROC)(glbind_Display* dpy, GLXContextID contextID); +typedef void (APIENTRYP PFNGLXFREECONTEXTEXTPROC)(glbind_Display* dpy, GLXContext context); +#endif /* GLX_EXT_import_context */ + +#ifndef GLX_EXT_libglvnd +#define GLX_EXT_libglvnd 1 +#define GLX_VENDOR_NAMES_EXT 0x20F6 +#endif /* GLX_EXT_libglvnd */ + +#ifndef GLX_EXT_no_config_context +#define GLX_EXT_no_config_context 1 +#endif /* GLX_EXT_no_config_context */ + +#ifndef GLX_EXT_stereo_tree +#define GLX_EXT_stereo_tree 1 +typedef struct { + int type; + unsigned long serial; + glbind_Bool send_event; + glbind_Display*display; + int extension; + int evtype; + GLXDrawable window; + glbind_Bool stereo_tree; +} GLXStereoNotifyEventEXT; +#define GLX_STEREO_TREE_EXT 0x20F5 +#define GLX_STEREO_NOTIFY_MASK_EXT 0x00000001 +#define GLX_STEREO_NOTIFY_EXT 0x00000000 +#endif /* GLX_EXT_stereo_tree */ + +#ifndef GLX_EXT_swap_control +#define GLX_EXT_swap_control 1 +#define GLX_SWAP_INTERVAL_EXT 0x20F1 +#define GLX_MAX_SWAP_INTERVAL_EXT 0x20F2 +typedef void (APIENTRYP PFNGLXSWAPINTERVALEXTPROC)(glbind_Display* dpy, GLXDrawable drawable, int interval); +#endif /* GLX_EXT_swap_control */ + +#ifndef GLX_EXT_swap_control_tear +#define GLX_EXT_swap_control_tear 1 +#define GLX_LATE_SWAPS_TEAR_EXT 0x20F3 +#endif /* GLX_EXT_swap_control_tear */ + +#ifndef GLX_EXT_texture_from_pixmap +#define GLX_EXT_texture_from_pixmap 1 +#define GLX_TEXTURE_1D_BIT_EXT 0x00000001 +#define GLX_TEXTURE_2D_BIT_EXT 0x00000002 +#define GLX_TEXTURE_RECTANGLE_BIT_EXT 0x00000004 +#define GLX_BIND_TO_TEXTURE_RGB_EXT 0x20D0 +#define GLX_BIND_TO_TEXTURE_RGBA_EXT 0x20D1 +#define GLX_BIND_TO_MIPMAP_TEXTURE_EXT 0x20D2 +#define GLX_BIND_TO_TEXTURE_TARGETS_EXT 0x20D3 +#define GLX_Y_INVERTED_EXT 0x20D4 +#define GLX_TEXTURE_FORMAT_EXT 0x20D5 +#define GLX_TEXTURE_TARGET_EXT 0x20D6 +#define GLX_MIPMAP_TEXTURE_EXT 0x20D7 +#define GLX_TEXTURE_FORMAT_NONE_EXT 0x20D8 +#define GLX_TEXTURE_FORMAT_RGB_EXT 0x20D9 +#define GLX_TEXTURE_FORMAT_RGBA_EXT 0x20DA +#define GLX_TEXTURE_1D_EXT 0x20DB +#define GLX_TEXTURE_2D_EXT 0x20DC +#define GLX_TEXTURE_RECTANGLE_EXT 0x20DD +#define GLX_FRONT_LEFT_EXT 0x20DE +#define GLX_FRONT_RIGHT_EXT 0x20DF +#define GLX_BACK_LEFT_EXT 0x20E0 +#define GLX_BACK_RIGHT_EXT 0x20E1 +#define GLX_FRONT_EXT 0x20DE +#define GLX_BACK_EXT 0x20E0 +#define GLX_AUX0_EXT 0x20E2 +#define GLX_AUX1_EXT 0x20E3 +#define GLX_AUX2_EXT 0x20E4 +#define GLX_AUX3_EXT 0x20E5 +#define GLX_AUX4_EXT 0x20E6 +#define GLX_AUX5_EXT 0x20E7 +#define GLX_AUX6_EXT 0x20E8 +#define GLX_AUX7_EXT 0x20E9 +#define GLX_AUX8_EXT 0x20EA +#define GLX_AUX9_EXT 0x20EB +typedef void (APIENTRYP PFNGLXBINDTEXIMAGEEXTPROC)(glbind_Display* dpy, GLXDrawable drawable, int buffer, const int * attrib_list); +typedef void (APIENTRYP PFNGLXRELEASETEXIMAGEEXTPROC)(glbind_Display* dpy, GLXDrawable drawable, int buffer); +#endif /* GLX_EXT_texture_from_pixmap */ + +#ifndef GLX_EXT_visual_info +#define GLX_EXT_visual_info 1 +#define GLX_X_VISUAL_TYPE_EXT 0x22 +#define GLX_TRANSPARENT_TYPE_EXT 0x23 +#define GLX_TRANSPARENT_INDEX_VALUE_EXT 0x24 +#define GLX_TRANSPARENT_RED_VALUE_EXT 0x25 +#define GLX_TRANSPARENT_GREEN_VALUE_EXT 0x26 +#define GLX_TRANSPARENT_BLUE_VALUE_EXT 0x27 +#define GLX_TRANSPARENT_ALPHA_VALUE_EXT 0x28 +#define GLX_NONE_EXT 0x8000 +#define GLX_TRUE_COLOR_EXT 0x8002 +#define GLX_DIRECT_COLOR_EXT 0x8003 +#define GLX_PSEUDO_COLOR_EXT 0x8004 +#define GLX_STATIC_COLOR_EXT 0x8005 +#define GLX_GRAY_SCALE_EXT 0x8006 +#define GLX_STATIC_GRAY_EXT 0x8007 +#define GLX_TRANSPARENT_RGB_EXT 0x8008 +#define GLX_TRANSPARENT_INDEX_EXT 0x8009 +#endif /* GLX_EXT_visual_info */ + +#ifndef GLX_EXT_visual_rating +#define GLX_EXT_visual_rating 1 +#define GLX_VISUAL_CAVEAT_EXT 0x20 +#define GLX_SLOW_VISUAL_EXT 0x8001 +#define GLX_NON_CONFORMANT_VISUAL_EXT 0x800D +#define GLX_NONE_EXT 0x8000 +#endif /* GLX_EXT_visual_rating */ + +#ifndef GLX_INTEL_swap_event +#define GLX_INTEL_swap_event 1 +#define GLX_BUFFER_SWAP_COMPLETE_INTEL_MASK 0x04000000 +#define GLX_EXCHANGE_COMPLETE_INTEL 0x8180 +#define GLX_COPY_COMPLETE_INTEL 0x8181 +#define GLX_FLIP_COMPLETE_INTEL 0x8182 +#endif /* GLX_INTEL_swap_event */ + +#ifndef GLX_MESA_agp_offset +#define GLX_MESA_agp_offset 1 +typedef unsigned int (APIENTRYP PFNGLXGETAGPOFFSETMESAPROC)(const void * pointer); +#endif /* GLX_MESA_agp_offset */ + +#ifndef GLX_MESA_copy_sub_buffer +#define GLX_MESA_copy_sub_buffer 1 +typedef void (APIENTRYP PFNGLXCOPYSUBBUFFERMESAPROC)(glbind_Display* dpy, GLXDrawable drawable, int x, int y, int width, int height); +#endif /* GLX_MESA_copy_sub_buffer */ + +#ifndef GLX_MESA_pixmap_colormap +#define GLX_MESA_pixmap_colormap 1 +typedef GLXPixmap (APIENTRYP PFNGLXCREATEGLXPIXMAPMESAPROC)(glbind_Display* dpy, glbind_XVisualInfo* visual, glbind_Pixmap pixmap, glbind_Colormap cmap); +#endif /* GLX_MESA_pixmap_colormap */ + +#ifndef GLX_MESA_query_renderer +#define GLX_MESA_query_renderer 1 +#define GLX_RENDERER_VENDOR_ID_MESA 0x8183 +#define GLX_RENDERER_DEVICE_ID_MESA 0x8184 +#define GLX_RENDERER_VERSION_MESA 0x8185 +#define GLX_RENDERER_ACCELERATED_MESA 0x8186 +#define GLX_RENDERER_VIDEO_MEMORY_MESA 0x8187 +#define GLX_RENDERER_UNIFIED_MEMORY_ARCHITECTURE_MESA 0x8188 +#define GLX_RENDERER_PREFERRED_PROFILE_MESA 0x8189 +#define GLX_RENDERER_OPENGL_CORE_PROFILE_VERSION_MESA 0x818A +#define GLX_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_MESA 0x818B +#define GLX_RENDERER_OPENGL_ES_PROFILE_VERSION_MESA 0x818C +#define GLX_RENDERER_OPENGL_ES2_PROFILE_VERSION_MESA 0x818D +typedef glbind_Bool (APIENTRYP PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC)(int attribute, unsigned int * value); +typedef const char * (APIENTRYP PFNGLXQUERYCURRENTRENDERERSTRINGMESAPROC)(int attribute); +typedef glbind_Bool (APIENTRYP PFNGLXQUERYRENDERERINTEGERMESAPROC)(glbind_Display* dpy, int screen, int renderer, int attribute, unsigned int * value); +typedef const char * (APIENTRYP PFNGLXQUERYRENDERERSTRINGMESAPROC)(glbind_Display* dpy, int screen, int renderer, int attribute); +#endif /* GLX_MESA_query_renderer */ + +#ifndef GLX_MESA_release_buffers +#define GLX_MESA_release_buffers 1 +typedef glbind_Bool (APIENTRYP PFNGLXRELEASEBUFFERSMESAPROC)(glbind_Display* dpy, GLXDrawable drawable); +#endif /* GLX_MESA_release_buffers */ + +#ifndef GLX_MESA_set_3dfx_mode +#define GLX_MESA_set_3dfx_mode 1 +#define GLX_3DFX_WINDOW_MODE_MESA 0x1 +#define GLX_3DFX_FULLSCREEN_MODE_MESA 0x2 +typedef GLboolean (APIENTRYP PFNGLXSET3DFXMODEMESAPROC)(GLint mode); +#endif /* GLX_MESA_set_3dfx_mode */ + +#ifndef GLX_MESA_swap_control +#define GLX_MESA_swap_control 1 +typedef int (APIENTRYP PFNGLXGETSWAPINTERVALMESAPROC)(void); +typedef int (APIENTRYP PFNGLXSWAPINTERVALMESAPROC)(unsigned int interval); +#endif /* GLX_MESA_swap_control */ + +#ifndef GLX_NV_copy_buffer +#define GLX_NV_copy_buffer 1 +typedef void (APIENTRYP PFNGLXCOPYBUFFERSUBDATANVPROC)(glbind_Display* dpy, GLXContext readCtx, GLXContext writeCtx, GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLXNAMEDCOPYBUFFERSUBDATANVPROC)(glbind_Display* dpy, GLXContext readCtx, GLXContext writeCtx, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +#endif /* GLX_NV_copy_buffer */ + +#ifndef GLX_NV_copy_image +#define GLX_NV_copy_image 1 +typedef void (APIENTRYP PFNGLXCOPYIMAGESUBDATANVPROC)(glbind_Display* dpy, GLXContext srcCtx, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLXContext dstCtx, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#endif /* GLX_NV_copy_image */ + +#ifndef GLX_NV_delay_before_swap +#define GLX_NV_delay_before_swap 1 +typedef glbind_Bool (APIENTRYP PFNGLXDELAYBEFORESWAPNVPROC)(glbind_Display* dpy, GLXDrawable drawable, GLfloat seconds); +#endif /* GLX_NV_delay_before_swap */ + +#ifndef GLX_NV_float_buffer +#define GLX_NV_float_buffer 1 +#define GLX_FLOAT_COMPONENTS_NV 0x20B0 +#endif /* GLX_NV_float_buffer */ + +#ifndef GLX_NV_multisample_coverage +#define GLX_NV_multisample_coverage 1 +#define GLX_COVERAGE_SAMPLES_NV 100001 +#define GLX_COLOR_SAMPLES_NV 0x20B3 +#endif /* GLX_NV_multisample_coverage */ + +#ifndef GLX_NV_present_video +#define GLX_NV_present_video 1 +#define GLX_NUM_VIDEO_SLOTS_NV 0x20F0 +typedef unsigned int * (APIENTRYP PFNGLXENUMERATEVIDEODEVICESNVPROC)(glbind_Display* dpy, int screen, int * nelements); +typedef int (APIENTRYP PFNGLXBINDVIDEODEVICENVPROC)(glbind_Display* dpy, unsigned int video_slot, unsigned int video_device, const int * attrib_list); +#endif /* GLX_NV_present_video */ + +#ifndef GLX_NV_robustness_video_memory_purge +#define GLX_NV_robustness_video_memory_purge 1 +#define GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x20F7 +#endif /* GLX_NV_robustness_video_memory_purge */ + +#ifndef GLX_NV_swap_group +#define GLX_NV_swap_group 1 +typedef glbind_Bool (APIENTRYP PFNGLXJOINSWAPGROUPNVPROC)(glbind_Display* dpy, GLXDrawable drawable, GLuint group); +typedef glbind_Bool (APIENTRYP PFNGLXBINDSWAPBARRIERNVPROC)(glbind_Display* dpy, GLuint group, GLuint barrier); +typedef glbind_Bool (APIENTRYP PFNGLXQUERYSWAPGROUPNVPROC)(glbind_Display* dpy, GLXDrawable drawable, GLuint * group, GLuint * barrier); +typedef glbind_Bool (APIENTRYP PFNGLXQUERYMAXSWAPGROUPSNVPROC)(glbind_Display* dpy, int screen, GLuint * maxGroups, GLuint * maxBarriers); +typedef glbind_Bool (APIENTRYP PFNGLXQUERYFRAMECOUNTNVPROC)(glbind_Display* dpy, int screen, GLuint * count); +typedef glbind_Bool (APIENTRYP PFNGLXRESETFRAMECOUNTNVPROC)(glbind_Display* dpy, int screen); +#endif /* GLX_NV_swap_group */ + +#ifndef GLX_NV_video_capture +#define GLX_NV_video_capture 1 +typedef glbind_XID GLXVideoCaptureDeviceNV; +#define GLX_DEVICE_ID_NV 0x20CD +#define GLX_UNIQUE_ID_NV 0x20CE +#define GLX_NUM_VIDEO_CAPTURE_SLOTS_NV 0x20CF +typedef int (APIENTRYP PFNGLXBINDVIDEOCAPTUREDEVICENVPROC)(glbind_Display* dpy, unsigned int video_capture_slot, GLXVideoCaptureDeviceNV device); +typedef GLXVideoCaptureDeviceNV * (APIENTRYP PFNGLXENUMERATEVIDEOCAPTUREDEVICESNVPROC)(glbind_Display* dpy, int screen, int * nelements); +typedef void (APIENTRYP PFNGLXLOCKVIDEOCAPTUREDEVICENVPROC)(glbind_Display* dpy, GLXVideoCaptureDeviceNV device); +typedef int (APIENTRYP PFNGLXQUERYVIDEOCAPTUREDEVICENVPROC)(glbind_Display* dpy, GLXVideoCaptureDeviceNV device, int attribute, int * value); +typedef void (APIENTRYP PFNGLXRELEASEVIDEOCAPTUREDEVICENVPROC)(glbind_Display* dpy, GLXVideoCaptureDeviceNV device); +#endif /* GLX_NV_video_capture */ + +#ifndef GLX_NV_video_out +#define GLX_NV_video_out 1 +typedef unsigned int GLXVideoDeviceNV; +#define GLX_VIDEO_OUT_COLOR_NV 0x20C3 +#define GLX_VIDEO_OUT_ALPHA_NV 0x20C4 +#define GLX_VIDEO_OUT_DEPTH_NV 0x20C5 +#define GLX_VIDEO_OUT_COLOR_AND_ALPHA_NV 0x20C6 +#define GLX_VIDEO_OUT_COLOR_AND_DEPTH_NV 0x20C7 +#define GLX_VIDEO_OUT_FRAME_NV 0x20C8 +#define GLX_VIDEO_OUT_FIELD_1_NV 0x20C9 +#define GLX_VIDEO_OUT_FIELD_2_NV 0x20CA +#define GLX_VIDEO_OUT_STACKED_FIELDS_1_2_NV 0x20CB +#define GLX_VIDEO_OUT_STACKED_FIELDS_2_1_NV 0x20CC +typedef int (APIENTRYP PFNGLXGETVIDEODEVICENVPROC)(glbind_Display* dpy, int screen, int numVideoDevices, GLXVideoDeviceNV * pVideoDevice); +typedef int (APIENTRYP PFNGLXRELEASEVIDEODEVICENVPROC)(glbind_Display* dpy, int screen, GLXVideoDeviceNV VideoDevice); +typedef int (APIENTRYP PFNGLXBINDVIDEOIMAGENVPROC)(glbind_Display* dpy, GLXVideoDeviceNV VideoDevice, GLXPbuffer pbuf, int iVideoBuffer); +typedef int (APIENTRYP PFNGLXRELEASEVIDEOIMAGENVPROC)(glbind_Display* dpy, GLXPbuffer pbuf); +typedef int (APIENTRYP PFNGLXSENDPBUFFERTOVIDEONVPROC)(glbind_Display* dpy, GLXPbuffer pbuf, int iBufferType, unsigned long * pulCounterPbuffer, GLboolean bBlock); +typedef int (APIENTRYP PFNGLXGETVIDEOINFONVPROC)(glbind_Display* dpy, int screen, GLXVideoDeviceNV VideoDevice, unsigned long * pulCounterOutputPbuffer, unsigned long * pulCounterOutputVideo); +#endif /* GLX_NV_video_out */ + +#ifndef GLX_OML_swap_method +#define GLX_OML_swap_method 1 +#define GLX_SWAP_METHOD_OML 0x8060 +#define GLX_SWAP_EXCHANGE_OML 0x8061 +#define GLX_SWAP_COPY_OML 0x8062 +#define GLX_SWAP_UNDEFINED_OML 0x8063 +#endif /* GLX_OML_swap_method */ + +#ifndef GLX_OML_sync_control +#define GLX_OML_sync_control 1 +typedef glbind_Bool (APIENTRYP PFNGLXGETSYNCVALUESOMLPROC)(glbind_Display* dpy, GLXDrawable drawable, int64_t * ust, int64_t * msc, int64_t * sbc); +typedef glbind_Bool (APIENTRYP PFNGLXGETMSCRATEOMLPROC)(glbind_Display* dpy, GLXDrawable drawable, int32_t * numerator, int32_t * denominator); +typedef int64_t (APIENTRYP PFNGLXSWAPBUFFERSMSCOMLPROC)(glbind_Display* dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder); +typedef glbind_Bool (APIENTRYP PFNGLXWAITFORMSCOMLPROC)(glbind_Display* dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t * ust, int64_t * msc, int64_t * sbc); +typedef glbind_Bool (APIENTRYP PFNGLXWAITFORSBCOMLPROC)(glbind_Display* dpy, GLXDrawable drawable, int64_t target_sbc, int64_t * ust, int64_t * msc, int64_t * sbc); +#endif /* GLX_OML_sync_control */ + +#ifndef GLX_SGI_cushion +#define GLX_SGI_cushion 1 +typedef void (APIENTRYP PFNGLXCUSHIONSGIPROC)(glbind_Display* dpy, glbind_Window window, float cushion); +#endif /* GLX_SGI_cushion */ + +#ifndef GLX_SGI_make_current_read +#define GLX_SGI_make_current_read 1 +typedef glbind_Bool (APIENTRYP PFNGLXMAKECURRENTREADSGIPROC)(glbind_Display* dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx); +typedef GLXDrawable (APIENTRYP PFNGLXGETCURRENTREADDRAWABLESGIPROC)(void); +#endif /* GLX_SGI_make_current_read */ + +#ifndef GLX_SGI_swap_control +#define GLX_SGI_swap_control 1 +typedef int (APIENTRYP PFNGLXSWAPINTERVALSGIPROC)(int interval); +#endif /* GLX_SGI_swap_control */ + +#ifndef GLX_SGI_video_sync +#define GLX_SGI_video_sync 1 +typedef int (APIENTRYP PFNGLXGETVIDEOSYNCSGIPROC)(unsigned int * count); +typedef int (APIENTRYP PFNGLXWAITVIDEOSYNCSGIPROC)(int divisor, int remainder, unsigned int * count); +#endif /* GLX_SGI_video_sync */ + +#ifndef GLX_SGIS_blended_overlay +#define GLX_SGIS_blended_overlay 1 +#define GLX_BLENDED_RGBA_SGIS 0x8025 +#endif /* GLX_SGIS_blended_overlay */ + +#ifndef GLX_SGIS_multisample +#define GLX_SGIS_multisample 1 +#define GLX_SAMPLE_BUFFERS_SGIS 100000 +#define GLX_SAMPLES_SGIS 100001 +#endif /* GLX_SGIS_multisample */ + +#ifndef GLX_SGIS_shared_multisample +#define GLX_SGIS_shared_multisample 1 +#define GLX_MULTISAMPLE_SUB_RECT_WIDTH_SGIS 0x8026 +#define GLX_MULTISAMPLE_SUB_RECT_HEIGHT_SGIS 0x8027 +#endif /* GLX_SGIS_shared_multisample */ + +#ifndef GLX_SGIX_fbconfig +#define GLX_SGIX_fbconfig 1 +typedef struct __GLXFBConfigRec *GLXFBConfigSGIX; +#define GLX_WINDOW_BIT_SGIX 0x00000001 +#define GLX_PIXMAP_BIT_SGIX 0x00000002 +#define GLX_RGBA_BIT_SGIX 0x00000001 +#define GLX_COLOR_INDEX_BIT_SGIX 0x00000002 +#define GLX_DRAWABLE_TYPE_SGIX 0x8010 +#define GLX_RENDER_TYPE_SGIX 0x8011 +#define GLX_X_RENDERABLE_SGIX 0x8012 +#define GLX_FBCONFIG_ID_SGIX 0x8013 +#define GLX_RGBA_TYPE_SGIX 0x8014 +#define GLX_COLOR_INDEX_TYPE_SGIX 0x8015 +#define GLX_SCREEN_EXT 0x800C +typedef int (APIENTRYP PFNGLXGETFBCONFIGATTRIBSGIXPROC)(glbind_Display* dpy, GLXFBConfigSGIX config, int attribute, int * value); +typedef GLXFBConfigSGIX * (APIENTRYP PFNGLXCHOOSEFBCONFIGSGIXPROC)(glbind_Display* dpy, int screen, int * attrib_list, int * nelements); +typedef GLXPixmap (APIENTRYP PFNGLXCREATEGLXPIXMAPWITHCONFIGSGIXPROC)(glbind_Display* dpy, GLXFBConfigSGIX config, glbind_Pixmap pixmap); +typedef GLXContext (APIENTRYP PFNGLXCREATECONTEXTWITHCONFIGSGIXPROC)(glbind_Display* dpy, GLXFBConfigSGIX config, int render_type, GLXContext share_list, glbind_Bool direct); +typedef glbind_XVisualInfo* (APIENTRYP PFNGLXGETVISUALFROMFBCONFIGSGIXPROC)(glbind_Display* dpy, GLXFBConfigSGIX config); +typedef GLXFBConfigSGIX (APIENTRYP PFNGLXGETFBCONFIGFROMVISUALSGIXPROC)(glbind_Display* dpy, glbind_XVisualInfo* vis); +#endif /* GLX_SGIX_fbconfig */ + +#ifndef GLX_SGIX_hyperpipe +#define GLX_SGIX_hyperpipe 1 +typedef struct { + char pipeName[80]; /* Should be [GLX_HYPERPIPE_PIPE_NAME_LENGTH_SGIX] */ + int networkId; +} GLXHyperpipeNetworkSGIX; +typedef struct { + char pipeName[80]; /* Should be [GLX_HYPERPIPE_PIPE_NAME_LENGTH_SGIX] */ + int channel; + unsigned int participationType; + int timeSlice; +} GLXHyperpipeConfigSGIX; +typedef struct { + char pipeName[80]; /* Should be [GLX_HYPERPIPE_PIPE_NAME_LENGTH_SGIX] */ + int srcXOrigin, srcYOrigin, srcWidth, srcHeight; + int destXOrigin, destYOrigin, destWidth, destHeight; +} GLXPipeRect; +typedef struct { + char pipeName[80]; /* Should be [GLX_HYPERPIPE_PIPE_NAME_LENGTH_SGIX] */ + int XOrigin, YOrigin, maxHeight, maxWidth; +} GLXPipeRectLimits; +#define GLX_HYPERPIPE_PIPE_NAME_LENGTH_SGIX 80 +#define GLX_BAD_HYPERPIPE_CONFIG_SGIX 91 +#define GLX_BAD_HYPERPIPE_SGIX 92 +#define GLX_HYPERPIPE_DISPLAY_PIPE_SGIX 0x00000001 +#define GLX_HYPERPIPE_RENDER_PIPE_SGIX 0x00000002 +#define GLX_PIPE_RECT_SGIX 0x00000001 +#define GLX_PIPE_RECT_LIMITS_SGIX 0x00000002 +#define GLX_HYPERPIPE_STEREO_SGIX 0x00000003 +#define GLX_HYPERPIPE_PIXEL_AVERAGE_SGIX 0x00000004 +#define GLX_HYPERPIPE_ID_SGIX 0x8030 +typedef GLXHyperpipeNetworkSGIX * (APIENTRYP PFNGLXQUERYHYPERPIPENETWORKSGIXPROC)(glbind_Display* dpy, int * npipes); +typedef int (APIENTRYP PFNGLXHYPERPIPECONFIGSGIXPROC)(glbind_Display* dpy, int networkId, int npipes, GLXHyperpipeConfigSGIX * cfg, int * hpId); +typedef GLXHyperpipeConfigSGIX * (APIENTRYP PFNGLXQUERYHYPERPIPECONFIGSGIXPROC)(glbind_Display* dpy, int hpId, int * npipes); +typedef int (APIENTRYP PFNGLXDESTROYHYPERPIPECONFIGSGIXPROC)(glbind_Display* dpy, int hpId); +typedef int (APIENTRYP PFNGLXBINDHYPERPIPESGIXPROC)(glbind_Display* dpy, int hpId); +typedef int (APIENTRYP PFNGLXQUERYHYPERPIPEBESTATTRIBSGIXPROC)(glbind_Display* dpy, int timeSlice, int attrib, int size, void * attribList, void * returnAttribList); +typedef int (APIENTRYP PFNGLXHYPERPIPEATTRIBSGIXPROC)(glbind_Display* dpy, int timeSlice, int attrib, int size, void * attribList); +typedef int (APIENTRYP PFNGLXQUERYHYPERPIPEATTRIBSGIXPROC)(glbind_Display* dpy, int timeSlice, int attrib, int size, void * returnAttribList); +#endif /* GLX_SGIX_hyperpipe */ + +#ifndef GLX_SGIX_pbuffer +#define GLX_SGIX_pbuffer 1 +typedef glbind_XID GLXPbufferSGIX; +#define GLX_PBUFFER_BIT_SGIX 0x00000004 +#define GLX_BUFFER_CLOBBER_MASK_SGIX 0x08000000 +#define GLX_FRONT_LEFT_BUFFER_BIT_SGIX 0x00000001 +#define GLX_FRONT_RIGHT_BUFFER_BIT_SGIX 0x00000002 +#define GLX_BACK_LEFT_BUFFER_BIT_SGIX 0x00000004 +#define GLX_BACK_RIGHT_BUFFER_BIT_SGIX 0x00000008 +#define GLX_AUX_BUFFERS_BIT_SGIX 0x00000010 +#define GLX_DEPTH_BUFFER_BIT_SGIX 0x00000020 +#define GLX_STENCIL_BUFFER_BIT_SGIX 0x00000040 +#define GLX_ACCUM_BUFFER_BIT_SGIX 0x00000080 +#define GLX_SAMPLE_BUFFERS_BIT_SGIX 0x00000100 +#define GLX_MAX_PBUFFER_WIDTH_SGIX 0x8016 +#define GLX_MAX_PBUFFER_HEIGHT_SGIX 0x8017 +#define GLX_MAX_PBUFFER_PIXELS_SGIX 0x8018 +#define GLX_OPTIMAL_PBUFFER_WIDTH_SGIX 0x8019 +#define GLX_OPTIMAL_PBUFFER_HEIGHT_SGIX 0x801A +#define GLX_PRESERVED_CONTENTS_SGIX 0x801B +#define GLX_LARGEST_PBUFFER_SGIX 0x801C +#define GLX_WIDTH_SGIX 0x801D +#define GLX_HEIGHT_SGIX 0x801E +#define GLX_EVENT_MASK_SGIX 0x801F +#define GLX_DAMAGED_SGIX 0x8020 +#define GLX_SAVED_SGIX 0x8021 +#define GLX_WINDOW_SGIX 0x8022 +#define GLX_PBUFFER_SGIX 0x8023 +typedef GLXPbufferSGIX (APIENTRYP PFNGLXCREATEGLXPBUFFERSGIXPROC)(glbind_Display* dpy, GLXFBConfigSGIX config, unsigned int width, unsigned int height, int * attrib_list); +typedef void (APIENTRYP PFNGLXDESTROYGLXPBUFFERSGIXPROC)(glbind_Display* dpy, GLXPbufferSGIX pbuf); +typedef void (APIENTRYP PFNGLXQUERYGLXPBUFFERSGIXPROC)(glbind_Display* dpy, GLXPbufferSGIX pbuf, int attribute, unsigned int * value); +typedef void (APIENTRYP PFNGLXSELECTEVENTSGIXPROC)(glbind_Display* dpy, GLXDrawable drawable, unsigned long mask); +typedef void (APIENTRYP PFNGLXGETSELECTEDEVENTSGIXPROC)(glbind_Display* dpy, GLXDrawable drawable, unsigned long * mask); +#endif /* GLX_SGIX_pbuffer */ + +#ifndef GLX_SGIX_swap_barrier +#define GLX_SGIX_swap_barrier 1 +typedef void (APIENTRYP PFNGLXBINDSWAPBARRIERSGIXPROC)(glbind_Display* dpy, GLXDrawable drawable, int barrier); +typedef glbind_Bool (APIENTRYP PFNGLXQUERYMAXSWAPBARRIERSSGIXPROC)(glbind_Display* dpy, int screen, int * max); +#endif /* GLX_SGIX_swap_barrier */ + +#ifndef GLX_SGIX_swap_group +#define GLX_SGIX_swap_group 1 +typedef void (APIENTRYP PFNGLXJOINSWAPGROUPSGIXPROC)(glbind_Display* dpy, GLXDrawable drawable, GLXDrawable member); +#endif /* GLX_SGIX_swap_group */ + +#ifndef GLX_SGIX_video_resize +#define GLX_SGIX_video_resize 1 +#define GLX_SYNC_FRAME_SGIX 0x00000000 +#define GLX_SYNC_SWAP_SGIX 0x00000001 +typedef int (APIENTRYP PFNGLXBINDCHANNELTOWINDOWSGIXPROC)(glbind_Display* display, int screen, int channel, glbind_Window window); +typedef int (APIENTRYP PFNGLXCHANNELRECTSGIXPROC)(glbind_Display* display, int screen, int channel, int x, int y, int w, int h); +typedef int (APIENTRYP PFNGLXQUERYCHANNELRECTSGIXPROC)(glbind_Display* display, int screen, int channel, int * dx, int * dy, int * dw, int * dh); +typedef int (APIENTRYP PFNGLXQUERYCHANNELDELTASSGIXPROC)(glbind_Display* display, int screen, int channel, int * x, int * y, int * w, int * h); +typedef int (APIENTRYP PFNGLXCHANNELRECTSYNCSGIXPROC)(glbind_Display* display, int screen, int channel, GLenum synctype); +#endif /* GLX_SGIX_video_resize */ + +#ifndef GLX_SGIX_visual_select_group +#define GLX_SGIX_visual_select_group 1 +#define GLX_VISUAL_SELECT_GROUP_SGIX 0x8028 +#endif /* GLX_SGIX_visual_select_group */ + +#ifndef GLX_SUN_get_transparent_index +#define GLX_SUN_get_transparent_index 1 +typedef glbind_Status (APIENTRYP PFNGLXGETTRANSPARENTINDEXSUNPROC)(glbind_Display* dpy, glbind_Window overlay, glbind_Window underlay, unsigned long * pTransparentIndex); +#endif /* GLX_SUN_get_transparent_index */ + +#ifndef GLX_NV_multigpu_context +#define GLX_NV_multigpu_context 1 +#define GLX_CONTEXT_MULTIGPU_ATTRIB_NV 0x20AA +#define GLX_CONTEXT_MULTIGPU_ATTRIB_SINGLE_NV 0x20AB +#define GLX_CONTEXT_MULTIGPU_ATTRIB_AFR_NV 0x20AC +#define GLX_CONTEXT_MULTIGPU_ATTRIB_MULTICAST_NV 0x20AD +#define GLX_CONTEXT_MULTIGPU_ATTRIB_MULTI_DISPLAY_MULTICAST_NV 0x20AE +#endif /* GLX_NV_multigpu_context */ +#endif /* GLBIND_GLX */ + + +PFNGLCULLFACEPROC glCullFace; +PFNGLFRONTFACEPROC glFrontFace; +PFNGLHINTPROC glHint; +PFNGLLINEWIDTHPROC glLineWidth; +PFNGLPOINTSIZEPROC glPointSize; +PFNGLPOLYGONMODEPROC glPolygonMode; +PFNGLSCISSORPROC glScissor; +PFNGLTEXPARAMETERFPROC glTexParameterf; +PFNGLTEXPARAMETERFVPROC glTexParameterfv; +PFNGLTEXPARAMETERIPROC glTexParameteri; +PFNGLTEXPARAMETERIVPROC glTexParameteriv; +PFNGLTEXIMAGE1DPROC glTexImage1D; +PFNGLTEXIMAGE2DPROC glTexImage2D; +PFNGLDRAWBUFFERPROC glDrawBuffer; +PFNGLCLEARPROC glClear; +PFNGLCLEARCOLORPROC glClearColor; +PFNGLCLEARSTENCILPROC glClearStencil; +PFNGLCLEARDEPTHPROC glClearDepth; +PFNGLSTENCILMASKPROC glStencilMask; +PFNGLCOLORMASKPROC glColorMask; +PFNGLDEPTHMASKPROC glDepthMask; +PFNGLDISABLEPROC glDisable; +PFNGLENABLEPROC glEnable; +PFNGLFINISHPROC glFinish; +PFNGLFLUSHPROC glFlush; +PFNGLBLENDFUNCPROC glBlendFunc; +PFNGLLOGICOPPROC glLogicOp; +PFNGLSTENCILFUNCPROC glStencilFunc; +PFNGLSTENCILOPPROC glStencilOp; +PFNGLDEPTHFUNCPROC glDepthFunc; +PFNGLPIXELSTOREFPROC glPixelStoref; +PFNGLPIXELSTOREIPROC glPixelStorei; +PFNGLREADBUFFERPROC glReadBuffer; +PFNGLREADPIXELSPROC glReadPixels; +PFNGLGETBOOLEANVPROC glGetBooleanv; +PFNGLGETDOUBLEVPROC glGetDoublev; +PFNGLGETERRORPROC glGetError; +PFNGLGETFLOATVPROC glGetFloatv; +PFNGLGETINTEGERVPROC glGetIntegerv; +PFNGLGETSTRINGPROC glGetString; +PFNGLGETTEXIMAGEPROC glGetTexImage; +PFNGLGETTEXPARAMETERFVPROC glGetTexParameterfv; +PFNGLGETTEXPARAMETERIVPROC glGetTexParameteriv; +PFNGLGETTEXLEVELPARAMETERFVPROC glGetTexLevelParameterfv; +PFNGLGETTEXLEVELPARAMETERIVPROC glGetTexLevelParameteriv; +PFNGLISENABLEDPROC glIsEnabled; +PFNGLDEPTHRANGEPROC glDepthRange; +PFNGLVIEWPORTPROC glViewport; +PFNGLNEWLISTPROC glNewList; +PFNGLENDLISTPROC glEndList; +PFNGLCALLLISTPROC glCallList; +PFNGLCALLLISTSPROC glCallLists; +PFNGLDELETELISTSPROC glDeleteLists; +PFNGLGENLISTSPROC glGenLists; +PFNGLLISTBASEPROC glListBase; +PFNGLBEGINPROC glBegin; +PFNGLBITMAPPROC glBitmap; +PFNGLCOLOR3BPROC glColor3b; +PFNGLCOLOR3BVPROC glColor3bv; +PFNGLCOLOR3DPROC glColor3d; +PFNGLCOLOR3DVPROC glColor3dv; +PFNGLCOLOR3FPROC glColor3f; +PFNGLCOLOR3FVPROC glColor3fv; +PFNGLCOLOR3IPROC glColor3i; +PFNGLCOLOR3IVPROC glColor3iv; +PFNGLCOLOR3SPROC glColor3s; +PFNGLCOLOR3SVPROC glColor3sv; +PFNGLCOLOR3UBPROC glColor3ub; +PFNGLCOLOR3UBVPROC glColor3ubv; +PFNGLCOLOR3UIPROC glColor3ui; +PFNGLCOLOR3UIVPROC glColor3uiv; +PFNGLCOLOR3USPROC glColor3us; +PFNGLCOLOR3USVPROC glColor3usv; +PFNGLCOLOR4BPROC glColor4b; +PFNGLCOLOR4BVPROC glColor4bv; +PFNGLCOLOR4DPROC glColor4d; +PFNGLCOLOR4DVPROC glColor4dv; +PFNGLCOLOR4FPROC glColor4f; +PFNGLCOLOR4FVPROC glColor4fv; +PFNGLCOLOR4IPROC glColor4i; +PFNGLCOLOR4IVPROC glColor4iv; +PFNGLCOLOR4SPROC glColor4s; +PFNGLCOLOR4SVPROC glColor4sv; +PFNGLCOLOR4UBPROC glColor4ub; +PFNGLCOLOR4UBVPROC glColor4ubv; +PFNGLCOLOR4UIPROC glColor4ui; +PFNGLCOLOR4UIVPROC glColor4uiv; +PFNGLCOLOR4USPROC glColor4us; +PFNGLCOLOR4USVPROC glColor4usv; +PFNGLEDGEFLAGPROC glEdgeFlag; +PFNGLEDGEFLAGVPROC glEdgeFlagv; +PFNGLENDPROC glEnd; +PFNGLINDEXDPROC glIndexd; +PFNGLINDEXDVPROC glIndexdv; +PFNGLINDEXFPROC glIndexf; +PFNGLINDEXFVPROC glIndexfv; +PFNGLINDEXIPROC glIndexi; +PFNGLINDEXIVPROC glIndexiv; +PFNGLINDEXSPROC glIndexs; +PFNGLINDEXSVPROC glIndexsv; +PFNGLNORMAL3BPROC glNormal3b; +PFNGLNORMAL3BVPROC glNormal3bv; +PFNGLNORMAL3DPROC glNormal3d; +PFNGLNORMAL3DVPROC glNormal3dv; +PFNGLNORMAL3FPROC glNormal3f; +PFNGLNORMAL3FVPROC glNormal3fv; +PFNGLNORMAL3IPROC glNormal3i; +PFNGLNORMAL3IVPROC glNormal3iv; +PFNGLNORMAL3SPROC glNormal3s; +PFNGLNORMAL3SVPROC glNormal3sv; +PFNGLRASTERPOS2DPROC glRasterPos2d; +PFNGLRASTERPOS2DVPROC glRasterPos2dv; +PFNGLRASTERPOS2FPROC glRasterPos2f; +PFNGLRASTERPOS2FVPROC glRasterPos2fv; +PFNGLRASTERPOS2IPROC glRasterPos2i; +PFNGLRASTERPOS2IVPROC glRasterPos2iv; +PFNGLRASTERPOS2SPROC glRasterPos2s; +PFNGLRASTERPOS2SVPROC glRasterPos2sv; +PFNGLRASTERPOS3DPROC glRasterPos3d; +PFNGLRASTERPOS3DVPROC glRasterPos3dv; +PFNGLRASTERPOS3FPROC glRasterPos3f; +PFNGLRASTERPOS3FVPROC glRasterPos3fv; +PFNGLRASTERPOS3IPROC glRasterPos3i; +PFNGLRASTERPOS3IVPROC glRasterPos3iv; +PFNGLRASTERPOS3SPROC glRasterPos3s; +PFNGLRASTERPOS3SVPROC glRasterPos3sv; +PFNGLRASTERPOS4DPROC glRasterPos4d; +PFNGLRASTERPOS4DVPROC glRasterPos4dv; +PFNGLRASTERPOS4FPROC glRasterPos4f; +PFNGLRASTERPOS4FVPROC glRasterPos4fv; +PFNGLRASTERPOS4IPROC glRasterPos4i; +PFNGLRASTERPOS4IVPROC glRasterPos4iv; +PFNGLRASTERPOS4SPROC glRasterPos4s; +PFNGLRASTERPOS4SVPROC glRasterPos4sv; +PFNGLRECTDPROC glRectd; +PFNGLRECTDVPROC glRectdv; +PFNGLRECTFPROC glRectf; +PFNGLRECTFVPROC glRectfv; +PFNGLRECTIPROC glRecti; +PFNGLRECTIVPROC glRectiv; +PFNGLRECTSPROC glRects; +PFNGLRECTSVPROC glRectsv; +PFNGLTEXCOORD1DPROC glTexCoord1d; +PFNGLTEXCOORD1DVPROC glTexCoord1dv; +PFNGLTEXCOORD1FPROC glTexCoord1f; +PFNGLTEXCOORD1FVPROC glTexCoord1fv; +PFNGLTEXCOORD1IPROC glTexCoord1i; +PFNGLTEXCOORD1IVPROC glTexCoord1iv; +PFNGLTEXCOORD1SPROC glTexCoord1s; +PFNGLTEXCOORD1SVPROC glTexCoord1sv; +PFNGLTEXCOORD2DPROC glTexCoord2d; +PFNGLTEXCOORD2DVPROC glTexCoord2dv; +PFNGLTEXCOORD2FPROC glTexCoord2f; +PFNGLTEXCOORD2FVPROC glTexCoord2fv; +PFNGLTEXCOORD2IPROC glTexCoord2i; +PFNGLTEXCOORD2IVPROC glTexCoord2iv; +PFNGLTEXCOORD2SPROC glTexCoord2s; +PFNGLTEXCOORD2SVPROC glTexCoord2sv; +PFNGLTEXCOORD3DPROC glTexCoord3d; +PFNGLTEXCOORD3DVPROC glTexCoord3dv; +PFNGLTEXCOORD3FPROC glTexCoord3f; +PFNGLTEXCOORD3FVPROC glTexCoord3fv; +PFNGLTEXCOORD3IPROC glTexCoord3i; +PFNGLTEXCOORD3IVPROC glTexCoord3iv; +PFNGLTEXCOORD3SPROC glTexCoord3s; +PFNGLTEXCOORD3SVPROC glTexCoord3sv; +PFNGLTEXCOORD4DPROC glTexCoord4d; +PFNGLTEXCOORD4DVPROC glTexCoord4dv; +PFNGLTEXCOORD4FPROC glTexCoord4f; +PFNGLTEXCOORD4FVPROC glTexCoord4fv; +PFNGLTEXCOORD4IPROC glTexCoord4i; +PFNGLTEXCOORD4IVPROC glTexCoord4iv; +PFNGLTEXCOORD4SPROC glTexCoord4s; +PFNGLTEXCOORD4SVPROC glTexCoord4sv; +PFNGLVERTEX2DPROC glVertex2d; +PFNGLVERTEX2DVPROC glVertex2dv; +PFNGLVERTEX2FPROC glVertex2f; +PFNGLVERTEX2FVPROC glVertex2fv; +PFNGLVERTEX2IPROC glVertex2i; +PFNGLVERTEX2IVPROC glVertex2iv; +PFNGLVERTEX2SPROC glVertex2s; +PFNGLVERTEX2SVPROC glVertex2sv; +PFNGLVERTEX3DPROC glVertex3d; +PFNGLVERTEX3DVPROC glVertex3dv; +PFNGLVERTEX3FPROC glVertex3f; +PFNGLVERTEX3FVPROC glVertex3fv; +PFNGLVERTEX3IPROC glVertex3i; +PFNGLVERTEX3IVPROC glVertex3iv; +PFNGLVERTEX3SPROC glVertex3s; +PFNGLVERTEX3SVPROC glVertex3sv; +PFNGLVERTEX4DPROC glVertex4d; +PFNGLVERTEX4DVPROC glVertex4dv; +PFNGLVERTEX4FPROC glVertex4f; +PFNGLVERTEX4FVPROC glVertex4fv; +PFNGLVERTEX4IPROC glVertex4i; +PFNGLVERTEX4IVPROC glVertex4iv; +PFNGLVERTEX4SPROC glVertex4s; +PFNGLVERTEX4SVPROC glVertex4sv; +PFNGLCLIPPLANEPROC glClipPlane; +PFNGLCOLORMATERIALPROC glColorMaterial; +PFNGLFOGFPROC glFogf; +PFNGLFOGFVPROC glFogfv; +PFNGLFOGIPROC glFogi; +PFNGLFOGIVPROC glFogiv; +PFNGLLIGHTFPROC glLightf; +PFNGLLIGHTFVPROC glLightfv; +PFNGLLIGHTIPROC glLighti; +PFNGLLIGHTIVPROC glLightiv; +PFNGLLIGHTMODELFPROC glLightModelf; +PFNGLLIGHTMODELFVPROC glLightModelfv; +PFNGLLIGHTMODELIPROC glLightModeli; +PFNGLLIGHTMODELIVPROC glLightModeliv; +PFNGLLINESTIPPLEPROC glLineStipple; +PFNGLMATERIALFPROC glMaterialf; +PFNGLMATERIALFVPROC glMaterialfv; +PFNGLMATERIALIPROC glMateriali; +PFNGLMATERIALIVPROC glMaterialiv; +PFNGLPOLYGONSTIPPLEPROC glPolygonStipple; +PFNGLSHADEMODELPROC glShadeModel; +PFNGLTEXENVFPROC glTexEnvf; +PFNGLTEXENVFVPROC glTexEnvfv; +PFNGLTEXENVIPROC glTexEnvi; +PFNGLTEXENVIVPROC glTexEnviv; +PFNGLTEXGENDPROC glTexGend; +PFNGLTEXGENDVPROC glTexGendv; +PFNGLTEXGENFPROC glTexGenf; +PFNGLTEXGENFVPROC glTexGenfv; +PFNGLTEXGENIPROC glTexGeni; +PFNGLTEXGENIVPROC glTexGeniv; +PFNGLFEEDBACKBUFFERPROC glFeedbackBuffer; +PFNGLSELECTBUFFERPROC glSelectBuffer; +PFNGLRENDERMODEPROC glRenderMode; +PFNGLINITNAMESPROC glInitNames; +PFNGLLOADNAMEPROC glLoadName; +PFNGLPASSTHROUGHPROC glPassThrough; +PFNGLPOPNAMEPROC glPopName; +PFNGLPUSHNAMEPROC glPushName; +PFNGLCLEARACCUMPROC glClearAccum; +PFNGLCLEARINDEXPROC glClearIndex; +PFNGLINDEXMASKPROC glIndexMask; +PFNGLACCUMPROC glAccum; +PFNGLPOPATTRIBPROC glPopAttrib; +PFNGLPUSHATTRIBPROC glPushAttrib; +PFNGLMAP1DPROC glMap1d; +PFNGLMAP1FPROC glMap1f; +PFNGLMAP2DPROC glMap2d; +PFNGLMAP2FPROC glMap2f; +PFNGLMAPGRID1DPROC glMapGrid1d; +PFNGLMAPGRID1FPROC glMapGrid1f; +PFNGLMAPGRID2DPROC glMapGrid2d; +PFNGLMAPGRID2FPROC glMapGrid2f; +PFNGLEVALCOORD1DPROC glEvalCoord1d; +PFNGLEVALCOORD1DVPROC glEvalCoord1dv; +PFNGLEVALCOORD1FPROC glEvalCoord1f; +PFNGLEVALCOORD1FVPROC glEvalCoord1fv; +PFNGLEVALCOORD2DPROC glEvalCoord2d; +PFNGLEVALCOORD2DVPROC glEvalCoord2dv; +PFNGLEVALCOORD2FPROC glEvalCoord2f; +PFNGLEVALCOORD2FVPROC glEvalCoord2fv; +PFNGLEVALMESH1PROC glEvalMesh1; +PFNGLEVALPOINT1PROC glEvalPoint1; +PFNGLEVALMESH2PROC glEvalMesh2; +PFNGLEVALPOINT2PROC glEvalPoint2; +PFNGLALPHAFUNCPROC glAlphaFunc; +PFNGLPIXELZOOMPROC glPixelZoom; +PFNGLPIXELTRANSFERFPROC glPixelTransferf; +PFNGLPIXELTRANSFERIPROC glPixelTransferi; +PFNGLPIXELMAPFVPROC glPixelMapfv; +PFNGLPIXELMAPUIVPROC glPixelMapuiv; +PFNGLPIXELMAPUSVPROC glPixelMapusv; +PFNGLCOPYPIXELSPROC glCopyPixels; +PFNGLDRAWPIXELSPROC glDrawPixels; +PFNGLGETCLIPPLANEPROC glGetClipPlane; +PFNGLGETLIGHTFVPROC glGetLightfv; +PFNGLGETLIGHTIVPROC glGetLightiv; +PFNGLGETMAPDVPROC glGetMapdv; +PFNGLGETMAPFVPROC glGetMapfv; +PFNGLGETMAPIVPROC glGetMapiv; +PFNGLGETMATERIALFVPROC glGetMaterialfv; +PFNGLGETMATERIALIVPROC glGetMaterialiv; +PFNGLGETPIXELMAPFVPROC glGetPixelMapfv; +PFNGLGETPIXELMAPUIVPROC glGetPixelMapuiv; +PFNGLGETPIXELMAPUSVPROC glGetPixelMapusv; +PFNGLGETPOLYGONSTIPPLEPROC glGetPolygonStipple; +PFNGLGETTEXENVFVPROC glGetTexEnvfv; +PFNGLGETTEXENVIVPROC glGetTexEnviv; +PFNGLGETTEXGENDVPROC glGetTexGendv; +PFNGLGETTEXGENFVPROC glGetTexGenfv; +PFNGLGETTEXGENIVPROC glGetTexGeniv; +PFNGLISLISTPROC glIsList; +PFNGLFRUSTUMPROC glFrustum; +PFNGLLOADIDENTITYPROC glLoadIdentity; +PFNGLLOADMATRIXFPROC glLoadMatrixf; +PFNGLLOADMATRIXDPROC glLoadMatrixd; +PFNGLMATRIXMODEPROC glMatrixMode; +PFNGLMULTMATRIXFPROC glMultMatrixf; +PFNGLMULTMATRIXDPROC glMultMatrixd; +PFNGLORTHOPROC glOrtho; +PFNGLPOPMATRIXPROC glPopMatrix; +PFNGLPUSHMATRIXPROC glPushMatrix; +PFNGLROTATEDPROC glRotated; +PFNGLROTATEFPROC glRotatef; +PFNGLSCALEDPROC glScaled; +PFNGLSCALEFPROC glScalef; +PFNGLTRANSLATEDPROC glTranslated; +PFNGLTRANSLATEFPROC glTranslatef; +PFNGLDRAWARRAYSPROC glDrawArrays; +PFNGLDRAWELEMENTSPROC glDrawElements; +PFNGLGETPOINTERVPROC glGetPointerv; +PFNGLPOLYGONOFFSETPROC glPolygonOffset; +PFNGLCOPYTEXIMAGE1DPROC glCopyTexImage1D; +PFNGLCOPYTEXIMAGE2DPROC glCopyTexImage2D; +PFNGLCOPYTEXSUBIMAGE1DPROC glCopyTexSubImage1D; +PFNGLCOPYTEXSUBIMAGE2DPROC glCopyTexSubImage2D; +PFNGLTEXSUBIMAGE1DPROC glTexSubImage1D; +PFNGLTEXSUBIMAGE2DPROC glTexSubImage2D; +PFNGLBINDTEXTUREPROC glBindTexture; +PFNGLDELETETEXTURESPROC glDeleteTextures; +PFNGLGENTEXTURESPROC glGenTextures; +PFNGLISTEXTUREPROC glIsTexture; +PFNGLARRAYELEMENTPROC glArrayElement; +PFNGLCOLORPOINTERPROC glColorPointer; +PFNGLDISABLECLIENTSTATEPROC glDisableClientState; +PFNGLEDGEFLAGPOINTERPROC glEdgeFlagPointer; +PFNGLENABLECLIENTSTATEPROC glEnableClientState; +PFNGLINDEXPOINTERPROC glIndexPointer; +PFNGLINTERLEAVEDARRAYSPROC glInterleavedArrays; +PFNGLNORMALPOINTERPROC glNormalPointer; +PFNGLTEXCOORDPOINTERPROC glTexCoordPointer; +PFNGLVERTEXPOINTERPROC glVertexPointer; +PFNGLARETEXTURESRESIDENTPROC glAreTexturesResident; +PFNGLPRIORITIZETEXTURESPROC glPrioritizeTextures; +PFNGLINDEXUBPROC glIndexub; +PFNGLINDEXUBVPROC glIndexubv; +PFNGLPOPCLIENTATTRIBPROC glPopClientAttrib; +PFNGLPUSHCLIENTATTRIBPROC glPushClientAttrib; +PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements; +PFNGLTEXIMAGE3DPROC glTexImage3D; +PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D; +PFNGLCOPYTEXSUBIMAGE3DPROC glCopyTexSubImage3D; +PFNGLACTIVETEXTUREPROC glActiveTexture; +PFNGLSAMPLECOVERAGEPROC glSampleCoverage; +PFNGLCOMPRESSEDTEXIMAGE3DPROC glCompressedTexImage3D; +PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D; +PFNGLCOMPRESSEDTEXIMAGE1DPROC glCompressedTexImage1D; +PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glCompressedTexSubImage3D; +PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glCompressedTexSubImage2D; +PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glCompressedTexSubImage1D; +PFNGLGETCOMPRESSEDTEXIMAGEPROC glGetCompressedTexImage; +PFNGLCLIENTACTIVETEXTUREPROC glClientActiveTexture; +PFNGLMULTITEXCOORD1DPROC glMultiTexCoord1d; +PFNGLMULTITEXCOORD1DVPROC glMultiTexCoord1dv; +PFNGLMULTITEXCOORD1FPROC glMultiTexCoord1f; +PFNGLMULTITEXCOORD1FVPROC glMultiTexCoord1fv; +PFNGLMULTITEXCOORD1IPROC glMultiTexCoord1i; +PFNGLMULTITEXCOORD1IVPROC glMultiTexCoord1iv; +PFNGLMULTITEXCOORD1SPROC glMultiTexCoord1s; +PFNGLMULTITEXCOORD1SVPROC glMultiTexCoord1sv; +PFNGLMULTITEXCOORD2DPROC glMultiTexCoord2d; +PFNGLMULTITEXCOORD2DVPROC glMultiTexCoord2dv; +PFNGLMULTITEXCOORD2FPROC glMultiTexCoord2f; +PFNGLMULTITEXCOORD2FVPROC glMultiTexCoord2fv; +PFNGLMULTITEXCOORD2IPROC glMultiTexCoord2i; +PFNGLMULTITEXCOORD2IVPROC glMultiTexCoord2iv; +PFNGLMULTITEXCOORD2SPROC glMultiTexCoord2s; +PFNGLMULTITEXCOORD2SVPROC glMultiTexCoord2sv; +PFNGLMULTITEXCOORD3DPROC glMultiTexCoord3d; +PFNGLMULTITEXCOORD3DVPROC glMultiTexCoord3dv; +PFNGLMULTITEXCOORD3FPROC glMultiTexCoord3f; +PFNGLMULTITEXCOORD3FVPROC glMultiTexCoord3fv; +PFNGLMULTITEXCOORD3IPROC glMultiTexCoord3i; +PFNGLMULTITEXCOORD3IVPROC glMultiTexCoord3iv; +PFNGLMULTITEXCOORD3SPROC glMultiTexCoord3s; +PFNGLMULTITEXCOORD3SVPROC glMultiTexCoord3sv; +PFNGLMULTITEXCOORD4DPROC glMultiTexCoord4d; +PFNGLMULTITEXCOORD4DVPROC glMultiTexCoord4dv; +PFNGLMULTITEXCOORD4FPROC glMultiTexCoord4f; +PFNGLMULTITEXCOORD4FVPROC glMultiTexCoord4fv; +PFNGLMULTITEXCOORD4IPROC glMultiTexCoord4i; +PFNGLMULTITEXCOORD4IVPROC glMultiTexCoord4iv; +PFNGLMULTITEXCOORD4SPROC glMultiTexCoord4s; +PFNGLMULTITEXCOORD4SVPROC glMultiTexCoord4sv; +PFNGLLOADTRANSPOSEMATRIXFPROC glLoadTransposeMatrixf; +PFNGLLOADTRANSPOSEMATRIXDPROC glLoadTransposeMatrixd; +PFNGLMULTTRANSPOSEMATRIXFPROC glMultTransposeMatrixf; +PFNGLMULTTRANSPOSEMATRIXDPROC glMultTransposeMatrixd; +PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate; +PFNGLMULTIDRAWARRAYSPROC glMultiDrawArrays; +PFNGLMULTIDRAWELEMENTSPROC glMultiDrawElements; +PFNGLPOINTPARAMETERFPROC glPointParameterf; +PFNGLPOINTPARAMETERFVPROC glPointParameterfv; +PFNGLPOINTPARAMETERIPROC glPointParameteri; +PFNGLPOINTPARAMETERIVPROC glPointParameteriv; +PFNGLFOGCOORDFPROC glFogCoordf; +PFNGLFOGCOORDFVPROC glFogCoordfv; +PFNGLFOGCOORDDPROC glFogCoordd; +PFNGLFOGCOORDDVPROC glFogCoorddv; +PFNGLFOGCOORDPOINTERPROC glFogCoordPointer; +PFNGLSECONDARYCOLOR3BPROC glSecondaryColor3b; +PFNGLSECONDARYCOLOR3BVPROC glSecondaryColor3bv; +PFNGLSECONDARYCOLOR3DPROC glSecondaryColor3d; +PFNGLSECONDARYCOLOR3DVPROC glSecondaryColor3dv; +PFNGLSECONDARYCOLOR3FPROC glSecondaryColor3f; +PFNGLSECONDARYCOLOR3FVPROC glSecondaryColor3fv; +PFNGLSECONDARYCOLOR3IPROC glSecondaryColor3i; +PFNGLSECONDARYCOLOR3IVPROC glSecondaryColor3iv; +PFNGLSECONDARYCOLOR3SPROC glSecondaryColor3s; +PFNGLSECONDARYCOLOR3SVPROC glSecondaryColor3sv; +PFNGLSECONDARYCOLOR3UBPROC glSecondaryColor3ub; +PFNGLSECONDARYCOLOR3UBVPROC glSecondaryColor3ubv; +PFNGLSECONDARYCOLOR3UIPROC glSecondaryColor3ui; +PFNGLSECONDARYCOLOR3UIVPROC glSecondaryColor3uiv; +PFNGLSECONDARYCOLOR3USPROC glSecondaryColor3us; +PFNGLSECONDARYCOLOR3USVPROC glSecondaryColor3usv; +PFNGLSECONDARYCOLORPOINTERPROC glSecondaryColorPointer; +PFNGLWINDOWPOS2DPROC glWindowPos2d; +PFNGLWINDOWPOS2DVPROC glWindowPos2dv; +PFNGLWINDOWPOS2FPROC glWindowPos2f; +PFNGLWINDOWPOS2FVPROC glWindowPos2fv; +PFNGLWINDOWPOS2IPROC glWindowPos2i; +PFNGLWINDOWPOS2IVPROC glWindowPos2iv; +PFNGLWINDOWPOS2SPROC glWindowPos2s; +PFNGLWINDOWPOS2SVPROC glWindowPos2sv; +PFNGLWINDOWPOS3DPROC glWindowPos3d; +PFNGLWINDOWPOS3DVPROC glWindowPos3dv; +PFNGLWINDOWPOS3FPROC glWindowPos3f; +PFNGLWINDOWPOS3FVPROC glWindowPos3fv; +PFNGLWINDOWPOS3IPROC glWindowPos3i; +PFNGLWINDOWPOS3IVPROC glWindowPos3iv; +PFNGLWINDOWPOS3SPROC glWindowPos3s; +PFNGLWINDOWPOS3SVPROC glWindowPos3sv; +PFNGLBLENDCOLORPROC glBlendColor; +PFNGLBLENDEQUATIONPROC glBlendEquation; +PFNGLGENQUERIESPROC glGenQueries; +PFNGLDELETEQUERIESPROC glDeleteQueries; +PFNGLISQUERYPROC glIsQuery; +PFNGLBEGINQUERYPROC glBeginQuery; +PFNGLENDQUERYPROC glEndQuery; +PFNGLGETQUERYIVPROC glGetQueryiv; +PFNGLGETQUERYOBJECTIVPROC glGetQueryObjectiv; +PFNGLGETQUERYOBJECTUIVPROC glGetQueryObjectuiv; +PFNGLBINDBUFFERPROC glBindBuffer; +PFNGLDELETEBUFFERSPROC glDeleteBuffers; +PFNGLGENBUFFERSPROC glGenBuffers; +PFNGLISBUFFERPROC glIsBuffer; +PFNGLBUFFERDATAPROC glBufferData; +PFNGLBUFFERSUBDATAPROC glBufferSubData; +PFNGLGETBUFFERSUBDATAPROC glGetBufferSubData; +PFNGLMAPBUFFERPROC glMapBuffer; +PFNGLUNMAPBUFFERPROC glUnmapBuffer; +PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv; +PFNGLGETBUFFERPOINTERVPROC glGetBufferPointerv; +PFNGLBLENDEQUATIONSEPARATEPROC glBlendEquationSeparate; +PFNGLDRAWBUFFERSPROC glDrawBuffers; +PFNGLSTENCILOPSEPARATEPROC glStencilOpSeparate; +PFNGLSTENCILFUNCSEPARATEPROC glStencilFuncSeparate; +PFNGLSTENCILMASKSEPARATEPROC glStencilMaskSeparate; +PFNGLATTACHSHADERPROC glAttachShader; +PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation; +PFNGLCOMPILESHADERPROC glCompileShader; +PFNGLCREATEPROGRAMPROC glCreateProgram; +PFNGLCREATESHADERPROC glCreateShader; +PFNGLDELETEPROGRAMPROC glDeleteProgram; +PFNGLDELETESHADERPROC glDeleteShader; +PFNGLDETACHSHADERPROC glDetachShader; +PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray; +PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; +PFNGLGETACTIVEATTRIBPROC glGetActiveAttrib; +PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform; +PFNGLGETATTACHEDSHADERSPROC glGetAttachedShaders; +PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation; +PFNGLGETPROGRAMIVPROC glGetProgramiv; +PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; +PFNGLGETSHADERIVPROC glGetShaderiv; +PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; +PFNGLGETSHADERSOURCEPROC glGetShaderSource; +PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; +PFNGLGETUNIFORMFVPROC glGetUniformfv; +PFNGLGETUNIFORMIVPROC glGetUniformiv; +PFNGLGETVERTEXATTRIBDVPROC glGetVertexAttribdv; +PFNGLGETVERTEXATTRIBFVPROC glGetVertexAttribfv; +PFNGLGETVERTEXATTRIBIVPROC glGetVertexAttribiv; +PFNGLGETVERTEXATTRIBPOINTERVPROC glGetVertexAttribPointerv; +PFNGLISPROGRAMPROC glIsProgram; +PFNGLISSHADERPROC glIsShader; +PFNGLLINKPROGRAMPROC glLinkProgram; +PFNGLSHADERSOURCEPROC glShaderSource; +PFNGLUSEPROGRAMPROC glUseProgram; +PFNGLUNIFORM1FPROC glUniform1f; +PFNGLUNIFORM2FPROC glUniform2f; +PFNGLUNIFORM3FPROC glUniform3f; +PFNGLUNIFORM4FPROC glUniform4f; +PFNGLUNIFORM1IPROC glUniform1i; +PFNGLUNIFORM2IPROC glUniform2i; +PFNGLUNIFORM3IPROC glUniform3i; +PFNGLUNIFORM4IPROC glUniform4i; +PFNGLUNIFORM1FVPROC glUniform1fv; +PFNGLUNIFORM2FVPROC glUniform2fv; +PFNGLUNIFORM3FVPROC glUniform3fv; +PFNGLUNIFORM4FVPROC glUniform4fv; +PFNGLUNIFORM1IVPROC glUniform1iv; +PFNGLUNIFORM2IVPROC glUniform2iv; +PFNGLUNIFORM3IVPROC glUniform3iv; +PFNGLUNIFORM4IVPROC glUniform4iv; +PFNGLUNIFORMMATRIX2FVPROC glUniformMatrix2fv; +PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv; +PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv; +PFNGLVALIDATEPROGRAMPROC glValidateProgram; +PFNGLVERTEXATTRIB1DPROC glVertexAttrib1d; +PFNGLVERTEXATTRIB1DVPROC glVertexAttrib1dv; +PFNGLVERTEXATTRIB1FPROC glVertexAttrib1f; +PFNGLVERTEXATTRIB1FVPROC glVertexAttrib1fv; +PFNGLVERTEXATTRIB1SPROC glVertexAttrib1s; +PFNGLVERTEXATTRIB1SVPROC glVertexAttrib1sv; +PFNGLVERTEXATTRIB2DPROC glVertexAttrib2d; +PFNGLVERTEXATTRIB2DVPROC glVertexAttrib2dv; +PFNGLVERTEXATTRIB2FPROC glVertexAttrib2f; +PFNGLVERTEXATTRIB2FVPROC glVertexAttrib2fv; +PFNGLVERTEXATTRIB2SPROC glVertexAttrib2s; +PFNGLVERTEXATTRIB2SVPROC glVertexAttrib2sv; +PFNGLVERTEXATTRIB3DPROC glVertexAttrib3d; +PFNGLVERTEXATTRIB3DVPROC glVertexAttrib3dv; +PFNGLVERTEXATTRIB3FPROC glVertexAttrib3f; +PFNGLVERTEXATTRIB3FVPROC glVertexAttrib3fv; +PFNGLVERTEXATTRIB3SPROC glVertexAttrib3s; +PFNGLVERTEXATTRIB3SVPROC glVertexAttrib3sv; +PFNGLVERTEXATTRIB4NBVPROC glVertexAttrib4Nbv; +PFNGLVERTEXATTRIB4NIVPROC glVertexAttrib4Niv; +PFNGLVERTEXATTRIB4NSVPROC glVertexAttrib4Nsv; +PFNGLVERTEXATTRIB4NUBPROC glVertexAttrib4Nub; +PFNGLVERTEXATTRIB4NUBVPROC glVertexAttrib4Nubv; +PFNGLVERTEXATTRIB4NUIVPROC glVertexAttrib4Nuiv; +PFNGLVERTEXATTRIB4NUSVPROC glVertexAttrib4Nusv; +PFNGLVERTEXATTRIB4BVPROC glVertexAttrib4bv; +PFNGLVERTEXATTRIB4DPROC glVertexAttrib4d; +PFNGLVERTEXATTRIB4DVPROC glVertexAttrib4dv; +PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f; +PFNGLVERTEXATTRIB4FVPROC glVertexAttrib4fv; +PFNGLVERTEXATTRIB4IVPROC glVertexAttrib4iv; +PFNGLVERTEXATTRIB4SPROC glVertexAttrib4s; +PFNGLVERTEXATTRIB4SVPROC glVertexAttrib4sv; +PFNGLVERTEXATTRIB4UBVPROC glVertexAttrib4ubv; +PFNGLVERTEXATTRIB4UIVPROC glVertexAttrib4uiv; +PFNGLVERTEXATTRIB4USVPROC glVertexAttrib4usv; +PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; +PFNGLUNIFORMMATRIX2X3FVPROC glUniformMatrix2x3fv; +PFNGLUNIFORMMATRIX3X2FVPROC glUniformMatrix3x2fv; +PFNGLUNIFORMMATRIX2X4FVPROC glUniformMatrix2x4fv; +PFNGLUNIFORMMATRIX4X2FVPROC glUniformMatrix4x2fv; +PFNGLUNIFORMMATRIX3X4FVPROC glUniformMatrix3x4fv; +PFNGLUNIFORMMATRIX4X3FVPROC glUniformMatrix4x3fv; +PFNGLCOLORMASKIPROC glColorMaski; +PFNGLGETBOOLEANI_VPROC glGetBooleani_v; +PFNGLGETINTEGERI_VPROC glGetIntegeri_v; +PFNGLENABLEIPROC glEnablei; +PFNGLDISABLEIPROC glDisablei; +PFNGLISENABLEDIPROC glIsEnabledi; +PFNGLBEGINTRANSFORMFEEDBACKPROC glBeginTransformFeedback; +PFNGLENDTRANSFORMFEEDBACKPROC glEndTransformFeedback; +PFNGLBINDBUFFERRANGEPROC glBindBufferRange; +PFNGLBINDBUFFERBASEPROC glBindBufferBase; +PFNGLTRANSFORMFEEDBACKVARYINGSPROC glTransformFeedbackVaryings; +PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glGetTransformFeedbackVarying; +PFNGLCLAMPCOLORPROC glClampColor; +PFNGLBEGINCONDITIONALRENDERPROC glBeginConditionalRender; +PFNGLENDCONDITIONALRENDERPROC glEndConditionalRender; +PFNGLVERTEXATTRIBIPOINTERPROC glVertexAttribIPointer; +PFNGLGETVERTEXATTRIBIIVPROC glGetVertexAttribIiv; +PFNGLGETVERTEXATTRIBIUIVPROC glGetVertexAttribIuiv; +PFNGLVERTEXATTRIBI1IPROC glVertexAttribI1i; +PFNGLVERTEXATTRIBI2IPROC glVertexAttribI2i; +PFNGLVERTEXATTRIBI3IPROC glVertexAttribI3i; +PFNGLVERTEXATTRIBI4IPROC glVertexAttribI4i; +PFNGLVERTEXATTRIBI1UIPROC glVertexAttribI1ui; +PFNGLVERTEXATTRIBI2UIPROC glVertexAttribI2ui; +PFNGLVERTEXATTRIBI3UIPROC glVertexAttribI3ui; +PFNGLVERTEXATTRIBI4UIPROC glVertexAttribI4ui; +PFNGLVERTEXATTRIBI1IVPROC glVertexAttribI1iv; +PFNGLVERTEXATTRIBI2IVPROC glVertexAttribI2iv; +PFNGLVERTEXATTRIBI3IVPROC glVertexAttribI3iv; +PFNGLVERTEXATTRIBI4IVPROC glVertexAttribI4iv; +PFNGLVERTEXATTRIBI1UIVPROC glVertexAttribI1uiv; +PFNGLVERTEXATTRIBI2UIVPROC glVertexAttribI2uiv; +PFNGLVERTEXATTRIBI3UIVPROC glVertexAttribI3uiv; +PFNGLVERTEXATTRIBI4UIVPROC glVertexAttribI4uiv; +PFNGLVERTEXATTRIBI4BVPROC glVertexAttribI4bv; +PFNGLVERTEXATTRIBI4SVPROC glVertexAttribI4sv; +PFNGLVERTEXATTRIBI4UBVPROC glVertexAttribI4ubv; +PFNGLVERTEXATTRIBI4USVPROC glVertexAttribI4usv; +PFNGLGETUNIFORMUIVPROC glGetUniformuiv; +PFNGLBINDFRAGDATALOCATIONPROC glBindFragDataLocation; +PFNGLGETFRAGDATALOCATIONPROC glGetFragDataLocation; +PFNGLUNIFORM1UIPROC glUniform1ui; +PFNGLUNIFORM2UIPROC glUniform2ui; +PFNGLUNIFORM3UIPROC glUniform3ui; +PFNGLUNIFORM4UIPROC glUniform4ui; +PFNGLUNIFORM1UIVPROC glUniform1uiv; +PFNGLUNIFORM2UIVPROC glUniform2uiv; +PFNGLUNIFORM3UIVPROC glUniform3uiv; +PFNGLUNIFORM4UIVPROC glUniform4uiv; +PFNGLTEXPARAMETERIIVPROC glTexParameterIiv; +PFNGLTEXPARAMETERIUIVPROC glTexParameterIuiv; +PFNGLGETTEXPARAMETERIIVPROC glGetTexParameterIiv; +PFNGLGETTEXPARAMETERIUIVPROC glGetTexParameterIuiv; +PFNGLCLEARBUFFERIVPROC glClearBufferiv; +PFNGLCLEARBUFFERUIVPROC glClearBufferuiv; +PFNGLCLEARBUFFERFVPROC glClearBufferfv; +PFNGLCLEARBUFFERFIPROC glClearBufferfi; +PFNGLGETSTRINGIPROC glGetStringi; +PFNGLISRENDERBUFFERPROC glIsRenderbuffer; +PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer; +PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers; +PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers; +PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage; +PFNGLGETRENDERBUFFERPARAMETERIVPROC glGetRenderbufferParameteriv; +PFNGLISFRAMEBUFFERPROC glIsFramebuffer; +PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer; +PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers; +PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers; +PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus; +PFNGLFRAMEBUFFERTEXTURE1DPROC glFramebufferTexture1D; +PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D; +PFNGLFRAMEBUFFERTEXTURE3DPROC glFramebufferTexture3D; +PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer; +PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetFramebufferAttachmentParameteriv; +PFNGLGENERATEMIPMAPPROC glGenerateMipmap; +PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer; +PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample; +PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer; +PFNGLMAPBUFFERRANGEPROC glMapBufferRange; +PFNGLFLUSHMAPPEDBUFFERRANGEPROC glFlushMappedBufferRange; +PFNGLBINDVERTEXARRAYPROC glBindVertexArray; +PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays; +PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; +PFNGLISVERTEXARRAYPROC glIsVertexArray; +PFNGLDRAWARRAYSINSTANCEDPROC glDrawArraysInstanced; +PFNGLDRAWELEMENTSINSTANCEDPROC glDrawElementsInstanced; +PFNGLTEXBUFFERPROC glTexBuffer; +PFNGLPRIMITIVERESTARTINDEXPROC glPrimitiveRestartIndex; +PFNGLCOPYBUFFERSUBDATAPROC glCopyBufferSubData; +PFNGLGETUNIFORMINDICESPROC glGetUniformIndices; +PFNGLGETACTIVEUNIFORMSIVPROC glGetActiveUniformsiv; +PFNGLGETACTIVEUNIFORMNAMEPROC glGetActiveUniformName; +PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex; +PFNGLGETACTIVEUNIFORMBLOCKIVPROC glGetActiveUniformBlockiv; +PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glGetActiveUniformBlockName; +PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding; +PFNGLDRAWELEMENTSBASEVERTEXPROC glDrawElementsBaseVertex; +PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glDrawRangeElementsBaseVertex; +PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glDrawElementsInstancedBaseVertex; +PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glMultiDrawElementsBaseVertex; +PFNGLPROVOKINGVERTEXPROC glProvokingVertex; +PFNGLFENCESYNCPROC glFenceSync; +PFNGLISSYNCPROC glIsSync; +PFNGLDELETESYNCPROC glDeleteSync; +PFNGLCLIENTWAITSYNCPROC glClientWaitSync; +PFNGLWAITSYNCPROC glWaitSync; +PFNGLGETINTEGER64VPROC glGetInteger64v; +PFNGLGETSYNCIVPROC glGetSynciv; +PFNGLGETINTEGER64I_VPROC glGetInteger64i_v; +PFNGLGETBUFFERPARAMETERI64VPROC glGetBufferParameteri64v; +PFNGLFRAMEBUFFERTEXTUREPROC glFramebufferTexture; +PFNGLTEXIMAGE2DMULTISAMPLEPROC glTexImage2DMultisample; +PFNGLTEXIMAGE3DMULTISAMPLEPROC glTexImage3DMultisample; +PFNGLGETMULTISAMPLEFVPROC glGetMultisamplefv; +PFNGLSAMPLEMASKIPROC glSampleMaski; +PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glBindFragDataLocationIndexed; +PFNGLGETFRAGDATAINDEXPROC glGetFragDataIndex; +PFNGLGENSAMPLERSPROC glGenSamplers; +PFNGLDELETESAMPLERSPROC glDeleteSamplers; +PFNGLISSAMPLERPROC glIsSampler; +PFNGLBINDSAMPLERPROC glBindSampler; +PFNGLSAMPLERPARAMETERIPROC glSamplerParameteri; +PFNGLSAMPLERPARAMETERIVPROC glSamplerParameteriv; +PFNGLSAMPLERPARAMETERFPROC glSamplerParameterf; +PFNGLSAMPLERPARAMETERFVPROC glSamplerParameterfv; +PFNGLSAMPLERPARAMETERIIVPROC glSamplerParameterIiv; +PFNGLSAMPLERPARAMETERIUIVPROC glSamplerParameterIuiv; +PFNGLGETSAMPLERPARAMETERIVPROC glGetSamplerParameteriv; +PFNGLGETSAMPLERPARAMETERIIVPROC glGetSamplerParameterIiv; +PFNGLGETSAMPLERPARAMETERFVPROC glGetSamplerParameterfv; +PFNGLGETSAMPLERPARAMETERIUIVPROC glGetSamplerParameterIuiv; +PFNGLQUERYCOUNTERPROC glQueryCounter; +PFNGLGETQUERYOBJECTI64VPROC glGetQueryObjecti64v; +PFNGLGETQUERYOBJECTUI64VPROC glGetQueryObjectui64v; +PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor; +PFNGLVERTEXATTRIBP1UIPROC glVertexAttribP1ui; +PFNGLVERTEXATTRIBP1UIVPROC glVertexAttribP1uiv; +PFNGLVERTEXATTRIBP2UIPROC glVertexAttribP2ui; +PFNGLVERTEXATTRIBP2UIVPROC glVertexAttribP2uiv; +PFNGLVERTEXATTRIBP3UIPROC glVertexAttribP3ui; +PFNGLVERTEXATTRIBP3UIVPROC glVertexAttribP3uiv; +PFNGLVERTEXATTRIBP4UIPROC glVertexAttribP4ui; +PFNGLVERTEXATTRIBP4UIVPROC glVertexAttribP4uiv; +PFNGLVERTEXP2UIPROC glVertexP2ui; +PFNGLVERTEXP2UIVPROC glVertexP2uiv; +PFNGLVERTEXP3UIPROC glVertexP3ui; +PFNGLVERTEXP3UIVPROC glVertexP3uiv; +PFNGLVERTEXP4UIPROC glVertexP4ui; +PFNGLVERTEXP4UIVPROC glVertexP4uiv; +PFNGLTEXCOORDP1UIPROC glTexCoordP1ui; +PFNGLTEXCOORDP1UIVPROC glTexCoordP1uiv; +PFNGLTEXCOORDP2UIPROC glTexCoordP2ui; +PFNGLTEXCOORDP2UIVPROC glTexCoordP2uiv; +PFNGLTEXCOORDP3UIPROC glTexCoordP3ui; +PFNGLTEXCOORDP3UIVPROC glTexCoordP3uiv; +PFNGLTEXCOORDP4UIPROC glTexCoordP4ui; +PFNGLTEXCOORDP4UIVPROC glTexCoordP4uiv; +PFNGLMULTITEXCOORDP1UIPROC glMultiTexCoordP1ui; +PFNGLMULTITEXCOORDP1UIVPROC glMultiTexCoordP1uiv; +PFNGLMULTITEXCOORDP2UIPROC glMultiTexCoordP2ui; +PFNGLMULTITEXCOORDP2UIVPROC glMultiTexCoordP2uiv; +PFNGLMULTITEXCOORDP3UIPROC glMultiTexCoordP3ui; +PFNGLMULTITEXCOORDP3UIVPROC glMultiTexCoordP3uiv; +PFNGLMULTITEXCOORDP4UIPROC glMultiTexCoordP4ui; +PFNGLMULTITEXCOORDP4UIVPROC glMultiTexCoordP4uiv; +PFNGLNORMALP3UIPROC glNormalP3ui; +PFNGLNORMALP3UIVPROC glNormalP3uiv; +PFNGLCOLORP3UIPROC glColorP3ui; +PFNGLCOLORP3UIVPROC glColorP3uiv; +PFNGLCOLORP4UIPROC glColorP4ui; +PFNGLCOLORP4UIVPROC glColorP4uiv; +PFNGLSECONDARYCOLORP3UIPROC glSecondaryColorP3ui; +PFNGLSECONDARYCOLORP3UIVPROC glSecondaryColorP3uiv; +PFNGLMINSAMPLESHADINGPROC glMinSampleShading; +PFNGLBLENDEQUATIONIPROC glBlendEquationi; +PFNGLBLENDEQUATIONSEPARATEIPROC glBlendEquationSeparatei; +PFNGLBLENDFUNCIPROC glBlendFunci; +PFNGLBLENDFUNCSEPARATEIPROC glBlendFuncSeparatei; +PFNGLDRAWARRAYSINDIRECTPROC glDrawArraysIndirect; +PFNGLDRAWELEMENTSINDIRECTPROC glDrawElementsIndirect; +PFNGLUNIFORM1DPROC glUniform1d; +PFNGLUNIFORM2DPROC glUniform2d; +PFNGLUNIFORM3DPROC glUniform3d; +PFNGLUNIFORM4DPROC glUniform4d; +PFNGLUNIFORM1DVPROC glUniform1dv; +PFNGLUNIFORM2DVPROC glUniform2dv; +PFNGLUNIFORM3DVPROC glUniform3dv; +PFNGLUNIFORM4DVPROC glUniform4dv; +PFNGLUNIFORMMATRIX2DVPROC glUniformMatrix2dv; +PFNGLUNIFORMMATRIX3DVPROC glUniformMatrix3dv; +PFNGLUNIFORMMATRIX4DVPROC glUniformMatrix4dv; +PFNGLUNIFORMMATRIX2X3DVPROC glUniformMatrix2x3dv; +PFNGLUNIFORMMATRIX2X4DVPROC glUniformMatrix2x4dv; +PFNGLUNIFORMMATRIX3X2DVPROC glUniformMatrix3x2dv; +PFNGLUNIFORMMATRIX3X4DVPROC glUniformMatrix3x4dv; +PFNGLUNIFORMMATRIX4X2DVPROC glUniformMatrix4x2dv; +PFNGLUNIFORMMATRIX4X3DVPROC glUniformMatrix4x3dv; +PFNGLGETUNIFORMDVPROC glGetUniformdv; +PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC glGetSubroutineUniformLocation; +PFNGLGETSUBROUTINEINDEXPROC glGetSubroutineIndex; +PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC glGetActiveSubroutineUniformiv; +PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC glGetActiveSubroutineUniformName; +PFNGLGETACTIVESUBROUTINENAMEPROC glGetActiveSubroutineName; +PFNGLUNIFORMSUBROUTINESUIVPROC glUniformSubroutinesuiv; +PFNGLGETUNIFORMSUBROUTINEUIVPROC glGetUniformSubroutineuiv; +PFNGLGETPROGRAMSTAGEIVPROC glGetProgramStageiv; +PFNGLPATCHPARAMETERIPROC glPatchParameteri; +PFNGLPATCHPARAMETERFVPROC glPatchParameterfv; +PFNGLBINDTRANSFORMFEEDBACKPROC glBindTransformFeedback; +PFNGLDELETETRANSFORMFEEDBACKSPROC glDeleteTransformFeedbacks; +PFNGLGENTRANSFORMFEEDBACKSPROC glGenTransformFeedbacks; +PFNGLISTRANSFORMFEEDBACKPROC glIsTransformFeedback; +PFNGLPAUSETRANSFORMFEEDBACKPROC glPauseTransformFeedback; +PFNGLRESUMETRANSFORMFEEDBACKPROC glResumeTransformFeedback; +PFNGLDRAWTRANSFORMFEEDBACKPROC glDrawTransformFeedback; +PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC glDrawTransformFeedbackStream; +PFNGLBEGINQUERYINDEXEDPROC glBeginQueryIndexed; +PFNGLENDQUERYINDEXEDPROC glEndQueryIndexed; +PFNGLGETQUERYINDEXEDIVPROC glGetQueryIndexediv; +PFNGLRELEASESHADERCOMPILERPROC glReleaseShaderCompiler; +PFNGLSHADERBINARYPROC glShaderBinary; +PFNGLGETSHADERPRECISIONFORMATPROC glGetShaderPrecisionFormat; +PFNGLDEPTHRANGEFPROC glDepthRangef; +PFNGLCLEARDEPTHFPROC glClearDepthf; +PFNGLGETPROGRAMBINARYPROC glGetProgramBinary; +PFNGLPROGRAMBINARYPROC glProgramBinary; +PFNGLPROGRAMPARAMETERIPROC glProgramParameteri; +PFNGLUSEPROGRAMSTAGESPROC glUseProgramStages; +PFNGLACTIVESHADERPROGRAMPROC glActiveShaderProgram; +PFNGLCREATESHADERPROGRAMVPROC glCreateShaderProgramv; +PFNGLBINDPROGRAMPIPELINEPROC glBindProgramPipeline; +PFNGLDELETEPROGRAMPIPELINESPROC glDeleteProgramPipelines; +PFNGLGENPROGRAMPIPELINESPROC glGenProgramPipelines; +PFNGLISPROGRAMPIPELINEPROC glIsProgramPipeline; +PFNGLGETPROGRAMPIPELINEIVPROC glGetProgramPipelineiv; +PFNGLPROGRAMUNIFORM1IPROC glProgramUniform1i; +PFNGLPROGRAMUNIFORM1IVPROC glProgramUniform1iv; +PFNGLPROGRAMUNIFORM1FPROC glProgramUniform1f; +PFNGLPROGRAMUNIFORM1FVPROC glProgramUniform1fv; +PFNGLPROGRAMUNIFORM1DPROC glProgramUniform1d; +PFNGLPROGRAMUNIFORM1DVPROC glProgramUniform1dv; +PFNGLPROGRAMUNIFORM1UIPROC glProgramUniform1ui; +PFNGLPROGRAMUNIFORM1UIVPROC glProgramUniform1uiv; +PFNGLPROGRAMUNIFORM2IPROC glProgramUniform2i; +PFNGLPROGRAMUNIFORM2IVPROC glProgramUniform2iv; +PFNGLPROGRAMUNIFORM2FPROC glProgramUniform2f; +PFNGLPROGRAMUNIFORM2FVPROC glProgramUniform2fv; +PFNGLPROGRAMUNIFORM2DPROC glProgramUniform2d; +PFNGLPROGRAMUNIFORM2DVPROC glProgramUniform2dv; +PFNGLPROGRAMUNIFORM2UIPROC glProgramUniform2ui; +PFNGLPROGRAMUNIFORM2UIVPROC glProgramUniform2uiv; +PFNGLPROGRAMUNIFORM3IPROC glProgramUniform3i; +PFNGLPROGRAMUNIFORM3IVPROC glProgramUniform3iv; +PFNGLPROGRAMUNIFORM3FPROC glProgramUniform3f; +PFNGLPROGRAMUNIFORM3FVPROC glProgramUniform3fv; +PFNGLPROGRAMUNIFORM3DPROC glProgramUniform3d; +PFNGLPROGRAMUNIFORM3DVPROC glProgramUniform3dv; +PFNGLPROGRAMUNIFORM3UIPROC glProgramUniform3ui; +PFNGLPROGRAMUNIFORM3UIVPROC glProgramUniform3uiv; +PFNGLPROGRAMUNIFORM4IPROC glProgramUniform4i; +PFNGLPROGRAMUNIFORM4IVPROC glProgramUniform4iv; +PFNGLPROGRAMUNIFORM4FPROC glProgramUniform4f; +PFNGLPROGRAMUNIFORM4FVPROC glProgramUniform4fv; +PFNGLPROGRAMUNIFORM4DPROC glProgramUniform4d; +PFNGLPROGRAMUNIFORM4DVPROC glProgramUniform4dv; +PFNGLPROGRAMUNIFORM4UIPROC glProgramUniform4ui; +PFNGLPROGRAMUNIFORM4UIVPROC glProgramUniform4uiv; +PFNGLPROGRAMUNIFORMMATRIX2FVPROC glProgramUniformMatrix2fv; +PFNGLPROGRAMUNIFORMMATRIX3FVPROC glProgramUniformMatrix3fv; +PFNGLPROGRAMUNIFORMMATRIX4FVPROC glProgramUniformMatrix4fv; +PFNGLPROGRAMUNIFORMMATRIX2DVPROC glProgramUniformMatrix2dv; +PFNGLPROGRAMUNIFORMMATRIX3DVPROC glProgramUniformMatrix3dv; +PFNGLPROGRAMUNIFORMMATRIX4DVPROC glProgramUniformMatrix4dv; +PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC glProgramUniformMatrix2x3fv; +PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC glProgramUniformMatrix3x2fv; +PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC glProgramUniformMatrix2x4fv; +PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC glProgramUniformMatrix4x2fv; +PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC glProgramUniformMatrix3x4fv; +PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC glProgramUniformMatrix4x3fv; +PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC glProgramUniformMatrix2x3dv; +PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC glProgramUniformMatrix3x2dv; +PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC glProgramUniformMatrix2x4dv; +PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC glProgramUniformMatrix4x2dv; +PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC glProgramUniformMatrix3x4dv; +PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC glProgramUniformMatrix4x3dv; +PFNGLVALIDATEPROGRAMPIPELINEPROC glValidateProgramPipeline; +PFNGLGETPROGRAMPIPELINEINFOLOGPROC glGetProgramPipelineInfoLog; +PFNGLVERTEXATTRIBL1DPROC glVertexAttribL1d; +PFNGLVERTEXATTRIBL2DPROC glVertexAttribL2d; +PFNGLVERTEXATTRIBL3DPROC glVertexAttribL3d; +PFNGLVERTEXATTRIBL4DPROC glVertexAttribL4d; +PFNGLVERTEXATTRIBL1DVPROC glVertexAttribL1dv; +PFNGLVERTEXATTRIBL2DVPROC glVertexAttribL2dv; +PFNGLVERTEXATTRIBL3DVPROC glVertexAttribL3dv; +PFNGLVERTEXATTRIBL4DVPROC glVertexAttribL4dv; +PFNGLVERTEXATTRIBLPOINTERPROC glVertexAttribLPointer; +PFNGLGETVERTEXATTRIBLDVPROC glGetVertexAttribLdv; +PFNGLVIEWPORTARRAYVPROC glViewportArrayv; +PFNGLVIEWPORTINDEXEDFPROC glViewportIndexedf; +PFNGLVIEWPORTINDEXEDFVPROC glViewportIndexedfv; +PFNGLSCISSORARRAYVPROC glScissorArrayv; +PFNGLSCISSORINDEXEDPROC glScissorIndexed; +PFNGLSCISSORINDEXEDVPROC glScissorIndexedv; +PFNGLDEPTHRANGEARRAYVPROC glDepthRangeArrayv; +PFNGLDEPTHRANGEINDEXEDPROC glDepthRangeIndexed; +PFNGLGETFLOATI_VPROC glGetFloati_v; +PFNGLGETDOUBLEI_VPROC glGetDoublei_v; +PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC glDrawArraysInstancedBaseInstance; +PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC glDrawElementsInstancedBaseInstance; +PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC glDrawElementsInstancedBaseVertexBaseInstance; +PFNGLGETINTERNALFORMATIVPROC glGetInternalformativ; +PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC glGetActiveAtomicCounterBufferiv; +PFNGLBINDIMAGETEXTUREPROC glBindImageTexture; +PFNGLMEMORYBARRIERPROC glMemoryBarrier; +PFNGLTEXSTORAGE1DPROC glTexStorage1D; +PFNGLTEXSTORAGE2DPROC glTexStorage2D; +PFNGLTEXSTORAGE3DPROC glTexStorage3D; +PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC glDrawTransformFeedbackInstanced; +PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC glDrawTransformFeedbackStreamInstanced; +PFNGLCLEARBUFFERDATAPROC glClearBufferData; +PFNGLCLEARBUFFERSUBDATAPROC glClearBufferSubData; +PFNGLDISPATCHCOMPUTEPROC glDispatchCompute; +PFNGLDISPATCHCOMPUTEINDIRECTPROC glDispatchComputeIndirect; +PFNGLCOPYIMAGESUBDATAPROC glCopyImageSubData; +PFNGLFRAMEBUFFERPARAMETERIPROC glFramebufferParameteri; +PFNGLGETFRAMEBUFFERPARAMETERIVPROC glGetFramebufferParameteriv; +PFNGLGETINTERNALFORMATI64VPROC glGetInternalformati64v; +PFNGLINVALIDATETEXSUBIMAGEPROC glInvalidateTexSubImage; +PFNGLINVALIDATETEXIMAGEPROC glInvalidateTexImage; +PFNGLINVALIDATEBUFFERSUBDATAPROC glInvalidateBufferSubData; +PFNGLINVALIDATEBUFFERDATAPROC glInvalidateBufferData; +PFNGLINVALIDATEFRAMEBUFFERPROC glInvalidateFramebuffer; +PFNGLINVALIDATESUBFRAMEBUFFERPROC glInvalidateSubFramebuffer; +PFNGLMULTIDRAWARRAYSINDIRECTPROC glMultiDrawArraysIndirect; +PFNGLMULTIDRAWELEMENTSINDIRECTPROC glMultiDrawElementsIndirect; +PFNGLGETPROGRAMINTERFACEIVPROC glGetProgramInterfaceiv; +PFNGLGETPROGRAMRESOURCEINDEXPROC glGetProgramResourceIndex; +PFNGLGETPROGRAMRESOURCENAMEPROC glGetProgramResourceName; +PFNGLGETPROGRAMRESOURCEIVPROC glGetProgramResourceiv; +PFNGLGETPROGRAMRESOURCELOCATIONPROC glGetProgramResourceLocation; +PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC glGetProgramResourceLocationIndex; +PFNGLSHADERSTORAGEBLOCKBINDINGPROC glShaderStorageBlockBinding; +PFNGLTEXBUFFERRANGEPROC glTexBufferRange; +PFNGLTEXSTORAGE2DMULTISAMPLEPROC glTexStorage2DMultisample; +PFNGLTEXSTORAGE3DMULTISAMPLEPROC glTexStorage3DMultisample; +PFNGLTEXTUREVIEWPROC glTextureView; +PFNGLBINDVERTEXBUFFERPROC glBindVertexBuffer; +PFNGLVERTEXATTRIBFORMATPROC glVertexAttribFormat; +PFNGLVERTEXATTRIBIFORMATPROC glVertexAttribIFormat; +PFNGLVERTEXATTRIBLFORMATPROC glVertexAttribLFormat; +PFNGLVERTEXATTRIBBINDINGPROC glVertexAttribBinding; +PFNGLVERTEXBINDINGDIVISORPROC glVertexBindingDivisor; +PFNGLDEBUGMESSAGECONTROLPROC glDebugMessageControl; +PFNGLDEBUGMESSAGEINSERTPROC glDebugMessageInsert; +PFNGLDEBUGMESSAGECALLBACKPROC glDebugMessageCallback; +PFNGLGETDEBUGMESSAGELOGPROC glGetDebugMessageLog; +PFNGLPUSHDEBUGGROUPPROC glPushDebugGroup; +PFNGLPOPDEBUGGROUPPROC glPopDebugGroup; +PFNGLOBJECTLABELPROC glObjectLabel; +PFNGLGETOBJECTLABELPROC glGetObjectLabel; +PFNGLOBJECTPTRLABELPROC glObjectPtrLabel; +PFNGLGETOBJECTPTRLABELPROC glGetObjectPtrLabel; +PFNGLBUFFERSTORAGEPROC glBufferStorage; +PFNGLCLEARTEXIMAGEPROC glClearTexImage; +PFNGLCLEARTEXSUBIMAGEPROC glClearTexSubImage; +PFNGLBINDBUFFERSBASEPROC glBindBuffersBase; +PFNGLBINDBUFFERSRANGEPROC glBindBuffersRange; +PFNGLBINDTEXTURESPROC glBindTextures; +PFNGLBINDSAMPLERSPROC glBindSamplers; +PFNGLBINDIMAGETEXTURESPROC glBindImageTextures; +PFNGLBINDVERTEXBUFFERSPROC glBindVertexBuffers; +PFNGLCLIPCONTROLPROC glClipControl; +PFNGLCREATETRANSFORMFEEDBACKSPROC glCreateTransformFeedbacks; +PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC glTransformFeedbackBufferBase; +PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC glTransformFeedbackBufferRange; +PFNGLGETTRANSFORMFEEDBACKIVPROC glGetTransformFeedbackiv; +PFNGLGETTRANSFORMFEEDBACKI_VPROC glGetTransformFeedbacki_v; +PFNGLGETTRANSFORMFEEDBACKI64_VPROC glGetTransformFeedbacki64_v; +PFNGLCREATEBUFFERSPROC glCreateBuffers; +PFNGLNAMEDBUFFERSTORAGEPROC glNamedBufferStorage; +PFNGLNAMEDBUFFERDATAPROC glNamedBufferData; +PFNGLNAMEDBUFFERSUBDATAPROC glNamedBufferSubData; +PFNGLCOPYNAMEDBUFFERSUBDATAPROC glCopyNamedBufferSubData; +PFNGLCLEARNAMEDBUFFERDATAPROC glClearNamedBufferData; +PFNGLCLEARNAMEDBUFFERSUBDATAPROC glClearNamedBufferSubData; +PFNGLMAPNAMEDBUFFERPROC glMapNamedBuffer; +PFNGLMAPNAMEDBUFFERRANGEPROC glMapNamedBufferRange; +PFNGLUNMAPNAMEDBUFFERPROC glUnmapNamedBuffer; +PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC glFlushMappedNamedBufferRange; +PFNGLGETNAMEDBUFFERPARAMETERIVPROC glGetNamedBufferParameteriv; +PFNGLGETNAMEDBUFFERPARAMETERI64VPROC glGetNamedBufferParameteri64v; +PFNGLGETNAMEDBUFFERPOINTERVPROC glGetNamedBufferPointerv; +PFNGLGETNAMEDBUFFERSUBDATAPROC glGetNamedBufferSubData; +PFNGLCREATEFRAMEBUFFERSPROC glCreateFramebuffers; +PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC glNamedFramebufferRenderbuffer; +PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC glNamedFramebufferParameteri; +PFNGLNAMEDFRAMEBUFFERTEXTUREPROC glNamedFramebufferTexture; +PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC glNamedFramebufferTextureLayer; +PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC glNamedFramebufferDrawBuffer; +PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC glNamedFramebufferDrawBuffers; +PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC glNamedFramebufferReadBuffer; +PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC glInvalidateNamedFramebufferData; +PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC glInvalidateNamedFramebufferSubData; +PFNGLCLEARNAMEDFRAMEBUFFERIVPROC glClearNamedFramebufferiv; +PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC glClearNamedFramebufferuiv; +PFNGLCLEARNAMEDFRAMEBUFFERFVPROC glClearNamedFramebufferfv; +PFNGLCLEARNAMEDFRAMEBUFFERFIPROC glClearNamedFramebufferfi; +PFNGLBLITNAMEDFRAMEBUFFERPROC glBlitNamedFramebuffer; +PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glCheckNamedFramebufferStatus; +PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC glGetNamedFramebufferParameteriv; +PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetNamedFramebufferAttachmentParameteriv; +PFNGLCREATERENDERBUFFERSPROC glCreateRenderbuffers; +PFNGLNAMEDRENDERBUFFERSTORAGEPROC glNamedRenderbufferStorage; +PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC glNamedRenderbufferStorageMultisample; +PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC glGetNamedRenderbufferParameteriv; +PFNGLCREATETEXTURESPROC glCreateTextures; +PFNGLTEXTUREBUFFERPROC glTextureBuffer; +PFNGLTEXTUREBUFFERRANGEPROC glTextureBufferRange; +PFNGLTEXTURESTORAGE1DPROC glTextureStorage1D; +PFNGLTEXTURESTORAGE2DPROC glTextureStorage2D; +PFNGLTEXTURESTORAGE3DPROC glTextureStorage3D; +PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC glTextureStorage2DMultisample; +PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC glTextureStorage3DMultisample; +PFNGLTEXTURESUBIMAGE1DPROC glTextureSubImage1D; +PFNGLTEXTURESUBIMAGE2DPROC glTextureSubImage2D; +PFNGLTEXTURESUBIMAGE3DPROC glTextureSubImage3D; +PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC glCompressedTextureSubImage1D; +PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC glCompressedTextureSubImage2D; +PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC glCompressedTextureSubImage3D; +PFNGLCOPYTEXTURESUBIMAGE1DPROC glCopyTextureSubImage1D; +PFNGLCOPYTEXTURESUBIMAGE2DPROC glCopyTextureSubImage2D; +PFNGLCOPYTEXTURESUBIMAGE3DPROC glCopyTextureSubImage3D; +PFNGLTEXTUREPARAMETERFPROC glTextureParameterf; +PFNGLTEXTUREPARAMETERFVPROC glTextureParameterfv; +PFNGLTEXTUREPARAMETERIPROC glTextureParameteri; +PFNGLTEXTUREPARAMETERIIVPROC glTextureParameterIiv; +PFNGLTEXTUREPARAMETERIUIVPROC glTextureParameterIuiv; +PFNGLTEXTUREPARAMETERIVPROC glTextureParameteriv; +PFNGLGENERATETEXTUREMIPMAPPROC glGenerateTextureMipmap; +PFNGLBINDTEXTUREUNITPROC glBindTextureUnit; +PFNGLGETTEXTUREIMAGEPROC glGetTextureImage; +PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC glGetCompressedTextureImage; +PFNGLGETTEXTURELEVELPARAMETERFVPROC glGetTextureLevelParameterfv; +PFNGLGETTEXTURELEVELPARAMETERIVPROC glGetTextureLevelParameteriv; +PFNGLGETTEXTUREPARAMETERFVPROC glGetTextureParameterfv; +PFNGLGETTEXTUREPARAMETERIIVPROC glGetTextureParameterIiv; +PFNGLGETTEXTUREPARAMETERIUIVPROC glGetTextureParameterIuiv; +PFNGLGETTEXTUREPARAMETERIVPROC glGetTextureParameteriv; +PFNGLCREATEVERTEXARRAYSPROC glCreateVertexArrays; +PFNGLDISABLEVERTEXARRAYATTRIBPROC glDisableVertexArrayAttrib; +PFNGLENABLEVERTEXARRAYATTRIBPROC glEnableVertexArrayAttrib; +PFNGLVERTEXARRAYELEMENTBUFFERPROC glVertexArrayElementBuffer; +PFNGLVERTEXARRAYVERTEXBUFFERPROC glVertexArrayVertexBuffer; +PFNGLVERTEXARRAYVERTEXBUFFERSPROC glVertexArrayVertexBuffers; +PFNGLVERTEXARRAYATTRIBBINDINGPROC glVertexArrayAttribBinding; +PFNGLVERTEXARRAYATTRIBFORMATPROC glVertexArrayAttribFormat; +PFNGLVERTEXARRAYATTRIBIFORMATPROC glVertexArrayAttribIFormat; +PFNGLVERTEXARRAYATTRIBLFORMATPROC glVertexArrayAttribLFormat; +PFNGLVERTEXARRAYBINDINGDIVISORPROC glVertexArrayBindingDivisor; +PFNGLGETVERTEXARRAYIVPROC glGetVertexArrayiv; +PFNGLGETVERTEXARRAYINDEXEDIVPROC glGetVertexArrayIndexediv; +PFNGLGETVERTEXARRAYINDEXED64IVPROC glGetVertexArrayIndexed64iv; +PFNGLCREATESAMPLERSPROC glCreateSamplers; +PFNGLCREATEPROGRAMPIPELINESPROC glCreateProgramPipelines; +PFNGLCREATEQUERIESPROC glCreateQueries; +PFNGLGETQUERYBUFFEROBJECTI64VPROC glGetQueryBufferObjecti64v; +PFNGLGETQUERYBUFFEROBJECTIVPROC glGetQueryBufferObjectiv; +PFNGLGETQUERYBUFFEROBJECTUI64VPROC glGetQueryBufferObjectui64v; +PFNGLGETQUERYBUFFEROBJECTUIVPROC glGetQueryBufferObjectuiv; +PFNGLMEMORYBARRIERBYREGIONPROC glMemoryBarrierByRegion; +PFNGLGETTEXTURESUBIMAGEPROC glGetTextureSubImage; +PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC glGetCompressedTextureSubImage; +PFNGLGETGRAPHICSRESETSTATUSPROC glGetGraphicsResetStatus; +PFNGLGETNCOMPRESSEDTEXIMAGEPROC glGetnCompressedTexImage; +PFNGLGETNTEXIMAGEPROC glGetnTexImage; +PFNGLGETNUNIFORMDVPROC glGetnUniformdv; +PFNGLGETNUNIFORMFVPROC glGetnUniformfv; +PFNGLGETNUNIFORMIVPROC glGetnUniformiv; +PFNGLGETNUNIFORMUIVPROC glGetnUniformuiv; +PFNGLREADNPIXELSPROC glReadnPixels; +PFNGLGETNMAPDVPROC glGetnMapdv; +PFNGLGETNMAPFVPROC glGetnMapfv; +PFNGLGETNMAPIVPROC glGetnMapiv; +PFNGLGETNPIXELMAPFVPROC glGetnPixelMapfv; +PFNGLGETNPIXELMAPUIVPROC glGetnPixelMapuiv; +PFNGLGETNPIXELMAPUSVPROC glGetnPixelMapusv; +PFNGLGETNPOLYGONSTIPPLEPROC glGetnPolygonStipple; +PFNGLGETNCOLORTABLEPROC glGetnColorTable; +PFNGLGETNCONVOLUTIONFILTERPROC glGetnConvolutionFilter; +PFNGLGETNSEPARABLEFILTERPROC glGetnSeparableFilter; +PFNGLGETNHISTOGRAMPROC glGetnHistogram; +PFNGLGETNMINMAXPROC glGetnMinmax; +PFNGLTEXTUREBARRIERPROC glTextureBarrier; +PFNGLSPECIALIZESHADERPROC glSpecializeShader; +PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC glMultiDrawArraysIndirectCount; +PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC glMultiDrawElementsIndirectCount; +PFNGLPOLYGONOFFSETCLAMPPROC glPolygonOffsetClamp; +PFNGLTBUFFERMASK3DFXPROC glTbufferMask3DFX; +PFNGLDEBUGMESSAGEENABLEAMDPROC glDebugMessageEnableAMD; +PFNGLDEBUGMESSAGEINSERTAMDPROC glDebugMessageInsertAMD; +PFNGLDEBUGMESSAGECALLBACKAMDPROC glDebugMessageCallbackAMD; +PFNGLGETDEBUGMESSAGELOGAMDPROC glGetDebugMessageLogAMD; +PFNGLBLENDFUNCINDEXEDAMDPROC glBlendFuncIndexedAMD; +PFNGLBLENDFUNCSEPARATEINDEXEDAMDPROC glBlendFuncSeparateIndexedAMD; +PFNGLBLENDEQUATIONINDEXEDAMDPROC glBlendEquationIndexedAMD; +PFNGLBLENDEQUATIONSEPARATEINDEXEDAMDPROC glBlendEquationSeparateIndexedAMD; +PFNGLRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC glRenderbufferStorageMultisampleAdvancedAMD; +PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC glNamedRenderbufferStorageMultisampleAdvancedAMD; +PFNGLFRAMEBUFFERSAMPLEPOSITIONSFVAMDPROC glFramebufferSamplePositionsfvAMD; +PFNGLNAMEDFRAMEBUFFERSAMPLEPOSITIONSFVAMDPROC glNamedFramebufferSamplePositionsfvAMD; +PFNGLGETFRAMEBUFFERPARAMETERFVAMDPROC glGetFramebufferParameterfvAMD; +PFNGLGETNAMEDFRAMEBUFFERPARAMETERFVAMDPROC glGetNamedFramebufferParameterfvAMD; +PFNGLUNIFORM1I64NVPROC glUniform1i64NV; +PFNGLUNIFORM2I64NVPROC glUniform2i64NV; +PFNGLUNIFORM3I64NVPROC glUniform3i64NV; +PFNGLUNIFORM4I64NVPROC glUniform4i64NV; +PFNGLUNIFORM1I64VNVPROC glUniform1i64vNV; +PFNGLUNIFORM2I64VNVPROC glUniform2i64vNV; +PFNGLUNIFORM3I64VNVPROC glUniform3i64vNV; +PFNGLUNIFORM4I64VNVPROC glUniform4i64vNV; +PFNGLUNIFORM1UI64NVPROC glUniform1ui64NV; +PFNGLUNIFORM2UI64NVPROC glUniform2ui64NV; +PFNGLUNIFORM3UI64NVPROC glUniform3ui64NV; +PFNGLUNIFORM4UI64NVPROC glUniform4ui64NV; +PFNGLUNIFORM1UI64VNVPROC glUniform1ui64vNV; +PFNGLUNIFORM2UI64VNVPROC glUniform2ui64vNV; +PFNGLUNIFORM3UI64VNVPROC glUniform3ui64vNV; +PFNGLUNIFORM4UI64VNVPROC glUniform4ui64vNV; +PFNGLGETUNIFORMI64VNVPROC glGetUniformi64vNV; +PFNGLGETUNIFORMUI64VNVPROC glGetUniformui64vNV; +PFNGLPROGRAMUNIFORM1I64NVPROC glProgramUniform1i64NV; +PFNGLPROGRAMUNIFORM2I64NVPROC glProgramUniform2i64NV; +PFNGLPROGRAMUNIFORM3I64NVPROC glProgramUniform3i64NV; +PFNGLPROGRAMUNIFORM4I64NVPROC glProgramUniform4i64NV; +PFNGLPROGRAMUNIFORM1I64VNVPROC glProgramUniform1i64vNV; +PFNGLPROGRAMUNIFORM2I64VNVPROC glProgramUniform2i64vNV; +PFNGLPROGRAMUNIFORM3I64VNVPROC glProgramUniform3i64vNV; +PFNGLPROGRAMUNIFORM4I64VNVPROC glProgramUniform4i64vNV; +PFNGLPROGRAMUNIFORM1UI64NVPROC glProgramUniform1ui64NV; +PFNGLPROGRAMUNIFORM2UI64NVPROC glProgramUniform2ui64NV; +PFNGLPROGRAMUNIFORM3UI64NVPROC glProgramUniform3ui64NV; +PFNGLPROGRAMUNIFORM4UI64NVPROC glProgramUniform4ui64NV; +PFNGLPROGRAMUNIFORM1UI64VNVPROC glProgramUniform1ui64vNV; +PFNGLPROGRAMUNIFORM2UI64VNVPROC glProgramUniform2ui64vNV; +PFNGLPROGRAMUNIFORM3UI64VNVPROC glProgramUniform3ui64vNV; +PFNGLPROGRAMUNIFORM4UI64VNVPROC glProgramUniform4ui64vNV; +PFNGLVERTEXATTRIBPARAMETERIAMDPROC glVertexAttribParameteriAMD; +PFNGLMULTIDRAWARRAYSINDIRECTAMDPROC glMultiDrawArraysIndirectAMD; +PFNGLMULTIDRAWELEMENTSINDIRECTAMDPROC glMultiDrawElementsIndirectAMD; +PFNGLGENNAMESAMDPROC glGenNamesAMD; +PFNGLDELETENAMESAMDPROC glDeleteNamesAMD; +PFNGLISNAMEAMDPROC glIsNameAMD; +PFNGLQUERYOBJECTPARAMETERUIAMDPROC glQueryObjectParameteruiAMD; +PFNGLGETPERFMONITORGROUPSAMDPROC glGetPerfMonitorGroupsAMD; +PFNGLGETPERFMONITORCOUNTERSAMDPROC glGetPerfMonitorCountersAMD; +PFNGLGETPERFMONITORGROUPSTRINGAMDPROC glGetPerfMonitorGroupStringAMD; +PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC glGetPerfMonitorCounterStringAMD; +PFNGLGETPERFMONITORCOUNTERINFOAMDPROC glGetPerfMonitorCounterInfoAMD; +PFNGLGENPERFMONITORSAMDPROC glGenPerfMonitorsAMD; +PFNGLDELETEPERFMONITORSAMDPROC glDeletePerfMonitorsAMD; +PFNGLSELECTPERFMONITORCOUNTERSAMDPROC glSelectPerfMonitorCountersAMD; +PFNGLBEGINPERFMONITORAMDPROC glBeginPerfMonitorAMD; +PFNGLENDPERFMONITORAMDPROC glEndPerfMonitorAMD; +PFNGLGETPERFMONITORCOUNTERDATAAMDPROC glGetPerfMonitorCounterDataAMD; +PFNGLSETMULTISAMPLEFVAMDPROC glSetMultisamplefvAMD; +PFNGLTEXSTORAGESPARSEAMDPROC glTexStorageSparseAMD; +PFNGLTEXTURESTORAGESPARSEAMDPROC glTextureStorageSparseAMD; +PFNGLSTENCILOPVALUEAMDPROC glStencilOpValueAMD; +PFNGLTESSELLATIONFACTORAMDPROC glTessellationFactorAMD; +PFNGLTESSELLATIONMODEAMDPROC glTessellationModeAMD; +PFNGLELEMENTPOINTERAPPLEPROC glElementPointerAPPLE; +PFNGLDRAWELEMENTARRAYAPPLEPROC glDrawElementArrayAPPLE; +PFNGLDRAWRANGEELEMENTARRAYAPPLEPROC glDrawRangeElementArrayAPPLE; +PFNGLMULTIDRAWELEMENTARRAYAPPLEPROC glMultiDrawElementArrayAPPLE; +PFNGLMULTIDRAWRANGEELEMENTARRAYAPPLEPROC glMultiDrawRangeElementArrayAPPLE; +PFNGLGENFENCESAPPLEPROC glGenFencesAPPLE; +PFNGLDELETEFENCESAPPLEPROC glDeleteFencesAPPLE; +PFNGLSETFENCEAPPLEPROC glSetFenceAPPLE; +PFNGLISFENCEAPPLEPROC glIsFenceAPPLE; +PFNGLTESTFENCEAPPLEPROC glTestFenceAPPLE; +PFNGLFINISHFENCEAPPLEPROC glFinishFenceAPPLE; +PFNGLTESTOBJECTAPPLEPROC glTestObjectAPPLE; +PFNGLFINISHOBJECTAPPLEPROC glFinishObjectAPPLE; +PFNGLBUFFERPARAMETERIAPPLEPROC glBufferParameteriAPPLE; +PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC glFlushMappedBufferRangeAPPLE; +PFNGLOBJECTPURGEABLEAPPLEPROC glObjectPurgeableAPPLE; +PFNGLOBJECTUNPURGEABLEAPPLEPROC glObjectUnpurgeableAPPLE; +PFNGLGETOBJECTPARAMETERIVAPPLEPROC glGetObjectParameterivAPPLE; +PFNGLTEXTURERANGEAPPLEPROC glTextureRangeAPPLE; +PFNGLGETTEXPARAMETERPOINTERVAPPLEPROC glGetTexParameterPointervAPPLE; +PFNGLBINDVERTEXARRAYAPPLEPROC glBindVertexArrayAPPLE; +PFNGLDELETEVERTEXARRAYSAPPLEPROC glDeleteVertexArraysAPPLE; +PFNGLGENVERTEXARRAYSAPPLEPROC glGenVertexArraysAPPLE; +PFNGLISVERTEXARRAYAPPLEPROC glIsVertexArrayAPPLE; +PFNGLVERTEXARRAYRANGEAPPLEPROC glVertexArrayRangeAPPLE; +PFNGLFLUSHVERTEXARRAYRANGEAPPLEPROC glFlushVertexArrayRangeAPPLE; +PFNGLVERTEXARRAYPARAMETERIAPPLEPROC glVertexArrayParameteriAPPLE; +PFNGLENABLEVERTEXATTRIBAPPLEPROC glEnableVertexAttribAPPLE; +PFNGLDISABLEVERTEXATTRIBAPPLEPROC glDisableVertexAttribAPPLE; +PFNGLISVERTEXATTRIBENABLEDAPPLEPROC glIsVertexAttribEnabledAPPLE; +PFNGLMAPVERTEXATTRIB1DAPPLEPROC glMapVertexAttrib1dAPPLE; +PFNGLMAPVERTEXATTRIB1FAPPLEPROC glMapVertexAttrib1fAPPLE; +PFNGLMAPVERTEXATTRIB2DAPPLEPROC glMapVertexAttrib2dAPPLE; +PFNGLMAPVERTEXATTRIB2FAPPLEPROC glMapVertexAttrib2fAPPLE; +PFNGLPRIMITIVEBOUNDINGBOXARBPROC glPrimitiveBoundingBoxARB; +PFNGLGETTEXTUREHANDLEARBPROC glGetTextureHandleARB; +PFNGLGETTEXTURESAMPLERHANDLEARBPROC glGetTextureSamplerHandleARB; +PFNGLMAKETEXTUREHANDLERESIDENTARBPROC glMakeTextureHandleResidentARB; +PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC glMakeTextureHandleNonResidentARB; +PFNGLGETIMAGEHANDLEARBPROC glGetImageHandleARB; +PFNGLMAKEIMAGEHANDLERESIDENTARBPROC glMakeImageHandleResidentARB; +PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC glMakeImageHandleNonResidentARB; +PFNGLUNIFORMHANDLEUI64ARBPROC glUniformHandleui64ARB; +PFNGLUNIFORMHANDLEUI64VARBPROC glUniformHandleui64vARB; +PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC glProgramUniformHandleui64ARB; +PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC glProgramUniformHandleui64vARB; +PFNGLISTEXTUREHANDLERESIDENTARBPROC glIsTextureHandleResidentARB; +PFNGLISIMAGEHANDLERESIDENTARBPROC glIsImageHandleResidentARB; +PFNGLVERTEXATTRIBL1UI64ARBPROC glVertexAttribL1ui64ARB; +PFNGLVERTEXATTRIBL1UI64VARBPROC glVertexAttribL1ui64vARB; +PFNGLGETVERTEXATTRIBLUI64VARBPROC glGetVertexAttribLui64vARB; +PFNGLCREATESYNCFROMCLEVENTARBPROC glCreateSyncFromCLeventARB; +PFNGLCLAMPCOLORARBPROC glClampColorARB; +PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC glDispatchComputeGroupSizeARB; +PFNGLDEBUGMESSAGECONTROLARBPROC glDebugMessageControlARB; +PFNGLDEBUGMESSAGEINSERTARBPROC glDebugMessageInsertARB; +PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARB; +PFNGLGETDEBUGMESSAGELOGARBPROC glGetDebugMessageLogARB; +PFNGLDRAWBUFFERSARBPROC glDrawBuffersARB; +PFNGLBLENDEQUATIONIARBPROC glBlendEquationiARB; +PFNGLBLENDEQUATIONSEPARATEIARBPROC glBlendEquationSeparateiARB; +PFNGLBLENDFUNCIARBPROC glBlendFunciARB; +PFNGLBLENDFUNCSEPARATEIARBPROC glBlendFuncSeparateiARB; +PFNGLDRAWARRAYSINSTANCEDARBPROC glDrawArraysInstancedARB; +PFNGLDRAWELEMENTSINSTANCEDARBPROC glDrawElementsInstancedARB; +PFNGLPROGRAMSTRINGARBPROC glProgramStringARB; +PFNGLBINDPROGRAMARBPROC glBindProgramARB; +PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB; +PFNGLGENPROGRAMSARBPROC glGenProgramsARB; +PFNGLPROGRAMENVPARAMETER4DARBPROC glProgramEnvParameter4dARB; +PFNGLPROGRAMENVPARAMETER4DVARBPROC glProgramEnvParameter4dvARB; +PFNGLPROGRAMENVPARAMETER4FARBPROC glProgramEnvParameter4fARB; +PFNGLPROGRAMENVPARAMETER4FVARBPROC glProgramEnvParameter4fvARB; +PFNGLPROGRAMLOCALPARAMETER4DARBPROC glProgramLocalParameter4dARB; +PFNGLPROGRAMLOCALPARAMETER4DVARBPROC glProgramLocalParameter4dvARB; +PFNGLPROGRAMLOCALPARAMETER4FARBPROC glProgramLocalParameter4fARB; +PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glProgramLocalParameter4fvARB; +PFNGLGETPROGRAMENVPARAMETERDVARBPROC glGetProgramEnvParameterdvARB; +PFNGLGETPROGRAMENVPARAMETERFVARBPROC glGetProgramEnvParameterfvARB; +PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC glGetProgramLocalParameterdvARB; +PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC glGetProgramLocalParameterfvARB; +PFNGLGETPROGRAMIVARBPROC glGetProgramivARB; +PFNGLGETPROGRAMSTRINGARBPROC glGetProgramStringARB; +PFNGLISPROGRAMARBPROC glIsProgramARB; +PFNGLPROGRAMPARAMETERIARBPROC glProgramParameteriARB; +PFNGLFRAMEBUFFERTEXTUREARBPROC glFramebufferTextureARB; +PFNGLFRAMEBUFFERTEXTURELAYERARBPROC glFramebufferTextureLayerARB; +PFNGLFRAMEBUFFERTEXTUREFACEARBPROC glFramebufferTextureFaceARB; +PFNGLSPECIALIZESHADERARBPROC glSpecializeShaderARB; +PFNGLUNIFORM1I64ARBPROC glUniform1i64ARB; +PFNGLUNIFORM2I64ARBPROC glUniform2i64ARB; +PFNGLUNIFORM3I64ARBPROC glUniform3i64ARB; +PFNGLUNIFORM4I64ARBPROC glUniform4i64ARB; +PFNGLUNIFORM1I64VARBPROC glUniform1i64vARB; +PFNGLUNIFORM2I64VARBPROC glUniform2i64vARB; +PFNGLUNIFORM3I64VARBPROC glUniform3i64vARB; +PFNGLUNIFORM4I64VARBPROC glUniform4i64vARB; +PFNGLUNIFORM1UI64ARBPROC glUniform1ui64ARB; +PFNGLUNIFORM2UI64ARBPROC glUniform2ui64ARB; +PFNGLUNIFORM3UI64ARBPROC glUniform3ui64ARB; +PFNGLUNIFORM4UI64ARBPROC glUniform4ui64ARB; +PFNGLUNIFORM1UI64VARBPROC glUniform1ui64vARB; +PFNGLUNIFORM2UI64VARBPROC glUniform2ui64vARB; +PFNGLUNIFORM3UI64VARBPROC glUniform3ui64vARB; +PFNGLUNIFORM4UI64VARBPROC glUniform4ui64vARB; +PFNGLGETUNIFORMI64VARBPROC glGetUniformi64vARB; +PFNGLGETUNIFORMUI64VARBPROC glGetUniformui64vARB; +PFNGLGETNUNIFORMI64VARBPROC glGetnUniformi64vARB; +PFNGLGETNUNIFORMUI64VARBPROC glGetnUniformui64vARB; +PFNGLPROGRAMUNIFORM1I64ARBPROC glProgramUniform1i64ARB; +PFNGLPROGRAMUNIFORM2I64ARBPROC glProgramUniform2i64ARB; +PFNGLPROGRAMUNIFORM3I64ARBPROC glProgramUniform3i64ARB; +PFNGLPROGRAMUNIFORM4I64ARBPROC glProgramUniform4i64ARB; +PFNGLPROGRAMUNIFORM1I64VARBPROC glProgramUniform1i64vARB; +PFNGLPROGRAMUNIFORM2I64VARBPROC glProgramUniform2i64vARB; +PFNGLPROGRAMUNIFORM3I64VARBPROC glProgramUniform3i64vARB; +PFNGLPROGRAMUNIFORM4I64VARBPROC glProgramUniform4i64vARB; +PFNGLPROGRAMUNIFORM1UI64ARBPROC glProgramUniform1ui64ARB; +PFNGLPROGRAMUNIFORM2UI64ARBPROC glProgramUniform2ui64ARB; +PFNGLPROGRAMUNIFORM3UI64ARBPROC glProgramUniform3ui64ARB; +PFNGLPROGRAMUNIFORM4UI64ARBPROC glProgramUniform4ui64ARB; +PFNGLPROGRAMUNIFORM1UI64VARBPROC glProgramUniform1ui64vARB; +PFNGLPROGRAMUNIFORM2UI64VARBPROC glProgramUniform2ui64vARB; +PFNGLPROGRAMUNIFORM3UI64VARBPROC glProgramUniform3ui64vARB; +PFNGLPROGRAMUNIFORM4UI64VARBPROC glProgramUniform4ui64vARB; +PFNGLCOLORTABLEPROC glColorTable; +PFNGLCOLORTABLEPARAMETERFVPROC glColorTableParameterfv; +PFNGLCOLORTABLEPARAMETERIVPROC glColorTableParameteriv; +PFNGLCOPYCOLORTABLEPROC glCopyColorTable; +PFNGLGETCOLORTABLEPROC glGetColorTable; +PFNGLGETCOLORTABLEPARAMETERFVPROC glGetColorTableParameterfv; +PFNGLGETCOLORTABLEPARAMETERIVPROC glGetColorTableParameteriv; +PFNGLCOLORSUBTABLEPROC glColorSubTable; +PFNGLCOPYCOLORSUBTABLEPROC glCopyColorSubTable; +PFNGLCONVOLUTIONFILTER1DPROC glConvolutionFilter1D; +PFNGLCONVOLUTIONFILTER2DPROC glConvolutionFilter2D; +PFNGLCONVOLUTIONPARAMETERFPROC glConvolutionParameterf; +PFNGLCONVOLUTIONPARAMETERFVPROC glConvolutionParameterfv; +PFNGLCONVOLUTIONPARAMETERIPROC glConvolutionParameteri; +PFNGLCONVOLUTIONPARAMETERIVPROC glConvolutionParameteriv; +PFNGLCOPYCONVOLUTIONFILTER1DPROC glCopyConvolutionFilter1D; +PFNGLCOPYCONVOLUTIONFILTER2DPROC glCopyConvolutionFilter2D; +PFNGLGETCONVOLUTIONFILTERPROC glGetConvolutionFilter; +PFNGLGETCONVOLUTIONPARAMETERFVPROC glGetConvolutionParameterfv; +PFNGLGETCONVOLUTIONPARAMETERIVPROC glGetConvolutionParameteriv; +PFNGLGETSEPARABLEFILTERPROC glGetSeparableFilter; +PFNGLSEPARABLEFILTER2DPROC glSeparableFilter2D; +PFNGLGETHISTOGRAMPROC glGetHistogram; +PFNGLGETHISTOGRAMPARAMETERFVPROC glGetHistogramParameterfv; +PFNGLGETHISTOGRAMPARAMETERIVPROC glGetHistogramParameteriv; +PFNGLGETMINMAXPROC glGetMinmax; +PFNGLGETMINMAXPARAMETERFVPROC glGetMinmaxParameterfv; +PFNGLGETMINMAXPARAMETERIVPROC glGetMinmaxParameteriv; +PFNGLHISTOGRAMPROC glHistogram; +PFNGLMINMAXPROC glMinmax; +PFNGLRESETHISTOGRAMPROC glResetHistogram; +PFNGLRESETMINMAXPROC glResetMinmax; +PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC glMultiDrawArraysIndirectCountARB; +PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC glMultiDrawElementsIndirectCountARB; +PFNGLVERTEXATTRIBDIVISORARBPROC glVertexAttribDivisorARB; +PFNGLCURRENTPALETTEMATRIXARBPROC glCurrentPaletteMatrixARB; +PFNGLMATRIXINDEXUBVARBPROC glMatrixIndexubvARB; +PFNGLMATRIXINDEXUSVARBPROC glMatrixIndexusvARB; +PFNGLMATRIXINDEXUIVARBPROC glMatrixIndexuivARB; +PFNGLMATRIXINDEXPOINTERARBPROC glMatrixIndexPointerARB; +PFNGLSAMPLECOVERAGEARBPROC glSampleCoverageARB; +PFNGLACTIVETEXTUREARBPROC glActiveTextureARB; +PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB; +PFNGLMULTITEXCOORD1DARBPROC glMultiTexCoord1dARB; +PFNGLMULTITEXCOORD1DVARBPROC glMultiTexCoord1dvARB; +PFNGLMULTITEXCOORD1FARBPROC glMultiTexCoord1fARB; +PFNGLMULTITEXCOORD1FVARBPROC glMultiTexCoord1fvARB; +PFNGLMULTITEXCOORD1IARBPROC glMultiTexCoord1iARB; +PFNGLMULTITEXCOORD1IVARBPROC glMultiTexCoord1ivARB; +PFNGLMULTITEXCOORD1SARBPROC glMultiTexCoord1sARB; +PFNGLMULTITEXCOORD1SVARBPROC glMultiTexCoord1svARB; +PFNGLMULTITEXCOORD2DARBPROC glMultiTexCoord2dARB; +PFNGLMULTITEXCOORD2DVARBPROC glMultiTexCoord2dvARB; +PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2fARB; +PFNGLMULTITEXCOORD2FVARBPROC glMultiTexCoord2fvARB; +PFNGLMULTITEXCOORD2IARBPROC glMultiTexCoord2iARB; +PFNGLMULTITEXCOORD2IVARBPROC glMultiTexCoord2ivARB; +PFNGLMULTITEXCOORD2SARBPROC glMultiTexCoord2sARB; +PFNGLMULTITEXCOORD2SVARBPROC glMultiTexCoord2svARB; +PFNGLMULTITEXCOORD3DARBPROC glMultiTexCoord3dARB; +PFNGLMULTITEXCOORD3DVARBPROC glMultiTexCoord3dvARB; +PFNGLMULTITEXCOORD3FARBPROC glMultiTexCoord3fARB; +PFNGLMULTITEXCOORD3FVARBPROC glMultiTexCoord3fvARB; +PFNGLMULTITEXCOORD3IARBPROC glMultiTexCoord3iARB; +PFNGLMULTITEXCOORD3IVARBPROC glMultiTexCoord3ivARB; +PFNGLMULTITEXCOORD3SARBPROC glMultiTexCoord3sARB; +PFNGLMULTITEXCOORD3SVARBPROC glMultiTexCoord3svARB; +PFNGLMULTITEXCOORD4DARBPROC glMultiTexCoord4dARB; +PFNGLMULTITEXCOORD4DVARBPROC glMultiTexCoord4dvARB; +PFNGLMULTITEXCOORD4FARBPROC glMultiTexCoord4fARB; +PFNGLMULTITEXCOORD4FVARBPROC glMultiTexCoord4fvARB; +PFNGLMULTITEXCOORD4IARBPROC glMultiTexCoord4iARB; +PFNGLMULTITEXCOORD4IVARBPROC glMultiTexCoord4ivARB; +PFNGLMULTITEXCOORD4SARBPROC glMultiTexCoord4sARB; +PFNGLMULTITEXCOORD4SVARBPROC glMultiTexCoord4svARB; +PFNGLGENQUERIESARBPROC glGenQueriesARB; +PFNGLDELETEQUERIESARBPROC glDeleteQueriesARB; +PFNGLISQUERYARBPROC glIsQueryARB; +PFNGLBEGINQUERYARBPROC glBeginQueryARB; +PFNGLENDQUERYARBPROC glEndQueryARB; +PFNGLGETQUERYIVARBPROC glGetQueryivARB; +PFNGLGETQUERYOBJECTIVARBPROC glGetQueryObjectivARB; +PFNGLGETQUERYOBJECTUIVARBPROC glGetQueryObjectuivARB; +PFNGLMAXSHADERCOMPILERTHREADSARBPROC glMaxShaderCompilerThreadsARB; +PFNGLPOINTPARAMETERFARBPROC glPointParameterfARB; +PFNGLPOINTPARAMETERFVARBPROC glPointParameterfvARB; +PFNGLGETGRAPHICSRESETSTATUSARBPROC glGetGraphicsResetStatusARB; +PFNGLGETNTEXIMAGEARBPROC glGetnTexImageARB; +PFNGLREADNPIXELSARBPROC glReadnPixelsARB; +PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC glGetnCompressedTexImageARB; +PFNGLGETNUNIFORMFVARBPROC glGetnUniformfvARB; +PFNGLGETNUNIFORMIVARBPROC glGetnUniformivARB; +PFNGLGETNUNIFORMUIVARBPROC glGetnUniformuivARB; +PFNGLGETNUNIFORMDVARBPROC glGetnUniformdvARB; +PFNGLGETNMAPDVARBPROC glGetnMapdvARB; +PFNGLGETNMAPFVARBPROC glGetnMapfvARB; +PFNGLGETNMAPIVARBPROC glGetnMapivARB; +PFNGLGETNPIXELMAPFVARBPROC glGetnPixelMapfvARB; +PFNGLGETNPIXELMAPUIVARBPROC glGetnPixelMapuivARB; +PFNGLGETNPIXELMAPUSVARBPROC glGetnPixelMapusvARB; +PFNGLGETNPOLYGONSTIPPLEARBPROC glGetnPolygonStippleARB; +PFNGLGETNCOLORTABLEARBPROC glGetnColorTableARB; +PFNGLGETNCONVOLUTIONFILTERARBPROC glGetnConvolutionFilterARB; +PFNGLGETNSEPARABLEFILTERARBPROC glGetnSeparableFilterARB; +PFNGLGETNHISTOGRAMARBPROC glGetnHistogramARB; +PFNGLGETNMINMAXARBPROC glGetnMinmaxARB; +PFNGLFRAMEBUFFERSAMPLELOCATIONSFVARBPROC glFramebufferSampleLocationsfvARB; +PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVARBPROC glNamedFramebufferSampleLocationsfvARB; +PFNGLEVALUATEDEPTHVALUESARBPROC glEvaluateDepthValuesARB; +PFNGLMINSAMPLESHADINGARBPROC glMinSampleShadingARB; +PFNGLDELETEOBJECTARBPROC glDeleteObjectARB; +PFNGLGETHANDLEARBPROC glGetHandleARB; +PFNGLDETACHOBJECTARBPROC glDetachObjectARB; +PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB; +PFNGLSHADERSOURCEARBPROC glShaderSourceARB; +PFNGLCOMPILESHADERARBPROC glCompileShaderARB; +PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB; +PFNGLATTACHOBJECTARBPROC glAttachObjectARB; +PFNGLLINKPROGRAMARBPROC glLinkProgramARB; +PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB; +PFNGLVALIDATEPROGRAMARBPROC glValidateProgramARB; +PFNGLUNIFORM1FARBPROC glUniform1fARB; +PFNGLUNIFORM2FARBPROC glUniform2fARB; +PFNGLUNIFORM3FARBPROC glUniform3fARB; +PFNGLUNIFORM4FARBPROC glUniform4fARB; +PFNGLUNIFORM1IARBPROC glUniform1iARB; +PFNGLUNIFORM2IARBPROC glUniform2iARB; +PFNGLUNIFORM3IARBPROC glUniform3iARB; +PFNGLUNIFORM4IARBPROC glUniform4iARB; +PFNGLUNIFORM1FVARBPROC glUniform1fvARB; +PFNGLUNIFORM2FVARBPROC glUniform2fvARB; +PFNGLUNIFORM3FVARBPROC glUniform3fvARB; +PFNGLUNIFORM4FVARBPROC glUniform4fvARB; +PFNGLUNIFORM1IVARBPROC glUniform1ivARB; +PFNGLUNIFORM2IVARBPROC glUniform2ivARB; +PFNGLUNIFORM3IVARBPROC glUniform3ivARB; +PFNGLUNIFORM4IVARBPROC glUniform4ivARB; +PFNGLUNIFORMMATRIX2FVARBPROC glUniformMatrix2fvARB; +PFNGLUNIFORMMATRIX3FVARBPROC glUniformMatrix3fvARB; +PFNGLUNIFORMMATRIX4FVARBPROC glUniformMatrix4fvARB; +PFNGLGETOBJECTPARAMETERFVARBPROC glGetObjectParameterfvARB; +PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB; +PFNGLGETINFOLOGARBPROC glGetInfoLogARB; +PFNGLGETATTACHEDOBJECTSARBPROC glGetAttachedObjectsARB; +PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB; +PFNGLGETACTIVEUNIFORMARBPROC glGetActiveUniformARB; +PFNGLGETUNIFORMFVARBPROC glGetUniformfvARB; +PFNGLGETUNIFORMIVARBPROC glGetUniformivARB; +PFNGLGETSHADERSOURCEARBPROC glGetShaderSourceARB; +PFNGLNAMEDSTRINGARBPROC glNamedStringARB; +PFNGLDELETENAMEDSTRINGARBPROC glDeleteNamedStringARB; +PFNGLCOMPILESHADERINCLUDEARBPROC glCompileShaderIncludeARB; +PFNGLISNAMEDSTRINGARBPROC glIsNamedStringARB; +PFNGLGETNAMEDSTRINGARBPROC glGetNamedStringARB; +PFNGLGETNAMEDSTRINGIVARBPROC glGetNamedStringivARB; +PFNGLBUFFERPAGECOMMITMENTARBPROC glBufferPageCommitmentARB; +PFNGLNAMEDBUFFERPAGECOMMITMENTEXTPROC glNamedBufferPageCommitmentEXT; +PFNGLNAMEDBUFFERPAGECOMMITMENTARBPROC glNamedBufferPageCommitmentARB; +PFNGLTEXPAGECOMMITMENTARBPROC glTexPageCommitmentARB; +PFNGLTEXBUFFERARBPROC glTexBufferARB; +PFNGLCOMPRESSEDTEXIMAGE3DARBPROC glCompressedTexImage3DARB; +PFNGLCOMPRESSEDTEXIMAGE2DARBPROC glCompressedTexImage2DARB; +PFNGLCOMPRESSEDTEXIMAGE1DARBPROC glCompressedTexImage1DARB; +PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC glCompressedTexSubImage3DARB; +PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC glCompressedTexSubImage2DARB; +PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC glCompressedTexSubImage1DARB; +PFNGLGETCOMPRESSEDTEXIMAGEARBPROC glGetCompressedTexImageARB; +PFNGLLOADTRANSPOSEMATRIXFARBPROC glLoadTransposeMatrixfARB; +PFNGLLOADTRANSPOSEMATRIXDARBPROC glLoadTransposeMatrixdARB; +PFNGLMULTTRANSPOSEMATRIXFARBPROC glMultTransposeMatrixfARB; +PFNGLMULTTRANSPOSEMATRIXDARBPROC glMultTransposeMatrixdARB; +PFNGLWEIGHTBVARBPROC glWeightbvARB; +PFNGLWEIGHTSVARBPROC glWeightsvARB; +PFNGLWEIGHTIVARBPROC glWeightivARB; +PFNGLWEIGHTFVARBPROC glWeightfvARB; +PFNGLWEIGHTDVARBPROC glWeightdvARB; +PFNGLWEIGHTUBVARBPROC glWeightubvARB; +PFNGLWEIGHTUSVARBPROC glWeightusvARB; +PFNGLWEIGHTUIVARBPROC glWeightuivARB; +PFNGLWEIGHTPOINTERARBPROC glWeightPointerARB; +PFNGLVERTEXBLENDARBPROC glVertexBlendARB; +PFNGLBINDBUFFERARBPROC glBindBufferARB; +PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB; +PFNGLGENBUFFERSARBPROC glGenBuffersARB; +PFNGLISBUFFERARBPROC glIsBufferARB; +PFNGLBUFFERDATAARBPROC glBufferDataARB; +PFNGLBUFFERSUBDATAARBPROC glBufferSubDataARB; +PFNGLGETBUFFERSUBDATAARBPROC glGetBufferSubDataARB; +PFNGLMAPBUFFERARBPROC glMapBufferARB; +PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB; +PFNGLGETBUFFERPARAMETERIVARBPROC glGetBufferParameterivARB; +PFNGLGETBUFFERPOINTERVARBPROC glGetBufferPointervARB; +PFNGLVERTEXATTRIB1DARBPROC glVertexAttrib1dARB; +PFNGLVERTEXATTRIB1DVARBPROC glVertexAttrib1dvARB; +PFNGLVERTEXATTRIB1FARBPROC glVertexAttrib1fARB; +PFNGLVERTEXATTRIB1FVARBPROC glVertexAttrib1fvARB; +PFNGLVERTEXATTRIB1SARBPROC glVertexAttrib1sARB; +PFNGLVERTEXATTRIB1SVARBPROC glVertexAttrib1svARB; +PFNGLVERTEXATTRIB2DARBPROC glVertexAttrib2dARB; +PFNGLVERTEXATTRIB2DVARBPROC glVertexAttrib2dvARB; +PFNGLVERTEXATTRIB2FARBPROC glVertexAttrib2fARB; +PFNGLVERTEXATTRIB2FVARBPROC glVertexAttrib2fvARB; +PFNGLVERTEXATTRIB2SARBPROC glVertexAttrib2sARB; +PFNGLVERTEXATTRIB2SVARBPROC glVertexAttrib2svARB; +PFNGLVERTEXATTRIB3DARBPROC glVertexAttrib3dARB; +PFNGLVERTEXATTRIB3DVARBPROC glVertexAttrib3dvARB; +PFNGLVERTEXATTRIB3FARBPROC glVertexAttrib3fARB; +PFNGLVERTEXATTRIB3FVARBPROC glVertexAttrib3fvARB; +PFNGLVERTEXATTRIB3SARBPROC glVertexAttrib3sARB; +PFNGLVERTEXATTRIB3SVARBPROC glVertexAttrib3svARB; +PFNGLVERTEXATTRIB4NBVARBPROC glVertexAttrib4NbvARB; +PFNGLVERTEXATTRIB4NIVARBPROC glVertexAttrib4NivARB; +PFNGLVERTEXATTRIB4NSVARBPROC glVertexAttrib4NsvARB; +PFNGLVERTEXATTRIB4NUBARBPROC glVertexAttrib4NubARB; +PFNGLVERTEXATTRIB4NUBVARBPROC glVertexAttrib4NubvARB; +PFNGLVERTEXATTRIB4NUIVARBPROC glVertexAttrib4NuivARB; +PFNGLVERTEXATTRIB4NUSVARBPROC glVertexAttrib4NusvARB; +PFNGLVERTEXATTRIB4BVARBPROC glVertexAttrib4bvARB; +PFNGLVERTEXATTRIB4DARBPROC glVertexAttrib4dARB; +PFNGLVERTEXATTRIB4DVARBPROC glVertexAttrib4dvARB; +PFNGLVERTEXATTRIB4FARBPROC glVertexAttrib4fARB; +PFNGLVERTEXATTRIB4FVARBPROC glVertexAttrib4fvARB; +PFNGLVERTEXATTRIB4IVARBPROC glVertexAttrib4ivARB; +PFNGLVERTEXATTRIB4SARBPROC glVertexAttrib4sARB; +PFNGLVERTEXATTRIB4SVARBPROC glVertexAttrib4svARB; +PFNGLVERTEXATTRIB4UBVARBPROC glVertexAttrib4ubvARB; +PFNGLVERTEXATTRIB4UIVARBPROC glVertexAttrib4uivARB; +PFNGLVERTEXATTRIB4USVARBPROC glVertexAttrib4usvARB; +PFNGLVERTEXATTRIBPOINTERARBPROC glVertexAttribPointerARB; +PFNGLENABLEVERTEXATTRIBARRAYARBPROC glEnableVertexAttribArrayARB; +PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glDisableVertexAttribArrayARB; +PFNGLGETVERTEXATTRIBDVARBPROC glGetVertexAttribdvARB; +PFNGLGETVERTEXATTRIBFVARBPROC glGetVertexAttribfvARB; +PFNGLGETVERTEXATTRIBIVARBPROC glGetVertexAttribivARB; +PFNGLGETVERTEXATTRIBPOINTERVARBPROC glGetVertexAttribPointervARB; +PFNGLBINDATTRIBLOCATIONARBPROC glBindAttribLocationARB; +PFNGLGETACTIVEATTRIBARBPROC glGetActiveAttribARB; +PFNGLGETATTRIBLOCATIONARBPROC glGetAttribLocationARB; +PFNGLDEPTHRANGEARRAYDVNVPROC glDepthRangeArraydvNV; +PFNGLDEPTHRANGEINDEXEDDNVPROC glDepthRangeIndexeddNV; +PFNGLWINDOWPOS2DARBPROC glWindowPos2dARB; +PFNGLWINDOWPOS2DVARBPROC glWindowPos2dvARB; +PFNGLWINDOWPOS2FARBPROC glWindowPos2fARB; +PFNGLWINDOWPOS2FVARBPROC glWindowPos2fvARB; +PFNGLWINDOWPOS2IARBPROC glWindowPos2iARB; +PFNGLWINDOWPOS2IVARBPROC glWindowPos2ivARB; +PFNGLWINDOWPOS2SARBPROC glWindowPos2sARB; +PFNGLWINDOWPOS2SVARBPROC glWindowPos2svARB; +PFNGLWINDOWPOS3DARBPROC glWindowPos3dARB; +PFNGLWINDOWPOS3DVARBPROC glWindowPos3dvARB; +PFNGLWINDOWPOS3FARBPROC glWindowPos3fARB; +PFNGLWINDOWPOS3FVARBPROC glWindowPos3fvARB; +PFNGLWINDOWPOS3IARBPROC glWindowPos3iARB; +PFNGLWINDOWPOS3IVARBPROC glWindowPos3ivARB; +PFNGLWINDOWPOS3SARBPROC glWindowPos3sARB; +PFNGLWINDOWPOS3SVARBPROC glWindowPos3svARB; +PFNGLDRAWBUFFERSATIPROC glDrawBuffersATI; +PFNGLELEMENTPOINTERATIPROC glElementPointerATI; +PFNGLDRAWELEMENTARRAYATIPROC glDrawElementArrayATI; +PFNGLDRAWRANGEELEMENTARRAYATIPROC glDrawRangeElementArrayATI; +PFNGLTEXBUMPPARAMETERIVATIPROC glTexBumpParameterivATI; +PFNGLTEXBUMPPARAMETERFVATIPROC glTexBumpParameterfvATI; +PFNGLGETTEXBUMPPARAMETERIVATIPROC glGetTexBumpParameterivATI; +PFNGLGETTEXBUMPPARAMETERFVATIPROC glGetTexBumpParameterfvATI; +PFNGLGENFRAGMENTSHADERSATIPROC glGenFragmentShadersATI; +PFNGLBINDFRAGMENTSHADERATIPROC glBindFragmentShaderATI; +PFNGLDELETEFRAGMENTSHADERATIPROC glDeleteFragmentShaderATI; +PFNGLBEGINFRAGMENTSHADERATIPROC glBeginFragmentShaderATI; +PFNGLENDFRAGMENTSHADERATIPROC glEndFragmentShaderATI; +PFNGLPASSTEXCOORDATIPROC glPassTexCoordATI; +PFNGLSAMPLEMAPATIPROC glSampleMapATI; +PFNGLCOLORFRAGMENTOP1ATIPROC glColorFragmentOp1ATI; +PFNGLCOLORFRAGMENTOP2ATIPROC glColorFragmentOp2ATI; +PFNGLCOLORFRAGMENTOP3ATIPROC glColorFragmentOp3ATI; +PFNGLALPHAFRAGMENTOP1ATIPROC glAlphaFragmentOp1ATI; +PFNGLALPHAFRAGMENTOP2ATIPROC glAlphaFragmentOp2ATI; +PFNGLALPHAFRAGMENTOP3ATIPROC glAlphaFragmentOp3ATI; +PFNGLSETFRAGMENTSHADERCONSTANTATIPROC glSetFragmentShaderConstantATI; +PFNGLMAPOBJECTBUFFERATIPROC glMapObjectBufferATI; +PFNGLUNMAPOBJECTBUFFERATIPROC glUnmapObjectBufferATI; +PFNGLPNTRIANGLESIATIPROC glPNTrianglesiATI; +PFNGLPNTRIANGLESFATIPROC glPNTrianglesfATI; +PFNGLSTENCILOPSEPARATEATIPROC glStencilOpSeparateATI; +PFNGLSTENCILFUNCSEPARATEATIPROC glStencilFuncSeparateATI; +PFNGLNEWOBJECTBUFFERATIPROC glNewObjectBufferATI; +PFNGLISOBJECTBUFFERATIPROC glIsObjectBufferATI; +PFNGLUPDATEOBJECTBUFFERATIPROC glUpdateObjectBufferATI; +PFNGLGETOBJECTBUFFERFVATIPROC glGetObjectBufferfvATI; +PFNGLGETOBJECTBUFFERIVATIPROC glGetObjectBufferivATI; +PFNGLFREEOBJECTBUFFERATIPROC glFreeObjectBufferATI; +PFNGLARRAYOBJECTATIPROC glArrayObjectATI; +PFNGLGETARRAYOBJECTFVATIPROC glGetArrayObjectfvATI; +PFNGLGETARRAYOBJECTIVATIPROC glGetArrayObjectivATI; +PFNGLVARIANTARRAYOBJECTATIPROC glVariantArrayObjectATI; +PFNGLGETVARIANTARRAYOBJECTFVATIPROC glGetVariantArrayObjectfvATI; +PFNGLGETVARIANTARRAYOBJECTIVATIPROC glGetVariantArrayObjectivATI; +PFNGLVERTEXATTRIBARRAYOBJECTATIPROC glVertexAttribArrayObjectATI; +PFNGLGETVERTEXATTRIBARRAYOBJECTFVATIPROC glGetVertexAttribArrayObjectfvATI; +PFNGLGETVERTEXATTRIBARRAYOBJECTIVATIPROC glGetVertexAttribArrayObjectivATI; +PFNGLVERTEXSTREAM1SATIPROC glVertexStream1sATI; +PFNGLVERTEXSTREAM1SVATIPROC glVertexStream1svATI; +PFNGLVERTEXSTREAM1IATIPROC glVertexStream1iATI; +PFNGLVERTEXSTREAM1IVATIPROC glVertexStream1ivATI; +PFNGLVERTEXSTREAM1FATIPROC glVertexStream1fATI; +PFNGLVERTEXSTREAM1FVATIPROC glVertexStream1fvATI; +PFNGLVERTEXSTREAM1DATIPROC glVertexStream1dATI; +PFNGLVERTEXSTREAM1DVATIPROC glVertexStream1dvATI; +PFNGLVERTEXSTREAM2SATIPROC glVertexStream2sATI; +PFNGLVERTEXSTREAM2SVATIPROC glVertexStream2svATI; +PFNGLVERTEXSTREAM2IATIPROC glVertexStream2iATI; +PFNGLVERTEXSTREAM2IVATIPROC glVertexStream2ivATI; +PFNGLVERTEXSTREAM2FATIPROC glVertexStream2fATI; +PFNGLVERTEXSTREAM2FVATIPROC glVertexStream2fvATI; +PFNGLVERTEXSTREAM2DATIPROC glVertexStream2dATI; +PFNGLVERTEXSTREAM2DVATIPROC glVertexStream2dvATI; +PFNGLVERTEXSTREAM3SATIPROC glVertexStream3sATI; +PFNGLVERTEXSTREAM3SVATIPROC glVertexStream3svATI; +PFNGLVERTEXSTREAM3IATIPROC glVertexStream3iATI; +PFNGLVERTEXSTREAM3IVATIPROC glVertexStream3ivATI; +PFNGLVERTEXSTREAM3FATIPROC glVertexStream3fATI; +PFNGLVERTEXSTREAM3FVATIPROC glVertexStream3fvATI; +PFNGLVERTEXSTREAM3DATIPROC glVertexStream3dATI; +PFNGLVERTEXSTREAM3DVATIPROC glVertexStream3dvATI; +PFNGLVERTEXSTREAM4SATIPROC glVertexStream4sATI; +PFNGLVERTEXSTREAM4SVATIPROC glVertexStream4svATI; +PFNGLVERTEXSTREAM4IATIPROC glVertexStream4iATI; +PFNGLVERTEXSTREAM4IVATIPROC glVertexStream4ivATI; +PFNGLVERTEXSTREAM4FATIPROC glVertexStream4fATI; +PFNGLVERTEXSTREAM4FVATIPROC glVertexStream4fvATI; +PFNGLVERTEXSTREAM4DATIPROC glVertexStream4dATI; +PFNGLVERTEXSTREAM4DVATIPROC glVertexStream4dvATI; +PFNGLNORMALSTREAM3BATIPROC glNormalStream3bATI; +PFNGLNORMALSTREAM3BVATIPROC glNormalStream3bvATI; +PFNGLNORMALSTREAM3SATIPROC glNormalStream3sATI; +PFNGLNORMALSTREAM3SVATIPROC glNormalStream3svATI; +PFNGLNORMALSTREAM3IATIPROC glNormalStream3iATI; +PFNGLNORMALSTREAM3IVATIPROC glNormalStream3ivATI; +PFNGLNORMALSTREAM3FATIPROC glNormalStream3fATI; +PFNGLNORMALSTREAM3FVATIPROC glNormalStream3fvATI; +PFNGLNORMALSTREAM3DATIPROC glNormalStream3dATI; +PFNGLNORMALSTREAM3DVATIPROC glNormalStream3dvATI; +PFNGLCLIENTACTIVEVERTEXSTREAMATIPROC glClientActiveVertexStreamATI; +PFNGLVERTEXBLENDENVIATIPROC glVertexBlendEnviATI; +PFNGLVERTEXBLENDENVFATIPROC glVertexBlendEnvfATI; +PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC glEGLImageTargetTexStorageEXT; +PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC glEGLImageTargetTextureStorageEXT; +PFNGLUNIFORMBUFFEREXTPROC glUniformBufferEXT; +PFNGLGETUNIFORMBUFFERSIZEEXTPROC glGetUniformBufferSizeEXT; +PFNGLGETUNIFORMOFFSETEXTPROC glGetUniformOffsetEXT; +PFNGLBLENDCOLOREXTPROC glBlendColorEXT; +PFNGLBLENDEQUATIONSEPARATEEXTPROC glBlendEquationSeparateEXT; +PFNGLBLENDFUNCSEPARATEEXTPROC glBlendFuncSeparateEXT; +PFNGLBLENDEQUATIONEXTPROC glBlendEquationEXT; +PFNGLCOLORSUBTABLEEXTPROC glColorSubTableEXT; +PFNGLCOPYCOLORSUBTABLEEXTPROC glCopyColorSubTableEXT; +PFNGLLOCKARRAYSEXTPROC glLockArraysEXT; +PFNGLUNLOCKARRAYSEXTPROC glUnlockArraysEXT; +PFNGLCONVOLUTIONFILTER1DEXTPROC glConvolutionFilter1DEXT; +PFNGLCONVOLUTIONFILTER2DEXTPROC glConvolutionFilter2DEXT; +PFNGLCONVOLUTIONPARAMETERFEXTPROC glConvolutionParameterfEXT; +PFNGLCONVOLUTIONPARAMETERFVEXTPROC glConvolutionParameterfvEXT; +PFNGLCONVOLUTIONPARAMETERIEXTPROC glConvolutionParameteriEXT; +PFNGLCONVOLUTIONPARAMETERIVEXTPROC glConvolutionParameterivEXT; +PFNGLCOPYCONVOLUTIONFILTER1DEXTPROC glCopyConvolutionFilter1DEXT; +PFNGLCOPYCONVOLUTIONFILTER2DEXTPROC glCopyConvolutionFilter2DEXT; +PFNGLGETCONVOLUTIONFILTEREXTPROC glGetConvolutionFilterEXT; +PFNGLGETCONVOLUTIONPARAMETERFVEXTPROC glGetConvolutionParameterfvEXT; +PFNGLGETCONVOLUTIONPARAMETERIVEXTPROC glGetConvolutionParameterivEXT; +PFNGLGETSEPARABLEFILTEREXTPROC glGetSeparableFilterEXT; +PFNGLSEPARABLEFILTER2DEXTPROC glSeparableFilter2DEXT; +PFNGLTANGENT3BEXTPROC glTangent3bEXT; +PFNGLTANGENT3BVEXTPROC glTangent3bvEXT; +PFNGLTANGENT3DEXTPROC glTangent3dEXT; +PFNGLTANGENT3DVEXTPROC glTangent3dvEXT; +PFNGLTANGENT3FEXTPROC glTangent3fEXT; +PFNGLTANGENT3FVEXTPROC glTangent3fvEXT; +PFNGLTANGENT3IEXTPROC glTangent3iEXT; +PFNGLTANGENT3IVEXTPROC glTangent3ivEXT; +PFNGLTANGENT3SEXTPROC glTangent3sEXT; +PFNGLTANGENT3SVEXTPROC glTangent3svEXT; +PFNGLBINORMAL3BEXTPROC glBinormal3bEXT; +PFNGLBINORMAL3BVEXTPROC glBinormal3bvEXT; +PFNGLBINORMAL3DEXTPROC glBinormal3dEXT; +PFNGLBINORMAL3DVEXTPROC glBinormal3dvEXT; +PFNGLBINORMAL3FEXTPROC glBinormal3fEXT; +PFNGLBINORMAL3FVEXTPROC glBinormal3fvEXT; +PFNGLBINORMAL3IEXTPROC glBinormal3iEXT; +PFNGLBINORMAL3IVEXTPROC glBinormal3ivEXT; +PFNGLBINORMAL3SEXTPROC glBinormal3sEXT; +PFNGLBINORMAL3SVEXTPROC glBinormal3svEXT; +PFNGLTANGENTPOINTEREXTPROC glTangentPointerEXT; +PFNGLBINORMALPOINTEREXTPROC glBinormalPointerEXT; +PFNGLCOPYTEXIMAGE1DEXTPROC glCopyTexImage1DEXT; +PFNGLCOPYTEXIMAGE2DEXTPROC glCopyTexImage2DEXT; +PFNGLCOPYTEXSUBIMAGE1DEXTPROC glCopyTexSubImage1DEXT; +PFNGLCOPYTEXSUBIMAGE2DEXTPROC glCopyTexSubImage2DEXT; +PFNGLCOPYTEXSUBIMAGE3DEXTPROC glCopyTexSubImage3DEXT; +PFNGLCULLPARAMETERDVEXTPROC glCullParameterdvEXT; +PFNGLCULLPARAMETERFVEXTPROC glCullParameterfvEXT; +PFNGLLABELOBJECTEXTPROC glLabelObjectEXT; +PFNGLGETOBJECTLABELEXTPROC glGetObjectLabelEXT; +PFNGLINSERTEVENTMARKEREXTPROC glInsertEventMarkerEXT; +PFNGLPUSHGROUPMARKEREXTPROC glPushGroupMarkerEXT; +PFNGLPOPGROUPMARKEREXTPROC glPopGroupMarkerEXT; +PFNGLDEPTHBOUNDSEXTPROC glDepthBoundsEXT; +PFNGLMATRIXLOADFEXTPROC glMatrixLoadfEXT; +PFNGLMATRIXLOADDEXTPROC glMatrixLoaddEXT; +PFNGLMATRIXMULTFEXTPROC glMatrixMultfEXT; +PFNGLMATRIXMULTDEXTPROC glMatrixMultdEXT; +PFNGLMATRIXLOADIDENTITYEXTPROC glMatrixLoadIdentityEXT; +PFNGLMATRIXROTATEFEXTPROC glMatrixRotatefEXT; +PFNGLMATRIXROTATEDEXTPROC glMatrixRotatedEXT; +PFNGLMATRIXSCALEFEXTPROC glMatrixScalefEXT; +PFNGLMATRIXSCALEDEXTPROC glMatrixScaledEXT; +PFNGLMATRIXTRANSLATEFEXTPROC glMatrixTranslatefEXT; +PFNGLMATRIXTRANSLATEDEXTPROC glMatrixTranslatedEXT; +PFNGLMATRIXFRUSTUMEXTPROC glMatrixFrustumEXT; +PFNGLMATRIXORTHOEXTPROC glMatrixOrthoEXT; +PFNGLMATRIXPOPEXTPROC glMatrixPopEXT; +PFNGLMATRIXPUSHEXTPROC glMatrixPushEXT; +PFNGLCLIENTATTRIBDEFAULTEXTPROC glClientAttribDefaultEXT; +PFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC glPushClientAttribDefaultEXT; +PFNGLTEXTUREPARAMETERFEXTPROC glTextureParameterfEXT; +PFNGLTEXTUREPARAMETERFVEXTPROC glTextureParameterfvEXT; +PFNGLTEXTUREPARAMETERIEXTPROC glTextureParameteriEXT; +PFNGLTEXTUREPARAMETERIVEXTPROC glTextureParameterivEXT; +PFNGLTEXTUREIMAGE1DEXTPROC glTextureImage1DEXT; +PFNGLTEXTUREIMAGE2DEXTPROC glTextureImage2DEXT; +PFNGLTEXTURESUBIMAGE1DEXTPROC glTextureSubImage1DEXT; +PFNGLTEXTURESUBIMAGE2DEXTPROC glTextureSubImage2DEXT; +PFNGLCOPYTEXTUREIMAGE1DEXTPROC glCopyTextureImage1DEXT; +PFNGLCOPYTEXTUREIMAGE2DEXTPROC glCopyTextureImage2DEXT; +PFNGLCOPYTEXTURESUBIMAGE1DEXTPROC glCopyTextureSubImage1DEXT; +PFNGLCOPYTEXTURESUBIMAGE2DEXTPROC glCopyTextureSubImage2DEXT; +PFNGLGETTEXTUREIMAGEEXTPROC glGetTextureImageEXT; +PFNGLGETTEXTUREPARAMETERFVEXTPROC glGetTextureParameterfvEXT; +PFNGLGETTEXTUREPARAMETERIVEXTPROC glGetTextureParameterivEXT; +PFNGLGETTEXTURELEVELPARAMETERFVEXTPROC glGetTextureLevelParameterfvEXT; +PFNGLGETTEXTURELEVELPARAMETERIVEXTPROC glGetTextureLevelParameterivEXT; +PFNGLTEXTUREIMAGE3DEXTPROC glTextureImage3DEXT; +PFNGLTEXTURESUBIMAGE3DEXTPROC glTextureSubImage3DEXT; +PFNGLCOPYTEXTURESUBIMAGE3DEXTPROC glCopyTextureSubImage3DEXT; +PFNGLBINDMULTITEXTUREEXTPROC glBindMultiTextureEXT; +PFNGLMULTITEXCOORDPOINTEREXTPROC glMultiTexCoordPointerEXT; +PFNGLMULTITEXENVFEXTPROC glMultiTexEnvfEXT; +PFNGLMULTITEXENVFVEXTPROC glMultiTexEnvfvEXT; +PFNGLMULTITEXENVIEXTPROC glMultiTexEnviEXT; +PFNGLMULTITEXENVIVEXTPROC glMultiTexEnvivEXT; +PFNGLMULTITEXGENDEXTPROC glMultiTexGendEXT; +PFNGLMULTITEXGENDVEXTPROC glMultiTexGendvEXT; +PFNGLMULTITEXGENFEXTPROC glMultiTexGenfEXT; +PFNGLMULTITEXGENFVEXTPROC glMultiTexGenfvEXT; +PFNGLMULTITEXGENIEXTPROC glMultiTexGeniEXT; +PFNGLMULTITEXGENIVEXTPROC glMultiTexGenivEXT; +PFNGLGETMULTITEXENVFVEXTPROC glGetMultiTexEnvfvEXT; +PFNGLGETMULTITEXENVIVEXTPROC glGetMultiTexEnvivEXT; +PFNGLGETMULTITEXGENDVEXTPROC glGetMultiTexGendvEXT; +PFNGLGETMULTITEXGENFVEXTPROC glGetMultiTexGenfvEXT; +PFNGLGETMULTITEXGENIVEXTPROC glGetMultiTexGenivEXT; +PFNGLMULTITEXPARAMETERIEXTPROC glMultiTexParameteriEXT; +PFNGLMULTITEXPARAMETERIVEXTPROC glMultiTexParameterivEXT; +PFNGLMULTITEXPARAMETERFEXTPROC glMultiTexParameterfEXT; +PFNGLMULTITEXPARAMETERFVEXTPROC glMultiTexParameterfvEXT; +PFNGLMULTITEXIMAGE1DEXTPROC glMultiTexImage1DEXT; +PFNGLMULTITEXIMAGE2DEXTPROC glMultiTexImage2DEXT; +PFNGLMULTITEXSUBIMAGE1DEXTPROC glMultiTexSubImage1DEXT; +PFNGLMULTITEXSUBIMAGE2DEXTPROC glMultiTexSubImage2DEXT; +PFNGLCOPYMULTITEXIMAGE1DEXTPROC glCopyMultiTexImage1DEXT; +PFNGLCOPYMULTITEXIMAGE2DEXTPROC glCopyMultiTexImage2DEXT; +PFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC glCopyMultiTexSubImage1DEXT; +PFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC glCopyMultiTexSubImage2DEXT; +PFNGLGETMULTITEXIMAGEEXTPROC glGetMultiTexImageEXT; +PFNGLGETMULTITEXPARAMETERFVEXTPROC glGetMultiTexParameterfvEXT; +PFNGLGETMULTITEXPARAMETERIVEXTPROC glGetMultiTexParameterivEXT; +PFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC glGetMultiTexLevelParameterfvEXT; +PFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC glGetMultiTexLevelParameterivEXT; +PFNGLMULTITEXIMAGE3DEXTPROC glMultiTexImage3DEXT; +PFNGLMULTITEXSUBIMAGE3DEXTPROC glMultiTexSubImage3DEXT; +PFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC glCopyMultiTexSubImage3DEXT; +PFNGLENABLECLIENTSTATEINDEXEDEXTPROC glEnableClientStateIndexedEXT; +PFNGLDISABLECLIENTSTATEINDEXEDEXTPROC glDisableClientStateIndexedEXT; +PFNGLGETFLOATINDEXEDVEXTPROC glGetFloatIndexedvEXT; +PFNGLGETDOUBLEINDEXEDVEXTPROC glGetDoubleIndexedvEXT; +PFNGLGETPOINTERINDEXEDVEXTPROC glGetPointerIndexedvEXT; +PFNGLENABLEINDEXEDEXTPROC glEnableIndexedEXT; +PFNGLDISABLEINDEXEDEXTPROC glDisableIndexedEXT; +PFNGLISENABLEDINDEXEDEXTPROC glIsEnabledIndexedEXT; +PFNGLGETINTEGERINDEXEDVEXTPROC glGetIntegerIndexedvEXT; +PFNGLGETBOOLEANINDEXEDVEXTPROC glGetBooleanIndexedvEXT; +PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC glCompressedTextureImage3DEXT; +PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC glCompressedTextureImage2DEXT; +PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC glCompressedTextureImage1DEXT; +PFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC glCompressedTextureSubImage3DEXT; +PFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC glCompressedTextureSubImage2DEXT; +PFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC glCompressedTextureSubImage1DEXT; +PFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC glGetCompressedTextureImageEXT; +PFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC glCompressedMultiTexImage3DEXT; +PFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC glCompressedMultiTexImage2DEXT; +PFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC glCompressedMultiTexImage1DEXT; +PFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC glCompressedMultiTexSubImage3DEXT; +PFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC glCompressedMultiTexSubImage2DEXT; +PFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC glCompressedMultiTexSubImage1DEXT; +PFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC glGetCompressedMultiTexImageEXT; +PFNGLMATRIXLOADTRANSPOSEFEXTPROC glMatrixLoadTransposefEXT; +PFNGLMATRIXLOADTRANSPOSEDEXTPROC glMatrixLoadTransposedEXT; +PFNGLMATRIXMULTTRANSPOSEFEXTPROC glMatrixMultTransposefEXT; +PFNGLMATRIXMULTTRANSPOSEDEXTPROC glMatrixMultTransposedEXT; +PFNGLNAMEDBUFFERDATAEXTPROC glNamedBufferDataEXT; +PFNGLNAMEDBUFFERSUBDATAEXTPROC glNamedBufferSubDataEXT; +PFNGLMAPNAMEDBUFFEREXTPROC glMapNamedBufferEXT; +PFNGLUNMAPNAMEDBUFFEREXTPROC glUnmapNamedBufferEXT; +PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC glGetNamedBufferParameterivEXT; +PFNGLGETNAMEDBUFFERPOINTERVEXTPROC glGetNamedBufferPointervEXT; +PFNGLGETNAMEDBUFFERSUBDATAEXTPROC glGetNamedBufferSubDataEXT; +PFNGLPROGRAMUNIFORM1FEXTPROC glProgramUniform1fEXT; +PFNGLPROGRAMUNIFORM2FEXTPROC glProgramUniform2fEXT; +PFNGLPROGRAMUNIFORM3FEXTPROC glProgramUniform3fEXT; +PFNGLPROGRAMUNIFORM4FEXTPROC glProgramUniform4fEXT; +PFNGLPROGRAMUNIFORM1IEXTPROC glProgramUniform1iEXT; +PFNGLPROGRAMUNIFORM2IEXTPROC glProgramUniform2iEXT; +PFNGLPROGRAMUNIFORM3IEXTPROC glProgramUniform3iEXT; +PFNGLPROGRAMUNIFORM4IEXTPROC glProgramUniform4iEXT; +PFNGLPROGRAMUNIFORM1FVEXTPROC glProgramUniform1fvEXT; +PFNGLPROGRAMUNIFORM2FVEXTPROC glProgramUniform2fvEXT; +PFNGLPROGRAMUNIFORM3FVEXTPROC glProgramUniform3fvEXT; +PFNGLPROGRAMUNIFORM4FVEXTPROC glProgramUniform4fvEXT; +PFNGLPROGRAMUNIFORM1IVEXTPROC glProgramUniform1ivEXT; +PFNGLPROGRAMUNIFORM2IVEXTPROC glProgramUniform2ivEXT; +PFNGLPROGRAMUNIFORM3IVEXTPROC glProgramUniform3ivEXT; +PFNGLPROGRAMUNIFORM4IVEXTPROC glProgramUniform4ivEXT; +PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC glProgramUniformMatrix2fvEXT; +PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC glProgramUniformMatrix3fvEXT; +PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC glProgramUniformMatrix4fvEXT; +PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC glProgramUniformMatrix2x3fvEXT; +PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC glProgramUniformMatrix3x2fvEXT; +PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC glProgramUniformMatrix2x4fvEXT; +PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC glProgramUniformMatrix4x2fvEXT; +PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC glProgramUniformMatrix3x4fvEXT; +PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC glProgramUniformMatrix4x3fvEXT; +PFNGLTEXTUREBUFFEREXTPROC glTextureBufferEXT; +PFNGLMULTITEXBUFFEREXTPROC glMultiTexBufferEXT; +PFNGLTEXTUREPARAMETERIIVEXTPROC glTextureParameterIivEXT; +PFNGLTEXTUREPARAMETERIUIVEXTPROC glTextureParameterIuivEXT; +PFNGLGETTEXTUREPARAMETERIIVEXTPROC glGetTextureParameterIivEXT; +PFNGLGETTEXTUREPARAMETERIUIVEXTPROC glGetTextureParameterIuivEXT; +PFNGLMULTITEXPARAMETERIIVEXTPROC glMultiTexParameterIivEXT; +PFNGLMULTITEXPARAMETERIUIVEXTPROC glMultiTexParameterIuivEXT; +PFNGLGETMULTITEXPARAMETERIIVEXTPROC glGetMultiTexParameterIivEXT; +PFNGLGETMULTITEXPARAMETERIUIVEXTPROC glGetMultiTexParameterIuivEXT; +PFNGLPROGRAMUNIFORM1UIEXTPROC glProgramUniform1uiEXT; +PFNGLPROGRAMUNIFORM2UIEXTPROC glProgramUniform2uiEXT; +PFNGLPROGRAMUNIFORM3UIEXTPROC glProgramUniform3uiEXT; +PFNGLPROGRAMUNIFORM4UIEXTPROC glProgramUniform4uiEXT; +PFNGLPROGRAMUNIFORM1UIVEXTPROC glProgramUniform1uivEXT; +PFNGLPROGRAMUNIFORM2UIVEXTPROC glProgramUniform2uivEXT; +PFNGLPROGRAMUNIFORM3UIVEXTPROC glProgramUniform3uivEXT; +PFNGLPROGRAMUNIFORM4UIVEXTPROC glProgramUniform4uivEXT; +PFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC glNamedProgramLocalParameters4fvEXT; +PFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC glNamedProgramLocalParameterI4iEXT; +PFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC glNamedProgramLocalParameterI4ivEXT; +PFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC glNamedProgramLocalParametersI4ivEXT; +PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC glNamedProgramLocalParameterI4uiEXT; +PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC glNamedProgramLocalParameterI4uivEXT; +PFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC glNamedProgramLocalParametersI4uivEXT; +PFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC glGetNamedProgramLocalParameterIivEXT; +PFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC glGetNamedProgramLocalParameterIuivEXT; +PFNGLENABLECLIENTSTATEIEXTPROC glEnableClientStateiEXT; +PFNGLDISABLECLIENTSTATEIEXTPROC glDisableClientStateiEXT; +PFNGLGETFLOATI_VEXTPROC glGetFloati_vEXT; +PFNGLGETDOUBLEI_VEXTPROC glGetDoublei_vEXT; +PFNGLGETPOINTERI_VEXTPROC glGetPointeri_vEXT; +PFNGLNAMEDPROGRAMSTRINGEXTPROC glNamedProgramStringEXT; +PFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC glNamedProgramLocalParameter4dEXT; +PFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC glNamedProgramLocalParameter4dvEXT; +PFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC glNamedProgramLocalParameter4fEXT; +PFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC glNamedProgramLocalParameter4fvEXT; +PFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC glGetNamedProgramLocalParameterdvEXT; +PFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC glGetNamedProgramLocalParameterfvEXT; +PFNGLGETNAMEDPROGRAMIVEXTPROC glGetNamedProgramivEXT; +PFNGLGETNAMEDPROGRAMSTRINGEXTPROC glGetNamedProgramStringEXT; +PFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC glNamedRenderbufferStorageEXT; +PFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC glGetNamedRenderbufferParameterivEXT; +PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glNamedRenderbufferStorageMultisampleEXT; +PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC glNamedRenderbufferStorageMultisampleCoverageEXT; +PFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC glCheckNamedFramebufferStatusEXT; +PFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC glNamedFramebufferTexture1DEXT; +PFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC glNamedFramebufferTexture2DEXT; +PFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC glNamedFramebufferTexture3DEXT; +PFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC glNamedFramebufferRenderbufferEXT; +PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glGetNamedFramebufferAttachmentParameterivEXT; +PFNGLGENERATETEXTUREMIPMAPEXTPROC glGenerateTextureMipmapEXT; +PFNGLGENERATEMULTITEXMIPMAPEXTPROC glGenerateMultiTexMipmapEXT; +PFNGLFRAMEBUFFERDRAWBUFFEREXTPROC glFramebufferDrawBufferEXT; +PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC glFramebufferDrawBuffersEXT; +PFNGLFRAMEBUFFERREADBUFFEREXTPROC glFramebufferReadBufferEXT; +PFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC glGetFramebufferParameterivEXT; +PFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC glNamedCopyBufferSubDataEXT; +PFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC glNamedFramebufferTextureEXT; +PFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC glNamedFramebufferTextureLayerEXT; +PFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC glNamedFramebufferTextureFaceEXT; +PFNGLTEXTURERENDERBUFFEREXTPROC glTextureRenderbufferEXT; +PFNGLMULTITEXRENDERBUFFEREXTPROC glMultiTexRenderbufferEXT; +PFNGLVERTEXARRAYVERTEXOFFSETEXTPROC glVertexArrayVertexOffsetEXT; +PFNGLVERTEXARRAYCOLOROFFSETEXTPROC glVertexArrayColorOffsetEXT; +PFNGLVERTEXARRAYEDGEFLAGOFFSETEXTPROC glVertexArrayEdgeFlagOffsetEXT; +PFNGLVERTEXARRAYINDEXOFFSETEXTPROC glVertexArrayIndexOffsetEXT; +PFNGLVERTEXARRAYNORMALOFFSETEXTPROC glVertexArrayNormalOffsetEXT; +PFNGLVERTEXARRAYTEXCOORDOFFSETEXTPROC glVertexArrayTexCoordOffsetEXT; +PFNGLVERTEXARRAYMULTITEXCOORDOFFSETEXTPROC glVertexArrayMultiTexCoordOffsetEXT; +PFNGLVERTEXARRAYFOGCOORDOFFSETEXTPROC glVertexArrayFogCoordOffsetEXT; +PFNGLVERTEXARRAYSECONDARYCOLOROFFSETEXTPROC glVertexArraySecondaryColorOffsetEXT; +PFNGLVERTEXARRAYVERTEXATTRIBOFFSETEXTPROC glVertexArrayVertexAttribOffsetEXT; +PFNGLVERTEXARRAYVERTEXATTRIBIOFFSETEXTPROC glVertexArrayVertexAttribIOffsetEXT; +PFNGLENABLEVERTEXARRAYEXTPROC glEnableVertexArrayEXT; +PFNGLDISABLEVERTEXARRAYEXTPROC glDisableVertexArrayEXT; +PFNGLENABLEVERTEXARRAYATTRIBEXTPROC glEnableVertexArrayAttribEXT; +PFNGLDISABLEVERTEXARRAYATTRIBEXTPROC glDisableVertexArrayAttribEXT; +PFNGLGETVERTEXARRAYINTEGERVEXTPROC glGetVertexArrayIntegervEXT; +PFNGLGETVERTEXARRAYPOINTERVEXTPROC glGetVertexArrayPointervEXT; +PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC glGetVertexArrayIntegeri_vEXT; +PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC glGetVertexArrayPointeri_vEXT; +PFNGLMAPNAMEDBUFFERRANGEEXTPROC glMapNamedBufferRangeEXT; +PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC glFlushMappedNamedBufferRangeEXT; +PFNGLNAMEDBUFFERSTORAGEEXTPROC glNamedBufferStorageEXT; +PFNGLCLEARNAMEDBUFFERDATAEXTPROC glClearNamedBufferDataEXT; +PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC glClearNamedBufferSubDataEXT; +PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC glNamedFramebufferParameteriEXT; +PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC glGetNamedFramebufferParameterivEXT; +PFNGLPROGRAMUNIFORM1DEXTPROC glProgramUniform1dEXT; +PFNGLPROGRAMUNIFORM2DEXTPROC glProgramUniform2dEXT; +PFNGLPROGRAMUNIFORM3DEXTPROC glProgramUniform3dEXT; +PFNGLPROGRAMUNIFORM4DEXTPROC glProgramUniform4dEXT; +PFNGLPROGRAMUNIFORM1DVEXTPROC glProgramUniform1dvEXT; +PFNGLPROGRAMUNIFORM2DVEXTPROC glProgramUniform2dvEXT; +PFNGLPROGRAMUNIFORM3DVEXTPROC glProgramUniform3dvEXT; +PFNGLPROGRAMUNIFORM4DVEXTPROC glProgramUniform4dvEXT; +PFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC glProgramUniformMatrix2dvEXT; +PFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC glProgramUniformMatrix3dvEXT; +PFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC glProgramUniformMatrix4dvEXT; +PFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC glProgramUniformMatrix2x3dvEXT; +PFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC glProgramUniformMatrix2x4dvEXT; +PFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC glProgramUniformMatrix3x2dvEXT; +PFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC glProgramUniformMatrix3x4dvEXT; +PFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC glProgramUniformMatrix4x2dvEXT; +PFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC glProgramUniformMatrix4x3dvEXT; +PFNGLTEXTUREBUFFERRANGEEXTPROC glTextureBufferRangeEXT; +PFNGLTEXTURESTORAGE1DEXTPROC glTextureStorage1DEXT; +PFNGLTEXTURESTORAGE2DEXTPROC glTextureStorage2DEXT; +PFNGLTEXTURESTORAGE3DEXTPROC glTextureStorage3DEXT; +PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC glTextureStorage2DMultisampleEXT; +PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC glTextureStorage3DMultisampleEXT; +PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC glVertexArrayBindVertexBufferEXT; +PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC glVertexArrayVertexAttribFormatEXT; +PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC glVertexArrayVertexAttribIFormatEXT; +PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC glVertexArrayVertexAttribLFormatEXT; +PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC glVertexArrayVertexAttribBindingEXT; +PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC glVertexArrayVertexBindingDivisorEXT; +PFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC glVertexArrayVertexAttribLOffsetEXT; +PFNGLTEXTUREPAGECOMMITMENTEXTPROC glTexturePageCommitmentEXT; +PFNGLVERTEXARRAYVERTEXATTRIBDIVISOREXTPROC glVertexArrayVertexAttribDivisorEXT; +PFNGLCOLORMASKINDEXEDEXTPROC glColorMaskIndexedEXT; +PFNGLDRAWARRAYSINSTANCEDEXTPROC glDrawArraysInstancedEXT; +PFNGLDRAWELEMENTSINSTANCEDEXTPROC glDrawElementsInstancedEXT; +PFNGLDRAWRANGEELEMENTSEXTPROC glDrawRangeElementsEXT; +PFNGLBUFFERSTORAGEEXTERNALEXTPROC glBufferStorageExternalEXT; +PFNGLNAMEDBUFFERSTORAGEEXTERNALEXTPROC glNamedBufferStorageExternalEXT; +PFNGLFOGCOORDFEXTPROC glFogCoordfEXT; +PFNGLFOGCOORDFVEXTPROC glFogCoordfvEXT; +PFNGLFOGCOORDDEXTPROC glFogCoorddEXT; +PFNGLFOGCOORDDVEXTPROC glFogCoorddvEXT; +PFNGLFOGCOORDPOINTEREXTPROC glFogCoordPointerEXT; +PFNGLBLITFRAMEBUFFEREXTPROC glBlitFramebufferEXT; +PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT; +PFNGLISRENDERBUFFEREXTPROC glIsRenderbufferEXT; +PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT; +PFNGLDELETERENDERBUFFERSEXTPROC glDeleteRenderbuffersEXT; +PFNGLGENRENDERBUFFERSEXTPROC glGenRenderbuffersEXT; +PFNGLRENDERBUFFERSTORAGEEXTPROC glRenderbufferStorageEXT; +PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC glGetRenderbufferParameterivEXT; +PFNGLISFRAMEBUFFEREXTPROC glIsFramebufferEXT; +PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT; +PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT; +PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT; +PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT; +PFNGLFRAMEBUFFERTEXTURE1DEXTPROC glFramebufferTexture1DEXT; +PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT; +PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glFramebufferTexture3DEXT; +PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbufferEXT; +PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glGetFramebufferAttachmentParameterivEXT; +PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmapEXT; +PFNGLPROGRAMPARAMETERIEXTPROC glProgramParameteriEXT; +PFNGLPROGRAMENVPARAMETERS4FVEXTPROC glProgramEnvParameters4fvEXT; +PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC glProgramLocalParameters4fvEXT; +PFNGLGETUNIFORMUIVEXTPROC glGetUniformuivEXT; +PFNGLBINDFRAGDATALOCATIONEXTPROC glBindFragDataLocationEXT; +PFNGLGETFRAGDATALOCATIONEXTPROC glGetFragDataLocationEXT; +PFNGLUNIFORM1UIEXTPROC glUniform1uiEXT; +PFNGLUNIFORM2UIEXTPROC glUniform2uiEXT; +PFNGLUNIFORM3UIEXTPROC glUniform3uiEXT; +PFNGLUNIFORM4UIEXTPROC glUniform4uiEXT; +PFNGLUNIFORM1UIVEXTPROC glUniform1uivEXT; +PFNGLUNIFORM2UIVEXTPROC glUniform2uivEXT; +PFNGLUNIFORM3UIVEXTPROC glUniform3uivEXT; +PFNGLUNIFORM4UIVEXTPROC glUniform4uivEXT; +PFNGLGETHISTOGRAMEXTPROC glGetHistogramEXT; +PFNGLGETHISTOGRAMPARAMETERFVEXTPROC glGetHistogramParameterfvEXT; +PFNGLGETHISTOGRAMPARAMETERIVEXTPROC glGetHistogramParameterivEXT; +PFNGLGETMINMAXEXTPROC glGetMinmaxEXT; +PFNGLGETMINMAXPARAMETERFVEXTPROC glGetMinmaxParameterfvEXT; +PFNGLGETMINMAXPARAMETERIVEXTPROC glGetMinmaxParameterivEXT; +PFNGLHISTOGRAMEXTPROC glHistogramEXT; +PFNGLMINMAXEXTPROC glMinmaxEXT; +PFNGLRESETHISTOGRAMEXTPROC glResetHistogramEXT; +PFNGLRESETMINMAXEXTPROC glResetMinmaxEXT; +PFNGLINDEXFUNCEXTPROC glIndexFuncEXT; +PFNGLINDEXMATERIALEXTPROC glIndexMaterialEXT; +PFNGLAPPLYTEXTUREEXTPROC glApplyTextureEXT; +PFNGLTEXTURELIGHTEXTPROC glTextureLightEXT; +PFNGLTEXTUREMATERIALEXTPROC glTextureMaterialEXT; +PFNGLGETUNSIGNEDBYTEVEXTPROC glGetUnsignedBytevEXT; +PFNGLGETUNSIGNEDBYTEI_VEXTPROC glGetUnsignedBytei_vEXT; +PFNGLDELETEMEMORYOBJECTSEXTPROC glDeleteMemoryObjectsEXT; +PFNGLISMEMORYOBJECTEXTPROC glIsMemoryObjectEXT; +PFNGLCREATEMEMORYOBJECTSEXTPROC glCreateMemoryObjectsEXT; +PFNGLMEMORYOBJECTPARAMETERIVEXTPROC glMemoryObjectParameterivEXT; +PFNGLGETMEMORYOBJECTPARAMETERIVEXTPROC glGetMemoryObjectParameterivEXT; +PFNGLTEXSTORAGEMEM2DEXTPROC glTexStorageMem2DEXT; +PFNGLTEXSTORAGEMEM2DMULTISAMPLEEXTPROC glTexStorageMem2DMultisampleEXT; +PFNGLTEXSTORAGEMEM3DEXTPROC glTexStorageMem3DEXT; +PFNGLTEXSTORAGEMEM3DMULTISAMPLEEXTPROC glTexStorageMem3DMultisampleEXT; +PFNGLBUFFERSTORAGEMEMEXTPROC glBufferStorageMemEXT; +PFNGLTEXTURESTORAGEMEM2DEXTPROC glTextureStorageMem2DEXT; +PFNGLTEXTURESTORAGEMEM2DMULTISAMPLEEXTPROC glTextureStorageMem2DMultisampleEXT; +PFNGLTEXTURESTORAGEMEM3DEXTPROC glTextureStorageMem3DEXT; +PFNGLTEXTURESTORAGEMEM3DMULTISAMPLEEXTPROC glTextureStorageMem3DMultisampleEXT; +PFNGLNAMEDBUFFERSTORAGEMEMEXTPROC glNamedBufferStorageMemEXT; +PFNGLTEXSTORAGEMEM1DEXTPROC glTexStorageMem1DEXT; +PFNGLTEXTURESTORAGEMEM1DEXTPROC glTextureStorageMem1DEXT; +PFNGLIMPORTMEMORYFDEXTPROC glImportMemoryFdEXT; +PFNGLIMPORTMEMORYWIN32HANDLEEXTPROC glImportMemoryWin32HandleEXT; +PFNGLIMPORTMEMORYWIN32NAMEEXTPROC glImportMemoryWin32NameEXT; +PFNGLMULTIDRAWARRAYSEXTPROC glMultiDrawArraysEXT; +PFNGLMULTIDRAWELEMENTSEXTPROC glMultiDrawElementsEXT; +PFNGLSAMPLEMASKEXTPROC glSampleMaskEXT; +PFNGLSAMPLEPATTERNEXTPROC glSamplePatternEXT; +PFNGLCOLORTABLEEXTPROC glColorTableEXT; +PFNGLGETCOLORTABLEEXTPROC glGetColorTableEXT; +PFNGLGETCOLORTABLEPARAMETERIVEXTPROC glGetColorTableParameterivEXT; +PFNGLGETCOLORTABLEPARAMETERFVEXTPROC glGetColorTableParameterfvEXT; +PFNGLPIXELTRANSFORMPARAMETERIEXTPROC glPixelTransformParameteriEXT; +PFNGLPIXELTRANSFORMPARAMETERFEXTPROC glPixelTransformParameterfEXT; +PFNGLPIXELTRANSFORMPARAMETERIVEXTPROC glPixelTransformParameterivEXT; +PFNGLPIXELTRANSFORMPARAMETERFVEXTPROC glPixelTransformParameterfvEXT; +PFNGLGETPIXELTRANSFORMPARAMETERIVEXTPROC glGetPixelTransformParameterivEXT; +PFNGLGETPIXELTRANSFORMPARAMETERFVEXTPROC glGetPixelTransformParameterfvEXT; +PFNGLPOINTPARAMETERFEXTPROC glPointParameterfEXT; +PFNGLPOINTPARAMETERFVEXTPROC glPointParameterfvEXT; +PFNGLPOLYGONOFFSETEXTPROC glPolygonOffsetEXT; +PFNGLPOLYGONOFFSETCLAMPEXTPROC glPolygonOffsetClampEXT; +PFNGLPROVOKINGVERTEXEXTPROC glProvokingVertexEXT; +PFNGLRASTERSAMPLESEXTPROC glRasterSamplesEXT; +PFNGLGENSEMAPHORESEXTPROC glGenSemaphoresEXT; +PFNGLDELETESEMAPHORESEXTPROC glDeleteSemaphoresEXT; +PFNGLISSEMAPHOREEXTPROC glIsSemaphoreEXT; +PFNGLSEMAPHOREPARAMETERUI64VEXTPROC glSemaphoreParameterui64vEXT; +PFNGLGETSEMAPHOREPARAMETERUI64VEXTPROC glGetSemaphoreParameterui64vEXT; +PFNGLWAITSEMAPHOREEXTPROC glWaitSemaphoreEXT; +PFNGLSIGNALSEMAPHOREEXTPROC glSignalSemaphoreEXT; +PFNGLIMPORTSEMAPHOREFDEXTPROC glImportSemaphoreFdEXT; +PFNGLIMPORTSEMAPHOREWIN32HANDLEEXTPROC glImportSemaphoreWin32HandleEXT; +PFNGLIMPORTSEMAPHOREWIN32NAMEEXTPROC glImportSemaphoreWin32NameEXT; +PFNGLSECONDARYCOLOR3BEXTPROC glSecondaryColor3bEXT; +PFNGLSECONDARYCOLOR3BVEXTPROC glSecondaryColor3bvEXT; +PFNGLSECONDARYCOLOR3DEXTPROC glSecondaryColor3dEXT; +PFNGLSECONDARYCOLOR3DVEXTPROC glSecondaryColor3dvEXT; +PFNGLSECONDARYCOLOR3FEXTPROC glSecondaryColor3fEXT; +PFNGLSECONDARYCOLOR3FVEXTPROC glSecondaryColor3fvEXT; +PFNGLSECONDARYCOLOR3IEXTPROC glSecondaryColor3iEXT; +PFNGLSECONDARYCOLOR3IVEXTPROC glSecondaryColor3ivEXT; +PFNGLSECONDARYCOLOR3SEXTPROC glSecondaryColor3sEXT; +PFNGLSECONDARYCOLOR3SVEXTPROC glSecondaryColor3svEXT; +PFNGLSECONDARYCOLOR3UBEXTPROC glSecondaryColor3ubEXT; +PFNGLSECONDARYCOLOR3UBVEXTPROC glSecondaryColor3ubvEXT; +PFNGLSECONDARYCOLOR3UIEXTPROC glSecondaryColor3uiEXT; +PFNGLSECONDARYCOLOR3UIVEXTPROC glSecondaryColor3uivEXT; +PFNGLSECONDARYCOLOR3USEXTPROC glSecondaryColor3usEXT; +PFNGLSECONDARYCOLOR3USVEXTPROC glSecondaryColor3usvEXT; +PFNGLSECONDARYCOLORPOINTEREXTPROC glSecondaryColorPointerEXT; +PFNGLUSESHADERPROGRAMEXTPROC glUseShaderProgramEXT; +PFNGLACTIVEPROGRAMEXTPROC glActiveProgramEXT; +PFNGLCREATESHADERPROGRAMEXTPROC glCreateShaderProgramEXT; +PFNGLACTIVESHADERPROGRAMEXTPROC glActiveShaderProgramEXT; +PFNGLBINDPROGRAMPIPELINEEXTPROC glBindProgramPipelineEXT; +PFNGLCREATESHADERPROGRAMVEXTPROC glCreateShaderProgramvEXT; +PFNGLDELETEPROGRAMPIPELINESEXTPROC glDeleteProgramPipelinesEXT; +PFNGLGENPROGRAMPIPELINESEXTPROC glGenProgramPipelinesEXT; +PFNGLGETPROGRAMPIPELINEINFOLOGEXTPROC glGetProgramPipelineInfoLogEXT; +PFNGLGETPROGRAMPIPELINEIVEXTPROC glGetProgramPipelineivEXT; +PFNGLISPROGRAMPIPELINEEXTPROC glIsProgramPipelineEXT; +PFNGLUSEPROGRAMSTAGESEXTPROC glUseProgramStagesEXT; +PFNGLVALIDATEPROGRAMPIPELINEEXTPROC glValidateProgramPipelineEXT; +PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC glFramebufferFetchBarrierEXT; +PFNGLBINDIMAGETEXTUREEXTPROC glBindImageTextureEXT; +PFNGLMEMORYBARRIEREXTPROC glMemoryBarrierEXT; +PFNGLSTENCILCLEARTAGEXTPROC glStencilClearTagEXT; +PFNGLACTIVESTENCILFACEEXTPROC glActiveStencilFaceEXT; +PFNGLTEXSUBIMAGE1DEXTPROC glTexSubImage1DEXT; +PFNGLTEXSUBIMAGE2DEXTPROC glTexSubImage2DEXT; +PFNGLTEXIMAGE3DEXTPROC glTexImage3DEXT; +PFNGLTEXSUBIMAGE3DEXTPROC glTexSubImage3DEXT; +PFNGLFRAMEBUFFERTEXTURELAYEREXTPROC glFramebufferTextureLayerEXT; +PFNGLTEXBUFFEREXTPROC glTexBufferEXT; +PFNGLTEXPARAMETERIIVEXTPROC glTexParameterIivEXT; +PFNGLTEXPARAMETERIUIVEXTPROC glTexParameterIuivEXT; +PFNGLGETTEXPARAMETERIIVEXTPROC glGetTexParameterIivEXT; +PFNGLGETTEXPARAMETERIUIVEXTPROC glGetTexParameterIuivEXT; +PFNGLCLEARCOLORIIEXTPROC glClearColorIiEXT; +PFNGLCLEARCOLORIUIEXTPROC glClearColorIuiEXT; +PFNGLARETEXTURESRESIDENTEXTPROC glAreTexturesResidentEXT; +PFNGLBINDTEXTUREEXTPROC glBindTextureEXT; +PFNGLDELETETEXTURESEXTPROC glDeleteTexturesEXT; +PFNGLGENTEXTURESEXTPROC glGenTexturesEXT; +PFNGLISTEXTUREEXTPROC glIsTextureEXT; +PFNGLPRIORITIZETEXTURESEXTPROC glPrioritizeTexturesEXT; +PFNGLTEXTURENORMALEXTPROC glTextureNormalEXT; +PFNGLCREATESEMAPHORESNVPROC glCreateSemaphoresNV; +PFNGLSEMAPHOREPARAMETERIVNVPROC glSemaphoreParameterivNV; +PFNGLGETSEMAPHOREPARAMETERIVNVPROC glGetSemaphoreParameterivNV; +PFNGLGETQUERYOBJECTI64VEXTPROC glGetQueryObjecti64vEXT; +PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64vEXT; +PFNGLBEGINTRANSFORMFEEDBACKEXTPROC glBeginTransformFeedbackEXT; +PFNGLENDTRANSFORMFEEDBACKEXTPROC glEndTransformFeedbackEXT; +PFNGLBINDBUFFERRANGEEXTPROC glBindBufferRangeEXT; +PFNGLBINDBUFFEROFFSETEXTPROC glBindBufferOffsetEXT; +PFNGLBINDBUFFERBASEEXTPROC glBindBufferBaseEXT; +PFNGLTRANSFORMFEEDBACKVARYINGSEXTPROC glTransformFeedbackVaryingsEXT; +PFNGLGETTRANSFORMFEEDBACKVARYINGEXTPROC glGetTransformFeedbackVaryingEXT; +PFNGLARRAYELEMENTEXTPROC glArrayElementEXT; +PFNGLCOLORPOINTEREXTPROC glColorPointerEXT; +PFNGLDRAWARRAYSEXTPROC glDrawArraysEXT; +PFNGLEDGEFLAGPOINTEREXTPROC glEdgeFlagPointerEXT; +PFNGLGETPOINTERVEXTPROC glGetPointervEXT; +PFNGLINDEXPOINTEREXTPROC glIndexPointerEXT; +PFNGLNORMALPOINTEREXTPROC glNormalPointerEXT; +PFNGLTEXCOORDPOINTEREXTPROC glTexCoordPointerEXT; +PFNGLVERTEXPOINTEREXTPROC glVertexPointerEXT; +PFNGLVERTEXATTRIBL1DEXTPROC glVertexAttribL1dEXT; +PFNGLVERTEXATTRIBL2DEXTPROC glVertexAttribL2dEXT; +PFNGLVERTEXATTRIBL3DEXTPROC glVertexAttribL3dEXT; +PFNGLVERTEXATTRIBL4DEXTPROC glVertexAttribL4dEXT; +PFNGLVERTEXATTRIBL1DVEXTPROC glVertexAttribL1dvEXT; +PFNGLVERTEXATTRIBL2DVEXTPROC glVertexAttribL2dvEXT; +PFNGLVERTEXATTRIBL3DVEXTPROC glVertexAttribL3dvEXT; +PFNGLVERTEXATTRIBL4DVEXTPROC glVertexAttribL4dvEXT; +PFNGLVERTEXATTRIBLPOINTEREXTPROC glVertexAttribLPointerEXT; +PFNGLGETVERTEXATTRIBLDVEXTPROC glGetVertexAttribLdvEXT; +PFNGLBEGINVERTEXSHADEREXTPROC glBeginVertexShaderEXT; +PFNGLENDVERTEXSHADEREXTPROC glEndVertexShaderEXT; +PFNGLBINDVERTEXSHADEREXTPROC glBindVertexShaderEXT; +PFNGLGENVERTEXSHADERSEXTPROC glGenVertexShadersEXT; +PFNGLDELETEVERTEXSHADEREXTPROC glDeleteVertexShaderEXT; +PFNGLSHADEROP1EXTPROC glShaderOp1EXT; +PFNGLSHADEROP2EXTPROC glShaderOp2EXT; +PFNGLSHADEROP3EXTPROC glShaderOp3EXT; +PFNGLSWIZZLEEXTPROC glSwizzleEXT; +PFNGLWRITEMASKEXTPROC glWriteMaskEXT; +PFNGLINSERTCOMPONENTEXTPROC glInsertComponentEXT; +PFNGLEXTRACTCOMPONENTEXTPROC glExtractComponentEXT; +PFNGLGENSYMBOLSEXTPROC glGenSymbolsEXT; +PFNGLSETINVARIANTEXTPROC glSetInvariantEXT; +PFNGLSETLOCALCONSTANTEXTPROC glSetLocalConstantEXT; +PFNGLVARIANTBVEXTPROC glVariantbvEXT; +PFNGLVARIANTSVEXTPROC glVariantsvEXT; +PFNGLVARIANTIVEXTPROC glVariantivEXT; +PFNGLVARIANTFVEXTPROC glVariantfvEXT; +PFNGLVARIANTDVEXTPROC glVariantdvEXT; +PFNGLVARIANTUBVEXTPROC glVariantubvEXT; +PFNGLVARIANTUSVEXTPROC glVariantusvEXT; +PFNGLVARIANTUIVEXTPROC glVariantuivEXT; +PFNGLVARIANTPOINTEREXTPROC glVariantPointerEXT; +PFNGLENABLEVARIANTCLIENTSTATEEXTPROC glEnableVariantClientStateEXT; +PFNGLDISABLEVARIANTCLIENTSTATEEXTPROC glDisableVariantClientStateEXT; +PFNGLBINDLIGHTPARAMETEREXTPROC glBindLightParameterEXT; +PFNGLBINDMATERIALPARAMETEREXTPROC glBindMaterialParameterEXT; +PFNGLBINDTEXGENPARAMETEREXTPROC glBindTexGenParameterEXT; +PFNGLBINDTEXTUREUNITPARAMETEREXTPROC glBindTextureUnitParameterEXT; +PFNGLBINDPARAMETEREXTPROC glBindParameterEXT; +PFNGLISVARIANTENABLEDEXTPROC glIsVariantEnabledEXT; +PFNGLGETVARIANTBOOLEANVEXTPROC glGetVariantBooleanvEXT; +PFNGLGETVARIANTINTEGERVEXTPROC glGetVariantIntegervEXT; +PFNGLGETVARIANTFLOATVEXTPROC glGetVariantFloatvEXT; +PFNGLGETVARIANTPOINTERVEXTPROC glGetVariantPointervEXT; +PFNGLGETINVARIANTBOOLEANVEXTPROC glGetInvariantBooleanvEXT; +PFNGLGETINVARIANTINTEGERVEXTPROC glGetInvariantIntegervEXT; +PFNGLGETINVARIANTFLOATVEXTPROC glGetInvariantFloatvEXT; +PFNGLGETLOCALCONSTANTBOOLEANVEXTPROC glGetLocalConstantBooleanvEXT; +PFNGLGETLOCALCONSTANTINTEGERVEXTPROC glGetLocalConstantIntegervEXT; +PFNGLGETLOCALCONSTANTFLOATVEXTPROC glGetLocalConstantFloatvEXT; +PFNGLVERTEXWEIGHTFEXTPROC glVertexWeightfEXT; +PFNGLVERTEXWEIGHTFVEXTPROC glVertexWeightfvEXT; +PFNGLVERTEXWEIGHTPOINTEREXTPROC glVertexWeightPointerEXT; +PFNGLACQUIREKEYEDMUTEXWIN32EXTPROC glAcquireKeyedMutexWin32EXT; +PFNGLRELEASEKEYEDMUTEXWIN32EXTPROC glReleaseKeyedMutexWin32EXT; +PFNGLWINDOWRECTANGLESEXTPROC glWindowRectanglesEXT; +PFNGLIMPORTSYNCEXTPROC glImportSyncEXT; +PFNGLFRAMETERMINATORGREMEDYPROC glFrameTerminatorGREMEDY; +PFNGLSTRINGMARKERGREMEDYPROC glStringMarkerGREMEDY; +PFNGLIMAGETRANSFORMPARAMETERIHPPROC glImageTransformParameteriHP; +PFNGLIMAGETRANSFORMPARAMETERFHPPROC glImageTransformParameterfHP; +PFNGLIMAGETRANSFORMPARAMETERIVHPPROC glImageTransformParameterivHP; +PFNGLIMAGETRANSFORMPARAMETERFVHPPROC glImageTransformParameterfvHP; +PFNGLGETIMAGETRANSFORMPARAMETERIVHPPROC glGetImageTransformParameterivHP; +PFNGLGETIMAGETRANSFORMPARAMETERFVHPPROC glGetImageTransformParameterfvHP; +PFNGLMULTIMODEDRAWARRAYSIBMPROC glMultiModeDrawArraysIBM; +PFNGLMULTIMODEDRAWELEMENTSIBMPROC glMultiModeDrawElementsIBM; +PFNGLFLUSHSTATICDATAIBMPROC glFlushStaticDataIBM; +PFNGLCOLORPOINTERLISTIBMPROC glColorPointerListIBM; +PFNGLSECONDARYCOLORPOINTERLISTIBMPROC glSecondaryColorPointerListIBM; +PFNGLEDGEFLAGPOINTERLISTIBMPROC glEdgeFlagPointerListIBM; +PFNGLFOGCOORDPOINTERLISTIBMPROC glFogCoordPointerListIBM; +PFNGLINDEXPOINTERLISTIBMPROC glIndexPointerListIBM; +PFNGLNORMALPOINTERLISTIBMPROC glNormalPointerListIBM; +PFNGLTEXCOORDPOINTERLISTIBMPROC glTexCoordPointerListIBM; +PFNGLVERTEXPOINTERLISTIBMPROC glVertexPointerListIBM; +PFNGLBLENDFUNCSEPARATEINGRPROC glBlendFuncSeparateINGR; +PFNGLAPPLYFRAMEBUFFERATTACHMENTCMAAINTELPROC glApplyFramebufferAttachmentCMAAINTEL; +PFNGLSYNCTEXTUREINTELPROC glSyncTextureINTEL; +PFNGLUNMAPTEXTURE2DINTELPROC glUnmapTexture2DINTEL; +PFNGLMAPTEXTURE2DINTELPROC glMapTexture2DINTEL; +PFNGLVERTEXPOINTERVINTELPROC glVertexPointervINTEL; +PFNGLNORMALPOINTERVINTELPROC glNormalPointervINTEL; +PFNGLCOLORPOINTERVINTELPROC glColorPointervINTEL; +PFNGLTEXCOORDPOINTERVINTELPROC glTexCoordPointervINTEL; +PFNGLBEGINPERFQUERYINTELPROC glBeginPerfQueryINTEL; +PFNGLCREATEPERFQUERYINTELPROC glCreatePerfQueryINTEL; +PFNGLDELETEPERFQUERYINTELPROC glDeletePerfQueryINTEL; +PFNGLENDPERFQUERYINTELPROC glEndPerfQueryINTEL; +PFNGLGETFIRSTPERFQUERYIDINTELPROC glGetFirstPerfQueryIdINTEL; +PFNGLGETNEXTPERFQUERYIDINTELPROC glGetNextPerfQueryIdINTEL; +PFNGLGETPERFCOUNTERINFOINTELPROC glGetPerfCounterInfoINTEL; +PFNGLGETPERFQUERYDATAINTELPROC glGetPerfQueryDataINTEL; +PFNGLGETPERFQUERYIDBYNAMEINTELPROC glGetPerfQueryIdByNameINTEL; +PFNGLGETPERFQUERYINFOINTELPROC glGetPerfQueryInfoINTEL; +PFNGLBLENDBARRIERKHRPROC glBlendBarrierKHR; +PFNGLDEBUGMESSAGECONTROLKHRPROC glDebugMessageControlKHR; +PFNGLDEBUGMESSAGEINSERTKHRPROC glDebugMessageInsertKHR; +PFNGLDEBUGMESSAGECALLBACKKHRPROC glDebugMessageCallbackKHR; +PFNGLGETDEBUGMESSAGELOGKHRPROC glGetDebugMessageLogKHR; +PFNGLPUSHDEBUGGROUPKHRPROC glPushDebugGroupKHR; +PFNGLPOPDEBUGGROUPKHRPROC glPopDebugGroupKHR; +PFNGLOBJECTLABELKHRPROC glObjectLabelKHR; +PFNGLGETOBJECTLABELKHRPROC glGetObjectLabelKHR; +PFNGLOBJECTPTRLABELKHRPROC glObjectPtrLabelKHR; +PFNGLGETOBJECTPTRLABELKHRPROC glGetObjectPtrLabelKHR; +PFNGLGETPOINTERVKHRPROC glGetPointervKHR; +PFNGLGETGRAPHICSRESETSTATUSKHRPROC glGetGraphicsResetStatusKHR; +PFNGLREADNPIXELSKHRPROC glReadnPixelsKHR; +PFNGLGETNUNIFORMFVKHRPROC glGetnUniformfvKHR; +PFNGLGETNUNIFORMIVKHRPROC glGetnUniformivKHR; +PFNGLGETNUNIFORMUIVKHRPROC glGetnUniformuivKHR; +PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR; +PFNGLFRAMEBUFFERPARAMETERIMESAPROC glFramebufferParameteriMESA; +PFNGLGETFRAMEBUFFERPARAMETERIVMESAPROC glGetFramebufferParameterivMESA; +PFNGLRESIZEBUFFERSMESAPROC glResizeBuffersMESA; +PFNGLWINDOWPOS2DMESAPROC glWindowPos2dMESA; +PFNGLWINDOWPOS2DVMESAPROC glWindowPos2dvMESA; +PFNGLWINDOWPOS2FMESAPROC glWindowPos2fMESA; +PFNGLWINDOWPOS2FVMESAPROC glWindowPos2fvMESA; +PFNGLWINDOWPOS2IMESAPROC glWindowPos2iMESA; +PFNGLWINDOWPOS2IVMESAPROC glWindowPos2ivMESA; +PFNGLWINDOWPOS2SMESAPROC glWindowPos2sMESA; +PFNGLWINDOWPOS2SVMESAPROC glWindowPos2svMESA; +PFNGLWINDOWPOS3DMESAPROC glWindowPos3dMESA; +PFNGLWINDOWPOS3DVMESAPROC glWindowPos3dvMESA; +PFNGLWINDOWPOS3FMESAPROC glWindowPos3fMESA; +PFNGLWINDOWPOS3FVMESAPROC glWindowPos3fvMESA; +PFNGLWINDOWPOS3IMESAPROC glWindowPos3iMESA; +PFNGLWINDOWPOS3IVMESAPROC glWindowPos3ivMESA; +PFNGLWINDOWPOS3SMESAPROC glWindowPos3sMESA; +PFNGLWINDOWPOS3SVMESAPROC glWindowPos3svMESA; +PFNGLWINDOWPOS4DMESAPROC glWindowPos4dMESA; +PFNGLWINDOWPOS4DVMESAPROC glWindowPos4dvMESA; +PFNGLWINDOWPOS4FMESAPROC glWindowPos4fMESA; +PFNGLWINDOWPOS4FVMESAPROC glWindowPos4fvMESA; +PFNGLWINDOWPOS4IMESAPROC glWindowPos4iMESA; +PFNGLWINDOWPOS4IVMESAPROC glWindowPos4ivMESA; +PFNGLWINDOWPOS4SMESAPROC glWindowPos4sMESA; +PFNGLWINDOWPOS4SVMESAPROC glWindowPos4svMESA; +PFNGLBEGINCONDITIONALRENDERNVXPROC glBeginConditionalRenderNVX; +PFNGLENDCONDITIONALRENDERNVXPROC glEndConditionalRenderNVX; +PFNGLLGPUNAMEDBUFFERSUBDATANVXPROC glLGPUNamedBufferSubDataNVX; +PFNGLLGPUCOPYIMAGESUBDATANVXPROC glLGPUCopyImageSubDataNVX; +PFNGLLGPUINTERLOCKNVXPROC glLGPUInterlockNVX; +PFNGLALPHATOCOVERAGEDITHERCONTROLNVPROC glAlphaToCoverageDitherControlNV; +PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC glMultiDrawArraysIndirectBindlessNV; +PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSNVPROC glMultiDrawElementsIndirectBindlessNV; +PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSCOUNTNVPROC glMultiDrawArraysIndirectBindlessCountNV; +PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSCOUNTNVPROC glMultiDrawElementsIndirectBindlessCountNV; +PFNGLGETTEXTUREHANDLENVPROC glGetTextureHandleNV; +PFNGLGETTEXTURESAMPLERHANDLENVPROC glGetTextureSamplerHandleNV; +PFNGLMAKETEXTUREHANDLERESIDENTNVPROC glMakeTextureHandleResidentNV; +PFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC glMakeTextureHandleNonResidentNV; +PFNGLGETIMAGEHANDLENVPROC glGetImageHandleNV; +PFNGLMAKEIMAGEHANDLERESIDENTNVPROC glMakeImageHandleResidentNV; +PFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC glMakeImageHandleNonResidentNV; +PFNGLUNIFORMHANDLEUI64NVPROC glUniformHandleui64NV; +PFNGLUNIFORMHANDLEUI64VNVPROC glUniformHandleui64vNV; +PFNGLPROGRAMUNIFORMHANDLEUI64NVPROC glProgramUniformHandleui64NV; +PFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC glProgramUniformHandleui64vNV; +PFNGLISTEXTUREHANDLERESIDENTNVPROC glIsTextureHandleResidentNV; +PFNGLISIMAGEHANDLERESIDENTNVPROC glIsImageHandleResidentNV; +PFNGLBLENDPARAMETERINVPROC glBlendParameteriNV; +PFNGLBLENDBARRIERNVPROC glBlendBarrierNV; +PFNGLVIEWPORTPOSITIONWSCALENVPROC glViewportPositionWScaleNV; +PFNGLCREATESTATESNVPROC glCreateStatesNV; +PFNGLDELETESTATESNVPROC glDeleteStatesNV; +PFNGLISSTATENVPROC glIsStateNV; +PFNGLSTATECAPTURENVPROC glStateCaptureNV; +PFNGLGETCOMMANDHEADERNVPROC glGetCommandHeaderNV; +PFNGLGETSTAGEINDEXNVPROC glGetStageIndexNV; +PFNGLDRAWCOMMANDSNVPROC glDrawCommandsNV; +PFNGLDRAWCOMMANDSADDRESSNVPROC glDrawCommandsAddressNV; +PFNGLDRAWCOMMANDSSTATESNVPROC glDrawCommandsStatesNV; +PFNGLDRAWCOMMANDSSTATESADDRESSNVPROC glDrawCommandsStatesAddressNV; +PFNGLCREATECOMMANDLISTSNVPROC glCreateCommandListsNV; +PFNGLDELETECOMMANDLISTSNVPROC glDeleteCommandListsNV; +PFNGLISCOMMANDLISTNVPROC glIsCommandListNV; +PFNGLLISTDRAWCOMMANDSSTATESCLIENTNVPROC glListDrawCommandsStatesClientNV; +PFNGLCOMMANDLISTSEGMENTSNVPROC glCommandListSegmentsNV; +PFNGLCOMPILECOMMANDLISTNVPROC glCompileCommandListNV; +PFNGLCALLCOMMANDLISTNVPROC glCallCommandListNV; +PFNGLBEGINCONDITIONALRENDERNVPROC glBeginConditionalRenderNV; +PFNGLENDCONDITIONALRENDERNVPROC glEndConditionalRenderNV; +PFNGLSUBPIXELPRECISIONBIASNVPROC glSubpixelPrecisionBiasNV; +PFNGLCONSERVATIVERASTERPARAMETERFNVPROC glConservativeRasterParameterfNV; +PFNGLCONSERVATIVERASTERPARAMETERINVPROC glConservativeRasterParameteriNV; +PFNGLCOPYIMAGESUBDATANVPROC glCopyImageSubDataNV; +PFNGLDEPTHRANGEDNVPROC glDepthRangedNV; +PFNGLCLEARDEPTHDNVPROC glClearDepthdNV; +PFNGLDEPTHBOUNDSDNVPROC glDepthBoundsdNV; +PFNGLDRAWTEXTURENVPROC glDrawTextureNV; +PFNGLDRAWVKIMAGENVPROC glDrawVkImageNV; +PFNGLGETVKPROCADDRNVPROC glGetVkProcAddrNV; +PFNGLWAITVKSEMAPHORENVPROC glWaitVkSemaphoreNV; +PFNGLSIGNALVKSEMAPHORENVPROC glSignalVkSemaphoreNV; +PFNGLSIGNALVKFENCENVPROC glSignalVkFenceNV; +PFNGLMAPCONTROLPOINTSNVPROC glMapControlPointsNV; +PFNGLMAPPARAMETERIVNVPROC glMapParameterivNV; +PFNGLMAPPARAMETERFVNVPROC glMapParameterfvNV; +PFNGLGETMAPCONTROLPOINTSNVPROC glGetMapControlPointsNV; +PFNGLGETMAPPARAMETERIVNVPROC glGetMapParameterivNV; +PFNGLGETMAPPARAMETERFVNVPROC glGetMapParameterfvNV; +PFNGLGETMAPATTRIBPARAMETERIVNVPROC glGetMapAttribParameterivNV; +PFNGLGETMAPATTRIBPARAMETERFVNVPROC glGetMapAttribParameterfvNV; +PFNGLEVALMAPSNVPROC glEvalMapsNV; +PFNGLGETMULTISAMPLEFVNVPROC glGetMultisamplefvNV; +PFNGLSAMPLEMASKINDEXEDNVPROC glSampleMaskIndexedNV; +PFNGLTEXRENDERBUFFERNVPROC glTexRenderbufferNV; +PFNGLDELETEFENCESNVPROC glDeleteFencesNV; +PFNGLGENFENCESNVPROC glGenFencesNV; +PFNGLISFENCENVPROC glIsFenceNV; +PFNGLTESTFENCENVPROC glTestFenceNV; +PFNGLGETFENCEIVNVPROC glGetFenceivNV; +PFNGLFINISHFENCENVPROC glFinishFenceNV; +PFNGLSETFENCENVPROC glSetFenceNV; +PFNGLFRAGMENTCOVERAGECOLORNVPROC glFragmentCoverageColorNV; +PFNGLPROGRAMNAMEDPARAMETER4FNVPROC glProgramNamedParameter4fNV; +PFNGLPROGRAMNAMEDPARAMETER4FVNVPROC glProgramNamedParameter4fvNV; +PFNGLPROGRAMNAMEDPARAMETER4DNVPROC glProgramNamedParameter4dNV; +PFNGLPROGRAMNAMEDPARAMETER4DVNVPROC glProgramNamedParameter4dvNV; +PFNGLGETPROGRAMNAMEDPARAMETERFVNVPROC glGetProgramNamedParameterfvNV; +PFNGLGETPROGRAMNAMEDPARAMETERDVNVPROC glGetProgramNamedParameterdvNV; +PFNGLCOVERAGEMODULATIONTABLENVPROC glCoverageModulationTableNV; +PFNGLGETCOVERAGEMODULATIONTABLENVPROC glGetCoverageModulationTableNV; +PFNGLCOVERAGEMODULATIONNVPROC glCoverageModulationNV; +PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC glRenderbufferStorageMultisampleCoverageNV; +PFNGLPROGRAMVERTEXLIMITNVPROC glProgramVertexLimitNV; +PFNGLFRAMEBUFFERTEXTUREEXTPROC glFramebufferTextureEXT; +PFNGLFRAMEBUFFERTEXTUREFACEEXTPROC glFramebufferTextureFaceEXT; +PFNGLPROGRAMLOCALPARAMETERI4INVPROC glProgramLocalParameterI4iNV; +PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC glProgramLocalParameterI4ivNV; +PFNGLPROGRAMLOCALPARAMETERSI4IVNVPROC glProgramLocalParametersI4ivNV; +PFNGLPROGRAMLOCALPARAMETERI4UINVPROC glProgramLocalParameterI4uiNV; +PFNGLPROGRAMLOCALPARAMETERI4UIVNVPROC glProgramLocalParameterI4uivNV; +PFNGLPROGRAMLOCALPARAMETERSI4UIVNVPROC glProgramLocalParametersI4uivNV; +PFNGLPROGRAMENVPARAMETERI4INVPROC glProgramEnvParameterI4iNV; +PFNGLPROGRAMENVPARAMETERI4IVNVPROC glProgramEnvParameterI4ivNV; +PFNGLPROGRAMENVPARAMETERSI4IVNVPROC glProgramEnvParametersI4ivNV; +PFNGLPROGRAMENVPARAMETERI4UINVPROC glProgramEnvParameterI4uiNV; +PFNGLPROGRAMENVPARAMETERI4UIVNVPROC glProgramEnvParameterI4uivNV; +PFNGLPROGRAMENVPARAMETERSI4UIVNVPROC glProgramEnvParametersI4uivNV; +PFNGLGETPROGRAMLOCALPARAMETERIIVNVPROC glGetProgramLocalParameterIivNV; +PFNGLGETPROGRAMLOCALPARAMETERIUIVNVPROC glGetProgramLocalParameterIuivNV; +PFNGLGETPROGRAMENVPARAMETERIIVNVPROC glGetProgramEnvParameterIivNV; +PFNGLGETPROGRAMENVPARAMETERIUIVNVPROC glGetProgramEnvParameterIuivNV; +PFNGLPROGRAMSUBROUTINEPARAMETERSUIVNVPROC glProgramSubroutineParametersuivNV; +PFNGLGETPROGRAMSUBROUTINEPARAMETERUIVNVPROC glGetProgramSubroutineParameteruivNV; +PFNGLVERTEX2HNVPROC glVertex2hNV; +PFNGLVERTEX2HVNVPROC glVertex2hvNV; +PFNGLVERTEX3HNVPROC glVertex3hNV; +PFNGLVERTEX3HVNVPROC glVertex3hvNV; +PFNGLVERTEX4HNVPROC glVertex4hNV; +PFNGLVERTEX4HVNVPROC glVertex4hvNV; +PFNGLNORMAL3HNVPROC glNormal3hNV; +PFNGLNORMAL3HVNVPROC glNormal3hvNV; +PFNGLCOLOR3HNVPROC glColor3hNV; +PFNGLCOLOR3HVNVPROC glColor3hvNV; +PFNGLCOLOR4HNVPROC glColor4hNV; +PFNGLCOLOR4HVNVPROC glColor4hvNV; +PFNGLTEXCOORD1HNVPROC glTexCoord1hNV; +PFNGLTEXCOORD1HVNVPROC glTexCoord1hvNV; +PFNGLTEXCOORD2HNVPROC glTexCoord2hNV; +PFNGLTEXCOORD2HVNVPROC glTexCoord2hvNV; +PFNGLTEXCOORD3HNVPROC glTexCoord3hNV; +PFNGLTEXCOORD3HVNVPROC glTexCoord3hvNV; +PFNGLTEXCOORD4HNVPROC glTexCoord4hNV; +PFNGLTEXCOORD4HVNVPROC glTexCoord4hvNV; +PFNGLMULTITEXCOORD1HNVPROC glMultiTexCoord1hNV; +PFNGLMULTITEXCOORD1HVNVPROC glMultiTexCoord1hvNV; +PFNGLMULTITEXCOORD2HNVPROC glMultiTexCoord2hNV; +PFNGLMULTITEXCOORD2HVNVPROC glMultiTexCoord2hvNV; +PFNGLMULTITEXCOORD3HNVPROC glMultiTexCoord3hNV; +PFNGLMULTITEXCOORD3HVNVPROC glMultiTexCoord3hvNV; +PFNGLMULTITEXCOORD4HNVPROC glMultiTexCoord4hNV; +PFNGLMULTITEXCOORD4HVNVPROC glMultiTexCoord4hvNV; +PFNGLFOGCOORDHNVPROC glFogCoordhNV; +PFNGLFOGCOORDHVNVPROC glFogCoordhvNV; +PFNGLSECONDARYCOLOR3HNVPROC glSecondaryColor3hNV; +PFNGLSECONDARYCOLOR3HVNVPROC glSecondaryColor3hvNV; +PFNGLVERTEXWEIGHTHNVPROC glVertexWeighthNV; +PFNGLVERTEXWEIGHTHVNVPROC glVertexWeighthvNV; +PFNGLVERTEXATTRIB1HNVPROC glVertexAttrib1hNV; +PFNGLVERTEXATTRIB1HVNVPROC glVertexAttrib1hvNV; +PFNGLVERTEXATTRIB2HNVPROC glVertexAttrib2hNV; +PFNGLVERTEXATTRIB2HVNVPROC glVertexAttrib2hvNV; +PFNGLVERTEXATTRIB3HNVPROC glVertexAttrib3hNV; +PFNGLVERTEXATTRIB3HVNVPROC glVertexAttrib3hvNV; +PFNGLVERTEXATTRIB4HNVPROC glVertexAttrib4hNV; +PFNGLVERTEXATTRIB4HVNVPROC glVertexAttrib4hvNV; +PFNGLVERTEXATTRIBS1HVNVPROC glVertexAttribs1hvNV; +PFNGLVERTEXATTRIBS2HVNVPROC glVertexAttribs2hvNV; +PFNGLVERTEXATTRIBS3HVNVPROC glVertexAttribs3hvNV; +PFNGLVERTEXATTRIBS4HVNVPROC glVertexAttribs4hvNV; +PFNGLGETINTERNALFORMATSAMPLEIVNVPROC glGetInternalformatSampleivNV; +PFNGLRENDERGPUMASKNVPROC glRenderGpuMaskNV; +PFNGLMULTICASTBUFFERSUBDATANVPROC glMulticastBufferSubDataNV; +PFNGLMULTICASTCOPYBUFFERSUBDATANVPROC glMulticastCopyBufferSubDataNV; +PFNGLMULTICASTCOPYIMAGESUBDATANVPROC glMulticastCopyImageSubDataNV; +PFNGLMULTICASTBLITFRAMEBUFFERNVPROC glMulticastBlitFramebufferNV; +PFNGLMULTICASTFRAMEBUFFERSAMPLELOCATIONSFVNVPROC glMulticastFramebufferSampleLocationsfvNV; +PFNGLMULTICASTBARRIERNVPROC glMulticastBarrierNV; +PFNGLMULTICASTWAITSYNCNVPROC glMulticastWaitSyncNV; +PFNGLMULTICASTGETQUERYOBJECTIVNVPROC glMulticastGetQueryObjectivNV; +PFNGLMULTICASTGETQUERYOBJECTUIVNVPROC glMulticastGetQueryObjectuivNV; +PFNGLMULTICASTGETQUERYOBJECTI64VNVPROC glMulticastGetQueryObjecti64vNV; +PFNGLMULTICASTGETQUERYOBJECTUI64VNVPROC glMulticastGetQueryObjectui64vNV; +PFNGLUPLOADGPUMASKNVXPROC glUploadGpuMaskNVX; +PFNGLMULTICASTVIEWPORTARRAYVNVXPROC glMulticastViewportArrayvNVX; +PFNGLMULTICASTVIEWPORTPOSITIONWSCALENVXPROC glMulticastViewportPositionWScaleNVX; +PFNGLMULTICASTSCISSORARRAYVNVXPROC glMulticastScissorArrayvNVX; +PFNGLASYNCCOPYBUFFERSUBDATANVXPROC glAsyncCopyBufferSubDataNVX; +PFNGLASYNCCOPYIMAGESUBDATANVXPROC glAsyncCopyImageSubDataNVX; +PFNGLCREATEPROGRESSFENCENVXPROC glCreateProgressFenceNVX; +PFNGLSIGNALSEMAPHOREUI64NVXPROC glSignalSemaphoreui64NVX; +PFNGLWAITSEMAPHOREUI64NVXPROC glWaitSemaphoreui64NVX; +PFNGLCLIENTWAITSEMAPHOREUI64NVXPROC glClientWaitSemaphoreui64NVX; +PFNGLGETMEMORYOBJECTDETACHEDRESOURCESUIVNVPROC glGetMemoryObjectDetachedResourcesuivNV; +PFNGLRESETMEMORYOBJECTPARAMETERNVPROC glResetMemoryObjectParameterNV; +PFNGLTEXATTACHMEMORYNVPROC glTexAttachMemoryNV; +PFNGLBUFFERATTACHMEMORYNVPROC glBufferAttachMemoryNV; +PFNGLTEXTUREATTACHMEMORYNVPROC glTextureAttachMemoryNV; +PFNGLNAMEDBUFFERATTACHMEMORYNVPROC glNamedBufferAttachMemoryNV; +PFNGLBUFFERPAGECOMMITMENTMEMNVPROC glBufferPageCommitmentMemNV; +PFNGLTEXPAGECOMMITMENTMEMNVPROC glTexPageCommitmentMemNV; +PFNGLNAMEDBUFFERPAGECOMMITMENTMEMNVPROC glNamedBufferPageCommitmentMemNV; +PFNGLTEXTUREPAGECOMMITMENTMEMNVPROC glTexturePageCommitmentMemNV; +PFNGLDRAWMESHTASKSNVPROC glDrawMeshTasksNV; +PFNGLDRAWMESHTASKSINDIRECTNVPROC glDrawMeshTasksIndirectNV; +PFNGLMULTIDRAWMESHTASKSINDIRECTNVPROC glMultiDrawMeshTasksIndirectNV; +PFNGLMULTIDRAWMESHTASKSINDIRECTCOUNTNVPROC glMultiDrawMeshTasksIndirectCountNV; +PFNGLGENOCCLUSIONQUERIESNVPROC glGenOcclusionQueriesNV; +PFNGLDELETEOCCLUSIONQUERIESNVPROC glDeleteOcclusionQueriesNV; +PFNGLISOCCLUSIONQUERYNVPROC glIsOcclusionQueryNV; +PFNGLBEGINOCCLUSIONQUERYNVPROC glBeginOcclusionQueryNV; +PFNGLENDOCCLUSIONQUERYNVPROC glEndOcclusionQueryNV; +PFNGLGETOCCLUSIONQUERYIVNVPROC glGetOcclusionQueryivNV; +PFNGLGETOCCLUSIONQUERYUIVNVPROC glGetOcclusionQueryuivNV; +PFNGLPROGRAMBUFFERPARAMETERSFVNVPROC glProgramBufferParametersfvNV; +PFNGLPROGRAMBUFFERPARAMETERSIIVNVPROC glProgramBufferParametersIivNV; +PFNGLPROGRAMBUFFERPARAMETERSIUIVNVPROC glProgramBufferParametersIuivNV; +PFNGLGENPATHSNVPROC glGenPathsNV; +PFNGLDELETEPATHSNVPROC glDeletePathsNV; +PFNGLISPATHNVPROC glIsPathNV; +PFNGLPATHCOMMANDSNVPROC glPathCommandsNV; +PFNGLPATHCOORDSNVPROC glPathCoordsNV; +PFNGLPATHSUBCOMMANDSNVPROC glPathSubCommandsNV; +PFNGLPATHSUBCOORDSNVPROC glPathSubCoordsNV; +PFNGLPATHSTRINGNVPROC glPathStringNV; +PFNGLPATHGLYPHSNVPROC glPathGlyphsNV; +PFNGLPATHGLYPHRANGENVPROC glPathGlyphRangeNV; +PFNGLWEIGHTPATHSNVPROC glWeightPathsNV; +PFNGLCOPYPATHNVPROC glCopyPathNV; +PFNGLINTERPOLATEPATHSNVPROC glInterpolatePathsNV; +PFNGLTRANSFORMPATHNVPROC glTransformPathNV; +PFNGLPATHPARAMETERIVNVPROC glPathParameterivNV; +PFNGLPATHPARAMETERINVPROC glPathParameteriNV; +PFNGLPATHPARAMETERFVNVPROC glPathParameterfvNV; +PFNGLPATHPARAMETERFNVPROC glPathParameterfNV; +PFNGLPATHDASHARRAYNVPROC glPathDashArrayNV; +PFNGLPATHSTENCILFUNCNVPROC glPathStencilFuncNV; +PFNGLPATHSTENCILDEPTHOFFSETNVPROC glPathStencilDepthOffsetNV; +PFNGLSTENCILFILLPATHNVPROC glStencilFillPathNV; +PFNGLSTENCILSTROKEPATHNVPROC glStencilStrokePathNV; +PFNGLSTENCILFILLPATHINSTANCEDNVPROC glStencilFillPathInstancedNV; +PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC glStencilStrokePathInstancedNV; +PFNGLPATHCOVERDEPTHFUNCNVPROC glPathCoverDepthFuncNV; +PFNGLCOVERFILLPATHNVPROC glCoverFillPathNV; +PFNGLCOVERSTROKEPATHNVPROC glCoverStrokePathNV; +PFNGLCOVERFILLPATHINSTANCEDNVPROC glCoverFillPathInstancedNV; +PFNGLCOVERSTROKEPATHINSTANCEDNVPROC glCoverStrokePathInstancedNV; +PFNGLGETPATHPARAMETERIVNVPROC glGetPathParameterivNV; +PFNGLGETPATHPARAMETERFVNVPROC glGetPathParameterfvNV; +PFNGLGETPATHCOMMANDSNVPROC glGetPathCommandsNV; +PFNGLGETPATHCOORDSNVPROC glGetPathCoordsNV; +PFNGLGETPATHDASHARRAYNVPROC glGetPathDashArrayNV; +PFNGLGETPATHMETRICSNVPROC glGetPathMetricsNV; +PFNGLGETPATHMETRICRANGENVPROC glGetPathMetricRangeNV; +PFNGLGETPATHSPACINGNVPROC glGetPathSpacingNV; +PFNGLISPOINTINFILLPATHNVPROC glIsPointInFillPathNV; +PFNGLISPOINTINSTROKEPATHNVPROC glIsPointInStrokePathNV; +PFNGLGETPATHLENGTHNVPROC glGetPathLengthNV; +PFNGLPOINTALONGPATHNVPROC glPointAlongPathNV; +PFNGLMATRIXLOAD3X2FNVPROC glMatrixLoad3x2fNV; +PFNGLMATRIXLOAD3X3FNVPROC glMatrixLoad3x3fNV; +PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC glMatrixLoadTranspose3x3fNV; +PFNGLMATRIXMULT3X2FNVPROC glMatrixMult3x2fNV; +PFNGLMATRIXMULT3X3FNVPROC glMatrixMult3x3fNV; +PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC glMatrixMultTranspose3x3fNV; +PFNGLSTENCILTHENCOVERFILLPATHNVPROC glStencilThenCoverFillPathNV; +PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC glStencilThenCoverStrokePathNV; +PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC glStencilThenCoverFillPathInstancedNV; +PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC glStencilThenCoverStrokePathInstancedNV; +PFNGLPATHGLYPHINDEXRANGENVPROC glPathGlyphIndexRangeNV; +PFNGLPATHGLYPHINDEXARRAYNVPROC glPathGlyphIndexArrayNV; +PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC glPathMemoryGlyphIndexArrayNV; +PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC glProgramPathFragmentInputGenNV; +PFNGLGETPROGRAMRESOURCEFVNVPROC glGetProgramResourcefvNV; +PFNGLPATHCOLORGENNVPROC glPathColorGenNV; +PFNGLPATHTEXGENNVPROC glPathTexGenNV; +PFNGLPATHFOGGENNVPROC glPathFogGenNV; +PFNGLGETPATHCOLORGENIVNVPROC glGetPathColorGenivNV; +PFNGLGETPATHCOLORGENFVNVPROC glGetPathColorGenfvNV; +PFNGLGETPATHTEXGENIVNVPROC glGetPathTexGenivNV; +PFNGLGETPATHTEXGENFVNVPROC glGetPathTexGenfvNV; +PFNGLPIXELDATARANGENVPROC glPixelDataRangeNV; +PFNGLFLUSHPIXELDATARANGENVPROC glFlushPixelDataRangeNV; +PFNGLPOINTPARAMETERINVPROC glPointParameteriNV; +PFNGLPOINTPARAMETERIVNVPROC glPointParameterivNV; +PFNGLPRESENTFRAMEKEYEDNVPROC glPresentFrameKeyedNV; +PFNGLPRESENTFRAMEDUALFILLNVPROC glPresentFrameDualFillNV; +PFNGLGETVIDEOIVNVPROC glGetVideoivNV; +PFNGLGETVIDEOUIVNVPROC glGetVideouivNV; +PFNGLGETVIDEOI64VNVPROC glGetVideoi64vNV; +PFNGLGETVIDEOUI64VNVPROC glGetVideoui64vNV; +PFNGLPRIMITIVERESTARTNVPROC glPrimitiveRestartNV; +PFNGLPRIMITIVERESTARTINDEXNVPROC glPrimitiveRestartIndexNV; +PFNGLQUERYRESOURCENVPROC glQueryResourceNV; +PFNGLGENQUERYRESOURCETAGNVPROC glGenQueryResourceTagNV; +PFNGLDELETEQUERYRESOURCETAGNVPROC glDeleteQueryResourceTagNV; +PFNGLQUERYRESOURCETAGNVPROC glQueryResourceTagNV; +PFNGLCOMBINERPARAMETERFVNVPROC glCombinerParameterfvNV; +PFNGLCOMBINERPARAMETERFNVPROC glCombinerParameterfNV; +PFNGLCOMBINERPARAMETERIVNVPROC glCombinerParameterivNV; +PFNGLCOMBINERPARAMETERINVPROC glCombinerParameteriNV; +PFNGLCOMBINERINPUTNVPROC glCombinerInputNV; +PFNGLCOMBINEROUTPUTNVPROC glCombinerOutputNV; +PFNGLFINALCOMBINERINPUTNVPROC glFinalCombinerInputNV; +PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC glGetCombinerInputParameterfvNV; +PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC glGetCombinerInputParameterivNV; +PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC glGetCombinerOutputParameterfvNV; +PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC glGetCombinerOutputParameterivNV; +PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC glGetFinalCombinerInputParameterfvNV; +PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC glGetFinalCombinerInputParameterivNV; +PFNGLCOMBINERSTAGEPARAMETERFVNVPROC glCombinerStageParameterfvNV; +PFNGLGETCOMBINERSTAGEPARAMETERFVNVPROC glGetCombinerStageParameterfvNV; +PFNGLFRAMEBUFFERSAMPLELOCATIONSFVNVPROC glFramebufferSampleLocationsfvNV; +PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVNVPROC glNamedFramebufferSampleLocationsfvNV; +PFNGLRESOLVEDEPTHVALUESNVPROC glResolveDepthValuesNV; +PFNGLSCISSOREXCLUSIVENVPROC glScissorExclusiveNV; +PFNGLSCISSOREXCLUSIVEARRAYVNVPROC glScissorExclusiveArrayvNV; +PFNGLMAKEBUFFERRESIDENTNVPROC glMakeBufferResidentNV; +PFNGLMAKEBUFFERNONRESIDENTNVPROC glMakeBufferNonResidentNV; +PFNGLISBUFFERRESIDENTNVPROC glIsBufferResidentNV; +PFNGLMAKENAMEDBUFFERRESIDENTNVPROC glMakeNamedBufferResidentNV; +PFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC glMakeNamedBufferNonResidentNV; +PFNGLISNAMEDBUFFERRESIDENTNVPROC glIsNamedBufferResidentNV; +PFNGLGETBUFFERPARAMETERUI64VNVPROC glGetBufferParameterui64vNV; +PFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC glGetNamedBufferParameterui64vNV; +PFNGLGETINTEGERUI64VNVPROC glGetIntegerui64vNV; +PFNGLUNIFORMUI64NVPROC glUniformui64NV; +PFNGLUNIFORMUI64VNVPROC glUniformui64vNV; +PFNGLPROGRAMUNIFORMUI64NVPROC glProgramUniformui64NV; +PFNGLPROGRAMUNIFORMUI64VNVPROC glProgramUniformui64vNV; +PFNGLBINDSHADINGRATEIMAGENVPROC glBindShadingRateImageNV; +PFNGLGETSHADINGRATEIMAGEPALETTENVPROC glGetShadingRateImagePaletteNV; +PFNGLGETSHADINGRATESAMPLELOCATIONIVNVPROC glGetShadingRateSampleLocationivNV; +PFNGLSHADINGRATEIMAGEBARRIERNVPROC glShadingRateImageBarrierNV; +PFNGLSHADINGRATEIMAGEPALETTENVPROC glShadingRateImagePaletteNV; +PFNGLSHADINGRATESAMPLEORDERNVPROC glShadingRateSampleOrderNV; +PFNGLSHADINGRATESAMPLEORDERCUSTOMNVPROC glShadingRateSampleOrderCustomNV; +PFNGLTEXTUREBARRIERNVPROC glTextureBarrierNV; +PFNGLTEXIMAGE2DMULTISAMPLECOVERAGENVPROC glTexImage2DMultisampleCoverageNV; +PFNGLTEXIMAGE3DMULTISAMPLECOVERAGENVPROC glTexImage3DMultisampleCoverageNV; +PFNGLTEXTUREIMAGE2DMULTISAMPLENVPROC glTextureImage2DMultisampleNV; +PFNGLTEXTUREIMAGE3DMULTISAMPLENVPROC glTextureImage3DMultisampleNV; +PFNGLTEXTUREIMAGE2DMULTISAMPLECOVERAGENVPROC glTextureImage2DMultisampleCoverageNV; +PFNGLTEXTUREIMAGE3DMULTISAMPLECOVERAGENVPROC glTextureImage3DMultisampleCoverageNV; +PFNGLBEGINTRANSFORMFEEDBACKNVPROC glBeginTransformFeedbackNV; +PFNGLENDTRANSFORMFEEDBACKNVPROC glEndTransformFeedbackNV; +PFNGLTRANSFORMFEEDBACKATTRIBSNVPROC glTransformFeedbackAttribsNV; +PFNGLBINDBUFFERRANGENVPROC glBindBufferRangeNV; +PFNGLBINDBUFFEROFFSETNVPROC glBindBufferOffsetNV; +PFNGLBINDBUFFERBASENVPROC glBindBufferBaseNV; +PFNGLTRANSFORMFEEDBACKVARYINGSNVPROC glTransformFeedbackVaryingsNV; +PFNGLACTIVEVARYINGNVPROC glActiveVaryingNV; +PFNGLGETVARYINGLOCATIONNVPROC glGetVaryingLocationNV; +PFNGLGETACTIVEVARYINGNVPROC glGetActiveVaryingNV; +PFNGLGETTRANSFORMFEEDBACKVARYINGNVPROC glGetTransformFeedbackVaryingNV; +PFNGLTRANSFORMFEEDBACKSTREAMATTRIBSNVPROC glTransformFeedbackStreamAttribsNV; +PFNGLBINDTRANSFORMFEEDBACKNVPROC glBindTransformFeedbackNV; +PFNGLDELETETRANSFORMFEEDBACKSNVPROC glDeleteTransformFeedbacksNV; +PFNGLGENTRANSFORMFEEDBACKSNVPROC glGenTransformFeedbacksNV; +PFNGLISTRANSFORMFEEDBACKNVPROC glIsTransformFeedbackNV; +PFNGLPAUSETRANSFORMFEEDBACKNVPROC glPauseTransformFeedbackNV; +PFNGLRESUMETRANSFORMFEEDBACKNVPROC glResumeTransformFeedbackNV; +PFNGLDRAWTRANSFORMFEEDBACKNVPROC glDrawTransformFeedbackNV; +PFNGLVDPAUINITNVPROC glVDPAUInitNV; +PFNGLVDPAUFININVPROC glVDPAUFiniNV; +PFNGLVDPAUREGISTERVIDEOSURFACENVPROC glVDPAURegisterVideoSurfaceNV; +PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC glVDPAURegisterOutputSurfaceNV; +PFNGLVDPAUISSURFACENVPROC glVDPAUIsSurfaceNV; +PFNGLVDPAUUNREGISTERSURFACENVPROC glVDPAUUnregisterSurfaceNV; +PFNGLVDPAUGETSURFACEIVNVPROC glVDPAUGetSurfaceivNV; +PFNGLVDPAUSURFACEACCESSNVPROC glVDPAUSurfaceAccessNV; +PFNGLVDPAUMAPSURFACESNVPROC glVDPAUMapSurfacesNV; +PFNGLVDPAUUNMAPSURFACESNVPROC glVDPAUUnmapSurfacesNV; +PFNGLVDPAUREGISTERVIDEOSURFACEWITHPICTURESTRUCTURENVPROC glVDPAURegisterVideoSurfaceWithPictureStructureNV; +PFNGLFLUSHVERTEXARRAYRANGENVPROC glFlushVertexArrayRangeNV; +PFNGLVERTEXARRAYRANGENVPROC glVertexArrayRangeNV; +PFNGLVERTEXATTRIBL1I64NVPROC glVertexAttribL1i64NV; +PFNGLVERTEXATTRIBL2I64NVPROC glVertexAttribL2i64NV; +PFNGLVERTEXATTRIBL3I64NVPROC glVertexAttribL3i64NV; +PFNGLVERTEXATTRIBL4I64NVPROC glVertexAttribL4i64NV; +PFNGLVERTEXATTRIBL1I64VNVPROC glVertexAttribL1i64vNV; +PFNGLVERTEXATTRIBL2I64VNVPROC glVertexAttribL2i64vNV; +PFNGLVERTEXATTRIBL3I64VNVPROC glVertexAttribL3i64vNV; +PFNGLVERTEXATTRIBL4I64VNVPROC glVertexAttribL4i64vNV; +PFNGLVERTEXATTRIBL1UI64NVPROC glVertexAttribL1ui64NV; +PFNGLVERTEXATTRIBL2UI64NVPROC glVertexAttribL2ui64NV; +PFNGLVERTEXATTRIBL3UI64NVPROC glVertexAttribL3ui64NV; +PFNGLVERTEXATTRIBL4UI64NVPROC glVertexAttribL4ui64NV; +PFNGLVERTEXATTRIBL1UI64VNVPROC glVertexAttribL1ui64vNV; +PFNGLVERTEXATTRIBL2UI64VNVPROC glVertexAttribL2ui64vNV; +PFNGLVERTEXATTRIBL3UI64VNVPROC glVertexAttribL3ui64vNV; +PFNGLVERTEXATTRIBL4UI64VNVPROC glVertexAttribL4ui64vNV; +PFNGLGETVERTEXATTRIBLI64VNVPROC glGetVertexAttribLi64vNV; +PFNGLGETVERTEXATTRIBLUI64VNVPROC glGetVertexAttribLui64vNV; +PFNGLVERTEXATTRIBLFORMATNVPROC glVertexAttribLFormatNV; +PFNGLBUFFERADDRESSRANGENVPROC glBufferAddressRangeNV; +PFNGLVERTEXFORMATNVPROC glVertexFormatNV; +PFNGLNORMALFORMATNVPROC glNormalFormatNV; +PFNGLCOLORFORMATNVPROC glColorFormatNV; +PFNGLINDEXFORMATNVPROC glIndexFormatNV; +PFNGLTEXCOORDFORMATNVPROC glTexCoordFormatNV; +PFNGLEDGEFLAGFORMATNVPROC glEdgeFlagFormatNV; +PFNGLSECONDARYCOLORFORMATNVPROC glSecondaryColorFormatNV; +PFNGLFOGCOORDFORMATNVPROC glFogCoordFormatNV; +PFNGLVERTEXATTRIBFORMATNVPROC glVertexAttribFormatNV; +PFNGLVERTEXATTRIBIFORMATNVPROC glVertexAttribIFormatNV; +PFNGLGETINTEGERUI64I_VNVPROC glGetIntegerui64i_vNV; +PFNGLAREPROGRAMSRESIDENTNVPROC glAreProgramsResidentNV; +PFNGLBINDPROGRAMNVPROC glBindProgramNV; +PFNGLDELETEPROGRAMSNVPROC glDeleteProgramsNV; +PFNGLEXECUTEPROGRAMNVPROC glExecuteProgramNV; +PFNGLGENPROGRAMSNVPROC glGenProgramsNV; +PFNGLGETPROGRAMPARAMETERDVNVPROC glGetProgramParameterdvNV; +PFNGLGETPROGRAMPARAMETERFVNVPROC glGetProgramParameterfvNV; +PFNGLGETPROGRAMIVNVPROC glGetProgramivNV; +PFNGLGETPROGRAMSTRINGNVPROC glGetProgramStringNV; +PFNGLGETTRACKMATRIXIVNVPROC glGetTrackMatrixivNV; +PFNGLGETVERTEXATTRIBDVNVPROC glGetVertexAttribdvNV; +PFNGLGETVERTEXATTRIBFVNVPROC glGetVertexAttribfvNV; +PFNGLGETVERTEXATTRIBIVNVPROC glGetVertexAttribivNV; +PFNGLGETVERTEXATTRIBPOINTERVNVPROC glGetVertexAttribPointervNV; +PFNGLISPROGRAMNVPROC glIsProgramNV; +PFNGLLOADPROGRAMNVPROC glLoadProgramNV; +PFNGLPROGRAMPARAMETER4DNVPROC glProgramParameter4dNV; +PFNGLPROGRAMPARAMETER4DVNVPROC glProgramParameter4dvNV; +PFNGLPROGRAMPARAMETER4FNVPROC glProgramParameter4fNV; +PFNGLPROGRAMPARAMETER4FVNVPROC glProgramParameter4fvNV; +PFNGLPROGRAMPARAMETERS4DVNVPROC glProgramParameters4dvNV; +PFNGLPROGRAMPARAMETERS4FVNVPROC glProgramParameters4fvNV; +PFNGLREQUESTRESIDENTPROGRAMSNVPROC glRequestResidentProgramsNV; +PFNGLTRACKMATRIXNVPROC glTrackMatrixNV; +PFNGLVERTEXATTRIBPOINTERNVPROC glVertexAttribPointerNV; +PFNGLVERTEXATTRIB1DNVPROC glVertexAttrib1dNV; +PFNGLVERTEXATTRIB1DVNVPROC glVertexAttrib1dvNV; +PFNGLVERTEXATTRIB1FNVPROC glVertexAttrib1fNV; +PFNGLVERTEXATTRIB1FVNVPROC glVertexAttrib1fvNV; +PFNGLVERTEXATTRIB1SNVPROC glVertexAttrib1sNV; +PFNGLVERTEXATTRIB1SVNVPROC glVertexAttrib1svNV; +PFNGLVERTEXATTRIB2DNVPROC glVertexAttrib2dNV; +PFNGLVERTEXATTRIB2DVNVPROC glVertexAttrib2dvNV; +PFNGLVERTEXATTRIB2FNVPROC glVertexAttrib2fNV; +PFNGLVERTEXATTRIB2FVNVPROC glVertexAttrib2fvNV; +PFNGLVERTEXATTRIB2SNVPROC glVertexAttrib2sNV; +PFNGLVERTEXATTRIB2SVNVPROC glVertexAttrib2svNV; +PFNGLVERTEXATTRIB3DNVPROC glVertexAttrib3dNV; +PFNGLVERTEXATTRIB3DVNVPROC glVertexAttrib3dvNV; +PFNGLVERTEXATTRIB3FNVPROC glVertexAttrib3fNV; +PFNGLVERTEXATTRIB3FVNVPROC glVertexAttrib3fvNV; +PFNGLVERTEXATTRIB3SNVPROC glVertexAttrib3sNV; +PFNGLVERTEXATTRIB3SVNVPROC glVertexAttrib3svNV; +PFNGLVERTEXATTRIB4DNVPROC glVertexAttrib4dNV; +PFNGLVERTEXATTRIB4DVNVPROC glVertexAttrib4dvNV; +PFNGLVERTEXATTRIB4FNVPROC glVertexAttrib4fNV; +PFNGLVERTEXATTRIB4FVNVPROC glVertexAttrib4fvNV; +PFNGLVERTEXATTRIB4SNVPROC glVertexAttrib4sNV; +PFNGLVERTEXATTRIB4SVNVPROC glVertexAttrib4svNV; +PFNGLVERTEXATTRIB4UBNVPROC glVertexAttrib4ubNV; +PFNGLVERTEXATTRIB4UBVNVPROC glVertexAttrib4ubvNV; +PFNGLVERTEXATTRIBS1DVNVPROC glVertexAttribs1dvNV; +PFNGLVERTEXATTRIBS1FVNVPROC glVertexAttribs1fvNV; +PFNGLVERTEXATTRIBS1SVNVPROC glVertexAttribs1svNV; +PFNGLVERTEXATTRIBS2DVNVPROC glVertexAttribs2dvNV; +PFNGLVERTEXATTRIBS2FVNVPROC glVertexAttribs2fvNV; +PFNGLVERTEXATTRIBS2SVNVPROC glVertexAttribs2svNV; +PFNGLVERTEXATTRIBS3DVNVPROC glVertexAttribs3dvNV; +PFNGLVERTEXATTRIBS3FVNVPROC glVertexAttribs3fvNV; +PFNGLVERTEXATTRIBS3SVNVPROC glVertexAttribs3svNV; +PFNGLVERTEXATTRIBS4DVNVPROC glVertexAttribs4dvNV; +PFNGLVERTEXATTRIBS4FVNVPROC glVertexAttribs4fvNV; +PFNGLVERTEXATTRIBS4SVNVPROC glVertexAttribs4svNV; +PFNGLVERTEXATTRIBS4UBVNVPROC glVertexAttribs4ubvNV; +PFNGLVERTEXATTRIBI1IEXTPROC glVertexAttribI1iEXT; +PFNGLVERTEXATTRIBI2IEXTPROC glVertexAttribI2iEXT; +PFNGLVERTEXATTRIBI3IEXTPROC glVertexAttribI3iEXT; +PFNGLVERTEXATTRIBI4IEXTPROC glVertexAttribI4iEXT; +PFNGLVERTEXATTRIBI1UIEXTPROC glVertexAttribI1uiEXT; +PFNGLVERTEXATTRIBI2UIEXTPROC glVertexAttribI2uiEXT; +PFNGLVERTEXATTRIBI3UIEXTPROC glVertexAttribI3uiEXT; +PFNGLVERTEXATTRIBI4UIEXTPROC glVertexAttribI4uiEXT; +PFNGLVERTEXATTRIBI1IVEXTPROC glVertexAttribI1ivEXT; +PFNGLVERTEXATTRIBI2IVEXTPROC glVertexAttribI2ivEXT; +PFNGLVERTEXATTRIBI3IVEXTPROC glVertexAttribI3ivEXT; +PFNGLVERTEXATTRIBI4IVEXTPROC glVertexAttribI4ivEXT; +PFNGLVERTEXATTRIBI1UIVEXTPROC glVertexAttribI1uivEXT; +PFNGLVERTEXATTRIBI2UIVEXTPROC glVertexAttribI2uivEXT; +PFNGLVERTEXATTRIBI3UIVEXTPROC glVertexAttribI3uivEXT; +PFNGLVERTEXATTRIBI4UIVEXTPROC glVertexAttribI4uivEXT; +PFNGLVERTEXATTRIBI4BVEXTPROC glVertexAttribI4bvEXT; +PFNGLVERTEXATTRIBI4SVEXTPROC glVertexAttribI4svEXT; +PFNGLVERTEXATTRIBI4UBVEXTPROC glVertexAttribI4ubvEXT; +PFNGLVERTEXATTRIBI4USVEXTPROC glVertexAttribI4usvEXT; +PFNGLVERTEXATTRIBIPOINTEREXTPROC glVertexAttribIPointerEXT; +PFNGLGETVERTEXATTRIBIIVEXTPROC glGetVertexAttribIivEXT; +PFNGLGETVERTEXATTRIBIUIVEXTPROC glGetVertexAttribIuivEXT; +PFNGLBEGINVIDEOCAPTURENVPROC glBeginVideoCaptureNV; +PFNGLBINDVIDEOCAPTURESTREAMBUFFERNVPROC glBindVideoCaptureStreamBufferNV; +PFNGLBINDVIDEOCAPTURESTREAMTEXTURENVPROC glBindVideoCaptureStreamTextureNV; +PFNGLENDVIDEOCAPTURENVPROC glEndVideoCaptureNV; +PFNGLGETVIDEOCAPTUREIVNVPROC glGetVideoCaptureivNV; +PFNGLGETVIDEOCAPTURESTREAMIVNVPROC glGetVideoCaptureStreamivNV; +PFNGLGETVIDEOCAPTURESTREAMFVNVPROC glGetVideoCaptureStreamfvNV; +PFNGLGETVIDEOCAPTURESTREAMDVNVPROC glGetVideoCaptureStreamdvNV; +PFNGLVIDEOCAPTURENVPROC glVideoCaptureNV; +PFNGLVIDEOCAPTURESTREAMPARAMETERIVNVPROC glVideoCaptureStreamParameterivNV; +PFNGLVIDEOCAPTURESTREAMPARAMETERFVNVPROC glVideoCaptureStreamParameterfvNV; +PFNGLVIDEOCAPTURESTREAMPARAMETERDVNVPROC glVideoCaptureStreamParameterdvNV; +PFNGLVIEWPORTSWIZZLENVPROC glViewportSwizzleNV; +PFNGLMULTITEXCOORD1BOESPROC glMultiTexCoord1bOES; +PFNGLMULTITEXCOORD1BVOESPROC glMultiTexCoord1bvOES; +PFNGLMULTITEXCOORD2BOESPROC glMultiTexCoord2bOES; +PFNGLMULTITEXCOORD2BVOESPROC glMultiTexCoord2bvOES; +PFNGLMULTITEXCOORD3BOESPROC glMultiTexCoord3bOES; +PFNGLMULTITEXCOORD3BVOESPROC glMultiTexCoord3bvOES; +PFNGLMULTITEXCOORD4BOESPROC glMultiTexCoord4bOES; +PFNGLMULTITEXCOORD4BVOESPROC glMultiTexCoord4bvOES; +PFNGLTEXCOORD1BOESPROC glTexCoord1bOES; +PFNGLTEXCOORD1BVOESPROC glTexCoord1bvOES; +PFNGLTEXCOORD2BOESPROC glTexCoord2bOES; +PFNGLTEXCOORD2BVOESPROC glTexCoord2bvOES; +PFNGLTEXCOORD3BOESPROC glTexCoord3bOES; +PFNGLTEXCOORD3BVOESPROC glTexCoord3bvOES; +PFNGLTEXCOORD4BOESPROC glTexCoord4bOES; +PFNGLTEXCOORD4BVOESPROC glTexCoord4bvOES; +PFNGLVERTEX2BOESPROC glVertex2bOES; +PFNGLVERTEX2BVOESPROC glVertex2bvOES; +PFNGLVERTEX3BOESPROC glVertex3bOES; +PFNGLVERTEX3BVOESPROC glVertex3bvOES; +PFNGLVERTEX4BOESPROC glVertex4bOES; +PFNGLVERTEX4BVOESPROC glVertex4bvOES; +PFNGLALPHAFUNCXOESPROC glAlphaFuncxOES; +PFNGLCLEARCOLORXOESPROC glClearColorxOES; +PFNGLCLEARDEPTHXOESPROC glClearDepthxOES; +PFNGLCLIPPLANEXOESPROC glClipPlanexOES; +PFNGLCOLOR4XOESPROC glColor4xOES; +PFNGLDEPTHRANGEXOESPROC glDepthRangexOES; +PFNGLFOGXOESPROC glFogxOES; +PFNGLFOGXVOESPROC glFogxvOES; +PFNGLFRUSTUMXOESPROC glFrustumxOES; +PFNGLGETCLIPPLANEXOESPROC glGetClipPlanexOES; +PFNGLGETFIXEDVOESPROC glGetFixedvOES; +PFNGLGETTEXENVXVOESPROC glGetTexEnvxvOES; +PFNGLGETTEXPARAMETERXVOESPROC glGetTexParameterxvOES; +PFNGLLIGHTMODELXOESPROC glLightModelxOES; +PFNGLLIGHTMODELXVOESPROC glLightModelxvOES; +PFNGLLIGHTXOESPROC glLightxOES; +PFNGLLIGHTXVOESPROC glLightxvOES; +PFNGLLINEWIDTHXOESPROC glLineWidthxOES; +PFNGLLOADMATRIXXOESPROC glLoadMatrixxOES; +PFNGLMATERIALXOESPROC glMaterialxOES; +PFNGLMATERIALXVOESPROC glMaterialxvOES; +PFNGLMULTMATRIXXOESPROC glMultMatrixxOES; +PFNGLMULTITEXCOORD4XOESPROC glMultiTexCoord4xOES; +PFNGLNORMAL3XOESPROC glNormal3xOES; +PFNGLORTHOXOESPROC glOrthoxOES; +PFNGLPOINTPARAMETERXVOESPROC glPointParameterxvOES; +PFNGLPOINTSIZEXOESPROC glPointSizexOES; +PFNGLPOLYGONOFFSETXOESPROC glPolygonOffsetxOES; +PFNGLROTATEXOESPROC glRotatexOES; +PFNGLSCALEXOESPROC glScalexOES; +PFNGLTEXENVXOESPROC glTexEnvxOES; +PFNGLTEXENVXVOESPROC glTexEnvxvOES; +PFNGLTEXPARAMETERXOESPROC glTexParameterxOES; +PFNGLTEXPARAMETERXVOESPROC glTexParameterxvOES; +PFNGLTRANSLATEXOESPROC glTranslatexOES; +PFNGLGETLIGHTXVOESPROC glGetLightxvOES; +PFNGLGETMATERIALXVOESPROC glGetMaterialxvOES; +PFNGLPOINTPARAMETERXOESPROC glPointParameterxOES; +PFNGLSAMPLECOVERAGEXOESPROC glSampleCoveragexOES; +PFNGLACCUMXOESPROC glAccumxOES; +PFNGLBITMAPXOESPROC glBitmapxOES; +PFNGLBLENDCOLORXOESPROC glBlendColorxOES; +PFNGLCLEARACCUMXOESPROC glClearAccumxOES; +PFNGLCOLOR3XOESPROC glColor3xOES; +PFNGLCOLOR3XVOESPROC glColor3xvOES; +PFNGLCOLOR4XVOESPROC glColor4xvOES; +PFNGLCONVOLUTIONPARAMETERXOESPROC glConvolutionParameterxOES; +PFNGLCONVOLUTIONPARAMETERXVOESPROC glConvolutionParameterxvOES; +PFNGLEVALCOORD1XOESPROC glEvalCoord1xOES; +PFNGLEVALCOORD1XVOESPROC glEvalCoord1xvOES; +PFNGLEVALCOORD2XOESPROC glEvalCoord2xOES; +PFNGLEVALCOORD2XVOESPROC glEvalCoord2xvOES; +PFNGLFEEDBACKBUFFERXOESPROC glFeedbackBufferxOES; +PFNGLGETCONVOLUTIONPARAMETERXVOESPROC glGetConvolutionParameterxvOES; +PFNGLGETHISTOGRAMPARAMETERXVOESPROC glGetHistogramParameterxvOES; +PFNGLGETLIGHTXOESPROC glGetLightxOES; +PFNGLGETMAPXVOESPROC glGetMapxvOES; +PFNGLGETMATERIALXOESPROC glGetMaterialxOES; +PFNGLGETPIXELMAPXVPROC glGetPixelMapxv; +PFNGLGETTEXGENXVOESPROC glGetTexGenxvOES; +PFNGLGETTEXLEVELPARAMETERXVOESPROC glGetTexLevelParameterxvOES; +PFNGLINDEXXOESPROC glIndexxOES; +PFNGLINDEXXVOESPROC glIndexxvOES; +PFNGLLOADTRANSPOSEMATRIXXOESPROC glLoadTransposeMatrixxOES; +PFNGLMAP1XOESPROC glMap1xOES; +PFNGLMAP2XOESPROC glMap2xOES; +PFNGLMAPGRID1XOESPROC glMapGrid1xOES; +PFNGLMAPGRID2XOESPROC glMapGrid2xOES; +PFNGLMULTTRANSPOSEMATRIXXOESPROC glMultTransposeMatrixxOES; +PFNGLMULTITEXCOORD1XOESPROC glMultiTexCoord1xOES; +PFNGLMULTITEXCOORD1XVOESPROC glMultiTexCoord1xvOES; +PFNGLMULTITEXCOORD2XOESPROC glMultiTexCoord2xOES; +PFNGLMULTITEXCOORD2XVOESPROC glMultiTexCoord2xvOES; +PFNGLMULTITEXCOORD3XOESPROC glMultiTexCoord3xOES; +PFNGLMULTITEXCOORD3XVOESPROC glMultiTexCoord3xvOES; +PFNGLMULTITEXCOORD4XVOESPROC glMultiTexCoord4xvOES; +PFNGLNORMAL3XVOESPROC glNormal3xvOES; +PFNGLPASSTHROUGHXOESPROC glPassThroughxOES; +PFNGLPIXELMAPXPROC glPixelMapx; +PFNGLPIXELSTOREXPROC glPixelStorex; +PFNGLPIXELTRANSFERXOESPROC glPixelTransferxOES; +PFNGLPIXELZOOMXOESPROC glPixelZoomxOES; +PFNGLPRIORITIZETEXTURESXOESPROC glPrioritizeTexturesxOES; +PFNGLRASTERPOS2XOESPROC glRasterPos2xOES; +PFNGLRASTERPOS2XVOESPROC glRasterPos2xvOES; +PFNGLRASTERPOS3XOESPROC glRasterPos3xOES; +PFNGLRASTERPOS3XVOESPROC glRasterPos3xvOES; +PFNGLRASTERPOS4XOESPROC glRasterPos4xOES; +PFNGLRASTERPOS4XVOESPROC glRasterPos4xvOES; +PFNGLRECTXOESPROC glRectxOES; +PFNGLRECTXVOESPROC glRectxvOES; +PFNGLTEXCOORD1XOESPROC glTexCoord1xOES; +PFNGLTEXCOORD1XVOESPROC glTexCoord1xvOES; +PFNGLTEXCOORD2XOESPROC glTexCoord2xOES; +PFNGLTEXCOORD2XVOESPROC glTexCoord2xvOES; +PFNGLTEXCOORD3XOESPROC glTexCoord3xOES; +PFNGLTEXCOORD3XVOESPROC glTexCoord3xvOES; +PFNGLTEXCOORD4XOESPROC glTexCoord4xOES; +PFNGLTEXCOORD4XVOESPROC glTexCoord4xvOES; +PFNGLTEXGENXOESPROC glTexGenxOES; +PFNGLTEXGENXVOESPROC glTexGenxvOES; +PFNGLVERTEX2XOESPROC glVertex2xOES; +PFNGLVERTEX2XVOESPROC glVertex2xvOES; +PFNGLVERTEX3XOESPROC glVertex3xOES; +PFNGLVERTEX3XVOESPROC glVertex3xvOES; +PFNGLVERTEX4XOESPROC glVertex4xOES; +PFNGLVERTEX4XVOESPROC glVertex4xvOES; +PFNGLQUERYMATRIXXOESPROC glQueryMatrixxOES; +PFNGLCLEARDEPTHFOESPROC glClearDepthfOES; +PFNGLCLIPPLANEFOESPROC glClipPlanefOES; +PFNGLDEPTHRANGEFOESPROC glDepthRangefOES; +PFNGLFRUSTUMFOESPROC glFrustumfOES; +PFNGLGETCLIPPLANEFOESPROC glGetClipPlanefOES; +PFNGLORTHOFOESPROC glOrthofOES; +PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR; +PFNGLHINTPGIPROC glHintPGI; +PFNGLDETAILTEXFUNCSGISPROC glDetailTexFuncSGIS; +PFNGLGETDETAILTEXFUNCSGISPROC glGetDetailTexFuncSGIS; +PFNGLFOGFUNCSGISPROC glFogFuncSGIS; +PFNGLGETFOGFUNCSGISPROC glGetFogFuncSGIS; +PFNGLSAMPLEMASKSGISPROC glSampleMaskSGIS; +PFNGLSAMPLEPATTERNSGISPROC glSamplePatternSGIS; +PFNGLPIXELTEXGENPARAMETERISGISPROC glPixelTexGenParameteriSGIS; +PFNGLPIXELTEXGENPARAMETERIVSGISPROC glPixelTexGenParameterivSGIS; +PFNGLPIXELTEXGENPARAMETERFSGISPROC glPixelTexGenParameterfSGIS; +PFNGLPIXELTEXGENPARAMETERFVSGISPROC glPixelTexGenParameterfvSGIS; +PFNGLGETPIXELTEXGENPARAMETERIVSGISPROC glGetPixelTexGenParameterivSGIS; +PFNGLGETPIXELTEXGENPARAMETERFVSGISPROC glGetPixelTexGenParameterfvSGIS; +PFNGLPOINTPARAMETERFSGISPROC glPointParameterfSGIS; +PFNGLPOINTPARAMETERFVSGISPROC glPointParameterfvSGIS; +PFNGLSHARPENTEXFUNCSGISPROC glSharpenTexFuncSGIS; +PFNGLGETSHARPENTEXFUNCSGISPROC glGetSharpenTexFuncSGIS; +PFNGLTEXIMAGE4DSGISPROC glTexImage4DSGIS; +PFNGLTEXSUBIMAGE4DSGISPROC glTexSubImage4DSGIS; +PFNGLTEXTURECOLORMASKSGISPROC glTextureColorMaskSGIS; +PFNGLGETTEXFILTERFUNCSGISPROC glGetTexFilterFuncSGIS; +PFNGLTEXFILTERFUNCSGISPROC glTexFilterFuncSGIS; +PFNGLASYNCMARKERSGIXPROC glAsyncMarkerSGIX; +PFNGLFINISHASYNCSGIXPROC glFinishAsyncSGIX; +PFNGLPOLLASYNCSGIXPROC glPollAsyncSGIX; +PFNGLGENASYNCMARKERSSGIXPROC glGenAsyncMarkersSGIX; +PFNGLDELETEASYNCMARKERSSGIXPROC glDeleteAsyncMarkersSGIX; +PFNGLISASYNCMARKERSGIXPROC glIsAsyncMarkerSGIX; +PFNGLFLUSHRASTERSGIXPROC glFlushRasterSGIX; +PFNGLFRAGMENTCOLORMATERIALSGIXPROC glFragmentColorMaterialSGIX; +PFNGLFRAGMENTLIGHTFSGIXPROC glFragmentLightfSGIX; +PFNGLFRAGMENTLIGHTFVSGIXPROC glFragmentLightfvSGIX; +PFNGLFRAGMENTLIGHTISGIXPROC glFragmentLightiSGIX; +PFNGLFRAGMENTLIGHTIVSGIXPROC glFragmentLightivSGIX; +PFNGLFRAGMENTLIGHTMODELFSGIXPROC glFragmentLightModelfSGIX; +PFNGLFRAGMENTLIGHTMODELFVSGIXPROC glFragmentLightModelfvSGIX; +PFNGLFRAGMENTLIGHTMODELISGIXPROC glFragmentLightModeliSGIX; +PFNGLFRAGMENTLIGHTMODELIVSGIXPROC glFragmentLightModelivSGIX; +PFNGLFRAGMENTMATERIALFSGIXPROC glFragmentMaterialfSGIX; +PFNGLFRAGMENTMATERIALFVSGIXPROC glFragmentMaterialfvSGIX; +PFNGLFRAGMENTMATERIALISGIXPROC glFragmentMaterialiSGIX; +PFNGLFRAGMENTMATERIALIVSGIXPROC glFragmentMaterialivSGIX; +PFNGLGETFRAGMENTLIGHTFVSGIXPROC glGetFragmentLightfvSGIX; +PFNGLGETFRAGMENTLIGHTIVSGIXPROC glGetFragmentLightivSGIX; +PFNGLGETFRAGMENTMATERIALFVSGIXPROC glGetFragmentMaterialfvSGIX; +PFNGLGETFRAGMENTMATERIALIVSGIXPROC glGetFragmentMaterialivSGIX; +PFNGLLIGHTENVISGIXPROC glLightEnviSGIX; +PFNGLFRAMEZOOMSGIXPROC glFrameZoomSGIX; +PFNGLIGLOOINTERFACESGIXPROC glIglooInterfaceSGIX; +PFNGLGETINSTRUMENTSSGIXPROC glGetInstrumentsSGIX; +PFNGLINSTRUMENTSBUFFERSGIXPROC glInstrumentsBufferSGIX; +PFNGLPOLLINSTRUMENTSSGIXPROC glPollInstrumentsSGIX; +PFNGLREADINSTRUMENTSSGIXPROC glReadInstrumentsSGIX; +PFNGLSTARTINSTRUMENTSSGIXPROC glStartInstrumentsSGIX; +PFNGLSTOPINSTRUMENTSSGIXPROC glStopInstrumentsSGIX; +PFNGLGETLISTPARAMETERFVSGIXPROC glGetListParameterfvSGIX; +PFNGLGETLISTPARAMETERIVSGIXPROC glGetListParameterivSGIX; +PFNGLLISTPARAMETERFSGIXPROC glListParameterfSGIX; +PFNGLLISTPARAMETERFVSGIXPROC glListParameterfvSGIX; +PFNGLLISTPARAMETERISGIXPROC glListParameteriSGIX; +PFNGLLISTPARAMETERIVSGIXPROC glListParameterivSGIX; +PFNGLPIXELTEXGENSGIXPROC glPixelTexGenSGIX; +PFNGLDEFORMATIONMAP3DSGIXPROC glDeformationMap3dSGIX; +PFNGLDEFORMATIONMAP3FSGIXPROC glDeformationMap3fSGIX; +PFNGLDEFORMSGIXPROC glDeformSGIX; +PFNGLLOADIDENTITYDEFORMATIONMAPSGIXPROC glLoadIdentityDeformationMapSGIX; +PFNGLREFERENCEPLANESGIXPROC glReferencePlaneSGIX; +PFNGLSPRITEPARAMETERFSGIXPROC glSpriteParameterfSGIX; +PFNGLSPRITEPARAMETERFVSGIXPROC glSpriteParameterfvSGIX; +PFNGLSPRITEPARAMETERISGIXPROC glSpriteParameteriSGIX; +PFNGLSPRITEPARAMETERIVSGIXPROC glSpriteParameterivSGIX; +PFNGLTAGSAMPLEBUFFERSGIXPROC glTagSampleBufferSGIX; +PFNGLCOLORTABLESGIPROC glColorTableSGI; +PFNGLCOLORTABLEPARAMETERFVSGIPROC glColorTableParameterfvSGI; +PFNGLCOLORTABLEPARAMETERIVSGIPROC glColorTableParameterivSGI; +PFNGLCOPYCOLORTABLESGIPROC glCopyColorTableSGI; +PFNGLGETCOLORTABLESGIPROC glGetColorTableSGI; +PFNGLGETCOLORTABLEPARAMETERFVSGIPROC glGetColorTableParameterfvSGI; +PFNGLGETCOLORTABLEPARAMETERIVSGIPROC glGetColorTableParameterivSGI; +PFNGLFINISHTEXTURESUNXPROC glFinishTextureSUNX; +PFNGLGLOBALALPHAFACTORBSUNPROC glGlobalAlphaFactorbSUN; +PFNGLGLOBALALPHAFACTORSSUNPROC glGlobalAlphaFactorsSUN; +PFNGLGLOBALALPHAFACTORISUNPROC glGlobalAlphaFactoriSUN; +PFNGLGLOBALALPHAFACTORFSUNPROC glGlobalAlphaFactorfSUN; +PFNGLGLOBALALPHAFACTORDSUNPROC glGlobalAlphaFactordSUN; +PFNGLGLOBALALPHAFACTORUBSUNPROC glGlobalAlphaFactorubSUN; +PFNGLGLOBALALPHAFACTORUSSUNPROC glGlobalAlphaFactorusSUN; +PFNGLGLOBALALPHAFACTORUISUNPROC glGlobalAlphaFactoruiSUN; +PFNGLDRAWMESHARRAYSSUNPROC glDrawMeshArraysSUN; +PFNGLREPLACEMENTCODEUISUNPROC glReplacementCodeuiSUN; +PFNGLREPLACEMENTCODEUSSUNPROC glReplacementCodeusSUN; +PFNGLREPLACEMENTCODEUBSUNPROC glReplacementCodeubSUN; +PFNGLREPLACEMENTCODEUIVSUNPROC glReplacementCodeuivSUN; +PFNGLREPLACEMENTCODEUSVSUNPROC glReplacementCodeusvSUN; +PFNGLREPLACEMENTCODEUBVSUNPROC glReplacementCodeubvSUN; +PFNGLREPLACEMENTCODEPOINTERSUNPROC glReplacementCodePointerSUN; +PFNGLCOLOR4UBVERTEX2FSUNPROC glColor4ubVertex2fSUN; +PFNGLCOLOR4UBVERTEX2FVSUNPROC glColor4ubVertex2fvSUN; +PFNGLCOLOR4UBVERTEX3FSUNPROC glColor4ubVertex3fSUN; +PFNGLCOLOR4UBVERTEX3FVSUNPROC glColor4ubVertex3fvSUN; +PFNGLCOLOR3FVERTEX3FSUNPROC glColor3fVertex3fSUN; +PFNGLCOLOR3FVERTEX3FVSUNPROC glColor3fVertex3fvSUN; +PFNGLNORMAL3FVERTEX3FSUNPROC glNormal3fVertex3fSUN; +PFNGLNORMAL3FVERTEX3FVSUNPROC glNormal3fVertex3fvSUN; +PFNGLCOLOR4FNORMAL3FVERTEX3FSUNPROC glColor4fNormal3fVertex3fSUN; +PFNGLCOLOR4FNORMAL3FVERTEX3FVSUNPROC glColor4fNormal3fVertex3fvSUN; +PFNGLTEXCOORD2FVERTEX3FSUNPROC glTexCoord2fVertex3fSUN; +PFNGLTEXCOORD2FVERTEX3FVSUNPROC glTexCoord2fVertex3fvSUN; +PFNGLTEXCOORD4FVERTEX4FSUNPROC glTexCoord4fVertex4fSUN; +PFNGLTEXCOORD4FVERTEX4FVSUNPROC glTexCoord4fVertex4fvSUN; +PFNGLTEXCOORD2FCOLOR4UBVERTEX3FSUNPROC glTexCoord2fColor4ubVertex3fSUN; +PFNGLTEXCOORD2FCOLOR4UBVERTEX3FVSUNPROC glTexCoord2fColor4ubVertex3fvSUN; +PFNGLTEXCOORD2FCOLOR3FVERTEX3FSUNPROC glTexCoord2fColor3fVertex3fSUN; +PFNGLTEXCOORD2FCOLOR3FVERTEX3FVSUNPROC glTexCoord2fColor3fVertex3fvSUN; +PFNGLTEXCOORD2FNORMAL3FVERTEX3FSUNPROC glTexCoord2fNormal3fVertex3fSUN; +PFNGLTEXCOORD2FNORMAL3FVERTEX3FVSUNPROC glTexCoord2fNormal3fVertex3fvSUN; +PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC glTexCoord2fColor4fNormal3fVertex3fSUN; +PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC glTexCoord2fColor4fNormal3fVertex3fvSUN; +PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FSUNPROC glTexCoord4fColor4fNormal3fVertex4fSUN; +PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FVSUNPROC glTexCoord4fColor4fNormal3fVertex4fvSUN; +PFNGLREPLACEMENTCODEUIVERTEX3FSUNPROC glReplacementCodeuiVertex3fSUN; +PFNGLREPLACEMENTCODEUIVERTEX3FVSUNPROC glReplacementCodeuiVertex3fvSUN; +PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FSUNPROC glReplacementCodeuiColor4ubVertex3fSUN; +PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FVSUNPROC glReplacementCodeuiColor4ubVertex3fvSUN; +PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FSUNPROC glReplacementCodeuiColor3fVertex3fSUN; +PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FVSUNPROC glReplacementCodeuiColor3fVertex3fvSUN; +PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FSUNPROC glReplacementCodeuiNormal3fVertex3fSUN; +PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FVSUNPROC glReplacementCodeuiNormal3fVertex3fvSUN; +PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FSUNPROC glReplacementCodeuiColor4fNormal3fVertex3fSUN; +PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FVSUNPROC glReplacementCodeuiColor4fNormal3fVertex3fvSUN; +PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FSUNPROC glReplacementCodeuiTexCoord2fVertex3fSUN; +PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FVSUNPROC glReplacementCodeuiTexCoord2fVertex3fvSUN; +PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FSUNPROC glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN; +PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FVSUNPROC glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN; +PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN; +PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN; +#if defined(GLBIND_WGL) +PFNWGLSETSTEREOEMITTERSTATE3DLPROC wglSetStereoEmitterState3DL; +PFNWGLGETGPUIDSAMDPROC wglGetGPUIDsAMD; +PFNWGLGETGPUINFOAMDPROC wglGetGPUInfoAMD; +PFNWGLGETCONTEXTGPUIDAMDPROC wglGetContextGPUIDAMD; +PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC wglCreateAssociatedContextAMD; +PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC wglCreateAssociatedContextAttribsAMD; +PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC wglDeleteAssociatedContextAMD; +PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC wglMakeAssociatedContextCurrentAMD; +PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC wglGetCurrentAssociatedContextAMD; +PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC wglBlitContextFramebufferAMD; +PFNWGLCREATEBUFFERREGIONARBPROC wglCreateBufferRegionARB; +PFNWGLDELETEBUFFERREGIONARBPROC wglDeleteBufferRegionARB; +PFNWGLSAVEBUFFERREGIONARBPROC wglSaveBufferRegionARB; +PFNWGLRESTOREBUFFERREGIONARBPROC wglRestoreBufferRegionARB; +PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; +PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB; +PFNWGLMAKECONTEXTCURRENTARBPROC wglMakeContextCurrentARB; +PFNWGLGETCURRENTREADDCARBPROC wglGetCurrentReadDCARB; +PFNWGLCREATEPBUFFERARBPROC wglCreatePbufferARB; +PFNWGLGETPBUFFERDCARBPROC wglGetPbufferDCARB; +PFNWGLRELEASEPBUFFERDCARBPROC wglReleasePbufferDCARB; +PFNWGLDESTROYPBUFFERARBPROC wglDestroyPbufferARB; +PFNWGLQUERYPBUFFERARBPROC wglQueryPbufferARB; +PFNWGLGETPIXELFORMATATTRIBIVARBPROC wglGetPixelFormatAttribivARB; +PFNWGLGETPIXELFORMATATTRIBFVARBPROC wglGetPixelFormatAttribfvARB; +PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; +PFNWGLBINDTEXIMAGEARBPROC wglBindTexImageARB; +PFNWGLRELEASETEXIMAGEARBPROC wglReleaseTexImageARB; +PFNWGLSETPBUFFERATTRIBARBPROC wglSetPbufferAttribARB; +PFNWGLCREATEDISPLAYCOLORTABLEEXTPROC wglCreateDisplayColorTableEXT; +PFNWGLLOADDISPLAYCOLORTABLEEXTPROC wglLoadDisplayColorTableEXT; +PFNWGLBINDDISPLAYCOLORTABLEEXTPROC wglBindDisplayColorTableEXT; +PFNWGLDESTROYDISPLAYCOLORTABLEEXTPROC wglDestroyDisplayColorTableEXT; +PFNWGLGETEXTENSIONSSTRINGEXTPROC wglGetExtensionsStringEXT; +PFNWGLMAKECONTEXTCURRENTEXTPROC wglMakeContextCurrentEXT; +PFNWGLGETCURRENTREADDCEXTPROC wglGetCurrentReadDCEXT; +PFNWGLCREATEPBUFFEREXTPROC wglCreatePbufferEXT; +PFNWGLGETPBUFFERDCEXTPROC wglGetPbufferDCEXT; +PFNWGLRELEASEPBUFFERDCEXTPROC wglReleasePbufferDCEXT; +PFNWGLDESTROYPBUFFEREXTPROC wglDestroyPbufferEXT; +PFNWGLQUERYPBUFFEREXTPROC wglQueryPbufferEXT; +PFNWGLGETPIXELFORMATATTRIBIVEXTPROC wglGetPixelFormatAttribivEXT; +PFNWGLGETPIXELFORMATATTRIBFVEXTPROC wglGetPixelFormatAttribfvEXT; +PFNWGLCHOOSEPIXELFORMATEXTPROC wglChoosePixelFormatEXT; +PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT; +PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT; +PFNWGLGETDIGITALVIDEOPARAMETERSI3DPROC wglGetDigitalVideoParametersI3D; +PFNWGLSETDIGITALVIDEOPARAMETERSI3DPROC wglSetDigitalVideoParametersI3D; +PFNWGLGETGAMMATABLEPARAMETERSI3DPROC wglGetGammaTableParametersI3D; +PFNWGLSETGAMMATABLEPARAMETERSI3DPROC wglSetGammaTableParametersI3D; +PFNWGLGETGAMMATABLEI3DPROC wglGetGammaTableI3D; +PFNWGLSETGAMMATABLEI3DPROC wglSetGammaTableI3D; +PFNWGLENABLEGENLOCKI3DPROC wglEnableGenlockI3D; +PFNWGLDISABLEGENLOCKI3DPROC wglDisableGenlockI3D; +PFNWGLISENABLEDGENLOCKI3DPROC wglIsEnabledGenlockI3D; +PFNWGLGENLOCKSOURCEI3DPROC wglGenlockSourceI3D; +PFNWGLGETGENLOCKSOURCEI3DPROC wglGetGenlockSourceI3D; +PFNWGLGENLOCKSOURCEEDGEI3DPROC wglGenlockSourceEdgeI3D; +PFNWGLGETGENLOCKSOURCEEDGEI3DPROC wglGetGenlockSourceEdgeI3D; +PFNWGLGENLOCKSAMPLERATEI3DPROC wglGenlockSampleRateI3D; +PFNWGLGETGENLOCKSAMPLERATEI3DPROC wglGetGenlockSampleRateI3D; +PFNWGLGENLOCKSOURCEDELAYI3DPROC wglGenlockSourceDelayI3D; +PFNWGLGETGENLOCKSOURCEDELAYI3DPROC wglGetGenlockSourceDelayI3D; +PFNWGLQUERYGENLOCKMAXSOURCEDELAYI3DPROC wglQueryGenlockMaxSourceDelayI3D; +PFNWGLCREATEIMAGEBUFFERI3DPROC wglCreateImageBufferI3D; +PFNWGLDESTROYIMAGEBUFFERI3DPROC wglDestroyImageBufferI3D; +PFNWGLASSOCIATEIMAGEBUFFEREVENTSI3DPROC wglAssociateImageBufferEventsI3D; +PFNWGLRELEASEIMAGEBUFFEREVENTSI3DPROC wglReleaseImageBufferEventsI3D; +PFNWGLENABLEFRAMELOCKI3DPROC wglEnableFrameLockI3D; +PFNWGLDISABLEFRAMELOCKI3DPROC wglDisableFrameLockI3D; +PFNWGLISENABLEDFRAMELOCKI3DPROC wglIsEnabledFrameLockI3D; +PFNWGLQUERYFRAMELOCKMASTERI3DPROC wglQueryFrameLockMasterI3D; +PFNWGLGETFRAMEUSAGEI3DPROC wglGetFrameUsageI3D; +PFNWGLBEGINFRAMETRACKINGI3DPROC wglBeginFrameTrackingI3D; +PFNWGLENDFRAMETRACKINGI3DPROC wglEndFrameTrackingI3D; +PFNWGLQUERYFRAMETRACKINGI3DPROC wglQueryFrameTrackingI3D; +PFNWGLCOPYIMAGESUBDATANVPROC wglCopyImageSubDataNV; +PFNWGLDELAYBEFORESWAPNVPROC wglDelayBeforeSwapNV; +PFNWGLDXSETRESOURCESHAREHANDLENVPROC wglDXSetResourceShareHandleNV; +PFNWGLDXOPENDEVICENVPROC wglDXOpenDeviceNV; +PFNWGLDXCLOSEDEVICENVPROC wglDXCloseDeviceNV; +PFNWGLDXREGISTEROBJECTNVPROC wglDXRegisterObjectNV; +PFNWGLDXUNREGISTEROBJECTNVPROC wglDXUnregisterObjectNV; +PFNWGLDXOBJECTACCESSNVPROC wglDXObjectAccessNV; +PFNWGLDXLOCKOBJECTSNVPROC wglDXLockObjectsNV; +PFNWGLDXUNLOCKOBJECTSNVPROC wglDXUnlockObjectsNV; +PFNWGLENUMGPUSNVPROC wglEnumGpusNV; +PFNWGLENUMGPUDEVICESNVPROC wglEnumGpuDevicesNV; +PFNWGLCREATEAFFINITYDCNVPROC wglCreateAffinityDCNV; +PFNWGLENUMGPUSFROMAFFINITYDCNVPROC wglEnumGpusFromAffinityDCNV; +PFNWGLDELETEDCNVPROC wglDeleteDCNV; +PFNWGLENUMERATEVIDEODEVICESNVPROC wglEnumerateVideoDevicesNV; +PFNWGLBINDVIDEODEVICENVPROC wglBindVideoDeviceNV; +PFNWGLQUERYCURRENTCONTEXTNVPROC wglQueryCurrentContextNV; +PFNWGLJOINSWAPGROUPNVPROC wglJoinSwapGroupNV; +PFNWGLBINDSWAPBARRIERNVPROC wglBindSwapBarrierNV; +PFNWGLQUERYSWAPGROUPNVPROC wglQuerySwapGroupNV; +PFNWGLQUERYMAXSWAPGROUPSNVPROC wglQueryMaxSwapGroupsNV; +PFNWGLQUERYFRAMECOUNTNVPROC wglQueryFrameCountNV; +PFNWGLRESETFRAMECOUNTNVPROC wglResetFrameCountNV; +PFNWGLBINDVIDEOCAPTUREDEVICENVPROC wglBindVideoCaptureDeviceNV; +PFNWGLENUMERATEVIDEOCAPTUREDEVICESNVPROC wglEnumerateVideoCaptureDevicesNV; +PFNWGLLOCKVIDEOCAPTUREDEVICENVPROC wglLockVideoCaptureDeviceNV; +PFNWGLQUERYVIDEOCAPTUREDEVICENVPROC wglQueryVideoCaptureDeviceNV; +PFNWGLRELEASEVIDEOCAPTUREDEVICENVPROC wglReleaseVideoCaptureDeviceNV; +PFNWGLGETVIDEODEVICENVPROC wglGetVideoDeviceNV; +PFNWGLRELEASEVIDEODEVICENVPROC wglReleaseVideoDeviceNV; +PFNWGLBINDVIDEOIMAGENVPROC wglBindVideoImageNV; +PFNWGLRELEASEVIDEOIMAGENVPROC wglReleaseVideoImageNV; +PFNWGLSENDPBUFFERTOVIDEONVPROC wglSendPbufferToVideoNV; +PFNWGLGETVIDEOINFONVPROC wglGetVideoInfoNV; +PFNWGLALLOCATEMEMORYNVPROC wglAllocateMemoryNV; +PFNWGLFREEMEMORYNVPROC wglFreeMemoryNV; +PFNWGLGETSYNCVALUESOMLPROC wglGetSyncValuesOML; +PFNWGLGETMSCRATEOMLPROC wglGetMscRateOML; +PFNWGLSWAPBUFFERSMSCOMLPROC wglSwapBuffersMscOML; +PFNWGLSWAPLAYERBUFFERSMSCOMLPROC wglSwapLayerBuffersMscOML; +PFNWGLWAITFORMSCOMLPROC wglWaitForMscOML; +PFNWGLWAITFORSBCOMLPROC wglWaitForSbcOML; +#endif /* GLBIND_WGL */ +#if defined(GLBIND_GLX) +PFNGLXGETGPUIDSAMDPROC glXGetGPUIDsAMD; +PFNGLXGETGPUINFOAMDPROC glXGetGPUInfoAMD; +PFNGLXGETCONTEXTGPUIDAMDPROC glXGetContextGPUIDAMD; +PFNGLXCREATEASSOCIATEDCONTEXTAMDPROC glXCreateAssociatedContextAMD; +PFNGLXCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC glXCreateAssociatedContextAttribsAMD; +PFNGLXDELETEASSOCIATEDCONTEXTAMDPROC glXDeleteAssociatedContextAMD; +PFNGLXMAKEASSOCIATEDCONTEXTCURRENTAMDPROC glXMakeAssociatedContextCurrentAMD; +PFNGLXGETCURRENTASSOCIATEDCONTEXTAMDPROC glXGetCurrentAssociatedContextAMD; +PFNGLXBLITCONTEXTFRAMEBUFFERAMDPROC glXBlitContextFramebufferAMD; +PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB; +PFNGLXGETPROCADDRESSARBPROC glXGetProcAddressARB; +PFNGLXGETCURRENTDISPLAYEXTPROC glXGetCurrentDisplayEXT; +PFNGLXQUERYCONTEXTINFOEXTPROC glXQueryContextInfoEXT; +PFNGLXGETCONTEXTIDEXTPROC glXGetContextIDEXT; +PFNGLXIMPORTCONTEXTEXTPROC glXImportContextEXT; +PFNGLXFREECONTEXTEXTPROC glXFreeContextEXT; +PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT; +PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT; +PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT; +PFNGLXGETAGPOFFSETMESAPROC glXGetAGPOffsetMESA; +PFNGLXCOPYSUBBUFFERMESAPROC glXCopySubBufferMESA; +PFNGLXCREATEGLXPIXMAPMESAPROC glXCreateGLXPixmapMESA; +PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC glXQueryCurrentRendererIntegerMESA; +PFNGLXQUERYCURRENTRENDERERSTRINGMESAPROC glXQueryCurrentRendererStringMESA; +PFNGLXQUERYRENDERERINTEGERMESAPROC glXQueryRendererIntegerMESA; +PFNGLXQUERYRENDERERSTRINGMESAPROC glXQueryRendererStringMESA; +PFNGLXRELEASEBUFFERSMESAPROC glXReleaseBuffersMESA; +PFNGLXSET3DFXMODEMESAPROC glXSet3DfxModeMESA; +PFNGLXGETSWAPINTERVALMESAPROC glXGetSwapIntervalMESA; +PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA; +PFNGLXCOPYBUFFERSUBDATANVPROC glXCopyBufferSubDataNV; +PFNGLXNAMEDCOPYBUFFERSUBDATANVPROC glXNamedCopyBufferSubDataNV; +PFNGLXCOPYIMAGESUBDATANVPROC glXCopyImageSubDataNV; +PFNGLXDELAYBEFORESWAPNVPROC glXDelayBeforeSwapNV; +PFNGLXENUMERATEVIDEODEVICESNVPROC glXEnumerateVideoDevicesNV; +PFNGLXBINDVIDEODEVICENVPROC glXBindVideoDeviceNV; +PFNGLXJOINSWAPGROUPNVPROC glXJoinSwapGroupNV; +PFNGLXBINDSWAPBARRIERNVPROC glXBindSwapBarrierNV; +PFNGLXQUERYSWAPGROUPNVPROC glXQuerySwapGroupNV; +PFNGLXQUERYMAXSWAPGROUPSNVPROC glXQueryMaxSwapGroupsNV; +PFNGLXQUERYFRAMECOUNTNVPROC glXQueryFrameCountNV; +PFNGLXRESETFRAMECOUNTNVPROC glXResetFrameCountNV; +PFNGLXBINDVIDEOCAPTUREDEVICENVPROC glXBindVideoCaptureDeviceNV; +PFNGLXENUMERATEVIDEOCAPTUREDEVICESNVPROC glXEnumerateVideoCaptureDevicesNV; +PFNGLXLOCKVIDEOCAPTUREDEVICENVPROC glXLockVideoCaptureDeviceNV; +PFNGLXQUERYVIDEOCAPTUREDEVICENVPROC glXQueryVideoCaptureDeviceNV; +PFNGLXRELEASEVIDEOCAPTUREDEVICENVPROC glXReleaseVideoCaptureDeviceNV; +PFNGLXGETVIDEODEVICENVPROC glXGetVideoDeviceNV; +PFNGLXRELEASEVIDEODEVICENVPROC glXReleaseVideoDeviceNV; +PFNGLXBINDVIDEOIMAGENVPROC glXBindVideoImageNV; +PFNGLXRELEASEVIDEOIMAGENVPROC glXReleaseVideoImageNV; +PFNGLXSENDPBUFFERTOVIDEONVPROC glXSendPbufferToVideoNV; +PFNGLXGETVIDEOINFONVPROC glXGetVideoInfoNV; +PFNGLXGETSYNCVALUESOMLPROC glXGetSyncValuesOML; +PFNGLXGETMSCRATEOMLPROC glXGetMscRateOML; +PFNGLXSWAPBUFFERSMSCOMLPROC glXSwapBuffersMscOML; +PFNGLXWAITFORMSCOMLPROC glXWaitForMscOML; +PFNGLXWAITFORSBCOMLPROC glXWaitForSbcOML; +PFNGLXCUSHIONSGIPROC glXCushionSGI; +PFNGLXMAKECURRENTREADSGIPROC glXMakeCurrentReadSGI; +PFNGLXGETCURRENTREADDRAWABLESGIPROC glXGetCurrentReadDrawableSGI; +PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI; +PFNGLXGETVIDEOSYNCSGIPROC glXGetVideoSyncSGI; +PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI; +PFNGLXGETFBCONFIGATTRIBSGIXPROC glXGetFBConfigAttribSGIX; +PFNGLXCHOOSEFBCONFIGSGIXPROC glXChooseFBConfigSGIX; +PFNGLXCREATEGLXPIXMAPWITHCONFIGSGIXPROC glXCreateGLXPixmapWithConfigSGIX; +PFNGLXCREATECONTEXTWITHCONFIGSGIXPROC glXCreateContextWithConfigSGIX; +PFNGLXGETVISUALFROMFBCONFIGSGIXPROC glXGetVisualFromFBConfigSGIX; +PFNGLXGETFBCONFIGFROMVISUALSGIXPROC glXGetFBConfigFromVisualSGIX; +PFNGLXQUERYHYPERPIPENETWORKSGIXPROC glXQueryHyperpipeNetworkSGIX; +PFNGLXHYPERPIPECONFIGSGIXPROC glXHyperpipeConfigSGIX; +PFNGLXQUERYHYPERPIPECONFIGSGIXPROC glXQueryHyperpipeConfigSGIX; +PFNGLXDESTROYHYPERPIPECONFIGSGIXPROC glXDestroyHyperpipeConfigSGIX; +PFNGLXBINDHYPERPIPESGIXPROC glXBindHyperpipeSGIX; +PFNGLXQUERYHYPERPIPEBESTATTRIBSGIXPROC glXQueryHyperpipeBestAttribSGIX; +PFNGLXHYPERPIPEATTRIBSGIXPROC glXHyperpipeAttribSGIX; +PFNGLXQUERYHYPERPIPEATTRIBSGIXPROC glXQueryHyperpipeAttribSGIX; +PFNGLXCREATEGLXPBUFFERSGIXPROC glXCreateGLXPbufferSGIX; +PFNGLXDESTROYGLXPBUFFERSGIXPROC glXDestroyGLXPbufferSGIX; +PFNGLXQUERYGLXPBUFFERSGIXPROC glXQueryGLXPbufferSGIX; +PFNGLXSELECTEVENTSGIXPROC glXSelectEventSGIX; +PFNGLXGETSELECTEDEVENTSGIXPROC glXGetSelectedEventSGIX; +PFNGLXBINDSWAPBARRIERSGIXPROC glXBindSwapBarrierSGIX; +PFNGLXQUERYMAXSWAPBARRIERSSGIXPROC glXQueryMaxSwapBarriersSGIX; +PFNGLXJOINSWAPGROUPSGIXPROC glXJoinSwapGroupSGIX; +PFNGLXBINDCHANNELTOWINDOWSGIXPROC glXBindChannelToWindowSGIX; +PFNGLXCHANNELRECTSGIXPROC glXChannelRectSGIX; +PFNGLXQUERYCHANNELRECTSGIXPROC glXQueryChannelRectSGIX; +PFNGLXQUERYCHANNELDELTASSGIXPROC glXQueryChannelDeltasSGIX; +PFNGLXCHANNELRECTSYNCSGIXPROC glXChannelRectSyncSGIX; +PFNGLXGETTRANSPARENTINDEXSUNPROC glXGetTransparentIndexSUN; +#endif /* GLBIND_GLX */ + +typedef struct +{ + PFNGLCULLFACEPROC glCullFace; + PFNGLFRONTFACEPROC glFrontFace; + PFNGLHINTPROC glHint; + PFNGLLINEWIDTHPROC glLineWidth; + PFNGLPOINTSIZEPROC glPointSize; + PFNGLPOLYGONMODEPROC glPolygonMode; + PFNGLSCISSORPROC glScissor; + PFNGLTEXPARAMETERFPROC glTexParameterf; + PFNGLTEXPARAMETERFVPROC glTexParameterfv; + PFNGLTEXPARAMETERIPROC glTexParameteri; + PFNGLTEXPARAMETERIVPROC glTexParameteriv; + PFNGLTEXIMAGE1DPROC glTexImage1D; + PFNGLTEXIMAGE2DPROC glTexImage2D; + PFNGLDRAWBUFFERPROC glDrawBuffer; + PFNGLCLEARPROC glClear; + PFNGLCLEARCOLORPROC glClearColor; + PFNGLCLEARSTENCILPROC glClearStencil; + PFNGLCLEARDEPTHPROC glClearDepth; + PFNGLSTENCILMASKPROC glStencilMask; + PFNGLCOLORMASKPROC glColorMask; + PFNGLDEPTHMASKPROC glDepthMask; + PFNGLDISABLEPROC glDisable; + PFNGLENABLEPROC glEnable; + PFNGLFINISHPROC glFinish; + PFNGLFLUSHPROC glFlush; + PFNGLBLENDFUNCPROC glBlendFunc; + PFNGLLOGICOPPROC glLogicOp; + PFNGLSTENCILFUNCPROC glStencilFunc; + PFNGLSTENCILOPPROC glStencilOp; + PFNGLDEPTHFUNCPROC glDepthFunc; + PFNGLPIXELSTOREFPROC glPixelStoref; + PFNGLPIXELSTOREIPROC glPixelStorei; + PFNGLREADBUFFERPROC glReadBuffer; + PFNGLREADPIXELSPROC glReadPixels; + PFNGLGETBOOLEANVPROC glGetBooleanv; + PFNGLGETDOUBLEVPROC glGetDoublev; + PFNGLGETERRORPROC glGetError; + PFNGLGETFLOATVPROC glGetFloatv; + PFNGLGETINTEGERVPROC glGetIntegerv; + PFNGLGETSTRINGPROC glGetString; + PFNGLGETTEXIMAGEPROC glGetTexImage; + PFNGLGETTEXPARAMETERFVPROC glGetTexParameterfv; + PFNGLGETTEXPARAMETERIVPROC glGetTexParameteriv; + PFNGLGETTEXLEVELPARAMETERFVPROC glGetTexLevelParameterfv; + PFNGLGETTEXLEVELPARAMETERIVPROC glGetTexLevelParameteriv; + PFNGLISENABLEDPROC glIsEnabled; + PFNGLDEPTHRANGEPROC glDepthRange; + PFNGLVIEWPORTPROC glViewport; + PFNGLNEWLISTPROC glNewList; + PFNGLENDLISTPROC glEndList; + PFNGLCALLLISTPROC glCallList; + PFNGLCALLLISTSPROC glCallLists; + PFNGLDELETELISTSPROC glDeleteLists; + PFNGLGENLISTSPROC glGenLists; + PFNGLLISTBASEPROC glListBase; + PFNGLBEGINPROC glBegin; + PFNGLBITMAPPROC glBitmap; + PFNGLCOLOR3BPROC glColor3b; + PFNGLCOLOR3BVPROC glColor3bv; + PFNGLCOLOR3DPROC glColor3d; + PFNGLCOLOR3DVPROC glColor3dv; + PFNGLCOLOR3FPROC glColor3f; + PFNGLCOLOR3FVPROC glColor3fv; + PFNGLCOLOR3IPROC glColor3i; + PFNGLCOLOR3IVPROC glColor3iv; + PFNGLCOLOR3SPROC glColor3s; + PFNGLCOLOR3SVPROC glColor3sv; + PFNGLCOLOR3UBPROC glColor3ub; + PFNGLCOLOR3UBVPROC glColor3ubv; + PFNGLCOLOR3UIPROC glColor3ui; + PFNGLCOLOR3UIVPROC glColor3uiv; + PFNGLCOLOR3USPROC glColor3us; + PFNGLCOLOR3USVPROC glColor3usv; + PFNGLCOLOR4BPROC glColor4b; + PFNGLCOLOR4BVPROC glColor4bv; + PFNGLCOLOR4DPROC glColor4d; + PFNGLCOLOR4DVPROC glColor4dv; + PFNGLCOLOR4FPROC glColor4f; + PFNGLCOLOR4FVPROC glColor4fv; + PFNGLCOLOR4IPROC glColor4i; + PFNGLCOLOR4IVPROC glColor4iv; + PFNGLCOLOR4SPROC glColor4s; + PFNGLCOLOR4SVPROC glColor4sv; + PFNGLCOLOR4UBPROC glColor4ub; + PFNGLCOLOR4UBVPROC glColor4ubv; + PFNGLCOLOR4UIPROC glColor4ui; + PFNGLCOLOR4UIVPROC glColor4uiv; + PFNGLCOLOR4USPROC glColor4us; + PFNGLCOLOR4USVPROC glColor4usv; + PFNGLEDGEFLAGPROC glEdgeFlag; + PFNGLEDGEFLAGVPROC glEdgeFlagv; + PFNGLENDPROC glEnd; + PFNGLINDEXDPROC glIndexd; + PFNGLINDEXDVPROC glIndexdv; + PFNGLINDEXFPROC glIndexf; + PFNGLINDEXFVPROC glIndexfv; + PFNGLINDEXIPROC glIndexi; + PFNGLINDEXIVPROC glIndexiv; + PFNGLINDEXSPROC glIndexs; + PFNGLINDEXSVPROC glIndexsv; + PFNGLNORMAL3BPROC glNormal3b; + PFNGLNORMAL3BVPROC glNormal3bv; + PFNGLNORMAL3DPROC glNormal3d; + PFNGLNORMAL3DVPROC glNormal3dv; + PFNGLNORMAL3FPROC glNormal3f; + PFNGLNORMAL3FVPROC glNormal3fv; + PFNGLNORMAL3IPROC glNormal3i; + PFNGLNORMAL3IVPROC glNormal3iv; + PFNGLNORMAL3SPROC glNormal3s; + PFNGLNORMAL3SVPROC glNormal3sv; + PFNGLRASTERPOS2DPROC glRasterPos2d; + PFNGLRASTERPOS2DVPROC glRasterPos2dv; + PFNGLRASTERPOS2FPROC glRasterPos2f; + PFNGLRASTERPOS2FVPROC glRasterPos2fv; + PFNGLRASTERPOS2IPROC glRasterPos2i; + PFNGLRASTERPOS2IVPROC glRasterPos2iv; + PFNGLRASTERPOS2SPROC glRasterPos2s; + PFNGLRASTERPOS2SVPROC glRasterPos2sv; + PFNGLRASTERPOS3DPROC glRasterPos3d; + PFNGLRASTERPOS3DVPROC glRasterPos3dv; + PFNGLRASTERPOS3FPROC glRasterPos3f; + PFNGLRASTERPOS3FVPROC glRasterPos3fv; + PFNGLRASTERPOS3IPROC glRasterPos3i; + PFNGLRASTERPOS3IVPROC glRasterPos3iv; + PFNGLRASTERPOS3SPROC glRasterPos3s; + PFNGLRASTERPOS3SVPROC glRasterPos3sv; + PFNGLRASTERPOS4DPROC glRasterPos4d; + PFNGLRASTERPOS4DVPROC glRasterPos4dv; + PFNGLRASTERPOS4FPROC glRasterPos4f; + PFNGLRASTERPOS4FVPROC glRasterPos4fv; + PFNGLRASTERPOS4IPROC glRasterPos4i; + PFNGLRASTERPOS4IVPROC glRasterPos4iv; + PFNGLRASTERPOS4SPROC glRasterPos4s; + PFNGLRASTERPOS4SVPROC glRasterPos4sv; + PFNGLRECTDPROC glRectd; + PFNGLRECTDVPROC glRectdv; + PFNGLRECTFPROC glRectf; + PFNGLRECTFVPROC glRectfv; + PFNGLRECTIPROC glRecti; + PFNGLRECTIVPROC glRectiv; + PFNGLRECTSPROC glRects; + PFNGLRECTSVPROC glRectsv; + PFNGLTEXCOORD1DPROC glTexCoord1d; + PFNGLTEXCOORD1DVPROC glTexCoord1dv; + PFNGLTEXCOORD1FPROC glTexCoord1f; + PFNGLTEXCOORD1FVPROC glTexCoord1fv; + PFNGLTEXCOORD1IPROC glTexCoord1i; + PFNGLTEXCOORD1IVPROC glTexCoord1iv; + PFNGLTEXCOORD1SPROC glTexCoord1s; + PFNGLTEXCOORD1SVPROC glTexCoord1sv; + PFNGLTEXCOORD2DPROC glTexCoord2d; + PFNGLTEXCOORD2DVPROC glTexCoord2dv; + PFNGLTEXCOORD2FPROC glTexCoord2f; + PFNGLTEXCOORD2FVPROC glTexCoord2fv; + PFNGLTEXCOORD2IPROC glTexCoord2i; + PFNGLTEXCOORD2IVPROC glTexCoord2iv; + PFNGLTEXCOORD2SPROC glTexCoord2s; + PFNGLTEXCOORD2SVPROC glTexCoord2sv; + PFNGLTEXCOORD3DPROC glTexCoord3d; + PFNGLTEXCOORD3DVPROC glTexCoord3dv; + PFNGLTEXCOORD3FPROC glTexCoord3f; + PFNGLTEXCOORD3FVPROC glTexCoord3fv; + PFNGLTEXCOORD3IPROC glTexCoord3i; + PFNGLTEXCOORD3IVPROC glTexCoord3iv; + PFNGLTEXCOORD3SPROC glTexCoord3s; + PFNGLTEXCOORD3SVPROC glTexCoord3sv; + PFNGLTEXCOORD4DPROC glTexCoord4d; + PFNGLTEXCOORD4DVPROC glTexCoord4dv; + PFNGLTEXCOORD4FPROC glTexCoord4f; + PFNGLTEXCOORD4FVPROC glTexCoord4fv; + PFNGLTEXCOORD4IPROC glTexCoord4i; + PFNGLTEXCOORD4IVPROC glTexCoord4iv; + PFNGLTEXCOORD4SPROC glTexCoord4s; + PFNGLTEXCOORD4SVPROC glTexCoord4sv; + PFNGLVERTEX2DPROC glVertex2d; + PFNGLVERTEX2DVPROC glVertex2dv; + PFNGLVERTEX2FPROC glVertex2f; + PFNGLVERTEX2FVPROC glVertex2fv; + PFNGLVERTEX2IPROC glVertex2i; + PFNGLVERTEX2IVPROC glVertex2iv; + PFNGLVERTEX2SPROC glVertex2s; + PFNGLVERTEX2SVPROC glVertex2sv; + PFNGLVERTEX3DPROC glVertex3d; + PFNGLVERTEX3DVPROC glVertex3dv; + PFNGLVERTEX3FPROC glVertex3f; + PFNGLVERTEX3FVPROC glVertex3fv; + PFNGLVERTEX3IPROC glVertex3i; + PFNGLVERTEX3IVPROC glVertex3iv; + PFNGLVERTEX3SPROC glVertex3s; + PFNGLVERTEX3SVPROC glVertex3sv; + PFNGLVERTEX4DPROC glVertex4d; + PFNGLVERTEX4DVPROC glVertex4dv; + PFNGLVERTEX4FPROC glVertex4f; + PFNGLVERTEX4FVPROC glVertex4fv; + PFNGLVERTEX4IPROC glVertex4i; + PFNGLVERTEX4IVPROC glVertex4iv; + PFNGLVERTEX4SPROC glVertex4s; + PFNGLVERTEX4SVPROC glVertex4sv; + PFNGLCLIPPLANEPROC glClipPlane; + PFNGLCOLORMATERIALPROC glColorMaterial; + PFNGLFOGFPROC glFogf; + PFNGLFOGFVPROC glFogfv; + PFNGLFOGIPROC glFogi; + PFNGLFOGIVPROC glFogiv; + PFNGLLIGHTFPROC glLightf; + PFNGLLIGHTFVPROC glLightfv; + PFNGLLIGHTIPROC glLighti; + PFNGLLIGHTIVPROC glLightiv; + PFNGLLIGHTMODELFPROC glLightModelf; + PFNGLLIGHTMODELFVPROC glLightModelfv; + PFNGLLIGHTMODELIPROC glLightModeli; + PFNGLLIGHTMODELIVPROC glLightModeliv; + PFNGLLINESTIPPLEPROC glLineStipple; + PFNGLMATERIALFPROC glMaterialf; + PFNGLMATERIALFVPROC glMaterialfv; + PFNGLMATERIALIPROC glMateriali; + PFNGLMATERIALIVPROC glMaterialiv; + PFNGLPOLYGONSTIPPLEPROC glPolygonStipple; + PFNGLSHADEMODELPROC glShadeModel; + PFNGLTEXENVFPROC glTexEnvf; + PFNGLTEXENVFVPROC glTexEnvfv; + PFNGLTEXENVIPROC glTexEnvi; + PFNGLTEXENVIVPROC glTexEnviv; + PFNGLTEXGENDPROC glTexGend; + PFNGLTEXGENDVPROC glTexGendv; + PFNGLTEXGENFPROC glTexGenf; + PFNGLTEXGENFVPROC glTexGenfv; + PFNGLTEXGENIPROC glTexGeni; + PFNGLTEXGENIVPROC glTexGeniv; + PFNGLFEEDBACKBUFFERPROC glFeedbackBuffer; + PFNGLSELECTBUFFERPROC glSelectBuffer; + PFNGLRENDERMODEPROC glRenderMode; + PFNGLINITNAMESPROC glInitNames; + PFNGLLOADNAMEPROC glLoadName; + PFNGLPASSTHROUGHPROC glPassThrough; + PFNGLPOPNAMEPROC glPopName; + PFNGLPUSHNAMEPROC glPushName; + PFNGLCLEARACCUMPROC glClearAccum; + PFNGLCLEARINDEXPROC glClearIndex; + PFNGLINDEXMASKPROC glIndexMask; + PFNGLACCUMPROC glAccum; + PFNGLPOPATTRIBPROC glPopAttrib; + PFNGLPUSHATTRIBPROC glPushAttrib; + PFNGLMAP1DPROC glMap1d; + PFNGLMAP1FPROC glMap1f; + PFNGLMAP2DPROC glMap2d; + PFNGLMAP2FPROC glMap2f; + PFNGLMAPGRID1DPROC glMapGrid1d; + PFNGLMAPGRID1FPROC glMapGrid1f; + PFNGLMAPGRID2DPROC glMapGrid2d; + PFNGLMAPGRID2FPROC glMapGrid2f; + PFNGLEVALCOORD1DPROC glEvalCoord1d; + PFNGLEVALCOORD1DVPROC glEvalCoord1dv; + PFNGLEVALCOORD1FPROC glEvalCoord1f; + PFNGLEVALCOORD1FVPROC glEvalCoord1fv; + PFNGLEVALCOORD2DPROC glEvalCoord2d; + PFNGLEVALCOORD2DVPROC glEvalCoord2dv; + PFNGLEVALCOORD2FPROC glEvalCoord2f; + PFNGLEVALCOORD2FVPROC glEvalCoord2fv; + PFNGLEVALMESH1PROC glEvalMesh1; + PFNGLEVALPOINT1PROC glEvalPoint1; + PFNGLEVALMESH2PROC glEvalMesh2; + PFNGLEVALPOINT2PROC glEvalPoint2; + PFNGLALPHAFUNCPROC glAlphaFunc; + PFNGLPIXELZOOMPROC glPixelZoom; + PFNGLPIXELTRANSFERFPROC glPixelTransferf; + PFNGLPIXELTRANSFERIPROC glPixelTransferi; + PFNGLPIXELMAPFVPROC glPixelMapfv; + PFNGLPIXELMAPUIVPROC glPixelMapuiv; + PFNGLPIXELMAPUSVPROC glPixelMapusv; + PFNGLCOPYPIXELSPROC glCopyPixels; + PFNGLDRAWPIXELSPROC glDrawPixels; + PFNGLGETCLIPPLANEPROC glGetClipPlane; + PFNGLGETLIGHTFVPROC glGetLightfv; + PFNGLGETLIGHTIVPROC glGetLightiv; + PFNGLGETMAPDVPROC glGetMapdv; + PFNGLGETMAPFVPROC glGetMapfv; + PFNGLGETMAPIVPROC glGetMapiv; + PFNGLGETMATERIALFVPROC glGetMaterialfv; + PFNGLGETMATERIALIVPROC glGetMaterialiv; + PFNGLGETPIXELMAPFVPROC glGetPixelMapfv; + PFNGLGETPIXELMAPUIVPROC glGetPixelMapuiv; + PFNGLGETPIXELMAPUSVPROC glGetPixelMapusv; + PFNGLGETPOLYGONSTIPPLEPROC glGetPolygonStipple; + PFNGLGETTEXENVFVPROC glGetTexEnvfv; + PFNGLGETTEXENVIVPROC glGetTexEnviv; + PFNGLGETTEXGENDVPROC glGetTexGendv; + PFNGLGETTEXGENFVPROC glGetTexGenfv; + PFNGLGETTEXGENIVPROC glGetTexGeniv; + PFNGLISLISTPROC glIsList; + PFNGLFRUSTUMPROC glFrustum; + PFNGLLOADIDENTITYPROC glLoadIdentity; + PFNGLLOADMATRIXFPROC glLoadMatrixf; + PFNGLLOADMATRIXDPROC glLoadMatrixd; + PFNGLMATRIXMODEPROC glMatrixMode; + PFNGLMULTMATRIXFPROC glMultMatrixf; + PFNGLMULTMATRIXDPROC glMultMatrixd; + PFNGLORTHOPROC glOrtho; + PFNGLPOPMATRIXPROC glPopMatrix; + PFNGLPUSHMATRIXPROC glPushMatrix; + PFNGLROTATEDPROC glRotated; + PFNGLROTATEFPROC glRotatef; + PFNGLSCALEDPROC glScaled; + PFNGLSCALEFPROC glScalef; + PFNGLTRANSLATEDPROC glTranslated; + PFNGLTRANSLATEFPROC glTranslatef; + PFNGLDRAWARRAYSPROC glDrawArrays; + PFNGLDRAWELEMENTSPROC glDrawElements; + PFNGLGETPOINTERVPROC glGetPointerv; + PFNGLPOLYGONOFFSETPROC glPolygonOffset; + PFNGLCOPYTEXIMAGE1DPROC glCopyTexImage1D; + PFNGLCOPYTEXIMAGE2DPROC glCopyTexImage2D; + PFNGLCOPYTEXSUBIMAGE1DPROC glCopyTexSubImage1D; + PFNGLCOPYTEXSUBIMAGE2DPROC glCopyTexSubImage2D; + PFNGLTEXSUBIMAGE1DPROC glTexSubImage1D; + PFNGLTEXSUBIMAGE2DPROC glTexSubImage2D; + PFNGLBINDTEXTUREPROC glBindTexture; + PFNGLDELETETEXTURESPROC glDeleteTextures; + PFNGLGENTEXTURESPROC glGenTextures; + PFNGLISTEXTUREPROC glIsTexture; + PFNGLARRAYELEMENTPROC glArrayElement; + PFNGLCOLORPOINTERPROC glColorPointer; + PFNGLDISABLECLIENTSTATEPROC glDisableClientState; + PFNGLEDGEFLAGPOINTERPROC glEdgeFlagPointer; + PFNGLENABLECLIENTSTATEPROC glEnableClientState; + PFNGLINDEXPOINTERPROC glIndexPointer; + PFNGLINTERLEAVEDARRAYSPROC glInterleavedArrays; + PFNGLNORMALPOINTERPROC glNormalPointer; + PFNGLTEXCOORDPOINTERPROC glTexCoordPointer; + PFNGLVERTEXPOINTERPROC glVertexPointer; + PFNGLARETEXTURESRESIDENTPROC glAreTexturesResident; + PFNGLPRIORITIZETEXTURESPROC glPrioritizeTextures; + PFNGLINDEXUBPROC glIndexub; + PFNGLINDEXUBVPROC glIndexubv; + PFNGLPOPCLIENTATTRIBPROC glPopClientAttrib; + PFNGLPUSHCLIENTATTRIBPROC glPushClientAttrib; + PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements; + PFNGLTEXIMAGE3DPROC glTexImage3D; + PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D; + PFNGLCOPYTEXSUBIMAGE3DPROC glCopyTexSubImage3D; + PFNGLACTIVETEXTUREPROC glActiveTexture; + PFNGLSAMPLECOVERAGEPROC glSampleCoverage; + PFNGLCOMPRESSEDTEXIMAGE3DPROC glCompressedTexImage3D; + PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D; + PFNGLCOMPRESSEDTEXIMAGE1DPROC glCompressedTexImage1D; + PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glCompressedTexSubImage3D; + PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glCompressedTexSubImage2D; + PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glCompressedTexSubImage1D; + PFNGLGETCOMPRESSEDTEXIMAGEPROC glGetCompressedTexImage; + PFNGLCLIENTACTIVETEXTUREPROC glClientActiveTexture; + PFNGLMULTITEXCOORD1DPROC glMultiTexCoord1d; + PFNGLMULTITEXCOORD1DVPROC glMultiTexCoord1dv; + PFNGLMULTITEXCOORD1FPROC glMultiTexCoord1f; + PFNGLMULTITEXCOORD1FVPROC glMultiTexCoord1fv; + PFNGLMULTITEXCOORD1IPROC glMultiTexCoord1i; + PFNGLMULTITEXCOORD1IVPROC glMultiTexCoord1iv; + PFNGLMULTITEXCOORD1SPROC glMultiTexCoord1s; + PFNGLMULTITEXCOORD1SVPROC glMultiTexCoord1sv; + PFNGLMULTITEXCOORD2DPROC glMultiTexCoord2d; + PFNGLMULTITEXCOORD2DVPROC glMultiTexCoord2dv; + PFNGLMULTITEXCOORD2FPROC glMultiTexCoord2f; + PFNGLMULTITEXCOORD2FVPROC glMultiTexCoord2fv; + PFNGLMULTITEXCOORD2IPROC glMultiTexCoord2i; + PFNGLMULTITEXCOORD2IVPROC glMultiTexCoord2iv; + PFNGLMULTITEXCOORD2SPROC glMultiTexCoord2s; + PFNGLMULTITEXCOORD2SVPROC glMultiTexCoord2sv; + PFNGLMULTITEXCOORD3DPROC glMultiTexCoord3d; + PFNGLMULTITEXCOORD3DVPROC glMultiTexCoord3dv; + PFNGLMULTITEXCOORD3FPROC glMultiTexCoord3f; + PFNGLMULTITEXCOORD3FVPROC glMultiTexCoord3fv; + PFNGLMULTITEXCOORD3IPROC glMultiTexCoord3i; + PFNGLMULTITEXCOORD3IVPROC glMultiTexCoord3iv; + PFNGLMULTITEXCOORD3SPROC glMultiTexCoord3s; + PFNGLMULTITEXCOORD3SVPROC glMultiTexCoord3sv; + PFNGLMULTITEXCOORD4DPROC glMultiTexCoord4d; + PFNGLMULTITEXCOORD4DVPROC glMultiTexCoord4dv; + PFNGLMULTITEXCOORD4FPROC glMultiTexCoord4f; + PFNGLMULTITEXCOORD4FVPROC glMultiTexCoord4fv; + PFNGLMULTITEXCOORD4IPROC glMultiTexCoord4i; + PFNGLMULTITEXCOORD4IVPROC glMultiTexCoord4iv; + PFNGLMULTITEXCOORD4SPROC glMultiTexCoord4s; + PFNGLMULTITEXCOORD4SVPROC glMultiTexCoord4sv; + PFNGLLOADTRANSPOSEMATRIXFPROC glLoadTransposeMatrixf; + PFNGLLOADTRANSPOSEMATRIXDPROC glLoadTransposeMatrixd; + PFNGLMULTTRANSPOSEMATRIXFPROC glMultTransposeMatrixf; + PFNGLMULTTRANSPOSEMATRIXDPROC glMultTransposeMatrixd; + PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate; + PFNGLMULTIDRAWARRAYSPROC glMultiDrawArrays; + PFNGLMULTIDRAWELEMENTSPROC glMultiDrawElements; + PFNGLPOINTPARAMETERFPROC glPointParameterf; + PFNGLPOINTPARAMETERFVPROC glPointParameterfv; + PFNGLPOINTPARAMETERIPROC glPointParameteri; + PFNGLPOINTPARAMETERIVPROC glPointParameteriv; + PFNGLFOGCOORDFPROC glFogCoordf; + PFNGLFOGCOORDFVPROC glFogCoordfv; + PFNGLFOGCOORDDPROC glFogCoordd; + PFNGLFOGCOORDDVPROC glFogCoorddv; + PFNGLFOGCOORDPOINTERPROC glFogCoordPointer; + PFNGLSECONDARYCOLOR3BPROC glSecondaryColor3b; + PFNGLSECONDARYCOLOR3BVPROC glSecondaryColor3bv; + PFNGLSECONDARYCOLOR3DPROC glSecondaryColor3d; + PFNGLSECONDARYCOLOR3DVPROC glSecondaryColor3dv; + PFNGLSECONDARYCOLOR3FPROC glSecondaryColor3f; + PFNGLSECONDARYCOLOR3FVPROC glSecondaryColor3fv; + PFNGLSECONDARYCOLOR3IPROC glSecondaryColor3i; + PFNGLSECONDARYCOLOR3IVPROC glSecondaryColor3iv; + PFNGLSECONDARYCOLOR3SPROC glSecondaryColor3s; + PFNGLSECONDARYCOLOR3SVPROC glSecondaryColor3sv; + PFNGLSECONDARYCOLOR3UBPROC glSecondaryColor3ub; + PFNGLSECONDARYCOLOR3UBVPROC glSecondaryColor3ubv; + PFNGLSECONDARYCOLOR3UIPROC glSecondaryColor3ui; + PFNGLSECONDARYCOLOR3UIVPROC glSecondaryColor3uiv; + PFNGLSECONDARYCOLOR3USPROC glSecondaryColor3us; + PFNGLSECONDARYCOLOR3USVPROC glSecondaryColor3usv; + PFNGLSECONDARYCOLORPOINTERPROC glSecondaryColorPointer; + PFNGLWINDOWPOS2DPROC glWindowPos2d; + PFNGLWINDOWPOS2DVPROC glWindowPos2dv; + PFNGLWINDOWPOS2FPROC glWindowPos2f; + PFNGLWINDOWPOS2FVPROC glWindowPos2fv; + PFNGLWINDOWPOS2IPROC glWindowPos2i; + PFNGLWINDOWPOS2IVPROC glWindowPos2iv; + PFNGLWINDOWPOS2SPROC glWindowPos2s; + PFNGLWINDOWPOS2SVPROC glWindowPos2sv; + PFNGLWINDOWPOS3DPROC glWindowPos3d; + PFNGLWINDOWPOS3DVPROC glWindowPos3dv; + PFNGLWINDOWPOS3FPROC glWindowPos3f; + PFNGLWINDOWPOS3FVPROC glWindowPos3fv; + PFNGLWINDOWPOS3IPROC glWindowPos3i; + PFNGLWINDOWPOS3IVPROC glWindowPos3iv; + PFNGLWINDOWPOS3SPROC glWindowPos3s; + PFNGLWINDOWPOS3SVPROC glWindowPos3sv; + PFNGLBLENDCOLORPROC glBlendColor; + PFNGLBLENDEQUATIONPROC glBlendEquation; + PFNGLGENQUERIESPROC glGenQueries; + PFNGLDELETEQUERIESPROC glDeleteQueries; + PFNGLISQUERYPROC glIsQuery; + PFNGLBEGINQUERYPROC glBeginQuery; + PFNGLENDQUERYPROC glEndQuery; + PFNGLGETQUERYIVPROC glGetQueryiv; + PFNGLGETQUERYOBJECTIVPROC glGetQueryObjectiv; + PFNGLGETQUERYOBJECTUIVPROC glGetQueryObjectuiv; + PFNGLBINDBUFFERPROC glBindBuffer; + PFNGLDELETEBUFFERSPROC glDeleteBuffers; + PFNGLGENBUFFERSPROC glGenBuffers; + PFNGLISBUFFERPROC glIsBuffer; + PFNGLBUFFERDATAPROC glBufferData; + PFNGLBUFFERSUBDATAPROC glBufferSubData; + PFNGLGETBUFFERSUBDATAPROC glGetBufferSubData; + PFNGLMAPBUFFERPROC glMapBuffer; + PFNGLUNMAPBUFFERPROC glUnmapBuffer; + PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv; + PFNGLGETBUFFERPOINTERVPROC glGetBufferPointerv; + PFNGLBLENDEQUATIONSEPARATEPROC glBlendEquationSeparate; + PFNGLDRAWBUFFERSPROC glDrawBuffers; + PFNGLSTENCILOPSEPARATEPROC glStencilOpSeparate; + PFNGLSTENCILFUNCSEPARATEPROC glStencilFuncSeparate; + PFNGLSTENCILMASKSEPARATEPROC glStencilMaskSeparate; + PFNGLATTACHSHADERPROC glAttachShader; + PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation; + PFNGLCOMPILESHADERPROC glCompileShader; + PFNGLCREATEPROGRAMPROC glCreateProgram; + PFNGLCREATESHADERPROC glCreateShader; + PFNGLDELETEPROGRAMPROC glDeleteProgram; + PFNGLDELETESHADERPROC glDeleteShader; + PFNGLDETACHSHADERPROC glDetachShader; + PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray; + PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; + PFNGLGETACTIVEATTRIBPROC glGetActiveAttrib; + PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform; + PFNGLGETATTACHEDSHADERSPROC glGetAttachedShaders; + PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation; + PFNGLGETPROGRAMIVPROC glGetProgramiv; + PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; + PFNGLGETSHADERIVPROC glGetShaderiv; + PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; + PFNGLGETSHADERSOURCEPROC glGetShaderSource; + PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; + PFNGLGETUNIFORMFVPROC glGetUniformfv; + PFNGLGETUNIFORMIVPROC glGetUniformiv; + PFNGLGETVERTEXATTRIBDVPROC glGetVertexAttribdv; + PFNGLGETVERTEXATTRIBFVPROC glGetVertexAttribfv; + PFNGLGETVERTEXATTRIBIVPROC glGetVertexAttribiv; + PFNGLGETVERTEXATTRIBPOINTERVPROC glGetVertexAttribPointerv; + PFNGLISPROGRAMPROC glIsProgram; + PFNGLISSHADERPROC glIsShader; + PFNGLLINKPROGRAMPROC glLinkProgram; + PFNGLSHADERSOURCEPROC glShaderSource; + PFNGLUSEPROGRAMPROC glUseProgram; + PFNGLUNIFORM1FPROC glUniform1f; + PFNGLUNIFORM2FPROC glUniform2f; + PFNGLUNIFORM3FPROC glUniform3f; + PFNGLUNIFORM4FPROC glUniform4f; + PFNGLUNIFORM1IPROC glUniform1i; + PFNGLUNIFORM2IPROC glUniform2i; + PFNGLUNIFORM3IPROC glUniform3i; + PFNGLUNIFORM4IPROC glUniform4i; + PFNGLUNIFORM1FVPROC glUniform1fv; + PFNGLUNIFORM2FVPROC glUniform2fv; + PFNGLUNIFORM3FVPROC glUniform3fv; + PFNGLUNIFORM4FVPROC glUniform4fv; + PFNGLUNIFORM1IVPROC glUniform1iv; + PFNGLUNIFORM2IVPROC glUniform2iv; + PFNGLUNIFORM3IVPROC glUniform3iv; + PFNGLUNIFORM4IVPROC glUniform4iv; + PFNGLUNIFORMMATRIX2FVPROC glUniformMatrix2fv; + PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv; + PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv; + PFNGLVALIDATEPROGRAMPROC glValidateProgram; + PFNGLVERTEXATTRIB1DPROC glVertexAttrib1d; + PFNGLVERTEXATTRIB1DVPROC glVertexAttrib1dv; + PFNGLVERTEXATTRIB1FPROC glVertexAttrib1f; + PFNGLVERTEXATTRIB1FVPROC glVertexAttrib1fv; + PFNGLVERTEXATTRIB1SPROC glVertexAttrib1s; + PFNGLVERTEXATTRIB1SVPROC glVertexAttrib1sv; + PFNGLVERTEXATTRIB2DPROC glVertexAttrib2d; + PFNGLVERTEXATTRIB2DVPROC glVertexAttrib2dv; + PFNGLVERTEXATTRIB2FPROC glVertexAttrib2f; + PFNGLVERTEXATTRIB2FVPROC glVertexAttrib2fv; + PFNGLVERTEXATTRIB2SPROC glVertexAttrib2s; + PFNGLVERTEXATTRIB2SVPROC glVertexAttrib2sv; + PFNGLVERTEXATTRIB3DPROC glVertexAttrib3d; + PFNGLVERTEXATTRIB3DVPROC glVertexAttrib3dv; + PFNGLVERTEXATTRIB3FPROC glVertexAttrib3f; + PFNGLVERTEXATTRIB3FVPROC glVertexAttrib3fv; + PFNGLVERTEXATTRIB3SPROC glVertexAttrib3s; + PFNGLVERTEXATTRIB3SVPROC glVertexAttrib3sv; + PFNGLVERTEXATTRIB4NBVPROC glVertexAttrib4Nbv; + PFNGLVERTEXATTRIB4NIVPROC glVertexAttrib4Niv; + PFNGLVERTEXATTRIB4NSVPROC glVertexAttrib4Nsv; + PFNGLVERTEXATTRIB4NUBPROC glVertexAttrib4Nub; + PFNGLVERTEXATTRIB4NUBVPROC glVertexAttrib4Nubv; + PFNGLVERTEXATTRIB4NUIVPROC glVertexAttrib4Nuiv; + PFNGLVERTEXATTRIB4NUSVPROC glVertexAttrib4Nusv; + PFNGLVERTEXATTRIB4BVPROC glVertexAttrib4bv; + PFNGLVERTEXATTRIB4DPROC glVertexAttrib4d; + PFNGLVERTEXATTRIB4DVPROC glVertexAttrib4dv; + PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f; + PFNGLVERTEXATTRIB4FVPROC glVertexAttrib4fv; + PFNGLVERTEXATTRIB4IVPROC glVertexAttrib4iv; + PFNGLVERTEXATTRIB4SPROC glVertexAttrib4s; + PFNGLVERTEXATTRIB4SVPROC glVertexAttrib4sv; + PFNGLVERTEXATTRIB4UBVPROC glVertexAttrib4ubv; + PFNGLVERTEXATTRIB4UIVPROC glVertexAttrib4uiv; + PFNGLVERTEXATTRIB4USVPROC glVertexAttrib4usv; + PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; + PFNGLUNIFORMMATRIX2X3FVPROC glUniformMatrix2x3fv; + PFNGLUNIFORMMATRIX3X2FVPROC glUniformMatrix3x2fv; + PFNGLUNIFORMMATRIX2X4FVPROC glUniformMatrix2x4fv; + PFNGLUNIFORMMATRIX4X2FVPROC glUniformMatrix4x2fv; + PFNGLUNIFORMMATRIX3X4FVPROC glUniformMatrix3x4fv; + PFNGLUNIFORMMATRIX4X3FVPROC glUniformMatrix4x3fv; + PFNGLCOLORMASKIPROC glColorMaski; + PFNGLGETBOOLEANI_VPROC glGetBooleani_v; + PFNGLGETINTEGERI_VPROC glGetIntegeri_v; + PFNGLENABLEIPROC glEnablei; + PFNGLDISABLEIPROC glDisablei; + PFNGLISENABLEDIPROC glIsEnabledi; + PFNGLBEGINTRANSFORMFEEDBACKPROC glBeginTransformFeedback; + PFNGLENDTRANSFORMFEEDBACKPROC glEndTransformFeedback; + PFNGLBINDBUFFERRANGEPROC glBindBufferRange; + PFNGLBINDBUFFERBASEPROC glBindBufferBase; + PFNGLTRANSFORMFEEDBACKVARYINGSPROC glTransformFeedbackVaryings; + PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glGetTransformFeedbackVarying; + PFNGLCLAMPCOLORPROC glClampColor; + PFNGLBEGINCONDITIONALRENDERPROC glBeginConditionalRender; + PFNGLENDCONDITIONALRENDERPROC glEndConditionalRender; + PFNGLVERTEXATTRIBIPOINTERPROC glVertexAttribIPointer; + PFNGLGETVERTEXATTRIBIIVPROC glGetVertexAttribIiv; + PFNGLGETVERTEXATTRIBIUIVPROC glGetVertexAttribIuiv; + PFNGLVERTEXATTRIBI1IPROC glVertexAttribI1i; + PFNGLVERTEXATTRIBI2IPROC glVertexAttribI2i; + PFNGLVERTEXATTRIBI3IPROC glVertexAttribI3i; + PFNGLVERTEXATTRIBI4IPROC glVertexAttribI4i; + PFNGLVERTEXATTRIBI1UIPROC glVertexAttribI1ui; + PFNGLVERTEXATTRIBI2UIPROC glVertexAttribI2ui; + PFNGLVERTEXATTRIBI3UIPROC glVertexAttribI3ui; + PFNGLVERTEXATTRIBI4UIPROC glVertexAttribI4ui; + PFNGLVERTEXATTRIBI1IVPROC glVertexAttribI1iv; + PFNGLVERTEXATTRIBI2IVPROC glVertexAttribI2iv; + PFNGLVERTEXATTRIBI3IVPROC glVertexAttribI3iv; + PFNGLVERTEXATTRIBI4IVPROC glVertexAttribI4iv; + PFNGLVERTEXATTRIBI1UIVPROC glVertexAttribI1uiv; + PFNGLVERTEXATTRIBI2UIVPROC glVertexAttribI2uiv; + PFNGLVERTEXATTRIBI3UIVPROC glVertexAttribI3uiv; + PFNGLVERTEXATTRIBI4UIVPROC glVertexAttribI4uiv; + PFNGLVERTEXATTRIBI4BVPROC glVertexAttribI4bv; + PFNGLVERTEXATTRIBI4SVPROC glVertexAttribI4sv; + PFNGLVERTEXATTRIBI4UBVPROC glVertexAttribI4ubv; + PFNGLVERTEXATTRIBI4USVPROC glVertexAttribI4usv; + PFNGLGETUNIFORMUIVPROC glGetUniformuiv; + PFNGLBINDFRAGDATALOCATIONPROC glBindFragDataLocation; + PFNGLGETFRAGDATALOCATIONPROC glGetFragDataLocation; + PFNGLUNIFORM1UIPROC glUniform1ui; + PFNGLUNIFORM2UIPROC glUniform2ui; + PFNGLUNIFORM3UIPROC glUniform3ui; + PFNGLUNIFORM4UIPROC glUniform4ui; + PFNGLUNIFORM1UIVPROC glUniform1uiv; + PFNGLUNIFORM2UIVPROC glUniform2uiv; + PFNGLUNIFORM3UIVPROC glUniform3uiv; + PFNGLUNIFORM4UIVPROC glUniform4uiv; + PFNGLTEXPARAMETERIIVPROC glTexParameterIiv; + PFNGLTEXPARAMETERIUIVPROC glTexParameterIuiv; + PFNGLGETTEXPARAMETERIIVPROC glGetTexParameterIiv; + PFNGLGETTEXPARAMETERIUIVPROC glGetTexParameterIuiv; + PFNGLCLEARBUFFERIVPROC glClearBufferiv; + PFNGLCLEARBUFFERUIVPROC glClearBufferuiv; + PFNGLCLEARBUFFERFVPROC glClearBufferfv; + PFNGLCLEARBUFFERFIPROC glClearBufferfi; + PFNGLGETSTRINGIPROC glGetStringi; + PFNGLISRENDERBUFFERPROC glIsRenderbuffer; + PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer; + PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers; + PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers; + PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage; + PFNGLGETRENDERBUFFERPARAMETERIVPROC glGetRenderbufferParameteriv; + PFNGLISFRAMEBUFFERPROC glIsFramebuffer; + PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer; + PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers; + PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers; + PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus; + PFNGLFRAMEBUFFERTEXTURE1DPROC glFramebufferTexture1D; + PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D; + PFNGLFRAMEBUFFERTEXTURE3DPROC glFramebufferTexture3D; + PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer; + PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetFramebufferAttachmentParameteriv; + PFNGLGENERATEMIPMAPPROC glGenerateMipmap; + PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer; + PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample; + PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer; + PFNGLMAPBUFFERRANGEPROC glMapBufferRange; + PFNGLFLUSHMAPPEDBUFFERRANGEPROC glFlushMappedBufferRange; + PFNGLBINDVERTEXARRAYPROC glBindVertexArray; + PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays; + PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; + PFNGLISVERTEXARRAYPROC glIsVertexArray; + PFNGLDRAWARRAYSINSTANCEDPROC glDrawArraysInstanced; + PFNGLDRAWELEMENTSINSTANCEDPROC glDrawElementsInstanced; + PFNGLTEXBUFFERPROC glTexBuffer; + PFNGLPRIMITIVERESTARTINDEXPROC glPrimitiveRestartIndex; + PFNGLCOPYBUFFERSUBDATAPROC glCopyBufferSubData; + PFNGLGETUNIFORMINDICESPROC glGetUniformIndices; + PFNGLGETACTIVEUNIFORMSIVPROC glGetActiveUniformsiv; + PFNGLGETACTIVEUNIFORMNAMEPROC glGetActiveUniformName; + PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex; + PFNGLGETACTIVEUNIFORMBLOCKIVPROC glGetActiveUniformBlockiv; + PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glGetActiveUniformBlockName; + PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding; + PFNGLDRAWELEMENTSBASEVERTEXPROC glDrawElementsBaseVertex; + PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glDrawRangeElementsBaseVertex; + PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glDrawElementsInstancedBaseVertex; + PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glMultiDrawElementsBaseVertex; + PFNGLPROVOKINGVERTEXPROC glProvokingVertex; + PFNGLFENCESYNCPROC glFenceSync; + PFNGLISSYNCPROC glIsSync; + PFNGLDELETESYNCPROC glDeleteSync; + PFNGLCLIENTWAITSYNCPROC glClientWaitSync; + PFNGLWAITSYNCPROC glWaitSync; + PFNGLGETINTEGER64VPROC glGetInteger64v; + PFNGLGETSYNCIVPROC glGetSynciv; + PFNGLGETINTEGER64I_VPROC glGetInteger64i_v; + PFNGLGETBUFFERPARAMETERI64VPROC glGetBufferParameteri64v; + PFNGLFRAMEBUFFERTEXTUREPROC glFramebufferTexture; + PFNGLTEXIMAGE2DMULTISAMPLEPROC glTexImage2DMultisample; + PFNGLTEXIMAGE3DMULTISAMPLEPROC glTexImage3DMultisample; + PFNGLGETMULTISAMPLEFVPROC glGetMultisamplefv; + PFNGLSAMPLEMASKIPROC glSampleMaski; + PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glBindFragDataLocationIndexed; + PFNGLGETFRAGDATAINDEXPROC glGetFragDataIndex; + PFNGLGENSAMPLERSPROC glGenSamplers; + PFNGLDELETESAMPLERSPROC glDeleteSamplers; + PFNGLISSAMPLERPROC glIsSampler; + PFNGLBINDSAMPLERPROC glBindSampler; + PFNGLSAMPLERPARAMETERIPROC glSamplerParameteri; + PFNGLSAMPLERPARAMETERIVPROC glSamplerParameteriv; + PFNGLSAMPLERPARAMETERFPROC glSamplerParameterf; + PFNGLSAMPLERPARAMETERFVPROC glSamplerParameterfv; + PFNGLSAMPLERPARAMETERIIVPROC glSamplerParameterIiv; + PFNGLSAMPLERPARAMETERIUIVPROC glSamplerParameterIuiv; + PFNGLGETSAMPLERPARAMETERIVPROC glGetSamplerParameteriv; + PFNGLGETSAMPLERPARAMETERIIVPROC glGetSamplerParameterIiv; + PFNGLGETSAMPLERPARAMETERFVPROC glGetSamplerParameterfv; + PFNGLGETSAMPLERPARAMETERIUIVPROC glGetSamplerParameterIuiv; + PFNGLQUERYCOUNTERPROC glQueryCounter; + PFNGLGETQUERYOBJECTI64VPROC glGetQueryObjecti64v; + PFNGLGETQUERYOBJECTUI64VPROC glGetQueryObjectui64v; + PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor; + PFNGLVERTEXATTRIBP1UIPROC glVertexAttribP1ui; + PFNGLVERTEXATTRIBP1UIVPROC glVertexAttribP1uiv; + PFNGLVERTEXATTRIBP2UIPROC glVertexAttribP2ui; + PFNGLVERTEXATTRIBP2UIVPROC glVertexAttribP2uiv; + PFNGLVERTEXATTRIBP3UIPROC glVertexAttribP3ui; + PFNGLVERTEXATTRIBP3UIVPROC glVertexAttribP3uiv; + PFNGLVERTEXATTRIBP4UIPROC glVertexAttribP4ui; + PFNGLVERTEXATTRIBP4UIVPROC glVertexAttribP4uiv; + PFNGLVERTEXP2UIPROC glVertexP2ui; + PFNGLVERTEXP2UIVPROC glVertexP2uiv; + PFNGLVERTEXP3UIPROC glVertexP3ui; + PFNGLVERTEXP3UIVPROC glVertexP3uiv; + PFNGLVERTEXP4UIPROC glVertexP4ui; + PFNGLVERTEXP4UIVPROC glVertexP4uiv; + PFNGLTEXCOORDP1UIPROC glTexCoordP1ui; + PFNGLTEXCOORDP1UIVPROC glTexCoordP1uiv; + PFNGLTEXCOORDP2UIPROC glTexCoordP2ui; + PFNGLTEXCOORDP2UIVPROC glTexCoordP2uiv; + PFNGLTEXCOORDP3UIPROC glTexCoordP3ui; + PFNGLTEXCOORDP3UIVPROC glTexCoordP3uiv; + PFNGLTEXCOORDP4UIPROC glTexCoordP4ui; + PFNGLTEXCOORDP4UIVPROC glTexCoordP4uiv; + PFNGLMULTITEXCOORDP1UIPROC glMultiTexCoordP1ui; + PFNGLMULTITEXCOORDP1UIVPROC glMultiTexCoordP1uiv; + PFNGLMULTITEXCOORDP2UIPROC glMultiTexCoordP2ui; + PFNGLMULTITEXCOORDP2UIVPROC glMultiTexCoordP2uiv; + PFNGLMULTITEXCOORDP3UIPROC glMultiTexCoordP3ui; + PFNGLMULTITEXCOORDP3UIVPROC glMultiTexCoordP3uiv; + PFNGLMULTITEXCOORDP4UIPROC glMultiTexCoordP4ui; + PFNGLMULTITEXCOORDP4UIVPROC glMultiTexCoordP4uiv; + PFNGLNORMALP3UIPROC glNormalP3ui; + PFNGLNORMALP3UIVPROC glNormalP3uiv; + PFNGLCOLORP3UIPROC glColorP3ui; + PFNGLCOLORP3UIVPROC glColorP3uiv; + PFNGLCOLORP4UIPROC glColorP4ui; + PFNGLCOLORP4UIVPROC glColorP4uiv; + PFNGLSECONDARYCOLORP3UIPROC glSecondaryColorP3ui; + PFNGLSECONDARYCOLORP3UIVPROC glSecondaryColorP3uiv; + PFNGLMINSAMPLESHADINGPROC glMinSampleShading; + PFNGLBLENDEQUATIONIPROC glBlendEquationi; + PFNGLBLENDEQUATIONSEPARATEIPROC glBlendEquationSeparatei; + PFNGLBLENDFUNCIPROC glBlendFunci; + PFNGLBLENDFUNCSEPARATEIPROC glBlendFuncSeparatei; + PFNGLDRAWARRAYSINDIRECTPROC glDrawArraysIndirect; + PFNGLDRAWELEMENTSINDIRECTPROC glDrawElementsIndirect; + PFNGLUNIFORM1DPROC glUniform1d; + PFNGLUNIFORM2DPROC glUniform2d; + PFNGLUNIFORM3DPROC glUniform3d; + PFNGLUNIFORM4DPROC glUniform4d; + PFNGLUNIFORM1DVPROC glUniform1dv; + PFNGLUNIFORM2DVPROC glUniform2dv; + PFNGLUNIFORM3DVPROC glUniform3dv; + PFNGLUNIFORM4DVPROC glUniform4dv; + PFNGLUNIFORMMATRIX2DVPROC glUniformMatrix2dv; + PFNGLUNIFORMMATRIX3DVPROC glUniformMatrix3dv; + PFNGLUNIFORMMATRIX4DVPROC glUniformMatrix4dv; + PFNGLUNIFORMMATRIX2X3DVPROC glUniformMatrix2x3dv; + PFNGLUNIFORMMATRIX2X4DVPROC glUniformMatrix2x4dv; + PFNGLUNIFORMMATRIX3X2DVPROC glUniformMatrix3x2dv; + PFNGLUNIFORMMATRIX3X4DVPROC glUniformMatrix3x4dv; + PFNGLUNIFORMMATRIX4X2DVPROC glUniformMatrix4x2dv; + PFNGLUNIFORMMATRIX4X3DVPROC glUniformMatrix4x3dv; + PFNGLGETUNIFORMDVPROC glGetUniformdv; + PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC glGetSubroutineUniformLocation; + PFNGLGETSUBROUTINEINDEXPROC glGetSubroutineIndex; + PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC glGetActiveSubroutineUniformiv; + PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC glGetActiveSubroutineUniformName; + PFNGLGETACTIVESUBROUTINENAMEPROC glGetActiveSubroutineName; + PFNGLUNIFORMSUBROUTINESUIVPROC glUniformSubroutinesuiv; + PFNGLGETUNIFORMSUBROUTINEUIVPROC glGetUniformSubroutineuiv; + PFNGLGETPROGRAMSTAGEIVPROC glGetProgramStageiv; + PFNGLPATCHPARAMETERIPROC glPatchParameteri; + PFNGLPATCHPARAMETERFVPROC glPatchParameterfv; + PFNGLBINDTRANSFORMFEEDBACKPROC glBindTransformFeedback; + PFNGLDELETETRANSFORMFEEDBACKSPROC glDeleteTransformFeedbacks; + PFNGLGENTRANSFORMFEEDBACKSPROC glGenTransformFeedbacks; + PFNGLISTRANSFORMFEEDBACKPROC glIsTransformFeedback; + PFNGLPAUSETRANSFORMFEEDBACKPROC glPauseTransformFeedback; + PFNGLRESUMETRANSFORMFEEDBACKPROC glResumeTransformFeedback; + PFNGLDRAWTRANSFORMFEEDBACKPROC glDrawTransformFeedback; + PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC glDrawTransformFeedbackStream; + PFNGLBEGINQUERYINDEXEDPROC glBeginQueryIndexed; + PFNGLENDQUERYINDEXEDPROC glEndQueryIndexed; + PFNGLGETQUERYINDEXEDIVPROC glGetQueryIndexediv; + PFNGLRELEASESHADERCOMPILERPROC glReleaseShaderCompiler; + PFNGLSHADERBINARYPROC glShaderBinary; + PFNGLGETSHADERPRECISIONFORMATPROC glGetShaderPrecisionFormat; + PFNGLDEPTHRANGEFPROC glDepthRangef; + PFNGLCLEARDEPTHFPROC glClearDepthf; + PFNGLGETPROGRAMBINARYPROC glGetProgramBinary; + PFNGLPROGRAMBINARYPROC glProgramBinary; + PFNGLPROGRAMPARAMETERIPROC glProgramParameteri; + PFNGLUSEPROGRAMSTAGESPROC glUseProgramStages; + PFNGLACTIVESHADERPROGRAMPROC glActiveShaderProgram; + PFNGLCREATESHADERPROGRAMVPROC glCreateShaderProgramv; + PFNGLBINDPROGRAMPIPELINEPROC glBindProgramPipeline; + PFNGLDELETEPROGRAMPIPELINESPROC glDeleteProgramPipelines; + PFNGLGENPROGRAMPIPELINESPROC glGenProgramPipelines; + PFNGLISPROGRAMPIPELINEPROC glIsProgramPipeline; + PFNGLGETPROGRAMPIPELINEIVPROC glGetProgramPipelineiv; + PFNGLPROGRAMUNIFORM1IPROC glProgramUniform1i; + PFNGLPROGRAMUNIFORM1IVPROC glProgramUniform1iv; + PFNGLPROGRAMUNIFORM1FPROC glProgramUniform1f; + PFNGLPROGRAMUNIFORM1FVPROC glProgramUniform1fv; + PFNGLPROGRAMUNIFORM1DPROC glProgramUniform1d; + PFNGLPROGRAMUNIFORM1DVPROC glProgramUniform1dv; + PFNGLPROGRAMUNIFORM1UIPROC glProgramUniform1ui; + PFNGLPROGRAMUNIFORM1UIVPROC glProgramUniform1uiv; + PFNGLPROGRAMUNIFORM2IPROC glProgramUniform2i; + PFNGLPROGRAMUNIFORM2IVPROC glProgramUniform2iv; + PFNGLPROGRAMUNIFORM2FPROC glProgramUniform2f; + PFNGLPROGRAMUNIFORM2FVPROC glProgramUniform2fv; + PFNGLPROGRAMUNIFORM2DPROC glProgramUniform2d; + PFNGLPROGRAMUNIFORM2DVPROC glProgramUniform2dv; + PFNGLPROGRAMUNIFORM2UIPROC glProgramUniform2ui; + PFNGLPROGRAMUNIFORM2UIVPROC glProgramUniform2uiv; + PFNGLPROGRAMUNIFORM3IPROC glProgramUniform3i; + PFNGLPROGRAMUNIFORM3IVPROC glProgramUniform3iv; + PFNGLPROGRAMUNIFORM3FPROC glProgramUniform3f; + PFNGLPROGRAMUNIFORM3FVPROC glProgramUniform3fv; + PFNGLPROGRAMUNIFORM3DPROC glProgramUniform3d; + PFNGLPROGRAMUNIFORM3DVPROC glProgramUniform3dv; + PFNGLPROGRAMUNIFORM3UIPROC glProgramUniform3ui; + PFNGLPROGRAMUNIFORM3UIVPROC glProgramUniform3uiv; + PFNGLPROGRAMUNIFORM4IPROC glProgramUniform4i; + PFNGLPROGRAMUNIFORM4IVPROC glProgramUniform4iv; + PFNGLPROGRAMUNIFORM4FPROC glProgramUniform4f; + PFNGLPROGRAMUNIFORM4FVPROC glProgramUniform4fv; + PFNGLPROGRAMUNIFORM4DPROC glProgramUniform4d; + PFNGLPROGRAMUNIFORM4DVPROC glProgramUniform4dv; + PFNGLPROGRAMUNIFORM4UIPROC glProgramUniform4ui; + PFNGLPROGRAMUNIFORM4UIVPROC glProgramUniform4uiv; + PFNGLPROGRAMUNIFORMMATRIX2FVPROC glProgramUniformMatrix2fv; + PFNGLPROGRAMUNIFORMMATRIX3FVPROC glProgramUniformMatrix3fv; + PFNGLPROGRAMUNIFORMMATRIX4FVPROC glProgramUniformMatrix4fv; + PFNGLPROGRAMUNIFORMMATRIX2DVPROC glProgramUniformMatrix2dv; + PFNGLPROGRAMUNIFORMMATRIX3DVPROC glProgramUniformMatrix3dv; + PFNGLPROGRAMUNIFORMMATRIX4DVPROC glProgramUniformMatrix4dv; + PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC glProgramUniformMatrix2x3fv; + PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC glProgramUniformMatrix3x2fv; + PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC glProgramUniformMatrix2x4fv; + PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC glProgramUniformMatrix4x2fv; + PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC glProgramUniformMatrix3x4fv; + PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC glProgramUniformMatrix4x3fv; + PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC glProgramUniformMatrix2x3dv; + PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC glProgramUniformMatrix3x2dv; + PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC glProgramUniformMatrix2x4dv; + PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC glProgramUniformMatrix4x2dv; + PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC glProgramUniformMatrix3x4dv; + PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC glProgramUniformMatrix4x3dv; + PFNGLVALIDATEPROGRAMPIPELINEPROC glValidateProgramPipeline; + PFNGLGETPROGRAMPIPELINEINFOLOGPROC glGetProgramPipelineInfoLog; + PFNGLVERTEXATTRIBL1DPROC glVertexAttribL1d; + PFNGLVERTEXATTRIBL2DPROC glVertexAttribL2d; + PFNGLVERTEXATTRIBL3DPROC glVertexAttribL3d; + PFNGLVERTEXATTRIBL4DPROC glVertexAttribL4d; + PFNGLVERTEXATTRIBL1DVPROC glVertexAttribL1dv; + PFNGLVERTEXATTRIBL2DVPROC glVertexAttribL2dv; + PFNGLVERTEXATTRIBL3DVPROC glVertexAttribL3dv; + PFNGLVERTEXATTRIBL4DVPROC glVertexAttribL4dv; + PFNGLVERTEXATTRIBLPOINTERPROC glVertexAttribLPointer; + PFNGLGETVERTEXATTRIBLDVPROC glGetVertexAttribLdv; + PFNGLVIEWPORTARRAYVPROC glViewportArrayv; + PFNGLVIEWPORTINDEXEDFPROC glViewportIndexedf; + PFNGLVIEWPORTINDEXEDFVPROC glViewportIndexedfv; + PFNGLSCISSORARRAYVPROC glScissorArrayv; + PFNGLSCISSORINDEXEDPROC glScissorIndexed; + PFNGLSCISSORINDEXEDVPROC glScissorIndexedv; + PFNGLDEPTHRANGEARRAYVPROC glDepthRangeArrayv; + PFNGLDEPTHRANGEINDEXEDPROC glDepthRangeIndexed; + PFNGLGETFLOATI_VPROC glGetFloati_v; + PFNGLGETDOUBLEI_VPROC glGetDoublei_v; + PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC glDrawArraysInstancedBaseInstance; + PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC glDrawElementsInstancedBaseInstance; + PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC glDrawElementsInstancedBaseVertexBaseInstance; + PFNGLGETINTERNALFORMATIVPROC glGetInternalformativ; + PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC glGetActiveAtomicCounterBufferiv; + PFNGLBINDIMAGETEXTUREPROC glBindImageTexture; + PFNGLMEMORYBARRIERPROC glMemoryBarrier; + PFNGLTEXSTORAGE1DPROC glTexStorage1D; + PFNGLTEXSTORAGE2DPROC glTexStorage2D; + PFNGLTEXSTORAGE3DPROC glTexStorage3D; + PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC glDrawTransformFeedbackInstanced; + PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC glDrawTransformFeedbackStreamInstanced; + PFNGLCLEARBUFFERDATAPROC glClearBufferData; + PFNGLCLEARBUFFERSUBDATAPROC glClearBufferSubData; + PFNGLDISPATCHCOMPUTEPROC glDispatchCompute; + PFNGLDISPATCHCOMPUTEINDIRECTPROC glDispatchComputeIndirect; + PFNGLCOPYIMAGESUBDATAPROC glCopyImageSubData; + PFNGLFRAMEBUFFERPARAMETERIPROC glFramebufferParameteri; + PFNGLGETFRAMEBUFFERPARAMETERIVPROC glGetFramebufferParameteriv; + PFNGLGETINTERNALFORMATI64VPROC glGetInternalformati64v; + PFNGLINVALIDATETEXSUBIMAGEPROC glInvalidateTexSubImage; + PFNGLINVALIDATETEXIMAGEPROC glInvalidateTexImage; + PFNGLINVALIDATEBUFFERSUBDATAPROC glInvalidateBufferSubData; + PFNGLINVALIDATEBUFFERDATAPROC glInvalidateBufferData; + PFNGLINVALIDATEFRAMEBUFFERPROC glInvalidateFramebuffer; + PFNGLINVALIDATESUBFRAMEBUFFERPROC glInvalidateSubFramebuffer; + PFNGLMULTIDRAWARRAYSINDIRECTPROC glMultiDrawArraysIndirect; + PFNGLMULTIDRAWELEMENTSINDIRECTPROC glMultiDrawElementsIndirect; + PFNGLGETPROGRAMINTERFACEIVPROC glGetProgramInterfaceiv; + PFNGLGETPROGRAMRESOURCEINDEXPROC glGetProgramResourceIndex; + PFNGLGETPROGRAMRESOURCENAMEPROC glGetProgramResourceName; + PFNGLGETPROGRAMRESOURCEIVPROC glGetProgramResourceiv; + PFNGLGETPROGRAMRESOURCELOCATIONPROC glGetProgramResourceLocation; + PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC glGetProgramResourceLocationIndex; + PFNGLSHADERSTORAGEBLOCKBINDINGPROC glShaderStorageBlockBinding; + PFNGLTEXBUFFERRANGEPROC glTexBufferRange; + PFNGLTEXSTORAGE2DMULTISAMPLEPROC glTexStorage2DMultisample; + PFNGLTEXSTORAGE3DMULTISAMPLEPROC glTexStorage3DMultisample; + PFNGLTEXTUREVIEWPROC glTextureView; + PFNGLBINDVERTEXBUFFERPROC glBindVertexBuffer; + PFNGLVERTEXATTRIBFORMATPROC glVertexAttribFormat; + PFNGLVERTEXATTRIBIFORMATPROC glVertexAttribIFormat; + PFNGLVERTEXATTRIBLFORMATPROC glVertexAttribLFormat; + PFNGLVERTEXATTRIBBINDINGPROC glVertexAttribBinding; + PFNGLVERTEXBINDINGDIVISORPROC glVertexBindingDivisor; + PFNGLDEBUGMESSAGECONTROLPROC glDebugMessageControl; + PFNGLDEBUGMESSAGEINSERTPROC glDebugMessageInsert; + PFNGLDEBUGMESSAGECALLBACKPROC glDebugMessageCallback; + PFNGLGETDEBUGMESSAGELOGPROC glGetDebugMessageLog; + PFNGLPUSHDEBUGGROUPPROC glPushDebugGroup; + PFNGLPOPDEBUGGROUPPROC glPopDebugGroup; + PFNGLOBJECTLABELPROC glObjectLabel; + PFNGLGETOBJECTLABELPROC glGetObjectLabel; + PFNGLOBJECTPTRLABELPROC glObjectPtrLabel; + PFNGLGETOBJECTPTRLABELPROC glGetObjectPtrLabel; + PFNGLBUFFERSTORAGEPROC glBufferStorage; + PFNGLCLEARTEXIMAGEPROC glClearTexImage; + PFNGLCLEARTEXSUBIMAGEPROC glClearTexSubImage; + PFNGLBINDBUFFERSBASEPROC glBindBuffersBase; + PFNGLBINDBUFFERSRANGEPROC glBindBuffersRange; + PFNGLBINDTEXTURESPROC glBindTextures; + PFNGLBINDSAMPLERSPROC glBindSamplers; + PFNGLBINDIMAGETEXTURESPROC glBindImageTextures; + PFNGLBINDVERTEXBUFFERSPROC glBindVertexBuffers; + PFNGLCLIPCONTROLPROC glClipControl; + PFNGLCREATETRANSFORMFEEDBACKSPROC glCreateTransformFeedbacks; + PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC glTransformFeedbackBufferBase; + PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC glTransformFeedbackBufferRange; + PFNGLGETTRANSFORMFEEDBACKIVPROC glGetTransformFeedbackiv; + PFNGLGETTRANSFORMFEEDBACKI_VPROC glGetTransformFeedbacki_v; + PFNGLGETTRANSFORMFEEDBACKI64_VPROC glGetTransformFeedbacki64_v; + PFNGLCREATEBUFFERSPROC glCreateBuffers; + PFNGLNAMEDBUFFERSTORAGEPROC glNamedBufferStorage; + PFNGLNAMEDBUFFERDATAPROC glNamedBufferData; + PFNGLNAMEDBUFFERSUBDATAPROC glNamedBufferSubData; + PFNGLCOPYNAMEDBUFFERSUBDATAPROC glCopyNamedBufferSubData; + PFNGLCLEARNAMEDBUFFERDATAPROC glClearNamedBufferData; + PFNGLCLEARNAMEDBUFFERSUBDATAPROC glClearNamedBufferSubData; + PFNGLMAPNAMEDBUFFERPROC glMapNamedBuffer; + PFNGLMAPNAMEDBUFFERRANGEPROC glMapNamedBufferRange; + PFNGLUNMAPNAMEDBUFFERPROC glUnmapNamedBuffer; + PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC glFlushMappedNamedBufferRange; + PFNGLGETNAMEDBUFFERPARAMETERIVPROC glGetNamedBufferParameteriv; + PFNGLGETNAMEDBUFFERPARAMETERI64VPROC glGetNamedBufferParameteri64v; + PFNGLGETNAMEDBUFFERPOINTERVPROC glGetNamedBufferPointerv; + PFNGLGETNAMEDBUFFERSUBDATAPROC glGetNamedBufferSubData; + PFNGLCREATEFRAMEBUFFERSPROC glCreateFramebuffers; + PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC glNamedFramebufferRenderbuffer; + PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC glNamedFramebufferParameteri; + PFNGLNAMEDFRAMEBUFFERTEXTUREPROC glNamedFramebufferTexture; + PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC glNamedFramebufferTextureLayer; + PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC glNamedFramebufferDrawBuffer; + PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC glNamedFramebufferDrawBuffers; + PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC glNamedFramebufferReadBuffer; + PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC glInvalidateNamedFramebufferData; + PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC glInvalidateNamedFramebufferSubData; + PFNGLCLEARNAMEDFRAMEBUFFERIVPROC glClearNamedFramebufferiv; + PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC glClearNamedFramebufferuiv; + PFNGLCLEARNAMEDFRAMEBUFFERFVPROC glClearNamedFramebufferfv; + PFNGLCLEARNAMEDFRAMEBUFFERFIPROC glClearNamedFramebufferfi; + PFNGLBLITNAMEDFRAMEBUFFERPROC glBlitNamedFramebuffer; + PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glCheckNamedFramebufferStatus; + PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC glGetNamedFramebufferParameteriv; + PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetNamedFramebufferAttachmentParameteriv; + PFNGLCREATERENDERBUFFERSPROC glCreateRenderbuffers; + PFNGLNAMEDRENDERBUFFERSTORAGEPROC glNamedRenderbufferStorage; + PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC glNamedRenderbufferStorageMultisample; + PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC glGetNamedRenderbufferParameteriv; + PFNGLCREATETEXTURESPROC glCreateTextures; + PFNGLTEXTUREBUFFERPROC glTextureBuffer; + PFNGLTEXTUREBUFFERRANGEPROC glTextureBufferRange; + PFNGLTEXTURESTORAGE1DPROC glTextureStorage1D; + PFNGLTEXTURESTORAGE2DPROC glTextureStorage2D; + PFNGLTEXTURESTORAGE3DPROC glTextureStorage3D; + PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC glTextureStorage2DMultisample; + PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC glTextureStorage3DMultisample; + PFNGLTEXTURESUBIMAGE1DPROC glTextureSubImage1D; + PFNGLTEXTURESUBIMAGE2DPROC glTextureSubImage2D; + PFNGLTEXTURESUBIMAGE3DPROC glTextureSubImage3D; + PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC glCompressedTextureSubImage1D; + PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC glCompressedTextureSubImage2D; + PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC glCompressedTextureSubImage3D; + PFNGLCOPYTEXTURESUBIMAGE1DPROC glCopyTextureSubImage1D; + PFNGLCOPYTEXTURESUBIMAGE2DPROC glCopyTextureSubImage2D; + PFNGLCOPYTEXTURESUBIMAGE3DPROC glCopyTextureSubImage3D; + PFNGLTEXTUREPARAMETERFPROC glTextureParameterf; + PFNGLTEXTUREPARAMETERFVPROC glTextureParameterfv; + PFNGLTEXTUREPARAMETERIPROC glTextureParameteri; + PFNGLTEXTUREPARAMETERIIVPROC glTextureParameterIiv; + PFNGLTEXTUREPARAMETERIUIVPROC glTextureParameterIuiv; + PFNGLTEXTUREPARAMETERIVPROC glTextureParameteriv; + PFNGLGENERATETEXTUREMIPMAPPROC glGenerateTextureMipmap; + PFNGLBINDTEXTUREUNITPROC glBindTextureUnit; + PFNGLGETTEXTUREIMAGEPROC glGetTextureImage; + PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC glGetCompressedTextureImage; + PFNGLGETTEXTURELEVELPARAMETERFVPROC glGetTextureLevelParameterfv; + PFNGLGETTEXTURELEVELPARAMETERIVPROC glGetTextureLevelParameteriv; + PFNGLGETTEXTUREPARAMETERFVPROC glGetTextureParameterfv; + PFNGLGETTEXTUREPARAMETERIIVPROC glGetTextureParameterIiv; + PFNGLGETTEXTUREPARAMETERIUIVPROC glGetTextureParameterIuiv; + PFNGLGETTEXTUREPARAMETERIVPROC glGetTextureParameteriv; + PFNGLCREATEVERTEXARRAYSPROC glCreateVertexArrays; + PFNGLDISABLEVERTEXARRAYATTRIBPROC glDisableVertexArrayAttrib; + PFNGLENABLEVERTEXARRAYATTRIBPROC glEnableVertexArrayAttrib; + PFNGLVERTEXARRAYELEMENTBUFFERPROC glVertexArrayElementBuffer; + PFNGLVERTEXARRAYVERTEXBUFFERPROC glVertexArrayVertexBuffer; + PFNGLVERTEXARRAYVERTEXBUFFERSPROC glVertexArrayVertexBuffers; + PFNGLVERTEXARRAYATTRIBBINDINGPROC glVertexArrayAttribBinding; + PFNGLVERTEXARRAYATTRIBFORMATPROC glVertexArrayAttribFormat; + PFNGLVERTEXARRAYATTRIBIFORMATPROC glVertexArrayAttribIFormat; + PFNGLVERTEXARRAYATTRIBLFORMATPROC glVertexArrayAttribLFormat; + PFNGLVERTEXARRAYBINDINGDIVISORPROC glVertexArrayBindingDivisor; + PFNGLGETVERTEXARRAYIVPROC glGetVertexArrayiv; + PFNGLGETVERTEXARRAYINDEXEDIVPROC glGetVertexArrayIndexediv; + PFNGLGETVERTEXARRAYINDEXED64IVPROC glGetVertexArrayIndexed64iv; + PFNGLCREATESAMPLERSPROC glCreateSamplers; + PFNGLCREATEPROGRAMPIPELINESPROC glCreateProgramPipelines; + PFNGLCREATEQUERIESPROC glCreateQueries; + PFNGLGETQUERYBUFFEROBJECTI64VPROC glGetQueryBufferObjecti64v; + PFNGLGETQUERYBUFFEROBJECTIVPROC glGetQueryBufferObjectiv; + PFNGLGETQUERYBUFFEROBJECTUI64VPROC glGetQueryBufferObjectui64v; + PFNGLGETQUERYBUFFEROBJECTUIVPROC glGetQueryBufferObjectuiv; + PFNGLMEMORYBARRIERBYREGIONPROC glMemoryBarrierByRegion; + PFNGLGETTEXTURESUBIMAGEPROC glGetTextureSubImage; + PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC glGetCompressedTextureSubImage; + PFNGLGETGRAPHICSRESETSTATUSPROC glGetGraphicsResetStatus; + PFNGLGETNCOMPRESSEDTEXIMAGEPROC glGetnCompressedTexImage; + PFNGLGETNTEXIMAGEPROC glGetnTexImage; + PFNGLGETNUNIFORMDVPROC glGetnUniformdv; + PFNGLGETNUNIFORMFVPROC glGetnUniformfv; + PFNGLGETNUNIFORMIVPROC glGetnUniformiv; + PFNGLGETNUNIFORMUIVPROC glGetnUniformuiv; + PFNGLREADNPIXELSPROC glReadnPixels; + PFNGLGETNMAPDVPROC glGetnMapdv; + PFNGLGETNMAPFVPROC glGetnMapfv; + PFNGLGETNMAPIVPROC glGetnMapiv; + PFNGLGETNPIXELMAPFVPROC glGetnPixelMapfv; + PFNGLGETNPIXELMAPUIVPROC glGetnPixelMapuiv; + PFNGLGETNPIXELMAPUSVPROC glGetnPixelMapusv; + PFNGLGETNPOLYGONSTIPPLEPROC glGetnPolygonStipple; + PFNGLGETNCOLORTABLEPROC glGetnColorTable; + PFNGLGETNCONVOLUTIONFILTERPROC glGetnConvolutionFilter; + PFNGLGETNSEPARABLEFILTERPROC glGetnSeparableFilter; + PFNGLGETNHISTOGRAMPROC glGetnHistogram; + PFNGLGETNMINMAXPROC glGetnMinmax; + PFNGLTEXTUREBARRIERPROC glTextureBarrier; + PFNGLSPECIALIZESHADERPROC glSpecializeShader; + PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC glMultiDrawArraysIndirectCount; + PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC glMultiDrawElementsIndirectCount; + PFNGLPOLYGONOFFSETCLAMPPROC glPolygonOffsetClamp; +#if defined(GLBIND_WGL) + PFNWGLCOPYCONTEXTPROC wglCopyContext; + PFNWGLCREATECONTEXTPROC wglCreateContext; + PFNWGLCREATELAYERCONTEXTPROC wglCreateLayerContext; + PFNWGLDELETECONTEXTPROC wglDeleteContext; + PFNWGLDESCRIBELAYERPLANEPROC wglDescribeLayerPlane; + PFNWGLGETCURRENTCONTEXTPROC wglGetCurrentContext; + PFNWGLGETCURRENTDCPROC wglGetCurrentDC; + PFNWGLGETLAYERPALETTEENTRIESPROC wglGetLayerPaletteEntries; + PFNWGLGETPROCADDRESSPROC wglGetProcAddress; + PFNWGLMAKECURRENTPROC wglMakeCurrent; + PFNWGLREALIZELAYERPALETTEPROC wglRealizeLayerPalette; + PFNWGLSETLAYERPALETTEENTRIESPROC wglSetLayerPaletteEntries; + PFNWGLSHARELISTSPROC wglShareLists; + PFNWGLSWAPLAYERBUFFERSPROC wglSwapLayerBuffers; + PFNWGLUSEFONTBITMAPSAPROC wglUseFontBitmapsA; + PFNWGLUSEFONTBITMAPSWPROC wglUseFontBitmapsW; + PFNWGLUSEFONTOUTLINESAPROC wglUseFontOutlinesA; + PFNWGLUSEFONTOUTLINESWPROC wglUseFontOutlinesW; +#endif /* GLBIND_WGL */ +#if defined(GLBIND_GLX) + PFNGLXCHOOSEVISUALPROC glXChooseVisual; + PFNGLXCREATECONTEXTPROC glXCreateContext; + PFNGLXDESTROYCONTEXTPROC glXDestroyContext; + PFNGLXMAKECURRENTPROC glXMakeCurrent; + PFNGLXCOPYCONTEXTPROC glXCopyContext; + PFNGLXSWAPBUFFERSPROC glXSwapBuffers; + PFNGLXCREATEGLXPIXMAPPROC glXCreateGLXPixmap; + PFNGLXDESTROYGLXPIXMAPPROC glXDestroyGLXPixmap; + PFNGLXQUERYEXTENSIONPROC glXQueryExtension; + PFNGLXQUERYVERSIONPROC glXQueryVersion; + PFNGLXISDIRECTPROC glXIsDirect; + PFNGLXGETCONFIGPROC glXGetConfig; + PFNGLXGETCURRENTCONTEXTPROC glXGetCurrentContext; + PFNGLXGETCURRENTDRAWABLEPROC glXGetCurrentDrawable; + PFNGLXWAITGLPROC glXWaitGL; + PFNGLXWAITXPROC glXWaitX; + PFNGLXUSEXFONTPROC glXUseXFont; + PFNGLXQUERYEXTENSIONSSTRINGPROC glXQueryExtensionsString; + PFNGLXQUERYSERVERSTRINGPROC glXQueryServerString; + PFNGLXGETCLIENTSTRINGPROC glXGetClientString; + PFNGLXGETCURRENTDISPLAYPROC glXGetCurrentDisplay; + PFNGLXGETFBCONFIGSPROC glXGetFBConfigs; + PFNGLXCHOOSEFBCONFIGPROC glXChooseFBConfig; + PFNGLXGETFBCONFIGATTRIBPROC glXGetFBConfigAttrib; + PFNGLXGETVISUALFROMFBCONFIGPROC glXGetVisualFromFBConfig; + PFNGLXCREATEWINDOWPROC glXCreateWindow; + PFNGLXDESTROYWINDOWPROC glXDestroyWindow; + PFNGLXCREATEPIXMAPPROC glXCreatePixmap; + PFNGLXDESTROYPIXMAPPROC glXDestroyPixmap; + PFNGLXCREATEPBUFFERPROC glXCreatePbuffer; + PFNGLXDESTROYPBUFFERPROC glXDestroyPbuffer; + PFNGLXQUERYDRAWABLEPROC glXQueryDrawable; + PFNGLXCREATENEWCONTEXTPROC glXCreateNewContext; + PFNGLXMAKECONTEXTCURRENTPROC glXMakeContextCurrent; + PFNGLXGETCURRENTREADDRAWABLEPROC glXGetCurrentReadDrawable; + PFNGLXQUERYCONTEXTPROC glXQueryContext; + PFNGLXSELECTEVENTPROC glXSelectEvent; + PFNGLXGETSELECTEDEVENTPROC glXGetSelectedEvent; + PFNGLXGETPROCADDRESSPROC glXGetProcAddress; +#endif /* GLBIND_GLX */ + PFNGLTBUFFERMASK3DFXPROC glTbufferMask3DFX; + PFNGLDEBUGMESSAGEENABLEAMDPROC glDebugMessageEnableAMD; + PFNGLDEBUGMESSAGEINSERTAMDPROC glDebugMessageInsertAMD; + PFNGLDEBUGMESSAGECALLBACKAMDPROC glDebugMessageCallbackAMD; + PFNGLGETDEBUGMESSAGELOGAMDPROC glGetDebugMessageLogAMD; + PFNGLBLENDFUNCINDEXEDAMDPROC glBlendFuncIndexedAMD; + PFNGLBLENDFUNCSEPARATEINDEXEDAMDPROC glBlendFuncSeparateIndexedAMD; + PFNGLBLENDEQUATIONINDEXEDAMDPROC glBlendEquationIndexedAMD; + PFNGLBLENDEQUATIONSEPARATEINDEXEDAMDPROC glBlendEquationSeparateIndexedAMD; + PFNGLRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC glRenderbufferStorageMultisampleAdvancedAMD; + PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC glNamedRenderbufferStorageMultisampleAdvancedAMD; + PFNGLFRAMEBUFFERSAMPLEPOSITIONSFVAMDPROC glFramebufferSamplePositionsfvAMD; + PFNGLNAMEDFRAMEBUFFERSAMPLEPOSITIONSFVAMDPROC glNamedFramebufferSamplePositionsfvAMD; + PFNGLGETFRAMEBUFFERPARAMETERFVAMDPROC glGetFramebufferParameterfvAMD; + PFNGLGETNAMEDFRAMEBUFFERPARAMETERFVAMDPROC glGetNamedFramebufferParameterfvAMD; + PFNGLUNIFORM1I64NVPROC glUniform1i64NV; + PFNGLUNIFORM2I64NVPROC glUniform2i64NV; + PFNGLUNIFORM3I64NVPROC glUniform3i64NV; + PFNGLUNIFORM4I64NVPROC glUniform4i64NV; + PFNGLUNIFORM1I64VNVPROC glUniform1i64vNV; + PFNGLUNIFORM2I64VNVPROC glUniform2i64vNV; + PFNGLUNIFORM3I64VNVPROC glUniform3i64vNV; + PFNGLUNIFORM4I64VNVPROC glUniform4i64vNV; + PFNGLUNIFORM1UI64NVPROC glUniform1ui64NV; + PFNGLUNIFORM2UI64NVPROC glUniform2ui64NV; + PFNGLUNIFORM3UI64NVPROC glUniform3ui64NV; + PFNGLUNIFORM4UI64NVPROC glUniform4ui64NV; + PFNGLUNIFORM1UI64VNVPROC glUniform1ui64vNV; + PFNGLUNIFORM2UI64VNVPROC glUniform2ui64vNV; + PFNGLUNIFORM3UI64VNVPROC glUniform3ui64vNV; + PFNGLUNIFORM4UI64VNVPROC glUniform4ui64vNV; + PFNGLGETUNIFORMI64VNVPROC glGetUniformi64vNV; + PFNGLGETUNIFORMUI64VNVPROC glGetUniformui64vNV; + PFNGLPROGRAMUNIFORM1I64NVPROC glProgramUniform1i64NV; + PFNGLPROGRAMUNIFORM2I64NVPROC glProgramUniform2i64NV; + PFNGLPROGRAMUNIFORM3I64NVPROC glProgramUniform3i64NV; + PFNGLPROGRAMUNIFORM4I64NVPROC glProgramUniform4i64NV; + PFNGLPROGRAMUNIFORM1I64VNVPROC glProgramUniform1i64vNV; + PFNGLPROGRAMUNIFORM2I64VNVPROC glProgramUniform2i64vNV; + PFNGLPROGRAMUNIFORM3I64VNVPROC glProgramUniform3i64vNV; + PFNGLPROGRAMUNIFORM4I64VNVPROC glProgramUniform4i64vNV; + PFNGLPROGRAMUNIFORM1UI64NVPROC glProgramUniform1ui64NV; + PFNGLPROGRAMUNIFORM2UI64NVPROC glProgramUniform2ui64NV; + PFNGLPROGRAMUNIFORM3UI64NVPROC glProgramUniform3ui64NV; + PFNGLPROGRAMUNIFORM4UI64NVPROC glProgramUniform4ui64NV; + PFNGLPROGRAMUNIFORM1UI64VNVPROC glProgramUniform1ui64vNV; + PFNGLPROGRAMUNIFORM2UI64VNVPROC glProgramUniform2ui64vNV; + PFNGLPROGRAMUNIFORM3UI64VNVPROC glProgramUniform3ui64vNV; + PFNGLPROGRAMUNIFORM4UI64VNVPROC glProgramUniform4ui64vNV; + PFNGLVERTEXATTRIBPARAMETERIAMDPROC glVertexAttribParameteriAMD; + PFNGLMULTIDRAWARRAYSINDIRECTAMDPROC glMultiDrawArraysIndirectAMD; + PFNGLMULTIDRAWELEMENTSINDIRECTAMDPROC glMultiDrawElementsIndirectAMD; + PFNGLGENNAMESAMDPROC glGenNamesAMD; + PFNGLDELETENAMESAMDPROC glDeleteNamesAMD; + PFNGLISNAMEAMDPROC glIsNameAMD; + PFNGLQUERYOBJECTPARAMETERUIAMDPROC glQueryObjectParameteruiAMD; + PFNGLGETPERFMONITORGROUPSAMDPROC glGetPerfMonitorGroupsAMD; + PFNGLGETPERFMONITORCOUNTERSAMDPROC glGetPerfMonitorCountersAMD; + PFNGLGETPERFMONITORGROUPSTRINGAMDPROC glGetPerfMonitorGroupStringAMD; + PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC glGetPerfMonitorCounterStringAMD; + PFNGLGETPERFMONITORCOUNTERINFOAMDPROC glGetPerfMonitorCounterInfoAMD; + PFNGLGENPERFMONITORSAMDPROC glGenPerfMonitorsAMD; + PFNGLDELETEPERFMONITORSAMDPROC glDeletePerfMonitorsAMD; + PFNGLSELECTPERFMONITORCOUNTERSAMDPROC glSelectPerfMonitorCountersAMD; + PFNGLBEGINPERFMONITORAMDPROC glBeginPerfMonitorAMD; + PFNGLENDPERFMONITORAMDPROC glEndPerfMonitorAMD; + PFNGLGETPERFMONITORCOUNTERDATAAMDPROC glGetPerfMonitorCounterDataAMD; + PFNGLSETMULTISAMPLEFVAMDPROC glSetMultisamplefvAMD; + PFNGLTEXSTORAGESPARSEAMDPROC glTexStorageSparseAMD; + PFNGLTEXTURESTORAGESPARSEAMDPROC glTextureStorageSparseAMD; + PFNGLSTENCILOPVALUEAMDPROC glStencilOpValueAMD; + PFNGLTESSELLATIONFACTORAMDPROC glTessellationFactorAMD; + PFNGLTESSELLATIONMODEAMDPROC glTessellationModeAMD; + PFNGLELEMENTPOINTERAPPLEPROC glElementPointerAPPLE; + PFNGLDRAWELEMENTARRAYAPPLEPROC glDrawElementArrayAPPLE; + PFNGLDRAWRANGEELEMENTARRAYAPPLEPROC glDrawRangeElementArrayAPPLE; + PFNGLMULTIDRAWELEMENTARRAYAPPLEPROC glMultiDrawElementArrayAPPLE; + PFNGLMULTIDRAWRANGEELEMENTARRAYAPPLEPROC glMultiDrawRangeElementArrayAPPLE; + PFNGLGENFENCESAPPLEPROC glGenFencesAPPLE; + PFNGLDELETEFENCESAPPLEPROC glDeleteFencesAPPLE; + PFNGLSETFENCEAPPLEPROC glSetFenceAPPLE; + PFNGLISFENCEAPPLEPROC glIsFenceAPPLE; + PFNGLTESTFENCEAPPLEPROC glTestFenceAPPLE; + PFNGLFINISHFENCEAPPLEPROC glFinishFenceAPPLE; + PFNGLTESTOBJECTAPPLEPROC glTestObjectAPPLE; + PFNGLFINISHOBJECTAPPLEPROC glFinishObjectAPPLE; + PFNGLBUFFERPARAMETERIAPPLEPROC glBufferParameteriAPPLE; + PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC glFlushMappedBufferRangeAPPLE; + PFNGLOBJECTPURGEABLEAPPLEPROC glObjectPurgeableAPPLE; + PFNGLOBJECTUNPURGEABLEAPPLEPROC glObjectUnpurgeableAPPLE; + PFNGLGETOBJECTPARAMETERIVAPPLEPROC glGetObjectParameterivAPPLE; + PFNGLTEXTURERANGEAPPLEPROC glTextureRangeAPPLE; + PFNGLGETTEXPARAMETERPOINTERVAPPLEPROC glGetTexParameterPointervAPPLE; + PFNGLBINDVERTEXARRAYAPPLEPROC glBindVertexArrayAPPLE; + PFNGLDELETEVERTEXARRAYSAPPLEPROC glDeleteVertexArraysAPPLE; + PFNGLGENVERTEXARRAYSAPPLEPROC glGenVertexArraysAPPLE; + PFNGLISVERTEXARRAYAPPLEPROC glIsVertexArrayAPPLE; + PFNGLVERTEXARRAYRANGEAPPLEPROC glVertexArrayRangeAPPLE; + PFNGLFLUSHVERTEXARRAYRANGEAPPLEPROC glFlushVertexArrayRangeAPPLE; + PFNGLVERTEXARRAYPARAMETERIAPPLEPROC glVertexArrayParameteriAPPLE; + PFNGLENABLEVERTEXATTRIBAPPLEPROC glEnableVertexAttribAPPLE; + PFNGLDISABLEVERTEXATTRIBAPPLEPROC glDisableVertexAttribAPPLE; + PFNGLISVERTEXATTRIBENABLEDAPPLEPROC glIsVertexAttribEnabledAPPLE; + PFNGLMAPVERTEXATTRIB1DAPPLEPROC glMapVertexAttrib1dAPPLE; + PFNGLMAPVERTEXATTRIB1FAPPLEPROC glMapVertexAttrib1fAPPLE; + PFNGLMAPVERTEXATTRIB2DAPPLEPROC glMapVertexAttrib2dAPPLE; + PFNGLMAPVERTEXATTRIB2FAPPLEPROC glMapVertexAttrib2fAPPLE; + PFNGLPRIMITIVEBOUNDINGBOXARBPROC glPrimitiveBoundingBoxARB; + PFNGLGETTEXTUREHANDLEARBPROC glGetTextureHandleARB; + PFNGLGETTEXTURESAMPLERHANDLEARBPROC glGetTextureSamplerHandleARB; + PFNGLMAKETEXTUREHANDLERESIDENTARBPROC glMakeTextureHandleResidentARB; + PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC glMakeTextureHandleNonResidentARB; + PFNGLGETIMAGEHANDLEARBPROC glGetImageHandleARB; + PFNGLMAKEIMAGEHANDLERESIDENTARBPROC glMakeImageHandleResidentARB; + PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC glMakeImageHandleNonResidentARB; + PFNGLUNIFORMHANDLEUI64ARBPROC glUniformHandleui64ARB; + PFNGLUNIFORMHANDLEUI64VARBPROC glUniformHandleui64vARB; + PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC glProgramUniformHandleui64ARB; + PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC glProgramUniformHandleui64vARB; + PFNGLISTEXTUREHANDLERESIDENTARBPROC glIsTextureHandleResidentARB; + PFNGLISIMAGEHANDLERESIDENTARBPROC glIsImageHandleResidentARB; + PFNGLVERTEXATTRIBL1UI64ARBPROC glVertexAttribL1ui64ARB; + PFNGLVERTEXATTRIBL1UI64VARBPROC glVertexAttribL1ui64vARB; + PFNGLGETVERTEXATTRIBLUI64VARBPROC glGetVertexAttribLui64vARB; + PFNGLCREATESYNCFROMCLEVENTARBPROC glCreateSyncFromCLeventARB; + PFNGLCLAMPCOLORARBPROC glClampColorARB; + PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC glDispatchComputeGroupSizeARB; + PFNGLDEBUGMESSAGECONTROLARBPROC glDebugMessageControlARB; + PFNGLDEBUGMESSAGEINSERTARBPROC glDebugMessageInsertARB; + PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARB; + PFNGLGETDEBUGMESSAGELOGARBPROC glGetDebugMessageLogARB; + PFNGLDRAWBUFFERSARBPROC glDrawBuffersARB; + PFNGLBLENDEQUATIONIARBPROC glBlendEquationiARB; + PFNGLBLENDEQUATIONSEPARATEIARBPROC glBlendEquationSeparateiARB; + PFNGLBLENDFUNCIARBPROC glBlendFunciARB; + PFNGLBLENDFUNCSEPARATEIARBPROC glBlendFuncSeparateiARB; + PFNGLDRAWARRAYSINSTANCEDARBPROC glDrawArraysInstancedARB; + PFNGLDRAWELEMENTSINSTANCEDARBPROC glDrawElementsInstancedARB; + PFNGLPROGRAMSTRINGARBPROC glProgramStringARB; + PFNGLBINDPROGRAMARBPROC glBindProgramARB; + PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB; + PFNGLGENPROGRAMSARBPROC glGenProgramsARB; + PFNGLPROGRAMENVPARAMETER4DARBPROC glProgramEnvParameter4dARB; + PFNGLPROGRAMENVPARAMETER4DVARBPROC glProgramEnvParameter4dvARB; + PFNGLPROGRAMENVPARAMETER4FARBPROC glProgramEnvParameter4fARB; + PFNGLPROGRAMENVPARAMETER4FVARBPROC glProgramEnvParameter4fvARB; + PFNGLPROGRAMLOCALPARAMETER4DARBPROC glProgramLocalParameter4dARB; + PFNGLPROGRAMLOCALPARAMETER4DVARBPROC glProgramLocalParameter4dvARB; + PFNGLPROGRAMLOCALPARAMETER4FARBPROC glProgramLocalParameter4fARB; + PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glProgramLocalParameter4fvARB; + PFNGLGETPROGRAMENVPARAMETERDVARBPROC glGetProgramEnvParameterdvARB; + PFNGLGETPROGRAMENVPARAMETERFVARBPROC glGetProgramEnvParameterfvARB; + PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC glGetProgramLocalParameterdvARB; + PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC glGetProgramLocalParameterfvARB; + PFNGLGETPROGRAMIVARBPROC glGetProgramivARB; + PFNGLGETPROGRAMSTRINGARBPROC glGetProgramStringARB; + PFNGLISPROGRAMARBPROC glIsProgramARB; + PFNGLPROGRAMPARAMETERIARBPROC glProgramParameteriARB; + PFNGLFRAMEBUFFERTEXTUREARBPROC glFramebufferTextureARB; + PFNGLFRAMEBUFFERTEXTURELAYERARBPROC glFramebufferTextureLayerARB; + PFNGLFRAMEBUFFERTEXTUREFACEARBPROC glFramebufferTextureFaceARB; + PFNGLSPECIALIZESHADERARBPROC glSpecializeShaderARB; + PFNGLUNIFORM1I64ARBPROC glUniform1i64ARB; + PFNGLUNIFORM2I64ARBPROC glUniform2i64ARB; + PFNGLUNIFORM3I64ARBPROC glUniform3i64ARB; + PFNGLUNIFORM4I64ARBPROC glUniform4i64ARB; + PFNGLUNIFORM1I64VARBPROC glUniform1i64vARB; + PFNGLUNIFORM2I64VARBPROC glUniform2i64vARB; + PFNGLUNIFORM3I64VARBPROC glUniform3i64vARB; + PFNGLUNIFORM4I64VARBPROC glUniform4i64vARB; + PFNGLUNIFORM1UI64ARBPROC glUniform1ui64ARB; + PFNGLUNIFORM2UI64ARBPROC glUniform2ui64ARB; + PFNGLUNIFORM3UI64ARBPROC glUniform3ui64ARB; + PFNGLUNIFORM4UI64ARBPROC glUniform4ui64ARB; + PFNGLUNIFORM1UI64VARBPROC glUniform1ui64vARB; + PFNGLUNIFORM2UI64VARBPROC glUniform2ui64vARB; + PFNGLUNIFORM3UI64VARBPROC glUniform3ui64vARB; + PFNGLUNIFORM4UI64VARBPROC glUniform4ui64vARB; + PFNGLGETUNIFORMI64VARBPROC glGetUniformi64vARB; + PFNGLGETUNIFORMUI64VARBPROC glGetUniformui64vARB; + PFNGLGETNUNIFORMI64VARBPROC glGetnUniformi64vARB; + PFNGLGETNUNIFORMUI64VARBPROC glGetnUniformui64vARB; + PFNGLPROGRAMUNIFORM1I64ARBPROC glProgramUniform1i64ARB; + PFNGLPROGRAMUNIFORM2I64ARBPROC glProgramUniform2i64ARB; + PFNGLPROGRAMUNIFORM3I64ARBPROC glProgramUniform3i64ARB; + PFNGLPROGRAMUNIFORM4I64ARBPROC glProgramUniform4i64ARB; + PFNGLPROGRAMUNIFORM1I64VARBPROC glProgramUniform1i64vARB; + PFNGLPROGRAMUNIFORM2I64VARBPROC glProgramUniform2i64vARB; + PFNGLPROGRAMUNIFORM3I64VARBPROC glProgramUniform3i64vARB; + PFNGLPROGRAMUNIFORM4I64VARBPROC glProgramUniform4i64vARB; + PFNGLPROGRAMUNIFORM1UI64ARBPROC glProgramUniform1ui64ARB; + PFNGLPROGRAMUNIFORM2UI64ARBPROC glProgramUniform2ui64ARB; + PFNGLPROGRAMUNIFORM3UI64ARBPROC glProgramUniform3ui64ARB; + PFNGLPROGRAMUNIFORM4UI64ARBPROC glProgramUniform4ui64ARB; + PFNGLPROGRAMUNIFORM1UI64VARBPROC glProgramUniform1ui64vARB; + PFNGLPROGRAMUNIFORM2UI64VARBPROC glProgramUniform2ui64vARB; + PFNGLPROGRAMUNIFORM3UI64VARBPROC glProgramUniform3ui64vARB; + PFNGLPROGRAMUNIFORM4UI64VARBPROC glProgramUniform4ui64vARB; + PFNGLCOLORTABLEPROC glColorTable; + PFNGLCOLORTABLEPARAMETERFVPROC glColorTableParameterfv; + PFNGLCOLORTABLEPARAMETERIVPROC glColorTableParameteriv; + PFNGLCOPYCOLORTABLEPROC glCopyColorTable; + PFNGLGETCOLORTABLEPROC glGetColorTable; + PFNGLGETCOLORTABLEPARAMETERFVPROC glGetColorTableParameterfv; + PFNGLGETCOLORTABLEPARAMETERIVPROC glGetColorTableParameteriv; + PFNGLCOLORSUBTABLEPROC glColorSubTable; + PFNGLCOPYCOLORSUBTABLEPROC glCopyColorSubTable; + PFNGLCONVOLUTIONFILTER1DPROC glConvolutionFilter1D; + PFNGLCONVOLUTIONFILTER2DPROC glConvolutionFilter2D; + PFNGLCONVOLUTIONPARAMETERFPROC glConvolutionParameterf; + PFNGLCONVOLUTIONPARAMETERFVPROC glConvolutionParameterfv; + PFNGLCONVOLUTIONPARAMETERIPROC glConvolutionParameteri; + PFNGLCONVOLUTIONPARAMETERIVPROC glConvolutionParameteriv; + PFNGLCOPYCONVOLUTIONFILTER1DPROC glCopyConvolutionFilter1D; + PFNGLCOPYCONVOLUTIONFILTER2DPROC glCopyConvolutionFilter2D; + PFNGLGETCONVOLUTIONFILTERPROC glGetConvolutionFilter; + PFNGLGETCONVOLUTIONPARAMETERFVPROC glGetConvolutionParameterfv; + PFNGLGETCONVOLUTIONPARAMETERIVPROC glGetConvolutionParameteriv; + PFNGLGETSEPARABLEFILTERPROC glGetSeparableFilter; + PFNGLSEPARABLEFILTER2DPROC glSeparableFilter2D; + PFNGLGETHISTOGRAMPROC glGetHistogram; + PFNGLGETHISTOGRAMPARAMETERFVPROC glGetHistogramParameterfv; + PFNGLGETHISTOGRAMPARAMETERIVPROC glGetHistogramParameteriv; + PFNGLGETMINMAXPROC glGetMinmax; + PFNGLGETMINMAXPARAMETERFVPROC glGetMinmaxParameterfv; + PFNGLGETMINMAXPARAMETERIVPROC glGetMinmaxParameteriv; + PFNGLHISTOGRAMPROC glHistogram; + PFNGLMINMAXPROC glMinmax; + PFNGLRESETHISTOGRAMPROC glResetHistogram; + PFNGLRESETMINMAXPROC glResetMinmax; + PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC glMultiDrawArraysIndirectCountARB; + PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC glMultiDrawElementsIndirectCountARB; + PFNGLVERTEXATTRIBDIVISORARBPROC glVertexAttribDivisorARB; + PFNGLCURRENTPALETTEMATRIXARBPROC glCurrentPaletteMatrixARB; + PFNGLMATRIXINDEXUBVARBPROC glMatrixIndexubvARB; + PFNGLMATRIXINDEXUSVARBPROC glMatrixIndexusvARB; + PFNGLMATRIXINDEXUIVARBPROC glMatrixIndexuivARB; + PFNGLMATRIXINDEXPOINTERARBPROC glMatrixIndexPointerARB; + PFNGLSAMPLECOVERAGEARBPROC glSampleCoverageARB; + PFNGLACTIVETEXTUREARBPROC glActiveTextureARB; + PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB; + PFNGLMULTITEXCOORD1DARBPROC glMultiTexCoord1dARB; + PFNGLMULTITEXCOORD1DVARBPROC glMultiTexCoord1dvARB; + PFNGLMULTITEXCOORD1FARBPROC glMultiTexCoord1fARB; + PFNGLMULTITEXCOORD1FVARBPROC glMultiTexCoord1fvARB; + PFNGLMULTITEXCOORD1IARBPROC glMultiTexCoord1iARB; + PFNGLMULTITEXCOORD1IVARBPROC glMultiTexCoord1ivARB; + PFNGLMULTITEXCOORD1SARBPROC glMultiTexCoord1sARB; + PFNGLMULTITEXCOORD1SVARBPROC glMultiTexCoord1svARB; + PFNGLMULTITEXCOORD2DARBPROC glMultiTexCoord2dARB; + PFNGLMULTITEXCOORD2DVARBPROC glMultiTexCoord2dvARB; + PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2fARB; + PFNGLMULTITEXCOORD2FVARBPROC glMultiTexCoord2fvARB; + PFNGLMULTITEXCOORD2IARBPROC glMultiTexCoord2iARB; + PFNGLMULTITEXCOORD2IVARBPROC glMultiTexCoord2ivARB; + PFNGLMULTITEXCOORD2SARBPROC glMultiTexCoord2sARB; + PFNGLMULTITEXCOORD2SVARBPROC glMultiTexCoord2svARB; + PFNGLMULTITEXCOORD3DARBPROC glMultiTexCoord3dARB; + PFNGLMULTITEXCOORD3DVARBPROC glMultiTexCoord3dvARB; + PFNGLMULTITEXCOORD3FARBPROC glMultiTexCoord3fARB; + PFNGLMULTITEXCOORD3FVARBPROC glMultiTexCoord3fvARB; + PFNGLMULTITEXCOORD3IARBPROC glMultiTexCoord3iARB; + PFNGLMULTITEXCOORD3IVARBPROC glMultiTexCoord3ivARB; + PFNGLMULTITEXCOORD3SARBPROC glMultiTexCoord3sARB; + PFNGLMULTITEXCOORD3SVARBPROC glMultiTexCoord3svARB; + PFNGLMULTITEXCOORD4DARBPROC glMultiTexCoord4dARB; + PFNGLMULTITEXCOORD4DVARBPROC glMultiTexCoord4dvARB; + PFNGLMULTITEXCOORD4FARBPROC glMultiTexCoord4fARB; + PFNGLMULTITEXCOORD4FVARBPROC glMultiTexCoord4fvARB; + PFNGLMULTITEXCOORD4IARBPROC glMultiTexCoord4iARB; + PFNGLMULTITEXCOORD4IVARBPROC glMultiTexCoord4ivARB; + PFNGLMULTITEXCOORD4SARBPROC glMultiTexCoord4sARB; + PFNGLMULTITEXCOORD4SVARBPROC glMultiTexCoord4svARB; + PFNGLGENQUERIESARBPROC glGenQueriesARB; + PFNGLDELETEQUERIESARBPROC glDeleteQueriesARB; + PFNGLISQUERYARBPROC glIsQueryARB; + PFNGLBEGINQUERYARBPROC glBeginQueryARB; + PFNGLENDQUERYARBPROC glEndQueryARB; + PFNGLGETQUERYIVARBPROC glGetQueryivARB; + PFNGLGETQUERYOBJECTIVARBPROC glGetQueryObjectivARB; + PFNGLGETQUERYOBJECTUIVARBPROC glGetQueryObjectuivARB; + PFNGLMAXSHADERCOMPILERTHREADSARBPROC glMaxShaderCompilerThreadsARB; + PFNGLPOINTPARAMETERFARBPROC glPointParameterfARB; + PFNGLPOINTPARAMETERFVARBPROC glPointParameterfvARB; + PFNGLGETGRAPHICSRESETSTATUSARBPROC glGetGraphicsResetStatusARB; + PFNGLGETNTEXIMAGEARBPROC glGetnTexImageARB; + PFNGLREADNPIXELSARBPROC glReadnPixelsARB; + PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC glGetnCompressedTexImageARB; + PFNGLGETNUNIFORMFVARBPROC glGetnUniformfvARB; + PFNGLGETNUNIFORMIVARBPROC glGetnUniformivARB; + PFNGLGETNUNIFORMUIVARBPROC glGetnUniformuivARB; + PFNGLGETNUNIFORMDVARBPROC glGetnUniformdvARB; + PFNGLGETNMAPDVARBPROC glGetnMapdvARB; + PFNGLGETNMAPFVARBPROC glGetnMapfvARB; + PFNGLGETNMAPIVARBPROC glGetnMapivARB; + PFNGLGETNPIXELMAPFVARBPROC glGetnPixelMapfvARB; + PFNGLGETNPIXELMAPUIVARBPROC glGetnPixelMapuivARB; + PFNGLGETNPIXELMAPUSVARBPROC glGetnPixelMapusvARB; + PFNGLGETNPOLYGONSTIPPLEARBPROC glGetnPolygonStippleARB; + PFNGLGETNCOLORTABLEARBPROC glGetnColorTableARB; + PFNGLGETNCONVOLUTIONFILTERARBPROC glGetnConvolutionFilterARB; + PFNGLGETNSEPARABLEFILTERARBPROC glGetnSeparableFilterARB; + PFNGLGETNHISTOGRAMARBPROC glGetnHistogramARB; + PFNGLGETNMINMAXARBPROC glGetnMinmaxARB; + PFNGLFRAMEBUFFERSAMPLELOCATIONSFVARBPROC glFramebufferSampleLocationsfvARB; + PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVARBPROC glNamedFramebufferSampleLocationsfvARB; + PFNGLEVALUATEDEPTHVALUESARBPROC glEvaluateDepthValuesARB; + PFNGLMINSAMPLESHADINGARBPROC glMinSampleShadingARB; + PFNGLDELETEOBJECTARBPROC glDeleteObjectARB; + PFNGLGETHANDLEARBPROC glGetHandleARB; + PFNGLDETACHOBJECTARBPROC glDetachObjectARB; + PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB; + PFNGLSHADERSOURCEARBPROC glShaderSourceARB; + PFNGLCOMPILESHADERARBPROC glCompileShaderARB; + PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB; + PFNGLATTACHOBJECTARBPROC glAttachObjectARB; + PFNGLLINKPROGRAMARBPROC glLinkProgramARB; + PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB; + PFNGLVALIDATEPROGRAMARBPROC glValidateProgramARB; + PFNGLUNIFORM1FARBPROC glUniform1fARB; + PFNGLUNIFORM2FARBPROC glUniform2fARB; + PFNGLUNIFORM3FARBPROC glUniform3fARB; + PFNGLUNIFORM4FARBPROC glUniform4fARB; + PFNGLUNIFORM1IARBPROC glUniform1iARB; + PFNGLUNIFORM2IARBPROC glUniform2iARB; + PFNGLUNIFORM3IARBPROC glUniform3iARB; + PFNGLUNIFORM4IARBPROC glUniform4iARB; + PFNGLUNIFORM1FVARBPROC glUniform1fvARB; + PFNGLUNIFORM2FVARBPROC glUniform2fvARB; + PFNGLUNIFORM3FVARBPROC glUniform3fvARB; + PFNGLUNIFORM4FVARBPROC glUniform4fvARB; + PFNGLUNIFORM1IVARBPROC glUniform1ivARB; + PFNGLUNIFORM2IVARBPROC glUniform2ivARB; + PFNGLUNIFORM3IVARBPROC glUniform3ivARB; + PFNGLUNIFORM4IVARBPROC glUniform4ivARB; + PFNGLUNIFORMMATRIX2FVARBPROC glUniformMatrix2fvARB; + PFNGLUNIFORMMATRIX3FVARBPROC glUniformMatrix3fvARB; + PFNGLUNIFORMMATRIX4FVARBPROC glUniformMatrix4fvARB; + PFNGLGETOBJECTPARAMETERFVARBPROC glGetObjectParameterfvARB; + PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB; + PFNGLGETINFOLOGARBPROC glGetInfoLogARB; + PFNGLGETATTACHEDOBJECTSARBPROC glGetAttachedObjectsARB; + PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB; + PFNGLGETACTIVEUNIFORMARBPROC glGetActiveUniformARB; + PFNGLGETUNIFORMFVARBPROC glGetUniformfvARB; + PFNGLGETUNIFORMIVARBPROC glGetUniformivARB; + PFNGLGETSHADERSOURCEARBPROC glGetShaderSourceARB; + PFNGLNAMEDSTRINGARBPROC glNamedStringARB; + PFNGLDELETENAMEDSTRINGARBPROC glDeleteNamedStringARB; + PFNGLCOMPILESHADERINCLUDEARBPROC glCompileShaderIncludeARB; + PFNGLISNAMEDSTRINGARBPROC glIsNamedStringARB; + PFNGLGETNAMEDSTRINGARBPROC glGetNamedStringARB; + PFNGLGETNAMEDSTRINGIVARBPROC glGetNamedStringivARB; + PFNGLBUFFERPAGECOMMITMENTARBPROC glBufferPageCommitmentARB; + PFNGLNAMEDBUFFERPAGECOMMITMENTEXTPROC glNamedBufferPageCommitmentEXT; + PFNGLNAMEDBUFFERPAGECOMMITMENTARBPROC glNamedBufferPageCommitmentARB; + PFNGLTEXPAGECOMMITMENTARBPROC glTexPageCommitmentARB; + PFNGLTEXBUFFERARBPROC glTexBufferARB; + PFNGLCOMPRESSEDTEXIMAGE3DARBPROC glCompressedTexImage3DARB; + PFNGLCOMPRESSEDTEXIMAGE2DARBPROC glCompressedTexImage2DARB; + PFNGLCOMPRESSEDTEXIMAGE1DARBPROC glCompressedTexImage1DARB; + PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC glCompressedTexSubImage3DARB; + PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC glCompressedTexSubImage2DARB; + PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC glCompressedTexSubImage1DARB; + PFNGLGETCOMPRESSEDTEXIMAGEARBPROC glGetCompressedTexImageARB; + PFNGLLOADTRANSPOSEMATRIXFARBPROC glLoadTransposeMatrixfARB; + PFNGLLOADTRANSPOSEMATRIXDARBPROC glLoadTransposeMatrixdARB; + PFNGLMULTTRANSPOSEMATRIXFARBPROC glMultTransposeMatrixfARB; + PFNGLMULTTRANSPOSEMATRIXDARBPROC glMultTransposeMatrixdARB; + PFNGLWEIGHTBVARBPROC glWeightbvARB; + PFNGLWEIGHTSVARBPROC glWeightsvARB; + PFNGLWEIGHTIVARBPROC glWeightivARB; + PFNGLWEIGHTFVARBPROC glWeightfvARB; + PFNGLWEIGHTDVARBPROC glWeightdvARB; + PFNGLWEIGHTUBVARBPROC glWeightubvARB; + PFNGLWEIGHTUSVARBPROC glWeightusvARB; + PFNGLWEIGHTUIVARBPROC glWeightuivARB; + PFNGLWEIGHTPOINTERARBPROC glWeightPointerARB; + PFNGLVERTEXBLENDARBPROC glVertexBlendARB; + PFNGLBINDBUFFERARBPROC glBindBufferARB; + PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB; + PFNGLGENBUFFERSARBPROC glGenBuffersARB; + PFNGLISBUFFERARBPROC glIsBufferARB; + PFNGLBUFFERDATAARBPROC glBufferDataARB; + PFNGLBUFFERSUBDATAARBPROC glBufferSubDataARB; + PFNGLGETBUFFERSUBDATAARBPROC glGetBufferSubDataARB; + PFNGLMAPBUFFERARBPROC glMapBufferARB; + PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB; + PFNGLGETBUFFERPARAMETERIVARBPROC glGetBufferParameterivARB; + PFNGLGETBUFFERPOINTERVARBPROC glGetBufferPointervARB; + PFNGLVERTEXATTRIB1DARBPROC glVertexAttrib1dARB; + PFNGLVERTEXATTRIB1DVARBPROC glVertexAttrib1dvARB; + PFNGLVERTEXATTRIB1FARBPROC glVertexAttrib1fARB; + PFNGLVERTEXATTRIB1FVARBPROC glVertexAttrib1fvARB; + PFNGLVERTEXATTRIB1SARBPROC glVertexAttrib1sARB; + PFNGLVERTEXATTRIB1SVARBPROC glVertexAttrib1svARB; + PFNGLVERTEXATTRIB2DARBPROC glVertexAttrib2dARB; + PFNGLVERTEXATTRIB2DVARBPROC glVertexAttrib2dvARB; + PFNGLVERTEXATTRIB2FARBPROC glVertexAttrib2fARB; + PFNGLVERTEXATTRIB2FVARBPROC glVertexAttrib2fvARB; + PFNGLVERTEXATTRIB2SARBPROC glVertexAttrib2sARB; + PFNGLVERTEXATTRIB2SVARBPROC glVertexAttrib2svARB; + PFNGLVERTEXATTRIB3DARBPROC glVertexAttrib3dARB; + PFNGLVERTEXATTRIB3DVARBPROC glVertexAttrib3dvARB; + PFNGLVERTEXATTRIB3FARBPROC glVertexAttrib3fARB; + PFNGLVERTEXATTRIB3FVARBPROC glVertexAttrib3fvARB; + PFNGLVERTEXATTRIB3SARBPROC glVertexAttrib3sARB; + PFNGLVERTEXATTRIB3SVARBPROC glVertexAttrib3svARB; + PFNGLVERTEXATTRIB4NBVARBPROC glVertexAttrib4NbvARB; + PFNGLVERTEXATTRIB4NIVARBPROC glVertexAttrib4NivARB; + PFNGLVERTEXATTRIB4NSVARBPROC glVertexAttrib4NsvARB; + PFNGLVERTEXATTRIB4NUBARBPROC glVertexAttrib4NubARB; + PFNGLVERTEXATTRIB4NUBVARBPROC glVertexAttrib4NubvARB; + PFNGLVERTEXATTRIB4NUIVARBPROC glVertexAttrib4NuivARB; + PFNGLVERTEXATTRIB4NUSVARBPROC glVertexAttrib4NusvARB; + PFNGLVERTEXATTRIB4BVARBPROC glVertexAttrib4bvARB; + PFNGLVERTEXATTRIB4DARBPROC glVertexAttrib4dARB; + PFNGLVERTEXATTRIB4DVARBPROC glVertexAttrib4dvARB; + PFNGLVERTEXATTRIB4FARBPROC glVertexAttrib4fARB; + PFNGLVERTEXATTRIB4FVARBPROC glVertexAttrib4fvARB; + PFNGLVERTEXATTRIB4IVARBPROC glVertexAttrib4ivARB; + PFNGLVERTEXATTRIB4SARBPROC glVertexAttrib4sARB; + PFNGLVERTEXATTRIB4SVARBPROC glVertexAttrib4svARB; + PFNGLVERTEXATTRIB4UBVARBPROC glVertexAttrib4ubvARB; + PFNGLVERTEXATTRIB4UIVARBPROC glVertexAttrib4uivARB; + PFNGLVERTEXATTRIB4USVARBPROC glVertexAttrib4usvARB; + PFNGLVERTEXATTRIBPOINTERARBPROC glVertexAttribPointerARB; + PFNGLENABLEVERTEXATTRIBARRAYARBPROC glEnableVertexAttribArrayARB; + PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glDisableVertexAttribArrayARB; + PFNGLGETVERTEXATTRIBDVARBPROC glGetVertexAttribdvARB; + PFNGLGETVERTEXATTRIBFVARBPROC glGetVertexAttribfvARB; + PFNGLGETVERTEXATTRIBIVARBPROC glGetVertexAttribivARB; + PFNGLGETVERTEXATTRIBPOINTERVARBPROC glGetVertexAttribPointervARB; + PFNGLBINDATTRIBLOCATIONARBPROC glBindAttribLocationARB; + PFNGLGETACTIVEATTRIBARBPROC glGetActiveAttribARB; + PFNGLGETATTRIBLOCATIONARBPROC glGetAttribLocationARB; + PFNGLDEPTHRANGEARRAYDVNVPROC glDepthRangeArraydvNV; + PFNGLDEPTHRANGEINDEXEDDNVPROC glDepthRangeIndexeddNV; + PFNGLWINDOWPOS2DARBPROC glWindowPos2dARB; + PFNGLWINDOWPOS2DVARBPROC glWindowPos2dvARB; + PFNGLWINDOWPOS2FARBPROC glWindowPos2fARB; + PFNGLWINDOWPOS2FVARBPROC glWindowPos2fvARB; + PFNGLWINDOWPOS2IARBPROC glWindowPos2iARB; + PFNGLWINDOWPOS2IVARBPROC glWindowPos2ivARB; + PFNGLWINDOWPOS2SARBPROC glWindowPos2sARB; + PFNGLWINDOWPOS2SVARBPROC glWindowPos2svARB; + PFNGLWINDOWPOS3DARBPROC glWindowPos3dARB; + PFNGLWINDOWPOS3DVARBPROC glWindowPos3dvARB; + PFNGLWINDOWPOS3FARBPROC glWindowPos3fARB; + PFNGLWINDOWPOS3FVARBPROC glWindowPos3fvARB; + PFNGLWINDOWPOS3IARBPROC glWindowPos3iARB; + PFNGLWINDOWPOS3IVARBPROC glWindowPos3ivARB; + PFNGLWINDOWPOS3SARBPROC glWindowPos3sARB; + PFNGLWINDOWPOS3SVARBPROC glWindowPos3svARB; + PFNGLDRAWBUFFERSATIPROC glDrawBuffersATI; + PFNGLELEMENTPOINTERATIPROC glElementPointerATI; + PFNGLDRAWELEMENTARRAYATIPROC glDrawElementArrayATI; + PFNGLDRAWRANGEELEMENTARRAYATIPROC glDrawRangeElementArrayATI; + PFNGLTEXBUMPPARAMETERIVATIPROC glTexBumpParameterivATI; + PFNGLTEXBUMPPARAMETERFVATIPROC glTexBumpParameterfvATI; + PFNGLGETTEXBUMPPARAMETERIVATIPROC glGetTexBumpParameterivATI; + PFNGLGETTEXBUMPPARAMETERFVATIPROC glGetTexBumpParameterfvATI; + PFNGLGENFRAGMENTSHADERSATIPROC glGenFragmentShadersATI; + PFNGLBINDFRAGMENTSHADERATIPROC glBindFragmentShaderATI; + PFNGLDELETEFRAGMENTSHADERATIPROC glDeleteFragmentShaderATI; + PFNGLBEGINFRAGMENTSHADERATIPROC glBeginFragmentShaderATI; + PFNGLENDFRAGMENTSHADERATIPROC glEndFragmentShaderATI; + PFNGLPASSTEXCOORDATIPROC glPassTexCoordATI; + PFNGLSAMPLEMAPATIPROC glSampleMapATI; + PFNGLCOLORFRAGMENTOP1ATIPROC glColorFragmentOp1ATI; + PFNGLCOLORFRAGMENTOP2ATIPROC glColorFragmentOp2ATI; + PFNGLCOLORFRAGMENTOP3ATIPROC glColorFragmentOp3ATI; + PFNGLALPHAFRAGMENTOP1ATIPROC glAlphaFragmentOp1ATI; + PFNGLALPHAFRAGMENTOP2ATIPROC glAlphaFragmentOp2ATI; + PFNGLALPHAFRAGMENTOP3ATIPROC glAlphaFragmentOp3ATI; + PFNGLSETFRAGMENTSHADERCONSTANTATIPROC glSetFragmentShaderConstantATI; + PFNGLMAPOBJECTBUFFERATIPROC glMapObjectBufferATI; + PFNGLUNMAPOBJECTBUFFERATIPROC glUnmapObjectBufferATI; + PFNGLPNTRIANGLESIATIPROC glPNTrianglesiATI; + PFNGLPNTRIANGLESFATIPROC glPNTrianglesfATI; + PFNGLSTENCILOPSEPARATEATIPROC glStencilOpSeparateATI; + PFNGLSTENCILFUNCSEPARATEATIPROC glStencilFuncSeparateATI; + PFNGLNEWOBJECTBUFFERATIPROC glNewObjectBufferATI; + PFNGLISOBJECTBUFFERATIPROC glIsObjectBufferATI; + PFNGLUPDATEOBJECTBUFFERATIPROC glUpdateObjectBufferATI; + PFNGLGETOBJECTBUFFERFVATIPROC glGetObjectBufferfvATI; + PFNGLGETOBJECTBUFFERIVATIPROC glGetObjectBufferivATI; + PFNGLFREEOBJECTBUFFERATIPROC glFreeObjectBufferATI; + PFNGLARRAYOBJECTATIPROC glArrayObjectATI; + PFNGLGETARRAYOBJECTFVATIPROC glGetArrayObjectfvATI; + PFNGLGETARRAYOBJECTIVATIPROC glGetArrayObjectivATI; + PFNGLVARIANTARRAYOBJECTATIPROC glVariantArrayObjectATI; + PFNGLGETVARIANTARRAYOBJECTFVATIPROC glGetVariantArrayObjectfvATI; + PFNGLGETVARIANTARRAYOBJECTIVATIPROC glGetVariantArrayObjectivATI; + PFNGLVERTEXATTRIBARRAYOBJECTATIPROC glVertexAttribArrayObjectATI; + PFNGLGETVERTEXATTRIBARRAYOBJECTFVATIPROC glGetVertexAttribArrayObjectfvATI; + PFNGLGETVERTEXATTRIBARRAYOBJECTIVATIPROC glGetVertexAttribArrayObjectivATI; + PFNGLVERTEXSTREAM1SATIPROC glVertexStream1sATI; + PFNGLVERTEXSTREAM1SVATIPROC glVertexStream1svATI; + PFNGLVERTEXSTREAM1IATIPROC glVertexStream1iATI; + PFNGLVERTEXSTREAM1IVATIPROC glVertexStream1ivATI; + PFNGLVERTEXSTREAM1FATIPROC glVertexStream1fATI; + PFNGLVERTEXSTREAM1FVATIPROC glVertexStream1fvATI; + PFNGLVERTEXSTREAM1DATIPROC glVertexStream1dATI; + PFNGLVERTEXSTREAM1DVATIPROC glVertexStream1dvATI; + PFNGLVERTEXSTREAM2SATIPROC glVertexStream2sATI; + PFNGLVERTEXSTREAM2SVATIPROC glVertexStream2svATI; + PFNGLVERTEXSTREAM2IATIPROC glVertexStream2iATI; + PFNGLVERTEXSTREAM2IVATIPROC glVertexStream2ivATI; + PFNGLVERTEXSTREAM2FATIPROC glVertexStream2fATI; + PFNGLVERTEXSTREAM2FVATIPROC glVertexStream2fvATI; + PFNGLVERTEXSTREAM2DATIPROC glVertexStream2dATI; + PFNGLVERTEXSTREAM2DVATIPROC glVertexStream2dvATI; + PFNGLVERTEXSTREAM3SATIPROC glVertexStream3sATI; + PFNGLVERTEXSTREAM3SVATIPROC glVertexStream3svATI; + PFNGLVERTEXSTREAM3IATIPROC glVertexStream3iATI; + PFNGLVERTEXSTREAM3IVATIPROC glVertexStream3ivATI; + PFNGLVERTEXSTREAM3FATIPROC glVertexStream3fATI; + PFNGLVERTEXSTREAM3FVATIPROC glVertexStream3fvATI; + PFNGLVERTEXSTREAM3DATIPROC glVertexStream3dATI; + PFNGLVERTEXSTREAM3DVATIPROC glVertexStream3dvATI; + PFNGLVERTEXSTREAM4SATIPROC glVertexStream4sATI; + PFNGLVERTEXSTREAM4SVATIPROC glVertexStream4svATI; + PFNGLVERTEXSTREAM4IATIPROC glVertexStream4iATI; + PFNGLVERTEXSTREAM4IVATIPROC glVertexStream4ivATI; + PFNGLVERTEXSTREAM4FATIPROC glVertexStream4fATI; + PFNGLVERTEXSTREAM4FVATIPROC glVertexStream4fvATI; + PFNGLVERTEXSTREAM4DATIPROC glVertexStream4dATI; + PFNGLVERTEXSTREAM4DVATIPROC glVertexStream4dvATI; + PFNGLNORMALSTREAM3BATIPROC glNormalStream3bATI; + PFNGLNORMALSTREAM3BVATIPROC glNormalStream3bvATI; + PFNGLNORMALSTREAM3SATIPROC glNormalStream3sATI; + PFNGLNORMALSTREAM3SVATIPROC glNormalStream3svATI; + PFNGLNORMALSTREAM3IATIPROC glNormalStream3iATI; + PFNGLNORMALSTREAM3IVATIPROC glNormalStream3ivATI; + PFNGLNORMALSTREAM3FATIPROC glNormalStream3fATI; + PFNGLNORMALSTREAM3FVATIPROC glNormalStream3fvATI; + PFNGLNORMALSTREAM3DATIPROC glNormalStream3dATI; + PFNGLNORMALSTREAM3DVATIPROC glNormalStream3dvATI; + PFNGLCLIENTACTIVEVERTEXSTREAMATIPROC glClientActiveVertexStreamATI; + PFNGLVERTEXBLENDENVIATIPROC glVertexBlendEnviATI; + PFNGLVERTEXBLENDENVFATIPROC glVertexBlendEnvfATI; + PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC glEGLImageTargetTexStorageEXT; + PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC glEGLImageTargetTextureStorageEXT; + PFNGLUNIFORMBUFFEREXTPROC glUniformBufferEXT; + PFNGLGETUNIFORMBUFFERSIZEEXTPROC glGetUniformBufferSizeEXT; + PFNGLGETUNIFORMOFFSETEXTPROC glGetUniformOffsetEXT; + PFNGLBLENDCOLOREXTPROC glBlendColorEXT; + PFNGLBLENDEQUATIONSEPARATEEXTPROC glBlendEquationSeparateEXT; + PFNGLBLENDFUNCSEPARATEEXTPROC glBlendFuncSeparateEXT; + PFNGLBLENDEQUATIONEXTPROC glBlendEquationEXT; + PFNGLCOLORSUBTABLEEXTPROC glColorSubTableEXT; + PFNGLCOPYCOLORSUBTABLEEXTPROC glCopyColorSubTableEXT; + PFNGLLOCKARRAYSEXTPROC glLockArraysEXT; + PFNGLUNLOCKARRAYSEXTPROC glUnlockArraysEXT; + PFNGLCONVOLUTIONFILTER1DEXTPROC glConvolutionFilter1DEXT; + PFNGLCONVOLUTIONFILTER2DEXTPROC glConvolutionFilter2DEXT; + PFNGLCONVOLUTIONPARAMETERFEXTPROC glConvolutionParameterfEXT; + PFNGLCONVOLUTIONPARAMETERFVEXTPROC glConvolutionParameterfvEXT; + PFNGLCONVOLUTIONPARAMETERIEXTPROC glConvolutionParameteriEXT; + PFNGLCONVOLUTIONPARAMETERIVEXTPROC glConvolutionParameterivEXT; + PFNGLCOPYCONVOLUTIONFILTER1DEXTPROC glCopyConvolutionFilter1DEXT; + PFNGLCOPYCONVOLUTIONFILTER2DEXTPROC glCopyConvolutionFilter2DEXT; + PFNGLGETCONVOLUTIONFILTEREXTPROC glGetConvolutionFilterEXT; + PFNGLGETCONVOLUTIONPARAMETERFVEXTPROC glGetConvolutionParameterfvEXT; + PFNGLGETCONVOLUTIONPARAMETERIVEXTPROC glGetConvolutionParameterivEXT; + PFNGLGETSEPARABLEFILTEREXTPROC glGetSeparableFilterEXT; + PFNGLSEPARABLEFILTER2DEXTPROC glSeparableFilter2DEXT; + PFNGLTANGENT3BEXTPROC glTangent3bEXT; + PFNGLTANGENT3BVEXTPROC glTangent3bvEXT; + PFNGLTANGENT3DEXTPROC glTangent3dEXT; + PFNGLTANGENT3DVEXTPROC glTangent3dvEXT; + PFNGLTANGENT3FEXTPROC glTangent3fEXT; + PFNGLTANGENT3FVEXTPROC glTangent3fvEXT; + PFNGLTANGENT3IEXTPROC glTangent3iEXT; + PFNGLTANGENT3IVEXTPROC glTangent3ivEXT; + PFNGLTANGENT3SEXTPROC glTangent3sEXT; + PFNGLTANGENT3SVEXTPROC glTangent3svEXT; + PFNGLBINORMAL3BEXTPROC glBinormal3bEXT; + PFNGLBINORMAL3BVEXTPROC glBinormal3bvEXT; + PFNGLBINORMAL3DEXTPROC glBinormal3dEXT; + PFNGLBINORMAL3DVEXTPROC glBinormal3dvEXT; + PFNGLBINORMAL3FEXTPROC glBinormal3fEXT; + PFNGLBINORMAL3FVEXTPROC glBinormal3fvEXT; + PFNGLBINORMAL3IEXTPROC glBinormal3iEXT; + PFNGLBINORMAL3IVEXTPROC glBinormal3ivEXT; + PFNGLBINORMAL3SEXTPROC glBinormal3sEXT; + PFNGLBINORMAL3SVEXTPROC glBinormal3svEXT; + PFNGLTANGENTPOINTEREXTPROC glTangentPointerEXT; + PFNGLBINORMALPOINTEREXTPROC glBinormalPointerEXT; + PFNGLCOPYTEXIMAGE1DEXTPROC glCopyTexImage1DEXT; + PFNGLCOPYTEXIMAGE2DEXTPROC glCopyTexImage2DEXT; + PFNGLCOPYTEXSUBIMAGE1DEXTPROC glCopyTexSubImage1DEXT; + PFNGLCOPYTEXSUBIMAGE2DEXTPROC glCopyTexSubImage2DEXT; + PFNGLCOPYTEXSUBIMAGE3DEXTPROC glCopyTexSubImage3DEXT; + PFNGLCULLPARAMETERDVEXTPROC glCullParameterdvEXT; + PFNGLCULLPARAMETERFVEXTPROC glCullParameterfvEXT; + PFNGLLABELOBJECTEXTPROC glLabelObjectEXT; + PFNGLGETOBJECTLABELEXTPROC glGetObjectLabelEXT; + PFNGLINSERTEVENTMARKEREXTPROC glInsertEventMarkerEXT; + PFNGLPUSHGROUPMARKEREXTPROC glPushGroupMarkerEXT; + PFNGLPOPGROUPMARKEREXTPROC glPopGroupMarkerEXT; + PFNGLDEPTHBOUNDSEXTPROC glDepthBoundsEXT; + PFNGLMATRIXLOADFEXTPROC glMatrixLoadfEXT; + PFNGLMATRIXLOADDEXTPROC glMatrixLoaddEXT; + PFNGLMATRIXMULTFEXTPROC glMatrixMultfEXT; + PFNGLMATRIXMULTDEXTPROC glMatrixMultdEXT; + PFNGLMATRIXLOADIDENTITYEXTPROC glMatrixLoadIdentityEXT; + PFNGLMATRIXROTATEFEXTPROC glMatrixRotatefEXT; + PFNGLMATRIXROTATEDEXTPROC glMatrixRotatedEXT; + PFNGLMATRIXSCALEFEXTPROC glMatrixScalefEXT; + PFNGLMATRIXSCALEDEXTPROC glMatrixScaledEXT; + PFNGLMATRIXTRANSLATEFEXTPROC glMatrixTranslatefEXT; + PFNGLMATRIXTRANSLATEDEXTPROC glMatrixTranslatedEXT; + PFNGLMATRIXFRUSTUMEXTPROC glMatrixFrustumEXT; + PFNGLMATRIXORTHOEXTPROC glMatrixOrthoEXT; + PFNGLMATRIXPOPEXTPROC glMatrixPopEXT; + PFNGLMATRIXPUSHEXTPROC glMatrixPushEXT; + PFNGLCLIENTATTRIBDEFAULTEXTPROC glClientAttribDefaultEXT; + PFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC glPushClientAttribDefaultEXT; + PFNGLTEXTUREPARAMETERFEXTPROC glTextureParameterfEXT; + PFNGLTEXTUREPARAMETERFVEXTPROC glTextureParameterfvEXT; + PFNGLTEXTUREPARAMETERIEXTPROC glTextureParameteriEXT; + PFNGLTEXTUREPARAMETERIVEXTPROC glTextureParameterivEXT; + PFNGLTEXTUREIMAGE1DEXTPROC glTextureImage1DEXT; + PFNGLTEXTUREIMAGE2DEXTPROC glTextureImage2DEXT; + PFNGLTEXTURESUBIMAGE1DEXTPROC glTextureSubImage1DEXT; + PFNGLTEXTURESUBIMAGE2DEXTPROC glTextureSubImage2DEXT; + PFNGLCOPYTEXTUREIMAGE1DEXTPROC glCopyTextureImage1DEXT; + PFNGLCOPYTEXTUREIMAGE2DEXTPROC glCopyTextureImage2DEXT; + PFNGLCOPYTEXTURESUBIMAGE1DEXTPROC glCopyTextureSubImage1DEXT; + PFNGLCOPYTEXTURESUBIMAGE2DEXTPROC glCopyTextureSubImage2DEXT; + PFNGLGETTEXTUREIMAGEEXTPROC glGetTextureImageEXT; + PFNGLGETTEXTUREPARAMETERFVEXTPROC glGetTextureParameterfvEXT; + PFNGLGETTEXTUREPARAMETERIVEXTPROC glGetTextureParameterivEXT; + PFNGLGETTEXTURELEVELPARAMETERFVEXTPROC glGetTextureLevelParameterfvEXT; + PFNGLGETTEXTURELEVELPARAMETERIVEXTPROC glGetTextureLevelParameterivEXT; + PFNGLTEXTUREIMAGE3DEXTPROC glTextureImage3DEXT; + PFNGLTEXTURESUBIMAGE3DEXTPROC glTextureSubImage3DEXT; + PFNGLCOPYTEXTURESUBIMAGE3DEXTPROC glCopyTextureSubImage3DEXT; + PFNGLBINDMULTITEXTUREEXTPROC glBindMultiTextureEXT; + PFNGLMULTITEXCOORDPOINTEREXTPROC glMultiTexCoordPointerEXT; + PFNGLMULTITEXENVFEXTPROC glMultiTexEnvfEXT; + PFNGLMULTITEXENVFVEXTPROC glMultiTexEnvfvEXT; + PFNGLMULTITEXENVIEXTPROC glMultiTexEnviEXT; + PFNGLMULTITEXENVIVEXTPROC glMultiTexEnvivEXT; + PFNGLMULTITEXGENDEXTPROC glMultiTexGendEXT; + PFNGLMULTITEXGENDVEXTPROC glMultiTexGendvEXT; + PFNGLMULTITEXGENFEXTPROC glMultiTexGenfEXT; + PFNGLMULTITEXGENFVEXTPROC glMultiTexGenfvEXT; + PFNGLMULTITEXGENIEXTPROC glMultiTexGeniEXT; + PFNGLMULTITEXGENIVEXTPROC glMultiTexGenivEXT; + PFNGLGETMULTITEXENVFVEXTPROC glGetMultiTexEnvfvEXT; + PFNGLGETMULTITEXENVIVEXTPROC glGetMultiTexEnvivEXT; + PFNGLGETMULTITEXGENDVEXTPROC glGetMultiTexGendvEXT; + PFNGLGETMULTITEXGENFVEXTPROC glGetMultiTexGenfvEXT; + PFNGLGETMULTITEXGENIVEXTPROC glGetMultiTexGenivEXT; + PFNGLMULTITEXPARAMETERIEXTPROC glMultiTexParameteriEXT; + PFNGLMULTITEXPARAMETERIVEXTPROC glMultiTexParameterivEXT; + PFNGLMULTITEXPARAMETERFEXTPROC glMultiTexParameterfEXT; + PFNGLMULTITEXPARAMETERFVEXTPROC glMultiTexParameterfvEXT; + PFNGLMULTITEXIMAGE1DEXTPROC glMultiTexImage1DEXT; + PFNGLMULTITEXIMAGE2DEXTPROC glMultiTexImage2DEXT; + PFNGLMULTITEXSUBIMAGE1DEXTPROC glMultiTexSubImage1DEXT; + PFNGLMULTITEXSUBIMAGE2DEXTPROC glMultiTexSubImage2DEXT; + PFNGLCOPYMULTITEXIMAGE1DEXTPROC glCopyMultiTexImage1DEXT; + PFNGLCOPYMULTITEXIMAGE2DEXTPROC glCopyMultiTexImage2DEXT; + PFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC glCopyMultiTexSubImage1DEXT; + PFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC glCopyMultiTexSubImage2DEXT; + PFNGLGETMULTITEXIMAGEEXTPROC glGetMultiTexImageEXT; + PFNGLGETMULTITEXPARAMETERFVEXTPROC glGetMultiTexParameterfvEXT; + PFNGLGETMULTITEXPARAMETERIVEXTPROC glGetMultiTexParameterivEXT; + PFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC glGetMultiTexLevelParameterfvEXT; + PFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC glGetMultiTexLevelParameterivEXT; + PFNGLMULTITEXIMAGE3DEXTPROC glMultiTexImage3DEXT; + PFNGLMULTITEXSUBIMAGE3DEXTPROC glMultiTexSubImage3DEXT; + PFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC glCopyMultiTexSubImage3DEXT; + PFNGLENABLECLIENTSTATEINDEXEDEXTPROC glEnableClientStateIndexedEXT; + PFNGLDISABLECLIENTSTATEINDEXEDEXTPROC glDisableClientStateIndexedEXT; + PFNGLGETFLOATINDEXEDVEXTPROC glGetFloatIndexedvEXT; + PFNGLGETDOUBLEINDEXEDVEXTPROC glGetDoubleIndexedvEXT; + PFNGLGETPOINTERINDEXEDVEXTPROC glGetPointerIndexedvEXT; + PFNGLENABLEINDEXEDEXTPROC glEnableIndexedEXT; + PFNGLDISABLEINDEXEDEXTPROC glDisableIndexedEXT; + PFNGLISENABLEDINDEXEDEXTPROC glIsEnabledIndexedEXT; + PFNGLGETINTEGERINDEXEDVEXTPROC glGetIntegerIndexedvEXT; + PFNGLGETBOOLEANINDEXEDVEXTPROC glGetBooleanIndexedvEXT; + PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC glCompressedTextureImage3DEXT; + PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC glCompressedTextureImage2DEXT; + PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC glCompressedTextureImage1DEXT; + PFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC glCompressedTextureSubImage3DEXT; + PFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC glCompressedTextureSubImage2DEXT; + PFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC glCompressedTextureSubImage1DEXT; + PFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC glGetCompressedTextureImageEXT; + PFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC glCompressedMultiTexImage3DEXT; + PFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC glCompressedMultiTexImage2DEXT; + PFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC glCompressedMultiTexImage1DEXT; + PFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC glCompressedMultiTexSubImage3DEXT; + PFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC glCompressedMultiTexSubImage2DEXT; + PFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC glCompressedMultiTexSubImage1DEXT; + PFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC glGetCompressedMultiTexImageEXT; + PFNGLMATRIXLOADTRANSPOSEFEXTPROC glMatrixLoadTransposefEXT; + PFNGLMATRIXLOADTRANSPOSEDEXTPROC glMatrixLoadTransposedEXT; + PFNGLMATRIXMULTTRANSPOSEFEXTPROC glMatrixMultTransposefEXT; + PFNGLMATRIXMULTTRANSPOSEDEXTPROC glMatrixMultTransposedEXT; + PFNGLNAMEDBUFFERDATAEXTPROC glNamedBufferDataEXT; + PFNGLNAMEDBUFFERSUBDATAEXTPROC glNamedBufferSubDataEXT; + PFNGLMAPNAMEDBUFFEREXTPROC glMapNamedBufferEXT; + PFNGLUNMAPNAMEDBUFFEREXTPROC glUnmapNamedBufferEXT; + PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC glGetNamedBufferParameterivEXT; + PFNGLGETNAMEDBUFFERPOINTERVEXTPROC glGetNamedBufferPointervEXT; + PFNGLGETNAMEDBUFFERSUBDATAEXTPROC glGetNamedBufferSubDataEXT; + PFNGLPROGRAMUNIFORM1FEXTPROC glProgramUniform1fEXT; + PFNGLPROGRAMUNIFORM2FEXTPROC glProgramUniform2fEXT; + PFNGLPROGRAMUNIFORM3FEXTPROC glProgramUniform3fEXT; + PFNGLPROGRAMUNIFORM4FEXTPROC glProgramUniform4fEXT; + PFNGLPROGRAMUNIFORM1IEXTPROC glProgramUniform1iEXT; + PFNGLPROGRAMUNIFORM2IEXTPROC glProgramUniform2iEXT; + PFNGLPROGRAMUNIFORM3IEXTPROC glProgramUniform3iEXT; + PFNGLPROGRAMUNIFORM4IEXTPROC glProgramUniform4iEXT; + PFNGLPROGRAMUNIFORM1FVEXTPROC glProgramUniform1fvEXT; + PFNGLPROGRAMUNIFORM2FVEXTPROC glProgramUniform2fvEXT; + PFNGLPROGRAMUNIFORM3FVEXTPROC glProgramUniform3fvEXT; + PFNGLPROGRAMUNIFORM4FVEXTPROC glProgramUniform4fvEXT; + PFNGLPROGRAMUNIFORM1IVEXTPROC glProgramUniform1ivEXT; + PFNGLPROGRAMUNIFORM2IVEXTPROC glProgramUniform2ivEXT; + PFNGLPROGRAMUNIFORM3IVEXTPROC glProgramUniform3ivEXT; + PFNGLPROGRAMUNIFORM4IVEXTPROC glProgramUniform4ivEXT; + PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC glProgramUniformMatrix2fvEXT; + PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC glProgramUniformMatrix3fvEXT; + PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC glProgramUniformMatrix4fvEXT; + PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC glProgramUniformMatrix2x3fvEXT; + PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC glProgramUniformMatrix3x2fvEXT; + PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC glProgramUniformMatrix2x4fvEXT; + PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC glProgramUniformMatrix4x2fvEXT; + PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC glProgramUniformMatrix3x4fvEXT; + PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC glProgramUniformMatrix4x3fvEXT; + PFNGLTEXTUREBUFFEREXTPROC glTextureBufferEXT; + PFNGLMULTITEXBUFFEREXTPROC glMultiTexBufferEXT; + PFNGLTEXTUREPARAMETERIIVEXTPROC glTextureParameterIivEXT; + PFNGLTEXTUREPARAMETERIUIVEXTPROC glTextureParameterIuivEXT; + PFNGLGETTEXTUREPARAMETERIIVEXTPROC glGetTextureParameterIivEXT; + PFNGLGETTEXTUREPARAMETERIUIVEXTPROC glGetTextureParameterIuivEXT; + PFNGLMULTITEXPARAMETERIIVEXTPROC glMultiTexParameterIivEXT; + PFNGLMULTITEXPARAMETERIUIVEXTPROC glMultiTexParameterIuivEXT; + PFNGLGETMULTITEXPARAMETERIIVEXTPROC glGetMultiTexParameterIivEXT; + PFNGLGETMULTITEXPARAMETERIUIVEXTPROC glGetMultiTexParameterIuivEXT; + PFNGLPROGRAMUNIFORM1UIEXTPROC glProgramUniform1uiEXT; + PFNGLPROGRAMUNIFORM2UIEXTPROC glProgramUniform2uiEXT; + PFNGLPROGRAMUNIFORM3UIEXTPROC glProgramUniform3uiEXT; + PFNGLPROGRAMUNIFORM4UIEXTPROC glProgramUniform4uiEXT; + PFNGLPROGRAMUNIFORM1UIVEXTPROC glProgramUniform1uivEXT; + PFNGLPROGRAMUNIFORM2UIVEXTPROC glProgramUniform2uivEXT; + PFNGLPROGRAMUNIFORM3UIVEXTPROC glProgramUniform3uivEXT; + PFNGLPROGRAMUNIFORM4UIVEXTPROC glProgramUniform4uivEXT; + PFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC glNamedProgramLocalParameters4fvEXT; + PFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC glNamedProgramLocalParameterI4iEXT; + PFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC glNamedProgramLocalParameterI4ivEXT; + PFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC glNamedProgramLocalParametersI4ivEXT; + PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC glNamedProgramLocalParameterI4uiEXT; + PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC glNamedProgramLocalParameterI4uivEXT; + PFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC glNamedProgramLocalParametersI4uivEXT; + PFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC glGetNamedProgramLocalParameterIivEXT; + PFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC glGetNamedProgramLocalParameterIuivEXT; + PFNGLENABLECLIENTSTATEIEXTPROC glEnableClientStateiEXT; + PFNGLDISABLECLIENTSTATEIEXTPROC glDisableClientStateiEXT; + PFNGLGETFLOATI_VEXTPROC glGetFloati_vEXT; + PFNGLGETDOUBLEI_VEXTPROC glGetDoublei_vEXT; + PFNGLGETPOINTERI_VEXTPROC glGetPointeri_vEXT; + PFNGLNAMEDPROGRAMSTRINGEXTPROC glNamedProgramStringEXT; + PFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC glNamedProgramLocalParameter4dEXT; + PFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC glNamedProgramLocalParameter4dvEXT; + PFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC glNamedProgramLocalParameter4fEXT; + PFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC glNamedProgramLocalParameter4fvEXT; + PFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC glGetNamedProgramLocalParameterdvEXT; + PFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC glGetNamedProgramLocalParameterfvEXT; + PFNGLGETNAMEDPROGRAMIVEXTPROC glGetNamedProgramivEXT; + PFNGLGETNAMEDPROGRAMSTRINGEXTPROC glGetNamedProgramStringEXT; + PFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC glNamedRenderbufferStorageEXT; + PFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC glGetNamedRenderbufferParameterivEXT; + PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glNamedRenderbufferStorageMultisampleEXT; + PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC glNamedRenderbufferStorageMultisampleCoverageEXT; + PFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC glCheckNamedFramebufferStatusEXT; + PFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC glNamedFramebufferTexture1DEXT; + PFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC glNamedFramebufferTexture2DEXT; + PFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC glNamedFramebufferTexture3DEXT; + PFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC glNamedFramebufferRenderbufferEXT; + PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glGetNamedFramebufferAttachmentParameterivEXT; + PFNGLGENERATETEXTUREMIPMAPEXTPROC glGenerateTextureMipmapEXT; + PFNGLGENERATEMULTITEXMIPMAPEXTPROC glGenerateMultiTexMipmapEXT; + PFNGLFRAMEBUFFERDRAWBUFFEREXTPROC glFramebufferDrawBufferEXT; + PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC glFramebufferDrawBuffersEXT; + PFNGLFRAMEBUFFERREADBUFFEREXTPROC glFramebufferReadBufferEXT; + PFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC glGetFramebufferParameterivEXT; + PFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC glNamedCopyBufferSubDataEXT; + PFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC glNamedFramebufferTextureEXT; + PFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC glNamedFramebufferTextureLayerEXT; + PFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC glNamedFramebufferTextureFaceEXT; + PFNGLTEXTURERENDERBUFFEREXTPROC glTextureRenderbufferEXT; + PFNGLMULTITEXRENDERBUFFEREXTPROC glMultiTexRenderbufferEXT; + PFNGLVERTEXARRAYVERTEXOFFSETEXTPROC glVertexArrayVertexOffsetEXT; + PFNGLVERTEXARRAYCOLOROFFSETEXTPROC glVertexArrayColorOffsetEXT; + PFNGLVERTEXARRAYEDGEFLAGOFFSETEXTPROC glVertexArrayEdgeFlagOffsetEXT; + PFNGLVERTEXARRAYINDEXOFFSETEXTPROC glVertexArrayIndexOffsetEXT; + PFNGLVERTEXARRAYNORMALOFFSETEXTPROC glVertexArrayNormalOffsetEXT; + PFNGLVERTEXARRAYTEXCOORDOFFSETEXTPROC glVertexArrayTexCoordOffsetEXT; + PFNGLVERTEXARRAYMULTITEXCOORDOFFSETEXTPROC glVertexArrayMultiTexCoordOffsetEXT; + PFNGLVERTEXARRAYFOGCOORDOFFSETEXTPROC glVertexArrayFogCoordOffsetEXT; + PFNGLVERTEXARRAYSECONDARYCOLOROFFSETEXTPROC glVertexArraySecondaryColorOffsetEXT; + PFNGLVERTEXARRAYVERTEXATTRIBOFFSETEXTPROC glVertexArrayVertexAttribOffsetEXT; + PFNGLVERTEXARRAYVERTEXATTRIBIOFFSETEXTPROC glVertexArrayVertexAttribIOffsetEXT; + PFNGLENABLEVERTEXARRAYEXTPROC glEnableVertexArrayEXT; + PFNGLDISABLEVERTEXARRAYEXTPROC glDisableVertexArrayEXT; + PFNGLENABLEVERTEXARRAYATTRIBEXTPROC glEnableVertexArrayAttribEXT; + PFNGLDISABLEVERTEXARRAYATTRIBEXTPROC glDisableVertexArrayAttribEXT; + PFNGLGETVERTEXARRAYINTEGERVEXTPROC glGetVertexArrayIntegervEXT; + PFNGLGETVERTEXARRAYPOINTERVEXTPROC glGetVertexArrayPointervEXT; + PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC glGetVertexArrayIntegeri_vEXT; + PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC glGetVertexArrayPointeri_vEXT; + PFNGLMAPNAMEDBUFFERRANGEEXTPROC glMapNamedBufferRangeEXT; + PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC glFlushMappedNamedBufferRangeEXT; + PFNGLNAMEDBUFFERSTORAGEEXTPROC glNamedBufferStorageEXT; + PFNGLCLEARNAMEDBUFFERDATAEXTPROC glClearNamedBufferDataEXT; + PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC glClearNamedBufferSubDataEXT; + PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC glNamedFramebufferParameteriEXT; + PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC glGetNamedFramebufferParameterivEXT; + PFNGLPROGRAMUNIFORM1DEXTPROC glProgramUniform1dEXT; + PFNGLPROGRAMUNIFORM2DEXTPROC glProgramUniform2dEXT; + PFNGLPROGRAMUNIFORM3DEXTPROC glProgramUniform3dEXT; + PFNGLPROGRAMUNIFORM4DEXTPROC glProgramUniform4dEXT; + PFNGLPROGRAMUNIFORM1DVEXTPROC glProgramUniform1dvEXT; + PFNGLPROGRAMUNIFORM2DVEXTPROC glProgramUniform2dvEXT; + PFNGLPROGRAMUNIFORM3DVEXTPROC glProgramUniform3dvEXT; + PFNGLPROGRAMUNIFORM4DVEXTPROC glProgramUniform4dvEXT; + PFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC glProgramUniformMatrix2dvEXT; + PFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC glProgramUniformMatrix3dvEXT; + PFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC glProgramUniformMatrix4dvEXT; + PFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC glProgramUniformMatrix2x3dvEXT; + PFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC glProgramUniformMatrix2x4dvEXT; + PFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC glProgramUniformMatrix3x2dvEXT; + PFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC glProgramUniformMatrix3x4dvEXT; + PFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC glProgramUniformMatrix4x2dvEXT; + PFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC glProgramUniformMatrix4x3dvEXT; + PFNGLTEXTUREBUFFERRANGEEXTPROC glTextureBufferRangeEXT; + PFNGLTEXTURESTORAGE1DEXTPROC glTextureStorage1DEXT; + PFNGLTEXTURESTORAGE2DEXTPROC glTextureStorage2DEXT; + PFNGLTEXTURESTORAGE3DEXTPROC glTextureStorage3DEXT; + PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC glTextureStorage2DMultisampleEXT; + PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC glTextureStorage3DMultisampleEXT; + PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC glVertexArrayBindVertexBufferEXT; + PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC glVertexArrayVertexAttribFormatEXT; + PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC glVertexArrayVertexAttribIFormatEXT; + PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC glVertexArrayVertexAttribLFormatEXT; + PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC glVertexArrayVertexAttribBindingEXT; + PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC glVertexArrayVertexBindingDivisorEXT; + PFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC glVertexArrayVertexAttribLOffsetEXT; + PFNGLTEXTUREPAGECOMMITMENTEXTPROC glTexturePageCommitmentEXT; + PFNGLVERTEXARRAYVERTEXATTRIBDIVISOREXTPROC glVertexArrayVertexAttribDivisorEXT; + PFNGLCOLORMASKINDEXEDEXTPROC glColorMaskIndexedEXT; + PFNGLDRAWARRAYSINSTANCEDEXTPROC glDrawArraysInstancedEXT; + PFNGLDRAWELEMENTSINSTANCEDEXTPROC glDrawElementsInstancedEXT; + PFNGLDRAWRANGEELEMENTSEXTPROC glDrawRangeElementsEXT; + PFNGLBUFFERSTORAGEEXTERNALEXTPROC glBufferStorageExternalEXT; + PFNGLNAMEDBUFFERSTORAGEEXTERNALEXTPROC glNamedBufferStorageExternalEXT; + PFNGLFOGCOORDFEXTPROC glFogCoordfEXT; + PFNGLFOGCOORDFVEXTPROC glFogCoordfvEXT; + PFNGLFOGCOORDDEXTPROC glFogCoorddEXT; + PFNGLFOGCOORDDVEXTPROC glFogCoorddvEXT; + PFNGLFOGCOORDPOINTEREXTPROC glFogCoordPointerEXT; + PFNGLBLITFRAMEBUFFEREXTPROC glBlitFramebufferEXT; + PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT; + PFNGLISRENDERBUFFEREXTPROC glIsRenderbufferEXT; + PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT; + PFNGLDELETERENDERBUFFERSEXTPROC glDeleteRenderbuffersEXT; + PFNGLGENRENDERBUFFERSEXTPROC glGenRenderbuffersEXT; + PFNGLRENDERBUFFERSTORAGEEXTPROC glRenderbufferStorageEXT; + PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC glGetRenderbufferParameterivEXT; + PFNGLISFRAMEBUFFEREXTPROC glIsFramebufferEXT; + PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT; + PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT; + PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT; + PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT; + PFNGLFRAMEBUFFERTEXTURE1DEXTPROC glFramebufferTexture1DEXT; + PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT; + PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glFramebufferTexture3DEXT; + PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbufferEXT; + PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glGetFramebufferAttachmentParameterivEXT; + PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmapEXT; + PFNGLPROGRAMPARAMETERIEXTPROC glProgramParameteriEXT; + PFNGLPROGRAMENVPARAMETERS4FVEXTPROC glProgramEnvParameters4fvEXT; + PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC glProgramLocalParameters4fvEXT; + PFNGLGETUNIFORMUIVEXTPROC glGetUniformuivEXT; + PFNGLBINDFRAGDATALOCATIONEXTPROC glBindFragDataLocationEXT; + PFNGLGETFRAGDATALOCATIONEXTPROC glGetFragDataLocationEXT; + PFNGLUNIFORM1UIEXTPROC glUniform1uiEXT; + PFNGLUNIFORM2UIEXTPROC glUniform2uiEXT; + PFNGLUNIFORM3UIEXTPROC glUniform3uiEXT; + PFNGLUNIFORM4UIEXTPROC glUniform4uiEXT; + PFNGLUNIFORM1UIVEXTPROC glUniform1uivEXT; + PFNGLUNIFORM2UIVEXTPROC glUniform2uivEXT; + PFNGLUNIFORM3UIVEXTPROC glUniform3uivEXT; + PFNGLUNIFORM4UIVEXTPROC glUniform4uivEXT; + PFNGLGETHISTOGRAMEXTPROC glGetHistogramEXT; + PFNGLGETHISTOGRAMPARAMETERFVEXTPROC glGetHistogramParameterfvEXT; + PFNGLGETHISTOGRAMPARAMETERIVEXTPROC glGetHistogramParameterivEXT; + PFNGLGETMINMAXEXTPROC glGetMinmaxEXT; + PFNGLGETMINMAXPARAMETERFVEXTPROC glGetMinmaxParameterfvEXT; + PFNGLGETMINMAXPARAMETERIVEXTPROC glGetMinmaxParameterivEXT; + PFNGLHISTOGRAMEXTPROC glHistogramEXT; + PFNGLMINMAXEXTPROC glMinmaxEXT; + PFNGLRESETHISTOGRAMEXTPROC glResetHistogramEXT; + PFNGLRESETMINMAXEXTPROC glResetMinmaxEXT; + PFNGLINDEXFUNCEXTPROC glIndexFuncEXT; + PFNGLINDEXMATERIALEXTPROC glIndexMaterialEXT; + PFNGLAPPLYTEXTUREEXTPROC glApplyTextureEXT; + PFNGLTEXTURELIGHTEXTPROC glTextureLightEXT; + PFNGLTEXTUREMATERIALEXTPROC glTextureMaterialEXT; + PFNGLGETUNSIGNEDBYTEVEXTPROC glGetUnsignedBytevEXT; + PFNGLGETUNSIGNEDBYTEI_VEXTPROC glGetUnsignedBytei_vEXT; + PFNGLDELETEMEMORYOBJECTSEXTPROC glDeleteMemoryObjectsEXT; + PFNGLISMEMORYOBJECTEXTPROC glIsMemoryObjectEXT; + PFNGLCREATEMEMORYOBJECTSEXTPROC glCreateMemoryObjectsEXT; + PFNGLMEMORYOBJECTPARAMETERIVEXTPROC glMemoryObjectParameterivEXT; + PFNGLGETMEMORYOBJECTPARAMETERIVEXTPROC glGetMemoryObjectParameterivEXT; + PFNGLTEXSTORAGEMEM2DEXTPROC glTexStorageMem2DEXT; + PFNGLTEXSTORAGEMEM2DMULTISAMPLEEXTPROC glTexStorageMem2DMultisampleEXT; + PFNGLTEXSTORAGEMEM3DEXTPROC glTexStorageMem3DEXT; + PFNGLTEXSTORAGEMEM3DMULTISAMPLEEXTPROC glTexStorageMem3DMultisampleEXT; + PFNGLBUFFERSTORAGEMEMEXTPROC glBufferStorageMemEXT; + PFNGLTEXTURESTORAGEMEM2DEXTPROC glTextureStorageMem2DEXT; + PFNGLTEXTURESTORAGEMEM2DMULTISAMPLEEXTPROC glTextureStorageMem2DMultisampleEXT; + PFNGLTEXTURESTORAGEMEM3DEXTPROC glTextureStorageMem3DEXT; + PFNGLTEXTURESTORAGEMEM3DMULTISAMPLEEXTPROC glTextureStorageMem3DMultisampleEXT; + PFNGLNAMEDBUFFERSTORAGEMEMEXTPROC glNamedBufferStorageMemEXT; + PFNGLTEXSTORAGEMEM1DEXTPROC glTexStorageMem1DEXT; + PFNGLTEXTURESTORAGEMEM1DEXTPROC glTextureStorageMem1DEXT; + PFNGLIMPORTMEMORYFDEXTPROC glImportMemoryFdEXT; + PFNGLIMPORTMEMORYWIN32HANDLEEXTPROC glImportMemoryWin32HandleEXT; + PFNGLIMPORTMEMORYWIN32NAMEEXTPROC glImportMemoryWin32NameEXT; + PFNGLMULTIDRAWARRAYSEXTPROC glMultiDrawArraysEXT; + PFNGLMULTIDRAWELEMENTSEXTPROC glMultiDrawElementsEXT; + PFNGLSAMPLEMASKEXTPROC glSampleMaskEXT; + PFNGLSAMPLEPATTERNEXTPROC glSamplePatternEXT; + PFNGLCOLORTABLEEXTPROC glColorTableEXT; + PFNGLGETCOLORTABLEEXTPROC glGetColorTableEXT; + PFNGLGETCOLORTABLEPARAMETERIVEXTPROC glGetColorTableParameterivEXT; + PFNGLGETCOLORTABLEPARAMETERFVEXTPROC glGetColorTableParameterfvEXT; + PFNGLPIXELTRANSFORMPARAMETERIEXTPROC glPixelTransformParameteriEXT; + PFNGLPIXELTRANSFORMPARAMETERFEXTPROC glPixelTransformParameterfEXT; + PFNGLPIXELTRANSFORMPARAMETERIVEXTPROC glPixelTransformParameterivEXT; + PFNGLPIXELTRANSFORMPARAMETERFVEXTPROC glPixelTransformParameterfvEXT; + PFNGLGETPIXELTRANSFORMPARAMETERIVEXTPROC glGetPixelTransformParameterivEXT; + PFNGLGETPIXELTRANSFORMPARAMETERFVEXTPROC glGetPixelTransformParameterfvEXT; + PFNGLPOINTPARAMETERFEXTPROC glPointParameterfEXT; + PFNGLPOINTPARAMETERFVEXTPROC glPointParameterfvEXT; + PFNGLPOLYGONOFFSETEXTPROC glPolygonOffsetEXT; + PFNGLPOLYGONOFFSETCLAMPEXTPROC glPolygonOffsetClampEXT; + PFNGLPROVOKINGVERTEXEXTPROC glProvokingVertexEXT; + PFNGLRASTERSAMPLESEXTPROC glRasterSamplesEXT; + PFNGLGENSEMAPHORESEXTPROC glGenSemaphoresEXT; + PFNGLDELETESEMAPHORESEXTPROC glDeleteSemaphoresEXT; + PFNGLISSEMAPHOREEXTPROC glIsSemaphoreEXT; + PFNGLSEMAPHOREPARAMETERUI64VEXTPROC glSemaphoreParameterui64vEXT; + PFNGLGETSEMAPHOREPARAMETERUI64VEXTPROC glGetSemaphoreParameterui64vEXT; + PFNGLWAITSEMAPHOREEXTPROC glWaitSemaphoreEXT; + PFNGLSIGNALSEMAPHOREEXTPROC glSignalSemaphoreEXT; + PFNGLIMPORTSEMAPHOREFDEXTPROC glImportSemaphoreFdEXT; + PFNGLIMPORTSEMAPHOREWIN32HANDLEEXTPROC glImportSemaphoreWin32HandleEXT; + PFNGLIMPORTSEMAPHOREWIN32NAMEEXTPROC glImportSemaphoreWin32NameEXT; + PFNGLSECONDARYCOLOR3BEXTPROC glSecondaryColor3bEXT; + PFNGLSECONDARYCOLOR3BVEXTPROC glSecondaryColor3bvEXT; + PFNGLSECONDARYCOLOR3DEXTPROC glSecondaryColor3dEXT; + PFNGLSECONDARYCOLOR3DVEXTPROC glSecondaryColor3dvEXT; + PFNGLSECONDARYCOLOR3FEXTPROC glSecondaryColor3fEXT; + PFNGLSECONDARYCOLOR3FVEXTPROC glSecondaryColor3fvEXT; + PFNGLSECONDARYCOLOR3IEXTPROC glSecondaryColor3iEXT; + PFNGLSECONDARYCOLOR3IVEXTPROC glSecondaryColor3ivEXT; + PFNGLSECONDARYCOLOR3SEXTPROC glSecondaryColor3sEXT; + PFNGLSECONDARYCOLOR3SVEXTPROC glSecondaryColor3svEXT; + PFNGLSECONDARYCOLOR3UBEXTPROC glSecondaryColor3ubEXT; + PFNGLSECONDARYCOLOR3UBVEXTPROC glSecondaryColor3ubvEXT; + PFNGLSECONDARYCOLOR3UIEXTPROC glSecondaryColor3uiEXT; + PFNGLSECONDARYCOLOR3UIVEXTPROC glSecondaryColor3uivEXT; + PFNGLSECONDARYCOLOR3USEXTPROC glSecondaryColor3usEXT; + PFNGLSECONDARYCOLOR3USVEXTPROC glSecondaryColor3usvEXT; + PFNGLSECONDARYCOLORPOINTEREXTPROC glSecondaryColorPointerEXT; + PFNGLUSESHADERPROGRAMEXTPROC glUseShaderProgramEXT; + PFNGLACTIVEPROGRAMEXTPROC glActiveProgramEXT; + PFNGLCREATESHADERPROGRAMEXTPROC glCreateShaderProgramEXT; + PFNGLACTIVESHADERPROGRAMEXTPROC glActiveShaderProgramEXT; + PFNGLBINDPROGRAMPIPELINEEXTPROC glBindProgramPipelineEXT; + PFNGLCREATESHADERPROGRAMVEXTPROC glCreateShaderProgramvEXT; + PFNGLDELETEPROGRAMPIPELINESEXTPROC glDeleteProgramPipelinesEXT; + PFNGLGENPROGRAMPIPELINESEXTPROC glGenProgramPipelinesEXT; + PFNGLGETPROGRAMPIPELINEINFOLOGEXTPROC glGetProgramPipelineInfoLogEXT; + PFNGLGETPROGRAMPIPELINEIVEXTPROC glGetProgramPipelineivEXT; + PFNGLISPROGRAMPIPELINEEXTPROC glIsProgramPipelineEXT; + PFNGLUSEPROGRAMSTAGESEXTPROC glUseProgramStagesEXT; + PFNGLVALIDATEPROGRAMPIPELINEEXTPROC glValidateProgramPipelineEXT; + PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC glFramebufferFetchBarrierEXT; + PFNGLBINDIMAGETEXTUREEXTPROC glBindImageTextureEXT; + PFNGLMEMORYBARRIEREXTPROC glMemoryBarrierEXT; + PFNGLSTENCILCLEARTAGEXTPROC glStencilClearTagEXT; + PFNGLACTIVESTENCILFACEEXTPROC glActiveStencilFaceEXT; + PFNGLTEXSUBIMAGE1DEXTPROC glTexSubImage1DEXT; + PFNGLTEXSUBIMAGE2DEXTPROC glTexSubImage2DEXT; + PFNGLTEXIMAGE3DEXTPROC glTexImage3DEXT; + PFNGLTEXSUBIMAGE3DEXTPROC glTexSubImage3DEXT; + PFNGLFRAMEBUFFERTEXTURELAYEREXTPROC glFramebufferTextureLayerEXT; + PFNGLTEXBUFFEREXTPROC glTexBufferEXT; + PFNGLTEXPARAMETERIIVEXTPROC glTexParameterIivEXT; + PFNGLTEXPARAMETERIUIVEXTPROC glTexParameterIuivEXT; + PFNGLGETTEXPARAMETERIIVEXTPROC glGetTexParameterIivEXT; + PFNGLGETTEXPARAMETERIUIVEXTPROC glGetTexParameterIuivEXT; + PFNGLCLEARCOLORIIEXTPROC glClearColorIiEXT; + PFNGLCLEARCOLORIUIEXTPROC glClearColorIuiEXT; + PFNGLARETEXTURESRESIDENTEXTPROC glAreTexturesResidentEXT; + PFNGLBINDTEXTUREEXTPROC glBindTextureEXT; + PFNGLDELETETEXTURESEXTPROC glDeleteTexturesEXT; + PFNGLGENTEXTURESEXTPROC glGenTexturesEXT; + PFNGLISTEXTUREEXTPROC glIsTextureEXT; + PFNGLPRIORITIZETEXTURESEXTPROC glPrioritizeTexturesEXT; + PFNGLTEXTURENORMALEXTPROC glTextureNormalEXT; + PFNGLCREATESEMAPHORESNVPROC glCreateSemaphoresNV; + PFNGLSEMAPHOREPARAMETERIVNVPROC glSemaphoreParameterivNV; + PFNGLGETSEMAPHOREPARAMETERIVNVPROC glGetSemaphoreParameterivNV; + PFNGLGETQUERYOBJECTI64VEXTPROC glGetQueryObjecti64vEXT; + PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64vEXT; + PFNGLBEGINTRANSFORMFEEDBACKEXTPROC glBeginTransformFeedbackEXT; + PFNGLENDTRANSFORMFEEDBACKEXTPROC glEndTransformFeedbackEXT; + PFNGLBINDBUFFERRANGEEXTPROC glBindBufferRangeEXT; + PFNGLBINDBUFFEROFFSETEXTPROC glBindBufferOffsetEXT; + PFNGLBINDBUFFERBASEEXTPROC glBindBufferBaseEXT; + PFNGLTRANSFORMFEEDBACKVARYINGSEXTPROC glTransformFeedbackVaryingsEXT; + PFNGLGETTRANSFORMFEEDBACKVARYINGEXTPROC glGetTransformFeedbackVaryingEXT; + PFNGLARRAYELEMENTEXTPROC glArrayElementEXT; + PFNGLCOLORPOINTEREXTPROC glColorPointerEXT; + PFNGLDRAWARRAYSEXTPROC glDrawArraysEXT; + PFNGLEDGEFLAGPOINTEREXTPROC glEdgeFlagPointerEXT; + PFNGLGETPOINTERVEXTPROC glGetPointervEXT; + PFNGLINDEXPOINTEREXTPROC glIndexPointerEXT; + PFNGLNORMALPOINTEREXTPROC glNormalPointerEXT; + PFNGLTEXCOORDPOINTEREXTPROC glTexCoordPointerEXT; + PFNGLVERTEXPOINTEREXTPROC glVertexPointerEXT; + PFNGLVERTEXATTRIBL1DEXTPROC glVertexAttribL1dEXT; + PFNGLVERTEXATTRIBL2DEXTPROC glVertexAttribL2dEXT; + PFNGLVERTEXATTRIBL3DEXTPROC glVertexAttribL3dEXT; + PFNGLVERTEXATTRIBL4DEXTPROC glVertexAttribL4dEXT; + PFNGLVERTEXATTRIBL1DVEXTPROC glVertexAttribL1dvEXT; + PFNGLVERTEXATTRIBL2DVEXTPROC glVertexAttribL2dvEXT; + PFNGLVERTEXATTRIBL3DVEXTPROC glVertexAttribL3dvEXT; + PFNGLVERTEXATTRIBL4DVEXTPROC glVertexAttribL4dvEXT; + PFNGLVERTEXATTRIBLPOINTEREXTPROC glVertexAttribLPointerEXT; + PFNGLGETVERTEXATTRIBLDVEXTPROC glGetVertexAttribLdvEXT; + PFNGLBEGINVERTEXSHADEREXTPROC glBeginVertexShaderEXT; + PFNGLENDVERTEXSHADEREXTPROC glEndVertexShaderEXT; + PFNGLBINDVERTEXSHADEREXTPROC glBindVertexShaderEXT; + PFNGLGENVERTEXSHADERSEXTPROC glGenVertexShadersEXT; + PFNGLDELETEVERTEXSHADEREXTPROC glDeleteVertexShaderEXT; + PFNGLSHADEROP1EXTPROC glShaderOp1EXT; + PFNGLSHADEROP2EXTPROC glShaderOp2EXT; + PFNGLSHADEROP3EXTPROC glShaderOp3EXT; + PFNGLSWIZZLEEXTPROC glSwizzleEXT; + PFNGLWRITEMASKEXTPROC glWriteMaskEXT; + PFNGLINSERTCOMPONENTEXTPROC glInsertComponentEXT; + PFNGLEXTRACTCOMPONENTEXTPROC glExtractComponentEXT; + PFNGLGENSYMBOLSEXTPROC glGenSymbolsEXT; + PFNGLSETINVARIANTEXTPROC glSetInvariantEXT; + PFNGLSETLOCALCONSTANTEXTPROC glSetLocalConstantEXT; + PFNGLVARIANTBVEXTPROC glVariantbvEXT; + PFNGLVARIANTSVEXTPROC glVariantsvEXT; + PFNGLVARIANTIVEXTPROC glVariantivEXT; + PFNGLVARIANTFVEXTPROC glVariantfvEXT; + PFNGLVARIANTDVEXTPROC glVariantdvEXT; + PFNGLVARIANTUBVEXTPROC glVariantubvEXT; + PFNGLVARIANTUSVEXTPROC glVariantusvEXT; + PFNGLVARIANTUIVEXTPROC glVariantuivEXT; + PFNGLVARIANTPOINTEREXTPROC glVariantPointerEXT; + PFNGLENABLEVARIANTCLIENTSTATEEXTPROC glEnableVariantClientStateEXT; + PFNGLDISABLEVARIANTCLIENTSTATEEXTPROC glDisableVariantClientStateEXT; + PFNGLBINDLIGHTPARAMETEREXTPROC glBindLightParameterEXT; + PFNGLBINDMATERIALPARAMETEREXTPROC glBindMaterialParameterEXT; + PFNGLBINDTEXGENPARAMETEREXTPROC glBindTexGenParameterEXT; + PFNGLBINDTEXTUREUNITPARAMETEREXTPROC glBindTextureUnitParameterEXT; + PFNGLBINDPARAMETEREXTPROC glBindParameterEXT; + PFNGLISVARIANTENABLEDEXTPROC glIsVariantEnabledEXT; + PFNGLGETVARIANTBOOLEANVEXTPROC glGetVariantBooleanvEXT; + PFNGLGETVARIANTINTEGERVEXTPROC glGetVariantIntegervEXT; + PFNGLGETVARIANTFLOATVEXTPROC glGetVariantFloatvEXT; + PFNGLGETVARIANTPOINTERVEXTPROC glGetVariantPointervEXT; + PFNGLGETINVARIANTBOOLEANVEXTPROC glGetInvariantBooleanvEXT; + PFNGLGETINVARIANTINTEGERVEXTPROC glGetInvariantIntegervEXT; + PFNGLGETINVARIANTFLOATVEXTPROC glGetInvariantFloatvEXT; + PFNGLGETLOCALCONSTANTBOOLEANVEXTPROC glGetLocalConstantBooleanvEXT; + PFNGLGETLOCALCONSTANTINTEGERVEXTPROC glGetLocalConstantIntegervEXT; + PFNGLGETLOCALCONSTANTFLOATVEXTPROC glGetLocalConstantFloatvEXT; + PFNGLVERTEXWEIGHTFEXTPROC glVertexWeightfEXT; + PFNGLVERTEXWEIGHTFVEXTPROC glVertexWeightfvEXT; + PFNGLVERTEXWEIGHTPOINTEREXTPROC glVertexWeightPointerEXT; + PFNGLACQUIREKEYEDMUTEXWIN32EXTPROC glAcquireKeyedMutexWin32EXT; + PFNGLRELEASEKEYEDMUTEXWIN32EXTPROC glReleaseKeyedMutexWin32EXT; + PFNGLWINDOWRECTANGLESEXTPROC glWindowRectanglesEXT; + PFNGLIMPORTSYNCEXTPROC glImportSyncEXT; + PFNGLFRAMETERMINATORGREMEDYPROC glFrameTerminatorGREMEDY; + PFNGLSTRINGMARKERGREMEDYPROC glStringMarkerGREMEDY; + PFNGLIMAGETRANSFORMPARAMETERIHPPROC glImageTransformParameteriHP; + PFNGLIMAGETRANSFORMPARAMETERFHPPROC glImageTransformParameterfHP; + PFNGLIMAGETRANSFORMPARAMETERIVHPPROC glImageTransformParameterivHP; + PFNGLIMAGETRANSFORMPARAMETERFVHPPROC glImageTransformParameterfvHP; + PFNGLGETIMAGETRANSFORMPARAMETERIVHPPROC glGetImageTransformParameterivHP; + PFNGLGETIMAGETRANSFORMPARAMETERFVHPPROC glGetImageTransformParameterfvHP; + PFNGLMULTIMODEDRAWARRAYSIBMPROC glMultiModeDrawArraysIBM; + PFNGLMULTIMODEDRAWELEMENTSIBMPROC glMultiModeDrawElementsIBM; + PFNGLFLUSHSTATICDATAIBMPROC glFlushStaticDataIBM; + PFNGLCOLORPOINTERLISTIBMPROC glColorPointerListIBM; + PFNGLSECONDARYCOLORPOINTERLISTIBMPROC glSecondaryColorPointerListIBM; + PFNGLEDGEFLAGPOINTERLISTIBMPROC glEdgeFlagPointerListIBM; + PFNGLFOGCOORDPOINTERLISTIBMPROC glFogCoordPointerListIBM; + PFNGLINDEXPOINTERLISTIBMPROC glIndexPointerListIBM; + PFNGLNORMALPOINTERLISTIBMPROC glNormalPointerListIBM; + PFNGLTEXCOORDPOINTERLISTIBMPROC glTexCoordPointerListIBM; + PFNGLVERTEXPOINTERLISTIBMPROC glVertexPointerListIBM; + PFNGLBLENDFUNCSEPARATEINGRPROC glBlendFuncSeparateINGR; + PFNGLAPPLYFRAMEBUFFERATTACHMENTCMAAINTELPROC glApplyFramebufferAttachmentCMAAINTEL; + PFNGLSYNCTEXTUREINTELPROC glSyncTextureINTEL; + PFNGLUNMAPTEXTURE2DINTELPROC glUnmapTexture2DINTEL; + PFNGLMAPTEXTURE2DINTELPROC glMapTexture2DINTEL; + PFNGLVERTEXPOINTERVINTELPROC glVertexPointervINTEL; + PFNGLNORMALPOINTERVINTELPROC glNormalPointervINTEL; + PFNGLCOLORPOINTERVINTELPROC glColorPointervINTEL; + PFNGLTEXCOORDPOINTERVINTELPROC glTexCoordPointervINTEL; + PFNGLBEGINPERFQUERYINTELPROC glBeginPerfQueryINTEL; + PFNGLCREATEPERFQUERYINTELPROC glCreatePerfQueryINTEL; + PFNGLDELETEPERFQUERYINTELPROC glDeletePerfQueryINTEL; + PFNGLENDPERFQUERYINTELPROC glEndPerfQueryINTEL; + PFNGLGETFIRSTPERFQUERYIDINTELPROC glGetFirstPerfQueryIdINTEL; + PFNGLGETNEXTPERFQUERYIDINTELPROC glGetNextPerfQueryIdINTEL; + PFNGLGETPERFCOUNTERINFOINTELPROC glGetPerfCounterInfoINTEL; + PFNGLGETPERFQUERYDATAINTELPROC glGetPerfQueryDataINTEL; + PFNGLGETPERFQUERYIDBYNAMEINTELPROC glGetPerfQueryIdByNameINTEL; + PFNGLGETPERFQUERYINFOINTELPROC glGetPerfQueryInfoINTEL; + PFNGLBLENDBARRIERKHRPROC glBlendBarrierKHR; + PFNGLDEBUGMESSAGECONTROLKHRPROC glDebugMessageControlKHR; + PFNGLDEBUGMESSAGEINSERTKHRPROC glDebugMessageInsertKHR; + PFNGLDEBUGMESSAGECALLBACKKHRPROC glDebugMessageCallbackKHR; + PFNGLGETDEBUGMESSAGELOGKHRPROC glGetDebugMessageLogKHR; + PFNGLPUSHDEBUGGROUPKHRPROC glPushDebugGroupKHR; + PFNGLPOPDEBUGGROUPKHRPROC glPopDebugGroupKHR; + PFNGLOBJECTLABELKHRPROC glObjectLabelKHR; + PFNGLGETOBJECTLABELKHRPROC glGetObjectLabelKHR; + PFNGLOBJECTPTRLABELKHRPROC glObjectPtrLabelKHR; + PFNGLGETOBJECTPTRLABELKHRPROC glGetObjectPtrLabelKHR; + PFNGLGETPOINTERVKHRPROC glGetPointervKHR; + PFNGLGETGRAPHICSRESETSTATUSKHRPROC glGetGraphicsResetStatusKHR; + PFNGLREADNPIXELSKHRPROC glReadnPixelsKHR; + PFNGLGETNUNIFORMFVKHRPROC glGetnUniformfvKHR; + PFNGLGETNUNIFORMIVKHRPROC glGetnUniformivKHR; + PFNGLGETNUNIFORMUIVKHRPROC glGetnUniformuivKHR; + PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR; + PFNGLFRAMEBUFFERPARAMETERIMESAPROC glFramebufferParameteriMESA; + PFNGLGETFRAMEBUFFERPARAMETERIVMESAPROC glGetFramebufferParameterivMESA; + PFNGLRESIZEBUFFERSMESAPROC glResizeBuffersMESA; + PFNGLWINDOWPOS2DMESAPROC glWindowPos2dMESA; + PFNGLWINDOWPOS2DVMESAPROC glWindowPos2dvMESA; + PFNGLWINDOWPOS2FMESAPROC glWindowPos2fMESA; + PFNGLWINDOWPOS2FVMESAPROC glWindowPos2fvMESA; + PFNGLWINDOWPOS2IMESAPROC glWindowPos2iMESA; + PFNGLWINDOWPOS2IVMESAPROC glWindowPos2ivMESA; + PFNGLWINDOWPOS2SMESAPROC glWindowPos2sMESA; + PFNGLWINDOWPOS2SVMESAPROC glWindowPos2svMESA; + PFNGLWINDOWPOS3DMESAPROC glWindowPos3dMESA; + PFNGLWINDOWPOS3DVMESAPROC glWindowPos3dvMESA; + PFNGLWINDOWPOS3FMESAPROC glWindowPos3fMESA; + PFNGLWINDOWPOS3FVMESAPROC glWindowPos3fvMESA; + PFNGLWINDOWPOS3IMESAPROC glWindowPos3iMESA; + PFNGLWINDOWPOS3IVMESAPROC glWindowPos3ivMESA; + PFNGLWINDOWPOS3SMESAPROC glWindowPos3sMESA; + PFNGLWINDOWPOS3SVMESAPROC glWindowPos3svMESA; + PFNGLWINDOWPOS4DMESAPROC glWindowPos4dMESA; + PFNGLWINDOWPOS4DVMESAPROC glWindowPos4dvMESA; + PFNGLWINDOWPOS4FMESAPROC glWindowPos4fMESA; + PFNGLWINDOWPOS4FVMESAPROC glWindowPos4fvMESA; + PFNGLWINDOWPOS4IMESAPROC glWindowPos4iMESA; + PFNGLWINDOWPOS4IVMESAPROC glWindowPos4ivMESA; + PFNGLWINDOWPOS4SMESAPROC glWindowPos4sMESA; + PFNGLWINDOWPOS4SVMESAPROC glWindowPos4svMESA; + PFNGLBEGINCONDITIONALRENDERNVXPROC glBeginConditionalRenderNVX; + PFNGLENDCONDITIONALRENDERNVXPROC glEndConditionalRenderNVX; + PFNGLLGPUNAMEDBUFFERSUBDATANVXPROC glLGPUNamedBufferSubDataNVX; + PFNGLLGPUCOPYIMAGESUBDATANVXPROC glLGPUCopyImageSubDataNVX; + PFNGLLGPUINTERLOCKNVXPROC glLGPUInterlockNVX; + PFNGLALPHATOCOVERAGEDITHERCONTROLNVPROC glAlphaToCoverageDitherControlNV; + PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC glMultiDrawArraysIndirectBindlessNV; + PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSNVPROC glMultiDrawElementsIndirectBindlessNV; + PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSCOUNTNVPROC glMultiDrawArraysIndirectBindlessCountNV; + PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSCOUNTNVPROC glMultiDrawElementsIndirectBindlessCountNV; + PFNGLGETTEXTUREHANDLENVPROC glGetTextureHandleNV; + PFNGLGETTEXTURESAMPLERHANDLENVPROC glGetTextureSamplerHandleNV; + PFNGLMAKETEXTUREHANDLERESIDENTNVPROC glMakeTextureHandleResidentNV; + PFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC glMakeTextureHandleNonResidentNV; + PFNGLGETIMAGEHANDLENVPROC glGetImageHandleNV; + PFNGLMAKEIMAGEHANDLERESIDENTNVPROC glMakeImageHandleResidentNV; + PFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC glMakeImageHandleNonResidentNV; + PFNGLUNIFORMHANDLEUI64NVPROC glUniformHandleui64NV; + PFNGLUNIFORMHANDLEUI64VNVPROC glUniformHandleui64vNV; + PFNGLPROGRAMUNIFORMHANDLEUI64NVPROC glProgramUniformHandleui64NV; + PFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC glProgramUniformHandleui64vNV; + PFNGLISTEXTUREHANDLERESIDENTNVPROC glIsTextureHandleResidentNV; + PFNGLISIMAGEHANDLERESIDENTNVPROC glIsImageHandleResidentNV; + PFNGLBLENDPARAMETERINVPROC glBlendParameteriNV; + PFNGLBLENDBARRIERNVPROC glBlendBarrierNV; + PFNGLVIEWPORTPOSITIONWSCALENVPROC glViewportPositionWScaleNV; + PFNGLCREATESTATESNVPROC glCreateStatesNV; + PFNGLDELETESTATESNVPROC glDeleteStatesNV; + PFNGLISSTATENVPROC glIsStateNV; + PFNGLSTATECAPTURENVPROC glStateCaptureNV; + PFNGLGETCOMMANDHEADERNVPROC glGetCommandHeaderNV; + PFNGLGETSTAGEINDEXNVPROC glGetStageIndexNV; + PFNGLDRAWCOMMANDSNVPROC glDrawCommandsNV; + PFNGLDRAWCOMMANDSADDRESSNVPROC glDrawCommandsAddressNV; + PFNGLDRAWCOMMANDSSTATESNVPROC glDrawCommandsStatesNV; + PFNGLDRAWCOMMANDSSTATESADDRESSNVPROC glDrawCommandsStatesAddressNV; + PFNGLCREATECOMMANDLISTSNVPROC glCreateCommandListsNV; + PFNGLDELETECOMMANDLISTSNVPROC glDeleteCommandListsNV; + PFNGLISCOMMANDLISTNVPROC glIsCommandListNV; + PFNGLLISTDRAWCOMMANDSSTATESCLIENTNVPROC glListDrawCommandsStatesClientNV; + PFNGLCOMMANDLISTSEGMENTSNVPROC glCommandListSegmentsNV; + PFNGLCOMPILECOMMANDLISTNVPROC glCompileCommandListNV; + PFNGLCALLCOMMANDLISTNVPROC glCallCommandListNV; + PFNGLBEGINCONDITIONALRENDERNVPROC glBeginConditionalRenderNV; + PFNGLENDCONDITIONALRENDERNVPROC glEndConditionalRenderNV; + PFNGLSUBPIXELPRECISIONBIASNVPROC glSubpixelPrecisionBiasNV; + PFNGLCONSERVATIVERASTERPARAMETERFNVPROC glConservativeRasterParameterfNV; + PFNGLCONSERVATIVERASTERPARAMETERINVPROC glConservativeRasterParameteriNV; + PFNGLCOPYIMAGESUBDATANVPROC glCopyImageSubDataNV; + PFNGLDEPTHRANGEDNVPROC glDepthRangedNV; + PFNGLCLEARDEPTHDNVPROC glClearDepthdNV; + PFNGLDEPTHBOUNDSDNVPROC glDepthBoundsdNV; + PFNGLDRAWTEXTURENVPROC glDrawTextureNV; + PFNGLDRAWVKIMAGENVPROC glDrawVkImageNV; + PFNGLGETVKPROCADDRNVPROC glGetVkProcAddrNV; + PFNGLWAITVKSEMAPHORENVPROC glWaitVkSemaphoreNV; + PFNGLSIGNALVKSEMAPHORENVPROC glSignalVkSemaphoreNV; + PFNGLSIGNALVKFENCENVPROC glSignalVkFenceNV; + PFNGLMAPCONTROLPOINTSNVPROC glMapControlPointsNV; + PFNGLMAPPARAMETERIVNVPROC glMapParameterivNV; + PFNGLMAPPARAMETERFVNVPROC glMapParameterfvNV; + PFNGLGETMAPCONTROLPOINTSNVPROC glGetMapControlPointsNV; + PFNGLGETMAPPARAMETERIVNVPROC glGetMapParameterivNV; + PFNGLGETMAPPARAMETERFVNVPROC glGetMapParameterfvNV; + PFNGLGETMAPATTRIBPARAMETERIVNVPROC glGetMapAttribParameterivNV; + PFNGLGETMAPATTRIBPARAMETERFVNVPROC glGetMapAttribParameterfvNV; + PFNGLEVALMAPSNVPROC glEvalMapsNV; + PFNGLGETMULTISAMPLEFVNVPROC glGetMultisamplefvNV; + PFNGLSAMPLEMASKINDEXEDNVPROC glSampleMaskIndexedNV; + PFNGLTEXRENDERBUFFERNVPROC glTexRenderbufferNV; + PFNGLDELETEFENCESNVPROC glDeleteFencesNV; + PFNGLGENFENCESNVPROC glGenFencesNV; + PFNGLISFENCENVPROC glIsFenceNV; + PFNGLTESTFENCENVPROC glTestFenceNV; + PFNGLGETFENCEIVNVPROC glGetFenceivNV; + PFNGLFINISHFENCENVPROC glFinishFenceNV; + PFNGLSETFENCENVPROC glSetFenceNV; + PFNGLFRAGMENTCOVERAGECOLORNVPROC glFragmentCoverageColorNV; + PFNGLPROGRAMNAMEDPARAMETER4FNVPROC glProgramNamedParameter4fNV; + PFNGLPROGRAMNAMEDPARAMETER4FVNVPROC glProgramNamedParameter4fvNV; + PFNGLPROGRAMNAMEDPARAMETER4DNVPROC glProgramNamedParameter4dNV; + PFNGLPROGRAMNAMEDPARAMETER4DVNVPROC glProgramNamedParameter4dvNV; + PFNGLGETPROGRAMNAMEDPARAMETERFVNVPROC glGetProgramNamedParameterfvNV; + PFNGLGETPROGRAMNAMEDPARAMETERDVNVPROC glGetProgramNamedParameterdvNV; + PFNGLCOVERAGEMODULATIONTABLENVPROC glCoverageModulationTableNV; + PFNGLGETCOVERAGEMODULATIONTABLENVPROC glGetCoverageModulationTableNV; + PFNGLCOVERAGEMODULATIONNVPROC glCoverageModulationNV; + PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC glRenderbufferStorageMultisampleCoverageNV; + PFNGLPROGRAMVERTEXLIMITNVPROC glProgramVertexLimitNV; + PFNGLFRAMEBUFFERTEXTUREEXTPROC glFramebufferTextureEXT; + PFNGLFRAMEBUFFERTEXTUREFACEEXTPROC glFramebufferTextureFaceEXT; + PFNGLPROGRAMLOCALPARAMETERI4INVPROC glProgramLocalParameterI4iNV; + PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC glProgramLocalParameterI4ivNV; + PFNGLPROGRAMLOCALPARAMETERSI4IVNVPROC glProgramLocalParametersI4ivNV; + PFNGLPROGRAMLOCALPARAMETERI4UINVPROC glProgramLocalParameterI4uiNV; + PFNGLPROGRAMLOCALPARAMETERI4UIVNVPROC glProgramLocalParameterI4uivNV; + PFNGLPROGRAMLOCALPARAMETERSI4UIVNVPROC glProgramLocalParametersI4uivNV; + PFNGLPROGRAMENVPARAMETERI4INVPROC glProgramEnvParameterI4iNV; + PFNGLPROGRAMENVPARAMETERI4IVNVPROC glProgramEnvParameterI4ivNV; + PFNGLPROGRAMENVPARAMETERSI4IVNVPROC glProgramEnvParametersI4ivNV; + PFNGLPROGRAMENVPARAMETERI4UINVPROC glProgramEnvParameterI4uiNV; + PFNGLPROGRAMENVPARAMETERI4UIVNVPROC glProgramEnvParameterI4uivNV; + PFNGLPROGRAMENVPARAMETERSI4UIVNVPROC glProgramEnvParametersI4uivNV; + PFNGLGETPROGRAMLOCALPARAMETERIIVNVPROC glGetProgramLocalParameterIivNV; + PFNGLGETPROGRAMLOCALPARAMETERIUIVNVPROC glGetProgramLocalParameterIuivNV; + PFNGLGETPROGRAMENVPARAMETERIIVNVPROC glGetProgramEnvParameterIivNV; + PFNGLGETPROGRAMENVPARAMETERIUIVNVPROC glGetProgramEnvParameterIuivNV; + PFNGLPROGRAMSUBROUTINEPARAMETERSUIVNVPROC glProgramSubroutineParametersuivNV; + PFNGLGETPROGRAMSUBROUTINEPARAMETERUIVNVPROC glGetProgramSubroutineParameteruivNV; + PFNGLVERTEX2HNVPROC glVertex2hNV; + PFNGLVERTEX2HVNVPROC glVertex2hvNV; + PFNGLVERTEX3HNVPROC glVertex3hNV; + PFNGLVERTEX3HVNVPROC glVertex3hvNV; + PFNGLVERTEX4HNVPROC glVertex4hNV; + PFNGLVERTEX4HVNVPROC glVertex4hvNV; + PFNGLNORMAL3HNVPROC glNormal3hNV; + PFNGLNORMAL3HVNVPROC glNormal3hvNV; + PFNGLCOLOR3HNVPROC glColor3hNV; + PFNGLCOLOR3HVNVPROC glColor3hvNV; + PFNGLCOLOR4HNVPROC glColor4hNV; + PFNGLCOLOR4HVNVPROC glColor4hvNV; + PFNGLTEXCOORD1HNVPROC glTexCoord1hNV; + PFNGLTEXCOORD1HVNVPROC glTexCoord1hvNV; + PFNGLTEXCOORD2HNVPROC glTexCoord2hNV; + PFNGLTEXCOORD2HVNVPROC glTexCoord2hvNV; + PFNGLTEXCOORD3HNVPROC glTexCoord3hNV; + PFNGLTEXCOORD3HVNVPROC glTexCoord3hvNV; + PFNGLTEXCOORD4HNVPROC glTexCoord4hNV; + PFNGLTEXCOORD4HVNVPROC glTexCoord4hvNV; + PFNGLMULTITEXCOORD1HNVPROC glMultiTexCoord1hNV; + PFNGLMULTITEXCOORD1HVNVPROC glMultiTexCoord1hvNV; + PFNGLMULTITEXCOORD2HNVPROC glMultiTexCoord2hNV; + PFNGLMULTITEXCOORD2HVNVPROC glMultiTexCoord2hvNV; + PFNGLMULTITEXCOORD3HNVPROC glMultiTexCoord3hNV; + PFNGLMULTITEXCOORD3HVNVPROC glMultiTexCoord3hvNV; + PFNGLMULTITEXCOORD4HNVPROC glMultiTexCoord4hNV; + PFNGLMULTITEXCOORD4HVNVPROC glMultiTexCoord4hvNV; + PFNGLFOGCOORDHNVPROC glFogCoordhNV; + PFNGLFOGCOORDHVNVPROC glFogCoordhvNV; + PFNGLSECONDARYCOLOR3HNVPROC glSecondaryColor3hNV; + PFNGLSECONDARYCOLOR3HVNVPROC glSecondaryColor3hvNV; + PFNGLVERTEXWEIGHTHNVPROC glVertexWeighthNV; + PFNGLVERTEXWEIGHTHVNVPROC glVertexWeighthvNV; + PFNGLVERTEXATTRIB1HNVPROC glVertexAttrib1hNV; + PFNGLVERTEXATTRIB1HVNVPROC glVertexAttrib1hvNV; + PFNGLVERTEXATTRIB2HNVPROC glVertexAttrib2hNV; + PFNGLVERTEXATTRIB2HVNVPROC glVertexAttrib2hvNV; + PFNGLVERTEXATTRIB3HNVPROC glVertexAttrib3hNV; + PFNGLVERTEXATTRIB3HVNVPROC glVertexAttrib3hvNV; + PFNGLVERTEXATTRIB4HNVPROC glVertexAttrib4hNV; + PFNGLVERTEXATTRIB4HVNVPROC glVertexAttrib4hvNV; + PFNGLVERTEXATTRIBS1HVNVPROC glVertexAttribs1hvNV; + PFNGLVERTEXATTRIBS2HVNVPROC glVertexAttribs2hvNV; + PFNGLVERTEXATTRIBS3HVNVPROC glVertexAttribs3hvNV; + PFNGLVERTEXATTRIBS4HVNVPROC glVertexAttribs4hvNV; + PFNGLGETINTERNALFORMATSAMPLEIVNVPROC glGetInternalformatSampleivNV; + PFNGLRENDERGPUMASKNVPROC glRenderGpuMaskNV; + PFNGLMULTICASTBUFFERSUBDATANVPROC glMulticastBufferSubDataNV; + PFNGLMULTICASTCOPYBUFFERSUBDATANVPROC glMulticastCopyBufferSubDataNV; + PFNGLMULTICASTCOPYIMAGESUBDATANVPROC glMulticastCopyImageSubDataNV; + PFNGLMULTICASTBLITFRAMEBUFFERNVPROC glMulticastBlitFramebufferNV; + PFNGLMULTICASTFRAMEBUFFERSAMPLELOCATIONSFVNVPROC glMulticastFramebufferSampleLocationsfvNV; + PFNGLMULTICASTBARRIERNVPROC glMulticastBarrierNV; + PFNGLMULTICASTWAITSYNCNVPROC glMulticastWaitSyncNV; + PFNGLMULTICASTGETQUERYOBJECTIVNVPROC glMulticastGetQueryObjectivNV; + PFNGLMULTICASTGETQUERYOBJECTUIVNVPROC glMulticastGetQueryObjectuivNV; + PFNGLMULTICASTGETQUERYOBJECTI64VNVPROC glMulticastGetQueryObjecti64vNV; + PFNGLMULTICASTGETQUERYOBJECTUI64VNVPROC glMulticastGetQueryObjectui64vNV; + PFNGLUPLOADGPUMASKNVXPROC glUploadGpuMaskNVX; + PFNGLMULTICASTVIEWPORTARRAYVNVXPROC glMulticastViewportArrayvNVX; + PFNGLMULTICASTVIEWPORTPOSITIONWSCALENVXPROC glMulticastViewportPositionWScaleNVX; + PFNGLMULTICASTSCISSORARRAYVNVXPROC glMulticastScissorArrayvNVX; + PFNGLASYNCCOPYBUFFERSUBDATANVXPROC glAsyncCopyBufferSubDataNVX; + PFNGLASYNCCOPYIMAGESUBDATANVXPROC glAsyncCopyImageSubDataNVX; + PFNGLCREATEPROGRESSFENCENVXPROC glCreateProgressFenceNVX; + PFNGLSIGNALSEMAPHOREUI64NVXPROC glSignalSemaphoreui64NVX; + PFNGLWAITSEMAPHOREUI64NVXPROC glWaitSemaphoreui64NVX; + PFNGLCLIENTWAITSEMAPHOREUI64NVXPROC glClientWaitSemaphoreui64NVX; + PFNGLGETMEMORYOBJECTDETACHEDRESOURCESUIVNVPROC glGetMemoryObjectDetachedResourcesuivNV; + PFNGLRESETMEMORYOBJECTPARAMETERNVPROC glResetMemoryObjectParameterNV; + PFNGLTEXATTACHMEMORYNVPROC glTexAttachMemoryNV; + PFNGLBUFFERATTACHMEMORYNVPROC glBufferAttachMemoryNV; + PFNGLTEXTUREATTACHMEMORYNVPROC glTextureAttachMemoryNV; + PFNGLNAMEDBUFFERATTACHMEMORYNVPROC glNamedBufferAttachMemoryNV; + PFNGLBUFFERPAGECOMMITMENTMEMNVPROC glBufferPageCommitmentMemNV; + PFNGLTEXPAGECOMMITMENTMEMNVPROC glTexPageCommitmentMemNV; + PFNGLNAMEDBUFFERPAGECOMMITMENTMEMNVPROC glNamedBufferPageCommitmentMemNV; + PFNGLTEXTUREPAGECOMMITMENTMEMNVPROC glTexturePageCommitmentMemNV; + PFNGLDRAWMESHTASKSNVPROC glDrawMeshTasksNV; + PFNGLDRAWMESHTASKSINDIRECTNVPROC glDrawMeshTasksIndirectNV; + PFNGLMULTIDRAWMESHTASKSINDIRECTNVPROC glMultiDrawMeshTasksIndirectNV; + PFNGLMULTIDRAWMESHTASKSINDIRECTCOUNTNVPROC glMultiDrawMeshTasksIndirectCountNV; + PFNGLGENOCCLUSIONQUERIESNVPROC glGenOcclusionQueriesNV; + PFNGLDELETEOCCLUSIONQUERIESNVPROC glDeleteOcclusionQueriesNV; + PFNGLISOCCLUSIONQUERYNVPROC glIsOcclusionQueryNV; + PFNGLBEGINOCCLUSIONQUERYNVPROC glBeginOcclusionQueryNV; + PFNGLENDOCCLUSIONQUERYNVPROC glEndOcclusionQueryNV; + PFNGLGETOCCLUSIONQUERYIVNVPROC glGetOcclusionQueryivNV; + PFNGLGETOCCLUSIONQUERYUIVNVPROC glGetOcclusionQueryuivNV; + PFNGLPROGRAMBUFFERPARAMETERSFVNVPROC glProgramBufferParametersfvNV; + PFNGLPROGRAMBUFFERPARAMETERSIIVNVPROC glProgramBufferParametersIivNV; + PFNGLPROGRAMBUFFERPARAMETERSIUIVNVPROC glProgramBufferParametersIuivNV; + PFNGLGENPATHSNVPROC glGenPathsNV; + PFNGLDELETEPATHSNVPROC glDeletePathsNV; + PFNGLISPATHNVPROC glIsPathNV; + PFNGLPATHCOMMANDSNVPROC glPathCommandsNV; + PFNGLPATHCOORDSNVPROC glPathCoordsNV; + PFNGLPATHSUBCOMMANDSNVPROC glPathSubCommandsNV; + PFNGLPATHSUBCOORDSNVPROC glPathSubCoordsNV; + PFNGLPATHSTRINGNVPROC glPathStringNV; + PFNGLPATHGLYPHSNVPROC glPathGlyphsNV; + PFNGLPATHGLYPHRANGENVPROC glPathGlyphRangeNV; + PFNGLWEIGHTPATHSNVPROC glWeightPathsNV; + PFNGLCOPYPATHNVPROC glCopyPathNV; + PFNGLINTERPOLATEPATHSNVPROC glInterpolatePathsNV; + PFNGLTRANSFORMPATHNVPROC glTransformPathNV; + PFNGLPATHPARAMETERIVNVPROC glPathParameterivNV; + PFNGLPATHPARAMETERINVPROC glPathParameteriNV; + PFNGLPATHPARAMETERFVNVPROC glPathParameterfvNV; + PFNGLPATHPARAMETERFNVPROC glPathParameterfNV; + PFNGLPATHDASHARRAYNVPROC glPathDashArrayNV; + PFNGLPATHSTENCILFUNCNVPROC glPathStencilFuncNV; + PFNGLPATHSTENCILDEPTHOFFSETNVPROC glPathStencilDepthOffsetNV; + PFNGLSTENCILFILLPATHNVPROC glStencilFillPathNV; + PFNGLSTENCILSTROKEPATHNVPROC glStencilStrokePathNV; + PFNGLSTENCILFILLPATHINSTANCEDNVPROC glStencilFillPathInstancedNV; + PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC glStencilStrokePathInstancedNV; + PFNGLPATHCOVERDEPTHFUNCNVPROC glPathCoverDepthFuncNV; + PFNGLCOVERFILLPATHNVPROC glCoverFillPathNV; + PFNGLCOVERSTROKEPATHNVPROC glCoverStrokePathNV; + PFNGLCOVERFILLPATHINSTANCEDNVPROC glCoverFillPathInstancedNV; + PFNGLCOVERSTROKEPATHINSTANCEDNVPROC glCoverStrokePathInstancedNV; + PFNGLGETPATHPARAMETERIVNVPROC glGetPathParameterivNV; + PFNGLGETPATHPARAMETERFVNVPROC glGetPathParameterfvNV; + PFNGLGETPATHCOMMANDSNVPROC glGetPathCommandsNV; + PFNGLGETPATHCOORDSNVPROC glGetPathCoordsNV; + PFNGLGETPATHDASHARRAYNVPROC glGetPathDashArrayNV; + PFNGLGETPATHMETRICSNVPROC glGetPathMetricsNV; + PFNGLGETPATHMETRICRANGENVPROC glGetPathMetricRangeNV; + PFNGLGETPATHSPACINGNVPROC glGetPathSpacingNV; + PFNGLISPOINTINFILLPATHNVPROC glIsPointInFillPathNV; + PFNGLISPOINTINSTROKEPATHNVPROC glIsPointInStrokePathNV; + PFNGLGETPATHLENGTHNVPROC glGetPathLengthNV; + PFNGLPOINTALONGPATHNVPROC glPointAlongPathNV; + PFNGLMATRIXLOAD3X2FNVPROC glMatrixLoad3x2fNV; + PFNGLMATRIXLOAD3X3FNVPROC glMatrixLoad3x3fNV; + PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC glMatrixLoadTranspose3x3fNV; + PFNGLMATRIXMULT3X2FNVPROC glMatrixMult3x2fNV; + PFNGLMATRIXMULT3X3FNVPROC glMatrixMult3x3fNV; + PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC glMatrixMultTranspose3x3fNV; + PFNGLSTENCILTHENCOVERFILLPATHNVPROC glStencilThenCoverFillPathNV; + PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC glStencilThenCoverStrokePathNV; + PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC glStencilThenCoverFillPathInstancedNV; + PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC glStencilThenCoverStrokePathInstancedNV; + PFNGLPATHGLYPHINDEXRANGENVPROC glPathGlyphIndexRangeNV; + PFNGLPATHGLYPHINDEXARRAYNVPROC glPathGlyphIndexArrayNV; + PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC glPathMemoryGlyphIndexArrayNV; + PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC glProgramPathFragmentInputGenNV; + PFNGLGETPROGRAMRESOURCEFVNVPROC glGetProgramResourcefvNV; + PFNGLPATHCOLORGENNVPROC glPathColorGenNV; + PFNGLPATHTEXGENNVPROC glPathTexGenNV; + PFNGLPATHFOGGENNVPROC glPathFogGenNV; + PFNGLGETPATHCOLORGENIVNVPROC glGetPathColorGenivNV; + PFNGLGETPATHCOLORGENFVNVPROC glGetPathColorGenfvNV; + PFNGLGETPATHTEXGENIVNVPROC glGetPathTexGenivNV; + PFNGLGETPATHTEXGENFVNVPROC glGetPathTexGenfvNV; + PFNGLPIXELDATARANGENVPROC glPixelDataRangeNV; + PFNGLFLUSHPIXELDATARANGENVPROC glFlushPixelDataRangeNV; + PFNGLPOINTPARAMETERINVPROC glPointParameteriNV; + PFNGLPOINTPARAMETERIVNVPROC glPointParameterivNV; + PFNGLPRESENTFRAMEKEYEDNVPROC glPresentFrameKeyedNV; + PFNGLPRESENTFRAMEDUALFILLNVPROC glPresentFrameDualFillNV; + PFNGLGETVIDEOIVNVPROC glGetVideoivNV; + PFNGLGETVIDEOUIVNVPROC glGetVideouivNV; + PFNGLGETVIDEOI64VNVPROC glGetVideoi64vNV; + PFNGLGETVIDEOUI64VNVPROC glGetVideoui64vNV; + PFNGLPRIMITIVERESTARTNVPROC glPrimitiveRestartNV; + PFNGLPRIMITIVERESTARTINDEXNVPROC glPrimitiveRestartIndexNV; + PFNGLQUERYRESOURCENVPROC glQueryResourceNV; + PFNGLGENQUERYRESOURCETAGNVPROC glGenQueryResourceTagNV; + PFNGLDELETEQUERYRESOURCETAGNVPROC glDeleteQueryResourceTagNV; + PFNGLQUERYRESOURCETAGNVPROC glQueryResourceTagNV; + PFNGLCOMBINERPARAMETERFVNVPROC glCombinerParameterfvNV; + PFNGLCOMBINERPARAMETERFNVPROC glCombinerParameterfNV; + PFNGLCOMBINERPARAMETERIVNVPROC glCombinerParameterivNV; + PFNGLCOMBINERPARAMETERINVPROC glCombinerParameteriNV; + PFNGLCOMBINERINPUTNVPROC glCombinerInputNV; + PFNGLCOMBINEROUTPUTNVPROC glCombinerOutputNV; + PFNGLFINALCOMBINERINPUTNVPROC glFinalCombinerInputNV; + PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC glGetCombinerInputParameterfvNV; + PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC glGetCombinerInputParameterivNV; + PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC glGetCombinerOutputParameterfvNV; + PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC glGetCombinerOutputParameterivNV; + PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC glGetFinalCombinerInputParameterfvNV; + PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC glGetFinalCombinerInputParameterivNV; + PFNGLCOMBINERSTAGEPARAMETERFVNVPROC glCombinerStageParameterfvNV; + PFNGLGETCOMBINERSTAGEPARAMETERFVNVPROC glGetCombinerStageParameterfvNV; + PFNGLFRAMEBUFFERSAMPLELOCATIONSFVNVPROC glFramebufferSampleLocationsfvNV; + PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVNVPROC glNamedFramebufferSampleLocationsfvNV; + PFNGLRESOLVEDEPTHVALUESNVPROC glResolveDepthValuesNV; + PFNGLSCISSOREXCLUSIVENVPROC glScissorExclusiveNV; + PFNGLSCISSOREXCLUSIVEARRAYVNVPROC glScissorExclusiveArrayvNV; + PFNGLMAKEBUFFERRESIDENTNVPROC glMakeBufferResidentNV; + PFNGLMAKEBUFFERNONRESIDENTNVPROC glMakeBufferNonResidentNV; + PFNGLISBUFFERRESIDENTNVPROC glIsBufferResidentNV; + PFNGLMAKENAMEDBUFFERRESIDENTNVPROC glMakeNamedBufferResidentNV; + PFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC glMakeNamedBufferNonResidentNV; + PFNGLISNAMEDBUFFERRESIDENTNVPROC glIsNamedBufferResidentNV; + PFNGLGETBUFFERPARAMETERUI64VNVPROC glGetBufferParameterui64vNV; + PFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC glGetNamedBufferParameterui64vNV; + PFNGLGETINTEGERUI64VNVPROC glGetIntegerui64vNV; + PFNGLUNIFORMUI64NVPROC glUniformui64NV; + PFNGLUNIFORMUI64VNVPROC glUniformui64vNV; + PFNGLPROGRAMUNIFORMUI64NVPROC glProgramUniformui64NV; + PFNGLPROGRAMUNIFORMUI64VNVPROC glProgramUniformui64vNV; + PFNGLBINDSHADINGRATEIMAGENVPROC glBindShadingRateImageNV; + PFNGLGETSHADINGRATEIMAGEPALETTENVPROC glGetShadingRateImagePaletteNV; + PFNGLGETSHADINGRATESAMPLELOCATIONIVNVPROC glGetShadingRateSampleLocationivNV; + PFNGLSHADINGRATEIMAGEBARRIERNVPROC glShadingRateImageBarrierNV; + PFNGLSHADINGRATEIMAGEPALETTENVPROC glShadingRateImagePaletteNV; + PFNGLSHADINGRATESAMPLEORDERNVPROC glShadingRateSampleOrderNV; + PFNGLSHADINGRATESAMPLEORDERCUSTOMNVPROC glShadingRateSampleOrderCustomNV; + PFNGLTEXTUREBARRIERNVPROC glTextureBarrierNV; + PFNGLTEXIMAGE2DMULTISAMPLECOVERAGENVPROC glTexImage2DMultisampleCoverageNV; + PFNGLTEXIMAGE3DMULTISAMPLECOVERAGENVPROC glTexImage3DMultisampleCoverageNV; + PFNGLTEXTUREIMAGE2DMULTISAMPLENVPROC glTextureImage2DMultisampleNV; + PFNGLTEXTUREIMAGE3DMULTISAMPLENVPROC glTextureImage3DMultisampleNV; + PFNGLTEXTUREIMAGE2DMULTISAMPLECOVERAGENVPROC glTextureImage2DMultisampleCoverageNV; + PFNGLTEXTUREIMAGE3DMULTISAMPLECOVERAGENVPROC glTextureImage3DMultisampleCoverageNV; + PFNGLBEGINTRANSFORMFEEDBACKNVPROC glBeginTransformFeedbackNV; + PFNGLENDTRANSFORMFEEDBACKNVPROC glEndTransformFeedbackNV; + PFNGLTRANSFORMFEEDBACKATTRIBSNVPROC glTransformFeedbackAttribsNV; + PFNGLBINDBUFFERRANGENVPROC glBindBufferRangeNV; + PFNGLBINDBUFFEROFFSETNVPROC glBindBufferOffsetNV; + PFNGLBINDBUFFERBASENVPROC glBindBufferBaseNV; + PFNGLTRANSFORMFEEDBACKVARYINGSNVPROC glTransformFeedbackVaryingsNV; + PFNGLACTIVEVARYINGNVPROC glActiveVaryingNV; + PFNGLGETVARYINGLOCATIONNVPROC glGetVaryingLocationNV; + PFNGLGETACTIVEVARYINGNVPROC glGetActiveVaryingNV; + PFNGLGETTRANSFORMFEEDBACKVARYINGNVPROC glGetTransformFeedbackVaryingNV; + PFNGLTRANSFORMFEEDBACKSTREAMATTRIBSNVPROC glTransformFeedbackStreamAttribsNV; + PFNGLBINDTRANSFORMFEEDBACKNVPROC glBindTransformFeedbackNV; + PFNGLDELETETRANSFORMFEEDBACKSNVPROC glDeleteTransformFeedbacksNV; + PFNGLGENTRANSFORMFEEDBACKSNVPROC glGenTransformFeedbacksNV; + PFNGLISTRANSFORMFEEDBACKNVPROC glIsTransformFeedbackNV; + PFNGLPAUSETRANSFORMFEEDBACKNVPROC glPauseTransformFeedbackNV; + PFNGLRESUMETRANSFORMFEEDBACKNVPROC glResumeTransformFeedbackNV; + PFNGLDRAWTRANSFORMFEEDBACKNVPROC glDrawTransformFeedbackNV; + PFNGLVDPAUINITNVPROC glVDPAUInitNV; + PFNGLVDPAUFININVPROC glVDPAUFiniNV; + PFNGLVDPAUREGISTERVIDEOSURFACENVPROC glVDPAURegisterVideoSurfaceNV; + PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC glVDPAURegisterOutputSurfaceNV; + PFNGLVDPAUISSURFACENVPROC glVDPAUIsSurfaceNV; + PFNGLVDPAUUNREGISTERSURFACENVPROC glVDPAUUnregisterSurfaceNV; + PFNGLVDPAUGETSURFACEIVNVPROC glVDPAUGetSurfaceivNV; + PFNGLVDPAUSURFACEACCESSNVPROC glVDPAUSurfaceAccessNV; + PFNGLVDPAUMAPSURFACESNVPROC glVDPAUMapSurfacesNV; + PFNGLVDPAUUNMAPSURFACESNVPROC glVDPAUUnmapSurfacesNV; + PFNGLVDPAUREGISTERVIDEOSURFACEWITHPICTURESTRUCTURENVPROC glVDPAURegisterVideoSurfaceWithPictureStructureNV; + PFNGLFLUSHVERTEXARRAYRANGENVPROC glFlushVertexArrayRangeNV; + PFNGLVERTEXARRAYRANGENVPROC glVertexArrayRangeNV; + PFNGLVERTEXATTRIBL1I64NVPROC glVertexAttribL1i64NV; + PFNGLVERTEXATTRIBL2I64NVPROC glVertexAttribL2i64NV; + PFNGLVERTEXATTRIBL3I64NVPROC glVertexAttribL3i64NV; + PFNGLVERTEXATTRIBL4I64NVPROC glVertexAttribL4i64NV; + PFNGLVERTEXATTRIBL1I64VNVPROC glVertexAttribL1i64vNV; + PFNGLVERTEXATTRIBL2I64VNVPROC glVertexAttribL2i64vNV; + PFNGLVERTEXATTRIBL3I64VNVPROC glVertexAttribL3i64vNV; + PFNGLVERTEXATTRIBL4I64VNVPROC glVertexAttribL4i64vNV; + PFNGLVERTEXATTRIBL1UI64NVPROC glVertexAttribL1ui64NV; + PFNGLVERTEXATTRIBL2UI64NVPROC glVertexAttribL2ui64NV; + PFNGLVERTEXATTRIBL3UI64NVPROC glVertexAttribL3ui64NV; + PFNGLVERTEXATTRIBL4UI64NVPROC glVertexAttribL4ui64NV; + PFNGLVERTEXATTRIBL1UI64VNVPROC glVertexAttribL1ui64vNV; + PFNGLVERTEXATTRIBL2UI64VNVPROC glVertexAttribL2ui64vNV; + PFNGLVERTEXATTRIBL3UI64VNVPROC glVertexAttribL3ui64vNV; + PFNGLVERTEXATTRIBL4UI64VNVPROC glVertexAttribL4ui64vNV; + PFNGLGETVERTEXATTRIBLI64VNVPROC glGetVertexAttribLi64vNV; + PFNGLGETVERTEXATTRIBLUI64VNVPROC glGetVertexAttribLui64vNV; + PFNGLVERTEXATTRIBLFORMATNVPROC glVertexAttribLFormatNV; + PFNGLBUFFERADDRESSRANGENVPROC glBufferAddressRangeNV; + PFNGLVERTEXFORMATNVPROC glVertexFormatNV; + PFNGLNORMALFORMATNVPROC glNormalFormatNV; + PFNGLCOLORFORMATNVPROC glColorFormatNV; + PFNGLINDEXFORMATNVPROC glIndexFormatNV; + PFNGLTEXCOORDFORMATNVPROC glTexCoordFormatNV; + PFNGLEDGEFLAGFORMATNVPROC glEdgeFlagFormatNV; + PFNGLSECONDARYCOLORFORMATNVPROC glSecondaryColorFormatNV; + PFNGLFOGCOORDFORMATNVPROC glFogCoordFormatNV; + PFNGLVERTEXATTRIBFORMATNVPROC glVertexAttribFormatNV; + PFNGLVERTEXATTRIBIFORMATNVPROC glVertexAttribIFormatNV; + PFNGLGETINTEGERUI64I_VNVPROC glGetIntegerui64i_vNV; + PFNGLAREPROGRAMSRESIDENTNVPROC glAreProgramsResidentNV; + PFNGLBINDPROGRAMNVPROC glBindProgramNV; + PFNGLDELETEPROGRAMSNVPROC glDeleteProgramsNV; + PFNGLEXECUTEPROGRAMNVPROC glExecuteProgramNV; + PFNGLGENPROGRAMSNVPROC glGenProgramsNV; + PFNGLGETPROGRAMPARAMETERDVNVPROC glGetProgramParameterdvNV; + PFNGLGETPROGRAMPARAMETERFVNVPROC glGetProgramParameterfvNV; + PFNGLGETPROGRAMIVNVPROC glGetProgramivNV; + PFNGLGETPROGRAMSTRINGNVPROC glGetProgramStringNV; + PFNGLGETTRACKMATRIXIVNVPROC glGetTrackMatrixivNV; + PFNGLGETVERTEXATTRIBDVNVPROC glGetVertexAttribdvNV; + PFNGLGETVERTEXATTRIBFVNVPROC glGetVertexAttribfvNV; + PFNGLGETVERTEXATTRIBIVNVPROC glGetVertexAttribivNV; + PFNGLGETVERTEXATTRIBPOINTERVNVPROC glGetVertexAttribPointervNV; + PFNGLISPROGRAMNVPROC glIsProgramNV; + PFNGLLOADPROGRAMNVPROC glLoadProgramNV; + PFNGLPROGRAMPARAMETER4DNVPROC glProgramParameter4dNV; + PFNGLPROGRAMPARAMETER4DVNVPROC glProgramParameter4dvNV; + PFNGLPROGRAMPARAMETER4FNVPROC glProgramParameter4fNV; + PFNGLPROGRAMPARAMETER4FVNVPROC glProgramParameter4fvNV; + PFNGLPROGRAMPARAMETERS4DVNVPROC glProgramParameters4dvNV; + PFNGLPROGRAMPARAMETERS4FVNVPROC glProgramParameters4fvNV; + PFNGLREQUESTRESIDENTPROGRAMSNVPROC glRequestResidentProgramsNV; + PFNGLTRACKMATRIXNVPROC glTrackMatrixNV; + PFNGLVERTEXATTRIBPOINTERNVPROC glVertexAttribPointerNV; + PFNGLVERTEXATTRIB1DNVPROC glVertexAttrib1dNV; + PFNGLVERTEXATTRIB1DVNVPROC glVertexAttrib1dvNV; + PFNGLVERTEXATTRIB1FNVPROC glVertexAttrib1fNV; + PFNGLVERTEXATTRIB1FVNVPROC glVertexAttrib1fvNV; + PFNGLVERTEXATTRIB1SNVPROC glVertexAttrib1sNV; + PFNGLVERTEXATTRIB1SVNVPROC glVertexAttrib1svNV; + PFNGLVERTEXATTRIB2DNVPROC glVertexAttrib2dNV; + PFNGLVERTEXATTRIB2DVNVPROC glVertexAttrib2dvNV; + PFNGLVERTEXATTRIB2FNVPROC glVertexAttrib2fNV; + PFNGLVERTEXATTRIB2FVNVPROC glVertexAttrib2fvNV; + PFNGLVERTEXATTRIB2SNVPROC glVertexAttrib2sNV; + PFNGLVERTEXATTRIB2SVNVPROC glVertexAttrib2svNV; + PFNGLVERTEXATTRIB3DNVPROC glVertexAttrib3dNV; + PFNGLVERTEXATTRIB3DVNVPROC glVertexAttrib3dvNV; + PFNGLVERTEXATTRIB3FNVPROC glVertexAttrib3fNV; + PFNGLVERTEXATTRIB3FVNVPROC glVertexAttrib3fvNV; + PFNGLVERTEXATTRIB3SNVPROC glVertexAttrib3sNV; + PFNGLVERTEXATTRIB3SVNVPROC glVertexAttrib3svNV; + PFNGLVERTEXATTRIB4DNVPROC glVertexAttrib4dNV; + PFNGLVERTEXATTRIB4DVNVPROC glVertexAttrib4dvNV; + PFNGLVERTEXATTRIB4FNVPROC glVertexAttrib4fNV; + PFNGLVERTEXATTRIB4FVNVPROC glVertexAttrib4fvNV; + PFNGLVERTEXATTRIB4SNVPROC glVertexAttrib4sNV; + PFNGLVERTEXATTRIB4SVNVPROC glVertexAttrib4svNV; + PFNGLVERTEXATTRIB4UBNVPROC glVertexAttrib4ubNV; + PFNGLVERTEXATTRIB4UBVNVPROC glVertexAttrib4ubvNV; + PFNGLVERTEXATTRIBS1DVNVPROC glVertexAttribs1dvNV; + PFNGLVERTEXATTRIBS1FVNVPROC glVertexAttribs1fvNV; + PFNGLVERTEXATTRIBS1SVNVPROC glVertexAttribs1svNV; + PFNGLVERTEXATTRIBS2DVNVPROC glVertexAttribs2dvNV; + PFNGLVERTEXATTRIBS2FVNVPROC glVertexAttribs2fvNV; + PFNGLVERTEXATTRIBS2SVNVPROC glVertexAttribs2svNV; + PFNGLVERTEXATTRIBS3DVNVPROC glVertexAttribs3dvNV; + PFNGLVERTEXATTRIBS3FVNVPROC glVertexAttribs3fvNV; + PFNGLVERTEXATTRIBS3SVNVPROC glVertexAttribs3svNV; + PFNGLVERTEXATTRIBS4DVNVPROC glVertexAttribs4dvNV; + PFNGLVERTEXATTRIBS4FVNVPROC glVertexAttribs4fvNV; + PFNGLVERTEXATTRIBS4SVNVPROC glVertexAttribs4svNV; + PFNGLVERTEXATTRIBS4UBVNVPROC glVertexAttribs4ubvNV; + PFNGLVERTEXATTRIBI1IEXTPROC glVertexAttribI1iEXT; + PFNGLVERTEXATTRIBI2IEXTPROC glVertexAttribI2iEXT; + PFNGLVERTEXATTRIBI3IEXTPROC glVertexAttribI3iEXT; + PFNGLVERTEXATTRIBI4IEXTPROC glVertexAttribI4iEXT; + PFNGLVERTEXATTRIBI1UIEXTPROC glVertexAttribI1uiEXT; + PFNGLVERTEXATTRIBI2UIEXTPROC glVertexAttribI2uiEXT; + PFNGLVERTEXATTRIBI3UIEXTPROC glVertexAttribI3uiEXT; + PFNGLVERTEXATTRIBI4UIEXTPROC glVertexAttribI4uiEXT; + PFNGLVERTEXATTRIBI1IVEXTPROC glVertexAttribI1ivEXT; + PFNGLVERTEXATTRIBI2IVEXTPROC glVertexAttribI2ivEXT; + PFNGLVERTEXATTRIBI3IVEXTPROC glVertexAttribI3ivEXT; + PFNGLVERTEXATTRIBI4IVEXTPROC glVertexAttribI4ivEXT; + PFNGLVERTEXATTRIBI1UIVEXTPROC glVertexAttribI1uivEXT; + PFNGLVERTEXATTRIBI2UIVEXTPROC glVertexAttribI2uivEXT; + PFNGLVERTEXATTRIBI3UIVEXTPROC glVertexAttribI3uivEXT; + PFNGLVERTEXATTRIBI4UIVEXTPROC glVertexAttribI4uivEXT; + PFNGLVERTEXATTRIBI4BVEXTPROC glVertexAttribI4bvEXT; + PFNGLVERTEXATTRIBI4SVEXTPROC glVertexAttribI4svEXT; + PFNGLVERTEXATTRIBI4UBVEXTPROC glVertexAttribI4ubvEXT; + PFNGLVERTEXATTRIBI4USVEXTPROC glVertexAttribI4usvEXT; + PFNGLVERTEXATTRIBIPOINTEREXTPROC glVertexAttribIPointerEXT; + PFNGLGETVERTEXATTRIBIIVEXTPROC glGetVertexAttribIivEXT; + PFNGLGETVERTEXATTRIBIUIVEXTPROC glGetVertexAttribIuivEXT; + PFNGLBEGINVIDEOCAPTURENVPROC glBeginVideoCaptureNV; + PFNGLBINDVIDEOCAPTURESTREAMBUFFERNVPROC glBindVideoCaptureStreamBufferNV; + PFNGLBINDVIDEOCAPTURESTREAMTEXTURENVPROC glBindVideoCaptureStreamTextureNV; + PFNGLENDVIDEOCAPTURENVPROC glEndVideoCaptureNV; + PFNGLGETVIDEOCAPTUREIVNVPROC glGetVideoCaptureivNV; + PFNGLGETVIDEOCAPTURESTREAMIVNVPROC glGetVideoCaptureStreamivNV; + PFNGLGETVIDEOCAPTURESTREAMFVNVPROC glGetVideoCaptureStreamfvNV; + PFNGLGETVIDEOCAPTURESTREAMDVNVPROC glGetVideoCaptureStreamdvNV; + PFNGLVIDEOCAPTURENVPROC glVideoCaptureNV; + PFNGLVIDEOCAPTURESTREAMPARAMETERIVNVPROC glVideoCaptureStreamParameterivNV; + PFNGLVIDEOCAPTURESTREAMPARAMETERFVNVPROC glVideoCaptureStreamParameterfvNV; + PFNGLVIDEOCAPTURESTREAMPARAMETERDVNVPROC glVideoCaptureStreamParameterdvNV; + PFNGLVIEWPORTSWIZZLENVPROC glViewportSwizzleNV; + PFNGLMULTITEXCOORD1BOESPROC glMultiTexCoord1bOES; + PFNGLMULTITEXCOORD1BVOESPROC glMultiTexCoord1bvOES; + PFNGLMULTITEXCOORD2BOESPROC glMultiTexCoord2bOES; + PFNGLMULTITEXCOORD2BVOESPROC glMultiTexCoord2bvOES; + PFNGLMULTITEXCOORD3BOESPROC glMultiTexCoord3bOES; + PFNGLMULTITEXCOORD3BVOESPROC glMultiTexCoord3bvOES; + PFNGLMULTITEXCOORD4BOESPROC glMultiTexCoord4bOES; + PFNGLMULTITEXCOORD4BVOESPROC glMultiTexCoord4bvOES; + PFNGLTEXCOORD1BOESPROC glTexCoord1bOES; + PFNGLTEXCOORD1BVOESPROC glTexCoord1bvOES; + PFNGLTEXCOORD2BOESPROC glTexCoord2bOES; + PFNGLTEXCOORD2BVOESPROC glTexCoord2bvOES; + PFNGLTEXCOORD3BOESPROC glTexCoord3bOES; + PFNGLTEXCOORD3BVOESPROC glTexCoord3bvOES; + PFNGLTEXCOORD4BOESPROC glTexCoord4bOES; + PFNGLTEXCOORD4BVOESPROC glTexCoord4bvOES; + PFNGLVERTEX2BOESPROC glVertex2bOES; + PFNGLVERTEX2BVOESPROC glVertex2bvOES; + PFNGLVERTEX3BOESPROC glVertex3bOES; + PFNGLVERTEX3BVOESPROC glVertex3bvOES; + PFNGLVERTEX4BOESPROC glVertex4bOES; + PFNGLVERTEX4BVOESPROC glVertex4bvOES; + PFNGLALPHAFUNCXOESPROC glAlphaFuncxOES; + PFNGLCLEARCOLORXOESPROC glClearColorxOES; + PFNGLCLEARDEPTHXOESPROC glClearDepthxOES; + PFNGLCLIPPLANEXOESPROC glClipPlanexOES; + PFNGLCOLOR4XOESPROC glColor4xOES; + PFNGLDEPTHRANGEXOESPROC glDepthRangexOES; + PFNGLFOGXOESPROC glFogxOES; + PFNGLFOGXVOESPROC glFogxvOES; + PFNGLFRUSTUMXOESPROC glFrustumxOES; + PFNGLGETCLIPPLANEXOESPROC glGetClipPlanexOES; + PFNGLGETFIXEDVOESPROC glGetFixedvOES; + PFNGLGETTEXENVXVOESPROC glGetTexEnvxvOES; + PFNGLGETTEXPARAMETERXVOESPROC glGetTexParameterxvOES; + PFNGLLIGHTMODELXOESPROC glLightModelxOES; + PFNGLLIGHTMODELXVOESPROC glLightModelxvOES; + PFNGLLIGHTXOESPROC glLightxOES; + PFNGLLIGHTXVOESPROC glLightxvOES; + PFNGLLINEWIDTHXOESPROC glLineWidthxOES; + PFNGLLOADMATRIXXOESPROC glLoadMatrixxOES; + PFNGLMATERIALXOESPROC glMaterialxOES; + PFNGLMATERIALXVOESPROC glMaterialxvOES; + PFNGLMULTMATRIXXOESPROC glMultMatrixxOES; + PFNGLMULTITEXCOORD4XOESPROC glMultiTexCoord4xOES; + PFNGLNORMAL3XOESPROC glNormal3xOES; + PFNGLORTHOXOESPROC glOrthoxOES; + PFNGLPOINTPARAMETERXVOESPROC glPointParameterxvOES; + PFNGLPOINTSIZEXOESPROC glPointSizexOES; + PFNGLPOLYGONOFFSETXOESPROC glPolygonOffsetxOES; + PFNGLROTATEXOESPROC glRotatexOES; + PFNGLSCALEXOESPROC glScalexOES; + PFNGLTEXENVXOESPROC glTexEnvxOES; + PFNGLTEXENVXVOESPROC glTexEnvxvOES; + PFNGLTEXPARAMETERXOESPROC glTexParameterxOES; + PFNGLTEXPARAMETERXVOESPROC glTexParameterxvOES; + PFNGLTRANSLATEXOESPROC glTranslatexOES; + PFNGLGETLIGHTXVOESPROC glGetLightxvOES; + PFNGLGETMATERIALXVOESPROC glGetMaterialxvOES; + PFNGLPOINTPARAMETERXOESPROC glPointParameterxOES; + PFNGLSAMPLECOVERAGEXOESPROC glSampleCoveragexOES; + PFNGLACCUMXOESPROC glAccumxOES; + PFNGLBITMAPXOESPROC glBitmapxOES; + PFNGLBLENDCOLORXOESPROC glBlendColorxOES; + PFNGLCLEARACCUMXOESPROC glClearAccumxOES; + PFNGLCOLOR3XOESPROC glColor3xOES; + PFNGLCOLOR3XVOESPROC glColor3xvOES; + PFNGLCOLOR4XVOESPROC glColor4xvOES; + PFNGLCONVOLUTIONPARAMETERXOESPROC glConvolutionParameterxOES; + PFNGLCONVOLUTIONPARAMETERXVOESPROC glConvolutionParameterxvOES; + PFNGLEVALCOORD1XOESPROC glEvalCoord1xOES; + PFNGLEVALCOORD1XVOESPROC glEvalCoord1xvOES; + PFNGLEVALCOORD2XOESPROC glEvalCoord2xOES; + PFNGLEVALCOORD2XVOESPROC glEvalCoord2xvOES; + PFNGLFEEDBACKBUFFERXOESPROC glFeedbackBufferxOES; + PFNGLGETCONVOLUTIONPARAMETERXVOESPROC glGetConvolutionParameterxvOES; + PFNGLGETHISTOGRAMPARAMETERXVOESPROC glGetHistogramParameterxvOES; + PFNGLGETLIGHTXOESPROC glGetLightxOES; + PFNGLGETMAPXVOESPROC glGetMapxvOES; + PFNGLGETMATERIALXOESPROC glGetMaterialxOES; + PFNGLGETPIXELMAPXVPROC glGetPixelMapxv; + PFNGLGETTEXGENXVOESPROC glGetTexGenxvOES; + PFNGLGETTEXLEVELPARAMETERXVOESPROC glGetTexLevelParameterxvOES; + PFNGLINDEXXOESPROC glIndexxOES; + PFNGLINDEXXVOESPROC glIndexxvOES; + PFNGLLOADTRANSPOSEMATRIXXOESPROC glLoadTransposeMatrixxOES; + PFNGLMAP1XOESPROC glMap1xOES; + PFNGLMAP2XOESPROC glMap2xOES; + PFNGLMAPGRID1XOESPROC glMapGrid1xOES; + PFNGLMAPGRID2XOESPROC glMapGrid2xOES; + PFNGLMULTTRANSPOSEMATRIXXOESPROC glMultTransposeMatrixxOES; + PFNGLMULTITEXCOORD1XOESPROC glMultiTexCoord1xOES; + PFNGLMULTITEXCOORD1XVOESPROC glMultiTexCoord1xvOES; + PFNGLMULTITEXCOORD2XOESPROC glMultiTexCoord2xOES; + PFNGLMULTITEXCOORD2XVOESPROC glMultiTexCoord2xvOES; + PFNGLMULTITEXCOORD3XOESPROC glMultiTexCoord3xOES; + PFNGLMULTITEXCOORD3XVOESPROC glMultiTexCoord3xvOES; + PFNGLMULTITEXCOORD4XVOESPROC glMultiTexCoord4xvOES; + PFNGLNORMAL3XVOESPROC glNormal3xvOES; + PFNGLPASSTHROUGHXOESPROC glPassThroughxOES; + PFNGLPIXELMAPXPROC glPixelMapx; + PFNGLPIXELSTOREXPROC glPixelStorex; + PFNGLPIXELTRANSFERXOESPROC glPixelTransferxOES; + PFNGLPIXELZOOMXOESPROC glPixelZoomxOES; + PFNGLPRIORITIZETEXTURESXOESPROC glPrioritizeTexturesxOES; + PFNGLRASTERPOS2XOESPROC glRasterPos2xOES; + PFNGLRASTERPOS2XVOESPROC glRasterPos2xvOES; + PFNGLRASTERPOS3XOESPROC glRasterPos3xOES; + PFNGLRASTERPOS3XVOESPROC glRasterPos3xvOES; + PFNGLRASTERPOS4XOESPROC glRasterPos4xOES; + PFNGLRASTERPOS4XVOESPROC glRasterPos4xvOES; + PFNGLRECTXOESPROC glRectxOES; + PFNGLRECTXVOESPROC glRectxvOES; + PFNGLTEXCOORD1XOESPROC glTexCoord1xOES; + PFNGLTEXCOORD1XVOESPROC glTexCoord1xvOES; + PFNGLTEXCOORD2XOESPROC glTexCoord2xOES; + PFNGLTEXCOORD2XVOESPROC glTexCoord2xvOES; + PFNGLTEXCOORD3XOESPROC glTexCoord3xOES; + PFNGLTEXCOORD3XVOESPROC glTexCoord3xvOES; + PFNGLTEXCOORD4XOESPROC glTexCoord4xOES; + PFNGLTEXCOORD4XVOESPROC glTexCoord4xvOES; + PFNGLTEXGENXOESPROC glTexGenxOES; + PFNGLTEXGENXVOESPROC glTexGenxvOES; + PFNGLVERTEX2XOESPROC glVertex2xOES; + PFNGLVERTEX2XVOESPROC glVertex2xvOES; + PFNGLVERTEX3XOESPROC glVertex3xOES; + PFNGLVERTEX3XVOESPROC glVertex3xvOES; + PFNGLVERTEX4XOESPROC glVertex4xOES; + PFNGLVERTEX4XVOESPROC glVertex4xvOES; + PFNGLQUERYMATRIXXOESPROC glQueryMatrixxOES; + PFNGLCLEARDEPTHFOESPROC glClearDepthfOES; + PFNGLCLIPPLANEFOESPROC glClipPlanefOES; + PFNGLDEPTHRANGEFOESPROC glDepthRangefOES; + PFNGLFRUSTUMFOESPROC glFrustumfOES; + PFNGLGETCLIPPLANEFOESPROC glGetClipPlanefOES; + PFNGLORTHOFOESPROC glOrthofOES; + PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR; + PFNGLHINTPGIPROC glHintPGI; + PFNGLDETAILTEXFUNCSGISPROC glDetailTexFuncSGIS; + PFNGLGETDETAILTEXFUNCSGISPROC glGetDetailTexFuncSGIS; + PFNGLFOGFUNCSGISPROC glFogFuncSGIS; + PFNGLGETFOGFUNCSGISPROC glGetFogFuncSGIS; + PFNGLSAMPLEMASKSGISPROC glSampleMaskSGIS; + PFNGLSAMPLEPATTERNSGISPROC glSamplePatternSGIS; + PFNGLPIXELTEXGENPARAMETERISGISPROC glPixelTexGenParameteriSGIS; + PFNGLPIXELTEXGENPARAMETERIVSGISPROC glPixelTexGenParameterivSGIS; + PFNGLPIXELTEXGENPARAMETERFSGISPROC glPixelTexGenParameterfSGIS; + PFNGLPIXELTEXGENPARAMETERFVSGISPROC glPixelTexGenParameterfvSGIS; + PFNGLGETPIXELTEXGENPARAMETERIVSGISPROC glGetPixelTexGenParameterivSGIS; + PFNGLGETPIXELTEXGENPARAMETERFVSGISPROC glGetPixelTexGenParameterfvSGIS; + PFNGLPOINTPARAMETERFSGISPROC glPointParameterfSGIS; + PFNGLPOINTPARAMETERFVSGISPROC glPointParameterfvSGIS; + PFNGLSHARPENTEXFUNCSGISPROC glSharpenTexFuncSGIS; + PFNGLGETSHARPENTEXFUNCSGISPROC glGetSharpenTexFuncSGIS; + PFNGLTEXIMAGE4DSGISPROC glTexImage4DSGIS; + PFNGLTEXSUBIMAGE4DSGISPROC glTexSubImage4DSGIS; + PFNGLTEXTURECOLORMASKSGISPROC glTextureColorMaskSGIS; + PFNGLGETTEXFILTERFUNCSGISPROC glGetTexFilterFuncSGIS; + PFNGLTEXFILTERFUNCSGISPROC glTexFilterFuncSGIS; + PFNGLASYNCMARKERSGIXPROC glAsyncMarkerSGIX; + PFNGLFINISHASYNCSGIXPROC glFinishAsyncSGIX; + PFNGLPOLLASYNCSGIXPROC glPollAsyncSGIX; + PFNGLGENASYNCMARKERSSGIXPROC glGenAsyncMarkersSGIX; + PFNGLDELETEASYNCMARKERSSGIXPROC glDeleteAsyncMarkersSGIX; + PFNGLISASYNCMARKERSGIXPROC glIsAsyncMarkerSGIX; + PFNGLFLUSHRASTERSGIXPROC glFlushRasterSGIX; + PFNGLFRAGMENTCOLORMATERIALSGIXPROC glFragmentColorMaterialSGIX; + PFNGLFRAGMENTLIGHTFSGIXPROC glFragmentLightfSGIX; + PFNGLFRAGMENTLIGHTFVSGIXPROC glFragmentLightfvSGIX; + PFNGLFRAGMENTLIGHTISGIXPROC glFragmentLightiSGIX; + PFNGLFRAGMENTLIGHTIVSGIXPROC glFragmentLightivSGIX; + PFNGLFRAGMENTLIGHTMODELFSGIXPROC glFragmentLightModelfSGIX; + PFNGLFRAGMENTLIGHTMODELFVSGIXPROC glFragmentLightModelfvSGIX; + PFNGLFRAGMENTLIGHTMODELISGIXPROC glFragmentLightModeliSGIX; + PFNGLFRAGMENTLIGHTMODELIVSGIXPROC glFragmentLightModelivSGIX; + PFNGLFRAGMENTMATERIALFSGIXPROC glFragmentMaterialfSGIX; + PFNGLFRAGMENTMATERIALFVSGIXPROC glFragmentMaterialfvSGIX; + PFNGLFRAGMENTMATERIALISGIXPROC glFragmentMaterialiSGIX; + PFNGLFRAGMENTMATERIALIVSGIXPROC glFragmentMaterialivSGIX; + PFNGLGETFRAGMENTLIGHTFVSGIXPROC glGetFragmentLightfvSGIX; + PFNGLGETFRAGMENTLIGHTIVSGIXPROC glGetFragmentLightivSGIX; + PFNGLGETFRAGMENTMATERIALFVSGIXPROC glGetFragmentMaterialfvSGIX; + PFNGLGETFRAGMENTMATERIALIVSGIXPROC glGetFragmentMaterialivSGIX; + PFNGLLIGHTENVISGIXPROC glLightEnviSGIX; + PFNGLFRAMEZOOMSGIXPROC glFrameZoomSGIX; + PFNGLIGLOOINTERFACESGIXPROC glIglooInterfaceSGIX; + PFNGLGETINSTRUMENTSSGIXPROC glGetInstrumentsSGIX; + PFNGLINSTRUMENTSBUFFERSGIXPROC glInstrumentsBufferSGIX; + PFNGLPOLLINSTRUMENTSSGIXPROC glPollInstrumentsSGIX; + PFNGLREADINSTRUMENTSSGIXPROC glReadInstrumentsSGIX; + PFNGLSTARTINSTRUMENTSSGIXPROC glStartInstrumentsSGIX; + PFNGLSTOPINSTRUMENTSSGIXPROC glStopInstrumentsSGIX; + PFNGLGETLISTPARAMETERFVSGIXPROC glGetListParameterfvSGIX; + PFNGLGETLISTPARAMETERIVSGIXPROC glGetListParameterivSGIX; + PFNGLLISTPARAMETERFSGIXPROC glListParameterfSGIX; + PFNGLLISTPARAMETERFVSGIXPROC glListParameterfvSGIX; + PFNGLLISTPARAMETERISGIXPROC glListParameteriSGIX; + PFNGLLISTPARAMETERIVSGIXPROC glListParameterivSGIX; + PFNGLPIXELTEXGENSGIXPROC glPixelTexGenSGIX; + PFNGLDEFORMATIONMAP3DSGIXPROC glDeformationMap3dSGIX; + PFNGLDEFORMATIONMAP3FSGIXPROC glDeformationMap3fSGIX; + PFNGLDEFORMSGIXPROC glDeformSGIX; + PFNGLLOADIDENTITYDEFORMATIONMAPSGIXPROC glLoadIdentityDeformationMapSGIX; + PFNGLREFERENCEPLANESGIXPROC glReferencePlaneSGIX; + PFNGLSPRITEPARAMETERFSGIXPROC glSpriteParameterfSGIX; + PFNGLSPRITEPARAMETERFVSGIXPROC glSpriteParameterfvSGIX; + PFNGLSPRITEPARAMETERISGIXPROC glSpriteParameteriSGIX; + PFNGLSPRITEPARAMETERIVSGIXPROC glSpriteParameterivSGIX; + PFNGLTAGSAMPLEBUFFERSGIXPROC glTagSampleBufferSGIX; + PFNGLCOLORTABLESGIPROC glColorTableSGI; + PFNGLCOLORTABLEPARAMETERFVSGIPROC glColorTableParameterfvSGI; + PFNGLCOLORTABLEPARAMETERIVSGIPROC glColorTableParameterivSGI; + PFNGLCOPYCOLORTABLESGIPROC glCopyColorTableSGI; + PFNGLGETCOLORTABLESGIPROC glGetColorTableSGI; + PFNGLGETCOLORTABLEPARAMETERFVSGIPROC glGetColorTableParameterfvSGI; + PFNGLGETCOLORTABLEPARAMETERIVSGIPROC glGetColorTableParameterivSGI; + PFNGLFINISHTEXTURESUNXPROC glFinishTextureSUNX; + PFNGLGLOBALALPHAFACTORBSUNPROC glGlobalAlphaFactorbSUN; + PFNGLGLOBALALPHAFACTORSSUNPROC glGlobalAlphaFactorsSUN; + PFNGLGLOBALALPHAFACTORISUNPROC glGlobalAlphaFactoriSUN; + PFNGLGLOBALALPHAFACTORFSUNPROC glGlobalAlphaFactorfSUN; + PFNGLGLOBALALPHAFACTORDSUNPROC glGlobalAlphaFactordSUN; + PFNGLGLOBALALPHAFACTORUBSUNPROC glGlobalAlphaFactorubSUN; + PFNGLGLOBALALPHAFACTORUSSUNPROC glGlobalAlphaFactorusSUN; + PFNGLGLOBALALPHAFACTORUISUNPROC glGlobalAlphaFactoruiSUN; + PFNGLDRAWMESHARRAYSSUNPROC glDrawMeshArraysSUN; + PFNGLREPLACEMENTCODEUISUNPROC glReplacementCodeuiSUN; + PFNGLREPLACEMENTCODEUSSUNPROC glReplacementCodeusSUN; + PFNGLREPLACEMENTCODEUBSUNPROC glReplacementCodeubSUN; + PFNGLREPLACEMENTCODEUIVSUNPROC glReplacementCodeuivSUN; + PFNGLREPLACEMENTCODEUSVSUNPROC glReplacementCodeusvSUN; + PFNGLREPLACEMENTCODEUBVSUNPROC glReplacementCodeubvSUN; + PFNGLREPLACEMENTCODEPOINTERSUNPROC glReplacementCodePointerSUN; + PFNGLCOLOR4UBVERTEX2FSUNPROC glColor4ubVertex2fSUN; + PFNGLCOLOR4UBVERTEX2FVSUNPROC glColor4ubVertex2fvSUN; + PFNGLCOLOR4UBVERTEX3FSUNPROC glColor4ubVertex3fSUN; + PFNGLCOLOR4UBVERTEX3FVSUNPROC glColor4ubVertex3fvSUN; + PFNGLCOLOR3FVERTEX3FSUNPROC glColor3fVertex3fSUN; + PFNGLCOLOR3FVERTEX3FVSUNPROC glColor3fVertex3fvSUN; + PFNGLNORMAL3FVERTEX3FSUNPROC glNormal3fVertex3fSUN; + PFNGLNORMAL3FVERTEX3FVSUNPROC glNormal3fVertex3fvSUN; + PFNGLCOLOR4FNORMAL3FVERTEX3FSUNPROC glColor4fNormal3fVertex3fSUN; + PFNGLCOLOR4FNORMAL3FVERTEX3FVSUNPROC glColor4fNormal3fVertex3fvSUN; + PFNGLTEXCOORD2FVERTEX3FSUNPROC glTexCoord2fVertex3fSUN; + PFNGLTEXCOORD2FVERTEX3FVSUNPROC glTexCoord2fVertex3fvSUN; + PFNGLTEXCOORD4FVERTEX4FSUNPROC glTexCoord4fVertex4fSUN; + PFNGLTEXCOORD4FVERTEX4FVSUNPROC glTexCoord4fVertex4fvSUN; + PFNGLTEXCOORD2FCOLOR4UBVERTEX3FSUNPROC glTexCoord2fColor4ubVertex3fSUN; + PFNGLTEXCOORD2FCOLOR4UBVERTEX3FVSUNPROC glTexCoord2fColor4ubVertex3fvSUN; + PFNGLTEXCOORD2FCOLOR3FVERTEX3FSUNPROC glTexCoord2fColor3fVertex3fSUN; + PFNGLTEXCOORD2FCOLOR3FVERTEX3FVSUNPROC glTexCoord2fColor3fVertex3fvSUN; + PFNGLTEXCOORD2FNORMAL3FVERTEX3FSUNPROC glTexCoord2fNormal3fVertex3fSUN; + PFNGLTEXCOORD2FNORMAL3FVERTEX3FVSUNPROC glTexCoord2fNormal3fVertex3fvSUN; + PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC glTexCoord2fColor4fNormal3fVertex3fSUN; + PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC glTexCoord2fColor4fNormal3fVertex3fvSUN; + PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FSUNPROC glTexCoord4fColor4fNormal3fVertex4fSUN; + PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FVSUNPROC glTexCoord4fColor4fNormal3fVertex4fvSUN; + PFNGLREPLACEMENTCODEUIVERTEX3FSUNPROC glReplacementCodeuiVertex3fSUN; + PFNGLREPLACEMENTCODEUIVERTEX3FVSUNPROC glReplacementCodeuiVertex3fvSUN; + PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FSUNPROC glReplacementCodeuiColor4ubVertex3fSUN; + PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FVSUNPROC glReplacementCodeuiColor4ubVertex3fvSUN; + PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FSUNPROC glReplacementCodeuiColor3fVertex3fSUN; + PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FVSUNPROC glReplacementCodeuiColor3fVertex3fvSUN; + PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FSUNPROC glReplacementCodeuiNormal3fVertex3fSUN; + PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FVSUNPROC glReplacementCodeuiNormal3fVertex3fvSUN; + PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FSUNPROC glReplacementCodeuiColor4fNormal3fVertex3fSUN; + PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FVSUNPROC glReplacementCodeuiColor4fNormal3fVertex3fvSUN; + PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FSUNPROC glReplacementCodeuiTexCoord2fVertex3fSUN; + PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FVSUNPROC glReplacementCodeuiTexCoord2fVertex3fvSUN; + PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FSUNPROC glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN; + PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FVSUNPROC glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN; + PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN; + PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN; +#if defined(GLBIND_WGL) + PFNWGLSETSTEREOEMITTERSTATE3DLPROC wglSetStereoEmitterState3DL; + PFNWGLGETGPUIDSAMDPROC wglGetGPUIDsAMD; + PFNWGLGETGPUINFOAMDPROC wglGetGPUInfoAMD; + PFNWGLGETCONTEXTGPUIDAMDPROC wglGetContextGPUIDAMD; + PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC wglCreateAssociatedContextAMD; + PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC wglCreateAssociatedContextAttribsAMD; + PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC wglDeleteAssociatedContextAMD; + PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC wglMakeAssociatedContextCurrentAMD; + PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC wglGetCurrentAssociatedContextAMD; + PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC wglBlitContextFramebufferAMD; + PFNWGLCREATEBUFFERREGIONARBPROC wglCreateBufferRegionARB; + PFNWGLDELETEBUFFERREGIONARBPROC wglDeleteBufferRegionARB; + PFNWGLSAVEBUFFERREGIONARBPROC wglSaveBufferRegionARB; + PFNWGLRESTOREBUFFERREGIONARBPROC wglRestoreBufferRegionARB; + PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; + PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB; + PFNWGLMAKECONTEXTCURRENTARBPROC wglMakeContextCurrentARB; + PFNWGLGETCURRENTREADDCARBPROC wglGetCurrentReadDCARB; + PFNWGLCREATEPBUFFERARBPROC wglCreatePbufferARB; + PFNWGLGETPBUFFERDCARBPROC wglGetPbufferDCARB; + PFNWGLRELEASEPBUFFERDCARBPROC wglReleasePbufferDCARB; + PFNWGLDESTROYPBUFFERARBPROC wglDestroyPbufferARB; + PFNWGLQUERYPBUFFERARBPROC wglQueryPbufferARB; + PFNWGLGETPIXELFORMATATTRIBIVARBPROC wglGetPixelFormatAttribivARB; + PFNWGLGETPIXELFORMATATTRIBFVARBPROC wglGetPixelFormatAttribfvARB; + PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; + PFNWGLBINDTEXIMAGEARBPROC wglBindTexImageARB; + PFNWGLRELEASETEXIMAGEARBPROC wglReleaseTexImageARB; + PFNWGLSETPBUFFERATTRIBARBPROC wglSetPbufferAttribARB; + PFNWGLCREATEDISPLAYCOLORTABLEEXTPROC wglCreateDisplayColorTableEXT; + PFNWGLLOADDISPLAYCOLORTABLEEXTPROC wglLoadDisplayColorTableEXT; + PFNWGLBINDDISPLAYCOLORTABLEEXTPROC wglBindDisplayColorTableEXT; + PFNWGLDESTROYDISPLAYCOLORTABLEEXTPROC wglDestroyDisplayColorTableEXT; + PFNWGLGETEXTENSIONSSTRINGEXTPROC wglGetExtensionsStringEXT; + PFNWGLMAKECONTEXTCURRENTEXTPROC wglMakeContextCurrentEXT; + PFNWGLGETCURRENTREADDCEXTPROC wglGetCurrentReadDCEXT; + PFNWGLCREATEPBUFFEREXTPROC wglCreatePbufferEXT; + PFNWGLGETPBUFFERDCEXTPROC wglGetPbufferDCEXT; + PFNWGLRELEASEPBUFFERDCEXTPROC wglReleasePbufferDCEXT; + PFNWGLDESTROYPBUFFEREXTPROC wglDestroyPbufferEXT; + PFNWGLQUERYPBUFFEREXTPROC wglQueryPbufferEXT; + PFNWGLGETPIXELFORMATATTRIBIVEXTPROC wglGetPixelFormatAttribivEXT; + PFNWGLGETPIXELFORMATATTRIBFVEXTPROC wglGetPixelFormatAttribfvEXT; + PFNWGLCHOOSEPIXELFORMATEXTPROC wglChoosePixelFormatEXT; + PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT; + PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT; + PFNWGLGETDIGITALVIDEOPARAMETERSI3DPROC wglGetDigitalVideoParametersI3D; + PFNWGLSETDIGITALVIDEOPARAMETERSI3DPROC wglSetDigitalVideoParametersI3D; + PFNWGLGETGAMMATABLEPARAMETERSI3DPROC wglGetGammaTableParametersI3D; + PFNWGLSETGAMMATABLEPARAMETERSI3DPROC wglSetGammaTableParametersI3D; + PFNWGLGETGAMMATABLEI3DPROC wglGetGammaTableI3D; + PFNWGLSETGAMMATABLEI3DPROC wglSetGammaTableI3D; + PFNWGLENABLEGENLOCKI3DPROC wglEnableGenlockI3D; + PFNWGLDISABLEGENLOCKI3DPROC wglDisableGenlockI3D; + PFNWGLISENABLEDGENLOCKI3DPROC wglIsEnabledGenlockI3D; + PFNWGLGENLOCKSOURCEI3DPROC wglGenlockSourceI3D; + PFNWGLGETGENLOCKSOURCEI3DPROC wglGetGenlockSourceI3D; + PFNWGLGENLOCKSOURCEEDGEI3DPROC wglGenlockSourceEdgeI3D; + PFNWGLGETGENLOCKSOURCEEDGEI3DPROC wglGetGenlockSourceEdgeI3D; + PFNWGLGENLOCKSAMPLERATEI3DPROC wglGenlockSampleRateI3D; + PFNWGLGETGENLOCKSAMPLERATEI3DPROC wglGetGenlockSampleRateI3D; + PFNWGLGENLOCKSOURCEDELAYI3DPROC wglGenlockSourceDelayI3D; + PFNWGLGETGENLOCKSOURCEDELAYI3DPROC wglGetGenlockSourceDelayI3D; + PFNWGLQUERYGENLOCKMAXSOURCEDELAYI3DPROC wglQueryGenlockMaxSourceDelayI3D; + PFNWGLCREATEIMAGEBUFFERI3DPROC wglCreateImageBufferI3D; + PFNWGLDESTROYIMAGEBUFFERI3DPROC wglDestroyImageBufferI3D; + PFNWGLASSOCIATEIMAGEBUFFEREVENTSI3DPROC wglAssociateImageBufferEventsI3D; + PFNWGLRELEASEIMAGEBUFFEREVENTSI3DPROC wglReleaseImageBufferEventsI3D; + PFNWGLENABLEFRAMELOCKI3DPROC wglEnableFrameLockI3D; + PFNWGLDISABLEFRAMELOCKI3DPROC wglDisableFrameLockI3D; + PFNWGLISENABLEDFRAMELOCKI3DPROC wglIsEnabledFrameLockI3D; + PFNWGLQUERYFRAMELOCKMASTERI3DPROC wglQueryFrameLockMasterI3D; + PFNWGLGETFRAMEUSAGEI3DPROC wglGetFrameUsageI3D; + PFNWGLBEGINFRAMETRACKINGI3DPROC wglBeginFrameTrackingI3D; + PFNWGLENDFRAMETRACKINGI3DPROC wglEndFrameTrackingI3D; + PFNWGLQUERYFRAMETRACKINGI3DPROC wglQueryFrameTrackingI3D; + PFNWGLCOPYIMAGESUBDATANVPROC wglCopyImageSubDataNV; + PFNWGLDELAYBEFORESWAPNVPROC wglDelayBeforeSwapNV; + PFNWGLDXSETRESOURCESHAREHANDLENVPROC wglDXSetResourceShareHandleNV; + PFNWGLDXOPENDEVICENVPROC wglDXOpenDeviceNV; + PFNWGLDXCLOSEDEVICENVPROC wglDXCloseDeviceNV; + PFNWGLDXREGISTEROBJECTNVPROC wglDXRegisterObjectNV; + PFNWGLDXUNREGISTEROBJECTNVPROC wglDXUnregisterObjectNV; + PFNWGLDXOBJECTACCESSNVPROC wglDXObjectAccessNV; + PFNWGLDXLOCKOBJECTSNVPROC wglDXLockObjectsNV; + PFNWGLDXUNLOCKOBJECTSNVPROC wglDXUnlockObjectsNV; + PFNWGLENUMGPUSNVPROC wglEnumGpusNV; + PFNWGLENUMGPUDEVICESNVPROC wglEnumGpuDevicesNV; + PFNWGLCREATEAFFINITYDCNVPROC wglCreateAffinityDCNV; + PFNWGLENUMGPUSFROMAFFINITYDCNVPROC wglEnumGpusFromAffinityDCNV; + PFNWGLDELETEDCNVPROC wglDeleteDCNV; + PFNWGLENUMERATEVIDEODEVICESNVPROC wglEnumerateVideoDevicesNV; + PFNWGLBINDVIDEODEVICENVPROC wglBindVideoDeviceNV; + PFNWGLQUERYCURRENTCONTEXTNVPROC wglQueryCurrentContextNV; + PFNWGLJOINSWAPGROUPNVPROC wglJoinSwapGroupNV; + PFNWGLBINDSWAPBARRIERNVPROC wglBindSwapBarrierNV; + PFNWGLQUERYSWAPGROUPNVPROC wglQuerySwapGroupNV; + PFNWGLQUERYMAXSWAPGROUPSNVPROC wglQueryMaxSwapGroupsNV; + PFNWGLQUERYFRAMECOUNTNVPROC wglQueryFrameCountNV; + PFNWGLRESETFRAMECOUNTNVPROC wglResetFrameCountNV; + PFNWGLBINDVIDEOCAPTUREDEVICENVPROC wglBindVideoCaptureDeviceNV; + PFNWGLENUMERATEVIDEOCAPTUREDEVICESNVPROC wglEnumerateVideoCaptureDevicesNV; + PFNWGLLOCKVIDEOCAPTUREDEVICENVPROC wglLockVideoCaptureDeviceNV; + PFNWGLQUERYVIDEOCAPTUREDEVICENVPROC wglQueryVideoCaptureDeviceNV; + PFNWGLRELEASEVIDEOCAPTUREDEVICENVPROC wglReleaseVideoCaptureDeviceNV; + PFNWGLGETVIDEODEVICENVPROC wglGetVideoDeviceNV; + PFNWGLRELEASEVIDEODEVICENVPROC wglReleaseVideoDeviceNV; + PFNWGLBINDVIDEOIMAGENVPROC wglBindVideoImageNV; + PFNWGLRELEASEVIDEOIMAGENVPROC wglReleaseVideoImageNV; + PFNWGLSENDPBUFFERTOVIDEONVPROC wglSendPbufferToVideoNV; + PFNWGLGETVIDEOINFONVPROC wglGetVideoInfoNV; + PFNWGLALLOCATEMEMORYNVPROC wglAllocateMemoryNV; + PFNWGLFREEMEMORYNVPROC wglFreeMemoryNV; + PFNWGLGETSYNCVALUESOMLPROC wglGetSyncValuesOML; + PFNWGLGETMSCRATEOMLPROC wglGetMscRateOML; + PFNWGLSWAPBUFFERSMSCOMLPROC wglSwapBuffersMscOML; + PFNWGLSWAPLAYERBUFFERSMSCOMLPROC wglSwapLayerBuffersMscOML; + PFNWGLWAITFORMSCOMLPROC wglWaitForMscOML; + PFNWGLWAITFORSBCOMLPROC wglWaitForSbcOML; +#endif /* GLBIND_WGL */ +#if defined(GLBIND_GLX) + PFNGLXGETGPUIDSAMDPROC glXGetGPUIDsAMD; + PFNGLXGETGPUINFOAMDPROC glXGetGPUInfoAMD; + PFNGLXGETCONTEXTGPUIDAMDPROC glXGetContextGPUIDAMD; + PFNGLXCREATEASSOCIATEDCONTEXTAMDPROC glXCreateAssociatedContextAMD; + PFNGLXCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC glXCreateAssociatedContextAttribsAMD; + PFNGLXDELETEASSOCIATEDCONTEXTAMDPROC glXDeleteAssociatedContextAMD; + PFNGLXMAKEASSOCIATEDCONTEXTCURRENTAMDPROC glXMakeAssociatedContextCurrentAMD; + PFNGLXGETCURRENTASSOCIATEDCONTEXTAMDPROC glXGetCurrentAssociatedContextAMD; + PFNGLXBLITCONTEXTFRAMEBUFFERAMDPROC glXBlitContextFramebufferAMD; + PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB; + PFNGLXGETPROCADDRESSARBPROC glXGetProcAddressARB; + PFNGLXGETCURRENTDISPLAYEXTPROC glXGetCurrentDisplayEXT; + PFNGLXQUERYCONTEXTINFOEXTPROC glXQueryContextInfoEXT; + PFNGLXGETCONTEXTIDEXTPROC glXGetContextIDEXT; + PFNGLXIMPORTCONTEXTEXTPROC glXImportContextEXT; + PFNGLXFREECONTEXTEXTPROC glXFreeContextEXT; + PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT; + PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT; + PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT; + PFNGLXGETAGPOFFSETMESAPROC glXGetAGPOffsetMESA; + PFNGLXCOPYSUBBUFFERMESAPROC glXCopySubBufferMESA; + PFNGLXCREATEGLXPIXMAPMESAPROC glXCreateGLXPixmapMESA; + PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC glXQueryCurrentRendererIntegerMESA; + PFNGLXQUERYCURRENTRENDERERSTRINGMESAPROC glXQueryCurrentRendererStringMESA; + PFNGLXQUERYRENDERERINTEGERMESAPROC glXQueryRendererIntegerMESA; + PFNGLXQUERYRENDERERSTRINGMESAPROC glXQueryRendererStringMESA; + PFNGLXRELEASEBUFFERSMESAPROC glXReleaseBuffersMESA; + PFNGLXSET3DFXMODEMESAPROC glXSet3DfxModeMESA; + PFNGLXGETSWAPINTERVALMESAPROC glXGetSwapIntervalMESA; + PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA; + PFNGLXCOPYBUFFERSUBDATANVPROC glXCopyBufferSubDataNV; + PFNGLXNAMEDCOPYBUFFERSUBDATANVPROC glXNamedCopyBufferSubDataNV; + PFNGLXCOPYIMAGESUBDATANVPROC glXCopyImageSubDataNV; + PFNGLXDELAYBEFORESWAPNVPROC glXDelayBeforeSwapNV; + PFNGLXENUMERATEVIDEODEVICESNVPROC glXEnumerateVideoDevicesNV; + PFNGLXBINDVIDEODEVICENVPROC glXBindVideoDeviceNV; + PFNGLXJOINSWAPGROUPNVPROC glXJoinSwapGroupNV; + PFNGLXBINDSWAPBARRIERNVPROC glXBindSwapBarrierNV; + PFNGLXQUERYSWAPGROUPNVPROC glXQuerySwapGroupNV; + PFNGLXQUERYMAXSWAPGROUPSNVPROC glXQueryMaxSwapGroupsNV; + PFNGLXQUERYFRAMECOUNTNVPROC glXQueryFrameCountNV; + PFNGLXRESETFRAMECOUNTNVPROC glXResetFrameCountNV; + PFNGLXBINDVIDEOCAPTUREDEVICENVPROC glXBindVideoCaptureDeviceNV; + PFNGLXENUMERATEVIDEOCAPTUREDEVICESNVPROC glXEnumerateVideoCaptureDevicesNV; + PFNGLXLOCKVIDEOCAPTUREDEVICENVPROC glXLockVideoCaptureDeviceNV; + PFNGLXQUERYVIDEOCAPTUREDEVICENVPROC glXQueryVideoCaptureDeviceNV; + PFNGLXRELEASEVIDEOCAPTUREDEVICENVPROC glXReleaseVideoCaptureDeviceNV; + PFNGLXGETVIDEODEVICENVPROC glXGetVideoDeviceNV; + PFNGLXRELEASEVIDEODEVICENVPROC glXReleaseVideoDeviceNV; + PFNGLXBINDVIDEOIMAGENVPROC glXBindVideoImageNV; + PFNGLXRELEASEVIDEOIMAGENVPROC glXReleaseVideoImageNV; + PFNGLXSENDPBUFFERTOVIDEONVPROC glXSendPbufferToVideoNV; + PFNGLXGETVIDEOINFONVPROC glXGetVideoInfoNV; + PFNGLXGETSYNCVALUESOMLPROC glXGetSyncValuesOML; + PFNGLXGETMSCRATEOMLPROC glXGetMscRateOML; + PFNGLXSWAPBUFFERSMSCOMLPROC glXSwapBuffersMscOML; + PFNGLXWAITFORMSCOMLPROC glXWaitForMscOML; + PFNGLXWAITFORSBCOMLPROC glXWaitForSbcOML; + PFNGLXCUSHIONSGIPROC glXCushionSGI; + PFNGLXMAKECURRENTREADSGIPROC glXMakeCurrentReadSGI; + PFNGLXGETCURRENTREADDRAWABLESGIPROC glXGetCurrentReadDrawableSGI; + PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI; + PFNGLXGETVIDEOSYNCSGIPROC glXGetVideoSyncSGI; + PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI; + PFNGLXGETFBCONFIGATTRIBSGIXPROC glXGetFBConfigAttribSGIX; + PFNGLXCHOOSEFBCONFIGSGIXPROC glXChooseFBConfigSGIX; + PFNGLXCREATEGLXPIXMAPWITHCONFIGSGIXPROC glXCreateGLXPixmapWithConfigSGIX; + PFNGLXCREATECONTEXTWITHCONFIGSGIXPROC glXCreateContextWithConfigSGIX; + PFNGLXGETVISUALFROMFBCONFIGSGIXPROC glXGetVisualFromFBConfigSGIX; + PFNGLXGETFBCONFIGFROMVISUALSGIXPROC glXGetFBConfigFromVisualSGIX; + PFNGLXQUERYHYPERPIPENETWORKSGIXPROC glXQueryHyperpipeNetworkSGIX; + PFNGLXHYPERPIPECONFIGSGIXPROC glXHyperpipeConfigSGIX; + PFNGLXQUERYHYPERPIPECONFIGSGIXPROC glXQueryHyperpipeConfigSGIX; + PFNGLXDESTROYHYPERPIPECONFIGSGIXPROC glXDestroyHyperpipeConfigSGIX; + PFNGLXBINDHYPERPIPESGIXPROC glXBindHyperpipeSGIX; + PFNGLXQUERYHYPERPIPEBESTATTRIBSGIXPROC glXQueryHyperpipeBestAttribSGIX; + PFNGLXHYPERPIPEATTRIBSGIXPROC glXHyperpipeAttribSGIX; + PFNGLXQUERYHYPERPIPEATTRIBSGIXPROC glXQueryHyperpipeAttribSGIX; + PFNGLXCREATEGLXPBUFFERSGIXPROC glXCreateGLXPbufferSGIX; + PFNGLXDESTROYGLXPBUFFERSGIXPROC glXDestroyGLXPbufferSGIX; + PFNGLXQUERYGLXPBUFFERSGIXPROC glXQueryGLXPbufferSGIX; + PFNGLXSELECTEVENTSGIXPROC glXSelectEventSGIX; + PFNGLXGETSELECTEDEVENTSGIXPROC glXGetSelectedEventSGIX; + PFNGLXBINDSWAPBARRIERSGIXPROC glXBindSwapBarrierSGIX; + PFNGLXQUERYMAXSWAPBARRIERSSGIXPROC glXQueryMaxSwapBarriersSGIX; + PFNGLXJOINSWAPGROUPSGIXPROC glXJoinSwapGroupSGIX; + PFNGLXBINDCHANNELTOWINDOWSGIXPROC glXBindChannelToWindowSGIX; + PFNGLXCHANNELRECTSGIXPROC glXChannelRectSGIX; + PFNGLXQUERYCHANNELRECTSGIXPROC glXQueryChannelRectSGIX; + PFNGLXQUERYCHANNELDELTASSGIXPROC glXQueryChannelDeltasSGIX; + PFNGLXCHANNELRECTSYNCSGIXPROC glXChannelRectSyncSGIX; + PFNGLXGETTRANSPARENTINDEXSUNPROC glXGetTransparentIndexSUN; +#endif /* GLBIND_GLX */ +} GLBapi; + +typedef struct +{ + GLboolean singleBuffered; +#if defined(GLBIND_WGL) + HWND hWnd; +#endif +#if defined(GLBIND_GLX) + glbind_Display* pDisplay; +#endif +} GLBconfig; + +/* +Initializes a config object which can later be passed to glbInit() to configure the rendering context that's created by glbInit(). +*/ +GLBconfig glbConfigInit(); + +/* +Initializes glbind and attempts to load APIs statically. + +pAPI is optional. On output it will contain pointers to all OpenGL APIs found by the loader. + +This will initialize a dummy rendering context and make it current. It will also bind API's to global scope. If you want to load +APIs based on a specific rendering context, use glbInitContextAPI(). Then you can, optionally, call glbBindAPI() to bind those +APIs to global scope. + +This is not thread-safe. You can call this multiple times, but each call must be matched with a call to glbUninit(). The first +time this is called it will bind the APIs to global scope. + +The internal rendering context can be used like normal. It will be created in double-buffered mode. You can also create your own +context, but you may want to consider calling glbInitContextAPI() or glbInitCurrentContextAPI() after the fact to ensure function +pointers are valid for that context. + +You can configure the internal rendering context by specifying a GLBconfig object. This can NULL in which case it will use +defaults. Initialize the config object with glbConfigInit(). The default config creates a context with 32-bit color, 24-bit depth, +8-bit stencil and double-buffered. +*/ +GLenum glbInit(GLBapi* pAPI, GLBconfig* pConfig); + +/* +Loads context-specific APIs into the specified API object. + +This does not bind these APIs to global scope. Use glbBindAPI() for this. +*/ +#if defined(GLBIND_WGL) +GLenum glbInitContextAPI(HDC dc, HGLRC rc, GLBapi* pAPI); +#endif +#if defined(GLBIND_GLX) +GLenum glbInitContextAPI(glbind_Display* dpy, GLXDrawable drawable, GLXContext rc, GLBapi* pAPI); +#endif + +/* +Loads context-specific APIs from the current context into the specified API object. + +This does not bind these APIs to global scope. Use glbBindAPI() for this. +*/ +GLenum glbInitCurrentContextAPI(GLBapi* pAPI); + +/* +Uninitializes glbind. + +Each call to glbInit() must be matched up with a call to glbUninit(). +*/ +void glbUninit(); + +/* +Binds the function pointers in pAPI to global scope. +*/ +GLenum glbBindAPI(const GLBapi* pAPI); + +/* Platform-specific APIs. */ +#if defined(GLBIND_WGL) +/* +Retrieves the rendering context that was created on the first call to glbInit(). +*/ +HGLRC glbGetRC(); + +/* +Retrieves the device context of the dummy window that was created with the first call to glbInit(). + +You can use this function for creating another rendering context without having to create your own dummy window. +*/ +HDC glbGetDC(); + +/* +Retrieves the pixel format that's being used by the rendering context that was created on the first call to glbInit(). +*/ +int glbGetPixelFormat(); + +/* +Retrieves the pixel format descriptor being used by the rendering context that was created on the first call to glbInit(). +*/ +PIXELFORMATDESCRIPTOR* glbGetPFD(); +#endif + +#if defined(GLBIND_GLX) +/* +Retrieves a reference to the global Display that was created with the first call to glbInit(). If the display was set +in the config object, that Display will be returned. +*/ +glbind_Display* glbGetDisplay(); + +/* +Retrieves the rendering context that was created on the first call to glbInit(). +*/ +GLXContext glbGetRC(); + +/* +Retrieves the color map that was created on the first call to glbInit(). +*/ +glbind_Colormap glbGetColormap(); + +/* +Retrieves the framebuffer visual info that was created on the first call to glbInit(). +*/ +glbind_XVisualInfo* glbGetFBVisualInfo(); +#endif + +#ifdef __cplusplus +} +#endif + +/* +Helper API for checking if an extension is supported based on the current rendering context. + +This checks cross-platform extensions, WGL extensions and GLX extensions (in that order). + +pAPI is optional. If non-null, this relevant APIs from this object will be used. Otherwise, whatever is bound to global +scope will be used. +*/ +GLboolean glbIsExtensionSupported(GLBapi* pAPI, const char* extensionName); + +#endif /* GLBIND_H */ + + +/****************************************************************************** + ****************************************************************************** + + IMPLEMENTATION + + ****************************************************************************** + ******************************************************************************/ +#ifdef GLBIND_IMPLEMENTATION +#if defined(GLBIND_WGL) +#endif +#if defined(GLBIND_GLX) + #include + #include +#endif + +typedef void* GLBhandle; +typedef void (* GLBproc)(void); + +void glbZeroMemory(void* p, size_t sz) +{ + size_t i; + for (i = 0; i < sz; ++i) { + ((GLbyte*)p)[i] = 0; + } +} + +#define glbZeroObject(p) glbZeroMemory((p), sizeof(*(p))); + +GLBhandle glb_dlopen(const char* filename) +{ +#ifdef _WIN32 + return (GLBhandle)LoadLibraryA(filename); +#else + return (GLBhandle)dlopen(filename, RTLD_NOW); +#endif +} + +void glb_dlclose(GLBhandle handle) +{ +#ifdef _WIN32 + FreeLibrary((HMODULE)handle); +#else + dlclose((void*)handle); +#endif +} + +GLBproc glb_dlsym(GLBhandle handle, const char* symbol) +{ +#ifdef _WIN32 + return (GLBproc)GetProcAddress((HMODULE)handle, symbol); +#else +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" +#endif + return (GLBproc)dlsym((void*)handle, symbol); +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) + #pragma GCC diagnostic pop +#endif +#endif +} + + +static unsigned int g_glbInitCount = 0; +static GLBhandle g_glbOpenGLSO = NULL; + +#if defined(GLBIND_WGL) +HWND glbind_DummyHWND = 0; +HDC glbind_DC = 0; +HGLRC glbind_RC = 0; +PIXELFORMATDESCRIPTOR glbind_PFD; +int glbind_PixelFormat; + +static LRESULT GLBIND_DummyWindowProcWin32(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return DefWindowProc(hWnd, msg, wParam, lParam); +} +#endif +#if defined(GLBIND_GLX) +glbind_Display* glbind_pDisplay = 0; +glbind_Window glbind_DummyWindow = 0; +GLXContext glbind_RC = 0; +glbind_Colormap glbind_DummyColormap = 0; +glbind_XVisualInfo* glbind_pFBVisualInfo = 0; +GLboolean glbind_OwnsDisplay = GL_FALSE; +#endif + +#if defined(GLBIND_WGL) +PFNWGLCREATECONTEXTPROC glbind_wglCreateContext; +PFNWGLDELETECONTEXTPROC glbind_wglDeleteContext; +PFNWGLGETCURRENTCONTEXTPROC glbind_wglGetCurrentContext; +PFNWGLGETCURRENTDCPROC glbind_wglGetCurrentDC; +PFNWGLGETPROCADDRESSPROC glbind_wglGetProcAddress; +PFNWGLMAKECURRENTPROC glbind_wglMakeCurrent; + +static GLBhandle g_glbGdi32DLL = NULL; +PFNCHOOSEPIXELFORMATPROC glbind_ChoosePixelFormat; +PFNSETPIXELFORMATPROC glbind_SetPixelFormat; +PFNSWAPBUFFERSPROC glbind_SwapBuffers; +#endif +#if defined(GLBIND_GLX) +/* We need to define our own function types for the glX*() functions so they use our glbind_Display, etc. types instead of the normal types. */ +typedef glbind_XVisualInfo* (* GLB_PFNGLXCHOOSEVISUALPROC) (glbind_Display* pDisplay, int screen, int* pAttribList); +typedef GLXContext (* GLB_PFNGLXCREATECONTEXTPROC) (glbind_Display* pDisplay, glbind_XVisualInfo* pVisual, GLXContext shareList, GLboolean direct); +typedef void (* GLB_PFNGLXDESTROYCONTEXTPROC) (glbind_Display* pDisplay, GLXContext context); +typedef GLboolean (* GLB_PFNGLXMAKECURRENTPROC) (glbind_Display* pDisplay, GLXDrawable drawable, GLXContext context); +typedef void (* GLB_PFNGLXSWAPBUFFERSPROC) (glbind_Display* pDisplay, GLXDrawable drawable); +typedef GLXContext (* GLB_PFNGLXGETCURRENTCONTEXTPROC) (void); +typedef const char* (* GLB_PFNGLXQUERYEXTENSIONSSTRINGPROC) (glbind_Display* pDisplay, int screen); +typedef glbind_Display* (* GLB_PFNGLXGETCURRENTDISPLAYPROC) (void); +typedef GLXDrawable (* GLB_PFNGLXGETCURRENTDRAWABLEPROC) (void); +typedef glbind_XVisualInfo* (* GLB_PFNGLXGETVISUALFROMFBCONFIGPROC) (glbind_Display* pDisplay, GLXFBConfig config); +typedef GLXFBConfig* (* GLB_PFNGLXCHOOSEFBCONFIGPROC) (glbind_Display* pDisplay, int screen, const int* pAttribList, int* pCount); +typedef GLBproc (* GLB_PFNGLXGETPROCADDRESSPROC) (const GLubyte* pName); + +/* Declare our global functions using the types above. */ +GLB_PFNGLXCHOOSEVISUALPROC glbind_glXChooseVisual; +GLB_PFNGLXCREATECONTEXTPROC glbind_glXCreateContext; +GLB_PFNGLXDESTROYCONTEXTPROC glbind_glXDestroyContext; +GLB_PFNGLXMAKECURRENTPROC glbind_glXMakeCurrent; +GLB_PFNGLXSWAPBUFFERSPROC glbind_glXSwapBuffers; +GLB_PFNGLXGETCURRENTCONTEXTPROC glbind_glXGetCurrentContext; +GLB_PFNGLXQUERYEXTENSIONSSTRINGPROC glbind_glXQueryExtensionsString; +GLB_PFNGLXGETCURRENTDISPLAYPROC glbind_glXGetCurrentDisplay; +GLB_PFNGLXGETCURRENTDRAWABLEPROC glbind_glXGetCurrentDrawable; +GLB_PFNGLXGETVISUALFROMFBCONFIGPROC glbind_glXGetVisualFromFBConfig; +GLB_PFNGLXCHOOSEFBCONFIGPROC glbind_glXChooseFBConfig; +GLB_PFNGLXGETPROCADDRESSPROC glbind_glXGetProcAddress; + + +static GLBhandle g_glbX11SO = NULL; +typedef glbind_Display* (* GLB_PFNXOPENDISPLAYPROC) (const char* pDisplayName); +typedef int (* GLB_PFNXCLOSEDISPLAYPROC) (glbind_Display* pDisplay); +typedef glbind_Window (* GLB_PFNXCREATEWINDOWPROC) (glbind_Display* pDisplay, glbind_Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int borderWidth, int depth, unsigned int windowClass, glbind_Visual* pVisual, unsigned long valueMask, glbind_XSetWindowAttributes* pAttributes); +typedef int (* GLB_PFNXDESTROYWINDOWPROC) (glbind_Display* pDisplay, glbind_Window window); +typedef glbind_Colormap (* GLB_PFNXCREATECOLORMAPPROC)(glbind_Display* pDisplay, glbind_Window window, glbind_Visual* pVisual, int alloc); +typedef int (* GLB_PFNXFREECOLORMAPPROC) (glbind_Display* pDisplay, glbind_Colormap colormap); +typedef int (* GLB_PFNXDEFAULTSCREENPROC) (glbind_Display* pDisplay); +typedef glbind_Window (* GLB_PFNXROOTWINDOWPROC) (glbind_Display* pDisplay, int screenNumber); + +GLB_PFNXOPENDISPLAYPROC glbind_XOpenDisplay; +GLB_PFNXCLOSEDISPLAYPROC glbind_XCloseDisplay; +GLB_PFNXCREATEWINDOWPROC glbind_XCreateWindow; +GLB_PFNXDESTROYWINDOWPROC glbind_XDestroyWindow; +GLB_PFNXCREATECOLORMAPPROC glbind_XCreateColormap; +GLB_PFNXFREECOLORMAPPROC glbind_XFreeColormap; +GLB_PFNXDEFAULTSCREENPROC glbind_XDefaultScreen; +GLB_PFNXROOTWINDOWPROC glbind_XRootWindow; +#endif + +GLBproc glbGetProcAddress(const char* name) +{ + GLBproc func = NULL; +#if defined(GLBIND_WGL) + if (glbind_wglGetProcAddress) { + func = (GLBproc)glbind_wglGetProcAddress(name); + } +#endif +#if defined(GLBIND_GLX) + if (glbind_glXGetProcAddress) { + func = (GLBproc)glbind_glXGetProcAddress((const GLubyte*)name); + } +#endif + + if (func == NULL) { + func = glb_dlsym(g_glbOpenGLSO, name); + } + + return func; +} + +GLenum glbLoadOpenGLSO() +{ + GLenum result; + size_t i; + + const char* openGLSONames[] = { +#if defined(_WIN32) + "OpenGL32.dll" +#elif defined(__APPLE__) +#else + "libGL.so.1", + "libGL.so" +#endif + }; + + result = GL_INVALID_OPERATION; + for (i = 0; i < sizeof(openGLSONames)/sizeof(openGLSONames[0]); ++i) { + GLBhandle handle = glb_dlopen(openGLSONames[i]); + if (handle != NULL) { + g_glbOpenGLSO = handle; + result = GL_NO_ERROR; + break; + } + } + + if (result != GL_NO_ERROR) { + return result; + } + + /* Runtime linking for platform-specific libraries. */ + { + #if defined(_WIN32) + /* Win32 */ + g_glbGdi32DLL = glb_dlopen("gdi32.dll"); + if (g_glbGdi32DLL == NULL) { + glb_dlclose(g_glbOpenGLSO); + g_glbOpenGLSO = NULL; + return GL_INVALID_OPERATION; + } + #elif defined(__APPLE_) + /* Apple */ + #else + /* X11 */ + const char* x11SONames[] = { + "libX11.so", + "libX11.so.6" + }; + + result = GL_INVALID_OPERATION; + for (i = 0; i < sizeof(openGLSONames)/sizeof(openGLSONames[0]); ++i) { + GLBhandle handle = glb_dlopen(x11SONames[i]); + if (handle != NULL) { + g_glbX11SO = handle; + result = GL_NO_ERROR; + break; + } + } + #endif + } + + if (result != GL_NO_ERROR) { + glb_dlclose(g_glbOpenGLSO); + g_glbOpenGLSO = NULL; + } + + return result; +} + +void glbUnloadOpenGLSO() +{ + if (g_glbOpenGLSO == NULL) { + return; + } + + /* Unload platform-specific libraries. */ + { + #if defined(_WIN32) + /* Win32 */ + glb_dlclose(g_glbGdi32DLL); + g_glbGdi32DLL = NULL; + #elif defined(__APPLE_) + /* Apple */ + #else + /* X11 */ + glb_dlclose(g_glbX11SO); + g_glbX11SO = NULL; + #endif + } + + glb_dlclose(g_glbOpenGLSO); + g_glbOpenGLSO = NULL; +} + +GLBconfig glbConfigInit() +{ + GLBconfig config; + glbZeroObject(&config); + + return config; +} + +GLenum glbInit(GLBapi* pAPI, GLBconfig* pConfig) +{ + GLenum result; + + if (g_glbInitCount == 0) { + result = glbLoadOpenGLSO(); + if (result != GL_NO_ERROR) { + return result; + } + + /* Here is where we need to initialize some core APIs. We need these to initialize dummy objects and whatnot. */ +#if defined(GLBIND_WGL) + glbind_wglCreateContext = (PFNWGLCREATECONTEXTPROC )glb_dlsym(g_glbOpenGLSO, "wglCreateContext"); + glbind_wglDeleteContext = (PFNWGLDELETECONTEXTPROC )glb_dlsym(g_glbOpenGLSO, "wglDeleteContext"); + glbind_wglGetCurrentContext = (PFNWGLGETCURRENTCONTEXTPROC)glb_dlsym(g_glbOpenGLSO, "wglGetCurrentContext"); + glbind_wglGetCurrentDC = (PFNWGLGETCURRENTDCPROC )glb_dlsym(g_glbOpenGLSO, "wglGetCurrentDC"); + glbind_wglGetProcAddress = (PFNWGLGETPROCADDRESSPROC )glb_dlsym(g_glbOpenGLSO, "wglGetProcAddress"); + glbind_wglMakeCurrent = (PFNWGLMAKECURRENTPROC )glb_dlsym(g_glbOpenGLSO, "wglMakeCurrent"); + + if (glbind_wglCreateContext == NULL || + glbind_wglDeleteContext == NULL || + glbind_wglGetCurrentContext == NULL || + glbind_wglGetCurrentDC == NULL || + glbind_wglGetProcAddress == NULL || + glbind_wglMakeCurrent == NULL) { + glbUnloadOpenGLSO(); + return GL_INVALID_OPERATION; + } + + glbind_ChoosePixelFormat = (PFNCHOOSEPIXELFORMATPROC)glb_dlsym(g_glbGdi32DLL, "ChoosePixelFormat"); + glbind_SetPixelFormat = (PFNSETPIXELFORMATPROC )glb_dlsym(g_glbGdi32DLL, "SetPixelFormat"); + glbind_SwapBuffers = (PFNSWAPBUFFERSPROC )glb_dlsym(g_glbGdi32DLL, "SwapBuffers"); + + if (glbind_ChoosePixelFormat == NULL || + glbind_SetPixelFormat == NULL || + glbind_SwapBuffers == NULL) { + glbUnloadOpenGLSO(); + return GL_INVALID_OPERATION; + } +#endif +#if defined(GLBIND_GLX) + glbind_glXChooseVisual = (GLB_PFNGLXCHOOSEVISUALPROC )glb_dlsym(g_glbOpenGLSO, "glXChooseVisual"); + glbind_glXCreateContext = (GLB_PFNGLXCREATECONTEXTPROC )glb_dlsym(g_glbOpenGLSO, "glXCreateContext"); + glbind_glXDestroyContext = (GLB_PFNGLXDESTROYCONTEXTPROC )glb_dlsym(g_glbOpenGLSO, "glXDestroyContext"); + glbind_glXMakeCurrent = (GLB_PFNGLXMAKECURRENTPROC )glb_dlsym(g_glbOpenGLSO, "glXMakeCurrent"); + glbind_glXSwapBuffers = (GLB_PFNGLXSWAPBUFFERSPROC )glb_dlsym(g_glbOpenGLSO, "glXSwapBuffers"); + glbind_glXGetCurrentContext = (GLB_PFNGLXGETCURRENTCONTEXTPROC )glb_dlsym(g_glbOpenGLSO, "glXGetCurrentContext"); + glbind_glXQueryExtensionsString = (GLB_PFNGLXQUERYEXTENSIONSSTRINGPROC)glb_dlsym(g_glbOpenGLSO, "glXQueryExtensionsString"); + glbind_glXGetCurrentDisplay = (GLB_PFNGLXGETCURRENTDISPLAYPROC )glb_dlsym(g_glbOpenGLSO, "glXGetCurrentDisplay"); + glbind_glXGetCurrentDrawable = (GLB_PFNGLXGETCURRENTDRAWABLEPROC )glb_dlsym(g_glbOpenGLSO, "glXGetCurrentDrawable"); + glbind_glXChooseFBConfig = (GLB_PFNGLXCHOOSEFBCONFIGPROC )glb_dlsym(g_glbOpenGLSO, "glXChooseFBConfig"); + glbind_glXGetVisualFromFBConfig = (GLB_PFNGLXGETVISUALFROMFBCONFIGPROC)glb_dlsym(g_glbOpenGLSO, "glXGetVisualFromFBConfig"); + glbind_glXGetProcAddress = (GLB_PFNGLXGETPROCADDRESSPROC )glb_dlsym(g_glbOpenGLSO, "glXGetProcAddress"); + + if (glbind_glXChooseVisual == NULL || + glbind_glXCreateContext == NULL || + glbind_glXDestroyContext == NULL || + glbind_glXMakeCurrent == NULL || + glbind_glXSwapBuffers == NULL || + glbind_glXGetCurrentContext == NULL || + glbind_glXQueryExtensionsString == NULL || + glbind_glXGetCurrentDisplay == NULL || + glbind_glXGetCurrentDrawable == NULL || + glbind_glXChooseFBConfig == NULL || + glbind_glXGetVisualFromFBConfig == NULL || + glbind_glXGetProcAddress == NULL) { + glbUnloadOpenGLSO(); + return GL_INVALID_OPERATION; + } + + glbind_XOpenDisplay = (GLB_PFNXOPENDISPLAYPROC )glb_dlsym(g_glbX11SO, "XOpenDisplay"); + glbind_XCloseDisplay = (GLB_PFNXCLOSEDISPLAYPROC )glb_dlsym(g_glbX11SO, "XCloseDisplay"); + glbind_XCreateWindow = (GLB_PFNXCREATEWINDOWPROC )glb_dlsym(g_glbX11SO, "XCreateWindow"); + glbind_XDestroyWindow = (GLB_PFNXDESTROYWINDOWPROC )glb_dlsym(g_glbX11SO, "XDestroyWindow"); + glbind_XCreateColormap = (GLB_PFNXCREATECOLORMAPPROC)glb_dlsym(g_glbX11SO, "XCreateColormap"); + glbind_XFreeColormap = (GLB_PFNXFREECOLORMAPPROC )glb_dlsym(g_glbX11SO, "XFreeColormap"); + glbind_XDefaultScreen = (GLB_PFNXDEFAULTSCREENPROC )glb_dlsym(g_glbX11SO, "XDefaultScreen"); + glbind_XRootWindow = (GLB_PFNXROOTWINDOWPROC )glb_dlsym(g_glbX11SO, "XRootWindow"); + + if (glbind_XOpenDisplay == NULL || + glbind_XCloseDisplay == NULL || + glbind_XCreateWindow == NULL || + glbind_XDestroyWindow == NULL || + glbind_XCreateColormap == NULL || + glbind_XFreeColormap == NULL || + glbind_XDefaultScreen == NULL || + glbind_XRootWindow == NULL) { + glbUnloadOpenGLSO(); + return GL_INVALID_OPERATION; + } +#endif + + /* Here is where we need to initialize our dummy objects so we can get a context and retrieve some API pointers. */ +#if defined(GLBIND_WGL) + { + HWND hWnd = NULL; + + if (pConfig != NULL) { + hWnd = pConfig->hWnd; + } + + /* Create a dummy window if we haven't passed in an explicit window. */ + if (hWnd == NULL) { + WNDCLASSEXW dummyWC; + memset(&dummyWC, 0, sizeof(dummyWC)); + dummyWC.cbSize = sizeof(dummyWC); + dummyWC.lpfnWndProc = (WNDPROC)GLBIND_DummyWindowProcWin32; + dummyWC.lpszClassName = L"GLBIND_DummyHWND"; + dummyWC.style = CS_OWNDC; + if (!RegisterClassExW(&dummyWC)) { + glbUnloadOpenGLSO(); + return GL_INVALID_OPERATION; + } + + hWnd = CreateWindowExW(0, L"GLBIND_DummyHWND", L"", 0, 0, 0, 0, 0, NULL, NULL, GetModuleHandle(NULL), NULL); + glbind_DummyHWND = hWnd; + } + + glbind_DC = GetDC(hWnd); + + memset(&glbind_PFD, 0, sizeof(glbind_PFD)); + glbind_PFD.nSize = sizeof(glbind_PFD); + glbind_PFD.nVersion = 1; + glbind_PFD.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | ((pConfig == NULL || pConfig->singleBuffered == GL_FALSE) ? PFD_DOUBLEBUFFER : 0); + glbind_PFD.iPixelType = PFD_TYPE_RGBA; + glbind_PFD.cStencilBits = 8; + glbind_PFD.cDepthBits = 24; + glbind_PFD.cColorBits = 32; + glbind_PixelFormat = glbind_ChoosePixelFormat(glbind_DC, &glbind_PFD); + if (glbind_PixelFormat == 0) { + DestroyWindow(hWnd); + glbUnloadOpenGLSO(); + return GL_INVALID_OPERATION; + } + + if (!glbind_SetPixelFormat(glbind_DC, glbind_PixelFormat, &glbind_PFD)) { + DestroyWindow(hWnd); + glbUnloadOpenGLSO(); + return GL_INVALID_OPERATION; + } + + glbind_RC = glbind_wglCreateContext(glbind_DC); + if (glbind_RC == NULL) { + DestroyWindow(hWnd); + glbUnloadOpenGLSO(); + return GL_INVALID_OPERATION; + } + + glbind_wglMakeCurrent(glbind_DC, glbind_RC); + } +#endif + +#if defined(GLBIND_GLX) + { + static int attribs[] = { + GLX_RGBA, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 8, + GLX_DEPTH_SIZE, 24, + GLX_STENCIL_SIZE, 8, + GLX_DOUBLEBUFFER, + glbind_None, glbind_None + }; + + if (pConfig != NULL) { + if (!pConfig->singleBuffered) { + attribs[13] = glbind_None; + } + } + + glbind_OwnsDisplay = GL_TRUE; + glbind_pDisplay = glbind_XOpenDisplay(NULL); + if (glbind_pDisplay == NULL) { + glbUnloadOpenGLSO(); + return GL_INVALID_OPERATION; + } + + glbind_pFBVisualInfo = glbind_glXChooseVisual(glbind_pDisplay, glbind_XDefaultScreen(glbind_pDisplay), attribs); + if (glbind_pFBVisualInfo == NULL) { + glbUnloadOpenGLSO(); + return GL_INVALID_OPERATION; + } + + glbind_DummyColormap = glbind_XCreateColormap(glbind_pDisplay, glbind_XRootWindow(glbind_pDisplay, glbind_pFBVisualInfo->screen), glbind_pFBVisualInfo->visual, glbind_AllocNone); + + glbind_RC = glbind_glXCreateContext(glbind_pDisplay, glbind_pFBVisualInfo, NULL, GL_TRUE); + if (glbind_RC == NULL) { + glbUnloadOpenGLSO(); + return GL_INVALID_OPERATION; + } + + /* We cannot call any OpenGL APIs until a context is made current. In order to make a context current we will need a window. We just use a dummy window for this. */ + glbind_XSetWindowAttributes wa; + wa.colormap = glbind_DummyColormap; + wa.border_pixel = 0; + + /* Window's can not have dimensions of 0 in X11. We stick with dimensions of 1. */ + glbind_DummyWindow = glbind_XCreateWindow(glbind_pDisplay, glbind_XRootWindow(glbind_pDisplay, glbind_pFBVisualInfo->screen), 0, 0, 1, 1, 0, glbind_pFBVisualInfo->depth, glbind_InputOutput, glbind_pFBVisualInfo->visual, glbind_CWBorderPixel | glbind_CWColormap, &wa); + if (glbind_DummyWindow == 0) { + glbUnloadOpenGLSO(); + return GL_INVALID_OPERATION; + } + + glbind_glXMakeCurrent(glbind_pDisplay, glbind_DummyWindow, glbind_RC); + } +#endif + } + + if (pAPI != NULL) { +#if defined(GLBIND_WGL) + result = glbInitContextAPI(glbind_DC, glbind_RC, pAPI); +#endif +#if defined(GLBIND_GLX) + result = glbInitContextAPI(glbind_pDisplay, glbind_DummyWindow, glbind_RC, pAPI); +#endif + if (result == GL_NO_ERROR) { + if (g_glbInitCount == 0) { + result = glbBindAPI(pAPI); + } + } + } else { + GLBapi tempAPI; +#if defined(GLBIND_WGL) + result = glbInitContextAPI(glbind_DC, glbind_RC, &tempAPI); +#endif +#if defined(GLBIND_GLX) + result = glbInitContextAPI(glbind_pDisplay, glbind_DummyWindow, glbind_RC, &tempAPI); +#endif + if (result == GL_NO_ERROR) { + if (g_glbInitCount == 0) { + result = glbBindAPI(pAPI); + } + } + } + + /* If at this point we have an error we need to uninitialize the global objects (if this is the initial initialization) and return. */ + if (result != GL_NO_ERROR) { + if (g_glbInitCount == 0) { +#if defined(GLBIND_WGL) + if (glbind_RC) { + glbind_wglDeleteContext(glbind_RC); + glbind_RC = 0; + } + if (glbind_DummyHWND) { + DestroyWindow(glbind_DummyHWND); + glbind_DummyHWND = 0; + } + glbind_DC = 0; +#endif +#if defined(GLBIND_GLX) + if (glbind_RC) { + glbind_glXDestroyContext(glbind_pDisplay, glbind_RC); + glbind_RC = 0; + } + if (glbind_DummyWindow) { + glbind_XDestroyWindow(glbind_pDisplay, glbind_DummyWindow); + glbind_DummyWindow = 0; + } + if (glbind_pDisplay && glbind_OwnsDisplay) { + glbind_XCloseDisplay(glbind_pDisplay); + glbind_pDisplay = 0; + glbind_OwnsDisplay = GL_FALSE; + } +#endif + + glbUnloadOpenGLSO(); + } + + return result; + } + + g_glbInitCount += 1; /* <-- Only increment the init counter on success. */ + return GL_NO_ERROR; +} + +#if defined(GLBIND_WGL) +GLenum glbInitContextAPI(HDC dc, HGLRC rc, GLBapi* pAPI) +{ + GLenum result; + HDC dcPrev; + HGLRC rcPrev; + + dcPrev = glbind_wglGetCurrentDC(); + rcPrev = glbind_wglGetCurrentContext(); + + if (dcPrev != dc && rcPrev != rc) { + glbind_wglMakeCurrent(dc, rc); + } + + result = glbInitCurrentContextAPI(pAPI); + + if (dcPrev != dc && rcPrev != rc) { + glbind_wglMakeCurrent(dcPrev, rcPrev); + } + + return result; +} +#endif +#if defined(GLBIND_GLX) +GLenum glbInitContextAPI(glbind_Display*dpy, GLXDrawable drawable, GLXContext rc, GLBapi* pAPI) +{ + GLenum result; + GLXContext rcPrev = 0; + GLXDrawable drawablePrev = 0; + glbind_Display* dpyPrev = NULL; + + if (glbind_glXGetCurrentContext && glbind_glXGetCurrentDrawable && glbind_glXGetCurrentDisplay) { + rcPrev = glbind_glXGetCurrentContext(); + drawablePrev = glbind_glXGetCurrentDrawable(); + dpyPrev = glbind_glXGetCurrentDisplay(); + } + + glbind_glXMakeCurrent(dpy, drawable, rc); + result = glbInitCurrentContextAPI(pAPI); + glbind_glXMakeCurrent(dpyPrev, drawablePrev, rcPrev); + + return result; +} +#endif + +GLenum glbInitCurrentContextAPI(GLBapi* pAPI) +{ + if (pAPI == NULL) { + return GL_INVALID_OPERATION; + } + + glbZeroObject(pAPI); + + pAPI->glCullFace = (PFNGLCULLFACEPROC)glbGetProcAddress("glCullFace"); + pAPI->glFrontFace = (PFNGLFRONTFACEPROC)glbGetProcAddress("glFrontFace"); + pAPI->glHint = (PFNGLHINTPROC)glbGetProcAddress("glHint"); + pAPI->glLineWidth = (PFNGLLINEWIDTHPROC)glbGetProcAddress("glLineWidth"); + pAPI->glPointSize = (PFNGLPOINTSIZEPROC)glbGetProcAddress("glPointSize"); + pAPI->glPolygonMode = (PFNGLPOLYGONMODEPROC)glbGetProcAddress("glPolygonMode"); + pAPI->glScissor = (PFNGLSCISSORPROC)glbGetProcAddress("glScissor"); + pAPI->glTexParameterf = (PFNGLTEXPARAMETERFPROC)glbGetProcAddress("glTexParameterf"); + pAPI->glTexParameterfv = (PFNGLTEXPARAMETERFVPROC)glbGetProcAddress("glTexParameterfv"); + pAPI->glTexParameteri = (PFNGLTEXPARAMETERIPROC)glbGetProcAddress("glTexParameteri"); + pAPI->glTexParameteriv = (PFNGLTEXPARAMETERIVPROC)glbGetProcAddress("glTexParameteriv"); + pAPI->glTexImage1D = (PFNGLTEXIMAGE1DPROC)glbGetProcAddress("glTexImage1D"); + pAPI->glTexImage2D = (PFNGLTEXIMAGE2DPROC)glbGetProcAddress("glTexImage2D"); + pAPI->glDrawBuffer = (PFNGLDRAWBUFFERPROC)glbGetProcAddress("glDrawBuffer"); + pAPI->glClear = (PFNGLCLEARPROC)glbGetProcAddress("glClear"); + pAPI->glClearColor = (PFNGLCLEARCOLORPROC)glbGetProcAddress("glClearColor"); + pAPI->glClearStencil = (PFNGLCLEARSTENCILPROC)glbGetProcAddress("glClearStencil"); + pAPI->glClearDepth = (PFNGLCLEARDEPTHPROC)glbGetProcAddress("glClearDepth"); + pAPI->glStencilMask = (PFNGLSTENCILMASKPROC)glbGetProcAddress("glStencilMask"); + pAPI->glColorMask = (PFNGLCOLORMASKPROC)glbGetProcAddress("glColorMask"); + pAPI->glDepthMask = (PFNGLDEPTHMASKPROC)glbGetProcAddress("glDepthMask"); + pAPI->glDisable = (PFNGLDISABLEPROC)glbGetProcAddress("glDisable"); + pAPI->glEnable = (PFNGLENABLEPROC)glbGetProcAddress("glEnable"); + pAPI->glFinish = (PFNGLFINISHPROC)glbGetProcAddress("glFinish"); + pAPI->glFlush = (PFNGLFLUSHPROC)glbGetProcAddress("glFlush"); + pAPI->glBlendFunc = (PFNGLBLENDFUNCPROC)glbGetProcAddress("glBlendFunc"); + pAPI->glLogicOp = (PFNGLLOGICOPPROC)glbGetProcAddress("glLogicOp"); + pAPI->glStencilFunc = (PFNGLSTENCILFUNCPROC)glbGetProcAddress("glStencilFunc"); + pAPI->glStencilOp = (PFNGLSTENCILOPPROC)glbGetProcAddress("glStencilOp"); + pAPI->glDepthFunc = (PFNGLDEPTHFUNCPROC)glbGetProcAddress("glDepthFunc"); + pAPI->glPixelStoref = (PFNGLPIXELSTOREFPROC)glbGetProcAddress("glPixelStoref"); + pAPI->glPixelStorei = (PFNGLPIXELSTOREIPROC)glbGetProcAddress("glPixelStorei"); + pAPI->glReadBuffer = (PFNGLREADBUFFERPROC)glbGetProcAddress("glReadBuffer"); + pAPI->glReadPixels = (PFNGLREADPIXELSPROC)glbGetProcAddress("glReadPixels"); + pAPI->glGetBooleanv = (PFNGLGETBOOLEANVPROC)glbGetProcAddress("glGetBooleanv"); + pAPI->glGetDoublev = (PFNGLGETDOUBLEVPROC)glbGetProcAddress("glGetDoublev"); + pAPI->glGetError = (PFNGLGETERRORPROC)glbGetProcAddress("glGetError"); + pAPI->glGetFloatv = (PFNGLGETFLOATVPROC)glbGetProcAddress("glGetFloatv"); + pAPI->glGetIntegerv = (PFNGLGETINTEGERVPROC)glbGetProcAddress("glGetIntegerv"); + pAPI->glGetString = (PFNGLGETSTRINGPROC)glbGetProcAddress("glGetString"); + pAPI->glGetTexImage = (PFNGLGETTEXIMAGEPROC)glbGetProcAddress("glGetTexImage"); + pAPI->glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC)glbGetProcAddress("glGetTexParameterfv"); + pAPI->glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC)glbGetProcAddress("glGetTexParameteriv"); + pAPI->glGetTexLevelParameterfv = (PFNGLGETTEXLEVELPARAMETERFVPROC)glbGetProcAddress("glGetTexLevelParameterfv"); + pAPI->glGetTexLevelParameteriv = (PFNGLGETTEXLEVELPARAMETERIVPROC)glbGetProcAddress("glGetTexLevelParameteriv"); + pAPI->glIsEnabled = (PFNGLISENABLEDPROC)glbGetProcAddress("glIsEnabled"); + pAPI->glDepthRange = (PFNGLDEPTHRANGEPROC)glbGetProcAddress("glDepthRange"); + pAPI->glViewport = (PFNGLVIEWPORTPROC)glbGetProcAddress("glViewport"); + pAPI->glNewList = (PFNGLNEWLISTPROC)glbGetProcAddress("glNewList"); + pAPI->glEndList = (PFNGLENDLISTPROC)glbGetProcAddress("glEndList"); + pAPI->glCallList = (PFNGLCALLLISTPROC)glbGetProcAddress("glCallList"); + pAPI->glCallLists = (PFNGLCALLLISTSPROC)glbGetProcAddress("glCallLists"); + pAPI->glDeleteLists = (PFNGLDELETELISTSPROC)glbGetProcAddress("glDeleteLists"); + pAPI->glGenLists = (PFNGLGENLISTSPROC)glbGetProcAddress("glGenLists"); + pAPI->glListBase = (PFNGLLISTBASEPROC)glbGetProcAddress("glListBase"); + pAPI->glBegin = (PFNGLBEGINPROC)glbGetProcAddress("glBegin"); + pAPI->glBitmap = (PFNGLBITMAPPROC)glbGetProcAddress("glBitmap"); + pAPI->glColor3b = (PFNGLCOLOR3BPROC)glbGetProcAddress("glColor3b"); + pAPI->glColor3bv = (PFNGLCOLOR3BVPROC)glbGetProcAddress("glColor3bv"); + pAPI->glColor3d = (PFNGLCOLOR3DPROC)glbGetProcAddress("glColor3d"); + pAPI->glColor3dv = (PFNGLCOLOR3DVPROC)glbGetProcAddress("glColor3dv"); + pAPI->glColor3f = (PFNGLCOLOR3FPROC)glbGetProcAddress("glColor3f"); + pAPI->glColor3fv = (PFNGLCOLOR3FVPROC)glbGetProcAddress("glColor3fv"); + pAPI->glColor3i = (PFNGLCOLOR3IPROC)glbGetProcAddress("glColor3i"); + pAPI->glColor3iv = (PFNGLCOLOR3IVPROC)glbGetProcAddress("glColor3iv"); + pAPI->glColor3s = (PFNGLCOLOR3SPROC)glbGetProcAddress("glColor3s"); + pAPI->glColor3sv = (PFNGLCOLOR3SVPROC)glbGetProcAddress("glColor3sv"); + pAPI->glColor3ub = (PFNGLCOLOR3UBPROC)glbGetProcAddress("glColor3ub"); + pAPI->glColor3ubv = (PFNGLCOLOR3UBVPROC)glbGetProcAddress("glColor3ubv"); + pAPI->glColor3ui = (PFNGLCOLOR3UIPROC)glbGetProcAddress("glColor3ui"); + pAPI->glColor3uiv = (PFNGLCOLOR3UIVPROC)glbGetProcAddress("glColor3uiv"); + pAPI->glColor3us = (PFNGLCOLOR3USPROC)glbGetProcAddress("glColor3us"); + pAPI->glColor3usv = (PFNGLCOLOR3USVPROC)glbGetProcAddress("glColor3usv"); + pAPI->glColor4b = (PFNGLCOLOR4BPROC)glbGetProcAddress("glColor4b"); + pAPI->glColor4bv = (PFNGLCOLOR4BVPROC)glbGetProcAddress("glColor4bv"); + pAPI->glColor4d = (PFNGLCOLOR4DPROC)glbGetProcAddress("glColor4d"); + pAPI->glColor4dv = (PFNGLCOLOR4DVPROC)glbGetProcAddress("glColor4dv"); + pAPI->glColor4f = (PFNGLCOLOR4FPROC)glbGetProcAddress("glColor4f"); + pAPI->glColor4fv = (PFNGLCOLOR4FVPROC)glbGetProcAddress("glColor4fv"); + pAPI->glColor4i = (PFNGLCOLOR4IPROC)glbGetProcAddress("glColor4i"); + pAPI->glColor4iv = (PFNGLCOLOR4IVPROC)glbGetProcAddress("glColor4iv"); + pAPI->glColor4s = (PFNGLCOLOR4SPROC)glbGetProcAddress("glColor4s"); + pAPI->glColor4sv = (PFNGLCOLOR4SVPROC)glbGetProcAddress("glColor4sv"); + pAPI->glColor4ub = (PFNGLCOLOR4UBPROC)glbGetProcAddress("glColor4ub"); + pAPI->glColor4ubv = (PFNGLCOLOR4UBVPROC)glbGetProcAddress("glColor4ubv"); + pAPI->glColor4ui = (PFNGLCOLOR4UIPROC)glbGetProcAddress("glColor4ui"); + pAPI->glColor4uiv = (PFNGLCOLOR4UIVPROC)glbGetProcAddress("glColor4uiv"); + pAPI->glColor4us = (PFNGLCOLOR4USPROC)glbGetProcAddress("glColor4us"); + pAPI->glColor4usv = (PFNGLCOLOR4USVPROC)glbGetProcAddress("glColor4usv"); + pAPI->glEdgeFlag = (PFNGLEDGEFLAGPROC)glbGetProcAddress("glEdgeFlag"); + pAPI->glEdgeFlagv = (PFNGLEDGEFLAGVPROC)glbGetProcAddress("glEdgeFlagv"); + pAPI->glEnd = (PFNGLENDPROC)glbGetProcAddress("glEnd"); + pAPI->glIndexd = (PFNGLINDEXDPROC)glbGetProcAddress("glIndexd"); + pAPI->glIndexdv = (PFNGLINDEXDVPROC)glbGetProcAddress("glIndexdv"); + pAPI->glIndexf = (PFNGLINDEXFPROC)glbGetProcAddress("glIndexf"); + pAPI->glIndexfv = (PFNGLINDEXFVPROC)glbGetProcAddress("glIndexfv"); + pAPI->glIndexi = (PFNGLINDEXIPROC)glbGetProcAddress("glIndexi"); + pAPI->glIndexiv = (PFNGLINDEXIVPROC)glbGetProcAddress("glIndexiv"); + pAPI->glIndexs = (PFNGLINDEXSPROC)glbGetProcAddress("glIndexs"); + pAPI->glIndexsv = (PFNGLINDEXSVPROC)glbGetProcAddress("glIndexsv"); + pAPI->glNormal3b = (PFNGLNORMAL3BPROC)glbGetProcAddress("glNormal3b"); + pAPI->glNormal3bv = (PFNGLNORMAL3BVPROC)glbGetProcAddress("glNormal3bv"); + pAPI->glNormal3d = (PFNGLNORMAL3DPROC)glbGetProcAddress("glNormal3d"); + pAPI->glNormal3dv = (PFNGLNORMAL3DVPROC)glbGetProcAddress("glNormal3dv"); + pAPI->glNormal3f = (PFNGLNORMAL3FPROC)glbGetProcAddress("glNormal3f"); + pAPI->glNormal3fv = (PFNGLNORMAL3FVPROC)glbGetProcAddress("glNormal3fv"); + pAPI->glNormal3i = (PFNGLNORMAL3IPROC)glbGetProcAddress("glNormal3i"); + pAPI->glNormal3iv = (PFNGLNORMAL3IVPROC)glbGetProcAddress("glNormal3iv"); + pAPI->glNormal3s = (PFNGLNORMAL3SPROC)glbGetProcAddress("glNormal3s"); + pAPI->glNormal3sv = (PFNGLNORMAL3SVPROC)glbGetProcAddress("glNormal3sv"); + pAPI->glRasterPos2d = (PFNGLRASTERPOS2DPROC)glbGetProcAddress("glRasterPos2d"); + pAPI->glRasterPos2dv = (PFNGLRASTERPOS2DVPROC)glbGetProcAddress("glRasterPos2dv"); + pAPI->glRasterPos2f = (PFNGLRASTERPOS2FPROC)glbGetProcAddress("glRasterPos2f"); + pAPI->glRasterPos2fv = (PFNGLRASTERPOS2FVPROC)glbGetProcAddress("glRasterPos2fv"); + pAPI->glRasterPos2i = (PFNGLRASTERPOS2IPROC)glbGetProcAddress("glRasterPos2i"); + pAPI->glRasterPos2iv = (PFNGLRASTERPOS2IVPROC)glbGetProcAddress("glRasterPos2iv"); + pAPI->glRasterPos2s = (PFNGLRASTERPOS2SPROC)glbGetProcAddress("glRasterPos2s"); + pAPI->glRasterPos2sv = (PFNGLRASTERPOS2SVPROC)glbGetProcAddress("glRasterPos2sv"); + pAPI->glRasterPos3d = (PFNGLRASTERPOS3DPROC)glbGetProcAddress("glRasterPos3d"); + pAPI->glRasterPos3dv = (PFNGLRASTERPOS3DVPROC)glbGetProcAddress("glRasterPos3dv"); + pAPI->glRasterPos3f = (PFNGLRASTERPOS3FPROC)glbGetProcAddress("glRasterPos3f"); + pAPI->glRasterPos3fv = (PFNGLRASTERPOS3FVPROC)glbGetProcAddress("glRasterPos3fv"); + pAPI->glRasterPos3i = (PFNGLRASTERPOS3IPROC)glbGetProcAddress("glRasterPos3i"); + pAPI->glRasterPos3iv = (PFNGLRASTERPOS3IVPROC)glbGetProcAddress("glRasterPos3iv"); + pAPI->glRasterPos3s = (PFNGLRASTERPOS3SPROC)glbGetProcAddress("glRasterPos3s"); + pAPI->glRasterPos3sv = (PFNGLRASTERPOS3SVPROC)glbGetProcAddress("glRasterPos3sv"); + pAPI->glRasterPos4d = (PFNGLRASTERPOS4DPROC)glbGetProcAddress("glRasterPos4d"); + pAPI->glRasterPos4dv = (PFNGLRASTERPOS4DVPROC)glbGetProcAddress("glRasterPos4dv"); + pAPI->glRasterPos4f = (PFNGLRASTERPOS4FPROC)glbGetProcAddress("glRasterPos4f"); + pAPI->glRasterPos4fv = (PFNGLRASTERPOS4FVPROC)glbGetProcAddress("glRasterPos4fv"); + pAPI->glRasterPos4i = (PFNGLRASTERPOS4IPROC)glbGetProcAddress("glRasterPos4i"); + pAPI->glRasterPos4iv = (PFNGLRASTERPOS4IVPROC)glbGetProcAddress("glRasterPos4iv"); + pAPI->glRasterPos4s = (PFNGLRASTERPOS4SPROC)glbGetProcAddress("glRasterPos4s"); + pAPI->glRasterPos4sv = (PFNGLRASTERPOS4SVPROC)glbGetProcAddress("glRasterPos4sv"); + pAPI->glRectd = (PFNGLRECTDPROC)glbGetProcAddress("glRectd"); + pAPI->glRectdv = (PFNGLRECTDVPROC)glbGetProcAddress("glRectdv"); + pAPI->glRectf = (PFNGLRECTFPROC)glbGetProcAddress("glRectf"); + pAPI->glRectfv = (PFNGLRECTFVPROC)glbGetProcAddress("glRectfv"); + pAPI->glRecti = (PFNGLRECTIPROC)glbGetProcAddress("glRecti"); + pAPI->glRectiv = (PFNGLRECTIVPROC)glbGetProcAddress("glRectiv"); + pAPI->glRects = (PFNGLRECTSPROC)glbGetProcAddress("glRects"); + pAPI->glRectsv = (PFNGLRECTSVPROC)glbGetProcAddress("glRectsv"); + pAPI->glTexCoord1d = (PFNGLTEXCOORD1DPROC)glbGetProcAddress("glTexCoord1d"); + pAPI->glTexCoord1dv = (PFNGLTEXCOORD1DVPROC)glbGetProcAddress("glTexCoord1dv"); + pAPI->glTexCoord1f = (PFNGLTEXCOORD1FPROC)glbGetProcAddress("glTexCoord1f"); + pAPI->glTexCoord1fv = (PFNGLTEXCOORD1FVPROC)glbGetProcAddress("glTexCoord1fv"); + pAPI->glTexCoord1i = (PFNGLTEXCOORD1IPROC)glbGetProcAddress("glTexCoord1i"); + pAPI->glTexCoord1iv = (PFNGLTEXCOORD1IVPROC)glbGetProcAddress("glTexCoord1iv"); + pAPI->glTexCoord1s = (PFNGLTEXCOORD1SPROC)glbGetProcAddress("glTexCoord1s"); + pAPI->glTexCoord1sv = (PFNGLTEXCOORD1SVPROC)glbGetProcAddress("glTexCoord1sv"); + pAPI->glTexCoord2d = (PFNGLTEXCOORD2DPROC)glbGetProcAddress("glTexCoord2d"); + pAPI->glTexCoord2dv = (PFNGLTEXCOORD2DVPROC)glbGetProcAddress("glTexCoord2dv"); + pAPI->glTexCoord2f = (PFNGLTEXCOORD2FPROC)glbGetProcAddress("glTexCoord2f"); + pAPI->glTexCoord2fv = (PFNGLTEXCOORD2FVPROC)glbGetProcAddress("glTexCoord2fv"); + pAPI->glTexCoord2i = (PFNGLTEXCOORD2IPROC)glbGetProcAddress("glTexCoord2i"); + pAPI->glTexCoord2iv = (PFNGLTEXCOORD2IVPROC)glbGetProcAddress("glTexCoord2iv"); + pAPI->glTexCoord2s = (PFNGLTEXCOORD2SPROC)glbGetProcAddress("glTexCoord2s"); + pAPI->glTexCoord2sv = (PFNGLTEXCOORD2SVPROC)glbGetProcAddress("glTexCoord2sv"); + pAPI->glTexCoord3d = (PFNGLTEXCOORD3DPROC)glbGetProcAddress("glTexCoord3d"); + pAPI->glTexCoord3dv = (PFNGLTEXCOORD3DVPROC)glbGetProcAddress("glTexCoord3dv"); + pAPI->glTexCoord3f = (PFNGLTEXCOORD3FPROC)glbGetProcAddress("glTexCoord3f"); + pAPI->glTexCoord3fv = (PFNGLTEXCOORD3FVPROC)glbGetProcAddress("glTexCoord3fv"); + pAPI->glTexCoord3i = (PFNGLTEXCOORD3IPROC)glbGetProcAddress("glTexCoord3i"); + pAPI->glTexCoord3iv = (PFNGLTEXCOORD3IVPROC)glbGetProcAddress("glTexCoord3iv"); + pAPI->glTexCoord3s = (PFNGLTEXCOORD3SPROC)glbGetProcAddress("glTexCoord3s"); + pAPI->glTexCoord3sv = (PFNGLTEXCOORD3SVPROC)glbGetProcAddress("glTexCoord3sv"); + pAPI->glTexCoord4d = (PFNGLTEXCOORD4DPROC)glbGetProcAddress("glTexCoord4d"); + pAPI->glTexCoord4dv = (PFNGLTEXCOORD4DVPROC)glbGetProcAddress("glTexCoord4dv"); + pAPI->glTexCoord4f = (PFNGLTEXCOORD4FPROC)glbGetProcAddress("glTexCoord4f"); + pAPI->glTexCoord4fv = (PFNGLTEXCOORD4FVPROC)glbGetProcAddress("glTexCoord4fv"); + pAPI->glTexCoord4i = (PFNGLTEXCOORD4IPROC)glbGetProcAddress("glTexCoord4i"); + pAPI->glTexCoord4iv = (PFNGLTEXCOORD4IVPROC)glbGetProcAddress("glTexCoord4iv"); + pAPI->glTexCoord4s = (PFNGLTEXCOORD4SPROC)glbGetProcAddress("glTexCoord4s"); + pAPI->glTexCoord4sv = (PFNGLTEXCOORD4SVPROC)glbGetProcAddress("glTexCoord4sv"); + pAPI->glVertex2d = (PFNGLVERTEX2DPROC)glbGetProcAddress("glVertex2d"); + pAPI->glVertex2dv = (PFNGLVERTEX2DVPROC)glbGetProcAddress("glVertex2dv"); + pAPI->glVertex2f = (PFNGLVERTEX2FPROC)glbGetProcAddress("glVertex2f"); + pAPI->glVertex2fv = (PFNGLVERTEX2FVPROC)glbGetProcAddress("glVertex2fv"); + pAPI->glVertex2i = (PFNGLVERTEX2IPROC)glbGetProcAddress("glVertex2i"); + pAPI->glVertex2iv = (PFNGLVERTEX2IVPROC)glbGetProcAddress("glVertex2iv"); + pAPI->glVertex2s = (PFNGLVERTEX2SPROC)glbGetProcAddress("glVertex2s"); + pAPI->glVertex2sv = (PFNGLVERTEX2SVPROC)glbGetProcAddress("glVertex2sv"); + pAPI->glVertex3d = (PFNGLVERTEX3DPROC)glbGetProcAddress("glVertex3d"); + pAPI->glVertex3dv = (PFNGLVERTEX3DVPROC)glbGetProcAddress("glVertex3dv"); + pAPI->glVertex3f = (PFNGLVERTEX3FPROC)glbGetProcAddress("glVertex3f"); + pAPI->glVertex3fv = (PFNGLVERTEX3FVPROC)glbGetProcAddress("glVertex3fv"); + pAPI->glVertex3i = (PFNGLVERTEX3IPROC)glbGetProcAddress("glVertex3i"); + pAPI->glVertex3iv = (PFNGLVERTEX3IVPROC)glbGetProcAddress("glVertex3iv"); + pAPI->glVertex3s = (PFNGLVERTEX3SPROC)glbGetProcAddress("glVertex3s"); + pAPI->glVertex3sv = (PFNGLVERTEX3SVPROC)glbGetProcAddress("glVertex3sv"); + pAPI->glVertex4d = (PFNGLVERTEX4DPROC)glbGetProcAddress("glVertex4d"); + pAPI->glVertex4dv = (PFNGLVERTEX4DVPROC)glbGetProcAddress("glVertex4dv"); + pAPI->glVertex4f = (PFNGLVERTEX4FPROC)glbGetProcAddress("glVertex4f"); + pAPI->glVertex4fv = (PFNGLVERTEX4FVPROC)glbGetProcAddress("glVertex4fv"); + pAPI->glVertex4i = (PFNGLVERTEX4IPROC)glbGetProcAddress("glVertex4i"); + pAPI->glVertex4iv = (PFNGLVERTEX4IVPROC)glbGetProcAddress("glVertex4iv"); + pAPI->glVertex4s = (PFNGLVERTEX4SPROC)glbGetProcAddress("glVertex4s"); + pAPI->glVertex4sv = (PFNGLVERTEX4SVPROC)glbGetProcAddress("glVertex4sv"); + pAPI->glClipPlane = (PFNGLCLIPPLANEPROC)glbGetProcAddress("glClipPlane"); + pAPI->glColorMaterial = (PFNGLCOLORMATERIALPROC)glbGetProcAddress("glColorMaterial"); + pAPI->glFogf = (PFNGLFOGFPROC)glbGetProcAddress("glFogf"); + pAPI->glFogfv = (PFNGLFOGFVPROC)glbGetProcAddress("glFogfv"); + pAPI->glFogi = (PFNGLFOGIPROC)glbGetProcAddress("glFogi"); + pAPI->glFogiv = (PFNGLFOGIVPROC)glbGetProcAddress("glFogiv"); + pAPI->glLightf = (PFNGLLIGHTFPROC)glbGetProcAddress("glLightf"); + pAPI->glLightfv = (PFNGLLIGHTFVPROC)glbGetProcAddress("glLightfv"); + pAPI->glLighti = (PFNGLLIGHTIPROC)glbGetProcAddress("glLighti"); + pAPI->glLightiv = (PFNGLLIGHTIVPROC)glbGetProcAddress("glLightiv"); + pAPI->glLightModelf = (PFNGLLIGHTMODELFPROC)glbGetProcAddress("glLightModelf"); + pAPI->glLightModelfv = (PFNGLLIGHTMODELFVPROC)glbGetProcAddress("glLightModelfv"); + pAPI->glLightModeli = (PFNGLLIGHTMODELIPROC)glbGetProcAddress("glLightModeli"); + pAPI->glLightModeliv = (PFNGLLIGHTMODELIVPROC)glbGetProcAddress("glLightModeliv"); + pAPI->glLineStipple = (PFNGLLINESTIPPLEPROC)glbGetProcAddress("glLineStipple"); + pAPI->glMaterialf = (PFNGLMATERIALFPROC)glbGetProcAddress("glMaterialf"); + pAPI->glMaterialfv = (PFNGLMATERIALFVPROC)glbGetProcAddress("glMaterialfv"); + pAPI->glMateriali = (PFNGLMATERIALIPROC)glbGetProcAddress("glMateriali"); + pAPI->glMaterialiv = (PFNGLMATERIALIVPROC)glbGetProcAddress("glMaterialiv"); + pAPI->glPolygonStipple = (PFNGLPOLYGONSTIPPLEPROC)glbGetProcAddress("glPolygonStipple"); + pAPI->glShadeModel = (PFNGLSHADEMODELPROC)glbGetProcAddress("glShadeModel"); + pAPI->glTexEnvf = (PFNGLTEXENVFPROC)glbGetProcAddress("glTexEnvf"); + pAPI->glTexEnvfv = (PFNGLTEXENVFVPROC)glbGetProcAddress("glTexEnvfv"); + pAPI->glTexEnvi = (PFNGLTEXENVIPROC)glbGetProcAddress("glTexEnvi"); + pAPI->glTexEnviv = (PFNGLTEXENVIVPROC)glbGetProcAddress("glTexEnviv"); + pAPI->glTexGend = (PFNGLTEXGENDPROC)glbGetProcAddress("glTexGend"); + pAPI->glTexGendv = (PFNGLTEXGENDVPROC)glbGetProcAddress("glTexGendv"); + pAPI->glTexGenf = (PFNGLTEXGENFPROC)glbGetProcAddress("glTexGenf"); + pAPI->glTexGenfv = (PFNGLTEXGENFVPROC)glbGetProcAddress("glTexGenfv"); + pAPI->glTexGeni = (PFNGLTEXGENIPROC)glbGetProcAddress("glTexGeni"); + pAPI->glTexGeniv = (PFNGLTEXGENIVPROC)glbGetProcAddress("glTexGeniv"); + pAPI->glFeedbackBuffer = (PFNGLFEEDBACKBUFFERPROC)glbGetProcAddress("glFeedbackBuffer"); + pAPI->glSelectBuffer = (PFNGLSELECTBUFFERPROC)glbGetProcAddress("glSelectBuffer"); + pAPI->glRenderMode = (PFNGLRENDERMODEPROC)glbGetProcAddress("glRenderMode"); + pAPI->glInitNames = (PFNGLINITNAMESPROC)glbGetProcAddress("glInitNames"); + pAPI->glLoadName = (PFNGLLOADNAMEPROC)glbGetProcAddress("glLoadName"); + pAPI->glPassThrough = (PFNGLPASSTHROUGHPROC)glbGetProcAddress("glPassThrough"); + pAPI->glPopName = (PFNGLPOPNAMEPROC)glbGetProcAddress("glPopName"); + pAPI->glPushName = (PFNGLPUSHNAMEPROC)glbGetProcAddress("glPushName"); + pAPI->glClearAccum = (PFNGLCLEARACCUMPROC)glbGetProcAddress("glClearAccum"); + pAPI->glClearIndex = (PFNGLCLEARINDEXPROC)glbGetProcAddress("glClearIndex"); + pAPI->glIndexMask = (PFNGLINDEXMASKPROC)glbGetProcAddress("glIndexMask"); + pAPI->glAccum = (PFNGLACCUMPROC)glbGetProcAddress("glAccum"); + pAPI->glPopAttrib = (PFNGLPOPATTRIBPROC)glbGetProcAddress("glPopAttrib"); + pAPI->glPushAttrib = (PFNGLPUSHATTRIBPROC)glbGetProcAddress("glPushAttrib"); + pAPI->glMap1d = (PFNGLMAP1DPROC)glbGetProcAddress("glMap1d"); + pAPI->glMap1f = (PFNGLMAP1FPROC)glbGetProcAddress("glMap1f"); + pAPI->glMap2d = (PFNGLMAP2DPROC)glbGetProcAddress("glMap2d"); + pAPI->glMap2f = (PFNGLMAP2FPROC)glbGetProcAddress("glMap2f"); + pAPI->glMapGrid1d = (PFNGLMAPGRID1DPROC)glbGetProcAddress("glMapGrid1d"); + pAPI->glMapGrid1f = (PFNGLMAPGRID1FPROC)glbGetProcAddress("glMapGrid1f"); + pAPI->glMapGrid2d = (PFNGLMAPGRID2DPROC)glbGetProcAddress("glMapGrid2d"); + pAPI->glMapGrid2f = (PFNGLMAPGRID2FPROC)glbGetProcAddress("glMapGrid2f"); + pAPI->glEvalCoord1d = (PFNGLEVALCOORD1DPROC)glbGetProcAddress("glEvalCoord1d"); + pAPI->glEvalCoord1dv = (PFNGLEVALCOORD1DVPROC)glbGetProcAddress("glEvalCoord1dv"); + pAPI->glEvalCoord1f = (PFNGLEVALCOORD1FPROC)glbGetProcAddress("glEvalCoord1f"); + pAPI->glEvalCoord1fv = (PFNGLEVALCOORD1FVPROC)glbGetProcAddress("glEvalCoord1fv"); + pAPI->glEvalCoord2d = (PFNGLEVALCOORD2DPROC)glbGetProcAddress("glEvalCoord2d"); + pAPI->glEvalCoord2dv = (PFNGLEVALCOORD2DVPROC)glbGetProcAddress("glEvalCoord2dv"); + pAPI->glEvalCoord2f = (PFNGLEVALCOORD2FPROC)glbGetProcAddress("glEvalCoord2f"); + pAPI->glEvalCoord2fv = (PFNGLEVALCOORD2FVPROC)glbGetProcAddress("glEvalCoord2fv"); + pAPI->glEvalMesh1 = (PFNGLEVALMESH1PROC)glbGetProcAddress("glEvalMesh1"); + pAPI->glEvalPoint1 = (PFNGLEVALPOINT1PROC)glbGetProcAddress("glEvalPoint1"); + pAPI->glEvalMesh2 = (PFNGLEVALMESH2PROC)glbGetProcAddress("glEvalMesh2"); + pAPI->glEvalPoint2 = (PFNGLEVALPOINT2PROC)glbGetProcAddress("glEvalPoint2"); + pAPI->glAlphaFunc = (PFNGLALPHAFUNCPROC)glbGetProcAddress("glAlphaFunc"); + pAPI->glPixelZoom = (PFNGLPIXELZOOMPROC)glbGetProcAddress("glPixelZoom"); + pAPI->glPixelTransferf = (PFNGLPIXELTRANSFERFPROC)glbGetProcAddress("glPixelTransferf"); + pAPI->glPixelTransferi = (PFNGLPIXELTRANSFERIPROC)glbGetProcAddress("glPixelTransferi"); + pAPI->glPixelMapfv = (PFNGLPIXELMAPFVPROC)glbGetProcAddress("glPixelMapfv"); + pAPI->glPixelMapuiv = (PFNGLPIXELMAPUIVPROC)glbGetProcAddress("glPixelMapuiv"); + pAPI->glPixelMapusv = (PFNGLPIXELMAPUSVPROC)glbGetProcAddress("glPixelMapusv"); + pAPI->glCopyPixels = (PFNGLCOPYPIXELSPROC)glbGetProcAddress("glCopyPixels"); + pAPI->glDrawPixels = (PFNGLDRAWPIXELSPROC)glbGetProcAddress("glDrawPixels"); + pAPI->glGetClipPlane = (PFNGLGETCLIPPLANEPROC)glbGetProcAddress("glGetClipPlane"); + pAPI->glGetLightfv = (PFNGLGETLIGHTFVPROC)glbGetProcAddress("glGetLightfv"); + pAPI->glGetLightiv = (PFNGLGETLIGHTIVPROC)glbGetProcAddress("glGetLightiv"); + pAPI->glGetMapdv = (PFNGLGETMAPDVPROC)glbGetProcAddress("glGetMapdv"); + pAPI->glGetMapfv = (PFNGLGETMAPFVPROC)glbGetProcAddress("glGetMapfv"); + pAPI->glGetMapiv = (PFNGLGETMAPIVPROC)glbGetProcAddress("glGetMapiv"); + pAPI->glGetMaterialfv = (PFNGLGETMATERIALFVPROC)glbGetProcAddress("glGetMaterialfv"); + pAPI->glGetMaterialiv = (PFNGLGETMATERIALIVPROC)glbGetProcAddress("glGetMaterialiv"); + pAPI->glGetPixelMapfv = (PFNGLGETPIXELMAPFVPROC)glbGetProcAddress("glGetPixelMapfv"); + pAPI->glGetPixelMapuiv = (PFNGLGETPIXELMAPUIVPROC)glbGetProcAddress("glGetPixelMapuiv"); + pAPI->glGetPixelMapusv = (PFNGLGETPIXELMAPUSVPROC)glbGetProcAddress("glGetPixelMapusv"); + pAPI->glGetPolygonStipple = (PFNGLGETPOLYGONSTIPPLEPROC)glbGetProcAddress("glGetPolygonStipple"); + pAPI->glGetTexEnvfv = (PFNGLGETTEXENVFVPROC)glbGetProcAddress("glGetTexEnvfv"); + pAPI->glGetTexEnviv = (PFNGLGETTEXENVIVPROC)glbGetProcAddress("glGetTexEnviv"); + pAPI->glGetTexGendv = (PFNGLGETTEXGENDVPROC)glbGetProcAddress("glGetTexGendv"); + pAPI->glGetTexGenfv = (PFNGLGETTEXGENFVPROC)glbGetProcAddress("glGetTexGenfv"); + pAPI->glGetTexGeniv = (PFNGLGETTEXGENIVPROC)glbGetProcAddress("glGetTexGeniv"); + pAPI->glIsList = (PFNGLISLISTPROC)glbGetProcAddress("glIsList"); + pAPI->glFrustum = (PFNGLFRUSTUMPROC)glbGetProcAddress("glFrustum"); + pAPI->glLoadIdentity = (PFNGLLOADIDENTITYPROC)glbGetProcAddress("glLoadIdentity"); + pAPI->glLoadMatrixf = (PFNGLLOADMATRIXFPROC)glbGetProcAddress("glLoadMatrixf"); + pAPI->glLoadMatrixd = (PFNGLLOADMATRIXDPROC)glbGetProcAddress("glLoadMatrixd"); + pAPI->glMatrixMode = (PFNGLMATRIXMODEPROC)glbGetProcAddress("glMatrixMode"); + pAPI->glMultMatrixf = (PFNGLMULTMATRIXFPROC)glbGetProcAddress("glMultMatrixf"); + pAPI->glMultMatrixd = (PFNGLMULTMATRIXDPROC)glbGetProcAddress("glMultMatrixd"); + pAPI->glOrtho = (PFNGLORTHOPROC)glbGetProcAddress("glOrtho"); + pAPI->glPopMatrix = (PFNGLPOPMATRIXPROC)glbGetProcAddress("glPopMatrix"); + pAPI->glPushMatrix = (PFNGLPUSHMATRIXPROC)glbGetProcAddress("glPushMatrix"); + pAPI->glRotated = (PFNGLROTATEDPROC)glbGetProcAddress("glRotated"); + pAPI->glRotatef = (PFNGLROTATEFPROC)glbGetProcAddress("glRotatef"); + pAPI->glScaled = (PFNGLSCALEDPROC)glbGetProcAddress("glScaled"); + pAPI->glScalef = (PFNGLSCALEFPROC)glbGetProcAddress("glScalef"); + pAPI->glTranslated = (PFNGLTRANSLATEDPROC)glbGetProcAddress("glTranslated"); + pAPI->glTranslatef = (PFNGLTRANSLATEFPROC)glbGetProcAddress("glTranslatef"); + pAPI->glDrawArrays = (PFNGLDRAWARRAYSPROC)glbGetProcAddress("glDrawArrays"); + pAPI->glDrawElements = (PFNGLDRAWELEMENTSPROC)glbGetProcAddress("glDrawElements"); + pAPI->glGetPointerv = (PFNGLGETPOINTERVPROC)glbGetProcAddress("glGetPointerv"); + pAPI->glPolygonOffset = (PFNGLPOLYGONOFFSETPROC)glbGetProcAddress("glPolygonOffset"); + pAPI->glCopyTexImage1D = (PFNGLCOPYTEXIMAGE1DPROC)glbGetProcAddress("glCopyTexImage1D"); + pAPI->glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC)glbGetProcAddress("glCopyTexImage2D"); + pAPI->glCopyTexSubImage1D = (PFNGLCOPYTEXSUBIMAGE1DPROC)glbGetProcAddress("glCopyTexSubImage1D"); + pAPI->glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC)glbGetProcAddress("glCopyTexSubImage2D"); + pAPI->glTexSubImage1D = (PFNGLTEXSUBIMAGE1DPROC)glbGetProcAddress("glTexSubImage1D"); + pAPI->glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC)glbGetProcAddress("glTexSubImage2D"); + pAPI->glBindTexture = (PFNGLBINDTEXTUREPROC)glbGetProcAddress("glBindTexture"); + pAPI->glDeleteTextures = (PFNGLDELETETEXTURESPROC)glbGetProcAddress("glDeleteTextures"); + pAPI->glGenTextures = (PFNGLGENTEXTURESPROC)glbGetProcAddress("glGenTextures"); + pAPI->glIsTexture = (PFNGLISTEXTUREPROC)glbGetProcAddress("glIsTexture"); + pAPI->glArrayElement = (PFNGLARRAYELEMENTPROC)glbGetProcAddress("glArrayElement"); + pAPI->glColorPointer = (PFNGLCOLORPOINTERPROC)glbGetProcAddress("glColorPointer"); + pAPI->glDisableClientState = (PFNGLDISABLECLIENTSTATEPROC)glbGetProcAddress("glDisableClientState"); + pAPI->glEdgeFlagPointer = (PFNGLEDGEFLAGPOINTERPROC)glbGetProcAddress("glEdgeFlagPointer"); + pAPI->glEnableClientState = (PFNGLENABLECLIENTSTATEPROC)glbGetProcAddress("glEnableClientState"); + pAPI->glIndexPointer = (PFNGLINDEXPOINTERPROC)glbGetProcAddress("glIndexPointer"); + pAPI->glInterleavedArrays = (PFNGLINTERLEAVEDARRAYSPROC)glbGetProcAddress("glInterleavedArrays"); + pAPI->glNormalPointer = (PFNGLNORMALPOINTERPROC)glbGetProcAddress("glNormalPointer"); + pAPI->glTexCoordPointer = (PFNGLTEXCOORDPOINTERPROC)glbGetProcAddress("glTexCoordPointer"); + pAPI->glVertexPointer = (PFNGLVERTEXPOINTERPROC)glbGetProcAddress("glVertexPointer"); + pAPI->glAreTexturesResident = (PFNGLARETEXTURESRESIDENTPROC)glbGetProcAddress("glAreTexturesResident"); + pAPI->glPrioritizeTextures = (PFNGLPRIORITIZETEXTURESPROC)glbGetProcAddress("glPrioritizeTextures"); + pAPI->glIndexub = (PFNGLINDEXUBPROC)glbGetProcAddress("glIndexub"); + pAPI->glIndexubv = (PFNGLINDEXUBVPROC)glbGetProcAddress("glIndexubv"); + pAPI->glPopClientAttrib = (PFNGLPOPCLIENTATTRIBPROC)glbGetProcAddress("glPopClientAttrib"); + pAPI->glPushClientAttrib = (PFNGLPUSHCLIENTATTRIBPROC)glbGetProcAddress("glPushClientAttrib"); + pAPI->glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)glbGetProcAddress("glDrawRangeElements"); + pAPI->glTexImage3D = (PFNGLTEXIMAGE3DPROC)glbGetProcAddress("glTexImage3D"); + pAPI->glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)glbGetProcAddress("glTexSubImage3D"); + pAPI->glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)glbGetProcAddress("glCopyTexSubImage3D"); + pAPI->glActiveTexture = (PFNGLACTIVETEXTUREPROC)glbGetProcAddress("glActiveTexture"); + pAPI->glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC)glbGetProcAddress("glSampleCoverage"); + pAPI->glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)glbGetProcAddress("glCompressedTexImage3D"); + pAPI->glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)glbGetProcAddress("glCompressedTexImage2D"); + pAPI->glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC)glbGetProcAddress("glCompressedTexImage1D"); + pAPI->glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)glbGetProcAddress("glCompressedTexSubImage3D"); + pAPI->glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)glbGetProcAddress("glCompressedTexSubImage2D"); + pAPI->glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)glbGetProcAddress("glCompressedTexSubImage1D"); + pAPI->glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC)glbGetProcAddress("glGetCompressedTexImage"); + pAPI->glClientActiveTexture = (PFNGLCLIENTACTIVETEXTUREPROC)glbGetProcAddress("glClientActiveTexture"); + pAPI->glMultiTexCoord1d = (PFNGLMULTITEXCOORD1DPROC)glbGetProcAddress("glMultiTexCoord1d"); + pAPI->glMultiTexCoord1dv = (PFNGLMULTITEXCOORD1DVPROC)glbGetProcAddress("glMultiTexCoord1dv"); + pAPI->glMultiTexCoord1f = (PFNGLMULTITEXCOORD1FPROC)glbGetProcAddress("glMultiTexCoord1f"); + pAPI->glMultiTexCoord1fv = (PFNGLMULTITEXCOORD1FVPROC)glbGetProcAddress("glMultiTexCoord1fv"); + pAPI->glMultiTexCoord1i = (PFNGLMULTITEXCOORD1IPROC)glbGetProcAddress("glMultiTexCoord1i"); + pAPI->glMultiTexCoord1iv = (PFNGLMULTITEXCOORD1IVPROC)glbGetProcAddress("glMultiTexCoord1iv"); + pAPI->glMultiTexCoord1s = (PFNGLMULTITEXCOORD1SPROC)glbGetProcAddress("glMultiTexCoord1s"); + pAPI->glMultiTexCoord1sv = (PFNGLMULTITEXCOORD1SVPROC)glbGetProcAddress("glMultiTexCoord1sv"); + pAPI->glMultiTexCoord2d = (PFNGLMULTITEXCOORD2DPROC)glbGetProcAddress("glMultiTexCoord2d"); + pAPI->glMultiTexCoord2dv = (PFNGLMULTITEXCOORD2DVPROC)glbGetProcAddress("glMultiTexCoord2dv"); + pAPI->glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FPROC)glbGetProcAddress("glMultiTexCoord2f"); + pAPI->glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC)glbGetProcAddress("glMultiTexCoord2fv"); + pAPI->glMultiTexCoord2i = (PFNGLMULTITEXCOORD2IPROC)glbGetProcAddress("glMultiTexCoord2i"); + pAPI->glMultiTexCoord2iv = (PFNGLMULTITEXCOORD2IVPROC)glbGetProcAddress("glMultiTexCoord2iv"); + pAPI->glMultiTexCoord2s = (PFNGLMULTITEXCOORD2SPROC)glbGetProcAddress("glMultiTexCoord2s"); + pAPI->glMultiTexCoord2sv = (PFNGLMULTITEXCOORD2SVPROC)glbGetProcAddress("glMultiTexCoord2sv"); + pAPI->glMultiTexCoord3d = (PFNGLMULTITEXCOORD3DPROC)glbGetProcAddress("glMultiTexCoord3d"); + pAPI->glMultiTexCoord3dv = (PFNGLMULTITEXCOORD3DVPROC)glbGetProcAddress("glMultiTexCoord3dv"); + pAPI->glMultiTexCoord3f = (PFNGLMULTITEXCOORD3FPROC)glbGetProcAddress("glMultiTexCoord3f"); + pAPI->glMultiTexCoord3fv = (PFNGLMULTITEXCOORD3FVPROC)glbGetProcAddress("glMultiTexCoord3fv"); + pAPI->glMultiTexCoord3i = (PFNGLMULTITEXCOORD3IPROC)glbGetProcAddress("glMultiTexCoord3i"); + pAPI->glMultiTexCoord3iv = (PFNGLMULTITEXCOORD3IVPROC)glbGetProcAddress("glMultiTexCoord3iv"); + pAPI->glMultiTexCoord3s = (PFNGLMULTITEXCOORD3SPROC)glbGetProcAddress("glMultiTexCoord3s"); + pAPI->glMultiTexCoord3sv = (PFNGLMULTITEXCOORD3SVPROC)glbGetProcAddress("glMultiTexCoord3sv"); + pAPI->glMultiTexCoord4d = (PFNGLMULTITEXCOORD4DPROC)glbGetProcAddress("glMultiTexCoord4d"); + pAPI->glMultiTexCoord4dv = (PFNGLMULTITEXCOORD4DVPROC)glbGetProcAddress("glMultiTexCoord4dv"); + pAPI->glMultiTexCoord4f = (PFNGLMULTITEXCOORD4FPROC)glbGetProcAddress("glMultiTexCoord4f"); + pAPI->glMultiTexCoord4fv = (PFNGLMULTITEXCOORD4FVPROC)glbGetProcAddress("glMultiTexCoord4fv"); + pAPI->glMultiTexCoord4i = (PFNGLMULTITEXCOORD4IPROC)glbGetProcAddress("glMultiTexCoord4i"); + pAPI->glMultiTexCoord4iv = (PFNGLMULTITEXCOORD4IVPROC)glbGetProcAddress("glMultiTexCoord4iv"); + pAPI->glMultiTexCoord4s = (PFNGLMULTITEXCOORD4SPROC)glbGetProcAddress("glMultiTexCoord4s"); + pAPI->glMultiTexCoord4sv = (PFNGLMULTITEXCOORD4SVPROC)glbGetProcAddress("glMultiTexCoord4sv"); + pAPI->glLoadTransposeMatrixf = (PFNGLLOADTRANSPOSEMATRIXFPROC)glbGetProcAddress("glLoadTransposeMatrixf"); + pAPI->glLoadTransposeMatrixd = (PFNGLLOADTRANSPOSEMATRIXDPROC)glbGetProcAddress("glLoadTransposeMatrixd"); + pAPI->glMultTransposeMatrixf = (PFNGLMULTTRANSPOSEMATRIXFPROC)glbGetProcAddress("glMultTransposeMatrixf"); + pAPI->glMultTransposeMatrixd = (PFNGLMULTTRANSPOSEMATRIXDPROC)glbGetProcAddress("glMultTransposeMatrixd"); + pAPI->glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC)glbGetProcAddress("glBlendFuncSeparate"); + pAPI->glMultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC)glbGetProcAddress("glMultiDrawArrays"); + pAPI->glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC)glbGetProcAddress("glMultiDrawElements"); + pAPI->glPointParameterf = (PFNGLPOINTPARAMETERFPROC)glbGetProcAddress("glPointParameterf"); + pAPI->glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC)glbGetProcAddress("glPointParameterfv"); + pAPI->glPointParameteri = (PFNGLPOINTPARAMETERIPROC)glbGetProcAddress("glPointParameteri"); + pAPI->glPointParameteriv = (PFNGLPOINTPARAMETERIVPROC)glbGetProcAddress("glPointParameteriv"); + pAPI->glFogCoordf = (PFNGLFOGCOORDFPROC)glbGetProcAddress("glFogCoordf"); + pAPI->glFogCoordfv = (PFNGLFOGCOORDFVPROC)glbGetProcAddress("glFogCoordfv"); + pAPI->glFogCoordd = (PFNGLFOGCOORDDPROC)glbGetProcAddress("glFogCoordd"); + pAPI->glFogCoorddv = (PFNGLFOGCOORDDVPROC)glbGetProcAddress("glFogCoorddv"); + pAPI->glFogCoordPointer = (PFNGLFOGCOORDPOINTERPROC)glbGetProcAddress("glFogCoordPointer"); + pAPI->glSecondaryColor3b = (PFNGLSECONDARYCOLOR3BPROC)glbGetProcAddress("glSecondaryColor3b"); + pAPI->glSecondaryColor3bv = (PFNGLSECONDARYCOLOR3BVPROC)glbGetProcAddress("glSecondaryColor3bv"); + pAPI->glSecondaryColor3d = (PFNGLSECONDARYCOLOR3DPROC)glbGetProcAddress("glSecondaryColor3d"); + pAPI->glSecondaryColor3dv = (PFNGLSECONDARYCOLOR3DVPROC)glbGetProcAddress("glSecondaryColor3dv"); + pAPI->glSecondaryColor3f = (PFNGLSECONDARYCOLOR3FPROC)glbGetProcAddress("glSecondaryColor3f"); + pAPI->glSecondaryColor3fv = (PFNGLSECONDARYCOLOR3FVPROC)glbGetProcAddress("glSecondaryColor3fv"); + pAPI->glSecondaryColor3i = (PFNGLSECONDARYCOLOR3IPROC)glbGetProcAddress("glSecondaryColor3i"); + pAPI->glSecondaryColor3iv = (PFNGLSECONDARYCOLOR3IVPROC)glbGetProcAddress("glSecondaryColor3iv"); + pAPI->glSecondaryColor3s = (PFNGLSECONDARYCOLOR3SPROC)glbGetProcAddress("glSecondaryColor3s"); + pAPI->glSecondaryColor3sv = (PFNGLSECONDARYCOLOR3SVPROC)glbGetProcAddress("glSecondaryColor3sv"); + pAPI->glSecondaryColor3ub = (PFNGLSECONDARYCOLOR3UBPROC)glbGetProcAddress("glSecondaryColor3ub"); + pAPI->glSecondaryColor3ubv = (PFNGLSECONDARYCOLOR3UBVPROC)glbGetProcAddress("glSecondaryColor3ubv"); + pAPI->glSecondaryColor3ui = (PFNGLSECONDARYCOLOR3UIPROC)glbGetProcAddress("glSecondaryColor3ui"); + pAPI->glSecondaryColor3uiv = (PFNGLSECONDARYCOLOR3UIVPROC)glbGetProcAddress("glSecondaryColor3uiv"); + pAPI->glSecondaryColor3us = (PFNGLSECONDARYCOLOR3USPROC)glbGetProcAddress("glSecondaryColor3us"); + pAPI->glSecondaryColor3usv = (PFNGLSECONDARYCOLOR3USVPROC)glbGetProcAddress("glSecondaryColor3usv"); + pAPI->glSecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTERPROC)glbGetProcAddress("glSecondaryColorPointer"); + pAPI->glWindowPos2d = (PFNGLWINDOWPOS2DPROC)glbGetProcAddress("glWindowPos2d"); + pAPI->glWindowPos2dv = (PFNGLWINDOWPOS2DVPROC)glbGetProcAddress("glWindowPos2dv"); + pAPI->glWindowPos2f = (PFNGLWINDOWPOS2FPROC)glbGetProcAddress("glWindowPos2f"); + pAPI->glWindowPos2fv = (PFNGLWINDOWPOS2FVPROC)glbGetProcAddress("glWindowPos2fv"); + pAPI->glWindowPos2i = (PFNGLWINDOWPOS2IPROC)glbGetProcAddress("glWindowPos2i"); + pAPI->glWindowPos2iv = (PFNGLWINDOWPOS2IVPROC)glbGetProcAddress("glWindowPos2iv"); + pAPI->glWindowPos2s = (PFNGLWINDOWPOS2SPROC)glbGetProcAddress("glWindowPos2s"); + pAPI->glWindowPos2sv = (PFNGLWINDOWPOS2SVPROC)glbGetProcAddress("glWindowPos2sv"); + pAPI->glWindowPos3d = (PFNGLWINDOWPOS3DPROC)glbGetProcAddress("glWindowPos3d"); + pAPI->glWindowPos3dv = (PFNGLWINDOWPOS3DVPROC)glbGetProcAddress("glWindowPos3dv"); + pAPI->glWindowPos3f = (PFNGLWINDOWPOS3FPROC)glbGetProcAddress("glWindowPos3f"); + pAPI->glWindowPos3fv = (PFNGLWINDOWPOS3FVPROC)glbGetProcAddress("glWindowPos3fv"); + pAPI->glWindowPos3i = (PFNGLWINDOWPOS3IPROC)glbGetProcAddress("glWindowPos3i"); + pAPI->glWindowPos3iv = (PFNGLWINDOWPOS3IVPROC)glbGetProcAddress("glWindowPos3iv"); + pAPI->glWindowPos3s = (PFNGLWINDOWPOS3SPROC)glbGetProcAddress("glWindowPos3s"); + pAPI->glWindowPos3sv = (PFNGLWINDOWPOS3SVPROC)glbGetProcAddress("glWindowPos3sv"); + pAPI->glBlendColor = (PFNGLBLENDCOLORPROC)glbGetProcAddress("glBlendColor"); + pAPI->glBlendEquation = (PFNGLBLENDEQUATIONPROC)glbGetProcAddress("glBlendEquation"); + pAPI->glGenQueries = (PFNGLGENQUERIESPROC)glbGetProcAddress("glGenQueries"); + pAPI->glDeleteQueries = (PFNGLDELETEQUERIESPROC)glbGetProcAddress("glDeleteQueries"); + pAPI->glIsQuery = (PFNGLISQUERYPROC)glbGetProcAddress("glIsQuery"); + pAPI->glBeginQuery = (PFNGLBEGINQUERYPROC)glbGetProcAddress("glBeginQuery"); + pAPI->glEndQuery = (PFNGLENDQUERYPROC)glbGetProcAddress("glEndQuery"); + pAPI->glGetQueryiv = (PFNGLGETQUERYIVPROC)glbGetProcAddress("glGetQueryiv"); + pAPI->glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC)glbGetProcAddress("glGetQueryObjectiv"); + pAPI->glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)glbGetProcAddress("glGetQueryObjectuiv"); + pAPI->glBindBuffer = (PFNGLBINDBUFFERPROC)glbGetProcAddress("glBindBuffer"); + pAPI->glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)glbGetProcAddress("glDeleteBuffers"); + pAPI->glGenBuffers = (PFNGLGENBUFFERSPROC)glbGetProcAddress("glGenBuffers"); + pAPI->glIsBuffer = (PFNGLISBUFFERPROC)glbGetProcAddress("glIsBuffer"); + pAPI->glBufferData = (PFNGLBUFFERDATAPROC)glbGetProcAddress("glBufferData"); + pAPI->glBufferSubData = (PFNGLBUFFERSUBDATAPROC)glbGetProcAddress("glBufferSubData"); + pAPI->glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC)glbGetProcAddress("glGetBufferSubData"); + pAPI->glMapBuffer = (PFNGLMAPBUFFERPROC)glbGetProcAddress("glMapBuffer"); + pAPI->glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)glbGetProcAddress("glUnmapBuffer"); + pAPI->glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)glbGetProcAddress("glGetBufferParameteriv"); + pAPI->glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC)glbGetProcAddress("glGetBufferPointerv"); + pAPI->glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC)glbGetProcAddress("glBlendEquationSeparate"); + pAPI->glDrawBuffers = (PFNGLDRAWBUFFERSPROC)glbGetProcAddress("glDrawBuffers"); + pAPI->glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)glbGetProcAddress("glStencilOpSeparate"); + pAPI->glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC)glbGetProcAddress("glStencilFuncSeparate"); + pAPI->glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC)glbGetProcAddress("glStencilMaskSeparate"); + pAPI->glAttachShader = (PFNGLATTACHSHADERPROC)glbGetProcAddress("glAttachShader"); + pAPI->glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)glbGetProcAddress("glBindAttribLocation"); + pAPI->glCompileShader = (PFNGLCOMPILESHADERPROC)glbGetProcAddress("glCompileShader"); + pAPI->glCreateProgram = (PFNGLCREATEPROGRAMPROC)glbGetProcAddress("glCreateProgram"); + pAPI->glCreateShader = (PFNGLCREATESHADERPROC)glbGetProcAddress("glCreateShader"); + pAPI->glDeleteProgram = (PFNGLDELETEPROGRAMPROC)glbGetProcAddress("glDeleteProgram"); + pAPI->glDeleteShader = (PFNGLDELETESHADERPROC)glbGetProcAddress("glDeleteShader"); + pAPI->glDetachShader = (PFNGLDETACHSHADERPROC)glbGetProcAddress("glDetachShader"); + pAPI->glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)glbGetProcAddress("glDisableVertexAttribArray"); + pAPI->glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)glbGetProcAddress("glEnableVertexAttribArray"); + pAPI->glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC)glbGetProcAddress("glGetActiveAttrib"); + pAPI->glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)glbGetProcAddress("glGetActiveUniform"); + pAPI->glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC)glbGetProcAddress("glGetAttachedShaders"); + pAPI->glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)glbGetProcAddress("glGetAttribLocation"); + pAPI->glGetProgramiv = (PFNGLGETPROGRAMIVPROC)glbGetProcAddress("glGetProgramiv"); + pAPI->glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)glbGetProcAddress("glGetProgramInfoLog"); + pAPI->glGetShaderiv = (PFNGLGETSHADERIVPROC)glbGetProcAddress("glGetShaderiv"); + pAPI->glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)glbGetProcAddress("glGetShaderInfoLog"); + pAPI->glGetShaderSource = (PFNGLGETSHADERSOURCEPROC)glbGetProcAddress("glGetShaderSource"); + pAPI->glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)glbGetProcAddress("glGetUniformLocation"); + pAPI->glGetUniformfv = (PFNGLGETUNIFORMFVPROC)glbGetProcAddress("glGetUniformfv"); + pAPI->glGetUniformiv = (PFNGLGETUNIFORMIVPROC)glbGetProcAddress("glGetUniformiv"); + pAPI->glGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC)glbGetProcAddress("glGetVertexAttribdv"); + pAPI->glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC)glbGetProcAddress("glGetVertexAttribfv"); + pAPI->glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC)glbGetProcAddress("glGetVertexAttribiv"); + pAPI->glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC)glbGetProcAddress("glGetVertexAttribPointerv"); + pAPI->glIsProgram = (PFNGLISPROGRAMPROC)glbGetProcAddress("glIsProgram"); + pAPI->glIsShader = (PFNGLISSHADERPROC)glbGetProcAddress("glIsShader"); + pAPI->glLinkProgram = (PFNGLLINKPROGRAMPROC)glbGetProcAddress("glLinkProgram"); + pAPI->glShaderSource = (PFNGLSHADERSOURCEPROC)glbGetProcAddress("glShaderSource"); + pAPI->glUseProgram = (PFNGLUSEPROGRAMPROC)glbGetProcAddress("glUseProgram"); + pAPI->glUniform1f = (PFNGLUNIFORM1FPROC)glbGetProcAddress("glUniform1f"); + pAPI->glUniform2f = (PFNGLUNIFORM2FPROC)glbGetProcAddress("glUniform2f"); + pAPI->glUniform3f = (PFNGLUNIFORM3FPROC)glbGetProcAddress("glUniform3f"); + pAPI->glUniform4f = (PFNGLUNIFORM4FPROC)glbGetProcAddress("glUniform4f"); + pAPI->glUniform1i = (PFNGLUNIFORM1IPROC)glbGetProcAddress("glUniform1i"); + pAPI->glUniform2i = (PFNGLUNIFORM2IPROC)glbGetProcAddress("glUniform2i"); + pAPI->glUniform3i = (PFNGLUNIFORM3IPROC)glbGetProcAddress("glUniform3i"); + pAPI->glUniform4i = (PFNGLUNIFORM4IPROC)glbGetProcAddress("glUniform4i"); + pAPI->glUniform1fv = (PFNGLUNIFORM1FVPROC)glbGetProcAddress("glUniform1fv"); + pAPI->glUniform2fv = (PFNGLUNIFORM2FVPROC)glbGetProcAddress("glUniform2fv"); + pAPI->glUniform3fv = (PFNGLUNIFORM3FVPROC)glbGetProcAddress("glUniform3fv"); + pAPI->glUniform4fv = (PFNGLUNIFORM4FVPROC)glbGetProcAddress("glUniform4fv"); + pAPI->glUniform1iv = (PFNGLUNIFORM1IVPROC)glbGetProcAddress("glUniform1iv"); + pAPI->glUniform2iv = (PFNGLUNIFORM2IVPROC)glbGetProcAddress("glUniform2iv"); + pAPI->glUniform3iv = (PFNGLUNIFORM3IVPROC)glbGetProcAddress("glUniform3iv"); + pAPI->glUniform4iv = (PFNGLUNIFORM4IVPROC)glbGetProcAddress("glUniform4iv"); + pAPI->glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC)glbGetProcAddress("glUniformMatrix2fv"); + pAPI->glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)glbGetProcAddress("glUniformMatrix3fv"); + pAPI->glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)glbGetProcAddress("glUniformMatrix4fv"); + pAPI->glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)glbGetProcAddress("glValidateProgram"); + pAPI->glVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC)glbGetProcAddress("glVertexAttrib1d"); + pAPI->glVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC)glbGetProcAddress("glVertexAttrib1dv"); + pAPI->glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC)glbGetProcAddress("glVertexAttrib1f"); + pAPI->glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC)glbGetProcAddress("glVertexAttrib1fv"); + pAPI->glVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC)glbGetProcAddress("glVertexAttrib1s"); + pAPI->glVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC)glbGetProcAddress("glVertexAttrib1sv"); + pAPI->glVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC)glbGetProcAddress("glVertexAttrib2d"); + pAPI->glVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC)glbGetProcAddress("glVertexAttrib2dv"); + pAPI->glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC)glbGetProcAddress("glVertexAttrib2f"); + pAPI->glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC)glbGetProcAddress("glVertexAttrib2fv"); + pAPI->glVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC)glbGetProcAddress("glVertexAttrib2s"); + pAPI->glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC)glbGetProcAddress("glVertexAttrib2sv"); + pAPI->glVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC)glbGetProcAddress("glVertexAttrib3d"); + pAPI->glVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC)glbGetProcAddress("glVertexAttrib3dv"); + pAPI->glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC)glbGetProcAddress("glVertexAttrib3f"); + pAPI->glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC)glbGetProcAddress("glVertexAttrib3fv"); + pAPI->glVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC)glbGetProcAddress("glVertexAttrib3s"); + pAPI->glVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC)glbGetProcAddress("glVertexAttrib3sv"); + pAPI->glVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC)glbGetProcAddress("glVertexAttrib4Nbv"); + pAPI->glVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC)glbGetProcAddress("glVertexAttrib4Niv"); + pAPI->glVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC)glbGetProcAddress("glVertexAttrib4Nsv"); + pAPI->glVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC)glbGetProcAddress("glVertexAttrib4Nub"); + pAPI->glVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC)glbGetProcAddress("glVertexAttrib4Nubv"); + pAPI->glVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC)glbGetProcAddress("glVertexAttrib4Nuiv"); + pAPI->glVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC)glbGetProcAddress("glVertexAttrib4Nusv"); + pAPI->glVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC)glbGetProcAddress("glVertexAttrib4bv"); + pAPI->glVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC)glbGetProcAddress("glVertexAttrib4d"); + pAPI->glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC)glbGetProcAddress("glVertexAttrib4dv"); + pAPI->glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC)glbGetProcAddress("glVertexAttrib4f"); + pAPI->glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)glbGetProcAddress("glVertexAttrib4fv"); + pAPI->glVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC)glbGetProcAddress("glVertexAttrib4iv"); + pAPI->glVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC)glbGetProcAddress("glVertexAttrib4s"); + pAPI->glVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC)glbGetProcAddress("glVertexAttrib4sv"); + pAPI->glVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC)glbGetProcAddress("glVertexAttrib4ubv"); + pAPI->glVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC)glbGetProcAddress("glVertexAttrib4uiv"); + pAPI->glVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC)glbGetProcAddress("glVertexAttrib4usv"); + pAPI->glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)glbGetProcAddress("glVertexAttribPointer"); + pAPI->glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC)glbGetProcAddress("glUniformMatrix2x3fv"); + pAPI->glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC)glbGetProcAddress("glUniformMatrix3x2fv"); + pAPI->glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC)glbGetProcAddress("glUniformMatrix2x4fv"); + pAPI->glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC)glbGetProcAddress("glUniformMatrix4x2fv"); + pAPI->glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC)glbGetProcAddress("glUniformMatrix3x4fv"); + pAPI->glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC)glbGetProcAddress("glUniformMatrix4x3fv"); + pAPI->glColorMaski = (PFNGLCOLORMASKIPROC)glbGetProcAddress("glColorMaski"); + pAPI->glGetBooleani_v = (PFNGLGETBOOLEANI_VPROC)glbGetProcAddress("glGetBooleani_v"); + pAPI->glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)glbGetProcAddress("glGetIntegeri_v"); + pAPI->glEnablei = (PFNGLENABLEIPROC)glbGetProcAddress("glEnablei"); + pAPI->glDisablei = (PFNGLDISABLEIPROC)glbGetProcAddress("glDisablei"); + pAPI->glIsEnabledi = (PFNGLISENABLEDIPROC)glbGetProcAddress("glIsEnabledi"); + pAPI->glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC)glbGetProcAddress("glBeginTransformFeedback"); + pAPI->glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC)glbGetProcAddress("glEndTransformFeedback"); + pAPI->glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)glbGetProcAddress("glBindBufferRange"); + pAPI->glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)glbGetProcAddress("glBindBufferBase"); + pAPI->glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC)glbGetProcAddress("glTransformFeedbackVaryings"); + pAPI->glGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)glbGetProcAddress("glGetTransformFeedbackVarying"); + pAPI->glClampColor = (PFNGLCLAMPCOLORPROC)glbGetProcAddress("glClampColor"); + pAPI->glBeginConditionalRender = (PFNGLBEGINCONDITIONALRENDERPROC)glbGetProcAddress("glBeginConditionalRender"); + pAPI->glEndConditionalRender = (PFNGLENDCONDITIONALRENDERPROC)glbGetProcAddress("glEndConditionalRender"); + pAPI->glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)glbGetProcAddress("glVertexAttribIPointer"); + pAPI->glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC)glbGetProcAddress("glGetVertexAttribIiv"); + pAPI->glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC)glbGetProcAddress("glGetVertexAttribIuiv"); + pAPI->glVertexAttribI1i = (PFNGLVERTEXATTRIBI1IPROC)glbGetProcAddress("glVertexAttribI1i"); + pAPI->glVertexAttribI2i = (PFNGLVERTEXATTRIBI2IPROC)glbGetProcAddress("glVertexAttribI2i"); + pAPI->glVertexAttribI3i = (PFNGLVERTEXATTRIBI3IPROC)glbGetProcAddress("glVertexAttribI3i"); + pAPI->glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC)glbGetProcAddress("glVertexAttribI4i"); + pAPI->glVertexAttribI1ui = (PFNGLVERTEXATTRIBI1UIPROC)glbGetProcAddress("glVertexAttribI1ui"); + pAPI->glVertexAttribI2ui = (PFNGLVERTEXATTRIBI2UIPROC)glbGetProcAddress("glVertexAttribI2ui"); + pAPI->glVertexAttribI3ui = (PFNGLVERTEXATTRIBI3UIPROC)glbGetProcAddress("glVertexAttribI3ui"); + pAPI->glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC)glbGetProcAddress("glVertexAttribI4ui"); + pAPI->glVertexAttribI1iv = (PFNGLVERTEXATTRIBI1IVPROC)glbGetProcAddress("glVertexAttribI1iv"); + pAPI->glVertexAttribI2iv = (PFNGLVERTEXATTRIBI2IVPROC)glbGetProcAddress("glVertexAttribI2iv"); + pAPI->glVertexAttribI3iv = (PFNGLVERTEXATTRIBI3IVPROC)glbGetProcAddress("glVertexAttribI3iv"); + pAPI->glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC)glbGetProcAddress("glVertexAttribI4iv"); + pAPI->glVertexAttribI1uiv = (PFNGLVERTEXATTRIBI1UIVPROC)glbGetProcAddress("glVertexAttribI1uiv"); + pAPI->glVertexAttribI2uiv = (PFNGLVERTEXATTRIBI2UIVPROC)glbGetProcAddress("glVertexAttribI2uiv"); + pAPI->glVertexAttribI3uiv = (PFNGLVERTEXATTRIBI3UIVPROC)glbGetProcAddress("glVertexAttribI3uiv"); + pAPI->glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC)glbGetProcAddress("glVertexAttribI4uiv"); + pAPI->glVertexAttribI4bv = (PFNGLVERTEXATTRIBI4BVPROC)glbGetProcAddress("glVertexAttribI4bv"); + pAPI->glVertexAttribI4sv = (PFNGLVERTEXATTRIBI4SVPROC)glbGetProcAddress("glVertexAttribI4sv"); + pAPI->glVertexAttribI4ubv = (PFNGLVERTEXATTRIBI4UBVPROC)glbGetProcAddress("glVertexAttribI4ubv"); + pAPI->glVertexAttribI4usv = (PFNGLVERTEXATTRIBI4USVPROC)glbGetProcAddress("glVertexAttribI4usv"); + pAPI->glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC)glbGetProcAddress("glGetUniformuiv"); + pAPI->glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC)glbGetProcAddress("glBindFragDataLocation"); + pAPI->glGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC)glbGetProcAddress("glGetFragDataLocation"); + pAPI->glUniform1ui = (PFNGLUNIFORM1UIPROC)glbGetProcAddress("glUniform1ui"); + pAPI->glUniform2ui = (PFNGLUNIFORM2UIPROC)glbGetProcAddress("glUniform2ui"); + pAPI->glUniform3ui = (PFNGLUNIFORM3UIPROC)glbGetProcAddress("glUniform3ui"); + pAPI->glUniform4ui = (PFNGLUNIFORM4UIPROC)glbGetProcAddress("glUniform4ui"); + pAPI->glUniform1uiv = (PFNGLUNIFORM1UIVPROC)glbGetProcAddress("glUniform1uiv"); + pAPI->glUniform2uiv = (PFNGLUNIFORM2UIVPROC)glbGetProcAddress("glUniform2uiv"); + pAPI->glUniform3uiv = (PFNGLUNIFORM3UIVPROC)glbGetProcAddress("glUniform3uiv"); + pAPI->glUniform4uiv = (PFNGLUNIFORM4UIVPROC)glbGetProcAddress("glUniform4uiv"); + pAPI->glTexParameterIiv = (PFNGLTEXPARAMETERIIVPROC)glbGetProcAddress("glTexParameterIiv"); + pAPI->glTexParameterIuiv = (PFNGLTEXPARAMETERIUIVPROC)glbGetProcAddress("glTexParameterIuiv"); + pAPI->glGetTexParameterIiv = (PFNGLGETTEXPARAMETERIIVPROC)glbGetProcAddress("glGetTexParameterIiv"); + pAPI->glGetTexParameterIuiv = (PFNGLGETTEXPARAMETERIUIVPROC)glbGetProcAddress("glGetTexParameterIuiv"); + pAPI->glClearBufferiv = (PFNGLCLEARBUFFERIVPROC)glbGetProcAddress("glClearBufferiv"); + pAPI->glClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC)glbGetProcAddress("glClearBufferuiv"); + pAPI->glClearBufferfv = (PFNGLCLEARBUFFERFVPROC)glbGetProcAddress("glClearBufferfv"); + pAPI->glClearBufferfi = (PFNGLCLEARBUFFERFIPROC)glbGetProcAddress("glClearBufferfi"); + pAPI->glGetStringi = (PFNGLGETSTRINGIPROC)glbGetProcAddress("glGetStringi"); + pAPI->glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)glbGetProcAddress("glIsRenderbuffer"); + pAPI->glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)glbGetProcAddress("glBindRenderbuffer"); + pAPI->glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)glbGetProcAddress("glDeleteRenderbuffers"); + pAPI->glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)glbGetProcAddress("glGenRenderbuffers"); + pAPI->glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)glbGetProcAddress("glRenderbufferStorage"); + pAPI->glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)glbGetProcAddress("glGetRenderbufferParameteriv"); + pAPI->glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)glbGetProcAddress("glIsFramebuffer"); + pAPI->glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)glbGetProcAddress("glBindFramebuffer"); + pAPI->glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)glbGetProcAddress("glDeleteFramebuffers"); + pAPI->glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)glbGetProcAddress("glGenFramebuffers"); + pAPI->glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)glbGetProcAddress("glCheckFramebufferStatus"); + pAPI->glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)glbGetProcAddress("glFramebufferTexture1D"); + pAPI->glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)glbGetProcAddress("glFramebufferTexture2D"); + pAPI->glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)glbGetProcAddress("glFramebufferTexture3D"); + pAPI->glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)glbGetProcAddress("glFramebufferRenderbuffer"); + pAPI->glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)glbGetProcAddress("glGetFramebufferAttachmentParameteriv"); + pAPI->glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)glbGetProcAddress("glGenerateMipmap"); + pAPI->glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)glbGetProcAddress("glBlitFramebuffer"); + pAPI->glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)glbGetProcAddress("glRenderbufferStorageMultisample"); + pAPI->glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)glbGetProcAddress("glFramebufferTextureLayer"); + pAPI->glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC)glbGetProcAddress("glMapBufferRange"); + pAPI->glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC)glbGetProcAddress("glFlushMappedBufferRange"); + pAPI->glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)glbGetProcAddress("glBindVertexArray"); + pAPI->glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)glbGetProcAddress("glDeleteVertexArrays"); + pAPI->glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)glbGetProcAddress("glGenVertexArrays"); + pAPI->glIsVertexArray = (PFNGLISVERTEXARRAYPROC)glbGetProcAddress("glIsVertexArray"); + pAPI->glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)glbGetProcAddress("glDrawArraysInstanced"); + pAPI->glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)glbGetProcAddress("glDrawElementsInstanced"); + pAPI->glTexBuffer = (PFNGLTEXBUFFERPROC)glbGetProcAddress("glTexBuffer"); + pAPI->glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC)glbGetProcAddress("glPrimitiveRestartIndex"); + pAPI->glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC)glbGetProcAddress("glCopyBufferSubData"); + pAPI->glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC)glbGetProcAddress("glGetUniformIndices"); + pAPI->glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC)glbGetProcAddress("glGetActiveUniformsiv"); + pAPI->glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC)glbGetProcAddress("glGetActiveUniformName"); + pAPI->glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)glbGetProcAddress("glGetUniformBlockIndex"); + pAPI->glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)glbGetProcAddress("glGetActiveUniformBlockiv"); + pAPI->glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)glbGetProcAddress("glGetActiveUniformBlockName"); + pAPI->glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)glbGetProcAddress("glUniformBlockBinding"); + pAPI->glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC)glbGetProcAddress("glDrawElementsBaseVertex"); + pAPI->glDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)glbGetProcAddress("glDrawRangeElementsBaseVertex"); + pAPI->glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)glbGetProcAddress("glDrawElementsInstancedBaseVertex"); + pAPI->glMultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)glbGetProcAddress("glMultiDrawElementsBaseVertex"); + pAPI->glProvokingVertex = (PFNGLPROVOKINGVERTEXPROC)glbGetProcAddress("glProvokingVertex"); + pAPI->glFenceSync = (PFNGLFENCESYNCPROC)glbGetProcAddress("glFenceSync"); + pAPI->glIsSync = (PFNGLISSYNCPROC)glbGetProcAddress("glIsSync"); + pAPI->glDeleteSync = (PFNGLDELETESYNCPROC)glbGetProcAddress("glDeleteSync"); + pAPI->glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)glbGetProcAddress("glClientWaitSync"); + pAPI->glWaitSync = (PFNGLWAITSYNCPROC)glbGetProcAddress("glWaitSync"); + pAPI->glGetInteger64v = (PFNGLGETINTEGER64VPROC)glbGetProcAddress("glGetInteger64v"); + pAPI->glGetSynciv = (PFNGLGETSYNCIVPROC)glbGetProcAddress("glGetSynciv"); + pAPI->glGetInteger64i_v = (PFNGLGETINTEGER64I_VPROC)glbGetProcAddress("glGetInteger64i_v"); + pAPI->glGetBufferParameteri64v = (PFNGLGETBUFFERPARAMETERI64VPROC)glbGetProcAddress("glGetBufferParameteri64v"); + pAPI->glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC)glbGetProcAddress("glFramebufferTexture"); + pAPI->glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC)glbGetProcAddress("glTexImage2DMultisample"); + pAPI->glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC)glbGetProcAddress("glTexImage3DMultisample"); + pAPI->glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC)glbGetProcAddress("glGetMultisamplefv"); + pAPI->glSampleMaski = (PFNGLSAMPLEMASKIPROC)glbGetProcAddress("glSampleMaski"); + pAPI->glBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)glbGetProcAddress("glBindFragDataLocationIndexed"); + pAPI->glGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC)glbGetProcAddress("glGetFragDataIndex"); + pAPI->glGenSamplers = (PFNGLGENSAMPLERSPROC)glbGetProcAddress("glGenSamplers"); + pAPI->glDeleteSamplers = (PFNGLDELETESAMPLERSPROC)glbGetProcAddress("glDeleteSamplers"); + pAPI->glIsSampler = (PFNGLISSAMPLERPROC)glbGetProcAddress("glIsSampler"); + pAPI->glBindSampler = (PFNGLBINDSAMPLERPROC)glbGetProcAddress("glBindSampler"); + pAPI->glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC)glbGetProcAddress("glSamplerParameteri"); + pAPI->glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC)glbGetProcAddress("glSamplerParameteriv"); + pAPI->glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC)glbGetProcAddress("glSamplerParameterf"); + pAPI->glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC)glbGetProcAddress("glSamplerParameterfv"); + pAPI->glSamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC)glbGetProcAddress("glSamplerParameterIiv"); + pAPI->glSamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC)glbGetProcAddress("glSamplerParameterIuiv"); + pAPI->glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC)glbGetProcAddress("glGetSamplerParameteriv"); + pAPI->glGetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC)glbGetProcAddress("glGetSamplerParameterIiv"); + pAPI->glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC)glbGetProcAddress("glGetSamplerParameterfv"); + pAPI->glGetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC)glbGetProcAddress("glGetSamplerParameterIuiv"); + pAPI->glQueryCounter = (PFNGLQUERYCOUNTERPROC)glbGetProcAddress("glQueryCounter"); + pAPI->glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC)glbGetProcAddress("glGetQueryObjecti64v"); + pAPI->glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC)glbGetProcAddress("glGetQueryObjectui64v"); + pAPI->glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)glbGetProcAddress("glVertexAttribDivisor"); + pAPI->glVertexAttribP1ui = (PFNGLVERTEXATTRIBP1UIPROC)glbGetProcAddress("glVertexAttribP1ui"); + pAPI->glVertexAttribP1uiv = (PFNGLVERTEXATTRIBP1UIVPROC)glbGetProcAddress("glVertexAttribP1uiv"); + pAPI->glVertexAttribP2ui = (PFNGLVERTEXATTRIBP2UIPROC)glbGetProcAddress("glVertexAttribP2ui"); + pAPI->glVertexAttribP2uiv = (PFNGLVERTEXATTRIBP2UIVPROC)glbGetProcAddress("glVertexAttribP2uiv"); + pAPI->glVertexAttribP3ui = (PFNGLVERTEXATTRIBP3UIPROC)glbGetProcAddress("glVertexAttribP3ui"); + pAPI->glVertexAttribP3uiv = (PFNGLVERTEXATTRIBP3UIVPROC)glbGetProcAddress("glVertexAttribP3uiv"); + pAPI->glVertexAttribP4ui = (PFNGLVERTEXATTRIBP4UIPROC)glbGetProcAddress("glVertexAttribP4ui"); + pAPI->glVertexAttribP4uiv = (PFNGLVERTEXATTRIBP4UIVPROC)glbGetProcAddress("glVertexAttribP4uiv"); + pAPI->glVertexP2ui = (PFNGLVERTEXP2UIPROC)glbGetProcAddress("glVertexP2ui"); + pAPI->glVertexP2uiv = (PFNGLVERTEXP2UIVPROC)glbGetProcAddress("glVertexP2uiv"); + pAPI->glVertexP3ui = (PFNGLVERTEXP3UIPROC)glbGetProcAddress("glVertexP3ui"); + pAPI->glVertexP3uiv = (PFNGLVERTEXP3UIVPROC)glbGetProcAddress("glVertexP3uiv"); + pAPI->glVertexP4ui = (PFNGLVERTEXP4UIPROC)glbGetProcAddress("glVertexP4ui"); + pAPI->glVertexP4uiv = (PFNGLVERTEXP4UIVPROC)glbGetProcAddress("glVertexP4uiv"); + pAPI->glTexCoordP1ui = (PFNGLTEXCOORDP1UIPROC)glbGetProcAddress("glTexCoordP1ui"); + pAPI->glTexCoordP1uiv = (PFNGLTEXCOORDP1UIVPROC)glbGetProcAddress("glTexCoordP1uiv"); + pAPI->glTexCoordP2ui = (PFNGLTEXCOORDP2UIPROC)glbGetProcAddress("glTexCoordP2ui"); + pAPI->glTexCoordP2uiv = (PFNGLTEXCOORDP2UIVPROC)glbGetProcAddress("glTexCoordP2uiv"); + pAPI->glTexCoordP3ui = (PFNGLTEXCOORDP3UIPROC)glbGetProcAddress("glTexCoordP3ui"); + pAPI->glTexCoordP3uiv = (PFNGLTEXCOORDP3UIVPROC)glbGetProcAddress("glTexCoordP3uiv"); + pAPI->glTexCoordP4ui = (PFNGLTEXCOORDP4UIPROC)glbGetProcAddress("glTexCoordP4ui"); + pAPI->glTexCoordP4uiv = (PFNGLTEXCOORDP4UIVPROC)glbGetProcAddress("glTexCoordP4uiv"); + pAPI->glMultiTexCoordP1ui = (PFNGLMULTITEXCOORDP1UIPROC)glbGetProcAddress("glMultiTexCoordP1ui"); + pAPI->glMultiTexCoordP1uiv = (PFNGLMULTITEXCOORDP1UIVPROC)glbGetProcAddress("glMultiTexCoordP1uiv"); + pAPI->glMultiTexCoordP2ui = (PFNGLMULTITEXCOORDP2UIPROC)glbGetProcAddress("glMultiTexCoordP2ui"); + pAPI->glMultiTexCoordP2uiv = (PFNGLMULTITEXCOORDP2UIVPROC)glbGetProcAddress("glMultiTexCoordP2uiv"); + pAPI->glMultiTexCoordP3ui = (PFNGLMULTITEXCOORDP3UIPROC)glbGetProcAddress("glMultiTexCoordP3ui"); + pAPI->glMultiTexCoordP3uiv = (PFNGLMULTITEXCOORDP3UIVPROC)glbGetProcAddress("glMultiTexCoordP3uiv"); + pAPI->glMultiTexCoordP4ui = (PFNGLMULTITEXCOORDP4UIPROC)glbGetProcAddress("glMultiTexCoordP4ui"); + pAPI->glMultiTexCoordP4uiv = (PFNGLMULTITEXCOORDP4UIVPROC)glbGetProcAddress("glMultiTexCoordP4uiv"); + pAPI->glNormalP3ui = (PFNGLNORMALP3UIPROC)glbGetProcAddress("glNormalP3ui"); + pAPI->glNormalP3uiv = (PFNGLNORMALP3UIVPROC)glbGetProcAddress("glNormalP3uiv"); + pAPI->glColorP3ui = (PFNGLCOLORP3UIPROC)glbGetProcAddress("glColorP3ui"); + pAPI->glColorP3uiv = (PFNGLCOLORP3UIVPROC)glbGetProcAddress("glColorP3uiv"); + pAPI->glColorP4ui = (PFNGLCOLORP4UIPROC)glbGetProcAddress("glColorP4ui"); + pAPI->glColorP4uiv = (PFNGLCOLORP4UIVPROC)glbGetProcAddress("glColorP4uiv"); + pAPI->glSecondaryColorP3ui = (PFNGLSECONDARYCOLORP3UIPROC)glbGetProcAddress("glSecondaryColorP3ui"); + pAPI->glSecondaryColorP3uiv = (PFNGLSECONDARYCOLORP3UIVPROC)glbGetProcAddress("glSecondaryColorP3uiv"); + pAPI->glMinSampleShading = (PFNGLMINSAMPLESHADINGPROC)glbGetProcAddress("glMinSampleShading"); + pAPI->glBlendEquationi = (PFNGLBLENDEQUATIONIPROC)glbGetProcAddress("glBlendEquationi"); + pAPI->glBlendEquationSeparatei = (PFNGLBLENDEQUATIONSEPARATEIPROC)glbGetProcAddress("glBlendEquationSeparatei"); + pAPI->glBlendFunci = (PFNGLBLENDFUNCIPROC)glbGetProcAddress("glBlendFunci"); + pAPI->glBlendFuncSeparatei = (PFNGLBLENDFUNCSEPARATEIPROC)glbGetProcAddress("glBlendFuncSeparatei"); + pAPI->glDrawArraysIndirect = (PFNGLDRAWARRAYSINDIRECTPROC)glbGetProcAddress("glDrawArraysIndirect"); + pAPI->glDrawElementsIndirect = (PFNGLDRAWELEMENTSINDIRECTPROC)glbGetProcAddress("glDrawElementsIndirect"); + pAPI->glUniform1d = (PFNGLUNIFORM1DPROC)glbGetProcAddress("glUniform1d"); + pAPI->glUniform2d = (PFNGLUNIFORM2DPROC)glbGetProcAddress("glUniform2d"); + pAPI->glUniform3d = (PFNGLUNIFORM3DPROC)glbGetProcAddress("glUniform3d"); + pAPI->glUniform4d = (PFNGLUNIFORM4DPROC)glbGetProcAddress("glUniform4d"); + pAPI->glUniform1dv = (PFNGLUNIFORM1DVPROC)glbGetProcAddress("glUniform1dv"); + pAPI->glUniform2dv = (PFNGLUNIFORM2DVPROC)glbGetProcAddress("glUniform2dv"); + pAPI->glUniform3dv = (PFNGLUNIFORM3DVPROC)glbGetProcAddress("glUniform3dv"); + pAPI->glUniform4dv = (PFNGLUNIFORM4DVPROC)glbGetProcAddress("glUniform4dv"); + pAPI->glUniformMatrix2dv = (PFNGLUNIFORMMATRIX2DVPROC)glbGetProcAddress("glUniformMatrix2dv"); + pAPI->glUniformMatrix3dv = (PFNGLUNIFORMMATRIX3DVPROC)glbGetProcAddress("glUniformMatrix3dv"); + pAPI->glUniformMatrix4dv = (PFNGLUNIFORMMATRIX4DVPROC)glbGetProcAddress("glUniformMatrix4dv"); + pAPI->glUniformMatrix2x3dv = (PFNGLUNIFORMMATRIX2X3DVPROC)glbGetProcAddress("glUniformMatrix2x3dv"); + pAPI->glUniformMatrix2x4dv = (PFNGLUNIFORMMATRIX2X4DVPROC)glbGetProcAddress("glUniformMatrix2x4dv"); + pAPI->glUniformMatrix3x2dv = (PFNGLUNIFORMMATRIX3X2DVPROC)glbGetProcAddress("glUniformMatrix3x2dv"); + pAPI->glUniformMatrix3x4dv = (PFNGLUNIFORMMATRIX3X4DVPROC)glbGetProcAddress("glUniformMatrix3x4dv"); + pAPI->glUniformMatrix4x2dv = (PFNGLUNIFORMMATRIX4X2DVPROC)glbGetProcAddress("glUniformMatrix4x2dv"); + pAPI->glUniformMatrix4x3dv = (PFNGLUNIFORMMATRIX4X3DVPROC)glbGetProcAddress("glUniformMatrix4x3dv"); + pAPI->glGetUniformdv = (PFNGLGETUNIFORMDVPROC)glbGetProcAddress("glGetUniformdv"); + pAPI->glGetSubroutineUniformLocation = (PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC)glbGetProcAddress("glGetSubroutineUniformLocation"); + pAPI->glGetSubroutineIndex = (PFNGLGETSUBROUTINEINDEXPROC)glbGetProcAddress("glGetSubroutineIndex"); + pAPI->glGetActiveSubroutineUniformiv = (PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC)glbGetProcAddress("glGetActiveSubroutineUniformiv"); + pAPI->glGetActiveSubroutineUniformName = (PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC)glbGetProcAddress("glGetActiveSubroutineUniformName"); + pAPI->glGetActiveSubroutineName = (PFNGLGETACTIVESUBROUTINENAMEPROC)glbGetProcAddress("glGetActiveSubroutineName"); + pAPI->glUniformSubroutinesuiv = (PFNGLUNIFORMSUBROUTINESUIVPROC)glbGetProcAddress("glUniformSubroutinesuiv"); + pAPI->glGetUniformSubroutineuiv = (PFNGLGETUNIFORMSUBROUTINEUIVPROC)glbGetProcAddress("glGetUniformSubroutineuiv"); + pAPI->glGetProgramStageiv = (PFNGLGETPROGRAMSTAGEIVPROC)glbGetProcAddress("glGetProgramStageiv"); + pAPI->glPatchParameteri = (PFNGLPATCHPARAMETERIPROC)glbGetProcAddress("glPatchParameteri"); + pAPI->glPatchParameterfv = (PFNGLPATCHPARAMETERFVPROC)glbGetProcAddress("glPatchParameterfv"); + pAPI->glBindTransformFeedback = (PFNGLBINDTRANSFORMFEEDBACKPROC)glbGetProcAddress("glBindTransformFeedback"); + pAPI->glDeleteTransformFeedbacks = (PFNGLDELETETRANSFORMFEEDBACKSPROC)glbGetProcAddress("glDeleteTransformFeedbacks"); + pAPI->glGenTransformFeedbacks = (PFNGLGENTRANSFORMFEEDBACKSPROC)glbGetProcAddress("glGenTransformFeedbacks"); + pAPI->glIsTransformFeedback = (PFNGLISTRANSFORMFEEDBACKPROC)glbGetProcAddress("glIsTransformFeedback"); + pAPI->glPauseTransformFeedback = (PFNGLPAUSETRANSFORMFEEDBACKPROC)glbGetProcAddress("glPauseTransformFeedback"); + pAPI->glResumeTransformFeedback = (PFNGLRESUMETRANSFORMFEEDBACKPROC)glbGetProcAddress("glResumeTransformFeedback"); + pAPI->glDrawTransformFeedback = (PFNGLDRAWTRANSFORMFEEDBACKPROC)glbGetProcAddress("glDrawTransformFeedback"); + pAPI->glDrawTransformFeedbackStream = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC)glbGetProcAddress("glDrawTransformFeedbackStream"); + pAPI->glBeginQueryIndexed = (PFNGLBEGINQUERYINDEXEDPROC)glbGetProcAddress("glBeginQueryIndexed"); + pAPI->glEndQueryIndexed = (PFNGLENDQUERYINDEXEDPROC)glbGetProcAddress("glEndQueryIndexed"); + pAPI->glGetQueryIndexediv = (PFNGLGETQUERYINDEXEDIVPROC)glbGetProcAddress("glGetQueryIndexediv"); + pAPI->glReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC)glbGetProcAddress("glReleaseShaderCompiler"); + pAPI->glShaderBinary = (PFNGLSHADERBINARYPROC)glbGetProcAddress("glShaderBinary"); + pAPI->glGetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC)glbGetProcAddress("glGetShaderPrecisionFormat"); + pAPI->glDepthRangef = (PFNGLDEPTHRANGEFPROC)glbGetProcAddress("glDepthRangef"); + pAPI->glClearDepthf = (PFNGLCLEARDEPTHFPROC)glbGetProcAddress("glClearDepthf"); + pAPI->glGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC)glbGetProcAddress("glGetProgramBinary"); + pAPI->glProgramBinary = (PFNGLPROGRAMBINARYPROC)glbGetProcAddress("glProgramBinary"); + pAPI->glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)glbGetProcAddress("glProgramParameteri"); + pAPI->glUseProgramStages = (PFNGLUSEPROGRAMSTAGESPROC)glbGetProcAddress("glUseProgramStages"); + pAPI->glActiveShaderProgram = (PFNGLACTIVESHADERPROGRAMPROC)glbGetProcAddress("glActiveShaderProgram"); + pAPI->glCreateShaderProgramv = (PFNGLCREATESHADERPROGRAMVPROC)glbGetProcAddress("glCreateShaderProgramv"); + pAPI->glBindProgramPipeline = (PFNGLBINDPROGRAMPIPELINEPROC)glbGetProcAddress("glBindProgramPipeline"); + pAPI->glDeleteProgramPipelines = (PFNGLDELETEPROGRAMPIPELINESPROC)glbGetProcAddress("glDeleteProgramPipelines"); + pAPI->glGenProgramPipelines = (PFNGLGENPROGRAMPIPELINESPROC)glbGetProcAddress("glGenProgramPipelines"); + pAPI->glIsProgramPipeline = (PFNGLISPROGRAMPIPELINEPROC)glbGetProcAddress("glIsProgramPipeline"); + pAPI->glGetProgramPipelineiv = (PFNGLGETPROGRAMPIPELINEIVPROC)glbGetProcAddress("glGetProgramPipelineiv"); + pAPI->glProgramUniform1i = (PFNGLPROGRAMUNIFORM1IPROC)glbGetProcAddress("glProgramUniform1i"); + pAPI->glProgramUniform1iv = (PFNGLPROGRAMUNIFORM1IVPROC)glbGetProcAddress("glProgramUniform1iv"); + pAPI->glProgramUniform1f = (PFNGLPROGRAMUNIFORM1FPROC)glbGetProcAddress("glProgramUniform1f"); + pAPI->glProgramUniform1fv = (PFNGLPROGRAMUNIFORM1FVPROC)glbGetProcAddress("glProgramUniform1fv"); + pAPI->glProgramUniform1d = (PFNGLPROGRAMUNIFORM1DPROC)glbGetProcAddress("glProgramUniform1d"); + pAPI->glProgramUniform1dv = (PFNGLPROGRAMUNIFORM1DVPROC)glbGetProcAddress("glProgramUniform1dv"); + pAPI->glProgramUniform1ui = (PFNGLPROGRAMUNIFORM1UIPROC)glbGetProcAddress("glProgramUniform1ui"); + pAPI->glProgramUniform1uiv = (PFNGLPROGRAMUNIFORM1UIVPROC)glbGetProcAddress("glProgramUniform1uiv"); + pAPI->glProgramUniform2i = (PFNGLPROGRAMUNIFORM2IPROC)glbGetProcAddress("glProgramUniform2i"); + pAPI->glProgramUniform2iv = (PFNGLPROGRAMUNIFORM2IVPROC)glbGetProcAddress("glProgramUniform2iv"); + pAPI->glProgramUniform2f = (PFNGLPROGRAMUNIFORM2FPROC)glbGetProcAddress("glProgramUniform2f"); + pAPI->glProgramUniform2fv = (PFNGLPROGRAMUNIFORM2FVPROC)glbGetProcAddress("glProgramUniform2fv"); + pAPI->glProgramUniform2d = (PFNGLPROGRAMUNIFORM2DPROC)glbGetProcAddress("glProgramUniform2d"); + pAPI->glProgramUniform2dv = (PFNGLPROGRAMUNIFORM2DVPROC)glbGetProcAddress("glProgramUniform2dv"); + pAPI->glProgramUniform2ui = (PFNGLPROGRAMUNIFORM2UIPROC)glbGetProcAddress("glProgramUniform2ui"); + pAPI->glProgramUniform2uiv = (PFNGLPROGRAMUNIFORM2UIVPROC)glbGetProcAddress("glProgramUniform2uiv"); + pAPI->glProgramUniform3i = (PFNGLPROGRAMUNIFORM3IPROC)glbGetProcAddress("glProgramUniform3i"); + pAPI->glProgramUniform3iv = (PFNGLPROGRAMUNIFORM3IVPROC)glbGetProcAddress("glProgramUniform3iv"); + pAPI->glProgramUniform3f = (PFNGLPROGRAMUNIFORM3FPROC)glbGetProcAddress("glProgramUniform3f"); + pAPI->glProgramUniform3fv = (PFNGLPROGRAMUNIFORM3FVPROC)glbGetProcAddress("glProgramUniform3fv"); + pAPI->glProgramUniform3d = (PFNGLPROGRAMUNIFORM3DPROC)glbGetProcAddress("glProgramUniform3d"); + pAPI->glProgramUniform3dv = (PFNGLPROGRAMUNIFORM3DVPROC)glbGetProcAddress("glProgramUniform3dv"); + pAPI->glProgramUniform3ui = (PFNGLPROGRAMUNIFORM3UIPROC)glbGetProcAddress("glProgramUniform3ui"); + pAPI->glProgramUniform3uiv = (PFNGLPROGRAMUNIFORM3UIVPROC)glbGetProcAddress("glProgramUniform3uiv"); + pAPI->glProgramUniform4i = (PFNGLPROGRAMUNIFORM4IPROC)glbGetProcAddress("glProgramUniform4i"); + pAPI->glProgramUniform4iv = (PFNGLPROGRAMUNIFORM4IVPROC)glbGetProcAddress("glProgramUniform4iv"); + pAPI->glProgramUniform4f = (PFNGLPROGRAMUNIFORM4FPROC)glbGetProcAddress("glProgramUniform4f"); + pAPI->glProgramUniform4fv = (PFNGLPROGRAMUNIFORM4FVPROC)glbGetProcAddress("glProgramUniform4fv"); + pAPI->glProgramUniform4d = (PFNGLPROGRAMUNIFORM4DPROC)glbGetProcAddress("glProgramUniform4d"); + pAPI->glProgramUniform4dv = (PFNGLPROGRAMUNIFORM4DVPROC)glbGetProcAddress("glProgramUniform4dv"); + pAPI->glProgramUniform4ui = (PFNGLPROGRAMUNIFORM4UIPROC)glbGetProcAddress("glProgramUniform4ui"); + pAPI->glProgramUniform4uiv = (PFNGLPROGRAMUNIFORM4UIVPROC)glbGetProcAddress("glProgramUniform4uiv"); + pAPI->glProgramUniformMatrix2fv = (PFNGLPROGRAMUNIFORMMATRIX2FVPROC)glbGetProcAddress("glProgramUniformMatrix2fv"); + pAPI->glProgramUniformMatrix3fv = (PFNGLPROGRAMUNIFORMMATRIX3FVPROC)glbGetProcAddress("glProgramUniformMatrix3fv"); + pAPI->glProgramUniformMatrix4fv = (PFNGLPROGRAMUNIFORMMATRIX4FVPROC)glbGetProcAddress("glProgramUniformMatrix4fv"); + pAPI->glProgramUniformMatrix2dv = (PFNGLPROGRAMUNIFORMMATRIX2DVPROC)glbGetProcAddress("glProgramUniformMatrix2dv"); + pAPI->glProgramUniformMatrix3dv = (PFNGLPROGRAMUNIFORMMATRIX3DVPROC)glbGetProcAddress("glProgramUniformMatrix3dv"); + pAPI->glProgramUniformMatrix4dv = (PFNGLPROGRAMUNIFORMMATRIX4DVPROC)glbGetProcAddress("glProgramUniformMatrix4dv"); + pAPI->glProgramUniformMatrix2x3fv = (PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC)glbGetProcAddress("glProgramUniformMatrix2x3fv"); + pAPI->glProgramUniformMatrix3x2fv = (PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC)glbGetProcAddress("glProgramUniformMatrix3x2fv"); + pAPI->glProgramUniformMatrix2x4fv = (PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC)glbGetProcAddress("glProgramUniformMatrix2x4fv"); + pAPI->glProgramUniformMatrix4x2fv = (PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC)glbGetProcAddress("glProgramUniformMatrix4x2fv"); + pAPI->glProgramUniformMatrix3x4fv = (PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC)glbGetProcAddress("glProgramUniformMatrix3x4fv"); + pAPI->glProgramUniformMatrix4x3fv = (PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC)glbGetProcAddress("glProgramUniformMatrix4x3fv"); + pAPI->glProgramUniformMatrix2x3dv = (PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC)glbGetProcAddress("glProgramUniformMatrix2x3dv"); + pAPI->glProgramUniformMatrix3x2dv = (PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC)glbGetProcAddress("glProgramUniformMatrix3x2dv"); + pAPI->glProgramUniformMatrix2x4dv = (PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC)glbGetProcAddress("glProgramUniformMatrix2x4dv"); + pAPI->glProgramUniformMatrix4x2dv = (PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC)glbGetProcAddress("glProgramUniformMatrix4x2dv"); + pAPI->glProgramUniformMatrix3x4dv = (PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC)glbGetProcAddress("glProgramUniformMatrix3x4dv"); + pAPI->glProgramUniformMatrix4x3dv = (PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC)glbGetProcAddress("glProgramUniformMatrix4x3dv"); + pAPI->glValidateProgramPipeline = (PFNGLVALIDATEPROGRAMPIPELINEPROC)glbGetProcAddress("glValidateProgramPipeline"); + pAPI->glGetProgramPipelineInfoLog = (PFNGLGETPROGRAMPIPELINEINFOLOGPROC)glbGetProcAddress("glGetProgramPipelineInfoLog"); + pAPI->glVertexAttribL1d = (PFNGLVERTEXATTRIBL1DPROC)glbGetProcAddress("glVertexAttribL1d"); + pAPI->glVertexAttribL2d = (PFNGLVERTEXATTRIBL2DPROC)glbGetProcAddress("glVertexAttribL2d"); + pAPI->glVertexAttribL3d = (PFNGLVERTEXATTRIBL3DPROC)glbGetProcAddress("glVertexAttribL3d"); + pAPI->glVertexAttribL4d = (PFNGLVERTEXATTRIBL4DPROC)glbGetProcAddress("glVertexAttribL4d"); + pAPI->glVertexAttribL1dv = (PFNGLVERTEXATTRIBL1DVPROC)glbGetProcAddress("glVertexAttribL1dv"); + pAPI->glVertexAttribL2dv = (PFNGLVERTEXATTRIBL2DVPROC)glbGetProcAddress("glVertexAttribL2dv"); + pAPI->glVertexAttribL3dv = (PFNGLVERTEXATTRIBL3DVPROC)glbGetProcAddress("glVertexAttribL3dv"); + pAPI->glVertexAttribL4dv = (PFNGLVERTEXATTRIBL4DVPROC)glbGetProcAddress("glVertexAttribL4dv"); + pAPI->glVertexAttribLPointer = (PFNGLVERTEXATTRIBLPOINTERPROC)glbGetProcAddress("glVertexAttribLPointer"); + pAPI->glGetVertexAttribLdv = (PFNGLGETVERTEXATTRIBLDVPROC)glbGetProcAddress("glGetVertexAttribLdv"); + pAPI->glViewportArrayv = (PFNGLVIEWPORTARRAYVPROC)glbGetProcAddress("glViewportArrayv"); + pAPI->glViewportIndexedf = (PFNGLVIEWPORTINDEXEDFPROC)glbGetProcAddress("glViewportIndexedf"); + pAPI->glViewportIndexedfv = (PFNGLVIEWPORTINDEXEDFVPROC)glbGetProcAddress("glViewportIndexedfv"); + pAPI->glScissorArrayv = (PFNGLSCISSORARRAYVPROC)glbGetProcAddress("glScissorArrayv"); + pAPI->glScissorIndexed = (PFNGLSCISSORINDEXEDPROC)glbGetProcAddress("glScissorIndexed"); + pAPI->glScissorIndexedv = (PFNGLSCISSORINDEXEDVPROC)glbGetProcAddress("glScissorIndexedv"); + pAPI->glDepthRangeArrayv = (PFNGLDEPTHRANGEARRAYVPROC)glbGetProcAddress("glDepthRangeArrayv"); + pAPI->glDepthRangeIndexed = (PFNGLDEPTHRANGEINDEXEDPROC)glbGetProcAddress("glDepthRangeIndexed"); + pAPI->glGetFloati_v = (PFNGLGETFLOATI_VPROC)glbGetProcAddress("glGetFloati_v"); + pAPI->glGetDoublei_v = (PFNGLGETDOUBLEI_VPROC)glbGetProcAddress("glGetDoublei_v"); + pAPI->glDrawArraysInstancedBaseInstance = (PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC)glbGetProcAddress("glDrawArraysInstancedBaseInstance"); + pAPI->glDrawElementsInstancedBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC)glbGetProcAddress("glDrawElementsInstancedBaseInstance"); + pAPI->glDrawElementsInstancedBaseVertexBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC)glbGetProcAddress("glDrawElementsInstancedBaseVertexBaseInstance"); + pAPI->glGetInternalformativ = (PFNGLGETINTERNALFORMATIVPROC)glbGetProcAddress("glGetInternalformativ"); + pAPI->glGetActiveAtomicCounterBufferiv = (PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC)glbGetProcAddress("glGetActiveAtomicCounterBufferiv"); + pAPI->glBindImageTexture = (PFNGLBINDIMAGETEXTUREPROC)glbGetProcAddress("glBindImageTexture"); + pAPI->glMemoryBarrier = (PFNGLMEMORYBARRIERPROC)glbGetProcAddress("glMemoryBarrier"); + pAPI->glTexStorage1D = (PFNGLTEXSTORAGE1DPROC)glbGetProcAddress("glTexStorage1D"); + pAPI->glTexStorage2D = (PFNGLTEXSTORAGE2DPROC)glbGetProcAddress("glTexStorage2D"); + pAPI->glTexStorage3D = (PFNGLTEXSTORAGE3DPROC)glbGetProcAddress("glTexStorage3D"); + pAPI->glDrawTransformFeedbackInstanced = (PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC)glbGetProcAddress("glDrawTransformFeedbackInstanced"); + pAPI->glDrawTransformFeedbackStreamInstanced = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC)glbGetProcAddress("glDrawTransformFeedbackStreamInstanced"); + pAPI->glClearBufferData = (PFNGLCLEARBUFFERDATAPROC)glbGetProcAddress("glClearBufferData"); + pAPI->glClearBufferSubData = (PFNGLCLEARBUFFERSUBDATAPROC)glbGetProcAddress("glClearBufferSubData"); + pAPI->glDispatchCompute = (PFNGLDISPATCHCOMPUTEPROC)glbGetProcAddress("glDispatchCompute"); + pAPI->glDispatchComputeIndirect = (PFNGLDISPATCHCOMPUTEINDIRECTPROC)glbGetProcAddress("glDispatchComputeIndirect"); + pAPI->glCopyImageSubData = (PFNGLCOPYIMAGESUBDATAPROC)glbGetProcAddress("glCopyImageSubData"); + pAPI->glFramebufferParameteri = (PFNGLFRAMEBUFFERPARAMETERIPROC)glbGetProcAddress("glFramebufferParameteri"); + pAPI->glGetFramebufferParameteriv = (PFNGLGETFRAMEBUFFERPARAMETERIVPROC)glbGetProcAddress("glGetFramebufferParameteriv"); + pAPI->glGetInternalformati64v = (PFNGLGETINTERNALFORMATI64VPROC)glbGetProcAddress("glGetInternalformati64v"); + pAPI->glInvalidateTexSubImage = (PFNGLINVALIDATETEXSUBIMAGEPROC)glbGetProcAddress("glInvalidateTexSubImage"); + pAPI->glInvalidateTexImage = (PFNGLINVALIDATETEXIMAGEPROC)glbGetProcAddress("glInvalidateTexImage"); + pAPI->glInvalidateBufferSubData = (PFNGLINVALIDATEBUFFERSUBDATAPROC)glbGetProcAddress("glInvalidateBufferSubData"); + pAPI->glInvalidateBufferData = (PFNGLINVALIDATEBUFFERDATAPROC)glbGetProcAddress("glInvalidateBufferData"); + pAPI->glInvalidateFramebuffer = (PFNGLINVALIDATEFRAMEBUFFERPROC)glbGetProcAddress("glInvalidateFramebuffer"); + pAPI->glInvalidateSubFramebuffer = (PFNGLINVALIDATESUBFRAMEBUFFERPROC)glbGetProcAddress("glInvalidateSubFramebuffer"); + pAPI->glMultiDrawArraysIndirect = (PFNGLMULTIDRAWARRAYSINDIRECTPROC)glbGetProcAddress("glMultiDrawArraysIndirect"); + pAPI->glMultiDrawElementsIndirect = (PFNGLMULTIDRAWELEMENTSINDIRECTPROC)glbGetProcAddress("glMultiDrawElementsIndirect"); + pAPI->glGetProgramInterfaceiv = (PFNGLGETPROGRAMINTERFACEIVPROC)glbGetProcAddress("glGetProgramInterfaceiv"); + pAPI->glGetProgramResourceIndex = (PFNGLGETPROGRAMRESOURCEINDEXPROC)glbGetProcAddress("glGetProgramResourceIndex"); + pAPI->glGetProgramResourceName = (PFNGLGETPROGRAMRESOURCENAMEPROC)glbGetProcAddress("glGetProgramResourceName"); + pAPI->glGetProgramResourceiv = (PFNGLGETPROGRAMRESOURCEIVPROC)glbGetProcAddress("glGetProgramResourceiv"); + pAPI->glGetProgramResourceLocation = (PFNGLGETPROGRAMRESOURCELOCATIONPROC)glbGetProcAddress("glGetProgramResourceLocation"); + pAPI->glGetProgramResourceLocationIndex = (PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC)glbGetProcAddress("glGetProgramResourceLocationIndex"); + pAPI->glShaderStorageBlockBinding = (PFNGLSHADERSTORAGEBLOCKBINDINGPROC)glbGetProcAddress("glShaderStorageBlockBinding"); + pAPI->glTexBufferRange = (PFNGLTEXBUFFERRANGEPROC)glbGetProcAddress("glTexBufferRange"); + pAPI->glTexStorage2DMultisample = (PFNGLTEXSTORAGE2DMULTISAMPLEPROC)glbGetProcAddress("glTexStorage2DMultisample"); + pAPI->glTexStorage3DMultisample = (PFNGLTEXSTORAGE3DMULTISAMPLEPROC)glbGetProcAddress("glTexStorage3DMultisample"); + pAPI->glTextureView = (PFNGLTEXTUREVIEWPROC)glbGetProcAddress("glTextureView"); + pAPI->glBindVertexBuffer = (PFNGLBINDVERTEXBUFFERPROC)glbGetProcAddress("glBindVertexBuffer"); + pAPI->glVertexAttribFormat = (PFNGLVERTEXATTRIBFORMATPROC)glbGetProcAddress("glVertexAttribFormat"); + pAPI->glVertexAttribIFormat = (PFNGLVERTEXATTRIBIFORMATPROC)glbGetProcAddress("glVertexAttribIFormat"); + pAPI->glVertexAttribLFormat = (PFNGLVERTEXATTRIBLFORMATPROC)glbGetProcAddress("glVertexAttribLFormat"); + pAPI->glVertexAttribBinding = (PFNGLVERTEXATTRIBBINDINGPROC)glbGetProcAddress("glVertexAttribBinding"); + pAPI->glVertexBindingDivisor = (PFNGLVERTEXBINDINGDIVISORPROC)glbGetProcAddress("glVertexBindingDivisor"); + pAPI->glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC)glbGetProcAddress("glDebugMessageControl"); + pAPI->glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC)glbGetProcAddress("glDebugMessageInsert"); + pAPI->glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC)glbGetProcAddress("glDebugMessageCallback"); + pAPI->glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC)glbGetProcAddress("glGetDebugMessageLog"); + pAPI->glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC)glbGetProcAddress("glPushDebugGroup"); + pAPI->glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC)glbGetProcAddress("glPopDebugGroup"); + pAPI->glObjectLabel = (PFNGLOBJECTLABELPROC)glbGetProcAddress("glObjectLabel"); + pAPI->glGetObjectLabel = (PFNGLGETOBJECTLABELPROC)glbGetProcAddress("glGetObjectLabel"); + pAPI->glObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC)glbGetProcAddress("glObjectPtrLabel"); + pAPI->glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC)glbGetProcAddress("glGetObjectPtrLabel"); + pAPI->glBufferStorage = (PFNGLBUFFERSTORAGEPROC)glbGetProcAddress("glBufferStorage"); + pAPI->glClearTexImage = (PFNGLCLEARTEXIMAGEPROC)glbGetProcAddress("glClearTexImage"); + pAPI->glClearTexSubImage = (PFNGLCLEARTEXSUBIMAGEPROC)glbGetProcAddress("glClearTexSubImage"); + pAPI->glBindBuffersBase = (PFNGLBINDBUFFERSBASEPROC)glbGetProcAddress("glBindBuffersBase"); + pAPI->glBindBuffersRange = (PFNGLBINDBUFFERSRANGEPROC)glbGetProcAddress("glBindBuffersRange"); + pAPI->glBindTextures = (PFNGLBINDTEXTURESPROC)glbGetProcAddress("glBindTextures"); + pAPI->glBindSamplers = (PFNGLBINDSAMPLERSPROC)glbGetProcAddress("glBindSamplers"); + pAPI->glBindImageTextures = (PFNGLBINDIMAGETEXTURESPROC)glbGetProcAddress("glBindImageTextures"); + pAPI->glBindVertexBuffers = (PFNGLBINDVERTEXBUFFERSPROC)glbGetProcAddress("glBindVertexBuffers"); + pAPI->glClipControl = (PFNGLCLIPCONTROLPROC)glbGetProcAddress("glClipControl"); + pAPI->glCreateTransformFeedbacks = (PFNGLCREATETRANSFORMFEEDBACKSPROC)glbGetProcAddress("glCreateTransformFeedbacks"); + pAPI->glTransformFeedbackBufferBase = (PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC)glbGetProcAddress("glTransformFeedbackBufferBase"); + pAPI->glTransformFeedbackBufferRange = (PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC)glbGetProcAddress("glTransformFeedbackBufferRange"); + pAPI->glGetTransformFeedbackiv = (PFNGLGETTRANSFORMFEEDBACKIVPROC)glbGetProcAddress("glGetTransformFeedbackiv"); + pAPI->glGetTransformFeedbacki_v = (PFNGLGETTRANSFORMFEEDBACKI_VPROC)glbGetProcAddress("glGetTransformFeedbacki_v"); + pAPI->glGetTransformFeedbacki64_v = (PFNGLGETTRANSFORMFEEDBACKI64_VPROC)glbGetProcAddress("glGetTransformFeedbacki64_v"); + pAPI->glCreateBuffers = (PFNGLCREATEBUFFERSPROC)glbGetProcAddress("glCreateBuffers"); + pAPI->glNamedBufferStorage = (PFNGLNAMEDBUFFERSTORAGEPROC)glbGetProcAddress("glNamedBufferStorage"); + pAPI->glNamedBufferData = (PFNGLNAMEDBUFFERDATAPROC)glbGetProcAddress("glNamedBufferData"); + pAPI->glNamedBufferSubData = (PFNGLNAMEDBUFFERSUBDATAPROC)glbGetProcAddress("glNamedBufferSubData"); + pAPI->glCopyNamedBufferSubData = (PFNGLCOPYNAMEDBUFFERSUBDATAPROC)glbGetProcAddress("glCopyNamedBufferSubData"); + pAPI->glClearNamedBufferData = (PFNGLCLEARNAMEDBUFFERDATAPROC)glbGetProcAddress("glClearNamedBufferData"); + pAPI->glClearNamedBufferSubData = (PFNGLCLEARNAMEDBUFFERSUBDATAPROC)glbGetProcAddress("glClearNamedBufferSubData"); + pAPI->glMapNamedBuffer = (PFNGLMAPNAMEDBUFFERPROC)glbGetProcAddress("glMapNamedBuffer"); + pAPI->glMapNamedBufferRange = (PFNGLMAPNAMEDBUFFERRANGEPROC)glbGetProcAddress("glMapNamedBufferRange"); + pAPI->glUnmapNamedBuffer = (PFNGLUNMAPNAMEDBUFFERPROC)glbGetProcAddress("glUnmapNamedBuffer"); + pAPI->glFlushMappedNamedBufferRange = (PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC)glbGetProcAddress("glFlushMappedNamedBufferRange"); + pAPI->glGetNamedBufferParameteriv = (PFNGLGETNAMEDBUFFERPARAMETERIVPROC)glbGetProcAddress("glGetNamedBufferParameteriv"); + pAPI->glGetNamedBufferParameteri64v = (PFNGLGETNAMEDBUFFERPARAMETERI64VPROC)glbGetProcAddress("glGetNamedBufferParameteri64v"); + pAPI->glGetNamedBufferPointerv = (PFNGLGETNAMEDBUFFERPOINTERVPROC)glbGetProcAddress("glGetNamedBufferPointerv"); + pAPI->glGetNamedBufferSubData = (PFNGLGETNAMEDBUFFERSUBDATAPROC)glbGetProcAddress("glGetNamedBufferSubData"); + pAPI->glCreateFramebuffers = (PFNGLCREATEFRAMEBUFFERSPROC)glbGetProcAddress("glCreateFramebuffers"); + pAPI->glNamedFramebufferRenderbuffer = (PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC)glbGetProcAddress("glNamedFramebufferRenderbuffer"); + pAPI->glNamedFramebufferParameteri = (PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC)glbGetProcAddress("glNamedFramebufferParameteri"); + pAPI->glNamedFramebufferTexture = (PFNGLNAMEDFRAMEBUFFERTEXTUREPROC)glbGetProcAddress("glNamedFramebufferTexture"); + pAPI->glNamedFramebufferTextureLayer = (PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC)glbGetProcAddress("glNamedFramebufferTextureLayer"); + pAPI->glNamedFramebufferDrawBuffer = (PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC)glbGetProcAddress("glNamedFramebufferDrawBuffer"); + pAPI->glNamedFramebufferDrawBuffers = (PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC)glbGetProcAddress("glNamedFramebufferDrawBuffers"); + pAPI->glNamedFramebufferReadBuffer = (PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC)glbGetProcAddress("glNamedFramebufferReadBuffer"); + pAPI->glInvalidateNamedFramebufferData = (PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC)glbGetProcAddress("glInvalidateNamedFramebufferData"); + pAPI->glInvalidateNamedFramebufferSubData = (PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC)glbGetProcAddress("glInvalidateNamedFramebufferSubData"); + pAPI->glClearNamedFramebufferiv = (PFNGLCLEARNAMEDFRAMEBUFFERIVPROC)glbGetProcAddress("glClearNamedFramebufferiv"); + pAPI->glClearNamedFramebufferuiv = (PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC)glbGetProcAddress("glClearNamedFramebufferuiv"); + pAPI->glClearNamedFramebufferfv = (PFNGLCLEARNAMEDFRAMEBUFFERFVPROC)glbGetProcAddress("glClearNamedFramebufferfv"); + pAPI->glClearNamedFramebufferfi = (PFNGLCLEARNAMEDFRAMEBUFFERFIPROC)glbGetProcAddress("glClearNamedFramebufferfi"); + pAPI->glBlitNamedFramebuffer = (PFNGLBLITNAMEDFRAMEBUFFERPROC)glbGetProcAddress("glBlitNamedFramebuffer"); + pAPI->glCheckNamedFramebufferStatus = (PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC)glbGetProcAddress("glCheckNamedFramebufferStatus"); + pAPI->glGetNamedFramebufferParameteriv = (PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC)glbGetProcAddress("glGetNamedFramebufferParameteriv"); + pAPI->glGetNamedFramebufferAttachmentParameteriv = (PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC)glbGetProcAddress("glGetNamedFramebufferAttachmentParameteriv"); + pAPI->glCreateRenderbuffers = (PFNGLCREATERENDERBUFFERSPROC)glbGetProcAddress("glCreateRenderbuffers"); + pAPI->glNamedRenderbufferStorage = (PFNGLNAMEDRENDERBUFFERSTORAGEPROC)glbGetProcAddress("glNamedRenderbufferStorage"); + pAPI->glNamedRenderbufferStorageMultisample = (PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC)glbGetProcAddress("glNamedRenderbufferStorageMultisample"); + pAPI->glGetNamedRenderbufferParameteriv = (PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC)glbGetProcAddress("glGetNamedRenderbufferParameteriv"); + pAPI->glCreateTextures = (PFNGLCREATETEXTURESPROC)glbGetProcAddress("glCreateTextures"); + pAPI->glTextureBuffer = (PFNGLTEXTUREBUFFERPROC)glbGetProcAddress("glTextureBuffer"); + pAPI->glTextureBufferRange = (PFNGLTEXTUREBUFFERRANGEPROC)glbGetProcAddress("glTextureBufferRange"); + pAPI->glTextureStorage1D = (PFNGLTEXTURESTORAGE1DPROC)glbGetProcAddress("glTextureStorage1D"); + pAPI->glTextureStorage2D = (PFNGLTEXTURESTORAGE2DPROC)glbGetProcAddress("glTextureStorage2D"); + pAPI->glTextureStorage3D = (PFNGLTEXTURESTORAGE3DPROC)glbGetProcAddress("glTextureStorage3D"); + pAPI->glTextureStorage2DMultisample = (PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC)glbGetProcAddress("glTextureStorage2DMultisample"); + pAPI->glTextureStorage3DMultisample = (PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC)glbGetProcAddress("glTextureStorage3DMultisample"); + pAPI->glTextureSubImage1D = (PFNGLTEXTURESUBIMAGE1DPROC)glbGetProcAddress("glTextureSubImage1D"); + pAPI->glTextureSubImage2D = (PFNGLTEXTURESUBIMAGE2DPROC)glbGetProcAddress("glTextureSubImage2D"); + pAPI->glTextureSubImage3D = (PFNGLTEXTURESUBIMAGE3DPROC)glbGetProcAddress("glTextureSubImage3D"); + pAPI->glCompressedTextureSubImage1D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC)glbGetProcAddress("glCompressedTextureSubImage1D"); + pAPI->glCompressedTextureSubImage2D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC)glbGetProcAddress("glCompressedTextureSubImage2D"); + pAPI->glCompressedTextureSubImage3D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC)glbGetProcAddress("glCompressedTextureSubImage3D"); + pAPI->glCopyTextureSubImage1D = (PFNGLCOPYTEXTURESUBIMAGE1DPROC)glbGetProcAddress("glCopyTextureSubImage1D"); + pAPI->glCopyTextureSubImage2D = (PFNGLCOPYTEXTURESUBIMAGE2DPROC)glbGetProcAddress("glCopyTextureSubImage2D"); + pAPI->glCopyTextureSubImage3D = (PFNGLCOPYTEXTURESUBIMAGE3DPROC)glbGetProcAddress("glCopyTextureSubImage3D"); + pAPI->glTextureParameterf = (PFNGLTEXTUREPARAMETERFPROC)glbGetProcAddress("glTextureParameterf"); + pAPI->glTextureParameterfv = (PFNGLTEXTUREPARAMETERFVPROC)glbGetProcAddress("glTextureParameterfv"); + pAPI->glTextureParameteri = (PFNGLTEXTUREPARAMETERIPROC)glbGetProcAddress("glTextureParameteri"); + pAPI->glTextureParameterIiv = (PFNGLTEXTUREPARAMETERIIVPROC)glbGetProcAddress("glTextureParameterIiv"); + pAPI->glTextureParameterIuiv = (PFNGLTEXTUREPARAMETERIUIVPROC)glbGetProcAddress("glTextureParameterIuiv"); + pAPI->glTextureParameteriv = (PFNGLTEXTUREPARAMETERIVPROC)glbGetProcAddress("glTextureParameteriv"); + pAPI->glGenerateTextureMipmap = (PFNGLGENERATETEXTUREMIPMAPPROC)glbGetProcAddress("glGenerateTextureMipmap"); + pAPI->glBindTextureUnit = (PFNGLBINDTEXTUREUNITPROC)glbGetProcAddress("glBindTextureUnit"); + pAPI->glGetTextureImage = (PFNGLGETTEXTUREIMAGEPROC)glbGetProcAddress("glGetTextureImage"); + pAPI->glGetCompressedTextureImage = (PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC)glbGetProcAddress("glGetCompressedTextureImage"); + pAPI->glGetTextureLevelParameterfv = (PFNGLGETTEXTURELEVELPARAMETERFVPROC)glbGetProcAddress("glGetTextureLevelParameterfv"); + pAPI->glGetTextureLevelParameteriv = (PFNGLGETTEXTURELEVELPARAMETERIVPROC)glbGetProcAddress("glGetTextureLevelParameteriv"); + pAPI->glGetTextureParameterfv = (PFNGLGETTEXTUREPARAMETERFVPROC)glbGetProcAddress("glGetTextureParameterfv"); + pAPI->glGetTextureParameterIiv = (PFNGLGETTEXTUREPARAMETERIIVPROC)glbGetProcAddress("glGetTextureParameterIiv"); + pAPI->glGetTextureParameterIuiv = (PFNGLGETTEXTUREPARAMETERIUIVPROC)glbGetProcAddress("glGetTextureParameterIuiv"); + pAPI->glGetTextureParameteriv = (PFNGLGETTEXTUREPARAMETERIVPROC)glbGetProcAddress("glGetTextureParameteriv"); + pAPI->glCreateVertexArrays = (PFNGLCREATEVERTEXARRAYSPROC)glbGetProcAddress("glCreateVertexArrays"); + pAPI->glDisableVertexArrayAttrib = (PFNGLDISABLEVERTEXARRAYATTRIBPROC)glbGetProcAddress("glDisableVertexArrayAttrib"); + pAPI->glEnableVertexArrayAttrib = (PFNGLENABLEVERTEXARRAYATTRIBPROC)glbGetProcAddress("glEnableVertexArrayAttrib"); + pAPI->glVertexArrayElementBuffer = (PFNGLVERTEXARRAYELEMENTBUFFERPROC)glbGetProcAddress("glVertexArrayElementBuffer"); + pAPI->glVertexArrayVertexBuffer = (PFNGLVERTEXARRAYVERTEXBUFFERPROC)glbGetProcAddress("glVertexArrayVertexBuffer"); + pAPI->glVertexArrayVertexBuffers = (PFNGLVERTEXARRAYVERTEXBUFFERSPROC)glbGetProcAddress("glVertexArrayVertexBuffers"); + pAPI->glVertexArrayAttribBinding = (PFNGLVERTEXARRAYATTRIBBINDINGPROC)glbGetProcAddress("glVertexArrayAttribBinding"); + pAPI->glVertexArrayAttribFormat = (PFNGLVERTEXARRAYATTRIBFORMATPROC)glbGetProcAddress("glVertexArrayAttribFormat"); + pAPI->glVertexArrayAttribIFormat = (PFNGLVERTEXARRAYATTRIBIFORMATPROC)glbGetProcAddress("glVertexArrayAttribIFormat"); + pAPI->glVertexArrayAttribLFormat = (PFNGLVERTEXARRAYATTRIBLFORMATPROC)glbGetProcAddress("glVertexArrayAttribLFormat"); + pAPI->glVertexArrayBindingDivisor = (PFNGLVERTEXARRAYBINDINGDIVISORPROC)glbGetProcAddress("glVertexArrayBindingDivisor"); + pAPI->glGetVertexArrayiv = (PFNGLGETVERTEXARRAYIVPROC)glbGetProcAddress("glGetVertexArrayiv"); + pAPI->glGetVertexArrayIndexediv = (PFNGLGETVERTEXARRAYINDEXEDIVPROC)glbGetProcAddress("glGetVertexArrayIndexediv"); + pAPI->glGetVertexArrayIndexed64iv = (PFNGLGETVERTEXARRAYINDEXED64IVPROC)glbGetProcAddress("glGetVertexArrayIndexed64iv"); + pAPI->glCreateSamplers = (PFNGLCREATESAMPLERSPROC)glbGetProcAddress("glCreateSamplers"); + pAPI->glCreateProgramPipelines = (PFNGLCREATEPROGRAMPIPELINESPROC)glbGetProcAddress("glCreateProgramPipelines"); + pAPI->glCreateQueries = (PFNGLCREATEQUERIESPROC)glbGetProcAddress("glCreateQueries"); + pAPI->glGetQueryBufferObjecti64v = (PFNGLGETQUERYBUFFEROBJECTI64VPROC)glbGetProcAddress("glGetQueryBufferObjecti64v"); + pAPI->glGetQueryBufferObjectiv = (PFNGLGETQUERYBUFFEROBJECTIVPROC)glbGetProcAddress("glGetQueryBufferObjectiv"); + pAPI->glGetQueryBufferObjectui64v = (PFNGLGETQUERYBUFFEROBJECTUI64VPROC)glbGetProcAddress("glGetQueryBufferObjectui64v"); + pAPI->glGetQueryBufferObjectuiv = (PFNGLGETQUERYBUFFEROBJECTUIVPROC)glbGetProcAddress("glGetQueryBufferObjectuiv"); + pAPI->glMemoryBarrierByRegion = (PFNGLMEMORYBARRIERBYREGIONPROC)glbGetProcAddress("glMemoryBarrierByRegion"); + pAPI->glGetTextureSubImage = (PFNGLGETTEXTURESUBIMAGEPROC)glbGetProcAddress("glGetTextureSubImage"); + pAPI->glGetCompressedTextureSubImage = (PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC)glbGetProcAddress("glGetCompressedTextureSubImage"); + pAPI->glGetGraphicsResetStatus = (PFNGLGETGRAPHICSRESETSTATUSPROC)glbGetProcAddress("glGetGraphicsResetStatus"); + pAPI->glGetnCompressedTexImage = (PFNGLGETNCOMPRESSEDTEXIMAGEPROC)glbGetProcAddress("glGetnCompressedTexImage"); + pAPI->glGetnTexImage = (PFNGLGETNTEXIMAGEPROC)glbGetProcAddress("glGetnTexImage"); + pAPI->glGetnUniformdv = (PFNGLGETNUNIFORMDVPROC)glbGetProcAddress("glGetnUniformdv"); + pAPI->glGetnUniformfv = (PFNGLGETNUNIFORMFVPROC)glbGetProcAddress("glGetnUniformfv"); + pAPI->glGetnUniformiv = (PFNGLGETNUNIFORMIVPROC)glbGetProcAddress("glGetnUniformiv"); + pAPI->glGetnUniformuiv = (PFNGLGETNUNIFORMUIVPROC)glbGetProcAddress("glGetnUniformuiv"); + pAPI->glReadnPixels = (PFNGLREADNPIXELSPROC)glbGetProcAddress("glReadnPixels"); + pAPI->glGetnMapdv = (PFNGLGETNMAPDVPROC)glbGetProcAddress("glGetnMapdv"); + pAPI->glGetnMapfv = (PFNGLGETNMAPFVPROC)glbGetProcAddress("glGetnMapfv"); + pAPI->glGetnMapiv = (PFNGLGETNMAPIVPROC)glbGetProcAddress("glGetnMapiv"); + pAPI->glGetnPixelMapfv = (PFNGLGETNPIXELMAPFVPROC)glbGetProcAddress("glGetnPixelMapfv"); + pAPI->glGetnPixelMapuiv = (PFNGLGETNPIXELMAPUIVPROC)glbGetProcAddress("glGetnPixelMapuiv"); + pAPI->glGetnPixelMapusv = (PFNGLGETNPIXELMAPUSVPROC)glbGetProcAddress("glGetnPixelMapusv"); + pAPI->glGetnPolygonStipple = (PFNGLGETNPOLYGONSTIPPLEPROC)glbGetProcAddress("glGetnPolygonStipple"); + pAPI->glGetnColorTable = (PFNGLGETNCOLORTABLEPROC)glbGetProcAddress("glGetnColorTable"); + pAPI->glGetnConvolutionFilter = (PFNGLGETNCONVOLUTIONFILTERPROC)glbGetProcAddress("glGetnConvolutionFilter"); + pAPI->glGetnSeparableFilter = (PFNGLGETNSEPARABLEFILTERPROC)glbGetProcAddress("glGetnSeparableFilter"); + pAPI->glGetnHistogram = (PFNGLGETNHISTOGRAMPROC)glbGetProcAddress("glGetnHistogram"); + pAPI->glGetnMinmax = (PFNGLGETNMINMAXPROC)glbGetProcAddress("glGetnMinmax"); + pAPI->glTextureBarrier = (PFNGLTEXTUREBARRIERPROC)glbGetProcAddress("glTextureBarrier"); + pAPI->glSpecializeShader = (PFNGLSPECIALIZESHADERPROC)glbGetProcAddress("glSpecializeShader"); + pAPI->glMultiDrawArraysIndirectCount = (PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC)glbGetProcAddress("glMultiDrawArraysIndirectCount"); + pAPI->glMultiDrawElementsIndirectCount = (PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC)glbGetProcAddress("glMultiDrawElementsIndirectCount"); + pAPI->glPolygonOffsetClamp = (PFNGLPOLYGONOFFSETCLAMPPROC)glbGetProcAddress("glPolygonOffsetClamp"); +#if defined(GLBIND_WGL) + pAPI->wglCopyContext = (PFNWGLCOPYCONTEXTPROC)glbGetProcAddress("wglCopyContext"); + pAPI->wglCreateContext = (PFNWGLCREATECONTEXTPROC)glbGetProcAddress("wglCreateContext"); + pAPI->wglCreateLayerContext = (PFNWGLCREATELAYERCONTEXTPROC)glbGetProcAddress("wglCreateLayerContext"); + pAPI->wglDeleteContext = (PFNWGLDELETECONTEXTPROC)glbGetProcAddress("wglDeleteContext"); + pAPI->wglDescribeLayerPlane = (PFNWGLDESCRIBELAYERPLANEPROC)glbGetProcAddress("wglDescribeLayerPlane"); + pAPI->wglGetCurrentContext = (PFNWGLGETCURRENTCONTEXTPROC)glbGetProcAddress("wglGetCurrentContext"); + pAPI->wglGetCurrentDC = (PFNWGLGETCURRENTDCPROC)glbGetProcAddress("wglGetCurrentDC"); + pAPI->wglGetLayerPaletteEntries = (PFNWGLGETLAYERPALETTEENTRIESPROC)glbGetProcAddress("wglGetLayerPaletteEntries"); + pAPI->wglGetProcAddress = (PFNWGLGETPROCADDRESSPROC)glbGetProcAddress("wglGetProcAddress"); + pAPI->wglMakeCurrent = (PFNWGLMAKECURRENTPROC)glbGetProcAddress("wglMakeCurrent"); + pAPI->wglRealizeLayerPalette = (PFNWGLREALIZELAYERPALETTEPROC)glbGetProcAddress("wglRealizeLayerPalette"); + pAPI->wglSetLayerPaletteEntries = (PFNWGLSETLAYERPALETTEENTRIESPROC)glbGetProcAddress("wglSetLayerPaletteEntries"); + pAPI->wglShareLists = (PFNWGLSHARELISTSPROC)glbGetProcAddress("wglShareLists"); + pAPI->wglSwapLayerBuffers = (PFNWGLSWAPLAYERBUFFERSPROC)glbGetProcAddress("wglSwapLayerBuffers"); + pAPI->wglUseFontBitmapsA = (PFNWGLUSEFONTBITMAPSAPROC)glbGetProcAddress("wglUseFontBitmapsA"); + pAPI->wglUseFontBitmapsW = (PFNWGLUSEFONTBITMAPSWPROC)glbGetProcAddress("wglUseFontBitmapsW"); + pAPI->wglUseFontOutlinesA = (PFNWGLUSEFONTOUTLINESAPROC)glbGetProcAddress("wglUseFontOutlinesA"); + pAPI->wglUseFontOutlinesW = (PFNWGLUSEFONTOUTLINESWPROC)glbGetProcAddress("wglUseFontOutlinesW"); +#endif /* GLBIND_WGL */ +#if defined(GLBIND_GLX) + pAPI->glXChooseVisual = (PFNGLXCHOOSEVISUALPROC)glbGetProcAddress("glXChooseVisual"); + pAPI->glXCreateContext = (PFNGLXCREATECONTEXTPROC)glbGetProcAddress("glXCreateContext"); + pAPI->glXDestroyContext = (PFNGLXDESTROYCONTEXTPROC)glbGetProcAddress("glXDestroyContext"); + pAPI->glXMakeCurrent = (PFNGLXMAKECURRENTPROC)glbGetProcAddress("glXMakeCurrent"); + pAPI->glXCopyContext = (PFNGLXCOPYCONTEXTPROC)glbGetProcAddress("glXCopyContext"); + pAPI->glXSwapBuffers = (PFNGLXSWAPBUFFERSPROC)glbGetProcAddress("glXSwapBuffers"); + pAPI->glXCreateGLXPixmap = (PFNGLXCREATEGLXPIXMAPPROC)glbGetProcAddress("glXCreateGLXPixmap"); + pAPI->glXDestroyGLXPixmap = (PFNGLXDESTROYGLXPIXMAPPROC)glbGetProcAddress("glXDestroyGLXPixmap"); + pAPI->glXQueryExtension = (PFNGLXQUERYEXTENSIONPROC)glbGetProcAddress("glXQueryExtension"); + pAPI->glXQueryVersion = (PFNGLXQUERYVERSIONPROC)glbGetProcAddress("glXQueryVersion"); + pAPI->glXIsDirect = (PFNGLXISDIRECTPROC)glbGetProcAddress("glXIsDirect"); + pAPI->glXGetConfig = (PFNGLXGETCONFIGPROC)glbGetProcAddress("glXGetConfig"); + pAPI->glXGetCurrentContext = (PFNGLXGETCURRENTCONTEXTPROC)glbGetProcAddress("glXGetCurrentContext"); + pAPI->glXGetCurrentDrawable = (PFNGLXGETCURRENTDRAWABLEPROC)glbGetProcAddress("glXGetCurrentDrawable"); + pAPI->glXWaitGL = (PFNGLXWAITGLPROC)glbGetProcAddress("glXWaitGL"); + pAPI->glXWaitX = (PFNGLXWAITXPROC)glbGetProcAddress("glXWaitX"); + pAPI->glXUseXFont = (PFNGLXUSEXFONTPROC)glbGetProcAddress("glXUseXFont"); + pAPI->glXQueryExtensionsString = (PFNGLXQUERYEXTENSIONSSTRINGPROC)glbGetProcAddress("glXQueryExtensionsString"); + pAPI->glXQueryServerString = (PFNGLXQUERYSERVERSTRINGPROC)glbGetProcAddress("glXQueryServerString"); + pAPI->glXGetClientString = (PFNGLXGETCLIENTSTRINGPROC)glbGetProcAddress("glXGetClientString"); + pAPI->glXGetCurrentDisplay = (PFNGLXGETCURRENTDISPLAYPROC)glbGetProcAddress("glXGetCurrentDisplay"); + pAPI->glXGetFBConfigs = (PFNGLXGETFBCONFIGSPROC)glbGetProcAddress("glXGetFBConfigs"); + pAPI->glXChooseFBConfig = (PFNGLXCHOOSEFBCONFIGPROC)glbGetProcAddress("glXChooseFBConfig"); + pAPI->glXGetFBConfigAttrib = (PFNGLXGETFBCONFIGATTRIBPROC)glbGetProcAddress("glXGetFBConfigAttrib"); + pAPI->glXGetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC)glbGetProcAddress("glXGetVisualFromFBConfig"); + pAPI->glXCreateWindow = (PFNGLXCREATEWINDOWPROC)glbGetProcAddress("glXCreateWindow"); + pAPI->glXDestroyWindow = (PFNGLXDESTROYWINDOWPROC)glbGetProcAddress("glXDestroyWindow"); + pAPI->glXCreatePixmap = (PFNGLXCREATEPIXMAPPROC)glbGetProcAddress("glXCreatePixmap"); + pAPI->glXDestroyPixmap = (PFNGLXDESTROYPIXMAPPROC)glbGetProcAddress("glXDestroyPixmap"); + pAPI->glXCreatePbuffer = (PFNGLXCREATEPBUFFERPROC)glbGetProcAddress("glXCreatePbuffer"); + pAPI->glXDestroyPbuffer = (PFNGLXDESTROYPBUFFERPROC)glbGetProcAddress("glXDestroyPbuffer"); + pAPI->glXQueryDrawable = (PFNGLXQUERYDRAWABLEPROC)glbGetProcAddress("glXQueryDrawable"); + pAPI->glXCreateNewContext = (PFNGLXCREATENEWCONTEXTPROC)glbGetProcAddress("glXCreateNewContext"); + pAPI->glXMakeContextCurrent = (PFNGLXMAKECONTEXTCURRENTPROC)glbGetProcAddress("glXMakeContextCurrent"); + pAPI->glXGetCurrentReadDrawable = (PFNGLXGETCURRENTREADDRAWABLEPROC)glbGetProcAddress("glXGetCurrentReadDrawable"); + pAPI->glXQueryContext = (PFNGLXQUERYCONTEXTPROC)glbGetProcAddress("glXQueryContext"); + pAPI->glXSelectEvent = (PFNGLXSELECTEVENTPROC)glbGetProcAddress("glXSelectEvent"); + pAPI->glXGetSelectedEvent = (PFNGLXGETSELECTEDEVENTPROC)glbGetProcAddress("glXGetSelectedEvent"); + pAPI->glXGetProcAddress = (PFNGLXGETPROCADDRESSPROC)glbGetProcAddress("glXGetProcAddress"); +#endif /* GLBIND_GLX */ + pAPI->glTbufferMask3DFX = (PFNGLTBUFFERMASK3DFXPROC)glbGetProcAddress("glTbufferMask3DFX"); + pAPI->glDebugMessageEnableAMD = (PFNGLDEBUGMESSAGEENABLEAMDPROC)glbGetProcAddress("glDebugMessageEnableAMD"); + pAPI->glDebugMessageInsertAMD = (PFNGLDEBUGMESSAGEINSERTAMDPROC)glbGetProcAddress("glDebugMessageInsertAMD"); + pAPI->glDebugMessageCallbackAMD = (PFNGLDEBUGMESSAGECALLBACKAMDPROC)glbGetProcAddress("glDebugMessageCallbackAMD"); + pAPI->glGetDebugMessageLogAMD = (PFNGLGETDEBUGMESSAGELOGAMDPROC)glbGetProcAddress("glGetDebugMessageLogAMD"); + pAPI->glBlendFuncIndexedAMD = (PFNGLBLENDFUNCINDEXEDAMDPROC)glbGetProcAddress("glBlendFuncIndexedAMD"); + pAPI->glBlendFuncSeparateIndexedAMD = (PFNGLBLENDFUNCSEPARATEINDEXEDAMDPROC)glbGetProcAddress("glBlendFuncSeparateIndexedAMD"); + pAPI->glBlendEquationIndexedAMD = (PFNGLBLENDEQUATIONINDEXEDAMDPROC)glbGetProcAddress("glBlendEquationIndexedAMD"); + pAPI->glBlendEquationSeparateIndexedAMD = (PFNGLBLENDEQUATIONSEPARATEINDEXEDAMDPROC)glbGetProcAddress("glBlendEquationSeparateIndexedAMD"); + pAPI->glRenderbufferStorageMultisampleAdvancedAMD = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC)glbGetProcAddress("glRenderbufferStorageMultisampleAdvancedAMD"); + pAPI->glNamedRenderbufferStorageMultisampleAdvancedAMD = (PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC)glbGetProcAddress("glNamedRenderbufferStorageMultisampleAdvancedAMD"); + pAPI->glFramebufferSamplePositionsfvAMD = (PFNGLFRAMEBUFFERSAMPLEPOSITIONSFVAMDPROC)glbGetProcAddress("glFramebufferSamplePositionsfvAMD"); + pAPI->glNamedFramebufferSamplePositionsfvAMD = (PFNGLNAMEDFRAMEBUFFERSAMPLEPOSITIONSFVAMDPROC)glbGetProcAddress("glNamedFramebufferSamplePositionsfvAMD"); + pAPI->glGetFramebufferParameterfvAMD = (PFNGLGETFRAMEBUFFERPARAMETERFVAMDPROC)glbGetProcAddress("glGetFramebufferParameterfvAMD"); + pAPI->glGetNamedFramebufferParameterfvAMD = (PFNGLGETNAMEDFRAMEBUFFERPARAMETERFVAMDPROC)glbGetProcAddress("glGetNamedFramebufferParameterfvAMD"); + pAPI->glUniform1i64NV = (PFNGLUNIFORM1I64NVPROC)glbGetProcAddress("glUniform1i64NV"); + pAPI->glUniform2i64NV = (PFNGLUNIFORM2I64NVPROC)glbGetProcAddress("glUniform2i64NV"); + pAPI->glUniform3i64NV = (PFNGLUNIFORM3I64NVPROC)glbGetProcAddress("glUniform3i64NV"); + pAPI->glUniform4i64NV = (PFNGLUNIFORM4I64NVPROC)glbGetProcAddress("glUniform4i64NV"); + pAPI->glUniform1i64vNV = (PFNGLUNIFORM1I64VNVPROC)glbGetProcAddress("glUniform1i64vNV"); + pAPI->glUniform2i64vNV = (PFNGLUNIFORM2I64VNVPROC)glbGetProcAddress("glUniform2i64vNV"); + pAPI->glUniform3i64vNV = (PFNGLUNIFORM3I64VNVPROC)glbGetProcAddress("glUniform3i64vNV"); + pAPI->glUniform4i64vNV = (PFNGLUNIFORM4I64VNVPROC)glbGetProcAddress("glUniform4i64vNV"); + pAPI->glUniform1ui64NV = (PFNGLUNIFORM1UI64NVPROC)glbGetProcAddress("glUniform1ui64NV"); + pAPI->glUniform2ui64NV = (PFNGLUNIFORM2UI64NVPROC)glbGetProcAddress("glUniform2ui64NV"); + pAPI->glUniform3ui64NV = (PFNGLUNIFORM3UI64NVPROC)glbGetProcAddress("glUniform3ui64NV"); + pAPI->glUniform4ui64NV = (PFNGLUNIFORM4UI64NVPROC)glbGetProcAddress("glUniform4ui64NV"); + pAPI->glUniform1ui64vNV = (PFNGLUNIFORM1UI64VNVPROC)glbGetProcAddress("glUniform1ui64vNV"); + pAPI->glUniform2ui64vNV = (PFNGLUNIFORM2UI64VNVPROC)glbGetProcAddress("glUniform2ui64vNV"); + pAPI->glUniform3ui64vNV = (PFNGLUNIFORM3UI64VNVPROC)glbGetProcAddress("glUniform3ui64vNV"); + pAPI->glUniform4ui64vNV = (PFNGLUNIFORM4UI64VNVPROC)glbGetProcAddress("glUniform4ui64vNV"); + pAPI->glGetUniformi64vNV = (PFNGLGETUNIFORMI64VNVPROC)glbGetProcAddress("glGetUniformi64vNV"); + pAPI->glGetUniformui64vNV = (PFNGLGETUNIFORMUI64VNVPROC)glbGetProcAddress("glGetUniformui64vNV"); + pAPI->glProgramUniform1i64NV = (PFNGLPROGRAMUNIFORM1I64NVPROC)glbGetProcAddress("glProgramUniform1i64NV"); + pAPI->glProgramUniform2i64NV = (PFNGLPROGRAMUNIFORM2I64NVPROC)glbGetProcAddress("glProgramUniform2i64NV"); + pAPI->glProgramUniform3i64NV = (PFNGLPROGRAMUNIFORM3I64NVPROC)glbGetProcAddress("glProgramUniform3i64NV"); + pAPI->glProgramUniform4i64NV = (PFNGLPROGRAMUNIFORM4I64NVPROC)glbGetProcAddress("glProgramUniform4i64NV"); + pAPI->glProgramUniform1i64vNV = (PFNGLPROGRAMUNIFORM1I64VNVPROC)glbGetProcAddress("glProgramUniform1i64vNV"); + pAPI->glProgramUniform2i64vNV = (PFNGLPROGRAMUNIFORM2I64VNVPROC)glbGetProcAddress("glProgramUniform2i64vNV"); + pAPI->glProgramUniform3i64vNV = (PFNGLPROGRAMUNIFORM3I64VNVPROC)glbGetProcAddress("glProgramUniform3i64vNV"); + pAPI->glProgramUniform4i64vNV = (PFNGLPROGRAMUNIFORM4I64VNVPROC)glbGetProcAddress("glProgramUniform4i64vNV"); + pAPI->glProgramUniform1ui64NV = (PFNGLPROGRAMUNIFORM1UI64NVPROC)glbGetProcAddress("glProgramUniform1ui64NV"); + pAPI->glProgramUniform2ui64NV = (PFNGLPROGRAMUNIFORM2UI64NVPROC)glbGetProcAddress("glProgramUniform2ui64NV"); + pAPI->glProgramUniform3ui64NV = (PFNGLPROGRAMUNIFORM3UI64NVPROC)glbGetProcAddress("glProgramUniform3ui64NV"); + pAPI->glProgramUniform4ui64NV = (PFNGLPROGRAMUNIFORM4UI64NVPROC)glbGetProcAddress("glProgramUniform4ui64NV"); + pAPI->glProgramUniform1ui64vNV = (PFNGLPROGRAMUNIFORM1UI64VNVPROC)glbGetProcAddress("glProgramUniform1ui64vNV"); + pAPI->glProgramUniform2ui64vNV = (PFNGLPROGRAMUNIFORM2UI64VNVPROC)glbGetProcAddress("glProgramUniform2ui64vNV"); + pAPI->glProgramUniform3ui64vNV = (PFNGLPROGRAMUNIFORM3UI64VNVPROC)glbGetProcAddress("glProgramUniform3ui64vNV"); + pAPI->glProgramUniform4ui64vNV = (PFNGLPROGRAMUNIFORM4UI64VNVPROC)glbGetProcAddress("glProgramUniform4ui64vNV"); + pAPI->glVertexAttribParameteriAMD = (PFNGLVERTEXATTRIBPARAMETERIAMDPROC)glbGetProcAddress("glVertexAttribParameteriAMD"); + pAPI->glMultiDrawArraysIndirectAMD = (PFNGLMULTIDRAWARRAYSINDIRECTAMDPROC)glbGetProcAddress("glMultiDrawArraysIndirectAMD"); + pAPI->glMultiDrawElementsIndirectAMD = (PFNGLMULTIDRAWELEMENTSINDIRECTAMDPROC)glbGetProcAddress("glMultiDrawElementsIndirectAMD"); + pAPI->glGenNamesAMD = (PFNGLGENNAMESAMDPROC)glbGetProcAddress("glGenNamesAMD"); + pAPI->glDeleteNamesAMD = (PFNGLDELETENAMESAMDPROC)glbGetProcAddress("glDeleteNamesAMD"); + pAPI->glIsNameAMD = (PFNGLISNAMEAMDPROC)glbGetProcAddress("glIsNameAMD"); + pAPI->glQueryObjectParameteruiAMD = (PFNGLQUERYOBJECTPARAMETERUIAMDPROC)glbGetProcAddress("glQueryObjectParameteruiAMD"); + pAPI->glGetPerfMonitorGroupsAMD = (PFNGLGETPERFMONITORGROUPSAMDPROC)glbGetProcAddress("glGetPerfMonitorGroupsAMD"); + pAPI->glGetPerfMonitorCountersAMD = (PFNGLGETPERFMONITORCOUNTERSAMDPROC)glbGetProcAddress("glGetPerfMonitorCountersAMD"); + pAPI->glGetPerfMonitorGroupStringAMD = (PFNGLGETPERFMONITORGROUPSTRINGAMDPROC)glbGetProcAddress("glGetPerfMonitorGroupStringAMD"); + pAPI->glGetPerfMonitorCounterStringAMD = (PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC)glbGetProcAddress("glGetPerfMonitorCounterStringAMD"); + pAPI->glGetPerfMonitorCounterInfoAMD = (PFNGLGETPERFMONITORCOUNTERINFOAMDPROC)glbGetProcAddress("glGetPerfMonitorCounterInfoAMD"); + pAPI->glGenPerfMonitorsAMD = (PFNGLGENPERFMONITORSAMDPROC)glbGetProcAddress("glGenPerfMonitorsAMD"); + pAPI->glDeletePerfMonitorsAMD = (PFNGLDELETEPERFMONITORSAMDPROC)glbGetProcAddress("glDeletePerfMonitorsAMD"); + pAPI->glSelectPerfMonitorCountersAMD = (PFNGLSELECTPERFMONITORCOUNTERSAMDPROC)glbGetProcAddress("glSelectPerfMonitorCountersAMD"); + pAPI->glBeginPerfMonitorAMD = (PFNGLBEGINPERFMONITORAMDPROC)glbGetProcAddress("glBeginPerfMonitorAMD"); + pAPI->glEndPerfMonitorAMD = (PFNGLENDPERFMONITORAMDPROC)glbGetProcAddress("glEndPerfMonitorAMD"); + pAPI->glGetPerfMonitorCounterDataAMD = (PFNGLGETPERFMONITORCOUNTERDATAAMDPROC)glbGetProcAddress("glGetPerfMonitorCounterDataAMD"); + pAPI->glSetMultisamplefvAMD = (PFNGLSETMULTISAMPLEFVAMDPROC)glbGetProcAddress("glSetMultisamplefvAMD"); + pAPI->glTexStorageSparseAMD = (PFNGLTEXSTORAGESPARSEAMDPROC)glbGetProcAddress("glTexStorageSparseAMD"); + pAPI->glTextureStorageSparseAMD = (PFNGLTEXTURESTORAGESPARSEAMDPROC)glbGetProcAddress("glTextureStorageSparseAMD"); + pAPI->glStencilOpValueAMD = (PFNGLSTENCILOPVALUEAMDPROC)glbGetProcAddress("glStencilOpValueAMD"); + pAPI->glTessellationFactorAMD = (PFNGLTESSELLATIONFACTORAMDPROC)glbGetProcAddress("glTessellationFactorAMD"); + pAPI->glTessellationModeAMD = (PFNGLTESSELLATIONMODEAMDPROC)glbGetProcAddress("glTessellationModeAMD"); + pAPI->glElementPointerAPPLE = (PFNGLELEMENTPOINTERAPPLEPROC)glbGetProcAddress("glElementPointerAPPLE"); + pAPI->glDrawElementArrayAPPLE = (PFNGLDRAWELEMENTARRAYAPPLEPROC)glbGetProcAddress("glDrawElementArrayAPPLE"); + pAPI->glDrawRangeElementArrayAPPLE = (PFNGLDRAWRANGEELEMENTARRAYAPPLEPROC)glbGetProcAddress("glDrawRangeElementArrayAPPLE"); + pAPI->glMultiDrawElementArrayAPPLE = (PFNGLMULTIDRAWELEMENTARRAYAPPLEPROC)glbGetProcAddress("glMultiDrawElementArrayAPPLE"); + pAPI->glMultiDrawRangeElementArrayAPPLE = (PFNGLMULTIDRAWRANGEELEMENTARRAYAPPLEPROC)glbGetProcAddress("glMultiDrawRangeElementArrayAPPLE"); + pAPI->glGenFencesAPPLE = (PFNGLGENFENCESAPPLEPROC)glbGetProcAddress("glGenFencesAPPLE"); + pAPI->glDeleteFencesAPPLE = (PFNGLDELETEFENCESAPPLEPROC)glbGetProcAddress("glDeleteFencesAPPLE"); + pAPI->glSetFenceAPPLE = (PFNGLSETFENCEAPPLEPROC)glbGetProcAddress("glSetFenceAPPLE"); + pAPI->glIsFenceAPPLE = (PFNGLISFENCEAPPLEPROC)glbGetProcAddress("glIsFenceAPPLE"); + pAPI->glTestFenceAPPLE = (PFNGLTESTFENCEAPPLEPROC)glbGetProcAddress("glTestFenceAPPLE"); + pAPI->glFinishFenceAPPLE = (PFNGLFINISHFENCEAPPLEPROC)glbGetProcAddress("glFinishFenceAPPLE"); + pAPI->glTestObjectAPPLE = (PFNGLTESTOBJECTAPPLEPROC)glbGetProcAddress("glTestObjectAPPLE"); + pAPI->glFinishObjectAPPLE = (PFNGLFINISHOBJECTAPPLEPROC)glbGetProcAddress("glFinishObjectAPPLE"); + pAPI->glBufferParameteriAPPLE = (PFNGLBUFFERPARAMETERIAPPLEPROC)glbGetProcAddress("glBufferParameteriAPPLE"); + pAPI->glFlushMappedBufferRangeAPPLE = (PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC)glbGetProcAddress("glFlushMappedBufferRangeAPPLE"); + pAPI->glObjectPurgeableAPPLE = (PFNGLOBJECTPURGEABLEAPPLEPROC)glbGetProcAddress("glObjectPurgeableAPPLE"); + pAPI->glObjectUnpurgeableAPPLE = (PFNGLOBJECTUNPURGEABLEAPPLEPROC)glbGetProcAddress("glObjectUnpurgeableAPPLE"); + pAPI->glGetObjectParameterivAPPLE = (PFNGLGETOBJECTPARAMETERIVAPPLEPROC)glbGetProcAddress("glGetObjectParameterivAPPLE"); + pAPI->glTextureRangeAPPLE = (PFNGLTEXTURERANGEAPPLEPROC)glbGetProcAddress("glTextureRangeAPPLE"); + pAPI->glGetTexParameterPointervAPPLE = (PFNGLGETTEXPARAMETERPOINTERVAPPLEPROC)glbGetProcAddress("glGetTexParameterPointervAPPLE"); + pAPI->glBindVertexArrayAPPLE = (PFNGLBINDVERTEXARRAYAPPLEPROC)glbGetProcAddress("glBindVertexArrayAPPLE"); + pAPI->glDeleteVertexArraysAPPLE = (PFNGLDELETEVERTEXARRAYSAPPLEPROC)glbGetProcAddress("glDeleteVertexArraysAPPLE"); + pAPI->glGenVertexArraysAPPLE = (PFNGLGENVERTEXARRAYSAPPLEPROC)glbGetProcAddress("glGenVertexArraysAPPLE"); + pAPI->glIsVertexArrayAPPLE = (PFNGLISVERTEXARRAYAPPLEPROC)glbGetProcAddress("glIsVertexArrayAPPLE"); + pAPI->glVertexArrayRangeAPPLE = (PFNGLVERTEXARRAYRANGEAPPLEPROC)glbGetProcAddress("glVertexArrayRangeAPPLE"); + pAPI->glFlushVertexArrayRangeAPPLE = (PFNGLFLUSHVERTEXARRAYRANGEAPPLEPROC)glbGetProcAddress("glFlushVertexArrayRangeAPPLE"); + pAPI->glVertexArrayParameteriAPPLE = (PFNGLVERTEXARRAYPARAMETERIAPPLEPROC)glbGetProcAddress("glVertexArrayParameteriAPPLE"); + pAPI->glEnableVertexAttribAPPLE = (PFNGLENABLEVERTEXATTRIBAPPLEPROC)glbGetProcAddress("glEnableVertexAttribAPPLE"); + pAPI->glDisableVertexAttribAPPLE = (PFNGLDISABLEVERTEXATTRIBAPPLEPROC)glbGetProcAddress("glDisableVertexAttribAPPLE"); + pAPI->glIsVertexAttribEnabledAPPLE = (PFNGLISVERTEXATTRIBENABLEDAPPLEPROC)glbGetProcAddress("glIsVertexAttribEnabledAPPLE"); + pAPI->glMapVertexAttrib1dAPPLE = (PFNGLMAPVERTEXATTRIB1DAPPLEPROC)glbGetProcAddress("glMapVertexAttrib1dAPPLE"); + pAPI->glMapVertexAttrib1fAPPLE = (PFNGLMAPVERTEXATTRIB1FAPPLEPROC)glbGetProcAddress("glMapVertexAttrib1fAPPLE"); + pAPI->glMapVertexAttrib2dAPPLE = (PFNGLMAPVERTEXATTRIB2DAPPLEPROC)glbGetProcAddress("glMapVertexAttrib2dAPPLE"); + pAPI->glMapVertexAttrib2fAPPLE = (PFNGLMAPVERTEXATTRIB2FAPPLEPROC)glbGetProcAddress("glMapVertexAttrib2fAPPLE"); + pAPI->glPrimitiveBoundingBoxARB = (PFNGLPRIMITIVEBOUNDINGBOXARBPROC)glbGetProcAddress("glPrimitiveBoundingBoxARB"); + pAPI->glGetTextureHandleARB = (PFNGLGETTEXTUREHANDLEARBPROC)glbGetProcAddress("glGetTextureHandleARB"); + pAPI->glGetTextureSamplerHandleARB = (PFNGLGETTEXTURESAMPLERHANDLEARBPROC)glbGetProcAddress("glGetTextureSamplerHandleARB"); + pAPI->glMakeTextureHandleResidentARB = (PFNGLMAKETEXTUREHANDLERESIDENTARBPROC)glbGetProcAddress("glMakeTextureHandleResidentARB"); + pAPI->glMakeTextureHandleNonResidentARB = (PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC)glbGetProcAddress("glMakeTextureHandleNonResidentARB"); + pAPI->glGetImageHandleARB = (PFNGLGETIMAGEHANDLEARBPROC)glbGetProcAddress("glGetImageHandleARB"); + pAPI->glMakeImageHandleResidentARB = (PFNGLMAKEIMAGEHANDLERESIDENTARBPROC)glbGetProcAddress("glMakeImageHandleResidentARB"); + pAPI->glMakeImageHandleNonResidentARB = (PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC)glbGetProcAddress("glMakeImageHandleNonResidentARB"); + pAPI->glUniformHandleui64ARB = (PFNGLUNIFORMHANDLEUI64ARBPROC)glbGetProcAddress("glUniformHandleui64ARB"); + pAPI->glUniformHandleui64vARB = (PFNGLUNIFORMHANDLEUI64VARBPROC)glbGetProcAddress("glUniformHandleui64vARB"); + pAPI->glProgramUniformHandleui64ARB = (PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC)glbGetProcAddress("glProgramUniformHandleui64ARB"); + pAPI->glProgramUniformHandleui64vARB = (PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC)glbGetProcAddress("glProgramUniformHandleui64vARB"); + pAPI->glIsTextureHandleResidentARB = (PFNGLISTEXTUREHANDLERESIDENTARBPROC)glbGetProcAddress("glIsTextureHandleResidentARB"); + pAPI->glIsImageHandleResidentARB = (PFNGLISIMAGEHANDLERESIDENTARBPROC)glbGetProcAddress("glIsImageHandleResidentARB"); + pAPI->glVertexAttribL1ui64ARB = (PFNGLVERTEXATTRIBL1UI64ARBPROC)glbGetProcAddress("glVertexAttribL1ui64ARB"); + pAPI->glVertexAttribL1ui64vARB = (PFNGLVERTEXATTRIBL1UI64VARBPROC)glbGetProcAddress("glVertexAttribL1ui64vARB"); + pAPI->glGetVertexAttribLui64vARB = (PFNGLGETVERTEXATTRIBLUI64VARBPROC)glbGetProcAddress("glGetVertexAttribLui64vARB"); + pAPI->glCreateSyncFromCLeventARB = (PFNGLCREATESYNCFROMCLEVENTARBPROC)glbGetProcAddress("glCreateSyncFromCLeventARB"); + pAPI->glClampColorARB = (PFNGLCLAMPCOLORARBPROC)glbGetProcAddress("glClampColorARB"); + pAPI->glDispatchComputeGroupSizeARB = (PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC)glbGetProcAddress("glDispatchComputeGroupSizeARB"); + pAPI->glDebugMessageControlARB = (PFNGLDEBUGMESSAGECONTROLARBPROC)glbGetProcAddress("glDebugMessageControlARB"); + pAPI->glDebugMessageInsertARB = (PFNGLDEBUGMESSAGEINSERTARBPROC)glbGetProcAddress("glDebugMessageInsertARB"); + pAPI->glDebugMessageCallbackARB = (PFNGLDEBUGMESSAGECALLBACKARBPROC)glbGetProcAddress("glDebugMessageCallbackARB"); + pAPI->glGetDebugMessageLogARB = (PFNGLGETDEBUGMESSAGELOGARBPROC)glbGetProcAddress("glGetDebugMessageLogARB"); + pAPI->glDrawBuffersARB = (PFNGLDRAWBUFFERSARBPROC)glbGetProcAddress("glDrawBuffersARB"); + pAPI->glBlendEquationiARB = (PFNGLBLENDEQUATIONIARBPROC)glbGetProcAddress("glBlendEquationiARB"); + pAPI->glBlendEquationSeparateiARB = (PFNGLBLENDEQUATIONSEPARATEIARBPROC)glbGetProcAddress("glBlendEquationSeparateiARB"); + pAPI->glBlendFunciARB = (PFNGLBLENDFUNCIARBPROC)glbGetProcAddress("glBlendFunciARB"); + pAPI->glBlendFuncSeparateiARB = (PFNGLBLENDFUNCSEPARATEIARBPROC)glbGetProcAddress("glBlendFuncSeparateiARB"); + pAPI->glDrawArraysInstancedARB = (PFNGLDRAWARRAYSINSTANCEDARBPROC)glbGetProcAddress("glDrawArraysInstancedARB"); + pAPI->glDrawElementsInstancedARB = (PFNGLDRAWELEMENTSINSTANCEDARBPROC)glbGetProcAddress("glDrawElementsInstancedARB"); + pAPI->glProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC)glbGetProcAddress("glProgramStringARB"); + pAPI->glBindProgramARB = (PFNGLBINDPROGRAMARBPROC)glbGetProcAddress("glBindProgramARB"); + pAPI->glDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC)glbGetProcAddress("glDeleteProgramsARB"); + pAPI->glGenProgramsARB = (PFNGLGENPROGRAMSARBPROC)glbGetProcAddress("glGenProgramsARB"); + pAPI->glProgramEnvParameter4dARB = (PFNGLPROGRAMENVPARAMETER4DARBPROC)glbGetProcAddress("glProgramEnvParameter4dARB"); + pAPI->glProgramEnvParameter4dvARB = (PFNGLPROGRAMENVPARAMETER4DVARBPROC)glbGetProcAddress("glProgramEnvParameter4dvARB"); + pAPI->glProgramEnvParameter4fARB = (PFNGLPROGRAMENVPARAMETER4FARBPROC)glbGetProcAddress("glProgramEnvParameter4fARB"); + pAPI->glProgramEnvParameter4fvARB = (PFNGLPROGRAMENVPARAMETER4FVARBPROC)glbGetProcAddress("glProgramEnvParameter4fvARB"); + pAPI->glProgramLocalParameter4dARB = (PFNGLPROGRAMLOCALPARAMETER4DARBPROC)glbGetProcAddress("glProgramLocalParameter4dARB"); + pAPI->glProgramLocalParameter4dvARB = (PFNGLPROGRAMLOCALPARAMETER4DVARBPROC)glbGetProcAddress("glProgramLocalParameter4dvARB"); + pAPI->glProgramLocalParameter4fARB = (PFNGLPROGRAMLOCALPARAMETER4FARBPROC)glbGetProcAddress("glProgramLocalParameter4fARB"); + pAPI->glProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC)glbGetProcAddress("glProgramLocalParameter4fvARB"); + pAPI->glGetProgramEnvParameterdvARB = (PFNGLGETPROGRAMENVPARAMETERDVARBPROC)glbGetProcAddress("glGetProgramEnvParameterdvARB"); + pAPI->glGetProgramEnvParameterfvARB = (PFNGLGETPROGRAMENVPARAMETERFVARBPROC)glbGetProcAddress("glGetProgramEnvParameterfvARB"); + pAPI->glGetProgramLocalParameterdvARB = (PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC)glbGetProcAddress("glGetProgramLocalParameterdvARB"); + pAPI->glGetProgramLocalParameterfvARB = (PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC)glbGetProcAddress("glGetProgramLocalParameterfvARB"); + pAPI->glGetProgramivARB = (PFNGLGETPROGRAMIVARBPROC)glbGetProcAddress("glGetProgramivARB"); + pAPI->glGetProgramStringARB = (PFNGLGETPROGRAMSTRINGARBPROC)glbGetProcAddress("glGetProgramStringARB"); + pAPI->glIsProgramARB = (PFNGLISPROGRAMARBPROC)glbGetProcAddress("glIsProgramARB"); + pAPI->glProgramParameteriARB = (PFNGLPROGRAMPARAMETERIARBPROC)glbGetProcAddress("glProgramParameteriARB"); + pAPI->glFramebufferTextureARB = (PFNGLFRAMEBUFFERTEXTUREARBPROC)glbGetProcAddress("glFramebufferTextureARB"); + pAPI->glFramebufferTextureLayerARB = (PFNGLFRAMEBUFFERTEXTURELAYERARBPROC)glbGetProcAddress("glFramebufferTextureLayerARB"); + pAPI->glFramebufferTextureFaceARB = (PFNGLFRAMEBUFFERTEXTUREFACEARBPROC)glbGetProcAddress("glFramebufferTextureFaceARB"); + pAPI->glSpecializeShaderARB = (PFNGLSPECIALIZESHADERARBPROC)glbGetProcAddress("glSpecializeShaderARB"); + pAPI->glUniform1i64ARB = (PFNGLUNIFORM1I64ARBPROC)glbGetProcAddress("glUniform1i64ARB"); + pAPI->glUniform2i64ARB = (PFNGLUNIFORM2I64ARBPROC)glbGetProcAddress("glUniform2i64ARB"); + pAPI->glUniform3i64ARB = (PFNGLUNIFORM3I64ARBPROC)glbGetProcAddress("glUniform3i64ARB"); + pAPI->glUniform4i64ARB = (PFNGLUNIFORM4I64ARBPROC)glbGetProcAddress("glUniform4i64ARB"); + pAPI->glUniform1i64vARB = (PFNGLUNIFORM1I64VARBPROC)glbGetProcAddress("glUniform1i64vARB"); + pAPI->glUniform2i64vARB = (PFNGLUNIFORM2I64VARBPROC)glbGetProcAddress("glUniform2i64vARB"); + pAPI->glUniform3i64vARB = (PFNGLUNIFORM3I64VARBPROC)glbGetProcAddress("glUniform3i64vARB"); + pAPI->glUniform4i64vARB = (PFNGLUNIFORM4I64VARBPROC)glbGetProcAddress("glUniform4i64vARB"); + pAPI->glUniform1ui64ARB = (PFNGLUNIFORM1UI64ARBPROC)glbGetProcAddress("glUniform1ui64ARB"); + pAPI->glUniform2ui64ARB = (PFNGLUNIFORM2UI64ARBPROC)glbGetProcAddress("glUniform2ui64ARB"); + pAPI->glUniform3ui64ARB = (PFNGLUNIFORM3UI64ARBPROC)glbGetProcAddress("glUniform3ui64ARB"); + pAPI->glUniform4ui64ARB = (PFNGLUNIFORM4UI64ARBPROC)glbGetProcAddress("glUniform4ui64ARB"); + pAPI->glUniform1ui64vARB = (PFNGLUNIFORM1UI64VARBPROC)glbGetProcAddress("glUniform1ui64vARB"); + pAPI->glUniform2ui64vARB = (PFNGLUNIFORM2UI64VARBPROC)glbGetProcAddress("glUniform2ui64vARB"); + pAPI->glUniform3ui64vARB = (PFNGLUNIFORM3UI64VARBPROC)glbGetProcAddress("glUniform3ui64vARB"); + pAPI->glUniform4ui64vARB = (PFNGLUNIFORM4UI64VARBPROC)glbGetProcAddress("glUniform4ui64vARB"); + pAPI->glGetUniformi64vARB = (PFNGLGETUNIFORMI64VARBPROC)glbGetProcAddress("glGetUniformi64vARB"); + pAPI->glGetUniformui64vARB = (PFNGLGETUNIFORMUI64VARBPROC)glbGetProcAddress("glGetUniformui64vARB"); + pAPI->glGetnUniformi64vARB = (PFNGLGETNUNIFORMI64VARBPROC)glbGetProcAddress("glGetnUniformi64vARB"); + pAPI->glGetnUniformui64vARB = (PFNGLGETNUNIFORMUI64VARBPROC)glbGetProcAddress("glGetnUniformui64vARB"); + pAPI->glProgramUniform1i64ARB = (PFNGLPROGRAMUNIFORM1I64ARBPROC)glbGetProcAddress("glProgramUniform1i64ARB"); + pAPI->glProgramUniform2i64ARB = (PFNGLPROGRAMUNIFORM2I64ARBPROC)glbGetProcAddress("glProgramUniform2i64ARB"); + pAPI->glProgramUniform3i64ARB = (PFNGLPROGRAMUNIFORM3I64ARBPROC)glbGetProcAddress("glProgramUniform3i64ARB"); + pAPI->glProgramUniform4i64ARB = (PFNGLPROGRAMUNIFORM4I64ARBPROC)glbGetProcAddress("glProgramUniform4i64ARB"); + pAPI->glProgramUniform1i64vARB = (PFNGLPROGRAMUNIFORM1I64VARBPROC)glbGetProcAddress("glProgramUniform1i64vARB"); + pAPI->glProgramUniform2i64vARB = (PFNGLPROGRAMUNIFORM2I64VARBPROC)glbGetProcAddress("glProgramUniform2i64vARB"); + pAPI->glProgramUniform3i64vARB = (PFNGLPROGRAMUNIFORM3I64VARBPROC)glbGetProcAddress("glProgramUniform3i64vARB"); + pAPI->glProgramUniform4i64vARB = (PFNGLPROGRAMUNIFORM4I64VARBPROC)glbGetProcAddress("glProgramUniform4i64vARB"); + pAPI->glProgramUniform1ui64ARB = (PFNGLPROGRAMUNIFORM1UI64ARBPROC)glbGetProcAddress("glProgramUniform1ui64ARB"); + pAPI->glProgramUniform2ui64ARB = (PFNGLPROGRAMUNIFORM2UI64ARBPROC)glbGetProcAddress("glProgramUniform2ui64ARB"); + pAPI->glProgramUniform3ui64ARB = (PFNGLPROGRAMUNIFORM3UI64ARBPROC)glbGetProcAddress("glProgramUniform3ui64ARB"); + pAPI->glProgramUniform4ui64ARB = (PFNGLPROGRAMUNIFORM4UI64ARBPROC)glbGetProcAddress("glProgramUniform4ui64ARB"); + pAPI->glProgramUniform1ui64vARB = (PFNGLPROGRAMUNIFORM1UI64VARBPROC)glbGetProcAddress("glProgramUniform1ui64vARB"); + pAPI->glProgramUniform2ui64vARB = (PFNGLPROGRAMUNIFORM2UI64VARBPROC)glbGetProcAddress("glProgramUniform2ui64vARB"); + pAPI->glProgramUniform3ui64vARB = (PFNGLPROGRAMUNIFORM3UI64VARBPROC)glbGetProcAddress("glProgramUniform3ui64vARB"); + pAPI->glProgramUniform4ui64vARB = (PFNGLPROGRAMUNIFORM4UI64VARBPROC)glbGetProcAddress("glProgramUniform4ui64vARB"); + pAPI->glColorTable = (PFNGLCOLORTABLEPROC)glbGetProcAddress("glColorTable"); + pAPI->glColorTableParameterfv = (PFNGLCOLORTABLEPARAMETERFVPROC)glbGetProcAddress("glColorTableParameterfv"); + pAPI->glColorTableParameteriv = (PFNGLCOLORTABLEPARAMETERIVPROC)glbGetProcAddress("glColorTableParameteriv"); + pAPI->glCopyColorTable = (PFNGLCOPYCOLORTABLEPROC)glbGetProcAddress("glCopyColorTable"); + pAPI->glGetColorTable = (PFNGLGETCOLORTABLEPROC)glbGetProcAddress("glGetColorTable"); + pAPI->glGetColorTableParameterfv = (PFNGLGETCOLORTABLEPARAMETERFVPROC)glbGetProcAddress("glGetColorTableParameterfv"); + pAPI->glGetColorTableParameteriv = (PFNGLGETCOLORTABLEPARAMETERIVPROC)glbGetProcAddress("glGetColorTableParameteriv"); + pAPI->glColorSubTable = (PFNGLCOLORSUBTABLEPROC)glbGetProcAddress("glColorSubTable"); + pAPI->glCopyColorSubTable = (PFNGLCOPYCOLORSUBTABLEPROC)glbGetProcAddress("glCopyColorSubTable"); + pAPI->glConvolutionFilter1D = (PFNGLCONVOLUTIONFILTER1DPROC)glbGetProcAddress("glConvolutionFilter1D"); + pAPI->glConvolutionFilter2D = (PFNGLCONVOLUTIONFILTER2DPROC)glbGetProcAddress("glConvolutionFilter2D"); + pAPI->glConvolutionParameterf = (PFNGLCONVOLUTIONPARAMETERFPROC)glbGetProcAddress("glConvolutionParameterf"); + pAPI->glConvolutionParameterfv = (PFNGLCONVOLUTIONPARAMETERFVPROC)glbGetProcAddress("glConvolutionParameterfv"); + pAPI->glConvolutionParameteri = (PFNGLCONVOLUTIONPARAMETERIPROC)glbGetProcAddress("glConvolutionParameteri"); + pAPI->glConvolutionParameteriv = (PFNGLCONVOLUTIONPARAMETERIVPROC)glbGetProcAddress("glConvolutionParameteriv"); + pAPI->glCopyConvolutionFilter1D = (PFNGLCOPYCONVOLUTIONFILTER1DPROC)glbGetProcAddress("glCopyConvolutionFilter1D"); + pAPI->glCopyConvolutionFilter2D = (PFNGLCOPYCONVOLUTIONFILTER2DPROC)glbGetProcAddress("glCopyConvolutionFilter2D"); + pAPI->glGetConvolutionFilter = (PFNGLGETCONVOLUTIONFILTERPROC)glbGetProcAddress("glGetConvolutionFilter"); + pAPI->glGetConvolutionParameterfv = (PFNGLGETCONVOLUTIONPARAMETERFVPROC)glbGetProcAddress("glGetConvolutionParameterfv"); + pAPI->glGetConvolutionParameteriv = (PFNGLGETCONVOLUTIONPARAMETERIVPROC)glbGetProcAddress("glGetConvolutionParameteriv"); + pAPI->glGetSeparableFilter = (PFNGLGETSEPARABLEFILTERPROC)glbGetProcAddress("glGetSeparableFilter"); + pAPI->glSeparableFilter2D = (PFNGLSEPARABLEFILTER2DPROC)glbGetProcAddress("glSeparableFilter2D"); + pAPI->glGetHistogram = (PFNGLGETHISTOGRAMPROC)glbGetProcAddress("glGetHistogram"); + pAPI->glGetHistogramParameterfv = (PFNGLGETHISTOGRAMPARAMETERFVPROC)glbGetProcAddress("glGetHistogramParameterfv"); + pAPI->glGetHistogramParameteriv = (PFNGLGETHISTOGRAMPARAMETERIVPROC)glbGetProcAddress("glGetHistogramParameteriv"); + pAPI->glGetMinmax = (PFNGLGETMINMAXPROC)glbGetProcAddress("glGetMinmax"); + pAPI->glGetMinmaxParameterfv = (PFNGLGETMINMAXPARAMETERFVPROC)glbGetProcAddress("glGetMinmaxParameterfv"); + pAPI->glGetMinmaxParameteriv = (PFNGLGETMINMAXPARAMETERIVPROC)glbGetProcAddress("glGetMinmaxParameteriv"); + pAPI->glHistogram = (PFNGLHISTOGRAMPROC)glbGetProcAddress("glHistogram"); + pAPI->glMinmax = (PFNGLMINMAXPROC)glbGetProcAddress("glMinmax"); + pAPI->glResetHistogram = (PFNGLRESETHISTOGRAMPROC)glbGetProcAddress("glResetHistogram"); + pAPI->glResetMinmax = (PFNGLRESETMINMAXPROC)glbGetProcAddress("glResetMinmax"); + pAPI->glMultiDrawArraysIndirectCountARB = (PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC)glbGetProcAddress("glMultiDrawArraysIndirectCountARB"); + pAPI->glMultiDrawElementsIndirectCountARB = (PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC)glbGetProcAddress("glMultiDrawElementsIndirectCountARB"); + pAPI->glVertexAttribDivisorARB = (PFNGLVERTEXATTRIBDIVISORARBPROC)glbGetProcAddress("glVertexAttribDivisorARB"); + pAPI->glCurrentPaletteMatrixARB = (PFNGLCURRENTPALETTEMATRIXARBPROC)glbGetProcAddress("glCurrentPaletteMatrixARB"); + pAPI->glMatrixIndexubvARB = (PFNGLMATRIXINDEXUBVARBPROC)glbGetProcAddress("glMatrixIndexubvARB"); + pAPI->glMatrixIndexusvARB = (PFNGLMATRIXINDEXUSVARBPROC)glbGetProcAddress("glMatrixIndexusvARB"); + pAPI->glMatrixIndexuivARB = (PFNGLMATRIXINDEXUIVARBPROC)glbGetProcAddress("glMatrixIndexuivARB"); + pAPI->glMatrixIndexPointerARB = (PFNGLMATRIXINDEXPOINTERARBPROC)glbGetProcAddress("glMatrixIndexPointerARB"); + pAPI->glSampleCoverageARB = (PFNGLSAMPLECOVERAGEARBPROC)glbGetProcAddress("glSampleCoverageARB"); + pAPI->glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC)glbGetProcAddress("glActiveTextureARB"); + pAPI->glClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC)glbGetProcAddress("glClientActiveTextureARB"); + pAPI->glMultiTexCoord1dARB = (PFNGLMULTITEXCOORD1DARBPROC)glbGetProcAddress("glMultiTexCoord1dARB"); + pAPI->glMultiTexCoord1dvARB = (PFNGLMULTITEXCOORD1DVARBPROC)glbGetProcAddress("glMultiTexCoord1dvARB"); + pAPI->glMultiTexCoord1fARB = (PFNGLMULTITEXCOORD1FARBPROC)glbGetProcAddress("glMultiTexCoord1fARB"); + pAPI->glMultiTexCoord1fvARB = (PFNGLMULTITEXCOORD1FVARBPROC)glbGetProcAddress("glMultiTexCoord1fvARB"); + pAPI->glMultiTexCoord1iARB = (PFNGLMULTITEXCOORD1IARBPROC)glbGetProcAddress("glMultiTexCoord1iARB"); + pAPI->glMultiTexCoord1ivARB = (PFNGLMULTITEXCOORD1IVARBPROC)glbGetProcAddress("glMultiTexCoord1ivARB"); + pAPI->glMultiTexCoord1sARB = (PFNGLMULTITEXCOORD1SARBPROC)glbGetProcAddress("glMultiTexCoord1sARB"); + pAPI->glMultiTexCoord1svARB = (PFNGLMULTITEXCOORD1SVARBPROC)glbGetProcAddress("glMultiTexCoord1svARB"); + pAPI->glMultiTexCoord2dARB = (PFNGLMULTITEXCOORD2DARBPROC)glbGetProcAddress("glMultiTexCoord2dARB"); + pAPI->glMultiTexCoord2dvARB = (PFNGLMULTITEXCOORD2DVARBPROC)glbGetProcAddress("glMultiTexCoord2dvARB"); + pAPI->glMultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC)glbGetProcAddress("glMultiTexCoord2fARB"); + pAPI->glMultiTexCoord2fvARB = (PFNGLMULTITEXCOORD2FVARBPROC)glbGetProcAddress("glMultiTexCoord2fvARB"); + pAPI->glMultiTexCoord2iARB = (PFNGLMULTITEXCOORD2IARBPROC)glbGetProcAddress("glMultiTexCoord2iARB"); + pAPI->glMultiTexCoord2ivARB = (PFNGLMULTITEXCOORD2IVARBPROC)glbGetProcAddress("glMultiTexCoord2ivARB"); + pAPI->glMultiTexCoord2sARB = (PFNGLMULTITEXCOORD2SARBPROC)glbGetProcAddress("glMultiTexCoord2sARB"); + pAPI->glMultiTexCoord2svARB = (PFNGLMULTITEXCOORD2SVARBPROC)glbGetProcAddress("glMultiTexCoord2svARB"); + pAPI->glMultiTexCoord3dARB = (PFNGLMULTITEXCOORD3DARBPROC)glbGetProcAddress("glMultiTexCoord3dARB"); + pAPI->glMultiTexCoord3dvARB = (PFNGLMULTITEXCOORD3DVARBPROC)glbGetProcAddress("glMultiTexCoord3dvARB"); + pAPI->glMultiTexCoord3fARB = (PFNGLMULTITEXCOORD3FARBPROC)glbGetProcAddress("glMultiTexCoord3fARB"); + pAPI->glMultiTexCoord3fvARB = (PFNGLMULTITEXCOORD3FVARBPROC)glbGetProcAddress("glMultiTexCoord3fvARB"); + pAPI->glMultiTexCoord3iARB = (PFNGLMULTITEXCOORD3IARBPROC)glbGetProcAddress("glMultiTexCoord3iARB"); + pAPI->glMultiTexCoord3ivARB = (PFNGLMULTITEXCOORD3IVARBPROC)glbGetProcAddress("glMultiTexCoord3ivARB"); + pAPI->glMultiTexCoord3sARB = (PFNGLMULTITEXCOORD3SARBPROC)glbGetProcAddress("glMultiTexCoord3sARB"); + pAPI->glMultiTexCoord3svARB = (PFNGLMULTITEXCOORD3SVARBPROC)glbGetProcAddress("glMultiTexCoord3svARB"); + pAPI->glMultiTexCoord4dARB = (PFNGLMULTITEXCOORD4DARBPROC)glbGetProcAddress("glMultiTexCoord4dARB"); + pAPI->glMultiTexCoord4dvARB = (PFNGLMULTITEXCOORD4DVARBPROC)glbGetProcAddress("glMultiTexCoord4dvARB"); + pAPI->glMultiTexCoord4fARB = (PFNGLMULTITEXCOORD4FARBPROC)glbGetProcAddress("glMultiTexCoord4fARB"); + pAPI->glMultiTexCoord4fvARB = (PFNGLMULTITEXCOORD4FVARBPROC)glbGetProcAddress("glMultiTexCoord4fvARB"); + pAPI->glMultiTexCoord4iARB = (PFNGLMULTITEXCOORD4IARBPROC)glbGetProcAddress("glMultiTexCoord4iARB"); + pAPI->glMultiTexCoord4ivARB = (PFNGLMULTITEXCOORD4IVARBPROC)glbGetProcAddress("glMultiTexCoord4ivARB"); + pAPI->glMultiTexCoord4sARB = (PFNGLMULTITEXCOORD4SARBPROC)glbGetProcAddress("glMultiTexCoord4sARB"); + pAPI->glMultiTexCoord4svARB = (PFNGLMULTITEXCOORD4SVARBPROC)glbGetProcAddress("glMultiTexCoord4svARB"); + pAPI->glGenQueriesARB = (PFNGLGENQUERIESARBPROC)glbGetProcAddress("glGenQueriesARB"); + pAPI->glDeleteQueriesARB = (PFNGLDELETEQUERIESARBPROC)glbGetProcAddress("glDeleteQueriesARB"); + pAPI->glIsQueryARB = (PFNGLISQUERYARBPROC)glbGetProcAddress("glIsQueryARB"); + pAPI->glBeginQueryARB = (PFNGLBEGINQUERYARBPROC)glbGetProcAddress("glBeginQueryARB"); + pAPI->glEndQueryARB = (PFNGLENDQUERYARBPROC)glbGetProcAddress("glEndQueryARB"); + pAPI->glGetQueryivARB = (PFNGLGETQUERYIVARBPROC)glbGetProcAddress("glGetQueryivARB"); + pAPI->glGetQueryObjectivARB = (PFNGLGETQUERYOBJECTIVARBPROC)glbGetProcAddress("glGetQueryObjectivARB"); + pAPI->glGetQueryObjectuivARB = (PFNGLGETQUERYOBJECTUIVARBPROC)glbGetProcAddress("glGetQueryObjectuivARB"); + pAPI->glMaxShaderCompilerThreadsARB = (PFNGLMAXSHADERCOMPILERTHREADSARBPROC)glbGetProcAddress("glMaxShaderCompilerThreadsARB"); + pAPI->glPointParameterfARB = (PFNGLPOINTPARAMETERFARBPROC)glbGetProcAddress("glPointParameterfARB"); + pAPI->glPointParameterfvARB = (PFNGLPOINTPARAMETERFVARBPROC)glbGetProcAddress("glPointParameterfvARB"); + pAPI->glGetGraphicsResetStatusARB = (PFNGLGETGRAPHICSRESETSTATUSARBPROC)glbGetProcAddress("glGetGraphicsResetStatusARB"); + pAPI->glGetnTexImageARB = (PFNGLGETNTEXIMAGEARBPROC)glbGetProcAddress("glGetnTexImageARB"); + pAPI->glReadnPixelsARB = (PFNGLREADNPIXELSARBPROC)glbGetProcAddress("glReadnPixelsARB"); + pAPI->glGetnCompressedTexImageARB = (PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC)glbGetProcAddress("glGetnCompressedTexImageARB"); + pAPI->glGetnUniformfvARB = (PFNGLGETNUNIFORMFVARBPROC)glbGetProcAddress("glGetnUniformfvARB"); + pAPI->glGetnUniformivARB = (PFNGLGETNUNIFORMIVARBPROC)glbGetProcAddress("glGetnUniformivARB"); + pAPI->glGetnUniformuivARB = (PFNGLGETNUNIFORMUIVARBPROC)glbGetProcAddress("glGetnUniformuivARB"); + pAPI->glGetnUniformdvARB = (PFNGLGETNUNIFORMDVARBPROC)glbGetProcAddress("glGetnUniformdvARB"); + pAPI->glGetnMapdvARB = (PFNGLGETNMAPDVARBPROC)glbGetProcAddress("glGetnMapdvARB"); + pAPI->glGetnMapfvARB = (PFNGLGETNMAPFVARBPROC)glbGetProcAddress("glGetnMapfvARB"); + pAPI->glGetnMapivARB = (PFNGLGETNMAPIVARBPROC)glbGetProcAddress("glGetnMapivARB"); + pAPI->glGetnPixelMapfvARB = (PFNGLGETNPIXELMAPFVARBPROC)glbGetProcAddress("glGetnPixelMapfvARB"); + pAPI->glGetnPixelMapuivARB = (PFNGLGETNPIXELMAPUIVARBPROC)glbGetProcAddress("glGetnPixelMapuivARB"); + pAPI->glGetnPixelMapusvARB = (PFNGLGETNPIXELMAPUSVARBPROC)glbGetProcAddress("glGetnPixelMapusvARB"); + pAPI->glGetnPolygonStippleARB = (PFNGLGETNPOLYGONSTIPPLEARBPROC)glbGetProcAddress("glGetnPolygonStippleARB"); + pAPI->glGetnColorTableARB = (PFNGLGETNCOLORTABLEARBPROC)glbGetProcAddress("glGetnColorTableARB"); + pAPI->glGetnConvolutionFilterARB = (PFNGLGETNCONVOLUTIONFILTERARBPROC)glbGetProcAddress("glGetnConvolutionFilterARB"); + pAPI->glGetnSeparableFilterARB = (PFNGLGETNSEPARABLEFILTERARBPROC)glbGetProcAddress("glGetnSeparableFilterARB"); + pAPI->glGetnHistogramARB = (PFNGLGETNHISTOGRAMARBPROC)glbGetProcAddress("glGetnHistogramARB"); + pAPI->glGetnMinmaxARB = (PFNGLGETNMINMAXARBPROC)glbGetProcAddress("glGetnMinmaxARB"); + pAPI->glFramebufferSampleLocationsfvARB = (PFNGLFRAMEBUFFERSAMPLELOCATIONSFVARBPROC)glbGetProcAddress("glFramebufferSampleLocationsfvARB"); + pAPI->glNamedFramebufferSampleLocationsfvARB = (PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVARBPROC)glbGetProcAddress("glNamedFramebufferSampleLocationsfvARB"); + pAPI->glEvaluateDepthValuesARB = (PFNGLEVALUATEDEPTHVALUESARBPROC)glbGetProcAddress("glEvaluateDepthValuesARB"); + pAPI->glMinSampleShadingARB = (PFNGLMINSAMPLESHADINGARBPROC)glbGetProcAddress("glMinSampleShadingARB"); + pAPI->glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC)glbGetProcAddress("glDeleteObjectARB"); + pAPI->glGetHandleARB = (PFNGLGETHANDLEARBPROC)glbGetProcAddress("glGetHandleARB"); + pAPI->glDetachObjectARB = (PFNGLDETACHOBJECTARBPROC)glbGetProcAddress("glDetachObjectARB"); + pAPI->glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC)glbGetProcAddress("glCreateShaderObjectARB"); + pAPI->glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC)glbGetProcAddress("glShaderSourceARB"); + pAPI->glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC)glbGetProcAddress("glCompileShaderARB"); + pAPI->glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC)glbGetProcAddress("glCreateProgramObjectARB"); + pAPI->glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC)glbGetProcAddress("glAttachObjectARB"); + pAPI->glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC)glbGetProcAddress("glLinkProgramARB"); + pAPI->glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC)glbGetProcAddress("glUseProgramObjectARB"); + pAPI->glValidateProgramARB = (PFNGLVALIDATEPROGRAMARBPROC)glbGetProcAddress("glValidateProgramARB"); + pAPI->glUniform1fARB = (PFNGLUNIFORM1FARBPROC)glbGetProcAddress("glUniform1fARB"); + pAPI->glUniform2fARB = (PFNGLUNIFORM2FARBPROC)glbGetProcAddress("glUniform2fARB"); + pAPI->glUniform3fARB = (PFNGLUNIFORM3FARBPROC)glbGetProcAddress("glUniform3fARB"); + pAPI->glUniform4fARB = (PFNGLUNIFORM4FARBPROC)glbGetProcAddress("glUniform4fARB"); + pAPI->glUniform1iARB = (PFNGLUNIFORM1IARBPROC)glbGetProcAddress("glUniform1iARB"); + pAPI->glUniform2iARB = (PFNGLUNIFORM2IARBPROC)glbGetProcAddress("glUniform2iARB"); + pAPI->glUniform3iARB = (PFNGLUNIFORM3IARBPROC)glbGetProcAddress("glUniform3iARB"); + pAPI->glUniform4iARB = (PFNGLUNIFORM4IARBPROC)glbGetProcAddress("glUniform4iARB"); + pAPI->glUniform1fvARB = (PFNGLUNIFORM1FVARBPROC)glbGetProcAddress("glUniform1fvARB"); + pAPI->glUniform2fvARB = (PFNGLUNIFORM2FVARBPROC)glbGetProcAddress("glUniform2fvARB"); + pAPI->glUniform3fvARB = (PFNGLUNIFORM3FVARBPROC)glbGetProcAddress("glUniform3fvARB"); + pAPI->glUniform4fvARB = (PFNGLUNIFORM4FVARBPROC)glbGetProcAddress("glUniform4fvARB"); + pAPI->glUniform1ivARB = (PFNGLUNIFORM1IVARBPROC)glbGetProcAddress("glUniform1ivARB"); + pAPI->glUniform2ivARB = (PFNGLUNIFORM2IVARBPROC)glbGetProcAddress("glUniform2ivARB"); + pAPI->glUniform3ivARB = (PFNGLUNIFORM3IVARBPROC)glbGetProcAddress("glUniform3ivARB"); + pAPI->glUniform4ivARB = (PFNGLUNIFORM4IVARBPROC)glbGetProcAddress("glUniform4ivARB"); + pAPI->glUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC)glbGetProcAddress("glUniformMatrix2fvARB"); + pAPI->glUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC)glbGetProcAddress("glUniformMatrix3fvARB"); + pAPI->glUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC)glbGetProcAddress("glUniformMatrix4fvARB"); + pAPI->glGetObjectParameterfvARB = (PFNGLGETOBJECTPARAMETERFVARBPROC)glbGetProcAddress("glGetObjectParameterfvARB"); + pAPI->glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC)glbGetProcAddress("glGetObjectParameterivARB"); + pAPI->glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC)glbGetProcAddress("glGetInfoLogARB"); + pAPI->glGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC)glbGetProcAddress("glGetAttachedObjectsARB"); + pAPI->glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC)glbGetProcAddress("glGetUniformLocationARB"); + pAPI->glGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC)glbGetProcAddress("glGetActiveUniformARB"); + pAPI->glGetUniformfvARB = (PFNGLGETUNIFORMFVARBPROC)glbGetProcAddress("glGetUniformfvARB"); + pAPI->glGetUniformivARB = (PFNGLGETUNIFORMIVARBPROC)glbGetProcAddress("glGetUniformivARB"); + pAPI->glGetShaderSourceARB = (PFNGLGETSHADERSOURCEARBPROC)glbGetProcAddress("glGetShaderSourceARB"); + pAPI->glNamedStringARB = (PFNGLNAMEDSTRINGARBPROC)glbGetProcAddress("glNamedStringARB"); + pAPI->glDeleteNamedStringARB = (PFNGLDELETENAMEDSTRINGARBPROC)glbGetProcAddress("glDeleteNamedStringARB"); + pAPI->glCompileShaderIncludeARB = (PFNGLCOMPILESHADERINCLUDEARBPROC)glbGetProcAddress("glCompileShaderIncludeARB"); + pAPI->glIsNamedStringARB = (PFNGLISNAMEDSTRINGARBPROC)glbGetProcAddress("glIsNamedStringARB"); + pAPI->glGetNamedStringARB = (PFNGLGETNAMEDSTRINGARBPROC)glbGetProcAddress("glGetNamedStringARB"); + pAPI->glGetNamedStringivARB = (PFNGLGETNAMEDSTRINGIVARBPROC)glbGetProcAddress("glGetNamedStringivARB"); + pAPI->glBufferPageCommitmentARB = (PFNGLBUFFERPAGECOMMITMENTARBPROC)glbGetProcAddress("glBufferPageCommitmentARB"); + pAPI->glNamedBufferPageCommitmentEXT = (PFNGLNAMEDBUFFERPAGECOMMITMENTEXTPROC)glbGetProcAddress("glNamedBufferPageCommitmentEXT"); + pAPI->glNamedBufferPageCommitmentARB = (PFNGLNAMEDBUFFERPAGECOMMITMENTARBPROC)glbGetProcAddress("glNamedBufferPageCommitmentARB"); + pAPI->glTexPageCommitmentARB = (PFNGLTEXPAGECOMMITMENTARBPROC)glbGetProcAddress("glTexPageCommitmentARB"); + pAPI->glTexBufferARB = (PFNGLTEXBUFFERARBPROC)glbGetProcAddress("glTexBufferARB"); + pAPI->glCompressedTexImage3DARB = (PFNGLCOMPRESSEDTEXIMAGE3DARBPROC)glbGetProcAddress("glCompressedTexImage3DARB"); + pAPI->glCompressedTexImage2DARB = (PFNGLCOMPRESSEDTEXIMAGE2DARBPROC)glbGetProcAddress("glCompressedTexImage2DARB"); + pAPI->glCompressedTexImage1DARB = (PFNGLCOMPRESSEDTEXIMAGE1DARBPROC)glbGetProcAddress("glCompressedTexImage1DARB"); + pAPI->glCompressedTexSubImage3DARB = (PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC)glbGetProcAddress("glCompressedTexSubImage3DARB"); + pAPI->glCompressedTexSubImage2DARB = (PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC)glbGetProcAddress("glCompressedTexSubImage2DARB"); + pAPI->glCompressedTexSubImage1DARB = (PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC)glbGetProcAddress("glCompressedTexSubImage1DARB"); + pAPI->glGetCompressedTexImageARB = (PFNGLGETCOMPRESSEDTEXIMAGEARBPROC)glbGetProcAddress("glGetCompressedTexImageARB"); + pAPI->glLoadTransposeMatrixfARB = (PFNGLLOADTRANSPOSEMATRIXFARBPROC)glbGetProcAddress("glLoadTransposeMatrixfARB"); + pAPI->glLoadTransposeMatrixdARB = (PFNGLLOADTRANSPOSEMATRIXDARBPROC)glbGetProcAddress("glLoadTransposeMatrixdARB"); + pAPI->glMultTransposeMatrixfARB = (PFNGLMULTTRANSPOSEMATRIXFARBPROC)glbGetProcAddress("glMultTransposeMatrixfARB"); + pAPI->glMultTransposeMatrixdARB = (PFNGLMULTTRANSPOSEMATRIXDARBPROC)glbGetProcAddress("glMultTransposeMatrixdARB"); + pAPI->glWeightbvARB = (PFNGLWEIGHTBVARBPROC)glbGetProcAddress("glWeightbvARB"); + pAPI->glWeightsvARB = (PFNGLWEIGHTSVARBPROC)glbGetProcAddress("glWeightsvARB"); + pAPI->glWeightivARB = (PFNGLWEIGHTIVARBPROC)glbGetProcAddress("glWeightivARB"); + pAPI->glWeightfvARB = (PFNGLWEIGHTFVARBPROC)glbGetProcAddress("glWeightfvARB"); + pAPI->glWeightdvARB = (PFNGLWEIGHTDVARBPROC)glbGetProcAddress("glWeightdvARB"); + pAPI->glWeightubvARB = (PFNGLWEIGHTUBVARBPROC)glbGetProcAddress("glWeightubvARB"); + pAPI->glWeightusvARB = (PFNGLWEIGHTUSVARBPROC)glbGetProcAddress("glWeightusvARB"); + pAPI->glWeightuivARB = (PFNGLWEIGHTUIVARBPROC)glbGetProcAddress("glWeightuivARB"); + pAPI->glWeightPointerARB = (PFNGLWEIGHTPOINTERARBPROC)glbGetProcAddress("glWeightPointerARB"); + pAPI->glVertexBlendARB = (PFNGLVERTEXBLENDARBPROC)glbGetProcAddress("glVertexBlendARB"); + pAPI->glBindBufferARB = (PFNGLBINDBUFFERARBPROC)glbGetProcAddress("glBindBufferARB"); + pAPI->glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)glbGetProcAddress("glDeleteBuffersARB"); + pAPI->glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)glbGetProcAddress("glGenBuffersARB"); + pAPI->glIsBufferARB = (PFNGLISBUFFERARBPROC)glbGetProcAddress("glIsBufferARB"); + pAPI->glBufferDataARB = (PFNGLBUFFERDATAARBPROC)glbGetProcAddress("glBufferDataARB"); + pAPI->glBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC)glbGetProcAddress("glBufferSubDataARB"); + pAPI->glGetBufferSubDataARB = (PFNGLGETBUFFERSUBDATAARBPROC)glbGetProcAddress("glGetBufferSubDataARB"); + pAPI->glMapBufferARB = (PFNGLMAPBUFFERARBPROC)glbGetProcAddress("glMapBufferARB"); + pAPI->glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)glbGetProcAddress("glUnmapBufferARB"); + pAPI->glGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC)glbGetProcAddress("glGetBufferParameterivARB"); + pAPI->glGetBufferPointervARB = (PFNGLGETBUFFERPOINTERVARBPROC)glbGetProcAddress("glGetBufferPointervARB"); + pAPI->glVertexAttrib1dARB = (PFNGLVERTEXATTRIB1DARBPROC)glbGetProcAddress("glVertexAttrib1dARB"); + pAPI->glVertexAttrib1dvARB = (PFNGLVERTEXATTRIB1DVARBPROC)glbGetProcAddress("glVertexAttrib1dvARB"); + pAPI->glVertexAttrib1fARB = (PFNGLVERTEXATTRIB1FARBPROC)glbGetProcAddress("glVertexAttrib1fARB"); + pAPI->glVertexAttrib1fvARB = (PFNGLVERTEXATTRIB1FVARBPROC)glbGetProcAddress("glVertexAttrib1fvARB"); + pAPI->glVertexAttrib1sARB = (PFNGLVERTEXATTRIB1SARBPROC)glbGetProcAddress("glVertexAttrib1sARB"); + pAPI->glVertexAttrib1svARB = (PFNGLVERTEXATTRIB1SVARBPROC)glbGetProcAddress("glVertexAttrib1svARB"); + pAPI->glVertexAttrib2dARB = (PFNGLVERTEXATTRIB2DARBPROC)glbGetProcAddress("glVertexAttrib2dARB"); + pAPI->glVertexAttrib2dvARB = (PFNGLVERTEXATTRIB2DVARBPROC)glbGetProcAddress("glVertexAttrib2dvARB"); + pAPI->glVertexAttrib2fARB = (PFNGLVERTEXATTRIB2FARBPROC)glbGetProcAddress("glVertexAttrib2fARB"); + pAPI->glVertexAttrib2fvARB = (PFNGLVERTEXATTRIB2FVARBPROC)glbGetProcAddress("glVertexAttrib2fvARB"); + pAPI->glVertexAttrib2sARB = (PFNGLVERTEXATTRIB2SARBPROC)glbGetProcAddress("glVertexAttrib2sARB"); + pAPI->glVertexAttrib2svARB = (PFNGLVERTEXATTRIB2SVARBPROC)glbGetProcAddress("glVertexAttrib2svARB"); + pAPI->glVertexAttrib3dARB = (PFNGLVERTEXATTRIB3DARBPROC)glbGetProcAddress("glVertexAttrib3dARB"); + pAPI->glVertexAttrib3dvARB = (PFNGLVERTEXATTRIB3DVARBPROC)glbGetProcAddress("glVertexAttrib3dvARB"); + pAPI->glVertexAttrib3fARB = (PFNGLVERTEXATTRIB3FARBPROC)glbGetProcAddress("glVertexAttrib3fARB"); + pAPI->glVertexAttrib3fvARB = (PFNGLVERTEXATTRIB3FVARBPROC)glbGetProcAddress("glVertexAttrib3fvARB"); + pAPI->glVertexAttrib3sARB = (PFNGLVERTEXATTRIB3SARBPROC)glbGetProcAddress("glVertexAttrib3sARB"); + pAPI->glVertexAttrib3svARB = (PFNGLVERTEXATTRIB3SVARBPROC)glbGetProcAddress("glVertexAttrib3svARB"); + pAPI->glVertexAttrib4NbvARB = (PFNGLVERTEXATTRIB4NBVARBPROC)glbGetProcAddress("glVertexAttrib4NbvARB"); + pAPI->glVertexAttrib4NivARB = (PFNGLVERTEXATTRIB4NIVARBPROC)glbGetProcAddress("glVertexAttrib4NivARB"); + pAPI->glVertexAttrib4NsvARB = (PFNGLVERTEXATTRIB4NSVARBPROC)glbGetProcAddress("glVertexAttrib4NsvARB"); + pAPI->glVertexAttrib4NubARB = (PFNGLVERTEXATTRIB4NUBARBPROC)glbGetProcAddress("glVertexAttrib4NubARB"); + pAPI->glVertexAttrib4NubvARB = (PFNGLVERTEXATTRIB4NUBVARBPROC)glbGetProcAddress("glVertexAttrib4NubvARB"); + pAPI->glVertexAttrib4NuivARB = (PFNGLVERTEXATTRIB4NUIVARBPROC)glbGetProcAddress("glVertexAttrib4NuivARB"); + pAPI->glVertexAttrib4NusvARB = (PFNGLVERTEXATTRIB4NUSVARBPROC)glbGetProcAddress("glVertexAttrib4NusvARB"); + pAPI->glVertexAttrib4bvARB = (PFNGLVERTEXATTRIB4BVARBPROC)glbGetProcAddress("glVertexAttrib4bvARB"); + pAPI->glVertexAttrib4dARB = (PFNGLVERTEXATTRIB4DARBPROC)glbGetProcAddress("glVertexAttrib4dARB"); + pAPI->glVertexAttrib4dvARB = (PFNGLVERTEXATTRIB4DVARBPROC)glbGetProcAddress("glVertexAttrib4dvARB"); + pAPI->glVertexAttrib4fARB = (PFNGLVERTEXATTRIB4FARBPROC)glbGetProcAddress("glVertexAttrib4fARB"); + pAPI->glVertexAttrib4fvARB = (PFNGLVERTEXATTRIB4FVARBPROC)glbGetProcAddress("glVertexAttrib4fvARB"); + pAPI->glVertexAttrib4ivARB = (PFNGLVERTEXATTRIB4IVARBPROC)glbGetProcAddress("glVertexAttrib4ivARB"); + pAPI->glVertexAttrib4sARB = (PFNGLVERTEXATTRIB4SARBPROC)glbGetProcAddress("glVertexAttrib4sARB"); + pAPI->glVertexAttrib4svARB = (PFNGLVERTEXATTRIB4SVARBPROC)glbGetProcAddress("glVertexAttrib4svARB"); + pAPI->glVertexAttrib4ubvARB = (PFNGLVERTEXATTRIB4UBVARBPROC)glbGetProcAddress("glVertexAttrib4ubvARB"); + pAPI->glVertexAttrib4uivARB = (PFNGLVERTEXATTRIB4UIVARBPROC)glbGetProcAddress("glVertexAttrib4uivARB"); + pAPI->glVertexAttrib4usvARB = (PFNGLVERTEXATTRIB4USVARBPROC)glbGetProcAddress("glVertexAttrib4usvARB"); + pAPI->glVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC)glbGetProcAddress("glVertexAttribPointerARB"); + pAPI->glEnableVertexAttribArrayARB = (PFNGLENABLEVERTEXATTRIBARRAYARBPROC)glbGetProcAddress("glEnableVertexAttribArrayARB"); + pAPI->glDisableVertexAttribArrayARB = (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC)glbGetProcAddress("glDisableVertexAttribArrayARB"); + pAPI->glGetVertexAttribdvARB = (PFNGLGETVERTEXATTRIBDVARBPROC)glbGetProcAddress("glGetVertexAttribdvARB"); + pAPI->glGetVertexAttribfvARB = (PFNGLGETVERTEXATTRIBFVARBPROC)glbGetProcAddress("glGetVertexAttribfvARB"); + pAPI->glGetVertexAttribivARB = (PFNGLGETVERTEXATTRIBIVARBPROC)glbGetProcAddress("glGetVertexAttribivARB"); + pAPI->glGetVertexAttribPointervARB = (PFNGLGETVERTEXATTRIBPOINTERVARBPROC)glbGetProcAddress("glGetVertexAttribPointervARB"); + pAPI->glBindAttribLocationARB = (PFNGLBINDATTRIBLOCATIONARBPROC)glbGetProcAddress("glBindAttribLocationARB"); + pAPI->glGetActiveAttribARB = (PFNGLGETACTIVEATTRIBARBPROC)glbGetProcAddress("glGetActiveAttribARB"); + pAPI->glGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC)glbGetProcAddress("glGetAttribLocationARB"); + pAPI->glDepthRangeArraydvNV = (PFNGLDEPTHRANGEARRAYDVNVPROC)glbGetProcAddress("glDepthRangeArraydvNV"); + pAPI->glDepthRangeIndexeddNV = (PFNGLDEPTHRANGEINDEXEDDNVPROC)glbGetProcAddress("glDepthRangeIndexeddNV"); + pAPI->glWindowPos2dARB = (PFNGLWINDOWPOS2DARBPROC)glbGetProcAddress("glWindowPos2dARB"); + pAPI->glWindowPos2dvARB = (PFNGLWINDOWPOS2DVARBPROC)glbGetProcAddress("glWindowPos2dvARB"); + pAPI->glWindowPos2fARB = (PFNGLWINDOWPOS2FARBPROC)glbGetProcAddress("glWindowPos2fARB"); + pAPI->glWindowPos2fvARB = (PFNGLWINDOWPOS2FVARBPROC)glbGetProcAddress("glWindowPos2fvARB"); + pAPI->glWindowPos2iARB = (PFNGLWINDOWPOS2IARBPROC)glbGetProcAddress("glWindowPos2iARB"); + pAPI->glWindowPos2ivARB = (PFNGLWINDOWPOS2IVARBPROC)glbGetProcAddress("glWindowPos2ivARB"); + pAPI->glWindowPos2sARB = (PFNGLWINDOWPOS2SARBPROC)glbGetProcAddress("glWindowPos2sARB"); + pAPI->glWindowPos2svARB = (PFNGLWINDOWPOS2SVARBPROC)glbGetProcAddress("glWindowPos2svARB"); + pAPI->glWindowPos3dARB = (PFNGLWINDOWPOS3DARBPROC)glbGetProcAddress("glWindowPos3dARB"); + pAPI->glWindowPos3dvARB = (PFNGLWINDOWPOS3DVARBPROC)glbGetProcAddress("glWindowPos3dvARB"); + pAPI->glWindowPos3fARB = (PFNGLWINDOWPOS3FARBPROC)glbGetProcAddress("glWindowPos3fARB"); + pAPI->glWindowPos3fvARB = (PFNGLWINDOWPOS3FVARBPROC)glbGetProcAddress("glWindowPos3fvARB"); + pAPI->glWindowPos3iARB = (PFNGLWINDOWPOS3IARBPROC)glbGetProcAddress("glWindowPos3iARB"); + pAPI->glWindowPos3ivARB = (PFNGLWINDOWPOS3IVARBPROC)glbGetProcAddress("glWindowPos3ivARB"); + pAPI->glWindowPos3sARB = (PFNGLWINDOWPOS3SARBPROC)glbGetProcAddress("glWindowPos3sARB"); + pAPI->glWindowPos3svARB = (PFNGLWINDOWPOS3SVARBPROC)glbGetProcAddress("glWindowPos3svARB"); + pAPI->glDrawBuffersATI = (PFNGLDRAWBUFFERSATIPROC)glbGetProcAddress("glDrawBuffersATI"); + pAPI->glElementPointerATI = (PFNGLELEMENTPOINTERATIPROC)glbGetProcAddress("glElementPointerATI"); + pAPI->glDrawElementArrayATI = (PFNGLDRAWELEMENTARRAYATIPROC)glbGetProcAddress("glDrawElementArrayATI"); + pAPI->glDrawRangeElementArrayATI = (PFNGLDRAWRANGEELEMENTARRAYATIPROC)glbGetProcAddress("glDrawRangeElementArrayATI"); + pAPI->glTexBumpParameterivATI = (PFNGLTEXBUMPPARAMETERIVATIPROC)glbGetProcAddress("glTexBumpParameterivATI"); + pAPI->glTexBumpParameterfvATI = (PFNGLTEXBUMPPARAMETERFVATIPROC)glbGetProcAddress("glTexBumpParameterfvATI"); + pAPI->glGetTexBumpParameterivATI = (PFNGLGETTEXBUMPPARAMETERIVATIPROC)glbGetProcAddress("glGetTexBumpParameterivATI"); + pAPI->glGetTexBumpParameterfvATI = (PFNGLGETTEXBUMPPARAMETERFVATIPROC)glbGetProcAddress("glGetTexBumpParameterfvATI"); + pAPI->glGenFragmentShadersATI = (PFNGLGENFRAGMENTSHADERSATIPROC)glbGetProcAddress("glGenFragmentShadersATI"); + pAPI->glBindFragmentShaderATI = (PFNGLBINDFRAGMENTSHADERATIPROC)glbGetProcAddress("glBindFragmentShaderATI"); + pAPI->glDeleteFragmentShaderATI = (PFNGLDELETEFRAGMENTSHADERATIPROC)glbGetProcAddress("glDeleteFragmentShaderATI"); + pAPI->glBeginFragmentShaderATI = (PFNGLBEGINFRAGMENTSHADERATIPROC)glbGetProcAddress("glBeginFragmentShaderATI"); + pAPI->glEndFragmentShaderATI = (PFNGLENDFRAGMENTSHADERATIPROC)glbGetProcAddress("glEndFragmentShaderATI"); + pAPI->glPassTexCoordATI = (PFNGLPASSTEXCOORDATIPROC)glbGetProcAddress("glPassTexCoordATI"); + pAPI->glSampleMapATI = (PFNGLSAMPLEMAPATIPROC)glbGetProcAddress("glSampleMapATI"); + pAPI->glColorFragmentOp1ATI = (PFNGLCOLORFRAGMENTOP1ATIPROC)glbGetProcAddress("glColorFragmentOp1ATI"); + pAPI->glColorFragmentOp2ATI = (PFNGLCOLORFRAGMENTOP2ATIPROC)glbGetProcAddress("glColorFragmentOp2ATI"); + pAPI->glColorFragmentOp3ATI = (PFNGLCOLORFRAGMENTOP3ATIPROC)glbGetProcAddress("glColorFragmentOp3ATI"); + pAPI->glAlphaFragmentOp1ATI = (PFNGLALPHAFRAGMENTOP1ATIPROC)glbGetProcAddress("glAlphaFragmentOp1ATI"); + pAPI->glAlphaFragmentOp2ATI = (PFNGLALPHAFRAGMENTOP2ATIPROC)glbGetProcAddress("glAlphaFragmentOp2ATI"); + pAPI->glAlphaFragmentOp3ATI = (PFNGLALPHAFRAGMENTOP3ATIPROC)glbGetProcAddress("glAlphaFragmentOp3ATI"); + pAPI->glSetFragmentShaderConstantATI = (PFNGLSETFRAGMENTSHADERCONSTANTATIPROC)glbGetProcAddress("glSetFragmentShaderConstantATI"); + pAPI->glMapObjectBufferATI = (PFNGLMAPOBJECTBUFFERATIPROC)glbGetProcAddress("glMapObjectBufferATI"); + pAPI->glUnmapObjectBufferATI = (PFNGLUNMAPOBJECTBUFFERATIPROC)glbGetProcAddress("glUnmapObjectBufferATI"); + pAPI->glPNTrianglesiATI = (PFNGLPNTRIANGLESIATIPROC)glbGetProcAddress("glPNTrianglesiATI"); + pAPI->glPNTrianglesfATI = (PFNGLPNTRIANGLESFATIPROC)glbGetProcAddress("glPNTrianglesfATI"); + pAPI->glStencilOpSeparateATI = (PFNGLSTENCILOPSEPARATEATIPROC)glbGetProcAddress("glStencilOpSeparateATI"); + pAPI->glStencilFuncSeparateATI = (PFNGLSTENCILFUNCSEPARATEATIPROC)glbGetProcAddress("glStencilFuncSeparateATI"); + pAPI->glNewObjectBufferATI = (PFNGLNEWOBJECTBUFFERATIPROC)glbGetProcAddress("glNewObjectBufferATI"); + pAPI->glIsObjectBufferATI = (PFNGLISOBJECTBUFFERATIPROC)glbGetProcAddress("glIsObjectBufferATI"); + pAPI->glUpdateObjectBufferATI = (PFNGLUPDATEOBJECTBUFFERATIPROC)glbGetProcAddress("glUpdateObjectBufferATI"); + pAPI->glGetObjectBufferfvATI = (PFNGLGETOBJECTBUFFERFVATIPROC)glbGetProcAddress("glGetObjectBufferfvATI"); + pAPI->glGetObjectBufferivATI = (PFNGLGETOBJECTBUFFERIVATIPROC)glbGetProcAddress("glGetObjectBufferivATI"); + pAPI->glFreeObjectBufferATI = (PFNGLFREEOBJECTBUFFERATIPROC)glbGetProcAddress("glFreeObjectBufferATI"); + pAPI->glArrayObjectATI = (PFNGLARRAYOBJECTATIPROC)glbGetProcAddress("glArrayObjectATI"); + pAPI->glGetArrayObjectfvATI = (PFNGLGETARRAYOBJECTFVATIPROC)glbGetProcAddress("glGetArrayObjectfvATI"); + pAPI->glGetArrayObjectivATI = (PFNGLGETARRAYOBJECTIVATIPROC)glbGetProcAddress("glGetArrayObjectivATI"); + pAPI->glVariantArrayObjectATI = (PFNGLVARIANTARRAYOBJECTATIPROC)glbGetProcAddress("glVariantArrayObjectATI"); + pAPI->glGetVariantArrayObjectfvATI = (PFNGLGETVARIANTARRAYOBJECTFVATIPROC)glbGetProcAddress("glGetVariantArrayObjectfvATI"); + pAPI->glGetVariantArrayObjectivATI = (PFNGLGETVARIANTARRAYOBJECTIVATIPROC)glbGetProcAddress("glGetVariantArrayObjectivATI"); + pAPI->glVertexAttribArrayObjectATI = (PFNGLVERTEXATTRIBARRAYOBJECTATIPROC)glbGetProcAddress("glVertexAttribArrayObjectATI"); + pAPI->glGetVertexAttribArrayObjectfvATI = (PFNGLGETVERTEXATTRIBARRAYOBJECTFVATIPROC)glbGetProcAddress("glGetVertexAttribArrayObjectfvATI"); + pAPI->glGetVertexAttribArrayObjectivATI = (PFNGLGETVERTEXATTRIBARRAYOBJECTIVATIPROC)glbGetProcAddress("glGetVertexAttribArrayObjectivATI"); + pAPI->glVertexStream1sATI = (PFNGLVERTEXSTREAM1SATIPROC)glbGetProcAddress("glVertexStream1sATI"); + pAPI->glVertexStream1svATI = (PFNGLVERTEXSTREAM1SVATIPROC)glbGetProcAddress("glVertexStream1svATI"); + pAPI->glVertexStream1iATI = (PFNGLVERTEXSTREAM1IATIPROC)glbGetProcAddress("glVertexStream1iATI"); + pAPI->glVertexStream1ivATI = (PFNGLVERTEXSTREAM1IVATIPROC)glbGetProcAddress("glVertexStream1ivATI"); + pAPI->glVertexStream1fATI = (PFNGLVERTEXSTREAM1FATIPROC)glbGetProcAddress("glVertexStream1fATI"); + pAPI->glVertexStream1fvATI = (PFNGLVERTEXSTREAM1FVATIPROC)glbGetProcAddress("glVertexStream1fvATI"); + pAPI->glVertexStream1dATI = (PFNGLVERTEXSTREAM1DATIPROC)glbGetProcAddress("glVertexStream1dATI"); + pAPI->glVertexStream1dvATI = (PFNGLVERTEXSTREAM1DVATIPROC)glbGetProcAddress("glVertexStream1dvATI"); + pAPI->glVertexStream2sATI = (PFNGLVERTEXSTREAM2SATIPROC)glbGetProcAddress("glVertexStream2sATI"); + pAPI->glVertexStream2svATI = (PFNGLVERTEXSTREAM2SVATIPROC)glbGetProcAddress("glVertexStream2svATI"); + pAPI->glVertexStream2iATI = (PFNGLVERTEXSTREAM2IATIPROC)glbGetProcAddress("glVertexStream2iATI"); + pAPI->glVertexStream2ivATI = (PFNGLVERTEXSTREAM2IVATIPROC)glbGetProcAddress("glVertexStream2ivATI"); + pAPI->glVertexStream2fATI = (PFNGLVERTEXSTREAM2FATIPROC)glbGetProcAddress("glVertexStream2fATI"); + pAPI->glVertexStream2fvATI = (PFNGLVERTEXSTREAM2FVATIPROC)glbGetProcAddress("glVertexStream2fvATI"); + pAPI->glVertexStream2dATI = (PFNGLVERTEXSTREAM2DATIPROC)glbGetProcAddress("glVertexStream2dATI"); + pAPI->glVertexStream2dvATI = (PFNGLVERTEXSTREAM2DVATIPROC)glbGetProcAddress("glVertexStream2dvATI"); + pAPI->glVertexStream3sATI = (PFNGLVERTEXSTREAM3SATIPROC)glbGetProcAddress("glVertexStream3sATI"); + pAPI->glVertexStream3svATI = (PFNGLVERTEXSTREAM3SVATIPROC)glbGetProcAddress("glVertexStream3svATI"); + pAPI->glVertexStream3iATI = (PFNGLVERTEXSTREAM3IATIPROC)glbGetProcAddress("glVertexStream3iATI"); + pAPI->glVertexStream3ivATI = (PFNGLVERTEXSTREAM3IVATIPROC)glbGetProcAddress("glVertexStream3ivATI"); + pAPI->glVertexStream3fATI = (PFNGLVERTEXSTREAM3FATIPROC)glbGetProcAddress("glVertexStream3fATI"); + pAPI->glVertexStream3fvATI = (PFNGLVERTEXSTREAM3FVATIPROC)glbGetProcAddress("glVertexStream3fvATI"); + pAPI->glVertexStream3dATI = (PFNGLVERTEXSTREAM3DATIPROC)glbGetProcAddress("glVertexStream3dATI"); + pAPI->glVertexStream3dvATI = (PFNGLVERTEXSTREAM3DVATIPROC)glbGetProcAddress("glVertexStream3dvATI"); + pAPI->glVertexStream4sATI = (PFNGLVERTEXSTREAM4SATIPROC)glbGetProcAddress("glVertexStream4sATI"); + pAPI->glVertexStream4svATI = (PFNGLVERTEXSTREAM4SVATIPROC)glbGetProcAddress("glVertexStream4svATI"); + pAPI->glVertexStream4iATI = (PFNGLVERTEXSTREAM4IATIPROC)glbGetProcAddress("glVertexStream4iATI"); + pAPI->glVertexStream4ivATI = (PFNGLVERTEXSTREAM4IVATIPROC)glbGetProcAddress("glVertexStream4ivATI"); + pAPI->glVertexStream4fATI = (PFNGLVERTEXSTREAM4FATIPROC)glbGetProcAddress("glVertexStream4fATI"); + pAPI->glVertexStream4fvATI = (PFNGLVERTEXSTREAM4FVATIPROC)glbGetProcAddress("glVertexStream4fvATI"); + pAPI->glVertexStream4dATI = (PFNGLVERTEXSTREAM4DATIPROC)glbGetProcAddress("glVertexStream4dATI"); + pAPI->glVertexStream4dvATI = (PFNGLVERTEXSTREAM4DVATIPROC)glbGetProcAddress("glVertexStream4dvATI"); + pAPI->glNormalStream3bATI = (PFNGLNORMALSTREAM3BATIPROC)glbGetProcAddress("glNormalStream3bATI"); + pAPI->glNormalStream3bvATI = (PFNGLNORMALSTREAM3BVATIPROC)glbGetProcAddress("glNormalStream3bvATI"); + pAPI->glNormalStream3sATI = (PFNGLNORMALSTREAM3SATIPROC)glbGetProcAddress("glNormalStream3sATI"); + pAPI->glNormalStream3svATI = (PFNGLNORMALSTREAM3SVATIPROC)glbGetProcAddress("glNormalStream3svATI"); + pAPI->glNormalStream3iATI = (PFNGLNORMALSTREAM3IATIPROC)glbGetProcAddress("glNormalStream3iATI"); + pAPI->glNormalStream3ivATI = (PFNGLNORMALSTREAM3IVATIPROC)glbGetProcAddress("glNormalStream3ivATI"); + pAPI->glNormalStream3fATI = (PFNGLNORMALSTREAM3FATIPROC)glbGetProcAddress("glNormalStream3fATI"); + pAPI->glNormalStream3fvATI = (PFNGLNORMALSTREAM3FVATIPROC)glbGetProcAddress("glNormalStream3fvATI"); + pAPI->glNormalStream3dATI = (PFNGLNORMALSTREAM3DATIPROC)glbGetProcAddress("glNormalStream3dATI"); + pAPI->glNormalStream3dvATI = (PFNGLNORMALSTREAM3DVATIPROC)glbGetProcAddress("glNormalStream3dvATI"); + pAPI->glClientActiveVertexStreamATI = (PFNGLCLIENTACTIVEVERTEXSTREAMATIPROC)glbGetProcAddress("glClientActiveVertexStreamATI"); + pAPI->glVertexBlendEnviATI = (PFNGLVERTEXBLENDENVIATIPROC)glbGetProcAddress("glVertexBlendEnviATI"); + pAPI->glVertexBlendEnvfATI = (PFNGLVERTEXBLENDENVFATIPROC)glbGetProcAddress("glVertexBlendEnvfATI"); + pAPI->glEGLImageTargetTexStorageEXT = (PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC)glbGetProcAddress("glEGLImageTargetTexStorageEXT"); + pAPI->glEGLImageTargetTextureStorageEXT = (PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC)glbGetProcAddress("glEGLImageTargetTextureStorageEXT"); + pAPI->glUniformBufferEXT = (PFNGLUNIFORMBUFFEREXTPROC)glbGetProcAddress("glUniformBufferEXT"); + pAPI->glGetUniformBufferSizeEXT = (PFNGLGETUNIFORMBUFFERSIZEEXTPROC)glbGetProcAddress("glGetUniformBufferSizeEXT"); + pAPI->glGetUniformOffsetEXT = (PFNGLGETUNIFORMOFFSETEXTPROC)glbGetProcAddress("glGetUniformOffsetEXT"); + pAPI->glBlendColorEXT = (PFNGLBLENDCOLOREXTPROC)glbGetProcAddress("glBlendColorEXT"); + pAPI->glBlendEquationSeparateEXT = (PFNGLBLENDEQUATIONSEPARATEEXTPROC)glbGetProcAddress("glBlendEquationSeparateEXT"); + pAPI->glBlendFuncSeparateEXT = (PFNGLBLENDFUNCSEPARATEEXTPROC)glbGetProcAddress("glBlendFuncSeparateEXT"); + pAPI->glBlendEquationEXT = (PFNGLBLENDEQUATIONEXTPROC)glbGetProcAddress("glBlendEquationEXT"); + pAPI->glColorSubTableEXT = (PFNGLCOLORSUBTABLEEXTPROC)glbGetProcAddress("glColorSubTableEXT"); + pAPI->glCopyColorSubTableEXT = (PFNGLCOPYCOLORSUBTABLEEXTPROC)glbGetProcAddress("glCopyColorSubTableEXT"); + pAPI->glLockArraysEXT = (PFNGLLOCKARRAYSEXTPROC)glbGetProcAddress("glLockArraysEXT"); + pAPI->glUnlockArraysEXT = (PFNGLUNLOCKARRAYSEXTPROC)glbGetProcAddress("glUnlockArraysEXT"); + pAPI->glConvolutionFilter1DEXT = (PFNGLCONVOLUTIONFILTER1DEXTPROC)glbGetProcAddress("glConvolutionFilter1DEXT"); + pAPI->glConvolutionFilter2DEXT = (PFNGLCONVOLUTIONFILTER2DEXTPROC)glbGetProcAddress("glConvolutionFilter2DEXT"); + pAPI->glConvolutionParameterfEXT = (PFNGLCONVOLUTIONPARAMETERFEXTPROC)glbGetProcAddress("glConvolutionParameterfEXT"); + pAPI->glConvolutionParameterfvEXT = (PFNGLCONVOLUTIONPARAMETERFVEXTPROC)glbGetProcAddress("glConvolutionParameterfvEXT"); + pAPI->glConvolutionParameteriEXT = (PFNGLCONVOLUTIONPARAMETERIEXTPROC)glbGetProcAddress("glConvolutionParameteriEXT"); + pAPI->glConvolutionParameterivEXT = (PFNGLCONVOLUTIONPARAMETERIVEXTPROC)glbGetProcAddress("glConvolutionParameterivEXT"); + pAPI->glCopyConvolutionFilter1DEXT = (PFNGLCOPYCONVOLUTIONFILTER1DEXTPROC)glbGetProcAddress("glCopyConvolutionFilter1DEXT"); + pAPI->glCopyConvolutionFilter2DEXT = (PFNGLCOPYCONVOLUTIONFILTER2DEXTPROC)glbGetProcAddress("glCopyConvolutionFilter2DEXT"); + pAPI->glGetConvolutionFilterEXT = (PFNGLGETCONVOLUTIONFILTEREXTPROC)glbGetProcAddress("glGetConvolutionFilterEXT"); + pAPI->glGetConvolutionParameterfvEXT = (PFNGLGETCONVOLUTIONPARAMETERFVEXTPROC)glbGetProcAddress("glGetConvolutionParameterfvEXT"); + pAPI->glGetConvolutionParameterivEXT = (PFNGLGETCONVOLUTIONPARAMETERIVEXTPROC)glbGetProcAddress("glGetConvolutionParameterivEXT"); + pAPI->glGetSeparableFilterEXT = (PFNGLGETSEPARABLEFILTEREXTPROC)glbGetProcAddress("glGetSeparableFilterEXT"); + pAPI->glSeparableFilter2DEXT = (PFNGLSEPARABLEFILTER2DEXTPROC)glbGetProcAddress("glSeparableFilter2DEXT"); + pAPI->glTangent3bEXT = (PFNGLTANGENT3BEXTPROC)glbGetProcAddress("glTangent3bEXT"); + pAPI->glTangent3bvEXT = (PFNGLTANGENT3BVEXTPROC)glbGetProcAddress("glTangent3bvEXT"); + pAPI->glTangent3dEXT = (PFNGLTANGENT3DEXTPROC)glbGetProcAddress("glTangent3dEXT"); + pAPI->glTangent3dvEXT = (PFNGLTANGENT3DVEXTPROC)glbGetProcAddress("glTangent3dvEXT"); + pAPI->glTangent3fEXT = (PFNGLTANGENT3FEXTPROC)glbGetProcAddress("glTangent3fEXT"); + pAPI->glTangent3fvEXT = (PFNGLTANGENT3FVEXTPROC)glbGetProcAddress("glTangent3fvEXT"); + pAPI->glTangent3iEXT = (PFNGLTANGENT3IEXTPROC)glbGetProcAddress("glTangent3iEXT"); + pAPI->glTangent3ivEXT = (PFNGLTANGENT3IVEXTPROC)glbGetProcAddress("glTangent3ivEXT"); + pAPI->glTangent3sEXT = (PFNGLTANGENT3SEXTPROC)glbGetProcAddress("glTangent3sEXT"); + pAPI->glTangent3svEXT = (PFNGLTANGENT3SVEXTPROC)glbGetProcAddress("glTangent3svEXT"); + pAPI->glBinormal3bEXT = (PFNGLBINORMAL3BEXTPROC)glbGetProcAddress("glBinormal3bEXT"); + pAPI->glBinormal3bvEXT = (PFNGLBINORMAL3BVEXTPROC)glbGetProcAddress("glBinormal3bvEXT"); + pAPI->glBinormal3dEXT = (PFNGLBINORMAL3DEXTPROC)glbGetProcAddress("glBinormal3dEXT"); + pAPI->glBinormal3dvEXT = (PFNGLBINORMAL3DVEXTPROC)glbGetProcAddress("glBinormal3dvEXT"); + pAPI->glBinormal3fEXT = (PFNGLBINORMAL3FEXTPROC)glbGetProcAddress("glBinormal3fEXT"); + pAPI->glBinormal3fvEXT = (PFNGLBINORMAL3FVEXTPROC)glbGetProcAddress("glBinormal3fvEXT"); + pAPI->glBinormal3iEXT = (PFNGLBINORMAL3IEXTPROC)glbGetProcAddress("glBinormal3iEXT"); + pAPI->glBinormal3ivEXT = (PFNGLBINORMAL3IVEXTPROC)glbGetProcAddress("glBinormal3ivEXT"); + pAPI->glBinormal3sEXT = (PFNGLBINORMAL3SEXTPROC)glbGetProcAddress("glBinormal3sEXT"); + pAPI->glBinormal3svEXT = (PFNGLBINORMAL3SVEXTPROC)glbGetProcAddress("glBinormal3svEXT"); + pAPI->glTangentPointerEXT = (PFNGLTANGENTPOINTEREXTPROC)glbGetProcAddress("glTangentPointerEXT"); + pAPI->glBinormalPointerEXT = (PFNGLBINORMALPOINTEREXTPROC)glbGetProcAddress("glBinormalPointerEXT"); + pAPI->glCopyTexImage1DEXT = (PFNGLCOPYTEXIMAGE1DEXTPROC)glbGetProcAddress("glCopyTexImage1DEXT"); + pAPI->glCopyTexImage2DEXT = (PFNGLCOPYTEXIMAGE2DEXTPROC)glbGetProcAddress("glCopyTexImage2DEXT"); + pAPI->glCopyTexSubImage1DEXT = (PFNGLCOPYTEXSUBIMAGE1DEXTPROC)glbGetProcAddress("glCopyTexSubImage1DEXT"); + pAPI->glCopyTexSubImage2DEXT = (PFNGLCOPYTEXSUBIMAGE2DEXTPROC)glbGetProcAddress("glCopyTexSubImage2DEXT"); + pAPI->glCopyTexSubImage3DEXT = (PFNGLCOPYTEXSUBIMAGE3DEXTPROC)glbGetProcAddress("glCopyTexSubImage3DEXT"); + pAPI->glCullParameterdvEXT = (PFNGLCULLPARAMETERDVEXTPROC)glbGetProcAddress("glCullParameterdvEXT"); + pAPI->glCullParameterfvEXT = (PFNGLCULLPARAMETERFVEXTPROC)glbGetProcAddress("glCullParameterfvEXT"); + pAPI->glLabelObjectEXT = (PFNGLLABELOBJECTEXTPROC)glbGetProcAddress("glLabelObjectEXT"); + pAPI->glGetObjectLabelEXT = (PFNGLGETOBJECTLABELEXTPROC)glbGetProcAddress("glGetObjectLabelEXT"); + pAPI->glInsertEventMarkerEXT = (PFNGLINSERTEVENTMARKEREXTPROC)glbGetProcAddress("glInsertEventMarkerEXT"); + pAPI->glPushGroupMarkerEXT = (PFNGLPUSHGROUPMARKEREXTPROC)glbGetProcAddress("glPushGroupMarkerEXT"); + pAPI->glPopGroupMarkerEXT = (PFNGLPOPGROUPMARKEREXTPROC)glbGetProcAddress("glPopGroupMarkerEXT"); + pAPI->glDepthBoundsEXT = (PFNGLDEPTHBOUNDSEXTPROC)glbGetProcAddress("glDepthBoundsEXT"); + pAPI->glMatrixLoadfEXT = (PFNGLMATRIXLOADFEXTPROC)glbGetProcAddress("glMatrixLoadfEXT"); + pAPI->glMatrixLoaddEXT = (PFNGLMATRIXLOADDEXTPROC)glbGetProcAddress("glMatrixLoaddEXT"); + pAPI->glMatrixMultfEXT = (PFNGLMATRIXMULTFEXTPROC)glbGetProcAddress("glMatrixMultfEXT"); + pAPI->glMatrixMultdEXT = (PFNGLMATRIXMULTDEXTPROC)glbGetProcAddress("glMatrixMultdEXT"); + pAPI->glMatrixLoadIdentityEXT = (PFNGLMATRIXLOADIDENTITYEXTPROC)glbGetProcAddress("glMatrixLoadIdentityEXT"); + pAPI->glMatrixRotatefEXT = (PFNGLMATRIXROTATEFEXTPROC)glbGetProcAddress("glMatrixRotatefEXT"); + pAPI->glMatrixRotatedEXT = (PFNGLMATRIXROTATEDEXTPROC)glbGetProcAddress("glMatrixRotatedEXT"); + pAPI->glMatrixScalefEXT = (PFNGLMATRIXSCALEFEXTPROC)glbGetProcAddress("glMatrixScalefEXT"); + pAPI->glMatrixScaledEXT = (PFNGLMATRIXSCALEDEXTPROC)glbGetProcAddress("glMatrixScaledEXT"); + pAPI->glMatrixTranslatefEXT = (PFNGLMATRIXTRANSLATEFEXTPROC)glbGetProcAddress("glMatrixTranslatefEXT"); + pAPI->glMatrixTranslatedEXT = (PFNGLMATRIXTRANSLATEDEXTPROC)glbGetProcAddress("glMatrixTranslatedEXT"); + pAPI->glMatrixFrustumEXT = (PFNGLMATRIXFRUSTUMEXTPROC)glbGetProcAddress("glMatrixFrustumEXT"); + pAPI->glMatrixOrthoEXT = (PFNGLMATRIXORTHOEXTPROC)glbGetProcAddress("glMatrixOrthoEXT"); + pAPI->glMatrixPopEXT = (PFNGLMATRIXPOPEXTPROC)glbGetProcAddress("glMatrixPopEXT"); + pAPI->glMatrixPushEXT = (PFNGLMATRIXPUSHEXTPROC)glbGetProcAddress("glMatrixPushEXT"); + pAPI->glClientAttribDefaultEXT = (PFNGLCLIENTATTRIBDEFAULTEXTPROC)glbGetProcAddress("glClientAttribDefaultEXT"); + pAPI->glPushClientAttribDefaultEXT = (PFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC)glbGetProcAddress("glPushClientAttribDefaultEXT"); + pAPI->glTextureParameterfEXT = (PFNGLTEXTUREPARAMETERFEXTPROC)glbGetProcAddress("glTextureParameterfEXT"); + pAPI->glTextureParameterfvEXT = (PFNGLTEXTUREPARAMETERFVEXTPROC)glbGetProcAddress("glTextureParameterfvEXT"); + pAPI->glTextureParameteriEXT = (PFNGLTEXTUREPARAMETERIEXTPROC)glbGetProcAddress("glTextureParameteriEXT"); + pAPI->glTextureParameterivEXT = (PFNGLTEXTUREPARAMETERIVEXTPROC)glbGetProcAddress("glTextureParameterivEXT"); + pAPI->glTextureImage1DEXT = (PFNGLTEXTUREIMAGE1DEXTPROC)glbGetProcAddress("glTextureImage1DEXT"); + pAPI->glTextureImage2DEXT = (PFNGLTEXTUREIMAGE2DEXTPROC)glbGetProcAddress("glTextureImage2DEXT"); + pAPI->glTextureSubImage1DEXT = (PFNGLTEXTURESUBIMAGE1DEXTPROC)glbGetProcAddress("glTextureSubImage1DEXT"); + pAPI->glTextureSubImage2DEXT = (PFNGLTEXTURESUBIMAGE2DEXTPROC)glbGetProcAddress("glTextureSubImage2DEXT"); + pAPI->glCopyTextureImage1DEXT = (PFNGLCOPYTEXTUREIMAGE1DEXTPROC)glbGetProcAddress("glCopyTextureImage1DEXT"); + pAPI->glCopyTextureImage2DEXT = (PFNGLCOPYTEXTUREIMAGE2DEXTPROC)glbGetProcAddress("glCopyTextureImage2DEXT"); + pAPI->glCopyTextureSubImage1DEXT = (PFNGLCOPYTEXTURESUBIMAGE1DEXTPROC)glbGetProcAddress("glCopyTextureSubImage1DEXT"); + pAPI->glCopyTextureSubImage2DEXT = (PFNGLCOPYTEXTURESUBIMAGE2DEXTPROC)glbGetProcAddress("glCopyTextureSubImage2DEXT"); + pAPI->glGetTextureImageEXT = (PFNGLGETTEXTUREIMAGEEXTPROC)glbGetProcAddress("glGetTextureImageEXT"); + pAPI->glGetTextureParameterfvEXT = (PFNGLGETTEXTUREPARAMETERFVEXTPROC)glbGetProcAddress("glGetTextureParameterfvEXT"); + pAPI->glGetTextureParameterivEXT = (PFNGLGETTEXTUREPARAMETERIVEXTPROC)glbGetProcAddress("glGetTextureParameterivEXT"); + pAPI->glGetTextureLevelParameterfvEXT = (PFNGLGETTEXTURELEVELPARAMETERFVEXTPROC)glbGetProcAddress("glGetTextureLevelParameterfvEXT"); + pAPI->glGetTextureLevelParameterivEXT = (PFNGLGETTEXTURELEVELPARAMETERIVEXTPROC)glbGetProcAddress("glGetTextureLevelParameterivEXT"); + pAPI->glTextureImage3DEXT = (PFNGLTEXTUREIMAGE3DEXTPROC)glbGetProcAddress("glTextureImage3DEXT"); + pAPI->glTextureSubImage3DEXT = (PFNGLTEXTURESUBIMAGE3DEXTPROC)glbGetProcAddress("glTextureSubImage3DEXT"); + pAPI->glCopyTextureSubImage3DEXT = (PFNGLCOPYTEXTURESUBIMAGE3DEXTPROC)glbGetProcAddress("glCopyTextureSubImage3DEXT"); + pAPI->glBindMultiTextureEXT = (PFNGLBINDMULTITEXTUREEXTPROC)glbGetProcAddress("glBindMultiTextureEXT"); + pAPI->glMultiTexCoordPointerEXT = (PFNGLMULTITEXCOORDPOINTEREXTPROC)glbGetProcAddress("glMultiTexCoordPointerEXT"); + pAPI->glMultiTexEnvfEXT = (PFNGLMULTITEXENVFEXTPROC)glbGetProcAddress("glMultiTexEnvfEXT"); + pAPI->glMultiTexEnvfvEXT = (PFNGLMULTITEXENVFVEXTPROC)glbGetProcAddress("glMultiTexEnvfvEXT"); + pAPI->glMultiTexEnviEXT = (PFNGLMULTITEXENVIEXTPROC)glbGetProcAddress("glMultiTexEnviEXT"); + pAPI->glMultiTexEnvivEXT = (PFNGLMULTITEXENVIVEXTPROC)glbGetProcAddress("glMultiTexEnvivEXT"); + pAPI->glMultiTexGendEXT = (PFNGLMULTITEXGENDEXTPROC)glbGetProcAddress("glMultiTexGendEXT"); + pAPI->glMultiTexGendvEXT = (PFNGLMULTITEXGENDVEXTPROC)glbGetProcAddress("glMultiTexGendvEXT"); + pAPI->glMultiTexGenfEXT = (PFNGLMULTITEXGENFEXTPROC)glbGetProcAddress("glMultiTexGenfEXT"); + pAPI->glMultiTexGenfvEXT = (PFNGLMULTITEXGENFVEXTPROC)glbGetProcAddress("glMultiTexGenfvEXT"); + pAPI->glMultiTexGeniEXT = (PFNGLMULTITEXGENIEXTPROC)glbGetProcAddress("glMultiTexGeniEXT"); + pAPI->glMultiTexGenivEXT = (PFNGLMULTITEXGENIVEXTPROC)glbGetProcAddress("glMultiTexGenivEXT"); + pAPI->glGetMultiTexEnvfvEXT = (PFNGLGETMULTITEXENVFVEXTPROC)glbGetProcAddress("glGetMultiTexEnvfvEXT"); + pAPI->glGetMultiTexEnvivEXT = (PFNGLGETMULTITEXENVIVEXTPROC)glbGetProcAddress("glGetMultiTexEnvivEXT"); + pAPI->glGetMultiTexGendvEXT = (PFNGLGETMULTITEXGENDVEXTPROC)glbGetProcAddress("glGetMultiTexGendvEXT"); + pAPI->glGetMultiTexGenfvEXT = (PFNGLGETMULTITEXGENFVEXTPROC)glbGetProcAddress("glGetMultiTexGenfvEXT"); + pAPI->glGetMultiTexGenivEXT = (PFNGLGETMULTITEXGENIVEXTPROC)glbGetProcAddress("glGetMultiTexGenivEXT"); + pAPI->glMultiTexParameteriEXT = (PFNGLMULTITEXPARAMETERIEXTPROC)glbGetProcAddress("glMultiTexParameteriEXT"); + pAPI->glMultiTexParameterivEXT = (PFNGLMULTITEXPARAMETERIVEXTPROC)glbGetProcAddress("glMultiTexParameterivEXT"); + pAPI->glMultiTexParameterfEXT = (PFNGLMULTITEXPARAMETERFEXTPROC)glbGetProcAddress("glMultiTexParameterfEXT"); + pAPI->glMultiTexParameterfvEXT = (PFNGLMULTITEXPARAMETERFVEXTPROC)glbGetProcAddress("glMultiTexParameterfvEXT"); + pAPI->glMultiTexImage1DEXT = (PFNGLMULTITEXIMAGE1DEXTPROC)glbGetProcAddress("glMultiTexImage1DEXT"); + pAPI->glMultiTexImage2DEXT = (PFNGLMULTITEXIMAGE2DEXTPROC)glbGetProcAddress("glMultiTexImage2DEXT"); + pAPI->glMultiTexSubImage1DEXT = (PFNGLMULTITEXSUBIMAGE1DEXTPROC)glbGetProcAddress("glMultiTexSubImage1DEXT"); + pAPI->glMultiTexSubImage2DEXT = (PFNGLMULTITEXSUBIMAGE2DEXTPROC)glbGetProcAddress("glMultiTexSubImage2DEXT"); + pAPI->glCopyMultiTexImage1DEXT = (PFNGLCOPYMULTITEXIMAGE1DEXTPROC)glbGetProcAddress("glCopyMultiTexImage1DEXT"); + pAPI->glCopyMultiTexImage2DEXT = (PFNGLCOPYMULTITEXIMAGE2DEXTPROC)glbGetProcAddress("glCopyMultiTexImage2DEXT"); + pAPI->glCopyMultiTexSubImage1DEXT = (PFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC)glbGetProcAddress("glCopyMultiTexSubImage1DEXT"); + pAPI->glCopyMultiTexSubImage2DEXT = (PFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC)glbGetProcAddress("glCopyMultiTexSubImage2DEXT"); + pAPI->glGetMultiTexImageEXT = (PFNGLGETMULTITEXIMAGEEXTPROC)glbGetProcAddress("glGetMultiTexImageEXT"); + pAPI->glGetMultiTexParameterfvEXT = (PFNGLGETMULTITEXPARAMETERFVEXTPROC)glbGetProcAddress("glGetMultiTexParameterfvEXT"); + pAPI->glGetMultiTexParameterivEXT = (PFNGLGETMULTITEXPARAMETERIVEXTPROC)glbGetProcAddress("glGetMultiTexParameterivEXT"); + pAPI->glGetMultiTexLevelParameterfvEXT = (PFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC)glbGetProcAddress("glGetMultiTexLevelParameterfvEXT"); + pAPI->glGetMultiTexLevelParameterivEXT = (PFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC)glbGetProcAddress("glGetMultiTexLevelParameterivEXT"); + pAPI->glMultiTexImage3DEXT = (PFNGLMULTITEXIMAGE3DEXTPROC)glbGetProcAddress("glMultiTexImage3DEXT"); + pAPI->glMultiTexSubImage3DEXT = (PFNGLMULTITEXSUBIMAGE3DEXTPROC)glbGetProcAddress("glMultiTexSubImage3DEXT"); + pAPI->glCopyMultiTexSubImage3DEXT = (PFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC)glbGetProcAddress("glCopyMultiTexSubImage3DEXT"); + pAPI->glEnableClientStateIndexedEXT = (PFNGLENABLECLIENTSTATEINDEXEDEXTPROC)glbGetProcAddress("glEnableClientStateIndexedEXT"); + pAPI->glDisableClientStateIndexedEXT = (PFNGLDISABLECLIENTSTATEINDEXEDEXTPROC)glbGetProcAddress("glDisableClientStateIndexedEXT"); + pAPI->glGetFloatIndexedvEXT = (PFNGLGETFLOATINDEXEDVEXTPROC)glbGetProcAddress("glGetFloatIndexedvEXT"); + pAPI->glGetDoubleIndexedvEXT = (PFNGLGETDOUBLEINDEXEDVEXTPROC)glbGetProcAddress("glGetDoubleIndexedvEXT"); + pAPI->glGetPointerIndexedvEXT = (PFNGLGETPOINTERINDEXEDVEXTPROC)glbGetProcAddress("glGetPointerIndexedvEXT"); + pAPI->glEnableIndexedEXT = (PFNGLENABLEINDEXEDEXTPROC)glbGetProcAddress("glEnableIndexedEXT"); + pAPI->glDisableIndexedEXT = (PFNGLDISABLEINDEXEDEXTPROC)glbGetProcAddress("glDisableIndexedEXT"); + pAPI->glIsEnabledIndexedEXT = (PFNGLISENABLEDINDEXEDEXTPROC)glbGetProcAddress("glIsEnabledIndexedEXT"); + pAPI->glGetIntegerIndexedvEXT = (PFNGLGETINTEGERINDEXEDVEXTPROC)glbGetProcAddress("glGetIntegerIndexedvEXT"); + pAPI->glGetBooleanIndexedvEXT = (PFNGLGETBOOLEANINDEXEDVEXTPROC)glbGetProcAddress("glGetBooleanIndexedvEXT"); + pAPI->glCompressedTextureImage3DEXT = (PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC)glbGetProcAddress("glCompressedTextureImage3DEXT"); + pAPI->glCompressedTextureImage2DEXT = (PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC)glbGetProcAddress("glCompressedTextureImage2DEXT"); + pAPI->glCompressedTextureImage1DEXT = (PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC)glbGetProcAddress("glCompressedTextureImage1DEXT"); + pAPI->glCompressedTextureSubImage3DEXT = (PFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC)glbGetProcAddress("glCompressedTextureSubImage3DEXT"); + pAPI->glCompressedTextureSubImage2DEXT = (PFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC)glbGetProcAddress("glCompressedTextureSubImage2DEXT"); + pAPI->glCompressedTextureSubImage1DEXT = (PFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC)glbGetProcAddress("glCompressedTextureSubImage1DEXT"); + pAPI->glGetCompressedTextureImageEXT = (PFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC)glbGetProcAddress("glGetCompressedTextureImageEXT"); + pAPI->glCompressedMultiTexImage3DEXT = (PFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC)glbGetProcAddress("glCompressedMultiTexImage3DEXT"); + pAPI->glCompressedMultiTexImage2DEXT = (PFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC)glbGetProcAddress("glCompressedMultiTexImage2DEXT"); + pAPI->glCompressedMultiTexImage1DEXT = (PFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC)glbGetProcAddress("glCompressedMultiTexImage1DEXT"); + pAPI->glCompressedMultiTexSubImage3DEXT = (PFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC)glbGetProcAddress("glCompressedMultiTexSubImage3DEXT"); + pAPI->glCompressedMultiTexSubImage2DEXT = (PFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC)glbGetProcAddress("glCompressedMultiTexSubImage2DEXT"); + pAPI->glCompressedMultiTexSubImage1DEXT = (PFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC)glbGetProcAddress("glCompressedMultiTexSubImage1DEXT"); + pAPI->glGetCompressedMultiTexImageEXT = (PFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC)glbGetProcAddress("glGetCompressedMultiTexImageEXT"); + pAPI->glMatrixLoadTransposefEXT = (PFNGLMATRIXLOADTRANSPOSEFEXTPROC)glbGetProcAddress("glMatrixLoadTransposefEXT"); + pAPI->glMatrixLoadTransposedEXT = (PFNGLMATRIXLOADTRANSPOSEDEXTPROC)glbGetProcAddress("glMatrixLoadTransposedEXT"); + pAPI->glMatrixMultTransposefEXT = (PFNGLMATRIXMULTTRANSPOSEFEXTPROC)glbGetProcAddress("glMatrixMultTransposefEXT"); + pAPI->glMatrixMultTransposedEXT = (PFNGLMATRIXMULTTRANSPOSEDEXTPROC)glbGetProcAddress("glMatrixMultTransposedEXT"); + pAPI->glNamedBufferDataEXT = (PFNGLNAMEDBUFFERDATAEXTPROC)glbGetProcAddress("glNamedBufferDataEXT"); + pAPI->glNamedBufferSubDataEXT = (PFNGLNAMEDBUFFERSUBDATAEXTPROC)glbGetProcAddress("glNamedBufferSubDataEXT"); + pAPI->glMapNamedBufferEXT = (PFNGLMAPNAMEDBUFFEREXTPROC)glbGetProcAddress("glMapNamedBufferEXT"); + pAPI->glUnmapNamedBufferEXT = (PFNGLUNMAPNAMEDBUFFEREXTPROC)glbGetProcAddress("glUnmapNamedBufferEXT"); + pAPI->glGetNamedBufferParameterivEXT = (PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC)glbGetProcAddress("glGetNamedBufferParameterivEXT"); + pAPI->glGetNamedBufferPointervEXT = (PFNGLGETNAMEDBUFFERPOINTERVEXTPROC)glbGetProcAddress("glGetNamedBufferPointervEXT"); + pAPI->glGetNamedBufferSubDataEXT = (PFNGLGETNAMEDBUFFERSUBDATAEXTPROC)glbGetProcAddress("glGetNamedBufferSubDataEXT"); + pAPI->glProgramUniform1fEXT = (PFNGLPROGRAMUNIFORM1FEXTPROC)glbGetProcAddress("glProgramUniform1fEXT"); + pAPI->glProgramUniform2fEXT = (PFNGLPROGRAMUNIFORM2FEXTPROC)glbGetProcAddress("glProgramUniform2fEXT"); + pAPI->glProgramUniform3fEXT = (PFNGLPROGRAMUNIFORM3FEXTPROC)glbGetProcAddress("glProgramUniform3fEXT"); + pAPI->glProgramUniform4fEXT = (PFNGLPROGRAMUNIFORM4FEXTPROC)glbGetProcAddress("glProgramUniform4fEXT"); + pAPI->glProgramUniform1iEXT = (PFNGLPROGRAMUNIFORM1IEXTPROC)glbGetProcAddress("glProgramUniform1iEXT"); + pAPI->glProgramUniform2iEXT = (PFNGLPROGRAMUNIFORM2IEXTPROC)glbGetProcAddress("glProgramUniform2iEXT"); + pAPI->glProgramUniform3iEXT = (PFNGLPROGRAMUNIFORM3IEXTPROC)glbGetProcAddress("glProgramUniform3iEXT"); + pAPI->glProgramUniform4iEXT = (PFNGLPROGRAMUNIFORM4IEXTPROC)glbGetProcAddress("glProgramUniform4iEXT"); + pAPI->glProgramUniform1fvEXT = (PFNGLPROGRAMUNIFORM1FVEXTPROC)glbGetProcAddress("glProgramUniform1fvEXT"); + pAPI->glProgramUniform2fvEXT = (PFNGLPROGRAMUNIFORM2FVEXTPROC)glbGetProcAddress("glProgramUniform2fvEXT"); + pAPI->glProgramUniform3fvEXT = (PFNGLPROGRAMUNIFORM3FVEXTPROC)glbGetProcAddress("glProgramUniform3fvEXT"); + pAPI->glProgramUniform4fvEXT = (PFNGLPROGRAMUNIFORM4FVEXTPROC)glbGetProcAddress("glProgramUniform4fvEXT"); + pAPI->glProgramUniform1ivEXT = (PFNGLPROGRAMUNIFORM1IVEXTPROC)glbGetProcAddress("glProgramUniform1ivEXT"); + pAPI->glProgramUniform2ivEXT = (PFNGLPROGRAMUNIFORM2IVEXTPROC)glbGetProcAddress("glProgramUniform2ivEXT"); + pAPI->glProgramUniform3ivEXT = (PFNGLPROGRAMUNIFORM3IVEXTPROC)glbGetProcAddress("glProgramUniform3ivEXT"); + pAPI->glProgramUniform4ivEXT = (PFNGLPROGRAMUNIFORM4IVEXTPROC)glbGetProcAddress("glProgramUniform4ivEXT"); + pAPI->glProgramUniformMatrix2fvEXT = (PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC)glbGetProcAddress("glProgramUniformMatrix2fvEXT"); + pAPI->glProgramUniformMatrix3fvEXT = (PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC)glbGetProcAddress("glProgramUniformMatrix3fvEXT"); + pAPI->glProgramUniformMatrix4fvEXT = (PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC)glbGetProcAddress("glProgramUniformMatrix4fvEXT"); + pAPI->glProgramUniformMatrix2x3fvEXT = (PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC)glbGetProcAddress("glProgramUniformMatrix2x3fvEXT"); + pAPI->glProgramUniformMatrix3x2fvEXT = (PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC)glbGetProcAddress("glProgramUniformMatrix3x2fvEXT"); + pAPI->glProgramUniformMatrix2x4fvEXT = (PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC)glbGetProcAddress("glProgramUniformMatrix2x4fvEXT"); + pAPI->glProgramUniformMatrix4x2fvEXT = (PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC)glbGetProcAddress("glProgramUniformMatrix4x2fvEXT"); + pAPI->glProgramUniformMatrix3x4fvEXT = (PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC)glbGetProcAddress("glProgramUniformMatrix3x4fvEXT"); + pAPI->glProgramUniformMatrix4x3fvEXT = (PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC)glbGetProcAddress("glProgramUniformMatrix4x3fvEXT"); + pAPI->glTextureBufferEXT = (PFNGLTEXTUREBUFFEREXTPROC)glbGetProcAddress("glTextureBufferEXT"); + pAPI->glMultiTexBufferEXT = (PFNGLMULTITEXBUFFEREXTPROC)glbGetProcAddress("glMultiTexBufferEXT"); + pAPI->glTextureParameterIivEXT = (PFNGLTEXTUREPARAMETERIIVEXTPROC)glbGetProcAddress("glTextureParameterIivEXT"); + pAPI->glTextureParameterIuivEXT = (PFNGLTEXTUREPARAMETERIUIVEXTPROC)glbGetProcAddress("glTextureParameterIuivEXT"); + pAPI->glGetTextureParameterIivEXT = (PFNGLGETTEXTUREPARAMETERIIVEXTPROC)glbGetProcAddress("glGetTextureParameterIivEXT"); + pAPI->glGetTextureParameterIuivEXT = (PFNGLGETTEXTUREPARAMETERIUIVEXTPROC)glbGetProcAddress("glGetTextureParameterIuivEXT"); + pAPI->glMultiTexParameterIivEXT = (PFNGLMULTITEXPARAMETERIIVEXTPROC)glbGetProcAddress("glMultiTexParameterIivEXT"); + pAPI->glMultiTexParameterIuivEXT = (PFNGLMULTITEXPARAMETERIUIVEXTPROC)glbGetProcAddress("glMultiTexParameterIuivEXT"); + pAPI->glGetMultiTexParameterIivEXT = (PFNGLGETMULTITEXPARAMETERIIVEXTPROC)glbGetProcAddress("glGetMultiTexParameterIivEXT"); + pAPI->glGetMultiTexParameterIuivEXT = (PFNGLGETMULTITEXPARAMETERIUIVEXTPROC)glbGetProcAddress("glGetMultiTexParameterIuivEXT"); + pAPI->glProgramUniform1uiEXT = (PFNGLPROGRAMUNIFORM1UIEXTPROC)glbGetProcAddress("glProgramUniform1uiEXT"); + pAPI->glProgramUniform2uiEXT = (PFNGLPROGRAMUNIFORM2UIEXTPROC)glbGetProcAddress("glProgramUniform2uiEXT"); + pAPI->glProgramUniform3uiEXT = (PFNGLPROGRAMUNIFORM3UIEXTPROC)glbGetProcAddress("glProgramUniform3uiEXT"); + pAPI->glProgramUniform4uiEXT = (PFNGLPROGRAMUNIFORM4UIEXTPROC)glbGetProcAddress("glProgramUniform4uiEXT"); + pAPI->glProgramUniform1uivEXT = (PFNGLPROGRAMUNIFORM1UIVEXTPROC)glbGetProcAddress("glProgramUniform1uivEXT"); + pAPI->glProgramUniform2uivEXT = (PFNGLPROGRAMUNIFORM2UIVEXTPROC)glbGetProcAddress("glProgramUniform2uivEXT"); + pAPI->glProgramUniform3uivEXT = (PFNGLPROGRAMUNIFORM3UIVEXTPROC)glbGetProcAddress("glProgramUniform3uivEXT"); + pAPI->glProgramUniform4uivEXT = (PFNGLPROGRAMUNIFORM4UIVEXTPROC)glbGetProcAddress("glProgramUniform4uivEXT"); + pAPI->glNamedProgramLocalParameters4fvEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC)glbGetProcAddress("glNamedProgramLocalParameters4fvEXT"); + pAPI->glNamedProgramLocalParameterI4iEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC)glbGetProcAddress("glNamedProgramLocalParameterI4iEXT"); + pAPI->glNamedProgramLocalParameterI4ivEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC)glbGetProcAddress("glNamedProgramLocalParameterI4ivEXT"); + pAPI->glNamedProgramLocalParametersI4ivEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC)glbGetProcAddress("glNamedProgramLocalParametersI4ivEXT"); + pAPI->glNamedProgramLocalParameterI4uiEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC)glbGetProcAddress("glNamedProgramLocalParameterI4uiEXT"); + pAPI->glNamedProgramLocalParameterI4uivEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC)glbGetProcAddress("glNamedProgramLocalParameterI4uivEXT"); + pAPI->glNamedProgramLocalParametersI4uivEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC)glbGetProcAddress("glNamedProgramLocalParametersI4uivEXT"); + pAPI->glGetNamedProgramLocalParameterIivEXT = (PFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC)glbGetProcAddress("glGetNamedProgramLocalParameterIivEXT"); + pAPI->glGetNamedProgramLocalParameterIuivEXT = (PFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC)glbGetProcAddress("glGetNamedProgramLocalParameterIuivEXT"); + pAPI->glEnableClientStateiEXT = (PFNGLENABLECLIENTSTATEIEXTPROC)glbGetProcAddress("glEnableClientStateiEXT"); + pAPI->glDisableClientStateiEXT = (PFNGLDISABLECLIENTSTATEIEXTPROC)glbGetProcAddress("glDisableClientStateiEXT"); + pAPI->glGetFloati_vEXT = (PFNGLGETFLOATI_VEXTPROC)glbGetProcAddress("glGetFloati_vEXT"); + pAPI->glGetDoublei_vEXT = (PFNGLGETDOUBLEI_VEXTPROC)glbGetProcAddress("glGetDoublei_vEXT"); + pAPI->glGetPointeri_vEXT = (PFNGLGETPOINTERI_VEXTPROC)glbGetProcAddress("glGetPointeri_vEXT"); + pAPI->glNamedProgramStringEXT = (PFNGLNAMEDPROGRAMSTRINGEXTPROC)glbGetProcAddress("glNamedProgramStringEXT"); + pAPI->glNamedProgramLocalParameter4dEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC)glbGetProcAddress("glNamedProgramLocalParameter4dEXT"); + pAPI->glNamedProgramLocalParameter4dvEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC)glbGetProcAddress("glNamedProgramLocalParameter4dvEXT"); + pAPI->glNamedProgramLocalParameter4fEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC)glbGetProcAddress("glNamedProgramLocalParameter4fEXT"); + pAPI->glNamedProgramLocalParameter4fvEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC)glbGetProcAddress("glNamedProgramLocalParameter4fvEXT"); + pAPI->glGetNamedProgramLocalParameterdvEXT = (PFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC)glbGetProcAddress("glGetNamedProgramLocalParameterdvEXT"); + pAPI->glGetNamedProgramLocalParameterfvEXT = (PFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC)glbGetProcAddress("glGetNamedProgramLocalParameterfvEXT"); + pAPI->glGetNamedProgramivEXT = (PFNGLGETNAMEDPROGRAMIVEXTPROC)glbGetProcAddress("glGetNamedProgramivEXT"); + pAPI->glGetNamedProgramStringEXT = (PFNGLGETNAMEDPROGRAMSTRINGEXTPROC)glbGetProcAddress("glGetNamedProgramStringEXT"); + pAPI->glNamedRenderbufferStorageEXT = (PFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC)glbGetProcAddress("glNamedRenderbufferStorageEXT"); + pAPI->glGetNamedRenderbufferParameterivEXT = (PFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC)glbGetProcAddress("glGetNamedRenderbufferParameterivEXT"); + pAPI->glNamedRenderbufferStorageMultisampleEXT = (PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)glbGetProcAddress("glNamedRenderbufferStorageMultisampleEXT"); + pAPI->glNamedRenderbufferStorageMultisampleCoverageEXT = (PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC)glbGetProcAddress("glNamedRenderbufferStorageMultisampleCoverageEXT"); + pAPI->glCheckNamedFramebufferStatusEXT = (PFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC)glbGetProcAddress("glCheckNamedFramebufferStatusEXT"); + pAPI->glNamedFramebufferTexture1DEXT = (PFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC)glbGetProcAddress("glNamedFramebufferTexture1DEXT"); + pAPI->glNamedFramebufferTexture2DEXT = (PFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC)glbGetProcAddress("glNamedFramebufferTexture2DEXT"); + pAPI->glNamedFramebufferTexture3DEXT = (PFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC)glbGetProcAddress("glNamedFramebufferTexture3DEXT"); + pAPI->glNamedFramebufferRenderbufferEXT = (PFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC)glbGetProcAddress("glNamedFramebufferRenderbufferEXT"); + pAPI->glGetNamedFramebufferAttachmentParameterivEXT = (PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC)glbGetProcAddress("glGetNamedFramebufferAttachmentParameterivEXT"); + pAPI->glGenerateTextureMipmapEXT = (PFNGLGENERATETEXTUREMIPMAPEXTPROC)glbGetProcAddress("glGenerateTextureMipmapEXT"); + pAPI->glGenerateMultiTexMipmapEXT = (PFNGLGENERATEMULTITEXMIPMAPEXTPROC)glbGetProcAddress("glGenerateMultiTexMipmapEXT"); + pAPI->glFramebufferDrawBufferEXT = (PFNGLFRAMEBUFFERDRAWBUFFEREXTPROC)glbGetProcAddress("glFramebufferDrawBufferEXT"); + pAPI->glFramebufferDrawBuffersEXT = (PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC)glbGetProcAddress("glFramebufferDrawBuffersEXT"); + pAPI->glFramebufferReadBufferEXT = (PFNGLFRAMEBUFFERREADBUFFEREXTPROC)glbGetProcAddress("glFramebufferReadBufferEXT"); + pAPI->glGetFramebufferParameterivEXT = (PFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC)glbGetProcAddress("glGetFramebufferParameterivEXT"); + pAPI->glNamedCopyBufferSubDataEXT = (PFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC)glbGetProcAddress("glNamedCopyBufferSubDataEXT"); + pAPI->glNamedFramebufferTextureEXT = (PFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC)glbGetProcAddress("glNamedFramebufferTextureEXT"); + pAPI->glNamedFramebufferTextureLayerEXT = (PFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC)glbGetProcAddress("glNamedFramebufferTextureLayerEXT"); + pAPI->glNamedFramebufferTextureFaceEXT = (PFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC)glbGetProcAddress("glNamedFramebufferTextureFaceEXT"); + pAPI->glTextureRenderbufferEXT = (PFNGLTEXTURERENDERBUFFEREXTPROC)glbGetProcAddress("glTextureRenderbufferEXT"); + pAPI->glMultiTexRenderbufferEXT = (PFNGLMULTITEXRENDERBUFFEREXTPROC)glbGetProcAddress("glMultiTexRenderbufferEXT"); + pAPI->glVertexArrayVertexOffsetEXT = (PFNGLVERTEXARRAYVERTEXOFFSETEXTPROC)glbGetProcAddress("glVertexArrayVertexOffsetEXT"); + pAPI->glVertexArrayColorOffsetEXT = (PFNGLVERTEXARRAYCOLOROFFSETEXTPROC)glbGetProcAddress("glVertexArrayColorOffsetEXT"); + pAPI->glVertexArrayEdgeFlagOffsetEXT = (PFNGLVERTEXARRAYEDGEFLAGOFFSETEXTPROC)glbGetProcAddress("glVertexArrayEdgeFlagOffsetEXT"); + pAPI->glVertexArrayIndexOffsetEXT = (PFNGLVERTEXARRAYINDEXOFFSETEXTPROC)glbGetProcAddress("glVertexArrayIndexOffsetEXT"); + pAPI->glVertexArrayNormalOffsetEXT = (PFNGLVERTEXARRAYNORMALOFFSETEXTPROC)glbGetProcAddress("glVertexArrayNormalOffsetEXT"); + pAPI->glVertexArrayTexCoordOffsetEXT = (PFNGLVERTEXARRAYTEXCOORDOFFSETEXTPROC)glbGetProcAddress("glVertexArrayTexCoordOffsetEXT"); + pAPI->glVertexArrayMultiTexCoordOffsetEXT = (PFNGLVERTEXARRAYMULTITEXCOORDOFFSETEXTPROC)glbGetProcAddress("glVertexArrayMultiTexCoordOffsetEXT"); + pAPI->glVertexArrayFogCoordOffsetEXT = (PFNGLVERTEXARRAYFOGCOORDOFFSETEXTPROC)glbGetProcAddress("glVertexArrayFogCoordOffsetEXT"); + pAPI->glVertexArraySecondaryColorOffsetEXT = (PFNGLVERTEXARRAYSECONDARYCOLOROFFSETEXTPROC)glbGetProcAddress("glVertexArraySecondaryColorOffsetEXT"); + pAPI->glVertexArrayVertexAttribOffsetEXT = (PFNGLVERTEXARRAYVERTEXATTRIBOFFSETEXTPROC)glbGetProcAddress("glVertexArrayVertexAttribOffsetEXT"); + pAPI->glVertexArrayVertexAttribIOffsetEXT = (PFNGLVERTEXARRAYVERTEXATTRIBIOFFSETEXTPROC)glbGetProcAddress("glVertexArrayVertexAttribIOffsetEXT"); + pAPI->glEnableVertexArrayEXT = (PFNGLENABLEVERTEXARRAYEXTPROC)glbGetProcAddress("glEnableVertexArrayEXT"); + pAPI->glDisableVertexArrayEXT = (PFNGLDISABLEVERTEXARRAYEXTPROC)glbGetProcAddress("glDisableVertexArrayEXT"); + pAPI->glEnableVertexArrayAttribEXT = (PFNGLENABLEVERTEXARRAYATTRIBEXTPROC)glbGetProcAddress("glEnableVertexArrayAttribEXT"); + pAPI->glDisableVertexArrayAttribEXT = (PFNGLDISABLEVERTEXARRAYATTRIBEXTPROC)glbGetProcAddress("glDisableVertexArrayAttribEXT"); + pAPI->glGetVertexArrayIntegervEXT = (PFNGLGETVERTEXARRAYINTEGERVEXTPROC)glbGetProcAddress("glGetVertexArrayIntegervEXT"); + pAPI->glGetVertexArrayPointervEXT = (PFNGLGETVERTEXARRAYPOINTERVEXTPROC)glbGetProcAddress("glGetVertexArrayPointervEXT"); + pAPI->glGetVertexArrayIntegeri_vEXT = (PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC)glbGetProcAddress("glGetVertexArrayIntegeri_vEXT"); + pAPI->glGetVertexArrayPointeri_vEXT = (PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC)glbGetProcAddress("glGetVertexArrayPointeri_vEXT"); + pAPI->glMapNamedBufferRangeEXT = (PFNGLMAPNAMEDBUFFERRANGEEXTPROC)glbGetProcAddress("glMapNamedBufferRangeEXT"); + pAPI->glFlushMappedNamedBufferRangeEXT = (PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC)glbGetProcAddress("glFlushMappedNamedBufferRangeEXT"); + pAPI->glNamedBufferStorageEXT = (PFNGLNAMEDBUFFERSTORAGEEXTPROC)glbGetProcAddress("glNamedBufferStorageEXT"); + pAPI->glClearNamedBufferDataEXT = (PFNGLCLEARNAMEDBUFFERDATAEXTPROC)glbGetProcAddress("glClearNamedBufferDataEXT"); + pAPI->glClearNamedBufferSubDataEXT = (PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC)glbGetProcAddress("glClearNamedBufferSubDataEXT"); + pAPI->glNamedFramebufferParameteriEXT = (PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC)glbGetProcAddress("glNamedFramebufferParameteriEXT"); + pAPI->glGetNamedFramebufferParameterivEXT = (PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC)glbGetProcAddress("glGetNamedFramebufferParameterivEXT"); + pAPI->glProgramUniform1dEXT = (PFNGLPROGRAMUNIFORM1DEXTPROC)glbGetProcAddress("glProgramUniform1dEXT"); + pAPI->glProgramUniform2dEXT = (PFNGLPROGRAMUNIFORM2DEXTPROC)glbGetProcAddress("glProgramUniform2dEXT"); + pAPI->glProgramUniform3dEXT = (PFNGLPROGRAMUNIFORM3DEXTPROC)glbGetProcAddress("glProgramUniform3dEXT"); + pAPI->glProgramUniform4dEXT = (PFNGLPROGRAMUNIFORM4DEXTPROC)glbGetProcAddress("glProgramUniform4dEXT"); + pAPI->glProgramUniform1dvEXT = (PFNGLPROGRAMUNIFORM1DVEXTPROC)glbGetProcAddress("glProgramUniform1dvEXT"); + pAPI->glProgramUniform2dvEXT = (PFNGLPROGRAMUNIFORM2DVEXTPROC)glbGetProcAddress("glProgramUniform2dvEXT"); + pAPI->glProgramUniform3dvEXT = (PFNGLPROGRAMUNIFORM3DVEXTPROC)glbGetProcAddress("glProgramUniform3dvEXT"); + pAPI->glProgramUniform4dvEXT = (PFNGLPROGRAMUNIFORM4DVEXTPROC)glbGetProcAddress("glProgramUniform4dvEXT"); + pAPI->glProgramUniformMatrix2dvEXT = (PFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC)glbGetProcAddress("glProgramUniformMatrix2dvEXT"); + pAPI->glProgramUniformMatrix3dvEXT = (PFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC)glbGetProcAddress("glProgramUniformMatrix3dvEXT"); + pAPI->glProgramUniformMatrix4dvEXT = (PFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC)glbGetProcAddress("glProgramUniformMatrix4dvEXT"); + pAPI->glProgramUniformMatrix2x3dvEXT = (PFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC)glbGetProcAddress("glProgramUniformMatrix2x3dvEXT"); + pAPI->glProgramUniformMatrix2x4dvEXT = (PFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC)glbGetProcAddress("glProgramUniformMatrix2x4dvEXT"); + pAPI->glProgramUniformMatrix3x2dvEXT = (PFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC)glbGetProcAddress("glProgramUniformMatrix3x2dvEXT"); + pAPI->glProgramUniformMatrix3x4dvEXT = (PFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC)glbGetProcAddress("glProgramUniformMatrix3x4dvEXT"); + pAPI->glProgramUniformMatrix4x2dvEXT = (PFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC)glbGetProcAddress("glProgramUniformMatrix4x2dvEXT"); + pAPI->glProgramUniformMatrix4x3dvEXT = (PFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC)glbGetProcAddress("glProgramUniformMatrix4x3dvEXT"); + pAPI->glTextureBufferRangeEXT = (PFNGLTEXTUREBUFFERRANGEEXTPROC)glbGetProcAddress("glTextureBufferRangeEXT"); + pAPI->glTextureStorage1DEXT = (PFNGLTEXTURESTORAGE1DEXTPROC)glbGetProcAddress("glTextureStorage1DEXT"); + pAPI->glTextureStorage2DEXT = (PFNGLTEXTURESTORAGE2DEXTPROC)glbGetProcAddress("glTextureStorage2DEXT"); + pAPI->glTextureStorage3DEXT = (PFNGLTEXTURESTORAGE3DEXTPROC)glbGetProcAddress("glTextureStorage3DEXT"); + pAPI->glTextureStorage2DMultisampleEXT = (PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC)glbGetProcAddress("glTextureStorage2DMultisampleEXT"); + pAPI->glTextureStorage3DMultisampleEXT = (PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC)glbGetProcAddress("glTextureStorage3DMultisampleEXT"); + pAPI->glVertexArrayBindVertexBufferEXT = (PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC)glbGetProcAddress("glVertexArrayBindVertexBufferEXT"); + pAPI->glVertexArrayVertexAttribFormatEXT = (PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC)glbGetProcAddress("glVertexArrayVertexAttribFormatEXT"); + pAPI->glVertexArrayVertexAttribIFormatEXT = (PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC)glbGetProcAddress("glVertexArrayVertexAttribIFormatEXT"); + pAPI->glVertexArrayVertexAttribLFormatEXT = (PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC)glbGetProcAddress("glVertexArrayVertexAttribLFormatEXT"); + pAPI->glVertexArrayVertexAttribBindingEXT = (PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC)glbGetProcAddress("glVertexArrayVertexAttribBindingEXT"); + pAPI->glVertexArrayVertexBindingDivisorEXT = (PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC)glbGetProcAddress("glVertexArrayVertexBindingDivisorEXT"); + pAPI->glVertexArrayVertexAttribLOffsetEXT = (PFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC)glbGetProcAddress("glVertexArrayVertexAttribLOffsetEXT"); + pAPI->glTexturePageCommitmentEXT = (PFNGLTEXTUREPAGECOMMITMENTEXTPROC)glbGetProcAddress("glTexturePageCommitmentEXT"); + pAPI->glVertexArrayVertexAttribDivisorEXT = (PFNGLVERTEXARRAYVERTEXATTRIBDIVISOREXTPROC)glbGetProcAddress("glVertexArrayVertexAttribDivisorEXT"); + pAPI->glColorMaskIndexedEXT = (PFNGLCOLORMASKINDEXEDEXTPROC)glbGetProcAddress("glColorMaskIndexedEXT"); + pAPI->glDrawArraysInstancedEXT = (PFNGLDRAWARRAYSINSTANCEDEXTPROC)glbGetProcAddress("glDrawArraysInstancedEXT"); + pAPI->glDrawElementsInstancedEXT = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC)glbGetProcAddress("glDrawElementsInstancedEXT"); + pAPI->glDrawRangeElementsEXT = (PFNGLDRAWRANGEELEMENTSEXTPROC)glbGetProcAddress("glDrawRangeElementsEXT"); + pAPI->glBufferStorageExternalEXT = (PFNGLBUFFERSTORAGEEXTERNALEXTPROC)glbGetProcAddress("glBufferStorageExternalEXT"); + pAPI->glNamedBufferStorageExternalEXT = (PFNGLNAMEDBUFFERSTORAGEEXTERNALEXTPROC)glbGetProcAddress("glNamedBufferStorageExternalEXT"); + pAPI->glFogCoordfEXT = (PFNGLFOGCOORDFEXTPROC)glbGetProcAddress("glFogCoordfEXT"); + pAPI->glFogCoordfvEXT = (PFNGLFOGCOORDFVEXTPROC)glbGetProcAddress("glFogCoordfvEXT"); + pAPI->glFogCoorddEXT = (PFNGLFOGCOORDDEXTPROC)glbGetProcAddress("glFogCoorddEXT"); + pAPI->glFogCoorddvEXT = (PFNGLFOGCOORDDVEXTPROC)glbGetProcAddress("glFogCoorddvEXT"); + pAPI->glFogCoordPointerEXT = (PFNGLFOGCOORDPOINTEREXTPROC)glbGetProcAddress("glFogCoordPointerEXT"); + pAPI->glBlitFramebufferEXT = (PFNGLBLITFRAMEBUFFEREXTPROC)glbGetProcAddress("glBlitFramebufferEXT"); + pAPI->glRenderbufferStorageMultisampleEXT = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)glbGetProcAddress("glRenderbufferStorageMultisampleEXT"); + pAPI->glIsRenderbufferEXT = (PFNGLISRENDERBUFFEREXTPROC)glbGetProcAddress("glIsRenderbufferEXT"); + pAPI->glBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC)glbGetProcAddress("glBindRenderbufferEXT"); + pAPI->glDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC)glbGetProcAddress("glDeleteRenderbuffersEXT"); + pAPI->glGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC)glbGetProcAddress("glGenRenderbuffersEXT"); + pAPI->glRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC)glbGetProcAddress("glRenderbufferStorageEXT"); + pAPI->glGetRenderbufferParameterivEXT = (PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC)glbGetProcAddress("glGetRenderbufferParameterivEXT"); + pAPI->glIsFramebufferEXT = (PFNGLISFRAMEBUFFEREXTPROC)glbGetProcAddress("glIsFramebufferEXT"); + pAPI->glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC)glbGetProcAddress("glBindFramebufferEXT"); + pAPI->glDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC)glbGetProcAddress("glDeleteFramebuffersEXT"); + pAPI->glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC)glbGetProcAddress("glGenFramebuffersEXT"); + pAPI->glCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)glbGetProcAddress("glCheckFramebufferStatusEXT"); + pAPI->glFramebufferTexture1DEXT = (PFNGLFRAMEBUFFERTEXTURE1DEXTPROC)glbGetProcAddress("glFramebufferTexture1DEXT"); + pAPI->glFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)glbGetProcAddress("glFramebufferTexture2DEXT"); + pAPI->glFramebufferTexture3DEXT = (PFNGLFRAMEBUFFERTEXTURE3DEXTPROC)glbGetProcAddress("glFramebufferTexture3DEXT"); + pAPI->glFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)glbGetProcAddress("glFramebufferRenderbufferEXT"); + pAPI->glGetFramebufferAttachmentParameterivEXT = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC)glbGetProcAddress("glGetFramebufferAttachmentParameterivEXT"); + pAPI->glGenerateMipmapEXT = (PFNGLGENERATEMIPMAPEXTPROC)glbGetProcAddress("glGenerateMipmapEXT"); + pAPI->glProgramParameteriEXT = (PFNGLPROGRAMPARAMETERIEXTPROC)glbGetProcAddress("glProgramParameteriEXT"); + pAPI->glProgramEnvParameters4fvEXT = (PFNGLPROGRAMENVPARAMETERS4FVEXTPROC)glbGetProcAddress("glProgramEnvParameters4fvEXT"); + pAPI->glProgramLocalParameters4fvEXT = (PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC)glbGetProcAddress("glProgramLocalParameters4fvEXT"); + pAPI->glGetUniformuivEXT = (PFNGLGETUNIFORMUIVEXTPROC)glbGetProcAddress("glGetUniformuivEXT"); + pAPI->glBindFragDataLocationEXT = (PFNGLBINDFRAGDATALOCATIONEXTPROC)glbGetProcAddress("glBindFragDataLocationEXT"); + pAPI->glGetFragDataLocationEXT = (PFNGLGETFRAGDATALOCATIONEXTPROC)glbGetProcAddress("glGetFragDataLocationEXT"); + pAPI->glUniform1uiEXT = (PFNGLUNIFORM1UIEXTPROC)glbGetProcAddress("glUniform1uiEXT"); + pAPI->glUniform2uiEXT = (PFNGLUNIFORM2UIEXTPROC)glbGetProcAddress("glUniform2uiEXT"); + pAPI->glUniform3uiEXT = (PFNGLUNIFORM3UIEXTPROC)glbGetProcAddress("glUniform3uiEXT"); + pAPI->glUniform4uiEXT = (PFNGLUNIFORM4UIEXTPROC)glbGetProcAddress("glUniform4uiEXT"); + pAPI->glUniform1uivEXT = (PFNGLUNIFORM1UIVEXTPROC)glbGetProcAddress("glUniform1uivEXT"); + pAPI->glUniform2uivEXT = (PFNGLUNIFORM2UIVEXTPROC)glbGetProcAddress("glUniform2uivEXT"); + pAPI->glUniform3uivEXT = (PFNGLUNIFORM3UIVEXTPROC)glbGetProcAddress("glUniform3uivEXT"); + pAPI->glUniform4uivEXT = (PFNGLUNIFORM4UIVEXTPROC)glbGetProcAddress("glUniform4uivEXT"); + pAPI->glGetHistogramEXT = (PFNGLGETHISTOGRAMEXTPROC)glbGetProcAddress("glGetHistogramEXT"); + pAPI->glGetHistogramParameterfvEXT = (PFNGLGETHISTOGRAMPARAMETERFVEXTPROC)glbGetProcAddress("glGetHistogramParameterfvEXT"); + pAPI->glGetHistogramParameterivEXT = (PFNGLGETHISTOGRAMPARAMETERIVEXTPROC)glbGetProcAddress("glGetHistogramParameterivEXT"); + pAPI->glGetMinmaxEXT = (PFNGLGETMINMAXEXTPROC)glbGetProcAddress("glGetMinmaxEXT"); + pAPI->glGetMinmaxParameterfvEXT = (PFNGLGETMINMAXPARAMETERFVEXTPROC)glbGetProcAddress("glGetMinmaxParameterfvEXT"); + pAPI->glGetMinmaxParameterivEXT = (PFNGLGETMINMAXPARAMETERIVEXTPROC)glbGetProcAddress("glGetMinmaxParameterivEXT"); + pAPI->glHistogramEXT = (PFNGLHISTOGRAMEXTPROC)glbGetProcAddress("glHistogramEXT"); + pAPI->glMinmaxEXT = (PFNGLMINMAXEXTPROC)glbGetProcAddress("glMinmaxEXT"); + pAPI->glResetHistogramEXT = (PFNGLRESETHISTOGRAMEXTPROC)glbGetProcAddress("glResetHistogramEXT"); + pAPI->glResetMinmaxEXT = (PFNGLRESETMINMAXEXTPROC)glbGetProcAddress("glResetMinmaxEXT"); + pAPI->glIndexFuncEXT = (PFNGLINDEXFUNCEXTPROC)glbGetProcAddress("glIndexFuncEXT"); + pAPI->glIndexMaterialEXT = (PFNGLINDEXMATERIALEXTPROC)glbGetProcAddress("glIndexMaterialEXT"); + pAPI->glApplyTextureEXT = (PFNGLAPPLYTEXTUREEXTPROC)glbGetProcAddress("glApplyTextureEXT"); + pAPI->glTextureLightEXT = (PFNGLTEXTURELIGHTEXTPROC)glbGetProcAddress("glTextureLightEXT"); + pAPI->glTextureMaterialEXT = (PFNGLTEXTUREMATERIALEXTPROC)glbGetProcAddress("glTextureMaterialEXT"); + pAPI->glGetUnsignedBytevEXT = (PFNGLGETUNSIGNEDBYTEVEXTPROC)glbGetProcAddress("glGetUnsignedBytevEXT"); + pAPI->glGetUnsignedBytei_vEXT = (PFNGLGETUNSIGNEDBYTEI_VEXTPROC)glbGetProcAddress("glGetUnsignedBytei_vEXT"); + pAPI->glDeleteMemoryObjectsEXT = (PFNGLDELETEMEMORYOBJECTSEXTPROC)glbGetProcAddress("glDeleteMemoryObjectsEXT"); + pAPI->glIsMemoryObjectEXT = (PFNGLISMEMORYOBJECTEXTPROC)glbGetProcAddress("glIsMemoryObjectEXT"); + pAPI->glCreateMemoryObjectsEXT = (PFNGLCREATEMEMORYOBJECTSEXTPROC)glbGetProcAddress("glCreateMemoryObjectsEXT"); + pAPI->glMemoryObjectParameterivEXT = (PFNGLMEMORYOBJECTPARAMETERIVEXTPROC)glbGetProcAddress("glMemoryObjectParameterivEXT"); + pAPI->glGetMemoryObjectParameterivEXT = (PFNGLGETMEMORYOBJECTPARAMETERIVEXTPROC)glbGetProcAddress("glGetMemoryObjectParameterivEXT"); + pAPI->glTexStorageMem2DEXT = (PFNGLTEXSTORAGEMEM2DEXTPROC)glbGetProcAddress("glTexStorageMem2DEXT"); + pAPI->glTexStorageMem2DMultisampleEXT = (PFNGLTEXSTORAGEMEM2DMULTISAMPLEEXTPROC)glbGetProcAddress("glTexStorageMem2DMultisampleEXT"); + pAPI->glTexStorageMem3DEXT = (PFNGLTEXSTORAGEMEM3DEXTPROC)glbGetProcAddress("glTexStorageMem3DEXT"); + pAPI->glTexStorageMem3DMultisampleEXT = (PFNGLTEXSTORAGEMEM3DMULTISAMPLEEXTPROC)glbGetProcAddress("glTexStorageMem3DMultisampleEXT"); + pAPI->glBufferStorageMemEXT = (PFNGLBUFFERSTORAGEMEMEXTPROC)glbGetProcAddress("glBufferStorageMemEXT"); + pAPI->glTextureStorageMem2DEXT = (PFNGLTEXTURESTORAGEMEM2DEXTPROC)glbGetProcAddress("glTextureStorageMem2DEXT"); + pAPI->glTextureStorageMem2DMultisampleEXT = (PFNGLTEXTURESTORAGEMEM2DMULTISAMPLEEXTPROC)glbGetProcAddress("glTextureStorageMem2DMultisampleEXT"); + pAPI->glTextureStorageMem3DEXT = (PFNGLTEXTURESTORAGEMEM3DEXTPROC)glbGetProcAddress("glTextureStorageMem3DEXT"); + pAPI->glTextureStorageMem3DMultisampleEXT = (PFNGLTEXTURESTORAGEMEM3DMULTISAMPLEEXTPROC)glbGetProcAddress("glTextureStorageMem3DMultisampleEXT"); + pAPI->glNamedBufferStorageMemEXT = (PFNGLNAMEDBUFFERSTORAGEMEMEXTPROC)glbGetProcAddress("glNamedBufferStorageMemEXT"); + pAPI->glTexStorageMem1DEXT = (PFNGLTEXSTORAGEMEM1DEXTPROC)glbGetProcAddress("glTexStorageMem1DEXT"); + pAPI->glTextureStorageMem1DEXT = (PFNGLTEXTURESTORAGEMEM1DEXTPROC)glbGetProcAddress("glTextureStorageMem1DEXT"); + pAPI->glImportMemoryFdEXT = (PFNGLIMPORTMEMORYFDEXTPROC)glbGetProcAddress("glImportMemoryFdEXT"); + pAPI->glImportMemoryWin32HandleEXT = (PFNGLIMPORTMEMORYWIN32HANDLEEXTPROC)glbGetProcAddress("glImportMemoryWin32HandleEXT"); + pAPI->glImportMemoryWin32NameEXT = (PFNGLIMPORTMEMORYWIN32NAMEEXTPROC)glbGetProcAddress("glImportMemoryWin32NameEXT"); + pAPI->glMultiDrawArraysEXT = (PFNGLMULTIDRAWARRAYSEXTPROC)glbGetProcAddress("glMultiDrawArraysEXT"); + pAPI->glMultiDrawElementsEXT = (PFNGLMULTIDRAWELEMENTSEXTPROC)glbGetProcAddress("glMultiDrawElementsEXT"); + pAPI->glSampleMaskEXT = (PFNGLSAMPLEMASKEXTPROC)glbGetProcAddress("glSampleMaskEXT"); + pAPI->glSamplePatternEXT = (PFNGLSAMPLEPATTERNEXTPROC)glbGetProcAddress("glSamplePatternEXT"); + pAPI->glColorTableEXT = (PFNGLCOLORTABLEEXTPROC)glbGetProcAddress("glColorTableEXT"); + pAPI->glGetColorTableEXT = (PFNGLGETCOLORTABLEEXTPROC)glbGetProcAddress("glGetColorTableEXT"); + pAPI->glGetColorTableParameterivEXT = (PFNGLGETCOLORTABLEPARAMETERIVEXTPROC)glbGetProcAddress("glGetColorTableParameterivEXT"); + pAPI->glGetColorTableParameterfvEXT = (PFNGLGETCOLORTABLEPARAMETERFVEXTPROC)glbGetProcAddress("glGetColorTableParameterfvEXT"); + pAPI->glPixelTransformParameteriEXT = (PFNGLPIXELTRANSFORMPARAMETERIEXTPROC)glbGetProcAddress("glPixelTransformParameteriEXT"); + pAPI->glPixelTransformParameterfEXT = (PFNGLPIXELTRANSFORMPARAMETERFEXTPROC)glbGetProcAddress("glPixelTransformParameterfEXT"); + pAPI->glPixelTransformParameterivEXT = (PFNGLPIXELTRANSFORMPARAMETERIVEXTPROC)glbGetProcAddress("glPixelTransformParameterivEXT"); + pAPI->glPixelTransformParameterfvEXT = (PFNGLPIXELTRANSFORMPARAMETERFVEXTPROC)glbGetProcAddress("glPixelTransformParameterfvEXT"); + pAPI->glGetPixelTransformParameterivEXT = (PFNGLGETPIXELTRANSFORMPARAMETERIVEXTPROC)glbGetProcAddress("glGetPixelTransformParameterivEXT"); + pAPI->glGetPixelTransformParameterfvEXT = (PFNGLGETPIXELTRANSFORMPARAMETERFVEXTPROC)glbGetProcAddress("glGetPixelTransformParameterfvEXT"); + pAPI->glPointParameterfEXT = (PFNGLPOINTPARAMETERFEXTPROC)glbGetProcAddress("glPointParameterfEXT"); + pAPI->glPointParameterfvEXT = (PFNGLPOINTPARAMETERFVEXTPROC)glbGetProcAddress("glPointParameterfvEXT"); + pAPI->glPolygonOffsetEXT = (PFNGLPOLYGONOFFSETEXTPROC)glbGetProcAddress("glPolygonOffsetEXT"); + pAPI->glPolygonOffsetClampEXT = (PFNGLPOLYGONOFFSETCLAMPEXTPROC)glbGetProcAddress("glPolygonOffsetClampEXT"); + pAPI->glProvokingVertexEXT = (PFNGLPROVOKINGVERTEXEXTPROC)glbGetProcAddress("glProvokingVertexEXT"); + pAPI->glRasterSamplesEXT = (PFNGLRASTERSAMPLESEXTPROC)glbGetProcAddress("glRasterSamplesEXT"); + pAPI->glGenSemaphoresEXT = (PFNGLGENSEMAPHORESEXTPROC)glbGetProcAddress("glGenSemaphoresEXT"); + pAPI->glDeleteSemaphoresEXT = (PFNGLDELETESEMAPHORESEXTPROC)glbGetProcAddress("glDeleteSemaphoresEXT"); + pAPI->glIsSemaphoreEXT = (PFNGLISSEMAPHOREEXTPROC)glbGetProcAddress("glIsSemaphoreEXT"); + pAPI->glSemaphoreParameterui64vEXT = (PFNGLSEMAPHOREPARAMETERUI64VEXTPROC)glbGetProcAddress("glSemaphoreParameterui64vEXT"); + pAPI->glGetSemaphoreParameterui64vEXT = (PFNGLGETSEMAPHOREPARAMETERUI64VEXTPROC)glbGetProcAddress("glGetSemaphoreParameterui64vEXT"); + pAPI->glWaitSemaphoreEXT = (PFNGLWAITSEMAPHOREEXTPROC)glbGetProcAddress("glWaitSemaphoreEXT"); + pAPI->glSignalSemaphoreEXT = (PFNGLSIGNALSEMAPHOREEXTPROC)glbGetProcAddress("glSignalSemaphoreEXT"); + pAPI->glImportSemaphoreFdEXT = (PFNGLIMPORTSEMAPHOREFDEXTPROC)glbGetProcAddress("glImportSemaphoreFdEXT"); + pAPI->glImportSemaphoreWin32HandleEXT = (PFNGLIMPORTSEMAPHOREWIN32HANDLEEXTPROC)glbGetProcAddress("glImportSemaphoreWin32HandleEXT"); + pAPI->glImportSemaphoreWin32NameEXT = (PFNGLIMPORTSEMAPHOREWIN32NAMEEXTPROC)glbGetProcAddress("glImportSemaphoreWin32NameEXT"); + pAPI->glSecondaryColor3bEXT = (PFNGLSECONDARYCOLOR3BEXTPROC)glbGetProcAddress("glSecondaryColor3bEXT"); + pAPI->glSecondaryColor3bvEXT = (PFNGLSECONDARYCOLOR3BVEXTPROC)glbGetProcAddress("glSecondaryColor3bvEXT"); + pAPI->glSecondaryColor3dEXT = (PFNGLSECONDARYCOLOR3DEXTPROC)glbGetProcAddress("glSecondaryColor3dEXT"); + pAPI->glSecondaryColor3dvEXT = (PFNGLSECONDARYCOLOR3DVEXTPROC)glbGetProcAddress("glSecondaryColor3dvEXT"); + pAPI->glSecondaryColor3fEXT = (PFNGLSECONDARYCOLOR3FEXTPROC)glbGetProcAddress("glSecondaryColor3fEXT"); + pAPI->glSecondaryColor3fvEXT = (PFNGLSECONDARYCOLOR3FVEXTPROC)glbGetProcAddress("glSecondaryColor3fvEXT"); + pAPI->glSecondaryColor3iEXT = (PFNGLSECONDARYCOLOR3IEXTPROC)glbGetProcAddress("glSecondaryColor3iEXT"); + pAPI->glSecondaryColor3ivEXT = (PFNGLSECONDARYCOLOR3IVEXTPROC)glbGetProcAddress("glSecondaryColor3ivEXT"); + pAPI->glSecondaryColor3sEXT = (PFNGLSECONDARYCOLOR3SEXTPROC)glbGetProcAddress("glSecondaryColor3sEXT"); + pAPI->glSecondaryColor3svEXT = (PFNGLSECONDARYCOLOR3SVEXTPROC)glbGetProcAddress("glSecondaryColor3svEXT"); + pAPI->glSecondaryColor3ubEXT = (PFNGLSECONDARYCOLOR3UBEXTPROC)glbGetProcAddress("glSecondaryColor3ubEXT"); + pAPI->glSecondaryColor3ubvEXT = (PFNGLSECONDARYCOLOR3UBVEXTPROC)glbGetProcAddress("glSecondaryColor3ubvEXT"); + pAPI->glSecondaryColor3uiEXT = (PFNGLSECONDARYCOLOR3UIEXTPROC)glbGetProcAddress("glSecondaryColor3uiEXT"); + pAPI->glSecondaryColor3uivEXT = (PFNGLSECONDARYCOLOR3UIVEXTPROC)glbGetProcAddress("glSecondaryColor3uivEXT"); + pAPI->glSecondaryColor3usEXT = (PFNGLSECONDARYCOLOR3USEXTPROC)glbGetProcAddress("glSecondaryColor3usEXT"); + pAPI->glSecondaryColor3usvEXT = (PFNGLSECONDARYCOLOR3USVEXTPROC)glbGetProcAddress("glSecondaryColor3usvEXT"); + pAPI->glSecondaryColorPointerEXT = (PFNGLSECONDARYCOLORPOINTEREXTPROC)glbGetProcAddress("glSecondaryColorPointerEXT"); + pAPI->glUseShaderProgramEXT = (PFNGLUSESHADERPROGRAMEXTPROC)glbGetProcAddress("glUseShaderProgramEXT"); + pAPI->glActiveProgramEXT = (PFNGLACTIVEPROGRAMEXTPROC)glbGetProcAddress("glActiveProgramEXT"); + pAPI->glCreateShaderProgramEXT = (PFNGLCREATESHADERPROGRAMEXTPROC)glbGetProcAddress("glCreateShaderProgramEXT"); + pAPI->glActiveShaderProgramEXT = (PFNGLACTIVESHADERPROGRAMEXTPROC)glbGetProcAddress("glActiveShaderProgramEXT"); + pAPI->glBindProgramPipelineEXT = (PFNGLBINDPROGRAMPIPELINEEXTPROC)glbGetProcAddress("glBindProgramPipelineEXT"); + pAPI->glCreateShaderProgramvEXT = (PFNGLCREATESHADERPROGRAMVEXTPROC)glbGetProcAddress("glCreateShaderProgramvEXT"); + pAPI->glDeleteProgramPipelinesEXT = (PFNGLDELETEPROGRAMPIPELINESEXTPROC)glbGetProcAddress("glDeleteProgramPipelinesEXT"); + pAPI->glGenProgramPipelinesEXT = (PFNGLGENPROGRAMPIPELINESEXTPROC)glbGetProcAddress("glGenProgramPipelinesEXT"); + pAPI->glGetProgramPipelineInfoLogEXT = (PFNGLGETPROGRAMPIPELINEINFOLOGEXTPROC)glbGetProcAddress("glGetProgramPipelineInfoLogEXT"); + pAPI->glGetProgramPipelineivEXT = (PFNGLGETPROGRAMPIPELINEIVEXTPROC)glbGetProcAddress("glGetProgramPipelineivEXT"); + pAPI->glIsProgramPipelineEXT = (PFNGLISPROGRAMPIPELINEEXTPROC)glbGetProcAddress("glIsProgramPipelineEXT"); + pAPI->glUseProgramStagesEXT = (PFNGLUSEPROGRAMSTAGESEXTPROC)glbGetProcAddress("glUseProgramStagesEXT"); + pAPI->glValidateProgramPipelineEXT = (PFNGLVALIDATEPROGRAMPIPELINEEXTPROC)glbGetProcAddress("glValidateProgramPipelineEXT"); + pAPI->glFramebufferFetchBarrierEXT = (PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC)glbGetProcAddress("glFramebufferFetchBarrierEXT"); + pAPI->glBindImageTextureEXT = (PFNGLBINDIMAGETEXTUREEXTPROC)glbGetProcAddress("glBindImageTextureEXT"); + pAPI->glMemoryBarrierEXT = (PFNGLMEMORYBARRIEREXTPROC)glbGetProcAddress("glMemoryBarrierEXT"); + pAPI->glStencilClearTagEXT = (PFNGLSTENCILCLEARTAGEXTPROC)glbGetProcAddress("glStencilClearTagEXT"); + pAPI->glActiveStencilFaceEXT = (PFNGLACTIVESTENCILFACEEXTPROC)glbGetProcAddress("glActiveStencilFaceEXT"); + pAPI->glTexSubImage1DEXT = (PFNGLTEXSUBIMAGE1DEXTPROC)glbGetProcAddress("glTexSubImage1DEXT"); + pAPI->glTexSubImage2DEXT = (PFNGLTEXSUBIMAGE2DEXTPROC)glbGetProcAddress("glTexSubImage2DEXT"); + pAPI->glTexImage3DEXT = (PFNGLTEXIMAGE3DEXTPROC)glbGetProcAddress("glTexImage3DEXT"); + pAPI->glTexSubImage3DEXT = (PFNGLTEXSUBIMAGE3DEXTPROC)glbGetProcAddress("glTexSubImage3DEXT"); + pAPI->glFramebufferTextureLayerEXT = (PFNGLFRAMEBUFFERTEXTURELAYEREXTPROC)glbGetProcAddress("glFramebufferTextureLayerEXT"); + pAPI->glTexBufferEXT = (PFNGLTEXBUFFEREXTPROC)glbGetProcAddress("glTexBufferEXT"); + pAPI->glTexParameterIivEXT = (PFNGLTEXPARAMETERIIVEXTPROC)glbGetProcAddress("glTexParameterIivEXT"); + pAPI->glTexParameterIuivEXT = (PFNGLTEXPARAMETERIUIVEXTPROC)glbGetProcAddress("glTexParameterIuivEXT"); + pAPI->glGetTexParameterIivEXT = (PFNGLGETTEXPARAMETERIIVEXTPROC)glbGetProcAddress("glGetTexParameterIivEXT"); + pAPI->glGetTexParameterIuivEXT = (PFNGLGETTEXPARAMETERIUIVEXTPROC)glbGetProcAddress("glGetTexParameterIuivEXT"); + pAPI->glClearColorIiEXT = (PFNGLCLEARCOLORIIEXTPROC)glbGetProcAddress("glClearColorIiEXT"); + pAPI->glClearColorIuiEXT = (PFNGLCLEARCOLORIUIEXTPROC)glbGetProcAddress("glClearColorIuiEXT"); + pAPI->glAreTexturesResidentEXT = (PFNGLARETEXTURESRESIDENTEXTPROC)glbGetProcAddress("glAreTexturesResidentEXT"); + pAPI->glBindTextureEXT = (PFNGLBINDTEXTUREEXTPROC)glbGetProcAddress("glBindTextureEXT"); + pAPI->glDeleteTexturesEXT = (PFNGLDELETETEXTURESEXTPROC)glbGetProcAddress("glDeleteTexturesEXT"); + pAPI->glGenTexturesEXT = (PFNGLGENTEXTURESEXTPROC)glbGetProcAddress("glGenTexturesEXT"); + pAPI->glIsTextureEXT = (PFNGLISTEXTUREEXTPROC)glbGetProcAddress("glIsTextureEXT"); + pAPI->glPrioritizeTexturesEXT = (PFNGLPRIORITIZETEXTURESEXTPROC)glbGetProcAddress("glPrioritizeTexturesEXT"); + pAPI->glTextureNormalEXT = (PFNGLTEXTURENORMALEXTPROC)glbGetProcAddress("glTextureNormalEXT"); + pAPI->glCreateSemaphoresNV = (PFNGLCREATESEMAPHORESNVPROC)glbGetProcAddress("glCreateSemaphoresNV"); + pAPI->glSemaphoreParameterivNV = (PFNGLSEMAPHOREPARAMETERIVNVPROC)glbGetProcAddress("glSemaphoreParameterivNV"); + pAPI->glGetSemaphoreParameterivNV = (PFNGLGETSEMAPHOREPARAMETERIVNVPROC)glbGetProcAddress("glGetSemaphoreParameterivNV"); + pAPI->glGetQueryObjecti64vEXT = (PFNGLGETQUERYOBJECTI64VEXTPROC)glbGetProcAddress("glGetQueryObjecti64vEXT"); + pAPI->glGetQueryObjectui64vEXT = (PFNGLGETQUERYOBJECTUI64VEXTPROC)glbGetProcAddress("glGetQueryObjectui64vEXT"); + pAPI->glBeginTransformFeedbackEXT = (PFNGLBEGINTRANSFORMFEEDBACKEXTPROC)glbGetProcAddress("glBeginTransformFeedbackEXT"); + pAPI->glEndTransformFeedbackEXT = (PFNGLENDTRANSFORMFEEDBACKEXTPROC)glbGetProcAddress("glEndTransformFeedbackEXT"); + pAPI->glBindBufferRangeEXT = (PFNGLBINDBUFFERRANGEEXTPROC)glbGetProcAddress("glBindBufferRangeEXT"); + pAPI->glBindBufferOffsetEXT = (PFNGLBINDBUFFEROFFSETEXTPROC)glbGetProcAddress("glBindBufferOffsetEXT"); + pAPI->glBindBufferBaseEXT = (PFNGLBINDBUFFERBASEEXTPROC)glbGetProcAddress("glBindBufferBaseEXT"); + pAPI->glTransformFeedbackVaryingsEXT = (PFNGLTRANSFORMFEEDBACKVARYINGSEXTPROC)glbGetProcAddress("glTransformFeedbackVaryingsEXT"); + pAPI->glGetTransformFeedbackVaryingEXT = (PFNGLGETTRANSFORMFEEDBACKVARYINGEXTPROC)glbGetProcAddress("glGetTransformFeedbackVaryingEXT"); + pAPI->glArrayElementEXT = (PFNGLARRAYELEMENTEXTPROC)glbGetProcAddress("glArrayElementEXT"); + pAPI->glColorPointerEXT = (PFNGLCOLORPOINTEREXTPROC)glbGetProcAddress("glColorPointerEXT"); + pAPI->glDrawArraysEXT = (PFNGLDRAWARRAYSEXTPROC)glbGetProcAddress("glDrawArraysEXT"); + pAPI->glEdgeFlagPointerEXT = (PFNGLEDGEFLAGPOINTEREXTPROC)glbGetProcAddress("glEdgeFlagPointerEXT"); + pAPI->glGetPointervEXT = (PFNGLGETPOINTERVEXTPROC)glbGetProcAddress("glGetPointervEXT"); + pAPI->glIndexPointerEXT = (PFNGLINDEXPOINTEREXTPROC)glbGetProcAddress("glIndexPointerEXT"); + pAPI->glNormalPointerEXT = (PFNGLNORMALPOINTEREXTPROC)glbGetProcAddress("glNormalPointerEXT"); + pAPI->glTexCoordPointerEXT = (PFNGLTEXCOORDPOINTEREXTPROC)glbGetProcAddress("glTexCoordPointerEXT"); + pAPI->glVertexPointerEXT = (PFNGLVERTEXPOINTEREXTPROC)glbGetProcAddress("glVertexPointerEXT"); + pAPI->glVertexAttribL1dEXT = (PFNGLVERTEXATTRIBL1DEXTPROC)glbGetProcAddress("glVertexAttribL1dEXT"); + pAPI->glVertexAttribL2dEXT = (PFNGLVERTEXATTRIBL2DEXTPROC)glbGetProcAddress("glVertexAttribL2dEXT"); + pAPI->glVertexAttribL3dEXT = (PFNGLVERTEXATTRIBL3DEXTPROC)glbGetProcAddress("glVertexAttribL3dEXT"); + pAPI->glVertexAttribL4dEXT = (PFNGLVERTEXATTRIBL4DEXTPROC)glbGetProcAddress("glVertexAttribL4dEXT"); + pAPI->glVertexAttribL1dvEXT = (PFNGLVERTEXATTRIBL1DVEXTPROC)glbGetProcAddress("glVertexAttribL1dvEXT"); + pAPI->glVertexAttribL2dvEXT = (PFNGLVERTEXATTRIBL2DVEXTPROC)glbGetProcAddress("glVertexAttribL2dvEXT"); + pAPI->glVertexAttribL3dvEXT = (PFNGLVERTEXATTRIBL3DVEXTPROC)glbGetProcAddress("glVertexAttribL3dvEXT"); + pAPI->glVertexAttribL4dvEXT = (PFNGLVERTEXATTRIBL4DVEXTPROC)glbGetProcAddress("glVertexAttribL4dvEXT"); + pAPI->glVertexAttribLPointerEXT = (PFNGLVERTEXATTRIBLPOINTEREXTPROC)glbGetProcAddress("glVertexAttribLPointerEXT"); + pAPI->glGetVertexAttribLdvEXT = (PFNGLGETVERTEXATTRIBLDVEXTPROC)glbGetProcAddress("glGetVertexAttribLdvEXT"); + pAPI->glBeginVertexShaderEXT = (PFNGLBEGINVERTEXSHADEREXTPROC)glbGetProcAddress("glBeginVertexShaderEXT"); + pAPI->glEndVertexShaderEXT = (PFNGLENDVERTEXSHADEREXTPROC)glbGetProcAddress("glEndVertexShaderEXT"); + pAPI->glBindVertexShaderEXT = (PFNGLBINDVERTEXSHADEREXTPROC)glbGetProcAddress("glBindVertexShaderEXT"); + pAPI->glGenVertexShadersEXT = (PFNGLGENVERTEXSHADERSEXTPROC)glbGetProcAddress("glGenVertexShadersEXT"); + pAPI->glDeleteVertexShaderEXT = (PFNGLDELETEVERTEXSHADEREXTPROC)glbGetProcAddress("glDeleteVertexShaderEXT"); + pAPI->glShaderOp1EXT = (PFNGLSHADEROP1EXTPROC)glbGetProcAddress("glShaderOp1EXT"); + pAPI->glShaderOp2EXT = (PFNGLSHADEROP2EXTPROC)glbGetProcAddress("glShaderOp2EXT"); + pAPI->glShaderOp3EXT = (PFNGLSHADEROP3EXTPROC)glbGetProcAddress("glShaderOp3EXT"); + pAPI->glSwizzleEXT = (PFNGLSWIZZLEEXTPROC)glbGetProcAddress("glSwizzleEXT"); + pAPI->glWriteMaskEXT = (PFNGLWRITEMASKEXTPROC)glbGetProcAddress("glWriteMaskEXT"); + pAPI->glInsertComponentEXT = (PFNGLINSERTCOMPONENTEXTPROC)glbGetProcAddress("glInsertComponentEXT"); + pAPI->glExtractComponentEXT = (PFNGLEXTRACTCOMPONENTEXTPROC)glbGetProcAddress("glExtractComponentEXT"); + pAPI->glGenSymbolsEXT = (PFNGLGENSYMBOLSEXTPROC)glbGetProcAddress("glGenSymbolsEXT"); + pAPI->glSetInvariantEXT = (PFNGLSETINVARIANTEXTPROC)glbGetProcAddress("glSetInvariantEXT"); + pAPI->glSetLocalConstantEXT = (PFNGLSETLOCALCONSTANTEXTPROC)glbGetProcAddress("glSetLocalConstantEXT"); + pAPI->glVariantbvEXT = (PFNGLVARIANTBVEXTPROC)glbGetProcAddress("glVariantbvEXT"); + pAPI->glVariantsvEXT = (PFNGLVARIANTSVEXTPROC)glbGetProcAddress("glVariantsvEXT"); + pAPI->glVariantivEXT = (PFNGLVARIANTIVEXTPROC)glbGetProcAddress("glVariantivEXT"); + pAPI->glVariantfvEXT = (PFNGLVARIANTFVEXTPROC)glbGetProcAddress("glVariantfvEXT"); + pAPI->glVariantdvEXT = (PFNGLVARIANTDVEXTPROC)glbGetProcAddress("glVariantdvEXT"); + pAPI->glVariantubvEXT = (PFNGLVARIANTUBVEXTPROC)glbGetProcAddress("glVariantubvEXT"); + pAPI->glVariantusvEXT = (PFNGLVARIANTUSVEXTPROC)glbGetProcAddress("glVariantusvEXT"); + pAPI->glVariantuivEXT = (PFNGLVARIANTUIVEXTPROC)glbGetProcAddress("glVariantuivEXT"); + pAPI->glVariantPointerEXT = (PFNGLVARIANTPOINTEREXTPROC)glbGetProcAddress("glVariantPointerEXT"); + pAPI->glEnableVariantClientStateEXT = (PFNGLENABLEVARIANTCLIENTSTATEEXTPROC)glbGetProcAddress("glEnableVariantClientStateEXT"); + pAPI->glDisableVariantClientStateEXT = (PFNGLDISABLEVARIANTCLIENTSTATEEXTPROC)glbGetProcAddress("glDisableVariantClientStateEXT"); + pAPI->glBindLightParameterEXT = (PFNGLBINDLIGHTPARAMETEREXTPROC)glbGetProcAddress("glBindLightParameterEXT"); + pAPI->glBindMaterialParameterEXT = (PFNGLBINDMATERIALPARAMETEREXTPROC)glbGetProcAddress("glBindMaterialParameterEXT"); + pAPI->glBindTexGenParameterEXT = (PFNGLBINDTEXGENPARAMETEREXTPROC)glbGetProcAddress("glBindTexGenParameterEXT"); + pAPI->glBindTextureUnitParameterEXT = (PFNGLBINDTEXTUREUNITPARAMETEREXTPROC)glbGetProcAddress("glBindTextureUnitParameterEXT"); + pAPI->glBindParameterEXT = (PFNGLBINDPARAMETEREXTPROC)glbGetProcAddress("glBindParameterEXT"); + pAPI->glIsVariantEnabledEXT = (PFNGLISVARIANTENABLEDEXTPROC)glbGetProcAddress("glIsVariantEnabledEXT"); + pAPI->glGetVariantBooleanvEXT = (PFNGLGETVARIANTBOOLEANVEXTPROC)glbGetProcAddress("glGetVariantBooleanvEXT"); + pAPI->glGetVariantIntegervEXT = (PFNGLGETVARIANTINTEGERVEXTPROC)glbGetProcAddress("glGetVariantIntegervEXT"); + pAPI->glGetVariantFloatvEXT = (PFNGLGETVARIANTFLOATVEXTPROC)glbGetProcAddress("glGetVariantFloatvEXT"); + pAPI->glGetVariantPointervEXT = (PFNGLGETVARIANTPOINTERVEXTPROC)glbGetProcAddress("glGetVariantPointervEXT"); + pAPI->glGetInvariantBooleanvEXT = (PFNGLGETINVARIANTBOOLEANVEXTPROC)glbGetProcAddress("glGetInvariantBooleanvEXT"); + pAPI->glGetInvariantIntegervEXT = (PFNGLGETINVARIANTINTEGERVEXTPROC)glbGetProcAddress("glGetInvariantIntegervEXT"); + pAPI->glGetInvariantFloatvEXT = (PFNGLGETINVARIANTFLOATVEXTPROC)glbGetProcAddress("glGetInvariantFloatvEXT"); + pAPI->glGetLocalConstantBooleanvEXT = (PFNGLGETLOCALCONSTANTBOOLEANVEXTPROC)glbGetProcAddress("glGetLocalConstantBooleanvEXT"); + pAPI->glGetLocalConstantIntegervEXT = (PFNGLGETLOCALCONSTANTINTEGERVEXTPROC)glbGetProcAddress("glGetLocalConstantIntegervEXT"); + pAPI->glGetLocalConstantFloatvEXT = (PFNGLGETLOCALCONSTANTFLOATVEXTPROC)glbGetProcAddress("glGetLocalConstantFloatvEXT"); + pAPI->glVertexWeightfEXT = (PFNGLVERTEXWEIGHTFEXTPROC)glbGetProcAddress("glVertexWeightfEXT"); + pAPI->glVertexWeightfvEXT = (PFNGLVERTEXWEIGHTFVEXTPROC)glbGetProcAddress("glVertexWeightfvEXT"); + pAPI->glVertexWeightPointerEXT = (PFNGLVERTEXWEIGHTPOINTEREXTPROC)glbGetProcAddress("glVertexWeightPointerEXT"); + pAPI->glAcquireKeyedMutexWin32EXT = (PFNGLACQUIREKEYEDMUTEXWIN32EXTPROC)glbGetProcAddress("glAcquireKeyedMutexWin32EXT"); + pAPI->glReleaseKeyedMutexWin32EXT = (PFNGLRELEASEKEYEDMUTEXWIN32EXTPROC)glbGetProcAddress("glReleaseKeyedMutexWin32EXT"); + pAPI->glWindowRectanglesEXT = (PFNGLWINDOWRECTANGLESEXTPROC)glbGetProcAddress("glWindowRectanglesEXT"); + pAPI->glImportSyncEXT = (PFNGLIMPORTSYNCEXTPROC)glbGetProcAddress("glImportSyncEXT"); + pAPI->glFrameTerminatorGREMEDY = (PFNGLFRAMETERMINATORGREMEDYPROC)glbGetProcAddress("glFrameTerminatorGREMEDY"); + pAPI->glStringMarkerGREMEDY = (PFNGLSTRINGMARKERGREMEDYPROC)glbGetProcAddress("glStringMarkerGREMEDY"); + pAPI->glImageTransformParameteriHP = (PFNGLIMAGETRANSFORMPARAMETERIHPPROC)glbGetProcAddress("glImageTransformParameteriHP"); + pAPI->glImageTransformParameterfHP = (PFNGLIMAGETRANSFORMPARAMETERFHPPROC)glbGetProcAddress("glImageTransformParameterfHP"); + pAPI->glImageTransformParameterivHP = (PFNGLIMAGETRANSFORMPARAMETERIVHPPROC)glbGetProcAddress("glImageTransformParameterivHP"); + pAPI->glImageTransformParameterfvHP = (PFNGLIMAGETRANSFORMPARAMETERFVHPPROC)glbGetProcAddress("glImageTransformParameterfvHP"); + pAPI->glGetImageTransformParameterivHP = (PFNGLGETIMAGETRANSFORMPARAMETERIVHPPROC)glbGetProcAddress("glGetImageTransformParameterivHP"); + pAPI->glGetImageTransformParameterfvHP = (PFNGLGETIMAGETRANSFORMPARAMETERFVHPPROC)glbGetProcAddress("glGetImageTransformParameterfvHP"); + pAPI->glMultiModeDrawArraysIBM = (PFNGLMULTIMODEDRAWARRAYSIBMPROC)glbGetProcAddress("glMultiModeDrawArraysIBM"); + pAPI->glMultiModeDrawElementsIBM = (PFNGLMULTIMODEDRAWELEMENTSIBMPROC)glbGetProcAddress("glMultiModeDrawElementsIBM"); + pAPI->glFlushStaticDataIBM = (PFNGLFLUSHSTATICDATAIBMPROC)glbGetProcAddress("glFlushStaticDataIBM"); + pAPI->glColorPointerListIBM = (PFNGLCOLORPOINTERLISTIBMPROC)glbGetProcAddress("glColorPointerListIBM"); + pAPI->glSecondaryColorPointerListIBM = (PFNGLSECONDARYCOLORPOINTERLISTIBMPROC)glbGetProcAddress("glSecondaryColorPointerListIBM"); + pAPI->glEdgeFlagPointerListIBM = (PFNGLEDGEFLAGPOINTERLISTIBMPROC)glbGetProcAddress("glEdgeFlagPointerListIBM"); + pAPI->glFogCoordPointerListIBM = (PFNGLFOGCOORDPOINTERLISTIBMPROC)glbGetProcAddress("glFogCoordPointerListIBM"); + pAPI->glIndexPointerListIBM = (PFNGLINDEXPOINTERLISTIBMPROC)glbGetProcAddress("glIndexPointerListIBM"); + pAPI->glNormalPointerListIBM = (PFNGLNORMALPOINTERLISTIBMPROC)glbGetProcAddress("glNormalPointerListIBM"); + pAPI->glTexCoordPointerListIBM = (PFNGLTEXCOORDPOINTERLISTIBMPROC)glbGetProcAddress("glTexCoordPointerListIBM"); + pAPI->glVertexPointerListIBM = (PFNGLVERTEXPOINTERLISTIBMPROC)glbGetProcAddress("glVertexPointerListIBM"); + pAPI->glBlendFuncSeparateINGR = (PFNGLBLENDFUNCSEPARATEINGRPROC)glbGetProcAddress("glBlendFuncSeparateINGR"); + pAPI->glApplyFramebufferAttachmentCMAAINTEL = (PFNGLAPPLYFRAMEBUFFERATTACHMENTCMAAINTELPROC)glbGetProcAddress("glApplyFramebufferAttachmentCMAAINTEL"); + pAPI->glSyncTextureINTEL = (PFNGLSYNCTEXTUREINTELPROC)glbGetProcAddress("glSyncTextureINTEL"); + pAPI->glUnmapTexture2DINTEL = (PFNGLUNMAPTEXTURE2DINTELPROC)glbGetProcAddress("glUnmapTexture2DINTEL"); + pAPI->glMapTexture2DINTEL = (PFNGLMAPTEXTURE2DINTELPROC)glbGetProcAddress("glMapTexture2DINTEL"); + pAPI->glVertexPointervINTEL = (PFNGLVERTEXPOINTERVINTELPROC)glbGetProcAddress("glVertexPointervINTEL"); + pAPI->glNormalPointervINTEL = (PFNGLNORMALPOINTERVINTELPROC)glbGetProcAddress("glNormalPointervINTEL"); + pAPI->glColorPointervINTEL = (PFNGLCOLORPOINTERVINTELPROC)glbGetProcAddress("glColorPointervINTEL"); + pAPI->glTexCoordPointervINTEL = (PFNGLTEXCOORDPOINTERVINTELPROC)glbGetProcAddress("glTexCoordPointervINTEL"); + pAPI->glBeginPerfQueryINTEL = (PFNGLBEGINPERFQUERYINTELPROC)glbGetProcAddress("glBeginPerfQueryINTEL"); + pAPI->glCreatePerfQueryINTEL = (PFNGLCREATEPERFQUERYINTELPROC)glbGetProcAddress("glCreatePerfQueryINTEL"); + pAPI->glDeletePerfQueryINTEL = (PFNGLDELETEPERFQUERYINTELPROC)glbGetProcAddress("glDeletePerfQueryINTEL"); + pAPI->glEndPerfQueryINTEL = (PFNGLENDPERFQUERYINTELPROC)glbGetProcAddress("glEndPerfQueryINTEL"); + pAPI->glGetFirstPerfQueryIdINTEL = (PFNGLGETFIRSTPERFQUERYIDINTELPROC)glbGetProcAddress("glGetFirstPerfQueryIdINTEL"); + pAPI->glGetNextPerfQueryIdINTEL = (PFNGLGETNEXTPERFQUERYIDINTELPROC)glbGetProcAddress("glGetNextPerfQueryIdINTEL"); + pAPI->glGetPerfCounterInfoINTEL = (PFNGLGETPERFCOUNTERINFOINTELPROC)glbGetProcAddress("glGetPerfCounterInfoINTEL"); + pAPI->glGetPerfQueryDataINTEL = (PFNGLGETPERFQUERYDATAINTELPROC)glbGetProcAddress("glGetPerfQueryDataINTEL"); + pAPI->glGetPerfQueryIdByNameINTEL = (PFNGLGETPERFQUERYIDBYNAMEINTELPROC)glbGetProcAddress("glGetPerfQueryIdByNameINTEL"); + pAPI->glGetPerfQueryInfoINTEL = (PFNGLGETPERFQUERYINFOINTELPROC)glbGetProcAddress("glGetPerfQueryInfoINTEL"); + pAPI->glBlendBarrierKHR = (PFNGLBLENDBARRIERKHRPROC)glbGetProcAddress("glBlendBarrierKHR"); + pAPI->glDebugMessageControlKHR = (PFNGLDEBUGMESSAGECONTROLKHRPROC)glbGetProcAddress("glDebugMessageControlKHR"); + pAPI->glDebugMessageInsertKHR = (PFNGLDEBUGMESSAGEINSERTKHRPROC)glbGetProcAddress("glDebugMessageInsertKHR"); + pAPI->glDebugMessageCallbackKHR = (PFNGLDEBUGMESSAGECALLBACKKHRPROC)glbGetProcAddress("glDebugMessageCallbackKHR"); + pAPI->glGetDebugMessageLogKHR = (PFNGLGETDEBUGMESSAGELOGKHRPROC)glbGetProcAddress("glGetDebugMessageLogKHR"); + pAPI->glPushDebugGroupKHR = (PFNGLPUSHDEBUGGROUPKHRPROC)glbGetProcAddress("glPushDebugGroupKHR"); + pAPI->glPopDebugGroupKHR = (PFNGLPOPDEBUGGROUPKHRPROC)glbGetProcAddress("glPopDebugGroupKHR"); + pAPI->glObjectLabelKHR = (PFNGLOBJECTLABELKHRPROC)glbGetProcAddress("glObjectLabelKHR"); + pAPI->glGetObjectLabelKHR = (PFNGLGETOBJECTLABELKHRPROC)glbGetProcAddress("glGetObjectLabelKHR"); + pAPI->glObjectPtrLabelKHR = (PFNGLOBJECTPTRLABELKHRPROC)glbGetProcAddress("glObjectPtrLabelKHR"); + pAPI->glGetObjectPtrLabelKHR = (PFNGLGETOBJECTPTRLABELKHRPROC)glbGetProcAddress("glGetObjectPtrLabelKHR"); + pAPI->glGetPointervKHR = (PFNGLGETPOINTERVKHRPROC)glbGetProcAddress("glGetPointervKHR"); + pAPI->glGetGraphicsResetStatusKHR = (PFNGLGETGRAPHICSRESETSTATUSKHRPROC)glbGetProcAddress("glGetGraphicsResetStatusKHR"); + pAPI->glReadnPixelsKHR = (PFNGLREADNPIXELSKHRPROC)glbGetProcAddress("glReadnPixelsKHR"); + pAPI->glGetnUniformfvKHR = (PFNGLGETNUNIFORMFVKHRPROC)glbGetProcAddress("glGetnUniformfvKHR"); + pAPI->glGetnUniformivKHR = (PFNGLGETNUNIFORMIVKHRPROC)glbGetProcAddress("glGetnUniformivKHR"); + pAPI->glGetnUniformuivKHR = (PFNGLGETNUNIFORMUIVKHRPROC)glbGetProcAddress("glGetnUniformuivKHR"); + pAPI->glMaxShaderCompilerThreadsKHR = (PFNGLMAXSHADERCOMPILERTHREADSKHRPROC)glbGetProcAddress("glMaxShaderCompilerThreadsKHR"); + pAPI->glFramebufferParameteriMESA = (PFNGLFRAMEBUFFERPARAMETERIMESAPROC)glbGetProcAddress("glFramebufferParameteriMESA"); + pAPI->glGetFramebufferParameterivMESA = (PFNGLGETFRAMEBUFFERPARAMETERIVMESAPROC)glbGetProcAddress("glGetFramebufferParameterivMESA"); + pAPI->glResizeBuffersMESA = (PFNGLRESIZEBUFFERSMESAPROC)glbGetProcAddress("glResizeBuffersMESA"); + pAPI->glWindowPos2dMESA = (PFNGLWINDOWPOS2DMESAPROC)glbGetProcAddress("glWindowPos2dMESA"); + pAPI->glWindowPos2dvMESA = (PFNGLWINDOWPOS2DVMESAPROC)glbGetProcAddress("glWindowPos2dvMESA"); + pAPI->glWindowPos2fMESA = (PFNGLWINDOWPOS2FMESAPROC)glbGetProcAddress("glWindowPos2fMESA"); + pAPI->glWindowPos2fvMESA = (PFNGLWINDOWPOS2FVMESAPROC)glbGetProcAddress("glWindowPos2fvMESA"); + pAPI->glWindowPos2iMESA = (PFNGLWINDOWPOS2IMESAPROC)glbGetProcAddress("glWindowPos2iMESA"); + pAPI->glWindowPos2ivMESA = (PFNGLWINDOWPOS2IVMESAPROC)glbGetProcAddress("glWindowPos2ivMESA"); + pAPI->glWindowPos2sMESA = (PFNGLWINDOWPOS2SMESAPROC)glbGetProcAddress("glWindowPos2sMESA"); + pAPI->glWindowPos2svMESA = (PFNGLWINDOWPOS2SVMESAPROC)glbGetProcAddress("glWindowPos2svMESA"); + pAPI->glWindowPos3dMESA = (PFNGLWINDOWPOS3DMESAPROC)glbGetProcAddress("glWindowPos3dMESA"); + pAPI->glWindowPos3dvMESA = (PFNGLWINDOWPOS3DVMESAPROC)glbGetProcAddress("glWindowPos3dvMESA"); + pAPI->glWindowPos3fMESA = (PFNGLWINDOWPOS3FMESAPROC)glbGetProcAddress("glWindowPos3fMESA"); + pAPI->glWindowPos3fvMESA = (PFNGLWINDOWPOS3FVMESAPROC)glbGetProcAddress("glWindowPos3fvMESA"); + pAPI->glWindowPos3iMESA = (PFNGLWINDOWPOS3IMESAPROC)glbGetProcAddress("glWindowPos3iMESA"); + pAPI->glWindowPos3ivMESA = (PFNGLWINDOWPOS3IVMESAPROC)glbGetProcAddress("glWindowPos3ivMESA"); + pAPI->glWindowPos3sMESA = (PFNGLWINDOWPOS3SMESAPROC)glbGetProcAddress("glWindowPos3sMESA"); + pAPI->glWindowPos3svMESA = (PFNGLWINDOWPOS3SVMESAPROC)glbGetProcAddress("glWindowPos3svMESA"); + pAPI->glWindowPos4dMESA = (PFNGLWINDOWPOS4DMESAPROC)glbGetProcAddress("glWindowPos4dMESA"); + pAPI->glWindowPos4dvMESA = (PFNGLWINDOWPOS4DVMESAPROC)glbGetProcAddress("glWindowPos4dvMESA"); + pAPI->glWindowPos4fMESA = (PFNGLWINDOWPOS4FMESAPROC)glbGetProcAddress("glWindowPos4fMESA"); + pAPI->glWindowPos4fvMESA = (PFNGLWINDOWPOS4FVMESAPROC)glbGetProcAddress("glWindowPos4fvMESA"); + pAPI->glWindowPos4iMESA = (PFNGLWINDOWPOS4IMESAPROC)glbGetProcAddress("glWindowPos4iMESA"); + pAPI->glWindowPos4ivMESA = (PFNGLWINDOWPOS4IVMESAPROC)glbGetProcAddress("glWindowPos4ivMESA"); + pAPI->glWindowPos4sMESA = (PFNGLWINDOWPOS4SMESAPROC)glbGetProcAddress("glWindowPos4sMESA"); + pAPI->glWindowPos4svMESA = (PFNGLWINDOWPOS4SVMESAPROC)glbGetProcAddress("glWindowPos4svMESA"); + pAPI->glBeginConditionalRenderNVX = (PFNGLBEGINCONDITIONALRENDERNVXPROC)glbGetProcAddress("glBeginConditionalRenderNVX"); + pAPI->glEndConditionalRenderNVX = (PFNGLENDCONDITIONALRENDERNVXPROC)glbGetProcAddress("glEndConditionalRenderNVX"); + pAPI->glLGPUNamedBufferSubDataNVX = (PFNGLLGPUNAMEDBUFFERSUBDATANVXPROC)glbGetProcAddress("glLGPUNamedBufferSubDataNVX"); + pAPI->glLGPUCopyImageSubDataNVX = (PFNGLLGPUCOPYIMAGESUBDATANVXPROC)glbGetProcAddress("glLGPUCopyImageSubDataNVX"); + pAPI->glLGPUInterlockNVX = (PFNGLLGPUINTERLOCKNVXPROC)glbGetProcAddress("glLGPUInterlockNVX"); + pAPI->glAlphaToCoverageDitherControlNV = (PFNGLALPHATOCOVERAGEDITHERCONTROLNVPROC)glbGetProcAddress("glAlphaToCoverageDitherControlNV"); + pAPI->glMultiDrawArraysIndirectBindlessNV = (PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC)glbGetProcAddress("glMultiDrawArraysIndirectBindlessNV"); + pAPI->glMultiDrawElementsIndirectBindlessNV = (PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSNVPROC)glbGetProcAddress("glMultiDrawElementsIndirectBindlessNV"); + pAPI->glMultiDrawArraysIndirectBindlessCountNV = (PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSCOUNTNVPROC)glbGetProcAddress("glMultiDrawArraysIndirectBindlessCountNV"); + pAPI->glMultiDrawElementsIndirectBindlessCountNV = (PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSCOUNTNVPROC)glbGetProcAddress("glMultiDrawElementsIndirectBindlessCountNV"); + pAPI->glGetTextureHandleNV = (PFNGLGETTEXTUREHANDLENVPROC)glbGetProcAddress("glGetTextureHandleNV"); + pAPI->glGetTextureSamplerHandleNV = (PFNGLGETTEXTURESAMPLERHANDLENVPROC)glbGetProcAddress("glGetTextureSamplerHandleNV"); + pAPI->glMakeTextureHandleResidentNV = (PFNGLMAKETEXTUREHANDLERESIDENTNVPROC)glbGetProcAddress("glMakeTextureHandleResidentNV"); + pAPI->glMakeTextureHandleNonResidentNV = (PFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC)glbGetProcAddress("glMakeTextureHandleNonResidentNV"); + pAPI->glGetImageHandleNV = (PFNGLGETIMAGEHANDLENVPROC)glbGetProcAddress("glGetImageHandleNV"); + pAPI->glMakeImageHandleResidentNV = (PFNGLMAKEIMAGEHANDLERESIDENTNVPROC)glbGetProcAddress("glMakeImageHandleResidentNV"); + pAPI->glMakeImageHandleNonResidentNV = (PFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC)glbGetProcAddress("glMakeImageHandleNonResidentNV"); + pAPI->glUniformHandleui64NV = (PFNGLUNIFORMHANDLEUI64NVPROC)glbGetProcAddress("glUniformHandleui64NV"); + pAPI->glUniformHandleui64vNV = (PFNGLUNIFORMHANDLEUI64VNVPROC)glbGetProcAddress("glUniformHandleui64vNV"); + pAPI->glProgramUniformHandleui64NV = (PFNGLPROGRAMUNIFORMHANDLEUI64NVPROC)glbGetProcAddress("glProgramUniformHandleui64NV"); + pAPI->glProgramUniformHandleui64vNV = (PFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC)glbGetProcAddress("glProgramUniformHandleui64vNV"); + pAPI->glIsTextureHandleResidentNV = (PFNGLISTEXTUREHANDLERESIDENTNVPROC)glbGetProcAddress("glIsTextureHandleResidentNV"); + pAPI->glIsImageHandleResidentNV = (PFNGLISIMAGEHANDLERESIDENTNVPROC)glbGetProcAddress("glIsImageHandleResidentNV"); + pAPI->glBlendParameteriNV = (PFNGLBLENDPARAMETERINVPROC)glbGetProcAddress("glBlendParameteriNV"); + pAPI->glBlendBarrierNV = (PFNGLBLENDBARRIERNVPROC)glbGetProcAddress("glBlendBarrierNV"); + pAPI->glViewportPositionWScaleNV = (PFNGLVIEWPORTPOSITIONWSCALENVPROC)glbGetProcAddress("glViewportPositionWScaleNV"); + pAPI->glCreateStatesNV = (PFNGLCREATESTATESNVPROC)glbGetProcAddress("glCreateStatesNV"); + pAPI->glDeleteStatesNV = (PFNGLDELETESTATESNVPROC)glbGetProcAddress("glDeleteStatesNV"); + pAPI->glIsStateNV = (PFNGLISSTATENVPROC)glbGetProcAddress("glIsStateNV"); + pAPI->glStateCaptureNV = (PFNGLSTATECAPTURENVPROC)glbGetProcAddress("glStateCaptureNV"); + pAPI->glGetCommandHeaderNV = (PFNGLGETCOMMANDHEADERNVPROC)glbGetProcAddress("glGetCommandHeaderNV"); + pAPI->glGetStageIndexNV = (PFNGLGETSTAGEINDEXNVPROC)glbGetProcAddress("glGetStageIndexNV"); + pAPI->glDrawCommandsNV = (PFNGLDRAWCOMMANDSNVPROC)glbGetProcAddress("glDrawCommandsNV"); + pAPI->glDrawCommandsAddressNV = (PFNGLDRAWCOMMANDSADDRESSNVPROC)glbGetProcAddress("glDrawCommandsAddressNV"); + pAPI->glDrawCommandsStatesNV = (PFNGLDRAWCOMMANDSSTATESNVPROC)glbGetProcAddress("glDrawCommandsStatesNV"); + pAPI->glDrawCommandsStatesAddressNV = (PFNGLDRAWCOMMANDSSTATESADDRESSNVPROC)glbGetProcAddress("glDrawCommandsStatesAddressNV"); + pAPI->glCreateCommandListsNV = (PFNGLCREATECOMMANDLISTSNVPROC)glbGetProcAddress("glCreateCommandListsNV"); + pAPI->glDeleteCommandListsNV = (PFNGLDELETECOMMANDLISTSNVPROC)glbGetProcAddress("glDeleteCommandListsNV"); + pAPI->glIsCommandListNV = (PFNGLISCOMMANDLISTNVPROC)glbGetProcAddress("glIsCommandListNV"); + pAPI->glListDrawCommandsStatesClientNV = (PFNGLLISTDRAWCOMMANDSSTATESCLIENTNVPROC)glbGetProcAddress("glListDrawCommandsStatesClientNV"); + pAPI->glCommandListSegmentsNV = (PFNGLCOMMANDLISTSEGMENTSNVPROC)glbGetProcAddress("glCommandListSegmentsNV"); + pAPI->glCompileCommandListNV = (PFNGLCOMPILECOMMANDLISTNVPROC)glbGetProcAddress("glCompileCommandListNV"); + pAPI->glCallCommandListNV = (PFNGLCALLCOMMANDLISTNVPROC)glbGetProcAddress("glCallCommandListNV"); + pAPI->glBeginConditionalRenderNV = (PFNGLBEGINCONDITIONALRENDERNVPROC)glbGetProcAddress("glBeginConditionalRenderNV"); + pAPI->glEndConditionalRenderNV = (PFNGLENDCONDITIONALRENDERNVPROC)glbGetProcAddress("glEndConditionalRenderNV"); + pAPI->glSubpixelPrecisionBiasNV = (PFNGLSUBPIXELPRECISIONBIASNVPROC)glbGetProcAddress("glSubpixelPrecisionBiasNV"); + pAPI->glConservativeRasterParameterfNV = (PFNGLCONSERVATIVERASTERPARAMETERFNVPROC)glbGetProcAddress("glConservativeRasterParameterfNV"); + pAPI->glConservativeRasterParameteriNV = (PFNGLCONSERVATIVERASTERPARAMETERINVPROC)glbGetProcAddress("glConservativeRasterParameteriNV"); + pAPI->glCopyImageSubDataNV = (PFNGLCOPYIMAGESUBDATANVPROC)glbGetProcAddress("glCopyImageSubDataNV"); + pAPI->glDepthRangedNV = (PFNGLDEPTHRANGEDNVPROC)glbGetProcAddress("glDepthRangedNV"); + pAPI->glClearDepthdNV = (PFNGLCLEARDEPTHDNVPROC)glbGetProcAddress("glClearDepthdNV"); + pAPI->glDepthBoundsdNV = (PFNGLDEPTHBOUNDSDNVPROC)glbGetProcAddress("glDepthBoundsdNV"); + pAPI->glDrawTextureNV = (PFNGLDRAWTEXTURENVPROC)glbGetProcAddress("glDrawTextureNV"); + pAPI->glDrawVkImageNV = (PFNGLDRAWVKIMAGENVPROC)glbGetProcAddress("glDrawVkImageNV"); + pAPI->glGetVkProcAddrNV = (PFNGLGETVKPROCADDRNVPROC)glbGetProcAddress("glGetVkProcAddrNV"); + pAPI->glWaitVkSemaphoreNV = (PFNGLWAITVKSEMAPHORENVPROC)glbGetProcAddress("glWaitVkSemaphoreNV"); + pAPI->glSignalVkSemaphoreNV = (PFNGLSIGNALVKSEMAPHORENVPROC)glbGetProcAddress("glSignalVkSemaphoreNV"); + pAPI->glSignalVkFenceNV = (PFNGLSIGNALVKFENCENVPROC)glbGetProcAddress("glSignalVkFenceNV"); + pAPI->glMapControlPointsNV = (PFNGLMAPCONTROLPOINTSNVPROC)glbGetProcAddress("glMapControlPointsNV"); + pAPI->glMapParameterivNV = (PFNGLMAPPARAMETERIVNVPROC)glbGetProcAddress("glMapParameterivNV"); + pAPI->glMapParameterfvNV = (PFNGLMAPPARAMETERFVNVPROC)glbGetProcAddress("glMapParameterfvNV"); + pAPI->glGetMapControlPointsNV = (PFNGLGETMAPCONTROLPOINTSNVPROC)glbGetProcAddress("glGetMapControlPointsNV"); + pAPI->glGetMapParameterivNV = (PFNGLGETMAPPARAMETERIVNVPROC)glbGetProcAddress("glGetMapParameterivNV"); + pAPI->glGetMapParameterfvNV = (PFNGLGETMAPPARAMETERFVNVPROC)glbGetProcAddress("glGetMapParameterfvNV"); + pAPI->glGetMapAttribParameterivNV = (PFNGLGETMAPATTRIBPARAMETERIVNVPROC)glbGetProcAddress("glGetMapAttribParameterivNV"); + pAPI->glGetMapAttribParameterfvNV = (PFNGLGETMAPATTRIBPARAMETERFVNVPROC)glbGetProcAddress("glGetMapAttribParameterfvNV"); + pAPI->glEvalMapsNV = (PFNGLEVALMAPSNVPROC)glbGetProcAddress("glEvalMapsNV"); + pAPI->glGetMultisamplefvNV = (PFNGLGETMULTISAMPLEFVNVPROC)glbGetProcAddress("glGetMultisamplefvNV"); + pAPI->glSampleMaskIndexedNV = (PFNGLSAMPLEMASKINDEXEDNVPROC)glbGetProcAddress("glSampleMaskIndexedNV"); + pAPI->glTexRenderbufferNV = (PFNGLTEXRENDERBUFFERNVPROC)glbGetProcAddress("glTexRenderbufferNV"); + pAPI->glDeleteFencesNV = (PFNGLDELETEFENCESNVPROC)glbGetProcAddress("glDeleteFencesNV"); + pAPI->glGenFencesNV = (PFNGLGENFENCESNVPROC)glbGetProcAddress("glGenFencesNV"); + pAPI->glIsFenceNV = (PFNGLISFENCENVPROC)glbGetProcAddress("glIsFenceNV"); + pAPI->glTestFenceNV = (PFNGLTESTFENCENVPROC)glbGetProcAddress("glTestFenceNV"); + pAPI->glGetFenceivNV = (PFNGLGETFENCEIVNVPROC)glbGetProcAddress("glGetFenceivNV"); + pAPI->glFinishFenceNV = (PFNGLFINISHFENCENVPROC)glbGetProcAddress("glFinishFenceNV"); + pAPI->glSetFenceNV = (PFNGLSETFENCENVPROC)glbGetProcAddress("glSetFenceNV"); + pAPI->glFragmentCoverageColorNV = (PFNGLFRAGMENTCOVERAGECOLORNVPROC)glbGetProcAddress("glFragmentCoverageColorNV"); + pAPI->glProgramNamedParameter4fNV = (PFNGLPROGRAMNAMEDPARAMETER4FNVPROC)glbGetProcAddress("glProgramNamedParameter4fNV"); + pAPI->glProgramNamedParameter4fvNV = (PFNGLPROGRAMNAMEDPARAMETER4FVNVPROC)glbGetProcAddress("glProgramNamedParameter4fvNV"); + pAPI->glProgramNamedParameter4dNV = (PFNGLPROGRAMNAMEDPARAMETER4DNVPROC)glbGetProcAddress("glProgramNamedParameter4dNV"); + pAPI->glProgramNamedParameter4dvNV = (PFNGLPROGRAMNAMEDPARAMETER4DVNVPROC)glbGetProcAddress("glProgramNamedParameter4dvNV"); + pAPI->glGetProgramNamedParameterfvNV = (PFNGLGETPROGRAMNAMEDPARAMETERFVNVPROC)glbGetProcAddress("glGetProgramNamedParameterfvNV"); + pAPI->glGetProgramNamedParameterdvNV = (PFNGLGETPROGRAMNAMEDPARAMETERDVNVPROC)glbGetProcAddress("glGetProgramNamedParameterdvNV"); + pAPI->glCoverageModulationTableNV = (PFNGLCOVERAGEMODULATIONTABLENVPROC)glbGetProcAddress("glCoverageModulationTableNV"); + pAPI->glGetCoverageModulationTableNV = (PFNGLGETCOVERAGEMODULATIONTABLENVPROC)glbGetProcAddress("glGetCoverageModulationTableNV"); + pAPI->glCoverageModulationNV = (PFNGLCOVERAGEMODULATIONNVPROC)glbGetProcAddress("glCoverageModulationNV"); + pAPI->glRenderbufferStorageMultisampleCoverageNV = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC)glbGetProcAddress("glRenderbufferStorageMultisampleCoverageNV"); + pAPI->glProgramVertexLimitNV = (PFNGLPROGRAMVERTEXLIMITNVPROC)glbGetProcAddress("glProgramVertexLimitNV"); + pAPI->glFramebufferTextureEXT = (PFNGLFRAMEBUFFERTEXTUREEXTPROC)glbGetProcAddress("glFramebufferTextureEXT"); + pAPI->glFramebufferTextureFaceEXT = (PFNGLFRAMEBUFFERTEXTUREFACEEXTPROC)glbGetProcAddress("glFramebufferTextureFaceEXT"); + pAPI->glProgramLocalParameterI4iNV = (PFNGLPROGRAMLOCALPARAMETERI4INVPROC)glbGetProcAddress("glProgramLocalParameterI4iNV"); + pAPI->glProgramLocalParameterI4ivNV = (PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC)glbGetProcAddress("glProgramLocalParameterI4ivNV"); + pAPI->glProgramLocalParametersI4ivNV = (PFNGLPROGRAMLOCALPARAMETERSI4IVNVPROC)glbGetProcAddress("glProgramLocalParametersI4ivNV"); + pAPI->glProgramLocalParameterI4uiNV = (PFNGLPROGRAMLOCALPARAMETERI4UINVPROC)glbGetProcAddress("glProgramLocalParameterI4uiNV"); + pAPI->glProgramLocalParameterI4uivNV = (PFNGLPROGRAMLOCALPARAMETERI4UIVNVPROC)glbGetProcAddress("glProgramLocalParameterI4uivNV"); + pAPI->glProgramLocalParametersI4uivNV = (PFNGLPROGRAMLOCALPARAMETERSI4UIVNVPROC)glbGetProcAddress("glProgramLocalParametersI4uivNV"); + pAPI->glProgramEnvParameterI4iNV = (PFNGLPROGRAMENVPARAMETERI4INVPROC)glbGetProcAddress("glProgramEnvParameterI4iNV"); + pAPI->glProgramEnvParameterI4ivNV = (PFNGLPROGRAMENVPARAMETERI4IVNVPROC)glbGetProcAddress("glProgramEnvParameterI4ivNV"); + pAPI->glProgramEnvParametersI4ivNV = (PFNGLPROGRAMENVPARAMETERSI4IVNVPROC)glbGetProcAddress("glProgramEnvParametersI4ivNV"); + pAPI->glProgramEnvParameterI4uiNV = (PFNGLPROGRAMENVPARAMETERI4UINVPROC)glbGetProcAddress("glProgramEnvParameterI4uiNV"); + pAPI->glProgramEnvParameterI4uivNV = (PFNGLPROGRAMENVPARAMETERI4UIVNVPROC)glbGetProcAddress("glProgramEnvParameterI4uivNV"); + pAPI->glProgramEnvParametersI4uivNV = (PFNGLPROGRAMENVPARAMETERSI4UIVNVPROC)glbGetProcAddress("glProgramEnvParametersI4uivNV"); + pAPI->glGetProgramLocalParameterIivNV = (PFNGLGETPROGRAMLOCALPARAMETERIIVNVPROC)glbGetProcAddress("glGetProgramLocalParameterIivNV"); + pAPI->glGetProgramLocalParameterIuivNV = (PFNGLGETPROGRAMLOCALPARAMETERIUIVNVPROC)glbGetProcAddress("glGetProgramLocalParameterIuivNV"); + pAPI->glGetProgramEnvParameterIivNV = (PFNGLGETPROGRAMENVPARAMETERIIVNVPROC)glbGetProcAddress("glGetProgramEnvParameterIivNV"); + pAPI->glGetProgramEnvParameterIuivNV = (PFNGLGETPROGRAMENVPARAMETERIUIVNVPROC)glbGetProcAddress("glGetProgramEnvParameterIuivNV"); + pAPI->glProgramSubroutineParametersuivNV = (PFNGLPROGRAMSUBROUTINEPARAMETERSUIVNVPROC)glbGetProcAddress("glProgramSubroutineParametersuivNV"); + pAPI->glGetProgramSubroutineParameteruivNV = (PFNGLGETPROGRAMSUBROUTINEPARAMETERUIVNVPROC)glbGetProcAddress("glGetProgramSubroutineParameteruivNV"); + pAPI->glVertex2hNV = (PFNGLVERTEX2HNVPROC)glbGetProcAddress("glVertex2hNV"); + pAPI->glVertex2hvNV = (PFNGLVERTEX2HVNVPROC)glbGetProcAddress("glVertex2hvNV"); + pAPI->glVertex3hNV = (PFNGLVERTEX3HNVPROC)glbGetProcAddress("glVertex3hNV"); + pAPI->glVertex3hvNV = (PFNGLVERTEX3HVNVPROC)glbGetProcAddress("glVertex3hvNV"); + pAPI->glVertex4hNV = (PFNGLVERTEX4HNVPROC)glbGetProcAddress("glVertex4hNV"); + pAPI->glVertex4hvNV = (PFNGLVERTEX4HVNVPROC)glbGetProcAddress("glVertex4hvNV"); + pAPI->glNormal3hNV = (PFNGLNORMAL3HNVPROC)glbGetProcAddress("glNormal3hNV"); + pAPI->glNormal3hvNV = (PFNGLNORMAL3HVNVPROC)glbGetProcAddress("glNormal3hvNV"); + pAPI->glColor3hNV = (PFNGLCOLOR3HNVPROC)glbGetProcAddress("glColor3hNV"); + pAPI->glColor3hvNV = (PFNGLCOLOR3HVNVPROC)glbGetProcAddress("glColor3hvNV"); + pAPI->glColor4hNV = (PFNGLCOLOR4HNVPROC)glbGetProcAddress("glColor4hNV"); + pAPI->glColor4hvNV = (PFNGLCOLOR4HVNVPROC)glbGetProcAddress("glColor4hvNV"); + pAPI->glTexCoord1hNV = (PFNGLTEXCOORD1HNVPROC)glbGetProcAddress("glTexCoord1hNV"); + pAPI->glTexCoord1hvNV = (PFNGLTEXCOORD1HVNVPROC)glbGetProcAddress("glTexCoord1hvNV"); + pAPI->glTexCoord2hNV = (PFNGLTEXCOORD2HNVPROC)glbGetProcAddress("glTexCoord2hNV"); + pAPI->glTexCoord2hvNV = (PFNGLTEXCOORD2HVNVPROC)glbGetProcAddress("glTexCoord2hvNV"); + pAPI->glTexCoord3hNV = (PFNGLTEXCOORD3HNVPROC)glbGetProcAddress("glTexCoord3hNV"); + pAPI->glTexCoord3hvNV = (PFNGLTEXCOORD3HVNVPROC)glbGetProcAddress("glTexCoord3hvNV"); + pAPI->glTexCoord4hNV = (PFNGLTEXCOORD4HNVPROC)glbGetProcAddress("glTexCoord4hNV"); + pAPI->glTexCoord4hvNV = (PFNGLTEXCOORD4HVNVPROC)glbGetProcAddress("glTexCoord4hvNV"); + pAPI->glMultiTexCoord1hNV = (PFNGLMULTITEXCOORD1HNVPROC)glbGetProcAddress("glMultiTexCoord1hNV"); + pAPI->glMultiTexCoord1hvNV = (PFNGLMULTITEXCOORD1HVNVPROC)glbGetProcAddress("glMultiTexCoord1hvNV"); + pAPI->glMultiTexCoord2hNV = (PFNGLMULTITEXCOORD2HNVPROC)glbGetProcAddress("glMultiTexCoord2hNV"); + pAPI->glMultiTexCoord2hvNV = (PFNGLMULTITEXCOORD2HVNVPROC)glbGetProcAddress("glMultiTexCoord2hvNV"); + pAPI->glMultiTexCoord3hNV = (PFNGLMULTITEXCOORD3HNVPROC)glbGetProcAddress("glMultiTexCoord3hNV"); + pAPI->glMultiTexCoord3hvNV = (PFNGLMULTITEXCOORD3HVNVPROC)glbGetProcAddress("glMultiTexCoord3hvNV"); + pAPI->glMultiTexCoord4hNV = (PFNGLMULTITEXCOORD4HNVPROC)glbGetProcAddress("glMultiTexCoord4hNV"); + pAPI->glMultiTexCoord4hvNV = (PFNGLMULTITEXCOORD4HVNVPROC)glbGetProcAddress("glMultiTexCoord4hvNV"); + pAPI->glFogCoordhNV = (PFNGLFOGCOORDHNVPROC)glbGetProcAddress("glFogCoordhNV"); + pAPI->glFogCoordhvNV = (PFNGLFOGCOORDHVNVPROC)glbGetProcAddress("glFogCoordhvNV"); + pAPI->glSecondaryColor3hNV = (PFNGLSECONDARYCOLOR3HNVPROC)glbGetProcAddress("glSecondaryColor3hNV"); + pAPI->glSecondaryColor3hvNV = (PFNGLSECONDARYCOLOR3HVNVPROC)glbGetProcAddress("glSecondaryColor3hvNV"); + pAPI->glVertexWeighthNV = (PFNGLVERTEXWEIGHTHNVPROC)glbGetProcAddress("glVertexWeighthNV"); + pAPI->glVertexWeighthvNV = (PFNGLVERTEXWEIGHTHVNVPROC)glbGetProcAddress("glVertexWeighthvNV"); + pAPI->glVertexAttrib1hNV = (PFNGLVERTEXATTRIB1HNVPROC)glbGetProcAddress("glVertexAttrib1hNV"); + pAPI->glVertexAttrib1hvNV = (PFNGLVERTEXATTRIB1HVNVPROC)glbGetProcAddress("glVertexAttrib1hvNV"); + pAPI->glVertexAttrib2hNV = (PFNGLVERTEXATTRIB2HNVPROC)glbGetProcAddress("glVertexAttrib2hNV"); + pAPI->glVertexAttrib2hvNV = (PFNGLVERTEXATTRIB2HVNVPROC)glbGetProcAddress("glVertexAttrib2hvNV"); + pAPI->glVertexAttrib3hNV = (PFNGLVERTEXATTRIB3HNVPROC)glbGetProcAddress("glVertexAttrib3hNV"); + pAPI->glVertexAttrib3hvNV = (PFNGLVERTEXATTRIB3HVNVPROC)glbGetProcAddress("glVertexAttrib3hvNV"); + pAPI->glVertexAttrib4hNV = (PFNGLVERTEXATTRIB4HNVPROC)glbGetProcAddress("glVertexAttrib4hNV"); + pAPI->glVertexAttrib4hvNV = (PFNGLVERTEXATTRIB4HVNVPROC)glbGetProcAddress("glVertexAttrib4hvNV"); + pAPI->glVertexAttribs1hvNV = (PFNGLVERTEXATTRIBS1HVNVPROC)glbGetProcAddress("glVertexAttribs1hvNV"); + pAPI->glVertexAttribs2hvNV = (PFNGLVERTEXATTRIBS2HVNVPROC)glbGetProcAddress("glVertexAttribs2hvNV"); + pAPI->glVertexAttribs3hvNV = (PFNGLVERTEXATTRIBS3HVNVPROC)glbGetProcAddress("glVertexAttribs3hvNV"); + pAPI->glVertexAttribs4hvNV = (PFNGLVERTEXATTRIBS4HVNVPROC)glbGetProcAddress("glVertexAttribs4hvNV"); + pAPI->glGetInternalformatSampleivNV = (PFNGLGETINTERNALFORMATSAMPLEIVNVPROC)glbGetProcAddress("glGetInternalformatSampleivNV"); + pAPI->glRenderGpuMaskNV = (PFNGLRENDERGPUMASKNVPROC)glbGetProcAddress("glRenderGpuMaskNV"); + pAPI->glMulticastBufferSubDataNV = (PFNGLMULTICASTBUFFERSUBDATANVPROC)glbGetProcAddress("glMulticastBufferSubDataNV"); + pAPI->glMulticastCopyBufferSubDataNV = (PFNGLMULTICASTCOPYBUFFERSUBDATANVPROC)glbGetProcAddress("glMulticastCopyBufferSubDataNV"); + pAPI->glMulticastCopyImageSubDataNV = (PFNGLMULTICASTCOPYIMAGESUBDATANVPROC)glbGetProcAddress("glMulticastCopyImageSubDataNV"); + pAPI->glMulticastBlitFramebufferNV = (PFNGLMULTICASTBLITFRAMEBUFFERNVPROC)glbGetProcAddress("glMulticastBlitFramebufferNV"); + pAPI->glMulticastFramebufferSampleLocationsfvNV = (PFNGLMULTICASTFRAMEBUFFERSAMPLELOCATIONSFVNVPROC)glbGetProcAddress("glMulticastFramebufferSampleLocationsfvNV"); + pAPI->glMulticastBarrierNV = (PFNGLMULTICASTBARRIERNVPROC)glbGetProcAddress("glMulticastBarrierNV"); + pAPI->glMulticastWaitSyncNV = (PFNGLMULTICASTWAITSYNCNVPROC)glbGetProcAddress("glMulticastWaitSyncNV"); + pAPI->glMulticastGetQueryObjectivNV = (PFNGLMULTICASTGETQUERYOBJECTIVNVPROC)glbGetProcAddress("glMulticastGetQueryObjectivNV"); + pAPI->glMulticastGetQueryObjectuivNV = (PFNGLMULTICASTGETQUERYOBJECTUIVNVPROC)glbGetProcAddress("glMulticastGetQueryObjectuivNV"); + pAPI->glMulticastGetQueryObjecti64vNV = (PFNGLMULTICASTGETQUERYOBJECTI64VNVPROC)glbGetProcAddress("glMulticastGetQueryObjecti64vNV"); + pAPI->glMulticastGetQueryObjectui64vNV = (PFNGLMULTICASTGETQUERYOBJECTUI64VNVPROC)glbGetProcAddress("glMulticastGetQueryObjectui64vNV"); + pAPI->glUploadGpuMaskNVX = (PFNGLUPLOADGPUMASKNVXPROC)glbGetProcAddress("glUploadGpuMaskNVX"); + pAPI->glMulticastViewportArrayvNVX = (PFNGLMULTICASTVIEWPORTARRAYVNVXPROC)glbGetProcAddress("glMulticastViewportArrayvNVX"); + pAPI->glMulticastViewportPositionWScaleNVX = (PFNGLMULTICASTVIEWPORTPOSITIONWSCALENVXPROC)glbGetProcAddress("glMulticastViewportPositionWScaleNVX"); + pAPI->glMulticastScissorArrayvNVX = (PFNGLMULTICASTSCISSORARRAYVNVXPROC)glbGetProcAddress("glMulticastScissorArrayvNVX"); + pAPI->glAsyncCopyBufferSubDataNVX = (PFNGLASYNCCOPYBUFFERSUBDATANVXPROC)glbGetProcAddress("glAsyncCopyBufferSubDataNVX"); + pAPI->glAsyncCopyImageSubDataNVX = (PFNGLASYNCCOPYIMAGESUBDATANVXPROC)glbGetProcAddress("glAsyncCopyImageSubDataNVX"); + pAPI->glCreateProgressFenceNVX = (PFNGLCREATEPROGRESSFENCENVXPROC)glbGetProcAddress("glCreateProgressFenceNVX"); + pAPI->glSignalSemaphoreui64NVX = (PFNGLSIGNALSEMAPHOREUI64NVXPROC)glbGetProcAddress("glSignalSemaphoreui64NVX"); + pAPI->glWaitSemaphoreui64NVX = (PFNGLWAITSEMAPHOREUI64NVXPROC)glbGetProcAddress("glWaitSemaphoreui64NVX"); + pAPI->glClientWaitSemaphoreui64NVX = (PFNGLCLIENTWAITSEMAPHOREUI64NVXPROC)glbGetProcAddress("glClientWaitSemaphoreui64NVX"); + pAPI->glGetMemoryObjectDetachedResourcesuivNV = (PFNGLGETMEMORYOBJECTDETACHEDRESOURCESUIVNVPROC)glbGetProcAddress("glGetMemoryObjectDetachedResourcesuivNV"); + pAPI->glResetMemoryObjectParameterNV = (PFNGLRESETMEMORYOBJECTPARAMETERNVPROC)glbGetProcAddress("glResetMemoryObjectParameterNV"); + pAPI->glTexAttachMemoryNV = (PFNGLTEXATTACHMEMORYNVPROC)glbGetProcAddress("glTexAttachMemoryNV"); + pAPI->glBufferAttachMemoryNV = (PFNGLBUFFERATTACHMEMORYNVPROC)glbGetProcAddress("glBufferAttachMemoryNV"); + pAPI->glTextureAttachMemoryNV = (PFNGLTEXTUREATTACHMEMORYNVPROC)glbGetProcAddress("glTextureAttachMemoryNV"); + pAPI->glNamedBufferAttachMemoryNV = (PFNGLNAMEDBUFFERATTACHMEMORYNVPROC)glbGetProcAddress("glNamedBufferAttachMemoryNV"); + pAPI->glBufferPageCommitmentMemNV = (PFNGLBUFFERPAGECOMMITMENTMEMNVPROC)glbGetProcAddress("glBufferPageCommitmentMemNV"); + pAPI->glTexPageCommitmentMemNV = (PFNGLTEXPAGECOMMITMENTMEMNVPROC)glbGetProcAddress("glTexPageCommitmentMemNV"); + pAPI->glNamedBufferPageCommitmentMemNV = (PFNGLNAMEDBUFFERPAGECOMMITMENTMEMNVPROC)glbGetProcAddress("glNamedBufferPageCommitmentMemNV"); + pAPI->glTexturePageCommitmentMemNV = (PFNGLTEXTUREPAGECOMMITMENTMEMNVPROC)glbGetProcAddress("glTexturePageCommitmentMemNV"); + pAPI->glDrawMeshTasksNV = (PFNGLDRAWMESHTASKSNVPROC)glbGetProcAddress("glDrawMeshTasksNV"); + pAPI->glDrawMeshTasksIndirectNV = (PFNGLDRAWMESHTASKSINDIRECTNVPROC)glbGetProcAddress("glDrawMeshTasksIndirectNV"); + pAPI->glMultiDrawMeshTasksIndirectNV = (PFNGLMULTIDRAWMESHTASKSINDIRECTNVPROC)glbGetProcAddress("glMultiDrawMeshTasksIndirectNV"); + pAPI->glMultiDrawMeshTasksIndirectCountNV = (PFNGLMULTIDRAWMESHTASKSINDIRECTCOUNTNVPROC)glbGetProcAddress("glMultiDrawMeshTasksIndirectCountNV"); + pAPI->glGenOcclusionQueriesNV = (PFNGLGENOCCLUSIONQUERIESNVPROC)glbGetProcAddress("glGenOcclusionQueriesNV"); + pAPI->glDeleteOcclusionQueriesNV = (PFNGLDELETEOCCLUSIONQUERIESNVPROC)glbGetProcAddress("glDeleteOcclusionQueriesNV"); + pAPI->glIsOcclusionQueryNV = (PFNGLISOCCLUSIONQUERYNVPROC)glbGetProcAddress("glIsOcclusionQueryNV"); + pAPI->glBeginOcclusionQueryNV = (PFNGLBEGINOCCLUSIONQUERYNVPROC)glbGetProcAddress("glBeginOcclusionQueryNV"); + pAPI->glEndOcclusionQueryNV = (PFNGLENDOCCLUSIONQUERYNVPROC)glbGetProcAddress("glEndOcclusionQueryNV"); + pAPI->glGetOcclusionQueryivNV = (PFNGLGETOCCLUSIONQUERYIVNVPROC)glbGetProcAddress("glGetOcclusionQueryivNV"); + pAPI->glGetOcclusionQueryuivNV = (PFNGLGETOCCLUSIONQUERYUIVNVPROC)glbGetProcAddress("glGetOcclusionQueryuivNV"); + pAPI->glProgramBufferParametersfvNV = (PFNGLPROGRAMBUFFERPARAMETERSFVNVPROC)glbGetProcAddress("glProgramBufferParametersfvNV"); + pAPI->glProgramBufferParametersIivNV = (PFNGLPROGRAMBUFFERPARAMETERSIIVNVPROC)glbGetProcAddress("glProgramBufferParametersIivNV"); + pAPI->glProgramBufferParametersIuivNV = (PFNGLPROGRAMBUFFERPARAMETERSIUIVNVPROC)glbGetProcAddress("glProgramBufferParametersIuivNV"); + pAPI->glGenPathsNV = (PFNGLGENPATHSNVPROC)glbGetProcAddress("glGenPathsNV"); + pAPI->glDeletePathsNV = (PFNGLDELETEPATHSNVPROC)glbGetProcAddress("glDeletePathsNV"); + pAPI->glIsPathNV = (PFNGLISPATHNVPROC)glbGetProcAddress("glIsPathNV"); + pAPI->glPathCommandsNV = (PFNGLPATHCOMMANDSNVPROC)glbGetProcAddress("glPathCommandsNV"); + pAPI->glPathCoordsNV = (PFNGLPATHCOORDSNVPROC)glbGetProcAddress("glPathCoordsNV"); + pAPI->glPathSubCommandsNV = (PFNGLPATHSUBCOMMANDSNVPROC)glbGetProcAddress("glPathSubCommandsNV"); + pAPI->glPathSubCoordsNV = (PFNGLPATHSUBCOORDSNVPROC)glbGetProcAddress("glPathSubCoordsNV"); + pAPI->glPathStringNV = (PFNGLPATHSTRINGNVPROC)glbGetProcAddress("glPathStringNV"); + pAPI->glPathGlyphsNV = (PFNGLPATHGLYPHSNVPROC)glbGetProcAddress("glPathGlyphsNV"); + pAPI->glPathGlyphRangeNV = (PFNGLPATHGLYPHRANGENVPROC)glbGetProcAddress("glPathGlyphRangeNV"); + pAPI->glWeightPathsNV = (PFNGLWEIGHTPATHSNVPROC)glbGetProcAddress("glWeightPathsNV"); + pAPI->glCopyPathNV = (PFNGLCOPYPATHNVPROC)glbGetProcAddress("glCopyPathNV"); + pAPI->glInterpolatePathsNV = (PFNGLINTERPOLATEPATHSNVPROC)glbGetProcAddress("glInterpolatePathsNV"); + pAPI->glTransformPathNV = (PFNGLTRANSFORMPATHNVPROC)glbGetProcAddress("glTransformPathNV"); + pAPI->glPathParameterivNV = (PFNGLPATHPARAMETERIVNVPROC)glbGetProcAddress("glPathParameterivNV"); + pAPI->glPathParameteriNV = (PFNGLPATHPARAMETERINVPROC)glbGetProcAddress("glPathParameteriNV"); + pAPI->glPathParameterfvNV = (PFNGLPATHPARAMETERFVNVPROC)glbGetProcAddress("glPathParameterfvNV"); + pAPI->glPathParameterfNV = (PFNGLPATHPARAMETERFNVPROC)glbGetProcAddress("glPathParameterfNV"); + pAPI->glPathDashArrayNV = (PFNGLPATHDASHARRAYNVPROC)glbGetProcAddress("glPathDashArrayNV"); + pAPI->glPathStencilFuncNV = (PFNGLPATHSTENCILFUNCNVPROC)glbGetProcAddress("glPathStencilFuncNV"); + pAPI->glPathStencilDepthOffsetNV = (PFNGLPATHSTENCILDEPTHOFFSETNVPROC)glbGetProcAddress("glPathStencilDepthOffsetNV"); + pAPI->glStencilFillPathNV = (PFNGLSTENCILFILLPATHNVPROC)glbGetProcAddress("glStencilFillPathNV"); + pAPI->glStencilStrokePathNV = (PFNGLSTENCILSTROKEPATHNVPROC)glbGetProcAddress("glStencilStrokePathNV"); + pAPI->glStencilFillPathInstancedNV = (PFNGLSTENCILFILLPATHINSTANCEDNVPROC)glbGetProcAddress("glStencilFillPathInstancedNV"); + pAPI->glStencilStrokePathInstancedNV = (PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC)glbGetProcAddress("glStencilStrokePathInstancedNV"); + pAPI->glPathCoverDepthFuncNV = (PFNGLPATHCOVERDEPTHFUNCNVPROC)glbGetProcAddress("glPathCoverDepthFuncNV"); + pAPI->glCoverFillPathNV = (PFNGLCOVERFILLPATHNVPROC)glbGetProcAddress("glCoverFillPathNV"); + pAPI->glCoverStrokePathNV = (PFNGLCOVERSTROKEPATHNVPROC)glbGetProcAddress("glCoverStrokePathNV"); + pAPI->glCoverFillPathInstancedNV = (PFNGLCOVERFILLPATHINSTANCEDNVPROC)glbGetProcAddress("glCoverFillPathInstancedNV"); + pAPI->glCoverStrokePathInstancedNV = (PFNGLCOVERSTROKEPATHINSTANCEDNVPROC)glbGetProcAddress("glCoverStrokePathInstancedNV"); + pAPI->glGetPathParameterivNV = (PFNGLGETPATHPARAMETERIVNVPROC)glbGetProcAddress("glGetPathParameterivNV"); + pAPI->glGetPathParameterfvNV = (PFNGLGETPATHPARAMETERFVNVPROC)glbGetProcAddress("glGetPathParameterfvNV"); + pAPI->glGetPathCommandsNV = (PFNGLGETPATHCOMMANDSNVPROC)glbGetProcAddress("glGetPathCommandsNV"); + pAPI->glGetPathCoordsNV = (PFNGLGETPATHCOORDSNVPROC)glbGetProcAddress("glGetPathCoordsNV"); + pAPI->glGetPathDashArrayNV = (PFNGLGETPATHDASHARRAYNVPROC)glbGetProcAddress("glGetPathDashArrayNV"); + pAPI->glGetPathMetricsNV = (PFNGLGETPATHMETRICSNVPROC)glbGetProcAddress("glGetPathMetricsNV"); + pAPI->glGetPathMetricRangeNV = (PFNGLGETPATHMETRICRANGENVPROC)glbGetProcAddress("glGetPathMetricRangeNV"); + pAPI->glGetPathSpacingNV = (PFNGLGETPATHSPACINGNVPROC)glbGetProcAddress("glGetPathSpacingNV"); + pAPI->glIsPointInFillPathNV = (PFNGLISPOINTINFILLPATHNVPROC)glbGetProcAddress("glIsPointInFillPathNV"); + pAPI->glIsPointInStrokePathNV = (PFNGLISPOINTINSTROKEPATHNVPROC)glbGetProcAddress("glIsPointInStrokePathNV"); + pAPI->glGetPathLengthNV = (PFNGLGETPATHLENGTHNVPROC)glbGetProcAddress("glGetPathLengthNV"); + pAPI->glPointAlongPathNV = (PFNGLPOINTALONGPATHNVPROC)glbGetProcAddress("glPointAlongPathNV"); + pAPI->glMatrixLoad3x2fNV = (PFNGLMATRIXLOAD3X2FNVPROC)glbGetProcAddress("glMatrixLoad3x2fNV"); + pAPI->glMatrixLoad3x3fNV = (PFNGLMATRIXLOAD3X3FNVPROC)glbGetProcAddress("glMatrixLoad3x3fNV"); + pAPI->glMatrixLoadTranspose3x3fNV = (PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC)glbGetProcAddress("glMatrixLoadTranspose3x3fNV"); + pAPI->glMatrixMult3x2fNV = (PFNGLMATRIXMULT3X2FNVPROC)glbGetProcAddress("glMatrixMult3x2fNV"); + pAPI->glMatrixMult3x3fNV = (PFNGLMATRIXMULT3X3FNVPROC)glbGetProcAddress("glMatrixMult3x3fNV"); + pAPI->glMatrixMultTranspose3x3fNV = (PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC)glbGetProcAddress("glMatrixMultTranspose3x3fNV"); + pAPI->glStencilThenCoverFillPathNV = (PFNGLSTENCILTHENCOVERFILLPATHNVPROC)glbGetProcAddress("glStencilThenCoverFillPathNV"); + pAPI->glStencilThenCoverStrokePathNV = (PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC)glbGetProcAddress("glStencilThenCoverStrokePathNV"); + pAPI->glStencilThenCoverFillPathInstancedNV = (PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC)glbGetProcAddress("glStencilThenCoverFillPathInstancedNV"); + pAPI->glStencilThenCoverStrokePathInstancedNV = (PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC)glbGetProcAddress("glStencilThenCoverStrokePathInstancedNV"); + pAPI->glPathGlyphIndexRangeNV = (PFNGLPATHGLYPHINDEXRANGENVPROC)glbGetProcAddress("glPathGlyphIndexRangeNV"); + pAPI->glPathGlyphIndexArrayNV = (PFNGLPATHGLYPHINDEXARRAYNVPROC)glbGetProcAddress("glPathGlyphIndexArrayNV"); + pAPI->glPathMemoryGlyphIndexArrayNV = (PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC)glbGetProcAddress("glPathMemoryGlyphIndexArrayNV"); + pAPI->glProgramPathFragmentInputGenNV = (PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC)glbGetProcAddress("glProgramPathFragmentInputGenNV"); + pAPI->glGetProgramResourcefvNV = (PFNGLGETPROGRAMRESOURCEFVNVPROC)glbGetProcAddress("glGetProgramResourcefvNV"); + pAPI->glPathColorGenNV = (PFNGLPATHCOLORGENNVPROC)glbGetProcAddress("glPathColorGenNV"); + pAPI->glPathTexGenNV = (PFNGLPATHTEXGENNVPROC)glbGetProcAddress("glPathTexGenNV"); + pAPI->glPathFogGenNV = (PFNGLPATHFOGGENNVPROC)glbGetProcAddress("glPathFogGenNV"); + pAPI->glGetPathColorGenivNV = (PFNGLGETPATHCOLORGENIVNVPROC)glbGetProcAddress("glGetPathColorGenivNV"); + pAPI->glGetPathColorGenfvNV = (PFNGLGETPATHCOLORGENFVNVPROC)glbGetProcAddress("glGetPathColorGenfvNV"); + pAPI->glGetPathTexGenivNV = (PFNGLGETPATHTEXGENIVNVPROC)glbGetProcAddress("glGetPathTexGenivNV"); + pAPI->glGetPathTexGenfvNV = (PFNGLGETPATHTEXGENFVNVPROC)glbGetProcAddress("glGetPathTexGenfvNV"); + pAPI->glPixelDataRangeNV = (PFNGLPIXELDATARANGENVPROC)glbGetProcAddress("glPixelDataRangeNV"); + pAPI->glFlushPixelDataRangeNV = (PFNGLFLUSHPIXELDATARANGENVPROC)glbGetProcAddress("glFlushPixelDataRangeNV"); + pAPI->glPointParameteriNV = (PFNGLPOINTPARAMETERINVPROC)glbGetProcAddress("glPointParameteriNV"); + pAPI->glPointParameterivNV = (PFNGLPOINTPARAMETERIVNVPROC)glbGetProcAddress("glPointParameterivNV"); + pAPI->glPresentFrameKeyedNV = (PFNGLPRESENTFRAMEKEYEDNVPROC)glbGetProcAddress("glPresentFrameKeyedNV"); + pAPI->glPresentFrameDualFillNV = (PFNGLPRESENTFRAMEDUALFILLNVPROC)glbGetProcAddress("glPresentFrameDualFillNV"); + pAPI->glGetVideoivNV = (PFNGLGETVIDEOIVNVPROC)glbGetProcAddress("glGetVideoivNV"); + pAPI->glGetVideouivNV = (PFNGLGETVIDEOUIVNVPROC)glbGetProcAddress("glGetVideouivNV"); + pAPI->glGetVideoi64vNV = (PFNGLGETVIDEOI64VNVPROC)glbGetProcAddress("glGetVideoi64vNV"); + pAPI->glGetVideoui64vNV = (PFNGLGETVIDEOUI64VNVPROC)glbGetProcAddress("glGetVideoui64vNV"); + pAPI->glPrimitiveRestartNV = (PFNGLPRIMITIVERESTARTNVPROC)glbGetProcAddress("glPrimitiveRestartNV"); + pAPI->glPrimitiveRestartIndexNV = (PFNGLPRIMITIVERESTARTINDEXNVPROC)glbGetProcAddress("glPrimitiveRestartIndexNV"); + pAPI->glQueryResourceNV = (PFNGLQUERYRESOURCENVPROC)glbGetProcAddress("glQueryResourceNV"); + pAPI->glGenQueryResourceTagNV = (PFNGLGENQUERYRESOURCETAGNVPROC)glbGetProcAddress("glGenQueryResourceTagNV"); + pAPI->glDeleteQueryResourceTagNV = (PFNGLDELETEQUERYRESOURCETAGNVPROC)glbGetProcAddress("glDeleteQueryResourceTagNV"); + pAPI->glQueryResourceTagNV = (PFNGLQUERYRESOURCETAGNVPROC)glbGetProcAddress("glQueryResourceTagNV"); + pAPI->glCombinerParameterfvNV = (PFNGLCOMBINERPARAMETERFVNVPROC)glbGetProcAddress("glCombinerParameterfvNV"); + pAPI->glCombinerParameterfNV = (PFNGLCOMBINERPARAMETERFNVPROC)glbGetProcAddress("glCombinerParameterfNV"); + pAPI->glCombinerParameterivNV = (PFNGLCOMBINERPARAMETERIVNVPROC)glbGetProcAddress("glCombinerParameterivNV"); + pAPI->glCombinerParameteriNV = (PFNGLCOMBINERPARAMETERINVPROC)glbGetProcAddress("glCombinerParameteriNV"); + pAPI->glCombinerInputNV = (PFNGLCOMBINERINPUTNVPROC)glbGetProcAddress("glCombinerInputNV"); + pAPI->glCombinerOutputNV = (PFNGLCOMBINEROUTPUTNVPROC)glbGetProcAddress("glCombinerOutputNV"); + pAPI->glFinalCombinerInputNV = (PFNGLFINALCOMBINERINPUTNVPROC)glbGetProcAddress("glFinalCombinerInputNV"); + pAPI->glGetCombinerInputParameterfvNV = (PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC)glbGetProcAddress("glGetCombinerInputParameterfvNV"); + pAPI->glGetCombinerInputParameterivNV = (PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC)glbGetProcAddress("glGetCombinerInputParameterivNV"); + pAPI->glGetCombinerOutputParameterfvNV = (PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC)glbGetProcAddress("glGetCombinerOutputParameterfvNV"); + pAPI->glGetCombinerOutputParameterivNV = (PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC)glbGetProcAddress("glGetCombinerOutputParameterivNV"); + pAPI->glGetFinalCombinerInputParameterfvNV = (PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC)glbGetProcAddress("glGetFinalCombinerInputParameterfvNV"); + pAPI->glGetFinalCombinerInputParameterivNV = (PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC)glbGetProcAddress("glGetFinalCombinerInputParameterivNV"); + pAPI->glCombinerStageParameterfvNV = (PFNGLCOMBINERSTAGEPARAMETERFVNVPROC)glbGetProcAddress("glCombinerStageParameterfvNV"); + pAPI->glGetCombinerStageParameterfvNV = (PFNGLGETCOMBINERSTAGEPARAMETERFVNVPROC)glbGetProcAddress("glGetCombinerStageParameterfvNV"); + pAPI->glFramebufferSampleLocationsfvNV = (PFNGLFRAMEBUFFERSAMPLELOCATIONSFVNVPROC)glbGetProcAddress("glFramebufferSampleLocationsfvNV"); + pAPI->glNamedFramebufferSampleLocationsfvNV = (PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVNVPROC)glbGetProcAddress("glNamedFramebufferSampleLocationsfvNV"); + pAPI->glResolveDepthValuesNV = (PFNGLRESOLVEDEPTHVALUESNVPROC)glbGetProcAddress("glResolveDepthValuesNV"); + pAPI->glScissorExclusiveNV = (PFNGLSCISSOREXCLUSIVENVPROC)glbGetProcAddress("glScissorExclusiveNV"); + pAPI->glScissorExclusiveArrayvNV = (PFNGLSCISSOREXCLUSIVEARRAYVNVPROC)glbGetProcAddress("glScissorExclusiveArrayvNV"); + pAPI->glMakeBufferResidentNV = (PFNGLMAKEBUFFERRESIDENTNVPROC)glbGetProcAddress("glMakeBufferResidentNV"); + pAPI->glMakeBufferNonResidentNV = (PFNGLMAKEBUFFERNONRESIDENTNVPROC)glbGetProcAddress("glMakeBufferNonResidentNV"); + pAPI->glIsBufferResidentNV = (PFNGLISBUFFERRESIDENTNVPROC)glbGetProcAddress("glIsBufferResidentNV"); + pAPI->glMakeNamedBufferResidentNV = (PFNGLMAKENAMEDBUFFERRESIDENTNVPROC)glbGetProcAddress("glMakeNamedBufferResidentNV"); + pAPI->glMakeNamedBufferNonResidentNV = (PFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC)glbGetProcAddress("glMakeNamedBufferNonResidentNV"); + pAPI->glIsNamedBufferResidentNV = (PFNGLISNAMEDBUFFERRESIDENTNVPROC)glbGetProcAddress("glIsNamedBufferResidentNV"); + pAPI->glGetBufferParameterui64vNV = (PFNGLGETBUFFERPARAMETERUI64VNVPROC)glbGetProcAddress("glGetBufferParameterui64vNV"); + pAPI->glGetNamedBufferParameterui64vNV = (PFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC)glbGetProcAddress("glGetNamedBufferParameterui64vNV"); + pAPI->glGetIntegerui64vNV = (PFNGLGETINTEGERUI64VNVPROC)glbGetProcAddress("glGetIntegerui64vNV"); + pAPI->glUniformui64NV = (PFNGLUNIFORMUI64NVPROC)glbGetProcAddress("glUniformui64NV"); + pAPI->glUniformui64vNV = (PFNGLUNIFORMUI64VNVPROC)glbGetProcAddress("glUniformui64vNV"); + pAPI->glProgramUniformui64NV = (PFNGLPROGRAMUNIFORMUI64NVPROC)glbGetProcAddress("glProgramUniformui64NV"); + pAPI->glProgramUniformui64vNV = (PFNGLPROGRAMUNIFORMUI64VNVPROC)glbGetProcAddress("glProgramUniformui64vNV"); + pAPI->glBindShadingRateImageNV = (PFNGLBINDSHADINGRATEIMAGENVPROC)glbGetProcAddress("glBindShadingRateImageNV"); + pAPI->glGetShadingRateImagePaletteNV = (PFNGLGETSHADINGRATEIMAGEPALETTENVPROC)glbGetProcAddress("glGetShadingRateImagePaletteNV"); + pAPI->glGetShadingRateSampleLocationivNV = (PFNGLGETSHADINGRATESAMPLELOCATIONIVNVPROC)glbGetProcAddress("glGetShadingRateSampleLocationivNV"); + pAPI->glShadingRateImageBarrierNV = (PFNGLSHADINGRATEIMAGEBARRIERNVPROC)glbGetProcAddress("glShadingRateImageBarrierNV"); + pAPI->glShadingRateImagePaletteNV = (PFNGLSHADINGRATEIMAGEPALETTENVPROC)glbGetProcAddress("glShadingRateImagePaletteNV"); + pAPI->glShadingRateSampleOrderNV = (PFNGLSHADINGRATESAMPLEORDERNVPROC)glbGetProcAddress("glShadingRateSampleOrderNV"); + pAPI->glShadingRateSampleOrderCustomNV = (PFNGLSHADINGRATESAMPLEORDERCUSTOMNVPROC)glbGetProcAddress("glShadingRateSampleOrderCustomNV"); + pAPI->glTextureBarrierNV = (PFNGLTEXTUREBARRIERNVPROC)glbGetProcAddress("glTextureBarrierNV"); + pAPI->glTexImage2DMultisampleCoverageNV = (PFNGLTEXIMAGE2DMULTISAMPLECOVERAGENVPROC)glbGetProcAddress("glTexImage2DMultisampleCoverageNV"); + pAPI->glTexImage3DMultisampleCoverageNV = (PFNGLTEXIMAGE3DMULTISAMPLECOVERAGENVPROC)glbGetProcAddress("glTexImage3DMultisampleCoverageNV"); + pAPI->glTextureImage2DMultisampleNV = (PFNGLTEXTUREIMAGE2DMULTISAMPLENVPROC)glbGetProcAddress("glTextureImage2DMultisampleNV"); + pAPI->glTextureImage3DMultisampleNV = (PFNGLTEXTUREIMAGE3DMULTISAMPLENVPROC)glbGetProcAddress("glTextureImage3DMultisampleNV"); + pAPI->glTextureImage2DMultisampleCoverageNV = (PFNGLTEXTUREIMAGE2DMULTISAMPLECOVERAGENVPROC)glbGetProcAddress("glTextureImage2DMultisampleCoverageNV"); + pAPI->glTextureImage3DMultisampleCoverageNV = (PFNGLTEXTUREIMAGE3DMULTISAMPLECOVERAGENVPROC)glbGetProcAddress("glTextureImage3DMultisampleCoverageNV"); + pAPI->glBeginTransformFeedbackNV = (PFNGLBEGINTRANSFORMFEEDBACKNVPROC)glbGetProcAddress("glBeginTransformFeedbackNV"); + pAPI->glEndTransformFeedbackNV = (PFNGLENDTRANSFORMFEEDBACKNVPROC)glbGetProcAddress("glEndTransformFeedbackNV"); + pAPI->glTransformFeedbackAttribsNV = (PFNGLTRANSFORMFEEDBACKATTRIBSNVPROC)glbGetProcAddress("glTransformFeedbackAttribsNV"); + pAPI->glBindBufferRangeNV = (PFNGLBINDBUFFERRANGENVPROC)glbGetProcAddress("glBindBufferRangeNV"); + pAPI->glBindBufferOffsetNV = (PFNGLBINDBUFFEROFFSETNVPROC)glbGetProcAddress("glBindBufferOffsetNV"); + pAPI->glBindBufferBaseNV = (PFNGLBINDBUFFERBASENVPROC)glbGetProcAddress("glBindBufferBaseNV"); + pAPI->glTransformFeedbackVaryingsNV = (PFNGLTRANSFORMFEEDBACKVARYINGSNVPROC)glbGetProcAddress("glTransformFeedbackVaryingsNV"); + pAPI->glActiveVaryingNV = (PFNGLACTIVEVARYINGNVPROC)glbGetProcAddress("glActiveVaryingNV"); + pAPI->glGetVaryingLocationNV = (PFNGLGETVARYINGLOCATIONNVPROC)glbGetProcAddress("glGetVaryingLocationNV"); + pAPI->glGetActiveVaryingNV = (PFNGLGETACTIVEVARYINGNVPROC)glbGetProcAddress("glGetActiveVaryingNV"); + pAPI->glGetTransformFeedbackVaryingNV = (PFNGLGETTRANSFORMFEEDBACKVARYINGNVPROC)glbGetProcAddress("glGetTransformFeedbackVaryingNV"); + pAPI->glTransformFeedbackStreamAttribsNV = (PFNGLTRANSFORMFEEDBACKSTREAMATTRIBSNVPROC)glbGetProcAddress("glTransformFeedbackStreamAttribsNV"); + pAPI->glBindTransformFeedbackNV = (PFNGLBINDTRANSFORMFEEDBACKNVPROC)glbGetProcAddress("glBindTransformFeedbackNV"); + pAPI->glDeleteTransformFeedbacksNV = (PFNGLDELETETRANSFORMFEEDBACKSNVPROC)glbGetProcAddress("glDeleteTransformFeedbacksNV"); + pAPI->glGenTransformFeedbacksNV = (PFNGLGENTRANSFORMFEEDBACKSNVPROC)glbGetProcAddress("glGenTransformFeedbacksNV"); + pAPI->glIsTransformFeedbackNV = (PFNGLISTRANSFORMFEEDBACKNVPROC)glbGetProcAddress("glIsTransformFeedbackNV"); + pAPI->glPauseTransformFeedbackNV = (PFNGLPAUSETRANSFORMFEEDBACKNVPROC)glbGetProcAddress("glPauseTransformFeedbackNV"); + pAPI->glResumeTransformFeedbackNV = (PFNGLRESUMETRANSFORMFEEDBACKNVPROC)glbGetProcAddress("glResumeTransformFeedbackNV"); + pAPI->glDrawTransformFeedbackNV = (PFNGLDRAWTRANSFORMFEEDBACKNVPROC)glbGetProcAddress("glDrawTransformFeedbackNV"); + pAPI->glVDPAUInitNV = (PFNGLVDPAUINITNVPROC)glbGetProcAddress("glVDPAUInitNV"); + pAPI->glVDPAUFiniNV = (PFNGLVDPAUFININVPROC)glbGetProcAddress("glVDPAUFiniNV"); + pAPI->glVDPAURegisterVideoSurfaceNV = (PFNGLVDPAUREGISTERVIDEOSURFACENVPROC)glbGetProcAddress("glVDPAURegisterVideoSurfaceNV"); + pAPI->glVDPAURegisterOutputSurfaceNV = (PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC)glbGetProcAddress("glVDPAURegisterOutputSurfaceNV"); + pAPI->glVDPAUIsSurfaceNV = (PFNGLVDPAUISSURFACENVPROC)glbGetProcAddress("glVDPAUIsSurfaceNV"); + pAPI->glVDPAUUnregisterSurfaceNV = (PFNGLVDPAUUNREGISTERSURFACENVPROC)glbGetProcAddress("glVDPAUUnregisterSurfaceNV"); + pAPI->glVDPAUGetSurfaceivNV = (PFNGLVDPAUGETSURFACEIVNVPROC)glbGetProcAddress("glVDPAUGetSurfaceivNV"); + pAPI->glVDPAUSurfaceAccessNV = (PFNGLVDPAUSURFACEACCESSNVPROC)glbGetProcAddress("glVDPAUSurfaceAccessNV"); + pAPI->glVDPAUMapSurfacesNV = (PFNGLVDPAUMAPSURFACESNVPROC)glbGetProcAddress("glVDPAUMapSurfacesNV"); + pAPI->glVDPAUUnmapSurfacesNV = (PFNGLVDPAUUNMAPSURFACESNVPROC)glbGetProcAddress("glVDPAUUnmapSurfacesNV"); + pAPI->glVDPAURegisterVideoSurfaceWithPictureStructureNV = (PFNGLVDPAUREGISTERVIDEOSURFACEWITHPICTURESTRUCTURENVPROC)glbGetProcAddress("glVDPAURegisterVideoSurfaceWithPictureStructureNV"); + pAPI->glFlushVertexArrayRangeNV = (PFNGLFLUSHVERTEXARRAYRANGENVPROC)glbGetProcAddress("glFlushVertexArrayRangeNV"); + pAPI->glVertexArrayRangeNV = (PFNGLVERTEXARRAYRANGENVPROC)glbGetProcAddress("glVertexArrayRangeNV"); + pAPI->glVertexAttribL1i64NV = (PFNGLVERTEXATTRIBL1I64NVPROC)glbGetProcAddress("glVertexAttribL1i64NV"); + pAPI->glVertexAttribL2i64NV = (PFNGLVERTEXATTRIBL2I64NVPROC)glbGetProcAddress("glVertexAttribL2i64NV"); + pAPI->glVertexAttribL3i64NV = (PFNGLVERTEXATTRIBL3I64NVPROC)glbGetProcAddress("glVertexAttribL3i64NV"); + pAPI->glVertexAttribL4i64NV = (PFNGLVERTEXATTRIBL4I64NVPROC)glbGetProcAddress("glVertexAttribL4i64NV"); + pAPI->glVertexAttribL1i64vNV = (PFNGLVERTEXATTRIBL1I64VNVPROC)glbGetProcAddress("glVertexAttribL1i64vNV"); + pAPI->glVertexAttribL2i64vNV = (PFNGLVERTEXATTRIBL2I64VNVPROC)glbGetProcAddress("glVertexAttribL2i64vNV"); + pAPI->glVertexAttribL3i64vNV = (PFNGLVERTEXATTRIBL3I64VNVPROC)glbGetProcAddress("glVertexAttribL3i64vNV"); + pAPI->glVertexAttribL4i64vNV = (PFNGLVERTEXATTRIBL4I64VNVPROC)glbGetProcAddress("glVertexAttribL4i64vNV"); + pAPI->glVertexAttribL1ui64NV = (PFNGLVERTEXATTRIBL1UI64NVPROC)glbGetProcAddress("glVertexAttribL1ui64NV"); + pAPI->glVertexAttribL2ui64NV = (PFNGLVERTEXATTRIBL2UI64NVPROC)glbGetProcAddress("glVertexAttribL2ui64NV"); + pAPI->glVertexAttribL3ui64NV = (PFNGLVERTEXATTRIBL3UI64NVPROC)glbGetProcAddress("glVertexAttribL3ui64NV"); + pAPI->glVertexAttribL4ui64NV = (PFNGLVERTEXATTRIBL4UI64NVPROC)glbGetProcAddress("glVertexAttribL4ui64NV"); + pAPI->glVertexAttribL1ui64vNV = (PFNGLVERTEXATTRIBL1UI64VNVPROC)glbGetProcAddress("glVertexAttribL1ui64vNV"); + pAPI->glVertexAttribL2ui64vNV = (PFNGLVERTEXATTRIBL2UI64VNVPROC)glbGetProcAddress("glVertexAttribL2ui64vNV"); + pAPI->glVertexAttribL3ui64vNV = (PFNGLVERTEXATTRIBL3UI64VNVPROC)glbGetProcAddress("glVertexAttribL3ui64vNV"); + pAPI->glVertexAttribL4ui64vNV = (PFNGLVERTEXATTRIBL4UI64VNVPROC)glbGetProcAddress("glVertexAttribL4ui64vNV"); + pAPI->glGetVertexAttribLi64vNV = (PFNGLGETVERTEXATTRIBLI64VNVPROC)glbGetProcAddress("glGetVertexAttribLi64vNV"); + pAPI->glGetVertexAttribLui64vNV = (PFNGLGETVERTEXATTRIBLUI64VNVPROC)glbGetProcAddress("glGetVertexAttribLui64vNV"); + pAPI->glVertexAttribLFormatNV = (PFNGLVERTEXATTRIBLFORMATNVPROC)glbGetProcAddress("glVertexAttribLFormatNV"); + pAPI->glBufferAddressRangeNV = (PFNGLBUFFERADDRESSRANGENVPROC)glbGetProcAddress("glBufferAddressRangeNV"); + pAPI->glVertexFormatNV = (PFNGLVERTEXFORMATNVPROC)glbGetProcAddress("glVertexFormatNV"); + pAPI->glNormalFormatNV = (PFNGLNORMALFORMATNVPROC)glbGetProcAddress("glNormalFormatNV"); + pAPI->glColorFormatNV = (PFNGLCOLORFORMATNVPROC)glbGetProcAddress("glColorFormatNV"); + pAPI->glIndexFormatNV = (PFNGLINDEXFORMATNVPROC)glbGetProcAddress("glIndexFormatNV"); + pAPI->glTexCoordFormatNV = (PFNGLTEXCOORDFORMATNVPROC)glbGetProcAddress("glTexCoordFormatNV"); + pAPI->glEdgeFlagFormatNV = (PFNGLEDGEFLAGFORMATNVPROC)glbGetProcAddress("glEdgeFlagFormatNV"); + pAPI->glSecondaryColorFormatNV = (PFNGLSECONDARYCOLORFORMATNVPROC)glbGetProcAddress("glSecondaryColorFormatNV"); + pAPI->glFogCoordFormatNV = (PFNGLFOGCOORDFORMATNVPROC)glbGetProcAddress("glFogCoordFormatNV"); + pAPI->glVertexAttribFormatNV = (PFNGLVERTEXATTRIBFORMATNVPROC)glbGetProcAddress("glVertexAttribFormatNV"); + pAPI->glVertexAttribIFormatNV = (PFNGLVERTEXATTRIBIFORMATNVPROC)glbGetProcAddress("glVertexAttribIFormatNV"); + pAPI->glGetIntegerui64i_vNV = (PFNGLGETINTEGERUI64I_VNVPROC)glbGetProcAddress("glGetIntegerui64i_vNV"); + pAPI->glAreProgramsResidentNV = (PFNGLAREPROGRAMSRESIDENTNVPROC)glbGetProcAddress("glAreProgramsResidentNV"); + pAPI->glBindProgramNV = (PFNGLBINDPROGRAMNVPROC)glbGetProcAddress("glBindProgramNV"); + pAPI->glDeleteProgramsNV = (PFNGLDELETEPROGRAMSNVPROC)glbGetProcAddress("glDeleteProgramsNV"); + pAPI->glExecuteProgramNV = (PFNGLEXECUTEPROGRAMNVPROC)glbGetProcAddress("glExecuteProgramNV"); + pAPI->glGenProgramsNV = (PFNGLGENPROGRAMSNVPROC)glbGetProcAddress("glGenProgramsNV"); + pAPI->glGetProgramParameterdvNV = (PFNGLGETPROGRAMPARAMETERDVNVPROC)glbGetProcAddress("glGetProgramParameterdvNV"); + pAPI->glGetProgramParameterfvNV = (PFNGLGETPROGRAMPARAMETERFVNVPROC)glbGetProcAddress("glGetProgramParameterfvNV"); + pAPI->glGetProgramivNV = (PFNGLGETPROGRAMIVNVPROC)glbGetProcAddress("glGetProgramivNV"); + pAPI->glGetProgramStringNV = (PFNGLGETPROGRAMSTRINGNVPROC)glbGetProcAddress("glGetProgramStringNV"); + pAPI->glGetTrackMatrixivNV = (PFNGLGETTRACKMATRIXIVNVPROC)glbGetProcAddress("glGetTrackMatrixivNV"); + pAPI->glGetVertexAttribdvNV = (PFNGLGETVERTEXATTRIBDVNVPROC)glbGetProcAddress("glGetVertexAttribdvNV"); + pAPI->glGetVertexAttribfvNV = (PFNGLGETVERTEXATTRIBFVNVPROC)glbGetProcAddress("glGetVertexAttribfvNV"); + pAPI->glGetVertexAttribivNV = (PFNGLGETVERTEXATTRIBIVNVPROC)glbGetProcAddress("glGetVertexAttribivNV"); + pAPI->glGetVertexAttribPointervNV = (PFNGLGETVERTEXATTRIBPOINTERVNVPROC)glbGetProcAddress("glGetVertexAttribPointervNV"); + pAPI->glIsProgramNV = (PFNGLISPROGRAMNVPROC)glbGetProcAddress("glIsProgramNV"); + pAPI->glLoadProgramNV = (PFNGLLOADPROGRAMNVPROC)glbGetProcAddress("glLoadProgramNV"); + pAPI->glProgramParameter4dNV = (PFNGLPROGRAMPARAMETER4DNVPROC)glbGetProcAddress("glProgramParameter4dNV"); + pAPI->glProgramParameter4dvNV = (PFNGLPROGRAMPARAMETER4DVNVPROC)glbGetProcAddress("glProgramParameter4dvNV"); + pAPI->glProgramParameter4fNV = (PFNGLPROGRAMPARAMETER4FNVPROC)glbGetProcAddress("glProgramParameter4fNV"); + pAPI->glProgramParameter4fvNV = (PFNGLPROGRAMPARAMETER4FVNVPROC)glbGetProcAddress("glProgramParameter4fvNV"); + pAPI->glProgramParameters4dvNV = (PFNGLPROGRAMPARAMETERS4DVNVPROC)glbGetProcAddress("glProgramParameters4dvNV"); + pAPI->glProgramParameters4fvNV = (PFNGLPROGRAMPARAMETERS4FVNVPROC)glbGetProcAddress("glProgramParameters4fvNV"); + pAPI->glRequestResidentProgramsNV = (PFNGLREQUESTRESIDENTPROGRAMSNVPROC)glbGetProcAddress("glRequestResidentProgramsNV"); + pAPI->glTrackMatrixNV = (PFNGLTRACKMATRIXNVPROC)glbGetProcAddress("glTrackMatrixNV"); + pAPI->glVertexAttribPointerNV = (PFNGLVERTEXATTRIBPOINTERNVPROC)glbGetProcAddress("glVertexAttribPointerNV"); + pAPI->glVertexAttrib1dNV = (PFNGLVERTEXATTRIB1DNVPROC)glbGetProcAddress("glVertexAttrib1dNV"); + pAPI->glVertexAttrib1dvNV = (PFNGLVERTEXATTRIB1DVNVPROC)glbGetProcAddress("glVertexAttrib1dvNV"); + pAPI->glVertexAttrib1fNV = (PFNGLVERTEXATTRIB1FNVPROC)glbGetProcAddress("glVertexAttrib1fNV"); + pAPI->glVertexAttrib1fvNV = (PFNGLVERTEXATTRIB1FVNVPROC)glbGetProcAddress("glVertexAttrib1fvNV"); + pAPI->glVertexAttrib1sNV = (PFNGLVERTEXATTRIB1SNVPROC)glbGetProcAddress("glVertexAttrib1sNV"); + pAPI->glVertexAttrib1svNV = (PFNGLVERTEXATTRIB1SVNVPROC)glbGetProcAddress("glVertexAttrib1svNV"); + pAPI->glVertexAttrib2dNV = (PFNGLVERTEXATTRIB2DNVPROC)glbGetProcAddress("glVertexAttrib2dNV"); + pAPI->glVertexAttrib2dvNV = (PFNGLVERTEXATTRIB2DVNVPROC)glbGetProcAddress("glVertexAttrib2dvNV"); + pAPI->glVertexAttrib2fNV = (PFNGLVERTEXATTRIB2FNVPROC)glbGetProcAddress("glVertexAttrib2fNV"); + pAPI->glVertexAttrib2fvNV = (PFNGLVERTEXATTRIB2FVNVPROC)glbGetProcAddress("glVertexAttrib2fvNV"); + pAPI->glVertexAttrib2sNV = (PFNGLVERTEXATTRIB2SNVPROC)glbGetProcAddress("glVertexAttrib2sNV"); + pAPI->glVertexAttrib2svNV = (PFNGLVERTEXATTRIB2SVNVPROC)glbGetProcAddress("glVertexAttrib2svNV"); + pAPI->glVertexAttrib3dNV = (PFNGLVERTEXATTRIB3DNVPROC)glbGetProcAddress("glVertexAttrib3dNV"); + pAPI->glVertexAttrib3dvNV = (PFNGLVERTEXATTRIB3DVNVPROC)glbGetProcAddress("glVertexAttrib3dvNV"); + pAPI->glVertexAttrib3fNV = (PFNGLVERTEXATTRIB3FNVPROC)glbGetProcAddress("glVertexAttrib3fNV"); + pAPI->glVertexAttrib3fvNV = (PFNGLVERTEXATTRIB3FVNVPROC)glbGetProcAddress("glVertexAttrib3fvNV"); + pAPI->glVertexAttrib3sNV = (PFNGLVERTEXATTRIB3SNVPROC)glbGetProcAddress("glVertexAttrib3sNV"); + pAPI->glVertexAttrib3svNV = (PFNGLVERTEXATTRIB3SVNVPROC)glbGetProcAddress("glVertexAttrib3svNV"); + pAPI->glVertexAttrib4dNV = (PFNGLVERTEXATTRIB4DNVPROC)glbGetProcAddress("glVertexAttrib4dNV"); + pAPI->glVertexAttrib4dvNV = (PFNGLVERTEXATTRIB4DVNVPROC)glbGetProcAddress("glVertexAttrib4dvNV"); + pAPI->glVertexAttrib4fNV = (PFNGLVERTEXATTRIB4FNVPROC)glbGetProcAddress("glVertexAttrib4fNV"); + pAPI->glVertexAttrib4fvNV = (PFNGLVERTEXATTRIB4FVNVPROC)glbGetProcAddress("glVertexAttrib4fvNV"); + pAPI->glVertexAttrib4sNV = (PFNGLVERTEXATTRIB4SNVPROC)glbGetProcAddress("glVertexAttrib4sNV"); + pAPI->glVertexAttrib4svNV = (PFNGLVERTEXATTRIB4SVNVPROC)glbGetProcAddress("glVertexAttrib4svNV"); + pAPI->glVertexAttrib4ubNV = (PFNGLVERTEXATTRIB4UBNVPROC)glbGetProcAddress("glVertexAttrib4ubNV"); + pAPI->glVertexAttrib4ubvNV = (PFNGLVERTEXATTRIB4UBVNVPROC)glbGetProcAddress("glVertexAttrib4ubvNV"); + pAPI->glVertexAttribs1dvNV = (PFNGLVERTEXATTRIBS1DVNVPROC)glbGetProcAddress("glVertexAttribs1dvNV"); + pAPI->glVertexAttribs1fvNV = (PFNGLVERTEXATTRIBS1FVNVPROC)glbGetProcAddress("glVertexAttribs1fvNV"); + pAPI->glVertexAttribs1svNV = (PFNGLVERTEXATTRIBS1SVNVPROC)glbGetProcAddress("glVertexAttribs1svNV"); + pAPI->glVertexAttribs2dvNV = (PFNGLVERTEXATTRIBS2DVNVPROC)glbGetProcAddress("glVertexAttribs2dvNV"); + pAPI->glVertexAttribs2fvNV = (PFNGLVERTEXATTRIBS2FVNVPROC)glbGetProcAddress("glVertexAttribs2fvNV"); + pAPI->glVertexAttribs2svNV = (PFNGLVERTEXATTRIBS2SVNVPROC)glbGetProcAddress("glVertexAttribs2svNV"); + pAPI->glVertexAttribs3dvNV = (PFNGLVERTEXATTRIBS3DVNVPROC)glbGetProcAddress("glVertexAttribs3dvNV"); + pAPI->glVertexAttribs3fvNV = (PFNGLVERTEXATTRIBS3FVNVPROC)glbGetProcAddress("glVertexAttribs3fvNV"); + pAPI->glVertexAttribs3svNV = (PFNGLVERTEXATTRIBS3SVNVPROC)glbGetProcAddress("glVertexAttribs3svNV"); + pAPI->glVertexAttribs4dvNV = (PFNGLVERTEXATTRIBS4DVNVPROC)glbGetProcAddress("glVertexAttribs4dvNV"); + pAPI->glVertexAttribs4fvNV = (PFNGLVERTEXATTRIBS4FVNVPROC)glbGetProcAddress("glVertexAttribs4fvNV"); + pAPI->glVertexAttribs4svNV = (PFNGLVERTEXATTRIBS4SVNVPROC)glbGetProcAddress("glVertexAttribs4svNV"); + pAPI->glVertexAttribs4ubvNV = (PFNGLVERTEXATTRIBS4UBVNVPROC)glbGetProcAddress("glVertexAttribs4ubvNV"); + pAPI->glVertexAttribI1iEXT = (PFNGLVERTEXATTRIBI1IEXTPROC)glbGetProcAddress("glVertexAttribI1iEXT"); + pAPI->glVertexAttribI2iEXT = (PFNGLVERTEXATTRIBI2IEXTPROC)glbGetProcAddress("glVertexAttribI2iEXT"); + pAPI->glVertexAttribI3iEXT = (PFNGLVERTEXATTRIBI3IEXTPROC)glbGetProcAddress("glVertexAttribI3iEXT"); + pAPI->glVertexAttribI4iEXT = (PFNGLVERTEXATTRIBI4IEXTPROC)glbGetProcAddress("glVertexAttribI4iEXT"); + pAPI->glVertexAttribI1uiEXT = (PFNGLVERTEXATTRIBI1UIEXTPROC)glbGetProcAddress("glVertexAttribI1uiEXT"); + pAPI->glVertexAttribI2uiEXT = (PFNGLVERTEXATTRIBI2UIEXTPROC)glbGetProcAddress("glVertexAttribI2uiEXT"); + pAPI->glVertexAttribI3uiEXT = (PFNGLVERTEXATTRIBI3UIEXTPROC)glbGetProcAddress("glVertexAttribI3uiEXT"); + pAPI->glVertexAttribI4uiEXT = (PFNGLVERTEXATTRIBI4UIEXTPROC)glbGetProcAddress("glVertexAttribI4uiEXT"); + pAPI->glVertexAttribI1ivEXT = (PFNGLVERTEXATTRIBI1IVEXTPROC)glbGetProcAddress("glVertexAttribI1ivEXT"); + pAPI->glVertexAttribI2ivEXT = (PFNGLVERTEXATTRIBI2IVEXTPROC)glbGetProcAddress("glVertexAttribI2ivEXT"); + pAPI->glVertexAttribI3ivEXT = (PFNGLVERTEXATTRIBI3IVEXTPROC)glbGetProcAddress("glVertexAttribI3ivEXT"); + pAPI->glVertexAttribI4ivEXT = (PFNGLVERTEXATTRIBI4IVEXTPROC)glbGetProcAddress("glVertexAttribI4ivEXT"); + pAPI->glVertexAttribI1uivEXT = (PFNGLVERTEXATTRIBI1UIVEXTPROC)glbGetProcAddress("glVertexAttribI1uivEXT"); + pAPI->glVertexAttribI2uivEXT = (PFNGLVERTEXATTRIBI2UIVEXTPROC)glbGetProcAddress("glVertexAttribI2uivEXT"); + pAPI->glVertexAttribI3uivEXT = (PFNGLVERTEXATTRIBI3UIVEXTPROC)glbGetProcAddress("glVertexAttribI3uivEXT"); + pAPI->glVertexAttribI4uivEXT = (PFNGLVERTEXATTRIBI4UIVEXTPROC)glbGetProcAddress("glVertexAttribI4uivEXT"); + pAPI->glVertexAttribI4bvEXT = (PFNGLVERTEXATTRIBI4BVEXTPROC)glbGetProcAddress("glVertexAttribI4bvEXT"); + pAPI->glVertexAttribI4svEXT = (PFNGLVERTEXATTRIBI4SVEXTPROC)glbGetProcAddress("glVertexAttribI4svEXT"); + pAPI->glVertexAttribI4ubvEXT = (PFNGLVERTEXATTRIBI4UBVEXTPROC)glbGetProcAddress("glVertexAttribI4ubvEXT"); + pAPI->glVertexAttribI4usvEXT = (PFNGLVERTEXATTRIBI4USVEXTPROC)glbGetProcAddress("glVertexAttribI4usvEXT"); + pAPI->glVertexAttribIPointerEXT = (PFNGLVERTEXATTRIBIPOINTEREXTPROC)glbGetProcAddress("glVertexAttribIPointerEXT"); + pAPI->glGetVertexAttribIivEXT = (PFNGLGETVERTEXATTRIBIIVEXTPROC)glbGetProcAddress("glGetVertexAttribIivEXT"); + pAPI->glGetVertexAttribIuivEXT = (PFNGLGETVERTEXATTRIBIUIVEXTPROC)glbGetProcAddress("glGetVertexAttribIuivEXT"); + pAPI->glBeginVideoCaptureNV = (PFNGLBEGINVIDEOCAPTURENVPROC)glbGetProcAddress("glBeginVideoCaptureNV"); + pAPI->glBindVideoCaptureStreamBufferNV = (PFNGLBINDVIDEOCAPTURESTREAMBUFFERNVPROC)glbGetProcAddress("glBindVideoCaptureStreamBufferNV"); + pAPI->glBindVideoCaptureStreamTextureNV = (PFNGLBINDVIDEOCAPTURESTREAMTEXTURENVPROC)glbGetProcAddress("glBindVideoCaptureStreamTextureNV"); + pAPI->glEndVideoCaptureNV = (PFNGLENDVIDEOCAPTURENVPROC)glbGetProcAddress("glEndVideoCaptureNV"); + pAPI->glGetVideoCaptureivNV = (PFNGLGETVIDEOCAPTUREIVNVPROC)glbGetProcAddress("glGetVideoCaptureivNV"); + pAPI->glGetVideoCaptureStreamivNV = (PFNGLGETVIDEOCAPTURESTREAMIVNVPROC)glbGetProcAddress("glGetVideoCaptureStreamivNV"); + pAPI->glGetVideoCaptureStreamfvNV = (PFNGLGETVIDEOCAPTURESTREAMFVNVPROC)glbGetProcAddress("glGetVideoCaptureStreamfvNV"); + pAPI->glGetVideoCaptureStreamdvNV = (PFNGLGETVIDEOCAPTURESTREAMDVNVPROC)glbGetProcAddress("glGetVideoCaptureStreamdvNV"); + pAPI->glVideoCaptureNV = (PFNGLVIDEOCAPTURENVPROC)glbGetProcAddress("glVideoCaptureNV"); + pAPI->glVideoCaptureStreamParameterivNV = (PFNGLVIDEOCAPTURESTREAMPARAMETERIVNVPROC)glbGetProcAddress("glVideoCaptureStreamParameterivNV"); + pAPI->glVideoCaptureStreamParameterfvNV = (PFNGLVIDEOCAPTURESTREAMPARAMETERFVNVPROC)glbGetProcAddress("glVideoCaptureStreamParameterfvNV"); + pAPI->glVideoCaptureStreamParameterdvNV = (PFNGLVIDEOCAPTURESTREAMPARAMETERDVNVPROC)glbGetProcAddress("glVideoCaptureStreamParameterdvNV"); + pAPI->glViewportSwizzleNV = (PFNGLVIEWPORTSWIZZLENVPROC)glbGetProcAddress("glViewportSwizzleNV"); + pAPI->glMultiTexCoord1bOES = (PFNGLMULTITEXCOORD1BOESPROC)glbGetProcAddress("glMultiTexCoord1bOES"); + pAPI->glMultiTexCoord1bvOES = (PFNGLMULTITEXCOORD1BVOESPROC)glbGetProcAddress("glMultiTexCoord1bvOES"); + pAPI->glMultiTexCoord2bOES = (PFNGLMULTITEXCOORD2BOESPROC)glbGetProcAddress("glMultiTexCoord2bOES"); + pAPI->glMultiTexCoord2bvOES = (PFNGLMULTITEXCOORD2BVOESPROC)glbGetProcAddress("glMultiTexCoord2bvOES"); + pAPI->glMultiTexCoord3bOES = (PFNGLMULTITEXCOORD3BOESPROC)glbGetProcAddress("glMultiTexCoord3bOES"); + pAPI->glMultiTexCoord3bvOES = (PFNGLMULTITEXCOORD3BVOESPROC)glbGetProcAddress("glMultiTexCoord3bvOES"); + pAPI->glMultiTexCoord4bOES = (PFNGLMULTITEXCOORD4BOESPROC)glbGetProcAddress("glMultiTexCoord4bOES"); + pAPI->glMultiTexCoord4bvOES = (PFNGLMULTITEXCOORD4BVOESPROC)glbGetProcAddress("glMultiTexCoord4bvOES"); + pAPI->glTexCoord1bOES = (PFNGLTEXCOORD1BOESPROC)glbGetProcAddress("glTexCoord1bOES"); + pAPI->glTexCoord1bvOES = (PFNGLTEXCOORD1BVOESPROC)glbGetProcAddress("glTexCoord1bvOES"); + pAPI->glTexCoord2bOES = (PFNGLTEXCOORD2BOESPROC)glbGetProcAddress("glTexCoord2bOES"); + pAPI->glTexCoord2bvOES = (PFNGLTEXCOORD2BVOESPROC)glbGetProcAddress("glTexCoord2bvOES"); + pAPI->glTexCoord3bOES = (PFNGLTEXCOORD3BOESPROC)glbGetProcAddress("glTexCoord3bOES"); + pAPI->glTexCoord3bvOES = (PFNGLTEXCOORD3BVOESPROC)glbGetProcAddress("glTexCoord3bvOES"); + pAPI->glTexCoord4bOES = (PFNGLTEXCOORD4BOESPROC)glbGetProcAddress("glTexCoord4bOES"); + pAPI->glTexCoord4bvOES = (PFNGLTEXCOORD4BVOESPROC)glbGetProcAddress("glTexCoord4bvOES"); + pAPI->glVertex2bOES = (PFNGLVERTEX2BOESPROC)glbGetProcAddress("glVertex2bOES"); + pAPI->glVertex2bvOES = (PFNGLVERTEX2BVOESPROC)glbGetProcAddress("glVertex2bvOES"); + pAPI->glVertex3bOES = (PFNGLVERTEX3BOESPROC)glbGetProcAddress("glVertex3bOES"); + pAPI->glVertex3bvOES = (PFNGLVERTEX3BVOESPROC)glbGetProcAddress("glVertex3bvOES"); + pAPI->glVertex4bOES = (PFNGLVERTEX4BOESPROC)glbGetProcAddress("glVertex4bOES"); + pAPI->glVertex4bvOES = (PFNGLVERTEX4BVOESPROC)glbGetProcAddress("glVertex4bvOES"); + pAPI->glAlphaFuncxOES = (PFNGLALPHAFUNCXOESPROC)glbGetProcAddress("glAlphaFuncxOES"); + pAPI->glClearColorxOES = (PFNGLCLEARCOLORXOESPROC)glbGetProcAddress("glClearColorxOES"); + pAPI->glClearDepthxOES = (PFNGLCLEARDEPTHXOESPROC)glbGetProcAddress("glClearDepthxOES"); + pAPI->glClipPlanexOES = (PFNGLCLIPPLANEXOESPROC)glbGetProcAddress("glClipPlanexOES"); + pAPI->glColor4xOES = (PFNGLCOLOR4XOESPROC)glbGetProcAddress("glColor4xOES"); + pAPI->glDepthRangexOES = (PFNGLDEPTHRANGEXOESPROC)glbGetProcAddress("glDepthRangexOES"); + pAPI->glFogxOES = (PFNGLFOGXOESPROC)glbGetProcAddress("glFogxOES"); + pAPI->glFogxvOES = (PFNGLFOGXVOESPROC)glbGetProcAddress("glFogxvOES"); + pAPI->glFrustumxOES = (PFNGLFRUSTUMXOESPROC)glbGetProcAddress("glFrustumxOES"); + pAPI->glGetClipPlanexOES = (PFNGLGETCLIPPLANEXOESPROC)glbGetProcAddress("glGetClipPlanexOES"); + pAPI->glGetFixedvOES = (PFNGLGETFIXEDVOESPROC)glbGetProcAddress("glGetFixedvOES"); + pAPI->glGetTexEnvxvOES = (PFNGLGETTEXENVXVOESPROC)glbGetProcAddress("glGetTexEnvxvOES"); + pAPI->glGetTexParameterxvOES = (PFNGLGETTEXPARAMETERXVOESPROC)glbGetProcAddress("glGetTexParameterxvOES"); + pAPI->glLightModelxOES = (PFNGLLIGHTMODELXOESPROC)glbGetProcAddress("glLightModelxOES"); + pAPI->glLightModelxvOES = (PFNGLLIGHTMODELXVOESPROC)glbGetProcAddress("glLightModelxvOES"); + pAPI->glLightxOES = (PFNGLLIGHTXOESPROC)glbGetProcAddress("glLightxOES"); + pAPI->glLightxvOES = (PFNGLLIGHTXVOESPROC)glbGetProcAddress("glLightxvOES"); + pAPI->glLineWidthxOES = (PFNGLLINEWIDTHXOESPROC)glbGetProcAddress("glLineWidthxOES"); + pAPI->glLoadMatrixxOES = (PFNGLLOADMATRIXXOESPROC)glbGetProcAddress("glLoadMatrixxOES"); + pAPI->glMaterialxOES = (PFNGLMATERIALXOESPROC)glbGetProcAddress("glMaterialxOES"); + pAPI->glMaterialxvOES = (PFNGLMATERIALXVOESPROC)glbGetProcAddress("glMaterialxvOES"); + pAPI->glMultMatrixxOES = (PFNGLMULTMATRIXXOESPROC)glbGetProcAddress("glMultMatrixxOES"); + pAPI->glMultiTexCoord4xOES = (PFNGLMULTITEXCOORD4XOESPROC)glbGetProcAddress("glMultiTexCoord4xOES"); + pAPI->glNormal3xOES = (PFNGLNORMAL3XOESPROC)glbGetProcAddress("glNormal3xOES"); + pAPI->glOrthoxOES = (PFNGLORTHOXOESPROC)glbGetProcAddress("glOrthoxOES"); + pAPI->glPointParameterxvOES = (PFNGLPOINTPARAMETERXVOESPROC)glbGetProcAddress("glPointParameterxvOES"); + pAPI->glPointSizexOES = (PFNGLPOINTSIZEXOESPROC)glbGetProcAddress("glPointSizexOES"); + pAPI->glPolygonOffsetxOES = (PFNGLPOLYGONOFFSETXOESPROC)glbGetProcAddress("glPolygonOffsetxOES"); + pAPI->glRotatexOES = (PFNGLROTATEXOESPROC)glbGetProcAddress("glRotatexOES"); + pAPI->glScalexOES = (PFNGLSCALEXOESPROC)glbGetProcAddress("glScalexOES"); + pAPI->glTexEnvxOES = (PFNGLTEXENVXOESPROC)glbGetProcAddress("glTexEnvxOES"); + pAPI->glTexEnvxvOES = (PFNGLTEXENVXVOESPROC)glbGetProcAddress("glTexEnvxvOES"); + pAPI->glTexParameterxOES = (PFNGLTEXPARAMETERXOESPROC)glbGetProcAddress("glTexParameterxOES"); + pAPI->glTexParameterxvOES = (PFNGLTEXPARAMETERXVOESPROC)glbGetProcAddress("glTexParameterxvOES"); + pAPI->glTranslatexOES = (PFNGLTRANSLATEXOESPROC)glbGetProcAddress("glTranslatexOES"); + pAPI->glGetLightxvOES = (PFNGLGETLIGHTXVOESPROC)glbGetProcAddress("glGetLightxvOES"); + pAPI->glGetMaterialxvOES = (PFNGLGETMATERIALXVOESPROC)glbGetProcAddress("glGetMaterialxvOES"); + pAPI->glPointParameterxOES = (PFNGLPOINTPARAMETERXOESPROC)glbGetProcAddress("glPointParameterxOES"); + pAPI->glSampleCoveragexOES = (PFNGLSAMPLECOVERAGEXOESPROC)glbGetProcAddress("glSampleCoveragexOES"); + pAPI->glAccumxOES = (PFNGLACCUMXOESPROC)glbGetProcAddress("glAccumxOES"); + pAPI->glBitmapxOES = (PFNGLBITMAPXOESPROC)glbGetProcAddress("glBitmapxOES"); + pAPI->glBlendColorxOES = (PFNGLBLENDCOLORXOESPROC)glbGetProcAddress("glBlendColorxOES"); + pAPI->glClearAccumxOES = (PFNGLCLEARACCUMXOESPROC)glbGetProcAddress("glClearAccumxOES"); + pAPI->glColor3xOES = (PFNGLCOLOR3XOESPROC)glbGetProcAddress("glColor3xOES"); + pAPI->glColor3xvOES = (PFNGLCOLOR3XVOESPROC)glbGetProcAddress("glColor3xvOES"); + pAPI->glColor4xvOES = (PFNGLCOLOR4XVOESPROC)glbGetProcAddress("glColor4xvOES"); + pAPI->glConvolutionParameterxOES = (PFNGLCONVOLUTIONPARAMETERXOESPROC)glbGetProcAddress("glConvolutionParameterxOES"); + pAPI->glConvolutionParameterxvOES = (PFNGLCONVOLUTIONPARAMETERXVOESPROC)glbGetProcAddress("glConvolutionParameterxvOES"); + pAPI->glEvalCoord1xOES = (PFNGLEVALCOORD1XOESPROC)glbGetProcAddress("glEvalCoord1xOES"); + pAPI->glEvalCoord1xvOES = (PFNGLEVALCOORD1XVOESPROC)glbGetProcAddress("glEvalCoord1xvOES"); + pAPI->glEvalCoord2xOES = (PFNGLEVALCOORD2XOESPROC)glbGetProcAddress("glEvalCoord2xOES"); + pAPI->glEvalCoord2xvOES = (PFNGLEVALCOORD2XVOESPROC)glbGetProcAddress("glEvalCoord2xvOES"); + pAPI->glFeedbackBufferxOES = (PFNGLFEEDBACKBUFFERXOESPROC)glbGetProcAddress("glFeedbackBufferxOES"); + pAPI->glGetConvolutionParameterxvOES = (PFNGLGETCONVOLUTIONPARAMETERXVOESPROC)glbGetProcAddress("glGetConvolutionParameterxvOES"); + pAPI->glGetHistogramParameterxvOES = (PFNGLGETHISTOGRAMPARAMETERXVOESPROC)glbGetProcAddress("glGetHistogramParameterxvOES"); + pAPI->glGetLightxOES = (PFNGLGETLIGHTXOESPROC)glbGetProcAddress("glGetLightxOES"); + pAPI->glGetMapxvOES = (PFNGLGETMAPXVOESPROC)glbGetProcAddress("glGetMapxvOES"); + pAPI->glGetMaterialxOES = (PFNGLGETMATERIALXOESPROC)glbGetProcAddress("glGetMaterialxOES"); + pAPI->glGetPixelMapxv = (PFNGLGETPIXELMAPXVPROC)glbGetProcAddress("glGetPixelMapxv"); + pAPI->glGetTexGenxvOES = (PFNGLGETTEXGENXVOESPROC)glbGetProcAddress("glGetTexGenxvOES"); + pAPI->glGetTexLevelParameterxvOES = (PFNGLGETTEXLEVELPARAMETERXVOESPROC)glbGetProcAddress("glGetTexLevelParameterxvOES"); + pAPI->glIndexxOES = (PFNGLINDEXXOESPROC)glbGetProcAddress("glIndexxOES"); + pAPI->glIndexxvOES = (PFNGLINDEXXVOESPROC)glbGetProcAddress("glIndexxvOES"); + pAPI->glLoadTransposeMatrixxOES = (PFNGLLOADTRANSPOSEMATRIXXOESPROC)glbGetProcAddress("glLoadTransposeMatrixxOES"); + pAPI->glMap1xOES = (PFNGLMAP1XOESPROC)glbGetProcAddress("glMap1xOES"); + pAPI->glMap2xOES = (PFNGLMAP2XOESPROC)glbGetProcAddress("glMap2xOES"); + pAPI->glMapGrid1xOES = (PFNGLMAPGRID1XOESPROC)glbGetProcAddress("glMapGrid1xOES"); + pAPI->glMapGrid2xOES = (PFNGLMAPGRID2XOESPROC)glbGetProcAddress("glMapGrid2xOES"); + pAPI->glMultTransposeMatrixxOES = (PFNGLMULTTRANSPOSEMATRIXXOESPROC)glbGetProcAddress("glMultTransposeMatrixxOES"); + pAPI->glMultiTexCoord1xOES = (PFNGLMULTITEXCOORD1XOESPROC)glbGetProcAddress("glMultiTexCoord1xOES"); + pAPI->glMultiTexCoord1xvOES = (PFNGLMULTITEXCOORD1XVOESPROC)glbGetProcAddress("glMultiTexCoord1xvOES"); + pAPI->glMultiTexCoord2xOES = (PFNGLMULTITEXCOORD2XOESPROC)glbGetProcAddress("glMultiTexCoord2xOES"); + pAPI->glMultiTexCoord2xvOES = (PFNGLMULTITEXCOORD2XVOESPROC)glbGetProcAddress("glMultiTexCoord2xvOES"); + pAPI->glMultiTexCoord3xOES = (PFNGLMULTITEXCOORD3XOESPROC)glbGetProcAddress("glMultiTexCoord3xOES"); + pAPI->glMultiTexCoord3xvOES = (PFNGLMULTITEXCOORD3XVOESPROC)glbGetProcAddress("glMultiTexCoord3xvOES"); + pAPI->glMultiTexCoord4xvOES = (PFNGLMULTITEXCOORD4XVOESPROC)glbGetProcAddress("glMultiTexCoord4xvOES"); + pAPI->glNormal3xvOES = (PFNGLNORMAL3XVOESPROC)glbGetProcAddress("glNormal3xvOES"); + pAPI->glPassThroughxOES = (PFNGLPASSTHROUGHXOESPROC)glbGetProcAddress("glPassThroughxOES"); + pAPI->glPixelMapx = (PFNGLPIXELMAPXPROC)glbGetProcAddress("glPixelMapx"); + pAPI->glPixelStorex = (PFNGLPIXELSTOREXPROC)glbGetProcAddress("glPixelStorex"); + pAPI->glPixelTransferxOES = (PFNGLPIXELTRANSFERXOESPROC)glbGetProcAddress("glPixelTransferxOES"); + pAPI->glPixelZoomxOES = (PFNGLPIXELZOOMXOESPROC)glbGetProcAddress("glPixelZoomxOES"); + pAPI->glPrioritizeTexturesxOES = (PFNGLPRIORITIZETEXTURESXOESPROC)glbGetProcAddress("glPrioritizeTexturesxOES"); + pAPI->glRasterPos2xOES = (PFNGLRASTERPOS2XOESPROC)glbGetProcAddress("glRasterPos2xOES"); + pAPI->glRasterPos2xvOES = (PFNGLRASTERPOS2XVOESPROC)glbGetProcAddress("glRasterPos2xvOES"); + pAPI->glRasterPos3xOES = (PFNGLRASTERPOS3XOESPROC)glbGetProcAddress("glRasterPos3xOES"); + pAPI->glRasterPos3xvOES = (PFNGLRASTERPOS3XVOESPROC)glbGetProcAddress("glRasterPos3xvOES"); + pAPI->glRasterPos4xOES = (PFNGLRASTERPOS4XOESPROC)glbGetProcAddress("glRasterPos4xOES"); + pAPI->glRasterPos4xvOES = (PFNGLRASTERPOS4XVOESPROC)glbGetProcAddress("glRasterPos4xvOES"); + pAPI->glRectxOES = (PFNGLRECTXOESPROC)glbGetProcAddress("glRectxOES"); + pAPI->glRectxvOES = (PFNGLRECTXVOESPROC)glbGetProcAddress("glRectxvOES"); + pAPI->glTexCoord1xOES = (PFNGLTEXCOORD1XOESPROC)glbGetProcAddress("glTexCoord1xOES"); + pAPI->glTexCoord1xvOES = (PFNGLTEXCOORD1XVOESPROC)glbGetProcAddress("glTexCoord1xvOES"); + pAPI->glTexCoord2xOES = (PFNGLTEXCOORD2XOESPROC)glbGetProcAddress("glTexCoord2xOES"); + pAPI->glTexCoord2xvOES = (PFNGLTEXCOORD2XVOESPROC)glbGetProcAddress("glTexCoord2xvOES"); + pAPI->glTexCoord3xOES = (PFNGLTEXCOORD3XOESPROC)glbGetProcAddress("glTexCoord3xOES"); + pAPI->glTexCoord3xvOES = (PFNGLTEXCOORD3XVOESPROC)glbGetProcAddress("glTexCoord3xvOES"); + pAPI->glTexCoord4xOES = (PFNGLTEXCOORD4XOESPROC)glbGetProcAddress("glTexCoord4xOES"); + pAPI->glTexCoord4xvOES = (PFNGLTEXCOORD4XVOESPROC)glbGetProcAddress("glTexCoord4xvOES"); + pAPI->glTexGenxOES = (PFNGLTEXGENXOESPROC)glbGetProcAddress("glTexGenxOES"); + pAPI->glTexGenxvOES = (PFNGLTEXGENXVOESPROC)glbGetProcAddress("glTexGenxvOES"); + pAPI->glVertex2xOES = (PFNGLVERTEX2XOESPROC)glbGetProcAddress("glVertex2xOES"); + pAPI->glVertex2xvOES = (PFNGLVERTEX2XVOESPROC)glbGetProcAddress("glVertex2xvOES"); + pAPI->glVertex3xOES = (PFNGLVERTEX3XOESPROC)glbGetProcAddress("glVertex3xOES"); + pAPI->glVertex3xvOES = (PFNGLVERTEX3XVOESPROC)glbGetProcAddress("glVertex3xvOES"); + pAPI->glVertex4xOES = (PFNGLVERTEX4XOESPROC)glbGetProcAddress("glVertex4xOES"); + pAPI->glVertex4xvOES = (PFNGLVERTEX4XVOESPROC)glbGetProcAddress("glVertex4xvOES"); + pAPI->glQueryMatrixxOES = (PFNGLQUERYMATRIXXOESPROC)glbGetProcAddress("glQueryMatrixxOES"); + pAPI->glClearDepthfOES = (PFNGLCLEARDEPTHFOESPROC)glbGetProcAddress("glClearDepthfOES"); + pAPI->glClipPlanefOES = (PFNGLCLIPPLANEFOESPROC)glbGetProcAddress("glClipPlanefOES"); + pAPI->glDepthRangefOES = (PFNGLDEPTHRANGEFOESPROC)glbGetProcAddress("glDepthRangefOES"); + pAPI->glFrustumfOES = (PFNGLFRUSTUMFOESPROC)glbGetProcAddress("glFrustumfOES"); + pAPI->glGetClipPlanefOES = (PFNGLGETCLIPPLANEFOESPROC)glbGetProcAddress("glGetClipPlanefOES"); + pAPI->glOrthofOES = (PFNGLORTHOFOESPROC)glbGetProcAddress("glOrthofOES"); + pAPI->glFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)glbGetProcAddress("glFramebufferTextureMultiviewOVR"); + pAPI->glHintPGI = (PFNGLHINTPGIPROC)glbGetProcAddress("glHintPGI"); + pAPI->glDetailTexFuncSGIS = (PFNGLDETAILTEXFUNCSGISPROC)glbGetProcAddress("glDetailTexFuncSGIS"); + pAPI->glGetDetailTexFuncSGIS = (PFNGLGETDETAILTEXFUNCSGISPROC)glbGetProcAddress("glGetDetailTexFuncSGIS"); + pAPI->glFogFuncSGIS = (PFNGLFOGFUNCSGISPROC)glbGetProcAddress("glFogFuncSGIS"); + pAPI->glGetFogFuncSGIS = (PFNGLGETFOGFUNCSGISPROC)glbGetProcAddress("glGetFogFuncSGIS"); + pAPI->glSampleMaskSGIS = (PFNGLSAMPLEMASKSGISPROC)glbGetProcAddress("glSampleMaskSGIS"); + pAPI->glSamplePatternSGIS = (PFNGLSAMPLEPATTERNSGISPROC)glbGetProcAddress("glSamplePatternSGIS"); + pAPI->glPixelTexGenParameteriSGIS = (PFNGLPIXELTEXGENPARAMETERISGISPROC)glbGetProcAddress("glPixelTexGenParameteriSGIS"); + pAPI->glPixelTexGenParameterivSGIS = (PFNGLPIXELTEXGENPARAMETERIVSGISPROC)glbGetProcAddress("glPixelTexGenParameterivSGIS"); + pAPI->glPixelTexGenParameterfSGIS = (PFNGLPIXELTEXGENPARAMETERFSGISPROC)glbGetProcAddress("glPixelTexGenParameterfSGIS"); + pAPI->glPixelTexGenParameterfvSGIS = (PFNGLPIXELTEXGENPARAMETERFVSGISPROC)glbGetProcAddress("glPixelTexGenParameterfvSGIS"); + pAPI->glGetPixelTexGenParameterivSGIS = (PFNGLGETPIXELTEXGENPARAMETERIVSGISPROC)glbGetProcAddress("glGetPixelTexGenParameterivSGIS"); + pAPI->glGetPixelTexGenParameterfvSGIS = (PFNGLGETPIXELTEXGENPARAMETERFVSGISPROC)glbGetProcAddress("glGetPixelTexGenParameterfvSGIS"); + pAPI->glPointParameterfSGIS = (PFNGLPOINTPARAMETERFSGISPROC)glbGetProcAddress("glPointParameterfSGIS"); + pAPI->glPointParameterfvSGIS = (PFNGLPOINTPARAMETERFVSGISPROC)glbGetProcAddress("glPointParameterfvSGIS"); + pAPI->glSharpenTexFuncSGIS = (PFNGLSHARPENTEXFUNCSGISPROC)glbGetProcAddress("glSharpenTexFuncSGIS"); + pAPI->glGetSharpenTexFuncSGIS = (PFNGLGETSHARPENTEXFUNCSGISPROC)glbGetProcAddress("glGetSharpenTexFuncSGIS"); + pAPI->glTexImage4DSGIS = (PFNGLTEXIMAGE4DSGISPROC)glbGetProcAddress("glTexImage4DSGIS"); + pAPI->glTexSubImage4DSGIS = (PFNGLTEXSUBIMAGE4DSGISPROC)glbGetProcAddress("glTexSubImage4DSGIS"); + pAPI->glTextureColorMaskSGIS = (PFNGLTEXTURECOLORMASKSGISPROC)glbGetProcAddress("glTextureColorMaskSGIS"); + pAPI->glGetTexFilterFuncSGIS = (PFNGLGETTEXFILTERFUNCSGISPROC)glbGetProcAddress("glGetTexFilterFuncSGIS"); + pAPI->glTexFilterFuncSGIS = (PFNGLTEXFILTERFUNCSGISPROC)glbGetProcAddress("glTexFilterFuncSGIS"); + pAPI->glAsyncMarkerSGIX = (PFNGLASYNCMARKERSGIXPROC)glbGetProcAddress("glAsyncMarkerSGIX"); + pAPI->glFinishAsyncSGIX = (PFNGLFINISHASYNCSGIXPROC)glbGetProcAddress("glFinishAsyncSGIX"); + pAPI->glPollAsyncSGIX = (PFNGLPOLLASYNCSGIXPROC)glbGetProcAddress("glPollAsyncSGIX"); + pAPI->glGenAsyncMarkersSGIX = (PFNGLGENASYNCMARKERSSGIXPROC)glbGetProcAddress("glGenAsyncMarkersSGIX"); + pAPI->glDeleteAsyncMarkersSGIX = (PFNGLDELETEASYNCMARKERSSGIXPROC)glbGetProcAddress("glDeleteAsyncMarkersSGIX"); + pAPI->glIsAsyncMarkerSGIX = (PFNGLISASYNCMARKERSGIXPROC)glbGetProcAddress("glIsAsyncMarkerSGIX"); + pAPI->glFlushRasterSGIX = (PFNGLFLUSHRASTERSGIXPROC)glbGetProcAddress("glFlushRasterSGIX"); + pAPI->glFragmentColorMaterialSGIX = (PFNGLFRAGMENTCOLORMATERIALSGIXPROC)glbGetProcAddress("glFragmentColorMaterialSGIX"); + pAPI->glFragmentLightfSGIX = (PFNGLFRAGMENTLIGHTFSGIXPROC)glbGetProcAddress("glFragmentLightfSGIX"); + pAPI->glFragmentLightfvSGIX = (PFNGLFRAGMENTLIGHTFVSGIXPROC)glbGetProcAddress("glFragmentLightfvSGIX"); + pAPI->glFragmentLightiSGIX = (PFNGLFRAGMENTLIGHTISGIXPROC)glbGetProcAddress("glFragmentLightiSGIX"); + pAPI->glFragmentLightivSGIX = (PFNGLFRAGMENTLIGHTIVSGIXPROC)glbGetProcAddress("glFragmentLightivSGIX"); + pAPI->glFragmentLightModelfSGIX = (PFNGLFRAGMENTLIGHTMODELFSGIXPROC)glbGetProcAddress("glFragmentLightModelfSGIX"); + pAPI->glFragmentLightModelfvSGIX = (PFNGLFRAGMENTLIGHTMODELFVSGIXPROC)glbGetProcAddress("glFragmentLightModelfvSGIX"); + pAPI->glFragmentLightModeliSGIX = (PFNGLFRAGMENTLIGHTMODELISGIXPROC)glbGetProcAddress("glFragmentLightModeliSGIX"); + pAPI->glFragmentLightModelivSGIX = (PFNGLFRAGMENTLIGHTMODELIVSGIXPROC)glbGetProcAddress("glFragmentLightModelivSGIX"); + pAPI->glFragmentMaterialfSGIX = (PFNGLFRAGMENTMATERIALFSGIXPROC)glbGetProcAddress("glFragmentMaterialfSGIX"); + pAPI->glFragmentMaterialfvSGIX = (PFNGLFRAGMENTMATERIALFVSGIXPROC)glbGetProcAddress("glFragmentMaterialfvSGIX"); + pAPI->glFragmentMaterialiSGIX = (PFNGLFRAGMENTMATERIALISGIXPROC)glbGetProcAddress("glFragmentMaterialiSGIX"); + pAPI->glFragmentMaterialivSGIX = (PFNGLFRAGMENTMATERIALIVSGIXPROC)glbGetProcAddress("glFragmentMaterialivSGIX"); + pAPI->glGetFragmentLightfvSGIX = (PFNGLGETFRAGMENTLIGHTFVSGIXPROC)glbGetProcAddress("glGetFragmentLightfvSGIX"); + pAPI->glGetFragmentLightivSGIX = (PFNGLGETFRAGMENTLIGHTIVSGIXPROC)glbGetProcAddress("glGetFragmentLightivSGIX"); + pAPI->glGetFragmentMaterialfvSGIX = (PFNGLGETFRAGMENTMATERIALFVSGIXPROC)glbGetProcAddress("glGetFragmentMaterialfvSGIX"); + pAPI->glGetFragmentMaterialivSGIX = (PFNGLGETFRAGMENTMATERIALIVSGIXPROC)glbGetProcAddress("glGetFragmentMaterialivSGIX"); + pAPI->glLightEnviSGIX = (PFNGLLIGHTENVISGIXPROC)glbGetProcAddress("glLightEnviSGIX"); + pAPI->glFrameZoomSGIX = (PFNGLFRAMEZOOMSGIXPROC)glbGetProcAddress("glFrameZoomSGIX"); + pAPI->glIglooInterfaceSGIX = (PFNGLIGLOOINTERFACESGIXPROC)glbGetProcAddress("glIglooInterfaceSGIX"); + pAPI->glGetInstrumentsSGIX = (PFNGLGETINSTRUMENTSSGIXPROC)glbGetProcAddress("glGetInstrumentsSGIX"); + pAPI->glInstrumentsBufferSGIX = (PFNGLINSTRUMENTSBUFFERSGIXPROC)glbGetProcAddress("glInstrumentsBufferSGIX"); + pAPI->glPollInstrumentsSGIX = (PFNGLPOLLINSTRUMENTSSGIXPROC)glbGetProcAddress("glPollInstrumentsSGIX"); + pAPI->glReadInstrumentsSGIX = (PFNGLREADINSTRUMENTSSGIXPROC)glbGetProcAddress("glReadInstrumentsSGIX"); + pAPI->glStartInstrumentsSGIX = (PFNGLSTARTINSTRUMENTSSGIXPROC)glbGetProcAddress("glStartInstrumentsSGIX"); + pAPI->glStopInstrumentsSGIX = (PFNGLSTOPINSTRUMENTSSGIXPROC)glbGetProcAddress("glStopInstrumentsSGIX"); + pAPI->glGetListParameterfvSGIX = (PFNGLGETLISTPARAMETERFVSGIXPROC)glbGetProcAddress("glGetListParameterfvSGIX"); + pAPI->glGetListParameterivSGIX = (PFNGLGETLISTPARAMETERIVSGIXPROC)glbGetProcAddress("glGetListParameterivSGIX"); + pAPI->glListParameterfSGIX = (PFNGLLISTPARAMETERFSGIXPROC)glbGetProcAddress("glListParameterfSGIX"); + pAPI->glListParameterfvSGIX = (PFNGLLISTPARAMETERFVSGIXPROC)glbGetProcAddress("glListParameterfvSGIX"); + pAPI->glListParameteriSGIX = (PFNGLLISTPARAMETERISGIXPROC)glbGetProcAddress("glListParameteriSGIX"); + pAPI->glListParameterivSGIX = (PFNGLLISTPARAMETERIVSGIXPROC)glbGetProcAddress("glListParameterivSGIX"); + pAPI->glPixelTexGenSGIX = (PFNGLPIXELTEXGENSGIXPROC)glbGetProcAddress("glPixelTexGenSGIX"); + pAPI->glDeformationMap3dSGIX = (PFNGLDEFORMATIONMAP3DSGIXPROC)glbGetProcAddress("glDeformationMap3dSGIX"); + pAPI->glDeformationMap3fSGIX = (PFNGLDEFORMATIONMAP3FSGIXPROC)glbGetProcAddress("glDeformationMap3fSGIX"); + pAPI->glDeformSGIX = (PFNGLDEFORMSGIXPROC)glbGetProcAddress("glDeformSGIX"); + pAPI->glLoadIdentityDeformationMapSGIX = (PFNGLLOADIDENTITYDEFORMATIONMAPSGIXPROC)glbGetProcAddress("glLoadIdentityDeformationMapSGIX"); + pAPI->glReferencePlaneSGIX = (PFNGLREFERENCEPLANESGIXPROC)glbGetProcAddress("glReferencePlaneSGIX"); + pAPI->glSpriteParameterfSGIX = (PFNGLSPRITEPARAMETERFSGIXPROC)glbGetProcAddress("glSpriteParameterfSGIX"); + pAPI->glSpriteParameterfvSGIX = (PFNGLSPRITEPARAMETERFVSGIXPROC)glbGetProcAddress("glSpriteParameterfvSGIX"); + pAPI->glSpriteParameteriSGIX = (PFNGLSPRITEPARAMETERISGIXPROC)glbGetProcAddress("glSpriteParameteriSGIX"); + pAPI->glSpriteParameterivSGIX = (PFNGLSPRITEPARAMETERIVSGIXPROC)glbGetProcAddress("glSpriteParameterivSGIX"); + pAPI->glTagSampleBufferSGIX = (PFNGLTAGSAMPLEBUFFERSGIXPROC)glbGetProcAddress("glTagSampleBufferSGIX"); + pAPI->glColorTableSGI = (PFNGLCOLORTABLESGIPROC)glbGetProcAddress("glColorTableSGI"); + pAPI->glColorTableParameterfvSGI = (PFNGLCOLORTABLEPARAMETERFVSGIPROC)glbGetProcAddress("glColorTableParameterfvSGI"); + pAPI->glColorTableParameterivSGI = (PFNGLCOLORTABLEPARAMETERIVSGIPROC)glbGetProcAddress("glColorTableParameterivSGI"); + pAPI->glCopyColorTableSGI = (PFNGLCOPYCOLORTABLESGIPROC)glbGetProcAddress("glCopyColorTableSGI"); + pAPI->glGetColorTableSGI = (PFNGLGETCOLORTABLESGIPROC)glbGetProcAddress("glGetColorTableSGI"); + pAPI->glGetColorTableParameterfvSGI = (PFNGLGETCOLORTABLEPARAMETERFVSGIPROC)glbGetProcAddress("glGetColorTableParameterfvSGI"); + pAPI->glGetColorTableParameterivSGI = (PFNGLGETCOLORTABLEPARAMETERIVSGIPROC)glbGetProcAddress("glGetColorTableParameterivSGI"); + pAPI->glFinishTextureSUNX = (PFNGLFINISHTEXTURESUNXPROC)glbGetProcAddress("glFinishTextureSUNX"); + pAPI->glGlobalAlphaFactorbSUN = (PFNGLGLOBALALPHAFACTORBSUNPROC)glbGetProcAddress("glGlobalAlphaFactorbSUN"); + pAPI->glGlobalAlphaFactorsSUN = (PFNGLGLOBALALPHAFACTORSSUNPROC)glbGetProcAddress("glGlobalAlphaFactorsSUN"); + pAPI->glGlobalAlphaFactoriSUN = (PFNGLGLOBALALPHAFACTORISUNPROC)glbGetProcAddress("glGlobalAlphaFactoriSUN"); + pAPI->glGlobalAlphaFactorfSUN = (PFNGLGLOBALALPHAFACTORFSUNPROC)glbGetProcAddress("glGlobalAlphaFactorfSUN"); + pAPI->glGlobalAlphaFactordSUN = (PFNGLGLOBALALPHAFACTORDSUNPROC)glbGetProcAddress("glGlobalAlphaFactordSUN"); + pAPI->glGlobalAlphaFactorubSUN = (PFNGLGLOBALALPHAFACTORUBSUNPROC)glbGetProcAddress("glGlobalAlphaFactorubSUN"); + pAPI->glGlobalAlphaFactorusSUN = (PFNGLGLOBALALPHAFACTORUSSUNPROC)glbGetProcAddress("glGlobalAlphaFactorusSUN"); + pAPI->glGlobalAlphaFactoruiSUN = (PFNGLGLOBALALPHAFACTORUISUNPROC)glbGetProcAddress("glGlobalAlphaFactoruiSUN"); + pAPI->glDrawMeshArraysSUN = (PFNGLDRAWMESHARRAYSSUNPROC)glbGetProcAddress("glDrawMeshArraysSUN"); + pAPI->glReplacementCodeuiSUN = (PFNGLREPLACEMENTCODEUISUNPROC)glbGetProcAddress("glReplacementCodeuiSUN"); + pAPI->glReplacementCodeusSUN = (PFNGLREPLACEMENTCODEUSSUNPROC)glbGetProcAddress("glReplacementCodeusSUN"); + pAPI->glReplacementCodeubSUN = (PFNGLREPLACEMENTCODEUBSUNPROC)glbGetProcAddress("glReplacementCodeubSUN"); + pAPI->glReplacementCodeuivSUN = (PFNGLREPLACEMENTCODEUIVSUNPROC)glbGetProcAddress("glReplacementCodeuivSUN"); + pAPI->glReplacementCodeusvSUN = (PFNGLREPLACEMENTCODEUSVSUNPROC)glbGetProcAddress("glReplacementCodeusvSUN"); + pAPI->glReplacementCodeubvSUN = (PFNGLREPLACEMENTCODEUBVSUNPROC)glbGetProcAddress("glReplacementCodeubvSUN"); + pAPI->glReplacementCodePointerSUN = (PFNGLREPLACEMENTCODEPOINTERSUNPROC)glbGetProcAddress("glReplacementCodePointerSUN"); + pAPI->glColor4ubVertex2fSUN = (PFNGLCOLOR4UBVERTEX2FSUNPROC)glbGetProcAddress("glColor4ubVertex2fSUN"); + pAPI->glColor4ubVertex2fvSUN = (PFNGLCOLOR4UBVERTEX2FVSUNPROC)glbGetProcAddress("glColor4ubVertex2fvSUN"); + pAPI->glColor4ubVertex3fSUN = (PFNGLCOLOR4UBVERTEX3FSUNPROC)glbGetProcAddress("glColor4ubVertex3fSUN"); + pAPI->glColor4ubVertex3fvSUN = (PFNGLCOLOR4UBVERTEX3FVSUNPROC)glbGetProcAddress("glColor4ubVertex3fvSUN"); + pAPI->glColor3fVertex3fSUN = (PFNGLCOLOR3FVERTEX3FSUNPROC)glbGetProcAddress("glColor3fVertex3fSUN"); + pAPI->glColor3fVertex3fvSUN = (PFNGLCOLOR3FVERTEX3FVSUNPROC)glbGetProcAddress("glColor3fVertex3fvSUN"); + pAPI->glNormal3fVertex3fSUN = (PFNGLNORMAL3FVERTEX3FSUNPROC)glbGetProcAddress("glNormal3fVertex3fSUN"); + pAPI->glNormal3fVertex3fvSUN = (PFNGLNORMAL3FVERTEX3FVSUNPROC)glbGetProcAddress("glNormal3fVertex3fvSUN"); + pAPI->glColor4fNormal3fVertex3fSUN = (PFNGLCOLOR4FNORMAL3FVERTEX3FSUNPROC)glbGetProcAddress("glColor4fNormal3fVertex3fSUN"); + pAPI->glColor4fNormal3fVertex3fvSUN = (PFNGLCOLOR4FNORMAL3FVERTEX3FVSUNPROC)glbGetProcAddress("glColor4fNormal3fVertex3fvSUN"); + pAPI->glTexCoord2fVertex3fSUN = (PFNGLTEXCOORD2FVERTEX3FSUNPROC)glbGetProcAddress("glTexCoord2fVertex3fSUN"); + pAPI->glTexCoord2fVertex3fvSUN = (PFNGLTEXCOORD2FVERTEX3FVSUNPROC)glbGetProcAddress("glTexCoord2fVertex3fvSUN"); + pAPI->glTexCoord4fVertex4fSUN = (PFNGLTEXCOORD4FVERTEX4FSUNPROC)glbGetProcAddress("glTexCoord4fVertex4fSUN"); + pAPI->glTexCoord4fVertex4fvSUN = (PFNGLTEXCOORD4FVERTEX4FVSUNPROC)glbGetProcAddress("glTexCoord4fVertex4fvSUN"); + pAPI->glTexCoord2fColor4ubVertex3fSUN = (PFNGLTEXCOORD2FCOLOR4UBVERTEX3FSUNPROC)glbGetProcAddress("glTexCoord2fColor4ubVertex3fSUN"); + pAPI->glTexCoord2fColor4ubVertex3fvSUN = (PFNGLTEXCOORD2FCOLOR4UBVERTEX3FVSUNPROC)glbGetProcAddress("glTexCoord2fColor4ubVertex3fvSUN"); + pAPI->glTexCoord2fColor3fVertex3fSUN = (PFNGLTEXCOORD2FCOLOR3FVERTEX3FSUNPROC)glbGetProcAddress("glTexCoord2fColor3fVertex3fSUN"); + pAPI->glTexCoord2fColor3fVertex3fvSUN = (PFNGLTEXCOORD2FCOLOR3FVERTEX3FVSUNPROC)glbGetProcAddress("glTexCoord2fColor3fVertex3fvSUN"); + pAPI->glTexCoord2fNormal3fVertex3fSUN = (PFNGLTEXCOORD2FNORMAL3FVERTEX3FSUNPROC)glbGetProcAddress("glTexCoord2fNormal3fVertex3fSUN"); + pAPI->glTexCoord2fNormal3fVertex3fvSUN = (PFNGLTEXCOORD2FNORMAL3FVERTEX3FVSUNPROC)glbGetProcAddress("glTexCoord2fNormal3fVertex3fvSUN"); + pAPI->glTexCoord2fColor4fNormal3fVertex3fSUN = (PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC)glbGetProcAddress("glTexCoord2fColor4fNormal3fVertex3fSUN"); + pAPI->glTexCoord2fColor4fNormal3fVertex3fvSUN = (PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC)glbGetProcAddress("glTexCoord2fColor4fNormal3fVertex3fvSUN"); + pAPI->glTexCoord4fColor4fNormal3fVertex4fSUN = (PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FSUNPROC)glbGetProcAddress("glTexCoord4fColor4fNormal3fVertex4fSUN"); + pAPI->glTexCoord4fColor4fNormal3fVertex4fvSUN = (PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FVSUNPROC)glbGetProcAddress("glTexCoord4fColor4fNormal3fVertex4fvSUN"); + pAPI->glReplacementCodeuiVertex3fSUN = (PFNGLREPLACEMENTCODEUIVERTEX3FSUNPROC)glbGetProcAddress("glReplacementCodeuiVertex3fSUN"); + pAPI->glReplacementCodeuiVertex3fvSUN = (PFNGLREPLACEMENTCODEUIVERTEX3FVSUNPROC)glbGetProcAddress("glReplacementCodeuiVertex3fvSUN"); + pAPI->glReplacementCodeuiColor4ubVertex3fSUN = (PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FSUNPROC)glbGetProcAddress("glReplacementCodeuiColor4ubVertex3fSUN"); + pAPI->glReplacementCodeuiColor4ubVertex3fvSUN = (PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FVSUNPROC)glbGetProcAddress("glReplacementCodeuiColor4ubVertex3fvSUN"); + pAPI->glReplacementCodeuiColor3fVertex3fSUN = (PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FSUNPROC)glbGetProcAddress("glReplacementCodeuiColor3fVertex3fSUN"); + pAPI->glReplacementCodeuiColor3fVertex3fvSUN = (PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FVSUNPROC)glbGetProcAddress("glReplacementCodeuiColor3fVertex3fvSUN"); + pAPI->glReplacementCodeuiNormal3fVertex3fSUN = (PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FSUNPROC)glbGetProcAddress("glReplacementCodeuiNormal3fVertex3fSUN"); + pAPI->glReplacementCodeuiNormal3fVertex3fvSUN = (PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FVSUNPROC)glbGetProcAddress("glReplacementCodeuiNormal3fVertex3fvSUN"); + pAPI->glReplacementCodeuiColor4fNormal3fVertex3fSUN = (PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FSUNPROC)glbGetProcAddress("glReplacementCodeuiColor4fNormal3fVertex3fSUN"); + pAPI->glReplacementCodeuiColor4fNormal3fVertex3fvSUN = (PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FVSUNPROC)glbGetProcAddress("glReplacementCodeuiColor4fNormal3fVertex3fvSUN"); + pAPI->glReplacementCodeuiTexCoord2fVertex3fSUN = (PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FSUNPROC)glbGetProcAddress("glReplacementCodeuiTexCoord2fVertex3fSUN"); + pAPI->glReplacementCodeuiTexCoord2fVertex3fvSUN = (PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FVSUNPROC)glbGetProcAddress("glReplacementCodeuiTexCoord2fVertex3fvSUN"); + pAPI->glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN = (PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FSUNPROC)glbGetProcAddress("glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN"); + pAPI->glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN = (PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FVSUNPROC)glbGetProcAddress("glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN"); + pAPI->glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN = (PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC)glbGetProcAddress("glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN"); + pAPI->glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN = (PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC)glbGetProcAddress("glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN"); +#if defined(GLBIND_WGL) + pAPI->wglSetStereoEmitterState3DL = (PFNWGLSETSTEREOEMITTERSTATE3DLPROC)glbGetProcAddress("wglSetStereoEmitterState3DL"); + pAPI->wglGetGPUIDsAMD = (PFNWGLGETGPUIDSAMDPROC)glbGetProcAddress("wglGetGPUIDsAMD"); + pAPI->wglGetGPUInfoAMD = (PFNWGLGETGPUINFOAMDPROC)glbGetProcAddress("wglGetGPUInfoAMD"); + pAPI->wglGetContextGPUIDAMD = (PFNWGLGETCONTEXTGPUIDAMDPROC)glbGetProcAddress("wglGetContextGPUIDAMD"); + pAPI->wglCreateAssociatedContextAMD = (PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC)glbGetProcAddress("wglCreateAssociatedContextAMD"); + pAPI->wglCreateAssociatedContextAttribsAMD = (PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC)glbGetProcAddress("wglCreateAssociatedContextAttribsAMD"); + pAPI->wglDeleteAssociatedContextAMD = (PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC)glbGetProcAddress("wglDeleteAssociatedContextAMD"); + pAPI->wglMakeAssociatedContextCurrentAMD = (PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC)glbGetProcAddress("wglMakeAssociatedContextCurrentAMD"); + pAPI->wglGetCurrentAssociatedContextAMD = (PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC)glbGetProcAddress("wglGetCurrentAssociatedContextAMD"); + pAPI->wglBlitContextFramebufferAMD = (PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC)glbGetProcAddress("wglBlitContextFramebufferAMD"); + pAPI->wglCreateBufferRegionARB = (PFNWGLCREATEBUFFERREGIONARBPROC)glbGetProcAddress("wglCreateBufferRegionARB"); + pAPI->wglDeleteBufferRegionARB = (PFNWGLDELETEBUFFERREGIONARBPROC)glbGetProcAddress("wglDeleteBufferRegionARB"); + pAPI->wglSaveBufferRegionARB = (PFNWGLSAVEBUFFERREGIONARBPROC)glbGetProcAddress("wglSaveBufferRegionARB"); + pAPI->wglRestoreBufferRegionARB = (PFNWGLRESTOREBUFFERREGIONARBPROC)glbGetProcAddress("wglRestoreBufferRegionARB"); + pAPI->wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)glbGetProcAddress("wglCreateContextAttribsARB"); + pAPI->wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)glbGetProcAddress("wglGetExtensionsStringARB"); + pAPI->wglMakeContextCurrentARB = (PFNWGLMAKECONTEXTCURRENTARBPROC)glbGetProcAddress("wglMakeContextCurrentARB"); + pAPI->wglGetCurrentReadDCARB = (PFNWGLGETCURRENTREADDCARBPROC)glbGetProcAddress("wglGetCurrentReadDCARB"); + pAPI->wglCreatePbufferARB = (PFNWGLCREATEPBUFFERARBPROC)glbGetProcAddress("wglCreatePbufferARB"); + pAPI->wglGetPbufferDCARB = (PFNWGLGETPBUFFERDCARBPROC)glbGetProcAddress("wglGetPbufferDCARB"); + pAPI->wglReleasePbufferDCARB = (PFNWGLRELEASEPBUFFERDCARBPROC)glbGetProcAddress("wglReleasePbufferDCARB"); + pAPI->wglDestroyPbufferARB = (PFNWGLDESTROYPBUFFERARBPROC)glbGetProcAddress("wglDestroyPbufferARB"); + pAPI->wglQueryPbufferARB = (PFNWGLQUERYPBUFFERARBPROC)glbGetProcAddress("wglQueryPbufferARB"); + pAPI->wglGetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)glbGetProcAddress("wglGetPixelFormatAttribivARB"); + pAPI->wglGetPixelFormatAttribfvARB = (PFNWGLGETPIXELFORMATATTRIBFVARBPROC)glbGetProcAddress("wglGetPixelFormatAttribfvARB"); + pAPI->wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)glbGetProcAddress("wglChoosePixelFormatARB"); + pAPI->wglBindTexImageARB = (PFNWGLBINDTEXIMAGEARBPROC)glbGetProcAddress("wglBindTexImageARB"); + pAPI->wglReleaseTexImageARB = (PFNWGLRELEASETEXIMAGEARBPROC)glbGetProcAddress("wglReleaseTexImageARB"); + pAPI->wglSetPbufferAttribARB = (PFNWGLSETPBUFFERATTRIBARBPROC)glbGetProcAddress("wglSetPbufferAttribARB"); + pAPI->wglCreateDisplayColorTableEXT = (PFNWGLCREATEDISPLAYCOLORTABLEEXTPROC)glbGetProcAddress("wglCreateDisplayColorTableEXT"); + pAPI->wglLoadDisplayColorTableEXT = (PFNWGLLOADDISPLAYCOLORTABLEEXTPROC)glbGetProcAddress("wglLoadDisplayColorTableEXT"); + pAPI->wglBindDisplayColorTableEXT = (PFNWGLBINDDISPLAYCOLORTABLEEXTPROC)glbGetProcAddress("wglBindDisplayColorTableEXT"); + pAPI->wglDestroyDisplayColorTableEXT = (PFNWGLDESTROYDISPLAYCOLORTABLEEXTPROC)glbGetProcAddress("wglDestroyDisplayColorTableEXT"); + pAPI->wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)glbGetProcAddress("wglGetExtensionsStringEXT"); + pAPI->wglMakeContextCurrentEXT = (PFNWGLMAKECONTEXTCURRENTEXTPROC)glbGetProcAddress("wglMakeContextCurrentEXT"); + pAPI->wglGetCurrentReadDCEXT = (PFNWGLGETCURRENTREADDCEXTPROC)glbGetProcAddress("wglGetCurrentReadDCEXT"); + pAPI->wglCreatePbufferEXT = (PFNWGLCREATEPBUFFEREXTPROC)glbGetProcAddress("wglCreatePbufferEXT"); + pAPI->wglGetPbufferDCEXT = (PFNWGLGETPBUFFERDCEXTPROC)glbGetProcAddress("wglGetPbufferDCEXT"); + pAPI->wglReleasePbufferDCEXT = (PFNWGLRELEASEPBUFFERDCEXTPROC)glbGetProcAddress("wglReleasePbufferDCEXT"); + pAPI->wglDestroyPbufferEXT = (PFNWGLDESTROYPBUFFEREXTPROC)glbGetProcAddress("wglDestroyPbufferEXT"); + pAPI->wglQueryPbufferEXT = (PFNWGLQUERYPBUFFEREXTPROC)glbGetProcAddress("wglQueryPbufferEXT"); + pAPI->wglGetPixelFormatAttribivEXT = (PFNWGLGETPIXELFORMATATTRIBIVEXTPROC)glbGetProcAddress("wglGetPixelFormatAttribivEXT"); + pAPI->wglGetPixelFormatAttribfvEXT = (PFNWGLGETPIXELFORMATATTRIBFVEXTPROC)glbGetProcAddress("wglGetPixelFormatAttribfvEXT"); + pAPI->wglChoosePixelFormatEXT = (PFNWGLCHOOSEPIXELFORMATEXTPROC)glbGetProcAddress("wglChoosePixelFormatEXT"); + pAPI->wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)glbGetProcAddress("wglSwapIntervalEXT"); + pAPI->wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)glbGetProcAddress("wglGetSwapIntervalEXT"); + pAPI->wglGetDigitalVideoParametersI3D = (PFNWGLGETDIGITALVIDEOPARAMETERSI3DPROC)glbGetProcAddress("wglGetDigitalVideoParametersI3D"); + pAPI->wglSetDigitalVideoParametersI3D = (PFNWGLSETDIGITALVIDEOPARAMETERSI3DPROC)glbGetProcAddress("wglSetDigitalVideoParametersI3D"); + pAPI->wglGetGammaTableParametersI3D = (PFNWGLGETGAMMATABLEPARAMETERSI3DPROC)glbGetProcAddress("wglGetGammaTableParametersI3D"); + pAPI->wglSetGammaTableParametersI3D = (PFNWGLSETGAMMATABLEPARAMETERSI3DPROC)glbGetProcAddress("wglSetGammaTableParametersI3D"); + pAPI->wglGetGammaTableI3D = (PFNWGLGETGAMMATABLEI3DPROC)glbGetProcAddress("wglGetGammaTableI3D"); + pAPI->wglSetGammaTableI3D = (PFNWGLSETGAMMATABLEI3DPROC)glbGetProcAddress("wglSetGammaTableI3D"); + pAPI->wglEnableGenlockI3D = (PFNWGLENABLEGENLOCKI3DPROC)glbGetProcAddress("wglEnableGenlockI3D"); + pAPI->wglDisableGenlockI3D = (PFNWGLDISABLEGENLOCKI3DPROC)glbGetProcAddress("wglDisableGenlockI3D"); + pAPI->wglIsEnabledGenlockI3D = (PFNWGLISENABLEDGENLOCKI3DPROC)glbGetProcAddress("wglIsEnabledGenlockI3D"); + pAPI->wglGenlockSourceI3D = (PFNWGLGENLOCKSOURCEI3DPROC)glbGetProcAddress("wglGenlockSourceI3D"); + pAPI->wglGetGenlockSourceI3D = (PFNWGLGETGENLOCKSOURCEI3DPROC)glbGetProcAddress("wglGetGenlockSourceI3D"); + pAPI->wglGenlockSourceEdgeI3D = (PFNWGLGENLOCKSOURCEEDGEI3DPROC)glbGetProcAddress("wglGenlockSourceEdgeI3D"); + pAPI->wglGetGenlockSourceEdgeI3D = (PFNWGLGETGENLOCKSOURCEEDGEI3DPROC)glbGetProcAddress("wglGetGenlockSourceEdgeI3D"); + pAPI->wglGenlockSampleRateI3D = (PFNWGLGENLOCKSAMPLERATEI3DPROC)glbGetProcAddress("wglGenlockSampleRateI3D"); + pAPI->wglGetGenlockSampleRateI3D = (PFNWGLGETGENLOCKSAMPLERATEI3DPROC)glbGetProcAddress("wglGetGenlockSampleRateI3D"); + pAPI->wglGenlockSourceDelayI3D = (PFNWGLGENLOCKSOURCEDELAYI3DPROC)glbGetProcAddress("wglGenlockSourceDelayI3D"); + pAPI->wglGetGenlockSourceDelayI3D = (PFNWGLGETGENLOCKSOURCEDELAYI3DPROC)glbGetProcAddress("wglGetGenlockSourceDelayI3D"); + pAPI->wglQueryGenlockMaxSourceDelayI3D = (PFNWGLQUERYGENLOCKMAXSOURCEDELAYI3DPROC)glbGetProcAddress("wglQueryGenlockMaxSourceDelayI3D"); + pAPI->wglCreateImageBufferI3D = (PFNWGLCREATEIMAGEBUFFERI3DPROC)glbGetProcAddress("wglCreateImageBufferI3D"); + pAPI->wglDestroyImageBufferI3D = (PFNWGLDESTROYIMAGEBUFFERI3DPROC)glbGetProcAddress("wglDestroyImageBufferI3D"); + pAPI->wglAssociateImageBufferEventsI3D = (PFNWGLASSOCIATEIMAGEBUFFEREVENTSI3DPROC)glbGetProcAddress("wglAssociateImageBufferEventsI3D"); + pAPI->wglReleaseImageBufferEventsI3D = (PFNWGLRELEASEIMAGEBUFFEREVENTSI3DPROC)glbGetProcAddress("wglReleaseImageBufferEventsI3D"); + pAPI->wglEnableFrameLockI3D = (PFNWGLENABLEFRAMELOCKI3DPROC)glbGetProcAddress("wglEnableFrameLockI3D"); + pAPI->wglDisableFrameLockI3D = (PFNWGLDISABLEFRAMELOCKI3DPROC)glbGetProcAddress("wglDisableFrameLockI3D"); + pAPI->wglIsEnabledFrameLockI3D = (PFNWGLISENABLEDFRAMELOCKI3DPROC)glbGetProcAddress("wglIsEnabledFrameLockI3D"); + pAPI->wglQueryFrameLockMasterI3D = (PFNWGLQUERYFRAMELOCKMASTERI3DPROC)glbGetProcAddress("wglQueryFrameLockMasterI3D"); + pAPI->wglGetFrameUsageI3D = (PFNWGLGETFRAMEUSAGEI3DPROC)glbGetProcAddress("wglGetFrameUsageI3D"); + pAPI->wglBeginFrameTrackingI3D = (PFNWGLBEGINFRAMETRACKINGI3DPROC)glbGetProcAddress("wglBeginFrameTrackingI3D"); + pAPI->wglEndFrameTrackingI3D = (PFNWGLENDFRAMETRACKINGI3DPROC)glbGetProcAddress("wglEndFrameTrackingI3D"); + pAPI->wglQueryFrameTrackingI3D = (PFNWGLQUERYFRAMETRACKINGI3DPROC)glbGetProcAddress("wglQueryFrameTrackingI3D"); + pAPI->wglCopyImageSubDataNV = (PFNWGLCOPYIMAGESUBDATANVPROC)glbGetProcAddress("wglCopyImageSubDataNV"); + pAPI->wglDelayBeforeSwapNV = (PFNWGLDELAYBEFORESWAPNVPROC)glbGetProcAddress("wglDelayBeforeSwapNV"); + pAPI->wglDXSetResourceShareHandleNV = (PFNWGLDXSETRESOURCESHAREHANDLENVPROC)glbGetProcAddress("wglDXSetResourceShareHandleNV"); + pAPI->wglDXOpenDeviceNV = (PFNWGLDXOPENDEVICENVPROC)glbGetProcAddress("wglDXOpenDeviceNV"); + pAPI->wglDXCloseDeviceNV = (PFNWGLDXCLOSEDEVICENVPROC)glbGetProcAddress("wglDXCloseDeviceNV"); + pAPI->wglDXRegisterObjectNV = (PFNWGLDXREGISTEROBJECTNVPROC)glbGetProcAddress("wglDXRegisterObjectNV"); + pAPI->wglDXUnregisterObjectNV = (PFNWGLDXUNREGISTEROBJECTNVPROC)glbGetProcAddress("wglDXUnregisterObjectNV"); + pAPI->wglDXObjectAccessNV = (PFNWGLDXOBJECTACCESSNVPROC)glbGetProcAddress("wglDXObjectAccessNV"); + pAPI->wglDXLockObjectsNV = (PFNWGLDXLOCKOBJECTSNVPROC)glbGetProcAddress("wglDXLockObjectsNV"); + pAPI->wglDXUnlockObjectsNV = (PFNWGLDXUNLOCKOBJECTSNVPROC)glbGetProcAddress("wglDXUnlockObjectsNV"); + pAPI->wglEnumGpusNV = (PFNWGLENUMGPUSNVPROC)glbGetProcAddress("wglEnumGpusNV"); + pAPI->wglEnumGpuDevicesNV = (PFNWGLENUMGPUDEVICESNVPROC)glbGetProcAddress("wglEnumGpuDevicesNV"); + pAPI->wglCreateAffinityDCNV = (PFNWGLCREATEAFFINITYDCNVPROC)glbGetProcAddress("wglCreateAffinityDCNV"); + pAPI->wglEnumGpusFromAffinityDCNV = (PFNWGLENUMGPUSFROMAFFINITYDCNVPROC)glbGetProcAddress("wglEnumGpusFromAffinityDCNV"); + pAPI->wglDeleteDCNV = (PFNWGLDELETEDCNVPROC)glbGetProcAddress("wglDeleteDCNV"); + pAPI->wglEnumerateVideoDevicesNV = (PFNWGLENUMERATEVIDEODEVICESNVPROC)glbGetProcAddress("wglEnumerateVideoDevicesNV"); + pAPI->wglBindVideoDeviceNV = (PFNWGLBINDVIDEODEVICENVPROC)glbGetProcAddress("wglBindVideoDeviceNV"); + pAPI->wglQueryCurrentContextNV = (PFNWGLQUERYCURRENTCONTEXTNVPROC)glbGetProcAddress("wglQueryCurrentContextNV"); + pAPI->wglJoinSwapGroupNV = (PFNWGLJOINSWAPGROUPNVPROC)glbGetProcAddress("wglJoinSwapGroupNV"); + pAPI->wglBindSwapBarrierNV = (PFNWGLBINDSWAPBARRIERNVPROC)glbGetProcAddress("wglBindSwapBarrierNV"); + pAPI->wglQuerySwapGroupNV = (PFNWGLQUERYSWAPGROUPNVPROC)glbGetProcAddress("wglQuerySwapGroupNV"); + pAPI->wglQueryMaxSwapGroupsNV = (PFNWGLQUERYMAXSWAPGROUPSNVPROC)glbGetProcAddress("wglQueryMaxSwapGroupsNV"); + pAPI->wglQueryFrameCountNV = (PFNWGLQUERYFRAMECOUNTNVPROC)glbGetProcAddress("wglQueryFrameCountNV"); + pAPI->wglResetFrameCountNV = (PFNWGLRESETFRAMECOUNTNVPROC)glbGetProcAddress("wglResetFrameCountNV"); + pAPI->wglBindVideoCaptureDeviceNV = (PFNWGLBINDVIDEOCAPTUREDEVICENVPROC)glbGetProcAddress("wglBindVideoCaptureDeviceNV"); + pAPI->wglEnumerateVideoCaptureDevicesNV = (PFNWGLENUMERATEVIDEOCAPTUREDEVICESNVPROC)glbGetProcAddress("wglEnumerateVideoCaptureDevicesNV"); + pAPI->wglLockVideoCaptureDeviceNV = (PFNWGLLOCKVIDEOCAPTUREDEVICENVPROC)glbGetProcAddress("wglLockVideoCaptureDeviceNV"); + pAPI->wglQueryVideoCaptureDeviceNV = (PFNWGLQUERYVIDEOCAPTUREDEVICENVPROC)glbGetProcAddress("wglQueryVideoCaptureDeviceNV"); + pAPI->wglReleaseVideoCaptureDeviceNV = (PFNWGLRELEASEVIDEOCAPTUREDEVICENVPROC)glbGetProcAddress("wglReleaseVideoCaptureDeviceNV"); + pAPI->wglGetVideoDeviceNV = (PFNWGLGETVIDEODEVICENVPROC)glbGetProcAddress("wglGetVideoDeviceNV"); + pAPI->wglReleaseVideoDeviceNV = (PFNWGLRELEASEVIDEODEVICENVPROC)glbGetProcAddress("wglReleaseVideoDeviceNV"); + pAPI->wglBindVideoImageNV = (PFNWGLBINDVIDEOIMAGENVPROC)glbGetProcAddress("wglBindVideoImageNV"); + pAPI->wglReleaseVideoImageNV = (PFNWGLRELEASEVIDEOIMAGENVPROC)glbGetProcAddress("wglReleaseVideoImageNV"); + pAPI->wglSendPbufferToVideoNV = (PFNWGLSENDPBUFFERTOVIDEONVPROC)glbGetProcAddress("wglSendPbufferToVideoNV"); + pAPI->wglGetVideoInfoNV = (PFNWGLGETVIDEOINFONVPROC)glbGetProcAddress("wglGetVideoInfoNV"); + pAPI->wglAllocateMemoryNV = (PFNWGLALLOCATEMEMORYNVPROC)glbGetProcAddress("wglAllocateMemoryNV"); + pAPI->wglFreeMemoryNV = (PFNWGLFREEMEMORYNVPROC)glbGetProcAddress("wglFreeMemoryNV"); + pAPI->wglGetSyncValuesOML = (PFNWGLGETSYNCVALUESOMLPROC)glbGetProcAddress("wglGetSyncValuesOML"); + pAPI->wglGetMscRateOML = (PFNWGLGETMSCRATEOMLPROC)glbGetProcAddress("wglGetMscRateOML"); + pAPI->wglSwapBuffersMscOML = (PFNWGLSWAPBUFFERSMSCOMLPROC)glbGetProcAddress("wglSwapBuffersMscOML"); + pAPI->wglSwapLayerBuffersMscOML = (PFNWGLSWAPLAYERBUFFERSMSCOMLPROC)glbGetProcAddress("wglSwapLayerBuffersMscOML"); + pAPI->wglWaitForMscOML = (PFNWGLWAITFORMSCOMLPROC)glbGetProcAddress("wglWaitForMscOML"); + pAPI->wglWaitForSbcOML = (PFNWGLWAITFORSBCOMLPROC)glbGetProcAddress("wglWaitForSbcOML"); +#endif /* GLBIND_WGL */ +#if defined(GLBIND_GLX) + pAPI->glXGetGPUIDsAMD = (PFNGLXGETGPUIDSAMDPROC)glbGetProcAddress("glXGetGPUIDsAMD"); + pAPI->glXGetGPUInfoAMD = (PFNGLXGETGPUINFOAMDPROC)glbGetProcAddress("glXGetGPUInfoAMD"); + pAPI->glXGetContextGPUIDAMD = (PFNGLXGETCONTEXTGPUIDAMDPROC)glbGetProcAddress("glXGetContextGPUIDAMD"); + pAPI->glXCreateAssociatedContextAMD = (PFNGLXCREATEASSOCIATEDCONTEXTAMDPROC)glbGetProcAddress("glXCreateAssociatedContextAMD"); + pAPI->glXCreateAssociatedContextAttribsAMD = (PFNGLXCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC)glbGetProcAddress("glXCreateAssociatedContextAttribsAMD"); + pAPI->glXDeleteAssociatedContextAMD = (PFNGLXDELETEASSOCIATEDCONTEXTAMDPROC)glbGetProcAddress("glXDeleteAssociatedContextAMD"); + pAPI->glXMakeAssociatedContextCurrentAMD = (PFNGLXMAKEASSOCIATEDCONTEXTCURRENTAMDPROC)glbGetProcAddress("glXMakeAssociatedContextCurrentAMD"); + pAPI->glXGetCurrentAssociatedContextAMD = (PFNGLXGETCURRENTASSOCIATEDCONTEXTAMDPROC)glbGetProcAddress("glXGetCurrentAssociatedContextAMD"); + pAPI->glXBlitContextFramebufferAMD = (PFNGLXBLITCONTEXTFRAMEBUFFERAMDPROC)glbGetProcAddress("glXBlitContextFramebufferAMD"); + pAPI->glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glbGetProcAddress("glXCreateContextAttribsARB"); + pAPI->glXGetProcAddressARB = (PFNGLXGETPROCADDRESSARBPROC)glbGetProcAddress("glXGetProcAddressARB"); + pAPI->glXGetCurrentDisplayEXT = (PFNGLXGETCURRENTDISPLAYEXTPROC)glbGetProcAddress("glXGetCurrentDisplayEXT"); + pAPI->glXQueryContextInfoEXT = (PFNGLXQUERYCONTEXTINFOEXTPROC)glbGetProcAddress("glXQueryContextInfoEXT"); + pAPI->glXGetContextIDEXT = (PFNGLXGETCONTEXTIDEXTPROC)glbGetProcAddress("glXGetContextIDEXT"); + pAPI->glXImportContextEXT = (PFNGLXIMPORTCONTEXTEXTPROC)glbGetProcAddress("glXImportContextEXT"); + pAPI->glXFreeContextEXT = (PFNGLXFREECONTEXTEXTPROC)glbGetProcAddress("glXFreeContextEXT"); + pAPI->glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)glbGetProcAddress("glXSwapIntervalEXT"); + pAPI->glXBindTexImageEXT = (PFNGLXBINDTEXIMAGEEXTPROC)glbGetProcAddress("glXBindTexImageEXT"); + pAPI->glXReleaseTexImageEXT = (PFNGLXRELEASETEXIMAGEEXTPROC)glbGetProcAddress("glXReleaseTexImageEXT"); + pAPI->glXGetAGPOffsetMESA = (PFNGLXGETAGPOFFSETMESAPROC)glbGetProcAddress("glXGetAGPOffsetMESA"); + pAPI->glXCopySubBufferMESA = (PFNGLXCOPYSUBBUFFERMESAPROC)glbGetProcAddress("glXCopySubBufferMESA"); + pAPI->glXCreateGLXPixmapMESA = (PFNGLXCREATEGLXPIXMAPMESAPROC)glbGetProcAddress("glXCreateGLXPixmapMESA"); + pAPI->glXQueryCurrentRendererIntegerMESA = (PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC)glbGetProcAddress("glXQueryCurrentRendererIntegerMESA"); + pAPI->glXQueryCurrentRendererStringMESA = (PFNGLXQUERYCURRENTRENDERERSTRINGMESAPROC)glbGetProcAddress("glXQueryCurrentRendererStringMESA"); + pAPI->glXQueryRendererIntegerMESA = (PFNGLXQUERYRENDERERINTEGERMESAPROC)glbGetProcAddress("glXQueryRendererIntegerMESA"); + pAPI->glXQueryRendererStringMESA = (PFNGLXQUERYRENDERERSTRINGMESAPROC)glbGetProcAddress("glXQueryRendererStringMESA"); + pAPI->glXReleaseBuffersMESA = (PFNGLXRELEASEBUFFERSMESAPROC)glbGetProcAddress("glXReleaseBuffersMESA"); + pAPI->glXSet3DfxModeMESA = (PFNGLXSET3DFXMODEMESAPROC)glbGetProcAddress("glXSet3DfxModeMESA"); + pAPI->glXGetSwapIntervalMESA = (PFNGLXGETSWAPINTERVALMESAPROC)glbGetProcAddress("glXGetSwapIntervalMESA"); + pAPI->glXSwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC)glbGetProcAddress("glXSwapIntervalMESA"); + pAPI->glXCopyBufferSubDataNV = (PFNGLXCOPYBUFFERSUBDATANVPROC)glbGetProcAddress("glXCopyBufferSubDataNV"); + pAPI->glXNamedCopyBufferSubDataNV = (PFNGLXNAMEDCOPYBUFFERSUBDATANVPROC)glbGetProcAddress("glXNamedCopyBufferSubDataNV"); + pAPI->glXCopyImageSubDataNV = (PFNGLXCOPYIMAGESUBDATANVPROC)glbGetProcAddress("glXCopyImageSubDataNV"); + pAPI->glXDelayBeforeSwapNV = (PFNGLXDELAYBEFORESWAPNVPROC)glbGetProcAddress("glXDelayBeforeSwapNV"); + pAPI->glXEnumerateVideoDevicesNV = (PFNGLXENUMERATEVIDEODEVICESNVPROC)glbGetProcAddress("glXEnumerateVideoDevicesNV"); + pAPI->glXBindVideoDeviceNV = (PFNGLXBINDVIDEODEVICENVPROC)glbGetProcAddress("glXBindVideoDeviceNV"); + pAPI->glXJoinSwapGroupNV = (PFNGLXJOINSWAPGROUPNVPROC)glbGetProcAddress("glXJoinSwapGroupNV"); + pAPI->glXBindSwapBarrierNV = (PFNGLXBINDSWAPBARRIERNVPROC)glbGetProcAddress("glXBindSwapBarrierNV"); + pAPI->glXQuerySwapGroupNV = (PFNGLXQUERYSWAPGROUPNVPROC)glbGetProcAddress("glXQuerySwapGroupNV"); + pAPI->glXQueryMaxSwapGroupsNV = (PFNGLXQUERYMAXSWAPGROUPSNVPROC)glbGetProcAddress("glXQueryMaxSwapGroupsNV"); + pAPI->glXQueryFrameCountNV = (PFNGLXQUERYFRAMECOUNTNVPROC)glbGetProcAddress("glXQueryFrameCountNV"); + pAPI->glXResetFrameCountNV = (PFNGLXRESETFRAMECOUNTNVPROC)glbGetProcAddress("glXResetFrameCountNV"); + pAPI->glXBindVideoCaptureDeviceNV = (PFNGLXBINDVIDEOCAPTUREDEVICENVPROC)glbGetProcAddress("glXBindVideoCaptureDeviceNV"); + pAPI->glXEnumerateVideoCaptureDevicesNV = (PFNGLXENUMERATEVIDEOCAPTUREDEVICESNVPROC)glbGetProcAddress("glXEnumerateVideoCaptureDevicesNV"); + pAPI->glXLockVideoCaptureDeviceNV = (PFNGLXLOCKVIDEOCAPTUREDEVICENVPROC)glbGetProcAddress("glXLockVideoCaptureDeviceNV"); + pAPI->glXQueryVideoCaptureDeviceNV = (PFNGLXQUERYVIDEOCAPTUREDEVICENVPROC)glbGetProcAddress("glXQueryVideoCaptureDeviceNV"); + pAPI->glXReleaseVideoCaptureDeviceNV = (PFNGLXRELEASEVIDEOCAPTUREDEVICENVPROC)glbGetProcAddress("glXReleaseVideoCaptureDeviceNV"); + pAPI->glXGetVideoDeviceNV = (PFNGLXGETVIDEODEVICENVPROC)glbGetProcAddress("glXGetVideoDeviceNV"); + pAPI->glXReleaseVideoDeviceNV = (PFNGLXRELEASEVIDEODEVICENVPROC)glbGetProcAddress("glXReleaseVideoDeviceNV"); + pAPI->glXBindVideoImageNV = (PFNGLXBINDVIDEOIMAGENVPROC)glbGetProcAddress("glXBindVideoImageNV"); + pAPI->glXReleaseVideoImageNV = (PFNGLXRELEASEVIDEOIMAGENVPROC)glbGetProcAddress("glXReleaseVideoImageNV"); + pAPI->glXSendPbufferToVideoNV = (PFNGLXSENDPBUFFERTOVIDEONVPROC)glbGetProcAddress("glXSendPbufferToVideoNV"); + pAPI->glXGetVideoInfoNV = (PFNGLXGETVIDEOINFONVPROC)glbGetProcAddress("glXGetVideoInfoNV"); + pAPI->glXGetSyncValuesOML = (PFNGLXGETSYNCVALUESOMLPROC)glbGetProcAddress("glXGetSyncValuesOML"); + pAPI->glXGetMscRateOML = (PFNGLXGETMSCRATEOMLPROC)glbGetProcAddress("glXGetMscRateOML"); + pAPI->glXSwapBuffersMscOML = (PFNGLXSWAPBUFFERSMSCOMLPROC)glbGetProcAddress("glXSwapBuffersMscOML"); + pAPI->glXWaitForMscOML = (PFNGLXWAITFORMSCOMLPROC)glbGetProcAddress("glXWaitForMscOML"); + pAPI->glXWaitForSbcOML = (PFNGLXWAITFORSBCOMLPROC)glbGetProcAddress("glXWaitForSbcOML"); + pAPI->glXCushionSGI = (PFNGLXCUSHIONSGIPROC)glbGetProcAddress("glXCushionSGI"); + pAPI->glXMakeCurrentReadSGI = (PFNGLXMAKECURRENTREADSGIPROC)glbGetProcAddress("glXMakeCurrentReadSGI"); + pAPI->glXGetCurrentReadDrawableSGI = (PFNGLXGETCURRENTREADDRAWABLESGIPROC)glbGetProcAddress("glXGetCurrentReadDrawableSGI"); + pAPI->glXSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)glbGetProcAddress("glXSwapIntervalSGI"); + pAPI->glXGetVideoSyncSGI = (PFNGLXGETVIDEOSYNCSGIPROC)glbGetProcAddress("glXGetVideoSyncSGI"); + pAPI->glXWaitVideoSyncSGI = (PFNGLXWAITVIDEOSYNCSGIPROC)glbGetProcAddress("glXWaitVideoSyncSGI"); + pAPI->glXGetFBConfigAttribSGIX = (PFNGLXGETFBCONFIGATTRIBSGIXPROC)glbGetProcAddress("glXGetFBConfigAttribSGIX"); + pAPI->glXChooseFBConfigSGIX = (PFNGLXCHOOSEFBCONFIGSGIXPROC)glbGetProcAddress("glXChooseFBConfigSGIX"); + pAPI->glXCreateGLXPixmapWithConfigSGIX = (PFNGLXCREATEGLXPIXMAPWITHCONFIGSGIXPROC)glbGetProcAddress("glXCreateGLXPixmapWithConfigSGIX"); + pAPI->glXCreateContextWithConfigSGIX = (PFNGLXCREATECONTEXTWITHCONFIGSGIXPROC)glbGetProcAddress("glXCreateContextWithConfigSGIX"); + pAPI->glXGetVisualFromFBConfigSGIX = (PFNGLXGETVISUALFROMFBCONFIGSGIXPROC)glbGetProcAddress("glXGetVisualFromFBConfigSGIX"); + pAPI->glXGetFBConfigFromVisualSGIX = (PFNGLXGETFBCONFIGFROMVISUALSGIXPROC)glbGetProcAddress("glXGetFBConfigFromVisualSGIX"); + pAPI->glXQueryHyperpipeNetworkSGIX = (PFNGLXQUERYHYPERPIPENETWORKSGIXPROC)glbGetProcAddress("glXQueryHyperpipeNetworkSGIX"); + pAPI->glXHyperpipeConfigSGIX = (PFNGLXHYPERPIPECONFIGSGIXPROC)glbGetProcAddress("glXHyperpipeConfigSGIX"); + pAPI->glXQueryHyperpipeConfigSGIX = (PFNGLXQUERYHYPERPIPECONFIGSGIXPROC)glbGetProcAddress("glXQueryHyperpipeConfigSGIX"); + pAPI->glXDestroyHyperpipeConfigSGIX = (PFNGLXDESTROYHYPERPIPECONFIGSGIXPROC)glbGetProcAddress("glXDestroyHyperpipeConfigSGIX"); + pAPI->glXBindHyperpipeSGIX = (PFNGLXBINDHYPERPIPESGIXPROC)glbGetProcAddress("glXBindHyperpipeSGIX"); + pAPI->glXQueryHyperpipeBestAttribSGIX = (PFNGLXQUERYHYPERPIPEBESTATTRIBSGIXPROC)glbGetProcAddress("glXQueryHyperpipeBestAttribSGIX"); + pAPI->glXHyperpipeAttribSGIX = (PFNGLXHYPERPIPEATTRIBSGIXPROC)glbGetProcAddress("glXHyperpipeAttribSGIX"); + pAPI->glXQueryHyperpipeAttribSGIX = (PFNGLXQUERYHYPERPIPEATTRIBSGIXPROC)glbGetProcAddress("glXQueryHyperpipeAttribSGIX"); + pAPI->glXCreateGLXPbufferSGIX = (PFNGLXCREATEGLXPBUFFERSGIXPROC)glbGetProcAddress("glXCreateGLXPbufferSGIX"); + pAPI->glXDestroyGLXPbufferSGIX = (PFNGLXDESTROYGLXPBUFFERSGIXPROC)glbGetProcAddress("glXDestroyGLXPbufferSGIX"); + pAPI->glXQueryGLXPbufferSGIX = (PFNGLXQUERYGLXPBUFFERSGIXPROC)glbGetProcAddress("glXQueryGLXPbufferSGIX"); + pAPI->glXSelectEventSGIX = (PFNGLXSELECTEVENTSGIXPROC)glbGetProcAddress("glXSelectEventSGIX"); + pAPI->glXGetSelectedEventSGIX = (PFNGLXGETSELECTEDEVENTSGIXPROC)glbGetProcAddress("glXGetSelectedEventSGIX"); + pAPI->glXBindSwapBarrierSGIX = (PFNGLXBINDSWAPBARRIERSGIXPROC)glbGetProcAddress("glXBindSwapBarrierSGIX"); + pAPI->glXQueryMaxSwapBarriersSGIX = (PFNGLXQUERYMAXSWAPBARRIERSSGIXPROC)glbGetProcAddress("glXQueryMaxSwapBarriersSGIX"); + pAPI->glXJoinSwapGroupSGIX = (PFNGLXJOINSWAPGROUPSGIXPROC)glbGetProcAddress("glXJoinSwapGroupSGIX"); + pAPI->glXBindChannelToWindowSGIX = (PFNGLXBINDCHANNELTOWINDOWSGIXPROC)glbGetProcAddress("glXBindChannelToWindowSGIX"); + pAPI->glXChannelRectSGIX = (PFNGLXCHANNELRECTSGIXPROC)glbGetProcAddress("glXChannelRectSGIX"); + pAPI->glXQueryChannelRectSGIX = (PFNGLXQUERYCHANNELRECTSGIXPROC)glbGetProcAddress("glXQueryChannelRectSGIX"); + pAPI->glXQueryChannelDeltasSGIX = (PFNGLXQUERYCHANNELDELTASSGIXPROC)glbGetProcAddress("glXQueryChannelDeltasSGIX"); + pAPI->glXChannelRectSyncSGIX = (PFNGLXCHANNELRECTSYNCSGIXPROC)glbGetProcAddress("glXChannelRectSyncSGIX"); + pAPI->glXGetTransparentIndexSUN = (PFNGLXGETTRANSPARENTINDEXSUNPROC)glbGetProcAddress("glXGetTransparentIndexSUN"); +#endif /* GLBIND_GLX */ + + return GL_NO_ERROR; +} + +void glbUninit() +{ + if (g_glbInitCount == 0) { + return; + } + + g_glbInitCount -= 1; + if (g_glbInitCount == 0) { +#if defined(GLBIND_WGL) + if (glbind_RC) { + glbind_wglDeleteContext(glbind_RC); + glbind_RC = 0; + } + if (glbind_DummyHWND) { + DestroyWindow(glbind_DummyHWND); + glbind_DummyHWND = 0; + } + glbind_DC = 0; +#endif +#if defined(GLBIND_GLX) + if (glbind_RC) { + glbind_glXDestroyContext(glbind_pDisplay, glbind_RC); + glbind_RC = 0; + } + if (glbind_DummyWindow) { + glbind_XDestroyWindow(glbind_pDisplay, glbind_DummyWindow); + glbind_DummyWindow = 0; + } + if (glbind_pDisplay && glbind_OwnsDisplay) { + glbind_XCloseDisplay(glbind_pDisplay); + glbind_pDisplay = 0; + glbind_OwnsDisplay = GL_FALSE; + } +#endif + + glb_dlclose(g_glbOpenGLSO); + g_glbOpenGLSO = NULL; + } +} + +GLenum glbBindAPI(const GLBapi* pAPI) +{ + GLenum result; + + if (pAPI == NULL) { + GLBapi tempAPI; +#if defined(GLBIND_WGL) + result = glbInitContextAPI(glbind_DC, glbind_RC, &tempAPI); +#endif +#if defined(GLBIND_GLX) + result = glbInitContextAPI(glbind_pDisplay, glbind_DummyWindow, glbind_RC, &tempAPI); +#endif + if (result != GL_NO_ERROR) { + return result; + } + + return glbBindAPI(&tempAPI); + } + + glCullFace = pAPI->glCullFace; + glFrontFace = pAPI->glFrontFace; + glHint = pAPI->glHint; + glLineWidth = pAPI->glLineWidth; + glPointSize = pAPI->glPointSize; + glPolygonMode = pAPI->glPolygonMode; + glScissor = pAPI->glScissor; + glTexParameterf = pAPI->glTexParameterf; + glTexParameterfv = pAPI->glTexParameterfv; + glTexParameteri = pAPI->glTexParameteri; + glTexParameteriv = pAPI->glTexParameteriv; + glTexImage1D = pAPI->glTexImage1D; + glTexImage2D = pAPI->glTexImage2D; + glDrawBuffer = pAPI->glDrawBuffer; + glClear = pAPI->glClear; + glClearColor = pAPI->glClearColor; + glClearStencil = pAPI->glClearStencil; + glClearDepth = pAPI->glClearDepth; + glStencilMask = pAPI->glStencilMask; + glColorMask = pAPI->glColorMask; + glDepthMask = pAPI->glDepthMask; + glDisable = pAPI->glDisable; + glEnable = pAPI->glEnable; + glFinish = pAPI->glFinish; + glFlush = pAPI->glFlush; + glBlendFunc = pAPI->glBlendFunc; + glLogicOp = pAPI->glLogicOp; + glStencilFunc = pAPI->glStencilFunc; + glStencilOp = pAPI->glStencilOp; + glDepthFunc = pAPI->glDepthFunc; + glPixelStoref = pAPI->glPixelStoref; + glPixelStorei = pAPI->glPixelStorei; + glReadBuffer = pAPI->glReadBuffer; + glReadPixels = pAPI->glReadPixels; + glGetBooleanv = pAPI->glGetBooleanv; + glGetDoublev = pAPI->glGetDoublev; + glGetError = pAPI->glGetError; + glGetFloatv = pAPI->glGetFloatv; + glGetIntegerv = pAPI->glGetIntegerv; + glGetString = pAPI->glGetString; + glGetTexImage = pAPI->glGetTexImage; + glGetTexParameterfv = pAPI->glGetTexParameterfv; + glGetTexParameteriv = pAPI->glGetTexParameteriv; + glGetTexLevelParameterfv = pAPI->glGetTexLevelParameterfv; + glGetTexLevelParameteriv = pAPI->glGetTexLevelParameteriv; + glIsEnabled = pAPI->glIsEnabled; + glDepthRange = pAPI->glDepthRange; + glViewport = pAPI->glViewport; + glNewList = pAPI->glNewList; + glEndList = pAPI->glEndList; + glCallList = pAPI->glCallList; + glCallLists = pAPI->glCallLists; + glDeleteLists = pAPI->glDeleteLists; + glGenLists = pAPI->glGenLists; + glListBase = pAPI->glListBase; + glBegin = pAPI->glBegin; + glBitmap = pAPI->glBitmap; + glColor3b = pAPI->glColor3b; + glColor3bv = pAPI->glColor3bv; + glColor3d = pAPI->glColor3d; + glColor3dv = pAPI->glColor3dv; + glColor3f = pAPI->glColor3f; + glColor3fv = pAPI->glColor3fv; + glColor3i = pAPI->glColor3i; + glColor3iv = pAPI->glColor3iv; + glColor3s = pAPI->glColor3s; + glColor3sv = pAPI->glColor3sv; + glColor3ub = pAPI->glColor3ub; + glColor3ubv = pAPI->glColor3ubv; + glColor3ui = pAPI->glColor3ui; + glColor3uiv = pAPI->glColor3uiv; + glColor3us = pAPI->glColor3us; + glColor3usv = pAPI->glColor3usv; + glColor4b = pAPI->glColor4b; + glColor4bv = pAPI->glColor4bv; + glColor4d = pAPI->glColor4d; + glColor4dv = pAPI->glColor4dv; + glColor4f = pAPI->glColor4f; + glColor4fv = pAPI->glColor4fv; + glColor4i = pAPI->glColor4i; + glColor4iv = pAPI->glColor4iv; + glColor4s = pAPI->glColor4s; + glColor4sv = pAPI->glColor4sv; + glColor4ub = pAPI->glColor4ub; + glColor4ubv = pAPI->glColor4ubv; + glColor4ui = pAPI->glColor4ui; + glColor4uiv = pAPI->glColor4uiv; + glColor4us = pAPI->glColor4us; + glColor4usv = pAPI->glColor4usv; + glEdgeFlag = pAPI->glEdgeFlag; + glEdgeFlagv = pAPI->glEdgeFlagv; + glEnd = pAPI->glEnd; + glIndexd = pAPI->glIndexd; + glIndexdv = pAPI->glIndexdv; + glIndexf = pAPI->glIndexf; + glIndexfv = pAPI->glIndexfv; + glIndexi = pAPI->glIndexi; + glIndexiv = pAPI->glIndexiv; + glIndexs = pAPI->glIndexs; + glIndexsv = pAPI->glIndexsv; + glNormal3b = pAPI->glNormal3b; + glNormal3bv = pAPI->glNormal3bv; + glNormal3d = pAPI->glNormal3d; + glNormal3dv = pAPI->glNormal3dv; + glNormal3f = pAPI->glNormal3f; + glNormal3fv = pAPI->glNormal3fv; + glNormal3i = pAPI->glNormal3i; + glNormal3iv = pAPI->glNormal3iv; + glNormal3s = pAPI->glNormal3s; + glNormal3sv = pAPI->glNormal3sv; + glRasterPos2d = pAPI->glRasterPos2d; + glRasterPos2dv = pAPI->glRasterPos2dv; + glRasterPos2f = pAPI->glRasterPos2f; + glRasterPos2fv = pAPI->glRasterPos2fv; + glRasterPos2i = pAPI->glRasterPos2i; + glRasterPos2iv = pAPI->glRasterPos2iv; + glRasterPos2s = pAPI->glRasterPos2s; + glRasterPos2sv = pAPI->glRasterPos2sv; + glRasterPos3d = pAPI->glRasterPos3d; + glRasterPos3dv = pAPI->glRasterPos3dv; + glRasterPos3f = pAPI->glRasterPos3f; + glRasterPos3fv = pAPI->glRasterPos3fv; + glRasterPos3i = pAPI->glRasterPos3i; + glRasterPos3iv = pAPI->glRasterPos3iv; + glRasterPos3s = pAPI->glRasterPos3s; + glRasterPos3sv = pAPI->glRasterPos3sv; + glRasterPos4d = pAPI->glRasterPos4d; + glRasterPos4dv = pAPI->glRasterPos4dv; + glRasterPos4f = pAPI->glRasterPos4f; + glRasterPos4fv = pAPI->glRasterPos4fv; + glRasterPos4i = pAPI->glRasterPos4i; + glRasterPos4iv = pAPI->glRasterPos4iv; + glRasterPos4s = pAPI->glRasterPos4s; + glRasterPos4sv = pAPI->glRasterPos4sv; + glRectd = pAPI->glRectd; + glRectdv = pAPI->glRectdv; + glRectf = pAPI->glRectf; + glRectfv = pAPI->glRectfv; + glRecti = pAPI->glRecti; + glRectiv = pAPI->glRectiv; + glRects = pAPI->glRects; + glRectsv = pAPI->glRectsv; + glTexCoord1d = pAPI->glTexCoord1d; + glTexCoord1dv = pAPI->glTexCoord1dv; + glTexCoord1f = pAPI->glTexCoord1f; + glTexCoord1fv = pAPI->glTexCoord1fv; + glTexCoord1i = pAPI->glTexCoord1i; + glTexCoord1iv = pAPI->glTexCoord1iv; + glTexCoord1s = pAPI->glTexCoord1s; + glTexCoord1sv = pAPI->glTexCoord1sv; + glTexCoord2d = pAPI->glTexCoord2d; + glTexCoord2dv = pAPI->glTexCoord2dv; + glTexCoord2f = pAPI->glTexCoord2f; + glTexCoord2fv = pAPI->glTexCoord2fv; + glTexCoord2i = pAPI->glTexCoord2i; + glTexCoord2iv = pAPI->glTexCoord2iv; + glTexCoord2s = pAPI->glTexCoord2s; + glTexCoord2sv = pAPI->glTexCoord2sv; + glTexCoord3d = pAPI->glTexCoord3d; + glTexCoord3dv = pAPI->glTexCoord3dv; + glTexCoord3f = pAPI->glTexCoord3f; + glTexCoord3fv = pAPI->glTexCoord3fv; + glTexCoord3i = pAPI->glTexCoord3i; + glTexCoord3iv = pAPI->glTexCoord3iv; + glTexCoord3s = pAPI->glTexCoord3s; + glTexCoord3sv = pAPI->glTexCoord3sv; + glTexCoord4d = pAPI->glTexCoord4d; + glTexCoord4dv = pAPI->glTexCoord4dv; + glTexCoord4f = pAPI->glTexCoord4f; + glTexCoord4fv = pAPI->glTexCoord4fv; + glTexCoord4i = pAPI->glTexCoord4i; + glTexCoord4iv = pAPI->glTexCoord4iv; + glTexCoord4s = pAPI->glTexCoord4s; + glTexCoord4sv = pAPI->glTexCoord4sv; + glVertex2d = pAPI->glVertex2d; + glVertex2dv = pAPI->glVertex2dv; + glVertex2f = pAPI->glVertex2f; + glVertex2fv = pAPI->glVertex2fv; + glVertex2i = pAPI->glVertex2i; + glVertex2iv = pAPI->glVertex2iv; + glVertex2s = pAPI->glVertex2s; + glVertex2sv = pAPI->glVertex2sv; + glVertex3d = pAPI->glVertex3d; + glVertex3dv = pAPI->glVertex3dv; + glVertex3f = pAPI->glVertex3f; + glVertex3fv = pAPI->glVertex3fv; + glVertex3i = pAPI->glVertex3i; + glVertex3iv = pAPI->glVertex3iv; + glVertex3s = pAPI->glVertex3s; + glVertex3sv = pAPI->glVertex3sv; + glVertex4d = pAPI->glVertex4d; + glVertex4dv = pAPI->glVertex4dv; + glVertex4f = pAPI->glVertex4f; + glVertex4fv = pAPI->glVertex4fv; + glVertex4i = pAPI->glVertex4i; + glVertex4iv = pAPI->glVertex4iv; + glVertex4s = pAPI->glVertex4s; + glVertex4sv = pAPI->glVertex4sv; + glClipPlane = pAPI->glClipPlane; + glColorMaterial = pAPI->glColorMaterial; + glFogf = pAPI->glFogf; + glFogfv = pAPI->glFogfv; + glFogi = pAPI->glFogi; + glFogiv = pAPI->glFogiv; + glLightf = pAPI->glLightf; + glLightfv = pAPI->glLightfv; + glLighti = pAPI->glLighti; + glLightiv = pAPI->glLightiv; + glLightModelf = pAPI->glLightModelf; + glLightModelfv = pAPI->glLightModelfv; + glLightModeli = pAPI->glLightModeli; + glLightModeliv = pAPI->glLightModeliv; + glLineStipple = pAPI->glLineStipple; + glMaterialf = pAPI->glMaterialf; + glMaterialfv = pAPI->glMaterialfv; + glMateriali = pAPI->glMateriali; + glMaterialiv = pAPI->glMaterialiv; + glPolygonStipple = pAPI->glPolygonStipple; + glShadeModel = pAPI->glShadeModel; + glTexEnvf = pAPI->glTexEnvf; + glTexEnvfv = pAPI->glTexEnvfv; + glTexEnvi = pAPI->glTexEnvi; + glTexEnviv = pAPI->glTexEnviv; + glTexGend = pAPI->glTexGend; + glTexGendv = pAPI->glTexGendv; + glTexGenf = pAPI->glTexGenf; + glTexGenfv = pAPI->glTexGenfv; + glTexGeni = pAPI->glTexGeni; + glTexGeniv = pAPI->glTexGeniv; + glFeedbackBuffer = pAPI->glFeedbackBuffer; + glSelectBuffer = pAPI->glSelectBuffer; + glRenderMode = pAPI->glRenderMode; + glInitNames = pAPI->glInitNames; + glLoadName = pAPI->glLoadName; + glPassThrough = pAPI->glPassThrough; + glPopName = pAPI->glPopName; + glPushName = pAPI->glPushName; + glClearAccum = pAPI->glClearAccum; + glClearIndex = pAPI->glClearIndex; + glIndexMask = pAPI->glIndexMask; + glAccum = pAPI->glAccum; + glPopAttrib = pAPI->glPopAttrib; + glPushAttrib = pAPI->glPushAttrib; + glMap1d = pAPI->glMap1d; + glMap1f = pAPI->glMap1f; + glMap2d = pAPI->glMap2d; + glMap2f = pAPI->glMap2f; + glMapGrid1d = pAPI->glMapGrid1d; + glMapGrid1f = pAPI->glMapGrid1f; + glMapGrid2d = pAPI->glMapGrid2d; + glMapGrid2f = pAPI->glMapGrid2f; + glEvalCoord1d = pAPI->glEvalCoord1d; + glEvalCoord1dv = pAPI->glEvalCoord1dv; + glEvalCoord1f = pAPI->glEvalCoord1f; + glEvalCoord1fv = pAPI->glEvalCoord1fv; + glEvalCoord2d = pAPI->glEvalCoord2d; + glEvalCoord2dv = pAPI->glEvalCoord2dv; + glEvalCoord2f = pAPI->glEvalCoord2f; + glEvalCoord2fv = pAPI->glEvalCoord2fv; + glEvalMesh1 = pAPI->glEvalMesh1; + glEvalPoint1 = pAPI->glEvalPoint1; + glEvalMesh2 = pAPI->glEvalMesh2; + glEvalPoint2 = pAPI->glEvalPoint2; + glAlphaFunc = pAPI->glAlphaFunc; + glPixelZoom = pAPI->glPixelZoom; + glPixelTransferf = pAPI->glPixelTransferf; + glPixelTransferi = pAPI->glPixelTransferi; + glPixelMapfv = pAPI->glPixelMapfv; + glPixelMapuiv = pAPI->glPixelMapuiv; + glPixelMapusv = pAPI->glPixelMapusv; + glCopyPixels = pAPI->glCopyPixels; + glDrawPixels = pAPI->glDrawPixels; + glGetClipPlane = pAPI->glGetClipPlane; + glGetLightfv = pAPI->glGetLightfv; + glGetLightiv = pAPI->glGetLightiv; + glGetMapdv = pAPI->glGetMapdv; + glGetMapfv = pAPI->glGetMapfv; + glGetMapiv = pAPI->glGetMapiv; + glGetMaterialfv = pAPI->glGetMaterialfv; + glGetMaterialiv = pAPI->glGetMaterialiv; + glGetPixelMapfv = pAPI->glGetPixelMapfv; + glGetPixelMapuiv = pAPI->glGetPixelMapuiv; + glGetPixelMapusv = pAPI->glGetPixelMapusv; + glGetPolygonStipple = pAPI->glGetPolygonStipple; + glGetTexEnvfv = pAPI->glGetTexEnvfv; + glGetTexEnviv = pAPI->glGetTexEnviv; + glGetTexGendv = pAPI->glGetTexGendv; + glGetTexGenfv = pAPI->glGetTexGenfv; + glGetTexGeniv = pAPI->glGetTexGeniv; + glIsList = pAPI->glIsList; + glFrustum = pAPI->glFrustum; + glLoadIdentity = pAPI->glLoadIdentity; + glLoadMatrixf = pAPI->glLoadMatrixf; + glLoadMatrixd = pAPI->glLoadMatrixd; + glMatrixMode = pAPI->glMatrixMode; + glMultMatrixf = pAPI->glMultMatrixf; + glMultMatrixd = pAPI->glMultMatrixd; + glOrtho = pAPI->glOrtho; + glPopMatrix = pAPI->glPopMatrix; + glPushMatrix = pAPI->glPushMatrix; + glRotated = pAPI->glRotated; + glRotatef = pAPI->glRotatef; + glScaled = pAPI->glScaled; + glScalef = pAPI->glScalef; + glTranslated = pAPI->glTranslated; + glTranslatef = pAPI->glTranslatef; + glDrawArrays = pAPI->glDrawArrays; + glDrawElements = pAPI->glDrawElements; + glGetPointerv = pAPI->glGetPointerv; + glPolygonOffset = pAPI->glPolygonOffset; + glCopyTexImage1D = pAPI->glCopyTexImage1D; + glCopyTexImage2D = pAPI->glCopyTexImage2D; + glCopyTexSubImage1D = pAPI->glCopyTexSubImage1D; + glCopyTexSubImage2D = pAPI->glCopyTexSubImage2D; + glTexSubImage1D = pAPI->glTexSubImage1D; + glTexSubImage2D = pAPI->glTexSubImage2D; + glBindTexture = pAPI->glBindTexture; + glDeleteTextures = pAPI->glDeleteTextures; + glGenTextures = pAPI->glGenTextures; + glIsTexture = pAPI->glIsTexture; + glArrayElement = pAPI->glArrayElement; + glColorPointer = pAPI->glColorPointer; + glDisableClientState = pAPI->glDisableClientState; + glEdgeFlagPointer = pAPI->glEdgeFlagPointer; + glEnableClientState = pAPI->glEnableClientState; + glIndexPointer = pAPI->glIndexPointer; + glInterleavedArrays = pAPI->glInterleavedArrays; + glNormalPointer = pAPI->glNormalPointer; + glTexCoordPointer = pAPI->glTexCoordPointer; + glVertexPointer = pAPI->glVertexPointer; + glAreTexturesResident = pAPI->glAreTexturesResident; + glPrioritizeTextures = pAPI->glPrioritizeTextures; + glIndexub = pAPI->glIndexub; + glIndexubv = pAPI->glIndexubv; + glPopClientAttrib = pAPI->glPopClientAttrib; + glPushClientAttrib = pAPI->glPushClientAttrib; + glDrawRangeElements = pAPI->glDrawRangeElements; + glTexImage3D = pAPI->glTexImage3D; + glTexSubImage3D = pAPI->glTexSubImage3D; + glCopyTexSubImage3D = pAPI->glCopyTexSubImage3D; + glActiveTexture = pAPI->glActiveTexture; + glSampleCoverage = pAPI->glSampleCoverage; + glCompressedTexImage3D = pAPI->glCompressedTexImage3D; + glCompressedTexImage2D = pAPI->glCompressedTexImage2D; + glCompressedTexImage1D = pAPI->glCompressedTexImage1D; + glCompressedTexSubImage3D = pAPI->glCompressedTexSubImage3D; + glCompressedTexSubImage2D = pAPI->glCompressedTexSubImage2D; + glCompressedTexSubImage1D = pAPI->glCompressedTexSubImage1D; + glGetCompressedTexImage = pAPI->glGetCompressedTexImage; + glClientActiveTexture = pAPI->glClientActiveTexture; + glMultiTexCoord1d = pAPI->glMultiTexCoord1d; + glMultiTexCoord1dv = pAPI->glMultiTexCoord1dv; + glMultiTexCoord1f = pAPI->glMultiTexCoord1f; + glMultiTexCoord1fv = pAPI->glMultiTexCoord1fv; + glMultiTexCoord1i = pAPI->glMultiTexCoord1i; + glMultiTexCoord1iv = pAPI->glMultiTexCoord1iv; + glMultiTexCoord1s = pAPI->glMultiTexCoord1s; + glMultiTexCoord1sv = pAPI->glMultiTexCoord1sv; + glMultiTexCoord2d = pAPI->glMultiTexCoord2d; + glMultiTexCoord2dv = pAPI->glMultiTexCoord2dv; + glMultiTexCoord2f = pAPI->glMultiTexCoord2f; + glMultiTexCoord2fv = pAPI->glMultiTexCoord2fv; + glMultiTexCoord2i = pAPI->glMultiTexCoord2i; + glMultiTexCoord2iv = pAPI->glMultiTexCoord2iv; + glMultiTexCoord2s = pAPI->glMultiTexCoord2s; + glMultiTexCoord2sv = pAPI->glMultiTexCoord2sv; + glMultiTexCoord3d = pAPI->glMultiTexCoord3d; + glMultiTexCoord3dv = pAPI->glMultiTexCoord3dv; + glMultiTexCoord3f = pAPI->glMultiTexCoord3f; + glMultiTexCoord3fv = pAPI->glMultiTexCoord3fv; + glMultiTexCoord3i = pAPI->glMultiTexCoord3i; + glMultiTexCoord3iv = pAPI->glMultiTexCoord3iv; + glMultiTexCoord3s = pAPI->glMultiTexCoord3s; + glMultiTexCoord3sv = pAPI->glMultiTexCoord3sv; + glMultiTexCoord4d = pAPI->glMultiTexCoord4d; + glMultiTexCoord4dv = pAPI->glMultiTexCoord4dv; + glMultiTexCoord4f = pAPI->glMultiTexCoord4f; + glMultiTexCoord4fv = pAPI->glMultiTexCoord4fv; + glMultiTexCoord4i = pAPI->glMultiTexCoord4i; + glMultiTexCoord4iv = pAPI->glMultiTexCoord4iv; + glMultiTexCoord4s = pAPI->glMultiTexCoord4s; + glMultiTexCoord4sv = pAPI->glMultiTexCoord4sv; + glLoadTransposeMatrixf = pAPI->glLoadTransposeMatrixf; + glLoadTransposeMatrixd = pAPI->glLoadTransposeMatrixd; + glMultTransposeMatrixf = pAPI->glMultTransposeMatrixf; + glMultTransposeMatrixd = pAPI->glMultTransposeMatrixd; + glBlendFuncSeparate = pAPI->glBlendFuncSeparate; + glMultiDrawArrays = pAPI->glMultiDrawArrays; + glMultiDrawElements = pAPI->glMultiDrawElements; + glPointParameterf = pAPI->glPointParameterf; + glPointParameterfv = pAPI->glPointParameterfv; + glPointParameteri = pAPI->glPointParameteri; + glPointParameteriv = pAPI->glPointParameteriv; + glFogCoordf = pAPI->glFogCoordf; + glFogCoordfv = pAPI->glFogCoordfv; + glFogCoordd = pAPI->glFogCoordd; + glFogCoorddv = pAPI->glFogCoorddv; + glFogCoordPointer = pAPI->glFogCoordPointer; + glSecondaryColor3b = pAPI->glSecondaryColor3b; + glSecondaryColor3bv = pAPI->glSecondaryColor3bv; + glSecondaryColor3d = pAPI->glSecondaryColor3d; + glSecondaryColor3dv = pAPI->glSecondaryColor3dv; + glSecondaryColor3f = pAPI->glSecondaryColor3f; + glSecondaryColor3fv = pAPI->glSecondaryColor3fv; + glSecondaryColor3i = pAPI->glSecondaryColor3i; + glSecondaryColor3iv = pAPI->glSecondaryColor3iv; + glSecondaryColor3s = pAPI->glSecondaryColor3s; + glSecondaryColor3sv = pAPI->glSecondaryColor3sv; + glSecondaryColor3ub = pAPI->glSecondaryColor3ub; + glSecondaryColor3ubv = pAPI->glSecondaryColor3ubv; + glSecondaryColor3ui = pAPI->glSecondaryColor3ui; + glSecondaryColor3uiv = pAPI->glSecondaryColor3uiv; + glSecondaryColor3us = pAPI->glSecondaryColor3us; + glSecondaryColor3usv = pAPI->glSecondaryColor3usv; + glSecondaryColorPointer = pAPI->glSecondaryColorPointer; + glWindowPos2d = pAPI->glWindowPos2d; + glWindowPos2dv = pAPI->glWindowPos2dv; + glWindowPos2f = pAPI->glWindowPos2f; + glWindowPos2fv = pAPI->glWindowPos2fv; + glWindowPos2i = pAPI->glWindowPos2i; + glWindowPos2iv = pAPI->glWindowPos2iv; + glWindowPos2s = pAPI->glWindowPos2s; + glWindowPos2sv = pAPI->glWindowPos2sv; + glWindowPos3d = pAPI->glWindowPos3d; + glWindowPos3dv = pAPI->glWindowPos3dv; + glWindowPos3f = pAPI->glWindowPos3f; + glWindowPos3fv = pAPI->glWindowPos3fv; + glWindowPos3i = pAPI->glWindowPos3i; + glWindowPos3iv = pAPI->glWindowPos3iv; + glWindowPos3s = pAPI->glWindowPos3s; + glWindowPos3sv = pAPI->glWindowPos3sv; + glBlendColor = pAPI->glBlendColor; + glBlendEquation = pAPI->glBlendEquation; + glGenQueries = pAPI->glGenQueries; + glDeleteQueries = pAPI->glDeleteQueries; + glIsQuery = pAPI->glIsQuery; + glBeginQuery = pAPI->glBeginQuery; + glEndQuery = pAPI->glEndQuery; + glGetQueryiv = pAPI->glGetQueryiv; + glGetQueryObjectiv = pAPI->glGetQueryObjectiv; + glGetQueryObjectuiv = pAPI->glGetQueryObjectuiv; + glBindBuffer = pAPI->glBindBuffer; + glDeleteBuffers = pAPI->glDeleteBuffers; + glGenBuffers = pAPI->glGenBuffers; + glIsBuffer = pAPI->glIsBuffer; + glBufferData = pAPI->glBufferData; + glBufferSubData = pAPI->glBufferSubData; + glGetBufferSubData = pAPI->glGetBufferSubData; + glMapBuffer = pAPI->glMapBuffer; + glUnmapBuffer = pAPI->glUnmapBuffer; + glGetBufferParameteriv = pAPI->glGetBufferParameteriv; + glGetBufferPointerv = pAPI->glGetBufferPointerv; + glBlendEquationSeparate = pAPI->glBlendEquationSeparate; + glDrawBuffers = pAPI->glDrawBuffers; + glStencilOpSeparate = pAPI->glStencilOpSeparate; + glStencilFuncSeparate = pAPI->glStencilFuncSeparate; + glStencilMaskSeparate = pAPI->glStencilMaskSeparate; + glAttachShader = pAPI->glAttachShader; + glBindAttribLocation = pAPI->glBindAttribLocation; + glCompileShader = pAPI->glCompileShader; + glCreateProgram = pAPI->glCreateProgram; + glCreateShader = pAPI->glCreateShader; + glDeleteProgram = pAPI->glDeleteProgram; + glDeleteShader = pAPI->glDeleteShader; + glDetachShader = pAPI->glDetachShader; + glDisableVertexAttribArray = pAPI->glDisableVertexAttribArray; + glEnableVertexAttribArray = pAPI->glEnableVertexAttribArray; + glGetActiveAttrib = pAPI->glGetActiveAttrib; + glGetActiveUniform = pAPI->glGetActiveUniform; + glGetAttachedShaders = pAPI->glGetAttachedShaders; + glGetAttribLocation = pAPI->glGetAttribLocation; + glGetProgramiv = pAPI->glGetProgramiv; + glGetProgramInfoLog = pAPI->glGetProgramInfoLog; + glGetShaderiv = pAPI->glGetShaderiv; + glGetShaderInfoLog = pAPI->glGetShaderInfoLog; + glGetShaderSource = pAPI->glGetShaderSource; + glGetUniformLocation = pAPI->glGetUniformLocation; + glGetUniformfv = pAPI->glGetUniformfv; + glGetUniformiv = pAPI->glGetUniformiv; + glGetVertexAttribdv = pAPI->glGetVertexAttribdv; + glGetVertexAttribfv = pAPI->glGetVertexAttribfv; + glGetVertexAttribiv = pAPI->glGetVertexAttribiv; + glGetVertexAttribPointerv = pAPI->glGetVertexAttribPointerv; + glIsProgram = pAPI->glIsProgram; + glIsShader = pAPI->glIsShader; + glLinkProgram = pAPI->glLinkProgram; + glShaderSource = pAPI->glShaderSource; + glUseProgram = pAPI->glUseProgram; + glUniform1f = pAPI->glUniform1f; + glUniform2f = pAPI->glUniform2f; + glUniform3f = pAPI->glUniform3f; + glUniform4f = pAPI->glUniform4f; + glUniform1i = pAPI->glUniform1i; + glUniform2i = pAPI->glUniform2i; + glUniform3i = pAPI->glUniform3i; + glUniform4i = pAPI->glUniform4i; + glUniform1fv = pAPI->glUniform1fv; + glUniform2fv = pAPI->glUniform2fv; + glUniform3fv = pAPI->glUniform3fv; + glUniform4fv = pAPI->glUniform4fv; + glUniform1iv = pAPI->glUniform1iv; + glUniform2iv = pAPI->glUniform2iv; + glUniform3iv = pAPI->glUniform3iv; + glUniform4iv = pAPI->glUniform4iv; + glUniformMatrix2fv = pAPI->glUniformMatrix2fv; + glUniformMatrix3fv = pAPI->glUniformMatrix3fv; + glUniformMatrix4fv = pAPI->glUniformMatrix4fv; + glValidateProgram = pAPI->glValidateProgram; + glVertexAttrib1d = pAPI->glVertexAttrib1d; + glVertexAttrib1dv = pAPI->glVertexAttrib1dv; + glVertexAttrib1f = pAPI->glVertexAttrib1f; + glVertexAttrib1fv = pAPI->glVertexAttrib1fv; + glVertexAttrib1s = pAPI->glVertexAttrib1s; + glVertexAttrib1sv = pAPI->glVertexAttrib1sv; + glVertexAttrib2d = pAPI->glVertexAttrib2d; + glVertexAttrib2dv = pAPI->glVertexAttrib2dv; + glVertexAttrib2f = pAPI->glVertexAttrib2f; + glVertexAttrib2fv = pAPI->glVertexAttrib2fv; + glVertexAttrib2s = pAPI->glVertexAttrib2s; + glVertexAttrib2sv = pAPI->glVertexAttrib2sv; + glVertexAttrib3d = pAPI->glVertexAttrib3d; + glVertexAttrib3dv = pAPI->glVertexAttrib3dv; + glVertexAttrib3f = pAPI->glVertexAttrib3f; + glVertexAttrib3fv = pAPI->glVertexAttrib3fv; + glVertexAttrib3s = pAPI->glVertexAttrib3s; + glVertexAttrib3sv = pAPI->glVertexAttrib3sv; + glVertexAttrib4Nbv = pAPI->glVertexAttrib4Nbv; + glVertexAttrib4Niv = pAPI->glVertexAttrib4Niv; + glVertexAttrib4Nsv = pAPI->glVertexAttrib4Nsv; + glVertexAttrib4Nub = pAPI->glVertexAttrib4Nub; + glVertexAttrib4Nubv = pAPI->glVertexAttrib4Nubv; + glVertexAttrib4Nuiv = pAPI->glVertexAttrib4Nuiv; + glVertexAttrib4Nusv = pAPI->glVertexAttrib4Nusv; + glVertexAttrib4bv = pAPI->glVertexAttrib4bv; + glVertexAttrib4d = pAPI->glVertexAttrib4d; + glVertexAttrib4dv = pAPI->glVertexAttrib4dv; + glVertexAttrib4f = pAPI->glVertexAttrib4f; + glVertexAttrib4fv = pAPI->glVertexAttrib4fv; + glVertexAttrib4iv = pAPI->glVertexAttrib4iv; + glVertexAttrib4s = pAPI->glVertexAttrib4s; + glVertexAttrib4sv = pAPI->glVertexAttrib4sv; + glVertexAttrib4ubv = pAPI->glVertexAttrib4ubv; + glVertexAttrib4uiv = pAPI->glVertexAttrib4uiv; + glVertexAttrib4usv = pAPI->glVertexAttrib4usv; + glVertexAttribPointer = pAPI->glVertexAttribPointer; + glUniformMatrix2x3fv = pAPI->glUniformMatrix2x3fv; + glUniformMatrix3x2fv = pAPI->glUniformMatrix3x2fv; + glUniformMatrix2x4fv = pAPI->glUniformMatrix2x4fv; + glUniformMatrix4x2fv = pAPI->glUniformMatrix4x2fv; + glUniformMatrix3x4fv = pAPI->glUniformMatrix3x4fv; + glUniformMatrix4x3fv = pAPI->glUniformMatrix4x3fv; + glColorMaski = pAPI->glColorMaski; + glGetBooleani_v = pAPI->glGetBooleani_v; + glGetIntegeri_v = pAPI->glGetIntegeri_v; + glEnablei = pAPI->glEnablei; + glDisablei = pAPI->glDisablei; + glIsEnabledi = pAPI->glIsEnabledi; + glBeginTransformFeedback = pAPI->glBeginTransformFeedback; + glEndTransformFeedback = pAPI->glEndTransformFeedback; + glBindBufferRange = pAPI->glBindBufferRange; + glBindBufferBase = pAPI->glBindBufferBase; + glTransformFeedbackVaryings = pAPI->glTransformFeedbackVaryings; + glGetTransformFeedbackVarying = pAPI->glGetTransformFeedbackVarying; + glClampColor = pAPI->glClampColor; + glBeginConditionalRender = pAPI->glBeginConditionalRender; + glEndConditionalRender = pAPI->glEndConditionalRender; + glVertexAttribIPointer = pAPI->glVertexAttribIPointer; + glGetVertexAttribIiv = pAPI->glGetVertexAttribIiv; + glGetVertexAttribIuiv = pAPI->glGetVertexAttribIuiv; + glVertexAttribI1i = pAPI->glVertexAttribI1i; + glVertexAttribI2i = pAPI->glVertexAttribI2i; + glVertexAttribI3i = pAPI->glVertexAttribI3i; + glVertexAttribI4i = pAPI->glVertexAttribI4i; + glVertexAttribI1ui = pAPI->glVertexAttribI1ui; + glVertexAttribI2ui = pAPI->glVertexAttribI2ui; + glVertexAttribI3ui = pAPI->glVertexAttribI3ui; + glVertexAttribI4ui = pAPI->glVertexAttribI4ui; + glVertexAttribI1iv = pAPI->glVertexAttribI1iv; + glVertexAttribI2iv = pAPI->glVertexAttribI2iv; + glVertexAttribI3iv = pAPI->glVertexAttribI3iv; + glVertexAttribI4iv = pAPI->glVertexAttribI4iv; + glVertexAttribI1uiv = pAPI->glVertexAttribI1uiv; + glVertexAttribI2uiv = pAPI->glVertexAttribI2uiv; + glVertexAttribI3uiv = pAPI->glVertexAttribI3uiv; + glVertexAttribI4uiv = pAPI->glVertexAttribI4uiv; + glVertexAttribI4bv = pAPI->glVertexAttribI4bv; + glVertexAttribI4sv = pAPI->glVertexAttribI4sv; + glVertexAttribI4ubv = pAPI->glVertexAttribI4ubv; + glVertexAttribI4usv = pAPI->glVertexAttribI4usv; + glGetUniformuiv = pAPI->glGetUniformuiv; + glBindFragDataLocation = pAPI->glBindFragDataLocation; + glGetFragDataLocation = pAPI->glGetFragDataLocation; + glUniform1ui = pAPI->glUniform1ui; + glUniform2ui = pAPI->glUniform2ui; + glUniform3ui = pAPI->glUniform3ui; + glUniform4ui = pAPI->glUniform4ui; + glUniform1uiv = pAPI->glUniform1uiv; + glUniform2uiv = pAPI->glUniform2uiv; + glUniform3uiv = pAPI->glUniform3uiv; + glUniform4uiv = pAPI->glUniform4uiv; + glTexParameterIiv = pAPI->glTexParameterIiv; + glTexParameterIuiv = pAPI->glTexParameterIuiv; + glGetTexParameterIiv = pAPI->glGetTexParameterIiv; + glGetTexParameterIuiv = pAPI->glGetTexParameterIuiv; + glClearBufferiv = pAPI->glClearBufferiv; + glClearBufferuiv = pAPI->glClearBufferuiv; + glClearBufferfv = pAPI->glClearBufferfv; + glClearBufferfi = pAPI->glClearBufferfi; + glGetStringi = pAPI->glGetStringi; + glIsRenderbuffer = pAPI->glIsRenderbuffer; + glBindRenderbuffer = pAPI->glBindRenderbuffer; + glDeleteRenderbuffers = pAPI->glDeleteRenderbuffers; + glGenRenderbuffers = pAPI->glGenRenderbuffers; + glRenderbufferStorage = pAPI->glRenderbufferStorage; + glGetRenderbufferParameteriv = pAPI->glGetRenderbufferParameteriv; + glIsFramebuffer = pAPI->glIsFramebuffer; + glBindFramebuffer = pAPI->glBindFramebuffer; + glDeleteFramebuffers = pAPI->glDeleteFramebuffers; + glGenFramebuffers = pAPI->glGenFramebuffers; + glCheckFramebufferStatus = pAPI->glCheckFramebufferStatus; + glFramebufferTexture1D = pAPI->glFramebufferTexture1D; + glFramebufferTexture2D = pAPI->glFramebufferTexture2D; + glFramebufferTexture3D = pAPI->glFramebufferTexture3D; + glFramebufferRenderbuffer = pAPI->glFramebufferRenderbuffer; + glGetFramebufferAttachmentParameteriv = pAPI->glGetFramebufferAttachmentParameteriv; + glGenerateMipmap = pAPI->glGenerateMipmap; + glBlitFramebuffer = pAPI->glBlitFramebuffer; + glRenderbufferStorageMultisample = pAPI->glRenderbufferStorageMultisample; + glFramebufferTextureLayer = pAPI->glFramebufferTextureLayer; + glMapBufferRange = pAPI->glMapBufferRange; + glFlushMappedBufferRange = pAPI->glFlushMappedBufferRange; + glBindVertexArray = pAPI->glBindVertexArray; + glDeleteVertexArrays = pAPI->glDeleteVertexArrays; + glGenVertexArrays = pAPI->glGenVertexArrays; + glIsVertexArray = pAPI->glIsVertexArray; + glDrawArraysInstanced = pAPI->glDrawArraysInstanced; + glDrawElementsInstanced = pAPI->glDrawElementsInstanced; + glTexBuffer = pAPI->glTexBuffer; + glPrimitiveRestartIndex = pAPI->glPrimitiveRestartIndex; + glCopyBufferSubData = pAPI->glCopyBufferSubData; + glGetUniformIndices = pAPI->glGetUniformIndices; + glGetActiveUniformsiv = pAPI->glGetActiveUniformsiv; + glGetActiveUniformName = pAPI->glGetActiveUniformName; + glGetUniformBlockIndex = pAPI->glGetUniformBlockIndex; + glGetActiveUniformBlockiv = pAPI->glGetActiveUniformBlockiv; + glGetActiveUniformBlockName = pAPI->glGetActiveUniformBlockName; + glUniformBlockBinding = pAPI->glUniformBlockBinding; + glDrawElementsBaseVertex = pAPI->glDrawElementsBaseVertex; + glDrawRangeElementsBaseVertex = pAPI->glDrawRangeElementsBaseVertex; + glDrawElementsInstancedBaseVertex = pAPI->glDrawElementsInstancedBaseVertex; + glMultiDrawElementsBaseVertex = pAPI->glMultiDrawElementsBaseVertex; + glProvokingVertex = pAPI->glProvokingVertex; + glFenceSync = pAPI->glFenceSync; + glIsSync = pAPI->glIsSync; + glDeleteSync = pAPI->glDeleteSync; + glClientWaitSync = pAPI->glClientWaitSync; + glWaitSync = pAPI->glWaitSync; + glGetInteger64v = pAPI->glGetInteger64v; + glGetSynciv = pAPI->glGetSynciv; + glGetInteger64i_v = pAPI->glGetInteger64i_v; + glGetBufferParameteri64v = pAPI->glGetBufferParameteri64v; + glFramebufferTexture = pAPI->glFramebufferTexture; + glTexImage2DMultisample = pAPI->glTexImage2DMultisample; + glTexImage3DMultisample = pAPI->glTexImage3DMultisample; + glGetMultisamplefv = pAPI->glGetMultisamplefv; + glSampleMaski = pAPI->glSampleMaski; + glBindFragDataLocationIndexed = pAPI->glBindFragDataLocationIndexed; + glGetFragDataIndex = pAPI->glGetFragDataIndex; + glGenSamplers = pAPI->glGenSamplers; + glDeleteSamplers = pAPI->glDeleteSamplers; + glIsSampler = pAPI->glIsSampler; + glBindSampler = pAPI->glBindSampler; + glSamplerParameteri = pAPI->glSamplerParameteri; + glSamplerParameteriv = pAPI->glSamplerParameteriv; + glSamplerParameterf = pAPI->glSamplerParameterf; + glSamplerParameterfv = pAPI->glSamplerParameterfv; + glSamplerParameterIiv = pAPI->glSamplerParameterIiv; + glSamplerParameterIuiv = pAPI->glSamplerParameterIuiv; + glGetSamplerParameteriv = pAPI->glGetSamplerParameteriv; + glGetSamplerParameterIiv = pAPI->glGetSamplerParameterIiv; + glGetSamplerParameterfv = pAPI->glGetSamplerParameterfv; + glGetSamplerParameterIuiv = pAPI->glGetSamplerParameterIuiv; + glQueryCounter = pAPI->glQueryCounter; + glGetQueryObjecti64v = pAPI->glGetQueryObjecti64v; + glGetQueryObjectui64v = pAPI->glGetQueryObjectui64v; + glVertexAttribDivisor = pAPI->glVertexAttribDivisor; + glVertexAttribP1ui = pAPI->glVertexAttribP1ui; + glVertexAttribP1uiv = pAPI->glVertexAttribP1uiv; + glVertexAttribP2ui = pAPI->glVertexAttribP2ui; + glVertexAttribP2uiv = pAPI->glVertexAttribP2uiv; + glVertexAttribP3ui = pAPI->glVertexAttribP3ui; + glVertexAttribP3uiv = pAPI->glVertexAttribP3uiv; + glVertexAttribP4ui = pAPI->glVertexAttribP4ui; + glVertexAttribP4uiv = pAPI->glVertexAttribP4uiv; + glVertexP2ui = pAPI->glVertexP2ui; + glVertexP2uiv = pAPI->glVertexP2uiv; + glVertexP3ui = pAPI->glVertexP3ui; + glVertexP3uiv = pAPI->glVertexP3uiv; + glVertexP4ui = pAPI->glVertexP4ui; + glVertexP4uiv = pAPI->glVertexP4uiv; + glTexCoordP1ui = pAPI->glTexCoordP1ui; + glTexCoordP1uiv = pAPI->glTexCoordP1uiv; + glTexCoordP2ui = pAPI->glTexCoordP2ui; + glTexCoordP2uiv = pAPI->glTexCoordP2uiv; + glTexCoordP3ui = pAPI->glTexCoordP3ui; + glTexCoordP3uiv = pAPI->glTexCoordP3uiv; + glTexCoordP4ui = pAPI->glTexCoordP4ui; + glTexCoordP4uiv = pAPI->glTexCoordP4uiv; + glMultiTexCoordP1ui = pAPI->glMultiTexCoordP1ui; + glMultiTexCoordP1uiv = pAPI->glMultiTexCoordP1uiv; + glMultiTexCoordP2ui = pAPI->glMultiTexCoordP2ui; + glMultiTexCoordP2uiv = pAPI->glMultiTexCoordP2uiv; + glMultiTexCoordP3ui = pAPI->glMultiTexCoordP3ui; + glMultiTexCoordP3uiv = pAPI->glMultiTexCoordP3uiv; + glMultiTexCoordP4ui = pAPI->glMultiTexCoordP4ui; + glMultiTexCoordP4uiv = pAPI->glMultiTexCoordP4uiv; + glNormalP3ui = pAPI->glNormalP3ui; + glNormalP3uiv = pAPI->glNormalP3uiv; + glColorP3ui = pAPI->glColorP3ui; + glColorP3uiv = pAPI->glColorP3uiv; + glColorP4ui = pAPI->glColorP4ui; + glColorP4uiv = pAPI->glColorP4uiv; + glSecondaryColorP3ui = pAPI->glSecondaryColorP3ui; + glSecondaryColorP3uiv = pAPI->glSecondaryColorP3uiv; + glMinSampleShading = pAPI->glMinSampleShading; + glBlendEquationi = pAPI->glBlendEquationi; + glBlendEquationSeparatei = pAPI->glBlendEquationSeparatei; + glBlendFunci = pAPI->glBlendFunci; + glBlendFuncSeparatei = pAPI->glBlendFuncSeparatei; + glDrawArraysIndirect = pAPI->glDrawArraysIndirect; + glDrawElementsIndirect = pAPI->glDrawElementsIndirect; + glUniform1d = pAPI->glUniform1d; + glUniform2d = pAPI->glUniform2d; + glUniform3d = pAPI->glUniform3d; + glUniform4d = pAPI->glUniform4d; + glUniform1dv = pAPI->glUniform1dv; + glUniform2dv = pAPI->glUniform2dv; + glUniform3dv = pAPI->glUniform3dv; + glUniform4dv = pAPI->glUniform4dv; + glUniformMatrix2dv = pAPI->glUniformMatrix2dv; + glUniformMatrix3dv = pAPI->glUniformMatrix3dv; + glUniformMatrix4dv = pAPI->glUniformMatrix4dv; + glUniformMatrix2x3dv = pAPI->glUniformMatrix2x3dv; + glUniformMatrix2x4dv = pAPI->glUniformMatrix2x4dv; + glUniformMatrix3x2dv = pAPI->glUniformMatrix3x2dv; + glUniformMatrix3x4dv = pAPI->glUniformMatrix3x4dv; + glUniformMatrix4x2dv = pAPI->glUniformMatrix4x2dv; + glUniformMatrix4x3dv = pAPI->glUniformMatrix4x3dv; + glGetUniformdv = pAPI->glGetUniformdv; + glGetSubroutineUniformLocation = pAPI->glGetSubroutineUniformLocation; + glGetSubroutineIndex = pAPI->glGetSubroutineIndex; + glGetActiveSubroutineUniformiv = pAPI->glGetActiveSubroutineUniformiv; + glGetActiveSubroutineUniformName = pAPI->glGetActiveSubroutineUniformName; + glGetActiveSubroutineName = pAPI->glGetActiveSubroutineName; + glUniformSubroutinesuiv = pAPI->glUniformSubroutinesuiv; + glGetUniformSubroutineuiv = pAPI->glGetUniformSubroutineuiv; + glGetProgramStageiv = pAPI->glGetProgramStageiv; + glPatchParameteri = pAPI->glPatchParameteri; + glPatchParameterfv = pAPI->glPatchParameterfv; + glBindTransformFeedback = pAPI->glBindTransformFeedback; + glDeleteTransformFeedbacks = pAPI->glDeleteTransformFeedbacks; + glGenTransformFeedbacks = pAPI->glGenTransformFeedbacks; + glIsTransformFeedback = pAPI->glIsTransformFeedback; + glPauseTransformFeedback = pAPI->glPauseTransformFeedback; + glResumeTransformFeedback = pAPI->glResumeTransformFeedback; + glDrawTransformFeedback = pAPI->glDrawTransformFeedback; + glDrawTransformFeedbackStream = pAPI->glDrawTransformFeedbackStream; + glBeginQueryIndexed = pAPI->glBeginQueryIndexed; + glEndQueryIndexed = pAPI->glEndQueryIndexed; + glGetQueryIndexediv = pAPI->glGetQueryIndexediv; + glReleaseShaderCompiler = pAPI->glReleaseShaderCompiler; + glShaderBinary = pAPI->glShaderBinary; + glGetShaderPrecisionFormat = pAPI->glGetShaderPrecisionFormat; + glDepthRangef = pAPI->glDepthRangef; + glClearDepthf = pAPI->glClearDepthf; + glGetProgramBinary = pAPI->glGetProgramBinary; + glProgramBinary = pAPI->glProgramBinary; + glProgramParameteri = pAPI->glProgramParameteri; + glUseProgramStages = pAPI->glUseProgramStages; + glActiveShaderProgram = pAPI->glActiveShaderProgram; + glCreateShaderProgramv = pAPI->glCreateShaderProgramv; + glBindProgramPipeline = pAPI->glBindProgramPipeline; + glDeleteProgramPipelines = pAPI->glDeleteProgramPipelines; + glGenProgramPipelines = pAPI->glGenProgramPipelines; + glIsProgramPipeline = pAPI->glIsProgramPipeline; + glGetProgramPipelineiv = pAPI->glGetProgramPipelineiv; + glProgramUniform1i = pAPI->glProgramUniform1i; + glProgramUniform1iv = pAPI->glProgramUniform1iv; + glProgramUniform1f = pAPI->glProgramUniform1f; + glProgramUniform1fv = pAPI->glProgramUniform1fv; + glProgramUniform1d = pAPI->glProgramUniform1d; + glProgramUniform1dv = pAPI->glProgramUniform1dv; + glProgramUniform1ui = pAPI->glProgramUniform1ui; + glProgramUniform1uiv = pAPI->glProgramUniform1uiv; + glProgramUniform2i = pAPI->glProgramUniform2i; + glProgramUniform2iv = pAPI->glProgramUniform2iv; + glProgramUniform2f = pAPI->glProgramUniform2f; + glProgramUniform2fv = pAPI->glProgramUniform2fv; + glProgramUniform2d = pAPI->glProgramUniform2d; + glProgramUniform2dv = pAPI->glProgramUniform2dv; + glProgramUniform2ui = pAPI->glProgramUniform2ui; + glProgramUniform2uiv = pAPI->glProgramUniform2uiv; + glProgramUniform3i = pAPI->glProgramUniform3i; + glProgramUniform3iv = pAPI->glProgramUniform3iv; + glProgramUniform3f = pAPI->glProgramUniform3f; + glProgramUniform3fv = pAPI->glProgramUniform3fv; + glProgramUniform3d = pAPI->glProgramUniform3d; + glProgramUniform3dv = pAPI->glProgramUniform3dv; + glProgramUniform3ui = pAPI->glProgramUniform3ui; + glProgramUniform3uiv = pAPI->glProgramUniform3uiv; + glProgramUniform4i = pAPI->glProgramUniform4i; + glProgramUniform4iv = pAPI->glProgramUniform4iv; + glProgramUniform4f = pAPI->glProgramUniform4f; + glProgramUniform4fv = pAPI->glProgramUniform4fv; + glProgramUniform4d = pAPI->glProgramUniform4d; + glProgramUniform4dv = pAPI->glProgramUniform4dv; + glProgramUniform4ui = pAPI->glProgramUniform4ui; + glProgramUniform4uiv = pAPI->glProgramUniform4uiv; + glProgramUniformMatrix2fv = pAPI->glProgramUniformMatrix2fv; + glProgramUniformMatrix3fv = pAPI->glProgramUniformMatrix3fv; + glProgramUniformMatrix4fv = pAPI->glProgramUniformMatrix4fv; + glProgramUniformMatrix2dv = pAPI->glProgramUniformMatrix2dv; + glProgramUniformMatrix3dv = pAPI->glProgramUniformMatrix3dv; + glProgramUniformMatrix4dv = pAPI->glProgramUniformMatrix4dv; + glProgramUniformMatrix2x3fv = pAPI->glProgramUniformMatrix2x3fv; + glProgramUniformMatrix3x2fv = pAPI->glProgramUniformMatrix3x2fv; + glProgramUniformMatrix2x4fv = pAPI->glProgramUniformMatrix2x4fv; + glProgramUniformMatrix4x2fv = pAPI->glProgramUniformMatrix4x2fv; + glProgramUniformMatrix3x4fv = pAPI->glProgramUniformMatrix3x4fv; + glProgramUniformMatrix4x3fv = pAPI->glProgramUniformMatrix4x3fv; + glProgramUniformMatrix2x3dv = pAPI->glProgramUniformMatrix2x3dv; + glProgramUniformMatrix3x2dv = pAPI->glProgramUniformMatrix3x2dv; + glProgramUniformMatrix2x4dv = pAPI->glProgramUniformMatrix2x4dv; + glProgramUniformMatrix4x2dv = pAPI->glProgramUniformMatrix4x2dv; + glProgramUniformMatrix3x4dv = pAPI->glProgramUniformMatrix3x4dv; + glProgramUniformMatrix4x3dv = pAPI->glProgramUniformMatrix4x3dv; + glValidateProgramPipeline = pAPI->glValidateProgramPipeline; + glGetProgramPipelineInfoLog = pAPI->glGetProgramPipelineInfoLog; + glVertexAttribL1d = pAPI->glVertexAttribL1d; + glVertexAttribL2d = pAPI->glVertexAttribL2d; + glVertexAttribL3d = pAPI->glVertexAttribL3d; + glVertexAttribL4d = pAPI->glVertexAttribL4d; + glVertexAttribL1dv = pAPI->glVertexAttribL1dv; + glVertexAttribL2dv = pAPI->glVertexAttribL2dv; + glVertexAttribL3dv = pAPI->glVertexAttribL3dv; + glVertexAttribL4dv = pAPI->glVertexAttribL4dv; + glVertexAttribLPointer = pAPI->glVertexAttribLPointer; + glGetVertexAttribLdv = pAPI->glGetVertexAttribLdv; + glViewportArrayv = pAPI->glViewportArrayv; + glViewportIndexedf = pAPI->glViewportIndexedf; + glViewportIndexedfv = pAPI->glViewportIndexedfv; + glScissorArrayv = pAPI->glScissorArrayv; + glScissorIndexed = pAPI->glScissorIndexed; + glScissorIndexedv = pAPI->glScissorIndexedv; + glDepthRangeArrayv = pAPI->glDepthRangeArrayv; + glDepthRangeIndexed = pAPI->glDepthRangeIndexed; + glGetFloati_v = pAPI->glGetFloati_v; + glGetDoublei_v = pAPI->glGetDoublei_v; + glDrawArraysInstancedBaseInstance = pAPI->glDrawArraysInstancedBaseInstance; + glDrawElementsInstancedBaseInstance = pAPI->glDrawElementsInstancedBaseInstance; + glDrawElementsInstancedBaseVertexBaseInstance = pAPI->glDrawElementsInstancedBaseVertexBaseInstance; + glGetInternalformativ = pAPI->glGetInternalformativ; + glGetActiveAtomicCounterBufferiv = pAPI->glGetActiveAtomicCounterBufferiv; + glBindImageTexture = pAPI->glBindImageTexture; + glMemoryBarrier = pAPI->glMemoryBarrier; + glTexStorage1D = pAPI->glTexStorage1D; + glTexStorage2D = pAPI->glTexStorage2D; + glTexStorage3D = pAPI->glTexStorage3D; + glDrawTransformFeedbackInstanced = pAPI->glDrawTransformFeedbackInstanced; + glDrawTransformFeedbackStreamInstanced = pAPI->glDrawTransformFeedbackStreamInstanced; + glClearBufferData = pAPI->glClearBufferData; + glClearBufferSubData = pAPI->glClearBufferSubData; + glDispatchCompute = pAPI->glDispatchCompute; + glDispatchComputeIndirect = pAPI->glDispatchComputeIndirect; + glCopyImageSubData = pAPI->glCopyImageSubData; + glFramebufferParameteri = pAPI->glFramebufferParameteri; + glGetFramebufferParameteriv = pAPI->glGetFramebufferParameteriv; + glGetInternalformati64v = pAPI->glGetInternalformati64v; + glInvalidateTexSubImage = pAPI->glInvalidateTexSubImage; + glInvalidateTexImage = pAPI->glInvalidateTexImage; + glInvalidateBufferSubData = pAPI->glInvalidateBufferSubData; + glInvalidateBufferData = pAPI->glInvalidateBufferData; + glInvalidateFramebuffer = pAPI->glInvalidateFramebuffer; + glInvalidateSubFramebuffer = pAPI->glInvalidateSubFramebuffer; + glMultiDrawArraysIndirect = pAPI->glMultiDrawArraysIndirect; + glMultiDrawElementsIndirect = pAPI->glMultiDrawElementsIndirect; + glGetProgramInterfaceiv = pAPI->glGetProgramInterfaceiv; + glGetProgramResourceIndex = pAPI->glGetProgramResourceIndex; + glGetProgramResourceName = pAPI->glGetProgramResourceName; + glGetProgramResourceiv = pAPI->glGetProgramResourceiv; + glGetProgramResourceLocation = pAPI->glGetProgramResourceLocation; + glGetProgramResourceLocationIndex = pAPI->glGetProgramResourceLocationIndex; + glShaderStorageBlockBinding = pAPI->glShaderStorageBlockBinding; + glTexBufferRange = pAPI->glTexBufferRange; + glTexStorage2DMultisample = pAPI->glTexStorage2DMultisample; + glTexStorage3DMultisample = pAPI->glTexStorage3DMultisample; + glTextureView = pAPI->glTextureView; + glBindVertexBuffer = pAPI->glBindVertexBuffer; + glVertexAttribFormat = pAPI->glVertexAttribFormat; + glVertexAttribIFormat = pAPI->glVertexAttribIFormat; + glVertexAttribLFormat = pAPI->glVertexAttribLFormat; + glVertexAttribBinding = pAPI->glVertexAttribBinding; + glVertexBindingDivisor = pAPI->glVertexBindingDivisor; + glDebugMessageControl = pAPI->glDebugMessageControl; + glDebugMessageInsert = pAPI->glDebugMessageInsert; + glDebugMessageCallback = pAPI->glDebugMessageCallback; + glGetDebugMessageLog = pAPI->glGetDebugMessageLog; + glPushDebugGroup = pAPI->glPushDebugGroup; + glPopDebugGroup = pAPI->glPopDebugGroup; + glObjectLabel = pAPI->glObjectLabel; + glGetObjectLabel = pAPI->glGetObjectLabel; + glObjectPtrLabel = pAPI->glObjectPtrLabel; + glGetObjectPtrLabel = pAPI->glGetObjectPtrLabel; + glBufferStorage = pAPI->glBufferStorage; + glClearTexImage = pAPI->glClearTexImage; + glClearTexSubImage = pAPI->glClearTexSubImage; + glBindBuffersBase = pAPI->glBindBuffersBase; + glBindBuffersRange = pAPI->glBindBuffersRange; + glBindTextures = pAPI->glBindTextures; + glBindSamplers = pAPI->glBindSamplers; + glBindImageTextures = pAPI->glBindImageTextures; + glBindVertexBuffers = pAPI->glBindVertexBuffers; + glClipControl = pAPI->glClipControl; + glCreateTransformFeedbacks = pAPI->glCreateTransformFeedbacks; + glTransformFeedbackBufferBase = pAPI->glTransformFeedbackBufferBase; + glTransformFeedbackBufferRange = pAPI->glTransformFeedbackBufferRange; + glGetTransformFeedbackiv = pAPI->glGetTransformFeedbackiv; + glGetTransformFeedbacki_v = pAPI->glGetTransformFeedbacki_v; + glGetTransformFeedbacki64_v = pAPI->glGetTransformFeedbacki64_v; + glCreateBuffers = pAPI->glCreateBuffers; + glNamedBufferStorage = pAPI->glNamedBufferStorage; + glNamedBufferData = pAPI->glNamedBufferData; + glNamedBufferSubData = pAPI->glNamedBufferSubData; + glCopyNamedBufferSubData = pAPI->glCopyNamedBufferSubData; + glClearNamedBufferData = pAPI->glClearNamedBufferData; + glClearNamedBufferSubData = pAPI->glClearNamedBufferSubData; + glMapNamedBuffer = pAPI->glMapNamedBuffer; + glMapNamedBufferRange = pAPI->glMapNamedBufferRange; + glUnmapNamedBuffer = pAPI->glUnmapNamedBuffer; + glFlushMappedNamedBufferRange = pAPI->glFlushMappedNamedBufferRange; + glGetNamedBufferParameteriv = pAPI->glGetNamedBufferParameteriv; + glGetNamedBufferParameteri64v = pAPI->glGetNamedBufferParameteri64v; + glGetNamedBufferPointerv = pAPI->glGetNamedBufferPointerv; + glGetNamedBufferSubData = pAPI->glGetNamedBufferSubData; + glCreateFramebuffers = pAPI->glCreateFramebuffers; + glNamedFramebufferRenderbuffer = pAPI->glNamedFramebufferRenderbuffer; + glNamedFramebufferParameteri = pAPI->glNamedFramebufferParameteri; + glNamedFramebufferTexture = pAPI->glNamedFramebufferTexture; + glNamedFramebufferTextureLayer = pAPI->glNamedFramebufferTextureLayer; + glNamedFramebufferDrawBuffer = pAPI->glNamedFramebufferDrawBuffer; + glNamedFramebufferDrawBuffers = pAPI->glNamedFramebufferDrawBuffers; + glNamedFramebufferReadBuffer = pAPI->glNamedFramebufferReadBuffer; + glInvalidateNamedFramebufferData = pAPI->glInvalidateNamedFramebufferData; + glInvalidateNamedFramebufferSubData = pAPI->glInvalidateNamedFramebufferSubData; + glClearNamedFramebufferiv = pAPI->glClearNamedFramebufferiv; + glClearNamedFramebufferuiv = pAPI->glClearNamedFramebufferuiv; + glClearNamedFramebufferfv = pAPI->glClearNamedFramebufferfv; + glClearNamedFramebufferfi = pAPI->glClearNamedFramebufferfi; + glBlitNamedFramebuffer = pAPI->glBlitNamedFramebuffer; + glCheckNamedFramebufferStatus = pAPI->glCheckNamedFramebufferStatus; + glGetNamedFramebufferParameteriv = pAPI->glGetNamedFramebufferParameteriv; + glGetNamedFramebufferAttachmentParameteriv = pAPI->glGetNamedFramebufferAttachmentParameteriv; + glCreateRenderbuffers = pAPI->glCreateRenderbuffers; + glNamedRenderbufferStorage = pAPI->glNamedRenderbufferStorage; + glNamedRenderbufferStorageMultisample = pAPI->glNamedRenderbufferStorageMultisample; + glGetNamedRenderbufferParameteriv = pAPI->glGetNamedRenderbufferParameteriv; + glCreateTextures = pAPI->glCreateTextures; + glTextureBuffer = pAPI->glTextureBuffer; + glTextureBufferRange = pAPI->glTextureBufferRange; + glTextureStorage1D = pAPI->glTextureStorage1D; + glTextureStorage2D = pAPI->glTextureStorage2D; + glTextureStorage3D = pAPI->glTextureStorage3D; + glTextureStorage2DMultisample = pAPI->glTextureStorage2DMultisample; + glTextureStorage3DMultisample = pAPI->glTextureStorage3DMultisample; + glTextureSubImage1D = pAPI->glTextureSubImage1D; + glTextureSubImage2D = pAPI->glTextureSubImage2D; + glTextureSubImage3D = pAPI->glTextureSubImage3D; + glCompressedTextureSubImage1D = pAPI->glCompressedTextureSubImage1D; + glCompressedTextureSubImage2D = pAPI->glCompressedTextureSubImage2D; + glCompressedTextureSubImage3D = pAPI->glCompressedTextureSubImage3D; + glCopyTextureSubImage1D = pAPI->glCopyTextureSubImage1D; + glCopyTextureSubImage2D = pAPI->glCopyTextureSubImage2D; + glCopyTextureSubImage3D = pAPI->glCopyTextureSubImage3D; + glTextureParameterf = pAPI->glTextureParameterf; + glTextureParameterfv = pAPI->glTextureParameterfv; + glTextureParameteri = pAPI->glTextureParameteri; + glTextureParameterIiv = pAPI->glTextureParameterIiv; + glTextureParameterIuiv = pAPI->glTextureParameterIuiv; + glTextureParameteriv = pAPI->glTextureParameteriv; + glGenerateTextureMipmap = pAPI->glGenerateTextureMipmap; + glBindTextureUnit = pAPI->glBindTextureUnit; + glGetTextureImage = pAPI->glGetTextureImage; + glGetCompressedTextureImage = pAPI->glGetCompressedTextureImage; + glGetTextureLevelParameterfv = pAPI->glGetTextureLevelParameterfv; + glGetTextureLevelParameteriv = pAPI->glGetTextureLevelParameteriv; + glGetTextureParameterfv = pAPI->glGetTextureParameterfv; + glGetTextureParameterIiv = pAPI->glGetTextureParameterIiv; + glGetTextureParameterIuiv = pAPI->glGetTextureParameterIuiv; + glGetTextureParameteriv = pAPI->glGetTextureParameteriv; + glCreateVertexArrays = pAPI->glCreateVertexArrays; + glDisableVertexArrayAttrib = pAPI->glDisableVertexArrayAttrib; + glEnableVertexArrayAttrib = pAPI->glEnableVertexArrayAttrib; + glVertexArrayElementBuffer = pAPI->glVertexArrayElementBuffer; + glVertexArrayVertexBuffer = pAPI->glVertexArrayVertexBuffer; + glVertexArrayVertexBuffers = pAPI->glVertexArrayVertexBuffers; + glVertexArrayAttribBinding = pAPI->glVertexArrayAttribBinding; + glVertexArrayAttribFormat = pAPI->glVertexArrayAttribFormat; + glVertexArrayAttribIFormat = pAPI->glVertexArrayAttribIFormat; + glVertexArrayAttribLFormat = pAPI->glVertexArrayAttribLFormat; + glVertexArrayBindingDivisor = pAPI->glVertexArrayBindingDivisor; + glGetVertexArrayiv = pAPI->glGetVertexArrayiv; + glGetVertexArrayIndexediv = pAPI->glGetVertexArrayIndexediv; + glGetVertexArrayIndexed64iv = pAPI->glGetVertexArrayIndexed64iv; + glCreateSamplers = pAPI->glCreateSamplers; + glCreateProgramPipelines = pAPI->glCreateProgramPipelines; + glCreateQueries = pAPI->glCreateQueries; + glGetQueryBufferObjecti64v = pAPI->glGetQueryBufferObjecti64v; + glGetQueryBufferObjectiv = pAPI->glGetQueryBufferObjectiv; + glGetQueryBufferObjectui64v = pAPI->glGetQueryBufferObjectui64v; + glGetQueryBufferObjectuiv = pAPI->glGetQueryBufferObjectuiv; + glMemoryBarrierByRegion = pAPI->glMemoryBarrierByRegion; + glGetTextureSubImage = pAPI->glGetTextureSubImage; + glGetCompressedTextureSubImage = pAPI->glGetCompressedTextureSubImage; + glGetGraphicsResetStatus = pAPI->glGetGraphicsResetStatus; + glGetnCompressedTexImage = pAPI->glGetnCompressedTexImage; + glGetnTexImage = pAPI->glGetnTexImage; + glGetnUniformdv = pAPI->glGetnUniformdv; + glGetnUniformfv = pAPI->glGetnUniformfv; + glGetnUniformiv = pAPI->glGetnUniformiv; + glGetnUniformuiv = pAPI->glGetnUniformuiv; + glReadnPixels = pAPI->glReadnPixels; + glGetnMapdv = pAPI->glGetnMapdv; + glGetnMapfv = pAPI->glGetnMapfv; + glGetnMapiv = pAPI->glGetnMapiv; + glGetnPixelMapfv = pAPI->glGetnPixelMapfv; + glGetnPixelMapuiv = pAPI->glGetnPixelMapuiv; + glGetnPixelMapusv = pAPI->glGetnPixelMapusv; + glGetnPolygonStipple = pAPI->glGetnPolygonStipple; + glGetnColorTable = pAPI->glGetnColorTable; + glGetnConvolutionFilter = pAPI->glGetnConvolutionFilter; + glGetnSeparableFilter = pAPI->glGetnSeparableFilter; + glGetnHistogram = pAPI->glGetnHistogram; + glGetnMinmax = pAPI->glGetnMinmax; + glTextureBarrier = pAPI->glTextureBarrier; + glSpecializeShader = pAPI->glSpecializeShader; + glMultiDrawArraysIndirectCount = pAPI->glMultiDrawArraysIndirectCount; + glMultiDrawElementsIndirectCount = pAPI->glMultiDrawElementsIndirectCount; + glPolygonOffsetClamp = pAPI->glPolygonOffsetClamp; + glTbufferMask3DFX = pAPI->glTbufferMask3DFX; + glDebugMessageEnableAMD = pAPI->glDebugMessageEnableAMD; + glDebugMessageInsertAMD = pAPI->glDebugMessageInsertAMD; + glDebugMessageCallbackAMD = pAPI->glDebugMessageCallbackAMD; + glGetDebugMessageLogAMD = pAPI->glGetDebugMessageLogAMD; + glBlendFuncIndexedAMD = pAPI->glBlendFuncIndexedAMD; + glBlendFuncSeparateIndexedAMD = pAPI->glBlendFuncSeparateIndexedAMD; + glBlendEquationIndexedAMD = pAPI->glBlendEquationIndexedAMD; + glBlendEquationSeparateIndexedAMD = pAPI->glBlendEquationSeparateIndexedAMD; + glRenderbufferStorageMultisampleAdvancedAMD = pAPI->glRenderbufferStorageMultisampleAdvancedAMD; + glNamedRenderbufferStorageMultisampleAdvancedAMD = pAPI->glNamedRenderbufferStorageMultisampleAdvancedAMD; + glFramebufferSamplePositionsfvAMD = pAPI->glFramebufferSamplePositionsfvAMD; + glNamedFramebufferSamplePositionsfvAMD = pAPI->glNamedFramebufferSamplePositionsfvAMD; + glGetFramebufferParameterfvAMD = pAPI->glGetFramebufferParameterfvAMD; + glGetNamedFramebufferParameterfvAMD = pAPI->glGetNamedFramebufferParameterfvAMD; + glUniform1i64NV = pAPI->glUniform1i64NV; + glUniform2i64NV = pAPI->glUniform2i64NV; + glUniform3i64NV = pAPI->glUniform3i64NV; + glUniform4i64NV = pAPI->glUniform4i64NV; + glUniform1i64vNV = pAPI->glUniform1i64vNV; + glUniform2i64vNV = pAPI->glUniform2i64vNV; + glUniform3i64vNV = pAPI->glUniform3i64vNV; + glUniform4i64vNV = pAPI->glUniform4i64vNV; + glUniform1ui64NV = pAPI->glUniform1ui64NV; + glUniform2ui64NV = pAPI->glUniform2ui64NV; + glUniform3ui64NV = pAPI->glUniform3ui64NV; + glUniform4ui64NV = pAPI->glUniform4ui64NV; + glUniform1ui64vNV = pAPI->glUniform1ui64vNV; + glUniform2ui64vNV = pAPI->glUniform2ui64vNV; + glUniform3ui64vNV = pAPI->glUniform3ui64vNV; + glUniform4ui64vNV = pAPI->glUniform4ui64vNV; + glGetUniformi64vNV = pAPI->glGetUniformi64vNV; + glGetUniformui64vNV = pAPI->glGetUniformui64vNV; + glProgramUniform1i64NV = pAPI->glProgramUniform1i64NV; + glProgramUniform2i64NV = pAPI->glProgramUniform2i64NV; + glProgramUniform3i64NV = pAPI->glProgramUniform3i64NV; + glProgramUniform4i64NV = pAPI->glProgramUniform4i64NV; + glProgramUniform1i64vNV = pAPI->glProgramUniform1i64vNV; + glProgramUniform2i64vNV = pAPI->glProgramUniform2i64vNV; + glProgramUniform3i64vNV = pAPI->glProgramUniform3i64vNV; + glProgramUniform4i64vNV = pAPI->glProgramUniform4i64vNV; + glProgramUniform1ui64NV = pAPI->glProgramUniform1ui64NV; + glProgramUniform2ui64NV = pAPI->glProgramUniform2ui64NV; + glProgramUniform3ui64NV = pAPI->glProgramUniform3ui64NV; + glProgramUniform4ui64NV = pAPI->glProgramUniform4ui64NV; + glProgramUniform1ui64vNV = pAPI->glProgramUniform1ui64vNV; + glProgramUniform2ui64vNV = pAPI->glProgramUniform2ui64vNV; + glProgramUniform3ui64vNV = pAPI->glProgramUniform3ui64vNV; + glProgramUniform4ui64vNV = pAPI->glProgramUniform4ui64vNV; + glVertexAttribParameteriAMD = pAPI->glVertexAttribParameteriAMD; + glMultiDrawArraysIndirectAMD = pAPI->glMultiDrawArraysIndirectAMD; + glMultiDrawElementsIndirectAMD = pAPI->glMultiDrawElementsIndirectAMD; + glGenNamesAMD = pAPI->glGenNamesAMD; + glDeleteNamesAMD = pAPI->glDeleteNamesAMD; + glIsNameAMD = pAPI->glIsNameAMD; + glQueryObjectParameteruiAMD = pAPI->glQueryObjectParameteruiAMD; + glGetPerfMonitorGroupsAMD = pAPI->glGetPerfMonitorGroupsAMD; + glGetPerfMonitorCountersAMD = pAPI->glGetPerfMonitorCountersAMD; + glGetPerfMonitorGroupStringAMD = pAPI->glGetPerfMonitorGroupStringAMD; + glGetPerfMonitorCounterStringAMD = pAPI->glGetPerfMonitorCounterStringAMD; + glGetPerfMonitorCounterInfoAMD = pAPI->glGetPerfMonitorCounterInfoAMD; + glGenPerfMonitorsAMD = pAPI->glGenPerfMonitorsAMD; + glDeletePerfMonitorsAMD = pAPI->glDeletePerfMonitorsAMD; + glSelectPerfMonitorCountersAMD = pAPI->glSelectPerfMonitorCountersAMD; + glBeginPerfMonitorAMD = pAPI->glBeginPerfMonitorAMD; + glEndPerfMonitorAMD = pAPI->glEndPerfMonitorAMD; + glGetPerfMonitorCounterDataAMD = pAPI->glGetPerfMonitorCounterDataAMD; + glSetMultisamplefvAMD = pAPI->glSetMultisamplefvAMD; + glTexStorageSparseAMD = pAPI->glTexStorageSparseAMD; + glTextureStorageSparseAMD = pAPI->glTextureStorageSparseAMD; + glStencilOpValueAMD = pAPI->glStencilOpValueAMD; + glTessellationFactorAMD = pAPI->glTessellationFactorAMD; + glTessellationModeAMD = pAPI->glTessellationModeAMD; + glElementPointerAPPLE = pAPI->glElementPointerAPPLE; + glDrawElementArrayAPPLE = pAPI->glDrawElementArrayAPPLE; + glDrawRangeElementArrayAPPLE = pAPI->glDrawRangeElementArrayAPPLE; + glMultiDrawElementArrayAPPLE = pAPI->glMultiDrawElementArrayAPPLE; + glMultiDrawRangeElementArrayAPPLE = pAPI->glMultiDrawRangeElementArrayAPPLE; + glGenFencesAPPLE = pAPI->glGenFencesAPPLE; + glDeleteFencesAPPLE = pAPI->glDeleteFencesAPPLE; + glSetFenceAPPLE = pAPI->glSetFenceAPPLE; + glIsFenceAPPLE = pAPI->glIsFenceAPPLE; + glTestFenceAPPLE = pAPI->glTestFenceAPPLE; + glFinishFenceAPPLE = pAPI->glFinishFenceAPPLE; + glTestObjectAPPLE = pAPI->glTestObjectAPPLE; + glFinishObjectAPPLE = pAPI->glFinishObjectAPPLE; + glBufferParameteriAPPLE = pAPI->glBufferParameteriAPPLE; + glFlushMappedBufferRangeAPPLE = pAPI->glFlushMappedBufferRangeAPPLE; + glObjectPurgeableAPPLE = pAPI->glObjectPurgeableAPPLE; + glObjectUnpurgeableAPPLE = pAPI->glObjectUnpurgeableAPPLE; + glGetObjectParameterivAPPLE = pAPI->glGetObjectParameterivAPPLE; + glTextureRangeAPPLE = pAPI->glTextureRangeAPPLE; + glGetTexParameterPointervAPPLE = pAPI->glGetTexParameterPointervAPPLE; + glBindVertexArrayAPPLE = pAPI->glBindVertexArrayAPPLE; + glDeleteVertexArraysAPPLE = pAPI->glDeleteVertexArraysAPPLE; + glGenVertexArraysAPPLE = pAPI->glGenVertexArraysAPPLE; + glIsVertexArrayAPPLE = pAPI->glIsVertexArrayAPPLE; + glVertexArrayRangeAPPLE = pAPI->glVertexArrayRangeAPPLE; + glFlushVertexArrayRangeAPPLE = pAPI->glFlushVertexArrayRangeAPPLE; + glVertexArrayParameteriAPPLE = pAPI->glVertexArrayParameteriAPPLE; + glEnableVertexAttribAPPLE = pAPI->glEnableVertexAttribAPPLE; + glDisableVertexAttribAPPLE = pAPI->glDisableVertexAttribAPPLE; + glIsVertexAttribEnabledAPPLE = pAPI->glIsVertexAttribEnabledAPPLE; + glMapVertexAttrib1dAPPLE = pAPI->glMapVertexAttrib1dAPPLE; + glMapVertexAttrib1fAPPLE = pAPI->glMapVertexAttrib1fAPPLE; + glMapVertexAttrib2dAPPLE = pAPI->glMapVertexAttrib2dAPPLE; + glMapVertexAttrib2fAPPLE = pAPI->glMapVertexAttrib2fAPPLE; + glPrimitiveBoundingBoxARB = pAPI->glPrimitiveBoundingBoxARB; + glGetTextureHandleARB = pAPI->glGetTextureHandleARB; + glGetTextureSamplerHandleARB = pAPI->glGetTextureSamplerHandleARB; + glMakeTextureHandleResidentARB = pAPI->glMakeTextureHandleResidentARB; + glMakeTextureHandleNonResidentARB = pAPI->glMakeTextureHandleNonResidentARB; + glGetImageHandleARB = pAPI->glGetImageHandleARB; + glMakeImageHandleResidentARB = pAPI->glMakeImageHandleResidentARB; + glMakeImageHandleNonResidentARB = pAPI->glMakeImageHandleNonResidentARB; + glUniformHandleui64ARB = pAPI->glUniformHandleui64ARB; + glUniformHandleui64vARB = pAPI->glUniformHandleui64vARB; + glProgramUniformHandleui64ARB = pAPI->glProgramUniformHandleui64ARB; + glProgramUniformHandleui64vARB = pAPI->glProgramUniformHandleui64vARB; + glIsTextureHandleResidentARB = pAPI->glIsTextureHandleResidentARB; + glIsImageHandleResidentARB = pAPI->glIsImageHandleResidentARB; + glVertexAttribL1ui64ARB = pAPI->glVertexAttribL1ui64ARB; + glVertexAttribL1ui64vARB = pAPI->glVertexAttribL1ui64vARB; + glGetVertexAttribLui64vARB = pAPI->glGetVertexAttribLui64vARB; + glCreateSyncFromCLeventARB = pAPI->glCreateSyncFromCLeventARB; + glClampColorARB = pAPI->glClampColorARB; + glDispatchComputeGroupSizeARB = pAPI->glDispatchComputeGroupSizeARB; + glDebugMessageControlARB = pAPI->glDebugMessageControlARB; + glDebugMessageInsertARB = pAPI->glDebugMessageInsertARB; + glDebugMessageCallbackARB = pAPI->glDebugMessageCallbackARB; + glGetDebugMessageLogARB = pAPI->glGetDebugMessageLogARB; + glDrawBuffersARB = pAPI->glDrawBuffersARB; + glBlendEquationiARB = pAPI->glBlendEquationiARB; + glBlendEquationSeparateiARB = pAPI->glBlendEquationSeparateiARB; + glBlendFunciARB = pAPI->glBlendFunciARB; + glBlendFuncSeparateiARB = pAPI->glBlendFuncSeparateiARB; + glDrawArraysInstancedARB = pAPI->glDrawArraysInstancedARB; + glDrawElementsInstancedARB = pAPI->glDrawElementsInstancedARB; + glProgramStringARB = pAPI->glProgramStringARB; + glBindProgramARB = pAPI->glBindProgramARB; + glDeleteProgramsARB = pAPI->glDeleteProgramsARB; + glGenProgramsARB = pAPI->glGenProgramsARB; + glProgramEnvParameter4dARB = pAPI->glProgramEnvParameter4dARB; + glProgramEnvParameter4dvARB = pAPI->glProgramEnvParameter4dvARB; + glProgramEnvParameter4fARB = pAPI->glProgramEnvParameter4fARB; + glProgramEnvParameter4fvARB = pAPI->glProgramEnvParameter4fvARB; + glProgramLocalParameter4dARB = pAPI->glProgramLocalParameter4dARB; + glProgramLocalParameter4dvARB = pAPI->glProgramLocalParameter4dvARB; + glProgramLocalParameter4fARB = pAPI->glProgramLocalParameter4fARB; + glProgramLocalParameter4fvARB = pAPI->glProgramLocalParameter4fvARB; + glGetProgramEnvParameterdvARB = pAPI->glGetProgramEnvParameterdvARB; + glGetProgramEnvParameterfvARB = pAPI->glGetProgramEnvParameterfvARB; + glGetProgramLocalParameterdvARB = pAPI->glGetProgramLocalParameterdvARB; + glGetProgramLocalParameterfvARB = pAPI->glGetProgramLocalParameterfvARB; + glGetProgramivARB = pAPI->glGetProgramivARB; + glGetProgramStringARB = pAPI->glGetProgramStringARB; + glIsProgramARB = pAPI->glIsProgramARB; + glProgramParameteriARB = pAPI->glProgramParameteriARB; + glFramebufferTextureARB = pAPI->glFramebufferTextureARB; + glFramebufferTextureLayerARB = pAPI->glFramebufferTextureLayerARB; + glFramebufferTextureFaceARB = pAPI->glFramebufferTextureFaceARB; + glSpecializeShaderARB = pAPI->glSpecializeShaderARB; + glUniform1i64ARB = pAPI->glUniform1i64ARB; + glUniform2i64ARB = pAPI->glUniform2i64ARB; + glUniform3i64ARB = pAPI->glUniform3i64ARB; + glUniform4i64ARB = pAPI->glUniform4i64ARB; + glUniform1i64vARB = pAPI->glUniform1i64vARB; + glUniform2i64vARB = pAPI->glUniform2i64vARB; + glUniform3i64vARB = pAPI->glUniform3i64vARB; + glUniform4i64vARB = pAPI->glUniform4i64vARB; + glUniform1ui64ARB = pAPI->glUniform1ui64ARB; + glUniform2ui64ARB = pAPI->glUniform2ui64ARB; + glUniform3ui64ARB = pAPI->glUniform3ui64ARB; + glUniform4ui64ARB = pAPI->glUniform4ui64ARB; + glUniform1ui64vARB = pAPI->glUniform1ui64vARB; + glUniform2ui64vARB = pAPI->glUniform2ui64vARB; + glUniform3ui64vARB = pAPI->glUniform3ui64vARB; + glUniform4ui64vARB = pAPI->glUniform4ui64vARB; + glGetUniformi64vARB = pAPI->glGetUniformi64vARB; + glGetUniformui64vARB = pAPI->glGetUniformui64vARB; + glGetnUniformi64vARB = pAPI->glGetnUniformi64vARB; + glGetnUniformui64vARB = pAPI->glGetnUniformui64vARB; + glProgramUniform1i64ARB = pAPI->glProgramUniform1i64ARB; + glProgramUniform2i64ARB = pAPI->glProgramUniform2i64ARB; + glProgramUniform3i64ARB = pAPI->glProgramUniform3i64ARB; + glProgramUniform4i64ARB = pAPI->glProgramUniform4i64ARB; + glProgramUniform1i64vARB = pAPI->glProgramUniform1i64vARB; + glProgramUniform2i64vARB = pAPI->glProgramUniform2i64vARB; + glProgramUniform3i64vARB = pAPI->glProgramUniform3i64vARB; + glProgramUniform4i64vARB = pAPI->glProgramUniform4i64vARB; + glProgramUniform1ui64ARB = pAPI->glProgramUniform1ui64ARB; + glProgramUniform2ui64ARB = pAPI->glProgramUniform2ui64ARB; + glProgramUniform3ui64ARB = pAPI->glProgramUniform3ui64ARB; + glProgramUniform4ui64ARB = pAPI->glProgramUniform4ui64ARB; + glProgramUniform1ui64vARB = pAPI->glProgramUniform1ui64vARB; + glProgramUniform2ui64vARB = pAPI->glProgramUniform2ui64vARB; + glProgramUniform3ui64vARB = pAPI->glProgramUniform3ui64vARB; + glProgramUniform4ui64vARB = pAPI->glProgramUniform4ui64vARB; + glColorTable = pAPI->glColorTable; + glColorTableParameterfv = pAPI->glColorTableParameterfv; + glColorTableParameteriv = pAPI->glColorTableParameteriv; + glCopyColorTable = pAPI->glCopyColorTable; + glGetColorTable = pAPI->glGetColorTable; + glGetColorTableParameterfv = pAPI->glGetColorTableParameterfv; + glGetColorTableParameteriv = pAPI->glGetColorTableParameteriv; + glColorSubTable = pAPI->glColorSubTable; + glCopyColorSubTable = pAPI->glCopyColorSubTable; + glConvolutionFilter1D = pAPI->glConvolutionFilter1D; + glConvolutionFilter2D = pAPI->glConvolutionFilter2D; + glConvolutionParameterf = pAPI->glConvolutionParameterf; + glConvolutionParameterfv = pAPI->glConvolutionParameterfv; + glConvolutionParameteri = pAPI->glConvolutionParameteri; + glConvolutionParameteriv = pAPI->glConvolutionParameteriv; + glCopyConvolutionFilter1D = pAPI->glCopyConvolutionFilter1D; + glCopyConvolutionFilter2D = pAPI->glCopyConvolutionFilter2D; + glGetConvolutionFilter = pAPI->glGetConvolutionFilter; + glGetConvolutionParameterfv = pAPI->glGetConvolutionParameterfv; + glGetConvolutionParameteriv = pAPI->glGetConvolutionParameteriv; + glGetSeparableFilter = pAPI->glGetSeparableFilter; + glSeparableFilter2D = pAPI->glSeparableFilter2D; + glGetHistogram = pAPI->glGetHistogram; + glGetHistogramParameterfv = pAPI->glGetHistogramParameterfv; + glGetHistogramParameteriv = pAPI->glGetHistogramParameteriv; + glGetMinmax = pAPI->glGetMinmax; + glGetMinmaxParameterfv = pAPI->glGetMinmaxParameterfv; + glGetMinmaxParameteriv = pAPI->glGetMinmaxParameteriv; + glHistogram = pAPI->glHistogram; + glMinmax = pAPI->glMinmax; + glResetHistogram = pAPI->glResetHistogram; + glResetMinmax = pAPI->glResetMinmax; + glMultiDrawArraysIndirectCountARB = pAPI->glMultiDrawArraysIndirectCountARB; + glMultiDrawElementsIndirectCountARB = pAPI->glMultiDrawElementsIndirectCountARB; + glVertexAttribDivisorARB = pAPI->glVertexAttribDivisorARB; + glCurrentPaletteMatrixARB = pAPI->glCurrentPaletteMatrixARB; + glMatrixIndexubvARB = pAPI->glMatrixIndexubvARB; + glMatrixIndexusvARB = pAPI->glMatrixIndexusvARB; + glMatrixIndexuivARB = pAPI->glMatrixIndexuivARB; + glMatrixIndexPointerARB = pAPI->glMatrixIndexPointerARB; + glSampleCoverageARB = pAPI->glSampleCoverageARB; + glActiveTextureARB = pAPI->glActiveTextureARB; + glClientActiveTextureARB = pAPI->glClientActiveTextureARB; + glMultiTexCoord1dARB = pAPI->glMultiTexCoord1dARB; + glMultiTexCoord1dvARB = pAPI->glMultiTexCoord1dvARB; + glMultiTexCoord1fARB = pAPI->glMultiTexCoord1fARB; + glMultiTexCoord1fvARB = pAPI->glMultiTexCoord1fvARB; + glMultiTexCoord1iARB = pAPI->glMultiTexCoord1iARB; + glMultiTexCoord1ivARB = pAPI->glMultiTexCoord1ivARB; + glMultiTexCoord1sARB = pAPI->glMultiTexCoord1sARB; + glMultiTexCoord1svARB = pAPI->glMultiTexCoord1svARB; + glMultiTexCoord2dARB = pAPI->glMultiTexCoord2dARB; + glMultiTexCoord2dvARB = pAPI->glMultiTexCoord2dvARB; + glMultiTexCoord2fARB = pAPI->glMultiTexCoord2fARB; + glMultiTexCoord2fvARB = pAPI->glMultiTexCoord2fvARB; + glMultiTexCoord2iARB = pAPI->glMultiTexCoord2iARB; + glMultiTexCoord2ivARB = pAPI->glMultiTexCoord2ivARB; + glMultiTexCoord2sARB = pAPI->glMultiTexCoord2sARB; + glMultiTexCoord2svARB = pAPI->glMultiTexCoord2svARB; + glMultiTexCoord3dARB = pAPI->glMultiTexCoord3dARB; + glMultiTexCoord3dvARB = pAPI->glMultiTexCoord3dvARB; + glMultiTexCoord3fARB = pAPI->glMultiTexCoord3fARB; + glMultiTexCoord3fvARB = pAPI->glMultiTexCoord3fvARB; + glMultiTexCoord3iARB = pAPI->glMultiTexCoord3iARB; + glMultiTexCoord3ivARB = pAPI->glMultiTexCoord3ivARB; + glMultiTexCoord3sARB = pAPI->glMultiTexCoord3sARB; + glMultiTexCoord3svARB = pAPI->glMultiTexCoord3svARB; + glMultiTexCoord4dARB = pAPI->glMultiTexCoord4dARB; + glMultiTexCoord4dvARB = pAPI->glMultiTexCoord4dvARB; + glMultiTexCoord4fARB = pAPI->glMultiTexCoord4fARB; + glMultiTexCoord4fvARB = pAPI->glMultiTexCoord4fvARB; + glMultiTexCoord4iARB = pAPI->glMultiTexCoord4iARB; + glMultiTexCoord4ivARB = pAPI->glMultiTexCoord4ivARB; + glMultiTexCoord4sARB = pAPI->glMultiTexCoord4sARB; + glMultiTexCoord4svARB = pAPI->glMultiTexCoord4svARB; + glGenQueriesARB = pAPI->glGenQueriesARB; + glDeleteQueriesARB = pAPI->glDeleteQueriesARB; + glIsQueryARB = pAPI->glIsQueryARB; + glBeginQueryARB = pAPI->glBeginQueryARB; + glEndQueryARB = pAPI->glEndQueryARB; + glGetQueryivARB = pAPI->glGetQueryivARB; + glGetQueryObjectivARB = pAPI->glGetQueryObjectivARB; + glGetQueryObjectuivARB = pAPI->glGetQueryObjectuivARB; + glMaxShaderCompilerThreadsARB = pAPI->glMaxShaderCompilerThreadsARB; + glPointParameterfARB = pAPI->glPointParameterfARB; + glPointParameterfvARB = pAPI->glPointParameterfvARB; + glGetGraphicsResetStatusARB = pAPI->glGetGraphicsResetStatusARB; + glGetnTexImageARB = pAPI->glGetnTexImageARB; + glReadnPixelsARB = pAPI->glReadnPixelsARB; + glGetnCompressedTexImageARB = pAPI->glGetnCompressedTexImageARB; + glGetnUniformfvARB = pAPI->glGetnUniformfvARB; + glGetnUniformivARB = pAPI->glGetnUniformivARB; + glGetnUniformuivARB = pAPI->glGetnUniformuivARB; + glGetnUniformdvARB = pAPI->glGetnUniformdvARB; + glGetnMapdvARB = pAPI->glGetnMapdvARB; + glGetnMapfvARB = pAPI->glGetnMapfvARB; + glGetnMapivARB = pAPI->glGetnMapivARB; + glGetnPixelMapfvARB = pAPI->glGetnPixelMapfvARB; + glGetnPixelMapuivARB = pAPI->glGetnPixelMapuivARB; + glGetnPixelMapusvARB = pAPI->glGetnPixelMapusvARB; + glGetnPolygonStippleARB = pAPI->glGetnPolygonStippleARB; + glGetnColorTableARB = pAPI->glGetnColorTableARB; + glGetnConvolutionFilterARB = pAPI->glGetnConvolutionFilterARB; + glGetnSeparableFilterARB = pAPI->glGetnSeparableFilterARB; + glGetnHistogramARB = pAPI->glGetnHistogramARB; + glGetnMinmaxARB = pAPI->glGetnMinmaxARB; + glFramebufferSampleLocationsfvARB = pAPI->glFramebufferSampleLocationsfvARB; + glNamedFramebufferSampleLocationsfvARB = pAPI->glNamedFramebufferSampleLocationsfvARB; + glEvaluateDepthValuesARB = pAPI->glEvaluateDepthValuesARB; + glMinSampleShadingARB = pAPI->glMinSampleShadingARB; + glDeleteObjectARB = pAPI->glDeleteObjectARB; + glGetHandleARB = pAPI->glGetHandleARB; + glDetachObjectARB = pAPI->glDetachObjectARB; + glCreateShaderObjectARB = pAPI->glCreateShaderObjectARB; + glShaderSourceARB = pAPI->glShaderSourceARB; + glCompileShaderARB = pAPI->glCompileShaderARB; + glCreateProgramObjectARB = pAPI->glCreateProgramObjectARB; + glAttachObjectARB = pAPI->glAttachObjectARB; + glLinkProgramARB = pAPI->glLinkProgramARB; + glUseProgramObjectARB = pAPI->glUseProgramObjectARB; + glValidateProgramARB = pAPI->glValidateProgramARB; + glUniform1fARB = pAPI->glUniform1fARB; + glUniform2fARB = pAPI->glUniform2fARB; + glUniform3fARB = pAPI->glUniform3fARB; + glUniform4fARB = pAPI->glUniform4fARB; + glUniform1iARB = pAPI->glUniform1iARB; + glUniform2iARB = pAPI->glUniform2iARB; + glUniform3iARB = pAPI->glUniform3iARB; + glUniform4iARB = pAPI->glUniform4iARB; + glUniform1fvARB = pAPI->glUniform1fvARB; + glUniform2fvARB = pAPI->glUniform2fvARB; + glUniform3fvARB = pAPI->glUniform3fvARB; + glUniform4fvARB = pAPI->glUniform4fvARB; + glUniform1ivARB = pAPI->glUniform1ivARB; + glUniform2ivARB = pAPI->glUniform2ivARB; + glUniform3ivARB = pAPI->glUniform3ivARB; + glUniform4ivARB = pAPI->glUniform4ivARB; + glUniformMatrix2fvARB = pAPI->glUniformMatrix2fvARB; + glUniformMatrix3fvARB = pAPI->glUniformMatrix3fvARB; + glUniformMatrix4fvARB = pAPI->glUniformMatrix4fvARB; + glGetObjectParameterfvARB = pAPI->glGetObjectParameterfvARB; + glGetObjectParameterivARB = pAPI->glGetObjectParameterivARB; + glGetInfoLogARB = pAPI->glGetInfoLogARB; + glGetAttachedObjectsARB = pAPI->glGetAttachedObjectsARB; + glGetUniformLocationARB = pAPI->glGetUniformLocationARB; + glGetActiveUniformARB = pAPI->glGetActiveUniformARB; + glGetUniformfvARB = pAPI->glGetUniformfvARB; + glGetUniformivARB = pAPI->glGetUniformivARB; + glGetShaderSourceARB = pAPI->glGetShaderSourceARB; + glNamedStringARB = pAPI->glNamedStringARB; + glDeleteNamedStringARB = pAPI->glDeleteNamedStringARB; + glCompileShaderIncludeARB = pAPI->glCompileShaderIncludeARB; + glIsNamedStringARB = pAPI->glIsNamedStringARB; + glGetNamedStringARB = pAPI->glGetNamedStringARB; + glGetNamedStringivARB = pAPI->glGetNamedStringivARB; + glBufferPageCommitmentARB = pAPI->glBufferPageCommitmentARB; + glNamedBufferPageCommitmentEXT = pAPI->glNamedBufferPageCommitmentEXT; + glNamedBufferPageCommitmentARB = pAPI->glNamedBufferPageCommitmentARB; + glTexPageCommitmentARB = pAPI->glTexPageCommitmentARB; + glTexBufferARB = pAPI->glTexBufferARB; + glCompressedTexImage3DARB = pAPI->glCompressedTexImage3DARB; + glCompressedTexImage2DARB = pAPI->glCompressedTexImage2DARB; + glCompressedTexImage1DARB = pAPI->glCompressedTexImage1DARB; + glCompressedTexSubImage3DARB = pAPI->glCompressedTexSubImage3DARB; + glCompressedTexSubImage2DARB = pAPI->glCompressedTexSubImage2DARB; + glCompressedTexSubImage1DARB = pAPI->glCompressedTexSubImage1DARB; + glGetCompressedTexImageARB = pAPI->glGetCompressedTexImageARB; + glLoadTransposeMatrixfARB = pAPI->glLoadTransposeMatrixfARB; + glLoadTransposeMatrixdARB = pAPI->glLoadTransposeMatrixdARB; + glMultTransposeMatrixfARB = pAPI->glMultTransposeMatrixfARB; + glMultTransposeMatrixdARB = pAPI->glMultTransposeMatrixdARB; + glWeightbvARB = pAPI->glWeightbvARB; + glWeightsvARB = pAPI->glWeightsvARB; + glWeightivARB = pAPI->glWeightivARB; + glWeightfvARB = pAPI->glWeightfvARB; + glWeightdvARB = pAPI->glWeightdvARB; + glWeightubvARB = pAPI->glWeightubvARB; + glWeightusvARB = pAPI->glWeightusvARB; + glWeightuivARB = pAPI->glWeightuivARB; + glWeightPointerARB = pAPI->glWeightPointerARB; + glVertexBlendARB = pAPI->glVertexBlendARB; + glBindBufferARB = pAPI->glBindBufferARB; + glDeleteBuffersARB = pAPI->glDeleteBuffersARB; + glGenBuffersARB = pAPI->glGenBuffersARB; + glIsBufferARB = pAPI->glIsBufferARB; + glBufferDataARB = pAPI->glBufferDataARB; + glBufferSubDataARB = pAPI->glBufferSubDataARB; + glGetBufferSubDataARB = pAPI->glGetBufferSubDataARB; + glMapBufferARB = pAPI->glMapBufferARB; + glUnmapBufferARB = pAPI->glUnmapBufferARB; + glGetBufferParameterivARB = pAPI->glGetBufferParameterivARB; + glGetBufferPointervARB = pAPI->glGetBufferPointervARB; + glVertexAttrib1dARB = pAPI->glVertexAttrib1dARB; + glVertexAttrib1dvARB = pAPI->glVertexAttrib1dvARB; + glVertexAttrib1fARB = pAPI->glVertexAttrib1fARB; + glVertexAttrib1fvARB = pAPI->glVertexAttrib1fvARB; + glVertexAttrib1sARB = pAPI->glVertexAttrib1sARB; + glVertexAttrib1svARB = pAPI->glVertexAttrib1svARB; + glVertexAttrib2dARB = pAPI->glVertexAttrib2dARB; + glVertexAttrib2dvARB = pAPI->glVertexAttrib2dvARB; + glVertexAttrib2fARB = pAPI->glVertexAttrib2fARB; + glVertexAttrib2fvARB = pAPI->glVertexAttrib2fvARB; + glVertexAttrib2sARB = pAPI->glVertexAttrib2sARB; + glVertexAttrib2svARB = pAPI->glVertexAttrib2svARB; + glVertexAttrib3dARB = pAPI->glVertexAttrib3dARB; + glVertexAttrib3dvARB = pAPI->glVertexAttrib3dvARB; + glVertexAttrib3fARB = pAPI->glVertexAttrib3fARB; + glVertexAttrib3fvARB = pAPI->glVertexAttrib3fvARB; + glVertexAttrib3sARB = pAPI->glVertexAttrib3sARB; + glVertexAttrib3svARB = pAPI->glVertexAttrib3svARB; + glVertexAttrib4NbvARB = pAPI->glVertexAttrib4NbvARB; + glVertexAttrib4NivARB = pAPI->glVertexAttrib4NivARB; + glVertexAttrib4NsvARB = pAPI->glVertexAttrib4NsvARB; + glVertexAttrib4NubARB = pAPI->glVertexAttrib4NubARB; + glVertexAttrib4NubvARB = pAPI->glVertexAttrib4NubvARB; + glVertexAttrib4NuivARB = pAPI->glVertexAttrib4NuivARB; + glVertexAttrib4NusvARB = pAPI->glVertexAttrib4NusvARB; + glVertexAttrib4bvARB = pAPI->glVertexAttrib4bvARB; + glVertexAttrib4dARB = pAPI->glVertexAttrib4dARB; + glVertexAttrib4dvARB = pAPI->glVertexAttrib4dvARB; + glVertexAttrib4fARB = pAPI->glVertexAttrib4fARB; + glVertexAttrib4fvARB = pAPI->glVertexAttrib4fvARB; + glVertexAttrib4ivARB = pAPI->glVertexAttrib4ivARB; + glVertexAttrib4sARB = pAPI->glVertexAttrib4sARB; + glVertexAttrib4svARB = pAPI->glVertexAttrib4svARB; + glVertexAttrib4ubvARB = pAPI->glVertexAttrib4ubvARB; + glVertexAttrib4uivARB = pAPI->glVertexAttrib4uivARB; + glVertexAttrib4usvARB = pAPI->glVertexAttrib4usvARB; + glVertexAttribPointerARB = pAPI->glVertexAttribPointerARB; + glEnableVertexAttribArrayARB = pAPI->glEnableVertexAttribArrayARB; + glDisableVertexAttribArrayARB = pAPI->glDisableVertexAttribArrayARB; + glGetVertexAttribdvARB = pAPI->glGetVertexAttribdvARB; + glGetVertexAttribfvARB = pAPI->glGetVertexAttribfvARB; + glGetVertexAttribivARB = pAPI->glGetVertexAttribivARB; + glGetVertexAttribPointervARB = pAPI->glGetVertexAttribPointervARB; + glBindAttribLocationARB = pAPI->glBindAttribLocationARB; + glGetActiveAttribARB = pAPI->glGetActiveAttribARB; + glGetAttribLocationARB = pAPI->glGetAttribLocationARB; + glDepthRangeArraydvNV = pAPI->glDepthRangeArraydvNV; + glDepthRangeIndexeddNV = pAPI->glDepthRangeIndexeddNV; + glWindowPos2dARB = pAPI->glWindowPos2dARB; + glWindowPos2dvARB = pAPI->glWindowPos2dvARB; + glWindowPos2fARB = pAPI->glWindowPos2fARB; + glWindowPos2fvARB = pAPI->glWindowPos2fvARB; + glWindowPos2iARB = pAPI->glWindowPos2iARB; + glWindowPos2ivARB = pAPI->glWindowPos2ivARB; + glWindowPos2sARB = pAPI->glWindowPos2sARB; + glWindowPos2svARB = pAPI->glWindowPos2svARB; + glWindowPos3dARB = pAPI->glWindowPos3dARB; + glWindowPos3dvARB = pAPI->glWindowPos3dvARB; + glWindowPos3fARB = pAPI->glWindowPos3fARB; + glWindowPos3fvARB = pAPI->glWindowPos3fvARB; + glWindowPos3iARB = pAPI->glWindowPos3iARB; + glWindowPos3ivARB = pAPI->glWindowPos3ivARB; + glWindowPos3sARB = pAPI->glWindowPos3sARB; + glWindowPos3svARB = pAPI->glWindowPos3svARB; + glDrawBuffersATI = pAPI->glDrawBuffersATI; + glElementPointerATI = pAPI->glElementPointerATI; + glDrawElementArrayATI = pAPI->glDrawElementArrayATI; + glDrawRangeElementArrayATI = pAPI->glDrawRangeElementArrayATI; + glTexBumpParameterivATI = pAPI->glTexBumpParameterivATI; + glTexBumpParameterfvATI = pAPI->glTexBumpParameterfvATI; + glGetTexBumpParameterivATI = pAPI->glGetTexBumpParameterivATI; + glGetTexBumpParameterfvATI = pAPI->glGetTexBumpParameterfvATI; + glGenFragmentShadersATI = pAPI->glGenFragmentShadersATI; + glBindFragmentShaderATI = pAPI->glBindFragmentShaderATI; + glDeleteFragmentShaderATI = pAPI->glDeleteFragmentShaderATI; + glBeginFragmentShaderATI = pAPI->glBeginFragmentShaderATI; + glEndFragmentShaderATI = pAPI->glEndFragmentShaderATI; + glPassTexCoordATI = pAPI->glPassTexCoordATI; + glSampleMapATI = pAPI->glSampleMapATI; + glColorFragmentOp1ATI = pAPI->glColorFragmentOp1ATI; + glColorFragmentOp2ATI = pAPI->glColorFragmentOp2ATI; + glColorFragmentOp3ATI = pAPI->glColorFragmentOp3ATI; + glAlphaFragmentOp1ATI = pAPI->glAlphaFragmentOp1ATI; + glAlphaFragmentOp2ATI = pAPI->glAlphaFragmentOp2ATI; + glAlphaFragmentOp3ATI = pAPI->glAlphaFragmentOp3ATI; + glSetFragmentShaderConstantATI = pAPI->glSetFragmentShaderConstantATI; + glMapObjectBufferATI = pAPI->glMapObjectBufferATI; + glUnmapObjectBufferATI = pAPI->glUnmapObjectBufferATI; + glPNTrianglesiATI = pAPI->glPNTrianglesiATI; + glPNTrianglesfATI = pAPI->glPNTrianglesfATI; + glStencilOpSeparateATI = pAPI->glStencilOpSeparateATI; + glStencilFuncSeparateATI = pAPI->glStencilFuncSeparateATI; + glNewObjectBufferATI = pAPI->glNewObjectBufferATI; + glIsObjectBufferATI = pAPI->glIsObjectBufferATI; + glUpdateObjectBufferATI = pAPI->glUpdateObjectBufferATI; + glGetObjectBufferfvATI = pAPI->glGetObjectBufferfvATI; + glGetObjectBufferivATI = pAPI->glGetObjectBufferivATI; + glFreeObjectBufferATI = pAPI->glFreeObjectBufferATI; + glArrayObjectATI = pAPI->glArrayObjectATI; + glGetArrayObjectfvATI = pAPI->glGetArrayObjectfvATI; + glGetArrayObjectivATI = pAPI->glGetArrayObjectivATI; + glVariantArrayObjectATI = pAPI->glVariantArrayObjectATI; + glGetVariantArrayObjectfvATI = pAPI->glGetVariantArrayObjectfvATI; + glGetVariantArrayObjectivATI = pAPI->glGetVariantArrayObjectivATI; + glVertexAttribArrayObjectATI = pAPI->glVertexAttribArrayObjectATI; + glGetVertexAttribArrayObjectfvATI = pAPI->glGetVertexAttribArrayObjectfvATI; + glGetVertexAttribArrayObjectivATI = pAPI->glGetVertexAttribArrayObjectivATI; + glVertexStream1sATI = pAPI->glVertexStream1sATI; + glVertexStream1svATI = pAPI->glVertexStream1svATI; + glVertexStream1iATI = pAPI->glVertexStream1iATI; + glVertexStream1ivATI = pAPI->glVertexStream1ivATI; + glVertexStream1fATI = pAPI->glVertexStream1fATI; + glVertexStream1fvATI = pAPI->glVertexStream1fvATI; + glVertexStream1dATI = pAPI->glVertexStream1dATI; + glVertexStream1dvATI = pAPI->glVertexStream1dvATI; + glVertexStream2sATI = pAPI->glVertexStream2sATI; + glVertexStream2svATI = pAPI->glVertexStream2svATI; + glVertexStream2iATI = pAPI->glVertexStream2iATI; + glVertexStream2ivATI = pAPI->glVertexStream2ivATI; + glVertexStream2fATI = pAPI->glVertexStream2fATI; + glVertexStream2fvATI = pAPI->glVertexStream2fvATI; + glVertexStream2dATI = pAPI->glVertexStream2dATI; + glVertexStream2dvATI = pAPI->glVertexStream2dvATI; + glVertexStream3sATI = pAPI->glVertexStream3sATI; + glVertexStream3svATI = pAPI->glVertexStream3svATI; + glVertexStream3iATI = pAPI->glVertexStream3iATI; + glVertexStream3ivATI = pAPI->glVertexStream3ivATI; + glVertexStream3fATI = pAPI->glVertexStream3fATI; + glVertexStream3fvATI = pAPI->glVertexStream3fvATI; + glVertexStream3dATI = pAPI->glVertexStream3dATI; + glVertexStream3dvATI = pAPI->glVertexStream3dvATI; + glVertexStream4sATI = pAPI->glVertexStream4sATI; + glVertexStream4svATI = pAPI->glVertexStream4svATI; + glVertexStream4iATI = pAPI->glVertexStream4iATI; + glVertexStream4ivATI = pAPI->glVertexStream4ivATI; + glVertexStream4fATI = pAPI->glVertexStream4fATI; + glVertexStream4fvATI = pAPI->glVertexStream4fvATI; + glVertexStream4dATI = pAPI->glVertexStream4dATI; + glVertexStream4dvATI = pAPI->glVertexStream4dvATI; + glNormalStream3bATI = pAPI->glNormalStream3bATI; + glNormalStream3bvATI = pAPI->glNormalStream3bvATI; + glNormalStream3sATI = pAPI->glNormalStream3sATI; + glNormalStream3svATI = pAPI->glNormalStream3svATI; + glNormalStream3iATI = pAPI->glNormalStream3iATI; + glNormalStream3ivATI = pAPI->glNormalStream3ivATI; + glNormalStream3fATI = pAPI->glNormalStream3fATI; + glNormalStream3fvATI = pAPI->glNormalStream3fvATI; + glNormalStream3dATI = pAPI->glNormalStream3dATI; + glNormalStream3dvATI = pAPI->glNormalStream3dvATI; + glClientActiveVertexStreamATI = pAPI->glClientActiveVertexStreamATI; + glVertexBlendEnviATI = pAPI->glVertexBlendEnviATI; + glVertexBlendEnvfATI = pAPI->glVertexBlendEnvfATI; + glEGLImageTargetTexStorageEXT = pAPI->glEGLImageTargetTexStorageEXT; + glEGLImageTargetTextureStorageEXT = pAPI->glEGLImageTargetTextureStorageEXT; + glUniformBufferEXT = pAPI->glUniformBufferEXT; + glGetUniformBufferSizeEXT = pAPI->glGetUniformBufferSizeEXT; + glGetUniformOffsetEXT = pAPI->glGetUniformOffsetEXT; + glBlendColorEXT = pAPI->glBlendColorEXT; + glBlendEquationSeparateEXT = pAPI->glBlendEquationSeparateEXT; + glBlendFuncSeparateEXT = pAPI->glBlendFuncSeparateEXT; + glBlendEquationEXT = pAPI->glBlendEquationEXT; + glColorSubTableEXT = pAPI->glColorSubTableEXT; + glCopyColorSubTableEXT = pAPI->glCopyColorSubTableEXT; + glLockArraysEXT = pAPI->glLockArraysEXT; + glUnlockArraysEXT = pAPI->glUnlockArraysEXT; + glConvolutionFilter1DEXT = pAPI->glConvolutionFilter1DEXT; + glConvolutionFilter2DEXT = pAPI->glConvolutionFilter2DEXT; + glConvolutionParameterfEXT = pAPI->glConvolutionParameterfEXT; + glConvolutionParameterfvEXT = pAPI->glConvolutionParameterfvEXT; + glConvolutionParameteriEXT = pAPI->glConvolutionParameteriEXT; + glConvolutionParameterivEXT = pAPI->glConvolutionParameterivEXT; + glCopyConvolutionFilter1DEXT = pAPI->glCopyConvolutionFilter1DEXT; + glCopyConvolutionFilter2DEXT = pAPI->glCopyConvolutionFilter2DEXT; + glGetConvolutionFilterEXT = pAPI->glGetConvolutionFilterEXT; + glGetConvolutionParameterfvEXT = pAPI->glGetConvolutionParameterfvEXT; + glGetConvolutionParameterivEXT = pAPI->glGetConvolutionParameterivEXT; + glGetSeparableFilterEXT = pAPI->glGetSeparableFilterEXT; + glSeparableFilter2DEXT = pAPI->glSeparableFilter2DEXT; + glTangent3bEXT = pAPI->glTangent3bEXT; + glTangent3bvEXT = pAPI->glTangent3bvEXT; + glTangent3dEXT = pAPI->glTangent3dEXT; + glTangent3dvEXT = pAPI->glTangent3dvEXT; + glTangent3fEXT = pAPI->glTangent3fEXT; + glTangent3fvEXT = pAPI->glTangent3fvEXT; + glTangent3iEXT = pAPI->glTangent3iEXT; + glTangent3ivEXT = pAPI->glTangent3ivEXT; + glTangent3sEXT = pAPI->glTangent3sEXT; + glTangent3svEXT = pAPI->glTangent3svEXT; + glBinormal3bEXT = pAPI->glBinormal3bEXT; + glBinormal3bvEXT = pAPI->glBinormal3bvEXT; + glBinormal3dEXT = pAPI->glBinormal3dEXT; + glBinormal3dvEXT = pAPI->glBinormal3dvEXT; + glBinormal3fEXT = pAPI->glBinormal3fEXT; + glBinormal3fvEXT = pAPI->glBinormal3fvEXT; + glBinormal3iEXT = pAPI->glBinormal3iEXT; + glBinormal3ivEXT = pAPI->glBinormal3ivEXT; + glBinormal3sEXT = pAPI->glBinormal3sEXT; + glBinormal3svEXT = pAPI->glBinormal3svEXT; + glTangentPointerEXT = pAPI->glTangentPointerEXT; + glBinormalPointerEXT = pAPI->glBinormalPointerEXT; + glCopyTexImage1DEXT = pAPI->glCopyTexImage1DEXT; + glCopyTexImage2DEXT = pAPI->glCopyTexImage2DEXT; + glCopyTexSubImage1DEXT = pAPI->glCopyTexSubImage1DEXT; + glCopyTexSubImage2DEXT = pAPI->glCopyTexSubImage2DEXT; + glCopyTexSubImage3DEXT = pAPI->glCopyTexSubImage3DEXT; + glCullParameterdvEXT = pAPI->glCullParameterdvEXT; + glCullParameterfvEXT = pAPI->glCullParameterfvEXT; + glLabelObjectEXT = pAPI->glLabelObjectEXT; + glGetObjectLabelEXT = pAPI->glGetObjectLabelEXT; + glInsertEventMarkerEXT = pAPI->glInsertEventMarkerEXT; + glPushGroupMarkerEXT = pAPI->glPushGroupMarkerEXT; + glPopGroupMarkerEXT = pAPI->glPopGroupMarkerEXT; + glDepthBoundsEXT = pAPI->glDepthBoundsEXT; + glMatrixLoadfEXT = pAPI->glMatrixLoadfEXT; + glMatrixLoaddEXT = pAPI->glMatrixLoaddEXT; + glMatrixMultfEXT = pAPI->glMatrixMultfEXT; + glMatrixMultdEXT = pAPI->glMatrixMultdEXT; + glMatrixLoadIdentityEXT = pAPI->glMatrixLoadIdentityEXT; + glMatrixRotatefEXT = pAPI->glMatrixRotatefEXT; + glMatrixRotatedEXT = pAPI->glMatrixRotatedEXT; + glMatrixScalefEXT = pAPI->glMatrixScalefEXT; + glMatrixScaledEXT = pAPI->glMatrixScaledEXT; + glMatrixTranslatefEXT = pAPI->glMatrixTranslatefEXT; + glMatrixTranslatedEXT = pAPI->glMatrixTranslatedEXT; + glMatrixFrustumEXT = pAPI->glMatrixFrustumEXT; + glMatrixOrthoEXT = pAPI->glMatrixOrthoEXT; + glMatrixPopEXT = pAPI->glMatrixPopEXT; + glMatrixPushEXT = pAPI->glMatrixPushEXT; + glClientAttribDefaultEXT = pAPI->glClientAttribDefaultEXT; + glPushClientAttribDefaultEXT = pAPI->glPushClientAttribDefaultEXT; + glTextureParameterfEXT = pAPI->glTextureParameterfEXT; + glTextureParameterfvEXT = pAPI->glTextureParameterfvEXT; + glTextureParameteriEXT = pAPI->glTextureParameteriEXT; + glTextureParameterivEXT = pAPI->glTextureParameterivEXT; + glTextureImage1DEXT = pAPI->glTextureImage1DEXT; + glTextureImage2DEXT = pAPI->glTextureImage2DEXT; + glTextureSubImage1DEXT = pAPI->glTextureSubImage1DEXT; + glTextureSubImage2DEXT = pAPI->glTextureSubImage2DEXT; + glCopyTextureImage1DEXT = pAPI->glCopyTextureImage1DEXT; + glCopyTextureImage2DEXT = pAPI->glCopyTextureImage2DEXT; + glCopyTextureSubImage1DEXT = pAPI->glCopyTextureSubImage1DEXT; + glCopyTextureSubImage2DEXT = pAPI->glCopyTextureSubImage2DEXT; + glGetTextureImageEXT = pAPI->glGetTextureImageEXT; + glGetTextureParameterfvEXT = pAPI->glGetTextureParameterfvEXT; + glGetTextureParameterivEXT = pAPI->glGetTextureParameterivEXT; + glGetTextureLevelParameterfvEXT = pAPI->glGetTextureLevelParameterfvEXT; + glGetTextureLevelParameterivEXT = pAPI->glGetTextureLevelParameterivEXT; + glTextureImage3DEXT = pAPI->glTextureImage3DEXT; + glTextureSubImage3DEXT = pAPI->glTextureSubImage3DEXT; + glCopyTextureSubImage3DEXT = pAPI->glCopyTextureSubImage3DEXT; + glBindMultiTextureEXT = pAPI->glBindMultiTextureEXT; + glMultiTexCoordPointerEXT = pAPI->glMultiTexCoordPointerEXT; + glMultiTexEnvfEXT = pAPI->glMultiTexEnvfEXT; + glMultiTexEnvfvEXT = pAPI->glMultiTexEnvfvEXT; + glMultiTexEnviEXT = pAPI->glMultiTexEnviEXT; + glMultiTexEnvivEXT = pAPI->glMultiTexEnvivEXT; + glMultiTexGendEXT = pAPI->glMultiTexGendEXT; + glMultiTexGendvEXT = pAPI->glMultiTexGendvEXT; + glMultiTexGenfEXT = pAPI->glMultiTexGenfEXT; + glMultiTexGenfvEXT = pAPI->glMultiTexGenfvEXT; + glMultiTexGeniEXT = pAPI->glMultiTexGeniEXT; + glMultiTexGenivEXT = pAPI->glMultiTexGenivEXT; + glGetMultiTexEnvfvEXT = pAPI->glGetMultiTexEnvfvEXT; + glGetMultiTexEnvivEXT = pAPI->glGetMultiTexEnvivEXT; + glGetMultiTexGendvEXT = pAPI->glGetMultiTexGendvEXT; + glGetMultiTexGenfvEXT = pAPI->glGetMultiTexGenfvEXT; + glGetMultiTexGenivEXT = pAPI->glGetMultiTexGenivEXT; + glMultiTexParameteriEXT = pAPI->glMultiTexParameteriEXT; + glMultiTexParameterivEXT = pAPI->glMultiTexParameterivEXT; + glMultiTexParameterfEXT = pAPI->glMultiTexParameterfEXT; + glMultiTexParameterfvEXT = pAPI->glMultiTexParameterfvEXT; + glMultiTexImage1DEXT = pAPI->glMultiTexImage1DEXT; + glMultiTexImage2DEXT = pAPI->glMultiTexImage2DEXT; + glMultiTexSubImage1DEXT = pAPI->glMultiTexSubImage1DEXT; + glMultiTexSubImage2DEXT = pAPI->glMultiTexSubImage2DEXT; + glCopyMultiTexImage1DEXT = pAPI->glCopyMultiTexImage1DEXT; + glCopyMultiTexImage2DEXT = pAPI->glCopyMultiTexImage2DEXT; + glCopyMultiTexSubImage1DEXT = pAPI->glCopyMultiTexSubImage1DEXT; + glCopyMultiTexSubImage2DEXT = pAPI->glCopyMultiTexSubImage2DEXT; + glGetMultiTexImageEXT = pAPI->glGetMultiTexImageEXT; + glGetMultiTexParameterfvEXT = pAPI->glGetMultiTexParameterfvEXT; + glGetMultiTexParameterivEXT = pAPI->glGetMultiTexParameterivEXT; + glGetMultiTexLevelParameterfvEXT = pAPI->glGetMultiTexLevelParameterfvEXT; + glGetMultiTexLevelParameterivEXT = pAPI->glGetMultiTexLevelParameterivEXT; + glMultiTexImage3DEXT = pAPI->glMultiTexImage3DEXT; + glMultiTexSubImage3DEXT = pAPI->glMultiTexSubImage3DEXT; + glCopyMultiTexSubImage3DEXT = pAPI->glCopyMultiTexSubImage3DEXT; + glEnableClientStateIndexedEXT = pAPI->glEnableClientStateIndexedEXT; + glDisableClientStateIndexedEXT = pAPI->glDisableClientStateIndexedEXT; + glGetFloatIndexedvEXT = pAPI->glGetFloatIndexedvEXT; + glGetDoubleIndexedvEXT = pAPI->glGetDoubleIndexedvEXT; + glGetPointerIndexedvEXT = pAPI->glGetPointerIndexedvEXT; + glEnableIndexedEXT = pAPI->glEnableIndexedEXT; + glDisableIndexedEXT = pAPI->glDisableIndexedEXT; + glIsEnabledIndexedEXT = pAPI->glIsEnabledIndexedEXT; + glGetIntegerIndexedvEXT = pAPI->glGetIntegerIndexedvEXT; + glGetBooleanIndexedvEXT = pAPI->glGetBooleanIndexedvEXT; + glCompressedTextureImage3DEXT = pAPI->glCompressedTextureImage3DEXT; + glCompressedTextureImage2DEXT = pAPI->glCompressedTextureImage2DEXT; + glCompressedTextureImage1DEXT = pAPI->glCompressedTextureImage1DEXT; + glCompressedTextureSubImage3DEXT = pAPI->glCompressedTextureSubImage3DEXT; + glCompressedTextureSubImage2DEXT = pAPI->glCompressedTextureSubImage2DEXT; + glCompressedTextureSubImage1DEXT = pAPI->glCompressedTextureSubImage1DEXT; + glGetCompressedTextureImageEXT = pAPI->glGetCompressedTextureImageEXT; + glCompressedMultiTexImage3DEXT = pAPI->glCompressedMultiTexImage3DEXT; + glCompressedMultiTexImage2DEXT = pAPI->glCompressedMultiTexImage2DEXT; + glCompressedMultiTexImage1DEXT = pAPI->glCompressedMultiTexImage1DEXT; + glCompressedMultiTexSubImage3DEXT = pAPI->glCompressedMultiTexSubImage3DEXT; + glCompressedMultiTexSubImage2DEXT = pAPI->glCompressedMultiTexSubImage2DEXT; + glCompressedMultiTexSubImage1DEXT = pAPI->glCompressedMultiTexSubImage1DEXT; + glGetCompressedMultiTexImageEXT = pAPI->glGetCompressedMultiTexImageEXT; + glMatrixLoadTransposefEXT = pAPI->glMatrixLoadTransposefEXT; + glMatrixLoadTransposedEXT = pAPI->glMatrixLoadTransposedEXT; + glMatrixMultTransposefEXT = pAPI->glMatrixMultTransposefEXT; + glMatrixMultTransposedEXT = pAPI->glMatrixMultTransposedEXT; + glNamedBufferDataEXT = pAPI->glNamedBufferDataEXT; + glNamedBufferSubDataEXT = pAPI->glNamedBufferSubDataEXT; + glMapNamedBufferEXT = pAPI->glMapNamedBufferEXT; + glUnmapNamedBufferEXT = pAPI->glUnmapNamedBufferEXT; + glGetNamedBufferParameterivEXT = pAPI->glGetNamedBufferParameterivEXT; + glGetNamedBufferPointervEXT = pAPI->glGetNamedBufferPointervEXT; + glGetNamedBufferSubDataEXT = pAPI->glGetNamedBufferSubDataEXT; + glProgramUniform1fEXT = pAPI->glProgramUniform1fEXT; + glProgramUniform2fEXT = pAPI->glProgramUniform2fEXT; + glProgramUniform3fEXT = pAPI->glProgramUniform3fEXT; + glProgramUniform4fEXT = pAPI->glProgramUniform4fEXT; + glProgramUniform1iEXT = pAPI->glProgramUniform1iEXT; + glProgramUniform2iEXT = pAPI->glProgramUniform2iEXT; + glProgramUniform3iEXT = pAPI->glProgramUniform3iEXT; + glProgramUniform4iEXT = pAPI->glProgramUniform4iEXT; + glProgramUniform1fvEXT = pAPI->glProgramUniform1fvEXT; + glProgramUniform2fvEXT = pAPI->glProgramUniform2fvEXT; + glProgramUniform3fvEXT = pAPI->glProgramUniform3fvEXT; + glProgramUniform4fvEXT = pAPI->glProgramUniform4fvEXT; + glProgramUniform1ivEXT = pAPI->glProgramUniform1ivEXT; + glProgramUniform2ivEXT = pAPI->glProgramUniform2ivEXT; + glProgramUniform3ivEXT = pAPI->glProgramUniform3ivEXT; + glProgramUniform4ivEXT = pAPI->glProgramUniform4ivEXT; + glProgramUniformMatrix2fvEXT = pAPI->glProgramUniformMatrix2fvEXT; + glProgramUniformMatrix3fvEXT = pAPI->glProgramUniformMatrix3fvEXT; + glProgramUniformMatrix4fvEXT = pAPI->glProgramUniformMatrix4fvEXT; + glProgramUniformMatrix2x3fvEXT = pAPI->glProgramUniformMatrix2x3fvEXT; + glProgramUniformMatrix3x2fvEXT = pAPI->glProgramUniformMatrix3x2fvEXT; + glProgramUniformMatrix2x4fvEXT = pAPI->glProgramUniformMatrix2x4fvEXT; + glProgramUniformMatrix4x2fvEXT = pAPI->glProgramUniformMatrix4x2fvEXT; + glProgramUniformMatrix3x4fvEXT = pAPI->glProgramUniformMatrix3x4fvEXT; + glProgramUniformMatrix4x3fvEXT = pAPI->glProgramUniformMatrix4x3fvEXT; + glTextureBufferEXT = pAPI->glTextureBufferEXT; + glMultiTexBufferEXT = pAPI->glMultiTexBufferEXT; + glTextureParameterIivEXT = pAPI->glTextureParameterIivEXT; + glTextureParameterIuivEXT = pAPI->glTextureParameterIuivEXT; + glGetTextureParameterIivEXT = pAPI->glGetTextureParameterIivEXT; + glGetTextureParameterIuivEXT = pAPI->glGetTextureParameterIuivEXT; + glMultiTexParameterIivEXT = pAPI->glMultiTexParameterIivEXT; + glMultiTexParameterIuivEXT = pAPI->glMultiTexParameterIuivEXT; + glGetMultiTexParameterIivEXT = pAPI->glGetMultiTexParameterIivEXT; + glGetMultiTexParameterIuivEXT = pAPI->glGetMultiTexParameterIuivEXT; + glProgramUniform1uiEXT = pAPI->glProgramUniform1uiEXT; + glProgramUniform2uiEXT = pAPI->glProgramUniform2uiEXT; + glProgramUniform3uiEXT = pAPI->glProgramUniform3uiEXT; + glProgramUniform4uiEXT = pAPI->glProgramUniform4uiEXT; + glProgramUniform1uivEXT = pAPI->glProgramUniform1uivEXT; + glProgramUniform2uivEXT = pAPI->glProgramUniform2uivEXT; + glProgramUniform3uivEXT = pAPI->glProgramUniform3uivEXT; + glProgramUniform4uivEXT = pAPI->glProgramUniform4uivEXT; + glNamedProgramLocalParameters4fvEXT = pAPI->glNamedProgramLocalParameters4fvEXT; + glNamedProgramLocalParameterI4iEXT = pAPI->glNamedProgramLocalParameterI4iEXT; + glNamedProgramLocalParameterI4ivEXT = pAPI->glNamedProgramLocalParameterI4ivEXT; + glNamedProgramLocalParametersI4ivEXT = pAPI->glNamedProgramLocalParametersI4ivEXT; + glNamedProgramLocalParameterI4uiEXT = pAPI->glNamedProgramLocalParameterI4uiEXT; + glNamedProgramLocalParameterI4uivEXT = pAPI->glNamedProgramLocalParameterI4uivEXT; + glNamedProgramLocalParametersI4uivEXT = pAPI->glNamedProgramLocalParametersI4uivEXT; + glGetNamedProgramLocalParameterIivEXT = pAPI->glGetNamedProgramLocalParameterIivEXT; + glGetNamedProgramLocalParameterIuivEXT = pAPI->glGetNamedProgramLocalParameterIuivEXT; + glEnableClientStateiEXT = pAPI->glEnableClientStateiEXT; + glDisableClientStateiEXT = pAPI->glDisableClientStateiEXT; + glGetFloati_vEXT = pAPI->glGetFloati_vEXT; + glGetDoublei_vEXT = pAPI->glGetDoublei_vEXT; + glGetPointeri_vEXT = pAPI->glGetPointeri_vEXT; + glNamedProgramStringEXT = pAPI->glNamedProgramStringEXT; + glNamedProgramLocalParameter4dEXT = pAPI->glNamedProgramLocalParameter4dEXT; + glNamedProgramLocalParameter4dvEXT = pAPI->glNamedProgramLocalParameter4dvEXT; + glNamedProgramLocalParameter4fEXT = pAPI->glNamedProgramLocalParameter4fEXT; + glNamedProgramLocalParameter4fvEXT = pAPI->glNamedProgramLocalParameter4fvEXT; + glGetNamedProgramLocalParameterdvEXT = pAPI->glGetNamedProgramLocalParameterdvEXT; + glGetNamedProgramLocalParameterfvEXT = pAPI->glGetNamedProgramLocalParameterfvEXT; + glGetNamedProgramivEXT = pAPI->glGetNamedProgramivEXT; + glGetNamedProgramStringEXT = pAPI->glGetNamedProgramStringEXT; + glNamedRenderbufferStorageEXT = pAPI->glNamedRenderbufferStorageEXT; + glGetNamedRenderbufferParameterivEXT = pAPI->glGetNamedRenderbufferParameterivEXT; + glNamedRenderbufferStorageMultisampleEXT = pAPI->glNamedRenderbufferStorageMultisampleEXT; + glNamedRenderbufferStorageMultisampleCoverageEXT = pAPI->glNamedRenderbufferStorageMultisampleCoverageEXT; + glCheckNamedFramebufferStatusEXT = pAPI->glCheckNamedFramebufferStatusEXT; + glNamedFramebufferTexture1DEXT = pAPI->glNamedFramebufferTexture1DEXT; + glNamedFramebufferTexture2DEXT = pAPI->glNamedFramebufferTexture2DEXT; + glNamedFramebufferTexture3DEXT = pAPI->glNamedFramebufferTexture3DEXT; + glNamedFramebufferRenderbufferEXT = pAPI->glNamedFramebufferRenderbufferEXT; + glGetNamedFramebufferAttachmentParameterivEXT = pAPI->glGetNamedFramebufferAttachmentParameterivEXT; + glGenerateTextureMipmapEXT = pAPI->glGenerateTextureMipmapEXT; + glGenerateMultiTexMipmapEXT = pAPI->glGenerateMultiTexMipmapEXT; + glFramebufferDrawBufferEXT = pAPI->glFramebufferDrawBufferEXT; + glFramebufferDrawBuffersEXT = pAPI->glFramebufferDrawBuffersEXT; + glFramebufferReadBufferEXT = pAPI->glFramebufferReadBufferEXT; + glGetFramebufferParameterivEXT = pAPI->glGetFramebufferParameterivEXT; + glNamedCopyBufferSubDataEXT = pAPI->glNamedCopyBufferSubDataEXT; + glNamedFramebufferTextureEXT = pAPI->glNamedFramebufferTextureEXT; + glNamedFramebufferTextureLayerEXT = pAPI->glNamedFramebufferTextureLayerEXT; + glNamedFramebufferTextureFaceEXT = pAPI->glNamedFramebufferTextureFaceEXT; + glTextureRenderbufferEXT = pAPI->glTextureRenderbufferEXT; + glMultiTexRenderbufferEXT = pAPI->glMultiTexRenderbufferEXT; + glVertexArrayVertexOffsetEXT = pAPI->glVertexArrayVertexOffsetEXT; + glVertexArrayColorOffsetEXT = pAPI->glVertexArrayColorOffsetEXT; + glVertexArrayEdgeFlagOffsetEXT = pAPI->glVertexArrayEdgeFlagOffsetEXT; + glVertexArrayIndexOffsetEXT = pAPI->glVertexArrayIndexOffsetEXT; + glVertexArrayNormalOffsetEXT = pAPI->glVertexArrayNormalOffsetEXT; + glVertexArrayTexCoordOffsetEXT = pAPI->glVertexArrayTexCoordOffsetEXT; + glVertexArrayMultiTexCoordOffsetEXT = pAPI->glVertexArrayMultiTexCoordOffsetEXT; + glVertexArrayFogCoordOffsetEXT = pAPI->glVertexArrayFogCoordOffsetEXT; + glVertexArraySecondaryColorOffsetEXT = pAPI->glVertexArraySecondaryColorOffsetEXT; + glVertexArrayVertexAttribOffsetEXT = pAPI->glVertexArrayVertexAttribOffsetEXT; + glVertexArrayVertexAttribIOffsetEXT = pAPI->glVertexArrayVertexAttribIOffsetEXT; + glEnableVertexArrayEXT = pAPI->glEnableVertexArrayEXT; + glDisableVertexArrayEXT = pAPI->glDisableVertexArrayEXT; + glEnableVertexArrayAttribEXT = pAPI->glEnableVertexArrayAttribEXT; + glDisableVertexArrayAttribEXT = pAPI->glDisableVertexArrayAttribEXT; + glGetVertexArrayIntegervEXT = pAPI->glGetVertexArrayIntegervEXT; + glGetVertexArrayPointervEXT = pAPI->glGetVertexArrayPointervEXT; + glGetVertexArrayIntegeri_vEXT = pAPI->glGetVertexArrayIntegeri_vEXT; + glGetVertexArrayPointeri_vEXT = pAPI->glGetVertexArrayPointeri_vEXT; + glMapNamedBufferRangeEXT = pAPI->glMapNamedBufferRangeEXT; + glFlushMappedNamedBufferRangeEXT = pAPI->glFlushMappedNamedBufferRangeEXT; + glNamedBufferStorageEXT = pAPI->glNamedBufferStorageEXT; + glClearNamedBufferDataEXT = pAPI->glClearNamedBufferDataEXT; + glClearNamedBufferSubDataEXT = pAPI->glClearNamedBufferSubDataEXT; + glNamedFramebufferParameteriEXT = pAPI->glNamedFramebufferParameteriEXT; + glGetNamedFramebufferParameterivEXT = pAPI->glGetNamedFramebufferParameterivEXT; + glProgramUniform1dEXT = pAPI->glProgramUniform1dEXT; + glProgramUniform2dEXT = pAPI->glProgramUniform2dEXT; + glProgramUniform3dEXT = pAPI->glProgramUniform3dEXT; + glProgramUniform4dEXT = pAPI->glProgramUniform4dEXT; + glProgramUniform1dvEXT = pAPI->glProgramUniform1dvEXT; + glProgramUniform2dvEXT = pAPI->glProgramUniform2dvEXT; + glProgramUniform3dvEXT = pAPI->glProgramUniform3dvEXT; + glProgramUniform4dvEXT = pAPI->glProgramUniform4dvEXT; + glProgramUniformMatrix2dvEXT = pAPI->glProgramUniformMatrix2dvEXT; + glProgramUniformMatrix3dvEXT = pAPI->glProgramUniformMatrix3dvEXT; + glProgramUniformMatrix4dvEXT = pAPI->glProgramUniformMatrix4dvEXT; + glProgramUniformMatrix2x3dvEXT = pAPI->glProgramUniformMatrix2x3dvEXT; + glProgramUniformMatrix2x4dvEXT = pAPI->glProgramUniformMatrix2x4dvEXT; + glProgramUniformMatrix3x2dvEXT = pAPI->glProgramUniformMatrix3x2dvEXT; + glProgramUniformMatrix3x4dvEXT = pAPI->glProgramUniformMatrix3x4dvEXT; + glProgramUniformMatrix4x2dvEXT = pAPI->glProgramUniformMatrix4x2dvEXT; + glProgramUniformMatrix4x3dvEXT = pAPI->glProgramUniformMatrix4x3dvEXT; + glTextureBufferRangeEXT = pAPI->glTextureBufferRangeEXT; + glTextureStorage1DEXT = pAPI->glTextureStorage1DEXT; + glTextureStorage2DEXT = pAPI->glTextureStorage2DEXT; + glTextureStorage3DEXT = pAPI->glTextureStorage3DEXT; + glTextureStorage2DMultisampleEXT = pAPI->glTextureStorage2DMultisampleEXT; + glTextureStorage3DMultisampleEXT = pAPI->glTextureStorage3DMultisampleEXT; + glVertexArrayBindVertexBufferEXT = pAPI->glVertexArrayBindVertexBufferEXT; + glVertexArrayVertexAttribFormatEXT = pAPI->glVertexArrayVertexAttribFormatEXT; + glVertexArrayVertexAttribIFormatEXT = pAPI->glVertexArrayVertexAttribIFormatEXT; + glVertexArrayVertexAttribLFormatEXT = pAPI->glVertexArrayVertexAttribLFormatEXT; + glVertexArrayVertexAttribBindingEXT = pAPI->glVertexArrayVertexAttribBindingEXT; + glVertexArrayVertexBindingDivisorEXT = pAPI->glVertexArrayVertexBindingDivisorEXT; + glVertexArrayVertexAttribLOffsetEXT = pAPI->glVertexArrayVertexAttribLOffsetEXT; + glTexturePageCommitmentEXT = pAPI->glTexturePageCommitmentEXT; + glVertexArrayVertexAttribDivisorEXT = pAPI->glVertexArrayVertexAttribDivisorEXT; + glColorMaskIndexedEXT = pAPI->glColorMaskIndexedEXT; + glDrawArraysInstancedEXT = pAPI->glDrawArraysInstancedEXT; + glDrawElementsInstancedEXT = pAPI->glDrawElementsInstancedEXT; + glDrawRangeElementsEXT = pAPI->glDrawRangeElementsEXT; + glBufferStorageExternalEXT = pAPI->glBufferStorageExternalEXT; + glNamedBufferStorageExternalEXT = pAPI->glNamedBufferStorageExternalEXT; + glFogCoordfEXT = pAPI->glFogCoordfEXT; + glFogCoordfvEXT = pAPI->glFogCoordfvEXT; + glFogCoorddEXT = pAPI->glFogCoorddEXT; + glFogCoorddvEXT = pAPI->glFogCoorddvEXT; + glFogCoordPointerEXT = pAPI->glFogCoordPointerEXT; + glBlitFramebufferEXT = pAPI->glBlitFramebufferEXT; + glRenderbufferStorageMultisampleEXT = pAPI->glRenderbufferStorageMultisampleEXT; + glIsRenderbufferEXT = pAPI->glIsRenderbufferEXT; + glBindRenderbufferEXT = pAPI->glBindRenderbufferEXT; + glDeleteRenderbuffersEXT = pAPI->glDeleteRenderbuffersEXT; + glGenRenderbuffersEXT = pAPI->glGenRenderbuffersEXT; + glRenderbufferStorageEXT = pAPI->glRenderbufferStorageEXT; + glGetRenderbufferParameterivEXT = pAPI->glGetRenderbufferParameterivEXT; + glIsFramebufferEXT = pAPI->glIsFramebufferEXT; + glBindFramebufferEXT = pAPI->glBindFramebufferEXT; + glDeleteFramebuffersEXT = pAPI->glDeleteFramebuffersEXT; + glGenFramebuffersEXT = pAPI->glGenFramebuffersEXT; + glCheckFramebufferStatusEXT = pAPI->glCheckFramebufferStatusEXT; + glFramebufferTexture1DEXT = pAPI->glFramebufferTexture1DEXT; + glFramebufferTexture2DEXT = pAPI->glFramebufferTexture2DEXT; + glFramebufferTexture3DEXT = pAPI->glFramebufferTexture3DEXT; + glFramebufferRenderbufferEXT = pAPI->glFramebufferRenderbufferEXT; + glGetFramebufferAttachmentParameterivEXT = pAPI->glGetFramebufferAttachmentParameterivEXT; + glGenerateMipmapEXT = pAPI->glGenerateMipmapEXT; + glProgramParameteriEXT = pAPI->glProgramParameteriEXT; + glProgramEnvParameters4fvEXT = pAPI->glProgramEnvParameters4fvEXT; + glProgramLocalParameters4fvEXT = pAPI->glProgramLocalParameters4fvEXT; + glGetUniformuivEXT = pAPI->glGetUniformuivEXT; + glBindFragDataLocationEXT = pAPI->glBindFragDataLocationEXT; + glGetFragDataLocationEXT = pAPI->glGetFragDataLocationEXT; + glUniform1uiEXT = pAPI->glUniform1uiEXT; + glUniform2uiEXT = pAPI->glUniform2uiEXT; + glUniform3uiEXT = pAPI->glUniform3uiEXT; + glUniform4uiEXT = pAPI->glUniform4uiEXT; + glUniform1uivEXT = pAPI->glUniform1uivEXT; + glUniform2uivEXT = pAPI->glUniform2uivEXT; + glUniform3uivEXT = pAPI->glUniform3uivEXT; + glUniform4uivEXT = pAPI->glUniform4uivEXT; + glGetHistogramEXT = pAPI->glGetHistogramEXT; + glGetHistogramParameterfvEXT = pAPI->glGetHistogramParameterfvEXT; + glGetHistogramParameterivEXT = pAPI->glGetHistogramParameterivEXT; + glGetMinmaxEXT = pAPI->glGetMinmaxEXT; + glGetMinmaxParameterfvEXT = pAPI->glGetMinmaxParameterfvEXT; + glGetMinmaxParameterivEXT = pAPI->glGetMinmaxParameterivEXT; + glHistogramEXT = pAPI->glHistogramEXT; + glMinmaxEXT = pAPI->glMinmaxEXT; + glResetHistogramEXT = pAPI->glResetHistogramEXT; + glResetMinmaxEXT = pAPI->glResetMinmaxEXT; + glIndexFuncEXT = pAPI->glIndexFuncEXT; + glIndexMaterialEXT = pAPI->glIndexMaterialEXT; + glApplyTextureEXT = pAPI->glApplyTextureEXT; + glTextureLightEXT = pAPI->glTextureLightEXT; + glTextureMaterialEXT = pAPI->glTextureMaterialEXT; + glGetUnsignedBytevEXT = pAPI->glGetUnsignedBytevEXT; + glGetUnsignedBytei_vEXT = pAPI->glGetUnsignedBytei_vEXT; + glDeleteMemoryObjectsEXT = pAPI->glDeleteMemoryObjectsEXT; + glIsMemoryObjectEXT = pAPI->glIsMemoryObjectEXT; + glCreateMemoryObjectsEXT = pAPI->glCreateMemoryObjectsEXT; + glMemoryObjectParameterivEXT = pAPI->glMemoryObjectParameterivEXT; + glGetMemoryObjectParameterivEXT = pAPI->glGetMemoryObjectParameterivEXT; + glTexStorageMem2DEXT = pAPI->glTexStorageMem2DEXT; + glTexStorageMem2DMultisampleEXT = pAPI->glTexStorageMem2DMultisampleEXT; + glTexStorageMem3DEXT = pAPI->glTexStorageMem3DEXT; + glTexStorageMem3DMultisampleEXT = pAPI->glTexStorageMem3DMultisampleEXT; + glBufferStorageMemEXT = pAPI->glBufferStorageMemEXT; + glTextureStorageMem2DEXT = pAPI->glTextureStorageMem2DEXT; + glTextureStorageMem2DMultisampleEXT = pAPI->glTextureStorageMem2DMultisampleEXT; + glTextureStorageMem3DEXT = pAPI->glTextureStorageMem3DEXT; + glTextureStorageMem3DMultisampleEXT = pAPI->glTextureStorageMem3DMultisampleEXT; + glNamedBufferStorageMemEXT = pAPI->glNamedBufferStorageMemEXT; + glTexStorageMem1DEXT = pAPI->glTexStorageMem1DEXT; + glTextureStorageMem1DEXT = pAPI->glTextureStorageMem1DEXT; + glImportMemoryFdEXT = pAPI->glImportMemoryFdEXT; + glImportMemoryWin32HandleEXT = pAPI->glImportMemoryWin32HandleEXT; + glImportMemoryWin32NameEXT = pAPI->glImportMemoryWin32NameEXT; + glMultiDrawArraysEXT = pAPI->glMultiDrawArraysEXT; + glMultiDrawElementsEXT = pAPI->glMultiDrawElementsEXT; + glSampleMaskEXT = pAPI->glSampleMaskEXT; + glSamplePatternEXT = pAPI->glSamplePatternEXT; + glColorTableEXT = pAPI->glColorTableEXT; + glGetColorTableEXT = pAPI->glGetColorTableEXT; + glGetColorTableParameterivEXT = pAPI->glGetColorTableParameterivEXT; + glGetColorTableParameterfvEXT = pAPI->glGetColorTableParameterfvEXT; + glPixelTransformParameteriEXT = pAPI->glPixelTransformParameteriEXT; + glPixelTransformParameterfEXT = pAPI->glPixelTransformParameterfEXT; + glPixelTransformParameterivEXT = pAPI->glPixelTransformParameterivEXT; + glPixelTransformParameterfvEXT = pAPI->glPixelTransformParameterfvEXT; + glGetPixelTransformParameterivEXT = pAPI->glGetPixelTransformParameterivEXT; + glGetPixelTransformParameterfvEXT = pAPI->glGetPixelTransformParameterfvEXT; + glPointParameterfEXT = pAPI->glPointParameterfEXT; + glPointParameterfvEXT = pAPI->glPointParameterfvEXT; + glPolygonOffsetEXT = pAPI->glPolygonOffsetEXT; + glPolygonOffsetClampEXT = pAPI->glPolygonOffsetClampEXT; + glProvokingVertexEXT = pAPI->glProvokingVertexEXT; + glRasterSamplesEXT = pAPI->glRasterSamplesEXT; + glGenSemaphoresEXT = pAPI->glGenSemaphoresEXT; + glDeleteSemaphoresEXT = pAPI->glDeleteSemaphoresEXT; + glIsSemaphoreEXT = pAPI->glIsSemaphoreEXT; + glSemaphoreParameterui64vEXT = pAPI->glSemaphoreParameterui64vEXT; + glGetSemaphoreParameterui64vEXT = pAPI->glGetSemaphoreParameterui64vEXT; + glWaitSemaphoreEXT = pAPI->glWaitSemaphoreEXT; + glSignalSemaphoreEXT = pAPI->glSignalSemaphoreEXT; + glImportSemaphoreFdEXT = pAPI->glImportSemaphoreFdEXT; + glImportSemaphoreWin32HandleEXT = pAPI->glImportSemaphoreWin32HandleEXT; + glImportSemaphoreWin32NameEXT = pAPI->glImportSemaphoreWin32NameEXT; + glSecondaryColor3bEXT = pAPI->glSecondaryColor3bEXT; + glSecondaryColor3bvEXT = pAPI->glSecondaryColor3bvEXT; + glSecondaryColor3dEXT = pAPI->glSecondaryColor3dEXT; + glSecondaryColor3dvEXT = pAPI->glSecondaryColor3dvEXT; + glSecondaryColor3fEXT = pAPI->glSecondaryColor3fEXT; + glSecondaryColor3fvEXT = pAPI->glSecondaryColor3fvEXT; + glSecondaryColor3iEXT = pAPI->glSecondaryColor3iEXT; + glSecondaryColor3ivEXT = pAPI->glSecondaryColor3ivEXT; + glSecondaryColor3sEXT = pAPI->glSecondaryColor3sEXT; + glSecondaryColor3svEXT = pAPI->glSecondaryColor3svEXT; + glSecondaryColor3ubEXT = pAPI->glSecondaryColor3ubEXT; + glSecondaryColor3ubvEXT = pAPI->glSecondaryColor3ubvEXT; + glSecondaryColor3uiEXT = pAPI->glSecondaryColor3uiEXT; + glSecondaryColor3uivEXT = pAPI->glSecondaryColor3uivEXT; + glSecondaryColor3usEXT = pAPI->glSecondaryColor3usEXT; + glSecondaryColor3usvEXT = pAPI->glSecondaryColor3usvEXT; + glSecondaryColorPointerEXT = pAPI->glSecondaryColorPointerEXT; + glUseShaderProgramEXT = pAPI->glUseShaderProgramEXT; + glActiveProgramEXT = pAPI->glActiveProgramEXT; + glCreateShaderProgramEXT = pAPI->glCreateShaderProgramEXT; + glActiveShaderProgramEXT = pAPI->glActiveShaderProgramEXT; + glBindProgramPipelineEXT = pAPI->glBindProgramPipelineEXT; + glCreateShaderProgramvEXT = pAPI->glCreateShaderProgramvEXT; + glDeleteProgramPipelinesEXT = pAPI->glDeleteProgramPipelinesEXT; + glGenProgramPipelinesEXT = pAPI->glGenProgramPipelinesEXT; + glGetProgramPipelineInfoLogEXT = pAPI->glGetProgramPipelineInfoLogEXT; + glGetProgramPipelineivEXT = pAPI->glGetProgramPipelineivEXT; + glIsProgramPipelineEXT = pAPI->glIsProgramPipelineEXT; + glUseProgramStagesEXT = pAPI->glUseProgramStagesEXT; + glValidateProgramPipelineEXT = pAPI->glValidateProgramPipelineEXT; + glFramebufferFetchBarrierEXT = pAPI->glFramebufferFetchBarrierEXT; + glBindImageTextureEXT = pAPI->glBindImageTextureEXT; + glMemoryBarrierEXT = pAPI->glMemoryBarrierEXT; + glStencilClearTagEXT = pAPI->glStencilClearTagEXT; + glActiveStencilFaceEXT = pAPI->glActiveStencilFaceEXT; + glTexSubImage1DEXT = pAPI->glTexSubImage1DEXT; + glTexSubImage2DEXT = pAPI->glTexSubImage2DEXT; + glTexImage3DEXT = pAPI->glTexImage3DEXT; + glTexSubImage3DEXT = pAPI->glTexSubImage3DEXT; + glFramebufferTextureLayerEXT = pAPI->glFramebufferTextureLayerEXT; + glTexBufferEXT = pAPI->glTexBufferEXT; + glTexParameterIivEXT = pAPI->glTexParameterIivEXT; + glTexParameterIuivEXT = pAPI->glTexParameterIuivEXT; + glGetTexParameterIivEXT = pAPI->glGetTexParameterIivEXT; + glGetTexParameterIuivEXT = pAPI->glGetTexParameterIuivEXT; + glClearColorIiEXT = pAPI->glClearColorIiEXT; + glClearColorIuiEXT = pAPI->glClearColorIuiEXT; + glAreTexturesResidentEXT = pAPI->glAreTexturesResidentEXT; + glBindTextureEXT = pAPI->glBindTextureEXT; + glDeleteTexturesEXT = pAPI->glDeleteTexturesEXT; + glGenTexturesEXT = pAPI->glGenTexturesEXT; + glIsTextureEXT = pAPI->glIsTextureEXT; + glPrioritizeTexturesEXT = pAPI->glPrioritizeTexturesEXT; + glTextureNormalEXT = pAPI->glTextureNormalEXT; + glCreateSemaphoresNV = pAPI->glCreateSemaphoresNV; + glSemaphoreParameterivNV = pAPI->glSemaphoreParameterivNV; + glGetSemaphoreParameterivNV = pAPI->glGetSemaphoreParameterivNV; + glGetQueryObjecti64vEXT = pAPI->glGetQueryObjecti64vEXT; + glGetQueryObjectui64vEXT = pAPI->glGetQueryObjectui64vEXT; + glBeginTransformFeedbackEXT = pAPI->glBeginTransformFeedbackEXT; + glEndTransformFeedbackEXT = pAPI->glEndTransformFeedbackEXT; + glBindBufferRangeEXT = pAPI->glBindBufferRangeEXT; + glBindBufferOffsetEXT = pAPI->glBindBufferOffsetEXT; + glBindBufferBaseEXT = pAPI->glBindBufferBaseEXT; + glTransformFeedbackVaryingsEXT = pAPI->glTransformFeedbackVaryingsEXT; + glGetTransformFeedbackVaryingEXT = pAPI->glGetTransformFeedbackVaryingEXT; + glArrayElementEXT = pAPI->glArrayElementEXT; + glColorPointerEXT = pAPI->glColorPointerEXT; + glDrawArraysEXT = pAPI->glDrawArraysEXT; + glEdgeFlagPointerEXT = pAPI->glEdgeFlagPointerEXT; + glGetPointervEXT = pAPI->glGetPointervEXT; + glIndexPointerEXT = pAPI->glIndexPointerEXT; + glNormalPointerEXT = pAPI->glNormalPointerEXT; + glTexCoordPointerEXT = pAPI->glTexCoordPointerEXT; + glVertexPointerEXT = pAPI->glVertexPointerEXT; + glVertexAttribL1dEXT = pAPI->glVertexAttribL1dEXT; + glVertexAttribL2dEXT = pAPI->glVertexAttribL2dEXT; + glVertexAttribL3dEXT = pAPI->glVertexAttribL3dEXT; + glVertexAttribL4dEXT = pAPI->glVertexAttribL4dEXT; + glVertexAttribL1dvEXT = pAPI->glVertexAttribL1dvEXT; + glVertexAttribL2dvEXT = pAPI->glVertexAttribL2dvEXT; + glVertexAttribL3dvEXT = pAPI->glVertexAttribL3dvEXT; + glVertexAttribL4dvEXT = pAPI->glVertexAttribL4dvEXT; + glVertexAttribLPointerEXT = pAPI->glVertexAttribLPointerEXT; + glGetVertexAttribLdvEXT = pAPI->glGetVertexAttribLdvEXT; + glBeginVertexShaderEXT = pAPI->glBeginVertexShaderEXT; + glEndVertexShaderEXT = pAPI->glEndVertexShaderEXT; + glBindVertexShaderEXT = pAPI->glBindVertexShaderEXT; + glGenVertexShadersEXT = pAPI->glGenVertexShadersEXT; + glDeleteVertexShaderEXT = pAPI->glDeleteVertexShaderEXT; + glShaderOp1EXT = pAPI->glShaderOp1EXT; + glShaderOp2EXT = pAPI->glShaderOp2EXT; + glShaderOp3EXT = pAPI->glShaderOp3EXT; + glSwizzleEXT = pAPI->glSwizzleEXT; + glWriteMaskEXT = pAPI->glWriteMaskEXT; + glInsertComponentEXT = pAPI->glInsertComponentEXT; + glExtractComponentEXT = pAPI->glExtractComponentEXT; + glGenSymbolsEXT = pAPI->glGenSymbolsEXT; + glSetInvariantEXT = pAPI->glSetInvariantEXT; + glSetLocalConstantEXT = pAPI->glSetLocalConstantEXT; + glVariantbvEXT = pAPI->glVariantbvEXT; + glVariantsvEXT = pAPI->glVariantsvEXT; + glVariantivEXT = pAPI->glVariantivEXT; + glVariantfvEXT = pAPI->glVariantfvEXT; + glVariantdvEXT = pAPI->glVariantdvEXT; + glVariantubvEXT = pAPI->glVariantubvEXT; + glVariantusvEXT = pAPI->glVariantusvEXT; + glVariantuivEXT = pAPI->glVariantuivEXT; + glVariantPointerEXT = pAPI->glVariantPointerEXT; + glEnableVariantClientStateEXT = pAPI->glEnableVariantClientStateEXT; + glDisableVariantClientStateEXT = pAPI->glDisableVariantClientStateEXT; + glBindLightParameterEXT = pAPI->glBindLightParameterEXT; + glBindMaterialParameterEXT = pAPI->glBindMaterialParameterEXT; + glBindTexGenParameterEXT = pAPI->glBindTexGenParameterEXT; + glBindTextureUnitParameterEXT = pAPI->glBindTextureUnitParameterEXT; + glBindParameterEXT = pAPI->glBindParameterEXT; + glIsVariantEnabledEXT = pAPI->glIsVariantEnabledEXT; + glGetVariantBooleanvEXT = pAPI->glGetVariantBooleanvEXT; + glGetVariantIntegervEXT = pAPI->glGetVariantIntegervEXT; + glGetVariantFloatvEXT = pAPI->glGetVariantFloatvEXT; + glGetVariantPointervEXT = pAPI->glGetVariantPointervEXT; + glGetInvariantBooleanvEXT = pAPI->glGetInvariantBooleanvEXT; + glGetInvariantIntegervEXT = pAPI->glGetInvariantIntegervEXT; + glGetInvariantFloatvEXT = pAPI->glGetInvariantFloatvEXT; + glGetLocalConstantBooleanvEXT = pAPI->glGetLocalConstantBooleanvEXT; + glGetLocalConstantIntegervEXT = pAPI->glGetLocalConstantIntegervEXT; + glGetLocalConstantFloatvEXT = pAPI->glGetLocalConstantFloatvEXT; + glVertexWeightfEXT = pAPI->glVertexWeightfEXT; + glVertexWeightfvEXT = pAPI->glVertexWeightfvEXT; + glVertexWeightPointerEXT = pAPI->glVertexWeightPointerEXT; + glAcquireKeyedMutexWin32EXT = pAPI->glAcquireKeyedMutexWin32EXT; + glReleaseKeyedMutexWin32EXT = pAPI->glReleaseKeyedMutexWin32EXT; + glWindowRectanglesEXT = pAPI->glWindowRectanglesEXT; + glImportSyncEXT = pAPI->glImportSyncEXT; + glFrameTerminatorGREMEDY = pAPI->glFrameTerminatorGREMEDY; + glStringMarkerGREMEDY = pAPI->glStringMarkerGREMEDY; + glImageTransformParameteriHP = pAPI->glImageTransformParameteriHP; + glImageTransformParameterfHP = pAPI->glImageTransformParameterfHP; + glImageTransformParameterivHP = pAPI->glImageTransformParameterivHP; + glImageTransformParameterfvHP = pAPI->glImageTransformParameterfvHP; + glGetImageTransformParameterivHP = pAPI->glGetImageTransformParameterivHP; + glGetImageTransformParameterfvHP = pAPI->glGetImageTransformParameterfvHP; + glMultiModeDrawArraysIBM = pAPI->glMultiModeDrawArraysIBM; + glMultiModeDrawElementsIBM = pAPI->glMultiModeDrawElementsIBM; + glFlushStaticDataIBM = pAPI->glFlushStaticDataIBM; + glColorPointerListIBM = pAPI->glColorPointerListIBM; + glSecondaryColorPointerListIBM = pAPI->glSecondaryColorPointerListIBM; + glEdgeFlagPointerListIBM = pAPI->glEdgeFlagPointerListIBM; + glFogCoordPointerListIBM = pAPI->glFogCoordPointerListIBM; + glIndexPointerListIBM = pAPI->glIndexPointerListIBM; + glNormalPointerListIBM = pAPI->glNormalPointerListIBM; + glTexCoordPointerListIBM = pAPI->glTexCoordPointerListIBM; + glVertexPointerListIBM = pAPI->glVertexPointerListIBM; + glBlendFuncSeparateINGR = pAPI->glBlendFuncSeparateINGR; + glApplyFramebufferAttachmentCMAAINTEL = pAPI->glApplyFramebufferAttachmentCMAAINTEL; + glSyncTextureINTEL = pAPI->glSyncTextureINTEL; + glUnmapTexture2DINTEL = pAPI->glUnmapTexture2DINTEL; + glMapTexture2DINTEL = pAPI->glMapTexture2DINTEL; + glVertexPointervINTEL = pAPI->glVertexPointervINTEL; + glNormalPointervINTEL = pAPI->glNormalPointervINTEL; + glColorPointervINTEL = pAPI->glColorPointervINTEL; + glTexCoordPointervINTEL = pAPI->glTexCoordPointervINTEL; + glBeginPerfQueryINTEL = pAPI->glBeginPerfQueryINTEL; + glCreatePerfQueryINTEL = pAPI->glCreatePerfQueryINTEL; + glDeletePerfQueryINTEL = pAPI->glDeletePerfQueryINTEL; + glEndPerfQueryINTEL = pAPI->glEndPerfQueryINTEL; + glGetFirstPerfQueryIdINTEL = pAPI->glGetFirstPerfQueryIdINTEL; + glGetNextPerfQueryIdINTEL = pAPI->glGetNextPerfQueryIdINTEL; + glGetPerfCounterInfoINTEL = pAPI->glGetPerfCounterInfoINTEL; + glGetPerfQueryDataINTEL = pAPI->glGetPerfQueryDataINTEL; + glGetPerfQueryIdByNameINTEL = pAPI->glGetPerfQueryIdByNameINTEL; + glGetPerfQueryInfoINTEL = pAPI->glGetPerfQueryInfoINTEL; + glBlendBarrierKHR = pAPI->glBlendBarrierKHR; + glDebugMessageControlKHR = pAPI->glDebugMessageControlKHR; + glDebugMessageInsertKHR = pAPI->glDebugMessageInsertKHR; + glDebugMessageCallbackKHR = pAPI->glDebugMessageCallbackKHR; + glGetDebugMessageLogKHR = pAPI->glGetDebugMessageLogKHR; + glPushDebugGroupKHR = pAPI->glPushDebugGroupKHR; + glPopDebugGroupKHR = pAPI->glPopDebugGroupKHR; + glObjectLabelKHR = pAPI->glObjectLabelKHR; + glGetObjectLabelKHR = pAPI->glGetObjectLabelKHR; + glObjectPtrLabelKHR = pAPI->glObjectPtrLabelKHR; + glGetObjectPtrLabelKHR = pAPI->glGetObjectPtrLabelKHR; + glGetPointervKHR = pAPI->glGetPointervKHR; + glGetGraphicsResetStatusKHR = pAPI->glGetGraphicsResetStatusKHR; + glReadnPixelsKHR = pAPI->glReadnPixelsKHR; + glGetnUniformfvKHR = pAPI->glGetnUniformfvKHR; + glGetnUniformivKHR = pAPI->glGetnUniformivKHR; + glGetnUniformuivKHR = pAPI->glGetnUniformuivKHR; + glMaxShaderCompilerThreadsKHR = pAPI->glMaxShaderCompilerThreadsKHR; + glFramebufferParameteriMESA = pAPI->glFramebufferParameteriMESA; + glGetFramebufferParameterivMESA = pAPI->glGetFramebufferParameterivMESA; + glResizeBuffersMESA = pAPI->glResizeBuffersMESA; + glWindowPos2dMESA = pAPI->glWindowPos2dMESA; + glWindowPos2dvMESA = pAPI->glWindowPos2dvMESA; + glWindowPos2fMESA = pAPI->glWindowPos2fMESA; + glWindowPos2fvMESA = pAPI->glWindowPos2fvMESA; + glWindowPos2iMESA = pAPI->glWindowPos2iMESA; + glWindowPos2ivMESA = pAPI->glWindowPos2ivMESA; + glWindowPos2sMESA = pAPI->glWindowPos2sMESA; + glWindowPos2svMESA = pAPI->glWindowPos2svMESA; + glWindowPos3dMESA = pAPI->glWindowPos3dMESA; + glWindowPos3dvMESA = pAPI->glWindowPos3dvMESA; + glWindowPos3fMESA = pAPI->glWindowPos3fMESA; + glWindowPos3fvMESA = pAPI->glWindowPos3fvMESA; + glWindowPos3iMESA = pAPI->glWindowPos3iMESA; + glWindowPos3ivMESA = pAPI->glWindowPos3ivMESA; + glWindowPos3sMESA = pAPI->glWindowPos3sMESA; + glWindowPos3svMESA = pAPI->glWindowPos3svMESA; + glWindowPos4dMESA = pAPI->glWindowPos4dMESA; + glWindowPos4dvMESA = pAPI->glWindowPos4dvMESA; + glWindowPos4fMESA = pAPI->glWindowPos4fMESA; + glWindowPos4fvMESA = pAPI->glWindowPos4fvMESA; + glWindowPos4iMESA = pAPI->glWindowPos4iMESA; + glWindowPos4ivMESA = pAPI->glWindowPos4ivMESA; + glWindowPos4sMESA = pAPI->glWindowPos4sMESA; + glWindowPos4svMESA = pAPI->glWindowPos4svMESA; + glBeginConditionalRenderNVX = pAPI->glBeginConditionalRenderNVX; + glEndConditionalRenderNVX = pAPI->glEndConditionalRenderNVX; + glLGPUNamedBufferSubDataNVX = pAPI->glLGPUNamedBufferSubDataNVX; + glLGPUCopyImageSubDataNVX = pAPI->glLGPUCopyImageSubDataNVX; + glLGPUInterlockNVX = pAPI->glLGPUInterlockNVX; + glAlphaToCoverageDitherControlNV = pAPI->glAlphaToCoverageDitherControlNV; + glMultiDrawArraysIndirectBindlessNV = pAPI->glMultiDrawArraysIndirectBindlessNV; + glMultiDrawElementsIndirectBindlessNV = pAPI->glMultiDrawElementsIndirectBindlessNV; + glMultiDrawArraysIndirectBindlessCountNV = pAPI->glMultiDrawArraysIndirectBindlessCountNV; + glMultiDrawElementsIndirectBindlessCountNV = pAPI->glMultiDrawElementsIndirectBindlessCountNV; + glGetTextureHandleNV = pAPI->glGetTextureHandleNV; + glGetTextureSamplerHandleNV = pAPI->glGetTextureSamplerHandleNV; + glMakeTextureHandleResidentNV = pAPI->glMakeTextureHandleResidentNV; + glMakeTextureHandleNonResidentNV = pAPI->glMakeTextureHandleNonResidentNV; + glGetImageHandleNV = pAPI->glGetImageHandleNV; + glMakeImageHandleResidentNV = pAPI->glMakeImageHandleResidentNV; + glMakeImageHandleNonResidentNV = pAPI->glMakeImageHandleNonResidentNV; + glUniformHandleui64NV = pAPI->glUniformHandleui64NV; + glUniformHandleui64vNV = pAPI->glUniformHandleui64vNV; + glProgramUniformHandleui64NV = pAPI->glProgramUniformHandleui64NV; + glProgramUniformHandleui64vNV = pAPI->glProgramUniformHandleui64vNV; + glIsTextureHandleResidentNV = pAPI->glIsTextureHandleResidentNV; + glIsImageHandleResidentNV = pAPI->glIsImageHandleResidentNV; + glBlendParameteriNV = pAPI->glBlendParameteriNV; + glBlendBarrierNV = pAPI->glBlendBarrierNV; + glViewportPositionWScaleNV = pAPI->glViewportPositionWScaleNV; + glCreateStatesNV = pAPI->glCreateStatesNV; + glDeleteStatesNV = pAPI->glDeleteStatesNV; + glIsStateNV = pAPI->glIsStateNV; + glStateCaptureNV = pAPI->glStateCaptureNV; + glGetCommandHeaderNV = pAPI->glGetCommandHeaderNV; + glGetStageIndexNV = pAPI->glGetStageIndexNV; + glDrawCommandsNV = pAPI->glDrawCommandsNV; + glDrawCommandsAddressNV = pAPI->glDrawCommandsAddressNV; + glDrawCommandsStatesNV = pAPI->glDrawCommandsStatesNV; + glDrawCommandsStatesAddressNV = pAPI->glDrawCommandsStatesAddressNV; + glCreateCommandListsNV = pAPI->glCreateCommandListsNV; + glDeleteCommandListsNV = pAPI->glDeleteCommandListsNV; + glIsCommandListNV = pAPI->glIsCommandListNV; + glListDrawCommandsStatesClientNV = pAPI->glListDrawCommandsStatesClientNV; + glCommandListSegmentsNV = pAPI->glCommandListSegmentsNV; + glCompileCommandListNV = pAPI->glCompileCommandListNV; + glCallCommandListNV = pAPI->glCallCommandListNV; + glBeginConditionalRenderNV = pAPI->glBeginConditionalRenderNV; + glEndConditionalRenderNV = pAPI->glEndConditionalRenderNV; + glSubpixelPrecisionBiasNV = pAPI->glSubpixelPrecisionBiasNV; + glConservativeRasterParameterfNV = pAPI->glConservativeRasterParameterfNV; + glConservativeRasterParameteriNV = pAPI->glConservativeRasterParameteriNV; + glCopyImageSubDataNV = pAPI->glCopyImageSubDataNV; + glDepthRangedNV = pAPI->glDepthRangedNV; + glClearDepthdNV = pAPI->glClearDepthdNV; + glDepthBoundsdNV = pAPI->glDepthBoundsdNV; + glDrawTextureNV = pAPI->glDrawTextureNV; + glDrawVkImageNV = pAPI->glDrawVkImageNV; + glGetVkProcAddrNV = pAPI->glGetVkProcAddrNV; + glWaitVkSemaphoreNV = pAPI->glWaitVkSemaphoreNV; + glSignalVkSemaphoreNV = pAPI->glSignalVkSemaphoreNV; + glSignalVkFenceNV = pAPI->glSignalVkFenceNV; + glMapControlPointsNV = pAPI->glMapControlPointsNV; + glMapParameterivNV = pAPI->glMapParameterivNV; + glMapParameterfvNV = pAPI->glMapParameterfvNV; + glGetMapControlPointsNV = pAPI->glGetMapControlPointsNV; + glGetMapParameterivNV = pAPI->glGetMapParameterivNV; + glGetMapParameterfvNV = pAPI->glGetMapParameterfvNV; + glGetMapAttribParameterivNV = pAPI->glGetMapAttribParameterivNV; + glGetMapAttribParameterfvNV = pAPI->glGetMapAttribParameterfvNV; + glEvalMapsNV = pAPI->glEvalMapsNV; + glGetMultisamplefvNV = pAPI->glGetMultisamplefvNV; + glSampleMaskIndexedNV = pAPI->glSampleMaskIndexedNV; + glTexRenderbufferNV = pAPI->glTexRenderbufferNV; + glDeleteFencesNV = pAPI->glDeleteFencesNV; + glGenFencesNV = pAPI->glGenFencesNV; + glIsFenceNV = pAPI->glIsFenceNV; + glTestFenceNV = pAPI->glTestFenceNV; + glGetFenceivNV = pAPI->glGetFenceivNV; + glFinishFenceNV = pAPI->glFinishFenceNV; + glSetFenceNV = pAPI->glSetFenceNV; + glFragmentCoverageColorNV = pAPI->glFragmentCoverageColorNV; + glProgramNamedParameter4fNV = pAPI->glProgramNamedParameter4fNV; + glProgramNamedParameter4fvNV = pAPI->glProgramNamedParameter4fvNV; + glProgramNamedParameter4dNV = pAPI->glProgramNamedParameter4dNV; + glProgramNamedParameter4dvNV = pAPI->glProgramNamedParameter4dvNV; + glGetProgramNamedParameterfvNV = pAPI->glGetProgramNamedParameterfvNV; + glGetProgramNamedParameterdvNV = pAPI->glGetProgramNamedParameterdvNV; + glCoverageModulationTableNV = pAPI->glCoverageModulationTableNV; + glGetCoverageModulationTableNV = pAPI->glGetCoverageModulationTableNV; + glCoverageModulationNV = pAPI->glCoverageModulationNV; + glRenderbufferStorageMultisampleCoverageNV = pAPI->glRenderbufferStorageMultisampleCoverageNV; + glProgramVertexLimitNV = pAPI->glProgramVertexLimitNV; + glFramebufferTextureEXT = pAPI->glFramebufferTextureEXT; + glFramebufferTextureFaceEXT = pAPI->glFramebufferTextureFaceEXT; + glProgramLocalParameterI4iNV = pAPI->glProgramLocalParameterI4iNV; + glProgramLocalParameterI4ivNV = pAPI->glProgramLocalParameterI4ivNV; + glProgramLocalParametersI4ivNV = pAPI->glProgramLocalParametersI4ivNV; + glProgramLocalParameterI4uiNV = pAPI->glProgramLocalParameterI4uiNV; + glProgramLocalParameterI4uivNV = pAPI->glProgramLocalParameterI4uivNV; + glProgramLocalParametersI4uivNV = pAPI->glProgramLocalParametersI4uivNV; + glProgramEnvParameterI4iNV = pAPI->glProgramEnvParameterI4iNV; + glProgramEnvParameterI4ivNV = pAPI->glProgramEnvParameterI4ivNV; + glProgramEnvParametersI4ivNV = pAPI->glProgramEnvParametersI4ivNV; + glProgramEnvParameterI4uiNV = pAPI->glProgramEnvParameterI4uiNV; + glProgramEnvParameterI4uivNV = pAPI->glProgramEnvParameterI4uivNV; + glProgramEnvParametersI4uivNV = pAPI->glProgramEnvParametersI4uivNV; + glGetProgramLocalParameterIivNV = pAPI->glGetProgramLocalParameterIivNV; + glGetProgramLocalParameterIuivNV = pAPI->glGetProgramLocalParameterIuivNV; + glGetProgramEnvParameterIivNV = pAPI->glGetProgramEnvParameterIivNV; + glGetProgramEnvParameterIuivNV = pAPI->glGetProgramEnvParameterIuivNV; + glProgramSubroutineParametersuivNV = pAPI->glProgramSubroutineParametersuivNV; + glGetProgramSubroutineParameteruivNV = pAPI->glGetProgramSubroutineParameteruivNV; + glVertex2hNV = pAPI->glVertex2hNV; + glVertex2hvNV = pAPI->glVertex2hvNV; + glVertex3hNV = pAPI->glVertex3hNV; + glVertex3hvNV = pAPI->glVertex3hvNV; + glVertex4hNV = pAPI->glVertex4hNV; + glVertex4hvNV = pAPI->glVertex4hvNV; + glNormal3hNV = pAPI->glNormal3hNV; + glNormal3hvNV = pAPI->glNormal3hvNV; + glColor3hNV = pAPI->glColor3hNV; + glColor3hvNV = pAPI->glColor3hvNV; + glColor4hNV = pAPI->glColor4hNV; + glColor4hvNV = pAPI->glColor4hvNV; + glTexCoord1hNV = pAPI->glTexCoord1hNV; + glTexCoord1hvNV = pAPI->glTexCoord1hvNV; + glTexCoord2hNV = pAPI->glTexCoord2hNV; + glTexCoord2hvNV = pAPI->glTexCoord2hvNV; + glTexCoord3hNV = pAPI->glTexCoord3hNV; + glTexCoord3hvNV = pAPI->glTexCoord3hvNV; + glTexCoord4hNV = pAPI->glTexCoord4hNV; + glTexCoord4hvNV = pAPI->glTexCoord4hvNV; + glMultiTexCoord1hNV = pAPI->glMultiTexCoord1hNV; + glMultiTexCoord1hvNV = pAPI->glMultiTexCoord1hvNV; + glMultiTexCoord2hNV = pAPI->glMultiTexCoord2hNV; + glMultiTexCoord2hvNV = pAPI->glMultiTexCoord2hvNV; + glMultiTexCoord3hNV = pAPI->glMultiTexCoord3hNV; + glMultiTexCoord3hvNV = pAPI->glMultiTexCoord3hvNV; + glMultiTexCoord4hNV = pAPI->glMultiTexCoord4hNV; + glMultiTexCoord4hvNV = pAPI->glMultiTexCoord4hvNV; + glFogCoordhNV = pAPI->glFogCoordhNV; + glFogCoordhvNV = pAPI->glFogCoordhvNV; + glSecondaryColor3hNV = pAPI->glSecondaryColor3hNV; + glSecondaryColor3hvNV = pAPI->glSecondaryColor3hvNV; + glVertexWeighthNV = pAPI->glVertexWeighthNV; + glVertexWeighthvNV = pAPI->glVertexWeighthvNV; + glVertexAttrib1hNV = pAPI->glVertexAttrib1hNV; + glVertexAttrib1hvNV = pAPI->glVertexAttrib1hvNV; + glVertexAttrib2hNV = pAPI->glVertexAttrib2hNV; + glVertexAttrib2hvNV = pAPI->glVertexAttrib2hvNV; + glVertexAttrib3hNV = pAPI->glVertexAttrib3hNV; + glVertexAttrib3hvNV = pAPI->glVertexAttrib3hvNV; + glVertexAttrib4hNV = pAPI->glVertexAttrib4hNV; + glVertexAttrib4hvNV = pAPI->glVertexAttrib4hvNV; + glVertexAttribs1hvNV = pAPI->glVertexAttribs1hvNV; + glVertexAttribs2hvNV = pAPI->glVertexAttribs2hvNV; + glVertexAttribs3hvNV = pAPI->glVertexAttribs3hvNV; + glVertexAttribs4hvNV = pAPI->glVertexAttribs4hvNV; + glGetInternalformatSampleivNV = pAPI->glGetInternalformatSampleivNV; + glRenderGpuMaskNV = pAPI->glRenderGpuMaskNV; + glMulticastBufferSubDataNV = pAPI->glMulticastBufferSubDataNV; + glMulticastCopyBufferSubDataNV = pAPI->glMulticastCopyBufferSubDataNV; + glMulticastCopyImageSubDataNV = pAPI->glMulticastCopyImageSubDataNV; + glMulticastBlitFramebufferNV = pAPI->glMulticastBlitFramebufferNV; + glMulticastFramebufferSampleLocationsfvNV = pAPI->glMulticastFramebufferSampleLocationsfvNV; + glMulticastBarrierNV = pAPI->glMulticastBarrierNV; + glMulticastWaitSyncNV = pAPI->glMulticastWaitSyncNV; + glMulticastGetQueryObjectivNV = pAPI->glMulticastGetQueryObjectivNV; + glMulticastGetQueryObjectuivNV = pAPI->glMulticastGetQueryObjectuivNV; + glMulticastGetQueryObjecti64vNV = pAPI->glMulticastGetQueryObjecti64vNV; + glMulticastGetQueryObjectui64vNV = pAPI->glMulticastGetQueryObjectui64vNV; + glUploadGpuMaskNVX = pAPI->glUploadGpuMaskNVX; + glMulticastViewportArrayvNVX = pAPI->glMulticastViewportArrayvNVX; + glMulticastViewportPositionWScaleNVX = pAPI->glMulticastViewportPositionWScaleNVX; + glMulticastScissorArrayvNVX = pAPI->glMulticastScissorArrayvNVX; + glAsyncCopyBufferSubDataNVX = pAPI->glAsyncCopyBufferSubDataNVX; + glAsyncCopyImageSubDataNVX = pAPI->glAsyncCopyImageSubDataNVX; + glCreateProgressFenceNVX = pAPI->glCreateProgressFenceNVX; + glSignalSemaphoreui64NVX = pAPI->glSignalSemaphoreui64NVX; + glWaitSemaphoreui64NVX = pAPI->glWaitSemaphoreui64NVX; + glClientWaitSemaphoreui64NVX = pAPI->glClientWaitSemaphoreui64NVX; + glGetMemoryObjectDetachedResourcesuivNV = pAPI->glGetMemoryObjectDetachedResourcesuivNV; + glResetMemoryObjectParameterNV = pAPI->glResetMemoryObjectParameterNV; + glTexAttachMemoryNV = pAPI->glTexAttachMemoryNV; + glBufferAttachMemoryNV = pAPI->glBufferAttachMemoryNV; + glTextureAttachMemoryNV = pAPI->glTextureAttachMemoryNV; + glNamedBufferAttachMemoryNV = pAPI->glNamedBufferAttachMemoryNV; + glBufferPageCommitmentMemNV = pAPI->glBufferPageCommitmentMemNV; + glTexPageCommitmentMemNV = pAPI->glTexPageCommitmentMemNV; + glNamedBufferPageCommitmentMemNV = pAPI->glNamedBufferPageCommitmentMemNV; + glTexturePageCommitmentMemNV = pAPI->glTexturePageCommitmentMemNV; + glDrawMeshTasksNV = pAPI->glDrawMeshTasksNV; + glDrawMeshTasksIndirectNV = pAPI->glDrawMeshTasksIndirectNV; + glMultiDrawMeshTasksIndirectNV = pAPI->glMultiDrawMeshTasksIndirectNV; + glMultiDrawMeshTasksIndirectCountNV = pAPI->glMultiDrawMeshTasksIndirectCountNV; + glGenOcclusionQueriesNV = pAPI->glGenOcclusionQueriesNV; + glDeleteOcclusionQueriesNV = pAPI->glDeleteOcclusionQueriesNV; + glIsOcclusionQueryNV = pAPI->glIsOcclusionQueryNV; + glBeginOcclusionQueryNV = pAPI->glBeginOcclusionQueryNV; + glEndOcclusionQueryNV = pAPI->glEndOcclusionQueryNV; + glGetOcclusionQueryivNV = pAPI->glGetOcclusionQueryivNV; + glGetOcclusionQueryuivNV = pAPI->glGetOcclusionQueryuivNV; + glProgramBufferParametersfvNV = pAPI->glProgramBufferParametersfvNV; + glProgramBufferParametersIivNV = pAPI->glProgramBufferParametersIivNV; + glProgramBufferParametersIuivNV = pAPI->glProgramBufferParametersIuivNV; + glGenPathsNV = pAPI->glGenPathsNV; + glDeletePathsNV = pAPI->glDeletePathsNV; + glIsPathNV = pAPI->glIsPathNV; + glPathCommandsNV = pAPI->glPathCommandsNV; + glPathCoordsNV = pAPI->glPathCoordsNV; + glPathSubCommandsNV = pAPI->glPathSubCommandsNV; + glPathSubCoordsNV = pAPI->glPathSubCoordsNV; + glPathStringNV = pAPI->glPathStringNV; + glPathGlyphsNV = pAPI->glPathGlyphsNV; + glPathGlyphRangeNV = pAPI->glPathGlyphRangeNV; + glWeightPathsNV = pAPI->glWeightPathsNV; + glCopyPathNV = pAPI->glCopyPathNV; + glInterpolatePathsNV = pAPI->glInterpolatePathsNV; + glTransformPathNV = pAPI->glTransformPathNV; + glPathParameterivNV = pAPI->glPathParameterivNV; + glPathParameteriNV = pAPI->glPathParameteriNV; + glPathParameterfvNV = pAPI->glPathParameterfvNV; + glPathParameterfNV = pAPI->glPathParameterfNV; + glPathDashArrayNV = pAPI->glPathDashArrayNV; + glPathStencilFuncNV = pAPI->glPathStencilFuncNV; + glPathStencilDepthOffsetNV = pAPI->glPathStencilDepthOffsetNV; + glStencilFillPathNV = pAPI->glStencilFillPathNV; + glStencilStrokePathNV = pAPI->glStencilStrokePathNV; + glStencilFillPathInstancedNV = pAPI->glStencilFillPathInstancedNV; + glStencilStrokePathInstancedNV = pAPI->glStencilStrokePathInstancedNV; + glPathCoverDepthFuncNV = pAPI->glPathCoverDepthFuncNV; + glCoverFillPathNV = pAPI->glCoverFillPathNV; + glCoverStrokePathNV = pAPI->glCoverStrokePathNV; + glCoverFillPathInstancedNV = pAPI->glCoverFillPathInstancedNV; + glCoverStrokePathInstancedNV = pAPI->glCoverStrokePathInstancedNV; + glGetPathParameterivNV = pAPI->glGetPathParameterivNV; + glGetPathParameterfvNV = pAPI->glGetPathParameterfvNV; + glGetPathCommandsNV = pAPI->glGetPathCommandsNV; + glGetPathCoordsNV = pAPI->glGetPathCoordsNV; + glGetPathDashArrayNV = pAPI->glGetPathDashArrayNV; + glGetPathMetricsNV = pAPI->glGetPathMetricsNV; + glGetPathMetricRangeNV = pAPI->glGetPathMetricRangeNV; + glGetPathSpacingNV = pAPI->glGetPathSpacingNV; + glIsPointInFillPathNV = pAPI->glIsPointInFillPathNV; + glIsPointInStrokePathNV = pAPI->glIsPointInStrokePathNV; + glGetPathLengthNV = pAPI->glGetPathLengthNV; + glPointAlongPathNV = pAPI->glPointAlongPathNV; + glMatrixLoad3x2fNV = pAPI->glMatrixLoad3x2fNV; + glMatrixLoad3x3fNV = pAPI->glMatrixLoad3x3fNV; + glMatrixLoadTranspose3x3fNV = pAPI->glMatrixLoadTranspose3x3fNV; + glMatrixMult3x2fNV = pAPI->glMatrixMult3x2fNV; + glMatrixMult3x3fNV = pAPI->glMatrixMult3x3fNV; + glMatrixMultTranspose3x3fNV = pAPI->glMatrixMultTranspose3x3fNV; + glStencilThenCoverFillPathNV = pAPI->glStencilThenCoverFillPathNV; + glStencilThenCoverStrokePathNV = pAPI->glStencilThenCoverStrokePathNV; + glStencilThenCoverFillPathInstancedNV = pAPI->glStencilThenCoverFillPathInstancedNV; + glStencilThenCoverStrokePathInstancedNV = pAPI->glStencilThenCoverStrokePathInstancedNV; + glPathGlyphIndexRangeNV = pAPI->glPathGlyphIndexRangeNV; + glPathGlyphIndexArrayNV = pAPI->glPathGlyphIndexArrayNV; + glPathMemoryGlyphIndexArrayNV = pAPI->glPathMemoryGlyphIndexArrayNV; + glProgramPathFragmentInputGenNV = pAPI->glProgramPathFragmentInputGenNV; + glGetProgramResourcefvNV = pAPI->glGetProgramResourcefvNV; + glPathColorGenNV = pAPI->glPathColorGenNV; + glPathTexGenNV = pAPI->glPathTexGenNV; + glPathFogGenNV = pAPI->glPathFogGenNV; + glGetPathColorGenivNV = pAPI->glGetPathColorGenivNV; + glGetPathColorGenfvNV = pAPI->glGetPathColorGenfvNV; + glGetPathTexGenivNV = pAPI->glGetPathTexGenivNV; + glGetPathTexGenfvNV = pAPI->glGetPathTexGenfvNV; + glPixelDataRangeNV = pAPI->glPixelDataRangeNV; + glFlushPixelDataRangeNV = pAPI->glFlushPixelDataRangeNV; + glPointParameteriNV = pAPI->glPointParameteriNV; + glPointParameterivNV = pAPI->glPointParameterivNV; + glPresentFrameKeyedNV = pAPI->glPresentFrameKeyedNV; + glPresentFrameDualFillNV = pAPI->glPresentFrameDualFillNV; + glGetVideoivNV = pAPI->glGetVideoivNV; + glGetVideouivNV = pAPI->glGetVideouivNV; + glGetVideoi64vNV = pAPI->glGetVideoi64vNV; + glGetVideoui64vNV = pAPI->glGetVideoui64vNV; + glPrimitiveRestartNV = pAPI->glPrimitiveRestartNV; + glPrimitiveRestartIndexNV = pAPI->glPrimitiveRestartIndexNV; + glQueryResourceNV = pAPI->glQueryResourceNV; + glGenQueryResourceTagNV = pAPI->glGenQueryResourceTagNV; + glDeleteQueryResourceTagNV = pAPI->glDeleteQueryResourceTagNV; + glQueryResourceTagNV = pAPI->glQueryResourceTagNV; + glCombinerParameterfvNV = pAPI->glCombinerParameterfvNV; + glCombinerParameterfNV = pAPI->glCombinerParameterfNV; + glCombinerParameterivNV = pAPI->glCombinerParameterivNV; + glCombinerParameteriNV = pAPI->glCombinerParameteriNV; + glCombinerInputNV = pAPI->glCombinerInputNV; + glCombinerOutputNV = pAPI->glCombinerOutputNV; + glFinalCombinerInputNV = pAPI->glFinalCombinerInputNV; + glGetCombinerInputParameterfvNV = pAPI->glGetCombinerInputParameterfvNV; + glGetCombinerInputParameterivNV = pAPI->glGetCombinerInputParameterivNV; + glGetCombinerOutputParameterfvNV = pAPI->glGetCombinerOutputParameterfvNV; + glGetCombinerOutputParameterivNV = pAPI->glGetCombinerOutputParameterivNV; + glGetFinalCombinerInputParameterfvNV = pAPI->glGetFinalCombinerInputParameterfvNV; + glGetFinalCombinerInputParameterivNV = pAPI->glGetFinalCombinerInputParameterivNV; + glCombinerStageParameterfvNV = pAPI->glCombinerStageParameterfvNV; + glGetCombinerStageParameterfvNV = pAPI->glGetCombinerStageParameterfvNV; + glFramebufferSampleLocationsfvNV = pAPI->glFramebufferSampleLocationsfvNV; + glNamedFramebufferSampleLocationsfvNV = pAPI->glNamedFramebufferSampleLocationsfvNV; + glResolveDepthValuesNV = pAPI->glResolveDepthValuesNV; + glScissorExclusiveNV = pAPI->glScissorExclusiveNV; + glScissorExclusiveArrayvNV = pAPI->glScissorExclusiveArrayvNV; + glMakeBufferResidentNV = pAPI->glMakeBufferResidentNV; + glMakeBufferNonResidentNV = pAPI->glMakeBufferNonResidentNV; + glIsBufferResidentNV = pAPI->glIsBufferResidentNV; + glMakeNamedBufferResidentNV = pAPI->glMakeNamedBufferResidentNV; + glMakeNamedBufferNonResidentNV = pAPI->glMakeNamedBufferNonResidentNV; + glIsNamedBufferResidentNV = pAPI->glIsNamedBufferResidentNV; + glGetBufferParameterui64vNV = pAPI->glGetBufferParameterui64vNV; + glGetNamedBufferParameterui64vNV = pAPI->glGetNamedBufferParameterui64vNV; + glGetIntegerui64vNV = pAPI->glGetIntegerui64vNV; + glUniformui64NV = pAPI->glUniformui64NV; + glUniformui64vNV = pAPI->glUniformui64vNV; + glProgramUniformui64NV = pAPI->glProgramUniformui64NV; + glProgramUniformui64vNV = pAPI->glProgramUniformui64vNV; + glBindShadingRateImageNV = pAPI->glBindShadingRateImageNV; + glGetShadingRateImagePaletteNV = pAPI->glGetShadingRateImagePaletteNV; + glGetShadingRateSampleLocationivNV = pAPI->glGetShadingRateSampleLocationivNV; + glShadingRateImageBarrierNV = pAPI->glShadingRateImageBarrierNV; + glShadingRateImagePaletteNV = pAPI->glShadingRateImagePaletteNV; + glShadingRateSampleOrderNV = pAPI->glShadingRateSampleOrderNV; + glShadingRateSampleOrderCustomNV = pAPI->glShadingRateSampleOrderCustomNV; + glTextureBarrierNV = pAPI->glTextureBarrierNV; + glTexImage2DMultisampleCoverageNV = pAPI->glTexImage2DMultisampleCoverageNV; + glTexImage3DMultisampleCoverageNV = pAPI->glTexImage3DMultisampleCoverageNV; + glTextureImage2DMultisampleNV = pAPI->glTextureImage2DMultisampleNV; + glTextureImage3DMultisampleNV = pAPI->glTextureImage3DMultisampleNV; + glTextureImage2DMultisampleCoverageNV = pAPI->glTextureImage2DMultisampleCoverageNV; + glTextureImage3DMultisampleCoverageNV = pAPI->glTextureImage3DMultisampleCoverageNV; + glBeginTransformFeedbackNV = pAPI->glBeginTransformFeedbackNV; + glEndTransformFeedbackNV = pAPI->glEndTransformFeedbackNV; + glTransformFeedbackAttribsNV = pAPI->glTransformFeedbackAttribsNV; + glBindBufferRangeNV = pAPI->glBindBufferRangeNV; + glBindBufferOffsetNV = pAPI->glBindBufferOffsetNV; + glBindBufferBaseNV = pAPI->glBindBufferBaseNV; + glTransformFeedbackVaryingsNV = pAPI->glTransformFeedbackVaryingsNV; + glActiveVaryingNV = pAPI->glActiveVaryingNV; + glGetVaryingLocationNV = pAPI->glGetVaryingLocationNV; + glGetActiveVaryingNV = pAPI->glGetActiveVaryingNV; + glGetTransformFeedbackVaryingNV = pAPI->glGetTransformFeedbackVaryingNV; + glTransformFeedbackStreamAttribsNV = pAPI->glTransformFeedbackStreamAttribsNV; + glBindTransformFeedbackNV = pAPI->glBindTransformFeedbackNV; + glDeleteTransformFeedbacksNV = pAPI->glDeleteTransformFeedbacksNV; + glGenTransformFeedbacksNV = pAPI->glGenTransformFeedbacksNV; + glIsTransformFeedbackNV = pAPI->glIsTransformFeedbackNV; + glPauseTransformFeedbackNV = pAPI->glPauseTransformFeedbackNV; + glResumeTransformFeedbackNV = pAPI->glResumeTransformFeedbackNV; + glDrawTransformFeedbackNV = pAPI->glDrawTransformFeedbackNV; + glVDPAUInitNV = pAPI->glVDPAUInitNV; + glVDPAUFiniNV = pAPI->glVDPAUFiniNV; + glVDPAURegisterVideoSurfaceNV = pAPI->glVDPAURegisterVideoSurfaceNV; + glVDPAURegisterOutputSurfaceNV = pAPI->glVDPAURegisterOutputSurfaceNV; + glVDPAUIsSurfaceNV = pAPI->glVDPAUIsSurfaceNV; + glVDPAUUnregisterSurfaceNV = pAPI->glVDPAUUnregisterSurfaceNV; + glVDPAUGetSurfaceivNV = pAPI->glVDPAUGetSurfaceivNV; + glVDPAUSurfaceAccessNV = pAPI->glVDPAUSurfaceAccessNV; + glVDPAUMapSurfacesNV = pAPI->glVDPAUMapSurfacesNV; + glVDPAUUnmapSurfacesNV = pAPI->glVDPAUUnmapSurfacesNV; + glVDPAURegisterVideoSurfaceWithPictureStructureNV = pAPI->glVDPAURegisterVideoSurfaceWithPictureStructureNV; + glFlushVertexArrayRangeNV = pAPI->glFlushVertexArrayRangeNV; + glVertexArrayRangeNV = pAPI->glVertexArrayRangeNV; + glVertexAttribL1i64NV = pAPI->glVertexAttribL1i64NV; + glVertexAttribL2i64NV = pAPI->glVertexAttribL2i64NV; + glVertexAttribL3i64NV = pAPI->glVertexAttribL3i64NV; + glVertexAttribL4i64NV = pAPI->glVertexAttribL4i64NV; + glVertexAttribL1i64vNV = pAPI->glVertexAttribL1i64vNV; + glVertexAttribL2i64vNV = pAPI->glVertexAttribL2i64vNV; + glVertexAttribL3i64vNV = pAPI->glVertexAttribL3i64vNV; + glVertexAttribL4i64vNV = pAPI->glVertexAttribL4i64vNV; + glVertexAttribL1ui64NV = pAPI->glVertexAttribL1ui64NV; + glVertexAttribL2ui64NV = pAPI->glVertexAttribL2ui64NV; + glVertexAttribL3ui64NV = pAPI->glVertexAttribL3ui64NV; + glVertexAttribL4ui64NV = pAPI->glVertexAttribL4ui64NV; + glVertexAttribL1ui64vNV = pAPI->glVertexAttribL1ui64vNV; + glVertexAttribL2ui64vNV = pAPI->glVertexAttribL2ui64vNV; + glVertexAttribL3ui64vNV = pAPI->glVertexAttribL3ui64vNV; + glVertexAttribL4ui64vNV = pAPI->glVertexAttribL4ui64vNV; + glGetVertexAttribLi64vNV = pAPI->glGetVertexAttribLi64vNV; + glGetVertexAttribLui64vNV = pAPI->glGetVertexAttribLui64vNV; + glVertexAttribLFormatNV = pAPI->glVertexAttribLFormatNV; + glBufferAddressRangeNV = pAPI->glBufferAddressRangeNV; + glVertexFormatNV = pAPI->glVertexFormatNV; + glNormalFormatNV = pAPI->glNormalFormatNV; + glColorFormatNV = pAPI->glColorFormatNV; + glIndexFormatNV = pAPI->glIndexFormatNV; + glTexCoordFormatNV = pAPI->glTexCoordFormatNV; + glEdgeFlagFormatNV = pAPI->glEdgeFlagFormatNV; + glSecondaryColorFormatNV = pAPI->glSecondaryColorFormatNV; + glFogCoordFormatNV = pAPI->glFogCoordFormatNV; + glVertexAttribFormatNV = pAPI->glVertexAttribFormatNV; + glVertexAttribIFormatNV = pAPI->glVertexAttribIFormatNV; + glGetIntegerui64i_vNV = pAPI->glGetIntegerui64i_vNV; + glAreProgramsResidentNV = pAPI->glAreProgramsResidentNV; + glBindProgramNV = pAPI->glBindProgramNV; + glDeleteProgramsNV = pAPI->glDeleteProgramsNV; + glExecuteProgramNV = pAPI->glExecuteProgramNV; + glGenProgramsNV = pAPI->glGenProgramsNV; + glGetProgramParameterdvNV = pAPI->glGetProgramParameterdvNV; + glGetProgramParameterfvNV = pAPI->glGetProgramParameterfvNV; + glGetProgramivNV = pAPI->glGetProgramivNV; + glGetProgramStringNV = pAPI->glGetProgramStringNV; + glGetTrackMatrixivNV = pAPI->glGetTrackMatrixivNV; + glGetVertexAttribdvNV = pAPI->glGetVertexAttribdvNV; + glGetVertexAttribfvNV = pAPI->glGetVertexAttribfvNV; + glGetVertexAttribivNV = pAPI->glGetVertexAttribivNV; + glGetVertexAttribPointervNV = pAPI->glGetVertexAttribPointervNV; + glIsProgramNV = pAPI->glIsProgramNV; + glLoadProgramNV = pAPI->glLoadProgramNV; + glProgramParameter4dNV = pAPI->glProgramParameter4dNV; + glProgramParameter4dvNV = pAPI->glProgramParameter4dvNV; + glProgramParameter4fNV = pAPI->glProgramParameter4fNV; + glProgramParameter4fvNV = pAPI->glProgramParameter4fvNV; + glProgramParameters4dvNV = pAPI->glProgramParameters4dvNV; + glProgramParameters4fvNV = pAPI->glProgramParameters4fvNV; + glRequestResidentProgramsNV = pAPI->glRequestResidentProgramsNV; + glTrackMatrixNV = pAPI->glTrackMatrixNV; + glVertexAttribPointerNV = pAPI->glVertexAttribPointerNV; + glVertexAttrib1dNV = pAPI->glVertexAttrib1dNV; + glVertexAttrib1dvNV = pAPI->glVertexAttrib1dvNV; + glVertexAttrib1fNV = pAPI->glVertexAttrib1fNV; + glVertexAttrib1fvNV = pAPI->glVertexAttrib1fvNV; + glVertexAttrib1sNV = pAPI->glVertexAttrib1sNV; + glVertexAttrib1svNV = pAPI->glVertexAttrib1svNV; + glVertexAttrib2dNV = pAPI->glVertexAttrib2dNV; + glVertexAttrib2dvNV = pAPI->glVertexAttrib2dvNV; + glVertexAttrib2fNV = pAPI->glVertexAttrib2fNV; + glVertexAttrib2fvNV = pAPI->glVertexAttrib2fvNV; + glVertexAttrib2sNV = pAPI->glVertexAttrib2sNV; + glVertexAttrib2svNV = pAPI->glVertexAttrib2svNV; + glVertexAttrib3dNV = pAPI->glVertexAttrib3dNV; + glVertexAttrib3dvNV = pAPI->glVertexAttrib3dvNV; + glVertexAttrib3fNV = pAPI->glVertexAttrib3fNV; + glVertexAttrib3fvNV = pAPI->glVertexAttrib3fvNV; + glVertexAttrib3sNV = pAPI->glVertexAttrib3sNV; + glVertexAttrib3svNV = pAPI->glVertexAttrib3svNV; + glVertexAttrib4dNV = pAPI->glVertexAttrib4dNV; + glVertexAttrib4dvNV = pAPI->glVertexAttrib4dvNV; + glVertexAttrib4fNV = pAPI->glVertexAttrib4fNV; + glVertexAttrib4fvNV = pAPI->glVertexAttrib4fvNV; + glVertexAttrib4sNV = pAPI->glVertexAttrib4sNV; + glVertexAttrib4svNV = pAPI->glVertexAttrib4svNV; + glVertexAttrib4ubNV = pAPI->glVertexAttrib4ubNV; + glVertexAttrib4ubvNV = pAPI->glVertexAttrib4ubvNV; + glVertexAttribs1dvNV = pAPI->glVertexAttribs1dvNV; + glVertexAttribs1fvNV = pAPI->glVertexAttribs1fvNV; + glVertexAttribs1svNV = pAPI->glVertexAttribs1svNV; + glVertexAttribs2dvNV = pAPI->glVertexAttribs2dvNV; + glVertexAttribs2fvNV = pAPI->glVertexAttribs2fvNV; + glVertexAttribs2svNV = pAPI->glVertexAttribs2svNV; + glVertexAttribs3dvNV = pAPI->glVertexAttribs3dvNV; + glVertexAttribs3fvNV = pAPI->glVertexAttribs3fvNV; + glVertexAttribs3svNV = pAPI->glVertexAttribs3svNV; + glVertexAttribs4dvNV = pAPI->glVertexAttribs4dvNV; + glVertexAttribs4fvNV = pAPI->glVertexAttribs4fvNV; + glVertexAttribs4svNV = pAPI->glVertexAttribs4svNV; + glVertexAttribs4ubvNV = pAPI->glVertexAttribs4ubvNV; + glVertexAttribI1iEXT = pAPI->glVertexAttribI1iEXT; + glVertexAttribI2iEXT = pAPI->glVertexAttribI2iEXT; + glVertexAttribI3iEXT = pAPI->glVertexAttribI3iEXT; + glVertexAttribI4iEXT = pAPI->glVertexAttribI4iEXT; + glVertexAttribI1uiEXT = pAPI->glVertexAttribI1uiEXT; + glVertexAttribI2uiEXT = pAPI->glVertexAttribI2uiEXT; + glVertexAttribI3uiEXT = pAPI->glVertexAttribI3uiEXT; + glVertexAttribI4uiEXT = pAPI->glVertexAttribI4uiEXT; + glVertexAttribI1ivEXT = pAPI->glVertexAttribI1ivEXT; + glVertexAttribI2ivEXT = pAPI->glVertexAttribI2ivEXT; + glVertexAttribI3ivEXT = pAPI->glVertexAttribI3ivEXT; + glVertexAttribI4ivEXT = pAPI->glVertexAttribI4ivEXT; + glVertexAttribI1uivEXT = pAPI->glVertexAttribI1uivEXT; + glVertexAttribI2uivEXT = pAPI->glVertexAttribI2uivEXT; + glVertexAttribI3uivEXT = pAPI->glVertexAttribI3uivEXT; + glVertexAttribI4uivEXT = pAPI->glVertexAttribI4uivEXT; + glVertexAttribI4bvEXT = pAPI->glVertexAttribI4bvEXT; + glVertexAttribI4svEXT = pAPI->glVertexAttribI4svEXT; + glVertexAttribI4ubvEXT = pAPI->glVertexAttribI4ubvEXT; + glVertexAttribI4usvEXT = pAPI->glVertexAttribI4usvEXT; + glVertexAttribIPointerEXT = pAPI->glVertexAttribIPointerEXT; + glGetVertexAttribIivEXT = pAPI->glGetVertexAttribIivEXT; + glGetVertexAttribIuivEXT = pAPI->glGetVertexAttribIuivEXT; + glBeginVideoCaptureNV = pAPI->glBeginVideoCaptureNV; + glBindVideoCaptureStreamBufferNV = pAPI->glBindVideoCaptureStreamBufferNV; + glBindVideoCaptureStreamTextureNV = pAPI->glBindVideoCaptureStreamTextureNV; + glEndVideoCaptureNV = pAPI->glEndVideoCaptureNV; + glGetVideoCaptureivNV = pAPI->glGetVideoCaptureivNV; + glGetVideoCaptureStreamivNV = pAPI->glGetVideoCaptureStreamivNV; + glGetVideoCaptureStreamfvNV = pAPI->glGetVideoCaptureStreamfvNV; + glGetVideoCaptureStreamdvNV = pAPI->glGetVideoCaptureStreamdvNV; + glVideoCaptureNV = pAPI->glVideoCaptureNV; + glVideoCaptureStreamParameterivNV = pAPI->glVideoCaptureStreamParameterivNV; + glVideoCaptureStreamParameterfvNV = pAPI->glVideoCaptureStreamParameterfvNV; + glVideoCaptureStreamParameterdvNV = pAPI->glVideoCaptureStreamParameterdvNV; + glViewportSwizzleNV = pAPI->glViewportSwizzleNV; + glMultiTexCoord1bOES = pAPI->glMultiTexCoord1bOES; + glMultiTexCoord1bvOES = pAPI->glMultiTexCoord1bvOES; + glMultiTexCoord2bOES = pAPI->glMultiTexCoord2bOES; + glMultiTexCoord2bvOES = pAPI->glMultiTexCoord2bvOES; + glMultiTexCoord3bOES = pAPI->glMultiTexCoord3bOES; + glMultiTexCoord3bvOES = pAPI->glMultiTexCoord3bvOES; + glMultiTexCoord4bOES = pAPI->glMultiTexCoord4bOES; + glMultiTexCoord4bvOES = pAPI->glMultiTexCoord4bvOES; + glTexCoord1bOES = pAPI->glTexCoord1bOES; + glTexCoord1bvOES = pAPI->glTexCoord1bvOES; + glTexCoord2bOES = pAPI->glTexCoord2bOES; + glTexCoord2bvOES = pAPI->glTexCoord2bvOES; + glTexCoord3bOES = pAPI->glTexCoord3bOES; + glTexCoord3bvOES = pAPI->glTexCoord3bvOES; + glTexCoord4bOES = pAPI->glTexCoord4bOES; + glTexCoord4bvOES = pAPI->glTexCoord4bvOES; + glVertex2bOES = pAPI->glVertex2bOES; + glVertex2bvOES = pAPI->glVertex2bvOES; + glVertex3bOES = pAPI->glVertex3bOES; + glVertex3bvOES = pAPI->glVertex3bvOES; + glVertex4bOES = pAPI->glVertex4bOES; + glVertex4bvOES = pAPI->glVertex4bvOES; + glAlphaFuncxOES = pAPI->glAlphaFuncxOES; + glClearColorxOES = pAPI->glClearColorxOES; + glClearDepthxOES = pAPI->glClearDepthxOES; + glClipPlanexOES = pAPI->glClipPlanexOES; + glColor4xOES = pAPI->glColor4xOES; + glDepthRangexOES = pAPI->glDepthRangexOES; + glFogxOES = pAPI->glFogxOES; + glFogxvOES = pAPI->glFogxvOES; + glFrustumxOES = pAPI->glFrustumxOES; + glGetClipPlanexOES = pAPI->glGetClipPlanexOES; + glGetFixedvOES = pAPI->glGetFixedvOES; + glGetTexEnvxvOES = pAPI->glGetTexEnvxvOES; + glGetTexParameterxvOES = pAPI->glGetTexParameterxvOES; + glLightModelxOES = pAPI->glLightModelxOES; + glLightModelxvOES = pAPI->glLightModelxvOES; + glLightxOES = pAPI->glLightxOES; + glLightxvOES = pAPI->glLightxvOES; + glLineWidthxOES = pAPI->glLineWidthxOES; + glLoadMatrixxOES = pAPI->glLoadMatrixxOES; + glMaterialxOES = pAPI->glMaterialxOES; + glMaterialxvOES = pAPI->glMaterialxvOES; + glMultMatrixxOES = pAPI->glMultMatrixxOES; + glMultiTexCoord4xOES = pAPI->glMultiTexCoord4xOES; + glNormal3xOES = pAPI->glNormal3xOES; + glOrthoxOES = pAPI->glOrthoxOES; + glPointParameterxvOES = pAPI->glPointParameterxvOES; + glPointSizexOES = pAPI->glPointSizexOES; + glPolygonOffsetxOES = pAPI->glPolygonOffsetxOES; + glRotatexOES = pAPI->glRotatexOES; + glScalexOES = pAPI->glScalexOES; + glTexEnvxOES = pAPI->glTexEnvxOES; + glTexEnvxvOES = pAPI->glTexEnvxvOES; + glTexParameterxOES = pAPI->glTexParameterxOES; + glTexParameterxvOES = pAPI->glTexParameterxvOES; + glTranslatexOES = pAPI->glTranslatexOES; + glGetLightxvOES = pAPI->glGetLightxvOES; + glGetMaterialxvOES = pAPI->glGetMaterialxvOES; + glPointParameterxOES = pAPI->glPointParameterxOES; + glSampleCoveragexOES = pAPI->glSampleCoveragexOES; + glAccumxOES = pAPI->glAccumxOES; + glBitmapxOES = pAPI->glBitmapxOES; + glBlendColorxOES = pAPI->glBlendColorxOES; + glClearAccumxOES = pAPI->glClearAccumxOES; + glColor3xOES = pAPI->glColor3xOES; + glColor3xvOES = pAPI->glColor3xvOES; + glColor4xvOES = pAPI->glColor4xvOES; + glConvolutionParameterxOES = pAPI->glConvolutionParameterxOES; + glConvolutionParameterxvOES = pAPI->glConvolutionParameterxvOES; + glEvalCoord1xOES = pAPI->glEvalCoord1xOES; + glEvalCoord1xvOES = pAPI->glEvalCoord1xvOES; + glEvalCoord2xOES = pAPI->glEvalCoord2xOES; + glEvalCoord2xvOES = pAPI->glEvalCoord2xvOES; + glFeedbackBufferxOES = pAPI->glFeedbackBufferxOES; + glGetConvolutionParameterxvOES = pAPI->glGetConvolutionParameterxvOES; + glGetHistogramParameterxvOES = pAPI->glGetHistogramParameterxvOES; + glGetLightxOES = pAPI->glGetLightxOES; + glGetMapxvOES = pAPI->glGetMapxvOES; + glGetMaterialxOES = pAPI->glGetMaterialxOES; + glGetPixelMapxv = pAPI->glGetPixelMapxv; + glGetTexGenxvOES = pAPI->glGetTexGenxvOES; + glGetTexLevelParameterxvOES = pAPI->glGetTexLevelParameterxvOES; + glIndexxOES = pAPI->glIndexxOES; + glIndexxvOES = pAPI->glIndexxvOES; + glLoadTransposeMatrixxOES = pAPI->glLoadTransposeMatrixxOES; + glMap1xOES = pAPI->glMap1xOES; + glMap2xOES = pAPI->glMap2xOES; + glMapGrid1xOES = pAPI->glMapGrid1xOES; + glMapGrid2xOES = pAPI->glMapGrid2xOES; + glMultTransposeMatrixxOES = pAPI->glMultTransposeMatrixxOES; + glMultiTexCoord1xOES = pAPI->glMultiTexCoord1xOES; + glMultiTexCoord1xvOES = pAPI->glMultiTexCoord1xvOES; + glMultiTexCoord2xOES = pAPI->glMultiTexCoord2xOES; + glMultiTexCoord2xvOES = pAPI->glMultiTexCoord2xvOES; + glMultiTexCoord3xOES = pAPI->glMultiTexCoord3xOES; + glMultiTexCoord3xvOES = pAPI->glMultiTexCoord3xvOES; + glMultiTexCoord4xvOES = pAPI->glMultiTexCoord4xvOES; + glNormal3xvOES = pAPI->glNormal3xvOES; + glPassThroughxOES = pAPI->glPassThroughxOES; + glPixelMapx = pAPI->glPixelMapx; + glPixelStorex = pAPI->glPixelStorex; + glPixelTransferxOES = pAPI->glPixelTransferxOES; + glPixelZoomxOES = pAPI->glPixelZoomxOES; + glPrioritizeTexturesxOES = pAPI->glPrioritizeTexturesxOES; + glRasterPos2xOES = pAPI->glRasterPos2xOES; + glRasterPos2xvOES = pAPI->glRasterPos2xvOES; + glRasterPos3xOES = pAPI->glRasterPos3xOES; + glRasterPos3xvOES = pAPI->glRasterPos3xvOES; + glRasterPos4xOES = pAPI->glRasterPos4xOES; + glRasterPos4xvOES = pAPI->glRasterPos4xvOES; + glRectxOES = pAPI->glRectxOES; + glRectxvOES = pAPI->glRectxvOES; + glTexCoord1xOES = pAPI->glTexCoord1xOES; + glTexCoord1xvOES = pAPI->glTexCoord1xvOES; + glTexCoord2xOES = pAPI->glTexCoord2xOES; + glTexCoord2xvOES = pAPI->glTexCoord2xvOES; + glTexCoord3xOES = pAPI->glTexCoord3xOES; + glTexCoord3xvOES = pAPI->glTexCoord3xvOES; + glTexCoord4xOES = pAPI->glTexCoord4xOES; + glTexCoord4xvOES = pAPI->glTexCoord4xvOES; + glTexGenxOES = pAPI->glTexGenxOES; + glTexGenxvOES = pAPI->glTexGenxvOES; + glVertex2xOES = pAPI->glVertex2xOES; + glVertex2xvOES = pAPI->glVertex2xvOES; + glVertex3xOES = pAPI->glVertex3xOES; + glVertex3xvOES = pAPI->glVertex3xvOES; + glVertex4xOES = pAPI->glVertex4xOES; + glVertex4xvOES = pAPI->glVertex4xvOES; + glQueryMatrixxOES = pAPI->glQueryMatrixxOES; + glClearDepthfOES = pAPI->glClearDepthfOES; + glClipPlanefOES = pAPI->glClipPlanefOES; + glDepthRangefOES = pAPI->glDepthRangefOES; + glFrustumfOES = pAPI->glFrustumfOES; + glGetClipPlanefOES = pAPI->glGetClipPlanefOES; + glOrthofOES = pAPI->glOrthofOES; + glFramebufferTextureMultiviewOVR = pAPI->glFramebufferTextureMultiviewOVR; + glHintPGI = pAPI->glHintPGI; + glDetailTexFuncSGIS = pAPI->glDetailTexFuncSGIS; + glGetDetailTexFuncSGIS = pAPI->glGetDetailTexFuncSGIS; + glFogFuncSGIS = pAPI->glFogFuncSGIS; + glGetFogFuncSGIS = pAPI->glGetFogFuncSGIS; + glSampleMaskSGIS = pAPI->glSampleMaskSGIS; + glSamplePatternSGIS = pAPI->glSamplePatternSGIS; + glPixelTexGenParameteriSGIS = pAPI->glPixelTexGenParameteriSGIS; + glPixelTexGenParameterivSGIS = pAPI->glPixelTexGenParameterivSGIS; + glPixelTexGenParameterfSGIS = pAPI->glPixelTexGenParameterfSGIS; + glPixelTexGenParameterfvSGIS = pAPI->glPixelTexGenParameterfvSGIS; + glGetPixelTexGenParameterivSGIS = pAPI->glGetPixelTexGenParameterivSGIS; + glGetPixelTexGenParameterfvSGIS = pAPI->glGetPixelTexGenParameterfvSGIS; + glPointParameterfSGIS = pAPI->glPointParameterfSGIS; + glPointParameterfvSGIS = pAPI->glPointParameterfvSGIS; + glSharpenTexFuncSGIS = pAPI->glSharpenTexFuncSGIS; + glGetSharpenTexFuncSGIS = pAPI->glGetSharpenTexFuncSGIS; + glTexImage4DSGIS = pAPI->glTexImage4DSGIS; + glTexSubImage4DSGIS = pAPI->glTexSubImage4DSGIS; + glTextureColorMaskSGIS = pAPI->glTextureColorMaskSGIS; + glGetTexFilterFuncSGIS = pAPI->glGetTexFilterFuncSGIS; + glTexFilterFuncSGIS = pAPI->glTexFilterFuncSGIS; + glAsyncMarkerSGIX = pAPI->glAsyncMarkerSGIX; + glFinishAsyncSGIX = pAPI->glFinishAsyncSGIX; + glPollAsyncSGIX = pAPI->glPollAsyncSGIX; + glGenAsyncMarkersSGIX = pAPI->glGenAsyncMarkersSGIX; + glDeleteAsyncMarkersSGIX = pAPI->glDeleteAsyncMarkersSGIX; + glIsAsyncMarkerSGIX = pAPI->glIsAsyncMarkerSGIX; + glFlushRasterSGIX = pAPI->glFlushRasterSGIX; + glFragmentColorMaterialSGIX = pAPI->glFragmentColorMaterialSGIX; + glFragmentLightfSGIX = pAPI->glFragmentLightfSGIX; + glFragmentLightfvSGIX = pAPI->glFragmentLightfvSGIX; + glFragmentLightiSGIX = pAPI->glFragmentLightiSGIX; + glFragmentLightivSGIX = pAPI->glFragmentLightivSGIX; + glFragmentLightModelfSGIX = pAPI->glFragmentLightModelfSGIX; + glFragmentLightModelfvSGIX = pAPI->glFragmentLightModelfvSGIX; + glFragmentLightModeliSGIX = pAPI->glFragmentLightModeliSGIX; + glFragmentLightModelivSGIX = pAPI->glFragmentLightModelivSGIX; + glFragmentMaterialfSGIX = pAPI->glFragmentMaterialfSGIX; + glFragmentMaterialfvSGIX = pAPI->glFragmentMaterialfvSGIX; + glFragmentMaterialiSGIX = pAPI->glFragmentMaterialiSGIX; + glFragmentMaterialivSGIX = pAPI->glFragmentMaterialivSGIX; + glGetFragmentLightfvSGIX = pAPI->glGetFragmentLightfvSGIX; + glGetFragmentLightivSGIX = pAPI->glGetFragmentLightivSGIX; + glGetFragmentMaterialfvSGIX = pAPI->glGetFragmentMaterialfvSGIX; + glGetFragmentMaterialivSGIX = pAPI->glGetFragmentMaterialivSGIX; + glLightEnviSGIX = pAPI->glLightEnviSGIX; + glFrameZoomSGIX = pAPI->glFrameZoomSGIX; + glIglooInterfaceSGIX = pAPI->glIglooInterfaceSGIX; + glGetInstrumentsSGIX = pAPI->glGetInstrumentsSGIX; + glInstrumentsBufferSGIX = pAPI->glInstrumentsBufferSGIX; + glPollInstrumentsSGIX = pAPI->glPollInstrumentsSGIX; + glReadInstrumentsSGIX = pAPI->glReadInstrumentsSGIX; + glStartInstrumentsSGIX = pAPI->glStartInstrumentsSGIX; + glStopInstrumentsSGIX = pAPI->glStopInstrumentsSGIX; + glGetListParameterfvSGIX = pAPI->glGetListParameterfvSGIX; + glGetListParameterivSGIX = pAPI->glGetListParameterivSGIX; + glListParameterfSGIX = pAPI->glListParameterfSGIX; + glListParameterfvSGIX = pAPI->glListParameterfvSGIX; + glListParameteriSGIX = pAPI->glListParameteriSGIX; + glListParameterivSGIX = pAPI->glListParameterivSGIX; + glPixelTexGenSGIX = pAPI->glPixelTexGenSGIX; + glDeformationMap3dSGIX = pAPI->glDeformationMap3dSGIX; + glDeformationMap3fSGIX = pAPI->glDeformationMap3fSGIX; + glDeformSGIX = pAPI->glDeformSGIX; + glLoadIdentityDeformationMapSGIX = pAPI->glLoadIdentityDeformationMapSGIX; + glReferencePlaneSGIX = pAPI->glReferencePlaneSGIX; + glSpriteParameterfSGIX = pAPI->glSpriteParameterfSGIX; + glSpriteParameterfvSGIX = pAPI->glSpriteParameterfvSGIX; + glSpriteParameteriSGIX = pAPI->glSpriteParameteriSGIX; + glSpriteParameterivSGIX = pAPI->glSpriteParameterivSGIX; + glTagSampleBufferSGIX = pAPI->glTagSampleBufferSGIX; + glColorTableSGI = pAPI->glColorTableSGI; + glColorTableParameterfvSGI = pAPI->glColorTableParameterfvSGI; + glColorTableParameterivSGI = pAPI->glColorTableParameterivSGI; + glCopyColorTableSGI = pAPI->glCopyColorTableSGI; + glGetColorTableSGI = pAPI->glGetColorTableSGI; + glGetColorTableParameterfvSGI = pAPI->glGetColorTableParameterfvSGI; + glGetColorTableParameterivSGI = pAPI->glGetColorTableParameterivSGI; + glFinishTextureSUNX = pAPI->glFinishTextureSUNX; + glGlobalAlphaFactorbSUN = pAPI->glGlobalAlphaFactorbSUN; + glGlobalAlphaFactorsSUN = pAPI->glGlobalAlphaFactorsSUN; + glGlobalAlphaFactoriSUN = pAPI->glGlobalAlphaFactoriSUN; + glGlobalAlphaFactorfSUN = pAPI->glGlobalAlphaFactorfSUN; + glGlobalAlphaFactordSUN = pAPI->glGlobalAlphaFactordSUN; + glGlobalAlphaFactorubSUN = pAPI->glGlobalAlphaFactorubSUN; + glGlobalAlphaFactorusSUN = pAPI->glGlobalAlphaFactorusSUN; + glGlobalAlphaFactoruiSUN = pAPI->glGlobalAlphaFactoruiSUN; + glDrawMeshArraysSUN = pAPI->glDrawMeshArraysSUN; + glReplacementCodeuiSUN = pAPI->glReplacementCodeuiSUN; + glReplacementCodeusSUN = pAPI->glReplacementCodeusSUN; + glReplacementCodeubSUN = pAPI->glReplacementCodeubSUN; + glReplacementCodeuivSUN = pAPI->glReplacementCodeuivSUN; + glReplacementCodeusvSUN = pAPI->glReplacementCodeusvSUN; + glReplacementCodeubvSUN = pAPI->glReplacementCodeubvSUN; + glReplacementCodePointerSUN = pAPI->glReplacementCodePointerSUN; + glColor4ubVertex2fSUN = pAPI->glColor4ubVertex2fSUN; + glColor4ubVertex2fvSUN = pAPI->glColor4ubVertex2fvSUN; + glColor4ubVertex3fSUN = pAPI->glColor4ubVertex3fSUN; + glColor4ubVertex3fvSUN = pAPI->glColor4ubVertex3fvSUN; + glColor3fVertex3fSUN = pAPI->glColor3fVertex3fSUN; + glColor3fVertex3fvSUN = pAPI->glColor3fVertex3fvSUN; + glNormal3fVertex3fSUN = pAPI->glNormal3fVertex3fSUN; + glNormal3fVertex3fvSUN = pAPI->glNormal3fVertex3fvSUN; + glColor4fNormal3fVertex3fSUN = pAPI->glColor4fNormal3fVertex3fSUN; + glColor4fNormal3fVertex3fvSUN = pAPI->glColor4fNormal3fVertex3fvSUN; + glTexCoord2fVertex3fSUN = pAPI->glTexCoord2fVertex3fSUN; + glTexCoord2fVertex3fvSUN = pAPI->glTexCoord2fVertex3fvSUN; + glTexCoord4fVertex4fSUN = pAPI->glTexCoord4fVertex4fSUN; + glTexCoord4fVertex4fvSUN = pAPI->glTexCoord4fVertex4fvSUN; + glTexCoord2fColor4ubVertex3fSUN = pAPI->glTexCoord2fColor4ubVertex3fSUN; + glTexCoord2fColor4ubVertex3fvSUN = pAPI->glTexCoord2fColor4ubVertex3fvSUN; + glTexCoord2fColor3fVertex3fSUN = pAPI->glTexCoord2fColor3fVertex3fSUN; + glTexCoord2fColor3fVertex3fvSUN = pAPI->glTexCoord2fColor3fVertex3fvSUN; + glTexCoord2fNormal3fVertex3fSUN = pAPI->glTexCoord2fNormal3fVertex3fSUN; + glTexCoord2fNormal3fVertex3fvSUN = pAPI->glTexCoord2fNormal3fVertex3fvSUN; + glTexCoord2fColor4fNormal3fVertex3fSUN = pAPI->glTexCoord2fColor4fNormal3fVertex3fSUN; + glTexCoord2fColor4fNormal3fVertex3fvSUN = pAPI->glTexCoord2fColor4fNormal3fVertex3fvSUN; + glTexCoord4fColor4fNormal3fVertex4fSUN = pAPI->glTexCoord4fColor4fNormal3fVertex4fSUN; + glTexCoord4fColor4fNormal3fVertex4fvSUN = pAPI->glTexCoord4fColor4fNormal3fVertex4fvSUN; + glReplacementCodeuiVertex3fSUN = pAPI->glReplacementCodeuiVertex3fSUN; + glReplacementCodeuiVertex3fvSUN = pAPI->glReplacementCodeuiVertex3fvSUN; + glReplacementCodeuiColor4ubVertex3fSUN = pAPI->glReplacementCodeuiColor4ubVertex3fSUN; + glReplacementCodeuiColor4ubVertex3fvSUN = pAPI->glReplacementCodeuiColor4ubVertex3fvSUN; + glReplacementCodeuiColor3fVertex3fSUN = pAPI->glReplacementCodeuiColor3fVertex3fSUN; + glReplacementCodeuiColor3fVertex3fvSUN = pAPI->glReplacementCodeuiColor3fVertex3fvSUN; + glReplacementCodeuiNormal3fVertex3fSUN = pAPI->glReplacementCodeuiNormal3fVertex3fSUN; + glReplacementCodeuiNormal3fVertex3fvSUN = pAPI->glReplacementCodeuiNormal3fVertex3fvSUN; + glReplacementCodeuiColor4fNormal3fVertex3fSUN = pAPI->glReplacementCodeuiColor4fNormal3fVertex3fSUN; + glReplacementCodeuiColor4fNormal3fVertex3fvSUN = pAPI->glReplacementCodeuiColor4fNormal3fVertex3fvSUN; + glReplacementCodeuiTexCoord2fVertex3fSUN = pAPI->glReplacementCodeuiTexCoord2fVertex3fSUN; + glReplacementCodeuiTexCoord2fVertex3fvSUN = pAPI->glReplacementCodeuiTexCoord2fVertex3fvSUN; + glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN = pAPI->glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN; + glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN = pAPI->glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN; + glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN = pAPI->glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN; + glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN = pAPI->glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN; +#if defined(GLBIND_WGL) + wglSetStereoEmitterState3DL = pAPI->wglSetStereoEmitterState3DL; + wglGetGPUIDsAMD = pAPI->wglGetGPUIDsAMD; + wglGetGPUInfoAMD = pAPI->wglGetGPUInfoAMD; + wglGetContextGPUIDAMD = pAPI->wglGetContextGPUIDAMD; + wglCreateAssociatedContextAMD = pAPI->wglCreateAssociatedContextAMD; + wglCreateAssociatedContextAttribsAMD = pAPI->wglCreateAssociatedContextAttribsAMD; + wglDeleteAssociatedContextAMD = pAPI->wglDeleteAssociatedContextAMD; + wglMakeAssociatedContextCurrentAMD = pAPI->wglMakeAssociatedContextCurrentAMD; + wglGetCurrentAssociatedContextAMD = pAPI->wglGetCurrentAssociatedContextAMD; + wglBlitContextFramebufferAMD = pAPI->wglBlitContextFramebufferAMD; + wglCreateBufferRegionARB = pAPI->wglCreateBufferRegionARB; + wglDeleteBufferRegionARB = pAPI->wglDeleteBufferRegionARB; + wglSaveBufferRegionARB = pAPI->wglSaveBufferRegionARB; + wglRestoreBufferRegionARB = pAPI->wglRestoreBufferRegionARB; + wglCreateContextAttribsARB = pAPI->wglCreateContextAttribsARB; + wglGetExtensionsStringARB = pAPI->wglGetExtensionsStringARB; + wglMakeContextCurrentARB = pAPI->wglMakeContextCurrentARB; + wglGetCurrentReadDCARB = pAPI->wglGetCurrentReadDCARB; + wglCreatePbufferARB = pAPI->wglCreatePbufferARB; + wglGetPbufferDCARB = pAPI->wglGetPbufferDCARB; + wglReleasePbufferDCARB = pAPI->wglReleasePbufferDCARB; + wglDestroyPbufferARB = pAPI->wglDestroyPbufferARB; + wglQueryPbufferARB = pAPI->wglQueryPbufferARB; + wglGetPixelFormatAttribivARB = pAPI->wglGetPixelFormatAttribivARB; + wglGetPixelFormatAttribfvARB = pAPI->wglGetPixelFormatAttribfvARB; + wglChoosePixelFormatARB = pAPI->wglChoosePixelFormatARB; + wglBindTexImageARB = pAPI->wglBindTexImageARB; + wglReleaseTexImageARB = pAPI->wglReleaseTexImageARB; + wglSetPbufferAttribARB = pAPI->wglSetPbufferAttribARB; + wglCreateDisplayColorTableEXT = pAPI->wglCreateDisplayColorTableEXT; + wglLoadDisplayColorTableEXT = pAPI->wglLoadDisplayColorTableEXT; + wglBindDisplayColorTableEXT = pAPI->wglBindDisplayColorTableEXT; + wglDestroyDisplayColorTableEXT = pAPI->wglDestroyDisplayColorTableEXT; + wglGetExtensionsStringEXT = pAPI->wglGetExtensionsStringEXT; + wglMakeContextCurrentEXT = pAPI->wglMakeContextCurrentEXT; + wglGetCurrentReadDCEXT = pAPI->wglGetCurrentReadDCEXT; + wglCreatePbufferEXT = pAPI->wglCreatePbufferEXT; + wglGetPbufferDCEXT = pAPI->wglGetPbufferDCEXT; + wglReleasePbufferDCEXT = pAPI->wglReleasePbufferDCEXT; + wglDestroyPbufferEXT = pAPI->wglDestroyPbufferEXT; + wglQueryPbufferEXT = pAPI->wglQueryPbufferEXT; + wglGetPixelFormatAttribivEXT = pAPI->wglGetPixelFormatAttribivEXT; + wglGetPixelFormatAttribfvEXT = pAPI->wglGetPixelFormatAttribfvEXT; + wglChoosePixelFormatEXT = pAPI->wglChoosePixelFormatEXT; + wglSwapIntervalEXT = pAPI->wglSwapIntervalEXT; + wglGetSwapIntervalEXT = pAPI->wglGetSwapIntervalEXT; + wglGetDigitalVideoParametersI3D = pAPI->wglGetDigitalVideoParametersI3D; + wglSetDigitalVideoParametersI3D = pAPI->wglSetDigitalVideoParametersI3D; + wglGetGammaTableParametersI3D = pAPI->wglGetGammaTableParametersI3D; + wglSetGammaTableParametersI3D = pAPI->wglSetGammaTableParametersI3D; + wglGetGammaTableI3D = pAPI->wglGetGammaTableI3D; + wglSetGammaTableI3D = pAPI->wglSetGammaTableI3D; + wglEnableGenlockI3D = pAPI->wglEnableGenlockI3D; + wglDisableGenlockI3D = pAPI->wglDisableGenlockI3D; + wglIsEnabledGenlockI3D = pAPI->wglIsEnabledGenlockI3D; + wglGenlockSourceI3D = pAPI->wglGenlockSourceI3D; + wglGetGenlockSourceI3D = pAPI->wglGetGenlockSourceI3D; + wglGenlockSourceEdgeI3D = pAPI->wglGenlockSourceEdgeI3D; + wglGetGenlockSourceEdgeI3D = pAPI->wglGetGenlockSourceEdgeI3D; + wglGenlockSampleRateI3D = pAPI->wglGenlockSampleRateI3D; + wglGetGenlockSampleRateI3D = pAPI->wglGetGenlockSampleRateI3D; + wglGenlockSourceDelayI3D = pAPI->wglGenlockSourceDelayI3D; + wglGetGenlockSourceDelayI3D = pAPI->wglGetGenlockSourceDelayI3D; + wglQueryGenlockMaxSourceDelayI3D = pAPI->wglQueryGenlockMaxSourceDelayI3D; + wglCreateImageBufferI3D = pAPI->wglCreateImageBufferI3D; + wglDestroyImageBufferI3D = pAPI->wglDestroyImageBufferI3D; + wglAssociateImageBufferEventsI3D = pAPI->wglAssociateImageBufferEventsI3D; + wglReleaseImageBufferEventsI3D = pAPI->wglReleaseImageBufferEventsI3D; + wglEnableFrameLockI3D = pAPI->wglEnableFrameLockI3D; + wglDisableFrameLockI3D = pAPI->wglDisableFrameLockI3D; + wglIsEnabledFrameLockI3D = pAPI->wglIsEnabledFrameLockI3D; + wglQueryFrameLockMasterI3D = pAPI->wglQueryFrameLockMasterI3D; + wglGetFrameUsageI3D = pAPI->wglGetFrameUsageI3D; + wglBeginFrameTrackingI3D = pAPI->wglBeginFrameTrackingI3D; + wglEndFrameTrackingI3D = pAPI->wglEndFrameTrackingI3D; + wglQueryFrameTrackingI3D = pAPI->wglQueryFrameTrackingI3D; + wglCopyImageSubDataNV = pAPI->wglCopyImageSubDataNV; + wglDelayBeforeSwapNV = pAPI->wglDelayBeforeSwapNV; + wglDXSetResourceShareHandleNV = pAPI->wglDXSetResourceShareHandleNV; + wglDXOpenDeviceNV = pAPI->wglDXOpenDeviceNV; + wglDXCloseDeviceNV = pAPI->wglDXCloseDeviceNV; + wglDXRegisterObjectNV = pAPI->wglDXRegisterObjectNV; + wglDXUnregisterObjectNV = pAPI->wglDXUnregisterObjectNV; + wglDXObjectAccessNV = pAPI->wglDXObjectAccessNV; + wglDXLockObjectsNV = pAPI->wglDXLockObjectsNV; + wglDXUnlockObjectsNV = pAPI->wglDXUnlockObjectsNV; + wglEnumGpusNV = pAPI->wglEnumGpusNV; + wglEnumGpuDevicesNV = pAPI->wglEnumGpuDevicesNV; + wglCreateAffinityDCNV = pAPI->wglCreateAffinityDCNV; + wglEnumGpusFromAffinityDCNV = pAPI->wglEnumGpusFromAffinityDCNV; + wglDeleteDCNV = pAPI->wglDeleteDCNV; + wglEnumerateVideoDevicesNV = pAPI->wglEnumerateVideoDevicesNV; + wglBindVideoDeviceNV = pAPI->wglBindVideoDeviceNV; + wglQueryCurrentContextNV = pAPI->wglQueryCurrentContextNV; + wglJoinSwapGroupNV = pAPI->wglJoinSwapGroupNV; + wglBindSwapBarrierNV = pAPI->wglBindSwapBarrierNV; + wglQuerySwapGroupNV = pAPI->wglQuerySwapGroupNV; + wglQueryMaxSwapGroupsNV = pAPI->wglQueryMaxSwapGroupsNV; + wglQueryFrameCountNV = pAPI->wglQueryFrameCountNV; + wglResetFrameCountNV = pAPI->wglResetFrameCountNV; + wglBindVideoCaptureDeviceNV = pAPI->wglBindVideoCaptureDeviceNV; + wglEnumerateVideoCaptureDevicesNV = pAPI->wglEnumerateVideoCaptureDevicesNV; + wglLockVideoCaptureDeviceNV = pAPI->wglLockVideoCaptureDeviceNV; + wglQueryVideoCaptureDeviceNV = pAPI->wglQueryVideoCaptureDeviceNV; + wglReleaseVideoCaptureDeviceNV = pAPI->wglReleaseVideoCaptureDeviceNV; + wglGetVideoDeviceNV = pAPI->wglGetVideoDeviceNV; + wglReleaseVideoDeviceNV = pAPI->wglReleaseVideoDeviceNV; + wglBindVideoImageNV = pAPI->wglBindVideoImageNV; + wglReleaseVideoImageNV = pAPI->wglReleaseVideoImageNV; + wglSendPbufferToVideoNV = pAPI->wglSendPbufferToVideoNV; + wglGetVideoInfoNV = pAPI->wglGetVideoInfoNV; + wglAllocateMemoryNV = pAPI->wglAllocateMemoryNV; + wglFreeMemoryNV = pAPI->wglFreeMemoryNV; + wglGetSyncValuesOML = pAPI->wglGetSyncValuesOML; + wglGetMscRateOML = pAPI->wglGetMscRateOML; + wglSwapBuffersMscOML = pAPI->wglSwapBuffersMscOML; + wglSwapLayerBuffersMscOML = pAPI->wglSwapLayerBuffersMscOML; + wglWaitForMscOML = pAPI->wglWaitForMscOML; + wglWaitForSbcOML = pAPI->wglWaitForSbcOML; +#endif /* GLBIND_WGL */ +#if defined(GLBIND_GLX) + glXGetGPUIDsAMD = pAPI->glXGetGPUIDsAMD; + glXGetGPUInfoAMD = pAPI->glXGetGPUInfoAMD; + glXGetContextGPUIDAMD = pAPI->glXGetContextGPUIDAMD; + glXCreateAssociatedContextAMD = pAPI->glXCreateAssociatedContextAMD; + glXCreateAssociatedContextAttribsAMD = pAPI->glXCreateAssociatedContextAttribsAMD; + glXDeleteAssociatedContextAMD = pAPI->glXDeleteAssociatedContextAMD; + glXMakeAssociatedContextCurrentAMD = pAPI->glXMakeAssociatedContextCurrentAMD; + glXGetCurrentAssociatedContextAMD = pAPI->glXGetCurrentAssociatedContextAMD; + glXBlitContextFramebufferAMD = pAPI->glXBlitContextFramebufferAMD; + glXCreateContextAttribsARB = pAPI->glXCreateContextAttribsARB; + glXGetProcAddressARB = pAPI->glXGetProcAddressARB; + glXGetCurrentDisplayEXT = pAPI->glXGetCurrentDisplayEXT; + glXQueryContextInfoEXT = pAPI->glXQueryContextInfoEXT; + glXGetContextIDEXT = pAPI->glXGetContextIDEXT; + glXImportContextEXT = pAPI->glXImportContextEXT; + glXFreeContextEXT = pAPI->glXFreeContextEXT; + glXSwapIntervalEXT = pAPI->glXSwapIntervalEXT; + glXBindTexImageEXT = pAPI->glXBindTexImageEXT; + glXReleaseTexImageEXT = pAPI->glXReleaseTexImageEXT; + glXGetAGPOffsetMESA = pAPI->glXGetAGPOffsetMESA; + glXCopySubBufferMESA = pAPI->glXCopySubBufferMESA; + glXCreateGLXPixmapMESA = pAPI->glXCreateGLXPixmapMESA; + glXQueryCurrentRendererIntegerMESA = pAPI->glXQueryCurrentRendererIntegerMESA; + glXQueryCurrentRendererStringMESA = pAPI->glXQueryCurrentRendererStringMESA; + glXQueryRendererIntegerMESA = pAPI->glXQueryRendererIntegerMESA; + glXQueryRendererStringMESA = pAPI->glXQueryRendererStringMESA; + glXReleaseBuffersMESA = pAPI->glXReleaseBuffersMESA; + glXSet3DfxModeMESA = pAPI->glXSet3DfxModeMESA; + glXGetSwapIntervalMESA = pAPI->glXGetSwapIntervalMESA; + glXSwapIntervalMESA = pAPI->glXSwapIntervalMESA; + glXCopyBufferSubDataNV = pAPI->glXCopyBufferSubDataNV; + glXNamedCopyBufferSubDataNV = pAPI->glXNamedCopyBufferSubDataNV; + glXCopyImageSubDataNV = pAPI->glXCopyImageSubDataNV; + glXDelayBeforeSwapNV = pAPI->glXDelayBeforeSwapNV; + glXEnumerateVideoDevicesNV = pAPI->glXEnumerateVideoDevicesNV; + glXBindVideoDeviceNV = pAPI->glXBindVideoDeviceNV; + glXJoinSwapGroupNV = pAPI->glXJoinSwapGroupNV; + glXBindSwapBarrierNV = pAPI->glXBindSwapBarrierNV; + glXQuerySwapGroupNV = pAPI->glXQuerySwapGroupNV; + glXQueryMaxSwapGroupsNV = pAPI->glXQueryMaxSwapGroupsNV; + glXQueryFrameCountNV = pAPI->glXQueryFrameCountNV; + glXResetFrameCountNV = pAPI->glXResetFrameCountNV; + glXBindVideoCaptureDeviceNV = pAPI->glXBindVideoCaptureDeviceNV; + glXEnumerateVideoCaptureDevicesNV = pAPI->glXEnumerateVideoCaptureDevicesNV; + glXLockVideoCaptureDeviceNV = pAPI->glXLockVideoCaptureDeviceNV; + glXQueryVideoCaptureDeviceNV = pAPI->glXQueryVideoCaptureDeviceNV; + glXReleaseVideoCaptureDeviceNV = pAPI->glXReleaseVideoCaptureDeviceNV; + glXGetVideoDeviceNV = pAPI->glXGetVideoDeviceNV; + glXReleaseVideoDeviceNV = pAPI->glXReleaseVideoDeviceNV; + glXBindVideoImageNV = pAPI->glXBindVideoImageNV; + glXReleaseVideoImageNV = pAPI->glXReleaseVideoImageNV; + glXSendPbufferToVideoNV = pAPI->glXSendPbufferToVideoNV; + glXGetVideoInfoNV = pAPI->glXGetVideoInfoNV; + glXGetSyncValuesOML = pAPI->glXGetSyncValuesOML; + glXGetMscRateOML = pAPI->glXGetMscRateOML; + glXSwapBuffersMscOML = pAPI->glXSwapBuffersMscOML; + glXWaitForMscOML = pAPI->glXWaitForMscOML; + glXWaitForSbcOML = pAPI->glXWaitForSbcOML; + glXCushionSGI = pAPI->glXCushionSGI; + glXMakeCurrentReadSGI = pAPI->glXMakeCurrentReadSGI; + glXGetCurrentReadDrawableSGI = pAPI->glXGetCurrentReadDrawableSGI; + glXSwapIntervalSGI = pAPI->glXSwapIntervalSGI; + glXGetVideoSyncSGI = pAPI->glXGetVideoSyncSGI; + glXWaitVideoSyncSGI = pAPI->glXWaitVideoSyncSGI; + glXGetFBConfigAttribSGIX = pAPI->glXGetFBConfigAttribSGIX; + glXChooseFBConfigSGIX = pAPI->glXChooseFBConfigSGIX; + glXCreateGLXPixmapWithConfigSGIX = pAPI->glXCreateGLXPixmapWithConfigSGIX; + glXCreateContextWithConfigSGIX = pAPI->glXCreateContextWithConfigSGIX; + glXGetVisualFromFBConfigSGIX = pAPI->glXGetVisualFromFBConfigSGIX; + glXGetFBConfigFromVisualSGIX = pAPI->glXGetFBConfigFromVisualSGIX; + glXQueryHyperpipeNetworkSGIX = pAPI->glXQueryHyperpipeNetworkSGIX; + glXHyperpipeConfigSGIX = pAPI->glXHyperpipeConfigSGIX; + glXQueryHyperpipeConfigSGIX = pAPI->glXQueryHyperpipeConfigSGIX; + glXDestroyHyperpipeConfigSGIX = pAPI->glXDestroyHyperpipeConfigSGIX; + glXBindHyperpipeSGIX = pAPI->glXBindHyperpipeSGIX; + glXQueryHyperpipeBestAttribSGIX = pAPI->glXQueryHyperpipeBestAttribSGIX; + glXHyperpipeAttribSGIX = pAPI->glXHyperpipeAttribSGIX; + glXQueryHyperpipeAttribSGIX = pAPI->glXQueryHyperpipeAttribSGIX; + glXCreateGLXPbufferSGIX = pAPI->glXCreateGLXPbufferSGIX; + glXDestroyGLXPbufferSGIX = pAPI->glXDestroyGLXPbufferSGIX; + glXQueryGLXPbufferSGIX = pAPI->glXQueryGLXPbufferSGIX; + glXSelectEventSGIX = pAPI->glXSelectEventSGIX; + glXGetSelectedEventSGIX = pAPI->glXGetSelectedEventSGIX; + glXBindSwapBarrierSGIX = pAPI->glXBindSwapBarrierSGIX; + glXQueryMaxSwapBarriersSGIX = pAPI->glXQueryMaxSwapBarriersSGIX; + glXJoinSwapGroupSGIX = pAPI->glXJoinSwapGroupSGIX; + glXBindChannelToWindowSGIX = pAPI->glXBindChannelToWindowSGIX; + glXChannelRectSGIX = pAPI->glXChannelRectSGIX; + glXQueryChannelRectSGIX = pAPI->glXQueryChannelRectSGIX; + glXQueryChannelDeltasSGIX = pAPI->glXQueryChannelDeltasSGIX; + glXChannelRectSyncSGIX = pAPI->glXChannelRectSyncSGIX; + glXGetTransparentIndexSUN = pAPI->glXGetTransparentIndexSUN; +#endif /* GLBIND_GLX */ + + return GL_NO_ERROR; +} + +#if defined(GLBIND_WGL) +HGLRC glbGetRC() +{ + return glbind_RC; +} + +HDC glbGetDC() +{ + return glbind_DC; +} + +int glbGetPixelFormat() +{ + return glbind_PixelFormat; +} + +PIXELFORMATDESCRIPTOR* glbGetPFD() +{ + return &glbind_PFD; +} +#endif + +#if defined(GLBIND_GLX) +glbind_Display* glbGetDisplay() +{ + return glbind_pDisplay; +} + +GLXContext glbGetRC() +{ + return glbind_RC; +} + +glbind_Colormap glbGetColormap() +{ + return glbind_DummyColormap; +} + +glbind_XVisualInfo* glbGetFBVisualInfo() +{ + return glbind_pFBVisualInfo; +} +#endif + + +int glb_strcmp(const char* s1, const char* s2) +{ + while ((*s1) && (*s1 == *s2)) { + ++s1; + ++s2; + } + + return (*(unsigned char*)s1 - *(unsigned char*)s2); +} + +int glb_strncmp(const char* s1, const char* s2, size_t n) +{ + while (n && *s1 && (*s1 == *s2)) { + ++s1; + ++s2; + --n; + } + + if (n == 0) { + return 0; + } else { + return (*(unsigned char*)s1 - *(unsigned char*)s2); + } +} + +GLboolean glbIsExtensionInString(const char* ext, const char* str) +{ + const char* ext2beg; + const char* ext2end; + + if (ext == NULL || str == NULL) { + return GL_FALSE; + } + + ext2beg = str; + ext2end = ext2beg; + + for (;;) { + while (ext2end[0] != ' ' && ext2end[0] != '\0') { + ext2end += 1; + } + + if (glb_strncmp(ext, ext2beg, ext2end - ext2beg) == 0) { + return GL_TRUE; + } + + /* Break if we've reached the end. Otherwise, just move to start fo the next extension. */ + if (ext2end[0] == '\0') { + break; + } else { + ext2beg = ext2end + 1; + ext2end = ext2beg; + } + } + + return GL_FALSE; +} + +#if defined(GLBIND_WGL) +GLboolean glbIsExtensionSupportedWGL(GLBapi* pAPI, const char* extensionName) +{ + PFNWGLGETEXTENSIONSSTRINGARBPROC _wglGetExtensionsStringARB = (pAPI != NULL) ? pAPI->wglGetExtensionsStringARB : wglGetExtensionsStringARB; + PFNWGLGETEXTENSIONSSTRINGEXTPROC _wglGetExtensionsStringEXT = (pAPI != NULL) ? pAPI->wglGetExtensionsStringEXT : wglGetExtensionsStringEXT; + PFNWGLGETCURRENTDCPROC _wglGetCurrentDC = (pAPI != NULL) ? pAPI->wglGetCurrentDC : glbind_wglGetCurrentDC; + + if (_wglGetExtensionsStringARB) { + return glbIsExtensionInString(extensionName, _wglGetExtensionsStringARB(_wglGetCurrentDC())); + } + if (_wglGetExtensionsStringEXT) { + return glbIsExtensionInString(extensionName, _wglGetExtensionsStringEXT()); + } + + return GL_FALSE; +} +#endif + +#if defined(GLBIND_GLX) +GLboolean glbIsExtensionSupportedGLX(GLBapi* pAPI, const char* extensionName) +{ + PFNGLXQUERYEXTENSIONSSTRINGPROC _glXQueryExtensionsString = (pAPI != NULL) ? pAPI->glXQueryExtensionsString : glbind_glXQueryExtensionsString; + + if (_glXQueryExtensionsString) { + return glbIsExtensionInString(extensionName, _glXQueryExtensionsString(glbGetDisplay(), glbind_XDefaultScreen(glbGetDisplay()))); + } + + return GL_FALSE; +} +#endif + +GLboolean glbIsExtensionSupported(GLBapi* pAPI, const char* extensionName) +{ + GLboolean isSupported = GL_FALSE; + PFNGLGETSTRINGIPROC _glGetStringi = (pAPI != NULL) ? pAPI->glGetStringi : glGetStringi; + PFNGLGETSTRINGPROC _glGetString = (pAPI != NULL) ? pAPI->glGetString : glGetString; + PFNGLGETINTEGERVPROC _glGetIntegerv = (pAPI != NULL) ? pAPI->glGetIntegerv : glGetIntegerv; + + /* Try the new way first. */ + if (_glGetStringi && _glGetIntegerv) { + GLint iExtension; + GLint supportedExtensionCount = 0; + _glGetIntegerv(GL_NUM_EXTENSIONS, &supportedExtensionCount); + + for (iExtension = 0; iExtension < supportedExtensionCount; ++iExtension) { + const char* pSupportedExtension = (const char*)_glGetStringi(GL_EXTENSIONS, iExtension); + if (pSupportedExtension != NULL) { + if (glb_strcmp(pSupportedExtension, extensionName) == 0) { + return GL_TRUE; + } + } + } + + /* It's not a core extension. Check platform-specific extensions. */ + isSupported = GL_FALSE; +#if defined(GLBIND_WGL) + isSupported = glbIsExtensionSupportedWGL(pAPI, extensionName); +#endif +#if defined(GLBIND_GLX) + isSupported = glbIsExtensionSupportedGLX(pAPI, extensionName); +#endif + return isSupported; + } + + /* Fall back to old style. */ + if (_glGetString) { + isSupported = glbIsExtensionInString(extensionName, (const char*)_glGetString(GL_EXTENSIONS)); + if (!isSupported) { +#if defined(GLBIND_WGL) + isSupported = glbIsExtensionSupportedWGL(pAPI, extensionName); +#endif +#if defined(GLBIND_GLX) + isSupported = glbIsExtensionSupportedGLX(pAPI, extensionName); +#endif + } + } + + return isSupported; +} + +#endif /* GLBIND_IMPLEMENTATION */ + +/* +This software is available as a choice of the following licenses. Choose +whichever you prefer. + +=============================================================================== +ALTERNATIVE 1 - Public Domain (www.unlicense.org) +=============================================================================== +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + +=============================================================================== +ALTERNATIVE 2 - MIT No Attribution +=============================================================================== +Copyright 2019 David Reid + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ \ No newline at end of file diff --git a/thirdparty/ssl/linux/bin/libssl.a b/thirdparty/ssl/linux/bin/libssl.a new file mode 100644 index 0000000..2f98a2d Binary files /dev/null and b/thirdparty/ssl/linux/bin/libssl.a differ diff --git a/thirdparty/ssl/linux/build.sh b/thirdparty/ssl/linux/build.sh new file mode 100755 index 0000000..cfccaab --- /dev/null +++ b/thirdparty/ssl/linux/build.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# TODO: Find a more robust way to prevent sokol from including the OpenGL headers +SOKOL_PATH="../../../thirdparty/sokol/" +if ! grep -q '//#include ' "${SOKOL_PATH}sokol_app.h"; then + sed -i 's|#include |//&|' "${SOKOL_PATH}sokol_app.h" + sed -i 's|typedef struct __GLXFBConfig\* GLXFBConfig;|/*&|' "${SOKOL_PATH}sokol_app.h" + sed -i 's|typedef GLXContext (\*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display\*,GLXFBConfig,GLXContext,Bool,const int\*);|&*/|' "${SOKOL_PATH}sokol_app.h" +fi +if ! grep -q '//#include ' "${SOKOL_PATH}sokol_gfx.h"; then + sed -i 's|#include |//&|' "${SOKOL_PATH}sokol_gfx.h" +fi +zig cc -o bin/ssl.o -c ssl.c -DSOKOL_NO_ENTRY -O3 +zig ar rcs bin/libssl.a bin/ssl.o +rm bin/ssl.o \ No newline at end of file diff --git a/thirdparty/ssl/linux/glbind_dummies.c b/thirdparty/ssl/linux/glbind_dummies.c new file mode 100644 index 0000000..f37eedd --- /dev/null +++ b/thirdparty/ssl/linux/glbind_dummies.c @@ -0,0 +1,14 @@ +#define GLBIND_IMPLEMENTATION +#include "../glbind.h" + +#include +#include + +#define SSL_ERROR_CB(...) { printf("SSL Error: "); printf(__VA_ARGS__); printf("\n"); exit(1); } + +void ssl_init_glbind(){ + GLenum result = glbInit(NULL, NULL); + if (result != GL_NO_ERROR) { + SSL_ERROR_CB("Failed to initialize glbind: %d\n", result); + } +} \ No newline at end of file diff --git a/thirdparty/ssl/linux/sokol_gfx.h b/thirdparty/ssl/linux/sokol_gfx.h new file mode 100644 index 0000000..351fab3 --- /dev/null +++ b/thirdparty/ssl/linux/sokol_gfx.h @@ -0,0 +1,19454 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_GFX_IMPL) +#define SOKOL_GFX_IMPL +#endif +#ifndef SOKOL_GFX_INCLUDED +/* + sokol_gfx.h -- simple 3D API wrapper + + Project URL: https://github.com/floooh/sokol + + Example code: https://github.com/floooh/sokol-samples + + Do this: + #define SOKOL_IMPL or + #define SOKOL_GFX_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + In the same place define one of the following to select the rendering + backend: + #define SOKOL_GLCORE + #define SOKOL_GLES3 + #define SOKOL_D3D11 + #define SOKOL_METAL + #define SOKOL_WGPU + #define SOKOL_DUMMY_BACKEND + + I.e. for the desktop GL it should look like this: + + #include ... + #include ... + #define SOKOL_IMPL + #define SOKOL_GLCORE + #include "sokol_gfx.h" + + The dummy backend replaces the platform-specific backend code with empty + stub functions. This is useful for writing tests that need to run on the + command line. + + Optionally provide the following defines with your own implementations: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + SOKOL_GFX_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_GFX_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + SOKOL_TRACE_HOOKS - enable trace hook callbacks (search below for TRACE HOOKS) + SOKOL_EXTERNAL_GL_LOADER - indicates that you're using your own GL loader, in this case + sokol_gfx.h will not include any platform GL headers and disable + the integrated Win32 GL loader + + If sokol_gfx.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_GFX_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + If you want to compile without deprecated structs and functions, + define: + + SOKOL_NO_DEPRECATED + + Optionally define the following to force debug checks and validations + even in release mode: + + SOKOL_DEBUG - by default this is defined if _DEBUG is defined + + sokol_gfx DOES NOT: + =================== + - create a window, swapchain or the 3D-API context/device, you must do this + before sokol_gfx is initialized, and pass any required information + (like 3D device pointers) to the sokol_gfx initialization call + + - present the rendered frame, how this is done exactly usually depends + on how the window and 3D-API context/device was created + + - provide a unified shader language, instead 3D-API-specific shader + source-code or shader-bytecode must be provided (for the "official" + offline shader cross-compiler, see here: + https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md) + + + STEP BY STEP + ============ + --- to initialize sokol_gfx, after creating a window and a 3D-API + context/device, call: + + sg_setup(const sg_desc*) + + Depending on the selected 3D backend, sokol-gfx requires some + information, like a device pointer, default swapchain pixel formats + and so on. If you are using sokol_app.h for the window system + glue, you can use a helper function provided in the sokol_glue.h + header: + + #include "sokol_gfx.h" + #include "sokol_app.h" + #include "sokol_glue.h" + //... + sg_setup(&(sg_desc){ + .environment = sglue_environment(), + }); + + To get any logging output for errors and from the validation layer, you + need to provide a logging callback. Easiest way is through sokol_log.h: + + #include "sokol_log.h" + //... + sg_setup(&(sg_desc){ + //... + .logger.func = slog_func, + }); + + --- create resource objects (at least buffers, shaders and pipelines, + and optionally images, samplers and render-pass-attachments): + + sg_buffer sg_make_buffer(const sg_buffer_desc*) + sg_image sg_make_image(const sg_image_desc*) + sg_sampler sg_make_sampler(const sg_sampler_desc*) + sg_shader sg_make_shader(const sg_shader_desc*) + sg_pipeline sg_make_pipeline(const sg_pipeline_desc*) + sg_attachments sg_make_attachments(const sg_attachments_desc*) + + --- start a render pass: + + sg_begin_pass(const sg_pass* pass); + + Typically, passes render into an externally provided swapchain which + presents the rendering result on the display. Such a 'swapchain pass' + is started like this: + + sg_begin_pass(&(sg_pass){ .action = { ... }, .swapchain = sglue_swapchain() }) + + ...where .action is an sg_pass_action struct containing actions to be performed + at the start and end of a render pass (such as clearing the render surfaces to + a specific color), and .swapchain is an sg_swapchain + struct all the required information to render into the swapchain's surfaces. + + To start an 'offscreen pass' into sokol-gfx image objects, an sg_attachment + object handle is required instead of an sg_swapchain struct. An offscreen + pass is started like this (assuming attachments is an sg_attachments handle): + + sg_begin_pass(&(sg_pass){ .action = { ... }, .attachments = attachments }); + + --- set the render pipeline state for the next draw call with: + + sg_apply_pipeline(sg_pipeline pip) + + --- fill an sg_bindings struct with the resource bindings for the next + draw call (0..N vertex buffers, 0 or 1 index buffer, 0..N image-objects, + samplers and storage-buffers), and call: + + sg_apply_bindings(const sg_bindings* bindings) + + to update the resource bindings + + --- optionally update shader uniform data with: + + sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data) + + Read the section 'UNIFORM DATA LAYOUT' to learn about the expected memory layout + of the uniform data passed into sg_apply_uniforms(). + + --- kick off a draw call with: + + sg_draw(int base_element, int num_elements, int num_instances) + + The sg_draw() function unifies all the different ways to render primitives + in a single call (indexed vs non-indexed rendering, and instanced vs non-instanced + rendering). In case of indexed rendering, base_element and num_element specify + indices in the currently bound index buffer. In case of non-indexed rendering + base_element and num_elements specify vertices in the currently bound + vertex-buffer(s). To perform instanced rendering, the rendering pipeline + must be setup for instancing (see sg_pipeline_desc below), a separate vertex buffer + containing per-instance data must be bound, and the num_instances parameter + must be > 1. + + --- finish the current rendering pass with: + + sg_end_pass() + + --- when done with the current frame, call + + sg_commit() + + --- at the end of your program, shutdown sokol_gfx with: + + sg_shutdown() + + --- if you need to destroy resources before sg_shutdown(), call: + + sg_destroy_buffer(sg_buffer buf) + sg_destroy_image(sg_image img) + sg_destroy_sampler(sg_sampler smp) + sg_destroy_shader(sg_shader shd) + sg_destroy_pipeline(sg_pipeline pip) + sg_destroy_attachments(sg_attachments atts) + + --- to set a new viewport rectangle, call + + sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) + + ...or if you want to specify the viewport rectangle with float values: + + sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left) + + --- to set a new scissor rect, call: + + sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) + + ...or with float values: + + sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left) + + Both sg_apply_viewport() and sg_apply_scissor_rect() must be called + inside a rendering pass + + Note that sg_begin_default_pass() and sg_begin_pass() will reset both the + viewport and scissor rectangles to cover the entire framebuffer. + + --- to update (overwrite) the content of buffer and image resources, call: + + sg_update_buffer(sg_buffer buf, const sg_range* data) + sg_update_image(sg_image img, const sg_image_data* data) + + Buffers and images to be updated must have been created with + SG_USAGE_DYNAMIC or SG_USAGE_STREAM + + Only one update per frame is allowed for buffer and image resources when + using the sg_update_*() functions. The rationale is to have a simple + countermeasure to avoid the CPU scribbling over data the GPU is currently + using, or the CPU having to wait for the GPU + + Buffer and image updates can be partial, as long as a rendering + operation only references the valid (updated) data in the + buffer or image. + + --- to append a chunk of data to a buffer resource, call: + + int sg_append_buffer(sg_buffer buf, const sg_range* data) + + The difference to sg_update_buffer() is that sg_append_buffer() + can be called multiple times per frame to append new data to the + buffer piece by piece, optionally interleaved with draw calls referencing + the previously written data. + + sg_append_buffer() returns a byte offset to the start of the + written data, this offset can be assigned to + sg_bindings.vertex_buffer_offsets[n] or + sg_bindings.index_buffer_offset + + Code example: + + for (...) { + const void* data = ...; + const int num_bytes = ...; + int offset = sg_append_buffer(buf, &(sg_range) { .ptr=data, .size=num_bytes }); + bindings.vertex_buffer_offsets[0] = offset; + sg_apply_pipeline(pip); + sg_apply_bindings(&bindings); + sg_apply_uniforms(...); + sg_draw(...); + } + + A buffer to be used with sg_append_buffer() must have been created + with SG_USAGE_DYNAMIC or SG_USAGE_STREAM. + + If the application appends more data to the buffer then fits into + the buffer, the buffer will go into the "overflow" state for the + rest of the frame. + + Any draw calls attempting to render an overflown buffer will be + silently dropped (in debug mode this will also result in a + validation error). + + You can also check manually if a buffer is in overflow-state by calling + + bool sg_query_buffer_overflow(sg_buffer buf) + + You can manually check to see if an overflow would occur before adding + any data to a buffer by calling + + bool sg_query_buffer_will_overflow(sg_buffer buf, size_t size) + + NOTE: Due to restrictions in underlying 3D-APIs, appended chunks of + data will be 4-byte aligned in the destination buffer. This means + that there will be gaps in index buffers containing 16-bit indices + when the number of indices in a call to sg_append_buffer() is + odd. This isn't a problem when each call to sg_append_buffer() + is associated with one draw call, but will be problematic when + a single indexed draw call spans several appended chunks of indices. + + --- to check at runtime for optional features, limits and pixelformat support, + call: + + sg_features sg_query_features() + sg_limits sg_query_limits() + sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt) + + --- if you need to call into the underlying 3D-API directly, you must call: + + sg_reset_state_cache() + + ...before calling sokol_gfx functions again + + --- you can inspect the original sg_desc structure handed to sg_setup() + by calling sg_query_desc(). This will return an sg_desc struct with + the default values patched in instead of any zero-initialized values + + --- you can get a desc struct matching the creation attributes of a + specific resource object via: + + sg_buffer_desc sg_query_buffer_desc(sg_buffer buf) + sg_image_desc sg_query_image_desc(sg_image img) + sg_sampler_desc sg_query_sampler_desc(sg_sampler smp) + sg_shader_desc sq_query_shader_desc(sg_shader shd) + sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip) + sg_attachments_desc sg_query_attachments_desc(sg_attachments atts) + + ...but NOTE that the returned desc structs may be incomplete, only + creation attributes that are kept around internally after resource + creation will be filled in, and in some cases (like shaders) that's + very little. Any missing attributes will be set to zero. The returned + desc structs might still be useful as partial blueprint for creating + similar resources if filled up with the missing attributes. + + Calling the query-desc functions on an invalid resource will return + completely zeroed structs (it makes sense to check the resource state + with sg_query_*_state() first) + + --- you can query the default resource creation parameters through the functions + + sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) + sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) + sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc) + sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) + sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc) + sg_attachments_desc sg_query_attachments_defaults(const sg_attachments_desc* desc) + + These functions take a pointer to a desc structure which may contain + zero-initialized items for default values. These zero-init values + will be replaced with their concrete values in the returned desc + struct. + + --- you can inspect various internal resource runtime values via: + + sg_buffer_info sg_query_buffer_info(sg_buffer buf) + sg_image_info sg_query_image_info(sg_image img) + sg_sampler_info sg_query_sampler_info(sg_sampler smp) + sg_shader_info sg_query_shader_info(sg_shader shd) + sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip) + sg_attachments_info sg_query_attachments_info(sg_attachments atts) + + ...please note that the returned info-structs are tied quite closely + to sokol_gfx.h internals, and may change more often than other + public API functions and structs. + + --- you can query frame stats and control stats collection via: + + sg_query_frame_stats() + sg_enable_frame_stats() + sg_disable_frame_stats() + sg_frame_stats_enabled() + + --- you can ask at runtime what backend sokol_gfx.h has been compiled for: + + sg_backend sg_query_backend(void) + + --- call the following helper functions to compute the number of + bytes in a texture row or surface for a specific pixel format. + These functions might be helpful when preparing image data for consumption + by sg_make_image() or sg_update_image(): + + int sg_query_row_pitch(sg_pixel_format fmt, int width, int int row_align_bytes); + int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes); + + Width and height are generally in number pixels, but note that 'row' has different meaning + for uncompressed vs compressed pixel formats: for uncompressed formats, a row is identical + with a single line if pixels, while in compressed formats, one row is a line of *compression blocks*. + + This is why calling sg_query_surface_pitch() for a compressed pixel format and height + N, N+1, N+2, ... may return the same result. + + The row_align_bytes parammeter is for added flexibility. For image data that goes into + the sg_make_image() or sg_update_image() this should generally be 1, because these + functions take tightly packed image data as input no matter what alignment restrictions + exist in the backend 3D APIs. + + ON INITIALIZATION: + ================== + When calling sg_setup(), a pointer to an sg_desc struct must be provided + which contains initialization options. These options provide two types + of information to sokol-gfx: + + (1) upper bounds and limits needed to allocate various internal + data structures: + - the max number of resources of each type that can + be alive at the same time, this is used for allocating + internal pools + - the max overall size of uniform data that can be + updated per frame, including a worst-case alignment + per uniform update (this worst-case alignment is 256 bytes) + - the max size of all dynamic resource updates (sg_update_buffer, + sg_append_buffer and sg_update_image) per frame + Not all of those limit values are used by all backends, but it is + good practice to provide them none-the-less. + + (2) 3D backend "environment information" in a nested sg_environment struct: + - pointers to backend-specific context- or device-objects (for instance + the D3D11, WebGPU or Metal device objects) + - defaults for external swapchain pixel formats and sample counts, + these will be used as default values in image and pipeline objects, + and the sg_swapchain struct passed into sg_begin_pass() + Usually you provide a complete sg_environment struct through + a helper function, as an example look at the sglue_environment() + function in the sokol_glue.h header. + + See the documentation block of the sg_desc struct below for more information. + + + ON RENDER PASSES + ================ + Relevant samples: + - https://floooh.github.io/sokol-html5/offscreen-sapp.html + - https://floooh.github.io/sokol-html5/offscreen-msaa-sapp.html + - https://floooh.github.io/sokol-html5/mrt-sapp.html + - https://floooh.github.io/sokol-html5/mrt-pixelformats-sapp.html + + A render pass groups rendering commands into a set of render target images + (called 'pass attachments'). Render target images can be used in subsequent + passes as textures (it is invalid to use the same image both as render target + and as texture in the same pass). + + The following sokol-gfx functions must only be called inside a render pass: + + sg_apply_viewport(f) + sg_apply_scissor_rect(f) + sg_apply_pipeline + sg_apply_bindings + sg_apply_uniforms + sg_draw + + A frame must have at least one 'swapchain render pass' which renders into an + externally provided swapchain provided as an sg_swapchain struct to the + sg_begin_pass() function. The sg_swapchain struct must contain the + following information: + + - the color pixel-format of the swapchain's render surface + - an optional depth/stencil pixel format if the swapchain + has a depth/stencil buffer + - an optional sample-count for MSAA rendering + - NOTE: the above three values can be zero-initialized, in that + case the defaults from the sg_environment struct will be used that + had been passed to the sg_setup() function. + - a number of backend specific objects: + - GL/GLES3: just a GL framebuffer handle + - D3D11: + - an ID3D11RenderTargetView for the rendering surface + - if MSAA is used, an ID3D11RenderTargetView as + MSAA resolve-target + - an optional ID3D11DepthStencilView for the + depth/stencil buffer + - WebGPU + - a WGPUTextureView object for the rendering surface + - if MSAA is used, a WGPUTextureView object as MSAA resolve target + - an optional WGPUTextureView for the + - Metal (NOTE that the roles of provided surfaces is slightly + different in Metal than in D3D11 or WebGPU, notably, the + CAMetalDrawable is either rendered to directly, or serves + as MSAA resolve target): + - a CAMetalDrawable object which is either rendered + into directly, or in case of MSAA rendering, serves + as MSAA-resolve-target + - if MSAA is used, an multisampled MTLTexture where + rendering goes into + - an optional MTLTexture for the depth/stencil buffer + + It's recommended that you create a helper function which returns an + initialized sg_swapchain struct by value. This can then be directly plugged + into the sg_begin_pass function like this: + + sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain() }); + + As an example for such a helper function check out the function sglue_swapchain() + in the sokol_glue.h header. + + For offscreen render passes, the render target images used in a render pass + are baked into an immutable sg_attachments object. + + For a simple offscreen scenario with one color-, one depth-stencil-render + target and without multisampling, creating an attachment object looks like this: + + First create two render target images, one with a color pixel format, + and one with the depth- or depth-stencil pixel format. Both images + must have the same dimensions: + + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .sample_count = 1, + }); + const sg_image depth_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_DEPTH, + .sample_count = 1, + }); + + NOTE: when creating render target images, have in mind that some default values + are aligned with the default environment attributes in the sg_environment struct + that was passed into the sg_setup() call: + + - the default value for sg_image_desc.pixel_format is taken from + sg_environment.defaults.color_format + - the default value for sg_image_desc.sample_count is taken from + sg_environment.defaults.sample_count + - the default value for sg_image_desc.num_mipmaps is always 1 + + Next create an attachments object: + + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0].image = color_img, + .depth_stencil.image = depth_img, + }); + + This attachments object is then passed into the sg_begin_pass() function + in place of the swapchain struct: + + sg_begin_pass(&(sg_pass){ .attachments = atts }); + + Swapchain and offscreen passes form dependency trees each with a swapchain + pass at the root, offscreen passes as nodes, and render target images as + dependencies between passes. + + sg_pass_action structs are used to define actions that should happen at the + start and end of rendering passes (such as clearing pass attachments to a + specific color or depth-value, or performing an MSAA resolve operation at + the end of a pass). + + A typical sg_pass_action object which clears the color attachment to black + might look like this: + + const sg_pass_action = { + .colors[0] = { + .load_action = SG_LOADACTION_CLEAR, + .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f } + } + }; + + This omits the defaults for the color attachment store action, and + the depth-stencil-attachments actions. The same pass action with the + defaults explicitly filled in would look like this: + + const sg_pass_action pass_action = { + .colors[0] = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_STORE, + .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f } + }, + .depth = = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_DONTCARE, + .clear_value = 1.0f, + }, + .stencil = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_DONTCARE, + .clear_value = 0 + } + }; + + With the sg_pass object and sg_pass_action struct in place everything + is ready now for the actual render pass: + + Using such this prepared sg_pass_action in a swapchain pass looks like + this: + + sg_begin_pass(&(sg_pass){ + .action = pass_action, + .swapchain = sglue_swapchain() + }); + ... + sg_end_pass(); + + ...of alternatively in one offscreen pass: + + sg_begin_pass(&(sg_pass){ + .action = pass_action, + .attachments = attachments, + }); + ... + sg_end_pass(); + + Offscreen rendering can also go into a mipmap, or a slice/face of + a cube-, array- or 3d-image (which some restrictions, for instance + it's not possible to create a 3D image with a depth/stencil pixel format, + these exceptions are generally caught by the sokol-gfx validation layer). + + The mipmap/slice selection happens at attachments creation time, for instance + to render into mipmap 2 of slice 3 of an array texture: + + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0] = { + .image = color_img, + .mip_level = 2, + .slice = 3, + }, + .depth_stencil.image = depth_img, + }); + + If MSAA offscreen rendering is desired, the multi-sample rendering result + must be 'resolved' into a separate 'resolve image', before that image can + be used as texture. + + NOTE: currently multisample-images cannot be bound as textures. + + Creating a simple attachments object for multisampled rendering requires + 3 attachment images: the color attachment image which has a sample + count > 1, a resolve attachment image of the same size and pixel format + but a sample count == 1, and a depth/stencil attachment image with + the same size and sample count as the color attachment image: + + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .sample_count = 4, + }); + const sg_image resolve_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .sample_count = 1, + }); + const sg_image depth_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_DEPTH, + .sample_count = 4, + }); + + ...create the attachments object: + + const sg_attachments atts = sg_make_attachments(&(sg_attachments_desc){ + .colors[0].image = color_img, + .resolves[0].image = resolve_img, + .depth_stencil.image = depth_img, + }); + + If an attachments object defines a resolve image in a specific resolve attachment slot, + an 'msaa resolve operation' will happen in sg_end_pass(). + + In this scenario, the content of the MSAA color attachment doesn't need to be + preserved (since it's only needed inside sg_end_pass for the msaa-resolve), so + the .store_action should be set to "don't care": + + const sg_pass_action = { + .colors[0] = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_DONTCARE, + .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f } + } + }; + + The actual render pass looks as usual: + + sg_begin_pass(&(sg_pass){ .action = pass_action, .attachments = atts }); + ... + sg_end_pass(); + + ...after sg_end_pass() the only difference to the non-msaa scenario is that the + rendering result which is going to be used as texture in a followup pass is + in 'resolve_img', not in 'color_img' (in fact, trying to bind color_img as a + texture would result in a validation error). + + + ON SHADER CREATION + ================== + sokol-gfx doesn't come with an integrated shader cross-compiler, instead + backend-specific shader sources or binary blobs need to be provided when + creating a shader object, along with information about the shader resource + binding interface needed in the sokol-gfx validation layer and to properly + bind shader resources on the CPU-side to be consumable by the GPU-side. + + The easiest way to provide all this shader creation data is to use the + sokol-shdc shader compiler tool to compile shaders from a common + GLSL syntax into backend-specific sources or binary blobs, along with + shader interface information and uniform blocks mapped to C structs. + + To create a shader using a C header which has been code-generated by sokol-shdc: + + // include the C header code-generated by sokol-shdc: + #include "myshader.glsl.h" + ... + + // create shader using a code-generated helper function from the C header: + sg_shader shd = sg_make_shader(myshader_shader_desc(sg_query_backend())); + + The samples in the 'sapp' subdirectory of the sokol-samples project + also use the sokol-shdc approach: + + https://github.com/floooh/sokol-samples/tree/master/sapp + + If you're planning to use sokol-shdc, you can stop reading here, instead + continue with the sokol-shdc documentation: + + https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md + + To create shaders with backend-specific shader code or binary blobs, + the sg_make_shader() function requires the following information: + + - Shader code or shader binary blobs for the vertex- and fragment- shader-stage: + - for the desktop GL backend, source code can be provided in '#version 410' or + '#version 430', version 430 is required for storage buffer support, but note + that this is not available on macOS + - for the GLES3 backend, source code must be provided in '#version 300 es' syntax + - for the D3D11 backend, shaders can be provided as source or binary blobs, the + source code should be in HLSL4.0 (for best compatibility) or alternatively + in HLSL5.0 syntax (other versions may work but are not tested), NOTE: when + shader source code is provided for the D3D11 backend, sokol-gfx will dynamically + load 'd3dcompiler_47.dll' + - for the Metal backends, shaders can be provided as source or binary blobs, the + MSL version should be in 'metal-1.1' (other versions may work but are not tested) + - for the WebGPU backend, shader must be provided as WGSL source code + - optionally the following shader-code related attributes can be provided: + - an entry function name (only on D3D11 or Metal, but not OpenGL) + - on D3D11 only, a compilation target (default is "vs_4_0" and "ps_4_0") + + - Depending on backend, information about the input vertex attributes used by the + vertex shader: + - Metal: no information needed since vertex attributes are always bound + by their attribute location defined in the shader via '[[attribute(N)]]' + - WebGPU: no information needed since vertex attributes are always + bound by their attribute location defined in the shader via `@location(N)` + - GLSL: vertex attribute names can be optionally provided, in that case their + location will be looked up by name, otherwise, the vertex attribute location + can be defined with 'layout(location = N)', PLEASE NOTE that the name-lookup method + may be removed at some point + - D3D11: a 'semantic name' and 'semantic index' must be provided for each vertex + attribute, e.g. if the vertex attribute is defined as 'TEXCOORD1' in the shader, + the semantic name would be 'TEXCOORD', and the semantic index would be '1' + + - Information about each uniform block used in the shader: + - The size of the uniform block in number of bytes. + - A memory layout hint (currently 'native' or 'std140') where 'native' defines a + backend-specific memory layout which shouldn't be used for cross-platform code. + Only std140 guarantees a backend-agnostic memory layout. + - For GLSL only: a description of the internal uniform block layout, which maps + member types and their offsets on the CPU side to uniform variable names + in the GLSL shader + - please also NOTE the documentation sections about UNIFORM DATA LAYOUT + and CROSS-BACKEND COMMON UNIFORM DATA LAYOUT below! + + - A description of each storage buffer used in the shader: + - a boolean 'readonly' flag, note that currently only + readonly storage buffers are supported + - note that storage buffers are not supported on all backends + and platforms + + - A description of each texture/image used in the shader: + - the expected image type: + - SG_IMAGETYPE_2D + - SG_IMAGETYPE_CUBE + - SG_IMAGETYPE_3D + - SG_IMAGETYPE_ARRAY + - the expected 'image sample type': + - SG_IMAGESAMPLETYPE_FLOAT + - SG_IMAGESAMPLETYPE_DEPTH + - SG_IMAGESAMPLETYPE_SINT + - SG_IMAGESAMPLETYPE_UINT + - SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT + - a flag whether the texture is expected to be multisampled + (currently it's not supported to fetch data from multisampled + textures in shaders, but this is planned for a later time) + + - A description of each texture sampler used in the shader: + - SG_SAMPLERTYPE_FILTERING, + - SG_SAMPLERTYPE_NONFILTERING, + - SG_SAMPLERTYPE_COMPARISON, + + - An array of 'image-sampler-pairs' used by the shader to sample textures, + for D3D11, Metal and WebGPU this is used for validation purposes to check + whether the texture and sampler are compatible with each other (especially + WebGPU is very picky about combining the correct + texture-sample-type with the correct sampler-type). For GLSL an + additional 'combined-image-sampler name' must be provided because 'OpenGL + style GLSL' cannot handle separate texture and sampler objects, but still + groups them into a traditional GLSL 'sampler object'. + + Compatibility rules for image-sample-type vs sampler-type are as follows: + + - SG_IMAGESAMPLETYPE_FLOAT => (SG_SAMPLERTYPE_FILTERING or SG_SAMPLERTYPE_NONFILTERING) + - SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT => SG_SAMPLERTYPE_NONFILTERING + - SG_IMAGESAMPLETYPE_SINT => SG_SAMPLERTYPE_NONFILTERING + - SG_IMAGESAMPLETYPE_UINT => SG_SAMPLERTYPE_NONFILTERING + - SG_IMAGESAMPLETYPE_DEPTH => SG_SAMPLERTYPE_COMPARISON + + For example code of how to create backend-specific shader objects, + please refer to the following samples: + + - for D3D11: https://github.com/floooh/sokol-samples/tree/master/d3d11 + - for Metal: https://github.com/floooh/sokol-samples/tree/master/metal + - for OpenGL: https://github.com/floooh/sokol-samples/tree/master/glfw + - for GLES3: https://github.com/floooh/sokol-samples/tree/master/html5 + - for WebGPI: https://github.com/floooh/sokol-samples/tree/master/wgpu + + + ON SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT AND SG_SAMPLERTYPE_NONFILTERING + ======================================================================== + The WebGPU backend introduces the concept of 'unfilterable-float' textures, + which can only be combined with 'nonfiltering' samplers (this is a restriction + specific to WebGPU, but since the same sokol-gfx code should work across + all backend, the sokol-gfx validation layer also enforces this restriction + - the alternative would be undefined behaviour in some backend APIs on + some devices). + + The background is that some mobile devices (most notably iOS devices) can + not perform linear filtering when sampling textures with certain pixel + formats, most notable the 32F formats: + + - SG_PIXELFORMAT_R32F + - SG_PIXELFORMAT_RG32F + - SG_PIXELFORMAT_RGBA32F + + The information of whether a shader is going to be used with such an + unfilterable-float texture must already be provided in the sg_shader_desc + struct when creating the shader (see the above section "ON SHADER CREATION"). + + If you are using the sokol-shdc shader compiler, the information whether a + texture/sampler binding expects an 'unfilterable-float/nonfiltering' + texture/sampler combination cannot be inferred from the shader source + alone, you'll need to provide this hint via annotation-tags. For instance + here is an example from the ozz-skin-sapp.c sample shader which samples an + RGBA32F texture with skinning matrices in the vertex shader: + + ```glsl + @image_sample_type joint_tex unfilterable_float + uniform texture2D joint_tex; + @sampler_type smp nonfiltering + uniform sampler smp; + ``` + + This will result in SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT and + SG_SAMPLERTYPE_NONFILTERING being written to the code-generated + sg_shader_desc struct. + + + UNIFORM DATA LAYOUT: + ==================== + NOTE: if you use the sokol-shdc shader compiler tool, you don't need to worry + about the following details. + + The data that's passed into the sg_apply_uniforms() function must adhere to + specific layout rules so that the GPU shader finds the uniform block + items at the right offset. + + For the D3D11 and Metal backends, sokol-gfx only cares about the size of uniform + blocks, but not about the internal layout. The data will just be copied into + a uniform/constant buffer in a single operation and it's up you to arrange the + CPU-side layout so that it matches the GPU side layout. This also means that with + the D3D11 and Metal backends you are not limited to a 'cross-platform' subset + of uniform variable types. + + If you ever only use one of the D3D11, Metal *or* WebGPU backend, you can stop reading here. + + For the GL backends, the internal layout of uniform blocks matters though, + and you are limited to a small number of uniform variable types. This is + because sokol-gfx must be able to locate the uniform block members in order + to upload them to the GPU with glUniformXXX() calls. + + To describe the uniform block layout to sokol-gfx, the following information + must be passed to the sg_make_shader() call in the sg_shader_desc struct: + + - a hint about the used packing rule (either SG_UNIFORMLAYOUT_NATIVE or + SG_UNIFORMLAYOUT_STD140) + - a list of the uniform block members types in the correct order they + appear on the CPU side + + For example if the GLSL shader has the following uniform declarations: + + uniform mat4 mvp; + uniform vec2 offset0; + uniform vec2 offset1; + uniform vec2 offset2; + + ...and on the CPU side, there's a similar C struct: + + typedef struct { + float mvp[16]; + float offset0[2]; + float offset1[2]; + float offset2[2]; + } params_t; + + ...the uniform block description in the sg_shader_desc must look like this: + + sg_shader_desc desc = { + .vs.uniform_blocks[0] = { + .size = sizeof(params_t), + .layout = SG_UNIFORMLAYOUT_NATIVE, // this is the default and can be omitted + .uniforms = { + // order must be the same as in 'params_t': + [0] = { .name = "mvp", .type = SG_UNIFORMTYPE_MAT4 }, + [1] = { .name = "offset0", .type = SG_UNIFORMTYPE_VEC2 }, + [2] = { .name = "offset1", .type = SG_UNIFORMTYPE_VEC2 }, + [3] = { .name = "offset2", .type = SG_UNIFORMTYPE_VEC2 }, + } + } + }; + + With this information sokol-gfx can now compute the correct offsets of the data items + within the uniform block struct. + + The SG_UNIFORMLAYOUT_NATIVE packing rule works fine if only the GL backends are used, + but for proper D3D11/Metal/GL a subset of the std140 layout must be used which is + described in the next section: + + + CROSS-BACKEND COMMON UNIFORM DATA LAYOUT + ======================================== + For cross-platform / cross-3D-backend code it is important that the same uniform block + layout on the CPU side can be used for all sokol-gfx backends. To achieve this, + a common subset of the std140 layout must be used: + + - The uniform block layout hint in sg_shader_desc must be explicitly set to + SG_UNIFORMLAYOUT_STD140. + - Only the following GLSL uniform types can be used (with their associated sokol-gfx enums): + - float => SG_UNIFORMTYPE_FLOAT + - vec2 => SG_UNIFORMTYPE_FLOAT2 + - vec3 => SG_UNIFORMTYPE_FLOAT3 + - vec4 => SG_UNIFORMTYPE_FLOAT4 + - int => SG_UNIFORMTYPE_INT + - ivec2 => SG_UNIFORMTYPE_INT2 + - ivec3 => SG_UNIFORMTYPE_INT3 + - ivec4 => SG_UNIFORMTYPE_INT4 + - mat4 => SG_UNIFORMTYPE_MAT4 + - Alignment for those types must be as follows (in bytes): + - float => 4 + - vec2 => 8 + - vec3 => 16 + - vec4 => 16 + - int => 4 + - ivec2 => 8 + - ivec3 => 16 + - ivec4 => 16 + - mat4 => 16 + - Arrays are only allowed for the following types: vec4, int4, mat4. + + Note that the HLSL cbuffer layout rules are slightly different from the + std140 layout rules, this means that the cbuffer declarations in HLSL code + must be tweaked so that the layout is compatible with std140. + + The by far easiest way to tackle the common uniform block layout problem is + to use the sokol-shdc shader cross-compiler tool! + + ON STORAGE BUFFERS + ================== + Storage buffers can be used to pass large amounts of random access structured + data fromt the CPU side to the shaders. They are similar to data textures, but are + more convenient to use both on the CPU and shader side since they can be accessed + in shaders as as a 1-dimensional array of struct items. + + Storage buffers are *NOT* supported on the following platform/backend combos: + + - macOS+GL (because storage buffers require GL 4.3, while macOS only goes up to GL 4.1) + - all GLES3 platforms (WebGL2, iOS, Android - with the option that support on + Android may be added at a later point) + + Currently only 'readonly' storage buffers are supported (meaning it's not possible + to write to storage buffers from shaders). + + To use storage buffers, the following steps are required: + + - write a shader which uses storage buffers (also see the example links below) + - create one or more storage buffers via sg_make_buffer() with the + buffer type SG_BUFFERTYPE_STORAGEBUFFER + - when creating a shader via sg_make_shader(), populate the sg_shader_desc + struct with binding info (when using sokol-shdc, this step will be taken care + of automatically) + - which storage buffer bind slots on the vertex- and fragment-stage + are occupied + - whether the storage buffer on that bind slot is readonly (this is currently required + to be true) + - when calling sg_apply_bindings(), apply the matching bind slots with the previously + created storage buffers + - ...and that's it. + + For more details, see the following backend-agnostic sokol samples: + + - simple vertex pulling from a storage buffer: + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/vertexpull-sapp.c + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/vertexpull-sapp.glsl + - instanced rendering via storage buffers (vertex- and instance-pulling): + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-pull-sapp.c + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/instancing-pull-sapp.glsl + - storage buffers both on the vertex- and fragment-stage: + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/sbuftex-sapp.c + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/sbuftex-sapp.glsl + - the Ozz animation sample rewritten to pull all rendering data from storage buffers: + - C code: https://github.com/floooh/sokol-samples/blob/master/sapp/ozz-storagebuffer-sapp.cc + - shader: https://github.com/floooh/sokol-samples/blob/master/sapp/ozz-storagebuffer-sapp.glsl + + ...also see the following backend-specific vertex pulling samples (those also don't use sokol-shdc): + + - D3D11: https://github.com/floooh/sokol-samples/blob/master/d3d11/vertexpulling-d3d11.c + - desktop GL: https://github.com/floooh/sokol-samples/blob/master/glfw/vertexpulling-glfw.c + - Metal: https://github.com/floooh/sokol-samples/blob/master/metal/vertexpulling-metal.c + - WebGPU: https://github.com/floooh/sokol-samples/blob/master/wgpu/vertexpulling-wgpu.c + + Storage buffer shader authoring caveats when using sokol-shdc: + + - declare a storage buffer interface block with `readonly buffer [name] { ... }` + - do NOT annotate storage buffers with `layout(...)`, sokol-shdc will take care of that + - declare a struct which describes a single array item in the storage buffer interface block + - only put a single flexible array member into the storage buffer interface block + + E.g. a complete example in 'sokol-shdc GLSL': + + ```glsl + // declare a struct: + struct sb_vertex { + vec3 pos; + vec4 color; + } + // declare a buffer interface block with a single flexible struct array: + readonly buffer vertices { + sb_vertex vtx[]; + } + // in the shader function, access the storage buffer like this: + void main() { + vec3 pos = vtx[gl_VertexIndex].pos; + ... + } + ``` + + Backend-specific storage-buffer caveats (not relevant when using sokol-shdc): + + D3D11: + - storage buffers are created as 'raw' Byte Address Buffers + (https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-intro#raw-views-of-buffers) + - in HLSL, use a ByteAddressBuffer to access the buffer content + (https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-byteaddressbuffer) + - in D3D11, storage buffers and textures share the same bind slots, sokol-gfx reserves + shader resource slots 0..15 for textures and 16..23 for storage buffers. + - e.g. in HLSL, storage buffer bindings start at register(t16) no matter the shader stage + + Metal: + - in Metal there is no internal difference between vertex-, uniform- and + storage-buffers, all are bound to the same 'buffer bind slots' with the + following reserved ranges: + - vertex shader stage: + - uniform buffers (internal): slots 0..3 + - vertex buffers: slots 4..11 + - storage buffers: slots 12..19 + - fragment shader stage: + - uniform buffers (internal): slots 0..3 + - storage buffers: slots 4..11 + - this means in MSL, storage buffer bindings start at [[buffer(12)]] in the vertex + shaders, and at [[buffer(4)]] in fragment shaders + + GL: + - the GL backend doesn't use name-lookup to find storage buffer bindings, this + means you must annotate buffers with `layout(std430, binding=N)` in GLSL + - ...where N is 0..7 in the vertex shader, and 8..15 in the fragment shader + + WebGPU: + - in WGSL, use the following bind locations for the various shader resource types: + - vertex shader stage: + - textures `@group(1) @binding(0..15)` + - samplers `@group(1) @binding(16..31)` + - storage buffers `@group(1) @binding(32..47)` + - fragment shader stage: + - textures `@group(1) @binding(48..63)` + - samplers `@group(1) @binding(64..79)` + - storage buffers `@group(1) @binding(80..95)` + + TRACE HOOKS: + ============ + sokol_gfx.h optionally allows to install "trace hook" callbacks for + each public API functions. When a public API function is called, and + a trace hook callback has been installed for this function, the + callback will be invoked with the parameters and result of the function. + This is useful for things like debugging- and profiling-tools, or + keeping track of resource creation and destruction. + + To use the trace hook feature: + + --- Define SOKOL_TRACE_HOOKS before including the implementation. + + --- Setup an sg_trace_hooks structure with your callback function + pointers (keep all function pointers you're not interested + in zero-initialized), optionally set the user_data member + in the sg_trace_hooks struct. + + --- Install the trace hooks by calling sg_install_trace_hooks(), + the return value of this function is another sg_trace_hooks + struct which contains the previously set of trace hooks. + You should keep this struct around, and call those previous + functions pointers from your own trace callbacks for proper + chaining. + + As an example of how trace hooks are used, have a look at the + imgui/sokol_gfx_imgui.h header which implements a realtime + debugging UI for sokol_gfx.h on top of Dear ImGui. + + + A NOTE ON PORTABLE PACKED VERTEX FORMATS: + ========================================= + There are two things to consider when using packed + vertex formats like UBYTE4, SHORT2, etc which need to work + across all backends: + + - D3D11 can only convert *normalized* vertex formats to + floating point during vertex fetch, normalized formats + have a trailing 'N', and are "normalized" to a range + -1.0..+1.0 (for the signed formats) or 0.0..1.0 (for the + unsigned formats): + + - SG_VERTEXFORMAT_BYTE4N + - SG_VERTEXFORMAT_UBYTE4N + - SG_VERTEXFORMAT_SHORT2N + - SG_VERTEXFORMAT_USHORT2N + - SG_VERTEXFORMAT_SHORT4N + - SG_VERTEXFORMAT_USHORT4N + + D3D11 will not convert *non-normalized* vertex formats to floating point + vertex shader inputs, those can only be uses with the *ivecn* vertex shader + input types when D3D11 is used as backend (GL and Metal can use both formats) + + - SG_VERTEXFORMAT_BYTE4, + - SG_VERTEXFORMAT_UBYTE4 + - SG_VERTEXFORMAT_SHORT2 + - SG_VERTEXFORMAT_SHORT4 + + For a vertex input layout which works on all platforms, only use the following + vertex formats, and if needed "expand" the normalized vertex shader + inputs in the vertex shader by multiplying with 127.0, 255.0, 32767.0 or + 65535.0: + + - SG_VERTEXFORMAT_FLOAT, + - SG_VERTEXFORMAT_FLOAT2, + - SG_VERTEXFORMAT_FLOAT3, + - SG_VERTEXFORMAT_FLOAT4, + - SG_VERTEXFORMAT_BYTE4N, + - SG_VERTEXFORMAT_UBYTE4N, + - SG_VERTEXFORMAT_SHORT2N, + - SG_VERTEXFORMAT_USHORT2N + - SG_VERTEXFORMAT_SHORT4N, + - SG_VERTEXFORMAT_USHORT4N + - SG_VERTEXFORMAT_UINT10_N2 + - SG_VERTEXFORMAT_HALF2 + - SG_VERTEXFORMAT_HALF4 + + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + sg_setup(&(sg_desc){ + // ... + .allocator = { + .alloc_fn = my_alloc, + .free_fn = my_free, + .user_data = ..., + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_gfx.h + itself though, not any allocations in OS libraries. + + + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + sg_setup(&(sg_desc){ .logger.func = slog_func }); + + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'sg' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SG_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_gfx.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-gfx like this: + + sg_setup(&(sg_desc){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); + + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. + + + COMMIT LISTENERS + ================ + It's possible to hook callback functions into sokol-gfx which are called from + inside sg_commit() in unspecified order. This is mainly useful for libraries + that build on top of sokol_gfx.h to be notified about the end/start of a frame. + + To add a commit listener, call: + + static void my_commit_listener(void* user_data) { + ... + } + + bool success = sg_add_commit_listener((sg_commit_listener){ + .func = my_commit_listener, + .user_data = ..., + }); + + The function returns false if the internal array of commit listeners is full, + or the same commit listener had already been added. + + If the function returns true, my_commit_listener() will be called each frame + from inside sg_commit(). + + By default, 1024 distinct commit listeners can be added, but this number + can be tweaked in the sg_setup() call: + + sg_setup(&(sg_desc){ + .max_commit_listeners = 2048, + }); + + An sg_commit_listener item is equal to another if both the function + pointer and user_data field are equal. + + To remove a commit listener: + + bool success = sg_remove_commit_listener((sg_commit_listener){ + .func = my_commit_listener, + .user_data = ..., + }); + + ...where the .func and .user_data field are equal to a previous + sg_add_commit_listener() call. The function returns true if the commit + listener item was found and removed, and false otherwise. + + + RESOURCE CREATION AND DESTRUCTION IN DETAIL + =========================================== + The 'vanilla' way to create resource objects is with the 'make functions': + + sg_buffer sg_make_buffer(const sg_buffer_desc* desc) + sg_image sg_make_image(const sg_image_desc* desc) + sg_sampler sg_make_sampler(const sg_sampler_desc* desc) + sg_shader sg_make_shader(const sg_shader_desc* desc) + sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) + sg_attachments sg_make_attachments(const sg_attachments_desc* desc) + + This will result in one of three cases: + + 1. The returned handle is invalid. This happens when there are no more + free slots in the resource pool for this resource type. An invalid + handle is associated with the INVALID resource state, for instance: + + sg_buffer buf = sg_make_buffer(...) + if (sg_query_buffer_state(buf) == SG_RESOURCESTATE_INVALID) { + // buffer pool is exhausted + } + + 2. The returned handle is valid, but creating the underlying resource + has failed for some reason. This results in a resource object in the + FAILED state. The reason *why* resource creation has failed differ + by resource type. Look for log messages with more details. A failed + resource state can be checked with: + + sg_buffer buf = sg_make_buffer(...) + if (sg_query_buffer_state(buf) == SG_RESOURCESTATE_FAILED) { + // creating the resource has failed + } + + 3. And finally, if everything goes right, the returned resource is + in resource state VALID and ready to use. This can be checked + with: + + sg_buffer buf = sg_make_buffer(...) + if (sg_query_buffer_state(buf) == SG_RESOURCESTATE_VALID) { + // creating the resource has failed + } + + When calling the 'make functions', the created resource goes through a number + of states: + + - INITIAL: the resource slot associated with the new resource is currently + free (technically, there is no resource yet, just an empty pool slot) + - ALLOC: a handle for the new resource has been allocated, this just means + a pool slot has been reserved. + - VALID or FAILED: in VALID state any 3D API backend resource objects have + been successfully created, otherwise if anything went wrong, the resource + will be in FAILED state. + + Sometimes it makes sense to first grab a handle, but initialize the + underlying resource at a later time. For instance when loading data + asynchronously from a slow data source, you may know what buffers and + textures are needed at an early stage of the loading process, but actually + loading the buffer or texture content can only be completed at a later time. + + For such situations, sokol-gfx resource objects can be created in two steps. + You can allocate a handle upfront with one of the 'alloc functions': + + sg_buffer sg_alloc_buffer(void) + sg_image sg_alloc_image(void) + sg_sampler sg_alloc_sampler(void) + sg_shader sg_alloc_shader(void) + sg_pipeline sg_alloc_pipeline(void) + sg_attachments sg_alloc_attachments(void) + + This will return a handle with the underlying resource object in the + ALLOC state: + + sg_image img = sg_alloc_image(); + if (sg_query_image_state(img) == SG_RESOURCESTATE_ALLOC) { + // allocating an image handle has succeeded, otherwise + // the image pool is full + } + + Such an 'incomplete' handle can be used in most sokol-gfx rendering functions + without doing any harm, sokol-gfx will simply skip any rendering operation + that involve resources which are not in VALID state. + + At a later time (for instance once the texture has completed loading + asynchronously), the resource creation can be completed by calling one of + the 'init functions', those functions take an existing resource handle and + 'desc struct': + + void sg_init_buffer(sg_buffer buf, const sg_buffer_desc* desc) + void sg_init_image(sg_image img, const sg_image_desc* desc) + void sg_init_sampler(sg_sampler smp, const sg_sampler_desc* desc) + void sg_init_shader(sg_shader shd, const sg_shader_desc* desc) + void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc* desc) + void sg_init_attachments(sg_attachments atts, const sg_attachments_desc* desc) + + The init functions expect a resource in ALLOC state, and after the function + returns, the resource will be either in VALID or FAILED state. Calling + an 'alloc function' followed by the matching 'init function' is fully + equivalent with calling the 'make function' alone. + + Destruction can also happen as a two-step process. The 'uninit functions' + will put a resource object from the VALID or FAILED state back into the + ALLOC state: + + void sg_uninit_buffer(sg_buffer buf) + void sg_uninit_image(sg_image img) + void sg_uninit_sampler(sg_sampler smp) + void sg_uninit_shader(sg_shader shd) + void sg_uninit_pipeline(sg_pipeline pip) + void sg_uninit_attachments(sg_attachments pass) + + Calling the 'uninit functions' with a resource that is not in the VALID or + FAILED state is a no-op. + + To finally free the pool slot for recycling call the 'dealloc functions': + + void sg_dealloc_buffer(sg_buffer buf) + void sg_dealloc_image(sg_image img) + void sg_dealloc_sampler(sg_sampler smp) + void sg_dealloc_shader(sg_shader shd) + void sg_dealloc_pipeline(sg_pipeline pip) + void sg_dealloc_attachments(sg_attachments atts) + + Calling the 'dealloc functions' on a resource that's not in ALLOC state is + a no-op, but will generate a warning log message. + + Calling an 'uninit function' and 'dealloc function' in sequence is equivalent + with calling the associated 'destroy function': + + void sg_destroy_buffer(sg_buffer buf) + void sg_destroy_image(sg_image img) + void sg_destroy_sampler(sg_sampler smp) + void sg_destroy_shader(sg_shader shd) + void sg_destroy_pipeline(sg_pipeline pip) + void sg_destroy_attachments(sg_attachments atts) + + The 'destroy functions' can be called on resources in any state and generally + do the right thing (for instance if the resource is in ALLOC state, the destroy + function will be equivalent to the 'dealloc function' and skip the 'uninit part'). + + And finally to close the circle, the 'fail functions' can be called to manually + put a resource in ALLOC state into the FAILED state: + + sg_fail_buffer(sg_buffer buf) + sg_fail_image(sg_image img) + sg_fail_sampler(sg_sampler smp) + sg_fail_shader(sg_shader shd) + sg_fail_pipeline(sg_pipeline pip) + sg_fail_attachments(sg_attachments atts) + + This is recommended if anything went wrong outside of sokol-gfx during asynchronous + resource setup (for instance a file loading operation failed). In this case, + the 'fail function' should be called instead of the 'init function'. + + Calling a 'fail function' on a resource that's not in ALLOC state is a no-op, + but will generate a warning log message. + + NOTE: that two-step resource creation usually only makes sense for buffers + and images, but not for samplers, shaders, pipelines or attachments. Most notably, trying + to create a pipeline object with a shader that's not in VALID state will + trigger a validation layer error, or if the validation layer is disabled, + result in a pipeline object in FAILED state. Same when trying to create + an attachments object with invalid image objects. + + + WEBGPU CAVEATS + ============== + For a general overview and design notes of the WebGPU backend see: + + https://floooh.github.io/2023/10/16/sokol-webgpu.html + + In general, don't expect an automatic speedup when switching from the WebGL2 + backend to the WebGPU backend. Some WebGPU functions currently actually + have a higher CPU overhead than similar WebGL2 functions, leading to the + paradoxical situation that some WebGPU code may be slower than similar WebGL2 + code. + + - when writing WGSL shader code by hand, a specific bind-slot convention + must be used: + + All uniform block structs must use `@group(0)`, with up to + 4 uniform blocks per shader stage. + - Vertex shader uniform block bindings must start at `@group(0) @binding(0)` + - Fragment shader uniform blocks bindings must start at `@group(0) @binding(4)` + + All textures and samplers must use `@group(1)` and start at specific + offsets depending on resource type and shader stage. + - Vertex shader textures must start at `@group(1) @binding(0)` + - Vertex shader samplers must start at `@group(1) @binding(16)` + - Vertex shader storage buffers must start at `@group(1) @binding(32)` + - Fragment shader textures must start at `@group(1) @binding(48)` + - Fragment shader samplers must start at `@group(1) @binding(64)` + - Fragment shader storage buffers must start at `@group(1) @binding(80)` + + Note that the actual number of allowed per-stage texture- and sampler-bindings + in sokol-gfx is currently lower than the above ranges (currently only up to + 12 textures, 8 samplers and 8 storage buffers are allowed per shader stage). + + If you use sokol-shdc to generate WGSL shader code, you don't need to worry + about the above binding convention since sokol-shdc assigns bind slots + automatically. + + - The sokol-gfx WebGPU backend uses the sg_desc.uniform_buffer_size item + to allocate a single per-frame uniform buffer which must be big enough + to hold all data written by sg_apply_uniforms() during a single frame, + including a worst-case 256-byte alignment (e.g. each sg_apply_uniform + call will cost 256 bytes of uniform buffer size). The default size + is 4 MB, which is enough for 16384 sg_apply_uniform() calls per + frame (assuming the uniform data 'payload' is less than 256 bytes + per call). These rules are the same as for the Metal backend, so if + you are already using the Metal backend you'll be fine. + + - sg_apply_bindings(): the sokol-gfx WebGPU backend implements a bindgroup + cache to prevent excessive creation and destruction of BindGroup objects + when calling sg_apply_bindings(). The number of slots in the bindgroups + cache is defined in sg_desc.wgpu_bindgroups_cache_size when calling + sg_setup. The cache size must be a power-of-2 number, with the default being + 1024. The bindgroups cache behaviour can be observed by calling the new + function sg_query_frame_stats(), where the following struct items are + of interest: + + .wgpu.num_bindgroup_cache_hits + .wgpu.num_bindgroup_cache_misses + .wgpu.num_bindgroup_cache_collisions + .wgpu.num_bindgroup_cache_vs_hash_key_mismatch + + The value to pay attention to is `.wgpu.num_bindgroup_cache_collisions`, + if this number if consistently higher than a few percent of the + .wgpu.num_set_bindgroup value, it might be a good idea to bump the + bindgroups cache size to the next power-of-2. + + - sg_apply_viewport(): WebGPU currently has a unique restriction that viewport + rectangles must be contained entirely within the framebuffer. As a shitty + workaround sokol_gfx.h will clip incoming viewport rectangles against + the framebuffer, but this will distort the clipspace-to-screenspace mapping. + There's no proper way to handle this inside sokol_gfx.h, this must be fixed + in a future WebGPU update. + + - The sokol shader compiler generally adds `diagnostic(off, derivative_uniformity);` + into the WGSL output. Currently only the Chrome WebGPU implementation seems + to recognize this. + + - The vertex format SG_VERTEXFORMAT_UINT10_N2 is currently not supported because + WebGPU lacks a matching vertex format (this is currently being worked on though, + as soon as the vertex format shows up in webgpu.h, sokol_gfx.h will add support. + + - Likewise, the following sokol-gfx vertex formats are not supported in WebGPU: + R16, R16SN, RG16, RG16SN, RGBA16, RGBA16SN and all PVRTC compressed format. + Unlike unsupported vertex formats, unsupported pixel formats can be queried + in cross-backend code via sg_query_pixel_format() though. + + - The Emscripten WebGPU shim currently doesn't support the Closure minification + post-link-step (e.g. currently the emcc argument '--closure 1' or '--closure 2' + will generate broken Javascript code. + + - sokol-gfx requires the WebGPU device feature `depth32float-stencil8` to be enabled + (this should be widely supported) + + - sokol-gfx expects that the WebGPU device feature `float32-filterable` to *not* be + enabled (since this would exclude all iOS devices) + + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_GFX_INCLUDED (1) +#include // size_t +#include +#include + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_GFX_API_DECL) +#define SOKOL_GFX_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_GFX_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GFX_IMPL) +#define SOKOL_GFX_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_GFX_API_DECL __declspec(dllimport) +#else +#define SOKOL_GFX_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + Resource id typedefs: + + sg_buffer: vertex- and index-buffers + sg_image: images used as textures and render targets + sg_sampler sampler object describing how a texture is sampled in a shader + sg_shader: vertex- and fragment-shaders and shader interface information + sg_pipeline: associated shader and vertex-layouts, and render states + sg_attachments: a baked collection of render pass attachment images + + Instead of pointers, resource creation functions return a 32-bit + number which uniquely identifies the resource object. + + The 32-bit resource id is split into a 16-bit pool index in the lower bits, + and a 16-bit 'generation counter' in the upper bits. The index allows fast + pool lookups, and combined with the generation-counter it allows to detect + 'dangling accesses' (trying to use an object which no longer exists, and + its pool slot has been reused for a new object) + + The resource ids are wrapped into a strongly-typed struct so that + trying to pass an incompatible resource id is a compile error. +*/ +typedef struct sg_buffer { uint32_t id; } sg_buffer; +typedef struct sg_image { uint32_t id; } sg_image; +typedef struct sg_sampler { uint32_t id; } sg_sampler; +typedef struct sg_shader { uint32_t id; } sg_shader; +typedef struct sg_pipeline { uint32_t id; } sg_pipeline; +typedef struct sg_attachments { uint32_t id; } sg_attachments; + +/* + sg_range is a pointer-size-pair struct used to pass memory blobs into + sokol-gfx. When initialized from a value type (array or struct), you can + use the SG_RANGE() macro to build an sg_range struct. For functions which + take either a sg_range pointer, or a (C++) sg_range reference, use the + SG_RANGE_REF macro as a solution which compiles both in C and C++. +*/ +typedef struct sg_range { + const void* ptr; + size_t size; +} sg_range; + +// disabling this for every includer isn't great, but the warnings are also quite pointless +#if defined(_MSC_VER) +#pragma warning(disable:4221) // /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y' +#pragma warning(disable:4204) // VS2015: nonstandard extension used: non-constant aggregate initializer +#endif +#if defined(__cplusplus) +#define SG_RANGE(x) sg_range{ &x, sizeof(x) } +#define SG_RANGE_REF(x) sg_range{ &x, sizeof(x) } +#else +#define SG_RANGE(x) (sg_range){ &x, sizeof(x) } +#define SG_RANGE_REF(x) &(sg_range){ &x, sizeof(x) } +#endif + +// various compile-time constants +enum { + SG_INVALID_ID = 0, + SG_NUM_SHADER_STAGES = 2, + SG_NUM_INFLIGHT_FRAMES = 2, + SG_MAX_COLOR_ATTACHMENTS = 4, + SG_MAX_VERTEX_BUFFERS = 8, + SG_MAX_SHADERSTAGE_IMAGES = 12, + SG_MAX_SHADERSTAGE_SAMPLERS = 8, + SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS = 12, + SG_MAX_SHADERSTAGE_STORAGEBUFFERS = 8, + SG_MAX_SHADERSTAGE_UBS = 4, + SG_MAX_UB_MEMBERS = 16, + SG_MAX_VERTEX_ATTRIBUTES = 16, + SG_MAX_MIPMAPS = 16, + SG_MAX_TEXTUREARRAY_LAYERS = 128 +}; + +/* + sg_color + + An RGBA color value. +*/ +typedef struct sg_color { float r, g, b, a; } sg_color; + +/* + sg_backend + + The active 3D-API backend, use the function sg_query_backend() + to get the currently active backend. +*/ +typedef enum sg_backend { + SG_BACKEND_GLCORE, + SG_BACKEND_GLES3, + SG_BACKEND_D3D11, + SG_BACKEND_METAL_IOS, + SG_BACKEND_METAL_MACOS, + SG_BACKEND_METAL_SIMULATOR, + SG_BACKEND_WGPU, + SG_BACKEND_DUMMY, +} sg_backend; + +/* + sg_pixel_format + + sokol_gfx.h basically uses the same pixel formats as WebGPU, since these + are supported on most newer GPUs. + + A pixelformat name consist of three parts: + + - components (R, RG, RGB or RGBA) + - bit width per component (8, 16 or 32) + - component data type: + - unsigned normalized (no postfix) + - signed normalized (SN postfix) + - unsigned integer (UI postfix) + - signed integer (SI postfix) + - float (F postfix) + + Not all pixel formats can be used for everything, call sg_query_pixelformat() + to inspect the capabilities of a given pixelformat. The function returns + an sg_pixelformat_info struct with the following members: + + - sample: the pixelformat can be sampled as texture at least with + nearest filtering + - filter: the pixelformat can be samples as texture with linear + filtering + - render: the pixelformat can be used for render targets + - blend: blending is supported when using the pixelformat for + render targets + - msaa: multisample-antialiasing is supported when using the + pixelformat for render targets + - depth: the pixelformat can be used for depth-stencil attachments + - compressed: this is a block-compressed format + - bytes_per_pixel: the numbers of bytes in a pixel (0 for compressed formats) + + The default pixel format for texture images is SG_PIXELFORMAT_RGBA8. + + The default pixel format for render target images is platform-dependent + and taken from the sg_environment struct passed into sg_setup(). Typically + the default formats are: + + - for the Metal, D3D11 and WebGPU backends: SG_PIXELFORMAT_BGRA8 + - for GL backends: SG_PIXELFORMAT_RGBA8 +*/ +typedef enum sg_pixel_format { + _SG_PIXELFORMAT_DEFAULT, // value 0 reserved for default-init + SG_PIXELFORMAT_NONE, + + SG_PIXELFORMAT_R8, + SG_PIXELFORMAT_R8SN, + SG_PIXELFORMAT_R8UI, + SG_PIXELFORMAT_R8SI, + + SG_PIXELFORMAT_R16, + SG_PIXELFORMAT_R16SN, + SG_PIXELFORMAT_R16UI, + SG_PIXELFORMAT_R16SI, + SG_PIXELFORMAT_R16F, + SG_PIXELFORMAT_RG8, + SG_PIXELFORMAT_RG8SN, + SG_PIXELFORMAT_RG8UI, + SG_PIXELFORMAT_RG8SI, + + SG_PIXELFORMAT_R32UI, + SG_PIXELFORMAT_R32SI, + SG_PIXELFORMAT_R32F, + SG_PIXELFORMAT_RG16, + SG_PIXELFORMAT_RG16SN, + SG_PIXELFORMAT_RG16UI, + SG_PIXELFORMAT_RG16SI, + SG_PIXELFORMAT_RG16F, + SG_PIXELFORMAT_RGBA8, + SG_PIXELFORMAT_SRGB8A8, + SG_PIXELFORMAT_RGBA8SN, + SG_PIXELFORMAT_RGBA8UI, + SG_PIXELFORMAT_RGBA8SI, + SG_PIXELFORMAT_BGRA8, + SG_PIXELFORMAT_RGB10A2, + SG_PIXELFORMAT_RG11B10F, + SG_PIXELFORMAT_RGB9E5, + + SG_PIXELFORMAT_RG32UI, + SG_PIXELFORMAT_RG32SI, + SG_PIXELFORMAT_RG32F, + SG_PIXELFORMAT_RGBA16, + SG_PIXELFORMAT_RGBA16SN, + SG_PIXELFORMAT_RGBA16UI, + SG_PIXELFORMAT_RGBA16SI, + SG_PIXELFORMAT_RGBA16F, + + SG_PIXELFORMAT_RGBA32UI, + SG_PIXELFORMAT_RGBA32SI, + SG_PIXELFORMAT_RGBA32F, + + // NOTE: when adding/removing pixel formats before DEPTH, also update sokol_app.h/_SAPP_PIXELFORMAT_* + SG_PIXELFORMAT_DEPTH, + SG_PIXELFORMAT_DEPTH_STENCIL, + + // NOTE: don't put any new compressed format in front of here + SG_PIXELFORMAT_BC1_RGBA, + SG_PIXELFORMAT_BC2_RGBA, + SG_PIXELFORMAT_BC3_RGBA, + SG_PIXELFORMAT_BC3_SRGBA, + SG_PIXELFORMAT_BC4_R, + SG_PIXELFORMAT_BC4_RSN, + SG_PIXELFORMAT_BC5_RG, + SG_PIXELFORMAT_BC5_RGSN, + SG_PIXELFORMAT_BC6H_RGBF, + SG_PIXELFORMAT_BC6H_RGBUF, + SG_PIXELFORMAT_BC7_RGBA, + SG_PIXELFORMAT_BC7_SRGBA, + SG_PIXELFORMAT_PVRTC_RGB_2BPP, // FIXME: deprecated + SG_PIXELFORMAT_PVRTC_RGB_4BPP, // FIXME: deprecated + SG_PIXELFORMAT_PVRTC_RGBA_2BPP, // FIXME: deprecated + SG_PIXELFORMAT_PVRTC_RGBA_4BPP, // FIXME: deprecated + SG_PIXELFORMAT_ETC2_RGB8, + SG_PIXELFORMAT_ETC2_SRGB8, + SG_PIXELFORMAT_ETC2_RGB8A1, + SG_PIXELFORMAT_ETC2_RGBA8, + SG_PIXELFORMAT_ETC2_SRGB8A8, + SG_PIXELFORMAT_EAC_R11, + SG_PIXELFORMAT_EAC_R11SN, + SG_PIXELFORMAT_EAC_RG11, + SG_PIXELFORMAT_EAC_RG11SN, + + SG_PIXELFORMAT_ASTC_4x4_RGBA, + SG_PIXELFORMAT_ASTC_4x4_SRGBA, + + _SG_PIXELFORMAT_NUM, + _SG_PIXELFORMAT_FORCE_U32 = 0x7FFFFFFF +} sg_pixel_format; + +/* + Runtime information about a pixel format, returned + by sg_query_pixelformat(). +*/ +typedef struct sg_pixelformat_info { + bool sample; // pixel format can be sampled in shaders at least with nearest filtering + bool filter; // pixel format can be sampled with linear filtering + bool render; // pixel format can be used as render target + bool blend; // alpha-blending is supported + bool msaa; // pixel format can be used as MSAA render target + bool depth; // pixel format is a depth format + bool compressed; // true if this is a hardware-compressed format + int bytes_per_pixel; // NOTE: this is 0 for compressed formats, use sg_query_row_pitch() / sg_query_surface_pitch() as alternative +} sg_pixelformat_info; + +/* + Runtime information about available optional features, + returned by sg_query_features() +*/ +typedef struct sg_features { + bool origin_top_left; // framebuffer and texture origin is in top left corner + bool image_clamp_to_border; // border color and clamp-to-border UV-wrap mode is supported + bool mrt_independent_blend_state; // multiple-render-target rendering can use per-render-target blend state + bool mrt_independent_write_mask; // multiple-render-target rendering can use per-render-target color write masks + bool storage_buffer; // storage buffers are supported +} sg_features; + +/* + Runtime information about resource limits, returned by sg_query_limit() +*/ +typedef struct sg_limits { + int max_image_size_2d; // max width/height of SG_IMAGETYPE_2D images + int max_image_size_cube; // max width/height of SG_IMAGETYPE_CUBE images + int max_image_size_3d; // max width/height/depth of SG_IMAGETYPE_3D images + int max_image_size_array; // max width/height of SG_IMAGETYPE_ARRAY images + int max_image_array_layers; // max number of layers in SG_IMAGETYPE_ARRAY images + int max_vertex_attrs; // max number of vertex attributes, clamped to SG_MAX_VERTEX_ATTRIBUTES + int gl_max_vertex_uniform_components; // <= GL_MAX_VERTEX_UNIFORM_COMPONENTS (only on GL backends) + int gl_max_combined_texture_image_units; // <= GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS (only on GL backends) +} sg_limits; + +/* + sg_resource_state + + The current state of a resource in its resource pool. + Resources start in the INITIAL state, which means the + pool slot is unoccupied and can be allocated. When a resource is + created, first an id is allocated, and the resource pool slot + is set to state ALLOC. After allocation, the resource is + initialized, which may result in the VALID or FAILED state. The + reason why allocation and initialization are separate is because + some resource types (e.g. buffers and images) might be asynchronously + initialized by the user application. If a resource which is not + in the VALID state is attempted to be used for rendering, rendering + operations will silently be dropped. + + The special INVALID state is returned in sg_query_xxx_state() if no + resource object exists for the provided resource id. +*/ +typedef enum sg_resource_state { + SG_RESOURCESTATE_INITIAL, + SG_RESOURCESTATE_ALLOC, + SG_RESOURCESTATE_VALID, + SG_RESOURCESTATE_FAILED, + SG_RESOURCESTATE_INVALID, + _SG_RESOURCESTATE_FORCE_U32 = 0x7FFFFFFF +} sg_resource_state; + +/* + sg_usage + + A resource usage hint describing the update strategy of + buffers and images. This is used in the sg_buffer_desc.usage + and sg_image_desc.usage members when creating buffers + and images: + + SG_USAGE_IMMUTABLE: the resource will never be updated with + new data, instead the content of the + resource must be provided on creation + SG_USAGE_DYNAMIC: the resource will be updated infrequently + with new data (this could range from "once + after creation", to "quite often but not + every frame") + SG_USAGE_STREAM: the resource will be updated each frame + with new content + + The rendering backends use this hint to prevent that the + CPU needs to wait for the GPU when attempting to update + a resource that might be currently accessed by the GPU. + + Resource content is updated with the functions sg_update_buffer() or + sg_append_buffer() for buffer objects, and sg_update_image() for image + objects. For the sg_update_*() functions, only one update is allowed per + frame and resource object, while sg_append_buffer() can be called + multiple times per frame on the same buffer. The application must update + all data required for rendering (this means that the update data can be + smaller than the resource size, if only a part of the overall resource + size is used for rendering, you only need to make sure that the data that + *is* used is valid). + + The default usage is SG_USAGE_IMMUTABLE. +*/ +typedef enum sg_usage { + _SG_USAGE_DEFAULT, // value 0 reserved for default-init + SG_USAGE_IMMUTABLE, + SG_USAGE_DYNAMIC, + SG_USAGE_STREAM, + _SG_USAGE_NUM, + _SG_USAGE_FORCE_U32 = 0x7FFFFFFF +} sg_usage; + +/* + sg_buffer_type + + Indicates whether a buffer will be bound as vertex-, + index- or storage-buffer. + + Used in the sg_buffer_desc.type member when creating a buffer. + + The default value is SG_BUFFERTYPE_VERTEXBUFFER. +*/ +typedef enum sg_buffer_type { + _SG_BUFFERTYPE_DEFAULT, // value 0 reserved for default-init + SG_BUFFERTYPE_VERTEXBUFFER, + SG_BUFFERTYPE_INDEXBUFFER, + SG_BUFFERTYPE_STORAGEBUFFER, + _SG_BUFFERTYPE_NUM, + _SG_BUFFERTYPE_FORCE_U32 = 0x7FFFFFFF +} sg_buffer_type; + +/* + sg_index_type + + Indicates whether indexed rendering (fetching vertex-indices from an + index buffer) is used, and if yes, the index data type (16- or 32-bits). + This is used in the sg_pipeline_desc.index_type member when creating a + pipeline object. + + The default index type is SG_INDEXTYPE_NONE. +*/ +typedef enum sg_index_type { + _SG_INDEXTYPE_DEFAULT, // value 0 reserved for default-init + SG_INDEXTYPE_NONE, + SG_INDEXTYPE_UINT16, + SG_INDEXTYPE_UINT32, + _SG_INDEXTYPE_NUM, + _SG_INDEXTYPE_FORCE_U32 = 0x7FFFFFFF +} sg_index_type; + +/* + sg_image_type + + Indicates the basic type of an image object (2D-texture, cubemap, + 3D-texture or 2D-array-texture). Used in the sg_image_desc.type member when + creating an image, and in sg_shader_image_desc to describe a sampled texture + in the shader (both must match and will be checked in the validation layer + when calling sg_apply_bindings). + + The default image type when creating an image is SG_IMAGETYPE_2D. +*/ +typedef enum sg_image_type { + _SG_IMAGETYPE_DEFAULT, // value 0 reserved for default-init + SG_IMAGETYPE_2D, + SG_IMAGETYPE_CUBE, + SG_IMAGETYPE_3D, + SG_IMAGETYPE_ARRAY, + _SG_IMAGETYPE_NUM, + _SG_IMAGETYPE_FORCE_U32 = 0x7FFFFFFF +} sg_image_type; + +/* + sg_image_sample_type + + The basic data type of a texture sample as expected by a shader. + Must be provided in sg_shader_image_desc and used by the validation + layer in sg_apply_bindings() to check if the provided image object + is compatible with what the shader expects. Apart from the sokol-gfx + validation layer, WebGPU is the only backend API which actually requires + matching texture and sampler type to be provided upfront for validation + (other 3D APIs treat texture/sampler type mismatches as undefined behaviour). + + NOTE that the following texture pixel formats require the use + of SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT, combined with a sampler + of type SG_SAMPLERTYPE_NONFILTERING: + + - SG_PIXELFORMAT_R32F + - SG_PIXELFORMAT_RG32F + - SG_PIXELFORMAT_RGBA32F + + (when using sokol-shdc, also check out the meta tags `@image_sample_type` + and `@sampler_type`) +*/ +typedef enum sg_image_sample_type { + _SG_IMAGESAMPLETYPE_DEFAULT, // value 0 reserved for default-init + SG_IMAGESAMPLETYPE_FLOAT, + SG_IMAGESAMPLETYPE_DEPTH, + SG_IMAGESAMPLETYPE_SINT, + SG_IMAGESAMPLETYPE_UINT, + SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT, + _SG_IMAGESAMPLETYPE_NUM, + _SG_IMAGESAMPLETYPE_FORCE_U32 = 0x7FFFFFFF +} sg_image_sample_type; + +/* + sg_sampler_type + + The basic type of a texture sampler (sampling vs comparison) as + defined in a shader. Must be provided in sg_shader_sampler_desc. + + sg_image_sample_type and sg_sampler_type for a texture/sampler + pair must be compatible with each other, specifically only + the following pairs are allowed: + + - SG_IMAGESAMPLETYPE_FLOAT => (SG_SAMPLERTYPE_FILTERING or SG_SAMPLERTYPE_NONFILTERING) + - SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT => SG_SAMPLERTYPE_NONFILTERING + - SG_IMAGESAMPLETYPE_SINT => SG_SAMPLERTYPE_NONFILTERING + - SG_IMAGESAMPLETYPE_UINT => SG_SAMPLERTYPE_NONFILTERING + - SG_IMAGESAMPLETYPE_DEPTH => SG_SAMPLERTYPE_COMPARISON +*/ +typedef enum sg_sampler_type { + _SG_SAMPLERTYPE_DEFAULT, + SG_SAMPLERTYPE_FILTERING, + SG_SAMPLERTYPE_NONFILTERING, + SG_SAMPLERTYPE_COMPARISON, + _SG_SAMPLERTYPE_NUM, + _SG_SAMPLERTYPE_FORCE_U32, +} sg_sampler_type; + +/* + sg_cube_face + + The cubemap faces. Use these as indices in the sg_image_desc.content + array. +*/ +typedef enum sg_cube_face { + SG_CUBEFACE_POS_X, + SG_CUBEFACE_NEG_X, + SG_CUBEFACE_POS_Y, + SG_CUBEFACE_NEG_Y, + SG_CUBEFACE_POS_Z, + SG_CUBEFACE_NEG_Z, + SG_CUBEFACE_NUM, + _SG_CUBEFACE_FORCE_U32 = 0x7FFFFFFF +} sg_cube_face; + +/* + sg_shader_stage + + There are 2 shader stages: vertex- and fragment-shader-stage. + Each shader stage + + - SG_MAX_SHADERSTAGE_UBS slots for applying uniform data + - SG_MAX_SHADERSTAGE_IMAGES slots for images used as textures + - SG_MAX_SHADERSTAGE_SAMPLERS slots for texture samplers + - SG_MAX_SHADERSTAGE_STORAGEBUFFERS slots for storage buffer bindings +*/ +typedef enum sg_shader_stage { + SG_SHADERSTAGE_VS, + SG_SHADERSTAGE_FS, + _SG_SHADERSTAGE_FORCE_U32 = 0x7FFFFFFF +} sg_shader_stage; + +/* + sg_primitive_type + + This is the common subset of 3D primitive types supported across all 3D + APIs. This is used in the sg_pipeline_desc.primitive_type member when + creating a pipeline object. + + The default primitive type is SG_PRIMITIVETYPE_TRIANGLES. +*/ +typedef enum sg_primitive_type { + _SG_PRIMITIVETYPE_DEFAULT, // value 0 reserved for default-init + SG_PRIMITIVETYPE_POINTS, + SG_PRIMITIVETYPE_LINES, + SG_PRIMITIVETYPE_LINE_STRIP, + SG_PRIMITIVETYPE_TRIANGLES, + SG_PRIMITIVETYPE_TRIANGLE_STRIP, + _SG_PRIMITIVETYPE_NUM, + _SG_PRIMITIVETYPE_FORCE_U32 = 0x7FFFFFFF +} sg_primitive_type; + +/* + sg_filter + + The filtering mode when sampling a texture image. This is + used in the sg_sampler_desc.min_filter, sg_sampler_desc.mag_filter + and sg_sampler_desc.mipmap_filter members when creating a sampler object. + + For min_filter and mag_filter the default is SG_FILTER_NEAREST. + + For mipmap_filter the default is SG_FILTER_NONE. +*/ +typedef enum sg_filter { + _SG_FILTER_DEFAULT, // value 0 reserved for default-init + SG_FILTER_NONE, // FIXME: deprecated + SG_FILTER_NEAREST, + SG_FILTER_LINEAR, + _SG_FILTER_NUM, + _SG_FILTER_FORCE_U32 = 0x7FFFFFFF +} sg_filter; + +/* + sg_wrap + + The texture coordinates wrapping mode when sampling a texture + image. This is used in the sg_image_desc.wrap_u, .wrap_v + and .wrap_w members when creating an image. + + The default wrap mode is SG_WRAP_REPEAT. + + NOTE: SG_WRAP_CLAMP_TO_BORDER is not supported on all backends + and platforms. To check for support, call sg_query_features() + and check the "clamp_to_border" boolean in the returned + sg_features struct. + + Platforms which don't support SG_WRAP_CLAMP_TO_BORDER will silently fall back + to SG_WRAP_CLAMP_TO_EDGE without a validation error. +*/ +typedef enum sg_wrap { + _SG_WRAP_DEFAULT, // value 0 reserved for default-init + SG_WRAP_REPEAT, + SG_WRAP_CLAMP_TO_EDGE, + SG_WRAP_CLAMP_TO_BORDER, + SG_WRAP_MIRRORED_REPEAT, + _SG_WRAP_NUM, + _SG_WRAP_FORCE_U32 = 0x7FFFFFFF +} sg_wrap; + +/* + sg_border_color + + The border color to use when sampling a texture, and the UV wrap + mode is SG_WRAP_CLAMP_TO_BORDER. + + The default border color is SG_BORDERCOLOR_OPAQUE_BLACK +*/ +typedef enum sg_border_color { + _SG_BORDERCOLOR_DEFAULT, // value 0 reserved for default-init + SG_BORDERCOLOR_TRANSPARENT_BLACK, + SG_BORDERCOLOR_OPAQUE_BLACK, + SG_BORDERCOLOR_OPAQUE_WHITE, + _SG_BORDERCOLOR_NUM, + _SG_BORDERCOLOR_FORCE_U32 = 0x7FFFFFFF +} sg_border_color; + +/* + sg_vertex_format + + The data type of a vertex component. This is used to describe + the layout of vertex data when creating a pipeline object. +*/ +typedef enum sg_vertex_format { + SG_VERTEXFORMAT_INVALID, + SG_VERTEXFORMAT_FLOAT, + SG_VERTEXFORMAT_FLOAT2, + SG_VERTEXFORMAT_FLOAT3, + SG_VERTEXFORMAT_FLOAT4, + SG_VERTEXFORMAT_BYTE4, + SG_VERTEXFORMAT_BYTE4N, + SG_VERTEXFORMAT_UBYTE4, + SG_VERTEXFORMAT_UBYTE4N, + SG_VERTEXFORMAT_SHORT2, + SG_VERTEXFORMAT_SHORT2N, + SG_VERTEXFORMAT_USHORT2N, + SG_VERTEXFORMAT_SHORT4, + SG_VERTEXFORMAT_SHORT4N, + SG_VERTEXFORMAT_USHORT4N, + SG_VERTEXFORMAT_UINT10_N2, + SG_VERTEXFORMAT_HALF2, + SG_VERTEXFORMAT_HALF4, + _SG_VERTEXFORMAT_NUM, + _SG_VERTEXFORMAT_FORCE_U32 = 0x7FFFFFFF +} sg_vertex_format; + +/* + sg_vertex_step + + Defines whether the input pointer of a vertex input stream is advanced + 'per vertex' or 'per instance'. The default step-func is + SG_VERTEXSTEP_PER_VERTEX. SG_VERTEXSTEP_PER_INSTANCE is used with + instanced-rendering. + + The vertex-step is part of the vertex-layout definition + when creating pipeline objects. +*/ +typedef enum sg_vertex_step { + _SG_VERTEXSTEP_DEFAULT, // value 0 reserved for default-init + SG_VERTEXSTEP_PER_VERTEX, + SG_VERTEXSTEP_PER_INSTANCE, + _SG_VERTEXSTEP_NUM, + _SG_VERTEXSTEP_FORCE_U32 = 0x7FFFFFFF +} sg_vertex_step; + +/* + sg_uniform_type + + The data type of a uniform block member. This is used to + describe the internal layout of uniform blocks when creating + a shader object. +*/ +typedef enum sg_uniform_type { + SG_UNIFORMTYPE_INVALID, + SG_UNIFORMTYPE_FLOAT, + SG_UNIFORMTYPE_FLOAT2, + SG_UNIFORMTYPE_FLOAT3, + SG_UNIFORMTYPE_FLOAT4, + SG_UNIFORMTYPE_INT, + SG_UNIFORMTYPE_INT2, + SG_UNIFORMTYPE_INT3, + SG_UNIFORMTYPE_INT4, + SG_UNIFORMTYPE_MAT4, + _SG_UNIFORMTYPE_NUM, + _SG_UNIFORMTYPE_FORCE_U32 = 0x7FFFFFFF +} sg_uniform_type; + +/* + sg_uniform_layout + + A hint for the interior memory layout of uniform blocks. This is + only really relevant for the GL backend where the internal layout + of uniform blocks must be known to sokol-gfx. For all other backends the + internal memory layout of uniform blocks doesn't matter, sokol-gfx + will just pass uniform data as a single memory blob to the + 3D backend. + + SG_UNIFORMLAYOUT_NATIVE (default) + Native layout means that a 'backend-native' memory layout + is used. For the GL backend this means that uniforms + are packed tightly in memory (e.g. there are no padding + bytes). + + SG_UNIFORMLAYOUT_STD140 + The memory layout is a subset of std140. Arrays are only + allowed for the FLOAT4, INT4 and MAT4. Alignment is as + is as follows: + + FLOAT, INT: 4 byte alignment + FLOAT2, INT2: 8 byte alignment + FLOAT3, INT3: 16 byte alignment(!) + FLOAT4, INT4: 16 byte alignment + MAT4: 16 byte alignment + FLOAT4[], INT4[]: 16 byte alignment + + The overall size of the uniform block must be a multiple + of 16. + + For more information search for 'UNIFORM DATA LAYOUT' in the documentation block + at the start of the header. +*/ +typedef enum sg_uniform_layout { + _SG_UNIFORMLAYOUT_DEFAULT, // value 0 reserved for default-init + SG_UNIFORMLAYOUT_NATIVE, // default: layout depends on currently active backend + SG_UNIFORMLAYOUT_STD140, // std140: memory layout according to std140 + _SG_UNIFORMLAYOUT_NUM, + _SG_UNIFORMLAYOUT_FORCE_U32 = 0x7FFFFFFF +} sg_uniform_layout; + +/* + sg_cull_mode + + The face-culling mode, this is used in the + sg_pipeline_desc.cull_mode member when creating a + pipeline object. + + The default cull mode is SG_CULLMODE_NONE +*/ +typedef enum sg_cull_mode { + _SG_CULLMODE_DEFAULT, // value 0 reserved for default-init + SG_CULLMODE_NONE, + SG_CULLMODE_FRONT, + SG_CULLMODE_BACK, + _SG_CULLMODE_NUM, + _SG_CULLMODE_FORCE_U32 = 0x7FFFFFFF +} sg_cull_mode; + +/* + sg_face_winding + + The vertex-winding rule that determines a front-facing primitive. This + is used in the member sg_pipeline_desc.face_winding + when creating a pipeline object. + + The default winding is SG_FACEWINDING_CW (clockwise) +*/ +typedef enum sg_face_winding { + _SG_FACEWINDING_DEFAULT, // value 0 reserved for default-init + SG_FACEWINDING_CCW, + SG_FACEWINDING_CW, + _SG_FACEWINDING_NUM, + _SG_FACEWINDING_FORCE_U32 = 0x7FFFFFFF +} sg_face_winding; + +/* + sg_compare_func + + The compare-function for configuring depth- and stencil-ref tests + in pipeline objects, and for texture samplers which perform a comparison + instead of regular sampling operation. + + sg_pipeline_desc + .depth + .compare + .stencil + .front.compare + .back.compar + + sg_sampler_desc + .compare + + The default compare func for depth- and stencil-tests is + SG_COMPAREFUNC_ALWAYS. + + The default compare func for sampler is SG_COMPAREFUNC_NEVER. +*/ +typedef enum sg_compare_func { + _SG_COMPAREFUNC_DEFAULT, // value 0 reserved for default-init + SG_COMPAREFUNC_NEVER, + SG_COMPAREFUNC_LESS, + SG_COMPAREFUNC_EQUAL, + SG_COMPAREFUNC_LESS_EQUAL, + SG_COMPAREFUNC_GREATER, + SG_COMPAREFUNC_NOT_EQUAL, + SG_COMPAREFUNC_GREATER_EQUAL, + SG_COMPAREFUNC_ALWAYS, + _SG_COMPAREFUNC_NUM, + _SG_COMPAREFUNC_FORCE_U32 = 0x7FFFFFFF +} sg_compare_func; + +/* + sg_stencil_op + + The operation performed on a currently stored stencil-value when a + comparison test passes or fails. This is used when creating a pipeline + object in the members: + + sg_pipeline_desc + .stencil + .front + .fail_op + .depth_fail_op + .pass_op + .back + .fail_op + .depth_fail_op + .pass_op + + The default value is SG_STENCILOP_KEEP. +*/ +typedef enum sg_stencil_op { + _SG_STENCILOP_DEFAULT, // value 0 reserved for default-init + SG_STENCILOP_KEEP, + SG_STENCILOP_ZERO, + SG_STENCILOP_REPLACE, + SG_STENCILOP_INCR_CLAMP, + SG_STENCILOP_DECR_CLAMP, + SG_STENCILOP_INVERT, + SG_STENCILOP_INCR_WRAP, + SG_STENCILOP_DECR_WRAP, + _SG_STENCILOP_NUM, + _SG_STENCILOP_FORCE_U32 = 0x7FFFFFFF +} sg_stencil_op; + +/* + sg_blend_factor + + The source and destination factors in blending operations. + This is used in the following members when creating a pipeline object: + + sg_pipeline_desc + .colors[i] + .blend + .src_factor_rgb + .dst_factor_rgb + .src_factor_alpha + .dst_factor_alpha + + The default value is SG_BLENDFACTOR_ONE for source + factors, and SG_BLENDFACTOR_ZERO for destination factors. +*/ +typedef enum sg_blend_factor { + _SG_BLENDFACTOR_DEFAULT, // value 0 reserved for default-init + SG_BLENDFACTOR_ZERO, + SG_BLENDFACTOR_ONE, + SG_BLENDFACTOR_SRC_COLOR, + SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR, + SG_BLENDFACTOR_SRC_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, + SG_BLENDFACTOR_DST_COLOR, + SG_BLENDFACTOR_ONE_MINUS_DST_COLOR, + SG_BLENDFACTOR_DST_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA, + SG_BLENDFACTOR_SRC_ALPHA_SATURATED, + SG_BLENDFACTOR_BLEND_COLOR, + SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR, + SG_BLENDFACTOR_BLEND_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA, + _SG_BLENDFACTOR_NUM, + _SG_BLENDFACTOR_FORCE_U32 = 0x7FFFFFFF +} sg_blend_factor; + +/* + sg_blend_op + + Describes how the source and destination values are combined in the + fragment blending operation. It is used in the following members when + creating a pipeline object: + + sg_pipeline_desc + .colors[i] + .blend + .op_rgb + .op_alpha + + The default value is SG_BLENDOP_ADD. +*/ +typedef enum sg_blend_op { + _SG_BLENDOP_DEFAULT, // value 0 reserved for default-init + SG_BLENDOP_ADD, + SG_BLENDOP_SUBTRACT, + SG_BLENDOP_REVERSE_SUBTRACT, + _SG_BLENDOP_NUM, + _SG_BLENDOP_FORCE_U32 = 0x7FFFFFFF +} sg_blend_op; + +/* + sg_color_mask + + Selects the active color channels when writing a fragment color to the + framebuffer. This is used in the members + sg_pipeline_desc.colors[i].write_mask when creating a pipeline object. + + The default colormask is SG_COLORMASK_RGBA (write all colors channels) + + NOTE: since the color mask value 0 is reserved for the default value + (SG_COLORMASK_RGBA), use SG_COLORMASK_NONE if all color channels + should be disabled. +*/ +typedef enum sg_color_mask { + _SG_COLORMASK_DEFAULT = 0, // value 0 reserved for default-init + SG_COLORMASK_NONE = 0x10, // special value for 'all channels disabled + SG_COLORMASK_R = 0x1, + SG_COLORMASK_G = 0x2, + SG_COLORMASK_RG = 0x3, + SG_COLORMASK_B = 0x4, + SG_COLORMASK_RB = 0x5, + SG_COLORMASK_GB = 0x6, + SG_COLORMASK_RGB = 0x7, + SG_COLORMASK_A = 0x8, + SG_COLORMASK_RA = 0x9, + SG_COLORMASK_GA = 0xA, + SG_COLORMASK_RGA = 0xB, + SG_COLORMASK_BA = 0xC, + SG_COLORMASK_RBA = 0xD, + SG_COLORMASK_GBA = 0xE, + SG_COLORMASK_RGBA = 0xF, + _SG_COLORMASK_FORCE_U32 = 0x7FFFFFFF +} sg_color_mask; + +/* + sg_load_action + + Defines the load action that should be performed at the start of a render pass: + + SG_LOADACTION_CLEAR: clear the render target + SG_LOADACTION_LOAD: load the previous content of the render target + SG_LOADACTION_DONTCARE: leave the render target in an undefined state + + This is used in the sg_pass_action structure. + + The default load action for all pass attachments is SG_LOADACTION_CLEAR, + with the values rgba = { 0.5f, 0.5f, 0.5f, 1.0f }, depth=1.0f and stencil=0. + + If you want to override the default behaviour, it is important to not + only set the clear color, but the 'action' field as well (as long as this + is _SG_LOADACTION_DEFAULT, the value fields will be ignored). +*/ +typedef enum sg_load_action { + _SG_LOADACTION_DEFAULT, + SG_LOADACTION_CLEAR, + SG_LOADACTION_LOAD, + SG_LOADACTION_DONTCARE, + _SG_LOADACTION_FORCE_U32 = 0x7FFFFFFF +} sg_load_action; + +/* + sg_store_action + + Defines the store action that be performed at the end of a render pass: + + SG_STOREACTION_STORE: store the rendered content to the color attachment image + SG_STOREACTION_DONTCARE: allows the GPU to discard the rendered content +*/ +typedef enum sg_store_action { + _SG_STOREACTION_DEFAULT, + SG_STOREACTION_STORE, + SG_STOREACTION_DONTCARE, + _SG_STOREACTION_FORCE_U32 = 0x7FFFFFFF +} sg_store_action; + + +/* + sg_pass_action + + The sg_pass_action struct defines the actions to be performed + at the start and end of a render pass. + + - at the start of the pass: whether the render targets should be cleared, + loaded with their previous content, or start in an undefined state + - for clear operations: the clear value (color, depth, or stencil values) + - at the end of the pass: whether the rendering result should be + stored back into the render target or discarded +*/ +typedef struct sg_color_attachment_action { + sg_load_action load_action; // default: SG_LOADACTION_CLEAR + sg_store_action store_action; // default: SG_STOREACTION_STORE + sg_color clear_value; // default: { 0.5f, 0.5f, 0.5f, 1.0f } +} sg_color_attachment_action; + +typedef struct sg_depth_attachment_action { + sg_load_action load_action; // default: SG_LOADACTION_CLEAR + sg_store_action store_action; // default: SG_STOREACTION_DONTCARE + float clear_value; // default: 1.0 +} sg_depth_attachment_action; + +typedef struct sg_stencil_attachment_action { + sg_load_action load_action; // default: SG_LOADACTION_CLEAR + sg_store_action store_action; // default: SG_STOREACTION_DONTCARE + uint8_t clear_value; // default: 0 +} sg_stencil_attachment_action; + +typedef struct sg_pass_action { + sg_color_attachment_action colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_depth_attachment_action depth; + sg_stencil_attachment_action stencil; +} sg_pass_action; + +/* + sg_swapchain + + Used in sg_begin_pass() to provide details about an external swapchain + (pixel formats, sample count and backend-API specific render surface objects). + + The following information must be provided: + + - the width and height of the swapchain surfaces in number of pixels, + - the pixel format of the render- and optional msaa-resolve-surface + - the pixel format of the optional depth- or depth-stencil-surface + - the MSAA sample count for the render and depth-stencil surface + + If the pixel formats and MSAA sample counts are left zero-initialized, + their defaults are taken from the sg_environment struct provided in the + sg_setup() call. + + The width and height *must* be > 0. + + Additionally the following backend API specific objects must be passed in + as 'type erased' void pointers: + + GL: on all GL backends, a GL framebuffer object must be provided. This + can be zero for the default framebuffer. + + D3D11: + - an ID3D11RenderTargetView for the rendering surface, without + MSAA rendering this surface will also be displayed + - an optional ID3D11DepthStencilView for the depth- or depth/stencil + buffer surface + - when MSAA rendering is used, another ID3D11RenderTargetView + which serves as MSAA resolve target and will be displayed + + WebGPU (same as D3D11, except different types) + - a WGPUTextureView for the rendering surface, without + MSAA rendering this surface will also be displayed + - an optional WGPUTextureView for the depth- or depth/stencil + buffer surface + - when MSAA rendering is used, another WGPUTextureView + which serves as MSAA resolve target and will be displayed + + Metal (NOTE that the rolves of provided surfaces is slightly different + than on D3D11 or WebGPU in case of MSAA vs non-MSAA rendering): + + - A current CAMetalDrawable (NOT an MTLDrawable!) which will be presented. + This will either be rendered to directly (if no MSAA is used), or serve + as MSAA-resolve target. + - an optional MTLTexture for the depth- or depth-stencil buffer + - an optional multisampled MTLTexture which serves as intermediate + rendering surface which will then be resolved into the + CAMetalDrawable. + + NOTE that for Metal you must use an ObjC __bridge cast to + properly tunnel the ObjC object handle through a C void*, e.g.: + + swapchain.metal.current_drawable = (__bridge const void*) [mtkView currentDrawable]; + + On all other backends you shouldn't need to mess with the reference count. + + It's a good practice to write a helper function which returns an initialized + sg_swapchain structs, which can then be plugged directly into + sg_pass.swapchain. Look at the function sglue_swapchain() in the sokol_glue.h + as an example. +*/ +typedef struct sg_metal_swapchain { + const void* current_drawable; // CAMetalDrawable (NOT MTLDrawable!!!) + const void* depth_stencil_texture; // MTLTexture + const void* msaa_color_texture; // MTLTexture +} sg_metal_swapchain; + +typedef struct sg_d3d11_swapchain { + const void* render_view; // ID3D11RenderTargetView + const void* resolve_view; // ID3D11RenderTargetView + const void* depth_stencil_view; // ID3D11DepthStencilView +} sg_d3d11_swapchain; + +typedef struct sg_wgpu_swapchain { + const void* render_view; // WGPUTextureView + const void* resolve_view; // WGPUTextureView + const void* depth_stencil_view; // WGPUTextureView +} sg_wgpu_swapchain; + +typedef struct sg_gl_swapchain { + uint32_t framebuffer; // GL framebuffer object +} sg_gl_swapchain; + +typedef struct sg_swapchain { + int width; + int height; + int sample_count; + sg_pixel_format color_format; + sg_pixel_format depth_format; + sg_metal_swapchain metal; + sg_d3d11_swapchain d3d11; + sg_wgpu_swapchain wgpu; + sg_gl_swapchain gl; +} sg_swapchain; + +/* + sg_pass + + The sg_pass structure is passed as argument into the sg_begin_pass() + function. + + For an offscreen rendering pass, an sg_pass_action struct and sg_attachments + object must be provided, and for swapchain passes, and sg_pass_action and + an sg_swapchain struct. It is an error to provide both an sg_attachments + handle and an initialized sg_swapchain struct in the same sg_begin_pass(). + + An sg_begin_pass() call for an offscreen pass would look like this (where + `attachments` is an sg_attachments handle): + + sg_begin_pass(&(sg_pass){ + .action = { ... }, + .attachments = attachments, + }); + + ...and a swapchain render pass would look like this (using the sokol_glue.h + helper function sglue_swapchain() which gets the swapchain properties from + sokol_app.h): + + sg_begin_pass(&(sg_pass){ + .action = { ... }, + .swapchain = sglue_swapchain(), + }); + + You can also omit the .action object to get default pass action behaviour + (clear to color=grey, depth=1 and stencil=0). +*/ +typedef struct sg_pass { + uint32_t _start_canary; + sg_pass_action action; + sg_attachments attachments; + sg_swapchain swapchain; + const char* label; + uint32_t _end_canary; +} sg_pass; + +/* + sg_bindings + + The sg_bindings structure defines the resource binding slots + of the sokol_gfx render pipeline, used as argument to the + sg_apply_bindings() function. + + A resource binding struct contains: + + - 1..N vertex buffers + - 0..N vertex buffer offsets + - 0..1 index buffers + - 0..1 index buffer offsets + - 0..N vertex shader stage images + - 0..N vertex shader stage samplers + - 0..N vertex shader storage buffers + - 0..N fragment shader stage images + - 0..N fragment shader stage samplers + - 0..N fragment shader storage buffers + + For the max number of bindings, see the constant definitions: + + - SG_MAX_VERTEX_BUFFERS + - SG_MAX_SHADERSTAGE_IMAGES + - SG_MAX_SHADERSTAGE_SAMPLERS + - SG_MAX_SHADERSTAGE_STORAGEBUFFERS + + The optional buffer offsets can be used to put different unrelated + chunks of vertex- and/or index-data into the same buffer objects. +*/ +typedef struct sg_stage_bindings { + sg_image images[SG_MAX_SHADERSTAGE_IMAGES]; + sg_sampler samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; + sg_buffer storage_buffers[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; +} sg_stage_bindings; + +typedef struct sg_bindings { + uint32_t _start_canary; + sg_buffer vertex_buffers[SG_MAX_VERTEX_BUFFERS]; + int vertex_buffer_offsets[SG_MAX_VERTEX_BUFFERS]; + sg_buffer index_buffer; + int index_buffer_offset; + sg_stage_bindings vs; + sg_stage_bindings fs; + uint32_t _end_canary; +} sg_bindings; + +/* + sg_buffer_desc + + Creation parameters for sg_buffer objects, used in the + sg_make_buffer() call. + + The default configuration is: + + .size: 0 (*must* be >0 for buffers without data) + .type: SG_BUFFERTYPE_VERTEXBUFFER + .usage: SG_USAGE_IMMUTABLE + .data.ptr 0 (*must* be valid for immutable buffers) + .data.size 0 (*must* be > 0 for immutable buffers) + .label 0 (optional string label) + + For immutable buffers which are initialized with initial data, + keep the .size item zero-initialized, and set the size together with the + pointer to the initial data in the .data item. + + For mutable buffers without initial data, keep the .data item + zero-initialized, and set the buffer size in the .size item instead. + + You can also set both size values, but currently both size values must + be identical (this may change in the future when the dynamic resource + management may become more flexible). + + ADVANCED TOPIC: Injecting native 3D-API buffers: + + The following struct members allow to inject your own GL, Metal + or D3D11 buffers into sokol_gfx: + + .gl_buffers[SG_NUM_INFLIGHT_FRAMES] + .mtl_buffers[SG_NUM_INFLIGHT_FRAMES] + .d3d11_buffer + + You must still provide all other struct items except the .data item, and + these must match the creation parameters of the native buffers you + provide. For SG_USAGE_IMMUTABLE, only provide a single native 3D-API + buffer, otherwise you need to provide SG_NUM_INFLIGHT_FRAMES buffers + (only for GL and Metal, not D3D11). Providing multiple buffers for GL and + Metal is necessary because sokol_gfx will rotate through them when + calling sg_update_buffer() to prevent lock-stalls. + + Note that it is expected that immutable injected buffer have already been + initialized with content, and the .content member must be 0! + + Also you need to call sg_reset_state_cache() after calling native 3D-API + functions, and before calling any sokol_gfx function. +*/ +typedef struct sg_buffer_desc { + uint32_t _start_canary; + size_t size; + sg_buffer_type type; + sg_usage usage; + sg_range data; + const char* label; + // optionally inject backend-specific resources + uint32_t gl_buffers[SG_NUM_INFLIGHT_FRAMES]; + const void* mtl_buffers[SG_NUM_INFLIGHT_FRAMES]; + const void* d3d11_buffer; + const void* wgpu_buffer; + uint32_t _end_canary; +} sg_buffer_desc; + +/* + sg_image_data + + Defines the content of an image through a 2D array of sg_range structs. + The first array dimension is the cubemap face, and the second array + dimension the mipmap level. +*/ +typedef struct sg_image_data { + sg_range subimage[SG_CUBEFACE_NUM][SG_MAX_MIPMAPS]; +} sg_image_data; + +/* + sg_image_desc + + Creation parameters for sg_image objects, used in the sg_make_image() call. + + The default configuration is: + + .type: SG_IMAGETYPE_2D + .render_target: false + .width 0 (must be set to >0) + .height 0 (must be set to >0) + .num_slices 1 (3D textures: depth; array textures: number of layers) + .num_mipmaps: 1 + .usage: SG_USAGE_IMMUTABLE + .pixel_format: SG_PIXELFORMAT_RGBA8 for textures, or sg_desc.environment.defaults.color_format for render targets + .sample_count: 1 for textures, or sg_desc.environment.defaults.sample_count for render targets + .data an sg_image_data struct to define the initial content + .label 0 (optional string label for trace hooks) + + Q: Why is the default sample_count for render targets identical with the + "default sample count" from sg_desc.environment.defaults.sample_count? + + A: So that it matches the default sample count in pipeline objects. Even + though it is a bit strange/confusing that offscreen render targets by default + get the same sample count as 'default swapchains', but it's better that + an offscreen render target created with default parameters matches + a pipeline object created with default parameters. + + NOTE: + + Images with usage SG_USAGE_IMMUTABLE must be fully initialized by + providing a valid .data member which points to initialization data. + + ADVANCED TOPIC: Injecting native 3D-API textures: + + The following struct members allow to inject your own GL, Metal or D3D11 + textures into sokol_gfx: + + .gl_textures[SG_NUM_INFLIGHT_FRAMES] + .mtl_textures[SG_NUM_INFLIGHT_FRAMES] + .d3d11_texture + .d3d11_shader_resource_view + .wgpu_texture + .wgpu_texture_view + + For GL, you can also specify the texture target or leave it empty to use + the default texture target for the image type (GL_TEXTURE_2D for + SG_IMAGETYPE_2D etc) + + For D3D11 and WebGPU, either only provide a texture, or both a texture and + shader-resource-view / texture-view object. If you want to use access the + injected texture in a shader you *must* provide a shader-resource-view. + + The same rules apply as for injecting native buffers (see sg_buffer_desc + documentation for more details). +*/ +typedef struct sg_image_desc { + uint32_t _start_canary; + sg_image_type type; + bool render_target; + int width; + int height; + int num_slices; + int num_mipmaps; + sg_usage usage; + sg_pixel_format pixel_format; + int sample_count; + sg_image_data data; + const char* label; + // optionally inject backend-specific resources + uint32_t gl_textures[SG_NUM_INFLIGHT_FRAMES]; + uint32_t gl_texture_target; + const void* mtl_textures[SG_NUM_INFLIGHT_FRAMES]; + const void* d3d11_texture; + const void* d3d11_shader_resource_view; + const void* wgpu_texture; + const void* wgpu_texture_view; + uint32_t _end_canary; +} sg_image_desc; + +/* + sg_sampler_desc + + Creation parameters for sg_sampler objects, used in the sg_make_sampler() call + + .min_filter: SG_FILTER_NEAREST + .mag_filter: SG_FILTER_NEAREST + .mipmap_filter SG_FILTER_NONE + .wrap_u: SG_WRAP_REPEAT + .wrap_v: SG_WRAP_REPEAT + .wrap_w: SG_WRAP_REPEAT (only SG_IMAGETYPE_3D) + .min_lod 0.0f + .max_lod FLT_MAX + .border_color SG_BORDERCOLOR_OPAQUE_BLACK + .compare SG_COMPAREFUNC_NEVER + .max_anisotropy 1 (must be 1..16) + +*/ +typedef struct sg_sampler_desc { + uint32_t _start_canary; + sg_filter min_filter; + sg_filter mag_filter; + sg_filter mipmap_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + float min_lod; + float max_lod; + sg_border_color border_color; + sg_compare_func compare; + uint32_t max_anisotropy; + const char* label; + // optionally inject backend-specific resources + uint32_t gl_sampler; + const void* mtl_sampler; + const void* d3d11_sampler; + const void* wgpu_sampler; + uint32_t _end_canary; +} sg_sampler_desc; + +/* + sg_shader_desc + + The structure sg_shader_desc defines all creation parameters for shader + programs, used as input to the sg_make_shader() function: + + - reflection information for vertex attributes (vertex shader inputs): + - vertex attribute name (only optionally used by GLES3 and GL) + - a semantic name and index (required for D3D11) + - for each shader-stage (vertex and fragment): + - the shader source or bytecode + - an optional entry function name + - an optional compile target (only for D3D11 when source is provided, + defaults are "vs_4_0" and "ps_4_0") + - reflection info for each uniform block used by the shader stage: + - the size of the uniform block in bytes + - a memory layout hint (native vs std140, only required for GL backends) + - reflection info for each uniform block member (only required for GL backends): + - member name + - member type (SG_UNIFORMTYPE_xxx) + - if the member is an array, the number of array items + - reflection info for textures used in the shader stage: + - the image type (SG_IMAGETYPE_xxx) + - the image-sample type (SG_IMAGESAMPLETYPE_xxx, default is SG_IMAGESAMPLETYPE_FLOAT) + - whether the shader expects a multisampled texture + - reflection info for samplers used in the shader stage: + - the sampler type (SG_SAMPLERTYPE_xxx) + - reflection info for each image-sampler-pair used by the shader: + - the texture slot of the involved texture + - the sampler slot of the involved sampler + - for GLSL only: the name of the combined image-sampler object + - reflection info for each storage-buffer used by the shader: + - whether the storage buffer is readonly (currently this + must be true) + + For all GL backends, shader source-code must be provided. For D3D11 and Metal, + either shader source-code or byte-code can be provided. + + For D3D11, if source code is provided, the d3dcompiler_47.dll will be loaded + on demand. If this fails, shader creation will fail. When compiling HLSL + source code, you can provide an optional target string via + sg_shader_stage_desc.d3d11_target, the default target is "vs_4_0" for the + vertex shader stage and "ps_4_0" for the pixel shader stage. +*/ +typedef struct sg_shader_attr_desc { + const char* name; // GLSL vertex attribute name (optional) + const char* sem_name; // HLSL semantic name + int sem_index; // HLSL semantic index +} sg_shader_attr_desc; + +typedef struct sg_shader_uniform_desc { + const char* name; + sg_uniform_type type; + int array_count; +} sg_shader_uniform_desc; + +typedef struct sg_shader_uniform_block_desc { + size_t size; + sg_uniform_layout layout; + sg_shader_uniform_desc uniforms[SG_MAX_UB_MEMBERS]; +} sg_shader_uniform_block_desc; + +typedef struct sg_shader_storage_buffer_desc { + bool used; + bool readonly; +} sg_shader_storage_buffer_desc; + +typedef struct sg_shader_image_desc { + bool used; + bool multisampled; + sg_image_type image_type; + sg_image_sample_type sample_type; +} sg_shader_image_desc; + +typedef struct sg_shader_sampler_desc { + bool used; + sg_sampler_type sampler_type; +} sg_shader_sampler_desc; + +typedef struct sg_shader_image_sampler_pair_desc { + bool used; + int image_slot; + int sampler_slot; + const char* glsl_name; +} sg_shader_image_sampler_pair_desc; + +typedef struct sg_shader_stage_desc { + const char* source; + sg_range bytecode; + const char* entry; + const char* d3d11_target; + sg_shader_uniform_block_desc uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + sg_shader_storage_buffer_desc storage_buffers[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; + sg_shader_image_desc images[SG_MAX_SHADERSTAGE_IMAGES]; + sg_shader_sampler_desc samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; + sg_shader_image_sampler_pair_desc image_sampler_pairs[SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS]; +} sg_shader_stage_desc; + +typedef struct sg_shader_desc { + uint32_t _start_canary; + sg_shader_attr_desc attrs[SG_MAX_VERTEX_ATTRIBUTES]; + sg_shader_stage_desc vs; + sg_shader_stage_desc fs; + const char* label; + uint32_t _end_canary; +} sg_shader_desc; + +/* + sg_pipeline_desc + + The sg_pipeline_desc struct defines all creation parameters for an + sg_pipeline object, used as argument to the sg_make_pipeline() function: + + - the vertex layout for all input vertex buffers + - a shader object + - the 3D primitive type (points, lines, triangles, ...) + - the index type (none, 16- or 32-bit) + - all the fixed-function-pipeline state (depth-, stencil-, blend-state, etc...) + + If the vertex data has no gaps between vertex components, you can omit + the .layout.buffers[].stride and layout.attrs[].offset items (leave them + default-initialized to 0), sokol-gfx will then compute the offsets and + strides from the vertex component formats (.layout.attrs[].format). + Please note that ALL vertex attribute offsets must be 0 in order for the + automatic offset computation to kick in. + + The default configuration is as follows: + + .shader: 0 (must be initialized with a valid sg_shader id!) + .layout: + .buffers[]: vertex buffer layouts + .stride: 0 (if no stride is given it will be computed) + .step_func SG_VERTEXSTEP_PER_VERTEX + .step_rate 1 + .attrs[]: vertex attribute declarations + .buffer_index 0 the vertex buffer bind slot + .offset 0 (offsets can be omitted if the vertex layout has no gaps) + .format SG_VERTEXFORMAT_INVALID (must be initialized!) + .depth: + .pixel_format: sg_desc.context.depth_format + .compare: SG_COMPAREFUNC_ALWAYS + .write_enabled: false + .bias: 0.0f + .bias_slope_scale: 0.0f + .bias_clamp: 0.0f + .stencil: + .enabled: false + .front/back: + .compare: SG_COMPAREFUNC_ALWAYS + .fail_op: SG_STENCILOP_KEEP + .depth_fail_op: SG_STENCILOP_KEEP + .pass_op: SG_STENCILOP_KEEP + .read_mask: 0 + .write_mask: 0 + .ref: 0 + .color_count 1 + .colors[0..color_count] + .pixel_format sg_desc.context.color_format + .write_mask: SG_COLORMASK_RGBA + .blend: + .enabled: false + .src_factor_rgb: SG_BLENDFACTOR_ONE + .dst_factor_rgb: SG_BLENDFACTOR_ZERO + .op_rgb: SG_BLENDOP_ADD + .src_factor_alpha: SG_BLENDFACTOR_ONE + .dst_factor_alpha: SG_BLENDFACTOR_ZERO + .op_alpha: SG_BLENDOP_ADD + .primitive_type: SG_PRIMITIVETYPE_TRIANGLES + .index_type: SG_INDEXTYPE_NONE + .cull_mode: SG_CULLMODE_NONE + .face_winding: SG_FACEWINDING_CW + .sample_count: sg_desc.context.sample_count + .blend_color: (sg_color) { 0.0f, 0.0f, 0.0f, 0.0f } + .alpha_to_coverage_enabled: false + .label 0 (optional string label for trace hooks) +*/ +typedef struct sg_vertex_buffer_layout_state { + int stride; + sg_vertex_step step_func; + int step_rate; +} sg_vertex_buffer_layout_state; + +typedef struct sg_vertex_attr_state { + int buffer_index; + int offset; + sg_vertex_format format; +} sg_vertex_attr_state; + +typedef struct sg_vertex_layout_state { + sg_vertex_buffer_layout_state buffers[SG_MAX_VERTEX_BUFFERS]; + sg_vertex_attr_state attrs[SG_MAX_VERTEX_ATTRIBUTES]; +} sg_vertex_layout_state; + +typedef struct sg_stencil_face_state { + sg_compare_func compare; + sg_stencil_op fail_op; + sg_stencil_op depth_fail_op; + sg_stencil_op pass_op; +} sg_stencil_face_state; + +typedef struct sg_stencil_state { + bool enabled; + sg_stencil_face_state front; + sg_stencil_face_state back; + uint8_t read_mask; + uint8_t write_mask; + uint8_t ref; +} sg_stencil_state; + +typedef struct sg_depth_state { + sg_pixel_format pixel_format; + sg_compare_func compare; + bool write_enabled; + float bias; + float bias_slope_scale; + float bias_clamp; +} sg_depth_state; + +typedef struct sg_blend_state { + bool enabled; + sg_blend_factor src_factor_rgb; + sg_blend_factor dst_factor_rgb; + sg_blend_op op_rgb; + sg_blend_factor src_factor_alpha; + sg_blend_factor dst_factor_alpha; + sg_blend_op op_alpha; +} sg_blend_state; + +typedef struct sg_color_target_state { + sg_pixel_format pixel_format; + sg_color_mask write_mask; + sg_blend_state blend; +} sg_color_target_state; + +typedef struct sg_pipeline_desc { + uint32_t _start_canary; + sg_shader shader; + sg_vertex_layout_state layout; + sg_depth_state depth; + sg_stencil_state stencil; + int color_count; + sg_color_target_state colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_primitive_type primitive_type; + sg_index_type index_type; + sg_cull_mode cull_mode; + sg_face_winding face_winding; + int sample_count; + sg_color blend_color; + bool alpha_to_coverage_enabled; + const char* label; + uint32_t _end_canary; +} sg_pipeline_desc; + +/* + sg_attachments_desc + + Creation parameters for an sg_attachments object, used as argument to the + sg_make_attachments() function. + + An attachments object bundles 0..4 color attachments, 0..4 msaa-resolve + attachments, and none or one depth-stencil attachmente for use + in a render pass. At least one color attachment or one depth-stencil + attachment must be provided (no color attachment and a depth-stencil + attachment is useful for a depth-only render pass). + + Each attachment definition consists of an image object, and two additional indices + describing which subimage the pass will render into: one mipmap index, and if the image + is a cubemap, array-texture or 3D-texture, the face-index, array-layer or + depth-slice. + + All attachments must have the same width and height. + + All color attachments and the depth-stencil attachment must have the + same sample count. + + If a resolve attachment is set, an MSAA-resolve operation from the + associated color attachment image into the resolve attachment image will take + place in the sg_end_pass() function. In this case, the color attachment + must have a (sample_count>1), and the resolve attachment a + (sample_count==1). The resolve attachment also must have the same pixel + format as the color attachment. + + NOTE that MSAA depth-stencil attachments cannot be msaa-resolved! +*/ +typedef struct sg_attachment_desc { + sg_image image; + int mip_level; + int slice; // cube texture: face; array texture: layer; 3D texture: slice +} sg_attachment_desc; + +typedef struct sg_attachments_desc { + uint32_t _start_canary; + sg_attachment_desc colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_attachment_desc resolves[SG_MAX_COLOR_ATTACHMENTS]; + sg_attachment_desc depth_stencil; + const char* label; + uint32_t _end_canary; +} sg_attachments_desc; + +/* + sg_trace_hooks + + Installable callback functions to keep track of the sokol-gfx calls, + this is useful for debugging, or keeping track of resource creation + and destruction. + + Trace hooks are installed with sg_install_trace_hooks(), this returns + another sg_trace_hooks struct with the previous set of + trace hook function pointers. These should be invoked by the + new trace hooks to form a proper call chain. +*/ +typedef struct sg_trace_hooks { + void* user_data; + void (*reset_state_cache)(void* user_data); + void (*make_buffer)(const sg_buffer_desc* desc, sg_buffer result, void* user_data); + void (*make_image)(const sg_image_desc* desc, sg_image result, void* user_data); + void (*make_sampler)(const sg_sampler_desc* desc, sg_sampler result, void* user_data); + void (*make_shader)(const sg_shader_desc* desc, sg_shader result, void* user_data); + void (*make_pipeline)(const sg_pipeline_desc* desc, sg_pipeline result, void* user_data); + void (*make_attachments)(const sg_attachments_desc* desc, sg_attachments result, void* user_data); + void (*destroy_buffer)(sg_buffer buf, void* user_data); + void (*destroy_image)(sg_image img, void* user_data); + void (*destroy_sampler)(sg_sampler smp, void* user_data); + void (*destroy_shader)(sg_shader shd, void* user_data); + void (*destroy_pipeline)(sg_pipeline pip, void* user_data); + void (*destroy_attachments)(sg_attachments atts, void* user_data); + void (*update_buffer)(sg_buffer buf, const sg_range* data, void* user_data); + void (*update_image)(sg_image img, const sg_image_data* data, void* user_data); + void (*append_buffer)(sg_buffer buf, const sg_range* data, int result, void* user_data); + void (*begin_pass)(const sg_pass* pass, void* user_data); + void (*apply_viewport)(int x, int y, int width, int height, bool origin_top_left, void* user_data); + void (*apply_scissor_rect)(int x, int y, int width, int height, bool origin_top_left, void* user_data); + void (*apply_pipeline)(sg_pipeline pip, void* user_data); + void (*apply_bindings)(const sg_bindings* bindings, void* user_data); + void (*apply_uniforms)(sg_shader_stage stage, int ub_index, const sg_range* data, void* user_data); + void (*draw)(int base_element, int num_elements, int num_instances, void* user_data); + void (*end_pass)(void* user_data); + void (*commit)(void* user_data); + void (*alloc_buffer)(sg_buffer result, void* user_data); + void (*alloc_image)(sg_image result, void* user_data); + void (*alloc_sampler)(sg_sampler result, void* user_data); + void (*alloc_shader)(sg_shader result, void* user_data); + void (*alloc_pipeline)(sg_pipeline result, void* user_data); + void (*alloc_attachments)(sg_attachments result, void* user_data); + void (*dealloc_buffer)(sg_buffer buf_id, void* user_data); + void (*dealloc_image)(sg_image img_id, void* user_data); + void (*dealloc_sampler)(sg_sampler smp_id, void* user_data); + void (*dealloc_shader)(sg_shader shd_id, void* user_data); + void (*dealloc_pipeline)(sg_pipeline pip_id, void* user_data); + void (*dealloc_attachments)(sg_attachments atts_id, void* user_data); + void (*init_buffer)(sg_buffer buf_id, const sg_buffer_desc* desc, void* user_data); + void (*init_image)(sg_image img_id, const sg_image_desc* desc, void* user_data); + void (*init_sampler)(sg_sampler smp_id, const sg_sampler_desc* desc, void* user_data); + void (*init_shader)(sg_shader shd_id, const sg_shader_desc* desc, void* user_data); + void (*init_pipeline)(sg_pipeline pip_id, const sg_pipeline_desc* desc, void* user_data); + void (*init_attachments)(sg_attachments atts_id, const sg_attachments_desc* desc, void* user_data); + void (*uninit_buffer)(sg_buffer buf_id, void* user_data); + void (*uninit_image)(sg_image img_id, void* user_data); + void (*uninit_sampler)(sg_sampler smp_id, void* user_data); + void (*uninit_shader)(sg_shader shd_id, void* user_data); + void (*uninit_pipeline)(sg_pipeline pip_id, void* user_data); + void (*uninit_attachments)(sg_attachments atts_id, void* user_data); + void (*fail_buffer)(sg_buffer buf_id, void* user_data); + void (*fail_image)(sg_image img_id, void* user_data); + void (*fail_sampler)(sg_sampler smp_id, void* user_data); + void (*fail_shader)(sg_shader shd_id, void* user_data); + void (*fail_pipeline)(sg_pipeline pip_id, void* user_data); + void (*fail_attachments)(sg_attachments atts_id, void* user_data); + void (*push_debug_group)(const char* name, void* user_data); + void (*pop_debug_group)(void* user_data); +} sg_trace_hooks; + +/* + sg_buffer_info + sg_image_info + sg_sampler_info + sg_shader_info + sg_pipeline_info + sg_attachments_info + + These structs contain various internal resource attributes which + might be useful for debug-inspection. Please don't rely on the + actual content of those structs too much, as they are quite closely + tied to sokol_gfx.h internals and may change more frequently than + the other public API elements. + + The *_info structs are used as the return values of the following functions: + + sg_query_buffer_info() + sg_query_image_info() + sg_query_sampler_info() + sg_query_shader_info() + sg_query_pipeline_info() + sg_query_pass_info() +*/ +typedef struct sg_slot_info { + sg_resource_state state; // the current state of this resource slot + uint32_t res_id; // type-neutral resource if (e.g. sg_buffer.id) +} sg_slot_info; + +typedef struct sg_buffer_info { + sg_slot_info slot; // resource pool slot info + uint32_t update_frame_index; // frame index of last sg_update_buffer() + uint32_t append_frame_index; // frame index of last sg_append_buffer() + int append_pos; // current position in buffer for sg_append_buffer() + bool append_overflow; // is buffer in overflow state (due to sg_append_buffer) + int num_slots; // number of renaming-slots for dynamically updated buffers + int active_slot; // currently active write-slot for dynamically updated buffers +} sg_buffer_info; + +typedef struct sg_image_info { + sg_slot_info slot; // resource pool slot info + uint32_t upd_frame_index; // frame index of last sg_update_image() + int num_slots; // number of renaming-slots for dynamically updated images + int active_slot; // currently active write-slot for dynamically updated images +} sg_image_info; + +typedef struct sg_sampler_info { + sg_slot_info slot; // resource pool slot info +} sg_sampler_info; + +typedef struct sg_shader_info { + sg_slot_info slot; // resource pool slot info +} sg_shader_info; + +typedef struct sg_pipeline_info { + sg_slot_info slot; // resource pool slot info +} sg_pipeline_info; + +typedef struct sg_attachments_info { + sg_slot_info slot; // resource pool slot info +} sg_attachments_info; + +/* + sg_frame_stats + + Allows to track generic and backend-specific stats about a + render frame. Obtained by calling sg_query_frame_stats(). The returned + struct contains information about the *previous* frame. +*/ +typedef struct sg_frame_stats_gl { + uint32_t num_bind_buffer; + uint32_t num_active_texture; + uint32_t num_bind_texture; + uint32_t num_bind_sampler; + uint32_t num_use_program; + uint32_t num_render_state; + uint32_t num_vertex_attrib_pointer; + uint32_t num_vertex_attrib_divisor; + uint32_t num_enable_vertex_attrib_array; + uint32_t num_disable_vertex_attrib_array; + uint32_t num_uniform; +} sg_frame_stats_gl; + +typedef struct sg_frame_stats_d3d11_pass { + uint32_t num_om_set_render_targets; + uint32_t num_clear_render_target_view; + uint32_t num_clear_depth_stencil_view; + uint32_t num_resolve_subresource; +} sg_frame_stats_d3d11_pass; + +typedef struct sg_frame_stats_d3d11_pipeline { + uint32_t num_rs_set_state; + uint32_t num_om_set_depth_stencil_state; + uint32_t num_om_set_blend_state; + uint32_t num_ia_set_primitive_topology; + uint32_t num_ia_set_input_layout; + uint32_t num_vs_set_shader; + uint32_t num_vs_set_constant_buffers; + uint32_t num_ps_set_shader; + uint32_t num_ps_set_constant_buffers; +} sg_frame_stats_d3d11_pipeline; + +typedef struct sg_frame_stats_d3d11_bindings { + uint32_t num_ia_set_vertex_buffers; + uint32_t num_ia_set_index_buffer; + uint32_t num_vs_set_shader_resources; + uint32_t num_ps_set_shader_resources; + uint32_t num_vs_set_samplers; + uint32_t num_ps_set_samplers; +} sg_frame_stats_d3d11_bindings; + +typedef struct sg_frame_stats_d3d11_uniforms { + uint32_t num_update_subresource; +} sg_frame_stats_d3d11_uniforms; + +typedef struct sg_frame_stats_d3d11_draw { + uint32_t num_draw_indexed_instanced; + uint32_t num_draw_indexed; + uint32_t num_draw_instanced; + uint32_t num_draw; +} sg_frame_stats_d3d11_draw; + +typedef struct sg_frame_stats_d3d11 { + sg_frame_stats_d3d11_pass pass; + sg_frame_stats_d3d11_pipeline pipeline; + sg_frame_stats_d3d11_bindings bindings; + sg_frame_stats_d3d11_uniforms uniforms; + sg_frame_stats_d3d11_draw draw; + uint32_t num_map; + uint32_t num_unmap; +} sg_frame_stats_d3d11; + +typedef struct sg_frame_stats_metal_idpool { + uint32_t num_added; + uint32_t num_released; + uint32_t num_garbage_collected; +} sg_frame_stats_metal_idpool; + +typedef struct sg_frame_stats_metal_pipeline { + uint32_t num_set_blend_color; + uint32_t num_set_cull_mode; + uint32_t num_set_front_facing_winding; + uint32_t num_set_stencil_reference_value; + uint32_t num_set_depth_bias; + uint32_t num_set_render_pipeline_state; + uint32_t num_set_depth_stencil_state; +} sg_frame_stats_metal_pipeline; + +typedef struct sg_frame_stats_metal_bindings { + uint32_t num_set_vertex_buffer; + uint32_t num_set_vertex_texture; + uint32_t num_set_vertex_sampler_state; + uint32_t num_set_fragment_buffer; + uint32_t num_set_fragment_texture; + uint32_t num_set_fragment_sampler_state; +} sg_frame_stats_metal_bindings; + +typedef struct sg_frame_stats_metal_uniforms { + uint32_t num_set_vertex_buffer_offset; + uint32_t num_set_fragment_buffer_offset; +} sg_frame_stats_metal_uniforms; + +typedef struct sg_frame_stats_metal { + sg_frame_stats_metal_idpool idpool; + sg_frame_stats_metal_pipeline pipeline; + sg_frame_stats_metal_bindings bindings; + sg_frame_stats_metal_uniforms uniforms; +} sg_frame_stats_metal; + +typedef struct sg_frame_stats_wgpu_uniforms { + uint32_t num_set_bindgroup; + uint32_t size_write_buffer; +} sg_frame_stats_wgpu_uniforms; + +typedef struct sg_frame_stats_wgpu_bindings { + uint32_t num_set_vertex_buffer; + uint32_t num_skip_redundant_vertex_buffer; + uint32_t num_set_index_buffer; + uint32_t num_skip_redundant_index_buffer; + uint32_t num_create_bindgroup; + uint32_t num_discard_bindgroup; + uint32_t num_set_bindgroup; + uint32_t num_skip_redundant_bindgroup; + uint32_t num_bindgroup_cache_hits; + uint32_t num_bindgroup_cache_misses; + uint32_t num_bindgroup_cache_collisions; + uint32_t num_bindgroup_cache_hash_vs_key_mismatch; +} sg_frame_stats_wgpu_bindings; + +typedef struct sg_frame_stats_wgpu { + sg_frame_stats_wgpu_uniforms uniforms; + sg_frame_stats_wgpu_bindings bindings; +} sg_frame_stats_wgpu; + +typedef struct sg_frame_stats { + uint32_t frame_index; // current frame counter, starts at 0 + + uint32_t num_passes; + uint32_t num_apply_viewport; + uint32_t num_apply_scissor_rect; + uint32_t num_apply_pipeline; + uint32_t num_apply_bindings; + uint32_t num_apply_uniforms; + uint32_t num_draw; + uint32_t num_update_buffer; + uint32_t num_append_buffer; + uint32_t num_update_image; + + uint32_t size_apply_uniforms; + uint32_t size_update_buffer; + uint32_t size_append_buffer; + uint32_t size_update_image; + + sg_frame_stats_gl gl; + sg_frame_stats_d3d11 d3d11; + sg_frame_stats_metal metal; + sg_frame_stats_wgpu wgpu; +} sg_frame_stats; + +/* + sg_log_item + + An enum with a unique item for each log message, warning, error + and validation layer message. +*/ +#define _SG_LOG_ITEMS \ + _SG_LOGITEM_XMACRO(OK, "Ok") \ + _SG_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SG_LOGITEM_XMACRO(GL_TEXTURE_FORMAT_NOT_SUPPORTED, "pixel format not supported for texture (gl)") \ + _SG_LOGITEM_XMACRO(GL_3D_TEXTURES_NOT_SUPPORTED, "3d textures not supported (gl)") \ + _SG_LOGITEM_XMACRO(GL_ARRAY_TEXTURES_NOT_SUPPORTED, "array textures not supported (gl)") \ + _SG_LOGITEM_XMACRO(GL_SHADER_COMPILATION_FAILED, "shader compilation failed (gl)") \ + _SG_LOGITEM_XMACRO(GL_SHADER_LINKING_FAILED, "shader linking failed (gl)") \ + _SG_LOGITEM_XMACRO(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER, "vertex attribute not found in shader (gl)") \ + _SG_LOGITEM_XMACRO(GL_TEXTURE_NAME_NOT_FOUND_IN_SHADER, "texture name not found in shader (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_UNDEFINED, "framebuffer completeness check failed with GL_FRAMEBUFFER_UNDEFINED (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_INCOMPLETE_ATTACHMENT, "framebuffer completeness check failed with GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MISSING_ATTACHMENT, "framebuffer completeness check failed with GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_UNSUPPORTED, "framebuffer completeness check failed with GL_FRAMEBUFFER_UNSUPPORTED (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MULTISAMPLE, "framebuffer completeness check failed with GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_STATUS_UNKNOWN, "framebuffer completeness check failed (unknown reason) (gl)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_BUFFER_FAILED, "CreateBuffer() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_BUFFER_SRV_FAILED, "CreateShaderResourceView() failed for storage buffer (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for depth-stencil texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_TEXTURE_FAILED, "CreateTexture2D() failed for depth-stencil texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for 2d-, cube- or array-texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_TEXTURE_FAILED, "CreateTexture2D() failed for 2d-, cube- or array-texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_SRV_FAILED, "CreateShaderResourceView() failed for 2d-, cube- or array-texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_3D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for 3D texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_3D_TEXTURE_FAILED, "CreateTexture3D() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_3D_SRV_FAILED, "CreateShaderResourceView() failed for 3d texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_MSAA_TEXTURE_FAILED, "CreateTexture2D() failed for MSAA render target texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_SAMPLER_STATE_FAILED, "CreateSamplerState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_LOAD_D3DCOMPILER_47_DLL_FAILED, "loading d3dcompiler_47.dll failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_SHADER_COMPILATION_FAILED, "shader compilation failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_SHADER_COMPILATION_OUTPUT, "") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_CONSTANT_BUFFER_FAILED, "CreateBuffer() failed for uniform constant buffer (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_INPUT_LAYOUT_FAILED, "CreateInputLayout() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_RASTERIZER_STATE_FAILED, "CreateRasterizerState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_STENCIL_STATE_FAILED, "CreateDepthStencilState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_BLEND_STATE_FAILED, "CreateBlendState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_RTV_FAILED, "CreateRenderTargetView() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DSV_FAILED, "CreateDepthStencilView() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_UPDATE_BUFFER_FAILED, "Map() failed when updating buffer (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_APPEND_BUFFER_FAILED, "Map() failed when appending to buffer (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_UPDATE_IMAGE_FAILED, "Map() failed when updating image (d3d11)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_BUFFER_FAILED, "failed to create buffer object (metal)") \ + _SG_LOGITEM_XMACRO(METAL_TEXTURE_FORMAT_NOT_SUPPORTED, "pixel format not supported for texture (metal)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_TEXTURE_FAILED, "failed to create texture object (metal)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_SAMPLER_FAILED, "failed to create sampler object (metal)") \ + _SG_LOGITEM_XMACRO(METAL_SHADER_COMPILATION_FAILED, "shader compilation failed (metal)") \ + _SG_LOGITEM_XMACRO(METAL_SHADER_CREATION_FAILED, "shader creation failed (metal)") \ + _SG_LOGITEM_XMACRO(METAL_SHADER_COMPILATION_OUTPUT, "") \ + _SG_LOGITEM_XMACRO(METAL_VERTEX_SHADER_ENTRY_NOT_FOUND, "vertex shader entry function not found (metal)") \ + _SG_LOGITEM_XMACRO(METAL_FRAGMENT_SHADER_ENTRY_NOT_FOUND, "fragment shader entry not found (metal)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_RPS_FAILED, "failed to create render pipeline state (metal)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_RPS_OUTPUT, "") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_DSS_FAILED, "failed to create depth stencil state (metal)") \ + _SG_LOGITEM_XMACRO(WGPU_BINDGROUPS_POOL_EXHAUSTED, "bindgroups pool exhausted (increase sg_desc.bindgroups_cache_size) (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_BINDGROUPSCACHE_SIZE_GREATER_ONE, "sg_desc.wgpu_bindgroups_cache_size must be > 1 (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_BINDGROUPSCACHE_SIZE_POW2, "sg_desc.wgpu_bindgroups_cache_size must be a power of 2 (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_CREATEBINDGROUP_FAILED, "wgpuDeviceCreateBindGroup failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_BUFFER_FAILED, "wgpuDeviceCreateBuffer() failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_TEXTURE_FAILED, "wgpuDeviceCreateTexture() failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_TEXTURE_VIEW_FAILED, "wgpuTextureCreateView() failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_SAMPLER_FAILED, "wgpuDeviceCreateSampler() failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_SHADER_MODULE_FAILED, "wgpuDeviceCreateShaderModule() failed") \ + _SG_LOGITEM_XMACRO(WGPU_SHADER_TOO_MANY_IMAGES, "shader uses too many sampled images on shader stage (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_SHADER_TOO_MANY_SAMPLERS, "shader uses too many samplers on shader stage (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_SHADER_TOO_MANY_STORAGEBUFFERS, "shader uses too many storage buffer bindings on shader stage (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_SHADER_CREATE_BINDGROUP_LAYOUT_FAILED, "wgpuDeviceCreateBindGroupLayout() for shader stage failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_PIPELINE_LAYOUT_FAILED, "wgpuDeviceCreatePipelineLayout() failed") \ + _SG_LOGITEM_XMACRO(WGPU_CREATE_RENDER_PIPELINE_FAILED, "wgpuDeviceCreateRenderPipeline() failed") \ + _SG_LOGITEM_XMACRO(WGPU_ATTACHMENTS_CREATE_TEXTURE_VIEW_FAILED, "wgpuTextureCreateView() failed in create attachments") \ + _SG_LOGITEM_XMACRO(IDENTICAL_COMMIT_LISTENER, "attempting to add identical commit listener") \ + _SG_LOGITEM_XMACRO(COMMIT_LISTENER_ARRAY_FULL, "commit listener array full") \ + _SG_LOGITEM_XMACRO(TRACE_HOOKS_NOT_ENABLED, "sg_install_trace_hooks() called, but SG_TRACE_HOOKS is not defined") \ + _SG_LOGITEM_XMACRO(DEALLOC_BUFFER_INVALID_STATE, "sg_dealloc_buffer(): buffer must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(DEALLOC_IMAGE_INVALID_STATE, "sg_dealloc_image(): image must be in alloc state") \ + _SG_LOGITEM_XMACRO(DEALLOC_SAMPLER_INVALID_STATE, "sg_dealloc_sampler(): sampler must be in alloc state") \ + _SG_LOGITEM_XMACRO(DEALLOC_SHADER_INVALID_STATE, "sg_dealloc_shader(): shader must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(DEALLOC_PIPELINE_INVALID_STATE, "sg_dealloc_pipeline(): pipeline must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(DEALLOC_ATTACHMENTS_INVALID_STATE, "sg_dealloc_attachments(): attachments must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_BUFFER_INVALID_STATE, "sg_init_buffer(): buffer must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_IMAGE_INVALID_STATE, "sg_init_image(): image must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_SAMPLER_INVALID_STATE, "sg_init_sampler(): sampler must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_SHADER_INVALID_STATE, "sg_init_shader(): shader must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_PIPELINE_INVALID_STATE, "sg_init_pipeline(): pipeline must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_ATTACHMENTS_INVALID_STATE, "sg_init_attachments(): pass must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(UNINIT_BUFFER_INVALID_STATE, "sg_uninit_buffer(): buffer must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_IMAGE_INVALID_STATE, "sg_uninit_image(): image must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_SAMPLER_INVALID_STATE, "sg_uninit_sampler(): sampler must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_SHADER_INVALID_STATE, "sg_uninit_shader(): shader must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_PIPELINE_INVALID_STATE, "sg_uninit_pipeline(): pipeline must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_ATTACHMENTS_INVALID_STATE, "sg_uninit_attachments(): attachments must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(FAIL_BUFFER_INVALID_STATE, "sg_fail_buffer(): buffer must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_IMAGE_INVALID_STATE, "sg_fail_image(): image must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_SAMPLER_INVALID_STATE, "sg_fail_sampler(): sampler must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_SHADER_INVALID_STATE, "sg_fail_shader(): shader must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_PIPELINE_INVALID_STATE, "sg_fail_pipeline(): pipeline must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_ATTACHMENTS_INVALID_STATE, "sg_fail_attachments(): attachments must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(BUFFER_POOL_EXHAUSTED, "buffer pool exhausted") \ + _SG_LOGITEM_XMACRO(IMAGE_POOL_EXHAUSTED, "image pool exhausted") \ + _SG_LOGITEM_XMACRO(SAMPLER_POOL_EXHAUSTED, "sampler pool exhausted") \ + _SG_LOGITEM_XMACRO(SHADER_POOL_EXHAUSTED, "shader pool exhausted") \ + _SG_LOGITEM_XMACRO(PIPELINE_POOL_EXHAUSTED, "pipeline pool exhausted") \ + _SG_LOGITEM_XMACRO(PASS_POOL_EXHAUSTED, "pass pool exhausted") \ + _SG_LOGITEM_XMACRO(BEGINPASS_ATTACHMENT_INVALID, "sg_begin_pass: an attachment was provided that no longer exists") \ + _SG_LOGITEM_XMACRO(DRAW_WITHOUT_BINDINGS, "attempting to draw without resource bindings") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_CANARY, "sg_buffer_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_SIZE, "sg_buffer_desc.size and .data.size cannot both be 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_DATA, "immutable buffers must be initialized with data (sg_buffer_desc.data.ptr and sg_buffer_desc.data.size)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_DATA_SIZE, "immutable buffer data size differs from buffer size") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_NO_DATA, "dynamic/stream usage buffers cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_STORAGEBUFFER_SUPPORTED, "storage buffers not supported by the backend 3D API (requires OpenGL >= 4.3)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_STORAGEBUFFER_SIZE_MULTIPLE_4, "size of storage buffers must be a multiple of 4") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDATA_NODATA, "sg_image_data: no data (.ptr and/or .size is zero)") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDATA_DATA_SIZE, "sg_image_data: data size doesn't match expected surface size") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_CANARY, "sg_image_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_WIDTH, "sg_image_desc.width must be > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_HEIGHT, "sg_image_desc.height must be > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_PIXELFORMAT, "invalid pixel format for render-target image") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT, "invalid pixel format for non-render-target image") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT, "non-render-target images cannot be multisampled") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT, "MSAA not supported for this pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_NUM_MIPMAPS, "MSAA images must have num_mipmaps == 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_3D_IMAGE, "3D images cannot have a sample_count > 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE, "3D images cannot have a depth/stencil image format") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_IMMUTABLE, "render target images must be SG_USAGE_IMMUTABLE") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_NO_DATA, "render target images cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_INJECTED_NO_DATA, "images with injected textures cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA, "dynamic/stream images cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE, "compressed images must be immutable") \ + _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_CANARY, "sg_sampler_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_MINFILTER_NONE, "sg_sampler_desc.min_filter cannot be SG_FILTER_NONE") \ + _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_MAGFILTER_NONE, "sg_sampler_desc.mag_filter cannot be SG_FILTER_NONE") \ + _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING, "sg_sampler_desc.max_anisotropy > 1 requires min/mag/mipmap_filter to be SG_FILTER_LINEAR") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_CANARY, "sg_shader_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SOURCE, "shader source code required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_BYTECODE, "shader byte code required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE, "shader source or byte code required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_BYTECODE_SIZE, "shader byte code length (in bytes) required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_UBS, "shader uniform blocks must occupy continuous slots") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS, "uniform block members must occupy continuous slots") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_UB_MEMBERS, "GL backend requires uniform block member declarations") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_MEMBER_NAME, "uniform block member name missing") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_SIZE_MISMATCH, "size of uniform block members doesn't match uniform block size") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_ARRAY_COUNT, "uniform array count must be >= 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE, "uniform arrays only allowed for FLOAT4, INT4, MAT4 in std140 layout") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_STORAGEBUFFERS, "shader stage storage buffers must occupy continuous slots (sg_shader_desc.vs|fs.storage_buffers[])") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_STORAGEBUFFER_READONLY, "shader stage storage buffers must be readonly (sg_shader_desc.vs|fs.storage_buffers[].readonly)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_IMAGES, "shader stage images must occupy continuous slots (sg_shader_desc.vs|fs.images[])") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_SAMPLERS, "shader stage samplers must occupy continuous slots (sg_shader_desc.vs|fs.samplers[])") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_IMAGE_SLOT_OUT_OF_RANGE, "shader stage: image-sampler-pair image slot index is out of range (sg_shader_desc.vs|fs.image_sampler_pairs[].image_slot)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_SAMPLER_SLOT_OUT_OF_RANGE, "shader stage: image-sampler-pair image slot index is out of range (sg_shader_desc.vs|fs.image_sampler_pairs[].sampler_slot)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_NAME_REQUIRED_FOR_GL, "shader stage: image-sampler-pairs must be named in GL (sg_shader_desc.vs|fs.image_sampler_pairs[].name)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_NAME_BUT_NOT_USED, "shader stage: image-sampler-pair has name but .used field not true") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_IMAGE_BUT_NOT_USED, "shader stage: image-sampler-pair has .image_slot != 0 but .used field not true") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_SAMPLER_BUT_NOT_USED, "shader stage: image-sampler-pair .sampler_slot != 0 but .used field not true") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NONFILTERING_SAMPLER_REQUIRED, "shader stage: image sample type UNFILTERABLE_FLOAT, UINT, SINT can only be used with NONFILTERING sampler") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_COMPARISON_SAMPLER_REQUIRED, "shader stage: image sample type DEPTH can only be used with COMPARISON sampler") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS, "shader stage: one or more images are note referenced by (sg_shader_desc.vs|fs.image_sampler_pairs[].image_slot)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS, "shader stage: one or more samplers are not referenced by image-sampler-pairs (sg_shader_desc.vs|fs.image_sampler_pairs[].sampler_slot)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_IMAGE_SAMPLER_PAIRS, "shader stage image-sampler-pairs must occupy continuous slots (sg_shader_desc.vs|fs.image_samplers[])") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG, "vertex attribute name/semantic string too long (max len 16)") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_CANARY, "sg_pipeline_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_SHADER, "sg_pipeline_desc.shader missing or invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_NO_CONT_ATTRS, "sg_pipeline_desc.layout.attrs is not continuous") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4, "sg_pipeline_desc.layout.buffers[].stride must be multiple of 4") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_ATTR_SEMANTICS, "D3D11 missing vertex attribute semantics in shader") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_CANARY, "sg_attachments_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_NO_ATTACHMENTS, "sg_attachments_desc no color or depth-stencil attachments") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_NO_CONT_COLOR_ATTS, "color attachments must occupy continuous slots") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_IMAGE, "pass attachment image is not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_MIPLEVEL, "pass attachment mip level is bigger than image has mipmaps") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_FACE, "pass attachment image is cubemap, but face index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_LAYER, "pass attachment image is array texture, but layer index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_SLICE, "pass attachment image is 3d texture, but slice value is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_IMAGE_NO_RT, "pass attachment image must be have render_target=true") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_COLOR_INV_PIXELFORMAT, "pass color-attachment images must be renderable color pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_INV_PIXELFORMAT, "pass depth-attachment image must be depth or depth-stencil pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_IMAGE_SIZES, "all pass attachments must have the same size") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_IMAGE_SAMPLE_COUNTS, "all pass attachments must have the same sample count") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_COLOR_IMAGE_MSAA, "pass resolve attachments must have a color attachment image with sample count > 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE, "pass resolve attachment image not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_SAMPLE_COUNT, "pass resolve attachment image sample count must be 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_MIPLEVEL, "pass resolve attachment mip level is bigger than image has mipmaps") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_FACE, "pass resolve attachment is cubemap, but face index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_LAYER, "pass resolve attachment is array texture, but layer index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_SLICE, "pass resolve attachment is 3d texture, but slice value is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_NO_RT, "pass resolve attachment image must have render_target=true") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_SIZES, "pass resolve attachment size must match color attachment image size") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_FORMAT, "pass resolve attachment pixel format must match color attachment pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE, "pass depth attachment image is not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_MIPLEVEL, "pass depth attachment mip level is bigger than image has mipmaps") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_FACE, "pass depth attachment image is cubemap, but face index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_LAYER, "pass depth attachment image is array texture, but layer index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_SLICE, "pass depth attachment image is 3d texture, but slice value is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_NO_RT, "pass depth attachment image must be have render_target=true") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SIZES, "pass depth attachment image size must match color attachment image size") \ + _SG_LOGITEM_XMACRO(VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SAMPLE_COUNT, "pass depth attachment sample count must match color attachment sample count") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_CANARY, "sg_begin_pass: pass struct not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_ATTACHMENTS_EXISTS, "sg_begin_pass: attachments object no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_ATTACHMENTS_VALID, "sg_begin_pass: attachments object not in resource state VALID") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLOR_ATTACHMENT_IMAGE, "sg_begin_pass: one or more color attachment images are not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVE_ATTACHMENT_IMAGE, "sg_begin_pass: one or more resolve attachment images are not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCIL_ATTACHMENT_IMAGE, "sg_begin_pass: one or more depth-stencil attachment images are not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH, "sg_begin_pass: expected pass.swapchain.width > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH_NOTSET, "sg_begin_pass: expected pass.swapchain.width == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT, "sg_begin_pass: expected pass.swapchain.height > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT_NOTSET, "sg_begin_pass: expected pass.swapchain.height == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT, "sg_begin_pass: expected pass.swapchain.sample_count > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT_NOTSET, "sg_begin_pass: expected pass.swapchain.sample_count == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT, "sg_begin_pass: expected pass.swapchain.color_format to be valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT_NOTSET, "sg_begin_pass: expected pass.swapchain.color_format to be unset") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_DEPTHFORMAT_NOTSET, "sg_begin_pass: expected pass.swapchain.depth_format to be unset") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE, "sg_begin_pass: expected pass.swapchain.metal.current_drawable != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE_NOTSET, "sg_begin_pass: expected pass.swapchain.metal.current_drawable == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE, "sg_begin_pass: expected pass.swapchain.metal.depth_stencil_texture != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE_NOTSET, "sg_begin_pass: expected pass.swapchain.metal.depth_stencil_texture == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE, "sg_begin_pass: expected pass.swapchain.metal.msaa_color_texture != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE_NOTSET, "sg_begin_pass: expected pass.swapchain.metal.msaa_color_texture == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW, "sg_begin_pass: expected pass.swapchain.d3d11.render_view != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.d3d11.render_view == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW, "sg_begin_pass: expected pass.swapchain.d3d11.resolve_view != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.d3d11.resolve_view == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW, "sg_begin_pass: expected pass.swapchain.d3d11.depth_stencil_view != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.d3d11.depth_stencil_view == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW, "sg_begin_pass: expected pass.swapchain.wgpu.render_view != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.wgpu.render_view == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW, "sg_begin_pass: expected pass.swapchain.wgpu.resolve_view != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.wgpu.resolve_view == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW, "sg_begin_pass: expected pass.swapchain.wgpu.depth_stencil_view != 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW_NOTSET, "sg_begin_pass: expected pass.swapchain.wgpu.depth_stencil_view == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_SWAPCHAIN_GL_EXPECT_FRAMEBUFFER_NOTSET, "sg_begin_pass: expected pass.swapchain.gl.framebuffer == 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_VALID_ID, "sg_apply_pipeline: invalid pipeline id provided") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_EXISTS, "sg_apply_pipeline: pipeline object no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_VALID, "sg_apply_pipeline: pipeline object not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_SHADER_EXISTS, "sg_apply_pipeline: shader object no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_SHADER_VALID, "sg_apply_pipeline: shader object not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_CURPASS_ATTACHMENTS_EXISTS, "sg_apply_pipeline: current pass attachments no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_CURPASS_ATTACHMENTS_VALID, "sg_apply_pipeline: current pass attachments not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_ATT_COUNT, "sg_apply_pipeline: number of pipeline color attachments doesn't match number of pass color attachments") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_COLOR_FORMAT, "sg_apply_pipeline: pipeline color attachment pixel format doesn't match pass color attachment pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_DEPTH_FORMAT, "sg_apply_pipeline: pipeline depth pixel_format doesn't match pass depth attachment pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_SAMPLE_COUNT, "sg_apply_pipeline: pipeline MSAA sample count doesn't match render pass attachment sample count") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE, "sg_apply_bindings: must be called after sg_apply_pipeline") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE_EXISTS, "sg_apply_bindings: currently applied pipeline object no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE_VALID, "sg_apply_bindings: currently applied pipeline object not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VBS, "sg_apply_bindings: number of vertex buffers doesn't match number of pipeline vertex layouts") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VB_EXISTS, "sg_apply_bindings: vertex buffer no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VB_TYPE, "sg_apply_bindings: buffer in vertex buffer slot is not a SG_BUFFERTYPE_VERTEXBUFFER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VB_OVERFLOW, "sg_apply_bindings: buffer in vertex buffer slot is overflown") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_NO_IB, "sg_apply_bindings: pipeline object defines indexed rendering, but no index buffer provided") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB, "sg_apply_bindings: pipeline object defines non-indexed rendering, but index buffer provided") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_EXISTS, "sg_apply_bindings: index buffer no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_TYPE, "sg_apply_bindings: buffer in index buffer slot is not a SG_BUFFERTYPE_INDEXBUFFER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_OVERFLOW, "sg_apply_bindings: buffer in index buffer slot is overflown") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_IMAGE_BINDING, "sg_apply_bindings: image binding on vertex stage is missing or the image handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMG_EXISTS, "sg_apply_bindings: image bound to vertex stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMAGE_TYPE_MISMATCH, "sg_apply_bindings: type of image bound to vertex stage doesn't match shader desc") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMAGE_MSAA, "sg_apply_bindings: cannot bind image with sample_count>1 to vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_FILTERABLE_IMAGE, "sg_apply_bindings: filterable image expected on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_DEPTH_IMAGE, "sg_apply_bindings: depth image expected on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_IMAGE_BINDING, "sg_apply_bindings: unexpected image binding on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_SAMPLER_BINDING, "sg_apply_bindings: sampler binding on vertex stage is missing or the sampler handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_COMPARISON on vertex stage but sampler has SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_FILTERING or SG_SAMPLERTYPE_NONFILTERING on vertex stage but sampler doesn't have SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_NONFILTERING_SAMPLER, "sg_apply_bindings: shader expected SG_SAMPLERTYPE_NONFILTERING on vertex stage, but sampler has SG_FILTER_LINEAR filters") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_BINDING, "sg_apply_bindings: unexpected sampler binding on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_SMP_EXISTS, "sg_apply_bindings: sampler bound to vertex stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_STORAGEBUFFER_BINDING, "sg_apply_bindings: storage buffer binding on vertex stage is missing or the buffer handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_STORAGEBUFFER_EXISTS, "sg_apply_bindings: storage buffer bound to vertex stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_STORAGEBUFFER_BINDING_BUFFERTYPE, "sg_apply_bindings: buffer bound to vertex stage storage buffer slot is not of type storage buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_STORAGEBUFFER_BINDING, "sg_apply_bindings: unexpected storage buffer binding on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_IMAGE_BINDING, "sg_apply_bindings: image binding on fragment stage is missing or the image handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMG_EXISTS, "sg_apply_bindings: image bound to fragment stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMAGE_TYPE_MISMATCH, "sg_apply_bindings: type of image bound to fragment stage doesn't match shader desc") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMAGE_MSAA, "sg_apply_bindings: cannot bind image with sample_count>1 to fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_FILTERABLE_IMAGE, "sg_apply_bindings: filterable image expected on fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_DEPTH_IMAGE, "sg_apply_bindings: depth image expected on fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_IMAGE_BINDING, "sg_apply_bindings: unexpected image binding on fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_SAMPLER_BINDING, "sg_apply_bindings: sampler binding on fragment stage is missing or the sampler handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_COMPARISON on fragment stage but sampler has SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_FILTERING on fragment stage but sampler doesn't have SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_NONFILTERING_SAMPLER, "sg_apply_bindings: shader expected SG_SAMPLERTYPE_NONFILTERING on fragment stage, but sampler has SG_FILTER_LINEAR filters") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_BINDING, "sg_apply_bindings: unexpected sampler binding on fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_SMP_EXISTS, "sg_apply_bindings: sampler bound to fragment stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_STORAGEBUFFER_BINDING, "sg_apply_bindings: storage buffer binding on fragment stage is missing or the buffer handle is invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_STORAGEBUFFER_EXISTS, "sg_apply_bindings: storage buffer bound to fragment stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_STORAGEBUFFER_BINDING_BUFFERTYPE, "sg_apply_bindings: buffer bound to frahment stage storage buffer slot is not of type storage buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_STORAGEBUFFER_BINDING, "sg_apply_bindings: unexpected storage buffer binding on fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_AUB_NO_PIPELINE, "sg_apply_uniforms: must be called after sg_apply_pipeline()") \ + _SG_LOGITEM_XMACRO(VALIDATE_AUB_NO_UB_AT_SLOT, "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot") \ + _SG_LOGITEM_XMACRO(VALIDATE_AUB_SIZE, "sg_apply_uniforms: data size doesn't match declared uniform block size") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_USAGE, "sg_update_buffer: cannot update immutable buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_SIZE, "sg_update_buffer: update size is bigger than buffer size") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_ONCE, "sg_update_buffer: only one update allowed per buffer and frame") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_APPEND, "sg_update_buffer: cannot call sg_update_buffer and sg_append_buffer in same frame") \ + _SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_USAGE, "sg_append_buffer: cannot append to immutable buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_SIZE, "sg_append_buffer: overall appended size is bigger than buffer size") \ + _SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_UPDATE, "sg_append_buffer: cannot call sg_append_buffer and sg_update_buffer in same frame") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDIMG_USAGE, "sg_update_image: cannot update immutable image") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDIMG_ONCE, "sg_update_image: only one update allowed per image and frame") \ + _SG_LOGITEM_XMACRO(VALIDATION_FAILED, "validation layer checks failed") \ + +#define _SG_LOGITEM_XMACRO(item,msg) SG_LOGITEM_##item, +typedef enum sg_log_item { + _SG_LOG_ITEMS +} sg_log_item; +#undef _SG_LOGITEM_XMACRO + +/* + sg_desc + + The sg_desc struct contains configuration values for sokol_gfx, + it is used as parameter to the sg_setup() call. + + The default configuration is: + + .buffer_pool_size 128 + .image_pool_size 128 + .sampler_pool_size 64 + .shader_pool_size 32 + .pipeline_pool_size 64 + .pass_pool_size 16 + .uniform_buffer_size 4 MB (4*1024*1024) + .max_commit_listeners 1024 + .disable_validation false + .mtl_force_managed_storage_mode false + .wgpu_disable_bindgroups_cache false + .wgpu_bindgroups_cache_size 1024 + + .allocator.alloc_fn 0 (in this case, malloc() will be called) + .allocator.free_fn 0 (in this case, free() will be called) + .allocator.user_data 0 + + .environment.defaults.color_format: default value depends on selected backend: + all GL backends: SG_PIXELFORMAT_RGBA8 + Metal and D3D11: SG_PIXELFORMAT_BGRA8 + WebGPU: *no default* (must be queried from WebGPU swapchain object) + .environment.defaults.depth_format: SG_PIXELFORMAT_DEPTH_STENCIL + .environment.defaults.sample_count: 1 + + Metal specific: + (NOTE: All Objective-C object references are transferred through + a bridged (const void*) to sokol_gfx, which will use a unretained + bridged cast (__bridged id) to retrieve the Objective-C + references back. Since the bridge cast is unretained, the caller + must hold a strong reference to the Objective-C object for the + duration of the sokol_gfx call! + + .mtl_force_managed_storage_mode + when enabled, Metal buffers and texture resources are created in managed storage + mode, otherwise sokol-gfx will decide whether to create buffers and + textures in managed or shared storage mode (this is mainly a debugging option) + .mtl_use_command_buffer_with_retained_references + when true, the sokol-gfx Metal backend will use Metal command buffers which + bump the reference count of resource objects as long as they are inflight, + this is slower than the default command-buffer-with-unretained-references + method, this may be a workaround when confronted with lifetime validation + errors from the Metal validation layer until a proper fix has been implemented + .environment.metal.device + a pointer to the MTLDevice object + + D3D11 specific: + .environment.d3d11.device + a pointer to the ID3D11Device object, this must have been created + before sg_setup() is called + .environment.d3d11.device_context + a pointer to the ID3D11DeviceContext object + + WebGPU specific: + .wgpu_disable_bindgroups_cache + When this is true, the WebGPU backend will create and immediately + release a BindGroup object in the sg_apply_bindings() call, only + use this for debugging purposes. + .wgpu_bindgroups_cache_size + The size of the bindgroups cache for re-using BindGroup objects + between sg_apply_bindings() calls. The smaller the cache size, + the more likely are cache slot collisions which will cause + a BindGroups object to be destroyed and a new one created. + Use the information returned by sg_query_stats() to check + if this is a frequent occurrence, and increase the cache size as + needed (the default is 1024). + NOTE: wgpu_bindgroups_cache_size must be a power-of-2 number! + .environment.wgpu.device + a WGPUDevice handle + + When using sokol_gfx.h and sokol_app.h together, consider using the + helper function sglue_environment() in the sokol_glue.h header to + initialize the sg_desc.environment nested struct. sglue_environment() returns + a completely initialized sg_environment struct with information + provided by sokol_app.h. +*/ +typedef struct sg_environment_defaults { + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; +} sg_environment_defaults; + +typedef struct sg_metal_environment { + const void* device; +} sg_metal_environment; + +typedef struct sg_d3d11_environment { + const void* device; + const void* device_context; +} sg_d3d11_environment; + +typedef struct sg_wgpu_environment { + const void* device; +} sg_wgpu_environment; + +typedef struct sg_environment { + sg_environment_defaults defaults; + sg_metal_environment metal; + sg_d3d11_environment d3d11; + sg_wgpu_environment wgpu; +} sg_environment; + +/* + sg_commit_listener + + Used with function sg_add_commit_listener() to add a callback + which will be called in sg_commit(). This is useful for libraries + building on top of sokol-gfx to be notified about when a frame + ends (instead of having to guess, or add a manual 'new-frame' + function. +*/ +typedef struct sg_commit_listener { + void (*func)(void* user_data); + void* user_data; +} sg_commit_listener; + +/* + sg_allocator + + Used in sg_desc to provide custom memory-alloc and -free functions + to sokol_gfx.h. If memory management should be overridden, both the + alloc_fn and free_fn function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct sg_allocator { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} sg_allocator; + +/* + sg_logger + + Used in sg_desc to provide a logging function. Please be aware + that without logging function, sokol-gfx will be completely + silent, e.g. it will not report errors, warnings and + validation layer messages. For maximum error verbosity, + compile in debug mode (e.g. NDEBUG *not* defined) and provide a + compatible logger function in the sg_setup() call + (for instance the standard logging function from sokol_log.h). +*/ +typedef struct sg_logger { + void (*func)( + const char* tag, // always "sg" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SG_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_gfx.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} sg_logger; + +typedef struct sg_desc { + uint32_t _start_canary; + int buffer_pool_size; + int image_pool_size; + int sampler_pool_size; + int shader_pool_size; + int pipeline_pool_size; + int attachments_pool_size; + int uniform_buffer_size; + int max_commit_listeners; + bool disable_validation; // disable validation layer even in debug mode, useful for tests + bool mtl_force_managed_storage_mode; // for debugging: use Metal managed storage mode for resources even with UMA + bool mtl_use_command_buffer_with_retained_references; // Metal: use a managed MTLCommandBuffer which ref-counts used resources + bool wgpu_disable_bindgroups_cache; // set to true to disable the WebGPU backend BindGroup cache + int wgpu_bindgroups_cache_size; // number of slots in the WebGPU bindgroup cache (must be 2^N) + sg_allocator allocator; + sg_logger logger; // optional log function override + sg_environment environment; + uint32_t _end_canary; +} sg_desc; + +// setup and misc functions +SOKOL_GFX_API_DECL void sg_setup(const sg_desc* desc); +SOKOL_GFX_API_DECL void sg_shutdown(void); +SOKOL_GFX_API_DECL bool sg_isvalid(void); +SOKOL_GFX_API_DECL void sg_reset_state_cache(void); +SOKOL_GFX_API_DECL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks); +SOKOL_GFX_API_DECL void sg_push_debug_group(const char* name); +SOKOL_GFX_API_DECL void sg_pop_debug_group(void); +SOKOL_GFX_API_DECL bool sg_add_commit_listener(sg_commit_listener listener); +SOKOL_GFX_API_DECL bool sg_remove_commit_listener(sg_commit_listener listener); + +// resource creation, destruction and updating +SOKOL_GFX_API_DECL sg_buffer sg_make_buffer(const sg_buffer_desc* desc); +SOKOL_GFX_API_DECL sg_image sg_make_image(const sg_image_desc* desc); +SOKOL_GFX_API_DECL sg_sampler sg_make_sampler(const sg_sampler_desc* desc); +SOKOL_GFX_API_DECL sg_shader sg_make_shader(const sg_shader_desc* desc); +SOKOL_GFX_API_DECL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc); +SOKOL_GFX_API_DECL sg_attachments sg_make_attachments(const sg_attachments_desc* desc); +SOKOL_GFX_API_DECL void sg_destroy_buffer(sg_buffer buf); +SOKOL_GFX_API_DECL void sg_destroy_image(sg_image img); +SOKOL_GFX_API_DECL void sg_destroy_sampler(sg_sampler smp); +SOKOL_GFX_API_DECL void sg_destroy_shader(sg_shader shd); +SOKOL_GFX_API_DECL void sg_destroy_pipeline(sg_pipeline pip); +SOKOL_GFX_API_DECL void sg_destroy_attachments(sg_attachments atts); +SOKOL_GFX_API_DECL void sg_update_buffer(sg_buffer buf, const sg_range* data); +SOKOL_GFX_API_DECL void sg_update_image(sg_image img, const sg_image_data* data); +SOKOL_GFX_API_DECL int sg_append_buffer(sg_buffer buf, const sg_range* data); +SOKOL_GFX_API_DECL bool sg_query_buffer_overflow(sg_buffer buf); +SOKOL_GFX_API_DECL bool sg_query_buffer_will_overflow(sg_buffer buf, size_t size); + +// rendering functions +SOKOL_GFX_API_DECL void sg_begin_pass(const sg_pass* pass); +SOKOL_GFX_API_DECL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left); +SOKOL_GFX_API_DECL void sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left); +SOKOL_GFX_API_DECL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left); +SOKOL_GFX_API_DECL void sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left); +SOKOL_GFX_API_DECL void sg_apply_pipeline(sg_pipeline pip); +SOKOL_GFX_API_DECL void sg_apply_bindings(const sg_bindings* bindings); +SOKOL_GFX_API_DECL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data); +SOKOL_GFX_API_DECL void sg_draw(int base_element, int num_elements, int num_instances); +SOKOL_GFX_API_DECL void sg_end_pass(void); +SOKOL_GFX_API_DECL void sg_commit(void); + +// getting information +SOKOL_GFX_API_DECL sg_desc sg_query_desc(void); +SOKOL_GFX_API_DECL sg_backend sg_query_backend(void); +SOKOL_GFX_API_DECL sg_features sg_query_features(void); +SOKOL_GFX_API_DECL sg_limits sg_query_limits(void); +SOKOL_GFX_API_DECL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt); +SOKOL_GFX_API_DECL int sg_query_row_pitch(sg_pixel_format fmt, int width, int row_align_bytes); +SOKOL_GFX_API_DECL int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes); +// get current state of a resource (INITIAL, ALLOC, VALID, FAILED, INVALID) +SOKOL_GFX_API_DECL sg_resource_state sg_query_buffer_state(sg_buffer buf); +SOKOL_GFX_API_DECL sg_resource_state sg_query_image_state(sg_image img); +SOKOL_GFX_API_DECL sg_resource_state sg_query_sampler_state(sg_sampler smp); +SOKOL_GFX_API_DECL sg_resource_state sg_query_shader_state(sg_shader shd); +SOKOL_GFX_API_DECL sg_resource_state sg_query_pipeline_state(sg_pipeline pip); +SOKOL_GFX_API_DECL sg_resource_state sg_query_attachments_state(sg_attachments atts); +// get runtime information about a resource +SOKOL_GFX_API_DECL sg_buffer_info sg_query_buffer_info(sg_buffer buf); +SOKOL_GFX_API_DECL sg_image_info sg_query_image_info(sg_image img); +SOKOL_GFX_API_DECL sg_sampler_info sg_query_sampler_info(sg_sampler smp); +SOKOL_GFX_API_DECL sg_shader_info sg_query_shader_info(sg_shader shd); +SOKOL_GFX_API_DECL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip); +SOKOL_GFX_API_DECL sg_attachments_info sg_query_attachments_info(sg_attachments atts); +// get desc structs matching a specific resource (NOTE that not all creation attributes may be provided) +SOKOL_GFX_API_DECL sg_buffer_desc sg_query_buffer_desc(sg_buffer buf); +SOKOL_GFX_API_DECL sg_image_desc sg_query_image_desc(sg_image img); +SOKOL_GFX_API_DECL sg_sampler_desc sg_query_sampler_desc(sg_sampler smp); +SOKOL_GFX_API_DECL sg_shader_desc sg_query_shader_desc(sg_shader shd); +SOKOL_GFX_API_DECL sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip); +SOKOL_GFX_API_DECL sg_attachments_desc sg_query_attachments_desc(sg_attachments atts); +// get resource creation desc struct with their default values replaced +SOKOL_GFX_API_DECL sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc); +SOKOL_GFX_API_DECL sg_image_desc sg_query_image_defaults(const sg_image_desc* desc); +SOKOL_GFX_API_DECL sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc); +SOKOL_GFX_API_DECL sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc); +SOKOL_GFX_API_DECL sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc); +SOKOL_GFX_API_DECL sg_attachments_desc sg_query_attachments_defaults(const sg_attachments_desc* desc); + +// separate resource allocation and initialization (for async setup) +SOKOL_GFX_API_DECL sg_buffer sg_alloc_buffer(void); +SOKOL_GFX_API_DECL sg_image sg_alloc_image(void); +SOKOL_GFX_API_DECL sg_sampler sg_alloc_sampler(void); +SOKOL_GFX_API_DECL sg_shader sg_alloc_shader(void); +SOKOL_GFX_API_DECL sg_pipeline sg_alloc_pipeline(void); +SOKOL_GFX_API_DECL sg_attachments sg_alloc_attachments(void); +SOKOL_GFX_API_DECL void sg_dealloc_buffer(sg_buffer buf); +SOKOL_GFX_API_DECL void sg_dealloc_image(sg_image img); +SOKOL_GFX_API_DECL void sg_dealloc_sampler(sg_sampler smp); +SOKOL_GFX_API_DECL void sg_dealloc_shader(sg_shader shd); +SOKOL_GFX_API_DECL void sg_dealloc_pipeline(sg_pipeline pip); +SOKOL_GFX_API_DECL void sg_dealloc_attachments(sg_attachments attachments); +SOKOL_GFX_API_DECL void sg_init_buffer(sg_buffer buf, const sg_buffer_desc* desc); +SOKOL_GFX_API_DECL void sg_init_image(sg_image img, const sg_image_desc* desc); +SOKOL_GFX_API_DECL void sg_init_sampler(sg_sampler smg, const sg_sampler_desc* desc); +SOKOL_GFX_API_DECL void sg_init_shader(sg_shader shd, const sg_shader_desc* desc); +SOKOL_GFX_API_DECL void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc* desc); +SOKOL_GFX_API_DECL void sg_init_attachments(sg_attachments attachments, const sg_attachments_desc* desc); +SOKOL_GFX_API_DECL void sg_uninit_buffer(sg_buffer buf); +SOKOL_GFX_API_DECL void sg_uninit_image(sg_image img); +SOKOL_GFX_API_DECL void sg_uninit_sampler(sg_sampler smp); +SOKOL_GFX_API_DECL void sg_uninit_shader(sg_shader shd); +SOKOL_GFX_API_DECL void sg_uninit_pipeline(sg_pipeline pip); +SOKOL_GFX_API_DECL void sg_uninit_attachments(sg_attachments atts); +SOKOL_GFX_API_DECL void sg_fail_buffer(sg_buffer buf); +SOKOL_GFX_API_DECL void sg_fail_image(sg_image img); +SOKOL_GFX_API_DECL void sg_fail_sampler(sg_sampler smp); +SOKOL_GFX_API_DECL void sg_fail_shader(sg_shader shd); +SOKOL_GFX_API_DECL void sg_fail_pipeline(sg_pipeline pip); +SOKOL_GFX_API_DECL void sg_fail_attachments(sg_attachments atts); + +// frame stats +SOKOL_GFX_API_DECL void sg_enable_frame_stats(void); +SOKOL_GFX_API_DECL void sg_disable_frame_stats(void); +SOKOL_GFX_API_DECL bool sg_frame_stats_enabled(void); +SOKOL_GFX_API_DECL sg_frame_stats sg_query_frame_stats(void); + +/* Backend-specific structs and functions, these may come in handy for mixing + sokol-gfx rendering with 'native backend' rendering functions. + + This group of functions will be expanded as needed. +*/ + +typedef struct sg_d3d11_buffer_info { + const void* buf; // ID3D11Buffer* +} sg_d3d11_buffer_info; + +typedef struct sg_d3d11_image_info { + const void* tex2d; // ID3D11Texture2D* + const void* tex3d; // ID3D11Texture3D* + const void* res; // ID3D11Resource* (either tex2d or tex3d) + const void* srv; // ID3D11ShaderResourceView* +} sg_d3d11_image_info; + +typedef struct sg_d3d11_sampler_info { + const void* smp; // ID3D11SamplerState* +} sg_d3d11_sampler_info; + +typedef struct sg_d3d11_shader_info { + const void* vs_cbufs[SG_MAX_SHADERSTAGE_UBS]; // ID3D11Buffer* (vertex stage constant buffers) + const void* fs_cbufs[SG_MAX_SHADERSTAGE_UBS]; // ID3D11Buffer* (fragment stage constant buffers) + const void* vs; // ID3D11VertexShader* + const void* fs; // ID3D11PixelShader* +} sg_d3d11_shader_info; + +typedef struct sg_d3d11_pipeline_info { + const void* il; // ID3D11InputLayout* + const void* rs; // ID3D11RasterizerState* + const void* dss; // ID3D11DepthStencilState* + const void* bs; // ID3D11BlendState* +} sg_d3d11_pipeline_info; + +typedef struct sg_d3d11_attachments_info { + const void* color_rtv[SG_MAX_COLOR_ATTACHMENTS]; // ID3D11RenderTargetView + const void* resolve_rtv[SG_MAX_COLOR_ATTACHMENTS]; // ID3D11RenderTargetView + const void* dsv; // ID3D11DepthStencilView +} sg_d3d11_attachments_info; + +typedef struct sg_mtl_buffer_info { + const void* buf[SG_NUM_INFLIGHT_FRAMES]; // id + int active_slot; +} sg_mtl_buffer_info; + +typedef struct sg_mtl_image_info { + const void* tex[SG_NUM_INFLIGHT_FRAMES]; // id + int active_slot; +} sg_mtl_image_info; + +typedef struct sg_mtl_sampler_info { + const void* smp; // id +} sg_mtl_sampler_info; + +typedef struct sg_mtl_shader_info { + const void* vs_lib; // id + const void* fs_lib; // id + const void* vs_func; // id + const void* fs_func; // id +} sg_mtl_shader_info; + +typedef struct sg_mtl_pipeline_info { + const void* rps; // id + const void* dss; // id +} sg_mtl_pipeline_info; + +typedef struct sg_wgpu_buffer_info { + const void* buf; // WGPUBuffer +} sg_wgpu_buffer_info; + +typedef struct sg_wgpu_image_info { + const void* tex; // WGPUTexture + const void* view; // WGPUTextureView +} sg_wgpu_image_info; + +typedef struct sg_wgpu_sampler_info { + const void* smp; // WGPUSampler +} sg_wgpu_sampler_info; + +typedef struct sg_wgpu_shader_info { + const void* vs_mod; // WGPUShaderModule + const void* fs_mod; // WGPUShaderModule + const void* bgl; // WGPUBindGroupLayout; +} sg_wgpu_shader_info; + +typedef struct sg_wgpu_pipeline_info { + const void* pip; // WGPURenderPipeline +} sg_wgpu_pipeline_info; + +typedef struct sg_wgpu_attachments_info { + const void* color_view[SG_MAX_COLOR_ATTACHMENTS]; // WGPUTextureView + const void* resolve_view[SG_MAX_COLOR_ATTACHMENTS]; // WGPUTextureView + const void* ds_view; // WGPUTextureView +} sg_wgpu_attachments_info; + +typedef struct sg_gl_buffer_info { + uint32_t buf[SG_NUM_INFLIGHT_FRAMES]; + int active_slot; +} sg_gl_buffer_info; + +typedef struct sg_gl_image_info { + uint32_t tex[SG_NUM_INFLIGHT_FRAMES]; + uint32_t tex_target; + uint32_t msaa_render_buffer; + int active_slot; +} sg_gl_image_info; + +typedef struct sg_gl_sampler_info { + uint32_t smp; +} sg_gl_sampler_info; + +typedef struct sg_gl_shader_info { + uint32_t prog; +} sg_gl_shader_info; + +typedef struct sg_gl_attachments_info { + uint32_t framebuffer; + uint32_t msaa_resolve_framebuffer[SG_MAX_COLOR_ATTACHMENTS]; +} sg_gl_attachments_info; + +// D3D11: return ID3D11Device +SOKOL_GFX_API_DECL const void* sg_d3d11_device(void); +// D3D11: return ID3D11DeviceContext +SOKOL_GFX_API_DECL const void* sg_d3d11_device_context(void); +// D3D11: get internal buffer resource objects +SOKOL_GFX_API_DECL sg_d3d11_buffer_info sg_d3d11_query_buffer_info(sg_buffer buf); +// D3D11: get internal image resource objects +SOKOL_GFX_API_DECL sg_d3d11_image_info sg_d3d11_query_image_info(sg_image img); +// D3D11: get internal sampler resource objects +SOKOL_GFX_API_DECL sg_d3d11_sampler_info sg_d3d11_query_sampler_info(sg_sampler smp); +// D3D11: get internal shader resource objects +SOKOL_GFX_API_DECL sg_d3d11_shader_info sg_d3d11_query_shader_info(sg_shader shd); +// D3D11: get internal pipeline resource objects +SOKOL_GFX_API_DECL sg_d3d11_pipeline_info sg_d3d11_query_pipeline_info(sg_pipeline pip); +// D3D11: get internal pass resource objects +SOKOL_GFX_API_DECL sg_d3d11_attachments_info sg_d3d11_query_attachments_info(sg_attachments atts); + +// Metal: return __bridge-casted MTLDevice +SOKOL_GFX_API_DECL const void* sg_mtl_device(void); +// Metal: return __bridge-casted MTLRenderCommandEncoder in current pass (or zero if outside pass) +SOKOL_GFX_API_DECL const void* sg_mtl_render_command_encoder(void); +// Metal: get internal __bridge-casted buffer resource objects +SOKOL_GFX_API_DECL sg_mtl_buffer_info sg_mtl_query_buffer_info(sg_buffer buf); +// Metal: get internal __bridge-casted image resource objects +SOKOL_GFX_API_DECL sg_mtl_image_info sg_mtl_query_image_info(sg_image img); +// Metal: get internal __bridge-casted sampler resource objects +SOKOL_GFX_API_DECL sg_mtl_sampler_info sg_mtl_query_sampler_info(sg_sampler smp); +// Metal: get internal __bridge-casted shader resource objects +SOKOL_GFX_API_DECL sg_mtl_shader_info sg_mtl_query_shader_info(sg_shader shd); +// Metal: get internal __bridge-casted pipeline resource objects +SOKOL_GFX_API_DECL sg_mtl_pipeline_info sg_mtl_query_pipeline_info(sg_pipeline pip); + +// WebGPU: return WGPUDevice object +SOKOL_GFX_API_DECL const void* sg_wgpu_device(void); +// WebGPU: return WGPUQueue object +SOKOL_GFX_API_DECL const void* sg_wgpu_queue(void); +// WebGPU: return this frame's WGPUCommandEncoder +SOKOL_GFX_API_DECL const void* sg_wgpu_command_encoder(void); +// WebGPU: return WGPURenderPassEncoder of current pass +SOKOL_GFX_API_DECL const void* sg_wgpu_render_pass_encoder(void); +// WebGPU: get internal buffer resource objects +SOKOL_GFX_API_DECL sg_wgpu_buffer_info sg_wgpu_query_buffer_info(sg_buffer buf); +// WebGPU: get internal image resource objects +SOKOL_GFX_API_DECL sg_wgpu_image_info sg_wgpu_query_image_info(sg_image img); +// WebGPU: get internal sampler resource objects +SOKOL_GFX_API_DECL sg_wgpu_sampler_info sg_wgpu_query_sampler_info(sg_sampler smp); +// WebGPU: get internal shader resource objects +SOKOL_GFX_API_DECL sg_wgpu_shader_info sg_wgpu_query_shader_info(sg_shader shd); +// WebGPU: get internal pipeline resource objects +SOKOL_GFX_API_DECL sg_wgpu_pipeline_info sg_wgpu_query_pipeline_info(sg_pipeline pip); +// WebGPU: get internal pass resource objects +SOKOL_GFX_API_DECL sg_wgpu_attachments_info sg_wgpu_query_attachments_info(sg_attachments atts); + +// GL: get internal buffer resource objects +SOKOL_GFX_API_DECL sg_gl_buffer_info sg_gl_query_buffer_info(sg_buffer buf); +// GL: get internal image resource objects +SOKOL_GFX_API_DECL sg_gl_image_info sg_gl_query_image_info(sg_image img); +// GL: get internal sampler resource objects +SOKOL_GFX_API_DECL sg_gl_sampler_info sg_gl_query_sampler_info(sg_sampler smp); +// GL: get internal shader resource objects +SOKOL_GFX_API_DECL sg_gl_shader_info sg_gl_query_shader_info(sg_shader shd); +// GL: get internal pass resource objects +SOKOL_GFX_API_DECL sg_gl_attachments_info sg_gl_query_attachments_info(sg_attachments atts); + +#ifdef __cplusplus +} // extern "C" + +// reference-based equivalents for c++ +inline void sg_setup(const sg_desc& desc) { return sg_setup(&desc); } + +inline sg_buffer sg_make_buffer(const sg_buffer_desc& desc) { return sg_make_buffer(&desc); } +inline sg_image sg_make_image(const sg_image_desc& desc) { return sg_make_image(&desc); } +inline sg_sampler sg_make_sampler(const sg_sampler_desc& desc) { return sg_make_sampler(&desc); } +inline sg_shader sg_make_shader(const sg_shader_desc& desc) { return sg_make_shader(&desc); } +inline sg_pipeline sg_make_pipeline(const sg_pipeline_desc& desc) { return sg_make_pipeline(&desc); } +inline sg_attachments sg_make_attachments(const sg_attachments_desc& desc) { return sg_make_attachments(&desc); } +inline void sg_update_image(sg_image img, const sg_image_data& data) { return sg_update_image(img, &data); } + +inline void sg_begin_pass(const sg_pass& pass) { return sg_begin_pass(&pass); } +inline void sg_apply_bindings(const sg_bindings& bindings) { return sg_apply_bindings(&bindings); } +inline void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range& data) { return sg_apply_uniforms(stage, ub_index, &data); } + +inline sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc& desc) { return sg_query_buffer_defaults(&desc); } +inline sg_image_desc sg_query_image_defaults(const sg_image_desc& desc) { return sg_query_image_defaults(&desc); } +inline sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc& desc) { return sg_query_sampler_defaults(&desc); } +inline sg_shader_desc sg_query_shader_defaults(const sg_shader_desc& desc) { return sg_query_shader_defaults(&desc); } +inline sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc& desc) { return sg_query_pipeline_defaults(&desc); } +inline sg_attachments_desc sg_query_attachments_defaults(const sg_attachments_desc& desc) { return sg_query_attachments_defaults(&desc); } + +inline void sg_init_buffer(sg_buffer buf, const sg_buffer_desc& desc) { return sg_init_buffer(buf, &desc); } +inline void sg_init_image(sg_image img, const sg_image_desc& desc) { return sg_init_image(img, &desc); } +inline void sg_init_sampler(sg_sampler smp, const sg_sampler_desc& desc) { return sg_init_sampler(smp, &desc); } +inline void sg_init_shader(sg_shader shd, const sg_shader_desc& desc) { return sg_init_shader(shd, &desc); } +inline void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc& desc) { return sg_init_pipeline(pip, &desc); } +inline void sg_init_attachments(sg_attachments atts, const sg_attachments_desc& desc) { return sg_init_attachments(atts, &desc); } + +inline void sg_update_buffer(sg_buffer buf_id, const sg_range& data) { return sg_update_buffer(buf_id, &data); } +inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_append_buffer(buf_id, &data); } +#endif +#endif // SOKOL_GFX_INCLUDED + +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation +#ifdef SOKOL_GFX_IMPL +#define SOKOL_GFX_IMPL_INCLUDED (1) + +#if !(defined(SOKOL_GLCORE)||defined(SOKOL_GLES3)||defined(SOKOL_D3D11)||defined(SOKOL_METAL)||defined(SOKOL_WGPU)||defined(SOKOL_DUMMY_BACKEND)) +#error "Please select a backend with SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND" +#endif +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sg_desc.allocator to override memory allocation functions" +#endif + +#include // malloc, free +#include // memset +#include // FLT_MAX + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef SOKOL_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#endif + +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif + +#ifndef _SOKOL_UNUSED + #define _SOKOL_UNUSED(x) (void)(x) +#endif + +#if defined(SOKOL_TRACE_HOOKS) +#define _SG_TRACE_ARGS(fn, ...) if (_sg.hooks.fn) { _sg.hooks.fn(__VA_ARGS__, _sg.hooks.user_data); } +#define _SG_TRACE_NOARGS(fn) if (_sg.hooks.fn) { _sg.hooks.fn(_sg.hooks.user_data); } +#else +#define _SG_TRACE_ARGS(fn, ...) +#define _SG_TRACE_NOARGS(fn) +#endif + +// default clear values +#ifndef SG_DEFAULT_CLEAR_RED +#define SG_DEFAULT_CLEAR_RED (0.5f) +#endif +#ifndef SG_DEFAULT_CLEAR_GREEN +#define SG_DEFAULT_CLEAR_GREEN (0.5f) +#endif +#ifndef SG_DEFAULT_CLEAR_BLUE +#define SG_DEFAULT_CLEAR_BLUE (0.5f) +#endif +#ifndef SG_DEFAULT_CLEAR_ALPHA +#define SG_DEFAULT_CLEAR_ALPHA (1.0f) +#endif +#ifndef SG_DEFAULT_CLEAR_DEPTH +#define SG_DEFAULT_CLEAR_DEPTH (1.0f) +#endif +#ifndef SG_DEFAULT_CLEAR_STENCIL +#define SG_DEFAULT_CLEAR_STENCIL (0) +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4115) // named type definition in parentheses +#pragma warning(disable:4505) // unreferenced local function has been removed +#pragma warning(disable:4201) // nonstandard extension used: nameless struct/union (needed by d3d11.h) +#pragma warning(disable:4054) // 'type cast': from function pointer +#pragma warning(disable:4055) // 'type cast': from data pointer +#endif + +#if defined(SOKOL_D3D11) + #ifndef D3D11_NO_HELPERS + #define D3D11_NO_HELPERS + #endif + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #include + #ifdef _MSC_VER + #pragma comment (lib, "kernel32") + #pragma comment (lib, "user32") + #pragma comment (lib, "dxgi") + #pragma comment (lib, "d3d11") + #endif +#elif defined(SOKOL_METAL) + // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting + #if !defined(__cplusplus) + #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields) + #error "sokol_gfx.h requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)" + #endif + #endif + #include + #include + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + #define _SG_TARGET_MACOS (1) + #else + #define _SG_TARGET_IOS (1) + #if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR + #define _SG_TARGET_IOS_SIMULATOR (1) + #endif + #endif + #import + #import // needed for CAMetalDrawable +#elif defined(SOKOL_WGPU) + #include + #if defined(__EMSCRIPTEN__) + #include + #endif +#elif defined(SOKOL_GLCORE) || defined(SOKOL_GLES3) + #define _SOKOL_ANY_GL (1) + + // include platform specific GL headers (or on Win32: use an embedded GL loader) + #if !defined(SOKOL_EXTERNAL_GL_LOADER) + #if defined(_WIN32) + #if defined(SOKOL_GLCORE) && !defined(SOKOL_EXTERNAL_GL_LOADER) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #define _SOKOL_USE_WIN32_GL_LOADER (1) + #pragma comment (lib, "kernel32") // GetProcAddress() + #endif + #elif defined(__APPLE__) + #include + #ifndef GL_SILENCE_DEPRECATION + #define GL_SILENCE_DEPRECATION + #endif + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + #include + #else + #include + #include + #endif + #elif defined(__EMSCRIPTEN__) || defined(__ANDROID__) + #if defined(SOKOL_GLES3) + #include + #endif + #elif defined(__linux__) || defined(__unix__) + #if defined(SOKOL_GLCORE) + #define GL_GLEXT_PROTOTYPES + //#include + #else + #include + #include + #endif + #endif + #endif + + // optional GL loader definitions (only on Win32) + #if defined(_SOKOL_USE_WIN32_GL_LOADER) + #define __gl_h_ 1 + #define __gl32_h_ 1 + #define __gl31_h_ 1 + #define __GL_H__ 1 + #define __glext_h_ 1 + #define __GLEXT_H_ 1 + #define __gltypes_h_ 1 + #define __glcorearb_h_ 1 + #define __gl_glcorearb_h_ 1 + #define GL_APIENTRY APIENTRY + + typedef unsigned int GLenum; + typedef unsigned int GLuint; + typedef int GLsizei; + typedef char GLchar; + typedef ptrdiff_t GLintptr; + typedef ptrdiff_t GLsizeiptr; + typedef double GLclampd; + typedef unsigned short GLushort; + typedef unsigned char GLubyte; + typedef unsigned char GLboolean; + typedef uint64_t GLuint64; + typedef double GLdouble; + typedef unsigned short GLhalf; + typedef float GLclampf; + typedef unsigned int GLbitfield; + typedef signed char GLbyte; + typedef short GLshort; + typedef void GLvoid; + typedef int64_t GLint64; + typedef float GLfloat; + typedef int GLint; + #define GL_INT_2_10_10_10_REV 0x8D9F + #define GL_R32F 0x822E + #define GL_PROGRAM_POINT_SIZE 0x8642 + #define GL_DEPTH_ATTACHMENT 0x8D00 + #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A + #define GL_COLOR_ATTACHMENT2 0x8CE2 + #define GL_COLOR_ATTACHMENT0 0x8CE0 + #define GL_R16F 0x822D + #define GL_COLOR_ATTACHMENT22 0x8CF6 + #define GL_DRAW_FRAMEBUFFER 0x8CA9 + #define GL_FRAMEBUFFER_COMPLETE 0x8CD5 + #define GL_NUM_EXTENSIONS 0x821D + #define GL_INFO_LOG_LENGTH 0x8B84 + #define GL_VERTEX_SHADER 0x8B31 + #define GL_INCR 0x1E02 + #define GL_DYNAMIC_DRAW 0x88E8 + #define GL_STATIC_DRAW 0x88E4 + #define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 + #define GL_TEXTURE_CUBE_MAP 0x8513 + #define GL_FUNC_SUBTRACT 0x800A + #define GL_FUNC_REVERSE_SUBTRACT 0x800B + #define GL_CONSTANT_COLOR 0x8001 + #define GL_DECR_WRAP 0x8508 + #define GL_R8 0x8229 + #define GL_LINEAR_MIPMAP_LINEAR 0x2703 + #define GL_ELEMENT_ARRAY_BUFFER 0x8893 + #define GL_SHORT 0x1402 + #define GL_DEPTH_TEST 0x0B71 + #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 + #define GL_LINK_STATUS 0x8B82 + #define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 + #define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E + #define GL_RGBA16F 0x881A + #define GL_CONSTANT_ALPHA 0x8003 + #define GL_READ_FRAMEBUFFER 0x8CA8 + #define GL_TEXTURE0 0x84C0 + #define GL_TEXTURE_MIN_LOD 0x813A + #define GL_CLAMP_TO_EDGE 0x812F + #define GL_UNSIGNED_SHORT_5_6_5 0x8363 + #define GL_TEXTURE_WRAP_R 0x8072 + #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 + #define GL_NEAREST_MIPMAP_NEAREST 0x2700 + #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 + #define GL_SRC_ALPHA_SATURATE 0x0308 + #define GL_STREAM_DRAW 0x88E0 + #define GL_ONE 1 + #define GL_NEAREST_MIPMAP_LINEAR 0x2702 + #define GL_RGB10_A2 0x8059 + #define GL_RGBA8 0x8058 + #define GL_SRGB8_ALPHA8 0x8C43 + #define GL_COLOR_ATTACHMENT1 0x8CE1 + #define GL_RGBA4 0x8056 + #define GL_RGB8 0x8051 + #define GL_ARRAY_BUFFER 0x8892 + #define GL_STENCIL 0x1802 + #define GL_TEXTURE_2D 0x0DE1 + #define GL_DEPTH 0x1801 + #define GL_FRONT 0x0404 + #define GL_STENCIL_BUFFER_BIT 0x00000400 + #define GL_REPEAT 0x2901 + #define GL_RGBA 0x1908 + #define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 + #define GL_DECR 0x1E03 + #define GL_FRAGMENT_SHADER 0x8B30 + #define GL_FLOAT 0x1406 + #define GL_TEXTURE_MAX_LOD 0x813B + #define GL_DEPTH_COMPONENT 0x1902 + #define GL_ONE_MINUS_DST_ALPHA 0x0305 + #define GL_COLOR 0x1800 + #define GL_TEXTURE_2D_ARRAY 0x8C1A + #define GL_TRIANGLES 0x0004 + #define GL_UNSIGNED_BYTE 0x1401 + #define GL_TEXTURE_MAG_FILTER 0x2800 + #define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 + #define GL_NONE 0 + #define GL_SRC_COLOR 0x0300 + #define GL_BYTE 0x1400 + #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A + #define GL_LINE_STRIP 0x0003 + #define GL_TEXTURE_3D 0x806F + #define GL_CW 0x0900 + #define GL_LINEAR 0x2601 + #define GL_RENDERBUFFER 0x8D41 + #define GL_GEQUAL 0x0206 + #define GL_COLOR_BUFFER_BIT 0x00004000 + #define GL_RGBA32F 0x8814 + #define GL_BLEND 0x0BE2 + #define GL_ONE_MINUS_SRC_ALPHA 0x0303 + #define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 + #define GL_TEXTURE_WRAP_T 0x2803 + #define GL_TEXTURE_WRAP_S 0x2802 + #define GL_TEXTURE_MIN_FILTER 0x2801 + #define GL_LINEAR_MIPMAP_NEAREST 0x2701 + #define GL_EXTENSIONS 0x1F03 + #define GL_NO_ERROR 0 + #define GL_REPLACE 0x1E01 + #define GL_KEEP 0x1E00 + #define GL_CCW 0x0901 + #define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 + #define GL_RGB 0x1907 + #define GL_TRIANGLE_STRIP 0x0005 + #define GL_FALSE 0 + #define GL_ZERO 0 + #define GL_CULL_FACE 0x0B44 + #define GL_INVERT 0x150A + #define GL_INT 0x1404 + #define GL_UNSIGNED_INT 0x1405 + #define GL_UNSIGNED_SHORT 0x1403 + #define GL_NEAREST 0x2600 + #define GL_SCISSOR_TEST 0x0C11 + #define GL_LEQUAL 0x0203 + #define GL_STENCIL_TEST 0x0B90 + #define GL_DITHER 0x0BD0 + #define GL_DEPTH_COMPONENT32F 0x8CAC + #define GL_EQUAL 0x0202 + #define GL_FRAMEBUFFER 0x8D40 + #define GL_RGB5 0x8050 + #define GL_LINES 0x0001 + #define GL_DEPTH_BUFFER_BIT 0x00000100 + #define GL_SRC_ALPHA 0x0302 + #define GL_INCR_WRAP 0x8507 + #define GL_LESS 0x0201 + #define GL_MULTISAMPLE 0x809D + #define GL_FRAMEBUFFER_BINDING 0x8CA6 + #define GL_BACK 0x0405 + #define GL_ALWAYS 0x0207 + #define GL_FUNC_ADD 0x8006 + #define GL_ONE_MINUS_DST_COLOR 0x0307 + #define GL_NOTEQUAL 0x0205 + #define GL_DST_COLOR 0x0306 + #define GL_COMPILE_STATUS 0x8B81 + #define GL_RED 0x1903 + #define GL_COLOR_ATTACHMENT3 0x8CE3 + #define GL_DST_ALPHA 0x0304 + #define GL_RGB5_A1 0x8057 + #define GL_GREATER 0x0204 + #define GL_POLYGON_OFFSET_FILL 0x8037 + #define GL_TRUE 1 + #define GL_NEVER 0x0200 + #define GL_POINTS 0x0000 + #define GL_ONE_MINUS_SRC_COLOR 0x0301 + #define GL_MIRRORED_REPEAT 0x8370 + #define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D + #define GL_R11F_G11F_B10F 0x8C3A + #define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B + #define GL_RGB9_E5 0x8C3D + #define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E + #define GL_RGBA32UI 0x8D70 + #define GL_RGB32UI 0x8D71 + #define GL_RGBA16UI 0x8D76 + #define GL_RGB16UI 0x8D77 + #define GL_RGBA8UI 0x8D7C + #define GL_RGB8UI 0x8D7D + #define GL_RGBA32I 0x8D82 + #define GL_RGB32I 0x8D83 + #define GL_RGBA16I 0x8D88 + #define GL_RGB16I 0x8D89 + #define GL_RGBA8I 0x8D8E + #define GL_RGB8I 0x8D8F + #define GL_RED_INTEGER 0x8D94 + #define GL_RG 0x8227 + #define GL_RG_INTEGER 0x8228 + #define GL_R8 0x8229 + #define GL_R16 0x822A + #define GL_RG8 0x822B + #define GL_RG16 0x822C + #define GL_R16F 0x822D + #define GL_R32F 0x822E + #define GL_RG16F 0x822F + #define GL_RG32F 0x8230 + #define GL_R8I 0x8231 + #define GL_R8UI 0x8232 + #define GL_R16I 0x8233 + #define GL_R16UI 0x8234 + #define GL_R32I 0x8235 + #define GL_R32UI 0x8236 + #define GL_RG8I 0x8237 + #define GL_RG8UI 0x8238 + #define GL_RG16I 0x8239 + #define GL_RG16UI 0x823A + #define GL_RG32I 0x823B + #define GL_RG32UI 0x823C + #define GL_RGBA_INTEGER 0x8D99 + #define GL_R8_SNORM 0x8F94 + #define GL_RG8_SNORM 0x8F95 + #define GL_RGB8_SNORM 0x8F96 + #define GL_RGBA8_SNORM 0x8F97 + #define GL_R16_SNORM 0x8F98 + #define GL_RG16_SNORM 0x8F99 + #define GL_RGB16_SNORM 0x8F9A + #define GL_RGBA16_SNORM 0x8F9B + #define GL_RGBA16 0x805B + #define GL_MAX_TEXTURE_SIZE 0x0D33 + #define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C + #define GL_MAX_3D_TEXTURE_SIZE 0x8073 + #define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF + #define GL_MAX_VERTEX_ATTRIBS 0x8869 + #define GL_CLAMP_TO_BORDER 0x812D + #define GL_TEXTURE_BORDER_COLOR 0x1004 + #define GL_CURRENT_PROGRAM 0x8B8D + #define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A + #define GL_UNPACK_ALIGNMENT 0x0CF5 + #define GL_FRAMEBUFFER_SRGB 0x8DB9 + #define GL_TEXTURE_COMPARE_MODE 0x884C + #define GL_TEXTURE_COMPARE_FUNC 0x884D + #define GL_COMPARE_REF_TO_TEXTURE 0x884E + #define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F + #define GL_TEXTURE_MAX_LEVEL 0x813D + #define GL_FRAMEBUFFER_UNDEFINED 0x8219 + #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 + #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 + #define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD + #define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 + #define GL_MAJOR_VERSION 0x821B + #define GL_MINOR_VERSION 0x821C + #endif + + #ifndef GL_UNSIGNED_INT_2_10_10_10_REV + #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 + #endif + #ifndef GL_UNSIGNED_INT_24_8 + #define GL_UNSIGNED_INT_24_8 0x84FA + #endif + #ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE + #endif + #ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF + #endif + #ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 + #endif + #ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 + #endif + #ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 + #endif + #ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT + #define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F + #endif + #ifndef GL_COMPRESSED_RED_RGTC1 + #define GL_COMPRESSED_RED_RGTC1 0x8DBB + #endif + #ifndef GL_COMPRESSED_SIGNED_RED_RGTC1 + #define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC + #endif + #ifndef GL_COMPRESSED_RED_GREEN_RGTC2 + #define GL_COMPRESSED_RED_GREEN_RGTC2 0x8DBD + #endif + #ifndef GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2 + #define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2 0x8DBE + #endif + #ifndef GL_COMPRESSED_RGBA_BPTC_UNORM_ARB + #define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C + #endif + #ifndef GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB + #define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D + #endif + #ifndef GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB + #define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E + #endif + #ifndef GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB + #define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F + #endif + #ifndef GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG + #define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 + #endif + #ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG + #define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 + #endif + #ifndef GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG + #define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 + #endif + #ifndef GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG + #define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 + #endif + #ifndef GL_COMPRESSED_RGB8_ETC2 + #define GL_COMPRESSED_RGB8_ETC2 0x9274 + #endif + #ifndef GL_COMPRESSED_SRGB8_ETC2 + #define GL_COMPRESSED_SRGB8_ETC2 0x9275 + #endif + #ifndef GL_COMPRESSED_RGBA8_ETC2_EAC + #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 + #endif + #ifndef GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC + #define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 + #endif + #ifndef GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 + #define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 + #endif + #ifndef GL_COMPRESSED_R11_EAC + #define GL_COMPRESSED_R11_EAC 0x9270 + #endif + #ifndef GL_COMPRESSED_SIGNED_R11_EAC + #define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 + #endif + #ifndef GL_COMPRESSED_RG11_EAC + #define GL_COMPRESSED_RG11_EAC 0x9272 + #endif + #ifndef GL_COMPRESSED_SIGNED_RG11_EAC + #define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 + #endif + #ifndef GL_COMPRESSED_RGBA_ASTC_4x4_KHR + #define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 + #endif + #ifndef GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR + #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 + #endif + #ifndef GL_DEPTH24_STENCIL8 + #define GL_DEPTH24_STENCIL8 0x88F0 + #endif + #ifndef GL_HALF_FLOAT + #define GL_HALF_FLOAT 0x140B + #endif + #ifndef GL_DEPTH_STENCIL + #define GL_DEPTH_STENCIL 0x84F9 + #endif + #ifndef GL_LUMINANCE + #define GL_LUMINANCE 0x1909 + #endif + #ifndef _SG_GL_CHECK_ERROR + #define _SG_GL_CHECK_ERROR() { SOKOL_ASSERT(glGetError() == GL_NO_ERROR); } + #endif +#endif + +#if defined(SOKOL_GLES3) + // on WebGL2, GL_FRAMEBUFFER_UNDEFINED technically doesn't exist (it is defined + // in the Emscripten headers, but may not exist in other WebGL2 shims) + // see: https://github.com/floooh/sokol/pull/933 + #ifndef GL_FRAMEBUFFER_UNDEFINED + #define GL_FRAMEBUFFER_UNDEFINED 0x8219 + #endif +#endif + +// make some GL constants generally available to simplify compilation, +// use of those constants will be filtered by runtime flags +#ifndef GL_SHADER_STORAGE_BUFFER +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#endif + +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs +// resource pool slots +typedef struct { + uint32_t id; + sg_resource_state state; +} _sg_slot_t; + +// resource pool housekeeping struct +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _sg_pool_t; + +_SOKOL_PRIVATE void _sg_init_pool(_sg_pool_t* pool, int num); +_SOKOL_PRIVATE void _sg_discard_pool(_sg_pool_t* pool); +_SOKOL_PRIVATE int _sg_pool_alloc_index(_sg_pool_t* pool); +_SOKOL_PRIVATE void _sg_pool_free_index(_sg_pool_t* pool, int slot_index); +_SOKOL_PRIVATE void _sg_reset_slot(_sg_slot_t* slot); +_SOKOL_PRIVATE uint32_t _sg_slot_alloc(_sg_pool_t* pool, _sg_slot_t* slot, int slot_index); +_SOKOL_PRIVATE int _sg_slot_index(uint32_t id); + +// constants +enum { + _SG_STRING_SIZE = 32, + _SG_SLOT_SHIFT = 16, + _SG_SLOT_MASK = (1<<_SG_SLOT_SHIFT)-1, + _SG_MAX_POOL_SIZE = (1<<_SG_SLOT_SHIFT), + _SG_DEFAULT_BUFFER_POOL_SIZE = 128, + _SG_DEFAULT_IMAGE_POOL_SIZE = 128, + _SG_DEFAULT_SAMPLER_POOL_SIZE = 64, + _SG_DEFAULT_SHADER_POOL_SIZE = 32, + _SG_DEFAULT_PIPELINE_POOL_SIZE = 64, + _SG_DEFAULT_ATTACHMENTS_POOL_SIZE = 16, + _SG_DEFAULT_UB_SIZE = 4 * 1024 * 1024, + _SG_DEFAULT_MAX_COMMIT_LISTENERS = 1024, + _SG_DEFAULT_WGPU_BINDGROUP_CACHE_SIZE = 1024, +}; + +// fixed-size string +typedef struct { + char buf[_SG_STRING_SIZE]; +} _sg_str_t; + +// helper macros +#define _sg_def(val, def) (((val) == 0) ? (def) : (val)) +#define _sg_def_flt(val, def) (((val) == 0.0f) ? (def) : (val)) +#define _sg_min(a,b) (((a)<(b))?(a):(b)) +#define _sg_max(a,b) (((a)>(b))?(a):(b)) +#define _sg_clamp(v,v0,v1) (((v)<(v0))?(v0):(((v)>(v1))?(v1):(v))) +#define _sg_fequal(val,cmp,delta) ((((val)-(cmp))> -(delta))&&(((val)-(cmp))<(delta))) +#define _sg_ispow2(val) ((val&(val-1))==0) +#define _sg_stats_add(key,val) {if(_sg.stats_enabled){ _sg.stats.key+=val;}} + +_SOKOL_PRIVATE void* _sg_malloc_clear(size_t size); +_SOKOL_PRIVATE void _sg_free(void* ptr); +_SOKOL_PRIVATE void _sg_clear(void* ptr, size_t size); + +typedef struct { + int size; + int append_pos; + bool append_overflow; + uint32_t update_frame_index; + uint32_t append_frame_index; + int num_slots; + int active_slot; + sg_buffer_type type; + sg_usage usage; +} _sg_buffer_common_t; + +_SOKOL_PRIVATE void _sg_buffer_common_init(_sg_buffer_common_t* cmn, const sg_buffer_desc* desc) { + cmn->size = (int)desc->size; + cmn->append_pos = 0; + cmn->append_overflow = false; + cmn->update_frame_index = 0; + cmn->append_frame_index = 0; + cmn->num_slots = (desc->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + cmn->active_slot = 0; + cmn->type = desc->type; + cmn->usage = desc->usage; +} + +typedef struct { + uint32_t upd_frame_index; + int num_slots; + int active_slot; + sg_image_type type; + bool render_target; + int width; + int height; + int num_slices; + int num_mipmaps; + sg_usage usage; + sg_pixel_format pixel_format; + int sample_count; +} _sg_image_common_t; + +_SOKOL_PRIVATE void _sg_image_common_init(_sg_image_common_t* cmn, const sg_image_desc* desc) { + cmn->upd_frame_index = 0; + cmn->num_slots = (desc->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + cmn->active_slot = 0; + cmn->type = desc->type; + cmn->render_target = desc->render_target; + cmn->width = desc->width; + cmn->height = desc->height; + cmn->num_slices = desc->num_slices; + cmn->num_mipmaps = desc->num_mipmaps; + cmn->usage = desc->usage; + cmn->pixel_format = desc->pixel_format; + cmn->sample_count = desc->sample_count; +} + +typedef struct { + sg_filter min_filter; + sg_filter mag_filter; + sg_filter mipmap_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + float min_lod; + float max_lod; + sg_border_color border_color; + sg_compare_func compare; + uint32_t max_anisotropy; +} _sg_sampler_common_t; + +_SOKOL_PRIVATE void _sg_sampler_common_init(_sg_sampler_common_t* cmn, const sg_sampler_desc* desc) { + cmn->min_filter = desc->min_filter; + cmn->mag_filter = desc->mag_filter; + cmn->mipmap_filter = desc->mipmap_filter; + cmn->wrap_u = desc->wrap_u; + cmn->wrap_v = desc->wrap_v; + cmn->wrap_w = desc->wrap_w; + cmn->min_lod = desc->min_lod; + cmn->max_lod = desc->max_lod; + cmn->border_color = desc->border_color; + cmn->compare = desc->compare; + cmn->max_anisotropy = desc->max_anisotropy; +} + +typedef struct { + size_t size; +} _sg_shader_uniform_block_t; + +typedef struct { + bool used; + bool readonly; +} _sg_shader_storage_buffer_t; + +typedef struct { + sg_image_type image_type; + sg_image_sample_type sample_type; + bool multisampled; +} _sg_shader_image_t; + +typedef struct { + sg_sampler_type sampler_type; +} _sg_shader_sampler_t; + +// combined image sampler mappings, only needed on GL +typedef struct { + int image_slot; + int sampler_slot; +} _sg_shader_image_sampler_t; + +typedef struct { + int num_uniform_blocks; + int num_storage_buffers; + int num_images; + int num_samplers; + int num_image_samplers; + _sg_shader_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + _sg_shader_storage_buffer_t storage_buffers[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; + _sg_shader_image_t images[SG_MAX_SHADERSTAGE_IMAGES]; + _sg_shader_sampler_t samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; + _sg_shader_image_sampler_t image_samplers[SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS]; +} _sg_shader_stage_t; + +typedef struct { + _sg_shader_stage_t stage[SG_NUM_SHADER_STAGES]; +} _sg_shader_common_t; + +_SOKOL_PRIVATE void _sg_shader_common_init(_sg_shader_common_t* cmn, const sg_shader_desc* desc) { + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; + _sg_shader_stage_t* stage = &cmn->stage[stage_index]; + SOKOL_ASSERT(stage->num_uniform_blocks == 0); + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + if (0 == ub_desc->size) { + break; + } + stage->uniform_blocks[ub_index].size = ub_desc->size; + stage->num_uniform_blocks++; + } + SOKOL_ASSERT(stage->num_images == 0); + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (!img_desc->used) { + break; + } + stage->images[img_index].multisampled = img_desc->multisampled; + stage->images[img_index].image_type = img_desc->image_type; + stage->images[img_index].sample_type = img_desc->sample_type; + stage->num_images++; + } + SOKOL_ASSERT(stage->num_samplers == 0); + for (int smp_index = 0; smp_index < SG_MAX_SHADERSTAGE_SAMPLERS; smp_index++) { + const sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_index]; + if (!smp_desc->used) { + break; + } + stage->samplers[smp_index].sampler_type = smp_desc->sampler_type; + stage->num_samplers++; + } + SOKOL_ASSERT(stage->num_image_samplers == 0); + for (int img_smp_index = 0; img_smp_index < SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS; img_smp_index++) { + const sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_index]; + if (!img_smp_desc->used) { + break; + } + SOKOL_ASSERT((img_smp_desc->image_slot >= 0) && (img_smp_desc->image_slot < stage->num_images)); + stage->image_samplers[img_smp_index].image_slot = img_smp_desc->image_slot; + SOKOL_ASSERT((img_smp_desc->sampler_slot >= 0) && (img_smp_desc->sampler_slot < stage->num_samplers)); + stage->image_samplers[img_smp_index].sampler_slot = img_smp_desc->sampler_slot; + stage->num_image_samplers++; + } + SOKOL_ASSERT(stage->num_storage_buffers == 0); + for (int sbuf_index = 0; sbuf_index < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; sbuf_index++) { + const sg_shader_storage_buffer_desc* sbuf_desc = &stage_desc->storage_buffers[sbuf_index]; + if (!sbuf_desc->used) { + break; + } + stage->storage_buffers[sbuf_index].used = sbuf_desc->used; + stage->storage_buffers[sbuf_index].readonly = sbuf_desc->readonly; + stage->num_storage_buffers++; + } + } +} + +typedef struct { + bool vertex_buffer_layout_active[SG_MAX_VERTEX_BUFFERS]; + bool use_instanced_draw; + sg_shader shader_id; + sg_vertex_layout_state layout; + sg_depth_state depth; + sg_stencil_state stencil; + int color_count; + sg_color_target_state colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_primitive_type primitive_type; + sg_index_type index_type; + sg_cull_mode cull_mode; + sg_face_winding face_winding; + int sample_count; + sg_color blend_color; + bool alpha_to_coverage_enabled; +} _sg_pipeline_common_t; + +_SOKOL_PRIVATE void _sg_pipeline_common_init(_sg_pipeline_common_t* cmn, const sg_pipeline_desc* desc) { + SOKOL_ASSERT((desc->color_count >= 0) && (desc->color_count <= SG_MAX_COLOR_ATTACHMENTS)); + for (int i = 0; i < SG_MAX_VERTEX_BUFFERS; i++) { + cmn->vertex_buffer_layout_active[i] = false; + } + cmn->use_instanced_draw = false; + cmn->shader_id = desc->shader; + cmn->layout = desc->layout; + cmn->depth = desc->depth; + cmn->stencil = desc->stencil; + cmn->color_count = desc->color_count; + for (int i = 0; i < desc->color_count; i++) { + cmn->colors[i] = desc->colors[i]; + } + cmn->primitive_type = desc->primitive_type; + cmn->index_type = desc->index_type; + cmn->cull_mode = desc->cull_mode; + cmn->face_winding = desc->face_winding; + cmn->sample_count = desc->sample_count; + cmn->blend_color = desc->blend_color; + cmn->alpha_to_coverage_enabled = desc->alpha_to_coverage_enabled; +} + +typedef struct { + sg_image image_id; + int mip_level; + int slice; +} _sg_attachment_common_t; + +typedef struct { + int width; + int height; + int num_colors; + _sg_attachment_common_t colors[SG_MAX_COLOR_ATTACHMENTS]; + _sg_attachment_common_t resolves[SG_MAX_COLOR_ATTACHMENTS]; + _sg_attachment_common_t depth_stencil; +} _sg_attachments_common_t; + +_SOKOL_PRIVATE void _sg_attachment_common_init(_sg_attachment_common_t* cmn, const sg_attachment_desc* desc) { + cmn->image_id = desc->image; + cmn->mip_level = desc->mip_level; + cmn->slice = desc->slice; +} + +_SOKOL_PRIVATE void _sg_attachments_common_init(_sg_attachments_common_t* cmn, const sg_attachments_desc* desc, int width, int height) { + SOKOL_ASSERT((width > 0) && (height > 0)); + cmn->width = width; + cmn->height = height; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (desc->colors[i].image.id != SG_INVALID_ID) { + cmn->num_colors++; + _sg_attachment_common_init(&cmn->colors[i], &desc->colors[i]); + _sg_attachment_common_init(&cmn->resolves[i], &desc->resolves[i]); + } + } + if (desc->depth_stencil.image.id != SG_INVALID_ID) { + _sg_attachment_common_init(&cmn->depth_stencil, &desc->depth_stencil); + } +} + +#if defined(SOKOL_DUMMY_BACKEND) +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; +} _sg_dummy_buffer_t; +typedef _sg_dummy_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; +} _sg_dummy_image_t; +typedef _sg_dummy_image_t _sg_image_t; + +typedef struct { + _sg_slot_t slot; + _sg_sampler_common_t cmn; +} _sg_dummy_sampler_t; +typedef _sg_dummy_sampler_t _sg_sampler_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; +} _sg_dummy_shader_t; +typedef _sg_dummy_shader_t _sg_shader_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_t* shader; + _sg_pipeline_common_t cmn; +} _sg_dummy_pipeline_t; +typedef _sg_dummy_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; +} _sg_dummy_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_attachments_common_t cmn; + struct { + _sg_dummy_attachment_t colors[SG_MAX_COLOR_ATTACHMENTS]; + _sg_dummy_attachment_t resolves[SG_MAX_COLOR_ATTACHMENTS]; + _sg_dummy_attachment_t depth_stencil; + } dmy; +} _sg_dummy_attachments_t; +typedef _sg_dummy_attachments_t _sg_attachments_t; + +#elif defined(_SOKOL_ANY_GL) + +#define _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE (SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS * SG_NUM_SHADER_STAGES) +#define _SG_GL_STORAGEBUFFER_STAGE_INDEX_PITCH (SG_MAX_SHADERSTAGE_STORAGEBUFFERS) + +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + GLuint buf[SG_NUM_INFLIGHT_FRAMES]; + bool injected; // if true, external buffers were injected with sg_buffer_desc.gl_buffers + } gl; +} _sg_gl_buffer_t; +typedef _sg_gl_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + GLenum target; + GLuint msaa_render_buffer; + GLuint tex[SG_NUM_INFLIGHT_FRAMES]; + bool injected; // if true, external textures were injected with sg_image_desc.gl_textures + } gl; +} _sg_gl_image_t; +typedef _sg_gl_image_t _sg_image_t; + +typedef struct { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + GLuint smp; + bool injected; // true if external sampler was injects in sg_sampler_desc.gl_sampler + } gl; +} _sg_gl_sampler_t; +typedef _sg_gl_sampler_t _sg_sampler_t; + +typedef struct { + GLint gl_loc; + sg_uniform_type type; + uint16_t count; + uint16_t offset; +} _sg_gl_uniform_t; + +typedef struct { + int num_uniforms; + _sg_gl_uniform_t uniforms[SG_MAX_UB_MEMBERS]; +} _sg_gl_uniform_block_t; + +typedef struct { + int gl_tex_slot; +} _sg_gl_shader_image_sampler_t; + +typedef struct { + _sg_str_t name; +} _sg_gl_shader_attr_t; + +typedef struct { + _sg_gl_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + _sg_gl_shader_image_sampler_t image_samplers[SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS]; +} _sg_gl_shader_stage_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + GLuint prog; + _sg_gl_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + _sg_gl_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + } gl; +} _sg_gl_shader_t; +typedef _sg_gl_shader_t _sg_shader_t; + +typedef struct { + int8_t vb_index; // -1 if attr is not enabled + int8_t divisor; // -1 if not initialized + uint8_t stride; + uint8_t size; + uint8_t normalized; + int offset; + GLenum type; +} _sg_gl_attr_t; + +typedef struct { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + _sg_shader_t* shader; + struct { + _sg_gl_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + sg_depth_state depth; + sg_stencil_state stencil; + sg_primitive_type primitive_type; + sg_blend_state blend; + sg_color_mask color_write_mask[SG_MAX_COLOR_ATTACHMENTS]; + sg_cull_mode cull_mode; + sg_face_winding face_winding; + int sample_count; + bool alpha_to_coverage_enabled; + } gl; +} _sg_gl_pipeline_t; +typedef _sg_gl_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; +} _sg_gl_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_attachments_common_t cmn; + struct { + GLuint fb; + _sg_gl_attachment_t colors[SG_MAX_COLOR_ATTACHMENTS]; + _sg_gl_attachment_t resolves[SG_MAX_COLOR_ATTACHMENTS]; + _sg_gl_attachment_t depth_stencil; + GLuint msaa_resolve_framebuffer[SG_MAX_COLOR_ATTACHMENTS]; + } gl; +} _sg_gl_attachments_t; +typedef _sg_gl_attachments_t _sg_attachments_t; + +typedef struct { + _sg_gl_attr_t gl_attr; + GLuint gl_vbuf; +} _sg_gl_cache_attr_t; + +typedef struct { + GLenum target; + GLuint texture; + GLuint sampler; +} _sg_gl_cache_texture_sampler_bind_slot; + +typedef struct { + sg_depth_state depth; + sg_stencil_state stencil; + sg_blend_state blend; + sg_color_mask color_write_mask[SG_MAX_COLOR_ATTACHMENTS]; + sg_cull_mode cull_mode; + sg_face_winding face_winding; + bool polygon_offset_enabled; + int sample_count; + sg_color blend_color; + bool alpha_to_coverage_enabled; + _sg_gl_cache_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + GLuint vertex_buffer; + GLuint index_buffer; + GLuint storage_buffer; // general bind point + GLuint stage_storage_buffers[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; + GLuint stored_vertex_buffer; + GLuint stored_index_buffer; + GLuint stored_storage_buffer; + GLuint prog; + _sg_gl_cache_texture_sampler_bind_slot texture_samplers[_SG_GL_TEXTURE_SAMPLER_CACHE_SIZE]; + _sg_gl_cache_texture_sampler_bind_slot stored_texture_sampler; + int cur_ib_offset; + GLenum cur_primitive_type; + GLenum cur_index_type; + GLenum cur_active_texture; + _sg_pipeline_t* cur_pipeline; + sg_pipeline cur_pipeline_id; +} _sg_gl_state_cache_t; + +typedef struct { + bool valid; + GLuint vao; + _sg_gl_state_cache_t cache; + bool ext_anisotropic; + GLint max_anisotropy; + sg_store_action color_store_actions[SG_MAX_COLOR_ATTACHMENTS]; + sg_store_action depth_store_action; + sg_store_action stencil_store_action; + #if _SOKOL_USE_WIN32_GL_LOADER + HINSTANCE opengl32_dll; + #endif +} _sg_gl_backend_t; + +#elif defined(SOKOL_D3D11) + +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + ID3D11Buffer* buf; + ID3D11ShaderResourceView* srv; + } d3d11; +} _sg_d3d11_buffer_t; +typedef _sg_d3d11_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + DXGI_FORMAT format; + ID3D11Texture2D* tex2d; + ID3D11Texture3D* tex3d; + ID3D11Resource* res; // either tex2d or tex3d + ID3D11ShaderResourceView* srv; + } d3d11; +} _sg_d3d11_image_t; +typedef _sg_d3d11_image_t _sg_image_t; + +typedef struct { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + ID3D11SamplerState* smp; + } d3d11; +} _sg_d3d11_sampler_t; +typedef _sg_d3d11_sampler_t _sg_sampler_t; + +typedef struct { + _sg_str_t sem_name; + int sem_index; +} _sg_d3d11_shader_attr_t; + +typedef struct { + ID3D11Buffer* cbufs[SG_MAX_SHADERSTAGE_UBS]; +} _sg_d3d11_shader_stage_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + _sg_d3d11_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + _sg_d3d11_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + ID3D11VertexShader* vs; + ID3D11PixelShader* fs; + void* vs_blob; + size_t vs_blob_length; + } d3d11; +} _sg_d3d11_shader_t; +typedef _sg_d3d11_shader_t _sg_shader_t; + +typedef struct { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + _sg_shader_t* shader; + struct { + UINT stencil_ref; + UINT vb_strides[SG_MAX_VERTEX_BUFFERS]; + D3D_PRIMITIVE_TOPOLOGY topology; + DXGI_FORMAT index_format; + ID3D11InputLayout* il; + ID3D11RasterizerState* rs; + ID3D11DepthStencilState* dss; + ID3D11BlendState* bs; + } d3d11; +} _sg_d3d11_pipeline_t; +typedef _sg_d3d11_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; + union { + ID3D11RenderTargetView* rtv; + ID3D11DepthStencilView* dsv; + } view; +} _sg_d3d11_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_attachments_common_t cmn; + struct { + _sg_d3d11_attachment_t colors[SG_MAX_COLOR_ATTACHMENTS]; + _sg_d3d11_attachment_t resolves[SG_MAX_COLOR_ATTACHMENTS]; + _sg_d3d11_attachment_t depth_stencil; + } d3d11; +} _sg_d3d11_attachments_t; +typedef _sg_d3d11_attachments_t _sg_attachments_t; + +typedef struct { + bool valid; + ID3D11Device* dev; + ID3D11DeviceContext* ctx; + bool use_indexed_draw; + bool use_instanced_draw; + _sg_pipeline_t* cur_pipeline; + sg_pipeline cur_pipeline_id; + struct { + ID3D11RenderTargetView* render_view; + ID3D11RenderTargetView* resolve_view; + } cur_pass; + // on-demand loaded d3dcompiler_47.dll handles + HINSTANCE d3dcompiler_dll; + bool d3dcompiler_dll_load_failed; + pD3DCompile D3DCompile_func; + // global subresourcedata array for texture updates + D3D11_SUBRESOURCE_DATA subres_data[SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS]; +} _sg_d3d11_backend_t; + +#elif defined(SOKOL_METAL) + +#if defined(_SG_TARGET_MACOS) || defined(_SG_TARGET_IOS_SIMULATOR) +#define _SG_MTL_UB_ALIGN (256) +#else +#define _SG_MTL_UB_ALIGN (16) +#endif +#define _SG_MTL_INVALID_SLOT_INDEX (0) + +typedef struct { + uint32_t frame_index; // frame index at which it is safe to release this resource + int slot_index; +} _sg_mtl_release_item_t; + +typedef struct { + NSMutableArray* pool; + int num_slots; + int free_queue_top; + int* free_queue; + int release_queue_front; + int release_queue_back; + _sg_mtl_release_item_t* release_queue; +} _sg_mtl_idpool_t; + +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + int buf[SG_NUM_INFLIGHT_FRAMES]; // index into _sg_mtl_pool + } mtl; +} _sg_mtl_buffer_t; +typedef _sg_mtl_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + int tex[SG_NUM_INFLIGHT_FRAMES]; + } mtl; +} _sg_mtl_image_t; +typedef _sg_mtl_image_t _sg_image_t; + +typedef struct { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + int sampler_state; + } mtl; +} _sg_mtl_sampler_t; +typedef _sg_mtl_sampler_t _sg_sampler_t; + +typedef struct { + int mtl_lib; + int mtl_func; +} _sg_mtl_shader_stage_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + _sg_mtl_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + } mtl; +} _sg_mtl_shader_t; +typedef _sg_mtl_shader_t _sg_shader_t; + +typedef struct { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + _sg_shader_t* shader; + struct { + MTLPrimitiveType prim_type; + int index_size; + MTLIndexType index_type; + MTLCullMode cull_mode; + MTLWinding winding; + uint32_t stencil_ref; + int rps; + int dss; + } mtl; +} _sg_mtl_pipeline_t; +typedef _sg_mtl_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; +} _sg_mtl_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_attachments_common_t cmn; + struct { + _sg_mtl_attachment_t colors[SG_MAX_COLOR_ATTACHMENTS]; + _sg_mtl_attachment_t resolves[SG_MAX_COLOR_ATTACHMENTS]; + _sg_mtl_attachment_t depth_stencil; + } mtl; +} _sg_mtl_attachments_t; +typedef _sg_mtl_attachments_t _sg_attachments_t; + +// resource binding state cache +typedef struct { + const _sg_pipeline_t* cur_pipeline; + sg_pipeline cur_pipeline_id; + const _sg_buffer_t* cur_indexbuffer; + sg_buffer cur_indexbuffer_id; + int cur_indexbuffer_offset; + int cur_vertexbuffer_offsets[SG_MAX_VERTEX_BUFFERS]; + sg_buffer cur_vertexbuffer_ids[SG_MAX_VERTEX_BUFFERS]; + sg_image cur_vs_image_ids[SG_MAX_SHADERSTAGE_IMAGES]; + sg_image cur_fs_image_ids[SG_MAX_SHADERSTAGE_IMAGES]; + sg_sampler cur_vs_sampler_ids[SG_MAX_SHADERSTAGE_SAMPLERS]; + sg_sampler cur_fs_sampler_ids[SG_MAX_SHADERSTAGE_SAMPLERS]; + sg_buffer cur_vs_storagebuffer_ids[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; + sg_buffer cur_fs_storagebuffer_ids[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; +} _sg_mtl_state_cache_t; + +typedef struct { + bool valid; + bool use_shared_storage_mode; + uint32_t cur_frame_rotate_index; + int ub_size; + int cur_ub_offset; + uint8_t* cur_ub_base_ptr; + _sg_mtl_state_cache_t state_cache; + _sg_mtl_idpool_t idpool; + dispatch_semaphore_t sem; + id device; + id cmd_queue; + id cmd_buffer; + id cmd_encoder; + id cur_drawable; + id uniform_buffers[SG_NUM_INFLIGHT_FRAMES]; +} _sg_mtl_backend_t; + +#elif defined(SOKOL_WGPU) + +#define _SG_WGPU_ROWPITCH_ALIGN (256) +#define _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE (1<<16) // also see WGPULimits.maxUniformBufferBindingSize +#define _SG_WGPU_NUM_BINDGROUPS (2) // 0: uniforms, 1: images and sampler on both shader stages +#define _SG_WGPU_UNIFORM_BINDGROUP_INDEX (0) +#define _SG_WGPU_IMAGE_SAMPLER_BINDGROUP_INDEX (1) +#define _SG_WGPU_MAX_BINDGROUP_ENTRIES (SG_NUM_SHADER_STAGES * (SG_MAX_SHADERSTAGE_IMAGES + SG_MAX_SHADERSTAGE_SAMPLERS + SG_MAX_SHADERSTAGE_STORAGEBUFFERS)) + +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + WGPUBuffer buf; + } wgpu; +} _sg_wgpu_buffer_t; +typedef _sg_wgpu_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + WGPUTexture tex; + WGPUTextureView view; + } wgpu; +} _sg_wgpu_image_t; +typedef _sg_wgpu_image_t _sg_image_t; + +typedef struct { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + WGPUSampler smp; + } wgpu; +} _sg_wgpu_sampler_t; +typedef _sg_wgpu_sampler_t _sg_sampler_t; + +typedef struct { + WGPUShaderModule module; + _sg_str_t entry; +} _sg_wgpu_shader_stage_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + _sg_wgpu_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + WGPUBindGroupLayout bind_group_layout; + } wgpu; +} _sg_wgpu_shader_t; +typedef _sg_wgpu_shader_t _sg_shader_t; + +typedef struct { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + _sg_shader_t* shader; + struct { + WGPURenderPipeline pip; + WGPUColor blend_color; + } wgpu; +} _sg_wgpu_pipeline_t; +typedef _sg_wgpu_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; + WGPUTextureView view; +} _sg_wgpu_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_attachments_common_t cmn; + struct { + _sg_wgpu_attachment_t colors[SG_MAX_COLOR_ATTACHMENTS]; + _sg_wgpu_attachment_t resolves[SG_MAX_COLOR_ATTACHMENTS]; + _sg_wgpu_attachment_t depth_stencil; + } wgpu; +} _sg_wgpu_attachments_t; +typedef _sg_wgpu_attachments_t _sg_attachments_t; + +// a pool of per-frame uniform buffers +typedef struct { + uint32_t num_bytes; + uint32_t offset; // current offset into buf + uint8_t* staging; // intermediate buffer for uniform data updates + WGPUBuffer buf; // the GPU-side uniform buffer + struct { + WGPUBindGroupLayout group_layout; + WGPUBindGroup group; + uint32_t offsets[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + } bind; +} _sg_wgpu_uniform_buffer_t; + +typedef struct { + uint32_t id; +} _sg_wgpu_bindgroup_handle_t; + +#define _SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS (1 + _SG_WGPU_MAX_BINDGROUP_ENTRIES) +typedef struct { + uint64_t hash; + uint32_t items[_SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS]; +} _sg_wgpu_bindgroups_cache_key_t; + +typedef struct { + uint32_t num; // must be 2^n + uint32_t index_mask; // mask to turn hash into valid index + _sg_wgpu_bindgroup_handle_t* items; +} _sg_wgpu_bindgroups_cache_t; + +typedef struct { + _sg_slot_t slot; + WGPUBindGroup bindgroup; + _sg_wgpu_bindgroups_cache_key_t key; +} _sg_wgpu_bindgroup_t; + +typedef struct { + _sg_pool_t pool; + _sg_wgpu_bindgroup_t* bindgroups; +} _sg_wgpu_bindgroups_pool_t; + +typedef struct { + struct { + sg_buffer buffer; + int offset; + } vbs[SG_MAX_VERTEX_BUFFERS]; + struct { + sg_buffer buffer; + int offset; + } ib; + _sg_wgpu_bindgroup_handle_t bg; +} _sg_wgpu_bindings_cache_t; + +// the WGPU backend state +typedef struct { + bool valid; + bool use_indexed_draw; + WGPUDevice dev; + WGPUSupportedLimits limits; + WGPUQueue queue; + WGPUCommandEncoder cmd_enc; + WGPURenderPassEncoder pass_enc; + WGPUBindGroup empty_bind_group; + const _sg_pipeline_t* cur_pipeline; + sg_pipeline cur_pipeline_id; + _sg_wgpu_uniform_buffer_t uniform; + _sg_wgpu_bindings_cache_t bindings_cache; + _sg_wgpu_bindgroups_cache_t bindgroups_cache; + _sg_wgpu_bindgroups_pool_t bindgroups_pool; +} _sg_wgpu_backend_t; +#endif + +// POOL STRUCTS + +// this *MUST* remain 0 +#define _SG_INVALID_SLOT_INDEX (0) + +typedef struct { + _sg_pool_t buffer_pool; + _sg_pool_t image_pool; + _sg_pool_t sampler_pool; + _sg_pool_t shader_pool; + _sg_pool_t pipeline_pool; + _sg_pool_t attachments_pool; + _sg_buffer_t* buffers; + _sg_image_t* images; + _sg_sampler_t* samplers; + _sg_shader_t* shaders; + _sg_pipeline_t* pipelines; + _sg_attachments_t* attachments; +} _sg_pools_t; + +typedef struct { + int num; // number of allocated commit listener items + int upper; // the current upper index (no valid items past this point) + sg_commit_listener* items; +} _sg_commit_listeners_t; + +// resolved resource bindings struct +typedef struct { + _sg_pipeline_t* pip; + int num_vbs; + int num_vs_imgs; + int num_vs_smps; + int num_vs_sbufs; + int num_fs_imgs; + int num_fs_smps; + int num_fs_sbufs; + int vb_offsets[SG_MAX_VERTEX_BUFFERS]; + int ib_offset; + _sg_buffer_t* vbs[SG_MAX_VERTEX_BUFFERS]; + _sg_buffer_t* ib; + _sg_image_t* vs_imgs[SG_MAX_SHADERSTAGE_IMAGES]; + _sg_sampler_t* vs_smps[SG_MAX_SHADERSTAGE_SAMPLERS]; + _sg_buffer_t* vs_sbufs[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; + _sg_image_t* fs_imgs[SG_MAX_SHADERSTAGE_IMAGES]; + _sg_sampler_t* fs_smps[SG_MAX_SHADERSTAGE_SAMPLERS]; + _sg_buffer_t* fs_sbufs[SG_MAX_SHADERSTAGE_STORAGEBUFFERS]; +} _sg_bindings_t; + +typedef struct { + bool sample; + bool filter; + bool render; + bool blend; + bool msaa; + bool depth; +} _sg_pixelformat_info_t; + +typedef struct { + bool valid; + sg_desc desc; // original desc with default values patched in + uint32_t frame_index; + struct { + bool valid; + bool in_pass; + sg_attachments atts_id; // SG_INVALID_ID in a swapchain pass + _sg_attachments_t* atts; // 0 in a swapchain pass + int width; + int height; + struct { + sg_pixel_format color_fmt; + sg_pixel_format depth_fmt; + int sample_count; + } swapchain; + } cur_pass; + sg_pipeline cur_pipeline; + bool next_draw_valid; + #if defined(SOKOL_DEBUG) + sg_log_item validate_error; + #endif + _sg_pools_t pools; + sg_backend backend; + sg_features features; + sg_limits limits; + _sg_pixelformat_info_t formats[_SG_PIXELFORMAT_NUM]; + bool stats_enabled; + sg_frame_stats stats; + sg_frame_stats prev_stats; + #if defined(_SOKOL_ANY_GL) + _sg_gl_backend_t gl; + #elif defined(SOKOL_METAL) + _sg_mtl_backend_t mtl; + #elif defined(SOKOL_D3D11) + _sg_d3d11_backend_t d3d11; + #elif defined(SOKOL_WGPU) + _sg_wgpu_backend_t wgpu; + #endif + #if defined(SOKOL_TRACE_HOOKS) + sg_trace_hooks hooks; + #endif + _sg_commit_listeners_t commit_listeners; +} _sg_state_t; +static _sg_state_t _sg; + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SG_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sg_log_messages[] = { + _SG_LOG_ITEMS +}; +#undef _SG_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SG_PANIC(code) _sg_log(SG_LOGITEM_ ##code, 0, 0, __LINE__) +#define _SG_ERROR(code) _sg_log(SG_LOGITEM_ ##code, 1, 0, __LINE__) +#define _SG_WARN(code) _sg_log(SG_LOGITEM_ ##code, 2, 0, __LINE__) +#define _SG_INFO(code) _sg_log(SG_LOGITEM_ ##code, 3, 0, __LINE__) +#define _SG_LOGMSG(code,msg) _sg_log(SG_LOGITEM_ ##code, 3, msg, __LINE__) +#define _SG_VALIDATE(cond,code) if (!(cond)){ _sg.validate_error = SG_LOGITEM_ ##code; _sg_log(SG_LOGITEM_ ##code, 1, 0, __LINE__); } + +static void _sg_log(sg_log_item log_item, uint32_t log_level, const char* msg, uint32_t line_nr) { + if (_sg.desc.logger.func) { + const char* filename = 0; + #if defined(SOKOL_DEBUG) + filename = __FILE__; + if (0 == msg) { + msg = _sg_log_messages[log_item]; + } + #endif + _sg.desc.logger.func("sg", log_level, log_item, msg, line_nr, filename, _sg.desc.logger.user_data); + } else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory + +// a helper macro to clear a struct with potentially ARC'ed ObjC references +#if defined(SOKOL_METAL) + #if defined(__cplusplus) + #define _SG_CLEAR_ARC_STRUCT(type, item) { item = type(); } + #else + #define _SG_CLEAR_ARC_STRUCT(type, item) { item = (type) { 0 }; } + #endif +#else + #define _SG_CLEAR_ARC_STRUCT(type, item) { _sg_clear(&item, sizeof(item)); } +#endif + +_SOKOL_PRIVATE void _sg_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +_SOKOL_PRIVATE void* _sg_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_sg.desc.allocator.alloc_fn) { + ptr = _sg.desc.allocator.alloc_fn(size, _sg.desc.allocator.user_data); + } else { + ptr = malloc(size); + } + if (0 == ptr) { + _SG_PANIC(MALLOC_FAILED); + } + return ptr; +} + +_SOKOL_PRIVATE void* _sg_malloc_clear(size_t size) { + void* ptr = _sg_malloc(size); + _sg_clear(ptr, size); + return ptr; +} + +_SOKOL_PRIVATE void _sg_free(void* ptr) { + if (_sg.desc.allocator.free_fn) { + _sg.desc.allocator.free_fn(ptr, _sg.desc.allocator.user_data); + } else { + free(ptr); + } +} + +_SOKOL_PRIVATE bool _sg_strempty(const _sg_str_t* str) { + return 0 == str->buf[0]; +} + +_SOKOL_PRIVATE const char* _sg_strptr(const _sg_str_t* str) { + return &str->buf[0]; +} + +_SOKOL_PRIVATE void _sg_strcpy(_sg_str_t* dst, const char* src) { + SOKOL_ASSERT(dst); + if (src) { + #if defined(_MSC_VER) + strncpy_s(dst->buf, _SG_STRING_SIZE, src, (_SG_STRING_SIZE-1)); + #else + strncpy(dst->buf, src, _SG_STRING_SIZE); + #endif + dst->buf[_SG_STRING_SIZE-1] = 0; + } else { + _sg_clear(dst->buf, _SG_STRING_SIZE); + } +} + +// ██ ██ ███████ ██ ██████ ███████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ █████ ██ ██████ █████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ +// +// >>helpers +_SOKOL_PRIVATE uint32_t _sg_align_u32(uint32_t val, uint32_t align) { + SOKOL_ASSERT((align > 0) && ((align & (align - 1)) == 0)); + return (val + (align - 1)) & ~(align - 1); +} + +typedef struct { int x, y, w, h; } _sg_recti_t; + +_SOKOL_PRIVATE _sg_recti_t _sg_clipi(int x, int y, int w, int h, int clip_width, int clip_height) { + x = _sg_min(_sg_max(0, x), clip_width-1); + y = _sg_min(_sg_max(0, y), clip_height-1); + if ((x + w) > clip_width) { + w = clip_width - x; + } + if ((y + h) > clip_height) { + h = clip_height - y; + } + w = _sg_max(w, 1); + h = _sg_max(h, 1); + const _sg_recti_t res = { x, y, w, h }; + return res; +} + +_SOKOL_PRIVATE int _sg_vertexformat_bytesize(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return 4; + case SG_VERTEXFORMAT_FLOAT2: return 8; + case SG_VERTEXFORMAT_FLOAT3: return 12; + case SG_VERTEXFORMAT_FLOAT4: return 16; + case SG_VERTEXFORMAT_BYTE4: return 4; + case SG_VERTEXFORMAT_BYTE4N: return 4; + case SG_VERTEXFORMAT_UBYTE4: return 4; + case SG_VERTEXFORMAT_UBYTE4N: return 4; + case SG_VERTEXFORMAT_SHORT2: return 4; + case SG_VERTEXFORMAT_SHORT2N: return 4; + case SG_VERTEXFORMAT_USHORT2N: return 4; + case SG_VERTEXFORMAT_SHORT4: return 8; + case SG_VERTEXFORMAT_SHORT4N: return 8; + case SG_VERTEXFORMAT_USHORT4N: return 8; + case SG_VERTEXFORMAT_UINT10_N2: return 4; + case SG_VERTEXFORMAT_HALF2: return 4; + case SG_VERTEXFORMAT_HALF4: return 8; + case SG_VERTEXFORMAT_INVALID: return 0; + default: + SOKOL_UNREACHABLE; + return -1; + } +} + +_SOKOL_PRIVATE uint32_t _sg_uniform_alignment(sg_uniform_type type, int array_count, sg_uniform_layout ub_layout) { + if (ub_layout == SG_UNIFORMLAYOUT_NATIVE) { + return 1; + } else { + SOKOL_ASSERT(array_count > 0); + if (array_count == 1) { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_INT: + return 4; + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_INT2: + return 8; + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT3: + case SG_UNIFORMTYPE_INT4: + return 16; + case SG_UNIFORMTYPE_MAT4: + return 16; + default: + SOKOL_UNREACHABLE; + return 1; + } + } else { + return 16; + } + } +} + +_SOKOL_PRIVATE uint32_t _sg_uniform_size(sg_uniform_type type, int array_count, sg_uniform_layout ub_layout) { + SOKOL_ASSERT(array_count > 0); + if (array_count == 1) { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_INT: + return 4; + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_INT2: + return 8; + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_INT3: + return 12; + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT4: + return 16; + case SG_UNIFORMTYPE_MAT4: + return 64; + default: + SOKOL_UNREACHABLE; + return 0; + } + } else { + if (ub_layout == SG_UNIFORMLAYOUT_NATIVE) { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_INT: + return 4 * (uint32_t)array_count; + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_INT2: + return 8 * (uint32_t)array_count; + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_INT3: + return 12 * (uint32_t)array_count; + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT4: + return 16 * (uint32_t)array_count; + case SG_UNIFORMTYPE_MAT4: + return 64 * (uint32_t)array_count; + default: + SOKOL_UNREACHABLE; + return 0; + } + } else { + switch (type) { + case SG_UNIFORMTYPE_FLOAT: + case SG_UNIFORMTYPE_FLOAT2: + case SG_UNIFORMTYPE_FLOAT3: + case SG_UNIFORMTYPE_FLOAT4: + case SG_UNIFORMTYPE_INT: + case SG_UNIFORMTYPE_INT2: + case SG_UNIFORMTYPE_INT3: + case SG_UNIFORMTYPE_INT4: + return 16 * (uint32_t)array_count; + case SG_UNIFORMTYPE_MAT4: + return 64 * (uint32_t)array_count; + default: + SOKOL_UNREACHABLE; + return 0; + } + } + } +} + +_SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_BC1_RGBA: + case SG_PIXELFORMAT_BC2_RGBA: + case SG_PIXELFORMAT_BC3_RGBA: + case SG_PIXELFORMAT_BC3_SRGBA: + case SG_PIXELFORMAT_BC4_R: + case SG_PIXELFORMAT_BC4_RSN: + case SG_PIXELFORMAT_BC5_RG: + case SG_PIXELFORMAT_BC5_RGSN: + case SG_PIXELFORMAT_BC6H_RGBF: + case SG_PIXELFORMAT_BC6H_RGBUF: + case SG_PIXELFORMAT_BC7_RGBA: + case SG_PIXELFORMAT_BC7_SRGBA: + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_SRGB8: + case SG_PIXELFORMAT_ETC2_RGB8A1: + case SG_PIXELFORMAT_ETC2_RGBA8: + case SG_PIXELFORMAT_ETC2_SRGB8A8: + case SG_PIXELFORMAT_EAC_R11: + case SG_PIXELFORMAT_EAC_R11SN: + case SG_PIXELFORMAT_EAC_RG11: + case SG_PIXELFORMAT_EAC_RG11SN: + case SG_PIXELFORMAT_ASTC_4x4_RGBA: + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: + return true; + default: + return false; + } +} + +_SOKOL_PRIVATE bool _sg_is_valid_rendertarget_color_format(sg_pixel_format fmt) { + const int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM)); + return _sg.formats[fmt_index].render && !_sg.formats[fmt_index].depth; +} + +_SOKOL_PRIVATE bool _sg_is_valid_rendertarget_depth_format(sg_pixel_format fmt) { + const int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM)); + return _sg.formats[fmt_index].render && _sg.formats[fmt_index].depth; +} + +_SOKOL_PRIVATE bool _sg_is_depth_or_depth_stencil_format(sg_pixel_format fmt) { + return (SG_PIXELFORMAT_DEPTH == fmt) || (SG_PIXELFORMAT_DEPTH_STENCIL == fmt); +} + +_SOKOL_PRIVATE bool _sg_is_depth_stencil_format(sg_pixel_format fmt) { + return (SG_PIXELFORMAT_DEPTH_STENCIL == fmt); +} + +_SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: + case SG_PIXELFORMAT_R8SN: + case SG_PIXELFORMAT_R8UI: + case SG_PIXELFORMAT_R8SI: + return 1; + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_R16UI: + case SG_PIXELFORMAT_R16SI: + case SG_PIXELFORMAT_R16F: + case SG_PIXELFORMAT_RG8: + case SG_PIXELFORMAT_RG8SN: + case SG_PIXELFORMAT_RG8UI: + case SG_PIXELFORMAT_RG8SI: + return 2; + case SG_PIXELFORMAT_R32UI: + case SG_PIXELFORMAT_R32SI: + case SG_PIXELFORMAT_R32F: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RG16UI: + case SG_PIXELFORMAT_RG16SI: + case SG_PIXELFORMAT_RG16F: + case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_SRGB8A8: + case SG_PIXELFORMAT_RGBA8SN: + case SG_PIXELFORMAT_RGBA8UI: + case SG_PIXELFORMAT_RGBA8SI: + case SG_PIXELFORMAT_BGRA8: + case SG_PIXELFORMAT_RGB10A2: + case SG_PIXELFORMAT_RG11B10F: + case SG_PIXELFORMAT_RGB9E5: + return 4; + case SG_PIXELFORMAT_RG32UI: + case SG_PIXELFORMAT_RG32SI: + case SG_PIXELFORMAT_RG32F: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_RGBA16UI: + case SG_PIXELFORMAT_RGBA16SI: + case SG_PIXELFORMAT_RGBA16F: + return 8; + case SG_PIXELFORMAT_RGBA32UI: + case SG_PIXELFORMAT_RGBA32SI: + case SG_PIXELFORMAT_RGBA32F: + return 16; + case SG_PIXELFORMAT_DEPTH: + case SG_PIXELFORMAT_DEPTH_STENCIL: + return 4; + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE int _sg_roundup(int val, int round_to) { + return (val+(round_to-1)) & ~(round_to-1); +} + +_SOKOL_PRIVATE uint32_t _sg_roundup_u32(uint32_t val, uint32_t round_to) { + return (val+(round_to-1)) & ~(round_to-1); +} + +_SOKOL_PRIVATE uint64_t _sg_roundup_u64(uint64_t val, uint64_t round_to) { + return (val+(round_to-1)) & ~(round_to-1); +} + +_SOKOL_PRIVATE bool _sg_multiple_u64(uint64_t val, uint64_t of) { + return (val & (of-1)) == 0; +} + +/* return row pitch for an image + + see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp + + For the special PVRTC pitch computation, see: + GL extension requirement (https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt) + + Quote: + + 6) How is the imageSize argument calculated for the CompressedTexImage2D + and CompressedTexSubImage2D functions. + + Resolution: For PVRTC 4BPP formats the imageSize is calculated as: + ( max(width, 8) * max(height, 8) * 4 + 7) / 8 + For PVRTC 2BPP formats the imageSize is calculated as: + ( max(width, 16) * max(height, 8) * 2 + 7) / 8 +*/ +_SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width, int row_align) { + int pitch; + switch (fmt) { + case SG_PIXELFORMAT_BC1_RGBA: + case SG_PIXELFORMAT_BC4_R: + case SG_PIXELFORMAT_BC4_RSN: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_SRGB8: + case SG_PIXELFORMAT_ETC2_RGB8A1: + case SG_PIXELFORMAT_EAC_R11: + case SG_PIXELFORMAT_EAC_R11SN: + pitch = ((width + 3) / 4) * 8; + pitch = pitch < 8 ? 8 : pitch; + break; + case SG_PIXELFORMAT_BC2_RGBA: + case SG_PIXELFORMAT_BC3_RGBA: + case SG_PIXELFORMAT_BC3_SRGBA: + case SG_PIXELFORMAT_BC5_RG: + case SG_PIXELFORMAT_BC5_RGSN: + case SG_PIXELFORMAT_BC6H_RGBF: + case SG_PIXELFORMAT_BC6H_RGBUF: + case SG_PIXELFORMAT_BC7_RGBA: + case SG_PIXELFORMAT_BC7_SRGBA: + case SG_PIXELFORMAT_ETC2_RGBA8: + case SG_PIXELFORMAT_ETC2_SRGB8A8: + case SG_PIXELFORMAT_EAC_RG11: + case SG_PIXELFORMAT_EAC_RG11SN: + case SG_PIXELFORMAT_ASTC_4x4_RGBA: + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: + pitch = ((width + 3) / 4) * 16; + pitch = pitch < 16 ? 16 : pitch; + break; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + pitch = (_sg_max(width, 8) * 4 + 7) / 8; + break; + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + pitch = (_sg_max(width, 16) * 2 + 7) / 8; + break; + default: + pitch = width * _sg_pixelformat_bytesize(fmt); + break; + } + pitch = _sg_roundup(pitch, row_align); + return pitch; +} + +// compute the number of rows in a surface depending on pixel format +_SOKOL_PRIVATE int _sg_num_rows(sg_pixel_format fmt, int height) { + int num_rows; + switch (fmt) { + case SG_PIXELFORMAT_BC1_RGBA: + case SG_PIXELFORMAT_BC4_R: + case SG_PIXELFORMAT_BC4_RSN: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_SRGB8: + case SG_PIXELFORMAT_ETC2_RGB8A1: + case SG_PIXELFORMAT_ETC2_RGBA8: + case SG_PIXELFORMAT_ETC2_SRGB8A8: + case SG_PIXELFORMAT_EAC_R11: + case SG_PIXELFORMAT_EAC_R11SN: + case SG_PIXELFORMAT_EAC_RG11: + case SG_PIXELFORMAT_EAC_RG11SN: + case SG_PIXELFORMAT_BC2_RGBA: + case SG_PIXELFORMAT_BC3_RGBA: + case SG_PIXELFORMAT_BC3_SRGBA: + case SG_PIXELFORMAT_BC5_RG: + case SG_PIXELFORMAT_BC5_RGSN: + case SG_PIXELFORMAT_BC6H_RGBF: + case SG_PIXELFORMAT_BC6H_RGBUF: + case SG_PIXELFORMAT_BC7_RGBA: + case SG_PIXELFORMAT_BC7_SRGBA: + case SG_PIXELFORMAT_ASTC_4x4_RGBA: + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: + num_rows = ((height + 3) / 4); + break; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + /* NOTE: this is most likely not correct because it ignores any + PVCRTC block size, but multiplied with _sg_row_pitch() + it gives the correct surface pitch. + + See: https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt + */ + num_rows = ((_sg_max(height, 8) + 7) / 8) * 8; + break; + default: + num_rows = height; + break; + } + if (num_rows < 1) { + num_rows = 1; + } + return num_rows; +} + +// return size of a mipmap level +_SOKOL_PRIVATE int _sg_miplevel_dim(int base_dim, int mip_level) { + return _sg_max(base_dim >> mip_level, 1); +} + +/* return pitch of a 2D subimage / texture slice + see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp +*/ +_SOKOL_PRIVATE int _sg_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align) { + int num_rows = _sg_num_rows(fmt, height); + return num_rows * _sg_row_pitch(fmt, width, row_align); +} + +// capability table pixel format helper functions +_SOKOL_PRIVATE void _sg_pixelformat_all(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->blend = true; + pfi->render = true; + pfi->msaa = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_s(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sf(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->filter = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sr(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->render = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sfr(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->render = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_srmd(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->render = true; + pfi->msaa = true; + pfi->depth = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_srm(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->render = true; + pfi->msaa = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sfrm(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->render = true; + pfi->msaa = true; +} +_SOKOL_PRIVATE void _sg_pixelformat_sbrm(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->blend = true; + pfi->render = true; + pfi->msaa = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sbr(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->blend = true; + pfi->render = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sfbr(_sg_pixelformat_info_t* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->blend = true; + pfi->render = true; +} + +_SOKOL_PRIVATE sg_pass_action _sg_pass_action_defaults(const sg_pass_action* action) { + SOKOL_ASSERT(action); + sg_pass_action res = *action; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (res.colors[i].load_action == _SG_LOADACTION_DEFAULT) { + res.colors[i].load_action = SG_LOADACTION_CLEAR; + res.colors[i].clear_value.r = SG_DEFAULT_CLEAR_RED; + res.colors[i].clear_value.g = SG_DEFAULT_CLEAR_GREEN; + res.colors[i].clear_value.b = SG_DEFAULT_CLEAR_BLUE; + res.colors[i].clear_value.a = SG_DEFAULT_CLEAR_ALPHA; + } + if (res.colors[i].store_action == _SG_STOREACTION_DEFAULT) { + res.colors[i].store_action = SG_STOREACTION_STORE; + } + } + if (res.depth.load_action == _SG_LOADACTION_DEFAULT) { + res.depth.load_action = SG_LOADACTION_CLEAR; + res.depth.clear_value = SG_DEFAULT_CLEAR_DEPTH; + } + if (res.depth.store_action == _SG_STOREACTION_DEFAULT) { + res.depth.store_action = SG_STOREACTION_DONTCARE; + } + if (res.stencil.load_action == _SG_LOADACTION_DEFAULT) { + res.stencil.load_action = SG_LOADACTION_CLEAR; + res.stencil.clear_value = SG_DEFAULT_CLEAR_STENCIL; + } + if (res.stencil.store_action == _SG_STOREACTION_DEFAULT) { + res.stencil.store_action = SG_STOREACTION_DONTCARE; + } + return res; +} + +// ██████ ██ ██ ███ ███ ███ ███ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ████ ████ ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ ██ ██ ██ ████ ██ ██ ████ ██ ████ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ██ ██ ██ ██ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>dummy backend +#if defined(SOKOL_DUMMY_BACKEND) + +_SOKOL_PRIVATE void _sg_dummy_setup_backend(const sg_desc* desc) { + SOKOL_ASSERT(desc); + _SOKOL_UNUSED(desc); + _sg.backend = SG_BACKEND_DUMMY; + for (int i = SG_PIXELFORMAT_R8; i < SG_PIXELFORMAT_BC1_RGBA; i++) { + _sg.formats[i].sample = true; + _sg.formats[i].filter = true; + _sg.formats[i].render = true; + _sg.formats[i].blend = true; + _sg.formats[i].msaa = true; + } + _sg.formats[SG_PIXELFORMAT_DEPTH].depth = true; + _sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL].depth = true; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_backend(void) { + // empty +} + +_SOKOL_PRIVATE void _sg_dummy_reset_state_cache(void) { + // empty +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + _SOKOL_UNUSED(buf); + _SOKOL_UNUSED(desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + _SOKOL_UNUSED(buf); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + _SOKOL_UNUSED(img); + _SOKOL_UNUSED(desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + _SOKOL_UNUSED(img); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + _SOKOL_UNUSED(smp); + _SOKOL_UNUSED(desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + _SOKOL_UNUSED(smp); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + _SOKOL_UNUSED(shd); + _SOKOL_UNUSED(desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _SOKOL_UNUSED(shd); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && desc); + pip->shader = shd; + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + pip->cmn.vertex_buffer_layout_active[a_state->buffer_index] = true; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _SOKOL_UNUSED(pip); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_attachments(_sg_attachments_t* atts, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_attachments_desc* desc) { + SOKOL_ASSERT(atts && desc); + SOKOL_ASSERT(color_images && resolve_images); + + for (int i = 0; i < atts->cmn.num_colors; i++) { + const sg_attachment_desc* color_desc = &desc->colors[i]; + _SOKOL_UNUSED(color_desc); + SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == atts->dmy.colors[i].image); + SOKOL_ASSERT(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); + atts->dmy.colors[i].image = color_images[i]; + + const sg_attachment_desc* resolve_desc = &desc->resolves[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == atts->dmy.resolves[i].image); + SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); + SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); + atts->dmy.resolves[i].image = resolve_images[i]; + } + } + + SOKOL_ASSERT(0 == atts->dmy.depth_stencil.image); + const sg_attachment_desc* ds_desc = &desc->depth_stencil; + if (ds_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(ds_img && (ds_img->slot.id == ds_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_img->cmn.pixel_format)); + atts->dmy.depth_stencil.image = ds_img; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + _SOKOL_UNUSED(atts); +} + +_SOKOL_PRIVATE _sg_image_t* _sg_dummy_attachments_color_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->dmy.colors[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_dummy_attachments_resolve_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->dmy.resolves[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_dummy_attachments_ds_image(const _sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + return atts->dmy.depth_stencil.image; +} + +_SOKOL_PRIVATE void _sg_dummy_begin_pass(const sg_pass* pass) { + SOKOL_ASSERT(pass); + _SOKOL_UNUSED(pass); +} + +_SOKOL_PRIVATE void _sg_dummy_end_pass(void) { + // empty +} + +_SOKOL_PRIVATE void _sg_dummy_commit(void) { + // empty +} + +_SOKOL_PRIVATE void _sg_dummy_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + _SOKOL_UNUSED(x); + _SOKOL_UNUSED(y); + _SOKOL_UNUSED(w); + _SOKOL_UNUSED(h); + _SOKOL_UNUSED(origin_top_left); +} + +_SOKOL_PRIVATE void _sg_dummy_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + _SOKOL_UNUSED(x); + _SOKOL_UNUSED(y); + _SOKOL_UNUSED(w); + _SOKOL_UNUSED(h); + _SOKOL_UNUSED(origin_top_left); +} + +_SOKOL_PRIVATE void _sg_dummy_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _SOKOL_UNUSED(pip); +} + +_SOKOL_PRIVATE bool _sg_dummy_apply_bindings(_sg_bindings_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); + _SOKOL_UNUSED(bnd); + return true; +} + +_SOKOL_PRIVATE void _sg_dummy_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + _SOKOL_UNUSED(stage_index); + _SOKOL_UNUSED(ub_index); + _SOKOL_UNUSED(data); +} + +_SOKOL_PRIVATE void _sg_dummy_draw(int base_element, int num_elements, int num_instances) { + _SOKOL_UNUSED(base_element); + _SOKOL_UNUSED(num_elements); + _SOKOL_UNUSED(num_instances); +} + +_SOKOL_PRIVATE void _sg_dummy_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + _SOKOL_UNUSED(data); + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } +} + +_SOKOL_PRIVATE bool _sg_dummy_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + _SOKOL_UNUSED(data); + if (new_frame) { + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + } + return true; +} + +_SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + _SOKOL_UNUSED(data); + if (++img->cmn.active_slot >= img->cmn.num_slots) { + img->cmn.active_slot = 0; + } +} + +// ██████ ██████ ███████ ███ ██ ██████ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ ██████ █████ ██ ██ ██ ██ ███ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ███████ ██ ████ ██████ ███████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>opengl backend +#elif defined(_SOKOL_ANY_GL) + +// optional GL loader for win32 +#if defined(_SOKOL_USE_WIN32_GL_LOADER) + +#ifndef SG_GL_FUNCS_EXT +#define SG_GL_FUNCS_EXT +#endif + +// X Macro list of GL function names and signatures +#define _SG_GL_FUNCS \ + SG_GL_FUNCS_EXT \ + _SG_XMACRO(glBindVertexArray, void, (GLuint array)) \ + _SG_XMACRO(glFramebufferTextureLayer, void, (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer)) \ + _SG_XMACRO(glGenFramebuffers, void, (GLsizei n, GLuint * framebuffers)) \ + _SG_XMACRO(glBindFramebuffer, void, (GLenum target, GLuint framebuffer)) \ + _SG_XMACRO(glBindRenderbuffer, void, (GLenum target, GLuint renderbuffer)) \ + _SG_XMACRO(glGetStringi, const GLubyte *, (GLenum name, GLuint index)) \ + _SG_XMACRO(glClearBufferfi, void, (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil)) \ + _SG_XMACRO(glClearBufferfv, void, (GLenum buffer, GLint drawbuffer, const GLfloat * value)) \ + _SG_XMACRO(glClearBufferuiv, void, (GLenum buffer, GLint drawbuffer, const GLuint * value)) \ + _SG_XMACRO(glClearBufferiv, void, (GLenum buffer, GLint drawbuffer, const GLint * value)) \ + _SG_XMACRO(glDeleteRenderbuffers, void, (GLsizei n, const GLuint * renderbuffers)) \ + _SG_XMACRO(glUniform1fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SG_XMACRO(glUniform2fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SG_XMACRO(glUniform3fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SG_XMACRO(glUniform4fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SG_XMACRO(glUniform1iv, void, (GLint location, GLsizei count, const GLint * value)) \ + _SG_XMACRO(glUniform2iv, void, (GLint location, GLsizei count, const GLint * value)) \ + _SG_XMACRO(glUniform3iv, void, (GLint location, GLsizei count, const GLint * value)) \ + _SG_XMACRO(glUniform4iv, void, (GLint location, GLsizei count, const GLint * value)) \ + _SG_XMACRO(glUniformMatrix4fv, void, (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)) \ + _SG_XMACRO(glUseProgram, void, (GLuint program)) \ + _SG_XMACRO(glShaderSource, void, (GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length)) \ + _SG_XMACRO(glLinkProgram, void, (GLuint program)) \ + _SG_XMACRO(glGetUniformLocation, GLint, (GLuint program, const GLchar * name)) \ + _SG_XMACRO(glGetShaderiv, void, (GLuint shader, GLenum pname, GLint * params)) \ + _SG_XMACRO(glGetProgramInfoLog, void, (GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \ + _SG_XMACRO(glGetAttribLocation, GLint, (GLuint program, const GLchar * name)) \ + _SG_XMACRO(glDisableVertexAttribArray, void, (GLuint index)) \ + _SG_XMACRO(glDeleteShader, void, (GLuint shader)) \ + _SG_XMACRO(glDeleteProgram, void, (GLuint program)) \ + _SG_XMACRO(glCompileShader, void, (GLuint shader)) \ + _SG_XMACRO(glStencilFuncSeparate, void, (GLenum face, GLenum func, GLint ref, GLuint mask)) \ + _SG_XMACRO(glStencilOpSeparate, void, (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)) \ + _SG_XMACRO(glRenderbufferStorageMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)) \ + _SG_XMACRO(glDrawBuffers, void, (GLsizei n, const GLenum * bufs)) \ + _SG_XMACRO(glVertexAttribDivisor, void, (GLuint index, GLuint divisor)) \ + _SG_XMACRO(glBufferSubData, void, (GLenum target, GLintptr offset, GLsizeiptr size, const void * data)) \ + _SG_XMACRO(glGenBuffers, void, (GLsizei n, GLuint * buffers)) \ + _SG_XMACRO(glCheckFramebufferStatus, GLenum, (GLenum target)) \ + _SG_XMACRO(glFramebufferRenderbuffer, void, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)) \ + _SG_XMACRO(glCompressedTexImage2D, void, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data)) \ + _SG_XMACRO(glCompressedTexImage3D, void, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data)) \ + _SG_XMACRO(glActiveTexture, void, (GLenum texture)) \ + _SG_XMACRO(glTexSubImage3D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels)) \ + _SG_XMACRO(glRenderbufferStorage, void, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height)) \ + _SG_XMACRO(glGenTextures, void, (GLsizei n, GLuint * textures)) \ + _SG_XMACRO(glPolygonOffset, void, (GLfloat factor, GLfloat units)) \ + _SG_XMACRO(glDrawElements, void, (GLenum mode, GLsizei count, GLenum type, const void * indices)) \ + _SG_XMACRO(glDeleteFramebuffers, void, (GLsizei n, const GLuint * framebuffers)) \ + _SG_XMACRO(glBlendEquationSeparate, void, (GLenum modeRGB, GLenum modeAlpha)) \ + _SG_XMACRO(glDeleteTextures, void, (GLsizei n, const GLuint * textures)) \ + _SG_XMACRO(glGetProgramiv, void, (GLuint program, GLenum pname, GLint * params)) \ + _SG_XMACRO(glBindTexture, void, (GLenum target, GLuint texture)) \ + _SG_XMACRO(glTexImage3D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels)) \ + _SG_XMACRO(glCreateShader, GLuint, (GLenum type)) \ + _SG_XMACRO(glTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels)) \ + _SG_XMACRO(glFramebufferTexture2D, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) \ + _SG_XMACRO(glCreateProgram, GLuint, (void)) \ + _SG_XMACRO(glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ + _SG_XMACRO(glDeleteBuffers, void, (GLsizei n, const GLuint * buffers)) \ + _SG_XMACRO(glDrawArrays, void, (GLenum mode, GLint first, GLsizei count)) \ + _SG_XMACRO(glDrawElementsInstanced, void, (GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount)) \ + _SG_XMACRO(glVertexAttribPointer, void, (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer)) \ + _SG_XMACRO(glUniform1i, void, (GLint location, GLint v0)) \ + _SG_XMACRO(glDisable, void, (GLenum cap)) \ + _SG_XMACRO(glColorMask, void, (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) \ + _SG_XMACRO(glColorMaski, void, (GLuint buf, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) \ + _SG_XMACRO(glBindBuffer, void, (GLenum target, GLuint buffer)) \ + _SG_XMACRO(glDeleteVertexArrays, void, (GLsizei n, const GLuint * arrays)) \ + _SG_XMACRO(glDepthMask, void, (GLboolean flag)) \ + _SG_XMACRO(glDrawArraysInstanced, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount)) \ + _SG_XMACRO(glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ + _SG_XMACRO(glGenRenderbuffers, void, (GLsizei n, GLuint * renderbuffers)) \ + _SG_XMACRO(glBufferData, void, (GLenum target, GLsizeiptr size, const void * data, GLenum usage)) \ + _SG_XMACRO(glBlendFuncSeparate, void, (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha)) \ + _SG_XMACRO(glTexParameteri, void, (GLenum target, GLenum pname, GLint param)) \ + _SG_XMACRO(glGetIntegerv, void, (GLenum pname, GLint * data)) \ + _SG_XMACRO(glEnable, void, (GLenum cap)) \ + _SG_XMACRO(glBlitFramebuffer, void, (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)) \ + _SG_XMACRO(glStencilMask, void, (GLuint mask)) \ + _SG_XMACRO(glAttachShader, void, (GLuint program, GLuint shader)) \ + _SG_XMACRO(glGetError, GLenum, (void)) \ + _SG_XMACRO(glBlendColor, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) \ + _SG_XMACRO(glTexParameterf, void, (GLenum target, GLenum pname, GLfloat param)) \ + _SG_XMACRO(glTexParameterfv, void, (GLenum target, GLenum pname, const GLfloat* params)) \ + _SG_XMACRO(glGetShaderInfoLog, void, (GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \ + _SG_XMACRO(glDepthFunc, void, (GLenum func)) \ + _SG_XMACRO(glStencilOp , void, (GLenum fail, GLenum zfail, GLenum zpass)) \ + _SG_XMACRO(glStencilFunc, void, (GLenum func, GLint ref, GLuint mask)) \ + _SG_XMACRO(glEnableVertexAttribArray, void, (GLuint index)) \ + _SG_XMACRO(glBlendFunc, void, (GLenum sfactor, GLenum dfactor)) \ + _SG_XMACRO(glReadBuffer, void, (GLenum src)) \ + _SG_XMACRO(glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels)) \ + _SG_XMACRO(glGenVertexArrays, void, (GLsizei n, GLuint * arrays)) \ + _SG_XMACRO(glFrontFace, void, (GLenum mode)) \ + _SG_XMACRO(glCullFace, void, (GLenum mode)) \ + _SG_XMACRO(glPixelStorei, void, (GLenum pname, GLint param)) \ + _SG_XMACRO(glBindSampler, void, (GLuint unit, GLuint sampler)) \ + _SG_XMACRO(glGenSamplers, void, (GLsizei n, GLuint* samplers)) \ + _SG_XMACRO(glSamplerParameteri, void, (GLuint sampler, GLenum pname, GLint param)) \ + _SG_XMACRO(glSamplerParameterf, void, (GLuint sampler, GLenum pname, GLfloat param)) \ + _SG_XMACRO(glSamplerParameterfv, void, (GLuint sampler, GLenum pname, const GLfloat* params)) \ + _SG_XMACRO(glDeleteSamplers, void, (GLsizei n, const GLuint* samplers)) \ + _SG_XMACRO(glBindBufferBase, void, (GLenum target, GLuint index, GLuint buffer)) + +// generate GL function pointer typedefs +#define _SG_XMACRO(name, ret, args) typedef ret (GL_APIENTRY* PFN_ ## name) args; +_SG_GL_FUNCS +#undef _SG_XMACRO + +// generate GL function pointers +#define _SG_XMACRO(name, ret, args) static PFN_ ## name name; +_SG_GL_FUNCS +#undef _SG_XMACRO + +// helper function to lookup GL functions in GL DLL +typedef PROC (WINAPI * _sg_wglGetProcAddress)(LPCSTR); +_SOKOL_PRIVATE void* _sg_gl_getprocaddr(const char* name, _sg_wglGetProcAddress wgl_getprocaddress) { + void* proc_addr = (void*) wgl_getprocaddress(name); + if (0 == proc_addr) { + proc_addr = (void*) GetProcAddress(_sg.gl.opengl32_dll, name); + } + SOKOL_ASSERT(proc_addr); + return proc_addr; +} + +// populate GL function pointers +_SOKOL_PRIVATE void _sg_gl_load_opengl(void) { + SOKOL_ASSERT(0 == _sg.gl.opengl32_dll); + _sg.gl.opengl32_dll = LoadLibraryA("opengl32.dll"); + SOKOL_ASSERT(_sg.gl.opengl32_dll); + _sg_wglGetProcAddress wgl_getprocaddress = (_sg_wglGetProcAddress) GetProcAddress(_sg.gl.opengl32_dll, "wglGetProcAddress"); + SOKOL_ASSERT(wgl_getprocaddress); + #define _SG_XMACRO(name, ret, args) name = (PFN_ ## name) _sg_gl_getprocaddr(#name, wgl_getprocaddress); + _SG_GL_FUNCS + #undef _SG_XMACRO +} + +_SOKOL_PRIVATE void _sg_gl_unload_opengl(void) { + SOKOL_ASSERT(_sg.gl.opengl32_dll); + FreeLibrary(_sg.gl.opengl32_dll); + _sg.gl.opengl32_dll = 0; +} +#endif // _SOKOL_USE_WIN32_GL_LOADER + +//-- type translation ---------------------------------------------------------- +_SOKOL_PRIVATE GLenum _sg_gl_buffer_target(sg_buffer_type t) { + switch (t) { + case SG_BUFFERTYPE_VERTEXBUFFER: return GL_ARRAY_BUFFER; + case SG_BUFFERTYPE_INDEXBUFFER: return GL_ELEMENT_ARRAY_BUFFER; + case SG_BUFFERTYPE_STORAGEBUFFER: return GL_SHADER_STORAGE_BUFFER; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_texture_target(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return GL_TEXTURE_2D; + case SG_IMAGETYPE_CUBE: return GL_TEXTURE_CUBE_MAP; + case SG_IMAGETYPE_3D: return GL_TEXTURE_3D; + case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_ARRAY; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_usage(sg_usage u) { + switch (u) { + case SG_USAGE_IMMUTABLE: return GL_STATIC_DRAW; + case SG_USAGE_DYNAMIC: return GL_DYNAMIC_DRAW; + case SG_USAGE_STREAM: return GL_STREAM_DRAW; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_shader_stage(sg_shader_stage stage) { + switch (stage) { + case SG_SHADERSTAGE_VS: return GL_VERTEX_SHADER; + case SG_SHADERSTAGE_FS: return GL_FRAGMENT_SHADER; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLint _sg_gl_vertexformat_size(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return 1; + case SG_VERTEXFORMAT_FLOAT2: return 2; + case SG_VERTEXFORMAT_FLOAT3: return 3; + case SG_VERTEXFORMAT_FLOAT4: return 4; + case SG_VERTEXFORMAT_BYTE4: return 4; + case SG_VERTEXFORMAT_BYTE4N: return 4; + case SG_VERTEXFORMAT_UBYTE4: return 4; + case SG_VERTEXFORMAT_UBYTE4N: return 4; + case SG_VERTEXFORMAT_SHORT2: return 2; + case SG_VERTEXFORMAT_SHORT2N: return 2; + case SG_VERTEXFORMAT_USHORT2N: return 2; + case SG_VERTEXFORMAT_SHORT4: return 4; + case SG_VERTEXFORMAT_SHORT4N: return 4; + case SG_VERTEXFORMAT_USHORT4N: return 4; + case SG_VERTEXFORMAT_UINT10_N2: return 4; + case SG_VERTEXFORMAT_HALF2: return 2; + case SG_VERTEXFORMAT_HALF4: return 4; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_vertexformat_type(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: + case SG_VERTEXFORMAT_FLOAT2: + case SG_VERTEXFORMAT_FLOAT3: + case SG_VERTEXFORMAT_FLOAT4: + return GL_FLOAT; + case SG_VERTEXFORMAT_BYTE4: + case SG_VERTEXFORMAT_BYTE4N: + return GL_BYTE; + case SG_VERTEXFORMAT_UBYTE4: + case SG_VERTEXFORMAT_UBYTE4N: + return GL_UNSIGNED_BYTE; + case SG_VERTEXFORMAT_SHORT2: + case SG_VERTEXFORMAT_SHORT2N: + case SG_VERTEXFORMAT_SHORT4: + case SG_VERTEXFORMAT_SHORT4N: + return GL_SHORT; + case SG_VERTEXFORMAT_USHORT2N: + case SG_VERTEXFORMAT_USHORT4N: + return GL_UNSIGNED_SHORT; + case SG_VERTEXFORMAT_UINT10_N2: + return GL_UNSIGNED_INT_2_10_10_10_REV; + case SG_VERTEXFORMAT_HALF2: + case SG_VERTEXFORMAT_HALF4: + return GL_HALF_FLOAT; + default: + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLboolean _sg_gl_vertexformat_normalized(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_BYTE4N: + case SG_VERTEXFORMAT_UBYTE4N: + case SG_VERTEXFORMAT_SHORT2N: + case SG_VERTEXFORMAT_USHORT2N: + case SG_VERTEXFORMAT_SHORT4N: + case SG_VERTEXFORMAT_USHORT4N: + case SG_VERTEXFORMAT_UINT10_N2: + return GL_TRUE; + default: + return GL_FALSE; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_primitive_type(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return GL_POINTS; + case SG_PRIMITIVETYPE_LINES: return GL_LINES; + case SG_PRIMITIVETYPE_LINE_STRIP: return GL_LINE_STRIP; + case SG_PRIMITIVETYPE_TRIANGLES: return GL_TRIANGLES; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_index_type(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_NONE: return 0; + case SG_INDEXTYPE_UINT16: return GL_UNSIGNED_SHORT; + case SG_INDEXTYPE_UINT32: return GL_UNSIGNED_INT; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_compare_func(sg_compare_func cmp) { + switch (cmp) { + case SG_COMPAREFUNC_NEVER: return GL_NEVER; + case SG_COMPAREFUNC_LESS: return GL_LESS; + case SG_COMPAREFUNC_EQUAL: return GL_EQUAL; + case SG_COMPAREFUNC_LESS_EQUAL: return GL_LEQUAL; + case SG_COMPAREFUNC_GREATER: return GL_GREATER; + case SG_COMPAREFUNC_NOT_EQUAL: return GL_NOTEQUAL; + case SG_COMPAREFUNC_GREATER_EQUAL: return GL_GEQUAL; + case SG_COMPAREFUNC_ALWAYS: return GL_ALWAYS; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return GL_KEEP; + case SG_STENCILOP_ZERO: return GL_ZERO; + case SG_STENCILOP_REPLACE: return GL_REPLACE; + case SG_STENCILOP_INCR_CLAMP: return GL_INCR; + case SG_STENCILOP_DECR_CLAMP: return GL_DECR; + case SG_STENCILOP_INVERT: return GL_INVERT; + case SG_STENCILOP_INCR_WRAP: return GL_INCR_WRAP; + case SG_STENCILOP_DECR_WRAP: return GL_DECR_WRAP; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return GL_ZERO; + case SG_BLENDFACTOR_ONE: return GL_ONE; + case SG_BLENDFACTOR_SRC_COLOR: return GL_SRC_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR; + case SG_BLENDFACTOR_SRC_ALPHA: return GL_SRC_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA; + case SG_BLENDFACTOR_DST_COLOR: return GL_DST_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return GL_ONE_MINUS_DST_COLOR; + case SG_BLENDFACTOR_DST_ALPHA: return GL_DST_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return GL_SRC_ALPHA_SATURATE; + case SG_BLENDFACTOR_BLEND_COLOR: return GL_CONSTANT_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return GL_ONE_MINUS_CONSTANT_COLOR; + case SG_BLENDFACTOR_BLEND_ALPHA: return GL_CONSTANT_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return GL_ONE_MINUS_CONSTANT_ALPHA; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return GL_FUNC_ADD; + case SG_BLENDOP_SUBTRACT: return GL_FUNC_SUBTRACT; + case SG_BLENDOP_REVERSE_SUBTRACT: return GL_FUNC_REVERSE_SUBTRACT; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_min_filter(sg_filter min_f, sg_filter mipmap_f) { + if (min_f == SG_FILTER_NEAREST) { + switch (mipmap_f) { + case SG_FILTER_NONE: return GL_NEAREST; + case SG_FILTER_NEAREST: return GL_NEAREST_MIPMAP_NEAREST; + case SG_FILTER_LINEAR: return GL_NEAREST_MIPMAP_LINEAR; + default: SOKOL_UNREACHABLE; return (GLenum)0; + } + } else if (min_f == SG_FILTER_LINEAR) { + switch (mipmap_f) { + case SG_FILTER_NONE: return GL_LINEAR; + case SG_FILTER_NEAREST: return GL_LINEAR_MIPMAP_NEAREST; + case SG_FILTER_LINEAR: return GL_LINEAR_MIPMAP_LINEAR; + default: SOKOL_UNREACHABLE; return (GLenum)0; + } + } else { + SOKOL_UNREACHABLE; return (GLenum)0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_mag_filter(sg_filter mag_f) { + if (mag_f == SG_FILTER_NEAREST) { + return GL_NEAREST; + } else { + return GL_LINEAR; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_wrap(sg_wrap w) { + switch (w) { + case SG_WRAP_CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE; + #if defined(SOKOL_GLCORE) + case SG_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_BORDER; + #else + case SG_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_EDGE; + #endif + case SG_WRAP_REPEAT: return GL_REPEAT; + case SG_WRAP_MIRRORED_REPEAT: return GL_MIRRORED_REPEAT; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_teximage_type(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: + case SG_PIXELFORMAT_R8UI: + case SG_PIXELFORMAT_RG8: + case SG_PIXELFORMAT_RG8UI: + case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_SRGB8A8: + case SG_PIXELFORMAT_RGBA8UI: + case SG_PIXELFORMAT_BGRA8: + return GL_UNSIGNED_BYTE; + case SG_PIXELFORMAT_R8SN: + case SG_PIXELFORMAT_R8SI: + case SG_PIXELFORMAT_RG8SN: + case SG_PIXELFORMAT_RG8SI: + case SG_PIXELFORMAT_RGBA8SN: + case SG_PIXELFORMAT_RGBA8SI: + return GL_BYTE; + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16UI: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16UI: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16UI: + return GL_UNSIGNED_SHORT; + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_R16SI: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RG16SI: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_RGBA16SI: + return GL_SHORT; + case SG_PIXELFORMAT_R16F: + case SG_PIXELFORMAT_RG16F: + case SG_PIXELFORMAT_RGBA16F: + return GL_HALF_FLOAT; + case SG_PIXELFORMAT_R32UI: + case SG_PIXELFORMAT_RG32UI: + case SG_PIXELFORMAT_RGBA32UI: + return GL_UNSIGNED_INT; + case SG_PIXELFORMAT_R32SI: + case SG_PIXELFORMAT_RG32SI: + case SG_PIXELFORMAT_RGBA32SI: + return GL_INT; + case SG_PIXELFORMAT_R32F: + case SG_PIXELFORMAT_RG32F: + case SG_PIXELFORMAT_RGBA32F: + return GL_FLOAT; + case SG_PIXELFORMAT_RGB10A2: + return GL_UNSIGNED_INT_2_10_10_10_REV; + case SG_PIXELFORMAT_RG11B10F: + return GL_UNSIGNED_INT_10F_11F_11F_REV; + case SG_PIXELFORMAT_RGB9E5: + return GL_UNSIGNED_INT_5_9_9_9_REV; + case SG_PIXELFORMAT_DEPTH: + return GL_FLOAT; + case SG_PIXELFORMAT_DEPTH_STENCIL: + return GL_UNSIGNED_INT_24_8; + default: + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: + case SG_PIXELFORMAT_R8SN: + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_R16F: + case SG_PIXELFORMAT_R32F: + return GL_RED; + case SG_PIXELFORMAT_R8UI: + case SG_PIXELFORMAT_R8SI: + case SG_PIXELFORMAT_R16UI: + case SG_PIXELFORMAT_R16SI: + case SG_PIXELFORMAT_R32UI: + case SG_PIXELFORMAT_R32SI: + return GL_RED_INTEGER; + case SG_PIXELFORMAT_RG8: + case SG_PIXELFORMAT_RG8SN: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RG16F: + case SG_PIXELFORMAT_RG32F: + return GL_RG; + case SG_PIXELFORMAT_RG8UI: + case SG_PIXELFORMAT_RG8SI: + case SG_PIXELFORMAT_RG16UI: + case SG_PIXELFORMAT_RG16SI: + case SG_PIXELFORMAT_RG32UI: + case SG_PIXELFORMAT_RG32SI: + return GL_RG_INTEGER; + case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_SRGB8A8: + case SG_PIXELFORMAT_RGBA8SN: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_RGBA16F: + case SG_PIXELFORMAT_RGBA32F: + case SG_PIXELFORMAT_RGB10A2: + return GL_RGBA; + case SG_PIXELFORMAT_RGBA8UI: + case SG_PIXELFORMAT_RGBA8SI: + case SG_PIXELFORMAT_RGBA16UI: + case SG_PIXELFORMAT_RGBA16SI: + case SG_PIXELFORMAT_RGBA32UI: + case SG_PIXELFORMAT_RGBA32SI: + return GL_RGBA_INTEGER; + case SG_PIXELFORMAT_RG11B10F: + case SG_PIXELFORMAT_RGB9E5: + return GL_RGB; + case SG_PIXELFORMAT_DEPTH: + return GL_DEPTH_COMPONENT; + case SG_PIXELFORMAT_DEPTH_STENCIL: + return GL_DEPTH_STENCIL; + case SG_PIXELFORMAT_BC1_RGBA: + return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case SG_PIXELFORMAT_BC2_RGBA: + return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case SG_PIXELFORMAT_BC3_RGBA: + return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC3_SRGBA: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC4_R: + return GL_COMPRESSED_RED_RGTC1; + case SG_PIXELFORMAT_BC4_RSN: + return GL_COMPRESSED_SIGNED_RED_RGTC1; + case SG_PIXELFORMAT_BC5_RG: + return GL_COMPRESSED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC5_RGSN: + return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC6H_RGBF: + return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC6H_RGBUF: + return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC7_RGBA: + return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_BC7_SRGBA: + return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_ETC2_RGB8: + return GL_COMPRESSED_RGB8_ETC2; + case SG_PIXELFORMAT_ETC2_SRGB8: + return GL_COMPRESSED_SRGB8_ETC2; + case SG_PIXELFORMAT_ETC2_RGB8A1: + return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + case SG_PIXELFORMAT_ETC2_RGBA8: + return GL_COMPRESSED_RGBA8_ETC2_EAC; + case SG_PIXELFORMAT_ETC2_SRGB8A8: + return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; + case SG_PIXELFORMAT_EAC_R11: + return GL_COMPRESSED_R11_EAC; + case SG_PIXELFORMAT_EAC_R11SN: + return GL_COMPRESSED_SIGNED_R11_EAC; + case SG_PIXELFORMAT_EAC_RG11: + return GL_COMPRESSED_RG11_EAC; + case SG_PIXELFORMAT_EAC_RG11SN: + return GL_COMPRESSED_SIGNED_RG11_EAC; + case SG_PIXELFORMAT_ASTC_4x4_RGBA: + return GL_COMPRESSED_RGBA_ASTC_4x4_KHR; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: + return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR; + default: + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_teximage_internal_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: return GL_R8; + case SG_PIXELFORMAT_R8SN: return GL_R8_SNORM; + case SG_PIXELFORMAT_R8UI: return GL_R8UI; + case SG_PIXELFORMAT_R8SI: return GL_R8I; + #if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_R16: return GL_R16; + case SG_PIXELFORMAT_R16SN: return GL_R16_SNORM; + #endif + case SG_PIXELFORMAT_R16UI: return GL_R16UI; + case SG_PIXELFORMAT_R16SI: return GL_R16I; + case SG_PIXELFORMAT_R16F: return GL_R16F; + case SG_PIXELFORMAT_RG8: return GL_RG8; + case SG_PIXELFORMAT_RG8SN: return GL_RG8_SNORM; + case SG_PIXELFORMAT_RG8UI: return GL_RG8UI; + case SG_PIXELFORMAT_RG8SI: return GL_RG8I; + case SG_PIXELFORMAT_R32UI: return GL_R32UI; + case SG_PIXELFORMAT_R32SI: return GL_R32I; + case SG_PIXELFORMAT_R32F: return GL_R32F; + #if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_RG16: return GL_RG16; + case SG_PIXELFORMAT_RG16SN: return GL_RG16_SNORM; + #endif + case SG_PIXELFORMAT_RG16UI: return GL_RG16UI; + case SG_PIXELFORMAT_RG16SI: return GL_RG16I; + case SG_PIXELFORMAT_RG16F: return GL_RG16F; + case SG_PIXELFORMAT_RGBA8: return GL_RGBA8; + case SG_PIXELFORMAT_SRGB8A8: return GL_SRGB8_ALPHA8; + case SG_PIXELFORMAT_RGBA8SN: return GL_RGBA8_SNORM; + case SG_PIXELFORMAT_RGBA8UI: return GL_RGBA8UI; + case SG_PIXELFORMAT_RGBA8SI: return GL_RGBA8I; + case SG_PIXELFORMAT_RGB10A2: return GL_RGB10_A2; + case SG_PIXELFORMAT_RG11B10F: return GL_R11F_G11F_B10F; + case SG_PIXELFORMAT_RGB9E5: return GL_RGB9_E5; + case SG_PIXELFORMAT_RG32UI: return GL_RG32UI; + case SG_PIXELFORMAT_RG32SI: return GL_RG32I; + case SG_PIXELFORMAT_RG32F: return GL_RG32F; + #if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_RGBA16: return GL_RGBA16; + case SG_PIXELFORMAT_RGBA16SN: return GL_RGBA16_SNORM; + #endif + case SG_PIXELFORMAT_RGBA16UI: return GL_RGBA16UI; + case SG_PIXELFORMAT_RGBA16SI: return GL_RGBA16I; + case SG_PIXELFORMAT_RGBA16F: return GL_RGBA16F; + case SG_PIXELFORMAT_RGBA32UI: return GL_RGBA32UI; + case SG_PIXELFORMAT_RGBA32SI: return GL_RGBA32I; + case SG_PIXELFORMAT_RGBA32F: return GL_RGBA32F; + case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT32F; + case SG_PIXELFORMAT_DEPTH_STENCIL: return GL_DEPTH24_STENCIL8; + case SG_PIXELFORMAT_BC1_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case SG_PIXELFORMAT_BC2_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case SG_PIXELFORMAT_BC3_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC3_SRGBA: return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC4_R: return GL_COMPRESSED_RED_RGTC1; + case SG_PIXELFORMAT_BC4_RSN: return GL_COMPRESSED_SIGNED_RED_RGTC1; + case SG_PIXELFORMAT_BC5_RG: return GL_COMPRESSED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC5_RGSN: return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC6H_RGBF: return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC6H_RGBUF: return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC7_RGBA: return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_BC7_SRGBA: return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_ETC2_RGB8: return GL_COMPRESSED_RGB8_ETC2; + case SG_PIXELFORMAT_ETC2_SRGB8: return GL_COMPRESSED_SRGB8_ETC2; + case SG_PIXELFORMAT_ETC2_RGB8A1: return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + case SG_PIXELFORMAT_ETC2_RGBA8: return GL_COMPRESSED_RGBA8_ETC2_EAC; + case SG_PIXELFORMAT_ETC2_SRGB8A8: return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; + case SG_PIXELFORMAT_EAC_R11: return GL_COMPRESSED_R11_EAC; + case SG_PIXELFORMAT_EAC_R11SN: return GL_COMPRESSED_SIGNED_R11_EAC; + case SG_PIXELFORMAT_EAC_RG11: return GL_COMPRESSED_RG11_EAC; + case SG_PIXELFORMAT_EAC_RG11SN: return GL_COMPRESSED_SIGNED_RG11_EAC; + case SG_PIXELFORMAT_ASTC_4x4_RGBA: return GL_COMPRESSED_RGBA_ASTC_4x4_KHR; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_cubeface_target(int face_index) { + switch (face_index) { + case 0: return GL_TEXTURE_CUBE_MAP_POSITIVE_X; + case 1: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; + case 2: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; + case 3: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; + case 4: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; + case 5: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; + default: SOKOL_UNREACHABLE; return 0; + } +} + +// see: https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml +_SOKOL_PRIVATE void _sg_gl_init_pixelformats(bool has_bgra) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); + #if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); + #if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_SRGB8A8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + if (has_bgra) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); + } + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGB9E5]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + #if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); +} + +// FIXME: OES_half_float_blend +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_half_float(bool has_colorbuffer_half_float) { + if (has_colorbuffer_half_float) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } else { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_float(bool has_colorbuffer_float, bool has_texture_float_linear, bool has_float_blend) { + if (has_texture_float_linear) { + if (has_colorbuffer_float) { + if (has_float_blend) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } else { + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + } else { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + } else { + if (has_colorbuffer_float) { + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } else { + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + } +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_s3tc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_SRGBA]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_rgtc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_bptc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_SRGBA]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_pvrtc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_2BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_4BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_2BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_4BPP]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_etc2(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_astc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_SRGBA]); + } + +_SOKOL_PRIVATE void _sg_gl_init_limits(void) { + _SG_GL_CHECK_ERROR(); + GLint gl_int; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_size_2d = gl_int; + _sg.limits.max_image_size_array = gl_int; + glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_size_cube = gl_int; + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &gl_int); + _SG_GL_CHECK_ERROR(); + if (gl_int > SG_MAX_VERTEX_ATTRIBUTES) { + gl_int = SG_MAX_VERTEX_ATTRIBUTES; + } + _sg.limits.max_vertex_attrs = gl_int; + glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.gl_max_vertex_uniform_components = gl_int; + glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_size_3d = gl_int; + glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_array_layers = gl_int; + if (_sg.gl.ext_anisotropic) { + glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.gl.max_anisotropy = gl_int; + } else { + _sg.gl.max_anisotropy = 1; + } + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.gl_max_combined_texture_image_units = gl_int; +} + +#if defined(SOKOL_GLCORE) +_SOKOL_PRIVATE void _sg_gl_init_caps_glcore(void) { + _sg.backend = SG_BACKEND_GLCORE; + + GLint major_version = 0; + GLint minor_version = 0; + glGetIntegerv(GL_MAJOR_VERSION, &major_version); + glGetIntegerv(GL_MINOR_VERSION, &minor_version); + const int version = major_version * 100 + minor_version * 10; + _sg.features.origin_top_left = false; + _sg.features.image_clamp_to_border = true; + _sg.features.mrt_independent_blend_state = false; + _sg.features.mrt_independent_write_mask = true; + _sg.features.storage_buffer = version >= 430; + + // scan extensions + bool has_s3tc = false; // BC1..BC3 + bool has_rgtc = false; // BC4 and BC5 + bool has_bptc = false; // BC6H and BC7 + bool has_pvrtc = false; + bool has_etc2 = false; + bool has_astc = false; + GLint num_ext = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_ext); + for (int i = 0; i < num_ext; i++) { + const char* ext = (const char*) glGetStringi(GL_EXTENSIONS, (GLuint)i); + if (ext) { + if (strstr(ext, "_texture_compression_s3tc")) { + has_s3tc = true; + } else if (strstr(ext, "_texture_compression_rgtc")) { + has_rgtc = true; + } else if (strstr(ext, "_texture_compression_bptc")) { + has_bptc = true; + } else if (strstr(ext, "_texture_compression_pvrtc")) { + has_pvrtc = true; + } else if (strstr(ext, "_ES3_compatibility")) { + has_etc2 = true; + } else if (strstr(ext, "_texture_filter_anisotropic")) { + _sg.gl.ext_anisotropic = true; + } else if (strstr(ext, "_texture_compression_astc_ldr")) { + has_astc = true; + } + } + } + + // limits + _sg_gl_init_limits(); + + // pixel formats + const bool has_bgra = false; // not a bug + const bool has_colorbuffer_float = true; + const bool has_colorbuffer_half_float = true; + const bool has_texture_float_linear = true; // FIXME??? + const bool has_float_blend = true; + _sg_gl_init_pixelformats(has_bgra); + _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); + _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float); + if (has_s3tc) { + _sg_gl_init_pixelformats_s3tc(); + } + if (has_rgtc) { + _sg_gl_init_pixelformats_rgtc(); + } + if (has_bptc) { + _sg_gl_init_pixelformats_bptc(); + } + if (has_pvrtc) { + _sg_gl_init_pixelformats_pvrtc(); + } + if (has_etc2) { + _sg_gl_init_pixelformats_etc2(); + } + if (has_astc) { + _sg_gl_init_pixelformats_astc(); + } +} +#endif + +#if defined(SOKOL_GLES3) +_SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { + _sg.backend = SG_BACKEND_GLES3; + + _sg.features.origin_top_left = false; + _sg.features.image_clamp_to_border = false; + _sg.features.mrt_independent_blend_state = false; + _sg.features.mrt_independent_write_mask = false; + _sg.features.storage_buffer = false; + + bool has_s3tc = false; // BC1..BC3 + bool has_rgtc = false; // BC4 and BC5 + bool has_bptc = false; // BC6H and BC7 + bool has_pvrtc = false; + #if defined(__EMSCRIPTEN__) + bool has_etc2 = false; + #else + bool has_etc2 = true; + #endif + bool has_astc = false; + bool has_colorbuffer_float = false; + bool has_colorbuffer_half_float = false; + bool has_texture_float_linear = false; + bool has_float_blend = false; + GLint num_ext = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_ext); + for (int i = 0; i < num_ext; i++) { + const char* ext = (const char*) glGetStringi(GL_EXTENSIONS, (GLuint)i); + if (ext) { + if (strstr(ext, "_texture_compression_s3tc")) { + has_s3tc = true; + } else if (strstr(ext, "_compressed_texture_s3tc")) { + has_s3tc = true; + } else if (strstr(ext, "_texture_compression_rgtc")) { + has_rgtc = true; + } else if (strstr(ext, "_texture_compression_bptc")) { + has_bptc = true; + } else if (strstr(ext, "_texture_compression_pvrtc")) { + has_pvrtc = true; + } else if (strstr(ext, "_compressed_texture_pvrtc")) { + has_pvrtc = true; + } else if (strstr(ext, "_compressed_texture_etc")) { + has_etc2 = true; + } else if (strstr(ext, "_compressed_texture_astc")) { + has_astc = true; + } else if (strstr(ext, "_color_buffer_float")) { + has_colorbuffer_float = true; + } else if (strstr(ext, "_color_buffer_half_float")) { + has_colorbuffer_half_float = true; + } else if (strstr(ext, "_texture_float_linear")) { + has_texture_float_linear = true; + } else if (strstr(ext, "_float_blend")) { + has_float_blend = true; + } else if (strstr(ext, "_texture_filter_anisotropic")) { + _sg.gl.ext_anisotropic = true; + } + } + } + + /* on WebGL2, color_buffer_float also includes 16-bit formats + see: https://developer.mozilla.org/en-US/docs/Web/API/EXT_color_buffer_float + */ + #if defined(__EMSCRIPTEN__) + if (!has_colorbuffer_half_float && has_colorbuffer_float) { + has_colorbuffer_half_float = has_colorbuffer_float; + } + #endif + + // limits + _sg_gl_init_limits(); + + // pixel formats + const bool has_bgra = false; // not a bug + _sg_gl_init_pixelformats(has_bgra); + _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); + _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float); + if (has_s3tc) { + _sg_gl_init_pixelformats_s3tc(); + } + if (has_rgtc) { + _sg_gl_init_pixelformats_rgtc(); + } + if (has_bptc) { + _sg_gl_init_pixelformats_bptc(); + } + if (has_pvrtc) { + _sg_gl_init_pixelformats_pvrtc(); + } + if (has_etc2) { + _sg_gl_init_pixelformats_etc2(); + } + if (has_astc) { + _sg_gl_init_pixelformats_astc(); + } +} +#endif + +//-- state cache implementation ------------------------------------------------ +_SOKOL_PRIVATE GLuint _sg_gl_storagebuffer_bind_index(int stage, int slot) { + SOKOL_ASSERT((stage >= 0) && (stage < SG_NUM_SHADER_STAGES)); + SOKOL_ASSERT((slot >= 0) && (slot < SG_MAX_SHADERSTAGE_STORAGEBUFFERS)); + return (GLuint) (stage * _SG_GL_STORAGEBUFFER_STAGE_INDEX_PITCH + slot); +} + +_SOKOL_PRIVATE void _sg_gl_cache_clear_buffer_bindings(bool force) { + if (force || (_sg.gl.cache.vertex_buffer != 0)) { + glBindBuffer(GL_ARRAY_BUFFER, 0); + _sg.gl.cache.vertex_buffer = 0; + _sg_stats_add(gl.num_bind_buffer, 1); + } + if (force || (_sg.gl.cache.index_buffer != 0)) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + _sg.gl.cache.index_buffer = 0; + _sg_stats_add(gl.num_bind_buffer, 1); + } + if (force || (_sg.gl.cache.storage_buffer != 0)) { + if (_sg.features.storage_buffer) { + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + } + _sg.gl.cache.storage_buffer = 0; + _sg_stats_add(gl.num_bind_buffer, 1); + } + for (int stage = 0; stage < SG_NUM_SHADER_STAGES; stage++) { + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { + if (force || (_sg.gl.cache.stage_storage_buffers[stage][i] != 0)) { + const GLuint bind_index = _sg_gl_storagebuffer_bind_index(stage, i); + if (_sg.features.storage_buffer) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, bind_index, 0); + } + _sg.gl.cache.stage_storage_buffers[stage][i] = 0; + _sg_stats_add(gl.num_bind_buffer, 1); + } + } + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_bind_buffer(GLenum target, GLuint buffer) { + SOKOL_ASSERT((GL_ARRAY_BUFFER == target) || (GL_ELEMENT_ARRAY_BUFFER == target) || (GL_SHADER_STORAGE_BUFFER == target)); + if (target == GL_ARRAY_BUFFER) { + if (_sg.gl.cache.vertex_buffer != buffer) { + _sg.gl.cache.vertex_buffer = buffer; + glBindBuffer(target, buffer); + _sg_stats_add(gl.num_bind_buffer, 1); + } + } else if (target == GL_ELEMENT_ARRAY_BUFFER) { + if (_sg.gl.cache.index_buffer != buffer) { + _sg.gl.cache.index_buffer = buffer; + glBindBuffer(target, buffer); + _sg_stats_add(gl.num_bind_buffer, 1); + } + } else if (target == GL_SHADER_STORAGE_BUFFER) { + if (_sg.gl.cache.storage_buffer != buffer) { + _sg.gl.cache.storage_buffer = buffer; + if (_sg.features.storage_buffer) { + glBindBuffer(target, buffer); + } + _sg_stats_add(gl.num_bind_buffer, 1); + } + } else { + SOKOL_UNREACHABLE; + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_bind_storage_buffer(int stage, int slot, GLuint buffer) { + SOKOL_ASSERT((stage >= 0) && (stage < SG_NUM_SHADER_STAGES)); + SOKOL_ASSERT((slot >= 0) && (slot < SG_MAX_SHADERSTAGE_STORAGEBUFFERS)); + if (_sg.gl.cache.stage_storage_buffers[stage][slot] != buffer) { + _sg.gl.cache.stage_storage_buffers[stage][slot] = buffer; + _sg.gl.cache.storage_buffer = buffer; // not a bug + GLuint bind_index = _sg_gl_storagebuffer_bind_index(stage, slot); + if (_sg.features.storage_buffer) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, bind_index, buffer); + } + _sg_stats_add(gl.num_bind_buffer, 1); + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_store_buffer_binding(GLenum target) { + if (target == GL_ARRAY_BUFFER) { + _sg.gl.cache.stored_vertex_buffer = _sg.gl.cache.vertex_buffer; + } else if (target == GL_ELEMENT_ARRAY_BUFFER) { + _sg.gl.cache.stored_index_buffer = _sg.gl.cache.index_buffer; + } else if (target == GL_SHADER_STORAGE_BUFFER) { + _sg.gl.cache.stored_storage_buffer = _sg.gl.cache.storage_buffer; + } else { + SOKOL_UNREACHABLE; + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_restore_buffer_binding(GLenum target) { + if (target == GL_ARRAY_BUFFER) { + if (_sg.gl.cache.stored_vertex_buffer != 0) { + // we only care about restoring valid ids + _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_vertex_buffer); + _sg.gl.cache.stored_vertex_buffer = 0; + } + } else if (target == GL_ELEMENT_ARRAY_BUFFER) { + if (_sg.gl.cache.stored_index_buffer != 0) { + // we only care about restoring valid ids + _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_index_buffer); + _sg.gl.cache.stored_index_buffer = 0; + } + } else if (target == GL_SHADER_STORAGE_BUFFER) { + if (_sg.gl.cache.stored_storage_buffer != 0) { + // we only care about restoring valid ids + _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_storage_buffer); + _sg.gl.cache.stored_storage_buffer = 0; + } + } else { + SOKOL_UNREACHABLE; + } +} + +// called when _sg_gl_discard_buffer() +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_buffer(GLuint buf) { + if (buf == _sg.gl.cache.vertex_buffer) { + _sg.gl.cache.vertex_buffer = 0; + glBindBuffer(GL_ARRAY_BUFFER, 0); + _sg_stats_add(gl.num_bind_buffer, 1); + } + if (buf == _sg.gl.cache.index_buffer) { + _sg.gl.cache.index_buffer = 0; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + _sg_stats_add(gl.num_bind_buffer, 1); + } + if (buf == _sg.gl.cache.storage_buffer) { + _sg.gl.cache.storage_buffer = 0; + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + _sg_stats_add(gl.num_bind_buffer, 1); + } + for (int stage = 0; stage < SG_NUM_SHADER_STAGES; stage++) { + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { + if (buf == _sg.gl.cache.stage_storage_buffers[stage][i]) { + _sg.gl.cache.stage_storage_buffers[stage][i] = 0; + _sg.gl.cache.storage_buffer = 0; // not a bug! + const GLuint bind_index = _sg_gl_storagebuffer_bind_index(stage, i); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, bind_index, 0); + _sg_stats_add(gl.num_bind_buffer, 1); + } + } + } + if (buf == _sg.gl.cache.stored_vertex_buffer) { + _sg.gl.cache.stored_vertex_buffer = 0; + } + if (buf == _sg.gl.cache.stored_index_buffer) { + _sg.gl.cache.stored_index_buffer = 0; + } + if (buf == _sg.gl.cache.stored_storage_buffer) { + _sg.gl.cache.stored_storage_buffer = 0; + } + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + if (buf == _sg.gl.cache.attrs[i].gl_vbuf) { + _sg.gl.cache.attrs[i].gl_vbuf = 0; + } + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_active_texture(GLenum texture) { + _SG_GL_CHECK_ERROR(); + if (_sg.gl.cache.cur_active_texture != texture) { + _sg.gl.cache.cur_active_texture = texture; + glActiveTexture(texture); + _sg_stats_add(gl.num_active_texture, 1); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_cache_clear_texture_sampler_bindings(bool force) { + _SG_GL_CHECK_ERROR(); + for (int i = 0; (i < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE) && (i < _sg.limits.gl_max_combined_texture_image_units); i++) { + if (force || (_sg.gl.cache.texture_samplers[i].texture != 0)) { + GLenum gl_texture_unit = (GLenum) (GL_TEXTURE0 + i); + glActiveTexture(gl_texture_unit); + _sg_stats_add(gl.num_active_texture, 1); + glBindTexture(GL_TEXTURE_2D, 0); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + glBindTexture(GL_TEXTURE_3D, 0); + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); + _sg_stats_add(gl.num_bind_texture, 4); + glBindSampler((GLuint)i, 0); + _sg_stats_add(gl.num_bind_sampler, 1); + _sg.gl.cache.texture_samplers[i].target = 0; + _sg.gl.cache.texture_samplers[i].texture = 0; + _sg.gl.cache.texture_samplers[i].sampler = 0; + _sg.gl.cache.cur_active_texture = gl_texture_unit; + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_cache_bind_texture_sampler(int slot_index, GLenum target, GLuint texture, GLuint sampler) { + /* it's valid to call this function with target=0 and/or texture=0 + target=0 will unbind the previous binding, texture=0 will clear + the new binding + */ + SOKOL_ASSERT((slot_index >= 0) && (slot_index < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE)); + if (slot_index >= _sg.limits.gl_max_combined_texture_image_units) { + return; + } + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_texture_sampler_bind_slot* slot = &_sg.gl.cache.texture_samplers[slot_index]; + if ((slot->target != target) || (slot->texture != texture) || (slot->sampler != sampler)) { + _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + slot_index)); + // if the target has changed, clear the previous binding on that target + if ((target != slot->target) && (slot->target != 0)) { + glBindTexture(slot->target, 0); + _SG_GL_CHECK_ERROR(); + _sg_stats_add(gl.num_bind_texture, 1); + } + // apply new binding (can be 0 to unbind) + if (target != 0) { + glBindTexture(target, texture); + _SG_GL_CHECK_ERROR(); + _sg_stats_add(gl.num_bind_texture, 1); + } + // apply new sampler (can be 0 to unbind) + glBindSampler((GLuint)slot_index, sampler); + _SG_GL_CHECK_ERROR(); + _sg_stats_add(gl.num_bind_sampler, 1); + + slot->target = target; + slot->texture = texture; + slot->sampler = sampler; + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_store_texture_sampler_binding(int slot_index) { + SOKOL_ASSERT((slot_index >= 0) && (slot_index < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE)); + _sg.gl.cache.stored_texture_sampler = _sg.gl.cache.texture_samplers[slot_index]; +} + +_SOKOL_PRIVATE void _sg_gl_cache_restore_texture_sampler_binding(int slot_index) { + SOKOL_ASSERT((slot_index >= 0) && (slot_index < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE)); + _sg_gl_cache_texture_sampler_bind_slot* slot = &_sg.gl.cache.stored_texture_sampler; + if (slot->texture != 0) { + // we only care about restoring valid ids + SOKOL_ASSERT(slot->target != 0); + _sg_gl_cache_bind_texture_sampler(slot_index, slot->target, slot->texture, slot->sampler); + slot->target = 0; + slot->texture = 0; + slot->sampler = 0; + } +} + +// called from _sg_gl_discard_texture() and _sg_gl_discard_sampler() +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_texture_sampler(GLuint tex, GLuint smp) { + _SG_GL_CHECK_ERROR(); + for (int i = 0; i < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE; i++) { + _sg_gl_cache_texture_sampler_bind_slot* slot = &_sg.gl.cache.texture_samplers[i]; + if ((0 != slot->target) && ((tex == slot->texture) || (smp == slot->sampler))) { + _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + i)); + glBindTexture(slot->target, 0); + _SG_GL_CHECK_ERROR(); + _sg_stats_add(gl.num_bind_texture, 1); + glBindSampler((GLuint)i, 0); + _SG_GL_CHECK_ERROR(); + _sg_stats_add(gl.num_bind_sampler, 1); + slot->target = 0; + slot->texture = 0; + slot->sampler = 0; + } + } + if ((tex == _sg.gl.cache.stored_texture_sampler.texture) || (smp == _sg.gl.cache.stored_texture_sampler.sampler)) { + _sg.gl.cache.stored_texture_sampler.target = 0; + _sg.gl.cache.stored_texture_sampler.texture = 0; + _sg.gl.cache.stored_texture_sampler.sampler = 0; + } +} + +// called from _sg_gl_discard_shader() +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_program(GLuint prog) { + if (prog == _sg.gl.cache.prog) { + _sg.gl.cache.prog = 0; + glUseProgram(0); + _sg_stats_add(gl.num_use_program, 1); + } +} + +// called from _sg_gl_discard_pipeline() +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_pipeline(_sg_pipeline_t* pip) { + if (pip == _sg.gl.cache.cur_pipeline) { + _sg.gl.cache.cur_pipeline = 0; + _sg.gl.cache.cur_pipeline_id.id = SG_INVALID_ID; + } +} + +_SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { + _SG_GL_CHECK_ERROR(); + glBindVertexArray(_sg.gl.vao); + _SG_GL_CHECK_ERROR(); + _sg_clear(&_sg.gl.cache, sizeof(_sg.gl.cache)); + _sg_gl_cache_clear_buffer_bindings(true); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_clear_texture_sampler_bindings(true); + _SG_GL_CHECK_ERROR(); + for (int i = 0; i < _sg.limits.max_vertex_attrs; i++) { + _sg_gl_attr_t* attr = &_sg.gl.cache.attrs[i].gl_attr; + attr->vb_index = -1; + attr->divisor = -1; + glDisableVertexAttribArray((GLuint)i); + _SG_GL_CHECK_ERROR(); + _sg_stats_add(gl.num_disable_vertex_attrib_array, 1); + } + _sg.gl.cache.cur_primitive_type = GL_TRIANGLES; + + // shader program + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&_sg.gl.cache.prog); + _SG_GL_CHECK_ERROR(); + + // depth and stencil state + _sg.gl.cache.depth.compare = SG_COMPAREFUNC_ALWAYS; + _sg.gl.cache.stencil.front.compare = SG_COMPAREFUNC_ALWAYS; + _sg.gl.cache.stencil.front.fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.front.depth_fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.front.pass_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.back.compare = SG_COMPAREFUNC_ALWAYS; + _sg.gl.cache.stencil.back.fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.back.depth_fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.back.pass_op = SG_STENCILOP_KEEP; + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_ALWAYS); + glDepthMask(GL_FALSE); + glDisable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 0, 0); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilMask(0); + _sg_stats_add(gl.num_render_state, 7); + + // blend state + _sg.gl.cache.blend.src_factor_rgb = SG_BLENDFACTOR_ONE; + _sg.gl.cache.blend.dst_factor_rgb = SG_BLENDFACTOR_ZERO; + _sg.gl.cache.blend.op_rgb = SG_BLENDOP_ADD; + _sg.gl.cache.blend.src_factor_alpha = SG_BLENDFACTOR_ONE; + _sg.gl.cache.blend.dst_factor_alpha = SG_BLENDFACTOR_ZERO; + _sg.gl.cache.blend.op_alpha = SG_BLENDOP_ADD; + glDisable(GL_BLEND); + glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO); + glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); + glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); + _sg_stats_add(gl.num_render_state, 4); + + // standalone state + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.gl.cache.color_write_mask[i] = SG_COLORMASK_RGBA; + } + _sg.gl.cache.cull_mode = SG_CULLMODE_NONE; + _sg.gl.cache.face_winding = SG_FACEWINDING_CW; + _sg.gl.cache.sample_count = 1; + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glPolygonOffset(0.0f, 0.0f); + glDisable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_CULL_FACE); + glFrontFace(GL_CW); + glCullFace(GL_BACK); + glEnable(GL_SCISSOR_TEST); + glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + glEnable(GL_DITHER); + glDisable(GL_POLYGON_OFFSET_FILL); + _sg_stats_add(gl.num_render_state, 10); + #if defined(SOKOL_GLCORE) + glEnable(GL_MULTISAMPLE); + glEnable(GL_PROGRAM_POINT_SIZE); + _sg_stats_add(gl.num_render_state, 2); + #endif +} + +_SOKOL_PRIVATE void _sg_gl_setup_backend(const sg_desc* desc) { + _SOKOL_UNUSED(desc); + + // assumes that _sg.gl is already zero-initialized + _sg.gl.valid = true; + + #if defined(_SOKOL_USE_WIN32_GL_LOADER) + _sg_gl_load_opengl(); + #endif + + // clear initial GL error state + #if defined(SOKOL_DEBUG) + while (glGetError() != GL_NO_ERROR); + #endif + #if defined(SOKOL_GLCORE) + _sg_gl_init_caps_glcore(); + #elif defined(SOKOL_GLES3) + _sg_gl_init_caps_gles3(); + #endif + + glGenVertexArrays(1, &_sg.gl.vao); + glBindVertexArray(_sg.gl.vao); + _SG_GL_CHECK_ERROR(); + // incoming texture data is generally expected to be packed tightly + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + #if defined(SOKOL_GLCORE) + // enable seamless cubemap sampling (only desktop GL) + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + #endif + _sg_gl_reset_state_cache(); +} + +_SOKOL_PRIVATE void _sg_gl_discard_backend(void) { + SOKOL_ASSERT(_sg.gl.valid); + if (_sg.gl.vao) { + glDeleteVertexArrays(1, &_sg.gl.vao); + } + #if defined(_SOKOL_USE_WIN32_GL_LOADER) + _sg_gl_unload_opengl(); + #endif + _sg.gl.valid = false; +} + +//-- GL backend resource creation and destruction ------------------------------ +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + _SG_GL_CHECK_ERROR(); + buf->gl.injected = (0 != desc->gl_buffers[0]); + const GLenum gl_target = _sg_gl_buffer_target(buf->cmn.type); + const GLenum gl_usage = _sg_gl_usage(buf->cmn.usage); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + GLuint gl_buf = 0; + if (buf->gl.injected) { + SOKOL_ASSERT(desc->gl_buffers[slot]); + gl_buf = desc->gl_buffers[slot]; + } else { + glGenBuffers(1, &gl_buf); + SOKOL_ASSERT(gl_buf); + _sg_gl_cache_store_buffer_binding(gl_target); + _sg_gl_cache_bind_buffer(gl_target, gl_buf); + glBufferData(gl_target, buf->cmn.size, 0, gl_usage); + if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { + SOKOL_ASSERT(desc->data.ptr); + glBufferSubData(gl_target, 0, buf->cmn.size, desc->data.ptr); + } + _sg_gl_cache_restore_buffer_binding(gl_target); + } + buf->gl.buf[slot] = gl_buf; + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_discard_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + _SG_GL_CHECK_ERROR(); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + if (buf->gl.buf[slot]) { + _sg_gl_cache_invalidate_buffer(buf->gl.buf[slot]); + if (!buf->gl.injected) { + glDeleteBuffers(1, &buf->gl.buf[slot]); + } + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE bool _sg_gl_supported_texture_format(sg_pixel_format fmt) { + const int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index > SG_PIXELFORMAT_NONE) && (fmt_index < _SG_PIXELFORMAT_NUM)); + return _sg.formats[fmt_index].sample; +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + _SG_GL_CHECK_ERROR(); + img->gl.injected = (0 != desc->gl_textures[0]); + + // check if texture format is support + if (!_sg_gl_supported_texture_format(img->cmn.pixel_format)) { + _SG_ERROR(GL_TEXTURE_FORMAT_NOT_SUPPORTED); + return SG_RESOURCESTATE_FAILED; + } + const GLenum gl_internal_format = _sg_gl_teximage_internal_format(img->cmn.pixel_format); + + // if this is a MSAA render target, a render buffer object will be created instead of a regulat texture + // (since GLES3 has no multisampled texture objects) + if (img->cmn.render_target && (img->cmn.sample_count > 1)) { + glGenRenderbuffers(1, &img->gl.msaa_render_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, img->gl.msaa_render_buffer); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->cmn.sample_count, gl_internal_format, img->cmn.width, img->cmn.height); + } else if (img->gl.injected) { + img->gl.target = _sg_gl_texture_target(img->cmn.type); + // inject externally GL textures + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + SOKOL_ASSERT(desc->gl_textures[slot]); + img->gl.tex[slot] = desc->gl_textures[slot]; + } + if (desc->gl_texture_target) { + img->gl.target = (GLenum)desc->gl_texture_target; + } + } else { + // create our own GL texture(s) + img->gl.target = _sg_gl_texture_target(img->cmn.type); + const GLenum gl_format = _sg_gl_teximage_format(img->cmn.pixel_format); + const bool is_compressed = _sg_is_compressed_pixel_format(img->cmn.pixel_format); + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + glGenTextures(1, &img->gl.tex[slot]); + SOKOL_ASSERT(img->gl.tex[slot]); + _sg_gl_cache_store_texture_sampler_binding(0); + _sg_gl_cache_bind_texture_sampler(0, img->gl.target, img->gl.tex[slot], 0); + glTexParameteri(img->gl.target, GL_TEXTURE_MAX_LEVEL, img->cmn.num_mipmaps - 1); + const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1; + int data_index = 0; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, data_index++) { + GLenum gl_img_target = img->gl.target; + if (SG_IMAGETYPE_CUBE == img->cmn.type) { + gl_img_target = _sg_gl_cubeface_target(face_index); + } + const GLvoid* data_ptr = desc->data.subimage[face_index][mip_index].ptr; + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { + if (is_compressed) { + const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; + glCompressedTexImage2D(gl_img_target, mip_index, gl_internal_format, + mip_width, mip_height, 0, data_size, data_ptr); + } else { + const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); + glTexImage2D(gl_img_target, mip_index, (GLint)gl_internal_format, + mip_width, mip_height, 0, gl_format, gl_type, data_ptr); + } + } else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) { + int mip_depth = img->cmn.num_slices; + if (SG_IMAGETYPE_3D == img->cmn.type) { + mip_depth = _sg_miplevel_dim(mip_depth, mip_index); + } + if (is_compressed) { + const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; + glCompressedTexImage3D(gl_img_target, mip_index, gl_internal_format, + mip_width, mip_height, mip_depth, 0, data_size, data_ptr); + } else { + const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); + glTexImage3D(gl_img_target, mip_index, (GLint)gl_internal_format, + mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr); + } + } + } + } + _sg_gl_cache_restore_texture_sampler_binding(0); + } + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_discard_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + _SG_GL_CHECK_ERROR(); + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + if (img->gl.tex[slot]) { + _sg_gl_cache_invalidate_texture_sampler(img->gl.tex[slot], 0); + if (!img->gl.injected) { + glDeleteTextures(1, &img->gl.tex[slot]); + } + } + } + if (img->gl.msaa_render_buffer) { + glDeleteRenderbuffers(1, &img->gl.msaa_render_buffer); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + _SG_GL_CHECK_ERROR(); + smp->gl.injected = (0 != desc->gl_sampler); + if (smp->gl.injected) { + smp->gl.smp = (GLuint) desc->gl_sampler; + } else { + glGenSamplers(1, &smp->gl.smp); + SOKOL_ASSERT(smp->gl.smp); + + const GLenum gl_min_filter = _sg_gl_min_filter(smp->cmn.min_filter, smp->cmn.mipmap_filter); + const GLenum gl_mag_filter = _sg_gl_mag_filter(smp->cmn.mag_filter); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_MIN_FILTER, (GLint)gl_min_filter); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_MAG_FILTER, (GLint)gl_mag_filter); + // GL spec has strange defaults for mipmap min/max lod: -1000 to +1000 + const float min_lod = _sg_clamp(desc->min_lod, 0.0f, 1000.0f); + const float max_lod = _sg_clamp(desc->max_lod, 0.0f, 1000.0f); + glSamplerParameterf(smp->gl.smp, GL_TEXTURE_MIN_LOD, min_lod); + glSamplerParameterf(smp->gl.smp, GL_TEXTURE_MAX_LOD, max_lod); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_S, (GLint)_sg_gl_wrap(smp->cmn.wrap_u)); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_T, (GLint)_sg_gl_wrap(smp->cmn.wrap_v)); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_R, (GLint)_sg_gl_wrap(smp->cmn.wrap_w)); + #if defined(SOKOL_GLCORE) + float border[4]; + switch (smp->cmn.border_color) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: + border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 0.0f; + break; + case SG_BORDERCOLOR_OPAQUE_WHITE: + border[0] = 1.0f; border[1] = 1.0f; border[2] = 1.0f; border[3] = 1.0f; + break; + default: + border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 1.0f; + break; + } + glSamplerParameterfv(smp->gl.smp, GL_TEXTURE_BORDER_COLOR, border); + #endif + if (smp->cmn.compare != SG_COMPAREFUNC_NEVER) { + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_COMPARE_FUNC, (GLint)_sg_gl_compare_func(smp->cmn.compare)); + } else { + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_COMPARE_MODE, GL_NONE); + } + if (_sg.gl.ext_anisotropic && (smp->cmn.max_anisotropy > 1)) { + GLint max_aniso = (GLint) smp->cmn.max_anisotropy; + if (max_aniso > _sg.gl.max_anisotropy) { + max_aniso = _sg.gl.max_anisotropy; + } + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso); + } + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_invalidate_texture_sampler(0, smp->gl.smp); + if (!smp->gl.injected) { + glDeleteSamplers(1, &smp->gl.smp); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE GLuint _sg_gl_compile_shader(sg_shader_stage stage, const char* src) { + SOKOL_ASSERT(src); + _SG_GL_CHECK_ERROR(); + GLuint gl_shd = glCreateShader(_sg_gl_shader_stage(stage)); + glShaderSource(gl_shd, 1, &src, 0); + glCompileShader(gl_shd); + GLint compile_status = 0; + glGetShaderiv(gl_shd, GL_COMPILE_STATUS, &compile_status); + if (!compile_status) { + // compilation failed, log error and delete shader + GLint log_len = 0; + glGetShaderiv(gl_shd, GL_INFO_LOG_LENGTH, &log_len); + if (log_len > 0) { + GLchar* log_buf = (GLchar*) _sg_malloc((size_t)log_len); + glGetShaderInfoLog(gl_shd, log_len, &log_len, log_buf); + _SG_ERROR(GL_SHADER_COMPILATION_FAILED); + _SG_LOGMSG(GL_SHADER_COMPILATION_FAILED, log_buf); + _sg_free(log_buf); + } + glDeleteShader(gl_shd); + gl_shd = 0; + } + _SG_GL_CHECK_ERROR(); + return gl_shd; +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(!shd->gl.prog); + _SG_GL_CHECK_ERROR(); + + // copy the optional vertex attribute names over + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + _sg_strcpy(&shd->gl.attrs[i].name, desc->attrs[i].name); + } + + GLuint gl_vs = _sg_gl_compile_shader(SG_SHADERSTAGE_VS, desc->vs.source); + GLuint gl_fs = _sg_gl_compile_shader(SG_SHADERSTAGE_FS, desc->fs.source); + if (!(gl_vs && gl_fs)) { + return SG_RESOURCESTATE_FAILED; + } + GLuint gl_prog = glCreateProgram(); + glAttachShader(gl_prog, gl_vs); + glAttachShader(gl_prog, gl_fs); + glLinkProgram(gl_prog); + glDeleteShader(gl_vs); + glDeleteShader(gl_fs); + _SG_GL_CHECK_ERROR(); + + GLint link_status; + glGetProgramiv(gl_prog, GL_LINK_STATUS, &link_status); + if (!link_status) { + GLint log_len = 0; + glGetProgramiv(gl_prog, GL_INFO_LOG_LENGTH, &log_len); + if (log_len > 0) { + GLchar* log_buf = (GLchar*) _sg_malloc((size_t)log_len); + glGetProgramInfoLog(gl_prog, log_len, &log_len, log_buf); + _SG_ERROR(GL_SHADER_LINKING_FAILED); + _SG_LOGMSG(GL_SHADER_LINKING_FAILED, log_buf); + _sg_free(log_buf); + } + glDeleteProgram(gl_prog); + return SG_RESOURCESTATE_FAILED; + } + shd->gl.prog = gl_prog; + + // resolve uniforms + _SG_GL_CHECK_ERROR(); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &desc->vs : &desc->fs; + const _sg_shader_stage_t* stage = &shd->cmn.stage[stage_index]; + _sg_gl_shader_stage_t* gl_stage = &shd->gl.stage[stage_index]; + for (int ub_index = 0; ub_index < stage->num_uniform_blocks; ub_index++) { + const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + SOKOL_ASSERT(ub_desc->size > 0); + _sg_gl_uniform_block_t* ub = &gl_stage->uniform_blocks[ub_index]; + SOKOL_ASSERT(ub->num_uniforms == 0); + uint32_t cur_uniform_offset = 0; + for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { + const sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; + if (u_desc->type == SG_UNIFORMTYPE_INVALID) { + break; + } + const uint32_t u_align = _sg_uniform_alignment(u_desc->type, u_desc->array_count, ub_desc->layout); + const uint32_t u_size = _sg_uniform_size(u_desc->type, u_desc->array_count, ub_desc->layout); + cur_uniform_offset = _sg_align_u32(cur_uniform_offset, u_align); + _sg_gl_uniform_t* u = &ub->uniforms[u_index]; + u->type = u_desc->type; + u->count = (uint16_t) u_desc->array_count; + u->offset = (uint16_t) cur_uniform_offset; + cur_uniform_offset += u_size; + if (u_desc->name) { + u->gl_loc = glGetUniformLocation(gl_prog, u_desc->name); + } else { + u->gl_loc = u_index; + } + ub->num_uniforms++; + } + if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { + cur_uniform_offset = _sg_align_u32(cur_uniform_offset, 16); + } + SOKOL_ASSERT(ub_desc->size == (size_t)cur_uniform_offset); + _SOKOL_UNUSED(cur_uniform_offset); + } + } + + // resolve combined image samplers + _SG_GL_CHECK_ERROR(); + GLuint cur_prog = 0; + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&cur_prog); + glUseProgram(gl_prog); + int gl_tex_slot = 0; + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &desc->vs : &desc->fs; + const _sg_shader_stage_t* stage = &shd->cmn.stage[stage_index]; + _sg_gl_shader_stage_t* gl_stage = &shd->gl.stage[stage_index]; + for (int img_smp_index = 0; img_smp_index < stage->num_image_samplers; img_smp_index++) { + const sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_index]; + _sg_gl_shader_image_sampler_t* gl_img_smp = &gl_stage->image_samplers[img_smp_index]; + SOKOL_ASSERT(img_smp_desc->glsl_name); + GLint gl_loc = glGetUniformLocation(gl_prog, img_smp_desc->glsl_name); + if (gl_loc != -1) { + gl_img_smp->gl_tex_slot = gl_tex_slot++; + glUniform1i(gl_loc, gl_img_smp->gl_tex_slot); + } else { + gl_img_smp->gl_tex_slot = -1; + _SG_ERROR(GL_TEXTURE_NAME_NOT_FOUND_IN_SHADER); + _SG_LOGMSG(GL_TEXTURE_NAME_NOT_FOUND_IN_SHADER, img_smp_desc->glsl_name); + } + } + } + // it's legal to call glUseProgram with 0 + glUseProgram(cur_prog); + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_discard_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _SG_GL_CHECK_ERROR(); + if (shd->gl.prog) { + _sg_gl_cache_invalidate_program(shd->gl.prog); + glDeleteProgram(shd->gl.prog); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT((pip->shader == 0) && (pip->cmn.shader_id.id != SG_INVALID_ID)); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + SOKOL_ASSERT(shd->gl.prog); + pip->shader = shd; + pip->gl.primitive_type = desc->primitive_type; + pip->gl.depth = desc->depth; + pip->gl.stencil = desc->stencil; + // FIXME: blend color and write mask per draw-buffer-attachment (requires GL4) + pip->gl.blend = desc->colors[0].blend; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + pip->gl.color_write_mask[i] = desc->colors[i].write_mask; + } + pip->gl.cull_mode = desc->cull_mode; + pip->gl.face_winding = desc->face_winding; + pip->gl.sample_count = desc->sample_count; + pip->gl.alpha_to_coverage_enabled = desc->alpha_to_coverage_enabled; + + // resolve vertex attributes + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + pip->gl.attrs[attr_index].vb_index = -1; + } + for (int attr_index = 0; attr_index < _sg.limits.max_vertex_attrs; attr_index++) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[a_state->buffer_index]; + const sg_vertex_step step_func = l_state->step_func; + const int step_rate = l_state->step_rate; + GLint attr_loc = attr_index; + if (!_sg_strempty(&shd->gl.attrs[attr_index].name)) { + attr_loc = glGetAttribLocation(pip->shader->gl.prog, _sg_strptr(&shd->gl.attrs[attr_index].name)); + } + SOKOL_ASSERT(attr_loc < (GLint)_sg.limits.max_vertex_attrs); + if (attr_loc != -1) { + _sg_gl_attr_t* gl_attr = &pip->gl.attrs[attr_loc]; + SOKOL_ASSERT(gl_attr->vb_index == -1); + gl_attr->vb_index = (int8_t) a_state->buffer_index; + if (step_func == SG_VERTEXSTEP_PER_VERTEX) { + gl_attr->divisor = 0; + } else { + gl_attr->divisor = (int8_t) step_rate; + pip->cmn.use_instanced_draw = true; + } + SOKOL_ASSERT(l_state->stride > 0); + gl_attr->stride = (uint8_t) l_state->stride; + gl_attr->offset = a_state->offset; + gl_attr->size = (uint8_t) _sg_gl_vertexformat_size(a_state->format); + gl_attr->type = _sg_gl_vertexformat_type(a_state->format); + gl_attr->normalized = _sg_gl_vertexformat_normalized(a_state->format); + pip->cmn.vertex_buffer_layout_active[a_state->buffer_index] = true; + } else { + _SG_ERROR(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER); + _SG_LOGMSG(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER, _sg_strptr(&shd->gl.attrs[attr_index].name)); + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_discard_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _sg_gl_cache_invalidate_pipeline(pip); +} + +_SOKOL_PRIVATE void _sg_gl_fb_attach_texture(const _sg_gl_attachment_t* gl_att, const _sg_attachment_common_t* cmn_att, GLenum gl_att_type) { + const _sg_image_t* img = gl_att->image; + SOKOL_ASSERT(img); + const GLuint gl_tex = img->gl.tex[0]; + SOKOL_ASSERT(gl_tex); + const GLuint gl_target = img->gl.target; + SOKOL_ASSERT(gl_target); + const int mip_level = cmn_att->mip_level; + const int slice = cmn_att->slice; + switch (img->cmn.type) { + case SG_IMAGETYPE_2D: + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, gl_target, gl_tex, mip_level); + break; + case SG_IMAGETYPE_CUBE: + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, _sg_gl_cubeface_target(slice), gl_tex, mip_level); + break; + default: + glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_att_type, gl_tex, mip_level, slice); + break; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_depth_stencil_attachment_type(const _sg_gl_attachment_t* ds_att) { + const _sg_image_t* img = ds_att->image; + SOKOL_ASSERT(img); + if (_sg_is_depth_stencil_format(img->cmn.pixel_format)) { + return GL_DEPTH_STENCIL_ATTACHMENT; + } else { + return GL_DEPTH_ATTACHMENT; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_attachments(_sg_attachments_t* atts, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_image, const sg_attachments_desc* desc) { + SOKOL_ASSERT(atts && desc); + SOKOL_ASSERT(color_images && resolve_images); + _SG_GL_CHECK_ERROR(); + + // copy image pointers + for (int i = 0; i < atts->cmn.num_colors; i++) { + const sg_attachment_desc* color_desc = &desc->colors[i]; + _SOKOL_UNUSED(color_desc); + SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == atts->gl.colors[i].image); + SOKOL_ASSERT(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); + atts->gl.colors[i].image = color_images[i]; + + const sg_attachment_desc* resolve_desc = &desc->resolves[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == atts->gl.resolves[i].image); + SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); + SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); + atts->gl.resolves[i].image = resolve_images[i]; + } + } + SOKOL_ASSERT(0 == atts->gl.depth_stencil.image); + const sg_attachment_desc* ds_desc = &desc->depth_stencil; + if (ds_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(ds_image && (ds_image->slot.id == ds_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_image->cmn.pixel_format)); + atts->gl.depth_stencil.image = ds_image; + } + + // store current framebuffer binding (restored at end of function) + GLuint gl_orig_fb; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&gl_orig_fb); + + // create a framebuffer object + glGenFramebuffers(1, &atts->gl.fb); + glBindFramebuffer(GL_FRAMEBUFFER, atts->gl.fb); + + // attach color attachments to framebuffer + for (int i = 0; i < atts->cmn.num_colors; i++) { + const _sg_image_t* color_img = atts->gl.colors[i].image; + SOKOL_ASSERT(color_img); + const GLuint gl_msaa_render_buffer = color_img->gl.msaa_render_buffer; + if (gl_msaa_render_buffer) { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, (GLenum)(GL_COLOR_ATTACHMENT0+i), GL_RENDERBUFFER, gl_msaa_render_buffer); + } else { + const GLenum gl_att_type = (GLenum)(GL_COLOR_ATTACHMENT0 + i); + _sg_gl_fb_attach_texture(&atts->gl.colors[i], &atts->cmn.colors[i], gl_att_type); + } + } + // attach depth-stencil attachment + if (atts->gl.depth_stencil.image) { + const GLenum gl_att = _sg_gl_depth_stencil_attachment_type(&atts->gl.depth_stencil); + const _sg_image_t* ds_img = atts->gl.depth_stencil.image; + const GLuint gl_msaa_render_buffer = ds_img->gl.msaa_render_buffer; + if (gl_msaa_render_buffer) { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, gl_att, GL_RENDERBUFFER, gl_msaa_render_buffer); + } else { + const GLenum gl_att_type = _sg_gl_depth_stencil_attachment_type(&atts->gl.depth_stencil); + _sg_gl_fb_attach_texture(&atts->gl.depth_stencil, &atts->cmn.depth_stencil, gl_att_type); + } + } + + // check if framebuffer is complete + { + const GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (fb_status != GL_FRAMEBUFFER_COMPLETE) { + switch (fb_status) { + case GL_FRAMEBUFFER_UNDEFINED: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNDEFINED); + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_ATTACHMENT); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MISSING_ATTACHMENT); + break; + case GL_FRAMEBUFFER_UNSUPPORTED: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNSUPPORTED); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MULTISAMPLE); + break; + default: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNKNOWN); + break; + } + return SG_RESOURCESTATE_FAILED; + } + } + + // setup color attachments for the framebuffer + static const GLenum gl_draw_bufs[SG_MAX_COLOR_ATTACHMENTS] = { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3 + }; + glDrawBuffers(atts->cmn.num_colors, gl_draw_bufs); + + // create MSAA resolve framebuffers if necessary + for (int i = 0; i < atts->cmn.num_colors; i++) { + _sg_gl_attachment_t* gl_resolve_att = &atts->gl.resolves[i]; + if (gl_resolve_att->image) { + _sg_attachment_common_t* cmn_resolve_att = &atts->cmn.resolves[i]; + SOKOL_ASSERT(0 == atts->gl.msaa_resolve_framebuffer[i]); + glGenFramebuffers(1, &atts->gl.msaa_resolve_framebuffer[i]); + glBindFramebuffer(GL_FRAMEBUFFER, atts->gl.msaa_resolve_framebuffer[i]); + _sg_gl_fb_attach_texture(gl_resolve_att, cmn_resolve_att, GL_COLOR_ATTACHMENT0); + // check if framebuffer is complete + const GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (fb_status != GL_FRAMEBUFFER_COMPLETE) { + switch (fb_status) { + case GL_FRAMEBUFFER_UNDEFINED: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNDEFINED); + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_ATTACHMENT); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MISSING_ATTACHMENT); + break; + case GL_FRAMEBUFFER_UNSUPPORTED: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNSUPPORTED); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_INCOMPLETE_MULTISAMPLE); + break; + default: + _SG_ERROR(GL_FRAMEBUFFER_STATUS_UNKNOWN); + break; + } + return SG_RESOURCESTATE_FAILED; + } + // setup color attachments for the framebuffer + glDrawBuffers(1, &gl_draw_bufs[0]); + } + } + + // restore original framebuffer binding + glBindFramebuffer(GL_FRAMEBUFFER, gl_orig_fb); + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_discard_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + _SG_GL_CHECK_ERROR(); + if (0 != atts->gl.fb) { + glDeleteFramebuffers(1, &atts->gl.fb); + } + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (atts->gl.msaa_resolve_framebuffer[i]) { + glDeleteFramebuffers(1, &atts->gl.msaa_resolve_framebuffer[i]); + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE _sg_image_t* _sg_gl_attachments_color_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->gl.colors[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_gl_attachments_resolve_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->gl.resolves[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_gl_attachments_ds_image(const _sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + return atts->gl.depth_stencil.image; +} + +_SOKOL_PRIVATE void _sg_gl_begin_pass(const sg_pass* pass) { + // FIXME: what if a texture used as render target is still bound, should we + // unbind all currently bound textures in begin pass? + SOKOL_ASSERT(pass); + _SG_GL_CHECK_ERROR(); + const _sg_attachments_t* atts = _sg.cur_pass.atts; + const sg_swapchain* swapchain = &pass->swapchain; + const sg_pass_action* action = &pass->action; + + // bind the render pass framebuffer + // + // FIXME: Disabling SRGB conversion for the default framebuffer is + // a crude hack to make behaviour for sRGB render target textures + // identical with the Metal and D3D11 swapchains created by sokol-app. + // + // This will need a cleaner solution (e.g. allowing to configure + // sokol_app.h with an sRGB or RGB framebuffer. + if (atts) { + // offscreen pass + SOKOL_ASSERT(atts->gl.fb); + #if defined(SOKOL_GLCORE) + glEnable(GL_FRAMEBUFFER_SRGB); + #endif + glBindFramebuffer(GL_FRAMEBUFFER, atts->gl.fb); + } else { + // default pass + #if defined(SOKOL_GLCORE) + glDisable(GL_FRAMEBUFFER_SRGB); + #endif + // NOTE: on some platforms, the default framebuffer of a context + // is null, so we can't actually assert here that the + // framebuffer has been provided + glBindFramebuffer(GL_FRAMEBUFFER, swapchain->gl.framebuffer); + } + glViewport(0, 0, _sg.cur_pass.width, _sg.cur_pass.height); + glScissor(0, 0, _sg.cur_pass.width, _sg.cur_pass.height); + + // number of color attachments + const int num_color_atts = atts ? atts->cmn.num_colors : 1; + + // clear color and depth-stencil attachments if needed + bool clear_any_color = false; + for (int i = 0; i < num_color_atts; i++) { + if (SG_LOADACTION_CLEAR == action->colors[i].load_action) { + clear_any_color = true; + break; + } + } + const bool clear_depth = (action->depth.load_action == SG_LOADACTION_CLEAR); + const bool clear_stencil = (action->stencil.load_action == SG_LOADACTION_CLEAR); + + bool need_pip_cache_flush = false; + if (clear_any_color) { + bool need_color_mask_flush = false; + // NOTE: not a bug to iterate over all possible color attachments + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (SG_COLORMASK_RGBA != _sg.gl.cache.color_write_mask[i]) { + need_pip_cache_flush = true; + need_color_mask_flush = true; + _sg.gl.cache.color_write_mask[i] = SG_COLORMASK_RGBA; + } + } + if (need_color_mask_flush) { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + } + if (clear_depth) { + if (!_sg.gl.cache.depth.write_enabled) { + need_pip_cache_flush = true; + _sg.gl.cache.depth.write_enabled = true; + glDepthMask(GL_TRUE); + } + if (_sg.gl.cache.depth.compare != SG_COMPAREFUNC_ALWAYS) { + need_pip_cache_flush = true; + _sg.gl.cache.depth.compare = SG_COMPAREFUNC_ALWAYS; + glDepthFunc(GL_ALWAYS); + } + } + if (clear_stencil) { + if (_sg.gl.cache.stencil.write_mask != 0xFF) { + need_pip_cache_flush = true; + _sg.gl.cache.stencil.write_mask = 0xFF; + glStencilMask(0xFF); + } + } + if (need_pip_cache_flush) { + // we messed with the state cache directly, need to clear cached + // pipeline to force re-evaluation in next sg_apply_pipeline() + _sg.gl.cache.cur_pipeline = 0; + _sg.gl.cache.cur_pipeline_id.id = SG_INVALID_ID; + } + for (int i = 0; i < num_color_atts; i++) { + if (action->colors[i].load_action == SG_LOADACTION_CLEAR) { + glClearBufferfv(GL_COLOR, i, &action->colors[i].clear_value.r); + } + } + if ((atts == 0) || (atts->gl.depth_stencil.image)) { + if (clear_depth && clear_stencil) { + glClearBufferfi(GL_DEPTH_STENCIL, 0, action->depth.clear_value, action->stencil.clear_value); + } else if (clear_depth) { + glClearBufferfv(GL_DEPTH, 0, &action->depth.clear_value); + } else if (clear_stencil) { + GLint val = (GLint) action->stencil.clear_value; + glClearBufferiv(GL_STENCIL, 0, &val); + } + } + // keep store actions for end-pass + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.gl.color_store_actions[i] = action->colors[i].store_action; + } + _sg.gl.depth_store_action = action->depth.store_action; + _sg.gl.stencil_store_action = action->stencil.store_action; + + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_end_pass(void) { + _SG_GL_CHECK_ERROR(); + + if (_sg.cur_pass.atts) { + const _sg_attachments_t* atts = _sg.cur_pass.atts; + SOKOL_ASSERT(atts->slot.id == _sg.cur_pass.atts_id.id); + bool fb_read_bound = false; + bool fb_draw_bound = false; + const int num_color_atts = atts->cmn.num_colors; + for (int i = 0; i < num_color_atts; i++) { + // perform MSAA resolve if needed + if (atts->gl.msaa_resolve_framebuffer[i] != 0) { + if (!fb_read_bound) { + SOKOL_ASSERT(atts->gl.fb); + glBindFramebuffer(GL_READ_FRAMEBUFFER, atts->gl.fb); + fb_read_bound = true; + } + const int w = atts->gl.colors[i].image->cmn.width; + const int h = atts->gl.colors[i].image->cmn.height; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, atts->gl.msaa_resolve_framebuffer[i]); + glReadBuffer((GLenum)(GL_COLOR_ATTACHMENT0 + i)); + glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); + fb_draw_bound = true; + } + } + + // invalidate framebuffers + _SOKOL_UNUSED(fb_draw_bound); + #if defined(SOKOL_GLES3) + // need to restore framebuffer binding before invalidate if the MSAA resolve had changed the binding + if (fb_draw_bound) { + glBindFramebuffer(GL_FRAMEBUFFER, atts->gl.fb); + } + GLenum invalidate_atts[SG_MAX_COLOR_ATTACHMENTS + 2] = { 0 }; + int att_index = 0; + for (int i = 0; i < num_color_atts; i++) { + if (_sg.gl.color_store_actions[i] == SG_STOREACTION_DONTCARE) { + invalidate_atts[att_index++] = (GLenum)(GL_COLOR_ATTACHMENT0 + i); + } + } + if ((_sg.gl.depth_store_action == SG_STOREACTION_DONTCARE) && (_sg.cur_pass.atts->cmn.depth_stencil.image_id.id != SG_INVALID_ID)) { + invalidate_atts[att_index++] = GL_DEPTH_ATTACHMENT; + } + if ((_sg.gl.stencil_store_action == SG_STOREACTION_DONTCARE) && (_sg.cur_pass.atts->cmn.depth_stencil.image_id.id != SG_INVALID_ID)) { + invalidate_atts[att_index++] = GL_STENCIL_ATTACHMENT; + } + if (att_index > 0) { + glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, att_index, invalidate_atts); + } + #endif + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + y = origin_top_left ? (_sg.cur_pass.height - (y+h)) : y; + glViewport(x, y, w, h); +} + +_SOKOL_PRIVATE void _sg_gl_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + y = origin_top_left ? (_sg.cur_pass.height - (y+h)) : y; + glScissor(x, y, w, h); +} + +_SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + _SG_GL_CHECK_ERROR(); + if ((_sg.gl.cache.cur_pipeline != pip) || (_sg.gl.cache.cur_pipeline_id.id != pip->slot.id)) { + _sg.gl.cache.cur_pipeline = pip; + _sg.gl.cache.cur_pipeline_id.id = pip->slot.id; + _sg.gl.cache.cur_primitive_type = _sg_gl_primitive_type(pip->gl.primitive_type); + _sg.gl.cache.cur_index_type = _sg_gl_index_type(pip->cmn.index_type); + + // update depth state + { + const sg_depth_state* state_ds = &pip->gl.depth; + sg_depth_state* cache_ds = &_sg.gl.cache.depth; + if (state_ds->compare != cache_ds->compare) { + cache_ds->compare = state_ds->compare; + glDepthFunc(_sg_gl_compare_func(state_ds->compare)); + _sg_stats_add(gl.num_render_state, 1); + } + if (state_ds->write_enabled != cache_ds->write_enabled) { + cache_ds->write_enabled = state_ds->write_enabled; + glDepthMask(state_ds->write_enabled); + _sg_stats_add(gl.num_render_state, 1); + } + if (!_sg_fequal(state_ds->bias, cache_ds->bias, 0.000001f) || + !_sg_fequal(state_ds->bias_slope_scale, cache_ds->bias_slope_scale, 0.000001f)) + { + /* according to ANGLE's D3D11 backend: + D3D11 SlopeScaledDepthBias ==> GL polygonOffsetFactor + D3D11 DepthBias ==> GL polygonOffsetUnits + DepthBiasClamp has no meaning on GL + */ + cache_ds->bias = state_ds->bias; + cache_ds->bias_slope_scale = state_ds->bias_slope_scale; + glPolygonOffset(state_ds->bias_slope_scale, state_ds->bias); + _sg_stats_add(gl.num_render_state, 1); + bool po_enabled = true; + if (_sg_fequal(state_ds->bias, 0.0f, 0.000001f) && + _sg_fequal(state_ds->bias_slope_scale, 0.0f, 0.000001f)) + { + po_enabled = false; + } + if (po_enabled != _sg.gl.cache.polygon_offset_enabled) { + _sg.gl.cache.polygon_offset_enabled = po_enabled; + if (po_enabled) { + glEnable(GL_POLYGON_OFFSET_FILL); + } else { + glDisable(GL_POLYGON_OFFSET_FILL); + } + _sg_stats_add(gl.num_render_state, 1); + } + } + } + + // update stencil state + { + const sg_stencil_state* state_ss = &pip->gl.stencil; + sg_stencil_state* cache_ss = &_sg.gl.cache.stencil; + if (state_ss->enabled != cache_ss->enabled) { + cache_ss->enabled = state_ss->enabled; + if (state_ss->enabled) { + glEnable(GL_STENCIL_TEST); + } else { + glDisable(GL_STENCIL_TEST); + } + _sg_stats_add(gl.num_render_state, 1); + } + if (state_ss->write_mask != cache_ss->write_mask) { + cache_ss->write_mask = state_ss->write_mask; + glStencilMask(state_ss->write_mask); + _sg_stats_add(gl.num_render_state, 1); + } + for (int i = 0; i < 2; i++) { + const sg_stencil_face_state* state_sfs = (i==0)? &state_ss->front : &state_ss->back; + sg_stencil_face_state* cache_sfs = (i==0)? &cache_ss->front : &cache_ss->back; + GLenum gl_face = (i==0)? GL_FRONT : GL_BACK; + if ((state_sfs->compare != cache_sfs->compare) || + (state_ss->read_mask != cache_ss->read_mask) || + (state_ss->ref != cache_ss->ref)) + { + cache_sfs->compare = state_sfs->compare; + glStencilFuncSeparate(gl_face, + _sg_gl_compare_func(state_sfs->compare), + state_ss->ref, + state_ss->read_mask); + _sg_stats_add(gl.num_render_state, 1); + } + if ((state_sfs->fail_op != cache_sfs->fail_op) || + (state_sfs->depth_fail_op != cache_sfs->depth_fail_op) || + (state_sfs->pass_op != cache_sfs->pass_op)) + { + cache_sfs->fail_op = state_sfs->fail_op; + cache_sfs->depth_fail_op = state_sfs->depth_fail_op; + cache_sfs->pass_op = state_sfs->pass_op; + glStencilOpSeparate(gl_face, + _sg_gl_stencil_op(state_sfs->fail_op), + _sg_gl_stencil_op(state_sfs->depth_fail_op), + _sg_gl_stencil_op(state_sfs->pass_op)); + _sg_stats_add(gl.num_render_state, 1); + } + } + cache_ss->read_mask = state_ss->read_mask; + cache_ss->ref = state_ss->ref; + } + + if (pip->cmn.color_count > 0) { + // update blend state + // FIXME: separate blend state per color attachment not support, needs GL4 + const sg_blend_state* state_bs = &pip->gl.blend; + sg_blend_state* cache_bs = &_sg.gl.cache.blend; + if (state_bs->enabled != cache_bs->enabled) { + cache_bs->enabled = state_bs->enabled; + if (state_bs->enabled) { + glEnable(GL_BLEND); + } else { + glDisable(GL_BLEND); + } + _sg_stats_add(gl.num_render_state, 1); + } + if ((state_bs->src_factor_rgb != cache_bs->src_factor_rgb) || + (state_bs->dst_factor_rgb != cache_bs->dst_factor_rgb) || + (state_bs->src_factor_alpha != cache_bs->src_factor_alpha) || + (state_bs->dst_factor_alpha != cache_bs->dst_factor_alpha)) + { + cache_bs->src_factor_rgb = state_bs->src_factor_rgb; + cache_bs->dst_factor_rgb = state_bs->dst_factor_rgb; + cache_bs->src_factor_alpha = state_bs->src_factor_alpha; + cache_bs->dst_factor_alpha = state_bs->dst_factor_alpha; + glBlendFuncSeparate(_sg_gl_blend_factor(state_bs->src_factor_rgb), + _sg_gl_blend_factor(state_bs->dst_factor_rgb), + _sg_gl_blend_factor(state_bs->src_factor_alpha), + _sg_gl_blend_factor(state_bs->dst_factor_alpha)); + _sg_stats_add(gl.num_render_state, 1); + } + if ((state_bs->op_rgb != cache_bs->op_rgb) || (state_bs->op_alpha != cache_bs->op_alpha)) { + cache_bs->op_rgb = state_bs->op_rgb; + cache_bs->op_alpha = state_bs->op_alpha; + glBlendEquationSeparate(_sg_gl_blend_op(state_bs->op_rgb), _sg_gl_blend_op(state_bs->op_alpha)); + _sg_stats_add(gl.num_render_state, 1); + } + + // standalone color target state + for (GLuint i = 0; i < (GLuint)pip->cmn.color_count; i++) { + if (pip->gl.color_write_mask[i] != _sg.gl.cache.color_write_mask[i]) { + const sg_color_mask cm = pip->gl.color_write_mask[i]; + _sg.gl.cache.color_write_mask[i] = cm; + #ifdef SOKOL_GLCORE + glColorMaski(i, + (cm & SG_COLORMASK_R) != 0, + (cm & SG_COLORMASK_G) != 0, + (cm & SG_COLORMASK_B) != 0, + (cm & SG_COLORMASK_A) != 0); + #else + if (0 == i) { + glColorMask((cm & SG_COLORMASK_R) != 0, + (cm & SG_COLORMASK_G) != 0, + (cm & SG_COLORMASK_B) != 0, + (cm & SG_COLORMASK_A) != 0); + } + #endif + _sg_stats_add(gl.num_render_state, 1); + } + } + + if (!_sg_fequal(pip->cmn.blend_color.r, _sg.gl.cache.blend_color.r, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.g, _sg.gl.cache.blend_color.g, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.b, _sg.gl.cache.blend_color.b, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.a, _sg.gl.cache.blend_color.a, 0.0001f)) + { + sg_color c = pip->cmn.blend_color; + _sg.gl.cache.blend_color = c; + glBlendColor(c.r, c.g, c.b, c.a); + _sg_stats_add(gl.num_render_state, 1); + } + } // pip->cmn.color_count > 0 + + if (pip->gl.cull_mode != _sg.gl.cache.cull_mode) { + _sg.gl.cache.cull_mode = pip->gl.cull_mode; + if (SG_CULLMODE_NONE == pip->gl.cull_mode) { + glDisable(GL_CULL_FACE); + _sg_stats_add(gl.num_render_state, 1); + } else { + glEnable(GL_CULL_FACE); + GLenum gl_mode = (SG_CULLMODE_FRONT == pip->gl.cull_mode) ? GL_FRONT : GL_BACK; + glCullFace(gl_mode); + _sg_stats_add(gl.num_render_state, 2); + } + } + if (pip->gl.face_winding != _sg.gl.cache.face_winding) { + _sg.gl.cache.face_winding = pip->gl.face_winding; + GLenum gl_winding = (SG_FACEWINDING_CW == pip->gl.face_winding) ? GL_CW : GL_CCW; + glFrontFace(gl_winding); + _sg_stats_add(gl.num_render_state, 1); + } + if (pip->gl.alpha_to_coverage_enabled != _sg.gl.cache.alpha_to_coverage_enabled) { + _sg.gl.cache.alpha_to_coverage_enabled = pip->gl.alpha_to_coverage_enabled; + if (pip->gl.alpha_to_coverage_enabled) { + glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); + } else { + glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + } + _sg_stats_add(gl.num_render_state, 1); + } + #ifdef SOKOL_GLCORE + if (pip->gl.sample_count != _sg.gl.cache.sample_count) { + _sg.gl.cache.sample_count = pip->gl.sample_count; + if (pip->gl.sample_count > 1) { + glEnable(GL_MULTISAMPLE); + } else { + glDisable(GL_MULTISAMPLE); + } + _sg_stats_add(gl.num_render_state, 1); + } + #endif + + // bind shader program + if (pip->shader->gl.prog != _sg.gl.cache.prog) { + _sg.gl.cache.prog = pip->shader->gl.prog; + glUseProgram(pip->shader->gl.prog); + _sg_stats_add(gl.num_use_program, 1); + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE bool _sg_gl_apply_bindings(_sg_bindings_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); + _SG_GL_CHECK_ERROR(); + + // bind combined image-samplers + _SG_GL_CHECK_ERROR(); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const _sg_shader_stage_t* stage = &bnd->pip->shader->cmn.stage[stage_index]; + const _sg_gl_shader_stage_t* gl_stage = &bnd->pip->shader->gl.stage[stage_index]; + _sg_image_t** imgs = (stage_index == SG_SHADERSTAGE_VS) ? bnd->vs_imgs : bnd->fs_imgs; + _sg_sampler_t** smps = (stage_index == SG_SHADERSTAGE_VS) ? bnd->vs_smps : bnd->fs_smps; + const int num_imgs = (stage_index == SG_SHADERSTAGE_VS) ? bnd->num_vs_imgs : bnd->num_fs_imgs; + const int num_smps = (stage_index == SG_SHADERSTAGE_VS) ? bnd->num_vs_smps : bnd->num_fs_smps; + SOKOL_ASSERT(num_imgs == stage->num_images); _SOKOL_UNUSED(num_imgs); + SOKOL_ASSERT(num_smps == stage->num_samplers); _SOKOL_UNUSED(num_smps); + for (int img_smp_index = 0; img_smp_index < stage->num_image_samplers; img_smp_index++) { + const int gl_tex_slot = gl_stage->image_samplers[img_smp_index].gl_tex_slot; + if (gl_tex_slot != -1) { + const int img_index = stage->image_samplers[img_smp_index].image_slot; + const int smp_index = stage->image_samplers[img_smp_index].sampler_slot; + SOKOL_ASSERT(img_index < num_imgs); + SOKOL_ASSERT(smp_index < num_smps); + _sg_image_t* img = imgs[img_index]; + _sg_sampler_t* smp = smps[smp_index]; + const GLenum gl_tgt = img->gl.target; + const GLuint gl_tex = img->gl.tex[img->cmn.active_slot]; + const GLuint gl_smp = smp->gl.smp; + _sg_gl_cache_bind_texture_sampler(gl_tex_slot, gl_tgt, gl_tex, gl_smp); + } + } + } + _SG_GL_CHECK_ERROR(); + + // bind storage buffers + for (int slot = 0; slot < bnd->num_vs_sbufs; slot++) { + _sg_buffer_t* sb = bnd->vs_sbufs[slot]; + GLuint gl_sb = sb->gl.buf[sb->cmn.active_slot]; + _sg_gl_cache_bind_storage_buffer(SG_SHADERSTAGE_VS, slot, gl_sb); + } + for (int slot = 0; slot < bnd->num_fs_sbufs; slot++) { + _sg_buffer_t* sb = bnd->fs_sbufs[slot]; + GLuint gl_sb = sb->gl.buf[sb->cmn.active_slot]; + _sg_gl_cache_bind_storage_buffer(SG_SHADERSTAGE_FS, slot, gl_sb); + } + + // index buffer (can be 0) + const GLuint gl_ib = bnd->ib ? bnd->ib->gl.buf[bnd->ib->cmn.active_slot] : 0; + _sg_gl_cache_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, gl_ib); + _sg.gl.cache.cur_ib_offset = bnd->ib_offset; + + // vertex attributes + for (GLuint attr_index = 0; attr_index < (GLuint)_sg.limits.max_vertex_attrs; attr_index++) { + _sg_gl_attr_t* attr = &bnd->pip->gl.attrs[attr_index]; + _sg_gl_cache_attr_t* cache_attr = &_sg.gl.cache.attrs[attr_index]; + bool cache_attr_dirty = false; + int vb_offset = 0; + GLuint gl_vb = 0; + if (attr->vb_index >= 0) { + // attribute is enabled + SOKOL_ASSERT(attr->vb_index < bnd->num_vbs); + _sg_buffer_t* vb = bnd->vbs[attr->vb_index]; + SOKOL_ASSERT(vb); + gl_vb = vb->gl.buf[vb->cmn.active_slot]; + vb_offset = bnd->vb_offsets[attr->vb_index] + attr->offset; + if ((gl_vb != cache_attr->gl_vbuf) || + (attr->size != cache_attr->gl_attr.size) || + (attr->type != cache_attr->gl_attr.type) || + (attr->normalized != cache_attr->gl_attr.normalized) || + (attr->stride != cache_attr->gl_attr.stride) || + (vb_offset != cache_attr->gl_attr.offset) || + (cache_attr->gl_attr.divisor != attr->divisor)) + { + _sg_gl_cache_bind_buffer(GL_ARRAY_BUFFER, gl_vb); + glVertexAttribPointer(attr_index, attr->size, attr->type, attr->normalized, attr->stride, (const GLvoid*)(GLintptr)vb_offset); + _sg_stats_add(gl.num_vertex_attrib_pointer, 1); + glVertexAttribDivisor(attr_index, (GLuint)attr->divisor); + _sg_stats_add(gl.num_vertex_attrib_divisor, 1); + cache_attr_dirty = true; + } + if (cache_attr->gl_attr.vb_index == -1) { + glEnableVertexAttribArray(attr_index); + _sg_stats_add(gl.num_enable_vertex_attrib_array, 1); + cache_attr_dirty = true; + } + } else { + // attribute is disabled + if (cache_attr->gl_attr.vb_index != -1) { + glDisableVertexAttribArray(attr_index); + _sg_stats_add(gl.num_disable_vertex_attrib_array, 1); + cache_attr_dirty = true; + } + } + if (cache_attr_dirty) { + cache_attr->gl_attr = *attr; + cache_attr->gl_attr.offset = vb_offset; + cache_attr->gl_vbuf = gl_vb; + } + } + _SG_GL_CHECK_ERROR(); + return true; +} + +_SOKOL_PRIVATE void _sg_gl_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->slot.id == _sg.gl.cache.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->slot.id == _sg.gl.cache.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks > ub_index); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size == data->size); + const _sg_gl_shader_stage_t* gl_stage = &_sg.gl.cache.cur_pipeline->shader->gl.stage[stage_index]; + const _sg_gl_uniform_block_t* gl_ub = &gl_stage->uniform_blocks[ub_index]; + for (int u_index = 0; u_index < gl_ub->num_uniforms; u_index++) { + const _sg_gl_uniform_t* u = &gl_ub->uniforms[u_index]; + SOKOL_ASSERT(u->type != SG_UNIFORMTYPE_INVALID); + if (u->gl_loc == -1) { + continue; + } + _sg_stats_add(gl.num_uniform, 1); + GLfloat* fptr = (GLfloat*) (((uint8_t*)data->ptr) + u->offset); + GLint* iptr = (GLint*) (((uint8_t*)data->ptr) + u->offset); + switch (u->type) { + case SG_UNIFORMTYPE_INVALID: + break; + case SG_UNIFORMTYPE_FLOAT: + glUniform1fv(u->gl_loc, u->count, fptr); + break; + case SG_UNIFORMTYPE_FLOAT2: + glUniform2fv(u->gl_loc, u->count, fptr); + break; + case SG_UNIFORMTYPE_FLOAT3: + glUniform3fv(u->gl_loc, u->count, fptr); + break; + case SG_UNIFORMTYPE_FLOAT4: + glUniform4fv(u->gl_loc, u->count, fptr); + break; + case SG_UNIFORMTYPE_INT: + glUniform1iv(u->gl_loc, u->count, iptr); + break; + case SG_UNIFORMTYPE_INT2: + glUniform2iv(u->gl_loc, u->count, iptr); + break; + case SG_UNIFORMTYPE_INT3: + glUniform3iv(u->gl_loc, u->count, iptr); + break; + case SG_UNIFORMTYPE_INT4: + glUniform4iv(u->gl_loc, u->count, iptr); + break; + case SG_UNIFORMTYPE_MAT4: + glUniformMatrix4fv(u->gl_loc, u->count, GL_FALSE, fptr); + break; + default: + SOKOL_UNREACHABLE; + break; + } + } +} + +_SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline); + const GLenum i_type = _sg.gl.cache.cur_index_type; + const GLenum p_type = _sg.gl.cache.cur_primitive_type; + const bool use_instanced_draw = (num_instances > 1) || (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw); + if (0 != i_type) { + // indexed rendering + const int i_size = (i_type == GL_UNSIGNED_SHORT) ? 2 : 4; + const int ib_offset = _sg.gl.cache.cur_ib_offset; + const GLvoid* indices = (const GLvoid*)(GLintptr)(base_element*i_size+ib_offset); + if (use_instanced_draw) { + glDrawElementsInstanced(p_type, num_elements, i_type, indices, num_instances); + } else { + glDrawElements(p_type, num_elements, i_type, indices); + } + } else { + // non-indexed rendering + if (use_instanced_draw) { + glDrawArraysInstanced(p_type, base_element, num_elements, num_instances); + } else { + glDrawArrays(p_type, base_element, num_elements); + } + } +} + +_SOKOL_PRIVATE void _sg_gl_commit(void) { + // "soft" clear bindings (only those that are actually bound) + _sg_gl_cache_clear_buffer_bindings(false); + _sg_gl_cache_clear_texture_sampler_bindings(false); +} + +_SOKOL_PRIVATE void _sg_gl_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + // only one update per buffer per frame allowed + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + GLenum gl_tgt = _sg_gl_buffer_target(buf->cmn.type); + SOKOL_ASSERT(buf->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); + GLuint gl_buf = buf->gl.buf[buf->cmn.active_slot]; + SOKOL_ASSERT(gl_buf); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_store_buffer_binding(gl_tgt); + _sg_gl_cache_bind_buffer(gl_tgt, gl_buf); + glBufferSubData(gl_tgt, 0, (GLsizeiptr)data->size, data->ptr); + _sg_gl_cache_restore_buffer_binding(gl_tgt); + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + if (new_frame) { + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + } + GLenum gl_tgt = _sg_gl_buffer_target(buf->cmn.type); + SOKOL_ASSERT(buf->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); + GLuint gl_buf = buf->gl.buf[buf->cmn.active_slot]; + SOKOL_ASSERT(gl_buf); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_store_buffer_binding(gl_tgt); + _sg_gl_cache_bind_buffer(gl_tgt, gl_buf); + glBufferSubData(gl_tgt, buf->cmn.append_pos, (GLsizeiptr)data->size, data->ptr); + _sg_gl_cache_restore_buffer_binding(gl_tgt); + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + // only one update per image per frame allowed + if (++img->cmn.active_slot >= img->cmn.num_slots) { + img->cmn.active_slot = 0; + } + SOKOL_ASSERT(img->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); + SOKOL_ASSERT(0 != img->gl.tex[img->cmn.active_slot]); + _sg_gl_cache_store_texture_sampler_binding(0); + _sg_gl_cache_bind_texture_sampler(0, img->gl.target, img->gl.tex[img->cmn.active_slot], 0); + const GLenum gl_img_format = _sg_gl_teximage_format(img->cmn.pixel_format); + const GLenum gl_img_type = _sg_gl_teximage_type(img->cmn.pixel_format); + const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1; + const int num_mips = img->cmn.num_mipmaps; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < num_mips; mip_index++) { + GLenum gl_img_target = img->gl.target; + if (SG_IMAGETYPE_CUBE == img->cmn.type) { + gl_img_target = _sg_gl_cubeface_target(face_index); + } + const GLvoid* data_ptr = data->subimage[face_index][mip_index].ptr; + int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { + glTexSubImage2D(gl_img_target, mip_index, + 0, 0, + mip_width, mip_height, + gl_img_format, gl_img_type, + data_ptr); + } else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) { + int mip_depth = img->cmn.num_slices; + if (SG_IMAGETYPE_3D == img->cmn.type) { + mip_depth = _sg_miplevel_dim(img->cmn.num_slices, mip_index); + } + glTexSubImage3D(gl_img_target, mip_index, + 0, 0, 0, + mip_width, mip_height, mip_depth, + gl_img_format, gl_img_type, + data_ptr); + + } + } + } + _sg_gl_cache_restore_texture_sampler_binding(0); +} + +// ██████ ██████ ██████ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ███ ███ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ █████ ██ ██ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██████ ██ ██ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>d3d11 backend +#elif defined(SOKOL_D3D11) + +#define _SG_D3D11_MAX_SHADERSTAGE_SRVS (32) +#define _SG_D3D11_SHADERSTAGE_IMAGE_SRV_OFFSET (0) +#define _SG_D3D11_SHADERSTAGE_BUFFER_SRV_OFFSET (16) + +#if defined(__cplusplus) +#define _sg_d3d11_AddRef(self) (self)->AddRef() +#else +#define _sg_d3d11_AddRef(self) (self)->lpVtbl->AddRef(self) +#endif + +#if defined(__cplusplus) +#define _sg_d3d11_Release(self) (self)->Release() +#else +#define _sg_d3d11_Release(self) (self)->lpVtbl->Release(self) +#endif + +// NOTE: This needs to be a macro since we can't use the polymorphism in C. It's called on many kinds of resources. +// NOTE: Based on microsoft docs, it's fine to call this with pData=NULL if DataSize is also zero. +#if defined(__cplusplus) +#define _sg_d3d11_SetPrivateData(self, guid, DataSize, pData) (self)->SetPrivateData(guid, DataSize, pData) +#else +#define _sg_d3d11_SetPrivateData(self, guid, DataSize, pData) (self)->lpVtbl->SetPrivateData(self, guid, DataSize, pData) +#endif + +#if defined(__cplusplus) +#define _sg_win32_refguid(guid) guid +#else +#define _sg_win32_refguid(guid) &guid +#endif + +static const GUID _sg_d3d11_WKPDID_D3DDebugObjectName = { 0x429b8c22,0x9188,0x4b0c, {0x87,0x42,0xac,0xb0,0xbf,0x85,0xc2,0x00} }; + +#if defined(SOKOL_DEBUG) +#define _sg_d3d11_setlabel(self, label) _sg_d3d11_SetPrivateData(self, _sg_win32_refguid(_sg_d3d11_WKPDID_D3DDebugObjectName), label ? (UINT)strlen(label) : 0, label) +#else +#define _sg_d3d11_setlabel(self, label) +#endif + + +//-- D3D11 C/C++ wrappers ------------------------------------------------------ +static inline HRESULT _sg_d3d11_CheckFormatSupport(ID3D11Device* self, DXGI_FORMAT Format, UINT* pFormatSupport) { + #if defined(__cplusplus) + return self->CheckFormatSupport(Format, pFormatSupport); + #else + return self->lpVtbl->CheckFormatSupport(self, Format, pFormatSupport); + #endif +} + +static inline void _sg_d3d11_OMSetRenderTargets(ID3D11DeviceContext* self, UINT NumViews, ID3D11RenderTargetView* const* ppRenderTargetViews, ID3D11DepthStencilView *pDepthStencilView) { + #if defined(__cplusplus) + self->OMSetRenderTargets(NumViews, ppRenderTargetViews, pDepthStencilView); + #else + self->lpVtbl->OMSetRenderTargets(self, NumViews, ppRenderTargetViews, pDepthStencilView); + #endif +} + +static inline void _sg_d3d11_RSSetState(ID3D11DeviceContext* self, ID3D11RasterizerState* pRasterizerState) { + #if defined(__cplusplus) + self->RSSetState(pRasterizerState); + #else + self->lpVtbl->RSSetState(self, pRasterizerState); + #endif +} + +static inline void _sg_d3d11_OMSetDepthStencilState(ID3D11DeviceContext* self, ID3D11DepthStencilState* pDepthStencilState, UINT StencilRef) { + #if defined(__cplusplus) + self->OMSetDepthStencilState(pDepthStencilState, StencilRef); + #else + self->lpVtbl->OMSetDepthStencilState(self, pDepthStencilState, StencilRef); + #endif +} + +static inline void _sg_d3d11_OMSetBlendState(ID3D11DeviceContext* self, ID3D11BlendState* pBlendState, const FLOAT BlendFactor[4], UINT SampleMask) { + #if defined(__cplusplus) + self->OMSetBlendState(pBlendState, BlendFactor, SampleMask); + #else + self->lpVtbl->OMSetBlendState(self, pBlendState, BlendFactor, SampleMask); + #endif +} + +static inline void _sg_d3d11_IASetVertexBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppVertexBuffers, const UINT* pStrides, const UINT* pOffsets) { + #if defined(__cplusplus) + self->IASetVertexBuffers(StartSlot, NumBuffers, ppVertexBuffers, pStrides, pOffsets); + #else + self->lpVtbl->IASetVertexBuffers(self, StartSlot, NumBuffers, ppVertexBuffers, pStrides, pOffsets); + #endif +} + +static inline void _sg_d3d11_IASetIndexBuffer(ID3D11DeviceContext* self, ID3D11Buffer* pIndexBuffer, DXGI_FORMAT Format, UINT Offset) { + #if defined(__cplusplus) + self->IASetIndexBuffer(pIndexBuffer, Format, Offset); + #else + self->lpVtbl->IASetIndexBuffer(self, pIndexBuffer, Format, Offset); + #endif +} + +static inline void _sg_d3d11_IASetInputLayout(ID3D11DeviceContext* self, ID3D11InputLayout* pInputLayout) { + #if defined(__cplusplus) + self->IASetInputLayout(pInputLayout); + #else + self->lpVtbl->IASetInputLayout(self, pInputLayout); + #endif +} + +static inline void _sg_d3d11_VSSetShader(ID3D11DeviceContext* self, ID3D11VertexShader* pVertexShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { + #if defined(__cplusplus) + self->VSSetShader(pVertexShader, ppClassInstances, NumClassInstances); + #else + self->lpVtbl->VSSetShader(self, pVertexShader, ppClassInstances, NumClassInstances); + #endif +} + +static inline void _sg_d3d11_PSSetShader(ID3D11DeviceContext* self, ID3D11PixelShader* pPixelShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { + #if defined(__cplusplus) + self->PSSetShader(pPixelShader, ppClassInstances, NumClassInstances); + #else + self->lpVtbl->PSSetShader(self, pPixelShader, ppClassInstances, NumClassInstances); + #endif +} + +static inline void _sg_d3d11_VSSetConstantBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { + #if defined(__cplusplus) + self->VSSetConstantBuffers(StartSlot, NumBuffers, ppConstantBuffers); + #else + self->lpVtbl->VSSetConstantBuffers(self, StartSlot, NumBuffers, ppConstantBuffers); + #endif +} + +static inline void _sg_d3d11_PSSetConstantBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { + #if defined(__cplusplus) + self->PSSetConstantBuffers(StartSlot, NumBuffers, ppConstantBuffers); + #else + self->lpVtbl->PSSetConstantBuffers(self, StartSlot, NumBuffers, ppConstantBuffers); + #endif +} + +static inline void _sg_d3d11_VSSetShaderResources(ID3D11DeviceContext* self, UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { + #if defined(__cplusplus) + self->VSSetShaderResources(StartSlot, NumViews, ppShaderResourceViews); + #else + self->lpVtbl->VSSetShaderResources(self, StartSlot, NumViews, ppShaderResourceViews); + #endif +} + +static inline void _sg_d3d11_PSSetShaderResources(ID3D11DeviceContext* self, UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { + #if defined(__cplusplus) + self->PSSetShaderResources(StartSlot, NumViews, ppShaderResourceViews); + #else + self->lpVtbl->PSSetShaderResources(self, StartSlot, NumViews, ppShaderResourceViews); + #endif +} + +static inline void _sg_d3d11_VSSetSamplers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { + #if defined(__cplusplus) + self->VSSetSamplers(StartSlot, NumSamplers, ppSamplers); + #else + self->lpVtbl->VSSetSamplers(self, StartSlot, NumSamplers, ppSamplers); + #endif +} + +static inline void _sg_d3d11_PSSetSamplers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { + #if defined(__cplusplus) + self->PSSetSamplers(StartSlot, NumSamplers, ppSamplers); + #else + self->lpVtbl->PSSetSamplers(self, StartSlot, NumSamplers, ppSamplers); + #endif +} + +static inline HRESULT _sg_d3d11_CreateBuffer(ID3D11Device* self, const D3D11_BUFFER_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Buffer** ppBuffer) { + #if defined(__cplusplus) + return self->CreateBuffer(pDesc, pInitialData, ppBuffer); + #else + return self->lpVtbl->CreateBuffer(self, pDesc, pInitialData, ppBuffer); + #endif +} + +static inline HRESULT _sg_d3d11_CreateTexture2D(ID3D11Device* self, const D3D11_TEXTURE2D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D** ppTexture2D) { + #if defined(__cplusplus) + return self->CreateTexture2D(pDesc, pInitialData, ppTexture2D); + #else + return self->lpVtbl->CreateTexture2D(self, pDesc, pInitialData, ppTexture2D); + #endif +} + +static inline HRESULT _sg_d3d11_CreateShaderResourceView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, ID3D11ShaderResourceView** ppSRView) { + #if defined(__cplusplus) + return self->CreateShaderResourceView(pResource, pDesc, ppSRView); + #else + return self->lpVtbl->CreateShaderResourceView(self, pResource, pDesc, ppSRView); + #endif +} + +static inline void _sg_d3d11_GetResource(ID3D11View* self, ID3D11Resource** ppResource) { + #if defined(__cplusplus) + self->GetResource(ppResource); + #else + self->lpVtbl->GetResource(self, ppResource); + #endif +} + +static inline HRESULT _sg_d3d11_CreateTexture3D(ID3D11Device* self, const D3D11_TEXTURE3D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture3D** ppTexture3D) { + #if defined(__cplusplus) + return self->CreateTexture3D(pDesc, pInitialData, ppTexture3D); + #else + return self->lpVtbl->CreateTexture3D(self, pDesc, pInitialData, ppTexture3D); + #endif +} + +static inline HRESULT _sg_d3d11_CreateSamplerState(ID3D11Device* self, const D3D11_SAMPLER_DESC* pSamplerDesc, ID3D11SamplerState** ppSamplerState) { + #if defined(__cplusplus) + return self->CreateSamplerState(pSamplerDesc, ppSamplerState); + #else + return self->lpVtbl->CreateSamplerState(self, pSamplerDesc, ppSamplerState); + #endif +} + +static inline LPVOID _sg_d3d11_GetBufferPointer(ID3D10Blob* self) { + #if defined(__cplusplus) + return self->GetBufferPointer(); + #else + return self->lpVtbl->GetBufferPointer(self); + #endif +} + +static inline SIZE_T _sg_d3d11_GetBufferSize(ID3D10Blob* self) { + #if defined(__cplusplus) + return self->GetBufferSize(); + #else + return self->lpVtbl->GetBufferSize(self); + #endif +} + +static inline HRESULT _sg_d3d11_CreateVertexShader(ID3D11Device* self, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11VertexShader** ppVertexShader) { + #if defined(__cplusplus) + return self->CreateVertexShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppVertexShader); + #else + return self->lpVtbl->CreateVertexShader(self, pShaderBytecode, BytecodeLength, pClassLinkage, ppVertexShader); + #endif +} + +static inline HRESULT _sg_d3d11_CreatePixelShader(ID3D11Device* self, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11PixelShader** ppPixelShader) { + #if defined(__cplusplus) + return self->CreatePixelShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppPixelShader); + #else + return self->lpVtbl->CreatePixelShader(self, pShaderBytecode, BytecodeLength, pClassLinkage, ppPixelShader); + #endif +} + +static inline HRESULT _sg_d3d11_CreateInputLayout(ID3D11Device* self, const D3D11_INPUT_ELEMENT_DESC* pInputElementDescs, UINT NumElements, const void* pShaderBytecodeWithInputSignature, SIZE_T BytecodeLength, ID3D11InputLayout **ppInputLayout) { + #if defined(__cplusplus) + return self->CreateInputLayout(pInputElementDescs, NumElements, pShaderBytecodeWithInputSignature, BytecodeLength, ppInputLayout); + #else + return self->lpVtbl->CreateInputLayout(self, pInputElementDescs, NumElements, pShaderBytecodeWithInputSignature, BytecodeLength, ppInputLayout); + #endif +} + +static inline HRESULT _sg_d3d11_CreateRasterizerState(ID3D11Device* self, const D3D11_RASTERIZER_DESC* pRasterizerDesc, ID3D11RasterizerState** ppRasterizerState) { + #if defined(__cplusplus) + return self->CreateRasterizerState(pRasterizerDesc, ppRasterizerState); + #else + return self->lpVtbl->CreateRasterizerState(self, pRasterizerDesc, ppRasterizerState); + #endif +} + +static inline HRESULT _sg_d3d11_CreateDepthStencilState(ID3D11Device* self, const D3D11_DEPTH_STENCIL_DESC* pDepthStencilDesc, ID3D11DepthStencilState** ppDepthStencilState) { + #if defined(__cplusplus) + return self->CreateDepthStencilState(pDepthStencilDesc, ppDepthStencilState); + #else + return self->lpVtbl->CreateDepthStencilState(self, pDepthStencilDesc, ppDepthStencilState); + #endif +} + +static inline HRESULT _sg_d3d11_CreateBlendState(ID3D11Device* self, const D3D11_BLEND_DESC* pBlendStateDesc, ID3D11BlendState** ppBlendState) { + #if defined(__cplusplus) + return self->CreateBlendState(pBlendStateDesc, ppBlendState); + #else + return self->lpVtbl->CreateBlendState(self, pBlendStateDesc, ppBlendState); + #endif +} + +static inline HRESULT _sg_d3d11_CreateRenderTargetView(ID3D11Device* self, ID3D11Resource *pResource, const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, ID3D11RenderTargetView** ppRTView) { + #if defined(__cplusplus) + return self->CreateRenderTargetView(pResource, pDesc, ppRTView); + #else + return self->lpVtbl->CreateRenderTargetView(self, pResource, pDesc, ppRTView); + #endif +} + +static inline HRESULT _sg_d3d11_CreateDepthStencilView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D11DepthStencilView** ppDepthStencilView) { + #if defined(__cplusplus) + return self->CreateDepthStencilView(pResource, pDesc, ppDepthStencilView); + #else + return self->lpVtbl->CreateDepthStencilView(self, pResource, pDesc, ppDepthStencilView); + #endif +} + +static inline void _sg_d3d11_RSSetViewports(ID3D11DeviceContext* self, UINT NumViewports, const D3D11_VIEWPORT* pViewports) { + #if defined(__cplusplus) + self->RSSetViewports(NumViewports, pViewports); + #else + self->lpVtbl->RSSetViewports(self, NumViewports, pViewports); + #endif +} + +static inline void _sg_d3d11_RSSetScissorRects(ID3D11DeviceContext* self, UINT NumRects, const D3D11_RECT* pRects) { + #if defined(__cplusplus) + self->RSSetScissorRects(NumRects, pRects); + #else + self->lpVtbl->RSSetScissorRects(self, NumRects, pRects); + #endif +} + +static inline void _sg_d3d11_ClearRenderTargetView(ID3D11DeviceContext* self, ID3D11RenderTargetView* pRenderTargetView, const FLOAT ColorRGBA[4]) { + #if defined(__cplusplus) + self->ClearRenderTargetView(pRenderTargetView, ColorRGBA); + #else + self->lpVtbl->ClearRenderTargetView(self, pRenderTargetView, ColorRGBA); + #endif +} + +static inline void _sg_d3d11_ClearDepthStencilView(ID3D11DeviceContext* self, ID3D11DepthStencilView* pDepthStencilView, UINT ClearFlags, FLOAT Depth, UINT8 Stencil) { + #if defined(__cplusplus) + self->ClearDepthStencilView(pDepthStencilView, ClearFlags, Depth, Stencil); + #else + self->lpVtbl->ClearDepthStencilView(self, pDepthStencilView, ClearFlags, Depth, Stencil); + #endif +} + +static inline void _sg_d3d11_ResolveSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, ID3D11Resource* pSrcResource, UINT SrcSubresource, DXGI_FORMAT Format) { + #if defined(__cplusplus) + self->ResolveSubresource(pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format); + #else + self->lpVtbl->ResolveSubresource(self, pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format); + #endif +} + +static inline void _sg_d3d11_IASetPrimitiveTopology(ID3D11DeviceContext* self, D3D11_PRIMITIVE_TOPOLOGY Topology) { + #if defined(__cplusplus) + self->IASetPrimitiveTopology(Topology); + #else + self->lpVtbl->IASetPrimitiveTopology(self, Topology); + #endif +} + +static inline void _sg_d3d11_UpdateSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch) { + #if defined(__cplusplus) + self->UpdateSubresource(pDstResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch); + #else + self->lpVtbl->UpdateSubresource(self, pDstResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch); + #endif +} + +static inline void _sg_d3d11_DrawIndexed(ID3D11DeviceContext* self, UINT IndexCount, UINT StartIndexLocation, INT BaseVertexLocation) { + #if defined(__cplusplus) + self->DrawIndexed(IndexCount, StartIndexLocation, BaseVertexLocation); + #else + self->lpVtbl->DrawIndexed(self, IndexCount, StartIndexLocation, BaseVertexLocation); + #endif +} + +static inline void _sg_d3d11_DrawIndexedInstanced(ID3D11DeviceContext* self, UINT IndexCountPerInstance, UINT InstanceCount, UINT StartIndexLocation, INT BaseVertexLocation, UINT StartInstanceLocation) { + #if defined(__cplusplus) + self->DrawIndexedInstanced(IndexCountPerInstance, InstanceCount, StartIndexLocation, BaseVertexLocation, StartInstanceLocation); + #else + self->lpVtbl->DrawIndexedInstanced(self, IndexCountPerInstance, InstanceCount, StartIndexLocation, BaseVertexLocation, StartInstanceLocation); + #endif +} + +static inline void _sg_d3d11_Draw(ID3D11DeviceContext* self, UINT VertexCount, UINT StartVertexLocation) { + #if defined(__cplusplus) + self->Draw(VertexCount, StartVertexLocation); + #else + self->lpVtbl->Draw(self, VertexCount, StartVertexLocation); + #endif +} + +static inline void _sg_d3d11_DrawInstanced(ID3D11DeviceContext* self, UINT VertexCountPerInstance, UINT InstanceCount, UINT StartVertexLocation, UINT StartInstanceLocation) { + #if defined(__cplusplus) + self->DrawInstanced(VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation); + #else + self->lpVtbl->DrawInstanced(self, VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation); + #endif +} + +static inline HRESULT _sg_d3d11_Map(ID3D11DeviceContext* self, ID3D11Resource* pResource, UINT Subresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource) { + #if defined(__cplusplus) + return self->Map(pResource, Subresource, MapType, MapFlags, pMappedResource); + #else + return self->lpVtbl->Map(self, pResource, Subresource, MapType, MapFlags, pMappedResource); + #endif +} + +static inline void _sg_d3d11_Unmap(ID3D11DeviceContext* self, ID3D11Resource* pResource, UINT Subresource) { + #if defined(__cplusplus) + self->Unmap(pResource, Subresource); + #else + self->lpVtbl->Unmap(self, pResource, Subresource); + #endif +} + +static inline void _sg_d3d11_ClearState(ID3D11DeviceContext* self) { + #if defined(__cplusplus) + self->ClearState(); + #else + self->lpVtbl->ClearState(self); + #endif +} + +//-- enum translation functions ------------------------------------------------ +_SOKOL_PRIVATE D3D11_USAGE _sg_d3d11_usage(sg_usage usg) { + switch (usg) { + case SG_USAGE_IMMUTABLE: + return D3D11_USAGE_IMMUTABLE; + case SG_USAGE_DYNAMIC: + case SG_USAGE_STREAM: + return D3D11_USAGE_DYNAMIC; + default: + SOKOL_UNREACHABLE; + return (D3D11_USAGE) 0; + } +} + +_SOKOL_PRIVATE UINT _sg_d3d11_buffer_bind_flags(sg_buffer_type t) { + switch (t) { + case SG_BUFFERTYPE_VERTEXBUFFER: + return D3D11_BIND_VERTEX_BUFFER; + case SG_BUFFERTYPE_INDEXBUFFER: + return D3D11_BIND_INDEX_BUFFER; + case SG_BUFFERTYPE_STORAGEBUFFER: + // FIXME: for compute shaders we'd want UNORDERED_ACCESS? + return D3D11_BIND_SHADER_RESOURCE; + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE UINT _sg_d3d11_buffer_misc_flags(sg_buffer_type t) { + switch (t) { + case SG_BUFFERTYPE_VERTEXBUFFER: + case SG_BUFFERTYPE_INDEXBUFFER: + return 0; + case SG_BUFFERTYPE_STORAGEBUFFER: + return D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS; + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE UINT _sg_d3d11_cpu_access_flags(sg_usage usg) { + switch (usg) { + case SG_USAGE_IMMUTABLE: + return 0; + case SG_USAGE_DYNAMIC: + case SG_USAGE_STREAM: + return D3D11_CPU_ACCESS_WRITE; + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_texture_pixel_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: return DXGI_FORMAT_R8_UNORM; + case SG_PIXELFORMAT_R8SN: return DXGI_FORMAT_R8_SNORM; + case SG_PIXELFORMAT_R8UI: return DXGI_FORMAT_R8_UINT; + case SG_PIXELFORMAT_R8SI: return DXGI_FORMAT_R8_SINT; + case SG_PIXELFORMAT_R16: return DXGI_FORMAT_R16_UNORM; + case SG_PIXELFORMAT_R16SN: return DXGI_FORMAT_R16_SNORM; + case SG_PIXELFORMAT_R16UI: return DXGI_FORMAT_R16_UINT; + case SG_PIXELFORMAT_R16SI: return DXGI_FORMAT_R16_SINT; + case SG_PIXELFORMAT_R16F: return DXGI_FORMAT_R16_FLOAT; + case SG_PIXELFORMAT_RG8: return DXGI_FORMAT_R8G8_UNORM; + case SG_PIXELFORMAT_RG8SN: return DXGI_FORMAT_R8G8_SNORM; + case SG_PIXELFORMAT_RG8UI: return DXGI_FORMAT_R8G8_UINT; + case SG_PIXELFORMAT_RG8SI: return DXGI_FORMAT_R8G8_SINT; + case SG_PIXELFORMAT_R32UI: return DXGI_FORMAT_R32_UINT; + case SG_PIXELFORMAT_R32SI: return DXGI_FORMAT_R32_SINT; + case SG_PIXELFORMAT_R32F: return DXGI_FORMAT_R32_FLOAT; + case SG_PIXELFORMAT_RG16: return DXGI_FORMAT_R16G16_UNORM; + case SG_PIXELFORMAT_RG16SN: return DXGI_FORMAT_R16G16_SNORM; + case SG_PIXELFORMAT_RG16UI: return DXGI_FORMAT_R16G16_UINT; + case SG_PIXELFORMAT_RG16SI: return DXGI_FORMAT_R16G16_SINT; + case SG_PIXELFORMAT_RG16F: return DXGI_FORMAT_R16G16_FLOAT; + case SG_PIXELFORMAT_RGBA8: return DXGI_FORMAT_R8G8B8A8_UNORM; + case SG_PIXELFORMAT_SRGB8A8: return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + case SG_PIXELFORMAT_RGBA8SN: return DXGI_FORMAT_R8G8B8A8_SNORM; + case SG_PIXELFORMAT_RGBA8UI: return DXGI_FORMAT_R8G8B8A8_UINT; + case SG_PIXELFORMAT_RGBA8SI: return DXGI_FORMAT_R8G8B8A8_SINT; + case SG_PIXELFORMAT_BGRA8: return DXGI_FORMAT_B8G8R8A8_UNORM; + case SG_PIXELFORMAT_RGB10A2: return DXGI_FORMAT_R10G10B10A2_UNORM; + case SG_PIXELFORMAT_RG11B10F: return DXGI_FORMAT_R11G11B10_FLOAT; + case SG_PIXELFORMAT_RGB9E5: return DXGI_FORMAT_R9G9B9E5_SHAREDEXP; + case SG_PIXELFORMAT_RG32UI: return DXGI_FORMAT_R32G32_UINT; + case SG_PIXELFORMAT_RG32SI: return DXGI_FORMAT_R32G32_SINT; + case SG_PIXELFORMAT_RG32F: return DXGI_FORMAT_R32G32_FLOAT; + case SG_PIXELFORMAT_RGBA16: return DXGI_FORMAT_R16G16B16A16_UNORM; + case SG_PIXELFORMAT_RGBA16SN: return DXGI_FORMAT_R16G16B16A16_SNORM; + case SG_PIXELFORMAT_RGBA16UI: return DXGI_FORMAT_R16G16B16A16_UINT; + case SG_PIXELFORMAT_RGBA16SI: return DXGI_FORMAT_R16G16B16A16_SINT; + case SG_PIXELFORMAT_RGBA16F: return DXGI_FORMAT_R16G16B16A16_FLOAT; + case SG_PIXELFORMAT_RGBA32UI: return DXGI_FORMAT_R32G32B32A32_UINT; + case SG_PIXELFORMAT_RGBA32SI: return DXGI_FORMAT_R32G32B32A32_SINT; + case SG_PIXELFORMAT_RGBA32F: return DXGI_FORMAT_R32G32B32A32_FLOAT; + case SG_PIXELFORMAT_DEPTH: return DXGI_FORMAT_R32_TYPELESS; + case SG_PIXELFORMAT_DEPTH_STENCIL: return DXGI_FORMAT_R24G8_TYPELESS; + case SG_PIXELFORMAT_BC1_RGBA: return DXGI_FORMAT_BC1_UNORM; + case SG_PIXELFORMAT_BC2_RGBA: return DXGI_FORMAT_BC2_UNORM; + case SG_PIXELFORMAT_BC3_RGBA: return DXGI_FORMAT_BC3_UNORM; + case SG_PIXELFORMAT_BC3_SRGBA: return DXGI_FORMAT_BC3_UNORM_SRGB; + case SG_PIXELFORMAT_BC4_R: return DXGI_FORMAT_BC4_UNORM; + case SG_PIXELFORMAT_BC4_RSN: return DXGI_FORMAT_BC4_SNORM; + case SG_PIXELFORMAT_BC5_RG: return DXGI_FORMAT_BC5_UNORM; + case SG_PIXELFORMAT_BC5_RGSN: return DXGI_FORMAT_BC5_SNORM; + case SG_PIXELFORMAT_BC6H_RGBF: return DXGI_FORMAT_BC6H_SF16; + case SG_PIXELFORMAT_BC6H_RGBUF: return DXGI_FORMAT_BC6H_UF16; + case SG_PIXELFORMAT_BC7_RGBA: return DXGI_FORMAT_BC7_UNORM; + case SG_PIXELFORMAT_BC7_SRGBA: return DXGI_FORMAT_BC7_UNORM_SRGB; + default: return DXGI_FORMAT_UNKNOWN; + }; +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_srv_pixel_format(sg_pixel_format fmt) { + if (fmt == SG_PIXELFORMAT_DEPTH) { + return DXGI_FORMAT_R32_FLOAT; + } else if (fmt == SG_PIXELFORMAT_DEPTH_STENCIL) { + return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; + } else { + return _sg_d3d11_texture_pixel_format(fmt); + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_dsv_pixel_format(sg_pixel_format fmt) { + if (fmt == SG_PIXELFORMAT_DEPTH) { + return DXGI_FORMAT_D32_FLOAT; + } else if (fmt == SG_PIXELFORMAT_DEPTH_STENCIL) { + return DXGI_FORMAT_D24_UNORM_S8_UINT; + } else { + return _sg_d3d11_texture_pixel_format(fmt); + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_rtv_pixel_format(sg_pixel_format fmt) { + if (fmt == SG_PIXELFORMAT_DEPTH) { + return DXGI_FORMAT_R32_FLOAT; + } else if (fmt == SG_PIXELFORMAT_DEPTH_STENCIL) { + return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; + } else { + return _sg_d3d11_texture_pixel_format(fmt); + } +} + +_SOKOL_PRIVATE D3D11_PRIMITIVE_TOPOLOGY _sg_d3d11_primitive_topology(sg_primitive_type prim_type) { + switch (prim_type) { + case SG_PRIMITIVETYPE_POINTS: return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; + case SG_PRIMITIVETYPE_LINES: return D3D11_PRIMITIVE_TOPOLOGY_LINELIST; + case SG_PRIMITIVETYPE_LINE_STRIP: return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP; + case SG_PRIMITIVETYPE_TRIANGLES: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; + default: SOKOL_UNREACHABLE; return (D3D11_PRIMITIVE_TOPOLOGY) 0; + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_index_format(sg_index_type index_type) { + switch (index_type) { + case SG_INDEXTYPE_NONE: return DXGI_FORMAT_UNKNOWN; + case SG_INDEXTYPE_UINT16: return DXGI_FORMAT_R16_UINT; + case SG_INDEXTYPE_UINT32: return DXGI_FORMAT_R32_UINT; + default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0; + } +} + +_SOKOL_PRIVATE D3D11_FILTER _sg_d3d11_filter(sg_filter min_f, sg_filter mag_f, sg_filter mipmap_f, bool comparison, uint32_t max_anisotropy) { + uint32_t d3d11_filter = 0; + if (max_anisotropy > 1) { + // D3D11_FILTER_ANISOTROPIC = 0x55, + d3d11_filter |= 0x55; + } else { + // D3D11_FILTER_MIN_MAG_MIP_POINT = 0, + // D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR = 0x1, + // D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT = 0x4, + // D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR = 0x5, + // D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT = 0x10, + // D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 0x11, + // D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT = 0x14, + // D3D11_FILTER_MIN_MAG_MIP_LINEAR = 0x15, + if (mipmap_f == SG_FILTER_LINEAR) { + d3d11_filter |= 0x01; + } + if (mag_f == SG_FILTER_LINEAR) { + d3d11_filter |= 0x04; + } + if (min_f == SG_FILTER_LINEAR) { + d3d11_filter |= 0x10; + } + } + // D3D11_FILTER_COMPARISON_MIN_MAG_MIP_POINT = 0x80, + // D3D11_FILTER_COMPARISON_MIN_MAG_POINT_MIP_LINEAR = 0x81, + // D3D11_FILTER_COMPARISON_MIN_POINT_MAG_LINEAR_MIP_POINT = 0x84, + // D3D11_FILTER_COMPARISON_MIN_POINT_MAG_MIP_LINEAR = 0x85, + // D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_MIP_POINT = 0x90, + // D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 0x91, + // D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT = 0x94, + // D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR = 0x95, + // D3D11_FILTER_COMPARISON_ANISOTROPIC = 0xd5, + if (comparison) { + d3d11_filter |= 0x80; + } + return (D3D11_FILTER)d3d11_filter; +} + +_SOKOL_PRIVATE D3D11_TEXTURE_ADDRESS_MODE _sg_d3d11_address_mode(sg_wrap m) { + switch (m) { + case SG_WRAP_REPEAT: return D3D11_TEXTURE_ADDRESS_WRAP; + case SG_WRAP_CLAMP_TO_EDGE: return D3D11_TEXTURE_ADDRESS_CLAMP; + case SG_WRAP_CLAMP_TO_BORDER: return D3D11_TEXTURE_ADDRESS_BORDER; + case SG_WRAP_MIRRORED_REPEAT: return D3D11_TEXTURE_ADDRESS_MIRROR; + default: SOKOL_UNREACHABLE; return (D3D11_TEXTURE_ADDRESS_MODE) 0; + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_vertex_format(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return DXGI_FORMAT_R32_FLOAT; + case SG_VERTEXFORMAT_FLOAT2: return DXGI_FORMAT_R32G32_FLOAT; + case SG_VERTEXFORMAT_FLOAT3: return DXGI_FORMAT_R32G32B32_FLOAT; + case SG_VERTEXFORMAT_FLOAT4: return DXGI_FORMAT_R32G32B32A32_FLOAT; + case SG_VERTEXFORMAT_BYTE4: return DXGI_FORMAT_R8G8B8A8_SINT; + case SG_VERTEXFORMAT_BYTE4N: return DXGI_FORMAT_R8G8B8A8_SNORM; + case SG_VERTEXFORMAT_UBYTE4: return DXGI_FORMAT_R8G8B8A8_UINT; + case SG_VERTEXFORMAT_UBYTE4N: return DXGI_FORMAT_R8G8B8A8_UNORM; + case SG_VERTEXFORMAT_SHORT2: return DXGI_FORMAT_R16G16_SINT; + case SG_VERTEXFORMAT_SHORT2N: return DXGI_FORMAT_R16G16_SNORM; + case SG_VERTEXFORMAT_USHORT2N: return DXGI_FORMAT_R16G16_UNORM; + case SG_VERTEXFORMAT_SHORT4: return DXGI_FORMAT_R16G16B16A16_SINT; + case SG_VERTEXFORMAT_SHORT4N: return DXGI_FORMAT_R16G16B16A16_SNORM; + case SG_VERTEXFORMAT_USHORT4N: return DXGI_FORMAT_R16G16B16A16_UNORM; + case SG_VERTEXFORMAT_UINT10_N2: return DXGI_FORMAT_R10G10B10A2_UNORM; + case SG_VERTEXFORMAT_HALF2: return DXGI_FORMAT_R16G16_FLOAT; + case SG_VERTEXFORMAT_HALF4: return DXGI_FORMAT_R16G16B16A16_FLOAT; + default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0; + } +} + +_SOKOL_PRIVATE D3D11_INPUT_CLASSIFICATION _sg_d3d11_input_classification(sg_vertex_step step) { + switch (step) { + case SG_VERTEXSTEP_PER_VERTEX: return D3D11_INPUT_PER_VERTEX_DATA; + case SG_VERTEXSTEP_PER_INSTANCE: return D3D11_INPUT_PER_INSTANCE_DATA; + default: SOKOL_UNREACHABLE; return (D3D11_INPUT_CLASSIFICATION) 0; + } +} + +_SOKOL_PRIVATE D3D11_CULL_MODE _sg_d3d11_cull_mode(sg_cull_mode m) { + switch (m) { + case SG_CULLMODE_NONE: return D3D11_CULL_NONE; + case SG_CULLMODE_FRONT: return D3D11_CULL_FRONT; + case SG_CULLMODE_BACK: return D3D11_CULL_BACK; + default: SOKOL_UNREACHABLE; return (D3D11_CULL_MODE) 0; + } +} + +_SOKOL_PRIVATE D3D11_COMPARISON_FUNC _sg_d3d11_compare_func(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return D3D11_COMPARISON_NEVER; + case SG_COMPAREFUNC_LESS: return D3D11_COMPARISON_LESS; + case SG_COMPAREFUNC_EQUAL: return D3D11_COMPARISON_EQUAL; + case SG_COMPAREFUNC_LESS_EQUAL: return D3D11_COMPARISON_LESS_EQUAL; + case SG_COMPAREFUNC_GREATER: return D3D11_COMPARISON_GREATER; + case SG_COMPAREFUNC_NOT_EQUAL: return D3D11_COMPARISON_NOT_EQUAL; + case SG_COMPAREFUNC_GREATER_EQUAL: return D3D11_COMPARISON_GREATER_EQUAL; + case SG_COMPAREFUNC_ALWAYS: return D3D11_COMPARISON_ALWAYS; + default: SOKOL_UNREACHABLE; return (D3D11_COMPARISON_FUNC) 0; + } +} + +_SOKOL_PRIVATE D3D11_STENCIL_OP _sg_d3d11_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return D3D11_STENCIL_OP_KEEP; + case SG_STENCILOP_ZERO: return D3D11_STENCIL_OP_ZERO; + case SG_STENCILOP_REPLACE: return D3D11_STENCIL_OP_REPLACE; + case SG_STENCILOP_INCR_CLAMP: return D3D11_STENCIL_OP_INCR_SAT; + case SG_STENCILOP_DECR_CLAMP: return D3D11_STENCIL_OP_DECR_SAT; + case SG_STENCILOP_INVERT: return D3D11_STENCIL_OP_INVERT; + case SG_STENCILOP_INCR_WRAP: return D3D11_STENCIL_OP_INCR; + case SG_STENCILOP_DECR_WRAP: return D3D11_STENCIL_OP_DECR; + default: SOKOL_UNREACHABLE; return (D3D11_STENCIL_OP) 0; + } +} + +_SOKOL_PRIVATE D3D11_BLEND _sg_d3d11_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return D3D11_BLEND_ZERO; + case SG_BLENDFACTOR_ONE: return D3D11_BLEND_ONE; + case SG_BLENDFACTOR_SRC_COLOR: return D3D11_BLEND_SRC_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return D3D11_BLEND_INV_SRC_COLOR; + case SG_BLENDFACTOR_SRC_ALPHA: return D3D11_BLEND_SRC_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return D3D11_BLEND_INV_SRC_ALPHA; + case SG_BLENDFACTOR_DST_COLOR: return D3D11_BLEND_DEST_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return D3D11_BLEND_INV_DEST_COLOR; + case SG_BLENDFACTOR_DST_ALPHA: return D3D11_BLEND_DEST_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return D3D11_BLEND_INV_DEST_ALPHA; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return D3D11_BLEND_SRC_ALPHA_SAT; + case SG_BLENDFACTOR_BLEND_COLOR: return D3D11_BLEND_BLEND_FACTOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return D3D11_BLEND_INV_BLEND_FACTOR; + case SG_BLENDFACTOR_BLEND_ALPHA: return D3D11_BLEND_BLEND_FACTOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return D3D11_BLEND_INV_BLEND_FACTOR; + default: SOKOL_UNREACHABLE; return (D3D11_BLEND) 0; + } +} + +_SOKOL_PRIVATE D3D11_BLEND_OP _sg_d3d11_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return D3D11_BLEND_OP_ADD; + case SG_BLENDOP_SUBTRACT: return D3D11_BLEND_OP_SUBTRACT; + case SG_BLENDOP_REVERSE_SUBTRACT: return D3D11_BLEND_OP_REV_SUBTRACT; + default: SOKOL_UNREACHABLE; return (D3D11_BLEND_OP) 0; + } +} + +_SOKOL_PRIVATE UINT8 _sg_d3d11_color_write_mask(sg_color_mask m) { + UINT8 res = 0; + if (m & SG_COLORMASK_R) { + res |= D3D11_COLOR_WRITE_ENABLE_RED; + } + if (m & SG_COLORMASK_G) { + res |= D3D11_COLOR_WRITE_ENABLE_GREEN; + } + if (m & SG_COLORMASK_B) { + res |= D3D11_COLOR_WRITE_ENABLE_BLUE; + } + if (m & SG_COLORMASK_A) { + res |= D3D11_COLOR_WRITE_ENABLE_ALPHA; + } + return res; +} + +_SOKOL_PRIVATE UINT _sg_d3d11_dxgi_fmt_caps(DXGI_FORMAT dxgi_fmt) { + UINT dxgi_fmt_caps = 0; + if (dxgi_fmt != DXGI_FORMAT_UNKNOWN) { + HRESULT hr = _sg_d3d11_CheckFormatSupport(_sg.d3d11.dev, dxgi_fmt, &dxgi_fmt_caps); + SOKOL_ASSERT(SUCCEEDED(hr) || (E_FAIL == hr)); + if (!SUCCEEDED(hr)) { + dxgi_fmt_caps = 0; + } + } + return dxgi_fmt_caps; +} + +// see: https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-limits#resource-limits-for-feature-level-11-hardware +_SOKOL_PRIVATE void _sg_d3d11_init_caps(void) { + _sg.backend = SG_BACKEND_D3D11; + + _sg.features.origin_top_left = true; + _sg.features.image_clamp_to_border = true; + _sg.features.mrt_independent_blend_state = true; + _sg.features.mrt_independent_write_mask = true; + _sg.features.storage_buffer = true; + + _sg.limits.max_image_size_2d = 16 * 1024; + _sg.limits.max_image_size_cube = 16 * 1024; + _sg.limits.max_image_size_3d = 2 * 1024; + _sg.limits.max_image_size_array = 16 * 1024; + _sg.limits.max_image_array_layers = 2 * 1024; + _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; + + // see: https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_format_support + for (int fmt = (SG_PIXELFORMAT_NONE+1); fmt < _SG_PIXELFORMAT_NUM; fmt++) { + const UINT srv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_srv_pixel_format((sg_pixel_format)fmt)); + const UINT rtv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_rtv_pixel_format((sg_pixel_format)fmt)); + const UINT dsv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_dsv_pixel_format((sg_pixel_format)fmt)); + _sg_pixelformat_info_t* info = &_sg.formats[fmt]; + const bool render = 0 != (rtv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_RENDER_TARGET); + const bool depth = 0 != (dsv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL); + info->sample = 0 != (srv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_TEXTURE2D); + info->filter = 0 != (srv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_SHADER_SAMPLE); + info->render = render || depth; + info->blend = 0 != (rtv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_BLENDABLE); + info->msaa = 0 != (rtv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET); + info->depth = depth; + } +} + +_SOKOL_PRIVATE void _sg_d3d11_setup_backend(const sg_desc* desc) { + // assume _sg.d3d11 already is zero-initialized + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->environment.d3d11.device); + SOKOL_ASSERT(desc->environment.d3d11.device_context); + _sg.d3d11.valid = true; + _sg.d3d11.dev = (ID3D11Device*) desc->environment.d3d11.device; + _sg.d3d11.ctx = (ID3D11DeviceContext*) desc->environment.d3d11.device_context; + _sg_d3d11_init_caps(); +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_backend(void) { + SOKOL_ASSERT(_sg.d3d11.valid); + _sg.d3d11.valid = false; +} + +_SOKOL_PRIVATE void _sg_d3d11_clear_state(void) { + // clear all the device context state, so that resource refs don't keep stuck in the d3d device context + _sg_d3d11_ClearState(_sg.d3d11.ctx); +} + +_SOKOL_PRIVATE void _sg_d3d11_reset_state_cache(void) { + // just clear the d3d11 device context state + _sg_d3d11_clear_state(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + SOKOL_ASSERT(!buf->d3d11.buf); + const bool injected = (0 != desc->d3d11_buffer); + if (injected) { + buf->d3d11.buf = (ID3D11Buffer*) desc->d3d11_buffer; + _sg_d3d11_AddRef(buf->d3d11.buf); + // FIXME: for storage buffers also need to inject resource view + } else { + D3D11_BUFFER_DESC d3d11_buf_desc; + _sg_clear(&d3d11_buf_desc, sizeof(d3d11_buf_desc)); + d3d11_buf_desc.ByteWidth = (UINT)buf->cmn.size; + d3d11_buf_desc.Usage = _sg_d3d11_usage(buf->cmn.usage); + d3d11_buf_desc.BindFlags = _sg_d3d11_buffer_bind_flags(buf->cmn.type); + d3d11_buf_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(buf->cmn.usage); + d3d11_buf_desc.MiscFlags = _sg_d3d11_buffer_misc_flags(buf->cmn.type); + D3D11_SUBRESOURCE_DATA* init_data_ptr = 0; + D3D11_SUBRESOURCE_DATA init_data; + _sg_clear(&init_data, sizeof(init_data)); + if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { + SOKOL_ASSERT(desc->data.ptr); + init_data.pSysMem = desc->data.ptr; + init_data_ptr = &init_data; + } + HRESULT hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &d3d11_buf_desc, init_data_ptr, &buf->d3d11.buf); + if (!(SUCCEEDED(hr) && buf->d3d11.buf)) { + _SG_ERROR(D3D11_CREATE_BUFFER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + + // for storage buffers need to create a view object + if (buf->cmn.type == SG_BUFFERTYPE_STORAGEBUFFER) { + // FIXME: currently only shader-resource-view, in future also UAV + // storage buffer size must be multiple of 4 + SOKOL_ASSERT(_sg_multiple_u64(buf->cmn.size, 4)); + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = DXGI_FORMAT_R32_TYPELESS; + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX; + d3d11_srv_desc.BufferEx.FirstElement = 0; + d3d11_srv_desc.BufferEx.NumElements = buf->cmn.size / 4; + d3d11_srv_desc.BufferEx.Flags = D3D11_BUFFEREX_SRV_FLAG_RAW; + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)buf->d3d11.buf, &d3d11_srv_desc, &buf->d3d11.srv); + if (!(SUCCEEDED(hr) && buf->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_BUFFER_SRV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + _sg_d3d11_setlabel(buf->d3d11.buf, desc->label); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + if (buf->d3d11.buf) { + _sg_d3d11_Release(buf->d3d11.buf); + } + if (buf->d3d11.srv) { + _sg_d3d11_Release(buf->d3d11.srv); + } +} + +_SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_image_data* data) { + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices:1; + int subres_index = 0; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int slice_index = 0; slice_index < num_slices; slice_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) { + SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); + D3D11_SUBRESOURCE_DATA* subres_data = &_sg.d3d11.subres_data[subres_index]; + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + const sg_range* subimg_data = &(data->subimage[face_index][mip_index]); + const size_t slice_size = subimg_data->size / (size_t)num_slices; + const size_t slice_offset = slice_size * (size_t)slice_index; + const uint8_t* ptr = (const uint8_t*) subimg_data->ptr; + subres_data->pSysMem = ptr + slice_offset; + subres_data->SysMemPitch = (UINT)_sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + if (img->cmn.type == SG_IMAGETYPE_3D) { + // FIXME? const int mip_depth = _sg_miplevel_dim(img->depth, mip_index); + subres_data->SysMemSlicePitch = (UINT)_sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + } else { + subres_data->SysMemSlicePitch = 0; + } + } + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + SOKOL_ASSERT((0 == img->d3d11.tex2d) && (0 == img->d3d11.tex3d) && (0 == img->d3d11.res) && (0 == img->d3d11.srv)); + HRESULT hr; + + const bool injected = (0 != desc->d3d11_texture); + const bool msaa = (img->cmn.sample_count > 1); + img->d3d11.format = _sg_d3d11_texture_pixel_format(img->cmn.pixel_format); + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { + _SG_ERROR(D3D11_CREATE_2D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT); + return SG_RESOURCESTATE_FAILED; + } + + // prepare initial content pointers + D3D11_SUBRESOURCE_DATA* init_data = 0; + if (!injected && (img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { + _sg_d3d11_fill_subres_data(img, &desc->data); + init_data = _sg.d3d11.subres_data; + } + if (img->cmn.type != SG_IMAGETYPE_3D) { + // 2D-, cube- or array-texture + // first check for injected texture and/or resource view + if (injected) { + img->d3d11.tex2d = (ID3D11Texture2D*) desc->d3d11_texture; + _sg_d3d11_AddRef(img->d3d11.tex2d); + img->d3d11.srv = (ID3D11ShaderResourceView*) desc->d3d11_shader_resource_view; + if (img->d3d11.srv) { + _sg_d3d11_AddRef(img->d3d11.srv); + } + } else { + // if not injected, create 2D texture + D3D11_TEXTURE2D_DESC d3d11_tex_desc; + _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); + d3d11_tex_desc.Width = (UINT)img->cmn.width; + d3d11_tex_desc.Height = (UINT)img->cmn.height; + d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; + switch (img->cmn.type) { + case SG_IMAGETYPE_ARRAY: d3d11_tex_desc.ArraySize = (UINT)img->cmn.num_slices; break; + case SG_IMAGETYPE_CUBE: d3d11_tex_desc.ArraySize = 6; break; + default: d3d11_tex_desc.ArraySize = 1; break; + } + d3d11_tex_desc.Format = img->d3d11.format; + if (img->cmn.render_target) { + d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; + if (_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)) { + d3d11_tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + } else { + d3d11_tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; + } + if (!msaa) { + d3d11_tex_desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE; + } + d3d11_tex_desc.CPUAccessFlags = 0; + } else { + d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); + d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); + } + d3d11_tex_desc.SampleDesc.Count = (UINT)img->cmn.sample_count; + d3d11_tex_desc.SampleDesc.Quality = (UINT) (msaa ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0); + d3d11_tex_desc.MiscFlags = (img->cmn.type == SG_IMAGETYPE_CUBE) ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; + + hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex2d); + if (!(SUCCEEDED(hr) && img->d3d11.tex2d)) { + _SG_ERROR(D3D11_CREATE_2D_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(img->d3d11.tex2d, desc->label); + + // create shader-resource-view for 2D texture + // FIXME: currently we don't support setting MSAA texture as shader resource + if (!msaa) { + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = _sg_d3d11_srv_pixel_format(img->cmn.pixel_format); + switch (img->cmn.type) { + case SG_IMAGETYPE_2D: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + d3d11_srv_desc.Texture2D.MipLevels = (UINT)img->cmn.num_mipmaps; + break; + case SG_IMAGETYPE_CUBE: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + d3d11_srv_desc.TextureCube.MipLevels = (UINT)img->cmn.num_mipmaps; + break; + case SG_IMAGETYPE_ARRAY: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + d3d11_srv_desc.Texture2DArray.MipLevels = (UINT)img->cmn.num_mipmaps; + d3d11_srv_desc.Texture2DArray.ArraySize = (UINT)img->cmn.num_slices; + break; + default: + SOKOL_UNREACHABLE; break; + } + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex2d, &d3d11_srv_desc, &img->d3d11.srv); + if (!(SUCCEEDED(hr) && img->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_2D_SRV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(img->d3d11.srv, desc->label); + } + } + SOKOL_ASSERT(img->d3d11.tex2d); + img->d3d11.res = (ID3D11Resource*)img->d3d11.tex2d; + _sg_d3d11_AddRef(img->d3d11.res); + } else { + // 3D texture - same procedure, first check if injected, than create non-injected + if (injected) { + img->d3d11.tex3d = (ID3D11Texture3D*) desc->d3d11_texture; + _sg_d3d11_AddRef(img->d3d11.tex3d); + img->d3d11.srv = (ID3D11ShaderResourceView*) desc->d3d11_shader_resource_view; + if (img->d3d11.srv) { + _sg_d3d11_AddRef(img->d3d11.srv); + } + } else { + // not injected, create 3d texture + D3D11_TEXTURE3D_DESC d3d11_tex_desc; + _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); + d3d11_tex_desc.Width = (UINT)img->cmn.width; + d3d11_tex_desc.Height = (UINT)img->cmn.height; + d3d11_tex_desc.Depth = (UINT)img->cmn.num_slices; + d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; + d3d11_tex_desc.Format = img->d3d11.format; + if (img->cmn.render_target) { + d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; + d3d11_tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; + d3d11_tex_desc.CPUAccessFlags = 0; + } else { + d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); + d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); + } + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { + _SG_ERROR(D3D11_CREATE_3D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT); + return SG_RESOURCESTATE_FAILED; + } + hr = _sg_d3d11_CreateTexture3D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex3d); + if (!(SUCCEEDED(hr) && img->d3d11.tex3d)) { + _SG_ERROR(D3D11_CREATE_3D_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(img->d3d11.tex3d, desc->label); + + // create shader-resource-view for 3D texture + if (!msaa) { + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = _sg_d3d11_srv_pixel_format(img->cmn.pixel_format); + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + d3d11_srv_desc.Texture3D.MipLevels = (UINT)img->cmn.num_mipmaps; + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex3d, &d3d11_srv_desc, &img->d3d11.srv); + if (!(SUCCEEDED(hr) && img->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_3D_SRV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(img->d3d11.srv, desc->label); + } + } + SOKOL_ASSERT(img->d3d11.tex3d); + img->d3d11.res = (ID3D11Resource*)img->d3d11.tex3d; + _sg_d3d11_AddRef(img->d3d11.res); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + if (img->d3d11.tex2d) { + _sg_d3d11_Release(img->d3d11.tex2d); + } + if (img->d3d11.tex3d) { + _sg_d3d11_Release(img->d3d11.tex3d); + } + if (img->d3d11.res) { + _sg_d3d11_Release(img->d3d11.res); + } + if (img->d3d11.srv) { + _sg_d3d11_Release(img->d3d11.srv); + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + SOKOL_ASSERT(0 == smp->d3d11.smp); + const bool injected = (0 != desc->d3d11_sampler); + if (injected) { + smp->d3d11.smp = (ID3D11SamplerState*)desc->d3d11_sampler; + _sg_d3d11_AddRef(smp->d3d11.smp); + } else { + D3D11_SAMPLER_DESC d3d11_smp_desc; + _sg_clear(&d3d11_smp_desc, sizeof(d3d11_smp_desc)); + d3d11_smp_desc.Filter = _sg_d3d11_filter(desc->min_filter, desc->mag_filter, desc->mipmap_filter, desc->compare != SG_COMPAREFUNC_NEVER, desc->max_anisotropy); + d3d11_smp_desc.AddressU = _sg_d3d11_address_mode(desc->wrap_u); + d3d11_smp_desc.AddressV = _sg_d3d11_address_mode(desc->wrap_v); + d3d11_smp_desc.AddressW = _sg_d3d11_address_mode(desc->wrap_w); + d3d11_smp_desc.MipLODBias = 0.0f; // FIXME? + switch (desc->border_color) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: + // all 0.0f + break; + case SG_BORDERCOLOR_OPAQUE_WHITE: + for (int i = 0; i < 4; i++) { + d3d11_smp_desc.BorderColor[i] = 1.0f; + } + break; + default: + // opaque black + d3d11_smp_desc.BorderColor[3] = 1.0f; + break; + } + d3d11_smp_desc.MaxAnisotropy = desc->max_anisotropy; + d3d11_smp_desc.ComparisonFunc = _sg_d3d11_compare_func(desc->compare); + d3d11_smp_desc.MinLOD = desc->min_lod; + d3d11_smp_desc.MaxLOD = desc->max_lod; + HRESULT hr = _sg_d3d11_CreateSamplerState(_sg.d3d11.dev, &d3d11_smp_desc, &smp->d3d11.smp); + if (!(SUCCEEDED(hr) && smp->d3d11.smp)) { + _SG_ERROR(D3D11_CREATE_SAMPLER_STATE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(smp->d3d11.smp, desc->label); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + if (smp->d3d11.smp) { + _sg_d3d11_Release(smp->d3d11.smp); + } +} + +_SOKOL_PRIVATE bool _sg_d3d11_load_d3dcompiler_dll(void) { + if ((0 == _sg.d3d11.d3dcompiler_dll) && !_sg.d3d11.d3dcompiler_dll_load_failed) { + _sg.d3d11.d3dcompiler_dll = LoadLibraryA("d3dcompiler_47.dll"); + if (0 == _sg.d3d11.d3dcompiler_dll) { + // don't attempt to load missing DLL in the future + _SG_ERROR(D3D11_LOAD_D3DCOMPILER_47_DLL_FAILED); + _sg.d3d11.d3dcompiler_dll_load_failed = true; + return false; + } + // look up function pointers + _sg.d3d11.D3DCompile_func = (pD3DCompile)(void*) GetProcAddress(_sg.d3d11.d3dcompiler_dll, "D3DCompile"); + SOKOL_ASSERT(_sg.d3d11.D3DCompile_func); + } + return 0 != _sg.d3d11.d3dcompiler_dll; +} + +_SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_stage_desc* stage_desc) { + if (!_sg_d3d11_load_d3dcompiler_dll()) { + return NULL; + } + SOKOL_ASSERT(stage_desc->d3d11_target); + ID3DBlob* output = NULL; + ID3DBlob* errors_or_warnings = NULL; + HRESULT hr = _sg.d3d11.D3DCompile_func( + stage_desc->source, // pSrcData + strlen(stage_desc->source), // SrcDataSize + NULL, // pSourceName + NULL, // pDefines + NULL, // pInclude + stage_desc->entry ? stage_desc->entry : "main", // pEntryPoint + stage_desc->d3d11_target, // pTarget + D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR | D3DCOMPILE_OPTIMIZATION_LEVEL3, // Flags1 + 0, // Flags2 + &output, // ppCode + &errors_or_warnings); // ppErrorMsgs + if (FAILED(hr)) { + _SG_ERROR(D3D11_SHADER_COMPILATION_FAILED); + } + if (errors_or_warnings) { + _SG_WARN(D3D11_SHADER_COMPILATION_OUTPUT); + _SG_LOGMSG(D3D11_SHADER_COMPILATION_OUTPUT, (LPCSTR)_sg_d3d11_GetBufferPointer(errors_or_warnings)); + _sg_d3d11_Release(errors_or_warnings); errors_or_warnings = NULL; + } + if (FAILED(hr)) { + // just in case, usually output is NULL here + if (output) { + _sg_d3d11_Release(output); + output = NULL; + } + } + return output; +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(!shd->d3d11.vs && !shd->d3d11.fs && !shd->d3d11.vs_blob); + HRESULT hr; + + // copy vertex attribute semantic names and indices + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + _sg_strcpy(&shd->d3d11.attrs[i].sem_name, desc->attrs[i].sem_name); + shd->d3d11.attrs[i].sem_index = desc->attrs[i].sem_index; + } + + // shader stage uniform blocks and image slots + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; + _sg_d3d11_shader_stage_t* d3d11_stage = &shd->d3d11.stage[stage_index]; + for (int ub_index = 0; ub_index < cmn_stage->num_uniform_blocks; ub_index++) { + const _sg_shader_uniform_block_t* ub = &cmn_stage->uniform_blocks[ub_index]; + + // create a D3D constant buffer for each uniform block + SOKOL_ASSERT(0 == d3d11_stage->cbufs[ub_index]); + D3D11_BUFFER_DESC cb_desc; + _sg_clear(&cb_desc, sizeof(cb_desc)); + cb_desc.ByteWidth = (UINT)_sg_roundup((int)ub->size, 16); + cb_desc.Usage = D3D11_USAGE_DEFAULT; + cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &cb_desc, NULL, &d3d11_stage->cbufs[ub_index]); + if (!(SUCCEEDED(hr) && d3d11_stage->cbufs[ub_index])) { + _SG_ERROR(D3D11_CREATE_CONSTANT_BUFFER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(d3d11_stage->cbufs[ub_index], desc->label); + } + } + + const void* vs_ptr = 0, *fs_ptr = 0; + SIZE_T vs_length = 0, fs_length = 0; + ID3DBlob* vs_blob = 0, *fs_blob = 0; + if (desc->vs.bytecode.ptr && desc->fs.bytecode.ptr) { + // create from shader byte code + vs_ptr = desc->vs.bytecode.ptr; + fs_ptr = desc->fs.bytecode.ptr; + vs_length = desc->vs.bytecode.size; + fs_length = desc->fs.bytecode.size; + } else { + // compile from shader source code + vs_blob = _sg_d3d11_compile_shader(&desc->vs); + fs_blob = _sg_d3d11_compile_shader(&desc->fs); + if (vs_blob && fs_blob) { + vs_ptr = _sg_d3d11_GetBufferPointer(vs_blob); + vs_length = _sg_d3d11_GetBufferSize(vs_blob); + fs_ptr = _sg_d3d11_GetBufferPointer(fs_blob); + fs_length = _sg_d3d11_GetBufferSize(fs_blob); + } + } + sg_resource_state result = SG_RESOURCESTATE_FAILED; + if (vs_ptr && fs_ptr && (vs_length > 0) && (fs_length > 0)) { + // create the D3D vertex- and pixel-shader objects + hr = _sg_d3d11_CreateVertexShader(_sg.d3d11.dev, vs_ptr, vs_length, NULL, &shd->d3d11.vs); + bool vs_succeeded = SUCCEEDED(hr) && shd->d3d11.vs; + hr = _sg_d3d11_CreatePixelShader(_sg.d3d11.dev, fs_ptr, fs_length, NULL, &shd->d3d11.fs); + bool fs_succeeded = SUCCEEDED(hr) && shd->d3d11.fs; + + // need to store the vertex shader byte code, this is needed later in sg_create_pipeline + if (vs_succeeded && fs_succeeded) { + shd->d3d11.vs_blob_length = vs_length; + shd->d3d11.vs_blob = _sg_malloc((size_t)vs_length); + SOKOL_ASSERT(shd->d3d11.vs_blob); + memcpy(shd->d3d11.vs_blob, vs_ptr, vs_length); + result = SG_RESOURCESTATE_VALID; + _sg_d3d11_setlabel(shd->d3d11.vs, desc->label); + _sg_d3d11_setlabel(shd->d3d11.fs, desc->label); + } + } + if (vs_blob) { + _sg_d3d11_Release(vs_blob); vs_blob = 0; + } + if (fs_blob) { + _sg_d3d11_Release(fs_blob); fs_blob = 0; + } + return result; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + if (shd->d3d11.vs) { + _sg_d3d11_Release(shd->d3d11.vs); + } + if (shd->d3d11.fs) { + _sg_d3d11_Release(shd->d3d11.fs); + } + if (shd->d3d11.vs_blob) { + _sg_free(shd->d3d11.vs_blob); + } + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; + _sg_d3d11_shader_stage_t* d3d11_stage = &shd->d3d11.stage[stage_index]; + for (int ub_index = 0; ub_index < cmn_stage->num_uniform_blocks; ub_index++) { + if (d3d11_stage->cbufs[ub_index]) { + _sg_d3d11_Release(d3d11_stage->cbufs[ub_index]); + } + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(shd->d3d11.vs_blob && shd->d3d11.vs_blob_length > 0); + SOKOL_ASSERT(!pip->d3d11.il && !pip->d3d11.rs && !pip->d3d11.dss && !pip->d3d11.bs); + + pip->shader = shd; + pip->d3d11.index_format = _sg_d3d11_index_format(pip->cmn.index_type); + pip->d3d11.topology = _sg_d3d11_primitive_topology(desc->primitive_type); + pip->d3d11.stencil_ref = desc->stencil.ref; + + // create input layout object + HRESULT hr; + D3D11_INPUT_ELEMENT_DESC d3d11_comps[SG_MAX_VERTEX_ATTRIBUTES]; + _sg_clear(d3d11_comps, sizeof(d3d11_comps)); + int attr_index = 0; + for (; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[a_state->buffer_index]; + const sg_vertex_step step_func = l_state->step_func; + const int step_rate = l_state->step_rate; + D3D11_INPUT_ELEMENT_DESC* d3d11_comp = &d3d11_comps[attr_index]; + d3d11_comp->SemanticName = _sg_strptr(&shd->d3d11.attrs[attr_index].sem_name); + d3d11_comp->SemanticIndex = (UINT)shd->d3d11.attrs[attr_index].sem_index; + d3d11_comp->Format = _sg_d3d11_vertex_format(a_state->format); + d3d11_comp->InputSlot = (UINT)a_state->buffer_index; + d3d11_comp->AlignedByteOffset = (UINT)a_state->offset; + d3d11_comp->InputSlotClass = _sg_d3d11_input_classification(step_func); + if (SG_VERTEXSTEP_PER_INSTANCE == step_func) { + d3d11_comp->InstanceDataStepRate = (UINT)step_rate; + pip->cmn.use_instanced_draw = true; + } + pip->cmn.vertex_buffer_layout_active[a_state->buffer_index] = true; + } + for (int layout_index = 0; layout_index < SG_MAX_VERTEX_BUFFERS; layout_index++) { + if (pip->cmn.vertex_buffer_layout_active[layout_index]) { + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[layout_index]; + SOKOL_ASSERT(l_state->stride > 0); + pip->d3d11.vb_strides[layout_index] = (UINT)l_state->stride; + } else { + pip->d3d11.vb_strides[layout_index] = 0; + } + } + if (attr_index > 0) { + hr = _sg_d3d11_CreateInputLayout(_sg.d3d11.dev, + d3d11_comps, // pInputElementDesc + (UINT)attr_index, // NumElements + shd->d3d11.vs_blob, // pShaderByteCodeWithInputSignature + shd->d3d11.vs_blob_length, // BytecodeLength + &pip->d3d11.il); + if (!(SUCCEEDED(hr) && pip->d3d11.il)) { + _SG_ERROR(D3D11_CREATE_INPUT_LAYOUT_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(pip->d3d11.il, desc->label); + } + + // create rasterizer state + D3D11_RASTERIZER_DESC rs_desc; + _sg_clear(&rs_desc, sizeof(rs_desc)); + rs_desc.FillMode = D3D11_FILL_SOLID; + rs_desc.CullMode = _sg_d3d11_cull_mode(desc->cull_mode); + rs_desc.FrontCounterClockwise = desc->face_winding == SG_FACEWINDING_CCW; + rs_desc.DepthBias = (INT) pip->cmn.depth.bias; + rs_desc.DepthBiasClamp = pip->cmn.depth.bias_clamp; + rs_desc.SlopeScaledDepthBias = pip->cmn.depth.bias_slope_scale; + rs_desc.DepthClipEnable = TRUE; + rs_desc.ScissorEnable = TRUE; + rs_desc.MultisampleEnable = desc->sample_count > 1; + rs_desc.AntialiasedLineEnable = FALSE; + hr = _sg_d3d11_CreateRasterizerState(_sg.d3d11.dev, &rs_desc, &pip->d3d11.rs); + if (!(SUCCEEDED(hr) && pip->d3d11.rs)) { + _SG_ERROR(D3D11_CREATE_RASTERIZER_STATE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(pip->d3d11.rs, desc->label); + + // create depth-stencil state + D3D11_DEPTH_STENCIL_DESC dss_desc; + _sg_clear(&dss_desc, sizeof(dss_desc)); + dss_desc.DepthEnable = TRUE; + dss_desc.DepthWriteMask = desc->depth.write_enabled ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + dss_desc.DepthFunc = _sg_d3d11_compare_func(desc->depth.compare); + dss_desc.StencilEnable = desc->stencil.enabled; + dss_desc.StencilReadMask = desc->stencil.read_mask; + dss_desc.StencilWriteMask = desc->stencil.write_mask; + const sg_stencil_face_state* sf = &desc->stencil.front; + dss_desc.FrontFace.StencilFailOp = _sg_d3d11_stencil_op(sf->fail_op); + dss_desc.FrontFace.StencilDepthFailOp = _sg_d3d11_stencil_op(sf->depth_fail_op); + dss_desc.FrontFace.StencilPassOp = _sg_d3d11_stencil_op(sf->pass_op); + dss_desc.FrontFace.StencilFunc = _sg_d3d11_compare_func(sf->compare); + const sg_stencil_face_state* sb = &desc->stencil.back; + dss_desc.BackFace.StencilFailOp = _sg_d3d11_stencil_op(sb->fail_op); + dss_desc.BackFace.StencilDepthFailOp = _sg_d3d11_stencil_op(sb->depth_fail_op); + dss_desc.BackFace.StencilPassOp = _sg_d3d11_stencil_op(sb->pass_op); + dss_desc.BackFace.StencilFunc = _sg_d3d11_compare_func(sb->compare); + hr = _sg_d3d11_CreateDepthStencilState(_sg.d3d11.dev, &dss_desc, &pip->d3d11.dss); + if (!(SUCCEEDED(hr) && pip->d3d11.dss)) { + _SG_ERROR(D3D11_CREATE_DEPTH_STENCIL_STATE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(pip->d3d11.dss, desc->label); + + // create blend state + D3D11_BLEND_DESC bs_desc; + _sg_clear(&bs_desc, sizeof(bs_desc)); + bs_desc.AlphaToCoverageEnable = desc->alpha_to_coverage_enabled; + bs_desc.IndependentBlendEnable = TRUE; + { + int i = 0; + for (i = 0; i < desc->color_count; i++) { + const sg_blend_state* src = &desc->colors[i].blend; + D3D11_RENDER_TARGET_BLEND_DESC* dst = &bs_desc.RenderTarget[i]; + dst->BlendEnable = src->enabled; + dst->SrcBlend = _sg_d3d11_blend_factor(src->src_factor_rgb); + dst->DestBlend = _sg_d3d11_blend_factor(src->dst_factor_rgb); + dst->BlendOp = _sg_d3d11_blend_op(src->op_rgb); + dst->SrcBlendAlpha = _sg_d3d11_blend_factor(src->src_factor_alpha); + dst->DestBlendAlpha = _sg_d3d11_blend_factor(src->dst_factor_alpha); + dst->BlendOpAlpha = _sg_d3d11_blend_op(src->op_alpha); + dst->RenderTargetWriteMask = _sg_d3d11_color_write_mask(desc->colors[i].write_mask); + } + for (; i < 8; i++) { + D3D11_RENDER_TARGET_BLEND_DESC* dst = &bs_desc.RenderTarget[i]; + dst->BlendEnable = FALSE; + dst->SrcBlend = dst->SrcBlendAlpha = D3D11_BLEND_ONE; + dst->DestBlend = dst->DestBlendAlpha = D3D11_BLEND_ZERO; + dst->BlendOp = dst->BlendOpAlpha = D3D11_BLEND_OP_ADD; + dst->RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + } + } + hr = _sg_d3d11_CreateBlendState(_sg.d3d11.dev, &bs_desc, &pip->d3d11.bs); + if (!(SUCCEEDED(hr) && pip->d3d11.bs)) { + _SG_ERROR(D3D11_CREATE_BLEND_STATE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(pip->d3d11.bs, desc->label); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + if (pip == _sg.d3d11.cur_pipeline) { + _sg.d3d11.cur_pipeline = 0; + _sg.d3d11.cur_pipeline_id.id = SG_INVALID_ID; + } + if (pip->d3d11.il) { + _sg_d3d11_Release(pip->d3d11.il); + } + if (pip->d3d11.rs) { + _sg_d3d11_Release(pip->d3d11.rs); + } + if (pip->d3d11.dss) { + _sg_d3d11_Release(pip->d3d11.dss); + } + if (pip->d3d11.bs) { + _sg_d3d11_Release(pip->d3d11.bs); + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_attachments(_sg_attachments_t* atts, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_attachments_desc* desc) { + SOKOL_ASSERT(atts && desc); + SOKOL_ASSERT(color_images && resolve_images); + SOKOL_ASSERT(_sg.d3d11.dev); + + // copy image pointers + for (int i = 0; i < atts->cmn.num_colors; i++) { + const sg_attachment_desc* color_desc = &desc->colors[i]; + _SOKOL_UNUSED(color_desc); + SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == atts->d3d11.colors[i].image); + SOKOL_ASSERT(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); + atts->d3d11.colors[i].image = color_images[i]; + + const sg_attachment_desc* resolve_desc = &desc->resolves[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == atts->d3d11.resolves[i].image); + SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); + SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); + atts->d3d11.resolves[i].image = resolve_images[i]; + } + } + SOKOL_ASSERT(0 == atts->d3d11.depth_stencil.image); + const sg_attachment_desc* ds_desc = &desc->depth_stencil; + if (ds_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(ds_img && (ds_img->slot.id == ds_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_img->cmn.pixel_format)); + atts->d3d11.depth_stencil.image = ds_img; + } + + // create render-target views + for (int i = 0; i < atts->cmn.num_colors; i++) { + const _sg_attachment_common_t* cmn_color_att = &atts->cmn.colors[i]; + const _sg_image_t* color_img = color_images[i]; + SOKOL_ASSERT(0 == atts->d3d11.colors[i].view.rtv); + const bool msaa = color_img->cmn.sample_count > 1; + D3D11_RENDER_TARGET_VIEW_DESC d3d11_rtv_desc; + _sg_clear(&d3d11_rtv_desc, sizeof(d3d11_rtv_desc)); + d3d11_rtv_desc.Format = _sg_d3d11_rtv_pixel_format(color_img->cmn.pixel_format); + if (color_img->cmn.type == SG_IMAGETYPE_2D) { + if (msaa) { + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; + } else { + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + d3d11_rtv_desc.Texture2D.MipSlice = (UINT)cmn_color_att->mip_level; + } + } else if ((color_img->cmn.type == SG_IMAGETYPE_CUBE) || (color_img->cmn.type == SG_IMAGETYPE_ARRAY)) { + if (msaa) { + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY; + d3d11_rtv_desc.Texture2DMSArray.FirstArraySlice = (UINT)cmn_color_att->slice; + d3d11_rtv_desc.Texture2DMSArray.ArraySize = 1; + } else { + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + d3d11_rtv_desc.Texture2DArray.MipSlice = (UINT)cmn_color_att->mip_level; + d3d11_rtv_desc.Texture2DArray.FirstArraySlice = (UINT)cmn_color_att->slice; + d3d11_rtv_desc.Texture2DArray.ArraySize = 1; + } + } else { + SOKOL_ASSERT(color_img->cmn.type == SG_IMAGETYPE_3D); + SOKOL_ASSERT(!msaa); + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; + d3d11_rtv_desc.Texture3D.MipSlice = (UINT)cmn_color_att->mip_level; + d3d11_rtv_desc.Texture3D.FirstWSlice = (UINT)cmn_color_att->slice; + d3d11_rtv_desc.Texture3D.WSize = 1; + } + SOKOL_ASSERT(color_img->d3d11.res); + HRESULT hr = _sg_d3d11_CreateRenderTargetView(_sg.d3d11.dev, color_img->d3d11.res, &d3d11_rtv_desc, &atts->d3d11.colors[i].view.rtv); + if (!(SUCCEEDED(hr) && atts->d3d11.colors[i].view.rtv)) { + _SG_ERROR(D3D11_CREATE_RTV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(atts->d3d11.colors[i].view.rtv, desc->label); + } + SOKOL_ASSERT(0 == atts->d3d11.depth_stencil.view.dsv); + if (ds_desc->image.id != SG_INVALID_ID) { + const _sg_attachment_common_t* cmn_ds_att = &atts->cmn.depth_stencil; + const bool msaa = ds_img->cmn.sample_count > 1; + D3D11_DEPTH_STENCIL_VIEW_DESC d3d11_dsv_desc; + _sg_clear(&d3d11_dsv_desc, sizeof(d3d11_dsv_desc)); + d3d11_dsv_desc.Format = _sg_d3d11_dsv_pixel_format(ds_img->cmn.pixel_format); + SOKOL_ASSERT(ds_img && ds_img->cmn.type != SG_IMAGETYPE_3D); + if (ds_img->cmn.type == SG_IMAGETYPE_2D) { + if (msaa) { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; + } else { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + d3d11_dsv_desc.Texture2D.MipSlice = (UINT)cmn_ds_att->mip_level; + } + } else if ((ds_img->cmn.type == SG_IMAGETYPE_CUBE) || (ds_img->cmn.type == SG_IMAGETYPE_ARRAY)) { + if (msaa) { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY; + d3d11_dsv_desc.Texture2DMSArray.FirstArraySlice = (UINT)cmn_ds_att->slice; + d3d11_dsv_desc.Texture2DMSArray.ArraySize = 1; + } else { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY; + d3d11_dsv_desc.Texture2DArray.MipSlice = (UINT)cmn_ds_att->mip_level; + d3d11_dsv_desc.Texture2DArray.FirstArraySlice = (UINT)cmn_ds_att->slice; + d3d11_dsv_desc.Texture2DArray.ArraySize = 1; + } + } + SOKOL_ASSERT(ds_img->d3d11.res); + HRESULT hr = _sg_d3d11_CreateDepthStencilView(_sg.d3d11.dev, ds_img->d3d11.res, &d3d11_dsv_desc, &atts->d3d11.depth_stencil.view.dsv); + if (!(SUCCEEDED(hr) && atts->d3d11.depth_stencil.view.dsv)) { + _SG_ERROR(D3D11_CREATE_DSV_FAILED); + return SG_RESOURCESTATE_FAILED; + } + _sg_d3d11_setlabel(atts->d3d11.depth_stencil.view.dsv, desc->label); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (atts->d3d11.colors[i].view.rtv) { + _sg_d3d11_Release(atts->d3d11.colors[i].view.rtv); + } + if (atts->d3d11.resolves[i].view.rtv) { + _sg_d3d11_Release(atts->d3d11.resolves[i].view.rtv); + } + } + if (atts->d3d11.depth_stencil.view.dsv) { + _sg_d3d11_Release(atts->d3d11.depth_stencil.view.dsv); + } +} + +_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_attachments_color_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->d3d11.colors[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_attachments_resolve_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->d3d11.resolves[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_attachments_ds_image(const _sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + return atts->d3d11.depth_stencil.image; +} + +_SOKOL_PRIVATE void _sg_d3d11_begin_pass(const sg_pass* pass) { + SOKOL_ASSERT(pass); + + const _sg_attachments_t* atts = _sg.cur_pass.atts; + const sg_swapchain* swapchain = &pass->swapchain; + const sg_pass_action* action = &pass->action; + + int num_rtvs = 0; + ID3D11RenderTargetView* rtvs[SG_MAX_COLOR_ATTACHMENTS] = { 0 }; + ID3D11DepthStencilView* dsv = 0; + _sg.d3d11.cur_pass.render_view = 0; + _sg.d3d11.cur_pass.resolve_view = 0; + if (atts) { + num_rtvs = atts->cmn.num_colors; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + rtvs[i] = atts->d3d11.colors[i].view.rtv; + } + dsv = atts->d3d11.depth_stencil.view.dsv; + } else { + // NOTE: depth-stencil-view is optional + SOKOL_ASSERT(swapchain->d3d11.render_view); + num_rtvs = 1; + rtvs[0] = (ID3D11RenderTargetView*) swapchain->d3d11.render_view; + dsv = (ID3D11DepthStencilView*) swapchain->d3d11.depth_stencil_view; + _sg.d3d11.cur_pass.render_view = (ID3D11RenderTargetView*) swapchain->d3d11.render_view; + _sg.d3d11.cur_pass.resolve_view = (ID3D11RenderTargetView*) swapchain->d3d11.resolve_view; + } + // apply the render-target- and depth-stencil-views + _sg_d3d11_OMSetRenderTargets(_sg.d3d11.ctx, SG_MAX_COLOR_ATTACHMENTS, rtvs, dsv); + _sg_stats_add(d3d11.pass.num_om_set_render_targets, 1); + + // set viewport and scissor rect to cover whole screen + D3D11_VIEWPORT vp; + _sg_clear(&vp, sizeof(vp)); + vp.Width = (FLOAT) _sg.cur_pass.width; + vp.Height = (FLOAT) _sg.cur_pass.height; + vp.MaxDepth = 1.0f; + _sg_d3d11_RSSetViewports(_sg.d3d11.ctx, 1, &vp); + D3D11_RECT rect; + rect.left = 0; + rect.top = 0; + rect.right = _sg.cur_pass.width; + rect.bottom = _sg.cur_pass.height; + _sg_d3d11_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); + + // perform clear action + for (int i = 0; i < num_rtvs; i++) { + if (action->colors[i].load_action == SG_LOADACTION_CLEAR) { + _sg_d3d11_ClearRenderTargetView(_sg.d3d11.ctx, rtvs[i], (float*)&action->colors[i].clear_value); + _sg_stats_add(d3d11.pass.num_clear_render_target_view, 1); + } + } + UINT ds_flags = 0; + if (action->depth.load_action == SG_LOADACTION_CLEAR) { + ds_flags |= D3D11_CLEAR_DEPTH; + } + if (action->stencil.load_action == SG_LOADACTION_CLEAR) { + ds_flags |= D3D11_CLEAR_STENCIL; + } + if ((0 != ds_flags) && dsv) { + _sg_d3d11_ClearDepthStencilView(_sg.d3d11.ctx, dsv, ds_flags, action->depth.clear_value, action->stencil.clear_value); + _sg_stats_add(d3d11.pass.num_clear_depth_stencil_view, 1); + } +} + +// D3D11CalcSubresource only exists for C++ +_SOKOL_PRIVATE UINT _sg_d3d11_calcsubresource(UINT mip_slice, UINT array_slice, UINT mip_levels) { + return mip_slice + array_slice * mip_levels; +} + +_SOKOL_PRIVATE void _sg_d3d11_end_pass(void) { + SOKOL_ASSERT(_sg.d3d11.ctx); + + // need to resolve MSAA render attachments into texture? + if (_sg.cur_pass.atts_id.id != SG_INVALID_ID) { + // ...for offscreen pass... + SOKOL_ASSERT(_sg.cur_pass.atts && _sg.cur_pass.atts->slot.id == _sg.cur_pass.atts_id.id); + for (int i = 0; i < _sg.cur_pass.atts->cmn.num_colors; i++) { + const _sg_image_t* resolve_img = _sg.cur_pass.atts->d3d11.resolves[i].image; + if (resolve_img) { + const _sg_image_t* color_img = _sg.cur_pass.atts->d3d11.colors[i].image; + const _sg_attachment_common_t* cmn_color_att = &_sg.cur_pass.atts->cmn.colors[i]; + const _sg_attachment_common_t* cmn_resolve_att = &_sg.cur_pass.atts->cmn.resolves[i]; + SOKOL_ASSERT(resolve_img->slot.id == cmn_resolve_att->image_id.id); + SOKOL_ASSERT(color_img && (color_img->slot.id == cmn_color_att->image_id.id)); + SOKOL_ASSERT(color_img->cmn.sample_count > 1); + SOKOL_ASSERT(resolve_img->cmn.sample_count == 1); + const UINT src_subres = _sg_d3d11_calcsubresource( + (UINT)cmn_color_att->mip_level, + (UINT)cmn_color_att->slice, + (UINT)color_img->cmn.num_mipmaps); + const UINT dst_subres = _sg_d3d11_calcsubresource( + (UINT)cmn_resolve_att->mip_level, + (UINT)cmn_resolve_att->slice, + (UINT)resolve_img->cmn.num_mipmaps); + _sg_d3d11_ResolveSubresource(_sg.d3d11.ctx, + resolve_img->d3d11.res, + dst_subres, + color_img->d3d11.res, + src_subres, + color_img->d3d11.format); + _sg_stats_add(d3d11.pass.num_resolve_subresource, 1); + } + } + } else { + // ...for swapchain pass... + if (_sg.d3d11.cur_pass.resolve_view) { + SOKOL_ASSERT(_sg.d3d11.cur_pass.render_view); + SOKOL_ASSERT(_sg.cur_pass.swapchain.sample_count > 1); + SOKOL_ASSERT(_sg.cur_pass.swapchain.color_fmt > SG_PIXELFORMAT_NONE); + ID3D11Resource* d3d11_render_res = 0; + ID3D11Resource* d3d11_resolve_res = 0; + _sg_d3d11_GetResource((ID3D11View*)_sg.d3d11.cur_pass.render_view, &d3d11_render_res); + _sg_d3d11_GetResource((ID3D11View*)_sg.d3d11.cur_pass.resolve_view, &d3d11_resolve_res); + SOKOL_ASSERT(d3d11_render_res); + SOKOL_ASSERT(d3d11_resolve_res); + const sg_pixel_format color_fmt = _sg.cur_pass.swapchain.color_fmt; + _sg_d3d11_ResolveSubresource(_sg.d3d11.ctx, d3d11_resolve_res, 0, d3d11_render_res, 0, _sg_d3d11_rtv_pixel_format(color_fmt)); + _sg_d3d11_Release(d3d11_render_res); + _sg_d3d11_Release(d3d11_resolve_res); + _sg_stats_add(d3d11.pass.num_resolve_subresource, 1); + } + } + _sg.d3d11.cur_pass.render_view = 0; + _sg.d3d11.cur_pass.resolve_view = 0; + _sg.d3d11.cur_pipeline = 0; + _sg.d3d11.cur_pipeline_id.id = SG_INVALID_ID; + _sg_d3d11_clear_state(); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.d3d11.ctx); + D3D11_VIEWPORT vp; + vp.TopLeftX = (FLOAT) x; + vp.TopLeftY = (FLOAT) (origin_top_left ? y : (_sg.cur_pass.height - (y + h))); + vp.Width = (FLOAT) w; + vp.Height = (FLOAT) h; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + _sg_d3d11_RSSetViewports(_sg.d3d11.ctx, 1, &vp); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.d3d11.ctx); + D3D11_RECT rect; + rect.left = x; + rect.top = (origin_top_left ? y : (_sg.cur_pass.height - (y + h))); + rect.right = x + w; + rect.bottom = origin_top_left ? (y + h) : (_sg.cur_pass.height - y); + _sg_d3d11_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(pip->d3d11.rs && pip->d3d11.bs && pip->d3d11.dss); + + _sg.d3d11.cur_pipeline = pip; + _sg.d3d11.cur_pipeline_id.id = pip->slot.id; + _sg.d3d11.use_indexed_draw = (pip->d3d11.index_format != DXGI_FORMAT_UNKNOWN); + _sg.d3d11.use_instanced_draw = pip->cmn.use_instanced_draw; + + _sg_d3d11_RSSetState(_sg.d3d11.ctx, pip->d3d11.rs); + _sg_d3d11_OMSetDepthStencilState(_sg.d3d11.ctx, pip->d3d11.dss, pip->d3d11.stencil_ref); + _sg_d3d11_OMSetBlendState(_sg.d3d11.ctx, pip->d3d11.bs, (float*)&pip->cmn.blend_color, 0xFFFFFFFF); + _sg_d3d11_IASetPrimitiveTopology(_sg.d3d11.ctx, pip->d3d11.topology); + _sg_d3d11_IASetInputLayout(_sg.d3d11.ctx, pip->d3d11.il); + _sg_d3d11_VSSetShader(_sg.d3d11.ctx, pip->shader->d3d11.vs, NULL, 0); + _sg_d3d11_VSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->d3d11.stage[SG_SHADERSTAGE_VS].cbufs); + _sg_d3d11_PSSetShader(_sg.d3d11.ctx, pip->shader->d3d11.fs, NULL, 0); + _sg_d3d11_PSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->d3d11.stage[SG_SHADERSTAGE_FS].cbufs); + _sg_stats_add(d3d11.pipeline.num_rs_set_state, 1); + _sg_stats_add(d3d11.pipeline.num_om_set_depth_stencil_state, 1); + _sg_stats_add(d3d11.pipeline.num_om_set_blend_state, 1); + _sg_stats_add(d3d11.pipeline.num_ia_set_primitive_topology, 1); + _sg_stats_add(d3d11.pipeline.num_ia_set_input_layout, 1); + _sg_stats_add(d3d11.pipeline.num_vs_set_shader, 1); + _sg_stats_add(d3d11.pipeline.num_vs_set_constant_buffers, 1); + _sg_stats_add(d3d11.pipeline.num_ps_set_shader, 1); + _sg_stats_add(d3d11.pipeline.num_ps_set_constant_buffers, 1); +} + +_SOKOL_PRIVATE bool _sg_d3d11_apply_bindings(_sg_bindings_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); + SOKOL_ASSERT(_sg.d3d11.ctx); + + // gather all the D3D11 resources into arrays + ID3D11Buffer* d3d11_ib = bnd->ib ? bnd->ib->d3d11.buf : 0; + ID3D11Buffer* d3d11_vbs[SG_MAX_VERTEX_BUFFERS] = {0}; + UINT d3d11_vb_offsets[SG_MAX_VERTEX_BUFFERS] = {0}; + ID3D11ShaderResourceView* d3d11_vs_srvs[_SG_D3D11_MAX_SHADERSTAGE_SRVS] = {0}; + ID3D11ShaderResourceView* d3d11_fs_srvs[_SG_D3D11_MAX_SHADERSTAGE_SRVS] = {0}; + ID3D11SamplerState* d3d11_vs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = {0}; + ID3D11SamplerState* d3d11_fs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = {0}; + for (int i = 0; i < bnd->num_vbs; i++) { + SOKOL_ASSERT(bnd->vbs[i]->d3d11.buf); + d3d11_vbs[i] = bnd->vbs[i]->d3d11.buf; + d3d11_vb_offsets[i] = (UINT)bnd->vb_offsets[i]; + } + for (int i = 0; i < bnd->num_vs_imgs; i++) { + SOKOL_ASSERT(bnd->vs_imgs[i]->d3d11.srv); + d3d11_vs_srvs[_SG_D3D11_SHADERSTAGE_IMAGE_SRV_OFFSET + i] = bnd->vs_imgs[i]->d3d11.srv; + } + for (int i = 0; i < bnd->num_vs_sbufs; i++) { + SOKOL_ASSERT(bnd->vs_sbufs[i]->d3d11.srv); + d3d11_vs_srvs[_SG_D3D11_SHADERSTAGE_BUFFER_SRV_OFFSET + i] = bnd->vs_sbufs[i]->d3d11.srv; + } + for (int i = 0; i < bnd->num_fs_imgs; i++) { + SOKOL_ASSERT(bnd->fs_imgs[i]->d3d11.srv); + d3d11_fs_srvs[_SG_D3D11_SHADERSTAGE_IMAGE_SRV_OFFSET + i] = bnd->fs_imgs[i]->d3d11.srv; + } + for (int i = 0; i < bnd->num_fs_sbufs; i++) { + SOKOL_ASSERT(bnd->fs_sbufs[i]->d3d11.srv); + d3d11_fs_srvs[_SG_D3D11_SHADERSTAGE_BUFFER_SRV_OFFSET + i] = bnd->fs_sbufs[i]->d3d11.srv; + } + for (int i = 0; i < bnd->num_vs_smps; i++) { + SOKOL_ASSERT(bnd->vs_smps[i]->d3d11.smp); + d3d11_vs_smps[i] = bnd->vs_smps[i]->d3d11.smp; + } + for (int i = 0; i < bnd->num_fs_smps; i++) { + SOKOL_ASSERT(bnd->fs_smps[i]->d3d11.smp); + d3d11_fs_smps[i] = bnd->fs_smps[i]->d3d11.smp; + } + _sg_d3d11_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_VERTEX_BUFFERS, d3d11_vbs, bnd->pip->d3d11.vb_strides, d3d11_vb_offsets); + _sg_d3d11_IASetIndexBuffer(_sg.d3d11.ctx, d3d11_ib, bnd->pip->d3d11.index_format, (UINT)bnd->ib_offset); + _sg_d3d11_VSSetShaderResources(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_SHADERSTAGE_SRVS, d3d11_vs_srvs); + _sg_d3d11_PSSetShaderResources(_sg.d3d11.ctx, 0, _SG_D3D11_MAX_SHADERSTAGE_SRVS, d3d11_fs_srvs); + _sg_d3d11_VSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_SAMPLERS, d3d11_vs_smps); + _sg_d3d11_PSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_SAMPLERS, d3d11_fs_smps); + _sg_stats_add(d3d11.bindings.num_ia_set_vertex_buffers, 1); + _sg_stats_add(d3d11.bindings.num_ia_set_index_buffer, 1); + _sg_stats_add(d3d11.bindings.num_vs_set_shader_resources, 1); + _sg_stats_add(d3d11.bindings.num_ps_set_shader_resources, 1); + _sg_stats_add(d3d11.bindings.num_vs_set_samplers, 1); + _sg_stats_add(d3d11.bindings.num_ps_set_samplers, 1); + return true; +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(_sg.d3d11.cur_pipeline && _sg.d3d11.cur_pipeline->slot.id == _sg.d3d11.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.d3d11.cur_pipeline->shader && _sg.d3d11.cur_pipeline->shader->slot.id == _sg.d3d11.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(ub_index < _sg.d3d11.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); + SOKOL_ASSERT(data->size == _sg.d3d11.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); + ID3D11Buffer* cb = _sg.d3d11.cur_pipeline->shader->d3d11.stage[stage_index].cbufs[ub_index]; + SOKOL_ASSERT(cb); + _sg_d3d11_UpdateSubresource(_sg.d3d11.ctx, (ID3D11Resource*)cb, 0, NULL, data->ptr, 0, 0); + _sg_stats_add(d3d11.uniforms.num_update_subresource, 1); +} + +_SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_instances) { + const bool use_instanced_draw = (num_instances > 1) || (_sg.d3d11.use_instanced_draw); + if (_sg.d3d11.use_indexed_draw) { + if (use_instanced_draw) { + _sg_d3d11_DrawIndexedInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0, 0); + _sg_stats_add(d3d11.draw.num_draw_indexed_instanced, 1); + } else { + _sg_d3d11_DrawIndexed(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element, 0); + _sg_stats_add(d3d11.draw.num_draw_indexed, 1); + } + } else { + if (use_instanced_draw) { + _sg_d3d11_DrawInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0); + _sg_stats_add(d3d11.draw.num_draw_instanced, 1); + } else { + _sg_d3d11_Draw(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element); + _sg_stats_add(d3d11.draw.num_draw, 1); + } + } +} + +_SOKOL_PRIVATE void _sg_d3d11_commit(void) { + // empty +} + +_SOKOL_PRIVATE void _sg_d3d11_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(buf->d3d11.buf); + D3D11_MAPPED_SUBRESOURCE d3d11_msr; + HRESULT hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); + _sg_stats_add(d3d11.num_map, 1); + if (SUCCEEDED(hr)) { + memcpy(d3d11_msr.pData, data->ptr, data->size); + _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); + _sg_stats_add(d3d11.num_unmap, 1); + } else { + _SG_ERROR(D3D11_MAP_FOR_UPDATE_BUFFER_FAILED); + } +} + +_SOKOL_PRIVATE void _sg_d3d11_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(buf->d3d11.buf); + D3D11_MAP map_type = new_frame ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE_NO_OVERWRITE; + D3D11_MAPPED_SUBRESOURCE d3d11_msr; + HRESULT hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, map_type, 0, &d3d11_msr); + _sg_stats_add(d3d11.num_map, 1); + if (SUCCEEDED(hr)) { + uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData + buf->cmn.append_pos; + memcpy(dst_ptr, data->ptr, data->size); + _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); + _sg_stats_add(d3d11.num_unmap, 1); + } else { + _SG_ERROR(D3D11_MAP_FOR_APPEND_BUFFER_FAILED); + } +} + +// see: https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-subresources +// also see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-d3d11calcsubresource +_SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(img->d3d11.res); + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices:1; + const int num_depth_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? img->cmn.num_slices:1; + UINT subres_index = 0; + HRESULT hr; + D3D11_MAPPED_SUBRESOURCE d3d11_msr; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int slice_index = 0; slice_index < num_slices; slice_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) { + SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + const int src_row_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + const int src_depth_pitch = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + const sg_range* subimg_data = &(data->subimage[face_index][mip_index]); + const size_t slice_size = subimg_data->size / (size_t)num_slices; + SOKOL_ASSERT(slice_size == (size_t)(src_depth_pitch * num_depth_slices)); + const size_t slice_offset = slice_size * (size_t)slice_index; + const uint8_t* slice_ptr = ((const uint8_t*)subimg_data->ptr) + slice_offset; + hr = _sg_d3d11_Map(_sg.d3d11.ctx, img->d3d11.res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); + _sg_stats_add(d3d11.num_map, 1); + if (SUCCEEDED(hr)) { + const uint8_t* src_ptr = slice_ptr; + uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData; + for (int depth_index = 0; depth_index < num_depth_slices; depth_index++) { + if (src_row_pitch == (int)d3d11_msr.RowPitch) { + const size_t copy_size = slice_size / (size_t)num_depth_slices; + SOKOL_ASSERT((copy_size * (size_t)num_depth_slices) == slice_size); + memcpy(dst_ptr, src_ptr, copy_size); + } else { + SOKOL_ASSERT(src_row_pitch < (int)d3d11_msr.RowPitch); + const uint8_t* src_row_ptr = src_ptr; + uint8_t* dst_row_ptr = dst_ptr; + for (int row_index = 0; row_index < mip_height; row_index++) { + memcpy(dst_row_ptr, src_row_ptr, (size_t)src_row_pitch); + src_row_ptr += src_row_pitch; + dst_row_ptr += d3d11_msr.RowPitch; + } + } + src_ptr += src_depth_pitch; + dst_ptr += d3d11_msr.DepthPitch; + } + _sg_d3d11_Unmap(_sg.d3d11.ctx, img->d3d11.res, subres_index); + _sg_stats_add(d3d11.num_unmap, 1); + } else { + _SG_ERROR(D3D11_MAP_FOR_UPDATE_IMAGE_FAILED); + } + } + } + } +} + +// ███ ███ ███████ ████████ █████ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ████ ██ █████ ██ ███████ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██ ███████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>metal backend +#elif defined(SOKOL_METAL) + +#if __has_feature(objc_arc) +#define _SG_OBJC_RETAIN(obj) { } +#define _SG_OBJC_RELEASE(obj) { obj = nil; } +#else +#define _SG_OBJC_RETAIN(obj) { [obj retain]; } +#define _SG_OBJC_RELEASE(obj) { [obj release]; obj = nil; } +#endif + +//-- enum translation functions ------------------------------------------------ +_SOKOL_PRIVATE MTLLoadAction _sg_mtl_load_action(sg_load_action a) { + switch (a) { + case SG_LOADACTION_CLEAR: return MTLLoadActionClear; + case SG_LOADACTION_LOAD: return MTLLoadActionLoad; + case SG_LOADACTION_DONTCARE: return MTLLoadActionDontCare; + default: SOKOL_UNREACHABLE; return (MTLLoadAction)0; + } +} + +_SOKOL_PRIVATE MTLStoreAction _sg_mtl_store_action(sg_store_action a, bool resolve) { + switch (a) { + case SG_STOREACTION_STORE: + if (resolve) { + return MTLStoreActionStoreAndMultisampleResolve; + } else { + return MTLStoreActionStore; + } + break; + case SG_STOREACTION_DONTCARE: + if (resolve) { + return MTLStoreActionMultisampleResolve; + } else { + return MTLStoreActionDontCare; + } + break; + default: SOKOL_UNREACHABLE; return (MTLStoreAction)0; + } +} + +_SOKOL_PRIVATE MTLResourceOptions _sg_mtl_resource_options_storage_mode_managed_or_shared(void) { + #if defined(_SG_TARGET_MACOS) + if (_sg.mtl.use_shared_storage_mode) { + return MTLResourceStorageModeShared; + } else { + return MTLResourceStorageModeManaged; + } + #else + // MTLResourceStorageModeManaged is not even defined on iOS SDK + return MTLResourceStorageModeShared; + #endif +} + +_SOKOL_PRIVATE MTLResourceOptions _sg_mtl_buffer_resource_options(sg_usage usg) { + switch (usg) { + case SG_USAGE_IMMUTABLE: + return _sg_mtl_resource_options_storage_mode_managed_or_shared(); + case SG_USAGE_DYNAMIC: + case SG_USAGE_STREAM: + return MTLResourceCPUCacheModeWriteCombined | _sg_mtl_resource_options_storage_mode_managed_or_shared(); + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE MTLVertexStepFunction _sg_mtl_step_function(sg_vertex_step step) { + switch (step) { + case SG_VERTEXSTEP_PER_VERTEX: return MTLVertexStepFunctionPerVertex; + case SG_VERTEXSTEP_PER_INSTANCE: return MTLVertexStepFunctionPerInstance; + default: SOKOL_UNREACHABLE; return (MTLVertexStepFunction)0; + } +} + +_SOKOL_PRIVATE MTLVertexFormat _sg_mtl_vertex_format(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return MTLVertexFormatFloat; + case SG_VERTEXFORMAT_FLOAT2: return MTLVertexFormatFloat2; + case SG_VERTEXFORMAT_FLOAT3: return MTLVertexFormatFloat3; + case SG_VERTEXFORMAT_FLOAT4: return MTLVertexFormatFloat4; + case SG_VERTEXFORMAT_BYTE4: return MTLVertexFormatChar4; + case SG_VERTEXFORMAT_BYTE4N: return MTLVertexFormatChar4Normalized; + case SG_VERTEXFORMAT_UBYTE4: return MTLVertexFormatUChar4; + case SG_VERTEXFORMAT_UBYTE4N: return MTLVertexFormatUChar4Normalized; + case SG_VERTEXFORMAT_SHORT2: return MTLVertexFormatShort2; + case SG_VERTEXFORMAT_SHORT2N: return MTLVertexFormatShort2Normalized; + case SG_VERTEXFORMAT_USHORT2N: return MTLVertexFormatUShort2Normalized; + case SG_VERTEXFORMAT_SHORT4: return MTLVertexFormatShort4; + case SG_VERTEXFORMAT_SHORT4N: return MTLVertexFormatShort4Normalized; + case SG_VERTEXFORMAT_USHORT4N: return MTLVertexFormatUShort4Normalized; + case SG_VERTEXFORMAT_UINT10_N2: return MTLVertexFormatUInt1010102Normalized; + case SG_VERTEXFORMAT_HALF2: return MTLVertexFormatHalf2; + case SG_VERTEXFORMAT_HALF4: return MTLVertexFormatHalf4; + default: SOKOL_UNREACHABLE; return (MTLVertexFormat)0; + } +} + +_SOKOL_PRIVATE MTLPrimitiveType _sg_mtl_primitive_type(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return MTLPrimitiveTypePoint; + case SG_PRIMITIVETYPE_LINES: return MTLPrimitiveTypeLine; + case SG_PRIMITIVETYPE_LINE_STRIP: return MTLPrimitiveTypeLineStrip; + case SG_PRIMITIVETYPE_TRIANGLES: return MTLPrimitiveTypeTriangle; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return MTLPrimitiveTypeTriangleStrip; + default: SOKOL_UNREACHABLE; return (MTLPrimitiveType)0; + } +} + +_SOKOL_PRIVATE MTLPixelFormat _sg_mtl_pixel_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: return MTLPixelFormatR8Unorm; + case SG_PIXELFORMAT_R8SN: return MTLPixelFormatR8Snorm; + case SG_PIXELFORMAT_R8UI: return MTLPixelFormatR8Uint; + case SG_PIXELFORMAT_R8SI: return MTLPixelFormatR8Sint; + case SG_PIXELFORMAT_R16: return MTLPixelFormatR16Unorm; + case SG_PIXELFORMAT_R16SN: return MTLPixelFormatR16Snorm; + case SG_PIXELFORMAT_R16UI: return MTLPixelFormatR16Uint; + case SG_PIXELFORMAT_R16SI: return MTLPixelFormatR16Sint; + case SG_PIXELFORMAT_R16F: return MTLPixelFormatR16Float; + case SG_PIXELFORMAT_RG8: return MTLPixelFormatRG8Unorm; + case SG_PIXELFORMAT_RG8SN: return MTLPixelFormatRG8Snorm; + case SG_PIXELFORMAT_RG8UI: return MTLPixelFormatRG8Uint; + case SG_PIXELFORMAT_RG8SI: return MTLPixelFormatRG8Sint; + case SG_PIXELFORMAT_R32UI: return MTLPixelFormatR32Uint; + case SG_PIXELFORMAT_R32SI: return MTLPixelFormatR32Sint; + case SG_PIXELFORMAT_R32F: return MTLPixelFormatR32Float; + case SG_PIXELFORMAT_RG16: return MTLPixelFormatRG16Unorm; + case SG_PIXELFORMAT_RG16SN: return MTLPixelFormatRG16Snorm; + case SG_PIXELFORMAT_RG16UI: return MTLPixelFormatRG16Uint; + case SG_PIXELFORMAT_RG16SI: return MTLPixelFormatRG16Sint; + case SG_PIXELFORMAT_RG16F: return MTLPixelFormatRG16Float; + case SG_PIXELFORMAT_RGBA8: return MTLPixelFormatRGBA8Unorm; + case SG_PIXELFORMAT_SRGB8A8: return MTLPixelFormatRGBA8Unorm_sRGB; + case SG_PIXELFORMAT_RGBA8SN: return MTLPixelFormatRGBA8Snorm; + case SG_PIXELFORMAT_RGBA8UI: return MTLPixelFormatRGBA8Uint; + case SG_PIXELFORMAT_RGBA8SI: return MTLPixelFormatRGBA8Sint; + case SG_PIXELFORMAT_BGRA8: return MTLPixelFormatBGRA8Unorm; + case SG_PIXELFORMAT_RGB10A2: return MTLPixelFormatRGB10A2Unorm; + case SG_PIXELFORMAT_RG11B10F: return MTLPixelFormatRG11B10Float; + case SG_PIXELFORMAT_RGB9E5: return MTLPixelFormatRGB9E5Float; + case SG_PIXELFORMAT_RG32UI: return MTLPixelFormatRG32Uint; + case SG_PIXELFORMAT_RG32SI: return MTLPixelFormatRG32Sint; + case SG_PIXELFORMAT_RG32F: return MTLPixelFormatRG32Float; + case SG_PIXELFORMAT_RGBA16: return MTLPixelFormatRGBA16Unorm; + case SG_PIXELFORMAT_RGBA16SN: return MTLPixelFormatRGBA16Snorm; + case SG_PIXELFORMAT_RGBA16UI: return MTLPixelFormatRGBA16Uint; + case SG_PIXELFORMAT_RGBA16SI: return MTLPixelFormatRGBA16Sint; + case SG_PIXELFORMAT_RGBA16F: return MTLPixelFormatRGBA16Float; + case SG_PIXELFORMAT_RGBA32UI: return MTLPixelFormatRGBA32Uint; + case SG_PIXELFORMAT_RGBA32SI: return MTLPixelFormatRGBA32Sint; + case SG_PIXELFORMAT_RGBA32F: return MTLPixelFormatRGBA32Float; + case SG_PIXELFORMAT_DEPTH: return MTLPixelFormatDepth32Float; + case SG_PIXELFORMAT_DEPTH_STENCIL: return MTLPixelFormatDepth32Float_Stencil8; + #if defined(_SG_TARGET_MACOS) + case SG_PIXELFORMAT_BC1_RGBA: return MTLPixelFormatBC1_RGBA; + case SG_PIXELFORMAT_BC2_RGBA: return MTLPixelFormatBC2_RGBA; + case SG_PIXELFORMAT_BC3_RGBA: return MTLPixelFormatBC3_RGBA; + case SG_PIXELFORMAT_BC3_SRGBA: return MTLPixelFormatBC3_RGBA_sRGB; + case SG_PIXELFORMAT_BC4_R: return MTLPixelFormatBC4_RUnorm; + case SG_PIXELFORMAT_BC4_RSN: return MTLPixelFormatBC4_RSnorm; + case SG_PIXELFORMAT_BC5_RG: return MTLPixelFormatBC5_RGUnorm; + case SG_PIXELFORMAT_BC5_RGSN: return MTLPixelFormatBC5_RGSnorm; + case SG_PIXELFORMAT_BC6H_RGBF: return MTLPixelFormatBC6H_RGBFloat; + case SG_PIXELFORMAT_BC6H_RGBUF: return MTLPixelFormatBC6H_RGBUfloat; + case SG_PIXELFORMAT_BC7_RGBA: return MTLPixelFormatBC7_RGBAUnorm; + case SG_PIXELFORMAT_BC7_SRGBA: return MTLPixelFormatBC7_RGBAUnorm_sRGB; + #else + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: return MTLPixelFormatPVRTC_RGB_2BPP; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: return MTLPixelFormatPVRTC_RGB_4BPP; + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: return MTLPixelFormatPVRTC_RGBA_2BPP; + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: return MTLPixelFormatPVRTC_RGBA_4BPP; + case SG_PIXELFORMAT_ETC2_RGB8: return MTLPixelFormatETC2_RGB8; + case SG_PIXELFORMAT_ETC2_SRGB8: return MTLPixelFormatETC2_RGB8_sRGB; + case SG_PIXELFORMAT_ETC2_RGB8A1: return MTLPixelFormatETC2_RGB8A1; + case SG_PIXELFORMAT_ETC2_RGBA8: return MTLPixelFormatEAC_RGBA8; + case SG_PIXELFORMAT_ETC2_SRGB8A8: return MTLPixelFormatEAC_RGBA8_sRGB; + case SG_PIXELFORMAT_EAC_R11: return MTLPixelFormatEAC_R11Unorm; + case SG_PIXELFORMAT_EAC_R11SN: return MTLPixelFormatEAC_R11Snorm; + case SG_PIXELFORMAT_EAC_RG11: return MTLPixelFormatEAC_RG11Unorm; + case SG_PIXELFORMAT_EAC_RG11SN: return MTLPixelFormatEAC_RG11Snorm; + case SG_PIXELFORMAT_ASTC_4x4_RGBA: return MTLPixelFormatASTC_4x4_LDR; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return MTLPixelFormatASTC_4x4_sRGB; + #endif + default: return MTLPixelFormatInvalid; + } +} + +_SOKOL_PRIVATE MTLColorWriteMask _sg_mtl_color_write_mask(sg_color_mask m) { + MTLColorWriteMask mtl_mask = MTLColorWriteMaskNone; + if (m & SG_COLORMASK_R) { + mtl_mask |= MTLColorWriteMaskRed; + } + if (m & SG_COLORMASK_G) { + mtl_mask |= MTLColorWriteMaskGreen; + } + if (m & SG_COLORMASK_B) { + mtl_mask |= MTLColorWriteMaskBlue; + } + if (m & SG_COLORMASK_A) { + mtl_mask |= MTLColorWriteMaskAlpha; + } + return mtl_mask; +} + +_SOKOL_PRIVATE MTLBlendOperation _sg_mtl_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return MTLBlendOperationAdd; + case SG_BLENDOP_SUBTRACT: return MTLBlendOperationSubtract; + case SG_BLENDOP_REVERSE_SUBTRACT: return MTLBlendOperationReverseSubtract; + default: SOKOL_UNREACHABLE; return (MTLBlendOperation)0; + } +} + +_SOKOL_PRIVATE MTLBlendFactor _sg_mtl_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return MTLBlendFactorZero; + case SG_BLENDFACTOR_ONE: return MTLBlendFactorOne; + case SG_BLENDFACTOR_SRC_COLOR: return MTLBlendFactorSourceColor; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return MTLBlendFactorOneMinusSourceColor; + case SG_BLENDFACTOR_SRC_ALPHA: return MTLBlendFactorSourceAlpha; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return MTLBlendFactorOneMinusSourceAlpha; + case SG_BLENDFACTOR_DST_COLOR: return MTLBlendFactorDestinationColor; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return MTLBlendFactorOneMinusDestinationColor; + case SG_BLENDFACTOR_DST_ALPHA: return MTLBlendFactorDestinationAlpha; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return MTLBlendFactorOneMinusDestinationAlpha; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return MTLBlendFactorSourceAlphaSaturated; + case SG_BLENDFACTOR_BLEND_COLOR: return MTLBlendFactorBlendColor; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return MTLBlendFactorOneMinusBlendColor; + case SG_BLENDFACTOR_BLEND_ALPHA: return MTLBlendFactorBlendAlpha; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return MTLBlendFactorOneMinusBlendAlpha; + default: SOKOL_UNREACHABLE; return (MTLBlendFactor)0; + } +} + +_SOKOL_PRIVATE MTLCompareFunction _sg_mtl_compare_func(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return MTLCompareFunctionNever; + case SG_COMPAREFUNC_LESS: return MTLCompareFunctionLess; + case SG_COMPAREFUNC_EQUAL: return MTLCompareFunctionEqual; + case SG_COMPAREFUNC_LESS_EQUAL: return MTLCompareFunctionLessEqual; + case SG_COMPAREFUNC_GREATER: return MTLCompareFunctionGreater; + case SG_COMPAREFUNC_NOT_EQUAL: return MTLCompareFunctionNotEqual; + case SG_COMPAREFUNC_GREATER_EQUAL: return MTLCompareFunctionGreaterEqual; + case SG_COMPAREFUNC_ALWAYS: return MTLCompareFunctionAlways; + default: SOKOL_UNREACHABLE; return (MTLCompareFunction)0; + } +} + +_SOKOL_PRIVATE MTLStencilOperation _sg_mtl_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return MTLStencilOperationKeep; + case SG_STENCILOP_ZERO: return MTLStencilOperationZero; + case SG_STENCILOP_REPLACE: return MTLStencilOperationReplace; + case SG_STENCILOP_INCR_CLAMP: return MTLStencilOperationIncrementClamp; + case SG_STENCILOP_DECR_CLAMP: return MTLStencilOperationDecrementClamp; + case SG_STENCILOP_INVERT: return MTLStencilOperationInvert; + case SG_STENCILOP_INCR_WRAP: return MTLStencilOperationIncrementWrap; + case SG_STENCILOP_DECR_WRAP: return MTLStencilOperationDecrementWrap; + default: SOKOL_UNREACHABLE; return (MTLStencilOperation)0; + } +} + +_SOKOL_PRIVATE MTLCullMode _sg_mtl_cull_mode(sg_cull_mode m) { + switch (m) { + case SG_CULLMODE_NONE: return MTLCullModeNone; + case SG_CULLMODE_FRONT: return MTLCullModeFront; + case SG_CULLMODE_BACK: return MTLCullModeBack; + default: SOKOL_UNREACHABLE; return (MTLCullMode)0; + } +} + +_SOKOL_PRIVATE MTLWinding _sg_mtl_winding(sg_face_winding w) { + switch (w) { + case SG_FACEWINDING_CW: return MTLWindingClockwise; + case SG_FACEWINDING_CCW: return MTLWindingCounterClockwise; + default: SOKOL_UNREACHABLE; return (MTLWinding)0; + } +} + +_SOKOL_PRIVATE MTLIndexType _sg_mtl_index_type(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_UINT16: return MTLIndexTypeUInt16; + case SG_INDEXTYPE_UINT32: return MTLIndexTypeUInt32; + default: SOKOL_UNREACHABLE; return (MTLIndexType)0; + } +} + +_SOKOL_PRIVATE int _sg_mtl_index_size(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_NONE: return 0; + case SG_INDEXTYPE_UINT16: return 2; + case SG_INDEXTYPE_UINT32: return 4; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE MTLTextureType _sg_mtl_texture_type(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return MTLTextureType2D; + case SG_IMAGETYPE_CUBE: return MTLTextureTypeCube; + case SG_IMAGETYPE_3D: return MTLTextureType3D; + case SG_IMAGETYPE_ARRAY: return MTLTextureType2DArray; + default: SOKOL_UNREACHABLE; return (MTLTextureType)0; + } +} + +_SOKOL_PRIVATE bool _sg_mtl_is_pvrtc(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + return true; + default: + return false; + } +} + +_SOKOL_PRIVATE MTLSamplerAddressMode _sg_mtl_address_mode(sg_wrap w) { + if (_sg.features.image_clamp_to_border) { + if (@available(macOS 12.0, iOS 14.0, *)) { + // border color feature available + switch (w) { + case SG_WRAP_REPEAT: return MTLSamplerAddressModeRepeat; + case SG_WRAP_CLAMP_TO_EDGE: return MTLSamplerAddressModeClampToEdge; + case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToBorderColor; + case SG_WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat; + default: SOKOL_UNREACHABLE; return (MTLSamplerAddressMode)0; + } + } + } + // fallthrough: clamp to border no supported + switch (w) { + case SG_WRAP_REPEAT: return MTLSamplerAddressModeRepeat; + case SG_WRAP_CLAMP_TO_EDGE: return MTLSamplerAddressModeClampToEdge; + case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToEdge; + case SG_WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat; + default: SOKOL_UNREACHABLE; return (MTLSamplerAddressMode)0; + } +} + +_SOKOL_PRIVATE API_AVAILABLE(ios(14.0), macos(12.0)) MTLSamplerBorderColor _sg_mtl_border_color(sg_border_color c) { + switch (c) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: return MTLSamplerBorderColorTransparentBlack; + case SG_BORDERCOLOR_OPAQUE_BLACK: return MTLSamplerBorderColorOpaqueBlack; + case SG_BORDERCOLOR_OPAQUE_WHITE: return MTLSamplerBorderColorOpaqueWhite; + default: SOKOL_UNREACHABLE; return (MTLSamplerBorderColor)0; + } +} + +_SOKOL_PRIVATE MTLSamplerMinMagFilter _sg_mtl_minmag_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + return MTLSamplerMinMagFilterNearest; + case SG_FILTER_LINEAR: + return MTLSamplerMinMagFilterLinear; + default: + SOKOL_UNREACHABLE; return (MTLSamplerMinMagFilter)0; + } +} + +_SOKOL_PRIVATE MTLSamplerMipFilter _sg_mtl_mipmap_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NONE: + return MTLSamplerMipFilterNotMipmapped; + case SG_FILTER_NEAREST: + return MTLSamplerMipFilterNearest; + case SG_FILTER_LINEAR: + return MTLSamplerMipFilterLinear; + default: + SOKOL_UNREACHABLE; return (MTLSamplerMipFilter)0; + } +} + +//-- a pool for all Metal resource objects, with deferred release queue --------- +_SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) { + _sg.mtl.idpool.num_slots = 2 * + ( + 2 * desc->buffer_pool_size + + 4 * desc->image_pool_size + + 1 * desc->sampler_pool_size + + 4 * desc->shader_pool_size + + 2 * desc->pipeline_pool_size + + desc->attachments_pool_size + + 128 + ); + _sg.mtl.idpool.pool = [NSMutableArray arrayWithCapacity:(NSUInteger)_sg.mtl.idpool.num_slots]; + _SG_OBJC_RETAIN(_sg.mtl.idpool.pool); + NSNull* null = [NSNull null]; + for (int i = 0; i < _sg.mtl.idpool.num_slots; i++) { + [_sg.mtl.idpool.pool addObject:null]; + } + SOKOL_ASSERT([_sg.mtl.idpool.pool count] == (NSUInteger)_sg.mtl.idpool.num_slots); + // a queue of currently free slot indices + _sg.mtl.idpool.free_queue_top = 0; + _sg.mtl.idpool.free_queue = (int*)_sg_malloc_clear((size_t)_sg.mtl.idpool.num_slots * sizeof(int)); + // pool slot 0 is reserved! + for (int i = _sg.mtl.idpool.num_slots-1; i >= 1; i--) { + _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = i; + } + // a circular queue which holds release items (frame index when a resource is to be released, and the resource's pool index + _sg.mtl.idpool.release_queue_front = 0; + _sg.mtl.idpool.release_queue_back = 0; + _sg.mtl.idpool.release_queue = (_sg_mtl_release_item_t*)_sg_malloc_clear((size_t)_sg.mtl.idpool.num_slots * sizeof(_sg_mtl_release_item_t)); + for (int i = 0; i < _sg.mtl.idpool.num_slots; i++) { + _sg.mtl.idpool.release_queue[i].frame_index = 0; + _sg.mtl.idpool.release_queue[i].slot_index = _SG_MTL_INVALID_SLOT_INDEX; + } +} + +_SOKOL_PRIVATE void _sg_mtl_destroy_pool(void) { + _sg_free(_sg.mtl.idpool.release_queue); _sg.mtl.idpool.release_queue = 0; + _sg_free(_sg.mtl.idpool.free_queue); _sg.mtl.idpool.free_queue = 0; + _SG_OBJC_RELEASE(_sg.mtl.idpool.pool); +} + +// get a new free resource pool slot +_SOKOL_PRIVATE int _sg_mtl_alloc_pool_slot(void) { + SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top > 0); + const int slot_index = _sg.mtl.idpool.free_queue[--_sg.mtl.idpool.free_queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + return slot_index; +} + +// put a free resource pool slot back into the free-queue +_SOKOL_PRIVATE void _sg_mtl_free_pool_slot(int slot_index) { + SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top < _sg.mtl.idpool.num_slots); + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = slot_index; +} + +// add an MTLResource to the pool, return pool index or 0 if input was 'nil' +_SOKOL_PRIVATE int _sg_mtl_add_resource(id res) { + if (nil == res) { + return _SG_MTL_INVALID_SLOT_INDEX; + } + _sg_stats_add(metal.idpool.num_added, 1); + const int slot_index = _sg_mtl_alloc_pool_slot(); + // NOTE: the NSMutableArray will take ownership of its items + SOKOL_ASSERT([NSNull null] == _sg.mtl.idpool.pool[(NSUInteger)slot_index]); + _sg.mtl.idpool.pool[(NSUInteger)slot_index] = res; + return slot_index; +} + +/* mark an MTLResource for release, this will put the resource into the + deferred-release queue, and the resource will then be released N frames later, + the special pool index 0 will be ignored (this means that a nil + value was provided to _sg_mtl_add_resource() +*/ +_SOKOL_PRIVATE void _sg_mtl_release_resource(uint32_t frame_index, int slot_index) { + if (slot_index == _SG_MTL_INVALID_SLOT_INDEX) { + return; + } + _sg_stats_add(metal.idpool.num_released, 1); + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + SOKOL_ASSERT([NSNull null] != _sg.mtl.idpool.pool[(NSUInteger)slot_index]); + int release_index = _sg.mtl.idpool.release_queue_front++; + if (_sg.mtl.idpool.release_queue_front >= _sg.mtl.idpool.num_slots) { + // wrap-around + _sg.mtl.idpool.release_queue_front = 0; + } + // release queue full? + SOKOL_ASSERT(_sg.mtl.idpool.release_queue_front != _sg.mtl.idpool.release_queue_back); + SOKOL_ASSERT(0 == _sg.mtl.idpool.release_queue[release_index].frame_index); + const uint32_t safe_to_release_frame_index = frame_index + SG_NUM_INFLIGHT_FRAMES + 1; + _sg.mtl.idpool.release_queue[release_index].frame_index = safe_to_release_frame_index; + _sg.mtl.idpool.release_queue[release_index].slot_index = slot_index; +} + +// run garbage-collection pass on all resources in the release-queue +_SOKOL_PRIVATE void _sg_mtl_garbage_collect(uint32_t frame_index) { + while (_sg.mtl.idpool.release_queue_back != _sg.mtl.idpool.release_queue_front) { + if (frame_index < _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index) { + // don't need to check further, release-items past this are too young + break; + } + _sg_stats_add(metal.idpool.num_garbage_collected, 1); + // safe to release this resource + const int slot_index = _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index; + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + // note: the NSMutableArray takes ownership of its items, assigning an NSNull object will + // release the object, no matter if using ARC or not + SOKOL_ASSERT(_sg.mtl.idpool.pool[(NSUInteger)slot_index] != [NSNull null]); + _sg.mtl.idpool.pool[(NSUInteger)slot_index] = [NSNull null]; + // put the now free pool index back on the free queue + _sg_mtl_free_pool_slot(slot_index); + // reset the release queue slot and advance the back index + _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index = 0; + _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index = _SG_MTL_INVALID_SLOT_INDEX; + _sg.mtl.idpool.release_queue_back++; + if (_sg.mtl.idpool.release_queue_back >= _sg.mtl.idpool.num_slots) { + // wrap-around + _sg.mtl.idpool.release_queue_back = 0; + } + } +} + +_SOKOL_PRIVATE id _sg_mtl_id(int slot_index) { + return _sg.mtl.idpool.pool[(NSUInteger)slot_index]; +} + +_SOKOL_PRIVATE void _sg_mtl_clear_state_cache(void) { + _sg_clear(&_sg.mtl.state_cache, sizeof(_sg.mtl.state_cache)); +} + +// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf +_SOKOL_PRIVATE void _sg_mtl_init_caps(void) { + #if defined(_SG_TARGET_MACOS) + _sg.backend = SG_BACKEND_METAL_MACOS; + #elif defined(_SG_TARGET_IOS) + #if defined(_SG_TARGET_IOS_SIMULATOR) + _sg.backend = SG_BACKEND_METAL_SIMULATOR; + #else + _sg.backend = SG_BACKEND_METAL_IOS; + #endif + #endif + _sg.features.origin_top_left = true; + _sg.features.mrt_independent_blend_state = true; + _sg.features.mrt_independent_write_mask = true; + _sg.features.storage_buffer = true; + + _sg.features.image_clamp_to_border = false; + #if (MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) || (__IPHONE_OS_VERSION_MAX_ALLOWED >= 140000) + if (@available(macOS 12.0, iOS 14.0, *)) { + _sg.features.image_clamp_to_border = [_sg.mtl.device supportsFamily:MTLGPUFamilyApple7] + || [_sg.mtl.device supportsFamily:MTLGPUFamilyMac2]; + #if (MAC_OS_X_VERSION_MAX_ALLOWED >= 130000) || (__IPHONE_OS_VERSION_MAX_ALLOWED >= 160000) + if (!_sg.features.image_clamp_to_border) { + if (@available(macOS 13.0, iOS 16.0, *)) { + _sg.features.image_clamp_to_border = [_sg.mtl.device supportsFamily:MTLGPUFamilyMetal3]; + } + } + #endif + } + #endif + + #if defined(_SG_TARGET_MACOS) + _sg.limits.max_image_size_2d = 16 * 1024; + _sg.limits.max_image_size_cube = 16 * 1024; + _sg.limits.max_image_size_3d = 2 * 1024; + _sg.limits.max_image_size_array = 16 * 1024; + _sg.limits.max_image_array_layers = 2 * 1024; + #else + // FIXME: newer iOS devices support 16k textures + _sg.limits.max_image_size_2d = 8 * 1024; + _sg.limits.max_image_size_cube = 8 * 1024; + _sg.limits.max_image_size_3d = 2 * 1024; + _sg.limits.max_image_size_array = 8 * 1024; + _sg.limits.max_image_array_layers = 2 * 1024; + #endif + _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; + + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16SN]); + #else + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_R16]); + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_R16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R32F]); + #else + _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_R32F]); + #endif + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]); + #else + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RG16]); + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RG16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_SRGB8A8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGB9E5]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + #else + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB9E5]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + #endif + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); + #else + _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RG32F]); + #endif + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); + #else + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RGBA16]); + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + #else + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + #endif + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_SRGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_SRGBA]); + #else + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_2BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_4BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_2BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_4BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_SRGBA]); + + #endif +} + +//-- main Metal backend state and functions ------------------------------------ +_SOKOL_PRIVATE void _sg_mtl_setup_backend(const sg_desc* desc) { + // assume already zero-initialized + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->environment.metal.device); + SOKOL_ASSERT(desc->uniform_buffer_size > 0); + _sg_mtl_init_pool(desc); + _sg_mtl_clear_state_cache(); + _sg.mtl.valid = true; + _sg.mtl.ub_size = desc->uniform_buffer_size; + _sg.mtl.sem = dispatch_semaphore_create(SG_NUM_INFLIGHT_FRAMES); + _sg.mtl.device = (__bridge id) desc->environment.metal.device; + _sg.mtl.cmd_queue = [_sg.mtl.device newCommandQueue]; + + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + _sg.mtl.uniform_buffers[i] = [_sg.mtl.device + newBufferWithLength:(NSUInteger)_sg.mtl.ub_size + options:MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeShared + ]; + #if defined(SOKOL_DEBUG) + _sg.mtl.uniform_buffers[i].label = [NSString stringWithFormat:@"sg-uniform-buffer.%d", i]; + #endif + } + + if (desc->mtl_force_managed_storage_mode) { + _sg.mtl.use_shared_storage_mode = false; + } else if (@available(macOS 10.15, iOS 13.0, *)) { + // on Intel Macs, always use managed resources even though the + // device says it supports unified memory (because of texture restrictions) + const bool is_apple_gpu = [_sg.mtl.device supportsFamily:MTLGPUFamilyApple1]; + if (!is_apple_gpu) { + _sg.mtl.use_shared_storage_mode = false; + } else { + _sg.mtl.use_shared_storage_mode = true; + } + } else { + #if defined(_SG_TARGET_MACOS) + _sg.mtl.use_shared_storage_mode = false; + #else + _sg.mtl.use_shared_storage_mode = true; + #endif + } + _sg_mtl_init_caps(); +} + +_SOKOL_PRIVATE void _sg_mtl_discard_backend(void) { + SOKOL_ASSERT(_sg.mtl.valid); + // wait for the last frame to finish + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER); + } + // semaphore must be "relinquished" before destruction + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + dispatch_semaphore_signal(_sg.mtl.sem); + } + _sg_mtl_garbage_collect(_sg.frame_index + SG_NUM_INFLIGHT_FRAMES + 2); + _sg_mtl_destroy_pool(); + _sg.mtl.valid = false; + + _SG_OBJC_RELEASE(_sg.mtl.sem); + _SG_OBJC_RELEASE(_sg.mtl.device); + _SG_OBJC_RELEASE(_sg.mtl.cmd_queue); + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + _SG_OBJC_RELEASE(_sg.mtl.uniform_buffers[i]); + } + // NOTE: MTLCommandBuffer and MTLRenderCommandEncoder are auto-released + _sg.mtl.cmd_buffer = nil; + _sg.mtl.cmd_encoder = nil; +} + +_SOKOL_PRIVATE void _sg_mtl_bind_uniform_buffers(void) { + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + for (int slot = 0; slot < SG_MAX_SHADERSTAGE_UBS; slot++) { + [_sg.mtl.cmd_encoder + setVertexBuffer:_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] + offset:0 + atIndex:(NSUInteger)slot]; + [_sg.mtl.cmd_encoder + setFragmentBuffer:_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] + offset:0 + atIndex:(NSUInteger)slot]; + } +} + +_SOKOL_PRIVATE void _sg_mtl_reset_state_cache(void) { + _sg_mtl_clear_state_cache(); + // need to restore the uniform buffer binding (normally happens in _sg_mtl_begin_pass() + if (nil != _sg.mtl.cmd_encoder) { + _sg_mtl_bind_uniform_buffers(); + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + const bool injected = (0 != desc->mtl_buffers[0]); + MTLResourceOptions mtl_options = _sg_mtl_buffer_resource_options(buf->cmn.usage); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + id mtl_buf; + if (injected) { + SOKOL_ASSERT(desc->mtl_buffers[slot]); + mtl_buf = (__bridge id) desc->mtl_buffers[slot]; + } else { + if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { + SOKOL_ASSERT(desc->data.ptr); + mtl_buf = [_sg.mtl.device newBufferWithBytes:desc->data.ptr length:(NSUInteger)buf->cmn.size options:mtl_options]; + } else { + mtl_buf = [_sg.mtl.device newBufferWithLength:(NSUInteger)buf->cmn.size options:mtl_options]; + } + if (nil == mtl_buf) { + _SG_ERROR(METAL_CREATE_BUFFER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + #if defined(SOKOL_DEBUG) + if (desc->label) { + mtl_buf.label = [NSString stringWithFormat:@"%s.%d", desc->label, slot]; + } + #endif + buf->mtl.buf[slot] = _sg_mtl_add_resource(mtl_buf); + _SG_OBJC_RELEASE(mtl_buf); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_discard_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + // it's valid to call release resource with '0' + _sg_mtl_release_resource(_sg.frame_index, buf->mtl.buf[slot]); + } +} + +_SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unretained id mtl_tex, const sg_image_data* data) { + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices : 1; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { + SOKOL_ASSERT(data->subimage[face_index][mip_index].ptr); + SOKOL_ASSERT(data->subimage[face_index][mip_index].size > 0); + const uint8_t* data_ptr = (const uint8_t*)data->subimage[face_index][mip_index].ptr; + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + // special case PVRTC formats: bytePerRow and bytesPerImage must be 0 + int bytes_per_row = 0; + int bytes_per_slice = 0; + if (!_sg_mtl_is_pvrtc(img->cmn.pixel_format)) { + bytes_per_row = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + } + /* bytesPerImage special case: https://developer.apple.com/documentation/metal/mtltexture/1515679-replaceregion + + "Supply a nonzero value only when you copy data to a MTLTextureType3D type texture" + */ + MTLRegion region; + int bytes_per_image; + if (img->cmn.type == SG_IMAGETYPE_3D) { + const int mip_depth = _sg_miplevel_dim(img->cmn.num_slices, mip_index); + region = MTLRegionMake3D(0, 0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height, (NSUInteger)mip_depth); + bytes_per_image = bytes_per_slice; + // FIXME: apparently the minimal bytes_per_image size for 3D texture is 4 KByte... somehow need to handle this + } else { + region = MTLRegionMake2D(0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height); + bytes_per_image = 0; + } + + for (int slice_index = 0; slice_index < num_slices; slice_index++) { + const int mtl_slice_index = (img->cmn.type == SG_IMAGETYPE_CUBE) ? face_index : slice_index; + const int slice_offset = slice_index * bytes_per_slice; + SOKOL_ASSERT((slice_offset + bytes_per_slice) <= (int)data->subimage[face_index][mip_index].size); + [mtl_tex replaceRegion:region + mipmapLevel:(NSUInteger)mip_index + slice:(NSUInteger)mtl_slice_index + withBytes:data_ptr + slice_offset + bytesPerRow:(NSUInteger)bytes_per_row + bytesPerImage:(NSUInteger)bytes_per_image]; + } + } + } +} + +// initialize MTLTextureDescriptor with common attributes +_SOKOL_PRIVATE bool _sg_mtl_init_texdesc_common(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { + mtl_desc.textureType = _sg_mtl_texture_type(img->cmn.type); + mtl_desc.pixelFormat = _sg_mtl_pixel_format(img->cmn.pixel_format); + if (MTLPixelFormatInvalid == mtl_desc.pixelFormat) { + _SG_ERROR(METAL_TEXTURE_FORMAT_NOT_SUPPORTED); + return false; + } + mtl_desc.width = (NSUInteger)img->cmn.width; + mtl_desc.height = (NSUInteger)img->cmn.height; + if (SG_IMAGETYPE_3D == img->cmn.type) { + mtl_desc.depth = (NSUInteger)img->cmn.num_slices; + } else { + mtl_desc.depth = 1; + } + mtl_desc.mipmapLevelCount = (NSUInteger)img->cmn.num_mipmaps; + if (SG_IMAGETYPE_ARRAY == img->cmn.type) { + mtl_desc.arrayLength = (NSUInteger)img->cmn.num_slices; + } else { + mtl_desc.arrayLength = 1; + } + mtl_desc.usage = MTLTextureUsageShaderRead; + MTLResourceOptions res_options = 0; + if (img->cmn.usage != SG_USAGE_IMMUTABLE) { + res_options |= MTLResourceCPUCacheModeWriteCombined; + } + res_options |= _sg_mtl_resource_options_storage_mode_managed_or_shared(); + mtl_desc.resourceOptions = res_options; + return true; +} + +// initialize MTLTextureDescriptor with rendertarget attributes +_SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { + SOKOL_ASSERT(img->cmn.render_target); + _SOKOL_UNUSED(img); + mtl_desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget; + mtl_desc.resourceOptions = MTLResourceStorageModePrivate; +} + +// initialize MTLTextureDescriptor with MSAA attributes +_SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt_msaa(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { + SOKOL_ASSERT(img->cmn.sample_count > 1); + mtl_desc.usage = MTLTextureUsageRenderTarget; + mtl_desc.resourceOptions = MTLResourceStorageModePrivate; + mtl_desc.textureType = MTLTextureType2DMultisample; + mtl_desc.sampleCount = (NSUInteger)img->cmn.sample_count; +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + const bool injected = (0 != desc->mtl_textures[0]); + + // first initialize all Metal resource pool slots to 'empty' + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + img->mtl.tex[i] = _sg_mtl_add_resource(nil); + } + + // initialize a Metal texture descriptor + MTLTextureDescriptor* mtl_desc = [[MTLTextureDescriptor alloc] init]; + if (!_sg_mtl_init_texdesc_common(mtl_desc, img)) { + _SG_OBJC_RELEASE(mtl_desc); + return SG_RESOURCESTATE_FAILED; + } + if (img->cmn.render_target) { + if (img->cmn.sample_count > 1) { + _sg_mtl_init_texdesc_rt_msaa(mtl_desc, img); + } else { + _sg_mtl_init_texdesc_rt(mtl_desc, img); + } + } + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + id mtl_tex; + if (injected) { + SOKOL_ASSERT(desc->mtl_textures[slot]); + mtl_tex = (__bridge id) desc->mtl_textures[slot]; + } else { + mtl_tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; + if (nil == mtl_tex) { + _SG_OBJC_RELEASE(mtl_desc); + _SG_ERROR(METAL_CREATE_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + if ((img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { + _sg_mtl_copy_image_data(img, mtl_tex, &desc->data); + } + } + #if defined(SOKOL_DEBUG) + if (desc->label) { + mtl_tex.label = [NSString stringWithFormat:@"%s.%d", desc->label, slot]; + } + #endif + img->mtl.tex[slot] = _sg_mtl_add_resource(mtl_tex); + _SG_OBJC_RELEASE(mtl_tex); + } + _SG_OBJC_RELEASE(mtl_desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_discard_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + // it's valid to call release resource with a 'null resource' + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + _sg_mtl_release_resource(_sg.frame_index, img->mtl.tex[slot]); + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + id mtl_smp; + const bool injected = (0 != desc->mtl_sampler); + if (injected) { + SOKOL_ASSERT(desc->mtl_sampler); + mtl_smp = (__bridge id) desc->mtl_sampler; + } else { + MTLSamplerDescriptor* mtl_desc = [[MTLSamplerDescriptor alloc] init]; + mtl_desc.sAddressMode = _sg_mtl_address_mode(desc->wrap_u); + mtl_desc.tAddressMode = _sg_mtl_address_mode(desc->wrap_v); + mtl_desc.rAddressMode = _sg_mtl_address_mode(desc->wrap_w); + if (_sg.features.image_clamp_to_border) { + if (@available(macOS 12.0, iOS 14.0, *)) { + mtl_desc.borderColor = _sg_mtl_border_color(desc->border_color); + } + } + mtl_desc.minFilter = _sg_mtl_minmag_filter(desc->min_filter); + mtl_desc.magFilter = _sg_mtl_minmag_filter(desc->mag_filter); + mtl_desc.mipFilter = _sg_mtl_mipmap_filter(desc->mipmap_filter); + mtl_desc.lodMinClamp = desc->min_lod; + mtl_desc.lodMaxClamp = desc->max_lod; + // FIXME: lodAverage? + mtl_desc.maxAnisotropy = desc->max_anisotropy; + mtl_desc.normalizedCoordinates = YES; + mtl_desc.compareFunction = _sg_mtl_compare_func(desc->compare); + #if defined(SOKOL_DEBUG) + if (desc->label) { + mtl_desc.label = [NSString stringWithUTF8String:desc->label]; + } + #endif + mtl_smp = [_sg.mtl.device newSamplerStateWithDescriptor:mtl_desc]; + _SG_OBJC_RELEASE(mtl_desc); + if (nil == mtl_smp) { + _SG_ERROR(METAL_CREATE_SAMPLER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + smp->mtl.sampler_state = _sg_mtl_add_resource(mtl_smp); + _SG_OBJC_RELEASE(mtl_smp); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + // it's valid to call release resource with a 'null resource' + _sg_mtl_release_resource(_sg.frame_index, smp->mtl.sampler_state); +} + +_SOKOL_PRIVATE id _sg_mtl_compile_library(const char* src) { + NSError* err = NULL; + id lib = [_sg.mtl.device + newLibraryWithSource:[NSString stringWithUTF8String:src] + options:nil + error:&err + ]; + if (err) { + _SG_ERROR(METAL_SHADER_COMPILATION_FAILED); + _SG_LOGMSG(METAL_SHADER_COMPILATION_OUTPUT, [err.localizedDescription UTF8String]); + } + return lib; +} + +_SOKOL_PRIVATE id _sg_mtl_library_from_bytecode(const void* ptr, size_t num_bytes) { + NSError* err = NULL; + dispatch_data_t lib_data = dispatch_data_create(ptr, num_bytes, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + id lib = [_sg.mtl.device newLibraryWithData:lib_data error:&err]; + if (err) { + _SG_ERROR(METAL_SHADER_CREATION_FAILED); + _SG_LOGMSG(METAL_SHADER_COMPILATION_OUTPUT, [err.localizedDescription UTF8String]); + } + _SG_OBJC_RELEASE(lib_data); + return lib; +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + + // create metal library objects and lookup entry functions + id vs_lib = nil; + id fs_lib = nil; + id vs_func = nil; + id fs_func = nil; + const char* vs_entry = desc->vs.entry; + const char* fs_entry = desc->fs.entry; + if (desc->vs.bytecode.ptr && desc->fs.bytecode.ptr) { + // separate byte code provided + vs_lib = _sg_mtl_library_from_bytecode(desc->vs.bytecode.ptr, desc->vs.bytecode.size); + fs_lib = _sg_mtl_library_from_bytecode(desc->fs.bytecode.ptr, desc->fs.bytecode.size); + if ((nil == vs_lib) || (nil == fs_lib)) { + goto failed; + } + vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; + fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; + } else if (desc->vs.source && desc->fs.source) { + // separate sources provided + vs_lib = _sg_mtl_compile_library(desc->vs.source); + fs_lib = _sg_mtl_compile_library(desc->fs.source); + if ((nil == vs_lib) || (nil == fs_lib)) { + goto failed; + } + vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; + fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; + } else { + goto failed; + } + if (nil == vs_func) { + _SG_ERROR(METAL_VERTEX_SHADER_ENTRY_NOT_FOUND); + goto failed; + } + if (nil == fs_func) { + _SG_ERROR(METAL_FRAGMENT_SHADER_ENTRY_NOT_FOUND); + goto failed; + } + #if defined(SOKOL_DEBUG) + if (desc->label) { + vs_lib.label = [NSString stringWithFormat:@"%s.vs", desc->label]; + fs_lib.label = [NSString stringWithFormat:@"%s.fs", desc->label]; + } + #endif + // it is legal to call _sg_mtl_add_resource with a nil value, this will return a special 0xFFFFFFFF index + shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib = _sg_mtl_add_resource(vs_lib); + _SG_OBJC_RELEASE(vs_lib); + shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib = _sg_mtl_add_resource(fs_lib); + _SG_OBJC_RELEASE(fs_lib); + shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func = _sg_mtl_add_resource(vs_func); + _SG_OBJC_RELEASE(vs_func); + shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func = _sg_mtl_add_resource(fs_func); + _SG_OBJC_RELEASE(fs_func); + return SG_RESOURCESTATE_VALID; +failed: + if (vs_lib != nil) { + _SG_OBJC_RELEASE(vs_lib); + } + if (fs_lib != nil) { + _SG_OBJC_RELEASE(fs_lib); + } + if (vs_func != nil) { + _SG_OBJC_RELEASE(vs_func); + } + if (fs_func != nil) { + _SG_OBJC_RELEASE(fs_func); + } + return SG_RESOURCESTATE_FAILED; +} + +_SOKOL_PRIVATE void _sg_mtl_discard_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + // it is valid to call _sg_mtl_release_resource with a 'null resource' + _sg_mtl_release_resource(_sg.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func); + _sg_mtl_release_resource(_sg.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib); + _sg_mtl_release_resource(_sg.frame_index, shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func); + _sg_mtl_release_resource(_sg.frame_index, shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib); +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + + pip->shader = shd; + + sg_primitive_type prim_type = desc->primitive_type; + pip->mtl.prim_type = _sg_mtl_primitive_type(prim_type); + pip->mtl.index_size = _sg_mtl_index_size(pip->cmn.index_type); + if (SG_INDEXTYPE_NONE != pip->cmn.index_type) { + pip->mtl.index_type = _sg_mtl_index_type(pip->cmn.index_type); + } + pip->mtl.cull_mode = _sg_mtl_cull_mode(desc->cull_mode); + pip->mtl.winding = _sg_mtl_winding(desc->face_winding); + pip->mtl.stencil_ref = desc->stencil.ref; + + // create vertex-descriptor + MTLVertexDescriptor* vtx_desc = [MTLVertexDescriptor vertexDescriptor]; + for (NSUInteger attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + vtx_desc.attributes[attr_index].format = _sg_mtl_vertex_format(a_state->format); + vtx_desc.attributes[attr_index].offset = (NSUInteger)a_state->offset; + vtx_desc.attributes[attr_index].bufferIndex = (NSUInteger)(a_state->buffer_index + SG_MAX_SHADERSTAGE_UBS); + pip->cmn.vertex_buffer_layout_active[a_state->buffer_index] = true; + } + for (NSUInteger layout_index = 0; layout_index < SG_MAX_VERTEX_BUFFERS; layout_index++) { + if (pip->cmn.vertex_buffer_layout_active[layout_index]) { + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[layout_index]; + const NSUInteger mtl_vb_slot = layout_index + SG_MAX_SHADERSTAGE_UBS; + SOKOL_ASSERT(l_state->stride > 0); + vtx_desc.layouts[mtl_vb_slot].stride = (NSUInteger)l_state->stride; + vtx_desc.layouts[mtl_vb_slot].stepFunction = _sg_mtl_step_function(l_state->step_func); + vtx_desc.layouts[mtl_vb_slot].stepRate = (NSUInteger)l_state->step_rate; + if (SG_VERTEXSTEP_PER_INSTANCE == l_state->step_func) { + // NOTE: not actually used in _sg_mtl_draw() + pip->cmn.use_instanced_draw = true; + } + } + } + + // render-pipeline descriptor + MTLRenderPipelineDescriptor* rp_desc = [[MTLRenderPipelineDescriptor alloc] init]; + rp_desc.vertexDescriptor = vtx_desc; + SOKOL_ASSERT(shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); + rp_desc.vertexFunction = _sg_mtl_id(shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func); + SOKOL_ASSERT(shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); + rp_desc.fragmentFunction = _sg_mtl_id(shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func); + rp_desc.rasterSampleCount = (NSUInteger)desc->sample_count; + rp_desc.alphaToCoverageEnabled = desc->alpha_to_coverage_enabled; + rp_desc.alphaToOneEnabled = NO; + rp_desc.rasterizationEnabled = YES; + rp_desc.depthAttachmentPixelFormat = _sg_mtl_pixel_format(desc->depth.pixel_format); + if (desc->depth.pixel_format == SG_PIXELFORMAT_DEPTH_STENCIL) { + rp_desc.stencilAttachmentPixelFormat = _sg_mtl_pixel_format(desc->depth.pixel_format); + } + if (@available(macOS 10.13, iOS 11.0, *)) { + for (NSUInteger i = 0; i < (SG_MAX_SHADERSTAGE_UBS+SG_MAX_VERTEX_BUFFERS); i++) { + rp_desc.vertexBuffers[i].mutability = MTLMutabilityImmutable; + } + for (NSUInteger i = 0; i < SG_MAX_SHADERSTAGE_UBS; i++) { + rp_desc.fragmentBuffers[i].mutability = MTLMutabilityImmutable; + } + } + for (NSUInteger i = 0; i < (NSUInteger)desc->color_count; i++) { + SOKOL_ASSERT(i < SG_MAX_COLOR_ATTACHMENTS); + const sg_color_target_state* cs = &desc->colors[i]; + rp_desc.colorAttachments[i].pixelFormat = _sg_mtl_pixel_format(cs->pixel_format); + rp_desc.colorAttachments[i].writeMask = _sg_mtl_color_write_mask(cs->write_mask); + rp_desc.colorAttachments[i].blendingEnabled = cs->blend.enabled; + rp_desc.colorAttachments[i].alphaBlendOperation = _sg_mtl_blend_op(cs->blend.op_alpha); + rp_desc.colorAttachments[i].rgbBlendOperation = _sg_mtl_blend_op(cs->blend.op_rgb); + rp_desc.colorAttachments[i].destinationAlphaBlendFactor = _sg_mtl_blend_factor(cs->blend.dst_factor_alpha); + rp_desc.colorAttachments[i].destinationRGBBlendFactor = _sg_mtl_blend_factor(cs->blend.dst_factor_rgb); + rp_desc.colorAttachments[i].sourceAlphaBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_alpha); + rp_desc.colorAttachments[i].sourceRGBBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_rgb); + } + #if defined(SOKOL_DEBUG) + if (desc->label) { + rp_desc.label = [NSString stringWithFormat:@"%s", desc->label]; + } + #endif + NSError* err = NULL; + id mtl_rps = [_sg.mtl.device newRenderPipelineStateWithDescriptor:rp_desc error:&err]; + _SG_OBJC_RELEASE(rp_desc); + if (nil == mtl_rps) { + SOKOL_ASSERT(err); + _SG_ERROR(METAL_CREATE_RPS_FAILED); + _SG_LOGMSG(METAL_CREATE_RPS_OUTPUT, [err.localizedDescription UTF8String]); + return SG_RESOURCESTATE_FAILED; + } + pip->mtl.rps = _sg_mtl_add_resource(mtl_rps); + _SG_OBJC_RELEASE(mtl_rps); + + // depth-stencil-state + MTLDepthStencilDescriptor* ds_desc = [[MTLDepthStencilDescriptor alloc] init]; + ds_desc.depthCompareFunction = _sg_mtl_compare_func(desc->depth.compare); + ds_desc.depthWriteEnabled = desc->depth.write_enabled; + if (desc->stencil.enabled) { + const sg_stencil_face_state* sb = &desc->stencil.back; + ds_desc.backFaceStencil = [[MTLStencilDescriptor alloc] init]; + ds_desc.backFaceStencil.stencilFailureOperation = _sg_mtl_stencil_op(sb->fail_op); + ds_desc.backFaceStencil.depthFailureOperation = _sg_mtl_stencil_op(sb->depth_fail_op); + ds_desc.backFaceStencil.depthStencilPassOperation = _sg_mtl_stencil_op(sb->pass_op); + ds_desc.backFaceStencil.stencilCompareFunction = _sg_mtl_compare_func(sb->compare); + ds_desc.backFaceStencil.readMask = desc->stencil.read_mask; + ds_desc.backFaceStencil.writeMask = desc->stencil.write_mask; + const sg_stencil_face_state* sf = &desc->stencil.front; + ds_desc.frontFaceStencil = [[MTLStencilDescriptor alloc] init]; + ds_desc.frontFaceStencil.stencilFailureOperation = _sg_mtl_stencil_op(sf->fail_op); + ds_desc.frontFaceStencil.depthFailureOperation = _sg_mtl_stencil_op(sf->depth_fail_op); + ds_desc.frontFaceStencil.depthStencilPassOperation = _sg_mtl_stencil_op(sf->pass_op); + ds_desc.frontFaceStencil.stencilCompareFunction = _sg_mtl_compare_func(sf->compare); + ds_desc.frontFaceStencil.readMask = desc->stencil.read_mask; + ds_desc.frontFaceStencil.writeMask = desc->stencil.write_mask; + } + #if defined(SOKOL_DEBUG) + if (desc->label) { + ds_desc.label = [NSString stringWithFormat:@"%s.dss", desc->label]; + } + #endif + id mtl_dss = [_sg.mtl.device newDepthStencilStateWithDescriptor:ds_desc]; + _SG_OBJC_RELEASE(ds_desc); + if (nil == mtl_dss) { + _SG_ERROR(METAL_CREATE_DSS_FAILED); + return SG_RESOURCESTATE_FAILED; + } + pip->mtl.dss = _sg_mtl_add_resource(mtl_dss); + _SG_OBJC_RELEASE(mtl_dss); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_discard_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + // it's valid to call release resource with a 'null resource' + _sg_mtl_release_resource(_sg.frame_index, pip->mtl.rps); + _sg_mtl_release_resource(_sg.frame_index, pip->mtl.dss); +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_attachments(_sg_attachments_t* atts, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_attachments_desc* desc) { + SOKOL_ASSERT(atts && desc); + SOKOL_ASSERT(color_images && resolve_images); + + // copy image pointers + for (int i = 0; i < atts->cmn.num_colors; i++) { + const sg_attachment_desc* color_desc = &desc->colors[i]; + _SOKOL_UNUSED(color_desc); + SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == atts->mtl.colors[i].image); + SOKOL_ASSERT(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); + atts->mtl.colors[i].image = color_images[i]; + + const sg_attachment_desc* resolve_desc = &desc->resolves[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == atts->mtl.resolves[i].image); + SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); + SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); + atts->mtl.resolves[i].image = resolve_images[i]; + } + } + SOKOL_ASSERT(0 == atts->mtl.depth_stencil.image); + const sg_attachment_desc* ds_desc = &desc->depth_stencil; + if (ds_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(ds_img && (ds_img->slot.id == ds_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_img->cmn.pixel_format)); + atts->mtl.depth_stencil.image = ds_img; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_discard_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + _SOKOL_UNUSED(atts); +} + +_SOKOL_PRIVATE _sg_image_t* _sg_mtl_attachments_color_image(const _sg_attachments_t* atts, int index) { + // NOTE: may return null + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->mtl.colors[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_mtl_attachments_resolve_image(const _sg_attachments_t* atts, int index) { + // NOTE: may return null + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return atts->mtl.resolves[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_mtl_attachments_ds_image(const _sg_attachments_t* atts) { + // NOTE: may return null + SOKOL_ASSERT(atts); + return atts->mtl.depth_stencil.image; +} + +_SOKOL_PRIVATE void _sg_mtl_begin_pass(const sg_pass* pass) { + SOKOL_ASSERT(pass); + SOKOL_ASSERT(_sg.mtl.cmd_queue); + SOKOL_ASSERT(nil == _sg.mtl.cmd_encoder); + SOKOL_ASSERT(nil == _sg.mtl.cur_drawable); + _sg_mtl_clear_state_cache(); + + const _sg_attachments_t* atts = _sg.cur_pass.atts; + const sg_swapchain* swapchain = &pass->swapchain; + const sg_pass_action* action = &pass->action; + + /* + if this is the first pass in the frame, create command buffers + + NOTE: we're creating two command buffers here, one with unretained references + for storing the regular commands, and one with retained references for + storing the presentDrawable call (this needs to hold on the drawable until + presentation has happened - and the easiest way to do this is to let the + command buffer manage the lifetime of the drawable). + + Also see: https://github.com/floooh/sokol/issues/762 + */ + if (nil == _sg.mtl.cmd_buffer) { + // block until the oldest frame in flight has finished + dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER); + if (_sg.desc.mtl_use_command_buffer_with_retained_references) { + _sg.mtl.cmd_buffer = [_sg.mtl.cmd_queue commandBuffer]; + } else { + _sg.mtl.cmd_buffer = [_sg.mtl.cmd_queue commandBufferWithUnretainedReferences]; + } + [_sg.mtl.cmd_buffer enqueue]; + [_sg.mtl.cmd_buffer addCompletedHandler:^(id cmd_buf) { + // NOTE: this code is called on a different thread! + _SOKOL_UNUSED(cmd_buf); + dispatch_semaphore_signal(_sg.mtl.sem); + }]; + } + + // if this is first pass in frame, get uniform buffer base pointer + if (0 == _sg.mtl.cur_ub_base_ptr) { + _sg.mtl.cur_ub_base_ptr = (uint8_t*)[_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] contents]; + } + + MTLRenderPassDescriptor* pass_desc = [MTLRenderPassDescriptor renderPassDescriptor]; + SOKOL_ASSERT(pass_desc); + if (atts) { + // setup pass descriptor for offscreen rendering + SOKOL_ASSERT(atts->slot.state == SG_RESOURCESTATE_VALID); + for (NSUInteger i = 0; i < (NSUInteger)atts->cmn.num_colors; i++) { + const _sg_attachment_common_t* cmn_color_att = &atts->cmn.colors[i]; + const _sg_mtl_attachment_t* mtl_color_att = &atts->mtl.colors[i]; + const _sg_image_t* color_att_img = mtl_color_att->image; + const _sg_attachment_common_t* cmn_resolve_att = &atts->cmn.resolves[i]; + const _sg_mtl_attachment_t* mtl_resolve_att = &atts->mtl.resolves[i]; + const _sg_image_t* resolve_att_img = mtl_resolve_att->image; + SOKOL_ASSERT(color_att_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(color_att_img->slot.id == cmn_color_att->image_id.id); + SOKOL_ASSERT(color_att_img->mtl.tex[color_att_img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.colorAttachments[i].loadAction = _sg_mtl_load_action(action->colors[i].load_action); + pass_desc.colorAttachments[i].storeAction = _sg_mtl_store_action(action->colors[i].store_action, resolve_att_img != 0); + sg_color c = action->colors[i].clear_value; + pass_desc.colorAttachments[i].clearColor = MTLClearColorMake(c.r, c.g, c.b, c.a); + pass_desc.colorAttachments[i].texture = _sg_mtl_id(color_att_img->mtl.tex[color_att_img->cmn.active_slot]); + pass_desc.colorAttachments[i].level = (NSUInteger)cmn_color_att->mip_level; + switch (color_att_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.colorAttachments[i].slice = (NSUInteger)cmn_color_att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.colorAttachments[i].depthPlane = (NSUInteger)cmn_color_att->slice; + break; + default: break; + } + if (resolve_att_img) { + SOKOL_ASSERT(resolve_att_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(resolve_att_img->slot.id == cmn_resolve_att->image_id.id); + SOKOL_ASSERT(resolve_att_img->mtl.tex[resolve_att_img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.colorAttachments[i].resolveTexture = _sg_mtl_id(resolve_att_img->mtl.tex[resolve_att_img->cmn.active_slot]); + pass_desc.colorAttachments[i].resolveLevel = (NSUInteger)cmn_resolve_att->mip_level; + switch (resolve_att_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.colorAttachments[i].resolveSlice = (NSUInteger)cmn_resolve_att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.colorAttachments[i].resolveDepthPlane = (NSUInteger)cmn_resolve_att->slice; + break; + default: break; + } + } + } + const _sg_image_t* ds_att_img = atts->mtl.depth_stencil.image; + if (0 != ds_att_img) { + SOKOL_ASSERT(ds_att_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(ds_att_img->slot.id == atts->cmn.depth_stencil.image_id.id); + SOKOL_ASSERT(ds_att_img->mtl.tex[ds_att_img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.depthAttachment.texture = _sg_mtl_id(ds_att_img->mtl.tex[ds_att_img->cmn.active_slot]); + pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.load_action); + pass_desc.depthAttachment.storeAction = _sg_mtl_store_action(action->depth.store_action, false); + pass_desc.depthAttachment.clearDepth = action->depth.clear_value; + const _sg_attachment_common_t* cmn_ds_att = &atts->cmn.depth_stencil; + switch (ds_att_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.depthAttachment.slice = (NSUInteger)cmn_ds_att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.depthAttachment.resolveDepthPlane = (NSUInteger)cmn_ds_att->slice; + break; + default: break; + } + if (_sg_is_depth_stencil_format(ds_att_img->cmn.pixel_format)) { + pass_desc.stencilAttachment.texture = _sg_mtl_id(ds_att_img->mtl.tex[ds_att_img->cmn.active_slot]); + pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.load_action); + pass_desc.stencilAttachment.storeAction = _sg_mtl_store_action(action->depth.store_action, false); + pass_desc.stencilAttachment.clearStencil = action->stencil.clear_value; + switch (ds_att_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.stencilAttachment.slice = (NSUInteger)cmn_ds_att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.stencilAttachment.resolveDepthPlane = (NSUInteger)cmn_ds_att->slice; + break; + default: break; + } + } + } + } else { + // setup pass descriptor for swapchain rendering + // + // NOTE: at least in macOS Sonoma this no longer seems to be the case, the + // current drawable is also valid in a minimized window + // === + // an MTKView current_drawable will not be valid if window is minimized, don't do any rendering in this case + if (0 == swapchain->metal.current_drawable) { + _sg.cur_pass.valid = false; + return; + } + // pin the swapchain resources into memory so that they outlive their command buffer + // (this is necessary because the command buffer doesn't retain references) + int pass_desc_ref = _sg_mtl_add_resource(pass_desc); + _sg_mtl_release_resource(_sg.frame_index, pass_desc_ref); + + _sg.mtl.cur_drawable = (__bridge id) swapchain->metal.current_drawable; + if (swapchain->sample_count > 1) { + // multi-sampling: render into msaa texture, resolve into drawable texture + id msaa_tex = (__bridge id) swapchain->metal.msaa_color_texture; + SOKOL_ASSERT(msaa_tex != nil); + pass_desc.colorAttachments[0].texture = msaa_tex; + pass_desc.colorAttachments[0].resolveTexture = _sg.mtl.cur_drawable.texture; + pass_desc.colorAttachments[0].storeAction = MTLStoreActionMultisampleResolve; + } else { + // non-msaa: render into current_drawable + pass_desc.colorAttachments[0].texture = _sg.mtl.cur_drawable.texture; + pass_desc.colorAttachments[0].storeAction = MTLStoreActionStore; + } + pass_desc.colorAttachments[0].loadAction = _sg_mtl_load_action(action->colors[0].load_action); + const sg_color c = action->colors[0].clear_value; + pass_desc.colorAttachments[0].clearColor = MTLClearColorMake(c.r, c.g, c.b, c.a); + + // optional depth-stencil texture + if (swapchain->metal.depth_stencil_texture) { + id ds_tex = (__bridge id) swapchain->metal.depth_stencil_texture; + SOKOL_ASSERT(ds_tex != nil); + pass_desc.depthAttachment.texture = ds_tex; + pass_desc.depthAttachment.storeAction = MTLStoreActionDontCare; + pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.load_action); + pass_desc.depthAttachment.clearDepth = action->depth.clear_value; + if (_sg_is_depth_stencil_format(swapchain->depth_format)) { + pass_desc.stencilAttachment.texture = ds_tex; + pass_desc.stencilAttachment.storeAction = MTLStoreActionDontCare; + pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.load_action); + pass_desc.stencilAttachment.clearStencil = action->stencil.clear_value; + } + } + } + + // NOTE: at least in macOS Sonoma, the following is no longer the case, a valid + // render command encoder is also returned in a minimized window + // === + // create a render command encoder, this might return nil if window is minimized + _sg.mtl.cmd_encoder = [_sg.mtl.cmd_buffer renderCommandEncoderWithDescriptor:pass_desc]; + if (nil == _sg.mtl.cmd_encoder) { + _sg.cur_pass.valid = false; + return; + } + + #if defined(SOKOL_DEBUG) + if (pass->label) { + _sg.mtl.cmd_encoder.label = [NSString stringWithUTF8String:pass->label]; + } + #endif + + // bind the global uniform buffer, this only happens once per pass + _sg_mtl_bind_uniform_buffers(); +} + +_SOKOL_PRIVATE void _sg_mtl_end_pass(void) { + if (nil != _sg.mtl.cmd_encoder) { + [_sg.mtl.cmd_encoder endEncoding]; + // NOTE: MTLRenderCommandEncoder is autoreleased + _sg.mtl.cmd_encoder = nil; + } + // if this is a swapchain pass, present the drawable + if (nil != _sg.mtl.cur_drawable) { + [_sg.mtl.cmd_buffer presentDrawable:_sg.mtl.cur_drawable]; + _sg.mtl.cur_drawable = nil; + } +} + +_SOKOL_PRIVATE void _sg_mtl_commit(void) { + SOKOL_ASSERT(nil == _sg.mtl.cmd_encoder); + SOKOL_ASSERT(nil != _sg.mtl.cmd_buffer); + + // commit the frame's command buffer + [_sg.mtl.cmd_buffer commit]; + + // garbage-collect resources pending for release + _sg_mtl_garbage_collect(_sg.frame_index); + + // rotate uniform buffer slot + if (++_sg.mtl.cur_frame_rotate_index >= SG_NUM_INFLIGHT_FRAMES) { + _sg.mtl.cur_frame_rotate_index = 0; + } + _sg.mtl.cur_ub_offset = 0; + _sg.mtl.cur_ub_base_ptr = 0; + // NOTE: MTLCommandBuffer is autoreleased + _sg.mtl.cmd_buffer = nil; +} + +_SOKOL_PRIVATE void _sg_mtl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + SOKOL_ASSERT(_sg.cur_pass.height > 0); + MTLViewport vp; + vp.originX = (double) x; + vp.originY = (double) (origin_top_left ? y : (_sg.cur_pass.height - (y + h))); + vp.width = (double) w; + vp.height = (double) h; + vp.znear = 0.0; + vp.zfar = 1.0; + [_sg.mtl.cmd_encoder setViewport:vp]; +} + +_SOKOL_PRIVATE void _sg_mtl_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + SOKOL_ASSERT(_sg.cur_pass.width > 0); + SOKOL_ASSERT(_sg.cur_pass.height > 0); + // clip against framebuffer rect + const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.cur_pass.width, _sg.cur_pass.height); + MTLScissorRect r; + r.x = (NSUInteger)clip.x; + r.y = (NSUInteger) (origin_top_left ? clip.y : (_sg.cur_pass.height - (clip.y + clip.h))); + r.width = (NSUInteger)clip.w; + r.height = (NSUInteger)clip.h; + [_sg.mtl.cmd_encoder setScissorRect:r]; +} + +_SOKOL_PRIVATE void _sg_mtl_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + + if (_sg.mtl.state_cache.cur_pipeline_id.id != pip->slot.id) { + _sg.mtl.state_cache.cur_pipeline = pip; + _sg.mtl.state_cache.cur_pipeline_id.id = pip->slot.id; + sg_color c = pip->cmn.blend_color; + [_sg.mtl.cmd_encoder setBlendColorRed:c.r green:c.g blue:c.b alpha:c.a]; + _sg_stats_add(metal.pipeline.num_set_blend_color, 1); + [_sg.mtl.cmd_encoder setCullMode:pip->mtl.cull_mode]; + _sg_stats_add(metal.pipeline.num_set_cull_mode, 1); + [_sg.mtl.cmd_encoder setFrontFacingWinding:pip->mtl.winding]; + _sg_stats_add(metal.pipeline.num_set_front_facing_winding, 1); + [_sg.mtl.cmd_encoder setStencilReferenceValue:pip->mtl.stencil_ref]; + _sg_stats_add(metal.pipeline.num_set_stencil_reference_value, 1); + [_sg.mtl.cmd_encoder setDepthBias:pip->cmn.depth.bias slopeScale:pip->cmn.depth.bias_slope_scale clamp:pip->cmn.depth.bias_clamp]; + _sg_stats_add(metal.pipeline.num_set_depth_bias, 1); + SOKOL_ASSERT(pip->mtl.rps != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setRenderPipelineState:_sg_mtl_id(pip->mtl.rps)]; + _sg_stats_add(metal.pipeline.num_set_render_pipeline_state, 1); + SOKOL_ASSERT(pip->mtl.dss != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setDepthStencilState:_sg_mtl_id(pip->mtl.dss)]; + _sg_stats_add(metal.pipeline.num_set_depth_stencil_state, 1); + } +} + +_SOKOL_PRIVATE bool _sg_mtl_apply_bindings(_sg_bindings_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + + // store index buffer binding, this will be needed later in sg_draw() + _sg.mtl.state_cache.cur_indexbuffer = bnd->ib; + _sg.mtl.state_cache.cur_indexbuffer_offset = bnd->ib_offset; + if (bnd->ib) { + SOKOL_ASSERT(bnd->pip->cmn.index_type != SG_INDEXTYPE_NONE); + _sg.mtl.state_cache.cur_indexbuffer_id.id = bnd->ib->slot.id; + } else { + SOKOL_ASSERT(bnd->pip->cmn.index_type == SG_INDEXTYPE_NONE); + _sg.mtl.state_cache.cur_indexbuffer_id.id = SG_INVALID_ID; + } + + // apply vertex buffers + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_vbs; slot++) { + const _sg_buffer_t* vb = bnd->vbs[slot]; + const int vb_offset = bnd->vb_offsets[slot]; + if ((_sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id != vb->slot.id) || + (_sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] != vb_offset)) + { + _sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] = vb_offset; + const NSUInteger mtl_slot = SG_MAX_SHADERSTAGE_UBS + slot; + if (_sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id != vb->slot.id) { + _sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id = vb->slot.id; + SOKOL_ASSERT(vb->mtl.buf[vb->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setVertexBuffer:_sg_mtl_id(vb->mtl.buf[vb->cmn.active_slot]) + offset:(NSUInteger)vb_offset + atIndex:mtl_slot]; + } else { + [_sg.mtl.cmd_encoder setVertexBufferOffset:(NSUInteger)vb_offset atIndex:mtl_slot]; + } + _sg_stats_add(metal.bindings.num_set_vertex_buffer, 1); + } + } + + // apply vertex stage images + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_vs_imgs; slot++) { + const _sg_image_t* img = bnd->vs_imgs[slot]; + if (_sg.mtl.state_cache.cur_vs_image_ids[slot].id != img->slot.id) { + _sg.mtl.state_cache.cur_vs_image_ids[slot].id = img->slot.id; + SOKOL_ASSERT(img->mtl.tex[img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setVertexTexture:_sg_mtl_id(img->mtl.tex[img->cmn.active_slot]) atIndex:slot]; + _sg_stats_add(metal.bindings.num_set_vertex_texture, 1); + } + } + + // apply vertex stage samplers + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_vs_smps; slot++) { + const _sg_sampler_t* smp = bnd->vs_smps[slot]; + if (_sg.mtl.state_cache.cur_vs_sampler_ids[slot].id != smp->slot.id) { + _sg.mtl.state_cache.cur_vs_sampler_ids[slot].id = smp->slot.id; + SOKOL_ASSERT(smp->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setVertexSamplerState:_sg_mtl_id(smp->mtl.sampler_state) atIndex:slot]; + _sg_stats_add(metal.bindings.num_set_vertex_sampler_state, 1); + } + } + + // apply vertex stage storage buffers + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_vs_sbufs; slot++) { + const _sg_buffer_t* sbuf = bnd->vs_sbufs[slot]; + if (_sg.mtl.state_cache.cur_vs_storagebuffer_ids[slot].id != sbuf->slot.id) { + _sg.mtl.state_cache.cur_vs_storagebuffer_ids[slot].id = sbuf->slot.id; + SOKOL_ASSERT(sbuf->mtl.buf[sbuf->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + const NSUInteger mtl_slot = SG_MAX_SHADERSTAGE_UBS + SG_MAX_VERTEX_BUFFERS + slot; + [_sg.mtl.cmd_encoder setVertexBuffer:_sg_mtl_id(sbuf->mtl.buf[sbuf->cmn.active_slot]) offset:0 atIndex:mtl_slot]; + _sg_stats_add(metal.bindings.num_set_vertex_buffer, 1); + } + } + + // apply fragment stage images + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_fs_imgs; slot++) { + const _sg_image_t* img = bnd->fs_imgs[slot]; + if (_sg.mtl.state_cache.cur_fs_image_ids[slot].id != img->slot.id) { + _sg.mtl.state_cache.cur_fs_image_ids[slot].id = img->slot.id; + SOKOL_ASSERT(img->mtl.tex[img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setFragmentTexture:_sg_mtl_id(img->mtl.tex[img->cmn.active_slot]) atIndex:slot]; + _sg_stats_add(metal.bindings.num_set_fragment_texture, 1); + } + } + + // apply fragment stage samplers + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_fs_smps; slot++) { + const _sg_sampler_t* smp = bnd->fs_smps[slot]; + if (_sg.mtl.state_cache.cur_fs_sampler_ids[slot].id != smp->slot.id) { + _sg.mtl.state_cache.cur_fs_sampler_ids[slot].id = smp->slot.id; + SOKOL_ASSERT(smp->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setFragmentSamplerState:_sg_mtl_id(smp->mtl.sampler_state) atIndex:slot]; + _sg_stats_add(metal.bindings.num_set_fragment_sampler_state, 1); + } + } + + // apply fragment stage storage buffers + for (NSUInteger slot = 0; slot < (NSUInteger)bnd->num_fs_sbufs; slot++) { + const _sg_buffer_t* sbuf = bnd->fs_sbufs[slot]; + if (_sg.mtl.state_cache.cur_fs_storagebuffer_ids[slot].id != sbuf->slot.id) { + _sg.mtl.state_cache.cur_fs_storagebuffer_ids[slot].id = sbuf->slot.id; + SOKOL_ASSERT(sbuf->mtl.buf[sbuf->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + const NSUInteger mtl_slot = SG_MAX_SHADERSTAGE_UBS + slot; + [_sg.mtl.cmd_encoder setFragmentBuffer:_sg_mtl_id(sbuf->mtl.buf[sbuf->cmn.active_slot]) offset:0 atIndex:mtl_slot]; + _sg_stats_add(metal.bindings.num_set_fragment_buffer, 1); + } + } + + return true; +} + +_SOKOL_PRIVATE void _sg_mtl_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + SOKOL_ASSERT(((size_t)_sg.mtl.cur_ub_offset + data->size) <= (size_t)_sg.mtl.ub_size); + SOKOL_ASSERT((_sg.mtl.cur_ub_offset & (_SG_MTL_UB_ALIGN-1)) == 0); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline && _sg.mtl.state_cache.cur_pipeline->shader); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->slot.id == _sg.mtl.state_cache.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->shader->slot.id == _sg.mtl.state_cache.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(ub_index < _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); + SOKOL_ASSERT(data->size == _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); + + // copy to global uniform buffer, record offset into cmd encoder, and advance offset + uint8_t* dst = &_sg.mtl.cur_ub_base_ptr[_sg.mtl.cur_ub_offset]; + memcpy(dst, data->ptr, data->size); + if (stage_index == SG_SHADERSTAGE_VS) { + [_sg.mtl.cmd_encoder setVertexBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:(NSUInteger)ub_index]; + _sg_stats_add(metal.uniforms.num_set_vertex_buffer_offset, 1); + } else { + [_sg.mtl.cmd_encoder setFragmentBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:(NSUInteger)ub_index]; + _sg_stats_add(metal.uniforms.num_set_fragment_buffer_offset, 1); + } + _sg.mtl.cur_ub_offset = _sg_roundup(_sg.mtl.cur_ub_offset + (int)data->size, _SG_MTL_UB_ALIGN); +} + +_SOKOL_PRIVATE void _sg_mtl_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline && (_sg.mtl.state_cache.cur_pipeline->slot.id == _sg.mtl.state_cache.cur_pipeline_id.id)); + if (SG_INDEXTYPE_NONE != _sg.mtl.state_cache.cur_pipeline->cmn.index_type) { + // indexed rendering + SOKOL_ASSERT(_sg.mtl.state_cache.cur_indexbuffer && (_sg.mtl.state_cache.cur_indexbuffer->slot.id == _sg.mtl.state_cache.cur_indexbuffer_id.id)); + const _sg_buffer_t* ib = _sg.mtl.state_cache.cur_indexbuffer; + SOKOL_ASSERT(ib->mtl.buf[ib->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + const NSUInteger index_buffer_offset = (NSUInteger) (_sg.mtl.state_cache.cur_indexbuffer_offset + base_element * _sg.mtl.state_cache.cur_pipeline->mtl.index_size); + [_sg.mtl.cmd_encoder drawIndexedPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl.prim_type + indexCount:(NSUInteger)num_elements + indexType:_sg.mtl.state_cache.cur_pipeline->mtl.index_type + indexBuffer:_sg_mtl_id(ib->mtl.buf[ib->cmn.active_slot]) + indexBufferOffset:index_buffer_offset + instanceCount:(NSUInteger)num_instances]; + } else { + // non-indexed rendering + [_sg.mtl.cmd_encoder drawPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl.prim_type + vertexStart:(NSUInteger)base_element + vertexCount:(NSUInteger)num_elements + instanceCount:(NSUInteger)num_instances]; + } +} + +_SOKOL_PRIVATE void _sg_mtl_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + __unsafe_unretained id mtl_buf = _sg_mtl_id(buf->mtl.buf[buf->cmn.active_slot]); + void* dst_ptr = [mtl_buf contents]; + memcpy(dst_ptr, data->ptr, data->size); + #if defined(_SG_TARGET_MACOS) + if (_sg_mtl_resource_options_storage_mode_managed_or_shared() == MTLResourceStorageModeManaged) { + [mtl_buf didModifyRange:NSMakeRange(0, data->size)]; + } + #endif +} + +_SOKOL_PRIVATE void _sg_mtl_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + if (new_frame) { + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + } + __unsafe_unretained id mtl_buf = _sg_mtl_id(buf->mtl.buf[buf->cmn.active_slot]); + uint8_t* dst_ptr = (uint8_t*) [mtl_buf contents]; + dst_ptr += buf->cmn.append_pos; + memcpy(dst_ptr, data->ptr, data->size); + #if defined(_SG_TARGET_MACOS) + if (_sg_mtl_resource_options_storage_mode_managed_or_shared() == MTLResourceStorageModeManaged) { + [mtl_buf didModifyRange:NSMakeRange((NSUInteger)buf->cmn.append_pos, (NSUInteger)data->size)]; + } + #endif +} + +_SOKOL_PRIVATE void _sg_mtl_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + if (++img->cmn.active_slot >= img->cmn.num_slots) { + img->cmn.active_slot = 0; + } + __unsafe_unretained id mtl_tex = _sg_mtl_id(img->mtl.tex[img->cmn.active_slot]); + _sg_mtl_copy_image_data(img, mtl_tex, data); +} + +_SOKOL_PRIVATE void _sg_mtl_push_debug_group(const char* name) { + SOKOL_ASSERT(name); + if (_sg.mtl.cmd_encoder) { + [_sg.mtl.cmd_encoder pushDebugGroup:[NSString stringWithUTF8String:name]]; + } +} + +_SOKOL_PRIVATE void _sg_mtl_pop_debug_group(void) { + if (_sg.mtl.cmd_encoder) { + [_sg.mtl.cmd_encoder popDebugGroup]; + } +} + +// ██ ██ ███████ ██████ ██████ ██████ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ █ ██ █████ ██████ ██ ███ ██████ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███ ███ ███████ ██████ ██████ ██ ██████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>webgpu +// >>wgpu +#elif defined(SOKOL_WGPU) + +_SOKOL_PRIVATE WGPUBufferUsageFlags _sg_wgpu_buffer_usage(sg_buffer_type t, sg_usage u) { + WGPUBufferUsageFlags res = 0; + if (SG_BUFFERTYPE_VERTEXBUFFER == t) { + res = WGPUBufferUsage_Vertex; + } else if (SG_BUFFERTYPE_STORAGEBUFFER == t) { + res = WGPUBufferUsage_Storage; + } else { + res = WGPUBufferUsage_Index; + } + if (SG_USAGE_IMMUTABLE != u) { + res |= WGPUBufferUsage_CopyDst; + } + return res; +} + +_SOKOL_PRIVATE WGPULoadOp _sg_wgpu_load_op(WGPUTextureView view, sg_load_action a) { + if (0 == view) { + return WGPULoadOp_Undefined; + } else switch (a) { + case SG_LOADACTION_CLEAR: + case SG_LOADACTION_DONTCARE: + return WGPULoadOp_Clear; + case SG_LOADACTION_LOAD: + return WGPULoadOp_Load; + default: + SOKOL_UNREACHABLE; + return WGPULoadOp_Force32; + } +} + +_SOKOL_PRIVATE WGPUStoreOp _sg_wgpu_store_op(WGPUTextureView view, sg_store_action a) { + if (0 == view) { + return WGPUStoreOp_Undefined; + } else switch (a) { + case SG_STOREACTION_STORE: + return WGPUStoreOp_Store; + case SG_STOREACTION_DONTCARE: + return WGPUStoreOp_Discard; + default: + SOKOL_UNREACHABLE; + return WGPUStoreOp_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureViewDimension _sg_wgpu_texture_view_dimension(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return WGPUTextureViewDimension_2D; + case SG_IMAGETYPE_CUBE: return WGPUTextureViewDimension_Cube; + case SG_IMAGETYPE_3D: return WGPUTextureViewDimension_3D; + case SG_IMAGETYPE_ARRAY: return WGPUTextureViewDimension_2DArray; + default: SOKOL_UNREACHABLE; return WGPUTextureViewDimension_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureDimension _sg_wgpu_texture_dimension(sg_image_type t) { + if (SG_IMAGETYPE_3D == t) { + return WGPUTextureDimension_3D; + } else { + return WGPUTextureDimension_2D; + } +} + +_SOKOL_PRIVATE WGPUTextureSampleType _sg_wgpu_texture_sample_type(sg_image_sample_type t) { + switch (t) { + case SG_IMAGESAMPLETYPE_FLOAT: return WGPUTextureSampleType_Float; + case SG_IMAGESAMPLETYPE_DEPTH: return WGPUTextureSampleType_Depth; + case SG_IMAGESAMPLETYPE_SINT: return WGPUTextureSampleType_Sint; + case SG_IMAGESAMPLETYPE_UINT: return WGPUTextureSampleType_Uint; + case SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT: return WGPUTextureSampleType_UnfilterableFloat; + default: SOKOL_UNREACHABLE; return WGPUTextureSampleType_Force32; + } +} + +_SOKOL_PRIVATE WGPUSamplerBindingType _sg_wgpu_sampler_binding_type(sg_sampler_type t) { + switch (t) { + case SG_SAMPLERTYPE_FILTERING: return WGPUSamplerBindingType_Filtering; + case SG_SAMPLERTYPE_COMPARISON: return WGPUSamplerBindingType_Comparison; + case SG_SAMPLERTYPE_NONFILTERING: return WGPUSamplerBindingType_NonFiltering; + default: SOKOL_UNREACHABLE; return WGPUSamplerBindingType_Force32; + } +} + +_SOKOL_PRIVATE WGPUAddressMode _sg_wgpu_sampler_address_mode(sg_wrap m) { + switch (m) { + case SG_WRAP_REPEAT: + return WGPUAddressMode_Repeat; + case SG_WRAP_CLAMP_TO_EDGE: + case SG_WRAP_CLAMP_TO_BORDER: + return WGPUAddressMode_ClampToEdge; + case SG_WRAP_MIRRORED_REPEAT: + return WGPUAddressMode_MirrorRepeat; + default: + SOKOL_UNREACHABLE; + return WGPUAddressMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_minmag_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + return WGPUFilterMode_Nearest; + case SG_FILTER_LINEAR: + return WGPUFilterMode_Linear; + default: + SOKOL_UNREACHABLE; + return WGPUFilterMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUMipmapFilterMode _sg_wgpu_sampler_mipmap_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NONE: + case SG_FILTER_NEAREST: + return WGPUMipmapFilterMode_Nearest; + case SG_FILTER_LINEAR: + return WGPUMipmapFilterMode_Linear; + default: + SOKOL_UNREACHABLE; + return WGPUMipmapFilterMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUIndexFormat _sg_wgpu_indexformat(sg_index_type t) { + // NOTE: there's no WGPUIndexFormat_None + return (t == SG_INDEXTYPE_UINT16) ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32; +} + +_SOKOL_PRIVATE WGPUIndexFormat _sg_wgpu_stripindexformat(sg_primitive_type prim_type, sg_index_type idx_type) { + if (idx_type == SG_INDEXTYPE_NONE) { + return WGPUIndexFormat_Undefined; + } else if ((prim_type == SG_PRIMITIVETYPE_LINE_STRIP) || (prim_type == SG_PRIMITIVETYPE_TRIANGLE_STRIP)) { + return _sg_wgpu_indexformat(idx_type); + } else { + return WGPUIndexFormat_Undefined; + } +} + +_SOKOL_PRIVATE WGPUVertexStepMode _sg_wgpu_stepmode(sg_vertex_step s) { + return (s == SG_VERTEXSTEP_PER_VERTEX) ? WGPUVertexStepMode_Vertex : WGPUVertexStepMode_Instance; +} + +_SOKOL_PRIVATE WGPUVertexFormat _sg_wgpu_vertexformat(sg_vertex_format f) { + switch (f) { + case SG_VERTEXFORMAT_FLOAT: return WGPUVertexFormat_Float32; + case SG_VERTEXFORMAT_FLOAT2: return WGPUVertexFormat_Float32x2; + case SG_VERTEXFORMAT_FLOAT3: return WGPUVertexFormat_Float32x3; + case SG_VERTEXFORMAT_FLOAT4: return WGPUVertexFormat_Float32x4; + case SG_VERTEXFORMAT_BYTE4: return WGPUVertexFormat_Sint8x4; + case SG_VERTEXFORMAT_BYTE4N: return WGPUVertexFormat_Snorm8x4; + case SG_VERTEXFORMAT_UBYTE4: return WGPUVertexFormat_Uint8x4; + case SG_VERTEXFORMAT_UBYTE4N: return WGPUVertexFormat_Unorm8x4; + case SG_VERTEXFORMAT_SHORT2: return WGPUVertexFormat_Sint16x2; + case SG_VERTEXFORMAT_SHORT2N: return WGPUVertexFormat_Snorm16x2; + case SG_VERTEXFORMAT_USHORT2N: return WGPUVertexFormat_Unorm16x2; + case SG_VERTEXFORMAT_SHORT4: return WGPUVertexFormat_Sint16x4; + case SG_VERTEXFORMAT_SHORT4N: return WGPUVertexFormat_Snorm16x4; + case SG_VERTEXFORMAT_USHORT4N: return WGPUVertexFormat_Unorm16x4; + case SG_VERTEXFORMAT_HALF2: return WGPUVertexFormat_Float16x2; + case SG_VERTEXFORMAT_HALF4: return WGPUVertexFormat_Float16x4; + // FIXME! UINT10_N2 (see https://github.com/gpuweb/gpuweb/issues/4275) + case SG_VERTEXFORMAT_UINT10_N2: return WGPUVertexFormat_Undefined; + default: + SOKOL_UNREACHABLE; + return WGPUVertexFormat_Force32; + } +} + +_SOKOL_PRIVATE WGPUPrimitiveTopology _sg_wgpu_topology(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return WGPUPrimitiveTopology_PointList; + case SG_PRIMITIVETYPE_LINES: return WGPUPrimitiveTopology_LineList; + case SG_PRIMITIVETYPE_LINE_STRIP: return WGPUPrimitiveTopology_LineStrip; + case SG_PRIMITIVETYPE_TRIANGLES: return WGPUPrimitiveTopology_TriangleList; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return WGPUPrimitiveTopology_TriangleStrip; + default: + SOKOL_UNREACHABLE; + return WGPUPrimitiveTopology_Force32; + } +} + +_SOKOL_PRIVATE WGPUFrontFace _sg_wgpu_frontface(sg_face_winding fw) { + return (fw == SG_FACEWINDING_CCW) ? WGPUFrontFace_CCW : WGPUFrontFace_CW; +} + +_SOKOL_PRIVATE WGPUCullMode _sg_wgpu_cullmode(sg_cull_mode cm) { + switch (cm) { + case SG_CULLMODE_NONE: return WGPUCullMode_None; + case SG_CULLMODE_FRONT: return WGPUCullMode_Front; + case SG_CULLMODE_BACK: return WGPUCullMode_Back; + default: + SOKOL_UNREACHABLE; + return WGPUCullMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) { + switch (p) { + case SG_PIXELFORMAT_NONE: return WGPUTextureFormat_Undefined; + case SG_PIXELFORMAT_R8: return WGPUTextureFormat_R8Unorm; + case SG_PIXELFORMAT_R8SN: return WGPUTextureFormat_R8Snorm; + case SG_PIXELFORMAT_R8UI: return WGPUTextureFormat_R8Uint; + case SG_PIXELFORMAT_R8SI: return WGPUTextureFormat_R8Sint; + case SG_PIXELFORMAT_R16UI: return WGPUTextureFormat_R16Uint; + case SG_PIXELFORMAT_R16SI: return WGPUTextureFormat_R16Sint; + case SG_PIXELFORMAT_R16F: return WGPUTextureFormat_R16Float; + case SG_PIXELFORMAT_RG8: return WGPUTextureFormat_RG8Unorm; + case SG_PIXELFORMAT_RG8SN: return WGPUTextureFormat_RG8Snorm; + case SG_PIXELFORMAT_RG8UI: return WGPUTextureFormat_RG8Uint; + case SG_PIXELFORMAT_RG8SI: return WGPUTextureFormat_RG8Sint; + case SG_PIXELFORMAT_R32UI: return WGPUTextureFormat_R32Uint; + case SG_PIXELFORMAT_R32SI: return WGPUTextureFormat_R32Sint; + case SG_PIXELFORMAT_R32F: return WGPUTextureFormat_R32Float; + case SG_PIXELFORMAT_RG16UI: return WGPUTextureFormat_RG16Uint; + case SG_PIXELFORMAT_RG16SI: return WGPUTextureFormat_RG16Sint; + case SG_PIXELFORMAT_RG16F: return WGPUTextureFormat_RG16Float; + case SG_PIXELFORMAT_RGBA8: return WGPUTextureFormat_RGBA8Unorm; + case SG_PIXELFORMAT_SRGB8A8: return WGPUTextureFormat_RGBA8UnormSrgb; + case SG_PIXELFORMAT_RGBA8SN: return WGPUTextureFormat_RGBA8Snorm; + case SG_PIXELFORMAT_RGBA8UI: return WGPUTextureFormat_RGBA8Uint; + case SG_PIXELFORMAT_RGBA8SI: return WGPUTextureFormat_RGBA8Sint; + case SG_PIXELFORMAT_BGRA8: return WGPUTextureFormat_BGRA8Unorm; + case SG_PIXELFORMAT_RGB10A2: return WGPUTextureFormat_RGB10A2Unorm; + case SG_PIXELFORMAT_RG11B10F: return WGPUTextureFormat_RG11B10Ufloat; + case SG_PIXELFORMAT_RG32UI: return WGPUTextureFormat_RG32Uint; + case SG_PIXELFORMAT_RG32SI: return WGPUTextureFormat_RG32Sint; + case SG_PIXELFORMAT_RG32F: return WGPUTextureFormat_RG32Float; + case SG_PIXELFORMAT_RGBA16UI: return WGPUTextureFormat_RGBA16Uint; + case SG_PIXELFORMAT_RGBA16SI: return WGPUTextureFormat_RGBA16Sint; + case SG_PIXELFORMAT_RGBA16F: return WGPUTextureFormat_RGBA16Float; + case SG_PIXELFORMAT_RGBA32UI: return WGPUTextureFormat_RGBA32Uint; + case SG_PIXELFORMAT_RGBA32SI: return WGPUTextureFormat_RGBA32Sint; + case SG_PIXELFORMAT_RGBA32F: return WGPUTextureFormat_RGBA32Float; + case SG_PIXELFORMAT_DEPTH: return WGPUTextureFormat_Depth32Float; + case SG_PIXELFORMAT_DEPTH_STENCIL: return WGPUTextureFormat_Depth32FloatStencil8; + case SG_PIXELFORMAT_BC1_RGBA: return WGPUTextureFormat_BC1RGBAUnorm; + case SG_PIXELFORMAT_BC2_RGBA: return WGPUTextureFormat_BC2RGBAUnorm; + case SG_PIXELFORMAT_BC3_RGBA: return WGPUTextureFormat_BC3RGBAUnorm; + case SG_PIXELFORMAT_BC3_SRGBA: return WGPUTextureFormat_BC3RGBAUnormSrgb; + case SG_PIXELFORMAT_BC4_R: return WGPUTextureFormat_BC4RUnorm; + case SG_PIXELFORMAT_BC4_RSN: return WGPUTextureFormat_BC4RSnorm; + case SG_PIXELFORMAT_BC5_RG: return WGPUTextureFormat_BC5RGUnorm; + case SG_PIXELFORMAT_BC5_RGSN: return WGPUTextureFormat_BC5RGSnorm; + case SG_PIXELFORMAT_BC6H_RGBF: return WGPUTextureFormat_BC6HRGBFloat; + case SG_PIXELFORMAT_BC6H_RGBUF: return WGPUTextureFormat_BC6HRGBUfloat; + case SG_PIXELFORMAT_BC7_RGBA: return WGPUTextureFormat_BC7RGBAUnorm; + case SG_PIXELFORMAT_BC7_SRGBA: return WGPUTextureFormat_BC7RGBAUnormSrgb; + case SG_PIXELFORMAT_ETC2_RGB8: return WGPUTextureFormat_ETC2RGB8Unorm; + case SG_PIXELFORMAT_ETC2_RGB8A1: return WGPUTextureFormat_ETC2RGB8A1Unorm; + case SG_PIXELFORMAT_ETC2_RGBA8: return WGPUTextureFormat_ETC2RGBA8Unorm; + case SG_PIXELFORMAT_ETC2_SRGB8: return WGPUTextureFormat_ETC2RGB8UnormSrgb; + case SG_PIXELFORMAT_ETC2_SRGB8A8: return WGPUTextureFormat_ETC2RGBA8UnormSrgb; + case SG_PIXELFORMAT_EAC_R11: return WGPUTextureFormat_EACR11Unorm; + case SG_PIXELFORMAT_EAC_R11SN: return WGPUTextureFormat_EACR11Snorm; + case SG_PIXELFORMAT_EAC_RG11: return WGPUTextureFormat_EACRG11Unorm; + case SG_PIXELFORMAT_EAC_RG11SN: return WGPUTextureFormat_EACRG11Snorm; + case SG_PIXELFORMAT_RGB9E5: return WGPUTextureFormat_RGB9E5Ufloat; + case SG_PIXELFORMAT_ASTC_4x4_RGBA: return WGPUTextureFormat_ASTC4x4Unorm; + case SG_PIXELFORMAT_ASTC_4x4_SRGBA: return WGPUTextureFormat_ASTC4x4UnormSrgb; + // NOT SUPPORTED + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + return WGPUTextureFormat_Undefined; + + default: + SOKOL_UNREACHABLE; + return WGPUTextureFormat_Force32; + } +} + +_SOKOL_PRIVATE WGPUCompareFunction _sg_wgpu_comparefunc(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return WGPUCompareFunction_Never; + case SG_COMPAREFUNC_LESS: return WGPUCompareFunction_Less; + case SG_COMPAREFUNC_EQUAL: return WGPUCompareFunction_Equal; + case SG_COMPAREFUNC_LESS_EQUAL: return WGPUCompareFunction_LessEqual; + case SG_COMPAREFUNC_GREATER: return WGPUCompareFunction_Greater; + case SG_COMPAREFUNC_NOT_EQUAL: return WGPUCompareFunction_NotEqual; + case SG_COMPAREFUNC_GREATER_EQUAL: return WGPUCompareFunction_GreaterEqual; + case SG_COMPAREFUNC_ALWAYS: return WGPUCompareFunction_Always; + default: + SOKOL_UNREACHABLE; + return WGPUCompareFunction_Force32; + } +} + +_SOKOL_PRIVATE WGPUStencilOperation _sg_wgpu_stencilop(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return WGPUStencilOperation_Keep; + case SG_STENCILOP_ZERO: return WGPUStencilOperation_Zero; + case SG_STENCILOP_REPLACE: return WGPUStencilOperation_Replace; + case SG_STENCILOP_INCR_CLAMP: return WGPUStencilOperation_IncrementClamp; + case SG_STENCILOP_DECR_CLAMP: return WGPUStencilOperation_DecrementClamp; + case SG_STENCILOP_INVERT: return WGPUStencilOperation_Invert; + case SG_STENCILOP_INCR_WRAP: return WGPUStencilOperation_IncrementWrap; + case SG_STENCILOP_DECR_WRAP: return WGPUStencilOperation_DecrementWrap; + default: + SOKOL_UNREACHABLE; + return WGPUStencilOperation_Force32; + } +} + +_SOKOL_PRIVATE WGPUBlendOperation _sg_wgpu_blendop(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return WGPUBlendOperation_Add; + case SG_BLENDOP_SUBTRACT: return WGPUBlendOperation_Subtract; + case SG_BLENDOP_REVERSE_SUBTRACT: return WGPUBlendOperation_ReverseSubtract; + default: + SOKOL_UNREACHABLE; + return WGPUBlendOperation_Force32; + } +} + +_SOKOL_PRIVATE WGPUBlendFactor _sg_wgpu_blendfactor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return WGPUBlendFactor_Zero; + case SG_BLENDFACTOR_ONE: return WGPUBlendFactor_One; + case SG_BLENDFACTOR_SRC_COLOR: return WGPUBlendFactor_Src; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return WGPUBlendFactor_OneMinusSrc; + case SG_BLENDFACTOR_SRC_ALPHA: return WGPUBlendFactor_SrcAlpha; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return WGPUBlendFactor_OneMinusSrcAlpha; + case SG_BLENDFACTOR_DST_COLOR: return WGPUBlendFactor_Dst; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return WGPUBlendFactor_OneMinusDst; + case SG_BLENDFACTOR_DST_ALPHA: return WGPUBlendFactor_DstAlpha; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return WGPUBlendFactor_OneMinusDstAlpha; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return WGPUBlendFactor_SrcAlphaSaturated; + case SG_BLENDFACTOR_BLEND_COLOR: return WGPUBlendFactor_Constant; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return WGPUBlendFactor_OneMinusConstant; + // FIXME: separate blend alpha value not supported? + case SG_BLENDFACTOR_BLEND_ALPHA: return WGPUBlendFactor_Constant; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return WGPUBlendFactor_OneMinusConstant; + default: + SOKOL_UNREACHABLE; + return WGPUBlendFactor_Force32; + } +} + +_SOKOL_PRIVATE WGPUColorWriteMaskFlags _sg_wgpu_colorwritemask(uint8_t m) { + WGPUColorWriteMaskFlags res = 0; + if (0 != (m & SG_COLORMASK_R)) { + res |= WGPUColorWriteMask_Red; + } + if (0 != (m & SG_COLORMASK_G)) { + res |= WGPUColorWriteMask_Green; + } + if (0 != (m & SG_COLORMASK_B)) { + res |= WGPUColorWriteMask_Blue; + } + if (0 != (m & SG_COLORMASK_A)) { + res |= WGPUColorWriteMask_Alpha; + } + return res; +} + +// image/sampler binding on wgpu follows this convention: +// +// - all images and sampler are in @group(1) +// - vertex stage images start at @binding(0) +// - vertex stage samplers start at @binding(16) +// - vertex stage storage buffers start at @binding(32) +// - fragment stage images start at @binding(48) +// - fragment stage samplers start at @binding(64) +// - fragment stage storage buffers start at @binding(80) +// +_SOKOL_PRIVATE uint32_t _sg_wgpu_image_binding(sg_shader_stage stage, int img_slot) { + SOKOL_ASSERT((img_slot >= 0) && (img_slot < 16)); + if (SG_SHADERSTAGE_VS == stage) { + return 0 + (uint32_t)img_slot; + } else { + return 48 + (uint32_t)img_slot; + } +} + +_SOKOL_PRIVATE uint32_t _sg_wgpu_sampler_binding(sg_shader_stage stage, int smp_slot) { + SOKOL_ASSERT((smp_slot >= 0) && (smp_slot < 16)); + if (SG_SHADERSTAGE_VS == stage) { + return 16 + (uint32_t)smp_slot; + } else { + return 64 + (uint32_t)smp_slot; + } +} + +_SOKOL_PRIVATE uint32_t _sg_wgpu_storagebuffer_binding(sg_shader_stage stage, int sbuf_slot) { + SOKOL_ASSERT((sbuf_slot >= 0) && (sbuf_slot < 16)); + if (SG_SHADERSTAGE_VS == stage) { + return 32 + (uint32_t)sbuf_slot; + } else { + return 80 + (uint32_t)sbuf_slot; + } +} + +_SOKOL_PRIVATE WGPUShaderStage _sg_wgpu_shader_stage(sg_shader_stage stage) { + switch (stage) { + case SG_SHADERSTAGE_VS: return WGPUShaderStage_Vertex; + case SG_SHADERSTAGE_FS: return WGPUShaderStage_Fragment; + default: SOKOL_UNREACHABLE; return WGPUShaderStage_None; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { + _sg.backend = SG_BACKEND_WGPU; + _sg.features.origin_top_left = true; + _sg.features.image_clamp_to_border = false; + _sg.features.mrt_independent_blend_state = true; + _sg.features.mrt_independent_write_mask = true; + _sg.features.storage_buffer = true; + + wgpuDeviceGetLimits(_sg.wgpu.dev, &_sg.wgpu.limits); + + const WGPULimits* l = &_sg.wgpu.limits.limits; + _sg.limits.max_image_size_2d = (int) l->maxTextureDimension2D; + _sg.limits.max_image_size_cube = (int) l->maxTextureDimension2D; // not a bug, see: https://github.com/gpuweb/gpuweb/issues/1327 + _sg.limits.max_image_size_3d = (int) l->maxTextureDimension3D; + _sg.limits.max_image_size_array = (int) l->maxTextureDimension2D; + _sg.limits.max_image_array_layers = (int) l->maxTextureArrayLayers; + _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; + + // NOTE: no WGPUTextureFormat_R16Unorm + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_SRGB8A8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG8SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + + // FIXME: can be made renderable via extension + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); + + // NOTE: msaa rendering is possible in WebGPU, but no resolve + // which is a combination that's not currently supported in sokol-gfx + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R8UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG8UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG16SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + + if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_Float32Filterable)) { + _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sfr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } else { + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); + + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGB9E5]); + + if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_TextureCompressionBC)) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_SRGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_SRGBA]); + } + if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_TextureCompressionETC2)) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_SRGB8A8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_R11SN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_EAC_RG11SN]); + } + + if (wgpuDeviceHasFeature(_sg.wgpu.dev, WGPUFeatureName_TextureCompressionASTC)) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ASTC_4x4_SRGBA]); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_uniform_buffer_init(const sg_desc* desc) { + SOKOL_ASSERT(0 == _sg.wgpu.uniform.staging); + SOKOL_ASSERT(0 == _sg.wgpu.uniform.buf); + SOKOL_ASSERT(0 == _sg.wgpu.uniform.bind.group_layout); + SOKOL_ASSERT(0 == _sg.wgpu.uniform.bind.group); + + // Add the max-uniform-update size (64 KB) to the requested buffer size, + // this is to prevent validation errors in the WebGPU implementation + // if the entire buffer size is used per frame. 64 KB is the allowed + // max uniform update size on NVIDIA + // + // FIXME: is this still needed? + _sg.wgpu.uniform.num_bytes = (uint32_t)(desc->uniform_buffer_size + _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE); + _sg.wgpu.uniform.staging = (uint8_t*)_sg_malloc(_sg.wgpu.uniform.num_bytes); + + WGPUBufferDescriptor ub_desc; + _sg_clear(&ub_desc, sizeof(ub_desc)); + ub_desc.size = _sg.wgpu.uniform.num_bytes; + ub_desc.usage = WGPUBufferUsage_Uniform|WGPUBufferUsage_CopyDst; + _sg.wgpu.uniform.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &ub_desc); + SOKOL_ASSERT(_sg.wgpu.uniform.buf); + + WGPUBindGroupLayoutEntry ub_bgle_desc[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + _sg_clear(ub_bgle_desc, sizeof(ub_bgle_desc)); + for (uint32_t stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + WGPUShaderStage vis = (stage_index == SG_SHADERSTAGE_VS) ? WGPUShaderStage_Vertex : WGPUShaderStage_Fragment; + for (uint32_t ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + uint32_t bind_index = stage_index * SG_MAX_SHADERSTAGE_UBS + ub_index; + ub_bgle_desc[stage_index][ub_index].binding = bind_index; + ub_bgle_desc[stage_index][ub_index].visibility = vis; + ub_bgle_desc[stage_index][ub_index].buffer.type = WGPUBufferBindingType_Uniform; + ub_bgle_desc[stage_index][ub_index].buffer.hasDynamicOffset = true; + } + } + + WGPUBindGroupLayoutDescriptor ub_bgl_desc; + _sg_clear(&ub_bgl_desc, sizeof(ub_bgl_desc)); + ub_bgl_desc.entryCount = SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS; + ub_bgl_desc.entries = &ub_bgle_desc[0][0]; + _sg.wgpu.uniform.bind.group_layout = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &ub_bgl_desc); + SOKOL_ASSERT(_sg.wgpu.uniform.bind.group_layout); + + WGPUBindGroupEntry ub_bge[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + _sg_clear(ub_bge, sizeof(ub_bge)); + for (uint32_t stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + for (uint32_t ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + uint32_t bind_index = stage_index * SG_MAX_SHADERSTAGE_UBS + ub_index; + ub_bge[stage_index][ub_index].binding = bind_index; + ub_bge[stage_index][ub_index].buffer = _sg.wgpu.uniform.buf; + ub_bge[stage_index][ub_index].size = _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE; + } + } + WGPUBindGroupDescriptor bg_desc; + _sg_clear(&bg_desc, sizeof(bg_desc)); + bg_desc.layout = _sg.wgpu.uniform.bind.group_layout; + bg_desc.entryCount = SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS; + bg_desc.entries = &ub_bge[0][0]; + _sg.wgpu.uniform.bind.group = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); + SOKOL_ASSERT(_sg.wgpu.uniform.bind.group); +} + +_SOKOL_PRIVATE void _sg_wgpu_uniform_buffer_discard(void) { + if (_sg.wgpu.uniform.buf) { + wgpuBufferRelease(_sg.wgpu.uniform.buf); + _sg.wgpu.uniform.buf = 0; + } + if (_sg.wgpu.uniform.bind.group) { + wgpuBindGroupRelease(_sg.wgpu.uniform.bind.group); + _sg.wgpu.uniform.bind.group = 0; + } + if (_sg.wgpu.uniform.bind.group_layout) { + wgpuBindGroupLayoutRelease(_sg.wgpu.uniform.bind.group_layout); + _sg.wgpu.uniform.bind.group_layout = 0; + } + if (_sg.wgpu.uniform.staging) { + _sg_free(_sg.wgpu.uniform.staging); + _sg.wgpu.uniform.staging = 0; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_uniform_buffer_on_begin_pass(void) { + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, + 0, // groupIndex 0 is reserved for uniform buffers + _sg.wgpu.uniform.bind.group, + SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS, + &_sg.wgpu.uniform.bind.offsets[0][0]); +} + +_SOKOL_PRIVATE void _sg_wgpu_uniform_buffer_on_commit(void) { + wgpuQueueWriteBuffer(_sg.wgpu.queue, _sg.wgpu.uniform.buf, 0, _sg.wgpu.uniform.staging, _sg.wgpu.uniform.offset); + _sg_stats_add(wgpu.uniforms.size_write_buffer, _sg.wgpu.uniform.offset); + _sg.wgpu.uniform.offset = 0; + _sg_clear(&_sg.wgpu.uniform.bind.offsets[0][0], sizeof(_sg.wgpu.uniform.bind.offsets)); +} + +_SOKOL_PRIVATE void _sg_wgpu_bindgroups_pool_init(const sg_desc* desc) { + SOKOL_ASSERT((desc->wgpu_bindgroups_cache_size > 0) && (desc->wgpu_bindgroups_cache_size < _SG_MAX_POOL_SIZE)); + _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool; + SOKOL_ASSERT(0 == p->bindgroups); + const int pool_size = desc->wgpu_bindgroups_cache_size; + _sg_init_pool(&p->pool, pool_size); + size_t pool_byte_size = sizeof(_sg_wgpu_bindgroup_t) * (size_t)p->pool.size; + p->bindgroups = (_sg_wgpu_bindgroup_t*) _sg_malloc_clear(pool_byte_size); +} + +_SOKOL_PRIVATE void _sg_wgpu_bindgroups_pool_discard(void) { + _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool; + SOKOL_ASSERT(p->bindgroups); + _sg_free(p->bindgroups); p->bindgroups = 0; + _sg_discard_pool(&p->pool); +} + +_SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_bindgroup_at(uint32_t bg_id) { + SOKOL_ASSERT(SG_INVALID_ID != bg_id); + _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool; + int slot_index = _sg_slot_index(bg_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->pool.size)); + return &p->bindgroups[slot_index]; +} + +_SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_lookup_bindgroup(uint32_t bg_id) { + if (SG_INVALID_ID != bg_id) { + _sg_wgpu_bindgroup_t* bg = _sg_wgpu_bindgroup_at(bg_id); + if (bg->slot.id == bg_id) { + return bg; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_wgpu_bindgroup_handle_t _sg_wgpu_alloc_bindgroup(void) { + _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool; + _sg_wgpu_bindgroup_handle_t res; + int slot_index = _sg_pool_alloc_index(&p->pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&p->pool, &p->bindgroups[slot_index].slot, slot_index); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(WGPU_BINDGROUPS_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE void _sg_wgpu_dealloc_bindgroup(_sg_wgpu_bindgroup_t* bg) { + SOKOL_ASSERT(bg && (bg->slot.state == SG_RESOURCESTATE_ALLOC) && (bg->slot.id != SG_INVALID_ID)); + _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool; + _sg_pool_free_index(&p->pool, _sg_slot_index(bg->slot.id)); + _sg_reset_slot(&bg->slot); +} + +_SOKOL_PRIVATE void _sg_wgpu_reset_bindgroup_to_alloc_state(_sg_wgpu_bindgroup_t* bg) { + SOKOL_ASSERT(bg); + _sg_slot_t slot = bg->slot; + _sg_clear(bg, sizeof(_sg_wgpu_bindgroup_t)); + bg->slot = slot; + bg->slot.state = SG_RESOURCESTATE_ALLOC; +} + +// MurmurHash64B (see: https://github.com/aappleby/smhasher/blob/61a0530f28277f2e850bfc39600ce61d02b518de/src/MurmurHash2.cpp#L142) +_SOKOL_PRIVATE uint64_t _sg_wgpu_hash(const void* key, int len, uint64_t seed) { + const uint32_t m = 0x5bd1e995; + const int r = 24; + uint32_t h1 = (uint32_t)seed ^ (uint32_t)len; + uint32_t h2 = (uint32_t)(seed >> 32); + const uint32_t * data = (const uint32_t *)key; + while (len >= 8) { + uint32_t k1 = *data++; + k1 *= m; k1 ^= k1 >> r; k1 *= m; + h1 *= m; h1 ^= k1; + len -= 4; + uint32_t k2 = *data++; + k2 *= m; k2 ^= k2 >> r; k2 *= m; + h2 *= m; h2 ^= k2; + len -= 4; + } + if (len >= 4) { + uint32_t k1 = *data++; + k1 *= m; k1 ^= k1 >> r; k1 *= m; + h1 *= m; h1 ^= k1; + len -= 4; + } + switch(len) { + case 3: h2 ^= (uint32_t)(((unsigned char*)data)[2] << 16); + case 2: h2 ^= (uint32_t)(((unsigned char*)data)[1] << 8); + case 1: h2 ^= ((unsigned char*)data)[0]; + h2 *= m; + }; + h1 ^= h2 >> 18; h1 *= m; + h2 ^= h1 >> 22; h2 *= m; + h1 ^= h2 >> 17; h1 *= m; + h2 ^= h1 >> 19; h2 *= m; + uint64_t h = h1; + h = (h << 32) | h2; + return h; +} + +_SOKOL_PRIVATE void _sg_wgpu_init_bindgroups_cache_key(_sg_wgpu_bindgroups_cache_key_t* key, const _sg_bindings_t* bnd) { + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip); + SOKOL_ASSERT(bnd->num_vs_imgs <= SG_MAX_SHADERSTAGE_IMAGES); + SOKOL_ASSERT(bnd->num_vs_smps <= SG_MAX_SHADERSTAGE_SAMPLERS); + SOKOL_ASSERT(bnd->num_vs_sbufs <= SG_MAX_SHADERSTAGE_STORAGEBUFFERS); + SOKOL_ASSERT(bnd->num_fs_imgs <= SG_MAX_SHADERSTAGE_IMAGES); + SOKOL_ASSERT(bnd->num_fs_smps <= SG_MAX_SHADERSTAGE_SAMPLERS); + SOKOL_ASSERT(bnd->num_fs_sbufs <= SG_MAX_SHADERSTAGE_STORAGEBUFFERS); + + _sg_clear(key->items, sizeof(key->items)); + key->items[0] = bnd->pip->slot.id; + const int vs_imgs_offset = 1; + const int vs_smps_offset = vs_imgs_offset + SG_MAX_SHADERSTAGE_IMAGES; + const int vs_sbufs_offset = vs_smps_offset + SG_MAX_SHADERSTAGE_SAMPLERS; + const int fs_imgs_offset = vs_sbufs_offset + SG_MAX_SHADERSTAGE_STORAGEBUFFERS; + const int fs_smps_offset = fs_imgs_offset + SG_MAX_SHADERSTAGE_IMAGES; + const int fs_sbufs_offset = fs_smps_offset + SG_MAX_SHADERSTAGE_SAMPLERS; + SOKOL_ASSERT((fs_sbufs_offset + SG_MAX_SHADERSTAGE_STORAGEBUFFERS) == _SG_WGPU_BINDGROUPSCACHE_NUM_ITEMS); + for (int i = 0; i < bnd->num_vs_imgs; i++) { + SOKOL_ASSERT(bnd->vs_imgs[i]); + key->items[vs_imgs_offset + i] = bnd->vs_imgs[i]->slot.id; + } + for (int i = 0; i < bnd->num_vs_smps; i++) { + SOKOL_ASSERT(bnd->vs_smps[i]); + key->items[vs_smps_offset + i] = bnd->vs_smps[i]->slot.id; + } + for (int i = 0; i < bnd->num_vs_sbufs; i++) { + SOKOL_ASSERT(bnd->vs_sbufs[i]); + key->items[vs_sbufs_offset + i] = bnd->vs_sbufs[i]->slot.id; + } + for (int i = 0; i < bnd->num_fs_imgs; i++) { + SOKOL_ASSERT(bnd->fs_imgs[i]); + key->items[fs_imgs_offset + i] = bnd->fs_imgs[i]->slot.id; + } + for (int i = 0; i < bnd->num_fs_smps; i++) { + SOKOL_ASSERT(bnd->fs_smps[i]); + key->items[fs_smps_offset + i] = bnd->fs_smps[i]->slot.id; + } + for (int i = 0; i < bnd->num_fs_sbufs; i++) { + SOKOL_ASSERT(bnd->fs_sbufs[i]); + key->items[fs_sbufs_offset + i] = bnd->fs_sbufs[i]->slot.id; + } + key->hash = _sg_wgpu_hash(&key->items, (int)sizeof(key->items), 0x1234567887654321); +} + +_SOKOL_PRIVATE bool _sg_wgpu_compare_bindgroups_cache_key(_sg_wgpu_bindgroups_cache_key_t* k0, _sg_wgpu_bindgroups_cache_key_t* k1) { + SOKOL_ASSERT(k0 && k1); + if (k0->hash != k1->hash) { + return false; + } + if (memcmp(&k0->items, &k1->items, sizeof(k0->items)) != 0) { + _sg_stats_add(wgpu.bindings.num_bindgroup_cache_hash_vs_key_mismatch, 1); + return false; + } + return true; +} + +_SOKOL_PRIVATE _sg_wgpu_bindgroup_t* _sg_wgpu_create_bindgroup(_sg_bindings_t* bnd) { + SOKOL_ASSERT(_sg.wgpu.dev); + SOKOL_ASSERT(bnd->pip); + SOKOL_ASSERT(bnd->pip->shader && (bnd->pip->cmn.shader_id.id == bnd->pip->shader->slot.id)); + _sg_stats_add(wgpu.bindings.num_create_bindgroup, 1); + _sg_wgpu_bindgroup_handle_t bg_id = _sg_wgpu_alloc_bindgroup(); + if (bg_id.id == SG_INVALID_ID) { + return 0; + } + _sg_wgpu_bindgroup_t* bg = _sg_wgpu_bindgroup_at(bg_id.id); + SOKOL_ASSERT(bg && (bg->slot.state == SG_RESOURCESTATE_ALLOC)); + + // create wgpu bindgroup object + WGPUBindGroupLayout bgl = bnd->pip->shader->wgpu.bind_group_layout; + SOKOL_ASSERT(bgl); + WGPUBindGroupEntry wgpu_entries[_SG_WGPU_MAX_BINDGROUP_ENTRIES]; + _sg_clear(&wgpu_entries, sizeof(wgpu_entries)); + int bge_index = 0; + for (int i = 0; i < bnd->num_vs_imgs; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_image_binding(SG_SHADERSTAGE_VS, i); + wgpu_entry->textureView = bnd->vs_imgs[i]->wgpu.view; + } + for (int i = 0; i < bnd->num_vs_smps; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_sampler_binding(SG_SHADERSTAGE_VS, i); + wgpu_entry->sampler = bnd->vs_smps[i]->wgpu.smp; + } + for (int i = 0; i < bnd->num_vs_sbufs; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_storagebuffer_binding(SG_SHADERSTAGE_VS, i); + wgpu_entry->buffer = bnd->vs_sbufs[i]->wgpu.buf; + wgpu_entry->size = (uint64_t) bnd->vs_sbufs[i]->cmn.size; + } + for (int i = 0; i < bnd->num_fs_imgs; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_image_binding(SG_SHADERSTAGE_FS, i); + wgpu_entry->textureView = bnd->fs_imgs[i]->wgpu.view; + } + for (int i = 0; i < bnd->num_fs_smps; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_sampler_binding(SG_SHADERSTAGE_FS, i); + wgpu_entry->sampler = bnd->fs_smps[i]->wgpu.smp; + } + for (int i = 0; i < bnd->num_fs_sbufs; i++) { + WGPUBindGroupEntry* wgpu_entry = &wgpu_entries[bge_index++]; + wgpu_entry->binding = _sg_wgpu_storagebuffer_binding(SG_SHADERSTAGE_FS, i); + wgpu_entry->buffer = bnd->fs_sbufs[i]->wgpu.buf; + wgpu_entry->size = (uint64_t) bnd->fs_sbufs[i]->cmn.size; + } + WGPUBindGroupDescriptor bg_desc; + _sg_clear(&bg_desc, sizeof(bg_desc)); + bg_desc.layout = bgl; + bg_desc.entryCount = (size_t)bge_index; + bg_desc.entries = &wgpu_entries[0]; + bg->bindgroup = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); + if (bg->bindgroup == 0) { + _SG_ERROR(WGPU_CREATEBINDGROUP_FAILED); + bg->slot.state = SG_RESOURCESTATE_FAILED; + return bg; + } + + _sg_wgpu_init_bindgroups_cache_key(&bg->key, bnd); + + bg->slot.state = SG_RESOURCESTATE_VALID; + return bg; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_bindgroup(_sg_wgpu_bindgroup_t* bg) { + SOKOL_ASSERT(bg); + _sg_stats_add(wgpu.bindings.num_discard_bindgroup, 1); + if (bg->slot.state == SG_RESOURCESTATE_VALID) { + if (bg->bindgroup) { + wgpuBindGroupRelease(bg->bindgroup); + bg->bindgroup = 0; + } + _sg_wgpu_reset_bindgroup_to_alloc_state(bg); + SOKOL_ASSERT(bg->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (bg->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_wgpu_dealloc_bindgroup(bg); + SOKOL_ASSERT(bg->slot.state == SG_RESOURCESTATE_INITIAL); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_all_bindgroups(void) { + _sg_wgpu_bindgroups_pool_t* p = &_sg.wgpu.bindgroups_pool; + for (int i = 0; i < p->pool.size; i++) { + sg_resource_state state = p->bindgroups[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_wgpu_discard_bindgroup(&p->bindgroups[i]); + } + } +} + +_SOKOL_PRIVATE void _sg_wgpu_bindgroups_cache_init(const sg_desc* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.num == 0); + SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.index_mask == 0); + SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.items == 0); + const int num = desc->wgpu_bindgroups_cache_size; + if (num <= 1) { + _SG_PANIC(WGPU_BINDGROUPSCACHE_SIZE_GREATER_ONE); + } + if (!_sg_ispow2(num)) { + _SG_PANIC(WGPU_BINDGROUPSCACHE_SIZE_POW2); + } + _sg.wgpu.bindgroups_cache.num = (uint32_t)desc->wgpu_bindgroups_cache_size; + _sg.wgpu.bindgroups_cache.index_mask = _sg.wgpu.bindgroups_cache.num - 1; + size_t size_in_bytes = sizeof(_sg_wgpu_bindgroup_handle_t) * (size_t)num; + _sg.wgpu.bindgroups_cache.items = (_sg_wgpu_bindgroup_handle_t*)_sg_malloc_clear(size_in_bytes); +} + +_SOKOL_PRIVATE void _sg_wgpu_bindgroups_cache_discard(void) { + if (_sg.wgpu.bindgroups_cache.items) { + _sg_free(_sg.wgpu.bindgroups_cache.items); + _sg.wgpu.bindgroups_cache.items = 0; + } + _sg.wgpu.bindgroups_cache.num = 0; + _sg.wgpu.bindgroups_cache.index_mask = 0; +} + +_SOKOL_PRIVATE void _sg_wgpu_bindgroups_cache_set(uint64_t hash, uint32_t bg_id) { + uint32_t index = hash & _sg.wgpu.bindgroups_cache.index_mask; + SOKOL_ASSERT(index < _sg.wgpu.bindgroups_cache.num); + SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.items); + _sg.wgpu.bindgroups_cache.items[index].id = bg_id; +} + +_SOKOL_PRIVATE uint32_t _sg_wgpu_bindgroups_cache_get(uint64_t hash) { + uint32_t index = hash & _sg.wgpu.bindgroups_cache.index_mask; + SOKOL_ASSERT(index < _sg.wgpu.bindgroups_cache.num); + SOKOL_ASSERT(_sg.wgpu.bindgroups_cache.items); + return _sg.wgpu.bindgroups_cache.items[index].id; +} + +_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_clear(void) { + memset(&_sg.wgpu.bindings_cache, 0, sizeof(_sg.wgpu.bindings_cache)); +} + +_SOKOL_PRIVATE bool _sg_wgpu_bindings_cache_vb_dirty(int index, const _sg_buffer_t* vb, int offset) { + SOKOL_ASSERT((index >= 0) && (index < SG_MAX_VERTEX_BUFFERS)); + if (vb) { + return (_sg.wgpu.bindings_cache.vbs[index].buffer.id != vb->slot.id) + || (_sg.wgpu.bindings_cache.vbs[index].offset != offset); + } else { + return _sg.wgpu.bindings_cache.vbs[index].buffer.id != SG_INVALID_ID; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_vb_update(int index, const _sg_buffer_t* vb, int offset) { + SOKOL_ASSERT((index >= 0) && (index < SG_MAX_VERTEX_BUFFERS)); + if (vb) { + _sg.wgpu.bindings_cache.vbs[index].buffer.id = vb->slot.id; + _sg.wgpu.bindings_cache.vbs[index].offset = offset; + } else { + _sg.wgpu.bindings_cache.vbs[index].buffer.id = SG_INVALID_ID; + _sg.wgpu.bindings_cache.vbs[index].offset = 0; + } +} + +_SOKOL_PRIVATE bool _sg_wgpu_bindings_cache_ib_dirty(const _sg_buffer_t* ib, int offset) { + if (ib) { + return (_sg.wgpu.bindings_cache.ib.buffer.id != ib->slot.id) + || (_sg.wgpu.bindings_cache.ib.offset != offset); + } else { + return _sg.wgpu.bindings_cache.ib.buffer.id != SG_INVALID_ID; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_ib_update(const _sg_buffer_t* ib, int offset) { + if (ib) { + _sg.wgpu.bindings_cache.ib.buffer.id = ib->slot.id; + _sg.wgpu.bindings_cache.ib.offset = offset; + } else { + _sg.wgpu.bindings_cache.ib.buffer.id = SG_INVALID_ID; + _sg.wgpu.bindings_cache.ib.offset = 0; + } +} + +_SOKOL_PRIVATE bool _sg_wgpu_bindings_cache_bg_dirty(const _sg_wgpu_bindgroup_t* bg) { + if (bg) { + return _sg.wgpu.bindings_cache.bg.id != bg->slot.id; + } else { + return _sg.wgpu.bindings_cache.bg.id != SG_INVALID_ID; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_bindings_cache_bg_update(const _sg_wgpu_bindgroup_t* bg) { + if (bg) { + _sg.wgpu.bindings_cache.bg.id = bg->slot.id; + } else { + _sg.wgpu.bindings_cache.bg.id = SG_INVALID_ID; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_set_bindings_bindgroup(_sg_wgpu_bindgroup_t* bg) { + if (_sg_wgpu_bindings_cache_bg_dirty(bg)) { + _sg_wgpu_bindings_cache_bg_update(bg); + _sg_stats_add(wgpu.bindings.num_set_bindgroup, 1); + if (bg) { + SOKOL_ASSERT(bg->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(bg->bindgroup); + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, _SG_WGPU_IMAGE_SAMPLER_BINDGROUP_INDEX, bg->bindgroup, 0, 0); + } else { + // a nullptr bindgroup means setting the empty bindgroup + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, _SG_WGPU_IMAGE_SAMPLER_BINDGROUP_INDEX, _sg.wgpu.empty_bind_group, 0, 0); + } + } else { + _sg_stats_add(wgpu.bindings.num_skip_redundant_bindgroup, 1); + } +} + +_SOKOL_PRIVATE bool _sg_wgpu_apply_bindgroup(_sg_bindings_t* bnd) { + if ((bnd->num_vs_imgs + bnd->num_vs_smps + bnd->num_vs_sbufs + bnd->num_fs_imgs + bnd->num_fs_smps + bnd->num_fs_sbufs) > 0) { + if (!_sg.desc.wgpu_disable_bindgroups_cache) { + _sg_wgpu_bindgroup_t* bg = 0; + _sg_wgpu_bindgroups_cache_key_t key; + _sg_wgpu_init_bindgroups_cache_key(&key, bnd); + uint32_t bg_id = _sg_wgpu_bindgroups_cache_get(key.hash); + if (bg_id != SG_INVALID_ID) { + // potential cache hit + bg = _sg_wgpu_lookup_bindgroup(bg_id); + SOKOL_ASSERT(bg && (bg->slot.state == SG_RESOURCESTATE_VALID)); + if (!_sg_wgpu_compare_bindgroups_cache_key(&key, &bg->key)) { + // cache collision, need to delete cached bindgroup + _sg_stats_add(wgpu.bindings.num_bindgroup_cache_collisions, 1); + _sg_wgpu_discard_bindgroup(bg); + _sg_wgpu_bindgroups_cache_set(key.hash, SG_INVALID_ID); + bg = 0; + } else { + _sg_stats_add(wgpu.bindings.num_bindgroup_cache_hits, 1); + } + } else { + _sg_stats_add(wgpu.bindings.num_bindgroup_cache_misses, 1); + } + if (bg == 0) { + // either no cache entry yet, or cache collision, create new bindgroup and store in cache + bg = _sg_wgpu_create_bindgroup(bnd); + _sg_wgpu_bindgroups_cache_set(key.hash, bg->slot.id); + } + if (bg && bg->slot.state == SG_RESOURCESTATE_VALID) { + _sg_wgpu_set_bindings_bindgroup(bg); + } else { + return false; + } + } else { + // bindgroups cache disabled, create and destroy bindgroup on the fly (expensive!) + _sg_wgpu_bindgroup_t* bg = _sg_wgpu_create_bindgroup(bnd); + if (bg) { + if (bg->slot.state == SG_RESOURCESTATE_VALID) { + _sg_wgpu_set_bindings_bindgroup(bg); + } + _sg_wgpu_discard_bindgroup(bg); + } else { + return false; + } + } + } else { + _sg_wgpu_set_bindings_bindgroup(0); + } + return true; +} + +_SOKOL_PRIVATE bool _sg_wgpu_apply_index_buffer(_sg_bindings_t* bnd) { + if (_sg_wgpu_bindings_cache_ib_dirty(bnd->ib, bnd->ib_offset)) { + _sg_wgpu_bindings_cache_ib_update(bnd->ib, bnd->ib_offset); + if (bnd->ib) { + const WGPUIndexFormat format = _sg_wgpu_indexformat(bnd->pip->cmn.index_type); + const uint64_t buf_size = (uint64_t)bnd->ib->cmn.size; + const uint64_t offset = (uint64_t)bnd->ib_offset; + SOKOL_ASSERT(buf_size > offset); + const uint64_t max_bytes = buf_size - offset; + wgpuRenderPassEncoderSetIndexBuffer(_sg.wgpu.pass_enc, bnd->ib->wgpu.buf, format, offset, max_bytes); + _sg_stats_add(wgpu.bindings.num_set_index_buffer, 1); + } + // FIXME: else-path should actually set a null index buffer (this was just recently implemented in WebGPU) + } else { + _sg_stats_add(wgpu.bindings.num_skip_redundant_index_buffer, 1); + } + return true; +} + +_SOKOL_PRIVATE bool _sg_wgpu_apply_vertex_buffers(_sg_bindings_t* bnd) { + for (int slot = 0; slot < bnd->num_vbs; slot++) { + if (_sg_wgpu_bindings_cache_vb_dirty(slot, bnd->vbs[slot], bnd->vb_offsets[slot])) { + _sg_wgpu_bindings_cache_vb_update(slot, bnd->vbs[slot], bnd->vb_offsets[slot]); + const uint64_t buf_size = (uint64_t)bnd->vbs[slot]->cmn.size; + const uint64_t offset = (uint64_t)bnd->vb_offsets[slot]; + SOKOL_ASSERT(buf_size > offset); + const uint64_t max_bytes = buf_size - offset; + wgpuRenderPassEncoderSetVertexBuffer(_sg.wgpu.pass_enc, (uint32_t)slot, bnd->vbs[slot]->wgpu.buf, offset, max_bytes); + _sg_stats_add(wgpu.bindings.num_set_vertex_buffer, 1); + } else { + _sg_stats_add(wgpu.bindings.num_skip_redundant_vertex_buffer, 1); + } + } + // FIXME: remaining vb slots should actually set a null vertex buffer (this was just recently implemented in WebGPU) + return true; +} + +_SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->environment.wgpu.device); + SOKOL_ASSERT(desc->uniform_buffer_size > 0); + _sg.backend = SG_BACKEND_WGPU; + _sg.wgpu.valid = true; + _sg.wgpu.dev = (WGPUDevice) desc->environment.wgpu.device; + _sg.wgpu.queue = wgpuDeviceGetQueue(_sg.wgpu.dev); + SOKOL_ASSERT(_sg.wgpu.queue); + + _sg_wgpu_init_caps(); + _sg_wgpu_uniform_buffer_init(desc); + _sg_wgpu_bindgroups_pool_init(desc); + _sg_wgpu_bindgroups_cache_init(desc); + _sg_wgpu_bindings_cache_clear(); + + // create an empty bind group for shader stages without bound images + // FIXME: once WebGPU supports setting null objects, this can be removed + WGPUBindGroupLayoutDescriptor bgl_desc; + _sg_clear(&bgl_desc, sizeof(bgl_desc)); + WGPUBindGroupLayout empty_bgl = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &bgl_desc); + SOKOL_ASSERT(empty_bgl); + WGPUBindGroupDescriptor bg_desc; + _sg_clear(&bg_desc, sizeof(bg_desc)); + bg_desc.layout = empty_bgl; + _sg.wgpu.empty_bind_group = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); + SOKOL_ASSERT(_sg.wgpu.empty_bind_group); + wgpuBindGroupLayoutRelease(empty_bgl); + + // create initial per-frame command encoder + WGPUCommandEncoderDescriptor cmd_enc_desc; + _sg_clear(&cmd_enc_desc, sizeof(cmd_enc_desc)); + _sg.wgpu.cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); + SOKOL_ASSERT(_sg.wgpu.cmd_enc); +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_backend(void) { + SOKOL_ASSERT(_sg.wgpu.valid); + SOKOL_ASSERT(_sg.wgpu.cmd_enc); + _sg.wgpu.valid = false; + _sg_wgpu_discard_all_bindgroups(); + _sg_wgpu_bindgroups_cache_discard(); + _sg_wgpu_bindgroups_pool_discard(); + _sg_wgpu_uniform_buffer_discard(); + wgpuBindGroupRelease(_sg.wgpu.empty_bind_group); _sg.wgpu.empty_bind_group = 0; + wgpuCommandEncoderRelease(_sg.wgpu.cmd_enc); _sg.wgpu.cmd_enc = 0; + wgpuQueueRelease(_sg.wgpu.queue); _sg.wgpu.queue = 0; +} + +_SOKOL_PRIVATE void _sg_wgpu_reset_state_cache(void) { + _sg_wgpu_bindings_cache_clear(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + const bool injected = (0 != desc->wgpu_buffer); + if (injected) { + buf->wgpu.buf = (WGPUBuffer) desc->wgpu_buffer; + wgpuBufferReference(buf->wgpu.buf); + } else { + // buffer mapping size must be multiple of 4, so round up buffer size (only a problem + // with index buffers containing odd number of indices) + const uint64_t wgpu_buf_size = _sg_roundup_u64((uint64_t)buf->cmn.size, 4); + const bool map_at_creation = (SG_USAGE_IMMUTABLE == buf->cmn.usage); + + WGPUBufferDescriptor wgpu_buf_desc; + _sg_clear(&wgpu_buf_desc, sizeof(wgpu_buf_desc)); + wgpu_buf_desc.usage = _sg_wgpu_buffer_usage(buf->cmn.type, buf->cmn.usage); + wgpu_buf_desc.size = wgpu_buf_size; + wgpu_buf_desc.mappedAtCreation = map_at_creation; + wgpu_buf_desc.label = desc->label; + buf->wgpu.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &wgpu_buf_desc); + if (0 == buf->wgpu.buf) { + _SG_ERROR(WGPU_CREATE_BUFFER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + if (map_at_creation) { + SOKOL_ASSERT(desc->data.ptr && (desc->data.size > 0)); + SOKOL_ASSERT(desc->data.size <= (size_t)buf->cmn.size); + // FIXME: inefficient on WASM + void* ptr = wgpuBufferGetMappedRange(buf->wgpu.buf, 0, wgpu_buf_size); + SOKOL_ASSERT(ptr); + memcpy(ptr, desc->data.ptr, desc->data.size); + wgpuBufferUnmap(buf->wgpu.buf); + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + if (buf->wgpu.buf) { + wgpuBufferDestroy(buf->wgpu.buf); + wgpuBufferRelease(buf->wgpu.buf); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_copy_buffer_data(const _sg_buffer_t* buf, uint64_t offset, const sg_range* data) { + SOKOL_ASSERT((offset + data->size) <= (size_t)buf->cmn.size); + // WebGPU's write-buffer requires the size to be a multiple of four, so we may need to split the copy + // operation into two writeBuffer calls + uint64_t clamped_size = data->size & ~3UL; + uint64_t extra_size = data->size & 3UL; + SOKOL_ASSERT(extra_size < 4); + wgpuQueueWriteBuffer(_sg.wgpu.queue, buf->wgpu.buf, offset, data->ptr, clamped_size); + if (extra_size > 0) { + const uint64_t extra_src_offset = clamped_size; + const uint64_t extra_dst_offset = offset + clamped_size; + uint8_t extra_data[4] = { 0 }; + uint8_t* extra_src_ptr = ((uint8_t*)data->ptr) + extra_src_offset; + for (size_t i = 0; i < extra_size; i++) { + extra_data[i] = extra_src_ptr[i]; + } + wgpuQueueWriteBuffer(_sg.wgpu.queue, buf->wgpu.buf, extra_dst_offset, extra_src_ptr, 4); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_copy_image_data(const _sg_image_t* img, WGPUTexture wgpu_tex, const sg_image_data* data) { + WGPUTextureDataLayout wgpu_layout; + _sg_clear(&wgpu_layout, sizeof(wgpu_layout)); + WGPUImageCopyTexture wgpu_copy_tex; + _sg_clear(&wgpu_copy_tex, sizeof(wgpu_copy_tex)); + wgpu_copy_tex.texture = wgpu_tex; + wgpu_copy_tex.aspect = WGPUTextureAspect_All; + WGPUExtent3D wgpu_extent; + _sg_clear(&wgpu_extent, sizeof(wgpu_extent)); + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6 : 1; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { + wgpu_copy_tex.mipLevel = (uint32_t)mip_index; + wgpu_copy_tex.origin.z = (uint32_t)face_index; + int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + int mip_slices; + switch (img->cmn.type) { + case SG_IMAGETYPE_CUBE: + mip_slices = 1; + break; + case SG_IMAGETYPE_3D: + mip_slices = _sg_miplevel_dim(img->cmn.num_slices, mip_index); + break; + default: + mip_slices = img->cmn.num_slices; + break; + } + const int row_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + const int num_rows = _sg_num_rows(img->cmn.pixel_format, mip_height); + if (_sg_is_compressed_pixel_format(img->cmn.pixel_format)) { + mip_width = _sg_roundup(mip_width, 4); + mip_height = _sg_roundup(mip_height, 4); + } + wgpu_layout.offset = 0; + wgpu_layout.bytesPerRow = (uint32_t)row_pitch; + wgpu_layout.rowsPerImage = (uint32_t)num_rows; + wgpu_extent.width = (uint32_t)mip_width; + wgpu_extent.height = (uint32_t)mip_height; + wgpu_extent.depthOrArrayLayers = (uint32_t)mip_slices; + const sg_range* mip_data = &data->subimage[face_index][mip_index]; + wgpuQueueWriteTexture(_sg.wgpu.queue, &wgpu_copy_tex, mip_data->ptr, mip_data->size, &wgpu_layout, &wgpu_extent); + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + const bool injected = (0 != desc->wgpu_texture); + if (injected) { + img->wgpu.tex = (WGPUTexture)desc->wgpu_texture; + wgpuTextureReference(img->wgpu.tex); + img->wgpu.view = (WGPUTextureView)desc->wgpu_texture_view; + if (img->wgpu.view) { + wgpuTextureViewReference(img->wgpu.view); + } + } else { + WGPUTextureDescriptor wgpu_tex_desc; + _sg_clear(&wgpu_tex_desc, sizeof(wgpu_tex_desc)); + wgpu_tex_desc.label = desc->label; + wgpu_tex_desc.usage = WGPUTextureUsage_TextureBinding|WGPUTextureUsage_CopyDst; + if (desc->render_target) { + wgpu_tex_desc.usage |= WGPUTextureUsage_RenderAttachment; + } + wgpu_tex_desc.dimension = _sg_wgpu_texture_dimension(img->cmn.type); + wgpu_tex_desc.size.width = (uint32_t) img->cmn.width; + wgpu_tex_desc.size.height = (uint32_t) img->cmn.height; + if (desc->type == SG_IMAGETYPE_CUBE) { + wgpu_tex_desc.size.depthOrArrayLayers = 6; + } else { + wgpu_tex_desc.size.depthOrArrayLayers = (uint32_t) img->cmn.num_slices; + } + wgpu_tex_desc.format = _sg_wgpu_textureformat(img->cmn.pixel_format); + wgpu_tex_desc.mipLevelCount = (uint32_t) img->cmn.num_mipmaps; + wgpu_tex_desc.sampleCount = (uint32_t) img->cmn.sample_count; + img->wgpu.tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); + if (0 == img->wgpu.tex) { + _SG_ERROR(WGPU_CREATE_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + if ((img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { + _sg_wgpu_copy_image_data(img, img->wgpu.tex, &desc->data); + } + WGPUTextureViewDescriptor wgpu_texview_desc; + _sg_clear(&wgpu_texview_desc, sizeof(wgpu_texview_desc)); + wgpu_texview_desc.label = desc->label; + wgpu_texview_desc.dimension = _sg_wgpu_texture_view_dimension(img->cmn.type); + wgpu_texview_desc.mipLevelCount = (uint32_t)img->cmn.num_mipmaps; + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + wgpu_texview_desc.arrayLayerCount = 6; + } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + wgpu_texview_desc.arrayLayerCount = (uint32_t)img->cmn.num_slices; + } else { + wgpu_texview_desc.arrayLayerCount = 1; + } + if (_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)) { + wgpu_texview_desc.aspect = WGPUTextureAspect_DepthOnly; + } else { + wgpu_texview_desc.aspect = WGPUTextureAspect_All; + } + img->wgpu.view = wgpuTextureCreateView(img->wgpu.tex, &wgpu_texview_desc); + if (0 == img->wgpu.view) { + _SG_ERROR(WGPU_CREATE_TEXTURE_VIEW_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + if (img->wgpu.view) { + wgpuTextureViewRelease(img->wgpu.view); + img->wgpu.view = 0; + } + if (img->wgpu.tex) { + wgpuTextureDestroy(img->wgpu.tex); + wgpuTextureRelease(img->wgpu.tex); + img->wgpu.tex = 0; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + SOKOL_ASSERT(_sg.wgpu.dev); + const bool injected = (0 != desc->wgpu_sampler); + if (injected) { + smp->wgpu.smp = (WGPUSampler) desc->wgpu_sampler; + wgpuSamplerReference(smp->wgpu.smp); + } else { + WGPUSamplerDescriptor wgpu_desc; + _sg_clear(&wgpu_desc, sizeof(wgpu_desc)); + wgpu_desc.label = desc->label; + wgpu_desc.addressModeU = _sg_wgpu_sampler_address_mode(desc->wrap_u); + wgpu_desc.addressModeV = _sg_wgpu_sampler_address_mode(desc->wrap_v); + wgpu_desc.addressModeW = _sg_wgpu_sampler_address_mode(desc->wrap_w); + wgpu_desc.magFilter = _sg_wgpu_sampler_minmag_filter(desc->mag_filter); + wgpu_desc.minFilter = _sg_wgpu_sampler_minmag_filter(desc->min_filter); + wgpu_desc.mipmapFilter = _sg_wgpu_sampler_mipmap_filter(desc->mipmap_filter); + wgpu_desc.lodMinClamp = desc->min_lod; + wgpu_desc.lodMaxClamp = desc->max_lod; + wgpu_desc.compare = _sg_wgpu_comparefunc(desc->compare); + if (wgpu_desc.compare == WGPUCompareFunction_Never) { + wgpu_desc.compare = WGPUCompareFunction_Undefined; + } + wgpu_desc.maxAnisotropy = (uint16_t)desc->max_anisotropy; + smp->wgpu.smp = wgpuDeviceCreateSampler(_sg.wgpu.dev, &wgpu_desc); + if (0 == smp->wgpu.smp) { + _SG_ERROR(WGPU_CREATE_SAMPLER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + if (smp->wgpu.smp) { + wgpuSamplerRelease(smp->wgpu.smp); + smp->wgpu.smp = 0; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(desc->vs.source && desc->fs.source); + + WGPUBindGroupLayoutEntry wgpu_bgl_entries[_SG_WGPU_MAX_BINDGROUP_ENTRIES]; + _sg_clear(wgpu_bgl_entries, sizeof(wgpu_bgl_entries)); + int bgl_index = 0; + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; + + _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; + _sg_wgpu_shader_stage_t* wgpu_stage = &shd->wgpu.stage[stage_index]; + _sg_strcpy(&wgpu_stage->entry, stage_desc->entry); + + WGPUShaderModuleWGSLDescriptor wgpu_shdmod_wgsl_desc; + _sg_clear(&wgpu_shdmod_wgsl_desc, sizeof(wgpu_shdmod_wgsl_desc)); + wgpu_shdmod_wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor; + wgpu_shdmod_wgsl_desc.code = stage_desc->source; + + WGPUShaderModuleDescriptor wgpu_shdmod_desc; + _sg_clear(&wgpu_shdmod_desc, sizeof(wgpu_shdmod_desc)); + wgpu_shdmod_desc.nextInChain = &wgpu_shdmod_wgsl_desc.chain; + wgpu_shdmod_desc.label = desc->label; + + wgpu_stage->module = wgpuDeviceCreateShaderModule(_sg.wgpu.dev, &wgpu_shdmod_desc); + if (0 == wgpu_stage->module) { + _SG_ERROR(WGPU_CREATE_SHADER_MODULE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + + const int num_images = cmn_stage->num_images; + if (num_images > (int)_sg.wgpu.limits.limits.maxSampledTexturesPerShaderStage) { + _SG_ERROR(WGPU_SHADER_TOO_MANY_IMAGES); + return SG_RESOURCESTATE_FAILED; + } + const int num_samplers = cmn_stage->num_samplers; + if (num_samplers > (int)_sg.wgpu.limits.limits.maxSamplersPerShaderStage) { + _SG_ERROR(WGPU_SHADER_TOO_MANY_SAMPLERS); + return SG_RESOURCESTATE_FAILED; + } + const int num_sbufs = cmn_stage->num_storage_buffers; + if (num_sbufs > (int)_sg.wgpu.limits.limits.maxStorageBuffersPerShaderStage) { + _SG_ERROR(WGPU_SHADER_TOO_MANY_STORAGEBUFFERS); + return SG_RESOURCESTATE_FAILED; + } + for (int img_index = 0; img_index < num_images; img_index++) { + SOKOL_ASSERT(bgl_index < _SG_WGPU_MAX_BINDGROUP_ENTRIES); + WGPUBindGroupLayoutEntry* wgpu_bgl_entry = &wgpu_bgl_entries[bgl_index++]; + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + wgpu_bgl_entry->binding = _sg_wgpu_image_binding((sg_shader_stage)stage_index, img_index); + wgpu_bgl_entry->visibility = _sg_wgpu_shader_stage((sg_shader_stage)stage_index); + wgpu_bgl_entry->texture.viewDimension = _sg_wgpu_texture_view_dimension(img_desc->image_type); + wgpu_bgl_entry->texture.sampleType = _sg_wgpu_texture_sample_type(img_desc->sample_type); + wgpu_bgl_entry->texture.multisampled = img_desc->multisampled; + } + for (int smp_index = 0; smp_index < num_samplers; smp_index++) { + SOKOL_ASSERT(bgl_index < _SG_WGPU_MAX_BINDGROUP_ENTRIES); + WGPUBindGroupLayoutEntry* wgpu_bgl_entry = &wgpu_bgl_entries[bgl_index++]; + const sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_index]; + wgpu_bgl_entry->binding = _sg_wgpu_sampler_binding((sg_shader_stage)stage_index, smp_index); + wgpu_bgl_entry->visibility = _sg_wgpu_shader_stage((sg_shader_stage)stage_index); + wgpu_bgl_entry->sampler.type = _sg_wgpu_sampler_binding_type(smp_desc->sampler_type); + } + for (int sbuf_index = 0; sbuf_index < num_sbufs; sbuf_index++) { + SOKOL_ASSERT(bgl_index < _SG_WGPU_MAX_BINDGROUP_ENTRIES); + WGPUBindGroupLayoutEntry* wgpu_bgl_entry = &wgpu_bgl_entries[bgl_index++]; + const sg_shader_storage_buffer_desc* sbuf_desc = &stage_desc->storage_buffers[sbuf_index]; + wgpu_bgl_entry->binding = _sg_wgpu_storagebuffer_binding((sg_shader_stage)stage_index, sbuf_index); + wgpu_bgl_entry->visibility = _sg_wgpu_shader_stage((sg_shader_stage)stage_index); + wgpu_bgl_entry->buffer.type = sbuf_desc->readonly ? WGPUBufferBindingType_ReadOnlyStorage : WGPUBufferBindingType_Storage; + } + } + + WGPUBindGroupLayoutDescriptor wgpu_bgl_desc; + _sg_clear(&wgpu_bgl_desc, sizeof(wgpu_bgl_desc)); + wgpu_bgl_desc.entryCount = (size_t)bgl_index; + wgpu_bgl_desc.entries = &wgpu_bgl_entries[0]; + shd->wgpu.bind_group_layout = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &wgpu_bgl_desc); + if (shd->wgpu.bind_group_layout == 0) { + _SG_ERROR(WGPU_SHADER_CREATE_BINDGROUP_LAYOUT_FAILED); + return SG_RESOURCESTATE_FAILED; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + if (shd->wgpu.bind_group_layout) { + wgpuBindGroupLayoutRelease(shd->wgpu.bind_group_layout); + shd->wgpu.bind_group_layout = 0; + } + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + _sg_wgpu_shader_stage_t* wgpu_stage = &shd->wgpu.stage[stage_index]; + if (wgpu_stage->module) { + wgpuShaderModuleRelease(wgpu_stage->module); + wgpu_stage->module = 0; + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + SOKOL_ASSERT(shd->wgpu.bind_group_layout); + pip->shader = shd; + + pip->wgpu.blend_color.r = (double) desc->blend_color.r; + pip->wgpu.blend_color.g = (double) desc->blend_color.g; + pip->wgpu.blend_color.b = (double) desc->blend_color.b; + pip->wgpu.blend_color.a = (double) desc->blend_color.a; + + // - @group(0) for uniform blocks + // - @group(1) for all image and sampler resources + WGPUBindGroupLayout wgpu_bgl[_SG_WGPU_NUM_BINDGROUPS]; + _sg_clear(&wgpu_bgl, sizeof(wgpu_bgl)); + wgpu_bgl[_SG_WGPU_UNIFORM_BINDGROUP_INDEX] = _sg.wgpu.uniform.bind.group_layout; + wgpu_bgl[_SG_WGPU_IMAGE_SAMPLER_BINDGROUP_INDEX] = shd->wgpu.bind_group_layout; + WGPUPipelineLayoutDescriptor wgpu_pl_desc; + _sg_clear(&wgpu_pl_desc, sizeof(wgpu_pl_desc)); + wgpu_pl_desc.bindGroupLayoutCount = _SG_WGPU_NUM_BINDGROUPS; + wgpu_pl_desc.bindGroupLayouts = &wgpu_bgl[0]; + const WGPUPipelineLayout wgpu_pip_layout = wgpuDeviceCreatePipelineLayout(_sg.wgpu.dev, &wgpu_pl_desc); + if (0 == wgpu_pip_layout) { + _SG_ERROR(WGPU_CREATE_PIPELINE_LAYOUT_FAILED); + return SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT(wgpu_pip_layout); + + WGPUVertexBufferLayout wgpu_vb_layouts[SG_MAX_VERTEX_BUFFERS]; + _sg_clear(wgpu_vb_layouts, sizeof(wgpu_vb_layouts)); + WGPUVertexAttribute wgpu_vtx_attrs[SG_MAX_VERTEX_BUFFERS][SG_MAX_VERTEX_ATTRIBUTES]; + _sg_clear(wgpu_vtx_attrs, sizeof(wgpu_vtx_attrs)); + int wgpu_vb_num = 0; + for (int vb_idx = 0; vb_idx < SG_MAX_VERTEX_BUFFERS; vb_idx++, wgpu_vb_num++) { + const sg_vertex_buffer_layout_state* vbl_state = &desc->layout.buffers[vb_idx]; + if (0 == vbl_state->stride) { + break; + } + wgpu_vb_layouts[vb_idx].arrayStride = (uint64_t)vbl_state->stride; + wgpu_vb_layouts[vb_idx].stepMode = _sg_wgpu_stepmode(vbl_state->step_func); + wgpu_vb_layouts[vb_idx].attributes = &wgpu_vtx_attrs[vb_idx][0]; + } + for (int va_idx = 0; va_idx < SG_MAX_VERTEX_ATTRIBUTES; va_idx++) { + const sg_vertex_attr_state* va_state = &desc->layout.attrs[va_idx]; + if (SG_VERTEXFORMAT_INVALID == va_state->format) { + break; + } + const int vb_idx = va_state->buffer_index; + SOKOL_ASSERT(vb_idx < SG_MAX_VERTEX_BUFFERS); + pip->cmn.vertex_buffer_layout_active[vb_idx] = true; + const size_t wgpu_attr_idx = wgpu_vb_layouts[vb_idx].attributeCount; + wgpu_vb_layouts[vb_idx].attributeCount += 1; + wgpu_vtx_attrs[vb_idx][wgpu_attr_idx].format = _sg_wgpu_vertexformat(va_state->format); + wgpu_vtx_attrs[vb_idx][wgpu_attr_idx].offset = (uint64_t)va_state->offset; + wgpu_vtx_attrs[vb_idx][wgpu_attr_idx].shaderLocation = (uint32_t)va_idx; + } + + WGPURenderPipelineDescriptor wgpu_pip_desc; + _sg_clear(&wgpu_pip_desc, sizeof(wgpu_pip_desc)); + WGPUDepthStencilState wgpu_ds_state; + _sg_clear(&wgpu_ds_state, sizeof(wgpu_ds_state)); + WGPUFragmentState wgpu_frag_state; + _sg_clear(&wgpu_frag_state, sizeof(wgpu_frag_state)); + WGPUColorTargetState wgpu_ctgt_state[SG_MAX_COLOR_ATTACHMENTS]; + _sg_clear(&wgpu_ctgt_state, sizeof(wgpu_ctgt_state)); + WGPUBlendState wgpu_blend_state[SG_MAX_COLOR_ATTACHMENTS]; + _sg_clear(&wgpu_blend_state, sizeof(wgpu_blend_state)); + wgpu_pip_desc.label = desc->label; + wgpu_pip_desc.layout = wgpu_pip_layout; + wgpu_pip_desc.vertex.module = shd->wgpu.stage[SG_SHADERSTAGE_VS].module; + wgpu_pip_desc.vertex.entryPoint = shd->wgpu.stage[SG_SHADERSTAGE_VS].entry.buf; + wgpu_pip_desc.vertex.bufferCount = (size_t)wgpu_vb_num; + wgpu_pip_desc.vertex.buffers = &wgpu_vb_layouts[0]; + wgpu_pip_desc.primitive.topology = _sg_wgpu_topology(desc->primitive_type); + wgpu_pip_desc.primitive.stripIndexFormat = _sg_wgpu_stripindexformat(desc->primitive_type, desc->index_type); + wgpu_pip_desc.primitive.frontFace = _sg_wgpu_frontface(desc->face_winding); + wgpu_pip_desc.primitive.cullMode = _sg_wgpu_cullmode(desc->cull_mode); + if (SG_PIXELFORMAT_NONE != desc->depth.pixel_format) { + wgpu_ds_state.format = _sg_wgpu_textureformat(desc->depth.pixel_format); + wgpu_ds_state.depthWriteEnabled = desc->depth.write_enabled; + wgpu_ds_state.depthCompare = _sg_wgpu_comparefunc(desc->depth.compare); + wgpu_ds_state.stencilFront.compare = _sg_wgpu_comparefunc(desc->stencil.front.compare); + wgpu_ds_state.stencilFront.failOp = _sg_wgpu_stencilop(desc->stencil.front.fail_op); + wgpu_ds_state.stencilFront.depthFailOp = _sg_wgpu_stencilop(desc->stencil.front.depth_fail_op); + wgpu_ds_state.stencilFront.passOp = _sg_wgpu_stencilop(desc->stencil.front.pass_op); + wgpu_ds_state.stencilBack.compare = _sg_wgpu_comparefunc(desc->stencil.back.compare); + wgpu_ds_state.stencilBack.failOp = _sg_wgpu_stencilop(desc->stencil.back.fail_op); + wgpu_ds_state.stencilBack.depthFailOp = _sg_wgpu_stencilop(desc->stencil.back.depth_fail_op); + wgpu_ds_state.stencilBack.passOp = _sg_wgpu_stencilop(desc->stencil.back.pass_op); + wgpu_ds_state.stencilReadMask = desc->stencil.read_mask; + wgpu_ds_state.stencilWriteMask = desc->stencil.write_mask; + wgpu_ds_state.depthBias = (int32_t)desc->depth.bias; + wgpu_ds_state.depthBiasSlopeScale = desc->depth.bias_slope_scale; + wgpu_ds_state.depthBiasClamp = desc->depth.bias_clamp; + wgpu_pip_desc.depthStencil = &wgpu_ds_state; + } + wgpu_pip_desc.multisample.count = (uint32_t)desc->sample_count; + wgpu_pip_desc.multisample.mask = 0xFFFFFFFF; + wgpu_pip_desc.multisample.alphaToCoverageEnabled = desc->alpha_to_coverage_enabled; + if (desc->color_count > 0) { + wgpu_frag_state.module = shd->wgpu.stage[SG_SHADERSTAGE_FS].module; + wgpu_frag_state.entryPoint = shd->wgpu.stage[SG_SHADERSTAGE_FS].entry.buf; + wgpu_frag_state.targetCount = (size_t)desc->color_count; + wgpu_frag_state.targets = &wgpu_ctgt_state[0]; + for (int i = 0; i < desc->color_count; i++) { + SOKOL_ASSERT(i < SG_MAX_COLOR_ATTACHMENTS); + wgpu_ctgt_state[i].format = _sg_wgpu_textureformat(desc->colors[i].pixel_format); + wgpu_ctgt_state[i].writeMask = _sg_wgpu_colorwritemask(desc->colors[i].write_mask); + if (desc->colors[i].blend.enabled) { + wgpu_ctgt_state[i].blend = &wgpu_blend_state[i]; + wgpu_blend_state[i].color.operation = _sg_wgpu_blendop(desc->colors[i].blend.op_rgb); + wgpu_blend_state[i].color.srcFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.src_factor_rgb); + wgpu_blend_state[i].color.dstFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.dst_factor_rgb); + wgpu_blend_state[i].alpha.operation = _sg_wgpu_blendop(desc->colors[i].blend.op_alpha); + wgpu_blend_state[i].alpha.srcFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.src_factor_alpha); + wgpu_blend_state[i].alpha.dstFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.dst_factor_alpha); + } + } + wgpu_pip_desc.fragment = &wgpu_frag_state; + } + pip->wgpu.pip = wgpuDeviceCreateRenderPipeline(_sg.wgpu.dev, &wgpu_pip_desc); + wgpuPipelineLayoutRelease(wgpu_pip_layout); + if (0 == pip->wgpu.pip) { + _SG_ERROR(WGPU_CREATE_RENDER_PIPELINE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + if (pip == _sg.wgpu.cur_pipeline) { + _sg.wgpu.cur_pipeline = 0; + _sg.wgpu.cur_pipeline_id.id = SG_INVALID_ID; + } + if (pip->wgpu.pip) { + wgpuRenderPipelineRelease(pip->wgpu.pip); + pip->wgpu.pip = 0; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_attachments(_sg_attachments_t* atts, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_attachments_desc* desc) { + SOKOL_ASSERT(atts && desc); + SOKOL_ASSERT(color_images && resolve_images); + + // copy image pointers and create renderable wgpu texture views + for (int i = 0; i < atts->cmn.num_colors; i++) { + const sg_attachment_desc* color_desc = &desc->colors[i]; + _SOKOL_UNUSED(color_desc); + SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == atts->wgpu.colors[i].image); + SOKOL_ASSERT(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); + SOKOL_ASSERT(color_images[i]->wgpu.tex); + atts->wgpu.colors[i].image = color_images[i]; + + WGPUTextureViewDescriptor wgpu_color_view_desc; + _sg_clear(&wgpu_color_view_desc, sizeof(wgpu_color_view_desc)); + wgpu_color_view_desc.baseMipLevel = (uint32_t) color_desc->mip_level; + wgpu_color_view_desc.mipLevelCount = 1; + wgpu_color_view_desc.baseArrayLayer = (uint32_t) color_desc->slice; + wgpu_color_view_desc.arrayLayerCount = 1; + atts->wgpu.colors[i].view = wgpuTextureCreateView(color_images[i]->wgpu.tex, &wgpu_color_view_desc); + if (0 == atts->wgpu.colors[i].view) { + _SG_ERROR(WGPU_ATTACHMENTS_CREATE_TEXTURE_VIEW_FAILED); + return SG_RESOURCESTATE_FAILED; + } + + const sg_attachment_desc* resolve_desc = &desc->resolves[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == atts->wgpu.resolves[i].image); + SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); + SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); + SOKOL_ASSERT(resolve_images[i]->wgpu.tex); + atts->wgpu.resolves[i].image = resolve_images[i]; + + WGPUTextureViewDescriptor wgpu_resolve_view_desc; + _sg_clear(&wgpu_resolve_view_desc, sizeof(wgpu_resolve_view_desc)); + wgpu_resolve_view_desc.baseMipLevel = (uint32_t) resolve_desc->mip_level; + wgpu_resolve_view_desc.mipLevelCount = 1; + wgpu_resolve_view_desc.baseArrayLayer = (uint32_t) resolve_desc->slice; + wgpu_resolve_view_desc.arrayLayerCount = 1; + atts->wgpu.resolves[i].view = wgpuTextureCreateView(resolve_images[i]->wgpu.tex, &wgpu_resolve_view_desc); + if (0 == atts->wgpu.resolves[i].view) { + _SG_ERROR(WGPU_ATTACHMENTS_CREATE_TEXTURE_VIEW_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + } + SOKOL_ASSERT(0 == atts->wgpu.depth_stencil.image); + const sg_attachment_desc* ds_desc = &desc->depth_stencil; + if (ds_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(ds_img && (ds_img->slot.id == ds_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_img->cmn.pixel_format)); + SOKOL_ASSERT(ds_img->wgpu.tex); + atts->wgpu.depth_stencil.image = ds_img; + + WGPUTextureViewDescriptor wgpu_ds_view_desc; + _sg_clear(&wgpu_ds_view_desc, sizeof(wgpu_ds_view_desc)); + wgpu_ds_view_desc.baseMipLevel = (uint32_t) ds_desc->mip_level; + wgpu_ds_view_desc.mipLevelCount = 1; + wgpu_ds_view_desc.baseArrayLayer = (uint32_t) ds_desc->slice; + wgpu_ds_view_desc.arrayLayerCount = 1; + atts->wgpu.depth_stencil.view = wgpuTextureCreateView(ds_img->wgpu.tex, &wgpu_ds_view_desc); + if (0 == atts->wgpu.depth_stencil.view) { + _SG_ERROR(WGPU_ATTACHMENTS_CREATE_TEXTURE_VIEW_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + for (int i = 0; i < atts->cmn.num_colors; i++) { + if (atts->wgpu.colors[i].view) { + wgpuTextureViewRelease(atts->wgpu.colors[i].view); + atts->wgpu.colors[i].view = 0; + } + if (atts->wgpu.resolves[i].view) { + wgpuTextureViewRelease(atts->wgpu.resolves[i].view); + atts->wgpu.resolves[i].view = 0; + } + } + if (atts->wgpu.depth_stencil.view) { + wgpuTextureViewRelease(atts->wgpu.depth_stencil.view); + atts->wgpu.depth_stencil.view = 0; + } +} + +_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_attachments_color_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + // NOTE: may return null + return atts->wgpu.colors[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_attachments_resolve_image(const _sg_attachments_t* atts, int index) { + SOKOL_ASSERT(atts && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + // NOTE: may return null + return atts->wgpu.resolves[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_attachments_ds_image(const _sg_attachments_t* atts) { + // NOTE: may return null + SOKOL_ASSERT(atts); + return atts->wgpu.depth_stencil.image; +} + +_SOKOL_PRIVATE void _sg_wgpu_init_color_att(WGPURenderPassColorAttachment* wgpu_att, const sg_color_attachment_action* action, WGPUTextureView color_view, WGPUTextureView resolve_view) { + wgpu_att->depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; + wgpu_att->view = color_view; + wgpu_att->resolveTarget = resolve_view; + wgpu_att->loadOp = _sg_wgpu_load_op(color_view, action->load_action); + wgpu_att->storeOp = _sg_wgpu_store_op(color_view, action->store_action); + wgpu_att->clearValue.r = action->clear_value.r; + wgpu_att->clearValue.g = action->clear_value.g; + wgpu_att->clearValue.b = action->clear_value.b; + wgpu_att->clearValue.a = action->clear_value.a; +} + +_SOKOL_PRIVATE void _sg_wgpu_init_ds_att(WGPURenderPassDepthStencilAttachment* wgpu_att, const sg_pass_action* action, sg_pixel_format fmt, WGPUTextureView view) { + wgpu_att->view = view; + wgpu_att->depthLoadOp = _sg_wgpu_load_op(view, action->depth.load_action); + wgpu_att->depthStoreOp = _sg_wgpu_store_op(view, action->depth.store_action); + wgpu_att->depthClearValue = action->depth.clear_value; + wgpu_att->depthReadOnly = false; + if (_sg_is_depth_stencil_format(fmt)) { + wgpu_att->stencilLoadOp = _sg_wgpu_load_op(view, action->stencil.load_action); + wgpu_att->stencilStoreOp = _sg_wgpu_store_op(view, action->stencil.store_action); + } else { + wgpu_att->stencilLoadOp = WGPULoadOp_Undefined; + wgpu_att->stencilStoreOp = WGPUStoreOp_Undefined; + } + wgpu_att->stencilClearValue = action->stencil.clear_value; + wgpu_att->stencilReadOnly = false; +} + +_SOKOL_PRIVATE void _sg_wgpu_begin_pass(const sg_pass* pass) { + SOKOL_ASSERT(pass); + SOKOL_ASSERT(_sg.wgpu.cmd_enc); + SOKOL_ASSERT(_sg.wgpu.dev); + + const _sg_attachments_t* atts = _sg.cur_pass.atts; + const sg_swapchain* swapchain = &pass->swapchain; + const sg_pass_action* action = &pass->action; + + _sg.wgpu.cur_pipeline = 0; + _sg.wgpu.cur_pipeline_id.id = SG_INVALID_ID; + + WGPURenderPassDescriptor wgpu_pass_desc; + WGPURenderPassColorAttachment wgpu_color_att[SG_MAX_COLOR_ATTACHMENTS]; + WGPURenderPassDepthStencilAttachment wgpu_ds_att; + _sg_clear(&wgpu_pass_desc, sizeof(wgpu_pass_desc)); + _sg_clear(&wgpu_color_att, sizeof(wgpu_color_att)); + _sg_clear(&wgpu_ds_att, sizeof(wgpu_ds_att)); + wgpu_pass_desc.label = pass->label; + if (atts) { + SOKOL_ASSERT(atts->slot.state == SG_RESOURCESTATE_VALID); + for (int i = 0; i < atts->cmn.num_colors; i++) { + _sg_wgpu_init_color_att(&wgpu_color_att[i], &action->colors[i], atts->wgpu.colors[i].view, atts->wgpu.resolves[i].view); + } + wgpu_pass_desc.colorAttachmentCount = (size_t)atts->cmn.num_colors; + wgpu_pass_desc.colorAttachments = &wgpu_color_att[0]; + if (atts->wgpu.depth_stencil.image) { + _sg_wgpu_init_ds_att(&wgpu_ds_att, action, atts->wgpu.depth_stencil.image->cmn.pixel_format, atts->wgpu.depth_stencil.view); + wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att; + } + } else { + WGPUTextureView wgpu_color_view = (WGPUTextureView) swapchain->wgpu.render_view; + WGPUTextureView wgpu_resolve_view = (WGPUTextureView) swapchain->wgpu.resolve_view; + WGPUTextureView wgpu_depth_stencil_view = (WGPUTextureView) swapchain->wgpu.depth_stencil_view; + _sg_wgpu_init_color_att(&wgpu_color_att[0], &action->colors[0], wgpu_color_view, wgpu_resolve_view); + wgpu_pass_desc.colorAttachmentCount = 1; + wgpu_pass_desc.colorAttachments = &wgpu_color_att[0]; + if (wgpu_depth_stencil_view) { + SOKOL_ASSERT(swapchain->depth_format > SG_PIXELFORMAT_NONE); + _sg_wgpu_init_ds_att(&wgpu_ds_att, action, swapchain->depth_format, wgpu_depth_stencil_view); + wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att; + } + } + _sg.wgpu.pass_enc = wgpuCommandEncoderBeginRenderPass(_sg.wgpu.cmd_enc, &wgpu_pass_desc); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + + // clear bindings cache and apply an empty image-sampler bindgroup + _sg_wgpu_bindings_cache_clear(); + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, _SG_WGPU_IMAGE_SAMPLER_BINDGROUP_INDEX, _sg.wgpu.empty_bind_group, 0, 0); + _sg_stats_add(wgpu.bindings.num_set_bindgroup, 1); + + // initial uniform buffer binding (required even if no uniforms are set in the frame) + _sg_wgpu_uniform_buffer_on_begin_pass(); +} + +_SOKOL_PRIVATE void _sg_wgpu_end_pass(void) { + if (_sg.wgpu.pass_enc) { + wgpuRenderPassEncoderEnd(_sg.wgpu.pass_enc); + wgpuRenderPassEncoderRelease(_sg.wgpu.pass_enc); + _sg.wgpu.pass_enc = 0; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_commit(void) { + SOKOL_ASSERT(_sg.wgpu.cmd_enc); + + _sg_wgpu_uniform_buffer_on_commit(); + + WGPUCommandBufferDescriptor cmd_buf_desc; + _sg_clear(&cmd_buf_desc, sizeof(cmd_buf_desc)); + WGPUCommandBuffer wgpu_cmd_buf = wgpuCommandEncoderFinish(_sg.wgpu.cmd_enc, &cmd_buf_desc); + SOKOL_ASSERT(wgpu_cmd_buf); + wgpuCommandEncoderRelease(_sg.wgpu.cmd_enc); + _sg.wgpu.cmd_enc = 0; + + wgpuQueueSubmit(_sg.wgpu.queue, 1, &wgpu_cmd_buf); + wgpuCommandBufferRelease(wgpu_cmd_buf); + + // create a new render-command-encoder for next frame + WGPUCommandEncoderDescriptor cmd_enc_desc; + _sg_clear(&cmd_enc_desc, sizeof(cmd_enc_desc)); + _sg.wgpu.cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.wgpu.pass_enc); + // FIXME FIXME FIXME: CLIPPING THE VIEWPORT HERE IS WRONG!!! + // (but currently required because WebGPU insists that the viewport rectangle must be + // fully contained inside the framebuffer, but this doesn't make any sense, and also + // isn't required by the backend APIs) + const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.cur_pass.width, _sg.cur_pass.height); + float xf = (float) clip.x; + float yf = (float) (origin_top_left ? clip.y : (_sg.cur_pass.height - (clip.y + clip.h))); + float wf = (float) clip.w; + float hf = (float) clip.h; + wgpuRenderPassEncoderSetViewport(_sg.wgpu.pass_enc, xf, yf, wf, hf, 0.0f, 1.0f); +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.wgpu.pass_enc); + const _sg_recti_t clip = _sg_clipi(x, y, w, h, _sg.cur_pass.width, _sg.cur_pass.height); + uint32_t sx = (uint32_t) clip.x; + uint32_t sy = (uint32_t) (origin_top_left ? clip.y : (_sg.cur_pass.height - (clip.y + clip.h))); + uint32_t sw = (uint32_t) clip.w; + uint32_t sh = (uint32_t) clip.h; + wgpuRenderPassEncoderSetScissorRect(_sg.wgpu.pass_enc, sx, sy, sw, sh); +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->wgpu.pip); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + _sg.wgpu.use_indexed_draw = (pip->cmn.index_type != SG_INDEXTYPE_NONE); + _sg.wgpu.cur_pipeline = pip; + _sg.wgpu.cur_pipeline_id.id = pip->slot.id; + wgpuRenderPassEncoderSetPipeline(_sg.wgpu.pass_enc, pip->wgpu.pip); + wgpuRenderPassEncoderSetBlendConstant(_sg.wgpu.pass_enc, &pip->wgpu.blend_color); + wgpuRenderPassEncoderSetStencilReference(_sg.wgpu.pass_enc, pip->cmn.stencil.ref); +} + +_SOKOL_PRIVATE bool _sg_wgpu_apply_bindings(_sg_bindings_t* bnd) { + SOKOL_ASSERT(_sg.wgpu.pass_enc); + SOKOL_ASSERT(bnd); + SOKOL_ASSERT(bnd->pip->shader && (bnd->pip->cmn.shader_id.id == bnd->pip->shader->slot.id)); + bool retval = true; + retval &= _sg_wgpu_apply_index_buffer(bnd); + retval &= _sg_wgpu_apply_vertex_buffers(bnd); + retval &= _sg_wgpu_apply_bindgroup(bnd); + return retval; +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + const uint32_t alignment = _sg.wgpu.limits.limits.minUniformBufferOffsetAlignment; + SOKOL_ASSERT(_sg.wgpu.pass_enc); + SOKOL_ASSERT(_sg.wgpu.uniform.staging); + SOKOL_ASSERT((_sg.wgpu.uniform.offset + data->size) <= _sg.wgpu.uniform.num_bytes); + SOKOL_ASSERT((_sg.wgpu.uniform.offset & (alignment - 1)) == 0); + SOKOL_ASSERT(_sg.wgpu.cur_pipeline && _sg.wgpu.cur_pipeline->shader); + SOKOL_ASSERT(_sg.wgpu.cur_pipeline->slot.id == _sg.wgpu.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.wgpu.cur_pipeline->shader->slot.id == _sg.wgpu.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(ub_index < _sg.wgpu.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); + SOKOL_ASSERT(data->size <= _sg.wgpu.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); + SOKOL_ASSERT(data->size <= _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE); + + _sg_stats_add(wgpu.uniforms.num_set_bindgroup, 1); + memcpy(_sg.wgpu.uniform.staging + _sg.wgpu.uniform.offset, data->ptr, data->size); + _sg.wgpu.uniform.bind.offsets[stage_index][ub_index] = _sg.wgpu.uniform.offset; + _sg.wgpu.uniform.offset = _sg_roundup_u32(_sg.wgpu.uniform.offset + (uint32_t)data->size, alignment); + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, + _SG_WGPU_UNIFORM_BINDGROUP_INDEX, + _sg.wgpu.uniform.bind.group, + SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS, + &_sg.wgpu.uniform.bind.offsets[0][0]); +} + +_SOKOL_PRIVATE void _sg_wgpu_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.wgpu.pass_enc); + SOKOL_ASSERT(_sg.wgpu.cur_pipeline && (_sg.wgpu.cur_pipeline->slot.id == _sg.wgpu.cur_pipeline_id.id)); + if (SG_INDEXTYPE_NONE != _sg.wgpu.cur_pipeline->cmn.index_type) { + wgpuRenderPassEncoderDrawIndexed(_sg.wgpu.pass_enc, (uint32_t)num_elements, (uint32_t)num_instances, (uint32_t)base_element, 0, 0); + } else { + wgpuRenderPassEncoderDraw(_sg.wgpu.pass_enc, (uint32_t)num_elements, (uint32_t)num_instances, (uint32_t)base_element, 0); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + SOKOL_ASSERT(buf); + _sg_wgpu_copy_buffer_data(buf, 0, data); +} + +_SOKOL_PRIVATE void _sg_wgpu_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + _SOKOL_UNUSED(new_frame); + _sg_wgpu_copy_buffer_data(buf, (uint64_t)buf->cmn.append_pos, data); +} + +_SOKOL_PRIVATE void _sg_wgpu_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + _sg_wgpu_copy_image_data(img, img->wgpu.tex, data); +} +#endif + +// ██████ ███████ ███ ██ ███████ ██████ ██ ██████ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ███ █████ ██ ██ ██ █████ ██████ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ███████ ██ ████ ███████ ██ ██ ██ ██████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>generic backend +static inline void _sg_setup_backend(const sg_desc* desc) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_setup_backend(desc); + #elif defined(SOKOL_METAL) + _sg_mtl_setup_backend(desc); + #elif defined(SOKOL_D3D11) + _sg_d3d11_setup_backend(desc); + #elif defined(SOKOL_WGPU) + _sg_wgpu_setup_backend(desc); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_setup_backend(desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_backend(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_backend(); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_backend(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_backend(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_backend(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_backend(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_reset_state_cache(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_reset_state_cache(); + #elif defined(SOKOL_METAL) + _sg_mtl_reset_state_cache(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_reset_state_cache(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_reset_state_cache(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_reset_state_cache(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_buffer(buf, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_buffer(buf, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_buffer(buf, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_buffer(buf, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_buffer(buf, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_buffer(_sg_buffer_t* buf) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_buffer(buf); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_buffer(buf); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_buffer(buf); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_buffer(buf); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_buffer(buf); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_image(img, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_image(img, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_image(img, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_image(img, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_image(img, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_image(_sg_image_t* img) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_image(img); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_image(img); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_image(img); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_image(img); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_image(img); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_sampler(smp, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_sampler(smp, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_sampler(smp, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_sampler(smp, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_sampler(smp, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_sampler(_sg_sampler_t* smp) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_sampler(smp); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_sampler(smp); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_sampler(smp); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_sampler(smp); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_sampler(smp); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_shader(shd, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_shader(shd, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_shader(shd, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_shader(shd, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_shader(shd, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_shader(_sg_shader_t* shd) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_shader(shd); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_shader(shd); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_shader(shd); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_shader(shd); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_shader(shd); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_pipeline(pip, shd, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_pipeline(pip, shd, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_pipeline(pip, shd, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_pipeline(pip, shd, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_pipeline(pip, shd, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_pipeline(_sg_pipeline_t* pip) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_pipeline(pip); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_pipeline(pip); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_pipeline(pip); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_pipeline(pip); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_pipeline(pip); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_attachments(_sg_attachments_t* atts, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_image, const sg_attachments_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_attachments(atts, color_images, resolve_images, ds_image, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_attachments(atts, color_images, resolve_images, ds_image, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_attachments(atts, color_images, resolve_images, ds_image, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_attachments(atts, color_images, resolve_images, ds_image, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_attachments(atts, color_images, resolve_images, ds_image, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_attachments(_sg_attachments_t* atts) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_attachments(atts); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_attachments(atts); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_attachments(atts); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_discard_attachments(atts); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_attachments(atts); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline _sg_image_t* _sg_attachments_color_image(const _sg_attachments_t* atts, int index) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_attachments_color_image(atts, index); + #elif defined(SOKOL_METAL) + return _sg_mtl_attachments_color_image(atts, index); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_attachments_color_image(atts, index); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_attachments_color_image(atts, index); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_attachments_color_image(atts, index); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline _sg_image_t* _sg_attachments_resolve_image(const _sg_attachments_t* atts, int index) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_attachments_resolve_image(atts, index); + #elif defined(SOKOL_METAL) + return _sg_mtl_attachments_resolve_image(atts, index); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_attachments_resolve_image(atts, index); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_attachments_resolve_image(atts, index); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_attachments_resolve_image(atts, index); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline _sg_image_t* _sg_attachments_ds_image(const _sg_attachments_t* atts) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_attachments_ds_image(atts); + #elif defined(SOKOL_METAL) + return _sg_mtl_attachments_ds_image(atts); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_attachments_ds_image(atts); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_attachments_ds_image(atts); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_attachments_ds_image(atts); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_begin_pass(const sg_pass* pass) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_begin_pass(pass); + #elif defined(SOKOL_METAL) + _sg_mtl_begin_pass(pass); + #elif defined(SOKOL_D3D11) + _sg_d3d11_begin_pass(pass); + #elif defined(SOKOL_WGPU) + _sg_wgpu_begin_pass(pass); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_begin_pass(pass); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_end_pass(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_end_pass(); + #elif defined(SOKOL_METAL) + _sg_mtl_end_pass(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_end_pass(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_end_pass(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_end_pass(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_viewport(x, y, w, h, origin_top_left); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_scissor_rect(x, y, w, h, origin_top_left); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_pipeline(_sg_pipeline_t* pip) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_pipeline(pip); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_pipeline(pip); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_pipeline(pip); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_pipeline(pip); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_pipeline(pip); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline bool _sg_apply_bindings(_sg_bindings_t* bnd) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_apply_bindings(bnd); + #elif defined(SOKOL_METAL) + return _sg_mtl_apply_bindings(bnd); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_apply_bindings(bnd); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_apply_bindings(bnd); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_apply_bindings(bnd); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_uniforms(stage_index, ub_index, data); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_uniforms(stage_index, ub_index, data); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_uniforms(stage_index, ub_index, data); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_uniforms(stage_index, ub_index, data); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_uniforms(stage_index, ub_index, data); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_draw(int base_element, int num_elements, int num_instances) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_draw(base_element, num_elements, num_instances); + #elif defined(SOKOL_METAL) + _sg_mtl_draw(base_element, num_elements, num_instances); + #elif defined(SOKOL_D3D11) + _sg_d3d11_draw(base_element, num_elements, num_instances); + #elif defined(SOKOL_WGPU) + _sg_wgpu_draw(base_element, num_elements, num_instances); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_draw(base_element, num_elements, num_instances); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_commit(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_commit(); + #elif defined(SOKOL_METAL) + _sg_mtl_commit(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_commit(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_commit(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_commit(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_update_buffer(buf, data); + #elif defined(SOKOL_METAL) + _sg_mtl_update_buffer(buf, data); + #elif defined(SOKOL_D3D11) + _sg_d3d11_update_buffer(buf, data); + #elif defined(SOKOL_WGPU) + _sg_wgpu_update_buffer(buf, data); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_update_buffer(buf, data); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_append_buffer(buf, data, new_frame); + #elif defined(SOKOL_METAL) + _sg_mtl_append_buffer(buf, data, new_frame); + #elif defined(SOKOL_D3D11) + _sg_d3d11_append_buffer(buf, data, new_frame); + #elif defined(SOKOL_WGPU) + _sg_wgpu_append_buffer(buf, data, new_frame); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_append_buffer(buf, data, new_frame); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_update_image(_sg_image_t* img, const sg_image_data* data) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_update_image(img, data); + #elif defined(SOKOL_METAL) + _sg_mtl_update_image(img, data); + #elif defined(SOKOL_D3D11) + _sg_d3d11_update_image(img, data); + #elif defined(SOKOL_WGPU) + _sg_wgpu_update_image(img, data); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_update_image(img, data); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_push_debug_group(const char* name) { + #if defined(SOKOL_METAL) + _sg_mtl_push_debug_group(name); + #else + _SOKOL_UNUSED(name); + #endif +} + +static inline void _sg_pop_debug_group(void) { + #if defined(SOKOL_METAL) + _sg_mtl_pop_debug_group(); + #endif +} + +// ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ +// +// >>pool +_SOKOL_PRIVATE void _sg_init_pool(_sg_pool_t* pool, int num) { + SOKOL_ASSERT(pool && (num >= 1)); + // slot 0 is reserved for the 'invalid id', so bump the pool size by 1 + pool->size = num + 1; + pool->queue_top = 0; + // generation counters indexable by pool slot index, slot 0 is reserved + size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size; + pool->gen_ctrs = (uint32_t*)_sg_malloc_clear(gen_ctrs_size); + // it's not a bug to only reserve 'num' here + pool->free_queue = (int*) _sg_malloc_clear(sizeof(int) * (size_t)num); + // never allocate the zero-th pool item since the invalid id is 0 + for (int i = pool->size-1; i >= 1; i--) { + pool->free_queue[pool->queue_top++] = i; + } +} + +_SOKOL_PRIVATE void _sg_discard_pool(_sg_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + _sg_free(pool->free_queue); + pool->free_queue = 0; + SOKOL_ASSERT(pool->gen_ctrs); + _sg_free(pool->gen_ctrs); + pool->gen_ctrs = 0; + pool->size = 0; + pool->queue_top = 0; +} + +_SOKOL_PRIVATE int _sg_pool_alloc_index(_sg_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } else { + // pool exhausted + return _SG_INVALID_SLOT_INDEX; + } +} + +_SOKOL_PRIVATE void _sg_pool_free_index(_sg_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_ASSERT(pool->queue_top < pool->size); + #ifdef SOKOL_DEBUG + // debug check against double-free + for (int i = 0; i < pool->queue_top; i++) { + SOKOL_ASSERT(pool->free_queue[i] != slot_index); + } + #endif + pool->free_queue[pool->queue_top++] = slot_index; + SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); +} + +_SOKOL_PRIVATE void _sg_reset_slot(_sg_slot_t* slot) { + SOKOL_ASSERT(slot); + _sg_clear(slot, sizeof(_sg_slot_t)); +} + +_SOKOL_PRIVATE void _sg_reset_buffer_to_alloc_state(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + _sg_slot_t slot = buf->slot; + _sg_clear(buf, sizeof(*buf)); + buf->slot = slot; + buf->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_image_to_alloc_state(_sg_image_t* img) { + SOKOL_ASSERT(img); + _sg_slot_t slot = img->slot; + _sg_clear(img, sizeof(*img)); + img->slot = slot; + img->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_sampler_to_alloc_state(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + _sg_slot_t slot = smp->slot; + _sg_clear(smp, sizeof(*smp)); + smp->slot = slot; + smp->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_shader_to_alloc_state(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _sg_slot_t slot = shd->slot; + _sg_clear(shd, sizeof(*shd)); + shd->slot = slot; + shd->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_pipeline_to_alloc_state(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _sg_slot_t slot = pip->slot; + _sg_clear(pip, sizeof(*pip)); + pip->slot = slot; + pip->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_attachments_to_alloc_state(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts); + _sg_slot_t slot = atts->slot; + _sg_clear(atts, sizeof(*atts)); + atts->slot = slot; + atts->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_setup_pools(_sg_pools_t* p, const sg_desc* desc) { + SOKOL_ASSERT(p); + SOKOL_ASSERT(desc); + // note: the pools here will have an additional item, since slot 0 is reserved + SOKOL_ASSERT((desc->buffer_pool_size > 0) && (desc->buffer_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->buffer_pool, desc->buffer_pool_size); + size_t buffer_pool_byte_size = sizeof(_sg_buffer_t) * (size_t)p->buffer_pool.size; + p->buffers = (_sg_buffer_t*) _sg_malloc_clear(buffer_pool_byte_size); + + SOKOL_ASSERT((desc->image_pool_size > 0) && (desc->image_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->image_pool, desc->image_pool_size); + size_t image_pool_byte_size = sizeof(_sg_image_t) * (size_t)p->image_pool.size; + p->images = (_sg_image_t*) _sg_malloc_clear(image_pool_byte_size); + + SOKOL_ASSERT((desc->sampler_pool_size > 0) && (desc->sampler_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->sampler_pool, desc->sampler_pool_size); + size_t sampler_pool_byte_size = sizeof(_sg_sampler_t) * (size_t)p->sampler_pool.size; + p->samplers = (_sg_sampler_t*) _sg_malloc_clear(sampler_pool_byte_size); + + SOKOL_ASSERT((desc->shader_pool_size > 0) && (desc->shader_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->shader_pool, desc->shader_pool_size); + size_t shader_pool_byte_size = sizeof(_sg_shader_t) * (size_t)p->shader_pool.size; + p->shaders = (_sg_shader_t*) _sg_malloc_clear(shader_pool_byte_size); + + SOKOL_ASSERT((desc->pipeline_pool_size > 0) && (desc->pipeline_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->pipeline_pool, desc->pipeline_pool_size); + size_t pipeline_pool_byte_size = sizeof(_sg_pipeline_t) * (size_t)p->pipeline_pool.size; + p->pipelines = (_sg_pipeline_t*) _sg_malloc_clear(pipeline_pool_byte_size); + + SOKOL_ASSERT((desc->attachments_pool_size > 0) && (desc->attachments_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->attachments_pool, desc->attachments_pool_size); + size_t attachments_pool_byte_size = sizeof(_sg_attachments_t) * (size_t)p->attachments_pool.size; + p->attachments = (_sg_attachments_t*) _sg_malloc_clear(attachments_pool_byte_size); +} + +_SOKOL_PRIVATE void _sg_discard_pools(_sg_pools_t* p) { + SOKOL_ASSERT(p); + _sg_free(p->attachments); p->attachments = 0; + _sg_free(p->pipelines); p->pipelines = 0; + _sg_free(p->shaders); p->shaders = 0; + _sg_free(p->samplers); p->samplers = 0; + _sg_free(p->images); p->images = 0; + _sg_free(p->buffers); p->buffers = 0; + _sg_discard_pool(&p->attachments_pool); + _sg_discard_pool(&p->pipeline_pool); + _sg_discard_pool(&p->shader_pool); + _sg_discard_pool(&p->sampler_pool); + _sg_discard_pool(&p->image_pool); + _sg_discard_pool(&p->buffer_pool); +} + +/* allocate the slot at slot_index: + - bump the slot's generation counter + - create a resource id from the generation counter and slot index + - set the slot's id to this id + - set the slot's state to ALLOC + - return the resource id +*/ +_SOKOL_PRIVATE uint32_t _sg_slot_alloc(_sg_pool_t* pool, _sg_slot_t* slot, int slot_index) { + /* FIXME: add handling for an overflowing generation counter, + for now, just overflow (another option is to disable + the slot) + */ + SOKOL_ASSERT(pool && pool->gen_ctrs); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(slot->id == SG_INVALID_ID); + SOKOL_ASSERT(slot->state == SG_RESOURCESTATE_INITIAL); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + slot->id = (ctr<<_SG_SLOT_SHIFT)|(slot_index & _SG_SLOT_MASK); + slot->state = SG_RESOURCESTATE_ALLOC; + return slot->id; +} + +// extract slot index from id +_SOKOL_PRIVATE int _sg_slot_index(uint32_t id) { + int slot_index = (int) (id & _SG_SLOT_MASK); + SOKOL_ASSERT(_SG_INVALID_SLOT_INDEX != slot_index); + return slot_index; +} + +// returns pointer to resource by id without matching id check +_SOKOL_PRIVATE _sg_buffer_t* _sg_buffer_at(const _sg_pools_t* p, uint32_t buf_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != buf_id)); + int slot_index = _sg_slot_index(buf_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->buffer_pool.size)); + return &p->buffers[slot_index]; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_image_at(const _sg_pools_t* p, uint32_t img_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != img_id)); + int slot_index = _sg_slot_index(img_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->image_pool.size)); + return &p->images[slot_index]; +} + +_SOKOL_PRIVATE _sg_sampler_t* _sg_sampler_at(const _sg_pools_t* p, uint32_t smp_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != smp_id)); + int slot_index = _sg_slot_index(smp_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->sampler_pool.size)); + return &p->samplers[slot_index]; +} + +_SOKOL_PRIVATE _sg_shader_t* _sg_shader_at(const _sg_pools_t* p, uint32_t shd_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != shd_id)); + int slot_index = _sg_slot_index(shd_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->shader_pool.size)); + return &p->shaders[slot_index]; +} + +_SOKOL_PRIVATE _sg_pipeline_t* _sg_pipeline_at(const _sg_pools_t* p, uint32_t pip_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != pip_id)); + int slot_index = _sg_slot_index(pip_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->pipeline_pool.size)); + return &p->pipelines[slot_index]; +} + +_SOKOL_PRIVATE _sg_attachments_t* _sg_attachments_at(const _sg_pools_t* p, uint32_t atts_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != atts_id)); + int slot_index = _sg_slot_index(atts_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->attachments_pool.size)); + return &p->attachments[slot_index]; +} + +// returns pointer to resource with matching id check, may return 0 +_SOKOL_PRIVATE _sg_buffer_t* _sg_lookup_buffer(const _sg_pools_t* p, uint32_t buf_id) { + if (SG_INVALID_ID != buf_id) { + _sg_buffer_t* buf = _sg_buffer_at(p, buf_id); + if (buf->slot.id == buf_id) { + return buf; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_lookup_image(const _sg_pools_t* p, uint32_t img_id) { + if (SG_INVALID_ID != img_id) { + _sg_image_t* img = _sg_image_at(p, img_id); + if (img->slot.id == img_id) { + return img; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_sampler_t* _sg_lookup_sampler(const _sg_pools_t* p, uint32_t smp_id) { + if (SG_INVALID_ID != smp_id) { + _sg_sampler_t* smp = _sg_sampler_at(p, smp_id); + if (smp->slot.id == smp_id) { + return smp; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_shader_t* _sg_lookup_shader(const _sg_pools_t* p, uint32_t shd_id) { + SOKOL_ASSERT(p); + if (SG_INVALID_ID != shd_id) { + _sg_shader_t* shd = _sg_shader_at(p, shd_id); + if (shd->slot.id == shd_id) { + return shd; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_pipeline_t* _sg_lookup_pipeline(const _sg_pools_t* p, uint32_t pip_id) { + SOKOL_ASSERT(p); + if (SG_INVALID_ID != pip_id) { + _sg_pipeline_t* pip = _sg_pipeline_at(p, pip_id); + if (pip->slot.id == pip_id) { + return pip; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_attachments_t* _sg_lookup_attachments(const _sg_pools_t* p, uint32_t atts_id) { + SOKOL_ASSERT(p); + if (SG_INVALID_ID != atts_id) { + _sg_attachments_t* atts = _sg_attachments_at(p, atts_id); + if (atts->slot.id == atts_id) { + return atts; + } + } + return 0; +} + +_SOKOL_PRIVATE void _sg_discard_all_resources(_sg_pools_t* p) { + /* this is a bit dumb since it loops over all pool slots to + find the occupied slots, on the other hand it is only ever + executed at shutdown + NOTE: ONLY EXECUTE THIS AT SHUTDOWN + ...because the free queues will not be reset + and the resource slots not be cleared! + */ + for (int i = 1; i < p->buffer_pool.size; i++) { + sg_resource_state state = p->buffers[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_buffer(&p->buffers[i]); + } + } + for (int i = 1; i < p->image_pool.size; i++) { + sg_resource_state state = p->images[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_image(&p->images[i]); + } + } + for (int i = 1; i < p->sampler_pool.size; i++) { + sg_resource_state state = p->samplers[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_sampler(&p->samplers[i]); + } + } + for (int i = 1; i < p->shader_pool.size; i++) { + sg_resource_state state = p->shaders[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_shader(&p->shaders[i]); + } + } + for (int i = 1; i < p->pipeline_pool.size; i++) { + sg_resource_state state = p->pipelines[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_pipeline(&p->pipelines[i]); + } + } + for (int i = 1; i < p->attachments_pool.size; i++) { + sg_resource_state state = p->attachments[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_attachments(&p->attachments[i]); + } + } +} + +// ██ ██ █████ ██ ██ ██████ █████ ████████ ██ ██████ ███ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ███████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ████ ██ ██ ███████ ██ ██████ ██ ██ ██ ██ ██████ ██ ████ +// +// >>validation +#if defined(SOKOL_DEBUG) +_SOKOL_PRIVATE void _sg_validate_begin(void) { + _sg.validate_error = SG_LOGITEM_OK; +} + +_SOKOL_PRIVATE bool _sg_validate_end(void) { + if (_sg.validate_error != SG_LOGITEM_OK) { + #if !defined(SOKOL_VALIDATE_NON_FATAL) + _SG_PANIC(VALIDATION_FAILED); + return false; + #else + return false; + #endif + } else { + return true; + } +} +#endif + +_SOKOL_PRIVATE bool _sg_validate_buffer_desc(const sg_buffer_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_BUFFERDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_BUFFERDESC_CANARY); + _SG_VALIDATE(desc->size > 0, VALIDATE_BUFFERDESC_SIZE); + bool injected = (0 != desc->gl_buffers[0]) || + (0 != desc->mtl_buffers[0]) || + (0 != desc->d3d11_buffer) || + (0 != desc->wgpu_buffer); + if (!injected && (desc->usage == SG_USAGE_IMMUTABLE)) { + _SG_VALIDATE((0 != desc->data.ptr) && (desc->data.size > 0), VALIDATE_BUFFERDESC_DATA); + _SG_VALIDATE(desc->size == desc->data.size, VALIDATE_BUFFERDESC_DATA_SIZE); + } else { + _SG_VALIDATE(0 == desc->data.ptr, VALIDATE_BUFFERDESC_NO_DATA); + } + if (desc->type == SG_BUFFERTYPE_STORAGEBUFFER) { + _SG_VALIDATE(_sg.features.storage_buffer, VALIDATE_BUFFERDESC_STORAGEBUFFER_SUPPORTED); + _SG_VALIDATE(_sg_multiple_u64(desc->size, 4), VALIDATE_BUFFERDESC_STORAGEBUFFER_SIZE_MULTIPLE_4); + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE void _sg_validate_image_data(const sg_image_data* data, sg_pixel_format fmt, int width, int height, int num_faces, int num_mips, int num_slices) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(data); + _SOKOL_UNUSED(fmt); + _SOKOL_UNUSED(width); + _SOKOL_UNUSED(height); + _SOKOL_UNUSED(num_faces); + _SOKOL_UNUSED(num_mips); + _SOKOL_UNUSED(num_slices); + #else + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < num_mips; mip_index++) { + const bool has_data = data->subimage[face_index][mip_index].ptr != 0; + const bool has_size = data->subimage[face_index][mip_index].size > 0; + _SG_VALIDATE(has_data && has_size, VALIDATE_IMAGEDATA_NODATA); + const int mip_width = _sg_miplevel_dim(width, mip_index); + const int mip_height = _sg_miplevel_dim(height, mip_index); + const int bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, 1); + const int expected_size = bytes_per_slice * num_slices; + _SG_VALIDATE(expected_size == (int)data->subimage[face_index][mip_index].size, VALIDATE_IMAGEDATA_DATA_SIZE); + } + } + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_IMAGEDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_IMAGEDESC_CANARY); + _SG_VALIDATE(desc->width > 0, VALIDATE_IMAGEDESC_WIDTH); + _SG_VALIDATE(desc->height > 0, VALIDATE_IMAGEDESC_HEIGHT); + const sg_pixel_format fmt = desc->pixel_format; + const sg_usage usage = desc->usage; + const bool injected = (0 != desc->gl_textures[0]) || + (0 != desc->mtl_textures[0]) || + (0 != desc->d3d11_texture) || + (0 != desc->wgpu_texture); + if (_sg_is_depth_or_depth_stencil_format(fmt)) { + _SG_VALIDATE(desc->type != SG_IMAGETYPE_3D, VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE); + } + if (desc->render_target) { + SOKOL_ASSERT(((int)fmt >= 0) && ((int)fmt < _SG_PIXELFORMAT_NUM)); + _SG_VALIDATE(_sg.formats[fmt].render, VALIDATE_IMAGEDESC_RT_PIXELFORMAT); + _SG_VALIDATE(usage == SG_USAGE_IMMUTABLE, VALIDATE_IMAGEDESC_RT_IMMUTABLE); + _SG_VALIDATE(desc->data.subimage[0][0].ptr==0, VALIDATE_IMAGEDESC_RT_NO_DATA); + if (desc->sample_count > 1) { + _SG_VALIDATE(_sg.formats[fmt].msaa, VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT); + _SG_VALIDATE(desc->num_mipmaps == 1, VALIDATE_IMAGEDESC_MSAA_NUM_MIPMAPS); + _SG_VALIDATE(desc->type != SG_IMAGETYPE_3D, VALIDATE_IMAGEDESC_MSAA_3D_IMAGE); + } + } else { + _SG_VALIDATE(desc->sample_count == 1, VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT); + const bool valid_nonrt_fmt = !_sg_is_valid_rendertarget_depth_format(fmt); + _SG_VALIDATE(valid_nonrt_fmt, VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT); + const bool is_compressed = _sg_is_compressed_pixel_format(desc->pixel_format); + const bool is_immutable = (usage == SG_USAGE_IMMUTABLE); + if (is_compressed) { + _SG_VALIDATE(is_immutable, VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE); + } + if (!injected && is_immutable) { + // image desc must have valid data + _sg_validate_image_data(&desc->data, + desc->pixel_format, + desc->width, + desc->height, + (desc->type == SG_IMAGETYPE_CUBE) ? 6 : 1, + desc->num_mipmaps, + desc->num_slices); + } else { + // image desc must not have data + for (int face_index = 0; face_index < SG_CUBEFACE_NUM; face_index++) { + for (int mip_index = 0; mip_index < SG_MAX_MIPMAPS; mip_index++) { + const bool no_data = 0 == desc->data.subimage[face_index][mip_index].ptr; + const bool no_size = 0 == desc->data.subimage[face_index][mip_index].size; + if (injected) { + _SG_VALIDATE(no_data && no_size, VALIDATE_IMAGEDESC_INJECTED_NO_DATA); + } + if (!is_immutable) { + _SG_VALIDATE(no_data && no_size, VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA); + } + } + } + } + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_sampler_desc(const sg_sampler_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_SAMPLERDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_SAMPLERDESC_CANARY); + _SG_VALIDATE(desc->min_filter != SG_FILTER_NONE, VALIDATE_SAMPLERDESC_MINFILTER_NONE); + _SG_VALIDATE(desc->mag_filter != SG_FILTER_NONE, VALIDATE_SAMPLERDESC_MAGFILTER_NONE); + // restriction from WebGPU: when anisotropy > 1, all filters must be linear + if (desc->max_anisotropy > 1) { + _SG_VALIDATE((desc->min_filter == SG_FILTER_LINEAR) + && (desc->mag_filter == SG_FILTER_LINEAR) + && (desc->mipmap_filter == SG_FILTER_LINEAR), + VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING); + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_SHADERDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_SHADERDESC_CANARY); + #if defined(SOKOL_GLCORE) || defined(SOKOL_GLES3) || defined(SOKOL_WGPU) + // on GL or WebGPU, must provide shader source code + _SG_VALIDATE(0 != desc->vs.source, VALIDATE_SHADERDESC_SOURCE); + _SG_VALIDATE(0 != desc->fs.source, VALIDATE_SHADERDESC_SOURCE); + #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) + // on Metal or D3D11, must provide shader source code or byte code + _SG_VALIDATE((0 != desc->vs.source)||(0 != desc->vs.bytecode.ptr), VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); + _SG_VALIDATE((0 != desc->fs.source)||(0 != desc->fs.bytecode.ptr), VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); + #else + // Dummy Backend, don't require source or bytecode + #endif + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + if (desc->attrs[i].name) { + _SG_VALIDATE(strlen(desc->attrs[i].name) < _SG_STRING_SIZE, VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); + } + if (desc->attrs[i].sem_name) { + _SG_VALIDATE(strlen(desc->attrs[i].sem_name) < _SG_STRING_SIZE, VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); + } + } + // if shader byte code, the size must also be provided + if (0 != desc->vs.bytecode.ptr) { + _SG_VALIDATE(desc->vs.bytecode.size > 0, VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + } + if (0 != desc->fs.bytecode.ptr) { + _SG_VALIDATE(desc->fs.bytecode.size > 0, VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + } + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == 0)? &desc->vs : &desc->fs; + bool uniform_blocks_continuous = true; + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + if (ub_desc->size > 0) { + _SG_VALIDATE(uniform_blocks_continuous, VALIDATE_SHADERDESC_NO_CONT_UBS); + #if defined(_SOKOL_ANY_GL) + bool uniforms_continuous = true; + uint32_t uniform_offset = 0; + int num_uniforms = 0; + for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { + const sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; + if (u_desc->type != SG_UNIFORMTYPE_INVALID) { + _SG_VALIDATE(uniforms_continuous, VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS); + #if defined(SOKOL_GLES3) + _SG_VALIDATE(0 != u_desc->name, VALIDATE_SHADERDESC_UB_MEMBER_NAME); + #endif + const int array_count = u_desc->array_count; + _SG_VALIDATE(array_count > 0, VALIDATE_SHADERDESC_UB_ARRAY_COUNT); + const uint32_t u_align = _sg_uniform_alignment(u_desc->type, array_count, ub_desc->layout); + const uint32_t u_size = _sg_uniform_size(u_desc->type, array_count, ub_desc->layout); + uniform_offset = _sg_align_u32(uniform_offset, u_align); + uniform_offset += u_size; + num_uniforms++; + // with std140, arrays are only allowed for FLOAT4, INT4, MAT4 + if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { + if (array_count > 1) { + _SG_VALIDATE((u_desc->type == SG_UNIFORMTYPE_FLOAT4) || (u_desc->type == SG_UNIFORMTYPE_INT4) || (u_desc->type == SG_UNIFORMTYPE_MAT4), VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE); + } + } + } else { + uniforms_continuous = false; + } + } + if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { + uniform_offset = _sg_align_u32(uniform_offset, 16); + } + _SG_VALIDATE((size_t)uniform_offset == ub_desc->size, VALIDATE_SHADERDESC_UB_SIZE_MISMATCH); + _SG_VALIDATE(num_uniforms > 0, VALIDATE_SHADERDESC_NO_UB_MEMBERS); + #endif + } else { + uniform_blocks_continuous = false; + } + } + bool storage_buffers_continuous = true; + for (int sbuf_index = 0; sbuf_index < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; sbuf_index++) { + const sg_shader_storage_buffer_desc* sbuf_desc = &stage_desc->storage_buffers[sbuf_index]; + if (sbuf_desc->used) { + _SG_VALIDATE(storage_buffers_continuous, VALIDATE_SHADERDESC_NO_CONT_STORAGEBUFFERS); + _SG_VALIDATE(sbuf_desc->readonly, VALIDATE_SHADERDESC_STORAGEBUFFER_READONLY); + } else { + storage_buffers_continuous = false; + } + } + bool images_continuous = true; + int num_images = 0; + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (img_desc->used) { + _SG_VALIDATE(images_continuous, VALIDATE_SHADERDESC_NO_CONT_IMAGES); + num_images++; + } else { + images_continuous = false; + } + } + bool samplers_continuous = true; + int num_samplers = 0; + for (int smp_index = 0; smp_index < SG_MAX_SHADERSTAGE_SAMPLERS; smp_index++) { + const sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_index]; + if (smp_desc->used) { + _SG_VALIDATE(samplers_continuous, VALIDATE_SHADERDESC_NO_CONT_SAMPLERS); + num_samplers++; + } else { + samplers_continuous = false; + } + } + bool image_samplers_continuous = true; + int num_image_samplers = 0; + for (int img_smp_index = 0; img_smp_index < SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS; img_smp_index++) { + const sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_index]; + if (img_smp_desc->used) { + _SG_VALIDATE(image_samplers_continuous, VALIDATE_SHADERDESC_NO_CONT_IMAGE_SAMPLER_PAIRS); + num_image_samplers++; + const bool img_slot_in_range = (img_smp_desc->image_slot >= 0) && (img_smp_desc->image_slot < SG_MAX_SHADERSTAGE_IMAGES); + const bool smp_slot_in_range = (img_smp_desc->sampler_slot >= 0) && (img_smp_desc->sampler_slot < SG_MAX_SHADERSTAGE_SAMPLERS); + _SG_VALIDATE(img_slot_in_range && (img_smp_desc->image_slot < num_images), VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_IMAGE_SLOT_OUT_OF_RANGE); + _SG_VALIDATE(smp_slot_in_range && (img_smp_desc->sampler_slot < num_samplers), VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_IMAGE_SLOT_OUT_OF_RANGE); + #if defined(_SOKOL_ANY_GL) + _SG_VALIDATE(img_smp_desc->glsl_name != 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_NAME_REQUIRED_FOR_GL); + #endif + if (img_slot_in_range && smp_slot_in_range) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_smp_desc->image_slot]; + const sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[img_smp_desc->sampler_slot]; + const bool needs_nonfiltering = (img_desc->sample_type == SG_IMAGESAMPLETYPE_UINT) + || (img_desc->sample_type == SG_IMAGESAMPLETYPE_SINT) + || (img_desc->sample_type == SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT); + const bool needs_comparison = img_desc->sample_type == SG_IMAGESAMPLETYPE_DEPTH; + if (needs_nonfiltering) { + _SG_VALIDATE(needs_nonfiltering && (smp_desc->sampler_type == SG_SAMPLERTYPE_NONFILTERING), VALIDATE_SHADERDESC_NONFILTERING_SAMPLER_REQUIRED); + } + if (needs_comparison) { + _SG_VALIDATE(needs_comparison && (smp_desc->sampler_type == SG_SAMPLERTYPE_COMPARISON), VALIDATE_SHADERDESC_COMPARISON_SAMPLER_REQUIRED); + } + } + } else { + _SG_VALIDATE(img_smp_desc->glsl_name == 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_NAME_BUT_NOT_USED); + _SG_VALIDATE(img_smp_desc->image_slot == 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_IMAGE_BUT_NOT_USED); + _SG_VALIDATE(img_smp_desc->sampler_slot == 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_SAMPLER_BUT_NOT_USED); + image_samplers_continuous = false; + } + } + // each image and sampler must be referenced by an image sampler + const uint32_t expected_img_slot_mask = (uint32_t)((1 << num_images) - 1); + const uint32_t expected_smp_slot_mask = (uint32_t)((1 << num_samplers) - 1); + uint32_t actual_img_slot_mask = 0; + uint32_t actual_smp_slot_mask = 0; + for (int img_smp_index = 0; img_smp_index < num_image_samplers; img_smp_index++) { + const sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_index]; + actual_img_slot_mask |= (1 << ((uint32_t)img_smp_desc->image_slot & 31)); + actual_smp_slot_mask |= (1 << ((uint32_t)img_smp_desc->sampler_slot & 31)); + } + _SG_VALIDATE(expected_img_slot_mask == actual_img_slot_mask, VALIDATE_SHADERDESC_IMAGE_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS); + _SG_VALIDATE(expected_smp_slot_mask == actual_smp_slot_mask, VALIDATE_SHADERDESC_SAMPLER_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS); + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_PIPELINEDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_PIPELINEDESC_CANARY); + _SG_VALIDATE(desc->shader.id != SG_INVALID_ID, VALIDATE_PIPELINEDESC_SHADER); + for (int buf_index = 0; buf_index < SG_MAX_VERTEX_BUFFERS; buf_index++) { + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[buf_index]; + if (l_state->stride == 0) { + continue; + } + _SG_VALIDATE(_sg_multiple_u64((uint64_t)l_state->stride, 4), VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); + } + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); + _SG_VALIDATE(0 != shd, VALIDATE_PIPELINEDESC_SHADER); + if (shd) { + _SG_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_PIPELINEDESC_SHADER); + bool attrs_cont = true; + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + attrs_cont = false; + continue; + } + _SG_VALIDATE(attrs_cont, VALIDATE_PIPELINEDESC_NO_CONT_ATTRS); + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + #if defined(SOKOL_D3D11) + // on D3D11, semantic names (and semantic indices) must be provided + _SG_VALIDATE(!_sg_strempty(&shd->d3d11.attrs[attr_index].sem_name), VALIDATE_PIPELINEDESC_ATTR_SEMANTICS); + #endif + } + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_attachments_desc(const sg_attachments_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_ATTACHMENTSDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_ATTACHMENTSDESC_CANARY); + bool atts_cont = true; + int color_width = -1, color_height = -1, color_sample_count = -1; + bool has_color_atts = false; + for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { + const sg_attachment_desc* att = &desc->colors[att_index]; + if (att->image.id == SG_INVALID_ID) { + atts_cont = false; + continue; + } + _SG_VALIDATE(atts_cont, VALIDATE_ATTACHMENTSDESC_NO_CONT_COLOR_ATTS); + has_color_atts = true; + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); + _SG_VALIDATE(img, VALIDATE_ATTACHMENTSDESC_IMAGE); + if (0 != img) { + _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ATTACHMENTSDESC_IMAGE); + _SG_VALIDATE(img->cmn.render_target, VALIDATE_ATTACHMENTSDESC_IMAGE_NO_RT); + _SG_VALIDATE(att->mip_level < img->cmn.num_mipmaps, VALIDATE_ATTACHMENTSDESC_MIPLEVEL); + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + _SG_VALIDATE(att->slice < 6, VALIDATE_ATTACHMENTSDESC_FACE); + } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_ATTACHMENTSDESC_LAYER); + } else if (img->cmn.type == SG_IMAGETYPE_3D) { + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_ATTACHMENTSDESC_SLICE); + } + if (att_index == 0) { + color_width = _sg_miplevel_dim(img->cmn.width, att->mip_level); + color_height = _sg_miplevel_dim(img->cmn.height, att->mip_level); + color_sample_count = img->cmn.sample_count; + } else { + _SG_VALIDATE(color_width == _sg_miplevel_dim(img->cmn.width, att->mip_level), VALIDATE_ATTACHMENTSDESC_IMAGE_SIZES); + _SG_VALIDATE(color_height == _sg_miplevel_dim(img->cmn.height, att->mip_level), VALIDATE_ATTACHMENTSDESC_IMAGE_SIZES); + _SG_VALIDATE(color_sample_count == img->cmn.sample_count, VALIDATE_ATTACHMENTSDESC_IMAGE_SAMPLE_COUNTS); + } + _SG_VALIDATE(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format), VALIDATE_ATTACHMENTSDESC_COLOR_INV_PIXELFORMAT); + + // check resolve attachment + const sg_attachment_desc* res_att = &desc->resolves[att_index]; + if (res_att->image.id != SG_INVALID_ID) { + // associated color attachment must be MSAA + _SG_VALIDATE(img->cmn.sample_count > 1, VALIDATE_ATTACHMENTSDESC_RESOLVE_COLOR_IMAGE_MSAA); + const _sg_image_t* res_img = _sg_lookup_image(&_sg.pools, res_att->image.id); + _SG_VALIDATE(res_img, VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE); + if (res_img != 0) { + _SG_VALIDATE(res_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE); + _SG_VALIDATE(res_img->cmn.render_target, VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_NO_RT); + _SG_VALIDATE(res_img->cmn.sample_count == 1, VALIDATE_ATTACHMENTSDESC_RESOLVE_SAMPLE_COUNT); + _SG_VALIDATE(res_att->mip_level < res_img->cmn.num_mipmaps, VALIDATE_ATTACHMENTSDESC_RESOLVE_MIPLEVEL); + if (res_img->cmn.type == SG_IMAGETYPE_CUBE) { + _SG_VALIDATE(res_att->slice < 6, VALIDATE_ATTACHMENTSDESC_RESOLVE_FACE); + } else if (res_img->cmn.type == SG_IMAGETYPE_ARRAY) { + _SG_VALIDATE(res_att->slice < res_img->cmn.num_slices, VALIDATE_ATTACHMENTSDESC_RESOLVE_LAYER); + } else if (res_img->cmn.type == SG_IMAGETYPE_3D) { + _SG_VALIDATE(res_att->slice < res_img->cmn.num_slices, VALIDATE_ATTACHMENTSDESC_RESOLVE_SLICE); + } + _SG_VALIDATE(img->cmn.pixel_format == res_img->cmn.pixel_format, VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_FORMAT); + _SG_VALIDATE(color_width == _sg_miplevel_dim(res_img->cmn.width, res_att->mip_level), VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_SIZES); + _SG_VALIDATE(color_height == _sg_miplevel_dim(res_img->cmn.height, res_att->mip_level), VALIDATE_ATTACHMENTSDESC_RESOLVE_IMAGE_SIZES); + } + } + } + } + bool has_depth_stencil_att = false; + if (desc->depth_stencil.image.id != SG_INVALID_ID) { + const sg_attachment_desc* att = &desc->depth_stencil; + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); + _SG_VALIDATE(img, VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE); + has_depth_stencil_att = true; + if (img) { + _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE); + _SG_VALIDATE(att->mip_level < img->cmn.num_mipmaps, VALIDATE_ATTACHMENTSDESC_DEPTH_MIPLEVEL); + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + _SG_VALIDATE(att->slice < 6, VALIDATE_ATTACHMENTSDESC_DEPTH_FACE); + } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_ATTACHMENTSDESC_DEPTH_LAYER); + } else if (img->cmn.type == SG_IMAGETYPE_3D) { + // NOTE: this can't actually happen because of VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_ATTACHMENTSDESC_DEPTH_SLICE); + } + _SG_VALIDATE(img->cmn.render_target, VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_NO_RT); + _SG_VALIDATE((color_width == -1) || (color_width == _sg_miplevel_dim(img->cmn.width, att->mip_level)), VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SIZES); + _SG_VALIDATE((color_height == -1) || (color_height == _sg_miplevel_dim(img->cmn.height, att->mip_level)), VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SIZES); + _SG_VALIDATE((color_sample_count == -1) || (color_sample_count == img->cmn.sample_count), VALIDATE_ATTACHMENTSDESC_DEPTH_IMAGE_SAMPLE_COUNT); + _SG_VALIDATE(_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format), VALIDATE_ATTACHMENTSDESC_DEPTH_INV_PIXELFORMAT); + } + } + _SG_VALIDATE(has_color_atts || has_depth_stencil_att, VALIDATE_ATTACHMENTSDESC_NO_ATTACHMENTS); + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_begin_pass(const sg_pass* pass) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(pass); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + _sg_validate_begin(); + _SG_VALIDATE(pass->_start_canary == 0, VALIDATE_BEGINPASS_CANARY); + _SG_VALIDATE(pass->_end_canary == 0, VALIDATE_BEGINPASS_CANARY); + if (pass->attachments.id == SG_INVALID_ID) { + // this is a swapchain pass + _SG_VALIDATE(pass->swapchain.width > 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH); + _SG_VALIDATE(pass->swapchain.height > 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT); + _SG_VALIDATE(pass->swapchain.sample_count > 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT); + _SG_VALIDATE(pass->swapchain.color_format > SG_PIXELFORMAT_NONE, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT); + // NOTE: depth buffer is optional, so depth_format is allowed to be invalid + // NOTE: the GL framebuffer handle may actually be 0 + #if defined(SOKOL_METAL) + _SG_VALIDATE(pass->swapchain.metal.current_drawable != 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE); + if (pass->swapchain.depth_format == SG_PIXELFORMAT_NONE) { + _SG_VALIDATE(pass->swapchain.metal.depth_stencil_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE_NOTSET); + } else { + _SG_VALIDATE(pass->swapchain.metal.depth_stencil_texture != 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE); + } + if (pass->swapchain.sample_count > 1) { + _SG_VALIDATE(pass->swapchain.metal.msaa_color_texture != 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE); + } else { + _SG_VALIDATE(pass->swapchain.metal.msaa_color_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE_NOTSET); + } + #elif defined(SOKOL_D3D11) + _SG_VALIDATE(pass->swapchain.d3d11.render_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW); + if (pass->swapchain.depth_format == SG_PIXELFORMAT_NONE) { + _SG_VALIDATE(pass->swapchain.d3d11.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW_NOTSET); + } else { + _SG_VALIDATE(pass->swapchain.d3d11.depth_stencil_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW); + } + if (pass->swapchain.sample_count > 1) { + _SG_VALIDATE(pass->swapchain.d3d11.resolve_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW); + } else { + _SG_VALIDATE(pass->swapchain.d3d11.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW_NOTSET); + } + #elif defined(SOKOL_WGPU) + _SG_VALIDATE(pass->swapchain.wgpu.render_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW); + if (pass->swapchain.depth_format == SG_PIXELFORMAT_NONE) { + _SG_VALIDATE(pass->swapchain.wgpu.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW_NOTSET); + } else { + _SG_VALIDATE(pass->swapchain.wgpu.depth_stencil_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW); + } + if (pass->swapchain.sample_count > 1) { + _SG_VALIDATE(pass->swapchain.wgpu.resolve_view != 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW); + } else { + _SG_VALIDATE(pass->swapchain.wgpu.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW_NOTSET); + } + #endif + } else { + // this is an 'offscreen pass' + const _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, pass->attachments.id); + if (atts) { + _SG_VALIDATE(atts->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_ATTACHMENTS_VALID); + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + const _sg_attachment_common_t* color_att = &atts->cmn.colors[i]; + const _sg_image_t* color_img = _sg_attachments_color_image(atts, i); + if (color_img) { + _SG_VALIDATE(color_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_COLOR_ATTACHMENT_IMAGE); + _SG_VALIDATE(color_img->slot.id == color_att->image_id.id, VALIDATE_BEGINPASS_COLOR_ATTACHMENT_IMAGE); + } + const _sg_attachment_common_t* resolve_att = &atts->cmn.resolves[i]; + const _sg_image_t* resolve_img = _sg_attachments_resolve_image(atts, i); + if (resolve_img) { + _SG_VALIDATE(resolve_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_RESOLVE_ATTACHMENT_IMAGE); + _SG_VALIDATE(resolve_img->slot.id == resolve_att->image_id.id, VALIDATE_BEGINPASS_RESOLVE_ATTACHMENT_IMAGE); + } + } + const _sg_image_t* ds_img = _sg_attachments_ds_image(atts); + if (ds_img) { + const _sg_attachment_common_t* att = &atts->cmn.depth_stencil; + _SG_VALIDATE(ds_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_DEPTHSTENCIL_ATTACHMENT_IMAGE); + _SG_VALIDATE(ds_img->slot.id == att->image_id.id, VALIDATE_BEGINPASS_DEPTHSTENCIL_ATTACHMENT_IMAGE); + } + } else { + _SG_VALIDATE(atts != 0, VALIDATE_BEGINPASS_ATTACHMENTS_EXISTS); + } + // swapchain params must be all zero! + _SG_VALIDATE(pass->swapchain.width == 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_WIDTH_NOTSET); + _SG_VALIDATE(pass->swapchain.height == 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_HEIGHT_NOTSET); + _SG_VALIDATE(pass->swapchain.sample_count == 0, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_SAMPLECOUNT_NOTSET); + _SG_VALIDATE(pass->swapchain.color_format == _SG_PIXELFORMAT_DEFAULT, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_COLORFORMAT_NOTSET); + _SG_VALIDATE(pass->swapchain.depth_format == _SG_PIXELFORMAT_DEFAULT, VALIDATE_BEGINPASS_SWAPCHAIN_EXPECT_DEPTHFORMAT_NOTSET); + #if defined(SOKOL_METAL) + _SG_VALIDATE(pass->swapchain.metal.current_drawable == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_CURRENTDRAWABLE_NOTSET); + _SG_VALIDATE(pass->swapchain.metal.depth_stencil_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_DEPTHSTENCILTEXTURE_NOTSET); + _SG_VALIDATE(pass->swapchain.metal.msaa_color_texture == 0, VALIDATE_BEGINPASS_SWAPCHAIN_METAL_EXPECT_MSAACOLORTEXTURE_NOTSET); + #elif defined(SOKOL_D3D11) + _SG_VALIDATE(pass->swapchain.d3d11.render_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RENDERVIEW_NOTSET); + _SG_VALIDATE(pass->swapchain.d3d11.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_DEPTHSTENCILVIEW_NOTSET); + _SG_VALIDATE(pass->swapchain.d3d11.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_D3D11_EXPECT_RESOLVEVIEW_NOTSET); + #elif defined(SOKOL_WGPU) + _SG_VALIDATE(pass->swapchain.wgpu.render_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RENDERVIEW_NOTSET); + _SG_VALIDATE(pass->swapchain.wgpu.depth_stencil_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_DEPTHSTENCILVIEW_NOTSET); + _SG_VALIDATE(pass->swapchain.wgpu.resolve_view == 0, VALIDATE_BEGINPASS_SWAPCHAIN_WGPU_EXPECT_RESOLVEVIEW_NOTSET); + #elif defined(_SOKOL_ANY_GL) + _SG_VALIDATE(pass->swapchain.gl.framebuffer == 0, VALIDATE_BEGINPASS_SWAPCHAIN_GL_EXPECT_FRAMEBUFFER_NOTSET); + #endif + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_pipeline(sg_pipeline pip_id) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(pip_id); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + _sg_validate_begin(); + // the pipeline object must be alive and valid + _SG_VALIDATE(pip_id.id != SG_INVALID_ID, VALIDATE_APIP_PIPELINE_VALID_ID); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + _SG_VALIDATE(pip != 0, VALIDATE_APIP_PIPELINE_EXISTS); + if (!pip) { + return _sg_validate_end(); + } + _SG_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_PIPELINE_VALID); + // the pipeline's shader must be alive and valid + SOKOL_ASSERT(pip->shader); + _SG_VALIDATE(pip->shader->slot.id == pip->cmn.shader_id.id, VALIDATE_APIP_SHADER_EXISTS); + _SG_VALIDATE(pip->shader->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_SHADER_VALID); + // check that pipeline attributes match current pass attributes + if (_sg.cur_pass.atts_id.id != SG_INVALID_ID) { + // an offscreen pass + const _sg_attachments_t* atts = _sg.cur_pass.atts; + SOKOL_ASSERT(atts); + _SG_VALIDATE(atts->slot.id == _sg.cur_pass.atts_id.id, VALIDATE_APIP_CURPASS_ATTACHMENTS_EXISTS); + _SG_VALIDATE(atts->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_CURPASS_ATTACHMENTS_VALID); + + _SG_VALIDATE(pip->cmn.color_count == atts->cmn.num_colors, VALIDATE_APIP_ATT_COUNT); + for (int i = 0; i < pip->cmn.color_count; i++) { + const _sg_image_t* att_img = _sg_attachments_color_image(atts, i); + _SG_VALIDATE(pip->cmn.colors[i].pixel_format == att_img->cmn.pixel_format, VALIDATE_APIP_COLOR_FORMAT); + _SG_VALIDATE(pip->cmn.sample_count == att_img->cmn.sample_count, VALIDATE_APIP_SAMPLE_COUNT); + } + const _sg_image_t* att_dsimg = _sg_attachments_ds_image(atts); + if (att_dsimg) { + _SG_VALIDATE(pip->cmn.depth.pixel_format == att_dsimg->cmn.pixel_format, VALIDATE_APIP_DEPTH_FORMAT); + } else { + _SG_VALIDATE(pip->cmn.depth.pixel_format == SG_PIXELFORMAT_NONE, VALIDATE_APIP_DEPTH_FORMAT); + } + } else { + // default pass + _SG_VALIDATE(pip->cmn.color_count == 1, VALIDATE_APIP_ATT_COUNT); + _SG_VALIDATE(pip->cmn.colors[0].pixel_format == _sg.cur_pass.swapchain.color_fmt, VALIDATE_APIP_COLOR_FORMAT); + _SG_VALIDATE(pip->cmn.depth.pixel_format == _sg.cur_pass.swapchain.depth_fmt, VALIDATE_APIP_DEPTH_FORMAT); + _SG_VALIDATE(pip->cmn.sample_count == _sg.cur_pass.swapchain.sample_count, VALIDATE_APIP_SAMPLE_COUNT); + } + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(bindings); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + _sg_validate_begin(); + + // a pipeline object must have been applied + _SG_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, VALIDATE_ABND_PIPELINE); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); + _SG_VALIDATE(pip != 0, VALIDATE_ABND_PIPELINE_EXISTS); + if (!pip) { + return _sg_validate_end(); + } + _SG_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ABND_PIPELINE_VALID); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + + // has expected vertex buffers, and vertex buffers still exist + for (int i = 0; i < SG_MAX_VERTEX_BUFFERS; i++) { + if (bindings->vertex_buffers[i].id != SG_INVALID_ID) { + _SG_VALIDATE(pip->cmn.vertex_buffer_layout_active[i], VALIDATE_ABND_VBS); + // buffers in vertex-buffer-slots must be of type SG_BUFFERTYPE_VERTEXBUFFER + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); + _SG_VALIDATE(buf != 0, VALIDATE_ABND_VB_EXISTS); + if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { + _SG_VALIDATE(SG_BUFFERTYPE_VERTEXBUFFER == buf->cmn.type, VALIDATE_ABND_VB_TYPE); + _SG_VALIDATE(!buf->cmn.append_overflow, VALIDATE_ABND_VB_OVERFLOW); + } + } else { + // vertex buffer provided in a slot which has no vertex layout in pipeline + _SG_VALIDATE(!pip->cmn.vertex_buffer_layout_active[i], VALIDATE_ABND_VBS); + } + } + + // index buffer expected or not, and index buffer still exists + if (pip->cmn.index_type == SG_INDEXTYPE_NONE) { + // pipeline defines non-indexed rendering, but index buffer provided + _SG_VALIDATE(bindings->index_buffer.id == SG_INVALID_ID, VALIDATE_ABND_IB); + } else { + // pipeline defines indexed rendering, but no index buffer provided + _SG_VALIDATE(bindings->index_buffer.id != SG_INVALID_ID, VALIDATE_ABND_NO_IB); + } + if (bindings->index_buffer.id != SG_INVALID_ID) { + // buffer in index-buffer-slot must be of type SG_BUFFERTYPE_INDEXBUFFER + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); + _SG_VALIDATE(buf != 0, VALIDATE_ABND_IB_EXISTS); + if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { + _SG_VALIDATE(SG_BUFFERTYPE_INDEXBUFFER == buf->cmn.type, VALIDATE_ABND_IB_TYPE); + _SG_VALIDATE(!buf->cmn.append_overflow, VALIDATE_ABND_IB_OVERFLOW); + } + } + + // has expected vertex shader images + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; + if (stage->images[i].image_type != _SG_IMAGETYPE_DEFAULT) { + _SG_VALIDATE(bindings->vs.images[i].id != SG_INVALID_ID, VALIDATE_ABND_VS_EXPECTED_IMAGE_BINDING); + if (bindings->vs.images[i].id != SG_INVALID_ID) { + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->vs.images[i].id); + _SG_VALIDATE(img != 0, VALIDATE_ABND_VS_IMG_EXISTS); + if (img && img->slot.state == SG_RESOURCESTATE_VALID) { + _SG_VALIDATE(img->cmn.type == stage->images[i].image_type, VALIDATE_ABND_VS_IMAGE_TYPE_MISMATCH); + _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_ABND_VS_IMAGE_MSAA); + const _sg_pixelformat_info_t* info = &_sg.formats[img->cmn.pixel_format]; + switch (stage->images[i].sample_type) { + case SG_IMAGESAMPLETYPE_FLOAT: + _SG_VALIDATE(info->filter, VALIDATE_ABND_VS_EXPECTED_FILTERABLE_IMAGE); + break; + case SG_IMAGESAMPLETYPE_DEPTH: + _SG_VALIDATE(info->depth, VALIDATE_ABND_VS_EXPECTED_DEPTH_IMAGE); + break; + default: + break; + } + } + } + } else { + _SG_VALIDATE(bindings->vs.images[i].id == SG_INVALID_ID, VALIDATE_ABND_VS_UNEXPECTED_IMAGE_BINDING); + } + } + + // has expected vertex shader image samplers + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; + if (stage->samplers[i].sampler_type != _SG_SAMPLERTYPE_DEFAULT) { + _SG_VALIDATE(bindings->vs.samplers[i].id != SG_INVALID_ID, VALIDATE_ABND_VS_EXPECTED_SAMPLER_BINDING); + if (bindings->vs.samplers[i].id != SG_INVALID_ID) { + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, bindings->vs.samplers[i].id); + _SG_VALIDATE(smp != 0, VALIDATE_ABND_VS_SMP_EXISTS); + if (smp) { + if (stage->samplers[i].sampler_type == SG_SAMPLERTYPE_COMPARISON) { + _SG_VALIDATE(smp->cmn.compare != SG_COMPAREFUNC_NEVER, VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_COMPARE_NEVER); + } else { + _SG_VALIDATE(smp->cmn.compare == SG_COMPAREFUNC_NEVER, VALIDATE_ABND_VS_EXPECTED_SAMPLER_COMPARE_NEVER); + } + if (stage->samplers[i].sampler_type == SG_SAMPLERTYPE_NONFILTERING) { + const bool nonfiltering = (smp->cmn.min_filter != SG_FILTER_LINEAR) + && (smp->cmn.mag_filter != SG_FILTER_LINEAR) + && (smp->cmn.mipmap_filter != SG_FILTER_LINEAR); + _SG_VALIDATE(nonfiltering, VALIDATE_ABND_VS_EXPECTED_NONFILTERING_SAMPLER); + } + } + } + } else { + _SG_VALIDATE(bindings->vs.samplers[i].id == SG_INVALID_ID, VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_BINDING); + } + } + + // has expected vertex shader storage buffers + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; + if (stage->storage_buffers[i].used) { + _SG_VALIDATE(bindings->vs.storage_buffers[i].id != SG_INVALID_ID, VALIDATE_ABND_VS_EXPECTED_STORAGEBUFFER_BINDING); + if (bindings->vs.storage_buffers[i].id != SG_INVALID_ID) { + const _sg_buffer_t* sbuf = _sg_lookup_buffer(&_sg.pools, bindings->vs.storage_buffers[i].id); + _SG_VALIDATE(sbuf != 0, VALIDATE_ABND_VS_STORAGEBUFFER_EXISTS); + if (sbuf) { + _SG_VALIDATE(sbuf->cmn.type == SG_BUFFERTYPE_STORAGEBUFFER, VALIDATE_ABND_VS_STORAGEBUFFER_BINDING_BUFFERTYPE); + } + } + } else { + _SG_VALIDATE(bindings->vs.storage_buffers[i].id == SG_INVALID_ID, VALIDATE_ABND_VS_UNEXPECTED_STORAGEBUFFER_BINDING); + } + } + + // has expected fragment shader images + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; + if (stage->images[i].image_type != _SG_IMAGETYPE_DEFAULT) { + _SG_VALIDATE(bindings->fs.images[i].id != SG_INVALID_ID, VALIDATE_ABND_FS_EXPECTED_IMAGE_BINDING); + if (bindings->fs.images[i].id != SG_INVALID_ID) { + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->fs.images[i].id); + _SG_VALIDATE(img != 0, VALIDATE_ABND_FS_IMG_EXISTS); + if (img && img->slot.state == SG_RESOURCESTATE_VALID) { + _SG_VALIDATE(img->cmn.type == stage->images[i].image_type, VALIDATE_ABND_FS_IMAGE_TYPE_MISMATCH); + _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_ABND_FS_IMAGE_MSAA); + const _sg_pixelformat_info_t* info = &_sg.formats[img->cmn.pixel_format]; + switch (stage->images[i].sample_type) { + case SG_IMAGESAMPLETYPE_FLOAT: + _SG_VALIDATE(info->filter, VALIDATE_ABND_FS_EXPECTED_FILTERABLE_IMAGE); + break; + case SG_IMAGESAMPLETYPE_DEPTH: + _SG_VALIDATE(info->depth, VALIDATE_ABND_FS_EXPECTED_DEPTH_IMAGE); + break; + default: + break; + } + } + } + } else { + _SG_VALIDATE(bindings->fs.images[i].id == SG_INVALID_ID, VALIDATE_ABND_FS_UNEXPECTED_IMAGE_BINDING); + } + } + + // has expected fragment shader samplers + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; + if (stage->samplers[i].sampler_type != _SG_SAMPLERTYPE_DEFAULT) { + _SG_VALIDATE(bindings->fs.samplers[i].id != SG_INVALID_ID, VALIDATE_ABND_FS_EXPECTED_SAMPLER_BINDING); + if (bindings->fs.samplers[i].id != SG_INVALID_ID) { + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, bindings->fs.samplers[i].id); + _SG_VALIDATE(smp != 0, VALIDATE_ABND_FS_SMP_EXISTS); + if (smp) { + if (stage->samplers[i].sampler_type == SG_SAMPLERTYPE_COMPARISON) { + _SG_VALIDATE(smp->cmn.compare != SG_COMPAREFUNC_NEVER, VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_COMPARE_NEVER); + } else { + _SG_VALIDATE(smp->cmn.compare == SG_COMPAREFUNC_NEVER, VALIDATE_ABND_FS_EXPECTED_SAMPLER_COMPARE_NEVER); + } + if (stage->samplers[i].sampler_type == SG_SAMPLERTYPE_NONFILTERING) { + const bool nonfiltering = (smp->cmn.min_filter != SG_FILTER_LINEAR) + && (smp->cmn.mag_filter != SG_FILTER_LINEAR) + && (smp->cmn.mipmap_filter != SG_FILTER_LINEAR); + _SG_VALIDATE(nonfiltering, VALIDATE_ABND_FS_EXPECTED_NONFILTERING_SAMPLER); + } + } + } + } else { + _SG_VALIDATE(bindings->fs.samplers[i].id == SG_INVALID_ID, VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_BINDING); + } + } + + // has expected fragment shader storage buffers + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; + if (stage->storage_buffers[i].used) { + _SG_VALIDATE(bindings->fs.storage_buffers[i].id != SG_INVALID_ID, VALIDATE_ABND_FS_EXPECTED_STORAGEBUFFER_BINDING); + if (bindings->fs.storage_buffers[i].id != SG_INVALID_ID) { + const _sg_buffer_t* sbuf = _sg_lookup_buffer(&_sg.pools, bindings->fs.storage_buffers[i].id); + _SG_VALIDATE(sbuf != 0, VALIDATE_ABND_FS_STORAGEBUFFER_EXISTS); + if (sbuf) { + _SG_VALIDATE(sbuf->cmn.type == SG_BUFFERTYPE_STORAGEBUFFER, VALIDATE_ABND_FS_STORAGEBUFFER_BINDING_BUFFERTYPE); + } + } + } else { + _SG_VALIDATE(bindings->fs.storage_buffers[i].id == SG_INVALID_ID, VALIDATE_ABND_FS_UNEXPECTED_STORAGEBUFFER_BINDING); + } + } + + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(stage_index); + _SOKOL_UNUSED(ub_index); + _SOKOL_UNUSED(data); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT((stage_index == SG_SHADERSTAGE_VS) || (stage_index == SG_SHADERSTAGE_FS)); + SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); + _sg_validate_begin(); + _SG_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, VALIDATE_AUB_NO_PIPELINE); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); + SOKOL_ASSERT(pip && (pip->slot.id == _sg.cur_pipeline.id)); + SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->cmn.shader_id.id)); + + // check that there is a uniform block at 'stage' and 'ub_index' + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[stage_index]; + _SG_VALIDATE(ub_index < stage->num_uniform_blocks, VALIDATE_AUB_NO_UB_AT_SLOT); + + // check that the provided data size matches the uniform block size + _SG_VALIDATE(data->size == stage->uniform_blocks[ub_index].size, VALIDATE_AUB_SIZE); + + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_update_buffer(const _sg_buffer_t* buf, const sg_range* data) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(buf); + _SOKOL_UNUSED(data); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(buf && data && data->ptr); + _sg_validate_begin(); + _SG_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, VALIDATE_UPDATEBUF_USAGE); + _SG_VALIDATE(buf->cmn.size >= (int)data->size, VALIDATE_UPDATEBUF_SIZE); + _SG_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, VALIDATE_UPDATEBUF_ONCE); + _SG_VALIDATE(buf->cmn.append_frame_index != _sg.frame_index, VALIDATE_UPDATEBUF_APPEND); + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_append_buffer(const _sg_buffer_t* buf, const sg_range* data) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(buf); + _SOKOL_UNUSED(data); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(buf && data && data->ptr); + _sg_validate_begin(); + _SG_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, VALIDATE_APPENDBUF_USAGE); + _SG_VALIDATE(buf->cmn.size >= (buf->cmn.append_pos + (int)data->size), VALIDATE_APPENDBUF_SIZE); + _SG_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, VALIDATE_APPENDBUF_UPDATE); + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_update_image(const _sg_image_t* img, const sg_image_data* data) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(img); + _SOKOL_UNUSED(data); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(img && data); + _sg_validate_begin(); + _SG_VALIDATE(img->cmn.usage != SG_USAGE_IMMUTABLE, VALIDATE_UPDIMG_USAGE); + _SG_VALIDATE(img->cmn.upd_frame_index != _sg.frame_index, VALIDATE_UPDIMG_ONCE); + _sg_validate_image_data(data, + img->cmn.pixel_format, + img->cmn.width, + img->cmn.height, + (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6 : 1, + img->cmn.num_mipmaps, + img->cmn.num_slices); + return _sg_validate_end(); + #endif +} + +// ██████ ███████ ███████ ██████ ██ ██ ██████ ██████ ███████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ █████ ███████ ██ ██ ██ ██ ██████ ██ █████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██████ ██████ ██ ██ ██████ ███████ ███████ +// +// >>resources +_SOKOL_PRIVATE sg_buffer_desc _sg_buffer_desc_defaults(const sg_buffer_desc* desc) { + sg_buffer_desc def = *desc; + def.type = _sg_def(def.type, SG_BUFFERTYPE_VERTEXBUFFER); + def.usage = _sg_def(def.usage, SG_USAGE_IMMUTABLE); + if (def.size == 0) { + def.size = def.data.size; + } else if (def.data.size == 0) { + def.data.size = def.size; + } + return def; +} + +_SOKOL_PRIVATE sg_image_desc _sg_image_desc_defaults(const sg_image_desc* desc) { + sg_image_desc def = *desc; + def.type = _sg_def(def.type, SG_IMAGETYPE_2D); + def.num_slices = _sg_def(def.num_slices, 1); + def.num_mipmaps = _sg_def(def.num_mipmaps, 1); + def.usage = _sg_def(def.usage, SG_USAGE_IMMUTABLE); + if (desc->render_target) { + def.pixel_format = _sg_def(def.pixel_format, _sg.desc.environment.defaults.color_format); + def.sample_count = _sg_def(def.sample_count, _sg.desc.environment.defaults.sample_count); + } else { + def.pixel_format = _sg_def(def.pixel_format, SG_PIXELFORMAT_RGBA8); + def.sample_count = _sg_def(def.sample_count, 1); + } + return def; +} + +_SOKOL_PRIVATE sg_sampler_desc _sg_sampler_desc_defaults(const sg_sampler_desc* desc) { + sg_sampler_desc def = *desc; + def.min_filter = _sg_def(def.min_filter, SG_FILTER_NEAREST); + def.mag_filter = _sg_def(def.mag_filter, SG_FILTER_NEAREST); + def.mipmap_filter = _sg_def(def.mipmap_filter, SG_FILTER_NONE); + def.wrap_u = _sg_def(def.wrap_u, SG_WRAP_REPEAT); + def.wrap_v = _sg_def(def.wrap_v, SG_WRAP_REPEAT); + def.wrap_w = _sg_def(def.wrap_w, SG_WRAP_REPEAT); + def.max_lod = _sg_def_flt(def.max_lod, FLT_MAX); + def.border_color = _sg_def(def.border_color, SG_BORDERCOLOR_OPAQUE_BLACK); + def.compare = _sg_def(def.compare, SG_COMPAREFUNC_NEVER); + def.max_anisotropy = _sg_def(def.max_anisotropy, 1); + return def; +} + +_SOKOL_PRIVATE sg_shader_desc _sg_shader_desc_defaults(const sg_shader_desc* desc) { + sg_shader_desc def = *desc; + #if defined(SOKOL_METAL) + def.vs.entry = _sg_def(def.vs.entry, "_main"); + def.fs.entry = _sg_def(def.fs.entry, "_main"); + #else + def.vs.entry = _sg_def(def.vs.entry, "main"); + def.fs.entry = _sg_def(def.fs.entry, "main"); + #endif + #if defined(SOKOL_D3D11) + if (def.vs.source) { + def.vs.d3d11_target = _sg_def(def.vs.d3d11_target, "vs_4_0"); + } + if (def.fs.source) { + def.fs.d3d11_target = _sg_def(def.fs.d3d11_target, "ps_4_0"); + } + #endif + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &def.vs : &def.fs; + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + if (0 == ub_desc->size) { + break; + } + ub_desc->layout = _sg_def(ub_desc->layout, SG_UNIFORMLAYOUT_NATIVE); + for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { + sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; + if (u_desc->type == SG_UNIFORMTYPE_INVALID) { + break; + } + u_desc->array_count = _sg_def(u_desc->array_count, 1); + } + } + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (!img_desc->used) { + break; + } + img_desc->image_type = _sg_def(img_desc->image_type, SG_IMAGETYPE_2D); + img_desc->sample_type = _sg_def(img_desc->sample_type, SG_IMAGESAMPLETYPE_FLOAT); + } + for (int smp_index = 0; smp_index < SG_MAX_SHADERSTAGE_SAMPLERS; smp_index++) { + sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_index]; + if (!smp_desc->used) { + break; + } + smp_desc->sampler_type = _sg_def(smp_desc->sampler_type, SG_SAMPLERTYPE_FILTERING); + } + } + return def; +} + +_SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_desc* desc) { + sg_pipeline_desc def = *desc; + + def.primitive_type = _sg_def(def.primitive_type, SG_PRIMITIVETYPE_TRIANGLES); + def.index_type = _sg_def(def.index_type, SG_INDEXTYPE_NONE); + def.cull_mode = _sg_def(def.cull_mode, SG_CULLMODE_NONE); + def.face_winding = _sg_def(def.face_winding, SG_FACEWINDING_CW); + def.sample_count = _sg_def(def.sample_count, _sg.desc.environment.defaults.sample_count); + + def.stencil.front.compare = _sg_def(def.stencil.front.compare, SG_COMPAREFUNC_ALWAYS); + def.stencil.front.fail_op = _sg_def(def.stencil.front.fail_op, SG_STENCILOP_KEEP); + def.stencil.front.depth_fail_op = _sg_def(def.stencil.front.depth_fail_op, SG_STENCILOP_KEEP); + def.stencil.front.pass_op = _sg_def(def.stencil.front.pass_op, SG_STENCILOP_KEEP); + def.stencil.back.compare = _sg_def(def.stencil.back.compare, SG_COMPAREFUNC_ALWAYS); + def.stencil.back.fail_op = _sg_def(def.stencil.back.fail_op, SG_STENCILOP_KEEP); + def.stencil.back.depth_fail_op = _sg_def(def.stencil.back.depth_fail_op, SG_STENCILOP_KEEP); + def.stencil.back.pass_op = _sg_def(def.stencil.back.pass_op, SG_STENCILOP_KEEP); + + def.depth.compare = _sg_def(def.depth.compare, SG_COMPAREFUNC_ALWAYS); + def.depth.pixel_format = _sg_def(def.depth.pixel_format, _sg.desc.environment.defaults.depth_format); + if (def.colors[0].pixel_format == SG_PIXELFORMAT_NONE) { + // special case depth-only rendering, enforce a color count of 0 + def.color_count = 0; + } else { + def.color_count = _sg_def(def.color_count, 1); + } + if (def.color_count > SG_MAX_COLOR_ATTACHMENTS) { + def.color_count = SG_MAX_COLOR_ATTACHMENTS; + } + for (int i = 0; i < def.color_count; i++) { + sg_color_target_state* cs = &def.colors[i]; + cs->pixel_format = _sg_def(cs->pixel_format, _sg.desc.environment.defaults.color_format); + cs->write_mask = _sg_def(cs->write_mask, SG_COLORMASK_RGBA); + sg_blend_state* bs = &def.colors[i].blend; + bs->src_factor_rgb = _sg_def(bs->src_factor_rgb, SG_BLENDFACTOR_ONE); + bs->dst_factor_rgb = _sg_def(bs->dst_factor_rgb, SG_BLENDFACTOR_ZERO); + bs->op_rgb = _sg_def(bs->op_rgb, SG_BLENDOP_ADD); + bs->src_factor_alpha = _sg_def(bs->src_factor_alpha, SG_BLENDFACTOR_ONE); + bs->dst_factor_alpha = _sg_def(bs->dst_factor_alpha, SG_BLENDFACTOR_ZERO); + bs->op_alpha = _sg_def(bs->op_alpha, SG_BLENDOP_ADD); + } + + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + sg_vertex_attr_state* a_state = &def.layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + sg_vertex_buffer_layout_state* l_state = &def.layout.buffers[a_state->buffer_index]; + l_state->step_func = _sg_def(l_state->step_func, SG_VERTEXSTEP_PER_VERTEX); + l_state->step_rate = _sg_def(l_state->step_rate, 1); + } + + // resolve vertex layout strides and offsets + int auto_offset[SG_MAX_VERTEX_BUFFERS]; + _sg_clear(auto_offset, sizeof(auto_offset)); + bool use_auto_offset = true; + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + // to use computed offsets, *all* attr offsets must be 0 + if (def.layout.attrs[attr_index].offset != 0) { + use_auto_offset = false; + } + } + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + sg_vertex_attr_state* a_state = &def.layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + if (use_auto_offset) { + a_state->offset = auto_offset[a_state->buffer_index]; + } + auto_offset[a_state->buffer_index] += _sg_vertexformat_bytesize(a_state->format); + } + // compute vertex strides if needed + for (int buf_index = 0; buf_index < SG_MAX_VERTEX_BUFFERS; buf_index++) { + sg_vertex_buffer_layout_state* l_state = &def.layout.buffers[buf_index]; + if (l_state->stride == 0) { + l_state->stride = auto_offset[buf_index]; + } + } + + return def; +} + +_SOKOL_PRIVATE sg_attachments_desc _sg_attachments_desc_defaults(const sg_attachments_desc* desc) { + sg_attachments_desc def = *desc; + return def; +} + +_SOKOL_PRIVATE sg_buffer _sg_alloc_buffer(void) { + sg_buffer res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.buffer_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.buffer_pool, &_sg.pools.buffers[slot_index].slot, slot_index); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(BUFFER_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE sg_image _sg_alloc_image(void) { + sg_image res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.image_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.image_pool, &_sg.pools.images[slot_index].slot, slot_index); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(IMAGE_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE sg_sampler _sg_alloc_sampler(void) { + sg_sampler res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.sampler_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.sampler_pool, &_sg.pools.samplers[slot_index].slot, slot_index); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(SAMPLER_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE sg_shader _sg_alloc_shader(void) { + sg_shader res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.shader_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.shader_pool, &_sg.pools.shaders[slot_index].slot, slot_index); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(SHADER_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE sg_pipeline _sg_alloc_pipeline(void) { + sg_pipeline res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.pipeline_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id =_sg_slot_alloc(&_sg.pools.pipeline_pool, &_sg.pools.pipelines[slot_index].slot, slot_index); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(PIPELINE_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE sg_attachments _sg_alloc_attachments(void) { + sg_attachments res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.attachments_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.attachments_pool, &_sg.pools.attachments[slot_index].slot, slot_index); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(PASS_POOL_EXHAUSTED); + } + return res; +} + +_SOKOL_PRIVATE void _sg_dealloc_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf && (buf->slot.state == SG_RESOURCESTATE_ALLOC) && (buf->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.buffer_pool, _sg_slot_index(buf->slot.id)); + _sg_reset_slot(&buf->slot); +} + +_SOKOL_PRIVATE void _sg_dealloc_image(_sg_image_t* img) { + SOKOL_ASSERT(img && (img->slot.state == SG_RESOURCESTATE_ALLOC) && (img->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.image_pool, _sg_slot_index(img->slot.id)); + _sg_reset_slot(&img->slot); +} + +_SOKOL_PRIVATE void _sg_dealloc_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp && (smp->slot.state == SG_RESOURCESTATE_ALLOC) && (smp->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.sampler_pool, _sg_slot_index(smp->slot.id)); + _sg_reset_slot(&smp->slot); +} + +_SOKOL_PRIVATE void _sg_dealloc_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd && (shd->slot.state == SG_RESOURCESTATE_ALLOC) && (shd->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.shader_pool, _sg_slot_index(shd->slot.id)); + _sg_reset_slot(&shd->slot); +} + +_SOKOL_PRIVATE void _sg_dealloc_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip && (pip->slot.state == SG_RESOURCESTATE_ALLOC) && (pip->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.pipeline_pool, _sg_slot_index(pip->slot.id)); + _sg_reset_slot(&pip->slot); +} + +_SOKOL_PRIVATE void _sg_dealloc_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts && (atts->slot.state == SG_RESOURCESTATE_ALLOC) && (atts->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.attachments_pool, _sg_slot_index(atts->slot.id)); + _sg_reset_slot(&atts->slot); +} + +_SOKOL_PRIVATE void _sg_init_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && (buf->slot.state == SG_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + if (_sg_validate_buffer_desc(desc)) { + _sg_buffer_common_init(&buf->cmn, desc); + buf->slot.state = _sg_create_buffer(buf, desc); + } else { + buf->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID)||(buf->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && (img->slot.state == SG_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + if (_sg_validate_image_desc(desc)) { + _sg_image_common_init(&img->cmn, desc); + img->slot.state = _sg_create_image(img, desc); + } else { + img->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID)||(img->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && (smp->slot.state == SG_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + if (_sg_validate_sampler_desc(desc)) { + _sg_sampler_common_init(&smp->cmn, desc); + smp->slot.state = _sg_create_sampler(smp, desc); + } else { + smp->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((smp->slot.state == SG_RESOURCESTATE_VALID)||(smp->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && (shd->slot.state == SG_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + if (_sg_validate_shader_desc(desc)) { + _sg_shader_common_init(&shd->cmn, desc); + shd->slot.state = _sg_create_shader(shd, desc); + } else { + shd->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID)||(shd->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_pipeline(_sg_pipeline_t* pip, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && (pip->slot.state == SG_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + if (_sg_validate_pipeline_desc(desc)) { + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); + if (shd && (shd->slot.state == SG_RESOURCESTATE_VALID)) { + _sg_pipeline_common_init(&pip->cmn, desc); + pip->slot.state = _sg_create_pipeline(pip, shd, desc); + } else { + pip->slot.state = SG_RESOURCESTATE_FAILED; + } + } else { + pip->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID)||(pip->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_attachments(_sg_attachments_t* atts, const sg_attachments_desc* desc) { + SOKOL_ASSERT(atts && atts->slot.state == SG_RESOURCESTATE_ALLOC); + SOKOL_ASSERT(desc); + if (_sg_validate_attachments_desc(desc)) { + // lookup pass attachment image pointers + _sg_image_t* color_images[SG_MAX_COLOR_ATTACHMENTS] = { 0 }; + _sg_image_t* resolve_images[SG_MAX_COLOR_ATTACHMENTS] = { 0 }; + _sg_image_t* ds_image = 0; + // NOTE: validation already checked that all surfaces are same width/height + int width = 0; + int height = 0; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (desc->colors[i].image.id) { + color_images[i] = _sg_lookup_image(&_sg.pools, desc->colors[i].image.id); + if (!(color_images[i] && color_images[i]->slot.state == SG_RESOURCESTATE_VALID)) { + atts->slot.state = SG_RESOURCESTATE_FAILED; + return; + } + const int mip_level = desc->colors[i].mip_level; + width = _sg_miplevel_dim(color_images[i]->cmn.width, mip_level); + height = _sg_miplevel_dim(color_images[i]->cmn.height, mip_level); + } + if (desc->resolves[i].image.id) { + resolve_images[i] = _sg_lookup_image(&_sg.pools, desc->resolves[i].image.id); + if (!(resolve_images[i] && resolve_images[i]->slot.state == SG_RESOURCESTATE_VALID)) { + atts->slot.state = SG_RESOURCESTATE_FAILED; + return; + } + } + } + if (desc->depth_stencil.image.id) { + ds_image = _sg_lookup_image(&_sg.pools, desc->depth_stencil.image.id); + if (!(ds_image && ds_image->slot.state == SG_RESOURCESTATE_VALID)) { + atts->slot.state = SG_RESOURCESTATE_FAILED; + return; + } + const int mip_level = desc->depth_stencil.mip_level; + width = _sg_miplevel_dim(ds_image->cmn.width, mip_level); + height = _sg_miplevel_dim(ds_image->cmn.height, mip_level); + } + _sg_attachments_common_init(&atts->cmn, desc, width, height); + atts->slot.state = _sg_create_attachments(atts, color_images, resolve_images, ds_image, desc); + } else { + atts->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((atts->slot.state == SG_RESOURCESTATE_VALID)||(atts->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_uninit_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf && ((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED))); + _sg_discard_buffer(buf); + _sg_reset_buffer_to_alloc_state(buf); +} + +_SOKOL_PRIVATE void _sg_uninit_image(_sg_image_t* img) { + SOKOL_ASSERT(img && ((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED))); + _sg_discard_image(img); + _sg_reset_image_to_alloc_state(img); +} + +_SOKOL_PRIVATE void _sg_uninit_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp && ((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED))); + _sg_discard_sampler(smp); + _sg_reset_sampler_to_alloc_state(smp); +} + +_SOKOL_PRIVATE void _sg_uninit_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd && ((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED))); + _sg_discard_shader(shd); + _sg_reset_shader_to_alloc_state(shd); +} + +_SOKOL_PRIVATE void _sg_uninit_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip && ((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED))); + _sg_discard_pipeline(pip); + _sg_reset_pipeline_to_alloc_state(pip); +} + +_SOKOL_PRIVATE void _sg_uninit_attachments(_sg_attachments_t* atts) { + SOKOL_ASSERT(atts && ((atts->slot.state == SG_RESOURCESTATE_VALID) || (atts->slot.state == SG_RESOURCESTATE_FAILED))); + _sg_discard_attachments(atts); + _sg_reset_attachments_to_alloc_state(atts); +} + +_SOKOL_PRIVATE void _sg_setup_commit_listeners(const sg_desc* desc) { + SOKOL_ASSERT(desc->max_commit_listeners > 0); + SOKOL_ASSERT(0 == _sg.commit_listeners.items); + SOKOL_ASSERT(0 == _sg.commit_listeners.num); + SOKOL_ASSERT(0 == _sg.commit_listeners.upper); + _sg.commit_listeners.num = desc->max_commit_listeners; + const size_t size = (size_t)_sg.commit_listeners.num * sizeof(sg_commit_listener); + _sg.commit_listeners.items = (sg_commit_listener*)_sg_malloc_clear(size); +} + +_SOKOL_PRIVATE void _sg_discard_commit_listeners(void) { + SOKOL_ASSERT(0 != _sg.commit_listeners.items); + _sg_free(_sg.commit_listeners.items); + _sg.commit_listeners.items = 0; +} + +_SOKOL_PRIVATE void _sg_notify_commit_listeners(void) { + SOKOL_ASSERT(_sg.commit_listeners.items); + for (int i = 0; i < _sg.commit_listeners.upper; i++) { + const sg_commit_listener* listener = &_sg.commit_listeners.items[i]; + if (listener->func) { + listener->func(listener->user_data); + } + } +} + +_SOKOL_PRIVATE bool _sg_add_commit_listener(const sg_commit_listener* new_listener) { + SOKOL_ASSERT(new_listener && new_listener->func); + SOKOL_ASSERT(_sg.commit_listeners.items); + // first check if the listener hadn't been added already + for (int i = 0; i < _sg.commit_listeners.upper; i++) { + const sg_commit_listener* slot = &_sg.commit_listeners.items[i]; + if ((slot->func == new_listener->func) && (slot->user_data == new_listener->user_data)) { + _SG_ERROR(IDENTICAL_COMMIT_LISTENER); + return false; + } + } + // first try to plug a hole + sg_commit_listener* slot = 0; + for (int i = 0; i < _sg.commit_listeners.upper; i++) { + if (_sg.commit_listeners.items[i].func == 0) { + slot = &_sg.commit_listeners.items[i]; + break; + } + } + if (!slot) { + // append to end + if (_sg.commit_listeners.upper < _sg.commit_listeners.num) { + slot = &_sg.commit_listeners.items[_sg.commit_listeners.upper++]; + } + } + if (!slot) { + _SG_ERROR(COMMIT_LISTENER_ARRAY_FULL); + return false; + } + *slot = *new_listener; + return true; +} + +_SOKOL_PRIVATE bool _sg_remove_commit_listener(const sg_commit_listener* listener) { + SOKOL_ASSERT(listener && listener->func); + SOKOL_ASSERT(_sg.commit_listeners.items); + for (int i = 0; i < _sg.commit_listeners.upper; i++) { + sg_commit_listener* slot = &_sg.commit_listeners.items[i]; + // both the function pointer and user data must match! + if ((slot->func == listener->func) && (slot->user_data == listener->user_data)) { + slot->func = 0; + slot->user_data = 0; + // NOTE: since _sg_add_commit_listener() already catches duplicates, + // we don't need to worry about them here + return true; + } + } + return false; +} + +_SOKOL_PRIVATE sg_desc _sg_desc_defaults(const sg_desc* desc) { + /* + NOTE: on WebGPU, the default color pixel format MUST be provided, + cannot be a default compile-time constant. + */ + sg_desc res = *desc; + #if defined(SOKOL_WGPU) + SOKOL_ASSERT(SG_PIXELFORMAT_NONE < res.environment.defaults.color_format); + #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) + res.environment.defaults.color_format = _sg_def(res.environment.defaults.color_format, SG_PIXELFORMAT_BGRA8); + #else + res.environment.defaults.color_format = _sg_def(res.environment.defaults.color_format, SG_PIXELFORMAT_RGBA8); + #endif + res.environment.defaults.depth_format = _sg_def(res.environment.defaults.depth_format, SG_PIXELFORMAT_DEPTH_STENCIL); + res.environment.defaults.sample_count = _sg_def(res.environment.defaults.sample_count, 1); + res.buffer_pool_size = _sg_def(res.buffer_pool_size, _SG_DEFAULT_BUFFER_POOL_SIZE); + res.image_pool_size = _sg_def(res.image_pool_size, _SG_DEFAULT_IMAGE_POOL_SIZE); + res.sampler_pool_size = _sg_def(res.sampler_pool_size, _SG_DEFAULT_SAMPLER_POOL_SIZE); + res.shader_pool_size = _sg_def(res.shader_pool_size, _SG_DEFAULT_SHADER_POOL_SIZE); + res.pipeline_pool_size = _sg_def(res.pipeline_pool_size, _SG_DEFAULT_PIPELINE_POOL_SIZE); + res.attachments_pool_size = _sg_def(res.attachments_pool_size, _SG_DEFAULT_ATTACHMENTS_POOL_SIZE); + res.uniform_buffer_size = _sg_def(res.uniform_buffer_size, _SG_DEFAULT_UB_SIZE); + res.max_commit_listeners = _sg_def(res.max_commit_listeners, _SG_DEFAULT_MAX_COMMIT_LISTENERS); + res.wgpu_bindgroups_cache_size = _sg_def(res.wgpu_bindgroups_cache_size, _SG_DEFAULT_WGPU_BINDGROUP_CACHE_SIZE); + return res; +} + +_SOKOL_PRIVATE sg_pass _sg_pass_defaults(const sg_pass* pass) { + sg_pass res = *pass; + if (res.attachments.id == SG_INVALID_ID) { + // this is a swapchain-pass + res.swapchain.sample_count = _sg_def(res.swapchain.sample_count, _sg.desc.environment.defaults.sample_count); + res.swapchain.color_format = _sg_def(res.swapchain.color_format, _sg.desc.environment.defaults.color_format); + res.swapchain.depth_format = _sg_def(res.swapchain.depth_format, _sg.desc.environment.defaults.depth_format); + } + res.action = _sg_pass_action_defaults(&res.action); + return res; +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void sg_setup(const sg_desc* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0)); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + _SG_CLEAR_ARC_STRUCT(_sg_state_t, _sg); + _sg.desc = _sg_desc_defaults(desc); + _sg_setup_pools(&_sg.pools, &_sg.desc); + _sg_setup_commit_listeners(&_sg.desc); + _sg.frame_index = 1; + _sg.stats_enabled = true; + _sg_setup_backend(&_sg.desc); + _sg.valid = true; +} + +SOKOL_API_IMPL void sg_shutdown(void) { + _sg_discard_all_resources(&_sg.pools); + _sg_discard_backend(); + _sg_discard_commit_listeners(); + _sg_discard_pools(&_sg.pools); + _SG_CLEAR_ARC_STRUCT(_sg_state_t, _sg); +} + +SOKOL_API_IMPL bool sg_isvalid(void) { + return _sg.valid; +} + +SOKOL_API_IMPL sg_desc sg_query_desc(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.desc; +} + +SOKOL_API_IMPL sg_backend sg_query_backend(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.backend; +} + +SOKOL_API_IMPL sg_features sg_query_features(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.features; +} + +SOKOL_API_IMPL sg_limits sg_query_limits(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.limits; +} + +SOKOL_API_IMPL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt) { + SOKOL_ASSERT(_sg.valid); + int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index > SG_PIXELFORMAT_NONE) && (fmt_index < _SG_PIXELFORMAT_NUM)); + const _sg_pixelformat_info_t* src = &_sg.formats[fmt_index]; + sg_pixelformat_info res; + _sg_clear(&res, sizeof(res)); + res.sample = src->sample; + res.filter = src->filter; + res.render = src->render; + res.blend = src->blend; + res.msaa = src->msaa; + res.depth = src->depth; + res.compressed = _sg_is_compressed_pixel_format(fmt); + if (!res.compressed) { + res.bytes_per_pixel = _sg_pixelformat_bytesize(fmt); + } + return res; +} + +SOKOL_API_IMPL int sg_query_row_pitch(sg_pixel_format fmt, int width, int row_align_bytes) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(width > 0); + SOKOL_ASSERT((row_align_bytes > 0) && _sg_ispow2(row_align_bytes)); + SOKOL_ASSERT(((int)fmt > SG_PIXELFORMAT_NONE) && ((int)fmt < _SG_PIXELFORMAT_NUM)); + return _sg_row_pitch(fmt, width, row_align_bytes); +} + +SOKOL_API_IMPL int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT((width > 0) && (height > 0)); + SOKOL_ASSERT((row_align_bytes > 0) && _sg_ispow2(row_align_bytes)); + SOKOL_ASSERT(((int)fmt > SG_PIXELFORMAT_NONE) && ((int)fmt < _SG_PIXELFORMAT_NUM)); + return _sg_surface_pitch(fmt, width, height, row_align_bytes); +} + +SOKOL_API_IMPL sg_frame_stats sg_query_frame_stats(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.prev_stats; +} + +SOKOL_API_IMPL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(trace_hooks); + _SOKOL_UNUSED(trace_hooks); + #if defined(SOKOL_TRACE_HOOKS) + sg_trace_hooks old_hooks = _sg.hooks; + _sg.hooks = *trace_hooks; + #else + static sg_trace_hooks old_hooks; + _SG_WARN(TRACE_HOOKS_NOT_ENABLED); + #endif + return old_hooks; +} + +SOKOL_API_IMPL sg_buffer sg_alloc_buffer(void) { + SOKOL_ASSERT(_sg.valid); + sg_buffer res = _sg_alloc_buffer(); + _SG_TRACE_ARGS(alloc_buffer, res); + return res; +} + +SOKOL_API_IMPL sg_image sg_alloc_image(void) { + SOKOL_ASSERT(_sg.valid); + sg_image res = _sg_alloc_image(); + _SG_TRACE_ARGS(alloc_image, res); + return res; +} + +SOKOL_API_IMPL sg_sampler sg_alloc_sampler(void) { + SOKOL_ASSERT(_sg.valid); + sg_sampler res = _sg_alloc_sampler(); + _SG_TRACE_ARGS(alloc_sampler, res); + return res; +} + +SOKOL_API_IMPL sg_shader sg_alloc_shader(void) { + SOKOL_ASSERT(_sg.valid); + sg_shader res = _sg_alloc_shader(); + _SG_TRACE_ARGS(alloc_shader, res); + return res; +} + +SOKOL_API_IMPL sg_pipeline sg_alloc_pipeline(void) { + SOKOL_ASSERT(_sg.valid); + sg_pipeline res = _sg_alloc_pipeline(); + _SG_TRACE_ARGS(alloc_pipeline, res); + return res; +} + +SOKOL_API_IMPL sg_attachments sg_alloc_attachments(void) { + SOKOL_ASSERT(_sg.valid); + sg_attachments res = _sg_alloc_attachments(); + _SG_TRACE_ARGS(alloc_attachments, res); + return res; +} + +SOKOL_API_IMPL void sg_dealloc_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_buffer(buf); + } else { + _SG_ERROR(DEALLOC_BUFFER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(dealloc_buffer, buf_id); +} + +SOKOL_API_IMPL void sg_dealloc_image(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + if (img->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_image(img); + } else { + _SG_ERROR(DEALLOC_IMAGE_INVALID_STATE); + } + } + _SG_TRACE_ARGS(dealloc_image, img_id); +} + +SOKOL_API_IMPL void sg_dealloc_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_sampler(smp); + } else { + _SG_ERROR(DEALLOC_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(dealloc_sampler, smp_id); +} + +SOKOL_API_IMPL void sg_dealloc_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_shader(shd); + } else { + _SG_ERROR(DEALLOC_SHADER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(dealloc_shader, shd_id); +} + +SOKOL_API_IMPL void sg_dealloc_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_pipeline(pip); + } else { + _SG_ERROR(DEALLOC_PIPELINE_INVALID_STATE); + } + } + _SG_TRACE_ARGS(dealloc_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_dealloc_attachments(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + if (atts->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_attachments(atts); + } else { + _SG_ERROR(DEALLOC_ATTACHMENTS_INVALID_STATE); + } + } + _SG_TRACE_ARGS(dealloc_attachments, atts_id); +} + +SOKOL_API_IMPL void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_buffer_desc desc_def = _sg_buffer_desc_defaults(desc); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_buffer(buf, &desc_def); + SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)); + } else { + _SG_ERROR(INIT_BUFFER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(init_buffer, buf_id, &desc_def); +} + +SOKOL_API_IMPL void sg_init_image(sg_image img_id, const sg_image_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_image_desc desc_def = _sg_image_desc_defaults(desc); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + if (img->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_image(img, &desc_def); + SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)); + } else { + _SG_ERROR(INIT_IMAGE_INVALID_STATE); + } + } + _SG_TRACE_ARGS(init_image, img_id, &desc_def); +} + +SOKOL_API_IMPL void sg_init_sampler(sg_sampler smp_id, const sg_sampler_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_sampler_desc desc_def = _sg_sampler_desc_defaults(desc); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_sampler(smp, &desc_def); + SOKOL_ASSERT((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)); + } else { + _SG_ERROR(INIT_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(init_sampler, smp_id, &desc_def); +} + +SOKOL_API_IMPL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_shader_desc desc_def = _sg_shader_desc_defaults(desc); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_shader(shd, &desc_def); + SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)); + } else { + _SG_ERROR(INIT_SHADER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(init_shader, shd_id, &desc_def); +} + +SOKOL_API_IMPL void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_pipeline_desc desc_def = _sg_pipeline_desc_defaults(desc); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_pipeline(pip, &desc_def); + SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)); + } else { + _SG_ERROR(INIT_PIPELINE_INVALID_STATE); + } + } + _SG_TRACE_ARGS(init_pipeline, pip_id, &desc_def); +} + +SOKOL_API_IMPL void sg_init_attachments(sg_attachments atts_id, const sg_attachments_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_attachments_desc desc_def = _sg_attachments_desc_defaults(desc); + _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + if (atts->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_attachments(atts, &desc_def); + SOKOL_ASSERT((atts->slot.state == SG_RESOURCESTATE_VALID) || (atts->slot.state == SG_RESOURCESTATE_FAILED)); + } else { + _SG_ERROR(INIT_ATTACHMENTS_INVALID_STATE); + } + } + _SG_TRACE_ARGS(init_attachments, atts_id, &desc_def); +} + +SOKOL_API_IMPL void sg_uninit_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + if ((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_buffer(buf); + SOKOL_ASSERT(buf->slot.state == SG_RESOURCESTATE_ALLOC); + } else { + _SG_ERROR(UNINIT_BUFFER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_buffer, buf_id); +} + +SOKOL_API_IMPL void sg_uninit_image(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + if ((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_image(img); + SOKOL_ASSERT(img->slot.state == SG_RESOURCESTATE_ALLOC); + } else { + _SG_ERROR(UNINIT_IMAGE_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_image, img_id); +} + +SOKOL_API_IMPL void sg_uninit_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if ((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_sampler(smp); + SOKOL_ASSERT(smp->slot.state == SG_RESOURCESTATE_ALLOC); + } else { + _SG_ERROR(UNINIT_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_sampler, smp_id); +} + +SOKOL_API_IMPL void sg_uninit_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + if ((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_shader(shd); + SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_ALLOC); + } else { + _SG_ERROR(UNINIT_SHADER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_shader, shd_id); +} + +SOKOL_API_IMPL void sg_uninit_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if ((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_pipeline(pip); + SOKOL_ASSERT(pip->slot.state == SG_RESOURCESTATE_ALLOC); + } else { + _SG_ERROR(UNINIT_PIPELINE_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_uninit_attachments(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + if ((atts->slot.state == SG_RESOURCESTATE_VALID) || (atts->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_attachments(atts); + SOKOL_ASSERT(atts->slot.state == SG_RESOURCESTATE_ALLOC); + } else { + _SG_ERROR(UNINIT_ATTACHMENTS_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_attachments, atts_id); +} + +SOKOL_API_IMPL void sg_fail_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { + buf->slot.state = SG_RESOURCESTATE_FAILED; + } else { + _SG_ERROR(FAIL_BUFFER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(fail_buffer, buf_id); +} + +SOKOL_API_IMPL void sg_fail_image(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + if (img->slot.state == SG_RESOURCESTATE_ALLOC) { + img->slot.state = SG_RESOURCESTATE_FAILED; + } else { + _SG_ERROR(FAIL_IMAGE_INVALID_STATE); + } + } + _SG_TRACE_ARGS(fail_image, img_id); +} + +SOKOL_API_IMPL void sg_fail_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + smp->slot.state = SG_RESOURCESTATE_FAILED; + } else { + _SG_ERROR(FAIL_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(fail_sampler, smp_id); +} + +SOKOL_API_IMPL void sg_fail_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { + shd->slot.state = SG_RESOURCESTATE_FAILED; + } else { + _SG_ERROR(FAIL_SHADER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(fail_shader, shd_id); +} + +SOKOL_API_IMPL void sg_fail_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { + pip->slot.state = SG_RESOURCESTATE_FAILED; + } else { + _SG_ERROR(FAIL_PIPELINE_INVALID_STATE); + } + } + _SG_TRACE_ARGS(fail_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_fail_attachments(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + if (atts->slot.state == SG_RESOURCESTATE_ALLOC) { + atts->slot.state = SG_RESOURCESTATE_FAILED; + } else { + _SG_ERROR(FAIL_ATTACHMENTS_INVALID_STATE); + } + } + _SG_TRACE_ARGS(fail_attachments, atts_id); +} + +SOKOL_API_IMPL sg_resource_state sg_query_buffer_state(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + sg_resource_state res = buf ? buf->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_image_state(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + sg_resource_state res = img ? img->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_sampler_state(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + sg_resource_state res = smp ? smp->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_shader_state(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + sg_resource_state res = shd ? shd->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_pipeline_state(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + sg_resource_state res = pip ? pip->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_attachments_state(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + sg_resource_state res = atts ? atts->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_buffer sg_make_buffer(const sg_buffer_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_buffer_desc desc_def = _sg_buffer_desc_defaults(desc); + sg_buffer buf_id = _sg_alloc_buffer(); + if (buf_id.id != SG_INVALID_ID) { + _sg_buffer_t* buf = _sg_buffer_at(&_sg.pools, buf_id.id); + SOKOL_ASSERT(buf && (buf->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_buffer(buf, &desc_def); + SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)); + } + _SG_TRACE_ARGS(make_buffer, &desc_def, buf_id); + return buf_id; +} + +SOKOL_API_IMPL sg_image sg_make_image(const sg_image_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_image_desc desc_def = _sg_image_desc_defaults(desc); + sg_image img_id = _sg_alloc_image(); + if (img_id.id != SG_INVALID_ID) { + _sg_image_t* img = _sg_image_at(&_sg.pools, img_id.id); + SOKOL_ASSERT(img && (img->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_image(img, &desc_def); + SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)); + } + _SG_TRACE_ARGS(make_image, &desc_def, img_id); + return img_id; +} + +SOKOL_API_IMPL sg_sampler sg_make_sampler(const sg_sampler_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_sampler_desc desc_def = _sg_sampler_desc_defaults(desc); + sg_sampler smp_id = _sg_alloc_sampler(); + if (smp_id.id != SG_INVALID_ID) { + _sg_sampler_t* smp = _sg_sampler_at(&_sg.pools, smp_id.id); + SOKOL_ASSERT(smp && (smp->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_sampler(smp, &desc_def); + SOKOL_ASSERT((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)); + } + _SG_TRACE_ARGS(make_sampler, &desc_def, smp_id); + return smp_id; +} + +SOKOL_API_IMPL sg_shader sg_make_shader(const sg_shader_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_shader_desc desc_def = _sg_shader_desc_defaults(desc); + sg_shader shd_id = _sg_alloc_shader(); + if (shd_id.id != SG_INVALID_ID) { + _sg_shader_t* shd = _sg_shader_at(&_sg.pools, shd_id.id); + SOKOL_ASSERT(shd && (shd->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_shader(shd, &desc_def); + SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)); + } + _SG_TRACE_ARGS(make_shader, &desc_def, shd_id); + return shd_id; +} + +SOKOL_API_IMPL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_pipeline_desc desc_def = _sg_pipeline_desc_defaults(desc); + sg_pipeline pip_id = _sg_alloc_pipeline(); + if (pip_id.id != SG_INVALID_ID) { + _sg_pipeline_t* pip = _sg_pipeline_at(&_sg.pools, pip_id.id); + SOKOL_ASSERT(pip && (pip->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_pipeline(pip, &desc_def); + SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)); + } + _SG_TRACE_ARGS(make_pipeline, &desc_def, pip_id); + return pip_id; +} + +SOKOL_API_IMPL sg_attachments sg_make_attachments(const sg_attachments_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_attachments_desc desc_def = _sg_attachments_desc_defaults(desc); + sg_attachments atts_id = _sg_alloc_attachments(); + if (atts_id.id != SG_INVALID_ID) { + _sg_attachments_t* atts = _sg_attachments_at(&_sg.pools, atts_id.id); + SOKOL_ASSERT(atts && (atts->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_attachments(atts, &desc_def); + SOKOL_ASSERT((atts->slot.state == SG_RESOURCESTATE_VALID) || (atts->slot.state == SG_RESOURCESTATE_FAILED)); + } + _SG_TRACE_ARGS(make_attachments, &desc_def, atts_id); + return atts_id; +} + +SOKOL_API_IMPL void sg_destroy_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_buffer, buf_id); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + if ((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_buffer(buf); + SOKOL_ASSERT(buf->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_buffer(buf); + SOKOL_ASSERT(buf->slot.state == SG_RESOURCESTATE_INITIAL); + } + } +} + +SOKOL_API_IMPL void sg_destroy_image(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_image, img_id); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + if ((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_image(img); + SOKOL_ASSERT(img->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (img->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_image(img); + SOKOL_ASSERT(img->slot.state == SG_RESOURCESTATE_INITIAL); + } + } +} + +SOKOL_API_IMPL void sg_destroy_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_sampler, smp_id); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if ((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_sampler(smp); + SOKOL_ASSERT(smp->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_sampler(smp); + SOKOL_ASSERT(smp->slot.state == SG_RESOURCESTATE_INITIAL); + } + } +} + +SOKOL_API_IMPL void sg_destroy_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_shader, shd_id); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + if ((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_shader(shd); + SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_shader(shd); + SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_INITIAL); + } + } +} + +SOKOL_API_IMPL void sg_destroy_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_pipeline, pip_id); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if ((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_pipeline(pip); + SOKOL_ASSERT(pip->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_pipeline(pip); + SOKOL_ASSERT(pip->slot.state == SG_RESOURCESTATE_INITIAL); + } + } +} + +SOKOL_API_IMPL void sg_destroy_attachments(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_attachments, atts_id); + _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + if ((atts->slot.state == SG_RESOURCESTATE_VALID) || (atts->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_attachments(atts); + SOKOL_ASSERT(atts->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (atts->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_attachments(atts); + SOKOL_ASSERT(atts->slot.state == SG_RESOURCESTATE_INITIAL); + } + } +} + +SOKOL_API_IMPL void sg_begin_pass(const sg_pass* pass) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(!_sg.cur_pass.valid); + SOKOL_ASSERT(!_sg.cur_pass.in_pass); + SOKOL_ASSERT(pass); + SOKOL_ASSERT((pass->_start_canary == 0) && (pass->_end_canary == 0)); + const sg_pass pass_def = _sg_pass_defaults(pass); + if (!_sg_validate_begin_pass(&pass_def)) { + return; + } + if (pass_def.attachments.id != SG_INVALID_ID) { + // an offscreen pass + SOKOL_ASSERT(_sg.cur_pass.atts == 0); + _sg.cur_pass.atts = _sg_lookup_attachments(&_sg.pools, pass_def.attachments.id); + if (0 == _sg.cur_pass.atts) { + _SG_ERROR(BEGINPASS_ATTACHMENT_INVALID); + return; + } + _sg.cur_pass.atts_id = pass_def.attachments; + _sg.cur_pass.width = _sg.cur_pass.atts->cmn.width; + _sg.cur_pass.height = _sg.cur_pass.atts->cmn.height; + } else { + // a swapchain pass + SOKOL_ASSERT(pass_def.swapchain.width > 0); + SOKOL_ASSERT(pass_def.swapchain.height > 0); + SOKOL_ASSERT(pass_def.swapchain.color_format > SG_PIXELFORMAT_NONE); + SOKOL_ASSERT(pass_def.swapchain.sample_count > 0); + _sg.cur_pass.width = pass_def.swapchain.width; + _sg.cur_pass.height = pass_def.swapchain.height; + _sg.cur_pass.swapchain.color_fmt = pass_def.swapchain.color_format; + _sg.cur_pass.swapchain.depth_fmt = pass_def.swapchain.depth_format; + _sg.cur_pass.swapchain.sample_count = pass_def.swapchain.sample_count; + } + _sg.cur_pass.valid = true; // may be overruled by backend begin-pass functions + _sg.cur_pass.in_pass = true; + _sg_begin_pass(&pass_def); + _SG_TRACE_ARGS(begin_pass, &pass_def); +} + +SOKOL_API_IMPL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); + _sg_stats_add(num_apply_viewport, 1); + if (!_sg.cur_pass.valid) { + return; + } + _sg_apply_viewport(x, y, width, height, origin_top_left); + _SG_TRACE_ARGS(apply_viewport, x, y, width, height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left) { + sg_apply_viewport((int)x, (int)y, (int)width, (int)height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); + _sg_stats_add(num_apply_scissor_rect, 1); + if (!_sg.cur_pass.valid) { + return; + } + _sg_apply_scissor_rect(x, y, width, height, origin_top_left); + _SG_TRACE_ARGS(apply_scissor_rect, x, y, width, height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left) { + sg_apply_scissor_rect((int)x, (int)y, (int)width, (int)height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); + _sg_stats_add(num_apply_pipeline, 1); + if (!_sg_validate_apply_pipeline(pip_id)) { + _sg.next_draw_valid = false; + return; + } + if (!_sg.cur_pass.valid) { + return; + } + _sg.cur_pipeline = pip_id; + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + SOKOL_ASSERT(pip); + _sg.next_draw_valid = (SG_RESOURCESTATE_VALID == pip->slot.state); + SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->cmn.shader_id.id)); + _sg_apply_pipeline(pip); + _SG_TRACE_ARGS(apply_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); + SOKOL_ASSERT(bindings); + SOKOL_ASSERT((bindings->_start_canary == 0) && (bindings->_end_canary==0)); + _sg_stats_add(num_apply_bindings, 1); + if (!_sg_validate_apply_bindings(bindings)) { + _sg.next_draw_valid = false; + return; + } + if (!_sg.cur_pass.valid) { + return; + } + + _sg_bindings_t bnd; + _sg_clear(&bnd, sizeof(bnd)); + bnd.pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); + if (0 == bnd.pip) { + _sg.next_draw_valid = false; + } + + for (int i = 0; i < SG_MAX_VERTEX_BUFFERS; i++, bnd.num_vbs++) { + if (bindings->vertex_buffers[i].id) { + bnd.vbs[i] = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); + bnd.vb_offsets[i] = bindings->vertex_buffer_offsets[i]; + if (bnd.vbs[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.vbs[i]->slot.state); + _sg.next_draw_valid &= !bnd.vbs[i]->cmn.append_overflow; + } else { + _sg.next_draw_valid = false; + } + } else { + break; + } + } + + if (bindings->index_buffer.id) { + bnd.ib = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); + bnd.ib_offset = bindings->index_buffer_offset; + if (bnd.ib) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.ib->slot.state); + _sg.next_draw_valid &= !bnd.ib->cmn.append_overflow; + } else { + _sg.next_draw_valid = false; + } + } + + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, bnd.num_vs_imgs++) { + if (bindings->vs.images[i].id) { + bnd.vs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->vs.images[i].id); + if (bnd.vs_imgs[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.vs_imgs[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } + } else { + break; + } + } + + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++, bnd.num_vs_smps++) { + if (bindings->vs.samplers[i].id) { + bnd.vs_smps[i] = _sg_lookup_sampler(&_sg.pools, bindings->vs.samplers[i].id); + if (bnd.vs_smps[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.vs_smps[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } + } else { + break; + } + } + + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++, bnd.num_vs_sbufs++) { + if (bindings->vs.storage_buffers[i].id) { + bnd.vs_sbufs[i] = _sg_lookup_buffer(&_sg.pools, bindings->vs.storage_buffers[i].id); + if (bnd.vs_sbufs[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.vs_sbufs[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } + } else { + break; + } + } + + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, bnd.num_fs_imgs++) { + if (bindings->fs.images[i].id) { + bnd.fs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->fs.images[i].id); + if (bnd.fs_imgs[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.fs_imgs[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } + } else { + break; + } + } + + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++, bnd.num_fs_smps++) { + if (bindings->fs.samplers[i].id) { + bnd.fs_smps[i] = _sg_lookup_sampler(&_sg.pools, bindings->fs.samplers[i].id); + if (bnd.fs_smps[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.fs_smps[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } + } else { + break; + } + } + + for (int i = 0; i < SG_MAX_SHADERSTAGE_STORAGEBUFFERS; i++, bnd.num_fs_sbufs++) { + if (bindings->fs.storage_buffers[i].id) { + bnd.fs_sbufs[i] = _sg_lookup_buffer(&_sg.pools, bindings->fs.storage_buffers[i].id); + if (bnd.fs_sbufs[i]) { + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == bnd.fs_sbufs[i]->slot.state); + } else { + _sg.next_draw_valid = false; + } + } else { + break; + } + } + if (_sg.next_draw_valid) { + _sg.next_draw_valid &= _sg_apply_bindings(&bnd); + _SG_TRACE_ARGS(apply_bindings, bindings); + } +} + +SOKOL_API_IMPL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); + SOKOL_ASSERT((stage == SG_SHADERSTAGE_VS) || (stage == SG_SHADERSTAGE_FS)); + SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + _sg_stats_add(num_apply_uniforms, 1); + _sg_stats_add(size_apply_uniforms, (uint32_t)data->size); + if (!_sg_validate_apply_uniforms(stage, ub_index, data)) { + _sg.next_draw_valid = false; + return; + } + if (!_sg.cur_pass.valid) { + return; + } + if (!_sg.next_draw_valid) { + return; + } + _sg_apply_uniforms(stage, ub_index, data); + _SG_TRACE_ARGS(apply_uniforms, stage, ub_index, data); +} + +SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); + SOKOL_ASSERT(base_element >= 0); + SOKOL_ASSERT(num_elements >= 0); + SOKOL_ASSERT(num_instances >= 0); + _sg_stats_add(num_draw, 1); + if (!_sg.cur_pass.valid) { + return; + } + if (!_sg.next_draw_valid) { + return; + } + /* attempting to draw with zero elements or instances is not technically an + error, but might be handled as an error in the backend API (e.g. on Metal) + */ + if ((0 == num_elements) || (0 == num_instances)) { + return; + } + _sg_draw(base_element, num_elements, num_instances); + _SG_TRACE_ARGS(draw, base_element, num_elements, num_instances); +} + +SOKOL_API_IMPL void sg_end_pass(void) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(_sg.cur_pass.in_pass); + _sg_stats_add(num_passes, 1); + // NOTE: don't exit early if !_sg.cur_pass.valid + _sg_end_pass(); + _sg.cur_pipeline.id = SG_INVALID_ID; + _sg_clear(&_sg.cur_pass, sizeof(_sg.cur_pass)); + _SG_TRACE_NOARGS(end_pass); +} + +SOKOL_API_IMPL void sg_commit(void) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(!_sg.cur_pass.valid); + SOKOL_ASSERT(!_sg.cur_pass.in_pass); + _sg_commit(); + _sg.stats.frame_index = _sg.frame_index; + _sg.prev_stats = _sg.stats; + _sg_clear(&_sg.stats, sizeof(_sg.stats)); + _sg_notify_commit_listeners(); + _SG_TRACE_NOARGS(commit); + _sg.frame_index++; +} + +SOKOL_API_IMPL void sg_reset_state_cache(void) { + SOKOL_ASSERT(_sg.valid); + _sg_reset_state_cache(); + _SG_TRACE_NOARGS(reset_state_cache); +} + +SOKOL_API_IMPL void sg_update_buffer(sg_buffer buf_id, const sg_range* data) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + _sg_stats_add(num_update_buffer, 1); + _sg_stats_add(size_update_buffer, (uint32_t)data->size); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if ((data->size > 0) && buf && (buf->slot.state == SG_RESOURCESTATE_VALID)) { + if (_sg_validate_update_buffer(buf, data)) { + SOKOL_ASSERT(data->size <= (size_t)buf->cmn.size); + // only one update allowed per buffer and frame + SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index); + // update and append on same buffer in same frame not allowed + SOKOL_ASSERT(buf->cmn.append_frame_index != _sg.frame_index); + _sg_update_buffer(buf, data); + buf->cmn.update_frame_index = _sg.frame_index; + } + } + _SG_TRACE_ARGS(update_buffer, buf_id, data); +} + +SOKOL_API_IMPL int sg_append_buffer(sg_buffer buf_id, const sg_range* data) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(data && data->ptr); + _sg_stats_add(num_append_buffer, 1); + _sg_stats_add(size_append_buffer, (uint32_t)data->size); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + int result; + if (buf) { + // rewind append cursor in a new frame + if (buf->cmn.append_frame_index != _sg.frame_index) { + buf->cmn.append_pos = 0; + buf->cmn.append_overflow = false; + } + if (((size_t)buf->cmn.append_pos + data->size) > (size_t)buf->cmn.size) { + buf->cmn.append_overflow = true; + } + const int start_pos = buf->cmn.append_pos; + // NOTE: the multiple-of-4 requirement for the buffer offset is coming + // from WebGPU, but we want identical behaviour between backends + SOKOL_ASSERT(_sg_multiple_u64((uint64_t)start_pos, 4)); + if (buf->slot.state == SG_RESOURCESTATE_VALID) { + if (_sg_validate_append_buffer(buf, data)) { + if (!buf->cmn.append_overflow && (data->size > 0)) { + // update and append on same buffer in same frame not allowed + SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index); + _sg_append_buffer(buf, data, buf->cmn.append_frame_index != _sg.frame_index); + buf->cmn.append_pos += (int) _sg_roundup_u64(data->size, 4); + buf->cmn.append_frame_index = _sg.frame_index; + } + } + } + result = start_pos; + } else { + // FIXME: should we return -1 here? + result = 0; + } + _SG_TRACE_ARGS(append_buffer, buf_id, data, result); + return result; +} + +SOKOL_API_IMPL bool sg_query_buffer_overflow(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + bool result = buf ? buf->cmn.append_overflow : false; + return result; +} + +SOKOL_API_IMPL bool sg_query_buffer_will_overflow(sg_buffer buf_id, size_t size) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + bool result = false; + if (buf) { + int append_pos = buf->cmn.append_pos; + // rewind append cursor in a new frame + if (buf->cmn.append_frame_index != _sg.frame_index) { + append_pos = 0; + } + if ((append_pos + _sg_roundup((int)size, 4)) > buf->cmn.size) { + result = true; + } + } + return result; +} + +SOKOL_API_IMPL void sg_update_image(sg_image img_id, const sg_image_data* data) { + SOKOL_ASSERT(_sg.valid); + _sg_stats_add(num_update_image, 1); + for (int face_index = 0; face_index < SG_CUBEFACE_NUM; face_index++) { + for (int mip_index = 0; mip_index < SG_MAX_MIPMAPS; mip_index++) { + if (data->subimage[face_index][mip_index].size == 0) { + break; + } + _sg_stats_add(size_update_image, (uint32_t)data->subimage[face_index][mip_index].size); + } + } + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img && img->slot.state == SG_RESOURCESTATE_VALID) { + if (_sg_validate_update_image(img, data)) { + SOKOL_ASSERT(img->cmn.upd_frame_index != _sg.frame_index); + _sg_update_image(img, data); + img->cmn.upd_frame_index = _sg.frame_index; + } + } + _SG_TRACE_ARGS(update_image, img_id, data); +} + +SOKOL_API_IMPL void sg_push_debug_group(const char* name) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(name); + _sg_push_debug_group(name); + _SG_TRACE_ARGS(push_debug_group, name); +} + +SOKOL_API_IMPL void sg_pop_debug_group(void) { + SOKOL_ASSERT(_sg.valid); + _sg_pop_debug_group(); + _SG_TRACE_NOARGS(pop_debug_group); +} + +SOKOL_API_IMPL bool sg_add_commit_listener(sg_commit_listener listener) { + SOKOL_ASSERT(_sg.valid); + return _sg_add_commit_listener(&listener); +} + +SOKOL_API_IMPL bool sg_remove_commit_listener(sg_commit_listener listener) { + SOKOL_ASSERT(_sg.valid); + return _sg_remove_commit_listener(&listener); +} + +SOKOL_API_IMPL void sg_enable_frame_stats(void) { + SOKOL_ASSERT(_sg.valid); + _sg.stats_enabled = true; +} + +SOKOL_API_IMPL void sg_disable_frame_stats(void) { + SOKOL_ASSERT(_sg.valid); + _sg.stats_enabled = false; +} + +SOKOL_API_IMPL bool sg_frame_stats_enabled(void) { + return _sg.stats_enabled; +} + +SOKOL_API_IMPL sg_buffer_info sg_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_buffer_info info; + _sg_clear(&info, sizeof(info)); + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + info.slot.state = buf->slot.state; + info.slot.res_id = buf->slot.id; + info.update_frame_index = buf->cmn.update_frame_index; + info.append_frame_index = buf->cmn.append_frame_index; + info.append_pos = buf->cmn.append_pos; + info.append_overflow = buf->cmn.append_overflow; + #if defined(SOKOL_D3D11) + info.num_slots = 1; + info.active_slot = 0; + #else + info.num_slots = buf->cmn.num_slots; + info.active_slot = buf->cmn.active_slot; + #endif + } + return info; +} + +SOKOL_API_IMPL sg_image_info sg_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_image_info info; + _sg_clear(&info, sizeof(info)); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + info.slot.state = img->slot.state; + info.slot.res_id = img->slot.id; + info.upd_frame_index = img->cmn.upd_frame_index; + #if defined(SOKOL_D3D11) + info.num_slots = 1; + info.active_slot = 0; + #else + info.num_slots = img->cmn.num_slots; + info.active_slot = img->cmn.active_slot; + #endif + } + return info; +} + +SOKOL_API_IMPL sg_sampler_info sg_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_sampler_info info; + _sg_clear(&info, sizeof(info)); + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + info.slot.state = smp->slot.state; + info.slot.res_id = smp->slot.id; + } + return info; +} + +SOKOL_API_IMPL sg_shader_info sg_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_shader_info info; + _sg_clear(&info, sizeof(info)); + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + info.slot.state = shd->slot.state; + info.slot.res_id = shd->slot.id; + } + return info; +} + +SOKOL_API_IMPL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + sg_pipeline_info info; + _sg_clear(&info, sizeof(info)); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + info.slot.state = pip->slot.state; + info.slot.res_id = pip->slot.id; + } + return info; +} + +SOKOL_API_IMPL sg_attachments_info sg_query_attachments_info(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + sg_attachments_info info; + _sg_clear(&info, sizeof(info)); + const _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + info.slot.state = atts->slot.state; + info.slot.res_id = atts->slot.id; + } + return info; +} + +SOKOL_API_IMPL sg_buffer_desc sg_query_buffer_desc(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_buffer_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + desc.size = (size_t)buf->cmn.size; + desc.type = buf->cmn.type; + desc.usage = buf->cmn.usage; + } + return desc; +} + +SOKOL_API_IMPL sg_image_desc sg_query_image_desc(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_image_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + desc.type = img->cmn.type; + desc.render_target = img->cmn.render_target; + desc.width = img->cmn.width; + desc.height = img->cmn.height; + desc.num_slices = img->cmn.num_slices; + desc.num_mipmaps = img->cmn.num_mipmaps; + desc.usage = img->cmn.usage; + desc.pixel_format = img->cmn.pixel_format; + desc.sample_count = img->cmn.sample_count; + } + return desc; +} + +SOKOL_API_IMPL sg_sampler_desc sg_query_sampler_desc(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_sampler_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + desc.min_filter = smp->cmn.min_filter; + desc.mag_filter = smp->cmn.mag_filter; + desc.mipmap_filter = smp->cmn.mipmap_filter; + desc.wrap_u = smp->cmn.wrap_u; + desc.wrap_v = smp->cmn.wrap_v; + desc.wrap_w = smp->cmn.wrap_w; + desc.min_lod = smp->cmn.min_lod; + desc.max_lod = smp->cmn.max_lod; + desc.border_color = smp->cmn.border_color; + desc.compare = smp->cmn.compare; + desc.max_anisotropy = smp->cmn.max_anisotropy; + } + return desc; +} + +SOKOL_API_IMPL sg_shader_desc sg_query_shader_desc(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_shader_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + for (int stage_idx = 0; stage_idx < SG_NUM_SHADER_STAGES; stage_idx++) { + sg_shader_stage_desc* stage_desc = (stage_idx == 0) ? &desc.vs : &desc.fs; + const _sg_shader_stage_t* stage = &shd->cmn.stage[stage_idx]; + for (int ub_idx = 0; ub_idx < stage->num_uniform_blocks; ub_idx++) { + sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_idx]; + const _sg_shader_uniform_block_t* ub = &stage->uniform_blocks[ub_idx]; + ub_desc->size = ub->size; + } + for (int img_idx = 0; img_idx < stage->num_images; img_idx++) { + sg_shader_image_desc* img_desc = &stage_desc->images[img_idx]; + const _sg_shader_image_t* img = &stage->images[img_idx]; + img_desc->used = true; + img_desc->image_type = img->image_type; + img_desc->sample_type = img->sample_type; + img_desc->multisampled = img->multisampled; + } + for (int smp_idx = 0; smp_idx < stage->num_samplers; smp_idx++) { + sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_idx]; + const _sg_shader_sampler_t* smp = &stage->samplers[smp_idx]; + smp_desc->used = true; + smp_desc->sampler_type = smp->sampler_type; + } + for (int img_smp_idx = 0; img_smp_idx < stage->num_image_samplers; img_smp_idx++) { + sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_idx]; + const _sg_shader_image_sampler_t* img_smp = &stage->image_samplers[img_smp_idx]; + img_smp_desc->used = true; + img_smp_desc->image_slot = img_smp->image_slot; + img_smp_desc->sampler_slot = img_smp->sampler_slot; + img_smp_desc->glsl_name = 0; + } + } + } + return desc; +} + +SOKOL_API_IMPL sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + sg_pipeline_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + desc.shader = pip->cmn.shader_id; + desc.layout = pip->cmn.layout; + desc.depth = pip->cmn.depth; + desc.stencil = pip->cmn.stencil; + desc.color_count = pip->cmn.color_count; + for (int i = 0; i < pip->cmn.color_count; i++) { + desc.colors[i] = pip->cmn.colors[i]; + } + desc.primitive_type = pip->cmn.primitive_type; + desc.index_type = pip->cmn.index_type; + desc.cull_mode = pip->cmn.cull_mode; + desc.face_winding = pip->cmn.face_winding; + desc.sample_count = pip->cmn.sample_count; + desc.blend_color = pip->cmn.blend_color; + desc.alpha_to_coverage_enabled = pip->cmn.alpha_to_coverage_enabled; + } + return desc; +} + +SOKOL_API_IMPL sg_attachments_desc sg_query_attachments_desc(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + sg_attachments_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + for (int i = 0; i < atts->cmn.num_colors; i++) { + desc.colors[i].image = atts->cmn.colors[i].image_id; + desc.colors[i].mip_level = atts->cmn.colors[i].mip_level; + desc.colors[i].slice = atts->cmn.colors[i].slice; + } + desc.depth_stencil.image = atts->cmn.depth_stencil.image_id; + desc.depth_stencil.mip_level = atts->cmn.depth_stencil.mip_level; + desc.depth_stencil.slice = atts->cmn.depth_stencil.slice; + } + return desc; +} + +SOKOL_API_IMPL sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_buffer_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_image_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_sampler_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_shader_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_pipeline_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_attachments_desc sg_query_attachments_defaults(const sg_attachments_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_attachments_desc_defaults(desc); +} + +SOKOL_API_IMPL const void* sg_d3d11_device(void) { + #if defined(SOKOL_D3D11) + return (const void*) _sg.d3d11.dev; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_d3d11_device_context(void) { + #if defined(SOKOL_D3D11) + return (const void*) _sg.d3d11.ctx; + #else + return 0; + #endif +} + +SOKOL_API_IMPL sg_d3d11_buffer_info sg_d3d11_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_buffer_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + res.buf = (const void*) buf->d3d11.buf; + } + #else + _SOKOL_UNUSED(buf_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_image_info sg_d3d11_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_image_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + res.tex2d = (const void*) img->d3d11.tex2d; + res.tex3d = (const void*) img->d3d11.tex3d; + res.res = (const void*) img->d3d11.res; + res.srv = (const void*) img->d3d11.srv; + } + #else + _SOKOL_UNUSED(img_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_sampler_info sg_d3d11_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_sampler_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + res.smp = (const void*) smp->d3d11.smp; + } + #else + _SOKOL_UNUSED(smp_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_shader_info sg_d3d11_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_shader_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + for (int i = 0; i < SG_MAX_SHADERSTAGE_UBS; i++) { + res.vs_cbufs[i] = (const void*) shd->d3d11.stage[SG_SHADERSTAGE_VS].cbufs[i]; + res.fs_cbufs[i] = (const void*) shd->d3d11.stage[SG_SHADERSTAGE_FS].cbufs[i]; + } + res.vs = (const void*) shd->d3d11.vs; + res.fs = (const void*) shd->d3d11.fs; + } + #else + _SOKOL_UNUSED(shd_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_pipeline_info sg_d3d11_query_pipeline_info(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_pipeline_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + res.il = (const void*) pip->d3d11.il; + res.rs = (const void*) pip->d3d11.rs; + res.dss = (const void*) pip->d3d11.dss; + res.bs = (const void*) pip->d3d11.bs; + } + #else + _SOKOL_UNUSED(pip_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_d3d11_attachments_info sg_d3d11_query_attachments_info(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + sg_d3d11_attachments_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_D3D11) + const _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + res.color_rtv[i] = (const void*) atts->d3d11.colors[i].view.rtv; + res.resolve_rtv[i] = (const void*) atts->d3d11.resolves[i].view.rtv; + } + res.dsv = (const void*) atts->d3d11.depth_stencil.view.dsv; + } + #else + _SOKOL_UNUSED(atts_id); + #endif + return res; +} + +SOKOL_API_IMPL const void* sg_mtl_device(void) { + #if defined(SOKOL_METAL) + if (nil != _sg.mtl.device) { + return (__bridge const void*) _sg.mtl.device; + } else { + return 0; + } + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_mtl_render_command_encoder(void) { + #if defined(SOKOL_METAL) + if (nil != _sg.mtl.cmd_encoder) { + return (__bridge const void*) _sg.mtl.cmd_encoder; + } else { + return 0; + } + #else + return 0; + #endif +} + +SOKOL_API_IMPL sg_mtl_buffer_info sg_mtl_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_mtl_buffer_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + if (buf->mtl.buf[i] != 0) { + res.buf[i] = (__bridge void*) _sg_mtl_id(buf->mtl.buf[i]); + } + } + res.active_slot = buf->cmn.active_slot; + } + #else + _SOKOL_UNUSED(buf_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_mtl_image_info sg_mtl_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_mtl_image_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + if (img->mtl.tex[i] != 0) { + res.tex[i] = (__bridge void*) _sg_mtl_id(img->mtl.tex[i]); + } + } + res.active_slot = img->cmn.active_slot; + } + #else + _SOKOL_UNUSED(img_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_mtl_sampler_info sg_mtl_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_mtl_sampler_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if (smp->mtl.sampler_state != 0) { + res.smp = (__bridge void*) _sg_mtl_id(smp->mtl.sampler_state); + } + } + #else + _SOKOL_UNUSED(smp_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_mtl_shader_info sg_mtl_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_mtl_shader_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + const int vs_lib = shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib; + const int vs_func = shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func; + const int fs_lib = shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib; + const int fs_func = shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func; + if (vs_lib != 0) { + res.vs_lib = (__bridge void*) _sg_mtl_id(vs_lib); + } + if (fs_lib != 0) { + res.fs_lib = (__bridge void*) _sg_mtl_id(fs_lib); + } + if (vs_func != 0) { + res.vs_func = (__bridge void*) _sg_mtl_id(vs_func); + } + if (fs_func != 0) { + res.fs_func = (__bridge void*) _sg_mtl_id(fs_func); + } + } + #else + _SOKOL_UNUSED(shd_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_mtl_pipeline_info sg_mtl_query_pipeline_info(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + sg_mtl_pipeline_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_METAL) + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if (pip->mtl.rps != 0) { + res.rps = (__bridge void*) _sg_mtl_id(pip->mtl.rps); + } + if (pip->mtl.dss != 0) { + res.dss = (__bridge void*) _sg_mtl_id(pip->mtl.dss); + } + } + #else + _SOKOL_UNUSED(pip_id); + #endif + return res; +} + +SOKOL_API_IMPL const void* sg_wgpu_device(void) { + #if defined(SOKOL_WGPU) + return (const void*) _sg.wgpu.dev; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_wgpu_queue(void) { + #if defined(SOKOL_WGPU) + return (const void*) _sg.wgpu.queue; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_wgpu_command_encoder(void) { + #if defined(SOKOL_WGPU) + return (const void*) _sg.wgpu.cmd_enc; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sg_wgpu_render_pass_encoder(void) { + #if defined(SOKOL_WGPU) + return (const void*) _sg.wgpu.pass_enc; + #else + return 0; + #endif +} + +SOKOL_API_IMPL sg_wgpu_buffer_info sg_wgpu_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_buffer_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + res.buf = (const void*) buf->wgpu.buf; + } + #else + _SOKOL_UNUSED(buf_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_image_info sg_wgpu_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_image_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + res.tex = (const void*) img->wgpu.tex; + res.view = (const void*) img->wgpu.view; + } + #else + _SOKOL_UNUSED(img_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_sampler_info sg_wgpu_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_sampler_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + res.smp = (const void*) smp->wgpu.smp; + } + #else + _SOKOL_UNUSED(smp_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_shader_info sg_wgpu_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_shader_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + res.vs_mod = (const void*) shd->wgpu.stage[SG_SHADERSTAGE_VS].module; + res.fs_mod = (const void*) shd->wgpu.stage[SG_SHADERSTAGE_FS].module; + res.bgl = (const void*) shd->wgpu.bind_group_layout; + } + #else + _SOKOL_UNUSED(shd_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_pipeline_info sg_wgpu_query_pipeline_info(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_pipeline_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + res.pip = (const void*) pip->wgpu.pip; + } + #else + _SOKOL_UNUSED(pip_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_wgpu_attachments_info sg_wgpu_query_attachments_info(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + sg_wgpu_attachments_info res; + _sg_clear(&res, sizeof(res)); + #if defined(SOKOL_WGPU) + const _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + res.color_view[i] = (const void*) atts->wgpu.colors[i].view; + res.resolve_view[i] = (const void*) atts->wgpu.resolves[i].view; + } + res.ds_view = (const void*) atts->wgpu.depth_stencil.view; + } + #else + _SOKOL_UNUSED(atts_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_buffer_info sg_gl_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_gl_buffer_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + res.buf[i] = buf->gl.buf[i]; + } + res.active_slot = buf->cmn.active_slot; + } + #else + _SOKOL_UNUSED(buf_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_image_info sg_gl_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_gl_image_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + res.tex[i] = img->gl.tex[i]; + } + res.tex_target = img->gl.target; + res.msaa_render_buffer = img->gl.msaa_render_buffer; + res.active_slot = img->cmn.active_slot; + } + #else + _SOKOL_UNUSED(img_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_sampler_info sg_gl_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_gl_sampler_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + res.smp = smp->gl.smp; + } + #else + _SOKOL_UNUSED(smp_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_shader_info sg_gl_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_gl_shader_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + res.prog = shd->gl.prog; + } + #else + _SOKOL_UNUSED(shd_id); + #endif + return res; +} + +SOKOL_API_IMPL sg_gl_attachments_info sg_gl_query_attachments_info(sg_attachments atts_id) { + SOKOL_ASSERT(_sg.valid); + sg_gl_attachments_info res; + _sg_clear(&res, sizeof(res)); + #if defined(_SOKOL_ANY_GL) + const _sg_attachments_t* atts = _sg_lookup_attachments(&_sg.pools, atts_id.id); + if (atts) { + res.framebuffer = atts->gl.fb; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + res.msaa_resolve_framebuffer[i] = atts->gl.msaa_resolve_framebuffer[i]; + } + } + #else + _SOKOL_UNUSED(atts_id); + #endif + return res; +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif // SOKOL_GFX_IMPL diff --git a/thirdparty/ssl/linux/ssl.c b/thirdparty/ssl/linux/ssl.c new file mode 100644 index 0000000..16e6724 --- /dev/null +++ b/thirdparty/ssl/linux/ssl.c @@ -0,0 +1,19 @@ +#include "x11_dummies.c" +#include "glbind_dummies.c" + +#define SOKOL_APP_IMPL +#define SOKOL_GLCORE +#include "../../../thirdparty/sokol/sokol_app.h" +#define SOKOL_LOG_IMPL +#include "../../../thirdparty/sokol/sokol_log.h" +#define SOKOL_GFX_IMPL +#include "../../../thirdparty/sokol/sokol_gfx.h" +#define SOKOL_GLUE_IMPL +#include "../../../thirdparty/sokol/sokol_glue.h" +#define SOKOL_GL_IMPL +#include "../../../thirdparty/sokol/util/sokol_gl.h" + +void ssl_init(){ + ssl_init_x11(); + ssl_init_glbind(); +} \ No newline at end of file diff --git a/thirdparty/ssl/linux/ssl.h b/thirdparty/ssl/linux/ssl.h new file mode 100644 index 0000000..0a46d10 --- /dev/null +++ b/thirdparty/ssl/linux/ssl.h @@ -0,0 +1 @@ +void ssl_init(); \ No newline at end of file diff --git a/thirdparty/ssl/linux/x11_dummies.c b/thirdparty/ssl/linux/x11_dummies.c new file mode 100644 index 0000000..8d489e1 --- /dev/null +++ b/thirdparty/ssl/linux/x11_dummies.c @@ -0,0 +1,619 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef SSL_ERROR_CB +#include +#define SSL_ERROR_CB(...) { printf("SSL Error: "); printf(__VA_ARGS__); printf("\n"); exit(1); } +#endif + +// dlopen the X11 libraries +char* x11Handle; +char* xcursorHandle; +char* xiHandle; +// dlsym the X11 functions +// XInput2.h +int (*XISelectEvents_ptr)(Display*, Window, XIEventMask*, int); +Status(*XIQueryVersion_ptr)(Display*, int*, int*); + +// Xcursor.h +char* (*XcursorGetTheme_ptr)(Display*); +int (*XcursorGetDefaultSize_ptr)(Display*); +XcursorImage* (*XcursorLibraryLoadImage_ptr)(const char*, const char*, int); +Cursor(*XcursorLibraryLoadCursor_ptr)(Display*, const char*); +Cursor(*XcursorImageLoadCursor_ptr)(Display*, const XcursorImage*); +void (*XcursorImageDestroy_ptr)(XcursorImage*); +XcursorImage* (*XcursorImageCreate_ptr)(int, int); + +// Xlib.h +Status(*XInitThreads_ptr)(void); +Status(*XrmInitialize_ptr)(void); +Display* (*XOpenDisplay_ptr)(const char*); +Bool(*XkbSetDetectableAutoRepeat_ptr)(Display*, Bool, Bool*); +int (*XFlush_ptr)(Display*); +int (*XPending_ptr)(Display*); +int (*XNextEvent_ptr)(Display*, XEvent*); +int (*XCloseDisplay_ptr)(Display*); +int (*XDefineCursor_ptr)(Display*, Window, Cursor); +int (*XUndefineCursor_ptr)(Display*, Window); +int (*XGrabPointer_ptr)(Display*, Window, Bool, unsigned int, int, int, Window, Cursor, Time); +int (*XWarpPointer_ptr)(Display*, Window, Window, int, int, unsigned int, unsigned int, int, int); +int (*XUngrabPointer_ptr)(Display*, Time); +void (*Xutf8SetWMProperties_ptr)(Display*, Window, const char*, const char*, char**, int, XSizeHints*, XWMHints*, XClassHint*); +int (*XChangeProperty_ptr)(Display*, Window, Atom, Atom, int, int, const unsigned char*, int); +int (*XDeleteProperty_ptr)(Display*, Window, Atom); +char* (*XResourceManagerString_ptr)(Display*); +XrmDatabase(*XrmGetStringDatabase_ptr)(const char*); +Bool(*XrmGetResource_ptr)(XrmDatabase, const char*, const char*, char**, XrmValue*); +void (*XrmDestroyDatabase_ptr)(XrmDatabase); +Atom(*XInternAtom_ptr)(Display*, const char*, Bool); +Bool(*XQueryExtension_ptr)(Display*, const char*, int*, int*, int*); +int (*XFree_ptr)(void*); +Colormap(*XCreateColormap_ptr)(Display*, Window, Visual*, int); +Window(*XCreateWindow_ptr)(Display*, Window, int, int, unsigned int, unsigned int, unsigned int, int, unsigned int, Visual*, unsigned long, XSetWindowAttributes*); +Status(*XSetWMProtocols_ptr)(Display*, Window, Atom*, int); +int (*XChangeGC_ptr)(Display*, GC, unsigned long, XGCValues*); +XSizeHints* (*XAllocSizeHints_ptr)(void); +void (*XSetWMNormalHints_ptr)(Display*, Window, XSizeHints*); +int (*XMapWindow_ptr)(Display*, Window); +int (*XRaiseWindow_ptr)(Display*, Window); +Bool(*XFilterEvent_ptr)(XEvent*, Window); +Bool(*XGetEventData_ptr)(Display*, XGenericEventCookie*); +void (*XFreeEventData_ptr)(Display*, XGenericEventCookie*); +int (*XLookupString_ptr)(XKeyEvent*, char*, int, KeySym*, XComposeStatus*); +int (*XConvertSelection_ptr)(Display*, Atom, Atom, Atom, Window, Time); +Status(*XSendEvent_ptr)(Display*, Window, Bool, long, XEvent*); +int (*XUnmapWindow_ptr)(Display*, Window); +int (*XDestroyWindow_ptr)(Display*, Window); +int (*XFreeColormap_ptr)(Display*, Colormap); +int (*XFreeCursor_ptr)(Display*, Cursor); +Cursor(*XCreateFontCursor_ptr)(Display*, unsigned int); +XErrorHandler(*XSetErrorHandler_ptr)(XErrorHandler); +int (*XSync_ptr)(Display*, Bool); +Status(*XGetWindowAttributes_ptr)(Display*, Window, XWindowAttributes*); +int (*XGetWindowProperty_ptr)(Display*, Window, Atom, long, long, Bool, Atom, Atom*, int*, unsigned long*, unsigned long*, unsigned char**); +KeySym* (*XGetKeyboardMapping_ptr)(Display*, KeyCode, int, int*); + +void* check_dlsym_success(void* ptr) { + if (ptr == NULL) { + SSL_ERROR_CB("Could not load X11 function pointers: %s", dlerror()); + } + return ptr; +} + +void ssl_init_x11() { + x11Handle = dlopen("libX11.so", RTLD_LAZY); + if (!x11Handle) { + SSL_ERROR_CB("Error while loading X11: %s", dlerror()); + } + + xcursorHandle = dlopen("libXcursor.so", RTLD_LAZY); + if (!xcursorHandle) { + SSL_ERROR_CB("Error while loading Xcursor: %s", dlerror()); + } + + xiHandle = dlopen("libXi.so", RTLD_LAZY); + if (!xiHandle) { + SSL_ERROR_CB("Error while loading XInput2: %s", dlerror()); + } + + XInitThreads_ptr = check_dlsym_success(dlsym(x11Handle, "XInitThreads")); + XrmInitialize_ptr = check_dlsym_success(dlsym(x11Handle, "XrmInitialize")); + XOpenDisplay_ptr = check_dlsym_success(dlsym(x11Handle, "XOpenDisplay")); + XkbSetDetectableAutoRepeat_ptr = check_dlsym_success(dlsym(x11Handle, "XkbSetDetectableAutoRepeat")); + XFlush_ptr = check_dlsym_success(dlsym(x11Handle, "XFlush")); + XPending_ptr = check_dlsym_success(dlsym(x11Handle, "XPending")); + XNextEvent_ptr = check_dlsym_success(dlsym(x11Handle, "XNextEvent")); + XCloseDisplay_ptr = check_dlsym_success(dlsym(x11Handle, "XCloseDisplay")); + XDefineCursor_ptr = check_dlsym_success(dlsym(x11Handle, "XDefineCursor")); + XUndefineCursor_ptr = check_dlsym_success(dlsym(x11Handle, "XUndefineCursor")); + XISelectEvents_ptr = check_dlsym_success(dlsym(xiHandle, "XISelectEvents")); + XGrabPointer_ptr = check_dlsym_success(dlsym(x11Handle, "XGrabPointer")); + XWarpPointer_ptr = check_dlsym_success(dlsym(x11Handle, "XWarpPointer")); + XUngrabPointer_ptr = check_dlsym_success(dlsym(x11Handle, "XUngrabPointer")); + Xutf8SetWMProperties_ptr = check_dlsym_success(dlsym(x11Handle, "Xutf8SetWMProperties")); + XChangeProperty_ptr = check_dlsym_success(dlsym(x11Handle, "XChangeProperty")); + XDeleteProperty_ptr = check_dlsym_success(dlsym(x11Handle, "XDeleteProperty")); + XResourceManagerString_ptr = check_dlsym_success(dlsym(x11Handle, "XResourceManagerString")); + XrmGetStringDatabase_ptr = check_dlsym_success(dlsym(x11Handle, "XrmGetStringDatabase")); + XrmGetResource_ptr = check_dlsym_success(dlsym(x11Handle, "XrmGetResource")); + XrmDestroyDatabase_ptr = check_dlsym_success(dlsym(x11Handle, "XrmDestroyDatabase")); + XInternAtom_ptr = check_dlsym_success(dlsym(x11Handle, "XInternAtom")); + XQueryExtension_ptr = check_dlsym_success(dlsym(x11Handle, "XQueryExtension")); + XIQueryVersion_ptr = check_dlsym_success(dlsym(xiHandle, "XIQueryVersion")); + XcursorGetTheme_ptr = check_dlsym_success(dlsym(xcursorHandle, "XcursorGetTheme")); + XcursorGetDefaultSize_ptr = check_dlsym_success(dlsym(xcursorHandle, "XcursorGetDefaultSize")); + XFree_ptr = check_dlsym_success(dlsym(x11Handle, "XFree")); + XCreateColormap_ptr = check_dlsym_success(dlsym(x11Handle, "XCreateColormap")); + XCreateWindow_ptr = check_dlsym_success(dlsym(x11Handle, "XCreateWindow")); + XSetWMProtocols_ptr = check_dlsym_success(dlsym(x11Handle, "XSetWMProtocols")); + XChangeGC_ptr = check_dlsym_success(dlsym(x11Handle, "XChangeGC")); + XAllocSizeHints_ptr = check_dlsym_success(dlsym(x11Handle, "XAllocSizeHints")); + XSetWMNormalHints_ptr = check_dlsym_success(dlsym(x11Handle, "XSetWMNormalHints")); + XMapWindow_ptr = check_dlsym_success(dlsym(x11Handle, "XMapWindow")); + XRaiseWindow_ptr = check_dlsym_success(dlsym(x11Handle, "XRaiseWindow")); + XFilterEvent_ptr = check_dlsym_success(dlsym(x11Handle, "XFilterEvent")); + XGetEventData_ptr = check_dlsym_success(dlsym(x11Handle, "XGetEventData")); + XFreeEventData_ptr = check_dlsym_success(dlsym(x11Handle, "XFreeEventData")); + XLookupString_ptr = check_dlsym_success(dlsym(x11Handle, "XLookupString")); + XConvertSelection_ptr = check_dlsym_success(dlsym(x11Handle, "XConvertSelection")); + XSendEvent_ptr = check_dlsym_success(dlsym(x11Handle, "XSendEvent")); + XUnmapWindow_ptr = check_dlsym_success(dlsym(x11Handle, "XUnmapWindow")); + XDestroyWindow_ptr = check_dlsym_success(dlsym(x11Handle, "XDestroyWindow")); + XFreeColormap_ptr = check_dlsym_success(dlsym(x11Handle, "XFreeColormap")); + XFreeCursor_ptr = check_dlsym_success(dlsym(x11Handle, "XFreeCursor")); + XcursorLibraryLoadImage_ptr = check_dlsym_success(dlsym(xcursorHandle, "XcursorLibraryLoadImage")); + XcursorLibraryLoadCursor_ptr = check_dlsym_success(dlsym(xcursorHandle, "XcursorLibraryLoadCursor")); + XcursorImageLoadCursor_ptr = check_dlsym_success(dlsym(xcursorHandle, "XcursorImageLoadCursor")); + XcursorImageDestroy_ptr = check_dlsym_success(dlsym(xcursorHandle, "XcursorImageDestroy")); + XCreateFontCursor_ptr = check_dlsym_success(dlsym(x11Handle, "XCreateFontCursor")); + XcursorImageCreate_ptr = check_dlsym_success(dlsym(xcursorHandle, "XcursorImageCreate")); + XSetErrorHandler_ptr = check_dlsym_success(dlsym(x11Handle, "XSetErrorHandler")); + XSync_ptr = check_dlsym_success(dlsym(x11Handle, "XSync")); + XGetWindowAttributes_ptr = check_dlsym_success(dlsym(x11Handle, "XGetWindowAttributes")); + XGetWindowProperty_ptr = check_dlsym_success(dlsym(x11Handle, "XGetWindowProperty")); + XGetKeyboardMapping_ptr = check_dlsym_success(dlsym(x11Handle, "XGetKeyboardMapping")); +} + +Status XInitThreads( + void +) { + return XInitThreads_ptr(); +} + +void XrmInitialize( + void +) { + XrmInitialize_ptr(); +} + +Display* XOpenDisplay( + _Xconst char* display_name +) { + return XOpenDisplay_ptr(display_name); +} + +Bool XkbSetDetectableAutoRepeat( + Display* dpy, + Bool detectable, + Bool* supported +) { + return XkbSetDetectableAutoRepeat_ptr(dpy, detectable, supported); +} + +int XFlush( + Display* display +) { + return XFlush_ptr(display); +} + +int XPending( + Display* display +) { + return XPending_ptr(display); +} + +int XNextEvent( + Display* display, + XEvent* event_return +) { + return XNextEvent_ptr(display, event_return); +} + +int XCloseDisplay( + Display* display +) { + return XCloseDisplay_ptr(display); +} + +int XDefineCursor( + Display* display, + Window w, + Cursor cursor +) { + return XDefineCursor_ptr(display, w, cursor); +} + +int XUndefineCursor( + Display* display, + Window w +) { + return XUndefineCursor_ptr(display, w); +} + +int XISelectEvents( + Display* dpy, + Window win, + XIEventMask* masks, + int num_masks +) { + return XISelectEvents_ptr(dpy, win, masks, num_masks); +} + +int XGrabPointer( + Display* display, + Window grab_window, + Bool owner_events, + unsigned int event_mask, + int pointer_mode, + int keyboard_mode, + Window confine_to, + Cursor cursor, + Time time +) { + return XGrabPointer_ptr(display, grab_window, owner_events, event_mask, pointer_mode, keyboard_mode, confine_to, cursor, time); +} + +int XWarpPointer( + Display* display, + Window src_w, + Window dest_w, + int src_x, + int src_y, + unsigned int src_width, + unsigned int src_height, + int dest_x, + int dest_y +) { + return XWarpPointer_ptr(display, src_w, dest_w, src_x, src_y, src_width, src_height, dest_x, dest_y); +} + +int XUngrabPointer( + Display* display, + Time time +) { + return XUngrabPointer_ptr(display, time); +} + +void Xutf8SetWMProperties( + Display* display, + Window w, + _Xconst char* window_name, + _Xconst char* icon_name, + char** argv, + int argc, + XSizeHints* normal_hints, + XWMHints* wm_hints, + XClassHint* class_hints +) { + Xutf8SetWMProperties_ptr(display, w, window_name, icon_name, argv, argc, normal_hints, wm_hints, class_hints); +} + +int XChangeProperty( + Display* display, + Window w, + Atom property, + Atom type, + int format, + int mode, + _Xconst unsigned char* data, + int nelements +) { + return XChangeProperty_ptr(display, w, property, type, format, mode, data, nelements); +} + +int XDeleteProperty( + Display* display, + Window w, + Atom property +) { + return XDeleteProperty_ptr(display, w, property); +} + +int XSetWMProtocols( + Display* display, + Window w, + Atom* protocols, + int count +) { + return XSetWMProtocols_ptr(display, w, protocols, count); +} + +int XChangeGC( + Display* display, + GC gc, + unsigned long valuemask, + XGCValues* values +) { + return XChangeGC_ptr(display, gc, valuemask, values); +} + +int XGetWindowProperty( + Display* display, + Window w, + Atom property, + long long_offset, + long long_length, + Bool delete, + Atom req_type, + Atom* actual_type_return, + int* actual_format_return, + unsigned long* nitems_return, + unsigned long* bytes_after_return, + unsigned char** prop_return +) { + return XGetWindowProperty_ptr(display, w, property, long_offset, long_length, delete, req_type, actual_type_return, actual_format_return, nitems_return, bytes_after_return, prop_return); +} + +int XFree( + void* data +) { + return XFree_ptr(data); +} + +Atom XInternAtom( + Display* display, + _Xconst char* atom_name, + Bool only_if_exists +) { + return XInternAtom_ptr(display, atom_name, only_if_exists); +} + +char* XResourceManagerString( + Display* display +) { + return XResourceManagerString_ptr(display); +} + +XrmDatabase XrmGetStringDatabase( + _Xconst char* data // null terminated string +) { + return XrmGetStringDatabase_ptr(data); +} + +Bool XrmGetResource( + XrmDatabase database, + _Xconst char* str_name, + _Xconst char* str_class, + char** str_type_return, + XrmValue* value_return +) { + return XrmGetResource_ptr(database, str_name, str_class, str_type_return, value_return); +} + +void XrmDestroyDatabase( + XrmDatabase database +) { + XrmDestroyDatabase_ptr(database); +} + +Bool XQueryExtension( + Display* display, + _Xconst char* name, + int* major_opcode_return, + int* first_event_return, + int* first_error_return +) { + return XQueryExtension_ptr(display, name, major_opcode_return, first_event_return, first_error_return); +} + +Status XIQueryVersion( + Display* dpy, + int* major_version_inout, + int* minor_version_inout +) { + return XIQueryVersion_ptr(dpy, major_version_inout, minor_version_inout); +} + +char* +XcursorGetTheme(Display* dpy) +{ + return XcursorGetTheme_ptr(dpy); +} + +int +XcursorGetDefaultSize(Display* dpy) +{ + return XcursorGetDefaultSize_ptr(dpy); +} + +Colormap XCreateColormap( + Display* display, + Window w, + Visual* visual, + int alloc +) { + return XCreateColormap_ptr(display, w, visual, alloc); +} + +Window XCreateWindow( + Display* display, + Window parent, + int x, + int y, + unsigned int width, + unsigned int height, + unsigned int border_width, + int depth, + unsigned int class, + Visual* visual, + unsigned long valuemask, + XSetWindowAttributes* attributes +) { + return XCreateWindow_ptr(display, parent, x, y, width, height, border_width, depth, class, visual, valuemask, attributes); +} + +XSizeHints* XAllocSizeHints( + void +) { + return XAllocSizeHints_ptr(); +} + +void XSetWMNormalHints( + Display* display, + Window w, + XSizeHints* hints +) { + XSetWMNormalHints_ptr(display, w, hints); +} + +int XMapWindow( + Display* display, + Window w +) { + return XMapWindow_ptr(display, w); +} + +int XRaiseWindow( + Display* display, + Window w +) { + return XRaiseWindow_ptr(display, w); +} + +Bool XFilterEvent( + XEvent* event, + Window window +) { + return XFilterEvent_ptr(event, window); +} + +Bool XGetEventData( + Display* dpy, + XGenericEventCookie* cookie +) { + return XGetEventData_ptr(dpy, cookie); +} + +void XFreeEventData( + Display* dpy, + XGenericEventCookie* cookie +) { + XFreeEventData_ptr(dpy, cookie); +} + +int XLookupString( + XKeyEvent* event_struct, + char* buffer_return, + int bytes_buffer, + KeySym* keysym_return, + XComposeStatus* status_in_out +) { + return XLookupString_ptr(event_struct, buffer_return, bytes_buffer, keysym_return, status_in_out); +} + +int XConvertSelection( + Display* display, + Atom selection, + Atom target, + Atom property, + Window requestor, + Time time +) { + return XConvertSelection_ptr(display, selection, target, property, requestor, time); +} + +Status XSendEvent( + Display* display, + Window w, + Bool propagate, + long event_mask, + XEvent* event_send +) { + return XSendEvent_ptr(display, w, propagate, event_mask, event_send); +} + +int XUnmapWindow( + Display* display, + Window w +) { + return XUnmapWindow_ptr(display, w); +} + +int XDestroyWindow( + Display* display, + Window w +) { + return XDestroyWindow_ptr(display, w); +} + +int XFreeColormap( + Display* display, + Colormap colormap +) { + return XFreeColormap_ptr(display, colormap); +} + +int XFreeCursor( + Display* display, + Cursor cursor +) { + return XFreeCursor_ptr(display, cursor); +} + +XcursorImage* +XcursorLibraryLoadImage(const char* library, const char* theme, int size) +{ + return XcursorLibraryLoadImage_ptr(library, theme, size); +} + +Cursor +XcursorLibraryLoadCursor(Display* dpy, const char* file) +{ + return XcursorLibraryLoadCursor_ptr(dpy, file); +} + +Cursor +XcursorImageLoadCursor(Display* dpy, const XcursorImage* image) +{ + return XcursorImageLoadCursor_ptr(dpy, image); +} + +void +XcursorImageDestroy(XcursorImage* image) +{ + XcursorImageDestroy_ptr(image); +} + +Cursor XCreateFontCursor( + Display* display, + unsigned int shape +) { + return XCreateFontCursor_ptr(display, shape); +} + +XcursorImage* +XcursorImageCreate(int width, int height) { + return XcursorImageCreate_ptr(width, height); +} + +XErrorHandler XSetErrorHandler( + XErrorHandler x11Handler +) { + return XSetErrorHandler_ptr(x11Handler); +} + +int XSync( + Display* display, + Bool discard +) { + return XSync_ptr(display, discard); +} + +Status XGetWindowAttributes( + Display* display, + Window w, + XWindowAttributes* window_attributes_return +) { + return XGetWindowAttributes_ptr(display, w, window_attributes_return); +} + +KeySym* XGetKeyboardMapping( + Display* display, +#if NeedWidePrototypes + unsigned int first_keycode, +#else + KeyCode first_keycode, +#endif + int keycode_count, + int* keysyms_per_keycode_return +) { + return XGetKeyboardMapping_ptr(display, first_keycode, keycode_count, keysyms_per_keycode_return); +} \ No newline at end of file diff --git a/thirdparty/thread.h/README.md b/thirdparty/thread.h/README.md new file mode 100644 index 0000000..202c888 --- /dev/null +++ b/thirdparty/thread.h/README.md @@ -0,0 +1,25 @@ +# thread.h +A simple header-only library for cross-plattform multithreading in C: https://github.com/mattiasgustavsson/libs/blob/main/thread.h + +## License +MIT License + +Copyright (c) 2015 Mattias Gustavsson + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/thirdparty/thread.h/thread.h b/thirdparty/thread.h/thread.h new file mode 100644 index 0000000..624ec80 --- /dev/null +++ b/thirdparty/thread.h/thread.h @@ -0,0 +1,1505 @@ +/* +------------------------------------------------------------------------------ + Licensing information can be found at the end of the file. +------------------------------------------------------------------------------ + +thread.h - v0.3 - Cross platform threading functions for C/C++. + +Do this: + #define THREAD_IMPLEMENTATION +before you include this file in *one* C/C++ file to create the implementation. +*/ + +#ifndef thread_h +#define thread_h + +#ifndef THREAD_U64 + #define THREAD_U64 unsigned long long +#endif + +#define THREAD_STACK_SIZE_DEFAULT ( 0 ) +#define THREAD_SIGNAL_WAIT_INFINITE ( -1 ) +#define THREAD_QUEUE_WAIT_INFINITE ( -1 ) + +typedef void* thread_id_t; +thread_id_t thread_current_thread_id( void ); +void thread_yield( void ); +void thread_set_high_priority( void ); +void thread_exit( int return_code ); + +typedef void* thread_ptr_t; +thread_ptr_t thread_create( int (*thread_proc)( void* ), void* user_data, int stack_size ); +void thread_destroy( thread_ptr_t thread ); +int thread_join( thread_ptr_t thread ); +int thread_detach( thread_ptr_t thread ); + +typedef union thread_mutex_t thread_mutex_t; +void thread_mutex_init( thread_mutex_t* mutex ); +void thread_mutex_term( thread_mutex_t* mutex ); +void thread_mutex_lock( thread_mutex_t* mutex ); +void thread_mutex_unlock( thread_mutex_t* mutex ); + +typedef union thread_signal_t thread_signal_t; +void thread_signal_init( thread_signal_t* signal ); +void thread_signal_term( thread_signal_t* signal ); +void thread_signal_raise( thread_signal_t* signal ); +int thread_signal_wait( thread_signal_t* signal, int timeout_ms ); + +typedef union thread_atomic_int_t thread_atomic_int_t; +int thread_atomic_int_load( thread_atomic_int_t* atomic ); +void thread_atomic_int_store( thread_atomic_int_t* atomic, int desired ); +int thread_atomic_int_inc( thread_atomic_int_t* atomic ); +int thread_atomic_int_dec( thread_atomic_int_t* atomic ); +int thread_atomic_int_add( thread_atomic_int_t* atomic, int value ); +int thread_atomic_int_sub( thread_atomic_int_t* atomic, int value ); +int thread_atomic_int_swap( thread_atomic_int_t* atomic, int desired ); +int thread_atomic_int_compare_and_swap( thread_atomic_int_t* atomic, int expected, int desired ); + +typedef union thread_atomic_ptr_t thread_atomic_ptr_t; +void* thread_atomic_ptr_load( thread_atomic_ptr_t* atomic ); +void thread_atomic_ptr_store( thread_atomic_ptr_t* atomic, void* desired ); +void* thread_atomic_ptr_swap( thread_atomic_ptr_t* atomic, void* desired ); +void* thread_atomic_ptr_compare_and_swap( thread_atomic_ptr_t* atomic, void* expected, void* desired ); + +typedef union thread_timer_t thread_timer_t; +void thread_timer_init( thread_timer_t* timer ); +void thread_timer_term( thread_timer_t* timer ); +void thread_timer_wait( thread_timer_t* timer, THREAD_U64 nanoseconds ); + +typedef void* thread_tls_t; +thread_tls_t thread_tls_create( void ); +void thread_tls_destroy( thread_tls_t tls ); +void thread_tls_set( thread_tls_t tls, void* value ); +void* thread_tls_get( thread_tls_t tls ); + +typedef struct thread_queue_t thread_queue_t; +void thread_queue_init( thread_queue_t* queue, int size, void** values, int count ); +void thread_queue_term( thread_queue_t* queue ); +int thread_queue_produce( thread_queue_t* queue, void* value, int timeout_ms ); +void* thread_queue_consume( thread_queue_t* queue, int timeout_ms ); +int thread_queue_count( thread_queue_t* queue ); + +#endif /* thread_h */ + + +/** + +thread.h +======== + +Cross platform threading functions for C/C++. + +Example +------- + +Here's a basic sample program which starts a second thread which just waits and prints a message. + + #define THREAD_IMPLEMENTATION + #include "thread.h" + + #include // for printf + + int thread_proc( void* user_data) { + thread_timer_t timer; + thread_timer_init( &timer ); + + int count = 0; + thread_atomic_int_t* exit_flag = (thread_atomic_int_t*) user_data; + while( thread_atomic_int_load( exit_flag ) == 0 ) { + printf( "Thread... " ); + thread_timer_wait( &timer, 1000000000 ); // sleep for a second + ++count; + } + + thread_timer_term( &timer ); + printf( "Done\n" ); + return count; + } + + int main( int argc, char** argv ) { + thread_atomic_int_t exit_flag; + thread_atomic_int_store( &exit_flag, 0 ); + + thread_ptr_t thread = thread_create( thread_proc, &exit_flag, "Example thread", THREAD_STACK_SIZE_DEFAULT ); + + thread_timer_t timer; + thread_timer_init( &timer ); + for( int i = 0; i < 5; ++i ) { + printf( "Main... " ); + thread_timer_wait( &timer, 2000000000 ); // sleep for two seconds + } + thread_timer_term( &timer ); + + thread_atomic_int_store( &exit_flag, 1 ); // signal thread to exit + int retval = thread_join( thread ); + + printf( "Count: %d\n", retval ); + + thread_destroy( thread ); + return retval; + } + + +API Documentation +----------------- + +thread.h is a single-header library, and does not need any .lib files or other binaries, or any build scripts. To use it, +you just include thread.h to get the API declarations. To get the definitions, you must include thread.h from *one* +single C or C++ file, and #define the symbol `THREAD_IMPLEMENTATION` before you do. + + +### Customization + +thread.h allows for specifying the exact type of 64-bit unsigned integer to be used in its API. By default, it is +defined as `unsigned long long`, but as this is not a standard type on all compilers, you can redefine it by #defining +THREAD_U64 before including thread.h. This is useful if you, for example, use the types from `` in the rest of +your program, and you want thread.h to use compatible types. In this case, you would include thread.h using the +following code: + + #define THREAD_U64 uint64_t + #include "thread.h" + +Note that when customizing this data type, you need to use the same definition in every place where you include +thread.h, as it affect the declarations as well as the definitions. + + +thread_current_thread_id +------------------------ + + thread_id_t thread_current_thread_id( void ) + +Returns a unique identifier for the calling thread. After the thread terminates, the id might be reused for new threads. + + +thread_yield +------------ + + void thread_yield( void ) + +Makes the calling thread yield execution to another thread. The operating system controls which thread is switched to. + + +thread_set_high_priority +------------------------ + + void thread_set_high_priority( void ) + +When created, threads are set to run at normal priority. In some rare cases, such as a sound buffer update loop, it can +be necessary to have one thread of your application run on a higher priority than the rest. Calling +`thread_set_high_priority` will raise the priority of the calling thread, giving it a chance to be run more often. +Do not increase the priority of a thread unless you absolutely have to, as it can negatively affect performance if used +without care. + + +thread_exit +----------- + + void thread_exit( int return_code ) + +Exits the calling thread, as if you had done `return return_code;` from the main body of the thread function. + + +thread_create +------------- + + thread_ptr_t thread_create( int (*thread_proc)( void* ), void* user_data, int stack_size ) + +Creates a new thread running the `thread_proc` function, passing the `user_data` through to it. The thread will have +the stack size specified in the `stack_size` parameter. To get the operating system default stack size, use the +defined constant `THREAD_STACK_SIZE_DEFAULT`. When returning from the thread_proc function, the value you return can +be received in another thread by calling thread_join. `thread_create` returns a pointer to the thread instance, which +can be used as a parameter to the functions `thread_destroy` and `thread_join`. + + +thread_destroy +-------------- + + void thread_destroy( thread_ptr_t thread ) + +Destroys a thread that was created by calling `thread_create`. Make sure the thread has exited before you attempt to +destroy it. This can be accomplished by calling `thread_join`. It is not possible for force termination of a thread by +calling `thread_destroy`. + + +thread_join +----------- + + int thread_join( thread_ptr_t thread ) + +Waits for the specified thread to exit. Returns the value which the thread returned when exiting. + + +thread_detach +------------- + + int thread_detach( thread_ptr_t thread ) + +Marks the thread as detached. When a detached thread terminates, its resources are automatically released back to the +system without the need for another thread to join with the terminated thread. + + +thread_mutex_init +----------------- + + void thread_mutex_init( thread_mutex_t* mutex ) + +Initializes the specified mutex instance, preparing it for use. A mutex can be used to lock sections of code, such that +it can only be run by one thread at a time. + + +thread_mutex_term +----------------- + + void thread_mutex_term( thread_mutex_t* mutex ) + +Terminates the specified mutex instance, releasing any system resources held by it. + + +thread_mutex_lock +----------------- + + void thread_mutex_lock( thread_mutex_t* mutex ) + +Takes an exclusive lock on a mutex. If the lock is already taken by another thread, `thread_mutex_lock` will yield the +calling thread and wait for the lock to become available before returning. The mutex must be initialized by calling +`thread_mutex_init` before it can be locked. + + +thread_mutex_unlock +------------------- + + void thread_mutex_unlock( thread_mutex_t* mutex ) + +Releases a lock taken by calling `thread_mutex_lock`. + + +thread_signal_init +------------------ + + void thread_signal_init( thread_signal_t* signal ) + +Initializes the specified signal instance, preparing it for use. A signal works like a flag, which can be waited on by +one thread, until it is raised from another thread. + + +thread_signal_term +------------------ + + void thread_signal_term( thread_signal_t* signal ) + +Terminates the specified signal instance, releasing any system resources held by it. + + +thread_signal_raise +------------------- + + void thread_signal_raise( thread_signal_t* signal ) + +Raise the specified signal. Other threads waiting for the signal will proceed. + + +thread_signal_wait +------------------ + + int thread_signal_wait( thread_signal_t* signal, int timeout_ms ) + +Waits for a signal to be raised, or until `timeout_ms` milliseconds have passed. If the wait timed out, a value of 0 is +returned, otherwise a non-zero value is returned. If the `timeout_ms` parameter is THREAD_SIGNAL_WAIT_INFINITE, +`thread_signal_wait` waits indefinitely. + + +thread_atomic_int_load +---------------------- + + int thread_atomic_int_load( thread_atomic_int_t* atomic ) + +Returns the value of `atomic` as an atomic operation. + + +thread_atomic_int_store +----------------------- + + void thread_atomic_int_store( thread_atomic_int_t* atomic, int desired ) + +Sets the value of `atomic` as an atomic operation. + + +thread_atomic_int_inc +--------------------- + + int thread_atomic_int_inc( thread_atomic_int_t* atomic ) + +Increments the value of `atomic` by one, as an atomic operation. Returns the value `atomic` had before the operation. + + +thread_atomic_int_dec +--------------------- + + int thread_atomic_int_dec( thread_atomic_int_t* atomic ) + +Decrements the value of `atomic` by one, as an atomic operation. Returns the value `atomic` had before the operation. + + +thread_atomic_int_add +--------------------- + + int thread_atomic_int_add( thread_atomic_int_t* atomic, int value ) + +Adds the specified value to `atomic`, as an atomic operation. Returns the value `atomic` had before the operation. + + +thread_atomic_int_sub +--------------------- + + int thread_atomic_int_sub( thread_atomic_int_t* atomic, int value ) + +Subtracts the specified value to `atomic`, as an atomic operation. Returns the value `atomic` had before the operation. + + +thread_atomic_int_swap +---------------------- + + int thread_atomic_int_swap( thread_atomic_int_t* atomic, int desired ) + +Sets the value of `atomic` as an atomic operation. Returns the value `atomic` had before the operation. + + +thread_atomic_int_compare_and_swap +---------------------------------- + + int thread_atomic_int_compare_and_swap( thread_atomic_int_t* atomic, int expected, int desired ) + +Compares the value of `atomic` to the value of `expected`, and if they match, sets the vale of `atomic` to `desired`, +all as an atomic operation. Returns the value `atomic` had before the operation. + + +thread_atomic_ptr_load +---------------------- + + void* thread_atomic_ptr_load( thread_atomic_ptr_t* atomic ) + +Returns the value of `atomic` as an atomic operation. + + +thread_atomic_ptr_store +----------------------- + + void thread_atomic_ptr_store( thread_atomic_ptr_t* atomic, void* desired ) + +Sets the value of `atomic` as an atomic operation. + + +thread_atomic_ptr_swap +---------------------- + + void* thread_atomic_ptr_swap( thread_atomic_ptr_t* atomic, void* desired ) + +Sets the value of `atomic` as an atomic operation. Returns the value `atomic` had before the operation. + + +thread_atomic_ptr_compare_and_swap +---------------------------------- + + void* thread_atomic_ptr_compare_and_swap( thread_atomic_ptr_t* atomic, void* expected, void* desired ) + +Compares the value of `atomic` to the value of `expected`, and if they match, sets the vale of `atomic` to `desired`, +all as an atomic operation. Returns the value `atomic` had before the operation. + + +thread_timer_init +----------------- + + void thread_timer_init( thread_timer_t* timer ) + +Initializes the specified timer instance, preparing it for use. A timer can be used to sleep a thread for a high +precision duration. + + +thread_timer_term +----------------- + + void thread_timer_term( thread_timer_t* timer ) + +Terminates the specified timer instance, releasing any system resources held by it. + + +thread_timer_wait +----------------- + + void thread_timer_wait( thread_timer_t* timer, THREAD_U64 nanoseconds ) + +Waits until `nanoseconds` amount of time have passed, before returning. + + +thread_tls_create +----------------- + + thread_tls_t thread_tls_create( void ) + +Creates a thread local storage (TLS) index. Once created, each thread has its own value for that TLS index, which can +be set or retrieved individually. + + +thread_tls_destroy +------------------ + + void thread_tls_destroy( thread_tls_t tls ) + +Destroys the specified TLS index. No further calls to `thread_tls_set` or `thread_tls_get` are valid after this. + + +thread_tls_set +-------------- + + void thread_tls_set( thread_tls_t tls, void* value ) + +Stores a value in the calling thread's slot for the specified TLS index. Each thread has its own value for each TLS +index. + + +thread_tls_get +-------------- + + void* thread_tls_get( thread_tls_t tls ) + +Retrieves the value from the calling thread's slot for the specified TLS index. Each thread has its own value for each +TLS index. + + +thread_queue_init +----------------- + + void thread_queue_init( thread_queue_t* queue, int size, void** values, int count ) + +Initializes the specified queue instance, preparing it for use. The queue is a lock-free (but not wait-free) +single-producer/single-consumer queue - it will not acquire any locks as long as there is space for adding or items to +be consume, but will lock and wait when there is not. The `size` parameter specifies the number of elements in the +queue. The `values` parameter is an array of queue slots (`size` elements in length), each being of type `void*`. If +the queue is initially empty, the `count` parameter should be 0, otherwise it indicates the number of entires, from the +start of the `values` array, that the queue is initialized with. The `values` array is not copied, and must remain valid +until `thread_queue_term` is called. + + +thread_queue_term +----------------- + + void thread_queue_term( thread_queue_t* queue ) + +Terminates the specified queue instance, releasing any system resources held by it. + + +thread_queue_produce +-------------------- + + int thread_queue_produce( thread_queue_t* queue, void* value, int timeout_ms ) + +Adds an element to a single-producer/single-consumer queue. If there is space in the queue to add another element, no +lock will be taken. If the queue is full, calling thread will sleep until an element is consumed from another thread, +before adding the element, or until `timeout_ms` milliseconds have passed. If the wait timed out, a value of 0 is +returned, otherwise a non-zero value is returned. If the `timeout_ms` parameter is THREAD_QUEUE_WAIT_INFINITE, +`thread_queue_produce` waits indefinitely. + + +thread_queue_consume +-------------------- + + void* thread_queue_consume( thread_queue_t* queue, int timeout_ms ) + +Removes an element from a single-producer/single-consumer queue. If the queue contains at least one element, no lock +will be taken. If the queue is empty, the calling thread will sleep until an element is added from another thread, or +until `timeout_ms` milliseconds have passed. If the wait timed out, a value of NULL is returned, otherwise +`thread_queue_consume` returns the value that was removed from the queue. If the `timeout_ms` parameter is +THREAD_QUEUE_WAIT_INFINITE, `thread_queue_consume` waits indefinitely. + + +thread_queue_count +------------------ + + int thread_queue_count( thread_queue_t* queue ) + +Returns the number of elements currently held in a single-producer/single-consumer queue. Be aware that by the time you +get the count, it might have changed by another thread calling consume or produce, so use with care. + +*/ + + +/* +---------------------- + IMPLEMENTATION +---------------------- +*/ + +#ifndef thread_impl +#define thread_impl + +union thread_mutex_t + { + void* align; + char data[ 64 ]; + }; + +union thread_signal_t + { + void* align; + char data[ 116 ]; + }; + +union thread_atomic_int_t + { + void* align; + long i; + }; + +union thread_atomic_ptr_t + { + void* ptr; + }; + +union thread_timer_t + { + void* data; + char d[ 8 ]; + }; + +struct thread_queue_t + { + thread_signal_t data_ready; + thread_signal_t space_open; + thread_atomic_int_t count; + thread_atomic_int_t head; + thread_atomic_int_t tail; + void** values; + int size; + #ifndef NDEBUG + thread_atomic_int_t id_produce_is_set; + thread_id_t id_produce; + thread_atomic_int_t id_consume_is_set; + thread_id_t id_consume; + #endif + }; + +#endif /* thread_impl */ + + + +#ifdef THREAD_IMPLEMENTATION +#undef THREAD_IMPLEMENTATION + +#ifndef THREAD_ASSERT + #undef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #undef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #include + #define THREAD_ASSERT( expression, message ) assert( ( expression ) && ( message ) ) +#endif + + +#if defined( _WIN32 ) + + #pragma comment( lib, "winmm.lib" ) + + #define _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_SECURE_NO_WARNINGS + + #if !defined( _WIN32_WINNT ) || _WIN32_WINNT < 0x0501 + #undef _WIN32_WINNT + #define _WIN32_WINNT 0x501// requires Windows XP minimum + #endif + + #define _WINSOCKAPI_ + #pragma warning( push ) + #pragma warning( disable: 4619 ) + #pragma warning( disable: 4668 ) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' + #pragma warning( disable: 4768 ) // __declspec attributes before linkage specification are ignored + #pragma warning( disable: 4255 ) // 'function' : no function prototype given: converting '()' to '(void)' + #include + #pragma warning( pop ) + + +#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + #include + #include + #include + #include + #include + +#else + #error Unknown platform. +#endif + + + +thread_id_t thread_current_thread_id( void ) + { + #if defined( _WIN32 ) + + return (void*) (uintptr_t)GetCurrentThreadId(); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + return (void*) pthread_self(); + + #else + #error Unknown platform. + #endif + } + + +void thread_yield( void ) + { + #if defined( _WIN32 ) + + SwitchToThread(); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + sched_yield(); + + #else + #error Unknown platform. + #endif + } + + +void thread_exit( int return_code ) + { + #if defined( _WIN32 ) + + ExitThread( (DWORD) return_code ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + pthread_exit( (void*)(uintptr_t) return_code ); + + #else + #error Unknown platform. + #endif + } + + +thread_ptr_t thread_create( int (*thread_proc)( void* ), void* user_data, int stack_size ) + { + #if defined( _WIN32 ) + + DWORD thread_id; + HANDLE handle = CreateThread( NULL, stack_size > 0 ? (size_t)stack_size : 0U, + (LPTHREAD_START_ROUTINE)(uintptr_t) thread_proc, user_data, 0, &thread_id ); + if( !handle ) return NULL; + + return (thread_ptr_t) handle; + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + pthread_t thread; + if( 0 != pthread_create( &thread, NULL, ( void* (*)( void * ) ) thread_proc, user_data ) ) + return NULL; + + return (thread_ptr_t) thread; + + #else + #error Unknown platform. + #endif + } + + +void thread_destroy( thread_ptr_t thread ) + { + #if defined( _WIN32 ) + + WaitForSingleObject( (HANDLE) thread, INFINITE ); + CloseHandle( (HANDLE) thread ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + pthread_join( (pthread_t) thread, NULL ); + + #else + #error Unknown platform. + #endif + } + + +int thread_join( thread_ptr_t thread ) + { + #if defined( _WIN32 ) + + WaitForSingleObject( (HANDLE) thread, INFINITE ); + DWORD retval; + GetExitCodeThread( (HANDLE) thread, &retval ); + return (int) retval; + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + void* retval; + pthread_join( (pthread_t) thread, &retval ); + return (int)(uintptr_t) retval; + + #else + #error Unknown platform. + #endif + } + + +int thread_detach( thread_ptr_t thread ) + { + #if defined( _WIN32 ) + + return CloseHandle( (HANDLE) thread ) != 0; + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + return pthread_detach( (pthread_t) thread ) == 0; + + #else + #error Unknown platform. + #endif + } + + +void thread_set_high_priority( void ) + { + #if defined( _WIN32 ) + + SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + struct sched_param sp; + memset( &sp, 0, sizeof( sp ) ); + sp.sched_priority = sched_get_priority_min( SCHED_RR ); + pthread_setschedparam( pthread_self(), SCHED_RR, &sp); + + #else + #error Unknown platform. + #endif + } + + +void thread_mutex_init( thread_mutex_t* mutex ) + { + #if defined( _WIN32 ) + + // Compile-time size check + #pragma warning( push ) + #pragma warning( disable: 4214 ) // nonstandard extension used: bit field types other than int + struct x { char thread_mutex_type_too_small : ( sizeof( thread_mutex_t ) < sizeof( CRITICAL_SECTION ) ? 0 : 1 ); }; + #pragma warning( pop ) + + InitializeCriticalSectionAndSpinCount( (CRITICAL_SECTION*) mutex, 32 ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + // Compile-time size check + struct x { char thread_mutex_type_too_small : ( sizeof( thread_mutex_t ) < sizeof( pthread_mutex_t ) ? 0 : 1 ); }; + + pthread_mutex_init( (pthread_mutex_t*) mutex, NULL ); + + #else + #error Unknown platform. + #endif + } + + +void thread_mutex_term( thread_mutex_t* mutex ) + { + #if defined( _WIN32 ) + + DeleteCriticalSection( (CRITICAL_SECTION*) mutex ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + pthread_mutex_destroy( (pthread_mutex_t*) mutex ); + + #else + #error Unknown platform. + #endif + } + + +void thread_mutex_lock( thread_mutex_t* mutex ) + { + #if defined( _WIN32 ) + + EnterCriticalSection( (CRITICAL_SECTION*) mutex ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + pthread_mutex_lock( (pthread_mutex_t*) mutex ); + + #else + #error Unknown platform. + #endif + } + + +void thread_mutex_unlock( thread_mutex_t* mutex ) + { + #if defined( _WIN32 ) + + LeaveCriticalSection( (CRITICAL_SECTION*) mutex ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + pthread_mutex_unlock( (pthread_mutex_t*) mutex ); + + #else + #error Unknown platform. + #endif + } + + +struct thread_internal_signal_t + { + #if defined( _WIN32 ) + + #if _WIN32_WINNT >= 0x0600 + CRITICAL_SECTION mutex; + CONDITION_VARIABLE condition; + int value; + #else + #pragma message( "Warning: _WIN32_WINNT < 0x0600 - condition variables not available" ) + HANDLE event; + #endif + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + pthread_mutex_t mutex; + pthread_cond_t condition; + int value; + + #else + #error Unknown platform. + #endif + }; + + +void thread_signal_init( thread_signal_t* signal ) + { + // Compile-time size check + #pragma warning( push ) + #pragma warning( disable: 4214 ) // nonstandard extension used: bit field types other than int + struct x { char thread_signal_type_too_small : ( sizeof( thread_signal_t ) < sizeof( struct thread_internal_signal_t ) ? 0 : 1 ); }; + #pragma warning( pop ) + + struct thread_internal_signal_t* internal = (struct thread_internal_signal_t*) signal; + + #if defined( _WIN32 ) + + #if _WIN32_WINNT >= 0x0600 + InitializeCriticalSectionAndSpinCount( &internal->mutex, 32 ); + InitializeConditionVariable( &internal->condition ); + internal->value = 0; + #else + internal->event = CreateEvent( NULL, FALSE, FALSE, NULL ); + #endif + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + pthread_mutex_init( &internal->mutex, NULL ); + pthread_cond_init( &internal->condition, NULL ); + internal->value = 0; + + #else + #error Unknown platform. + #endif + } + + + void thread_signal_term( thread_signal_t* signal ) + { + struct thread_internal_signal_t* internal = (struct thread_internal_signal_t*) signal; + + #if defined( _WIN32 ) + + #if _WIN32_WINNT >= 0x0600 + DeleteCriticalSection( &internal->mutex ); + #else + CloseHandle( internal->event ); + #endif + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + pthread_mutex_destroy( &internal->mutex ); + pthread_cond_destroy( &internal->condition ); + + #else + #error Unknown platform. + #endif + } + + +void thread_signal_raise( thread_signal_t* signal ) + { + struct thread_internal_signal_t* internal = (struct thread_internal_signal_t*) signal; + + #if defined( _WIN32 ) + + #if _WIN32_WINNT >= 0x0600 + EnterCriticalSection( &internal->mutex ); + internal->value = 1; + LeaveCriticalSection( &internal->mutex ); + WakeConditionVariable( &internal->condition ); + #else + SetEvent( internal->event ); + #endif + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + pthread_mutex_lock( &internal->mutex ); + internal->value = 1; + pthread_mutex_unlock( &internal->mutex ); + pthread_cond_signal( &internal->condition ); + + #else + #error Unknown platform. + #endif + } + + +int thread_signal_wait( thread_signal_t* signal, int timeout_ms ) + { + struct thread_internal_signal_t* internal = (struct thread_internal_signal_t*) signal; + + #if defined( _WIN32 ) + + #if _WIN32_WINNT >= 0x0600 + int timed_out = 0; + EnterCriticalSection( &internal->mutex ); + while( internal->value == 0 ) + { + BOOL res = SleepConditionVariableCS( &internal->condition, &internal->mutex, timeout_ms < 0 ? INFINITE : timeout_ms ); + if( !res && GetLastError() == ERROR_TIMEOUT ) { timed_out = 1; break; } + } + internal->value = 0; + LeaveCriticalSection( &internal->mutex ); + return !timed_out; + #else + int failed = WAIT_OBJECT_0 != WaitForSingleObject( internal->event, timeout_ms < 0 ? INFINITE : timeout_ms ); + return !failed; + #endif + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + struct timespec ts; + if( timeout_ms >= 0 ) + { + struct timeval tv; + gettimeofday( &tv, NULL ); + ts.tv_sec = time( NULL ) + timeout_ms / 1000; + ts.tv_nsec = tv.tv_usec * 1000 + 1000 * 1000 * ( timeout_ms % 1000 ); + ts.tv_sec += ts.tv_nsec / ( 1000 * 1000 * 1000 ); + ts.tv_nsec %= ( 1000 * 1000 * 1000 ); + } + + int timed_out = 0; + pthread_mutex_lock( &internal->mutex ); + while( internal->value == 0 ) + { + if( timeout_ms < 0 ) + pthread_cond_wait( &internal->condition, &internal->mutex ); + else if( pthread_cond_timedwait( &internal->condition, &internal->mutex, &ts ) == ETIMEDOUT ) + { + timed_out = 1; + break; + } + + } + if( !timed_out ) internal->value = 0; + pthread_mutex_unlock( &internal->mutex ); + return !timed_out; + + #else + #error Unknown platform. + #endif + } + + +int thread_atomic_int_load( thread_atomic_int_t* atomic ) + { + #if defined( _WIN32 ) + + return InterlockedCompareExchange( &atomic->i, 0, 0 ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + return (int)__sync_fetch_and_add( &atomic->i, 0 ); + + #else + #error Unknown platform. + #endif + } + + +void thread_atomic_int_store( thread_atomic_int_t* atomic, int desired ) + { + #if defined( _WIN32 ) + + InterlockedExchange( &atomic->i, desired ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + __sync_fetch_and_and( &atomic->i, 0 ); + __sync_fetch_and_or( &atomic->i, desired ); + + #else + #error Unknown platform. + #endif + } + + +int thread_atomic_int_inc( thread_atomic_int_t* atomic ) + { + #if defined( _WIN32 ) + + return InterlockedIncrement( &atomic->i ) - 1; + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + return (int)__sync_fetch_and_add( &atomic->i, 1 ); + + #else + #error Unknown platform. + #endif + } + + +int thread_atomic_int_dec( thread_atomic_int_t* atomic ) + { + #if defined( _WIN32 ) + + return InterlockedDecrement( &atomic->i ) + 1; + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + return (int)__sync_fetch_and_sub( &atomic->i, 1 ); + + #else + #error Unknown platform. + #endif + } + + +int thread_atomic_int_add( thread_atomic_int_t* atomic, int value ) + { + #if defined( _WIN32 ) + + return InterlockedExchangeAdd ( &atomic->i, value ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + return (int)__sync_fetch_and_add( &atomic->i, value ); + + #else + #error Unknown platform. + #endif + } + + +int thread_atomic_int_sub( thread_atomic_int_t* atomic, int value ) + { + #if defined( _WIN32 ) + + return InterlockedExchangeAdd( &atomic->i, -value ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + return (int)__sync_fetch_and_sub( &atomic->i, value ); + + #else + #error Unknown platform. + #endif + } + + +int thread_atomic_int_swap( thread_atomic_int_t* atomic, int desired ) + { + #if defined( _WIN32 ) + + return InterlockedExchange( &atomic->i, desired ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + int old = (int)__sync_lock_test_and_set( &atomic->i, desired ); + __sync_lock_release( &atomic->i ); + return old; + + #else + #error Unknown platform. + #endif + } + + +int thread_atomic_int_compare_and_swap( thread_atomic_int_t* atomic, int expected, int desired ) + { + #if defined( _WIN32 ) + + return InterlockedCompareExchange( &atomic->i, desired, expected ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + return (int)__sync_val_compare_and_swap( &atomic->i, expected, desired ); + + #else + #error Unknown platform. + #endif + } + + +void* thread_atomic_ptr_load( thread_atomic_ptr_t* atomic ) + { + #if defined( _WIN32 ) + + return InterlockedCompareExchangePointer( &atomic->ptr, 0, 0 ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + return __sync_fetch_and_add( &atomic->ptr, 0 ); + + #else + #error Unknown platform. + #endif + } + + +void thread_atomic_ptr_store( thread_atomic_ptr_t* atomic, void* desired ) + { + #if defined( _WIN32 ) + + #pragma warning( push ) + #pragma warning( disable: 4302 ) // 'type cast' : truncation from 'void *' to 'LONG' + #pragma warning( disable: 4311 ) // pointer truncation from 'void *' to 'LONG' + #pragma warning( disable: 4312 ) // conversion from 'LONG' to 'PVOID' of greater size + InterlockedExchangePointer( &atomic->ptr, desired ); + #pragma warning( pop ) + + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + __sync_lock_test_and_set( &atomic->ptr, desired ); + __sync_lock_release( &atomic->ptr ); + + #else + #error Unknown platform. + #endif + } + + +void* thread_atomic_ptr_swap( thread_atomic_ptr_t* atomic, void* desired ) + { + #if defined( _WIN32 ) + + #pragma warning( push ) + #pragma warning( disable: 4302 ) // 'type cast' : truncation from 'void *' to 'LONG' + #pragma warning( disable: 4311 ) // pointer truncation from 'void *' to 'LONG' + #pragma warning( disable: 4312 ) // conversion from 'LONG' to 'PVOID' of greater size + return InterlockedExchangePointer( &atomic->ptr, desired ); + #pragma warning( pop ) + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + void* old = __sync_lock_test_and_set( &atomic->ptr, desired ); + __sync_lock_release( &atomic->ptr ); + return old; + + #else + #error Unknown platform. + #endif + } + + +void* thread_atomic_ptr_compare_and_swap( thread_atomic_ptr_t* atomic, void* expected, void* desired ) + { + #if defined( _WIN32 ) + + return InterlockedCompareExchangePointer( &atomic->ptr, desired, expected ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + return __sync_val_compare_and_swap( &atomic->ptr, expected, desired ); + + #else + #error Unknown platform. + #endif + } + + +void thread_timer_init( thread_timer_t* timer ) + { + #if defined( _WIN32 ) + + // Compile-time size check + #pragma warning( push ) + #pragma warning( disable: 4214 ) // nonstandard extension used: bit field types other than int + struct x { char thread_timer_type_too_small : ( sizeof( thread_mutex_t ) < sizeof( HANDLE ) ? 0 : 1 ); }; + #pragma warning( pop ) + + TIMECAPS tc; + if( timeGetDevCaps( &tc, sizeof( TIMECAPS ) ) == TIMERR_NOERROR ) + timeBeginPeriod( tc.wPeriodMin ); + + *(HANDLE*)timer = CreateWaitableTimer( NULL, TRUE, NULL ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + // Nothing + + #else + #error Unknown platform. + #endif + } + + +void thread_timer_term( thread_timer_t* timer ) + { + #if defined( _WIN32 ) + + CloseHandle( *(HANDLE*)timer ); + + TIMECAPS tc; + if( timeGetDevCaps( &tc, sizeof( TIMECAPS ) ) == TIMERR_NOERROR ) + timeEndPeriod( tc.wPeriodMin ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + // Nothing + + #else + #error Unknown platform. + #endif + } + + +void thread_timer_wait( thread_timer_t* timer, THREAD_U64 nanoseconds ) + { + #if defined( _WIN32 ) + + LARGE_INTEGER due_time; + due_time.QuadPart = - (LONGLONG) ( nanoseconds / 100 ); + BOOL b = SetWaitableTimer( *(HANDLE*)timer, &due_time, 0, 0, 0, FALSE ); + (void) b; + WaitForSingleObject( *(HANDLE*)timer, INFINITE ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + struct timespec rem; + struct timespec req; + req.tv_sec = nanoseconds / 1000000000ULL; + req.tv_nsec = nanoseconds - req.tv_sec * 1000000000ULL; + while( nanosleep( &req, &rem ) ) + req = rem; + + #else + #error Unknown platform. + #endif + } + + +thread_tls_t thread_tls_create( void ) + { + #if defined( _WIN32 ) + + DWORD tls = TlsAlloc(); + if( tls == TLS_OUT_OF_INDEXES ) + return NULL; + else + return (thread_tls_t) (uintptr_t) tls; + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + pthread_key_t tls; + if( pthread_key_create( &tls, NULL ) == 0 ) + return (thread_tls_t) (uintptr_t) tls; + else + return NULL; + + #else + #error Unknown platform. + #endif + } + + +void thread_tls_destroy( thread_tls_t tls ) + { + #if defined( _WIN32 ) + + TlsFree( (DWORD) (uintptr_t) tls ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + pthread_key_delete( (pthread_key_t) (uintptr_t) tls ); + + #else + #error Unknown platform. + #endif + } + + +void thread_tls_set( thread_tls_t tls, void* value ) + { + #if defined( _WIN32 ) + + TlsSetValue( (DWORD) (uintptr_t) tls, value ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + pthread_setspecific( (pthread_key_t) (uintptr_t) tls, value ); + + #else + #error Unknown platform. + #endif + } + + +void* thread_tls_get( thread_tls_t tls ) + { + #if defined( _WIN32 ) + + return TlsGetValue( (DWORD) (uintptr_t) tls ); + + #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) + + return pthread_getspecific( (pthread_key_t) (uintptr_t) tls ); + + #else + #error Unknown platform. + #endif + } + + +void thread_queue_init( thread_queue_t* queue, int size, void** values, int count ) + { + queue->values = values; + thread_signal_init( &queue->data_ready ); + thread_signal_init( &queue->space_open ); + thread_atomic_int_store( &queue->head, 0 ); + thread_atomic_int_store( &queue->tail, count > size ? size : count ); + thread_atomic_int_store( &queue->count, count > size ? size : count ); + queue->size = size; + #ifndef NDEBUG + thread_atomic_int_store( &queue->id_produce_is_set, 0 ); + thread_atomic_int_store( &queue->id_consume_is_set, 0 ); + #endif + } + + +void thread_queue_term( thread_queue_t* queue ) + { + thread_signal_term( &queue->space_open ); + thread_signal_term( &queue->data_ready ); + } + + +int thread_queue_produce( thread_queue_t* queue, void* value, int timeout_ms ) + { + #ifndef NDEBUG + if( thread_atomic_int_compare_and_swap( &queue->id_produce_is_set, 0, 1 ) == 0 ) + queue->id_produce = thread_current_thread_id(); + THREAD_ASSERT( thread_current_thread_id() == queue->id_produce, "thread_queue_produce called from multiple threads" ); + #endif + while( thread_atomic_int_load( &queue->count ) == queue->size ) // TODO: fix signal so that this can be an "if" instead of "while" + { + if( timeout_ms == 0 ) return 0; + if( thread_signal_wait( &queue->space_open, timeout_ms == THREAD_QUEUE_WAIT_INFINITE ? THREAD_SIGNAL_WAIT_INFINITE : timeout_ms ) == 0 ) + return 0; + } + int tail = thread_atomic_int_inc( &queue->tail ); + queue->values[ tail % queue->size ] = value; + if( thread_atomic_int_inc( &queue->count ) == 0 ) + thread_signal_raise( &queue->data_ready ); + return 1; + } + + +void* thread_queue_consume( thread_queue_t* queue, int timeout_ms ) + { + #ifndef NDEBUG + if( thread_atomic_int_compare_and_swap( &queue->id_consume_is_set, 0, 1 ) == 0 ) + queue->id_consume = thread_current_thread_id(); + THREAD_ASSERT( thread_current_thread_id() == queue->id_consume, "thread_queue_consume called from multiple threads" ); + #endif + while( thread_atomic_int_load( &queue->count ) == 0 ) // TODO: fix signal so that this can be an "if" instead of "while" + { + if( timeout_ms == 0 ) return NULL; + if( thread_signal_wait( &queue->data_ready, timeout_ms == THREAD_QUEUE_WAIT_INFINITE ? THREAD_SIGNAL_WAIT_INFINITE : timeout_ms ) == 0 ) + return NULL; + } + int head = thread_atomic_int_inc( &queue->head ); + void* retval = queue->values[ head % queue->size ]; + if( thread_atomic_int_dec( &queue->count ) == queue->size ) + thread_signal_raise( &queue->space_open ); + return retval; + } + + +int thread_queue_count( thread_queue_t* queue ) + { + return thread_atomic_int_load( &queue->count ); + } + + +#endif /* THREAD_IMPLEMENTATION */ + +/* +revision history: + 0.3 set_high_priority API change. Fixed spurious wakeup bug in signal. Added + timeout param to queue produce/consume. Various cleanup and trivial fixes. + 0.2 first publicly released version +*/ + +/* +------------------------------------------------------------------------------ + +This software is available under 2 licenses - you may choose the one you like. + +------------------------------------------------------------------------------ + +ALTERNATIVE A - MIT License + +Copyright (c) 2015 Mattias Gustavsson + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------ + +ALTERNATIVE B - Public Domain (www.unlicense.org) + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------ +*/